diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..86a64e158 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,15 @@ +[//]: # "Pull Request Template" +[//]: # "Replace the placeholder values in the template below" + +- **File(s) Modified**: _0001-two-sum.py, 0002-add-two-numbers.py, etc..._ +- **Language(s) Used**: _python, javascript, etc..._ +- **Submission URL**: _https://leetcode.com/problems/[problem-name]/submissions/xxxxxxxxx/_ + +[//]: # "Getting the Submission URL" +[//]: # "Go to the leetcode [`Submissions tab`](https://user-images.githubusercontent.com/71089234/180188604-b1ecaf90-bf27-4fd6-a559-5567aebf8930.png)" +[//]: # "and [click on the `Accepted` status of your submission.](https://user-images.githubusercontent.com/71089234/180189321-1a48c33f-aa65-4b29-8aaa-685f4f5f8c9e.png)]" +[//]: # "Finally copy the URL from the nav bar, it should look like https://leetcode.com/problems/[problem-name]/submissions/xxxxxxxxx/" + + +### Important +Please make sure the file name is lowercase and a duplicate file does not already exist before merging. diff --git a/.github/workflows/build-readme.yml b/.github/workflows/build-readme.yml new file mode 100644 index 000000000..fcd5b875e --- /dev/null +++ b/.github/workflows/build-readme.yml @@ -0,0 +1,40 @@ +name: Build readme file + +on: + #push: + workflow_dispatch: + schedule: + - cron: '0 * * * *' + +jobs: + Build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} + fetch-depth: 1 + + - name: Use Node.js (dependency) + uses: actions/setup-node@v3 + with: + node-version: 16 + + - name: Completion Table + run: node updateCompletionTable.js; + + - name: Check for modified files + id: git-check + run: echo modified=$(if git diff-index --quiet HEAD --; then echo "false"; else echo "true"; fi) >> $GITHUB_OUTPUT + + - name: Push + if: steps.git-check.outputs.modified == 'true' + run: | + git config --global user.email "71089234+Ahmad-A0@users.noreply.github.com" + git config --global user.name "Bot-A0" + git add . + git commit -am "📜 Update README table (🛠️ from Github Actions)" || true + git push || git pull --rebase && git push + + diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml new file mode 100644 index 000000000..f2847ab45 --- /dev/null +++ b/.github/workflows/format.yml @@ -0,0 +1,65 @@ +name: Format Files + +# cpp +# csharp - https://github.com/dotnet/format ? +# java - prettier-plugin-java +# javascript - prettier +# python - black +# ruby - rufo +# swift - https://github.com/apple/swift-format ? +# typescript - prettier + +on: + # pull_request_target: + # types: [opened, synchronize] + workflow_dispatch: + +jobs: + Format: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [16.x] + + steps: + - uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} + fetch-depth: 1 + + - name: Use Node.js (dependency) + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + + - name: Install Rufo (dependency) + run: | + sudo apt update + sudo gem install rufo + + - name: Install Prettier + run: | + npm install -g prettier + npm install -g prettier-plugin-java + + - name: Install Black + uses: BSFishy/pip-action@v1 + with: + packages: black + + - name: Format + run: | + prettier --config "$GITHUB_WORKSPACE/.prettierrc" --write "$GITHUB_WORKSPACE/javascript/*.js" 2>&1 || true + prettier --config "$GITHUB_WORKSPACE/.prettierrc" --write "$GITHUB_WORKSPACE/typescript/*.ts" 2>&1 || true + prettier --config "$GITHUB_WORKSPACE/.prettierrc" --write "$GITHUB_WORKSPACE/java/*.java" 2>&1 || true + rufo "$GITHUB_WORKSPACE/ruby" 2>&1 || true + python -m black "$GITHUB_WORKSPACE/" 2>&1 || true + + - name: Push + run: | + git config --global user.email "71089234+Ahmad-A0@users.noreply.github.com" + git config --global user.name "Bot-A0" + git add . + git commit -m "🎨 Format files (🛠️ from Github Actions)" || true + git push || true diff --git a/.github/workflows/stale._yml b/.github/workflows/stale._yml new file mode 100644 index 000000000..381bfa5dd --- /dev/null +++ b/.github/workflows/stale._yml @@ -0,0 +1,33 @@ +# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time. +# +# You can adjust the behavior by modifying this file. +# For more information, see: +# https://github.com/actions/stale +name: Mark stale issues and pull requests + +on: + schedule: + - cron: '19 17 * * *' + workflow_dispatch: + +jobs: + stale: + + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + + steps: + - uses: actions/stale@v5 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + stale-issue-message: 'Stale issue message' + stale-pr-message: 'Stale pull request message' + stale-issue-label: 'stale' + stale-pr-label: 'stale' + days-before-stale: 30 + days-before-close: 7 + days-before-issue-stale: 900 + exempt-milestones: true + exempt-pr-labels: 'pending' diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..146f8c207 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +.metals +.vscode \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 000000000..13566b81b --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 000000000..898ccd0de --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,51 @@ + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 000000000..79ee123c2 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 000000000..03f397ce0 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 000000000..5842c940c --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 000000000..35eb1ddfb --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..d30dcd75b --- /dev/null +++ b/.prettierrc @@ -0,0 +1,24 @@ +{ + "semi": true, + "singleQuote": true, + "endOfLine": "lf", + "tabWidth": 4, + "overrides": [ + { + "files": ["javascript/*.js"], + "options": {} + }, + { + "files": ["typescript/*.ts"], + "options": {} + }, + { + "files": ["ruby/*.rb"], + "options": {} + }, + { + "files": ["java/*.java"], + "options": {} + } + ] +} diff --git a/.problemSiteData.json b/.problemSiteData.json new file mode 100644 index 000000000..5699c6b13 --- /dev/null +++ b/.problemSiteData.json @@ -0,0 +1,6779 @@ +[ + { + "neetcode150":true, + "blind75":true, + "problem":"Contains Duplicate", + "pattern":"Arrays & Hashing", + "link":"contains-duplicate/", + "video":"3OamzN90kPg", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0217-contains-duplicate", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true, + "scala":true, + "dart":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Valid Anagram", + "pattern":"Arrays & Hashing", + "link":"valid-anagram/", + "video":"9UtInBqnCgA", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0242-valid-anagram", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true, + "scala":true, + "dart":true + }, + { + "problem":"Concatenation of Array", + "pattern":"Arrays & Hashing", + "link":"concatenation-of-array/", + "video":"68isPRHgcFQ", + "difficulty":"Easy", + "code":"1929-concatenation-of-array", + "python":true, + "cpp":true, + "java":true, + "javascript":true, + "kotlin":true, + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "swift":true, + "rust":true, + "scala":true, + "dart":true + }, + { + "problem":"Replace Elements With Greatest Element On Right Side", + "pattern":"Arrays & Hashing", + "link":"replace-elements-with-greatest-element-on-right-side/", + "video":"ZHjKhUjcsaU", + "difficulty":"Easy", + "code":"1299-replace-elements-with-greatest-element-on-right-side", + "c":true, + "cpp":true, + "csharp":true, + "java":true, + "python":true, + "javascript":true, + "typescript":true, + "go":true, + "rust":true, + "dart":true, + "kotlin":true, + "swift":true + }, + { + "problem":"Is Subsequence", + "pattern":"Arrays & Hashing", + "link":"is-subsequence/", + "video":"99RVfqklbCE", + "difficulty":"Easy", + "code":"0392-is-subsequence", + "c":true, + "cpp":true, + "csharp":true, + "java":true, + "python":true, + "javascript":true, + "typescript":true, + "go":true, + "rust":true, + "dart":true, + "kotlin":true, + "swift":true + }, + { + "problem":"Length of Last Word", + "pattern":"Arrays & Hashing", + "link":"length-of-last-word/", + "video":"KT9rltZTybQ", + "difficulty":"Easy", + "code":"0058-length-of-last-word", + "c":true, + "cpp":true, + "csharp":true, + "java":true, + "python":true, + "javascript":true, + "typescript":true, + "rust":true, + "go":true, + "swift":true, + "dart":true, + "ruby":true, + "kotlin":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Two Sum", + "pattern":"Arrays & Hashing", + "link":"two-sum/", + "video":"KLlXCFG5TnA", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0001-two-sum", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true, + "scala":true, + "dart":true + }, + { + "problem":"Longest Common Prefix", + "pattern":"Arrays & Hashing", + "link":"longest-common-prefix/", + "video":"0sWShKIJoo4", + "difficulty":"Easy", + "code":"0014-longest-common-prefix", + "cpp":true, + "python":true, + "javascript":true, + "c":true, + "csharp":true, + "java":true, + "typescript":true, + "go":true, + "rust":true, + "dart":true, + "kotlin":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Group Anagrams", + "pattern":"Arrays & Hashing", + "link":"group-anagrams/", + "video":"vzdNOK2oB2E", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0049-group-anagrams", + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true, + "dart":true, + "c":true, + "scala":true + }, + { + "problem":"Pascals Triangle", + "pattern":"Arrays & Hashing", + "link":"pascals-triangle/", + "video":"nPVEaB3AjUM", + "difficulty":"Easy", + "code":"0118-pascals-triangle", + "c":true, + "cpp":true, + "java":true, + "python":true, + "javascript":true, + "typescript":true, + "rust":true, + "csharp":true, + "go":true, + "dart":true, + "kotlin":true + }, + { + "problem":"Remove Element", + "pattern":"Arrays & Hashing", + "link":"remove-element/", + "video":"Pcd1ii9P9ZI", + "difficulty":"Easy", + "code":"0027-remove-element", + "c":true, + "java":true, + "python":true, + "javascript":true, + "typescript":true, + "go":true, + "cpp":true, + "csharp":true, + "swift":true, + "rust":true, + "dart":true, + "kotlin":true + }, + { + "problem":"Unique Email Addresses", + "pattern":"Arrays & Hashing", + "link":"unique-email-addresses/", + "video":"TC_xLIWl7qY", + "difficulty":"Easy", + "code":"0929-unique-email-addresses", + "java":true, + "python":true, + "javascript":true, + "typescript":true, + "swift":true, + "cpp":true, + "csharp":true, + "go":true, + "rust":true, + "c":true, + "kotlin":true + }, + { + "problem":"Isomorphic Strings", + "pattern":"Arrays & Hashing", + "link":"isomorphic-strings/", + "video":"7yF-U1hLEqQ", + "difficulty":"Easy", + "code":"0205-isomorphic-strings", + "c":true, + "java":true, + "python":true, + "javascript":true, + "typescript":true, + "swift":true, + "cpp":true, + "csharp":true, + "go":true, + "rust":true, + "kotlin":true + }, + { + "problem":"Can Place Flowers", + "pattern":"Arrays & Hashing", + "link":"can-place-flowers/", + "video":"ZGxqqjljpUI", + "difficulty":"Easy", + "code":"0605-can-place-flowers", + "c":true, + "cpp":true, + "python":true, + "javascript":true, + "typescript":true, + "csharp":true, + "go":true, + "rust":true, + "java":true, + "kotlin":true, + "swift":true + }, + { + "problem":"Majority Element", + "pattern":"Arrays & Hashing", + "link":"majority-element/", + "video":"7pnhv842keE", + "difficulty":"Easy", + "code":"0169-majority-element", + "c":true, + "cpp":true, + "python":true, + "javascript":true, + "typescript":true, + "swift":true, + "csharp":true, + "java":true, + "go":true, + "kotlin":true, + "rust":true + }, + { + "problem":"Next Greater Element I", + "pattern":"Arrays & Hashing", + "link":"next-greater-element-i/", + "video":"68a1Dc_qVq4", + "difficulty":"Easy", + "code":"0496-next-greater-element-i", + "java":true, + "python":true, + "typescript":true, + "c":true, + "cpp":true, + "csharp":true, + "javascript":true, + "go":true, + "rust":true, + "kotlin":true + }, + { + "problem":"Find Pivot Index", + "pattern":"Arrays & Hashing", + "link":"find-pivot-index/", + "video":"u89i60lYx8U", + "difficulty":"Easy", + "code":"0724-find-pivot-index", + "c":true, + "cpp":true, + "java":true, + "python":true, + "javascript":true, + "typescript":true, + "go":true, + "rust":true, + "csharp":true, + "kotlin":true, + "swift":true + }, + { + "problem":"Range Sum Query - Immutable", + "pattern":"Arrays & Hashing", + "link":"range-sum-query-immutable/", + "video":"2pndAmo_sMA", + "difficulty":"Easy", + "code":"0303-range-sum-query-immutable", + "python":true, + "c":true, + "cpp":true, + "java":true, + "kotlin":true, + "csharp":true, + "javascript":true, + "rust":true + }, + { + "problem":"Find All Numbers Disappeared in An Array", + "pattern":"Arrays & Hashing", + "link":"find-all-numbers-disappeared-in-an-array/", + "video":"8i-f24YFWC4", + "difficulty":"Easy", + "code":"0448-find-all-numbers-disappeared-in-an-array", + "cpp":true, + "java":true, + "python":true, + "javascript":true, + "c":true, + "typescript":true, + "go":true, + "rust":true, + "csharp":true, + "kotlin":true + }, + { + "problem":"Maximum Number of Balloons", + "pattern":"Arrays & Hashing", + "link":"maximum-number-of-balloons/", + "video":"G9xeB2-7PqY", + "difficulty":"Easy", + "code":"1189-maximum-number-of-balloons", + "c":true, + "java":true, + "python":true, + "javascript":true, + "cpp":true, + "typescript":true, + "go":true, + "rust":true, + "csharp":true, + "kotlin":true + }, + { + "problem":"Word Pattern", + "pattern":"Arrays & Hashing", + "link":"word-pattern/", + "video":"W_akoecmCbM", + "difficulty":"Easy", + "code":"0290-word-pattern", + "java":true, + "python":true, + "javascript":true, + "typescript":true, + "c":true, + "cpp":true, + "go":true, + "rust":true, + "csharp":true, + "kotlin":true + }, + { + "problem":"Design HashSet", + "pattern":"Arrays & Hashing", + "link":"design-hashset/", + "video":"VymjPQUXjL8", + "difficulty":"Easy", + "code":"0705-design-hashset", + "kotlin":true, + "c":true, + "cpp":true, + "java":true, + "python":true, + "javascript":true, + "rust":true + }, + { + "problem":"Design HashMap", + "pattern":"Arrays & Hashing", + "link":"design-hashmap/", + "video":"cNWsgbKwwoU", + "difficulty":"Easy", + "code":"0706-design-hashmap", + "python":true, + "c":true, + "cpp":true, + "java":true, + "javascript":true, + "kotlin":true, + "rust":true + }, + { + "problem":"Sort an Array", + "pattern":"Arrays & Hashing", + "link":"sort-an-array/", + "video":"MsYZSinhuFo", + "difficulty":"Medium", + "code":"0912-sort-an-array", + "python":true, + "java":true, + "kotlin":true, + "cpp":true, + "csharp":true, + "javascript":true, + "go":true, + "rust":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Top K Frequent Elements", + "pattern":"Arrays & Hashing", + "link":"top-k-frequent-elements/", + "video":"YPTqKIgVk-k", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0347-top-k-frequent-elements", + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true, + "c":true, + "scala":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Product of Array Except Self", + "pattern":"Arrays & Hashing", + "link":"product-of-array-except-self/", + "video":"bNvIQI2wAjk", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0238-product-of-array-except-self", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "problem":"Valid Sudoku", + "pattern":"Arrays & Hashing", + "link":"valid-sudoku/", + "video":"TjFXEUCMqI8", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0036-valid-sudoku", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "kotlin":true, + "rust":true, + "dart":true, + "scala":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Encode and Decode Strings", + "premium":true, + "freeLink":"https://www.lintcode.com/problem/659/", + "pattern":"Arrays & Hashing", + "link":"encode-and-decode-strings/", + "video":"B1k_sxOSgv8", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0271-encode-and-decode-strings", + "csharp":true, + "go":true, + "ruby":true, + "swift":true, + "rust":true, + "typescript":true, + "kotlin":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Longest Consecutive Sequence", + "pattern":"Arrays & Hashing", + "link":"longest-consecutive-sequence/", + "video":"P6RZZMu_maU", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0128-longest-consecutive-sequence", + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true, + "c":true + }, + { + "problem":"Sort Colors", + "pattern":"Arrays & Hashing", + "link":"sort-colors/", + "video":"4xbWSRZHqac", + "difficulty":"Medium", + "code":"0075-sort-colors", + "c":true, + "java":true, + "javascript":true, + "go":true, + "kotlin":true, + "cpp":true, + "python":true, + "typescript":true, + "csharp":true, + "rust":true, + "swift":true + }, + { + "problem":"Encode and Decode TinyURL", + "pattern":"Arrays & Hashing", + "link":"encode-and-decode-tinyurl/", + "video":"VyBOaboQLGc", + "difficulty":"Medium", + "code":"0535-encode-and-decode-tinyurl", + "javascript":true, + "cpp":true, + "python":true, + "typescript":true, + "go":true, + "rust":true, + "swift":true, + "kotlin":true, + "c":true, + "java":true + }, + { + "problem":"Brick Wall", + "pattern":"Arrays & Hashing", + "link":"brick-wall/", + "video":"Kkmv2h48ekw", + "difficulty":"Medium", + "code":"0554-brick-wall", + "javascript":true, + "typescript":true, + "c":true, + "cpp":true, + "java":true, + "python":true, + "rust":true, + "kotlin":true + }, + { + "problem":"Best Time to Buy And Sell Stock II", + "pattern":"Arrays & Hashing", + "link":"best-time-to-buy-and-sell-stock-ii/", + "video":"3SJ3pUkPQMc", + "difficulty":"Medium", + "code":"0122-best-time-to-buy-and-sell-stock-ii", + "c":true, + "javascript":true, + "go":true, + "cpp":true, + "java":true, + "python":true, + "kotlin":true, + "rust":true + }, + { + "problem":"Subarray Sum Equals K", + "pattern":"Arrays & Hashing", + "link":"subarray-sum-equals-k/", + "video":"fFVZt-6sgyo", + "difficulty":"Medium", + "code":"0560-subarray-sum-equals-k", + "java":true, + "cpp":true, + "go":true, + "c":true, + "python":true, + "javascript":true, + "typescript":true, + "kotlin":true, + "rust":true, + "csharp":true + }, + { + "problem":"Unique Length 3 Palindromic Subsequences", + "pattern":"Arrays & Hashing", + "link":"unique-length-3-palindromic-subsequences/", + "video":"3THUt0vAFLU", + "difficulty":"Medium", + "code":"1930-unique-length-3-palindromic-subsequences", + "cpp":true, + "java":true, + "kotlin":true, + "c":true, + "python":true, + "javascript":true, + "typescript":true, + "rust":true + }, + { + "problem":"Minimum Number of Swaps to Make The String Balanced", + "pattern":"Arrays & Hashing", + "link":"minimum-number-of-swaps-to-make-the-string-balanced/", + "video":"3YDBT9ZrfaU", + "difficulty":"Medium", + "code":"1963-minimum-number-of-swaps-to-make-the-string-balanced", + "javascript":true, + "cpp":true, + "c":true, + "python":true, + "kotlin":true, + "java":true, + "rust":true + }, + { + "problem":"Number of Pairs of Interchangeable Rectangles", + "pattern":"Arrays & Hashing", + "link":"number-of-pairs-of-interchangeable-rectangles/", + "video":"lEQ8ZlLOuyQ", + "difficulty":"Medium", + "code":"2001-number-of-pairs-of-interchangeable-rectangles", + "javascript":true, + "cpp":true, + "python":true, + "c":true, + "java":true, + "rust":true, + "kotlin":true + }, + { + "problem":"Maximum Product of The Length of Two Palindromic Subsequences", + "pattern":"Arrays & Hashing", + "link":"maximum-product-of-the-length-of-two-palindromic-subsequences/", + "video":"aoHbYlO8vDg", + "difficulty":"Medium", + "code":"2002-maximum-product-of-the-length-of-two-palindromic-subsequences", + "cpp":true, + "c":true, + "csharp":true, + "java":true, + "python":true, + "javascript":true, + "typescript":true, + "go":true, + "kotlin":true, + "rust":true + }, + { + "problem":"Grid Game", + "pattern":"Arrays & Hashing", + "link":"grid-game/", + "video":"N4wDSOw65hI", + "difficulty":"Medium", + "code":"2017-grid-game", + "python":true, + "cpp":true, + "kotlin":true, + "java":true, + "javascript":true, + "rust":true + }, + { + "problem":"Find All Anagrams in a String", + "pattern":"Arrays & Hashing", + "link":"find-all-anagrams-in-a-string/", + "video":"G8xtZy0fDKg", + "difficulty":"Medium", + "code":"0438-find-all-anagrams-in-a-string", + "cpp":true, + "csharp":true, + "python":true, + "java":true, + "javascript":true, + "go":true, + "kotlin":true, + "c":true + }, + { + "problem":"Find The Index of The First Occurrence in a String", + "pattern":"Arrays & Hashing", + "link":"find-the-index-of-the-first-occurrence-in-a-string/", + "video":"JoF0Z7nVSrA", + "difficulty":"Easy", + "code":"0028-find-the-index-of-the-first-occurrence-in-a-string", + "python":true, + "cpp":true, + "go":true, + "c":true, + "java":true, + "kotlin":true, + "csharp":true, + "javascript":true + }, + { + "problem":"Wiggle Sort", + "pattern":"Arrays & Hashing", + "premium":true, + "freeLink":"https://www.lintcode.com/problem/508/", + "link":"wiggle-sort/", + "video":"vGsyTE4s34w", + "difficulty":"Medium", + "code":"0280-wiggle-sort", + "cpp":true, + "python":true, + "kotlin":true + }, + { + "problem":"Largest Number", + "pattern":"Arrays & Hashing", + "link":"largest-number/", + "video":"WDx6Y4i4xJ8", + "difficulty":"Medium", + "code":"0179-largest-number", + "typescript":true, + "cpp":true, + "java":true, + "kotlin":true, + "c":true, + "csharp":true, + "python":true, + "javascript":true, + "go":true, + "ruby":true, + "swift":true, + "rust":true, + "scala":true + }, + { + "problem":"Continuous Subarray Sum", + "pattern":"Arrays & Hashing", + "link":"continuous-subarray-sum/", + "video":"OKcrLfR-8mE", + "difficulty":"Medium", + "code":"0523-continuous-subarray-sum", + "java":true, + "python":true, + "cpp":true, + "kotlin":true, + "c":true, + "javascript":true, + "rust":true + }, + { + "problem":"Push Dominoes", + "pattern":"Arrays & Hashing", + "link":"push-dominoes/", + "video":"evUFsOb_iLY", + "difficulty":"Medium", + "code":"0838-push-dominoes", + "typescript":true, + "cpp":true, + "python":true, + "kotlin":true, + "java":true + }, + { + "problem":"Repeated DNA Sequences", + "pattern":"Arrays & Hashing", + "link":"repeated-dna-sequences/", + "video":"FzTYfsmtOso", + "difficulty":"Medium", + "code":"0187-repeated-dna-sequences", + "java":true, + "typescript":true, + "cpp":true, + "kotlin":true, + "python":true, + "javascript":true + }, + { + "problem":"Insert Delete Get Random O(1)", + "pattern":"Arrays & Hashing", + "link":"insert-delete-getrandom-o1/", + "video":"j4KwhBziOpg", + "difficulty":"Medium", + "code":"0380-insert-delete-getrandom-o1", + "java":true, + "typescript":true, + "javascript":true, + "go":true, + "cpp":true, + "swift":true, + "kotlin":true, + "python":true + }, + { + "problem":"Check if a String Contains all Binary Codes of Size K", + "pattern":"Arrays & Hashing", + "link":"check-if-a-string-contains-all-binary-codes-of-size-k/", + "video":"qU32rTy_kOM", + "difficulty":"Medium", + "code":"1461-check-if-a-string-contains-all-binary-codes-of-size-k", + "cpp":true, + "kotlin":true, + "python":true, + "javascript":true + }, + { + "problem":"Range Sum Query 2D Immutable", + "pattern":"Arrays & Hashing", + "link":"range-sum-query-2d-immutable/", + "video":"KE8MQuwE2yA", + "difficulty":"Medium", + "code":"0304-range-sum-query-2d-immutable", + "cpp":true, + "python":true, + "kotlin":true, + "javascript":true + }, + { + "problem":"Non Decreasing Array", + "pattern":"Arrays & Hashing", + "link":"non-decreasing-array/", + "video":"RegQckCegDk", + "difficulty":"Medium", + "code":"0665-non-decreasing-array", + "cpp":true, + "java":true, + "javascript":true, + "typescript":true, + "go":true, + "scala":true, + "kotlin":true, + "python":true, + "csharp":true + }, + { + "problem":"First Missing Positive", + "pattern":"Arrays & Hashing", + "link":"first-missing-positive/", + "video":"8g78yfzMlao", + "difficulty":"Hard", + "code":"0041-first-missing-positive", + "python":true, + "typescript":true, + "cpp":true, + "java":true, + "go":true, + "kotlin":true, + "c":true, + "javascript":true + }, + { + "problem":"Sign of An Array", + "pattern":"Arrays & Hashing", + "link":"sign-of-the-product-of-an-array/", + "video":"ILDLM86jNow", + "difficulty":"Easy", + "code":"1822-sign-of-the-product-of-an-array", + "cpp":true, + "java":true, + "python":true, + "javascript":true, + "typescript":true, + "go":true, + "kotlin":true + }, + { + "problem":"Find the Difference of Two Arrays", + "pattern":"Arrays & Hashing", + "link":"find-the-difference-of-two-arrays/", + "video":"a4wqKR-znBE", + "difficulty":"Easy", + "code":"2215-find-the-difference-of-two-arrays", + "kotlin":true, + "cpp":true, + "csharp":true, + "java":true, + "python":true, + "javascript":true, + "typescript":true + }, + { + "problem":"Design Parking System", + "pattern":"Arrays & Hashing", + "link":"design-parking-system/", + "video":"d5zCHesOrSk", + "difficulty":"Easy", + "code":"1603-design-parking-system", + "kotlin":true, + "java":true, + "python":true, + "javascript":true + }, + { + "problem":"Number of Zero-Filled Subarrays", + "pattern":"Arrays & Hashing", + "link":"number-of-zero-filled-subarrays/", + "video":"G-EWVGCcL_w", + "difficulty":"Medium", + "code":"2348-number-of-zero-filled-subarrays", + "kotlin":true, + "cpp":true, + "java":true, + "python":true + }, + { + "problem":"Optimal Partition of String", + "pattern":"Arrays & Hashing", + "link":"optimal-partition-of-string/", + "video":"CKZPdiXiQf0", + "difficulty":"Medium", + "code":"2405-optimal-partition-of-string", + "kotlin":true, + "java":true + }, + { + "problem":"Design Underground System", + "pattern":"Arrays & Hashing", + "link":"design-underground-system/", + "video":"W5QOLqXskZM", + "difficulty":"Medium", + "code":"1396-design-underground-system", + "kotlin":true, + "java":true, + "javascript":true + }, + { + "problem":"Minimum Penalty for a Shop", + "pattern":"Arrays & Hashing", + "link":"minimum-penalty-for-a-shop/", + "video":"0d7ShRoOFVE", + "difficulty":"Medium", + "code":"2483-minimum-penalty-for-a-shop", + "cpp":true, + "java":true, + "kotlin":true + }, + { + "problem":"Text Justification", + "pattern":"Arrays & Hashing", + "link":"text-justification/", + "video":"TzMl4Z7pVh8", + "difficulty":"Hard", + "code":"0068-naming-a-company", + "python":true, + "kotlin":true + }, + { + "problem":"Naming a Company", + "pattern":"Arrays & Hashing", + "link":"naming-a-company/", + "video":"NrHpgTScOcY", + "difficulty":"Hard", + "code":"2306-naming-a-company", + "kotlin":true, + "java":true, + "javascript":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Valid Palindrome", + "pattern":"Two Pointers", + "link":"valid-palindrome/", + "video":"jJXJ16kPFWg", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0125-valid-palindrome", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true, + "dart":true, + "scala":true + }, + { + "problem":"Valid Palindrome II", + "pattern":"Two Pointers", + "link":"valid-palindrome-ii/", + "video":"JrxRYBwG6EI", + "difficulty":"Easy", + "code":"0680-valid-palindrome-ii", + "python":true, + "cpp":true, + "csharp":true, + "java":true, + "javascript":true, + "typescript":true, + "go":true, + "rust":true, + "c":true, + "kotlin":true + }, + { + "problem":"Minimum Difference Between Highest And Lowest of K Scores", + "pattern":"Two Pointers", + "link":"minimum-difference-between-highest-and-lowest-of-k-scores/", + "video":"JU5XdBZZtlk", + "difficulty":"Easy", + "code":"1984-minimum-difference-between-highest-and-lowest-of-k-scores", + "javascript":true, + "cpp":true, + "python":true, + "typescript":true, + "go":true, + "rust":true, + "java":true, + "kotlin":true + }, + { + "problem":"Merge Strings Alternately", + "pattern":"Two Pointers", + "link":"merge-strings-alternately/", + "video":"LECWOvTo-Sc", + "difficulty":"Easy", + "code":"1768-merge-strings-alternately", + "kotlin":true, + "cpp":true, + "java":true, + "python":true, + "javascript":true, + "typescript":true + }, + { + "problem":"Reverse String", + "pattern":"Two Pointers", + "link":"reverse-string/", + "video":"_d0T_2Lk2qA", + "difficulty":"Easy", + "code":"0344-reverse-string", + "c":true, + "python":true, + "javascript":true, + "typescript":true, + "swift":true, + "cpp":true, + "java":true, + "go":true, + "rust":true, + "kotlin":true, + "dart":true + }, + { + "problem":"Merge Sorted Array", + "pattern":"Two Pointers", + "link":"merge-sorted-array/", + "video":"P1Ic85RarKY", + "difficulty":"Easy", + "code":"0088-merge-sorted-array", + "c":true, + "java":true, + "javascript":true, + "cpp":true, + "python":true, + "typescript":true, + "go":true, + "rust":true, + "kotlin":true + }, + { + "problem":"Move Zeroes", + "pattern":"Two Pointers", + "link":"move-zeroes/", + "video":"aayNRwUN3Do", + "difficulty":"Easy", + "code":"0283-move-zeroes", + "c":true, + "cpp":true, + "javascript":true, + "csharp":true, + "python":true, + "typescript":true, + "go":true, + "swift":true, + "rust":true, + "java":true, + "kotlin":true + }, + { + "problem":"Remove Duplicates From Sorted Array", + "pattern":"Two Pointers", + "link":"remove-duplicates-from-sorted-array/", + "video":"DEJAZBq0FDA", + "difficulty":"Easy", + "code":"0026-remove-duplicates-from-sorted-array", + "python":true, + "javascript":true, + "go":true, + "cpp":true, + "typescript":true, + "swift":true, + "rust":true, + "java":true, + "kotlin":true, + "c":true + }, + { + "problem":"Remove Duplicates From Sorted Array II", + "pattern":"Two Pointers", + "link":"remove-duplicates-from-sorted-array-ii/", + "video":"ycAq8iqh0TI", + "difficulty":"Medium", + "code":"0080-remove-duplicates-from-sorted-array-ii", + "python":true, + "kotlin":true, + "cpp":true, + "csharp":true, + "javascript":true, + "swift":true + }, + { + "neetcode150":true, + "problem":"Two Sum II Input Array Is Sorted", + "pattern":"Two Pointers", + "link":"two-sum-ii-input-array-is-sorted/", + "video":"cQ1Oz4ckceM", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0167-two-sum-ii-input-array-is-sorted", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true, + "scala":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"3Sum", + "pattern":"Two Pointers", + "link":"3sum/", + "video":"jzZsG8n2R9A", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0015-3sum", + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true, + "scala":true, + "c":true + }, + { + "problem":"4Sum", + "pattern":"Two Pointers", + "link":"4sum/", + "video":"EYeR-_1NRlQ", + "difficulty":"Medium", + "code":"0018-4sum", + "python":true, + "javascript":true, + "typescript":true, + "go":true, + "cpp":true, + "kotlin":true, + "java":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Container With Most Water", + "pattern":"Two Pointers", + "link":"container-with-most-water/", + "video":"UuiTKBwPgAo", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0011-container-with-most-water", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true, + "scala":true, + "dart":true + }, + { + "problem":"Number of Subsequences That Satisfy The Given Sum Condition", + "pattern":"Two Pointers", + "link":"number-of-subsequences-that-satisfy-the-given-sum-condition/", + "video":"xCsIkPLS4Ls", + "difficulty":"Medium", + "code":"1498-number-of-subsequences-that-satisfy-the-given-sum-condition", + "cpp":true, + "python":true, + "kotlin":true + }, + { + "problem":"Rotate Array", + "pattern":"Two Pointers", + "link":"rotate-array/", + "video":"BHr381Guz3Y", + "difficulty":"Medium", + "code":"0189-rotate-array", + "cpp":true, + "java":true, + "python":true, + "typescript":true, + "go":true, + "kotlin":true, + "javascript":true + }, + { + "problem":"Array With Elements Not Equal to Average of Neighbors", + "pattern":"Two Pointers", + "link":"array-with-elements-not-equal-to-average-of-neighbors/", + "video":"Wmb3YdVYfqM", + "difficulty":"Medium", + "code":"1968-array-with-elements-not-equal-to-average-of-neighbors", + "c":true, + "cpp":true, + "kotlin":true, + "python":true, + "javascript":true + }, + { + "problem":"Boats to Save People", + "pattern":"Two Pointers", + "link":"boats-to-save-people/", + "video":"XbaxWuHIWUs", + "difficulty":"Medium", + "code":"0881-boats-to-save-people", + "c":true, + "cpp":true, + "typescript":true, + "javascript":true, + "python":true, + "kotlin":true, + "java":true, + "swift":true + }, + { + "neetcode150":true, + "problem":"Trapping Rain Water", + "pattern":"Two Pointers", + "link":"trapping-rain-water/", + "video":"ZI2z5pq0TqA", + "difficulty":"Hard", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0042-trapping-rain-water", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Best Time to Buy And Sell Stock", + "pattern":"Sliding Window", + "link":"best-time-to-buy-and-sell-stock/", + "video":"1pkOgXD63yU", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0121-best-time-to-buy-and-sell-stock", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true, + "scala":true + }, + { + "problem":"Contains Duplicate II", + "pattern":"Sliding Window", + "link":"contains-duplicate-ii/", + "video":"ypn0aZ0nrL4", + "difficulty":"Easy", + "python":true, + "code":"0219-contains-duplicate-ii", + "cpp":true, + "java":true, + "javascript":true, + "kotlin":true, + "rust":true + }, + { + "problem":"Number of Sub Arrays of Size K and Avg Greater than or Equal to Threshold", + "pattern":"Sliding Window", + "link":"number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold/", + "video":"D8B4tKxMTnY", + "difficulty":"Medium", + "python":true, + "code":"1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold", + "cpp":true, + "javascript":true, + "kotlin":true, + "java":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Longest Substring Without Repeating Characters", + "pattern":"Sliding Window", + "link":"longest-substring-without-repeating-characters/", + "video":"wiGpQwVHdE0", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0003-longest-substring-without-repeating-characters", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true, + "scala":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Longest Repeating Character Replacement", + "pattern":"Sliding Window", + "link":"longest-repeating-character-replacement/", + "video":"gqXU1UyA8pk", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0424-longest-repeating-character-replacement", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "kotlin":true, + "rust":true, + "swift":true + }, + { + "neetcode150":true, + "problem":"Permutation In String", + "pattern":"Sliding Window", + "link":"permutation-in-string/", + "video":"UbyhOgBN834", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0567-permutation-in-string", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "kotlin":true, + "rust":true + }, + { + "problem":"Frequency of The Most Frequent Element", + "pattern":"Sliding Window", + "link":"frequency-of-the-most-frequent-element/", + "video":"vgBrQ0NM5vE", + "difficulty":"Medium", + "code":"1838-frequency-of-the-most-frequent-element", + "csharp":true, + "javascript":true, + "typescript":true, + "go":true, + "rust":true, + "c":true, + "cpp":true, + "kotlin":true, + "java":true, + "python":true + }, + { + "problem":"Fruits into Basket", + "pattern":"Sliding Window", + "link":"fruit-into-baskets/", + "video":"yYtaV0G3mWQ", + "difficulty":"Medium", + "code":"0904-fruit-into-baskets", + "javascript":true, + "typescript":true, + "go":true, + "kotlin":true, + "rust":true, + "python":true + }, + { + "problem":"Maximum Number of Vowels in a Substring of Given Length", + "pattern":"Sliding Window", + "link":"maximum-number-of-vowels-in-a-substring-of-given-length/", + "video":"kEfPSzgL-Ss", + "difficulty":"Medium", + "code":"1456-maximum-number-of-vowels-in-a-substring-of-given-length", + "kotlin":true, + "cpp":true, + "java":true, + "python":true + }, + { + "problem":"Minimum Number of Flips to Make The Binary String Alternating", + "pattern":"Sliding Window", + "link":"minimum-number-of-flips-to-make-the-binary-string-alternating/", + "video":"MOeuK6gaC2A", + "difficulty":"Medium", + "code":"1888-minimum-number-of-flips-to-make-the-binary-string-alternating", + "javascript":true, + "typescript":true, + "kotlin":true, + "python":true + }, + { + "problem":"Minimum Size Subarray Sum", + "pattern":"Sliding Window", + "link":"minimum-size-subarray-sum/", + "video":"aYqYMIqZx5s", + "difficulty":"Medium", + "code":"0209-minimum-size-subarray-sum", + "c":true, + "cpp":true, + "javascript":true, + "go":true, + "java":true, + "kotlin":true, + "python":true + }, + { + "problem":"Find K Closest Elements", + "pattern":"Sliding Window", + "link":"find-k-closest-elements/", + "video":"o-YDQzHoaKM", + "difficulty":"Medium", + "code":"0658-find-k-closest-elements", + "python":true, + "typescript":true, + "javascript":true, + "kotlin":true + }, + { + "problem":"Minimum Operations to Reduce X to Zero", + "pattern":"Sliding Window", + "link":"minimum-operations-to-reduce-x-to-zero/", + "video":"xumn16n7njs", + "difficulty":"Medium", + "code":"1658-minimum-operations-to-reduce-x-to-zero", + "java":true, + "kotlin":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Minimum Window Substring", + "pattern":"Sliding Window", + "link":"minimum-window-substring/", + "video":"jSto0O4AJbM", + "difficulty":"Hard", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0076-minimum-window-substring", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "kotlin":true, + "rust":true, + "ruby":true, + "swift":true + }, + { + "neetcode150":true, + "problem":"Sliding Window Maximum", + "pattern":"Sliding Window", + "link":"sliding-window-maximum/", + "video":"DfljaUwZsOk", + "difficulty":"Hard", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0239-sliding-window-maximum", + "csharp":true, + "go":true, + "kotlin":true, + "typescript":true, + "ruby":true, + "c":true, + "rust":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Valid Parentheses", + "pattern":"Stack", + "link":"valid-parentheses/", + "video":"WTzjTskDFMg", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0020-valid-parentheses", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true, + "dart":true + }, + { + "problem":"Baseball Game", + "pattern":"Stack", + "link":"baseball-game/", + "video":"Id_tqGdsZQI", + "difficulty":"Easy", + "code":"0682-baseball-game", + "c":true, + "cpp":true, + "python":true, + "java":true, + "typescript":true, + "go":true, + "rust":true, + "kotlin":true, + "csharp":true, + "javascript":true, + "swift":true + }, + { + "problem":"Implement Stack Using Queues", + "pattern":"Stack", + "link":"implement-stack-using-queues/", + "video":"rW4vm0-DLYc", + "difficulty":"Easy", + "code":"0225-implement-stack-using-queues", + "cpp":true, + "go":true, + "c":true, + "java":true, + "python":true, + "javascript":true, + "typescript":true, + "rust":true, + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Min Stack", + "pattern":"Stack", + "link":"min-stack/", + "video":"qkLl7nAwDPo", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0155-min-stack", + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "kotlin":true, + "c":true, + "rust":true, + "swift":true + }, + { + "neetcode150":true, + "problem":"Evaluate Reverse Polish Notation", + "pattern":"Stack", + "link":"evaluate-reverse-polish-notation/", + "video":"iu0082c4HDE", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0150-evaluate-reverse-polish-notation", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "problem":"Removing Stars From a String", + "pattern":"Stack", + "link":"removing-stars-from-a-string/", + "video":"pRyFZIaKegA", + "difficulty":"Medium", + "code":"2390-removing-stars-from-a-string", + "kotlin":true, + "java":true, + "python":true, + "javascript":true + }, + { + "problem":"Validate Stack Sequences", + "pattern":"Stack", + "link":"validate-stack-sequences/", + "video":"mzua0r94kb8", + "difficulty":"Medium", + "code":"0946-validate-stack-sequences", + "kotlin":true, + "python":true + }, + { + "neetcode150":true, + "problem":"Generate Parentheses", + "pattern":"Stack", + "link":"generate-parentheses/", + "video":"s9fokUqJ76A", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0022-generate-parentheses", + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "kotlin":true, + "c":true, + "rust":true, + "swift":true + }, + { + "problem":"Asteroid Collision", + "pattern":"Stack", + "link":"asteroid-collision/", + "video":"LN7KjRszjk4", + "difficulty":"Medium", + "code":"0735-asteroid-collision", + "java":true, + "javascript":true, + "typescript":true, + "rust":true, + "python":true, + "cpp":true, + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Daily Temperatures", + "pattern":"Stack", + "link":"daily-temperatures/", + "video":"cTBiBSnjO3c", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0739-daily-temperatures", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "problem":"Online Stock Span", + "pattern":"Stack", + "link":"online-stock-span/", + "video":"slYh0ZNEqSw", + "difficulty":"Medium", + "code":"0901-online-stock-span", + "python":true, + "typescript":true, + "rust":true, + "cpp":true, + "java":true, + "javascript":true, + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Car Fleet", + "pattern":"Stack", + "link":"car-fleet/", + "video":"Pr6T-3yB9RM", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0853-car-fleet", + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "kotlin":true, + "rust":true, + "c":true + }, + { + "problem":"Simplify Path", + "pattern":"Stack", + "link":"simplify-path/", + "video":"qYlHrAKJfyA", + "difficulty":"Medium", + "code":"0071-simplify-path", + "python":true, + "typescript":true, + "rust":true, + "java":true, + "kotlin":true, + "javascript":true + }, + { + "problem":"Decode String", + "pattern":"Stack", + "link":"decode-string/", + "video":"qB0zZpBJlh8", + "difficulty":"Medium", + "code":"0394-decode-string", + "python":true, + "typescript":true, + "rust":true, + "scala":true, + "java":true, + "kotlin":true + }, + { + "problem":"Remove K Digits", + "pattern":"Stack", + "link":"remove-k-digits/", + "video":"cFabMOnJaq0", + "difficulty":"Medium", + "code":"0402-remove-k-digits", + "cpp":true, + "javascript":true, + "typescript":true, + "rust":true, + "kotlin":true, + "java":true, + "python":true + }, + { + "problem":"Remove All Adjacent Duplicates In String II", + "pattern":"Stack", + "link":"remove-all-adjacent-duplicates-in-string-ii/", + "video":"w6LcypDgC4w", + "difficulty":"Medium", + "code":"1209-remove-all-adjacent-duplicates-in-string-ii", + "cpp":true, + "python":true, + "javascript":true, + "typescript":true, + "rust":true, + "kotlin":true + }, + { + "problem":"132 Pattern", + "pattern":"Stack", + "link":"132-pattern/", + "video":"q5ANAl8Z458", + "difficulty":"Medium", + "code":"0456-132-pattern", + "python":true, + "cpp":true, + "java":true, + "javascript":true, + "typescript":true, + "kotlin":true, + "rust":true + }, + { + "problem":"Maximum Frequency Stack", + "pattern":"Stack", + "link":"maximum-frequency-stack/", + "video":"Z6idIicFDOE", + "difficulty":"Hard", + "code":"0895-maximum-frequency-stack", + "javascript":true, + "typescript":true, + "rust":true, + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Largest Rectangle In Histogram", + "pattern":"Stack", + "link":"largest-rectangle-in-histogram/", + "video":"zx5Sw9130L0", + "difficulty":"Hard", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0084-largest-rectangle-in-histogram", + "csharp":true, + "typescript":true, + "go":true, + "kotlin":true, + "rust":true, + "c":true + }, + { + "neetcode150":true, + "problem":"Binary Search", + "pattern":"Binary Search", + "link":"binary-search/", + "video":"s4DPM8ct1pI", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0704-binary-search", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true, + "dart":true + }, + { + "problem":"Search Insert Position", + "pattern":"Binary Search", + "link":"search-insert-position/", + "video":"K-RYzDZkzCI", + "difficulty":"Easy", + "code":"0035-search-insert-position", + "c":true, + "cpp":true, + "python":true, + "javascript":true, + "swift":true, + "csharp":true, + "java":true, + "go":true, + "kotlin":true, + "typescript":true, + "rust":true + }, + { + "problem":"Guess Number Higher Or Lower", + "pattern":"Binary Search", + "link":"guess-number-higher-or-lower/", + "video":"xW4QsTtaCa4", + "difficulty":"Easy", + "code":"0374-guess-number-higher-or-lower", + "c":true, + "python":true, + "cpp":true, + "java":true, + "javascript":true, + "go":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "problem":"Arranging Coins", + "pattern":"Binary Search", + "link":"arranging-coins/", + "video":"5rHz_6s2Buw", + "difficulty":"Easy", + "code":"0441-arranging-coins", + "python":true, + "cpp":true, + "kotlin":true, + "java":true, + "javascript":true + }, + { + "problem":"Squares of a Sorted Array", + "pattern":"Binary Search", + "link":"squares-of-a-sorted-array/", + "video":"FPCZsG_AkUg", + "difficulty":"Easy", + "code":"0977-squares-of-a-sorted-array", + "cpp":true, + "python":true, + "javascript":true, + "typescript":true, + "go":true, + "kotlin":true, + "java":true, + "rust":true + }, + { + "problem":"Valid Perfect Square", + "pattern":"Binary Search", + "link":"valid-perfect-square/", + "video":"Cg_wWPHJ2Sk", + "difficulty":"Easy", + "code":"0367-valid-perfect-square", + "python":true, + "javascript":true, + "cpp":true, + "swift":true, + "java":true, + "kotlin":true + }, + { + "problem":"Sqrt(x) ", + "pattern":"Binary Search", + "link":"sqrtx/", + "video":"zdMhGxRWutQ", + "difficulty":"Easy", + "code":"0069-sqrtx", + "kotlin":true, + "c":true, + "cpp":true, + "java":true, + "python":true, + "javascript":true + }, + { + "problem":"Single Element in a Sorted Array", + "pattern":"Binary Search", + "link":"single-element-in-a-sorted-array/", + "video":"HGtqdzyUJ3k", + "difficulty":"Medium", + "code":"0540-single-element-in-a-sorted-array", + "kotlin":true, + "cpp":true, + "java":true, + "python":true, + "javascript":true, + "typescript":true + }, + { + "problem":"Capacity to Ship Packages", + "pattern":"Binary Search", + "link":"capacity-to-ship-packages-within-d-days/", + "video":"ER_oLmdc-nw", + "difficulty":"Medium", + "code":"1011-capacity-to-ship-packages-within-d-days", + "kotlin":true, + "java":true, + "python":true + }, + { + "problem":"Find Peak Element", + "pattern":"Binary Search", + "link":"find-peak-element/", + "video":"kMzJy9es7Hc", + "difficulty":"Medium", + "code":"0162-find-peak-element", + "kotlin":true, + "cpp":true, + "java":true, + "python":true, + "javascript":true + }, + { + "problem":"Successful Pairs of Spells and Potions", + "pattern":"Binary Search", + "link":"successful-pairs-of-spells-and-potions/", + "video":"OKnm5oyAhWg", + "difficulty":"Medium", + "code":"2300-successful-pairs-of-spells-and-potions", + "kotlin":true, + "cpp":true, + "python":true + }, + { + "neetcode150":true, + "problem":"Search a 2D Matrix", + "pattern":"Binary Search", + "link":"search-a-2d-matrix/", + "video":"Ber2pi2C0j0", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0074-search-a-2d-matrix", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "problem":"Koko Eating Bananas", + "pattern":"Binary Search", + "link":"koko-eating-bananas/", + "video":"U2SozAs9RzA", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0875-koko-eating-bananas", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "problem":"Minimize the Maximum Difference of Pairs", + "pattern":"Binary Search", + "link":"minimize-the-maximum-difference-of-pairs/", + "video":"lf1Pxg7IrzQ", + "difficulty":"Medium", + "code":"2616-minimize-the-maximum-difference-of-pairs", + "java":true, + "kotlin":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Find Minimum In Rotated Sorted Array", + "pattern":"Binary Search", + "link":"find-minimum-in-rotated-sorted-array/", + "video":"nIVW4P8b1VA", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0153-find-minimum-in-rotated-sorted-array", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "swift":true, + "kotlin":true, + "rust":true, + "scala":true, + "ruby":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Search In Rotated Sorted Array", + "pattern":"Binary Search", + "link":"search-in-rotated-sorted-array/", + "video":"U8XENwh8Oy8", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0033-search-in-rotated-sorted-array", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "swift":true, + "kotlin":true, + "rust":true, + "scala":true + }, + { + "problem":"Search In Rotated Sorted Array II", + "pattern":"Binary Search", + "link":"search-in-rotated-sorted-array-ii/", + "video":"oUnF7o88_Xc", + "difficulty":"Medium", + "code":"0081-search-in-rotated-sorted-array-ii", + "java":true, + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Time Based Key Value Store", + "pattern":"Binary Search", + "link":"time-based-key-value-store/", + "video":"fu2cD_6E8Hw", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0981-time-based-key-value-store", + "csharp":true, + "typescript":true, + "go":true, + "kotlin":true, + "ruby":true, + "rust":true, + "c":true + }, + { + "problem":"Find First And Last Position of Element In Sorted Array", + "pattern":"Binary Search", + "link":"find-first-and-last-position-of-element-in-sorted-array/", + "video":"4sQL7R5ySUU", + "difficulty":"Medium", + "code":"0034-find-first-and-last-position-of-element-in-sorted-array", + "csharp":true, + "python":true, + "kotlin":true, + "cpp":true, + "java":true, + "swift":true, + "javascript":true + }, + { + "problem":"Maximum Number of Removable Characters", + "pattern":"Binary Search", + "link":"maximum-number-of-removable-characters/", + "video":"NMP3nRPyX5g", + "difficulty":"Medium", + "code":"1898-maximum-number-of-removable-characters", + "javascript":true, + "kotlin":true + }, + { + "problem":"Populating Next Right Pointers In Each Node", + "pattern":"Binary Search", + "link":"populating-next-right-pointers-in-each-node/", + "video":"U4hFQCa1Cq0", + "difficulty":"Medium", + "code":"0116-populating-next-right-pointers-in-each-node", + "go":true, + "cpp":true, + "javascript":true, + "kotlin":true + }, + { + "problem":"Search Suggestions System", + "pattern":"Binary Search", + "link":"search-suggestions-system/", + "video":"D4T2N0yAr20", + "difficulty":"Medium", + "code":"1268-search-suggestions-system", + "java":true, + "javascript":true, + "kotlin":true + }, + { + "problem":"Split Array Largest Sum", + "pattern":"Binary Search", + "link":"split-array-largest-sum/", + "video":"YUF3_eBdzsk", + "difficulty":"Hard", + "code":"0410-split-array-largest-sum", + "python":true, + "javascript":true, + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Median of Two Sorted Arrays", + "pattern":"Binary Search", + "link":"median-of-two-sorted-arrays/", + "video":"q6IEA26hvXc", + "difficulty":"Hard", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0004-median-of-two-sorted-arrays", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Reverse Linked List", + "pattern":"Linked List", + "link":"reverse-linked-list/", + "video":"G0_I-ZF0S38", + "difficulty":"Easy", + "python":true, + "cpp":true, + "javascript":true, + "java":true, + "code":"0206-reverse-linked-list", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "scala":true, + "rust":true, + "dart":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Merge Two Sorted Lists", + "pattern":"Linked List", + "link":"merge-two-sorted-lists/", + "video":"XIdigk956u0", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0021-merge-two-sorted-lists", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "scala":true, + "dart":true, + "rust":true + }, + { + "problem":"Palindrome Linked List", + "pattern":"Linked List", + "link":"palindrome-linked-list/", + "video":"yOzXms1J6Nk", + "difficulty":"Easy", + "code":"0234-palindrome-linked-list", + "cpp":true, + "javascript":true, + "python":true, + "go":true, + "java":true, + "kotlin":true, + "c":true + }, + { + "problem":"Remove Linked List Elements", + "pattern":"Linked List", + "link":"remove-linked-list-elements/", + "video":"JI71sxtHTng", + "difficulty":"Easy", + "code":"0203-remove-linked-list-elements", + "javascript":true, + "typescript":true, + "go":true, + "cpp":true, + "java":true, + "python":true, + "kotlin":true + }, + { + "problem":"Remove Duplicates From Sorted List", + "pattern":"Linked List", + "link":"remove-duplicates-from-sorted-list/", + "video":"p10f-VpO4nE", + "difficulty":"Easy", + "code":"0083-remove-duplicates-from-sorted-list", + "cpp":true, + "python":true, + "javascript":true, + "go":true, + "java":true, + "kotlin":true, + "c":true + }, + { + "problem":"Middle of the Linked List", + "pattern":"Linked List", + "link":"middle-of-the-linked-list/", + "video":"A2_ldqM4QcY", + "difficulty":"Easy", + "python":true, + "code":"0876-middle-of-the-linked-list", + "c":true, + "cpp":true, + "csharp":true, + "java":true, + "javascript":true, + "typescript":true, + "go":true, + "kotlin":true, + "rust":true, + "swift":true + }, + { + "problem":"Intersection of Two Linked Lists", + "pattern":"Linked List", + "link":"intersection-of-two-linked-lists/", + "video":"D0X0BONOQhI", + "difficulty":"Easy", + "code":"0160-intersection-of-two-linked-lists", + "python":true, + "javascript":true, + "java":true, + "go":true, + "kotlin":true, + "c":true, + "cpp":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Reorder List", + "pattern":"Linked List", + "link":"reorder-list/", + "video":"S5bfdUTrKLM", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0143-reorder-list", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "kotlin":true + }, + { + "problem":"Maximum Twin Sum Of A Linked List", + "pattern":"Linked List", + "link":"maximum-twin-sum-of-a-linked-list/", + "video":"doj95MelfSA", + "difficulty":"Medium", + "code":"2130-maximum-twin-sum-of-a-linked-list", + "python":true, + "java":true, + "kotlin":true, + "cpp":true, + "javascript":true, + "go":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Remove Nth Node From End of List", + "pattern":"Linked List", + "link":"remove-nth-node-from-end-of-list/", + "video":"XVuQxVej6y8", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0019-remove-nth-node-from-end-of-list", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "problem":"Swapping Nodes in a Linked List", + "pattern":"Linked List", + "link":"swapping-nodes-in-a-linked-list/", + "video":"4LsrgMyQIjQ", + "difficulty":"Medium", + "code":"1721-swapping-nodes-in-a-linked-list", + "kotlin":true, + "cpp":true, + "java":true, + "python":true, + "go":true + }, + { + "problem":"LFU Cache", + "pattern":"Linked List", + "link":"lfu-cache/", + "video":"bLEIHn-DgoA", + "difficulty":"Hard", + "code":"0460-lfu-cache", + "javascript":true, + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Copy List With Random Pointer", + "pattern":"Linked List", + "link":"copy-list-with-random-pointer/", + "video":"5Y2EiZST97Y", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0138-copy-list-with-random-pointer", + "c":true, + "csharp":true, + "typescript":true, + "ruby":true, + "swift":true, + "kotlin":true, + "go":true + }, + { + "problem":"Design Linked List", + "pattern":"Linked List", + "link":"design-linked-list/", + "video":"Wf4QhpdVFQo", + "difficulty":"Medium", + "python":true, + "code":"0707-design-linked-list", + "go":true, + "kotlin":true, + "c":true, + "java":true + }, + { + "problem":"Design Browser History", + "pattern":"Linked List", + "link":"design-browser-history/", + "video":"i1G-kKnBu8k", + "difficulty":"Medium", + "python":true, + "code":"1472-design-browser-history", + "javascript":true, + "typescript":true, + "go":true, + "rust":true, + "java":true, + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Add Two Numbers", + "pattern":"Linked List", + "link":"add-two-numbers/", + "video":"wgFPrzTjm7s", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0002-add-two-numbers", + "c":true, + "csharp":true, + "typescript":true, + "ruby":true, + "swift":true, + "kotlin":true, + "scala":true, + "go":true, + "rust":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Linked List Cycle", + "pattern":"Linked List", + "link":"linked-list-cycle/", + "video":"gBTe7lFR3vc", + "difficulty":"Easy", + "python":true, + "cpp":true, + "javascript":true, + "java":true, + "code":"0141-linked-list-cycle", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "scala":true + }, + { + "neetcode150":true, + "problem":"Find The Duplicate Number", + "pattern":"Linked List", + "link":"find-the-duplicate-number/", + "video":"wjYnzkAhcNk", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0287-find-the-duplicate-number", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "problem":"Swap Nodes In Pairs", + "pattern":"Linked List", + "link":"swap-nodes-in-pairs/", + "video":"o811TZLAWOo", + "difficulty":"Medium", + "code":"0024-swap-nodes-in-pairs", + "python":true, + "go":true, + "java":true, + "kotlin":true, + "c":true, + "cpp":true + }, + { + "problem":"Sort List", + "pattern":"Linked List", + "link":"sort-list/", + "video":"TGveA1oFhrc", + "difficulty":"Medium", + "code":"0148-sort-list", + "java":true, + "python":true, + "c":true, + "cpp":true, + "kotlin":true + }, + { + "problem":"Partition List", + "pattern":"Linked List", + "link":"partition-list/", + "video":"KT1iUciJr4g", + "difficulty":"Medium", + "code":"0086-partition-list", + "python":true, + "java":true, + "kotlin":true + }, + { + "problem":"Rotate List", + "pattern":"Linked List", + "link":"rotate-list/", + "video":"UcGtPs2LE_c", + "difficulty":"Medium", + "code":"0061-rotate-list", + "python":true, + "cpp":true, + "java":true, + "kotlin":true + }, + { + "problem":"Reverse Linked List II", + "pattern":"Linked List", + "link":"reverse-linked-list-ii/", + "video":"RF_M9tX4Eag", + "difficulty":"Medium", + "code":"0092-reverse-linked-list-ii", + "python":true, + "javascript":true, + "cpp":true, + "java":true, + "kotlin":true + }, + { + "problem":"Design Circular Queue", + "pattern":"Linked List", + "link":"design-circular-queue/", + "video":"aBbsfn863oA", + "difficulty":"Medium", + "code":"0622-design-circular-queue", + "python":true, + "go":true, + "kotlin":true, + "java":true + }, + { + "problem":"Insertion Sort List", + "pattern":"Linked List", + "link":"insertion-sort-list/", + "video":"Kk6mXAzqX3Y", + "difficulty":"Medium", + "code":"0147-insertion-sort-list", + "python":true, + "cpp":true, + "kotlin":true + }, + { + "problem":"Split Linked List in Parts", + "pattern":"Linked List", + "link":"split-linked-list-in-parts/", + "video":"-OTlqdrxrVI", + "difficulty":"Medium", + "code":"0725-split-linked-list-in-parts", + "java":true, + "kotlin":true + }, + { + "neetcode150":true, + "problem":"LRU Cache", + "pattern":"Linked List", + "link":"lru-cache/", + "video":"7ABFKPK2hD4", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0146-lru-cache", + "c":true, + "csharp":true, + "ruby":true, + "kotlin":true, + "go":true, + "typescript":true, + "swift":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Merge K Sorted Lists", + "pattern":"Linked List", + "link":"merge-k-sorted-lists/", + "video":"q5a5OiGbT6Q", + "difficulty":"Hard", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0023-merge-k-sorted-lists", + "c":true, + "csharp":true, + "typescript":true, + "kotlin":true, + "go":true, + "swift":true, + "rust":true + }, + { + "neetcode150":true, + "problem":"Reverse Nodes In K Group", + "pattern":"Linked List", + "link":"reverse-nodes-in-k-group/", + "video":"1UOPsfP85V4", + "difficulty":"Hard", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0025-reverse-nodes-in-k-group", + "c":true, + "csharp":true, + "typescript":true, + "kotlin":true, + "go":true, + "swift":true, + "rust":true + }, + { + "problem":"Binary Tree Inorder Traversal", + "pattern":"Trees", + "link":"binary-tree-inorder-traversal/", + "video":"g_S5WuasWUE", + "difficulty":"Easy", + "code":"0094-binary-tree-inorder-traversal", + "c":true, + "python":true, + "javascript":true, + "typescript":true, + "ruby":true, + "csharp":true, + "java":true, + "go":true, + "swift":true, + "kotlin":true, + "rust":true, + "cpp":true + }, + { + "problem":"Binary Tree Preorder Traversal", + "pattern":"Trees", + "link":"binary-tree-preorder-traversal/", + "video":"afTpieEZXck", + "difficulty":"Easy", + "code":"0144-binary-tree-preorder-traversal", + "python":true, + "cpp":true, + "typescript":true, + "kotlin":true + }, + { + "problem":"Binary Tree Postorder Traversal", + "pattern":"Trees", + "link":"binary-tree-postorder-traversal/", + "video":"QhszUQhGGlA", + "difficulty":"Easy", + "code":"0145-binary-tree-postorder-traversal", + "python":true, + "java":true, + "cpp":true, + "typescript":true, + "kotlin":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Invert Binary Tree", + "pattern":"Trees", + "link":"invert-binary-tree/", + "video":"OnSn2XEQ4MY", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0226-invert-binary-tree", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Maximum Depth of Binary Tree", + "pattern":"Trees", + "link":"maximum-depth-of-binary-tree/", + "video":"hTM3phVI6YQ", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0104-maximum-depth-of-binary-tree", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "problem":"Diameter of Binary Tree", + "pattern":"Trees", + "link":"diameter-of-binary-tree/", + "video":"bkxqA8Rfv04", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0543-diameter-of-binary-tree", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "problem":"Balanced Binary Tree", + "pattern":"Trees", + "link":"balanced-binary-tree/", + "video":"QfJsau0ItOY", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0110-balanced-binary-tree", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Same Tree", + "pattern":"Trees", + "link":"same-tree/", + "video":"vRbbcKXCxOw", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0100-same-tree", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Subtree of Another Tree", + "pattern":"Trees", + "link":"subtree-of-another-tree/", + "video":"E36O5SWp-LE", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0572-subtree-of-another-tree", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "scala":true, + "rust":true + }, + { + "problem":"Convert Sorted Array to Binary Search Tree", + "pattern":"Trees", + "link":"convert-sorted-array-to-binary-search-tree/", + "video":"0K0uCMYq5ng", + "difficulty":"Easy", + "code":"0108-convert-sorted-array-to-binary-search-tree", + "c":true, + "javascript":true, + "go":true, + "kotlin":true, + "java":true, + "python":true + }, + { + "problem":"Merge Two Binary Trees", + "pattern":"Trees", + "link":"merge-two-binary-trees/", + "video":"QHH6rIK3dDQ", + "difficulty":"Easy", + "code":"0617-merge-two-binary-trees", + "c":true, + "java":true, + "python":true, + "javascript":true, + "go":true, + "dart":true, + "kotlin":true, + "cpp":true + }, + { + "problem":"Path Sum", + "pattern":"Trees", + "link":"path-sum/", + "video":"LSKQyOz_P8I", + "difficulty":"Easy", + "code":"0112-path-sum", + "go":true, + "c":true, + "csharp":true, + "javascript":true, + "swift":true, + "java":true, + "python":true, + "kotlin":true + }, + { + "problem":"Construct String From Binary Tree", + "pattern":"Trees", + "link":"construct-string-from-binary-tree/", + "video":"b1WpYxnuebQ", + "difficulty":"Easy", + "code":"0606-construct-string-from-binary-tree", + "java":true, + "python":true, + "kotlin":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Lowest Common Ancestor of a Binary Search Tree", + "pattern":"Trees", + "link":"lowest-common-ancestor-of-a-binary-search-tree/", + "video":"gs2LMfuOR9k", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0235-lowest-common-ancestor-of-a-binary-search-tree", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "scala":true, + "rust":true + }, + { + "problem":"Insert into a Binary Search Tree", + "pattern":"Trees", + "link":"insert-into-a-binary-search-tree/", + "video":"Cpg8f79luEA", + "difficulty":"Medium", + "python":true, + "code":"0701-insert-into-a-binary-search-tree", + "kotlin":true, + "cpp":true, + "csharp":true, + "java":true, + "typescript":true + }, + { + "problem":"Delete Node in a BST", + "pattern":"Trees", + "link":"delete-node-in-a-bst/", + "video":"LFzAoJJt92M", + "difficulty":"Medium", + "python":true, + "code":"0450-delete-node-in-a-bst", + "kotlin":true, + "cpp":true, + "java":true, + "typescript":true, + "rust":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Binary Tree Level Order Traversal", + "pattern":"Trees", + "link":"binary-tree-level-order-traversal/", + "video":"6ZnyEApgFYg", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0102-binary-tree-level-order-traversal", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "problem":"Binary Tree Right Side View", + "pattern":"Trees", + "link":"binary-tree-right-side-view/", + "video":"d4zLyf32e3I", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0199-binary-tree-right-side-view", + "c":true, + "csharp":true, + "typescript":true, + "swift":true, + "kotlin":true, + "go":true, + "rust":true + }, + { + "problem":"Minimum Distance between BST Nodes", + "pattern":"Trees", + "link":"minimum-distance-between-bst-nodes/", + "video":"joxx4hTYwcw", + "difficulty":"Easy", + "code":"0783-minimum-distance-between-bst-nodes", + "kotlin":true, + "python":true + }, + { + "problem":"Symmetric Tree ", + "pattern":"Trees", + "link":"symmetric-tree/", + "video":"Mao9uzxwvmc", + "difficulty":"Easy", + "code":"0101-symmetric-tree", + "kotlin":true, + "python":true + }, + { + "problem":"Minimum Time to Collect All Apples in a Tree", + "pattern":"Trees", + "link":"minimum-time-to-collect-all-apples-in-a-tree/", + "video":"Xdt5Z583auM", + "difficulty":"Medium", + "code":"1443-minimum-time-to-collect-all-apples-in-a-tree", + "rust":true, + "kotlin":true + }, + { + "problem":"Binary Tree Zigzag Level Order Traversal", + "pattern":"Trees", + "link":"binary-tree-zigzag-level-order-traversal/", + "video":"igbboQbiwqw", + "difficulty":"Medium", + "code":"0103-binary-tree-zigzag-level-order-traversal", + "kotlin":true, + "cpp":true, + "python":true + }, + { + "problem":"Construct Quad Tree", + "pattern":"Trees", + "link":"construct-quad-tree/", + "video":"UQ-1sBMV0v4", + "difficulty":"Medium", + "code":"0427-construct-quad-tree", + "kotlin":true + }, + { + "problem":"Find Duplicate Subtrees", + "pattern":"Trees", + "link":"find-duplicate-subtrees/", + "video":"kn0Z5_qPPzY", + "difficulty":"Medium", + "code":"0652-find-duplicate-subtrees", + "kotlin":true + }, + { + "problem":"Check Completeness of a Binary Tree", + "pattern":"Trees", + "link":"check-completeness-of-a-binary-tree/", + "video":"olbiZ-EOSig", + "difficulty":"Medium", + "code":"0958-check-completeness-of-a-binary-tree", + "kotlin":true, + "cpp":true + }, + { + "problem":"Construct Binary Tree from Inorder and Postorder Traversal", + "pattern":"Trees", + "link":"construct-binary-tree-from-inorder-and-postorder-traversal/", + "video":"vm63HuIU7kw", + "difficulty":"Medium", + "code":"0106-construct-binary-tree-from-inorder-and-postorder-traversal", + "java":true, + "kotlin":true + }, + { + "problem":"Maximum Width of Binary Tree ", + "pattern":"Trees", + "link":"maximum-width-of-binary-tree/", + "video":"FPzLE2L7uHs", + "difficulty":"Medium", + "code":"0662-maximum-width-of-binary-tree", + "java":true, + "kotlin":true + }, + { + "problem":"Time Needed to Inform All Employees ", + "pattern":"Trees", + "link":"time-needed-to-inform-all-employees/", + "video":"zdBYi0p4L5Q", + "difficulty":"Medium", + "code":"1376-time-needed-to-inform-all-employees", + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Count Good Nodes In Binary Tree", + "pattern":"Trees", + "link":"count-good-nodes-in-binary-tree/", + "video":"7cp5imvDzl4", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"1448-count-good-nodes-in-binary-tree", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Validate Binary Search Tree", + "pattern":"Trees", + "link":"validate-binary-search-tree/", + "video":"s6ATEkipzow", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0098-validate-binary-search-tree", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Kth Smallest Element In a Bst", + "pattern":"Trees", + "link":"kth-smallest-element-in-a-bst/", + "video":"5LUXSvjmGCw", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0230-kth-smallest-element-in-a-bst", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "swift":true, + "kotlin":true, + "scala":true, + "rust":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Construct Binary Tree From Preorder And Inorder Traversal", + "pattern":"Trees", + "link":"construct-binary-tree-from-preorder-and-inorder-traversal/", + "video":"ihj4IQGZ2zc", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0105-construct-binary-tree-from-preorder-and-inorder-traversal", + "c":true, + "csharp":true, + "typescript":true, + "kotlin":true, + "go":true + }, + { + "problem":"Unique Binary Search Trees", + "pattern":"Trees", + "link":"unique-binary-search-trees/", + "video":"Ox0TenN3Zpg", + "difficulty":"Medium", + "code":"0096-unique-binary-search-trees", + "c":true, + "java":true, + "kotlin":true + }, + { + "problem":"Unique Binary Search Trees II", + "pattern":"Trees", + "link":"unique-binary-search-trees-ii/", + "video":"m907FlQa2Yc", + "difficulty":"Medium", + "code":"0095-unique-binary-search-trees-ii", + "kotlin":true + }, + { + "problem":"Sum Root to Leaf Numbers", + "pattern":"Trees", + "link":"sum-root-to-leaf-numbers/", + "video":"Jk16lZGFWxE", + "difficulty":"Medium", + "code":"0129-sum-root-to-leaf-numbers", + "c":true, + "java":true, + "cpp":true, + "kotlin":true + }, + { + "problem":"House Robber III", + "pattern":"Trees", + "link":"house-robber-iii/", + "video":"nHR8ytpzz7c", + "difficulty":"Medium", + "code":"0337-house-robber-iii", + "java":true, + "kotlin":true + }, + { + "problem":"Flip Equivalent Binary Trees", + "pattern":"Trees", + "link":"flip-equivalent-binary-trees/", + "video":"izRDc1il9Pk", + "difficulty":"Medium", + "code":"0951-flip-equivalent-binary-trees", + "java":true, + "kotlin":true + }, + { + "problem":"Operations On Tree", + "pattern":"Trees", + "link":"operations-on-tree/", + "video":"qK4PtjrVD0U", + "difficulty":"Medium", + "code":"1993-operations-on-tree", + "kotlin":true + }, + { + "problem":"All Possible Full Binary Trees", + "pattern":"Trees", + "link":"all-possible-full-binary-trees/", + "video":"nZtrZPTTCAo", + "difficulty":"Medium", + "code":"0894-all-possible-full-binary-trees", + "python":true, + "java":true, + "kotlin":true + }, + { + "problem":"Find Bottom Left Tree Value", + "pattern":"Trees", + "link":"find-bottom-left-tree-value/", + "video":"u_by_cTsNJA", + "difficulty":"Medium", + "code":"0513-find-bottom-left-tree-value", + "java":true, + "kotlin":true + }, + { + "problem":"Trim a Binary Search Tree", + "pattern":"Trees", + "link":"trim-a-binary-search-tree/", + "video":"jwt5mTjEXGc", + "difficulty":"Medium", + "code":"0669-trim-a-binary-search-tree", + "python":true, + "javascript":true, + "typescript":true, + "go":true, + "java":true, + "kotlin":true + }, + { + "problem":"Binary Search Tree Iterator", + "pattern":"Trees", + "link":"binary-search-tree-iterator/", + "video":"RXy5RzGF5wo", + "difficulty":"Medium", + "code":"0173-binary-search-tree-iterator", + "java":true, + "javascript":true, + "kotlin":true + }, + { + "problem":"Convert Bst to Greater Tree", + "pattern":"Trees", + "link":"convert-bst-to-greater-tree/", + "video":"7vVEJwVvAlI", + "difficulty":"Medium", + "code":"0538-convert-bst-to-greater-tree", + "cpp":true, + "kotlin":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Binary Tree Maximum Path Sum", + "pattern":"Trees", + "link":"binary-tree-maximum-path-sum/", + "video":"Hr5cWUld4vU", + "difficulty":"Hard", + "code":"0124-binary-tree-maximum-path-sum", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Serialize And Deserialize Binary Tree", + "pattern":"Trees", + "link":"serialize-and-deserialize-binary-tree/", + "video":"u4JAi2JJhI8", + "difficulty":"Hard", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0297-serialize-and-deserialize-binary-tree", + "c":true, + "csharp":true, + "kotlin":true, + "go":true, + "typescript":true, + "swift":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Implement Trie Prefix Tree", + "pattern":"Tries", + "link":"implement-trie-prefix-tree/", + "video":"oobqoCJlHA0", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0208-implement-trie-prefix-tree", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true, + "scala":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Design Add And Search Words Data Structure", + "pattern":"Tries", + "link":"design-add-and-search-words-data-structure/", + "video":"BTf05gs_8iU", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0211-design-add-and-search-words-data-structure", + "csharp":true, + "typescript":true, + "ruby":true, + "kotlin":true, + "rust":true, + "go":true, + "c":true, + "scala":true + }, + { + "problem":"Extra Characters in a String", + "pattern":"Tries", + "link":"extra-characters-in-a-string/", + "video":"ONstwO1cD7c", + "difficulty":"Medium", + "code":"2707-extra-characters-in-a-string", + "cpp":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Word Search II", + "pattern":"Tries", + "link":"word-search-ii/", + "video":"asbcE9mZz_U", + "difficulty":"Hard", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0212-word-search-ii", + "csharp":true, + "swift":true, + "kotlin":true, + "rust":true, + "go":true, + "c":true + }, + { + "neetcode150":true, + "problem":"Kth Largest Element In a Stream", + "pattern":"Heap / Priority Queue", + "link":"kth-largest-element-in-a-stream/", + "video":"hOjcdrqMoQ8", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0703-kth-largest-element-in-a-stream", + "c":true, + "csharp":true, + "ruby":true, + "swift":true, + "go":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "problem":"Last Stone Weight", + "pattern":"Heap / Priority Queue", + "link":"last-stone-weight/", + "video":"B-QCq79-Vfw", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"1046-last-stone-weight", + "csharp":true, + "typescript":true, + "ruby":true, + "kotlin":true, + "go":true, + "rust":true, + "c":true + }, + { + "neetcode150":true, + "problem":"K Closest Points to Origin", + "pattern":"Heap / Priority Queue", + "link":"k-closest-points-to-origin/", + "video":"rI2EBUEMfTk", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0973-k-closest-points-to-origin", + "csharp":true, + "kotlin":true, + "go":true, + "rust":true, + "c":true + }, + { + "neetcode150":true, + "problem":"Kth Largest Element In An Array", + "pattern":"Heap / Priority Queue", + "link":"kth-largest-element-in-an-array/", + "video":"XEmy13g1Qxc", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0215-kth-largest-element-in-an-array", + "csharp":true, + "typescript":true, + "kotlin":true, + "go":true, + "scala":true, + "c":true, + "rust":true + }, + { + "neetcode150":true, + "problem":"Task Scheduler", + "pattern":"Heap / Priority Queue", + "link":"task-scheduler/", + "video":"s8p8ukTyA2I", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0621-task-scheduler", + "csharp":true, + "typescript":true, + "kotlin":true, + "rust":true, + "c":true + }, + { + "neetcode150":true, + "problem":"Design Twitter", + "pattern":"Heap / Priority Queue", + "link":"design-twitter/", + "video":"pNichitDD2E", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0355-design-twitter", + "csharp":true, + "go":true, + "kotlin":true + }, + { + "problem":"Minimize Deviation in Array", + "pattern":"Heap / Priority Queue", + "link":"minimize-deviation-in-array/", + "video":"boHNFptxo2A", + "difficulty":"Hard", + "code":"1675-minimize-deviation-in-array", + "kotlin":true, + "cpp":true + }, + { + "problem":"Maximum Subsequence Score", + "pattern":"Heap / Priority Queue", + "link":"maximum-subsequence-score/", + "video":"ax1DKi5lJwk", + "difficulty":"Medium", + "code":"2542-maximum-subsequence-score", + "kotlin":true, + "cpp":true + }, + { + "problem":"Single Threaded Cpu", + "pattern":"Heap / Priority Queue", + "link":"single-threaded-cpu/", + "video":"RR1n-d4oYqE", + "difficulty":"Medium", + "code":"1834-single-threaded-cpu", + "java":true, + "python":true, + "javascript":true, + "kotlin":true, + "rust":true + }, + { + "problem":"Seat Reservation Manager", + "pattern":"Heap / Priority Queue", + "link":"seat-reservation-manager/", + "video":"ahobllKXEEY", + "difficulty":"Medium", + "code":"1845-seat-reservation-manager", + "python":true, + "cpp":true, + "kotlin":true + }, + { + "problem":"Process Tasks Using Servers", + "pattern":"Heap / Priority Queue", + "link":"process-tasks-using-servers/", + "video":"XKA22PecuMQ", + "difficulty":"Medium", + "code":"1882-process-tasks-using-servers", + "kotlin":true + }, + { + "problem":"Find The Kth Largest Integer In The Array", + "pattern":"Heap / Priority Queue", + "link":"find-the-kth-largest-integer-in-the-array/", + "video":"lRCaNiqO3xI", + "difficulty":"Medium", + "code":"1985-find-the-kth-largest-integer-in-the-array", + "java":true, + "python":true, + "swift":true, + "cpp":true, + "go":true, + "kotlin":true + }, + { + "problem":"Reorganize String", + "pattern":"Heap / Priority Queue", + "link":"reorganize-string/", + "video":"2g_b1aYTHeg", + "difficulty":"Medium", + "code":"0767-reorganize-string", + "java":true, + "python":true, + "cpp":true, + "kotlin":true + }, + { + "problem":"Longest Happy String", + "pattern":"Heap / Priority Queue", + "link":"longest-happy-string/", + "video":"8u-H6O_XQKE", + "difficulty":"Medium", + "code":"1405-longest-happy-string", + "kotlin":true + }, + { + "problem":"Car Pooling", + "pattern":"Heap / Priority Queue", + "link":"car-pooling/", + "video":"08sn_w4LWEE", + "difficulty":"Medium", + "code":"1094-car-pooling", + "csharp":true, + "cpp":true, + "kotlin":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Find Median From Data Stream", + "pattern":"Heap / Priority Queue", + "link":"find-median-from-data-stream/", + "video":"itmhHWaHupI", + "difficulty":"Hard", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0295-find-median-from-data-stream", + "csharp":true, + "kotlin":true, + "go":true, + "typescript":true + }, + { + "problem":"Maximum Performance of a Team", + "pattern":"Heap / Priority Queue", + "link":"maximum-performance-of-a-team/", + "video":"Y7UTvogADH0", + "difficulty":"Hard", + "code":"1383-maximum-performance-of-a-team", + "csharp":true, + "python":true, + "kotlin":true + }, + { + "problem":"IPO", + "pattern":"Heap / Priority Queue", + "link":"ipo/", + "video":"1IUzNJ6TPEM", + "difficulty":"Hard", + "code":"0502-ipo", + "python":true, + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Subsets", + "pattern":"Backtracking", + "link":"subsets/", + "video":"REOH22Xwdkk", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0078-subsets", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Combination Sum", + "pattern":"Backtracking", + "link":"combination-sum/", + "video":"GBKI9VSKdGg", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0039-combination-sum", + "csharp":true, + "typescript":true, + "go":true, + "kotlin":true, + "ruby":true, + "rust":true, + "c":true, + "swift":true + }, + { + "problem":"Combinations", + "pattern":"Backtracking", + "link":"combinations/", + "video":"q0s6m7AiM7o", + "difficulty":"Medium", + "code":"0077-combinations", + "python":true, + "go":true, + "kotlin":true, + "java":true + }, + { + "neetcode150":true, + "problem":"Permutations", + "pattern":"Backtracking", + "link":"permutations/", + "video":"s7AvT7cGdSo", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0046-permutations", + "csharp":true, + "typescript":true, + "go":true, + "swift":true, + "kotlin":true, + "ruby":true, + "rust":true, + "c":true + }, + { + "neetcode150":true, + "problem":"Subsets II", + "pattern":"Backtracking", + "link":"subsets-ii/", + "video":"Vn2v6ajA7U0", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0090-subsets-ii", + "csharp":true, + "typescript":true, + "go":true, + "kotlin":true, + "ruby":true, + "rust":true, + "c":true + }, + { + "neetcode150":true, + "problem":"Combination Sum II", + "pattern":"Backtracking", + "link":"combination-sum-ii/", + "video":"rSA3t6BDDwg", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0040-combination-sum-ii", + "csharp":true, + "typescript":true, + "go":true, + "kotlin":true, + "ruby":true, + "c":true + }, + { + "problem":"Permutations II", + "pattern":"Backtracking", + "link":"permutations-ii/", + "video":"qhBVWf0YafA", + "difficulty":"Medium", + "code":"0047-permutations-ii", + "python":true, + "go":true, + "kotlin":true, + "java":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Word Search", + "pattern":"Backtracking", + "link":"word-search/", + "video":"pfiQ_PS1g8E", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0079-word-search", + "c":true, + "csharp":true, + "go":true, + "swift":true, + "kotlin":true, + "rust":true, + "ruby":true + }, + { + "neetcode150":true, + "problem":"Palindrome Partitioning", + "pattern":"Backtracking", + "link":"palindrome-partitioning/", + "video":"3jvWodd7ht0", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0131-palindrome-partitioning", + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "rust":true, + "swift":true, + "kotlin":true + }, + { + "problem":"Restore IP Addresses", + "pattern":"Backtracking", + "link":"restore-ip-addresses/", + "video":"61tN4YEdiTM", + "difficulty":"Medium", + "code":"0093-restore-ip-addresses", + "javascript":true, + "typescript":true, + "go":true, + "rust":true, + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Letter Combinations of a Phone Number", + "pattern":"Backtracking", + "link":"letter-combinations-of-a-phone-number/", + "video":"0snEunUacZY", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0017-letter-combinations-of-a-phone-number", + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "rust":true, + "kotlin":true, + "swift":true + }, + { + "problem":"Matchsticks to Square", + "pattern":"Backtracking", + "link":"matchsticks-to-square/", + "video":"hUe0cUKV-YY", + "difficulty":"Medium", + "code":"0473-matchsticks-to-square", + "cpp":true, + "python":true, + "javascript":true, + "java":true, + "kotlin":true + }, + { + "problem":"Splitting a String Into Descending Consecutive Values", + "pattern":"Backtracking", + "link":"splitting-a-string-into-descending-consecutive-values/", + "video":"eDtMmysldaw", + "difficulty":"Medium", + "code":"1849-splitting-a-string-into-descending-consecutive-values", + "python":true, + "cpp":true, + "kotlin":true + }, + { + "problem":"Find Unique Binary String", + "pattern":"Backtracking", + "link":"find-unique-binary-string/", + "video":"aHqn4Dynd1k", + "difficulty":"Medium", + "code":"1980-find-unique-binary-string", + "python":true, + "kotlin":true, + "java":true + }, + { + "problem":"Maximum Length of a Concatenated String With Unique Characters", + "pattern":"Backtracking", + "link":"maximum-length-of-a-concatenated-string-with-unique-characters/", + "video":"d4SPuvkaeoo", + "difficulty":"Medium", + "code":"1239-maximum-length-of-a-concatenated-string-with-unique-characters", + "python":true, + "kotlin":true + }, + { + "problem":"Partition to K Equal Sum Subsets", + "pattern":"Backtracking", + "link":"partition-to-k-equal-sum-subsets/", + "video":"mBk4I0X46oI", + "difficulty":"Medium", + "code":"0698-partition-to-k-equal-sum-subsets", + "kotlin":true, + "python":true, + "java":true + }, + { + "neetcode150":true, + "problem":"N Queens", + "pattern":"Backtracking", + "link":"n-queens/", + "video":"Ph95IHmRp5M", + "difficulty":"Hard", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0051-n-queens", + "csharp":true, + "typescript":true, + "go":true, + "kotlin":true, + "c":true, + "rust":true + }, + { + "problem":"N Queens II", + "pattern":"Backtracking", + "link":"n-queens-ii/", + "video":"nalYyLZgvCY", + "difficulty":"Hard", + "code":"0052-n-queens-ii", + "c":true, + "cpp":true, + "javascript":true, + "python":true, + "kotlin":true + }, + { + "problem":"Island Perimeter", + "pattern":"Graphs", + "link":"island-perimeter/", + "video":"fISIuAFRM2s", + "difficulty":"Easy", + "code":"0463-island-perimeter", + "c":true, + "cpp":true, + "csharp":true, + "python":true, + "java":true, + "javascript":true, + "go":true, + "kotlin":true + }, + { + "problem":"Verifying An Alien Dictionary", + "pattern":"Graphs", + "link":"verifying-an-alien-dictionary/", + "video":"OVgPAJIyX6o", + "difficulty":"Easy", + "code":"0953-verifying-an-alien-dictionary", + "c":true, + "cpp":true, + "java":true, + "python":true, + "javascript":true, + "go":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Number of Islands", + "pattern":"Graphs", + "link":"number-of-islands/", + "video":"pV2kpPD66nE", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0200-number-of-islands", + "c":true, + "csharp":true, + "typescript":true, + "ruby":true, + "swift":true, + "kotlin":true, + "go":true, + "rust":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Clone Graph", + "pattern":"Graphs", + "link":"clone-graph/", + "video":"mQeF6bN8hMk", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0133-clone-graph", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Max Area of Island", + "pattern":"Graphs", + "link":"max-area-of-island/", + "video":"iJGr1OtmH0c", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0695-max-area-of-island", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "kotlin":true, + "rust":true + }, + { + "problem":"Count Sub Islands", + "pattern":"Graphs", + "link":"count-sub-islands/", + "video":"mLpW3qfbNJ8", + "difficulty":"Medium", + "code":"1905-count-sub-islands", + "c":true, + "csharp":true, + "python":true, + "cpp":true, + "java":true, + "javascript":true, + "go":true, + "kotlin":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Pacific Atlantic Water Flow", + "pattern":"Graphs", + "link":"pacific-atlantic-water-flow/", + "video":"s-VkcjHqkGI", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0417-pacific-atlantic-water-flow", + "c":true, + "csharp":true, + "kotlin":true, + "typescript":true, + "go":true, + "rust":true + }, + { + "neetcode150":true, + "problem":"Surrounded Regions", + "pattern":"Graphs", + "link":"surrounded-regions/", + "video":"9z2BunfoZ5Y", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0130-surrounded-regions", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "swift":true, + "kotlin":true + }, + { + "problem":"Reorder Routes to Make All Paths Lead to The City Zero", + "pattern":"Graphs", + "link":"reorder-routes-to-make-all-paths-lead-to-the-city-zero/", + "video":"m17yOR5_PpI", + "difficulty":"Medium", + "code":"1466-reorder-routes-to-make-all-paths-lead-to-the-city-zero", + "csharp":true, + "cpp":true, + "kotlin":true, + "java":true + }, + { + "neetcode150":true, + "problem":"Rotting Oranges", + "pattern":"Graphs", + "link":"rotting-oranges/", + "video":"y704fEOx0s0", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0994-rotting-oranges", + "c":true, + "csharp":true, + "typescript":true, + "kotlin":true, + "go":true + }, + { + "neetcode150":true, + "problem":"Walls And Gates", + "premium":true, + "freeLink":"https://www.lintcode.com/problem/663/", + "pattern":"Graphs", + "link":"walls-and-gates/", + "video":"e69C6xhiSQE", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0286-walls-and-gates", + "csharp":true + }, + { + "problem":"Snakes And Ladders", + "pattern":"Graphs", + "link":"snakes-and-ladders/", + "video":"6lH4nO3JfLk", + "difficulty":"Medium", + "code":"0909-snakes-and-ladders", + "csharp":true, + "python":true, + "java":true, + "kotlin":true + }, + { + "problem":"Open The Lock", + "pattern":"Graphs", + "link":"open-the-lock/", + "video":"Pzg3bCDY87w", + "difficulty":"Medium", + "code":"0752-open-the-lock", + "csharp":true, + "java":true, + "python":true, + "kotlin":true + }, + { + "problem":"Find Eventual Safe States", + "pattern":"Graphs", + "link":"find-eventual-safe-states/", + "video":"Re_v0j0CRsg", + "difficulty":"Medium", + "code":"0802-find-eventual-safe-states", + "kotlin":true, + "java":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Course Schedule", + "pattern":"Graphs", + "link":"course-schedule/", + "video":"EgI5nU9etnU", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0207-course-schedule", + "csharp":true, + "typescript":true, + "go":true, + "swift":true, + "kotlin":true, + "c":true + }, + { + "neetcode150":true, + "problem":"Course Schedule II", + "pattern":"Graphs", + "link":"course-schedule-ii/", + "video":"Akt3glAwyfY", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0210-course-schedule-ii", + "csharp":true, + "typescript":true, + "go":true, + "kotlin":true + }, + { + "problem":"Course Schedule IV", + "pattern":"Graphs", + "link":"course-schedule-iv/", + "video":"cEW05ofxhn0", + "difficulty":"Medium", + "python":true, + "code":"1462-course-schedule-iv", + "kotlin":true + }, + { + "problem":"Check if Move Is Legal", + "pattern":"Graphs", + "link":"check-if-move-is-legal/", + "video":"KxK33AcQZpQ", + "difficulty":"Medium", + "code":"1958-check-if-move-is-legal", + "cpp":true, + "java":true, + "python":true, + "javascript":true, + "go":true, + "kotlin":true + }, + { + "problem":"Shortest Bridge", + "pattern":"Graphs", + "link":"shortest-bridge/", + "video":"gkINMhbbIbU", + "difficulty":"Medium", + "code":"0934-shortest-bridge", + "kotlin":true, + "javascript":true + }, + { + "problem":"Shortest Path in Binary Matrix", + "pattern":"Graphs", + "video":"YnxUdAO7TAo", + "link":"shortest-path-in-binary-matrix/", + "difficulty":"Medium", + "code":"1091-shortest-path-in-binary-matrix", + "python":true, + "java":true, + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Redundant Connection", + "pattern":"Graphs", + "link":"redundant-connection/", + "video":"FXWRE67PLL0", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0684-redundant-connection", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Number of Connected Components In An Undirected Graph", + "premium":true, + "freeLink":"https://www.lintcode.com/problem/3651/", + "pattern":"Graphs", + "link":"number-of-connected-components-in-an-undirected-graph/", + "video":"8f1XPm4WOUc", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0323-number-of-connected-components-in-an-undirected-graph", + "csharp":true, + "go":true, + "swift":true, + "kotlin":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Graph Valid Tree", + "premium":true, + "freeLink":"https://www.lintcode.com/problem/178/", + "pattern":"Graphs", + "link":"graph-valid-tree/", + "video":"bXsUuownnoQ", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0261-graph-valid-tree", + "csharp":true, + "typescript":true, + "swift":true + }, + { + "problem":"Accounts Merge", + "pattern":"Graphs", + "link":"accounts-merge/", + "video":"6st4IxEF-90", + "difficulty":"Medium", + "code":"0721-accounts-merge", + "python":true, + "kotlin":true, + "java":true + }, + { + "problem":"Find Closest Node to Given Two Nodes", + "pattern":"Graphs", + "link":"find-closest-node-to-given-two-nodes/", + "video":"AZA8orksO4w", + "difficulty":"Medium", + "code":"2359-find-closest-node-to-given-two-nodes", + "kotlin":true + }, + { + "problem":"As Far from Land as Possible", + "pattern":"Graphs", + "link":"as-far-from-land-as-possible/", + "video":"fjxb1hQfrZk", + "difficulty":"Medium", + "code":"1162-as-far-from-land-as-possible", + "kotlin":true + }, + { + "problem":"Shortest Path with Alternating Colors", + "pattern":"Graphs", + "link":"shortest-path-with-alternating-colors/", + "video":"69rcy6lb-HQ", + "difficulty":"Medium", + "code":"1129-shortest-path-with-alternating-colors", + "kotlin":true + }, + { + "problem":"Minimum Fuel Cost to Report to the Capital", + "pattern":"Graphs", + "link":"minimum-fuel-cost-to-report-to-the-capital/", + "video":"I3lnDUIzIG4", + "difficulty":"Medium", + "code":"2477-minimum-fuel-cost-to-report-to-the-capital", + "kotlin":true, + "java":true + }, + { + "problem":"Minimum Score of a Path Between Two Cities", + "pattern":"Graphs", + "link":"minimum-score-of-a-path-between-two-cities/", + "video":"K7-mXA0irhY", + "difficulty":"Medium", + "code":"2492-minimum-score-of-a-path-between-two-cities", + "kotlin":true + }, + { + "problem":"Number of Closed Islands", + "pattern":"Graphs", + "link":"number-of-closed-islands/", + "video":"X8k48xek8g8", + "difficulty":"Medium", + "code":"1254-number-of-closed-islands", + "kotlin":true + }, + { + "problem":"Number of Enclaves", + "pattern":"Graphs", + "link":"number-of-enclaves/", + "video":"gf0zsh1FIgE", + "difficulty":"Medium", + "code":"1020-number-of-enclaves", + "kotlin":true, + "java":true + }, + { + "problem":"Minimum Number of Vertices to Reach all Nodes", + "pattern":"Graphs", + "link":"minimum-number-of-vertices-to-reach-all-nodes/", + "video":"TLzcum7vrTc", + "difficulty":"Medium", + "code":"1557-minimum-number-of-vertices-to-reach-all-nodes", + "java":true, + "kotlin":true + }, + { + "problem":"Is Graph Bipartite?", + "pattern":"Graphs", + "link":"is-graph-bipartite/", + "video":"mev55LTubBY", + "difficulty":"Medium", + "code":"0785-is-graph-bipartite", + "kotlin":true, + "java":true + }, + { + "problem":"Evaluate Division", + "pattern":"Graphs", + "link":"evaluate-division/", + "video":"Uei1fwDoyKk", + "difficulty":"Medium", + "code":"0399-evaluate-division", + "kotlin":true, + "cpp":true + }, + { + "problem":"Detonate the Maximum Bombs", + "pattern":"Graphs", + "link":"detonate-the-maximum-bombs/", + "video":"8NPbAvVXKR4", + "difficulty":"Medium", + "code":"2101-detonate-the-maximum-bombs", + "kotlin":true + }, + { + "problem":"Largest Color Value in a Directed Graph", + "pattern":"Graphs", + "link":"largest-color-value-in-a-directed-graph/", + "video":"xLoDjKczUSk", + "difficulty":"Hard", + "code":"1857-largest-color-value-in-a-directed-graph", + "kotlin":true + }, + { + "problem":"Minimum Number of Days to Eat N Oranges", + "pattern":"Graphs", + "link":"minimum-number-of-days-to-eat-n-oranges/", + "video":"LziQ6Qx9sks", + "difficulty":"Hard", + "code":"1553-minimum-number-of-days-to-eat-n-oranges", + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Word Ladder", + "pattern":"Graphs", + "link":"word-ladder/", + "video":"h9iTnkgv05E", + "difficulty":"Hard", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0127-word-ladder", + "csharp":true, + "typescript":true, + "kotlin":true, + "go":true, + "rust":true + }, + { + "problem":"Path with Minimum Effort", + "pattern":"Advanced Graphs", + "link":"path-with-minimum-effort/", + "video":"XQlxCCx2vI4", + "difficulty":"Medium", + "code":"1631-path-with-minimum-effort", + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Reconstruct Itinerary", + "pattern":"Advanced Graphs", + "link":"reconstruct-itinerary/", + "video":"ZyB_gQ8vqGA", + "difficulty":"Hard", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0332-reconstruct-itinerary", + "csharp":true, + "kotlin":true, + "go":true, + "c":true, + "rust":true + }, + { + "neetcode150":true, + "problem":"Min Cost to Connect All Points", + "pattern":"Advanced Graphs", + "link":"min-cost-to-connect-all-points/", + "video":"f7JOBJIC-NA", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"1584-min-cost-to-connect-all-points", + "csharp":true, + "ruby":true, + "swift":true, + "go":true, + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Network Delay Time", + "pattern":"Advanced Graphs", + "link":"network-delay-time/", + "video":"EaphyqKU4PQ", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0743-network-delay-time", + "csharp":true, + "go":true, + "kotlin":true + }, + { + "problem":"Path with Maximum Probability", + "pattern":"Advanced Graphs", + "link":"path-with-maximum-probability/", + "video":"kPsDTGcrzGM", + "difficulty":"Medium", + "python":true, + "code":"1514-path-with-maximum-probability", + "java":true, + "javascript":true, + "kotlin":true, + "cpp":true + }, + { + "neetcode150":true, + "problem":"Swim In Rising Water", + "pattern":"Advanced Graphs", + "link":"swim-in-rising-water/", + "video":"amvrKlMLuGY", + "difficulty":"Hard", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0778-swim-in-rising-water", + "csharp":true, + "go":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Alien Dictionary", + "premium":true, + "freeLink":"https://www.lintcode.com/problem/892/", + "pattern":"Advanced Graphs", + "link":"alien-dictionary/", + "video":"6kTZYvNNyps", + "difficulty":"Hard", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0269-alien-dictionary", + "csharp":true + }, + { + "neetcode150":true, + "problem":"Cheapest Flights Within K Stops", + "pattern":"Advanced Graphs", + "link":"cheapest-flights-within-k-stops/", + "video":"5eIK3zUdYmE", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0787-cheapest-flights-within-k-stops", + "csharp":true, + "typescript":true, + "go":true, + "kotlin":true + }, + { + "problem":"Number of Good Paths", + "pattern":"Advanced Graphs", + "link":"number-of-good-paths/", + "video":"rv2GBYQm7xM", + "difficulty":"Hard", + "code":"2421-number-of-good-paths", + "javascript":true, + "typescript":true, + "go":true, + "rust":true, + "kotlin":true + }, + { + "problem":"Remove Max Number of Edges to Keep Graph Fully Traversable", + "pattern":"Advanced Graphs", + "link":"remove-max-number-of-edges-to-keep-graph-fully-traversable/", + "video":"booGwg5wYm4", + "difficulty":"Hard", + "code":"1579-remove-max-number-of-edges-to-keep-graph-fully-traversable", + "kotlin":true + }, + { + "problem":"Find Critical and Pseudo Critical Edges in Minimum Spanning Tree", + "pattern":"Advanced Graphs", + "link":"find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree/", + "video":"83JnUxrLKJU", + "difficulty":"Hard", + "code":"1489-find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree", + "python":true, + "kotlin":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Climbing Stairs", + "pattern":"1-D Dynamic Programming", + "link":"climbing-stairs/", + "video":"Y0lT9Fck7qI", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0070-climbing-stairs", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true, + "dart":true + }, + { + "neetcode150":true, + "problem":"Min Cost Climbing Stairs", + "pattern":"1-D Dynamic Programming", + "link":"min-cost-climbing-stairs/", + "code":"0746-min-cost-climbing-stairs", + "video":"ktmzAZWkEZ0", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true, + "scala":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"House Robber", + "pattern":"1-D Dynamic Programming", + "link":"house-robber/", + "video":"73r3KWiEvyk", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0198-house-robber", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true, + "scala":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"House Robber II", + "pattern":"1-D Dynamic Programming", + "link":"house-robber-ii/", + "video":"rWAJCfYYOvM", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0213-house-robber-ii", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true, + "scala":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Longest Palindromic Substring", + "pattern":"1-D Dynamic Programming", + "link":"longest-palindromic-substring/", + "video":"XYQecbcd6_c", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0005-longest-palindromic-substring", + "c":true, + "csharp":true, + "typescript":true, + "rust":true, + "go":true, + "kotlin":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Palindromic Substrings", + "pattern":"1-D Dynamic Programming", + "link":"palindromic-substrings/", + "video":"4RACzI5-du8", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0647-palindromic-substrings", + "c":true, + "csharp":true, + "typescript":true, + "rust":true, + "go":true, + "kotlin":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Decode Ways", + "pattern":"1-D Dynamic Programming", + "link":"decode-ways/", + "video":"6aEyTjOwlJU", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0091-decode-ways", + "c":true, + "csharp":true, + "typescript":true, + "kotlin":true, + "scala":true, + "go":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Coin Change", + "pattern":"1-D Dynamic Programming", + "link":"coin-change/", + "video":"H9bfqozjoqs", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0322-coin-change", + "c":true, + "csharp":true, + "typescript":true, + "kotlin":true, + "rust":true, + "go":true, + "scala":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Maximum Product Subarray", + "pattern":"1-D Dynamic Programming", + "link":"maximum-product-subarray/", + "video":"lXVy6YWFcRM", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0152-maximum-product-subarray", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Word Break", + "pattern":"1-D Dynamic Programming", + "link":"word-break/", + "video":"Sx9NNgInc3A", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0139-word-break", + "csharp":true, + "typescript":true, + "go":true, + "kotlin":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Longest Increasing Subsequence", + "pattern":"1-D Dynamic Programming", + "link":"longest-increasing-subsequence/", + "video":"cjWnW0hdF1Y", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0300-longest-increasing-subsequence", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "problem":"Partition Equal Subset Sum", + "pattern":"1-D Dynamic Programming", + "link":"partition-equal-subset-sum/", + "video":"IsvocB5BJhw", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0416-partition-equal-subset-sum", + "csharp":true, + "go":true, + "kotlin":true, + "c":true + }, + { + "problem":"Triangle", + "pattern":"1-D Dynamic Programming", + "link":"triangle/", + "video":"OM1MTokvxs4", + "difficulty":"Medium", + "code":"0120-triangle", + "cpp":true, + "java":true, + "python":true, + "kotlin":true, + "c":true + }, + { + "problem":"Delete And Earn", + "pattern":"1-D Dynamic Programming", + "link":"delete-and-earn/", + "video":"7FCemBxvGw0", + "difficulty":"Medium", + "code":"0740-delete-and-earn", + "python":true, + "go":true, + "kotlin":true, + "c":true, + "cpp":true + }, + { + "problem":"Paint House", + "pattern":"1-D Dynamic Programming", + "link":"paint-house/", + "video":"-w67-4tnH5U", + "difficulty":"Medium", + "code":"0256-paint-house", + "csharp":true + }, + { + "problem":"Combination Sum IV", + "pattern":"1-D Dynamic Programming", + "link":"combination-sum-iv/", + "video":"dw2nMCxG0ik", + "difficulty":"Medium", + "code":"0377-combination-sum-iv", + "python":true, + "cpp":true, + "kotlin":true, + "c":true, + "java":true + }, + { + "problem":"Perfect Squares", + "pattern":"1-D Dynamic Programming", + "link":"perfect-squares/", + "video":"HLZLwjzIVGo", + "difficulty":"Medium", + "code":"0279-perfect-squares", + "java":true, + "go":true, + "cpp":true, + "kotlin":true, + "c":true + }, + { + "problem":"Check if There is a Valid Partition For The Array", + "pattern":"1-D Dynamic Programming", + "link":"check-if-there-is-a-valid-partition-for-the-array/", + "video":"OxXPiwWFdTI", + "difficulty":"Medium", + "code":"2369-check-if-there-is-a-valid-partition-for-the-array", + "kotlin":true + }, + { + "problem":"Maximum Subarray Min Product", + "pattern":"1-D Dynamic Programming", + "link":"maximum-subarray-min-product/", + "video":"YLesLbNkyjA", + "difficulty":"Medium", + "code":"1856-maximum-subarray-min-product", + "kotlin":true, + "c":true, + "java":true + }, + { + "problem":"Minimum Cost For Tickets", + "pattern":"1-D Dynamic Programming", + "link":"minimum-cost-for-tickets/", + "video":"4pY1bsBpIY4", + "difficulty":"Medium", + "code":"0983-minimum-cost-for-tickets", + "cpp":true, + "kotlin":true, + "c":true + }, + { + "problem":"Integer Break", + "pattern":"1-D Dynamic Programming", + "link":"integer-break/", + "video":"in6QbUPMJ3I", + "difficulty":"Medium", + "code":"0343-integer-break", + "cpp":true, + "kotlin":true, + "c":true, + "java":true + }, + { + "problem":"Number of Longest Increasing Subsequence", + "pattern":"1-D Dynamic Programming", + "link":"number-of-longest-increasing-subsequence/", + "video":"Tuc-rjJbsXU", + "difficulty":"Medium", + "code":"0673-number-of-longest-increasing-subsequence", + "python":true, + "kotlin":true, + "c":true + }, + { + "problem":"Stickers to Spell Word", + "pattern":"1-D Dynamic Programming", + "link":"stickers-to-spell-word/", + "video":"hsomLb6mUdI", + "difficulty":"Hard", + "code":"0691-stickers-to-spell-word", + "c":true, + "kotlin":true + }, + { + "problem":"N-th Tribonacci Number", + "pattern":"1-D Dynamic Programming", + "link":"n-th-tribonacci-number/", + "video":"3lpNp5Ojvrw", + "difficulty":"Easy", + "code":"1137-n-th-tribonacci-number", + "python":true, + "javascript":true, + "typescript":true, + "go":true, + "kotlin":true, + "rust":true, + "c":true, + "cpp":true + }, + { + "problem":"Uncrossed Lines", + "pattern":"1-D Dynamic Programming", + "link":"uncrossed-lines/", + "video":"mnJF4vJ7GyE", + "difficulty":"Medium", + "code":"1035-uncrossed-lines", + "kotlin":true, + "c":true + }, + { + "problem":"Solving Questions With Brainpower", + "pattern":"1-D Dynamic Programming", + "link":"solving-questions-with-brainpower/", + "video":"D7TD_ArkfkA", + "difficulty":"Medium", + "code":"2140-solving-questions-with-brainpower", + "kotlin":true, + "c":true, + "cpp":true + }, + { + "problem":"Count Ways to Build Good Strings", + "pattern":"1-D Dynamic Programming", + "link":"count-ways-to-build-good-strings/", + "video":"JKpVHG2mhbk", + "difficulty":"Medium", + "code":"2466-count-ways-to-build-good-strings", + "kotlin":true, + "c":true + }, + { + "problem":"New 21 Game", + "pattern":"1-D Dynamic Programming", + "link":"new-21-game/", + "video":"zKi4LzjK27k", + "difficulty":"Medium", + "code":"0837-new-21-game", + "kotlin":true, + "c":true, + "javascript":true + }, + { + "problem":"Best Team with no Conflicts", + "pattern":"1-D Dynamic Programming", + "link":"best-team-with-no-conflicts/", + "video":"7kURH3btcV4", + "difficulty":"Medium", + "code":"1626-best-team-with-no-conflicts", + "c":true, + "kotlin":true + }, + { + "problem":"Stone Game III", + "pattern":"1-D Dynamic Programming", + "link":"stone-game-iii/", + "video":"HsLG5QW9CFQ", + "difficulty":"Hard", + "code":"1406-stone-game-iii", + "kotlin":true, + "c":true + }, + { + "problem":"Concatenated Words", + "pattern":"1-D Dynamic Programming", + "link":"concatenated-words/", + "video":"iHp7fjw1R28", + "difficulty":"Hard", + "code":"0472-concatenated-words", + "javascript":true, + "typescript":true, + "go":true, + "rust":true, + "kotlin":true + }, + { + "problem":"Maximize Score after N Operations", + "pattern":"1-D Dynamic Programming", + "link":"maximize-score-after-n-operations/", + "video":"RRQVDqp5RSE", + "difficulty":"Hard", + "code":"1799-maximize-score-after-n-operations", + "kotlin":true, + "c":true + }, + { + "problem":"Find the Longest Valid Obstacle Course at Each Position", + "pattern":"1-D Dynamic Programming", + "link":"find-the-longest-valid-obstacle-course-at-each-position/", + "video":"Xq9VT7p0lic", + "difficulty":"Hard", + "code":"1964-find-the-longest-valid-obstacle-course-at-each-position", + "kotlin":true, + "c":true + }, + { + "problem":"Count all Valid Pickup and Delivery Options", + "pattern":"1-D Dynamic Programming", + "link":"count-all-valid-pickup-and-delivery-options/", + "video":"OpgslsirW8s", + "difficulty":"Hard", + "code":"1359-count-all-valid-pickup-and-delivery-options", + "java":true, + "kotlin":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Unique Paths", + "pattern":"2-D Dynamic Programming", + "link":"unique-paths/", + "video":"IlEsdxuD4lY", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0062-unique-paths", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "kotlin":true, + "rust":true + }, + { + "problem":"Unique Paths II", + "pattern":"2-D Dynamic Programming", + "link":"unique-paths-ii/", + "video":"d3UOz7zdE4I", + "difficulty":"Medium", + "python":true, + "code":"0063-unique-paths-ii", + "cpp":true, + "java":true, + "go":true, + "kotlin":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Longest Common Subsequence", + "pattern":"2-D Dynamic Programming", + "link":"longest-common-subsequence/", + "video":"Ua0GhsJSlWM", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"1143-longest-common-subsequence", + "csharp":true, + "typescript":true, + "go":true, + "swift":true, + "kotlin":true, + "rust":true, + "c":true + }, + { + "problem":"Longest Palindromic Subsequence", + "pattern":"2-D Dynamic Programming", + "link":"longest-palindromic-subsequence/", + "video":"bUr8cNWI09Q", + "difficulty":"Medium", + "python":true, + "code":"0516-longest-palindromic-subsequence", + "kotlin":true + }, + { + "problem":"Last Stone Weight II", + "pattern":"2-D Dynamic Programming", + "link":"last-stone-weight-ii/", + "video":"gdXkkmzvR3c", + "difficulty":"Medium", + "python":true, + "code":"1049-last-stone-weight-ii", + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Best Time to Buy And Sell Stock With Cooldown", + "pattern":"2-D Dynamic Programming", + "link":"best-time-to-buy-and-sell-stock-with-cooldown/", + "video":"I7j0F7AHpb8", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0309-best-time-to-buy-and-sell-stock-with-cooldown", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "problem":"Coin Change II", + "pattern":"2-D Dynamic Programming", + "link":"coin-change-ii/", + "video":"Mjy4hd2xgrs", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0518-coin-change-ii", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "problem":"Target Sum", + "pattern":"2-D Dynamic Programming", + "link":"target-sum/", + "video":"g0npyaQtAQM", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0494-target-sum", + "csharp":true, + "typescript":true, + "go":true, + "kotlin":true, + "c":true + }, + { + "neetcode150":true, + "problem":"Interleaving String", + "pattern":"2-D Dynamic Programming", + "link":"interleaving-string/", + "video":"3Rw3p9LrgvE", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0097-interleaving-string", + "csharp":true, + "typescript":true, + "go":true, + "scala":true, + "kotlin":true, + "c":true + }, + { + "problem":"Stone Game", + "pattern":"2-D Dynamic Programming", + "link":"stone-game/", + "video":"uhgdXOlGYqE", + "difficulty":"Medium", + "code":"0877-stone-game", + "kotlin":true, + "cpp":true + }, + { + "problem":"Minimum Path Sum", + "pattern":"2-D Dynamic Programming", + "link":"minimum-path-sum/", + "video":"pGMsrvt0fpk", + "difficulty":"Medium", + "code":"0064-minimum-path-sum", + "cpp":true, + "java":true, + "python":true, + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Longest Increasing Path In a Matrix", + "pattern":"2-D Dynamic Programming", + "link":"longest-increasing-path-in-a-matrix/", + "video":"wCc_nd-GiEc", + "difficulty":"Hard", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0329-longest-increasing-path-in-a-matrix", + "c":true, + "csharp":true, + "kotlin":true, + "rust":true + }, + { + "problem":"Maximal Square", + "pattern":"2-D Dynamic Programming", + "link":"maximal-square/", + "video":"6X7Ha2PrDmM", + "difficulty":"Medium", + "code":"0221-maximal-square", + "python":true, + "java":true, + "kotlin":true, + "cpp":true + }, + { + "problem":"Ones and Zeroes", + "pattern":"2-D Dynamic Programming", + "link":"ones-and-zeroes/", + "video":"miZ3qV04b1g", + "difficulty":"Medium", + "python":true, + "code":"0474-ones-and-zeroes", + "kotlin":true, + "cpp":true + }, + { + "problem":"Maximum Alternating Subsequence Sum", + "pattern":"2-D Dynamic Programming", + "link":"maximum-alternating-subsequence-sum/", + "video":"4v42XOuU1XA", + "difficulty":"Medium", + "code":"5782-maximum-alternating-subsequence-sum" + }, + { + "neetcode150":true, + "problem":"Distinct Subsequences", + "pattern":"2-D Dynamic Programming", + "link":"distinct-subsequences/", + "video":"-RDzMJ33nx8", + "difficulty":"Hard", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0115-distinct-subsequences", + "csharp":true, + "typescript":true, + "kotlin":true, + "c":true + }, + { + "neetcode150":true, + "problem":"Edit Distance", + "pattern":"2-D Dynamic Programming", + "link":"edit-distance/", + "video":"XYi2-LPrwm4", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0072-edit-distance", + "csharp":true, + "swift":true, + "scala":true, + "kotlin":true, + "c":true + }, + { + "problem":"Count Vowels Permutation", + "pattern":"2-D Dynamic Programming", + "link":"count-vowels-permutation/", + "video":"VUVpTZVa7Ls", + "difficulty":"Hard", + "code":"1220-count-vowels-permutation", + "python":true, + "java":true, + "go":true, + "cpp":true, + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Burst Balloons", + "pattern":"2-D Dynamic Programming", + "link":"burst-balloons/", + "video":"VFskby7lUbw", + "difficulty":"Hard", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0312-burst-balloons", + "csharp":true, + "typescript":true, + "kotlin":true, + "c":true + }, + { + "problem":"Number of Ways to Rearrange Sticks With K Sticks Visible", + "pattern":"2-D Dynamic Programming", + "link":"number-of-ways-to-rearrange-sticks-with-k-sticks-visible/", + "video":"O761YBjGxGA", + "difficulty":"Hard", + "code":"1866-number-of-ways-to-rearrange-sticks-with-k-sticks-visible", + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Regular Expression Matching", + "pattern":"2-D Dynamic Programming", + "link":"regular-expression-matching/", + "video":"HAA8mgxlov8", + "difficulty":"Hard", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0010-regular-expression-matching", + "csharp":true, + "typescript":true, + "go":true, + "c":true, + "kotlin":true + }, + { + "problem":"Stone Game II", + "pattern":"2-D Dynamic Programming", + "link":"stone-game-ii/", + "video":"I-z-u0zfQtg", + "difficulty":"Medium", + "code":"1140-stone-game-ii", + "kotlin":true + }, + { + "problem":"Flip String to Monotone Increasing", + "pattern":"2-D Dynamic Programming", + "link":"flip-string-to-monotone-increasing/", + "video":"tMq9z5k3umQ", + "difficulty":"Medium", + "code":"0926-flip-string-to-monotone-increasing", + "javascript":true, + "typescript":true, + "go":true, + "kotlin":true, + "rust":true + }, + { + "problem":"Maximum Value of K Coins from Piles", + "pattern":"2-D Dynamic Programming", + "link":"maximum-value-of-k-coins-from-piles/", + "video":"ZRdEd_eun8g", + "difficulty":"Hard", + "code":"2218-maximum-value-of-k-coins-from-piles", + "kotlin":true + }, + { + "problem":"Number of Music Playlists", + "pattern":"2-D Dynamic Programming", + "link":"number-of-music-playlists/", + "video":"gk4qzZSmyrs", + "difficulty":"Hard", + "code":"0920-number-of-music-playlists", + "java":true, + "kotlin":true + }, + { + "problem":"Number of Ways to Form a Target String Given a Dictionary", + "pattern":"2-D Dynamic Programming", + "link":"number-of-ways-to-form-a-target-string-given-a-dictionary/", + "video":"_GF-0T-YjW8", + "difficulty":"Hard", + "code":"1639-number-of-ways-to-form-a-target-string-given-a-dictionary", + "kotlin":true + }, + { + "problem":"Profitable Schemes", + "pattern":"2-D Dynamic Programming", + "link":"profitable-schemes/", + "video":"CcLKQLKvOl8", + "difficulty":"Hard", + "code":"0879-profitable-schemes", + "kotlin":true + }, + { + "problem":"Minimum Cost to Cut a Stick", + "pattern":"2-D Dynamic Programming", + "link":"minimum-cost-to-cut-a-stick/", + "video":"EVxTO5I0d7w", + "difficulty":"Hard", + "code":"1547-minimum-cost-to-cut-a-stick", + "kotlin":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Maximum Subarray", + "pattern":"Greedy", + "link":"maximum-subarray/", + "video":"5WZl3MMT0Eg", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0053-maximum-subarray", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "swift":true, + "kotlin":true, + "rust":true, + "ruby":true + }, + { + "problem":"Maximum Sum Circular Subarray", + "pattern":"Greedy", + "link":"maximum-sum-circular-subarray/", + "video":"fxT9KjakYPM", + "difficulty":"Medium", + "code":"0918-maximum-sum-circular-subarray", + "python":true, + "javascript":true, + "typescript":true, + "go":true, + "rust":true, + "kotlin":true, + "java":true + }, + { + "problem":"Longest Turbulent Array", + "pattern":"Greedy", + "link":"longest-turbulent-subarray/", + "video":"V_iHUhR8Dek", + "difficulty":"Medium", + "code":"0978-longest-turbulent-subarray", + "python":true, + "kotlin":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Jump Game", + "pattern":"Greedy", + "link":"jump-game/", + "video":"Yan0cv2cLy8", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0055-jump-game", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "problem":"Jump Game II", + "pattern":"Greedy", + "link":"jump-game-ii/", + "video":"dJ7sWiOoK7g", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0045-jump-game-ii", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "swift":true, + "kotlin":true, + "ruby":true + }, + { + "problem":"Jump Game VII", + "pattern":"Greedy", + "link":"jump-game-vii/", + "video":"v1HpZUnQ4Yo", + "difficulty":"Medium", + "code":"1871-jump-game-vii", + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Gas Station", + "pattern":"Greedy", + "link":"gas-station/", + "video":"lJwbPZGo05A", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0134-gas-station", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "swift":true, + "kotlin":true, + "ruby":true + }, + { + "neetcode150":true, + "problem":"Hand of Straights", + "pattern":"Greedy", + "link":"hand-of-straights/", + "video":"amnrMCVd2YI", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0846-hand-of-straights", + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "kotlin":true, + "c":true + }, + { + "problem":"Minimize Maximum of Array", + "pattern":"Greedy", + "link":"minimize-maximum-of-array/", + "video":"AeHMvcKuR0Y", + "difficulty":"Medium", + "code":"2439-minimize-maximum-of-array", + "kotlin":true + }, + { + "problem":"Dota2 Senate", + "pattern":"Greedy", + "link":"dota2-senate/", + "video":"zZA5KskfMuQ", + "difficulty":"Medium", + "code":"0649-dota2-senate", + "kotlin":true + }, + { + "problem":"Maximum Points You Can Obtain From Cards", + "pattern":"Greedy", + "link":"maximum-points-you-can-obtain-from-cards/", + "video":"TsA4vbtfCvo", + "difficulty":"Medium", + "code":"1423-maximum-points-you-can-obtain-from-cards", + "csharp":true, + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Merge Triplets to Form Target Triplet", + "pattern":"Greedy", + "link":"merge-triplets-to-form-target-triplet/", + "video":"kShkQLQZ9K4", + "difficulty":"Medium", + "code":"1899-merge-triplets-to-form-target-triplet", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "csharp":true, + "typescript":true, + "ruby":true, + "swift":true, + "kotlin":true, + "c":true + }, + { + "neetcode150":true, + "problem":"Partition Labels", + "pattern":"Greedy", + "link":"partition-labels/", + "video":"B7m8UmZE-vw", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0763-partition-labels", + "csharp":true, + "go":true, + "ruby":true, + "kotlin":true, + "c":true + }, + { + "neetcode150":true, + "problem":"Valid Parenthesis String", + "pattern":"Greedy", + "link":"valid-parenthesis-string/", + "video":"QhPdNS143Qg", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0678-valid-parenthesis-string", + "c":true, + "csharp":true, + "typescript":true, + "kotlin":true + }, + { + "problem":"Eliminate Maximum Number of Monsters", + "pattern":"Greedy", + "link":"eliminate-maximum-number-of-monsters/", + "video":"6QQRayzOTD4", + "difficulty":"Medium", + "code":"1921-eliminate-maximum-number-of-monsters", + "cpp":true, + "java":true, + "kotlin":true + }, + { + "problem":"Two City Scheduling", + "pattern":"Greedy", + "link":"two-city-scheduling/", + "video":"d-B_gk_gJtQ", + "difficulty":"Medium", + "code":"1029-two-city-scheduling", + "java":true, + "python":true, + "javascript":true, + "typescript":true, + "go":true, + "rust":true, + "kotlin":true + }, + { + "problem":"Maximum Length of Pair Chain", + "pattern":"Greedy", + "link":"maximum-length-of-pair-chain/", + "video":"LcNNorqMVTw", + "difficulty":"Medium", + "code":"0646-maximum-length-of-pair-chain", + "kotlin":true + }, + { + "problem":"Minimum Deletions to Make Character Frequencies Unique", + "pattern":"Greedy", + "link":"minimum-deletions-to-make-character-frequencies-unique/", + "video":"h8AZEN49gTc", + "difficulty":"Medium", + "code":"1647-minimum-deletions-to-make-character-frequencies-unique", + "java":true, + "kotlin":true + }, + { + "problem":"Candy", + "pattern":"Greedy", + "link":"candy/", + "video":"1IzCRCcK17A", + "difficulty":"Hard", + "code":"135-candy" + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Insert Interval", + "pattern":"Intervals", + "link":"insert-interval/", + "video":"A8NUOmlwOlM", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0057-insert-interval", + "csharp":true, + "typescript":true, + "swift":true, + "rust":true, + "go":true, + "kotlin":true, + "c":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Merge Intervals", + "pattern":"Intervals", + "link":"merge-intervals/", + "video":"44H3cEC2fFM", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0056-merge-intervals", + "csharp":true, + "typescript":true, + "go":true, + "kotlin":true, + "scala":true, + "c":true, + "rust":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Non Overlapping Intervals", + "pattern":"Intervals", + "link":"non-overlapping-intervals/", + "video":"nONCGxWoUfM", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0435-non-overlapping-intervals", + "csharp":true, + "typescript":true, + "go":true, + "scala":true, + "c":true, + "kotlin":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Meeting Rooms", + "premium":true, + "freeLink":"https://www.lintcode.com/problem/920/", + "pattern":"Intervals", + "link":"meeting-rooms/", + "video":"PaJxqZVPhbg", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0252-meeting-rooms", + "csharp":true, + "rust":true, + "kotlin":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Meeting Rooms II", + "premium":true, + "freeLink":"https://www.lintcode.com/problem/919/", + "pattern":"Intervals", + "link":"meeting-rooms-ii/", + "video":"FdzJmTCVyJU", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0253-meeting-rooms-ii", + "csharp":true, + "rust":true, + "kotlin":true + }, + { + "problem":"Remove Covered Intervals", + "pattern":"Intervals", + "link":"remove-covered-intervals/", + "video":"nhAsMabiVkM", + "difficulty":"Medium", + "code":"1288-remove-covered-intervals", + "c":true, + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Minimum Interval to Include Each Query", + "pattern":"Intervals", + "link":"minimum-interval-to-include-each-query/", + "video":"5hQ5WWW5awQ", + "difficulty":"Hard", + "python":true, + "java":true, + "cpp":true, + "code":"1851-minimum-interval-to-include-each-query", + "csharp":true, + "javascript":true, + "kotlin":true + }, + { + "problem":"Data Stream as Disjoint Intervals", + "pattern":"Intervals", + "link":"data-stream-as-disjoint-intervals/", + "video":"FavoZjPIWpo", + "difficulty":"Hard", + "code":"0352-data-stream-as-disjoint-intervals", + "javascript":true, + "typescript":true, + "go":true, + "rust":true, + "kotlin":true + }, + { + "problem":"Excel Sheet Column Title", + "pattern":"Math & Geometry", + "link":"excel-sheet-column-title/", + "video":"X_vJDpCCuoA", + "difficulty":"Easy", + "code":"0168-excel-sheet-column-title", + "java":true, + "python":true, + "kotlin":true + }, + { + "problem":"Greatest Common Divisor of Strings", + "pattern":"Math & Geometry", + "link":"greatest-common-divisor-of-strings/", + "video":"i5I_wrbUdzM", + "difficulty":"Easy", + "code":"1071-greatest-common-divisor-of-strings", + "javascript":true, + "typescript":true, + "go":true, + "kotlin":true, + "rust":true, + "cpp":true + }, + { + "problem":"Count Odd Numbers in an Interval Range", + "pattern":"Math & Geometry", + "link":"count-odd-numbers-in-an-interval-range/", + "video":"wrIWye928JQ", + "difficulty":"Easy", + "code":"1523-count-odd-numbers-in-an-interval-range", + "kotlin":true + }, + { + "problem":"Matrix Diagonal Sum", + "pattern":"Math & Geometry", + "link":"matrix-diagonal-sum/", + "video":"WliTu6gIK7o", + "difficulty":"Easy", + "code":"1572-matrix-diagonal-sum", + "kotlin":true, + "javascript":true + }, + { + "problem":"Maximum Points on a Line", + "pattern":"Math & Geometry", + "link":"max-points-on-a-line/", + "video":"Bb9lOXUOnFw", + "difficulty":"Hard", + "code":"0149-max-points-on-a-line", + "python":true, + "javascript":true, + "typescript":true, + "rust":true, + "cpp":true, + "kotlin":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Rotate Image", + "pattern":"Math & Geometry", + "link":"rotate-image/", + "video":"fMSJSS7eO1w", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0048-rotate-image", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "swift":true, + "ruby":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Spiral Matrix", + "pattern":"Math & Geometry", + "link":"spiral-matrix/", + "video":"BJnMZNwUk1M", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0054-spiral-matrix", + "csharp":true, + "typescript":true, + "kotlin":true, + "go":true, + "ruby":true, + "c":true + }, + { + "problem":"Spiral Matrix II ", + "pattern":"Math & Geometry", + "link":"spiral-matrix-ii/", + "video":"RvLrWFBJ9fM", + "difficulty":"Medium", + "code":"0059-spiral-matrix-ii", + "javascript":true, + "kotlin":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Set Matrix Zeroes", + "pattern":"Math & Geometry", + "link":"set-matrix-zeroes/", + "video":"T41rL0L3Pnw", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0073-set-matrix-zeroes", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "kotlin":true, + "ruby":true + }, + { + "neetcode150":true, + "problem":"Happy Number", + "pattern":"Math & Geometry", + "link":"happy-number/", + "video":"ljz85bxOYJ0", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0202-happy-number", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "swift":true, + "kotlin":true, + "ruby":true, + "rust":true + }, + { + "neetcode150":true, + "problem":"Plus One", + "pattern":"Math & Geometry", + "link":"plus-one/", + "video":"jIaA8boiG1s", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0066-plus-one", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "swift":true, + "kotlin":true, + "ruby":true, + "rust":true + }, + { + "problem":"Palindrome Number", + "pattern":"Math & Geometry", + "link":"palindrome-number/", + "video":"yubRKwixN-U", + "difficulty":"Easy", + "code":"0009-palindrome-number", + "javascript":true, + "typescript":true, + "cpp":true, + "java":true, + "python":true, + "go":true, + "rust":true, + "swift":true, + "c":true, + "kotlin":true + }, + { + "problem":"Ugly Number", + "pattern":"Math & Geometry", + "link":"ugly-number/", + "video":"M0Zay1Qr9ws", + "difficulty":"Easy", + "code":"0263-ugly-number", + "c":true, + "cpp":true, + "java":true, + "python":true, + "javascript":true, + "typescript":true, + "go":true, + "rust":true, + "swift":true, + "kotlin":true + }, + { + "problem":"Shift 2D Grid", + "pattern":"Math & Geometry", + "link":"shift-2d-grid/", + "video":"nJYFh4Dl-as", + "difficulty":"Easy", + "code":"1260-shift-2d-grid", + "cpp":true, + "java":true, + "python":true, + "javascript":true, + "kotlin":true + }, + { + "problem":"Roman to Integer", + "pattern":"Math & Geometry", + "link":"roman-to-integer/", + "video":"3jdxYj3DD98", + "difficulty":"Easy", + "code":"0013-roman-to-integer", + "python":true, + "javascript":true, + "go":true, + "typescript":true, + "rust":true, + "java":true, + "cpp":true, + "kotlin":true + }, + { + "problem":"Integer to Roman", + "pattern":"Math & Geometry", + "link":"integer-to-roman/", + "video":"ohBNdSJyLh8", + "difficulty":"Medium", + "code":"0012-integer-to-roman", + "python":true, + "typescript":true, + "go":true, + "rust":true, + "javascript":true, + "cpp":true, + "java":true, + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Pow(x, n)", + "pattern":"Math & Geometry", + "link":"powx-n/", + "video":"g9YQyYi4IQQ", + "difficulty":"Medium", + "python":true, + "java":true, + "javascript":true, + "cpp":true, + "code":"0050-powx-n", + "c":true, + "csharp":true, + "typescript":true, + "swift":true, + "ruby":true, + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Multiply Strings", + "pattern":"Math & Geometry", + "link":"multiply-strings/", + "video":"1vZswirL8Y8", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0043-multiply-strings", + "csharp":true, + "typescript":true, + "swift":true, + "ruby":true, + "kotlin":true, + "c":true + }, + { + "neetcode150":true, + "problem":"Detect Squares", + "pattern":"Math & Geometry", + "link":"detect-squares/", + "video":"bahebearrDc", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"2013-detect-squares", + "csharp":true, + "kotlin":true, + "ruby":true, + "rust":true + }, + { + "problem":"Robot Bounded In Circle", + "pattern":"Math & Geometry", + "link":"robot-bounded-in-circle/", + "video":"nKv2LnC_g6E", + "difficulty":"Medium", + "code":"1041-robot-bounded-in-circle", + "kotlin":true + }, + { + "problem":"Zigzag Conversion", + "pattern":"Math & Geometry", + "link":"zigzag-conversion/", + "video":"Q2Tw6gcVEwc", + "difficulty":"Medium", + "code":"0006-zigzag-conversion", + "java":true, + "cpp":true, + "go":true, + "kotlin":true + }, + { + "problem":"Find Missing Observations", + "pattern":"Math & Geometry", + "link":"find-missing-observations/", + "video":"86yKkaNi3sU", + "difficulty":"Medium", + "code":"2028-find-missing-observations", + "kotlin":true + }, + { + "neetcode150":true, + "problem":"Single Number", + "pattern":"Bit Manipulation", + "link":"single-number/", + "video":"qMPX1AOa83k", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0136-single-number", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true, + "dart":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Number of 1 Bits", + "pattern":"Bit Manipulation", + "link":"number-of-1-bits/", + "video":"5Km3utixwZs", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0191-number-of-1-bits", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Counting Bits", + "pattern":"Bit Manipulation", + "link":"counting-bits/", + "video":"RyBM56RIWrM", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0338-counting-bits", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Reverse Bits", + "pattern":"Bit Manipulation", + "link":"reverse-bits/", + "video":"UcoN6UjAI64", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0190-reverse-bits", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Missing Number", + "pattern":"Bit Manipulation", + "link":"missing-number/", + "video":"WnPLSRLSANE", + "difficulty":"Easy", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0268-missing-number", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "problem":"Shuffle the Array", + "pattern":"Bit Manipulation", + "link":"shuffle-the-array/", + "video":"IvIKD_EU8BY", + "difficulty":"Easy", + "code":"1470-shuffle-the-array", + "javascript":true, + "typescript":true, + "go":true, + "kotlin":true, + "rust":true, + "c":true + }, + { + "problem":"Add to Array-Form of Integer", + "pattern":"Bit Manipulation", + "link":"add-to-array-form-of-integer/", + "video":"eBTZQt1TWfk", + "difficulty":"Easy", + "code":"0989-add-to-array-form-of-integer", + "javascript":true, + "typescript":true, + "go":true, + "kotlin":true, + "rust":true, + "c":true + }, + { + "neetcode150":true, + "blind75":true, + "problem":"Sum of Two Integers", + "pattern":"Bit Manipulation", + "link":"sum-of-two-integers/", + "video":"gVUrDV4tZfY", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0371-sum-of-two-integers", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "rust":true + }, + { + "neetcode150":true, + "problem":"Reverse Integer", + "pattern":"Bit Manipulation", + "link":"reverse-integer/", + "video":"HAgLH58IgJQ", + "difficulty":"Medium", + "python":true, + "java":true, + "cpp":true, + "javascript":true, + "code":"0007-reverse-integer", + "c":true, + "csharp":true, + "typescript":true, + "go":true, + "ruby":true, + "swift":true, + "kotlin":true, + "scala":true, + "rust":true + }, + { + "problem":"Add Binary", + "pattern":"Bit Manipulation", + "link":"add-binary/", + "video":"keuWJ47xG8g", + "difficulty":"Easy", + "code":"0067-add-binary", + "cpp":true, + "java":true, + "python":true, + "kotlin":true, + "rust":true, + "c":true + }, + { + "problem":"Create Hello World Function", + "pattern":"JavaScript", + "link":"create-hello-world-function/", + "video":"P9Ldx1eTlRc", + "difficulty":"Easy", + "code":"2667-create-hello-world-function", + "javascript":true, + "typescript":true + }, + { + "problem":"Counter", + "pattern":"JavaScript", + "link":"counter/", + "video":"yEGQQAG5V68", + "difficulty":"Easy", + "code":"2620-counter", + "javascript":true, + "typescript":true + }, + { + "problem":"Counter II", + "pattern":"JavaScript", + "link":"counter-ii/", + "video":"UXNXKGFZD08", + "difficulty":"Easy", + "code":"2665-counter-ii", + "javascript":true + }, + { + "problem":"Apply Transform over each Element in Array", + "pattern":"JavaScript", + "link":"apply-transform-over-each-element-in-array/", + "video":"7FhJHA5jlYk", + "difficulty":"Easy", + "code":"2635-apply-transform-over-each-element-in-array" + }, + { + "problem":"Filter Elements from Array", + "pattern":"JavaScript", + "link":"filter-elements-from-array/", + "video":"w1o81gbEEJU", + "difficulty":"Easy", + "code":"2634-filter-elements-from-array" + }, + { + "problem":"Array Reduce Transformation", + "pattern":"JavaScript", + "link":"array-reduce-transformation/", + "video":"KmTbYfpGxdM", + "difficulty":"Easy", + "code":"2626-array-reduce-transformation" + }, + { + "problem":"Function Composition", + "pattern":"JavaScript", + "link":"function-composition/", + "video":"mIFw1H7Ljco", + "difficulty":"Easy", + "code":"2629-function-composition" + }, + { + "problem":"Allow One Function Call", + "pattern":"JavaScript", + "link":"allow-one-function-call/", + "video":"m_SWhM9iX3s", + "difficulty":"Easy", + "code":"2666-allow-one-function-call" + }, + { + "problem":"Memoize", + "pattern":"JavaScript", + "link":"memoize/", + "video":"oFXyzJt9VeU", + "difficulty":"Medium", + "code":"2623-memoize" + }, + { + "problem":"Curry", + "pattern":"JavaScript", + "link":"curry/", + "video":"pi4kqMWQXxA", + "difficulty":"Medium", + "code":"2632-curry" + }, + { + "problem":"Sleep", + "pattern":"JavaScript", + "link":"sleep/", + "video":"P0D9Z6H7O00", + "difficulty":"Easy", + "code":"2621-sleep" + }, + { + "problem":"Promise Time Limit", + "pattern":"JavaScript", + "link":"promise-time-limit/", + "video":"hfH57rdZhOk", + "difficulty":"Easy", + "code":"2637-promise-time-limit" + }, + { + "problem":"Promise Pool", + "pattern":"JavaScript", + "link":"promise-pool/", + "video":"DB8pAAg-9xw", + "difficulty":"Medium", + "code":"2636-promise-pool" + }, + { + "problem":"Cache With Time Limit", + "pattern":"JavaScript", + "link":"cache-with-time-limit/", + "video":"w772gtNK0Gw", + "difficulty":"Medium", + "code":"2622-cache-with-time-limit" + }, + { + "problem":"Debounce", + "pattern":"JavaScript", + "link":"debounce/", + "video":"1sxSpnxNx5w", + "difficulty":"Medium", + "code":"2627-debounce" + }, + { + "problem":"Throttle", + "pattern":"JavaScript", + "link":"throttle/", + "video":"zyGZV_fIQWk", + "difficulty":"Medium", + "code":"2676-throttle" + }, + { + "problem":"JSON Deep Equal", + "pattern":"JavaScript", + "link":"json-deep-equal/", + "video":"4JVZ-mVqJPg", + "difficulty":"Medium", + "code":"2628-json-deep-equal", + "javascript":true + }, + { + "problem":"Convert Object to JSON String", + "pattern":"JavaScript", + "link":"convert-object-to-json-string/", + "video":"f94fUbHU-FY", + "difficulty":"Medium", + "code":"2633-convert-object-to-json-string" + }, + { + "problem":"Array of Objects to Matrix", + "pattern":"JavaScript", + "link":"array-of-objects-to-matrix/", + "video":"LJwgAMHGcI0", + "difficulty":"Medium", + "code":"2675-array-of-objects-to-matrix" + }, + { + "problem":"Difference Between Two Objects", + "pattern":"JavaScript", + "link":"differences-between-two-objects/", + "video":"gH7oZs1WZfg", + "difficulty":"Medium", + "code":"2700-differences-between-two-objects" + }, + { + "problem":"Chunk Array", + "pattern":"JavaScript", + "link":"chunk-array/", + "video":"VUN-O3B9ceY", + "difficulty":"Easy", + "code":"2677-chunk-array" + }, + { + "problem":"Flatten Deeply Nested Array", + "pattern":"JavaScript", + "link":"flatten-deeply-nested-array/", + "video":"_DetLPKtFNk", + "difficulty":"Medium", + "code":"2625-flatten-deeply-nested-array" + }, + { + "problem":"Array Prototype Last", + "pattern":"JavaScript", + "link":"array-prototype-last/", + "video":"3JdtfMBrUqc", + "difficulty":"Easy", + "code":"2619-array-prototype-last" + }, + { + "problem":"Group By", + "pattern":"JavaScript", + "link":"group-by/", + "video":"zs9axOyYHRE", + "difficulty":"Medium", + "code":"2631-group-by" + }, + { + "problem":"Check if Object Instance of Class", + "pattern":"JavaScript", + "link":"check-if-object-instance-of-class/", + "video":"meIo-Q07Ba8", + "difficulty":"Medium", + "code":"2618-check-if-object-instance-of-class" + }, + { + "problem":"Call Function with Custom Context", + "pattern":"JavaScript", + "link":"call-function-with-custom-context/", + "video":"du_GH-Ooa8E", + "difficulty":"Medium", + "code":"2693-call-function-with-custom-context" + }, + { + "problem":"Event Emitter", + "pattern":"JavaScript", + "link":"event-emitter/", + "video":"M69bjWFarU0", + "difficulty":"Medium", + "code":"2694-event-emitter" + }, + { + "problem":"Array Wrapper", + "pattern":"JavaScript", + "link":"array-wrapper/", + "video":"XoGjPdPTAVA", + "difficulty":"Easy", + "code":"2695-array-wrapper" + }, + { + "problem":"Generate Fibonacci Sequence", + "pattern":"JavaScript", + "link":"generate-fibonacci-sequence/", + "video":"T643rQ70Jlk", + "difficulty":"Easy", + "code":"2648-generate-fibonacci-sequence" + }, + { + "problem":"Nested Array Generator", + "pattern":"JavaScript", + "link":"nested-array-generator/", + "video":"yo-J1jQaZYU", + "difficulty":"Medium", + "code":"2649-nested-array-generator" + } +] \ No newline at end of file diff --git a/1-Two-Sum.py b/1-Two-Sum.py deleted file mode 100644 index 3eb8df064..000000000 --- a/1-Two-Sum.py +++ /dev/null @@ -1,9 +0,0 @@ -class Solution: - def twoSum(self, nums: List[int], target: int) -> List[int]: - prevMap = {} # val -> index - - for i, n in enumerate(nums): - diff = target - n - if diff in prevMap: - return [prevMap[diff], i] - prevMap[n] = i diff --git a/10-Regular-Expression-Matching.py b/10-Regular-Expression-Matching.py deleted file mode 100644 index b4d2a68e8..000000000 --- a/10-Regular-Expression-Matching.py +++ /dev/null @@ -1,45 +0,0 @@ -# BOTTOM-UP Dynamic Programming -class Solution: - def isMatch(self, s: str, p: str) -> bool: - cache = [[False] * (len(p) + 1) for i in range(len(s) + 1)] - cache[len(s)][len(p)] = True - - for i in range(len(s), -1, -1): - for j in range(len(p) - 1, -1 ,-1): - match = i < len(s) and (s[i] == p[j] or p[j] == ".") - - if (j + 1) < len(p) and p[j + 1] == "*": - cache[i][j] = cache[i][j + 2] - if match: - cache[i][j] = cache[i + 1][j] or cache[i][j] - elif match: - cache[i][j] = cache[i+1][j+1] - - return cache[0][0] - - -# TOP DOWN MEMOIZATION -class Solution: - def isMatch(self, s: str, p: str) -> bool: - cache = {} - - def dfs(i, j): - if (i, j) in cache: - return cache[(i, j)] - if i >= len(s) and j >= len(p): - return True - if j >= len(p): - return False - - match = i < len(s) and (s[i] == p[j] or p[j] == ".") - if (j + 1) < len(p) and p[j + 1] == "*": - cache[(i, j)] = (dfs(i, j + 2) or # dont use * - (match and dfs(i + 1, j))) # use * - return cache[(i, j)] - if match: - cache[(i,j)] = dfs(i + 1, j + 1) - return cache[(i,j)] - cache[(i,j)] = False - return False - - return dfs(0, 0) diff --git a/100-Same-Tree.py b/100-Same-Tree.py deleted file mode 100644 index e054ed268..000000000 --- a/100-Same-Tree.py +++ /dev/null @@ -1,13 +0,0 @@ -# Definition for a binary tree node. -# class TreeNode: -# def __init__(self, x): -# self.val = x -# self.left = None -# self.right = None - -class Solution: - def isSameTree(self, p: TreeNode, q: TreeNode) -> bool: - if not p and not q: return True - if p and q and p.val == q.val: - return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right) - else: return False diff --git a/102-Binary-Tree-Level-Order-Traversal.py b/102-Binary-Tree-Level-Order-Traversal.py deleted file mode 100644 index 491c1406f..000000000 --- a/102-Binary-Tree-Level-Order-Traversal.py +++ /dev/null @@ -1,23 +0,0 @@ -# Definition for a binary tree node. -# class TreeNode: -# def __init__(self, x): -# self.val = x -# self.left = None -# self.right = None - -class Solution: - def levelOrder(self, root: TreeNode) -> List[List[int]]: - res = [] - q = collections.deque() - if root: q.append(root) - - while q: - val = [] - - for i in range(len(q)): - node = q.popleft() - val.append(node.val) - if node.left: q.append(node.left) - if node.right: q.append(node.right) - res.append(val) - return res diff --git a/104-Maximum-Depth-of-Binary-Tree.py b/104-Maximum-Depth-of-Binary-Tree.py deleted file mode 100644 index b02eb54dc..000000000 --- a/104-Maximum-Depth-of-Binary-Tree.py +++ /dev/null @@ -1,41 +0,0 @@ -# RECURSIVE DFS -class Solution: - def maxDepth(self, root: TreeNode) -> int: - if not root: - return 0 - - return 1 + max(self.maxDepth(root.left), self.maxDepth(root.right)) - -# ITERATIVE DFS -class Solution: - def maxDepth(self, root: TreeNode) -> int: - stack = [[root, 1]] - res = 0 - - while stack: - node, depth = stack.pop() - - if node: - res = max(res, depth) - stack.append([node.left, depth + 1]) - stack.append([node.right, depth + 1]) - return res - -# BFS -class Solution: - def maxDepth(self, root: TreeNode) -> int: - if not root: - return 0 - - level = 0 - q = deque([root]) - while q: - - for i in range(len(q)): - node = q.popleft() - if node.left: - q.append(node.left) - if node.right: - q.append(node.right) - level += 1 - return level diff --git a/1046-Last-Stone-Weight.py b/1046-Last-Stone-Weight.py deleted file mode 100644 index e1cc3c015..000000000 --- a/1046-Last-Stone-Weight.py +++ /dev/null @@ -1,13 +0,0 @@ -class Solution: - def lastStoneWeight(self, stones: List[int]) -> int: - stones = [-s for s in stones] - heapq.heapify(stones) - - while len(stones) > 1: - first = heapq.heappop(stones) - second = heapq.heappop(stones) - if second > first: - heapq.heappush(stones, first - second) - - stones.append(0) - return abs(stones[0]) diff --git a/105-Construct-Binary-Tree-from-Preorder-and-Inorder-Traversal.py b/105-Construct-Binary-Tree-from-Preorder-and-Inorder-Traversal.py deleted file mode 100644 index 570d3cee5..000000000 --- a/105-Construct-Binary-Tree-from-Preorder-and-Inorder-Traversal.py +++ /dev/null @@ -1,10 +0,0 @@ -class Solution: - def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: - if not preorder or not inorder: - return None - - root = TreeNode(preorder[0]) - mid = inorder.index(preorder[0]) - root.left = self.buildTree(preorder[1:mid + 1], inorder[:mid]) - root.right = self.buildTree(preorder[mid + 1:], inorder[mid + 1:]) - return root diff --git a/11-Container-With-Most-Water.py b/11-Container-With-Most-Water.py deleted file mode 100644 index e9607306c..000000000 --- a/11-Container-With-Most-Water.py +++ /dev/null @@ -1,12 +0,0 @@ -class Solution: - def maxArea(self, height: List[int]) -> int: - l, r = 0, len(height) - 1 - res = 0 - - while l < r: - res = max(res, min(height[l], height[r]) * (r - l)) - if height[l] < height[r]: - l += 1 - elif height[r] <= height[l]: - r -= 1 - return res diff --git a/110-Balanced-Binary-Tree.py b/110-Balanced-Binary-Tree.py deleted file mode 100644 index ffd200329..000000000 --- a/110-Balanced-Binary-Tree.py +++ /dev/null @@ -1,17 +0,0 @@ -# Definition for a binary tree node. -# class TreeNode: -# def __init__(self, val=0, left=None, right=None): -# self.val = val -# self.left = left -# self.right = right -class Solution: - def isBalanced(self, root: Optional[TreeNode]) -> bool: - - def dfs(root): - if not root: return [True, 0] - - left, right = dfs(root.left), dfs(root.right) - balanced = (left[0] and right[0] and - abs(left[1] - right[1]) <= 1) - return [balanced, 1 + max(left[1], right[1])] - return dfs(root)[0] diff --git a/1143-Longest-Common-Subsequence.py b/1143-Longest-Common-Subsequence.py deleted file mode 100644 index 77af92b4a..000000000 --- a/1143-Longest-Common-Subsequence.py +++ /dev/null @@ -1,12 +0,0 @@ -class Solution: - def longestCommonSubsequence(self, text1: str, text2: str) -> int: - dp = [[0 for j in range(len(text2) + 1)] for i in range(len(text1) + 1)] - - for i in range(len(text1) - 1, -1, -1): - for j in range(len(text2) - 1, -1, -1): - if text1[i] == text2[j]: - dp[i][j] = 1 + dp[i + 1][j + 1] - else: - dp[i][j] = max(dp[i][j + 1], dp[i + 1][j]) - - return dp[0][0] diff --git a/115-Distinct-Subsequences.py b/115-Distinct-Subsequences.py deleted file mode 100644 index 67f0172fc..000000000 --- a/115-Distinct-Subsequences.py +++ /dev/null @@ -1,16 +0,0 @@ -class Solution: - def numDistinct(self, s: str, t: str) -> int: - cache = {} - - for i in range(len(s) + 1): - cache[(i, len(t))] = 1 - for j in range(len(t)): - cache[(len(s), j)] = 0 - - for i in range(len(s) - 1, -1, -1): - for j in range(len(t) - 1, -1, -1): - if s[i] == t[j]: - cache[(i, j)] = cache[(i+1,j+1)] + cache[(i+1,j)] - else: - cache[(i,j)] = cache[(i+1,j)] - return cache[(0,0)] diff --git a/12-Integer-To-Roman.py b/12-Integer-To-Roman.py deleted file mode 100644 index 50d5393ca..000000000 --- a/12-Integer-To-Roman.py +++ /dev/null @@ -1,13 +0,0 @@ -class Solution: - def intToRoman(self, num: int) -> str: - symList = [["I", 1], ["IV" , 4], ["V" , 5], ["IX" , 9], - ["X" , 10], ["XL" , 40], ["L" , 50], ["XC" , 90], - ["C" , 100], ["CD", 400], ["D", 500], ["CM", 900], - ["M" , 1000]] - res = "" - for sym, val in reversed(symList): - if num // val: - count = num // val - res += (sym * count) - num = num % val - return res diff --git a/121-Best-Time-To-Buy-and-Sell-Stock.py b/121-Best-Time-To-Buy-and-Sell-Stock.py deleted file mode 100644 index 43f25e419..000000000 --- a/121-Best-Time-To-Buy-and-Sell-Stock.py +++ /dev/null @@ -1,10 +0,0 @@ -class Solution: - def maxProfit(self, prices: List[int]) -> int: - res = 0 - - l = 0 - for r in range(1, len(prices)): - if prices[r] < prices[l]: - l = r - res = max(res, prices[r] - prices[l]) - return res diff --git a/1239-Maximum-Length-of-a-Concatenated-String-with-Unique-Characters.py b/1239-Maximum-Length-of-a-Concatenated-String-with-Unique-Characters.py deleted file mode 100644 index 5c5628285..000000000 --- a/1239-Maximum-Length-of-a-Concatenated-String-with-Unique-Characters.py +++ /dev/null @@ -1,27 +0,0 @@ -class Solution: - def maxLength(self, arr: List[str]) -> int: - charSet = set() - - def overlap(charSet, s): - c = Counter(charSet) + Counter(s) - return max(c.values()) > 1 - # prev = set() - # for c in s: - # if c in charSet or c in prev: - # return True - # prev.add(c) - # return False - - def backtrack(i): - if i == len(arr): - return len(charSet) - res = 0 - if not overlap(charSet, arr[i]): - for c in arr[i]: - charSet.add(c) - res = backtrack(i + 1) - for c in arr[i]: - charSet.remove(c) - return max(res, backtrack(i + 1)) # dont concatenate arr[i] - - return backtrack(0) diff --git a/124-Binary-Tree-Maximum-Path-Sum.py b/124-Binary-Tree-Maximum-Path-Sum.py deleted file mode 100644 index e79190ced..000000000 --- a/124-Binary-Tree-Maximum-Path-Sum.py +++ /dev/null @@ -1,26 +0,0 @@ -# Definition for a binary tree node. -# class TreeNode: -# def __init__(self, val=0, left=None, right=None): -# self.val = val -# self.left = left -# self.right = right -class Solution: - def maxPathSum(self, root: TreeNode) -> int: - res = [root.val] - - # return max path sum without split - def dfs(root): - if not root: - return 0 - - leftMax = dfs(root.left) - rightMax = dfs(root.right) - leftMax = max(leftMax, 0) - rightMax = max(rightMax, 0) - - # compute max path sum WITH split - res[0] = max(res[0], root.val + leftMax + rightMax) - return root.val + max(leftMax, rightMax) - - dfs(root) - return res[0] diff --git a/125-Valid-Palindrome.py b/125-Valid-Palindrome.py deleted file mode 100644 index 31723b3d7..000000000 --- a/125-Valid-Palindrome.py +++ /dev/null @@ -1,19 +0,0 @@ -class Solution: - def isPalindrome(self, s: str) -> bool: - l, r = 0, len(s) - 1 - while l < r: - while l < r and not self.alphanum(s[l]): - l += 1 - while l < r and not self.alphanum(s[r]): - r -= 1 - if s[l].lower() != s[r].lower(): - return False - l += 1 - r -= 1 - return True - - # Could write own alpha-numeric function - def alphanum(self, c): - return (ord('A') <= ord(c) <= ord('Z') or - ord('a') <= ord(c) <= ord('z') or - ord('0') <= ord(c) <= ord('9')) diff --git a/127-Word-Ladder.py b/127-Word-Ladder.py deleted file mode 100644 index e117b2a7b..000000000 --- a/127-Word-Ladder.py +++ /dev/null @@ -1,28 +0,0 @@ -class Solution: - def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int: - if endWord not in wordList: - return 0 - - nei = collections.defaultdict(list) - wordList.append(beginWord) - for word in wordList: - for j in range(len(word)): - pattern = word[:j] + "*" + word[j + 1:] - nei[pattern].append(word) - - visit = set([beginWord]) - q = deque([beginWord]) - res = 1 - while q: - for i in range(len(q)): - word = q.popleft() - if word == endWord: - return res - for j in range(len(word)): - pattern = word[:j] + "*" + word[j + 1:] - for neiWord in nei[pattern]: - if neiWord not in visit: - visit.add(neiWord) - q.append(neiWord) - res += 1 - return 0 diff --git a/128-Longest-consecutive-sequence.py b/128-Longest-consecutive-sequence.py deleted file mode 100644 index 0c878a200..000000000 --- a/128-Longest-consecutive-sequence.py +++ /dev/null @@ -1,14 +0,0 @@ -class Solution: - def longestConsecutive(self, nums: List[int]) -> int: - numSet = set(nums) - longest = 0 - - for n in nums: - # check if its the start of a sequence - if (n - 1) not in numSet: - length = 1 - while (n + length) in numSet: - length += 1 - longest = max(length, longest) - return longest - diff --git a/13-Roman-To-Integer.py b/13-Roman-To-Integer.py deleted file mode 100644 index 3bd3a2abb..000000000 --- a/13-Roman-To-Integer.py +++ /dev/null @@ -1,11 +0,0 @@ -class Solution: - def romanToInt(self, s: str) -> int: - roman = { "I" : 1, "V" : 5, "X" : 10, - "L" : 50, "C" : 100, "D" : 500, "M" : 1000 } - res = 0 - for i in range(len(s)): - if i + 1 < len(s) and roman[s[i]] < roman[s[i + 1]]: - res -= roman[s[i]] - else: - res += roman[s[i]] - return res diff --git a/130-Surrounded-Regions.py b/130-Surrounded-Regions.py deleted file mode 100644 index dfbb8308a..000000000 --- a/130-Surrounded-Regions.py +++ /dev/null @@ -1,32 +0,0 @@ -class Solution: - def solve(self, board: List[List[str]]) -> None: - ROWS, COLS = len(board), len(board[0]) - - def capture(r, c): - if (r < 0 or c < 0 or r == ROWS or c == COLS - or board[r][c] != "O"): - return - board[r][c] = "T" - capture(r + 1, c) - capture(r - 1, c) - capture(r, c + 1) - capture(r, c - 1) - - # 1. (DFS) Capture unsurrounded regions (O -> T) - for r in range(ROWS): - for c in range(COLS): - if (board[r][c] == "O" and - (r in [0, ROWS - 1] or c in [0, COLS - 1])): - capture(r, c) - - # 2. Capture surrounded regions (O -> X) - for r in range(ROWS): - for c in range(COLS): - if board[r][c] == "O": - board[r][c] = "X" - - # 3. Uncapture unsurrounded regions (T -> O) - for r in range(ROWS): - for c in range(COLS): - if board[r][c] == "T": - board[r][c] = "O" diff --git a/131-Palindrome-Partitioning.py b/131-Palindrome-Partitioning.py deleted file mode 100644 index 21df07917..000000000 --- a/131-Palindrome-Partitioning.py +++ /dev/null @@ -1,22 +0,0 @@ -class Solution: - def partition(self, s: str) -> List[List[str]]: - res, part = [], [] - - def dfs(i): - if i >= len(s): - res.append(part.copy()) - return - for j in range(i, len(s)): - if self.isPali(s, i, j): - part.append(s[i:j+1]) - dfs(j + 1) - part.pop() - dfs(0) - return res - - def isPali(self, s, l, r): - while l < r: - if s[l] != s[r]: - return False - l, r = l + 1, r - 1 - return True diff --git a/133-Clone-Graph.py b/133-Clone-Graph.py deleted file mode 100644 index 36505a209..000000000 --- a/133-Clone-Graph.py +++ /dev/null @@ -1,15 +0,0 @@ -class Solution: - def cloneGraph(self, node: 'Node') -> 'Node': - oldToNew = {} - - def dfs(node): - if node in oldToNew: - return oldToNew[node] - - copy = Node(node.val) - oldToNew[node] = copy - for nei in node.neighbors: - copy.neighbors.append(dfs(nei)) - return copy - - return dfs(node) if node else None diff --git a/134-Gas-Station.py b/134-Gas-Station.py deleted file mode 100644 index 2e90d2a7b..000000000 --- a/134-Gas-Station.py +++ /dev/null @@ -1,14 +0,0 @@ -class Solution: - def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int: - start, end = len(gas) - 1, 0 - total = gas[start] - cost[start] - - while start >= end: - while total < 0 and start >= end: - start -= 1 - total += (gas[start] - cost[start]) - if start == end: - return start - total += (gas[end] - cost[end]) - end += 1 - return -1 diff --git a/138-Copy-List-With-Random-Pointer.py b/138-Copy-List-With-Random-Pointer.py deleted file mode 100644 index 386fd1a41..000000000 --- a/138-Copy-List-With-Random-Pointer.py +++ /dev/null @@ -1,25 +0,0 @@ -""" -# Definition for a Node. -class Node: - def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None): - self.val = int(x) - self.next = next - self.random = random -""" - -class Solution: - def copyRandomList(self, head: 'Node') -> 'Node': - oldToCopy = { None : None } - - cur = head - while cur: - copy = Node(cur.val) - oldToCopy[cur] = copy - cur = cur.next - cur = head - while cur: - copy = oldToCopy[cur] - copy.next = oldToCopy[cur.next] - copy.random = oldToCopy[cur.random] - cur = cur.next - return oldToCopy[head] diff --git a/139-Word-Break.py b/139-Word-Break.py deleted file mode 100644 index ae5d2e972..000000000 --- a/139-Word-Break.py +++ /dev/null @@ -1,14 +0,0 @@ -class Solution: - def wordBreak(self, s: str, wordDict: List[str]) -> bool: - - dp = [False] * (len(s) + 1) - dp[len(s)] = True - - for i in range(len(s) - 1, -1, -1): - for w in wordDict: - if (i + len(w)) <= len(s) and s[i : i + len(w)] == w: - dp[i] = dp[i + len(w)] - if dp[i]: - break - - return dp[0] diff --git a/141-Linked-List-Cycle.py b/141-Linked-List-Cycle.py deleted file mode 100644 index 6758f32a4..000000000 --- a/141-Linked-List-Cycle.py +++ /dev/null @@ -1,16 +0,0 @@ -# Definition for singly-linked list. -# class ListNode: -# def __init__(self, x): -# self.val = x -# self.next = None - -class Solution: - def hasCycle(self, head: ListNode) -> bool: - slow, fast = head, head - - while fast and fast.next: - slow = slow.next - fast = fast.next.next - if slow == fast: - return True - return False diff --git a/143-Reorder-List.py b/143-Reorder-List.py deleted file mode 100644 index 9b210b7c6..000000000 --- a/143-Reorder-List.py +++ /dev/null @@ -1,24 +0,0 @@ -class Solution: - def reorderList(self, head: ListNode) -> None: - # find middle - slow, fast = head, head.next - while fast and fast.next: - slow = slow.next - fast = fast.next.next - - # reverse second half - second = slow.next - prev = slow.next = None - while second: - tmp = second.next - second.next = prev - prev = second - second = tmp - - # merge two halfs - first, second = head, prev - while second: - tmp1, tmp2 = first.next, second.next - first.next = second - second.next = tmp1 - first, second = tmp1, tmp2 diff --git a/1448-Count-Good-Nodes-in-Binary-Tree.py b/1448-Count-Good-Nodes-in-Binary-Tree.py deleted file mode 100644 index 6b3325122..000000000 --- a/1448-Count-Good-Nodes-in-Binary-Tree.py +++ /dev/null @@ -1,20 +0,0 @@ -# Definition for a binary tree node. -# class TreeNode: -# def __init__(self, val=0, left=None, right=None): -# self.val = val -# self.left = left -# self.right = right -class Solution: - def goodNodes(self, root: TreeNode) -> int: - - def dfs(node, maxVal): - if not node: - return 0 - - res = 1 if node.val >= maxVal else 0 - maxVal = max(maxVal, node.val) - res += dfs(node.left, maxVal) - res += dfs(node.right, maxVal) - return res - - return dfs(root, root.val) diff --git a/146-LRU-Cache.py b/146-LRU-Cache.py deleted file mode 100644 index cd06aed11..000000000 --- a/146-LRU-Cache.py +++ /dev/null @@ -1,44 +0,0 @@ -class Node: - def __init__(self, key, val): - self.key, self.val = key, val - self.prev = self.next = None - -class LRUCache: - - def __init__(self, capacity: int): - self.cap = capacity - self.cache = {} # map key to node - - self.left, self.right = Node(0, 0), Node(0, 0) - self.left.next, self.right.prev = self.right, self.left - - # remove node from list - def remove(self, node): - prev, nxt = node.prev, node.next - prev.next, nxt.prev = nxt, prev - - # insert node at right - def insert(self, node): - prev, nxt = self.right.prev, self.right - prev.next = nxt.prev = node - node.next, node.prev = nxt, prev - - def get(self, key: int) -> int: - if key in self.cache: - self.remove(self.cache[key]) - self.insert(self.cache[key]) - return self.cache[key].val - return -1 - - def put(self, key: int, value: int) -> None: - if key in self.cache: - self.remove(self.cache[key]) - self.cache[key] = Node(key, value) - self.insert(self.cache[key]) - - if len(self.cache) > self.cap: - # remove from the list and delete the LRU from hashmap - lru = self.left.next - self.remove(lru) - print(lru in self.cache) - del self.cache[lru.key] diff --git a/15-3Sum.py b/15-3Sum.py deleted file mode 100644 index e8e256717..000000000 --- a/15-3Sum.py +++ /dev/null @@ -1,22 +0,0 @@ -class Solution: - def threeSum(self, nums: List[int]) -> List[List[int]]: - res = [] - nums.sort() - - for i, a in enumerate(nums): - if i > 0 and a == nums[i - 1]: - continue - - l, r = i + 1, len(nums) - 1 - while l < r: - threeSum = a + nums[l] + nums[r] - if threeSum > 0: - r -= 1 - elif threeSum < 0: - l += 1 - else: - res.append([a, nums[l], nums[r]]) - l += 1 - while nums[l] == nums[l - 1] and l < r: - l += 1 - return res diff --git a/150-Evaluate-Reverse-Polish-Notation.py b/150-Evaluate-Reverse-Polish-Notation.py deleted file mode 100644 index f75f2b774..000000000 --- a/150-Evaluate-Reverse-Polish-Notation.py +++ /dev/null @@ -1,17 +0,0 @@ -class Solution: - def evalRPN(self, tokens: List[str]) -> int: - stack = [] - for c in tokens: - if c == "+": - stack.append(stack.pop() + stack.pop()) - elif c == "-": - a, b = stack.pop(), stack.pop() - stack.append(b - a) - elif c == "*": - stack.append(stack.pop() * stack.pop()) - elif c == "/": - a, b = stack.pop(), stack.pop() - stack.append(int(b / a)) - else: - stack.append(int(c)) - return stack[0] diff --git a/152-Maximum-Product-Subarray.py b/152-Maximum-Product-Subarray.py deleted file mode 100644 index 5297e1fbe..000000000 --- a/152-Maximum-Product-Subarray.py +++ /dev/null @@ -1,13 +0,0 @@ -class Solution: - def maxProduct(self, nums: List[int]) -> int: - # O(n)/O(1) : Time/Memory - res = max(nums) - curMin, curMax = 1, 1 - - for n in nums: - - tmp = curMax * n - curMax = max(n * curMax, n * curMin, n) - curMin = min(tmp, n * curMin, n) - res = max(res, curMax) - return res diff --git a/153-Find-Minimum-in-Rotated-Sorted-Array.py b/153-Find-Minimum-in-Rotated-Sorted-Array.py deleted file mode 100644 index 8b4caf773..000000000 --- a/153-Find-Minimum-in-Rotated-Sorted-Array.py +++ /dev/null @@ -1,16 +0,0 @@ -class Solution: - def findMin(self, nums: List[int]) -> int: - res = nums[0] - l, r = 0, len(nums) - 1 - - while l <= r: - if nums[l] < nums[r]: - res = min(res, nums[l]) - break - m = (l + r) // 2 - res = min(res, nums[m]) - if nums[m] >= nums[l]: - l = m + 1 - else: - r = m - 1 - return res diff --git a/155-Min-Stack.py b/155-Min-Stack.py deleted file mode 100644 index f0789b4c4..000000000 --- a/155-Min-Stack.py +++ /dev/null @@ -1,20 +0,0 @@ -class MinStack: - - def __init__(self): - self.stack = [] - self.minStack = [] - - def push(self, val: int) -> None: - self.stack.append(val) - val = min(val, self.minStack[-1] if self.minStack else val) - self.minStack.append(val) - - def pop(self) -> None: - self.stack.pop() - self.minStack.pop() - - def top(self) -> int: - return self.stack[-1] - - def getMin(self) -> int: - return self.minStack[-1] diff --git a/1584-Min-Cost-to-Connect-all-Points.py b/1584-Min-Cost-to-Connect-all-Points.py deleted file mode 100644 index 4126570f7..000000000 --- a/1584-Min-Cost-to-Connect-all-Points.py +++ /dev/null @@ -1,26 +0,0 @@ -class Solution: - def minCostConnectPoints(self, points: List[List[int]]) -> int: - N = len(points) - adj = { i:[] for i in range(N) } # i : list of [cost, node] - for i in range(N): - x1, y1 = points[i] - for j in range(i + 1, N): - x2, y2 = points[j] - dist = abs(x1 - x2) + abs(y1 - y2) - adj[i].append([dist, j]) - adj[j].append([dist, i]) - - # Prim's - res = 0 - visit = set() - minH = [[0, 0]] # [cost, point] - while len(visit) < N: - cost, i = heapq.heappop(minH) - if i in visit: - continue - res += cost - visit.add(i) - for neiCost, nei in adj[i]: - if nei not in visit: - heapq.heappush(minH, [neiCost, nei]) - return res diff --git a/160-Intersection-of-Two-Linked-Lists.py b/160-Intersection-of-Two-Linked-Lists.py deleted file mode 100644 index d11c79f2f..000000000 --- a/160-Intersection-of-Two-Linked-Lists.py +++ /dev/null @@ -1,13 +0,0 @@ -# Definition for singly-linked list. -# class ListNode: -# def __init__(self, x): -# self.val = x -# self.next = None - -class Solution: - def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]: - l1, l2 = headA, headB - while l1 != l2: - l1 = l1.next if l1 else headB - l2 = l2.next if l2 else headA - return l1 diff --git a/167-Two-Sum-II.py b/167-Two-Sum-II.py deleted file mode 100644 index 47ae0adde..000000000 --- a/167-Two-Sum-II.py +++ /dev/null @@ -1,13 +0,0 @@ -class Solution: - def twoSum(self, numbers: List[int], target: int) -> List[int]: - l, r = 0, len(numbers) - 1 - - while l < r: - curSum = numbers[l] + numbers[r] - - if curSum > target: - r -= 1 - elif curSum < target: - l += 1 - else: - return [l + 1, r + 1] diff --git a/17-Letter-Combinations-of-a-Phone-Number.py b/17-Letter-Combinations-of-a-Phone-Number.py deleted file mode 100644 index 515bf7247..000000000 --- a/17-Letter-Combinations-of-a-Phone-Number.py +++ /dev/null @@ -1,23 +0,0 @@ -class Solution: - def letterCombinations(self, digits: str) -> List[str]: - res = [] - digitToChar = { "2": "abc", - "3": "def", - "4": "ghi", - "5": "jkl", - "6": "mno", - "7": "qprs", - "8": "tuv", - "9": "wxyz" } - - def backtrack(i, curStr): - if len(curStr) == len(digits): - res.append(curStr) - return - for c in digitToChar[digits[i]]: - backtrack(i + 1, curStr + c) - - if digits: - backtrack(0, "") - - return res diff --git a/1851-Minimum-Interval-to-Include-Each-Query.py b/1851-Minimum-Interval-to-Include-Each-Query.py deleted file mode 100644 index d10ffe14a..000000000 --- a/1851-Minimum-Interval-to-Include-Each-Query.py +++ /dev/null @@ -1,16 +0,0 @@ -class Solution: - def minInterval(self, intervals: List[List[int]], queries: List[int]) -> List[int]: - intervals.sort() - minHeap = [] - res = {} - i = 0 - for q in sorted(queries): - while i < len(intervals) and intervals[i][0] <= q: - l, r = intervals[i] - heapq.heappush(minHeap, (r - l + 1, r)) - i += 1 - - while minHeap and minHeap[0][1] < q: - heapq.heappop(minHeap) - res[q] = minHeap[0][0] if minHeap else -1 - return [res[q] for q in queries] diff --git a/1899-Merge-Triplets-to-Form-Target-Triplet.py b/1899-Merge-Triplets-to-Form-Target-Triplet.py deleted file mode 100644 index 170563995..000000000 --- a/1899-Merge-Triplets-to-Form-Target-Triplet.py +++ /dev/null @@ -1,11 +0,0 @@ -class Solution: - def mergeTriplets(self, triplets: List[List[int]], target: List[int]) -> bool: - good = set() - - for t in triplets: - if t[0] > target[0] or t[1] > target[1] or t[2] > target[2]: - continue - for i, v in enumerate(t): - if v == target[i]: - good.add(i) - return len(good) == 3 diff --git a/19-Remove-Nth-node-from-end-of-List.py b/19-Remove-Nth-node-from-end-of-List.py deleted file mode 100644 index 09c5b9f11..000000000 --- a/19-Remove-Nth-node-from-end-of-List.py +++ /dev/null @@ -1,17 +0,0 @@ -class Solution: - def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode: - dummy = ListNode(0, head) - left = dummy - right = head - - while n > 0: - right = right.next - n -= 1 - - while right: - left = left.next - right = right.next - - # delete - left.next = left.next.next - return dummy.next diff --git a/190-Reverse-Bits.py b/190-Reverse-Bits.py deleted file mode 100644 index 84d2b6139..000000000 --- a/190-Reverse-Bits.py +++ /dev/null @@ -1,7 +0,0 @@ -class Solution: - def reverseBits(self, n: int) -> int: - res = 0 - for i in range(32): - bit = (n >> i) & 1 - res = res | (bit << (31 - i)) - return res diff --git a/1905-Count-Sub-Islands.py b/1905-Count-Sub-Islands.py deleted file mode 100644 index ed0ddea98..000000000 --- a/1905-Count-Sub-Islands.py +++ /dev/null @@ -1,27 +0,0 @@ -class Solution: - def countSubIslands(self, grid1: List[List[int]], grid2: List[List[int]]) -> int: - ROWS, COLS = len(grid1), len(grid1[0]) - visit = set() - - def dfs(r, c): - if (r < 0 or c < 0 or r == ROWS or c == COLS or - grid2[r][c] == 0 or (r, c) in visit): - return True - - visit.add((r, c)) - res = True - if grid1[r][c] == 0: - res = False - - res = dfs(r - 1, c) and res - res = dfs(r + 1, c) and res - res = dfs(r, c - 1) and res - res = dfs(r, c + 1) and res - return res - - count = 0 - for r in range(ROWS): - for c in range(COLS): - if grid2[r][c] and (r, c) not in visit and dfs(r, c): - count += 1 - return count diff --git a/191-Number-of-1-Bits.py b/191-Number-of-1-Bits.py deleted file mode 100644 index 9a61e7784..000000000 --- a/191-Number-of-1-Bits.py +++ /dev/null @@ -1,7 +0,0 @@ -class Solution: - def hammingWeight(self, n: int) -> int: - res = 0 - while n: - n &= (n - 1) - res += 1 - return res diff --git a/198-House-Robber.py b/198-House-Robber.py deleted file mode 100644 index d6f380340..000000000 --- a/198-House-Robber.py +++ /dev/null @@ -1,9 +0,0 @@ -class Solution: - def rob(self, nums: List[int]) -> int: - rob1, rob2 = 0, 0 - - for n in nums: - temp = max(n + rob1, rob2) - rob1 = rob2 - rob2 = temp - return rob2 diff --git a/199-Binary-Tree-Right-Side-View.py b/199-Binary-Tree-Right-Side-View.py deleted file mode 100644 index 11040e880..000000000 --- a/199-Binary-Tree-Right-Side-View.py +++ /dev/null @@ -1,24 +0,0 @@ -# Definition for a binary tree node. -# class TreeNode: -# def __init__(self, val=0, left=None, right=None): -# self.val = val -# self.left = left -# self.right = right -class Solution: - def rightSideView(self, root: TreeNode) -> List[int]: - res = [] - q = collections.deque([root]) - - while q: - rightSide = None - qLen = len(q) - - for i in range(qLen): - node = q.popleft() - if node: - rightSide = node - q.append(node.left) - q.append(node.right) - if rightSide: - res.append(rightSide.val) - return res diff --git a/2-Add-Two-Numbers.py b/2-Add-Two-Numbers.py deleted file mode 100644 index 63f6ed14a..000000000 --- a/2-Add-Two-Numbers.py +++ /dev/null @@ -1,27 +0,0 @@ -# Definition for singly-linked list. -# class ListNode: -# def __init__(self, val=0, next=None): -# self.val = val -# self.next = next -class Solution: - def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode: - dummy = ListNode() - cur = dummy - - carry = 0 - while l1 or l2 or carry: - v1 = l1.val if l1 else 0 - v2 = l2.val if l2 else 0 - - # new digit - val = v1 + v2 + carry - carry = val // 10 - val = val % 10 - cur.next = ListNode(val) - - # update ptrs - cur = cur.next - l1 = l1.next if l1 else None - l2 = l2.next if l2 else None - - return dummy.next diff --git a/20-Valid-Parentheses.py b/20-Valid-Parentheses.py deleted file mode 100644 index 6bb09f9a7..000000000 --- a/20-Valid-Parentheses.py +++ /dev/null @@ -1,14 +0,0 @@ -class Solution: - def isValid(self, s: str) -> bool: - Map = { ")":"(", "]":"[", "}":"{" } - stack = [] - - for c in s: - if c not in Map: - stack.append(c) - continue - if not stack or stack[-1] != Map[c]: - return False - stack.pop() - - return not stack diff --git a/200-Number-of-Islands.py b/200-Number-of-Islands.py deleted file mode 100644 index 83c1e3f07..000000000 --- a/200-Number-of-Islands.py +++ /dev/null @@ -1,28 +0,0 @@ -class Solution: - def numIslands(self, grid: List[List[str]]) -> int: - if not grid or not grid[0]: - return 0 - - islands = 0 - q = collections.deque() - visit = set() - rows, cols = len(grid), len(grid[0]) - - def dfs(r, c): - if (r not in range(rows) or - c not in range(cols) or - grid[r][c] == "0" or - (r, c) in visit): - return - - visit.add((r, c)) - directions = [[0, 1], [0, -1], [1, 0], [-1, 0]] - for dr, dc in directions: - dfs(r + dr, c + dc) - - for r in range(rows): - for c in range(cols): - if grid[r][c] == "1" and (r, c) not in visit: - islands += 1 - dfs(r, c) - return islands diff --git a/2013-Detect-Squares.py b/2013-Detect-Squares.py deleted file mode 100644 index 82f16dc9b..000000000 --- a/2013-Detect-Squares.py +++ /dev/null @@ -1,19 +0,0 @@ - -class DetectSquares: - - def __init__(self): - self.ptsCount = defaultdict(int) - self.pts = [] - - def add(self, point: List[int]) -> None: - self.ptsCount[tuple(point)] += 1 - self.pts.append(point) - - def count(self, point: List[int]) -> int: - res = 0 - px, py = point - for x, y in self.pts: - if (abs(py - y) != abs(px - x)) or x == px or y == py: - continue - res += self.ptsCount[(x, py)] * self.ptsCount[(px, y)] - return res diff --git a/202-Happy-Number.py b/202-Happy-Number.py deleted file mode 100644 index 3264c91d3..000000000 --- a/202-Happy-Number.py +++ /dev/null @@ -1,17 +0,0 @@ -class Solution: - def isHappy(self, n: int) -> bool: - slow, fast = n, self.sumSquareDigits(n) - - while slow != fast: - fast = self.sumSquareDigits(fast) - fast = self.sumSquareDigits(fast) - slow = self.sumSquareDigits(slow) - - return True if fast == 1 else False - - def sumSquareDigits(self, n): - output = 0 - while n: - output += (n % 10) ** 2 - n = n // 10 - return output diff --git a/206-Reverse-Linked-List.py b/206-Reverse-Linked-List.py deleted file mode 100644 index a1f36aeee..000000000 --- a/206-Reverse-Linked-List.py +++ /dev/null @@ -1,16 +0,0 @@ -# Definition for singly-linked list. -# class ListNode: -# def __init__(self, x): -# self.val = x -# self.next = None - -class Solution: - def reverseList(self, head: ListNode) -> ListNode: - prev, curr = None, head - - while curr: - temp = curr.next - curr.next = prev - prev = curr - curr = temp - return prev diff --git a/207-Course-Schedule.py b/207-Course-Schedule.py deleted file mode 100644 index 26f56d773..000000000 --- a/207-Course-Schedule.py +++ /dev/null @@ -1,26 +0,0 @@ -class Solution: - def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool: - # dfs - preMap = { i:[] for i in range(numCourses)} - - # map each course to : prereq list - for crs, pre in prerequisites: - preMap[crs].append(pre) - - visiting = set() - def dfs(crs): - if crs in visiting: - return False - if preMap[crs] == []: - return True - - visiting.add(crs) - for pre in preMap[crs]: - if not dfs(pre): return False - visiting.remove(crs) - preMap[crs] = [] - return True - - for c in range(numCourses): - if not dfs(c): return False - return True diff --git a/208-Implement-Trie.py b/208-Implement-Trie.py deleted file mode 100644 index 8ab6ec933..000000000 --- a/208-Implement-Trie.py +++ /dev/null @@ -1,49 +0,0 @@ -class TrieNode: - def __init__(self): - self.children = [None] * 26 - self.end = False - -class Trie: - - def __init__(self): - """ - Initialize your data structure here. - """ - self.root = TrieNode() - - - def insert(self, word: str) -> None: - """ - Inserts a word into the trie. - """ - curr = self.root - for c in word: - i = ord(c)-ord("a") - if curr.children[i] == None: - curr.children[i] = TrieNode() - curr = curr.children[i] - curr.end = True - - def search(self, word: str) -> bool: - """ - Returns if the word is in the trie. - """ - curr = self.root - for c in word: - i = ord(c)-ord("a") - if curr.children[i] == None: - return False - curr = curr.children[i] - return curr.end - - def startsWith(self, prefix: str) -> bool: - """ - Returns if there is any word in the trie that starts with the given prefix. - """ - curr = self.root - for c in prefix: - i = ord(c)-ord("a") - if curr.children[i] == None: - return False - curr = curr.children[i] - return True diff --git a/21-Merge-Two-Sorted-Lists.py b/21-Merge-Two-Sorted-Lists.py deleted file mode 100644 index eb69a1c41..000000000 --- a/21-Merge-Two-Sorted-Lists.py +++ /dev/null @@ -1,25 +0,0 @@ -# Definition for singly-linked list. -# class ListNode: -# def __init__(self, val=0, next=None): -# self.val = val -# self.next = next -class Solution: - def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode: - dummy = ListNode() - tail = dummy - - while l1 and l2: - if l1.val < l2.val: - tail.next = l1 - l1 = l1.next - else: - tail.next = l2 - l2 = l2.next - tail = tail.next - - if l1: - tail.next = l1 - elif l2: - tail.next = l2 - - return dummy.next diff --git a/210-Course-Schedule-II.py b/210-Course-Schedule-II.py deleted file mode 100644 index 5c52edf67..000000000 --- a/210-Course-Schedule-II.py +++ /dev/null @@ -1,27 +0,0 @@ -class Solution: - def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -> List[int]: - prereq = { c:[] for c in range(numCourses) } - for crs, pre in prerequisites: - prereq[crs].append(pre) - - output = [] - visit, cycle = set(), set() - def dfs(crs): - if crs in cycle: - return False - if crs in visit: - return True - - cycle.add(crs) - for pre in prereq[crs]: - if dfs(pre) == False: - return False - cycle.remove(crs) - visit.add(crs) - output.append(crs) - return True - - for c in range(numCourses): - if dfs(c) == False: - return [] - return output diff --git a/211-Design-Add-and-Search-Words-Data-Structure.py b/211-Design-Add-and-Search-Words-Data-Structure.py deleted file mode 100644 index a55cb042e..000000000 --- a/211-Design-Add-and-Search-Words-Data-Structure.py +++ /dev/null @@ -1,35 +0,0 @@ -class TrieNode: - def __init__(self): - self.children = {} # a : TrieNode - self.word = False - -class WordDictionary: - def __init__(self): - self.root = TrieNode() - - def addWord(self, word: str) -> None: - cur = self.root - for c in word: - if c not in cur.children: - cur.children[c] = TrieNode() - cur = cur.children[c] - cur.word = True - - def search(self, word: str) -> bool: - def dfs(j, root): - cur = root - - for i in range(j, len(word)): - c = word[i] - if c == ".": - for child in cur.children.values(): - if dfs(i + 1, child): - return True - return False - else: - if c not in cur.children: - return False - cur = cur.children[c] - return cur.word - - return dfs(0, self.root) diff --git a/212-Word-Search-II.py b/212-Word-Search-II.py deleted file mode 100644 index 476ffbd32..000000000 --- a/212-Word-Search-II.py +++ /dev/null @@ -1,46 +0,0 @@ -class TrieNode: - def __init__(self): - self.children = {} - self.isWord = False - - def addWord(self, word): - cur = self - for c in word: - if c not in cur.children: - cur.children[c] = TrieNode() - cur = cur.children[c] - cur.isWord = True - - -class Solution: - def findWords(self, board: List[List[str]], words: List[str]) -> List[str]: - root = TrieNode() - for w in words: - root.addWord(w) - - ROWS, COLS = len(board), len(board[0]) - res, visit = set(), set() - - def dfs(r, c, node, word): - if (r < 0 or c < 0 or - r == ROWS or c == COLS or - board[r][c] not in node.children or (r, c) in visit): - return - - visit.add((r, c)) - node = node.children[board[r][c]] - word += board[r][c] - if node.isWord: - res.add(word) - - dfs(r + 1, c, node, word) - dfs(r - 1, c, node, word) - dfs(r, c + 1, node, word) - dfs(r, c - 1, node, word) - visit.remove((r, c)) - - for r in range(ROWS): - for c in range(COLS): - dfs(r, c, root, "") - - return list(res) diff --git a/213-House-Robber-II.py b/213-House-Robber-II.py deleted file mode 100644 index e03cef8ac..000000000 --- a/213-House-Robber-II.py +++ /dev/null @@ -1,12 +0,0 @@ -class Solution: - def rob(self, nums: List[int]) -> int: - return max(nums[0], self.helper(nums[1:]), self.helper(nums[:-1])) - - def helper(self, nums): - rob1, rob2 = 0, 0 - - for n in nums: - newRob = max(rob1 + n, rob2) - rob1 = rob2 - rob2 = newRob - return rob2 diff --git a/215-Kth-Largest-Element-in-an-Array.py b/215-Kth-Largest-Element-in-an-Array.py deleted file mode 100644 index 58221e21a..000000000 --- a/215-Kth-Largest-Element-in-an-Array.py +++ /dev/null @@ -1,20 +0,0 @@ -class Solution: - def findKthLargest(self, nums: List[int], k: int) -> int: - nums.sort() - return nums[len(nums) - k] - - k = len(nums) - k - def quickSelect(l, r): - if l == r: return nums[l] - - pivot, p = nums[r], l - for i in range(l, r): - if nums[i] <= pivot: - nums[p], nums[i] = nums[i], nums[p] - p += 1 - nums[p], nums[r] = nums[r], nums[p] - - if p > k: return quickSelect(l, p - 1) - elif p < k: return quickSelect(p + 1, r) - else: return nums[p] - return quickSelect(0, len(nums) - 1) diff --git a/217-Contains-Duplicate.py b/217-Contains-Duplicate.py deleted file mode 100644 index 344305a26..000000000 --- a/217-Contains-Duplicate.py +++ /dev/null @@ -1,9 +0,0 @@ -class Solution: - def containsDuplicate(self, nums: List[int]) -> bool: - hashset = set() - - for n in nums: - if n in hashset: - return True - hashset.add(n) - return False diff --git a/22-Generate-Parentheses.py b/22-Generate-Parentheses.py deleted file mode 100644 index 82e2e7e26..000000000 --- a/22-Generate-Parentheses.py +++ /dev/null @@ -1,21 +0,0 @@ -class Solution: - def generateParenthesis(self, n: int) -> List[str]: - stack = [] - res = [] - - def backtrack(openN, closedN): - if openN == closedN == n: - res.append("".join(stack)) - return - - if openN < n: - stack.append("(") - backtrack(openN + 1, closedN) - stack.pop() - if closedN < openN: - stack.append(")") - backtrack(openN, closedN + 1) - stack.pop() - - backtrack(0, 0) - return res diff --git a/221-Maximal-Square.py b/221-Maximal-Square.py deleted file mode 100644 index 944b21965..000000000 --- a/221-Maximal-Square.py +++ /dev/null @@ -1,20 +0,0 @@ -class Solution: - def maximalSquare(self, matrix: List[List[str]]) -> int: - ROWS, COLS = len(matrix), len(matrix[0]) - cache = {} # map each (r, c) -> maxLength of square - - def helper(r, c): - if r >= ROWS or c >= COLS: - return 0 - - if (r, c) not in cache: - down = helper(r + 1, c) - right = helper(r, c + 1) - diag = helper(r + 1, c + 1) - - cache[(r, c)] = 0 - if matrix[r][c] == "1": - cache[(r, c)] = 1 + min(down, right, diag) - return cache[(r, c)] - helper(0, 0) - return max(cache.values()) ** 2 diff --git a/226-Invert-Binary-Tree.py b/226-Invert-Binary-Tree.py deleted file mode 100644 index 36de77ef4..000000000 --- a/226-Invert-Binary-Tree.py +++ /dev/null @@ -1,19 +0,0 @@ -# Definition for a binary tree node. -# class TreeNode: -# def __init__(self, val=0, left=None, right=None): -# self.val = val -# self.left = left -# self.right = right -class Solution: - def invertTree(self, root: TreeNode) -> TreeNode: - if not root: - return None - - # swap the children - tmp = root.left - root.left = root.right - root.right = tmp - - self.invertTree(root.left) - self.invertTree(root.right) - return root diff --git a/23-Merge-K-Sorted-Lists.py b/23-Merge-K-Sorted-Lists.py deleted file mode 100644 index 54cabf592..000000000 --- a/23-Merge-K-Sorted-Lists.py +++ /dev/null @@ -1,36 +0,0 @@ -# Definition for singly-linked list. -# class ListNode: -# def __init__(self, val=0, next=None): -# self.val = val -# self.next = next -class Solution: - def mergeKLists(self, lists: List[ListNode]) -> ListNode: - if not lists or len(lists) == 0: - return None - - while len(lists) > 1: - mergedLists = [] - for i in range(0, len(lists), 2): - l1 = lists[i] - l2 = lists[i + 1] if (i + 1) < len(lists) else None - mergedLists.append(self.mergeList(l1, l2)) - lists = mergedLists - return lists[0] - - def mergeList(self, l1, l2): - dummy = ListNode() - tail = dummy - - while l1 and l2: - if l1.val < l2.val: - tail.next = l1 - l1 = l1.next - else: - tail.next = l2 - l2 = l2.next - tail = tail.next - if l1: - tail.next = l1 - if l2: - tail.next = l2 - return dummy.next diff --git a/230-Kth-Smallest-Element-in-a-BST.py b/230-Kth-Smallest-Element-in-a-BST.py deleted file mode 100644 index f79a0be6d..000000000 --- a/230-Kth-Smallest-Element-in-a-BST.py +++ /dev/null @@ -1,21 +0,0 @@ -# Definition for a binary tree node. -# class TreeNode: -# def __init__(self, x): -# self.val = x -# self.left = None -# self.right = None - -class Solution: - def kthSmallest(self, root: TreeNode, k: int) -> int: - stack = [] - curr = root - - while stack or curr: - while curr: - stack.append(curr) - curr = curr.left - curr = stack.pop() - k -= 1 - if k == 0: - return curr.val - curr = curr.right diff --git a/236-Lowest-Common-Ancestor-of-a-Binary-Tree.py b/236-Lowest-Common-Ancestor-of-a-Binary-Tree.py deleted file mode 100644 index 1c33c28b0..000000000 --- a/236-Lowest-Common-Ancestor-of-a-Binary-Tree.py +++ /dev/null @@ -1,29 +0,0 @@ -# Definition for a binary tree node. -# class TreeNode: -# def __init__(self, x): -# self.val = x -# self.left = None -# self.right = None - -class Solution: - res = None - def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': - if not root or not p or not q: return None - - def search(root, p, q): - if not root: return False - mid = left = right = False - if root.val == p.val or root.val == q.val: - mid = True - - left = search(root.left, p, q) - right = search(root.right, p, q) - if mid: - if left or right: - self.res = root - elif left and right: - self.res = root - return mid or left or right - - search(root, p, q) - return self.res diff --git a/238-Product-of-array-except-self.py b/238-Product-of-array-except-self.py deleted file mode 100644 index 77ccdf892..000000000 --- a/238-Product-of-array-except-self.py +++ /dev/null @@ -1,14 +0,0 @@ -class Solution: - def productExceptSelf(self, nums: List[int]) -> List[int]: - res = [1] * (len(nums)) - - prefix = 1 - for i in range(len(nums)): - res[i] = prefix - prefix *= nums[i] - postfix = 1 - for i in range(len(nums) - 1, -1, -1): - res[i] *= postfix - postfix *= nums[i] - return res - diff --git a/239-Sliding-Window-Maximum.py b/239-Sliding-Window-Maximum.py deleted file mode 100644 index d78266eb6..000000000 --- a/239-Sliding-Window-Maximum.py +++ /dev/null @@ -1,19 +0,0 @@ -class Solution: - def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]: - indexQ = deque() - valQ = deque() - - res = [] - for i, n in enumerate(nums): - while valQ and n > valQ[-1]: - valQ.pop() - indexQ.pop() - valQ.append(n) - indexQ.append(i) - - while i - indexQ[0] + 1 > k: - valQ.popleft() - indexQ.popleft() - if i + 1 >= k: - res.append(valQ[0]) - return res diff --git a/24-Swap-Nodes-in-Pairs.py b/24-Swap-Nodes-in-Pairs.py deleted file mode 100644 index 5781a78f3..000000000 --- a/24-Swap-Nodes-in-Pairs.py +++ /dev/null @@ -1,25 +0,0 @@ -# Definition for singly-linked list. -# class ListNode: -# def __init__(self, val=0, next=None): -# self.val = val -# self.next = next -class Solution: - def swapPairs(self, head: ListNode) -> ListNode: - dummy = ListNode(0, head) - prev, curr = dummy, head - - while curr and curr.next: - # save ptrs - nxtPair = curr.next.next - second = curr.next - - # reverse this pair - second.next = curr - curr.next = nxtPair - prev.next = second - - # update ptrs - prev = curr - curr = nxtPair - - return dummy.next diff --git a/242-Valid-Anagrams.py b/242-Valid-Anagrams.py deleted file mode 100644 index ffb09d661..000000000 --- a/242-Valid-Anagrams.py +++ /dev/null @@ -1,14 +0,0 @@ -class Solution: - def isAnagram(self, s: str, t: str) -> bool: - if len(s) != len(t): - return False - - countS, countT = {}, {} - - for i in range(len(s)): - countS[s[i]] = 1 + countS.get(s[i], 0) - countT[t[i]] = 1 + countT.get(t[i], 0) - for c in countS: - if countS[c] != countT.get(c, 0): - return False - return True diff --git a/25-Reverse-Nodes-in-K-Group.py b/25-Reverse-Nodes-in-K-Group.py deleted file mode 100644 index 0add4f17d..000000000 --- a/25-Reverse-Nodes-in-K-Group.py +++ /dev/null @@ -1,29 +0,0 @@ -class Solution: - def reverseKGroup(self, head: ListNode, k: int) -> ListNode: - dummy = ListNode(0, head) - groupPrev = dummy - - while True: - kth = self.getKth(groupPrev, k) - if not kth: - break - groupNext = kth.next - - # reverse group - prev, curr = kth.next, groupPrev.next - while curr != groupNext: - tmp = curr.next - curr.next = prev - prev = curr - curr = tmp - - tmp = groupPrev.next - groupPrev.next = kth - groupPrev = tmp - return dummy.next - - def getKth(self, curr, k): - while curr and k > 0: - curr = curr.next - k -= 1 - return curr diff --git a/252-Meeting-Rooms.py b/252-Meeting-Rooms.py deleted file mode 100644 index 85b827b8b..000000000 --- a/252-Meeting-Rooms.py +++ /dev/null @@ -1,15 +0,0 @@ -class Solution: - """ - @param intervals: an array of meeting time intervals - @return: if a person could attend all meetings - """ - def canAttendMeetings(self, intervals): - intervals.sort(key = lambda i : i.start) - - for i in range(1, len(intervals)): - i1 = intervals[i - 1] - i2 = intervals[i] - - if i1.end > i2.start: - return False - return True diff --git a/253-Meeting-Rooms-ii.py b/253-Meeting-Rooms-ii.py deleted file mode 100644 index f58fb7a20..000000000 --- a/253-Meeting-Rooms-ii.py +++ /dev/null @@ -1,20 +0,0 @@ -class Solution: - """ - @param intervals: an array of meeting time intervals - @return: the minimum number of conference rooms required - """ - def minMeetingRooms(self, intervals): - start = sorted([i.start for i in intervals]) - end = sorted([i.end for i in intervals]) - - res, count = 0, 0 - s, e = 0, 0 - while s < len(intervals): - if start[s] < end[e]: - s += 1 - count += 1 - else: - e += 1 - count -= 1 - res = max(res, count) - return res diff --git a/261-Graph-Valid-Tree.py b/261-Graph-Valid-Tree.py deleted file mode 100644 index 0f5e5f36b..000000000 --- a/261-Graph-Valid-Tree.py +++ /dev/null @@ -1,29 +0,0 @@ -# Problem is free on Lintcode -class Solution: - """ - @param n: An integer - @param edges: a list of undirected edges - @return: true if it's a valid tree, or false - """ - def validTree(self, n, edges): - if not n: - return True - adj = { i:[] for i in range(n) } - for n1, n2 in edges: - adj[n1].append(n2) - adj[n2].append(n1) - - visit = set() - def dfs(i, prev): - if i in visit: - return False - - visit.add(i) - for j in adj[i]: - if j == prev: - continue - if not dfs(j, i): - return False - return True - - return dfs(0, -1) and n == len(visit) diff --git a/268-Missing-Number.py b/268-Missing-Number.py deleted file mode 100644 index 1e90165d8..000000000 --- a/268-Missing-Number.py +++ /dev/null @@ -1,7 +0,0 @@ -class Solution: - def missingNumber(self, nums: List[int]) -> int: - res = len(nums) - - for i in range(len(nums)): - res += (i - nums[i]) - return res diff --git a/271-Encode-and-Decode-Strings.py b/271-Encode-and-Decode-Strings.py deleted file mode 100644 index b96c9a028..000000000 --- a/271-Encode-and-Decode-Strings.py +++ /dev/null @@ -1,26 +0,0 @@ -class Solution: - """ - @param: strs: a list of strings - @return: encodes a list of strings to a single string. - """ - def encode(self, strs): - res = "" - for s in strs: - res += str(len(s)) + "#" + s - return res - - """ - @param: str: A string - @return: dcodes a single string to a list of strings - """ - def decode(self, str): - res, i = [], 0 - - while i < len(str): - j = i - while str[j] != "#": - j += 1 - length = int(str[i:j]) - res.append(str[j + 1 : j + 1 + length]) - i = j + 1 + length - return res diff --git a/28-Implement-strStr.py b/28-Implement-strStr.py deleted file mode 100644 index e43bb4564..000000000 --- a/28-Implement-strStr.py +++ /dev/null @@ -1,30 +0,0 @@ -class Solution: - def strStr(self, haystack: str, needle: str) -> int: - if needle == "": return 0 - lps = [0] * len(needle) - - prevLPS, i = 0, 1 - while i < len(needle): - if needle[i] == needle[prevLPS]: - lps[i] = prevLPS + 1 - prevLPS += 1 - i += 1 - elif prevLPS == 0: - lps[i] = 0 - i += 1 - else: - prevLPS = lps[prevLPS - 1] - - i = 0 # ptr for haystack - j = 0 # ptr for needle - while i < len(haystack): - if haystack[i] == needle[j]: - i, j = i + 1, j + 1 - else: - if j == 0: - i += 1 - else: - j = lps[j - 1] - if j == len(needle): - return i - len(needle) - return -1 diff --git a/286-Walls-and-Gates.py b/286-Walls-and-Gates.py deleted file mode 100644 index 785df81f7..000000000 --- a/286-Walls-and-Gates.py +++ /dev/null @@ -1,27 +0,0 @@ -class Solution: - """ - @param rooms: m x n 2D grid - @return: nothing - """ - def walls_and_gates(self, rooms: List[List[int]]): - ROWS, COLS = len(rooms), len(rooms[0]) - visit = set() - q = deque() - - def addRooms(r, c): - if (min(r, c) < 0 or r == ROWS or c == COLS or - (r, c) in visit or rooms[r][c] == -1): - return - visit.add((r, c)) - q.append([r, c]) - - dist = 0 - while q: - for i in range(len(q)): - r, c = q.popleft() - rooms[r][c] = dist - addRooms(r + 1, c) - addRooms(r - 1, c) - addRooms(r, c + 1) - addRooms(r, c - 1) - dist += 1 diff --git a/287-Find-The-Duplicate-Number.py b/287-Find-The-Duplicate-Number.py deleted file mode 100644 index cf498bf8b..000000000 --- a/287-Find-The-Duplicate-Number.py +++ /dev/null @@ -1,15 +0,0 @@ -class Solution: - def findDuplicate(self, nums: List[int]) -> int: - slow, fast = 0, 0 - while True: - slow = nums[slow] - fast = nums[nums[fast]] - if slow == fast: - break - - slow2 = 0 - while True: - slow = nums[slow] - slow2 = nums[slow2] - if slow == slow2: - return slow diff --git a/295-Find-Median-from-Data-Stream.py b/295-Find-Median-from-Data-Stream.py deleted file mode 100644 index d2a5cc404..000000000 --- a/295-Find-Median-from-Data-Stream.py +++ /dev/null @@ -1,30 +0,0 @@ -class MedianFinder: - - def __init__(self): - """ - initialize your data structure here. - """ - # two heaps, large, small, minheap, maxheap - # heaps should be equal size - self.small, self.large = [], [] # maxHeap, minHeap (python default) - - def addNum(self, num: int) -> None: - heapq.heappush(self.small, -1 * num) - - if (self.small and self.large and (-1 * self.small[0]) > self.large[0]): - val = -1 * heapq.heappop(self.small) - heapq.heappush(self.large, val) - - if len(self.small) > len(self.large) + 1: - val = -1 * heapq.heappop(self.small) - heapq.heappush(self.large, val) - if len(self.large) > len(self.small) + 1: - val = heapq.heappop(self.large) - heapq.heappush(self.small, -1 * val) - - def findMedian(self) -> float: - if len(self.small) > len(self.large): - return -1 * self.small[0] - elif len(self.large) > len(self.small): - return self.large[0] - return (-1 * self.small[0] + self.large[0]) / 2 diff --git a/297-Serialize-and-Deserialize-Binary-Tree.py b/297-Serialize-and-Deserialize-Binary-Tree.py deleted file mode 100644 index 764c08422..000000000 --- a/297-Serialize-and-Deserialize-Binary-Tree.py +++ /dev/null @@ -1,36 +0,0 @@ -# Definition for a binary tree node. -# class TreeNode(object): -# def __init__(self, x): -# self.val = x -# self.left = None -# self.right = None - -class Codec: - - def serialize(self, root): - res = [] - - def dfs(node): - if not node: - res.append("N") - return - res.append(str(node.val)) - dfs(node.left) - dfs(node.right) - dfs(root) - return ",".join(res) - - def deserialize(self, data): - vals = data.split(",") - self.i = 0 - - def dfs(): - if vals[self.i] == "N": - self.i += 1 - return None - node = TreeNode(int(vals[self.i])) - self.i += 1 - node.left = dfs() - node.right = dfs() - return node - return dfs() diff --git a/3-Longest-Substring-Without-Repeating-Characters.py b/3-Longest-Substring-Without-Repeating-Characters.py deleted file mode 100644 index e37d07824..000000000 --- a/3-Longest-Substring-Without-Repeating-Characters.py +++ /dev/null @@ -1,13 +0,0 @@ -class Solution: - def lengthOfLongestSubstring(self, s: str) -> int: - charSet = set() - l = 0 - res = 0 - - for r in range(len(s)): - while s[r] in charSet: - charSet.remove(s[l]) - l += 1 - charSet.add(s[r]) - res = max(res, r - l + 1) - return res diff --git a/300-Longest-Increasing-Subsequence.py b/300-Longest-Increasing-Subsequence.py deleted file mode 100644 index 8799e78b5..000000000 --- a/300-Longest-Increasing-Subsequence.py +++ /dev/null @@ -1,9 +0,0 @@ -class Solution: - def lengthOfLIS(self, nums: List[int]) -> int: - LIS = [1] * len(nums) - - for i in range(len(nums) - 1, -1, -1): - for j in range(i + 1, len(nums)): - if nums[i] < nums[j]: - LIS[i] = max(LIS[i], 1 + LIS[j]) - return max(LIS) diff --git a/309-Best-Time-To-Buy-and-Sell-Stock-With-Cooldown.py b/309-Best-Time-To-Buy-and-Sell-Stock-With-Cooldown.py deleted file mode 100644 index 582475cb1..000000000 --- a/309-Best-Time-To-Buy-and-Sell-Stock-With-Cooldown.py +++ /dev/null @@ -1,24 +0,0 @@ -class Solution: - def maxProfit(self, prices: List[int]) -> int: - # State: Buying or Selling? - # If Buy -> i + 1 - # If Sell -> i + 2 - - dp = {} # key=(i, buying) val=max_profit - - def dfs(i, buying): - if i >= len(prices): - return 0 - if (i, buying) in dp: - return dp[(i, buying)] - - cooldown = dfs(i + 1, buying) - if buying: - buy = dfs(i + 1, not buying) - prices[i] - dp[(i, buying)] = max(buy, cooldown) - else: - sell = dfs(i + 2, not buying) + prices[i] - dp[(i, buying)] = max(sell, cooldown) - return dp[(i, buying)] - - return dfs(0, True) diff --git a/312-Burst-Balloons.py b/312-Burst-Balloons.py deleted file mode 100644 index f3199e8ac..000000000 --- a/312-Burst-Balloons.py +++ /dev/null @@ -1,13 +0,0 @@ -class Solution: - def maxCoins(self, nums: List[int]) -> int: - cache = {} - nums = [1] + nums + [1] - - for offset in range(2, len(nums)): - for left in range(len(nums) - offset): - right = left + offset - for pivot in range(left + 1, right): - coins = nums[left] * nums[pivot] * nums[right] - coins += cache.get((left, pivot), 0) + cache.get((pivot, right), 0) - cache[(left, right)] = max(coins, cache.get((left, right), 0)) - return cache.get((0, len(nums) - 1), 0) diff --git a/322-Coin-Change.py b/322-Coin-Change.py deleted file mode 100644 index ef870465a..000000000 --- a/322-Coin-Change.py +++ /dev/null @@ -1,10 +0,0 @@ -class Solution: - def coinChange(self, coins: List[int], amount: int) -> int: - dp = [amount + 1] * (amount + 1) - dp[0] = 0 - - for a in range(1, amount + 1): - for c in coins: - if a - c >= 0: - dp[a] = min(dp[a], 1 + dp[a - c]) - return dp[amount] if dp[amount] != amount + 1 else -1 diff --git a/329-Longest-Increasing-Path-in-a-Matrix.py b/329-Longest-Increasing-Path-in-a-Matrix.py deleted file mode 100644 index bea327654..000000000 --- a/329-Longest-Increasing-Path-in-a-Matrix.py +++ /dev/null @@ -1,25 +0,0 @@ -class Solution: - def longestIncreasingPath(self, matrix: List[List[int]]) -> int: - ROWS, COLS = len(matrix), len(matrix[0]) - dp = {} # (r, c) -> LIP - - def dfs(r, c, prevVal): - if (r < 0 or r == ROWS or - c < 0 or c == COLS or - matrix[r][c] <= prevVal): - return 0 - if (r, c) in dp: - return dp[(r, c)] - - res = 1 - res = max(res, 1 + dfs(r + 1, c, matrix[r][c])) - res = max(res, 1 + dfs(r - 1, c, matrix[r][c])) - res = max(res, 1 + dfs(r, c + 1, matrix[r][c])) - res = max(res, 1 + dfs(r, c - 1, matrix[r][c])) - dp[(r, c)] = res - return res - - for r in range(ROWS): - for c in range(COLS): - dfs(r, c, -1) - return max(dp.values()) diff --git a/33-Search-In-Rotated-Sorted-Array.py b/33-Search-In-Rotated-Sorted-Array.py deleted file mode 100644 index faf1939cd..000000000 --- a/33-Search-In-Rotated-Sorted-Array.py +++ /dev/null @@ -1,22 +0,0 @@ -class Solution: - def search(self, nums: List[int], target: int) -> int: - l, r = 0, len(nums) - 1 - - while l <= r: - mid = (l + r) // 2 - if target == nums[mid]: - return mid - - # left sorted portion - if nums[l] <= nums[mid]: - if target > nums[mid] or target < nums[l]: - l = mid + 1 - else: - r = mid - 1 - # right sorted portion - else: - if target < nums[mid] or target > nums[r]: - r = mid - 1 - else: - l = mid + 1 - return -1 diff --git a/332-Reconstruct-Itinerary.py b/332-Reconstruct-Itinerary.py deleted file mode 100644 index 7ff52ba29..000000000 --- a/332-Reconstruct-Itinerary.py +++ /dev/null @@ -1,26 +0,0 @@ -class Solution: - def findItinerary(self, tickets: List[List[str]]) -> List[str]: - adj = { u:collections.deque() for u, v in tickets } - res = ["JFK"] - - tickets.sort() - for u, v in tickets: - adj[u].append(v) - - def dfs(cur): - if len(res) == len(tickets) + 1: - return True - if cur not in adj: - return False - - temp = list(adj[cur]) - for v in temp: - adj[cur].popleft() - res.append(v) - if dfs(v): - return res - res.pop() - adj[cur].append(v) - return False - dfs("JFK") - return res diff --git a/338-Counting-Bits.py b/338-Counting-Bits.py deleted file mode 100644 index 81e7f3655..000000000 --- a/338-Counting-Bits.py +++ /dev/null @@ -1,10 +0,0 @@ -class Solution: - def countBits(self, n: int) -> List[int]: - dp = [0] * (n + 1) - offset = 1 - - for i in range(1, n + 1): - if offset * 2 == i: - offset = i - dp[i] = 1 + dp[i - offset] - return dp diff --git a/34-Find-First-and-Last-Position-of-Element-in-Sorted-Array.py b/34-Find-First-and-Last-Position-of-Element-in-Sorted-Array.py deleted file mode 100644 index ca3a84f5f..000000000 --- a/34-Find-First-and-Last-Position-of-Element-in-Sorted-Array.py +++ /dev/null @@ -1,23 +0,0 @@ -class Solution: - def searchRange(self, nums: List[int], target: int) -> List[int]: - left = self.binSearch(nums, target, True) - right = self.binSearch(nums, target, False) - return [left, right] - - # leftBias=[True/False], if false, res is rightBiased - def binSearch(self, nums, target, leftBias): - l, r = 0, len(nums) - 1 - i = -1 - while l <= r: - m = (l + r) // 2 - if target > nums[m]: - l = m + 1 - elif target < nums[m]: - r = m - 1 - else: - i = m - if leftBias: - r = m - 1 - else: - l = m + 1 - return i diff --git a/347-Top-k-frequent-elements.py b/347-Top-k-frequent-elements.py deleted file mode 100644 index 953241d42..000000000 --- a/347-Top-k-frequent-elements.py +++ /dev/null @@ -1,18 +0,0 @@ -class Solution: - def topKFrequent(self, nums: List[int], k: int) -> List[int]: - count = {} - freq = [[] for i in range(len(nums) + 1)] - - for n in nums: - count[n] = 1 + count.get(n, 0) - for n, c in count.items(): - freq[c].append(n) - - res = [] - for i in range(len(freq) - 1, 0, -1): - for n in freq[i]: - res.append(n) - if len(res) == k: - return res - - # O(n) diff --git a/355-Design-Twitter.py b/355-Design-Twitter.py deleted file mode 100644 index 7dce41b88..000000000 --- a/355-Design-Twitter.py +++ /dev/null @@ -1,36 +0,0 @@ -class Twitter: - - def __init__(self): - self.count = 0 - self.tweetMap = defaultdict(list) # userId -> list of [count, tweetIds] - self.followMap = defaultdict(set) # userId -> set of followeeId - - def postTweet(self, userId: int, tweetId: int) -> None: - self.tweetMap[userId].append([self.count, tweetId]) - self.count -= 1 - - def getNewsFeed(self, userId: int) -> List[int]: - res = [] - minHeap = [] - - self.followMap[userId].add(userId) - for followeeId in self.followMap[userId]: - if followeeId in self.tweetMap: - index = len(self.tweetMap[followeeId]) - 1 - count, tweetId = self.tweetMap[followeeId][index] - heapq.heappush(minHeap, [count, tweetId, followeeId, index - 1]) - - while minHeap and len(res) < 10: - count, tweetId, followeeId, index = heapq.heappop(minHeap) - res.append(tweetId) - if index >= 0: - count, tweetId = self.tweetMap[followeeId][index] - heapq.heappush(minHeap, [count, tweetId, followeeId, index - 1]) - return res - - def follow(self, followerId: int, followeeId: int) -> None: - self.followMap[followerId].add(followeeId) - - def unfollow(self, followerId: int, followeeId: int) -> None: - if followeeId in self.followMap[followerId]: - self.followMap[followerId].remove(followeeId) diff --git a/36-Valid-Sudoku.py b/36-Valid-Sudoku.py deleted file mode 100644 index 81821662e..000000000 --- a/36-Valid-Sudoku.py +++ /dev/null @@ -1,19 +0,0 @@ -class Solution: - def isValidSudoku(self, board: List[List[str]]) -> bool: - cols = collections.defaultdict(set) - rows = collections.defaultdict(set) - squares = collections.defaultdict(set) # key = (r /3, c /3) - - for r in range(9): - for c in range(9): - if board[r][c] == ".": - continue - if (board[r][c] in rows[r] or - board[r][c] in cols[c] or - board[r][c] in squares[(r // 3, c // 3)]): - return False - cols[c].add(board[r][c]) - rows[r].add(board[r][c]) - squares[(r // 3, c // 3)].add(board[r][c]) - - return True diff --git a/371-Sum-of-Two-Integers.java b/371-Sum-of-Two-Integers.java deleted file mode 100644 index ff641469f..000000000 --- a/371-Sum-of-Two-Integers.java +++ /dev/null @@ -1,10 +0,0 @@ -class Solution { - public int getSum(int a, int b) { - while (b != 0) { - int tmp = (a & b) << 1; - a = (a ^ b); - b = tmp; - } - return a; - } -} diff --git a/371-Sum-of-Two-Integers.py b/371-Sum-of-Two-Integers.py deleted file mode 100644 index 476bfe662..000000000 --- a/371-Sum-of-Two-Integers.py +++ /dev/null @@ -1,16 +0,0 @@ -class Solution: - def getSum(self, a: int, b: int) -> int: - def add(a, b): - if not a or not b: - return a or b - return add(a^b, (a&b) << 1) - - if a*b < 0: # assume a < 0, b > 0 - if a > 0: - return self.getSum(b, a) - if add(~a, 1) == b: # -a == b - return 0 - if add(~a, 1) < b: # -a < b - return add(~add(add(~a, 1), add(~b, 1)),1) # -add(-a, -b) - - return add(a, b) # a*b >= 0 or (-a) > b > 0 diff --git a/377-Combination-Sum-IV.py b/377-Combination-Sum-IV.py deleted file mode 100644 index 1b56bb779..000000000 --- a/377-Combination-Sum-IV.py +++ /dev/null @@ -1,23 +0,0 @@ -class Solution: - def combinationSum4(self, nums: List[int], target: int) -> int: - cache = { 0:1 } - - for total in range(1, target + 1): - cache[total] = 0 - for n in nums: - cache[total] += cache.get(total - n, 0) - return cache[target] - - def dfs(total): - if total == target: - return 1 - if total > target: - return 0 - if total in cache: - return cache[total] - - cache[total] = 0 - for n in nums: - cache[total] += dfs(total + n) - return cache[total] - return dfs(0) diff --git a/39-Combination-Sum.py b/39-Combination-Sum.py deleted file mode 100644 index f156c10f8..000000000 --- a/39-Combination-Sum.py +++ /dev/null @@ -1,18 +0,0 @@ -class Solution: - def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: - res = [] - - def dfs(i, cur, total): - if total == target: - res.append(cur.copy()) - return - if i >= len(candidates) or total > target: - return - - cur.append(candidates[i]) - dfs(i, cur, total + candidates[i]) - cur.pop() - dfs(i + 1, cur, total) - - dfs(0, [], 0) - return res diff --git a/394-decode-string.py b/394-decode-string.py deleted file mode 100644 index 445a81cfd..000000000 --- a/394-decode-string.py +++ /dev/null @@ -1,20 +0,0 @@ -class Solution: - def decodeString(self, s: str) -> str: - stack = [] - - for char in s: - if char is not "]": - stack.append(char) - else: - sub_str = "" - while stack[-1] is not "[": - sub_str = stack.pop()+sub_str - stack.pop() - - multiplier = "" - while stack and stack[-1].isdigit(): - multiplier = stack.pop() + multiplier - - stack.append(int(multiplier)*sub_str) - - return "".join(stack) \ No newline at end of file diff --git a/4-median-of-two-sorted-arrays.py b/4-median-of-two-sorted-arrays.py deleted file mode 100644 index 6ade3557b..000000000 --- a/4-median-of-two-sorted-arrays.py +++ /dev/null @@ -1,32 +0,0 @@ -# Time: log(min(n, m)) - -class Solution: - def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float: - A, B = nums1, nums2 - total = len(nums1) + len(nums2) - half = total // 2 - - if len(B) < len(A): - A, B = B, A - - l, r = 0, len(A) - 1 - while True: - i = (l + r) // 2 # A - j = half - i - 2 # B - - Aleft = A[i] if i >= 0 else float("-infinity") - Aright = A[i + 1] if (i + 1) < len(A) else float("infinity") - Bleft = B[j] if j >= 0 else float("-infinity") - Bright = B[j + 1] if (j + 1) < len(B) else float("infinity") - - # partition is correct - if Aleft <= Bright and Bleft <= Aright: - # odd - if total % 2: - return min(Aright, Bright) - # even - return (max(Aleft, Bleft) + min(Aright, Bright)) / 2 - elif Aleft > Bright: - r = i - 1 - else: - l = i + 1 diff --git a/40-Combination-Sum-II.py b/40-Combination-Sum-II.py deleted file mode 100644 index e85438173..000000000 --- a/40-Combination-Sum-II.py +++ /dev/null @@ -1,22 +0,0 @@ -class Solution: - def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]: - candidates.sort() - - res = [] - def backtrack(cur, pos, target): - if target == 0: - res.append(cur.copy()) - if target <= 0: - return - - prev = -1 - for i in range(pos, len(candidates)): - if candidates[i] == prev: - continue - cur.append(candidates[i]) - backtrack(cur, i + 1, target - candidates[i]) - cur.pop() - prev = candidates[i] - - backtrack([], 0, target) - return res diff --git a/40-Combinations-Sum-ii.py b/40-Combinations-Sum-ii.py deleted file mode 100644 index 27d39d393..000000000 --- a/40-Combinations-Sum-ii.py +++ /dev/null @@ -1,21 +0,0 @@ -class Solution: - def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]: - candidates.sort() - - res = [] - def backtrack(cur, pos, target): - if target == 0: - res.append(cur.copy()) - if target <= 0: - return - - prev = -1 - for i in range(pos, len(candidates)): - if candidates[i] == prev: - continue - cur.append(candidates[i]) - backtrack(cur, i + 1, target - candidates[i]) - cur.pop() - prev = candidates[i] - backtrack([], 0, target) - return res diff --git a/410-Split-Array-Largest-Sum.py b/410-Split-Array-Largest-Sum.py deleted file mode 100644 index bd2f910d1..000000000 --- a/410-Split-Array-Largest-Sum.py +++ /dev/null @@ -1,22 +0,0 @@ -class Solution: - def splitArray(self, nums: List[int], m: int) -> int: - def canSplit(largest): - subarray = 0 - curSum = 0 - for n in nums: - curSum += n - if curSum > largest: - subarray += 1 - curSum = n - return subarray + 1 <= m - - l, r = max(nums), sum(nums) - res = r - while l <= r: - mid = l + ((r - l) // 2) - if canSplit(mid): - res = mid - r = mid - 1 - else: - l = mid + 1 - return res diff --git a/416-Partition-Equal-Subset-Sum.py b/416-Partition-Equal-Subset-Sum.py deleted file mode 100644 index 4d51e90ec..000000000 --- a/416-Partition-Equal-Subset-Sum.py +++ /dev/null @@ -1,18 +0,0 @@ -class Solution: - def canPartition(self, nums: List[int]) -> bool: - if sum(nums) % 2: - return False - - dp = set() - dp.add(0) - target = sum(nums) // 2 - - for i in range(len(nums) - 1, -1, -1): - nextDP = set() - for t in dp: - if (t + nums[i]) == target: - return True - nextDP.add(t + nums[i]) - nextDP.add(t) - dp = nextDP - return True if target in dp else False diff --git a/417-Pacific-Atlantic-Waterflow.py b/417-Pacific-Atlantic-Waterflow.py deleted file mode 100644 index 0bb60579a..000000000 --- a/417-Pacific-Atlantic-Waterflow.py +++ /dev/null @@ -1,30 +0,0 @@ -class Solution: - def pacificAtlantic(self, heights: List[List[int]]) -> List[List[int]]: - ROWS, COLS = len(heights), len(heights[0]) - pac, atl = set(), set() - - def dfs(r, c, visit, prevHeight): - if ((r, c) in visit or - r < 0 or c < 0 or r == ROWS or c == COLS or - heights[r][c] < prevHeight): - return - visit.add((r, c)) - dfs(r + 1, c, visit, heights[r][c]) - dfs(r - 1, c, visit, heights[r][c]) - dfs(r, c + 1, visit, heights[r][c]) - dfs(r, c - 1, visit, heights[r][c]) - - for c in range(COLS): - dfs(0, c, pac, heights[0][c]) - dfs(ROWS - 1, c, atl, heights[ROWS - 1][c]) - - for r in range(ROWS): - dfs(r, 0, pac, heights[r][0]) - dfs(r, COLS - 1, atl, heights[r][COLS - 1]) - - res = [] - for r in range(ROWS): - for c in range(COLS): - if (r, c) in pac and (r, c) in atl: - res.append([r, c]) - return res diff --git a/42-Trapping-Rain-Water.py b/42-Trapping-Rain-Water.py deleted file mode 100644 index d39a48e5b..000000000 --- a/42-Trapping-Rain-Water.py +++ /dev/null @@ -1,17 +0,0 @@ -class Solution: - def trap(self, height: List[int]) -> int: - if not height: return 0 - - l, r = 0, len(height) - 1 - leftMax, rightMax = height[l], height[r] - res = 0 - while l < r: - if leftMax < rightMax: - l += 1 - leftMax = max(leftMax, height[l]) - res += leftMax - height[l] - else: - r -= 1 - rightMax = max(rightMax, height[r]) - res += rightMax - height[r] - return res diff --git a/424-Longest-Repeating-Character-Replacement.py b/424-Longest-Repeating-Character-Replacement.py deleted file mode 100644 index 2fdc379d5..000000000 --- a/424-Longest-Repeating-Character-Replacement.py +++ /dev/null @@ -1,17 +0,0 @@ -class Solution: - def characterReplacement(self, s: str, k: int) -> int: - count = {} - res = 0 - - l = 0 - maxf = 0 - for r in range(len(s)): - count[s[r]] = 1 + count.get(s[r], 0) - maxf = max(maxf, count[s[r]]) - - if (r - l + 1) - maxf > k: - count[s[l]] -= 1 - l += 1 - - res = max(res, r - l + 1) - return res diff --git a/43-Multiply-Strings.py b/43-Multiply-Strings.py deleted file mode 100644 index 1e4aaa56b..000000000 --- a/43-Multiply-Strings.py +++ /dev/null @@ -1,19 +0,0 @@ -class Solution: - def multiply(self, num1: str, num2: str) -> str: - if "0" in [num1, num2]: - return "0" - - res = [0] * (len(num1) + len(num2)) - num1, num2 = num1[::-1], num2[::-1] - for i1 in range(len(num1)): - for i2 in range(len(num2)): - digit = int(num1[i1]) * int(num2[i2]) - res[i1 + i2] += digit - res[i1 + i2 + 1] += res[i1 + i2] // 10 - res[i1 + i2] = res[i1 + i2] % 10 - - res, beg = res[::-1], 0 - while beg < len(res) and res[beg] == 0: - beg += 1 - res = map(str, res[beg:]) - return "".join(res) diff --git a/435-Non-Overlapping-Intervals.py b/435-Non-Overlapping-Intervals.py deleted file mode 100644 index f9d1b71e8..000000000 --- a/435-Non-Overlapping-Intervals.py +++ /dev/null @@ -1,12 +0,0 @@ -class Solution: - def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int: - intervals.sort() - res = 0 - prevEnd = intervals[0][1] - for start, end in intervals[1:]: - if start >= prevEnd: - prevEnd = end - else: - res += 1 - prevEnd = min(end, prevEnd) - return res diff --git a/448-Find-all-Numbers-Disappeared-in-an-Array.py b/448-Find-all-Numbers-Disappeared-in-an-Array.py deleted file mode 100644 index 45db6bf9e..000000000 --- a/448-Find-all-Numbers-Disappeared-in-an-Array.py +++ /dev/null @@ -1,11 +0,0 @@ -class Solution: - def findDisappearedNumbers(self, nums: List[int]) -> List[int]: - for n in nums: - i = abs(n) - 1 - nums[i] = -1 * abs(nums[i]) - - res = [] - for i, n in enumerate(nums): - if n > 0: - res.append(i + 1) - return res diff --git a/45-Jump-Game-II.py b/45-Jump-Game-II.py deleted file mode 100644 index 28a4d095f..000000000 --- a/45-Jump-Game-II.py +++ /dev/null @@ -1,12 +0,0 @@ -class Solution: - def jump(self, nums: List[int]) -> int: - l, r = 0, 0 - res = 0 - while r < (len(nums) - 1): - maxJump = 0 - for i in range(l, r + 1): - maxJump = max(maxJump, i + nums[i]) - l = r + 1 - r = maxJump - res += 1 - return res diff --git a/46-Permutations.py b/46-Permutations.py deleted file mode 100644 index 1e263a71a..000000000 --- a/46-Permutations.py +++ /dev/null @@ -1,17 +0,0 @@ -class Solution: - def permute(self, nums: List[int]) -> List[List[int]]: - res = [] - - # base case - if len(nums) == 1: - return [nums[:]] # nums[:] is a deep copy - - for i in range(len(nums)): - n = nums.pop(0) - perms = self.permute(nums) - - for perm in perms: - perm.append(n) - res.extend(perms) - nums.append(n) - return res diff --git a/463-Island-Perimeter.py b/463-Island-Perimeter.py deleted file mode 100644 index b60b55e49..000000000 --- a/463-Island-Perimeter.py +++ /dev/null @@ -1,22 +0,0 @@ -class Solution: - def islandPerimeter(self, grid: List[List[int]]) -> int: - visit = set() - - def dfs(i, j): - if i >= len(grid) or j >= len(grid[0]) or \ - i < 0 or j < 0 or grid[i][j] == 0: - return 1 - if (i, j) in visit: - return 0 - - visit.add((i, j)) - perim = dfs(i, j + 1) - perim += dfs(i + 1, j) - perim += dfs(i, j - 1) - perim += dfs(i - 1, j) - return perim - - for i in range(len(grid)): - for j in range(len(grid[0])): - if grid[i][j]: - return dfs(i, j) diff --git a/473-Matchsticks-to-Square.py b/473-Matchsticks-to-Square.py deleted file mode 100644 index fef8256f9..000000000 --- a/473-Matchsticks-to-Square.py +++ /dev/null @@ -1,21 +0,0 @@ -class Solution: - def makesquare(self, matchsticks: List[int]) -> bool: - length = sum(matchsticks) // 4 - sides = [0] * 4 - - if sum(matchsticks) / 4 != length: - return False - matchsticks.sort(reverse=True) - def backtrack(i): - if i == len(matchsticks): - return True - - for j in range(4): - if sides[j] + matchsticks[i] <= length: - sides[j] += matchsticks[i] - if backtrack(i + 1): - return True - sides[j] -= matchsticks[i] - return False - - return backtrack(0) diff --git a/48-Rotate-Image.py b/48-Rotate-Image.py deleted file mode 100644 index 5300ee201..000000000 --- a/48-Rotate-Image.py +++ /dev/null @@ -1,26 +0,0 @@ -class Solution: - def rotate(self, matrix: List[List[int]]) -> None: - """ - Do not return anything, modify matrix in-place instead. - """ - l, r = 0, len(matrix) - 1 - while l < r: - for i in range(r - l): - top, bottom = l, r - - # save the topleft - topLeft = matrix[top][l + i] - - # move bottom left into top left - matrix[top][l + i] = matrix[bottom - i][l] - - # move bottom right into bottom left - matrix[bottom - i][l] = matrix[bottom][r - i] - - # move top right into bottom right - matrix[bottom][r - i] = matrix[top + i][r] - - # move top left into top right - matrix[top + i][r] = topLeft - r -= 1 - l += 1 diff --git a/49-Group-Anagrams.py b/49-Group-Anagrams.py deleted file mode 100644 index a52b995a2..000000000 --- a/49-Group-Anagrams.py +++ /dev/null @@ -1,10 +0,0 @@ -class Solution: - def groupAnagrams(self, strs: List[str]) -> List[List[str]]: - ans = collections.defaultdict(list) - - for s in strs: - count = [0] * 26 - for c in s: - count[ord(c) - ord('a')] += 1 - ans[tuple(count)].append(s) - return ans.values() diff --git a/494-Target-Sum.py b/494-Target-Sum.py deleted file mode 100644 index 6b05c6574..000000000 --- a/494-Target-Sum.py +++ /dev/null @@ -1,14 +0,0 @@ -class Solution: - def findTargetSumWays(self, nums: List[int], target: int) -> int: - dp = {} # (index, total) -> # of ways - - def backtrack(i, total): - if i == len(nums): - return 1 if total == target else 0 - if (i, total) in dp: - return dp[(i, total)] - - dp[(i, total)] = (backtrack(i + 1, total + nums[i]) + - backtrack(i + 1, total - nums[i])) - return dp[(i, total)] - return backtrack(0, 0) diff --git a/5-Longest-Palindromic-Substring.py b/5-Longest-Palindromic-Substring.py deleted file mode 100644 index 587d3d03e..000000000 --- a/5-Longest-Palindromic-Substring.py +++ /dev/null @@ -1,25 +0,0 @@ -class Solution: - def longestPalindrome(self, s: str) -> str: - res = "" - resLen = 0 - - for i in range(len(s)): - # odd length - l, r = i, i - while l >= 0 and r < len(s) and s[l] == s[r]: - if (r - l + 1) > resLen: - res = s[l:r+1] - resLen = r - l + 1 - l -= 1 - r += 1 - - # even length - l, r = i, i + 1 - while l >= 0 and r < len(s) and s[l] == s[r]: - if (r - l + 1) > resLen: - res = s[l:r+1] - resLen = r - l + 1 - l -= 1 - r += 1 - - return res diff --git a/50-Pow(x, n) b/50-Pow(x, n) deleted file mode 100644 index 857443118..000000000 --- a/50-Pow(x, n) +++ /dev/null @@ -1,11 +0,0 @@ -class Solution: - def myPow(self, x: float, n: int) -> float: - def helper(x, n): - if x == 0: return 0 - if n == 0: return 1 - - res = helper(x * x, n // 2) - return x * res if n % 2 else res - - res = helper(x, abs(n)) - return res if n >= 0 else 1 / res diff --git a/51-N-Queens.py b/51-N-Queens.py deleted file mode 100644 index 9ebcfa661..000000000 --- a/51-N-Queens.py +++ /dev/null @@ -1,31 +0,0 @@ -class Solution: - def solveNQueens(self, n: int) -> List[List[str]]: - col = set() - posDiag = set() # (r + c) - negDiag = set() # (r - c) - - res = [] - board = [["."] * n for i in range(n)] - def backtrack(r): - if r == n: - copy = ["".join(row) for row in board] - res.append(copy) - return - - for c in range(n): - if c in col or (r + c) in posDiag or (r - c) in negDiag: - continue - - col.add(c) - posDiag.add(r + c) - negDiag.add(r - c) - board[r][c] = "Q" - - backtrack(r + 1) - - col.remove(c) - posDiag.remove(r + c) - negDiag.remove(r - c) - board[r][c] = "." - backtrack(0) - return res diff --git a/518-coin-change-2.py b/518-coin-change-2.py deleted file mode 100644 index 06d2b5409..000000000 --- a/518-coin-change-2.py +++ /dev/null @@ -1,51 +0,0 @@ -class Solution: - def change(self, amount: int, coins: List[int]) -> int: - # MEMOIZATION - # Time: O(n*m) - # Memory: O(n*m) - cache = {} - - def dfs(i, a): - if a == amount: - return 1 - if a > amount: - return 0 - if i == len(coins): - return 0 - if (i, a) in cache: - return cache[(i, a)] - - cache[(i, a)] = dfs(i, a + coins[i]) + dfs(i + 1, a) - return cache[(i, a)] - - return dfs(0, 0) - - - # DYNAMIC PROGRAMMING - # Time: O(n*m) - # Memory: O(n*m) - dp = [[0] * (len(coins) + 1) for i in range(amount + 1)] - dp[0] = [1] * (len(coins) + 1) - for a in range(1, amount + 1): - for i in range(len(coins) - 1, -1, -1): - dp[a][i] = dp[a][i + 1] - if a - coins[i] >= 0: - dp[a][i] += dp[a - coins[i]][i] - return dp[amount][0] - - - # DYNAMIC PROGRAMMING - # Time: O(n*m) - # Memory: O(n) where n = amount - dp = [0] * (amount + 1) - dp[0] = 1 - for i in range(len(coins) - 1, -1, -1): - nextDP = [0] * (amount + 1) - nextDP[0] = 1 - - for a in range(1, amount + 1): - nextDP[a] = dp[a] - if a - coins[i] >= 0: - nextDP[a] += nextDP[a - coins[i]] - dp = nextDP - return dp[amount] diff --git a/53-Maximum-Subarray.py b/53-Maximum-Subarray.py deleted file mode 100644 index 32f6defeb..000000000 --- a/53-Maximum-Subarray.py +++ /dev/null @@ -1,11 +0,0 @@ -class Solution: - def maxSubArray(self, nums: List[int]) -> int: - res = nums[0] - - total = 0 - for n in nums: - total += n - res = max(res, total) - if total < 0: - total = 0 - return res diff --git a/54-Spiral-Matrix.py b/54-Spiral-Matrix.py deleted file mode 100644 index f0226be82..000000000 --- a/54-Spiral-Matrix.py +++ /dev/null @@ -1,27 +0,0 @@ -class Solution: - def spiralOrder(self, matrix: List[List[int]]) -> List[int]: - res = [] - left, right = 0, len(matrix[0]) - top, bottom = 0, len(matrix) - - while left < right and top < bottom: - # get every i in the top row - for i in range(left, right): - res.append(matrix[top][i]) - top += 1 - # get every i in the right col - for i in range(top, bottom): - res.append(matrix[i][right - 1]) - right -= 1 - if not (left < right and top < bottom): - break - # get every i in the bottom row - for i in range(right - 1, left - 1, -1): - res.append(matrix[bottom - 1][i]) - bottom -= 1 - # get every i in the left col - for i in range(bottom - 1, top - 1, -1): - res.append(matrix[i][left]) - left += 1 - - return res diff --git a/543-Diameter-of-Binary-Tree.py b/543-Diameter-of-Binary-Tree.py deleted file mode 100644 index 8598087b3..000000000 --- a/543-Diameter-of-Binary-Tree.py +++ /dev/null @@ -1,21 +0,0 @@ -# Definition for a binary tree node. -# class TreeNode: -# def __init__(self, val=0, left=None, right=None): -# self.val = val -# self.left = left -# self.right = right -class Solution: - def diameterOfBinaryTree(self, root: TreeNode) -> int: - res = [0] - - def dfs(root): - if not root: - return -1 - left = dfs(root.left) - right = dfs(root.right) - res[0] = max(res[0], 2 + left + right) - - return 1 + max(left, right) - - dfs(root) - return res[0] diff --git a/55-Jump-Game.py b/55-Jump-Game.py deleted file mode 100644 index 67f4569e8..000000000 --- a/55-Jump-Game.py +++ /dev/null @@ -1,8 +0,0 @@ -class Solution: - def canJump(self, nums: List[int]) -> bool: - goal = len(nums) -1 - - for i in range(len(nums) - 2, -1, -1): - if i + nums[i] >= goal: - goal = i - return True if goal == 0 else False diff --git a/56-Merge-Intervals.py b/56-Merge-Intervals.py deleted file mode 100644 index 5b95bfe8b..000000000 --- a/56-Merge-Intervals.py +++ /dev/null @@ -1,14 +0,0 @@ -class Solution: - def merge(self, intervals: List[List[int]]) -> List[List[int]]: - intervals.sort(key = lambda pair : pair[0]) - output = [intervals[0]] - - for start, end in intervals: - lastEnd = output[-1][1] - - if start <= lastEnd: - # merge - output[-1][1] = max(lastEnd, end) - else: - output.append([start, end]) - return output diff --git a/567-Permutation-in-String.py b/567-Permutation-in-String.py deleted file mode 100644 index 45093bb68..000000000 --- a/567-Permutation-in-String.py +++ /dev/null @@ -1,32 +0,0 @@ -class Solution: - def checkInclusion(self, s1: str, s2: str) -> bool: - if len(s1) > len(s2): return False - - s1Count, s2Count = [0] * 26, [0] * 26 - for i in range(len(s1)): - s1Count[ord(s1[i]) - ord('a')] += 1 - s2Count[ord(s2[i]) - ord('a')] += 1 - - matches = 0 - for i in range(26): - matches += (1 if s1Count[i] == s2Count[i] else 0) - - l = 0 - for r in range(len(s1), len(s2)): - if matches == 26: return True - - index = ord(s2[r]) - ord('a') - s2Count[index] += 1 - if s1Count[index] == s2Count[index]: - matches += 1 - elif s1Count[index] + 1 == s2Count[index]: - matches -= 1 - - index = ord(s2[l]) - ord('a') - s2Count[index] -= 1 - if s1Count[index] == s2Count[index]: - matches += 1 - elif s1Count[index] - 1 == s2Count[index]: - matches -= 1 - l += 1 - return matches == 26 diff --git a/57-Insert-Interval.py b/57-Insert-Interval.py deleted file mode 100644 index e454bc19f..000000000 --- a/57-Insert-Interval.py +++ /dev/null @@ -1,14 +0,0 @@ -class Solution: - def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]: - res = [] - - for i in range(len(intervals)): - if newInterval[1] < intervals[i][0]: - res.append(newInterval) - return res + intervals[i:] - elif newInterval[0] > intervals[i][1]: - res.append(intervals[i]) - else: - newInterval = [min(newInterval[0], intervals[i][0]), max(newInterval[1], intervals[i][1])] - res.append(newInterval) - return res diff --git a/572-Subtree-of-Another-Tree.py b/572-Subtree-of-Another-Tree.py deleted file mode 100644 index 2fcdadca8..000000000 --- a/572-Subtree-of-Another-Tree.py +++ /dev/null @@ -1,23 +0,0 @@ -# Definition for a binary tree node. -# class TreeNode: -# def __init__(self, val=0, left=None, right=None): -# self.val = val -# self.left = left -# self.right = right -class Solution: - def isSubtree(self, s: TreeNode, t: TreeNode) -> bool: - if not t: return True - if not s: return False - - if self.sameTree(s, t): - return True - return (self.isSubtree(s.left, t) or - self.isSubtree(s.right, t)) - - def sameTree(self, s, t): - if not s and not t: - return True - if s and t and s.val == t.val: - return (self.sameTree(s.left, t.left) and - self.sameTree(s.right, t.right)) - return False diff --git a/617-Merge-Two-Binary-Trees.py b/617-Merge-Two-Binary-Trees.py deleted file mode 100644 index 1c42af8c8..000000000 --- a/617-Merge-Two-Binary-Trees.py +++ /dev/null @@ -1,18 +0,0 @@ -# Definition for a binary tree node. -# class TreeNode: -# def __init__(self, val=0, left=None, right=None): -# self.val = val -# self.left = left -# self.right = right -class Solution: - def mergeTrees(self, t1: TreeNode, t2: TreeNode) -> TreeNode: - if not t1 and not t2: - return None - - v1 = t1.val if t1 else 0 - v2 = t2.val if t2 else 0 - root = TreeNode(v1 + v2) - - root.left = self.mergeTrees(t1.left if t1 else None, t2.left if t2 else None) - root.right = self.mergeTrees(t1.right if t1 else None, t2.right if t2 else None) - return root diff --git a/62-Unique-Paths.py b/62-Unique-Paths.py deleted file mode 100644 index 399b27748..000000000 --- a/62-Unique-Paths.py +++ /dev/null @@ -1,12 +0,0 @@ -class Solution: - def uniquePaths(self, m: int, n: int) -> int: - row = [1] * n - - for i in range(m - 1): - newRow = [1] * n - for j in range(n - 2, -1, -1): - newRow[j] = newRow[j + 1] + row[j] - row = newRow - return row[0] - - # O(n * m) O(n) diff --git a/621-Task-Scheduler.py b/621-Task-Scheduler.py deleted file mode 100644 index 2dd9a41a8..000000000 --- a/621-Task-Scheduler.py +++ /dev/null @@ -1,20 +0,0 @@ -class Solution: - def leastInterval(self, tasks: List[str], n: int) -> int: - count = Counter(tasks) - maxHeap = [-cnt for cnt in count.values()] - heapq.heapify(maxHeap) - - time = 0 - q = deque() # pairs of [-cnt, idleTime] - while maxHeap or q: - time += 1 - - if not maxHeap: - time = q[0][1] - else: - cnt = 1 + heapq.heappop(maxHeap) - if cnt: - q.append([cnt, time + n]) - if q and q[0][1] == time: - heapq.heappush(maxHeap, q.popleft()[0]) - return time diff --git a/647-Palindromic-Substrings.py b/647-Palindromic-Substrings.py deleted file mode 100644 index b622cbfef..000000000 --- a/647-Palindromic-Substrings.py +++ /dev/null @@ -1,16 +0,0 @@ -class Solution: - def countSubstrings(self, s: str) -> int: - res = 0 - - for i in range(len(s)): - res += self.countPali(s, i, i) - res += self.countPali(s, i, i + 1) - return res - - def countPali(self, s, l, r): - res = 0 - while l >= 0 and r < len(s) and s[l] == s[r]: - res += 1 - l -= 1 - r += 1 - return res diff --git a/658-Find-K-Closest-Elements.py b/658-Find-K-Closest-Elements.py deleted file mode 100644 index a80f8c5d4..000000000 --- a/658-Find-K-Closest-Elements.py +++ /dev/null @@ -1,42 +0,0 @@ -# Log(n) + k -# More code but also more intuitive -class Solution: - def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]: - l, r = 0, len(arr) - 1 - - # Find index of x or the closest val to x - val, idx = arr[0], 0 - while l <= r: - m = (l + r) // 2 - curDiff, resDiff = abs(arr[m] - x), abs(val - x) - if (curDiff < resDiff or - (curDiff == resDiff and arr[m] < val)): - val, idx = arr[m], m - - if arr[m] < x: l = m + 1 - elif arr[m] > x: r = m - 1 - else: break - - l = r = idx - for i in range(k - 1): - if l == 0: - r += 1 - elif r == len(arr) - 1 or x - arr[l-1] <= arr[r+1] - x: - l -= 1 - else: - r += 1 - return arr[l:r+1] - -# Log(n-k) + k -# Elegant but very difficult to understand -class Solution: - def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]: - l, r = 0, len(arr) - k - - while l < r: - m = (l + r) // 2 - if x - arr[m] > arr[m + k] - x: - l = m + 1 - else: - r = m - return arr[l:l+k] diff --git a/66-Plus-One.py b/66-Plus-One.py deleted file mode 100644 index a13ba3d6a..000000000 --- a/66-Plus-One.py +++ /dev/null @@ -1,18 +0,0 @@ -class Solution: - def plusOne(self, digits: List[int]) -> List[int]: - one = 1 - i = 0 - digits = digits[::-1] - - while one: - if i < len(digits): - if digits[i] == 9: - digits[i] = 0 - else: - digits[i] += 1 - one = 0 - else: - digits.append(one) - one = 0 - i += 1 - return digits[::-1] diff --git a/673-Number-of-Longest-Increasing-Subsequence.py b/673-Number-of-Longest-Increasing-Subsequence.py deleted file mode 100644 index 7e84ab9d4..000000000 --- a/673-Number-of-Longest-Increasing-Subsequence.py +++ /dev/null @@ -1,52 +0,0 @@ -class Solution: - def findNumberOfLIS(self, nums: List[int]) -> int: - # 1. O(n^2) Recursive solution with Caching - - dp = {} # key = index, value = [length of LIS, count] - lenLIS, res = 0, 0 # length of LIS, count of LIS - - def dfs(i): - if i in dp: return dp[i] - - maxLen, maxCnt = 1, 1 # length and count of LIS - for j in range(i + 1, len(nums)): - if nums[j] > nums[i]: # make sure increasing order - length, count = dfs(j) - if length + 1 > maxLen: - maxLen, maxCnt = length + 1, count - elif length + 1 == maxLen: - maxCnt += count - nonlocal lenLIS, res - if maxLen > lenLIS: - lenLIS, res = maxLen, maxCnt - elif maxLen == lenLIS: - res += maxCnt - dp[i] = [maxLen, maxCnt] - return dp[i] - - for i in range(len(nums)): dfs(i) - return res - - # 2. O(n^2) Dynamic Programming - - dp = {} # key = index, value = [length of LIS, count] - lenLIS, res = 0, 0 # length of LIS, count of LIS - - # i = start of subseq - for i in range(len(nums) - 1, -1, -1): - maxLen, maxCnt = 1, 1 # len, cnt of LIS start from i - - for j in range(i + 1, len(nums)): - if nums[j] > nums[i]: - length, count = dp[j] # len, cnt of LIS start from j - if length + 1 > maxLen: - maxLen, maxCnt = length + 1, count - elif length + 1 == maxLen: - maxCnt += count - if maxLen > lenLIS: - lenLIS, res = maxLen, maxCnt - elif maxLen == lenLIS: - res += maxCnt - dp[i] = [maxLen, maxCnt] - - return res diff --git a/678-Valid-Parenthesis-String.py b/678-Valid-Parenthesis-String.py deleted file mode 100644 index 16d154fa5..000000000 --- a/678-Valid-Parenthesis-String.py +++ /dev/null @@ -1,39 +0,0 @@ -# Dynamic Programming: O(n^2) -class Solution: - def checkValidString(self, s: str) -> bool: - dp = { (len(s), 0) : True } # key=(i, leftCount) -> isValid - def dfs(i, left): - if i == len(s) or left < 0: - return left == 0 - if (i, left) in dp: - return dp[(i, left)] - - if s[i] == "(": - dp[(i, left)] = dfs(i + 1, left + 1) - elif s[i] == ")": - dp[(i, left)] = dfs(i + 1, left - 1) - else: - dp[(i, left)] = (dfs(i + 1, left + 1) or - dfs(i + 1, left - 1) or - dfs(i + 1, left)) - return dp[(i, left)] - - return dfs(0, 0) - -# Greedy: O(n) -class Solution: - def checkValidString(self, s: str) -> bool: - leftMin, leftMax = 0, 0 - - for c in s: - if c == "(": - leftMin, leftMax = leftMin + 1, leftMax + 1 - elif c == ")": - leftMin, leftMax = leftMin - 1, leftMax - 1 - else: - leftMin, leftMax = leftMin - 1, leftMax + 1 - if leftMax < 0: - return False - if leftMin < 0: # required because -> s = ( * ) ( - leftMin = 0 - return leftMin == 0 diff --git a/684-Redundant-Connection.py b/684-Redundant-Connection.py deleted file mode 100644 index 0b1c7ed17..000000000 --- a/684-Redundant-Connection.py +++ /dev/null @@ -1,28 +0,0 @@ -class Solution: - def findRedundantConnection(self, edges: List[List[int]]) -> List[int]: - par = [i for i in range(len(edges) + 1)] - rank = [1] * (len(edges) + 1) - - def find(n): - p = par[n] - while p != par[p]: - par[p] = par[par[p]] - p = par[p] - return p - - # return False if already unioned - def union(n1, n2): - p1, p2 = find(n1), find(n2) - - if p1 == p2: return False - if rank[p1] > rank[p2]: - par[p2] = p1 - rank[p1] += rank[p2] - else: - par[p1] = p2 - rank[p2] += rank[p1] - return True - - for n1, n2 in edges: - if not union(n1, n2): - return [n1, n2] diff --git a/695-Max-Area-of-Island.py b/695-Max-Area-of-Island.py deleted file mode 100644 index 8ca570a6a..000000000 --- a/695-Max-Area-of-Island.py +++ /dev/null @@ -1,19 +0,0 @@ -class Solution: - def maxAreaOfIsland(self, grid: List[List[int]]) -> int: - ROWS, COLS = len(grid), len(grid[0]) - visit = set() - - def dfs(r, c): - if (r < 0 or r == ROWS or c < 0 or c == COLS or - grid[r][c] == 0 or (r, c) in visit): - return 0 - visit.add((r, c)) - return (1 + dfs(r + 1, c) + - dfs(r - 1, c) + - dfs(r, c + 1) + - dfs(r, c - 1)) - area = 0 - for r in range(ROWS): - for c in range(COLS): - area = max(area, dfs(r, c)) - return area diff --git a/7-Reverse-Integer.py b/7-Reverse-Integer.py deleted file mode 100644 index 9d858a78f..000000000 --- a/7-Reverse-Integer.py +++ /dev/null @@ -1,22 +0,0 @@ -class Solution: - def reverse(self, x: int) -> int: - # Integer.MAX_VALUE = 2147483647 (end with 7) - # Integer.MIN_VALUE = -2147483648 (end with -8 ) - - MIN = -2147483648 # -2^31, - MAX = 2147483647 # 2^31 - 1 - - res = 0 - while x: - digit = int(math.fmod(x, 10)) # (python dumb) -1 % 10 = 9 - x = int(x / 10) # (python dumb) -1 // 10 = -1 - - if (res > MAX // 10 or - (res == MAX // 10 and digit >= MAX % 10)): - return 0 - if (res < MIN // 10 or - (res == MIN // 10 and digit <= MIN % 10)): - return 0 - res = (res * 10) + digit - - return res diff --git a/70-Climbing-Stairs.py b/70-Climbing-Stairs.py deleted file mode 100644 index 2bb252058..000000000 --- a/70-Climbing-Stairs.py +++ /dev/null @@ -1,10 +0,0 @@ -class Solution: - def climbStairs(self, n: int) -> int: - if n <= 3: return n - n1, n2 = 2, 3 - - for i in range(4, n + 1): - temp = n1 + n2 - n1 = n2 - n2 = temp - return n2 diff --git a/703-Kth-Largest-Element-in-a-Stream.py b/703-Kth-Largest-Element-in-a-Stream.py deleted file mode 100644 index fc4ee26b4..000000000 --- a/703-Kth-Largest-Element-in-a-Stream.py +++ /dev/null @@ -1,14 +0,0 @@ -class KthLargest: - - def __init__(self, k: int, nums: List[int]): - # minHeap w/ K largest integers - self.minHeap, self.k = nums, k - heapq.heapify(self.minHeap) - while len(self.minHeap) > k: - heapq.heappop(self.minHeap) - - def add(self, val: int) -> int: - heapq.heappush(self.minHeap, val) - if len(self.minHeap) > self.k: - heapq.heappop(self.minHeap) - return self.minHeap[0] diff --git a/704-Binary-Search.py b/704-Binary-Search.py deleted file mode 100644 index 88eb0325c..000000000 --- a/704-Binary-Search.py +++ /dev/null @@ -1,13 +0,0 @@ -class Solution: - def search(self, nums: List[int], target: int) -> int: - l, r = 0, len(nums) - 1 - - while l <= r: - m = l + ((r - l) // 2) # (l + r) // 2 can lead to overflow - if nums[m] > target: - r = m - 1 - elif nums[m] < target: - l = m + 1 - else: - return m - return -1 diff --git a/72-Edit-Distance.py b/72-Edit-Distance.py deleted file mode 100644 index dc84dc7a9..000000000 --- a/72-Edit-Distance.py +++ /dev/null @@ -1,16 +0,0 @@ -class Solution: - def minDistance(self, word1: str, word2: str) -> int: - dp = [[float("inf")] * (len(word2) + 1) for i in range(len(word1) + 1)] - - for j in range(len(word2) + 1): - dp[len(word1)][j] = len(word2) - j - for i in range(len(word1) + 1): - dp[i][len(word2)] = len(word1) - i - - for i in range(len(word1) - 1, -1, -1): - for j in range(len(word2) - 1, -1, -1): - if word1[i] == word2[j]: - dp[i][j] = dp[i + 1][j + 1] - else: - dp[i][j] = 1 + min(dp[i + 1][j], dp[i][j + 1], dp[i + 1][j + 1]) - return dp[0][0] diff --git a/724-Find-Pivot-Index.py b/724-Find-Pivot-Index.py deleted file mode 100644 index 1a094985f..000000000 --- a/724-Find-Pivot-Index.py +++ /dev/null @@ -1,11 +0,0 @@ -class Solution: - def pivotIndex(self, nums: List[int]) -> int: - total = sum(nums) # O(n) - - leftSum = 0 - for i in range(len(nums)): - rightSum = total - nums[i] - leftSum - if leftSum == rightSum: - return i - leftSum += nums[i] - return -1 diff --git a/73-Set-Matrix-Zeroes.py b/73-Set-Matrix-Zeroes.py deleted file mode 100644 index 4c960ec64..000000000 --- a/73-Set-Matrix-Zeroes.py +++ /dev/null @@ -1,28 +0,0 @@ -class Solution: - def setZeroes(self, matrix: List[List[int]]) -> None: - # O(1) - ROWS, COLS = len(matrix), len(matrix[0]) - rowZero = False - - # determine which rows/cols need to be zero - for r in range(ROWS): - for c in range(COLS): - if matrix[r][c] == 0: - matrix[0][c] = 0 - if r > 0: - matrix[r][0] = 0 - else: - rowZero = True - - for r in range(1, ROWS): - for c in range(1, COLS): - if matrix[0][c] == 0 or matrix[r][0] == 0: - matrix[r][c] = 0 - - if matrix[0][0] == 0: - for r in range(ROWS): - matrix[r][0] = 0 - - if rowZero: - for c in range(COLS): - matrix[0][c] = 0 diff --git a/739-Daily-Temperatures.py b/739-Daily-Temperatures.py deleted file mode 100644 index 7939d4779..000000000 --- a/739-Daily-Temperatures.py +++ /dev/null @@ -1,11 +0,0 @@ -class Solution: - def dailyTemperatures(self, temperatures: List[int]) -> List[int]: - res = [0] * len(temperatures) - stack = [] # pair: [temp, index] - - for i, t in enumerate(temperatures): - while stack and t > stack[-1][0]: - stackT, stackInd = stack.pop() - res[stackInd] = (i - stackInd) - stack.append([t, i]) - return res diff --git a/74-Search-a-2D-Matrix.py b/74-Search-a-2D-Matrix.py deleted file mode 100644 index 84c7ff2c3..000000000 --- a/74-Search-a-2D-Matrix.py +++ /dev/null @@ -1,27 +0,0 @@ -class Solution: - def searchMatrix(self, matrix: List[List[int]], target: int) -> bool: - ROWS, COLS = len(matrix), len(matrix[0]) - - top, bot = 0, ROWS - 1 - while top <= bot: - row = (top + bot) // 2 - if target > matrix[row][-1]: - top = row + 1 - elif target < matrix[row][0]: - bot = row - 1 - else: - break - - if not (top <= bot): - return False - row = (top + bot) // 2 - l, r = 0, COLS - 1 - while l <= r: - m = (l + r) // 2 - if target > matrix[row][m]: - l = m + 1 - elif target < matrix[row][m]: - r = m - 1 - else: - return True - return False diff --git a/743-Network-Delay-Time.py b/743-Network-Delay-Time.py deleted file mode 100644 index beb6613ec..000000000 --- a/743-Network-Delay-Time.py +++ /dev/null @@ -1,22 +0,0 @@ -class Solution: - def networkDelayTime(self, times: List[List[int]], n: int, k: int) -> int: - edges = collections.defaultdict(list) - for u, v, w in times: - edges[u].append((v, w)) - - minHeap = [(0, k)] - visit = set() - t = 0 - while minHeap: - w1, n1 = heapq.heappop(minHeap) - if n1 in visit: - continue - visit.add(n1) - t = max(t, w1) - - for n2, w2 in edges[n1]: - if n2 not in visit: - heapq.heappush(minHeap, (w1 + w2, n2)) - return t if len(visit) == n else -1 - - # O(E * logV) diff --git a/746-Min-Cost-Climbing-Stairs.py b/746-Min-Cost-Climbing-Stairs.py deleted file mode 100644 index e7ceadc8e..000000000 --- a/746-Min-Cost-Climbing-Stairs.py +++ /dev/null @@ -1,8 +0,0 @@ -class Solution: - def minCostClimbingStairs(self, cost: List[int]) -> int: - cost.append(0) - - for i in range(len(cost) - 3, -1, -1): - cost[i] += min(cost[i + 1], cost[i + 2]) - - return min(cost[0], cost[1]) diff --git a/752-Open-the-Lock.py b/752-Open-the-Lock.py deleted file mode 100644 index 179ea8504..000000000 --- a/752-Open-the-Lock.py +++ /dev/null @@ -1,26 +0,0 @@ -class Solution: - def openLock(self, deadends: List[str], target: str) -> int: - if "0000" in deadends: - return -1 - - def children(wheel): - res = [] - for i in range(4): - digit = str((int(wheel[i]) + 1) % 10) - res.append(wheel[:i] + digit + wheel[i+1:]) - digit = str((int(wheel[i]) + 10 - 1) % 10) - res.append(wheel[:i] + digit + wheel[i+1:]) - return res - - q = deque() - visit = set(deadends) - q.append(["0000", 0]) # [wheel, turns] - while q: - wheel, turns = q.popleft() - if wheel == target: - return turns - for child in children(wheel): - if child not in visit: - visit.add(child) - q.append([child, turns + 1]) - return -1 diff --git a/76-Minimum-Window-Substring.py b/76-Minimum-Window-Substring.py deleted file mode 100644 index 9f601bb57..000000000 --- a/76-Minimum-Window-Substring.py +++ /dev/null @@ -1,30 +0,0 @@ -class Solution: - def minWindow(self, s: str, t: str) -> str: - if t == "": return "" - - countT, window = {}, {} - for c in t: - countT[c] = 1 + countT.get(c, 0) - - have, need = 0, len(countT) - res, resLen = [-1, -1], float("infinity") - l = 0 - for r in range(len(s)): - c = s[r] - window[c] = 1 + window.get(c, 0) - - if c in countT and window[c] == countT[c]: - have += 1 - - while have == need: - # update our result - if (r - l + 1) < resLen: - res = [l, r] - resLen = (r - l + 1) - # pop from the left of our window - window[s[l]] -= 1 - if s[l] in countT and window[s[l]] < countT[s[l]]: - have -= 1 - l += 1 - l, r = res - return s[l:r+1] if resLen != float("infinity") else "" diff --git a/763-Partition-Labels.py b/763-Partition-Labels.py deleted file mode 100644 index 0594c1d39..000000000 --- a/763-Partition-Labels.py +++ /dev/null @@ -1,21 +0,0 @@ -class Solution: - def partitionLabels(self, S: str) -> List[int]: - count = {} - res = [] - i, length = 0, len(S) - for j in range(length): - c = S[j] - count[c] = j - - curLen = 0 - goal = 0 - while i < length: - c = S[i] - goal = max(goal, count[c]) - curLen += 1 - - if goal == i: - res.append(curLen) - curLen = 0 - i += 1 - return res diff --git a/767-Reorganize-String.py b/767-Reorganize-String.py deleted file mode 100644 index 85cd89593..000000000 --- a/767-Reorganize-String.py +++ /dev/null @@ -1,22 +0,0 @@ -class Solution: - def reorganizeString(self, s: str) -> str: - count = Counter(s) # Hashmap, count each char - maxHeap = [[-cnt, char] for char, cnt in count.items()] - heapq.heapify(maxHeap) # O(n) - - prev = None - res = "" - while maxHeap or prev: - if prev and not maxHeap: - return "" - # most frequent, except prev - cnt, char = heapq.heappop(maxHeap) - res += char - cnt += 1 - - if prev: - heapq.heappush(maxHeap, prev) - prev = None - if cnt != 0: - prev = [cnt, char] - return res diff --git a/778-Swim-in-Rising-Water.py b/778-Swim-in-Rising-Water.py deleted file mode 100644 index d00030567..000000000 --- a/778-Swim-in-Rising-Water.py +++ /dev/null @@ -1,20 +0,0 @@ -class Solution: - def swimInWater(self, grid: List[List[int]]) -> int: - N = len(grid) - visit = set() - minH = [[grid[0][0], 0, 0]] # (time/max-height, r, c) - directions = [[0, 1], [0, -1], [1, 0], [-1 ,0]] - - visit.add((0, 0)) - while minH: - t, r, c = heapq.heappop(minH) - if r == N - 1 and c == N - 1: - return t - for dr, dc in directions: - neiR, neiC = r + dr, c + dc - if (neiR < 0 or neiC < 0 or - neiR == N or neiC == N or - (neiR, neiC) in visit): - continue - visit.add((neiR, neiC)) - heapq.heappush(minH, [max(t, grid[neiR][neiC]), neiR, neiC]) diff --git a/78-Subsets.py b/78-Subsets.py deleted file mode 100644 index 206dc54b2..000000000 --- a/78-Subsets.py +++ /dev/null @@ -1,18 +0,0 @@ -class Solution: - def subsets(self, nums: List[int]) -> List[List[int]]: - res = [] - - subset = [] - def dfs(i): - if i >= len(nums): - res.append(subset.copy()) - return - # decision to include nums[i] - subset.append(nums[i]) - dfs(i + 1) - # decision NOT to include nums[i] - subset.pop() - dfs(i + 1) - - dfs(0) - return res diff --git a/787-Cheapest-Flights-within-K-stops.py b/787-Cheapest-Flights-within-K-stops.py deleted file mode 100644 index 767e1b98e..000000000 --- a/787-Cheapest-Flights-within-K-stops.py +++ /dev/null @@ -1,15 +0,0 @@ -class Solution: - def findCheapestPrice(self, n: int, flights: List[List[int]], src: int, dst: int, k: int) -> int: - prices = [float("inf")] * n - prices[src] = 0 - - for i in range(k + 1): - tmpPrices = prices.copy() - - for s, d, p in flights: # s=source, d=dest, p=price - if prices[s] == float("inf"): - continue - if prices[s] + p < tmpPrices[d]: - tmpPrices[d] = prices[s] + p - prices = tmpPrices - return -1 if prices[dst] == float("inf") else prices[dst] diff --git a/79-Word-Search.py b/79-Word-Search.py deleted file mode 100644 index 5c6b99a42..000000000 --- a/79-Word-Search.py +++ /dev/null @@ -1,25 +0,0 @@ -class Solution: - def exist(self, board: List[List[str]], word: str) -> bool: - ROWS, COLS = len(board), len(board[0]) - path = set() - - def dfs(r, c, i): - if i == len(word): - return True - if (min(r, c) < 0 or r >= ROWS or c >= COLS or - word[i] != board[r][c] or (r, c) in path): - return False - path.add((r, c)) - res = (dfs(r + 1, c, i + 1) or - dfs(r - 1, c, i + 1) or - dfs(r, c + 1, i + 1) or - dfs(r, c - 1, i + 1)) - path.remove((r, c)) - return res - - for r in range(ROWS): - for c in range(COLS): - if dfs(r, c, 0): return True - return False - - # O(n * m * 4^n) diff --git a/84-Largest-Rectangle-in-Histogram.py b/84-Largest-Rectangle-in-Histogram.py deleted file mode 100644 index 8f84af583..000000000 --- a/84-Largest-Rectangle-in-Histogram.py +++ /dev/null @@ -1,16 +0,0 @@ -class Solution: - def largestRectangleArea(self, heights: List[int]) -> int: - maxArea = 0 - stack = [] # pair: (index, height) - - for i, h in enumerate(heights): - start = i - while stack and stack[-1][1] > h: - index, height = stack.pop() - maxArea = max(maxArea, height * (i - index)) - start = index - stack.append((start, h)) - - for i, h in stack: - maxArea = max(maxArea, h * (len(heights) - i)) - return maxArea diff --git a/846-Hand-of-Straights.py b/846-Hand-of-Straights.py deleted file mode 100644 index 9f31c956b..000000000 --- a/846-Hand-of-Straights.py +++ /dev/null @@ -1,22 +0,0 @@ -class Solution: - def isNStraightHand(self, hand: List[int], groupSize: int) -> bool: - if len(hand) % groupSize: - return False - - count = {} - for n in hand: - count[n] = 1 + count.get(n, 0) - - minH = list(count.keys()) - heapq.heapify(minH) - while minH: - first = minH[0] - for i in range(first, first + groupSize): - if i not in count: - return False - count[i] -= 1 - if count[i] == 0: - if i != minH[0]: - return False - heapq.heappop(minH) - return True diff --git a/853-Car-Fleet.py b/853-Car-Fleet.py deleted file mode 100644 index 6290852fa..000000000 --- a/853-Car-Fleet.py +++ /dev/null @@ -1,10 +0,0 @@ -class Solution: - def carFleet(self, target: int, position: List[int], speed: List[int]) -> int: - pair = [[p, s] for p, s in zip(position, speed)] - - stack = [] - for p, s in sorted(pair)[::-1]: # Reverse Sorted Order - stack.append((target - p) / s) - if len(stack) >= 2 and stack[-1] <= stack[-2]: - stack.pop() - return len(stack) diff --git a/875-Koko-Eating-Bananas.py b/875-Koko-Eating-Bananas.py deleted file mode 100644 index cfb701141..000000000 --- a/875-Koko-Eating-Bananas.py +++ /dev/null @@ -1,17 +0,0 @@ -class Solution: - def minEatingSpeed(self, piles: List[int], H: int) -> int: - l, r = 1, max(piles) - k = 0 - - while l <= r: - m = (l + r) // 2 - - totalTime = 0 - for p in piles: - totalTime += ((p-1)//m) + 1 - if totalTime <= H: - k = m - r = m - 1 - else: - l = m + 1 - return k diff --git a/90-Subsets-II.py b/90-Subsets-II.py deleted file mode 100644 index b951812fb..000000000 --- a/90-Subsets-II.py +++ /dev/null @@ -1,20 +0,0 @@ -class Solution: - def subsetsWithDup(self, nums: List[int]) -> List[List[int]]: - res = [] - nums.sort() - - def backtrack(i, subset): - if i == len(nums): - res.append(subset[::]) - return - - # All subsets that include nums[i] - subset.append(nums[i]) - backtrack(i + 1, subset) - subset.pop() - # All subsets that don't include nums[i] - while i + 1 < len(nums) and nums[i] == nums[i+1]: - i += 1 - backtrack(i + 1, subset) - backtrack(0, []) - return res diff --git a/909-Snakes-and-Ladders.py b/909-Snakes-and-Ladders.py deleted file mode 100644 index b2338d7f0..000000000 --- a/909-Snakes-and-Ladders.py +++ /dev/null @@ -1,27 +0,0 @@ -class Solution: - def snakesAndLadders(self, board: List[List[int]]) -> int: - length = len(board) - board.reverse() - def intToPos(square): - r = (square - 1) // length - c = (square - 1) % length - if r % 2: - c = length - 1 - c - return [r, c] - - q = deque() - q.append([1, 0]) # [square, moves] - visit = set() - while q: - square, moves = q.popleft() - for i in range(1, 7): - nextSquare = square + i - r, c = intToPos(nextSquare) - if board[r][c] != -1: - nextSquare = board[r][c] - if nextSquare == length * length: - return moves + 1 - if nextSquare not in visit: - visit.add(nextSquare) - q.append([nextSquare, moves + 1]) - return -1 diff --git a/91-Decode-ways.py b/91-Decode-ways.py deleted file mode 100644 index 9b7fbd28e..000000000 --- a/91-Decode-ways.py +++ /dev/null @@ -1,30 +0,0 @@ -class Solution: - def numDecodings(self, s: str) -> int: - # Memoization - dp = { len(s) : 1 } - def dfs(i): - if i in dp: - return dp[i] - if s[i] == "0": - return 0 - - res = dfs(i + 1) - if (i + 1 < len(s) and (s[i] == "1" or - s[i] == "2" and s[i + 1] in "0123456")): - res += dfs(i + 2) - dp[i] = res - return res - return dfs(0) - - # Dynamic Programming - dp = { len(s) : 1 } - for i in range(len(s) - 1, -1, -1): - if s[i] == "0": - dp[i] = 0 - else: - dp[i] = dp[i + 1] - - if (i + 1 < len(s) and (s[i] == "1" or - s[i] == "2" and s[i + 1] in "0123456")): - dp[i] += dp[i + 2] - return dp[0] diff --git a/92-Reverse-Linked-List-II.py b/92-Reverse-Linked-List-II.py deleted file mode 100644 index 6a9676fb1..000000000 --- a/92-Reverse-Linked-List-II.py +++ /dev/null @@ -1,21 +0,0 @@ -class Solution: - def reverseBetween(self, head: Optional[ListNode], left: int, right: int) -> Optional[ListNode]: - dummy = ListNode(0, head) - - # 1) reach node at position "left" - leftPrev, cur = dummy, head - for i in range(left - 1): - leftPrev, cur = cur, cur.next - - # Now cur="left", leftPrev="node before left" - # 2) reverse from left to right - prev = None - for i in range(right - left + 1): - tmpNext = cur.next - cur.next = prev - prev, cur = cur, tmpNext - - # 3) Update pointers - leftPrev.next.next = cur # cur is node after "right" - leftPrev.next = prev # prev is "right" - return dummy.next diff --git a/94-Binary-Tree-Inorder-Traversal.py b/94-Binary-Tree-Inorder-Traversal.py deleted file mode 100644 index 85ab49cc6..000000000 --- a/94-Binary-Tree-Inorder-Traversal.py +++ /dev/null @@ -1,26 +0,0 @@ -class Solution: - def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]: - # Iterative - res, stack = [], [] - cur = root - - while cur or stack: - while cur: - stack.append(cur) - cur = cur.left - cur = stack.pop() - res.append(cur.val) - cur = cur.right - return res - - # Recursive - res = [] - - def helper(root): - if not root: - return - helper(root.left) - res.append(root.val) - helper(root.right) - helper(root) - return res diff --git a/97-Interleaving-Strings.py b/97-Interleaving-Strings.py deleted file mode 100644 index 3a0a149d3..000000000 --- a/97-Interleaving-Strings.py +++ /dev/null @@ -1,15 +0,0 @@ -class Solution: - def isInterleave(self, s1: str, s2: str, s3: str) -> bool: - if len(s1) + len(s2) != len(s3): - return False - - dp = [ [False] * (len(s2) + 1) for i in range(len(s1) + 1)] - dp[len(s1)][len(s2)] = True - - for i in range(len(s1), -1, -1): - for j in range(len(s2), -1, -1): - if i < len(s1) and s1[i] == s3[i + j] and dp[i + 1][j]: - dp[i][j] = True - if j < len(s2) and s2[j] == s3[i + j] and dp[i][j + 1]: - dp[i][j] = True - return dp[0][0] diff --git a/973-K-Closest-Points-to-Origin.py b/973-K-Closest-Points-to-Origin.py deleted file mode 100644 index 1318017cf..000000000 --- a/973-K-Closest-Points-to-Origin.py +++ /dev/null @@ -1,13 +0,0 @@ -class Solution: - def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]: - pts = [] - for x, y in points: - dist = (abs(x - 0) ** 2) + (abs(y - 0) ** 2) - pts.append([dist, x, y]) - - res = [] - heapq.heapify(pts) - for i in range(k): - dist, x, y = heapq.heappop(pts) - res.append([x, y]) - return res diff --git a/98-Validate-Binary-Search-Tree.py b/98-Validate-Binary-Search-Tree.py deleted file mode 100644 index bdf5228be..000000000 --- a/98-Validate-Binary-Search-Tree.py +++ /dev/null @@ -1,19 +0,0 @@ -# Definition for a binary tree node. -# class TreeNode: -# def __init__(self, val=0, left=None, right=None): -# self.val = val -# self.left = left -# self.right = right -class Solution: - def isValidBST(self, root: TreeNode) -> bool: - - def valid(node, left, right): - if not node: - return True - if not (node.val < right and node.val > left): - return False - - return (valid(node.left, left, node.val) and - valid(node.right, node.val, right)) - - return valid(root, float("-inf"), float("inf")) diff --git a/981-Time-Based-Key-Value-Store.py b/981-Time-Based-Key-Value-Store.py deleted file mode 100644 index d762fd774..000000000 --- a/981-Time-Based-Key-Value-Store.py +++ /dev/null @@ -1,24 +0,0 @@ -class TimeMap: - - def __init__(self): - """ - Initialize your data structure here. - """ - self.keyStore = {} # key : list of [val, timestamp] - - def set(self, key: str, value: str, timestamp: int) -> None: - if key not in self.keyStore: - self.keyStore[key] = [] - self.keyStore[key].append([value, timestamp]) - - def get(self, key: str, timestamp: int) -> str: - res, values = "", self.keyStore.get(key, []) - l, r = 0, len(values) - 1 - while l <= r: - m = (l + r) // 2 - if values[m][1] <= timestamp: - res = values[m][0] - l = m + 1 - else: - r = m - 1 - return res diff --git a/994-Rotting-Oranges.py b/994-Rotting-Oranges.py deleted file mode 100644 index 0f9afce04..000000000 --- a/994-Rotting-Oranges.py +++ /dev/null @@ -1,31 +0,0 @@ -class Solution: - def orangesRotting(self, grid: List[List[int]]) -> int: - q = collections.deque() - fresh = 0 - time = 0 - - for r in range(len(grid)): - for c in range(len(grid[0])): - if grid[r][c] == 1: - fresh += 1 - if grid[r][c] == 2: - q.append((r, c)) - - directions = [[0, 1], [0, -1], [1, 0], [-1, 0]] - while fresh > 0 and q: - length = len(q) - for i in range(length): - r, c = q.popleft() - - for dr, dc in directions: - row, col = r + dr, c + dc - # if in bounds and nonrotten, make rotten - # and add to q - if row in range(len(grid)) and \ - col in range(len(grid[0])) and \ - grid[row][col] == 1: - grid[row][col] = 2 - q.append((row, col)) - fresh -= 1 - time += 1 - return time if fresh == 0 else -1 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..83d524dd0 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,52 @@ +# Contributing +**Please read the [guidelines below](#contributing-guidelines) before opening a PR** + +Solutions from these languages will be linked from [NeetCode.io](https://neetcode.io): + +* Python +* C++ +* Java +* Javascript + +Solutions are also welcome for any other *supported* language on leetcode.com! + +To contribute, please fork this repo and open a PR adding a [missing solution](./README.md#missing-solutions) from the supported languages. + +If you would like to have collaborator permissions on the repo to merge your own PRs or review others' PRs please let me know. + +## Contributing Guidelines + +- **Match the casing of files and directories** + - For files, it's **`/-name-of-problem.`** (e.g. `java/0001-two-sum.java`) + - Make sure the `problem-number` is formatted as four digits adding leading zeroes if necessary + - You can find the `name-of-problem` in the leetcode URL, e.g https://leetcode.com/problems/ _**two-sum**_ / +- **Give your PR a succinct and accurate title** (e.g. _"Create: 0001-two-sum.py"_) +- Prefer **one solution/change per PR** (not a hard and fast rule, but will typically make the review cycle shorter) +- **Follow the** [PR template](./.github/pull_request_template.md) and add additional information when needed +- **Make sure your code passes** submission on [leetcode.com](https://leetcode.com) for the problem it solves +- **Write clean code** (Your code should use semantically meaningful variable/method names, consistent style, etc) and easy to understand. for example, a single letter is probably not a semantically meaningful name +- **Ensure the problem is not already solved** in the language you're contributing in + +## FAQs + +**Q:** What should my solution include? + +**A:** You can keep your solution exactly the same as the one you submit to leetcode, you don't need to write tests or your own implementation of leetcode's built-ins. +## + +**Q:** What if there are multiple ways to solve the problem? + +**A:** Multiple solutions to the same problem are accepted (as long as they differ in approach or time/memory complexity), although the first solution should always follow the same algorithm shown on [the channel](https://www.youtube.com/c/neetcode). Please make sure distinct solutions are grouped together in the same file, with appropriately differentiating names (e.g. `isValidBstIterative` and `isValidBstRecursive`) +## + +**Q:** What if my solution has a lower runtime but the same time/memory complexity? + +**A:** Leetcode's runtime measurement can be severely inaccurate varying based on server load, time of day and many other factors. In general, readability and clarity of the code (in the context of interviews) are features more important than performance gains, however changes that transparently improve performance will be accepted. +## + +**Q:** What if the problem I want to add isn't in the Neetcode 150 list or Missing Solutions table? + +**A:** Questions outside of the Neetcode 150 list can be added but please prioritise adding the listed solutions first. +## + +Thanks for contributing 🚀 diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..144c6f237 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 neetcode-gh + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Neetcode-update.iml b/Neetcode-update.iml new file mode 100644 index 000000000..908ad4f52 --- /dev/null +++ b/Neetcode-update.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 82c045bf8..621435471 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,576 @@ -# Leetcode solutions for [NeetCode](https://www.youtube.com/c/neetcode) +# Leetcode solutions for 🚀 [NeetCode.io](https://neetcode.io) +> This repo hosts the solutions found on [NeetCode.io](https://neetcode.io) including the solutions shown on the [NeetCode YouTube channel](https://www.youtube.com/c/neetcode). The site will periodically be updated with new solutions from this repo! -### Contributing +
-I mostly do solutions in Python, but if you would like to contribute solutions for other common languages like Java, C++ and JavaScript, please feel free to make a pull request! :) +Solutions from these languages will be linked from [NeetCode.io](https://neetcode.io): +> Python, Java, JavaScript, C++, Go, Swift, C#, TypeScript, Rust, Kotlin, Ruby, C, Scala and Dart + +Solutions are also welcome for any other *supported* language on leetcode.com! + +## Contributing +**Please read the [contributing guidlines](./CONTRIBUTING.md) before opening a PR** + + +To contribute, please fork this repo and open a PR adding a [missing solution](#missing-solutions) from the supported languages. + +If you would like to have collaborator permissions on the repo to merge your own PRs or review others' PRs please let me know. + +## Credits + + + + + +## Missing Solutions + +### Arrays & Hashing + +Problem | articles | C | C++ | C# | Dart | GO | hints | Java | JS | Kotlin | Python | Ruby | Rust | Scala | Swift | TS +---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- +[0217 - Contains Duplicate](https://leetcode.com/problems/contains-duplicate/) |
|
[✔️](c%2F0217-contains-duplicate.c)
|
[✔️](cpp%2F0217-contains-duplicate.cpp)
|
[✔️](csharp%2F0217-contains-duplicate.cs)
|
[✔️](dart%2F0217-contains-duplicate.dart)
|
[✔️](go%2F0217-contains-duplicate.go)
|
|
[✔️](java%2F0217-contains-duplicate.java)
|
[✔️](javascript%2F0217-contains-duplicate.js)
|
[✔️](kotlin%2F0217-contains-duplicate.kt)
|
[✔️](python%2F0217-contains-duplicate.py)
|
[✔️](ruby%2F0217-contains-duplicate.rb)
|
[✔️](rust%2F0217-contains-duplicate.rs)
|
[✔️](scala%2F0217-contains-duplicate.scala)
|
[✔️](swift%2F0217-contains-duplicate.swift)
|
[✔️](typescript%2F0217-contains-duplicate.ts)
+[0242 - Valid Anagram](https://leetcode.com/problems/valid-anagram/) |
|
[✔️](c%2F0242-valid-anagram.c)
|
[✔️](cpp%2F0242-valid-anagram.cpp)
|
[✔️](csharp%2F0242-valid-anagram.cs)
|
[✔️](dart%2F0242-valid-anagram.dart)
|
[✔️](go%2F0242-valid-anagram.go)
|
|
[✔️](java%2F0242-valid-anagram.java)
|
[✔️](javascript%2F0242-valid-anagram.js)
|
[✔️](kotlin%2F0242-valid-anagram.kt)
|
[✔️](python%2F0242-valid-anagram.py)
|
[✔️](ruby%2F0242-valid-anagram.rb)
|
[✔️](rust%2F0242-valid-anagram.rs)
|
[✔️](scala%2F0242-valid-anagram.scala)
|
[✔️](swift%2F0242-valid-anagram.swift)
|
[✔️](typescript%2F0242-valid-anagram.ts)
+[1929 - Concatenation of Array](https://leetcode.com/problems/concatenation-of-array/) |
|
[✔️](c%2F1929-concatenation-of-array.c)
|
[✔️](cpp%2F1929-concatenation-of-array.cpp)
|
[✔️](csharp%2F1929-concatenation-of-array.cs)
|
[✔️](dart%2F1929-concatenation-of-array.dart)
|
[✔️](go%2F1929-concatenation-of-array.go)
|
|
[✔️](java%2F1929-concatenation-of-array.java)
|
[✔️](javascript%2F1929-concatenation-of-array.js)
|
[✔️](kotlin%2F1929-concatenation-of-array.kt)
|
[✔️](python%2F1929-concatenation-of-array.py)
|
[✔️](ruby%2F1929-concatenation-of-array.rb)
|
[✔️](rust%2F1929-concatenation-of-array.rs)
|
[✔️](scala%2F1929-concatenation-of-array.scala)
|
[✔️](swift%2F1929-concatenation-of-array.swift)
|
[✔️](typescript%2F1929-concatenation-of-array.ts)
+[1299 - Replace Elements With Greatest Element On Right Side](https://leetcode.com/problems/replace-elements-with-greatest-element-on-right-side/) |
|
[✔️](c%2F1299-Replace-Elements-With-Greatest-Element-On-Right-Side.c)
|
[✔️](cpp%2F1299-Replace-Elements-with-Greatest-Element-on-Right-Side.cpp)
|
[✔️](csharp%2F1299-Replace-Elements-With-Greatest-Element-On-Right-Side.cs)
|
[✔️](dart%2F1299-replace-elements-with-greatest-element-on-right-side.dart)
|
[✔️](go%2F1299-Replace-Elements-With-Greatest-Element-On-Right-Side.go)
|
|
[✔️](java%2F1299-Replace-Elements-With-Greatest-Element-On-Right-Side.java)
|
[✔️](javascript%2F1299-Replace-Elements-with-Greatest-Element-on-Right-Side.js)
|
[✔️](kotlin%2F1299-replace-elements-with-greatest-element-on-right-side.kt)
|
[✔️](python%2F1299-replace-elements-with-greatest-element-on-right-side.py)
|
[✔️](ruby%2F1299-replace-elements-with-greatest-element-on-right-side.rb)
|
[✔️](rust%2F1299-Replace-Elements-With-Greatest-Element-On-Right-Side.rs)
|
|
[✔️](swift%2F1299-replace-elements-with-greatest-element-on-right-side.swift)
|
[✔️](typescript%2F1299-Replace-Elements-With-Greatest-Element-On-Right-Side.ts)
+[0392 - Is Subsequence](https://leetcode.com/problems/is-subsequence/) |
|
[✔️](c%2F0392-is-subsequence.c)
|
[✔️](cpp%2F0392-is-subsequence.cpp)
|
[✔️](csharp%2F0392-is-subsequence.cs)
|
[✔️](dart%2F0392-is-subsequence.dart)
|
[✔️](go%2F0392-is-subsequence.go)
|
|
[✔️](java%2F0392-is-subsequence.java)
|
[✔️](javascript%2F0392-is-subsequence.js)
|
[✔️](kotlin%2F0392-is-subsequence.kt)
|
[✔️](python%2F0392-is-subsequence.py)
|
[✔️](ruby%2F0392-is-subsequence.rb)
|
[✔️](rust%2F0392-is-subsequence.rs)
|
|
[✔️](swift%2F0392-is-subsequence.swift)
|
[✔️](typescript%2F0392-is-subsequence.ts)
+[0058 - Length of Last Word](https://leetcode.com/problems/length-of-last-word/) |
|
[✔️](c%2F0058-length-of-last-word.c)
|
[✔️](cpp%2F0058-length-of-last-word.cpp)
|
[✔️](csharp%2F0058-length-of-last-word.cs)
|
[✔️](dart%2F0058-length-of-last-word.dart)
|
[✔️](go%2F0058-Length-of-Last-Word.go)
|
|
[✔️](java%2F0058-length-of-last-word.java)
|
[✔️](javascript%2F0058-length-of-last-word.js)
|
[✔️](kotlin%2F0058-length-of-last-word.kt)
|
[✔️](python%2F0058-length-of-last-word.py)
|
[✔️](ruby%2F0058-length-of-last-word.rb)
|
[✔️](rust%2F0058-length-of-last-word.rs)
|
[✔️](scala%2F0058-length-of-last-word.scala)
|
[✔️](swift%2F0058-Length-of-Last-Word.swift)
|
[✔️](typescript%2F0058-length-of-last-word.ts)
+[0001 - Two Sum](https://leetcode.com/problems/two-sum/) |
|
[✔️](c%2F0001-two-sum.c)
|
[✔️](cpp%2F0001-two-sum.cpp)
|
[✔️](csharp%2F0001-two-sum.cs)
|
[✔️](dart%2F0001-two-sum.dart)
|
[✔️](go%2F0001-two-sum.go)
|
|
[✔️](java%2F0001-two-sum.java)
|
[✔️](javascript%2F0001-two-sum.js)
|
[✔️](kotlin%2F0001-two-sum.kt)
|
[✔️](python%2F0001-two-sum.py)
|
[✔️](ruby%2F0001-two-sum.rb)
|
[✔️](rust%2F0001-two-sum.rs)
|
[✔️](scala%2F0001-two-sum.scala)
|
[✔️](swift%2F0001-two-sum.swift)
|
[✔️](typescript%2F0001-two-sum.ts)
+[0014 - Longest Common Prefix](https://leetcode.com/problems/longest-common-prefix/) |
|
[✔️](c%2F0014-Longest-Common-Prefix.c)
|
[✔️](cpp%2F0014-longest-common-prefix.cpp)
|
[✔️](csharp%2F0014-longest-common-prefix.cs)
|
[✔️](dart%2F0014-longest-common-prefix.dart)
|
[✔️](go%2F0014-longest-common-prefix.go)
|
|
[✔️](java%2F0014-longest-common-prefix.java)
|
[✔️](javascript%2F0014-longest-common-prefix.js)
|
[✔️](kotlin%2F0014-longest-common-prefix.kt)
|
[✔️](python%2F0014-longest-common-prefix.py)
|
|
[✔️](rust%2F0014-longest-common-prefix.rs)
|
|
[✔️](swift%2F0014-longest-common-prefix.swift)
|
[✔️](typescript%2F0014-longest-common-prefix.ts)
+[0049 - Group Anagrams](https://leetcode.com/problems/group-anagrams/) |
|
[✔️](c%2F0049-group-anagrams.c)
|
[✔️](cpp%2F0049-group-anagrams.cpp)
|
[✔️](csharp%2F0049-group-anagrams.cs)
|
[✔️](dart%2F0049-group-anagrams.dart)
|
[✔️](go%2F0049-group-anagrams.go)
|
|
[✔️](java%2F0049-group-anagrams.java)
|
[✔️](javascript%2F0049-group-anagrams.js)
|
[✔️](kotlin%2F0049-group-anagrams.kt)
|
[✔️](python%2F0049-group-anagrams.py)
|
[✔️](ruby%2F0049-group-anagrams.rb)
|
[✔️](rust%2F0049-group-anagrams.rs)
|
[✔️](scala%2F0049-group-anagrams.scala)
|
[✔️](swift%2F0049-group-anagrams.swift)
|
[✔️](typescript%2F0049-group-anagrams.ts)
+[0118 - Pascals Triangle](https://leetcode.com/problems/pascals-triangle/) |
|
[✔️](c%2F0118-pascals-triangle.c)
|
[✔️](cpp%2F0118-pascals-triangle.cpp)
|
[✔️](csharp%2F0118-pascals-triangle.cs)
|
[✔️](dart%2F0118-pascals-triangle.dart)
|
[✔️](go%2F0118-pascals-triangle.go)
|
|
[✔️](java%2F0118-pascals-triangle.java)
|
[✔️](javascript%2F0118-pascals-triangle.js)
|
[✔️](kotlin%2F0118-pascals-triangle.kt)
|
[✔️](python%2F0118-pascals-triangle.py)
|
|
[✔️](rust%2F0118-pascals-triangle.rs)
|
|
|
[✔️](typescript%2F0118-pascals-triangle.ts)
+[0027 - Remove Element](https://leetcode.com/problems/remove-element/) |
|
[✔️](c%2F0027-remove-element.c)
|
[✔️](cpp%2F0027-remove-element.cpp)
|
[✔️](csharp%2F0027-remove-element.cs)
|
[✔️](dart%2F0027-remove-element.dart)
|
[✔️](go%2F0027-remove-element.go)
|
|
[✔️](java%2F0027-remove-element.java)
|
[✔️](javascript%2F0027-remove-element.js)
|
[✔️](kotlin%2F0027-remove-element.kt)
|
[✔️](python%2F0027-remove-element.py)
|
|
[✔️](rust%2F0027-remove-element.rs)
|
|
[✔️](swift%2F0027-Remove-Element.swift)
|
[✔️](typescript%2F0027-remove-element.ts)
+[0929 - Unique Email Addresses](https://leetcode.com/problems/unique-email-addresses/) |
|
[✔️](c%2F0929-unique-email-addresses.c)
|
[✔️](cpp%2F0929-unique-email-addresses.cpp)
|
[✔️](csharp%2F0929-unique-email-addresses.cs)
|
[✔️](dart%2F0929-unique-email-addresses.dart)
|
[✔️](go%2F0929-unique-email-addresses.go)
|
|
[✔️](java%2F0929-unique-email-addresses.java)
|
[✔️](javascript%2F0929-unique-email-addresses.js)
|
[✔️](kotlin%2F0929-unique-email-addresses.kt)
|
[✔️](python%2F0929-unique-email-addresses.py)
|
|
[✔️](rust%2F0929-unique-email-addresses.rs)
|
|
[✔️](swift%2F0929-unique-email-addresses.swift)
|
[✔️](typescript%2F0929-unique-email-addresses.ts)
+[0205 - Isomorphic Strings](https://leetcode.com/problems/isomorphic-strings/) |
|
[✔️](c%2F0205-isomorphic-strings.c)
|
[✔️](cpp%2F0205-Isomorphic-Strings.cpp)
|
[✔️](csharp%2F0205-isomorphic-strings.cs)
|
|
[✔️](go%2F0205-isomorphic-strings.go)
|
|
[✔️](java%2F0205-isomorphic-strings.java)
|
[✔️](javascript%2F0205-isomorphic-strings.js)
|
[✔️](kotlin%2F0205-isomorphic-strings.kt)
|
[✔️](python%2F0205-isomorphic-strings.py)
|
|
[✔️](rust%2F0205-isomorphic-strings.rs)
|
|
[✔️](swift%2F0205-isomorphic-strings.swift)
|
[✔️](typescript%2F0205-isomorphic-strings.ts)
+[0605 - Can Place Flowers](https://leetcode.com/problems/can-place-flowers/) |
|
[✔️](c%2F0605-can-place-flowers.c)
|
[✔️](cpp%2F0605-can-place-flowers.cpp)
|
[✔️](csharp%2F0605-can-place-flowers.cs)
|
|
[✔️](go%2F0605-can-place-flowers.go)
|
|
[✔️](java%2F0605-can-place-flowers.java)
|
[✔️](javascript%2F0605-can-place-flowers.js)
|
[✔️](kotlin%2F0605-can-place-flowers.kt)
|
[✔️](python%2F0605-can-place-flowers.py)
|
|
[✔️](rust%2F0605-can-place-flowers.rs)
|
|
[✔️](swift%2F0605-can-place-flowers.swift)
|
[✔️](typescript%2F0605-can-place-flowers.ts)
+[0169 - Majority Element](https://leetcode.com/problems/majority-element/) |
|
[✔️](c%2F0169-majority-element.c)
|
[✔️](cpp%2F0169-majority-element.cpp)
|
[✔️](csharp%2F0169-majority-element.cs)
|
[✔️](dart%2F0169-majority-element.dart)
|
[✔️](go%2F0169-majority-element.go)
|
|
[✔️](java%2F0169-majority-element.java)
|
[✔️](javascript%2F0169-majority-element.js)
|
[✔️](kotlin%2F0169-majority-element.kt)
|
[✔️](python%2F0169-majority-element.py)
|
|
[✔️](rust%2F0169-majority-element.rs)
|
|
[✔️](swift%2F0169-majority-element.swift)
|
[✔️](typescript%2F0169-majority-element.ts)
+[0496 - Next Greater Element I](https://leetcode.com/problems/next-greater-element-i/) |
|
[✔️](c%2F0496-next-greater-element-i.c)
|
[✔️](cpp%2F0496-next-greater-element-i.cpp)
|
[✔️](csharp%2F0496-next-greater-element-i.cs)
|
|
[✔️](go%2F0496-next-greater-element-i.go)
|
|
[✔️](java%2F0496-next-greater-element-i.java)
|
[✔️](javascript%2F0496-next-greater-element-i.js)
|
[✔️](kotlin%2F0496-next-greater-element-i.kt)
|
[✔️](python%2F0496-next-greater-element-i.py)
|
|
[✔️](rust%2F0496-next-greater-element-I.rs)
|
|
|
[✔️](typescript%2F0496-next-greater-element-i.ts)
+[0724 - Find Pivot Index](https://leetcode.com/problems/find-pivot-index/) |
|
[✔️](c%2F0724-find-pivot-index.c)
|
[✔️](cpp%2F0724-find-pivot-index.cpp)
|
[✔️](csharp%2F0724-find-pivot-index.cs)
|
|
[✔️](go%2F0724-find-pivot-index.go)
|
|
[✔️](java%2F0724-find-pivot-index.java)
|
[✔️](javascript%2F0724-find-pivot-index.js)
|
[✔️](kotlin%2F0724-find-pivot-index.kt)
|
[✔️](python%2F0724-find-pivot-index.py)
|
|
[✔️](rust%2F0724-find-pivot-index.rs)
|
|
[✔️](swift%2F0724-find-pivot-index.swift)
|
[✔️](typescript%2F0724-find-pivot-index.ts)
+[0303 - Range Sum Query - Immutable](https://leetcode.com/problems/range-sum-query-immutable/) |
|
[✔️](c%2F0303-range-sum-query-immutable.c)
|
[✔️](cpp%2F0303-range-sum-query-immutable.cpp)
|
[✔️](csharp%2F0303-range-sum-query-immutable.cs)
|
|
[✔️](go%2F0303-range-sum-query.go)
|
|
[✔️](java%2F0303-range-sum-query-immutable.java)
|
[✔️](javascript%2F0303-range-sum-query-immutable.js)
|
[✔️](kotlin%2F0303-range-sum-query-immutable.kt)
|
[✔️](python%2F0303-range-sum-query-immutable.py)
|
|
[✔️](rust%2F0303-range-sum-query-immutable.rs)
|
|
[✔️](swift%2F0303-range-sum-query-immutable.swift)
|
[✔️](typescript%2F0303-range-sum-query-immutable.ts)
+[0448 - Find All Numbers Disappeared in An Array](https://leetcode.com/problems/find-all-numbers-disappeared-in-an-array/) |
|
[✔️](c%2F0448-Find-All-Numbers-Disappeared-in-an-Array.c)
|
[✔️](cpp%2F0448-find-all-numbers-disappeared-in-an-array.cpp)
|
[✔️](csharp%2F0448-find-all-numbers-disappeared-in-an-array.cs)
|
|
[✔️](go%2F0448-find-all-numbers-disappeared-in-an-array.go)
|
|
[✔️](java%2F0448-find-all-numbers-disappeared-in-an-array.java)
|
[✔️](javascript%2F0448-find-all-numbers-disappeared-in-an-array.js)
|
[✔️](kotlin%2F0448-find-all-numbers-disappeared-in-an-array.kt)
|
[✔️](python%2F0448-find-all-numbers-disappeared-in-an-array.py)
|
|
[✔️](rust%2F0448-find-all-numbers-disappeared-in-an-array.rs)
|
|
|
[✔️](typescript%2F0448-find-all-numbers-disappeared-in-an-array.ts)
+[1189 - Maximum Number of Balloons](https://leetcode.com/problems/maximum-number-of-balloons/) |
|
[✔️](c%2F1189-Maximum-Number-of-Balloons.c)
|
[✔️](cpp%2F1189-maximum-number-of-balloons.cpp)
|
[✔️](csharp%2F1189-maximum-number-of-balloons.cs)
|
|
[✔️](go%2F1189-maximum-number-of-balloons.go)
|
|
[✔️](java%2F1189-maximum-number-of-balloons.java)
|
[✔️](javascript%2F1189-maximum-number-of-balloons.js)
|
[✔️](kotlin%2F1189-maximum-number-of-balloons.kt)
|
[✔️](python%2F1189-maximum-number-of-balloons.py)
|
|
[✔️](rust%2F1189-maximum-number-of-balloons.rs)
|
|
|
[✔️](typescript%2F1189-maximum-number-of-balloons.ts)
+[0290 - Word Pattern](https://leetcode.com/problems/word-pattern/) |
|
[✔️](c%2F0290-Word-Pattern.c)
|
[✔️](cpp%2F0290-word-pattern.cpp)
|
[✔️](csharp%2F0290-word-pattern.cs)
|
|
[✔️](go%2F0290-word-pattern.go)
|
|
[✔️](java%2F0290-word-pattern.java)
|
[✔️](javascript%2F0290-word-pattern.js)
|
[✔️](kotlin%2F0290-word-pattern.kt)
|
[✔️](python%2F0290-word-pattern.py)
|
|
[✔️](rust%2F0290-word-pattern.rs)
|
|
|
[✔️](typescript%2F0290-word-pattern.ts)
+[0705 - Design HashSet](https://leetcode.com/problems/design-hashset/) |
|
[✔️](c%2F0705-design-hashset.c)
|
[✔️](cpp%2F0705-design-hashset.cpp)
|
[✔️](csharp%2F0705-design-hashset.cs)
|
|
|
|
[✔️](java%2F0705-design-hashset.java)
|
[✔️](javascript%2F0705-design-hashset.js)
|
[✔️](kotlin%2F0705-design-hashset.kt)
|
[✔️](python%2F0705-design-hashset.py)
|
|
[✔️](rust%2F0705-design-hashset.rs)
|
|
[✔️](swift%2F0705-design-hashset.swift)
|
[✔️](typescript%2F0705-design-hashset.ts)
+[0706 - Design HashMap](https://leetcode.com/problems/design-hashmap/) |
|
[✔️](c%2F0706-design-hashmap.c)
|
[✔️](cpp%2F0706-design-hashmap.cpp)
|
[✔️](csharp%2F0706-design-hashmap.cs)
|
|
[✔️](go%2F0706-design-hashmap.go)
|
|
[✔️](java%2F0706-design-hashmap.java)
|
[✔️](javascript%2F0706-design-hashmap.js)
|
[✔️](kotlin%2F0706-design-hashmap.kt)
|
[✔️](python%2F0706-design-hashmap.py)
|
|
[✔️](rust%2F0706-design-hashmap.rs)
|
|
[✔️](swift%2F0706-design-hashmap.swift)
|
+[0912 - Sort an Array](https://leetcode.com/problems/sort-an-array/) |
|
[✔️](c%2F0912-sort-an-array.c)
|
[✔️](cpp%2F0912-sort-an-array.cpp)
|
[✔️](csharp%2F0912-sort-an-array.cs)
|
|
[✔️](go%2F0912-sort-an-array.go)
|
|
[✔️](java%2F0912-sort-an-array.java)
|
[✔️](javascript%2F0912-sort-an-array.js)
|
[✔️](kotlin%2F0912-sort-an-array.kt)
|
[✔️](python%2F0912-sort-an-array.py)
|
|
[✔️](rust%2F0912-sort-an-array.rs)
|
|
[✔️](swift%2F0912-sort-an-array.swift)
|
+[0347 - Top K Frequent Elements](https://leetcode.com/problems/top-k-frequent-elements/) |
|
[✔️](c%2F0347-top-k-frequent-elements.c)
|
[✔️](cpp%2F0347-top-k-frequent-elements.cpp)
|
[✔️](csharp%2F0347-top-k-frequent-elements.cs)
|
[✔️](dart%2F0347-top-k-frequent-elements.dart)
|
[✔️](go%2F0347-top-k-frequent-elements.go)
|
|
[✔️](java%2F0347-top-k-frequent-elements.java)
|
[✔️](javascript%2F0347-top-k-frequent-elements.js)
|
[✔️](kotlin%2F0347-top-k-frequent-elements.kt)
|
[✔️](python%2F0347-top-k-frequent-elements.py)
|
[✔️](ruby%2F0347-top-k-frequent-elements.rb)
|
[✔️](rust%2F0347-top-k-frequent-elements.rs)
|
[✔️](scala%2F0347-top-k-frequent-elements.scala)
|
[✔️](swift%2F0347-top-k-frequent-elements.swift)
|
[✔️](typescript%2F0347-top-k-frequent-elements.ts)
+[0238 - Product of Array Except Self](https://leetcode.com/problems/product-of-array-except-self/) |
|
[✔️](c%2F0238-product-of-array-except-self.c)
|
[✔️](cpp%2F0238-product-of-array-except-self.cpp)
|
[✔️](csharp%2F0238-product-of-array-except-self.cs)
|
|
[✔️](go%2F0238-product-of-array-except-self.go)
|
|
[✔️](java%2F0238-product-of-array-except-self.java)
|
[✔️](javascript%2F0238-product-of-array-except-self.js)
|
[✔️](kotlin%2F0238-product-of-array-except-self.kt)
|
[✔️](python%2F0238-product-of-array-except-self.py)
|
[✔️](ruby%2F0238-product-of-array-except-self.rb)
|
[✔️](rust%2F0238-product-of-array-except-self.rs)
|
|
[✔️](swift%2F0238-product-of-array-except-self.swift)
|
[✔️](typescript%2F0238-product-of-array-except-self.ts)
+[0036 - Valid Sudoku](https://leetcode.com/problems/valid-sudoku/) |
|
[✔️](c%2F0036-valid-sudoku.c)
|
[✔️](cpp%2F0036-valid-sudoku.cpp)
|
[✔️](csharp%2F0036-valid-sudoku.cs)
|
[✔️](dart%2F0036-valid-sudoku.dart)
|
[✔️](go%2F0036-valid-sudoku.go)
|
|
[✔️](java%2F0036-valid-sudoku.java)
|
[✔️](javascript%2F0036-valid-sudoku.js)
|
[✔️](kotlin%2F0036-valid-sudoku.kt)
|
[✔️](python%2F0036-valid-sudoku.py)
|
[✔️](ruby%2F0036-valid-sudoku.rb)
|
[✔️](rust%2F0036-valid-sudoku.rs)
|
[✔️](scala%2F0036-valid-sudoku.scala)
|
|
[✔️](typescript%2F0036-valid-sudoku.ts)
+[0271 - Encode and Decode Strings](https://leetcode.com/problems/encode-and-decode-strings/) |
|
|
[✔️](cpp%2F0271-encode-and-decode-strings.cpp)
|
[✔️](csharp%2F0271-encode-and-decode-strings.cs)
|
|
[✔️](go%2F0271-encode-and-decode-strings.go)
|
|
[✔️](java%2F0271-encode-and-decode-strings.java)
|
[✔️](javascript%2F0271-encode-and-decode-strings.js)
|
[✔️](kotlin%2F0271-encode-and-decode-strings.kt)
|
[✔️](python%2F0271-encode-and-decode-strings.py)
|
[✔️](ruby%2F0271-encode-and-decode-strings.rb)
|
[✔️](rust%2F0271-encode-and-decode-strings.rs)
|
|
[✔️](swift%2F0271-encode-and-decode-strings.swift)
|
[✔️](typescript%2F0271-encode-and-decode-strings.ts)
+[0128 - Longest Consecutive Sequence](https://leetcode.com/problems/longest-consecutive-sequence/) |
|
[✔️](c%2F0128-longest-consecutive-sequence.c)
|
[✔️](cpp%2F0128-longest-consecutive-sequence.cpp)
|
[✔️](csharp%2F0128-longest-consecutive-sequence.cs)
|
|
[✔️](go%2F0128-longest-consecutive-sequence.go)
|
|
[✔️](java%2F0128-longest-consecutive-sequence.java)
|
[✔️](javascript%2F0128-longest-consecutive-sequence.js)
|
[✔️](kotlin%2F0128-longest-consecutive-sequence.kt)
|
[✔️](python%2F0128-longest-consecutive-sequence.py)
|
[✔️](ruby%2F0128-longest-consecutive-sequence.rb)
|
[✔️](rust%2F0128-longest-consecutive-sequence.rs)
|
|
[✔️](swift%2F0128-longest-consecutive-sequence.swift)
|
[✔️](typescript%2F0128-longest-consecutive-sequence.ts)
+[0075 - Sort Colors](https://leetcode.com/problems/sort-colors/) |
|
[✔️](c%2F0075-sort-colors.c)
|
[✔️](cpp%2F0075-Sort-colors.cpp)
|
[✔️](csharp%2F0075-sort-colors.cs)
|
|
[✔️](go%2F0075-sort-colors.go)
|
|
[✔️](java%2F0075-sort-colors.java)
|
[✔️](javascript%2F0075-sort-colors.js)
|
[✔️](kotlin%2F0075-sort-colors.kt)
|
[✔️](python%2F0075-sort-colors.py)
|
|
[✔️](rust%2F0075-sort-colors.rs)
|
|
[✔️](swift%2F0075-sort-colors.swift)
|
[✔️](typescript%2F0075-sort-colors.ts)
+[0535 - Encode and Decode TinyURL](https://leetcode.com/problems/encode-and-decode-tinyurl/) |
|
[✔️](c%2F0535-encode-and-decode-tinyurl.c)
|
[✔️](cpp%2F0535-encode-and-decode-tinyurl.cpp)
|
[✔️](csharp%2F0535-encode-and-decode-tinyurl.cs)
|
|
[✔️](go%2F0535-encode-and-decode-tinyurl.go)
|
|
[✔️](java%2F0535-encode-and-decode-tinyurl.java)
|
[✔️](javascript%2F0535-encode-and-decode-tinyurl.js)
|
[✔️](kotlin%2F0535-encode-and-decode-tinyurl.kt)
|
[✔️](python%2F0535-encode-and-decode-tinyurl.py)
|
|
[✔️](rust%2F0535-encode-and-decode-tinyURL.rs)
|
|
[✔️](swift%2F0535-Encode-and-Decode-TinyURL.Swift)
|
[✔️](typescript%2F0535-encode-and-decode-tinyurl.ts)
+[0554 - Brick Wall](https://leetcode.com/problems/brick-wall/) |
|
[✔️](c%2F0554-Brick-Wall.c)
|
[✔️](cpp%2F0554-brick-wall.cpp)
|
[✔️](csharp%2F0554-brick-wall.cs)
|
|
[✔️](go%2F0554-brick-wall.go)
|
|
[✔️](java%2F0554-brick-wall.java)
|
[✔️](javascript%2F0554-brick-wall.js)
|
[✔️](kotlin%2F0554-brick-wall.kt)
|
[✔️](python%2F0554-brick-wall.py)
|
|
[✔️](rust%2F0554-brick-wall.rs)
|
|
|
[✔️](typescript%2F0554-brick-wall.ts)
+[0122 - Best Time to Buy And Sell Stock II](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/) |
|
[✔️](c%2F0122-best-time-to-buy-and-sell-stock-ii.c)
|
[✔️](cpp%2F0122-best-time-to-buy-and-sell-stock-ii.cpp)
|
|
|
[✔️](go%2F0122-best-time-to-buy-and-sell-stock-ii.go)
|
|
[✔️](java%2F0122-best-time-to-buy-and-sell-stock-II.java)
|
[✔️](javascript%2F0122-best-time-to-buy-and-sell-stock-ii.js)
|
[✔️](kotlin%2F0122-best-time-to-buy-and-sell-stock-ii.kt)
|
[✔️](python%2F0122-best-time-to-buy-and-sell-stock-ii.py)
|
|
[✔️](rust%2F0122-best-time-to-buy-and-sell-stock-ii.rs)
|
|
|
+[0560 - Subarray Sum Equals K](https://leetcode.com/problems/subarray-sum-equals-k/) |
|
[✔️](c%2F0560-subarray-sum-equals-k.c)
|
[✔️](cpp%2F0560-subarray-sum-equals-k.cpp)
|
[✔️](csharp%2F0560-subarray-sum-equals-k.cs)
|
|
[✔️](go%2F0560-subarray-sum-equals-k.go)
|
|
[✔️](java%2F0560-subarray-sum-equals-k.java)
|
[✔️](javascript%2F0560-subarray-sum-equals-k.js)
|
[✔️](kotlin%2F0560-subarray-sum-equals-k.kt)
|
[✔️](python%2F0560-subarray-sum-equals-k.py)
|
|
[✔️](rust%2F0560-subarray-sum-equals-k.rs)
|
|
[✔️](swift%2F0560-subarray-sum-equals-k.swift)
|
[✔️](typescript%2F0560-subarray-sum-equals-k.ts)
+[1930 - Unique Length 3 Palindromic Subsequences](https://leetcode.com/problems/unique-length-3-palindromic-subsequences/) |
|
[✔️](c%2F1930-unique-length-3-palindromic-subsequences.c)
|
[✔️](cpp%2F1930-unique-length-3-palindromic-subsequences.cpp)
|
|
|
|
|
[✔️](java%2F1930-unique-length-3-palindromic-subsequences.java)
|
[✔️](javascript%2F1930-unique-length-3-palindromic-subsequences.js)
|
[✔️](kotlin%2F1930-unique-length-3-palindromic-subsequences.kt)
|
[✔️](python%2F1930-unique-length-3-palindromic-subsequences.py)
|
|
[✔️](rust%2F1930-unique-length-3-palindromic-subsequences.rs)
|
|
|
[✔️](typescript%2F1930-unique-length-3-palindromic-subsequences.ts)
+[1963 - Minimum Number of Swaps to Make The String Balanced](https://leetcode.com/problems/minimum-number-of-swaps-to-make-the-string-balanced/) |
|
[✔️](c%2F1963-minimum-number-of-swaps-to-make-the-string-balanced.c)
|
[✔️](cpp%2F1963-minimum-number-of-swaps-to-make-the-string-balanced.cpp)
|
[✔️](csharp%2F1963-minimum-number-of-swaps-to-make-the-string-balanced.cs)
|
|
[✔️](go%2F1963-minimum-number-of-swaps-to-make-the-string-balanced.go)
|
|
[✔️](java%2F1963-minimum-number-of-swaps-to-make-the-string-balanced.java)
|
[✔️](javascript%2F1963-minimum-number-of-swaps-to-make-the-string-balanced.js)
|
[✔️](kotlin%2F1963-minimum-number-of-swaps-to-make-the-string-balanced.kt)
|
[✔️](python%2F1963-minimum-number-of-swaps-to-make-the-string-balanced.py)
|
|
[✔️](rust%2F1963-minimum-number-of-swaps-to-make-the-string-balanced.rs)
|
|
|
+[2001 - Number of Pairs of Interchangeable Rectangles](https://leetcode.com/problems/number-of-pairs-of-interchangeable-rectangles/) |
|
[✔️](c%2F2001-number-of-pairs-of-interchangeable-rectangles.c)
|
[✔️](cpp%2F2001-number-of-pairs-of-interchangeable-rectangles.cpp)
|
|
|
|
|
[✔️](java%2F2001-number-of-pairs-of-interchangeable-rectangles.java)
|
[✔️](javascript%2F2001-number-of-pairs-of-interchangeable-rectangles.js)
|
[✔️](kotlin%2F2001-number-of-pairs-of-interchangeable-rectangles.kt)
|
[✔️](python%2F2001-number-of-pairs-of-interchangeable-rectangles.py)
|
|
[✔️](rust%2F2001-number-of-pairs-of-interchangeable-rectangles.rs)
|
|
|
+[2002 - Maximum Product of The Length of Two Palindromic Subsequences](https://leetcode.com/problems/maximum-product-of-the-length-of-two-palindromic-subsequences/) |
|
[✔️](c%2F2002-maximum-product-of-the-length-of-two-palindromic-subsequences.c)
|
[✔️](cpp%2F2002-maximum-product-of-the-length-of-two-palindromic-subsequences.cpp)
|
[✔️](csharp%2F2002-maximum-product-of-the-length-of-two-palindromic-subsequences.cs)
|
|
[✔️](go%2F2002-maximum-product-of-the-length-of-two-palindromic-subsequences.go)
|
|
[✔️](java%2F2002-maximum-product-of-the-length-of-two-palindromic-subsequences.java)
|
[✔️](javascript%2F2002-maximum-product-of-the-length-of-two-palindromic-subsequences.js)
|
[✔️](kotlin%2F2002-maximum-product-of-the-length-of-two-palindromic-subsequences.kt)
|
[✔️](python%2F2002-maximum-product-of-the-length-of-two-palindromic-subsequences.py)
|
|
[✔️](rust%2F2002-maximum-product-of-the-length-of-two-palindromic-subsequences.rs)
|
|
|
[✔️](typescript%2F2002-maximum-product-of-the-length-of-two-palindromic-subsequences.ts)
+[2017 - Grid Game](https://leetcode.com/problems/grid-game/) |
|
|
[✔️](cpp%2F2017-grid-game.cpp)
|
|
|
|
|
[✔️](java%2F2017-grid-game.java)
|
[✔️](javascript%2F2017-grid-game.js)
|
[✔️](kotlin%2F2017-grid-game.kt)
|
[✔️](python%2F2017-grid-game.py)
|
|
[✔️](rust%2F2017-grid-game.rs)
|
|
|
+[0438 - Find All Anagrams in a String](https://leetcode.com/problems/find-all-anagrams-in-a-string/) |
|
[✔️](c%2F0438-find-all-anagrams-in-a-string.c)
|
[✔️](cpp%2F0438-find-all-anagrams-in-a-string.cpp)
|
[✔️](csharp%2F0438-find-all-anagrams-in-a-string.cs)
|
|
[✔️](go%2F0438-find-all-anagrams-in-a-string.go)
|
|
[✔️](java%2F0438-find-all-anagrams-in-a-string.java)
|
[✔️](javascript%2F0438-find-all-anagrams-in-a-string.js)
|
[✔️](kotlin%2F0438-find-all-anagrams-in-a-string.kt)
|
[✔️](python%2F0438-find-all-anagrams-in-a-string.py)
|
|
[✔️](rust%2F0438-find-all-anagrams-in-a-string.rs)
|
|
|
[✔️](typescript%2F0438-find-all-anagrams-in-a-string.ts)
+[0028 - Find The Index of The First Occurrence in a String](https://leetcode.com/problems/find-the-index-of-the-first-occurrence-in-a-string/) |
|
[✔️](c%2F0028-find-the-index-of-the-first-occurrence-in-a-string.c)
|
[✔️](cpp%2F0028-find-the-index-of-the-first-occurrence-in-a-string.cpp)
|
[✔️](csharp%2F0028-find-the-index-of-the-first-occurrence-in-a-string.cs)
|
[✔️](dart%2F0028-find-the-index-of-the-first-occurrence-in-a-string.dart)
|
[✔️](go%2F0028-find-the-index-of-the-first-occurrence-in-a-string.go)
|
|
[✔️](java%2F0028-find-the-index-of-the-first-occurrence-in-a-string.java)
|
[✔️](javascript%2F0028-find-the-index-of-the-first-occurrence-in-a-string.js)
|
[✔️](kotlin%2F0028-find-the-index-of-the-first-occurrence-in-a-string.kt)
|
[✔️](python%2F0028-find-the-index-of-the-first-occurrence-in-a-string.py)
|
|
[✔️](rust%2F0028-find-the-index-of-the-first-occurrence-in-a-string.rs)
|
|
|
+[0280 - Wiggle Sort](https://leetcode.com/problems/wiggle-sort/) |
|
|
[✔️](cpp%2F0280-wiggle-sort.cpp)
|
|
|
|
|
[✔️](java%2F0280-wiggle-sort.java)
|
|
[✔️](kotlin%2F0280-wiggle-sort.kt)
|
[✔️](python%2F0280-wiggle-sort.py)
|
|
|
|
|
+[0179 - Largest Number](https://leetcode.com/problems/largest-number/) |
|
[✔️](c%2F0179-largest-number.c)
|
[✔️](cpp%2F0179-largest-number.cpp)
|
[✔️](csharp%2F0179-largest-number.cs)
|
|
[✔️](go%2F0179-largest-number.go)
|
|
[✔️](java%2F0179-largest-number.java)
|
[✔️](javascript%2F0179-largest-number.js)
|
[✔️](kotlin%2F0179-largest-number.kt)
|
[✔️](python%2F0179-largest-number.py)
|
[✔️](ruby%2F0179-largest-number.rb)
|
[✔️](rust%2F0179-largest-number.rs)
|
[✔️](scala%2F0179-largest-number.scala)
|
[✔️](swift%2F0179-largest-number.swift)
|
[✔️](typescript%2F0179-largest-number.ts)
+[0523 - Continuous Subarray Sum](https://leetcode.com/problems/continuous-subarray-sum/) |
|
[✔️](c%2F0523-continuous-subarray-sum.c)
|
[✔️](cpp%2F0523-continuous-subarray-sum.cpp)
|
[✔️](csharp%2F0523-continuous-subarray-sum.cs)
|
|
|
|
[✔️](java%2F0523-continuous-subarray-sum.java)
|
[✔️](javascript%2F0523-continuous-subarray-sum.js)
|
[✔️](kotlin%2F0523-continuous-subarray-sum.kt)
|
[✔️](python%2F0523-continuous-subarray-sum.py)
|
|
[✔️](rust%2F0523-continuous-subarray-sum.rs)
|
|
|
+[0838 - Push Dominoes](https://leetcode.com/problems/push-dominoes/) |
|
|
[✔️](cpp%2F0838-push-dominoes.cpp)
|
|
|
|
|
[✔️](java%2F0838-push-dominoes.java)
|
[✔️](javascript%2F0838-push-dominoes.js)
|
[✔️](kotlin%2F0838-push-dominoes.kt)
|
[✔️](python%2F0838-push-dominoes.py)
|
|
|
|
|
[✔️](typescript%2F0838-push-dominoes.ts)
+[0187 - Repeated DNA Sequences](https://leetcode.com/problems/repeated-dna-sequences/) |
|
|
[✔️](cpp%2F0187-repeated-dna-sequences.cpp)
|
[✔️](csharp%2F0187-repeated-dna-sequences.cs)
|
|
[✔️](go%2F0187-repeated-dna-sequences.go)
|
|
[✔️](java%2F0187-repeated-dna-sequences.java)
|
[✔️](javascript%2F0187-repeated-dna-sequences.js)
|
[✔️](kotlin%2F0187-repeated-dna-sequences.kt)
|
[✔️](python%2F0187-repeated-dna-sequences.py)
|
|
|
|
|
[✔️](typescript%2F0187-repeated-dna-sequences.ts)
+[0380 - Insert Delete Get Random O(1)](https://leetcode.com/problems/insert-delete-getrandom-o1/) |
|
|
[✔️](cpp%2F0380-insert-delete-getrandom-o1.cpp)
|
|
|
[✔️](go%2F0380-insert-delete-getrandom-o1.go)
|
|
[✔️](java%2F0380-insert-delete-getrandom-o1.java)
|
[✔️](javascript%2F0380-insert-delete-getrandom-o1.js)
|
[✔️](kotlin%2F0380-insert-delete-getrandom-o1.kt)
|
[✔️](python%2F0380-insert-delete-getrandom-o1.py)
|
|
[✔️](rust%2F0380-insert-delete-getrandom-o1.rs)
|
|
[✔️](swift%2F0380-insert-delete-getrandom-o1.swift)
|
[✔️](typescript%2F0380-insert-delete-getrandom-o1.ts)
+[1461 - Check if a String Contains all Binary Codes of Size K](https://leetcode.com/problems/check-if-a-string-contains-all-binary-codes-of-size-k/) |
|
|
[✔️](cpp%2F1461-check-if-a-string-contains-all-binary-codes-of-size-k.cpp)
|
|
|
|
|
[✔️](java%2F1461-check-if-a-string-contains-all-binary-codes-of-size-k.java)
|
[✔️](javascript%2F1461-check-if-a-string-contains-all-binary-codes-of-size-k.js)
|
[✔️](kotlin%2F1461-check-if-a-string-contains-all-binary-codes-of-size-k.kt)
|
[✔️](python%2F1461-check-if-a-string-contains-all-binary-codes-of-size-k.py)
|
|
|
|
|
+[0304 - Range Sum Query 2D Immutable](https://leetcode.com/problems/range-sum-query-2d-immutable/) |
|
|
[✔️](cpp%2F0304-range-sum-query-2d-immutable.cpp)
|
|
|
|
|
[✔️](java%2F0304-range-sum-query-2d-immutable.java)
|
[✔️](javascript%2F0304-range-sum-query-2d-immutable.js)
|
[✔️](kotlin%2F0304-range-sum-query-2d-immutable.kt)
|
[✔️](python%2F0304-range-sum-query-2d-immutable.py)
|
|
|
|
[✔️](swift%2F0304-range-sum-query-2d-immutable.swift)
|
+[0665 - Non Decreasing Array](https://leetcode.com/problems/non-decreasing-array/) |
|
[✔️](c%2F0665-non-decreasing-array.c)
|
[✔️](cpp%2F0665-non-decreasing-array.cpp)
|
[✔️](csharp%2F0665-non-decreasing-array.cs)
|
|
[✔️](go%2F0665-non-decreasing-array.go)
|
|
[✔️](java%2F0665-non-decreasing-array.java)
|
[✔️](javascript%2F0665-non-decreasing-array.js)
|
[✔️](kotlin%2F0665-non-decreasing-array.kt)
|
[✔️](python%2F0665-non-decreasing-array.py)
|
|
|
[✔️](scala%2F0665-non-decreasing-array.scala)
|
|
[✔️](typescript%2F0665-non-decreasing-array.ts)
+[0041 - First Missing Positive](https://leetcode.com/problems/first-missing-positive/) |
|
[✔️](c%2F0041-first-missing-positive.c)
|
[✔️](cpp%2F0041-first-missing-positive.cpp)
|
|
|
[✔️](go%2F0041-first-missing-positive.go)
|
|
[✔️](java%2F0041-first-missing-positive.java)
|
[✔️](javascript%2F0041-first-missing-positive.js)
|
[✔️](kotlin%2F0041-first-missing-positive.kt)
|
[✔️](python%2F0041-first-missing-positive.py)
|
|
|
|
|
[✔️](typescript%2F0041-first-missing-positive.ts)
+[1822 - Sign of An Array](https://leetcode.com/problems/sign-of-the-product-of-an-array/) |
|
[✔️](c%2F1822-sign-of-the-product-of-an-array.c)
|
[✔️](cpp%2F1822-sign-of-the-product-of-an-array.cpp)
|
|
|
[✔️](go%2F1822-sign-of-the-product-of-an-array.go)
|
|
[✔️](java%2F1822-sign-of-the-product-of-an-array.java)
|
[✔️](javascript%2F1822-sign-of-the-product-of-an-array.js)
|
[✔️](kotlin%2F1822-sign-of-the-product-of-an-array.kt)
|
[✔️](python%2F1822-sign-of-the-product-of-an-array.py)
|
|
[✔️](rust%2F1822-sign-of-the-product-of-an-array.rs)
|
|
|
[✔️](typescript%2F1822-sign-of-the-product-of-an-array.ts)
+[2215 - Find the Difference of Two Arrays](https://leetcode.com/problems/find-the-difference-of-two-arrays/) |
|
[✔️](c%2F2215-find-the-difference-of-two-arrays.c)
|
[✔️](cpp%2F2215-find-the-difference-of-two-arrays.cpp)
|
[✔️](csharp%2F2215-find-the-difference-of-two-arrays.cs)
|
|
|
|
[✔️](java%2F2215-find-the-difference-of-two-arrays.java)
|
[✔️](javascript%2F2215-find-the-difference-of-two-arrays.js)
|
[✔️](kotlin%2F2215-find-the-difference-of-two-arrays.kt)
|
[✔️](python%2F2215-find-the-difference-of-two-arrays.py)
|
|
|
|
|
[✔️](typescript%2F2215-find-the-difference-of-two-arrays.ts)
+[1603 - Design Parking System](https://leetcode.com/problems/design-parking-system/) |
|
|
[✔️](cpp%2F1603-design-parking-system.cpp)
|
[✔️](csharp%2F1603-design-parking-system.cs)
|
|
|
|
[✔️](java%2F1603-design-parking-system.java)
|
[✔️](javascript%2F1603-design-parking-system.js)
|
[✔️](kotlin%2F1603-design-parking-system.kt)
|
[✔️](python%2F1603-design-parking-system.py)
|
|
|
|
|
+[2348 - Number of Zero-Filled Subarrays](https://leetcode.com/problems/number-of-zero-filled-subarrays/) |
|
|
[✔️](cpp%2F2348-number-of-zero-filled-subarrays.cpp)
|
|
|
|
|
[✔️](java%2F2348-number-of-zero-filled-subarrays.java)
|
[✔️](javascript%2F2348-number-of-zero-filled-subarrays.js)
|
[✔️](kotlin%2F2348-number-of-zero-filled-subarrays.kt)
|
[✔️](python%2F2348-number-of-zero-filled-subarrays.py)
|
|
|
|
|
+[2405 - Optimal Partition of String](https://leetcode.com/problems/optimal-partition-of-string/) |
|
|
[✔️](cpp%2F2405-optimal-partition-of-string.cpp)
|
|
|
|
|
[✔️](java%2F2405-optimal-partition-of-string.java)
|
|
[✔️](kotlin%2F2405-optimal-partition-of-string.kt)
|
[✔️](python%2F2405-optimal-partition-of-string.py)
|
|
|
|
|
+[1396 - Design Underground System](https://leetcode.com/problems/design-underground-system/) |
|
|
|
[✔️](csharp%2F1396-design-underground-system.cs)
|
|
|
|
[✔️](java%2F1396-design-underground-system.java)
|
[✔️](javascript%2F1396-design-underground-system.js)
|
[✔️](kotlin%2F1396-design-underground-system.kt)
|
[✔️](python%2F1396-design-underground-system.py)
|
|
|
|
|
+[2483 - Minimum Penalty for a Shop](https://leetcode.com/problems/minimum-penalty-for-a-shop/) |
|
|
[✔️](cpp%2F2483-minimum-penalty-for-a-shop.cpp)
|
|
|
|
|
[✔️](java%2F2483-minimum-penalty-for-a-shop.java)
|
|
[✔️](kotlin%2F2483-minimum-penalty-for-a-shop.kt)
|
[✔️](python%2F2483-minimum-penalty-for-a-shop.py)
|
|
|
|
|
+[0068 - Text Justification](https://leetcode.com/problems/text-justification/) |
|
|
|
|
|
|
|
[✔️](java%2F0068-text-justification.java)
|
|
[✔️](kotlin%2F0068-text-justification.kt)
|
[✔️](python%2F0068-text-justification.py)
|
|
|
|
|
+[2306 - Naming a Company](https://leetcode.com/problems/naming-a-company/) |
|
|
[✔️](cpp%2F2306-naming-a-company.cpp)
|
|
|
|
|
[✔️](java%2F2306-naming-a-company.java)
|
[✔️](javascript%2F2306-naming-a-company.js)
|
[✔️](kotlin%2F2306-naming-a-company.kt)
|
[✔️](python%2F2306-naming-a-company.py)
|
|
|
|
|
+ +### Two Pointers + +Problem | articles | C | C++ | C# | Dart | GO | hints | Java | JS | Kotlin | Python | Ruby | Rust | Scala | Swift | TS +---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- +[0125 - Valid Palindrome](https://leetcode.com/problems/valid-palindrome/) |
|
[✔️](c%2F0125-valid-palindrome.c)
|
[✔️](cpp%2F0125-valid-palindrome.cpp)
|
[✔️](csharp%2F0125-valid-palindrome.cs)
|
[✔️](dart%2F0125-valid-palindrome.dart)
|
[✔️](go%2F0125-valid-palindrome.go)
|
|
[✔️](java%2F0125-valid-palindrome.java)
|
[✔️](javascript%2F0125-valid-palindrome.js)
|
[✔️](kotlin%2F0125-valid-palindrome.kt)
|
[✔️](python%2F0125-valid-palindrome.py)
|
[✔️](ruby%2F0125-valid-palindrome.rb)
|
[✔️](rust%2F0125-valid-palindrome.rs)
|
[✔️](scala%2F0125-valid-palindrome.scala)
|
[✔️](swift%2F0125-valid-palindrome.swift)
|
[✔️](typescript%2F0125-valid-palindrome.ts)
+[0680 - Valid Palindrome II](https://leetcode.com/problems/valid-palindrome-ii/) |
|
[✔️](c%2F0680-valid-palindrome-ii.c)
|
[✔️](cpp%2F0680-valid-palindrome-ii.cpp)
|
[✔️](csharp%2F0680-valid-palindrome-ii.cs)
|
|
[✔️](go%2F0680-valid-palindrome-ii.go)
|
|
[✔️](java%2F0680-valid-palindrome-ii.java)
|
[✔️](javascript%2F0680-valid-palindrome-ii.js)
|
[✔️](kotlin%2F0680-valid-palindrome-ii.kt)
|
[✔️](python%2F0680-valid-palindrome-ii.py)
|
|
[✔️](rust%2F0680-valid-palindrome-II.rs)
|
|
|
[✔️](typescript%2F0680-valid-palindrome-ii.ts)
+[1984 - Minimum Difference Between Highest And Lowest of K Scores](https://leetcode.com/problems/minimum-difference-between-highest-and-lowest-of-k-scores/) |
|
[✔️](c%2F1984-minimum-difference-between-highest-and-lowest-of-k-scores.c)
|
[✔️](cpp%2F1984-minimum-difference-between-highest-and-lowest-of-k-scores.cpp)
|
[✔️](csharp%2F1984-minimum-difference-between-highest-and-lowest-of-k-scores.cs)
|
|
[✔️](go%2F1984-minimum-difference-between-highest-and-lowest-of-k-scores.go)
|
|
[✔️](java%2F1984-minimum-difference-between-highest-and-lowest-of-k-scores.java)
|
[✔️](javascript%2F1984-Minimum-Difference-Between-Highest-and-Lowest-of-K-Scores.js)
|
[✔️](kotlin%2F1984-minimum-difference-between-highest-and-lowest-of-k-scores.kt)
|
[✔️](python%2F1984-minimum-difference-between-highest-and-lowest-of-k-scores.py)
|
|
[✔️](rust%2F1984-minimum-difference-between-highest-and-lowest-of-k-scores.rs)
|
|
|
[✔️](typescript%2F1984-minimum-difference-between-highest-and-lowest-of-k-scores.ts)
+[1768 - Merge Strings Alternately](https://leetcode.com/problems/merge-strings-alternately/) |
|
|
[✔️](cpp%2F1768-merge-strings-alternately.cpp)
|
[✔️](csharp%2F1768-merge-strings-alternately.cs)
|
|
|
|
[✔️](java%2F1768-merge-strings-alternately.java)
|
[✔️](javascript%2F1768-merge-strings-alternately.js)
|
[✔️](kotlin%2F1768-merge-strings-alternately.kt)
|
[✔️](python%2F1768-merge-strings-alternately.py)
|
|
|
|
|
[✔️](typescript%2F1768-merge-strings-alternately.ts)
+[0344 - Reverse String](https://leetcode.com/problems/reverse-string/) |
|
[✔️](c%2F0344-reverse-string.c)
|
[✔️](cpp%2F0344-Reverse-String.cpp)
|
[✔️](csharp%2F0344-reverse-string.cs)
|
[✔️](dart%2F0344-reverse-string.dart)
|
[✔️](go%2F0344-reverse-string.go)
|
|
[✔️](java%2F0344-reverse-string.java)
|
[✔️](javascript%2F0344-reverse-string.js)
|
[✔️](kotlin%2F0344-reverse-string.kt)
|
[✔️](python%2F0344-reverse-string.py)
|
|
[✔️](rust%2F0344-reverse-string.rs)
|
|
[✔️](swift%2F0344-reverse-string.swift)
|
[✔️](typescript%2F0344-reverse-string.ts)
+[0088 - Merge Sorted Array](https://leetcode.com/problems/merge-sorted-array/) |
|
[✔️](c%2F0088-merge-sorted-array.c)
|
[✔️](cpp%2F0088-Merge-sorted-array.cpp)
|
[✔️](csharp%2F0088-merge-sorted-array.cs)
|
|
[✔️](go%2F0088-merge-sorted-array.go)
|
|
[✔️](java%2F0088-merge-sorted-array.java)
|
[✔️](javascript%2F0088-merge-sorted-array.js)
|
[✔️](kotlin%2F0088-merge-sorted-array.kt)
|
[✔️](python%2F0088-merge-sorted-array.py)
|
|
[✔️](rust%2F0088-merge-sorted-array.rs)
|
|
|
[✔️](typescript%2F0088-merge-sorted-array.ts)
+[0283 - Move Zeroes](https://leetcode.com/problems/move-zeroes/) |
|
[✔️](c%2F0283-move-zeroes.c)
|
[✔️](cpp%2F0283-move-zeroes.cpp)
|
[✔️](csharp%2F0283-move-zeroes.cs)
|
|
[✔️](go%2F0283-move-zeroes.go)
|
|
[✔️](java%2F0283-move-zeroes.java)
|
[✔️](javascript%2F0283-move-zeroes.js)
|
[✔️](kotlin%2F0283-move-zeroes.kt)
|
[✔️](python%2F0283-move-zeroes.py)
|
|
[✔️](rust%2F0283-move-zeroes.rs)
|
|
[✔️](swift%2F0283-Move-Zeroes.Swift)
|
[✔️](typescript%2F0283-move-zeroes.ts)
+[0026 - Remove Duplicates From Sorted Array](https://leetcode.com/problems/remove-duplicates-from-sorted-array/) |
|
[✔️](c%2F0026-remove-duplicates-from-sorted-array.c)
|
[✔️](cpp%2F0026-remove-duplicates-from-sorted-array.cpp)
|
[✔️](csharp%2F0026-remove-duplicates-from-sorted-array.cs)
|
|
[✔️](go%2F0026-remove-duplicates-from-sorted-array.go)
|
|
[✔️](java%2F0026-remove-duplicates-from-sorted-array.java)
|
[✔️](javascript%2F0026-remove-duplicates-from-sorted-array.js)
|
[✔️](kotlin%2F0026-remove-duplicates-from-sorted-array.kt)
|
[✔️](python%2F0026-remove-duplicates-from-sorted-array.py)
|
|
[✔️](rust%2F0026-remove-duplicates-from-sorted-array.rs)
|
|
[✔️](swift%2F0026-Remove-Duplicates-from-Sorted-Array.swift)
|
[✔️](typescript%2F0026-remove-duplicates-from-sorted-array.ts)
+[0080 - Remove Duplicates From Sorted Array II](https://leetcode.com/problems/remove-duplicates-from-sorted-array-ii/) |
|
[✔️](c%2F0080-remove-duplicates-from-sorted-array-ii.c)
|
[✔️](cpp%2F0080-remove-duplicates-from-sorted-array-ii.cpp)
|
[✔️](csharp%2F0080-remove-duplicates-from-sorted-array-ii.cs)
|
|
|
|
[✔️](java%2F0080-remove-duplicates-from-sorted-array-ii.java)
|
[✔️](javascript%2F0080-remove-duplicates-from-sorted-array-ii.js)
|
[✔️](kotlin%2F0080-remove-duplicates-from-sorted-array-ii.kt)
|
[✔️](python%2F0080-remove-duplicates-from-sorted-array-ii.py)
|
|
[✔️](rust%2F0080-remove-duplicates-from-sorted-array-ii.rs)
|
|
[✔️](swift%2F0080-remove-duplicates-from-sorted-array-ii.swift)
|
+[0167 - Two Sum II Input Array Is Sorted](https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/) |
|
[✔️](c%2F0167-two-sum-ii-input-array-is-sorted.c)
|
[✔️](cpp%2F0167-two-sum-ii-input-array-is-sorted.cpp)
|
[✔️](csharp%2F0167-two-sum-ii-input-array-is-sorted.cs)
|
|
[✔️](go%2F0167-two-sum-ii-input-array-is-sorted.go)
|
|
[✔️](java%2F0167-two-sum-ii-input-array-is-sorted.java)
|
[✔️](javascript%2F0167-two-sum-ii-input-array-is-sorted.js)
|
[✔️](kotlin%2F0167-two-sum-ii-input-array-is-sorted.kt)
|
[✔️](python%2F0167-two-sum-ii-input-array-is-sorted.py)
|
[✔️](ruby%2F0167-two-sum-ii-input-array-is-sorted.rb)
|
[✔️](rust%2F0167-two-sum-ii-input-array-is-sorted.rs)
|
[✔️](scala%2F0167-two-sum-ii-input-array-is-sorted.scala)
|
[✔️](swift%2F0167-two-sum-ii-input-array-is-sorted.swift)
|
[✔️](typescript%2F0167-two-sum-ii-input-array-is-sorted.ts)
+[0015 - 3Sum](https://leetcode.com/problems/3sum/) |
|
[✔️](c%2F0015-3sum.c)
|
[✔️](cpp%2F0015-3sum.cpp)
|
[✔️](csharp%2F0015-3sum.cs)
|
[✔️](dart%2F0015-3sum.dart)
|
[✔️](go%2F0015-3sum.go)
|
|
[✔️](java%2F0015-3sum.java)
|
[✔️](javascript%2F0015-3sum.js)
|
[✔️](kotlin%2F0015-3sum.kt)
|
[✔️](python%2F0015-3sum.py)
|
[✔️](ruby%2F0015-3sum.rb)
|
[✔️](rust%2F0015-3sum.rs)
|
[✔️](scala%2F0015-3sum.scala)
|
[✔️](swift%2F0015-3sum.swift)
|
[✔️](typescript%2F0015-3sum.ts)
+[0018 - 4Sum](https://leetcode.com/problems/4sum/) |
|
|
[✔️](cpp%2F0018-4sum.cpp)
|
|
|
[✔️](go%2F0018-4Sum.go)
|
|
[✔️](java%2F0018-4sum.java)
|
[✔️](javascript%2F0018-4sum.js)
|
[✔️](kotlin%2F0018-4sum.kt)
|
[✔️](python%2F0018-4sum.py)
|
|
|
|
[✔️](swift%2F0018-4sum.swift)
|
[✔️](typescript%2F0018-4sum.ts)
+[0011 - Container With Most Water](https://leetcode.com/problems/container-with-most-water/) |
|
[✔️](c%2F0011-container-with-most-water.c)
|
[✔️](cpp%2F0011-container-with-most-water.cpp)
|
[✔️](csharp%2F0011-container-with-most-water.cs)
|
[✔️](dart%2F0011-container-with-most-water.dart)
|
[✔️](go%2F0011-container-with-most-water.go)
|
|
[✔️](java%2F0011-container-with-most-water.java)
|
[✔️](javascript%2F0011-container-with-most-water.js)
|
[✔️](kotlin%2F0011-container-with-most-water.kt)
|
[✔️](python%2F0011-container-with-most-water.py)
|
[✔️](ruby%2F0011-container-with-most-water.rb)
|
[✔️](rust%2F0011-container-with-most-water.rs)
|
[✔️](scala%2F0011-container-with-most-water.scala)
|
[✔️](swift%2F0011-container-with-most-water.swift)
|
[✔️](typescript%2F0011-container-with-most-water.ts)
+[1498 - Number of Subsequences That Satisfy The Given Sum Condition](https://leetcode.com/problems/number-of-subsequences-that-satisfy-the-given-sum-condition/) |
|
|
[✔️](cpp%2F1498-number-of-subsequences-that-satisfy-the-given-sum-condition.cpp)
|
|
|
|
|
[✔️](java%2F1498-number-of-subsequences-that-satisfy-the-given-sum-condition.java)
|
|
[✔️](kotlin%2F1498-number-of-subsequences-that-satisfy-the-given-sum-condition.kt)
|
[✔️](python%2F1498-number-of-subsequences-that-satisfy-the-given-sum-condition.py)
|
|
|
|
|
+[0189 - Rotate Array](https://leetcode.com/problems/rotate-array/) |
|
|
[✔️](cpp%2F0189-rotate-array.cpp)
|
|
|
[✔️](go%2F0189-rotate-array.go)
|
|
[✔️](java%2F0189-rotate-array.java)
|
[✔️](javascript%2F0189-rotate-array.js)
|
[✔️](kotlin%2F0189-rotate-array.kt)
|
[✔️](python%2F0189-rotate-array.py)
|
|
|
|
|
[✔️](typescript%2F0189-rotate-array.ts)
+[1968 - Array With Elements Not Equal to Average of Neighbors](https://leetcode.com/problems/array-with-elements-not-equal-to-average-of-neighbors/) |
|
[✔️](c%2F1968-array-with-elements-not-equal-to-average-of-neighbors.c)
|
[✔️](cpp%2F1968-array-with-elements-not-equal-to-average-of-neighbors.cpp)
|
|
|
[✔️](go%2F1968-array-with-elements-not-equal-to-average-of-neighbors.go)
|
|
[✔️](java%2F1968-array-with-elements-not-equal-to-average-of-neighbors.java)
|
[✔️](javascript%2F1968-array-with-elements-not-equal-to-average-of-neighbors.js)
|
[✔️](kotlin%2F1968-array-with-elements-not-equal-to-average-of-neighbors.kt)
|
[✔️](python%2F1968-array-with-elements-not-equal-to-average-of-neighbors.py)
|
|
|
|
|
+[0881 - Boats to Save People](https://leetcode.com/problems/boats-to-save-people/) |
|
[✔️](c%2F0881-boats-to-save-people.c)
|
[✔️](cpp%2F0881-boats-to-save-people.cpp)
|
|
|
[✔️](go%2F0881-boats-to-save-people.go)
|
|
[✔️](java%2F0881-boats-to-save-people.java)
|
[✔️](javascript%2F0881-boats-to-save-people.js)
|
[✔️](kotlin%2F0881-boats-to-save-people.kt)
|
[✔️](python%2F0881-boats-to-save-people.py)
|
|
|
|
[✔️](swift%2F0881-boats-to-save-people.swift)
|
[✔️](typescript%2F0881-boats-to-save-people.ts)
+[0042 - Trapping Rain Water](https://leetcode.com/problems/trapping-rain-water/) |
|
[✔️](c%2F0042-trapping-rain-water.c)
|
[✔️](cpp%2F0042-trapping-rain-water.cpp)
|
[✔️](csharp%2F0042-trapping-rain-water.cs)
|
[✔️](dart%2F0042-trapping-rain-water.dart)
|
[✔️](go%2F0042-trapping-rain-water.go)
|
|
[✔️](java%2F0042-trapping-rain-water.java)
|
[✔️](javascript%2F0042-trapping-rain-water.js)
|
[✔️](kotlin%2F0042-trapping-rain-water.kt)
|
[✔️](python%2F0042-trapping-rain-water.py)
|
[✔️](ruby%2F0042-trapping-rain-water.rb)
|
[✔️](rust%2F0042-trapping-rain-water.rs)
|
|
[✔️](swift%2F0042-trapping-rain-water.swift)
|
[✔️](typescript%2F0042-trapping-rain-water.ts)
+ +### Sliding Window + +Problem | articles | C | C++ | C# | Dart | GO | hints | Java | JS | Kotlin | Python | Ruby | Rust | Scala | Swift | TS +---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- +[0121 - Best Time to Buy And Sell Stock](https://leetcode.com/problems/best-time-to-buy-and-sell-stock/) |
|
[✔️](c%2F0121-best-time-to-buy-and-sell-stock.c)
|
[✔️](cpp%2F0121-best-time-to-buy-and-sell-stock.cpp)
|
[✔️](csharp%2F0121-best-time-to-buy-and-sell-stock.cs)
|
[✔️](dart%2F0121-best-time-to-buy-and-sell-stock.dart)
|
[✔️](go%2F0121-best-time-to-buy-and-sell-stock.go)
|
|
[✔️](java%2F0121-best-time-to-buy-and-sell-stock.java)
|
[✔️](javascript%2F0121-best-time-to-buy-and-sell-stock.js)
|
[✔️](kotlin%2F0121-best-time-to-buy-and-sell-stock.kt)
|
[✔️](python%2F0121-best-time-to-buy-and-sell-stock.py)
|
[✔️](ruby%2F0121-best-time-to-buy-and-sell-stock.rb)
|
[✔️](rust%2F0121-best-time-to-buy-and-sell-stock.rs)
|
[✔️](scala%2F0121-best-time-to-buy-and-sell-stock.scala)
|
[✔️](swift%2F0121-best-time-to-buy-and-sell-stock.swift)
|
[✔️](typescript%2F0121-best-time-to-buy-and-sell-stock.ts)
+[0219 - Contains Duplicate II](https://leetcode.com/problems/contains-duplicate-ii/) |
|
|
[✔️](cpp%2F0219-contains-duplicate-ii.cpp)
|
[✔️](csharp%2F0219-contains-duplicate-ii.cs)
|
|
[✔️](go%2F0219-contains-duplicate-ii.go)
|
|
[✔️](java%2F0219-contains-duplicate-ii.java)
|
[✔️](javascript%2F0219-contains-duplicate-ii.js)
|
[✔️](kotlin%2F0219-contains-duplicate-ii.kt)
|
[✔️](python%2F0219-contains-duplicate-ii.py)
|
|
[✔️](rust%2F0219-contains-duplicate-ii.rs)
|
|
[✔️](swift%2F0219-contains-duplicate-ii.swift)
|
[✔️](typescript%2F0219-contains-duplicate-ii.ts)
+[1343 - Number of Sub Arrays of Size K and Avg Greater than or Equal to Threshold](https://leetcode.com/problems/number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold/) |
|
|
[✔️](cpp%2F1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.cpp)
|
[✔️](csharp%2F1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.cs)
|
|
[✔️](go%2F1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.go)
|
|
[✔️](java%2F1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.java)
|
[✔️](javascript%2F1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.js)
|
[✔️](kotlin%2F1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.kt)
|
[✔️](python%2F1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.py)
|
|
|
|
[✔️](swift%2F1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.swift)
|
+[0003 - Longest Substring Without Repeating Characters](https://leetcode.com/problems/longest-substring-without-repeating-characters/) |
|
[✔️](c%2F0003-longest-substring-without-repeating-characters.c)
|
[✔️](cpp%2F0003-longest-substring-without-repeating-characters.cpp)
|
[✔️](csharp%2F0003-longest-substring-without-repeating-characters.cs)
|
[✔️](dart%2F0003-longest-substring-without-repeating-characters.dart)
|
[✔️](go%2F0003-longest-substring-without-repeating-characters.go)
|
|
[✔️](java%2F0003-longest-substring-without-repeating-characters.java)
|
[✔️](javascript%2F0003-longest-substring-without-repeating-characters.js)
|
[✔️](kotlin%2F0003-longest-substring-without-repeating-characters.kt)
|
[✔️](python%2F0003-longest-substring-without-repeating-characters.py)
|
[✔️](ruby%2F0003-longest-substring-without-repeating-characters.rb)
|
[✔️](rust%2F0003-longest-substring-without-repeating-characters.rs)
|
[✔️](scala%2F0003-longest-substring-without-repeating-characters.scala)
|
[✔️](swift%2F0003-longest-substring-without-repeating-characters.swift)
|
[✔️](typescript%2F0003-longest-substring-without-repeating-characters.ts)
+[0424 - Longest Repeating Character Replacement](https://leetcode.com/problems/longest-repeating-character-replacement/) |
|
[✔️](c%2F0424-longest-repeating-character-replacement.c)
|
[✔️](cpp%2F0424-longest-repeating-character-replacement.cpp)
|
[✔️](csharp%2F0424-longest-repeating-character-replacement.cs)
|
|
[✔️](go%2F0424-longest-repeating-character-replacement.go)
|
|
[✔️](java%2F0424-longest-repeating-character-replacement.java)
|
[✔️](javascript%2F0424-longest-repeating-character-replacement.js)
|
[✔️](kotlin%2F0424-longest-repeating-character-replacement.kt)
|
[✔️](python%2F0424-longest-repeating-character-replacement.py)
|
[✔️](ruby%2F0424-longest-repeating-character-replacement.rb)
|
[✔️](rust%2F0424-longest-repeating-character-replacement.rs)
|
|
[✔️](swift%2F0424-longest-repeating-character-replacement.swift)
|
[✔️](typescript%2F0424-longest-repeating-character-replacement.ts)
+[0567 - Permutation In String](https://leetcode.com/problems/permutation-in-string/) |
|
[✔️](c%2F0567-permutation-in-string.c)
|
[✔️](cpp%2F0567-permutation-in-string.cpp)
|
[✔️](csharp%2F0567-permutation-in-string.cs)
|
|
[✔️](go%2F0567-permutation-in-string.go)
|
|
[✔️](java%2F0567-permutation-in-string.java)
|
[✔️](javascript%2F0567-permutation-in-string.js)
|
[✔️](kotlin%2F0567-permutation-in-string.kt)
|
[✔️](python%2F0567-permutation-in-string.py)
|
|
[✔️](rust%2F0567-permutation-in-string.rs)
|
|
[✔️](swift%2F0567-permutation-in-string.swift)
|
[✔️](typescript%2F0567-permutation-in-string.ts)
+[1838 - Frequency of The Most Frequent Element](https://leetcode.com/problems/frequency-of-the-most-frequent-element/) |
|
[✔️](c%2F1838-frequency-of-the-most-frequent-element.c)
|
[✔️](cpp%2F1838-frequency-of-the-most-frequent-element.cpp)
|
[✔️](csharp%2F1838-Frequency-Of-The-Most-Frequent-Element.cs)
|
|
[✔️](go%2F1838-frequency-of-the-most-frequent-element.go)
|
|
[✔️](java%2F1838-frequency-of-the-most-frequent-element.java)
|
[✔️](javascript%2F1838-frequency-of-the-most-frequent-element.js)
|
[✔️](kotlin%2F1838-frequency-of-the-most-frequent-element.kt)
|
[✔️](python%2F1838-frequency-of-the-most-frequent-element.py)
|
|
[✔️](rust%2F1838-frequency-of-the-most-frequent-element.rs)
|
|
|
[✔️](typescript%2F1838-frequency-of-the-most-frequent-element.ts)
+[0904 - Fruits into Basket](https://leetcode.com/problems/fruit-into-baskets/) |
|
|
[✔️](cpp%2F0904-fruit-into-baskets.cpp)
|
|
|
[✔️](go%2F0904-fruit-into-baskets.go)
|
|
[✔️](java%2F0904-fruit-into-baskets.java)
|
[✔️](javascript%2F0904-fruit-into-baskets.js)
|
[✔️](kotlin%2F0904-fruit-into-baskets.kt)
|
[✔️](python%2F0904-fruit-into-baskets.py)
|
|
[✔️](rust%2F0904-fruit-into-baskets.rs)
|
|
|
[✔️](typescript%2F0904-fruit-into-baskets.ts)
+[1456 - Maximum Number of Vowels in a Substring of Given Length](https://leetcode.com/problems/maximum-number-of-vowels-in-a-substring-of-given-length/) |
|
[✔️](c%2F1456-maximum-number-of-vowels-in-a-substring-of-given-length.c)
|
[✔️](cpp%2F1456-maximum-number-of-vowels-in-a-substring-of-given-length.cpp)
|
|
|
[✔️](go%2F1456-maximum-number-of-vowels-in-a-substring-of-given-length.go)
|
|
[✔️](java%2F1456-maximum-number-of-vowels-in-a-substring-of-given-length.java)
|
|
[✔️](kotlin%2F1456-maximum-number-of-vowels-in-a-substring-of-given-length.kt)
|
[✔️](python%2F1456-maximum-number-of-vowels-in-a-substring-of-given-length.py)
|
|
|
|
|
+[1888 - Minimum Number of Flips to Make The Binary String Alternating](https://leetcode.com/problems/minimum-number-of-flips-to-make-the-binary-string-alternating/) |
|
|
[✔️](cpp%2F1888-minimum-number-of-flips-to-make-the-binary-string-alternating.cpp)
|
|
|
|
|
|
[✔️](javascript%2F1888-minimum-number-of-flips-to-make-the-binary-string-alternating.js)
|
[✔️](kotlin%2F1888-minimum-number-of-flips-to-make-the-binary-string-alternating.kt)
|
[✔️](python%2F1888-minimum-number-of-flips-to-make-the-binary-string-alternating.py)
|
|
|
|
|
[✔️](typescript%2F1888-minimum-number-of-flips-to-make-the-binary-string-alternating.ts)
+[0209 - Minimum Size Subarray Sum](https://leetcode.com/problems/minimum-size-subarray-sum/) |
|
[✔️](c%2F0209-minimum-size-subarray-sum.c)
|
[✔️](cpp%2F0209-minimum-size-subarray-sum.cpp)
|
|
|
[✔️](go%2F0209-minimum-size-subarray-sum.go)
|
|
[✔️](java%2F0209-minimum-size-subarray-sum.java)
|
[✔️](javascript%2F0209-minimum-size-subarray-sum.js)
|
[✔️](kotlin%2F0209-minimum-size-subarray-sum.kt)
|
[✔️](python%2F0209-minimum-size-subarray-sum.py)
|
|
|
|
[✔️](swift%2F0209-minimum-size-subarray-sum.swift)
|
+[0658 - Find K Closest Elements](https://leetcode.com/problems/find-k-closest-elements/) |
|
|
|
|
|
[✔️](go%2F0658-find-k-closest-elements.go)
|
|
[✔️](java%2F0658-find-k-closest-elements.java)
|
[✔️](javascript%2F0658-find-k-closest-elements.js)
|
[✔️](kotlin%2F0658-find-k-closest-elements.kt)
|
[✔️](python%2F0658-find-k-closest-elements.py)
|
|
[✔️](rust%2F0658-find-k-closest-elements.rs)
|
|
|
[✔️](typescript%2F0658-find-k-closest-elements.ts)
+[1658 - Minimum Operations to Reduce X to Zero](https://leetcode.com/problems/minimum-operations-to-reduce-x-to-zero/) |
|
|
|
|
|
[✔️](go%2F1658-minimum-operations-to-reduce-x-to-zero.go)
|
|
[✔️](java%2F1658-minimum-operations-to-reduce-x-to-zero.java)
|
|
[✔️](kotlin%2F1658-minimum-operations-to-reduce-x-to-zero.kt)
|
[✔️](python%2F1658-minimum-operations-to-reduce-x-to-zero.py)
|
|
|
|
|
+[0076 - Minimum Window Substring](https://leetcode.com/problems/minimum-window-substring/) |
|
[✔️](c%2F0076-minimum-window-substring.c)
|
[✔️](cpp%2F0076-minimum-window-substring.cpp)
|
[✔️](csharp%2F0076-minimum-window-substring.cs)
|
|
[✔️](go%2F0076-minimum-window-substring.go)
|
|
[✔️](java%2F0076-minimum-window-substring.java)
|
[✔️](javascript%2F0076-minimum-window-substring.js)
|
[✔️](kotlin%2F0076-minimum-window-substring.kt)
|
[✔️](python%2F0076-minimum-window-substring.py)
|
[✔️](ruby%2F0076-minimum-window-substring.rb)
|
[✔️](rust%2F0076-minimum-window-substring.rs)
|
|
[✔️](swift%2F0076-minimum-window-substring.swift)
|
[✔️](typescript%2F0076-minimum-window-substring.ts)
+[0239 - Sliding Window Maximum](https://leetcode.com/problems/sliding-window-maximum/) |
|
[✔️](c%2F0239-sliding-window-maximum.c)
|
[✔️](cpp%2F0239-sliding-window-maximum.cpp)
|
[✔️](csharp%2F0239-sliding-window-maximum.cs)
|
|
[✔️](go%2F0239-sliding-window-maximum.go)
|
|
[✔️](java%2F0239-sliding-window-maximum.java)
|
[✔️](javascript%2F0239-sliding-window-maximum.js)
|
[✔️](kotlin%2F0239-sliding-window-maximum.kt)
|
[✔️](python%2F0239-sliding-window-maximum.py)
|
[✔️](ruby%2F0239-sliding-window-maximum.rb)
|
[✔️](rust%2F0239-sliding-window-maximum.rs)
|
|
[✔️](swift%2F0239-sliding-window-maximum.swift)
|
[✔️](typescript%2F0239-sliding-window-maximum.ts)
+ +### Stack + +Problem | articles | C | C++ | C# | Dart | GO | hints | Java | JS | Kotlin | Python | Ruby | Rust | Scala | Swift | TS +---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- +[0020 - Valid Parentheses](https://leetcode.com/problems/valid-parentheses/) |
|
[✔️](c%2F0020-valid-parentheses.c)
|
[✔️](cpp%2F0020-valid-parentheses.cpp)
|
[✔️](csharp%2F0020-valid-parentheses.cs)
|
[✔️](dart%2F0020-valid-parentheses.dart)
|
[✔️](go%2F0020-valid-parentheses.go)
|
|
[✔️](java%2F0020-valid-parentheses.java)
|
[✔️](javascript%2F0020-valid-parentheses.js)
|
[✔️](kotlin%2F0020-valid-parentheses.kt)
|
[✔️](python%2F0020-valid-parentheses.py)
|
[✔️](ruby%2F0020-valid-parentheses.rb)
|
[✔️](rust%2F0020-valid-parentheses.rs)
|
|
[✔️](swift%2F0020-valid-parentheses.swift)
|
[✔️](typescript%2F0020-valid-parentheses.ts)
+[0682 - Baseball Game](https://leetcode.com/problems/baseball-game/) |
|
[✔️](c%2F0682-baseball-game.c)
|
[✔️](cpp%2F0682-baseball-game.cpp)
|
[✔️](csharp%2F0682-baseball-game.cs)
|
|
[✔️](go%2F0682-baseball-game.go)
|
|
[✔️](java%2F0682-baseball-game.java)
|
[✔️](javascript%2F0682-Baseball-Game.js)
|
[✔️](kotlin%2F0682-baseball-game.kt)
|
[✔️](python%2F0682-baseball-game.py)
|
|
[✔️](rust%2F0682-baseball-game.rs)
|
|
[✔️](swift%2F0682-baseball-game.swift)
|
[✔️](typescript%2F0682-baseball-game.ts)
+[0225 - Implement Stack Using Queues](https://leetcode.com/problems/implement-stack-using-queues/) |
|
[✔️](c%2F0225-implement-stack-using-queues.c)
|
[✔️](cpp%2F0225-implement-stack-using-queues.cpp)
|
[✔️](csharp%2F0225-implement-stack-using-queues.cs)
|
|
[✔️](go%2F0225-implement-stack-using-queues.go)
|
|
[✔️](java%2F0225-implement-stack-using-queues.java)
|
[✔️](javascript%2F0225-implement-stack-using-queues.js)
|
[✔️](kotlin%2F0225-implement-stack-using-queues.kt)
|
[✔️](python%2F0225-implement-stack-using-queues.py)
|
|
[✔️](rust%2F0225-implement-stack-using-queues.rs)
|
|
[✔️](swift%2F0225-implement-stack-using-queues.swift)
|
[✔️](typescript%2F0225-implement-stack-using-queues.ts)
+[0155 - Min Stack](https://leetcode.com/problems/min-stack/) |
|
[✔️](c%2F0155-min-stack.c)
|
[✔️](cpp%2F0155-min-stack.cpp)
|
[✔️](csharp%2F0155-min-stack.cs)
|
[✔️](dart%2F0155-min-stack.dart)
|
[✔️](go%2F0155-min-stack.go)
|
|
[✔️](java%2F0155-min-stack.java)
|
[✔️](javascript%2F0155-min-stack.js)
|
[✔️](kotlin%2F0155-min-stack.kt)
|
[✔️](python%2F0155-min-stack.py)
|
[✔️](ruby%2F0155-min-stack.rb)
|
[✔️](rust%2F0155-min-stack.rs)
|
|
[✔️](swift%2F0155-min-stack.swift)
|
[✔️](typescript%2F0155-min-stack.ts)
+[0150 - Evaluate Reverse Polish Notation](https://leetcode.com/problems/evaluate-reverse-polish-notation/) |
|
[✔️](c%2F0150-evaluate-reverse-polish-notation.c)
|
[✔️](cpp%2F0150-evaluate-reverse-polish-notation.cpp)
|
[✔️](csharp%2F0150-evaluate-reverse-polish-notation.cs)
|
[✔️](dart%2F0150-evaluate-reverse-polish-notation.dart)
|
[✔️](go%2F0150-evaluate-reverse-polish-notation.go)
|
|
[✔️](java%2F0150-evaluate-reverse-polish-notation.java)
|
[✔️](javascript%2F0150-evaluate-reverse-polish-notation.js)
|
[✔️](kotlin%2F0150-evaluate-reverse-polish-notation.kt)
|
[✔️](python%2F0150-evaluate-reverse-polish-notation.py)
|
[✔️](ruby%2F0150-evaluate-reverse-polish-notation.rb)
|
[✔️](rust%2F0150-evaluate-reverse-polish-notation.rs)
|
|
[✔️](swift%2F0150-evaluate-reverse-polish-notation.swift)
|
[✔️](typescript%2F0150-evaluate-reverse-polish-notation.ts)
+[2390 - Removing Stars From a String](https://leetcode.com/problems/removing-stars-from-a-string/) |
|
|
[✔️](cpp%2F2390-removing-stars-from-a-string.cpp)
|
[✔️](csharp%2F2390-removing-stars-from-a-string.cs)
|
|
|
|
[✔️](java%2F2390-removing-stars-from-a-string.java)
|
[✔️](javascript%2F2390-removing-stars-from-a-string.js)
|
[✔️](kotlin%2F2390-removing-stars-from-a-string.kt)
|
[✔️](python%2F2390-removing-stars-from-a-string.py)
|
|
|
|
|
[✔️](typescript%2F2390-removing-stars-from-a-string.ts)
+[0946 - Validate Stack Sequences](https://leetcode.com/problems/validate-stack-sequences/) |
|
|
[✔️](cpp%2F0946-validate-stack-sequences.cpp)
|
|
|
|
|
[✔️](java%2F0946-validate-stack-sequences.java)
|
|
[✔️](kotlin%2F0946-validate-stack-sequences.kt)
|
[✔️](python%2F0946-validate-stack-sequences.py)
|
|
|
|
|
+[0022 - Generate Parentheses](https://leetcode.com/problems/generate-parentheses/) |
|
[✔️](c%2F0022-generate-parentheses.c)
|
[✔️](cpp%2F0022-generate-parentheses.cpp)
|
[✔️](csharp%2F0022-generate-parentheses.cs)
|
|
[✔️](go%2F0022-generate-parentheses.go)
|
|
[✔️](java%2F0022-generate-parentheses.java)
|
[✔️](javascript%2F0022-generate-parentheses.js)
|
[✔️](kotlin%2F0022-generate-parentheses.kt)
|
[✔️](python%2F0022-generate-parentheses.py)
|
[✔️](ruby%2F0022-generate-parentheses.rb)
|
[✔️](rust%2F0022-generate-parentheses.rs)
|
|
[✔️](swift%2F0022-generate-parentheses.swift)
|
[✔️](typescript%2F0022-generate-parentheses.ts)
+[0735 - Asteroid Collision](https://leetcode.com/problems/asteroid-collision/) |
|
|
[✔️](cpp%2F0735-asteroid-collision.cpp)
|
|
|
[✔️](go%2F0735-asteroid-collision.go)
|
|
[✔️](java%2F0735-asteroid-collision.java)
|
[✔️](javascript%2F0735-asteroid-collision.js)
|
[✔️](kotlin%2F0735-asteroid-collision.kt)
|
[✔️](python%2F0735-asteroid-collision.py)
|
|
[✔️](rust%2F0735-asteroid-collision.rs)
|
|
[✔️](swift%2F0735-asteroid-collision.swift)
|
[✔️](typescript%2F0735-asteroid-collision.ts)
+[0739 - Daily Temperatures](https://leetcode.com/problems/daily-temperatures/) |
|
[✔️](c%2F0739-daily-temperatures.c)
|
[✔️](cpp%2F0739-daily-temperatures.cpp)
|
[✔️](csharp%2F0739-daily-temperatures.cs)
|
|
[✔️](go%2F0739-daily-temperatures.go)
|
|
[✔️](java%2F0739-daily-temperatures.java)
|
[✔️](javascript%2F0739-daily-temperatures.js)
|
[✔️](kotlin%2F0739-daily-temperatures.kt)
|
[✔️](python%2F0739-daily-temperatures.py)
|
[✔️](ruby%2F0739-daily-temperatures.rb)
|
[✔️](rust%2F0739-daily-temperatures.rs)
|
|
[✔️](swift%2F0739-daily-temperatures.swift)
|
[✔️](typescript%2F0739-daily-temperatures.ts)
+[0901 - Online Stock Span](https://leetcode.com/problems/online-stock-span/) |
|
|
[✔️](cpp%2F0901-online-stock-span.cpp)
|
|
|
|
|
[✔️](java%2F0901-online-stock-span.java)
|
[✔️](javascript%2F0901-online-stock-span.js)
|
[✔️](kotlin%2F0901-online-stock-span.kt)
|
[✔️](python%2F0901-online-stock-span.py)
|
|
[✔️](rust%2F0901-online-stock-span.rs)
|
|
|
[✔️](typescript%2F0901-online-stock-span.ts)
+[0853 - Car Fleet](https://leetcode.com/problems/car-fleet/) |
|
[✔️](c%2F0853-car-fleet.c)
|
[✔️](cpp%2F0853-car-fleet.cpp)
|
[✔️](csharp%2F0853-car-fleet.cs)
|
|
[✔️](go%2F0853-car-fleet.go)
|
|
[✔️](java%2F0853-car-fleet.java)
|
[✔️](javascript%2F0853-car-fleet.js)
|
[✔️](kotlin%2F0853-car-fleet.kt)
|
[✔️](python%2F0853-car-fleet.py)
|
[✔️](ruby%2F0853-car-fleet.rb)
|
[✔️](rust%2F0853-car-fleet.rs)
|
|
[✔️](swift%2F0853-car-fleet.swift)
|
[✔️](typescript%2F0853-car-fleet.ts)
+[0071 - Simplify Path](https://leetcode.com/problems/simplify-path/) |
|
|
[✔️](cpp%2F0071-simplify-path.cpp)
|
|
|
|
|
[✔️](java%2F0071-simplify-path.java)
|
[✔️](javascript%2F0071-simplify-path.js)
|
[✔️](kotlin%2F0071-simplify-path.kt)
|
[✔️](python%2F0071-simplify-path.py)
|
|
[✔️](rust%2F0071-simplify-path.rs)
|
|
|
[✔️](typescript%2F0071-simplify-path.ts)
+[0394 - Decode String](https://leetcode.com/problems/decode-string/) |
|
|
[✔️](cpp%2F0394-decode-string.cpp)
|
|
|
|
|
[✔️](java%2F0394-decode-string.java)
|
|
[✔️](kotlin%2F0394-decode-string.kt)
|
[✔️](python%2F0394-decode-string.py)
|
|
[✔️](rust%2F0394-decode-string.rs)
|
[✔️](scala%2F0394-decode-string.scala)
|
|
[✔️](typescript%2F0394-decode-string.ts)
+[0402 - Remove K Digits](https://leetcode.com/problems/remove-k-digits/) |
|
|
[✔️](cpp%2F0402-remove-k-digits.cpp)
|
|
|
|
|
[✔️](java%2F0402-remove-k-digits.java)
|
[✔️](javascript%2F0402-remove-k-digits.js)
|
[✔️](kotlin%2F0402-remove-k-digits.kt)
|
[✔️](python%2F0402-remove-k-digits.py)
|
|
[✔️](rust%2F0402-remove-k-digits.rs)
|
|
|
[✔️](typescript%2F0402-remove-k-digits.ts)
+[1209 - Remove All Adjacent Duplicates In String II](https://leetcode.com/problems/remove-all-adjacent-duplicates-in-string-ii/) |
|
|
[✔️](cpp%2F1209-Remove-All-Adjacent-Duplicates-in-String-II.cpp)
|
|
|
|
|
[✔️](java%2F1209-remove-all-adjacent-duplicates-in-string-ii.java)
|
[✔️](javascript%2F1209-Remove-All-Adjacent-Duplicates-in-String-II.js)
|
[✔️](kotlin%2F1209-remove-all-adjacent-duplicates-in-string-ii.kt)
|
[✔️](python%2F1209-remove-all-adjacent-duplicates-in-string-ii.py)
|
|
[✔️](rust%2F1209-remove-all-adjacent-duplicates-in-string-II.rs)
|
|
|
[✔️](typescript%2F1209-remove-all-adjacent-duplicates-in-string-II.ts)
+[0456 - 132 Pattern](https://leetcode.com/problems/132-pattern/) |
|
|
[✔️](cpp%2F0456-132-pattern.cpp)
|
|
|
|
|
[✔️](java%2F0456-132-pattern.java)
|
[✔️](javascript%2F0456-132-pattern.js)
|
[✔️](kotlin%2F0456-132-pattern.kt)
|
[✔️](python%2F0456-132-pattern.py)
|
|
[✔️](rust%2F0456-132-pattern.rs)
|
|
|
[✔️](typescript%2F0456-132-pattern.ts)
+[0895 - Maximum Frequency Stack](https://leetcode.com/problems/maximum-frequency-stack/) |
|
|
[✔️](cpp%2F0895-maximum-frequency-stack.cpp)
|
|
|
|
|
[✔️](java%2F0895-maximum-frequency-stack.java)
|
[✔️](javascript%2F0895-maximum-frequency-stack.js)
|
[✔️](kotlin%2F0895-maximum-frequency-stack.kt)
|
[✔️](python%2F0895-maximum-frequency-stack.py)
|
|
[✔️](rust%2F0895-maximum-frequency-stack.rs)
|
|
|
[✔️](typescript%2F0895-maximum-frequency-stack.ts)
+[0084 - Largest Rectangle In Histogram](https://leetcode.com/problems/largest-rectangle-in-histogram/) |
|
[✔️](c%2F0084-largest-rectangle-in-histogram.c)
|
[✔️](cpp%2F0084-largest-rectangle-in-histogram.cpp)
|
[✔️](csharp%2F0084-largest-rectangle-in-histogram.cs)
|
|
[✔️](go%2F0084-largest-rectangle-in-histogram.go)
|
|
[✔️](java%2F0084-largest-rectangle-in-histogram.java)
|
[✔️](javascript%2F0084-largest-rectangle-in-histogram.js)
|
[✔️](kotlin%2F0084-largest-rectangle-in-histogram.kt)
|
[✔️](python%2F0084-largest-rectangle-in-histogram.py)
|
[✔️](ruby%2F0084-largest-rectangle-in-histogram.rb)
|
[✔️](rust%2F0084-largest-rectangle-in-histogram.rs)
|
|
[✔️](swift%2F0084-largest-rectangle-in-histogram.swift)
|
[✔️](typescript%2F0084-largest-rectangle-in-histogram.ts)
+ +### Binary Search + +Problem | articles | C | C++ | C# | Dart | GO | hints | Java | JS | Kotlin | Python | Ruby | Rust | Scala | Swift | TS +---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- +[0704 - Binary Search](https://leetcode.com/problems/binary-search/) |
|
[✔️](c%2F0704-binary-search.c)
|
[✔️](cpp%2F0704-binary-search.cpp)
|
[✔️](csharp%2F0704-binary-search.cs)
|
[✔️](dart%2F0704-binary-search.dart)
|
[✔️](go%2F0704-binary-search.go)
|
|
[✔️](java%2F0704-binary-search.java)
|
[✔️](javascript%2F0704-binary-search.js)
|
[✔️](kotlin%2F0704-binary-search.kt)
|
[✔️](python%2F0704-binary-search.py)
|
[✔️](ruby%2F0704-binary-search.rb)
|
[✔️](rust%2F0704-binary-search.rs)
|
[✔️](scala%2F0704-binary-search.scala)
|
[✔️](swift%2F0704-binary-search.swift)
|
[✔️](typescript%2F0704-binary-search.ts)
+[0035 - Search Insert Position](https://leetcode.com/problems/search-insert-position/) |
|
[✔️](c%2F0035-search-insert-position.c)
|
[✔️](cpp%2F0035-search-insert-position.cpp)
|
[✔️](csharp%2F0035-search-insert-position.cs)
|
|
[✔️](go%2F0035-search-insert-position.go)
|
|
[✔️](java%2F0035-search-insert-position.java)
|
[✔️](javascript%2F0035-search-insert-position.js)
|
[✔️](kotlin%2F0035-search-insert-position.kt)
|
[✔️](python%2F0035-search-insert-position.py)
|
[✔️](ruby%2F0035-search-insert-position.rb)
|
[✔️](rust%2F0035-search-insert-position.rs)
|
|
[✔️](swift%2F0035-search-insert-position.swift)
|
[✔️](typescript%2F0035-search-insert-position.ts)
+[0374 - Guess Number Higher Or Lower](https://leetcode.com/problems/guess-number-higher-or-lower/) |
|
[✔️](c%2F0374-guess-number-higher-or-lower.c)
|
[✔️](cpp%2F0374-guess-number-higher-or-lower.cpp)
|
[✔️](csharp%2F0374-guess-number-higher-or-lower.cs)
|
|
[✔️](go%2F0374-guess-number-higher-or-lower.go)
|
|
[✔️](java%2F0374-guess-number-higher-or-lower.java)
|
[✔️](javascript%2F0374-guess-number-higher-or-lower.js)
|
[✔️](kotlin%2F0374-guess-number-higher-or-lower.kt)
|
[✔️](python%2F0374-guess-number-higher-or-lower.py)
|
|
[✔️](rust%2F0374-guess-number-higher-or-lower.rs)
|
|
[✔️](swift%2F0374-guess-number-higher-or-lower.swift)
|
+[0441 - Arranging Coins](https://leetcode.com/problems/arranging-coins/) |
|
|
[✔️](cpp%2F0441-arranging-coins.cpp)
|
|
|
|
|
[✔️](java%2F0441-arranging-coins.java)
|
[✔️](javascript%2F0441-arranging-coins.js)
|
[✔️](kotlin%2F0441-arranging-coins.kt)
|
[✔️](python%2F0441-arranging-coins.py)
|
|
[✔️](rust%2F0441-arranging-coins.rs)
|
|
|
+[0977 - Squares of a Sorted Array](https://leetcode.com/problems/squares-of-a-sorted-array/) |
|
|
[✔️](cpp%2F0977-squares-of-a-sorted-array.cpp)
|
|
|
[✔️](go%2F0977-squares-of-a-sorted-array.go)
|
|
[✔️](java%2F0977-squares-of-a-sorted-array.java)
|
[✔️](javascript%2F0977-squares-of-a-sorted-array.js)
|
[✔️](kotlin%2F0977-squares-of-a-sorted-array.kt)
|
[✔️](python%2F0977-squares-of-a-sorted-array.py)
|
|
[✔️](rust%2F0977-squares-of-a-sorted-array.rs)
|
|
[✔️](swift%2F0977-squares-of-a-sorted-array.swift)
|
[✔️](typescript%2F0977-squares-of-a-sorted-array.ts)
+[0367 - Valid Perfect Square](https://leetcode.com/problems/valid-perfect-square/) |
|
[✔️](c%2F0367-valid-perfect-square.c)
|
[✔️](cpp%2F0367-valid-perfect-square.cpp)
|
|
|
[✔️](go%2F0367-valid-perfect-square.go)
|
|
[✔️](java%2F0367-valid-perfect-square.java)
|
[✔️](javascript%2F0367-valid-perfect-square.js)
|
[✔️](kotlin%2F0367-valid-perfect-square.kt)
|
[✔️](python%2F0367-valid-perfect-square.py)
|
|
|
|
[✔️](swift%2F0367-valid-perfect-square.swift)
|
+[0069 - Sqrt(x) ](https://leetcode.com/problems/sqrtx/) |
|
[✔️](c%2F0069-sqrtx.c)
|
[✔️](cpp%2F0069-sqrtx.cpp)
|
[✔️](csharp%2F0069-sqrtx.cs)
|
|
|
|
[✔️](java%2F0069-sqrtx.java)
|
[✔️](javascript%2F0069-sqrtx.js)
|
[✔️](kotlin%2F0069-sqrtx.kt)
|
[✔️](python%2F0069-sqrtx.py)
|
|
|
|
|
+[0540 - Single Element in a Sorted Array](https://leetcode.com/problems/single-element-in-a-sorted-array/) |
|
[✔️](c%2F0540-single-element-in-a-sorted-array.c)
|
[✔️](cpp%2F0540-single-element-in-a-sorted-array.cpp)
|
|
|
|
|
[✔️](java%2F0540-single-element-in-a-sorted-array.java)
|
[✔️](javascript%2F0540-single-element-in-a-sorted-array.js)
|
[✔️](kotlin%2F0540-single-element-in-a-sorted-array.kt)
|
[✔️](python%2F0540-single-element-in-a-sorted-array.py)
|
|
|
|
|
[✔️](typescript%2F0540-single-element-in-a-sorted-array.ts)
+[1011 - Capacity to Ship Packages](https://leetcode.com/problems/capacity-to-ship-packages-within-d-days/) |
|
|
[✔️](cpp%2F1011-capacity-to-ship-packages-within-d-days.cpp)
|
|
|
|
|
[✔️](java%2F1011-capacity-to-ship-packages-within-d-days.java)
|
|
[✔️](kotlin%2F1011-capacity-to-ship-packages-within-d-days.kt)
|
[✔️](python%2F1011-capacity-to-ship-packages-within-d-days.py)
|
|
|
|
|
+[0162 - Find Peak Element](https://leetcode.com/problems/find-peak-element/) |
|
[✔️](c%2F0162-find-peak-element.c)
|
[✔️](cpp%2F0162-find-peak-element.cpp)
|
|
|
|
|
[✔️](java%2F0162-find-peak-element.java)
|
[✔️](javascript%2F0162-find-peak-element.js)
|
[✔️](kotlin%2F0162-find-peak-element.kt)
|
[✔️](python%2F0162-find-peak-element.py)
|
|
|
|
|
+[2300 - Successful Pairs of Spells and Potions](https://leetcode.com/problems/successful-pairs-of-spells-and-potions/) |
|
|
[✔️](cpp%2F2300-successful-pairs-of-spells-and-potions.cpp)
|
|
|
|
|
[✔️](java%2F2300-successful-pairs-of-spells-and-potions.java)
|
|
[✔️](kotlin%2F2300-successful-pairs-of-spells-and-potions.kt)
|
[✔️](python%2F2300-successful-pairs-of-spells-and-potions.py)
|
|
|
|
|
+[0074 - Search a 2D Matrix](https://leetcode.com/problems/search-a-2d-matrix/) |
|
[✔️](c%2F0074-search-a-2d-matrix.c)
|
[✔️](cpp%2F0074-search-a-2d-matrix.cpp)
|
[✔️](csharp%2F0074-search-a-2d-matrix.cs)
|
|
[✔️](go%2F0074-search-a-2d-matrix.go)
|
|
[✔️](java%2F0074-search-a-2d-matrix.java)
|
[✔️](javascript%2F0074-search-a-2d-matrix.js)
|
[✔️](kotlin%2F0074-search-a-2d-matrix.kt)
|
[✔️](python%2F0074-search-a-2d-matrix.py)
|
[✔️](ruby%2F0074-search-a-2d-matrix.rb)
|
[✔️](rust%2F0074-search-a-2d-matrix.rs)
|
|
[✔️](swift%2F0074-search-a-2d-matrix.swift)
|
[✔️](typescript%2F0074-search-a-2d-matrix.ts)
+[0875 - Koko Eating Bananas](https://leetcode.com/problems/koko-eating-bananas/) |
|
[✔️](c%2F0875-koko-eating-bananas.c)
|
[✔️](cpp%2F0875-koko-eating-bananas.cpp)
|
[✔️](csharp%2F0875-koko-eating-bananas.cs)
|
|
[✔️](go%2F0875-koko-eating-bananas.go)
|
|
[✔️](java%2F0875-koko-eating-bananas.java)
|
[✔️](javascript%2F0875-koko-eating-bananas.js)
|
[✔️](kotlin%2F0875-koko-eating-bananas.kt)
|
[✔️](python%2F0875-koko-eating-bananas.py)
|
[✔️](ruby%2F0875-koko-eating-bananas.rb)
|
[✔️](rust%2F0875-koko-eating-bananas.rs)
|
|
[✔️](swift%2F0875-koko-eating-bananas.swift)
|
[✔️](typescript%2F0875-koko-eating-bananas.ts)
+[2616 - Minimize the Maximum Difference of Pairs](https://leetcode.com/problems/minimize-the-maximum-difference-of-pairs/) |
|
|
|
|
|
|
|
[✔️](java%2F2616-minimize-the-maximum-difference-of-pairs.java)
|
|
[✔️](kotlin%2F2616-minimize-the-maximum-difference-of-pairs.kt)
|
[✔️](python%2F2616-minimize-the-maximum-difference-of-pairs.py)
|
|
|
|
|
+[0153 - Find Minimum In Rotated Sorted Array](https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/) |
|
[✔️](c%2F0153-find-minimum-in-rotated-sorted-array.c)
|
[✔️](cpp%2F0153-find-minimum-in-rotated-sorted-array.cpp)
|
[✔️](csharp%2F0153-find-minimum-in-rotated-sorted-array.cs)
|
|
[✔️](go%2F0153-find-minimum-in-rotated-sorted-array.go)
|
|
[✔️](java%2F0153-find-minimum-in-rotated-sorted-array.java)
|
[✔️](javascript%2F0153-find-minimum-in-rotated-sorted-array.js)
|
[✔️](kotlin%2F0153-find-minimum-in-rotated-sorted-array.kt)
|
[✔️](python%2F0153-find-minimum-in-rotated-sorted-array.py)
|
[✔️](ruby%2F0153-find-minimum-in-rotated-sorted-array.rb)
|
[✔️](rust%2F0153-find-minimum-in-rotated-sorted-array.rs)
|
[✔️](scala%2F0153-find-minimum-in-rotated-sorted-array.scala)
|
[✔️](swift%2F0153-find-minimum-in-rotated-sorted-array.swift)
|
[✔️](typescript%2F0153-find-minimum-in-rotated-sorted-array.ts)
+[0033 - Search In Rotated Sorted Array](https://leetcode.com/problems/search-in-rotated-sorted-array/) |
|
[✔️](c%2F0033-search-in-rotated-sorted-array.c)
|
[✔️](cpp%2F0033-search-in-rotated-sorted-array.cpp)
|
[✔️](csharp%2F0033-search-in-rotated-sorted-array.cs)
|
|
[✔️](go%2F0033-search-in-rotated-sorted-array.go)
|
|
[✔️](java%2F0033-search-in-rotated-sorted-array.java)
|
[✔️](javascript%2F0033-search-in-rotated-sorted-array.js)
|
[✔️](kotlin%2F0033-search-in-rotated-sorted-array.kt)
|
[✔️](python%2F0033-search-in-rotated-sorted-array.py)
|
|
[✔️](rust%2F0033-search-in-rotated-sorted-array.rs)
|
[✔️](scala%2F0033-search-in-rotated-sorted-array.scala)
|
[✔️](swift%2F0033-search-in-rotated-sorted-array.swift)
|
[✔️](typescript%2F0033-search-in-rotated-sorted-array.ts)
+[0081 - Search In Rotated Sorted Array II](https://leetcode.com/problems/search-in-rotated-sorted-array-ii/) |
|
|
|
|
|
|
|
[✔️](java%2F0081-search-in-rotated-sorted-array-ii.java)
|
|
[✔️](kotlin%2F0081-search-in-rotated-sorted-array-ii.kt)
|
[✔️](python%2F0081-search-in-rotated-sorted-array-ii.py)
|
|
|
|
|
+[0981 - Time Based Key Value Store](https://leetcode.com/problems/time-based-key-value-store/) |
|
[✔️](c%2F0981-time-based-key-value-store.c)
|
[✔️](cpp%2F0981-time-based-key-value-store.cpp)
|
[✔️](csharp%2F0981-time-based-key-value-store.cs)
|
|
[✔️](go%2F0981-time-based-key-value-store.go)
|
|
[✔️](java%2F0981-time-based-key-value-store.java)
|
[✔️](javascript%2F0981-time-based-key-value-store.js)
|
[✔️](kotlin%2F0981-time-based-key-value-store.kt)
|
[✔️](python%2F0981-time-based-key-value-store.py)
|
[✔️](ruby%2F0981-Time-Based-Key-Value-Store.rb)
|
[✔️](rust%2F0981-time-based-key-value-store.rs)
|
|
[✔️](swift%2F0981-time-based-key-value-store.swift)
|
[✔️](typescript%2F0981-time-based-key-value-store.ts)
+[0034 - Find First And Last Position of Element In Sorted Array](https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array/) |
|
|
[✔️](cpp%2F0034-find-first-and-last-position-of-element-in-sorted-array.cpp)
|
[✔️](csharp%2F0034-find-first-and-last-position-of-element-in-sorted-array.cs)
|
|
[✔️](go%2F0034-find-first-and-last-position-of-element-in-sorted-array.go)
|
|
[✔️](java%2F0034-find-first-and-last-position-of-element-in-sorted-array.java)
|
[✔️](javascript%2F0034-find-first-and-last-position-of-element-in-sorted-array.js)
|
[✔️](kotlin%2F0034-find-first-and-last-position-of-element-in-sorted-array.kt)
|
[✔️](python%2F0034-find-first-and-last-position-of-element-in-sorted-array.py)
|
|
|
|
[✔️](swift%2F0034-find-first-and-last-position-of-element-in-sorted-array.swift)
|
+[1898 - Maximum Number of Removable Characters](https://leetcode.com/problems/maximum-number-of-removable-characters/) |
|
|
|
|
|
[✔️](go%2F1898-maximum-number-of-removable-characters.go)
|
|
[✔️](java%2F1898-maximum-number-of-removable-characters.java)
|
[✔️](javascript%2F1898-maximum-number-of-removable-characters.js)
|
[✔️](kotlin%2F1898-maximum-number-of-removable-characters.kt)
|
|
|
|
|
|
+[0116 - Populating Next Right Pointers In Each Node](https://leetcode.com/problems/populating-next-right-pointers-in-each-node/) |
|
|
[✔️](cpp%2F0116-populating-next-right-pointers-in-each-node.cpp)
|
|
|
[✔️](go%2F0116-populating-next-right-pointers-in-each-node.go)
|
|
[✔️](java%2F0116-populating-next-right-pointers-in-each-node.java)
|
[✔️](javascript%2F0116-populating-next-right-pointers-in-each-node.js)
|
[✔️](kotlin%2F0116-populating-next-right-pointers-in-each-node.kt)
|
|
|
|
|
|
+[1268 - Search Suggestions System](https://leetcode.com/problems/search-suggestions-system/) |
|
|
|
|
|
|
|
[✔️](java%2F1268-search-suggestions-system.java)
|
[✔️](javascript%2F1268-search-suggestions-system.js)
|
[✔️](kotlin%2F1268-search-suggestions-system.kt)
|
|
|
|
|
|
+[0410 - Split Array Largest Sum](https://leetcode.com/problems/split-array-largest-sum/) |
|
|
|
|
|
|
|
[✔️](java%2F0410-split-array-largest-sum.java)
|
[✔️](javascript%2F0410-split-array-largest-sum.js)
|
[✔️](kotlin%2F0410-split-array-largest-sum.kt)
|
[✔️](python%2F0410-split-array-largest-sum.py)
|
|
|
|
|
+[0004 - Median of Two Sorted Arrays](https://leetcode.com/problems/median-of-two-sorted-arrays/) |
|
[✔️](c%2F0004-median-of-two-sorted-arrays.c)
|
[✔️](cpp%2F0004-median-of-two-sorted-arrays.cpp)
|
[✔️](csharp%2F0004-median-of-two-sorted-arrays.cs)
|
[✔️](dart%2F0004-median-of-two-sorted-arrays.dart)
|
[✔️](go%2F0004-median-of-two-sorted-arrays.go)
|
|
[✔️](java%2F0004-median-of-two-sorted-arrays.java)
|
[✔️](javascript%2F0004-median-of-two-sorted-arrays.js)
|
[✔️](kotlin%2F0004-median-of-two-sorted-arrays.kt)
|
[✔️](python%2F0004-median-of-two-sorted-arrays.py)
|
|
[✔️](rust%2F0004-median-of-two-sorted-arrays.rs)
|
|
[✔️](swift%2F0004-median-of-two-sorted-arrays.swift)
|
[✔️](typescript%2F0004-median-of-two-sorted-arrays.ts)
+ +### Linked List + +Problem | articles | C | C++ | C# | Dart | GO | hints | Java | JS | Kotlin | Python | Ruby | Rust | Scala | Swift | TS +---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- +[0206 - Reverse Linked List](https://leetcode.com/problems/reverse-linked-list/) |
|
[✔️](c%2F0206-reverse-linked-list.c)
|
[✔️](cpp%2F0206-reverse-linked-list.cpp)
|
[✔️](csharp%2F0206-reverse-linked-list.cs)
|
[✔️](dart%2F0206-reverse-linked-list.dart)
|
[✔️](go%2F0206-reverse-linked-list.go)
|
|
[✔️](java%2F0206-reverse-linked-list.java)
|
[✔️](javascript%2F0206-reverse-linked-list.js)
|
[✔️](kotlin%2F0206-reverse-linked-list.kt)
|
[✔️](python%2F0206-reverse-linked-list.py)
|
[✔️](ruby%2F0206-reverse-linked-list.rb)
|
[✔️](rust%2F0206-reverse-linked-list.rs)
|
[✔️](scala%2F0206-reverse-linked-list.scala)
|
[✔️](swift%2F0206-reverse-linked-list.swift)
|
[✔️](typescript%2F0206-reverse-linked-list.ts)
+[0021 - Merge Two Sorted Lists](https://leetcode.com/problems/merge-two-sorted-lists/) |
|
[✔️](c%2F0021-merge-two-sorted-lists.c)
|
[✔️](cpp%2F0021-merge-two-sorted-lists.cpp)
|
[✔️](csharp%2F0021-merge-two-sorted-lists.cs)
|
[✔️](dart%2F0021-merge-two-sorted-lists.dart)
|
[✔️](go%2F0021-merge-two-sorted-lists.go)
|
|
[✔️](java%2F0021-merge-two-sorted-lists.java)
|
[✔️](javascript%2F0021-merge-two-sorted-lists.js)
|
[✔️](kotlin%2F0021-merge-two-sorted-lists.kt)
|
[✔️](python%2F0021-merge-two-sorted-lists.py)
|
[✔️](ruby%2F0021-merge-two-sorted-lists.rb)
|
[✔️](rust%2F0021-merge-two-sorted-lists.rs)
|
[✔️](scala%2F0021-merge-two-sorted-lists.scala)
|
[✔️](swift%2F0021-merge-two-sorted-lists.swift)
|
[✔️](typescript%2F0021-merge-two-sorted-lists.ts)
+[0234 - Palindrome Linked List](https://leetcode.com/problems/palindrome-linked-list/) |
|
[✔️](c%2F0234-palindrome-linked-list.c)
|
[✔️](cpp%2F0234-palindrome-linked-list.cpp)
|
|
|
[✔️](go%2F0234-palindrome-linked-list.go)
|
|
[✔️](java%2F0234-palindrome-linked-list.java)
|
[✔️](javascript%2F0234-palindrome-linked-list.js)
|
[✔️](kotlin%2F0234-palindrome-linked-list.kt)
|
[✔️](python%2F0234-palindrome-linked-list.py)
|
|
|
|
|
+[0203 - Remove Linked List Elements](https://leetcode.com/problems/remove-linked-list-elements/) |
|
|
[✔️](cpp%2F0203-remove-linked-list-elements.cpp)
|
[✔️](csharp%2F0203-remove-linked-list-elements.cs)
|
|
[✔️](go%2F0203-remove-linked-list-elements.go)
|
|
[✔️](java%2F0203-remove-linked-list-elements.java)
|
[✔️](javascript%2F0203-remove-linked-list-elements.js)
|
[✔️](kotlin%2F0203-remove-linked-list-elements.kt)
|
[✔️](python%2F0203-remove-linked-list-elements.py)
|
|
|
|
|
[✔️](typescript%2F0203-remove-linked-list-elements.ts)
+[0083 - Remove Duplicates From Sorted List](https://leetcode.com/problems/remove-duplicates-from-sorted-list/) |
|
[✔️](c%2F0083-remove-duplicates-from-sorted-list.c)
|
[✔️](cpp%2F0083-remove-duplicates-from-sorted-list.cpp)
|
|
|
[✔️](go%2F0083-remove-duplicates-from-sorted-list.go)
|
|
[✔️](java%2F0083-remove-duplicates-from-sorted-list.java)
|
[✔️](javascript%2F0083-remove-duplicates-from-sorted-list.js)
|
[✔️](kotlin%2F0083-remove-duplicates-from-sorted-list.kt)
|
[✔️](python%2F0083-remove-duplicates-from-sorted-list.py)
|
|
|
|
[✔️](swift%2F0083-remove-duplicates-from-sorted-list.swift)
|
+[0876 - Middle of the Linked List](https://leetcode.com/problems/middle-of-the-linked-list/) |
|
[✔️](c%2F0876-middle-of-the-linked-list.c)
|
[✔️](cpp%2F0876-middle-of-the-linked-list.cpp)
|
[✔️](csharp%2F0876-middle-of-the-linked-list.cs)
|
|
[✔️](go%2F0876-middle-of-the-linked-list.go)
|
|
[✔️](java%2F0876-middle-of-the-linked-list.java)
|
[✔️](javascript%2F0876-middle-of-the-linked-list.js)
|
[✔️](kotlin%2F0876-middle-of-the-linked-list.kt)
|
[✔️](python%2F0876-middle-of-the-linked-list.py)
|
|
[✔️](rust%2F0876-middle-of-the-linked-list.rs)
|
|
[✔️](swift%2F0876-middle-of-the-linked-list.swift)
|
[✔️](typescript%2F0876-middle-of-the-linked-list.ts)
+[0160 - Intersection of Two Linked Lists](https://leetcode.com/problems/intersection-of-two-linked-lists/) |
|
[✔️](c%2F0160-intersection-of-two-linked-lists.c)
|
[✔️](cpp%2F0160-intersection-of-two-linked-lists.cpp)
|
|
|
[✔️](go%2F0160-intersection-of-two-linked-lists.go)
|
|
[✔️](java%2F0160-intersection-of-two-linked-lists.java)
|
[✔️](javascript%2F0160-intersection-of-two-linked-lists.js)
|
[✔️](kotlin%2F0160-intersection-of-two-linked-lists.kt)
|
[✔️](python%2F0160-intersection-of-two-linked-lists.py)
|
|
|
|
|
+[0143 - Reorder List](https://leetcode.com/problems/reorder-list/) |
|
[✔️](c%2F0143-reorder-list.c)
|
[✔️](cpp%2F0143-reorder-list.cpp)
|
[✔️](csharp%2F0143-reorder-list.cs)
|
|
[✔️](go%2F0143-reorder-list.go)
|
|
[✔️](java%2F0143-reorder-list.java)
|
[✔️](javascript%2F0143-reorder-list.js)
|
[✔️](kotlin%2F0143-reorder-list.kt)
|
[✔️](python%2F0143-reorder-list.py)
|
|
[✔️](rust%2F0143-reorder-list.rs)
|
|
[✔️](swift%2F0143-reorder-list.swift)
|
[✔️](typescript%2F0143-reorder-list.ts)
+[2130 - Maximum Twin Sum Of A Linked List](https://leetcode.com/problems/maximum-twin-sum-of-a-linked-list/) |
|
|
[✔️](cpp%2F2130-maximum-twin-sum-of-a-linked-list.cpp)
|
|
|
[✔️](go%2F2130-maximum-twin-sum-of-a-linked-list.go)
|
|
[✔️](java%2F2130-maximum-twin-sum-of-a-linked-list.java)
|
[✔️](javascript%2F2130-maximum-twin-sum-of-a-linked-list.js)
|
[✔️](kotlin%2F2130-maximum-twin-sum-of-a-linked-list.kt)
|
[✔️](python%2F2130-maximum-twin-sum-of-a-linked-list.py)
|
|
|
|
[✔️](swift%2F2130-maximum-twin-sum-of-a-linked-list.swift)
|
+[0019 - Remove Nth Node From End of List](https://leetcode.com/problems/remove-nth-node-from-end-of-list/) |
|
[✔️](c%2F0019-remove-nth-node-from-end-of-list.c)
|
[✔️](cpp%2F0019-remove-nth-node-from-end-of-list.cpp)
|
[✔️](csharp%2F0019-remove-nth-node-from-end-of-list.cs)
|
|
[✔️](go%2F0019-remove-nth-node-from-end-of-list.go)
|
|
[✔️](java%2F0019-remove-nth-node-from-end-of-list.java)
|
[✔️](javascript%2F0019-remove-nth-node-from-end-of-list.js)
|
[✔️](kotlin%2F0019-remove-nth-node-from-end-of-list.kt)
|
[✔️](python%2F0019-remove-nth-node-from-end-of-list.py)
|
|
[✔️](rust%2F0019-remove-nth-node-from-end-of-list.rs)
|
|
[✔️](swift%2F0019-remove-nth-node-from-end-of-list.swift)
|
[✔️](typescript%2F0019-remove-nth-node-from-end-of-list.ts)
+[1721 - Swapping Nodes in a Linked List](https://leetcode.com/problems/swapping-nodes-in-a-linked-list/) |
|
|
[✔️](cpp%2F1721-swapping-nodes-in-a-linked-list.cpp)
|
|
|
[✔️](go%2F1721-swapping-nodes-in-a-linked-list.go)
|
|
[✔️](java%2F1721-swapping-nodes-in-a-linked-list.java)
|
|
[✔️](kotlin%2F1721-swapping-nodes-in-a-linked-list.kt)
|
[✔️](python%2F1721-swapping-nodes-in-a-linked-list.py)
|
|
|
|
|
+[0460 - LFU Cache](https://leetcode.com/problems/lfu-cache/) |
|
|
|
|
|
|
|
[✔️](java%2F0460-lfu-cache.java)
|
[✔️](javascript%2F0460-lfu-cache.js)
|
[✔️](kotlin%2F0460-lfu-cache.kt)
|
|
|
|
|
|
+[0138 - Copy List With Random Pointer](https://leetcode.com/problems/copy-list-with-random-pointer/) |
|
[✔️](c%2F0138-copy-list-with-random-pointer.c)
|
[✔️](cpp%2F0138-copy-list-with-random-pointer.cpp)
|
[✔️](csharp%2F0138-copy-list-with-random-pointer.cs)
|
|
[✔️](go%2F0138-copy-list-with-random-pointer.go)
|
|
[✔️](java%2F0138-copy-list-with-random-pointer.java)
|
[✔️](javascript%2F0138-copy-list-with-random-pointer.js)
|
[✔️](kotlin%2F0138-copy-list-with-random-pointer.kt)
|
[✔️](python%2F0138-copy-list-with-random-pointer.py)
|
[✔️](ruby%2F0138-copy-list-with-random-pointer.rb)
|
|
|
[✔️](swift%2F0138-copy-list-with-random-pointer.swift)
|
[✔️](typescript%2F0138-copy-list-with-random-pointer.ts)
+[0707 - Design Linked List](https://leetcode.com/problems/design-linked-list/) |
|
[✔️](c%2F0707-design-linked-list.c)
|
|
|
|
[✔️](go%2F0707-design-linked-list.go)
|
|
[✔️](java%2F0707-design-linked-list.java)
|
[✔️](javascript%2F0707-design-linked-list.js)
|
[✔️](kotlin%2F0707-design-linked-list.kt)
|
[✔️](python%2F0707-design-linked-list.py)
|
|
|
|
[✔️](swift%2F0707-design-linked-list.swift)
|
+[1472 - Design Browser History](https://leetcode.com/problems/design-browser-history/) |
|
|
|
|
|
[✔️](go%2F1472-design-browser-history.go)
|
|
[✔️](java%2F1472-design-browser-history.java)
|
[✔️](javascript%2F1472-design-browser-history.js)
|
[✔️](kotlin%2F1472-design-browser-history.kt)
|
[✔️](python%2F1472-design-browser-history.py)
|
|
[✔️](rust%2F1472-design-browser-history.rs)
|
|
[✔️](swift%2F1472-design-browser-history.swift)
|
[✔️](typescript%2F1472-design-browser-history.ts)
+[0002 - Add Two Numbers](https://leetcode.com/problems/add-two-numbers/) |
|
[✔️](c%2F0002-add-two-numbers.c)
|
[✔️](cpp%2F0002-add-two-numbers.cpp)
|
[✔️](csharp%2F0002-add-two-numbers.cs)
|
[✔️](dart%2F0002-add-two-numbers.dart)
|
[✔️](go%2F0002-add-two-numbers.go)
|
|
[✔️](java%2F0002-add-two-numbers.java)
|
[✔️](javascript%2F0002-add-two-numbers.js)
|
[✔️](kotlin%2F0002-add-two-numbers.kt)
|
[✔️](python%2F0002-add-two-numbers.py)
|
[✔️](ruby%2F0002-add-two-numbers.rb)
|
[✔️](rust%2F0002-add-two-numbers.rs)
|
[✔️](scala%2F0002-add-two-numbers.scala)
|
[✔️](swift%2F0002-add-two-numbers.swift)
|
[✔️](typescript%2F0002-add-two-numbers.ts)
+[0141 - Linked List Cycle](https://leetcode.com/problems/linked-list-cycle/) |
|
[✔️](c%2F0141-linked-list-cycle.c)
|
[✔️](cpp%2F0141-linked-list-cycle.cpp)
|
[✔️](csharp%2F0141-linked-list-cycle.cs)
|
|
[✔️](go%2F0141-linked-list-cycle.go)
|
|
[✔️](java%2F0141-linked-list-cycle.java)
|
[✔️](javascript%2F0141-linked-list-cycle.js)
|
[✔️](kotlin%2F0141-linked-list-cycle.kt)
|
[✔️](python%2F0141-linked-list-cycle.py)
|
[✔️](ruby%2F0141-linked-list-cycle.rb)
|
|
[✔️](scala%2F0141-linked-list-cycle.scala)
|
[✔️](swift%2F0141-linked-list-cycle.swift)
|
[✔️](typescript%2F0141-linked-list-cycle.ts)
+[0287 - Find The Duplicate Number](https://leetcode.com/problems/find-the-duplicate-number/) |
|
[✔️](c%2F0287-find-the-duplicate-number.c)
|
[✔️](cpp%2F0287-find-the-duplicate-number.cpp)
|
[✔️](csharp%2F0287-find-the-duplicate-number.cs)
|
|
[✔️](go%2F0287-find-the-duplicate-number.go)
|
|
[✔️](java%2F0287-find-the-duplicate-number.java)
|
[✔️](javascript%2F0287-find-the-duplicate-number.js)
|
[✔️](kotlin%2F0287-find-the-duplicate-number.kt)
|
[✔️](python%2F0287-find-the-duplicate-number.py)
|
[✔️](ruby%2F0287-find-the-duplicate-number.rb)
|
[✔️](rust%2F0287-find-the-duplicate-number.rs)
|
|
[✔️](swift%2F0287-find-the-duplicate-number.swift)
|
[✔️](typescript%2F0287-find-the-duplicate-number.ts)
+[0024 - Swap Nodes In Pairs](https://leetcode.com/problems/swap-nodes-in-pairs/) |
|
[✔️](c%2F0024-swap-nodes-in-pairs.c)
|
[✔️](cpp%2F0024-swap-nodes-in-pairs.cpp)
|
|
|
[✔️](go%2F0024-swap-nodes-in-pairs.go)
|
|
[✔️](java%2F0024-swap-nodes-in-pairs.java)
|
|
[✔️](kotlin%2F0024-swap-nodes-in-pairs.kt)
|
[✔️](python%2F0024-swap-nodes-in-pairs.py)
|
|
|
|
|
+[0148 - Sort List](https://leetcode.com/problems/sort-list/) |
|
[✔️](c%2F0148-sort-list.c)
|
[✔️](cpp%2F0148-sort-list.cpp)
|
[✔️](csharp%2F0148-sort-list.cs)
|
|
|
|
[✔️](java%2F0148-sort-list.java)
|
|
[✔️](kotlin%2F0148-sort-list.kt)
|
[✔️](python%2F0148-sort-list.py)
|
|
|
|
|
+[0086 - Partition List](https://leetcode.com/problems/partition-list/) |
|
|
|
|
|
|
|
[✔️](java%2F0086-partition-list.java)
|
|
[✔️](kotlin%2F0086-partition-list.kt)
|
[✔️](python%2F0086-partition-list.py)
|
|
|
|
|
+[0061 - Rotate List](https://leetcode.com/problems/rotate-list/) |
|
|
[✔️](cpp%2F0061-rotate-list.cpp)
|
|
|
|
|
[✔️](java%2F0061-rotate-list.java)
|
|
[✔️](kotlin%2F0061-rotate-list.kt)
|
[✔️](python%2F0061-rotate-list.py)
|
|
|
|
|
+[0092 - Reverse Linked List II](https://leetcode.com/problems/reverse-linked-list-ii/) |
|
|
[✔️](cpp%2F0092-reverse-linked-list-ii.cpp)
|
|
|
|
|
[✔️](java%2F0092-reverse-linked-list-ii.java)
|
[✔️](javascript%2F0092-reverse-linked-list-ii.js)
|
[✔️](kotlin%2F0092-reverse-linked-list-ii.kt)
|
[✔️](python%2F0092-reverse-linked-list-ii.py)
|
|
|
|
|
+[0622 - Design Circular Queue](https://leetcode.com/problems/design-circular-queue/) |
|
|
|
|
|
[✔️](go%2F0622-design-circular-queue.go)
|
|
[✔️](java%2F0622-design-circular-queue.java)
|
|
[✔️](kotlin%2F0622-design-circular-queue.kt)
|
[✔️](python%2F0622-design-circular-queue.py)
|
|
|
|
|
+[0147 - Insertion Sort List](https://leetcode.com/problems/insertion-sort-list/) |
|
|
[✔️](cpp%2F0147-insertion-sort-list.cpp)
|
|
|
[✔️](go%2F0147-insertion-sort-list.go)
|
|
[✔️](java%2F0147-insertion-sort-list.java)
|
|
[✔️](kotlin%2F0147-insertion-sort-list.kt)
|
[✔️](python%2F0147-insertion-sort-list.py)
|
|
|
|
|
+[0725 - Split Linked List in Parts](https://leetcode.com/problems/split-linked-list-in-parts/) |
|
|
|
|
|
|
|
[✔️](java%2F0725-split-linked-list-in-parts.java)
|
|
[✔️](kotlin%2F0725-split-linked-list-in-parts.kt)
|
|
|
|
|
|
+[0146 - LRU Cache](https://leetcode.com/problems/lru-cache/) |
|
[✔️](c%2F0146-lru-cache.c)
|
[✔️](cpp%2F0146-lru-cache.cpp)
|
[✔️](csharp%2F0146-lru-cache.cs)
|
|
[✔️](go%2F0146-lru-cache.go)
|
|
[✔️](java%2F0146-lru-cache.java)
|
[✔️](javascript%2F0146-lru-cache.js)
|
[✔️](kotlin%2F0146-lru-cache.kt)
|
[✔️](python%2F0146-lru-cache.py)
|
[✔️](ruby%2F0146-lru-cache.rb)
|
|
|
[✔️](swift%2F0146-lru-cache.swift)
|
[✔️](typescript%2F0146-lru-cache.ts)
+[0023 - Merge K Sorted Lists](https://leetcode.com/problems/merge-k-sorted-lists/) |
|
[✔️](c%2F0023-merge-k-sorted-lists.c)
|
[✔️](cpp%2F0023-merge-k-sorted-lists.cpp)
|
[✔️](csharp%2F0023-merge-k-sorted-lists.cs)
|
|
[✔️](go%2F0023-merge-k-sorted-lists.go)
|
|
[✔️](java%2F0023-merge-k-sorted-lists.java)
|
[✔️](javascript%2F0023-merge-k-sorted-lists.js)
|
[✔️](kotlin%2F0023-merge-k-sorted-lists.kt)
|
[✔️](python%2F0023-merge-k-sorted-lists.py)
|
|
[✔️](rust%2F0023-merge-k-sorted-lists.rs)
|
|
[✔️](swift%2F0023-merge-k-sorted-lists.swift)
|
[✔️](typescript%2F0023-merge-k-sorted-lists.ts)
+[0025 - Reverse Nodes In K Group](https://leetcode.com/problems/reverse-nodes-in-k-group/) |
|
[✔️](c%2F0025-reverse-nodes-in-k-group.c)
|
[✔️](cpp%2F0025-reverse-nodes-in-k-group.cpp)
|
[✔️](csharp%2F0025-reverse-nodes-in-k-group.cs)
|
|
[✔️](go%2F0025-reverse-nodes-in-k-group.go)
|
|
[✔️](java%2F0025-reverse-nodes-in-k-group.java)
|
[✔️](javascript%2F0025-reverse-nodes-in-k-group.js)
|
[✔️](kotlin%2F0025-reverse-nodes-in-k-group.kt)
|
[✔️](python%2F0025-reverse-nodes-in-k-group.py)
|
|
[✔️](rust%2F0025-reverse-nodes-in-k-group.rs)
|
|
[✔️](swift%2F0025-reverse-nodes-in-k-group.swift)
|
[✔️](typescript%2F0025-reverse-nodes-in-k-group.ts)
+ +### Trees + +Problem | articles | C | C++ | C# | Dart | GO | hints | Java | JS | Kotlin | Python | Ruby | Rust | Scala | Swift | TS +---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- +[0094 - Binary Tree Inorder Traversal](https://leetcode.com/problems/binary-tree-inorder-traversal/) |
|
[✔️](c%2F0094-binary-tree-inorder-traversal.c)
|
[✔️](cpp%2F0094-binary-tree-inorder-traversal.cpp)
|
[✔️](csharp%2F0094-binary-tree-inorder-traversal.cs)
|
|
[✔️](go%2F0094-binary-tree-inorder-traversal.go)
|
|
[✔️](java%2F0094-binary-tree-inorder-traversal.java)
|
[✔️](javascript%2F0094-binary-tree-inorder-traversal.js)
|
[✔️](kotlin%2F0094-binary-tree-inorder-traversal.kt)
|
[✔️](python%2F0094-binary-tree-inorder-traversal.py)
|
[✔️](ruby%2F0094-binary-tree-inorder-traversal.rb)
|
[✔️](rust%2F0094-binary-tree-inorder-traversal.rs)
|
|
[✔️](swift%2F0094-binary-tree-inorder-traversal.swift)
|
[✔️](typescript%2F0094-binary-tree-inorder-traversal.ts)
+[0144 - Binary Tree Preorder Traversal](https://leetcode.com/problems/binary-tree-preorder-traversal/) |
|
|
[✔️](cpp%2F0144-binary-tree-preorder-traversal.cpp)
|
[✔️](csharp%2F0144-binary-tree-preorder-traversal.cs)
|
|
|
|
[✔️](java%2F0144-binary-tree-preorder-traversal.java)
|
[✔️](javascript%2F0144-binary-tree-preorder-traversal.js)
|
[✔️](kotlin%2F0144-binary-tree-preorder-traversal.kt)
|
[✔️](python%2F0144-binary-tree-preorder-traversal.py)
|
|
|
|
[✔️](swift%2F0144-binary-tree-preorder-traversal.swift)
|
[✔️](typescript%2F0144-binary-tree-preorder-traversal.ts)
+[0145 - Binary Tree Postorder Traversal](https://leetcode.com/problems/binary-tree-postorder-traversal/) |
|
|
[✔️](cpp%2F0145-binary-tree-postorder-traversal.cpp)
|
[✔️](csharp%2F0145-binary-tree-postorder-traversal.cs)
|
|
|
|
[✔️](java%2F0145-binary-tree-postorder-traversal.java)
|
[✔️](javascript%2F0145-binary-tree-postorder-traversal.js)
|
[✔️](kotlin%2F0145-binary-tree-postorder-traversal.kt)
|
[✔️](python%2F0145-binary-tree-postorder-traversal.py)
|
|
|
|
[✔️](swift%2F0145-binary-tree-postorder-traversal.swift)
|
[✔️](typescript%2F0145-binary-tree-postorder-traversal.ts)
+[0226 - Invert Binary Tree](https://leetcode.com/problems/invert-binary-tree/) |
|
[✔️](c%2F0226-invert-binary-tree.c)
|
[✔️](cpp%2F0226-invert-binary-tree.cpp)
|
[✔️](csharp%2F0226-invert-binary-tree.cs)
|
[✔️](dart%2F0226-invert-binary-tree.dart)
|
[✔️](go%2F0226-invert-binary-tree.go)
|
|
[✔️](java%2F0226-invert-binary-tree.java)
|
[✔️](javascript%2F0226-invert-binary-tree.js)
|
[✔️](kotlin%2F0226-invert-binary-tree.kt)
|
[✔️](python%2F0226-invert-binary-tree.py)
|
[✔️](ruby%2F0226-invert-binary-tree.rb)
|
[✔️](rust%2F0226-invert-binary-tree.rs)
|
|
[✔️](swift%2F0226-invert-binary-tree.swift)
|
[✔️](typescript%2F0226-invert-binary-tree.ts)
+[0104 - Maximum Depth of Binary Tree](https://leetcode.com/problems/maximum-depth-of-binary-tree/) |
|
[✔️](c%2F0104-maximum-depth-of-binary-tree.c)
|
[✔️](cpp%2F0104-maximum-depth-of-binary-tree.cpp)
|
[✔️](csharp%2F0104-maximum-depth-of-binary-tree.cs)
|
|
[✔️](go%2F0104-maximum-depth-of-binary-tree.go)
|
|
[✔️](java%2F0104-maximum-depth-of-binary-tree.java)
|
[✔️](javascript%2F0104-maximum-depth-of-binary-tree.js)
|
[✔️](kotlin%2F0104-maximum-depth-of-binary-tree.kt)
|
[✔️](python%2F0104-maximum-depth-of-binary-tree.py)
|
[✔️](ruby%2F0104-maximum-depth-of-binary-tree.rb)
|
[✔️](rust%2F0104-maximum-depth-of-binary-tree.rs)
|
|
[✔️](swift%2F0104-maximum-depth-of-binary-tree.swift)
|
[✔️](typescript%2F0104-maximum-depth-of-binary-tree.ts)
+[0543 - Diameter of Binary Tree](https://leetcode.com/problems/diameter-of-binary-tree/) |
|
[✔️](c%2F0543-diameter-of-binary-tree.c)
|
[✔️](cpp%2F0543-diameter-of-binary-tree.cpp)
|
[✔️](csharp%2F0543-diameter-of-binary-tree.cs)
|
|
[✔️](go%2F0543-diameter-of-binary-tree.go)
|
|
[✔️](java%2F0543-diameter-of-binary-tree.java)
|
[✔️](javascript%2F0543-diameter-of-binary-tree.js)
|
[✔️](kotlin%2F0543-diameter-of-binary-tree.kt)
|
[✔️](python%2F0543-diameter-of-binary-tree.py)
|
[✔️](ruby%2F0543-diameter-of-binary-tree.rb)
|
[✔️](rust%2F0543-diameter-of-binary-tree.rs)
|
|
[✔️](swift%2F0543-diameter-of-binary-tree.swift)
|
[✔️](typescript%2F0543-diameter-of-binary-tree.ts)
+[0110 - Balanced Binary Tree](https://leetcode.com/problems/balanced-binary-tree/) |
|
[✔️](c%2F0110-balanced-binary-tree.c)
|
[✔️](cpp%2F0110-balanced-binary-tree.cpp)
|
[✔️](csharp%2F0110-balanced-binary-tree.cs)
|
|
[✔️](go%2F0110-balanced-binary-tree.go)
|
|
[✔️](java%2F0110-balanced-binary-tree.java)
|
[✔️](javascript%2F0110-balanced-binary-tree.js)
|
[✔️](kotlin%2F0110-balanced-binary-tree.kt)
|
[✔️](python%2F0110-balanced-binary-tree.py)
|
[✔️](ruby%2F0110-balanced-binary-tree.rb)
|
[✔️](rust%2F0110-balanced-binary-tree.rs)
|
|
[✔️](swift%2F0110-balanced-binary-tree.swift)
|
[✔️](typescript%2F0110-balanced-binary-tree.ts)
+[0100 - Same Tree](https://leetcode.com/problems/same-tree/) |
|
[✔️](c%2F0100-same-tree.c)
|
[✔️](cpp%2F0100-same-tree.cpp)
|
[✔️](csharp%2F0100-same-tree.cs)
|
|
[✔️](go%2F0100-same-tree.go)
|
|
[✔️](java%2F0100-same-tree.java)
|
[✔️](javascript%2F0100-same-tree.js)
|
[✔️](kotlin%2F0100-same-tree.kt)
|
[✔️](python%2F0100-same-tree.py)
|
[✔️](ruby%2F0100-same-tree.rb)
|
[✔️](rust%2F0100-same-tree.rs)
|
|
[✔️](swift%2F0100-same-tree.swift)
|
[✔️](typescript%2F0100-same-tree.ts)
+[0572 - Subtree of Another Tree](https://leetcode.com/problems/subtree-of-another-tree/) |
|
[✔️](c%2F0572-subtree-of-another-tree.c)
|
[✔️](cpp%2F0572-subtree-of-another-tree.cpp)
|
[✔️](csharp%2F0572-subtree-of-another-tree.cs)
|
|
[✔️](go%2F0572-subtree-of-another-tree.go)
|
|
[✔️](java%2F0572-subtree-of-another-tree.java)
|
[✔️](javascript%2F0572-subtree-of-another-tree.js)
|
[✔️](kotlin%2F0572-subtree-of-another-tree.kt)
|
[✔️](python%2F0572-subtree-of-another-tree.py)
|
[✔️](ruby%2F0572-subtree-of-another-tree.rb)
|
[✔️](rust%2F0572-subtree-of-another-tree.rs)
|
[✔️](scala%2F0572-subtree-of-another-tree.scala)
|
[✔️](swift%2F0572-subtree-of-another-tree.swift)
|
[✔️](typescript%2F0572-subtree-of-another-tree.ts)
+[0108 - Convert Sorted Array to Binary Search Tree](https://leetcode.com/problems/convert-sorted-array-to-binary-search-tree/) |
|
[✔️](c%2F0108-convert-sorted-array-to-binary-search-tree.c)
|
|
|
|
[✔️](go%2F0108-convert-sorted-array-to-binary-search-tree.go)
|
|
[✔️](java%2F0108-convert-sorted-array-to-binary-search-tree.java)
|
[✔️](javascript%2F0108-convert-sorted-array-to-binary-search-tree.js)
|
[✔️](kotlin%2F0108-convert-sorted-array-to-binary-search-tree.kt)
|
[✔️](python%2F0108-convert-sorted-array-to-binary-search-tree.py)
|
|
|
|
|
+[0617 - Merge Two Binary Trees](https://leetcode.com/problems/merge-two-binary-trees/) |
|
[✔️](c%2F0617-merge-two-binary-trees.c)
|
[✔️](cpp%2F0617-merge-two-binary-trees.cpp)
|
[✔️](csharp%2F0617-merge-two-binary-trees.cs)
|
[✔️](dart%2F0617-merge-two-binary-trees.dart)
|
[✔️](go%2F0617-merge-two-binary-trees.go)
|
|
[✔️](java%2F0617-merge-two-binary-trees.java)
|
[✔️](javascript%2F0617-merge-two-binary-trees.js)
|
[✔️](kotlin%2F0617-merge-two-binary-trees.kt)
|
[✔️](python%2F0617-merge-two-binary-trees.py)
|
|
|
|
|
+[0112 - Path Sum](https://leetcode.com/problems/path-sum/) |
|
[✔️](c%2F0112-path-sum.c)
|
[✔️](cpp%2F0112-path-sum.cpp)
|
[✔️](csharp%2F0112-path-sum.cs)
|
|
[✔️](go%2F0112-path-sum.go)
|
|
[✔️](java%2F0112-path-sum.java)
|
[✔️](javascript%2F0112-path-sum.js)
|
[✔️](kotlin%2F0112-path-sum.kt)
|
[✔️](python%2F0112-path-sum.py)
|
|
|
|
[✔️](swift%2F0112-path-sum.swift)
|
+[0606 - Construct String From Binary Tree](https://leetcode.com/problems/construct-string-from-binary-tree/) |
|
|
[✔️](cpp%2F0606-construct-string-from-binary-tree.cpp)
|
|
|
|
|
[✔️](java%2F0606-construct-string-from-binary-tree.java)
|
[✔️](javascript%2F0606-construct-string-from-binary-tree.js)
|
[✔️](kotlin%2F0606-construct-string-from-binary-tree.kt)
|
[✔️](python%2F0606-construct-string-from-binary-tree.py)
|
|
|
|
|
+[0235 - Lowest Common Ancestor of a Binary Search Tree](https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/) |
|
[✔️](c%2F0235-lowest-common-ancestor-of-a-binary-search-tree.c)
|
[✔️](cpp%2F0235-lowest-common-ancestor-of-a-binary-search-tree.cpp)
|
[✔️](csharp%2F0235-lowest-common-ancestor-of-a-binary-search-tree.cs)
|
|
[✔️](go%2F0235-lowest-common-ancestor-of-a-binary-search-tree.go)
|
|
[✔️](java%2F0235-lowest-common-ancestor-of-a-binary-search-tree.java)
|
[✔️](javascript%2F0235-lowest-common-ancestor-of-a-binary-search-tree.js)
|
[✔️](kotlin%2F0235-lowest-common-ancestor-of-a-binary-search-tree.kt)
|
[✔️](python%2F0235-lowest-common-ancestor-of-a-binary-search-tree.py)
|
[✔️](ruby%2F0235-lowest-common-ancestor-of-a-binary-search-tree.rb)
|
[✔️](rust%2F0235-lowest-common-ancestor-of-a-binary-search-tree.rs)
|
[✔️](scala%2F0235-lowest-common-ancestor-of-a-binary-search-tree.scala)
|
[✔️](swift%2F0235-lowest-common-ancestor-of-a-binary-search-tree.swift)
|
[✔️](typescript%2F0235-lowest-common-ancestor-of-a-binary-search-tree.ts)
+[0701 - Insert into a Binary Search Tree](https://leetcode.com/problems/insert-into-a-binary-search-tree/) |
|
|
[✔️](cpp%2F0701-insert-into-a-binary-search-tree.cpp)
|
[✔️](csharp%2F0701-insert-into-a-binary-search-tree.cs)
|
|
|
|
[✔️](java%2F0701-insert-into-a-binary-search-tree.java)
|
[✔️](javascript%2F0701-insert-into-a-binary-search-tree.js)
|
[✔️](kotlin%2F0701-insert-into-a-binary-search-tree.kt)
|
[✔️](python%2F0701-insert-into-a-binary-search-tree.py)
|
|
|
|
[✔️](swift%2F0701-insert-into-a-binary-search-tree.swift)
|
[✔️](typescript%2F0701-insert-into-a-binary-search-tree.ts)
+[0450 - Delete Node in a BST](https://leetcode.com/problems/delete-node-in-a-bst/) |
|
|
[✔️](cpp%2F0450-delete-node-in-a-bst.cpp)
|
|
|
|
|
[✔️](java%2F0450-delete-node-in-a-bst.java)
|
[✔️](javascript%2F0450-delete-node-in-a-bst.js)
|
[✔️](kotlin%2F0450-delete-node-in-a-bst.kt)
|
[✔️](python%2F0450-delete-node-in-a-bst.py)
|
|
[✔️](rust%2F0450-delete-node-in-a-bst.rs)
|
|
[✔️](swift%2F0450-delete-node-in-a-bst.swift)
|
[✔️](typescript%2F0450-delete-node-in-a-bst.ts)
+[0102 - Binary Tree Level Order Traversal](https://leetcode.com/problems/binary-tree-level-order-traversal/) |
|
[✔️](c%2F0102-binary-tree-level-order-traversal.c)
|
[✔️](cpp%2F0102-binary-tree-level-order-traversal.cpp)
|
[✔️](csharp%2F0102-binary-tree-level-order-traversal.cs)
|
|
[✔️](go%2F0102-binary-tree-level-order-traversal.go)
|
|
[✔️](java%2F0102-binary-tree-level-order-traversal.java)
|
[✔️](javascript%2F0102-binary-tree-level-order-traversal.js)
|
[✔️](kotlin%2F0102-binary-tree-level-order-traversal.kt)
|
[✔️](python%2F0102-binary-tree-level-order-traversal.py)
|
[✔️](ruby%2F0102-binary-tree-level-order-traversal.rb)
|
[✔️](rust%2F0102-binary-tree-level-order-traversal.rs)
|
|
[✔️](swift%2F0102-binary-tree-level-order-traversal.swift)
|
[✔️](typescript%2F0102-binary-tree-level-order-traversal.ts)
+[0199 - Binary Tree Right Side View](https://leetcode.com/problems/binary-tree-right-side-view/) |
|
[✔️](c%2F0199-binary-tree-right-side-view.c)
|
[✔️](cpp%2F0199-binary-tree-right-side-view.cpp)
|
[✔️](csharp%2F0199-binary-tree-right-side-view.cs)
|
|
[✔️](go%2F0199-binary-tree-right-side-view.go)
|
|
[✔️](java%2F0199-binary-tree-right-side-view.java)
|
[✔️](javascript%2F0199-binary-tree-right-side-view.js)
|
[✔️](kotlin%2F0199-binary-tree-right-side-view.kt)
|
[✔️](python%2F0199-binary-tree-right-side-view.py)
|
|
[✔️](rust%2F0199-binary-tree-right-side-view.rs)
|
|
[✔️](swift%2F0199-binary-tree-right-side-view.swift)
|
[✔️](typescript%2F0199-binary-tree-right-side-view.ts)
+[0783 - Minimum Distance between BST Nodes](https://leetcode.com/problems/minimum-distance-between-bst-nodes/) |
|
|
|
|
|
|
|
[✔️](java%2F0783-minimum-distance-between-bst-nodes.java)
|
[✔️](javascript%2F0783-minimum-distance-between-bst-nodes.js)
|
[✔️](kotlin%2F0783-minimum-distance-between-bst-nodes.kt)
|
[✔️](python%2F0783-minimum-distance-between-bst-nodes.py)
|
|
|
|
|
+[0101 - Symmetric Tree ](https://leetcode.com/problems/symmetric-tree/) |
|
|
[✔️](cpp%2F0101-symmetric-tree.cpp)
|
|
|
|
|
[✔️](java%2F0101-symmetric-tree.java)
|
[✔️](javascript%2F0101-symmetric-tree.js)
|
[✔️](kotlin%2F0101-symmetric-tree.kt)
|
[✔️](python%2F0101-symmetric-tree.py)
|
|
[✔️](rust%2F0101-symmetric-tree.rs)
|
|
|
+[1443 - Minimum Time to Collect All Apples in a Tree](https://leetcode.com/problems/minimum-time-to-collect-all-apples-in-a-tree/) |
|
|
|
|
|
|
|
|
[✔️](javascript%2F1443-minimum-time-to-collect-all-apples-in-a-tree.js)
|
[✔️](kotlin%2F1443-minimum-time-to-collect-all-apples-in-a-tree.kt)
|
|
|
[✔️](rust%2F1443-minimum-time-to-collect-all-apples-in-a-tree.rs)
|
|
|
+[0103 - Binary Tree Zigzag Level Order Traversal](https://leetcode.com/problems/binary-tree-zigzag-level-order-traversal/) |
|
|
[✔️](cpp%2F0103-binary-tree-zigzag-level-order-traversal.cpp)
|
|
|
|
|
[✔️](java%2F0103-binary-tree-zigzag-level-order-traversal.java)
|
[✔️](javascript%2F0103-binary-tree-zigzag-level-order-traversal.js)
|
[✔️](kotlin%2F0103-binary-tree-zigzag-level-order-traversal.kt)
|
[✔️](python%2F0103-binary-tree-zigzag-level-order-traversal.py)
|
|
|
|
|
+[0427 - Construct Quad Tree](https://leetcode.com/problems/construct-quad-tree/) |
|
|
|
|
|
|
|
[✔️](java%2F0427-construct-quad-tree.java)
|
|
[✔️](kotlin%2F0427-construct-quad-tree.kt)
|
|
|
|
|
|
+[0652 - Find Duplicate Subtrees](https://leetcode.com/problems/find-duplicate-subtrees/) |
|
|
|
|
|
|
|
|
[✔️](javascript%2F0652-find-duplicate-subtrees.js)
|
[✔️](kotlin%2F0652-find-duplicate-subtrees.kt)
|
|
|
|
|
|
+[0958 - Check Completeness of a Binary Tree](https://leetcode.com/problems/check-completeness-of-a-binary-tree/) |
|
|
[✔️](cpp%2F0958-check-completeness-of-a-binary-tree.cpp)
|
[✔️](csharp%2F0958-check-completeness-of-a-binary-tree.cs)
|
|
[✔️](go%2F0958-check-completeness-of-a-binary-tree.go)
|
|
|
[✔️](javascript%2F0958-check-completeness-of-a-binary-tree.js)
|
[✔️](kotlin%2F0958-check-completeness-of-a-binary-tree.kt)
|
|
|
|
|
|
+[0106 - Construct Binary Tree from Inorder and Postorder Traversal](https://leetcode.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/) |
|
|
|
|
|
|
|
[✔️](java%2F0106-construct-binary-tree-from-inorder-and-postorder-traversal.java)
|
[✔️](javascript%2F0106-construct-binary-tree-from-inorder-and-postorder-traversal.js)
|
[✔️](kotlin%2F0106-construct-binary-tree-from-inorder-and-postorder-traversal.kt)
|
[✔️](python%2F0106-construct-binary-tree-from-inorder-and-postorder-traversal.py)
|
|
|
|
|
+[0662 - Maximum Width of Binary Tree ](https://leetcode.com/problems/maximum-width-of-binary-tree/) |
|
|
|
|
|
|
|
[✔️](java%2F0662-maximum-width-of-binary-tree.java)
|
|
[✔️](kotlin%2F0662-maximum-width-of-binary-tree.kt)
|
[✔️](python%2F0662-maximum-width-of-binary-tree.py)
|
|
|
|
|
+[1376 - Time Needed to Inform All Employees ](https://leetcode.com/problems/time-needed-to-inform-all-employees/) |
|
|
|
|
|
|
|
[✔️](java%2F1376-time-needed-to-inform-all-employees.java)
|
[✔️](javascript%2F1376-time-needed-to-inform-all-employees.js)
|
[✔️](kotlin%2F1376-time-needed-to-inform-all-employees.kt)
|
|
|
|
|
|
+[1448 - Count Good Nodes In Binary Tree](https://leetcode.com/problems/count-good-nodes-in-binary-tree/) |
|
[✔️](c%2F1448-Count-Good-Nodes-in-Binary-Tree.c)
|
[✔️](cpp%2F1448-Count-Good-Nodes-In-Binary-Tree.cpp)
|
[✔️](csharp%2F1448-Count-Good-Nodes-in-Binary-Tree.cs)
|
|
[✔️](go%2F1448-count-good-nodes-in-binary-tree.go)
|
|
[✔️](java%2F1448-Count-Good-Nodes-in-Binary-Tree.java)
|
[✔️](javascript%2F1448-Count-Good-Nodes-in-Binary-Tree.js)
|
[✔️](kotlin%2F1448-Count-Good-Nodes-In-Binary-Tree.kt)
|
[✔️](python%2F1448-count-good-nodes-in-binary-tree.py)
|
|
[✔️](rust%2F1448-count-good-nodes-in-binary-tree.rs)
|
|
[✔️](swift%2F1448-count-good-nodes-in-binary-tree.swift)
|
[✔️](typescript%2F1448-Count-Good-Nodes-in-Binary-Tree.ts)
+[0098 - Validate Binary Search Tree](https://leetcode.com/problems/validate-binary-search-tree/) |
|
[✔️](c%2F0098-validate-binary-search-tree.c)
|
[✔️](cpp%2F0098-validate-binary-search-tree.cpp)
|
[✔️](csharp%2F0098-validate-binary-search-tree.cs)
|
|
[✔️](go%2F0098-validate-binary-search-tree.go)
|
|
[✔️](java%2F0098-validate-binary-search-tree.java)
|
[✔️](javascript%2F0098-validate-binary-search-tree.js)
|
[✔️](kotlin%2F0098-validate-binary-search-tree.kt)
|
[✔️](python%2F0098-validate-binary-search-tree.py)
|
|
[✔️](rust%2F0098-validate-binary-search-tree.rs)
|
|
[✔️](swift%2F0098-validate-binary-search-tree.swift)
|
[✔️](typescript%2F0098-validate-binary-search-tree.ts)
+[0230 - Kth Smallest Element In a Bst](https://leetcode.com/problems/kth-smallest-element-in-a-bst/) |
|
[✔️](c%2F0230-kth-smallest-element-in-a-bst.c)
|
[✔️](cpp%2F0230-kth-smallest-element-in-a-bst.cpp)
|
[✔️](csharp%2F0230-kth-smallest-element-in-a-bst.cs)
|
|
[✔️](go%2F0230-kth-smallest-element-in-a-bst.go)
|
|
[✔️](java%2F0230-kth-smallest-element-in-a-bst.java)
|
[✔️](javascript%2F0230-kth-smallest-element-in-a-bst.js)
|
[✔️](kotlin%2F0230-kth-smallest-element-in-a-bst.kt)
|
[✔️](python%2F0230-kth-smallest-element-in-a-bst.py)
|
|
[✔️](rust%2F0230-kth-smallest-element-in-a-bst.rs)
|
[✔️](scala%2F0230-kth-smallest-element-in-a-bst.scala)
|
[✔️](swift%2F0230-kth-smallest-element-in-a-bst.swift)
|
[✔️](typescript%2F0230-kth-smallest-element-in-a-bst.ts)
+[0105 - Construct Binary Tree From Preorder And Inorder Traversal](https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/) |
|
[✔️](c%2F0105-construct-binary-tree-from-preorder-and-inorder-traversal.c)
|
[✔️](cpp%2F0105-construct-binary-tree-from-preorder-and-inorder-traversal.cpp)
|
[✔️](csharp%2F0105-construct-binary-tree-from-preorder-and-inorder-traversal.cs)
|
|
[✔️](go%2F0105-construct-binary-tree-from-preorder-and-inorder-traversal.go)
|
|
[✔️](java%2F0105-construct-binary-tree-from-preorder-and-inorder-traversal.java)
|
[✔️](javascript%2F0105-construct-binary-tree-from-preorder-and-inorder-traversal.js)
|
[✔️](kotlin%2F0105-construct-binary-tree-from-preorder-and-inorder-traversal.kt)
|
[✔️](python%2F0105-construct-binary-tree-from-preorder-and-inorder-traversal.py)
|
|
|
|
[✔️](swift%2F0105-construct-binary-tree-from-preorder-and-inorder-traversal.swift)
|
[✔️](typescript%2F0105-construct-binary-tree-from-preorder-and-inorder-traversal.ts)
+[0096 - Unique Binary Search Trees](https://leetcode.com/problems/unique-binary-search-trees/) |
|
[✔️](c%2F0096-unique-binary-search-trees.c)
|
|
|
|
|
|
[✔️](java%2F0096-unique-binary-search-trees.java)
|
[✔️](javascript%2F0096-unique-binary-search-trees.js)
|
[✔️](kotlin%2F0096-unique-binary-search-trees.kt)
|
|
|
|
|
|
+[0095 - Unique Binary Search Trees II](https://leetcode.com/problems/unique-binary-search-trees-ii/) |
|
|
|
|
|
|
|
[✔️](java%2F0095-unique-binary-search-trees-ii.java)
|
[✔️](javascript%2F0095-unique-binary-search-trees-ii.js)
|
[✔️](kotlin%2F0095-unique-binary-search-trees-ii.kt)
|
|
|
|
|
|
+[0129 - Sum Root to Leaf Numbers](https://leetcode.com/problems/sum-root-to-leaf-numbers/) |
|
[✔️](c%2F0129-sum-root-to-leaf-numbers.c)
|
[✔️](cpp%2F0129-sum-root-to-leaf-numbers.cpp)
|
|
|
[✔️](go%2F0129-sum-root-to-leaf-numbers.go)
|
|
[✔️](java%2F0129-sum-root-to-leaf-numbers.java)
|
[✔️](javascript%2F0129-sum-root-to-leaf-numbers.js)
|
[✔️](kotlin%2F0129-sum-root-to-leaf-numbers.kt)
|
|
|
|
|
|
+[0337 - House Robber III](https://leetcode.com/problems/house-robber-iii/) |
|
|
|
|
|
|
|
[✔️](java%2F0337-house-robber-iii.java)
|
[✔️](javascript%2F0337-house-robber-iii.js)
|
[✔️](kotlin%2F0337-house-robber-iii.kt)
|
|
|
|
|
|
+[0951 - Flip Equivalent Binary Trees](https://leetcode.com/problems/flip-equivalent-binary-trees/) |
|
|
|
|
|
|
|
[✔️](java%2F0951-flip-equivalent-binary-trees.java)
|
[✔️](javascript%2F0951-flip-equivalent-binary-trees.js)
|
[✔️](kotlin%2F0951-flip-equivalent-binary-trees.kt)
|
|
|
|
|
|
+[1993 - Operations On Tree](https://leetcode.com/problems/operations-on-tree/) |
|
|
|
|
|
|
|
[✔️](java%2F1993-operations-on-tree.java)
|
[✔️](javascript%2F1993-operations-on-tree.js)
|
[✔️](kotlin%2F1993-operations-on-tree.kt)
|
|
|
|
|
|
+[0894 - All Possible Full Binary Trees](https://leetcode.com/problems/all-possible-full-binary-trees/) |
|
|
|
|
|
|
|
[✔️](java%2F0894-all-possible-full-binary-trees.java)
|
[✔️](javascript%2F0894-all-possible-full-binary-trees.js)
|
[✔️](kotlin%2F0894-all-possible-full-binary-trees.kt)
|
[✔️](python%2F0894-all-possible-full-binary-trees.py)
|
|
|
|
|
+[0513 - Find Bottom Left Tree Value](https://leetcode.com/problems/find-bottom-left-tree-value/) |
|
|
|
|
|
|
|
[✔️](java%2F0513-find-bottom-left-tree-value.java)
|
[✔️](javascript%2F0513-find-bottom-left-tree-value.js)
|
[✔️](kotlin%2F0513-find-bottom-left-tree-value.kt)
|
[✔️](python%2F0513-find-bottom-left-tree-value.py)
|
|
|
|
|
+[0669 - Trim a Binary Search Tree](https://leetcode.com/problems/trim-a-binary-search-tree/) |
|
|
|
|
|
[✔️](go%2F0669-trim-a-binary-search-tree.go)
|
|
[✔️](java%2F0669-trim-a-binary-search-tree.java)
|
[✔️](javascript%2F0669-trim-a-binary-search-tree.js)
|
[✔️](kotlin%2F0669-trim-a-binary-search-tree.kt)
|
[✔️](python%2F0669-trim-a-binary-search-tree.py)
|
|
|
|
|
[✔️](typescript%2F0669-trim-a-binary-search-tree.ts)
+[0173 - Binary Search Tree Iterator](https://leetcode.com/problems/binary-search-tree-iterator/) |
|
|
|
|
|
|
|
[✔️](java%2F0173-binary-search-tree-iterator.java)
|
[✔️](javascript%2F0173-binary-search-tree-iterator.js)
|
[✔️](kotlin%2F0173-binary-search-tree-iterator.kt)
|
|
|
|
|
[✔️](swift%2F0173-binary-search-tree-iterator.swift)
|
+[0538 - Convert Bst to Greater Tree](https://leetcode.com/problems/convert-bst-to-greater-tree/) |
|
|
[✔️](cpp%2F0538-convert-bst-to-greater-tree.cpp)
|
|
|
|
|
[✔️](java%2F0538-convert-bst-to-greater-tree.java)
|
[✔️](javascript%2F0538-convert-bst-to-greater-tree.js)
|
[✔️](kotlin%2F0538-convert-bst-to-greater-tree.kt)
|
|
|
|
|
|
+[0124 - Binary Tree Maximum Path Sum](https://leetcode.com/problems/binary-tree-maximum-path-sum/) |
|
[✔️](c%2F0124-binary-tree-maximum-path-sum.c)
|
[✔️](cpp%2F0124-binary-tree-maximum-path-sum.cpp)
|
[✔️](csharp%2F0124-binary-tree-maximum-path-sum.cs)
|
|
[✔️](go%2F0124-binary-tree-maximum-path-sum.go)
|
|
[✔️](java%2F0124-binary-tree-maximum-path-sum.java)
|
[✔️](javascript%2F0124-binary-tree-maximum-path-sum.js)
|
[✔️](kotlin%2F0124-binary-tree-maximum-path-sum.kt)
|
[✔️](python%2F0124-binary-tree-maximum-path-sum.py)
|
|
[✔️](rust%2F0124-binary-tree-maximum-path-sum.rs)
|
|
[✔️](swift%2F0124-binary-tree-maximum-path-sum.swift)
|
[✔️](typescript%2F0124-binary-tree-maximum-path-sum.ts)
+[0297 - Serialize And Deserialize Binary Tree](https://leetcode.com/problems/serialize-and-deserialize-binary-tree/) |
|
[✔️](c%2F0297-serialize-and-deserialize-binary-tree.c)
|
[✔️](cpp%2F0297-serialize-and-deserialize-binary-tree.cpp)
|
[✔️](csharp%2F0297-serialize-and-deserialize-binary-tree.cs)
|
|
[✔️](go%2F0297-serialize-and-deserialize-binary-tree.go)
|
|
[✔️](java%2F0297-serialize-and-deserialize-binary-tree.java)
|
[✔️](javascript%2F0297-serialize-and-deserialize-binary-tree.js)
|
[✔️](kotlin%2F0297-serialize-and-deserialize-binary-tree.kt)
|
[✔️](python%2F0297-serialize-and-deserialize-binary-tree.py)
|
|
|
|
[✔️](swift%2F0297-serialize-and-deserialize-binary-tree.swift)
|
[✔️](typescript%2F0297-serialize-and-deserialize-binary-tree.ts)
+ +### Tries + +Problem | articles | C | C++ | C# | Dart | GO | hints | Java | JS | Kotlin | Python | Ruby | Rust | Scala | Swift | TS +---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- +[0208 - Implement Trie Prefix Tree](https://leetcode.com/problems/implement-trie-prefix-tree/) |
|
[✔️](c%2F0208-implement-trie-prefix-tree.c)
|
[✔️](cpp%2F0208-implement-trie-prefix-tree.cpp)
|
[✔️](csharp%2F0208-implement-trie-prefix-tree.cs)
|
|
[✔️](go%2F0208-implement-trie-prefix-tree.go)
|
|
[✔️](java%2F0208-implement-trie-prefix-tree.java)
|
[✔️](javascript%2F0208-implement-trie-prefix-tree.js)
|
[✔️](kotlin%2F0208-implement-trie-prefix-tree.kt)
|
[✔️](python%2F0208-implement-trie-prefix-tree.py)
|
[✔️](ruby%2F0208-implement-trie-prefix-tree.rb)
|
[✔️](rust%2F0208-implement-trie-prefix-tree.rs)
|
[✔️](scala%2F0208-implement-trie-prefix-tree.scala)
|
[✔️](swift%2F0208-implement-trie-prefix-tree.swift)
|
[✔️](typescript%2F0208-implement-trie-prefix-tree.ts)
+[0211 - Design Add And Search Words Data Structure](https://leetcode.com/problems/design-add-and-search-words-data-structure/) |
|
[✔️](c%2F0211-design-add-and-search-words-data-structure.c)
|
[✔️](cpp%2F0211-design-add-and-search-words-data-structure.cpp)
|
[✔️](csharp%2F0211-design-add-and-search-words-data-structure.cs)
|
|
[✔️](go%2F0211-design-add-and-search-words-data-structure.go)
|
|
[✔️](java%2F0211-design-add-and-search-words-data-structure.java)
|
[✔️](javascript%2F0211-design-add-and-search-words-data-structure.js)
|
[✔️](kotlin%2F0211-design-add-and-search-words-data-structure.kt)
|
[✔️](python%2F0211-design-add-and-search-words-data-structure.py)
|
[✔️](ruby%2F0211-design-add-and-search-words-data-structure.rb)
|
[✔️](rust%2F0211-design-add-and-search-words-data-structure.rs)
|
[✔️](scala%2F0211-design-add-and-search-words-data-structure.scala)
|
[✔️](swift%2F0211-design-add-and-search-words-data-structure.swift)
|
[✔️](typescript%2F0211-design-add-and-search-words-data-structure.ts)
+[2707 - Extra Characters in a String](https://leetcode.com/problems/extra-characters-in-a-string/) |
|
|
[✔️](cpp%2F2707-extra-characters-in-a-string.cpp)
|
|
|
|
|
[✔️](java%2F2707-extra-characters-in-a-string.java)
|
|
|
|
|
|
|
|
+[0212 - Word Search II](https://leetcode.com/problems/word-search-ii/) |
|
[✔️](c%2F0212-word-search-ii.c)
|
[✔️](cpp%2F0212-word-search-ii.cpp)
|
[✔️](csharp%2F0212-word-search-ii.cs)
|
|
[✔️](go%2F0212-word-search-ii.go)
|
|
[✔️](java%2F0212-word-search-ii.java)
|
[✔️](javascript%2F0212-word-search-ii.js)
|
[✔️](kotlin%2F0212-word-search-ii.kt)
|
[✔️](python%2F0212-word-search-ii.py)
|
|
[✔️](rust%2F0212-word-search-ii.rs)
|
|
[✔️](swift%2F0212-word-search-ii.swift)
|
[✔️](typescript%2F0212-word-search-ii.ts)
+ +### Heap / Priority Queue + +Problem | articles | C | C++ | C# | Dart | GO | hints | Java | JS | Kotlin | Python | Ruby | Rust | Scala | Swift | TS +---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- +[0703 - Kth Largest Element In a Stream](https://leetcode.com/problems/kth-largest-element-in-a-stream/) |
|
[✔️](c%2F0703-kth-largest-element-in-a-stream.c)
|
[✔️](cpp%2F0703-kth-largest-element-in-a-stream.cpp)
|
[✔️](csharp%2F0703-kth-largest-element-in-a-stream.cs)
|
|
[✔️](go%2F0703-kth-largest-element-in-a-stream.go)
|
|
[✔️](java%2F0703-kth-largest-element-in-a-stream.java)
|
[✔️](javascript%2F0703-kth-largest-element-in-a-stream.js)
|
[✔️](kotlin%2F0703-kth-largest-element-in-a-stream.kt)
|
[✔️](python%2F0703-kth-largest-element-in-a-stream.py)
|
[✔️](ruby%2F0703-kth-largest-element-in-a-stream.rb)
|
[✔️](rust%2F0703-kth-largest-element-in-a-stream.rs)
|
|
[✔️](swift%2F0703-kth-largest-element-in-a-stream.swift)
|
[✔️](typescript%2F0703-kth-largest-element-in-a-stream.ts)
+[1046 - Last Stone Weight](https://leetcode.com/problems/last-stone-weight/) |
|
[✔️](c%2F1046-last-stone-weight.c)
|
[✔️](cpp%2F1046-Last-Stone-Weight.cpp)
|
[✔️](csharp%2F1046-Last-Stone-Weight.cs)
|
|
[✔️](go%2F1046-last-stone-weight.go)
|
|
[✔️](java%2F1046-Last-Stone-Weight.java)
|
[✔️](javascript%2F1046-Last-Stone-Weight.js)
|
[✔️](kotlin%2F1046-last-stone-weight.kt)
|
[✔️](python%2F1046-last-stone-weight.py)
|
[✔️](ruby%2F1046-Last-Stone-Weight.rb)
|
[✔️](rust%2F1046-last-stone-weight.rs)
|
|
[✔️](swift%2F1046-last-stone-weight.swift)
|
[✔️](typescript%2F1046-Last-Stone-Weight.ts)
+[0973 - K Closest Points to Origin](https://leetcode.com/problems/k-closest-points-to-origin/) |
|
[✔️](c%2F0973-k-closest-points-to-origin.c)
|
[✔️](cpp%2F0973-k-closest-points-to-origin.cpp)
|
[✔️](csharp%2F0973-k-closest-points-to-origin.cs)
|
|
[✔️](go%2F0973-k-closest-points-to-origin.go)
|
|
[✔️](java%2F0973-k-closest-points-to-origin.java)
|
[✔️](javascript%2F0973-k-closest-points-to-origin.js)
|
[✔️](kotlin%2F0973-k-closest-points-to-origin.kt)
|
[✔️](python%2F0973-k-closest-points-to-origin.py)
|
|
[✔️](rust%2F0973-k-closest-points-to-origin.rs)
|
|
[✔️](swift%2F0973-k-closest-points-to-origin.swift)
|
+[0215 - Kth Largest Element In An Array](https://leetcode.com/problems/kth-largest-element-in-an-array/) |
|
[✔️](c%2F0215-kth-largest-element-in-an-array.c)
|
[✔️](cpp%2F0215-kth-largest-element-in-an-array.cpp)
|
[✔️](csharp%2F0215-kth-largest-element-in-an-array.cs)
|
|
[✔️](go%2F0215-kth-largest-element-in-an-array.go)
|
|
[✔️](java%2F0215-kth-largest-element-in-an-array.java)
|
[✔️](javascript%2F0215-kth-largest-element-in-an-array.js)
|
[✔️](kotlin%2F0215-kth-largest-element-in-an-array.kt)
|
[✔️](python%2F0215-kth-largest-element-in-an-array.py)
|
|
[✔️](rust%2F0215-kth-largest-element-in-an-array.rs)
|
[✔️](scala%2F0215-kth-largest-element-in-an-array.scala)
|
[✔️](swift%2F0215-kth-largest-element-in-an-array.swift)
|
[✔️](typescript%2F0215-kth-largest-element-in-an-array.ts)
+[0621 - Task Scheduler](https://leetcode.com/problems/task-scheduler/) |
|
[✔️](c%2F0621-task-scheduler.c)
|
[✔️](cpp%2F0621-task-scheduler.cpp)
|
[✔️](csharp%2F0621-task-scheduler.cs)
|
|
|
|
[✔️](java%2F0621-task-scheduler.java)
|
[✔️](javascript%2F0621-task-scheduler.js)
|
[✔️](kotlin%2F0621-task-scheduler.kt)
|
[✔️](python%2F0621-task-scheduler.py)
|
|
[✔️](rust%2F0621-task-scheduler.rs)
|
|
[✔️](swift%2F0621-task-scheduler.swift)
|
[✔️](typescript%2F0621-task-scheduler.ts)
+[0355 - Design Twitter](https://leetcode.com/problems/design-twitter/) |
|
|
[✔️](cpp%2F0355-design-twitter.cpp)
|
[✔️](csharp%2F0355-design-twitter.cs)
|
|
[✔️](go%2F0355-design-twitter.go)
|
|
[✔️](java%2F0355-design-twitter.java)
|
[✔️](javascript%2F0355-design-twitter.js)
|
[✔️](kotlin%2F0355-design-twitter.kt)
|
[✔️](python%2F0355-design-twitter.py)
|
|
|
|
[✔️](swift%2F0355-design-twitter.swift)
|
+[1675 - Minimize Deviation in Array](https://leetcode.com/problems/minimize-deviation-in-array/) |
|
|
[✔️](cpp%2F1675-minimize-deviation-in-array.cpp)
|
|
|
|
|
|
|
[✔️](kotlin%2F1675-minimize-deviation-in-array.kt)
|
|
|
|
|
|
+[2542 - Maximum Subsequence Score](https://leetcode.com/problems/maximum-subsequence-score/) |
|
|
[✔️](cpp%2F2542-maximum-subsequence-score.cpp)
|
|
|
|
|
|
[✔️](javascript%2F2542-maximum-subsequence-score.js)
|
[✔️](kotlin%2F2542-maximum-subsequence-score.kt)
|
|
|
|
|
|
+[1834 - Single Threaded Cpu](https://leetcode.com/problems/single-threaded-cpu/) |
|
|
|
|
|
|
|
[✔️](java%2F1834-single-threaded-cpu.java)
|
[✔️](javascript%2F1834-single-threaded-cpu.js)
|
[✔️](kotlin%2F1834-single-threaded-cpu.kt)
|
[✔️](python%2F1834-single-threaded-cpu.py)
|
|
[✔️](rust%2F1834-single-threaded-cpu.rs)
|
|
|
+[1845 - Seat Reservation Manager](https://leetcode.com/problems/seat-reservation-manager/) |
|
|
[✔️](cpp%2F1845-seat-reservation-manager.cpp)
|
|
|
|
|
|
[✔️](javascript%2F1845-seat-reservation-manager.js)
|
[✔️](kotlin%2F1845-seat-reservation-manager.kt)
|
[✔️](python%2F1845-seat-reservation-manager.py)
|
|
|
|
|
+[1882 - Process Tasks Using Servers](https://leetcode.com/problems/process-tasks-using-servers/) |
|
|
|
|
|
|
|
[✔️](java%2F1882-process-tasks-using-servers.java)
|
[✔️](javascript%2F1882-process-tasks-using-servers.js)
|
[✔️](kotlin%2F1882-process-tasks-using-servers.kt)
|
|
|
|
|
|
+[1985 - Find The Kth Largest Integer In The Array](https://leetcode.com/problems/find-the-kth-largest-integer-in-the-array/) |
|
|
[✔️](cpp%2F1985-Find-The-Kth-Largest-Integer-In-The-Array.cpp)
|
|
|
[✔️](go%2F1985-find-the-kth-largest-integer-in-the-array.go)
|
|
[✔️](java%2F1985-Find-The-Kth-Largest-Integer-In-The-Array.java)
|
[✔️](javascript%2F1985-find-the-kth-largest-integer-in-the-array.js)
|
[✔️](kotlin%2F1985-find-the-kth-largest-integer-in-the-array.kt)
|
[✔️](python%2F1985-find-the-kth-largest-integer-in-the-array.py)
|
|
|
|
[✔️](swift%2F1985-Find-The-Kth-Largest-Integer-In-The-Array.swift)
|
+[0767 - Reorganize String](https://leetcode.com/problems/reorganize-string/) |
|
|
[✔️](cpp%2F0767-reorganize-string.cpp)
|
|
|
|
|
[✔️](java%2F0767-reorganize-string.java)
|
[✔️](javascript%2F0767-reorganize-string.js)
|
[✔️](kotlin%2F0767-reorganize-string.kt)
|
[✔️](python%2F0767-reorganize-string.py)
|
|
|
|
|
+[1405 - Longest Happy String](https://leetcode.com/problems/longest-happy-string/) |
|
|
|
|
|
|
|
[✔️](java%2F1405-longest-happy-string.java)
|
[✔️](javascript%2F1405-longest-happy-string.js)
|
[✔️](kotlin%2F1405-longest-happy-string.kt)
|
|
|
|
|
|
+[1094 - Car Pooling](https://leetcode.com/problems/car-pooling/) |
|
|
[✔️](cpp%2F1094-car-pooling.cpp)
|
[✔️](csharp%2F1094-Car-Pooling.cs)
|
|
|
|
[✔️](java%2F1094-car-pooling.java)
|
[✔️](javascript%2F1094-car-pooling.js)
|
[✔️](kotlin%2F1094-car-pooling.kt)
|
|
|
|
|
|
+[0295 - Find Median From Data Stream](https://leetcode.com/problems/find-median-from-data-stream/) |
|
|
[✔️](cpp%2F0295-find-median-from-data-stream.cpp)
|
[✔️](csharp%2F0295-find-median-from-data-stream.cs)
|
|
[✔️](go%2F0295-find-median-from-data-stream.go)
|
|
[✔️](java%2F0295-find-median-from-data-stream.java)
|
[✔️](javascript%2F0295-find-median-from-data-stream.js)
|
[✔️](kotlin%2F0295-find-median-from-data-stream.kt)
|
[✔️](python%2F0295-find-median-from-data-stream.py)
|
|
|
|
[✔️](swift%2F0295-find-median-from-data-stream.swift)
|
[✔️](typescript%2F0295-find-median-from-data-stream.ts)
+[1383 - Maximum Performance of a Team](https://leetcode.com/problems/maximum-performance-of-a-team/) |
|
|
|
[✔️](csharp%2F1383-Maximum-Performance-Of-A-Team.cs)
|
|
|
|
|
[✔️](javascript%2F1383-maximum-performance-of-a-team.js)
|
[✔️](kotlin%2F1383-maximum-performance-of-a-team.kt)
|
[✔️](python%2F1383-maximum-performance-of-a-team.py)
|
|
|
|
|
+[0502 - IPO](https://leetcode.com/problems/ipo/) |
|
|
|
|
|
|
|
[✔️](java%2F0502-ipo.java)
|
[✔️](javascript%2F0502-ipo.js)
|
[✔️](kotlin%2F0502-ipo.kt)
|
[✔️](python%2F0502-ipo.py)
|
|
[✔️](rust%2F0502-ipo.rs)
|
|
[✔️](swift%2F0502-ipo.swift)
|
[✔️](typescript%2F0502-ipo.ts)
+ +### Backtracking + +Problem | articles | C | C++ | C# | Dart | GO | hints | Java | JS | Kotlin | Python | Ruby | Rust | Scala | Swift | TS +---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- +[0078 - Subsets](https://leetcode.com/problems/subsets/) |
|
[✔️](c%2F0078-subsets.c)
|
[✔️](cpp%2F0078-subsets.cpp)
|
[✔️](csharp%2F0078-subsets.cs)
|
|
[✔️](go%2F0078-subsets.go)
|
|
[✔️](java%2F0078-subsets.java)
|
[✔️](javascript%2F0078-subsets.js)
|
[✔️](kotlin%2F0078-subsets.kt)
|
[✔️](python%2F0078-subsets.py)
|
[✔️](ruby%2F0078-subsets.rb)
|
[✔️](rust%2F0078-subsets.rs)
|
|
[✔️](swift%2F0078-subsets.swift)
|
[✔️](typescript%2F0078-subsets.ts)
+[0039 - Combination Sum](https://leetcode.com/problems/combination-sum/) |
|
[✔️](c%2F0039-combination-sum.c)
|
[✔️](cpp%2F0039-combination-sum.cpp)
|
[✔️](csharp%2F0039-combination-sum.cs)
|
|
[✔️](go%2F0039-combination-sum.go)
|
|
[✔️](java%2F0039-combination-sum.java)
|
[✔️](javascript%2F0039-combination-sum.js)
|
[✔️](kotlin%2F0039-combination-sum.kt)
|
[✔️](python%2F0039-combination-sum.py)
|
[✔️](ruby%2F0039-combination-sum.rb)
|
[✔️](rust%2F0039-combination-sum.rs)
|
|
[✔️](swift%2F0039-combination-sum.swift)
|
[✔️](typescript%2F0039-combination-sum.ts)
+[0077 - Combinations](https://leetcode.com/problems/combinations/) |
|
|
[✔️](cpp%2F0077-combinations.cpp)
|
[✔️](csharp%2F0077-combinations.cs)
|
|
[✔️](go%2F0077-combinations.go)
|
|
[✔️](java%2F0077-combinations.java)
|
|
[✔️](kotlin%2F0077-combinations.kt)
|
[✔️](python%2F0077-combinations.py)
|
|
|
|
[✔️](swift%2F0077-combinations.swift)
|
+[0046 - Permutations](https://leetcode.com/problems/permutations/) |
|
[✔️](c%2F0046-permutations.c)
|
[✔️](cpp%2F0046-permutations.cpp)
|
[✔️](csharp%2F0046-permutations.cs)
|
|
[✔️](go%2F0046-permutations.go)
|
|
[✔️](java%2F0046-permutations.java)
|
[✔️](javascript%2F0046-permutations.js)
|
[✔️](kotlin%2F0046-permutations.kt)
|
[✔️](python%2F0046-permutations.py)
|
[✔️](ruby%2F0046-permutations.rb)
|
[✔️](rust%2F0046-permutations.rs)
|
|
[✔️](swift%2F0046-permutations.swift)
|
[✔️](typescript%2F0046-permutations.ts)
+[0090 - Subsets II](https://leetcode.com/problems/subsets-ii/) |
|
[✔️](c%2F0090-subsets-ii.c)
|
[✔️](cpp%2F0090-subsets-ii.cpp)
|
[✔️](csharp%2F0090-subsets-ii.cs)
|
|
[✔️](go%2F0090-subsets-ii.go)
|
|
[✔️](java%2F0090-subsets-ii.java)
|
[✔️](javascript%2F0090-subsets-ii.js)
|
[✔️](kotlin%2F0090-subsets-ii.kt)
|
[✔️](python%2F0090-subsets-ii.py)
|
[✔️](ruby%2F0090-subsets-ii.rb)
|
[✔️](rust%2F0090-subsets-ii.rs)
|
|
[✔️](swift%2F0090-subsets-ii.swift)
|
[✔️](typescript%2F0090-subsets-ii.ts)
+[0040 - Combination Sum II](https://leetcode.com/problems/combination-sum-ii/) |
|
[✔️](c%2F0040-combination-sum-ii.c)
|
[✔️](cpp%2F0040-combination-sum-ii.cpp)
|
[✔️](csharp%2F0040-combination-sum-ii.cs)
|
|
[✔️](go%2F0040-combination-sum-ii.go)
|
|
[✔️](java%2F0040-combination-sum-ii.java)
|
[✔️](javascript%2F0040-combination-sum-ii.js)
|
[✔️](kotlin%2F0040-combination-sum-ii.kt)
|
[✔️](python%2F0040-combination-sum-ii.py)
|
[✔️](ruby%2F0040-combination-sum-ii.rb)
|
|
|
[✔️](swift%2F0040-combination-sum-ii.swift)
|
[✔️](typescript%2F0040-combination-sum-ii.ts)
+[0047 - Permutations II](https://leetcode.com/problems/permutations-ii/) |
|
|
|
[✔️](csharp%2F0047-permutations-ii.cs)
|
|
[✔️](go%2F0047-permutations-ii.go)
|
|
[✔️](java%2F0047-permutations-ii.java)
|
|
[✔️](kotlin%2F0047-permutations-ii.kt)
|
[✔️](python%2F0047-permutations-ii.py)
|
|
|
|
[✔️](swift%2F0047-permutations-ii.swift)
|
[✔️](typescript%2F0047-permutations-ii.ts)
+[0079 - Word Search](https://leetcode.com/problems/word-search/) |
|
[✔️](c%2F0079-word-search.c)
|
[✔️](cpp%2F0079-word-search.cpp)
|
[✔️](csharp%2F0079-word-search.cs)
|
|
[✔️](go%2F0079-word-search.go)
|
|
[✔️](java%2F0079-word-search.java)
|
[✔️](javascript%2F0079-word-search.js)
|
[✔️](kotlin%2F0079-word-search.kt)
|
[✔️](python%2F0079-word-search.py)
|
[✔️](ruby%2F0079-word-search.rb)
|
[✔️](rust%2F0079-word-search.rs)
|
|
[✔️](swift%2F0079-word-search.swift)
|
[✔️](typescript%2F0079-word-search.ts)
+[0131 - Palindrome Partitioning](https://leetcode.com/problems/palindrome-partitioning/) |
|
|
[✔️](cpp%2F0131-palindrome-partitioning.cpp)
|
[✔️](csharp%2F0131-palindrome-partitioning.cs)
|
|
[✔️](go%2F0131-palindrome-partitioning.go)
|
|
[✔️](java%2F0131-palindrome-partitioning.java)
|
[✔️](javascript%2F0131-palindrome-partitioning.js)
|
[✔️](kotlin%2F0131-palindrome-partitioning.kt)
|
[✔️](python%2F0131-palindrome-partitioning.py)
|
[✔️](ruby%2F0131-palindrome-partitioning.rb)
|
[✔️](rust%2F0131-palindrome-partitioning.rs)
|
|
[✔️](swift%2F0131-palindrome-partitioning.swift)
|
[✔️](typescript%2F0131-palindrome-partitioning.ts)
+[0093 - Restore IP Addresses](https://leetcode.com/problems/restore-ip-addresses/) |
|
|
|
[✔️](csharp%2F0093-restore-ip-addresses.cs)
|
|
[✔️](go%2F0093-restore-ip-addresses.go)
|
|
|
[✔️](javascript%2F0093-restore-ip-addresses.js)
|
[✔️](kotlin%2F0093-restore-ip-addresses.kt)
|
|
|
[✔️](rust%2F0093-restore-ip-addresses.rs)
|
|
|
[✔️](typescript%2F0093-restore-ip-addresses.ts)
+[0017 - Letter Combinations of a Phone Number](https://leetcode.com/problems/letter-combinations-of-a-phone-number/) |
|
|
[✔️](cpp%2F0017-letter-combinations-of-a-phone-number.cpp)
|
[✔️](csharp%2F0017-letter-combinations-of-a-phone-number.cs)
|
|
[✔️](go%2F0017-letter-combinations-of-a-phone-number.go)
|
|
[✔️](java%2F0017-letter-combinations-of-a-phone-number.java)
|
[✔️](javascript%2F0017-letter-combinations-of-a-phone-number.js)
|
[✔️](kotlin%2F0017-letter-combinations-of-a-phone-number.kt)
|
[✔️](python%2F0017-letter-combinations-of-a-phone-number.py)
|
[✔️](ruby%2F0017-letter-combinations-of-a-phone-number.rb)
|
[✔️](rust%2F0017-letter-combinations-of-a-phone-number.rs)
|
|
[✔️](swift%2F0017-letter-combinations-of-a-phone-number.swift)
|
[✔️](typescript%2F0017-letter-combinations-of-a-phone-number.ts)
+[0473 - Matchsticks to Square](https://leetcode.com/problems/matchsticks-to-square/) |
|
|
[✔️](cpp%2F0473-matchsticks-to-square.cpp)
|
|
|
|
|
[✔️](java%2F0473-matchsticks-to-square.java)
|
[✔️](javascript%2F0473-matchsticks-to-square.js)
|
[✔️](kotlin%2F0473-matchsticks-to-square.kt)
|
[✔️](python%2F0473-matchsticks-to-square.py)
|
|
|
|
|
+[1849 - Splitting a String Into Descending Consecutive Values](https://leetcode.com/problems/splitting-a-string-into-descending-consecutive-values/) |
|
|
[✔️](cpp%2F1849-splitting-a-string-into-descending-consecutive-values.cpp)
|
|
|
|
|
[✔️](java%2F1849-splitting-a-string-into-descending-consecutive-values.java)
|
|
[✔️](kotlin%2F1849-splitting-a-string-into-descending-consecutive-values.kt)
|
[✔️](python%2F1849-splitting-a-string-into-descending-consecutive-values.py)
|
|
|
|
|
+[1980 - Find Unique Binary String](https://leetcode.com/problems/find-unique-binary-string/) |
|
|
[✔️](cpp%2F1980-find-unique-binary-string.cpp)
|
|
|
|
|
[✔️](java%2F1980-find-unique-binary-string.java)
|
|
[✔️](kotlin%2F1980-find-unique-binary-string.kt)
|
[✔️](python%2F1980-find-unique-binary-string.py)
|
|
|
|
|
+[1239 - Maximum Length of a Concatenated String With Unique Characters](https://leetcode.com/problems/maximum-length-of-a-concatenated-string-with-unique-characters/) |
|
|
|
[✔️](csharp%2F1239-maximum-length-of-a-concatenated-string-with-unique-characters.cs)
|
|
|
|
|
|
[✔️](kotlin%2F1239-maximum-length-of-a-concatenated-string-with-unique-characters.kt)
|
[✔️](python%2F1239-maximum-length-of-a-concatenated-string-with-unique-characters.py)
|
|
|
|
|
+[0698 - Partition to K Equal Sum Subsets](https://leetcode.com/problems/partition-to-k-equal-sum-subsets/) |
|
|
|
|
|
|
|
[✔️](java%2F0698-partition-to-k-equal-sum-subsets.java)
|
|
[✔️](kotlin%2F0698-partition-to-k-equal-sum-subsets.kt)
|
[✔️](python%2F0698-partition-to-k-equal-sum-subsets.py)
|
|
|
|
|
+[0051 - N Queens](https://leetcode.com/problems/n-queens/) |
|
[✔️](c%2F0051-n-queens.c)
|
[✔️](cpp%2F0051-n-queens.cpp)
|
[✔️](csharp%2F0051-n-queens.cs)
|
|
[✔️](go%2F0051-n-queens.go)
|
|
[✔️](java%2F0051-n-queens.java)
|
[✔️](javascript%2F0051-n-queens.js)
|
[✔️](kotlin%2F0051-n-queens.kt)
|
[✔️](python%2F0051-n-queens.py)
|
|
[✔️](rust%2F0051-n-queens.rs)
|
|
[✔️](swift%2F0051-n-queens.swift)
|
[✔️](typescript%2F0051-n-queens.ts)
+[0052 - N Queens II](https://leetcode.com/problems/n-queens-ii/) |
|
[✔️](c%2F0052-n-queens-ii.c)
|
[✔️](cpp%2F0052-n-queens-ii.cpp)
|
[✔️](csharp%2F0052-n-queens-ii.cs)
|
|
|
|
[✔️](java%2F0052-n-queens-ii.java)
|
[✔️](javascript%2F0052-n-queens-ii.js)
|
[✔️](kotlin%2F0052-n-queens-ii.kt)
|
[✔️](python%2F0052-n-queens-ii.py)
|
|
|
|
|
+ +### Graphs + +Problem | articles | C | C++ | C# | Dart | GO | hints | Java | JS | Kotlin | Python | Ruby | Rust | Scala | Swift | TS +---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- +[0463 - Island Perimeter](https://leetcode.com/problems/island-perimeter/) |
|
[✔️](c%2F0463-island-perimeter.c)
|
[✔️](cpp%2F0463-island-perimeter.cpp)
|
[✔️](csharp%2F0463-island-perimeter.cs)
|
|
[✔️](go%2F0463-island-perimeter.go)
|
|
[✔️](java%2F0463-island-perimeter.java)
|
[✔️](javascript%2F0463-island-perimeter.js)
|
[✔️](kotlin%2F0463-island-perimeter.kt)
|
[✔️](python%2F0463-island-perimeter.py)
|
|
|
|
|
+[0953 - Verifying An Alien Dictionary](https://leetcode.com/problems/verifying-an-alien-dictionary/) |
|
[✔️](c%2F0953-verifying-an-alien-dictionary.c)
|
[✔️](cpp%2F0953-verifying-an-alien-dictionary.cpp)
|
|
|
[✔️](go%2F0953-verifying-an-alien-dictionary.go)
|
|
[✔️](java%2F0953-verifying-an-alien-dictionary.java)
|
[✔️](javascript%2F0953-verifying-an-alien-dictionary.js)
|
[✔️](kotlin%2F0953-verifying-an-alien-dictionary.kt)
|
[✔️](python%2F0953-verifying-an-alien-dictionary.py)
|
|
[✔️](rust%2F0953-verifying-an-alien-dictionary.rs)
|
|
|
+[0200 - Number of Islands](https://leetcode.com/problems/number-of-islands/) |
|
[✔️](c%2F0200-number-of-islands.c)
|
[✔️](cpp%2F0200-number-of-islands.cpp)
|
[✔️](csharp%2F0200-number-of-islands.cs)
|
|
[✔️](go%2F0200-number-of-islands.go)
|
|
[✔️](java%2F0200-number-of-islands.java)
|
[✔️](javascript%2F0200-number-of-islands.js)
|
[✔️](kotlin%2F0200-number-of-islands.kt)
|
[✔️](python%2F0200-number-of-islands.py)
|
[✔️](ruby%2F0200-number-of-islands.rb)
|
[✔️](rust%2F0200-number-of-islands.rs)
|
|
[✔️](swift%2F0200-number-of-islands.swift)
|
[✔️](typescript%2F0200-number-of-islands.ts)
+[0133 - Clone Graph](https://leetcode.com/problems/clone-graph/) |
|
[✔️](c%2F0133-clone-graph.c)
|
[✔️](cpp%2F0133-clone-graph.cpp)
|
[✔️](csharp%2F0133-clone-graph.cs)
|
|
[✔️](go%2F0133-clone-graph.go)
|
|
[✔️](java%2F0133-clone-graph.java)
|
[✔️](javascript%2F0133-clone-graph.js)
|
[✔️](kotlin%2F0133-clone-graph.kt)
|
[✔️](python%2F0133-clone-graph.py)
|
[✔️](ruby%2F0133-clone-graph.rb)
|
|
|
[✔️](swift%2F0133-clone-graph.swift)
|
[✔️](typescript%2F0133-clone-graph.ts)
+[0695 - Max Area of Island](https://leetcode.com/problems/max-area-of-island/) |
|
[✔️](c%2F0695-max-area-of-island.c)
|
[✔️](cpp%2F0695-max-area-of-island.cpp)
|
[✔️](csharp%2F0695-max-area-of-island.cs)
|
|
[✔️](go%2F0695-max-area-of-island.go)
|
|
[✔️](java%2F0695-max-area-of-island.java)
|
[✔️](javascript%2F0695-max-area-of-island.js)
|
[✔️](kotlin%2F0695-max-area-of-island.kt)
|
[✔️](python%2F0695-max-area-of-island.py)
|
|
[✔️](rust%2F0695-max-area-of-island.rs)
|
|
[✔️](swift%2F0695-max-area-of-island.swift)
|
[✔️](typescript%2F0695-max-area-of-island.ts)
+[1905 - Count Sub Islands](https://leetcode.com/problems/count-sub-islands/) |
|
[✔️](c%2F1905-Count-Sub-Islands.c)
|
[✔️](cpp%2F1905-count-sub-islands.cpp)
|
[✔️](csharp%2F1905-Count-Sub-Islands.cs)
|
|
[✔️](go%2F1905-count-sub-islands.go)
|
|
[✔️](java%2F1905-count-sub-islands.java)
|
[✔️](javascript%2F1905-count-sub-islands.js)
|
[✔️](kotlin%2F1905-count-sub-islands.kt)
|
[✔️](python%2F1905-count-sub-islands.py)
|
|
|
|
|
+[0417 - Pacific Atlantic Water Flow](https://leetcode.com/problems/pacific-atlantic-water-flow/) |
|
[✔️](c%2F0417-pacific-atlantic-water-flow.c)
|
[✔️](cpp%2F0417-pacific-atlantic-water-flow.cpp)
|
[✔️](csharp%2F0417-pacific-atlantic-water-flow.cs)
|
|
[✔️](go%2F0417-pacific-atlantic-water-flow.go)
|
|
[✔️](java%2F0417-pacific-atlantic-water-flow.java)
|
[✔️](javascript%2F0417-pacific-atlantic-water-flow.js)
|
[✔️](kotlin%2F0417-pacific-atlantic-water-flow.kt)
|
[✔️](python%2F0417-pacific-atlantic-water-flow.py)
|
|
[✔️](rust%2F0417-pacific-atlantic-water-flow.rs)
|
|
[✔️](swift%2F0417-pacific-atlantic-water-flow.swift)
|
[✔️](typescript%2F0417-pacific-atlantic-water-flow.ts)
+[0130 - Surrounded Regions](https://leetcode.com/problems/surrounded-regions/) |
|
[✔️](c%2F0130-surrounded-regions.c)
|
[✔️](cpp%2F0130-surrounded-regions.cpp)
|
[✔️](csharp%2F0130-surrounded-regions.cs)
|
[✔️](dart%2F0130-surrounded-regions.dart)
|
[✔️](go%2F0130-surrounded-regions.go)
|
|
[✔️](java%2F0130-surrounded-regions.java)
|
[✔️](javascript%2F0130-surrounded-regions.js)
|
[✔️](kotlin%2F0130-surrounded-regions.kt)
|
[✔️](python%2F0130-surrounded-regions.py)
|
|
|
|
[✔️](swift%2F0130-surrounded-regions.swift)
|
[✔️](typescript%2F0130-surrounded-regions.ts)
+[1466 - Reorder Routes to Make All Paths Lead to The City Zero](https://leetcode.com/problems/reorder-routes-to-make-all-paths-lead-to-the-city-zero/) |
|
|
[✔️](cpp%2F1466-reorder-routes-to-make-all-paths-lead-to-the-city-zero.cpp)
|
[✔️](csharp%2F1466-Reorder-Routes-to-Make-All-Paths-Lead-To-The-City-Zero.cs)
|
|
|
|
[✔️](java%2F1466-reorder-routes-to-make-all-paths-lead-to-the-city-zero.java)
|
|
[✔️](kotlin%2F1466-reorder-routes-to-make-all-paths-lead-to-the-city-zero.kt)
|
[✔️](python%2F1466-reorder-routes-to-make-all-paths-lead-to-the-city-zero.py)
|
|
|
|
|
+[0994 - Rotting Oranges](https://leetcode.com/problems/rotting-oranges/) |
|
[✔️](c%2F0994-rotting-oranges.c)
|
[✔️](cpp%2F0994-rotting-oranges.cpp)
|
[✔️](csharp%2F0994-rotting-oranges.cs)
|
|
[✔️](go%2F0994-rotting-oranges.go)
|
|
[✔️](java%2F0994-rotting-oranges.java)
|
[✔️](javascript%2F0994-rotting-oranges.js)
|
[✔️](kotlin%2F0994-rotting-oranges.kt)
|
[✔️](python%2F0994-rotting-oranges.py)
|
|
|
|
[✔️](swift%2F0994-rotting-oranges.swift)
|
[✔️](typescript%2F0994-rotting-oranges.ts)
+[0286 - Walls And Gates](https://leetcode.com/problems/walls-and-gates/) |
|
|
[✔️](cpp%2F0286-walls-and-gates.cpp)
|
[✔️](csharp%2F0286-walls-and-gates.cs)
|
|
|
|
[✔️](java%2F0286-walls-and-gates.java)
|
[✔️](javascript%2F0286-walls-and-gates.js)
|
[✔️](kotlin%2F0286-walls-and-gates.kt)
|
[✔️](python%2F0286-walls-and-gates.py)
|
|
|
|
[✔️](swift%2F0286-walls-and-gates.swift)
|
+[0909 - Snakes And Ladders](https://leetcode.com/problems/snakes-and-ladders/) |
|
|
|
[✔️](csharp%2F0909-snakes-and-ladders.cs)
|
|
|
|
[✔️](java%2F0909-snakes-and-ladders.java)
|
[✔️](javascript%2F0909-snakes-and-ladders.js)
|
[✔️](kotlin%2F0909-snakes-and-ladders.kt)
|
[✔️](python%2F0909-snakes-and-ladders.py)
|
|
|
|
|
+[0752 - Open The Lock](https://leetcode.com/problems/open-the-lock/) |
|
|
|
[✔️](csharp%2F0752-open-the-lock.cs)
|
|
|
|
[✔️](java%2F0752-open-the-lock.java)
|
[✔️](javascript%2F0752-open-the-lock.js)
|
[✔️](kotlin%2F0752-open-the-lock.kt)
|
[✔️](python%2F0752-open-the-lock.py)
|
|
|
|
|
+[0802 - Find Eventual Safe States](https://leetcode.com/problems/find-eventual-safe-states/) |
|
|
|
|
|
|
|
[✔️](java%2F0802-find-eventual-safe-states.java)
|
|
[✔️](kotlin%2F0802-find-eventual-safe-states.kt)
|
[✔️](python%2F0802-find-eventual-safe-states.py)
|
|
|
|
|
+[0207 - Course Schedule](https://leetcode.com/problems/course-schedule/) |
|
[✔️](c%2F0207-course-schedule.c)
|
[✔️](cpp%2F0207-course-schedule.cpp)
|
[✔️](csharp%2F0207-course-schedule.cs)
|
|
[✔️](go%2F0207-course-schedule.go)
|
|
[✔️](java%2F0207-course-schedule.java)
|
[✔️](javascript%2F0207-course-schedule.js)
|
[✔️](kotlin%2F0207-course-schedule.kt)
|
[✔️](python%2F0207-course-schedule.py)
|
|
[✔️](rust%2F0207-course-schedule.rs)
|
|
[✔️](swift%2F0207-course-schedule.swift)
|
[✔️](typescript%2F0207-course-schedule.ts)
+[0210 - Course Schedule II](https://leetcode.com/problems/course-schedule-ii/) |
|
|
[✔️](cpp%2F0210-course-schedule-ii.cpp)
|
[✔️](csharp%2F0210-course-schedule-ii.cs)
|
|
[✔️](go%2F0210-course-schedule-ii.go)
|
|
[✔️](java%2F0210-course-schedule-ii.java)
|
[✔️](javascript%2F0210-course-schedule-ii.js)
|
[✔️](kotlin%2F0210-course-schedule-ii.kt)
|
[✔️](python%2F0210-course-schedule-ii.py)
|
|
|
|
[✔️](swift%2F0210-course-schedule-ii.swift)
|
[✔️](typescript%2F0210-course-schedule-ii.ts)
+[1462 - Course Schedule IV](https://leetcode.com/problems/course-schedule-iv/) |
|
|
|
|
|
|
|
[✔️](java%2F1462-course-schedule-iv.java)
|
|
[✔️](kotlin%2F1462-course-schedule-iv.kt)
|
[✔️](python%2F1462-course-schedule-iv.py)
|
|
|
|
[✔️](swift%2F1462-course-schedule-iv.swift)
|
[✔️](typescript%2F1462-course-schedule-iv.ts)
+[1958 - Check if Move Is Legal](https://leetcode.com/problems/check-if-move-is-legal/) |
|
|
[✔️](cpp%2F1958-check-if-move-is-legal.cpp)
|
|
|
[✔️](go%2F1958-check-if-move-is-legal.go)
|
|
[✔️](java%2F1958-check-if-move-is-legal.java)
|
[✔️](javascript%2F1958-check-if-move-is-legal.js)
|
[✔️](kotlin%2F1958-check-if-move-is-legal.kt)
|
[✔️](python%2F1958-check-if-move-is-legal.py)
|
|
|
|
|
+[0934 - Shortest Bridge](https://leetcode.com/problems/shortest-bridge/) |
|
|
|
|
|
|
|
[✔️](java%2F0934-shortest-bridge.java)
|
[✔️](javascript%2F0934-shortest-bridge.js)
|
[✔️](kotlin%2F0934-shortest-bridge.kt)
|
|
|
|
|
|
+[1091 - Shortest Path in Binary Matrix](https://leetcode.com/problems/shortest-path-in-binary-matrix/) |
|
|
[✔️](cpp%2F1091-shortest-path-in-binary-matrix.cpp)
|
|
|
[✔️](go%2F1091-shortest-path-in-binary-matrix.go)
|
|
[✔️](java%2F1091-shortest-path-in-binary-matrix.java)
|
|
[✔️](kotlin%2F1091-shortest-path-in-binary-matrix.kt)
|
[✔️](python%2F1091-shortest-path-in-binary-matrix.py)
|
|
|
|
[✔️](swift%2F1091-shortest-path-in-binary-matrix.swift)
|
+[0684 - Redundant Connection](https://leetcode.com/problems/redundant-connection/) |
|
[✔️](c%2F0684-redundant-connection.c)
|
[✔️](cpp%2F0684-redundant-connection.cpp)
|
[✔️](csharp%2F0684-redundant-connection.cs)
|
|
[✔️](go%2F0684-redundant-connection.go)
|
|
[✔️](java%2F0684-redundant-connection.java)
|
[✔️](javascript%2F0684-redundant-connection.js)
|
[✔️](kotlin%2F0684-redundant-connection.kt)
|
[✔️](python%2F0684-redundant-connection.py)
|
|
[✔️](rust%2F0684-redundant-connection.rs)
|
|
[✔️](swift%2F0684-redundant-connection.swift)
|
[✔️](typescript%2F0684-redundant-connection.ts)
+[0323 - Number of Connected Components In An Undirected Graph](https://leetcode.com/problems/number-of-connected-components-in-an-undirected-graph/) |
|
|
[✔️](cpp%2F0323-number-of-connected-components-in-an-undirected-graph.cpp)
|
[✔️](csharp%2F0323-number-of-connected-components-in-an-undirected-graph.cs)
|
|
[✔️](go%2F0323-number-of-connected-components-in-an-undirected-graph.go)
|
|
[✔️](java%2F0323-number-of-connected-components-in-an-undirected-graph.java)
|
[✔️](javascript%2F0323-number-of-connected-components-in-an-undirected-graph.js)
|
[✔️](kotlin%2F0323-number-of-connected-components-in-an-undirected-graph.kt)
|
[✔️](python%2F0323-number-of-connected-components-in-an-undirected-graph.py)
|
|
|
|
[✔️](swift%2F0323-number-of-connected-components-in-an-undirected-graph.swift)
|
+[0261 - Graph Valid Tree](https://leetcode.com/problems/graph-valid-tree/) |
|
|
[✔️](cpp%2F0261-graph-valid-tree.cpp)
|
[✔️](csharp%2F0261-graph-valid-tree.cs)
|
|
|
|
[✔️](java%2F0261-graph-valid-tree.java)
|
[✔️](javascript%2F0261-graph-valid-tree.js)
|
[✔️](kotlin%2F0261-graph-valid-tree.kt)
|
[✔️](python%2F0261-graph-valid-tree.py)
|
|
|
|
[✔️](swift%2F0261-graph-valid-tree.swift)
|
[✔️](typescript%2F0261-graph-valid-tree.ts)
+[0721 - Accounts Merge](https://leetcode.com/problems/accounts-merge/) |
|
|
|
|
|
|
|
[✔️](java%2F0721-accounts-merge.java)
|
|
[✔️](kotlin%2F0721-accounts-merge.kt)
|
[✔️](python%2F0721-accounts-merge.py)
|
|
|
|
[✔️](swift%2F0721-accounts-merge.swift)
|
+[2359 - Find Closest Node to Given Two Nodes](https://leetcode.com/problems/find-closest-node-to-given-two-nodes/) |
|
|
|
|
|
|
|
|
|
[✔️](kotlin%2F2359-find-closest-node-to-given-two-nodes.kt)
|
|
|
|
|
|
+[1162 - As Far from Land as Possible](https://leetcode.com/problems/as-far-from-land-as-possible/) |
|
|
|
|
|
|
|
|
|
[✔️](kotlin%2F1162-as-far-from-land-as-possible.kt)
|
|
|
|
|
|
+[1129 - Shortest Path with Alternating Colors](https://leetcode.com/problems/shortest-path-with-alternating-colors/) |
|
|
|
|
|
|
|
[✔️](java%2F1129-shortest-path-with-alternating-colors.java)
|
|
[✔️](kotlin%2F1129-shortest-path-with-alternating-colors.kt)
|
|
|
|
|
|
+[2477 - Minimum Fuel Cost to Report to the Capital](https://leetcode.com/problems/minimum-fuel-cost-to-report-to-the-capital/) |
|
|
|
|
|
|
|
[✔️](java%2F2477-minimum-fuel-cost-to-report-to-the-capital.java)
|
|
[✔️](kotlin%2F2477-minimum-fuel-cost-to-report-to-the-capital.kt)
|
|
|
|
|
|
+[2492 - Minimum Score of a Path Between Two Cities](https://leetcode.com/problems/minimum-score-of-a-path-between-two-cities/) |
|
|
|
|
|
|
|
|
|
[✔️](kotlin%2F2492-minimum-score-of-a-path-between-two-cities.kt)
|
|
|
|
|
|
+[1254 - Number of Closed Islands](https://leetcode.com/problems/number-of-closed-islands/) |
|
|
|
|
|
|
|
|
|
[✔️](kotlin%2F1254-number-of-closed-islands.kt)
|
[✔️](python%2F1254-number-of-closed-islands.py)
|
|
|
|
|
+[1020 - Number of Enclaves](https://leetcode.com/problems/number-of-enclaves/) |
|
|
|
|
|
|
|
[✔️](java%2F1020-number-of-enclaves.java)
|
|
[✔️](kotlin%2F1020-number-of-enclaves.kt)
|
[✔️](python%2F1020-number-of-enclaves.py)
|
|
|
|
|
+[1557 - Minimum Number of Vertices to Reach all Nodes](https://leetcode.com/problems/minimum-number-of-vertices-to-reach-all-nodes/) |
|
|
|
|
|
|
|
[✔️](java%2F1557-minimum-number-of-vertices-to-reach-all-nodes.java)
|
|
[✔️](kotlin%2F1557-minimum-number-of-vertices-to-reach-all-nodes.kt)
|
|
|
|
|
|
+[0785 - Is Graph Bipartite?](https://leetcode.com/problems/is-graph-bipartite/) |
|
|
|
|
|
|
|
[✔️](java%2F0785-is-graph-bipartite.java)
|
|
[✔️](kotlin%2F0785-is-graph-bipartite.kt)
|
[✔️](python%2F0785-is-graph-bipartite.py)
|
|
|
|
|
+[0399 - Evaluate Division](https://leetcode.com/problems/evaluate-division/) |
|
|
[✔️](cpp%2F0399-evaluate-division.cpp)
|
|
|
|
|
[✔️](java%2F0399-evaluate-division.java)
|
|
[✔️](kotlin%2F0399-evaluate-division.kt)
|
|
|
|
|
|
+[2101 - Detonate the Maximum Bombs](https://leetcode.com/problems/detonate-the-maximum-bombs/) |
|
|
|
|
|
|
|
|
|
[✔️](kotlin%2F2101-detonate-the-maximum-bombs.kt)
|
[✔️](python%2F2101-detonate-the-maximum-bombs.py)
|
|
|
|
|
+[1857 - Largest Color Value in a Directed Graph](https://leetcode.com/problems/largest-color-value-in-a-directed-graph/) |
|
|
|
|
|
|
|
|
|
[✔️](kotlin%2F1857-largest-color-value-in-a-directed-graph.kt)
|
|
|
[✔️](rust%2F1857-largest-color-value-in-a-directed-graph.rs)
|
|
|
+[1553 - Minimum Number of Days to Eat N Oranges](https://leetcode.com/problems/minimum-number-of-days-to-eat-n-oranges/) |
|
|
|
|
|
|
|
[✔️](java%2F1553-minimum-number-of-days-to-eat-n-oranges.java)
|
|
[✔️](kotlin%2F1553-minimum-number-of-days-to-eat-n-oranges.kt)
|
|
|
|
|
|
+[0127 - Word Ladder](https://leetcode.com/problems/word-ladder/) |
|
|
[✔️](cpp%2F0127-word-ladder.cpp)
|
[✔️](csharp%2F0127-word-ladder.cs)
|
|
[✔️](go%2F0127-word-ladder.go)
|
|
[✔️](java%2F0127-word-ladder.java)
|
[✔️](javascript%2F0127-word-ladder.js)
|
[✔️](kotlin%2F0127-word-ladder.kt)
|
[✔️](python%2F0127-word-ladder.py)
|
|
[✔️](rust%2F0127-word-ladder.rs)
|
|
[✔️](swift%2F0127-word-ladder.swift)
|
[✔️](typescript%2F0127-word-ladder.ts)
+ +### Advanced Graphs + +Problem | articles | C | C++ | C# | Dart | GO | hints | Java | JS | Kotlin | Python | Ruby | Rust | Scala | Swift | TS +---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- +[1631 - Path with Minimum Effort](https://leetcode.com/problems/path-with-minimum-effort/) |
|
|
|
|
|
|
|
|
|
[✔️](kotlin%2F1631-path-with-minimum-effort.kt)
|
[✔️](python%2F1631-path-with-minimum-effort.py)
|
|
|
|
|
+[0332 - Reconstruct Itinerary](https://leetcode.com/problems/reconstruct-itinerary/) |
|
[✔️](c%2F0332-reconstruct-itinerary.c)
|
[✔️](cpp%2F0332-reconstruct-itinerary.cpp)
|
[✔️](csharp%2F0332-reconstruct-itinerary.cs)
|
|
[✔️](go%2F0332-reconstruct-itinerary.go)
|
|
[✔️](java%2F0332-reconstruct-itinerary.java)
|
[✔️](javascript%2F0332-reconstruct-itinerary.js)
|
[✔️](kotlin%2F0332-reconstruct-itinerary.kt)
|
[✔️](python%2F0332-reconstruct-itinerary.py)
|
|
[✔️](rust%2F0332-reconstruct-itinerary.rs)
|
|
[✔️](swift%2F0332-reconstruct-itinerary.swift)
|
+[1584 - Min Cost to Connect All Points](https://leetcode.com/problems/min-cost-to-connect-all-points/) |
|
|
[✔️](cpp%2F1584-Min-Cost-To-Connect-All-Points.cpp)
|
[✔️](csharp%2F1584-Min-Cost-to-Connect-All-Points.cs)
|
|
[✔️](go%2F1584-min-cost-to-connect-all-points.go)
|
|
[✔️](java%2F1584-Min-Cost-to-Connect-All-Points.java)
|
[✔️](javascript%2F1584-Min-Cost-to-Connect-all-Points.js)
|
[✔️](kotlin%2F1584-min-cost-to-connect-all-points.kt)
|
[✔️](python%2F1584-min-cost-to-connect-all-points.py)
|
[✔️](ruby%2F1584-Min-Cost-to-Connect-All-Points.rb)
|
|
|
[✔️](swift%2F1584-Min-Cost-to-Connect-All-Points.swift)
|
[✔️](typescript%2F1584-min-cost-to-connect-all-points.ts)
+[0743 - Network Delay Time](https://leetcode.com/problems/network-delay-time/) |
|
|
[✔️](cpp%2F0743-network-delay-time.cpp)
|
[✔️](csharp%2F0743-network-delay-time.cs)
|
|
[✔️](go%2F0743-network-delay-time.go)
|
|
[✔️](java%2F0743-network-delay-time.java)
|
[✔️](javascript%2F0743-network-delay-time.js)
|
[✔️](kotlin%2F0743-network-delay-time.kt)
|
[✔️](python%2F0743-network-delay-time.py)
|
|
|
|
[✔️](swift%2F0743-network-delay-time.swift)
|
[✔️](typescript%2F0743-network-delay-time.ts)
+[1514 - Path with Maximum Probability](https://leetcode.com/problems/path-with-maximum-probability/) |
|
|
[✔️](cpp%2F1514-path-with-maximum-probability.cpp)
|
|
|
|
|
[✔️](java%2F1514-path-with-maximum-probability.java)
|
[✔️](javascript%2F1514-path-with-maximum-probability.js)
|
[✔️](kotlin%2F1514-path-with-maximum-probability.kt)
|
[✔️](python%2F1514-path-with-maximum-probability.py)
|
|
|
|
[✔️](swift%2F1514-path-with-maximum-probability.swift)
|
[✔️](typescript%2F1514-path-with-maximum-probability.ts)
+[0778 - Swim In Rising Water](https://leetcode.com/problems/swim-in-rising-water/) |
|
|
[✔️](cpp%2F0778-swim-in-rising-water.cpp)
|
[✔️](csharp%2F0778-swim-in-rising-water.cs)
|
|
[✔️](go%2F0778-swim-in-rising-water.go)
|
|
[✔️](java%2F0778-swim-in-rising-water.java)
|
[✔️](javascript%2F0778-swim-in-rising-water.js)
|
[✔️](kotlin%2F0778-swim-in-rising-water.kt)
|
[✔️](python%2F0778-swim-in-rising-water.py)
|
|
[✔️](rust%2F0778-swim-in-rising-water.rs)
|
|
[✔️](swift%2F0778-swim-in-rising-water.swift)
|
[✔️](typescript%2F0778-swim-in-rising-water.ts)
+[0269 - Alien Dictionary](https://leetcode.com/problems/alien-dictionary/) |
|
|
[✔️](cpp%2F0269-alien-dictionary.cpp)
|
[✔️](csharp%2F0269-alien-dictionary.cs)
|
|
|
|
[✔️](java%2F0269-alien-dictionary.java)
|
[✔️](javascript%2F0269-alien-dictionary.js)
|
|
[✔️](python%2F0269-alien-dictionary.py)
|
|
|
|
[✔️](swift%2F0269-alien-dictionary.swift)
|
+[0787 - Cheapest Flights Within K Stops](https://leetcode.com/problems/cheapest-flights-within-k-stops/) |
|
|
[✔️](cpp%2F0787-cheapest-flights-within-k-stops.cpp)
|
[✔️](csharp%2F0787-cheapest-flights-within-k-stops.cs)
|
|
[✔️](go%2F0787-cheapest-flights-within-k-stops.go)
|
|
[✔️](java%2F0787-cheapest-flights-within-k-stops.java)
|
[✔️](javascript%2F0787-cheapest-flights-within-k-stops.js)
|
[✔️](kotlin%2F0787-cheapest-flights-within-k-stops.kt)
|
[✔️](python%2F0787-cheapest-flights-within-k-stops.py)
|
|
|
|
[✔️](swift%2F0787-cheapest-flights-within-k-stops.swift)
|
[✔️](typescript%2F0787-cheapest-flights-within-k-stops.ts)
+[2421 - Number of Good Paths](https://leetcode.com/problems/number-of-good-paths/) |
|
|
|
|
|
[✔️](go%2F2421-number-of-good-paths.go)
|
|
|
[✔️](javascript%2F2421-number-of-good-paths.js)
|
[✔️](kotlin%2F2421-number-of-good-paths.kt)
|
|
|
[✔️](rust%2F2421-number-of-good-paths.rs)
|
|
|
[✔️](typescript%2F2421-number-of-good-paths.ts)
+[1579 - Remove Max Number of Edges to Keep Graph Fully Traversable](https://leetcode.com/problems/remove-max-number-of-edges-to-keep-graph-fully-traversable/) |
|
|
|
|
|
|
|
|
|
[✔️](kotlin%2F1579-remove-max-number-of-edges-to-keep-graph-fully-traversable.kt)
|
|
|
|
|
|
+[1489 - Find Critical and Pseudo Critical Edges in Minimum Spanning Tree](https://leetcode.com/problems/find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree/) |
|
|
|
|
|
|
|
|
|
[✔️](kotlin%2F1489-find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.kt)
|
[✔️](python%2F1489-find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.py)
|
|
|
|
[✔️](swift%2F1489-find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.swift)
|
[✔️](typescript%2F1489-find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.ts)
+ +### 1-D Dynamic Programming + +Problem | articles | C | C++ | C# | Dart | GO | hints | Java | JS | Kotlin | Python | Ruby | Rust | Scala | Swift | TS +---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- +[0070 - Climbing Stairs](https://leetcode.com/problems/climbing-stairs/) |
|
[✔️](c%2F0070-climbing-stairs.c)
|
[✔️](cpp%2F0070-climbing-stairs.cpp)
|
[✔️](csharp%2F0070-climbing-stairs.cs)
|
[✔️](dart%2F0070-climbing-stairs.dart)
|
[✔️](go%2F0070-climbing-stairs.go)
|
|
[✔️](java%2F0070-climbing-stairs.java)
|
[✔️](javascript%2F0070-climbing-stairs.js)
|
[✔️](kotlin%2F0070-climbing-stairs.kt)
|
[✔️](python%2F0070-climbing-stairs.py)
|
[✔️](ruby%2F0070-climbing-stairs.rb)
|
[✔️](rust%2F0070-climbing-stairs.rs)
|
[✔️](scala%2F0070-climbing-stairs.scala)
|
[✔️](swift%2F0070-climbing-stairs.swift)
|
[✔️](typescript%2F0070-climbing-stairs.ts)
+[0746 - Min Cost Climbing Stairs](https://leetcode.com/problems/min-cost-climbing-stairs/) |
|
[✔️](c%2F0746-min-cost-climbing-stairs.c)
|
[✔️](cpp%2F0746-min-cost-climbing-stairs.cpp)
|
[✔️](csharp%2F0746-min-cost-climbing-stairs.cs)
|
[✔️](dart%2F0746-min-cost-climbing-stairs.dart)
|
[✔️](go%2F0746-min-cost-climbing-stairs.go)
|
|
[✔️](java%2F0746-min-cost-climbing-stairs.java)
|
[✔️](javascript%2F0746-min-cost-climbing-stairs.js)
|
[✔️](kotlin%2F0746-min-cost-climbing-stairs.kt)
|
[✔️](python%2F0746-min-cost-climbing-stairs.py)
|
[✔️](ruby%2F0746-min-cost-climbing-stairs.rb)
|
[✔️](rust%2F0746-min-cost-climbing-stairs.rs)
|
[✔️](scala%2F0746-min-cost-climbing-stairs.scala)
|
[✔️](swift%2F0746-min-cost-climbing-stairs.swift)
|
[✔️](typescript%2F0746-min-cost-climbing-stairs.ts)
+[0198 - House Robber](https://leetcode.com/problems/house-robber/) |
|
[✔️](c%2F0198-house-robber.c)
|
[✔️](cpp%2F0198-house-robber.cpp)
|
[✔️](csharp%2F0198-house-robber.cs)
|
|
[✔️](go%2F0198-house-robber.go)
|
|
[✔️](java%2F0198-house-robber.java)
|
[✔️](javascript%2F0198-house-robber.js)
|
[✔️](kotlin%2F0198-house-robber.kt)
|
[✔️](python%2F0198-house-robber.py)
|
[✔️](ruby%2F0198-house-robber.rb)
|
[✔️](rust%2F0198-house-robber.rs)
|
[✔️](scala%2F0198-house-robber.scala)
|
[✔️](swift%2F0198-house-robber.swift)
|
[✔️](typescript%2F0198-house-robber.ts)
+[0213 - House Robber II](https://leetcode.com/problems/house-robber-ii/) |
|
[✔️](c%2F0213-house-robber-ii.c)
|
[✔️](cpp%2F0213-house-robber-ii.cpp)
|
[✔️](csharp%2F0213-house-robber-ii.cs)
|
|
[✔️](go%2F0213-house-robber-ii.go)
|
|
[✔️](java%2F0213-house-robber-ii.java)
|
[✔️](javascript%2F0213-house-robber-ii.js)
|
[✔️](kotlin%2F0213-house-robber-ii.kt)
|
[✔️](python%2F0213-house-robber-ii.py)
|
[✔️](ruby%2F0213-house-robber-ii.rb)
|
[✔️](rust%2F0213-house-robber-ii.rs)
|
[✔️](scala%2F0213-house-robber-ii.scala)
|
[✔️](swift%2F0213-house-robber-ii.swift)
|
[✔️](typescript%2F0213-house-robber-ii.ts)
+[0005 - Longest Palindromic Substring](https://leetcode.com/problems/longest-palindromic-substring/) |
|
[✔️](c%2F0005-longest-palindromic-substring.c)
|
[✔️](cpp%2F0005-longest-palindromic-substring.cpp)
|
[✔️](csharp%2F0005-longest-palindromic-substring.cs)
|
|
[✔️](go%2F0005-Longest-Palindromic-Substring.go)
|
|
[✔️](java%2F0005-longest-palindromic-substring.java)
|
[✔️](javascript%2F0005-longest-palindromic-substring.js)
|
[✔️](kotlin%2F0005-longest-palindromic-substring.kt)
|
[✔️](python%2F0005-longest-palindromic-substring.py)
|
|
[✔️](rust%2F0005-longest-palindromic-substring.rs)
|
|
[✔️](swift%2F0005-longest-palindromic-substring.swift)
|
[✔️](typescript%2F0005-longest-palindromic-substring.ts)
+[0647 - Palindromic Substrings](https://leetcode.com/problems/palindromic-substrings/) |
|
[✔️](c%2F0647-palindromic-substrings.c)
|
[✔️](cpp%2F0647-palindromic-substrings.cpp)
|
[✔️](csharp%2F0647-palindromic-substrings.cs)
|
|
[✔️](go%2F0647-palindromic-substrings.go)
|
|
[✔️](java%2F0647-palindromic-substrings.java)
|
[✔️](javascript%2F0647-palindromic-substrings.js)
|
[✔️](kotlin%2F0647-palindromic-substrings.kt)
|
[✔️](python%2F0647-palindromic-substrings.py)
|
|
[✔️](rust%2F0647-palindromic-substrings.rs)
|
|
[✔️](swift%2F0647-palindromic-substrings.swift)
|
[✔️](typescript%2F0647-palindromic-substrings.ts)
+[0091 - Decode Ways](https://leetcode.com/problems/decode-ways/) |
|
[✔️](c%2F0091-decode-ways.c)
|
[✔️](cpp%2F0091-decode-ways.cpp)
|
[✔️](csharp%2F0091-decode-ways.cs)
|
|
[✔️](go%2F0091-decode-ways.go)
|
|
[✔️](java%2F0091-decode-ways.java)
|
[✔️](javascript%2F0091-decode-ways.js)
|
[✔️](kotlin%2F0091-decode-ways.kt)
|
[✔️](python%2F0091-decode-ways.py)
|
|
[✔️](rust%2F0091-decode-ways.rs)
|
[✔️](scala%2F0091-decode-ways.scala)
|
[✔️](swift%2F0091-decode-ways.swift)
|
[✔️](typescript%2F0091-decode-ways.ts)
+[0322 - Coin Change](https://leetcode.com/problems/coin-change/) |
|
[✔️](c%2F0322-coin-change.c)
|
[✔️](cpp%2F0322-coin-change.cpp)
|
[✔️](csharp%2F0322-coin-change.cs)
|
|
[✔️](go%2F0322-coin-change.go)
|
|
[✔️](java%2F0322-coin-change.java)
|
[✔️](javascript%2F0322-coin-change.js)
|
[✔️](kotlin%2F0322-coin-change.kt)
|
[✔️](python%2F0322-coin-change.py)
|
|
[✔️](rust%2F0322-coin-change.rs)
|
[✔️](scala%2F0322-coin-change.scala)
|
[✔️](swift%2F0322-coin-change.swift)
|
[✔️](typescript%2F0322-coin-change.ts)
+[0152 - Maximum Product Subarray](https://leetcode.com/problems/maximum-product-subarray/) |
|
[✔️](c%2F0152-maximum-product-subarray.c)
|
[✔️](cpp%2F0152-maximum-product-subarray.cpp)
|
[✔️](csharp%2F0152-maximum-product-subarray.cs)
|
|
[✔️](go%2F0152-maximum-product-subarray.go)
|
|
[✔️](java%2F0152-maximum-product-subarray.java)
|
[✔️](javascript%2F0152-maximum-product-subarray.js)
|
[✔️](kotlin%2F0152-maximum-product-subarray.kt)
|
[✔️](python%2F0152-maximum-product-subarray.py)
|
[✔️](ruby%2F0152-maximum-product-subarray.rb)
|
[✔️](rust%2F0152-maximum-product-subarray.rs)
|
|
[✔️](swift%2F0152-maximum-product-subarray.swift)
|
[✔️](typescript%2F0152-maximum-product-subarray.ts)
+[0139 - Word Break](https://leetcode.com/problems/word-break/) |
|
|
[✔️](cpp%2F0139-word-break.cpp)
|
[✔️](csharp%2F0139-word-break.cs)
|
|
[✔️](go%2F0139-word-break.go)
|
|
[✔️](java%2F0139-word-break.java)
|
[✔️](javascript%2F0139-word-break.js)
|
[✔️](kotlin%2F0139-word-break.kt)
|
[✔️](python%2F0139-word-break.py)
|
|
|
|
[✔️](swift%2F0139-word-break.swift)
|
[✔️](typescript%2F0139-word-break.ts)
+[0300 - Longest Increasing Subsequence](https://leetcode.com/problems/longest-increasing-subsequence/) |
|
[✔️](c%2F0300-longest-increasing-subsequence.c)
|
[✔️](cpp%2F0300-longest-increasing-subsequence.cpp)
|
[✔️](csharp%2F0300-longest-increasing-subsequence.cs)
|
|
[✔️](go%2F0300-longest-increasing-subsequence.go)
|
|
[✔️](java%2F0300-longest-increasing-subsequence.java)
|
[✔️](javascript%2F0300-longest-increasing-subsequence.js)
|
[✔️](kotlin%2F0300-longest-increasing-subsequence.kt)
|
[✔️](python%2F0300-longest-increasing-subsequence.py)
|
|
[✔️](rust%2F0300-longest-increasing-subsequence.rs)
|
|
[✔️](swift%2F0300-longest-increasing-subsequence.swift)
|
[✔️](typescript%2F0300-longest-increasing-subsequence.ts)
+[0416 - Partition Equal Subset Sum](https://leetcode.com/problems/partition-equal-subset-sum/) |
|
[✔️](c%2F0416-partition-equal-subset-sum.c)
|
[✔️](cpp%2F0416-partition-equal-subset-sum.cpp)
|
[✔️](csharp%2F0416-partition-equal-subset-sum.cs)
|
|
[✔️](go%2F0416-partition-equal-subset-sum.go)
|
|
[✔️](java%2F0416-partition-equal-subset-sum.java)
|
[✔️](javascript%2F0416-partition-equal-subset-sum.js)
|
[✔️](kotlin%2F0416-partition-equal-subset-sum.kt)
|
[✔️](python%2F0416-partition-equal-subset-sum.py)
|
|
|
|
[✔️](swift%2F0416-partition-equal-subset-sum.swift)
|
+[0120 - Triangle](https://leetcode.com/problems/triangle/) |
|
[✔️](c%2F0120-triangle.c)
|
[✔️](cpp%2F0120-triangle.cpp)
|
|
|
|
|
[✔️](java%2F0120-triangle.java)
|
|
[✔️](kotlin%2F0120-triangle.kt)
|
[✔️](python%2F0120-triangle.py)
|
|
|
|
|
+[0740 - Delete And Earn](https://leetcode.com/problems/delete-and-earn/) |
|
[✔️](c%2F0740-delete-and-earn.c)
|
[✔️](cpp%2F0740-delete-and-earn.cpp)
|
|
|
[✔️](go%2F0740-delete-and-earn.go)
|
|
[✔️](java%2F0740-delete-and-earn.java)
|
|
[✔️](kotlin%2F0740-delete-and-earn.kt)
|
[✔️](python%2F0740-delete-and-earn.py)
|
|
|
|
|
+[0256 - Paint House](https://leetcode.com/problems/paint-house/) |
|
|
|
[✔️](csharp%2F0256-paint-house.cs)
|
|
|
|
[✔️](java%2F0256-paint-house.java)
|
|
|
|
|
|
|
|
[✔️](typescript%2F0256-paint-house.ts)
+[0377 - Combination Sum IV](https://leetcode.com/problems/combination-sum-iv/) |
|
[✔️](c%2F0377-combination-sum-iv.c)
|
[✔️](cpp%2F0377-combination-sum-iv.cpp)
|
|
|
|
|
[✔️](java%2F0377-combination-sum-iv.java)
|
|
[✔️](kotlin%2F0377-combination-sum-iv.kt)
|
[✔️](python%2F0377-combination-sum-iv.py)
|
|
|
|
|
+[0279 - Perfect Squares](https://leetcode.com/problems/perfect-squares/) |
|
[✔️](c%2F0279-perfect-squares.c)
|
[✔️](cpp%2F0279-perfect-squares.cpp)
|
|
|
[✔️](go%2F0279-perfect-squares.go)
|
|
[✔️](java%2F0279-perfect-squares.java)
|
|
[✔️](kotlin%2F0279-perfect-squares.kt)
|
|
|
|
|
|
+[2369 - Check if There is a Valid Partition For The Array](https://leetcode.com/problems/check-if-there-is-a-valid-partition-for-the-array/) |
|
|
|
|
|
|
|
|
|
[✔️](kotlin%2F2369-check-if-there-is-a-valid-partition-for-the-array.kt)
|
|
|
|
|
|
+[1856 - Maximum Subarray Min Product](https://leetcode.com/problems/maximum-subarray-min-product/) |
|
[✔️](c%2F1856-maximum-subarray-min-product.c)
|
|
|
|
|
|
[✔️](java%2F1856-maximum-subarray-min-product.java)
|
|
[✔️](kotlin%2F1856-maximum-subarray-min-product.kt)
|
|
|
|
|
|
+[0983 - Minimum Cost For Tickets](https://leetcode.com/problems/minimum-cost-for-tickets/) |
|
[✔️](c%2F0983-minimum-cost-for-tickets.c)
|
[✔️](cpp%2F0983-minimum-cost-for-tickets.cpp)
|
|
|
|
|
|
|
[✔️](kotlin%2F0983-minimum-cost-for-tickets.kt)
|
|
|
|
|
[✔️](swift%2F0983-minimum-cost-for-tickets.swift)
|
[✔️](typescript%2F0983-minimum-cost-for-tickets.ts)
+[0343 - Integer Break](https://leetcode.com/problems/integer-break/) |
|
[✔️](c%2F0343-integer-break.c)
|
[✔️](cpp%2F0343-integer-break.cpp)
|
|
|
|
|
[✔️](java%2F0343-integer-break.java)
|
|
[✔️](kotlin%2F0343-integer-break.kt)
|
|
|
|
|
|
+[0673 - Number of Longest Increasing Subsequence](https://leetcode.com/problems/number-of-longest-increasing-subsequence/) |
|
[✔️](c%2F0673-number-of-longest-increasing-subsequence.c)
|
|
|
|
|
|
[✔️](java%2F0673-number-of-longest-increasing-subsequence.java)
|
|
[✔️](kotlin%2F0673-number-of-longest-increasing-subsequence.kt)
|
[✔️](python%2F0673-number-of-longest-increasing-subsequence.py)
|
|
|
|
|
+[0691 - Stickers to Spell Word](https://leetcode.com/problems/stickers-to-spell-word/) |
|
[✔️](c%2F0691-stickers-to-spell-word.c)
|
|
|
|
|
|
|
|
[✔️](kotlin%2F0691-stickers-to-spell-word.kt)
|
|
|
|
|
|
+[1137 - N-th Tribonacci Number](https://leetcode.com/problems/n-th-tribonacci-number/) |
|
[✔️](c%2F1137-n-th-tribonacci-number.c)
|
[✔️](cpp%2F1137-n-th-tribonacci-number.cpp)
|
[✔️](csharp%2F1137-n-th-tribonacci-number.cs)
|
|
[✔️](go%2F1137-n-th-tribonacci-number.go)
|
|
|
[✔️](javascript%2F1137-n-th-tribonacci-number.js)
|
[✔️](kotlin%2F1137-n-th-tribonacci-number.kt)
|
[✔️](python%2F1137-n-th-tribonacci-number.py)
|
|
[✔️](rust%2F1137-n-th-tribonacci-number.rs)
|
|
|
[✔️](typescript%2F1137-n-th-tribonacci-number.ts)
+[1035 - Uncrossed Lines](https://leetcode.com/problems/uncrossed-lines/) |
|
[✔️](c%2F1035-uncrossed-lines.c)
|
|
|
|
|
|
|
|
[✔️](kotlin%2F1035-uncrossed-lines.kt)
|
|
|
|
|
|
+[2140 - Solving Questions With Brainpower](https://leetcode.com/problems/solving-questions-with-brainpower/) |
|
[✔️](c%2F2140-solving-questions-with-brainpower.c)
|
[✔️](cpp%2F2140-solving-questions-with-brainpower.cpp)
|
|
|
|
|
|
|
[✔️](kotlin%2F2140-solving-questions-with-brainpower.kt)
|
|
|
|
|
|
+[2466 - Count Ways to Build Good Strings](https://leetcode.com/problems/count-ways-to-build-good-strings/) |
|
[✔️](c%2F2466-count-ways-to-build-good-strings.c)
|
|
|
|
|
|
|
|
[✔️](kotlin%2F2466-count-ways-to-build-good-strings.kt)
|
|
|
|
|
|
+[0837 - New 21 Game](https://leetcode.com/problems/new-21-game/) |
|
[✔️](c%2F0837-new-21-game.c)
|
|
|
|
|
|
[✔️](java%2F0837-new-21-game.java)
|
[✔️](javascript%2F0837-new-21-game.js)
|
[✔️](kotlin%2F0837-new-21-game.kt)
|
|
|
|
|
|
+[1626 - Best Team with no Conflicts](https://leetcode.com/problems/best-team-with-no-conflicts/) |
|
[✔️](c%2F1626-best-team-with-no-conflicts.c)
|
|
|
|
|
|
[✔️](java%2F1626-best-team-with-no-conflicts.java)
|
|
[✔️](kotlin%2F1626-best-team-with-no-conflicts.kt)
|
|
|
|
|
|
+[1406 - Stone Game III](https://leetcode.com/problems/stone-game-iii/) |
|
[✔️](c%2F1406-stone-game-iii.c)
|
|
|
|
|
|
|
|
[✔️](kotlin%2F1406-stone-game-iii.kt)
|
|
|
|
|
|
+[0472 - Concatenated Words](https://leetcode.com/problems/concatenated-words/) |
|
|
|
|
|
[✔️](go%2F0472-concatenated-words.go)
|
|
|
[✔️](javascript%2F0472-concatenated-words.js)
|
[✔️](kotlin%2F0472-concatenated-words.kt)
|
|
|
[✔️](rust%2F0472-concatenated-words.rs)
|
|
|
[✔️](typescript%2F0472-concatenated-words.ts)
+[1799 - Maximize Score after N Operations](https://leetcode.com/problems/maximize-score-after-n-operations/) |
|
[✔️](c%2F1799-maximize-score-after-n-operations.c)
|
|
|
|
|
|
|
|
[✔️](kotlin%2F1799-maximize-score-after-n-operations.kt)
|
|
|
|
|
|
+[1964 - Find the Longest Valid Obstacle Course at Each Position](https://leetcode.com/problems/find-the-longest-valid-obstacle-course-at-each-position/) |
|
[✔️](c%2F1964-find-the-longest-valid-obstacle-course-at-each-position.c)
|
|
|
|
|
|
|
|
[✔️](kotlin%2F1964-find-the-longest-valid-obstacle-course-at-each-position.kt)
|
|
|
|
|
|
+[1359 - Count all Valid Pickup and Delivery Options](https://leetcode.com/problems/count-all-valid-pickup-and-delivery-options/) |
|
|
|
|
|
|
|
[✔️](java%2F1359-count-all-valid-pickup-and-delivery-options.java)
|
|
[✔️](kotlin%2F1359-count-all-valid-pickup-and-delivery-options.kt)
|
|
|
|
|
|
+ +### 2-D Dynamic Programming + +Problem | articles | C | C++ | C# | Dart | GO | hints | Java | JS | Kotlin | Python | Ruby | Rust | Scala | Swift | TS +---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- +[0062 - Unique Paths](https://leetcode.com/problems/unique-paths/) |
|
[✔️](c%2F0062-unique-paths.c)
|
[✔️](cpp%2F0062-unique-paths.cpp)
|
[✔️](csharp%2F0062-unique-paths.cs)
|
|
[✔️](go%2F0062-unique-paths.go)
|
|
[✔️](java%2F0062-unique-paths.java)
|
[✔️](javascript%2F0062-unique-paths.js)
|
[✔️](kotlin%2F0062-unique-paths.kt)
|
[✔️](python%2F0062-unique-paths.py)
|
|
[✔️](rust%2F0062-unique-paths.rs)
|
|
[✔️](swift%2F0062-unique-paths.swift)
|
[✔️](typescript%2F0062-unique-paths.ts)
+[0063 - Unique Paths II](https://leetcode.com/problems/unique-paths-ii/) |
|
|
[✔️](cpp%2F0063-unique-paths-ii.cpp)
|
[✔️](csharp%2F0063-unique-paths-ii.cs)
|
|
[✔️](go%2F0063-unique-paths-ii.go)
|
|
[✔️](java%2F0063-unique-paths-ii.java)
|
|
[✔️](kotlin%2F0063-unique-paths-ii.kt)
|
[✔️](python%2F0063-unique-paths-ii.py)
|
|
[✔️](rust%2F0063-unique-paths-ii.rs)
|
|
[✔️](swift%2F0063-unique-paths-ii.swift)
|
+[1143 - Longest Common Subsequence](https://leetcode.com/problems/longest-common-subsequence/) |
|
[✔️](c%2F1143-longest-common-subsequence.c)
|
[✔️](cpp%2F1143-Longest-Common-Subsequence.cpp)
|
[✔️](csharp%2F1143-Longest-Common-Subsequence.cs)
|
[✔️](dart%2F1143-longest-common-subsequence.dart)
|
[✔️](go%2F1143-Longest-Common-Subsequence.go)
|
|
[✔️](java%2F1143-longest-common-subsequence.java)
|
[✔️](javascript%2F1143-Longest-Common-Subsequence.js)
|
[✔️](kotlin%2F1143-Longest-Common-Subsequence.kt)
|
[✔️](python%2F1143-longest-common-subsequence.py)
|
|
[✔️](rust%2F1143-Longest-Common-Subsequence.rs)
|
|
[✔️](swift%2F1143-Longest-Common-Subsequence.swift)
|
[✔️](typescript%2F1143-Longest-Common-Subsequence.ts)
+[0516 - Longest Palindromic Subsequence](https://leetcode.com/problems/longest-palindromic-subsequence/) |
|
|
|
|
|
|
|
|
|
[✔️](kotlin%2F0516-longest-palindromic-subsequence.kt)
|
[✔️](python%2F0516-longest-palindromic-subsequence.py)
|
|
|
|
[✔️](swift%2F0516-longest-palindromic-subsequence.swift)
|
+[1049 - Last Stone Weight II](https://leetcode.com/problems/last-stone-weight-ii/) |
|
|
|
|
|
|
|
|
|
[✔️](kotlin%2F1049-last-stone-weight-ii.kt)
|
[✔️](python%2F1049-last-stone-weight-ii.py)
|
|
|
|
[✔️](swift%2F1049-last-stone-weight-ii.swift)
|
[✔️](typescript%2F1049-last-stone-weight-ii.ts)
+[0309 - Best Time to Buy And Sell Stock With Cooldown](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/) |
|
[✔️](c%2F0309-best-time-to-buy-and-sell-stock-with-cooldown.c)
|
[✔️](cpp%2F0309-best-time-to-buy-and-sell-stock-with-cooldown.cpp)
|
[✔️](csharp%2F0309-best-time-to-buy-and-sell-stock-with-cooldown.cs)
|
|
[✔️](go%2F0309-best-time-to-buy-and-sell-stock-with-cooldown.go)
|
|
[✔️](java%2F0309-best-time-to-buy-and-sell-stock-with-cooldown.java)
|
[✔️](javascript%2F0309-best-time-to-buy-and-sell-stock-with-cooldown.js)
|
[✔️](kotlin%2F0309-best-time-to-buy-and-sell-stock-with-cooldown.kt)
|
[✔️](python%2F0309-best-time-to-buy-and-sell-stock-with-cooldown.py)
|
|
[✔️](rust%2F0309-best-time-to-buy-and-sell-stock-with-cooldown.rs)
|
|
[✔️](swift%2F0309-best-time-to-buy-and-sell-stock-with-cooldown.swift)
|
[✔️](typescript%2F0309-best-time-to-buy-and-sell-stock-with-cooldown.ts)
+[0518 - Coin Change II](https://leetcode.com/problems/coin-change-ii/) |
|
[✔️](c%2F0518-coin-change-ii.c)
|
[✔️](cpp%2F0518-coin-change-ii.cpp)
|
[✔️](csharp%2F0518-coin-change-ii.cs)
|
|
[✔️](go%2F0518-coin-change-ii.go)
|
|
[✔️](java%2F0518-coin-change-ii.java)
|
[✔️](javascript%2F0518-coin-change-ii.js)
|
[✔️](kotlin%2F0518-coin-change-ii.kt)
|
[✔️](python%2F0518-coin-change-ii.py)
|
|
[✔️](rust%2F0518-coin-change-ii.rs)
|
|
[✔️](swift%2F0518-coin-change-ii.swift)
|
[✔️](typescript%2F0518-coin-change-ii.ts)
+[0494 - Target Sum](https://leetcode.com/problems/target-sum/) |
|
[✔️](c%2F0494-target-sum.c)
|
[✔️](cpp%2F0494-target-sum.cpp)
|
[✔️](csharp%2F0494-target-sum.cs)
|
|
[✔️](go%2F0494-target-sum.go)
|
|
[✔️](java%2F0494-target-sum.java)
|
[✔️](javascript%2F0494-target-sum.js)
|
[✔️](kotlin%2F0494-target-sum.kt)
|
[✔️](python%2F0494-target-sum.py)
|
|
|
|
[✔️](swift%2F0494-target-sum.swift)
|
[✔️](typescript%2F0494-target-sum.ts)
+[0097 - Interleaving String](https://leetcode.com/problems/interleaving-string/) |
|
[✔️](c%2F0097-interleaving-string.c)
|
[✔️](cpp%2F0097-interleaving-string.cpp)
|
[✔️](csharp%2F0097-interleaving-string.cs)
|
|
[✔️](go%2F0097-interleaving-string.go)
|
|
[✔️](java%2F0097-interleaving-string.java)
|
[✔️](javascript%2F0097-interleaving-string.js)
|
[✔️](kotlin%2F0097-interleaving-string.kt)
|
[✔️](python%2F0097-interleaving-string.py)
|
|
|
[✔️](scala%2F0097-interleaving-string.scala)
|
[✔️](swift%2F0097-interleaving-string.swift)
|
[✔️](typescript%2F0097-interleaving-string.ts)
+[0877 - Stone Game](https://leetcode.com/problems/stone-game/) |
|
|
[✔️](cpp%2F0877-stone-game.cpp)
|
|
|
|
|
|
|
[✔️](kotlin%2F0877-stone-game.kt)
|
|
|
|
|
|
+[0064 - Minimum Path Sum](https://leetcode.com/problems/minimum-path-sum/) |
|
|
[✔️](cpp%2F0064-minimum-path-sum.cpp)
|
|
|
|
|
[✔️](java%2F0064-minimum-path-sum.java)
|
|
[✔️](kotlin%2F0064-minimum-path-sum.kt)
|
[✔️](python%2F0064-minimum-path-sum.py)
|
|
|
|
|
+[0329 - Longest Increasing Path In a Matrix](https://leetcode.com/problems/longest-increasing-path-in-a-matrix/) |
|
[✔️](c%2F0329-longest-increasing-path-in-a-matrix.c)
|
[✔️](cpp%2F0329-longest-increasing-path-in-a-matrix.cpp)
|
[✔️](csharp%2F0329-longest-increasing-path-in-a-matrix.cs)
|
|
|
|
[✔️](java%2F0329-longest-increasing-path-in-a-matrix.java)
|
[✔️](javascript%2F0329-longest-increasing-path-in-a-matrix.js)
|
[✔️](kotlin%2F0329-longest-increasing-path-in-a-matrix.kt)
|
[✔️](python%2F0329-longest-increasing-path-in-a-matrix.py)
|
|
[✔️](rust%2F0329-longest-increasing-path-in-a-matrix.rs)
|
|
[✔️](swift%2F0329-longest-increasing-path-in-a-matrix.swift)
|
+[0221 - Maximal Square](https://leetcode.com/problems/maximal-square/) |
|
|
[✔️](cpp%2F0221-maximal-square.cpp)
|
|
|
|
|
[✔️](java%2F0221-maximal-square.java)
|
|
[✔️](kotlin%2F0221-maximal-square.kt)
|
[✔️](python%2F0221-maximal-square.py)
|
|
|
|
|
+[0474 - Ones and Zeroes](https://leetcode.com/problems/ones-and-zeroes/) |
|
|
[✔️](cpp%2F0474-ones-and-zeroes.cpp)
|
|
|
|
|
[✔️](java%2F0474-ones-and-zeroes.java)
|
|
[✔️](kotlin%2F0474-ones-and-zeroes.kt)
|
[✔️](python%2F0474-ones-and-zeroes.py)
|
|
|
|
[✔️](swift%2F0474-ones-and-zeroes.swift)
|
[✔️](typescript%2F0474-ones-and-zeroes.ts)
+[5782 - Maximum Alternating Subsequence Sum](https://leetcode.com/problems/maximum-alternating-subsequence-sum/) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+[0115 - Distinct Subsequences](https://leetcode.com/problems/distinct-subsequences/) |
|
[✔️](c%2F0115-distinct-subsequences.c)
|
[✔️](cpp%2F0115-distinct-subsequences.cpp)
|
[✔️](csharp%2F0115-distinct-subsequences.cs)
|
|
|
|
[✔️](java%2F0115-distinct-subsequences.java)
|
[✔️](javascript%2F0115-distinct-subsequences.js)
|
[✔️](kotlin%2F0115-distinct-subsequences.kt)
|
[✔️](python%2F0115-distinct-subsequences.py)
|
|
|
|
[✔️](swift%2F0115-distinct-subsequences.swift)
|
[✔️](typescript%2F0115-distinct-subsequences.ts)
+[0072 - Edit Distance](https://leetcode.com/problems/edit-distance/) |
|
[✔️](c%2F0072-edit-distance.c)
|
[✔️](cpp%2F0072-edit-distance.cpp)
|
[✔️](csharp%2F0072-edit-distance.cs)
|
|
|
|
[✔️](java%2F0072-edit-distance.java)
|
[✔️](javascript%2F0072-edit-distance.js)
|
[✔️](kotlin%2F0072-edit-distance.kt)
|
[✔️](python%2F0072-edit-distance.py)
|
|
[✔️](rust%2F0072-edit-distance.rs)
|
[✔️](scala%2F0072-edit-distance.scala)
|
[✔️](swift%2F0072-edit-distance.swift)
|
[✔️](typescript%2F0072-edit-distance.ts)
+[1220 - Count Vowels Permutation](https://leetcode.com/problems/count-vowels-permutation/) |
|
|
[✔️](cpp%2F1220-count-vowels-permutation.cpp)
|
|
|
[✔️](go%2F1220-count-vowels-permutation.go)
|
|
[✔️](java%2F1220-count-vowels-permutation.java)
|
|
[✔️](kotlin%2F1220-count-vowels-permutation.kt)
|
[✔️](python%2F1220-count-vowels-permutation.py)
|
|
|
|
|
+[0312 - Burst Balloons](https://leetcode.com/problems/burst-balloons/) |
|
[✔️](c%2F0312-burst-balloons.c)
|
[✔️](cpp%2F0312-burst-balloons.cpp)
|
[✔️](csharp%2F0312-burst-balloons.cs)
|
|
|
|
[✔️](java%2F0312-burst-balloons.java)
|
[✔️](javascript%2F0312-burst-balloons.js)
|
[✔️](kotlin%2F0312-burst-balloons.kt)
|
[✔️](python%2F0312-burst-balloons.py)
|
|
|
|
[✔️](swift%2F0312-burst-balloons.swift)
|
[✔️](typescript%2F0312-burst-balloons.ts)
+[1866 - Number of Ways to Rearrange Sticks With K Sticks Visible](https://leetcode.com/problems/number-of-ways-to-rearrange-sticks-with-k-sticks-visible/) |
|
|
|
|
|
|
|
|
|
[✔️](kotlin%2F1866-number-of-ways-to-rearrange-sticks-with-k-sticks-visible.kt)
|
|
|
|
|
|
+[0010 - Regular Expression Matching](https://leetcode.com/problems/regular-expression-matching/) |
|
[✔️](c%2F0010-regular-expression-matching.c)
|
[✔️](cpp%2F0010-regular-expression-matching.cpp)
|
[✔️](csharp%2F0010-regular-expression-matching.cs)
|
|
[✔️](go%2F0010-regular-expression-matching.go)
|
|
[✔️](java%2F0010-regular-expression-matching.java)
|
[✔️](javascript%2F0010-regular-expression-matching.js)
|
[✔️](kotlin%2F0010-regular-expression-matching.kt)
|
[✔️](python%2F0010-regular-expression-matching.py)
|
|
|
|
[✔️](swift%2F0010-regular-expression-matching.swift)
|
[✔️](typescript%2F0010-regular-expression-matching.ts)
+[1140 - Stone Game II](https://leetcode.com/problems/stone-game-ii/) |
|
|
|
|
|
|
|
[✔️](java%2F1140-stone-game-ii.java)
|
|
[✔️](kotlin%2F1140-stone-game-ii.kt)
|
|
|
|
|
|
+[0926 - Flip String to Monotone Increasing](https://leetcode.com/problems/flip-string-to-monotone-increasing/) |
|
|
|
|
|
[✔️](go%2F0926-flip-string-to-monotone-increasing.go)
|
|
|
[✔️](javascript%2F0926-flip-string-to-monotone-increasing.js)
|
[✔️](kotlin%2F0926-flip-string-to-monotone-increasing.kt)
|
|
|
[✔️](rust%2F0926-flip-string-to-monotone-increasing.rs)
|
|
|
[✔️](typescript%2F0926-flip-string-to-monotone-increasing.ts)
+[2218 - Maximum Value of K Coins from Piles](https://leetcode.com/problems/maximum-value-of-k-coins-from-piles/) |
|
|
|
|
|
|
|
|
|
[✔️](kotlin%2F2218-maximum-value-of-k-coins-from-piles.kt)
|
|
|
|
|
|
+[0920 - Number of Music Playlists](https://leetcode.com/problems/number-of-music-playlists/) |
|
|
|
|
|
|
|
[✔️](java%2F0920-number-of-music-playlists.java)
|
|
[✔️](kotlin%2F0920-number-of-music-playlists.kt)
|
|
|
|
|
|
+[1639 - Number of Ways to Form a Target String Given a Dictionary](https://leetcode.com/problems/number-of-ways-to-form-a-target-string-given-a-dictionary/) |
|
|
|
|
|
|
|
|
|
[✔️](kotlin%2F1639-number-of-ways-to-form-a-target-string-given-a-dictionary.kt)
|
|
|
|
|
|
+[0879 - Profitable Schemes](https://leetcode.com/problems/profitable-schemes/) |
|
|
|
|
|
|
|
|
|
[✔️](kotlin%2F0879-profitable-schemes.kt)
|
|
|
|
|
|
+[1547 - Minimum Cost to Cut a Stick](https://leetcode.com/problems/minimum-cost-to-cut-a-stick/) |
|
|
|
|
|
|
|
|
|
[✔️](kotlin%2F1547-minimum-cost-to-cut-a-stick.kt)
|
|
|
|
|
|
+ +### Greedy + +Problem | articles | C | C++ | C# | Dart | GO | hints | Java | JS | Kotlin | Python | Ruby | Rust | Scala | Swift | TS +---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- +[0053 - Maximum Subarray](https://leetcode.com/problems/maximum-subarray/) |
|
[✔️](c%2F0053-maximum-subarray.c)
|
[✔️](cpp%2F0053-maximum-subarray.cpp)
|
[✔️](csharp%2F0053-maximum-subarray.cs)
|
|
[✔️](go%2F0053-maximum-subarray.go)
|
|
[✔️](java%2F0053-maximum-subarray.java)
|
[✔️](javascript%2F0053-maximum-subarray.js)
|
[✔️](kotlin%2F0053-maximum-subarray.kt)
|
[✔️](python%2F0053-maximum-subarray.py)
|
[✔️](ruby%2F0053-maximum-subarray.rb)
|
[✔️](rust%2F0053-maximum-subarray.rs)
|
|
[✔️](swift%2F0053-maximum-subarray.swift)
|
[✔️](typescript%2F0053-maximum-subarray.ts)
+[0918 - Maximum Sum Circular Subarray](https://leetcode.com/problems/maximum-sum-circular-subarray/) |
|
|
|
|
|
[✔️](go%2F0918-maximum-sum-circular-subarray.go)
|
|
[✔️](java%2F0918-maximum-sum-circular-subarray.java)
|
[✔️](javascript%2F0918-maximum-sum-circular-subarray.js)
|
[✔️](kotlin%2F0918-maximum-sum-circular-subarray.kt)
|
[✔️](python%2F0918-maximum-sum-circular-subarray.py)
|
|
[✔️](rust%2F0918-maximum-sum-circular-subarray.rs)
|
|
[✔️](swift%2F0918-maximum-sum-circular-subarray.swift)
|
[✔️](typescript%2F0918-maximum-sum-circular-subarray.ts)
+[0978 - Longest Turbulent Array](https://leetcode.com/problems/longest-turbulent-subarray/) |
|
|
|
|
|
|
|
|
[✔️](javascript%2F0978-longest-turbulent-subarray.js)
|
[✔️](kotlin%2F0978-longest-turbulent-subarray.kt)
|
[✔️](python%2F0978-longest-turbulent-subarray.py)
|
|
|
|
[✔️](swift%2F0978-longest-turbulent-subarray.swift)
|
+[0055 - Jump Game](https://leetcode.com/problems/jump-game/) |
|
[✔️](c%2F0055-jump-game.c)
|
[✔️](cpp%2F0055-jump-game.cpp)
|
[✔️](csharp%2F0055-jump-game.cs)
|
|
[✔️](go%2F0055-jump-game.go)
|
|
[✔️](java%2F0055-jump-game.java)
|
[✔️](javascript%2F0055-jump-game.js)
|
[✔️](kotlin%2F0055-jump-game.kt)
|
[✔️](python%2F0055-jump-game.py)
|
|
[✔️](rust%2F0055-jump-game.rs)
|
|
[✔️](swift%2F0055-jump-game.swift)
|
[✔️](typescript%2F0055-jump-game.ts)
+[0045 - Jump Game II](https://leetcode.com/problems/jump-game-ii/) |
|
[✔️](c%2F0045-jump-game-ii.c)
|
[✔️](cpp%2F0045-jump-game-ii.cpp)
|
[✔️](csharp%2F0045-jump-game-ii.cs)
|
|
[✔️](go%2F0045-jump-game-ii.go)
|
|
[✔️](java%2F0045-jump-game-ii.java)
|
[✔️](javascript%2F0045-jump-game-ii.js)
|
[✔️](kotlin%2F0045-jump-game-ii.kt)
|
[✔️](python%2F0045-jump-game-ii.py)
|
[✔️](ruby%2F0045-jump-game-ii.rb)
|
[✔️](rust%2F0045-jump-game-ii.rs)
|
|
[✔️](swift%2F0045-jump-game-ii.swift)
|
[✔️](typescript%2F0045-jump-game-ii.ts)
+[1871 - Jump Game VII](https://leetcode.com/problems/jump-game-vii/) |
|
|
|
|
|
|
|
|
|
[✔️](kotlin%2F1871-jump-game-vii.kt)
|
|
|
|
|
|
+[0134 - Gas Station](https://leetcode.com/problems/gas-station/) |
|
[✔️](c%2F0134-gas-station.c)
|
[✔️](cpp%2F0134-gas-station.cpp)
|
[✔️](csharp%2F0134-gas-station.cs)
|
|
[✔️](go%2F0134-gas-station.go)
|
|
[✔️](java%2F0134-gas-station.java)
|
[✔️](javascript%2F0134-gas-station.js)
|
[✔️](kotlin%2F0134-gas-station.kt)
|
[✔️](python%2F0134-gas-station.py)
|
[✔️](ruby%2F0134-gas-station.rb)
|
|
|
[✔️](swift%2F0134-gas-station.swift)
|
[✔️](typescript%2F0134-gas-station.ts)
+[0846 - Hand of Straights](https://leetcode.com/problems/hand-of-straights/) |
|
[✔️](c%2F0846-hand-of-straights.c)
|
[✔️](cpp%2F0846-hand-of-straights.cpp)
|
[✔️](csharp%2F0846-hand-of-straights.cs)
|
|
[✔️](go%2F0846-hand-of-straights.go)
|
|
[✔️](java%2F0846-hand-of-straights.java)
|
[✔️](javascript%2F0846-hand-of-straights.js)
|
[✔️](kotlin%2F0846-hand-of-straights.kt)
|
[✔️](python%2F0846-hand-of-straights.py)
|
[✔️](ruby%2F0846-hand-of-straights.rb)
|
|
|
[✔️](swift%2F0846-hand-of-straights.swift)
|
[✔️](typescript%2F0846-hand-of-straights.ts)
+[2439 - Minimize Maximum of Array](https://leetcode.com/problems/minimize-maximum-of-array/) |
|
|
|
|
|
|
|
|
[✔️](javascript%2F2439-minimize-maximum-of-array.js)
|
[✔️](kotlin%2F2439-minimize-maximum-of-array.kt)
|
|
|
|
|
|
+[0649 - Dota2 Senate](https://leetcode.com/problems/dota2-senate/) |
|
|
|
|
|
|
|
|
|
[✔️](kotlin%2F0649-dota2-senate.kt)
|
|
|
|
|
|
+[1423 - Maximum Points You Can Obtain From Cards](https://leetcode.com/problems/maximum-points-you-can-obtain-from-cards/) |
|
|
|
[✔️](csharp%2F1423-Maximum-Points-You-Can-Obtain-from-Cards.cs)
|
|
|
|
|
[✔️](javascript%2F1423-maximum-points-you-can-obtain-from-cards.js)
|
[✔️](kotlin%2F1423-maximum-points-you-can-obtain-from-cards.kt)
|
[✔️](python%2F1423-maximum-points-you-can-obtain-from-cards.py)
|
|
|
|
|
+[1899 - Merge Triplets to Form Target Triplet](https://leetcode.com/problems/merge-triplets-to-form-target-triplet/) |
|
[✔️](c%2F1899-merge-triplets-to-form-target-triplet.c)
|
[✔️](cpp%2F1899-merge-triplets-to-form-target-triplet.cpp)
|
[✔️](csharp%2F1899-Merge-Triplets-to-Form-Target-Triplet.cs)
|
|
|
|
[✔️](java%2F1899-merge-triplets-to-form-target-triplet.java)
|
[✔️](javascript%2F1899-merge-triplets-to-form-target-triplet.js)
|
[✔️](kotlin%2F1899-merge-triplets-to-form-target-triplet.kt)
|
[✔️](python%2F1899-merge-triplets-to-form-target-triplet.py)
|
[✔️](ruby%2F1899-merge-triplets-to-form-target-triplet.rb)
|
|
|
[✔️](swift%2F1899-Merge-Triplets-To-Form-Target-Triplet.swift)
|
[✔️](typescript%2F1899-Merge-Triplets-to-Form-Target-Triplet.ts)
+[0763 - Partition Labels](https://leetcode.com/problems/partition-labels/) |
|
[✔️](c%2F0763-partition-labels.c)
|
[✔️](cpp%2F0763-partition-labels.cpp)
|
[✔️](csharp%2F0763-partition-labels.cs)
|
|
[✔️](go%2F0763-partition-labels.go)
|
|
[✔️](java%2F0763-partition-labels.java)
|
[✔️](javascript%2F0763-partition-labels.js)
|
[✔️](kotlin%2F0763-partition-labels.kt)
|
[✔️](python%2F0763-partition-labels.py)
|
[✔️](ruby%2F0763-partition-labels.rb)
|
|
|
[✔️](swift%2F0763-partition-labels.swift)
|
+[0678 - Valid Parenthesis String](https://leetcode.com/problems/valid-parenthesis-string/) |
|
[✔️](c%2F0678-valid-parenthesis-string.c)
|
[✔️](cpp%2F0678-valid-parenthesis-string.cpp)
|
[✔️](csharp%2F0678-valid-parenthesis-string.cs)
|
|
[✔️](go%2F0678-valid-parenthesis-string.go)
|
|
[✔️](java%2F0678-valid-parenthesis-string.java)
|
[✔️](javascript%2F0678-valid-parenthesis-string.js)
|
[✔️](kotlin%2F0678-valid-parenthesis-string.kt)
|
[✔️](python%2F0678-valid-parenthesis-string.py)
|
|
[✔️](rust%2F0678-valid-parenthesis-string.rs)
|
|
[✔️](swift%2F0678-valid-parenthesis-string.swift)
|
[✔️](typescript%2F0678-valid-parenthesis-string.ts)
+[1921 - Eliminate Maximum Number of Monsters](https://leetcode.com/problems/eliminate-maximum-number-of-monsters/) |
|
|
[✔️](cpp%2F1921-eliminate-maximum-number-of-monsters.cpp)
|
|
|
|
|
[✔️](java%2F1921-eliminate-maximum-number-of-monsters.java)
|
[✔️](javascript%2F1921-eliminate-maximum-number-of-monsters.js)
|
[✔️](kotlin%2F1921-eliminate-maximum-number-of-monsters.kt)
|
|
|
|
|
|
+[1029 - Two City Scheduling](https://leetcode.com/problems/two-city-scheduling/) |
|
|
|
|
|
[✔️](go%2F1029-two-city-scheduling.go)
|
|
[✔️](java%2F1029-two-city-scheduling.java)
|
[✔️](javascript%2F1029-two-city-scheduling.js)
|
[✔️](kotlin%2F1029-two-city-scheduling.kt)
|
[✔️](python%2F1029-two-city-scheduling.py)
|
|
[✔️](rust%2F1029-two-city-scheduling.rs)
|
|
|
[✔️](typescript%2F1029-two-city-scheduling.ts)
+[0646 - Maximum Length of Pair Chain](https://leetcode.com/problems/maximum-length-of-pair-chain/) |
|
|
|
|
|
|
|
|
|
[✔️](kotlin%2F0646-maximum-length-of-pair-chain.kt)
|
|
|
|
|
|
+[1647 - Minimum Deletions to Make Character Frequencies Unique](https://leetcode.com/problems/minimum-deletions-to-make-character-frequencies-unique/) |
|
|
|
|
|
|
|
[✔️](java%2F1647-minimum-deletions-to-make-character-frequencies-unique.java)
|
[✔️](javascript%2F1647-minimum-deletions-to-make-character-frequencies-unique.js)
|
[✔️](kotlin%2F1647-minimum-deletions-to-make-character-frequencies-unique.kt)
|
|
|
|
|
|
+[135- - Candy](https://leetcode.com/problems/candy/) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+ +### Intervals + +Problem | articles | C | C++ | C# | Dart | GO | hints | Java | JS | Kotlin | Python | Ruby | Rust | Scala | Swift | TS +---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- +[0057 - Insert Interval](https://leetcode.com/problems/insert-interval/) |
|
[✔️](c%2F0057-insert-interval.c)
|
[✔️](cpp%2F0057-insert-interval.cpp)
|
[✔️](csharp%2F0057-insert-interval.cs)
|
|
[✔️](go%2F0057-insert-interval.go)
|
|
[✔️](java%2F0057-insert-interval.java)
|
[✔️](javascript%2F0057-insert-interval.js)
|
[✔️](kotlin%2F0057-insert-interval.kt)
|
[✔️](python%2F0057-insert-interval.py)
|
|
[✔️](rust%2F0057-insert-interval.rs)
|
|
[✔️](swift%2F0057-insert-interval.swift)
|
[✔️](typescript%2F0057-insert-interval.ts)
+[0056 - Merge Intervals](https://leetcode.com/problems/merge-intervals/) |
|
[✔️](c%2F0056-merge-intervals.c)
|
[✔️](cpp%2F0056-merge-intervals.cpp)
|
[✔️](csharp%2F0056-merge-intervals.cs)
|
|
[✔️](go%2F0056-merge-intervals.go)
|
|
[✔️](java%2F0056-merge-intervals.java)
|
[✔️](javascript%2F0056-merge-intervals.js)
|
[✔️](kotlin%2F0056-merge-intervals.kt)
|
[✔️](python%2F0056-merge-intervals.py)
|
|
[✔️](rust%2F0056-merge-intervals.rs)
|
[✔️](scala%2F0056-merge-intervals.scala)
|
[✔️](swift%2F0056-merge-intervals.swift)
|
[✔️](typescript%2F0056-merge-intervals.ts)
+[0435 - Non Overlapping Intervals](https://leetcode.com/problems/non-overlapping-intervals/) |
|
[✔️](c%2F0435-non-overlapping-intervals.c)
|
[✔️](cpp%2F0435-non-overlapping-intervals.cpp)
|
[✔️](csharp%2F0435-non-overlapping-intervals.cs)
|
|
[✔️](go%2F0435-non-overlapping-intervals.go)
|
|
[✔️](java%2F0435-non-overlapping-intervals.java)
|
[✔️](javascript%2F0435-non-overlapping-intervals.js)
|
[✔️](kotlin%2F0435-non-overlapping-intervals.kt)
|
[✔️](python%2F0435-non-overlapping-intervals.py)
|
|
[✔️](rust%2F0435-non-overlapping-intervals.rs)
|
[✔️](scala%2F0435-non-overlapping-intervals.scala)
|
[✔️](swift%2F0435-non-overlapping-intervals.swift)
|
[✔️](typescript%2F0435-non-overlapping-intervals.ts)
+[0252 - Meeting Rooms](https://leetcode.com/problems/meeting-rooms/) |
|
|
[✔️](cpp%2F0252-meeting-rooms.cpp)
|
[✔️](csharp%2F0252-meeting-rooms.cs)
|
|
[✔️](go%2F0252-meeting-rooms.go)
|
|
[✔️](java%2F0252-meeting-rooms.java)
|
[✔️](javascript%2F0252-meeting-rooms.js)
|
[✔️](kotlin%2F0252-meeting-rooms.kt)
|
[✔️](python%2F0252-meeting-rooms.py)
|
|
|
|
[✔️](swift%2F0252-meeting-rooms.swift)
|
+[0253 - Meeting Rooms II](https://leetcode.com/problems/meeting-rooms-ii/) |
|
|
[✔️](cpp%2F0253-meeting-rooms-ii.cpp)
|
[✔️](csharp%2F0253-meeting-rooms-ii.cs)
|
|
|
|
[✔️](java%2F0253-meeting-rooms-ii.java)
|
[✔️](javascript%2F0253-meeting-rooms-ii.js)
|
[✔️](kotlin%2F0253-meeting-rooms-ii.kt)
|
[✔️](python%2F0253-meeting-rooms-ii.py)
|
|
[✔️](rust%2F0253-meeting-rooms-ii.rs)
|
|
[✔️](swift%2F0253-meeting-rooms-ii.swift)
|
+[1288 - Remove Covered Intervals](https://leetcode.com/problems/remove-covered-intervals/) |
|
[✔️](c%2F1288-Remove-Covered-Intervals.c)
|
|
|
|
|
|
|
|
[✔️](kotlin%2F1288-remove-covered-intervals.kt)
|
[✔️](python%2F1288-remove-covered-intervals.py)
|
|
|
|
|
+[1851 - Minimum Interval to Include Each Query](https://leetcode.com/problems/minimum-interval-to-include-each-query/) |
|
|
[✔️](cpp%2F1851-Minimum-Interval-To-Include-Each-Query.cpp)
|
[✔️](csharp%2F1851-Minimum-Interval-to-Include-Each-Query.cs)
|
|
|
|
[✔️](java%2F1851-Minimum-Interval-to-Include-Each-Query.java)
|
[✔️](javascript%2F1851-minimum-interval-to-include-each-query.js)
|
[✔️](kotlin%2F1851-minimum-interval-to-include-each-query.kt)
|
[✔️](python%2F1851-minimum-interval-to-include-each-query.py)
|
|
|
|
[✔️](swift%2F1851-minimum-interval-to-include-each-query.swift)
|
+[0352 - Data Stream as Disjoint Intervals](https://leetcode.com/problems/data-stream-as-disjoint-intervals/) |
|
|
|
|
|
[✔️](go%2F0352-data-stream-as-disjoint-intervals.go)
|
|
|
[✔️](javascript%2F0352-data-stream-as-disjoint-intervals.js)
|
[✔️](kotlin%2F0352-data-stream-as-disjoint-intervals.kt)
|
|
|
[✔️](rust%2F0352-data-stream-as-disjoint-intervals.rs)
|
|
|
[✔️](typescript%2F0352-data-stream-as-disjoint-intervals.ts)
+ +### Math & Geometry + +Problem | articles | C | C++ | C# | Dart | GO | hints | Java | JS | Kotlin | Python | Ruby | Rust | Scala | Swift | TS +---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- +[0168 - Excel Sheet Column Title](https://leetcode.com/problems/excel-sheet-column-title/) |
|
|
|
[✔️](csharp%2F0168-excel-sheet-column-title.cs)
|
|
|
|
[✔️](java%2F0168-excel-sheet-column-title.java)
|
|
[✔️](kotlin%2F0168-excel-sheet-column-title.kt)
|
[✔️](python%2F0168-excel-sheet-column-title.py)
|
|
|
|
|
+[1071 - Greatest Common Divisor of Strings](https://leetcode.com/problems/greatest-common-divisor-of-strings/) |
|
|
[✔️](cpp%2F1071-greatest-common-divisor-of-strings.cpp)
|
|
|
[✔️](go%2F1071-greatest-common-divisor-of-strings.go)
|
|
[✔️](java%2F1071-greatest-common-divisor-of-strings.java)
|
[✔️](javascript%2F1071-greatest-common-divisor-of-strings.js)
|
[✔️](kotlin%2F1071-greatest-common-divisor-of-strings.kt)
|
|
|
[✔️](rust%2F1071-greatest-common-divisor-of-strings.rs)
|
|
|
[✔️](typescript%2F1071-greatest-common-divisor-of-strings.ts)
+[1523 - Count Odd Numbers in an Interval Range](https://leetcode.com/problems/count-odd-numbers-in-an-interval-range/) |
|
|
|
|
|
|
|
|
|
[✔️](kotlin%2F1523-count-odd-numbers-in-an-interval-range.kt)
|
[✔️](python%2F1523-count-odd-numbers-in-an-interval-range.py)
|
|
|
|
|
+[1572 - Matrix Diagonal Sum](https://leetcode.com/problems/matrix-diagonal-sum/) |
|
|
|
|
|
|
|
[✔️](java%2F1572-matrix-diagonal-sum.java)
|
[✔️](javascript%2F1572-matrix-diagonal-sum.js)
|
[✔️](kotlin%2F1572-matrix-diagonal-sum.kt)
|
[✔️](python%2F1572-matrix-diagonal-sum.py)
|
|
|
|
|
+[0149 - Maximum Points on a Line](https://leetcode.com/problems/max-points-on-a-line/) |
|
|
[✔️](cpp%2F0149-max-points-on-a-line.cpp)
|
|
|
|
|
[✔️](java%2F0149-max-points-on-a-line.java)
|
[✔️](javascript%2F0149-max-points-on-a-line.js)
|
[✔️](kotlin%2F0149-max-points-on-a-line.kt)
|
[✔️](python%2F0149-max-points-on-a-line.py)
|
|
[✔️](rust%2F0149-max-points-on-a-line.rs)
|
|
|
[✔️](typescript%2F0149-max-points-on-a-line.ts)
+[0048 - Rotate Image](https://leetcode.com/problems/rotate-image/) |
|
[✔️](c%2F0048-rotate-image.c)
|
[✔️](cpp%2F0048-rotate-image.cpp)
|
[✔️](csharp%2F0048-rotate-image.cs)
|
|
[✔️](go%2F0048-rotate-image.go)
|
|
[✔️](java%2F0048-rotate-image.java)
|
[✔️](javascript%2F0048-rotate-image.js)
|
[✔️](kotlin%2F0048-rotate-image.kt)
|
[✔️](python%2F0048-rotate-image.py)
|
[✔️](ruby%2F0048-rotate-image.rb)
|
[✔️](rust%2F0048-rotate-image.rs)
|
|
[✔️](swift%2F0048-rotate-image.swift)
|
[✔️](typescript%2F0048-rotate-image.ts)
+[0054 - Spiral Matrix](https://leetcode.com/problems/spiral-matrix/) |
|
[✔️](c%2F0054-spiral-matrix.c)
|
[✔️](cpp%2F0054-spiral-matrix.cpp)
|
[✔️](csharp%2F0054-spiral-matrix.cs)
|
|
[✔️](go%2F0054-spiral-matrix.go)
|
|
[✔️](java%2F0054-spiral-matrix.java)
|
[✔️](javascript%2F0054-spiral-matrix.js)
|
[✔️](kotlin%2F0054-spiral-matrix.kt)
|
[✔️](python%2F0054-spiral-matrix.py)
|
[✔️](ruby%2F0054-spiral-matrix.rb)
|
|
|
[✔️](swift%2F0054-spiral-matrix.swift)
|
[✔️](typescript%2F0054-spiral-matrix.ts)
+[0059 - Spiral Matrix II ](https://leetcode.com/problems/spiral-matrix-ii/) |
|
|
[✔️](cpp%2F0059-spiral-matrix-ii.cpp)
|
|
|
|
|
[✔️](java%2F0059-spiral-matrix-ii.java)
|
[✔️](javascript%2F0059-spiral-matrix-ii.js)
|
[✔️](kotlin%2F0059-spiral-matrix-ii.kt)
|
|
|
|
|
|
+[0073 - Set Matrix Zeroes](https://leetcode.com/problems/set-matrix-zeroes/) |
|
[✔️](c%2F0073-set-matrix-zeroes.c)
|
[✔️](cpp%2F0073-set-matrix-zeroes.cpp)
|
[✔️](csharp%2F0073-set-matrix-zeroes.cs)
|
|
[✔️](go%2F0073-set-matrix-zeroes.go)
|
|
[✔️](java%2F0073-set-matrix-zeroes.java)
|
[✔️](javascript%2F0073-set-matrix-zeroes.js)
|
[✔️](kotlin%2F0073-set-matrix-zeroes.kt)
|
[✔️](python%2F0073-set-matrix-zeroes.py)
|
[✔️](ruby%2F0073-set-matrix-zeroes.rb)
|
|
|
[✔️](swift%2F0073-set-matrix-zeroes.swift)
|
[✔️](typescript%2F0073-set-matrix-zeroes.ts)
+[0202 - Happy Number](https://leetcode.com/problems/happy-number/) |
|
[✔️](c%2F0202-happy-number.c)
|
[✔️](cpp%2F0202-happy-number.cpp)
|
[✔️](csharp%2F0202-happy-number.cs)
|
|
[✔️](go%2F0202-happy-number.go)
|
|
[✔️](java%2F0202-happy-number.java)
|
[✔️](javascript%2F0202-happy-number.js)
|
[✔️](kotlin%2F0202-happy-number.kt)
|
[✔️](python%2F0202-happy-number.py)
|
[✔️](ruby%2F0202-happy-number.rb)
|
[✔️](rust%2F0202-happy-number.rs)
|
|
[✔️](swift%2F0202-happy-number.swift)
|
[✔️](typescript%2F0202-happy-number.ts)
+[0066 - Plus One](https://leetcode.com/problems/plus-one/) |
|
[✔️](c%2F0066-plus-one.c)
|
[✔️](cpp%2F0066-plus-one.cpp)
|
[✔️](csharp%2F0066-plus-one.cs)
|
|
[✔️](go%2F0066-plus-one.go)
|
|
[✔️](java%2F0066-plus-one.java)
|
[✔️](javascript%2F0066-plus-one.js)
|
[✔️](kotlin%2F0066-plus-one.kt)
|
[✔️](python%2F0066-plus-one.py)
|
[✔️](ruby%2F0066-plus-one.rb)
|
[✔️](rust%2F0066-plus-one.rs)
|
|
[✔️](swift%2F0066-plus-one.swift)
|
[✔️](typescript%2F0066-plus-one.ts)
+[0009 - Palindrome Number](https://leetcode.com/problems/palindrome-number/) |
|
[✔️](c%2F0009-palindrome-number.c)
|
[✔️](cpp%2F0009-palindrome-number.cpp)
|
|
|
[✔️](go%2F0009-palindrome-number.go)
|
|
[✔️](java%2F0009-palindrome-number.java)
|
[✔️](javascript%2F0009-palindrome-number.js)
|
[✔️](kotlin%2F0009-palindrome-number.kt)
|
[✔️](python%2F0009-palindrome-number.py)
|
|
[✔️](rust%2F0009-palindrome-number.rs)
|
|
[✔️](swift%2F0009-palindrome-number.swift)
|
[✔️](typescript%2F0009-palindrome-number.ts)
+[0263 - Ugly Number](https://leetcode.com/problems/ugly-number/) |
|
[✔️](c%2F0263-ugly-number.c)
|
[✔️](cpp%2F0263-ugly-number.cpp)
|
|
|
[✔️](go%2F0263-ugly-number.go)
|
|
[✔️](java%2F0263-ugly-number.java)
|
[✔️](javascript%2F0263-ugly-number.js)
|
[✔️](kotlin%2F0263-ugly-number.kt)
|
[✔️](python%2F0263-ugly-number.py)
|
|
[✔️](rust%2F0263-ugly-number.rs)
|
|
[✔️](swift%2F0263-ugly-number.swift)
|
[✔️](typescript%2F0263-ugly-number.ts)
+[1260 - Shift 2D Grid](https://leetcode.com/problems/shift-2d-grid/) |
|
|
[✔️](cpp%2F1260-shift-2d-grid.cpp)
|
|
|
|
|
[✔️](java%2F1260-shift-2d-grid.java)
|
[✔️](javascript%2F1260-shift-2d-grid.js)
|
[✔️](kotlin%2F1260-shift-2d-grid.kt)
|
[✔️](python%2F1260-shift-2d-grid.py)
|
|
|
|
|
+[0013 - Roman to Integer](https://leetcode.com/problems/roman-to-integer/) |
|
[✔️](c%2F0013-roman-to-integer.c)
|
[✔️](cpp%2F0013-roman-to-integer.cpp)
|
|
|
[✔️](go%2F0013-roman-to-integer.go)
|
|
[✔️](java%2F0013-roman-to-integer.java)
|
[✔️](javascript%2F0013-roman-to-integer.js)
|
[✔️](kotlin%2F0013-roman-to-integer.kt)
|
[✔️](python%2F0013-roman-to-integer.py)
|
|
[✔️](rust%2F0013-roman-to-integer.rs)
|
|
|
[✔️](typescript%2F0013-roman-to-integer.ts)
+[0012 - Integer to Roman](https://leetcode.com/problems/integer-to-roman/) |
|
|
[✔️](cpp%2F0012-integer-to-roman.cpp)
|
|
|
[✔️](go%2F0012-integer-to-roman.go)
|
|
[✔️](java%2F0012-integer-to-roman.java)
|
[✔️](javascript%2F0012-integer-to-roman.js)
|
[✔️](kotlin%2F0012-integer-to-roman.kt)
|
[✔️](python%2F0012-integer-to-roman.py)
|
|
[✔️](rust%2F0012-integer-to-roman.rs)
|
|
|
[✔️](typescript%2F0012-integer-to-roman.ts)
+[0050 - Pow(x, n)](https://leetcode.com/problems/powx-n/) |
|
[✔️](c%2F0050-powx-n.c)
|
[✔️](cpp%2F0050-powx-n.cpp)
|
[✔️](csharp%2F0050-powx-n.cs)
|
|
|
|
[✔️](java%2F0050-powx-n.java)
|
[✔️](javascript%2F0050-powx-n.js)
|
[✔️](kotlin%2F0050-powx-n.kt)
|
[✔️](python%2F0050-powx-n.py)
|
[✔️](ruby%2F0050-powx-n.rb)
|
[✔️](rust%2F0050-powx-n.rs)
|
|
[✔️](swift%2F0050-powx-n.swift)
|
[✔️](typescript%2F0050-powx-n.ts)
+[0043 - Multiply Strings](https://leetcode.com/problems/multiply-strings/) |
|
[✔️](c%2F0043-multiply-strings.c)
|
[✔️](cpp%2F0043-multiply-strings.cpp)
|
[✔️](csharp%2F0043-multiply-strings.cs)
|
|
|
|
[✔️](java%2F0043-multiply-strings.java)
|
[✔️](javascript%2F0043-multiply-strings.js)
|
[✔️](kotlin%2F0043-multiply-strings.kt)
|
[✔️](python%2F0043-multiply-strings.py)
|
[✔️](ruby%2F0043-multiply-strings.rb)
|
[✔️](rust%2F0043-multiply-strings.rs)
|
|
[✔️](swift%2F0043-multiply-strings.swift)
|
[✔️](typescript%2F0043-multiply-strings.ts)
+[2013 - Detect Squares](https://leetcode.com/problems/detect-squares/) |
|
|
[✔️](cpp%2F2013-Detect-Squares.cpp)
|
[✔️](csharp%2F2013-Detect-Squares.cs)
|
|
|
|
[✔️](java%2F2013-Detect-Squares.java)
|
[✔️](javascript%2F2013-Detect-Squares.js)
|
[✔️](kotlin%2F2013-detect-squares.kt)
|
[✔️](python%2F2013-detect-squares.py)
|
[✔️](ruby%2F2013-detect-squares.rb)
|
[✔️](rust%2F2013-detect-squares.rs)
|
|
[✔️](swift%2F2013-detect-squares.swift)
|
+[1041 - Robot Bounded In Circle](https://leetcode.com/problems/robot-bounded-in-circle/) |
|
|
|
|
|
|
|
[✔️](java%2F1041-robot-bounded-in-circle.java)
|
|
[✔️](kotlin%2F1041-robot-bounded-in-circle.kt)
|
|
|
|
|
|
+[0006 - Zigzag Conversion](https://leetcode.com/problems/zigzag-conversion/) |
|
|
[✔️](cpp%2F0006-zigzag-conversion.cpp)
|
|
|
[✔️](go%2F0006-zigzag-conversion.go)
|
|
[✔️](java%2F0006-zigzag-conversion.java)
|
|
[✔️](kotlin%2F0006-zigzag-conversion.kt)
|
[✔️](python%2F0006-zigzag-conversion.py)
|
|
|
|
|
+[2028 - Find Missing Observations](https://leetcode.com/problems/find-missing-observations/) |
|
|
|
|
|
|
|
|
|
[✔️](kotlin%2F2028-find-missing-observations.kt)
|
|
|
|
|
|
+ +### Bit Manipulation + +Problem | articles | C | C++ | C# | Dart | GO | hints | Java | JS | Kotlin | Python | Ruby | Rust | Scala | Swift | TS +---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- +[0136 - Single Number](https://leetcode.com/problems/single-number/) |
|
[✔️](c%2F0136-single-number.c)
|
[✔️](cpp%2F0136-single-number.cpp)
|
[✔️](csharp%2F0136-single-number.cs)
|
[✔️](dart%2F0136-single-number.dart)
|
[✔️](go%2F0136-single-number.go)
|
|
[✔️](java%2F0136-single-number.java)
|
[✔️](javascript%2F0136-single-number.js)
|
[✔️](kotlin%2F0136-single-number.kt)
|
[✔️](python%2F0136-single-number.py)
|
[✔️](ruby%2F0136-single-number.rb)
|
[✔️](rust%2F0136-single-number.rs)
|
|
[✔️](swift%2F0136-single-number.swift)
|
[✔️](typescript%2F0136-single-number.ts)
+[0191 - Number of 1 Bits](https://leetcode.com/problems/number-of-1-bits/) |
|
[✔️](c%2F0191-number-of-1-bits.c)
|
[✔️](cpp%2F0191-number-of-1-bits.cpp)
|
[✔️](csharp%2F0191-number-of-1-bits.cs)
|
|
[✔️](go%2F0191-number-of-1-bits.go)
|
|
[✔️](java%2F0191-number-of-1-bits.java)
|
[✔️](javascript%2F0191-number-of-1-bits.js)
|
[✔️](kotlin%2F0191-number-of-1-bits.kt)
|
[✔️](python%2F0191-number-of-1-bits.py)
|
[✔️](ruby%2F0191-number-of-1-bits.rb)
|
[✔️](rust%2F0191-number-of-1-bits.rs)
|
|
[✔️](swift%2F0191-number-of-1-bits.swift)
|
[✔️](typescript%2F0191-number-of-1-bits.ts)
+[0338 - Counting Bits](https://leetcode.com/problems/counting-bits/) |
|
[✔️](c%2F0338-counting-bits.c)
|
[✔️](cpp%2F0338-counting-bits.cpp)
|
[✔️](csharp%2F0338-counting-bits.cs)
|
|
[✔️](go%2F0338-counting-bits.go)
|
|
[✔️](java%2F0338-counting-bits.java)
|
[✔️](javascript%2F0338-counting-bits.js)
|
[✔️](kotlin%2F0338-counting-bits.kt)
|
[✔️](python%2F0338-counting-bits.py)
|
[✔️](ruby%2F0338-counting-bits.rb)
|
[✔️](rust%2F0338-counting-bits.rs)
|
|
[✔️](swift%2F0338-counting-bits.swift)
|
[✔️](typescript%2F0338-counting-bits.ts)
+[0190 - Reverse Bits](https://leetcode.com/problems/reverse-bits/) |
|
[✔️](c%2F0190-reverse-bits.c)
|
[✔️](cpp%2F0190-reverse-bits.cpp)
|
[✔️](csharp%2F0190-reverse-bits.cs)
|
|
[✔️](go%2F0190-reverse-bits.go)
|
|
[✔️](java%2F0190-reverse-bits.java)
|
[✔️](javascript%2F0190-reverse-bits.js)
|
[✔️](kotlin%2F0190-reverse-bits.kt)
|
[✔️](python%2F0190-reverse-bits.py)
|
[✔️](ruby%2F0190-reverse-bits.rb)
|
[✔️](rust%2F0190-reverse-bits.rs)
|
|
[✔️](swift%2F0190-reverse-bits.swift)
|
[✔️](typescript%2F0190-reverse-bits.ts)
+[0268 - Missing Number](https://leetcode.com/problems/missing-number/) |
|
[✔️](c%2F0268-missing-number.c)
|
[✔️](cpp%2F0268-missing-number.cpp)
|
[✔️](csharp%2F0268-missing-number.cs)
|
|
[✔️](go%2F0268-missing-number.go)
|
|
[✔️](java%2F0268-missing-number.java)
|
[✔️](javascript%2F0268-missing-number.js)
|
[✔️](kotlin%2F0268-missing-number.kt)
|
[✔️](python%2F0268-missing-number.py)
|
[✔️](ruby%2F0268-missing-number.rb)
|
[✔️](rust%2F0268-missing-number.rs)
|
|
[✔️](swift%2F0268-missing-number.swift)
|
[✔️](typescript%2F0268-missing-number.ts)
+[1470 - Shuffle the Array](https://leetcode.com/problems/shuffle-the-array/) |
|
[✔️](c%2F1470-shuffle-the-array.c)
|
[✔️](cpp%2F1470-shuffle-the-array.cpp)
|
|
|
[✔️](go%2F1470-shuffle-the-array.go)
|
|
|
[✔️](javascript%2F1470-shuffle-the-array.js)
|
[✔️](kotlin%2F1470-shuffle-the-array.kt)
|
|
|
[✔️](rust%2F1470-shuffle-the-array.rs)
|
|
|
[✔️](typescript%2F1470-shuffle-the-array.ts)
+[0989 - Add to Array-Form of Integer](https://leetcode.com/problems/add-to-array-form-of-integer/) |
|
[✔️](c%2F0989-add-to-array-form-of-integer.c)
|
|
|
|
[✔️](go%2F0989-add-to-array-form-of-integer.go)
|
|
|
[✔️](javascript%2F0989-add-to-array-form-of-integer.js)
|
[✔️](kotlin%2F0989-add-to-array-form-of-integer.kt)
|
|
|
[✔️](rust%2F0989-add-to-array-form-of-integer.rs)
|
|
|
[✔️](typescript%2F0989-add-to-array-form-of-integer.ts)
+[0371 - Sum of Two Integers](https://leetcode.com/problems/sum-of-two-integers/) |
|
[✔️](c%2F0371-sum-of-two-integers.c)
|
[✔️](cpp%2F0371-sum-of-two-integers.cpp)
|
[✔️](csharp%2F0371-sum-of-two-integers.cs)
|
|
[✔️](go%2F0371-sum-of-two-integers.go)
|
|
[✔️](java%2F0371-sum-of-two-integers.java)
|
[✔️](javascript%2F0371-sum-of-two-integers.js)
|
[✔️](kotlin%2F0371-sum-of-two-integers.kt)
|
[✔️](python%2F0371-sum-of-two-integers.py)
|
[✔️](ruby%2F0371-sum-of-two-integers.rb)
|
[✔️](rust%2F0371-sum-of-two-integers.rs)
|
|
[✔️](swift%2F0371-sum-of-two-integers.swift)
|
[✔️](typescript%2F0371-sum-of-two-integers.ts)
+[0007 - Reverse Integer](https://leetcode.com/problems/reverse-integer/) |
|
[✔️](c%2F0007-reverse-integer.c)
|
[✔️](cpp%2F0007-reverse-integer.cpp)
|
[✔️](csharp%2F0007-reverse-integer.cs)
|
|
[✔️](go%2F0007-reverse-integer.go)
|
|
[✔️](java%2F0007-reverse-integer.java)
|
[✔️](javascript%2F0007-reverse-integer.js)
|
[✔️](kotlin%2F0007-reverse-integer.kt)
|
[✔️](python%2F0007-reverse-integer.py)
|
[✔️](ruby%2F0007-reverse-integer.rb)
|
[✔️](rust%2F0007-reverse-integer.rs)
|
[✔️](scala%2F0007-reverse-integer.scala)
|
[✔️](swift%2F0007-reverse-integer.swift)
|
[✔️](typescript%2F0007-reverse-integer.ts)
+[0067 - Add Binary](https://leetcode.com/problems/add-binary/) |
|
[✔️](c%2F0067-add-binary.c)
|
[✔️](cpp%2F0067-Add-Binary.cpp)
|
|
|
|
|
[✔️](java%2F0067-Add-Binary.java)
|
[✔️](javascript%2F0067-add-binary.js)
|
[✔️](kotlin%2F0067-add-binary.kt)
|
[✔️](python%2F0067-add-binary.py)
|
|
[✔️](rust%2F0067-add-binary.rs)
|
|
|
[✔️](typescript%2F0067-add-binary.ts)
+ +### JavaScript + +Problem | articles | C | C++ | C# | Dart | GO | hints | Java | JS | Kotlin | Python | Ruby | Rust | Scala | Swift | TS +---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- +[2667 - Create Hello World Function](https://leetcode.com/problems/create-hello-world-function/) |
|
|
|
|
|
|
|
|
[✔️](javascript%2F2667-create-hello-world-function.js)
|
|
|
|
|
|
|
[✔️](typescript%2F2667-create-hello-world-function.ts)
+[2620 - Counter](https://leetcode.com/problems/counter/) |
|
|
|
|
|
|
|
|
[✔️](javascript%2F2620-counter.js)
|
|
|
|
|
|
|
[✔️](typescript%2F2620-counter.ts)
+[2665 - Counter II](https://leetcode.com/problems/counter-ii/) |
|
|
|
|
|
|
|
|
[✔️](javascript%2F2665-counter-ii.js)
|
|
|
|
|
|
|
+[2635 - Apply Transform over each Element in Array](https://leetcode.com/problems/apply-transform-over-each-element-in-array/) |
|
|
|
|
|
|
|
|
[✔️](javascript%2F2635-apply-transform-over-each-element-in-array.js)
|
|
|
|
|
|
|
+[2634 - Filter Elements from Array](https://leetcode.com/problems/filter-elements-from-array/) |
|
|
|
|
|
|
|
|
[✔️](javascript%2F2634-filter-elements-from-array.js)
|
|
|
|
|
|
|
+[2626 - Array Reduce Transformation](https://leetcode.com/problems/array-reduce-transformation/) |
|
|
|
|
|
|
|
|
[✔️](javascript%2F2626-array-reduce-transformation.js)
|
|
|
|
|
|
|
+[2629 - Function Composition](https://leetcode.com/problems/function-composition/) |
|
|
|
|
|
|
|
|
[✔️](javascript%2F2629-function-composition.js)
|
|
|
|
|
|
|
+[2666 - Allow One Function Call](https://leetcode.com/problems/allow-one-function-call/) |
|
|
|
|
|
|
|
|
[✔️](javascript%2F2666-allow-one-function-call.js)
|
|
|
|
|
|
|
+[2623 - Memoize](https://leetcode.com/problems/memoize/) |
|
|
|
|
|
|
|
|
[✔️](javascript%2F2623-memoize.js)
|
|
|
|
|
|
|
+[2632 - Curry](https://leetcode.com/problems/curry/) |
|
|
|
|
|
|
|
|
[✔️](javascript%2F2632-curry.js)
|
|
|
|
|
|
|
+[2621 - Sleep](https://leetcode.com/problems/sleep/) |
|
|
|
|
|
|
|
|
[✔️](javascript%2F2621-sleep.js)
|
|
|
|
|
|
|
+[2637 - Promise Time Limit](https://leetcode.com/problems/promise-time-limit/) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+[2636 - Promise Pool](https://leetcode.com/problems/promise-pool/) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+[2622 - Cache With Time Limit](https://leetcode.com/problems/cache-with-time-limit/) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+[2627 - Debounce](https://leetcode.com/problems/debounce/) |
|
|
|
|
|
|
|
|
[✔️](javascript%2F2627-debounce.js)
|
|
|
|
|
|
|
+[2676 - Throttle](https://leetcode.com/problems/throttle/) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+[2628 - JSON Deep Equal](https://leetcode.com/problems/json-deep-equal/) |
|
|
|
|
|
|
|
|
[✔️](javascript%2F2628-json-deep-equal.js)
|
|
|
|
|
|
|
+[2633 - Convert Object to JSON String](https://leetcode.com/problems/convert-object-to-json-string/) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+[2675 - Array of Objects to Matrix](https://leetcode.com/problems/array-of-objects-to-matrix/) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+[2700 - Difference Between Two Objects](https://leetcode.com/problems/differences-between-two-objects/) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+[2677 - Chunk Array](https://leetcode.com/problems/chunk-array/) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+[2625 - Flatten Deeply Nested Array](https://leetcode.com/problems/flatten-deeply-nested-array/) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+[2619 - Array Prototype Last](https://leetcode.com/problems/array-prototype-last/) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+[2631 - Group By](https://leetcode.com/problems/group-by/) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+[2618 - Check if Object Instance of Class](https://leetcode.com/problems/check-if-object-instance-of-class/) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+[2693 - Call Function with Custom Context](https://leetcode.com/problems/call-function-with-custom-context/) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+[2694 - Event Emitter](https://leetcode.com/problems/event-emitter/) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+[2695 - Array Wrapper](https://leetcode.com/problems/array-wrapper/) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+[2648 - Generate Fibonacci Sequence](https://leetcode.com/problems/generate-fibonacci-sequence/) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+[2649 - Nested Array Generator](https://leetcode.com/problems/nested-array-generator/) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+ + + +--- + +Need to update the README? [Update the template instead.](README_template.md) diff --git a/README_template.md b/README_template.md new file mode 100644 index 000000000..177477379 --- /dev/null +++ b/README_template.md @@ -0,0 +1,31 @@ +# Leetcode solutions for 🚀 [NeetCode.io](https://neetcode.io) +> This repo hosts the solutions found on [NeetCode.io](https://neetcode.io) including the solutions shown on the [NeetCode YouTube channel](https://www.youtube.com/c/neetcode). The site will periodically be updated with new solutions from this repo! + +
+ +Solutions from these languages will be linked from [NeetCode.io](https://neetcode.io): +> Python, Java, JavaScript, C++, Go, Swift, C#, TypeScript, Rust, Kotlin, Ruby, C, Scala and Dart + +Solutions are also welcome for any other *supported* language on leetcode.com! + +## Contributing +**Please read the [contributing guidlines](./CONTRIBUTING.md) before opening a PR** + + +To contribute, please fork this repo and open a PR adding a [missing solution](#missing-solutions) from the supported languages. + +If you would like to have collaborator permissions on the repo to merge your own PRs or review others' PRs please let me know. + +## Credits + + + + + +## Missing Solutions + + + +--- + +Need to update the README? [Update the template instead.](README_template.md) diff --git a/articles/132-pattern.md b/articles/132-pattern.md new file mode 100644 index 000000000..b0dc31957 --- /dev/null +++ b/articles/132-pattern.md @@ -0,0 +1,420 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def find132pattern(self, nums: List[int]) -> bool: + n = len(nums) + + for k in range(2, n): + for j in range(k - 1, 0, -1): + if nums[j] <= nums[k]: + continue + + for i in range(j - 1, -1, -1): + if nums[i] < nums[k]: + return True + + return False +``` + +```java +public class Solution { + public boolean find132pattern(int[] nums) { + int n = nums.length; + + for (int k = 2; k < n; k++) { + for (int j = k - 1; j > 0; j--) { + if (nums[j] <= nums[k]) { + continue; + } + + for (int i = j - 1; i >= 0; i--) { + if (nums[i] < nums[k]) { + return true; + } + } + } + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool find132pattern(vector& nums) { + int n = nums.size(); + + for (int k = 2; k < n; k++) { + for (int j = k - 1; j > 0; j--) { + if (nums[j] <= nums[k]) { + continue; + } + + for (int i = j - 1; i >= 0; i--) { + if (nums[i] < nums[k]) { + return true; + } + } + } + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + find132pattern(nums) { + let n = nums.length; + + for (let k = 2; k < n; k++) { + for (let j = k - 1; j > 0; j--) { + if (nums[j] <= nums[k]) { + continue; + } + + for (let i = j - 1; i >= 0; i--) { + if (nums[i] < nums[k]) { + return true; + } + } + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Stack + +::tabs-start + +```python +class Solution: + def find132pattern(self, nums: List[int]) -> bool: + stack = [] # pair [num, minLeft], mono decreasing + curMin = nums[0] + + for i in range(1, len(nums)): + while stack and nums[i] >= stack[-1][0]: + stack.pop() + if stack and nums[i] > stack[-1][1]: + return True + + stack.append([nums[i], curMin]) + curMin = min(curMin, nums[i]) + + return False +``` + +```java +public class Solution { + public boolean find132pattern(int[] nums) { + Stack stack = new Stack<>(); // pair [num, minLeft] + int curMin = nums[0]; + + for (int i = 1; i < nums.length; i++) { + while (!stack.isEmpty() && nums[i] >= stack.peek()[0]) { + stack.pop(); + } + if (!stack.isEmpty() && nums[i] > stack.peek()[1]) { + return true; + } + + stack.push(new int[]{nums[i], curMin}); + curMin = Math.min(curMin, nums[i]); + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool find132pattern(vector& nums) { + stack> stack; // pair + int curMin = nums[0]; + + for (int i = 1; i < nums.size(); i++) { + while (!stack.empty() && nums[i] >= stack.top().first) { + stack.pop(); + } + if (!stack.empty() && nums[i] > stack.top().second) { + return true; + } + + stack.push({nums[i], curMin}); + curMin = min(curMin, nums[i]); + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + find132pattern(nums) { + const stack = []; // pair [num, minLeft] + let curMin = nums[0]; + + for (let i = 1; i < nums.length; i++) { + while (stack.length > 0 && nums[i] >= stack[stack.length - 1][0]) { + stack.pop(); + } + if (stack.length > 0 && nums[i] > stack[stack.length - 1][1]) { + return true; + } + + stack.push([nums[i], curMin]); + curMin = Math.min(curMin, nums[i]); + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Stack (Optimal) + +::tabs-start + +```python +class Solution: + def find132pattern(self, nums: List[int]) -> bool: + stack, k = [], float('-inf') + + for i in range(len(nums) - 1, -1, -1): + if nums[i] < k: + return True + + while stack and stack[-1] < nums[i]: + k = stack.pop() + stack.append(nums[i]) + + return False +``` + +```java +public class Solution { + public boolean find132pattern(int[] nums) { + Stack stack = new Stack<>(); + int k = Integer.MIN_VALUE; + + for (int i = nums.length - 1; i >= 0; i--) { + if (nums[i] < k) { + return true; + } + + while (!stack.isEmpty() && stack.peek() < nums[i]) { + k = stack.pop(); + } + stack.push(nums[i]); + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool find132pattern(vector& nums) { + stack stack; + int k = INT_MIN; + + for (int i = nums.size() - 1; i >= 0; i--) { + if (nums[i] < k) { + return true; + } + + while (!stack.empty() && stack.top() < nums[i]) { + k = stack.top(); + stack.pop(); + } + stack.push(nums[i]); + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + find132pattern(nums) { + const stack = []; + let k = -Infinity; + + for (let i = nums.length - 1; i >= 0; i--) { + if (nums[i] < k) { + return true; + } + + while (stack.length > 0 && stack[stack.length - 1] < nums[i]) { + k = stack.pop(); + } + stack.push(nums[i]); + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Two Pointers + +::tabs-start + +```python +class Solution: + def find132pattern(self, nums: List[int]) -> bool: + n = len(nums) + stkTop, k = n, float('-inf') + + for i in range(n - 1, -1, -1): + if nums[i] < k: + return True + + while stkTop < n and nums[i] > nums[stkTop]: + k = nums[stkTop] + stkTop += 1 + + stkTop -= 1 + nums[stkTop] = nums[i] + + return False +``` + +```java +public class Solution { + public boolean find132pattern(int[] nums) { + int n = nums.length; + int stkTop = n; + int k = Integer.MIN_VALUE; + + for (int i = n - 1; i >= 0; i--) { + if (nums[i] < k) { + return true; + } + + while (stkTop < n && nums[i] > nums[stkTop]) { + k = nums[stkTop++]; + } + + nums[--stkTop] = nums[i]; + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool find132pattern(vector& nums) { + int n = nums.size(); + int stkTop = n; + int k = INT_MIN; + + for (int i = n - 1; i >= 0; i--) { + if (nums[i] < k) { + return true; + } + + while (stkTop < n && nums[i] > nums[stkTop]) { + k = nums[stkTop++]; + } + + nums[--stkTop] = nums[i]; + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + find132pattern(nums) { + const n = nums.length; + let stkTop = n; + let k = -Infinity; + + for (let i = n - 1; i >= 0; i--) { + if (nums[i] < k) { + return true; + } + + while (stkTop < n && nums[i] > nums[stkTop]) { + k = nums[stkTop++]; + } + + nums[--stkTop] = nums[i]; + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/4sum.md b/articles/4sum.md new file mode 100644 index 000000000..b590cf39e --- /dev/null +++ b/articles/4sum.md @@ -0,0 +1,810 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def fourSum(self, nums: List[int], target: int) -> List[List[int]]: + n = len(nums) + nums.sort() + res = set() + + for a in range(n): + for b in range(a + 1, n): + for c in range(b + 1, n): + for d in range(c + 1, n): + if nums[a] + nums[b] + nums[c] + nums[d] == target: + res.add((nums[a], nums[b], nums[c], nums[d])) + return list(res) +``` + +```java +public class Solution { + public List> fourSum(int[] nums, int target) { + int n = nums.length; + Arrays.sort(nums); + Set> res = new HashSet<>(); + + for (int a = 0; a < n; a++) { + for (int b = a + 1; b < n; b++) { + for (int c = b + 1; c < n; c++) { + for (int d = c + 1; d < n; d++) { + if (nums[a] + nums[b] + 0L + nums[c] + nums[d] == target) { + res.add(Arrays.asList(nums[a], nums[b], nums[c], nums[d])); + } + } + } + } + } + + return new ArrayList<>(res); + } +} +``` + +```cpp +class Solution { +public: + vector> fourSum(vector& nums, int target) { + int n = nums.size(); + sort(nums.begin(), nums.end()); + set> res; + + for (int a = 0; a < n; a++) { + for (int b = a + 1; b < n; b++) { + for (int c = b + 1; c < n; c++) { + for (int d = c + 1; d < n; d++) { + if (nums[a] + nums[b] + 0LL + nums[c] + nums[d] == target) { + res.insert({nums[a], nums[b], nums[c], nums[d]}); + } + } + } + } + } + + return vector>(res.begin(), res.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number[][]} + */ + fourSum(nums, target) { + let n = nums.length; + nums.sort((a, b) => a - b); + let res = new Set(); + + for (let a = 0; a < n; a++) { + for (let b = a + 1; b < n; b++) { + for (let c = b + 1; c < n; c++) { + for (let d = c + 1; d < n; d++) { + if (nums[a] + nums[b] + nums[c] + nums[d] === target) { + res.add(JSON.stringify([nums[a], nums[b], nums[c], nums[d]])); + } + } + } + } + } + + return Array.from(res).map(JSON.parse); + } +} +``` + +```csharp +public class Solution { + public List> FourSum(int[] nums, int target) { + int n = nums.Length; + Array.Sort(nums); + HashSet<(int, int, int, int)> res = new HashSet<(int, int, int, int)>(); + + for (int a = 0; a < n; a++) { + for (int b = a + 1; b < n; b++) { + for (int c = b + 1; c < n; c++) { + for (int d = c + 1; d < n; d++) { + long sum = (long)nums[a] + nums[b] + nums[c] + nums[d]; + if (sum == target) { + res.Add((nums[a], nums[b], nums[c], nums[d])); + } + } + } + } + } + + var result = new List>(); + foreach (var quad in res) { + result.Add(new List { quad.Item1, quad.Item2, quad.Item3, quad.Item4 }); + } + return result; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 4)$ +* Space complexity: $O(m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the number of quadruplets. + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def fourSum(self, nums: List[int], target: int) -> List[List[int]]: + nums.sort() + count = defaultdict(int) + for num in nums: + count[num] += 1 + + res = [] + for i in range(len(nums)): + count[nums[i]] -= 1 + if i > 0 and nums[i] == nums[i - 1]: + continue + + for j in range(i + 1, len(nums)): + count[nums[j]] -= 1 + if j > i + 1 and nums[j] == nums[j - 1]: + continue + + for k in range(j + 1, len(nums)): + count[nums[k]] -= 1 + if k > j + 1 and nums[k] == nums[k - 1]: + continue + + fourth = target - (nums[i] + nums[j] + nums[k]) + if count[fourth] > 0: + res.append([nums[i], nums[j], nums[k], fourth]) + + for k in range(j + 1, len(nums)): + count[nums[k]] += 1 + + for j in range(i + 1, len(nums)): + count[nums[j]] += 1 + + return res +``` + +```java +public class Solution { + public List> fourSum(int[] nums, int target) { + Arrays.sort(nums); + Map count = new HashMap<>(); + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + List> res = new ArrayList<>(); + + for (int i = 0; i < nums.length; i++) { + count.put(nums[i], count.get(nums[i]) - 1); + if (i > 0 && nums[i] == nums[i - 1]) continue; + + for (int j = i + 1; j < nums.length; j++) { + count.put(nums[j], count.get(nums[j]) - 1); + if (j > i + 1 && nums[j] == nums[j - 1]) continue; + + for (int k = j + 1; k < nums.length; k++) { + count.put(nums[k], count.get(nums[k]) - 1); + if (k > j + 1 && nums[k] == nums[k - 1]) continue; + + long fourth = target - (nums[i] + nums[j] + 0L + nums[k]); + if (fourth > Integer.MAX_VALUE || fourth < Integer.MIN_VALUE) { + continue; + } + if (count.getOrDefault((int) fourth, 0) > 0) { + res.add(Arrays.asList(nums[i], nums[j], nums[k], (int) fourth)); + } + } + + for (int k = j + 1; k < nums.length; k++) { + count.put(nums[k], count.get(nums[k]) + 1); + } + } + + for (int j = i + 1; j < nums.length; j++) { + count.put(nums[j], count.get(nums[j]) + 1); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> fourSum(vector& nums, int target) { + sort(nums.begin(), nums.end()); + unordered_map count; + for (int num : nums) { + count[num]++; + } + vector> res; + + for (int i = 0; i < nums.size(); i++) { + count[nums[i]]--; + if (i > 0 && nums[i] == nums[i - 1]) continue; + + for (int j = i + 1; j < nums.size(); j++) { + count[nums[j]]--; + if (j > i + 1 && nums[j] == nums[j - 1]) continue; + + for (int k = j + 1; k < nums.size(); k++) { + count[nums[k]]--; + if (k > j + 1 && nums[k] == nums[k - 1]) continue; + + long long fourth = target - (nums[i] + nums[j] + 0LL + nums[k]); + if (fourth < INT_MIN || fourth > INT_MAX) continue; + if (count[fourth] > 0) { + res.push_back({nums[i], nums[j], nums[k], int(fourth)}); + } + } + + for (int k = j + 1; k < nums.size(); k++) { + count[nums[k]]++; + } + } + + for (int j = i + 1; j < nums.size(); j++) { + count[nums[j]]++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number[][]} + */ + fourSum(nums, target) { + nums.sort((a, b) => a - b); + const count = new Map(); + for (const num of nums) { + count.set(num, (count.get(num) || 0) + 1); + } + const res = []; + + for (let i = 0; i < nums.length; i++) { + count.set(nums[i], count.get(nums[i]) - 1); + if (i > 0 && nums[i] === nums[i - 1]) continue; + + for (let j = i + 1; j < nums.length; j++) { + count.set(nums[j], count.get(nums[j]) - 1); + if (j > i + 1 && nums[j] === nums[j - 1]) continue; + + for (let k = j + 1; k < nums.length; k++) { + count.set(nums[k], count.get(nums[k]) - 1); + if (k > j + 1 && nums[k] === nums[k - 1]) continue; + + const fourth = target - (nums[i] + nums[j] + nums[k]); + if ((count.get(fourth) || 0) > 0) { + res.push([nums[i], nums[j], nums[k], fourth]); + } + } + + for (let k = j + 1; k < nums.length; k++) { + count.set(nums[k], count.get(nums[k]) + 1); + } + } + + for (let j = i + 1; j < nums.length; j++) { + count.set(nums[j], count.get(nums[j]) + 1); + } + } + + return res; + } +} +``` + +```csharp +public class Solution { + public List> FourSum(int[] nums, int target) { + Array.Sort(nums); + Dictionary count = new Dictionary(); + + foreach (int num in nums) { + if (!count.ContainsKey(num)) { + count[num] = 0; + } + count[num]++; + } + + List> res = new List>(); + + for (int i = 0; i < nums.Length; i++) { + count[nums[i]]--; + if (i > 0 && nums[i] == nums[i - 1]) continue; + + for (int j = i + 1; j < nums.Length; j++) { + count[nums[j]]--; + if (j > i + 1 && nums[j] == nums[j - 1]) continue; + + for (int k = j + 1; k < nums.Length; k++) { + count[nums[k]]--; + if (k > j + 1 && nums[k] == nums[k - 1]) continue; + + long fourth = (long)target - (long)nums[i] - (long)nums[j] - (long)nums[k]; + if (fourth > int.MaxValue || fourth < int.MinValue) { + continue; + } + + if (count.ContainsKey((int)fourth) && count[(int)fourth] > 0) { + res.Add(new List { nums[i], nums[j], nums[k], (int)fourth }); + } + } + + for (int k = j + 1; k < nums.Length; k++) { + count[nums[k]]++; + } + } + + for (int j = i + 1; j < nums.Length; j++) { + count[nums[j]]++; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: + * $O(n)$ space for the hash map. + * $O(m)$ space for the output array. + +> Where $n$ is the size of the array $nums$ and $m$ is the number of quadruplets. + +--- + +## 3. Two Pointers + +::tabs-start + +```python +class Solution: + def fourSum(self, nums: List[int], target: int) -> List[List[int]]: + nums.sort() + n = len(nums) + res = [] + + for i in range(n): + if i > 0 and nums[i] == nums[i - 1]: + continue + for j in range(i + 1, n): + if j > i + 1 and nums[j] == nums[j - 1]: + continue + left, right = j + 1, n - 1 + while left < right: + total = nums[i] + nums[j] + nums[left] + nums[right] + if total == target: + res.append([nums[i], nums[j], nums[left], nums[right]]) + left += 1 + right -= 1 + while left < right and nums[left] == nums[left - 1]: + left += 1 + while left < right and nums[right] == nums[right + 1]: + right -= 1 + elif total < target: + left += 1 + else: + right -= 1 + + return res +``` + +```java +public class Solution { + public List> fourSum(int[] nums, int target) { + Arrays.sort(nums); + List> res = new ArrayList<>(); + int n = nums.length; + + for (int i = 0; i < n; i++) { + if (i > 0 && nums[i] == nums[i - 1]) continue; + + for (int j = i + 1; j < n; j++) { + if (j > i + 1 && nums[j] == nums[j - 1]) continue; + + int left = j + 1, right = n - 1; + while (left < right) { + long sum = (long) nums[i] + nums[j] + nums[left] + nums[right]; + if (sum == target) { + res.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right])); + left++; + right--; + while (left < right && nums[left] == nums[left - 1]) left++; + while (left < right && nums[right] == nums[right + 1]) right--; + } else if (sum < target) { + left++; + } else { + right--; + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> fourSum(vector& nums, int target) { + vector> res; + int n = nums.size(); + sort(nums.begin(), nums.end()); + + for (int i = 0; i < n; i++) { + if (i > 0 && nums[i] == nums[i - 1]) continue; + + for (int j = i + 1; j < n; j++) { + if (j > i + 1 && nums[j] == nums[j - 1]) continue; + + int left = j + 1, right = n - 1; + while (left < right) { + long long sum = (long long) nums[i] + nums[j] + nums[left] + nums[right]; + if (sum == target) { + res.push_back({nums[i], nums[j], nums[left], nums[right]}); + left++; + right--; + while (left < right && nums[left] == nums[left - 1]) left++; + while (left < right && nums[right] == nums[right + 1]) right--; + } else if (sum < target) { + left++; + } else { + right--; + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number[][]} + */ + fourSum(nums, target) { + nums.sort((a, b) => a - b); + const res = []; + const n = nums.length; + + for (let i = 0; i < n; i++) { + if (i > 0 && nums[i] === nums[i - 1]) continue; + + for (let j = i + 1; j < n; j++) { + if (j > i + 1 && nums[j] === nums[j - 1]) continue; + + let left = j + 1, right = n - 1; + while (left < right) { + const sum = nums[i] + nums[j] + nums[left] + nums[right]; + if (sum === target) { + res.push([nums[i], nums[j], nums[left], nums[right]]); + left++; + right--; + while (left < right && nums[left] === nums[left - 1]) left++; + while (left < right && nums[right] === nums[right + 1]) right--; + } else if (sum < target) { + left++; + } else { + right--; + } + } + } + } + + return res; + } +} +``` + +```csharp +public class Solution { + public List> FourSum(int[] nums, int target) { + Array.Sort(nums); + List> res = new List>(); + int n = nums.Length; + + for (int i = 0; i < n; i++) { + if (i > 0 && nums[i] == nums[i - 1]) continue; + + for (int j = i + 1; j < n; j++) { + if (j > i + 1 && nums[j] == nums[j - 1]) continue; + + int left = j + 1, right = n - 1; + while (left < right) { + long sum = (long)nums[i] + nums[j] + nums[left] + nums[right]; + if (sum == target) { + res.Add(new List { nums[i], nums[j], nums[left], nums[right] }); + left++; + right--; + while (left < right && nums[left] == nums[left - 1]) left++; + while (left < right && nums[right] == nums[right + 1]) right--; + } else if (sum < target) { + left++; + } else { + right--; + } + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: + * $O(1)$ or $O(n)$ space depending on the sorting algorithm. + * $O(m)$ space for the output array. + +--- + +## 4. K-Sum + Two Pointers + +::tabs-start + +```python +class Solution: + def fourSum(self, nums: List[int], target: int) -> List[List[int]]: + nums.sort() + res, quad = [], [] + + def kSum(k, start, target): + if k == 2: + l, r = start, len(nums) - 1 + while l < r: + if nums[l] + nums[r] < target: + l += 1 + elif nums[l] + nums[r] > target: + r -= 1 + else: + res.append(quad + [nums[l], nums[r]]) + l += 1 + r -= 1 + while l < r and nums[l] == nums[l - 1]: + l += 1 + while l < r and nums[r] == nums[r + 1]: + r -= 1 + return + + for i in range(start, len(nums) - k + 1): + if i > start and nums[i] == nums[i - 1]: + continue + quad.append(nums[i]) + kSum(k - 1, i + 1, target - nums[i]) + quad.pop() + + kSum(4, 0, target) + return res +``` + +```java +public class Solution { + private List> res; + private List quad; + + public List> fourSum(int[] nums, int target) { + Arrays.sort(nums); + res = new ArrayList<>(); + quad = new ArrayList<>(); + kSum(nums, 4, 0, target); + return res; + } + + private void kSum(int[] nums, int k, int start, long target) { + if (k == 2) { + int l = start, r = nums.length - 1; + while (l < r) { + long sum = nums[l] + nums[r]; + if (sum < target) { + l++; + } else if (sum > target) { + r--; + } else { + res.add(new ArrayList<>(quad)); + res.get(res.size() - 1).add(nums[l]); + res.get(res.size() - 1).add(nums[r]); + l++; + r--; + while (l < r && nums[l] == nums[l - 1]) l++; + while (l < r && nums[r] == nums[r + 1]) r--; + } + } + return; + } + + for (int i = start; i < nums.length - k + 1; i++) { + if (i > start && nums[i] == nums[i - 1]) continue; + quad.add(nums[i]); + kSum(nums, k - 1, i + 1, target - nums[i]); + quad.remove(quad.size() - 1); + } + } +} +``` + +```cpp +class Solution { + vector> res; + vector quad; + +public: + vector> fourSum(vector& nums, int target) { + if (nums.size() < 4) return {}; + sort(nums.begin(), nums.end()); + kSum(nums, 4, 0, (long long) target); + return res; + } + +private: + void kSum(vector& nums, int k, int start, long long target) { + if (k == 2) { + int l = start, r = nums.size() - 1; + while (l < r) { + long long sum = (long long) nums[l] + nums[r]; + if (sum < target) { + l++; + } else if (sum > target) { + r--; + } else { + quad.push_back(nums[l]); + quad.push_back(nums[r]); + res.push_back(quad); + quad.pop_back(); + quad.pop_back(); + l++; + r--; + while (l < r && nums[l] == nums[l - 1]) l++; + while (l < r && nums[r] == nums[r + 1]) r--; + } + } + return; + } + + for (int i = start; i < nums.size() - k + 1; i++) { + if (i > start && nums[i] == nums[i - 1]) continue; + quad.push_back(nums[i]); + kSum(nums, k - 1, i + 1, target - nums[i]); + quad.pop_back(); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number[][]} + */ + fourSum(nums, target) { + nums.sort((a, b) => a - b); + const res = []; + const quad = []; + + const kSum = (k, start, target) => { + if (k === 2) { + let l = start, r = nums.length - 1; + while (l < r) { + const sum = nums[l] + nums[r]; + if (sum < target) { + l++; + } else if (sum > target) { + r--; + } else { + res.push([...quad, nums[l], nums[r]]); + l++; + r--; + while (l < r && nums[l] === nums[l - 1]) l++; + while (l < r && nums[r] === nums[r + 1]) r--; + } + } + return; + } + + for (let i = start; i < nums.length - k + 1; i++) { + if (i > start && nums[i] === nums[i - 1]) continue; + quad.push(nums[i]); + kSum(k - 1, i + 1, target - nums[i]); + quad.pop(); + } + }; + + kSum(4, 0, target); + return res; + } +} +``` + +```csharp +public class Solution { + private List> res; + private List quad; + + public List> FourSum(int[] nums, int target) { + Array.Sort(nums); + res = new List>(); + quad = new List(); + KSum(nums, 4, 0, target); + return res; + } + + private void KSum(int[] nums, int k, int start, long target) { + if (k == 2) { + int l = start, r = nums.Length - 1; + while (l < r) { + long sum = (long)nums[l] + nums[r]; + if (sum < target) { + l++; + } else if (sum > target) { + r--; + } else { + List newQuad = new List(quad); + newQuad.Add(nums[l]); + newQuad.Add(nums[r]); + res.Add(newQuad); + l++; + r--; + while (l < r && nums[l] == nums[l - 1]) l++; + while (l < r && nums[r] == nums[r + 1]) r--; + } + } + return; + } + + for (int i = start; i < nums.Length - k + 1; i++) { + if (i > start && nums[i] == nums[i - 1]) continue; + quad.Add(nums[i]); + KSum(nums, k - 1, i + 1, target - nums[i]); + quad.RemoveAt(quad.Count - 1); + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: + * $O(1)$ or $O(n)$ space depending on the sorting algorithm. + * $O(m)$ space for the output array. \ No newline at end of file diff --git a/articles/README.md b/articles/README.md new file mode 100644 index 000000000..79d65db56 --- /dev/null +++ b/articles/README.md @@ -0,0 +1,6 @@ +# Article Guide + +1. Each article should be written in markdown. Exanple: [duplicate-integer.md](https://github.com/neetcode-gh/leetcode/blob/main/articles/duplicate-integer.md) +2. At least one solution should be similar to a solution present in the NeetCode video. Ideally all solutions from the video are present. +3. Add time & space complexity +4. Cover all relevant solutions if possible. diff --git a/articles/accounts-merge.md b/articles/accounts-merge.md new file mode 100644 index 000000000..15b8e9881 --- /dev/null +++ b/articles/accounts-merge.md @@ -0,0 +1,1165 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def accountsMerge(self, accounts: List[List[str]]) -> List[List[str]]: + n = len(accounts) + emailIdx = {} # email -> id + emails = [] # set of emails of all accounts + emailToAcc = {} # email_index -> account_Id + + m = 0 + for accId, a in enumerate(accounts): + for i in range(1, len(a)): + email = a[i] + if email in emailIdx: + continue + emails.append(email) + emailIdx[email] = m + emailToAcc[m] = accId + m += 1 + + adj = [[] for _ in range(m)] + for a in accounts: + for i in range(2, len(a)): + id1 = emailIdx[a[i]] + id2 = emailIdx[a[i - 1]] + adj[id1].append(id2) + adj[id2].append(id1) + + emailGroup = defaultdict(list) # index of acc -> list of emails + visited = [False] * m + def dfs(node, accId): + visited[node] = True + emailGroup[accId].append(emails[node]) + for nei in adj[node]: + if not visited[nei]: + dfs(nei, accId) + + for i in range(m): + if not visited[i]: + dfs(i, emailToAcc[i]) + + res = [] + for accId in emailGroup: + name = accounts[accId][0] + res.append([name] + sorted(emailGroup[accId])) + + return res +``` + +```java +public class Solution { + private Map emailIdx = new HashMap<>(); // email -> id + private List emails = new ArrayList<>(); // set of emails of all accounts + private Map emailToAcc = new HashMap<>(); // email_index -> account_Id + private List> adj; + private Map> emailGroup = new HashMap<>(); // index of acc -> list of emails + private boolean[] visited; + + public List> accountsMerge(List> accounts) { + int n = accounts.size(); + int m = 0; + + // Build email index and mappings + for (int accId = 0; accId < n; accId++) { + List account = accounts.get(accId); + for (int i = 1; i < account.size(); i++) { + String email = account.get(i); + if (!emailIdx.containsKey(email)) { + emails.add(email); + emailIdx.put(email, m); + emailToAcc.put(m, accId); + m++; + } + } + } + + // Build adjacency list + adj = new ArrayList<>(); + for (int i = 0; i < m; i++) { + adj.add(new ArrayList<>()); + } + for (List account : accounts) { + for (int i = 2; i < account.size(); i++) { + int id1 = emailIdx.get(account.get(i)); + int id2 = emailIdx.get(account.get(i - 1)); + adj.get(id1).add(id2); + adj.get(id2).add(id1); + } + } + + // Initialize visited array + visited = new boolean[m]; + + // DFS traversal + for (int i = 0; i < m; i++) { + if (!visited[i]) { + int accId = emailToAcc.get(i); + emailGroup.putIfAbsent(accId, new ArrayList<>()); + dfs(i, accId); + } + } + + // Build result + List> res = new ArrayList<>(); + for (int accId : emailGroup.keySet()) { + List group = emailGroup.get(accId); + Collections.sort(group); + List merged = new ArrayList<>(); + merged.add(accounts.get(accId).get(0)); + merged.addAll(group); + res.add(merged); + } + + return res; + } + + private void dfs(int node, int accId) { + visited[node] = true; + emailGroup.get(accId).add(emails.get(node)); + for (int neighbor : adj.get(node)) { + if (!visited[neighbor]) { + dfs(neighbor, accId); + } + } + } +} +``` + +```cpp +class Solution { + unordered_map emailIdx; // email -> id + vector emails; // set of emails of all accounts + unordered_map emailToAcc; // email_index -> account_Id + vector> adj; + unordered_map> emailGroup; // index of acc -> list of emails + vector visited; + +public: + vector> accountsMerge(vector>& accounts) { + int n = accounts.size(); + int m = 0; + + // Build email index and mappings + for (int accId = 0; accId < n; accId++) { + vector& account = accounts[accId]; + for (int i = 1; i < account.size(); i++) { + string& email = account[i]; + if (emailIdx.find(email) == emailIdx.end()) { + emails.push_back(email); + emailIdx[email] = m; + emailToAcc[m] = accId; + m++; + } + } + } + + // Build adjacency list + adj.resize(m); + for (auto& account : accounts) { + for (int i = 2; i < account.size(); i++) { + int id1 = emailIdx[account[i]]; + int id2 = emailIdx[account[i - 1]]; + adj[id1].push_back(id2); + adj[id2].push_back(id1); + } + } + + visited.resize(m, false); + // DFS traversal + for (int i = 0; i < m; i++) { + if (!visited[i]) { + int accId = emailToAcc[i]; + dfs(i, accId); + } + } + + // Build result + vector> res; + for (auto& [accId, group] : emailGroup) { + sort(group.begin(), group.end()); + vector merged; + merged.push_back(accounts[accId][0]); + merged.insert(merged.end(), group.begin(), group.end()); + res.push_back(merged); + } + + return res; + } + +private: + void dfs(int node, int& accId) { + visited[node] = true; + emailGroup[accId].push_back(emails[node]); + for (int& neighbor : adj[node]) { + if (!visited[neighbor]) { + dfs(neighbor, accId); + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[][]} accounts + * @return {string[][]} + */ + accountsMerge(accounts) { + const emailIdx = new Map(); // email -> id + const emails = []; // set of emails of all accounts + const emailToAcc = new Map(); // email_index -> account_Id + const adj = []; + const emailGroup = new Map(); // index of acc -> list of emails + let visited = []; + + const n = accounts.length; + let m = 0; + + // Build email index and mappings + for (let accId = 0; accId < n; accId++) { + const account = accounts[accId]; + for (let i = 1; i < account.length; i++) { + const email = account[i]; + if (!emailIdx.has(email)) { + emails.push(email); + emailIdx.set(email, m); + emailToAcc.set(m, accId); + m++; + } + } + } + + // Build adjacency list + for (let i = 0; i < m; i++) { + adj.push([]); + } + for (const account of accounts) { + for (let i = 2; i < account.length; i++) { + const id1 = emailIdx.get(account[i]); + const id2 = emailIdx.get(account[i - 1]); + adj[id1].push(id2); + adj[id2].push(id1); + } + } + + // Initialize visited array + visited = Array(m).fill(false); + + // DFS traversal + const dfs = (node, accId) => { + visited[node] = true; + emailGroup.get(accId).push(emails[node]); + for (const neighbor of adj[node]) { + if (!visited[neighbor]) { + dfs(neighbor, accId); + } + } + }; + + for (let i = 0; i < m; i++) { + if (!visited[i]) { + const accId = emailToAcc.get(i); + if (!emailGroup.has(accId)) { + emailGroup.set(accId, []); + } + dfs(i, accId); + } + } + + // Build result + const res = []; + for (const [accId, group] of emailGroup.entries()) { + group.sort(); + const merged = [accounts[accId][0], ...group]; + res.push(merged); + } + + return res; + } +} +``` + +```csharp +public class Solution { + private Dictionary emailIdx = new Dictionary(); + private List emails = new List(); + private List> adj; + private bool[] visited; + private Dictionary> components = new Dictionary>(); + private Dictionary componentName = new Dictionary(); + + public List> AccountsMerge(List> accounts) { + int m = 0; + + for (int accId = 0; accId < accounts.Count; accId++) { + var account = accounts[accId]; + for (int i = 1; i < account.Count; i++) { + string email = account[i]; + if (!emailIdx.ContainsKey(email)) { + emailIdx[email] = m++; + emails.Add(email); + } + } + } + + adj = new List>(); + for (int i = 0; i < m; i++) adj.Add(new List()); + + foreach (var account in accounts) { + for (int i = 2; i < account.Count; i++) { + int u = emailIdx[account[i - 1]]; + int v = emailIdx[account[i]]; + adj[u].Add(v); + adj[v].Add(u); + } + } + + visited = new bool[m]; + + foreach (var account in accounts) { + string name = account[0]; + foreach (var email in account.Skip(1)) { + int idx = emailIdx[email]; + if (!visited[idx]) { + components[idx] = new List(); + componentName[idx] = name; + Dfs(idx, idx); + } + } + } + + var res = new List>(); + foreach (var kvp in components) { + var group = kvp.Value; + group.Sort(StringComparer.Ordinal); + var merged = new List { componentName[kvp.Key] }; + merged.AddRange(group); + res.Add(merged); + } + + return res; + } + + private void Dfs(int node, int root) { + visited[node] = true; + components[root].Add(emails[node]); + foreach (int nei in adj[node]) { + if (!visited[nei]) { + Dfs(nei, root); + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((n * m)\log (n * m))$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of accounts and $m$ is the number of emails. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def accountsMerge(self, accounts: List[List[str]]) -> List[List[str]]: + n = len(accounts) + emailIdx = {} # email -> id + emails = [] # set of emails of all accounts + emailToAcc = {} # email_index -> account_Id + + m = 0 + for accId, a in enumerate(accounts): + for i in range(1, len(a)): + email = a[i] + if email in emailIdx: + continue + emails.append(email) + emailIdx[email] = m + emailToAcc[m] = accId + m += 1 + + adj = [[] for _ in range(m)] + for a in accounts: + for i in range(2, len(a)): + id1 = emailIdx[a[i]] + id2 = emailIdx[a[i - 1]] + adj[id1].append(id2) + adj[id2].append(id1) + + emailGroup = defaultdict(list) # index of acc -> list of emails + visited = [False] * m + + def bfs(start, accId): + queue = deque([start]) + visited[start] = True + while queue: + node = queue.popleft() + emailGroup[accId].append(emails[node]) + for nei in adj[node]: + if not visited[nei]: + visited[nei] = True + queue.append(nei) + + for i in range(m): + if not visited[i]: + bfs(i, emailToAcc[i]) + + res = [] + for accId in emailGroup: + name = accounts[accId][0] + res.append([name] + sorted(emailGroup[accId])) + + return res +``` + +```java +public class Solution { + private Map emailIdx = new HashMap<>(); // email -> id + private List emails = new ArrayList<>(); // set of emails of all accounts + private Map emailToAcc = new HashMap<>(); // email_index -> account_Id + private List> adj; + private Map> emailGroup = new HashMap<>(); // index of acc -> list of emails + private boolean[] visited; + + public List> accountsMerge(List> accounts) { + int n = accounts.size(); + int m = 0; + + // Build email index and mappings + for (int accId = 0; accId < n; accId++) { + List account = accounts.get(accId); + for (int i = 1; i < account.size(); i++) { + String email = account.get(i); + if (!emailIdx.containsKey(email)) { + emails.add(email); + emailIdx.put(email, m); + emailToAcc.put(m, accId); + m++; + } + } + } + + // Build adjacency list + adj = new ArrayList<>(); + for (int i = 0; i < m; i++) { + adj.add(new ArrayList<>()); + } + for (List account : accounts) { + for (int i = 2; i < account.size(); i++) { + int id1 = emailIdx.get(account.get(i)); + int id2 = emailIdx.get(account.get(i - 1)); + adj.get(id1).add(id2); + adj.get(id2).add(id1); + } + } + + // Initialize visited array + visited = new boolean[m]; + + // BFS traversal + for (int i = 0; i < m; i++) { + if (!visited[i]) { + int accId = emailToAcc.get(i); + emailGroup.putIfAbsent(accId, new ArrayList<>()); + bfs(i, accId); + } + } + + // Build result + List> res = new ArrayList<>(); + for (int accId : emailGroup.keySet()) { + List group = emailGroup.get(accId); + Collections.sort(group); + List merged = new ArrayList<>(); + merged.add(accounts.get(accId).get(0)); + merged.addAll(group); + res.add(merged); + } + + return res; + } + + private void bfs(int start, int accId) { + Queue queue = new LinkedList<>(); + queue.offer(start); + visited[start] = true; + + while (!queue.isEmpty()) { + int node = queue.poll(); + emailGroup.get(accId).add(emails.get(node)); + for (int neighbor : adj.get(node)) { + if (!visited[neighbor]) { + visited[neighbor] = true; + queue.offer(neighbor); + } + } + } + } +} +``` + +```cpp +class Solution { + unordered_map emailIdx; // email -> id + vector emails; // set of emails of all accounts + unordered_map emailToAcc; // email_index -> account_Id + vector> adj; + unordered_map> emailGroup; // index of acc -> list of emails + vector visited; + +public: + vector> accountsMerge(vector>& accounts) { + int n = accounts.size(); + int m = 0; + + // Build email index and mappings + for (int accId = 0; accId < n; accId++) { + vector& account = accounts[accId]; + for (int i = 1; i < account.size(); i++) { + string& email = account[i]; + if (emailIdx.find(email) == emailIdx.end()) { + emails.push_back(email); + emailIdx[email] = m; + emailToAcc[m] = accId; + m++; + } + } + } + + // Build adjacency list + adj.resize(m); + for (auto& account : accounts) { + for (int i = 2; i < account.size(); i++) { + int id1 = emailIdx[account[i]]; + int id2 = emailIdx[account[i - 1]]; + adj[id1].push_back(id2); + adj[id2].push_back(id1); + } + } + + visited.resize(m, false); + // BFS traversal + for (int i = 0; i < m; i++) { + if (!visited[i]) { + int accId = emailToAcc[i]; + bfs(i, accId); + } + } + + // Build result + vector> res; + for (auto& [accId, group] : emailGroup) { + sort(group.begin(), group.end()); + vector merged; + merged.push_back(accounts[accId][0]); + merged.insert(merged.end(), group.begin(), group.end()); + res.push_back(merged); + } + + return res; + } + +private: + void bfs(int start, int accId) { + queue q; + q.push(start); + visited[start] = true; + + while (!q.empty()) { + int node = q.front(); + q.pop(); + emailGroup[accId].push_back(emails[node]); + for (int& neighbor : adj[node]) { + if (!visited[neighbor]) { + visited[neighbor] = true; + q.push(neighbor); + } + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[][]} accounts + * @return {string[][]} + */ + accountsMerge(accounts) { + const emailIdx = new Map(); // email -> id + const emails = []; // set of emails of all accounts + const emailToAcc = new Map(); // email_index -> account_Id + const adj = []; + const emailGroup = new Map(); // index of acc -> list of emails + let visited = []; + + const n = accounts.length; + let m = 0; + + // Build email index and mappings + for (let accId = 0; accId < n; accId++) { + const account = accounts[accId]; + for (let i = 1; i < account.length; i++) { + const email = account[i]; + if (!emailIdx.has(email)) { + emails.push(email); + emailIdx.set(email, m); + emailToAcc.set(m, accId); + m++; + } + } + } + + // Build adjacency list + for (let i = 0; i < m; i++) { + adj.push([]); + } + for (const account of accounts) { + for (let i = 2; i < account.length; i++) { + const id1 = emailIdx.get(account[i]); + const id2 = emailIdx.get(account[i - 1]); + adj[id1].push(id2); + adj[id2].push(id1); + } + } + + // Initialize visited array + visited = Array(m).fill(false); + + // BFS traversal + const bfs = (start, accId) => { + const queue = new Queue([start]); + visited[start] = true; + + while (!queue.isEmpty()) { + const node = queue.pop(); + emailGroup.get(accId).push(emails[node]); + for (const neighbor of adj[node]) { + if (!visited[neighbor]) { + visited[neighbor] = true; + queue.push(neighbor); + } + } + } + }; + + for (let i = 0; i < m; i++) { + if (!visited[i]) { + const accId = emailToAcc.get(i); + if (!emailGroup.has(accId)) { + emailGroup.set(accId, []); + } + bfs(i, accId); + } + } + + // Build result + const res = []; + for (const [accId, group] of emailGroup.entries()) { + group.sort(); + const merged = [accounts[accId][0], ...group]; + res.push(merged); + } + + return res; + } +} +``` + +```csharp +public class Solution { + public List> AccountsMerge(List> accounts) { + int n = accounts.Count; + Dictionary emailIdx = new Dictionary(); + List emails = new List(); + Dictionary emailToAcc = new Dictionary(); + + int m = 0; + for (int accId = 0; accId < n; accId++) { + var account = accounts[accId]; + for (int i = 1; i < account.Count; i++) { + string email = account[i]; + if (!emailIdx.ContainsKey(email)) { + emailIdx[email] = m; + emails.Add(email); + emailToAcc[m] = accId; + m++; + } + } + } + + List> adj = new List>(); + for (int i = 0; i < m; i++) adj.Add(new List()); + + foreach (var account in accounts) { + for (int i = 2; i < account.Count; i++) { + int id1 = emailIdx[account[i]]; + int id2 = emailIdx[account[i - 1]]; + adj[id1].Add(id2); + adj[id2].Add(id1); + } + } + + Dictionary> emailGroup = new Dictionary>(); + bool[] visited = new bool[m]; + + void Bfs(int start, int accId) { + Queue queue = new Queue(); + queue.Enqueue(start); + visited[start] = true; + + if (!emailGroup.ContainsKey(accId)) + emailGroup[accId] = new List(); + + while (queue.Count > 0) { + int node = queue.Dequeue(); + emailGroup[accId].Add(emails[node]); + + foreach (int nei in adj[node]) { + if (!visited[nei]) { + visited[nei] = true; + queue.Enqueue(nei); + } + } + } + } + + for (int i = 0; i < m; i++) { + if (!visited[i]) { + Bfs(i, emailToAcc[i]); + } + } + + List> res = new List>(); + foreach (var kvp in emailGroup) { + int accId = kvp.Key; + string name = accounts[accId][0]; + List merged = new List { name }; + kvp.Value.Sort(StringComparer.Ordinal); + merged.AddRange(kvp.Value); + res.Add(merged); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((n * m)\log (n * m))$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of accounts and $m$ is the number of emails. + +--- + +## 3. Disjoint Set Union + +::tabs-start + +```python +class UnionFind: + def __init__(self, n): + self.par = [i for i in range(n)] + self.rank = [1] * n + + def find(self, x): + while x != self.par[x]: + self.par[x] = self.par[self.par[x]] + x = self.par[x] + return x + + def union(self, x1, x2): + p1, p2 = self.find(x1), self.find(x2) + if p1 == p2: + return False + if self.rank[p1] > self.rank[p2]: + self.par[p2] = p1 + self.rank[p1] += self.rank[p2] + else: + self.par[p1] = p2 + self.rank[p2] += self.rank[p1] + return True + +class Solution: + def accountsMerge(self, accounts: List[List[str]]) -> List[List[str]]: + uf = UnionFind(len(accounts)) + emailToAcc = {} # email -> index of acc + + for i, a in enumerate(accounts): + for e in a[1:]: + if e in emailToAcc: + uf.union(i, emailToAcc[e]) + else: + emailToAcc[e] = i + + emailGroup = defaultdict(list) # index of acc -> list of emails + for e, i in emailToAcc.items(): + leader = uf.find(i) + emailGroup[leader].append(e) + + res = [] + for i, emails in emailGroup.items(): + name = accounts[i][0] + res.append([name] + sorted(emailGroup[i])) + return res +``` + +```java +class UnionFind { + private int[] parent; + private int[] rank; + + public UnionFind(int n) { + parent = new int[n]; + rank = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + rank[i] = 1; + } + } + + public int find(int x) { + if (x != parent[x]) { + parent[x] = find(parent[x]); + } + return parent[x]; + } + + public boolean union(int x1, int x2) { + int p1 = find(x1); + int p2 = find(x2); + if (p1 == p2) { + return false; + } + if (rank[p1] > rank[p2]) { + parent[p2] = p1; + rank[p1] += rank[p2]; + } else { + parent[p1] = p2; + rank[p2] += rank[p1]; + } + return true; + } +} + +public class Solution { + public List> accountsMerge(List> accounts) { + int n = accounts.size(); + UnionFind uf = new UnionFind(n); + Map emailToAcc = new HashMap<>(); // email -> index of acc + + // Build union-find structure + for (int i = 0; i < n; i++) { + List account = accounts.get(i); + for (int j = 1; j < account.size(); j++) { + String email = account.get(j); + if (emailToAcc.containsKey(email)) { + uf.union(i, emailToAcc.get(email)); + } else { + emailToAcc.put(email, i); + } + } + } + + // Group emails by leader account + Map> emailGroup = new HashMap<>(); // index of acc -> list of emails + for (Map.Entry entry : emailToAcc.entrySet()) { + String email = entry.getKey(); + int accId = entry.getValue(); + int leader = uf.find(accId); + emailGroup.putIfAbsent(leader, new ArrayList<>()); + emailGroup.get(leader).add(email); + } + + // Build result + List> res = new ArrayList<>(); + for (Map.Entry> entry : emailGroup.entrySet()) { + int accId = entry.getKey(); + List emails = entry.getValue(); + Collections.sort(emails); + List merged = new ArrayList<>(); + merged.add(accounts.get(accId).get(0)); // Add account name + merged.addAll(emails); + res.add(merged); + } + + return res; + } +} +``` + +```cpp +class UnionFind { + vector parent; + vector rank; + +public: + UnionFind(int n) { + parent.resize(n); + rank.resize(n, 1); + for (int i = 0; i < n; i++) { + parent[i] = i; + } + } + + int find(int x) { + if (x != parent[x]) { + parent[x] = find(parent[x]); + } + return parent[x]; + } + + bool unionSets(int x1, int x2) { + int p1 = find(x1); + int p2 = find(x2); + if (p1 == p2) { + return false; + } + if (rank[p1] > rank[p2]) { + parent[p2] = p1; + rank[p1] += rank[p2]; + } else { + parent[p1] = p2; + rank[p2] += rank[p1]; + } + return true; + } +}; + +class Solution { +public: + vector> accountsMerge(vector>& accounts) { + int n = accounts.size(); + UnionFind uf(n); + unordered_map emailToAcc; // email -> index of acc + + // Build union-find structure + for (int i = 0; i < n; i++) { + for (int j = 1; j < accounts[i].size(); j++) { + const string& email = accounts[i][j]; + if (emailToAcc.count(email)) { + uf.unionSets(i, emailToAcc[email]); + } else { + emailToAcc[email] = i; + } + } + } + + // Group emails by leader account + map> emailGroup; // index of acc -> list of emails + for (const auto& [email, accId] : emailToAcc) { + int leader = uf.find(accId); + emailGroup[leader].push_back(email); + } + + // Build result + vector> res; + for (auto& [accId, emails] : emailGroup) { + sort(emails.begin(), emails.end()); + vector merged; + merged.push_back(accounts[accId][0]); + merged.insert(merged.end(), emails.begin(), emails.end()); + res.push_back(merged); + } + + return res; + } +}; +``` + +```javascript +class UnionFind { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.parent = Array.from({ length: n }, (_, i) => i); + this.rank = Array(n).fill(1); + } + + /** + * @param {number} x + * @return {number} + */ + find(x) { + if (x !== this.parent[x]) { + this.parent[x] = this.find(this.parent[x]); + } + return this.parent[x]; + } + + /** + * @param {number} x1 + * @param {number} x2 + * @return {boolean} + */ + union(x1, x2) { + const p1 = this.find(x1); + const p2 = this.find(x2); + if (p1 === p2) { + return false; + } + if (this.rank[p1] > this.rank[p2]) { + this.parent[p2] = p1; + this.rank[p1] += this.rank[p2]; + } else { + this.parent[p1] = p2; + this.rank[p2] += this.rank[p1]; + } + return true; + } +} + +class Solution { + /** + * @param {string[][]} accounts + * @return {string[][]} + */ + accountsMerge(accounts) { + const n = accounts.length; + const uf = new UnionFind(n); + const emailToAcc = new Map(); // email -> index of acc + + // Build union-find structure + for (let i = 0; i < n; i++) { + for (let j = 1; j < accounts[i].length; j++) { + const email = accounts[i][j]; + if (emailToAcc.has(email)) { + uf.union(i, emailToAcc.get(email)); + } else { + emailToAcc.set(email, i); + } + } + } + + // Group emails by leader account + const emailGroup = new Map(); // index of acc -> list of emails + for (const [email, accId] of emailToAcc.entries()) { + const leader = uf.find(accId); + if (!emailGroup.has(leader)) { + emailGroup.set(leader, []); + } + emailGroup.get(leader).push(email); + } + + // Build result + const res = []; + for (const [accId, emails] of emailGroup.entries()) { + emails.sort(); + const merged = [accounts[accId][0], ...emails]; + res.push(merged); + } + + return res; + } +} +``` + +```csharp +public class UnionFind { + private int[] parent; + private int[] rank; + + public UnionFind(int n) { + parent = new int[n]; + rank = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + rank[i] = 1; + } + } + + public int Find(int x) { + if (x != parent[x]) { + parent[x] = Find(parent[x]); + } + return parent[x]; + } + + public bool Union(int x, int y) { + int rootX = Find(x); + int rootY = Find(y); + + if (rootX == rootY) return false; + + if (rank[rootX] > rank[rootY]) { + parent[rootY] = rootX; + rank[rootX] += rank[rootY]; + } else { + parent[rootX] = rootY; + rank[rootY] += rank[rootX]; + } + + return true; + } +} + +public class Solution { + public List> AccountsMerge(List> accounts) { + int n = accounts.Count; + UnionFind uf = new UnionFind(n); + Dictionary emailToAcc = new Dictionary(); + + for (int i = 0; i < n; i++) { + for (int j = 1; j < accounts[i].Count; j++) { + string email = accounts[i][j]; + if (emailToAcc.ContainsKey(email)) { + uf.Union(i, emailToAcc[email]); + } else { + emailToAcc[email] = i; + } + } + } + + Dictionary> emailGroup = new Dictionary>(); + foreach (var kvp in emailToAcc) { + string email = kvp.Key; + int leader = uf.Find(kvp.Value); + if (!emailGroup.ContainsKey(leader)) { + emailGroup[leader] = new List(); + } + emailGroup[leader].Add(email); + } + + List> res = new List>(); + foreach (var kvp in emailGroup) { + int accId = kvp.Key; + List emails = kvp.Value; + emails.Sort(StringComparer.Ordinal); + List merged = new List { accounts[accId][0] }; + merged.AddRange(emails); + res.Add(merged); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((n * m)\log (n * m))$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of accounts and $m$ is the number of emails. \ No newline at end of file diff --git a/articles/add-binary.md b/articles/add-binary.md new file mode 100644 index 000000000..424adb990 --- /dev/null +++ b/articles/add-binary.md @@ -0,0 +1,240 @@ +## 1. Iteration + +::tabs-start + +```python +class Solution: + def addBinary(self, a: str, b: str) -> str: + res = "" + carry = 0 + + a, b = a[::-1], b[::-1] + for i in range(max(len(a), len(b))): + digitA = ord(a[i]) - ord("0") if i < len(a) else 0 + digitB = ord(b[i]) - ord("0") if i < len(b) else 0 + + total = digitA + digitB + carry + char = str(total % 2) + res = char + res + carry = total // 2 + + if carry: + res = "1" + res + + return res +``` + +```java +public class Solution { + public String addBinary(String a, String b) { + StringBuilder res = new StringBuilder(); + int carry = 0; + + StringBuilder sa = new StringBuilder(a).reverse(); + StringBuilder sb = new StringBuilder(b).reverse(); + + for (int i = 0; i < Math.max(sa.length(), sb.length()); i++) { + int digitA = i < sa.length() ? sa.charAt(i) - '0' : 0; + int digitB = i < sb.length() ? sb.charAt(i) - '0' : 0; + + int total = digitA + digitB + carry; + char c = (char)((total % 2) + '0'); + res.append(c); + carry = total / 2; + } + + if (carry > 0) { + res.append('1'); + } + + return res.reverse().toString(); + } +} +``` + +```cpp +class Solution { +public: + string addBinary(string a, string b) { + string res = ""; + int carry = 0; + + reverse(a.begin(), a.end()); + reverse(b.begin(), b.end()); + + for (int i = 0; i < max(a.length(), b.length()); i++) { + int digitA = i < a.length() ? a[i] - '0' : 0; + int digitB = i < b.length() ? b[i] - '0' : 0; + + int total = digitA + digitB + carry; + char c = (total % 2) + '0'; + res += c; + carry = total / 2; + } + + if (carry) { + res += '1'; + } + reverse(res.begin(), res.end()); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} a + * @param {string} b + * @return {string} + */ + addBinary(a, b) { + let res = []; + let carry = 0; + + a = a.split("").reverse().join(""); + b = b.split("").reverse().join(""); + + for (let i = 0; i < Math.max(a.length, b.length); i++) { + const digitA = i < a.length ? a[i] - '0' : 0; + const digitB = i < b.length ? b[i] - '0' : 0; + + const total = digitA + digitB + carry; + const char = (total % 2).toString(); + res.push(char) + carry = Math.floor(total / 2); + } + + if (carry) { + res.push('1'); + } + res.reverse() + return res.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(max(m, n))$ +* Space complexity: $O(m + n)$ + +> Where $m$ and $n$ are the lengths of the strings $a$ and $b$ respectively. + +--- + +## 2. Iteration (Optimal) + +::tabs-start + +```python +class Solution: + def addBinary(self, a: str, b: str) -> str: + res = [] + carry = 0 + + i, j = len(a) - 1, len(b) - 1 + while i >= 0 or j >= 0 or carry > 0: + digitA = int(a[i]) if i >= 0 else 0 + digitB = int(b[j]) if j >= 0 else 0 + + total = digitA + digitB + carry + res.append(total % 2) + carry = total // 2 + + i -= 1 + j -= 1 + + res.reverse() + return ''.join(map(str, res)) +``` + +```java +public class Solution { + public String addBinary(String a, String b) { + StringBuilder res = new StringBuilder(); + int carry = 0; + + int i = a.length() - 1, j = b.length() - 1; + while (i >= 0 || j >= 0 || carry > 0) { + int digitA = i >= 0 ? a.charAt(i) - '0' : 0; + int digitB = j >= 0 ? b.charAt(j) - '0' : 0; + + int total = digitA + digitB + carry; + res.append(total % 2); + carry = total / 2; + + i--; + j--; + } + + return res.reverse().toString(); + } +} +``` + +```cpp +class Solution { +public: + string addBinary(string a, string b) { + string res = ""; + int carry = 0; + + int i = a.size() - 1, j = b.size() - 1; + while (i >= 0 || j >= 0 || carry > 0) { + int digitA = i >= 0 ? a[i] - '0' : 0; + int digitB = j >= 0 ? b[j] - '0' : 0; + + int total = digitA + digitB + carry; + res += (total % 2) + '0'; + carry = total / 2; + + i--; + j--; + } + + reverse(res.begin(), res.end()); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} a + * @param {string} b + * @return {string} + */ + addBinary(a, b) { + let res = []; + let carry = 0; + + let i = a.length - 1, j = b.length - 1; + while (i >= 0 || j >= 0 || carry > 0) { + const digitA = i >= 0 ? a[i] - "0" : 0; + const digitB = j >= 0 ? b[j] - "0" : 0; + + const total = digitA + digitB + carry; + res.push(total % 2); + carry = Math.floor(total / 2); + + i--; + j--; + } + res.reverse() + return res.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(max(m, n))$ +* Space complexity: $O(max(m, n))$ + +> Where $m$ and $n$ are the lengths of the strings $a$ and $b$ respectively. \ No newline at end of file diff --git a/articles/add-to-array-form-of-integer.md b/articles/add-to-array-form-of-integer.md new file mode 100644 index 000000000..ecab6ca84 --- /dev/null +++ b/articles/add-to-array-form-of-integer.md @@ -0,0 +1,217 @@ +## 1. Reverse and Add + +::tabs-start + +```python +class Solution: + def addToArrayForm(self, num: List[int], k: int) -> List[int]: + num.reverse() + i = 0 + while k: + digit = k % 10 + if i < len(num): + num[i] += digit + else: + num.append(digit) + carry = num[i] // 10 + num[i] %= 10 + k //= 10 + k += carry + i += 1 + num.reverse() + return num +``` + +```java +public class Solution { + public List addToArrayForm(int[] num, int k) { + List result = new ArrayList<>(); + for (int i = num.length - 1; i >= 0; i--) { + k += num[i]; + result.add(k % 10); + k /= 10; + } + while (k > 0) { + result.add(k % 10); + k /= 10; + } + Collections.reverse(result); + return result; + } +} +``` + +```cpp +class Solution { +public: + vector addToArrayForm(vector& num, int k) { + reverse(num.begin(), num.end()); + int i = 0; + while (k) { + int digit = k % 10; + if (i < num.size()) { + num[i] += digit; + } else { + num.push_back(digit); + } + int carry = num[i] / 10; + num[i] %= 10; + k /= 10; + k += carry; + i++; + } + reverse(num.begin(), num.end()); + return num; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} num + * @param {number} k + * @return {number[]} + */ + addToArrayForm(num, k) { + num.reverse(); + let i = 0; + while (k > 0) { + const digit = k % 10; + if (i < num.length) { + num[i] += digit; + } else { + num.push(digit); + } + const carry = Math.floor(num[i] / 10); + num[i] %= 10; + k = Math.floor(k / 10) + carry; + i++; + } + num.reverse(); + return num; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(max(n, m))$ +* Space complexity: $O(n)$. + +> Where $n$ is the size of the array $num$ and $m$ is the number of digits in $k$. + +--- + +## 2. Without Reverse() + +::tabs-start + +```python +class Solution: + def addToArrayForm(self, num: List[int], k: int) -> List[int]: + from collections import deque + result = deque() + i = len(num) - 1 + carry = 0 + + while i >= 0 or k > 0 or carry > 0: + digit = k % 10 + sum_val = carry + (num[i] if i >= 0 else 0) + digit + + result.appendleft(sum_val % 10) + carry = sum_val // 10 + + k //= 10 + i -= 1 + + return list(result) +``` + +```java +public class Solution { + public List addToArrayForm(int[] num, int k) { + LinkedList result = new LinkedList<>(); + int carry = 0, i = num.length - 1; + + while (i >= 0 || k > 0 || carry > 0) { + int digit = k % 10; + int sum = carry + (i >= 0 ? num[i] : 0) + digit; + + result.addFirst(sum % 10); + carry = sum / 10; + + k /= 10; + i--; + } + + return result; + } +} +``` + +```cpp +class Solution { +public: + vector addToArrayForm(vector& num, int k) { + list result; + int carry = 0, i = num.size() - 1; + + while (i >= 0 || k > 0 || carry > 0) { + int digit = k % 10; + int sum = carry + (i >= 0 ? num[i] : 0) + digit; + + result.push_front(sum % 10); + carry = sum / 10; + + k /= 10; + i--; + } + + return vector(result.begin(), result.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} num + * @param {number} k + * @return {number[]} + */ + addToArrayForm(num, k) { + let res = new Deque(); + let carry = 0, i = num.length - 1; + + while (i >= 0 || k > 0 || carry > 0) { + const digit = k % 10; + const sum = carry + (i >= 0 ? num[i] : 0) + digit; + + res.pushFront(sum % 10); + carry = Math.floor(sum / 10); + + k = Math.floor(k / 10); + i--; + } + + const resultArray = []; + while (!res.isEmpty()) { + resultArray.push(res.popFront()); + } + + return resultArray; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(max(n, m))$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $num$ and $m$ is the number of digits in $k$. \ No newline at end of file diff --git a/articles/add-two-numbers.md b/articles/add-two-numbers.md new file mode 100644 index 000000000..118adab49 --- /dev/null +++ b/articles/add-two-numbers.md @@ -0,0 +1,659 @@ +## 1. Recursion + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + +class Solution: + def add(self, l1: Optional[ListNode], l2: Optional[ListNode], carry: int) -> Optional[ListNode]: + if not l1 and not l2 and carry == 0: + return None + + v1 = l1.val if l1 else 0 + v2 = l2.val if l2 else 0 + + carry, val = divmod(v1 + v2 + carry, 10) + + next_node = self.add( + l1.next if l1 else None, + l2.next if l2 else None, + carry + ) + return ListNode(val, next_node) + + def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]: + return self.add(l1, l2, 0) +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +public class Solution { + public ListNode add(ListNode l1, ListNode l2, int carry) { + if (l1 == null && l2 == null && carry == 0) { + return null; + } + + int v1 = 0; + int v2 = 0; + if (l1 != null) { + v1 = l1.val; + } + if (l2 != null) { + v2 = l2.val; + } + + int sum = v1 + v2 + carry; + int newCarry = sum / 10; + int nodeValue = sum % 10; + + ListNode nextNode = add( + (l1 != null) ? l1.next : null, + (l2 != null) ? l2.next : null, + newCarry + ); + + return new ListNode(nodeValue, nextNode); + } + + public ListNode addTwoNumbers(ListNode l1, ListNode l2) { + return add(l1, l2, 0); + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +class Solution { +public: + ListNode* add(ListNode* l1, ListNode* l2, int carry) { + if (!l1 && !l2 && carry == 0) { + return nullptr; + } + + int v1 = 0; + int v2 = 0; + if (l1) { + v1 = l1->val; + } + if (l2) { + v2 = l2->val; + } + + int sum = v1 + v2 + carry; + int newCarry = sum / 10; + int nodeValue = sum % 10; + + ListNode* nextNode = add( + (l1 ? l1->next : nullptr), + (l2 ? l2->next : nullptr), + newCarry + ); + + return new ListNode(nodeValue, nextNode); + } + + ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { + return add(l1, l2, 0); + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {ListNode} l1 + * @param {ListNode} l2 + * @param {number} carry + * @return {ListNode} + */ + add(l1, l2, carry) { + if (!l1 && !l2 && carry === 0) { + return null; + } + + let v1 = 0; + let v2 = 0; + if (l1) { + v1 = l1.val; + } + if (l2) { + v2 = l2.val; + } + + let sum = v1 + v2 + carry; + let newCarry = Math.floor(sum / 10); + let nodeValue = sum % 10; + + let nextNode = this.add( + (l1 ? l1.next : null), + (l2 ? l2.next : null), + newCarry + ); + + return new ListNode(nodeValue, nextNode); + } + + /** + * @param {ListNode} l1 + * @param {ListNode} l2 + * @return {ListNode} + */ + addTwoNumbers(l1, l2) { + return this.add(l1, l2, 0); + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +public class Solution { + public ListNode Add(ListNode l1, ListNode l2, int carry) { + if (l1 == null && l2 == null && carry == 0) { + return null; + } + + int v1 = 0; + int v2 = 0; + if (l1 != null) { + v1 = l1.val; + } + if (l2 != null) { + v2 = l2.val; + } + + int sum = v1 + v2 + carry; + int newCarry = sum / 10; + int nodeValue = sum % 10; + + ListNode nextNode = Add( + (l1 != null ? l1.next : null), + (l2 != null ? l2.next : null), + newCarry + ); + + return new ListNode(nodeValue) { next = nextNode }; + } + + public ListNode AddTwoNumbers(ListNode l1, ListNode l2) { + return Add(l1, l2, 0); + } +} +``` + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func add(l1 *ListNode, l2 *ListNode, carry int) *ListNode { + if l1 == nil && l2 == nil && carry == 0 { + return nil + } + + v1, v2 := 0, 0 + if l1 != nil { + v1 = l1.Val + } + if l2 != nil { + v2 = l2.Val + } + + sum := v1 + v2 + carry + carry, val := sum/10, sum%10 + + var nextNode *ListNode + nextL1 := l1 + nextL2 := l2 + if l1 != nil { + nextL1 = l1.Next + } else { + nextL1 = nil + } + if l2 != nil { + nextL2 = l2.Next + } else { + nextL2 = nil + } + nextNode = add(nextL1, nextL2, carry) + + return &ListNode{Val: val, Next: nextNode} +} + +func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode { + return add(l1, l2, 0) +} +``` + +```kotlin +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + private fun add(l1: ListNode?, l2: ListNode?, carry: Int): ListNode? { + if (l1 == null && l2 == null && carry == 0) return null + + val v1 = l1?.`val` ?: 0 + val v2 = l2?.`val` ?: 0 + val sum = v1 + v2 + carry + val newCarry = sum / 10 + val valNode = sum % 10 + + val nextNode = add(l1?.next, l2?.next, newCarry) + return ListNode(valNode).apply { next = nextNode } + } + + fun addTwoNumbers(l1: ListNode?, l2: ListNode?): ListNode? { + return add(l1, l2, 0) + } +} +``` + +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + private func add(_ l1: ListNode?, _ l2: ListNode?, _ carry: Int) -> ListNode? { + if l1 == nil && l2 == nil && carry == 0 { + return nil + } + + let v1 = l1?.val ?? 0 + let v2 = l2?.val ?? 0 + + let sum = v1 + v2 + carry + let newCarry = sum / 10 + let val = sum % 10 + + let nextNode = add(l1?.next, l2?.next, newCarry) + return ListNode(val, nextNode) + } + + func addTwoNumbers(_ l1: ListNode?, _ l2: ListNode?) -> ListNode? { + return add(l1, l2, 0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(m + n)$ + +> Where $m$ is the length of $l1$ and $n$ is the length of $l2$. + +--- + +## 2. Iteration + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + +class Solution: + def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]: + dummy = ListNode() + cur = dummy + + carry = 0 + while l1 or l2 or carry: + v1 = l1.val if l1 else 0 + v2 = l2.val if l2 else 0 + + # new digit + val = v1 + v2 + carry + carry = val // 10 + val = val % 10 + cur.next = ListNode(val) + + # update ptrs + cur = cur.next + l1 = l1.next if l1 else None + l2 = l2.next if l2 else None + + return dummy.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + + class Solution { + public ListNode addTwoNumbers(ListNode l1, ListNode l2) { + ListNode dummy = new ListNode(); + ListNode cur = dummy; + + int carry = 0; + while (l1 != null || l2 != null || carry != 0) { + int v1 = (l1 != null) ? l1.val : 0; + int v2 = (l2 != null) ? l2.val : 0; + + int val = v1 + v2 + carry; + carry = val / 10; + val = val % 10; + cur.next = new ListNode(val); + + cur = cur.next; + l1 = (l1 != null) ? l1.next : null; + l2 = (l2 != null) ? l2.next : null; + } + + return dummy.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +class Solution { +public: + ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { + ListNode* dummy = new ListNode(); + ListNode* cur = dummy; + + int carry = 0; + while (l1 != nullptr || l2 != nullptr || carry != 0) { + int v1 = (l1 != nullptr) ? l1->val : 0; + int v2 = (l2 != nullptr) ? l2->val : 0; + + int val = v1 + v2 + carry; + carry = val / 10; + val = val % 10; + cur->next = new ListNode(val); + + cur = cur->next; + l1 = (l1 != nullptr) ? l1->next : nullptr; + l2 = (l2 != nullptr) ? l2->next : nullptr; + } + ListNode* res = dummy->next; + delete dummy; + return res; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {ListNode} l1 + * @param {ListNode} l2 + * @return {ListNode} + */ + addTwoNumbers(l1, l2) { + const dummy = new ListNode(); + let cur = dummy; + + let carry = 0; + while (l1 || l2 || carry) { + const v1 = l1 ? l1.val : 0; + const v2 = l2 ? l2.val : 0; + + let val = v1 + v2 + carry; + carry = Math.floor(val / 10); + val = val % 10; + cur.next = new ListNode(val); + + cur = cur.next; + l1 = l1 ? l1.next : null; + l2 = l2 ? l2.next : null; + } + + return dummy.next; + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +public class Solution { + public ListNode AddTwoNumbers(ListNode l1, ListNode l2) { + ListNode dummy = new ListNode(); + ListNode cur = dummy; + + int carry = 0; + while (l1 != null || l2 != null || carry != 0) { + int v1 = (l1 != null) ? l1.val : 0; + int v2 = (l2 != null) ? l2.val : 0; + + int val = v1 + v2 + carry; + carry = val / 10; + val = val % 10; + cur.next = new ListNode(val); + + cur = cur.next; + l1 = (l1 != null) ? l1.next : null; + l2 = (l2 != null) ? l2.next : null; + } + + return dummy.next; + } +} +``` + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode { + dummy := &ListNode{} + cur := dummy + carry := 0 + + for l1 != nil || l2 != nil || carry != 0 { + v1 := 0 + if l1 != nil { + v1 = l1.Val + l1 = l1.Next + } + + v2 := 0 + if l2 != nil { + v2 = l2.Val + l2 = l2.Next + } + + val := v1 + v2 + carry + carry = val / 10 + cur.Next = &ListNode{Val: val % 10} + cur = cur.Next + } + + return dummy.Next +} +``` + +```kotlin +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun addTwoNumbers(l1: ListNode?, l2: ListNode?): ListNode? { + val dummy = ListNode(0) + var cur = dummy + var carry = 0 + + var p1 = l1 + var p2 = l2 + + while (p1 != null || p2 != null || carry != 0) { + val v1 = p1?.`val` ?: 0 + val v2 = p2?.`val` ?: 0 + + val sum = v1 + v2 + carry + carry = sum / 10 + cur.next = ListNode(sum % 10) + + cur = cur.next!! + p1 = p1?.next + p2 = p2?.next + } + + return dummy.next + } +} +``` + +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func addTwoNumbers(_ l1: ListNode?, _ l2: ListNode?) -> ListNode? { + let dummy = ListNode(0) + var cur = dummy + var l1 = l1, l2 = l2 + var carry = 0 + + while l1 != nil || l2 != nil || carry != 0 { + let v1 = l1?.val ?? 0 + let v2 = l2?.val ?? 0 + + let sum = v1 + v2 + carry + carry = sum / 10 + let val = sum % 10 + cur.next = ListNode(val) + + cur = cur.next! + l1 = l1?.next + l2 = l2?.next + } + return dummy.next + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(max(m, n))$ for the output list. + +> Where $m$ is the length of $l1$ and $n$ is the length of $l2$. \ No newline at end of file diff --git a/articles/all-possible-full-binary-trees.md b/articles/all-possible-full-binary-trees.md new file mode 100644 index 000000000..1e75b0293 --- /dev/null +++ b/articles/all-possible-full-binary-trees.md @@ -0,0 +1,693 @@ +## 1. Recursion + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def allPossibleFBT(self, n: int) -> List[Optional[TreeNode]]: + def backtrack(n): + if n == 0: + return [] + if n == 1: + return [TreeNode(0)] + + res = [] + for l in range(n): + r = n - 1 - l + leftTrees, rightTrees = backtrack(l), backtrack(r) + + for t1 in leftTrees: + for t2 in rightTrees: + res.append(TreeNode(0, t1, t2)) + return res + + return backtrack(n) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List allPossibleFBT(int n) { + return backtrack(n); + } + + private List backtrack(int n) { + if (n == 0) { + return new ArrayList<>(); + } + if (n == 1) { + return Arrays.asList(new TreeNode(0)); + } + + List res = new ArrayList<>(); + for (int l = 0; l < n; l++) { + int r = n - 1 - l; + List leftTrees = backtrack(l); + List rightTrees = backtrack(r); + + for (TreeNode t1 : leftTrees) { + for (TreeNode t2 : rightTrees) { + res.add(new TreeNode(0, t1, t2)); + } + } + } + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector allPossibleFBT(int n) { + return backtrack(n); + } + +private: + vector backtrack(int n) { + if (n == 0) { + return {}; + } + if (n == 1) { + return {new TreeNode(0)}; + } + + vector res; + for (int l = 0; l < n; l++) { + int r = n - 1 - l; + vector leftTrees = backtrack(l); + vector rightTrees = backtrack(r); + + for (auto& t1 : leftTrees) { + for (auto& t2 : rightTrees) { + res.push_back(new TreeNode(0, t1, t2)); + } + } + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number} n + * @return {TreeNode[]} + */ + allPossibleFBT(n) { + const backtrack = (n) => { + if (n === 0) { + return []; + } + if (n === 1) { + return [new TreeNode(0)]; + } + + let res = []; + for (let l = 0; l < n; l++) { + let r = n - 1 - l; + let leftTrees = backtrack(l); + let rightTrees = backtrack(r); + + for (let t1 of leftTrees) { + for (let t2 of rightTrees) { + res.push(new TreeNode(0, t1, t2)); + } + } + } + return res; + }; + + return backtrack(n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n * 2 ^ n)$ + +--- + +## 2. Recursion (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def allPossibleFBT(self, n: int) -> List[Optional[TreeNode]]: + if n % 2 == 0: + return [] + if n == 1: + return [TreeNode(0)] + + res = [] + for left in range(1, n, 2): + leftSubTree = self.allPossibleFBT(left) + rightSubTree = self.allPossibleFBT(n - 1 - left) + for l in leftSubTree: + for r in rightSubTree: + root = TreeNode(0, l, r) + res.append(root) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List allPossibleFBT(int n) { + if (n % 2 == 0) { + return new ArrayList<>(); + } + if (n == 1) { + return Arrays.asList(new TreeNode(0)); + } + + List res = new ArrayList<>(); + for (int left = 1; left < n; left += 2) { + List leftSubTree = allPossibleFBT(left); + List rightSubTree = allPossibleFBT(n - 1 - left); + for (TreeNode l : leftSubTree) { + for (TreeNode r : rightSubTree) { + TreeNode root = new TreeNode(0, l, r); + res.add(root); + } + } + } + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector allPossibleFBT(int n) { + if (n % 2 == 0) { + return {}; + } + if (n == 1) { + return {new TreeNode(0)}; + } + + vector res; + for (int left = 1; left < n; left += 2) { + vector leftSubTree = allPossibleFBT(left); + vector rightSubTree = allPossibleFBT(n - 1 - left); + for (auto& l : leftSubTree) { + for (auto& r : rightSubTree) { + TreeNode* root = new TreeNode(0, l, r); + res.push_back(root); + } + } + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number} n + * @return {TreeNode[]} + */ + allPossibleFBT(n) { + if (n % 2 === 0) { + return []; + } + if (n === 1) { + return [new TreeNode(0)]; + } + + let res = []; + for (let left = 1; left < n; left += 2) { + let leftSubTree = this.allPossibleFBT(left); + let rightSubTree = this.allPossibleFBT(n - 1 - left); + for (let l of leftSubTree) { + for (let r of rightSubTree) { + let root = new TreeNode(0, l, r); + res.push(root); + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n * 2 ^ n)$ + +--- + +## 3. Dynamic Programming (Top-Down) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def allPossibleFBT(self, n: int) -> List[Optional[TreeNode]]: + dp = {} + + def dfs(n): + if n % 2 == 0: + return [] + if n == 1: + return [TreeNode(0)] + if n in dp: + return dp[n] + + res = [] + for left in range(1, n, 2): + leftSubTree = dfs(left) + rightSubTree = dfs(n - 1 - left) + for l in leftSubTree: + for r in rightSubTree: + res.append(TreeNode(0, l, r)) + + dp[n] = res + return res + + return dfs(n) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private List[] dp; + + public List allPossibleFBT(int n) { + dp = new ArrayList[n + 1]; + return dfs(n); + } + + private List dfs(int n) { + if (n % 2 == 0) { + return new ArrayList<>(); + } + if (n == 1) { + return Arrays.asList(new TreeNode(0)); + } + if (dp[n] != null) { + return dp[n]; + } + + List res = new ArrayList<>(); + for (int left = 1; left < n; left += 2) { + List leftSubTree = dfs(left); + List rightSubTree = dfs(n - 1 - left); + for (TreeNode l : leftSubTree) { + for (TreeNode r : rightSubTree) { + res.add(new TreeNode(0, l, r)); + } + } + } + + return dp[n] = res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +private: + vector> dp; + +public: + vector allPossibleFBT(int n) { + dp.resize(n + 1); + return dfs(n); + } + + vector dfs(int n) { + if (n % 2 == 0) { + return {}; + } + if (n == 1) { + return {new TreeNode(0)}; + } + if (!dp[n].empty()) { + return dp[n]; + } + + vector res; + for (int left = 1; left < n; left += 2) { + vector leftSubTree = dfs(left); + vector rightSubTree = dfs(n - 1 - left); + for (auto& l : leftSubTree) { + for (auto& r : rightSubTree) { + res.push_back(new TreeNode(0, l, r)); + } + } + } + + return dp[n] = res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number} n + * @return {TreeNode[]} + */ + allPossibleFBT(n) { + let dp = new Array(n + 1); + + const dfs = (n) => { + if (n % 2 === 0) { + return []; + } + if (n === 1) { + return [new TreeNode(0)]; + } + if (dp[n]) { + return dp[n]; + } + + let res = []; + for (let left = 1; left < n; left += 2) { + let leftSubTree = dfs(left); + let rightSubTree = dfs(n - 1 - left); + for (let t1 of leftSubTree) { + for (let t2 of rightSubTree) { + res.push(new TreeNode(0, t1, t2)); + } + } + } + + return (dp[n] = res); + }; + + return dfs(n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n * 2 ^ n)$ + +--- + +## 4. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def allPossibleFBT(self, n: int) -> List[Optional[TreeNode]]: + if n % 2 == 0: + return [] + + dp = [[] for _ in range(n + 1)] + dp[1] = [TreeNode(0)] + + for nodes in range(3, n + 1, 2): + res = [] + for left in range(1, nodes, 2): + right = nodes - 1 - left + for t1 in dp[left]: + for t2 in dp[right]: + res.append(TreeNode(0, t1, t2)) + dp[nodes] = res + + return dp[n] +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List allPossibleFBT(int n) { + if (n % 2 == 0) { + return new ArrayList<>(); + } + + List[] dp = new ArrayList[n + 1]; + for (int i = 0; i <= n; i++) { + dp[i] = new ArrayList<>(); + } + dp[1].add(new TreeNode(0)); + + for (int nodes = 3; nodes <= n; nodes += 2) { + List res = new ArrayList<>(); + for (int left = 1; left < nodes; left += 2) { + int right = nodes - 1 - left; + for (TreeNode t1 : dp[left]) { + for (TreeNode t2 : dp[right]) { + res.add(new TreeNode(0, t1, t2)); + } + } + } + dp[nodes] = res; + } + + return dp[n]; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector allPossibleFBT(int n) { + if (n % 2 == 0) { + return {}; + } + + vector> dp(n + 1); + dp[1].push_back(new TreeNode(0)); + + for (int nodes = 3; nodes <= n; nodes += 2) { + vector res; + for (int left = 1; left < nodes; left += 2) { + int right = nodes - 1 - left; + for (auto& t1 : dp[left]) { + for (auto& t2 : dp[right]) { + res.push_back(new TreeNode(0, t1, t2)); + } + } + } + dp[nodes] = res; + } + + return dp[n]; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number} n + * @return {TreeNode[]} + */ + allPossibleFBT(n) { + if (n % 2 === 0) { + return []; + } + + let dp = Array.from({ length: n + 1 }, () => []); + dp[1] = [new TreeNode(0)]; + + for (let nodes = 3; nodes <= n; nodes += 2) { + let res = []; + for (let left = 1; left < nodes; left += 2) { + let right = nodes - 1 - left; + for (let t1 of dp[left]) { + for (let t2 of dp[right]) { + res.push(new TreeNode(0, t1, t2)); + } + } + } + dp[nodes] = res; + } + + return dp[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n * 2 ^ n)$ \ No newline at end of file diff --git a/articles/anagram-groups.md b/articles/anagram-groups.md new file mode 100644 index 000000000..bf41aaa79 --- /dev/null +++ b/articles/anagram-groups.md @@ -0,0 +1,322 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def groupAnagrams(self, strs: List[str]) -> List[List[str]]: + res = defaultdict(list) + for s in strs: + sortedS = ''.join(sorted(s)) + res[sortedS].append(s) + return list(res.values()) +``` + +```java +public class Solution { + public List> groupAnagrams(String[] strs) { + Map> res = new HashMap<>(); + for (String s : strs) { + char[] charArray = s.toCharArray(); + Arrays.sort(charArray); + String sortedS = new String(charArray); + res.putIfAbsent(sortedS, new ArrayList<>()); + res.get(sortedS).add(s); + } + return new ArrayList<>(res.values()); + } +} +``` + +```cpp +class Solution { +public: + vector> groupAnagrams(vector& strs) { + unordered_map> res; + for (const auto& s : strs) { + string sortedS = s; + sort(sortedS.begin(), sortedS.end()); + res[sortedS].push_back(s); + } + vector> result; + for (auto& pair : res) { + result.push_back(pair.second); + } + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} strs + * @return {string[][]} + */ + groupAnagrams(strs) { + const res = {}; + for (let s of strs) { + const sortedS = s.split('').sort().join(''); + if (!res[sortedS]) { + res[sortedS] = []; + } + res[sortedS].push(s); + } + return Object.values(res); + } +} +``` + +```csharp +public class Solution { + public List> GroupAnagrams(string[] strs) { + var res = new Dictionary>(); + foreach (var s in strs) { + char[] charArray = s.ToCharArray(); + Array.Sort(charArray); + string sortedS = new string(charArray); + if (!res.ContainsKey(sortedS)) { + res[sortedS] = new List(); + } + res[sortedS].Add(s); + } + return res.Values.ToList>(); + } +} +``` + +```go +func groupAnagrams(strs []string) [][]string { + res := make(map[string][]string) + + for _, s := range strs { + sortedS := sortString(s) + res[sortedS] = append(res[sortedS], s) + } + + var result [][]string + for _, group := range res { + result = append(result, group) + } + return result +} + +func sortString(s string) string { + characters := []rune(s) + sort.Slice(characters, func(i, j int) bool { + return characters[i] < characters[j] + }) + return string(characters) +} +``` + +```kotlin +class Solution { + fun groupAnagrams(strs: Array): List> { + val res = mutableMapOf>() + + for (s in strs) { + val sortedS = s.toCharArray().sorted().joinToString("") + res.getOrPut(sortedS) { mutableListOf() }.add(s) + } + + return res.values.toList() + } +} +``` + +```swift +class Solution { + func groupAnagrams(_ strs: [String]) -> [[String]] { + var res = [String: [String]]() + + for s in strs { + let sortedS = String(s.sorted()) + res[sortedS, default: []].append(s) + } + + return Array(res.values) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n \log n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of strings and $n$ is the length of the longest string. + +--- + +## 2. Hash Table + +::tabs-start + +```python +class Solution: + def groupAnagrams(self, strs: List[str]) -> List[List[str]]: + res = defaultdict(list) + for s in strs: + count = [0] * 26 + for c in s: + count[ord(c) - ord('a')] += 1 + res[tuple(count)].append(s) + return list(res.values()) +``` + +```java +public class Solution { + public List> groupAnagrams(String[] strs) { + Map> res = new HashMap<>(); + for (String s : strs) { + int[] count = new int[26]; + for (char c : s.toCharArray()) { + count[c - 'a']++; + } + String key = Arrays.toString(count); + res.putIfAbsent(key, new ArrayList<>()); + res.get(key).add(s); + } + return new ArrayList<>(res.values()); + } +} +``` + +```cpp +class Solution { +public: + vector> groupAnagrams(vector& strs) { + unordered_map> res; + for (const auto& s : strs) { + vector count(26, 0); + for (char c : s) { + count[c - 'a']++; + } + string key = to_string(count[0]); + for (int i = 1; i < 26; ++i) { + key += ',' + to_string(count[i]); + } + res[key].push_back(s); + } + vector> result; + for (const auto& pair : res) { + result.push_back(pair.second); + } + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} strs + * @return {string[][]} + */ + groupAnagrams(strs) { + const res = {}; + for (let s of strs) { + const count = new Array(26).fill(0); + for (let c of s) { + count[c.charCodeAt(0) - 'a'.charCodeAt(0)] += 1; + } + const key = count.join(','); + if (!res[key]) { + res[key] = []; + } + res[key].push(s); + } + return Object.values(res); + } +} +``` + +```csharp +public class Solution { + public List> GroupAnagrams(string[] strs) { + var res = new Dictionary>(); + foreach (var s in strs) { + int[] count = new int[26]; + foreach (char c in s) { + count[c - 'a']++; + } + string key = string.Join(",", count); + if (!res.ContainsKey(key)) { + res[key] = new List(); + } + res[key].Add(s); + } + return res.Values.ToList>(); + } +} +``` + +```go +func groupAnagrams(strs []string) [][]string { + res := make(map[[26]int][]string) + + for _, s := range strs { + var count [26]int + for _, c := range s { + count[c-'a']++ + } + res[count] = append(res[count], s) + } + + var result [][]string + for _, group := range res { + result = append(result, group) + } + return result +} +``` + +```kotlin +class Solution { + fun groupAnagrams(strs: Array): List> { + val res = HashMap, MutableList>() + + for (s in strs) { + val count = MutableList(26) { 0 } + for (c in s) { + count[c - 'a']++ + } + res.getOrPut(count) { mutableListOf() }.add(s) + } + + return res.values.toList() + } +} +``` + +```swift +class Solution { + func groupAnagrams(_ strs: [String]) -> [[String]] { + var res = [Array: [String]]() + + for s in strs { + var count = [Int](repeating: 0, count: 26) + for c in s { + count[Int(c.asciiValue!) - 97] += 1 + } + res[count, default: []].append(s) + } + + return Array(res.values) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: + * $O(m)$ extra space. + * $O(m * n)$ space for the output list. + +> Where $m$ is the number of strings and $n$ is the length of the longest string. \ No newline at end of file diff --git a/articles/analyze-user-website-visit-pattern.md b/articles/analyze-user-website-visit-pattern.md new file mode 100644 index 000000000..c43fce6ba --- /dev/null +++ b/articles/analyze-user-website-visit-pattern.md @@ -0,0 +1,224 @@ +## 1. Hash Map + +::tabs-start + +```python +class Solution: + def mostVisitedPattern(self, username: List[str], timestamp: List[int], website: List[str]) -> List[str]: + arr = list(zip(timestamp, username, website)) + arr.sort() + + mp = defaultdict(list) + for time, user, site in arr: + mp[user].append(site) + + count = defaultdict(int) + for user in mp: + patterns = set() + cur = mp[user] + for i in range(len(cur)): + for j in range(i + 1, len(cur)): + for k in range(j + 1, len(cur)): + patterns.add((cur[i], cur[j], cur[k])) + for p in patterns: + count[p] += 1 + + max_count = 0 + res = tuple() + for pattern in count: + if count[pattern] > max_count or (count[pattern] == max_count and pattern < res): + max_count = count[pattern] + res = pattern + + return list(res) +``` + +```java +public class Solution { + public List mostVisitedPattern(String[] username, int[] timestamp, String[] website) { + int n = timestamp.length; + List arr = new ArrayList<>(); + for (int i = 0; i < n; i++) arr.add(new int[]{timestamp[i], i}); + arr.sort((a, b) -> Integer.compare(a[0], b[0])); + + Map> mp = new HashMap<>(); + for (int[] p : arr) { + int idx = p[1]; + mp.computeIfAbsent(username[idx], k -> new ArrayList<>()).add(website[idx]); + } + + Map count = new HashMap<>(); + for (String user : mp.keySet()) { + List cur = mp.get(user); + Set patterns = new HashSet<>(); + for (int i = 0; i < cur.size(); i++) + for (int j = i + 1; j < cur.size(); j++) + for (int k = j + 1; k < cur.size(); k++) + patterns.add(cur.get(i) + "#" + cur.get(j) + "#" + cur.get(k)); + for (String p : patterns) + count.put(p, count.getOrDefault(p, 0) + 1); + } + + String res = ""; + int max_count = 0; + for (String p : count.keySet()) { + int c = count.get(p); + if (c > max_count || (c == max_count && p.compareTo(res) < 0)) { + max_count = c; + res = p; + } + } + return Arrays.asList(res.split("#")); + } +} +``` + +```cpp +class Solution { +public: + vector mostVisitedPattern(vector& username, vector& timestamp, vector& website) { + int n = timestamp.size(); + vector> arr; + for (int i = 0; i < n; ++i) arr.push_back({timestamp[i], i}); + sort(arr.begin(), arr.end(), + [](auto& a, auto& b){ return a.first < b.first; }); + + unordered_map> mp; + for (auto& p : arr) mp[username[p.second]].push_back(website[p.second]); + + unordered_map count; + for (auto& kv : mp) { + auto& cur = kv.second; + unordered_set patterns; + for (int i = 0; i < (int)cur.size(); ++i) + for (int j = i + 1; j < (int)cur.size(); ++j) + for (int k = j + 1; k < (int)cur.size(); ++k) + patterns.insert(cur[i] + "#" + cur[j] + "#" + cur[k]); + for (auto& p : patterns) ++count[p]; + } + + int maxCnt = 0; + string res; + for (auto& kv : count) + if (kv.second > maxCnt || + (kv.second == maxCnt && (res.empty() || kv.first < res))) { + maxCnt = kv.second; + res = kv.first; + } + + vector ans; + string tmp; + for (char ch : res) { + if (ch == '#') { + ans.push_back(tmp); + tmp.clear(); + } else { + tmp += ch; + } + } + ans.push_back(tmp); + return ans; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} username + * @param {number[]} timestamp + * @param {string[]} website + * @return {string[]} + */ + mostVisitedPattern(username, timestamp, website) { + const n = timestamp.length; + const arr = []; + for (let i = 0; i < n; i++) arr.push([timestamp[i], i]); + arr.sort((a, b) => a[0] - b[0]); + + const mp = new Map(); + for (const [, idx] of arr) { + const user = username[idx], site = website[idx]; + if (!mp.has(user)) mp.set(user, []); + mp.get(user).push(site); + } + + const count = new Map(); + for (const user of mp.keys()) { + const cur = mp.get(user); + const patterns = new Set(); + for (let i = 0; i < cur.length; i++) { + for (let j = i + 1; j < cur.length; j++) { + for (let k = j + 1; k < cur.length; k++) { + patterns.add(`${cur[i]}#${cur[j]}#${cur[k]}`); + } + } + } + for (const p of patterns) { + count.set(p, (count.get(p) || 0) + 1); + } + } + + let maxCnt = 0, res = ""; + for (const [pat, c] of count.entries()) { + if (c > maxCnt || (c === maxCnt && (res === "" || pat < res))) { + maxCnt = c; + res = pat; + } + } + return res.split("#"); + } +} +``` + +```csharp +public class Solution { + public List MostVisitedPattern(string[] username, int[] timestamp, string[] website) { + int n = timestamp.Length; + var arr = new List<(int t, int i)>(); + for (int i = 0; i < n; i++) arr.Add((timestamp[i], i)); + arr.Sort((a, b) => a.t.CompareTo(b.t)); + + var mp = new Dictionary>(); + foreach (var (t, idx) in arr) { + string user = username[idx], site = website[idx]; + if (!mp.ContainsKey(user)) mp[user] = new List(); + mp[user].Add(site); + } + + var count = new Dictionary(); + foreach (var kv in mp) { + var cur = kv.Value; + var patterns = new HashSet(); + for (int i = 0; i < cur.Count; i++) + for (int j = i + 1; j < cur.Count; j++) + for (int k = j + 1; k < cur.Count; k++) + patterns.Add($"{cur[i]}#{cur[j]}#{cur[k]}"); + foreach (var p in patterns) { + count[p] = count.ContainsKey(p) ? count[p] + 1 : 1; + } + } + + int maxCnt = 0; + string res = ""; + foreach (var kv in count) { + if (kv.Value > maxCnt || + (kv.Value == maxCnt && + (res == "" || string.Compare(kv.Key, res, StringComparison.Ordinal) < 0))) { + maxCnt = kv.Value; + res = kv.Key; + } + } + return new List(res.Split('#')); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + n * u + n ^ 3 * w)$ +* Space complexity: $O(n * u + n ^ 3 * w)$ + +> Where $n$ is the size of the array $timestamp$, $u$ is the maximum length of any string in the array $username$, and $w$ is the maximum length of any string in the array $website$. \ No newline at end of file diff --git a/articles/append-characters-to-string-to-make-subsequence.md b/articles/append-characters-to-string-to-make-subsequence.md new file mode 100644 index 000000000..de5f09613 --- /dev/null +++ b/articles/append-characters-to-string-to-make-subsequence.md @@ -0,0 +1,210 @@ +## 1. Two Pointers + +::tabs-start + +```python +class Solution: + def appendCharacters(self, s: str, t: str) -> int: + i, j = 0, 0 + + while i < len(s) and j < len(t): + if s[i] == t[j]: + i += 1 + j += 1 + else: + i += 1 + return len(t) - j +``` + +```java +public class Solution { + public int appendCharacters(String s, String t) { + int i = 0, j = 0; + + while (i < s.length() && j < t.length()) { + if (s.charAt(i) == t.charAt(j)) { + i++; + j++; + } else { + i++; + } + } + return t.length() - j; + } +} +``` + +```cpp +class Solution { +public: + int appendCharacters(string s, string t) { + int i = 0, j = 0; + + while (i < s.length() && j < t.length()) { + if (s[i] == t[j]) { + i++; + j++; + } else { + i++; + } + } + return t.length() - j; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {number} + */ + appendCharacters(s, t) { + let i = 0, j = 0; + + while (i < s.length && j < t.length) { + if (s[i] === t[j]) { + i++; + j++; + } else { + i++; + } + } + return t.length - j; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(1)$ + +> Where $n$ and $m$ are the lengths of the strings $s$ and $t$, respectively. + +--- + +## 2. Index Jumping + +::tabs-start + +```python +class Solution: + def appendCharacters(self, s: str, t: str) -> int: + n, m = len(s), len(t) + store = [[n + 1] * 26 for _ in range(n)] + store[n - 1][ord(s[n - 1]) - ord('a')] = n - 1 + + for i in range(n - 2, -1, -1): + store[i] = store[i + 1][:] + store[i][ord(s[i]) - ord('a')] = i + + i, j = 0, 0 + while i < n and j < m: + if store[i][ord(t[j]) - ord('a')] == n + 1: + break + + i = store[i][ord(t[j]) - ord('a')] + 1 + j += 1 + + return m - j +``` + +```java +public class Solution { + public int appendCharacters(String s, String t) { + int n = s.length(), m = t.length(); + int[][] store = new int[n][26]; + for (int[] row : store) { + Arrays.fill(row, n + 1); + } + store[n - 1][s.charAt(n - 1) - 'a'] = n - 1; + + for (int i = n - 2; i >= 0; i--) { + store[i] = store[i + 1].clone(); + store[i][s.charAt(i) - 'a'] = i; + } + + int i = 0, j = 0; + while (i < n && j < m) { + if (store[i][t.charAt(j) - 'a'] == n + 1) { + break; + } + i = store[i][t.charAt(j) - 'a'] + 1; + j++; + } + + return m - j; + } +} +``` + +```cpp +class Solution { +public: + int appendCharacters(string s, string t) { + int n = s.length(), m = t.length(); + vector> store(n, vector(26, n + 1)); + store[n - 1][s[n - 1] - 'a'] = n - 1; + + for (int i = n - 2; i >= 0; i--) { + store[i] = store[i + 1]; + store[i][s[i] - 'a'] = i; + } + + int i = 0, j = 0; + while (i < n && j < m) { + if (store[i][t[j] - 'a'] == n + 1) { + break; + } + i = store[i][t[j] - 'a'] + 1; + j++; + } + + return m - j; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {number} + */ + appendCharacters(s, t) { + const n = s.length, m = t.length; + const store = Array.from({ length: n }, () => Array(26).fill(n + 1)); + store[n - 1][s.charCodeAt(n - 1) - 97] = n - 1; + + for (let i = n - 2; i >= 0; i--) { + store[i] = store[i + 1].slice(); + store[i][s.charCodeAt(i) - 97] = i; + } + + let i = 0, j = 0; + while (i < n && j < m) { + if (store[i][t.charCodeAt(j) - 97] === n + 1) { + break; + } + i = store[i][t.charCodeAt(j) - 97] + 1; + j++; + } + + return m - j; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n)$ + +> Where $n$ and $m$ are the lengths of the strings $s$ and $t$, respectively. \ No newline at end of file diff --git a/articles/arithmetic-slices-ii-subsequence.md b/articles/arithmetic-slices-ii-subsequence.md new file mode 100644 index 000000000..427b1557c --- /dev/null +++ b/articles/arithmetic-slices-ii-subsequence.md @@ -0,0 +1,499 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def numberOfArithmeticSlices(self, nums: List[int]) -> int: + n = len(nums) + if n < 3: + return 0 + + INF = float('inf') + dp = {} + + def dfs(i, j, diff, flag): + if i == n: + return flag + if (i, j, diff, flag) in dp: + return dp[(i, j, diff, flag)] + + res = dfs(i + 1, j, diff, flag) + if j == -1: + res += dfs(i + 1, i, INF, flag) + else: + if diff == INF: + res += dfs(i + 1, i, nums[i] - nums[j], flag) + elif diff == nums[i] - nums[j]: + res += dfs(i + 1, i, diff, 1) + + dp[(i, j, diff, flag)] = res + return res + + return dfs(0, -1, INF, 0) +``` + +```java +public class Solution { + private static final long INF = (long) 1e15; + private Map dp = new HashMap<>(); + + public int numberOfArithmeticSlices(int[] nums) { + int n = nums.length; + if (n < 3) return 0; + + return dfs(nums, 0, -1, INF, 0); + } + + private int dfs(int[] nums, int i, int j, long diff, int flag) { + if (i == nums.length) { + return flag; + } + String key = i + "," + j + "," + diff + "," + flag; + if (dp.containsKey(key)) { + return dp.get(key); + } + + int res = dfs(nums, i + 1, j, diff, flag); + if (j == -1) { + res += dfs(nums, i + 1, i, INF, flag); + } else { + if (diff == INF) { + res += dfs(nums, i + 1, i, nums[i] - 0L - nums[j], flag); + } else if (diff == nums[i] - 0L - nums[j]) { + res += dfs(nums, i + 1, i, diff, 1); + } + } + + dp.put(key, res); + return res; + } +} +``` + +```cpp +class Solution { + static const long long INF = 1e15; + unordered_map dp; + + int dfs(vector& nums, int i, int j, long long diff, int flag) { + if (i == nums.size()) { + return flag; + } + + string key = to_string(i) + "," + to_string(j) + "," + to_string(diff) + "," + to_string(flag); + if (dp.count(key)) { + return dp[key]; + } + + int res = dfs(nums, i + 1, j, diff, flag); + if (j == -1) { + res += dfs(nums, i + 1, i, INF, flag); + } else { + if (diff == INF) { + res += dfs(nums, i + 1, i, nums[i] - 0LL - nums[j], flag); + } else if (diff == nums[i] - 0LL - nums[j]) { + res += dfs(nums, i + 1, i, diff, 1); + } + } + + dp[key] = res; + return res; + } + +public: + int numberOfArithmeticSlices(vector& nums) { + if (nums.size() < 3) return 0; + return dfs(nums, 0, -1, INF, 0); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + numberOfArithmeticSlices(nums) { + const n = nums.length; + if (n < 3) return 0; + + const INF = Infinity; + const dp = new Map(); + + const dfs = (i, j, diff, flag) => { + if (i === n) { + return flag; + } + const key = `${i},${j},${diff},${flag}`; + if (dp.has(key)) { + return dp.get(key); + } + + let res = dfs(i + 1, j, diff, flag); + if (j === -1) { + res += dfs(i + 1, i, INF, flag); + } else { + if (diff === INF) { + res += dfs(i + 1, i, nums[i] - nums[j], flag); + } else if (diff === nums[i] - nums[j]) { + res += dfs(i + 1, i, diff, 1); + } + } + + dp.set(key, res); + return res; + }; + + return dfs(0, -1, INF, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 3)$ + +--- + +## 2. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def numberOfArithmeticSlices(self, nums: List[int]) -> int: + res, n = 0, len(nums) + dp = [defaultdict(int) for _ in range(n)] + + for i in range(n): + for j in range(i): + diff = nums[i] - nums[j] + dp[i][diff] += 1 + dp[j][diff] + res += dp[j][diff] + + return res +``` + +```java +public class Solution { + public int numberOfArithmeticSlices(int[] nums) { + int n = nums.length; + int res = 0; + Map[] dp = new HashMap[n]; + + for (int i = 0; i < n; i++) { + dp[i] = new HashMap<>(); + for (int j = 0; j < i; j++) { + long diff = (long) nums[i] - nums[j]; + int count = dp[j].getOrDefault(diff, 0); + dp[i].put(diff, dp[i].getOrDefault(diff, 0) + count + 1); + res += count; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numberOfArithmeticSlices(vector& nums) { + int n = nums.size(); + int res = 0; + vector> dp(n); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + long long diff = (long long) nums[i] - nums[j]; + int count = dp[j][diff]; + dp[i][diff] += count + 1; + res += count; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + numberOfArithmeticSlices(nums) { + const n = nums.length; + let res = 0; + const dp = Array.from({ length: n }, () => new Map()); + + for (let i = 0; i < n; i++) { + for (let j = 0; j < i; j++) { + const diff = nums[i] - nums[j]; + const count = dp[j].get(diff) || 0; + dp[i].set(diff, (dp[i].get(diff) || 0) + count + 1); + res += count; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Optimization) - I + +::tabs-start + +```python +class Solution: + def numberOfArithmeticSlices(self, nums: List[int]) -> int: + res, n = 0, len(nums) + s = set(nums) + dp = [defaultdict(int) for _ in range(n)] + + for i in range(n): + for j in range(i): + diff = nums[i] - nums[j] + cnt = dp[j].get(diff, 0) + if nums[i] + diff in s: + dp[i][diff] += cnt + 1 + res += cnt + + return res +``` + +```java +public class Solution { + public int numberOfArithmeticSlices(int[] nums) { + int res = 0, n = nums.length; + Set s = new HashSet<>(); + for (int num : nums) s.add(num); + + Map[] dp = new HashMap[n]; + for (int i = 0; i < n; i++) dp[i] = new HashMap<>(); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + long diff = (long) nums[i] - nums[j]; + int cnt = dp[j].getOrDefault(diff, 0); + if (s.contains((int) (nums[i] + diff))) { + dp[i].put(diff, dp[i].getOrDefault(diff, 0) + cnt + 1); + } + res += cnt; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numberOfArithmeticSlices(vector& nums) { + int res = 0, n = nums.size(); + unordered_set s(nums.begin(), nums.end()); + vector> dp(n); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + long long diff = (long long)nums[i] - nums[j]; + int cnt = dp[j].count(diff) ? dp[j][diff] : 0; + if (s.count(nums[i] + diff)) { + dp[i][diff] += cnt + 1; + } + res += cnt; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + numberOfArithmeticSlices(nums) { + let res = 0, n = nums.length; + const s = new Set(nums); + const dp = Array.from({ length: n }, () => new Map()); + + for (let i = 0; i < n; i++) { + for (let j = 0; j < i; j++) { + const diff = nums[i] - nums[j]; + const cnt = dp[j].get(diff) || 0; + if (s.has(nums[i] + diff)) { + dp[i].set(diff, (dp[i].get(diff) || 0) + cnt + 1); + } + res += cnt; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Dynamic Programming (Optimization) - II + +::tabs-start + +```python +class Solution: + def numberOfArithmeticSlices(self, nums: List[int]) -> int: + res = 0 + mpIdx = defaultdict(list) + n = len(nums) + dp = [[0] * n for _ in range(n)] + + for i in range(n): + mpIdx[nums[i]].append(i) + + for i in range(n): + for j in range(i): + prev = 2 * nums[j] - nums[i] + if prev in mpIdx: + for k in mpIdx[prev]: + if k >= j: + break + dp[i][j] += dp[j][k] + 1 + res += dp[i][j] + + return res +``` + +```java +public class Solution { + public int numberOfArithmeticSlices(int[] nums) { + int res = 0; + Map> mpIdx = new HashMap<>(); + int n = nums.length; + int[][] dp = new int[n][n]; + + for (int i = 0; i < n; i++) { + mpIdx.putIfAbsent(nums[i], new ArrayList<>()); + mpIdx.get(nums[i]).add(i); + } + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + long prev = 2L * nums[j] - nums[i]; + if (prev < Integer.MIN_VALUE || prev > Integer.MAX_VALUE) { + continue; + } + + if (mpIdx.containsKey((int) prev)) { + for (int k : mpIdx.get((int) prev)) { + if (k >= j) break; + dp[i][j] += dp[j][k] + 1; + } + } + res += dp[i][j]; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numberOfArithmeticSlices(vector& nums) { + int res = 0; + unordered_map> mpIdx; + int n = nums.size(); + vector> dp(n, vector(n, 0)); + + for (int i = 0; i < n; i++) { + mpIdx[nums[i]].push_back(i); + } + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + long prev = 2L * nums[j] - nums[i]; + if (prev < INT_MIN || prev > INT_MAX) continue; + + if (mpIdx.count(prev)) { + for (int k : mpIdx[prev]) { + if (k >= j) break; + dp[i][j] += dp[j][k] + 1; + } + } + res += dp[i][j]; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + numberOfArithmeticSlices(nums) { + let res = 0; + const mpIdx = new Map(); + const n = nums.length; + const dp = Array.from({ length: n }, () => Array(n).fill(0)); + + for (let i = 0; i < n; i++) { + if (!mpIdx.has(nums[i])) mpIdx.set(nums[i], []); + mpIdx.get(nums[i]).push(i); + } + + for (let i = 0; i < n; i++) { + for (let j = 0; j < i; j++) { + const prev = 2 * nums[j] - nums[i]; + + if (mpIdx.has(prev)) { + for (const k of mpIdx.get(prev)) { + if (k >= j) break; + dp[i][j] += dp[j][k] + 1; + } + } + res += dp[i][j]; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/arranging-coins.md b/articles/arranging-coins.md new file mode 100644 index 000000000..cb8920a4a --- /dev/null +++ b/articles/arranging-coins.md @@ -0,0 +1,401 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def arrangeCoins(self, n: int) -> int: + row = 0 + while n - row > 0: + row += 1 + n -= row + return row +``` + +```java +public class Solution { + public int arrangeCoins(int n) { + int row = 0; + while (n - row > 0) { + row++; + n -= row; + } + return row; + } +} +``` + +```cpp +class Solution { +public: + int arrangeCoins(int n) { + int row = 0; + while (n - row > 0) { + row++; + n -= row; + } + return row; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + arrangeCoins(n) { + let row = 0; + while (n - row > 0) { + row++; + n -= row; + } + return row; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\sqrt {n})$ +* Space complexity: $O(1)$ + +--- + +## 2. Binary Search + +::tabs-start + +```python +class Solution: + def arrangeCoins(self, n: int) -> int: + l, r = 1, n + res = 0 + + while l <= r: + mid = (l + r) // 2 + coins = (mid * (mid + 1)) // 2 + if coins > n: + r = mid - 1 + else: + l = mid + 1 + res = max(res, mid) + + return res +``` + +```java +public class Solution { + public int arrangeCoins(int n) { + int l = 1, r = n, res = 0; + + while (l <= r) { + int mid = l + (r - l) / 2; + long coins = (long) mid * (mid + 1) / 2; + if (coins > n) { + r = mid - 1; + } else { + l = mid + 1; + res = Math.max(res, mid); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int arrangeCoins(int n) { + long long l = 1, r = n, res = 0; + + while (l <= r) { + long long mid = l + (r - l) / 2; + long long coins = (mid * (mid + 1)) / 2; + if (coins > n) { + r = mid - 1; + } else { + l = mid + 1; + res = max(res, mid); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + arrangeCoins(n) { + let l = 1, r = n, res = 0; + + while (l <= r) { + let mid = Math.floor((l + r) / 2); + let coins = (mid * (mid + 1)) / 2; + if (coins > n) { + r = mid - 1; + } else { + l = mid + 1; + res = Math.max(res, mid); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Binary Search (Optimal) + +::tabs-start + +```python +class Solution: + def arrangeCoins(self, n: int) -> int: + if n <= 3: + return n if n == 1 else n - 1 + + l, r = 1, (n // 2) + 1 + while l < r: + mid = (l + r) // 2 + if (mid * (mid + 1)) // 2 <= n: + l = mid + 1 + else: + r = mid + + return l - 1 +``` + +```java +public class Solution { + public int arrangeCoins(int n) { + if (n <= 3) { + return n == 1 ? 1 : n - 1; + } + + int l = 1, r = (n / 2) + 1; + while (l < r) { + int mid = l + (r - l) / 2; + long coins = (long) mid * (mid + 1) / 2; + if (coins <= n) { + l = mid + 1; + } else { + r = mid; + } + } + + return l - 1; + } +} +``` + +```cpp +class Solution { +public: + int arrangeCoins(int n) { + if (n <= 3) { + return n == 1 ? 1 : n - 1; + } + + int l = 1, r = (n / 2) + 1; + while (l < r) { + int mid = l + (r - l) / 2; + long long coins = (mid * (mid + 1LL)) / 2; + if (coins <= n) { + l = mid + 1; + } else { + r = mid; + } + } + + return l - 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + arrangeCoins(n) { + if (n <= 3) { + return n == 1 ? 1 : n - 1; + } + + let l = 1, r = (n / 2) + 1; + while (l < r) { + let mid = Math.floor((l + r) / 2); + let coins = (mid * (mid + 1)) / 2; + if (coins <= n) { + l = mid + 1; + } else { + r = mid; + } + } + + return l - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Bit Manipulation + +::tabs-start + +```python +class Solution: + def arrangeCoins(self, n: int) -> int: + mask = 1 << 15 + rows = 0 + while mask > 0: + rows |= mask + coins = rows * (rows + 1) // 2 + if coins > n: + rows ^= mask + mask >>= 1 + return rows +``` + +```java +public class Solution { + public int arrangeCoins(int n) { + int mask = 1 << 15; + int rows = 0; + while (mask > 0) { + rows |= mask; + long coins = (long) rows * (rows + 1) / 2; + if (coins > n) { + rows ^= mask; + } + mask >>= 1; + } + return rows; + } +} +``` + +```cpp +class Solution { +public: + int arrangeCoins(int n) { + int mask = 1 << 15; + int rows = 0; + while (mask > 0) { + rows |= mask; + long long coins = (long long) rows * (rows + 1) / 2; + if (coins > n) { + rows ^= mask; + } + mask >>= 1; + } + return rows; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + arrangeCoins(n) { + let mask = 1 << 15; + let rows = 0; + while (mask > 0) { + rows |= mask; + let coins = (rows * (rows + 1)) / 2; + if (coins > n) { + rows ^= mask; + } + mask >>= 1; + } + return rows; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ since we iterate $15$ times. +* Space complexity: $O(1)$ + +--- + +## 5. Math + +::tabs-start + +```python +class Solution: + def arrangeCoins(self, n: int) -> int: + return int(sqrt(2 * n + 0.25) - 0.5) +``` + +```java +public class Solution { + public int arrangeCoins(int n) { + return (int) (Math.sqrt(2L * n + 0.25) - 0.5); + } +} +``` + +```cpp +class Solution { +public: + int arrangeCoins(int n) { + return (int)(sqrt(2.0 * n + 0.25) - 0.5); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + arrangeCoins(n) { + return Math.floor(Math.sqrt(2 * n + 0.25) - 0.5); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ or $O(\sqrt {n})$ depending on the language. +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/array-with-elements-not-equal-to-average-of-neighbors.md b/articles/array-with-elements-not-equal-to-average-of-neighbors.md new file mode 100644 index 000000000..4e4a1daa1 --- /dev/null +++ b/articles/array-with-elements-not-equal-to-average-of-neighbors.md @@ -0,0 +1,338 @@ +## 1. Greedy + +::tabs-start + +```python +class Solution: + def rearrangeArray(self, nums: List[int]) -> List[int]: + nums.sort() + res = [] + l, r = 0, len(nums) - 1 + while len(res) != len(nums): + res.append(nums[l]) + l += 1 + if l <= r: + res.append(nums[r]) + r -= 1 + return res +``` + +```java +public class Solution { + public int[] rearrangeArray(int[] nums) { + Arrays.sort(nums); + int[] res = new int[nums.length]; + int l = 0, r = nums.length - 1; + int idx = 0; + + while (idx != nums.length) { + res[idx++] = nums[l++]; + if (l <= r) { + res[idx++] = nums[r--]; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector rearrangeArray(vector& nums) { + sort(nums.begin(), nums.end()); + vector res; + int l = 0, r = nums.size() - 1; + + while (res.size() != nums.size()) { + res.push_back(nums[l++]); + if (l <= r) { + res.push_back(nums[r--]); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + rearrangeArray(nums) { + nums.sort((a, b) => a - b); + const res = []; + let l = 0, r = nums.length - 1; + + while (res.length !== nums.length) { + res.push(nums[l++]); + if (l <= r) { + res.push(nums[r--]); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Greedy (Space Optimized) + +::tabs-start + +```python +class Solution: + def rearrangeArray(self, nums: List[int]) -> List[int]: + nums.sort() + for i in range(1, len(nums), 2): + nums[i], nums[i - 1] = nums[i - 1], nums[i] + return nums +``` + +```java +public class Solution { + public int[] rearrangeArray(int[] nums) { + Arrays.sort(nums); + for (int i = 1; i < nums.length; i += 2) { + int temp = nums[i]; + nums[i] = nums[i - 1]; + nums[i - 1] = temp; + } + return nums; + } +} +``` + +```cpp +class Solution { +public: + vector rearrangeArray(vector& nums) { + sort(nums.begin(), nums.end()); + for (int i = 1; i < nums.size(); i += 2) { + swap(nums[i], nums[i - 1]); + } + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + rearrangeArray(nums) { + nums.sort((a, b) => a - b); + for (let i = 1; i < nums.length; i += 2) { + [nums[i], nums[i - 1]] = [nums[i - 1], nums[i]]; + } + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Greedy (Optimal) - I + +::tabs-start + +```python +class Solution: + def rearrangeArray(self, nums: List[int]) -> List[int]: + n = len(nums) + + for i in range(1, n - 1): + if 2 * nums[i] == (nums[i - 1] + nums[i + 1]): + nums[i], nums[i + 1] = nums[i + 1], nums[i] + + for i in range(n - 2, 0, -1): + if 2 * nums[i] == (nums[i - 1] + nums[i + 1]): + nums[i], nums[i - 1] = nums[i - 1], nums[i] + + return nums +``` + +```java +public class Solution { + public int[] rearrangeArray(int[] nums) { + int n = nums.length; + + for (int i = 1; i < n - 1; i++) { + if (2 * nums[i] == (nums[i - 1] + nums[i + 1])) { + int temp = nums[i]; + nums[i] = nums[i + 1]; + nums[i + 1] = temp; + } + } + + for (int i = n - 2; i > 0; i--) { + if (2 * nums[i] == (nums[i - 1] + nums[i + 1])) { + int temp = nums[i]; + nums[i] = nums[i - 1]; + nums[i - 1] = temp; + } + } + + return nums; + } +} +``` + +```cpp +class Solution { +public: + vector rearrangeArray(vector& nums) { + int n = nums.size(); + + for (int i = 1; i < n - 1; i++) { + if (2 * nums[i] == (nums[i - 1] + nums[i + 1])) { + swap(nums[i], nums[i + 1]); + } + } + + for (int i = n - 2; i > 0; i--) { + if (2 * nums[i] == (nums[i - 1] + nums[i + 1])) { + swap(nums[i], nums[i - 1]); + } + } + + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + rearrangeArray(nums) { + const n = nums.length; + + for (let i = 1; i < n - 1; i++) { + if (2 * nums[i] === nums[i - 1] + nums[i + 1]) { + [nums[i], nums[i + 1]] = [nums[i + 1], nums[i]]; + } + } + + for (let i = n - 2; i > 0; i--) { + if (2 * nums[i] === nums[i - 1] + nums[i + 1]) { + [nums[i], nums[i - 1]] = [nums[i - 1], nums[i]]; + } + } + + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Greedy Optimal - II + +::tabs-start + +```python +class Solution: + def rearrangeArray(self, nums: List[int]) -> List[int]: + increase = nums[0] < nums[1] + for i in range(1, len(nums) - 1): + if ((increase and nums[i] < nums[i + 1]) or + (not increase and nums[i] > nums[i + 1]) + ): + nums[i], nums[i + 1] = nums[i + 1], nums[i] + increase = not increase + return nums +``` + +```java +public class Solution { + public int[] rearrangeArray(int[] nums) { + boolean increase = nums[0] < nums[1]; + for (int i = 1; i < nums.length - 1; i++) { + if ((increase && nums[i] < nums[i + 1]) || + (!increase && nums[i] > nums[i + 1])) { + int temp = nums[i]; + nums[i] = nums[i + 1]; + nums[i + 1] = temp; + } + increase = !increase; + } + return nums; + } +} +``` + +```cpp +class Solution { +public: + vector rearrangeArray(vector& nums) { + bool increase = nums[0] < nums[1]; + for (int i = 1; i < nums.size() - 1; i++) { + if ((increase && nums[i] < nums[i + 1]) || + (!increase && nums[i] > nums[i + 1])) { + swap(nums[i], nums[i + 1]); + } + increase = !increase; + } + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + rearrangeArray(nums) { + let increase = nums[0] < nums[1]; + for (let i = 1; i < nums.length - 1; i++) { + if ((increase && nums[i] < nums[i + 1]) || + (!increase && nums[i] > nums[i + 1])) { + [nums[i], nums[i + 1]] = [nums[i + 1], nums[i]]; + } + increase = !increase; + } + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/as-far-from-land-as-possible.md b/articles/as-far-from-land-as-possible.md new file mode 100644 index 000000000..15382ba80 --- /dev/null +++ b/articles/as-far-from-land-as-possible.md @@ -0,0 +1,757 @@ +## 1. Brute Force (BFS) + +::tabs-start + +```python +class Solution: + def maxDistance(self, grid: list[list[int]]) -> int: + N = len(grid) + direct = [[0, 1], [0, -1], [1, 0], [-1, 0]] + + def bfs(row, col): + q = deque([(row, col)]) + visit = [[False] * N for _ in range(N)] + dist, visit[row][col] = 0, True + + while q: + dist += 1 + for _ in range(len(q)): + r, c = q.popleft() + for dx, dy in direct: + newR, newC = r + dx, c + dy + if min(newR, newC) < 0 or max(newR, newC) >= N or visit[newR][newC]: + continue + if grid[newR][newC] == 1: + return dist + visit[newR][newC] = True + q.append((newR, newC)) + return -1 + + res = -1 + for r in range(N): + for c in range(N): + if grid[r][c] == 0: + res = max(res, bfs(r, c)) + if res == -1: + return res + + return res +``` + +```java +public class Solution { + private static final int[][] direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + public int maxDistance(int[][] grid) { + int N = grid.length; + int res = -1; + + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 0) { + res = Math.max(res, bfs(grid, r, c, N)); + if (res == -1) return res; + } + } + } + return res; + } + + private int bfs(int[][] grid, int row, int col, int N) { + Queue q = new LinkedList<>(); + boolean[][] visit = new boolean[N][N]; + q.offer(new int[]{row, col}); + visit[row][col] = true; + int dist = 0; + + while (!q.isEmpty()) { + dist++; + for (int i = q.size(); i > 0; i--) { + int[] cell = q.poll(); + int r = cell[0], c = cell[1]; + + for (int[] d : direct) { + int newR = r + d[0], newC = c + d[1]; + if (newR < 0 || newC < 0 || newR >= N || newC >= N || visit[newR][newC]) + continue; + if (grid[newR][newC] == 1) + return dist; + + visit[newR][newC] = true; + q.offer(new int[]{newR, newC}); + } + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int maxDistance(vector>& grid) { + int N = grid.size(); + int res = -1; + + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 0) { + res = max(res, bfs(grid, r, c, N)); + if (res == -1) return res; + } + } + } + return res; + } + +private: + const int direct[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + int bfs(vector>& grid, int row, int col, int N) { + queue> q; + vector> visit(N, vector(N, false)); + q.push({row, col}); + visit[row][col] = true; + int dist = 0; + + while (!q.empty()) { + dist++; + for (int i = q.size(); i > 0; i--) { + auto [r, c] = q.front();q.pop(); + + for (auto& d : direct) { + int newR = r + d[0], newC = c + d[1]; + if (newR < 0 || newC < 0 || newR >= N || newC >= N || visit[newR][newC]) + continue; + if (grid[newR][newC] == 1) + return dist; + + visit[newR][newC] = true; + q.push({newR, newC}); + } + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + maxDistance(grid) { + const N = grid.length; + const direct = [[0, 1], [0, -1], [1, 0], [-1, 0]]; + + const bfs = (row, col) => { + const q = new Queue([[row, col]]); + const visit = Array.from({ length: N }, () => Array(N).fill(false)); + visit[row][col] = true; + let dist = 0; + + while (!q.isEmpty()) { + dist++; + for (let i = q.size(); i > 0; i--) { + const [r, c] = q.pop(); + for (let d = 0; d < 4; d++) { + let newR = r + direct[d][0], newC = c + direct[d][1]; + if (newR < 0 || newC < 0 || newR >= N || newC >= N || visit[newR][newC]) + continue; + if (grid[newR][newC] === 1) + return dist; + + visit[newR][newC] = true; + q.push([newR, newC]); + } + } + } + return -1; + }; + + let res = -1; + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 0) { + res = Math.max(res, bfs(r, c)); + if (res === -1) return res; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 4)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Multi Source BFS (Overwriting Input) + +::tabs-start + +```python +class Solution: + def maxDistance(self, grid: List[List[int]]) -> int: + N = len(grid) + q = deque() + + for r in range(N): + for c in range(N): + if grid[r][c]: + q.append([r, c]) + + res = -1 + direct = [[0, 1], [1, 0], [0, -1], [-1, 0]] + + while q: + r, c = q.popleft() + res = grid[r][c] + + for dr, dc in direct: + newR, newC = r + dr, c + dc + if min(newR, newC) >= 0 and max(newR, newC) < N and grid[newR][newC] == 0: + q.append([newR, newC]) + grid[newR][newC] = grid[r][c] + 1 + + return res - 1 if res > 1 else -1 +``` + +```java +public class Solution { + private static final int[][] direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + public int maxDistance(int[][] grid) { + int N = grid.length; + Queue q = new LinkedList<>(); + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + q.offer(new int[]{r, c}); + } + } + } + + int res = -1; + while (!q.isEmpty()) { + int[] cell = q.poll(); + int r = cell[0], c = cell[1]; + res = grid[r][c]; + for (int d = 0; d < 4; d++) { + int newR = r + direct[d][0], newC = c + direct[d][1]; + if (newR >= 0 && newC >= 0 && newR < N && newC < N && grid[newR][newC] == 0) { + q.offer(new int[]{newR, newC}); + grid[newR][newC] = grid[r][c] + 1; + } + } + } + return res > 1 ? res - 1 : -1; + } +} +``` + +```cpp +class Solution { + const int direct[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + +public: + int maxDistance(vector>& grid) { + int N = grid.size(); + queue> q; + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + q.push({r, c}); + } + } + } + + int res = -1; + while (!q.empty()) { + auto [r, c] = q.front();q.pop(); + res = grid[r][c]; + for (int d = 0; d < 4; d++) { + int newR = r + direct[d][0], newC = c + direct[d][1]; + if (newR >= 0 && newC >= 0 && newR < N && newC < N && grid[newR][newC] == 0) { + q.push({newR, newC}); + grid[newR][newC] = grid[r][c] + 1; + } + } + } + return res > 1 ? res - 1 : -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + maxDistance(grid) { + const N = grid.length; + const q = new Queue(); + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) { + q.push([r, c]); + } + } + } + + let res = -1; + const direct = [[0, 1], [0, -1], [1, 0], [-1, 0]]; + while (!q.isEmpty()) { + const [r, c] = q.pop(); + res = grid[r][c]; + for (let d = 0; d < 4; d++) { + let newR = r + direct[d][0], newC = c + direct[d][1]; + if (newR >= 0 && newC >= 0 && newR < N && newC < N && grid[newR][newC] === 0) { + q.push([newR, newC]); + grid[newR][newC] = grid[r][c] + 1; + } + } + } + return res > 1 ? res - 1 : -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Multi Source BFS + +::tabs-start + +```python +class Solution: + def maxDistance(self, grid: List[List[int]]) -> int: + N = len(grid) + direct = [0, 1, 0, -1, 0] + visit = [[False] * N for _ in range(N)] + q = deque() + + for r in range(N): + for c in range(N): + if grid[r][c] == 1: + visit[r][c] = True + q.append((r, c)) + + res = 0 + while q: + res += 1 + for _ in range(len(q)): + r, c = q.popleft() + for d in range(4): + newR, newC = r + direct[d], c + direct[d + 1] + if 0 <= newR < N and 0 <= newC < N and not visit[newR][newC]: + q.append((newR, newC)) + visit[newR][newC] = True + + return res - 1 if res > 1 else -1 +``` + +```java +public class Solution { + public int maxDistance(int[][] grid) { + int N = grid.length; + int[] direct = {0, 1, 0, -1, 0}; + boolean[][] visit = new boolean[N][N]; + Queue q = new LinkedList<>(); + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + visit[r][c] = true; + q.offer(new int[]{r, c}); + } + } + } + + int res = 0; + while (!q.isEmpty()) { + res++; + for (int i = q.size(); i > 0; i--) { + int[] cur = q.poll(); + int r = cur[0], c = cur[1]; + for (int d = 0; d < 4; d++) { + int newR = r + direct[d], newC = c + direct[d + 1]; + if (newR >= 0 && newC >= 0 && newR < N && newC < N && !visit[newR][newC]) { + q.offer(new int[]{newR, newC}); + visit[newR][newC] = true; + } + } + } + } + return res > 1 ? res - 1 : -1; + } +} +``` + +```cpp +class Solution { +public: + int maxDistance(vector>& grid) { + int N = grid.size(); + vector direct = {0, 1, 0, -1, 0}; + vector> visit(N, vector(N, false)); + queue> q; + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + visit[r][c] = true; + q.push({r, c}); + } + } + } + + int res = 0; + while (!q.empty()) { + res++; + for (int i = q.size(); i > 0; i--) { + auto [r, c] = q.front();q.pop(); + for (int d = 0; d < 4; d++) { + int newR = r + direct[d], newC = c + direct[d + 1]; + if (newR >= 0 && newC >= 0 && newR < N && newC < N && !visit[newR][newC]) { + q.push({newR, newC}); + visit[newR][newC] = true; + } + } + } + } + return res > 1 ? res - 1 : -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + maxDistance(grid) { + const N = grid.length; + const direct = [0, 1, 0, -1, 0]; + const visit = Array.from({ length: N }, () => Array(N).fill(false)); + const q = new Queue(); + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) { + visit[r][c] = true; + q.push([r, c]); + } + } + } + + let res = 0; + while (!q.isEmpty()) { + res++; + for (let i = q.size(); i > 0; i--) { + const [r, c] = q.pop(); + for (let d = 0; d < 4; d++) { + let newR = r + direct[d], newC = c + direct[d + 1]; + if (newR >= 0 && newC >= 0 && newR < N && newC < N && !visit[newR][newC]) { + q.push([newR, newC]); + visit[newR][newC] = true; + } + } + } + } + return res > 1 ? res - 1 : -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Dynamic Programming (Overwrting the Input) + +::tabs-start + +```python +class Solution: + def maxDistance(self, grid: List[List[int]]) -> int: + N, INF = len(grid), 10**6 + + for r in range(N): + for c in range(N): + if grid[r][c] == 1: + continue + grid[r][c] = INF + if r > 0: + grid[r][c] = min(grid[r][c], grid[r - 1][c] + 1) + if c > 0: + grid[r][c] = min(grid[r][c], grid[r][c - 1] + 1) + + res = 0 + for r in range(N - 1, -1, -1): + for c in range(N - 1, -1, -1): + if grid[r][c] == 1: + continue + if r < N - 1: + grid[r][c] = min(grid[r][c], grid[r + 1][c] + 1) + if c < N - 1: + grid[r][c] = min(grid[r][c], grid[r][c + 1] + 1) + res = max(res, grid[r][c]) + + return res - 1 if res < INF else -1 +``` + +```java +public class Solution { + public int maxDistance(int[][] grid) { + int N = grid.length, INF = 1000000; + + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) continue; + grid[r][c] = INF; + if (r > 0) grid[r][c] = Math.min(grid[r][c], grid[r - 1][c] + 1); + if (c > 0) grid[r][c] = Math.min(grid[r][c], grid[r][c - 1] + 1); + } + } + + int res = 0; + for (int r = N - 1; r >= 0; r--) { + for (int c = N - 1; c >= 0; c--) { + if (grid[r][c] == 1) continue; + if (r < N - 1) grid[r][c] = Math.min(grid[r][c], grid[r + 1][c] + 1); + if (c < N - 1) grid[r][c] = Math.min(grid[r][c], grid[r][c + 1] + 1); + res = Math.max(res, grid[r][c]); + } + } + + return res < INF ? res - 1 : -1; + } +} +``` + +```cpp +class Solution { +public: + int maxDistance(vector>& grid) { + int N = grid.size(), INF = 1000000; + + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) continue; + grid[r][c] = INF; + if (r > 0) grid[r][c] = min(grid[r][c], grid[r - 1][c] + 1); + if (c > 0) grid[r][c] = min(grid[r][c], grid[r][c - 1] + 1); + } + } + + int res = 0; + for (int r = N - 1; r >= 0; r--) { + for (int c = N - 1; c >= 0; c--) { + if (grid[r][c] == 1) continue; + if (r < N - 1) grid[r][c] = min(grid[r][c], grid[r + 1][c] + 1); + if (c < N - 1) grid[r][c] = min(grid[r][c], grid[r][c + 1] + 1); + res = max(res, grid[r][c]); + } + } + + return res < INF ? res - 1 : -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + maxDistance(grid) { + const N = grid.length, INF = 1000000; + + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) continue; + grid[r][c] = INF; + if (r > 0) grid[r][c] = Math.min(grid[r][c], grid[r - 1][c] + 1); + if (c > 0) grid[r][c] = Math.min(grid[r][c], grid[r][c - 1] + 1); + } + } + + let res = 0; + for (let r = N - 1; r >= 0; r--) { + for (let c = N - 1; c >= 0; c--) { + if (grid[r][c] === 1) continue; + if (r < N - 1) grid[r][c] = Math.min(grid[r][c], grid[r + 1][c] + 1); + if (c < N - 1) grid[r][c] = Math.min(grid[r][c], grid[r][c + 1] + 1); + res = Math.max(res, grid[r][c]); + } + } + + return res < INF ? res - 1 : -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 5. Dynamic Programming + +::tabs-start + +```python +class Solution: + def maxDistance(self, grid: List[List[int]]) -> int: + N = len(grid) + res, INF = -1, 10**6 + dp = [[INF] * (N + 2) for _ in range(N + 2)] + + for r in range(1, N + 1): + for c in range(1, N + 1): + if grid[r - 1][c - 1] == 1: + dp[r][c] = 0 + else: + dp[r][c] = min(dp[r - 1][c], dp[r][c - 1]) + 1 + + for r in range(N, 0, -1): + for c in range(N, 0, -1): + if grid[r - 1][c - 1] == 0: + dp[r][c] = min(dp[r][c], min(dp[r + 1][c], dp[r][c + 1]) + 1) + res = max(res, dp[r][c]) + + return res if res < INF else -1 +``` + +```java +public class Solution { + public int maxDistance(int[][] grid) { + int N = grid.length; + int INF = 1000000; + int[][] dp = new int[N + 2][N + 2]; + for (int[] row : dp) { + Arrays.fill(row, INF); + } + + for (int r = 1; r <= N; r++) { + for (int c = 1; c <= N; c++) { + if (grid[r - 1][c - 1] == 1) { + dp[r][c] = 0; + } else { + dp[r][c] = Math.min(dp[r - 1][c], dp[r][c - 1]) + 1; + } + } + } + + int res = -1; + for (int r = N; r > 0; r--) { + for (int c = N; c > 0; c--) { + if (grid[r - 1][c - 1] == 0) { + dp[r][c] = Math.min(dp[r][c], Math.min(dp[r + 1][c], dp[r][c + 1]) + 1); + res = Math.max(res, dp[r][c]); + } + } + } + return res < INF ? res : -1; + } +} +``` + +```cpp +class Solution { +public: + int maxDistance(vector>& grid) { + int N = grid.size(); + int INF = 1000000, res = -1; + vector> dp(N + 2, vector(N + 2, INF)); + + for (int r = 1; r <= N; r++) { + for (int c = 1; c <= N; c++) { + if (grid[r - 1][c - 1] == 1) { + dp[r][c] = 0; + } else { + dp[r][c] = min(dp[r - 1][c], dp[r][c - 1]) + 1; + } + } + } + + for (int r = N; r > 0; r--) { + for (int c = N; c > 0; c--) { + if (grid[r - 1][c - 1] == 0) { + dp[r][c] = min(dp[r][c], min(dp[r + 1][c], dp[r][c + 1]) + 1); + res = max(res, dp[r][c]); + } + } + } + return res < INF ? res : -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + maxDistance(grid) { + const N = grid.length; + const INF = 1000000; + let res = -1; + let dp = Array.from({ length: N + 2 }, () => Array(N + 2).fill(INF)); + + for (let r = 1; r <= N; r++) { + for (let c = 1; c <= N; c++) { + if (grid[r - 1][c - 1] === 1) { + dp[r][c] = 0; + } else { + dp[r][c] = Math.min(dp[r - 1][c], dp[r][c - 1]) + 1; + } + } + } + + for (let r = N; r > 0; r--) { + for (let c = N; c > 0; c--) { + if (grid[r - 1][c - 1] === 0) { + dp[r][c] = Math.min(dp[r][c], Math.min(dp[r + 1][c], dp[r][c + 1]) + 1); + res = Math.max(res, dp[r][c]); + } + } + } + return res < INF ? res : -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/assign-cookies.md b/articles/assign-cookies.md new file mode 100644 index 000000000..b591a8ef9 --- /dev/null +++ b/articles/assign-cookies.md @@ -0,0 +1,300 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def findContentChildren(self, g: List[int], s: List[int]) -> int: + s.sort() + res = 0 + + for i in g: + minIdx = -1 + for j in range(len(s)): + if s[j] < i: + continue + + if minIdx == -1 or s[minIdx] > s[j]: + minIdx = j + + if minIdx != -1: + s[minIdx] = -1 + res += 1 + + return res +``` + +```java +public class Solution { + public int findContentChildren(int[] g, int[] s) { + Arrays.sort(s); + int res = 0; + + for (int i : g) { + int minIdx = -1; + for (int j = 0; j < s.length; j++) { + if (s[j] < i) continue; + + if (minIdx == -1 || s[minIdx] > s[j]) { + minIdx = j; + } + } + + if (minIdx != -1) { + s[minIdx] = -1; + res++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int findContentChildren(vector& g, vector& s) { + sort(s.begin(), s.end()); + int res = 0; + + for (int i : g) { + int minIdx = -1; + for (int j = 0; j < s.size(); j++) { + if (s[j] < i) continue; + + if (minIdx == -1 || s[minIdx] > s[j]) { + minIdx = j; + } + } + + if (minIdx != -1) { + s[minIdx] = -1; + res++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} g + * @param {number[]} s + * @return {number} + */ + findContentChildren(g, s) { + s.sort((a, b) => a - b); + let res = 0; + + for (let i of g) { + let minIdx = -1; + for (let j = 0; j < s.length; j++) { + if (s[j] < i) continue; + + if (minIdx === -1 || s[minIdx] > s[j]) { + minIdx = j; + } + } + + if (minIdx !== -1) { + s[minIdx] = -1; + res++; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m + m \log m)$ +* Space complexity: $O(1)$ or $O(m)$ depending on the sorting algorithm. + +> Where $n$ is the size of the array $g$ and $m$ is the size of the array $s$. + +--- + +## 2. Two Pointers - I + +::tabs-start + +```python +class Solution: + def findContentChildren(self, g: List[int], s: List[int]) -> int: + g.sort() + s.sort() + + i = j = 0 + while i < len(g): + while j < len(s) and g[i] > s[j]: + j += 1 + if j == len(s): + break + i += 1 + j += 1 + return i +``` + +```java +public class Solution { + public int findContentChildren(int[] g, int[] s) { + Arrays.sort(g); + Arrays.sort(s); + + int i = 0, j = 0; + while (i < g.length) { + while (j < s.length && g[i] > s[j]) { + j++; + } + if (j == s.length) break; + i++; + j++; + } + return i; + } +} +``` + +```cpp +class Solution { +public: + int findContentChildren(vector& g, vector& s) { + sort(g.begin(), g.end()); + sort(s.begin(), s.end()); + + int i = 0, j = 0; + while (i < g.size()) { + while (j < s.size() && g[i] > s[j]) { + j++; + } + if (j == s.size()) break; + i++; + j++; + } + return i; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} g + * @param {number[]} s + * @return {number} + */ + findContentChildren(g, s) { + g.sort((a, b) => a - b); + s.sort((a, b) => a - b); + + let i = 0, j = 0; + while (i < g.length) { + while (j < s.length && g[i] > s[j]) { + j++; + } + if (j === s.length) break; + i++; + j++; + } + return i; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + m \log m)$ +* Space complexity: $O(1)$ or $O(n + m)$ depending on the sorting algorithm. + +> Where $n$ is the size of the array $g$ and $m$ is the size of the array $s$. + +--- + +## 3. Two Pointers - II + +::tabs-start + +```python +class Solution: + def findContentChildren(self, g: List[int], s: List[int]) -> int: + g.sort() + s.sort() + + i = j = 0 + while i < len(g) and j < len(s): + if g[i] <= s[j]: + i += 1 + j += 1 + + return i +``` + +```java +public class Solution { + public int findContentChildren(int[] g, int[] s) { + Arrays.sort(g); + Arrays.sort(s); + + int i = 0; + for (int j = 0; i < g.length && j < s.length; j++) { + if (g[i] <= s[j]) i++; + } + return i; + } +} +``` + +```cpp +class Solution { +public: + int findContentChildren(vector& g, vector& s) { + sort(g.begin(), g.end()); + sort(s.begin(), s.end()); + + int i = 0; + for (int j = 0; i < g.size() && j < s.size(); j++) { + if (g[i] <= s[j]) i++; + } + return i; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} g + * @param {number[]} s + * @return {number} + */ + findContentChildren(g, s) { + g.sort((a, b) => a - b); + s.sort((a, b) => a - b); + + let i = 0; + for (let j = 0; i < g.length && j < s.length; j++) { + if (g[i] <= s[j]) i++; + } + return i; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + m \log m)$ +* Space complexity: $O(1)$ or $O(n + m)$ depending on the sorting algorithm. + +> Where $n$ is the size of the array $g$ and $m$ is the size of the array $s$. \ No newline at end of file diff --git a/articles/asteroid-collision.md b/articles/asteroid-collision.md new file mode 100644 index 000000000..94a5993d3 --- /dev/null +++ b/articles/asteroid-collision.md @@ -0,0 +1,302 @@ +## 1. Stack + +::tabs-start + +```python +class Solution: + def asteroidCollision(self, asteroids: List[int]) -> List[int]: + stack = [] + for a in asteroids: + while stack and a < 0 and stack[-1] > 0: + diff = a + stack[-1] + if diff < 0: + stack.pop() + elif diff > 0: + a = 0 + else: + a = 0 + stack.pop() + if a: + stack.append(a) + return stack +``` + +```java +public class Solution { + public int[] asteroidCollision(int[] asteroids) { + Stack stack = new Stack<>(); + for (int a : asteroids) { + while (!stack.isEmpty() && a < 0 && stack.peek() > 0) { + int diff = a + stack.peek(); + if (diff < 0) { + stack.pop(); + } else if (diff > 0) { + a = 0; + } else { + a = 0; + stack.pop(); + } + } + if (a != 0) { + stack.add(a); + } + } + return stack.stream().mapToInt(i -> i).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector asteroidCollision(vector& asteroids) { + vector stack; + for (int& a : asteroids) { + while (!stack.empty() && a < 0 && stack.back() > 0) { + int diff = a + stack.back(); + if (diff < 0) { + stack.pop_back(); + } else if (diff > 0) { + a = 0; + } else { + a = 0; + stack.pop_back(); + } + } + if (a != 0) { + stack.push_back(a); + } + } + return stack; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} asteroids + * @return {number[]} + */ + asteroidCollision(asteroids) { + const stack = []; + for (let a of asteroids) { + while (stack.length && a < 0 && stack[stack.length - 1] > 0) { + const diff = a + stack[stack.length - 1]; + if (diff < 0) { + stack.pop(); + } else if (diff > 0) { + a = 0; + } else { + a = 0; + stack.pop(); + } + } + if (a !== 0) { + stack.push(a); + } + } + return stack; + } +} +``` + +```csharp +public class Solution { + public int[] AsteroidCollision(int[] asteroids) { + Stack stack = new Stack(); + + foreach (int a in asteroids) { + int current = a; + while (stack.Count > 0 && current < 0 && stack.Peek() > 0) { + int diff = current + stack.Peek(); + if (diff < 0) { + stack.Pop(); + } else if (diff > 0) { + current = 0; + } else { + current = 0; + stack.Pop(); + } + } + if (current != 0) { + stack.Push(current); + } + } + + int[] result = stack.Reverse().ToArray(); + return result; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Without Stack + +::tabs-start + +```python +class Solution: + def asteroidCollision(self, asteroids: List[int]) -> List[int]: + n = len(asteroids) + j = -1 + + for a in asteroids: + while j >= 0 and asteroids[j] > 0 and a < 0: + if asteroids[j] > abs(a): + a = 0 + break + elif asteroids[j] == abs(a): + j -= 1 + a = 0 + break + else: + j -= 1 + if a: + j += 1 + asteroids[j] = a + + return asteroids[:j + 1] +``` + +```java +public class Solution { + public int[] asteroidCollision(int[] asteroids) { + int n = asteroids.length; + int j = -1; + + for (int a : asteroids) { + while (j >= 0 && asteroids[j] > 0 && a < 0) { + if (asteroids[j] > Math.abs(a)) { + a = 0; + break; + } else if (asteroids[j] == Math.abs(a)) { + j--; + a = 0; + break; + } else { + j--; + } + } + if (a != 0) { + asteroids[++j] = a; + } + } + + return Arrays.copyOfRange(asteroids, 0, j + 1); + } +} +``` + +```cpp +class Solution { +public: + vector asteroidCollision(vector& asteroids) { + int n = asteroids.size(); + int j = -1; + + for (int& a : asteroids) { + while (j >= 0 && asteroids[j] > 0 && a < 0) { + if (asteroids[j] > abs(a)) { + a = 0; + break; + } else if (asteroids[j] == abs(a)) { + j--; + a = 0; + break; + } else { + j--; + } + } + if (a != 0) { + asteroids[++j] = a; + } + } + + asteroids.resize(j + 1); + return asteroids; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} asteroids + * @return {number[]} + */ + asteroidCollision(asteroids) { + let n = asteroids.length; + let j = -1; + + for (let a of asteroids) { + while (j >= 0 && asteroids[j] > 0 && a < 0) { + if (asteroids[j] > Math.abs(a)) { + a = 0; + break; + } else if (asteroids[j] === Math.abs(a)) { + j--; + a = 0; + break; + } else { + j--; + } + } + if (a !== 0) { + asteroids[++j] = a; + } + } + + return asteroids.slice(0, j + 1); + } +} +``` + +```csharp +public class Solution { + public int[] AsteroidCollision(int[] asteroids) { + int n = asteroids.Length; + int j = -1; + + foreach (int a in asteroids) { + int current = a; + + while (j >= 0 && asteroids[j] > 0 && current < 0) { + if (asteroids[j] > Math.Abs(current)) { + current = 0; + break; + } else if (asteroids[j] == Math.Abs(current)) { + j--; + current = 0; + break; + } else { + j--; + } + } + + if (current != 0) { + asteroids[++j] = current; + } + } + + int[] result = new int[j + 1]; + Array.Copy(asteroids, 0, result, 0, j + 1); + return result; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for the output array. \ No newline at end of file diff --git a/articles/average-waiting-time.md b/articles/average-waiting-time.md new file mode 100644 index 000000000..0d6c7c818 --- /dev/null +++ b/articles/average-waiting-time.md @@ -0,0 +1,169 @@ +## 1. Simulation - I + +::tabs-start + +```python +class Solution: + def averageWaitingTime(self, customers: List[List[int]]) -> float: + t = 0 + total = 0 + + for arrival, order in customers: + if t > arrival: + total += t - arrival + else: + t = arrival + total += order + t += order + + return total / len(customers) +``` + +```java +public class Solution { + public double averageWaitingTime(int[][] customers) { + long t = 0, total = 0; + + for (int[] c : customers) { + int arrival = c[0], order = c[1]; + if (t > arrival) { + total += t - arrival; + } else { + t = arrival; + } + total += order; + t += order; + } + + return (double) total / customers.length; + } +} +``` + +```cpp +class Solution { +public: + double averageWaitingTime(vector>& customers) { + long long t = 0, total = 0; + + for (auto& c : customers) { + int arrival = c[0], order = c[1]; + if (t > arrival) { + total += t - arrival; + } else { + t = arrival; + } + total += order; + t += order; + } + + return (double) total / customers.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} customers + * @return {number} + */ + averageWaitingTime(customers) { + let t = 0, total = 0; + + for (let [arrival, order] of customers) { + if (t > arrival) { + total += t - arrival; + } else { + t = arrival; + } + total += order; + t += order; + } + + return total / customers.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Simulation - II + +::tabs-start + +```python +class Solution: + def averageWaitingTime(self, customers: List[List[int]]) -> float: + t = total = 0 + for arrival, order in customers: + t = max(t, arrival) + order + total += t - arrival + return total / len(customers) +``` + +```java +public class Solution { + public double averageWaitingTime(int[][] customers) { + long t = 0, total = 0; + + for (int[] c : customers) { + int arrival = c[0], order = c[1]; + t = Math.max(t, arrival) + order; + total += t - arrival; + } + + return (double) total / customers.length; + } +} +``` + +```cpp +class Solution { +public: + double averageWaitingTime(vector>& customers) { + long long t = 0, total = 0; + + for (auto& c : customers) { + int arrival = c[0], order = c[1]; + t = max(t, (long long)arrival) + order; + total += t - arrival; + } + + return (double) total / customers.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} customers + * @return {number} + */ + averageWaitingTime(customers) { + let t = 0, total = 0; + + for (let [arrival, order] of customers) { + t = Math.max(t, arrival) + order; + total += t - arrival; + } + + return total / customers.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/backspace-string-compare.md b/articles/backspace-string-compare.md new file mode 100644 index 000000000..6064e5bd6 --- /dev/null +++ b/articles/backspace-string-compare.md @@ -0,0 +1,506 @@ +## 1. Stack + +::tabs-start + +```python +class Solution: + def backspaceCompare(self, s: str, t: str) -> bool: + def convert(s): + res = [] + for char in s: + if char == '#': + if res: + res.pop() + else: + res.append(char) + return "".join(res) + + return convert(s) == convert(t) +``` + +```java +public class Solution { + public boolean backspaceCompare(String s, String t) { + return convert(s).equals(convert(t)); + } + + private List convert(String s) { + List res = new ArrayList<>(); + for (char c : s.toCharArray()) { + if (c == '#') { + if (!res.isEmpty()) { + res.remove(res.size() - 1); + } + } else { + res.add(c); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + bool backspaceCompare(string s, string t) { + return convert(s) == convert(t); + } + +private: + string convert(const string& s) { + string res = ""; + for (char c : s) { + if (c == '#') { + if (!res.empty()) { + res.pop_back(); + } + } else { + res += c; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + backspaceCompare(s, t) { + const convert = (str) => { + const res = []; + for (const char of str) { + if (char === '#') { + if (res.length > 0) { + res.pop(); + } + } else { + res.push(char); + } + } + return res.join(''); + }; + return convert(s) === convert(t); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $t$. + +--- + +## 2. Reverse iteration + +::tabs-start + +```python +class Solution: + def backspaceCompare(self, s: str, t: str) -> bool: + def convert(s): + res = [] + backspace = 0 + for i in range(len(s) - 1, -1, -1): + if s[i] == '#': + backspace += 1 + elif backspace: + backspace -= 1 + else: + res.append(s[i]) + return res + + return convert(s) == convert(t) +``` + +```java +public class Solution { + public boolean backspaceCompare(String s, String t) { + return convert(s).equals(convert(t)); + } + + private List convert(String s) { + List res = new ArrayList<>(); + int backspace = 0; + for (int i = s.length() - 1; i >= 0; i--) { + if (s.charAt(i) == '#') { + backspace++; + } else if (backspace > 0) { + backspace--; + } else { + res.add(s.charAt(i)); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + bool backspaceCompare(string s, string t) { + return convert(s) == convert(t); + } + +private: + string convert(string s) { + string res; + int backspace = 0; + for (int i = s.size() - 1; i >= 0; i--) { + if (s[i] == '#') { + backspace++; + } else if (backspace > 0) { + backspace--; + } else { + res += s[i]; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + backspaceCompare(s, t) { + const convert = (s) => { + const res = []; + let backspace = 0; + for (let i = s.length - 1; i >= 0; i--) { + if (s[i] === '#') { + backspace++; + } else if (backspace > 0) { + backspace--; + } else { + res.push(s[i]); + } + } + return res.join(''); + }; + return convert(s) === convert(t); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $t$. + +--- + +## 3. Two Pointers - I + +::tabs-start + +```python +class Solution: + def backspaceCompare(self, s: str, t: str) -> bool: + def nextValidChar(string, index): + backspace = 0 + while index >= 0: + if string[index] == '#': + backspace += 1 + elif backspace > 0: + backspace -= 1 + else: + break + index -= 1 + return index + + index_s, index_t = len(s) - 1, len(t) - 1 + + while index_s >= 0 or index_t >= 0: + index_s = nextValidChar(s, index_s) + index_t = nextValidChar(t, index_t) + + char_s = s[index_s] if index_s >= 0 else "" + char_t = t[index_t] if index_t >= 0 else "" + + if char_s != char_t: + return False + + index_s -= 1 + index_t -= 1 + + return True +``` + +```java +public class Solution { + public boolean backspaceCompare(String s, String t) { + int indexS = s.length() - 1, indexT = t.length() - 1; + + while (indexS >= 0 || indexT >= 0) { + indexS = nextValidChar(s, indexS); + indexT = nextValidChar(t, indexT); + + char charS = indexS >= 0 ? s.charAt(indexS) : '\0'; + char charT = indexT >= 0 ? t.charAt(indexT) : '\0'; + + if (charS != charT) return false; + + indexS--; + indexT--; + } + + return true; + } + + private int nextValidChar(String str, int index) { + int backspace = 0; + + while (index >= 0) { + if (str.charAt(index) == '#') { + backspace++; + } else if (backspace > 0) { + backspace--; + } else { + break; + } + index--; + } + + return index; + } +} +``` + +```cpp +class Solution { +public: + bool backspaceCompare(string s, string t) { + int indexS = s.size() - 1, indexT = t.size() - 1; + + while (indexS >= 0 || indexT >= 0) { + indexS = nextValidChar(s, indexS); + indexT = nextValidChar(t, indexT); + + char charS = indexS >= 0 ? s[indexS] : '\0'; + char charT = indexT >= 0 ? t[indexT] : '\0'; + + if (charS != charT) return false; + + indexS--; + indexT--; + } + + return true; + } + +private: + int nextValidChar(string &str, int index) { + int backspace = 0; + + while (index >= 0) { + if (str[index] == '#') { + backspace++; + } else if (backspace > 0) { + backspace--; + } else { + break; + } + index--; + } + + return index; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + backspaceCompare(s, t) { + const nextValidChar = (str, index) => { + let backspace = 0; + + while (index >= 0) { + if (str[index] === '#') { + backspace++; + } else if (backspace > 0) { + backspace--; + } else { + break; + } + index--; + } + + return index; + }; + + let indexS = s.length - 1, indexT = t.length - 1; + + while (indexS >= 0 || indexT >= 0) { + indexS = nextValidChar(s, indexS); + indexT = nextValidChar(t, indexT); + + const charS = indexS >= 0 ? s[indexS] : ''; + const charT = indexT >= 0 ? t[indexT] : ''; + + if (charS !== charT) return false; + + indexS--; + indexT--; + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(1)$ + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $t$. + +--- + +## 4. Two Pointers - II + +::tabs-start + +```python +class Solution: + def backspaceCompare(self, s: str, t: str) -> bool: + index_s, index_t = len(s) - 1, len(t) - 1 + backspace_s = backspace_t = 0 + + while True: + while index_s >= 0 and (backspace_s or s[index_s] == '#'): + backspace_s += 1 if s[index_s] == '#' else -1 + index_s -= 1 + + while index_t >= 0 and (backspace_t or t[index_t] == '#'): + backspace_t += 1 if t[index_t] == '#' else -1 + index_t -= 1 + + if not (index_s >= 0 and index_t >= 0 and s[index_s] == t[index_t]): + return index_s == index_t == -1 + index_s, index_t = index_s - 1, index_t - 1 +``` + +```java +public class Solution { + public boolean backspaceCompare(String s, String t) { + int indexS = s.length() - 1, indexT = t.length() - 1; + int backspaceS = 0, backspaceT = 0; + + while (true) { + while (indexS >= 0 && (backspaceS > 0 || s.charAt(indexS) == '#')) { + backspaceS += s.charAt(indexS) == '#' ? 1 : -1; + indexS--; + } + + while (indexT >= 0 && (backspaceT > 0 || t.charAt(indexT) == '#')) { + backspaceT += t.charAt(indexT) == '#' ? 1 : -1; + indexT--; + } + + if (!(indexS >= 0 && indexT >= 0 && s.charAt(indexS) == t.charAt(indexT))) { + return indexS == -1 && indexT == -1; + } + indexS--; + indexT--; + } + } +} +``` + +```cpp +class Solution { +public: + bool backspaceCompare(string s, string t) { + int indexS = s.size() - 1, indexT = t.size() - 1; + int backspaceS = 0, backspaceT = 0; + + while (true) { + + while (indexS >= 0 && (backspaceS > 0 || s[indexS] == '#')) { + backspaceS += (s[indexS] == '#') ? 1 : -1; + indexS--; + } + + while (indexT >= 0 && (backspaceT > 0 || t[indexT] == '#')) { + backspaceT += (t[indexT] == '#') ? 1 : -1; + indexT--; + } + + if (!(indexS >= 0 && indexT >= 0 && s[indexS] == t[indexT])) { + return indexS == -1 && indexT == -1; + } + indexS--; + indexT--; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + backspaceCompare(s, t) { + let indexS = s.length - 1, indexT = t.length - 1; + let backspaceS = 0, backspaceT = 0; + + while (true) { + while (indexS >= 0 && (backspaceS > 0 || s[indexS] === '#')) { + backspaceS += s[indexS] === '#' ? 1 : -1; + indexS--; + } + + while (indexT >= 0 && (backspaceT > 0 || t[indexT] === '#')) { + backspaceT += t[indexT] === '#' ? 1 : -1; + indexT--; + } + + if (!(indexS >= 0 && indexT >= 0 && s.charAt(indexS) === t.charAt(indexT))) { + return indexS === -1 && indexT === -1; + } + indexS--; + indexT--; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(1)$ + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $t$. \ No newline at end of file diff --git a/articles/bag-of-tokens.md b/articles/bag-of-tokens.md new file mode 100644 index 000000000..e6db737b8 --- /dev/null +++ b/articles/bag-of-tokens.md @@ -0,0 +1,109 @@ +## 1. Greedy + Two Pointers + +::tabs-start + +```python +class Solution: + def bagOfTokensScore(self, tokens: List[int], power: int) -> int: + res = score = 0 + tokens.sort() + l, r = 0, len(tokens) - 1 + while l <= r: + if power >= tokens[l]: + power -= tokens[l] + l += 1 + score += 1 + res = max(res, score) + elif score > 0: + power += tokens[r] + r -= 1 + score -= 1 + else: + break + return res +``` + +```java +public class Solution { + public int bagOfTokensScore(int[] tokens, int power) { + Arrays.sort(tokens); + int res = 0, score = 0, l = 0, r = tokens.length - 1; + + while (l <= r) { + if (power >= tokens[l]) { + power -= tokens[l++]; + score++; + res = Math.max(res, score); + } else if (score > 0) { + power += tokens[r--]; + score--; + } else { + break; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int bagOfTokensScore(vector& tokens, int power) { + sort(tokens.begin(), tokens.end()); + int res = 0, score = 0, l = 0, r = tokens.size() - 1; + + while (l <= r) { + if (power >= tokens[l]) { + power -= tokens[l++]; + score++; + res = max(res, score); + } else if (score > 0) { + power += tokens[r--]; + score--; + } else { + break; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} tokens + * @param {number} power + * @return {number} + */ + bagOfTokensScore(tokens, power) { + tokens.sort((a, b) => a - b); + let res = 0, score = 0, l = 0, r = tokens.length - 1; + + while (l <= r) { + if (power >= tokens[l]) { + power -= tokens[l++]; + score++; + res = Math.max(res, score); + } else if (score > 0) { + power += tokens[r--]; + score--; + } else { + break; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file diff --git a/articles/balanced-binary-tree.md b/articles/balanced-binary-tree.md new file mode 100644 index 000000000..21715bc6f --- /dev/null +++ b/articles/balanced-binary-tree.md @@ -0,0 +1,996 @@ +## 1. Brute Force + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def isBalanced(self, root: Optional[TreeNode]) -> bool: + if not root: + return True + + left = self.height(root.left) + right = self.height(root.right) + if abs(left - right) > 1: + return False + return self.isBalanced(root.left) and self.isBalanced(root.right) + + def height(self, root: Optional[TreeNode]) -> int: + if not root: + return 0 + + return 1 + max(self.height(root.left), self.height(root.right)) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + public boolean isBalanced(TreeNode root) { + if (root == null) return true; + + int left = height(root.left); + int right = height(root.right); + if (Math.abs(left - right) > 1) return false; + return isBalanced(root.left) && isBalanced(root.right); + } + + public int height(TreeNode root) { + if (root == null) { + return 0; + } + + return 1 + Math.max(height(root.left), height(root.right)); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + bool isBalanced(TreeNode* root) { + if (!root) return true; + + int left = height(root->left); + int right = height(root->right); + if (abs(left - right) > 1) return false; + return isBalanced(root->left) && isBalanced(root->right); + } + + int height(TreeNode* root) { + if (root == nullptr) { + return 0; + } + + return 1 + max(height(root->left), height(root->right)); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isBalanced(root) { + if (root === null) return true; + + let left = this.height(root.left); + let right = this.height(root.right); + if (Math.abs(left - right) > 1) return false; + return this.isBalanced(root.left) && this.isBalanced(root.right); + } + + /** + * @param {TreeNode} root + * @return {number} + */ + height(root) { + if (root === null) { + return 0; + } + + return ( + 1 + Math.max(this.height(root.left), this.height(root.right)) + ); + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public bool IsBalanced(TreeNode root) { + if (root == null) return true; + + int left = Height(root.left); + int right = Height(root.right); + if (Math.Abs(left - right) > 1) return false; + return IsBalanced(root.left) && IsBalanced(root.right); + } + + public int Height(TreeNode root) { + if (root == null) { + return 0; + } + + return 1 + Math.Max(Height(root.left), Height(root.right)); + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func isBalanced(root *TreeNode) bool { + if root == nil { + return true + } + + left := height(root.Left) + right := height(root.Right) + if abs(left-right) > 1 { + return false + } + return isBalanced(root.Left) && isBalanced(root.Right) +} + +func height(root *TreeNode) int { + if root == nil { + return 0 + } + return 1 + max(height(root.Left), height(root.Right)) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func abs(x int) int { + if x < 0 { + return -x + } + return x +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun isBalanced(root: TreeNode?): Boolean { + if (root == null) { + return true + } + + val left = height(root.left) + val right = height(root.right) + if (Math.abs(left - right) > 1) { + return false + } + return isBalanced(root.left) && isBalanced(root.right) + } + + private fun height(root: TreeNode?): Int { + if (root == null) { + return 0 + } + return 1 + maxOf(height(root.left), height(root.right)) + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func isBalanced(_ root: TreeNode?) -> Bool { + guard let root = root else { return true } + + let left = height(root.left) + let right = height(root.right) + + if abs(left - right) > 1 { + return false + } + + return isBalanced(root.left) && isBalanced(root.right) + } + + private func height(_ root: TreeNode?) -> Int { + guard let root = root else { return 0 } + return 1 + max(height(root.left), height(root.right)) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def isBalanced(self, root: Optional[TreeNode]) -> bool: + def dfs(root): + if not root: + return [True, 0] + + left, right = dfs(root.left), dfs(root.right) + balanced = left[0] and right[0] and abs(left[1] - right[1]) <= 1 + return [balanced, 1 + max(left[1], right[1])] + + return dfs(root)[0] +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + + public boolean isBalanced(TreeNode root) { + return dfs(root)[0] == 1; + } + + private int[] dfs(TreeNode root) { + if (root == null) { + return new int[]{1, 0}; + } + + int[] left = dfs(root.left); + int[] right = dfs(root.right); + + boolean balanced = (left[0] == 1 && right[0] == 1) && + (Math.abs(left[1] - right[1]) <= 1); + int height = 1 + Math.max(left[1], right[1]); + + return new int[]{balanced ? 1 : 0, height}; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + bool isBalanced(TreeNode* root) { + return dfs(root)[0] == 1; + } + +private: + vector dfs(TreeNode* root) { + if (!root) { + return {1, 0}; + } + + vector left = dfs(root->left); + vector right = dfs(root->right); + + bool balanced = (left[0] == 1 && right[0] == 1) && + (abs(left[1] - right[1]) <= 1); + int height = 1 + max(left[1], right[1]); + + return {balanced ? 1 : 0, height}; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isBalanced(root) { + return this.dfs(root)[0] === 1; + } + + /** + * @param {TreeNode} root + * @return {number[]} + */ + dfs(root) { + if (!root) { + return [1, 0]; + } + + const left = this.dfs(root.left); + const right = this.dfs(root.right); + + const balanced = + left[0] === 1 && + right[0] === 1 && + Math.abs(left[1] - right[1]) <= 1; + const height = 1 + Math.max(left[1], right[1]); + + return [balanced ? 1 : 0, height]; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + + public bool IsBalanced(TreeNode root) { + return Dfs(root)[0] == 1; + } + + private int[] Dfs(TreeNode root) { + if (root == null) { + return new int[]{1, 0}; + } + + int[] left = Dfs(root.left); + int[] right = Dfs(root.right); + + bool balanced = (left[0] == 1 && right[0] == 1) && + (Math.Abs(left[1] - right[1]) <= 1); + int height = 1 + Math.Max(left[1], right[1]); + + return new int[]{balanced ? 1 : 0, height}; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func isBalanced(root *TreeNode) bool { + return dfs(root).balanced +} + +type Result struct { + balanced bool + height int +} + +func dfs(root *TreeNode) Result { + if root == nil { + return Result{true, 0} + } + + left := dfs(root.Left) + right := dfs(root.Right) + + balanced := left.balanced && right.balanced && abs(left.height - right.height) <= 1 + return Result{balanced, 1 + max(left.height, right.height)} +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func abs(x int) int { + if x < 0 { + return -x + } + return x +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun isBalanced(root: TreeNode?): Boolean { + return dfs(root).first + } + + private fun dfs(root: TreeNode?): Pair { + if (root == null) { + return Pair(true, 0) + } + + val left = dfs(root.left) + val right = dfs(root.right) + val balanced = left.first && right.first && Math.abs(left.second - right.second) <= 1 + return Pair(balanced, 1 + maxOf(left.second, right.second)) + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func isBalanced(_ root: TreeNode?) -> Bool { + return dfs(root).0 + } + + private func dfs(_ root: TreeNode?) -> (Bool, Int) { + guard let root = root else { return (true, 0) } + + let left = dfs(root.left) + let right = dfs(root.right) + + let balanced = left.0 && right.0 && abs(left.1 - right.1) <= 1 + return (balanced, 1 + max(left.1, right.1)) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(h)$ + * Best Case ([balanced tree](https://www.geeksforgeeks.org/balanced-binary-tree/)): $O(log(n))$ + * Worst Case ([degenerate tree](https://www.geeksforgeeks.org/introduction-to-degenerate-binary-tree/)): $O(n)$ + +> Where $n$ is the number of nodes in the tree and $h$ is the height of the tree. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def isBalanced(self, root): + stack = [] + node = root + last = None + depths = {} + + while stack or node: + if node: + stack.append(node) + node = node.left + else: + node = stack[-1] + if not node.right or last == node.right: + stack.pop() + left = depths.get(node.left, 0) + right = depths.get(node.right, 0) + + if abs(left - right) > 1: + return False + + depths[node] = 1 + max(left, right) + last = node + node = None + else: + node = node.right + + return True +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public boolean isBalanced(TreeNode root) { + Stack stack = new Stack<>(); + TreeNode node = root, last = null; + Map depths = new HashMap<>(); + + while (!stack.isEmpty() || node != null) { + if (node != null) { + stack.push(node); + node = node.left; + } else { + node = stack.peek(); + if (node.right == null || last == node.right) { + stack.pop(); + int left = depths.getOrDefault(node.left, 0); + int right = depths.getOrDefault(node.right, 0); + if (Math.abs(left - right) > 1) return false; + depths.put(node, 1 + Math.max(left, right)); + last = node; + node = null; + } else { + node = node.right; + } + } + } + return true; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + bool isBalanced(TreeNode* root) { + stack stack; + TreeNode* node = root; + TreeNode* last = nullptr; + unordered_map depths; + + while (!stack.empty() || node != nullptr) { + if (node != nullptr) { + stack.push(node); + node = node->left; + } else { + node = stack.top(); + if (node->right == nullptr || last == node->right) { + stack.pop(); + int left = depths[node->left]; + int right = depths[node->right]; + if (abs(left - right) > 1) return false; + depths[node] = 1 + max(left, right); + last = node; + node = nullptr; + } else { + node = node->right; + } + } + } + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isBalanced(root) { + let stack = []; + let node = root, last = null; + let depths = new Map(); + + while (stack.length > 0 || node !== null) { + if (node !== null) { + stack.push(node); + node = node.left; + } else { + node = stack[stack.length - 1]; + if (node.right === null || last === node.right) { + stack.pop(); + let left = depths.get(node.left) || 0; + let right = depths.get(node.right) || 0; + if (Math.abs(left - right) > 1) return false; + depths.set(node, 1 + Math.max(left, right)); + last = node; + node = null; + } else { + node = node.right; + } + } + } + return true; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public bool IsBalanced(TreeNode root) { + Stack stack = new Stack(); + TreeNode node = root, last = null; + Dictionary depths = new Dictionary(); + + while (stack.Count > 0 || node != null) { + if (node != null) { + stack.Push(node); + node = node.left; + } else { + node = stack.Peek(); + if (node.right == null || last == node.right) { + stack.Pop(); + + int left = (node.left != null && depths.ContainsKey(node.left)) + ? depths[node.left] : 0; + int right = (node.right != null && depths.ContainsKey(node.right)) + ? depths[node.right] : 0; + + if (Math.Abs(left - right) > 1) return false; + + depths[node] = 1 + Math.Max(left, right); + last = node; + node = null; + } else { + node = node.right; + } + } + } + return true; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func isBalanced(root *TreeNode) bool { + stack := []*TreeNode{} + node := root + last := (*TreeNode)(nil) + depths := make(map[*TreeNode]int) + + for len(stack) > 0 || node != nil { + if node != nil { + stack = append(stack, node) + node = node.Left + } else { + node = stack[len(stack)-1] + if node.Right == nil || last == node.Right { + stack = stack[:len(stack)-1] + left := depths[node.Left] + right := depths[node.Right] + + if abs(left-right) > 1 { + return false + } + + depths[node] = 1 + max(left, right) + last = node + node = nil + } else { + node = node.Right + } + } + } + return true +} + +func abs(x int) int { + if x < 0 { + return -x + } + return x +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun isBalanced(root: TreeNode?): Boolean { + val stack = mutableListOf() + var node = root + var last: TreeNode? = null + val depths = HashMap() + + while (stack.isNotEmpty() || node != null) { + if (node != null) { + stack.add(node) + node = node.left + } else { + node = stack.last() + if (node?.right == null || last == node.right) { + stack.removeAt(stack.size - 1) + val left = depths[node?.left] ?: 0 + val right = depths[node?.right] ?: 0 + + if (Math.abs(left - right) > 1) { + return false + } + + depths[node] = 1 + Math.max(left, right) + last = node + node = null + } else { + node = node.right + } + } + } + return true + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func isBalanced(_ root: TreeNode?) -> Bool { + var stack = [TreeNode]() + var node = root + var last: TreeNode? = nil + var depths = [ObjectIdentifier: Int]() + + while !stack.isEmpty || node != nil { + if let current = node { + stack.append(current) + node = current.left + } else { + guard let current = stack.last else { break } + if current.right == nil || last === current.right { + stack.removeLast() + + let leftDepth = current.left != nil ? depths[ObjectIdentifier(current.left!)] ?? 0 : 0 + let rightDepth = current.right != nil ? depths[ObjectIdentifier(current.right!)] ?? 0 : 0 + if abs(leftDepth - rightDepth) > 1 { + return false + } + + depths[ObjectIdentifier(current)] = 1 + max(leftDepth, rightDepth) + last = current + node = nil + } else { + node = current.right + } + } + } + + return true + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/baseball-game.md b/articles/baseball-game.md new file mode 100644 index 000000000..ac8e53feb --- /dev/null +++ b/articles/baseball-game.md @@ -0,0 +1,285 @@ +## 1. Stack - I + +::tabs-start + +```python +class Solution: + def calPoints(self, operations: List[str]) -> int: + stack = [] + for op in operations: + if op == "+": + stack.append(stack[-1] + stack[-2]) + elif op == "D": + stack.append(2 * stack[-1]) + elif op == "C": + stack.pop() + else: + stack.append(int(op)) + return sum(stack) +``` + +```java +public class Solution { + public int calPoints(String[] operations) { + Stack stack = new Stack<>(); + for (String op : operations) { + if (op.equals("+")) { + int top = stack.pop(); + int newTop = top + stack.peek(); + stack.push(top); + stack.push(newTop); + } else if (op.equals("D")) { + stack.push(2 * stack.peek()); + } else if (op.equals("C")) { + stack.pop(); + } else { + stack.push(Integer.parseInt(op)); + } + } + int sum = 0; + for (int score : stack) { + sum += score; + } + return sum; + } +} +``` + +```cpp +class Solution { +public: + int calPoints(vector& operations) { + vector stack; + for (const string& op : operations) { + if (op == "+") { + int top = stack.back(); stack.pop_back(); + int newTop = top + stack.back(); + stack.push_back(top); + stack.push_back(newTop); + } else if (op == "D") { + stack.push_back(2 * stack.back()); + } else if (op == "C") { + stack.pop_back(); + } else { + stack.push_back(stoi(op)); + } + } + return accumulate(stack.begin(), stack.end(), 0); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} operations + * @return {number} + */ + calPoints(operations) { + const stack = []; + for (const op of operations) { + if (op === "+") { + const top = stack.pop(); + const newTop = top + stack[stack.length - 1]; + stack.push(top); + stack.push(newTop); + } else if (op === "D") { + stack.push(2 * stack[stack.length - 1]); + } else if (op === "C") { + stack.pop(); + } else { + stack.push(parseInt(op)); + } + } + return stack.reduce((a, b) => a + b, 0); + } +} +``` + +```csharp +public class Solution { + public int CalPoints(string[] operations) { + Stack stack = new Stack(); + + foreach (var op in operations) { + if (op == "+") { + int top = stack.Pop(); + int newTop = top + stack.Peek(); + stack.Push(top); + stack.Push(newTop); + } else if (op == "D") { + stack.Push(2 * stack.Peek()); + } else if (op == "C") { + stack.Pop(); + } else { + stack.Push(int.Parse(op)); + } + } + + int total = 0; + foreach (var val in stack) { + total += val; + } + + return total; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Stack - II + +::tabs-start + +```python +class Solution: + def calPoints(self, operations: List[str]) -> int: + stack, res = [], 0 + for op in operations: + if op == "+": + res += stack[-1] + stack[-2] + stack.append(stack[-1] + stack[-2]) + elif op == "D": + res += (2 * stack[-1]) + stack.append(2 * stack[-1]) + elif op == "C": + res -= stack.pop() + else: + res += int(op) + stack.append(int(op)) + return res +``` + +```java +public class Solution { + public int calPoints(String[] ops) { + int res = 0; + Stack stack = new Stack<>(); + for (String op : ops) { + if (op.equals("+")) { + int top = stack.pop(); + int newTop = top + stack.peek(); + stack.push(top); + stack.push(newTop); + res += newTop; + } else if (op.equals("D")) { + stack.push(2 * stack.peek()); + res += stack.peek(); + } else if (op.equals("C")) { + res -= stack.pop(); + } else { + stack.push(Integer.parseInt(op)); + res += stack.peek(); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int calPoints(vector& ops) { + stack stack; + int res = 0; + for (const string& op : ops) { + if (op == "+") { + int top = stack.top(); stack.pop(); + int newTop = top + stack.top(); + stack.push(top); + stack.push(newTop); + res += newTop; + } else if (op == "D") { + stack.push(2 * stack.top()); + res += stack.top(); + } else if (op == "C") { + res -= stack.top(); + stack.pop(); + } else { + stack.push(stoi(op)); + res += stack.top(); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} operations + * @return {number} + */ + calPoints(operations) { + const stack = []; + let res = 0; + for (const op of operations) { + if (op === "+") { + const top = stack.pop(); + const newTop = top + stack[stack.length - 1]; + stack.push(top); + stack.push(newTop); + res += newTop; + } else if (op === "D") { + stack.push(2 * stack[stack.length - 1]); + res += stack[stack.length - 1]; + } else if (op === "C") { + res -= stack.pop(); + } else { + stack.push(parseInt(op)); + res += stack[stack.length - 1]; + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int CalPoints(string[] operations) { + Stack stack = new Stack(); + int res = 0; + + foreach (var op in operations) { + if (op == "+") { + int top = stack.Pop(); + int second = stack.Peek(); + int sum = top + second; + stack.Push(top); + stack.Push(sum); + res += sum; + } else if (op == "D") { + int doubleVal = 2 * stack.Peek(); + stack.Push(doubleVal); + res += doubleVal; + } else if (op == "C") { + res -= stack.Pop(); + } else { + int num = int.Parse(op); + stack.Push(num); + res += num; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/best-team-with-no-conflicts.md b/articles/best-team-with-no-conflicts.md new file mode 100644 index 000000000..a4e77a374 --- /dev/null +++ b/articles/best-team-with-no-conflicts.md @@ -0,0 +1,649 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def bestTeamScore(self, scores: List[int], ages: List[int]) -> int: + pairs = [[scores[i], ages[i]] for i in range(len(scores))] + pairs.sort() + dp = {} + + def dfs(i, j): + if i == len(pairs): + return 0 + if (i, j) in dp: + return dp[(i, j)] + + mScore, mAge = pairs[j] if j >= 0 else [0, 0] + score, age = pairs[i] + res = 0 + if not (score > mScore and age < mAge): + res = dfs(i + 1, i) + score # add score + dp[(i, j)] = max(res, dfs(i + 1, j)) # skip score + return dp[(i, j)] + + return dfs(0, -1) +``` + +```java +public class Solution { + private int[][] pairs; + private int[][] dp; + + public int bestTeamScore(int[] scores, int[] ages) { + int n = scores.length; + pairs = new int[n][2]; + for (int i = 0; i < n; i++) { + pairs[i][0] = scores[i]; + pairs[i][1] = ages[i]; + } + Arrays.sort(pairs, (a, b) -> a[0] == b[0] ? Integer.compare(a[1], b[1]) : Integer.compare(a[0], b[0])); + + dp = new int[n][n + 1]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + + return dfs(0, -1); + } + + private int dfs(int i, int j) { + if (i == pairs.length) { + return 0; + } + if (dp[i][j + 1] != -1) { + return dp[i][j + 1]; + } + + int mScore = j >= 0 ? pairs[j][0] : 0; + int mAge = j >= 0 ? pairs[j][1] : 0; + int score = pairs[i][0]; + int age = pairs[i][1]; + + int res = 0; + if (!(score > mScore && age < mAge)) { + res = dfs(i + 1, i) + score; + } + dp[i][j + 1] = Math.max(res, dfs(i + 1, j)); + return dp[i][j + 1]; + } +} +``` + +```cpp +class Solution { +private: + vector> pairs; + vector> dp; + +public: + int bestTeamScore(vector& scores, vector& ages) { + int n = scores.size(); + pairs.resize(n); + for (int i = 0; i < n; i++) { + pairs[i] = {scores[i], ages[i]}; + } + sort(pairs.begin(), pairs.end()); + + dp = vector>(n, vector(n + 1, -1)); + return dfs(0, -1); + } + +private: + int dfs(int i, int j) { + if (i == pairs.size()) { + return 0; + } + if (dp[i][j + 1] != -1) { + return dp[i][j + 1]; + } + + int mScore = j >= 0 ? pairs[j].first : 0; + int mAge = j >= 0 ? pairs[j].second : 0; + int score = pairs[i].first; + int age = pairs[i].second; + + int res = 0; + if (!(score > mScore && age < mAge)) { + res = dfs(i + 1, i) + score; + } + dp[i][j + 1] = max(res, dfs(i + 1, j)); + return dp[i][j + 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} scores + * @param {number[]} ages + * @return {number} + */ + bestTeamScore(scores, ages) { + const n = scores.length; + const pairs = []; + for (let i = 0; i < n; i++) { + pairs.push([scores[i], ages[i]]); + } + pairs.sort((a, b) => (a[0] === b[0] ? a[1] - b[1] : a[0] - b[0])); + + const dp = Array.from({ length: n }, () => new Array(n + 1).fill(-1)); + + const dfs = (i, j) => { + if (i === n) { + return 0; + } + if (dp[i][j + 1] !== -1) { + return dp[i][j + 1]; + } + + const [mScore, mAge] = j >= 0 ? pairs[j] : [0, 0]; + const [score, age] = pairs[i]; + + let res = 0; + if (!(score > mScore && age < mAge)) { + res = dfs(i + 1, i) + score; + } + dp[i][j + 1] = Math.max(res, dfs(i + 1, j)); + return dp[i][j + 1]; + }; + + return dfs(0, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def bestTeamScore(self, scores: List[int], ages: List[int]) -> int: + pairs = [[scores[i], ages[i]] for i in range(len(scores))] + pairs.sort() + dp = [pairs[i][0] for i in range(len(pairs))] + + for i in range(len(pairs)): + mScore, mAge = pairs[i] + for j in range(i): + score, age = pairs[j] + if mAge >= age: + dp[i] = max(dp[i], mScore + dp[j]) + + return max(dp) +``` + +```java +public class Solution { + public int bestTeamScore(int[] scores, int[] ages) { + int n = scores.length; + int[][] pairs = new int[n][2]; + for (int i = 0; i < n; i++) { + pairs[i][0] = scores[i]; + pairs[i][1] = ages[i]; + } + Arrays.sort(pairs, (a, b) -> a[0] == b[0] ? Integer.compare(a[1], b[1]) : Integer.compare(a[0], b[0])); + + int[] dp = new int[n]; + for (int i = 0; i < n; i++) { + dp[i] = pairs[i][0]; + } + + for (int i = 0; i < n; i++) { + int mScore = pairs[i][0], mAge = pairs[i][1]; + for (int j = 0; j < i; j++) { + int score = pairs[j][0], age = pairs[j][1]; + if (mAge >= age) { + dp[i] = Math.max(dp[i], mScore + dp[j]); + } + } + } + + int maxScore = 0; + for (int score : dp) { + maxScore = Math.max(maxScore, score); + } + return maxScore; + } +} +``` + +```cpp +class Solution { +public: + int bestTeamScore(vector& scores, vector& ages) { + int n = scores.size(); + vector> pairs(n); + for (int i = 0; i < n; i++) { + pairs[i] = {scores[i], ages[i]}; + } + sort(pairs.begin(), pairs.end()); + + vector dp(n); + for (int i = 0; i < n; i++) { + dp[i] = pairs[i].first; + } + + for (int i = 0; i < n; i++) { + int mScore = pairs[i].first, mAge = pairs[i].second; + for (int j = 0; j < i; j++) { + int score = pairs[j].first, age = pairs[j].second; + if (mAge >= age) { + dp[i] = max(dp[i], mScore + dp[j]); + } + } + } + + return *max_element(dp.begin(), dp.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} scores + * @param {number[]} ages + * @return {number} + */ + bestTeamScore(scores, ages) { + const n = scores.length; + const pairs = []; + for (let i = 0; i < n; i++) { + pairs.push([scores[i], ages[i]]); + } + pairs.sort((a, b) => (a[0] === b[0] ? a[1] - b[1] : a[0] - b[0])); + + const dp = new Array(n).fill(0); + for (let i = 0; i < n; i++) { + dp[i] = pairs[i][0]; + } + + for (let i = 0; i < n; i++) { + const [mScore, mAge] = pairs[i]; + for (let j = 0; j < i; j++) { + const [score, age] = pairs[j]; + if (mAge >= age) { + dp[i] = Math.max(dp[i], mScore + dp[j]); + } + } + } + + return Math.max(...dp); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Segment Tree) + +::tabs-start + +```python +class SegmentTree: + def __init__(self, N): + self.n = N + while (self.n & (self.n - 1)) != 0: + self.n += 1 + self.build() + + def build(self): + self.tree = [0] * (2 * self.n) + + def update(self, i, val): + pos = self.n + i + self.tree[pos] = max(self.tree[pos], val) + pos >>= 1 + while pos >= 1: + self.tree[pos] = max(self.tree[pos << 1], self.tree[pos << 1 | 1]) + pos >>= 1 + + def query(self, l, r): + res = 0 + l += self.n + r += self.n + 1 + while l < r: + if l & 1: + res = max(res, self.tree[l]) + l += 1 + if r & 1: + r -= 1 + res = max(res, self.tree[r]) + l >>= 1 + r >>= 1 + return res + +class Solution: + def bestTeamScore(self, scores: list[int], ages: list[int]) -> int: + pairs = [[scores[i], ages[i]] for i in range(len(scores))] + pairs.sort() + + dp = [pairs[i][0] for i in range(len(pairs))] + unique_ages = sorted({ age for _, age in pairs }) + ageId = { val: idx for idx, val in enumerate(unique_ages) } + + segtree = SegmentTree(len(pairs)) + + res = 0 + for i in range(len(pairs)): + mScore, mAge = pairs[i] + idx = ageId[mAge] + j = segtree.query(0, idx) + dp[i] = j + mScore + segtree.update(idx, dp[i]) + res = max(res, dp[i]) + + return res +``` + +```java +class SegmentTree { + private int n; + private int[] tree; + + public SegmentTree(int N) { + n = N; + while ((n & (n - 1)) != 0) { + n++; + } + build(); + } + + private void build() { + tree = new int[2 * n]; + } + + public void update(int i, int val) { + int pos = n + i; + tree[pos] = Math.max(tree[pos], val); + pos >>= 1; + while (pos >= 1) { + tree[pos] = Math.max(tree[pos << 1], tree[pos << 1 | 1]); + pos >>= 1; + } + } + + public int query(int l, int r) { + int res = 0; + l += n; + r += n + 1; + while (l < r) { + if ((l & 1) == 1) { + res = Math.max(res, tree[l]); + l++; + } + if ((r & 1) == 1) { + r--; + res = Math.max(res, tree[r]); + } + l >>= 1; + r >>= 1; + } + return res; + } +} + +public class Solution { + public int bestTeamScore(int[] scores, int[] ages) { + int n = scores.length; + int[][] pairs = new int[n][2]; + for (int i = 0; i < n; i++) { + pairs[i][0] = scores[i]; + pairs[i][1] = ages[i]; + } + Arrays.sort(pairs, (a, b) -> a[0] == b[0] ? Integer.compare(a[1], b[1]) : Integer.compare(a[0], b[0])); + + int[] dp = new int[n]; + for (int i = 0; i < n; i++) { + dp[i] = pairs[i][0]; + } + + Set uniqueAgesSet = new TreeSet<>(); + for (int[] pair : pairs) { + uniqueAgesSet.add(pair[1]); + } + List uniqueAges = new ArrayList<>(uniqueAgesSet); + Map ageId = new HashMap<>(); + for (int i = 0; i < uniqueAges.size(); i++) { + ageId.put(uniqueAges.get(i), i); + } + + SegmentTree segtree = new SegmentTree(uniqueAges.size()); + + int res = 0; + for (int i = 0; i < n; i++) { + int mScore = pairs[i][0]; + int mAge = pairs[i][1]; + int idx = ageId.get(mAge); + int j = segtree.query(0, idx); + dp[i] = j + mScore; + segtree.update(idx, dp[i]); + res = Math.max(res, dp[i]); + } + + return res; + } +} +``` + +```cpp +class SegmentTree { +private: + int n; + vector tree; + +public: + SegmentTree(int N) { + n = N; + while ((n & (n - 1)) != 0) { + n++; + } + build(); + } + + void build() { + tree.resize(2 * n, 0); + } + + void update(int i, int val) { + int pos = n + i; + tree[pos] = max(tree[pos], val); + pos >>= 1; + while (pos >= 1) { + tree[pos] = max(tree[pos << 1], tree[pos << 1 | 1]); + pos >>= 1; + } + } + + int query(int l, int r) { + int res = 0; + l += n; + r += n + 1; + while (l < r) { + if (l & 1) { + res = max(res, tree[l]); + l++; + } + if (r & 1) { + r--; + res = max(res, tree[r]); + } + l >>= 1; + r >>= 1; + } + return res; + } +}; + +class Solution { +public: + int bestTeamScore(vector& scores, vector& ages) { + int n = scores.size(); + vector> pairs(n); + for (int i = 0; i < n; i++) { + pairs[i] = {scores[i], ages[i]}; + } + sort(pairs.begin(), pairs.end()); + + vector dp(n); + for (int i = 0; i < n; i++) { + dp[i] = pairs[i].first; + } + + set uniqueAgesSet; + for (auto& pair : pairs) { + uniqueAgesSet.insert(pair.second); + } + vector uniqueAges(uniqueAgesSet.begin(), uniqueAgesSet.end()); + map ageId; + for (int i = 0; i < uniqueAges.size(); i++) { + ageId[uniqueAges[i]] = i; + } + + SegmentTree segtree(uniqueAges.size()); + + int res = 0; + for (int i = 0; i < n; i++) { + int mScore = pairs[i].first; + int mAge = pairs[i].second; + int idx = ageId[mAge]; + int j = segtree.query(0, idx); + dp[i] = j + mScore; + segtree.update(idx, dp[i]); + res = max(res, dp[i]); + } + + return res; + } +}; +``` + +```javascript +class SegmentTree { + /** + * @constructor + * @param {number} N + */ + constructor(N) { + this.n = N; + while ((this.n & (this.n - 1)) !== 0) { + this.n++; + } + this.build(); + } + + /** + * @return {void} + */ + build() { + this.tree = new Array(2 * this.n).fill(0); + } + + /** + * @param {number} i + * @param {number} val + * @return {void} + */ + update(i, val) { + let pos = this.n + i; + this.tree[pos] = Math.max(this.tree[pos], val); + pos >>= 1; + while (pos >= 1) { + this.tree[pos] = Math.max(this.tree[pos << 1], this.tree[pos << 1 | 1]); + pos >>= 1; + } + } + + /** + * @param {number} l + * @param {number} r + * @return {number} + */ + query(l, r) { + let res = 0; + l += this.n; + r += this.n + 1; + while (l < r) { + if (l & 1) { + res = Math.max(res, this.tree[l]); + l++; + } + if (r & 1) { + r--; + res = Math.max(res, this.tree[r]); + } + l >>= 1; + r >>= 1; + } + return res; + } +} + +class Solution { + /** + * @param {number[]} scores + * @param {number[]} ages + * @return {number} + */ + bestTeamScore(scores, ages) { + const n = scores.length; + const pairs = []; + + for (let i = 0; i < n; i++) { + pairs.push([scores[i], ages[i]]); + } + pairs.sort((a, b) => (a[0] === b[0] ? a[1] - b[1] : a[0] - b[0])); + + const dp = new Array(n).fill(0); + for (let i = 0; i < n; i++) { + dp[i] = pairs[i][0]; + } + + const uniqueAges = [...new Set(pairs.map((p) => p[1]))].sort((a, b) => a - b); + const ageId = new Map(); + uniqueAges.forEach((val, idx) => ageId.set(val, idx)); + + const segtree = new SegmentTree(uniqueAges.length); + + let res = 0; + + for (let i = 0; i < n; i++) { + const [mScore, mAge] = pairs[i]; + const idx = ageId.get(mAge); + const j = segtree.query(0, idx); + dp[i] = j + mScore; + segtree.update(idx, dp[i]); + res = Math.max(res, dp[i]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/best-time-to-buy-and-sell-stock-ii.md b/articles/best-time-to-buy-and-sell-stock-ii.md new file mode 100644 index 000000000..6562e3544 --- /dev/null +++ b/articles/best-time-to-buy-and-sell-stock-ii.md @@ -0,0 +1,567 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def maxProfit(self, prices: List[int]) -> int: + def rec(i, bought): + if i == len(prices): + return 0 + res = rec(i + 1, bought) + if bought: + res = max(res, prices[i] + rec(i + 1, False)) + else: + res = max(res, -prices[i] + rec(i + 1, True)) + return res + return rec(0, False) +``` + +```java +public class Solution { + public int maxProfit(int[] prices) { + return rec(prices, 0, false); + } + + private int rec(int[] prices, int i, boolean bought) { + if (i == prices.length) { + return 0; + } + int res = rec(prices, i + 1, bought); + if (bought) { + res = Math.max(res, prices[i] + rec(prices, i + 1, false)); + } else { + res = Math.max(res, -prices[i] + rec(prices, i + 1, true)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxProfit(vector& prices) { + return rec(prices, 0, false); + } + +private: + int rec(vector& prices, int i, bool bought) { + if (i == prices.size()) { + return 0; + } + int res = rec(prices, i + 1, bought); + if (bought) { + res = max(res, prices[i] + rec(prices, i + 1, false)); + } else { + res = max(res, -prices[i] + rec(prices, i + 1, true)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} prices + * @return {number} + */ + maxProfit(prices) { + const rec = (i, bought) => { + if (i === prices.length) { + return 0; + } + let res = rec(i + 1, bought); + if (bought) { + res = Math.max(res, prices[i] + rec(i + 1, false)); + } else { + res = Math.max(res, -prices[i] + rec(i + 1, true)); + } + return res; + }; + return rec(0, false); + } +} +``` + +```csharp +public class Solution { + public int MaxProfit(int[] prices) { + return Rec(prices, 0, false); + } + + private int Rec(int[] prices, int i, bool bought) { + if (i == prices.Length) { + return 0; + } + + int res = Rec(prices, i + 1, bought); + + if (bought) { + res = Math.Max(res, prices[i] + Rec(prices, i + 1, false)); + } else { + res = Math.Max(res, -prices[i] + Rec(prices, i + 1, true)); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def maxProfit(self, prices: List[int]) -> int: + dp = {} + + def rec(i, bought): + if i == len(prices): + return 0 + if (i, bought) in dp: + return dp[(i, bought)] + res = rec(i + 1, bought) + if bought: + res = max(res, prices[i] + rec(i + 1, False)) + else: + res = max(res, -prices[i] + rec(i + 1, True)) + dp[(i, bought)] = res + return res + + return rec(0, False) +``` + +```java +public class Solution { + public int maxProfit(int[] prices) { + int n = prices.length; + int[][] dp = new int[n][2]; + for (int i = 0; i < n; i++) { + dp[i][0] = -1; + dp[i][1] = -1; + } + return rec(prices, 0, 0, dp); + } + + private int rec(int[] prices, int i, int bought, int[][] dp) { + if (i == prices.length) { + return 0; + } + if (dp[i][bought] != -1) { + return dp[i][bought]; + } + int res = rec(prices, i + 1, bought, dp); + if (bought == 1) { + res = Math.max(res, prices[i] + rec(prices, i + 1, 0, dp)); + } else { + res = Math.max(res, -prices[i] + rec(prices, i + 1, 1, dp)); + } + dp[i][bought] = res; + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxProfit(vector& prices) { + int n = prices.size(); + vector> dp(n, vector(2, -1)); + return rec(prices, 0, 0, dp); + } + +private: + int rec(vector& prices, int i, int bought, vector>& dp) { + if (i == prices.size()) { + return 0; + } + if (dp[i][bought] != -1) { + return dp[i][bought]; + } + int res = rec(prices, i + 1, bought, dp); + if (bought == 1) { + res = max(res, prices[i] + rec(prices, i + 1, 0, dp)); + } else { + res = max(res, -prices[i] + rec(prices, i + 1, 1, dp)); + } + dp[i][bought] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} prices + * @return {number} + */ + maxProfit(prices) { + const n = prices.length; + const dp = Array.from({ length: n }, () => Array(2).fill(-1)); + + const rec = (i, bought) => { + if (i === n) { + return 0; + } + if (dp[i][bought] !== -1) { + return dp[i][bought]; + } + let res = rec(i + 1, bought); + if (bought) { + res = Math.max(res, prices[i] + rec(i + 1, 0)); + } else { + res = Math.max(res, -prices[i] + rec(i + 1, 1)); + } + dp[i][bought] = res; + return res; + }; + + return rec(0, 0); + } +} +``` + +```csharp +public class Solution { + public int MaxProfit(int[] prices) { + int n = prices.Length; + int[,] dp = new int[n, 2]; + + for (int i = 0; i < n; i++) { + dp[i, 0] = -1; + dp[i, 1] = -1; + } + + return Rec(prices, 0, 0, dp); + } + + private int Rec(int[] prices, int i, int bought, int[,] dp) { + if (i == prices.Length) { + return 0; + } + + if (dp[i, bought] != -1) { + return dp[i, bought]; + } + + int res = Rec(prices, i + 1, bought, dp); + + if (bought == 1) { + res = Math.Max(res, prices[i] + Rec(prices, i + 1, 0, dp)); + } else { + res = Math.Max(res, -prices[i] + Rec(prices, i + 1, 1, dp)); + } + + dp[i, bought] = res; + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def maxProfit(self, prices: List[int]) -> int: + n = len(prices) + dp = [[0] * 2 for _ in range(n + 1)] + + for i in range(n - 1, -1, -1): + dp[i][0] = max(dp[i + 1][0], -prices[i] + dp[i + 1][1]) + dp[i][1] = max(dp[i + 1][1], prices[i] + dp[i + 1][0]) + + return dp[0][0] +``` + +```java +public class Solution { + public int maxProfit(int[] prices) { + int n = prices.length; + int[][] dp = new int[n + 1][2]; + + for (int i = n - 1; i >= 0; i--) { + dp[i][0] = Math.max(dp[i + 1][0], -prices[i] + dp[i + 1][1]); + dp[i][1] = Math.max(dp[i + 1][1], prices[i] + dp[i + 1][0]); + } + + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + int maxProfit(vector& prices) { + int n = prices.size(); + vector> dp(n + 1, vector(2, 0)); + + for (int i = n - 1; i >= 0; i--) { + dp[i][0] = max(dp[i + 1][0], -prices[i] + dp[i + 1][1]); + dp[i][1] = max(dp[i + 1][1], prices[i] + dp[i + 1][0]); + } + + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} prices + * @return {number} + */ + maxProfit(prices) { + const n = prices.length; + const dp = Array.from({ length: n + 1 }, () => Array(2).fill(0)); + + for (let i = n - 1; i >= 0; i--) { + dp[i][0] = Math.max(dp[i + 1][0], -prices[i] + dp[i + 1][1]); + dp[i][1] = Math.max(dp[i + 1][1], prices[i] + dp[i + 1][0]); + } + + return dp[0][0]; + } +} +``` + +```csharp +public class Solution { + public int MaxProfit(int[] prices) { + int n = prices.Length; + int[,] dp = new int[n + 1, 2]; + + for (int i = n - 1; i >= 0; i--) { + dp[i, 0] = Math.Max(dp[i + 1, 0], -prices[i] + dp[i + 1, 1]); + dp[i, 1] = Math.Max(dp[i + 1, 1], prices[i] + dp[i + 1, 0]); + } + + return dp[0, 0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def maxProfit(self, prices: List[int]) -> int: + n = len(prices) + next_buy = next_sell = 0 + cur_buy = cur_sell = 0 + + for i in range(n - 1, -1, -1): + cur_buy = max(next_buy, -prices[i] + next_sell) + cur_sell = max(next_sell, prices[i] + next_buy) + next_buy = cur_buy + next_sell = cur_sell + + return cur_buy +``` + +```java +public class Solution { + public int maxProfit(int[] prices) { + int nextBuy = 0, nextSell = 0; + int curBuy = 0, curSell = 0; + + for (int i = prices.length - 1; i >= 0; i--) { + curBuy = Math.max(nextBuy, -prices[i] + nextSell); + curSell = Math.max(nextSell, prices[i] + nextBuy); + nextBuy = curBuy; + nextSell = curSell; + } + + return curBuy; + } +} +``` + +```cpp +class Solution { +public: + int maxProfit(vector& prices) { + int nextBuy = 0, nextSell = 0; + int curBuy = 0, curSell = 0; + + for (int i = prices.size() - 1; i >= 0; i--) { + curBuy = max(nextBuy, -prices[i] + nextSell); + curSell = max(nextSell, prices[i] + nextBuy); + nextBuy = curBuy; + nextSell = curSell; + } + + return curBuy; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} prices + * @return {number} + */ + maxProfit(prices) { + let nextBuy = 0, nextSell = 0; + let curBuy = 0, curSell = 0; + + for (let i = prices.length - 1; i >= 0; i--) { + curBuy = Math.max(nextBuy, -prices[i] + nextSell); + curSell = Math.max(nextSell, prices[i] + nextBuy); + nextBuy = curBuy; + nextSell = curSell; + } + + return curBuy; + } +} +``` + +```csharp +public class Solution { + public int MaxProfit(int[] prices) { + int nextBuy = 0, nextSell = 0; + int curBuy = 0, curSell = 0; + + for (int i = prices.Length - 1; i >= 0; i--) { + curBuy = Math.Max(nextBuy, -prices[i] + nextSell); + curSell = Math.Max(nextSell, prices[i] + nextBuy); + nextBuy = curBuy; + nextSell = curSell; + } + + return curBuy; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Greedy + +::tabs-start + +```python +class Solution: + def maxProfit(self, prices: List[int]) -> int: + profit = 0 + + for i in range(1, len(prices)): + if prices[i] > prices[i - 1]: + profit += (prices[i] - prices[i - 1]) + + return profit +``` + +```java +public class Solution { + public int maxProfit(int[] prices) { + int profit = 0; + for (int i = 1; i < prices.length; i++) { + if (prices[i] > prices[i - 1]) { + profit += (prices[i] - prices[i - 1]); + } + } + return profit; + } +} +``` + +```cpp +class Solution { +public: + int maxProfit(vector& prices) { + int profit = 0; + for (int i = 1; i < prices.size(); i++) { + if (prices[i] > prices[i - 1]) { + profit += (prices[i] - prices[i - 1]); + } + } + return profit; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} prices + * @return {number} + */ + maxProfit(prices) { + let profit = 0; + for (let i = 1; i < prices.length; i++) { + if (prices[i] > prices[i - 1]) { + profit += (prices[i] - prices[i - 1]); + } + } + return profit; + } +} +``` + +```csharp +public class Solution { + public int MaxProfit(int[] prices) { + int profit = 0; + for (int i = 1; i < prices.Length; i++) { + if (prices[i] > prices[i - 1]) { + profit += prices[i] - prices[i - 1]; + } + } + return profit; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/binary-search-tree-iterator.md b/articles/binary-search-tree-iterator.md new file mode 100644 index 000000000..873698c70 --- /dev/null +++ b/articles/binary-search-tree-iterator.md @@ -0,0 +1,699 @@ +## 1. Flattening the BST (DFS) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class BSTIterator: + + def __init__(self, root: Optional[TreeNode]): + self.arr = [] + self.itr = 0 + + def dfs(node): + if not node: + return + dfs(node.left) + self.arr.append(node.val) + dfs(node.right) + + dfs(root) + + def next(self) -> int: + val = self.arr[self.itr] + self.itr += 1 + return val + + def hasNext(self) -> bool: + return self.itr < len(self.arr) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class BSTIterator { + private List arr; + private int itr; + + public BSTIterator(TreeNode root) { + arr = new ArrayList<>(); + itr = 0; + dfs(root); + } + + private void dfs(TreeNode node) { + if (node == null) { + return; + } + dfs(node.left); + arr.add(node.val); + dfs(node.right); + } + + public int next() { + return arr.get(itr++); + } + + public boolean hasNext() { + return itr < arr.size(); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class BSTIterator { +private: + vector arr; + int itr; + + void dfs(TreeNode* node) { + if (!node) { + return; + } + dfs(node->left); + arr.push_back(node->val); + dfs(node->right); + } + +public: + BSTIterator(TreeNode* root) { + itr = 0; + dfs(root); + } + + int next() { + return arr[itr++]; + } + + bool hasNext() { + return itr < arr.size(); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class BSTIterator { + /** + * @constructor + * @param {TreeNode} root + */ + constructor(root) { + this.arr = []; + this.itr = 0; + + const dfs = (node) => { + if (!node) { + return; + } + dfs(node.left); + this.arr.push(node.val); + dfs(node.right); + }; + + dfs(root); + } + + /** + * @return {number} + */ + next() { + return this.arr[this.itr++]; + } + + /** + * @return {boolean} + */ + hasNext() { + return this.itr < this.arr.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(n)$ time for each $next()$ and $hasNext()$ function calls. +* Space complexity: $O(n)$ + +--- + +## 2. Flatten the BST (Iterative DFS) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class BSTIterator: + + def __init__(self, root: Optional[TreeNode]): + self.arr = [] + self.itr = 0 + + stack = [] + while root or stack: + while root: + stack.append(root) + root = root.left + root = stack.pop() + self.arr.append(root.val) + root = root.right + + def next(self) -> int: + val = self.arr[self.itr] + self.itr += 1 + return val + + def hasNext(self) -> bool: + return self.itr < len(self.arr) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class BSTIterator { + private List arr; + private int itr; + + public BSTIterator(TreeNode root) { + arr = new ArrayList<>(); + itr = 0; + Stack stack = new Stack<>(); + while (root != null || !stack.isEmpty()) { + while (root != null) { + stack.push(root); + root = root.left; + } + root = stack.pop(); + arr.add(root.val); + root = root.right; + } + } + + public int next() { + return arr.get(itr++); + } + + public boolean hasNext() { + return itr < arr.size(); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class BSTIterator { +private: + vector arr; + int itr; + +public: + BSTIterator(TreeNode* root) { + itr = 0; + stack stack; + while (root || !stack.empty()) { + while (root) { + stack.push(root); + root = root->left; + } + root = stack.top(); + stack.pop(); + arr.push_back(root->val); + root = root->right; + } + } + + int next() { + return arr[itr++]; + } + + bool hasNext() { + return itr < arr.size(); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class BSTIterator { + /** + * @constructor + * @param {TreeNode} root + */ + constructor(root) { + this.arr = []; + this.itr = 0; + + let stack = []; + while (root || stack.length) { + while (root) { + stack.push(root); + root = root.left; + } + root = stack.pop(); + this.arr.push(root.val); + root = root.right; + } + } + + /** + * @return {number} + */ + next() { + return this.arr[this.itr++]; + } + + /** + * @return {boolean} + */ + hasNext() { + return this.itr < this.arr.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(n)$ time for each $next()$ and $hasNext()$ function calls. +* Space complexity: $O(n)$ + +--- + +## 3. Iterative DFS - I + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class BSTIterator: + + def __init__(self, root: Optional[TreeNode]): + self.stack = [] + while root: + self.stack.append(root) + root = root.left + + def next(self) -> int: + res = self.stack.pop() + cur = res.right + while cur: + self.stack.append(cur) + cur = cur.left + return res.val + + def hasNext(self) -> bool: + return bool(self.stack) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class BSTIterator { + private Stack stack; + + public BSTIterator(TreeNode root) { + stack = new Stack<>(); + while (root != null) { + stack.push(root); + root = root.left; + } + } + + public int next() { + TreeNode res = stack.pop(); + TreeNode cur = res.right; + while (cur != null) { + stack.push(cur); + cur = cur.left; + } + return res.val; + } + + public boolean hasNext() { + return !stack.isEmpty(); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class BSTIterator { +private: + stack stack; + +public: + BSTIterator(TreeNode* root) { + while (root) { + stack.push(root); + root = root->left; + } + } + + int next() { + TreeNode* res = stack.top(); + stack.pop(); + TreeNode* cur = res->right; + while (cur) { + stack.push(cur); + cur = cur->left; + } + return res->val; + } + + bool hasNext() { + return !stack.empty(); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class BSTIterator { + /** + * @constructor + * @param {TreeNode} root + */ + constructor(root) { + this.stack = []; + while (root) { + this.stack.push(root); + root = root.left; + } + } + + /** + * @return {number} + */ + next() { + let res = this.stack.pop(); + let cur = res.right; + while (cur) { + this.stack.push(cur); + cur = cur.left; + } + return res.val; + } + + /** + * @return {boolean} + */ + hasNext() { + return this.stack.length > 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ in average for each function call. +* Space complexity: $O(h)$ + +> Where $n$ is the number of nodes and $h$ is the height of the given tree. + +--- + +## 4. Iterative DFS - II + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class BSTIterator: + + def __init__(self, root: Optional[TreeNode]): + self.cur = root + self.stack = [] + + def next(self) -> int: + while self.cur: + self.stack.append(self.cur) + self.cur = self.cur.left + + node = self.stack.pop() + self.cur = node.right + return node.val + + def hasNext(self) -> bool: + return bool(self.cur) or bool(self.stack) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class BSTIterator { + private TreeNode cur; + private Stack stack; + + public BSTIterator(TreeNode root) { + cur = root; + stack = new Stack<>(); + } + + public int next() { + while (cur != null) { + stack.push(cur); + cur = cur.left; + } + + TreeNode node = stack.pop(); + cur = node.right; + return node.val; + } + + public boolean hasNext() { + return cur != null || !stack.isEmpty(); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class BSTIterator { +private: + TreeNode* cur; + stack stack; + +public: + BSTIterator(TreeNode* root) { + cur = root; + } + + int next() { + while (cur) { + stack.push(cur); + cur = cur->left; + } + + TreeNode* node = stack.top(); + stack.pop(); + cur = node->right; + return node->val; + } + + bool hasNext() { + return cur || !stack.empty(); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class BSTIterator { + /** + * @constructor + * @param {TreeNode} root + */ + constructor(root) { + this.cur = root; + this.stack = []; + } + + /** + * @return {number} + */ + next() { + while (this.cur) { + this.stack.push(this.cur); + this.cur = this.cur.left; + } + + let node = this.stack.pop(); + this.cur = node.right; + return node.val; + } + + /** + * @return {boolean} + */ + hasNext() { + return this.cur !== null || this.stack.length > 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ in average for each function call. +* Space complexity: $O(h)$ + +> Where $n$ is the number of nodes and $h$ is the height of the given tree. \ No newline at end of file diff --git a/articles/binary-search.md b/articles/binary-search.md new file mode 100644 index 000000000..59a970a26 --- /dev/null +++ b/articles/binary-search.md @@ -0,0 +1,762 @@ +## 1. Recursive Binary Search + +::tabs-start + +```python +class Solution: + def binary_search(self, l: int, r: int, nums: List[int], target: int) -> int: + if l > r: + return -1 + m = l + (r - l) // 2 + + if nums[m] == target: + return m + if nums[m] < target: + return self.binary_search(m + 1, r, nums, target) + return self.binary_search(l, m - 1, nums, target) + + def search(self, nums: List[int], target: int) -> int: + return self.binary_search(0, len(nums) - 1, nums, target) +``` + +```java +public class Solution { + public int binary_search(int l, int r, int[] nums, int target) { + if (l > r) return -1; + int m = l + (r - l) / 2; + + if (nums[m] == target) return m; + return (nums[m] < target) ? + binary_search(m + 1, r, nums, target) : + binary_search(l, m - 1, nums, target); + } + + public int search(int[] nums, int target) { + return binary_search(0, nums.length - 1, nums, target); + } +} +``` + +```cpp +class Solution { +public: + int binary_search(int l, int r, vector& nums, int target){ + if (l > r) return -1; + int m = l + (r - l) / 2; + + if (nums[m] == target) return m; + return ((nums[m] < target) ? + binary_search(m + 1, r, nums, target) : + binary_search(l, m - 1, nums, target)); + } + + int search(vector& nums, int target) { + return binary_search(0, nums.size() - 1, nums, target); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + binary_search(l, r, nums, target) { + if (l > r) return -1; + let m = l + Math.floor((r - l) / 2); + + if (nums[m] === target) return m; + return (nums[m] < target) ? + this.binary_search(m + 1, r, nums, target) : + this.binary_search(l, m - 1, nums, target); + } + + search(nums, target) { + return this.binary_search(0, nums.length - 1, nums, target); + } +} +``` + +```csharp +public class Solution { + public int BinarySearch(int l, int r, int[] nums, int target) { + if (l > r) return -1; + int m = l + (r - l) / 2; + + if (nums[m] == target) return m; + return (nums[m] < target) ? + BinarySearch(m + 1, r, nums, target) : + BinarySearch(l, m - 1, nums, target); + } + + public int Search(int[] nums, int target) { + return BinarySearch(0, nums.Length - 1, nums, target); + } +} +``` + +```go +func binarySearch(l, r int, nums []int, target int) int { + if l > r { + return -1 + } + m := l + (r-l)/2 + + if nums[m] == target { + return m + } + if nums[m] < target { + return binarySearch(m+1, r, nums, target) + } + return binarySearch(l, m-1, nums, target) +} + +func search(nums []int, target int) int { + return binarySearch(0, len(nums)-1, nums, target) +} +``` + +```kotlin +class Solution { + private fun binarySearch(l: Int, r: Int, nums: IntArray, target: Int): Int { + if (l > r) { + return -1 + } + val m = l + (r - l) / 2 + + return when { + nums[m] == target -> m + nums[m] < target -> binarySearch(m + 1, r, nums, target) + else -> binarySearch(l, m - 1, nums, target) + } + } + + fun search(nums: IntArray, target: Int): Int { + return binarySearch(0, nums.size - 1, nums, target) + } +} +``` + +```swift +class Solution { + func binarySearch(_ l: Int, _ r: Int, _ nums: [Int], _ target: Int) -> Int { + if l > r { + return -1 + } + let m = l + (r - l) / 2 + + if nums[m] == target { + return m + } + if nums[m] < target { + return binarySearch(m + 1, r, nums, target) + } + return binarySearch(l, m - 1, nums, target) + } + + func search(_ nums: [Int], _ target: Int) -> Int { + return binarySearch(0, nums.count - 1, nums, target) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ + +--- + +## 2. Iterative Binary Search + +::tabs-start + +```python +class Solution: + def search(self, nums: List[int], target: int) -> int: + l, r = 0, len(nums) - 1 + + while l <= r: + # (l + r) // 2 can lead to overflow + m = l + ((r - l) // 2) + + if nums[m] > target: + r = m - 1 + elif nums[m] < target: + l = m + 1 + else: + return m + return -1 +``` + +```java +public class Solution { + public int search(int[] nums, int target) { + int l = 0, r = nums.length - 1; + + while (l <= r) { + int m = l + ((r - l) / 2); + if (nums[m] > target) { + r = m - 1; + } else if (nums[m] < target) { + l = m + 1; + } else { + return m; + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int search(vector& nums, int target) { + int l = 0, r = nums.size() - 1; + + while (l <= r) { + int m = l + ((r - l) / 2); + if (nums[m] > target) { + r = m - 1; + } else if (nums[m] < target) { + l = m + 1; + } else { + return m; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + search(nums, target) { + let l = 0; + let r = nums.length - 1; + + while (l <= r) { + const m = l + Math.floor((r - l) / 2); + if (nums[m] > target) { + r = m - 1; + } else if (nums[m] < target) { + l = m + 1; + } else { + return m; + } + } + return -1; + } +} +``` + +```csharp +public class Solution { + public int Search(int[] nums, int target) { + int l = 0, r = nums.Length - 1; + + while (l <= r) { + int m = l + ((r - l) / 2); + if (nums[m] > target) { + r = m - 1; + } else if (nums[m] < target) { + l = m + 1; + } else { + return m; + } + } + return -1; + } +} +``` + +```go +func search(nums []int, target int) int { + l, r := 0, len(nums)-1 + + for l <= r { + m := l + (r-l)/2 + + if nums[m] > target { + r = m - 1 + } else if nums[m] < target { + l = m + 1 + } else { + return m + } + } + return -1 +} +``` + +```kotlin +class Solution { + fun search(nums: IntArray, target: Int): Int { + var l = 0 + var r = nums.size - 1 + + while (l <= r) { + val m = l + (r - l) / 2 + + when { + nums[m] > target -> r = m - 1 + nums[m] < target -> l = m + 1 + else -> return m + } + } + return -1 + } +} +``` + +```swift +class Solution { + func search(_ nums: [Int], _ target: Int) -> Int { + var l = 0, r = nums.count - 1 + + while l <= r { + // (l + r) // 2 can lead to overflow + let m = l + (r - l) / 2 + + if nums[m] > target { + r = m - 1 + } else if nums[m] < target { + l = m + 1 + } else { + return m + } + } + return -1 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Upper Bound + +::tabs-start + +```python +class Solution: + def search(self, nums: List[int], target: int) -> int: + l, r = 0, len(nums) + + while l < r: + m = l + ((r - l) // 2) + if nums[m] > target: + r = m + elif nums[m] <= target: + l = m + 1 + return l - 1 if (l and nums[l - 1] == target) else -1 +``` + +```java +public class Solution { + public int search(int[] nums, int target) { + int l = 0, r = nums.length; + + while (l < r) { + int m = l + ((r - l) / 2); + if (nums[m] > target) { + r = m; + } else { + l = m + 1; + } + } + return (l > 0 && nums[l - 1] == target) ? l - 1 : -1; + } +} +``` + +```cpp +class Solution { +public: + int search(vector& nums, int target) { + int l = 0, r = nums.size(); + + while (l < r) { + int m = l + (r - l) / 2; + if (nums[m] > target) { + r = m; + } else { + l = m + 1; + } + } + return (l > 0 && nums[l - 1] == target) ? l - 1 : -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + search(nums, target) { + let l = 0, r = nums.length; + + while (l < r) { + let m = l + Math.floor((r - l) / 2); + if (nums[m] > target) { + r = m; + } else { + l = m + 1; + } + } + return (l > 0 && nums[l - 1] === target) ? l - 1 : -1; + } +} +``` + +```csharp +public class Solution { + public int Search(int[] nums, int target) { + int l = 0, r = nums.Length; + + while (l < r) { + int m = l + (r - l) / 2; + if (nums[m] > target) { + r = m; + } else { + l = m + 1; + } + } + return (l > 0 && nums[l - 1] == target) ? l - 1 : -1; + } +} +``` + +```go +func search(nums []int, target int) int { + l, r := 0, len(nums) + + for l < r { + m := l + (r-l)/2 + if nums[m] > target { + r = m + } else { + l = m + 1 + } + } + if l > 0 && nums[l-1] == target { + return l - 1 + } + return -1 +} +``` + +```kotlin +class Solution { + fun search(nums: IntArray, target: Int): Int { + var l = 0 + var r = nums.size + + while (l < r) { + val m = l + (r - l) / 2 + if (nums[m] > target) { + r = m + } else { + l = m + 1 + } + } + return if (l > 0 && nums[l - 1] == target) l - 1 else -1 + } +} +``` + +```swift +class Solution { + func search(_ nums: [Int], _ target: Int) -> Int { + var l = 0, r = nums.count + + while l < r { + let m = l + (r - l) / 2 + if nums[m] > target { + r = m + } else { + l = m + 1 + } + } + return (l > 0 && nums[l - 1] == target) ? l - 1 : -1 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Lower Bound + +::tabs-start + +```python +class Solution: + def search(self, nums: List[int], target: int) -> int: + l, r = 0, len(nums) + + while l < r: + m = l + ((r - l) // 2) + if nums[m] >= target: + r = m + elif nums[m] < target: + l = m + 1 + return l if (l < len(nums) and nums[l] == target) else -1 +``` + +```java +public class Solution { + public int search(int[] nums, int target) { + int l = 0, r = nums.length; + + while (l < r) { + int m = l + (r - l) / 2; + if (nums[m] >= target) { + r = m; + } else { + l = m + 1; + } + } + return (l < nums.length && nums[l] == target) ? l : -1; + } +} +``` + +```cpp +class Solution { +public: + int search(vector& nums, int target) { + int l = 0, r = nums.size(); + + while (l < r) { + int m = l + (r - l) / 2; + if (nums[m] >= target) { + r = m; + } else { + l = m + 1; + } + } + return (l < nums.size() && nums[l] == target) ? l : -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + search(nums, target) { + let l = 0, r = nums.length; + + while (l < r) { + let m = l + Math.floor((r - l) / 2); + if (nums[m] >= target) { + r = m; + } else { + l = m + 1; + } + } + return (l < nums.length && nums[l] === target) ? l : -1; + } +} +``` + +```csharp +public class Solution { + public int Search(int[] nums, int target) { + int l = 0, r = nums.Length; + + while (l < r) { + int m = l + (r - l) / 2; + if (nums[m] >= target) { + r = m; + } else { + l = m + 1; + } + } + return (l < nums.Length && nums[l] == target) ? l : -1; + } +} +``` + +```go +func search(nums []int, target int) int { + l, r := 0, len(nums) + + for l < r { + m := l + (r-l)/2 + if nums[m] >= target { + r = m + } else { + l = m + 1 + } + } + if l < len(nums) && nums[l] == target { + return l + } + return -1 +} +``` + +```kotlin +class Solution { + fun search(nums: IntArray, target: Int): Int { + var l = 0 + var r = nums.size + + while (l < r) { + val m = l + (r - l) / 2 + if (nums[m] >= target) { + r = m + } else { + l = m + 1 + } + } + return if (l < nums.size && nums[l] == target) l else -1 + } +} +``` + +```swift +class Solution { + func search(_ nums: [Int], _ target: Int) -> Int { + var l = 0, r = nums.count + + while l < r { + let m = l + (r - l) / 2 + if nums[m] >= target { + r = m + } else { + l = m + 1 + } + } + return (l < nums.count && nums[l] == target) ? l : -1 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Built-In Function + +::tabs-start + +```python +import bisect +class Solution: + def search(self, nums: List[int], target: int) -> int: + index = bisect.bisect_left(nums, target) + return index if index < len(nums) and nums[index] == target else -1 +``` + +```java +public class Solution { + public int search(int[] nums, int target) { + int index = Arrays.binarySearch(nums, target); + return index >= 0 ? index : -1; + } +} +``` + +```cpp +class Solution { +public: + int search(vector& nums, int target) { + auto it = lower_bound(nums.begin(), nums.end(), target); + return (it != nums.end() && *it == target) ? it - nums.begin() : -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + search(nums, target) { + // There is no built in function for JS. + return nums.indexOf(target); + } +} +``` + +```csharp +public class Solution { + public int Search(int[] nums, int target) { + int index = Array.BinarySearch(nums, target); + return index >= 0 ? index : -1; + } +} +``` + +```go +func search(nums []int, target int) int { + index := sort.Search(len(nums), func(i int) bool { return nums[i] >= target }) + if index < len(nums) && nums[index] == target { + return index + } + return -1 +} +``` + +```kotlin +class Solution { + fun search(nums: IntArray, target: Int): Int { + val index = nums.binarySearch(target) + return if (index >= 0) index else -1 + } +} +``` + +```swift +class Solution { + func search(_ nums: [Int], _ target: Int) -> Int { + let index = nums.partitioningIndex { $0 >= target } + return (index < nums.count && nums[index] == target) ? index : -1 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/binary-subarrays-with-sum.md b/articles/binary-subarrays-with-sum.md new file mode 100644 index 000000000..65a07ca78 --- /dev/null +++ b/articles/binary-subarrays-with-sum.md @@ -0,0 +1,381 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def numSubarraysWithSum(self, nums: List[int], goal: int) -> int: + n, res = len(nums), 0 + + for i in range(n): + curSum = 0 + for j in range(i, n): + curSum += nums[j] + if curSum == goal: + res += 1 + + return res +``` + +```java +public class Solution { + public int numSubarraysWithSum(int[] nums, int goal) { + int n = nums.length, res = 0; + + for (int i = 0; i < n; i++) { + int curSum = 0; + for (int j = i; j < n; j++) { + curSum += nums[j]; + if (curSum == goal) { + res++; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubarraysWithSum(vector& nums, int goal) { + int n = nums.size(), res = 0; + + for (int i = 0; i < n; i++) { + int curSum = 0; + for (int j = i; j < n; j++) { + curSum += nums[j]; + if (curSum == goal) { + res++; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} goal + * @return {number} + */ + numSubarraysWithSum(nums, goal) { + const n = nums.length; + let res = 0; + + for (let i = 0; i < n; i++) { + let curSum = 0; + for (let j = i; j < n; j++) { + curSum += nums[j]; + if (curSum === goal) { + res++; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Prefix Sum + Hash Map + +::tabs-start + +```python +class Solution: + def numSubarraysWithSum(self, nums: List[int], goal: int) -> int: + prefixSum = 0 + count = { 0 : 1 } # prefixSum -> count + res = 0 + + for num in nums: + prefixSum += num + res += count.get(prefixSum - goal, 0) + count[prefixSum] = count.get(prefixSum, 0) + 1 + + return res +``` + +```java +public class Solution { + public int numSubarraysWithSum(int[] nums, int goal) { + int prefixSum = 0, res = 0; + HashMap count = new HashMap<>(); + count.put(0, 1); + + for (int num : nums) { + prefixSum += num; + res += count.getOrDefault(prefixSum - goal, 0); + count.put(prefixSum, count.getOrDefault(prefixSum, 0) + 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubarraysWithSum(vector& nums, int goal) { + int prefixSum = 0, res = 0; + unordered_map count; + count[0] = 1; + + for (int& num : nums) { + prefixSum += num; + res += count[prefixSum - goal]; + count[prefixSum]++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} goal + * @return {number} + */ + numSubarraysWithSum(nums, goal) { + let prefixSum = 0, res = 0; + const count = new Map(); + count.set(0, 1); + + for (const num of nums) { + prefixSum += num; + res += count.get(prefixSum - goal) || 0; + count.set(prefixSum, (count.get(prefixSum) || 0) + 1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Prefix Sum + Array + +::tabs-start + +```python +class Solution: + def numSubarraysWithSum(self, nums: List[int], goal: int) -> int: + n = len(nums) + count = [0] * (n + 1) + count[0] = 1 + prefixSum, res = 0, 0 + + for num in nums: + prefixSum += num + if prefixSum >= goal: + res += count[prefixSum - goal] + count[prefixSum] += 1 + + return res +``` + +```java +public class Solution { + public int numSubarraysWithSum(int[] nums, int goal) { + int n = nums.length, prefixSum = 0, res = 0; + int[] count = new int[n + 1]; + count[0] = 1; + + for (int num : nums) { + prefixSum += num; + if (prefixSum >= goal) { + res += count[prefixSum - goal]; + } + count[prefixSum]++; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubarraysWithSum(vector& nums, int goal) { + int n = nums.size(), prefixSum = 0, res = 0; + vector count(n + 1, 0); + count[0] = 1; + + for (int num : nums) { + prefixSum += num; + if (prefixSum >= goal) { + res += count[prefixSum - goal]; + } + count[prefixSum]++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} goal + * @return {number} + */ + numSubarraysWithSum(nums, goal) { + const n = nums.length; + const count = Array(n + 1).fill(0); + count[0] = 1; + let prefixSum = 0, res = 0; + + for (const num of nums) { + prefixSum += num; + if (prefixSum >= goal) { + res += count[prefixSum - goal]; + } + count[prefixSum]++; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Sliding Window + +::tabs-start + +```python +class Solution: + def numSubarraysWithSum(self, nums: List[int], goal: int) -> int: + def helper(x): + if x < 0: + return 0 + res = l = cur = 0 + for r in range(len(nums)): + cur += nums[r] + while cur > x: + cur -= nums[l] + l += 1 + res += (r - l + 1) + return res + + return helper(goal) - helper(goal - 1) +``` + +```java +public class Solution { + public int numSubarraysWithSum(int[] nums, int goal) { + return helper(nums, goal) - helper(nums, goal - 1); + } + + private int helper(int[] nums, int x) { + if (x < 0) return 0; + int res = 0, l = 0, cur = 0; + for (int r = 0; r < nums.length; r++) { + cur += nums[r]; + while (cur > x) { + cur -= nums[l]; + l++; + } + res += (r - l + 1); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubarraysWithSum(vector& nums, int goal) { + return helper(nums, goal) - helper(nums, goal - 1); + } + +private: + int helper(vector& nums, int x) { + if (x < 0) return 0; + int res = 0, l = 0, cur = 0; + for (int r = 0; r < nums.size(); r++) { + cur += nums[r]; + while (cur > x) { + cur -= nums[l]; + l++; + } + res += (r - l + 1); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} goal + * @return {number} + */ + numSubarraysWithSum(nums, goal) { + const helper = (x) => { + if (x < 0) return 0; + let res = 0, l = 0, cur = 0; + for (let r = 0; r < nums.length; r++) { + cur += nums[r]; + while (cur > x) { + cur -= nums[l]; + l++; + } + res += (r - l + 1); + } + return res; + }; + + return helper(goal) - helper(goal - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/binary-tree-diameter.md b/articles/binary-tree-diameter.md new file mode 100644 index 000000000..b11c3e40e --- /dev/null +++ b/articles/binary-tree-diameter.md @@ -0,0 +1,996 @@ +## 1. Brute Force + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int: + if not root: + return 0 + + leftHeight = self.maxHeight(root.left) + rightHeight = self.maxHeight(root.right) + diameter = leftHeight + rightHeight + sub = max(self.diameterOfBinaryTree(root.left), + self.diameterOfBinaryTree(root.right)) + return max(diameter, sub) + + + def maxHeight(self, root: Optional[TreeNode]) -> int: + if not root: + return 0 + + return 1 + max(self.maxHeight(root.left), self.maxHeight(root.right)) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public int diameterOfBinaryTree(TreeNode root) { + if (root == null) { + return 0; + } + + int leftHeight = maxHeight(root.left); + int rightHeight = maxHeight(root.right); + int diameter = leftHeight + rightHeight; + int sub = Math.max(diameterOfBinaryTree(root.left), + diameterOfBinaryTree(root.right)); + return Math.max(diameter, sub); + } + + public int maxHeight(TreeNode root) { + if (root == null) { + return 0; + } + return 1 + Math.max(maxHeight(root.left), maxHeight(root.right)); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + int diameterOfBinaryTree(TreeNode* root) { + if (!root) return 0; + + int leftHeight = maxHeight(root->left); + int rightHeight = maxHeight(root->right); + int diameter = leftHeight + rightHeight; + int sub = max(diameterOfBinaryTree(root->left), + diameterOfBinaryTree(root->right)); + return max(diameter, sub); + } + + int maxHeight(TreeNode* root) { + if (!root) return 0; + return 1 + max(maxHeight(root->left), maxHeight(root->right)); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + diameterOfBinaryTree(root) { + if (!root) return 0; + + let leftHeight = this.maxHeight(root.left); + let rightHeight = this.maxHeight(root.right); + let diameter = leftHeight + rightHeight; + let sub = Math.max(this.diameterOfBinaryTree(root.left), + this.diameterOfBinaryTree(root.right)); + return Math.max(diameter, sub); + } + + /** + * @param {TreeNode} root + * @return {number} + */ + maxHeight(root) { + if (!root) return 0; + return 1 + Math.max(this.maxHeight(root.left), this.maxHeight(root.right)); + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public int DiameterOfBinaryTree(TreeNode root) { + if (root == null) { + return 0; + } + + int leftHeight = MaxHeight(root.left); + int rightHeight = MaxHeight(root.right); + int diameter = leftHeight + rightHeight; + int sub = Math.Max(DiameterOfBinaryTree(root.left), + DiameterOfBinaryTree(root.right)); + return Math.Max(diameter, sub); + } + + public int MaxHeight(TreeNode root) { + if (root == null) { + return 0; + } + return 1 + Math.Max(MaxHeight(root.left), MaxHeight(root.right)); + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func diameterOfBinaryTree(root *TreeNode) int { + if root == nil { + return 0 + } + + leftHeight := maxHeight(root.Left) + rightHeight := maxHeight(root.Right) + diameter := leftHeight + rightHeight + + sub := max(diameterOfBinaryTree(root.Left), + diameterOfBinaryTree(root.Right)) + + return max(diameter, sub) +} + +func maxHeight(root *TreeNode) int { + if root == nil { + return 0 + } + + return 1 + max(maxHeight(root.Left), maxHeight(root.Right)) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun diameterOfBinaryTree(root: TreeNode?): Int { + if (root == null) { + return 0 + } + + val leftHeight = maxHeight(root.left) + val rightHeight = maxHeight(root.right) + val diameter = leftHeight + rightHeight + + val sub = maxOf( + diameterOfBinaryTree(root.left), + diameterOfBinaryTree(root.right) + ) + + return maxOf(diameter, sub) + } + + private fun maxHeight(root: TreeNode?): Int { + if (root == null) { + return 0 + } + + return 1 + maxOf(maxHeight(root.left), maxHeight(root.right)) + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func diameterOfBinaryTree(_ root: TreeNode?) -> Int { + guard let root = root else { return 0 } + + let leftHeight = maxHeight(root.left) + let rightHeight = maxHeight(root.right) + let diameter = leftHeight + rightHeight + let sub = max(diameterOfBinaryTree(root.left), diameterOfBinaryTree(root.right)) + + return max(diameter, sub) + } + + private func maxHeight(_ root: TreeNode?) -> Int { + guard let root = root else { return 0 } + return 1 + max(maxHeight(root.left), maxHeight(root.right)) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int: + res = 0 + + def dfs(root): + nonlocal res + + if not root: + return 0 + left = dfs(root.left) + right = dfs(root.right) + res = max(res, left + right) + + return 1 + max(left, right) + + dfs(root) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + + public int diameterOfBinaryTree(TreeNode root) { + int[] res = new int[1]; + dfs(root, res); + return res[0]; + } + + private int dfs(TreeNode root, int[] res) { + if (root == null) { + return 0; + } + int left = dfs(root.left, res); + int right = dfs(root.right, res); + res[0] = Math.max(res[0], left + right); + return 1 + Math.max(left, right); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + int diameterOfBinaryTree(TreeNode* root) { + int res = 0; + dfs(root, res); + return res; + } + +private: + int dfs(TreeNode* root, int& res) { + if (!root) { + return 0; + } + int left = dfs(root->left, res); + int right = dfs(root->right, res); + res = max(res, left + right); + return 1 + max(left, right); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + diameterOfBinaryTree(root) { + const res = [0]; + this.dfs(root, res); + return res[0]; + } + + /** + * @param {TreeNode} root + * @param {number[]} res + * @return {number} + */ + dfs(root, res) { + if (root === null) { + return 0; + } + const left = this.dfs(root.left, res); + const right = this.dfs(root.right, res); + res[0] = Math.max(res[0], left + right); + return 1 + Math.max(left, right); + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + + public int DiameterOfBinaryTree(TreeNode root) { + int res = 0; + DFS(root, ref res); + return res; + } + + private int DFS(TreeNode root, ref int res) { + if (root == null) { + return 0; + } + int left = DFS(root.left, ref res); + int right = DFS(root.right, ref res); + res = Math.Max(res, left + right); + return 1 + Math.Max(left, right); + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func diameterOfBinaryTree(root *TreeNode) int { + res := 0 + + var dfs func(*TreeNode) int + dfs = func(root *TreeNode) int { + if root == nil { + return 0 + } + + left := dfs(root.Left) + right := dfs(root.Right) + res = max(res, left + right) + + return 1 + max(left, right) + } + + dfs(root) + return res +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + private var res = 0 + + fun diameterOfBinaryTree(root: TreeNode?): Int { + dfs(root) + return res + } + + private fun dfs(root: TreeNode?): Int { + if (root == null) { + return 0 + } + + val left = dfs(root.left) + val right = dfs(root.right) + res = maxOf(res, left + right) + + return 1 + maxOf(left, right) + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func diameterOfBinaryTree(_ root: TreeNode?) -> Int { + var res = 0 + + func dfs(_ root: TreeNode?) -> Int { + guard let root = root else { return 0 } + let left = dfs(root.left) + let right = dfs(root.right) + res = max(res, left + right) + return 1 + max(left, right) + } + + dfs(root) + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(h)$ + * Best Case ([balanced tree](https://www.geeksforgeeks.org/balanced-binary-tree/)): $O(log(n))$ + * Worst Case ([degenerate tree](https://www.geeksforgeeks.org/introduction-to-degenerate-binary-tree/)): $O(n)$ + +> Where $n$ is the number of nodes in the tree and $h$ is the height of the tree. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int: + stack = [root] + mp = {None: (0, 0)} + + while stack: + node = stack[-1] + + if node.left and node.left not in mp: + stack.append(node.left) + elif node.right and node.right not in mp: + stack.append(node.right) + else: + node = stack.pop() + + leftHeight, leftDiameter = mp[node.left] + rightHeight, rightDiameter = mp[node.right] + + mp[node] = (1 + max(leftHeight, rightHeight), + max(leftHeight + rightHeight, leftDiameter, rightDiameter)) + + return mp[root][1] +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public int diameterOfBinaryTree(TreeNode root) { + Map mp = new HashMap<>(); + mp.put(null, new int[]{0, 0}); + Stack stack = new Stack<>(); + stack.push(root); + + while (!stack.isEmpty()) { + TreeNode node = stack.peek(); + + if (node.left != null && !mp.containsKey(node.left)) { + stack.push(node.left); + } else if (node.right != null && !mp.containsKey(node.right)) { + stack.push(node.right); + } else { + node = stack.pop(); + + int[] leftData = mp.get(node.left); + int[] rightData = mp.get(node.right); + + int leftHeight = leftData[0], leftDiameter = leftData[1]; + int rightHeight = rightData[0], rightDiameter = rightData[1]; + + int height = 1 + Math.max(leftHeight, rightHeight); + int diameter = Math.max(leftHeight + rightHeight, + Math.max(leftDiameter, rightDiameter)); + + mp.put(node, new int[]{height, diameter}); + } + } + return mp.get(root)[1]; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + int diameterOfBinaryTree(TreeNode* root) { + unordered_map> mp; + mp[nullptr] = {0, 0}; + stack stack; + stack.push(root); + + while (!stack.empty()) { + TreeNode* node = stack.top(); + + if (node->left && mp.find(node->left) == mp.end()) { + stack.push(node->left); + } else if (node->right && mp.find(node->right) == mp.end()) { + stack.push(node->right); + } else { + node = stack.top(); + stack.pop(); + + auto[leftHeight, leftDiameter] = mp[node->left]; + auto[rightHeight, rightDiameter] = mp[node->right]; + + int height = 1 + std::max(leftHeight, rightHeight); + int diameter = max(leftHeight + rightHeight, + max(leftDiameter, rightDiameter)); + + mp[node] = {height, diameter}; + } + } + return mp[root].second; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + diameterOfBinaryTree(root) { + let stack = [root]; + let mp = new Map(); + mp.set(null, [0, 0]); + + while (stack.length > 0) { + let node = stack[stack.length - 1]; + + if (node.left && !mp.has(node.left)) { + stack.push(node.left); + } else if (node.right && !mp.has(node.right)) { + stack.push(node.right); + } else { + node = stack.pop(); + + let [leftHeight, leftDiameter] = mp.get(node.left); + let [rightHeight, rightDiameter] = mp.get(node.right); + + let height = 1 + Math.max(leftHeight, rightHeight); + let diameter = Math.max(leftHeight + rightHeight, + Math.max(leftDiameter, rightDiameter)); + + mp.set(node, [height, diameter]); + } + } + return mp.get(root)[1]; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public int DiameterOfBinaryTree(TreeNode root) { + if (root == null) { + return 0; + } + + Stack stack = new Stack(); + Dictionary mp = new Dictionary(); + stack.Push(root); + + while (stack.Count > 0) { + TreeNode node = stack.Peek(); + + if (node.left != null && !mp.ContainsKey(node.left)) { + stack.Push(node.left); + } else if (node.right != null && !mp.ContainsKey(node.right)) { + stack.Push(node.right); + } else { + node = stack.Pop(); + + int leftHeight = 0, leftDiameter = 0; + if (node.left != null && mp.ContainsKey(node.left)) { + (leftHeight, leftDiameter) = mp[node.left]; + } + + int rightHeight = 0, rightDiameter = 0; + if (node.right != null && mp.ContainsKey(node.right)) { + (rightHeight, rightDiameter) = mp[node.right]; + } + + int height = 1 + Math.Max(leftHeight, rightHeight); + int diameter = Math.Max(leftHeight + rightHeight, + Math.Max(leftDiameter, rightDiameter)); + + mp[node] = (height, diameter); + } + } + + return mp[root].Item2; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func diameterOfBinaryTree(root *TreeNode) int { + if root == nil { + return 0 + } + + stack := linkedliststack.New() + stack.Push(root) + mp := make(map[*TreeNode][]int) + mp[nil] = []int{0, 0} + + for !stack.Empty() { + val, _ := stack.Peek() + node := val.(*TreeNode) + + if node.Left != nil && len(mp[node.Left]) == 0 { + stack.Push(node.Left) + } else if node.Right != nil && len(mp[node.Right]) == 0 { + stack.Push(node.Right) + } else { + stack.Pop() + + leftHeight := mp[node.Left][0] + leftDiameter := mp[node.Left][1] + rightHeight := mp[node.Right][0] + rightDiameter := mp[node.Right][1] + + height := 1 + max(leftHeight, rightHeight) + diameter := max(leftHeight+rightHeight, + max(leftDiameter, rightDiameter)) + + mp[node] = []int{height, diameter} + } + } + + return mp[root][1] +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun diameterOfBinaryTree(root: TreeNode?): Int { + if (root == null) { + return 0 + } + + val stack = ArrayDeque() + stack.addLast(root) + + val mp = HashMap>() + mp[null] = Pair(0, 0) + + while (stack.isNotEmpty()) { + val node = stack.last() + + when { + node.left != null && !mp.containsKey(node.left) -> { + stack.addLast(node.left) + } + node.right != null && !mp.containsKey(node.right) -> { + stack.addLast(node.right) + } + else -> { + stack.removeLast() + + val (leftHeight, leftDiameter) = mp[node.left] ?: Pair(0, 0) + val (rightHeight, rightDiameter) = mp[node.right] ?: Pair(0, 0) + + val height = 1 + maxOf(leftHeight, rightHeight) + val diameter = maxOf(leftHeight + rightHeight, + leftDiameter, + rightDiameter) + + mp[node] = Pair(height, diameter) + } + } + } + + return mp[root]?.second ?: 0 + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func diameterOfBinaryTree(_ root: TreeNode?) -> Int { + guard let root = root else { return 0 } + var stack: [TreeNode] = [root] + var mp = [ObjectIdentifier: (Int, Int)]() + + while !stack.isEmpty { + let node = stack.last! + if let left = node.left, mp[ObjectIdentifier(left)] == nil { + stack.append(left) + } else if let right = node.right, mp[ObjectIdentifier(right)] == nil { + stack.append(right) + } else { + let node = stack.removeLast() + let leftTuple = node.left != nil ? mp[ObjectIdentifier(node.left!)]! : (0, 0) + let rightTuple = node.right != nil ? mp[ObjectIdentifier(node.right!)]! : (0, 0) + let height = 1 + max(leftTuple.0, rightTuple.0) + let diameter = max(leftTuple.0 + rightTuple.0, leftTuple.1, rightTuple.1) + mp[ObjectIdentifier(node)] = (height, diameter) + } + } + + return mp[ObjectIdentifier(root)]!.1 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/binary-tree-from-preorder-and-inorder-traversal.md b/articles/binary-tree-from-preorder-and-inorder-traversal.md new file mode 100644 index 000000000..b21106a35 --- /dev/null +++ b/articles/binary-tree-from-preorder-and-inorder-traversal.md @@ -0,0 +1,913 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: + if not preorder or not inorder: + return None + + root = TreeNode(preorder[0]) + mid = inorder.index(preorder[0]) + root.left = self.buildTree(preorder[1 : mid + 1], inorder[:mid]) + root.right = self.buildTree(preorder[mid + 1 :], inorder[mid + 1 :]) + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + public TreeNode buildTree(int[] preorder, int[] inorder) { + if (preorder.length == 0 || inorder.length == 0) { + return null; + } + + TreeNode root = new TreeNode(preorder[0]); + int mid = -1; + for (int i = 0; i < inorder.length; i++) { + if (inorder[i] == preorder[0]) { + mid = i; + break; + } + } + + int[] leftPreorder = Arrays.copyOfRange(preorder, 1, mid + 1); + int[] leftInorder = Arrays.copyOfRange(inorder, 0, mid); + root.left = buildTree(leftPreorder, leftInorder); + + int[] rightPreorder = Arrays.copyOfRange(preorder, mid + 1, preorder.length); + int[] rightInorder = Arrays.copyOfRange(inorder, mid + 1, inorder.length); + root.right = buildTree(rightPreorder, rightInorder); + + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + TreeNode* buildTree(vector& preorder, vector& inorder) { + if (preorder.empty() || inorder.empty()) { + return nullptr; + } + + TreeNode* root = new TreeNode(preorder[0]); + auto mid = find(inorder.begin(), inorder.end(), preorder[0]) - inorder.begin(); + vector leftPre(preorder.begin() + 1, preorder.begin() + mid + 1); + vector rightPre(preorder.begin() + mid + 1, preorder.end()); + vector leftIn(inorder.begin(), inorder.begin() + mid); + vector rightIn(inorder.begin() + mid + 1, inorder.end()); + root->left = buildTree(leftPre, leftIn); + root->right = buildTree(rightPre, rightIn); + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {number[]} preorder + * @param {number[]} inorder + * @return {TreeNode} + */ + buildTree(preorder, inorder) { + if (!preorder.length || !inorder.length) { + return null; + } + + let root = new TreeNode(preorder[0]); + let mid = inorder.indexOf(preorder[0]); + root.left = this.buildTree( + preorder.slice(1, mid + 1), + inorder.slice(0, mid), + ); + root.right = this.buildTree( + preorder.slice(mid + 1), + inorder.slice(mid + 1), + ); + return root; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public TreeNode BuildTree(int[] preorder, int[] inorder) { + if (preorder.Length == 0 || inorder.Length == 0) { + return null; + } + + TreeNode root = new TreeNode(preorder[0]); + int mid = Array.IndexOf(inorder, preorder[0]); + root.left = BuildTree(preorder.Skip(1).Take(mid).ToArray(), inorder.Take(mid).ToArray()); + root.right = BuildTree(preorder.Skip(mid + 1).ToArray(), inorder.Skip(mid + 1).ToArray()); + return root; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func buildTree(preorder []int, inorder []int) *TreeNode { + if len(preorder) == 0 || len(inorder) == 0 { + return nil + } + + root := &TreeNode{Val: preorder[0]} + + mid := 0 + for i, val := range inorder { + if val == preorder[0] { + mid = i + break + } + } + + root.Left = buildTree(preorder[1:mid+1], inorder[:mid]) + root.Right = buildTree(preorder[mid+1:], inorder[mid+1:]) + + return root +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun buildTree(preorder: IntArray, inorder: IntArray): TreeNode? { + if (preorder.isEmpty() || inorder.isEmpty()) { + return null + } + + val root = TreeNode(preorder[0]) + + val mid = inorder.indexOf(preorder[0]) + + root.left = buildTree( + preorder.slice(1..mid).toIntArray(), + inorder.slice(0 until mid).toIntArray() + ) + + root.right = buildTree( + preorder.slice(mid + 1 until preorder.size).toIntArray(), + inorder.slice(mid + 1 until inorder.size).toIntArray() + ) + + return root + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func buildTree(_ preorder: [Int], _ inorder: [Int]) -> TreeNode? { + if preorder.isEmpty || inorder.isEmpty { + return nil + } + + let rootValue = preorder[0] + let root = TreeNode(rootValue) + guard let mid = inorder.firstIndex(of: rootValue) else { + return root + } + + root.left = buildTree( + Array(preorder[1..<(mid + 1)]), + Array(inorder[0.. Optional[TreeNode]: + indices = {val: idx for idx, val in enumerate(inorder)} + + self.pre_idx = 0 + def dfs(l, r): + if l > r: + return None + + root_val = preorder[self.pre_idx] + self.pre_idx += 1 + root = TreeNode(root_val) + mid = indices[root_val] + root.left = dfs(l, mid - 1) + root.right = dfs(mid + 1, r) + return root + + return dfs(0, len(inorder) - 1) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + int pre_idx = 0; + HashMap indices = new HashMap<>(); + + public TreeNode buildTree(int[] preorder, int[] inorder) { + for (int i = 0; i < inorder.length; i++) { + indices.put(inorder[i], i); + } + return dfs(preorder, 0, inorder.length - 1); + } + + private TreeNode dfs(int[] preorder, int l, int r) { + if (l > r) return null; + int root_val = preorder[pre_idx++]; + TreeNode root = new TreeNode(root_val); + int mid = indices.get(root_val); + root.left = dfs(preorder, l, mid - 1); + root.right = dfs(preorder, mid + 1, r); + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { + int pre_idx = 0; + unordered_map indices; + + TreeNode* dfs(vector& preorder, int l, int r) { + if (l > r) return nullptr; + int root_val = preorder[pre_idx++]; + TreeNode* root = new TreeNode(root_val); + int mid = indices[root_val]; + root->left = dfs(preorder, l, mid - 1); + root->right = dfs(preorder, mid + 1, r); + return root; + } + +public: + TreeNode* buildTree(vector& preorder, vector& inorder) { + for (int i = 0; i < inorder.size(); ++i) { + indices[inorder[i]] = i; + } + return dfs(preorder, 0, inorder.size() - 1); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {number[]} preorder + * @param {number[]} inorder + * @return {TreeNode} + */ + buildTree(preorder, inorder) { + let pre_idx = 0; + let indices = new Map(); + + inorder.forEach((val, i) => indices.set(val, i)); + + function dfs(l, r) { + if (l > r) return null; + let root_val = preorder[pre_idx++]; + let root = new TreeNode(root_val); + let mid = indices.get(root_val); + root.left = dfs(l, mid - 1); + root.right = dfs(mid + 1, r); + return root; + } + + return dfs(0, inorder.length - 1); + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + int pre_idx = 0; + Dictionary indices = new Dictionary(); + + public TreeNode BuildTree(int[] preorder, int[] inorder) { + for (int i = 0; i < inorder.Length; i++) { + indices[inorder[i]] = i; + } + return Dfs(preorder, 0, inorder.Length - 1); + } + + private TreeNode Dfs(int[] preorder, int l, int r) { + if (l > r) return null; + int root_val = preorder[pre_idx++]; + TreeNode root = new TreeNode(root_val); + int mid = indices[root_val]; + root.left = Dfs(preorder, l, mid - 1); + root.right = Dfs(preorder, mid + 1, r); + return root; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func buildTree(preorder []int, inorder []int) *TreeNode { + indices := make(map[int]int) + for i, val := range inorder { + indices[val] = i + } + + preIdx := 0 + + var dfs func(int, int) *TreeNode + dfs = func(left, right int) *TreeNode { + if left > right { + return nil + } + + rootVal := preorder[preIdx] + preIdx++ + + root := &TreeNode{Val: rootVal} + mid := indices[rootVal] + + root.Left = dfs(left, mid - 1) + root.Right = dfs(mid + 1, right) + + return root + } + + return dfs(0, len(inorder) - 1) +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + private var preIdx = 0 + + fun buildTree(preorder: IntArray, inorder: IntArray): TreeNode? { + val indices = inorder.withIndex() + .associate { (index, value) -> value to index } + + fun dfs(left: Int, right: Int): TreeNode? { + if (left > right) { + return null + } + + val rootVal = preorder[preIdx++] + val root = TreeNode(rootVal) + val mid = indices[rootVal]!! + + root.left = dfs(left, mid - 1) + root.right = dfs(mid + 1, right) + + return root + } + + return dfs(0, inorder.lastIndex) + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + var preIndex = 0 + var indexMap = [Int: Int]() + + func buildTree(_ preorder: [Int], _ inorder: [Int]) -> TreeNode? { + for (index, value) in inorder.enumerated() { + indexMap[value] = index + } + return dfs(preorder, 0, inorder.count - 1) + } + + private func dfs(_ preorder: [Int], _ left: Int, _ right: Int) -> TreeNode? { + if left > right { + return nil + } + + let rootVal = preorder[preIndex] + preIndex += 1 + let root = TreeNode(rootVal) + let mid = indexMap[rootVal]! + + root.left = dfs(preorder, left, mid - 1) + root.right = dfs(preorder, mid + 1, right) + + return root + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Depth First Search (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: + preIdx = inIdx = 0 + def dfs(limit): + nonlocal preIdx, inIdx + if preIdx >= len(preorder): + return None + if inorder[inIdx] == limit: + inIdx += 1 + return None + + root = TreeNode(preorder[preIdx]) + preIdx += 1 + root.left = dfs(root.val) + root.right = dfs(limit) + return root + return dfs(float('inf')) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + int preIdx = 0; + int inIdx = 0; + + public TreeNode buildTree(int[] preorder, int[] inorder) { + return dfs(preorder, inorder, Integer.MAX_VALUE); + } + + private TreeNode dfs(int[] preorder, int[] inorder, int limit) { + if (preIdx >= preorder.length) return null; + if (inorder[inIdx] == limit) { + inIdx++; + return null; + } + + TreeNode root = new TreeNode(preorder[preIdx++]); + root.left = dfs(preorder, inorder, root.val); + root.right = dfs(preorder, inorder, limit); + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { + int preIdx = 0; + int inIdx = 0; + + TreeNode* dfs(vector& preorder, vector& inorder, int limit) { + if (preIdx >= preorder.size()) return nullptr; + if (inorder[inIdx] == limit) { + inIdx++; + return nullptr; + } + + TreeNode* root = new TreeNode(preorder[preIdx++]); + root->left = dfs(preorder, inorder, root->val); + root->right = dfs(preorder, inorder, limit); + return root; + } + +public: + TreeNode* buildTree(vector& preorder, vector& inorder) { + return dfs(preorder, inorder, INT_MAX); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {number[]} preorder + * @param {number[]} inorder + * @return {TreeNode} + */ + buildTree(preorder, inorder) { + let preIdx = 0, inIdx = 0; + + function dfs(limit) { + if (preIdx >= preorder.length) return null; + if (inorder[inIdx] === limit) { + inIdx++; + return null; + } + + let root = new TreeNode(preorder[preIdx++]); + root.left = dfs(root.val); + root.right = dfs(limit); + return root; + } + + return dfs(Infinity); + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + int preIdx = 0; + int inIdx = 0; + + public TreeNode BuildTree(int[] preorder, int[] inorder) { + return Dfs(preorder, inorder, int.MaxValue); + } + + private TreeNode Dfs(int[] preorder, int[] inorder, int limit) { + if (preIdx >= preorder.Length) return null; + if (inorder[inIdx] == limit) { + inIdx++; + return null; + } + + TreeNode root = new TreeNode(preorder[preIdx++]); + root.left = Dfs(preorder, inorder, root.val); + root.right = Dfs(preorder, inorder, limit); + return root; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func buildTree(preorder []int, inorder []int) *TreeNode { + preIdx, inIdx := 0, 0 + + var dfs func(int) *TreeNode + dfs = func(limit int) *TreeNode { + if preIdx >= len(preorder) { + return nil + } + if inorder[inIdx] == limit { + inIdx++ + return nil + } + + root := &TreeNode{Val: preorder[preIdx]} + preIdx++ + + root.Left = dfs(root.Val) + root.Right = dfs(limit) + + return root + } + + return dfs(math.MaxInt) +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + private var preIdx = 0 + private var inIdx = 0 + + fun buildTree(preorder: IntArray, inorder: IntArray): TreeNode? { + fun dfs(limit: Int): TreeNode? { + if (preIdx >= preorder.size) { + return null + } + if (inorder[inIdx] == limit) { + inIdx++ + return null + } + + val root = TreeNode(preorder[preIdx]) + preIdx++ + + root.left = dfs(root.`val`) + root.right = dfs(limit) + + return root + } + + return dfs(Int.MAX_VALUE) + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + var preIdx = 0 + var inIdx = 0 + + func buildTree(_ preorder: [Int], _ inorder: [Int]) -> TreeNode? { + return dfs(preorder, inorder, Int.max) + } + + private func dfs(_ preorder: [Int], _ inorder: [Int], _ limit: Int) -> TreeNode? { + if preIdx >= preorder.count { + return nil + } + if inorder[inIdx] == limit { + inIdx += 1 + return nil + } + + let root = TreeNode(preorder[preIdx]) + preIdx += 1 + root.left = dfs(preorder, inorder, root.val) + root.right = dfs(preorder, inorder, limit) + return root + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. \ No newline at end of file diff --git a/articles/binary-tree-inorder-traversal.md b/articles/binary-tree-inorder-traversal.md new file mode 100644 index 000000000..84e525d71 --- /dev/null +++ b/articles/binary-tree-inorder-traversal.md @@ -0,0 +1,576 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]: + res = [] + + def inorder(node): + if not node: + return + + inorder(node.left) + res.append(node.val) + inorder(node.right) + + inorder(root) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private List res; + + public List inorderTraversal(TreeNode root) { + res = new ArrayList<>(); + inorder(root); + return res; + } + + private void inorder(TreeNode node) { + if (node == null) { + return; + } + inorder(node.left); + res.add(node.val); + inorder(node.right); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { + vector res; + +public: + vector inorderTraversal(TreeNode* root) { + inorder(root); + return res; + } + +private: + void inorder(TreeNode* node) { + if (!node) { + return; + } + inorder(node->left); + res.push_back(node->val); + inorder(node->right); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + inorderTraversal(root) { + const res = []; + + const inorder = (node) => { + if (!node) return; + inorder(node.left); + res.push(node.val); + inorder(node.right); + }; + + inorder(root); + return res; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List InorderTraversal(TreeNode root) { + List res = new List(); + void Inorder(TreeNode node) { + if (node == null) return; + Inorder(node.left); + res.Add(node.val); + Inorder(node.right); + } + Inorder(root); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(n)$ space for the recursion stack. + * $O(n)$ space for the output array. + +--- + +## 2. Iterative Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]: + res = [] + stack = [] + cur = root + + while cur or stack: + while cur: + stack.append(cur) + cur = cur.left + cur = stack.pop() + res.append(cur.val) + cur = cur.right + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List inorderTraversal(TreeNode root) { + List res = new ArrayList<>(); + Stack stack = new Stack<>(); + TreeNode cur = root; + + while (cur != null || !stack.isEmpty()) { + while (cur != null) { + stack.push(cur); + cur = cur.left; + } + cur = stack.pop(); + res.add(cur.val); + cur = cur.right; + } + + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector inorderTraversal(TreeNode* root) { + vector res; + stack stack; + TreeNode* cur = root; + + while (cur || !stack.empty()) { + while (cur) { + stack.push(cur); + cur = cur->left; + } + cur = stack.top(); + stack.pop(); + res.push_back(cur->val); + cur = cur->right; + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + inorderTraversal(root) { + const res = []; + const stack = []; + let cur = root; + + while (cur || stack.length > 0) { + while (cur) { + stack.push(cur); + cur = cur.left; + } + cur = stack.pop(); + res.push(cur.val); + cur = cur.right; + } + + return res; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public IList InorderTraversal(TreeNode root) { + List res = new List(); + Stack stack = new Stack(); + TreeNode cur = root; + + while (cur != null || stack.Count > 0) { + while (cur != null) { + stack.Push(cur); + cur = cur.left; + } + cur = stack.Pop(); + res.Add(cur.val); + cur = cur.right; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(n)$ space for the stack. + * $O(n)$ space for the output array. + +--- + +## 3. Morris Traversal + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]: + res = [] + cur = root + + while cur: + if not cur.left: + res.append(cur.val) + cur = cur.right + else: + prev = cur.left + while prev.right and prev.right != cur: + prev = prev.right + + if not prev.right: + prev.right = cur + cur = cur.left + else: + prev.right = None + res.append(cur.val) + cur = cur.right + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List inorderTraversal(TreeNode root) { + List res = new ArrayList<>(); + TreeNode cur = root; + + while (cur != null) { + if (cur.left == null) { + res.add(cur.val); + cur = cur.right; + } else { + TreeNode prev = cur.left; + while (prev.right != null && prev.right != cur) { + prev = prev.right; + } + + if (prev.right == null) { + prev.right = cur; + cur = cur.left; + } else { + prev.right = null; + res.add(cur.val); + cur = cur.right; + } + } + } + + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector inorderTraversal(TreeNode* root) { + vector res; + TreeNode* cur = root; + + while (cur) { + if (!cur->left) { + res.push_back(cur->val); + cur = cur->right; + } else { + TreeNode* prev = cur->left; + while (prev->right && prev->right != cur) { + prev = prev->right; + } + + if (!prev->right) { + prev->right = cur; + cur = cur->left; + } else { + prev->right = nullptr; + res.push_back(cur->val); + cur = cur->right; + } + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + inorderTraversal(root) { + const res = []; + let cur = root; + + while (cur) { + if (!cur.left) { + res.push(cur.val); + cur = cur.right; + } else { + let prev = cur.left; + while (prev.right && prev.right !== cur) { + prev = prev.right; + } + + if (!prev.right) { + prev.right = cur; + cur = cur.left; + } else { + prev.right = null; + res.push(cur.val); + cur = cur.right; + } + } + } + + return res; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List InorderTraversal(TreeNode root) { + List res = new List(); + TreeNode cur = root; + + while (cur != null) { + if (cur.left == null) { + res.Add(cur.val); + cur = cur.right; + } else { + TreeNode prev = cur.left; + while (prev.right != null && prev.right != cur) { + prev = prev.right; + } + + if (prev.right == null) { + prev.right = cur; + cur = cur.left; + } else { + prev.right = null; + res.Add(cur.val); + cur = cur.right; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. \ No newline at end of file diff --git a/articles/binary-tree-maximum-path-sum.md b/articles/binary-tree-maximum-path-sum.md new file mode 100644 index 000000000..ebd5467bc --- /dev/null +++ b/articles/binary-tree-maximum-path-sum.md @@ -0,0 +1,647 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def maxPathSum(self, root: Optional[TreeNode]) -> int: + res = -float('inf') + def dfs(root): + nonlocal res + if not root: + return + left = self.getMax(root.left) + right = self.getMax(root.right) + res =max(res, root.val + left + right) + dfs(root.left) + dfs(root.right) + dfs(root) + return res + + def getMax(self, root: Optional[TreeNode]) -> int: + if not root: + return 0 + + left = self.getMax(root.left) + right = self.getMax(root.right) + path = root.val + max(left, right) + return max(0, path) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + int res = Integer.MIN_VALUE; + + public int maxPathSum(TreeNode root) { + dfs(root); + return res; + } + + private int getMax(TreeNode root) { + if (root == null) return 0; + int left = getMax(root.left); + int right = getMax(root.right); + int path = root.val + Math.max(left, right); + return Math.max(0, path); + } + + private void dfs(TreeNode root) { + if (root == null) return; + int left = getMax(root.left); + int right = getMax(root.right); + res = Math.max(res, root.val + left + right); + dfs(root.left); + dfs(root.right); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { + int res = INT_MIN; + + int getMax(TreeNode* root) { + if (!root) return 0; + int left = getMax(root->left); + int right = getMax(root->right); + int path = root->val + std::max(left, right); + return std::max(0, path); + } + + void dfs(TreeNode* root) { + if (!root) return; + int left = getMax(root->left); + int right = getMax(root->right); + res = std::max(res, root->val + left + right); + dfs(root->left); + dfs(root->right); + } + +public: + int maxPathSum(TreeNode* root) { + dfs(root); + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + maxPathSum(root) { + let res = -Infinity; + + function getMax(root) { + if (!root) return 0; + let left = getMax(root.left); + let right = getMax(root.right); + let path = root.val + Math.max(left, right); + return Math.max(0, path); + } + + function dfs(root) { + if (!root) return; + let left = getMax(root.left); + let right = getMax(root.right); + res = Math.max(res, root.val + left + right); + dfs(root.left); + dfs(root.right); + } + + dfs(root); + return res; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + int res = int.MinValue; + + public int MaxPathSum(TreeNode root) { + dfs(root); + return res; + } + + private int GetMax(TreeNode root) { + if (root == null) return 0; + int left = GetMax(root.left); + int right = GetMax(root.right); + int path = root.val + Math.Max(left, right); + return Math.Max(0, path); + } + + private void dfs(TreeNode root) { + if (root == null) return; + int left = GetMax(root.left); + int right = GetMax(root.right); + res = Math.Max(res, root.val + left + right); + dfs(root.left); + dfs(root.right); + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func maxPathSum(root *TreeNode) int { + res := -1 << 31 + dfs(root, &res) + return res +} + +func dfs(root *TreeNode, res *int) { + if root == nil { + return + } + left := getMax(root.Left) + right := getMax(root.Right) + *res = max(*res, root.Val + left + right) + dfs(root.Left, res) + dfs(root.Right, res) +} + +func getMax(root *TreeNode) int { + if root == nil { + return 0 + } + left := getMax(root.Left) + right := getMax(root.Right) + path := root.Val + max(left, right) + return max(0, path) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + private var res = Int.MIN_VALUE + + fun maxPathSum(root: TreeNode?): Int { + dfs(root) + return res + } + + private fun dfs(root: TreeNode?) { + if (root == null) return + + val left = getMax(root.left) + val right = getMax(root.right) + res = maxOf(res, root.`val` + left + right) + + dfs(root.left) + dfs(root.right) + } + + private fun getMax(root: TreeNode?): Int { + if (root == null) return 0 + + val left = getMax(root.left) + val right = getMax(root.right) + val path = root.`val` + maxOf(left, right) + return maxOf(0, path) + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + var res = Int.min + + func maxPathSum(_ root: TreeNode?) -> Int { + dfs(root) + return res + } + + private func dfs(_ root: TreeNode?) { + guard let node = root else { return } + let left = getMax(node.left) + let right = getMax(node.right) + res = max(res, node.val + left + right) + dfs(node.left) + dfs(node.right) + } + + private func getMax(_ root: TreeNode?) -> Int { + guard let node = root else { return 0 } + let left = getMax(node.left) + let right = getMax(node.right) + let path = node.val + max(left, right) + return max(0, path) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Depth First Search (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def maxPathSum(self, root: Optional[TreeNode]) -> int: + res = [root.val] + + def dfs(root): + if not root: + return 0 + + leftMax = dfs(root.left) + rightMax = dfs(root.right) + leftMax = max(leftMax, 0) + rightMax = max(rightMax, 0) + + res[0] = max(res[0], root.val + leftMax + rightMax) + return root.val + max(leftMax, rightMax) + + dfs(root) + return res[0] +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + + public int maxPathSum(TreeNode root) { + int[] res = new int[]{root.val}; + dfs(root, res); + return res[0]; + } + + private int dfs(TreeNode root, int[] res) { + if (root == null) { + return 0; + } + + int leftMax = Math.max(dfs(root.left, res), 0); + int rightMax = Math.max(dfs(root.right, res), 0); + + res[0] = Math.max(res[0], root.val + leftMax + rightMax); + return root.val + Math.max(leftMax, rightMax); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + int maxPathSum(TreeNode* root) { + int res = root->val; + dfs(root, res); + return res; + } + +private: + int dfs(TreeNode* root, int& res) { + if (!root) { + return 0; + } + + int leftMax = max(dfs(root->left, res), 0); + int rightMax = max(dfs(root->right, res), 0); + + res = max(res, root->val + leftMax + rightMax); + return root->val + max(leftMax, rightMax); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + maxPathSum(root) { + const res = [root.val]; + this.dfs(root, res); + return res[0]; + } + + /** + * @param {TreeNode} root + * @param {number[]} res + * @return {number} + */ + dfs(root, res) { + if (root === null) { + return 0; + } + + const leftMax = Math.max(this.dfs(root.left, res), 0); + const rightMax = Math.max(this.dfs(root.right, res), 0); + + res[0] = Math.max(res[0], root.val + leftMax + rightMax); + return root.val + Math.max(leftMax, rightMax); + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + + public int MaxPathSum(TreeNode root) { + int res = root.val; + Dfs(root, ref res); + return res; + } + + private int Dfs(TreeNode root, ref int res) { + if (root == null) { + return 0; + } + + int leftMax = Math.Max(Dfs(root.left, ref res), 0); + int rightMax = Math.Max(Dfs(root.right, ref res), 0); + + res = Math.Max(res, root.val + leftMax + rightMax); + return root.val + Math.Max(leftMax, rightMax); + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func maxPathSum(root *TreeNode) int { + res := []int{root.Val} + + var dfs func(node *TreeNode) int + dfs = func(node *TreeNode) int { + if node == nil { + return 0 + } + + leftMax := dfs(node.Left) + rightMax := dfs(node.Right) + + leftMax = max(leftMax, 0) + rightMax = max(rightMax, 0) + + res[0] = max(res[0], node.Val+leftMax+rightMax) + + return node.Val + max(leftMax, rightMax) + } + + dfs(root) + return res[0] +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + private var res = Int.MIN_VALUE + + fun maxPathSum(root: TreeNode?): Int { + dfs(root) + return res + } + + private fun dfs(node: TreeNode?): Int { + if (node == null) { + return 0 + } + + val leftMax = maxOf(dfs(node.left), 0) + val rightMax = maxOf(dfs(node.right), 0) + + res = maxOf(res, node.`val` + leftMax + rightMax) + + return node.`val` + maxOf(leftMax, rightMax) + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func maxPathSum(_ root: TreeNode?) -> Int { + var res = root!.val + + func dfs(_ root: TreeNode?) -> Int { + guard let node = root else { return 0 } + + let leftMax = max(dfs(node.left), 0) + let rightMax = max(dfs(node.right), 0) + + res = max(res, node.val + leftMax + rightMax) + return node.val + max(leftMax, rightMax) + } + + dfs(root) + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/binary-tree-postorder-traversal.md b/articles/binary-tree-postorder-traversal.md new file mode 100644 index 000000000..8853be764 --- /dev/null +++ b/articles/binary-tree-postorder-traversal.md @@ -0,0 +1,822 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]: + res = [] + + def postorder(node): + if not node: + return + + postorder(node.left) + postorder(node.right) + res.append(node.val) + + postorder(root) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private List res; + + public List postorderTraversal(TreeNode root) { + res = new ArrayList<>(); + postorder(root); + return res; + } + + private void postorder(TreeNode node) { + if (node == null) { + return; + } + postorder(node.left); + postorder(node.right); + res.add(node.val); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { + vector res; + +public: + vector postorderTraversal(TreeNode* root) { + postorder(root); + return res; + } + +private: + void postorder(TreeNode* node) { + if (!node) { + return; + } + postorder(node->left); + postorder(node->right); + res.push_back(node->val); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + postorderTraversal(root) { + const res = []; + + const postorder = (node) => { + if (!node) return; + postorder(node.left); + postorder(node.right); + res.push(node.val); + }; + + postorder(root); + return res; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List PostorderTraversal(TreeNode root) { + List res = new List(); + + void Postorder(TreeNode node) { + if (node == null) return; + Postorder(node.left); + Postorder(node.right); + res.Add(node.val); + } + + Postorder(root); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(n)$ space for the recursion stack. + * $O(n)$ space for the output array. + +--- + +## 2. Iterative Depth First Search - I + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]: + stack = [root] + visit = [False] + res = [] + + while stack: + cur, v = stack.pop(), visit.pop() + if cur: + if v: + res.append(cur.val) + else: + stack.append(cur) + visit.append(True) + stack.append(cur.right) + visit.append(False) + stack.append(cur.left) + visit.append(False) + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List postorderTraversal(TreeNode root) { + Stack stack = new Stack<>(); + Stack visit = new Stack<>(); + List res = new ArrayList<>(); + + stack.push(root); + visit.push(false); + + while (!stack.isEmpty()) { + TreeNode cur = stack.pop(); + boolean v = visit.pop(); + + if (cur != null) { + if (v) { + res.add(cur.val); + } else { + stack.push(cur); + visit.push(true); + stack.push(cur.right); + visit.push(false); + stack.push(cur.left); + visit.push(false); + } + } + } + + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector postorderTraversal(TreeNode* root) { + stack stk; + stack visit; + vector res; + + stk.push(root); + visit.push(false); + + while (!stk.empty()) { + TreeNode* cur = stk.top(); + bool v = visit.top(); + stk.pop(); + visit.pop(); + + if (cur) { + if (v) { + res.push_back(cur->val); + } else { + stk.push(cur); + visit.push(true); + stk.push(cur->right); + visit.push(false); + stk.push(cur->left); + visit.push(false); + } + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + postorderTraversal(root) { + const stack = [root]; + const visit = [false]; + const res = []; + + while (stack.length) { + const cur = stack.pop(); + const v = visit.pop(); + + if (cur) { + if (v) { + res.push(cur.val); + } else { + stack.push(cur); + visit.push(true); + stack.push(cur.right); + visit.push(false); + stack.push(cur.left); + visit.push(false); + } + } + } + + return res; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List PostorderTraversal(TreeNode root) { + var stack = new Stack(); + var visit = new Stack(); + var res = new List(); + + stack.Push(root); + visit.Push(false); + + while (stack.Count > 0) { + var cur = stack.Pop(); + var v = visit.Pop(); + + if (cur != null) { + if (v) { + res.Add(cur.val); + } else { + stack.Push(cur); + visit.Push(true); + stack.Push(cur.right); + visit.Push(false); + stack.Push(cur.left); + visit.Push(false); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(n)$ space for the stacks. + * $O(n)$ space for the output array. + +--- + +## 3. Iterative Depth First Search - II + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]: + res = [] + stack = [] + cur = root + + while cur or stack: + if cur: + res.append(cur.val) + stack.append(cur) + cur = cur.right + else: + cur = stack.pop() + cur = cur.left + + res.reverse() + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List postorderTraversal(TreeNode root) { + List res = new ArrayList<>(); + Stack stack = new Stack<>(); + TreeNode cur = root; + + while (cur != null || !stack.isEmpty()) { + if (cur != null) { + res.add(cur.val); + stack.push(cur); + cur = cur.right; + } else { + cur = stack.pop(); + cur = cur.left; + } + } + + Collections.reverse(res); + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector postorderTraversal(TreeNode* root) { + vector res; + stack stack; + TreeNode* cur = root; + + while (cur || !stack.empty()) { + if (cur) { + res.push_back(cur->val); + stack.push(cur); + cur = cur->right; + } else { + cur = stack.top(); + stack.pop(); + cur = cur->left; + } + } + + reverse(res.begin(), res.end()); + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + postorderTraversal(root) { + const res = []; + const stack = []; + let cur = root; + + while (cur || stack.length) { + if (cur) { + res.push(cur.val); + stack.push(cur); + cur = cur.right; + } else { + cur = stack.pop(); + cur = cur.left; + } + } + + res.reverse(); + return res; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List PostorderTraversal(TreeNode root) { + List res = new List(); + Stack stack = new Stack(); + TreeNode cur = root; + + while (cur != null || stack.Count > 0) { + if (cur != null) { + res.Add(cur.val); + stack.Push(cur); + cur = cur.right; + } else { + cur = stack.Pop(); + cur = cur.left; + } + } + + res.Reverse(); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(n)$ space for the stack. + * $O(n)$ space for the output array. + +--- + +## 4. Morris Traversal + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]: + res = [] + cur = root + + while cur: + if not cur.right: + res.append(cur.val) + cur = cur.left + else: + prev = cur.right + while prev.left and prev.left != cur: + prev = prev.left + + if not prev.left: + res.append(cur.val) + prev.left = cur + cur = cur.right + else: + prev.left = None + cur = cur.left + + res.reverse() + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List postorderTraversal(TreeNode root) { + List res = new ArrayList<>(); + TreeNode cur = root; + + while (cur != null) { + if (cur.right == null) { + res.add(cur.val); + cur = cur.left; + } else { + TreeNode prev = cur.right; + while (prev.left != null && prev.left != cur) { + prev = prev.left; + } + + if (prev.left == null) { + res.add(cur.val); + prev.left = cur; + cur = cur.right; + } else { + prev.left = null; + cur = cur.left; + } + } + } + + Collections.reverse(res); + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector postorderTraversal(TreeNode* root) { + vector res; + TreeNode* cur = root; + + while (cur) { + if (!cur->right) { + res.push_back(cur->val); + cur = cur->left; + } else { + TreeNode* prev = cur->right; + while (prev->left && prev->left != cur) { + prev = prev->left; + } + + if (!prev->left) { + res.push_back(cur->val); + prev->left = cur; + cur = cur->right; + } else { + prev->left = nullptr; + cur = cur->left; + } + } + } + + reverse(res.begin(), res.end()); + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + postorderTraversal(root) { + const res = []; + let cur = root; + + while (cur) { + if (!cur.right) { + res.push(cur.val); + cur = cur.left; + } else { + let prev = cur.right; + while (prev.left && prev.left !== cur) { + prev = prev.left; + } + + if (!prev.left) { + res.push(cur.val); + prev.left = cur; + cur = cur.right; + } else { + prev.left = null; + cur = cur.left; + } + } + } + + res.reverse(); + return res; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List PostorderTraversal(TreeNode root) { + List res = new List(); + TreeNode cur = root; + + while (cur != null) { + if (cur.right == null) { + res.Add(cur.val); + cur = cur.left; + } else { + TreeNode prev = cur.right; + while (prev.left != null && prev.left != cur) { + prev = prev.left; + } + + if (prev.left == null) { + res.Add(cur.val); + prev.left = cur; + cur = cur.right; + } else { + prev.left = null; + cur = cur.left; + } + } + } + + res.Reverse(); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. \ No newline at end of file diff --git a/articles/binary-tree-preorder-traversal.md b/articles/binary-tree-preorder-traversal.md new file mode 100644 index 000000000..f7f86ffb9 --- /dev/null +++ b/articles/binary-tree-preorder-traversal.md @@ -0,0 +1,579 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]: + res = [] + + def preorder(node): + if not node: + return + + res.append(node.val) + preorder(node.left) + preorder(node.right) + + preorder(root) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private List res; + + public List preorderTraversal(TreeNode root) { + res = new ArrayList<>(); + preorder(root); + return res; + } + + private void preorder(TreeNode node) { + if (node == null) { + return; + } + res.add(node.val); + preorder(node.left); + preorder(node.right); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { + vector res; + +public: + vector preorderTraversal(TreeNode* root) { + preorder(root); + return res; + } + +private: + void preorder(TreeNode* node) { + if (!node) { + return; + } + res.push_back(node->val); + preorder(node->left); + preorder(node->right); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + preorderTraversal(root) { + const res = []; + + const preorder = (node) => { + if (!node) return; + res.push(node.val); + preorder(node.left); + preorder(node.right); + }; + + preorder(root); + return res; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List PreorderTraversal(TreeNode root) { + List res = new List(); + Preorder(root, res); + return res; + } + + private void Preorder(TreeNode node, List res) { + if (node == null) return; + + res.Add(node.val); + Preorder(node.left, res); + Preorder(node.right, res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(n)$ space for the recursion stack. + * $O(n)$ space for the output array. + +--- + +## 2. Iterative Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]: + res = [] + stack = [] + cur = root + + while cur or stack: + if cur: + res.append(cur.val) + stack.append(cur.right) + cur = cur.left + else: + cur = stack.pop() + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List preorderTraversal(TreeNode root) { + List res = new ArrayList<>(); + Stack stack = new Stack<>(); + TreeNode cur = root; + + while (cur != null || !stack.isEmpty()) { + if (cur != null) { + res.add(cur.val); + stack.push(cur.right); + cur = cur.left; + } else { + cur = stack.pop(); + } + } + + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector preorderTraversal(TreeNode* root) { + vector res; + stack stack; + TreeNode* cur = root; + + while (cur || !stack.empty()) { + if (cur) { + res.push_back(cur->val); + stack.push(cur->right); + cur = cur->left; + } else { + cur = stack.top(); + stack.pop(); + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + preorderTraversal(root) { + const res = []; + const stack = []; + let cur = root; + + while (cur || stack.length > 0) { + if (cur) { + res.push(cur.val); + stack.push(cur.right); + cur = cur.left; + } else { + cur = stack.pop(); + + } + } + + return res; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List PreorderTraversal(TreeNode root) { + List res = new List(); + Stack stack = new Stack(); + TreeNode cur = root; + + while (cur != null || stack.Count > 0) { + if (cur != null) { + res.Add(cur.val); + stack.Push(cur.right); + cur = cur.left; + } else { + cur = stack.Pop(); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(n)$ space for the stack. + * $O(n)$ space for the output array. + +--- + +## 3. Morris Traversal + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]: + res = [] + cur = root + + while cur: + if not cur.left: + res.append(cur.val) + cur = cur.right + else: + prev = cur.left + while prev.right and prev.right != cur: + prev = prev.right + + if not prev.right: + res.append(cur.val) + prev.right = cur + cur = cur.left + else: + prev.right = None + cur = cur.right + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List preorderTraversal(TreeNode root) { + List res = new ArrayList<>(); + TreeNode cur = root; + + while (cur != null) { + if (cur.left == null) { + res.add(cur.val); + cur = cur.right; + } else { + TreeNode prev = cur.left; + while (prev.right != null && prev.right != cur) { + prev = prev.right; + } + + if (prev.right == null) { + res.add(cur.val); + prev.right = cur; + cur = cur.left; + } else { + prev.right = null; + cur = cur.right; + } + } + } + + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector preorderTraversal(TreeNode* root) { + vector res; + TreeNode* cur = root; + + while (cur) { + if (!cur->left) { + res.push_back(cur->val); + cur = cur->right; + } else { + TreeNode* prev = cur->left; + while (prev->right && prev->right != cur) { + prev = prev->right; + } + + if (!prev->right) { + res.push_back(cur->val); + prev->right = cur; + cur = cur->left; + } else { + prev->right = nullptr; + cur = cur->right; + } + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + preorderTraversal(root) { + const res = []; + let cur = root; + + while (cur) { + if (!cur.left) { + res.push(cur.val); + cur = cur.right; + } else { + let prev = cur.left; + while (prev.right && prev.right !== cur) { + prev = prev.right; + } + + if (!prev.right) { + res.push(cur.val); + prev.right = cur; + cur = cur.left; + } else { + prev.right = null; + cur = cur.right; + } + } + } + + return res; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List PreorderTraversal(TreeNode root) { + List res = new List(); + TreeNode cur = root; + + while (cur != null) { + if (cur.left == null) { + res.Add(cur.val); + cur = cur.right; + } else { + TreeNode prev = cur.left; + while (prev.right != null && prev.right != cur) { + prev = prev.right; + } + + if (prev.right == null) { + res.Add(cur.val); + prev.right = cur; + cur = cur.left; + } else { + prev.right = null; + cur = cur.right; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. \ No newline at end of file diff --git a/articles/binary-tree-right-side-view.md b/articles/binary-tree-right-side-view.md new file mode 100644 index 000000000..4d8a68b19 --- /dev/null +++ b/articles/binary-tree-right-side-view.md @@ -0,0 +1,616 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def rightSideView(self, root: Optional[TreeNode]) -> List[int]: + res = [] + + def dfs(node, depth): + if not node: + return None + if depth == len(res): + res.append(node.val) + + dfs(node.right, depth + 1) + dfs(node.left, depth + 1) + + dfs(root, 0) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + List res = new ArrayList<>(); + + public List rightSideView(TreeNode root) { + dfs(root, 0); + return res; + } + + private void dfs(TreeNode node, int depth) { + if (node == null) { + return; + } + + if (res.size() == depth) { + res.add(node.val); + } + + dfs(node.right, depth + 1); + dfs(node.left, depth + 1); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + vector res; + + vector rightSideView(TreeNode* root) { + dfs(root, 0); + return res; + } + + void dfs(TreeNode* node, int depth) { + if (!node) return; + + if (res.size() == depth) { + res.push_back(node->val); + } + + dfs(node->right, depth + 1); + dfs(node->left, depth + 1); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + rightSideView(root) { + let res = []; + + function dfs(node, depth) { + if (!node) return; + + if (res.length === depth) { + res.push(node.val); + } + + dfs(node.right, depth + 1); + dfs(node.left, depth + 1); + } + + dfs(root, 0); + return res; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + List res = new List(); + + public List RightSideView(TreeNode root) { + dfs(root, 0); + return res; + } + + private void dfs(TreeNode node, int depth) { + if (node == null) { + return; + } + + if (res.Count == depth) { + res.Add(node.val); + } + + dfs(node.right, depth + 1); + dfs(node.left, depth + 1); + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func rightSideView(root *TreeNode) []int { + var res []int + + var dfs func(node *TreeNode, depth int) + dfs = func(node *TreeNode, depth int) { + if node == nil { + return + } + if depth == len(res) { + res = append(res, node.Val) + } + dfs(node.Right, depth+1) + dfs(node.Left, depth+1) + } + + dfs(root, 0) + return res +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun rightSideView(root: TreeNode?): List { + val res = mutableListOf() + + fun dfs(node: TreeNode?, depth: Int) { + if (node == null) return + if (depth == res.size) { + res.add(node.`val`) + } + dfs(node.right, depth + 1) + dfs(node.left, depth + 1) + } + + dfs(root, 0) + return res + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func rightSideView(_ root: TreeNode?) -> [Int] { + var res = [Int]() + + func dfs(_ node: TreeNode?, _ depth: Int) { + guard let node = node else { return } + if depth == res.count { + res.append(node.val) + } + + dfs(node.right, depth + 1) + dfs(node.left, depth + 1) + } + + dfs(root, 0) + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def rightSideView(self, root: Optional[TreeNode]) -> List[int]: + res = [] + q = deque([root]) + + while q: + rightSide = None + qLen = len(q) + + for i in range(qLen): + node = q.popleft() + if node: + rightSide = node + q.append(node.left) + q.append(node.right) + if rightSide: + res.append(rightSide.val) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + public List rightSideView(TreeNode root) { + List res = new ArrayList<>(); + Queue q = new LinkedList<>(); + q.offer(root); + + while (!q.isEmpty()) { + TreeNode rightSide = null; + int qLen = q.size(); + + for (int i = 0; i < qLen; i++) { + TreeNode node = q.poll(); + if (node != null) { + rightSide = node; + q.offer(node.left); + q.offer(node.right); + } + } + if (rightSide != null) { + res.add(rightSide.val); + } + } + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + vector rightSideView(TreeNode* root) { + vector res; + queue q; + q.push(root); + + while (!q.empty()) { + TreeNode* rightSide = nullptr; + int qLen = q.size(); + + for (int i = 0; i < qLen; i++) { + TreeNode* node = q.front(); + q.pop(); + if (node) { + rightSide = node; + q.push(node->left); + q.push(node->right); + } + } + if (rightSide) { + res.push_back(rightSide->val); + } + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + rightSideView(root) { + const res = []; + const q = new Queue(); + + q.push(root); + + while (!q.isEmpty()) { + let rightSide = null; + const qLen = q.size(); + + for (let i = 0; i < qLen; i++) { + const node = q.pop(); + if (node) { + rightSide = node; + q.push(node.left); + q.push(node.right); + } + } + if (rightSide) { + res.push(rightSide.val); + } + } + return res; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public List RightSideView(TreeNode root) { + List res = new List(); + Queue q = new Queue(); + q.Enqueue(root); + + while (q.Count > 0) { + TreeNode rightSide = null; + int qLen = q.Count; + + for (int i = 0; i < qLen; i++) { + TreeNode node = q.Dequeue(); + if (node != null) { + rightSide = node; + q.Enqueue(node.left); + q.Enqueue(node.right); + } + } + if (rightSide != null) { + res.Add(rightSide.val); + } + } + return res; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func rightSideView(root *TreeNode) []int { + if root == nil { + return []int{} + } + + res := []int{} + q := []*TreeNode{root} + + for len(q) > 0 { + rightSide := 0 + qLen := len(q) + + for i := 0; i < qLen; i++ { + node := q[0] + q = q[1:] + + if node != nil { + rightSide = node.Val + if node.Left != nil { + q = append(q, node.Left) + } + if node.Right != nil { + q = append(q, node.Right) + } + } + } + res = append(res, rightSide) + } + + return res +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun rightSideView(root: TreeNode?): List { + if (root == null) return emptyList() + + val res = mutableListOf() + val q = ArrayDeque(listOf(root)) + + while (q.isNotEmpty()) { + var rightSide: TreeNode? = null + var qLen = q.size + + while (qLen > 0) { + val node = q.removeFirst() + if (node != null) { + rightSide = node + node.left?.let { q.add(it) } + node.right?.let { q.add(it) } + } + qLen-- + } + rightSide?.let { res.add(it.`val`) } + } + + return res + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func rightSideView(_ root: TreeNode?) -> [Int] { + var res = [Int]() + var q = Deque() + q.append(root) + + while !q.isEmpty { + var rightSide: TreeNode? + let qLen = q.count + + for _ in 0.. List[List[int]]: + cols = defaultdict(list) + que = deque([(root, 0)]) + + while que: + node, pos = que.popleft() + if node: + cols[pos].append(node.val) + que.append((node.left, pos - 1)) + que.append((node.right, pos + 1)) + + return [cols[x] for x in sorted(cols)] +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List> verticalOrder(TreeNode root) { + if (root == null) return new ArrayList<>(); + + Map> cols = new TreeMap<>(); + Queue> queue = new LinkedList<>(); + queue.offer(new Pair<>(root, 0)); + + while (!queue.isEmpty()) { + Pair p = queue.poll(); + TreeNode node = p.getKey(); + int pos = p.getValue(); + + cols.computeIfAbsent(pos, k -> new ArrayList<>()).add(node.val); + + if (node.left != null) queue.offer(new Pair<>(node.left, pos - 1)); + if (node.right != null) queue.offer(new Pair<>(node.right, pos + 1)); + } + + return new ArrayList<>(cols.values()); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector> verticalOrder(TreeNode* root) { + if (!root) return {}; + map> cols; + queue> q; + q.push({root, 0}); + + while (!q.empty()) { + auto [node, pos] = q.front(); q.pop(); + cols[pos].push_back(node->val); + if (node->left) q.push({node->left, pos - 1}); + if (node->right) q.push({node->right, pos + 1}); + } + + vector> res; + for (auto& [_, vec] : cols) + res.push_back(vec); + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[][]} + */ + verticalOrder(root) { + if (!root) return []; + + const cols = new Map(); + const queue = new Queue([[root, 0]]); + + while (!queue.isEmpty()) { + const [node, pos] = queue.pop(); + if (!cols.has(pos)) cols.set(pos, []); + cols.get(pos).push(node.val); + + if (node.left) queue.push([node.left, pos - 1]); + if (node.right) queue.push([node.right, pos + 1]); + } + + const sortedKeys = Array.from(cols.keys()).sort((a, b) => a - b); + return sortedKeys.map(k => cols.get(k)); + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List> VerticalOrder(TreeNode root) { + if (root == null) return new List>(); + + var cols = new SortedDictionary>(); + var queue = new Queue<(TreeNode node, int pos)>(); + queue.Enqueue((root, 0)); + + while (queue.Count > 0) { + var (node, pos) = queue.Dequeue(); + + if (!cols.ContainsKey(pos)) + cols[pos] = new List(); + cols[pos].Add(node.val); + + if (node.left != null) queue.Enqueue((node.left, pos - 1)); + if (node.right != null) queue.Enqueue((node.right, pos + 1)); + } + + return cols.Values.ToList>(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Depth First Search + Sorting + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def verticalOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + cols = defaultdict(list) + + def dfs(node, row, col): + if not node: + return + cols[col].append((row, node.val)) + dfs(node.left, row + 1, col - 1) + dfs(node.right, row + 1, col + 1) + + dfs(root, 0, 0) + + res = [] + for col in sorted(cols): + col_vals = sorted(cols[col], key=lambda x: x[0]) + res.append([val for _, val in col_vals]) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private Map> cols = new TreeMap<>(); + + public List> verticalOrder(TreeNode root) { + dfs(root, 0, 0); + List> res = new ArrayList<>(); + + for (List list : cols.values()) { + list.sort(Comparator.comparingInt(a -> a[0])); + List colVals = new ArrayList<>(); + for (int[] p : list) colVals.add(p[1]); + res.add(colVals); + } + + return res; + } + + private void dfs(TreeNode node, int row, int col) { + if (node == null) return; + cols.computeIfAbsent(col, k -> new ArrayList<>()).add(new int[]{row, node.val}); + dfs(node.left, row + 1, col - 1); + dfs(node.right, row + 1, col + 1); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { + map>> cols; + + void dfs(TreeNode* node, int row, int col) { + if (!node) return; + cols[col].push_back({row, node->val}); + dfs(node->left, row + 1, col - 1); + dfs(node->right, row + 1, col + 1); + } + +public: + vector> verticalOrder(TreeNode* root) { + dfs(root, 0, 0); + vector> res; + + for (auto& [col, vec] : cols) { + stable_sort(vec.begin(), vec.end(), + [](const pair& a, const pair& b) { + return a.first < b.first; // sort ONLY by row + }); + + vector colVals; + for (auto& [_, val] : vec) + colVals.push_back(val); + res.push_back(colVals); + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[][]} + */ + verticalOrder(root) { + const cols = new Map(); + + const dfs = (node, row, col) => { + if (!node) return; + if (!cols.has(col)) cols.set(col, []); + cols.get(col).push([row, node.val]); + dfs(node.left, row + 1, col - 1); + dfs(node.right, row + 1, col + 1); + }; + + dfs(root, 0, 0); + + const sortedCols = Array.from(cols.entries()).sort((a, b) => a[0] - b[0]); + return sortedCols.map(([_, vec]) => + vec.sort((a, b) => a[0] - b[0]).map(([_, val]) => val) + ); + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private SortedDictionary> cols = new(); + + public List> VerticalOrder(TreeNode root) { + DFS(root, 0, 0); + + List> res = new(); + foreach (var entry in cols) { + var list = entry.Value.OrderBy(x => x.Item1).Select(x => x.Item2).ToList(); + res.Add(list); + } + + return res; + } + + private void DFS(TreeNode node, int row, int col) { + if (node == null) return; + if (!cols.ContainsKey(col)) cols[col] = new List<(int, int)>(); + cols[col].Add((row, node.val)); + DFS(node.left, row + 1, col - 1); + DFS(node.right, row + 1, col + 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n \log n)$ + +--- + +## 3. Breadth First Search (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def verticalOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + if not root: + return [] + + cols = defaultdict(list) + queue = deque([(root, 0)]) + minCol = maxCol = 0 + + while queue: + node, col = queue.popleft() + cols[col].append(node.val) + minCol = min(minCol, col) + maxCol = max(maxCol, col) + + if node.left: + queue.append((node.left, col - 1)) + if node.right: + queue.append((node.right, col + 1)) + + return [cols[c] for c in range(minCol, maxCol + 1)] +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List> verticalOrder(TreeNode root) { + if (root == null) return new ArrayList<>(); + + Map> cols = new HashMap<>(); + Queue> queue = new LinkedList<>(); + queue.offer(new Pair<>(root, 0)); + int minCol = 0, maxCol = 0; + + while (!queue.isEmpty()) { + Pair p = queue.poll(); + TreeNode node = p.getKey(); + int col = p.getValue(); + + cols.computeIfAbsent(col, x -> new ArrayList<>()).add(node.val); + minCol = Math.min(minCol, col); + maxCol = Math.max(maxCol, col); + + if (node.left != null) queue.offer(new Pair<>(node.left, col - 1)); + if (node.right != null) queue.offer(new Pair<>(node.right, col + 1)); + } + + List> res = new ArrayList<>(); + for (int c = minCol; c <= maxCol; c++) { + res.add(cols.get(c)); + } + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector> verticalOrder(TreeNode* root) { + if (!root) return {}; + + unordered_map> cols; + queue> q; + q.push({root, 0}); + int minCol = 0, maxCol = 0; + + while (!q.empty()) { + auto [node, col] = q.front(); q.pop(); + cols[col].push_back(node->val); + minCol = min(minCol, col); + maxCol = max(maxCol, col); + + if (node->left) q.push({node->left, col - 1}); + if (node->right) q.push({node->right, col + 1}); + } + + vector> res; + for (int c = minCol; c <= maxCol; ++c) + res.push_back(cols[c]); + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[][]} + */ + verticalOrder(root) { + if (!root) return []; + + const cols = new Map(); + const queue = new Queue([[root, 0]]); + let minCol = 0, maxCol = 0; + + while (!queue.isEmpty()) { + const [node, col] = queue.pop(); + if (!cols.has(col)) cols.set(col, []); + cols.get(col).push(node.val); + minCol = Math.min(minCol, col); + maxCol = Math.max(maxCol, col); + + if (node.left) queue.push([node.left, col - 1]); + if (node.right) queue.push([node.right, col + 1]); + } + + const res = []; + for (let c = minCol; c <= maxCol; c++) { + res.push(cols.get(c)); + } + return res; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List> VerticalOrder(TreeNode root) { + if (root == null) return new List>(); + + Dictionary> cols = new(); + Queue<(TreeNode node, int col)> queue = new(); + queue.Enqueue((root, 0)); + int minCol = 0, maxCol = 0; + + while (queue.Count > 0) { + var (node, col) = queue.Dequeue(); + if (!cols.ContainsKey(col)) + cols[col] = new List(); + cols[col].Add(node.val); + minCol = Math.Min(minCol, col); + maxCol = Math.Max(maxCol, col); + + if (node.left != null) queue.Enqueue((node.left, col - 1)); + if (node.right != null) queue.Enqueue((node.right, col + 1)); + } + + var res = new List>(); + for (int c = minCol; c <= maxCol; c++) { + res.Add(cols[c]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Depth First Search (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def verticalOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + if not root: + return [] + + cols = defaultdict(list) + minCol = maxCol = 0 + + def dfs(node, row, col): + nonlocal minCol, maxCol + if not node: + return + cols[col].append((row, node.val)) + minCol = min(minCol, col) + maxCol = max(maxCol, col) + dfs(node.left, row + 1, col - 1) + dfs(node.right, row + 1, col + 1) + + dfs(root, 0, 0) + + res = [] + for c in range(minCol, maxCol + 1): + # sort by row only + col_vals = sorted(cols[c], key=lambda x: x[0]) + res.append([val for _, val in col_vals]) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private Map> cols = new HashMap<>(); + private int minCol = 0, maxCol = 0; + + public List> verticalOrder(TreeNode root) { + if (root == null) return new ArrayList<>(); + dfs(root, 0, 0); + + List> res = new ArrayList<>(); + for (int c = minCol; c <= maxCol; c++) { + List list = cols.getOrDefault(c, new ArrayList<>()); + list.sort(Comparator.comparingInt(a -> a[0])); // sort by row + List colVals = new ArrayList<>(); + for (int[] p : list) colVals.add(p[1]); + res.add(colVals); + } + return res; + } + + private void dfs(TreeNode node, int row, int col) { + if (node == null) return; + cols.computeIfAbsent(col, k -> new ArrayList<>()).add(new int[]{row, node.val}); + minCol = Math.min(minCol, col); + maxCol = Math.max(maxCol, col); + dfs(node.left, row + 1, col - 1); + dfs(node.right, row + 1, col + 1); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { + unordered_map>> cols; + int minCol = 0, maxCol = 0; + + void dfs(TreeNode* node, int row, int col) { + if (!node) return; + cols[col].emplace_back(row, node->val); + minCol = min(minCol, col); + maxCol = max(maxCol, col); + dfs(node->left, row + 1, col - 1); + dfs(node->right, row + 1, col + 1); + } + +public: + vector> verticalOrder(TreeNode* root) { + if (!root) return {}; + dfs(root, 0, 0); + vector> res; + + for (int c = minCol; c <= maxCol; ++c) { + auto& vec = cols[c]; + stable_sort(vec.begin(), vec.end(), + [](const pair& a, const pair& b) { + return a.first < b.first; // sort by row only + }); + vector colVals; + for (auto& [_, val] : vec) + colVals.push_back(val); + res.push_back(colVals); + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[][]} + */ + verticalOrder(root) { + if (!root) return []; + + const cols = new Map(); + let minCol = 0, maxCol = 0; + + const dfs = (node, row, col) => { + if (!node) return; + if (!cols.has(col)) cols.set(col, []); + cols.get(col).push([row, node.val]); + minCol = Math.min(minCol, col); + maxCol = Math.max(maxCol, col); + dfs(node.left, row + 1, col - 1); + dfs(node.right, row + 1, col + 1); + }; + + dfs(root, 0, 0); + + const res = []; + for (let c = minCol; c <= maxCol; c++) { + let entries = cols.get(c) || []; + entries.sort((a, b) => a[0] - b[0]); // sort by row only + res.push(entries.map(([_, val]) => val)); + } + + return res; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private Dictionary> cols = new(); + private int minCol = 0, maxCol = 0; + + public List> VerticalOrder(TreeNode root) { + if (root == null) return new List>(); + DFS(root, 0, 0); + var res = new List>(); + + for (int c = minCol; c <= maxCol; c++) { + var list = cols.ContainsKey(c) ? cols[c] : new List<(int, int)>(); + list.Sort((a, b) => a.Item1.CompareTo(b.Item1)); // sort by row + res.Add(list.Select(p => p.Item2).ToList()); + } + + return res; + } + + private void DFS(TreeNode node, int row, int col) { + if (node == null) return; + if (!cols.ContainsKey(col)) cols[col] = new List<(int, int)>(); + cols[col].Add((row, node.val)); + minCol = Math.Min(minCol, col); + maxCol = Math.Max(maxCol, col); + DFS(node.left, row + 1, col - 1); + DFS(node.right, row + 1, col + 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(w * h \log h)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of nodes, $h$ is the height of the tree (i.e. maximum number of nodes in any vertical line of the tree), and $w$ is the width of the tree (i.e. maximum number of nodes in any of the levels of the tree). \ No newline at end of file diff --git a/articles/binary-tree-zigzag-level-order-traversal.md b/articles/binary-tree-zigzag-level-order-traversal.md new file mode 100644 index 000000000..d41aac9cd --- /dev/null +++ b/articles/binary-tree-zigzag-level-order-traversal.md @@ -0,0 +1,647 @@ +## 1. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + res = [] + q = deque([root] if root else []) + while q: + level = [] + for i in range(len(q)): + node = q.popleft() + level.append(node.val) + if node.left: + q.append(node.left) + if node.right: + q.append(node.right) + if len(res) % 2: + level.reverse() + res.append(level) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List> zigzagLevelOrder(TreeNode root) { + List> res = new ArrayList<>(); + if (root == null) return res; + + Queue q = new LinkedList<>(); + q.offer(root); + + while (!q.isEmpty()) { + List level = new ArrayList<>(); + for (int i = q.size(); i > 0; i--) { + TreeNode node = q.poll(); + level.add(node.val); + if (node.left != null) q.offer(node.left); + if (node.right != null) q.offer(node.right); + } + if (res.size() % 2 != 0) Collections.reverse(level); + res.add(level); + } + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector> zigzagLevelOrder(TreeNode* root) { + vector> res; + if (!root) return res; + + queue q; + q.push(root); + + while (!q.empty()) { + vector level; + for (int i = q.size(); i > 0; i--) { + TreeNode* node = q.front(); + q.pop(); + level.push_back(node->val); + if (node->left) q.push(node->left); + if (node->right) q.push(node->right); + } + if (res.size() % 2) reverse(level.begin(), level.end()); + res.push_back(level); + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[][]} + */ + zigzagLevelOrder(root) { + const res = []; + if (!root) return res; + + const queue = new Queue([root]); + + while (!queue.isEmpty()) { + const level = []; + for (let i = queue.size(); i > 0; i--) { + const node = queue.pop(); + level.push(node.val); + if (node.left) queue.push(node.left); + if (node.right) queue.push(node.right); + } + if (res.length % 2 !== 0) level.reverse(); + res.push(level); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Breadth First Search (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + res = [] + q = deque([root] if root else []) + while q: + size = len(q) + level = [0] * size + for i in range(size): + node = q.popleft() + idx = size - i - 1 if len(res) % 2 else i + level[idx] = node.val + if node.left: + q.append(node.left) + if node.right: + q.append(node.right) + res.append(level) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List> zigzagLevelOrder(TreeNode root) { + List> res = new ArrayList<>(); + if (root == null) return res; + + Queue q = new LinkedList<>(); + q.offer(root); + + while (!q.isEmpty()) { + int size = q.size(); + Integer[] level = new Integer[size]; + for (int i = 0; i < size; i++) { + TreeNode node = q.poll(); + int idx = (res.size() % 2 == 0) ? i : size - i - 1; + level[idx] = node.val; + if (node.left != null) q.offer(node.left); + if (node.right != null) q.offer(node.right); + } + res.add(Arrays.asList(level)); + } + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector> zigzagLevelOrder(TreeNode* root) { + vector> res; + if (!root) return res; + + queue q; + q.push(root); + + while (!q.empty()) { + int size = q.size(); + vector level(size); + for (int i = 0; i < size; ++i) { + TreeNode* node = q.front(); + q.pop(); + int idx = (res.size() % 2 == 0) ? i : size - i - 1; + level[idx] = node->val; + if (node->left) q.push(node->left); + if (node->right) q.push(node->right); + } + res.push_back(level); + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[][]} + */ + zigzagLevelOrder(root) { + const res = []; + if (!root) return res; + + const q = new Queue([root]); + + while (!q.isEmpty()) { + const size = q.size(); + const level = Array(size).fill(0); + + for (let i = 0; i < size; i++) { + const node = q.pop(); + const idx = res.length % 2 === 0 ? i : size - i - 1; + level[idx] = node.val; + if (node.left) q.push(node.left); + if (node.right) q.push(node.right); + } + res.push(level); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + res = [] + + def dfs(node, depth): + if not node: + return + if depth == len(res): + res.append([]) + res[depth].append(node.val) + dfs(node.left, depth + 1) + dfs(node.right, depth + 1) + + dfs(root, 0) + for i, level in enumerate(res): + if i & 1: + level.reverse() + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List> zigzagLevelOrder(TreeNode root) { + List> res = new ArrayList<>(); + dfs(root, 0, res); + for (int i = 0; i < res.size(); i++) { + if ((i & 1) == 1) { + Collections.reverse(res.get(i)); + } + } + return res; + } + + private void dfs(TreeNode node, int depth, List> res) { + if (node == null) return; + if (depth == res.size()) { + res.add(new ArrayList<>()); + } + res.get(depth).add(node.val); + dfs(node.left, depth + 1, res); + dfs(node.right, depth + 1, res); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector> zigzagLevelOrder(TreeNode* root) { + vector> res; + dfs(root, 0, res); + for (int i = 0; i < res.size(); ++i) { + if (i & 1) { + reverse(res[i].begin(), res[i].end()); + } + } + return res; + } + +private: + void dfs(TreeNode* node, int depth, vector>& res) { + if (!node) return; + if (depth == res.size()) { + res.push_back({}); + } + res[depth].push_back(node->val); + dfs(node->left, depth + 1, res); + dfs(node->right, depth + 1, res); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[][]} + */ + zigzagLevelOrder(root) { + const res = []; + const dfs = (node, depth) => { + if (!node) return; + if (depth === res.length) res.push([]); + res[depth].push(node.val); + dfs(node.left, depth + 1); + dfs(node.right, depth + 1); + }; + + dfs(root, 0); + for (let i = 0; i < res.length; i++) { + if (i % 2 === 1) res[i].reverse(); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + if not root: + return [] + + res = [] + stack = [(root, 0)] + + while stack: + node, depth = stack.pop() + if depth == len(res): + res.append([]) + + res[depth].append(node.val) + + if node.right: + stack.append((node.right, depth + 1)) + if node.left: + stack.append((node.left, depth + 1)) + + for i in range(len(res)): + if i % 2 == 1: + res[i].reverse() + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List> zigzagLevelOrder(TreeNode root) { + if (root == null) return new ArrayList<>(); + + List> res = new ArrayList<>(); + Stack stack = new Stack<>(); + stack.push(new Pair(root, 0)); + + while (!stack.isEmpty()) { + Pair current = stack.pop(); + TreeNode node = current.getKey(); + int depth = current.getValue(); + + if (depth == res.size()) { + res.add(new ArrayList<>()); + } + res.get(depth).add(node.val); + + if (node.right != null) stack.push(new Pair<>(node.right, depth + 1)); + if (node.left != null) stack.push(new Pair<>(node.left, depth + 1)); + } + + for (int i = 0; i < res.size(); i++) { + if (i % 2 == 1) { + Collections.reverse(res.get(i)); + } + } + + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector> zigzagLevelOrder(TreeNode* root) { + if (!root) return {}; + + vector> res; + stack> s; + s.push({root, 0}); + + while (!s.empty()) { + auto [node, depth] = s.top(); + s.pop(); + + if (depth == res.size()) { + res.push_back({}); + } + res[depth].push_back(node->val); + + if (node->right) s.push({node->right, depth + 1}); + if (node->left) s.push({node->left, depth + 1}); + } + + for (int i = 0; i < res.size(); i++) { + if (i % 2 == 1) { + reverse(res[i].begin(), res[i].end()); + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[][]} + */ + zigzagLevelOrder(root) { + if (!root) return []; + + const res = []; + const stack = [[root, 0]]; + + while (stack.length) { + const [node, depth] = stack.pop(); + + if (depth === res.length) res.push([]); + res[depth].push(node.val); + + if (node.right) stack.push([node.right, depth + 1]); + if (node.left) stack.push([node.left, depth + 1]); + } + + for (let i = 0; i < res.length; i++) { + if (i % 2 === 1) res[i].reverse(); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/bitwise-and-of-numbers-range.md b/articles/bitwise-and-of-numbers-range.md new file mode 100644 index 000000000..6c5ab4521 --- /dev/null +++ b/articles/bitwise-and-of-numbers-range.md @@ -0,0 +1,292 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def rangeBitwiseAnd(self, left: int, right: int) -> int: + res = left + for i in range(left + 1, right + 1): + res &= i + return res +``` + +```java +public class Solution { + public int rangeBitwiseAnd(int left, int right) { + int res = left; + for (int i = left + 1; i <= right; i++) { + res &= i; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int rangeBitwiseAnd(int left, int right) { + int res = left; + for (int i = left + 1; i <= right; i++) { + res &= i; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + rangeBitwiseAnd(left, right) { + let res = left; + for (let i = left + 1; i <= right; i++) { + res &= i; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Bit Manipulation - I + +::tabs-start + +```python +class Solution: + def rangeBitwiseAnd(self, left: int, right: int) -> int: + res = 0 + for i in range(32): + bit = (left >> i) & 1 + if not bit: + continue + + remain = left % (1 << (i + 1)) + diff = (1 << (i + 1)) - remain + if right - left < diff: + res |= (1 << i) + + return res +``` + +```java +public class Solution { + public int rangeBitwiseAnd(int left, int right) { + int res = 0; + for (int i = 0; i < 32; i++) { + int bit = (left >> i) & 1; + if (bit == 0) { + continue; + } + + int remain = left % (1 << (i + 1)); + int diff = (1 << (i + 1)) - remain; + if (right - left < diff) { + res |= (1 << i); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int rangeBitwiseAnd(int left, int right) { + int res = 0; + for (int i = 0; i < 32; i++) { + int bit = (left >> i) & 1; + if (!bit) { + continue; + } + + int remain = left % (1 << (i + 1)); + uint diff = (1ul << (i + 1)) - remain; + if (right - left < diff) { + res |= (1 << i); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + rangeBitwiseAnd(left, right) { + let res = 0; + for (let i = 0; i < 32; i++) { + const bit = (left >> i) & 1; + if (!bit) { + continue; + } + const next = Math.pow(2, i + 1); + const remain = left % next; + const diff = next - remain; + if (right - left < diff) { + res |= (1 << i); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ since we iterate $32$ times. +* Space complexity: $O(1)$ + +--- + +## 3. Bit Manipulation - II + +::tabs-start + +```python +class Solution: + def rangeBitwiseAnd(self, left: int, right: int) -> int: + i = 0 + while left != right: + left >>= 1 + right >>= 1 + i += 1 + return left << i +``` + +```java +public class Solution { + public int rangeBitwiseAnd(int left, int right) { + int i = 0; + while (left != right) { + left >>= 1; + right >>= 1; + i++; + } + return left << i; + } +} +``` + +```cpp +class Solution { +public: + int rangeBitwiseAnd(int left, int right) { + int i = 0; + while (left != right) { + left >>= 1; + right >>= 1; + i++; + } + return left << i; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + rangeBitwiseAnd(left, right) { + let i = 0; + while (left !== right) { + left >>= 1; + right >>= 1; + i++; + } + return left << i; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 4. Bit Manipulation - III + +::tabs-start + +```python +class Solution: + def rangeBitwiseAnd(self, left: int, right: int) -> int: + while left < right: + right &= right - 1 + return right +``` + +```java +public class Solution { + public int rangeBitwiseAnd(int left, int right) { + while (left < right) { + right &= (right - 1); + } + return right; + } +} +``` + +```cpp +class Solution { +public: + int rangeBitwiseAnd(int left, int right) { + while (left < right) { + right &= (right - 1); + } + return right; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + rangeBitwiseAnd(left, right) { + while (left < right) { + right &= (right - 1); + } + return right; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/boats-to-save-people.md b/articles/boats-to-save-people.md new file mode 100644 index 000000000..ce0498e89 --- /dev/null +++ b/articles/boats-to-save-people.md @@ -0,0 +1,280 @@ +## 1. Sorting + Two Pointers + +::tabs-start + +```python +class Solution: + def numRescueBoats(self, people: List[int], limit: int) -> int: + people.sort() + res, l, r = 0, 0, len(people) - 1 + while l <= r: + remain = limit - people[r] + r -= 1 + res += 1 + if l <= r and remain >= people[l]: + l += 1 + return res +``` + +```java +public class Solution { + public int numRescueBoats(int[] people, int limit) { + Arrays.sort(people); + int res = 0, l = 0, r = people.length - 1; + while (l <= r) { + int remain = limit - people[r--]; + res++; + if (l <= r && remain >= people[l]) { + l++; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numRescueBoats(vector& people, int limit) { + sort(people.begin(), people.end()); + int res = 0, l = 0, r = people.size() - 1; + while (l <= r) { + int remain = limit - people[r--]; + res++; + if (l <= r && remain >= people[l]) { + l++; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} people + * @param {number} limit + * @return {number} + */ + numRescueBoats(people, limit) { + people.sort((a, b) => a - b); + let res = 0, l = 0, r = people.length - 1; + while (l <= r) { + let remain = limit - people[r--]; + res++; + if (l <= r && remain >= people[l]) { + l++; + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int NumRescueBoats(int[] people, int limit) { + Array.Sort(people); + int res = 0, l = 0, r = people.Length - 1; + + while (l <= r) { + int remain = limit - people[r]; + r--; + res++; + + if (l <= r && remain >= people[l]) { + l++; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Counting Sort + +::tabs-start + +```python +class Solution: + def numRescueBoats(self, people: List[int], limit: int) -> int: + m = max(people) + count = [0] * (m + 1) + for p in people: + count[p] += 1 + + idx, i = 0, 1 + while idx < len(people): + while count[i] == 0: + i += 1 + people[idx] = i + count[i] -= 1 + idx += 1 + + res, l, r = 0, 0, len(people) - 1 + while l <= r: + remain = limit - people[r] + r -= 1 + res += 1 + if l <= r and remain >= people[l]: + l += 1 + return res +``` + +```java +public class Solution { + public int numRescueBoats(int[] people, int limit) { + int m = Arrays.stream(people).max().getAsInt(); + int[] count = new int[m + 1]; + for (int p : people) { + count[p]++; + } + + int idx = 0, i = 1; + while (idx < people.length) { + while (count[i] == 0) { + i++; + } + people[idx++] = i; + count[i]--; + } + + int res = 0, l = 0, r = people.length - 1; + while (l <= r) { + int remain = limit - people[r--]; + res++; + if (l <= r && remain >= people[l]) { + l++; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numRescueBoats(vector& people, int limit) { + int m = *max_element(people.begin(), people.end()); + vector count(m + 1, 0); + for (int p : people) { + count[p]++; + } + + int idx = 0, i = 1; + while (idx < people.size()) { + while (count[i] == 0) { + i++; + } + people[idx++] = i; + count[i]--; + } + + int res = 0, l = 0, r = people.size() - 1; + while (l <= r) { + int remain = limit - people[r--]; + res++; + if (l <= r && remain >= people[l]) { + l++; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} people + * @param {number} limit + * @return {number} + */ + numRescueBoats(people, limit) { + const m = Math.max(...people); + const count = new Array(m + 1).fill(0); + for (const p of people) { + count[p]++; + } + + let idx = 0, i = 1; + while (idx < people.length) { + while (count[i] === 0) { + i++; + } + people[idx++] = i; + count[i]--; + } + + let res = 0, l = 0, r = people.length - 1; + while (l <= r) { + const remain = limit - people[r--]; + res++; + if (l <= r && remain >= people[l]) { + l++; + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int NumRescueBoats(int[] people, int limit) { + int m = 0; + foreach (int p in people) { + m = Math.Max(m, p); + } + + int[] count = new int[m + 1]; + foreach (int p in people) { + count[p]++; + } + + int idx = 0, iVal = 1; + while (idx < people.Length) { + while (count[iVal] == 0) { + iVal++; + } + people[idx] = iVal; + count[iVal]--; + idx++; + } + + int res = 0, l = 0, r = people.Length - 1; + while (l <= r) { + int remain = limit - people[r]; + r--; + res++; + if (l <= r && remain >= people[l]) { + l++; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(m)$ + +> Where $n$ is the size of the input array and $m$ is the maximum value in the array. \ No newline at end of file diff --git a/articles/brick-wall.md b/articles/brick-wall.md new file mode 100644 index 000000000..991f98ce3 --- /dev/null +++ b/articles/brick-wall.md @@ -0,0 +1,245 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def leastBricks(self, wall: List[List[int]]) -> int: + n = len(wall) + m = 0 + for brick in wall[0]: + m += brick + + gaps = [[] for _ in range(n)] + for i in range(n): + gap = 0 + for brick in wall[i]: + gap += brick + gaps[i].append(gap) + + res = n + for line in range(1, m): + cuts = 0 + for i in range(n): + if line not in gaps[i]: + cuts += 1 + + res = min(res, cuts) + return res +``` + +```java +public class Solution { + public int leastBricks(List> wall) { + int n = wall.size(); + int m = 0; + for (int brick : wall.get(0)) { + m += brick; + } + + List> gaps = new ArrayList<>(); + for (int i = 0; i < n; i++) { + gaps.add(new ArrayList<>()); + int gap = 0; + for (int brick : wall.get(i)) { + gap += brick; + gaps.get(i).add(gap); + } + } + + int res = n; + for (int line = 1; line < m; line++) { + int cuts = 0; + for (int i = 0; i < n; i++) { + if (!gaps.get(i).contains(line)) { + cuts++; + } + } + res = Math.min(res, cuts); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int leastBricks(vector>& wall) { + int n = wall.size(); + int m = 0; + for (int brick : wall[0]) { + m += brick; + } + + vector> gaps(n); + for (int i = 0; i < n; i++) { + int gap = 0; + for (int brick : wall[i]) { + gap += brick; + gaps[i].push_back(gap); + } + } + + int res = n; + for (int line = 1; line < m; line++) { + int cuts = 0; + for (int i = 0; i < n; i++) { + if (find(gaps[i].begin(), gaps[i].end(), line) == gaps[i].end()) { + cuts++; + } + } + res = min(res, cuts); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} wall + * @return {number} + */ + leastBricks(wall) { + const n = wall.length; + let m = 0; + for (const brick of wall[0]) { + m += brick; + } + + const gaps = Array.from({ length: n }, () => []); + for (let i = 0; i < n; i++) { + let gap = 0; + for (const brick of wall[i]) { + gap += brick; + gaps[i].push(gap); + } + } + + let res = n; + for (let line = 1; line < m; line++) { + let cuts = 0; + for (let i = 0; i < n; i++) { + if (!gaps[i].includes(line)) { + cuts++; + } + } + res = Math.min(res, cuts); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n * g)$ +* Space complexity: $O(n * g)$ + +> Where $m$ is the sum of widths of the bricks in the first row, $n$ is the number of rows and $g$ is the average number of gaps in each row. + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def leastBricks(self, wall: List[List[int]]) -> int: + countGap = {0: 0} + + for r in wall: + total = 0 + for i in range(len(r) - 1): + total += r[i] + countGap[total] = 1 + countGap.get(total, 0) + + return len(wall) - max(countGap.values()) +``` + +```java +public class Solution { + public int leastBricks(List> wall) { + HashMap countGap = new HashMap<>(); + countGap.put(0, 0); + + for (List row : wall) { + int total = 0; + for (int i = 0; i < row.size() - 1; i++) { + total += row.get(i); + countGap.put(total, countGap.getOrDefault(total, 0) + 1); + } + } + + int maxGaps = 0; + for (int count : countGap.values()) { + maxGaps = Math.max(maxGaps, count); + } + + return wall.size() - maxGaps; + } +} +``` + +```cpp +class Solution { +public: + int leastBricks(vector>& wall) { + unordered_map countGap; + countGap[0] = 0; + + for (const auto& row : wall) { + int total = 0; + for (size_t i = 0; i < row.size() - 1; ++i) { + total += row[i]; + countGap[total]++; + } + } + + int maxGaps = 0; + for (const auto& [key, value] : countGap) { + maxGaps = max(maxGaps, value); + } + + return wall.size() - maxGaps; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} wall + * @return {number} + */ + leastBricks(wall) { + const countGap = new Map(); + countGap.set(0, 0); + for (const row of wall) { + let total = 0; + for (let i = 0; i < row.length - 1; i++) { + total += row[i]; + countGap.set(total, (countGap.get(total) || 0) + 1); + } + } + return wall.length - Math.max(...countGap.values()); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N)$ +* Space complexity: $O(g)$ + +> Where $N$ is the total number of bricks in the wall and $g$ is the total number of gaps in all the rows. \ No newline at end of file diff --git a/articles/build-a-matrix-with-conditions.md b/articles/build-a-matrix-with-conditions.md new file mode 100644 index 000000000..cd7d0bfd9 --- /dev/null +++ b/articles/build-a-matrix-with-conditions.md @@ -0,0 +1,526 @@ +## 1. Topological Sort (DFS) + +::tabs-start + +```python +class Solution: + def buildMatrix(self, k: int, rowConditions: List[List[int]], colConditions: List[List[int]]) -> List[List[int]]: + def dfs(src, adj, visit, path, order): + if src in path: + return False + if src in visit: + return True + visit.add(src) + path.add(src) + for nei in adj[src]: + if not dfs(nei, adj, visit, path, order): + return False + path.remove(src) + order.append(src) + return True + + def topo_sort(edges): + adj = defaultdict(list) + for src, dst in edges: + adj[src].append(dst) + + visit, path = set(), set() + order = [] + for src in range(1, k + 1): + if src not in visit: + if not dfs(src, adj, visit, path, order): + return [] + return order[::-1] + + row_order = topo_sort(rowConditions) + if not row_order: return [] + + col_order = topo_sort(colConditions) + if not col_order: return [] + + val_to_row = {num: i for i, num in enumerate(row_order)} + val_to_col = {num: i for i, num in enumerate(col_order)} + res = [[0] * k for _ in range(k)] + for num in range(1, k + 1): + r, c = val_to_row[num], val_to_col[num] + res[r][c] = num + + return res +``` + +```java +public class Solution { + private Set visit; + private Set path; + private List order; + + public int[][] buildMatrix(int k, int[][] rowConditions, int[][] colConditions) { + int[] rowOrder = topoSort(k, rowConditions); + if (rowOrder == null) return new int[0][0]; + int[] colOrder = topoSort(k, colConditions); + if (colOrder == null) return new int[0][0]; + + Map valToRow = new HashMap<>(); + for (int i = 0; i < rowOrder.length; i++) { + valToRow.put(rowOrder[i], i); + } + Map valToCol = new HashMap<>(); + for (int i = 0; i < colOrder.length; i++) { + valToCol.put(colOrder[i], i); + } + + int[][] res = new int[k][k]; + for (int num = 1; num <= k; num++) { + int r = valToRow.get(num); + int c = valToCol.get(num); + res[r][c] = num; + } + return res; + } + + private int[] topoSort(int k, int[][] edges) { + Map> adj = new HashMap<>(); + for (int i = 1; i <= k; i++) { + adj.put(i, new ArrayList<>()); + } + for (int[] edge : edges) { + adj.get(edge[0]).add(edge[1]); + } + + visit = new HashSet<>(); + path = new HashSet<>(); + order = new ArrayList<>(); + + for (int i = 1; i <= k; i++) { + if (!visit.contains(i)) { + if (!dfs(i, adj)) { + return null; + } + } + } + + Collections.reverse(order); + return order.stream().mapToInt(i -> i).toArray(); + } + + private boolean dfs(int src, Map> adj) { + if (path.contains(src)) return false; + if (visit.contains(src)) return true; + + visit.add(src); + path.add(src); + for (int nei : adj.get(src)) { + if (!dfs(nei, adj)) { + return false; + } + } + path.remove(src); + order.add(src); + return true; + } +} +``` + +```cpp +class Solution { +public: + vector> buildMatrix(int k, vector>& rowConditions, vector>& colConditions) { + vector rowOrder = topoSort(k, rowConditions); + if (rowOrder.empty()) return {}; + vector colOrder = topoSort(k, colConditions); + if (colOrder.empty()) return {}; + + unordered_map valToRow, valToCol; + for (int i = 0; i < rowOrder.size(); i++) { + valToRow[rowOrder[i]] = i; + } + for (int i = 0; i < colOrder.size(); i++) { + valToCol[colOrder[i]] = i; + } + + vector> res(k, vector(k, 0)); + for (int num = 1; num <= k; num++) { + int r = valToRow[num]; + int c = valToCol[num]; + res[r][c] = num; + } + return res; + } + +private: + unordered_set visit; + unordered_set path; + vector order; + + vector topoSort(int k, vector>& edges) { + unordered_map> adj; + for (int i = 1; i <= k; i++) { + adj[i] = {}; + } + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + } + + visit.clear(); + path.clear(); + order.clear(); + for (int i = 1; i <= k; i++) { + if (visit.find(i) == visit.end()) { + if (!dfs(i, adj)) { + return {}; + } + } + } + + reverse(order.begin(), order.end()); + return order; + } + + bool dfs(int src, unordered_map>& adj) { + if (path.find(src) != path.end()) return false; + if (visit.find(src) != visit.end()) return true; + + visit.insert(src); + path.insert(src); + for (int nei : adj[src]) { + if (!dfs(nei, adj)) { + return false; + } + } + path.erase(src); + order.push_back(src); + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} k + * @param {number[][]} rowConditions + * @param {number[][]} colConditions + * @return {number[][]} + */ + buildMatrix(k, rowConditions, colConditions) { + const rowOrder = this.topoSort(k, rowConditions); + if (!rowOrder) return []; + const colOrder = this.topoSort(k, colConditions); + if (!colOrder) return []; + + const valToRow = {}; + rowOrder.forEach((num, i) => { + valToRow[num] = i; + }); + + const valToCol = {}; + colOrder.forEach((num, i) => { + valToCol[num] = i; + }); + + const res = Array.from({ length: k }, () => Array(k).fill(0)); + for (let num = 1; num <= k; num++) { + const r = valToRow[num]; + const c = valToCol[num]; + res[r][c] = num; + } + return res; + } + + /** + * @param {number} k + * @param {number[][]} edges + * @return {number[]} + */ + topoSort(k, edges) { + const adj = Array.from({ length: k + 1 }, () => []); + edges.forEach(([src, dst]) => { + adj[src].push(dst); + }); + + const visit = new Set(); + const path = new Set(); + const order = []; + + const dfs = (src) => { + if (path.has(src)) return false; + if (visit.has(src)) return true; + + visit.add(src); + path.add(src); + for (const nei of adj[src]) { + if (!dfs(nei)) { + return false; + } + } + path.delete(src); + order.push(src); + return true; + }; + + for (let src = 1; src <= k; src++) { + if (!visit.has(src)) { + if (!dfs(src)) { + return null; + } + } + } + return order.reverse(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k ^ 2 + n + m)$ +* Space complexity: + * $O(k + n + m)$ extra space. + * $O(k ^ 2)$ space for the output matrix. + +> Where $n$ is the size of the array $rowConditions$, $m$ is the size of the array $colConditions$, and $k$ is the size of the output matrix. + +--- + +## 2. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def buildMatrix(self, k: int, rowConditions: List[List[int]], colConditions: List[List[int]]) -> List[List[int]]: + def topo_sort(edges): + indegree = [0] * (k + 1) + adj = [[] for _ in range(k + 1)] + for u, v in edges: + adj[u].append(v) + indegree[v] += 1 + + order = [] + q = deque() + for i in range(1, k + 1): + if not indegree[i]: + q.append(i) + + while q: + node = q.popleft() + order.append(node) + for nei in adj[node]: + indegree[nei] -= 1 + if not indegree[nei]: + q.append(nei) + + return order + + row_order = topo_sort(rowConditions) + if len(row_order) != k: return [] + + col_order = topo_sort(colConditions) + if len(col_order) != k: return [] + + res = [[0] * k for _ in range(k)] + colIndex = [0] * (k + 1) + for i in range(k): + colIndex[col_order[i]] = i + + for i in range(k): + res[i][colIndex[row_order[i]]] = row_order[i] + return res +``` + +```java +public class Solution { + public int[][] buildMatrix(int k, int[][] rowConditions, int[][] colConditions) { + int[] rowOrder = topoSort(k, rowConditions); + if (rowOrder.length != k) return new int[0][0]; + + int[] colOrder = topoSort(k, colConditions); + if (colOrder.length != k) return new int[0][0]; + + int[][] res = new int[k][k]; + int[] colIndex = new int[k + 1]; + for (int i = 0; i < k; i++) { + colIndex[colOrder[i]] = i; + } + + for (int i = 0; i < k; i++) { + res[i][colIndex[rowOrder[i]]] = rowOrder[i]; + } + + return res; + } + + private int[] topoSort(int k, int[][] edges) { + int[] indegree = new int[k + 1]; + List> adj = new ArrayList<>(); + for (int i = 0; i <= k; i++) { + adj.add(new ArrayList<>()); + } + + for (int[] edge : edges) { + adj.get(edge[0]).add(edge[1]); + indegree[edge[1]]++; + } + + Queue queue = new LinkedList<>(); + int[] order = new int[k]; + int idx = 0; + for (int i = 1; i <= k; i++) { + if (indegree[i] == 0) { + queue.offer(i); + } + } + + while (!queue.isEmpty()) { + int node = queue.poll(); + order[idx++] = node; + for (int nei : adj.get(node)) { + indegree[nei]--; + if (indegree[nei] == 0) { + queue.offer(nei); + } + } + } + + if (idx != k) return new int[0]; + return order; + } +} +``` + +```cpp +class Solution { +public: + vector> buildMatrix(int k, vector>& rowConditions, vector>& colConditions) { + vector rowOrder = topoSort(k, rowConditions); + if (rowOrder.size() != k) return {}; + + vector colOrder = topoSort(k, colConditions); + if (colOrder.size() != k) return {}; + + vector> res(k, vector(k, 0)); + vector colIndex(k + 1); + for (int i = 0; i < k; i++) { + colIndex[colOrder[i]] = i; + } + for (int i = 0; i < k; i++) { + res[i][colIndex[rowOrder[i]]] = rowOrder[i]; + } + return res; + } + +private: + vector topoSort(int k, vector>& edges) { + vector indegree(k + 1, 0); + vector> adj(k + 1); + for (const auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + indegree[edge[1]]++; + } + + queue q; + vector order; + for (int i = 1; i <= k; i++) { + if (indegree[i] == 0) { + q.push(i); + } + } + + while (!q.empty()) { + int node = q.front(); + q.pop(); + order.push_back(node); + + for (int nei : adj[node]) { + indegree[nei]--; + if (indegree[nei] == 0) { + q.push(nei); + } + } + } + + if (order.size() != k) return {}; + return order; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} k + * @param {number[][]} rowConditions + * @param {number[][]} colConditions + * @return {number[][]} + */ + buildMatrix(k, rowConditions, colConditions) { + const rowOrder = this.topoSort(k, rowConditions); + if (rowOrder.length !== k) return []; + + const colOrder = this.topoSort(k, colConditions); + if (colOrder.length !== k) return []; + + const res = Array.from({ length: k }, () => Array(k).fill(0)); + const colIndex = Array(k + 1).fill(0); + + for (let i = 0; i < k; i++) { + colIndex[colOrder[i]] = i; + } + + for (let i = 0; i < k; i++) { + res[i][colIndex[rowOrder[i]]] = rowOrder[i]; + } + + return res; + } + + /** + * @param {number} k + * @param {number[][]} edges + * @return {number[]} + */ + topoSort(k, edges) { + const indegree = Array(k + 1).fill(0); + const adj = Array.from({ length: k + 1 }, () => []); + + for (const [u, v] of edges) { + adj[u].push(v); + indegree[v]++; + } + + const queue = new Queue(); + const order = []; + + for (let i = 1; i <= k; i++) { + if (indegree[i] === 0) { + queue.push(i); + } + } + + while (!queue.isEmpty()) { + const node = queue.pop(); + order.push(node); + for (const nei of adj[node]) { + indegree[nei]--; + if (indegree[nei] === 0) { + queue.push(nei); + } + } + } + + return order.length === k ? order : []; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k ^ 2 + n + m)$ +* Space complexity: + * $O(k + n + m)$ extra space. + * $O(k ^ 2)$ space for the output matrix. + +> Where $n$ is the size of the array $rowConditions$, $m$ is the size of the array $colConditions$, and $k$ is the size of the output matrix. \ No newline at end of file diff --git a/articles/buildings-with-an-ocean-view.md b/articles/buildings-with-an-ocean-view.md new file mode 100644 index 000000000..9c418c11c --- /dev/null +++ b/articles/buildings-with-an-ocean-view.md @@ -0,0 +1,339 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def findBuildings(self, heights: List[int]) -> List[int]: + n = len(heights) + res = [] + + for i in range(n): + flag = True + for j in range(i + 1, n): + if heights[i] <= heights[j]: + flag = False + break + if flag: + res.append(i) + + return res +``` + +```java +public class Solution { + public int[] findBuildings(int[] heights) { + int n = heights.length; + List temp = new ArrayList<>(); + + for (int i = 0; i < n; i++) { + boolean flag = true; + for (int j = i + 1; j < n; j++) { + if (heights[i] <= heights[j]) { + flag = false; + break; + } + } + if (flag) temp.add(i); + } + + int[] res = new int[temp.size()]; + for (int i = 0; i < temp.size(); i++) { + res[i] = temp.get(i); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findBuildings(vector& heights) { + int n = heights.size(); + vector res; + + for (int i = 0; i < n; i++) { + bool flag = true; + for (int j = i + 1; j < n; j++) { + if (heights[i] <= heights[j]) { + flag = false; + break; + } + } + if (flag) res.push_back(i); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @return {number[]} + */ + findBuildings(heights) { + const n = heights.length; + const res = []; + + for (let i = 0; i < n; i++) { + let flag = true; + for (let j = i + 1; j < n; j++) { + if (heights[i] <= heights[j]) { + flag = false; + break; + } + } + if (flag) res.push(i); + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int[] FindBuildings(int[] heights) { + int n = heights.Length; + List temp = new List(); + + for (int i = 0; i < n; i++) { + bool flag = true; + for (int j = i + 1; j < n; j++) { + if (heights[i] <= heights[j]) { + flag = false; + break; + } + } + if (flag) temp.Add(i); + } + + return temp.ToArray(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ for the output array. + +--- + +## 2. Monotonic Stack + +::tabs-start + +```python +class Solution: + def findBuildings(self, heights: List[int]) -> List[int]: + stack = [] + + for i, h in enumerate(heights): + while stack and heights[stack[-1]] <= h: + stack.pop() + stack.append(i) + + return stack +``` + +```java +public class Solution { + public int[] findBuildings(int[] heights) { + int n = heights.length; + Stack stack = new Stack<>(); + + for (int i = 0; i < n; i++) { + while (!stack.isEmpty() && heights[stack.peek()] <= heights[i]) { + stack.pop(); + } + stack.push(i); + } + + int[] res = new int[stack.size()]; + for (int i = stack.size() - 1; i >= 0; i--) { + res[i] = stack.get(i); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findBuildings(vector& heights) { + vector stack; + + for (int i = 0; i < heights.size(); i++) { + while (!stack.empty() && heights[stack.back()] <= heights[i]) { + stack.pop_back(); + } + stack.push_back(i); + } + + return stack; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @return {number[]} + */ + findBuildings(heights) { + const stack = []; + + for (let i = 0; i < heights.length; i++) { + while (stack.length && heights[stack[stack.length - 1]] <= heights[i]) { + stack.pop(); + } + stack.push(i); + } + + return stack; + } +} +``` + +```csharp +public class Solution { + public int[] FindBuildings(int[] heights) { + List stack = new List(); + + for (int i = 0; i < heights.Length; i++) { + while (stack.Count > 0 && heights[stack[stack.Count - 1]] <= heights[i]) { + stack.RemoveAt(stack.Count - 1); + } + stack.Add(i); + } + + return stack.ToArray(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Greedy + +::tabs-start + +```python +class Solution: + def findBuildings(self, heights: List[int]) -> List[int]: + res = [len(heights) - 1] + for i in range(len(heights) - 2, -1, -1): + if heights[i] > heights[res[-1]]: + res.append(i) + res.reverse() + return res +``` + +```java +public class Solution { + public int[] findBuildings(int[] heights) { + List res = new ArrayList<>(); + int n = heights.length; + res.add(n - 1); + + for (int i = n - 2; i >= 0; i--) { + if (heights[i] > heights[res.get(res.size() - 1)]) { + res.add(i); + } + } + + Collections.reverse(res); + int[] ans = new int[res.size()]; + for (int i = 0; i < res.size(); i++) { + ans[i] = res.get(i); + } + + return ans; + } +} +``` + +```cpp +class Solution { +public: + vector findBuildings(vector& heights) { + vector res; + int n = heights.size(); + res.push_back(n - 1); + + for (int i = n - 2; i >= 0; i--) { + if (heights[i] > heights[res.back()]) { + res.push_back(i); + } + } + + reverse(res.begin(), res.end()); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @return {number[]} + */ + findBuildings(heights) { + const n = heights.length; + const res = [n - 1]; + + for (let i = n - 2; i >= 0; i--) { + if (heights[i] > heights[res[res.length - 1]]) { + res.push(i); + } + } + + return res.reverse(); + } +} +``` + +```csharp +public class Solution { + public int[] FindBuildings(int[] heights) { + int n = heights.Length; + List res = new List { n - 1 }; + + for (int i = n - 2; i >= 0; i--) { + if (heights[i] > heights[res[res.Count - 1]]) { + res.Add(i); + } + } + + res.Reverse(); + return res.ToArray(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ for the output array. \ No newline at end of file diff --git a/articles/burst-balloons.md b/articles/burst-balloons.md new file mode 100644 index 000000000..a52c66027 --- /dev/null +++ b/articles/burst-balloons.md @@ -0,0 +1,704 @@ +## 1. Brute Force (Recursion) + +::tabs-start + +```python +class Solution: + def maxCoins(self, nums: List[int]) -> int: + nums = [1] + nums + [1] + + def dfs(nums): + if len(nums) == 2: + return 0 + + maxCoins = 0 + for i in range(1, len(nums) - 1): + coins = nums[i - 1] * nums[i] * nums[i + 1] + coins += dfs(nums[:i] + nums[i + 1:]) + maxCoins = max(maxCoins, coins) + return maxCoins + + return dfs(nums) +``` + +```java +public class Solution { + public int maxCoins(int[] nums) { + int[] newNums = new int[nums.length + 2]; + newNums[0] = newNums[nums.length + 1] = 1; + for (int i = 0; i < nums.length; i++) { + newNums[i + 1] = nums[i]; + } + + return dfs(newNums); + } + + public int dfs(int[] nums) { + if (nums.length == 2) { + return 0; + } + + int maxCoins = 0; + for (int i = 1; i < nums.length - 1; i++) { + int coins = nums[i - 1] * nums[i] * nums[i + 1]; + int[] newNums = new int[nums.length - 1]; + for (int j = 0, k = 0; j < nums.length; j++) { + if (j != i) { + newNums[k++] = nums[j]; + } + } + coins += dfs(newNums); + maxCoins = Math.max(maxCoins, coins); + } + return maxCoins; + } +} +``` + +```cpp +class Solution { +public: + int maxCoins(vector& nums) { + nums.insert(nums.begin(), 1); + nums.push_back(1); + + return dfs(nums); + } + + int dfs(vector& nums) { + if (nums.size() == 2) return 0; + + int maxCoins = 0; + for (int i = 1; i < nums.size() - 1; i++) { + int coins = nums[i - 1] * nums[i] * nums[i + 1]; + vector newNums = nums; + newNums.erase(newNums.begin() + i); + coins += dfs(newNums); + maxCoins = max(maxCoins, coins); + } + return maxCoins; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxCoins(nums) { + nums.unshift(1); + nums.push(1); + return this.dfs(nums); + } + + /** + * @param {number[]} nums + * @return {number} + */ + dfs(nums) { + if (nums.length === 2) return 0; + + let maxCoins = 0; + for (let i = 1; i < nums.length - 1; i++) { + let coins = nums[i - 1] * nums[i] * nums[i + 1]; + let newNums = nums.slice(0, i).concat(nums.slice(i + 1)); + coins += this.dfs(newNums); + maxCoins = Math.max(maxCoins, coins); + } + return maxCoins; + } +} +``` + +```csharp +public class Solution { + public int MaxCoins(int[] nums) { + List newNums = new List {1}; + newNums.AddRange(nums); + newNums.Add(1); + + return Dfs(newNums); + } + + public int Dfs(List nums) { + if (nums.Count == 2) return 0; + + int maxCoins = 0; + for (int i = 1; i < nums.Count - 1; i++) { + int coins = nums[i - 1] * nums[i] * nums[i + 1]; + List newNums = new List(nums); + newNums.RemoveAt(i); + coins += Dfs(newNums); + maxCoins = Math.Max(maxCoins, coins); + } + return maxCoins; + } +} +``` + +```go +func maxCoins(nums []int) int { + nums = append([]int{1}, nums...) + nums = append(nums, 1) + + var dfs func(nums []int) int + dfs = func(nums []int) int { + if len(nums) == 2 { + return 0 + } + + maxCoins := 0 + for i := 1; i < len(nums)-1; i++ { + coins := nums[i-1] * nums[i] * nums[i+1] + coins += dfs(append(append([]int{}, nums[:i]...), nums[i+1:]...)) + if coins > maxCoins { + maxCoins = coins + } + } + return maxCoins + } + + return dfs(nums) +} +``` + +```kotlin +class Solution { + fun maxCoins(nums: IntArray): Int { + val newNums = intArrayOf(1) + nums + intArrayOf(1) + + fun dfs(nums: IntArray): Int { + if (nums.size == 2) { + return 0 + } + + var maxCoins = 0 + for (i in 1 until nums.size - 1) { + val coins = nums[i - 1] * nums[i] * nums[i + 1] + val nextCoins = dfs(nums.take(i).toIntArray() + + nums.drop(i + 1).toIntArray()) + maxCoins = maxOf(maxCoins, coins + nextCoins) + } + return maxCoins + } + + return dfs(newNums) + } +} +``` + +```swift +class Solution { + func maxCoins(_ nums: [Int]) -> Int { + var nums = [1] + nums + [1] + + func dfs(_ nums: [Int]) -> Int { + if nums.count == 2 { + return 0 + } + + var maxCoins = 0 + for i in 1..<(nums.count - 1) { + let coins = nums[i - 1] * nums[i] * nums[i + 1] + + dfs(Array(nums[.. int: + nums = [1] + nums + [1] + dp = {} + def dfs(l, r): + if l > r: + return 0 + if (l, r) in dp: + return dp[(l, r)] + + dp[(l, r)] = 0 + for i in range(l, r + 1): + coins = nums[l - 1] * nums[i] * nums[r + 1] + coins += dfs(l, i - 1) + dfs(i + 1, r) + dp[(l, r)] = max(dp[(l, r)], coins) + return dp[(l, r)] + + return dfs(1, len(nums) - 2) +``` + +```java +public class Solution { + public int maxCoins(int[] nums) { + int n = nums.length; + int[] newNums = new int[n + 2]; + newNums[0] = newNums[n + 1] = 1; + for (int i = 0; i < n; i++) { + newNums[i + 1] = nums[i]; + } + + int[][] dp = new int[n + 2][n + 2]; + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= n; j++) { + dp[i][j] = -1; + } + } + + return dfs(newNums, 1, newNums.length - 2, dp); + } + + public int dfs(int[] nums, int l, int r, int[][] dp) { + if (l > r) { + return 0; + } + if (dp[l][r] != -1) { + return dp[l][r]; + } + + dp[l][r] = 0; + for (int i = l; i <= r; i++) { + int coins = nums[l - 1] * nums[i] * nums[r + 1]; + coins += dfs(nums, l, i - 1, dp) + dfs(nums, i + 1, r, dp); + dp[l][r] = Math.max(dp[l][r], coins); + } + return dp[l][r]; + } +} +``` + +```cpp +class Solution { +public: + int maxCoins(vector& nums) { + int n = nums.size(); + vector newNums(n + 2, 1); + for (int i = 0; i < n; i++) { + newNums[i + 1] = nums[i]; + } + + vector> dp(n + 2, vector(n + 2, -1)); + return dfs(newNums, 1, newNums.size() - 2, dp); + } + + int dfs(vector& nums, int l, int r, vector>& dp) { + if (l > r) return 0; + if (dp[l][r] != -1) return dp[l][r]; + + dp[l][r] = 0; + for (int i = l; i <= r; i++) { + int coins = nums[l - 1] * nums[i] * nums[r + 1]; + coins += dfs(nums, l, i - 1, dp) + dfs(nums, i + 1, r, dp); + dp[l][r] = max(dp[l][r], coins); + } + return dp[l][r]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxCoins(nums) { + let n = nums.length; + let newNums = new Array(n + 2).fill(1); + for (let i = 0; i < n; i++) { + newNums[i + 1] = nums[i]; + } + + let dp = Array.from({ length: n + 2 }, () => new Array(n + 2).fill(-1)); + return this.dfs(newNums, 1, newNums.length - 2, dp); + } + + /** + * @param {number[]} nums + * @param {number} l + * @param {number} r + * @param {number[][]} dp + * @return {number} + */ + dfs(nums, l, r, dp) { + if (l > r) return 0; + if (dp[l][r] !== -1) return dp[l][r]; + + dp[l][r] = 0; + for (let i = l; i <= r; i++) { + let coins = nums[l - 1] * nums[i] * nums[r + 1]; + coins += this.dfs(nums, l, i - 1, dp) + this.dfs(nums, i + 1, r, dp); + dp[l][r] = Math.max(dp[l][r], coins); + } + return dp[l][r]; + } +} +``` + +```csharp +public class Solution { + public int MaxCoins(int[] nums) { + int n = nums.Length; + int[] newNums = new int[n + 2]; + newNums[0] = newNums[n + 1] = 1; + for (int i = 0; i < n; i++) { + newNums[i + 1] = nums[i]; + } + + int[,] dp = new int[n + 2, n + 2]; + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= n; j++) { + dp[i, j] = -1; + } + } + + return Dfs(newNums, 1, newNums.Length - 2, dp); + } + + public int Dfs(int[] nums, int l, int r, int[,] dp) { + if (l > r) return 0; + if (dp[l, r] != -1) return dp[l, r]; + + dp[l, r] = 0; + for (int i = l; i <= r; i++) { + int coins = nums[l - 1] * nums[i] * nums[r + 1]; + coins += Dfs(nums, l, i - 1, dp) + Dfs(nums, i + 1, r, dp); + dp[l, r] = Math.Max(dp[l, r], coins); + } + return dp[l, r]; + } +} +``` + +```go +func maxCoins(nums []int) int { + nums = append([]int{1}, nums...) + nums = append(nums, 1) + n := len(nums) + + dp := make([][]int, n) + for i := 0; i < n; i++ { + dp[i] = make([]int, n) + } + + var dfs func(l, r int) int + dfs = func(l, r int) int { + if l > r { + return 0 + } + if dp[l][r] > 0 { + return dp[l][r] + } + + dp[l][r] = 0 + for i := l; i <= r; i++ { + coins := nums[l-1] * nums[i] * nums[r+1] + coins += dfs(l, i-1) + dfs(i+1, r) + dp[l][r] = max(dp[l][r], coins) + } + return dp[l][r] + } + + return dfs(1, len(nums)-2) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun maxCoins(nums: IntArray): Int { + val newNums = intArrayOf(1) + nums + intArrayOf(1) + val n = newNums.size + val dp = Array(n) { IntArray(n) } + + fun dfs(l: Int, r: Int): Int { + if (l > r) return 0 + if (dp[l][r] > 0) return dp[l][r] + + dp[l][r] = 0 + for (i in l..r) { + val coins = newNums[l - 1] * newNums[i] * newNums[r + 1] + val totalCoins = coins + dfs(l, i - 1) + dfs(i + 1, r) + dp[l][r] = maxOf(dp[l][r], totalCoins) + } + return dp[l][r] + } + + return dfs(1, newNums.size - 2) + } +} +``` + +```swift +class Solution { + func maxCoins(_ nums: [Int]) -> Int { + var nums = [1] + nums + [1] + let n = nums.count + var dp = Array(repeating: Array(repeating: -1, count: n), count: n) + + func dfs(_ l: Int, _ r: Int) -> Int { + if l > r { + return 0 + } + if dp[l][r] != -1 { + return dp[l][r] + } + + dp[l][r] = 0 + for i in l...r { + let coins = nums[l - 1] * nums[i] * nums[r + 1] + dfs(l, i - 1) + dfs(i + 1, r) + dp[l][r] = max(dp[l][r], coins) + } + return dp[l][r] + } + + return dfs(1, n - 2) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def maxCoins(self, nums): + n = len(nums) + new_nums = [1] + nums + [1] + + dp = [[0] * (n + 2) for _ in range(n + 2)] + for l in range(n, 0, -1): + for r in range(l, n + 1): + for i in range(l, r + 1): + coins = new_nums[l - 1] * new_nums[i] * new_nums[r + 1] + coins += dp[l][i - 1] + dp[i + 1][r] + dp[l][r] = max(dp[l][r], coins) + + return dp[1][n] +``` + +```java +public class Solution { + public int maxCoins(int[] nums) { + int n = nums.length; + int[] newNums = new int[n + 2]; + newNums[0] = newNums[n + 1] = 1; + for (int i = 0; i < n; i++) { + newNums[i + 1] = nums[i]; + } + + int[][] dp = new int[n + 2][n + 2]; + for (int l = n; l >= 1; l--) { + for (int r = l; r <= n; r++) { + for (int i = l; i <= r; i++) { + int coins = newNums[l - 1] * newNums[i] * newNums[r + 1]; + coins += dp[l][i - 1] + dp[i + 1][r]; + dp[l][r] = Math.max(dp[l][r], coins); + } + } + } + + return dp[1][n]; + } +} +``` + +```cpp +class Solution { +public: + int maxCoins(vector& nums) { + int n = nums.size(); + vector newNums(n + 2, 1); + for (int i = 0; i < n; i++) { + newNums[i + 1] = nums[i]; + } + + vector> dp(n + 2, vector(n + 2, 0)); + for (int l = n; l >= 1; l--) { + for (int r = l; r <= n; r++) { + for (int i = l; i <= r; i++) { + int coins = newNums[l - 1] * newNums[i] * newNums[r + 1]; + coins += dp[l][i - 1] + dp[i + 1][r]; + dp[l][r] = max(dp[l][r], coins); + } + } + } + + return dp[1][n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxCoins(nums) { + let n = nums.length; + let newNums = new Array(n + 2).fill(1); + for (let i = 0; i < n; i++) { + newNums[i + 1] = nums[i]; + } + + let dp = Array.from({ length: n + 2 }, () => new Array(n + 2).fill(0)); + for (let l = n; l >= 1; l--) { + for (let r = l; r <= n; r++) { + for (let i = l; i <= r; i++) { + let coins = newNums[l - 1] * newNums[i] * newNums[r + 1]; + coins += dp[l][i - 1] + dp[i + 1][r]; + dp[l][r] = Math.max(dp[l][r], coins); + } + } + } + + return dp[1][n]; + } +} +``` + +```csharp +public class Solution { + public int MaxCoins(int[] nums) { + int n = nums.Length; + int[] newNums = new int[n + 2]; + newNums[0] = newNums[n + 1] = 1; + for (int i = 0; i < n; i++) { + newNums[i + 1] = nums[i]; + } + + int[,] dp = new int[n + 2, n + 2]; + for (int l = n; l >= 1; l--) { + for (int r = l; r <= n; r++) { + for (int i = l; i <= r; i++) { + int coins = newNums[l - 1] * newNums[i] * newNums[r + 1]; + coins += dp[l, i - 1] + dp[i + 1, r]; + dp[l, r] = Math.Max(dp[l, r], coins); + } + } + } + + return dp[1, n]; + } +} +``` + +```go +func maxCoins(nums []int) int { + n := len(nums) + newNums := append([]int{1}, nums...) + newNums = append(newNums, 1) + + dp := make([][]int, n+2) + for i := range dp { + dp[i] = make([]int, n+2) + } + + for l := n; l >= 1; l-- { + for r := l; r <= n; r++ { + for i := l; i <= r; i++ { + coins := newNums[l-1] * newNums[i] * newNums[r+1] + coins += dp[l][i-1] + dp[i+1][r] + dp[l][r] = max(dp[l][r], coins) + } + } + } + + return dp[1][n] +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun maxCoins(nums: IntArray): Int { + val n = nums.size + val newNums = intArrayOf(1) + nums + intArrayOf(1) + + val dp = Array(n + 2) { IntArray(n + 2) } + + for (l in n downTo 1) { + for (r in l..n) { + for (i in l..r) { + val coins = newNums[l - 1] * newNums[i] * newNums[r + 1] + dp[l][r] = maxOf(dp[l][r], coins + dp[l][i - 1] + dp[i + 1][r]) + } + } + } + + return dp[1][n] + } +} +``` + +```swift +class Solution { + func maxCoins(_ nums: [Int]) -> Int { + let n = nums.count + var newNums = [1] + nums + [1] + + var dp = Array(repeating: Array(repeating: 0, count: n + 2), count: n + 2) + + for l in stride(from: n, through: 1, by: -1) { + for r in l...n { + for i in l...r { + let coins = (newNums[l - 1] * newNums[i] * newNums[r + 1]) + + dp[l][i - 1] + dp[i + 1][r] + dp[l][r] = max(dp[l][r], coins) + } + } + } + + return dp[1][n] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n^3)$ +* Space complexity: $O(n^2)$ \ No newline at end of file diff --git a/articles/buy-and-sell-crypto-with-cooldown.md b/articles/buy-and-sell-crypto-with-cooldown.md new file mode 100644 index 000000000..9c4ae550b --- /dev/null +++ b/articles/buy-and-sell-crypto-with-cooldown.md @@ -0,0 +1,877 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def maxProfit(self, prices: List[int]) -> int: + + def dfs(i, buying): + if i >= len(prices): + return 0 + + cooldown = dfs(i + 1, buying) + if buying: + buy = dfs(i + 1, not buying) - prices[i] + return max(buy, cooldown) + else: + sell = dfs(i + 2, not buying) + prices[i] + return max(sell, cooldown) + + return dfs(0, True) +``` + +```java +public class Solution { + public int maxProfit(int[] prices) { + + return dfs(0, true, prices); + } + + private int dfs(int i, boolean buying, int[] prices) { + if (i >= prices.length) { + return 0; + } + + int cooldown = dfs(i + 1, buying, prices); + if (buying) { + int buy = dfs(i + 1, false, prices) - prices[i]; + return Math.max(buy, cooldown); + } else { + int sell = dfs(i + 2, true, prices) + prices[i]; + return Math.max(sell, cooldown); + } + } +} +``` + +```cpp +class Solution { +public: + int maxProfit(vector& prices) { + return dfs(0, true, prices); + } + +private: + int dfs(int i, bool buying, vector& prices) { + if (i >= prices.size()) { + return 0; + } + + int cooldown = dfs(i + 1, buying, prices); + if (buying) { + int buy = dfs(i + 1, false, prices) - prices[i]; + return max(buy, cooldown); + } else { + int sell = dfs(i + 2, true, prices) + prices[i]; + return max(sell, cooldown); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} prices + * @return {number} + */ + maxProfit(prices) { + + const dfs = (i, buying) => { + if (i >= prices.length) { + return 0; + } + + let cooldown = dfs(i + 1, buying); + if (buying) { + let buy = dfs(i + 1, false) - prices[i]; + return Math.max(buy, cooldown); + } else { + let sell = dfs(i + 2, true) + prices[i]; + return Math.max(sell, cooldown); + } + } + + return dfs(0, true); + } +} +``` + +```csharp +public class Solution { + public int MaxProfit(int[] prices) { + return Dfs(0, true, prices); + } + + private int Dfs(int i, bool buying, int[] prices) { + if (i >= prices.Length) { + return 0; + } + + int cooldown = Dfs(i + 1, buying, prices); + if (buying) { + int buy = Dfs(i + 1, false, prices) - prices[i]; + return Math.Max(buy, cooldown); + } else { + int sell = Dfs(i + 2, true, prices) + prices[i]; + return Math.Max(sell, cooldown); + } + } +} +``` + +```go +func maxProfit(prices []int) int { + var dfs func(i int, buying bool) int + dfs = func(i int, buying bool) int { + if i >= len(prices) { + return 0 + } + + cooldown := dfs(i + 1, buying) + if buying { + buy := dfs(i + 1, false) - prices[i] + return max(buy, cooldown) + } else { + sell := dfs(i + 2, true) + prices[i] + return max(sell, cooldown) + } + } + + return dfs(0, true) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun maxProfit(prices: IntArray): Int { + fun dfs(i: Int, buying: Boolean): Int { + if (i >= prices.size) return 0 + + val cooldown = dfs(i + 1, buying) + return if (buying) { + val buy = dfs(i + 1, false) - prices[i] + maxOf(buy, cooldown) + } else { + val sell = dfs(i + 2, true) + prices[i] + maxOf(sell, cooldown) + } + } + + return dfs(0, true) + } +} +``` + +```swift +class Solution { + func maxProfit(_ prices: [Int]) -> Int { + let n = prices.count + + func dfs(_ i: Int, _ buying: Bool) -> Int { + if i >= n { + return 0 + } + + let cooldown = dfs(i + 1, buying) + if buying { + let buy = dfs(i + 1, false) - prices[i] + return max(buy, cooldown) + } else { + let sell = dfs(i + 2, true) + prices[i] + return max(sell, cooldown) + } + } + + return dfs(0, true) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def maxProfit(self, prices: List[int]) -> int: + dp = {} # key=(i, buying) val=max_profit + + def dfs(i, buying): + if i >= len(prices): + return 0 + if (i, buying) in dp: + return dp[(i, buying)] + + cooldown = dfs(i + 1, buying) + if buying: + buy = dfs(i + 1, not buying) - prices[i] + dp[(i, buying)] = max(buy, cooldown) + else: + sell = dfs(i + 2, not buying) + prices[i] + dp[(i, buying)] = max(sell, cooldown) + return dp[(i, buying)] + + return dfs(0, True) +``` + +```java +public class Solution { + private Map dp = new HashMap<>(); + + public int maxProfit(int[] prices) { + return dfs(0, true, prices); + } + + private int dfs(int i, boolean buying, int[] prices) { + if (i >= prices.length) { + return 0; + } + + String key = i + "-" + buying; + if (dp.containsKey(key)) { + return dp.get(key); + } + + int cooldown = dfs(i + 1, buying, prices); + if (buying) { + int buy = dfs(i + 1, false, prices) - prices[i]; + dp.put(key, Math.max(buy, cooldown)); + } else { + int sell = dfs(i + 2, true, prices) + prices[i]; + dp.put(key, Math.max(sell, cooldown)); + } + + return dp.get(key); + } +} +``` + +```cpp +class Solution { +public: + unordered_map dp; + + int maxProfit(vector& prices) { + return dfs(0, true, prices); + } + +private: + int dfs(int i, bool buying, vector& prices) { + if (i >= prices.size()) { + return 0; + } + + string key = to_string(i) + "-" + to_string(buying); + if (dp.find(key) != dp.end()) { + return dp[key]; + } + + int cooldown = dfs(i + 1, buying, prices); + if (buying) { + int buy = dfs(i + 1, false, prices) - prices[i]; + dp[key] = max(buy, cooldown); + } else { + int sell = dfs(i + 2, true, prices) + prices[i]; + dp[key] = max(sell, cooldown); + } + + return dp[key]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} prices + * @return {number} + */ + maxProfit(prices) { + const dp = {}; + const dfs = (i, buying) => { + if (i >= prices.length) { + return 0; + } + let key = `${i}-${buying}`; + if (key in dp) { + return dp[key]; + } + + let cooldown = dfs(i + 1, buying); + if (buying) { + let buy = dfs(i + 1, false) - prices[i]; + dp[key] = Math.max(buy, cooldown); + } else { + let sell = dfs(i + 2, true) + prices[i]; + dp[key] = Math.max(sell, cooldown); + } + return dp[key]; + } + + return dfs(0, true); + } +} +``` + +```csharp +public class Solution { + private Dictionary<(int, bool), int> dp = + new Dictionary<(int, bool), int>(); + + public int MaxProfit(int[] prices) { + return Dfs(0, true, prices); + } + + private int Dfs(int i, bool buying, int[] prices) { + if (i >= prices.Length) { + return 0; + } + + var key = (i, buying); + if (dp.ContainsKey(key)) { + return dp[key]; + } + + int cooldown = Dfs(i + 1, buying, prices); + if (buying) { + int buy = Dfs(i + 1, false, prices) - prices[i]; + dp[key] = Math.Max(buy, cooldown); + } else { + int sell = Dfs(i + 2, true, prices) + prices[i]; + dp[key] = Math.Max(sell, cooldown); + } + + return dp[key]; + } +} +``` + +```go +func maxProfit(prices []int) int { + dp := make(map[[2]int]int) // key is [i, buying], value is max profit + + var dfs func(i int, buying bool) int + dfs = func(i int, buying bool) int { + if i >= len(prices) { + return 0 + } + + key := [2]int{i, boolToInt(buying)} + if val, found := dp[key]; found { + return val + } + + cooldown := dfs(i + 1, buying) + if buying { + buy := dfs(i + 1, false) - prices[i] + dp[key] = max(buy, cooldown) + } else { + sell := dfs(i + 2, true) + prices[i] + dp[key] = max(sell, cooldown) + } + + return dp[key] + } + + return dfs(0, true) +} + +func boolToInt(b bool) int { + if b { + return 1 + } + return 0 +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun maxProfit(prices: IntArray): Int { + val dp = HashMap, Int>() // key is Pair(i, buying) + + fun dfs(i: Int, buying: Boolean): Int { + if (i >= prices.size) return 0 + + val key = Pair(i, buying) + if (key in dp) return dp[key]!! + + val cooldown = dfs(i + 1, buying) + dp[key] = if (buying) { + val buy = dfs(i + 1, false) - prices[i] + maxOf(buy, cooldown) + } else { + val sell = dfs(i + 2, true) + prices[i] + maxOf(sell, cooldown) + } + + return dp[key]!! + } + + return dfs(0, true) + } +} +``` + +```swift +class Solution { + func maxProfit(_ prices: [Int]) -> Int { + let n = prices.count + var dp = [[Int?]](repeating: [Int?](repeating: nil, count: 2), count: n) + + func dfs(_ i: Int, _ buying: Int) -> Int { + if i >= n { + return 0 + } + if let cached = dp[i][buying] { + return cached + } + + let cooldown = dfs(i + 1, buying) + if buying == 1 { + let buy = dfs(i + 1, 0) - prices[i] + dp[i][buying] = max(buy, cooldown) + } else { + let sell = dfs(i + 2, 1) + prices[i] + dp[i][buying] = max(sell, cooldown) + } + return dp[i][buying]! + } + + return dfs(0, 1) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def maxProfit(self, prices: List[int]) -> int: + n = len(prices) + dp = [[0] * 2 for _ in range(n + 1)] + + for i in range(n - 1, -1, -1): + for buying in [True, False]: + if buying: + buy = dp[i + 1][False] - prices[i] if i + 1 < n else -prices[i] + cooldown = dp[i + 1][True] if i + 1 < n else 0 + dp[i][1] = max(buy, cooldown) + else: + sell = dp[i + 2][True] + prices[i] if i + 2 < n else prices[i] + cooldown = dp[i + 1][False] if i + 1 < n else 0 + dp[i][0] = max(sell, cooldown) + + return dp[0][1] +``` + +```java +public class Solution { + public int maxProfit(int[] prices) { + int n = prices.length; + int[][] dp = new int[n + 1][2]; + + for (int i = n - 1; i >= 0; i--) { + for (int buying = 1; buying >= 0; buying--) { + if (buying == 1) { + int buy = dp[i + 1][0] - prices[i]; + int cooldown = dp[i + 1][1]; + dp[i][1] = Math.max(buy, cooldown); + } else { + int sell = (i + 2 < n) ? dp[i + 2][1] + prices[i] : prices[i]; + int cooldown = dp[i + 1][0]; + dp[i][0] = Math.max(sell, cooldown); + } + } + } + + return dp[0][1]; + } +} +``` + +```cpp +class Solution { +public: + int maxProfit(vector& prices) { + int n = prices.size(); + vector> dp(n + 1, vector(2, 0)); + + for (int i = n - 1; i >= 0; --i) { + for (int buying = 1; buying >= 0; --buying) { + if (buying == 1) { + int buy = dp[i + 1][0] - prices[i]; + int cooldown = dp[i + 1][1]; + dp[i][1] = max(buy, cooldown); + } else { + int sell = (i + 2 < n) ? dp[i + 2][1] + prices[i] : prices[i]; + int cooldown = dp[i + 1][0]; + dp[i][0] = max(sell, cooldown); + } + } + } + + return dp[0][1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} prices + * @return {number} + */ + maxProfit(prices) { + const n = prices.length; + const dp = Array.from({ length: n + 1 }, () => [0, 0]); + + for (let i = n - 1; i >= 0; i--) { + for (let buying = 1; buying >= 0; buying--) { + if (buying === 1) { + let buy = dp[i + 1][0] - prices[i]; + let cooldown = dp[i + 1][1]; + dp[i][1] = Math.max(buy, cooldown); + } else { + let sell = (i + 2 < n) ? dp[i + 2][1] + prices[i] : prices[i]; + let cooldown = dp[i + 1][0]; + dp[i][0] = Math.max(sell, cooldown); + } + } + } + + return dp[0][1]; + } +} +``` + +```csharp +public class Solution { + public int MaxProfit(int[] prices) { + int n = prices.Length; + int[,] dp = new int[n + 1, 2]; + + for (int i = n - 1; i >= 0; i--) { + for (int buying = 1; buying >= 0; buying--) { + if (buying == 1) { + int buy = dp[i + 1, 0] - prices[i]; + int cooldown = dp[i + 1, 1]; + dp[i, 1] = Math.Max(buy, cooldown); + } else { + int sell = (i + 2 < n) ? dp[i + 2, 1] + prices[i] : prices[i]; + int cooldown = dp[i + 1, 0]; + dp[i, 0] = Math.Max(sell, cooldown); + } + } + } + + return dp[0, 1]; + } +} +``` + +```go +func maxProfit(prices []int) int { + n := len(prices) + dp := make([][]int, n+1) + for i := range dp { + dp[i] = make([]int, 2) + } + + for i := n - 1; i >= 0; i-- { + for buying := 1; buying >= 0; buying-- { + if buying == 1 { + buy := dp[i+1][0] - prices[i] + cooldown := dp[i+1][1] + dp[i][1] = max(buy, cooldown) + } else { + sell := prices[i] + if i+2 < n { + sell += dp[i+2][1] + } + cooldown := dp[i+1][0] + dp[i][0] = max(sell, cooldown) + } + } + } + + return dp[0][1] +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun maxProfit(prices: IntArray): Int { + val n = prices.size + val dp = Array(n + 1) { IntArray(2) } + + for (i in n - 1 downTo 0) { + for (buying in 1 downTo 0) { + dp[i][buying] = if (buying == 1) { + val buy = dp[i + 1][0] - prices[i] + val cooldown = dp[i + 1][1] + maxOf(buy, cooldown) + } else { + val sell = if (i + 2 < n) dp[i + 2][1] + prices[i] else prices[i] + val cooldown = dp[i + 1][0] + maxOf(sell, cooldown) + } + } + } + + return dp[0][1] + } +} +``` + +```swift +class Solution { + func maxProfit(_ prices: [Int]) -> Int { + let n = prices.count + var dp = Array(repeating: [0, 0], count: n + 1) + + for i in stride(from: n - 1, through: 0, by: -1) { + for buying in 0...1 { + if buying == 1 { + let buy = (i + 1 < n ? dp[i + 1][0] - prices[i] : -prices[i]) + let cooldown = (i + 1 < n ? dp[i + 1][1] : 0) + dp[i][1] = max(buy, cooldown) + } else { + let sell = (i + 2 < n ? dp[i + 2][1] + prices[i] : prices[i]) + let cooldown = (i + 1 < n ? dp[i + 1][0] : 0) + dp[i][0] = max(sell, cooldown) + } + } + } + + return dp[0][1] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def maxProfit(self, prices: List[int]) -> int: + n = len(prices) + dp1_buy, dp1_sell = 0, 0 + dp2_buy = 0 + + for i in range(n - 1, -1, -1): + dp_buy = max(dp1_sell - prices[i], dp1_buy) + dp_sell = max(dp2_buy + prices[i], dp1_sell) + dp2_buy = dp1_buy + dp1_buy, dp1_sell = dp_buy, dp_sell + + return dp1_buy +``` + +```java +public class Solution { + public int maxProfit(int[] prices) { + int n = prices.length; + int dp1_buy = 0, dp1_sell = 0; + int dp2_buy = 0; + + for (int i = n - 1; i >= 0; i--) { + int dp_buy = Math.max(dp1_sell - prices[i], dp1_buy); + int dp_sell = Math.max(dp2_buy + prices[i], dp1_sell); + dp2_buy = dp1_buy; + dp1_buy = dp_buy; + dp1_sell = dp_sell; + } + + return dp1_buy; + } +} +``` + +```cpp +class Solution { +public: + int maxProfit(vector& prices) { + int n = prices.size(); + int dp1_buy = 0, dp1_sell = 0; + int dp2_buy = 0; + + for (int i = n - 1; i >= 0; --i) { + int dp_buy = max(dp1_sell - prices[i], dp1_buy); + int dp_sell = max(dp2_buy + prices[i], dp1_sell); + dp2_buy = dp1_buy; + dp1_buy = dp_buy; + dp1_sell = dp_sell; + } + + return dp1_buy; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} prices + * @return {number} + */ + maxProfit(prices) { + const n = prices.length; + let dp1_buy = 0, dp1_sell = 0; + let dp2_buy = 0; + + for (let i = n - 1; i >= 0; i--) { + let dp_buy = Math.max(dp1_sell - prices[i], dp1_buy); + let dp_sell = Math.max(dp2_buy + prices[i], dp1_sell); + dp2_buy = dp1_buy; + dp1_buy = dp_buy; + dp1_sell = dp_sell; + } + + return dp1_buy; + } +} +``` + +```csharp +public class Solution { + public int MaxProfit(int[] prices) { + int n = prices.Length; + int dp1_buy = 0, dp1_sell = 0; + int dp2_buy = 0; + + for (int i = n - 1; i >= 0; i--) { + int dp_buy = Math.Max(dp1_sell - prices[i], dp1_buy); + int dp_sell = Math.Max(dp2_buy + prices[i], dp1_sell); + dp2_buy = dp1_buy; + dp1_buy = dp_buy; + dp1_sell = dp_sell; + } + + return dp1_buy; + } +} +``` + +```go +func maxProfit(prices []int) int { + n := len(prices) + dp1_buy, dp1_sell := 0, 0 + dp2_buy := 0 + + for i := n - 1; i >= 0; i-- { + dp_buy := max(dp1_sell - prices[i], dp1_buy) + dp_sell := max(dp2_buy + prices[i], dp1_sell) + dp2_buy, dp1_sell = dp1_buy, dp_sell + dp1_buy = dp_buy + } + + return dp1_buy +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun maxProfit(prices: IntArray): Int { + var dp1_buy = 0 + var dp1_sell = 0 + var dp2_buy = 0 + + for (i in prices.size - 1 downTo 0) { + val dp_buy = maxOf(dp1_sell - prices[i], dp1_buy) + val dp_sell = maxOf(dp2_buy + prices[i], dp1_sell) + dp2_buy = dp1_buy + dp1_buy = dp_buy + dp1_sell = dp_sell + } + + return dp1_buy + } +} +``` + +```swift +class Solution { + func maxProfit(_ prices: [Int]) -> Int { + let n = prices.count + var dp1Buy = 0, dp1Sell = 0 + var dp2Buy = 0 + + for i in stride(from: n - 1, through: 0, by: -1) { + let dpBuy = max(dp1Sell - prices[i], dp1Buy) + let dpSell = max(dp2Buy + prices[i], dp1Sell) + dp2Buy = dp1Buy + dp1Buy = dpBuy + dp1Sell = dpSell + } + + return dp1Buy + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/buy-and-sell-crypto.md b/articles/buy-and-sell-crypto.md new file mode 100644 index 000000000..b983fd272 --- /dev/null +++ b/articles/buy-and-sell-crypto.md @@ -0,0 +1,456 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxProfit(self, prices: List[int]) -> int: + res = 0 + for i in range(len(prices)): + buy = prices[i] + for j in range(i + 1, len(prices)): + sell = prices[j] + res = max(res, sell - buy) + return res +``` + +```java +public class Solution { + public int maxProfit(int[] prices) { + int res = 0; + for (int i = 0; i < prices.length; i++) { + int buy = prices[i]; + for (int j = i + 1; j < prices.length; j++) { + int sell = prices[j]; + res = Math.max(res, sell - buy); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxProfit(vector& prices) { + int res = 0; + for (int i = 0; i < prices.size(); i++) { + int buy = prices[i]; + for (int j = i + 1; j < prices.size(); j++) { + int sell = prices[j]; + res = max(res, sell - buy); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} prices + * @return {number} + */ + maxProfit(prices) { + let res = 0; + for (let i = 0; i < prices.length; i++) { + let buy = prices[i]; + for (let j = i + 1; j < prices.length; j++) { + let sell = prices[j]; + res = Math.max(res, sell - buy); + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int MaxProfit(int[] prices) { + int res = 0; + for (int i = 0; i < prices.Length; i++) { + int buy = prices[i]; + for (int j = i + 1; j < prices.Length; j++) { + int sell = prices[j]; + res = Math.Max(res, sell - buy); + } + } + return res; + } +} +``` + +```go +func maxProfit(prices []int) int { + res := 0 + for i := 0; i < len(prices); i++ { + buy := prices[i] + for j := i + 1; j < len(prices); j++ { + sell := prices[j] + res = max(res, sell - buy) + } + } + return res +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun maxProfit(prices: IntArray): Int { + var res = 0 + for (i in prices.indices) { + val buy = prices[i] + for (j in i + 1 until prices.size) { + val sell = prices[j] + res = maxOf(res, sell - buy) + } + } + return res + } +} +``` + +```swift +class Solution { + func maxProfit(_ prices: [Int]) -> Int { + var res = 0 + for i in 0.. int: + l, r = 0, 1 + maxP = 0 + + while r < len(prices): + if prices[l] < prices[r]: + profit = prices[r] - prices[l] + maxP = max(maxP, profit) + else: + l = r + r += 1 + return maxP +``` + +```java +public class Solution { + public int maxProfit(int[] prices) { + int l = 0, r = 1; + int maxP = 0; + + while (r < prices.length) { + if (prices[l] < prices[r]) { + int profit = prices[r] - prices[l]; + maxP = Math.max(maxP, profit); + } else { + l = r; + } + r++; + } + return maxP; + } +} +``` + +```cpp +class Solution { +public: + int maxProfit(vector& prices) { + int l = 0, r = 1; + int maxP = 0; + + while (r < prices.size()) { + if (prices[l] < prices[r]) { + int profit = prices[r] - prices[l]; + maxP = max(maxP, profit); + } else { + l = r; + } + r++; + } + return maxP; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} prices + * @return {number} + */ + maxProfit(prices) { + let l = 0, r = 1; + let maxP = 0; + + while (r < prices.length) { + if (prices[l] < prices[r]) { + let profit = prices[r] - prices[l]; + maxP = Math.max(maxP, profit); + } else { + l = r; + } + r++; + } + return maxP; + } +} +``` + +```csharp +public class Solution { + public int MaxProfit(int[] prices) { + int l = 0, r = 1; + int maxP = 0; + + while (r < prices.Length) { + if (prices[l] < prices[r]) { + int profit = prices[r] - prices[l]; + maxP = Math.Max(maxP, profit); + } else { + l = r; + } + r++; + } + return maxP; + } +} +``` + +```go +func maxProfit(prices []int) int { + l, r := 0, 1 + maxP := 0 + + for r < len(prices) { + if prices[l] < prices[r] { + profit := prices[r] - prices[l] + if profit > maxP { + maxP = profit + } + } else { + l = r + } + r++ + } + return maxP +} +``` + +```kotlin +class Solution { + fun maxProfit(prices: IntArray): Int { + var l = 0 + var r = 1 + var maxP = 0 + + while (r < prices.size) { + if (prices[l] < prices[r]) { + val profit = prices[r] - prices[l] + maxP = maxOf(maxP, profit) + } else { + l = r + } + r++ + } + return maxP + } +} +``` + +```swift +class Solution { + func maxProfit(_ prices: [Int]) -> Int { + var l = 0, r = 1 + var maxP = 0 + + while r < prices.count { + if prices[l] < prices[r] { + let profit = prices[r] - prices[l] + maxP = max(maxP, profit) + } else { + l = r + } + r += 1 + } + return maxP + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Dynamic Programming + +::tabs-start + +```python +class Solution: + def maxProfit(self, prices: List[int]) -> int: + maxP = 0 + minBuy = prices[0] + + for sell in prices: + maxP = max(maxP, sell - minBuy) + minBuy = min(minBuy, sell) + return maxP +``` + +```java +public class Solution { + public int maxProfit(int[] prices) { + int maxP = 0; + int minBuy = prices[0]; + + for (int sell : prices) { + maxP = Math.max(maxP, sell - minBuy); + minBuy = Math.min(minBuy, sell); + } + return maxP; + } +} +``` + +```cpp +class Solution { +public: + int maxProfit(vector& prices) { + int maxP = 0; + int minBuy = prices[0]; + + for (int& sell : prices) { + maxP = max(maxP, sell - minBuy); + minBuy = min(minBuy, sell); + } + return maxP; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} prices + * @return {number} + */ + maxProfit(prices) { + let maxP = 0; + let minBuy = prices[0]; + + for (let sell of prices) { + maxP = Math.max(maxP, sell - minBuy); + minBuy = Math.min(minBuy, sell); + } + return maxP; + } +} +``` + +```csharp +public class Solution { + public int MaxProfit(int[] prices) { + int maxP = 0; + int minBuy = prices[0]; + + foreach (int sell in prices) { + maxP = Math.Max(maxP, sell - minBuy); + minBuy = Math.Min(minBuy, sell); + } + return maxP; + } +} +``` + +```go +func maxProfit(prices []int) int { + maxP := 0 + minBuy := math.MaxInt32 + + for _, sell := range prices { + if sell - minBuy > maxP { + maxP = sell - minBuy + } + if sell < minBuy { + minBuy = sell + } + } + return maxP +} +``` + +```kotlin +class Solution { + fun maxProfit(prices: IntArray): Int { + var maxP = 0 + var minBuy = Int.MAX_VALUE + + for (sell in prices) { + maxP = maxOf(maxP, sell - minBuy) + minBuy = minOf(minBuy, sell) + } + return maxP + } +} +``` + +```swift +class Solution { + func maxProfit(_ prices: [Int]) -> Int { + var maxP = 0 + var minBuy = prices[0] + + for sell in prices { + maxP = max(maxP, sell - minBuy) + minBuy = min(minBuy, sell) + } + return maxP + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/buy-two-chocolates.md b/articles/buy-two-chocolates.md new file mode 100644 index 000000000..0b6edc79d --- /dev/null +++ b/articles/buy-two-chocolates.md @@ -0,0 +1,211 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def buyChoco(self, prices: List[int], money: int) -> int: + res = -1 + for i in range(len(prices)): + for j in range(i + 1, len(prices)): + if prices[i] + prices[j] <= money: + res = max(res, money - prices[i] - prices[j]) + return res if res != -1 else money +``` + +```java +public class Solution { + public int buyChoco(int[] prices, int money) { + int res = -1; + for (int i = 0; i < prices.length; i++) { + for (int j = i + 1; j < prices.length; j++) { + if (prices[i] + prices[j] <= money) { + res = Math.max(res, money - prices[i] - prices[j]); + } + } + } + return res == -1 ? money : res; + } +} +``` + +```cpp +class Solution { +public: + int buyChoco(vector& prices, int money) { + int res = -1; + for (int i = 0; i < prices.size(); i++) { + for (int j = i + 1; j < prices.size(); j++) { + if (prices[i] + prices[j] <= money) { + res = max(res, money - prices[i] - prices[j]); + } + } + } + return res == -1 ? money : res; + } +}; +``` + +```javascript +class Solution { + buyChoco(prices, money) { + let res = -1; + for (let i = 0; i < prices.length; i++) { + for (let j = i + 1; j < prices.length; j++) { + if (prices[i] + prices[j] <= money) { + res = Math.max(res, money - prices[i] - prices[j]); + } + } + } + return res === -1 ? money : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def buyChoco(self, prices: List[int], money: int) -> int: + prices.sort() + buy = prices[0] + prices[1] + return money if buy > money else money - buy +``` + +```java +public class Solution { + public int buyChoco(int[] prices, int money) { + Arrays.sort(prices); + int buy = prices[0] + prices[1]; + return buy > money ? money : money - buy; + } +} +``` + +```cpp +class Solution { +public: + int buyChoco(vector& prices, int money) { + sort(prices.begin(), prices.end()); + int buy = prices[0] + prices[1]; + return buy > money ? money : money - buy; + } +}; +``` + +```javascript +class Solution { + buyChoco(prices, money) { + prices.sort((a, b) => a - b); + let buy = prices[0] + prices[1]; + return buy > money ? money : money - buy; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Greedy + +::tabs-start + +```python +class Solution: + def buyChoco(self, prices: list[int], money: int) -> int: + min1 = min2 = float('inf') + + for p in prices: + if p < min1: + min1, min2 = p, min1 + elif p < min2: + min2 = p + + leftover = money - min1 - min2 + return leftover if leftover >= 0 else money +``` + +```java +public class Solution { + public int buyChoco(int[] prices, int money) { + int min1 = Integer.MAX_VALUE, min2 = Integer.MAX_VALUE; + + for (int p : prices) { + if (p < min1) { + min2 = min1; + min1 = p; + } else if (p < min2) { + min2 = p; + } + } + + int leftover = money - min1 - min2; + return leftover >= 0 ? leftover : money; + } +} +``` + +```cpp +class Solution { +public: + int buyChoco(vector& prices, int money) { + int min1 = INT_MAX, min2 = INT_MAX; + + for (int p : prices) { + if (p < min1) { + min2 = min1; + min1 = p; + } else if (p < min2) { + min2 = p; + } + } + + int leftover = money - min1 - min2; + return leftover >= 0 ? leftover : money; + } +}; +``` + +```javascript +class Solution { + buyChoco(prices, money) { + let min1 = Infinity, min2 = Infinity; + + for (const p of prices) { + if (p < min1) { + min2 = min1; + min1 = p; + } else if (p < min2) { + min2 = p; + } + } + + const leftover = money - min1 - min2; + return leftover >= 0 ? leftover : money; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/calculate-money-in-leetcode-bank.md b/articles/calculate-money-in-leetcode-bank.md new file mode 100644 index 000000000..1735a4724 --- /dev/null +++ b/articles/calculate-money-in-leetcode-bank.md @@ -0,0 +1,248 @@ +## 1. Simulation + +::tabs-start + +```python +class Solution: + def totalMoney(self, n: int) -> int: + day, deposit = 0, 1 + res = 0 + + while day < n: + res += deposit + deposit += 1 + day += 1 + + if day % 7 == 0: + deposit = 1 + day // 7 + + return res +``` + +```java +public class Solution { + public int totalMoney(int n) { + int day = 0, deposit = 1, res = 0; + + while (day < n) { + res += deposit; + deposit++; + day++; + + if (day % 7 == 0) { + deposit = 1 + day / 7; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int totalMoney(int n) { + int day = 0, deposit = 1, res = 0; + + while (day < n) { + res += deposit; + deposit++; + day++; + + if (day % 7 == 0) { + deposit = 1 + day / 7; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + totalMoney(n) { + let day = 0, deposit = 1, res = 0; + + while (day < n) { + res += deposit; + deposit++; + day++; + + if (day % 7 === 0) { + deposit = 1 + Math.floor(day / 7); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Math + +::tabs-start + +```python +class Solution: + def totalMoney(self, n: int) -> int: + weeks = n // 7 + low = 28 + high = 28 + 7 * (weeks - 1) + res = weeks * (low + high) // 2 + + monday = weeks + 1 + for i in range(n % 7): + res += i + monday + + return res +``` + +```java +public class Solution { + public int totalMoney(int n) { + int weeks = n / 7; + int low = 28; + int high = 28 + 7 * (weeks - 1); + int res = weeks * (low + high) / 2; + + int monday = weeks + 1; + for (int i = 0; i < n % 7; i++) { + res += i + monday; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int totalMoney(int n) { + int weeks = n / 7; + int low = 28; + int high = 28 + 7 * (weeks - 1); + int res = weeks * (low + high) / 2; + + int monday = weeks + 1; + for (int i = 0; i < n % 7; i++) { + res += i + monday; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + totalMoney(n) { + const weeks = Math.floor(n / 7); + const low = 28; + const high = 28 + 7 * (weeks - 1); + let res = weeks * (low + high) / 2; + + const monday = weeks + 1; + for (let i = 0; i < n % 7; i++) { + res += i + monday; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 3. Math (Optimal) + +::tabs-start + +```python +class Solution: + def totalMoney(self, n: int) -> int: + SUM = lambda x: (x * (x + 1)) >> 1 + weeks = n // 7 + res = SUM(weeks - 1) * 7 + weeks * SUM(7) + res += SUM(n % 7) + weeks * (n % 7) + return res +``` + +```java +public class Solution { + public int totalMoney(int n) { + int weeks = n / 7; + int res = SUM(weeks - 1) * 7 + weeks * SUM(7); + res += SUM(n % 7) + weeks * (n % 7); + return res; + } + + private int SUM(int n) { + return (n * (n + 1)) / 2; + } +} +``` + +```cpp +class Solution { +public: + int totalMoney(int n) { + auto SUM = [](int x) { return (x * (x + 1)) / 2; }; + + int weeks = n / 7; + int res = SUM(weeks - 1) * 7 + weeks * SUM(7); + res += SUM(n % 7) + weeks * (n % 7); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + totalMoney(n) { + const SUM = x => (x * (x + 1)) / 2; + + const weeks = Math.floor(n / 7); + let res = SUM(weeks - 1) * 7 + weeks * SUM(7); + res += SUM(n % 7) + weeks * (n % 7); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/can-place-flowers.md b/articles/can-place-flowers.md new file mode 100644 index 000000000..7ccaeba96 --- /dev/null +++ b/articles/can-place-flowers.md @@ -0,0 +1,178 @@ +## 1. Iteration - I + +::tabs-start + +```python +class Solution: + def canPlaceFlowers(self, flowerbed: List[int], n: int) -> bool: + f = [0] + flowerbed + [0] + + for i in range(1, len(f) - 1): + if f[i - 1] == 0 and f[i] == 0 and f[i + 1] == 0: + f[i] = 1 + n -= 1 + + return n <= 0 +``` + +```java +public class Solution { + public boolean canPlaceFlowers(int[] flowerbed, int n) { + int[] f = new int[flowerbed.length + 2]; + for (int i = 0; i < flowerbed.length; i++) { + f[i + 1] = flowerbed[i]; + } + + for (int i = 1; i < f.length - 1; i++) { + if (f[i - 1] == 0 && f[i] == 0 && f[i + 1] == 0) { + f[i] = 1; + n--; + } + } + return n <= 0; + } +} +``` + +```cpp +class Solution { +public: + bool canPlaceFlowers(vector& flowerbed, int n) { + vector f(flowerbed.size() + 2, 0); + for (int i = 0; i < flowerbed.size(); i++) { + f[i + 1] = flowerbed[i]; + } + + for (int i = 1; i < f.size() - 1; i++) { + if (f[i - 1] == 0 && f[i] == 0 && f[i + 1] == 0) { + f[i] = 1; + n--; + } + } + return n <= 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} flowerbed + * @param {number} n + * @return {boolean} + */ + canPlaceFlowers(flowerbed, n) { + const f = [0, ...flowerbed, 0]; + + for (let i = 1; i < f.length - 1; i++) { + if (f[i - 1] === 0 && f[i] === 0 && f[i + 1] === 0) { + f[i] = 1; + n--; + } + } + return n <= 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Iteration - II + +::tabs-start + +```python +class Solution: + def canPlaceFlowers(self, flowerbed: List[int], n: int) -> bool: + empty = 0 if flowerbed[0] else 1 + + for f in flowerbed: + if f: + n -= int((empty - 1) / 2) + empty = 0 + else: + empty += 1 + + n -= empty // 2 + return n <= 0 +``` + +```java +public class Solution { + public boolean canPlaceFlowers(int[] flowerbed, int n) { + int empty = flowerbed[0] == 0 ? 1 : 0; + + for (int f : flowerbed) { + if (f == 1) { + n -= (empty - 1) / 2; + empty = 0; + } else { + empty++; + } + } + + n -= empty / 2; + return n <= 0; + } +} +``` + +```cpp +class Solution { +public: + bool canPlaceFlowers(vector& flowerbed, int n) { + int empty = flowerbed[0] == 0 ? 1 : 0; + + for (int f : flowerbed) { + if (f == 1) { + n -= (empty - 1) / 2; + empty = 0; + } else { + empty++; + } + } + + n -= empty / 2; + return n <= 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} flowerbed + * @param {number} n + * @return {boolean} + */ + canPlaceFlowers(flowerbed, n) { + let empty = flowerbed[0] === 0 ? 1 : 0; + + for (let f of flowerbed) { + if (f === 1) { + n -= Math.floor(Math.max(0, empty - 1) / 2); + empty = 0; + } else { + empty++; + } + } + + n -= Math.floor(empty / 2); + return n <= 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/candy.md b/articles/candy.md new file mode 100644 index 000000000..adeba0a66 --- /dev/null +++ b/articles/candy.md @@ -0,0 +1,399 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def candy(self, ratings: List[int]) -> int: + n = len(ratings) + arr = [1] * n + + for i in range(n - 1): + if ratings[i] == ratings[i + 1]: + continue + if ratings[i + 1] > ratings[i]: + arr[i + 1] = arr[i] + 1 + elif arr[i] == arr[i + 1]: + arr[i + 1] = arr[i] + arr[i] += 1 + for j in range(i - 1, -1, -1): + if ratings[j] > ratings[j + 1]: + if arr[j + 1] < arr[j]: + break + arr[j] += 1 + + return sum(arr) +``` + +```java +public class Solution { + public int candy(int[] ratings) { + int n = ratings.length; + int[] arr = new int[n]; + for (int i = 0; i < n; i++) { + arr[i] = 1; + } + + for (int i = 0; i < n - 1; i++) { + if (ratings[i] == ratings[i + 1]) { + continue; + } + if (ratings[i + 1] > ratings[i]) { + arr[i + 1] = arr[i] + 1; + } else if (arr[i] == arr[i + 1]) { + arr[i + 1] = arr[i]; + arr[i]++; + for (int j = i - 1; j >= 0; j--) { + if (ratings[j] > ratings[j + 1]) { + if (arr[j + 1] < arr[j]) { + break; + } + arr[j]++; + } + } + } + } + + int sum = 0; + for (int num : arr) { + sum += num; + } + return sum; + } +} +``` + +```cpp +class Solution { +public: + int candy(vector& ratings) { + int n = ratings.size(); + vector arr(n, 1); + + for (int i = 0; i < n - 1; i++) { + if (ratings[i] == ratings[i + 1]) { + continue; + } + if (ratings[i + 1] > ratings[i]) { + arr[i + 1] = arr[i] + 1; + } else if (arr[i] == arr[i + 1]) { + arr[i + 1] = arr[i]; + arr[i]++; + for (int j = i - 1; j >= 0; j--) { + if (ratings[j] > ratings[j + 1]) { + if (arr[j + 1] < arr[j]) { + break; + } + arr[j]++; + } + } + } + } + + return accumulate(arr.begin(), arr.end(), 0); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} ratings + * @return {number} + */ + candy(ratings) { + const n = ratings.length; + const arr = new Array(n).fill(1); + + for (let i = 0; i < n - 1; i++) { + if (ratings[i] === ratings[i + 1]) { + continue; + } + if (ratings[i + 1] > ratings[i]) { + arr[i + 1] = arr[i] + 1; + } else if (arr[i] === arr[i + 1]) { + arr[i + 1] = arr[i]; + arr[i]++; + for (let j = i - 1; j >= 0; j--) { + if (ratings[j] > ratings[j + 1]) { + if (arr[j + 1] < arr[j]) { + break; + } + arr[j]++; + } + } + } + } + + return arr.reduce((sum, num) => sum + num, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Greedy (Two Pass) + +::tabs-start + +```python +class Solution: + def candy(self, ratings: List[int]) -> int: + n = len(ratings) + arr = [1] * n + + for i in range(1, n): + if ratings[i - 1] < ratings[i]: + arr[i] = arr[i - 1] + 1 + + for i in range(n - 2, -1, -1): + if ratings[i] > ratings[i + 1]: + arr[i] = max(arr[i], arr[i + 1] + 1) + + return sum(arr) +``` + +```java +public class Solution { + public int candy(int[] ratings) { + int n = ratings.length; + int[] arr = new int[n]; + Arrays.fill(arr, 1); + + for (int i = 1; i < n; i++) { + if (ratings[i - 1] < ratings[i]) { + arr[i] = arr[i - 1] + 1; + } + } + + for (int i = n - 2; i >= 0; i--) { + if (ratings[i] > ratings[i + 1]) { + arr[i] = Math.max(arr[i], arr[i + 1] + 1); + } + } + + int sum = 0; + for (int num : arr) { + sum += num; + } + return sum; + } +} +``` + +```cpp +class Solution { +public: + int candy(vector& ratings) { + int n = ratings.size(); + vector arr(n, 1); + + for (int i = 1; i < n; i++) { + if (ratings[i - 1] < ratings[i]) { + arr[i] = arr[i - 1] + 1; + } + } + + for (int i = n - 2; i >= 0; i--) { + if (ratings[i] > ratings[i + 1]) { + arr[i] = max(arr[i], arr[i + 1] + 1); + } + } + + return accumulate(arr.begin(), arr.end(), 0); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} ratings + * @return {number} + */ + candy(ratings) { + const n = ratings.length; + const arr = new Array(n).fill(1); + + for (let i = 1; i < n; i++) { + if (ratings[i - 1] < ratings[i]) { + arr[i] = arr[i - 1] + 1; + } + } + + for (let i = n - 2; i >= 0; i--) { + if (ratings[i] > ratings[i + 1]) { + arr[i] = Math.max(arr[i], arr[i + 1] + 1); + } + } + + return arr.reduce((sum, num) => sum + num, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Greedy (One Pass) + +::tabs-start + +```python +class Solution: + def candy(self, ratings: List[int]) -> int: + n = len(ratings) + res = n + + i = 1 + while i < n: + if ratings[i] == ratings[i - 1]: + i += 1 + continue + + inc = 0 + while i < n and ratings[i] > ratings[i - 1]: + inc += 1 + res += inc + i += 1 + + dec = 0 + while i < n and ratings[i] < ratings[i - 1]: + dec += 1 + res += dec + i += 1 + + res -= min(inc, dec) + + return res +``` + +```java +public class Solution { + public int candy(int[] ratings) { + int n = ratings.length; + int res = n; + + int i = 1; + while (i < n) { + if (ratings[i] == ratings[i - 1]) { + i++; + continue; + } + + int inc = 0; + while (i < n && ratings[i] > ratings[i - 1]) { + inc++; + res += inc; + i++; + } + + int dec = 0; + while (i < n && ratings[i] < ratings[i - 1]) { + dec++; + res += dec; + i++; + } + + res -= Math.min(inc, dec); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int candy(vector& ratings) { + int n = ratings.size(); + int res = n; + + int i = 1; + while (i < n) { + if (ratings[i] == ratings[i - 1]) { + i++; + continue; + } + + int inc = 0; + while (i < n && ratings[i] > ratings[i - 1]) { + inc++; + res += inc; + i++; + } + + int dec = 0; + while (i < n && ratings[i] < ratings[i - 1]) { + dec++; + res += dec; + i++; + } + + res -= min(inc, dec); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} ratings + * @return {number} + */ + candy(ratings) { + const n = ratings.length; + let res = n; + + let i = 1; + while (i < n) { + if (ratings[i] === ratings[i - 1]) { + i++; + continue; + } + + let inc = 0; + while (i < n && ratings[i] > ratings[i - 1]) { + inc++; + res += inc; + i++; + } + + let dec = 0; + while (i < n && ratings[i] < ratings[i - 1]) { + dec++; + res += dec; + i++; + } + + res -= Math.min(inc, dec); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/capacity-to-ship-packages-within-d-days.md b/articles/capacity-to-ship-packages-within-d-days.md new file mode 100644 index 000000000..dfc4f2146 --- /dev/null +++ b/articles/capacity-to-ship-packages-within-d-days.md @@ -0,0 +1,327 @@ +## 1. Linear Search + +::tabs-start + +```python +class Solution: + def shipWithinDays(self, weights: List[int], days: int) -> int: + res = max(weights) + while True: + ships = 1 + cap = res + for w in weights: + if cap - w < 0: + ships += 1 + cap = res + cap -= w + + if ships <= days: + return res + + res += 1 +``` + +```java +public class Solution { + public int shipWithinDays(int[] weights, int days) { + int res = 0; + for (int weight : weights) { + res = Math.max(res, weight); + } + while (true) { + int ships = 1; + int cap = res; + for (int weight : weights) { + if (cap - weight < 0) { + ships++; + cap = res; + } + cap -= weight; + } + if (ships <= days) { + return res; + } + res++; + } + } +} +``` + +```cpp +class Solution { +public: + int shipWithinDays(vector& weights, int days) { + int res = *max_element(weights.begin(), weights.end()); + while (true) { + int ships = 1, cap = res; + for (int w : weights) { + if (cap - w < 0) { + ships++; + cap = res; + } + cap -= w; + } + if (ships <= days) { + return res; + } + res++; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} weights + * @param {number} days + * @return {number} + */ + shipWithinDays(weights, days) { + let res = Math.max(...weights); + while (true) { + let ships = 1, cap = res; + for (let w of weights) { + if (cap - w < 0) { + ships++; + cap = res; + } + cap -= w; + } + if (ships <= days) { + return res; + } + res++; + } + } +} +``` + +```csharp +public class Solution { + public int ShipWithinDays(int[] weights, int days) { + int res = weights.Max(); + while (true) { + int ships = 1; + int cap = res; + foreach (int w in weights) { + if (cap - w < 0) { + ships++; + cap = res; + } + cap -= w; + } + if (ships <= days) { + return res; + } + res++; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Binary Search + +::tabs-start + +```python +class Solution: + def shipWithinDays(self, weights: List[int], days: int) -> int: + l, r = max(weights), sum(weights) + res = r + + def canShip(cap): + ships, currCap = 1, cap + for w in weights: + if currCap - w < 0: + ships += 1 + if ships > days: + return False + currCap = cap + + currCap -= w + return True + + while l <= r: + cap = (l + r) // 2 + if canShip(cap): + res = min(res, cap) + r = cap - 1 + else: + l = cap + 1 + + return res +``` + +```java +public class Solution { + public int shipWithinDays(int[] weights, int days) { + int l = 0, r = 0; + for (int w : weights) { + l = Math.max(l, w); + r += w; + } + int res = r; + + while (l <= r) { + int cap = (l + r) / 2; + if (canShip(weights, days, cap)) { + res = Math.min(res, cap); + r = cap - 1; + } else { + l = cap + 1; + } + } + return res; + } + + private boolean canShip(int[] weights, int days, int cap) { + int ships = 1, currCap = cap; + for (int w : weights) { + if (currCap - w < 0) { + ships++; + if (ships > days) { + return false; + } + currCap = cap; + } + currCap -= w; + } + return true; + } +} +``` + +```cpp +class Solution { +public: + int shipWithinDays(vector& weights, int days) { + int l = *max_element(weights.begin(), weights.end()); + int r = accumulate(weights.begin(), weights.end(), 0); + int res = r; + + while (l <= r) { + int cap = (l + r) / 2; + if (canShip(weights, days, cap)) { + res = min(res, cap); + r = cap - 1; + } else { + l = cap + 1; + } + } + return res; + } + +private: + bool canShip(const vector& weights, int days, int cap) { + int ships = 1, currCap = cap; + for (int w : weights) { + if (currCap - w < 0) { + ships++; + if (ships > days) { + return false; + } + currCap = cap; + } + currCap -= w; + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} weights + * @param {number} days + * @return {number} + */ + shipWithinDays(weights, days) { + let l = Math.max(...weights); + let r = weights.reduce((a, b) => a + b, 0); + let res = r; + + const canShip = (cap) => { + let ships = 1, currCap = cap; + for (const w of weights) { + if (currCap - w < 0) { + ships++; + if (ships > days) { + return false; + } + currCap = cap; + } + currCap -= w; + } + return true; + }; + + while (l <= r) { + const cap = Math.floor((l + r) / 2); + if (canShip(cap)) { + res = Math.min(res, cap); + r = cap - 1; + } else { + l = cap + 1; + } + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int ShipWithinDays(int[] weights, int days) { + int l = weights.Max(); + int r = weights.Sum(); + int res = r; + + bool CanShip(int cap) { + int ships = 1; + int currCap = cap; + + foreach (int w in weights) { + if (currCap - w < 0) { + ships++; + if (ships > days) return false; + currCap = cap; + } + currCap -= w; + } + + return true; + } + + while (l <= r) { + int cap = (l + r) / 2; + if (CanShip(cap)) { + res = Math.Min(res, cap); + r = cap - 1; + } else { + l = cap + 1; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/car-fleet.md b/articles/car-fleet.md new file mode 100644 index 000000000..22c7a9d0f --- /dev/null +++ b/articles/car-fleet.md @@ -0,0 +1,379 @@ +## 1. Stack + +::tabs-start + +```python +class Solution: + def carFleet(self, target: int, position: List[int], speed: List[int]) -> int: + pair = [(p, s) for p, s in zip(position, speed)] + pair.sort(reverse=True) + stack = [] + for p, s in pair: # Reverse Sorted Order + stack.append((target - p) / s) + if len(stack) >= 2 and stack[-1] <= stack[-2]: + stack.pop() + return len(stack) +``` + +```java +public class Solution { + public int carFleet(int target, int[] position, int[] speed) { + int[][] pair = new int[position.length][2]; + for (int i = 0; i < position.length; i++) { + pair[i][0] = position[i]; + pair[i][1] = speed[i]; + } + Arrays.sort(pair, (a, b) -> Integer.compare(b[0], a[0])); + Stack stack = new Stack<>(); + for (int[] p : pair) { + stack.push((double) (target - p[0]) / p[1]); + if (stack.size() >= 2 && + stack.peek() <= stack.get(stack.size() - 2)) + { + stack.pop(); + } + } + return stack.size(); + } +} +``` + +```cpp +class Solution { +public: + int carFleet(int target, vector& position, vector& speed) { + vector> pair; + for (int i = 0; i < position.size(); i++) { + pair.push_back({position[i], speed[i]}); + } + sort(pair.rbegin(), pair.rend()); + vector stack; + for (auto& p : pair) { + stack.push_back((double)(target - p.first) / p.second); + if (stack.size() >= 2 && + stack.back() <= stack[stack.size() - 2]) + { + stack.pop_back(); + } + } + return stack.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} target + * @param {number[]} position + * @param {number[]} speed + * @return {number} + */ + carFleet(target, position, speed) { + let pair = position.map((p, i) => [p, speed[i]]); + pair.sort((a, b) => b[0] - a[0]); + let stack = []; + for (let [p, s] of pair) { + stack.push((target - p) / s); + if (stack.length >= 2 && + stack[stack.length - 1] <= stack[stack.length - 2]) + { + stack.pop(); + } + } + return stack.length; + } +} +``` + +```csharp +public class Solution { + public int CarFleet(int target, int[] position, int[] speed) { + int[][] pair = new int[position.Length][]; + for (int i = 0; i < position.Length; i++) { + pair[i] = new int[] { position[i], speed[i] }; + } + Array.Sort(pair, (a, b) => b[0].CompareTo(a[0])); + Stack stack = new Stack(); + foreach (var p in pair) { + stack.Push((double)(target - p[0]) / p[1]); + if (stack.Count >= 2 && stack.Peek() <= stack.ElementAt(1)) { + stack.Pop(); + } + } + return stack.Count; + } +} +``` + +```go +func carFleet(target int, position []int, speed []int) int { + n := len(position) + pair := make([][2]int, n) + for i := 0; i < n; i++ { + pair[i] = [2]int{position[i], speed[i]} + } + + sort.Slice(pair, func(i, j int) bool { + return pair[i][0] > pair[j][0] + }) + + stack := []float64{} + for _, p := range pair { + time := float64(target - p[0]) / float64(p[1]) + stack = append(stack, time) + if len(stack) >= 2 && stack[len(stack)-1] <= stack[len(stack)-2] { + stack = stack[:len(stack)-1] + } + } + + return len(stack) +} +``` + +```kotlin +class Solution { + fun carFleet(target: Int, position: IntArray, speed: IntArray): Int { + val pair = position.zip(speed).sortedByDescending { it.first } + val stack = mutableListOf() + + for ((p, s) in pair) { + val time = (target - p).toDouble() / s + stack.add(time) + if (stack.size >= 2 && stack[stack.size - 1] <= stack[stack.size - 2]) { + stack.removeAt(stack.size - 1) + } + } + + return stack.size + } +} +``` + +```swift +class Solution { + func carFleet(_ target: Int, _ position: [Int], _ speed: [Int]) -> Int { + var pair = zip(position, speed).map { ($0, $1) } + pair.sort { $0.0 > $1.0 } // Sort in descending order by position + + var stack = [Double]() + + for (p, s) in pair { + stack.append(Double(target - p) / Double(s)) + if stack.count >= 2 && stack.last! <= stack[stack.count - 2] { + stack.removeLast() + } + } + + return stack.count + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Iteration + +::tabs-start + +```python +class Solution: + def carFleet(self, target: int, position: List[int], speed: List[int]) -> int: + pair = [(p, s) for p, s in zip(position, speed)] + pair.sort(reverse=True) + + fleets = 1 + prevTime = (target - pair[0][0]) / pair[0][1] + for i in range(1, len(pair)): + currCar = pair[i] + currTime = (target - currCar[0]) / currCar[1] + if currTime > prevTime: + fleets += 1 + prevTime = currTime + return fleets +``` + +```java +public class Solution { + public int carFleet(int target, int[] position, int[] speed) { + int n = position.length; + int[][] pair = new int[n][2]; + for (int i = 0; i < n; i++) { + pair[i][0] = position[i]; + pair[i][1] = speed[i]; + } + Arrays.sort(pair, (a, b) -> Integer.compare(b[0], a[0])); + + int fleets = 1; + double prevTime = (double)(target - pair[0][0]) / pair[0][1]; + for (int i = 1; i < n; i++) { + double currTime = (double)(target - pair[i][0]) / pair[i][1]; + if (currTime > prevTime) { + fleets++; + prevTime = currTime; + } + } + return fleets; + } +} +``` + +```cpp +class Solution { +public: + int carFleet(int target, vector& position, vector& speed) { + int n = position.size(); + vector> pair; + for (int i = 0; i < n; i++) { + pair.push_back({position[i], speed[i]}); + } + sort(pair.rbegin(), pair.rend()); + + int fleets = 1; + double prevTime = (double)(target - pair[0].first) / pair[0].second; + for (int i = 1; i < n; i++) { + double currTime = (double)(target - pair[i].first) / pair[i].second; + if (currTime > prevTime) { + fleets++; + prevTime = currTime; + } + } + return fleets; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} target + * @param {number[]} position + * @param {number[]} speed + * @return {number} + */ + carFleet(target, position, speed) { + let pair = position.map((p, i) => [p, speed[i]]); + pair.sort((a, b) => b[0] - a[0]); + + let fleets = 1; + let prevTime = (target - pair[0][0]) / pair[0][1]; + for (let i = 1; i < pair.length; i++) { + let currTime = (target - pair[i][0]) / pair[i][1]; + if (currTime > prevTime) { + fleets++; + prevTime = currTime; + } + } + return fleets; + } +} +``` + +```csharp +public class Solution { + public int CarFleet(int target, int[] position, int[] speed) { + int n = position.Length; + int[][] pair = new int[n][]; + for (int i = 0; i < n; i++) { + pair[i] = new int[] { position[i], speed[i] }; + } + Array.Sort(pair, (a, b) => b[0].CompareTo(a[0])); + + int fleets = 1; + double prevTime = (double)(target - pair[0][0]) / pair[0][1]; + for (int i = 1; i < n; i++) { + double currTime = (double)(target - pair[i][0]) / pair[i][1]; + if (currTime > prevTime) { + fleets++; + prevTime = currTime; + } + } + return fleets; + } +} +``` + +```go +func carFleet(target int, position []int, speed []int) int { + n := len(position) + pair := make([][2]int, n) + for i := 0; i < n; i++ { + pair[i] = [2]int{position[i], speed[i]} + } + + sort.Slice(pair, func(i, j int) bool { + return pair[i][0] > pair[j][0] + }) + + fleets := 1 + prevTime := float64(target - pair[0][0]) / float64(pair[0][1]) + for i := 1; i < n; i++ { + currTime := float64(target - pair[i][0]) / float64(pair[i][1]) + if currTime > prevTime { + fleets++ + prevTime = currTime + } + } + + return fleets +} +``` + +```kotlin +class Solution { + fun carFleet(target: Int, position: IntArray, speed: IntArray): Int { + val pair = position.zip(speed).sortedByDescending { it.first } + + var fleets = 1 + var prevTime = (target - pair[0].first).toDouble() / pair[0].second + for (i in 1 until pair.size) { + val (p, s) = pair[i] + val currTime = (target - p).toDouble() / s + if (currTime > prevTime) { + fleets++ + prevTime = currTime + } + } + + return fleets + } +} +``` + +```swift +class Solution { + func carFleet(_ target: Int, _ position: [Int], _ speed: [Int]) -> Int { + var pair = zip(position, speed).map { ($0, $1) } + pair.sort { $0.0 > $1.0 } // Sort in descending order by position + + var fleets = 1 + var prevTime = Double(target - pair[0].0) / Double(pair[0].1) + + for i in 1.. prevTime { + fleets += 1 + prevTime = currTime + } + } + + return fleets + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/car-pooling.md b/articles/car-pooling.md new file mode 100644 index 000000000..df5745d6d --- /dev/null +++ b/articles/car-pooling.md @@ -0,0 +1,585 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def carPooling(self, trips: List[List[int]], capacity: int) -> bool: + trips.sort(key=lambda x: x[1]) + + for i in range(len(trips)): + curPass = trips[i][0] + for j in range(i): + if trips[j][2] > trips[i][1]: + curPass += trips[j][0] + if curPass > capacity: + return False + + return True +``` + +```java +public class Solution { + public boolean carPooling(int[][] trips, int capacity) { + Arrays.sort(trips, (a, b) -> Integer.compare(a[1], b[1])); + + for (int i = 0; i < trips.length; i++) { + int curPass = trips[i][0]; + for (int j = 0; j < i; j++) { + if (trips[j][2] > trips[i][1]) { + curPass += trips[j][0]; + } + } + if (curPass > capacity) { + return false; + } + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool carPooling(vector>& trips, int capacity) { + sort(trips.begin(), trips.end(), [](const vector& a, const vector& b) { + return a[1] < b[1]; + }); + + for (int i = 0; i < trips.size(); i++) { + int curPass = trips[i][0]; + for (int j = 0; j < i; j++) { + if (trips[j][2] > trips[i][1]) { + curPass += trips[j][0]; + } + } + if (curPass > capacity) { + return false; + } + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} trips + * @param {number} capacity + * @return {boolean} + */ + carPooling(trips, capacity) { + trips.sort((a, b) => a[1] - b[1]); + + for (let i = 0; i < trips.length; i++) { + let curPass = trips[i][0]; + for (let j = 0; j < i; j++) { + if (trips[j][2] > trips[i][1]) { + curPass += trips[j][0]; + } + } + if (curPass > capacity) { + return false; + } + } + + return true; + } +} +``` + +```csharp +public class Solution { + public bool CarPooling(int[][] trips, int capacity) { + Array.Sort(trips, (a, b) => a[1].CompareTo(b[1])); + + for (int i = 0; i < trips.Length; i++) { + int curPass = trips[i][0]; + for (int j = 0; j < i; j++) { + if (trips[j][2] > trips[i][1]) { + curPass += trips[j][0]; + } + } + if (curPass > capacity) { + return false; + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Min-Heap + +::tabs-start + +```python +class Solution: + def carPooling(self, trips: List[List[int]], capacity: int) -> bool: + trips.sort(key=lambda t: t[1]) + + minHeap = [] # pair of [end, numPassengers] + curPass = 0 + + for numPass, start, end in trips: + while minHeap and minHeap[0][0] <= start: + curPass -= heapq.heappop(minHeap)[1] + + curPass += numPass + if curPass > capacity: + return False + + heapq.heappush(minHeap, [end, numPass]) + + return True +``` + +```java +public class Solution { + public boolean carPooling(int[][] trips, int capacity) { + Arrays.sort(trips, Comparator.comparingInt(a -> a[1])); + + PriorityQueue minHeap = new PriorityQueue<>(Comparator.comparingInt(a -> a[0])); // [end, numPassengers] + int curPass = 0; + + for (int[] trip : trips) { + int numPass = trip[0], start = trip[1], end = trip[2]; + + while (!minHeap.isEmpty() && minHeap.peek()[0] <= start) { + curPass -= minHeap.poll()[1]; + } + + curPass += numPass; + if (curPass > capacity) { + return false; + } + + minHeap.offer(new int[]{end, numPass}); + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool carPooling(vector>& trips, int capacity) { + sort(trips.begin(), trips.end(), [](const vector& a, const vector& b) { + return a[1] < b[1]; + }); + + priority_queue, vector>, greater<>> minHeap; // [end, numPassengers] + int curPass = 0; + + for (const auto& trip : trips) { + int numPass = trip[0], start = trip[1], end = trip[2]; + + while (!minHeap.empty() && minHeap.top().first <= start) { + curPass -= minHeap.top().second; + minHeap.pop(); + } + + curPass += numPass; + if (curPass > capacity) { + return false; + } + + minHeap.emplace(end, numPass); + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} trips + * @param {number} capacity + * @return {boolean} + */ + carPooling(trips, capacity) { + trips.sort((a, b) => a[1] - b[1]); + + const minHeap = new MinPriorityQueue(x => x[0]); // [end, numPassengers] + let curPass = 0; + + for (const [numPass, start, end] of trips) { + while (!minHeap.isEmpty() && minHeap.front()[0] <= start) { + curPass -= minHeap.dequeue()[1]; + } + + curPass += numPass; + if (curPass > capacity) { + return false; + } + + minHeap.enqueue([end, numPass]); + } + + return true; + } +} +``` + +```csharp +public class Solution { + public bool CarPooling(int[][] trips, int capacity) { + Array.Sort(trips, (a, b) => a[1].CompareTo(b[1])); + + var minHeap = new PriorityQueue(); + int curPass = 0; + + foreach (var trip in trips) { + int numPass = trip[0]; + int start = trip[1]; + int end = trip[2]; + + while (minHeap.Count > 0 && minHeap.Peek()[0] <= start) { + curPass -= minHeap.Dequeue()[1]; + } + + curPass += numPass; + if (curPass > capacity) { + return false; + } + + minHeap.Enqueue(new int[] { end, numPass }, end); + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Line Sweep - I + +::tabs-start + +```python +class Solution: + def carPooling(self, trips: List[List[int]], capacity: int) -> bool: + points = [] + for passengers, start, end in trips: + points.append([start, passengers]) + points.append([end, -passengers]) + + points.sort() + curPass = 0 + for point, passengers in points: + curPass += passengers + if curPass > capacity: + return False + + return True +``` + +```java +public class Solution { + public boolean carPooling(int[][] trips, int capacity) { + List points = new ArrayList<>(); + for (int[] trip : trips) { + int passengers = trip[0], start = trip[1], end = trip[2]; + points.add(new int[]{start, passengers}); + points.add(new int[]{end, -passengers}); + } + + points.sort((a, b) -> a[0] == b[0] ? Integer.compare(a[1], b[1]) : Integer.compare(a[0], b[0])); + + int curPass = 0; + for (int[] point : points) { + curPass += point[1]; + if (curPass > capacity) { + return false; + } + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool carPooling(vector>& trips, int capacity) { + vector> points; + for (const auto& trip : trips) { + int passengers = trip[0], start = trip[1], end = trip[2]; + points.emplace_back(start, passengers); + points.emplace_back(end, -passengers); + } + + sort(points.begin(), points.end(), [](const pair& a, const pair& b) { + return a.first == b.first ? a.second < b.second : a.first < b.first; + }); + + int curPass = 0; + for (const auto& point : points) { + curPass += point.second; + if (curPass > capacity) { + return false; + } + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} trips + * @param {number} capacity + * @return {boolean} + */ + carPooling(trips, capacity) { + const points = []; + for (const [passengers, start, end] of trips) { + points.push([start, passengers]); + points.push([end, -passengers]); + } + + points.sort((a, b) => a[0] === b[0] ? a[1] - b[1] : a[0] - b[0]); + + let curPass = 0; + for (const [point, passengers] of points) { + curPass += passengers; + if (curPass > capacity) { + return false; + } + } + + return true; + } +} +``` + +```csharp +public class Solution { + public bool CarPooling(int[][] trips, int capacity) { + List points = new List(); + + foreach (var trip in trips) { + int passengers = trip[0]; + int start = trip[1]; + int end = trip[2]; + + points.Add(new int[] { start, passengers }); + points.Add(new int[] { end, -passengers }); + } + + points.Sort((a, b) => { + if (a[0] == b[0]) return a[1] - b[1]; + return a[0] - b[0]; + }); + + int curPass = 0; + foreach (var point in points) { + curPass += point[1]; + if (curPass > capacity) return false; + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Line Sweep - II + +::tabs-start + +```python +class Solution: + def carPooling(self, trips: List[List[int]], capacity: int) -> bool: + L, R = float("inf"), float("-inf") + for _, start, end in trips: + L = min(L, start) + R = max(R, end) + + N = R - L + 1 + passChange = [0] * (N + 1) + for passengers, start, end in trips: + passChange[start - L] += passengers + passChange[end - L] -= passengers + + curPass = 0 + for change in passChange: + curPass += change + if curPass > capacity: + return False + + return True +``` + +```java +public class Solution { + public boolean carPooling(int[][] trips, int capacity) { + int L = Integer.MAX_VALUE, R = Integer.MIN_VALUE; + for (int[] trip : trips) { + L = Math.min(L, trip[1]); + R = Math.max(R, trip[2]); + } + + int N = R - L + 1; + int[] passChange = new int[N + 1]; + for (int[] trip : trips) { + passChange[trip[1] - L] += trip[0]; + passChange[trip[2] - L] -= trip[0]; + } + + int curPass = 0; + for (int change : passChange) { + curPass += change; + if (curPass > capacity) { + return false; + } + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool carPooling(vector>& trips, int capacity) { + int L = INT_MAX, R = INT_MIN; + for (const auto& trip : trips) { + L = min(L, trip[1]); + R = max(R, trip[2]); + } + + int N = R - L + 1; + vector passChange(N + 1, 0); + for (const auto& trip : trips) { + passChange[trip[1] - L] += trip[0]; + passChange[trip[2] - L] -= trip[0]; + } + + int curPass = 0; + for (int change : passChange) { + curPass += change; + if (curPass > capacity) { + return false; + } + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} trips + * @param {number} capacity + * @return {boolean} + */ + carPooling(trips, capacity) { + let L = Infinity, R = -Infinity; + for (const [passengers, start, end] of trips) { + L = Math.min(L, start); + R = Math.max(R, end); + } + + const N = R - L + 1; + const passChange = Array(N + 1).fill(0); + for (const [passengers, start, end] of trips) { + passChange[start - L] += passengers; + passChange[end - L] -= passengers; + } + + let curPass = 0; + for (const change of passChange) { + curPass += change; + if (curPass > capacity) { + return false; + } + } + + return true; + } +} +``` + +```csharp +public class Solution { + public bool CarPooling(int[][] trips, int capacity) { + int L = int.MaxValue, R = int.MinValue; + + foreach (var trip in trips) { + int start = trip[1], end = trip[2]; + L = Math.Min(L, start); + R = Math.Max(R, end); + } + + int N = R - L + 1; + int[] passChange = new int[N + 1]; + + foreach (var trip in trips) { + int passengers = trip[0]; + int start = trip[1]; + int end = trip[2]; + + passChange[start - L] += passengers; + passChange[end - L] -= passengers; + } + + int curPass = 0; + foreach (int change in passChange) { + curPass += change; + if (curPass > capacity) return false; + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + N)$ +* Space complexity: $O(N)$ + +> Where $n$ is the size of the array $trips$ and $N$ is the difference between the rightmost location and the leftmost location. \ No newline at end of file diff --git a/articles/champagne-tower.md b/articles/champagne-tower.md new file mode 100644 index 000000000..d2f36a28e --- /dev/null +++ b/articles/champagne-tower.md @@ -0,0 +1,562 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def champagneTower(self, poured: int, query_row: int, query_glass: int) -> float: + def rec(row, glass): + if row < 0 or glass < 0 or glass > row: + return 0 + + if row == 0 and glass == 0: + return poured + + left_parent = max(0, rec(row - 1, glass - 1) - 1) + right_parent = max(0, rec(row - 1, glass) - 1) + + return (left_parent + right_parent) / 2 + + return min(1, rec(query_row, query_glass)) +``` + +```java +public class Solution { + public double champagneTower(int poured, int query_row, int query_glass) { + return Math.min(1, rec(poured, query_row, query_glass)); + } + + private double rec(int poured, int row, int glass) { + if (row < 0 || glass < 0 || glass > row) { + return 0; + } + + if (row == 0 && glass == 0) { + return poured; + } + + double leftParent = Math.max(0, rec(poured, row - 1, glass - 1) - 1); + double rightParent = Math.max(0, rec(poured, row - 1, glass) - 1); + + return (leftParent + rightParent) / 2; + } +} +``` + +```cpp +class Solution { +public: + double champagneTower(int poured, int query_row, int query_glass) { + return min(1.0, rec(poured, query_row, query_glass)); + } + +private: + double rec(int poured, int row, int glass) { + if (row < 0 || glass < 0 || glass > row) { + return 0; + } + + if (row == 0 && glass == 0) { + return poured; + } + + double leftParent = max(0.0, rec(poured, row - 1, glass - 1) - 1); + double rightParent = max(0.0, rec(poured, row - 1, glass) - 1); + + return (leftParent + rightParent) / 2; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} poured + * @param {number} query_row + * @param {number} query_glass + * @return {number} + */ + champagneTower(poured, query_row, query_glass) { + const rec = (row, glass) => { + if (row < 0 || glass < 0 || glass > row) { + return 0; + } + + if (row === 0 && glass === 0) { + return poured; + } + + const leftParent = Math.max(0, rec(row - 1, glass - 1) - 1); + const rightParent = Math.max(0, rec(row - 1, glass) - 1); + + return (leftParent + rightParent) / 2; + }; + + return Math.min(1, rec(query_row, query_glass)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the given $queryRow$. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def champagneTower(self, poured: int, query_row: int, query_glass: int) -> float: + memo = { (0, 0) : poured } + + def rec(row, glass): + if row < 0 or glass < 0 or glass > row: + return 0 + if (row, glass) in memo: + return memo[(row, glass)] + + left_parent = max(0, rec(row - 1, glass - 1) - 1) + right_parent = max(0, rec(row - 1, glass) - 1) + + memo[(row, glass)] = (left_parent + right_parent) / 2 + return memo[(row, glass)] + + return min(1, rec(query_row, query_glass)) +``` + +```java +public class Solution { + public double champagneTower(int poured, int query_row, int query_glass) { + double[][] memo = new double[query_row + 5][]; + for (int i = 0; i < query_row + 5; i++) { + memo[i] = new double[i + 1]; + Arrays.fill(memo[i], -1); + } + memo[0][0] = poured; + + return Math.min(1, rec(memo, query_row, query_glass)); + } + + private double rec(double[][] memo, int row, int glass) { + if (row < 0 || glass < 0 || glass > row) { + return 0; + } + + if (memo[row][glass] != -1) { + return memo[row][glass]; + } + + double leftParent = Math.max(0, rec(memo, row - 1, glass - 1) - 1); + double rightParent = Math.max(0, rec(memo, row - 1, glass) - 1); + + memo[row][glass] = (leftParent + rightParent) / 2; + return memo[row][glass]; + } +} +``` + +```cpp +class Solution { +public: + double champagneTower(int poured, int query_row, int query_glass) { + vector> memo(query_row + 5); + for (int i = 0; i <= query_row + 4; i++) { + memo[i].resize(i + 1, -1); + } + + memo[0][0] = poured; + return min(1.0, rec(memo, query_row, query_glass)); + } + +private: + double rec(vector>& memo, int row, int glass) { + if (row < 0 || glass < 0 || glass > row) { + return 0; + } + + if (memo[row][glass] != -1) { + return memo[row][glass]; + } + + double leftParent = max(0.0, rec(memo, row - 1, glass - 1) - 1); + double rightParent = max(0.0, rec(memo, row - 1, glass) - 1); + + memo[row][glass] = (leftParent + rightParent) / 2; + return memo[row][glass]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} poured + * @param {number} query_row + * @param {number} query_glass + * @return {number} + */ + champagneTower(poured, query_row, query_glass) { + const memo = Array.from({ length: query_row + 5 }, (_, i) => Array(i + 1).fill(-1)); + memo[0][0] = poured; + + const rec = (row, glass) => { + if (row < 0 || glass < 0 || glass > row) { + return 0; + } + + if (memo[row][glass] != -1) { + return memo[row][glass]; + } + + const leftParent = Math.max(0, rec(row - 1, glass - 1) - 1); + const rightParent = Math.max(0, rec(row - 1, glass) - 1); + + memo[row][glass] = (leftParent + rightParent) / 2; + return memo[row][glass]; + + return (leftParent + rightParent) / 2; + }; + + return Math.min(1, rec(query_row, query_glass)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the given $queryRow$ and $m$ is the given $queryGlass$. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def champagneTower(self, poured: int, query_row: int, query_glass: int) -> float: + dp = [[0] * (i + 1) for i in range(query_row + 5)] + dp[0][0] += poured + + for row in range(min(99, query_row + 1)): + for glass in range(row + 1): + excess = (dp[row][glass] - 1.0) / 2.0 + if excess > 0: + dp[row + 1][glass] += excess + dp[row + 1][glass + 1] += excess + + return min(1.0, dp[query_row][query_glass]) +``` + +```java +public class Solution { + public double champagneTower(int poured, int query_row, int query_glass) { + double[][] dp = new double[query_row + 5][]; + for (int i = 0; i < query_row + 5; i++) { + dp[i] = new double[i + 1]; + } + + dp[0][0] += poured; + + for (int row = 0; row < Math.min(99, query_row + 1); row++) { + for (int glass = 0; glass <= row; glass++) { + double excess = (dp[row][glass] - 1.0) / 2.0; + if (excess > 0) { + dp[row + 1][glass] += excess; + dp[row + 1][glass + 1] += excess; + } + } + } + + return Math.min(1.0, dp[query_row][query_glass]); + } +} +``` + +```cpp +class Solution { +public: + double champagneTower(int poured, int query_row, int query_glass) { + vector> dp(query_row + 5); + for (int i = 0; i <= query_row + 4; i++) { + dp[i].resize(i + 1, 0); + } + + dp[0][0] += poured; + + for (int row = 0; row < min(99, query_row + 1); row++) { + for (int glass = 0; glass <= row; glass++) { + double excess = (dp[row][glass] - 1.0) / 2.0; + if (excess > 0) { + dp[row + 1][glass] += excess; + dp[row + 1][glass + 1] += excess; + } + } + } + + return min(1.0, dp[query_row][query_glass]); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} poured + * @param {number} query_row + * @param {number} query_glass + * @return {number} + */ + champagneTower(poured, query_row, query_glass) { + const dp = Array.from({ length: query_row + 5 }, (_, i) => Array(i + 1).fill(0)); + dp[0][0] += poured; + + for (let row = 0; row < Math.min(99, query_row + 1); row++) { + for (let glass = 0; glass <= row; glass++) { + const excess = (dp[row][glass] - 1.0) / 2.0; + if (excess > 0) { + dp[row + 1][glass] += excess; + dp[row + 1][glass + 1] += excess; + } + } + } + + return Math.min(1.0, dp[query_row][query_glass]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the given $queryRow$ and $m$ is the given $queryGlass$. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def champagneTower(self, poured: int, query_row: int, query_glass: int) -> float: + prev_row = [poured] # Flow + + for row in range(1, query_row + 1): + cur_row = [0] * (row + 1) + for i in range(row): + extra = prev_row[i] - 1 + if extra > 0: + cur_row[i] += 0.5 * extra + cur_row[i + 1] += 0.5 * extra + prev_row = cur_row + + return min(1, prev_row[query_glass]) +``` + +```java +public class Solution { + public double champagneTower(int poured, int query_row, int query_glass) { + double[] prev_row = new double[] { poured }; // Flow + + for (int row = 1; row <= query_row; row++) { + double[] cur_row = new double[row + 1]; + for (int i = 0; i < row; i++) { + double extra = prev_row[i] - 1; + if (extra > 0) { + cur_row[i] += 0.5 * extra; + cur_row[i + 1] += 0.5 * extra; + } + } + prev_row = cur_row; + } + + return Math.min(1.0, prev_row[query_glass]); + } +} +``` + +```cpp +class Solution { +public: + double champagneTower(int poured, int query_row, int query_glass) { + vector prev_row = {double(poured)}; // Flow + + for (int row = 1; row <= query_row; row++) { + vector cur_row(row + 1, 0); + for (int i = 0; i < row; i++) { + double extra = prev_row[i] - 1; + if (extra > 0) { + cur_row[i] += 0.5 * extra; + cur_row[i + 1] += 0.5 * extra; + } + } + prev_row = cur_row; + } + + return min(1.0, prev_row[query_glass]); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} poured + * @param {number} query_row + * @param {number} query_glass + * @return {number} + */ + champagneTower(poured, query_row, query_glass) { + let prev_row = [poured]; // Flow + + for (let row = 1; row <= query_row; row++) { + let cur_row = new Array(row + 1).fill(0); + for (let i = 0; i < row; i++) { + let extra = prev_row[i] - 1; + if (extra > 0) { + cur_row[i] += 0.5 * extra; + cur_row[i + 1] += 0.5 * extra; + } + } + prev_row = cur_row; + } + + return Math.min(1, prev_row[query_glass]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the given $queryRow$ and $m$ is the given $queryGlass$. + +--- + +## 5. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def champagneTower(self, poured: int, query_row: int, query_glass: int) -> float: + dp = [poured] + [0] * query_row + + for row in range(1, query_row + 1): + for i in range(row - 1, -1, -1): + extra = dp[i] - 1 + if extra > 0: + dp[i] = 0.5 * extra + dp[i + 1] += 0.5 * extra + else: + dp[i] = 0 + + return min(1, dp[query_glass]) +``` + +```java +public class Solution { + public double champagneTower(int poured, int query_row, int query_glass) { + double[] dp = new double[query_row + 1]; + dp[0] = poured; + + for (int row = 1; row <= query_row; row++) { + for (int i = row - 1; i >= 0; i--) { + double extra = dp[i] - 1; + if (extra > 0) { + dp[i] = 0.5 * extra; + dp[i + 1] += 0.5 * extra; + } else { + dp[i] = 0; + } + } + } + + return Math.min(1, dp[query_glass]); + } +} +``` + +```cpp +class Solution { +public: + double champagneTower(int poured, int query_row, int query_glass) { + vector dp(query_row + 1, 0); + dp[0] = poured; + + for (int row = 1; row <= query_row; row++) { + for (int i = row - 1; i >= 0; i--) { + double extra = dp[i] - 1; + if (extra > 0) { + dp[i] = 0.5 * extra; + dp[i + 1] += 0.5 * extra; + } else { + dp[i] = 0; + } + } + } + + return min(1.0, dp[query_glass]); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} poured + * @param {number} query_row + * @param {number} query_glass + * @return {number} + */ + champagneTower(poured, query_row, query_glass) { + let dp = new Array(query_row + 1).fill(0); + dp[0] = poured; + + for (let row = 1; row <= query_row; row++) { + for (let i = row - 1; i >= 0; i--) { + let extra = dp[i] - 1; + if (extra > 0) { + dp[i] = 0.5 * extra; + dp[i + 1] += 0.5 * extra; + } else { + dp[i] = 0; + } + } + } + + return Math.min(1, dp[query_glass]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the given $queryRow$ and $m$ is the given $queryGlass$. \ No newline at end of file diff --git a/articles/cheapest-flight-path.md b/articles/cheapest-flight-path.md new file mode 100644 index 000000000..976640249 --- /dev/null +++ b/articles/cheapest-flight-path.md @@ -0,0 +1,844 @@ +## 1. Dijkstra's Algorithm + +::tabs-start + +```python +class Solution: + def findCheapestPrice(self, n: int, flights: List[List[int]], src: int, dst: int, k: int) -> int: + INF = float("inf") + adj = [[] for _ in range(n)] + dist = [[INF] * (k + 5) for _ in range(n)] + for u, v, cst in flights: + adj[u].append([v, cst]) + + dist[src][0] = 0 + minHeap = [(0, src, -1)] # cost, node, stops + while len(minHeap): + cst, node, stops = heapq.heappop(minHeap) + if dst == node: return cst + if stops == k or dist[node][stops + 1] < cst: + continue + for nei, w in adj[node]: + nextCst = cst + w + nextStops = 1 + stops + if dist[nei][nextStops + 1] > nextCst: + dist[nei][nextStops + 1] = nextCst + heapq.heappush(minHeap, (nextCst, nei, nextStops)) + + return -1 +``` + +```java +public class Solution { + public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) { + int INF = Integer.MAX_VALUE; + List[] adj = new ArrayList[n]; + int[][] dist = new int[n][k + 5]; + for (int i = 0; i < n; i++) Arrays.fill(dist[i], INF); + + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + for (int[] flight : flights) { + adj[flight[0]].add(new int[]{flight[1], flight[2]}); + } + + dist[src][0] = 0; + PriorityQueue minHeap = new PriorityQueue<>( + Comparator.comparingInt(a -> a[0]) + ); + minHeap.offer(new int[]{0, src, -1}); + + while (!minHeap.isEmpty()) { + int[] top = minHeap.poll(); + int cst = top[0], node = top[1], stops = top[2]; + if (node == dst) return cst; + if (stops == k || dist[node][stops + 1] < cst) continue; + for (int[] neighbor : adj[node]) { + int nei = neighbor[0], w = neighbor[1]; + int nextCst = cst + w; + int nextStops = stops + 1; + if (dist[nei][nextStops + 1] > nextCst) { + dist[nei][nextStops + 1] = nextCst; + minHeap.offer(new int[]{nextCst, nei, nextStops}); + } + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int findCheapestPrice(int n, vector>& flights, int src, int dst, int k) { + int INF = 1e9; + vector>> adj(n); + vector> dist(n, vector(k + 5, INF)); + + for (auto& flight : flights) { + adj[flight[0]].emplace_back(flight[1], flight[2]); + } + + dist[src][0] = 0; + priority_queue, + vector>, greater<>> minHeap; + minHeap.emplace(0, src, -1); + + while (!minHeap.empty()) { + auto [cst, node, stops] = minHeap.top(); + minHeap.pop(); + if (node == dst) return cst; + if (stops == k || dist[node][stops + 1] < cst) continue; + for (auto& [nei, w] : adj[node]) { + int nextCst = cst + w; + int nextStops = stops + 1; + if (dist[nei][nextStops + 1] > nextCst) { + dist[nei][nextStops + 1] = nextCst; + minHeap.emplace(nextCst, nei, nextStops); + } + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} flights + * @param {number} src + * @param {number} dst + * @param {number} k + * @return {number} + */ + findCheapestPrice(n, flights, src, dst, k) { + const INF = Infinity; + const adj = Array.from({ length: n }, () => []); + const dist = Array.from({ length: n }, () => + Array(k + 5).fill(INF)); + + for (let [u, v, cst] of flights) { + adj[u].push([v, cst]); + } + + dist[src][0] = 0; + const minHeap = new MinPriorityQueue(entry => entry[0]); + minHeap.push([0, src, -1]); // cost, node, stops + while (!minHeap.isEmpty()) { + const [cst, node, stops] = minHeap.pop(); + if (node === dst) return cst; + if (stops === k || dist[node][stops + 1] < cst) continue; + for (let [nei, w] of adj[node]) { + const nextCst = cst + w; + const nextStops = stops + 1; + if (dist[nei][nextStops + 1] > nextCst) { + dist[nei][nextStops + 1] = nextCst; + minHeap.push([nextCst, nei, nextStops]); + } + } + } + return -1; + } +} +``` + +```csharp +public class Solution { + public int FindCheapestPrice(int n, int[][] flights, int src, int dst, int k) { + int INF = int.MaxValue; + List[] adj = new List[n]; + int[][] dist = new int[n][]; + + for (int i = 0; i < n; i++) { + adj[i] = new List(); + dist[i] = new int[k + 2]; + Array.Fill(dist[i], INF); + } + + foreach (var flight in flights) { + adj[flight[0]].Add(new int[] { flight[1], flight[2] }); + } + + dist[src][0] = 0; + var minHeap = new PriorityQueue<(int cst, int node, int stops), int>(); + minHeap.Enqueue((0, src, 0), 0); + + while (minHeap.Count > 0) { + var (cst, node, stops) = minHeap.Dequeue(); + if (node == dst) return cst; + if (stops > k) continue; + + foreach (var neighbor in adj[node]) { + int nei = neighbor[0], w = neighbor[1]; + int nextCst = cst + w; + int nextStops = stops + 1; + + if (dist[nei][nextStops] > nextCst) { + dist[nei][nextStops] = nextCst; + minHeap.Enqueue((nextCst, nei, nextStops), nextCst); + } + } + } + return -1; + } +} +``` + +```go +func findCheapestPrice(n int, flights [][]int, src int, dst int, k int) int { + INF := 1000000000 + adj := make([][]struct{ to, cost int }, n) + dist := make([][]int, n) + for i := range dist { + dist[i] = make([]int, k+5) + for j := range dist[i] { + dist[i][j] = INF + } + } + for _, flight := range flights { + from, to, cost := flight[0], flight[1], flight[2] + adj[from] = append(adj[from], struct{ to, cost int }{to, cost}) + } + dist[src][0] = 0 + minHeap := priorityqueue.NewWith(func(a, b interface{}) int { + return utils.IntComparator(a.([3]int)[0], b.([3]int)[0]) + }) + minHeap.Enqueue([3]int{0, src, -1}) + for !minHeap.Empty() { + value, _ := minHeap.Dequeue() + cst, node, stops := value.([3]int)[0], value.([3]int)[1], value.([3]int)[2] + if node == dst { + return cst + } + if stops == k || dist[node][stops+1] < cst { + continue + } + for _, nei := range adj[node] { + nextCst := cst + nei.cost + nextStops := stops + 1 + if dist[nei.to][nextStops+1] > nextCst { + dist[nei.to][nextStops+1] = nextCst + minHeap.Enqueue([3]int{nextCst, nei.to, nextStops}) + } + } + } + return -1 +} +``` + +```kotlin +class Solution { + fun findCheapestPrice(n: Int, flights: Array, src: Int, dst: Int, k: Int): Int { + val INF = 1_000_000_000 + val adj = Array(n) { mutableListOf>() } + val dist = Array(n) { IntArray(k + 5) { INF } } + for (flight in flights) { + adj[flight[0]].add(Pair(flight[1], flight[2])) + } + dist[src][0] = 0 + val minHeap = PriorityQueue(compareBy> { it.first }) + minHeap.add(Triple(0, src, -1)) + while (minHeap.isNotEmpty()) { + val (cst, node, stops) = minHeap.poll() + if (node == dst) return cst + if (stops == k || dist[node][stops + 1] < cst) continue + for ((nei, w) in adj[node]) { + val nextCst = cst + w + val nextStops = stops + 1 + if (dist[nei][nextStops + 1] > nextCst) { + dist[nei][nextStops + 1] = nextCst + minHeap.add(Triple(nextCst, nei, nextStops)) + } + } + } + return -1 + } +} +``` + +```swift +struct Item: Comparable { + let cost: Int + let node: Int + let stops: Int + + static func < (lhs: Item, rhs: Item) -> Bool { + return lhs.cost < rhs.cost + } +} + +class Solution { + func findCheapestPrice(_ n: Int, _ flights: [[Int]], _ src: Int, _ dst: Int, _ k: Int) -> Int { + let INF = Int.max + var adj = Array(repeating: [(Int, Int)](), count: n) + var dist = Array(repeating: Array(repeating: INF, count: k + 5), count: n) + + for flight in flights { + let u = flight[0], v = flight[1], cst = flight[2] + adj[u].append((v, cst)) + } + + var minHeap = Heap() + minHeap.insert(Item(cost: 0, node: src, stops: -1)) + dist[src][0] = 0 + + while !minHeap.isEmpty { + let item = minHeap.removeMin() + let cst = item.cost, node = item.node, stops = item.stops + if node == dst { return cst } + if stops == k || dist[node][stops + 1] < cst { continue } + + for (nei, w) in adj[node] { + let nextCst = cst + w + let nextStops = stops + 1 + if dist[nei][nextStops + 1] > nextCst { + dist[nei][nextStops + 1] = nextCst + minHeap.insert(Item(cost: nextCst, node: nei, stops: nextStops)) + } + } + } + + return -1 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((n + m) * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ is the number of cities, $m$ is the number of flights and $k$ is the number of stops. + +--- + +## 2. Bellman Ford Algorithm + +::tabs-start + +```python +class Solution: + def findCheapestPrice(self, n: int, flights: List[List[int]], src: int, dst: int, k: int) -> int: + prices = [float("inf")] * n + prices[src] = 0 + + for i in range(k + 1): + tmpPrices = prices.copy() + + for s, d, p in flights: # s=source, d=dest, p=price + if prices[s] == float("inf"): + continue + if prices[s] + p < tmpPrices[d]: + tmpPrices[d] = prices[s] + p + prices = tmpPrices + return -1 if prices[dst] == float("inf") else prices[dst] +``` + +```java +public class Solution { + public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) { + int[] prices = new int[n]; + Arrays.fill(prices, Integer.MAX_VALUE); + prices[src] = 0; + + for (int i = 0; i <= k; i++) { + int[] tmpPrices = Arrays.copyOf(prices, n); + + for (int[] flight : flights) { + int s = flight[0]; + int d = flight[1]; + int p = flight[2]; + + if (prices[s] == Integer.MAX_VALUE) { + continue; + } + + if (prices[s] + p < tmpPrices[d]) { + tmpPrices[d] = prices[s] + p; + } + } + + prices = tmpPrices; + } + + return prices[dst] == Integer.MAX_VALUE ? -1 : prices[dst]; + } +} +``` + +```cpp +class Solution { +public: + int findCheapestPrice(int n, vector>& flights, int src, int dst, int k) { + vector prices(n, INT_MAX); + prices[src] = 0; + + for (int i = 0; i <= k; i++) { + vector tmpPrices = prices; + + for (const auto& flight : flights) { + int s = flight[0]; + int d = flight[1]; + int p = flight[2]; + + if (prices[s] == INT_MAX) + continue; + + if (prices[s] + p < tmpPrices[d]) + tmpPrices[d] = prices[s] + p; + } + + prices = tmpPrices; + } + + return prices[dst] == INT_MAX ? -1 : prices[dst]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} flights + * @param {number} src + * @param {number} dst + * @param {number} k + * @return {number} + */ + findCheapestPrice(n, flights, src, dst, k) { + let prices = new Array(n).fill(Number.MAX_SAFE_INTEGER); + prices[src] = 0; + + for (let i = 0; i <= k; i++) { + const tmpPrices = [...prices]; + + for (const flight of flights) { + const s = flight[0]; + const d = flight[1]; + const p = flight[2]; + + if (prices[s] === Number.MAX_SAFE_INTEGER) continue; + + if (prices[s] + p < tmpPrices[d]) tmpPrices[d] = prices[s] + p; + } + + prices = tmpPrices; + } + + return prices[dst] === Number.MAX_SAFE_INTEGER ? -1 : prices[dst]; + } +} +``` + +```csharp +public class Solution { + public int FindCheapestPrice(int n, int[][] flights, int src, int dst, int k) { + int[] prices = new int[n]; + Array.Fill(prices, int.MaxValue); + prices[src] = 0; + + for (int i = 0; i <= k; i++) { + int[] tmpPrices = (int[])prices.Clone(); + + foreach (var flight in flights) { + int s = flight[0]; + int d = flight[1]; + int p = flight[2]; + + if (prices[s] == int.MaxValue) + continue; + + if (prices[s] + p < tmpPrices[d]) + tmpPrices[d] = prices[s] + p; + } + + prices = tmpPrices; + } + + return prices[dst] == int.MaxValue ? -1 : prices[dst]; + } +} +``` + +```go +func findCheapestPrice(n int, flights [][]int, src int, dst int, k int) int { + prices := make([]int, n) + for i := range prices { + prices[i] = math.MaxInt32 + } + prices[src] = 0 + + for i := 0; i <= k; i++ { + tmpPrices := make([]int, n) + copy(tmpPrices, prices) + + for _, flight := range flights { + s, d, p := flight[0], flight[1], flight[2] + if prices[s] == math.MaxInt32 { + continue + } + if prices[s] + p < tmpPrices[d] { + tmpPrices[d] = prices[s] + p + } + } + prices = tmpPrices + } + + if prices[dst] == math.MaxInt32 { + return -1 + } + return prices[dst] +} +``` + +```kotlin +class Solution { + fun findCheapestPrice(n: Int, flights: Array, src: Int, dst: Int, k: Int): Int { + val prices = IntArray(n) { Int.MAX_VALUE } + prices[src] = 0 + + repeat(k + 1) { + val tmpPrices = prices.copyOf() + for ((s, d, p) in flights) { + if (prices[s] == Int.MAX_VALUE) continue + if (prices[s] + p < tmpPrices[d]) { + tmpPrices[d] = prices[s] + p + } + } + tmpPrices.copyInto(prices) + } + + return if (prices[dst] == Int.MAX_VALUE) -1 else prices[dst] + } +} +``` + +```swift +class Solution { + func findCheapestPrice(_ n: Int, _ flights: [[Int]], _ src: Int, _ dst: Int, _ k: Int) -> Int { + var prices = Array(repeating: Int.max, count: n) + prices[src] = 0 + + for _ in 0...k { + var tmpPrices = prices + + for flight in flights { + let s = flight[0], d = flight[1], p = flight[2] + if prices[s] == Int.max { + continue + } + if prices[s] + p < tmpPrices[d] { + tmpPrices[d] = prices[s] + p + } + } + prices = tmpPrices + } + return prices[dst] == Int.max ? -1 : prices[dst] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + (m * k))$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of cities, $m$ is the number of flights and $k$ is the number of stops. + +--- + +## 3. Shortest Path Faster Algorithm + +::tabs-start + +```python +class Solution: + def findCheapestPrice(self, n: int, flights: List[List[int]], src: int, dst: int, k: int) -> int: + prices = [float("inf")] * n + prices[src] = 0 + adj = [[] for _ in range(n)] + for u, v, cst in flights: + adj[u].append([v, cst]) + + q = deque([(0, src, 0)]) + while q: + cst, node, stops = q.popleft() + if stops > k: + continue + + for nei, w in adj[node]: + nextCost = cst + w + if nextCost < prices[nei]: + prices[nei] = nextCost + q.append((nextCost, nei, stops + 1)) + + return prices[dst] if prices[dst] != float("inf") else -1 +``` + +```java +public class Solution { + public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) { + int[] prices = new int[n]; + Arrays.fill(prices, Integer.MAX_VALUE); + prices[src] = 0; + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (var flight : flights) { + adj[flight[0]].add(new int[] { flight[1], flight[2] }); + } + + Queue q = new LinkedList<>(); + q.offer(new int[] { 0, src, 0 }); + + while (!q.isEmpty()) { + var curr = q.poll(); + int cst = curr[0], node = curr[1], stops = curr[2]; + if (stops > k) continue; + + for (var neighbor : adj[node]) { + int nei = neighbor[0], w = neighbor[1]; + int nextCost = cst + w; + if (nextCost < prices[nei]) { + prices[nei] = nextCost; + q.offer(new int[] { nextCost, nei, stops + 1 }); + } + } + } + return prices[dst] == Integer.MAX_VALUE ? -1 : prices[dst]; + } +} +``` + +```cpp +class Solution { +public: + int findCheapestPrice(int n, vector>& flights, int src, int dst, int k) { + vector prices(n, INT_MAX); + prices[src] = 0; + vector>> adj(n); + for (const auto& flight : flights) { + adj[flight[0]].emplace_back(flight[1], flight[2]); + } + + queue> q; + q.push({0, src, 0}); + + while (!q.empty()) { + auto [cst, node, stops] = q.front(); + q.pop(); + if (stops > k) continue; + + for (const auto& neighbor : adj[node]) { + int nei = neighbor.first, w = neighbor.second; + int nextCost = cst + w; + if (nextCost < prices[nei]) { + prices[nei] = nextCost; + q.push({nextCost, nei, stops + 1}); + } + } + } + return prices[dst] == INT_MAX ? -1 : prices[dst]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} flights + * @param {number} src + * @param {number} dst + * @param {number} k + * @return {number} + */ + findCheapestPrice(n, flights, src, dst, k) { + const prices = Array(n).fill(Infinity); + prices[src] = 0; + const adj = Array.from({ length: n }, () => []); + + for (const [u, v, cst] of flights) { + adj[u].push([v, cst]); + } + + const q = new Queue([[0, src, 0]]); // [cost, node, stops] + + while (!q.isEmpty()) { + const [cst, node, stops] = q.pop(); + if (stops > k) continue; + + for (const [nei, w] of adj[node]) { + const nextCost = cst + w; + if (nextCost < prices[nei]) { + prices[nei] = nextCost; + q.push([nextCost, nei, stops + 1]); + } + } + } + return prices[dst] === Infinity ? -1 : prices[dst]; + } +} +``` + +```csharp +public class Solution { + public int FindCheapestPrice(int n, int[][] flights, int src, int dst, int k) { + int[] prices = new int[n]; + Array.Fill(prices, int.MaxValue); + prices[src] = 0; + List[] adj = new List[n]; + for (int i = 0; i < n; i++) { + adj[i] = new List(); + } + foreach (var flight in flights) { + adj[flight[0]].Add(new int[] { flight[1], flight[2] }); + } + + var q = new Queue<(int cst, int node, int stops)>(); + q.Enqueue((0, src, 0)); + + while (q.Count > 0) { + var (cst, node, stops) = q.Dequeue(); + if (stops > k) continue; + + foreach (var neighbor in adj[node]) { + int nei = neighbor[0], w = neighbor[1]; + int nextCost = cst + w; + if (nextCost < prices[nei]) { + prices[nei] = nextCost; + q.Enqueue((nextCost, nei, stops + 1)); + } + } + } + return prices[dst] == int.MaxValue ? -1 : prices[dst]; + } +} +``` + +```go +func findCheapestPrice(n int, flights [][]int, src int, dst int, k int) int { + prices := make([]int, n) + for i := range prices { + prices[i] = math.MaxInt32 + } + prices[src] = 0 + + adj := make([][][2]int, n) + for _, flight := range flights { + from, to, cost := flight[0], flight[1], flight[2] + adj[from] = append(adj[from], [2]int{to, cost}) + } + + q := [][3]int{{0, src, 0}} + + for len(q) > 0 { + curr := q[0] + q = q[1:] + cst, node, stops := curr[0], curr[1], curr[2] + + if stops > k { + continue + } + + for _, neighbor := range adj[node] { + nei, w := neighbor[0], neighbor[1] + nextCost := cst + w + if nextCost < prices[nei] { + prices[nei] = nextCost + q = append(q, [3]int{nextCost, nei, stops + 1}) + } + } + } + + if prices[dst] == math.MaxInt32 { + return -1 + } + return prices[dst] +} +``` + +```kotlin +class Solution { + fun findCheapestPrice(n: Int, flights: Array, src: Int, dst: Int, k: Int): Int { + val prices = IntArray(n) { Int.MAX_VALUE } + prices[src] = 0 + + val adj = Array(n) { mutableListOf>() } + for (flight in flights) { + val (from, to, cost) = flight + adj[from].add(Pair(to, cost)) + } + + val q: Queue> = LinkedList() + q.offer(Triple(0, src, 0)) + + while (q.isNotEmpty()) { + val (cst, node, stops) = q.poll() + if (stops > k) continue + + for ((nei, w) in adj[node]) { + val nextCost = cst + w + if (nextCost < prices[nei]) { + prices[nei] = nextCost + q.offer(Triple(nextCost, nei, stops + 1)) + } + } + } + + return if (prices[dst] == Int.MAX_VALUE) -1 else prices[dst] + } +} +``` + +```swift +class Solution { + func findCheapestPrice(_ n: Int, _ flights: [[Int]], _ src: Int, _ dst: Int, _ k: Int) -> Int { + var prices = Array(repeating: Int.max, count: n) + prices[src] = 0 + var adj = Array(repeating: [(Int, Int)](), count: n) + + for flight in flights { + let u = flight[0], v = flight[1], cst = flight[2] + adj[u].append((v, cst)) + } + + var queue = Deque<(Int, Int, Int)>() + queue.append((0, src, 0)) + + while !queue.isEmpty { + let (cst, node, stops) = queue.popFirst()! + if stops > k { + continue + } + + for (nei, w) in adj[node] { + let nextCost = cst + w + if nextCost < prices[nei] { + prices[nei] = nextCost + queue.append((nextCost, nei, stops + 1)) + } + } + } + + return prices[dst] == Int.max ? -1 : prices[dst] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the number of cities, $m$ is the number of flights and $k$ is the number of stops. \ No newline at end of file diff --git a/articles/check-completeness-of-a-binary-tree.md b/articles/check-completeness-of-a-binary-tree.md new file mode 100644 index 000000000..90a185822 --- /dev/null +++ b/articles/check-completeness-of-a-binary-tree.md @@ -0,0 +1,591 @@ +## 1. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def isCompleteTree(self, root: Optional[TreeNode]) -> bool: + q = deque([root]) + while q: + node = q.popleft() + if node: + q.append(node.left) + q.append(node.right) + else: + while q: + if q.popleft(): + return False + return True +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean isCompleteTree(TreeNode root) { + Queue q = new LinkedList<>(); + q.add(root); + + while (!q.isEmpty()) { + TreeNode node = q.poll(); + if (node != null) { + q.add(node.left); + q.add(node.right); + } else { + while (!q.isEmpty()) { + if (q.poll() != null) { + return false; + } + } + } + } + return true; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool isCompleteTree(TreeNode* root) { + queue q; + q.push(root); + + while (!q.empty()) { + TreeNode* node = q.front(); + q.pop(); + if (node) { + q.push(node->left); + q.push(node->right); + } else { + while (!q.empty()) { + if (q.front()) { + return false; + } + q.pop(); + } + } + } + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isCompleteTree(root) { + const queue = new Queue([root]); + + while (!queue.isEmpty()) { + const node = queue.pop(); + if (node) { + queue.push(node.left); + queue.push(node.right); + } else { + while (!queue.isEmpty()) { + if (queue.pop()) { + return false; + } + } + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Breadth First Search (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def isCompleteTree(self, root: Optional[TreeNode]) -> bool: + q = deque([root]) + nullSeen = False + while q: + node = q.popleft() + if node: + if nullSeen: + return False + q.append(node.left) + q.append(node.right) + else: + nullSeen = True + return True +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean isCompleteTree(TreeNode root) { + Queue q = new LinkedList<>(); + q.add(root); + boolean nullSeen = false; + + while (!q.isEmpty()) { + TreeNode node = q.poll(); + if (node != null) { + if (nullSeen) return false; + q.add(node.left); + q.add(node.right); + } else { + nullSeen = true; + } + } + return true; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool isCompleteTree(TreeNode* root) { + queue q; + q.push(root); + bool nullSeen = false; + + while (!q.empty()) { + TreeNode* node = q.front(); + q.pop(); + if (node) { + if (nullSeen) return false; + q.push(node->left); + q.push(node->right); + } else { + nullSeen = true; + } + } + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isCompleteTree(root) { + const queue = new Queue([root]); + let nullSeen = false; + + while (!queue.isEmpty()) { + const node = queue.pop(); + if (node) { + if (nullSeen) return false; + queue.push(node.left); + queue.push(node.right); + } else { + nullSeen = true; + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Depth First Search (Two Pass) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def isCompleteTree(self, root: Optional[TreeNode]) -> bool: + def dfs(node, index, n): + if not node: + return True + if index >= n: + return False + + left = dfs(node.left, 2 * index + 1, n) + right = dfs(node.right, 2 * index + 2, n) + return left and right + + def countNodes(node): + if not node: + return 0 + return 1 + countNodes(node.left) + countNodes(node.right) + + n = countNodes(root) + return dfs(root, 0, n) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private int countNodes(TreeNode root) { + if (root == null) return 0; + return 1 + countNodes(root.left) + countNodes(root.right); + } + + private boolean dfs(TreeNode node, int index, int n) { + if (node == null) return true; + if (index >= n) return false; + return dfs(node.left, 2 * index + 1, n) && dfs(node.right, 2 * index + 2, n); + } + + public boolean isCompleteTree(TreeNode root) { + int n = countNodes(root); + return dfs(root, 0, n); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int countNodes(TreeNode* root) { + if (!root) return 0; + return 1 + countNodes(root->left) + countNodes(root->right); + } + + bool dfs(TreeNode* node, int index, int n) { + if (!node) return true; + if (index >= n) return false; + return dfs(node->left, 2 * index + 1, n) && dfs(node->right, 2 * index + 2, n); + } + + bool isCompleteTree(TreeNode* root) { + int n = countNodes(root); + return dfs(root, 0, n); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isCompleteTree(root) { + const countNodes = (node) => { + if (!node) return 0; + return 1 + countNodes(node.left) + countNodes(node.right); + }; + + const dfs = (node, index, n) => { + if (!node) return true; + if (index >= n) return false; + return dfs(node.left, 2 * index + 1, n) && dfs(node.right, 2 * index + 2, n); + }; + + const n = countNodes(root); + return dfs(root, 0, n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 4. Depth First Search (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def isCompleteTree(self, root: Optional[TreeNode]) -> bool: + treeHgt = 0 + nullSeen = False + + def dfs(node, hgt): + nonlocal treeHgt, nullSeen + if not node: + if treeHgt == 0: + treeHgt = hgt + elif hgt == treeHgt - 1: + nullSeen = True + elif hgt != treeHgt: + return False + return not (hgt == treeHgt and nullSeen) + + return dfs(node.left, hgt + 1) and dfs(node.right, hgt + 1) + + return dfs(root, 0) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private int treeHgt = 0; + private boolean nullSeen = false; + + private boolean dfs(TreeNode node, int hgt) { + if (node == null) { + if (treeHgt == 0) { + treeHgt = hgt; + } else if (hgt == treeHgt - 1) { + nullSeen = true; + } else if (hgt != treeHgt) { + return false; + } + return !(hgt == treeHgt && nullSeen); + } + + return dfs(node.left, hgt + 1) && dfs(node.right, hgt + 1); + } + + public boolean isCompleteTree(TreeNode root) { + return dfs(root, 0); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int treeHgt = 0; + bool nullSeen = false; + + bool dfs(TreeNode* node, int hgt) { + if (!node) { + if (treeHgt == 0) { + treeHgt = hgt; + } else if (hgt == treeHgt - 1) { + nullSeen = true; + } else if (hgt != treeHgt) { + return false; + } + return !(hgt == treeHgt && nullSeen); + } + + return dfs(node->left, hgt + 1) && dfs(node->right, hgt + 1); + } + + bool isCompleteTree(TreeNode* root) { + return dfs(root, 0); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isCompleteTree(root) { + let treeHgt = 0; + let nullSeen = false; + + const dfs = (node, hgt) => { + if (!node) { + if (treeHgt === 0) { + treeHgt = hgt; + } else if (hgt === treeHgt - 1) { + nullSeen = true; + } else if (hgt !== treeHgt) { + return false; + } + return !(hgt === treeHgt && nullSeen); + } + + return dfs(node.left, hgt + 1) && dfs(node.right, hgt + 1); + }; + + return dfs(root, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. \ No newline at end of file diff --git a/articles/check-if-a-string-contains-all-binary-codes-of-size-k.md b/articles/check-if-a-string-contains-all-binary-codes-of-size-k.md new file mode 100644 index 000000000..917412238 --- /dev/null +++ b/articles/check-if-a-string-contains-all-binary-codes-of-size-k.md @@ -0,0 +1,497 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def hasAllCodes(self, s: str, k: int) -> bool: + n = len(s) + if n < (1 << k): + return False + + for num in range(1 << k): + binaryCode = format(num, f'0{k}b') + if binaryCode not in s: + return False + + return True +``` + +```java +public class Solution { + public boolean hasAllCodes(String s, int k) { + int n = s.length(); + if (n < (1 << k)) { + return false; + } + + for (int num = 0; num < (1 << k); num++) { + String binaryCode = String.format("%" + k + "s", Integer.toBinaryString(num)).replace(' ', '0'); + if (!s.contains(binaryCode)) { + return false; + } + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool hasAllCodes(string s, int k) { + int n = s.size(); + if (n < (1 << k)) { + return false; + } + + for (int num = 0; num < (1 << k); num++) { + string binaryCode = bitset<20>(num).to_string().substr(20 - k); + if (s.find(binaryCode) == string::npos) { + return false; + } + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {boolean} + */ + hasAllCodes(s, k) { + const n = s.length; + if (n < (1 << k)) { + return false; + } + + for (let num = 0; num < (1 << k); num++) { + const binaryCode = num.toString(2).padStart(k, '0'); + if (!s.includes(binaryCode)) { + return false; + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ k)$ +* Space complexity: $O(k)$ + +> Where $n$ is the length of the string $s$ and $k$ is the length of the binary code. + +--- + +## 2. Hash Set + +::tabs-start + +```python +class Solution: + def hasAllCodes(self, s: str, k: int) -> bool: + if len(s) < 2 ** k: + return False + + codeSet = set() + for i in range(len(s) - k + 1): + codeSet.add(s[i:i + k]) + + return len(codeSet) == 2 ** k +``` + +```java +public class Solution { + public boolean hasAllCodes(String s, int k) { + if (s.length() < (1 << k)) { + return false; + } + + HashSet codeSet = new HashSet<>(); + for (int i = 0; i <= s.length() - k; i++) { + codeSet.add(s.substring(i, i + k)); + } + + return codeSet.size() == (1 << k); + } +} +``` + +```cpp +class Solution { +public: + bool hasAllCodes(std::string s, int k) { + if (s.size() < (1 << k)) { + return false; + } + + std::unordered_set codeSet; + for (int i = 0; i <= s.size() - k; i++) { + codeSet.insert(s.substr(i, k)); + } + + return codeSet.size() == (1 << k); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {boolean} + */ + hasAllCodes(s, k) { + if (s.length < (1 << k)) { + return false; + } + + const codeSet = new Set(); + for (let i = 0; i <= s.length - k; i++) { + codeSet.add(s.substring(i, i + k)); + } + + return codeSet.size === (1 << k); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(2 ^ k)$ + +> Where $n$ is the length of the string $s$ and $k$ is the length of the binary code. + +--- + +## 3. Sliding Window + +::tabs-start + +```python +class Solution: + def hasAllCodes(self, s: str, k: int) -> bool: + n = len(s) + if n < (1 << k): + return False + + codeSet = [False] * (1 << k) + cur = 0 + i = j = 0 + bit = k - 1 + while j < k: + if s[j] == '1': + cur |= (1 << bit) + bit -= 1 + j += 1 + + have = 1 + codeSet[cur] = True + while j < n: + if s[i] == '1': + cur ^= (1 << (k - 1)) + i += 1 + + cur <<= 1 + if s[j] == '1': + cur |= 1 + j += 1 + + if not codeSet[cur]: + have += 1 + codeSet[cur] = True + + return have == (1 << k) +``` + +```java +public class Solution { + public boolean hasAllCodes(String s, int k) { + int n = s.length(); + if (n < (1 << k)) { + return false; + } + + boolean[] codeSet = new boolean[1 << k]; + int cur = 0; + int i = 0, j = 0, bit = k - 1; + + while (j < k) { + if (s.charAt(j) == '1') { + cur |= (1 << bit); + } + bit--; + j++; + } + + int have = 1; + codeSet[cur] = true; + + while (j < n) { + if (s.charAt(i) == '1') { + cur ^= (1 << (k - 1)); + } + i++; + + cur <<= 1; + if (s.charAt(j) == '1') { + cur |= 1; + } + j++; + + if (!codeSet[cur]) { + have++; + codeSet[cur] = true; + } + } + + return have == (1 << k); + } +} +``` + +```cpp +class Solution { +public: + bool hasAllCodes(string s, int k) { + int n = s.size(); + if (n < (1 << k)) { + return false; + } + + vector codeSet(1 << k, false); + int cur = 0; + int i = 0, j = 0, bit = k - 1; + + while (j < k) { + if (s[j] == '1') { + cur |= (1 << bit); + } + bit--; + j++; + } + + int have = 1; + codeSet[cur] = true; + + while (j < n) { + if (s[i] == '1') { + cur ^= (1 << (k - 1)); + } + i++; + + cur <<= 1; + if (s[j] == '1') { + cur |= 1; + } + j++; + + if (!codeSet[cur]) { + have++; + codeSet[cur] = true; + } + } + + return have == (1 << k); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {boolean} + */ + hasAllCodes(s, k) { + const n = s.length; + if (n < (1 << k)) { + return false; + } + + const codeSet = new Array(1 << k).fill(false); + let cur = 0; + let i = 0, j = 0, bit = k - 1; + + while (j < k) { + if (s[j] === '1') { + cur |= (1 << bit); + } + bit--; + j++; + } + + let have = 1; + codeSet[cur] = true; + + while (j < n) { + if (s[i] === '1') { + cur ^= (1 << (k - 1)); + } + i++; + + cur <<= 1; + if (s[j] === '1') { + cur |= 1; + } + j++; + + if (!codeSet[cur]) { + have++; + codeSet[cur] = true; + } + } + + return have === (1 << k); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(2 ^ k)$ + +> Where $n$ is the length of the string $s$ and $k$ is the length of the binary code. + +--- + +## 4. Sliding Window (Optimal) + +::tabs-start + +```python +class Solution: + def hasAllCodes(self, s: str, k: int) -> bool: + n = len(s) + if n < (1 << k): + return False + + codeSet = [False] * (1 << k) + cur = 0 + have = 0 + + for i in range(n): + cur = ((cur << 1) & ((1 << k) - 1)) | (ord(s[i]) - ord('0')) + + if i >= k - 1: + if not codeSet[cur]: + codeSet[cur] = True + have += 1 + + return have == (1 << k) +``` + +```java +public class Solution { + public boolean hasAllCodes(String s, int k) { + int n = s.length(); + if (n < (1 << k)) { + return false; + } + + boolean[] codeSet = new boolean[1 << k]; + int cur = 0, have = 0; + + for (int i = 0; i < n; i++) { + cur = ((cur << 1) & ((1 << k) - 1)) | (s.charAt(i) - '0'); + + if (i >= k - 1) { + if (!codeSet[cur]) { + codeSet[cur] = true; + have++; + } + } + } + + return have == (1 << k); + } +} +``` + +```cpp +class Solution { +public: + bool hasAllCodes(string s, int k) { + int n = s.size(); + if (n < (1 << k)) { + return false; + } + + vector codeSet(1 << k, false); + int cur = 0, have = 0; + + for (int i = 0; i < n; i++) { + cur = ((cur << 1) & ((1 << k) - 1)) | (s[i] - '0'); + + if (i >= k - 1) { + if (!codeSet[cur]) { + codeSet[cur] = true; + have++; + } + } + } + + return have == (1 << k); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {boolean} + */ + hasAllCodes(s, k) { + const n = s.length; + if (n < (1 << k)) { + return false; + } + + const codeSet = new Array(1 << k).fill(false); + let cur = 0, have = 0; + + for (let i = 0; i < n; i++) { + cur = ((cur << 1) & ((1 << k) - 1)) | (s[i] - '0'); + + if (i >= k - 1) { + if (!codeSet[cur]) { + codeSet[cur] = true; + have++; + } + } + } + + return have === (1 << k); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(2 ^ k)$ + +> Where $n$ is the length of the string $s$ and $k$ is the length of the binary code. \ No newline at end of file diff --git a/articles/check-if-array-is-sorted-and-rotated.md b/articles/check-if-array-is-sorted-and-rotated.md new file mode 100644 index 000000000..061da0d48 --- /dev/null +++ b/articles/check-if-array-is-sorted-and-rotated.md @@ -0,0 +1,306 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def check(self, nums: List[int]) -> bool: + sortedNums = sorted(nums) + arr = [] + + for i in range(len(nums)): + arr.insert(0, sortedNums.pop()) + if nums == arr + sortedNums: + return True + + return False +``` + +```java +public class Solution { + public boolean check(int[] nums) { + int n = nums.length; + int[] sortedNums = nums.clone(); + Arrays.sort(sortedNums); + + for (int i = 0; i < n; i++) { + boolean match = true; + int idx = 0; + for (int j = n - i; j < n && match; j++) { + if (nums[idx] != sortedNums[j]) { + match = false; + } + idx += 1; + } + + for (int j = 0; j < n - i && match; j++) { + if (nums[idx] != sortedNums[j]) { + match = false; + } + idx += 1; + } + + if (match) return true; + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool check(vector& nums) { + int n = nums.size(); + vector sortedNums = nums; + sort(sortedNums.begin(), sortedNums.end()); + + for (int i = 0; i < n; i++) { + bool match = true; + int idx = 0; + for (int j = n - i; j < n && match; j++) { + if (nums[idx] != sortedNums[j]) { + match = false; + } + idx++; + } + + for (int j = 0; j < n - i && match; j++) { + if (nums[idx] != sortedNums[j]) { + match = false; + } + idx++; + } + + if (match) return true; + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + check(nums) { + const n = nums.length; + const sortedNums = [...nums].sort((a, b) => a - b); + + for (let i = 0; i < n; i++) { + let match = true; + let idx = 0; + + for (let j = n - i; j < n && match; j++) { + if (nums[idx] !== sortedNums[j]) { + match = false; + } + idx++; + } + + for (let j = 0; j < n - i && match; j++) { + if (nums[idx] !== sortedNums[j]) { + match = false; + } + idx++; + } + + if (match) return true; + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Sliding Window + +::tabs-start + +```python +class Solution: + def check(self, nums: List[int]) -> bool: + N = len(nums) + count = 1 + + for i in range(1, 2 * N): + if nums[(i - 1) % N] <= nums[i % N]: + count += 1 + else: + count = 1 + if count == N: + return True + + return N == 1 +``` + +```java +public class Solution { + public boolean check(int[] nums) { + int N = nums.length; + int count = 1; + + for (int i = 1; i < 2 * N; i++) { + if (nums[(i - 1) % N] <= nums[i % N]) { + count++; + } else { + count = 1; + } + if (count == N) { + return true; + } + } + + return N == 1; + } +} +``` + +```cpp +class Solution { +public: + bool check(vector& nums) { + int N = nums.size(); + int count = 1; + + for (int i = 1; i < 2 * N; i++) { + if (nums[(i - 1) % N] <= nums[i % N]) { + count++; + } else { + count = 1; + } + if (count == N) { + return true; + } + } + + return N == 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + check(nums) { + const N = nums.length; + let count = 1; + + for (let i = 1; i < 2 * N; i++) { + if (nums[(i - 1) % N] <= nums[i % N]) { + count++; + } else { + count = 1; + } + if (count === N) { + return true; + } + } + + return N === 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Iteration + +::tabs-start + +```python +class Solution: + def check(self, nums: List[int]) -> bool: + count, N = 0, len(nums) + + for i in range(N): + if nums[i] > nums[(i + 1) % N]: + count += 1 + if count > 1: + return False + + return True +``` + +```java +public class Solution { + public boolean check(int[] nums) { + int count = 0, N = nums.length; + + for (int i = 0; i < N; i++) { + if (nums[i] > nums[(i + 1) % N] && ++count > 1) { + return false; + } + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool check(vector& nums) { + int count = 0, N = nums.size(); + + for (int i = 0; i < N; i++) { + if (nums[i] > nums[(i + 1) % N] && ++count > 1) { + return false; + } + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + check(nums) { + let count = 0, N = nums.length; + + for (let i = 0; i < N; i++) { + if (nums[i] > nums[(i + 1) % N] && ++count > 1) { + return false; + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/check-if-move-is-legal.md b/articles/check-if-move-is-legal.md new file mode 100644 index 000000000..acc1d8b66 --- /dev/null +++ b/articles/check-if-move-is-legal.md @@ -0,0 +1,298 @@ +## 1. Iteration - I + +::tabs-start + +```python +class Solution: + def checkMove(self, board: List[List[str]], rMove: int, cMove: int, color: str) -> bool: + ROWS, COLS = len(board), len(board[0]) + direction = [[1, 0], [-1, 0], [0, 1], [0, -1], + [1, 1], [-1, -1], [1, -1], [-1, 1]] + + board[rMove][cMove] = color + + def legal(row, col, color, direc): + dr, dc = direc + row, col = row + dr, col + dc + length = 1 + + while 0 <= row < ROWS and 0 <= col < COLS: + length += 1 + if board[row][col] == ".": + return False + if board[row][col] == color: + return length >= 3 + row, col = row + dr, col + dc + return False + + for d in direction: + if legal(rMove, cMove, color, d): + return True + return False +``` + +```java +public class Solution { + public boolean checkMove(char[][] board, int rMove, int cMove, char color) { + int ROWS = board.length, COLS = board[0].length; + int[][] direction = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}, + {1, 1}, {-1, -1}, {1, -1}, {-1, 1}}; + + board[rMove][cMove] = color; + + for (int[] d : direction) { + if (legal(board, rMove, cMove, color, d)) { + return true; + } + } + return false; + } + + private boolean legal(char[][] board, int row, int col, char color, int[] direc) { + int ROWS = board.length, COLS = board[0].length; + int dr = direc[0], dc = direc[1]; + row += dr; + col += dc; + int length = 1; + + while (row >= 0 && row < ROWS && col >= 0 && col < COLS) { + length++; + if (board[row][col] == '.') { + return false; + } + if (board[row][col] == color) { + return length >= 3; + } + row += dr; + col += dc; + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool checkMove(vector>& board, int rMove, int cMove, char color) { + int ROWS = board.size(), COLS = board[0].size(); + vector> direction = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}, + {1, 1}, {-1, -1}, {1, -1}, {-1, 1}}; + + board[rMove][cMove] = color; + + for (auto& d : direction) { + if (legal(board, rMove, cMove, color, d)) { + return true; + } + } + return false; + } + +private: + bool legal(vector>& board, int row, int col, char color, vector& direc) { + int ROWS = board.size(), COLS = board[0].size(); + int dr = direc[0], dc = direc[1]; + row += dr; + col += dc; + int length = 1; + + while (row >= 0 && row < ROWS && col >= 0 && col < COLS) { + length++; + if (board[row][col] == '.') { + return false; + } + if (board[row][col] == color) { + return length >= 3; + } + row += dr; + col += dc; + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[][]} board + * @param {number} rMove + * @param {number} cMove + * @param {character} color + * @return {boolean} + */ + checkMove(board, rMove, cMove, color) { + const ROWS = board.length, COLS = board[0].length; + const direction = [[1, 0], [-1, 0], [0, 1], [0, -1], + [1, 1], [-1, -1], [1, -1], [-1, 1]]; + + board[rMove][cMove] = color; + + const legal = (row, col, color, [dr, dc]) => { + row += dr; + col += dc; + let length = 1; + + while (row >= 0 && row < ROWS && col >= 0 && col < COLS) { + length++; + if (board[row][col] === ".") { + return false; + } + if (board[row][col] === color) { + return length >= 3; + } + row += dr; + col += dc; + } + return false; + }; + + for (let d of direction) { + if (legal(rMove, cMove, color, d)) { + return true; + } + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 2. Iteration - II + +::tabs-start + +```python +class Solution: + def checkMove(self, board: List[List[str]], rMove: int, cMove: int, color: str) -> bool: + ROWS, COLS = len(board), len(board[0]) + direction = [0, 1, 0, -1, 0, 1, 1, -1, -1, 1] + + board[rMove][cMove] = color + + for d in range(9): + length = 1 + row, col = rMove, cMove + while True: + row += direction[d] + col += direction[d + 1] + + if row < 0 or col < 0 or row >= ROWS or col >= COLS or board[row][col] == ".": + break + if board[row][col] == color: + if length > 1: + return True + break + length += 1 + + return False +``` + +```java +public class Solution { + public boolean checkMove(char[][] board, int rMove, int cMove, char color) { + int ROWS = board.length, COLS = board[0].length; + int[] direction = {0, 1, 0, -1, 0, 1, 1, -1, -1, 1}; + + board[rMove][cMove] = color; + + for (int d = 0; d < 9; d++) { + int row = rMove, col = cMove; + for (int length = 1; ; ++length) { + row += direction[d]; + col += direction[d + 1]; + + if (row < 0 || col < 0 || row >= ROWS || col >= COLS || board[row][col] == '.') + break; + if (board[row][col] == color) { + if (length > 1) + return true; + break; + } + } + } + return false; + } +} + +``` + +```cpp +class Solution { +public: + bool checkMove(vector>& board, int rMove, int cMove, char color) { + int ROWS = board.size(), COLS = board[0].size(); + int direction[10] = {0, 1, 0, -1, 0, 1, 1, -1, -1, 1}; + + board[rMove][cMove] = color; + + for (int d = 0; d < 9; ++d) { + int row = rMove, col = cMove; + for (int length = 1; ; ++length) { + row += direction[d]; + col += direction[d + 1]; + + if (row < 0 || col < 0 || row >= ROWS || col >= COLS || board[row][col] == '.') + break; + if (board[row][col] == color) { + if (length > 1) + return true; + break; + } + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[][]} board + * @param {number} rMove + * @param {number} cMove + * @param {character} color + * @return {boolean} + */ + checkMove(board, rMove, cMove, color) { + const ROWS = board.length, COLS = board[0].length; + const direction = [0, 1, 0, -1, 0, 1, 1, -1, -1, 1]; + + board[rMove][cMove] = color; + + for (let d = 0; d < 9; d++) { + let row = rMove, col = cMove; + for (let length = 1; ; ++length) { + row += direction[d]; + col += direction[d + 1]; + + if (row < 0 || col < 0 || row >= ROWS || col >= COLS || board[row][col] === '.') + break; + if (board[row][col] === color) { + if (length > 1) + return true; + break; + } + } + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/check-if-there-is-a-valid-partition-for-the-array.md b/articles/check-if-there-is-a-valid-partition-for-the-array.md new file mode 100644 index 000000000..e855fd84d --- /dev/null +++ b/articles/check-if-there-is-a-valid-partition-for-the-array.md @@ -0,0 +1,451 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def validPartition(self, nums: List[int]) -> bool: + def dfs(i): + if i == len(nums): + return True + + res = False + if i < len(nums) - 1 and nums[i] == nums[i + 1]: + res = dfs(i + 2) + if i < len(nums) - 2: + if ((nums[i] == nums[i + 1] == nums[i + 2]) or + (nums[i] + 1 == nums[i + 1] and nums[i + 1] + 1 == nums[i + 2]) + ): + res = res or dfs(i + 3) + return res + + return dfs(0) +``` + +```java +public class Solution { + public boolean validPartition(int[] nums) { + return dfs(nums, 0); + } + + private boolean dfs(int[] nums, int i) { + if (i == nums.length) return true; + + boolean res = false; + if (i < nums.length - 1 && nums[i] == nums[i + 1]) { + res = dfs(nums, i + 2); + } + if (i < nums.length - 2) { + if ((nums[i] == nums[i + 1] && nums[i + 1] == nums[i + 2]) || + (nums[i] + 1 == nums[i + 1] && nums[i + 1] + 1 == nums[i + 2])) { + res = res || dfs(nums, i + 3); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + bool validPartition(vector& nums) { + return dfs(nums, 0); + } + +private: + bool dfs(vector& nums, int i) { + if (i == nums.size()) return true; + + bool res = false; + if (i < nums.size() - 1 && nums[i] == nums[i + 1]) { + res = dfs(nums, i + 2); + } + if (i < nums.size() - 2) { + if ((nums[i] == nums[i + 1] && nums[i + 1] == nums[i + 2]) || + (nums[i] + 1 == nums[i + 1] && nums[i + 1] + 1 == nums[i + 2])) { + res = res || dfs(nums, i + 3); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + validPartition(nums) { + const dfs = (i) => { + if (i === nums.length) return true; + + let res = false; + if (i < nums.length - 1 && nums[i] === nums[i + 1]) { + res = dfs(i + 2); + } + if (i < nums.length - 2) { + if ((nums[i] === nums[i + 1] && nums[i + 1] === nums[i + 2]) || + (nums[i] + 1 === nums[i + 1] && nums[i + 1] + 1 === nums[i + 2])) { + res = res || dfs(i + 3); + } + } + return res; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def validPartition(self, nums: List[int]) -> bool: + dp = { len(nums) : True } + def dfs(i): + if i in dp: + return dp[i] + + res = False + if i < len(nums) - 1 and nums[i] == nums[i + 1]: + res = dfs(i + 2) + if i < len(nums) - 2: + if ((nums[i] == nums[i + 1] == nums[i + 2]) or + (nums[i] + 1 == nums[i + 1] and nums[i + 1] + 1 == nums[i + 2]) + ): + res = res or dfs(i + 3) + + dp[i] = res + return res + + return dfs(0) +``` + +```java +public class Solution { + private Map memo = new HashMap<>(); + + public boolean validPartition(int[] nums) { + return dfs(nums, 0); + } + + private boolean dfs(int[] nums, int i) { + if (i == nums.length) return true; + if (memo.containsKey(i)) return memo.get(i); + + boolean res = false; + if (i < nums.length - 1 && nums[i] == nums[i + 1]) { + res = dfs(nums, i + 2); + } + if (i < nums.length - 2) { + if ((nums[i] == nums[i + 1] && nums[i + 1] == nums[i + 2]) || + (nums[i] + 1 == nums[i + 1] && nums[i + 1] + 1 == nums[i + 2])) { + res = res || dfs(nums, i + 3); + } + } + + memo.put(i, res); + return res; + } +} +``` + +```cpp +class Solution { +public: + unordered_map memo; + + bool validPartition(vector& nums) { + return dfs(nums, 0); + } + +private: + bool dfs(vector& nums, int i) { + if (i == nums.size()) return true; + if (memo.count(i)) return memo[i]; + + bool res = false; + if (i < nums.size() - 1 && nums[i] == nums[i + 1]) { + res = dfs(nums, i + 2); + } + if (i < nums.size() - 2) { + if ((nums[i] == nums[i + 1] && nums[i + 1] == nums[i + 2]) || + (nums[i] + 1 == nums[i + 1] && nums[i + 1] + 1 == nums[i + 2])) { + res = res || dfs(nums, i + 3); + } + } + + return memo[i] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + validPartition(nums) { + const memo = new Map(); + + const dfs = (i) => { + if (i === nums.length) return true; + if (memo.has(i)) return memo.get(i); + + let res = false; + if (i < nums.length - 1 && nums[i] === nums[i + 1]) { + res = dfs(i + 2); + } + if (i < nums.length - 2) { + if ((nums[i] === nums[i + 1] && nums[i + 1] === nums[i + 2]) || + (nums[i] + 1 === nums[i + 1] && nums[i + 1] + 1 === nums[i + 2])) { + res = res || dfs(i + 3); + } + } + + memo.set(i, res); + return res; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def validPartition(self, nums: List[int]) -> bool: + dp = [False] * (len(nums) + 1) + dp[0] = True + + for i in range(2, len(nums) + 1): + if nums[i - 1] == nums[i - 2]: + dp[i] = dp[i] or dp[i - 2] + if i > 2 and ((nums[i - 1] == nums[i - 2] == nums[i - 3]) or + (nums[i - 3] + 1 == nums[i - 2] and nums[i - 2] + 1 == nums[i - 1])): + dp[i] = dp[i] or dp[i - 3] + + return dp[len(nums)] +``` + +```java +public class Solution { + public boolean validPartition(int[] nums) { + boolean[] dp = new boolean[nums.length + 1]; + dp[0] = true; + + for (int i = 2; i <= nums.length; i++) { + if (nums[i - 1] == nums[i - 2]) { + dp[i] = dp[i] || dp[i - 2]; + } + if (i > 2 && ((nums[i - 1] == nums[i - 2] && nums[i - 2] == nums[i - 3]) || + (nums[i - 3] + 1 == nums[i - 2] && nums[i - 2] + 1 == nums[i - 1]))) { + dp[i] = dp[i] || dp[i - 3]; + } + } + + return dp[nums.length]; + } +} +``` + +```cpp +class Solution { +public: + bool validPartition(vector& nums) { + vector dp(nums.size() + 1, false); + dp[0] = true; + + for (int i = 2; i <= nums.size(); i++) { + if (nums[i - 1] == nums[i - 2]) { + dp[i] = dp[i] || dp[i - 2]; + } + if (i > 2 && ((nums[i - 1] == nums[i - 2] && nums[i - 2] == nums[i - 3]) || + (nums[i - 3] + 1 == nums[i - 2] && nums[i - 2] + 1 == nums[i - 1]))) { + dp[i] = dp[i] || dp[i - 3]; + } + } + + return dp[nums.size()]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + validPartition(nums) { + const dp = Array(nums.length + 1).fill(false); + dp[0] = true; + + for (let i = 2; i <= nums.length; i++) { + if (nums[i - 1] === nums[i - 2]) { + dp[i] = dp[i] || dp[i - 2]; + } + if (i > 2 && ((nums[i - 1] === nums[i - 2] && nums[i - 2] === nums[i - 3]) || + (nums[i - 3] + 1 === nums[i - 2] && nums[i - 2] + 1 === nums[i - 1]))) { + dp[i] = dp[i] || dp[i - 3]; + } + } + + return dp[nums.length]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def validPartition(self, nums: List[int]) -> bool: + dp = [False, True, True] + + for i in range(len(nums) - 2, -1, -1): + dp1 = dp[0] + if nums[i] == nums[i + 1] and dp[1]: + dp[0] = True + elif i < len(nums) - 2 and dp[2] and ( + (nums[i] == nums[i + 1] == nums[i + 2]) or + (nums[i] + 1 == nums[i + 1] and nums[i + 1] == nums[i + 2] - 1) + ): + dp[0] = True + else: + dp[0] = False + dp[2] = dp[1] + dp[1] = dp1 + + return dp[0] +``` + +```java +public class Solution { + public boolean validPartition(int[] nums) { + boolean[] dp = new boolean[3]; + dp[2] = true; + dp[1] = true; + dp[0] = false; + + for (int i = nums.length - 2; i >= 0; i--) { + boolean dp1 = dp[0]; + if (nums[i] == nums[i + 1] && dp[1]) { + dp[0] = true; + } else if (i < nums.length - 2 && dp[2] && + ((nums[i] == nums[i + 1] && nums[i] == nums[i + 2]) || + (nums[i] + 1 == nums[i + 1] && nums[i + 1] == nums[i + 2] - 1))) { + dp[0] = true; + } else { + dp[0] = false; + } + dp[2] = dp[1]; + dp[1] = dp1; + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + bool validPartition(vector& nums) { + bool dp[3] = {false, true, true}; + + for (int i = nums.size() - 2; i >= 0; --i) { + bool dp1 = dp[0]; + if (nums[i] == nums[i + 1] && dp[1]) { + dp[0] = true; + } else if (i < nums.size() - 2 && dp[2] && + ((nums[i] == nums[i + 1] && nums[i] == nums[i + 2]) || + (nums[i] + 1 == nums[i + 1] && nums[i + 1] == nums[i + 2] - 1))) { + dp[0] = true; + } else { + dp[0] = false; + } + dp[2] = dp[1]; + dp[1] = dp1; + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + validPartition(nums) { + let dp = [false, true, true]; + + for (let i = nums.length - 2; i >= 0; i--) { + let dp1 = dp[0]; + if (nums[i] === nums[i + 1] && dp[1]) { + dp[0] = true; + } else if (i < nums.length - 2 && dp[2] && + ((nums[i] === nums[i + 1] && nums[i] === nums[i + 2]) || + (nums[i] + 1 === nums[i + 1] && nums[i + 1] === nums[i + 2] - 1))) { + dp[0] = true; + } else { + dp[0] = false; + } + dp[2] = dp[1]; + dp[1] = dp1; + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/check-if-two-string-arrays-are-equivalent.md b/articles/check-if-two-string-arrays-are-equivalent.md new file mode 100644 index 000000000..39805de58 --- /dev/null +++ b/articles/check-if-two-string-arrays-are-equivalent.md @@ -0,0 +1,270 @@ +## 1. Concatenate Strings + +::tabs-start + +```python +class Solution: + def arrayStringsAreEqual(self, word1: List[str], word2: List[str]) -> bool: + return "".join(word1) == "".join(word2) +``` + +```java +public class Solution { + public boolean arrayStringsAreEqual(String[] word1, String[] word2) { + return String.join("", word1).equals(String.join("", word2)); + } +} +``` + +```cpp +class Solution { +public: + bool arrayStringsAreEqual(vector& word1, vector& word2) { + string str1 = accumulate(word1.begin(), word1.end(), string()); + string str2 = accumulate(word2.begin(), word2.end(), string()); + return str1 == str2; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} word1 + * @param {string[]} word2 + * @return {boolean} + */ + arrayStringsAreEqual(word1, word2) { + return word1.join("") === word2.join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ and $m$ are the total number of characters in both the arrays $word1$ and $word2$, respectively. + +--- + +## 2. Concatenate Strings Of One Array + +::tabs-start + +```python +class Solution: + def arrayStringsAreEqual(self, word1: List[str], word2: List[str]) -> bool: + s1 = "".join(word1) + i = 0 + for w in word2: + for c in w: + if i == len(s1) or s1[i] != c: + return False + i += 1 + return i == len(s1) +``` + +```java +public class Solution { + public boolean arrayStringsAreEqual(String[] word1, String[] word2) { + StringBuilder s1 = new StringBuilder(); + for (String w : word1) { + s1.append(w); + } + + int i = 0; + for (String w : word2) { + for (char c : w.toCharArray()) { + if (i == s1.length() || s1.charAt(i) != c) { + return false; + } + i++; + } + } + return i == s1.length(); + } +} +``` + +```cpp +class Solution { +public: + bool arrayStringsAreEqual(vector& word1, vector& word2) { + string s1 = ""; + for (string w : word1) s1 += w; + + int i = 0; + for (string w : word2) { + for (char c : w) { + if (i == s1.length() || s1[i] != c) return false; + i++; + } + } + return i == s1.length(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} word1 + * @param {string[]} word2 + * @return {boolean} + */ + arrayStringsAreEqual(word1, word2) { + let s1 = word1.join(""); + let i = 0; + + for (let w of word2) { + for (let c of w) { + if (i === s1.length || s1[i] !== c) { + return false; + } + i++; + } + } + return i === s1.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n)$ + +> Where $n$ and $m$ are the total number of characters in both the arrays $word1$ and $word2$, respectively. + +--- + +## 3. Two Pointers + +::tabs-start + +```python +class Solution: + def arrayStringsAreEqual(self, word1: List[str], word2: List[str]) -> bool: + w1 = w2 = 0 # Index of word + i = j = 0 # Index of character + + while w1 < len(word1) and w2 < len(word2): + if word1[w1][i] != word2[w2][j]: + return False + + i, j = i + 1, j + 1 + + if i == len(word1[w1]): + w1 += 1 + i = 0 + if j == len(word2[w2]): + w2 += 1 + j = 0 + + return w1 == len(word1) and w2 == len(word2) +``` + +```java +public class Solution { + public boolean arrayStringsAreEqual(String[] word1, String[] word2) { + int w1 = 0, w2 = 0; // Index of word + int i = 0, j = 0; // Index of character + + while (w1 < word1.length && w2 < word2.length) { + if (word1[w1].charAt(i) != word2[w2].charAt(j)) { + return false; + } + + i++; + j++; + + if (i == word1[w1].length()) { + w1++; + i = 0; + } + if (j == word2[w2].length()) { + w2++; + j = 0; + } + } + return w1 == word1.length && w2 == word2.length; + } +} +``` + +```cpp +class Solution { +public: + bool arrayStringsAreEqual(vector& word1, vector& word2) { + int w1 = 0, w2 = 0; // Index of word + int i = 0, j = 0; // Index of character + + while (w1 < word1.size() && w2 < word2.size()) { + if (word1[w1][i] != word2[w2][j]) { + return false; + } + + i++; + j++; + + if (i == word1[w1].size()) { + w1++; + i = 0; + } + if (j == word2[w2].size()) { + w2++; + j = 0; + } + } + return w1 == word1.size() && w2 == word2.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} word1 + * @param {string[]} word2 + * @return {boolean} + */ + arrayStringsAreEqual(word1, word2) { + let w1 = 0, w2 = 0; // Index of word + let i = 0, j = 0; // Index of character + + while (w1 < word1.length && w2 < word2.length) { + if (word1[w1][i] !== word2[w2][j]) { + return false; + } + + i++; + j++; + + if (i === word1[w1].length) { + w1++; + i = 0; + } + if (j === word2[w2].length) { + w2++; + j = 0; + } + } + return w1 === word1.length && w2 === word2.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(1)$ extra space. + +> Where $n$ and $m$ are the total number of characters in both the arrays $word1$ and $word2$, respectively. \ No newline at end of file diff --git a/articles/cherry-pickup-ii.md b/articles/cherry-pickup-ii.md new file mode 100644 index 000000000..cc5ccc3da --- /dev/null +++ b/articles/cherry-pickup-ii.md @@ -0,0 +1,559 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def cherryPickup(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + + def dfs(r, c1, c2): + if c1 < 0 or c2 < 0 or c1 >= COLS or c2 >= COLS or c1 > c2: + return 0 + if r == ROWS - 1: + return grid[r][c1] + (grid[r][c2] if c1 != c2 else 0) + + res = 0 + for c1_d in [-1, 0, 1]: + for c2_d in [-1, 0, 1]: + res = max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d)) + + return res + grid[r][c1] + (grid[r][c2] if c1 != c2 else 0) + + return dfs(0, 0, COLS - 1) +``` + +```java +public class Solution { + public int cherryPickup(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + + return dfs(0, 0, COLS - 1, grid, ROWS, COLS); + } + + private int dfs(int r, int c1, int c2, int[][] grid, int ROWS, int COLS) { + if (c1 < 0 || c2 < 0 || c1 >= COLS || c2 >= COLS || c1 > c2) { + return 0; + } + if (r == ROWS - 1) { + return grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + } + + int res = 0; + for (int c1_d = -1; c1_d <= 1; c1_d++) { + for (int c2_d = -1; c2_d <= 1; c2_d++) { + res = Math.max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d, grid, ROWS, COLS)); + } + } + return res + grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + } +} +``` + +```cpp +class Solution { +public: + int cherryPickup(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + return dfs(0, 0, COLS - 1, grid, ROWS, COLS); + } + +private: + int dfs(int r, int c1, int c2, vector>& grid, int ROWS, int COLS) { + if (c1 < 0 || c2 < 0 || c1 >= COLS || c2 >= COLS || c1 > c2) { + return 0; + } + if (r == ROWS - 1) { + return grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + } + + int res = 0; + for (int c1_d = -1; c1_d <= 1; c1_d++) { + for (int c2_d = -1; c2_d <= 1; c2_d++) { + res = max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d, grid, ROWS, COLS)); + } + } + return res + grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + cherryPickup(grid) { + const ROWS = grid.length, COLS = grid[0].length; + + const dfs = (r, c1, c2) => { + if (c1 < 0 || c2 < 0 || c1 >= COLS || c2 >= COLS || c1 > c2) return 0; + if (r === ROWS - 1) { + return grid[r][c1] + (c1 === c2 ? 0 : grid[r][c2]); + } + + let res = 0; + for (let c1_d = -1; c1_d <= 1; c1_d++) { + for (let c2_d = -1; c2_d <= 1; c2_d++) { + res = Math.max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d)); + } + } + return res + grid[r][c1] + (c1 === c2 ? 0 : grid[r][c2]); + }; + + return dfs(0, 0, COLS - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * 9 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +> Where $n$ is the number of rows and $m$ is the number of columns in the grid. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def cherryPickup(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + cache = {} + + def dfs(r, c1, c2): + if (r, c1, c2) in cache: + return cache[(r, c1, c2)] + if c1 == c2 or min(c1, c2) < 0 or max(c1, c2) >= COLS: + return 0 + if r == ROWS - 1: + return grid[r][c1] + grid[r][c2] + + res = 0 + for c1_d in [-1, 0, 1]: + for c2_d in [-1, 0, 1]: + res = max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d)) + + cache[(r, c1, c2)] = res + grid[r][c1] + (grid[r][c2] if c1 != c2 else 0) + return cache[(r, c1, c2)] + + return dfs(0, 0, COLS - 1) +``` + +```java +public class Solution { + private int[][][] cache; + public int cherryPickup(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + cache = new int[ROWS][COLS][COLS]; + for (int[][] i : cache) { + for (int[] j : i) { + Arrays.fill(j, -1); + } + } + + return dfs(0, 0, COLS - 1, grid); + } + + private int dfs(int r, int c1, int c2, int[][] grid) { + if (Math.min(c1, c2) < 0 || Math.max(c1, c2) >= grid[0].length) { + return 0; + } + if (cache[r][c1][c2] != -1) { + return cache[r][c1][c2]; + } + if (r == grid.length - 1) { + return cache[r][c1][c2] = grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + } + + int res = 0; + for (int c1_d = -1; c1_d <= 1; c1_d++) { + for (int c2_d = -1; c2_d <= 1; c2_d++) { + res = Math.max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d, grid)); + } + } + return cache[r][c1][c2] = res + grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + } +} +``` + +```cpp +class Solution { + vector>> cache; + +public: + int cherryPickup(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + cache.assign(ROWS, vector>(COLS, vector(COLS, -1))); + return dfs(0, 0, COLS - 1, grid); + } + +private: + int dfs(int r, int c1, int c2, vector>& grid) { + if (min(c1, c2) < 0 || max(c1, c2) >= grid[0].size()) { + return 0; + } + if (cache[r][c1][c2] != -1) { + return cache[r][c1][c2]; + } + if (r == grid.size() - 1) { + return cache[r][c1][c2] = grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + } + + int res = 0; + for (int c1_d = -1; c1_d <= 1; c1_d++) { + for (int c2_d = -1; c2_d <= 1; c2_d++) { + res = max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d, grid)); + } + } + return cache[r][c1][c2] = res + grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + cherryPickup(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const cache = Array.from({ length: ROWS }, () => + Array.from({ length: COLS }, () => + Array(COLS).fill(-1) + ) + ); + + const dfs = (r, c1, c2) => { + if (Math.min(c1, c2) < 0 || Math.max(c1, c2) >= COLS) { + return 0; + } + if (cache[r][c1][c2] !== -1) return cache[r][c1][c2]; + if (r === ROWS - 1) { + return cache[r][c1][c2] = grid[r][c1] + (c1 === c2 ? 0 : grid[r][c2]); + } + + let res = 0; + for (let c1_d = -1; c1_d <= 1; c1_d++) { + for (let c2_d = -1; c2_d <= 1; c2_d++) { + res = Math.max(res, dfs(r + 1, c1 + c1_d, c2 + c2_d)); + } + } + return cache[r][c1][c2] = res + grid[r][c1] + (c1 === c2 ? 0 : grid[r][c2]); + }; + + return dfs(0, 0, COLS - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m ^ 2)$ +* Space complexity: $O(n * m ^ 2)$ + +> Where $n$ is the number of rows and $m$ is the number of columns in the grid. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def cherryPickup(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + dp = [[[0] * COLS for _ in range(COLS)] for _ in range(ROWS)] + + for r in range(ROWS - 1, -1, -1): + for c1 in range(COLS): + for c2 in range(COLS): + res = grid[r][c1] + if c1 != c2: + res += grid[r][c2] + + if r != ROWS - 1: + max_cherries = 0 + for d1 in [-1, 0, 1]: + for d2 in [-1, 0, 1]: + nc1, nc2 = c1 + d1, c2 + d2 + if 0 <= nc1 < COLS and 0 <= nc2 < COLS: + max_cherries = max(max_cherries, dp[r + 1][nc1][nc2]) + res += max_cherries + + dp[r][c1][c2] = res + + return dp[0][0][COLS - 1] +``` + +```java +public class Solution { + public int cherryPickup(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int[][][] dp = new int[ROWS][COLS][COLS]; + + for (int r = ROWS - 1; r >= 0; r--) { + for (int c1 = 0; c1 < COLS; c1++) { + for (int c2 = 0; c2 < COLS; c2++) { + int res = grid[r][c1]; + if (c1 != c2) { + res += grid[r][c2]; + } + + if (r != ROWS - 1) { + int maxCherries = 0; + for (int d1 = -1; d1 <= 1; d1++) { + for (int d2 = -1; d2 <= 1; d2++) { + int nc1 = c1 + d1, nc2 = c2 + d2; + if (nc1 >= 0 && nc1 < COLS && nc2 >= 0 && nc2 < COLS) { + maxCherries = Math.max(maxCherries, dp[r + 1][nc1][nc2]); + } + } + } + res += maxCherries; + } + + dp[r][c1][c2] = res; + } + } + } + + return dp[0][0][COLS - 1]; + } +} +``` + +```cpp +class Solution { +public: + int cherryPickup(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + int dp[ROWS][COLS][COLS]; + + for (int r = ROWS - 1; r >= 0; r--) { + for (int c1 = 0; c1 < COLS; c1++) { + for (int c2 = 0; c2 < COLS; c2++) { + int res = grid[r][c1]; + if (c1 != c2) { + res += grid[r][c2]; + } + + if (r != ROWS - 1) { + int maxCherries = 0; + for (int d1 = -1; d1 <= 1; d1++) { + for (int d2 = -1; d2 <= 1; d2++) { + int nc1 = c1 + d1, nc2 = c2 + d2; + if (nc1 >= 0 && nc1 < COLS && nc2 >= 0 && nc2 < COLS) { + maxCherries = max(maxCherries, dp[r + 1][nc1][nc2]); + } + } + } + res += maxCherries; + } + + dp[r][c1][c2] = res; + } + } + } + + return dp[0][0][COLS - 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + cherryPickup(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const dp = Array.from({ length: ROWS }, () => + Array.from({ length: COLS }, () => Array(COLS).fill(0)) + ); + + for (let r = ROWS - 1; r >= 0; r--) { + for (let c1 = 0; c1 < COLS; c1++) { + for (let c2 = 0; c2 < COLS; c2++) { + let res = grid[r][c1]; + if (c1 !== c2) { + res += grid[r][c2]; + } + + if (r !== ROWS - 1) { + let maxCherries = 0; + for (let d1 = -1; d1 <= 1; d1++) { + for (let d2 = -1; d2 <= 1; d2++) { + const nc1 = c1 + d1, nc2 = c2 + d2; + if (nc1 >= 0 && nc1 < COLS && nc2 >= 0 && nc2 < COLS) { + maxCherries = Math.max(maxCherries, dp[r + 1][nc1][nc2]); + } + } + } + res += maxCherries; + } + + dp[r][c1][c2] = res; + } + } + } + + return dp[0][0][COLS - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m ^ 2)$ +* Space complexity: $O(n * m ^ 2)$ + +> Where $n$ is the number of rows and $m$ is the number of columns in the grid. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def cherryPickup(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + dp = [[0] * COLS for _ in range(COLS)] + + for r in reversed(range(ROWS)): + cur_dp = [[0] * COLS for _ in range(COLS)] + for c1 in range(COLS): + for c2 in range(c1, COLS): + max_cherries = 0 + cherries = grid[r][c1] + (grid[r][c2] if c1 != c2 else 0) + for d1 in [-1, 0, 1]: + for d2 in [-1, 0, 1]: + nc1, nc2 = c1 + d1, c2 + d2 + if 0 <= nc1 < COLS and 0 <= nc2 < COLS: + max_cherries = max(max_cherries, cherries + dp[nc1][nc2]) + cur_dp[c1][c2] = max_cherries + dp = cur_dp + + return dp[0][COLS - 1] +``` + +```java +public class Solution { + public int cherryPickup(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int[][] dp = new int[COLS][COLS]; + + for (int r = ROWS - 1; r >= 0; r--) { + int[][] cur_dp = new int[COLS][COLS]; + for (int c1 = 0; c1 < COLS; c1++) { + for (int c2 = c1; c2 < COLS; c2++) { + int maxCherries = 0; + int cherries = grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + for (int d1 = -1; d1 <= 1; d1++) { + for (int d2 = -1; d2 <= 1; d2++) { + int nc1 = c1 + d1, nc2 = c2 + d2; + if (nc1 >= 0 && nc1 < COLS && nc2 >= 0 && nc2 < COLS) { + maxCherries = Math.max(maxCherries, cherries + dp[nc1][nc2]); + } + } + } + cur_dp[c1][c2] = maxCherries; + } + } + dp = cur_dp; + } + return dp[0][COLS - 1]; + } +} +``` + +```cpp +class Solution { +public: + int cherryPickup(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + vector> dp(COLS, vector(COLS, 0)); + + for (int r = ROWS - 1; r >= 0; r--) { + vector> cur_dp(COLS, vector(COLS, 0)); + for (int c1 = 0; c1 < COLS; c1++) { + for (int c2 = c1; c2 < COLS; c2++) { + int maxCherries = 0; + int cherries = grid[r][c1] + (c1 == c2 ? 0 : grid[r][c2]); + for (int d1 = -1; d1 <= 1; d1++) { + for (int d2 = -1; d2 <= 1; d2++) { + int nc1 = c1 + d1, nc2 = c2 + d2; + if (nc1 >= 0 && nc1 < COLS && nc2 >= 0 && nc2 < COLS) { + maxCherries = max(maxCherries, cherries + dp[nc1][nc2]); + } + } + } + cur_dp[c1][c2] = maxCherries; + } + } + dp = cur_dp; + } + return dp[0][COLS - 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + cherryPickup(grid) { + const ROWS = grid.length, COLS = grid[0].length; + let dp = Array.from({ length: COLS }, () => Array(COLS).fill(0)); + + for (let r = ROWS - 1; r >= 0; r--) { + const cur_dp = Array.from({ length: COLS }, () => Array(COLS).fill(0)); + for (let c1 = 0; c1 < COLS; c1++) { + for (let c2 = c1; c2 < COLS; c2++) { + let maxCherries = 0; + const cherries = grid[r][c1] + (c1 === c2 ? 0 : grid[r][c2]); + for (let d1 = -1; d1 <= 1; d1++) { + for (let d2 = -1; d2 <= 1; d2++) { + const nc1 = c1 + d1, nc2 = c2 + d2; + if (nc1 >= 0 && nc1 < COLS && nc2 >= 0 && nc2 < COLS) { + maxCherries = Math.max(maxCherries, cherries + dp[nc1][nc2]); + } + } + } + cur_dp[c1][c2] = maxCherries; + } + } + dp = cur_dp; + } + + return dp[0][COLS - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m ^ 2)$ +* Space complexity: $O(m ^ 2)$ + +> Where $n$ is the number of rows and $m$ is the number of columns in the grid. \ No newline at end of file diff --git a/articles/circular-sentence.md b/articles/circular-sentence.md new file mode 100644 index 000000000..c4e969dc1 --- /dev/null +++ b/articles/circular-sentence.md @@ -0,0 +1,154 @@ +## 1. Splitting the String + +::tabs-start + +```python +class Solution: + def isCircularSentence(self, sentence: str) -> bool: + w = sentence.split(" ") + + for i in range(len(w)): + if w[i][0] != w[i - 1][-1]: + return False + + return True +``` + +```java +public class Solution { + public boolean isCircularSentence(String sentence) { + String[] w = sentence.split(" "); + int n = w.length; + + for (int i = 0; i < n; i++) { + char start = w[i].charAt(0); + char end = w[(i - 1 + n) % n].charAt(w[(i - 1 + n) % n].length() - 1); + if (start != end) { + return false; + } + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isCircularSentence(string sentence) { + vector w; + stringstream ss(sentence); + string word; + + while (ss >> word) { + w.push_back(word); + } + + for (int i = 0; i < w.size(); i++) { + char start = w[i][0]; + char end = w[(i - 1 + w.size()) % w.size()].back(); + if (start != end) { + return false; + } + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} sentence + * @return {boolean} + */ + isCircularSentence(sentence) { + const w = sentence.split(" "); + + for (let i = 0; i < w.length; i++) { + const start = w[i][0]; + const prevEnd = w[(i - 1 + w.length) % w.length].slice(-1); + if (start !== prevEnd) { + return false; + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Iteration (Space Optimized) + +::tabs-start + +```python +class Solution: + def isCircularSentence(self, sentence: str) -> bool: + for i in range(len(sentence)): + if sentence[i] == " " and sentence[i - 1] != sentence[i + 1]: + return False + return sentence[0] == sentence[-1] +``` + +```java +public class Solution { + public boolean isCircularSentence(String sentence) { + for (int i = 0; i < sentence.length(); i++) { + if (sentence.charAt(i) == ' ' && sentence.charAt(i - 1) != sentence.charAt(i + 1)) { + return false; + } + } + return sentence.charAt(0) == sentence.charAt(sentence.length() - 1); + } +} +``` + +```cpp +class Solution { +public: + bool isCircularSentence(string sentence) { + for (int i = 0; i < sentence.size(); i++) { + if (sentence[i] == ' ' && sentence[i - 1] != sentence[i + 1]) { + return false; + } + } + return sentence.front() == sentence.back(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} sentence + * @return {boolean} + */ + isCircularSentence(sentence) { + for (let i = 0; i < sentence.length; i++) { + if (sentence[i] === ' ' && sentence[i - 1] !== sentence[i + 1]) { + return false; + } + } + return sentence[0] === sentence[sentence.length - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/climbing-stairs.md b/articles/climbing-stairs.md new file mode 100644 index 000000000..126511582 --- /dev/null +++ b/articles/climbing-stairs.md @@ -0,0 +1,993 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def climbStairs(self, n: int) -> int: + + def dfs(i): + if i >= n: + return i == n + return dfs(i + 1) + dfs(i + 2) + + return dfs(0) +``` + +```java +public class Solution { + public int climbStairs(int n) { + return dfs(n, 0); + } + + public int dfs(int n, int i) { + if (i >= n) return i == n ? 1 : 0; + return dfs(n, i + 1) + dfs(n, i + 2); + } +} +``` + +```cpp +class Solution { +public: + int climbStairs(int n) { + return dfs(n, 0); + } + + int dfs(int n, int i) { + if (i >= n) return i == n; + return dfs(n, i + 1) + dfs(n, i + 2); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + climbStairs(n) { + + const dfs = (i) => { + if (i >= n) return i == n; + return dfs(i + 1) + dfs(i + 2); + } + return dfs(0); + } +} +``` + +```csharp +public class Solution { + public int ClimbStairs(int n) { + return Dfs(n, 0); + } + + public int Dfs(int n, int i) { + if (i >= n) return i == n ? 1 : 0; + return Dfs(n, i + 1) + Dfs(n, i + 2); + } +} +``` + +```go +func climbStairs(n int) int { + var dfs func(i int) int + dfs = func(i int) int { + if i >= n { + if i == n { + return 1 + } + return 0 + } + return dfs(i + 1) + dfs(i + 2) + } + return dfs(0) +} +``` + +```kotlin +class Solution { + fun climbStairs(n: Int): Int { + fun dfs(i: Int): Int { + if (i >= n) return if (i == n) 1 else 0 + return dfs(i + 1) + dfs(i + 2) + } + return dfs(0) + } +} +``` + +```swift +class Solution { + func climbStairs(_ n: Int) -> Int { + func dfs(_ i: Int) -> Int { + if i >= n { + return i == n ? 1 : 0 + } + return dfs(i + 1) + dfs(i + 2) + } + + return dfs(0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def climbStairs(self, n: int) -> int: + cache = [-1] * n + def dfs(i): + if i >= n: + return i == n + if cache[i] != -1: + return cache[i] + cache[i] = dfs(i + 1) + dfs(i + 2) + return cache[i] + + return dfs(0) +``` + +```java +public class Solution { + int[] cache; + public int climbStairs(int n) { + cache = new int[n]; + for (int i = 0; i < n; i++) { + cache[i] = -1; + } + return dfs(n, 0); + } + + public int dfs(int n, int i) { + if (i >= n) return i == n ? 1 : 0; + if (cache[i] != -1) return cache[i]; + return cache[i] = dfs(n, i + 1) + dfs(n, i + 2); + } +} +``` + +```cpp +class Solution { +public: + vector cache; + int climbStairs(int n) { + cache.resize(n, -1); + return dfs(n, 0); + } + + int dfs(int n, int i) { + if (i >= n) return i == n; + if (cache[i] != -1) return cache[i]; + return cache[i] = dfs(n, i + 1) + dfs(n, i + 2); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + climbStairs(n) { + const cache = new Int32Array(n).fill(-1); + const dfs = (i) => { + if (i >= n) return i == n; + if (cache[i] != -1) return cache[i]; + return cache[i] = dfs(i + 1) + dfs(i + 2); + } + return dfs(0); + } +} +``` + +```csharp +public class Solution { + int[] cache; + public int ClimbStairs(int n) { + cache = new int[n]; + for (int i = 0; i < n; i++) { + cache[i] = -1; + } + return Dfs(n, 0); + } + + public int Dfs(int n, int i) { + if (i >= n) return i == n ? 1 : 0; + if (cache[i] != -1) return cache[i]; + return cache[i] = Dfs(n, i + 1) + Dfs(n, i + 2); + } +} +``` + +```go +func climbStairs(n int) int { + cache := make([]int, n+1) + for i := 0; i <= n; i++ { + cache[i] = -1 + } + + var dfs func(i int) int + dfs = func(i int) int { + if i >= n { + if i == n { + return 1 + } + return 0 + } + if cache[i] != -1 { + return cache[i] + } + cache[i] = dfs(i + 1) + dfs(i + 2) + return cache[i] + } + return dfs(0) +} +``` + +```kotlin +class Solution { + fun climbStairs(n: Int): Int { + var cache = IntArray(n+1){-1} + fun dfs(i: Int): Int { + if (i >= n) return if (i == n) 1 else 0 + if (cache[i] != -1) return cache[i] + cache[i] = dfs(i + 1) + dfs(i + 2) + return cache[i] + } + return dfs(0) + } +} +``` + +```swift +class Solution { + func climbStairs(_ n: Int) -> Int { + var cache = Array(repeating: -1, count: n) + + func dfs(_ i: Int) -> Int { + if i >= n { + return i == n ? 1 : 0 + } + if cache[i] != -1 { + return cache[i] + } + cache[i] = dfs(i + 1) + dfs(i + 2) + return cache[i] + } + + return dfs(0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def climbStairs(self, n: int) -> int: + if n <= 2: + return n + dp = [0] * (n + 1) + dp[1], dp[2] = 1, 2 + for i in range(3, n + 1): + dp[i] = dp[i - 1] + dp[i - 2] + return dp[n] +``` + +```java +public class Solution { + public int climbStairs(int n) { + if (n <= 2) { + return n; + } + int[] dp = new int[n + 1]; + dp[1] = 1; + dp[2] = 2; + for (int i = 3; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + return dp[n]; + } +} +``` + +```cpp +class Solution { +public: + int climbStairs(int n) { + if (n <= 2) { + return n; + } + vector dp(n + 1); + dp[1] = 1; + dp[2] = 2; + for (int i = 3; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + return dp[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + climbStairs(n) { + if (n <= 2) { + return n; + } + let dp = new Array(n + 1).fill(0); + dp[1] = 1; + dp[2] = 2; + for (let i = 3; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + return dp[n]; + } +} +``` + +```csharp +public class Solution { + public int ClimbStairs(int n) { + if (n <= 2) { + return n; + } + int[] dp = new int[n + 1]; + dp[1] = 1; + dp[2] = 2; + for (int i = 3; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + return dp[n]; + } +} +``` + +```go +func climbStairs(n int) int { + if n <= 2 { + return n + } + dp := make([]int, n+1) + dp[1] = 1 + dp[2] = 2 + for i := 3; i <= n; i++ { + dp[i] = dp[i - 1] + dp[i - 2] + } + return dp[n] +} +``` + +```kotlin +class Solution { + fun climbStairs(n: Int): Int { + if (n <= 2) return n + var dp = IntArray(n + 1) + dp[1] = 1 + dp[2] = 2 + for (i in 3..n) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + return dp[n]; + } +} +``` + +```swift +class Solution { + func climbStairs(_ n: Int) -> Int { + if n <= 2 { + return n + } + var dp = Array(repeating: 0, count: n + 1) + dp[1] = 1 + dp[2] = 2 + for i in 3...n { + dp[i] = dp[i - 1] + dp[i - 2] + } + return dp[n] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def climbStairs(self, n: int) -> int: + one, two = 1, 1 + + for i in range(n - 1): + temp = one + one = one + two + two = temp + + return one +``` + +```java +public class Solution { + public int climbStairs(int n) { + int one = 1, two = 1; + + for (int i = 0; i < n - 1; i++) { + int temp = one; + one = one + two; + two = temp; + } + + return one; + } +} +``` + +```cpp +class Solution { +public: + int climbStairs(int n) { + int one = 1, two = 1; + + for (int i = 0; i < n - 1; i++) { + int temp = one; + one = one + two; + two = temp; + } + + return one; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + climbStairs(n) { + let one = 1, two = 1; + + for (let i = 0; i < n - 1; i++) { + let temp = one; + one = one + two; + two = temp; + } + + return one; + } +} +``` + +```csharp +public class Solution { + public int ClimbStairs(int n) { + int one = 1, two = 1; + + for (int i = 0; i < n - 1; i++) { + int temp = one; + one = one + two; + two = temp; + } + + return one; + } +} +``` + +```go +func climbStairs(n int) int { + one := 1 + two := 1 + + for i := 0; i < n-1; i++ { + temp := one + one += two + two = temp + } + + return one +} +``` + +```kotlin +class Solution { + fun climbStairs(n: Int): Int { + var one = 1 + var two = 1 + + for (i in 0..(n - 2)) { + var temp = one + one += two + two = temp + } + + return one + } +} +``` + +```swift +class Solution { + func climbStairs(_ n: Int) -> Int { + var one = 1, two = 1 + + for _ in 0..<(n - 1) { + let temp = one + one = one + two + two = temp + } + + return one + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Matrix Exponentiation + +::tabs-start + +```python +class Solution: + def climbStairs(self, n: int) -> int: + if n == 1: + return 1 + + def matrix_mult(A, B): + return [[A[0][0] * B[0][0] + A[0][1] * B[1][0], + A[0][0] * B[0][1] + A[0][1] * B[1][1]], + [A[1][0] * B[0][0] + A[1][1] * B[1][0], + A[1][0] * B[0][1] + A[1][1] * B[1][1]]] + + def matrix_pow(M, p): + result = [[1, 0], [0, 1]] + base = M + + while p: + if p % 2 == 1: + result = matrix_mult(result, base) + base = matrix_mult(base, base) + p //= 2 + + return result + + M = [[1, 1], [1, 0]] + result = matrix_pow(M, n) + return result[0][0] +``` + +```java +public class Solution { + public int climbStairs(int n) { + if (n == 1) return 1; + + int[][] M = {{1, 1}, {1, 0}}; + int[][] result = matrixPow(M, n); + + return result[0][0]; + } + + private int[][] matrixMult(int[][] A, int[][] B) { + return new int[][] { + {A[0][0] * B[0][0] + A[0][1] * B[1][0], + A[0][0] * B[0][1] + A[0][1] * B[1][1]}, + {A[1][0] * B[0][0] + A[1][1] * B[1][0], + A[1][0] * B[0][1] + A[1][1] * B[1][1]} + }; + } + + private int[][] matrixPow(int[][] M, int p) { + int[][] result = {{1, 0}, {0, 1}}; + int[][] base = M; + + while (p > 0) { + if (p % 2 == 1) { + result = matrixMult(result, base); + } + base = matrixMult(base, base); + p /= 2; + } + + return result; + } +} +``` + +```cpp +class Solution { +public: + int climbStairs(int n) { + if (n == 1) return 1; + + vector> M = {{1, 1}, {1, 0}}; + vector> result = matrixPow(M, n); + + return result[0][0]; + } + +private: + vector> matrixMult(vector>& A, vector>& B) { + return {{A[0][0] * B[0][0] + A[0][1] * B[1][0], + A[0][0] * B[0][1] + A[0][1] * B[1][1]}, + {A[1][0] * B[0][0] + A[1][1] * B[1][0], + A[1][0] * B[0][1] + A[1][1] * B[1][1]}}; + } + + vector> matrixPow(vector>& M, int p) { + vector> result = {{1, 0}, {0, 1}}; + vector> base = M; + + while (p > 0) { + if (p % 2 == 1) { + result = matrixMult(result, base); + } + base = matrixMult(base, base); + p /= 2; + } + + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + climbStairs(n) { + if (n === 1) return 1; + + const matrixMult = (A, B) => { + return [ + [A[0][0] * B[0][0] + A[0][1] * B[1][0], + A[0][0] * B[0][1] + A[0][1] * B[1][1]], + [A[1][0] * B[0][0] + A[1][1] * B[1][0], + A[1][0] * B[0][1] + A[1][1] * B[1][1]] + ]; + }; + + const matrixPow = (M, p) => { + let result = [[1, 0], [0, 1]]; + let base = M; + + while (p > 0) { + if (p % 2 === 1) { + result = matrixMult(result, base); + } + base = matrixMult(base, base); + p = Math.floor(p / 2); + } + + return result; + }; + + const M = [[1, 1], [1, 0]]; + const result = matrixPow(M, n); + + return result[0][0]; + } +} +``` + +```csharp +public class Solution { + public int ClimbStairs(int n) { + if (n == 1) return 1; + + int[,] M = new int[,] {{1, 1}, {1, 0}}; + int[,] result = MatrixPow(M, n); + + return result[0, 0]; + } + + private int[,] MatrixMult(int[,] A, int[,] B) { + return new int[,] { + {A[0, 0] * B[0, 0] + A[0, 1] * B[1, 0], + A[0, 0] * B[0, 1] + A[0, 1] * B[1, 1]}, + {A[1, 0] * B[0, 0] + A[1, 1] * B[1, 0], + A[1, 0] * B[0, 1] + A[1, 1] * B[1, 1]} + }; + } + + private int[,] MatrixPow(int[,] M, int p) { + int[,] result = new int[,] {{1, 0}, {0, 1}}; + int[,] baseM = M; + + while (p > 0) { + if (p % 2 == 1) { + result = MatrixMult(result, baseM); + } + baseM = MatrixMult(baseM, baseM); + p /= 2; + } + + return result; + } +} +``` + +```go +func climbStairs(n int) int { + if n == 1 { + return 1 + } + + M := [][]int{{1, 1}, {1, 0}} + result := matrixPow(M, n) + + return result[0][0] +} + +func matrixMult(A, B [][]int) [][]int { + return [][]int{ + {A[0][0]*B[0][0] + A[0][1]*B[1][0], + A[0][0]*B[0][1] + A[0][1]*B[1][1]}, + {A[1][0]*B[0][0] + A[1][1]*B[1][0], + A[1][0]*B[0][1] + A[1][1]*B[1][1]}, + } +} + +func matrixPow(M [][]int, p int) [][]int { + result := [][]int{{1, 0}, {0, 1}} + base := M + + for p > 0 { + if p%2 == 1 { + result = matrixMult(result, base) + } + base = matrixMult(base, base) + p /= 2 + } + + return result +} +``` + +```kotlin +class Solution { + fun climbStairs(n: Int): Int { + if (n == 1) return 1 + + val M = arrayOf(intArrayOf(1, 1), intArrayOf(1, 0)) + val result = matrixPow(M, n) + + return result[0][0] + } + + private fun matrixMult(A: Array, B: Array): Array { + return arrayOf( + intArrayOf(A[0][0] * B[0][0] + A[0][1] * B[1][0], + A[0][0] * B[0][1] + A[0][1] * B[1][1]), + intArrayOf(A[1][0] * B[0][0] + A[1][1] * B[1][0], + A[1][0] * B[0][1] + A[1][1] * B[1][1]) + ) + } + + private fun matrixPow(M: Array, p: Int): Array { + var result = arrayOf(intArrayOf(1, 0), intArrayOf(0, 1)) + var base = M + var power = p + + while (power > 0) { + if (power % 2 == 1) { + result = matrixMult(result, base) + } + base = matrixMult(base, base) + power /= 2 + } + + return result + } +} +``` + +```swift +class Solution { + func climbStairs(_ n: Int) -> Int { + if n == 1 { + return 1 + } + + func matrixMult(_ A: [[Int]], _ B: [[Int]]) -> [[Int]] { + return [ + [A[0][0] * B[0][0] + A[0][1] * B[1][0], + A[0][0] * B[0][1] + A[0][1] * B[1][1]], + [A[1][0] * B[0][0] + A[1][1] * B[1][0], + A[1][0] * B[0][1] + A[1][1] * B[1][1]] + ] + } + + func matrixPow(_ M: [[Int]], _ p: Int) -> [[Int]] { + var result = [[1, 0], [0, 1]] + var base = M + var power = p + + while power > 0 { + if power % 2 == 1 { + result = matrixMult(result, base) + } + base = matrixMult(base, base) + power /= 2 + } + + return result + } + + let M = [[1, 1], [1, 0]] + let result = matrixPow(M, n) + return result[0][0] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 6. Math + +::tabs-start + +```python +class Solution: + def climbStairs(self, n: int) -> int: + sqrt5 = math.sqrt(5) + phi = (1 + sqrt5) / 2 + psi = (1 - sqrt5) / 2 + n += 1 + return round((phi**n - psi**n) / sqrt5) +``` + +```java +public class Solution { + public int climbStairs(int n) { + double sqrt5 = Math.sqrt(5); + double phi = (1 + sqrt5) / 2; + double psi = (1 - sqrt5) / 2; + n++; + return (int) Math.round((Math.pow(phi, n) - + Math.pow(psi, n)) / sqrt5); + } +} +``` + +```cpp +class Solution { +public: + int climbStairs(int n) { + double sqrt5 = sqrt(5); + double phi = (1 + sqrt5) / 2; + double psi = (1 - sqrt5) / 2; + n++; + return round((pow(phi, n) - pow(psi, n)) / sqrt5); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + climbStairs(n) { + let sqrt5 = Math.sqrt(5); + let phi = (1 + sqrt5) / 2; + let psi = (1 - sqrt5) / 2; + n++; + return Math.round((Math.pow(phi, n) - + Math.pow(psi, n)) / sqrt5); + } +} +``` + +```csharp +public class Solution { + public int ClimbStairs(int n) { + double sqrt5 = Math.Sqrt(5); + double phi = (1 + sqrt5) / 2; + double psi = (1 - sqrt5) / 2; + n++; + return (int) Math.Round((Math.Pow(phi, n) - + Math.Pow(psi, n)) / sqrt5); + } +} +``` + +```go +func climbStairs(n int) int { + sqrt5 := math.Sqrt(5) + phi := (1 + sqrt5) / 2 + psi := (1 - sqrt5) / 2 + n++ + return int(math.Round((math.Pow(phi, float64(n)) - + math.Pow(psi, float64(n))) / sqrt5)) +} +``` + +```kotlin +class Solution { + fun climbStairs(n: Int): Int { + val sqrt5 = sqrt(5.0) + val phi = (1 + sqrt5) / 2 + val psi = (1 - sqrt5) / 2 + return round((phi.pow(n + 1) - psi.pow(n + 1)) / sqrt5).toInt() + } +} +``` + +```swift +class Solution { + func climbStairs(_ n: Int) -> Int { + let sqrt5 = sqrt(5.0) + let phi = (1.0 + sqrt5) / 2.0 + let psi = (1.0 - sqrt5) / 2.0 + let n = n + 1 + return Int(round((pow(phi, Double(n)) - pow(psi, Double(n))) / sqrt5)) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/clone-graph.md b/articles/clone-graph.md new file mode 100644 index 000000000..e71e55184 --- /dev/null +++ b/articles/clone-graph.md @@ -0,0 +1,676 @@ +## 1. Depth First Seacrh + +::tabs-start + +```python +""" +# Definition for a Node. +class Node: + def __init__(self, val = 0, neighbors = None): + self.val = val + self.neighbors = neighbors if neighbors is not None else [] +""" + +class Solution: + def cloneGraph(self, node: Optional['Node']) -> Optional['Node']: + oldToNew = {} + + def dfs(node): + if node in oldToNew: + return oldToNew[node] + + copy = Node(node.val) + oldToNew[node] = copy + for nei in node.neighbors: + copy.neighbors.append(dfs(nei)) + return copy + + return dfs(node) if node else None +``` + +```java +/* +Definition for a Node. +class Node { + public int val; + public List neighbors; + public Node() { + val = 0; + neighbors = new ArrayList(); + } + public Node(int _val) { + val = _val; + neighbors = new ArrayList(); + } + public Node(int _val, ArrayList _neighbors) { + val = _val; + neighbors = _neighbors; + } +} +*/ + +public class Solution { + public Node cloneGraph(Node node) { + Map oldToNew = new HashMap<>(); + + return dfs(node, oldToNew); + } + + private Node dfs(Node node, Map oldToNew) { + if (node == null) { + return null; + } + + if (oldToNew.containsKey(node)) { + return oldToNew.get(node); + } + + Node copy = new Node(node.val); + oldToNew.put(node, copy); + + for (Node nei : node.neighbors) { + copy.neighbors.add(dfs(nei, oldToNew)); + } + + return copy; + } +} +``` + +```cpp +/* +// Definition for a Node. +class Node { +public: + int val; + vector neighbors; + Node() { + val = 0; + neighbors = vector(); + } + Node(int _val) { + val = _val; + neighbors = vector(); + } + Node(int _val, vector _neighbors) { + val = _val; + neighbors = _neighbors; + } +}; +*/ + +class Solution { +public: + Node* cloneGraph(Node* node) { + map oldToNew; + return dfs(node, oldToNew); + } + + Node* dfs(Node* node, map& oldToNew) { + if (node == nullptr) { + return nullptr; + } + + if (oldToNew.count(node)) { + return oldToNew[node]; + } + + Node* copy = new Node(node->val); + oldToNew[node] = copy; + + for (Node* nei : node->neighbors) { + copy->neighbors.push_back(dfs(nei, oldToNew)); + } + + return copy; + } +}; +``` + +```javascript +/** + * // Definition for a Node. + * class Node { + * constructor(val = 0, neighbors = []) { + * this.val = val; + * this.neighbors = neighbors; + * } + * } + */ + +class Solution { + /** + * @param {Node} node + * @return {Node} + */ + cloneGraph(node) { + const oldToNew = new Map(); + return this.dfs(node, oldToNew); + } + + /** + * @param {Node} node + * @param {Map} oldToNew + * @return {Node} + */ + dfs(node, oldToNew) { + if (node === null) { + return null; + } + + if (oldToNew.has(node)) { + return oldToNew.get(node); + } + + const copy = new Node(node.val); + oldToNew.set(node, copy); + + for (const nei of node.neighbors) { + copy.neighbors.push(this.dfs(nei, oldToNew)); + } + + return copy; + } +} +``` + +```csharp +/* +// Definition for a Node. +public class Node { + public int val; + public IList neighbors; + + public Node() { + val = 0; + neighbors = new List(); + } + + public Node(int _val) { + val = _val; + neighbors = new List(); + } + + public Node(int _val, List _neighbors) { + val = _val; + neighbors = _neighbors; + } +} +*/ + +public class Solution { + public Node CloneGraph(Node node) { + Dictionary oldToNew = new Dictionary(); + return Dfs(node, oldToNew); + } + + private Node Dfs(Node node, Dictionary oldToNew) { + if (node == null) + return null; + + if (oldToNew.ContainsKey(node)) + return oldToNew[node]; + + Node copy = new Node(node.val); + oldToNew[node] = copy; + + foreach (Node nei in node.neighbors) + copy.neighbors.Add(Dfs(nei, oldToNew)); + + return copy; + } +} +``` + +```go +/** + * Definition for a Node. + * type Node struct { + * Val int + * Neighbors []*Node + * } + */ + +func cloneGraph(node *Node) *Node { + oldToNew := make(map[*Node]*Node) + + var dfs func(*Node) *Node + dfs = func(node *Node) *Node { + if node == nil { + return nil + } + + if _, found := oldToNew[node]; found { + return oldToNew[node] + } + + copy := &Node{Val: node.Val} + oldToNew[node] = copy + for _, nei := range node.Neighbors { + copy.Neighbors = append(copy.Neighbors, dfs(nei)) + } + return copy + } + + return dfs(node) +} +``` + +```kotlin +/** + * Definition for a Node. + * class Node(var `val`: Int) { + * var neighbors: ArrayList = ArrayList() + * } + */ + +class Solution { + fun cloneGraph(node: Node?): Node? { + if (node == null) return null + + val oldToNew = HashMap() + + fun dfs(node: Node): Node { + if (node in oldToNew) { + return oldToNew[node]!! + } + + val copy = Node(node.`val`) + oldToNew[node] = copy + + for (nei in node.neighbors) { + nei?.let { copy.neighbors.add(dfs(it)) } + } + + return copy + } + + return dfs(node) + } +} +``` + +```swift +/** + * Definition for a Node. + * public class Node { + * public var val: Int + * public var neighbors: [Node?] + * public init(_ val: Int) { + * self.val = val + * self.neighbors = [] + * } + * } + */ + +class Solution { + func cloneGraph(_ node: Node?) -> Node? { + var oldToNew = [Node: Node]() + + func dfs(_ node: Node?) -> Node? { + guard let node = node else { return nil } + + if let existingCopy = oldToNew[node] { + return existingCopy + } + + let copy = Node(node.val) + oldToNew[node] = copy + + for neighbor in node.neighbors { + if let clonedNeighbor = dfs(neighbor) { + copy.neighbors.append(clonedNeighbor) + } + } + + return copy + } + + return dfs(node) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +""" +# Definition for a Node. +class Node: + def __init__(self, val = 0, neighbors = None): + self.val = val + self.neighbors = neighbors if neighbors is not None else [] +""" + +class Solution: + def cloneGraph(self, node: Optional['Node']) -> Optional['Node']: + if not node: + return None + + oldToNew = {} + oldToNew[node] = Node(node.val) + q = deque([node]) + + while q: + cur = q.popleft() + for nei in cur.neighbors: + if nei not in oldToNew: + oldToNew[nei] = Node(nei.val) + q.append(nei) + oldToNew[cur].neighbors.append(oldToNew[nei]) + + return oldToNew[node] +``` + +```java +/* +Definition for a Node. +class Node { + public int val; + public List neighbors; + public Node() { + val = 0; + neighbors = new ArrayList(); + } + public Node(int _val) { + val = _val; + neighbors = new ArrayList(); + } + public Node(int _val, ArrayList _neighbors) { + val = _val; + neighbors = _neighbors; + } +} +*/ + +public class Solution { + public Node cloneGraph(Node node) { + if (node == null) return null; + Map oldToNew = new HashMap<>(); + Queue q = new LinkedList<>(); + oldToNew.put(node, new Node(node.val)); + q.add(node); + + while (!q.isEmpty()) { + Node cur = q.poll(); + for (Node nei : cur.neighbors) { + if (!oldToNew.containsKey(nei)) { + oldToNew.put(nei, new Node(nei.val)); + q.add(nei); + } + oldToNew.get(cur).neighbors.add(oldToNew.get(nei)); + } + } + return oldToNew.get(node); + } +} +``` + +```cpp +/* +// Definition for a Node. +class Node { +public: + int val; + vector neighbors; + Node() { + val = 0; + neighbors = vector(); + } + Node(int _val) { + val = _val; + neighbors = vector(); + } + Node(int _val, vector _neighbors) { + val = _val; + neighbors = _neighbors; + } +}; +*/ + +class Solution { +public: + Node* cloneGraph(Node* node) { + if (!node) return nullptr; + unordered_map oldToNew; + queue q; + oldToNew[node] = new Node(node->val); + q.push(node); + + while (!q.empty()) { + Node* cur = q.front(); + q.pop(); + for (Node* nei : cur->neighbors) { + if (oldToNew.find(nei) == oldToNew.end()) { + oldToNew[nei] = new Node(nei->val); + q.push(nei); + } + oldToNew[cur]->neighbors.push_back(oldToNew[nei]); + } + } + return oldToNew[node]; + } +}; +``` + +```javascript +/** + * // Definition for a Node. + * class Node { + * constructor(val = 0, neighbors = []) { + * this.val = val; + * this.neighbors = neighbors; + * } + * } + */ + +class Solution { + /** + * @param {Node} node + * @return {Node} + */ + cloneGraph(node) { + if (!node) return null; + const oldToNew = new Map(); + const q = new Queue(); + oldToNew.set(node, new Node(node.val)); + q.push(node); + + while (!q.isEmpty()) { + const cur = q.pop(); + for (const nei of cur.neighbors) { + if (!oldToNew.has(nei)) { + oldToNew.set(nei, new Node(nei.val)); + q.push(nei); + } + oldToNew.get(cur).neighbors.push(oldToNew.get(nei)); + } + } + return oldToNew.get(node); + } +} +``` + +```csharp +/* +// Definition for a Node. +public class Node { + public int val; + public IList neighbors; + + public Node() { + val = 0; + neighbors = new List(); + } + + public Node(int _val) { + val = _val; + neighbors = new List(); + } + + public Node(int _val, List _neighbors) { + val = _val; + neighbors = _neighbors; + } +} +*/ + +public class Solution { + public Node CloneGraph(Node node) { + if (node == null) return null; + var oldToNew = new Dictionary(); + var q = new Queue(); + oldToNew[node] = new Node(node.val); + q.Enqueue(node); + + while (q.Count > 0) { + var cur = q.Dequeue(); + foreach (var nei in cur.neighbors) { + if (!oldToNew.ContainsKey(nei)) { + oldToNew[nei] = new Node(nei.val); + q.Enqueue(nei); + } + oldToNew[cur].neighbors.Add(oldToNew[nei]); + } + } + return oldToNew[node]; + } +} +``` + +```go +/** + * Definition for a Node. + * type Node struct { + * Val int + * Neighbors []*Node + * } + */ + +func cloneGraph(node *Node) *Node { + if node == nil { + return nil + } + + oldToNew := make(map[*Node]*Node) + oldToNew[node] = &Node{Val: node.Val, Neighbors: make([]*Node, 0)} + queue := make([]*Node, 0) + queue = append(queue, node) + + for len(queue) > 0 { + cur := queue[0] + queue = queue[1:] + + for _, nei := range cur.Neighbors { + if _, exists := oldToNew[nei]; !exists { + oldToNew[nei] = &Node{Val: nei.Val, Neighbors: make([]*Node, 0)} + queue = append(queue, nei) + } + oldToNew[cur].Neighbors = append(oldToNew[cur].Neighbors, oldToNew[nei]) + } + } + + return oldToNew[node] +} +``` + +```kotlin +/** + * Definition for a Node. + * class Node(var `val`: Int) { + * var neighbors: ArrayList = ArrayList() + * } + */ + +class Solution { + fun cloneGraph(node: Node?): Node? { + if (node == null) return null + + val oldToNew = HashMap() + oldToNew[node] = Node(node.`val`) + val queue = ArrayDeque() + queue.add(node) + + while (queue.isNotEmpty()) { + val cur = queue.removeFirst() + + for (nei in cur.neighbors) { + nei?.let { neighbor -> + if (neighbor !in oldToNew) { + oldToNew[neighbor] = Node(neighbor.`val`) + queue.add(neighbor) + } + oldToNew[cur]?.neighbors?.add(oldToNew[neighbor]) + } + } + } + + return oldToNew[node] + } +} +``` + +```swift +/** + * Definition for a Node. + * public class Node { + * public var val: Int + * public var neighbors: [Node?] + * public init(_ val: Int) { + * self.val = val + * self.neighbors = [] + * } + * } + */ + +class Solution { + func cloneGraph(_ node: Node?) -> Node? { + if node == nil { + return nil + } + + var oldToNew: [Node: Node] = [:] + let newNode = Node(node!.val) + oldToNew[node!] = newNode + var queue = Deque() + queue.append(node!) + + while !queue.isEmpty { + let cur = queue.popFirst()! + for nei in cur.neighbors { + if let nei = nei { + if oldToNew[nei] == nil { + oldToNew[nei] = Node(nei.val) + queue.append(nei) + } + oldToNew[cur]!.neighbors.append(oldToNew[nei]!) + } + } + } + + return oldToNew[node!] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. \ No newline at end of file diff --git a/articles/coin-change-ii.md b/articles/coin-change-ii.md new file mode 100644 index 000000000..6e4cce788 --- /dev/null +++ b/articles/coin-change-ii.md @@ -0,0 +1,1047 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def change(self, amount: int, coins: List[int]) -> int: + coins.sort() + + def dfs(i, a): + if a == 0: + return 1 + if i >= len(coins): + return 0 + + res = 0 + if a >= coins[i]: + res = dfs(i + 1, a) + res += dfs(i, a - coins[i]) + return res + + return dfs(0, amount) +``` + +```java +public class Solution { + public int change(int amount, int[] coins) { + Arrays.sort(coins); + + return dfs(coins, 0, amount); + } + + private int dfs(int[] coins, int i, int a) { + if (a == 0) { + return 1; + } + if (i >= coins.length) { + return 0; + } + + int res = 0; + if (a >= coins[i]) { + res = dfs(coins, i + 1, a); + res += dfs(coins, i, a - coins[i]); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int change(int amount, vector& coins) { + sort(coins.begin(), coins.end()); + return dfs(coins, 0, amount); + } + +private: + int dfs(const vector& coins, int i, int a) { + if (a == 0) { + return 1; + } + if (i >= coins.size()) { + return 0; + } + + int res = 0; + if (a >= coins[i]) { + res = dfs(coins, i + 1, a); + res += dfs(coins, i, a - coins[i]); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} amount + * @param {number[]} coins + * @return {number} + */ + change(amount, coins) { + coins.sort((a, b) => a - b); + + const dfs = (i, a) => { + if (a === 0) return 1; + if (i >= coins.length) return 0; + + let res = 0; + if (a >= coins[i]) { + res = dfs(i + 1, a); + res += dfs(i, a - coins[i]); + } + return res; + }; + + return dfs(0, amount); + } +} +``` + +```csharp +public class Solution { + public int Change(int amount, int[] coins) { + Array.Sort(coins); + return Dfs(coins, 0, amount); + } + + private int Dfs(int[] coins, int i, int a) { + if (a == 0) { + return 1; + } + if (i >= coins.Length) { + return 0; + } + + int res = 0; + if (a >= coins[i]) { + res = Dfs(coins, i + 1, a); + res += Dfs(coins, i, a - coins[i]); + } + return res; + } +} +``` + +```go +func change(amount int, coins []int) int { + sort.Ints(coins) + + var dfs func(i, a int) int + dfs = func(i, a int) int { + if a == 0 { + return 1 + } + if i >= len(coins) { + return 0 + } + + res := 0 + if a >= coins[i] { + res = dfs(i+1, a) + res += dfs(i, a-coins[i]) + } + return res + } + + return dfs(0, amount) +} +``` + +```kotlin +class Solution { + fun change(amount: Int, coins: IntArray): Int { + coins.sort() + + fun dfs(i: Int, a: Int): Int { + if (a == 0) { + return 1 + } + if (i >= coins.size) { + return 0 + } + + var res = 0 + if (a >= coins[i]) { + res = dfs(i + 1, a) + res += dfs(i, a - coins[i]) + } + return res + } + + return dfs(0, amount) + } +} +``` + +```swift +class Solution { + func change(_ amount: Int, _ coins: [Int]) -> Int { + let coins = coins.sorted() + + func dfs(_ i: Int, _ a: Int) -> Int { + if a == 0 { + return 1 + } + if i >= coins.count { + return 0 + } + + var res = 0 + if a >= coins[i] { + res = dfs(i + 1, a) + res += dfs(i, a - coins[i]) + } + return res + } + + return dfs(0, amount) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ {max(n, \frac{a}{m})})$ +* Space complexity: $O(max(n, \frac{a}{m}))$ + +> Where $n$ is the number of coins, $a$ is the given amount and $m$ is the minimum value among all the coins. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def change(self, amount: int, coins: List[int]) -> int: + coins.sort() + memo = [[-1] * (amount + 1) for _ in range(len(coins) + 1)] + + def dfs(i, a): + if a == 0: + return 1 + if i >= len(coins): + return 0 + if memo[i][a] != -1: + return memo[i][a] + + res = 0 + if a >= coins[i]: + res = dfs(i + 1, a) + res += dfs(i, a - coins[i]) + + memo[i][a] = res + return res + + return dfs(0, amount) +``` + +```java +public class Solution { + public int change(int amount, int[] coins) { + Arrays.sort(coins); + int[][] memo = new int[coins.length + 1][amount + 1]; + for (int[] row : memo) { + Arrays.fill(row, -1); + } + + return dfs(0, amount, coins, memo); + } + + private int dfs(int i, int a, int[] coins, int[][] memo) { + if (a == 0) return 1; + if (i >= coins.length) return 0; + if (memo[i][a] != -1) return memo[i][a]; + + int res = 0; + if (a >= coins[i]) { + res = dfs(i + 1, a, coins, memo); + res += dfs(i, a - coins[i], coins, memo); + } + memo[i][a] = res; + return res; + } +} +``` + +```cpp +class Solution { +public: + int change(int amount, vector& coins) { + sort(coins.begin(), coins.end()); + vector> memo(coins.size() + 1, + vector(amount + 1, -1)); + + return dfs(0, amount, coins, memo); + } + + int dfs(int i, int a, vector& coins, vector>& memo) { + if (a == 0) return 1; + if (i >= coins.size()) return 0; + if (memo[i][a] != -1) return memo[i][a]; + + int res = 0; + if (a >= coins[i]) { + res = dfs(i + 1, a, coins, memo); + res += dfs(i, a - coins[i], coins, memo); + } + memo[i][a] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} amount + * @param {number[]} coins + * @return {number} + */ + change(amount, coins) { + coins.sort((a, b) => a - b); + let memo = Array.from({ length: coins.length + 1 }, () => + Array(amount + 1).fill(-1)); + + const dfs = (i, a) => { + if (a === 0) return 1; + if (i >= coins.length) return 0; + if (memo[i][a] !== -1) return memo[i][a]; + + let res = 0; + if (a >= coins[i]) { + res = dfs(i + 1, a); + res += dfs(i, a - coins[i]); + } + memo[i][a] = res; + return res; + }; + + return dfs(0, amount); + } +} +``` + +```csharp +public class Solution { + public int Change(int amount, int[] coins) { + Array.Sort(coins); + int[,] memo = new int[coins.Length + 1, amount + 1]; + for (int i = 0; i <= coins.Length; i++) { + for (int j = 0; j <= amount; j++) { + memo[i, j] = -1; + } + } + + return Dfs(0, amount, coins, memo); + } + + private int Dfs(int i, int a, int[] coins, int[,] memo) { + if (a == 0) return 1; + if (i >= coins.Length) return 0; + if (memo[i, a] != -1) return memo[i, a]; + + int res = 0; + if (a >= coins[i]) { + res = Dfs(i + 1, a, coins, memo); + res += Dfs(i, a - coins[i], coins, memo); + } + memo[i, a] = res; + return res; + } +} +``` + +```go +func change(amount int, coins []int) int { + sort.Ints(coins) + memo := make([][]int, len(coins) + 1) + for i := range memo { + memo[i] = make([]int, amount + 1) + for j := range memo[i] { + memo[i][j] = -1 + } + } + + var dfs func(i, a int) int + dfs = func(i, a int) int { + if a == 0 { + return 1 + } + if i >= len(coins) { + return 0 + } + if memo[i][a] != -1 { + return memo[i][a] + } + + res := 0 + if a >= coins[i] { + res = dfs(i+1, a) + res += dfs(i, a-coins[i]) + } + memo[i][a] = res + return res + } + + return dfs(0, amount) +} +``` + +```kotlin +class Solution { + fun change(amount: Int, coins: IntArray): Int { + coins.sort() + val memo = Array(coins.size + 1) { IntArray(amount + 1) { -1 } } + + fun dfs(i: Int, a: Int): Int { + if (a == 0) { + return 1 + } + if (i >= coins.size) { + return 0 + } + if (memo[i][a] != -1) return memo[i][a] + + var res = 0 + if (a >= coins[i]) { + res = dfs(i + 1, a) + res += dfs(i, a - coins[i]) + } + memo[i][a] = res + return res + } + + return dfs(0, amount) + } +} +``` + +```swift +class Solution { + func change(_ amount: Int, _ coins: [Int]) -> Int { + let coins = coins.sorted() + var memo = Array(repeating: Array(repeating: -1, count: amount + 1), count: coins.count + 1) + + func dfs(_ i: Int, _ a: Int) -> Int { + if a == 0 { + return 1 + } + if i >= coins.count { + return 0 + } + if memo[i][a] != -1 { + return memo[i][a] + } + + var res = 0 + if a >= coins[i] { + res = dfs(i + 1, a) + res += dfs(i, a - coins[i]) + } + + memo[i][a] = res + return res + } + + return dfs(0, amount) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * a)$ +* Space complexity: $O(n * a)$ + +> Where $n$ is the number of coins and $a$ is the given amount. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def change(self, amount: int, coins: List[int]) -> int: + n = len(coins) + coins.sort() + dp = [[0] * (amount + 1) for _ in range(n + 1)] + + for i in range(n + 1): + dp[i][0] = 1 + + for i in range(n - 1, -1, -1): + for a in range(amount + 1): + if a >= coins[i]: + dp[i][a] = dp[i + 1][a] + dp[i][a] += dp[i][a - coins[i]] + + return dp[0][amount] +``` + +```java +public class Solution { + public int change(int amount, int[] coins) { + int n = coins.length; + Arrays.sort(coins); + int[][] dp = new int[n + 1][amount + 1]; + + for (int i = 0; i <= n; i++) { + dp[i][0] = 1; + } + + for (int i = n - 1; i >= 0; i--) { + for (int a = 0; a <= amount; a++) { + if (a >= coins[i]) { + dp[i][a] = dp[i + 1][a]; + dp[i][a] += dp[i][a - coins[i]]; + } + } + } + + return dp[0][amount]; + } +} +``` + +```cpp +class Solution { +public: + int change(int amount, vector& coins) { + int n = coins.size(); + sort(coins.begin(), coins.end()); + vector> dp(n + 1, vector(amount + 1, 0)); + + for (int i = 0; i <= n; i++) { + dp[i][0] = 1; + } + + for (int i = n - 1; i >= 0; i--) { + for (int a = 0; a <= amount; a++) { + if (a >= coins[i]) { + dp[i][a] = dp[i + 1][a]; + dp[i][a] += dp[i][a - coins[i]]; + } + } + } + + return dp[0][amount]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} amount + * @param {number[]} coins + * @return {number} + */ + change(amount, coins) { + coins.sort((a, b) => a - b); + const n = coins.length; + const dp = Array.from({ length: n + 1 }, () => Array(amount + 1).fill(0)); + + for (let i = 0; i <= n; i++) { + dp[i][0] = 1; + } + + for (let i = n - 1; i >= 0; i--) { + for (let a = 0; a <= amount; a++) { + if (a >= coins[i]) { + dp[i][a] = dp[i + 1][a]; + dp[i][a] += dp[i][a - coins[i]]; + } + } + } + + return dp[0][amount]; + } +} +``` + +```csharp +public class Solution { + public int Change(int amount, int[] coins) { + int n = coins.Length; + Array.Sort(coins); + int[,] dp = new int[n + 1, amount + 1]; + + for (int i = 0; i <= n; i++) { + dp[i, 0] = 1; + } + + for (int i = n - 1; i >= 0; i--) { + for (int a = 0; a <= amount; a++) { + if (a >= coins[i]) { + dp[i, a] = dp[i + 1, a]; + dp[i, a] += dp[i, a - coins[i]]; + } + } + } + + return dp[0, amount]; + } +} +``` + +```go +func change(amount int, coins []int) int { + n := len(coins) + dp := make([][]int, n+1) + for i := range dp { + dp[i] = make([]int, amount+1) + } + + for i := 0; i <= n; i++ { + dp[i][0] = 1 + } + + for i := n - 1; i >= 0; i-- { + for a := 0; a <= amount; a++ { + if a >= coins[i] { + dp[i][a] = dp[i+1][a] + dp[i][a] += dp[i][a-coins[i]] + } else { + dp[i][a] = dp[i+1][a] + } + } + } + + return dp[0][amount] +} +``` + +```kotlin +class Solution { + fun change(amount: Int, coins: IntArray): Int { + val n = coins.size + val dp = Array(n + 1) { IntArray(amount + 1) } + + for (i in 0..n) { + dp[i][0] = 1 + } + + for (i in n - 1 downTo 0) { + for (a in 0..amount) { + if (a >= coins[i]) { + dp[i][a] = dp[i + 1][a] + dp[i][a] += dp[i][a - coins[i]] + } else { + dp[i][a] = dp[i + 1][a] + } + } + } + + return dp[0][amount] + } +} +``` + +```swift +class Solution { + func change(_ amount: Int, _ coins: [Int]) -> Int { + let n = coins.count + let sortedCoins = coins.sorted() + var dp = Array( + repeating: Array(repeating: 0, count: amount + 1), + count: n + 1 + ) + + for i in 0...n { + dp[i][0] = 1 + } + + for i in stride(from: n - 1, through: 0, by: -1) { + for a in 0...amount { + let base = dp[i + 1][a] + if a >= sortedCoins[i] { + let addend = dp[i][a - sortedCoins[i]] + if base > Int.max - addend { + dp[i][a] = 0 + } else { + dp[i][a] = base + addend + } + } else { + dp[i][a] = base + } + } + } + + return dp[0][amount] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * a)$ +* Space complexity: $O(n * a)$ + +> Where $n$ is the number of coins and $a$ is the given amount. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def change(self, amount: int, coins: List[int]) -> int: + dp = [0] * (amount + 1) + dp[0] = 1 + for i in range(len(coins) - 1, -1, -1): + nextDP = [0] * (amount + 1) + nextDP[0] = 1 + + for a in range(1, amount + 1): + nextDP[a] = dp[a] + if a - coins[i] >= 0: + nextDP[a] += nextDP[a - coins[i]] + dp = nextDP + return dp[amount] +``` + +```java +public class Solution { + public int change(int amount, int[] coins) { + int[] dp = new int[amount + 1]; + dp[0] = 1; + for (int i = coins.length - 1; i >= 0; i--) { + int[] nextDP = new int[amount + 1]; + nextDP[0] = 1; + + for (int a = 1; a <= amount; a++) { + nextDP[a] = dp[a]; + if (a - coins[i] >= 0) { + nextDP[a] += nextDP[a - coins[i]]; + } + } + dp = nextDP; + } + return dp[amount]; + } +} +``` + +```cpp +class Solution { +public: + int change(int amount, vector& coins) { + vector dp(amount + 1, 0); + dp[0] = 1; + for (int i = coins.size() - 1; i >= 0; i--) { + vector nextDP(amount + 1, 0); + nextDP[0] = 1; + + for (int a = 1; a <= amount; a++) { + nextDP[a] = dp[a]; + if (a - coins[i] >= 0) { + nextDP[a] += nextDP[a - coins[i]]; + } + } + dp = nextDP; + } + return dp[amount]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} amount + * @param {number[]} coins + * @return {number} + */ + change(amount, coins) { + let dp = new Array(amount + 1).fill(0); + dp[0] = 1; + for (let i = coins.length - 1; i >= 0; i--) { + const nextDP = new Array(amount + 1).fill(0); + nextDP[0] = 1; + + for (let a = 1; a <= amount; a++) { + nextDP[a] = dp[a]; + if (a - coins[i] >= 0) { + nextDP[a] += nextDP[a - coins[i]]; + } + } + dp = nextDP; + } + return dp[amount]; + } +} +``` + +```csharp +public class Solution { + public int Change(int amount, int[] coins) { + int[] dp = new int[amount + 1]; + dp[0] = 1; + for (int i = coins.Length - 1; i >= 0; i--) { + int[] nextDP = new int[amount + 1]; + nextDP[0] = 1; + + for (int a = 1; a <= amount; a++) { + nextDP[a] = dp[a]; + if (a - coins[i] >= 0) { + nextDP[a] += nextDP[a - coins[i]]; + } + } + dp = nextDP; + } + return dp[amount]; + } +} +``` + +```go +func change(amount int, coins []int) int { + dp := make([]int, amount+1) + dp[0] = 1 + + for i := len(coins) - 1; i >= 0; i-- { + nextDP := make([]int, amount+1) + nextDP[0] = 1 + + for a := 1; a <= amount; a++ { + nextDP[a] = dp[a] + if a-coins[i] >= 0 { + nextDP[a] += nextDP[a-coins[i]] + } + } + dp = nextDP + } + + return dp[amount] +} +``` + +```kotlin +class Solution { + fun change(amount: Int, coins: IntArray): Int { + val dp = IntArray(amount + 1) + dp[0] = 1 + + for (i in coins.size - 1 downTo 0) { + val nextDP = IntArray(amount + 1) + nextDP[0] = 1 + + for (a in 1..amount) { + nextDP[a] = dp[a] + if (a - coins[i] >= 0) { + nextDP[a] += nextDP[a - coins[i]] + } + } + System.arraycopy(nextDP, 0, dp, 0, amount + 1) + } + + return dp[amount] + } +} +``` + +```swift +class Solution { + func change(_ amount: Int, _ coins: [Int]) -> Int { + var dp = [Int](repeating: 0, count: amount + 1) + dp[0] = 1 + + for i in stride(from: coins.count - 1, through: 0, by: -1) { + var nextDP = [Int](repeating: 0, count: amount + 1) + nextDP[0] = 1 + + for a in 1..<(amount + 1) { + nextDP[a] = dp[a] + if a - coins[i] >= 0 { + let addend = nextDP[a - coins[i]] + if nextDP[a] > Int.max - addend { + nextDP[a] = 0 + } else { + nextDP[a] += addend + } + } + } + + dp = nextDP + } + + return dp[amount] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * a)$ +* Space complexity: $O(a)$ + +> Where $n$ is the number of coins and $a$ is the given amount. + +--- + +## 5. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def change(self, amount: int, coins: List[int]) -> int: + dp = [0] * (amount + 1) + dp[0] = 1 + for i in range(len(coins) - 1, -1, -1): + for a in range(1, amount + 1): + dp[a] += dp[a - coins[i]] if coins[i] <= a else 0 + return dp[amount] +``` + +```java +public class Solution { + public int change(int amount, int[] coins) { + int[] dp = new int[amount + 1]; + dp[0] = 1; + for (int i = coins.length - 1; i >= 0; i--) + for (int a = 1; a <= amount; a++) + dp[a] = dp[a] + (coins[i] <= a ? dp[a - coins[i]] : 0); + return dp[amount]; + } +} +``` + +```cpp +class Solution { +public: + int change(int amount, vector& coins) { + vector dp(amount + 1, 0); + dp[0] = 1; + for (int i = coins.size() - 1; i >= 0; i--) { + for (int a = 1; a <= amount; a++) { + dp[a] = dp[a] + (coins[i] <= a ? dp[a - coins[i]] : 0); + } + } + return dp[amount]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} amount + * @param {number[]} coins + * @return {number} + */ + change(amount, coins) { + const dp = new Array(amount + 1).fill(0); + dp[0] = 1; + for (let i = coins.length - 1; i >= 0; i--) { + for (let a = 1; a <= amount; a++) { + dp[a] += (coins[i] <= a ? dp[a - coins[i]] : 0); + } + } + return dp[amount]; + } +} +``` + +```csharp +public class Solution { + public int Change(int amount, int[] coins) { + int[] dp = new int[amount + 1]; + dp[0] = 1; + for (int i = coins.Length - 1; i >= 0; i--) { + for (int a = 1; a <= amount; a++) { + dp[a] += (coins[i] <= a ? dp[a - coins[i]] : 0); + } + } + return dp[amount]; + } +} +``` + +```go +func change(amount int, coins []int) int { + dp := make([]int, amount+1) + dp[0] = 1 + + for i := len(coins) - 1; i >= 0; i-- { + for a := 1; a <= amount; a++ { + if a-coins[i] >= 0 { + dp[a] += dp[a - coins[i]] + } + } + } + + return dp[amount] +} +``` + +```kotlin +class Solution { + fun change(amount: Int, coins: IntArray): Int { + val dp = IntArray(amount + 1) + dp[0] = 1 + + for (i in coins.size - 1 downTo 0) { + for (a in 1..amount) { + if (a - coins[i] >= 0) { + dp[a] += dp[a - coins[i]] + } + } + } + + return dp[amount] + } +} +``` + +```swift +class Solution { + func change(_ amount: Int, _ coins: [Int]) -> Int { + var dp = [Int](repeating: 0, count: amount + 1) + dp[0] = 1 + + for i in stride(from: coins.count - 1, through: 0, by: -1) { + for a in 1..<(amount + 1) { + if coins[i] <= a { + let addend = dp[a - coins[i]] + if dp[a] > Int.max - addend { + dp[a] = 0 + } else { + dp[a] += addend + } + } + } + } + + return dp[amount] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * a)$ +* Space complexity: $O(a)$ + +> Where $n$ is the number of coins and $a$ is the given amount. \ No newline at end of file diff --git a/articles/coin-change.md b/articles/coin-change.md new file mode 100644 index 000000000..f61f99922 --- /dev/null +++ b/articles/coin-change.md @@ -0,0 +1,893 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def coinChange(self, coins: List[int], amount: int) -> int: + + def dfs(amount): + if amount == 0: + return 0 + + res = 1e9 + for coin in coins: + if amount - coin >= 0: + res = min(res, 1 + dfs(amount - coin)) + return res + + minCoins = dfs(amount) + return -1 if minCoins >= 1e9 else minCoins +``` + +```java +public class Solution { + public int dfs(int[] coins, int amount) { + if (amount == 0) return 0; + + int res = (int) 1e9; + for (int coin : coins) { + if (amount - coin >= 0) { + res = Math.min(res, + 1 + dfs(coins, amount - coin)); + } + } + return res; + } + + public int coinChange(int[] coins, int amount) { + int minCoins = dfs(coins, amount); + return (minCoins >= 1e9) ? -1 : minCoins; + } +} +``` + +```cpp +class Solution { +public: + int dfs(vector& coins, int amount) { + if (amount == 0) return 0; + + int res = 1e9; + for (int coin : coins) { + if (amount - coin >= 0) { + res = min(res, + 1 + dfs(coins, amount - coin)); + } + } + return res; + } + + int coinChange(vector& coins, int amount) { + int minCoins = dfs(coins, amount); + return (minCoins >= 1e9) ? -1 : minCoins; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} coins + * @param {number} amount + * @return {number} + */ + coinChange(coins, amount) { + + const dfs = (amount) => { + if (amount === 0) return 0; + + let res = Infinity; + for (let coin of coins) { + if (amount - coin >= 0) { + res = Math.min(res, + 1 + dfs(amount - coin)); + } + } + return res; + } + + const minCoins = dfs(amount); + return minCoins === Infinity ? -1 : minCoins; + } +} +``` + +```csharp +public class Solution { + public int Dfs(int[] coins, int amount) { + if (amount == 0) return 0; + + int res = (int)1e9; + foreach (var coin in coins) { + if (amount - coin >= 0) { + res = Math.Min(res, + 1 + Dfs(coins, amount - coin)); + } + } + return res; + } + + public int CoinChange(int[] coins, int amount) { + int minCoins = Dfs(coins, amount); + return (minCoins >= 1e9) ? -1 : minCoins; + } +} +``` + +```go +func coinChange(coins []int, amount int) int { + var dfs func(int) int + dfs = func(amt int) int { + if amt == 0 { + return 0 + } + + res := math.MaxInt32 + for _, coin := range coins { + if amt - coin >= 0 { + res = min(res, 1 + dfs(amt - coin)) + } + } + + return res + } + + minCoins := dfs(amount) + if minCoins >= math.MaxInt32 { + return -1 + } + return minCoins +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun coinChange(coins: IntArray, amount: Int): Int { + val INF = 1000000000 + fun dfs(amt: Int): Int { + if (amt == 0) return 0 + + var res = INF + for (coin in coins) { + if (amt - coin >= 0) { + res = minOf(res, 1 + dfs(amt - coin)) + } + } + return res + } + + val minCoins = dfs(amount) + return if (minCoins >= INF) -1 else minCoins + } +} +``` + +```swift +class Solution { + func coinChange(_ coins: [Int], _ amount: Int) -> Int { + func dfs(_ amount: Int) -> Int { + if amount == 0 { + return 0 + } + + var res = Int(1e9) + for coin in coins { + if amount - coin >= 0 { + res = min(res, 1 + dfs(amount - coin)) + } + } + return res + } + + let minCoins = dfs(amount) + return minCoins >= Int(1e9) ? -1 : minCoins + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ t)$ +* Space complexity: $O(t)$ + +> Where $n$ is the length of the array $coins$ and $t$ is the given $amount$. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def coinChange(self, coins: List[int], amount: int) -> int: + memo = {} + + def dfs(amount): + if amount == 0: + return 0 + if amount in memo: + return memo[amount] + + res = 1e9 + for coin in coins: + if amount - coin >= 0: + res = min(res, 1 + dfs(amount - coin)) + + memo[amount] = res + return res + + minCoins = dfs(amount) + return -1 if minCoins >= 1e9 else minCoins +``` + +```java +public class Solution { + HashMap memo = new HashMap<>(); + + public int dfs(int amount, int[] coins) { + if (amount == 0) return 0; + if (memo.containsKey(amount)) + return memo.get(amount); + + int res = Integer.MAX_VALUE; + for (int coin : coins) { + if (amount - coin >= 0) { + int result = dfs(amount - coin, coins); + if (result != Integer.MAX_VALUE) { + res = Math.min(res, 1 + result); + } + } + } + + memo.put(amount, res); + return res; + } + + public int coinChange(int[] coins, int amount) { + int minCoins = dfs(amount, coins); + return minCoins == Integer.MAX_VALUE ? -1 : minCoins; + } +} +``` + +```cpp +class Solution { +public: + unordered_map memo; + int dfs(int amount, vector& coins) { + if (amount == 0) return 0; + if (memo.find(amount) != memo.end()) + return memo[amount]; + + int res = INT_MAX; + for (int coin : coins) { + if (amount - coin >= 0) { + int result = dfs(amount - coin, coins); + if (result != INT_MAX) { + res = min(res, 1 + result); + } + } + } + + memo[amount] = res; + return res; + } + + int coinChange(vector& coins, int amount) { + int minCoins = dfs(amount, coins); + return minCoins == INT_MAX ? -1 : minCoins; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} coins + * @param {number} amount + * @return {number} + */ + coinChange(coins, amount) { + let memo = {}; + + const dfs = (amount) => { + if (amount === 0) return 0; + if (memo[amount] !== undefined) + return memo[amount]; + + let res = Infinity; + for (let coin of coins) { + if (amount - coin >= 0) { + res = Math.min(res, 1 + dfs(amount - coin)); + } + } + + memo[amount] = res; + return res; + } + + const minCoins = dfs(amount); + return minCoins === Infinity ? -1 : minCoins; + } +} +``` + +```csharp +public class Solution { + private Dictionary memo = new Dictionary(); + + private int Dfs(int amount, int[] coins) { + if (amount == 0) return 0; + if (memo.ContainsKey(amount)) + return memo[amount]; + + int res = int.MaxValue; + foreach (int coin in coins) { + if (amount - coin >= 0) { + int result = Dfs(amount - coin, coins); + if (result != int.MaxValue) { + res = Math.Min(res, 1 + result); + } + } + } + + memo[amount] = res; + return res; + } + + public int CoinChange(int[] coins, int amount) { + int minCoins = Dfs(amount, coins); + return minCoins == int.MaxValue ? -1 : minCoins; + } +} +``` + +```go +func coinChange(coins []int, amount int) int { + var memo = make(map[int]int) + memo[0] = 0 + + var dfs func(int) int + dfs = func(amt int) int { + if val, ok := memo[amt]; ok { + return val + } + + res := math.MaxInt32 + for _, coin := range coins { + if amt - coin >= 0 { + res = min(res, 1 + dfs(amt - coin)) + } + } + memo[amt] = res + return res + } + + minCoins := dfs(amount) + if minCoins >= math.MaxInt32 { + return -1 + } + return minCoins +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun coinChange(coins: IntArray, amount: Int): Int { + val INF = 1000000000 + var memo = hashMapOf(0 to 0) + + fun dfs(amt: Int): Int { + if (amt in memo) { + return memo[amt]!! + } + + var res = INF + for (coin in coins) { + if (amt - coin >= 0) { + res = minOf(res, 1 + dfs(amt - coin)) + } + } + memo[amt] = res + return res + } + + val minCoins = dfs(amount) + return if (minCoins >= INF) -1 else minCoins + } +} +``` + +```swift +class Solution { + func coinChange(_ coins: [Int], _ amount: Int) -> Int { + var memo = [Int: Int]() + + func dfs(_ amount: Int) -> Int { + if amount == 0 { + return 0 + } + if let cached = memo[amount] { + return cached + } + + var res = Int(1e9) + for coin in coins { + if amount - coin >= 0 { + res = min(res, 1 + dfs(amount - coin)) + } + } + + memo[amount] = res + return res + } + + let minCoins = dfs(amount) + return minCoins >= Int(1e9) ? -1 : minCoins + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * t)$ +* Space complexity: $O(t)$ + +> Where $n$ is the length of the array $coins$ and $t$ is the given $amount$. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def coinChange(self, coins: List[int], amount: int) -> int: + dp = [amount + 1] * (amount + 1) + dp[0] = 0 + + for a in range(1, amount + 1): + for c in coins: + if a - c >= 0: + dp[a] = min(dp[a], 1 + dp[a - c]) + return dp[amount] if dp[amount] != amount + 1 else -1 +``` + +```java +public class Solution { + public int coinChange(int[] coins, int amount) { + int[] dp = new int[amount + 1]; + Arrays.fill(dp, amount + 1); + dp[0] = 0; + for (int i = 1; i <= amount; i++) { + for (int j = 0; j < coins.length; j++) { + if (coins[j] <= i) { + dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1); + } + } + } + return dp[amount] > amount ? -1 : dp[amount]; + } +} +``` + +```cpp +class Solution { +public: + int coinChange(vector& coins, int amount) { + vector dp(amount + 1, amount + 1); + dp[0] = 0; + for (int i = 1; i <= amount; i++) { + for (int j = 0; j < coins.size(); j++) { + if (coins[j] <= i) { + dp[i] = min(dp[i], dp[i - coins[j]] + 1); + } + } + } + return dp[amount] > amount ? -1 : dp[amount]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} coins + * @param {number} amount + * @return {number} + */ + coinChange(coins, amount) { + const dp = new Array(amount + 1).fill(amount + 1); + dp[0] = 0; + for (let i = 1; i <= amount; i++) { + for (let j = 0; j < coins.length; j++) { + if (coins[j] <= i) { + dp[i] = Math.min(dp[i], 1 + dp[i - coins[j]]); + } + } + } + return dp[amount] > amount ? -1 : dp[amount]; + } +} +``` + +```csharp +public class Solution { + public int CoinChange(int[] coins, int amount) { + int[] dp = new int[amount + 1]; + Array.Fill(dp, amount + 1); + dp[0] = 0; + for (int i = 1; i <= amount; i++) { + foreach (int coin in coins) { + if (coin <= i) { + dp[i] = Math.Min(dp[i], dp[i - coin] + 1); + } + } + } + return dp[amount] > amount ? -1 : dp[amount]; + } +} +``` + +```go +func coinChange(coins []int, amount int) int { + dp := make([]int, amount+1) + for i := range dp { + dp[i] = amount + 1 + } + dp[0] = 0 + for a := 1; a <= amount; a++ { + for _, c := range coins { + if a-c >= 0 { + dp[a] = min(dp[a], 1+dp[a-c]) + } + } + } + if dp[amount] > amount { + return -1 + } + return dp[amount] +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun coinChange(coins: IntArray, amount: Int): Int { + val dp = IntArray(amount + 1) { amount + 1 } + dp[0] = 0 + for (a in 1..amount) { + for (c in coins) { + if (a - c >= 0) { + dp[a] = minOf(dp[a], 1 + dp[a - c]) + } + } + } + return if (dp[amount] > amount) -1 else dp[amount] + } +} +``` + +```swift +class Solution { + func coinChange(_ coins: [Int], _ amount: Int) -> Int { + if amount == 0 { + return 0 + } + + var dp = [Int](repeating: amount + 1, count: amount + 1) + dp[0] = 0 + + for a in 1...amount { + for coin in coins { + if a - coin >= 0 { + dp[a] = min(dp[a], 1 + dp[a - coin]) + } + } + } + + return dp[amount] == amount + 1 ? -1 : dp[amount] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * t)$ +* Space complexity: $O(t)$ + +> Where $n$ is the length of the array $coins$ and $t$ is the given $amount$. + +--- + +## 4. Breadth First Search + +::tabs-start + +```python +class Solution: + def coinChange(self, coins: List[int], amount: int) -> int: + if amount == 0: + return 0 + + q = deque([0]) + seen = [False] * (amount + 1) + seen[0] = True + res = 0 + + while q: + res += 1 + for _ in range(len(q)): + cur = q.popleft() + for coin in coins: + nxt = cur + coin + if nxt == amount: + return res + if nxt > amount or seen[nxt]: + continue + seen[nxt] = True + q.append(nxt) + + return -1 +``` + +```java +public class Solution { + public int coinChange(int[] coins, int amount) { + if (amount == 0) return 0; + + Queue q = new LinkedList<>(); + q.add(0); + boolean[] seen = new boolean[amount + 1]; + seen[0] = true; + int res = 0; + + while (!q.isEmpty()) { + res++; + int size = q.size(); + for (int i = 0; i < size; i++) { + int cur = q.poll(); + for (int coin : coins) { + int nxt = cur + coin; + if (nxt == amount) return res; + if (nxt > amount || seen[nxt]) continue; + seen[nxt] = true; + q.add(nxt); + } + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int coinChange(vector& coins, int amount) { + if (amount == 0) return 0; + + queue q; + q.push(0); + vector seen(amount + 1, false); + seen[0] = true; + int res = 0; + + while (!q.empty()) { + res++; + int size = q.size(); + for (int i = 0; i < size; i++) { + int cur = q.front(); + q.pop(); + for (int coin : coins) { + int nxt = cur + coin; + if (nxt == amount) return res; + if (nxt > amount || seen[nxt]) continue; + seen[nxt] = true; + q.push(nxt); + } + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} coins + * @param {number} amount + * @return {number} + */ + coinChange(coins, amount) { + if (amount === 0) return 0; + + const q = new Queue([0]); + const seen = new Array(amount + 1).fill(false); + seen[0] = true; + let res = 0; + + while (!q.isEmpty()) { + res++; + const size = q.size(); + for (let i = 0; i < size; i++) { + const cur = q.pop(); + for (const coin of coins) { + const nxt = cur + coin; + if (nxt === amount) return res; + if (nxt > amount || seen[nxt]) continue; + seen[nxt] = true; + q.push(nxt); + } + } + } + + return -1; + } +} +``` + +```csharp +public class Solution { + public int CoinChange(int[] coins, int amount) { + if (amount == 0) return 0; + + Queue q = new Queue(); + q.Enqueue(0); + bool[] seen = new bool[amount + 1]; + seen[0] = true; + int res = 0; + + while (q.Count > 0) { + res++; + int size = q.Count; + for (int i = 0; i < size; i++) { + int cur = q.Dequeue(); + foreach (int coin in coins) { + int nxt = cur + coin; + if (nxt == amount) return res; + if (nxt > amount || seen[nxt]) continue; + seen[nxt] = true; + q.Enqueue(nxt); + } + } + } + + return -1; + } +} +``` + +```go +func coinChange(coins []int, amount int) int { + if amount == 0 { + return 0 + } + queue := []int{0} + seen := make([]bool, amount+1) + seen[0] = true + res := 0 + for len(queue) > 0 { + res++ + size := len(queue) + for i := 0; i < size; i++ { + cur := queue[0] + queue = queue[1:] + for _, coin := range coins { + next := cur + coin + if next == amount { + return res + } + if next > amount || seen[next] { + continue + } + seen[next] = true + queue = append(queue, next) + } + } + } + return -1 +} +``` + +```kotlin +class Solution { + fun coinChange(coins: IntArray, amount: Int): Int { + if (amount == 0) return 0 + val queue = ArrayDeque().apply { add(0) } + val seen = BooleanArray(amount + 1) { false } + seen[0] = true + var res = 0 + while (queue.isNotEmpty()) { + res++ + repeat(queue.size) { + val cur = queue.removeFirst() + for (coin in coins) { + val next = cur + coin + if (next == amount) return res + if (next > amount || seen[next]) continue + seen[next] = true + queue.addLast(next) + } + } + } + return -1 + } +} +``` + +```swift +class Solution { + func coinChange(_ coins: [Int], _ amount: Int) -> Int { + if amount == 0 { + return 0 + } + + var q = Deque([0]) + var seen = Array(repeating: false, count: amount + 1) + seen[0] = true + var res = 0 + + while !q.isEmpty { + res += 1 + for _ in 0.. amount || seen[nxt] { + continue + } + seen[nxt] = true + q.append(nxt) + } + } + } + + return -1 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * t)$ +* Space complexity: $O(t)$ + +> Where $n$ is the length of the array $coins$ and $t$ is the given $amount$. \ No newline at end of file diff --git a/articles/combination-sum-iv.md b/articles/combination-sum-iv.md new file mode 100644 index 000000000..b2d2c5386 --- /dev/null +++ b/articles/combination-sum-iv.md @@ -0,0 +1,512 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def combinationSum4(self, nums: List[int], target: int) -> int: + nums.sort() + + def dfs(total): + if total == 0: + return 1 + + res = 0 + for i in range(len(nums)): + if total < nums[i]: + break + res += dfs(total - nums[i]) + return res + + return dfs(target) +``` + +```java +public class Solution { + public int combinationSum4(int[] nums, int target) { + Arrays.sort(nums); + + return dfs(nums, target); + } + + private int dfs(int[] nums, int total) { + if (total == 0) { + return 1; + } + + int res = 0; + for (int num : nums) { + if (total < num) { + break; + } + res += dfs(nums, total - num); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int combinationSum4(vector& nums, int target) { + sort(nums.begin(), nums.end()); + return dfs(nums, target); + } + + int dfs(vector& nums, int total) { + if (total == 0) { + return 1; + } + + int res = 0; + for (int num : nums) { + if (total < num) { + break; + } + res += dfs(nums, total - num); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + combinationSum4(nums, target) { + nums.sort((a, b) => a - b); + + const dfs = (total) => { + if (total === 0) return 1; + + let res = 0; + for (let num of nums) { + if (total < num) break; + res += dfs(total - num); + } + return res; + }; + + return dfs(target); + } +} +``` + +```csharp +public class Solution { + public int CombinationSum4(int[] nums, int target) { + Array.Sort(nums); + return Dfs(nums, target); + } + + private int Dfs(int[] nums, int total) { + if (total == 0) { + return 1; + } + + int res = 0; + foreach (int num in nums) { + if (total < num) { + break; + } + res += Dfs(nums, total - num); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ t)$ +* Space complexity: $O(t)$ + +> Where $n$ is the size of the array $nums$ and $t$ is the given target. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def combinationSum4(self, nums: List[int], target: int) -> int: + nums.sort() + memo = { 0 : 1 } + + def dfs(total): + if total in memo: + return memo[total] + + res = 0 + for num in nums: + if total < num: + break + res += dfs(total - num) + memo[total] = res + return res + + return dfs(target) +``` + +```java +public class Solution { + private Map memo; + + public int combinationSum4(int[] nums, int target) { + Arrays.sort(nums); + memo = new HashMap<>(); + memo.put(0, 1); + return dfs(nums, target); + } + + private int dfs(int[] nums, int total) { + if (memo.containsKey(total)) { + return memo.get(total); + } + + int res = 0; + for (int num : nums) { + if (total < num) { + break; + } + res += dfs(nums, total - num); + } + memo.put(total, res); + return res; + } +} +``` + +```cpp +class Solution { +private: + unordered_map memo; + +public: + int combinationSum4(vector& nums, int target) { + sort(nums.begin(), nums.end()); + memo[0] = 1; + return dfs(nums, target); + } + + int dfs(vector& nums, int total) { + if (memo.count(total)) { + return memo[total]; + } + + int res = 0; + for (int num : nums) { + if (total < num) { + break; + } + res += dfs(nums, total - num); + } + memo[total] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + combinationSum4(nums, target) { + nums.sort((a, b) => a - b); + const memo = { 0 : 1 }; + + const dfs = (total) => { + if (memo[total] !== undefined) { + return memo[total]; + } + + let res = 0; + for (let num of nums) { + if (total < num) break; + res += dfs(total - num); + } + memo[total] = res; + return res; + }; + + return dfs(target); + } +} +``` + +```csharp +public class Solution { + private Dictionary memo; + + public int CombinationSum4(int[] nums, int target) { + Array.Sort(nums); + memo = new Dictionary(); + memo[0] = 1; + return Dfs(nums, target); + } + + private int Dfs(int[] nums, int total) { + if (memo.ContainsKey(total)) { + return memo[total]; + } + + int res = 0; + foreach (int num in nums) { + if (total < num) { + break; + } + res += Dfs(nums, total - num); + } + + memo[total] = res; + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * t)$ +* Space complexity: $O(t)$ + +> Where $n$ is the size of the array $nums$ and $t$ is the given target. + +--- + +## 3. Dynamic Programming (Bottom-Up) - I + +::tabs-start + +```python +class Solution: + def combinationSum4(self, nums: List[int], target: int) -> int: + dp = { 0 : 1 } + + for total in range(1, target + 1): + dp[total] = 0 + for num in nums: + dp[total] += dp.get(total - num, 0) + + return dp[target] +``` + +```java +public class Solution { + public int combinationSum4(int[] nums, int target) { + Map dp = new HashMap<>(); + dp.put(0, 1); + + for (int total = 1; total <= target; total++) { + dp.put(total, 0); + for (int num : nums) { + dp.put(total, dp.get(total) + dp.getOrDefault(total - num, 0)); + } + } + return dp.get(target); + } +} +``` + +```cpp +class Solution { +public: + int combinationSum4(vector& nums, int target) { + unordered_map dp; + dp[0] = 1; + + for (int total = 1; total <= target; total++) { + dp[total] = 0; + for (int num : nums) { + if (total >= num) { + dp[total] += dp[total - num]; + } + } + if (dp[total] > INT_MAX) { + dp[total] = 0; + } + } + return dp[target]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + combinationSum4(nums, target) { + let dp = {0: 1}; + for (let total = 1; total <= target; total++) { + dp[total] = 0; + for (let num of nums) { + dp[total] += dp[total - num] || 0; + } + } + return dp[target]; + } +} +``` + +```csharp +public class Solution { + public int CombinationSum4(int[] nums, int target) { + Dictionary dp = new Dictionary(); + dp[0] = 1; + + for (int total = 1; total <= target; total++) { + dp[total] = 0; + foreach (int num in nums) { + if (dp.ContainsKey(total - num)) { + dp[total] += dp[total - num]; + } + } + } + + return dp[target]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * t)$ +* Space complexity: $O(t)$ + +> Where $n$ is the size of the array $nums$ and $t$ is the given target. + +--- + +## 4. Dynamic Programming (Bottom-Up) - II + +::tabs-start + +```python +class Solution: + def combinationSum4(self, nums: List[int], target: int) -> int: + nums.sort() + dp = defaultdict(int) + dp[target] = 1 + for total in range(target, 0, -1): + for num in nums: + if total < num: + break + dp[total - num] += dp[total] + return dp[0] +``` + +```java +public class Solution { + public int combinationSum4(int[] nums, int target) { + Arrays.sort(nums); + Map dp = new HashMap<>(); + dp.put(target, 1); + for (int total = target; total > 0; total--) { + for (int num : nums) { + if (total < num) break; + dp.put(total - num, dp.getOrDefault(total, 0) + dp.getOrDefault(total - num, 0)); + } + } + return dp.getOrDefault(0, 0); + } +} +``` + +```cpp +class Solution { +public: + int combinationSum4(vector& nums, int target) { + sort(nums.begin(), nums.end()); + unordered_map dp; + dp[target] = 1; + + for (int total = target; total > 0; total--) { + if (dp[total] == -1) continue; + for (auto& num : nums) { + if (total < num) break; + if (dp[total - num] + 0LL + dp[total] > INT_MAX) { + dp[total - num] = -1; + break; + } + dp[total - num] += dp[total]; + } + } + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + combinationSum4(nums, target) { + nums.sort((a, b) => a - b); + const dp = new Map(); + dp.set(target, 1); + + for (let total = target; total > 0; total--) { + for (const num of nums) { + if (total < num) break; + dp.set(total - num, (dp.get(total - num) || 0) + (dp.get(total) || 0)); + } + } + return dp.get(0) || 0; + } +} +``` + +```csharp +public class Solution { + public int CombinationSum4(int[] nums, int target) { + Array.Sort(nums); + Dictionary dp = new Dictionary(); + dp[target] = 1; + + for (int total = target; total > 0; total--) { + if (!dp.ContainsKey(total)) continue; + foreach (int num in nums) { + if (total < num) break; + int key = total - num; + if (!dp.ContainsKey(key)) { + dp[key] = 0; + } + dp[key] += dp[total]; + } + } + + return dp.ContainsKey(0) ? dp[0] : 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * t)$ +* Space complexity: $O(t)$ + +> Where $n$ is the size of the array $nums$ and $t$ is the given target. \ No newline at end of file diff --git a/articles/combination-target-sum-ii.md b/articles/combination-target-sum-ii.md new file mode 100644 index 000000000..3b35aab6d --- /dev/null +++ b/articles/combination-target-sum-ii.md @@ -0,0 +1,1152 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def combinationSum2(self, candidates, target): + res = set() + candidates.sort() + + def generate_subsets(i, cur, total): + if total == target: + res.add(tuple(cur)) + return + if total > target or i == len(candidates): + return + + cur.append(candidates[i]) + generate_subsets(i + 1, cur, total + candidates[i]) + cur.pop() + + generate_subsets(i + 1, cur, total) + + generate_subsets(0, [], 0) + return [list(combination) for combination in res] +``` + +```java +public class Solution { + private Set> res; + + public List> combinationSum2(int[] candidates, int target) { + res = new HashSet<>(); + Arrays.sort(candidates); + generateSubsets(candidates, target, 0, new ArrayList<>(), 0); + return new ArrayList<>(res); + } + + private void generateSubsets(int[] candidates, int target, int i, List cur, int total) { + if (total == target) { + res.add(new ArrayList<>(cur)); + return; + } + if (total > target || i == candidates.length) { + return; + } + + cur.add(candidates[i]); + generateSubsets(candidates, target, i + 1, cur, total + candidates[i]); + cur.remove(cur.size() - 1); + + generateSubsets(candidates, target, i + 1, cur, total); + } +} +``` + +```cpp +class Solution { +public: + set> res; + + vector> combinationSum2(vector& candidates, int target) { + res.clear(); + sort(candidates.begin(), candidates.end()); + vector cur; + generateSubsets(candidates, target, 0, cur, 0); + return vector>(res.begin(), res.end()); + } + +private: + void generateSubsets(vector& candidates, int target, int i, vector& cur, int total) { + if (total == target) { + res.insert(cur); + return; + } + if (total > target || i == candidates.size()) { + return; + } + + cur.push_back(candidates[i]); + generateSubsets(candidates, target, i + 1, cur, total + candidates[i]); + cur.pop_back(); + + generateSubsets(candidates, target, i + 1, cur, total); + } +}; +``` + +```javascript +class Solution { + constructor() { + this.res = new Set(); + } + + /** + * @param {number[]} candidates + * @param {number} target + * @return {number[][]} + */ + combinationSum2(candidates, target) { + this.res.clear(); + candidates.sort((a, b) => a - b); + this.generateSubsets(candidates, target, 0, [], 0); + return Array.from(this.res, subset => JSON.parse(subset)); + } + + /** + * @param {number[]} candidates + * @param {number} target + * @param {number} i + * @param {number[]} cur + * @param {number} total + * @return {void} + */ + generateSubsets(candidates, target, i, cur, total) { + if (total === target) { + this.res.add(JSON.stringify([...cur])); + return; + } + if (total > target || i === candidates.length) { + return; + } + + cur.push(candidates[i]); + this.generateSubsets(candidates, target, i + 1, cur, total + candidates[i]); + cur.pop(); + + this.generateSubsets(candidates, target, i + 1, cur, total); + } +} +``` + +```csharp +public class Solution { + private HashSet res; + + public List> CombinationSum2(int[] candidates, int target) { + res = new HashSet(); + Array.Sort(candidates); + GenerateSubsets(candidates, target, 0, new List(), 0); + return res.Select(s => s.Split(',').Select(int.Parse).ToList()).ToList(); + } + + private void GenerateSubsets(int[] candidates, int target, int i, List cur, int total) { + if (total == target) { + res.Add(string.Join(",", cur)); + return; + } + if (total > target || i == candidates.Length) { + return; + } + + cur.Add(candidates[i]); + GenerateSubsets(candidates, target, i + 1, cur, total + candidates[i]); + cur.RemoveAt(cur.Count - 1); + + GenerateSubsets(candidates, target, i + 1, cur, total); + } +} +``` + +```go +func combinationSum2(candidates []int, target int) [][]int { + res := make(map[string][]int) + var results [][]int + sort.Ints(candidates) + + var generateSubsets func(int, []int, int) + generateSubsets = func(i int, cur []int, total int) { + if total == target { + key := fmt.Sprint(cur) + if _, found := res[key]; !found { + res[key] = append([]int{}, cur...) + results = append(results, res[key]) + } + return + } + if total > target || i == len(candidates) { + return + } + + generateSubsets(i+1, append(cur, candidates[i]), total+candidates[i]) + generateSubsets(i+1, cur, total) + } + + generateSubsets(0, []int{}, 0) + return results +} +``` + +```kotlin +class Solution { + fun combinationSum2(candidates: IntArray, target: Int): List> { + val res = HashSet>() + candidates.sort() + + fun generateSubsets(i: Int, cur: MutableList, total: Int) { + if (total == target) { + res.add(ArrayList(cur)) + return + } + if (total > target || i == candidates.size) { + return + } + + cur.add(candidates[i]) + generateSubsets(i + 1, cur, total + candidates[i]) + cur.removeAt(cur.size - 1) + + generateSubsets(i + 1, cur, total) + } + + generateSubsets(0, mutableListOf(), 0) + return res.toList() + } +} +``` + +```swift +class Solution { + func combinationSum2(_ candidates: [Int], _ target: Int) -> [[Int]] { + var res = Set<[Int]>() + let sortedCandidates = candidates.sorted() + + func generateSubsets(_ i: Int, _ cur: inout [Int], _ total: Int) { + if total == target { + res.insert(cur) + return + } + if total > target || i == sortedCandidates.count { + return + } + + cur.append(sortedCandidates[i]) + generateSubsets(i + 1, &cur, total + sortedCandidates[i]) + cur.removeLast() + + generateSubsets(i + 1, &cur, total) + } + + var cur: [Int] = [] + generateSubsets(0, &cur, 0) + return Array(res) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: $O(n * 2 ^ n)$ + +--- + +## 2. Backtracking + +::tabs-start + +```python +class Solution: + def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]: + res = [] + candidates.sort() + + def dfs(i, cur, total): + if total == target: + res.append(cur.copy()) + return + if total > target or i == len(candidates): + return + + cur.append(candidates[i]) + dfs(i + 1, cur, total + candidates[i]) + cur.pop() + + + while i + 1 < len(candidates) and candidates[i] == candidates[i+1]: + i += 1 + dfs(i + 1, cur, total) + + dfs(0, [], 0) + return res +``` + +```java +public class Solution { + private List> res; + + public List> combinationSum2(int[] candidates, int target) { + res = new ArrayList<>(); + Arrays.sort(candidates); + dfs(candidates, target, 0, new ArrayList<>(), 0); + return res; + } + + private void dfs(int[] candidates, int target, int i, List cur, int total) { + if (total == target) { + res.add(new ArrayList<>(cur)); + return; + } + if (total > target || i == candidates.length) { + return; + } + + cur.add(candidates[i]); + dfs(candidates, target, i + 1, cur, total + candidates[i]); + cur.remove(cur.size() - 1); + + while (i + 1 < candidates.length && candidates[i] == candidates[i + 1]) { + i++; + } + dfs(candidates, target, i + 1, cur, total); + } +} +``` + +```cpp +class Solution { +public: + vector> res; + vector> combinationSum2(vector& candidates, int target) { + res.clear(); + sort(candidates.begin(), candidates.end()); + vector cur; + dfs(candidates, target, 0, cur, 0); + return res; + } + +private: + void dfs(vector& candidates, int target, int i, vector& cur, int total) { + if (total == target) { + res.push_back(cur); + return; + } + if (total > target || i == candidates.size()) { + return; + } + + cur.push_back(candidates[i]); + dfs(candidates, target, i + 1, cur, total + candidates[i]); + cur.pop_back(); + + while (i + 1 < candidates.size() && candidates[i] == candidates[i + 1]) { + i++; + } + dfs(candidates, target, i + 1, cur, total); + } +}; +``` + +```javascript +class Solution { + constructor() { + this.res = []; + } + + /** + * @param {number[]} candidates + * @param {number} target + * @return {number[][]} + */ + combinationSum2(candidates, target) { + this.res = []; + candidates.sort((a, b) => a - b); + this.dfs(candidates, target, 0, [], 0); + return this.res; + } + + /** + * @param {number[]} candidates + * @param {number} target + * @param {number} i + * @param {number[]} cur + * @param {number} total + * @return {void} + */ + dfs(candidates, target, i, cur, total) { + if (total === target) { + this.res.push([...cur]); + return; + } + if (total > target || i === candidates.length) { + return; + } + + cur.push(candidates[i]); + this.dfs(candidates, target, i + 1, cur, total + candidates[i]); + cur.pop(); + + while (i + 1 < candidates.length && candidates[i] === candidates[i + 1]) { + i++; + } + this.dfs(candidates, target, i + 1, cur, total); + } +} +``` + +```csharp +public class Solution { + private List> res; + + public List> CombinationSum2(int[] candidates, int target) { + res = new List>(); + Array.Sort(candidates); + Dfs(candidates, target, 0, new List(), 0); + return res; + } + + private void Dfs(int[] candidates, int target, int i, List cur, int total) { + if (total == target) { + res.Add(new List(cur)); + return; + } + if (total > target || i == candidates.Length) { + return; + } + + cur.Add(candidates[i]); + Dfs(candidates, target, i + 1, cur, total + candidates[i]); + cur.RemoveAt(cur.Count - 1); + + while (i + 1 < candidates.Length && candidates[i] == candidates[i + 1]) { + i++; + } + Dfs(candidates, target, i + 1, cur, total); + } +} +``` + +```go +func combinationSum2(candidates []int, target int) [][]int { + res := [][]int{} + sort.Ints(candidates) + + var dfs func(int, []int, int) + dfs = func(i int, cur []int, total int) { + if total == target { + temp := make([]int, len(cur)) + copy(temp, cur) + res = append(res, temp) + return + } + if total > target || i == len(candidates) { + return + } + + cur = append(cur, candidates[i]) + dfs(i+1, cur, total+candidates[i]) + cur = cur[:len(cur)-1] + + for i+1 < len(candidates) && candidates[i] == candidates[i+1] { + i++ + } + dfs(i+1, cur, total) + } + + dfs(0, []int{}, 0) + return res +} +``` + +```kotlin +class Solution { + fun combinationSum2(candidates: IntArray, target: Int): List> { + val res = mutableListOf>() + candidates.sort() + + fun dfs(i: Int, cur: MutableList, total: Int) { + if (total == target) { + res.add(ArrayList(cur)) + return + } + if (total > target || i == candidates.size) { + return + } + + cur.add(candidates[i]) + dfs(i + 1, cur, total + candidates[i]) + cur.removeAt(cur.size - 1) + + var next = i + 1 + while (next < candidates.size && candidates[next] == candidates[i]) { + next++ + } + dfs(next, cur, total) + } + + dfs(0, mutableListOf(), 0) + return res + } +} +``` + +```swift +class Solution { + func combinationSum2(_ candidates: [Int], _ target: Int) -> [[Int]] { + var res = [[Int]]() + let sortedCandidates = candidates.sorted() + + func dfs(_ i: Int, _ cur: inout [Int], _ total: Int) { + if total == target { + res.append(cur) + return + } + if total > target || i == sortedCandidates.count { + return + } + + cur.append(sortedCandidates[i]) + dfs(i + 1, &cur, total + sortedCandidates[i]) + cur.removeLast() + + var j = i + while j + 1 < sortedCandidates.count && sortedCandidates[j] == sortedCandidates[j + 1] { + j += 1 + } + dfs(j + 1, &cur, total) + } + + var cur: [Int] = [] + dfs(0, &cur, 0) + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Backtracking (Hash Map) + +::tabs-start + +```python +class Solution: + def combinationSum2(self, nums, target): + self.res = [] + self.count = defaultdict(int) + cur = [] + A = [] + + for num in nums: + if self.count[num] == 0: + A.append(num) + self.count[num] += 1 + self.backtrack(A, target, cur, 0) + return self.res + + def backtrack(self, nums, target, cur, i): + if target == 0: + self.res.append(cur.copy()) + return + if target < 0 or i >= len(nums): + return + + if self.count[nums[i]] > 0: + cur.append(nums[i]) + self.count[nums[i]] -= 1 + self.backtrack(nums, target - nums[i], cur, i) + self.count[nums[i]] += 1 + cur.pop() + + self.backtrack(nums, target, cur, i + 1) +``` + +```java +public class Solution { + List> res = new ArrayList<>(); + Map count = new HashMap<>(); + + public List> combinationSum2(int[] nums, int target) { + List cur = new ArrayList<>(); + List A = new ArrayList<>(); + + for (int num : nums) { + if (!count.containsKey(num)) { + A.add(num); + } + count.put(num, count.getOrDefault(num, 0) + 1); + } + backtrack(A, target, cur, 0); + return res; + } + + private void backtrack(List nums, int target, List cur, int i) { + if (target == 0) { + res.add(new ArrayList<>(cur)); + return; + } + if (target < 0 || i >= nums.size()) { + return; + } + + if (count.get(nums.get(i)) > 0) { + cur.add(nums.get(i)); + count.put(nums.get(i), count.get(nums.get(i)) - 1); + backtrack(nums, target - nums.get(i), cur, i); + count.put(nums.get(i), count.get(nums.get(i)) + 1); + cur.remove(cur.size() - 1); + } + + backtrack(nums, target, cur, i + 1); + } +} +``` + +```cpp +class Solution { +public: + vector> res; + unordered_map count; + vector> combinationSum2(vector& nums, int target) { + vector cur; + vector A; + for (int num : nums) { + if (!count[num]) { + A.push_back(num); + } + count[num]++; + } + backtrack(A, target, cur, 0); + return res; + } + + void backtrack(vector& nums, int target, vector& cur, int i) { + if (target == 0) { + res.push_back(cur); + return; + } + if (target < 0 || i >= nums.size()) { + return; + } + + if (count[nums[i]]) { + cur.push_back(nums[i]); + count[nums[i]]--; + backtrack(nums, target - nums[i], cur, i); + count[nums[i]]++; + cur.pop_back(); + } + + backtrack(nums, target, cur, i + 1); + } +}; +``` + +```javascript +class Solution { + constructor() { + this.res = []; + this.count = new Map(); + } + + /** + * @param {number[]} candidates + * @param {number} target + * @return {number[][]} + */ + combinationSum2(nums, target) { + const cur = []; + const A = []; + + for (const num of nums) { + if (!this.count.has(num)) { + A.push(num); + } + this.count.set(num, (this.count.get(num) || 0) + 1); + } + this.backtrack(A, target, cur, 0); + return this.res; + } + + /** + * @param {number[]} nums + * @param {number} target + * @param {number[]} cur + * @param {number} i + * @return {void} + */ + backtrack(nums, target, cur, i) { + if (target === 0) { + this.res.push([...cur]); + return; + } + if (target < 0 || i >= nums.length) { + return; + } + + if (this.count.get(nums[i]) > 0) { + cur.push(nums[i]); + this.count.set(nums[i], this.count.get(nums[i]) - 1); + this.backtrack(nums, target - nums[i], cur, i); + this.count.set(nums[i], this.count.get(nums[i]) + 1); + cur.pop(); + } + + this.backtrack(nums, target, cur, i + 1); + } +} +``` + +```csharp +public class Solution { + public List> res = new List>(); + public Dictionary count = new Dictionary(); + + public List> CombinationSum2(int[] nums, int target) { + List cur = new List(); + List A = new List(); + + foreach (int num in nums) { + if (!count.ContainsKey(num)) { + A.Add(num); + } + if (count.ContainsKey(num)) { + count[num]++; + } else { + count[num] = 1; + } + } + Backtrack(A, target, cur, 0); + return res; + } + + private void Backtrack(List nums, int target, List cur, int i) { + if (target == 0) { + res.Add(new List(cur)); + return; + } + if (target < 0 || i >= nums.Count) { + return; + } + + if (count[nums[i]] > 0) { + cur.Add(nums[i]); + count[nums[i]]--; + Backtrack(nums, target - nums[i], cur, i); + count[nums[i]]++; + cur.RemoveAt(cur.Count - 1); + } + + Backtrack(nums, target, cur, i + 1); + } +} +``` + +```go +func combinationSum2(nums []int, target int) [][]int { + res := [][]int{} + count := map[int]int{} + uniqueNums := []int{} + for _, num := range nums { + if count[num] == 0 { + uniqueNums = append(uniqueNums, num) + } + count[num]++ + } + sort.Ints(uniqueNums) + + var backtrack func(int, int, []int) + backtrack = func(target int, i int, cur []int) { + if target == 0 { + temp := make([]int, len(cur)) + copy(temp, cur) + res = append(res, temp) + return + } + if target < 0 || i >= len(uniqueNums) { + return + } + + if count[uniqueNums[i]] > 0 { + cur = append(cur, uniqueNums[i]) + count[uniqueNums[i]]-- + backtrack(target-uniqueNums[i], i, cur) + count[uniqueNums[i]]++ + cur = cur[:len(cur)-1] + } + + backtrack(target, i+1, cur) + } + + backtrack(target, 0, []int{}) + return res +} +``` + +```kotlin +class Solution { + private val res = mutableListOf>() + private val count = HashMap() + + fun combinationSum2(nums: IntArray, target: Int): List> { + val uniqueNums = mutableListOf() + for (num in nums) { + count[num] = count.getOrDefault(num, 0) + 1 + if (count[num] == 1) { + uniqueNums.add(num) + } + } + uniqueNums.sort() + + backtrack(uniqueNums, target, mutableListOf(), 0) + return res + } + + fun backtrack(nums: List, target: Int, cur: MutableList, i: Int) { + if (target == 0) { + res.add(ArrayList(cur)) + return + } + if (target < 0 || i >= nums.size) { + return + } + + if (count[nums[i]] ?: 0 > 0) { + cur.add(nums[i]) + count[nums[i]] = count[nums[i]]!! - 1 + backtrack(nums, target - nums[i], cur, i) + count[nums[i]] = count[nums[i]]!! + 1 + cur.removeAt(cur.size - 1) + } + + backtrack(nums, target, cur, i + 1) + } +} +``` + +```swift +class Solution { + func combinationSum2(_ nums: [Int], _ target: Int) -> [[Int]] { + var res = [[Int]]() + var count = [Int: Int]() + var uniqueNums = [Int]() + + for num in nums { + if count[num] == nil { + uniqueNums.append(num) + } + count[num, default: 0] += 1 + } + + func backtrack(_ nums: [Int], _ target: Int, _ cur: inout [Int], _ i: Int) { + if target == 0 { + res.append(cur) + return + } + if target < 0 || i >= nums.count { + return + } + + if count[nums[i], default: 0] > 0 { + cur.append(nums[i]) + count[nums[i], default: 0] -= 1 + backtrack(nums, target - nums[i], &cur, i) + count[nums[i], default: 0] += 1 + cur.removeLast() + } + + backtrack(nums, target, &cur, i + 1) + } + + var cur: [Int] = [] + backtrack(uniqueNums, target, &cur, 0) + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Backtracking (Optimal) + +::tabs-start + +```python +class Solution: + def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]: + res = [] + candidates.sort() + + def dfs(idx, path, cur): + if cur == target: + res.append(path.copy()) + return + for i in range(idx, len(candidates)): + if i > idx and candidates[i] == candidates[i - 1]: + continue + if cur + candidates[i] > target: + break + + path.append(candidates[i]) + dfs(i + 1, path, cur + candidates[i]) + path.pop() + + dfs(0, [], 0) + return res +``` + +```java +public class Solution { + private static List> res = new ArrayList<>(); + + public List> combinationSum2(int[] candidates, int target) { + res.clear(); + Arrays.sort(candidates); + dfs(0, new ArrayList<>(), 0, candidates, target); + return res; + } + + private static void dfs(int idx, List path, int cur, int[] candidates, int target) { + if (cur == target) { + res.add(new ArrayList<>(path)); + return; + } + for (int i = idx; i < candidates.length; i++) { + if (i > idx && candidates[i] == candidates[i - 1]) { + continue; + } + if (cur + candidates[i] > target) { + break; + } + + path.add(candidates[i]); + dfs(i + 1, path, cur + candidates[i], candidates, target); + path.remove(path.size() - 1); + } + } +} +``` + +```cpp +class Solution { +public: + vector> res; + vector> combinationSum2(vector& candidates, int target) { + res.clear(); + sort(candidates.begin(), candidates.end()); + dfs(0, {}, 0, candidates, target); + return res; + } + +private: + void dfs(int idx, vector path, int cur, vector& candidates, int target) { + if (cur == target) { + res.push_back(path); + return; + } + for (int i = idx; i < candidates.size(); i++) { + if (i > idx && candidates[i] == candidates[i - 1]) { + continue; + } + if (cur + candidates[i] > target) { + break; + } + + path.push_back(candidates[i]); + dfs(i + 1, path, cur + candidates[i], candidates, target); + path.pop_back(); + } + } +}; +``` + +```javascript +class Solution { + constructor() { + this.res = []; + } + + /** + * @param {number[]} candidates + * @param {number} target + * @return {number[][]} + */ + combinationSum2(candidates, target) { + this.res = []; + candidates.sort((a, b) => a - b); + + const dfs = (idx, path, cur) => { + if (cur === target) { + this.res.push([...path]); + return; + } + for (let i = idx; i < candidates.length; i++) { + if (i > idx && candidates[i] === candidates[i - 1]) { + continue; + } + if (cur + candidates[i] > target) { + break; + } + + path.push(candidates[i]); + dfs(i + 1, path, cur + candidates[i]); + path.pop(); + } + }; + + dfs(0, [], 0); + return this.res; + } +} +``` + +```csharp +public class Solution { + private static List> res = new List>(); + + public List> CombinationSum2(int[] candidates, int target) { + res.Clear(); + Array.Sort(candidates); + + dfs(0, new List(), 0, candidates, target); + return res; + } + + private void dfs(int idx, List path, int cur, int[] candidates, int target) { + if (cur == target) { + res.Add(new List(path)); + return; + } + for (int i = idx; i < candidates.Length; i++) { + if (i > idx && candidates[i] == candidates[i - 1]) { + continue; + } + if (cur + candidates[i] > target) { + break; + } + + path.Add(candidates[i]); + dfs(i + 1, path, cur + candidates[i], candidates, target); + path.RemoveAt(path.Count - 1); + } + } +} +``` + +```go +func combinationSum2(candidates []int, target int) [][]int { + var res [][]int + sort.Ints(candidates) + + var dfs func(int, []int, int) + dfs = func(idx int, path []int, cur int) { + if cur == target { + temp := make([]int, len(path)) + copy(temp, path) + res = append(res, temp) + return + } + + for i := idx; i < len(candidates); i++ { + if i > idx && candidates[i] == candidates[i-1] { + continue + } + if cur + candidates[i] > target { + break + } + + path = append(path, candidates[i]) + dfs(i+1, path, cur + candidates[i]) + path = path[:len(path)-1] + } + } + + dfs(0, []int{}, 0) + return res +} +``` + +```kotlin +class Solution { + fun combinationSum2(candidates: IntArray, target: Int): List> { + val res = mutableListOf>() + candidates.sort() + + fun dfs(idx: Int, path: MutableList, cur: Int) { + if (cur == target) { + res.add(ArrayList(path)) + return + } + + for (i in idx until candidates.size) { + if (i > idx && candidates[i] == candidates[i - 1]) continue + if (cur + candidates[i] > target) break + + path.add(candidates[i]) + dfs(i + 1, path, cur + candidates[i]) + path.removeAt(path.size - 1) + } + } + + dfs(0, mutableListOf(), 0) + return res + } +} +``` + +```swift +class Solution { + func combinationSum2(_ candidates: [Int], _ target: Int) -> [[Int]] { + var res = [[Int]]() + let sortedCandidates = candidates.sorted() + + func dfs(_ idx: Int, _ path: inout [Int], _ cur: Int) { + if cur == target { + res.append(path) + return + } + + for i in idx.. idx && sortedCandidates[i] == sortedCandidates[i - 1] { + continue + } + if cur + sortedCandidates[i] > target { + break + } + + path.append(sortedCandidates[i]) + dfs(i + 1, &path, cur + sortedCandidates[i]) + path.removeLast() + } + } + + var path: [Int] = [] + dfs(0, &path, 0) + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/combination-target-sum.md b/articles/combination-target-sum.md new file mode 100644 index 000000000..46436ad64 --- /dev/null +++ b/articles/combination-target-sum.md @@ -0,0 +1,472 @@ +## 1. Backtracking + +::tabs-start + +```python +class Solution: + def combinationSum(self, nums: List[int], target: int) -> List[List[int]]: + res = [] + + def dfs(i, cur, total): + if total == target: + res.append(cur.copy()) + return + if i >= len(nums) or total > target: + return + + cur.append(nums[i]) + dfs(i, cur, total + nums[i]) + cur.pop() + dfs(i + 1, cur, total) + + dfs(0, [], 0) + return res +``` + +```java +public class Solution { + List> res; + public List> combinationSum(int[] nums, int target) { + res = new ArrayList>(); + List cur = new ArrayList(); + backtrack(nums, target, cur, 0); + return res; + } + + public void backtrack(int[] nums, int target, List cur, int i) { + if (target == 0) { + res.add(new ArrayList(cur)); + return; + } + if (target < 0 || i >= nums.length) { + return; + } + + cur.add(nums[i]); + backtrack(nums, target - nums[i], cur, i); + cur.remove(cur.size() - 1); + backtrack(nums, target, cur, i + 1); + } +} +``` + +```cpp +class Solution { +public: + vector> res; + vector> combinationSum(vector& nums, int target) { + vector cur; + backtrack(nums, target, cur, 0); + return res; + } + + void backtrack(vector& nums, int target, vector& cur, int i) { + if (target == 0) { + res.push_back(cur); + return; + } + if (target < 0 || i >= nums.size()) { + return; + } + + cur.push_back(nums[i]); + backtrack(nums, target - nums[i], cur, i); + cur.pop_back(); + backtrack(nums, target, cur, i + 1); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @returns {number[][]} + */ + combinationSum(nums, target) { + let ans = []; + let cur = []; + this.backtrack(nums, target, ans, cur, 0); + return ans; + } + + /** + * @param {number[]} nums + * @param {number} target + * @param {number[][]} ans + * @param {number[]} cur + * @param {number} index + */ + backtrack(nums, target, ans, cur, index) { + if (target === 0) { + ans.push([...cur]); + } else if (target < 0 || index >= nums.length) { + return; + } else { + cur.push(nums[index]); + this.backtrack(nums, target - nums[index], ans, cur, index); + + cur.pop(); + this.backtrack(nums, target, ans, cur, index + 1); + } + } +} +``` + +```csharp +public class Solution { + + List> res = new List>(); + + public void backtrack(int i, List cur, int total, int[] nums, int target) { + if(total == target) { + res.Add(cur.ToList()); + return; + } + + if(total > target || i >= nums.Length) return; + + cur.Add(nums[i]); + backtrack(i, cur, total + nums[i], nums, target); + cur.Remove(cur.Last()); + + backtrack(i + 1, cur, total, nums, target); + + } + public List> CombinationSum(int[] nums, int target) { + backtrack(0, new List(), 0, nums, target); + return res; + } +} +``` + +```go +func combinationSum(nums []int, target int) [][]int { + res := [][]int{} + + var dfs func(int, []int, int) + dfs = func(i int, cur []int, total int) { + if total == target { + temp := make([]int, len(cur)) + copy(temp, cur) + res = append(res, temp) + return + } + if i >= len(nums) || total > target { + return + } + + cur = append(cur, nums[i]) + dfs(i, cur, total + nums[i]) + cur = cur[:len(cur)-1] + dfs(i+1, cur, total) + } + + dfs(0, []int{}, 0) + return res +} +``` + +```kotlin +class Solution { + fun combinationSum(nums: IntArray, target: Int): List> { + val res = mutableListOf>() + + fun dfs(i: Int, cur: MutableList, total: Int) { + if (total == target) { + res.add(ArrayList(cur)) + return + } + if (i >= nums.size || total > target) { + return + } + + cur.add(nums[i]) + dfs(i, cur, total + nums[i]) + cur.removeAt(cur.size - 1) + dfs(i + 1, cur, total) + } + + dfs(0, mutableListOf(), 0) + return res + } +} +``` + +```swift +class Solution { + func combinationSum(_ nums: [Int], _ target: Int) -> [[Int]] { + var res: [[Int]] = [] + + func dfs(_ i: Int, _ cur: inout [Int], _ total: Int) { + if total == target { + res.append(cur) + return + } + if i >= nums.count || total > target { + return + } + + cur.append(nums[i]) + dfs(i, &cur, total + nums[i]) + cur.removeLast() + dfs(i + 1, &cur, total) + } + + var cur: [Int] = [] + dfs(0, &cur, 0) + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ \frac{t}{m})$ +* Space complexity: $O(\frac{t}{m})$ + +> Where $t$ is the given $target$ and $m$ is the minimum value in $nums$. + +--- + +## 2. Backtracking (Optimal) + +::tabs-start + +```python +class Solution: + def combinationSum(self, nums: List[int], target: int) -> List[List[int]]: + res = [] + nums.sort() + + def dfs(i, cur, total): + if total == target: + res.append(cur.copy()) + return + + for j in range(i, len(nums)): + if total + nums[j] > target: + return + cur.append(nums[j]) + dfs(j, cur, total + nums[j]) + cur.pop() + + dfs(0, [], 0) + return res +``` + +```java +public class Solution { + List> res; + public List> combinationSum(int[] nums, int target) { + res = new ArrayList<>(); + Arrays.sort(nums); + + dfs(0, new ArrayList<>(), 0, nums, target); + return res; + } + + private void dfs(int i, List cur, int total, int[] nums, int target) { + if (total == target) { + res.add(new ArrayList<>(cur)); + return; + } + + for (int j = i; j < nums.length; j++) { + if (total + nums[j] > target) { + return; + } + cur.add(nums[j]); + dfs(j, cur, total + nums[j], nums, target); + cur.remove(cur.size() - 1); + } + } +} +``` + +```cpp +class Solution { +public: + vector> res; + vector> combinationSum(vector& nums, int target) { + sort(nums.begin(), nums.end()); + dfs(0, {}, 0, nums, target); + return res; + } + + void dfs(int i, vector cur, int total, vector& nums, int target) { + if (total == target) { + res.push_back(cur); + return; + } + + for (int j = i; j < nums.size(); j++) { + if (total + nums[j] > target) { + return; + } + cur.push_back(nums[j]); + dfs(j, cur, total + nums[j], nums, target); + cur.pop_back(); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @returns {number[][]} + */ + combinationSum(nums, target) { + const res = []; + nums.sort((a, b) => a - b); + + const dfs = (i, cur, total) => { + if (total === target) { + res.push([...cur]); + return; + } + + for (let j = i; j < nums.length; j++) { + if (total + nums[j] > target) { + return; + } + cur.push(nums[j]); + dfs(j, cur, total + nums[j]); + cur.pop(); + } + }; + + dfs(0, [], 0); + return res; + } +} +``` + +```csharp +public class Solution { + List> res; + public List> CombinationSum(int[] nums, int target) { + res = new List>(); + Array.Sort(nums); + dfs(0, new List(), 0, nums, target); + return res; + } + + private void dfs(int i, List cur, int total, int[] nums, int target) { + if (total == target) { + res.Add(new List(cur)); + return; + } + + for (int j = i; j < nums.Length; j++) { + if (total + nums[j] > target) { + return; + } + cur.Add(nums[j]); + dfs(j, cur, total + nums[j], nums, target); + cur.RemoveAt(cur.Count - 1); + } + } +} +``` + +```go +func combinationSum(nums []int, target int) [][]int { + res := [][]int{} + sort.Ints(nums) + + var dfs func(int, []int, int) + dfs = func(i int, cur []int, total int) { + if total == target { + temp := make([]int, len(cur)) + copy(temp, cur) + res = append(res, temp) + return + } + + for j := i; j < len(nums); j++ { + if total + nums[j] > target { + return + } + cur = append(cur, nums[j]) + dfs(j, cur, total + nums[j]) + cur = cur[:len(cur)-1] + } + } + + dfs(0, []int{}, 0) + return res +} +``` + +```kotlin +class Solution { + fun combinationSum(nums: IntArray, target: Int): List> { + val res = mutableListOf>() + nums.sort() + + fun dfs(i: Int, cur: MutableList, total: Int) { + if (total == target) { + res.add(ArrayList(cur)) + return + } + + for (j in i until nums.size) { + if (total + nums[j] > target) { + return + } + cur.add(nums[j]) + dfs(j, cur, total + nums[j]) + cur.removeAt(cur.size - 1) + } + } + + dfs(0, mutableListOf(), 0) + return res + } +} +``` + +```swift +class Solution { + func combinationSum(_ nums: [Int], _ target: Int) -> [[Int]] { + var res: [[Int]] = [] + let sortedNums = nums.sorted() + + func dfs(_ i: Int, _ cur: inout [Int], _ total: Int) { + if total == target { + res.append(cur) + return + } + + for j in i.. target { + return + } + cur.append(sortedNums[j]) + dfs(j, &cur, total + sortedNums[j]) + cur.removeLast() + } + } + + var cur: [Int] = [] + dfs(0, &cur, 0) + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ \frac{t}{m})$ +* Space complexity: $O(\frac{t}{m})$ + +> Where $t$ is the given $target$ and $m$ is the minimum value in $nums$. \ No newline at end of file diff --git a/articles/combinations-of-a-phone-number.md b/articles/combinations-of-a-phone-number.md new file mode 100644 index 000000000..51796e3f7 --- /dev/null +++ b/articles/combinations-of-a-phone-number.md @@ -0,0 +1,500 @@ +## 1. Backtracking + +::tabs-start + +```python +class Solution: + def letterCombinations(self, digits: str) -> List[str]: + res = [] + digitToChar = { + "2": "abc", + "3": "def", + "4": "ghi", + "5": "jkl", + "6": "mno", + "7": "qprs", + "8": "tuv", + "9": "wxyz", + } + + def backtrack(i, curStr): + if len(curStr) == len(digits): + res.append(curStr) + return + for c in digitToChar[digits[i]]: + backtrack(i + 1, curStr + c) + + if digits: + backtrack(0, "") + + return res +``` + +```java +public class Solution { + + private List res = new ArrayList<>(); + private String[] digitToChar = { + "", "", "abc", "def", "ghi", "jkl", "mno", "qprs", "tuv", "wxyz" + }; + + public List letterCombinations(String digits) { + if (digits.isEmpty()) return res; + backtrack(0, "", digits); + return res; + } + + private void backtrack(int i, String curStr, String digits) { + if (curStr.length() == digits.length()) { + res.add(curStr); + return; + } + String chars = digitToChar[digits.charAt(i) - '0']; + for (char c : chars.toCharArray()) { + backtrack(i + 1, curStr + c, digits); + } + } +} +``` + +```cpp +class Solution { +public: + vector res; + vector digitToChar = {"", "", "abc", "def", "ghi", "jkl", + "mno", "qprs", "tuv", "wxyz"}; + + vector letterCombinations(string digits) { + if (digits.empty()) return res; + backtrack(0, "", digits); + return res; + } + + void backtrack(int i, string curStr, string &digits) { + if (curStr.size() == digits.size()) { + res.push_back(curStr); + return; + } + string chars = digitToChar[digits[i] - '0']; + for (char c : chars) { + backtrack(i + 1, curStr + c, digits); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} digits + * @return {string[]} + */ + letterCombinations(digits) { + let res = []; + if (digits.length === 0) return res; + const digitToChar = { + "2": "abc", + "3": "def", + "4": "ghi", + "5": "jkl", + "6": "mno", + "7": "qprs", + "8": "tuv", + "9": "wxyz" + }; + + const backtrack = (i, curStr) => { + if (curStr.length === digits.length) { + res.push(curStr); + return; + } + for (const c of digitToChar[digits[i]]) { + backtrack(i + 1, curStr + c); + } + } + backtrack(0, ""); + return res; + } +} +``` + +```csharp +public class Solution { + + private List res = new List(); + private Dictionary digitToChar = new Dictionary { + {'2', "abc"}, {'3', "def"}, {'4', "ghi"}, {'5', "jkl"}, + {'6', "mno"}, {'7', "qprs"}, {'8', "tuv"}, {'9', "wxyz"} + }; + + public List LetterCombinations(string digits) { + if (digits.Length == 0) return res; + Backtrack(0, "", digits); + return res; + } + + private void Backtrack(int i, string curStr, string digits) { + if (curStr.Length == digits.Length) { + res.Add(curStr); + return; + } + foreach (char c in digitToChar[digits[i]]) { + Backtrack(i + 1, curStr + c, digits); + } + } +} +``` + +```go +func letterCombinations(digits string) []string { + if len(digits) == 0 { + return []string{} + } + + res := []string{} + digitToChar := map[byte]string{ + '2': "abc", + '3': "def", + '4': "ghi", + '5': "jkl", + '6': "mno", + '7': "pqrs", + '8': "tuv", + '9': "wxyz", + } + + var backtrack func(i int, curStr string) + backtrack = func(i int, curStr string) { + if len(curStr) == len(digits) { + res = append(res, curStr) + return + } + for _, c := range digitToChar[digits[i]] { + backtrack(i+1, curStr+string(c)) + } + } + + backtrack(0, "") + return res +} +``` + +```kotlin +class Solution { + fun letterCombinations(digits: String): List { + if (digits.isEmpty()) return emptyList() + + val res = mutableListOf() + val digitToChar = mapOf( + '2' to "abc", + '3' to "def", + '4' to "ghi", + '5' to "jkl", + '6' to "mno", + '7' to "pqrs", + '8' to "tuv", + '9' to "wxyz" + ) + + fun backtrack(i: Int, curStr: String) { + if (curStr.length == digits.length) { + res.add(curStr) + return + } + for (c in digitToChar[digits[i]]!!) { + backtrack(i + 1, curStr + c) + } + } + + backtrack(0, "") + return res + } +} +``` + +```swift +class Solution { + func letterCombinations(_ digits: String) -> [String] { + guard !digits.isEmpty else { return [] } + + let digitToChar: [Character: String] = [ + "2": "abc", "3": "def", "4": "ghi", "5": "jkl", + "6": "mno", "7": "pqrs", "8": "tuv", "9": "wxyz" + ] + + var res = [String]() + let digitsArray = Array(digits) + + func backtrack(_ i: Int, _ curStr: String) { + if curStr.count == digits.count { + res.append(curStr) + return + } + if let letters = digitToChar[digitsArray[i]] { + for c in letters { + backtrack(i + 1, curStr + String(c)) + } + } + } + + backtrack(0, "") + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 4 ^ n)$ +* Space complexity: + * $O(n)$ extra space. + * $O(n * 4 ^ n)$ space for the output list. + +--- + +## 2. Iteration + +::tabs-start + +```python +class Solution: + def letterCombinations(self, digits: str) -> List[str]: + if not digits: + return [] + + res = [""] + digitToChar = { + "2": "abc", + "3": "def", + "4": "ghi", + "5": "jkl", + "6": "mno", + "7": "qprs", + "8": "tuv", + "9": "wxyz", + } + + for digit in digits: + tmp = [] + for curStr in res: + for c in digitToChar[digit]: + tmp.append(curStr + c) + res = tmp + return res +``` + +```java +public class Solution { + + public List letterCombinations(String digits) { + if (digits.isEmpty()) return new ArrayList<>(); + + List res = new ArrayList<>(); + res.add(""); + String[] digitToChar = { + "", "", "abc", "def", "ghi", "jkl", + "mno", "qprs", "tuv", "wxyz" + }; + + for (char digit : digits.toCharArray()) { + List tmp = new ArrayList<>(); + for (String curStr : res) { + for (char c : digitToChar[digit - '0'].toCharArray()) { + tmp.add(curStr + c); + } + } + res = tmp; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector letterCombinations(string digits) { + if (digits.empty()) return {}; + + vector res = {""}; + vector digitToChar = { + "", "", "abc", "def", "ghi", "jkl", + "mno", "qprs", "tuv", "wxyz" + }; + + for (char digit : digits) { + vector tmp; + for (string &curStr : res) { + for (char c : digitToChar[digit - '0']) { + tmp.push_back(curStr + c); + } + } + res = tmp; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} digits + * @return {string[]} + */ + letterCombinations(digits) { + if (digits.length === 0) return []; + + let res = [""]; + const digitToChar = { + "2": "abc", + "3": "def", + "4": "ghi", + "5": "jkl", + "6": "mno", + "7": "qprs", + "8": "tuv", + "9": "wxyz" + }; + + for (const digit of digits) { + const tmp = []; + for (const curStr of res) { + for (const c of digitToChar[digit]) { + tmp.push(curStr + c); + } + } + res = tmp; + } + return res; + } +} +``` + +```csharp +public class Solution { + + public List LetterCombinations(string digits) { + if (digits.Length == 0) return new List(); + + List res = new List { "" }; + Dictionary digitToChar = new Dictionary { + { '2', "abc" }, { '3', "def" }, { '4', "ghi" }, { '5', "jkl" }, + { '6', "mno" }, { '7', "qprs" }, { '8', "tuv" }, { '9', "wxyz" } + }; + + foreach (char digit in digits) { + List tmp = new List(); + foreach (string curStr in res) { + foreach (char c in digitToChar[digit]) { + tmp.Add(curStr + c); + } + } + res = tmp; + } + return res; + } +} +``` + +```go +func letterCombinations(digits string) []string { + if len(digits) == 0 { + return []string{} + } + + res := []string{""} + digitToChar := map[byte]string{ + '2': "abc", + '3': "def", + '4': "ghi", + '5': "jkl", + '6': "mno", + '7': "pqrs", + '8': "tuv", + '9': "wxyz", + } + + for i := 0; i < len(digits); i++ { + digit := digits[i] + tmp := []string{} + for _, curStr := range res { + for _, c := range digitToChar[digit] { + tmp = append(tmp, curStr+string(c)) + } + } + res = tmp + } + return res +} +``` + +```kotlin +class Solution { + fun letterCombinations(digits: String): List { + if (digits.isEmpty()) return emptyList() + + var res = listOf("") + val digitToChar = mapOf( + '2' to "abc", + '3' to "def", + '4' to "ghi", + '5' to "jkl", + '6' to "mno", + '7' to "pqrs", + '8' to "tuv", + '9' to "wxyz" + ) + + for (digit in digits) { + val tmp = mutableListOf() + for (curStr in res) { + for (c in digitToChar[digit]!!) { + tmp.add(curStr + c) + } + } + res = tmp + } + return res + } +} +``` + +```swift +class Solution { + func letterCombinations(_ digits: String) -> [String] { + guard !digits.isEmpty else { return [] } + + let digitToChar: [Character: String] = [ + "2": "abc", "3": "def", "4": "ghi", "5": "jkl", + "6": "mno", "7": "pqrs", "8": "tuv", "9": "wxyz" + ] + + var res = [""] + + for digit in digits { + guard let letters = digitToChar[digit] else { continue } + var tmp = [String]() + for curStr in res { + for c in letters { + tmp.append(curStr + String(c)) + } + } + res = tmp + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 4 ^ n)$ +* Space complexity: + * $O(n)$ extra space. + * $O(n * 4 ^ n)$ space for the output list. diff --git a/articles/combinations.md b/articles/combinations.md new file mode 100644 index 000000000..321ffeab1 --- /dev/null +++ b/articles/combinations.md @@ -0,0 +1,554 @@ +## 1. Backtracking - I + +::tabs-start + +```python +class Solution: + def combine(self, n: int, k: int) -> List[List[int]]: + res = [] + + def backtrack(i, comb): + if i > n: + if len(comb) == k: + res.append(comb.copy()) + return + + comb.append(i) + backtrack(i + 1, comb) + comb.pop() + backtrack(i + 1, comb) + + backtrack(1, []) + return res +``` + +```java +public class Solution { + private List> res; + + public List> combine(int n, int k) { + res = new ArrayList<>(); + backtrack(1, n, k, new ArrayList<>()); + return res; + } + + private void backtrack(int i, int n, int k, List comb) { + if (i > n) { + if (comb.size() == k) { + res.add(new ArrayList<>(comb)); + } + return; + } + + comb.add(i); + backtrack(i + 1, n, k, comb); + comb.remove(comb.size() - 1); + backtrack(i + 1, n, k, comb); + } +} +``` + +```cpp +class Solution { + vector> res; +public: + vector> combine(int n, int k) { + vector comb; + backtrack(1, n, k, comb); + return res; + } + +private: + void backtrack(int i, int n, int k, vector& comb) { + if (i > n) { + if (comb.size() == k) { + res.push_back(comb); + } + return; + } + + comb.push_back(i); + backtrack(i + 1, n, k, comb); + comb.pop_back(); + backtrack(i + 1, n, k, comb); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number[][]} + */ + combine(n, k) { + const res = []; + + const backtrack = (i, comb) => { + if (i > n) { + if (comb.length === k) { + res.push([...comb]); + } + return; + } + + comb.push(i); + backtrack(i + 1, comb); + comb.pop(); + backtrack(i + 1, comb); + }; + + backtrack(1, []); + return res; + } +} +``` + +```csharp +public class Solution { + public List> Combine(int n, int k) { + List> res = new List>(); + + void Backtrack(int i, List comb) { + if (i > n) { + if (comb.Count == k) { + res.Add(new List(comb)); + } + return; + } + + comb.Add(i); + Backtrack(i + 1, comb); + comb.RemoveAt(comb.Count - 1); + Backtrack(i + 1, comb); + } + + Backtrack(1, new List()); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * \frac {n!}{(n - k)! * k!})$ +* Space complexity: $O(k * \frac {n!}{(n - k)! * k!})$ for the output array. + +> Where $n$ is the number of elements and $k$ is the number of elements to be picked. + +--- + +## 2. Backtracking - II + +::tabs-start + +```python +class Solution: + def combine(self, n: int, k: int) -> List[List[int]]: + res = [] + + def backtrack(start, comb): + if len(comb) == k: + res.append(comb.copy()) + return + + for i in range(start, n + 1): + comb.append(i) + backtrack(i + 1, comb) + comb.pop() + + backtrack(1, []) + return res +``` + +```java +public class Solution { + private List> res; + + public List> combine(int n, int k) { + res = new ArrayList<>(); + backtrack(1, n, k, new ArrayList<>()); + return res; + } + + private void backtrack(int start, int n, int k, List comb) { + if (comb.size() == k) { + res.add(new ArrayList<>(comb)); + return; + } + + for (int i = start; i <= n; i++) { + comb.add(i); + backtrack(i + 1, n, k, comb); + comb.remove(comb.size() - 1); + } + } +} +``` + +```cpp +class Solution { +public: + vector> res; + + vector> combine(int n, int k) { + res.clear(); + vector comb; + backtrack(1, n, k, comb); + return res; + } + + void backtrack(int start, int n, int k, vector& comb) { + if (comb.size() == k) { + res.push_back(comb); + return; + } + + for (int i = start; i <= n; i++) { + comb.push_back(i); + backtrack(i + 1, n, k, comb); + comb.pop_back(); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number[][]} + */ + combine(n, k) { + const res = []; + + const backtrack = (start, comb) => { + if (comb.length === k) { + res.push([...comb]); + return; + } + + for (let i = start; i <= n; i++) { + comb.push(i); + backtrack(i + 1, comb); + comb.pop(); + } + }; + + backtrack(1, []); + return res; + } +} +``` + +```csharp +public class Solution { + public List> Combine(int n, int k) { + List> res = new List>(); + + void Backtrack(int start, List comb) { + if (comb.Count == k) { + res.Add(new List(comb)); + return; + } + + for (int i = start; i <= n; i++) { + comb.Add(i); + Backtrack(i + 1, comb); + comb.RemoveAt(comb.Count - 1); + } + } + + Backtrack(1, new List()); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * \frac {n!}{(n - k)! * k!})$ +* Space complexity: $O(k * \frac {n!}{(n - k)! * k!})$ for the output array. + +> Where $n$ is the number of elements and $k$ is the number of elements to be picked. + +--- + +## 3. Iteration + +::tabs-start + +```python +class Solution: + def combine(self, n: int, k: int) -> List[List[int]]: + res = [] + i = 0 + comb = [0] * k + + while i >= 0: + comb[i] += 1 + if comb[i] > n: + i -= 1 + continue + + if i == k - 1: + res.append(comb.copy()) + else: + i += 1 + comb[i] = comb[i - 1] + + return res +``` + +```java +public class Solution { + public List> combine(int n, int k) { + List> res = new ArrayList<>(); + int[] comb = new int[k]; + int i = 0; + + while (i >= 0) { + comb[i]++; + if (comb[i] > n) { + i--; + continue; + } + + if (i == k - 1) { + List current = new ArrayList<>(); + for (int num : comb) { + current.add(num); + } + res.add(current); + } else { + i++; + comb[i] = comb[i - 1]; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> combine(int n, int k) { + vector> res; + vector comb(k, 0); + int i = 0; + + while (i >= 0) { + comb[i]++; + if (comb[i] > n) { + i--; + continue; + } + + if (i == k - 1) { + res.push_back(comb); + } else { + i++; + comb[i] = comb[i - 1]; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number[][]} + */ + combine(n, k) { + const res = []; + const comb = Array(k).fill(0); + let i = 0; + + while (i >= 0) { + comb[i]++; + if (comb[i] > n) { + i--; + continue; + } + + if (i === k - 1) { + res.push([...comb]); + } else { + i++; + comb[i] = comb[i - 1]; + } + } + + return res; + } +} +``` + +```csharp +public class Solution { + public List> Combine(int n, int k) { + List> res = new List>(); + int[] comb = new int[k]; + int i = 0; + + while (i >= 0) { + comb[i]++; + if (comb[i] > n) { + i--; + continue; + } + + if (i == k - 1) { + res.Add(new List(comb)); + } else { + i++; + comb[i] = comb[i - 1]; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * \frac {n!}{(n - k)! * k!})$ +* Space complexity: $O(k * \frac {n!}{(n - k)! * k!})$ for the output array. + +> Where $n$ is the number of elements and $k$ is the number of elements to be picked. + +--- + +## 4. Bit Manipulation + +::tabs-start + +```python +class Solution: + def combine(self, n: int, k: int) -> List[List[int]]: + res = [] + for mask in range(1 << n): + comb = [] + for bit in range(n): + if mask & (1 << bit): + comb.append(bit + 1) + + if len(comb) == k: + res.append(comb) + return res +``` + +```java +public class Solution { + public List> combine(int n, int k) { + List> res = new ArrayList<>(); + for (int mask = 0; mask < (1 << n); mask++) { + List comb = new ArrayList<>(); + for (int bit = 0; bit < n; bit++) { + if ((mask & (1 << bit)) != 0) { + comb.add(bit + 1); + } + } + if (comb.size() == k) { + res.add(comb); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> combine(int n, int k) { + vector> res; + for (int mask = 0; mask < (1 << n); ++mask) { + vector comb; + for (int bit = 0; bit < n; ++bit) { + if (mask & (1 << bit)) { + comb.push_back(bit + 1); + } + } + if (comb.size() == k) { + res.push_back(comb); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number[][]} + */ + combine(n, k) { + const res = []; + for (let mask = 0; mask < (1 << n); mask++) { + if (mask.toString(2).split("1").length - 1 !== k) { + continue; + } + + const comb = []; + for (let bit = 0; bit < n; bit++) { + if (mask & (1 << bit)) { + comb.push(bit + 1); + } + } + res.push(comb); + } + return res; + } +} +``` + +```csharp +public class Solution { + public List> Combine(int n, int k) { + List> res = new List>(); + + for (int mask = 0; mask < (1 << n); mask++) { + List comb = new List(); + for (int bit = 0; bit < n; bit++) { + if ((mask & (1 << bit)) != 0) { + comb.Add(bit + 1); + } + } + if (comb.Count == k) { + res.Add(comb); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: $O(k * \frac {n!}{(n - k)! * k!})$ for the output array. + +> Where $n$ is the number of elements and $k$ is the number of elements to be picked. \ No newline at end of file diff --git a/articles/concatenated-words.md b/articles/concatenated-words.md new file mode 100644 index 000000000..0d7eaf0d4 --- /dev/null +++ b/articles/concatenated-words.md @@ -0,0 +1,587 @@ +## 1. Brute Force (Backtracking) + +::tabs-start + +```python +class Solution: + def findAllConcatenatedWordsInADict(self, words: List[str]) -> List[str]: + n = len(words) + res = [] + wordSet = set(words) + maxLen = 0 + for w in words: + maxLen = max(maxLen, len(w)) + + def dfs(concatWord, totLen): + if len(concatWord) > 1: + word = "".join(concatWord) + if word in wordSet: + res.append(word) + wordSet.remove(word) + + for i in range(len(words)): + if totLen + len(words[i]) > maxLen: + continue + concatWord.append(words[i]) + dfs(concatWord, totLen + len(words[i])) + concatWord.pop() + + dfs([], 0) + return res +``` + +```java +public class Solution { + private Set wordSet; + private int maxLen; + private List res; + private String[] words; + + public List findAllConcatenatedWordsInADict(String[] words) { + this.words = words; + this.wordSet = new HashSet<>(Arrays.asList(words)); + this.maxLen = 0; + this.res = new ArrayList<>(); + + for (String w : words) { + maxLen = Math.max(maxLen, w.length()); + } + + dfs(new ArrayList<>(), 0); + return res; + } + + private void dfs(List concatWord, int totLen) { + if (concatWord.size() > 1) { + String word = String.join("", concatWord); + if (wordSet.contains(word)) { + res.add(word); + wordSet.remove(word); + } + } + + for (String word : words) { + if (totLen + word.length() > maxLen) continue; + concatWord.add(word); + dfs(concatWord, totLen + word.length()); + concatWord.remove(concatWord.size() - 1); + } + } +} +``` + +```cpp +class Solution { +private: + unordered_set wordSet; + int maxLen; + vector res; + vector words; + +public: + vector findAllConcatenatedWordsInADict(vector& words) { + this->words = words; + wordSet = unordered_set(words.begin(), words.end()); + maxLen = 0; + res.clear(); + + for (const string& w : words) { + maxLen = max(maxLen, (int)w.length()); + } + + vector concatWord; + dfs(concatWord, 0); + return res; + } + +private: + void dfs(vector& concatWord, int totLen) { + if (concatWord.size() > 1) { + string word = accumulate(concatWord.begin(), concatWord.end(), string("")); + if (wordSet.count(word)) { + res.push_back(word); + wordSet.erase(word); + } + } + + for (const string& word : words) { + if (totLen + word.size() > maxLen) continue; + concatWord.push_back(word); + dfs(concatWord, totLen + word.length()); + concatWord.pop_back(); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + findAllConcatenatedWordsInADict(words) { + let n = words.length; + let res = []; + let wordSet = new Set(words); + let maxLen = 0; + + for (let w of words) { + maxLen = Math.max(maxLen, w.length); + } + + const dfs = (concatWord, totLen) => { + if (concatWord.length > 1) { + let word = concatWord.join(""); + if (wordSet.has(word)) { + res.push(word); + wordSet.delete(word); + } + } + + for (let i = 0; i < words.length; i++) { + if (totLen + words[i].length > maxLen) continue; + concatWord.push(words[i]); + dfs(concatWord, totLen + words[i].length); + concatWord.pop(); + } + }; + + dfs([], 0); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n ^ n)$ +* Space complexity: $O(m * n)$ + +> Where $n$ is the size of the string array $words$ and $m$ is the length of the longest word in the array. + +--- + +## 2. Recursion + +::tabs-start + +```python +class Solution: + def findAllConcatenatedWordsInADict(self, words: List[str]) -> List[str]: + wordSet = set(words) + + def dfs(word): + for i in range(1, len(word)): + prefix, suffix = word[:i], word[i:] + if ((prefix in wordSet and suffix in wordSet) or + (prefix in wordSet and dfs(suffix)) + ): + return True + return False + + res = [] + for w in words: + if dfs(w): + res.append(w) + return res +``` + +```java +public class Solution { + public List findAllConcatenatedWordsInADict(String[] words) { + Set wordSet = new HashSet<>(Arrays.asList(words)); + List res = new ArrayList<>(); + + for (String w : words) { + if (dfs(w, wordSet)) { + res.add(w); + } + } + return res; + } + + private boolean dfs(String word, Set wordSet) { + for (int i = 1; i < word.length(); i++) { + String prefix = word.substring(0, i); + String suffix = word.substring(i); + + if ((wordSet.contains(prefix) && wordSet.contains(suffix)) || + (wordSet.contains(prefix) && dfs(suffix, wordSet))) { + return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + vector findAllConcatenatedWordsInADict(vector& words) { + unordered_set wordSet(words.begin(), words.end()); + vector res; + + for (const string& w : words) { + if (dfs(w, wordSet)) { + res.push_back(w); + } + } + return res; + } + +private: + bool dfs(const string& word, unordered_set& wordSet) { + for (int i = 1; i < word.size(); i++) { + string prefix = word.substr(0, i); + string suffix = word.substr(i); + + if ((wordSet.count(prefix) && wordSet.count(suffix)) || + (wordSet.count(prefix) && dfs(suffix, wordSet))) { + return true; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + findAllConcatenatedWordsInADict(words) { + const wordSet = new Set(words); + + const dfs = (word) => { + for (let i = 1; i < word.length; i++) { + const prefix = word.substring(0, i); + const suffix = word.substring(i); + + if ((wordSet.has(prefix) && wordSet.has(suffix)) || + (wordSet.has(prefix) && dfs(suffix))) { + return true; + } + } + return false; + }; + + const res = []; + for (let w of words) { + if (dfs(w)) { + res.push(w); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m ^ 4)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the size of the string array $words$ and $m$ is the length of the longest word in the array. + +--- + +## 3. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def findAllConcatenatedWordsInADict(self, words: List[str]) -> List[str]: + wordSet = set(words) + dp = {} + + def dfs(word): + if word in dp: + return dp[word] + + for i in range(1, len(word)): + prefix, suffix = word[:i], word[i:] + if ((prefix in wordSet and suffix in wordSet) or + (prefix in wordSet and dfs(suffix)) + ): + dp[word] = True + return True + dp[word] = False + return False + + res = [] + for w in words: + if dfs(w): + res.append(w) + return res +``` + +```java +public class Solution { + private Map dp; + + public List findAllConcatenatedWordsInADict(String[] words) { + Set wordSet = new HashSet<>(Arrays.asList(words)); + List res = new ArrayList<>(); + dp = new HashMap<>(); + + for (String w : words) { + if (dfs(w, wordSet)) { + res.add(w); + } + } + return res; + } + + private boolean dfs(String word, Set wordSet) { + if (dp.containsKey(word)) { + return dp.get(word); + } + + for (int i = 1; i < word.length(); i++) { + String prefix = word.substring(0, i); + String suffix = word.substring(i); + + if ((wordSet.contains(prefix) && wordSet.contains(suffix)) || + (wordSet.contains(prefix) && dfs(suffix, wordSet))) { + dp.put(word, true); + return true; + } + } + dp.put(word, false); + return false; + } +} +``` + +```cpp +class Solution { + unordered_map dp; + +public: + vector findAllConcatenatedWordsInADict(vector& words) { + unordered_set wordSet(words.begin(), words.end()); + vector res; + + for (const string& w : words) { + if (dfs(w, wordSet)) { + res.push_back(w); + } + } + return res; + } + +private: + bool dfs(const string& word, unordered_set& wordSet) { + if (dp.count(word)) { + return dp[word]; + } + + for (int i = 1; i < word.size(); i++) { + string prefix = word.substr(0, i); + string suffix = word.substr(i); + + if ((wordSet.count(prefix) && wordSet.count(suffix)) || + (wordSet.count(prefix) && dfs(suffix, wordSet))) { + dp[word] = true; + return true; + } + } + dp[word] = false; + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + findAllConcatenatedWordsInADict(words) { + const wordSet = new Set(words); + const dp = new Map(); + + const dfs = (word) => { + if (dp.has(word)) { + return dp.get(word); + } + + for (let i = 1; i < word.length; i++) { + const prefix = word.substring(0, i); + const suffix = word.substring(i); + + if ((wordSet.has(prefix) && wordSet.has(suffix)) || + (wordSet.has(prefix) && dfs(suffix))) { + dp.set(word, true); + return true; + } + } + dp.set(word, false); + return false; + }; + + const res = []; + for (let w of words) { + if (dfs(w)) { + res.push(w); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m ^ 3)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the size of the string array $words$ and $m$ is the length of the longest word in the array. + +--- + +## 4. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def findAllConcatenatedWordsInADict(self, words: List[str]) -> List[str]: + wordSet = set(words) + res = [] + + for word in words: + m = len(word) + + dp = [False] * (m + 1) + dp[0] = True + + for i in range(1, m + 1): + for j in range(i): + if j == 0 and i == m: + continue + if dp[j] and word[j:i] in wordSet: + dp[i] = True + break + + if dp[m]: + res.append(word) + + return res +``` + +```java +public class Solution { + public List findAllConcatenatedWordsInADict(String[] words) { + Set wordSet = new HashSet<>(Arrays.asList(words)); + List res = new ArrayList<>(); + + for (String word : words) { + int m = word.length(); + boolean[] dp = new boolean[m + 1]; + dp[0] = true; + + for (int i = 1; i <= m; i++) { + for (int j = 0; j < i; j++) { + if (j == 0 && i == m) continue; + if (dp[j] && wordSet.contains(word.substring(j, i))) { + dp[i] = true; + break; + } + } + } + + if (dp[m]) { + res.add(word); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findAllConcatenatedWordsInADict(vector& words) { + unordered_set wordSet(words.begin(), words.end()); + vector res; + + for (string& word : words) { + int m = word.length(); + vector dp(m + 1, false); + dp[0] = true; + + for (int i = 1; i <= m; i++) { + for (int j = 0; j < i; j++) { + if (j == 0 && i == m) continue; + if (dp[j] && wordSet.count(word.substr(j, i - j))) { + dp[i] = true; + break; + } + } + } + + if (dp[m]) { + res.push_back(word); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + findAllConcatenatedWordsInADict(words) { + const wordSet = new Set(words); + const res = []; + + for (const word of words) { + const m = word.length; + const dp = new Array(m + 1).fill(false); + dp[0] = true; + + for (let i = 1; i <= m; i++) { + for (let j = 0; j < i; j++) { + if (j === 0 && i === m) continue; + if (dp[j] && wordSet.has(word.substring(j, i))) { + dp[i] = true; + break; + } + } + } + + if (dp[m]) { + res.push(word); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m ^ 3)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the size of the string array $words$ and $m$ is the length of the longest word in the array. \ No newline at end of file diff --git a/articles/concatenation-of-array.md b/articles/concatenation-of-array.md new file mode 100644 index 000000000..f3740ed68 --- /dev/null +++ b/articles/concatenation-of-array.md @@ -0,0 +1,163 @@ +## 1. Iteration (Two Pass) + +::tabs-start + +```python +class Solution: + def getConcatenation(self, nums: List[int]) -> List[int]: + ans = [] + for i in range(2): + for num in nums: + ans.append(num) + return ans +``` + +```java +public class Solution { + public int[] getConcatenation(int[] nums) { + int[] ans = new int[2 * nums.length]; + int idx = 0; + for (int i = 0; i < 2; i++) { + for (int num : nums) { + ans[idx++] = num; + } + } + return ans; + } +} +``` + +```cpp +class Solution { +public: + vector getConcatenation(vector& nums) { + vector ans; + for (int i = 0; i < 2; ++i) { + for (int num : nums) { + ans.push_back(num); + } + } + return ans; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + getConcatenation(nums) { + let ans = []; + for (let i = 0; i < 2; i++) { + for (let num of nums) { + ans.push(num); + } + } + return ans; + } +} +``` + +```csharp +public class Solution { + public int[] GetConcatenation(int[] nums) { + int[] ans = new int[2 * nums.Length]; + int idx = 0; + for (int i = 0; i < 2; i++) { + foreach (int num in nums) { + ans[idx++] = num; + } + } + return ans; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for the output array. + +--- + +## 2. Iteration (One Pass) + +::tabs-start + +```python +class Solution: + def getConcatenation(self, nums: List[int]) -> List[int]: + n = len(nums) + ans = [0] * (2 * n) + for i, num in enumerate(nums): + ans[i] = ans[i + n] = num + return ans +``` + +```java +public class Solution { + public int[] getConcatenation(int[] nums) { + int n = nums.length; + int[] ans = new int[2 * n]; + for (int i = 0; i < n; i++) { + ans[i] = ans[i + n] = nums[i]; + } + return ans; + } +} +``` + +```cpp +class Solution { +public: + vector getConcatenation(vector& nums) { + int n = nums.size(); + vector ans(2 * n); + for (int i = 0; i < n; ++i) { + ans[i] = ans[i + n] = nums[i]; + } + return ans; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + getConcatenation(nums) { + let n = nums.length; + let ans = new Array(2 * n); + for (let i = 0; i < n; i++) { + ans[i] = ans[i + n] = nums[i]; + } + return ans; + } +} +``` + +```csharp +public class Solution { + public int[] GetConcatenation(int[] nums) { + int n = nums.Length; + int[] ans = new int[2 * n]; + for (int i = 0; i < n; i++) { + ans[i] = ans[i + n] = nums[i]; + } + return ans; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for the output array. \ No newline at end of file diff --git a/articles/constrained-subsequence-sum.md b/articles/constrained-subsequence-sum.md new file mode 100644 index 000000000..1c04624df --- /dev/null +++ b/articles/constrained-subsequence-sum.md @@ -0,0 +1,701 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def constrainedSubsetSum(self, nums: List[int], k: int) -> int: + memo = [None] * len(nums) + + def dfs(i): + if memo[i] != None: + return memo[i] + + res = nums[i] + for j in range(i + 1, len(nums)): + if j - i > k: + break + res = max(res, nums[i] + dfs(j)) + + memo[i] = res + return res + + ans = float('-inf') + for i in range(len(nums)): + ans = max(ans, dfs(i)) + return ans +``` + +```java +public class Solution { + private int[] nums; + private Integer[] memo; + private int k; + + public int constrainedSubsetSum(int[] nums, int k) { + this.nums = nums; + this.memo = new Integer[nums.length]; + this.k = k; + + int ans = Integer.MIN_VALUE; + for (int i = 0; i < nums.length; i++) { + ans = Math.max(ans, dfs(i)); + } + return ans; + } + + private int dfs(int i) { + if (memo[i] != null) { + return memo[i]; + } + + int res = nums[i]; + for (int j = i + 1; j < nums.length && j - i <= k; j++) { + res = Math.max(res, nums[i] + dfs(j)); + } + + memo[i] = res; + return res; + } +} +``` + +```cpp +class Solution { +public: + int constrainedSubsetSum(vector& nums, int k) { + vector memo(nums.size(), INT_MIN); + int ans = INT_MIN; + for (int i = 0; i < nums.size(); i++) { + ans = max(ans, dfs(nums, memo, k, i)); + } + return ans; + } + +private: + int dfs(vector& nums, vector& memo, int k, int i) { + if (memo[i] != INT_MIN) { + return memo[i]; + } + + int res = nums[i]; + for (int j = i + 1; j < nums.size() && j - i <= k; j++) { + res = max(res, nums[i] + dfs(nums, memo, k, j)); + } + + memo[i] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + constrainedSubsetSum(nums, k) { + const memo = new Array(nums.length).fill(null); + + const dfs = (i) => { + if (memo[i] !== null) { + return memo[i]; + } + + let res = nums[i]; + for (let j = i + 1; j < nums.length && j - i <= k; j++) { + res = Math.max(res, nums[i] + dfs(j)); + } + + memo[i] = res; + return res; + }; + + let ans = -Infinity; + for (let i = 0; i < nums.length; i++) { + ans = Math.max(ans, dfs(i)); + } + return ans; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def constrainedSubsetSum(self, nums: List[int], k: int) -> int: + dp = [num for num in nums] + + for i in range(1, len(nums)): + for j in range(max(0, i - k), i): + dp[i] = max(dp[i], nums[i] + dp[j]) + + return max(dp) +``` + +```java +public class Solution { + public int constrainedSubsetSum(int[] nums, int k) { + int n = nums.length; + int[] dp = new int[n]; + System.arraycopy(nums, 0, dp, 0, n); + + for (int i = 1; i < n; i++) { + for (int j = Math.max(0, i - k); j < i; j++) { + dp[i] = Math.max(dp[i], nums[i] + dp[j]); + } + } + + int res = Integer.MIN_VALUE; + for (int val : dp) { + res = Math.max(res, val); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int constrainedSubsetSum(vector& nums, int k) { + int n = nums.size(); + vector dp(nums.begin(), nums.end()); + + for (int i = 1; i < n; i++) { + for (int j = max(0, i - k); j < i; j++) { + dp[i] = max(dp[i], nums[i] + dp[j]); + } + } + + return *max_element(dp.begin(), dp.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + constrainedSubsetSum(nums, k) { + const n = nums.length; + const dp = [...nums]; + + for (let i = 1; i < n; i++) { + for (let j = Math.max(0, i - k); j < i; j++) { + dp[i] = Math.max(dp[i], nums[i] + dp[j]); + } + } + + return Math.max(...dp); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming + Segment Tree + +::tabs-start + +```python +class SegmentTree: + def __init__(self, N): + self.n = N + while (self.n & (self.n - 1)) != 0: + self.n += 1 + self.tree = [float('-inf')] * (2 * self.n) + + def update(self, i, val): + i += self.n + self.tree[i] = val + while i > 1: + i >>= 1 + self.tree[i] = max(self.tree[i << 1], self.tree[i << 1 | 1]) + + def query(self, l, r): + res = float('-inf') + l += self.n + r += self.n + 1 + while l < r: + if l & 1: + res = max(res, self.tree[l]) + l += 1 + if r & 1: + r -= 1 + res = max(res, self.tree[r]) + l >>= 1 + r >>= 1 + return max(0, res) + +class Solution: + def constrainedSubsetSum(self, nums: List[int], k: int) -> int: + n = len(nums) + maxSegTree = SegmentTree(n) + maxSegTree.update(0, nums[0]) + res = nums[0] + + for i in range(1, n): + cur = nums[i] + maxSegTree.query(max(0, i - k), i - 1) + maxSegTree.update(i, cur) + res = max(res, cur) + + return res +``` + +```java +class SegmentTree { + int n; + int[] tree; + + public SegmentTree(int N) { + this.n = N; + while ((this.n & (this.n - 1)) != 0) { + this.n++; + } + tree = new int[2 * this.n]; + for (int i = 0; i < 2 * this.n; i++) { + tree[i] = Integer.MIN_VALUE; + } + } + + public void update(int i, int val) { + i += n; + tree[i] = val; + while (i > 1) { + i >>= 1; + tree[i] = Math.max(tree[i << 1], tree[(i << 1) | 1]); + } + } + + public int query(int l, int r) { + int res = Integer.MIN_VALUE; + l += n; + r += n + 1; + while (l < r) { + if ((l & 1) == 1) { + res = Math.max(res, tree[l]); + l++; + } + if ((r & 1) == 1) { + r--; + res = Math.max(res, tree[r]); + } + l >>= 1; + r >>= 1; + } + return Math.max(0, res); + } +} + +public class Solution { + public int constrainedSubsetSum(int[] nums, int k) { + int n = nums.length; + SegmentTree maxSegTree = new SegmentTree(n); + maxSegTree.update(0, nums[0]); + int res = nums[0]; + + for (int i = 1; i < n; i++) { + int cur = nums[i] + maxSegTree.query(Math.max(0, i - k), i - 1); + maxSegTree.update(i, cur); + res = Math.max(res, cur); + } + + return res; + } +} +``` + +```cpp +class SegmentTree { +public: + int n; + vector tree; + + SegmentTree(int N) { + n = N; + while ((n & (n - 1)) != 0) { + n++; + } + tree.assign(2 * n, INT_MIN); + } + + void update(int i, int val) { + i += n; + tree[i] = val; + while (i > 1) { + i >>= 1; + tree[i] = max(tree[i << 1], tree[i << 1 | 1]); + } + } + + int query(int l, int r) { + int res = INT_MIN; + l += n; + r += n + 1; + while (l < r) { + if (l & 1) { + res = max(res, tree[l]); + l++; + } + if (r & 1) { + r--; + res = max(res, tree[r]); + } + l >>= 1; + r >>= 1; + } + return max(0, res); + } +}; + +class Solution { +public: + int constrainedSubsetSum(vector& nums, int k) { + int n = nums.size(); + SegmentTree maxSegTree(n); + maxSegTree.update(0, nums[0]); + int res = nums[0]; + + for (int i = 1; i < n; i++) { + int cur = nums[i] + maxSegTree.query(max(0, i - k), i - 1); + maxSegTree.update(i, cur); + res = max(res, cur); + } + + return res; + } +}; +``` + +```javascript +class SegmentTree { + /** + * @constructor + * @param {number} N + */ + constructor(N) { + this.n = N; + while ((this.n & (this.n - 1)) !== 0) { + this.n++; + } + this.tree = new Array(2 * this.n).fill(-Infinity); + } + + /** + * @param {number} i + * @param {number} val + * @return {void} + */ + update(i, val) { + i += this.n; + this.tree[i] = val; + while (i > 1) { + i >>= 1; + this.tree[i] = Math.max(this.tree[i << 1], this.tree[i << 1 | 1]); + } + } + + /** + * @param {number} l + * @param {number} r + * @return {number} + */ + query(l, r) { + let res = -Infinity; + l += this.n; + r += this.n + 1; + while (l < r) { + if (l & 1) { + res = Math.max(res, this.tree[l]); + l++; + } + if (r & 1) { + r--; + res = Math.max(res, this.tree[r]); + } + l >>= 1; + r >>= 1; + } + return Math.max(0, res); + } +} + +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + constrainedSubsetSum(nums, k) { + const n = nums.length; + const maxSegTree = new SegmentTree(n); + maxSegTree.update(0, nums[0]); + let res = nums[0]; + + for (let i = 1; i < n; i++) { + let cur = nums[i] + maxSegTree.query(Math.max(0, i - k), i - 1); + maxSegTree.update(i, cur); + res = Math.max(res, cur); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Max-Heap + +::tabs-start + +```python +class Solution: + def constrainedSubsetSum(self, nums: List[int], k: int) -> int: + res = nums[0] + max_heap = [(-nums[0], 0)] # max_sum, index + + for i in range(1, len(nums)): + while i - max_heap[0][1] > k: + heapq.heappop(max_heap) + + cur_max = max(nums[i], nums[i] - max_heap[0][0]) + res = max(res, cur_max) + heapq.heappush(max_heap, (-cur_max, i)) + + return res +``` + +```java +public class Solution { + public int constrainedSubsetSum(int[] nums, int k) { + int res = nums[0]; + PriorityQueue maxHeap = new PriorityQueue<>( + (a, b) -> b[0] - a[0] // max_sum, index + ); + maxHeap.offer(new int[]{nums[0], 0}); + + for (int i = 1; i < nums.length; i++) { + while (i - maxHeap.peek()[1] > k) { + maxHeap.poll(); + } + + int curMax = Math.max(nums[i], nums[i] + maxHeap.peek()[0]); + res = Math.max(res, curMax); + maxHeap.offer(new int[]{curMax, i}); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int constrainedSubsetSum(vector& nums, int k) { + int res = nums[0]; + priority_queue> maxHeap; // max_sum, index + maxHeap.emplace(nums[0], 0); + + for (int i = 1; i < nums.size(); i++) { + while (i - maxHeap.top().second > k) { + maxHeap.pop(); + } + + int curMax = max(nums[i], nums[i] + maxHeap.top().first); + res = max(res, curMax); + maxHeap.emplace(curMax, i); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + constrainedSubsetSum(nums, k) { + let res = nums[0]; + const maxHeap = new PriorityQueue( + (a, b) => b[0] - a[0] // max_sum, index + ); + maxHeap.enqueue([nums[0], 0]); + + for (let i = 1; i < nums.length; i++) { + while (i - maxHeap.front()[1] > k) { + maxHeap.dequeue(); + } + + let curMax = Math.max(nums[i], nums[i] + maxHeap.front()[0]); + res = Math.max(res, curMax); + maxHeap.enqueue([curMax, i]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Monotonic Deque + +::tabs-start + +```python +class Solution: + def constrainedSubsetSum(self, nums: List[int], k: int) -> int: + n = len(nums) + dq = deque([(0, nums[0])]) + res = nums[0] + + for i in range(1, n): + if dq and dq[0][0] < i - k: + dq.popleft() + + cur = max(0, dq[0][1]) + nums[i] + while dq and cur > dq[-1][1]: + dq.pop() + + dq.append((i, cur)) + res = max(res, cur) + + return res +``` + +```java +public class Solution { + public int constrainedSubsetSum(int[] nums, int k) { + int n = nums.length; + Deque dq = new ArrayDeque<>(); + dq.offer(new int[]{0, nums[0]}); + int res = nums[0]; + + for (int i = 1; i < n; i++) { + if (!dq.isEmpty() && dq.peekFirst()[0] < i - k) { + dq.pollFirst(); + } + + int cur = Math.max(0, dq.peekFirst()[1]) + nums[i]; + while (!dq.isEmpty() && cur > dq.peekLast()[1]) { + dq.pollLast(); + } + + dq.offer(new int[]{i, cur}); + res = Math.max(res, cur); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int constrainedSubsetSum(vector& nums, int k) { + int n = nums.size(); + deque> dq{{0, nums[0]}}; + int res = nums[0]; + + for (int i = 1; i < n; i++) { + if (!dq.empty() && dq.front().first < i - k) { + dq.pop_front(); + } + + int cur = max(0, dq.front().second) + nums[i]; + while (!dq.empty() && cur > dq.back().second) { + dq.pop_back(); + } + + dq.emplace_back(i, cur); + res = max(res, cur); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + constrainedSubsetSum(nums, k) { + const n = nums.length; + const dq = new Deque([[0, nums[0]]]); + let res = nums[0]; + + for (let i = 1; i < n; i++) { + if (!dq.isEmpty() && dq.front()[0] < i - k) { + dq.popFront(); + } + + let cur = Math.max(0, dq.front()[1]) + nums[i]; + while (!dq.isEmpty() && cur > dq.back()[1]) { + dq.popBack(); + } + + dq.pushBack([i, cur]); + res = Math.max(res, cur); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(k)$ \ No newline at end of file diff --git a/articles/construct-binary-tree-from-inorder-and-postorder-traversal.md b/articles/construct-binary-tree-from-inorder-and-postorder-traversal.md new file mode 100644 index 000000000..eb086eacd --- /dev/null +++ b/articles/construct-binary-tree-from-inorder-and-postorder-traversal.md @@ -0,0 +1,470 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]: + if not postorder or not inorder: + return None + + root = TreeNode(postorder[-1]) + mid = inorder.index(postorder[-1]) + root.left = self.buildTree(inorder[:mid], postorder[:mid]) + root.right = self.buildTree(inorder[mid + 1:], postorder[mid:-1]) + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode buildTree(int[] inorder, int[] postorder) { + if (postorder.length == 0 || inorder.length == 0) { + return null; + } + + TreeNode root = new TreeNode(postorder[postorder.length - 1]); + int mid = 0; + for (int i = 0; i < inorder.length; i++) { + if (inorder[i] == postorder[postorder.length - 1]) { + mid = i; + break; + } + } + + root.left = buildTree( + Arrays.copyOfRange(inorder, 0, mid), + Arrays.copyOfRange(postorder, 0, mid) + ); + root.right = buildTree( + Arrays.copyOfRange(inorder, mid + 1, inorder.length), + Arrays.copyOfRange(postorder, mid, postorder.length - 1) + ); + + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* buildTree(vector& inorder, vector& postorder) { + if (postorder.empty() || inorder.empty()) { + return nullptr; + } + + TreeNode* root = new TreeNode(postorder.back()); + auto it = find(inorder.begin(), inorder.end(), postorder.back()); + int mid = distance(inorder.begin(), it); + + vector leftInorder(inorder.begin(), inorder.begin() + mid); + vector rightInorder(inorder.begin() + mid + 1, inorder.end()); + vector leftPostorder(postorder.begin(), postorder.begin() + mid); + vector rightPostorder(postorder.begin() + mid, postorder.end() - 1); + + root->left = buildTree(leftInorder, leftPostorder); + root->right = buildTree(rightInorder, rightPostorder); + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number[]} inorder + * @param {number[]} postorder + * @return {TreeNode} + */ + buildTree(inorder, postorder) { + if (postorder.length === 0 || inorder.length === 0) { + return null; + } + + const rootVal = postorder[postorder.length - 1]; + const root = new TreeNode(rootVal); + const mid = inorder.indexOf(rootVal); + + root.left = this.buildTree(inorder.slice(0, mid), postorder.slice(0, mid)); + root.right = this.buildTree(inorder.slice(mid + 1), postorder.slice(mid, postorder.length - 1)); + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Hash Map + Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]: + inorderIdx = {v: i for i, v in enumerate(inorder)} + + def dfs(l, r): + if l > r: + return None + + root = TreeNode(postorder.pop()) + idx = inorderIdx[root.val] + root.right = dfs(idx + 1, r) + root.left = dfs(l, idx - 1) + return root + + return dfs(0, len(inorder) - 1) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private HashMap inorderIdx; + private int postIdx; + + public TreeNode buildTree(int[] inorder, int[] postorder) { + inorderIdx = new HashMap<>(); + for (int i = 0; i < inorder.length; i++) { + inorderIdx.put(inorder[i], i); + } + postIdx = postorder.length - 1; + + return dfs(0, inorder.length - 1, postorder); + } + + private TreeNode dfs(int l, int r, int[] postorder) { + if (l > r) { + return null; + } + + TreeNode root = new TreeNode(postorder[postIdx--]); + int idx = inorderIdx.get(root.val); + root.right = dfs(idx + 1, r, postorder); + root.left = dfs(l, idx - 1, postorder); + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + unordered_map inorderIdx; + int postIdx; + + TreeNode* buildTree(vector& inorder, vector& postorder) { + for (int i = 0; i < inorder.size(); i++) { + inorderIdx[inorder[i]] = i; + } + postIdx = postorder.size() - 1; + + return dfs(0, inorder.size() - 1, postorder); + } + +private: + TreeNode* dfs(int l, int r, vector& postorder) { + if (l > r) { + return nullptr; + } + + TreeNode* root = new TreeNode(postorder[postIdx--]); + int idx = inorderIdx[root->val]; + root->right = dfs(idx + 1, r, postorder); + root->left = dfs(l, idx - 1, postorder); + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number[]} inorder + * @param {number[]} postorder + * @return {TreeNode} + */ + buildTree(inorder, postorder) { + const inorderIdx = new Map(); + inorder.forEach((val, idx) => inorderIdx.set(val, idx)); + let postIdx = postorder.length - 1; + + const dfs = (l, r) => { + if (l > r) return null; + + const root = new TreeNode(postorder[postIdx--]); + const idx = inorderIdx.get(root.val); + root.right = dfs(idx + 1, r); + root.left = dfs(l, idx - 1); + return root; + }; + + return dfs(0, inorder.length - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Depth First Search (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]: + postIdx = inIdx = len(postorder) - 1 + def dfs(limit): + nonlocal postIdx, inIdx + if postIdx < 0: + return None + if inorder[inIdx] == limit: + inIdx -= 1 + return None + + root = TreeNode(postorder[postIdx]) + postIdx -= 1 + root.right = dfs(root.val) + root.left = dfs(limit) + return root + return dfs(float('inf')) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private int postIdx; + private int inIdx; + + public TreeNode buildTree(int[] inorder, int[] postorder) { + postIdx = postorder.length - 1; + inIdx = inorder.length - 1; + + return dfs(postorder, inorder, Integer.MAX_VALUE); + } + + private TreeNode dfs(int[] postorder, int[] inorder, int limit) { + if (postIdx < 0) { + return null; + } + + if (inorder[inIdx] == limit) { + inIdx--; + return null; + } + + TreeNode root = new TreeNode(postorder[postIdx--]); + root.right = dfs(postorder, inorder, root.val); + root.left = dfs(postorder, inorder, limit); + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int postIdx; + int inIdx; + + TreeNode* buildTree(vector& inorder, vector& postorder) { + postIdx = postorder.size() - 1; + inIdx = inorder.size() - 1; + + return dfs(postorder, inorder, numeric_limits::max()); + } + +private: + TreeNode* dfs(vector& postorder, vector& inorder, int limit) { + if (postIdx < 0) { + return nullptr; + } + + if (inorder[inIdx] == limit) { + inIdx--; + return nullptr; + } + + TreeNode* root = new TreeNode(postorder[postIdx--]); + root->right = dfs(postorder, inorder, root->val); + root->left = dfs(postorder, inorder, limit); + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number[]} inorder + * @param {number[]} postorder + * @return {TreeNode} + */ + buildTree(inorder, postorder) { + let postIdx = postorder.length - 1; + let inIdx = inorder.length - 1; + + const dfs = (limit) => { + if (postIdx < 0) return null; + + if (inorder[inIdx] === limit) { + inIdx--; + return null; + } + + const root = new TreeNode(postorder[postIdx--]); + root.right = dfs(root.val); + root.left = dfs(limit); + return root; + }; + + return dfs(Infinity); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. \ No newline at end of file diff --git a/articles/construct-quad-tree.md b/articles/construct-quad-tree.md new file mode 100644 index 000000000..5e2b91594 --- /dev/null +++ b/articles/construct-quad-tree.md @@ -0,0 +1,945 @@ +## 1. Recursion + +::tabs-start + +```python +""" +# Definition for a QuadTree node. +class Node: + def __init__(self, val, isLeaf, topLeft, topRight, bottomLeft, bottomRight): + self.val = val + self.isLeaf = isLeaf + self.topLeft = topLeft + self.topRight = topRight + self.bottomLeft = bottomLeft + self.bottomRight = bottomRight +""" + +class Solution: + def construct(self, grid: List[List[int]]) -> 'Node': + def dfs(n, r, c): + allSame = True + for i in range(n): + for j in range(n): + if grid[r][c] != grid[r + i][c + j]: + allSame = False + break + if allSame: + return Node(grid[r][c], True) + + n = n // 2 + topleft = dfs(n, r, c) + topright = dfs(n, r, c + n) + bottomleft = dfs(n, r + n, c) + bottomright = dfs(n, r + n, c + n) + + return Node(0, False, topleft, topright, bottomleft, bottomright) + + return dfs(len(grid), 0, 0) +``` + +```java +/* +// Definition for a QuadTree node. +class Node { + public boolean val; + public boolean isLeaf; + public Node topLeft; + public Node topRight; + public Node bottomLeft; + public Node bottomRight; + + + public Node() { + this.val = false; + this.isLeaf = false; + this.topLeft = null; + this.topRight = null; + this.bottomLeft = null; + this.bottomRight = null; + } + + public Node(boolean val, boolean isLeaf) { + this.val = val; + this.isLeaf = isLeaf; + this.topLeft = null; + this.topRight = null; + this.bottomLeft = null; + this.bottomRight = null; + } + + public Node(boolean val, boolean isLeaf, Node topLeft, Node topRight, Node bottomLeft, Node bottomRight) { + this.val = val; + this.isLeaf = isLeaf; + this.topLeft = topLeft; + this.topRight = topRight; + this.bottomLeft = bottomLeft; + this.bottomRight = bottomRight; + } +} +*/ + +public class Solution { + public Node construct(int[][] grid) { + return dfs(grid, grid.length, 0, 0); + } + + private Node dfs(int[][] grid, int n, int r, int c) { + boolean allSame = true; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (grid[r][c] != grid[r + i][c + j]) { + allSame = false; + break; + } + } + } + + if (allSame) { + return new Node(grid[r][c] == 1, true); + } + + int mid = n / 2; + Node topLeft = dfs(grid, mid, r, c); + Node topRight = dfs(grid, mid, r, c + mid); + Node bottomLeft = dfs(grid, mid, r + mid, c); + Node bottomRight = dfs(grid, mid, r + mid, c + mid); + + return new Node(false, false, topLeft, topRight, bottomLeft, bottomRight); + } +} +``` + +```cpp +/* +// Definition for a QuadTree node. +class Node { +public: + bool val; + bool isLeaf; + Node* topLeft; + Node* topRight; + Node* bottomLeft; + Node* bottomRight; + + Node() { + val = false; + isLeaf = false; + topLeft = NULL; + topRight = NULL; + bottomLeft = NULL; + bottomRight = NULL; + } + + Node(bool _val, bool _isLeaf) { + val = _val; + isLeaf = _isLeaf; + topLeft = NULL; + topRight = NULL; + bottomLeft = NULL; + bottomRight = NULL; + } + + Node(bool _val, bool _isLeaf, Node* _topLeft, Node* _topRight, Node* _bottomLeft, Node* _bottomRight) { + val = _val; + isLeaf = _isLeaf; + topLeft = _topLeft; + topRight = _topRight; + bottomLeft = _bottomLeft; + bottomRight = _bottomRight; + } +}; +*/ + +class Solution { +public: + Node* construct(vector>& grid) { + return dfs(grid, grid.size(), 0, 0); + } + +private: + Node* dfs(vector>& grid, int n, int r, int c) { + bool allSame = true; + + for (int i = 0; i < n && allSame; ++i) { + for (int j = 0; j < n; ++j) { + if (grid[r][c] != grid[r + i][c + j]) { + allSame = false; + break; + } + } + } + + if (allSame) { + return new Node(grid[r][c] == 1, true); + } + + int mid = n / 2; + Node* topLeft = dfs(grid, mid, r, c); + Node* topRight = dfs(grid, mid, r, c + mid); + Node* bottomLeft = dfs(grid, mid, r + mid, c); + Node* bottomRight = dfs(grid, mid, r + mid, c + mid); + + return new Node(false, false, topLeft, topRight, bottomLeft, bottomRight); + } +}; +``` + +```javascript +/** + * // Definition for a QuadTree node. + * class Node { + * constructor(val,isLeaf,topLeft,topRight,bottomLeft,bottomRight) { + * this.val = val; + * this.isLeaf = isLeaf; + * this.topLeft = topLeft; + * this.topRight = topRight; + * this.bottomLeft = bottomLeft; + * this.bottomRight = bottomRight; + * } + * } + */ + +class Solution { + /** + * @param {number[][]} grid + * @return {Node} + */ + construct(grid) { + const dfs = (n, r, c) => { + let allSame = true; + + for (let i = 0; i < n && allSame; i++) { + for (let j = 0; j < n; j++) { + if (grid[r][c] !== grid[r + i][c + j]) { + allSame = false; + break; + } + } + } + + if (allSame) { + return new Node(grid[r][c] === 1, true); + } + + const mid = Math.floor(n / 2); + const topLeft = dfs(mid, r, c); + const topRight = dfs(mid, r, c + mid); + const bottomLeft = dfs(mid, r + mid, c); + const bottomRight = dfs(mid, r + mid, c + mid); + + return new Node(false, false, topLeft, topRight, bottomLeft, bottomRight); + }; + + return dfs(grid.length, 0, 0); + } +} +``` + +```csharp +/* +// Definition for a QuadTree node. +public class Node { + public bool val; + public bool isLeaf; + public Node topLeft; + public Node topRight; + public Node bottomLeft; + public Node bottomRight; + + public Node() { + val = false; + isLeaf = false; + topLeft = null; + topRight = null; + bottomLeft = null; + bottomRight = null; + } + + public Node(bool _val, bool _isLeaf) { + val = _val; + isLeaf = _isLeaf; + topLeft = null; + topRight = null; + bottomLeft = null; + bottomRight = null; + } + + public Node(bool _val,bool _isLeaf,Node _topLeft,Node _topRight,Node _bottomLeft,Node _bottomRight) { + val = _val; + isLeaf = _isLeaf; + topLeft = _topLeft; + topRight = _topRight; + bottomLeft = _bottomLeft; + bottomRight = _bottomRight; + } +} +*/ + +public class Solution { + public Node Construct(int[][] grid) { + return Dfs(grid, grid.Length, 0, 0); + } + + private Node Dfs(int[][] grid, int n, int r, int c) { + bool allSame = true; + for (int i = 0; i < n && allSame; i++) { + for (int j = 0; j < n && allSame; j++) { + if (grid[r][c] != grid[r + i][c + j]) { + allSame = false; + } + } + } + + if (allSame) { + return new Node(grid[r][c] == 1, true); + } + + int half = n / 2; + Node topLeft = Dfs(grid, half, r, c); + Node topRight = Dfs(grid, half, r, c + half); + Node bottomLeft = Dfs(grid, half, r + half, c); + Node bottomRight = Dfs(grid, half, r + half, c + half); + + return new Node(false, false, topLeft, topRight, bottomLeft, bottomRight); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 \log n)$ +* Space complexity: $O(\log n)$ for recursion stack. + +--- + +## 2. Recursion (Optimal) + +::tabs-start + +```python +""" +# Definition for a QuadTree node. +class Node: + def __init__(self, val, isLeaf, topLeft, topRight, bottomLeft, bottomRight): + self.val = val + self.isLeaf = isLeaf + self.topLeft = topLeft + self.topRight = topRight + self.bottomLeft = bottomLeft + self.bottomRight = bottomRight +""" + +class Solution: + def construct(self, grid: List[List[int]]) -> 'Node': + def dfs(n, r, c): + if n == 1: + return Node(grid[r][c] == 1, True) + + mid = n // 2 + topLeft = dfs(mid, r, c) + topRight = dfs(mid, r, c + mid) + bottomLeft = dfs(mid, r + mid, c) + bottomRight = dfs(mid, r + mid, c + mid) + + if (topLeft.isLeaf and topRight.isLeaf and + bottomLeft.isLeaf and bottomRight.isLeaf and + topLeft.val == topRight.val == bottomLeft.val == bottomRight.val): + return Node(topLeft.val, True) + + return Node(False, False, topLeft, topRight, bottomLeft, bottomRight) + + return dfs(len(grid), 0, 0) +``` + +```java +/* +// Definition for a QuadTree node. +class Node { + public boolean val; + public boolean isLeaf; + public Node topLeft; + public Node topRight; + public Node bottomLeft; + public Node bottomRight; + + + public Node() { + this.val = false; + this.isLeaf = false; + this.topLeft = null; + this.topRight = null; + this.bottomLeft = null; + this.bottomRight = null; + } + + public Node(boolean val, boolean isLeaf) { + this.val = val; + this.isLeaf = isLeaf; + this.topLeft = null; + this.topRight = null; + this.bottomLeft = null; + this.bottomRight = null; + } + + public Node(boolean val, boolean isLeaf, Node topLeft, Node topRight, Node bottomLeft, Node bottomRight) { + this.val = val; + this.isLeaf = isLeaf; + this.topLeft = topLeft; + this.topRight = topRight; + this.bottomLeft = bottomLeft; + this.bottomRight = bottomRight; + } +} +*/ + +public class Solution { + public Node construct(int[][] grid) { + return dfs(grid, grid.length, 0, 0); + } + + private Node dfs(int[][] grid, int n, int r, int c) { + if (n == 1) { + return new Node(grid[r][c] == 1, true); + } + + int mid = n / 2; + Node topLeft = dfs(grid, mid, r, c); + Node topRight = dfs(grid, mid, r, c + mid); + Node bottomLeft = dfs(grid, mid, r + mid, c); + Node bottomRight = dfs(grid, mid, r + mid, c + mid); + + if (topLeft.isLeaf && topRight.isLeaf && + bottomLeft.isLeaf && bottomRight.isLeaf && + topLeft.val == topRight.val && + topLeft.val == bottomLeft.val && + topLeft.val == bottomRight.val) { + return new Node(topLeft.val, true); + } + + return new Node(false, false, topLeft, topRight, bottomLeft, bottomRight); + } +} +``` + +```cpp +/* +// Definition for a QuadTree node. +class Node { +public: + bool val; + bool isLeaf; + Node* topLeft; + Node* topRight; + Node* bottomLeft; + Node* bottomRight; + + Node() { + val = false; + isLeaf = false; + topLeft = NULL; + topRight = NULL; + bottomLeft = NULL; + bottomRight = NULL; + } + + Node(bool _val, bool _isLeaf) { + val = _val; + isLeaf = _isLeaf; + topLeft = NULL; + topRight = NULL; + bottomLeft = NULL; + bottomRight = NULL; + } + + Node(bool _val, bool _isLeaf, Node* _topLeft, Node* _topRight, Node* _bottomLeft, Node* _bottomRight) { + val = _val; + isLeaf = _isLeaf; + topLeft = _topLeft; + topRight = _topRight; + bottomLeft = _bottomLeft; + bottomRight = _bottomRight; + } +}; +*/ + +class Solution { +public: + Node* construct(vector>& grid) { + return dfs(grid, grid.size(), 0, 0); + } + +private: + Node* dfs(vector>& grid, int n, int r, int c) { + if (n == 1) { + return new Node(grid[r][c] == 1, true); + } + + int mid = n / 2; + Node* topLeft = dfs(grid, mid, r, c); + Node* topRight = dfs(grid, mid, r, c + mid); + Node* bottomLeft = dfs(grid, mid, r + mid, c); + Node* bottomRight = dfs(grid, mid, r + mid, c + mid); + + if (topLeft->isLeaf && topRight->isLeaf && + bottomLeft->isLeaf && bottomRight->isLeaf && + topLeft->val == topRight->val && + topLeft->val == bottomLeft->val && + topLeft->val == bottomRight->val) { + return new Node(topLeft->val, true); + } + + return new Node(false, false, topLeft, topRight, bottomLeft, bottomRight); + } +}; +``` + +```javascript +/** + * // Definition for a QuadTree node. + * class Node { + * constructor(val,isLeaf,topLeft,topRight,bottomLeft,bottomRight) { + * this.val = val; + * this.isLeaf = isLeaf; + * this.topLeft = topLeft; + * this.topRight = topRight; + * this.bottomLeft = bottomLeft; + * this.bottomRight = bottomRight; + * } + * } + */ + +class Solution { + /** + * @param {number[][]} grid + * @return {Node} + */ + construct(grid) { + const dfs = (n, r, c) => { + if (n === 1) { + return new Node(grid[r][c] === 1, true); + } + + const mid = Math.floor(n / 2); + const topLeft = dfs(mid, r, c); + const topRight = dfs(mid, r, c + mid); + const bottomLeft = dfs(mid, r + mid, c); + const bottomRight = dfs(mid, r + mid, c + mid); + + if (topLeft.isLeaf && topRight.isLeaf && + bottomLeft.isLeaf && bottomRight.isLeaf && + topLeft.val === topRight.val && + topLeft.val === bottomLeft.val && + topLeft.val === bottomRight.val) { + return new Node(topLeft.val, true); + } + + return new Node(false, false, topLeft, topRight, bottomLeft, bottomRight); + }; + + return dfs(grid.length, 0, 0); + } +} +``` + +```csharp +/* +// Definition for a QuadTree node. +public class Node { + public bool val; + public bool isLeaf; + public Node topLeft; + public Node topRight; + public Node bottomLeft; + public Node bottomRight; + + public Node() { + val = false; + isLeaf = false; + topLeft = null; + topRight = null; + bottomLeft = null; + bottomRight = null; + } + + public Node(bool _val, bool _isLeaf) { + val = _val; + isLeaf = _isLeaf; + topLeft = null; + topRight = null; + bottomLeft = null; + bottomRight = null; + } + + public Node(bool _val,bool _isLeaf,Node _topLeft,Node _topRight,Node _bottomLeft,Node _bottomRight) { + val = _val; + isLeaf = _isLeaf; + topLeft = _topLeft; + topRight = _topRight; + bottomLeft = _bottomLeft; + bottomRight = _bottomRight; + } +} +*/ + +public class Solution { + public Node Construct(int[][] grid) { + return Dfs(grid, grid.Length, 0, 0); + } + + private Node Dfs(int[][] grid, int n, int r, int c) { + if (n == 1) { + return new Node(grid[r][c] == 1, true); + } + + int mid = n / 2; + Node topLeft = Dfs(grid, mid, r, c); + Node topRight = Dfs(grid, mid, r, c + mid); + Node bottomLeft = Dfs(grid, mid, r + mid, c); + Node bottomRight = Dfs(grid, mid, r + mid, c + mid); + + if (topLeft.isLeaf && topRight.isLeaf && + bottomLeft.isLeaf && bottomRight.isLeaf && + topLeft.val == topRight.val && + topRight.val == bottomLeft.val && + bottomLeft.val == bottomRight.val) { + return new Node(topLeft.val, true); + } + + return new Node(false, false, topLeft, topRight, bottomLeft, bottomRight); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(\log n)$ for recursion stack. + +--- + +## 3. Recursion (Space Optimized) + +::tabs-start + +```python +""" +# Definition for a QuadTree node. +class Node: + def __init__(self, val, isLeaf, topLeft, topRight, bottomLeft, bottomRight): + self.val = val + self.isLeaf = isLeaf + self.topLeft = topLeft + self.topRight = topRight + self.bottomLeft = bottomLeft + self.bottomRight = bottomRight +""" + +class Solution: + def construct(self, grid: List[List[int]]) -> 'Node': + leafNodes = { + 0: Node(False, True), + 1: Node(True, True) + } + + def dfs(n, r, c): + if n == 1: + return leafNodes[grid[r][c]] + + n //= 2 + topLeft = dfs(n, r, c) + topRight = dfs(n, r, c + n) + bottomLeft = dfs(n, r + n, c) + bottomRight = dfs(n, r + n, c + n) + + if (topLeft.isLeaf and topRight.isLeaf and + bottomLeft.isLeaf and bottomRight.isLeaf and + topLeft.val == topRight.val == bottomLeft.val == bottomRight.val): + return topLeft + + return Node(False, False, topLeft, topRight, bottomLeft, bottomRight) + + return dfs(len(grid), 0, 0) +``` + +```java +/* +// Definition for a QuadTree node. +class Node { + public boolean val; + public boolean isLeaf; + public Node topLeft; + public Node topRight; + public Node bottomLeft; + public Node bottomRight; + + + public Node() { + this.val = false; + this.isLeaf = false; + this.topLeft = null; + this.topRight = null; + this.bottomLeft = null; + this.bottomRight = null; + } + + public Node(boolean val, boolean isLeaf) { + this.val = val; + this.isLeaf = isLeaf; + this.topLeft = null; + this.topRight = null; + this.bottomLeft = null; + this.bottomRight = null; + } + + public Node(boolean val, boolean isLeaf, Node topLeft, Node topRight, Node bottomLeft, Node bottomRight) { + this.val = val; + this.isLeaf = isLeaf; + this.topLeft = topLeft; + this.topRight = topRight; + this.bottomLeft = bottomLeft; + this.bottomRight = bottomRight; + } +} +*/ + +public class Solution { + private static final Node falseLeaf = new Node(false, true); + private static final Node trueLeaf = new Node(true, true); + + public Node construct(int[][] grid) { + return dfs(grid, grid.length, 0, 0); + } + + private Node dfs(int[][] grid, int n, int r, int c) { + if (n == 1) { + return grid[r][c] == 1 ? trueLeaf : falseLeaf; + } + + n /= 2; + Node topLeft = dfs(grid, n, r, c); + Node topRight = dfs(grid, n, r, c + n); + Node bottomLeft = dfs(grid, n, r + n, c); + Node bottomRight = dfs(grid, n, r + n, c + n); + + if (topLeft.isLeaf && topRight.isLeaf && + bottomLeft.isLeaf && bottomRight.isLeaf && + topLeft.val == topRight.val && topLeft.val == bottomLeft.val && + topLeft.val == bottomRight.val) { + return topLeft; + } + + return new Node(false, false, topLeft, topRight, bottomLeft, bottomRight); + } +} +``` + +```cpp +/* +// Definition for a QuadTree node. +class Node { +public: + bool val; + bool isLeaf; + Node* topLeft; + Node* topRight; + Node* bottomLeft; + Node* bottomRight; + + Node() { + val = false; + isLeaf = false; + topLeft = NULL; + topRight = NULL; + bottomLeft = NULL; + bottomRight = NULL; + } + + Node(bool _val, bool _isLeaf) { + val = _val; + isLeaf = _isLeaf; + topLeft = NULL; + topRight = NULL; + bottomLeft = NULL; + bottomRight = NULL; + } + + Node(bool _val, bool _isLeaf, Node* _topLeft, Node* _topRight, Node* _bottomLeft, Node* _bottomRight) { + val = _val; + isLeaf = _isLeaf; + topLeft = _topLeft; + topRight = _topRight; + bottomLeft = _bottomLeft; + bottomRight = _bottomRight; + } +}; +*/ + +class Solution { +private: + Node* falseLeaf = new Node(false, true); + Node* trueLeaf = new Node(true, true); + +public: + Node* construct(vector>& grid) { + return dfs(grid, grid.size(), 0, 0); + } + +private: + Node* dfs(vector>& grid, int n, int r, int c) { + if (n == 1) { + return grid[r][c] == 1 ? trueLeaf : falseLeaf; + } + + n /= 2; + Node* topLeft = dfs(grid, n, r, c); + Node* topRight = dfs(grid, n, r, c + n); + Node* bottomLeft = dfs(grid, n, r + n, c); + Node* bottomRight = dfs(grid, n, r + n, c + n); + + if (topLeft->isLeaf && topRight->isLeaf && + bottomLeft->isLeaf && bottomRight->isLeaf && + topLeft->val == topRight->val && topLeft->val == bottomLeft->val && + topLeft->val == bottomRight->val) { + return topLeft; + } + + return new Node(false, false, topLeft, topRight, bottomLeft, bottomRight); + } +}; +``` + +```javascript +/** + * // Definition for a QuadTree node. + * class Node { + * constructor(val,isLeaf,topLeft,topRight,bottomLeft,bottomRight) { + * this.val = val; + * this.isLeaf = isLeaf; + * this.topLeft = topLeft; + * this.topRight = topRight; + * this.bottomLeft = bottomLeft; + * this.bottomRight = bottomRight; + * } + * } + */ + +class Solution { + /** + * @param {number[][]} grid + * @return {Node} + */ + construct(grid) { + const falseLeaf = new Node(false, true); + const trueLeaf = new Node(true, true); + + const dfs = (n, r, c) => { + if (n === 1) { + return grid[r][c] === 1 ? trueLeaf : falseLeaf; + } + + n = Math.floor(n / 2); + const topLeft = dfs(n, r, c); + const topRight = dfs(n, r, c + n); + const bottomLeft = dfs(n, r + n, c); + const bottomRight = dfs(n, r + n, c + n); + + if (topLeft.isLeaf && topRight.isLeaf && + bottomLeft.isLeaf && bottomRight.isLeaf && + topLeft.val === topRight.val && topLeft.val === bottomLeft.val && + topLeft.val === bottomRight.val) { + return topLeft; + } + + return new Node(false, false, topLeft, topRight, bottomLeft, bottomRight); + }; + + return dfs(grid.length, 0, 0); + } +} +``` + +```csharp +/* +// Definition for a QuadTree node. +public class Node { + public bool val; + public bool isLeaf; + public Node topLeft; + public Node topRight; + public Node bottomLeft; + public Node bottomRight; + + public Node() { + val = false; + isLeaf = false; + topLeft = null; + topRight = null; + bottomLeft = null; + bottomRight = null; + } + + public Node(bool _val, bool _isLeaf) { + val = _val; + isLeaf = _isLeaf; + topLeft = null; + topRight = null; + bottomLeft = null; + bottomRight = null; + } + + public Node(bool _val,bool _isLeaf,Node _topLeft,Node _topRight,Node _bottomLeft,Node _bottomRight) { + val = _val; + isLeaf = _isLeaf; + topLeft = _topLeft; + topRight = _topRight; + bottomLeft = _bottomLeft; + bottomRight = _bottomRight; + } +} +*/ + +public class Solution { + private readonly Node falseLeaf = new Node(false, true); + private readonly Node trueLeaf = new Node(true, true); + + public Node Construct(int[][] grid) { + int n = grid.Length; + return Dfs(grid, n, 0, 0); + } + + private Node Dfs(int[][] grid, int n, int r, int c) { + if (n == 1) { + return grid[r][c] == 1 ? trueLeaf : falseLeaf; + } + + int half = n / 2; + Node topLeft = Dfs(grid, half, r, c); + Node topRight = Dfs(grid, half, r, c + half); + Node bottomLeft = Dfs(grid, half, r + half, c); + Node bottomRight = Dfs(grid, half, r + half, c + half); + + if (topLeft.isLeaf && topRight.isLeaf && + bottomLeft.isLeaf && bottomRight.isLeaf && + topLeft.val == topRight.val && + topLeft.val == bottomLeft.val && + topLeft.val == bottomRight.val) { + return topLeft; + } + + return new Node(false, false, topLeft, topRight, bottomLeft, bottomRight); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(\log n)$ for recursion stack. \ No newline at end of file diff --git a/articles/construct-string-from-binary-tree.md b/articles/construct-string-from-binary-tree.md new file mode 100644 index 000000000..98b9da146 --- /dev/null +++ b/articles/construct-string-from-binary-tree.md @@ -0,0 +1,518 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def tree2str(self, root: Optional[TreeNode]) -> str: + if not root: + return "" + + cur = root.val + left = self.tree2str(root.left) + right = self.tree2str(root.right) + + if left and right: + return f"{cur}({left})({right})" + + if right: + return f"{cur}()({right})" + + if left: + return f"{cur}({left})" + + return str(cur) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public String tree2str(TreeNode root) { + if (root == null) { + return ""; + } + + String cur = Integer.toString(root.val); + String left = tree2str(root.left); + String right = tree2str(root.right); + + if (!left.isEmpty() && !right.isEmpty()) { + return cur + "(" + left + ")" + "(" + right + ")"; + } + + if (!right.isEmpty()) { + return cur + "()" + "(" + right + ")"; + } + + if (!left.isEmpty()) { + return cur + "(" + left + ")"; + } + + return cur; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + string tree2str(TreeNode* root) { + if (!root) { + return ""; + } + + string cur = to_string(root->val); + string left = tree2str(root->left); + string right = tree2str(root->right); + + if (!left.empty() && !right.empty()) { + return cur + "(" + left + ")(" + right + ")"; + } + + if (!right.empty()) { + return cur + "()(" + right + ")"; + } + + if (!left.empty()) { + return cur + "(" + left + ")"; + } + + return cur; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {string} + */ + tree2str(root) { + if (!root) { + return ""; + } + + const cur = root.val.toString(); + const left = this.tree2str(root.left); + const right = this.tree2str(root.right); + + if (left && right) { + return `${cur}(${left})(${right})`; + } + + if (right) { + return `${cur}()(${right})`; + } + + if (left) { + return `${cur}(${left})`; + } + + return cur; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Depth First Search (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def tree2str(self, root: Optional[TreeNode]) -> str: + res = [] + + def preorder(root): + if not root: + return + res.append("(") + res.append(str(root.val)) + if not root.left and root.right: + res.append("()") + preorder(root.left) + preorder(root.right) + res.append(")") + + preorder(root) + return "".join(res)[1:-1] +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public String tree2str(TreeNode root) { + StringBuilder res = new StringBuilder(); + + preorder(root, res); + return res.substring(1, res.length() - 1); + } + + private void preorder(TreeNode root, StringBuilder res) { + if (root == null) return; + + res.append("(").append(root.val); + if (root.left == null && root.right != null) { + res.append("()"); + } + preorder(root.left, res); + preorder(root.right, res); + res.append(")"); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + string tree2str(TreeNode* root) { + string res; + preorder(root, res); + return res.substr(1, res.size() - 2); + } + +private: + void preorder(TreeNode* root, string& res) { + if (!root) return; + + res += "(" + to_string(root->val); + if (!root->left && root->right) { + res += "()"; + } + preorder(root->left, res); + preorder(root->right, res); + res += ")"; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {string} + */ + tree2str(root) { + let res = []; + + const preorder = (root) => { + if (!root) return; + + res.push("("); + res.push(root.val.toString()); + if (!root.left && root.right) { + res.push("()"); + } + preorder(root.left); + preorder(root.right); + res.push(")"); + }; + + preorder(root); + return res.join("").slice(1, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def tree2str(self, root: Optional[TreeNode]) -> str: + if not root: + return "" + + res = [] + stack = [] + last_visited = None + cur = root + + while cur or stack: + if cur: + res.append(f"({cur.val}") + if not cur.left and cur.right: + res.append("()") + + stack.append(cur) + cur = cur.left + else: + top = stack[-1] + if top.right and last_visited != top.right: + cur = top.right + else: + stack.pop() + res.append(")") + last_visited = top + + return "".join(res)[1:-1] +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public String tree2str(TreeNode root) { + if (root == null) { + return ""; + } + + StringBuilder res = new StringBuilder(); + Stack stack = new Stack<>(); + TreeNode lastVisited = null; + TreeNode cur = root; + + while (cur != null || !stack.isEmpty()) { + if (cur != null) { + res.append("(").append(cur.val); + if (cur.left == null && cur.right != null) { + res.append("()"); + } + + stack.push(cur); + cur = cur.left; + } else { + TreeNode top = stack.peek(); + if (top.right != null && lastVisited != top.right) { + cur = top.right; + } else { + stack.pop(); + res.append(")"); + lastVisited = top; + } + } + } + + return res.substring(1, res.length() - 1); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + string tree2str(TreeNode* root) { + if (!root) { + return ""; + } + + string res; + stack stack; + TreeNode* lastVisited = nullptr; + TreeNode* cur = root; + + while (cur || !stack.empty()) { + if (cur) { + res += "(" + to_string(cur->val); + if (!cur->left && cur->right) { + res += "()"; + } + + stack.push(cur); + cur = cur->left; + } else { + TreeNode* top = stack.top(); + if (top->right && lastVisited != top->right) { + cur = top->right; + } else { + stack.pop(); + res += ")"; + lastVisited = top; + } + } + } + + return res.substr(1, res.size() - 2); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {string} + */ + tree2str(root) { + if (!root) { + return ""; + } + + let res = []; + let stack = []; + let lastVisited = null; + let cur = root; + + while (cur || stack.length > 0) { + if (cur) { + res.push(`(${cur.val}`); + if (!cur.left && cur.right) { + res.push("()"); + } + + stack.push(cur); + cur = cur.left; + } else { + let top = stack[stack.length - 1]; + if (top.right && lastVisited !== top.right) { + cur = top.right; + } else { + stack.pop(); + res.push(")"); + lastVisited = top; + } + } + } + + return res.join("").slice(1, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/contains-duplicate-ii.md b/articles/contains-duplicate-ii.md new file mode 100644 index 000000000..cc98d9da1 --- /dev/null +++ b/articles/contains-duplicate-ii.md @@ -0,0 +1,313 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool: + for L in range(len(nums)): + for R in range(L + 1, min(len(nums), L + k + 1)): + if nums[L] == nums[R]: + return True + return False +``` + +```java +public class Solution { + public boolean containsNearbyDuplicate(int[] nums, int k) { + for (int L = 0; L < nums.length; L++) { + for (int R = L + 1; R < Math.min(nums.length, L + k + 1); R++) { + if (nums[L] == nums[R]) { + return true; + } + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool containsNearbyDuplicate(vector& nums, int k) { + for (int L = 0; L < nums.size(); L++) { + for (int R = L + 1; R < min((int)nums.size(), L + k + 1); R++) { + if (nums[L] == nums[R]) { + return true; + } + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {boolean} + */ + containsNearbyDuplicate(nums, k) { + for (let L = 0; L < nums.length; L++) { + for (let R = L + 1; R < Math.min(nums.length, L + k + 1); R++) { + if (nums[L] === nums[R]) { + return true; + } + } + } + return false; + } +} +``` + +```csharp +public class Solution { + public bool ContainsNearbyDuplicate(int[] nums, int k) { + for (int L = 0; L < nums.Length; L++) { + for (int R = L + 1; R < Math.Min(nums.Length, L + k + 1); R++) { + if (nums[L] == nums[R]) { + return true; + } + } + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * min(n, k))$ +* Space complexity: $O(1)$ + +> Where $n$ is the size of the array $nums$ and $k$ is the maximum distance between two equal numbers. + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool: + mp = {} + + for i in range(len(nums)): + if nums[i] in mp and i - mp[nums[i]] <= k: + return True + mp[nums[i]] = i + + return False +``` + +```java +public class Solution { + public boolean containsNearbyDuplicate(int[] nums, int k) { + Map map = new HashMap<>(); + + for (int i = 0; i < nums.length; i++) { + if (map.containsKey(nums[i]) && i - map.get(nums[i]) <= k) { + return true; + } + map.put(nums[i], i); + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool containsNearbyDuplicate(vector& nums, int k) { + unordered_map mp; + + for (int i = 0; i < nums.size(); i++) { + if (mp.find(nums[i]) != mp.end() && i - mp[nums[i]] <= k) { + return true; + } + mp[nums[i]] = i; + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {boolean} + */ + containsNearbyDuplicate(nums, k) { + const map = new Map(); + + for (let i = 0; i < nums.length; i++) { + if (map.has(nums[i]) && i - map.get(nums[i]) <= k) { + return true; + } + map.set(nums[i], i); + } + + return false; + } +} +``` + +```csharp +public class Solution { + public bool ContainsNearbyDuplicate(int[] nums, int k) { + Dictionary mp = new Dictionary(); + + for (int i = 0; i < nums.Length; i++) { + if (mp.ContainsKey(nums[i]) && i - mp[nums[i]] <= k) { + return true; + } + mp[nums[i]] = i; + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums$ and $k$ is the maximum distance between two equal numbers. + +--- + +## 3. Hash Set + +::tabs-start + +```python +class Solution: + def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool: + window = set() + L = 0 + + for R in range(len(nums)): + if R - L > k: + window.remove(nums[L]) + L += 1 + if nums[R] in window: + return True + window.add(nums[R]) + + return False +``` + +```java +public class Solution { + public boolean containsNearbyDuplicate(int[] nums, int k) { + Set window = new HashSet<>(); + int L = 0; + + for (int R = 0; R < nums.length; R++) { + if (R - L > k) { + window.remove(nums[L]); + L++; + } + if (window.contains(nums[R])) { + return true; + } + window.add(nums[R]); + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool containsNearbyDuplicate(vector& nums, int k) { + unordered_set window; + int L = 0; + + for (int R = 0; R < nums.size(); R++) { + if (R - L > k) { + window.erase(nums[L]); + L++; + } + if (window.find(nums[R]) != window.end()) { + return true; + } + window.insert(nums[R]); + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {boolean} + */ + containsNearbyDuplicate(nums, k) { + let window = new Set(); + let L = 0; + + for (let R = 0; R < nums.length; R++) { + if (R - L > k) { + window.delete(nums[L]); + L++; + } + if (window.has(nums[R])) { + return true; + } + window.add(nums[R]); + } + return false; + } +} +``` + +```csharp +public class Solution { + public bool ContainsNearbyDuplicate(int[] nums, int k) { + HashSet window = new HashSet(); + int L = 0; + + for (int R = 0; R < nums.Length; R++) { + if (R - L > k) { + window.Remove(nums[L]); + L++; + } + if (window.Contains(nums[R])) { + return true; + } + window.Add(nums[R]); + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(min(n, k))$ + +> Where $n$ is the size of the array $nums$ and $k$ is the maximum distance between two equal numbers. \ No newline at end of file diff --git a/articles/contiguous-array.md b/articles/contiguous-array.md new file mode 100644 index 000000000..f13825744 --- /dev/null +++ b/articles/contiguous-array.md @@ -0,0 +1,341 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def findMaxLength(self, nums: List[int]) -> int: + n, res = len(nums), 0 + + for i in range(n): + zeros = ones = 0 + for j in range(i, n): + if nums[j] == 1: + ones += 1 + else: + zeros += 1 + + if ones == zeros and res < (j - i + 1): + res = j - i + 1 + + return res +``` + +```java +public class Solution { + public int findMaxLength(int[] nums) { + int n = nums.length, res = 0; + + for (int i = 0; i < n; i++) { + int zeros = 0, ones = 0; + for (int j = i; j < n; j++) { + if (nums[j] == 1) { + ones++; + } else { + zeros++; + } + if (ones == zeros && res < (j - i + 1)) { + res = j - i + 1; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int findMaxLength(vector& nums) { + int n = nums.size(), res = 0; + + for (int i = 0; i < n; i++) { + int zeros = 0, ones = 0; + for (int j = i; j < n; j++) { + if (nums[j] == 1) { + ones++; + } else { + zeros++; + } + if (ones == zeros && res < (j - i + 1)) { + res = j - i + 1; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findMaxLength(nums) { + let n = nums.length, res = 0; + + for (let i = 0; i < n; i++) { + let zeros = 0, ones = 0; + for (let j = i; j < n; j++) { + if (nums[j] === 1) { + ones++; + } else { + zeros++; + } + if (ones === zeros && res < (j - i + 1)) { + res = j - i + 1; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Array + +::tabs-start + +```python +class Solution: + def findMaxLength(self, nums: List[int]) -> int: + n = len(nums) + res = 0 + diff_index = [None] * (2 * n + 1) + count = 0 + + for i in range(n): + count += 1 if nums[i] == 1 else -1 + if count == 0: + res = i + 1 + if diff_index[count + n] is not None: + res = max(res, i - diff_index[count + n]) + else: + diff_index[count + n] = i + + return res +``` + +```java +public class Solution { + public int findMaxLength(int[] nums) { + int n = nums.length, res = 0, count = 0; + int[] diffIndex = new int[2 * n + 1]; + Arrays.fill(diffIndex, -2); + diffIndex[n] = -1; + + for (int i = 0; i < n; i++) { + count += nums[i] == 1 ? 1 : -1; + if (diffIndex[count + n] != -2) { + res = Math.max(res, i - diffIndex[count + n]); + } else { + diffIndex[count + n] = i; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int findMaxLength(vector& nums) { + int n = nums.size(), res = 0, count = 0; + vector diffIndex(2 * n + 1, -2); + diffIndex[n] = -1; + + for (int i = 0; i < n; i++) { + count += nums[i] == 1 ? 1 : -1; + if (diffIndex[count + n] != -2) { + res = max(res, i - diffIndex[count + n]); + } else { + diffIndex[count + n] = i; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findMaxLength(nums) { + const n = nums.length; + let res = 0, count = 0; + const diffIndex = new Array(2 * n + 1).fill(-2); + diffIndex[n] = -1; + + for (let i = 0; i < n; i++) { + count += nums[i] === 1 ? 1 : -1; + if (diffIndex[count + n] !== -2) { + res = Math.max(res, i - diffIndex[count + n]); + } else { + diffIndex[count + n] = i; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Hash Map + +::tabs-start + +```python +class Solution: + def findMaxLength(self, nums: List[int]) -> int: + zero = one = res = 0 + diff_index = {} + + for i, n in enumerate(nums): + if n == 0: + zero += 1 + else: + one += 1 + + if one - zero not in diff_index: + diff_index[one - zero] = i + + if one == zero: + res = one + zero + else: + idx = diff_index[one - zero] + res = max(res, i - idx) + + return res +``` + +```java +public class Solution { + public int findMaxLength(int[] nums) { + int zero = 0, one = 0, res = 0; + HashMap diffIndex = new HashMap<>(); + + for (int i = 0; i < nums.length; i++) { + if (nums[i] == 0) { + zero++; + } else { + one++; + } + + int diff = one - zero; + if (!diffIndex.containsKey(diff)) { + diffIndex.put(diff, i); + } + + if (one == zero) { + res = one + zero; + } else { + res = Math.max(res, i - diffIndex.get(diff)); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int findMaxLength(vector& nums) { + int zero = 0, one = 0, res = 0; + unordered_map diffIndex; + + for (int i = 0; i < nums.size(); i++) { + if (nums[i] == 0) { + zero++; + } else { + one++; + } + + int diff = one - zero; + if (diffIndex.find(diff) == diffIndex.end()) { + diffIndex[diff] = i; + } + + if (one == zero) { + res = one + zero; + } else { + res = max(res, i - diffIndex[diff]); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findMaxLength(nums) { + let zero = 0, one = 0, res = 0; + const diffIndex = new Map(); + + for (let i = 0; i < nums.length; i++) { + if (nums[i] === 0) { + zero++; + } else { + one++; + } + + const diff = one - zero; + if (!diffIndex.has(diff)) { + diffIndex.set(diff, i); + } + + if (one === zero) { + res = one + zero; + } else { + res = Math.max(res, i - diffIndex.get(diff)); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/continuous-subarray-sum.md b/articles/continuous-subarray-sum.md new file mode 100644 index 000000000..a6509f498 --- /dev/null +++ b/articles/continuous-subarray-sum.md @@ -0,0 +1,177 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def checkSubarraySum(self, nums: List[int], k: int) -> bool: + for i in range(len(nums) - 1): + sum = nums[i] + for j in range(i + 1, len(nums)): + sum += nums[j] + if sum % k == 0: + return True + return False +``` + +```java +public class Solution { + public boolean checkSubarraySum(int[] nums, int k) { + for (int i = 0; i < nums.length - 1; i++) { + int sum = nums[i]; + for (int j = i + 1; j < nums.length; j++) { + sum += nums[j]; + if (sum % k == 0) return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool checkSubarraySum(vector& nums, int k) { + for (int i = 0; i < nums.size() - 1; i++) { + int sum = nums[i]; + for (int j = i + 1; j < nums.size(); j++) { + sum += nums[j]; + if (sum % k == 0) return true; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {boolean} + */ + checkSubarraySum(nums, k) { + for (let i = 0; i < nums.length - 1; i++) { + let sum = nums[i]; + for (let j = i + 1; j < nums.length; j++) { + sum += nums[j]; + if (sum % k == 0) return true; + } + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Prefix SUm + Hash Map + +::tabs-start + +```python +class Solution: + def checkSubarraySum(self, nums: List[int], k: int) -> bool: + remainder = {0: -1} # remainder -> end index + total = 0 + + for i, num in enumerate(nums): + total += num + r = total % k + if r not in remainder: + remainder[r] = i + elif i - remainder[r] > 1: + return True + + return False +``` + +```java +public class Solution { + public boolean checkSubarraySum(int[] nums, int k) { + HashMap remainder = new HashMap<>(); + remainder.put(0, -1); + int total = 0; + + for (int i = 0; i < nums.length; i++) { + total += nums[i]; + int r = total % k; + if (!remainder.containsKey(r)) { + remainder.put(r, i); + } else if (i - remainder.get(r) > 1) { + return true; + } + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool checkSubarraySum(vector& nums, int k) { + unordered_map remainder; + remainder[0] = -1; + int total = 0; + + for (int i = 0; i < nums.size(); i++) { + total += nums[i]; + int r = total % k; + if (remainder.find(r) == remainder.end()) { + remainder[r] = i; + } else if (i - remainder[r] > 1) { + return true; + } + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {boolean} + */ + checkSubarraySum(nums, k) { + const remainder = new Map(); + remainder.set(0, -1); + let total = 0; + + for (let i = 0; i < nums.length; i++) { + total += nums[i]; + let r = total % k; + if (!remainder.has(r)) { + remainder.set(r, i); + } else if (i - remainder.get(r) > 1) { + return true; + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(k)$ + +> Where $n$ is the size of the array $nums$ and $k$ is the number that a subarray sum needs to be multiple of. \ No newline at end of file diff --git a/articles/convert-an-array-into-a-2d-array-with-conditions.md b/articles/convert-an-array-into-a-2d-array-with-conditions.md new file mode 100644 index 000000000..7f5906f0d --- /dev/null +++ b/articles/convert-an-array-into-a-2d-array-with-conditions.md @@ -0,0 +1,317 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def findMatrix(self, nums: List[int]) -> List[List[int]]: + res = [] + + for num in nums: + r = 0 + while r < len(res): + if num not in res[r]: + break + r += 1 + if r == len(res): + res.append([]) + res[r].append(num) + + return res +``` + +```java +public class Solution { + public List> findMatrix(int[] nums) { + List> res = new ArrayList<>(); + + for (int num : nums) { + int r = 0; + while (r < res.size()) { + if (!res.get(r).contains(num)) { + break; + } + r++; + } + if (r == res.size()) { + res.add(new ArrayList<>()); + } + res.get(r).add(num); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> findMatrix(vector& nums) { + vector> res; + + for (int num : nums) { + int r = 0; + while (r < res.size()) { + if (find(res[r].begin(), res[r].end(), num) == res[r].end()) { + break; + } + r++; + } + if (r == res.size()) { + res.push_back({}); + } + res[r].push_back(num); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + findMatrix(nums) { + const res = []; + + for (const num of nums) { + let r = 0; + while (r < res.length) { + if (!res[r].includes(num)) { + break; + } + r++; + } + if (r === res.length) { + res.push([]); + } + res[r].push(num); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ for the output array. + +> Where $n$ is the size of the array $nums$ and $m$ is the frequency of the most frequent element in the given array. + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def findMatrix(self, nums: List[int]) -> List[List[int]]: + nums.sort() + res = [] + + i = 0 + while i < len(nums): + j = i + r = 0 + while j < len(nums) and nums[i] == nums[j]: + if r == len(res): + res.append([]) + res[r].append(nums[i]) + r += 1 + j += 1 + i = j + + return res +``` + +```java +public class Solution { + public List> findMatrix(int[] nums) { + Arrays.sort(nums); + List> res = new ArrayList<>(); + + int i = 0; + while (i < nums.length) { + int j = i; + int r = 0; + while (j < nums.length && nums[i] == nums[j]) { + if (r == res.size()) { + res.add(new ArrayList<>()); + } + res.get(r).add(nums[i]); + r++; + j++; + } + i = j; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> findMatrix(vector& nums) { + sort(nums.begin(), nums.end()); + vector> res; + + int i = 0; + while (i < nums.size()) { + int j = i, r = 0; + while (j < nums.size() && nums[i] == nums[j]) { + if (r == res.size()) { + res.push_back({}); + } + res[r].push_back(nums[i]); + r++; + j++; + } + i = j; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + findMatrix(nums) { + nums.sort((a, b) => a - b); + const res = []; + + let i = 0; + while (i < nums.length) { + let j = i; + let r = 0; + while (j < nums.length && nums[i] === nums[j]) { + if (r === res.length) { + res.push([]); + } + res[r].push(nums[i]); + r++; + j++; + } + i = j; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ for the output array. + +--- + +## 3. Frequency Count + +::tabs-start + +```python +class Solution: + def findMatrix(self, nums: List[int]) -> List[List[int]]: + count = defaultdict(int) + res = [] + + for num in nums: + row = count[num] + if len(res) == row: + res.append([]) + res[row].append(num) + count[num] += 1 + + return res +``` + +```java +public class Solution { + public List> findMatrix(int[] nums) { + Map count = new HashMap<>(); + List> res = new ArrayList<>(); + + for (int num : nums) { + int row = count.getOrDefault(num, 0); + if (res.size() == row) { + res.add(new ArrayList<>()); + } + res.get(row).add(num); + count.put(num, row + 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> findMatrix(vector& nums) { + unordered_map count; + vector> res; + + for (int num : nums) { + int row = count[num]; + if (res.size() == row) { + res.push_back({}); + } + res[row].push_back(num); + count[num]++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + findMatrix(nums) { + const count = new Map(); + const res = []; + + for (const num of nums) { + const row = count.get(num) || 0; + if (res.length === row) { + res.push([]); + } + res[row].push(num); + count.set(num, row + 1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/convert-bst-to-greater-tree.md b/articles/convert-bst-to-greater-tree.md new file mode 100644 index 000000000..9e01d51be --- /dev/null +++ b/articles/convert-bst-to-greater-tree.md @@ -0,0 +1,643 @@ +## 1. Depth First Search (Two Pass) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]: + def getSum(node): + if not node: + return 0 + return node.val + getSum(node.left) + getSum(node.right) + + totalSum = getSum(root) + + def dfs(node): + nonlocal totalSum + if not node: + return + + dfs(node.left) + tmp = node.val + node.val = totalSum + totalSum -= tmp + dfs(node.right) + + dfs(root) + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private int totalSum; + + public TreeNode convertBST(TreeNode root) { + totalSum = getSum(root); + dfs(root); + return root; + } + + private int getSum(TreeNode node) { + if (node == null) return 0; + return node.val + getSum(node.left) + getSum(node.right); + } + + private void dfs(TreeNode node) { + if (node == null) return; + + dfs(node.left); + int tmp = node.val; + node.val = totalSum; + totalSum -= tmp; + dfs(node.right); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int totalSum; + + TreeNode* convertBST(TreeNode* root) { + totalSum = getSum(root); + dfs(root); + return root; + } + +private: + int getSum(TreeNode* node) { + if (!node) return 0; + return node->val + getSum(node->left) + getSum(node->right); + } + + void dfs(TreeNode* node) { + if (!node) return; + + dfs(node->left); + int tmp = node->val; + node->val = totalSum; + totalSum -= tmp; + dfs(node->right); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {TreeNode} + */ + convertBST(root) { + const getSum = (node) => { + if (!node) return 0; + return node.val + getSum(node.left) + getSum(node.right); + }; + + let totalSum = getSum(root); + + const dfs = (node) => { + if (!node) return; + + dfs(node.left); + let tmp = node.val; + node.val = totalSum; + totalSum -= tmp; + dfs(node.right); + }; + + dfs(root); + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Depth First Search (One Pass) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]: + curSum = 0 + + def dfs(node): + nonlocal curSum + if not node: + return + + dfs(node.right) + tmp = node.val + node.val += curSum + curSum += tmp + dfs(node.left) + + dfs(root) + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private int curSum = 0; + + public TreeNode convertBST(TreeNode root) { + dfs(root); + return root; + } + + private void dfs(TreeNode node) { + if (node == null) return; + + dfs(node.right); + int tmp = node.val; + node.val += curSum; + curSum += tmp; + dfs(node.left); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int curSum = 0; + + TreeNode* convertBST(TreeNode* root) { + dfs(root); + return root; + } + +private: + void dfs(TreeNode* node) { + if (!node) return; + + dfs(node->right); + int tmp = node->val; + node->val += curSum; + curSum += tmp; + dfs(node->left); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {TreeNode} + */ + convertBST(root) { + let curSum = 0; + + const dfs = (node) => { + if (!node) return; + + dfs(node.right); + let tmp = node.val; + node.val += curSum; + curSum += tmp; + dfs(node.left); + }; + + dfs(root); + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]: + curSum = 0 + stack = [] + node = root + + while stack or node: + while node: + stack.append(node) + node = node.right + + node = stack.pop() + curSum += node.val + node.val = curSum + node = node.left + + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode convertBST(TreeNode root) { + int curSum = 0; + Stack stack = new Stack<>(); + TreeNode node = root; + + while (!stack.isEmpty() || node != null) { + while (node != null) { + stack.push(node); + node = node.right; + } + + node = stack.pop(); + curSum += node.val; + node.val = curSum; + node = node.left; + } + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* convertBST(TreeNode* root) { + int curSum = 0; + stack st; + TreeNode* node = root; + + while (!st.empty() || node) { + while (node) { + st.push(node); + node = node->right; + } + + node = st.top(); st.pop(); + curSum += node->val; + node->val = curSum; + node = node->left; + } + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {TreeNode} + */ + convertBST(root) { + let curSum = 0; + const stack = []; + let node = root; + + while (stack.length || node) { + while (node) { + stack.push(node); + node = node.right; + } + + node = stack.pop(); + curSum += node.val; + node.val = curSum; + node = node.left; + } + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Morris Traversal + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]: + curSum = 0 + cur = root + + while cur: + if cur.right: + prev = cur.right + while prev.left and prev.left != cur: + prev = prev.left + + if not prev.left: + prev.left = cur + cur = cur.right + else: + prev.left = None + curSum += cur.val + cur.val = curSum + cur = cur.left + else: + curSum += cur.val + cur.val = curSum + cur = cur.left + + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode convertBST(TreeNode root) { + int curSum = 0; + TreeNode cur = root; + + while (cur != null) { + if (cur.right != null) { + TreeNode prev = cur.right; + while (prev.left != null && prev.left != cur) { + prev = prev.left; + } + + if (prev.left == null) { + prev.left = cur; + cur = cur.right; + } else { + prev.left = null; + curSum += cur.val; + cur.val = curSum; + cur = cur.left; + } + } else { + curSum += cur.val; + cur.val = curSum; + cur = cur.left; + } + } + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* convertBST(TreeNode* root) { + int curSum = 0; + TreeNode* cur = root; + + while (cur) { + if (cur->right) { + TreeNode* prev = cur->right; + while (prev->left && prev->left != cur) { + prev = prev->left; + } + + if (!prev->left) { + prev->left = cur; + cur = cur->right; + } else { + prev->left = nullptr; + curSum += cur->val; + cur->val = curSum; + cur = cur->left; + } + } else { + curSum += cur->val; + cur->val = curSum; + cur = cur->left; + } + } + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {TreeNode} + */ + convertBST(root) { + let curSum = 0; + let cur = root; + + while (cur) { + if (cur.right) { + let prev = cur.right; + while (prev.left && prev.left !== cur) { + prev = prev.left; + } + + if (!prev.left) { + prev.left = cur; + cur = cur.right; + } else { + prev.left = null; + curSum += cur.val; + cur.val = curSum; + cur = cur.left; + } + } else { + curSum += cur.val; + cur.val = curSum; + cur = cur.left; + } + } + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/convert-sorted-array-to-binary-search-tree.md b/articles/convert-sorted-array-to-binary-search-tree.md new file mode 100644 index 000000000..dd933b982 --- /dev/null +++ b/articles/convert-sorted-array-to-binary-search-tree.md @@ -0,0 +1,437 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]: + if not nums: + return None + + mid = len(nums) // 2 + root = TreeNode(nums[mid]) + root.left = self.sortedArrayToBST(nums[:mid]) + root.right = self.sortedArrayToBST(nums[mid + 1:]) + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode sortedArrayToBST(int[] nums) { + if (nums.length == 0) { + return null; + } + + int mid = nums.length / 2; + TreeNode root = new TreeNode(nums[mid]); + root.left = sortedArrayToBST(Arrays.copyOfRange(nums, 0, mid)); + root.right = sortedArrayToBST(Arrays.copyOfRange(nums, mid + 1, nums.length)); + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* sortedArrayToBST(vector& nums) { + if (nums.empty()) { + return nullptr; + } + + int mid = nums.size() / 2; + TreeNode* root = new TreeNode(nums[mid]); + vector left(nums.begin(), nums.begin() + mid); + vector right(nums.begin() + mid + 1, nums.end()); + root->left = sortedArrayToBST(left); + root->right = sortedArrayToBST(right); + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number[]} nums + * @return {TreeNode} + */ + sortedArrayToBST(nums) { + if (nums.length === 0) { + return null; + } + + const mid = Math.floor(nums.length / 2); + const root = new TreeNode(nums[mid]); + root.left = this.sortedArrayToBST(nums.slice(0, mid)); + root.right = this.sortedArrayToBST(nums.slice(mid + 1)); + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Depth First Search (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def sortedArrayToBST(self, nums: List[int]) -> TreeNode: + def helper(l, r): + if l > r: + return None + m = (l + r) // 2 + root = TreeNode(nums[m]) + root.left = helper(l, m - 1) + root.right = helper(m + 1, r) + return root + + return helper(0, len(nums) - 1) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode sortedArrayToBST(int[] nums) { + return helper(nums, 0, nums.length - 1); + } + + private TreeNode helper(int[] nums, int l, int r) { + if (l > r) { + return null; + } + int m = (l + r) / 2; + TreeNode root = new TreeNode(nums[m]); + root.left = helper(nums, l, m - 1); + root.right = helper(nums, m + 1, r); + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* sortedArrayToBST(vector& nums) { + return helper(nums, 0, nums.size() - 1); + } + +private: + TreeNode* helper(vector& nums, int l, int r) { + if (l > r) { + return nullptr; + } + int m = (l + r) / 2; + TreeNode* root = new TreeNode(nums[m]); + root->left = helper(nums, l, m - 1); + root->right = helper(nums, m + 1, r); + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number[]} nums + * @return {TreeNode} + */ + sortedArrayToBST(nums) { + const helper = (l, r) => { + if (l > r) { + return null; + } + const m = Math.floor((l + r) / 2); + const root = new TreeNode(nums[m]); + root.left = helper(l, m - 1); + root.right = helper(m + 1, r); + return root; + }; + + return helper(0, nums.length - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(\log n)$ space for recursion stack. + * $O(n)$ space for the output. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def sortedArrayToBST(self, nums: List[int]) -> TreeNode: + if not nums: + return None + + root = TreeNode(0) + stack = [(root, 0, len(nums) - 1)] + + while stack: + node, l, r = stack.pop() + m = (l + r) // 2 + node.val = nums[m] + if l <= m - 1: + node.left = TreeNode(0) + stack.append((node.left, l, m - 1)) + if m + 1 <= r: + node.right = TreeNode(0) + stack.append((node.right, m + 1, r)) + + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode sortedArrayToBST(int[] nums) { + if (nums.length == 0) { + return null; + } + + TreeNode root = new TreeNode(0); + Stack stack = new Stack<>(); + Stack nodes = new Stack<>(); + stack.push(new int[]{0, nums.length - 1}); + nodes.push(root); + + while (!stack.isEmpty()) { + int[] range = stack.pop(); + TreeNode node = nodes.pop(); + int l = range[0], r = range[1]; + int m = (l + r) / 2; + node.val = nums[m]; + + if (l <= m - 1) { + node.left = new TreeNode(0); + stack.push(new int[]{l, m - 1}); + nodes.push(node.left); + } + if (m + 1 <= r) { + node.right = new TreeNode(0); + stack.push(new int[]{m + 1, r}); + nodes.push(node.right); + } + } + + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* sortedArrayToBST(vector& nums) { + if (nums.empty()) return nullptr; + + TreeNode* root = new TreeNode(0); + stack> stack; + stack.push({root, 0, (int)nums.size() - 1}); + + while (!stack.empty()) { + auto [node, l, r] = stack.top(); + stack.pop(); + int m = (l + r) / 2; + node->val = nums[m]; + + if (l <= m - 1) { + node->left = new TreeNode(0); + stack.push({node->left, l, m - 1}); + } + if (m + 1 <= r) { + node->right = new TreeNode(0); + stack.push({node->right, m + 1, r}); + } + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number[]} nums + * @return {TreeNode} + */ + sortedArrayToBST(nums) { + if (nums.length === 0) { + return null; + } + + const root = new TreeNode(0); + const stack = [[root, 0, nums.length - 1]]; + + while (stack.length) { + const [node, l, r] = stack.pop(); + const m = Math.floor((l + r) / 2); + node.val = nums[m]; + + if (l <= m - 1) { + node.left = new TreeNode(0); + stack.push([node.left, l, m - 1]); + } + if (m + 1 <= r) { + node.right = new TreeNode(0); + stack.push([node.right, m + 1, r]); + } + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(\log n)$ space for the stack. + * $O(n)$ space for the output. \ No newline at end of file diff --git a/articles/copy-linked-list-with-random-pointer.md b/articles/copy-linked-list-with-random-pointer.md new file mode 100644 index 000000000..0b595bf64 --- /dev/null +++ b/articles/copy-linked-list-with-random-pointer.md @@ -0,0 +1,1758 @@ +## 1. Recursion + Hash Map + +::tabs-start + +```python +""" +# Definition for a Node. +class Node: + def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None): + self.val = int(x) + self.next = next + self.random = random +""" + +class Solution: + def __init__(self): + self.map = {} + + def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]': + if head is None: + return None + if head in self.map: + return self.map[head] + + copy = Node(head.val) + self.map[head] = copy + copy.next = self.copyRandomList(head.next) + copy.random = self.map.get(head.random) + return copy +``` + +```java +/* +// Definition for a Node. +class Node { + int val; + Node next; + Node random; + + public Node(int val) { + this.val = val; + this.next = null; + this.random = null; + } +} +*/ + +public class Solution { + HashMap map = new HashMap<>(); + + public Node copyRandomList(Node head) { + if (head == null) return null; + if (map.containsKey(head)) return map.get(head); + + Node copy = new Node(head.val); + map.put(head, copy); + copy.next = copyRandomList(head.next); + copy.random = map.get(head.random); + return copy; + } +} +``` + +```cpp +/* +// Definition for a Node. +class Node { +public: + int val; + Node* next; + Node* random; + + Node(int _val) { + val = _val; + next = NULL; + random = NULL; + } +}; +*/ + +class Solution { +public: + unordered_map map; + + Node* copyRandomList(Node* head) { + if (head == nullptr) return nullptr; + if (map.count(head)) return map[head]; + + Node* copy = new Node(head->val); + map[head] = copy; + copy->next = copyRandomList(head->next); + copy->random = map[head->random]; + return copy; + } +}; +``` + +```javascript +// class Node { +// constructor(val, next = null, random = null) { +// this.val = val; +// this.next = next; +// this.random = random; +// } +// } + +class Solution { + constructor() { + this.map = new Map(); + } + /** + * @param {Node} head + * @return {Node} + */ + copyRandomList(head) { + if (head === null) return null; + if (this.map.has(head)) return this.map.get(head); + + const copy = new Node(head.val); + this.map.set(head, copy); + copy.next = this.copyRandomList(head.next); + copy.random = this.map.get(head.random) || null; + return copy; + } +} +``` + +```csharp +/* +// Definition for a Node. +public class Node { + public int val; + public Node next; + public Node random; + + public Node(int _val) { + val = _val; + next = null; + random = null; + } +} +*/ + +public class Solution { + private Dictionary map = new Dictionary(); + + public Node copyRandomList(Node head) { + if (head == null) return null; + if (map.ContainsKey(head)) return map[head]; + + Node copy = new Node(head.val); + map[head] = copy; + copy.next = copyRandomList(head.next); + + if (head.random != null) { + copy.random = copyRandomList(head.random); + } else { + copy.random = null; + } + + return copy; + } +} +``` + +```go +/** + * Definition for a Node. + * type Node struct { + * Val int + * Next *Node + * Random *Node + * } + */ + +var m = make(map[*Node]*Node) + +func copyRandomList(head *Node) *Node { + if head == nil { + return nil + } + if val, exists := m[head]; exists { + return val + } + + copy := &Node{Val: head.Val} + m[head] = copy + copy.Next = copyRandomList(head.Next) + copy.Random = m[head.Random] + return copy +} +``` + +```kotlin +/** + * Example: + * var ti = Node(5) + * var v = ti.`val` + * Definition for a Node. + * class Node(var `val`: Int) { + * var next: Node? = null + * var random: Node? = null + * } + */ + +class Solution { + private val map = HashMap() + + fun copyRandomList(head: Node?): Node? { + if (head == null) { + return null + } + if (map.containsKey(head)) { + return map[head] + } + + val copy = Node(head.`val`) + map[head] = copy + copy.next = copyRandomList(head.next) + copy.random = map[head.random] + return copy + } +} +``` + +```swift +/** + * Definition for a Node. + * public class Node { + * public var val: Int + * public var next: Node? + * public var random: Node? + * public init(_ val: Int) { + * self.val = val + * self.next = nil + * self.random = nil + * } + * } + */ + +class Solution { + private var map = [Node: Node]() + + func copyRandomList(_ head: Node?) -> Node? { + if head == nil { + return nil + } + + if let copied = map[head!] { + return copied + } + + let copy = Node(head!.val) + map[head!] = copy + + copy.next = copyRandomList(head!.next) + copy.random = copyRandomList(head!.random) + + return copy + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Hash Map (Two Pass) + +::tabs-start + +```python +""" +# Definition for a Node. +class Node: + def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None): + self.val = int(x) + self.next = next + self.random = random +""" + +class Solution: + def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]': + oldToCopy = {None: None} + + cur = head + while cur: + copy = Node(cur.val) + oldToCopy[cur] = copy + cur = cur.next + cur = head + while cur: + copy = oldToCopy[cur] + copy.next = oldToCopy[cur.next] + copy.random = oldToCopy[cur.random] + cur = cur.next + return oldToCopy[head] +``` + +```java +/* +// Definition for a Node. +class Node { + int val; + Node next; + Node random; + + public Node(int val) { + this.val = val; + this.next = null; + this.random = null; + } +} +*/ + +class Solution { + public Node copyRandomList(Node head) { + Map oldToCopy = new HashMap<>(); + oldToCopy.put(null, null); + + Node cur = head; + while (cur != null) { + Node copy = new Node(cur.val); + oldToCopy.put(cur, copy); + cur = cur.next; + } + + cur = head; + while (cur != null) { + Node copy = oldToCopy.get(cur); + copy.next = oldToCopy.get(cur.next); + copy.random = oldToCopy.get(cur.random); + cur = cur.next; + } + + return oldToCopy.get(head); + } +} +``` + +```cpp +/* +// Definition for a Node. +class Node { +public: + int val; + Node* next; + Node* random; + + Node(int _val) { + val = _val; + next = NULL; + random = NULL; + } +}; +*/ + +class Solution { +public: + Node* copyRandomList(Node* head) { + unordered_map oldToCopy; + oldToCopy[NULL] = NULL; + + Node* cur = head; + while (cur != NULL) { + Node* copy = new Node(cur->val); + oldToCopy[cur] = copy; + cur = cur->next; + } + + cur = head; + while (cur != NULL) { + Node* copy = oldToCopy[cur]; + copy->next = oldToCopy[cur->next]; + copy->random = oldToCopy[cur->random]; + cur = cur->next; + } + + return oldToCopy[head]; + } +}; +``` + +```javascript +// class Node { +// constructor(val, next = null, random = null) { +// this.val = val; +// this.next = next; +// this.random = random; +// } +// } + +class Solution { + /** + * @param {Node} head + * @return {Node} + */ + copyRandomList(head) { + const oldToCopy = new Map(); + oldToCopy.set(null, null); + + let cur = head; + while (cur) { + const copy = new Node(cur.val); + oldToCopy.set(cur, copy); + cur = cur.next; + } + + cur = head; + while (cur) { + const copy = oldToCopy.get(cur); + copy.next = oldToCopy.get(cur.next); + copy.random = oldToCopy.get(cur.random); + cur = cur.next; + } + + return oldToCopy.get(head); + } +} +``` + +```csharp +/* +// Definition for a Node. +public class Node { + public int val; + public Node next; + public Node random; + + public Node(int _val) { + val = _val; + next = null; + random = null; + } +} +*/ + +public class Solution { + public Node copyRandomList(Node head) { + Dictionary oldToCopy = new Dictionary(); + + Node cur = head; + while (cur != null) { + Node copy = new Node(cur.val); + oldToCopy[cur] = copy; + cur = cur.next; + } + + cur = head; + while (cur != null) { + Node copy = oldToCopy[cur]; + copy.next = cur.next != null ? oldToCopy[cur.next] : null; + copy.random = cur.random != null ? oldToCopy[cur.random] : null; + cur = cur.next; + } + + return head != null ? oldToCopy[head] : null; + } +} +``` + +```go +/** + * Definition for a Node. + * type Node struct { + * Val int + * Next *Node + * Random *Node + * } + */ + +func copyRandomList(head *Node) *Node { + oldToCopy := map[*Node]*Node{nil: nil} + + cur := head + for cur != nil { + copy := &Node{Val: cur.Val} + oldToCopy[cur] = copy + cur = cur.Next + } + + cur = head + for cur != nil { + copy := oldToCopy[cur] + copy.Next = oldToCopy[cur.Next] + copy.Random = oldToCopy[cur.Random] + cur = cur.Next + } + + return oldToCopy[head] +} +``` + +```kotlin +/** + * Example: + * var ti = Node(5) + * var v = ti.`val` + * Definition for a Node. + * class Node(var `val`: Int) { + * var next: Node? = null + * var random: Node? = null + * } + */ + +class Solution { + fun copyRandomList(head: Node?): Node? { + val oldToCopy = hashMapOf(null to null) + + var cur = head + while (cur != null) { + val copy = Node(cur.`val`) + oldToCopy[cur] = copy + cur = cur.next + } + + cur = head + while (cur != null) { + val copy = oldToCopy[cur] + copy?.next = oldToCopy[cur.next] + copy?.random = oldToCopy[cur.random] + cur = cur.next + } + + return oldToCopy[head] + } +} +``` + +```swift +/** + * Definition for a Node. + * public class Node { + * public var val: Int + * public var next: Node? + * public var random: Node? + * public init(_ val: Int) { + * self.val = val + * self.next = nil + * self.random = nil + * } + * } + */ + +class Solution { + func copyRandomList(_ head: Node?) -> Node? { + var oldToCopy: [Node?: Node?] = [nil: nil] + + var cur = head + while cur != nil { + let copy = Node(cur!.val) + oldToCopy[cur] = copy + cur = cur?.next + } + + cur = head + while cur != nil { + let copy = oldToCopy[cur]! + copy?.next = oldToCopy[cur?.next]! + copy?.random = oldToCopy[cur?.random]! + cur = cur?.next + } + + return oldToCopy[head]! + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Hash Map (One Pass) + +::tabs-start + +```python +""" +# Definition for a Node. +class Node: + def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None): + self.val = int(x) + self.next = next + self.random = random +""" + +class Solution: + def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]': + oldToCopy = collections.defaultdict(lambda: Node(0)) + oldToCopy[None] = None + + cur = head + while cur: + oldToCopy[cur].val = cur.val + oldToCopy[cur].next = oldToCopy[cur.next] + oldToCopy[cur].random = oldToCopy[cur.random] + cur = cur.next + return oldToCopy[head] +``` + +```java +/* +// Definition for a Node. +class Node { + int val; + Node next; + Node random; + + public Node(int val) { + this.val = val; + this.next = null; + this.random = null; + } +} +*/ + +public class Solution { + public Node copyRandomList(Node head) { + HashMap oldToCopy = new HashMap<>(); + oldToCopy.put(null, null); + + Node cur = head; + while (cur != null) { + if (!oldToCopy.containsKey(cur)) { + oldToCopy.put(cur, new Node(0)); + } + oldToCopy.get(cur).val = cur.val; + + if (!oldToCopy.containsKey(cur.next)) { + oldToCopy.put(cur.next, new Node(0)); + } + oldToCopy.get(cur).next = oldToCopy.get(cur.next); + + if (!oldToCopy.containsKey(cur.random)) { + oldToCopy.put(cur.random, new Node(0)); + } + oldToCopy.get(cur).random = oldToCopy.get(cur.random); + cur = cur.next; + } + return oldToCopy.get(head); + } +} +``` + +```cpp +/* +// Definition for a Node. +class Node { +public: + int val; + Node* next; + Node* random; + + Node(int _val) { + val = _val; + next = NULL; + random = NULL; + } +}; +*/ + +class Solution { +public: + Node* copyRandomList(Node* head) { + unordered_map oldToCopy; + oldToCopy[nullptr] = nullptr; + + Node* cur = head; + while (cur != nullptr) { + if (oldToCopy.find(cur) == oldToCopy.end()) { + oldToCopy[cur] = new Node(0); + } + oldToCopy[cur]->val = cur->val; + if (oldToCopy.find(cur->next) == oldToCopy.end()) { + oldToCopy[cur->next] = new Node(0); + } + oldToCopy[cur]->next = oldToCopy[cur->next]; + if (oldToCopy.find(cur->random) == oldToCopy.end()) { + oldToCopy[cur->random] = new Node(0); + } + oldToCopy[cur]->random = oldToCopy[cur->random]; + cur = cur->next; + } + return oldToCopy[head]; + } +}; +``` + +```javascript +// class Node { +// constructor(val, next = null, random = null) { +// this.val = val; +// this.next = next; +// this.random = random; +// } +// } + +class Solution { + /** + * @param {Node} head + * @return {Node} + */ + copyRandomList(head) { + const oldToCopy = new Map(); + oldToCopy.set(null, null); + + let cur = head; + while (cur !== null) { + if (!oldToCopy.has(cur)) { + oldToCopy.set(cur, new Node(0)); + } + oldToCopy.get(cur).val = cur.val; + if (!oldToCopy.has(cur.next)) { + oldToCopy.set(cur.next, new Node(0)); + } + oldToCopy.get(cur).next = oldToCopy.get(cur.next); + if (!oldToCopy.has(cur.random)) { + oldToCopy.set(cur.random, new Node(0)); + } + oldToCopy.get(cur).random = oldToCopy.get(cur.random); + cur = cur.next; + } + return oldToCopy.get(head); + } +} +``` + +```csharp +/* +// Definition for a Node. +public class Node { + public int val; + public Node next; + public Node random; + + public Node(int _val) { + val = _val; + next = null; + random = null; + } +} +*/ + +public class Solution { + public Node copyRandomList(Node head) { + if (head == null) return null; + Dictionary oldToCopy = new Dictionary(); + + Node cur = head; + while (cur != null) { + if (!oldToCopy.ContainsKey(cur)) { + oldToCopy[cur] = new Node(cur.val); + } else { + oldToCopy[cur].val = cur.val; + } + + if (cur.next != null) { + if (!oldToCopy.ContainsKey(cur.next)) { + oldToCopy[cur.next] = new Node(0); + } + oldToCopy[cur].next = oldToCopy[cur.next]; + } else { + oldToCopy[cur].next = null; + } + + if (cur.random != null) { + if (!oldToCopy.ContainsKey(cur.random)) { + oldToCopy[cur.random] = new Node(0); + } + oldToCopy[cur].random = oldToCopy[cur.random]; + } else { + oldToCopy[cur].random = null; + } + cur = cur.next; + } + return oldToCopy[head]; + } +} +``` + +```go +/** + * Definition for a Node. + * type Node struct { + * Val int + * Next *Node + * Random *Node + * } + */ + +func copyRandomList(head *Node) *Node { + oldToCopy := make(map[*Node]*Node) + oldToCopy[nil] = nil + + cur := head + for cur != nil { + if _, exists := oldToCopy[cur]; !exists { + oldToCopy[cur] = &Node{Val: cur.Val} + } + if cur.Next != nil { + if _, exists := oldToCopy[cur.Next]; !exists { + oldToCopy[cur.Next] = &Node{Val: cur.Next.Val} + } + oldToCopy[cur].Next = oldToCopy[cur.Next] + } + if cur.Random != nil { + if _, exists := oldToCopy[cur.Random]; !exists { + oldToCopy[cur.Random] = &Node{Val: cur.Random.Val} + } + oldToCopy[cur].Random = oldToCopy[cur.Random] + } + cur = cur.Next + } + return oldToCopy[head] +} +``` + +```kotlin +/** + * Example: + * var ti = Node(5) + * var v = ti.`val` + * Definition for a Node. + * class Node(var `val`: Int) { + * var next: Node? = null + * var random: Node? = null + * } + */ + +class Solution { + fun copyRandomList(head: Node?): Node? { + val oldToCopy = HashMap() + oldToCopy[null] = null + + var cur = head + while (cur != null) { + if (!oldToCopy.containsKey(cur)) { + oldToCopy[cur] = Node(cur.`val`) + } + if (cur.next != null) { + if (!oldToCopy.containsKey(cur.next)) { + oldToCopy[cur.next] = Node(cur.next!!.`val`) + } + oldToCopy[cur]!!.next = oldToCopy[cur.next] + } + if (cur.random != null) { + if (!oldToCopy.containsKey(cur.random)) { + oldToCopy[cur.random] = Node(cur.random!!.`val`) + } + oldToCopy[cur]!!.random = oldToCopy[cur.random] + } + cur = cur.next + } + return oldToCopy[head] + } +} +``` + +```swift +/** + * Definition for a Node. + * public class Node { + * public var val: Int + * public var next: Node? + * public var random: Node? + * public init(_ val: Int) { + * self.val = val + * self.next = nil + * self.random = nil + * } + * } + */ + +class Solution { + func copyRandomList(_ head: Node?) -> Node? { + var oldToCopy = [Node?: Node?]() + + func getNode(_ node: Node?) -> Node? { + if node == nil { return nil } + if oldToCopy[node] == nil { + oldToCopy[node] = Node(0) + } + return oldToCopy[node]! + } + + var cur = head + while cur != nil { + getNode(cur)!.val = cur!.val + getNode(cur)!.next = getNode(cur!.next) + getNode(cur)!.random = getNode(cur!.random) + cur = cur!.next + } + + return getNode(head) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Space Optimized - I + +::tabs-start + +```python +""" +# Definition for a Node. +class Node: + def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None): + self.val = int(x) + self.next = next + self.random = random +""" + +class Solution: + def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]': + if head is None: + return None + + l1 = head + while l1 is not None: + l2 = Node(l1.val) + l2.next = l1.next + l1.next = l2 + l1 = l2.next + + newHead = head.next + + l1 = head + while l1 is not None: + if l1.random is not None: + l1.next.random = l1.random.next + l1 = l1.next.next + + l1 = head + while l1 is not None: + l2 = l1.next + l1.next = l2.next + if l2.next is not None: + l2.next = l2.next.next + l1 = l1.next + + return newHead +``` + +```java +/* +// Definition for a Node. +class Node { + int val; + Node next; + Node random; + + public Node(int val) { + this.val = val; + this.next = null; + this.random = null; + } +} +*/ + +public class Solution { + public Node copyRandomList(Node head) { + if (head == null) { + return null; + } + + Node l1 = head; + while (l1 != null) { + Node l2 = new Node(l1.val); + l2.next = l1.next; + l1.next = l2; + l1 = l2.next; + } + + Node newHead = head.next; + + l1 = head; + while (l1 != null) { + if (l1.random != null) { + l1.next.random = l1.random.next; + } + l1 = l1.next.next; + } + + l1 = head; + while (l1 != null) { + Node l2 = l1.next; + l1.next = l2.next; + if (l2.next != null) { + l2.next = l2.next.next; + } + l1 = l1.next; + } + + return newHead; + } +} +``` + +```cpp +/* +// Definition for a Node. +class Node { +public: + int val; + Node* next; + Node* random; + + Node(int _val) { + val = _val; + next = NULL; + random = NULL; + } +}; +*/ + +class Solution { +public: + Node* copyRandomList(Node* head) { + if (head == nullptr) { + return nullptr; + } + + Node* l1 = head; + while (l1 != nullptr) { + Node* l2 = new Node(l1->val); + l2->next = l1->next; + l1->next = l2; + l1 = l2->next; + } + + Node* newHead = head->next; + + l1 = head; + while (l1 != nullptr) { + if (l1->random != nullptr) { + l1->next->random = l1->random->next; + } + l1 = l1->next->next; + } + + l1 = head; + while (l1 != nullptr) { + Node* l2 = l1->next; + l1->next = l2->next; + if (l2->next != nullptr) { + l2->next = l2->next->next; + } + l1 = l1->next; + } + + return newHead; + } +}; +``` + +```javascript +// class Node { +// constructor(val, next = null, random = null) { +// this.val = val; +// this.next = next; +// this.random = random; +// } +// } + +class Solution { + /** + * @param {Node} head + * @return {Node} + */ + copyRandomList(head) { + if (!head) { + return null; + } + + let l1 = head; + while (l1) { + const l2 = new Node(l1.val); + l2.next = l1.next; + l1.next = l2; + l1 = l2.next; + } + + const newHead = head.next; + + l1 = head; + while (l1) { + if (l1.random) { + l1.next.random = l1.random.next; + } + l1 = l1.next.next; + } + + l1 = head; + while (l1) { + const l2 = l1.next; + l1.next = l2.next; + if (l2.next) { + l2.next = l2.next.next; + } + l1 = l1.next; + } + + return newHead; + } +} +``` + +```csharp +/* +// Definition for a Node. +public class Node { + public int val; + public Node next; + public Node random; + + public Node(int _val) { + val = _val; + next = null; + random = null; + } +} +*/ + +public class Solution { + public Node copyRandomList(Node head) { + if (head == null) { + return null; + } + + Node l1 = head; + while (l1 != null) { + Node l2 = new Node(l1.val); + l2.next = l1.next; + l1.next = l2; + l1 = l2.next; + } + + Node newHead = head.next; + + l1 = head; + while (l1 != null) { + if (l1.random != null) { + l1.next.random = l1.random.next; + } + l1 = l1.next.next; + } + + l1 = head; + while (l1 != null) { + Node l2 = l1.next; + l1.next = l2.next; + if (l2.next != null) { + l2.next = l2.next.next; + } + l1 = l1.next; + } + + return newHead; + } +} +``` + +```go +/** + * Definition for a Node. + * type Node struct { + * Val int + * Next *Node + * Random *Node + * } + */ + +func copyRandomList(head *Node) *Node { + if head == nil { + return nil + } + + l1 := head + for l1 != nil { + l2 := &Node{Val: l1.Val} + l2.Next = l1.Next + l1.Next = l2 + l1 = l2.Next + } + + newHead := head.Next + + l1 = head + for l1 != nil { + if l1.Random != nil { + l1.Next.Random = l1.Random.Next + } + l1 = l1.Next.Next + } + + l1 = head + for l1 != nil { + l2 := l1.Next + l1.Next = l2.Next + if l2.Next != nil { + l2.Next = l2.Next.Next + } + l1 = l1.Next + } + + return newHead +} +``` + +```kotlin +/** + * Example: + * var ti = Node(5) + * var v = ti.`val` + * Definition for a Node. + * class Node(var `val`: Int) { + * var next: Node? = null + * var random: Node? = null + * } + */ + +class Solution { + fun copyRandomList(head: Node?): Node? { + if (head == null) { + return null + } + + var l1: Node? = head + while (l1 != null) { + val l2 = Node(l1.`val`) + l2.next = l1.next + l1.next = l2 + l1 = l2.next + } + + val newHead = head.next + + l1 = head + while (l1 != null) { + if (l1.random != null) { + l1.next?.random = l1.random?.next + } + l1 = l1.next?.next + } + + l1 = head + while (l1 != null) { + val l2 = l1.next + l1.next = l2?.next + val nextL2 = l2?.next + if (nextL2 != null) { + l2.next = nextL2.next + } + l1 = l1.next + } + + return newHead + } +} +``` + +```swift +/** + * Definition for a Node. + * public class Node { + * public var val: Int + * public var next: Node? + * public var random: Node? + * public init(_ val: Int) { + * self.val = val + * self.next = nil + * self.random = nil + * } + * } + */ + +class Solution { + func copyRandomList(_ head: Node?) -> Node? { + if head == nil { + return nil + } + + var l1 = head + while l1 != nil { + let l2 = Node(l1!.val) + l2.next = l1?.next + l1?.next = l2 + l1 = l2.next + } + + let newHead = head?.next + l1 = head + while l1 != nil { + if let random = l1?.random { + l1?.next?.random = random.next + } + l1 = l1?.next?.next + } + + l1 = head + while l1 != nil { + let l2 = l1?.next + l1?.next = l2?.next + if l2?.next != nil { + l2?.next = l2?.next?.next + } + l1 = l1?.next + } + + return newHead + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ for the output. + +--- + +## 5. Space Optimized - II + +::tabs-start + +```python +""" +# Definition for a Node. +class Node: + def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None): + self.val = int(x) + self.next = next + self.random = random +""" + +class Solution: + def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]': + if head is None: + return None + + l1 = head + while l1: + l2 = Node(l1.val) + l2.next = l1.random + l1.random = l2 + l1 = l1.next + + newHead = head.random + + l1 = head + while l1: + l2 = l1.random + l2.random = l2.next.random if l2.next else None + l1 = l1.next + + l1 = head + while l1 is not None: + l2 = l1.random + l1.random = l2.next + l2.next = l1.next.random if l1.next else None + l1 = l1.next + + return newHead +``` + +```java +/* +// Definition for a Node. +class Node { + int val; + Node next; + Node random; + + public Node(int val) { + this.val = val; + this.next = null; + this.random = null; + } +} +*/ + +public class Solution { + public Node copyRandomList(Node head) { + if (head == null) { + return null; + } + + Node l1 = head; + while (l1 != null) { + Node l2 = new Node(l1.val); + l2.next = l1.random; + l1.random = l2; + l1 = l1.next; + } + + Node newHead = head.random; + + l1 = head; + while (l1 != null) { + Node l2 = l1.random; + l2.random = (l2.next != null) ? l2.next.random : null; + l1 = l1.next; + } + + l1 = head; + while (l1 != null) { + Node l2 = l1.random; + l1.random = l2.next; + l2.next = (l1.next != null) ? l1.next.random : null; + l1 = l1.next; + } + + return newHead; + } +} +``` + +```cpp +/* +// Definition for a Node. +class Node { +public: + int val; + Node* next; + Node* random; + + Node(int _val) { + val = _val; + next = NULL; + random = NULL; + } +}; +*/ + +class Solution { +public: + Node* copyRandomList(Node* head) { + if (!head) { + return nullptr; + } + + Node* l1 = head; + while (l1) { + Node* l2 = new Node(l1->val); + l2->next = l1->random; + l1->random = l2; + l1 = l1->next; + } + + Node* newHead = head->random; + + l1 = head; + while (l1) { + Node* l2 = l1->random; + l2->random = (l2->next != nullptr) ? l2->next->random : nullptr; + l1 = l1->next; + } + + l1 = head; + while (l1) { + Node* l2 = l1->random; + l1->random = l2->next; + l2->next = (l1->next != nullptr) ? l1->next->random : nullptr; + l1 = l1->next; + } + + return newHead; + } +}; +``` + +```javascript +// class Node { +// constructor(val, next = null, random = null) { +// this.val = val; +// this.next = next; +// this.random = random; +// } +// } + +class Solution { + /** + * @param {Node} head + * @return {Node} + */ + copyRandomList(head) { + if (head === null) { + return null; + } + + let l1 = head; + while (l1) { + let l2 = new Node(l1.val); + l2.next = l1.random; + l1.random = l2; + l1 = l1.next; + } + + let newHead = head.random; + + l1 = head; + while (l1) { + let l2 = l1.random; + l2.random = l2.next ? l2.next.random : null; + l1 = l1.next; + } + + l1 = head; + while (l1) { + let l2 = l1.random; + l1.random = l2.next; + l2.next = l1.next ? l1.next.random : null; + l1 = l1.next; + } + + return newHead; + } +} +``` + +```csharp +/* +// Definition for a Node. +public class Node { + public int val; + public Node next; + public Node random; + + public Node(int _val) { + val = _val; + next = null; + random = null; + } +} +*/ + +public class Solution { + public Node copyRandomList(Node head) { + if (head == null) { + return null; + } + + Node l1 = head; + while (l1 != null) { + Node l2 = new Node(l1.val); + l2.next = l1.random; + l1.random = l2; + l1 = l1.next; + } + + Node newHead = head.random; + + l1 = head; + while (l1 != null) { + Node l2 = l1.random; + l2.random = (l2.next != null) ? l2.next.random : null; + l1 = l1.next; + } + + l1 = head; + while (l1 != null) { + Node l2 = l1.random; + l1.random = l2.next; + l2.next = (l1.next != null) ? l1.next.random : null; + l1 = l1.next; + } + + return newHead; + } +} +``` + +```go +/** + * Definition for a Node. + * type Node struct { + * Val int + * Next *Node + * Random *Node + * } + */ + +func copyRandomList(head *Node) *Node { + if head == nil { + return nil + } + + l1 := head + for l1 != nil { + l2 := &Node{Val: l1.Val} + l2.Next = l1.Random + l1.Random = l2 + l1 = l1.Next + } + + newHead := head.Random + + l1 = head + for l1 != nil { + l2 := l1.Random + if l2.Next != nil { + l2.Random = l2.Next.Random + } else { + l2.Random = nil + } + l1 = l1.Next + } + + l1 = head + for l1 != nil { + l2 := l1.Random + l1.Random = l2.Next + if l1.Next != nil { + l2.Next = l1.Next.Random + } else { + l2.Next = nil + } + l1 = l1.Next + } + + return newHead +} +``` + +```kotlin +/** + * Example: + * var ti = Node(5) + * var v = ti.`val` + * Definition for a Node. + * class Node(var `val`: Int) { + * var next: Node? = null + * var random: Node? = null + * } + */ + +class Solution { + fun copyRandomList(head: Node?): Node? { + if (head == null) { + return null + } + + var l1: Node? = head + while (l1 != null) { + val l2 = Node(l1.`val`) + l2.next = l1.random + l1.random = l2 + l1 = l1.next + } + + val newHead = head.random + + l1 = head + while (l1 != null) { + val l2 = l1.random + if (l2 != null) { + l2.random = l2.next?.random + } + l1 = l1.next + } + + l1 = head + while (l1 != null) { + val l2 = l1.random + l1.random = l2?.next + if (l2 != null) { + l2.next = l1.next?.random + } + l1 = l1.next + } + + return newHead + } +} +``` + +```swift +/** + * Definition for a Node. + * public class Node { + * public var val: Int + * public var next: Node? + * public var random: Node? + * public init(_ val: Int) { + * self.val = val + * self.next = nil + * self.random = nil + * } + * } + */ + +class Solution { + func copyRandomList(_ head: Node?) -> Node? { + if head == nil { + return nil + } + + var l1 = head + while l1 != nil { + let l2 = Node(l1!.val) + l2.next = l1?.random + l1?.random = l2 + l1 = l1?.next + } + + let newHead = head?.random + + l1 = head + while l1 != nil { + let l2 = l1?.random + l2?.random = l2?.next?.random + l1 = l1?.next + } + + l1 = head + while l1 != nil { + let l2 = l1?.random + l1?.random = l2?.next + l2?.next = l1?.next?.random + l1 = l1?.next + } + + return newHead + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ for the output. \ No newline at end of file diff --git a/articles/count-all-valid-pickup-and-delivery-options.md b/articles/count-all-valid-pickup-and-delivery-options.md new file mode 100644 index 000000000..78b58d069 --- /dev/null +++ b/articles/count-all-valid-pickup-and-delivery-options.md @@ -0,0 +1,668 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def countOrders(self, n: int) -> int: + MOD = 1000000007 + + def dfs(picked, delivered): + if picked == n and delivered == n: + return 1 + + res = 0 + if picked < n: + res = (res + (n - picked) * dfs(picked + 1, delivered)) % MOD + if delivered < picked: + res = (res + (picked - delivered) * dfs(picked, delivered + 1)) % MOD + + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int countOrders(int n) { + return dfs(0, 0, n); + } + + private int dfs(int picked, int delivered, int n) { + if (picked == n && delivered == n) { + return 1; + } + + long res = 0; + if (picked < n) { + res = ((res + (n - picked) * 1L * dfs(picked + 1, delivered, n)) % MOD); + } + if (delivered < picked) { + res = ((res + (picked - delivered) * 1L * dfs(picked, delivered + 1, n)) % MOD); + } + + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + static const int MOD = 1'000'000'007; + + int countOrders(int n) { + return dfs(0, 0, n); + } + +private: + int dfs(int picked, int delivered, int n) { + if (picked == n && delivered == n) { + return 1; + } + + int res = 0; + if (picked < n) { + res = (res + (n - picked) * 1LL * dfs(picked + 1, delivered, n)) % MOD; + } + if (delivered < picked) { + res = (res + (picked - delivered) * 1LL * dfs(picked, delivered + 1, n)) % MOD; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countOrders(n) { + const MOD = 1_000_000_007; + + const dfs = (picked, delivered) => { + if (picked === n && delivered === n) { + return 1; + } + + let res = 0; + if (picked < n) { + res = (res + (n - picked) * dfs(picked + 1, delivered)) % MOD; + } + if (delivered < picked) { + res = (res + (picked - delivered) * dfs(picked, delivered + 1)) % MOD; + } + + return res; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def countOrders(self, n: int) -> int: + MOD = 1000000007 + dp = [[-1] * (n + 1) for _ in range(n + 1)] + dp[n][n] = 1 + + def dfs(picked, delivered): + if dp[picked][delivered] != -1: + return dp[picked][delivered] + + res = 0 + if picked < n: + res = (res + (n - picked) * dfs(picked + 1, delivered)) % MOD + if delivered < picked: + res = (res + (picked - delivered) * dfs(picked, delivered + 1)) % MOD + + dp[picked][delivered] = res + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + private int[][] dp; + + public int countOrders(int n) { + dp = new int[n + 1][n + 1]; + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= n; j++) { + dp[i][j] = -1; + } + } + dp[n][n] = 1; + return dfs(0, 0, n); + } + + private int dfs(int picked, int delivered, int n) { + if (dp[picked][delivered] != -1) { + return dp[picked][delivered]; + } + + long res = 0; + if (picked < n) { + res = ((res + (n - picked) * 1L * dfs(picked + 1, delivered, n)) % MOD); + } + if (delivered < picked) { + res = ((res + (picked - delivered) * 1L * dfs(picked, delivered + 1, n)) % MOD); + } + + return dp[picked][delivered] = (int)res; + } +} +``` + +```cpp +class Solution { +public: + static const int MOD = 1'000'000'007; + vector> dp; + + int countOrders(int n) { + dp.assign(n + 1, vector(n + 1, -1)); + dp[n][n] = 1; + return dfs(0, 0, n); + } + +private: + int dfs(int picked, int delivered, int n) { + if (dp[picked][delivered] != -1) { + return dp[picked][delivered]; + } + + int res = 0; + if (picked < n) { + res = (res + (n - picked) * 1LL * dfs(picked + 1, delivered, n)) % MOD; + } + if (delivered < picked) { + res = (res + (picked - delivered) * 1LL * dfs(picked, delivered + 1, n)) % MOD; + } + + return dp[picked][delivered] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countOrders(n) { + const MOD = 1_000_000_007; + const dp = Array.from({ length: n + 1 }, () => Array(n + 1).fill(-1)); + + const dfs = (picked, delivered) => { + if (picked === n && delivered === n) { + return 1; + } + if (dp[picked][delivered] !== -1) { + return dp[picked][delivered]; + } + + let res = 0; + if (picked < n) { + res = (res + (n - picked) * dfs(picked + 1, delivered)) % MOD; + } + if (delivered < picked) { + res = (res + (picked - delivered) * dfs(picked, delivered + 1)) % MOD; + } + + dp[picked][delivered] = res; + return res; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def countOrders(self, n: int) -> int: + MOD = 1000000007 + dp = [[0] * (n + 1) for _ in range(n + 1)] + dp[0][0] = 1 + + for picked in range(n + 1): + for delivered in range(n + 1): + if picked < n: + dp[picked + 1][delivered] = ( + (dp[picked + 1][delivered] + + (n - picked) * dp[picked][delivered]) % MOD + ) + + if delivered < picked: + dp[picked][delivered + 1] = ( + (dp[picked][delivered + 1] + + (picked - delivered) * dp[picked][delivered]) % MOD + ) + + return dp[n][n] +``` + +```java +public class Solution { + public int countOrders(int n) { + final int MOD = 1_000_000_007; + int[][] dp = new int[n + 1][n + 1]; + dp[0][0] = 1; + + for (int picked = 0; picked <= n; picked++) { + for (int delivered = 0; delivered <= n; delivered++) { + if (picked < n) { + dp[picked + 1][delivered] = (int) ((dp[picked + 1][delivered] + + (n - picked) * 1L * dp[picked][delivered]) % MOD); + } + if (delivered < picked) { + dp[picked][delivered + 1] = (int) ((dp[picked][delivered + 1] + + (picked - delivered) * 1L * dp[picked][delivered]) % MOD); + } + } + } + + return dp[n][n]; + } +} +``` + +```cpp +class Solution { +public: + int countOrders(int n) { + const int MOD = 1'000'000'007; + vector> dp(n + 1, vector(n + 1, 0)); + dp[0][0] = 1; + + for (int picked = 0; picked <= n; picked++) { + for (int delivered = 0; delivered <= n; delivered++) { + if (picked < n) { + dp[picked + 1][delivered] = (dp[picked + 1][delivered] + + (n - picked) * 1LL * dp[picked][delivered]) % MOD; + } + if (delivered < picked) { + dp[picked][delivered + 1] = (dp[picked][delivered + 1] + + (picked - delivered) * 1LL * dp[picked][delivered]) % MOD; + } + } + } + + return dp[n][n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countOrders(n) { + const MOD = 1_000_000_007; + const dp = Array.from({ length: n + 1 }, () => Array(n + 1).fill(0)); + dp[0][0] = 1; + + for (let picked = 0; picked <= n; picked++) { + for (let delivered = 0; delivered <= n; delivered++) { + if (picked < n) { + dp[picked + 1][delivered] = ( + (dp[picked + 1][delivered] + + (n - picked) * dp[picked][delivered]) % MOD + ); + } + + if (delivered < picked) { + dp[picked][delivered + 1] = ( + (dp[picked][delivered + 1] + + (picked - delivered) * dp[picked][delivered]) % MOD + ); + } + } + } + + return dp[n][n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def countOrders(self, n: int) -> int: + MOD = 1000000007 + dp = [0] * (n + 1) + dp[0] = 1 + + for picked in range(n + 1): + for delivered in range(picked): + dp[delivered + 1] = ( + (dp[delivered + 1] + + (picked - delivered) * dp[delivered]) % MOD + ) + + if picked < n: + next_dp = [0] * (n + 1) + for delivered in range(picked + 1): + next_dp[delivered] = ( + (next_dp[delivered] + + (n - picked) * dp[delivered]) % MOD + ) + dp = next_dp + + return dp[n] +``` + +```java +public class Solution { + public int countOrders(int n) { + int MOD = 1000000007; + int[] dp = new int[n + 1]; + dp[0] = 1; + + for (int picked = 0; picked <= n; picked++) { + for (int delivered = 0; delivered < picked; delivered++) { + dp[delivered + 1] = (int)((dp[delivered + 1] + + (picked - delivered) * 1L * dp[delivered]) % MOD); + } + + if (picked < n) { + int[] next_dp = new int[n + 1]; + for (int delivered = 0; delivered <= picked; delivered++) { + next_dp[delivered] = (int)((next_dp[delivered] + + (n - picked) * 1L *dp[delivered]) % MOD); + } + dp = next_dp; + } + } + + return dp[n]; + } +} +``` + +```cpp +class Solution { +public: + int countOrders(int n) { + const int MOD = 1000000007; + vector dp(n + 1); + dp[0] = 1; + + for (int picked = 0; picked <= n; picked++) { + for (int delivered = 0; delivered < picked; delivered++) { + dp[delivered + 1] = (int)((dp[delivered + 1] + + (picked - delivered) * 1LL * dp[delivered]) % MOD); + } + if (picked < n) { + vector next_dp(n + 1); + for (int delivered = 0; delivered <= picked; delivered++) { + next_dp[delivered] = (int)((next_dp[delivered] + + (n - picked) * 1LL * dp[delivered]) % MOD); + } + dp = next_dp; + } + } + + return dp[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countOrders(n) { + const MOD = 1000000007; + let dp = new Array(n + 1).fill(0); + dp[0] = 1; + + for (let picked = 0; picked <= n; picked++) { + for (let delivered = 0; delivered < picked; delivered++) { + dp[delivered + 1] = ( + (dp[delivered + 1] + + (picked - delivered) * dp[delivered]) % MOD + ); + } + + if (picked < n) { + let next_dp = new Array(n + 1).fill(0); + for (let delivered = 0; delivered <= picked; delivered++) { + next_dp[delivered] = ( + (next_dp[delivered] + + (n - picked) * dp[delivered]) % MOD + ); + } + dp = next_dp; + } + } + + return dp[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 5. Combinatorics + +::tabs-start + +```python +class Solution: + def countOrders(self, n: int) -> int: + MOD = 1000000007 + slots, res = 2 * n, 1 + while slots > 0: + valid_choices = slots * (slots - 1) // 2 + res = (res * valid_choices) % MOD + slots -= 2 + return res +``` + +```java +public class Solution { + public int countOrders(int n) { + int MOD = 1000000007; + long slots = 2 * n, res = 1; + + while (slots > 0) { + long validChoices = slots * (slots - 1) / 2; + res = (res * validChoices) % MOD; + slots -= 2; + } + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + int countOrders(int n) { + const int MOD = 1000000007; + long long slots = 2 * n, res = 1; + + while (slots > 0) { + long long validChoices = slots * (slots - 1) / 2; + res = (res * validChoices) % MOD; + slots -= 2; + } + return (int) res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countOrders(n) { + const MOD = 1000000007; + let slots = 2 * n, res = 1; + + while (slots > 0) { + let validChoices = (slots * (slots - 1)) / 2; + res = (res * validChoices) % MOD; + slots -= 2; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 6. Probability + +::tabs-start + +```python +class Solution: + def countOrders(self, n: int) -> int: + MOD = 1000000007 + res = 1 + + for slot in range(1, 2 * n + 1): + res *= slot + if slot % 2 == 0: + res >>= 1 + res %= MOD + + return res +``` + +```java +public class Solution { + public int countOrders(int n) { + int MOD = 1000000007; + long res = 1; + + for (int slot = 1; slot <= 2 * n; slot++) { + res *= slot; + if (slot % 2 == 0) { + res >>= 1; + } + res %= MOD; + } + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + int countOrders(int n) { + const int MOD = 1000000007; + long long res = 1; + + for (int slot = 1; slot <= 2 * n; slot++) { + res *= slot; + if (slot % 2 == 0) { + res >>= 1; + } + res %= MOD; + } + return (int) res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countOrders(n) { + const MOD = BigInt(1000000007); + let res = BigInt(1); + + for (let slot = 1; slot <= 2 * n; slot++) { + res *= BigInt(slot); + if (slot % 2 === 0) { + res /= BigInt(2); + } + res %= MOD; + } + return Number(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/count-connected-components.md b/articles/count-connected-components.md new file mode 100644 index 000000000..c31580864 --- /dev/null +++ b/articles/count-connected-components.md @@ -0,0 +1,1009 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def countComponents(self, n: int, edges: List[List[int]]) -> int: + adj = [[] for _ in range(n)] + visit = [False] * n + for u, v in edges: + adj[u].append(v) + adj[v].append(u) + + def dfs(node): + for nei in adj[node]: + if not visit[nei]: + visit[nei] = True + dfs(nei) + + res = 0 + for node in range(n): + if not visit[node]: + visit[node] = True + dfs(node) + res += 1 + return res +``` + +```java +public class Solution { + public int countComponents(int n, int[][] edges) { + List> adj = new ArrayList<>(); + boolean[] visit = new boolean[n]; + for (int i = 0; i < n; i++) { + adj.add(new ArrayList<>()); + } + for (int[] edge : edges) { + adj.get(edge[0]).add(edge[1]); + adj.get(edge[1]).add(edge[0]); + } + + int res = 0; + for (int node = 0; node < n; node++) { + if (!visit[node]) { + dfs(adj, visit, node); + res++; + } + } + return res; + } + + private void dfs(List> adj, boolean[] visit, int node) { + visit[node] = true; + for (int nei : adj.get(node)) { + if (!visit[nei]) { + dfs(adj, visit, nei); + } + } + } +} +``` + +```cpp +class Solution { +public: + int countComponents(int n, vector>& edges) { + vector> adj(n); + vector visit(n, false); + for (const auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + int res = 0; + for (int node = 0; node < n; ++node) { + if (!visit[node]) { + dfs(adj, visit, node); + res++; + } + } + return res; + } + +private: + void dfs(const vector>& adj, vector& visit, int node) { + visit[node] = true; + for (int nei : adj[node]) { + if (!visit[nei]) { + dfs(adj, visit, nei); + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @returns {number} + */ + countComponents(n, edges) { + const adj = Array.from({ length: n }, () => []); + const visit = Array(n).fill(false); + + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + } + + const dfs = (node) => { + for (const nei of adj[node]) { + if (!visit[nei]) { + visit[nei] = true; + dfs(nei); + } + } + }; + + let res = 0; + for (let node = 0; node < n; node++) { + if (!visit[node]) { + visit[node] = true; + dfs(node); + res++; + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int CountComponents(int n, int[][] edges) { + List> adj = new List>(); + bool[] visit = new bool[n]; + for (int i = 0; i < n; i++) { + adj.Add(new List()); + } + foreach (var edge in edges) { + adj[edge[0]].Add(edge[1]); + adj[edge[1]].Add(edge[0]); + } + + int res = 0; + for (int node = 0; node < n; node++) { + if (!visit[node]) { + Dfs(adj, visit, node); + res++; + } + } + return res; + } + + private void Dfs(List> adj, bool[] visit, int node) { + visit[node] = true; + foreach (var nei in adj[node]) { + if (!visit[nei]) { + Dfs(adj, visit, nei); + } + } + } +} +``` + +```go +func countComponents(n int, edges [][]int) int { + adj := make([][]int, n) + visit := make([]bool, n) + for _, edge := range edges { + u, v := edge[0], edge[1] + adj[u] = append(adj[u], v) + adj[v] = append(adj[v], u) + } + + var dfs func(int) + dfs = func(node int) { + for _, nei := range adj[node] { + if !visit[nei] { + visit[nei] = true + dfs(nei) + } + } + } + + res := 0 + for node := 0; node < n; node++ { + if !visit[node] { + visit[node] = true + dfs(node) + res++ + } + } + return res +} +``` + +```kotlin +class Solution { + fun countComponents(n: Int, edges: Array): Int { + val adj = Array(n) { mutableListOf() } + val visit = BooleanArray(n) + for ((u, v) in edges) { + adj[u].add(v) + adj[v].add(u) + } + + fun dfs(node: Int) { + for (nei in adj[node]) { + if (!visit[nei]) { + visit[nei] = true + dfs(nei) + } + } + } + + var res = 0 + for (node in 0 until n) { + if (!visit[node]) { + visit[node] = true + dfs(node) + res++ + } + } + return res + } +} +``` + +```swift +class Solution { + func countComponents(_ n: Int, _ edges: [[Int]]) -> Int { + var adj = Array(repeating: [Int](), count: n) + var visit = Array(repeating: false, count: n) + + for edge in edges { + let u = edge[0], v = edge[1] + adj[u].append(v) + adj[v].append(u) + } + + func dfs(_ node: Int) { + for nei in adj[node] { + if !visit[nei] { + visit[nei] = true + dfs(nei) + } + } + } + + var res = 0 + for node in 0.. Where $V$ is the number of vertices and $E$ is the number of edges in the graph. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def countComponents(self, n: int, edges: List[List[int]]) -> int: + adj = [[] for _ in range(n)] + visit = [False] * n + for u, v in edges: + adj[u].append(v) + adj[v].append(u) + + def bfs(node): + q = deque([node]) + visit[node] = True + while q: + cur = q.popleft() + for nei in adj[cur]: + if not visit[nei]: + visit[nei] = True + q.append(nei) + + res = 0 + for node in range(n): + if not visit[node]: + bfs(node) + res += 1 + return res +``` + +```java +public class Solution { + public int countComponents(int n, int[][] edges) { + List> adj = new ArrayList<>(); + boolean[] visit = new boolean[n]; + for (int i = 0; i < n; i++) { + adj.add(new ArrayList<>()); + } + for (int[] edge : edges) { + adj.get(edge[0]).add(edge[1]); + adj.get(edge[1]).add(edge[0]); + } + + int res = 0; + for (int node = 0; node < n; node++) { + if (!visit[node]) { + bfs(adj, visit, node); + res++; + } + } + return res; + } + + private void bfs(List> adj, boolean[] visit, int node) { + Queue q = new LinkedList<>(); + q.offer(node); + visit[node] = true; + while (!q.isEmpty()) { + int cur = q.poll(); + for (int nei : adj.get(cur)) { + if (!visit[nei]) { + visit[nei] = true; + q.offer(nei); + } + } + } + } +} +``` + +```cpp +class Solution { +public: + int countComponents(int n, vector>& edges) { + vector> adj(n); + vector visit(n, false); + for (const auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + int res = 0; + for (int node = 0; node < n; ++node) { + if (!visit[node]) { + bfs(adj, visit, node); + res++; + } + } + return res; + } + +private: + void bfs(vector>& adj, vector& visit, int node) { + queue q; + q.push(node); + visit[node] = true; + while (!q.empty()) { + int cur = q.front(); + q.pop(); + for (int nei : adj[cur]) { + if (!visit[nei]) { + visit[nei] = true; + q.push(nei); + } + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @returns {number} + */ + countComponents(n, edges) { + const adj = Array.from({ length: n }, () => []); + const visit = Array(n).fill(false); + + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + } + + const bfs = (node) => { + const q = new Queue([node]); + visit[node] = true; + while (!q.isEmpty()) { + const cur = q.pop(); + for (const nei of adj[cur]) { + if (!visit[nei]) { + visit[nei] = true; + q.push(nei); + } + } + } + }; + + let res = 0; + for (let node = 0; node < n; node++) { + if (!visit[node]) { + bfs(node); + res++; + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int CountComponents(int n, int[][] edges) { + List> adj = new List>(); + bool[] visit = new bool[n]; + for (int i = 0; i < n; i++) { + adj.Add(new List()); + } + foreach (var edge in edges) { + adj[edge[0]].Add(edge[1]); + adj[edge[1]].Add(edge[0]); + } + + int res = 0; + for (int node = 0; node < n; node++) { + if (!visit[node]) { + Bfs(adj, visit, node); + res++; + } + } + return res; + } + + private void Bfs(List> adj, bool[] visit, int node) { + Queue q = new Queue(); + q.Enqueue(node); + visit[node] = true; + while (q.Count > 0) { + int cur = q.Dequeue(); + foreach (var nei in adj[cur]) { + if (!visit[nei]) { + visit[nei] = true; + q.Enqueue(nei); + } + } + } + } +} +``` + +```go +func countComponents(n int, edges [][]int) int { + adj := make([][]int, n) + visit := make([]bool, n) + for _, edge := range edges { + u, v := edge[0], edge[1] + adj[u] = append(adj[u], v) + adj[v] = append(adj[v], u) + } + + bfs := func(node int) { + q := []int{node} + visit[node] = true + for len(q) > 0 { + cur := q[0] + q = q[1:] + for _, nei := range adj[cur] { + if !visit[nei] { + visit[nei] = true + q = append(q, nei) + } + } + } + } + + res := 0 + for node := 0; node < n; node++ { + if !visit[node] { + bfs(node) + res++ + } + } + return res +} +``` + +```kotlin +class Solution { + fun countComponents(n: Int, edges: Array): Int { + val adj = Array(n) { mutableListOf() } + val visit = BooleanArray(n) + for ((u, v) in edges) { + adj[u].add(v) + adj[v].add(u) + } + + fun bfs(node: Int) { + val q: Queue = LinkedList() + q.offer(node) + visit[node] = true + while (q.isNotEmpty()) { + val cur = q.poll() + for (nei in adj[cur]) { + if (!visit[nei]) { + visit[nei] = true + q.offer(nei) + } + } + } + } + + var res = 0 + for (node in 0 until n) { + if (!visit[node]) { + bfs(node) + res++ + } + } + return res + } +} +``` + +```swift +class Solution { + func countComponents(_ n: Int, _ edges: [[Int]]) -> Int { + var adj = Array(repeating: [Int](), count: n) + var visit = Array(repeating: false, count: n) + for edge in edges { + let u = edge[0], v = edge[1] + adj[u].append(v) + adj[v].append(u) + } + + func bfs(_ node: Int) { + var q = Deque() + q.append(node) + visit[node] = true + while !q.isEmpty { + let cur = q.removeFirst() + for nei in adj[cur] { + if !visit[nei] { + visit[nei] = true + q.append(nei) + } + } + } + } + + var res = 0 + for node in 0.. Where $V$ is the number of vertices and $E$ is the number of edges in the graph. + +--- + +## 3. Disjoint Set Union (Rank | Size) + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.parent = list(range(n)) + self.rank = [1] * n + + def find(self, node): + cur = node + while cur != self.parent[cur]: + self.parent[cur] = self.parent[self.parent[cur]] + cur = self.parent[cur] + return cur + + def union(self, u, v): + pu = self.find(u) + pv = self.find(v) + if pu == pv: + return False + if self.rank[pv] > self.rank[pu]: + pu, pv = pv, pu + self.parent[pv] = pu + self.rank[pu] += self.rank[pv] + return True + +class Solution: + def countComponents(self, n: int, edges: List[List[int]]) -> int: + dsu = DSU(n) + res = n + for u, v in edges: + if dsu.union(u, v): + res -= 1 + return res +``` + +```java +class DSU { + int[] parent; + int[] rank; + + public DSU(int n) { + parent = new int[n]; + rank = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + rank[i] = 1; + } + } + + public int find(int node) { + int cur = node; + while (cur != parent[cur]) { + parent[cur] = parent[parent[cur]]; + cur = parent[cur]; + } + return cur; + } + + public boolean union(int u, int v) { + int pu = find(u); + int pv = find(v); + if (pu == pv) { + return false; + } + if (rank[pv] > rank[pu]) { + int temp = pu; + pu = pv; + pv = temp; + } + parent[pv] = pu; + rank[pu] += rank[pv]; + return true; + } +} + +public class Solution { + public int countComponents(int n, int[][] edges) { + DSU dsu = new DSU(n); + int res = n; + for (int[] edge : edges) { + if (dsu.union(edge[0], edge[1])) { + res--; + } + } + return res; + } +} +``` + +```cpp +class DSU { +public: + vector parent; + vector rank; + + DSU(int n) { + parent.resize(n); + rank.resize(n, 1); + for (int i = 0; i < n; i++) { + parent[i] = i; + } + } + + int find(int node) { + int cur = node; + while (cur != parent[cur]) { + parent[cur] = parent[parent[cur]]; + cur = parent[cur]; + } + return cur; + } + + bool unionSets(int u, int v) { + int pu = find(u); + int pv = find(v); + if (pu == pv) { + return false; + } + if (rank[pv] > rank[pu]) { + swap(pu, pv); + } + parent[pv] = pu; + rank[pu] += rank[pv]; + return true; + } +}; + +class Solution { +public: + int countComponents(int n, vector>& edges) { + DSU dsu(n); + int res = n; + for (auto& edge : edges) { + if (dsu.unionSets(edge[0], edge[1])) { + res--; + } + } + return res; + } +}; +``` + +```javascript +class DSU { + /** + * @param {number} n + */ + constructor(n) { + this.parent = Array.from({ length: n }, (_, i) => i); + this.rank = Array(n).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + let cur = node; + while (cur !== this.parent[cur]) { + this.parent[cur] = this.parent[this.parent[cur]]; + cur = this.parent[cur]; + } + return cur; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u); + let pv = this.find(v); + if (pu === pv) { + return false; + } + if (this.rank[pv] > this.rank[pu]) { + [pu, pv] = [pv, pu]; + } + this.parent[pv] = pu; + this.rank[pu] += this.rank[pv]; + return true; + } +} + +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @returns {number} + */ + countComponents(n, edges) { + const dsu = new DSU(n); + let res = n; + for (const [u, v] of edges) { + if (dsu.union(u, v)) { + res--; + } + } + return res; + } +} +``` + +```csharp +public class DSU { + private int[] parent; + private int[] rank; + + public DSU(int n) { + parent = new int[n]; + rank = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + rank[i] = 1; + } + } + + public int Find(int node) { + int cur = node; + while (cur != parent[cur]) { + parent[cur] = parent[parent[cur]]; + cur = parent[cur]; + } + return cur; + } + + public bool Union(int u, int v) { + int pu = Find(u); + int pv = Find(v); + if (pu == pv) { + return false; + } + if (rank[pv] > rank[pu]) { + int temp = pu; + pu = pv; + pv = temp; + } + parent[pv] = pu; + rank[pu] += rank[pv]; + return true; + } +} + +public class Solution { + public int CountComponents(int n, int[][] edges) { + DSU dsu = new DSU(n); + int res = n; + foreach (var edge in edges) { + if (dsu.Union(edge[0], edge[1])) { + res--; + } + } + return res; + } +} +``` + +```go +type DSU struct { + parent []int + rank []int +} + +func NewDSU(n int) *DSU { + dsu := &DSU{ + parent: make([]int, n), + rank: make([]int, n), + } + for i := 0; i < n; i++ { + dsu.parent[i] = i + dsu.rank[i] = 1 + } + return dsu +} + +func (dsu *DSU) Find(node int) int { + cur := node + for cur != dsu.parent[cur] { + dsu.parent[cur] = dsu.parent[dsu.parent[cur]] + cur = dsu.parent[cur] + } + return cur +} + +func (dsu *DSU) Union(u, v int) bool { + pu := dsu.Find(u) + pv := dsu.Find(v) + if pu == pv { + return false + } + if dsu.rank[pv] > dsu.rank[pu] { + pu, pv = pv, pu + } + dsu.parent[pv] = pu + dsu.rank[pu] += dsu.rank[pv] + return true +} + +func countComponents(n int, edges [][]int) int { + dsu := NewDSU(n) + res := n + for _, edge := range edges { + u, v := edge[0], edge[1] + if dsu.Union(u, v) { + res-- + } + } + return res +} +``` + +```kotlin +class DSU(n: Int) { + val parent = IntArray(n) { it } + val rank = IntArray(n) { 1 } + + fun find(node: Int): Int { + var cur = node + while (cur != parent[cur]) { + parent[cur] = parent[parent[cur]] + cur = parent[cur] + } + return cur + } + + fun union(u: Int, v: Int): Boolean { + val pu = find(u) + val pv = find(v) + if (pu == pv) { + return false + } + if (rank[pv] > rank[pu]) { + parent[pu] = pv + } else { + parent[pv] = pu + rank[pu] += rank[pv] + } + return true + } +} + +class Solution { + fun countComponents(n: Int, edges: Array): Int { + val dsu = DSU(n) + var res = n + for (edge in edges) { + val (u, v) = edge + if (dsu.union(u, v)) { + res-- + } + } + return res + } +} +``` + +```swift +class DSU { + var parent: [Int] + var rank: [Int] + + init(_ n: Int) { + parent = Array(0.. Int { + var cur = node + while cur != parent[cur] { + parent[cur] = parent[parent[cur]] + cur = parent[cur] + } + return cur + } + + func union(_ u: Int, _ v: Int) -> Bool { + let pu = find(u) + let pv = find(v) + if pu == pv { return false } + var rootU = pu + var rootV = pv + if rank[rootV] > rank[rootU] { + swap(&rootU, &rootV) + } + parent[rootV] = rootU + rank[rootU] += rank[rootV] + return true + } +} + +class Solution { + func countComponents(_ n: Int, _ edges: [[Int]]) -> Int { + let dsu = DSU(n) + var res = n + for edge in edges { + let u = edge[0], v = edge[1] + if dsu.union(u, v) { + res -= 1 + } + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + (E * α(V)))$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges in the graph. $α()$ is used for amortized complexity. \ No newline at end of file diff --git a/articles/count-good-nodes-in-binary-tree.md b/articles/count-good-nodes-in-binary-tree.md new file mode 100644 index 000000000..d9356f32c --- /dev/null +++ b/articles/count-good-nodes-in-binary-tree.md @@ -0,0 +1,631 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def goodNodes(self, root: TreeNode) -> int: + + def dfs(node, maxVal): + if not node: + return 0 + + res = 1 if node.val >= maxVal else 0 + maxVal = max(maxVal, node.val) + res += dfs(node.left, maxVal) + res += dfs(node.right, maxVal) + return res + + return dfs(root, root.val) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + + public int goodNodes(TreeNode root) { + return dfs(root, root.val); + } + + private int dfs(TreeNode node, int maxVal) { + if (node == null) { + return 0; + } + + int res = (node.val >= maxVal) ? 1 : 0; + maxVal = Math.max(maxVal, node.val); + res += dfs(node.left, maxVal); + res += dfs(node.right, maxVal); + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + int goodNodes(TreeNode* root) { + return dfs(root, root->val); + } + +private: + int dfs(TreeNode* node, int maxVal) { + if (!node) { + return 0; + } + + int res = (node->val >= maxVal) ? 1 : 0; + maxVal = max(maxVal, node->val); + res += dfs(node->left, maxVal); + res += dfs(node->right, maxVal); + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + goodNodes(root) { + return this.dfs(root, root.val); + } + + /** + * @param {TreeNode} node + * @param {number} maxVal + * @return {number} + */ + dfs(node, maxVal) { + if (!node) { + return 0; + } + + let res = node.val >= maxVal ? 1 : 0; + maxVal = Math.max(maxVal, node.val); + res += this.dfs(node.left, maxVal); + res += this.dfs(node.right, maxVal); + return res; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + + public int GoodNodes(TreeNode root) { + return Dfs(root, root.val); + } + + private int Dfs(TreeNode node, int maxVal) { + if (node == null) { + return 0; + } + + int res = (node.val >= maxVal) ? 1 : 0; + maxVal = Math.Max(maxVal, node.val); + res += Dfs(node.left, maxVal); + res += Dfs(node.right, maxVal); + return res; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func goodNodes(root *TreeNode) int { + if root == nil { + return 0 + } + + var dfs func(node *TreeNode, maxVal int) int + dfs = func(node *TreeNode, maxVal int) int { + if node == nil { + return 0 + } + + res := 0 + if node.Val >= maxVal { + res = 1 + } + + maxVal = max(maxVal, node.Val) + res += dfs(node.Left, maxVal) + res += dfs(node.Right, maxVal) + + return res + } + + return dfs(root, root.Val) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun goodNodes(root: TreeNode?): Int { + if (root == null) return 0 + + fun dfs(node: TreeNode?, maxVal: Int): Int { + if (node == null) return 0 + + var res = 0 + if (node.`val` >= maxVal) { + res = 1 + } + + val newMaxVal = maxOf(maxVal, node.`val`) + res += dfs(node.left, newMaxVal) + res += dfs(node.right, newMaxVal) + + return res + } + + return dfs(root, root.`val`) + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func goodNodes(_ root: TreeNode?) -> Int { + func dfs(_ node: TreeNode?, _ maxVal: Int) -> Int { + guard let node = node else { return 0 } + + var res = node.val >= maxVal ? 1 : 0 + let newMaxVal = max(maxVal, node.val) + res += dfs(node.left, newMaxVal) + res += dfs(node.right, newMaxVal) + return res + } + + return dfs(root, root?.val ?? Int.min) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def goodNodes(self, root: TreeNode) -> int: + res = 0 + q = deque() + + q.append((root,-float('inf'))) + + while q: + node,maxval = q.popleft() + if node.val >= maxval: + res += 1 + + if node.left: + q.append((node.left,max(maxval,node.val))) + + if node.right: + q.append((node.right,max(maxval,node.val))) + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public int goodNodes(TreeNode root) { + int res = 0; + Queue> q = new LinkedList<>(); + q.offer(new Pair<>(root, Integer.MIN_VALUE)); + + while (!q.isEmpty()) { + Pair pair = q.poll(); + TreeNode node = pair.getKey(); + int maxval = pair.getValue(); + if (node.val >= maxval) { + res++; + } + if (node.left != null) { + q.offer(new Pair<>(node.left, Math.max(maxval, node.val))); + } + if (node.right != null) { + q.offer(new Pair<>(node.right, Math.max(maxval, node.val))); + } + } + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + int goodNodes(TreeNode* root) { + int res = 0; + queue> q; + q.push({root, -INT_MAX}); + + while (!q.empty()) { + auto [node, maxval] = q.front(); + q.pop(); + if (node->val >= maxval) { + res++; + } + if (node->left) { + q.push({node->left, max(maxval, node->val)}); + } + if (node->right) { + q.push({node->right, max(maxval, node->val)}); + } + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + goodNodes(root) { + let res = 0; + let q = new Queue(); + q.push([root, -Infinity]); + + while (!q.isEmpty()) { + let [node, maxval] = q.pop(); + if (node.val >= maxval) { + res++; + } + if (node.left) { + q.push([node.left, Math.max(maxval, node.val)]); + } + if (node.right) { + q.push([node.right, Math.max(maxval, node.val)]); + } + } + return res; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public int GoodNodes(TreeNode root) { + int res = 0; + Queue<(TreeNode, int)> q = new Queue<(TreeNode, int)>(); + q.Enqueue((root, int.MinValue)); + + while (q.Count > 0) { + var (node, maxval) = q.Dequeue(); + if (node.val >= maxval) { + res++; + } + if (node.left != null) { + q.Enqueue((node.left, Math.Max(maxval, node.val))); + } + if (node.right != null) { + q.Enqueue((node.right, Math.Max(maxval, node.val))); + } + } + return res; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func goodNodes(root *TreeNode) int { + if root == nil { + return 0 + } + + res := 0 + q := []struct { + node *TreeNode + maxVal int + }{{root, math.MinInt32}} + + for len(q) > 0 { + front := q[0] + q = q[1:] + + node := front.node + maxVal := front.maxVal + + if node.Val >= maxVal { + res++ + } + + newMaxVal := max(maxVal, node.Val) + + if node.Left != nil { + q = append(q, struct { + node *TreeNode + maxVal int + }{node.Left, newMaxVal}) + } + + if node.Right != nil { + q = append(q, struct { + node *TreeNode + maxVal int + }{node.Right, newMaxVal}) + } + } + + return res +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun goodNodes(root: TreeNode?): Int { + if (root == null) return 0 + + var res = 0 + val q = ArrayDeque>() + q.add(root to Int.MIN_VALUE) + + while (q.isNotEmpty()) { + val (node, maxVal) = q.removeFirst() + + if (node.`val` >= maxVal) { + res++ + } + + val newMaxVal = maxOf(maxVal, node.`val`) + + node.left?.let { q.add(it to newMaxVal) } + node.right?.let { q.add(it to newMaxVal) } + } + + return res + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func goodNodes(_ root: TreeNode?) -> Int { + guard let root = root else { return 0 } + + var res = 0 + var q = Deque<(TreeNode, Int)>() + q.append((root, Int.min)) + + while !q.isEmpty { + let (node, maxVal) = q.popFirst()! + if node.val >= maxVal { + res += 1 + } + + let newMaxVal = max(maxVal, node.val) + + if let left = node.left { + q.append((left, newMaxVal)) + } + if let right = node.right { + q.append((right, newMaxVal)) + } + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/count-number-of-bad-pairs.md b/articles/count-number-of-bad-pairs.md new file mode 100644 index 000000000..936e3af7f --- /dev/null +++ b/articles/count-number-of-bad-pairs.md @@ -0,0 +1,157 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def countBadPairs(self, nums: List[int]) -> int: + n, res = len(nums), 0 + for i in range(n - 1): + for j in range(i + 1, n): + if j - i != nums[j] - nums[i]: + res += 1 + return res +``` + +```java +public class Solution { + public long countBadPairs(int[] nums) { + int n = nums.length; + long res = 0; + for (int i = 0; i < n - 1; i++) { + for (int j = i + 1; j < n; j++) { + if (j - i != nums[j] - nums[i]) { + res++; + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + long long countBadPairs(vector& nums) { + int n = nums.size(); + long long res = 0; + for (int i = 0; i < n - 1; i++) { + for (int j = i + 1; j < n; j++) { + if (j - i != nums[j] - nums[i]) { + res++; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + countBadPairs(nums) { + let n = nums.length, res = 0; + for (let i = 0; i < n - 1; i++) { + for (let j = i + 1; j < n; j++) { + if (j - i !== nums[j] - nums[i]) { + res++; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def countBadPairs(self, nums: List[int]) -> int: + good_pairs = 0 + total_pairs = 0 + count = defaultdict(int) + + for i in range(len(nums)): + total_pairs += i + good_pairs += count[nums[i] - i] + count[nums[i] - i] += 1 + + return total_pairs - good_pairs +``` + +```java +public class Solution { + public long countBadPairs(int[] nums) { + Map count = new HashMap<>(); + long total = 0, good = 0; + for (int i = 0; i < nums.length; i++) { + int key = nums[i] - i; + good += count.getOrDefault(key, 0); + count.put(key, count.getOrDefault(key, 0) + 1); + total += i; + } + return total - good; + } +} +``` + +```cpp +class Solution { +public: + long long countBadPairs(vector& nums) { + unordered_map count; + long long total = 0, good = 0; + for (int i = 0; i < nums.size(); i++) { + int key = nums[i] - i; + good += count[key]; + count[key]++; + total += i; + } + return total - good; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + countBadPairs(nums) { + let count = new Map(); + let total = 0, good = 0; + for (let i = 0; i < nums.length; i++) { + let key = nums[i] - i; + good += count.get(key) || 0; + count.set(key, (count.get(key) || 0) + 1); + total += i; + } + return total - good; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/count-number-of-islands.md b/articles/count-number-of-islands.md new file mode 100644 index 000000000..042d79082 --- /dev/null +++ b/articles/count-number-of-islands.md @@ -0,0 +1,1192 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def numIslands(self, grid: List[List[str]]) -> int: + directions = [[1, 0], [-1, 0], [0, 1], [0, -1]] + ROWS, COLS = len(grid), len(grid[0]) + islands = 0 + + def dfs(r, c): + if (r < 0 or c < 0 or r >= ROWS or + c >= COLS or grid[r][c] == "0" + ): + return + + grid[r][c] = "0" + for dr, dc in directions: + dfs(r + dr, c + dc) + + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] == "1": + dfs(r, c) + islands += 1 + + return islands +``` + +```java +public class Solution { + private static final int[][] directions = {{1, 0}, {-1, 0}, + {0, 1}, {0, -1}}; + + public int numIslands(char[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int islands = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == '1') { + dfs(grid, r, c); + islands++; + } + } + } + + return islands; + } + + private void dfs(char[][] grid, int r, int c) { + if (r < 0 || c < 0 || r >= grid.length || + c >= grid[0].length || grid[r][c] == '0') { + return; + } + + grid[r][c] = '0'; + for (int[] dir : directions) { + dfs(grid, r + dir[0], c + dir[1]); + } + } +} +``` + +```cpp +class Solution { + int directions[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; +public: + int numIslands(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + int islands = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == '1') { + dfs(grid, r, c); + islands++; + } + } + } + + return islands; + } + + void dfs(vector>& grid, int r, int c) { + if (r < 0 || c < 0 || r >= grid.size() || + c >= grid[0].size() || grid[r][c] == '0') { + return; + } + + grid[r][c] = '0'; + for (int i = 0; i < 4; i++) { + dfs(grid, r + directions[i][0], c + directions[i][1]); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[][]} grid + * @return {number} + */ + numIslands(grid) { + const directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]; + const ROWS = grid.length, COLS = grid[0].length; + let islands = 0; + + const dfs = (r, c) => { + if (r < 0 || c < 0 || r >= ROWS || + c >= COLS || grid[r][c] === '0') return; + + grid[r][c] = '0'; + for (const [dr, dc] of directions) { + dfs(r + dr, c + dc); + } + }; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] === '1') { + dfs(r, c); + islands++; + } + } + } + + return islands; + } +} +``` + +```csharp +public class Solution { + private static readonly int[][] directions = new int[][] { + new int[] {1, 0}, new int[] {-1, 0}, + new int[] {0, 1}, new int[] {0, -1} + }; + + public int NumIslands(char[][] grid) { + int ROWS = grid.Length, COLS = grid[0].Length; + int islands = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == '1') { + Dfs(grid, r, c); + islands++; + } + } + } + + return islands; + } + + private void Dfs(char[][] grid, int r, int c) { + if (r < 0 || c < 0 || r >= grid.Length || + c >= grid[0].Length || grid[r][c] == '0') { + return; + } + + grid[r][c] = '0'; + foreach (var dir in directions) { + Dfs(grid, r + dir[0], c + dir[1]); + } + } +} +``` + +```go +func numIslands(grid [][]byte) int { + directions := [][]int{{1, 0}, {-1, 0}, {0, 1}, {0, -1}} + rows, cols := len(grid), len(grid[0]) + islands := 0 + + var dfs func(r, c int) + dfs = func(r, c int) { + if r < 0 || c < 0 || r >= rows || + c >= cols || grid[r][c] == '0' { + return + } + grid[r][c] = '0' + for _, dir := range directions { + dfs(r+dir[0], c+dir[1]) + } + } + + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + if grid[r][c] == '1' { + dfs(r, c) + islands++ + } + } + } + + return islands +} +``` + +```kotlin +class Solution { + fun numIslands(grid: Array): Int { + val directions = arrayOf(intArrayOf(1, 0), + intArrayOf(-1, 0), + intArrayOf(0, 1), + intArrayOf(0, -1)) + val rows = grid.size + val cols = grid[0].size + var islands = 0 + + fun dfs(r: Int, c: Int) { + if (r < 0 || c < 0 || r >= rows || + c >= cols || grid[r][c] == '0') { + return + } + grid[r][c] = '0' + for (dir in directions) { + dfs(r + dir[0], c + dir[1]) + } + } + + for (r in 0 until rows) { + for (c in 0 until cols) { + if (grid[r][c] == '1') { + dfs(r, c) + islands++ + } + } + } + + return islands + } +} +``` + +```swift +class Solution { + func numIslands(_ grid: [[Character]]) -> Int { + let directions = [[1, 0], [-1, 0], [0, 1], [0, -1]] + let ROWS = grid.count + let COLS = grid[0].count + var islands = 0 + var grid = grid + + func dfs(_ r: Int, _ c: Int) { + if r < 0 || c < 0 || r >= ROWS || c >= COLS || grid[r][c] == "0" { + return + } + + grid[r][c] = "0" + for dir in directions { + dfs(r + dir[0], c + dir[1]) + } + } + + for r in 0.. Where $m$ is the number of rows and $n$ is the number of columns in the $grid$. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def numIslands(self, grid: List[List[str]]) -> int: + directions = [[1, 0], [-1, 0], [0, 1], [0, -1]] + ROWS, COLS = len(grid), len(grid[0]) + islands = 0 + + def bfs(r, c): + q = deque() + grid[r][c] = "0" + q.append((r, c)) + + while q: + row, col = q.popleft() + for dr, dc in directions: + nr, nc = dr + row, dc + col + if (nr < 0 or nc < 0 or nr >= ROWS or + nc >= COLS or grid[nr][nc] == "0" + ): + continue + q.append((nr, nc)) + grid[nr][nc] = "0" + + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] == "1": + bfs(r, c) + islands += 1 + + return islands +``` + +```java +public class Solution { + private static final int[][] directions = {{1, 0}, {-1, 0}, + {0, 1}, {0, -1}}; + + public int numIslands(char[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int islands = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == '1') { + bfs(grid, r, c); + islands++; + } + } + } + + return islands; + } + + private void bfs(char[][] grid, int r, int c) { + Queue q = new LinkedList<>(); + grid[r][c] = '0'; + q.add(new int[]{r, c}); + + while (!q.isEmpty()) { + int[] node = q.poll(); + int row = node[0], col = node[1]; + + for (int[] dir : directions) { + int nr = row + dir[0], nc = col + dir[1]; + if (nr >= 0 && nc >= 0 && nr < grid.length && + nc < grid[0].length && grid[nr][nc] == '1') { + q.add(new int[]{nr, nc}); + grid[nr][nc] = '0'; + } + } + } + } +} +``` + +```cpp +class Solution { + int directions[4][2] = {{1, 0}, {-1, 0}, + {0, 1}, {0, -1}}; +public: + int numIslands(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + int islands = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == '1') { + bfs(grid, r, c); + islands++; + } + } + } + + return islands; + } + + void bfs(vector>& grid, int r, int c) { + queue> q; + grid[r][c] = '0'; + q.push({r, c}); + + while (!q.empty()) { + auto node = q.front();q.pop(); + int row = node.first, col = node.second; + for (int i = 0; i < 4; i++) { + int nr = row + directions[i][0]; + int nc = col + directions[i][1]; + if (nr >= 0 && nc >= 0 && nr < grid.size() && + nc < grid[0].size() && grid[nr][nc] == '1') { + q.push({nr, nc}); + grid[nr][nc] = '0'; + } + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[][]} grid + * @return {number} + */ + numIslands(grid) { + const directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]; + const ROWS = grid.length, COLS = grid[0].length; + let islands = 0; + + const bfs = (r, c) => { + const q = new Queue(); + q.push([r, c]); + grid[r][c] = '0'; + + while (!q.isEmpty()) { + const [row, col] = q.pop(); + for (const [dr, dc] of directions) { + const nr = row + dr, nc = col + dc; + if (nr >= 0 && nc >= 0 && nr < ROWS && + nc < COLS && grid[nr][nc] === '1') { + q.push([nr, nc]); + grid[nr][nc] = '0'; + } + } + } + }; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] === '1') { + bfs(r, c); + islands++; + } + } + } + + return islands; + } +} +``` + +```csharp +public class Solution { + private static readonly int[][] directions = new int[][] { + new int[] {1, 0}, new int[] {-1, 0}, + new int[] {0, 1}, new int[] {0, -1} + }; + + public int NumIslands(char[][] grid) { + int ROWS = grid.Length, COLS = grid[0].Length; + int islands = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == '1') { + Bfs(grid, r, c); + islands++; + } + } + } + + return islands; + } + + private void Bfs(char[][] grid, int r, int c) { + Queue q = new Queue(); + grid[r][c] = '0'; + q.Enqueue(new int[] { r, c }); + + while (q.Count > 0) { + var node = q.Dequeue(); + int row = node[0], col = node[1]; + + foreach (var dir in directions) { + int nr = row + dir[0], nc = col + dir[1]; + if (nr >= 0 && nc >= 0 && nr < grid.Length && + nc < grid[0].Length && grid[nr][nc] == '1') { + q.Enqueue(new int[] { nr, nc }); + grid[nr][nc] = '0'; + } + } + } + } +} +``` + +```go +func numIslands(grid [][]byte) int { + directions := [][]int{{1, 0}, {-1, 0}, {0, 1}, {0, -1}} + rows, cols := len(grid), len(grid[0]) + islands := 0 + + var bfs func(r, c int) + bfs = func(r, c int) { + q := [][]int{{r, c}} + grid[r][c] = '0' + + for len(q) > 0 { + front := q[0] + q = q[1:] + row, col := front[0], front[1] + for _, dir := range directions { + nr, nc := row+dir[0], col+dir[1] + if nr < 0 || nc < 0 || nr >= rows || + nc >= cols || grid[nr][nc] == '0' { + continue + } + q = append(q, []int{nr, nc}) + grid[nr][nc] = '0' + } + } + } + + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + if grid[r][c] == '1' { + bfs(r, c) + islands++ + } + } + } + + return islands +} +``` + +```kotlin +class Solution { + fun numIslands(grid: Array): Int { + val directions = arrayOf(intArrayOf(1, 0), + intArrayOf(-1, 0), + intArrayOf(0, 1), + intArrayOf(0, -1)) + val rows = grid.size + val cols = grid[0].size + var islands = 0 + + fun bfs(r: Int, c: Int) { + val q: Queue = LinkedList() + grid[r][c] = '0' + q.add(intArrayOf(r, c)) + + while (q.isNotEmpty()) { + val (row, col) = q.poll() + for (dir in directions) { + val nr = row + dir[0] + val nc = col + dir[1] + if (nr < 0 || nc < 0 || nr >= rows || + nc >= cols || grid[nr][nc] == '0') { + continue + } + q.add(intArrayOf(nr, nc)) + grid[nr][nc] = '0' + } + } + } + + for (r in 0 until rows) { + for (c in 0 until cols) { + if (grid[r][c] == '1') { + bfs(r, c) + islands++ + } + } + } + + return islands + } +} +``` + +```swift +class Solution { + func numIslands(_ grid: [[Character]]) -> Int { + let directions = [[1, 0], [-1, 0], [0, 1], [0, -1]] + let ROWS = grid.count + let COLS = grid[0].count + var islands = 0 + var grid = grid + + func bfs(_ r: Int, _ c: Int) { + var queue = Deque<(Int, Int)>() + grid[r][c] = "0" + queue.append((r, c)) + + while !queue.isEmpty { + let (row, col) = queue.popFirst()! + for dir in directions { + let nr = row + dir[0] + let nc = col + dir[1] + if nr < 0 || nc < 0 || nr >= ROWS || nc >= COLS || grid[nr][nc] == "0" { + continue + } + queue.append((nr, nc)) + grid[nr][nc] = "0" + } + } + } + + for r in 0.. Where $m$ is the number of rows and $n$ is the number of columns in the $grid$. + +--- + +## 3. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu = self.find(u) + pv = self.find(v) + if pu == pv: + return False + if self.Size[pu] >= self.Size[pv]: + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + else: + self.Size[pv] += self.Size[pu] + self.Parent[pu] = pv + return True + +class Solution: + def numIslands(self, grid: List[List[str]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + dsu = DSU(ROWS * COLS) + + def index(r, c): + return r * COLS + c + + directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + islands = 0 + + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] == '1': + islands += 1 + for dr, dc in directions: + nr, nc = r + dr, c + dc + if (nr < 0 or nc < 0 or nr >= ROWS or + nc >= COLS or grid[nr][nc] == "0" + ): + continue + + if dsu.union(index(r, c), index(nr, nc)): + islands -= 1 + + return islands +``` + +```java +class DSU { + private int[] Parent, Size; + + public DSU(int n) { + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + public int find(int node) { + if (node != Parent[node]) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u); + int pv = find(v); + if (pu == pv) return false; + if (Size[pu] >= Size[pv]) { + Size[pu] += Size[pv]; + Parent[pv] = pu; + } else { + Size[pv] += Size[pu]; + Parent[pu] = pv; + } + return true; + } +} + +public class Solution { + public int numIslands(char[][] grid) { + int ROWS = grid.length; + int COLS = grid[0].length; + DSU dsu = new DSU(ROWS * COLS); + + int[][] directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + int islands = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == '1') { + islands++; + for (int[] d : directions) { + int nr = r + d[0]; + int nc = c + d[1]; + if (nr >= 0 && nc >= 0 && nr < ROWS && + nc < COLS && grid[nr][nc] == '1') { + if (dsu.union(r * COLS + c, nr * COLS + nc)) { + islands--; + } + } + } + } + } + } + + return islands; + } +} +``` + +```cpp +class DSU { + vector Parent, Size; +public: + DSU(int n) { + Parent.resize(n + 1); + Size.resize(n + 1); + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + int find(int node) { + if (node != Parent[node]) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + bool unionBySize(int u, int v) { + int pu = find(u); + int pv = find(v); + if (pu == pv) return false; + if (Size[pu] >= Size[pv]) { + Size[pu] += Size[pv]; + Parent[pv] = pu; + } else { + Size[pv] += Size[pu]; + Parent[pu] = pv; + } + return true; + } +}; + +class Solution { +public: + int numIslands(vector>& grid) { + int ROWS = grid.size(); + int COLS = grid[0].size(); + DSU dsu(ROWS * COLS); + + int directions[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + int islands = 0; + + auto index = [&](int r, int c) { + return r * COLS + c; + }; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == '1') { + islands++; + for (auto& d : directions) { + int nr = r + d[0]; + int nc = c + d[1]; + if (nr >= 0 && nc >= 0 && nr < ROWS && + nc < COLS && grid[nr][nc] == '1') { + if (dsu.unionBySize(index(r, c), index(nr, nc))) { + islands--; + } + } + } + } + } + } + + return islands; + } +}; +``` + +```javascript +class DSU { + constructor(n) { + this.Parent = Array(n + 1).fill(0).map((_, i) => i); + this.Size = Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.Parent[node] !== node) { + this.Parent[node] = this.find(this.Parent[node]); + } + return this.Parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u); + let pv = this.find(v); + if (pu === pv) return false; + if (this.Size[pu] >= this.Size[pv]) { + this.Size[pu] += this.Size[pv]; + this.Parent[pv] = pu; + } else { + this.Size[pv] += this.Size[pu]; + this.Parent[pu] = pv; + } + return true; + } +} + +class Solution { + /** + * @param {character[][]} grid + * @return {number} + */ + numIslands(grid) { + const ROWS = grid.length; + const COLS = grid[0].length; + const dsu = new DSU(ROWS * COLS); + + const directions = [ + [1, 0], [-1, 0], [0, 1], [0, -1] + ]; + + let islands = 0; + + const index = (r, c) => r * COLS + c; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] === '1') { + islands++; + for (let [dr, dc] of directions) { + let nr = r + dr, nc = c + dc; + if (nr >= 0 && nc >= 0 && nr < ROWS && + nc < COLS && grid[nr][nc] === '1') { + if (dsu.union(index(r, c), index(nr, nc))) { + islands--; + } + } + } + } + } + } + + return islands; + } +} +``` + +```csharp +public class DSU { + private int[] Parent, Size; + + public DSU(int n) { + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + public int Find(int node) { + if (node != Parent[node]) { + Parent[node] = Find(Parent[node]); + } + return Parent[node]; + } + + public bool Union(int u, int v) { + int pu = Find(u); + int pv = Find(v); + if (pu == pv) return false; + if (Size[pu] >= Size[pv]) { + Size[pu] += Size[pv]; + Parent[pv] = pu; + } else { + Size[pv] += Size[pu]; + Parent[pu] = pv; + } + return true; + } +} + +public class Solution { + public int NumIslands(char[][] grid) { + int ROWS = grid.Length; + int COLS = grid[0].Length; + DSU dsu = new DSU(ROWS * COLS); + + int[][] directions = new int[][] { + new int[] { 1, 0 }, new int[] { -1, 0 }, + new int[] { 0, 1 }, new int[] { 0, -1 } + }; + int islands = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == '1') { + islands++; + foreach (var d in directions) { + int nr = r + d[0]; + int nc = c + d[1]; + if (nr >= 0 && nc >= 0 && nr < ROWS && + nc < COLS && grid[nr][nc] == '1') { + if (dsu.Union(r * COLS + c, nr * COLS + nc)) { + islands--; + } + } + } + } + } + } + + return islands; + } +} +``` + +```go +type DSU struct { + Parent []int + Size []int +} + +func NewDSU(n int) *DSU { + dsu := &DSU{ + Parent: make([]int, n+1), + Size: make([]int, n+1), + } + for i := 0; i <= n; i++ { + dsu.Parent[i] = i + dsu.Size[i] = 1 + } + return dsu +} + +func (dsu *DSU) find(node int) int { + if dsu.Parent[node] != node { + dsu.Parent[node] = dsu.find(dsu.Parent[node]) + } + return dsu.Parent[node] +} + +func (dsu *DSU) union(u, v int) bool { + pu := dsu.find(u) + pv := dsu.find(v) + if pu == pv { + return false + } + if dsu.Size[pu] >= dsu.Size[pv] { + dsu.Size[pu] += dsu.Size[pv] + dsu.Parent[pv] = pu + } else { + dsu.Size[pv] += dsu.Size[pu] + dsu.Parent[pu] = pv + } + return true +} + +func numIslands(grid [][]byte) int { + rows, cols := len(grid), len(grid[0]) + dsu := NewDSU(rows * cols) + directions := [][]int{{1, 0}, {-1, 0}, {0, 1}, {0, -1}} + islands := 0 + + index := func(r, c int) int { + return r*cols + c + } + + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + if grid[r][c] == '1' { + islands++ + for _, dir := range directions { + nr, nc := r+dir[0], c+dir[1] + if nr < 0 || nc < 0 || nr >= rows || + nc >= cols || grid[nr][nc] == '0' { + continue + } + if dsu.union(index(r, c), index(nr, nc)) { + islands-- + } + } + } + } + } + + return islands +} +``` + +```kotlin +class DSU(n: Int) { + val Parent = IntArray(n + 1) { it } + val Size = IntArray(n + 1) { 1 } + + fun find(node: Int): Int { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]) + } + return Parent[node] + } + + fun union(u: Int, v: Int): Boolean { + val pu = find(u) + val pv = find(v) + if (pu == pv) return false + if (Size[pu] >= Size[pv]) { + Size[pu] += Size[pv] + Parent[pv] = pu + } else { + Size[pv] += Size[pu] + Parent[pu] = pv + } + return true + } +} + +class Solution { + fun numIslands(grid: Array): Int { + val rows = grid.size + val cols = grid[0].size + val dsu = DSU(rows * cols) + val directions = arrayOf(intArrayOf(1, 0), + intArrayOf(-1, 0), + intArrayOf(0, 1), + intArrayOf(0, -1)) + var islands = 0 + + fun index(r: Int, c: Int): Int { + return r * cols + c + } + + for (r in 0 until rows) { + for (c in 0 until cols) { + if (grid[r][c] == '1') { + islands++ + for (dir in directions) { + val nr = r + dir[0] + val nc = c + dir[1] + if (nr < 0 || nc < 0 || nr >= rows || + nc >= cols || grid[nr][nc] == '0') { + continue + } + if (dsu.union(index(r, c), index(nr, nc))) { + islands-- + } + } + } + } + } + + return islands + } +} +``` + +```swift +class DSU { + private var parent: [Int] + private var size: [Int] + + init(_ n: Int) { + parent = Array(0...n) + size = Array(repeating: 1, count: n + 1) + } + + func find(_ node: Int) -> Int { + if parent[node] != node { + parent[node] = find(parent[node]) + } + return parent[node] + } + + func union(_ u: Int, _ v: Int) -> Bool { + let pu = find(u) + let pv = find(v) + if pu == pv { + return false + } + if size[pu] >= size[pv] { + size[pu] += size[pv] + parent[pv] = pu + } else { + size[pv] += size[pu] + parent[pu] = pv + } + return true + } +} + +class Solution { + func numIslands(_ grid: [[Character]]) -> Int { + let ROWS = grid.count + let COLS = grid[0].count + let dsu = DSU(ROWS * COLS) + + func index(_ r: Int, _ c: Int) -> Int { + return r * COLS + c + } + + let directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + var islands = 0 + var grid = grid + + for r in 0..= ROWS || nc >= COLS || grid[nr][nc] == "0" { + continue + } + if dsu.union(index(r, c), index(nr, nc)) { + islands -= 1 + } + } + } + } + } + + return islands + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the $grid$. \ No newline at end of file diff --git a/articles/count-odd-numbers-in-an-interval-range.md b/articles/count-odd-numbers-in-an-interval-range.md new file mode 100644 index 000000000..1cbaf37b8 --- /dev/null +++ b/articles/count-odd-numbers-in-an-interval-range.md @@ -0,0 +1,187 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def countOdds(self, low: int, high: int) -> int: + odd = 0 + for num in range(low, high + 1): + if num & 1: + odd += 1 + return odd +``` + +```java +public class Solution { + public int countOdds(int low, int high) { + int odd = 0; + for (int num = low; num <= high; num++) { + if ((num & 1) == 1) { + odd++; + } + } + return odd; + } +} +``` + +```cpp +class Solution { +public: + int countOdds(int low, int high) { + int odd = 0; + for (int num = low; num <= high; num++) { + if (num & 1) { + odd++; + } + } + return odd; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} low + * @param {number} high + * @return {number} + */ + countOdds(low, high) { + let odd = 0; + for (let num = low; num <= high; num++) { + if (num & 1) { + odd++; + } + } + return odd; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +> Where $n$ is the number of integers in the given range. + +--- + +## 2. Math + +::tabs-start + +```python +class Solution: + def countOdds(self, low: int, high: int) -> int: + length = high - low + 1 + count = length // 2 + if length % 2 and low % 2: + count += 1 + return count +``` + +```java +public class Solution { + public int countOdds(int low, int high) { + int length = high - low + 1; + int count = length / 2; + if (length % 2 == 1 && low % 2 == 1) { + count++; + } + return count; + } +} +``` + +```cpp +class Solution { +public: + int countOdds(int low, int high) { + int length = high - low + 1; + int count = length / 2; + if (length % 2 == 1 && low % 2 == 1) { + count++; + } + return count; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} low + * @param {number} high + * @return {number} + */ + countOdds(low, high) { + let length = high - low + 1; + let count = Math.floor(length / 2); + if (length % 2 === 1 && low % 2 === 1) { + count++; + } + return count; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 3. Math (One Liner) + +::tabs-start + +```python +class Solution: + def countOdds(self, low: int, high: int) -> int: + return ((high + 1) >> 1) - (low >> 1) +``` + +```java +public class Solution { + public int countOdds(int low, int high) { + return ((high + 1) >> 1) - (low >> 1); + } +} +``` + +```cpp +class Solution { +public: + int countOdds(int low, int high) { + return ((high + 1) >> 1) - (low >> 1); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} low + * @param {number} high + * @return {number} + */ + countOdds(low, high) { + return ((high + 1) >> 1) - (low >> 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/count-of-matches-in-tournament.md b/articles/count-of-matches-in-tournament.md new file mode 100644 index 000000000..2ceeeae80 --- /dev/null +++ b/articles/count-of-matches-in-tournament.md @@ -0,0 +1,120 @@ +## 1. Simulation + +::tabs-start + +```python +class Solution: + def numberOfMatches(self, n: int) -> int: + res = 0 + + while n > 1: + res += n // 2 + n = (n + 1) // 2 + + return res +``` + +```java +public class Solution { + public int numberOfMatches(int n) { + int res = 0; + + while (n > 1) { + res += n / 2; + n = (n + 1) / 2; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numberOfMatches(int n) { + int res = 0; + + while (n > 1) { + res += n / 2; + n = (n + 1) / 2; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numberOfMatches(n) { + let res = 0; + + while (n > 1) { + res += Math.floor(n / 2); + n = Math.ceil(n / 2); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Math + +::tabs-start + +```python +class Solution: + def numberOfMatches(self, n: int) -> int: + return n - 1 +``` + +```java +public class Solution { + public int numberOfMatches(int n) { + return n - 1; + } +} +``` + +```cpp +class Solution { +public: + int numberOfMatches(int n) { + return n - 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numberOfMatches(n) { + return n - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/count-paths.md b/articles/count-paths.md new file mode 100644 index 000000000..5db166df2 --- /dev/null +++ b/articles/count-paths.md @@ -0,0 +1,1023 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def uniquePaths(self, m: int, n: int) -> int: + + def dfs(i, j): + if i == (m - 1) and j == (n - 1): + return 1 + if i >= m or j >= n: + return 0 + return dfs(i, j + 1) + dfs(i + 1, j) + + return dfs(0, 0) +``` + +```java +public class Solution { + public int uniquePaths(int m, int n) { + return dfs(0, 0, m, n); + } + + public int dfs(int i, int j, int m, int n) { + if (i == (m - 1) && j == (n - 1)) { + return 1; + } + if (i >= m || j >= n) return 0; + return dfs(i, j + 1, m, n) + + dfs(i + 1, j, m, n); + } +} +``` + +```cpp +class Solution { +public: + int uniquePaths(int m, int n) { + return dfs(0, 0, m, n); + } + + int dfs(int i, int j, int m, int n) { + if (i == (m - 1) && j == (n - 1)) { + return 1; + } + if (i >= m || j >= n) return 0; + return dfs(i, j + 1, m, n) + + dfs(i + 1, j, m, n); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} m + * @param {number} n + * @return {number} + */ + uniquePaths(m, n) { + + const dfs = (i, j) => { + if (i == (m - 1) && j == (n - 1)) { + return 1; + } + if (i >= m || j >= n) return 0; + return dfs(i, j + 1) + dfs(i + 1, j); + } + + return dfs(0, 0); + } +} +``` + +```csharp +public class Solution { + public int UniquePaths(int m, int n) { + return Dfs(0, 0, m, n); + } + + int Dfs(int i, int j, int m, int n) { + if (i == (m - 1) && j == (n - 1)) { + return 1; + } + if (i >= m || j >= n) return 0; + return Dfs(i, j + 1, m, n) + + Dfs(i + 1, j, m, n); + } +} +``` + +```go +func uniquePaths(m int, n int) int { + var dfs func(i, j int) int + dfs = func(i, j int) int { + if i == m-1 && j == n-1 { + return 1 + } + if i >= m || j >= n { + return 0 + } + return dfs(i, j+1) + dfs(i+1, j) + } + + return dfs(0, 0) +} +``` + +```kotlin +class Solution { + fun uniquePaths(m: Int, n: Int): Int { + fun dfs(i: Int, j: Int): Int { + if (i == m - 1 && j == n - 1) return 1 + if (i >= m || j >= n) return 0 + return dfs(i, j + 1) + dfs(i + 1, j) + } + + return dfs(0, 0) + } +} +``` + +```swift +class Solution { + func uniquePaths(_ m: Int, _ n: Int) -> Int { + func dfs(_ i: Int, _ j: Int) -> Int { + if i == m - 1 && j == n - 1 { + return 1 + } + if i >= m || j >= n { + return 0 + } + return dfs(i, j + 1) + dfs(i + 1, j) + } + + return dfs(0, 0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ {m + n})$ +* Space complexity: $O(m + n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def uniquePaths(self, m: int, n: int) -> int: + memo = [[-1] * n for _ in range(m)] + def dfs(i, j): + if i == (m - 1) and j == (n - 1): + return 1 + if i >= m or j >= n: + return 0 + if memo[i][j] != -1: + return memo[i][j] + + memo[i][j] = dfs(i, j + 1) + dfs(i + 1, j) + return memo[i][j] + + return dfs(0, 0) +``` + +```java +public class Solution { + int[][] memo; + public int uniquePaths(int m, int n) { + memo = new int[m][n]; + for(int[] it : memo) { + Arrays.fill(it, -1); + } + return dfs(0, 0, m, n); + } + + public int dfs(int i, int j, int m, int n) { + if (i == (m - 1) && j == (n - 1)) { + return 1; + } + if (i >= m || j >= n) return 0; + if (memo[i][j] != -1) { + return memo[i][j]; + } + return memo[i][j] = dfs(i, j + 1, m, n) + + dfs(i + 1, j, m, n); + } +} +``` + +```cpp +class Solution { +public: + vector> memo; + int uniquePaths(int m, int n) { + memo.resize(m, vector(n, -1)); + return dfs(0, 0, m, n); + } + + int dfs(int i, int j, int m, int n) { + if (i == (m - 1) && j == (n - 1)) { + return 1; + } + if (i >= m || j >= n) return 0; + if (memo[i][j] != -1) { + return memo[i][j]; + } + return memo[i][j] = dfs(i, j + 1, m, n) + + dfs(i + 1, j, m, n); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} m + * @param {number} n + * @return {number} + */ + uniquePaths(m, n) { + const memo = Array.from({ length: m }, () => + Array(n).fill(-1)); + const dfs = (i, j) => { + if (i == (m - 1) && j == (n - 1)) { + return 1; + } + if (i >= m || j >= n) return 0; + if (memo[i][j] != -1) { + return memo[i][j]; + } + memo[i][j] = dfs(i, j + 1) + dfs(i + 1, j); + return memo[i][j]; + } + + return dfs(0, 0); + } +} +``` + +```csharp +public class Solution { + int[,] memo; + public int UniquePaths(int m, int n) { + memo = new int[m, n]; + for (int i = 0; i < m; i++) + for (int j = 0; j < n; j++) + memo[i, j] = -1; + + return Dfs(0, 0, m, n); + } + + int Dfs(int i, int j, int m, int n) { + if (i == (m - 1) && j == (n - 1)) { + return 1; + } + if (i >= m || j >= n) return 0; + if (memo[i, j] != -1) { + return memo[i, j]; + } + return memo[i, j] = Dfs(i, j + 1, m, n) + + Dfs(i + 1, j, m, n); + } +} +``` + +```go +func uniquePaths(m int, n int) int { + memo := make([][]int, m) + for i := range memo { + memo[i] = make([]int, n) + for j := range memo[i] { + memo[i][j] = -1 + } + } + + var dfs func(i, j int) int + dfs = func(i, j int) int { + if i == m-1 && j == n-1 { + return 1 + } + if i >= m || j >= n { + return 0 + } + if memo[i][j] != -1 { + return memo[i][j] + } + + memo[i][j] = dfs(i, j+1) + dfs(i+1, j) + return memo[i][j] + } + + return dfs(0, 0) +} +``` + +```kotlin +class Solution { + fun uniquePaths(m: Int, n: Int): Int { + val memo = Array(m) { IntArray(n) { -1 } } + + fun dfs(i: Int, j: Int): Int { + if (i == m - 1 && j == n - 1) return 1 + if (i >= m || j >= n) return 0 + if (memo[i][j] != -1) return memo[i][j] + + memo[i][j] = dfs(i, j + 1) + dfs(i + 1, j) + return memo[i][j] + } + + return dfs(0, 0) + } +} +``` + +```swift +class Solution { + func uniquePaths(_ m: Int, _ n: Int) -> Int { + var memo = Array(repeating: Array(repeating: -1, count: n), count: m) + + func dfs(_ i: Int, _ j: Int) -> Int { + if i == m - 1 && j == n - 1 { + return 1 + } + if i >= m || j >= n { + return 0 + } + if memo[i][j] != -1 { + return memo[i][j] + } + + memo[i][j] = dfs(i, j + 1) + dfs(i + 1, j) + return memo[i][j] + } + + return dfs(0, 0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def uniquePaths(self, m: int, n: int) -> int: + dp = [[0] * (n + 1) for _ in range(m + 1)] + dp[m - 1][n - 1] = 1 + + for i in range(m - 1, -1, -1): + for j in range(n - 1, -1, -1): + dp[i][j] += dp[i + 1][j] + dp[i][j + 1] + + return dp[0][0] +``` + +```java +public class Solution { + public int uniquePaths(int m, int n) { + int[][] dp = new int[m + 1][n + 1]; + dp[m - 1][n - 1] = 1; + + for (int i = m - 1; i >= 0; i--) { + for (int j = n - 1; j >= 0; j--) { + dp[i][j] += dp[i + 1][j] + dp[i][j + 1]; + } + } + + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + int uniquePaths(int m, int n) { + vector> dp(m + 1, vector(n + 1, 0)); + dp[m - 1][n - 1] = 1; + + for (int i = m - 1; i >= 0; i--) { + for (int j = n - 1; j >= 0; j--) { + dp[i][j] += dp[i + 1][j] + dp[i][j + 1]; + } + } + + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} m + * @param {number} n + * @return {number} + */ + uniquePaths(m, n) { + let dp = Array.from({ length: m + 1 }, () => + Array(n + 1).fill(0)); + dp[m - 1][n - 1] = 1; + + for (let i = m - 1; i >= 0; i--) { + for (let j = n - 1; j >= 0; j--) { + dp[i][j] += dp[i + 1][j] + dp[i][j + 1]; + } + } + + return dp[0][0]; + } +} +``` + +```csharp +public class Solution { + public int UniquePaths(int m, int n) { + int[,] dp = new int[m + 1, n + 1]; + dp[m - 1, n - 1] = 1; + + for (int i = m - 1; i >= 0; i--) { + for (int j = n - 1; j >= 0; j--) { + dp[i, j] += dp[i + 1, j] + dp[i, j + 1]; + } + } + + return dp[0, 0]; + } +} +``` + +```go +func uniquePaths(m int, n int) int { + dp := make([][]int, m+1) + for i := range dp { + dp[i] = make([]int, n+1) + } + dp[m-1][n-1] = 1 + + for i := m - 1; i >= 0; i-- { + for j := n - 1; j >= 0; j-- { + dp[i][j] += dp[i+1][j] + dp[i][j+1] + } + } + + return dp[0][0] +} +``` + +```kotlin +class Solution { + fun uniquePaths(m: Int, n: Int): Int { + val dp = Array(m + 1) { IntArray(n + 1) } + dp[m - 1][n - 1] = 1 + + for (i in m - 1 downTo 0) { + for (j in n - 1 downTo 0) { + dp[i][j] += dp[i + 1][j] + dp[i][j + 1] + } + } + + return dp[0][0] + } +} +``` + +```swift +class Solution { + func uniquePaths(_ m: Int, _ n: Int) -> Int { + var dp = Array(repeating: Array(repeating: 0, count: n + 1), count: m + 1) + dp[m - 1][n - 1] = 1 + + for i in stride(from: m - 1, through: 0, by: -1) { + for j in stride(from: n - 1, through: 0, by: -1) { + dp[i][j] += dp[i + 1][j] + dp[i][j + 1] + } + } + + return dp[0][0] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def uniquePaths(self, m: int, n: int) -> int: + row = [1] * n + + for i in range(m - 1): + newRow = [1] * n + for j in range(n - 2, -1, -1): + newRow[j] = newRow[j + 1] + row[j] + row = newRow + return row[0] +``` + +```java +public class Solution { + public int uniquePaths(int m, int n) { + int[] row = new int[n]; + Arrays.fill(row, 1); + + for (int i = 0; i < m - 1; i++) { + int[] newRow = new int[n]; + Arrays.fill(newRow, 1); + for (int j = n - 2; j >= 0; j--) { + newRow[j] = newRow[j + 1] + row[j]; + } + row = newRow; + } + return row[0]; + } +} +``` + +```cpp +class Solution { +public: + int uniquePaths(int m, int n) { + vector row(n, 1); + + for (int i = 0; i < m - 1; ++i) { + vector newRow(n, 1); + for (int j = n - 2; j >= 0; --j) { + newRow[j] = newRow[j + 1] + row[j]; + } + row = newRow; + } + return row[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} m + * @param {number} n + * @return {number} + */ + uniquePaths(m, n) { + let row = new Array(n).fill(1); + + for (let i = 0; i < m - 1; i++) { + const newRow = new Array(n).fill(1); + for (let j = n - 2; j >= 0; j--) { + newRow[j] = newRow[j + 1] + row[j]; + } + row = newRow; + } + return row[0]; + } +} +``` + +```csharp +public class Solution { + public int UniquePaths(int m, int n) { + var row = new int[n]; + Array.Fill(row, 1); + for (int i = 0; i < m - 1; i++) { + var newRow = new int[n]; + Array.Fill(newRow, 1); + for (int j = n - 2; j >=0; j--) { + newRow[j] = newRow[j + 1] + row[j]; + } + row = newRow; + } + return row[0]; + } +} +``` + +```go +func uniquePaths(m int, n int) int { + row := make([]int, n) + for i := range row { + row[i] = 1 + } + + for i := 0; i < m - 1; i++ { + newRow := make([]int, n) + newRow[n-1] = 1 + for j := n - 2; j >= 0; j-- { + newRow[j] = newRow[j+1] + row[j] + } + row = newRow + } + + return row[0] +} +``` + +```kotlin +class Solution { + fun uniquePaths(m: Int, n: Int): Int { + var row = IntArray(n) { 1 } + + for (i in 0 until m - 1) { + val newRow = IntArray(n) { 1 } + for (j in n - 2 downTo 0) { + newRow[j] = newRow[j + 1] + row[j] + } + row = newRow + } + + return row[0] + } +} +``` + +```swift +class Solution { + func uniquePaths(_ m: Int, _ n: Int) -> Int { + var row = Array(repeating: 1, count: n) + + for _ in 0..<(m - 1) { + var newRow = Array(repeating: 1, count: n) + for j in stride(from: n - 2, through: 0, by: -1) { + newRow[j] = newRow[j + 1] + row[j] + } + row = newRow + } + return row[0] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 5. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def uniquePaths(self, m: int, n: int) -> int: + dp = [1] * n + for i in range(m - 2, -1, -1): + for j in range(n - 2, -1, -1): + dp[j] += dp[j + 1] + + return dp[0] +``` + +```java +public class Solution { + public int uniquePaths(int m, int n) { + int[] dp = new int[n]; + Arrays.fill(dp, 1); + + for (int i = m - 2; i >= 0; i--) { + for (int j = n - 2; j >= 0; j--) { + dp[j] += dp[j + 1]; + } + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int uniquePaths(int m, int n) { + vector dp(n, 1); + + for (int i = m - 2; i >= 0; i--) { + for (int j = n - 2; j >= 0; j--) { + dp[j] += dp[j + 1]; + } + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} m + * @param {number} n + * @return {number} + */ + uniquePaths(m, n) { + let dp = new Array(n).fill(1); + + for (let i = m - 2; i >= 0; i--) { + for (let j = n - 2; j >= 0; j--) { + dp[j] += dp[j + 1]; + } + } + + return dp[0]; + } +} +``` + +```csharp +public class Solution { + public int UniquePaths(int m, int n) { + int[] dp = new int[n]; + Array.Fill(dp, 1); + + for (int i = m - 2; i >= 0; i--) { + for (int j = n - 2; j >= 0; j--) { + dp[j] += dp[j + 1]; + } + } + + return dp[0]; + } +} +``` + +```go +func uniquePaths(m int, n int) int { + dp := make([]int, n) + for i := range dp { + dp[i] = 1 + } + + for i := m - 2; i >= 0; i-- { + for j := n - 2; j >= 0; j-- { + dp[j] += dp[j+1] + } + } + + return dp[0] +} +``` + +```kotlin +class Solution { + fun uniquePaths(m: Int, n: Int): Int { + var dp = IntArray(n) { 1 } + + for (i in m - 2 downTo 0) { + for (j in n - 2 downTo 0) { + dp[j] += dp[j + 1] + } + } + + return dp[0] + } +} +``` + +```swift +class Solution { + func uniquePaths(_ m: Int, _ n: Int) -> Int { + var dp = Array(repeating: 1, count: n) + + for _ in stride(from: m - 2, through: 0, by: -1) { + for j in stride(from: n - 2, through: 0, by: -1) { + dp[j] += dp[j + 1] + } + } + + return dp[0] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 6. Math + +::tabs-start + +```python +class Solution: + def uniquePaths(self, m: int, n: int) -> int: + if m == 1 or n == 1: + return 1 + if m < n: + m, n = n, m + + res = j = 1 + for i in range(m, m + n - 1): + res *= i + res //= j + j += 1 + + return res +``` + +```java +public class Solution { + public int uniquePaths(int m, int n) { + if (m == 1 || n == 1) { + return 1; + } + if (m < n) { + int temp = m; + m = n; + n = temp; + } + + long res = 1; + int j = 1; + for (int i = m; i < m + n - 1; i++) { + res *= i; + res /= j; + j++; + } + + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + int uniquePaths(int m, int n) { + if (m == 1 || n == 1) { + return 1; + } + if (m < n) { + swap(m, n); + } + + long long res = 1; + int j = 1; + for (int i = m; i < m + n - 1; i++) { + res *= i; + res /= j; + j++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} m + * @param {number} n + * @return {number} + */ + uniquePaths(m, n) { + if (m === 1 || n === 1) { + return 1; + } + if (m < n) { + [m, n] = [n, m]; + } + + let res = 1, j = 1; + for (let i = m; i < m + n - 1; i++) { + res *= i; + res = Math.floor(res / j); + j++; + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int UniquePaths(int m, int n) { + if (m == 1 || n == 1) { + return 1; + } + if (m < n) { + int temp = m; + m = n; + n = temp; + } + + long res = 1; + int j = 1; + for (int i = m; i < m + n - 1; i++) { + res *= i; + res /= j; + j++; + } + + return (int) res; + } +} +``` + +```go +func uniquePaths(m int, n int) int { + if m == 1 || n == 1 { + return 1 + } + if m < n { + tmp := m + m = n + n = tmp + } + + res, j := 1, 1 + for i := m; i < m + n - 1; i++ { + res *= i + res /= j + j++ + } + + return res +} +``` + +```kotlin +class Solution { + fun uniquePaths(m: Int, n: Int): Int { + if (m == 1 || n == 1) return 1 + var m = m + var n = n + if (m < n) { + val tmp = m + m = n + n = tmp + } + + var res: Long = 1 + var j = 1 + for (i in m until m + n - 1) { + res *= i + res /= j + j++ + } + + return res.toInt() + } +} +``` + +```swift +class Solution { + func uniquePaths(_ m: Int, _ n: Int) -> Int { + if m == 1 || n == 1 { + return 1 + } + var m = m, n = n + if m < n { + swap(&m, &n) + } + + var res = 1 + var j = 1 + for i in m..<(m + n - 1) { + res *= i + res /= j + j += 1 + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(min(m, n))$ +* Space complexity: $O(1)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. \ No newline at end of file diff --git a/articles/count-squares.md b/articles/count-squares.md new file mode 100644 index 000000000..1b5defe8c --- /dev/null +++ b/articles/count-squares.md @@ -0,0 +1,606 @@ +## 1. Hash Map - I + +::tabs-start + +```python +class CountSquares: + def __init__(self): + self.ptsCount = defaultdict(int) + self.pts = [] + + def add(self, point: List[int]) -> None: + self.ptsCount[tuple(point)] += 1 + self.pts.append(point) + + def count(self, point: List[int]) -> int: + res = 0 + px, py = point + for x, y in self.pts: + if (abs(py - y) != abs(px - x)) or x == px or y == py: + continue + res += self.ptsCount[(x, py)] * self.ptsCount[(px, y)] + return res +``` + +```java +public class CountSquares { + private Map, Integer> ptsCount; + private List> pts; + + public CountSquares() { + ptsCount = new HashMap<>(); + pts = new ArrayList<>(); + } + + public void add(int[] point) { + List p = Arrays.asList(point[0], point[1]); + ptsCount.put(p, ptsCount.getOrDefault(p, 0) + 1); + pts.add(p); + } + + public int count(int[] point) { + int res = 0; + int px = point[0], py = point[1]; + for (List pt : pts) { + int x = pt.get(0), y = pt.get(1); + if (Math.abs(py - y) != Math.abs(px - x) || x == px || y == py) { + continue; + } + res += ptsCount.getOrDefault(Arrays.asList(x, py), 0) * + ptsCount.getOrDefault(Arrays.asList(px, y), 0); + } + return res; + } +} +``` + +```cpp +class CountSquares { +private: + unordered_map ptsCount; + vector> pts; + + long getKey(int x, int y) { + return (static_cast(x) << 32) | static_cast(y); + } + +public: + CountSquares() { + } + + void add(vector point) { + long key = getKey(point[0], point[1]); + ptsCount[key]++; + pts.push_back(point); + } + + int count(vector point) { + int res = 0; + int px = point[0], py = point[1]; + + for (const auto& pt : pts) { + int x = pt[0], y = pt[1]; + if (abs(py - y) != abs(px - x) || x == px || y == py) continue; + res += ptsCount[getKey(x, py)] * ptsCount[getKey(px, y)]; + } + return res; + } +}; +``` + +```javascript +class CountSquares { + constructor() { + this.ptsCount = new Map(); + this.pts = []; + } + + /** + * @param {number[]} point + * @return {void} + */ + add(point) { + const p = point.join(','); + this.ptsCount.set(p, (this.ptsCount.get(p) || 0) + 1); + this.pts.push(point); + } + + /** + * @param {number[]} point + * @return {number} + */ + count(point) { + let res = 0; + const [px, py] = point; + for (const [x, y] of this.pts) { + if (Math.abs(py - y) !== Math.abs(px - x) || x === px || y === py) { + continue; + } + res += + (this.ptsCount.get(`${x},${py}`) || 0) * + (this.ptsCount.get(`${px},${y}`) || 0); + } + return res; + } +} +``` + +```csharp +public class CountSquares { + private Dictionary<(int, int), int> ptsCount; + private List pts; + + public CountSquares() { + ptsCount = new Dictionary<(int, int), int>(); + pts = new List(); + } + + public void Add(int[] point) { + var tuplePoint = (point[0], point[1]); + if (!ptsCount.ContainsKey(tuplePoint)) + ptsCount[tuplePoint] = 0; + + ptsCount[tuplePoint]++; + pts.Add(point); + } + + public int Count(int[] point) { + int res = 0; + int px = point[0]; + int py = point[1]; + + foreach (var pt in pts) { + int x = pt[0]; + int y = pt[1]; + + if (Math.Abs(py - y) != Math.Abs(px - x) || x == px || y == py) + continue; + + res += (ptsCount.GetValueOrDefault((x, py)) * + ptsCount.GetValueOrDefault((px, y))); + } + return res; + } +} +``` + +```go +type CountSquares struct { + ptsCount map[Point]int + pts []Point +} + +type Point struct { + x, y int +} + +func Constructor() CountSquares { + return CountSquares{ + ptsCount: make(map[Point]int), + pts: make([]Point, 0), + } +} + +func (this *CountSquares) Add(point []int) { + p := Point{point[0], point[1]} + this.ptsCount[p]++ + this.pts = append(this.pts, p) +} + +func (this *CountSquares) Count(point []int) int { + res := 0 + px, py := point[0], point[1] + + for _, pt := range this.pts { + if abs(py-pt.y) != abs(px-pt.x) || pt.x == px || pt.y == py { + continue + } + + p1 := Point{pt.x, py} + p2 := Point{px, pt.y} + + res += this.ptsCount[p1] * this.ptsCount[p2] + } + + return res +} + +func abs(x int) int { + if x < 0 { + return -x + } + return x +} +``` + +```kotlin +class CountSquares() { + private val pointCounts = HashMap, Int>() + private val points = mutableListOf() + + fun add(point: IntArray) { + val pair = Pair(point[0], point[1]) + pointCounts[pair] = pointCounts.getOrDefault(pair, 0) + 1 + points.add(point) + } + + fun count(point: IntArray): Int { + var result = 0 + val (px, py) = point + + for ((x, y) in points) { + if (kotlin.math.abs(py - y) != kotlin.math.abs(px - x) || + x == px || y == py) { + continue + } + result += (pointCounts[Pair(x, py)] ?: 0) * (pointCounts[Pair(px, y)] ?: 0) + } + return result + } +} +``` + +```swift +class CountSquares { + private var ptsCount: [String: Int] + private var pts: [[Int]] + + init() { + self.ptsCount = [:] + self.pts = [] + } + + func add(_ point: [Int]) { + let key = "\(point[0]),\(point[1])" + ptsCount[key, default: 0] += 1 + pts.append(point) + } + + func count(_ point: [Int]) -> Int { + var res = 0 + let px = point[0], py = point[1] + + for pt in pts { + let x = pt[0], y = pt[1] + if abs(py - y) != abs(px - x) || x == px || y == py { + continue + } + let key1 = "\(x),\(py)" + let key2 = "\(px),\(y)" + res += (ptsCount[key1] ?? 0) * (ptsCount[key2] ?? 0) + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for $add()$, $O(n)$ for $count()$. +* Space complexity: $O(n)$ + +--- + +## 2. Hash Map - II + +::tabs-start + +```python +class CountSquares: + + def __init__(self): + self.ptsCount = defaultdict(lambda: defaultdict(int)) + + def add(self, point: List[int]) -> None: + self.ptsCount[point[0]][point[1]] += 1 + + def count(self, point: List[int]) -> int: + res = 0 + x1, y1 = point + for y2 in self.ptsCount[x1]: + side = y2 - y1 + if side == 0: + continue + + x3, x4 = x1 + side, x1 - side + res += (self.ptsCount[x1][y2] * self.ptsCount[x3][y1] * + self.ptsCount[x3][y2]) + + res += (self.ptsCount[x1][y2] * self.ptsCount[x4][y1] * + self.ptsCount[x4][y2]) + return res +``` + +```java +public class CountSquares { + private Map> ptsCount; + + public CountSquares() { + ptsCount = new HashMap<>(); + } + + public void add(int[] point) { + int x = point[0], y = point[1]; + ptsCount.putIfAbsent(x, new HashMap<>()); + ptsCount.get(x).put(y, ptsCount.get(x).getOrDefault(y, 0) + 1); + } + + public int count(int[] point) { + int res = 0, x1 = point[0], y1 = point[1]; + + if (!ptsCount.containsKey(x1)) return res; + + for (int y2 : ptsCount.get(x1).keySet()) { + int side = y2 - y1; + if (side == 0) continue; + + int x3 = x1 + side, x4 = x1 - side; + res += ptsCount.get(x1).get(y2) * + ptsCount.getOrDefault(x3, new HashMap<>()).getOrDefault(y1, 0) * + ptsCount.getOrDefault(x3, new HashMap<>()).getOrDefault(y2, 0); + + res += ptsCount.get(x1).get(y2) * + ptsCount.getOrDefault(x4, new HashMap<>()).getOrDefault(y1, 0) * + ptsCount.getOrDefault(x4, new HashMap<>()).getOrDefault(y2, 0); + } + + return res; + } +} +``` + +```cpp +class CountSquares { + unordered_map> ptsCount; + +public: + CountSquares() {} + + void add(vector point) { + ptsCount[point[0]][point[1]]++; + } + + int count(vector point) { + int res = 0; + int x1 = point[0], y1 = point[1]; + + for (auto &[y2, cnt] : ptsCount[x1]) { + int side = y2 - y1; + if (side == 0) continue; + + int x3 = x1 + side, x4 = x1 - side; + res += cnt * ptsCount[x3][y1] * ptsCount[x3][y2]; + res += cnt * ptsCount[x4][y1] * ptsCount[x4][y2]; + } + + return res; + } +}; +``` + +```javascript +class CountSquares { + constructor() { + this.ptsCount = new Map(); + } + + /** + * @param {number[]} point + * @return {void} + */ + add(point) { + const [x, y] = point; + if (!this.ptsCount.has(x)) this.ptsCount.set(x, new Map()); + const yCount = this.ptsCount.get(x); + yCount.set(y, (yCount.get(y) || 0) + 1); + } + + /** + * @param {number[]} point + * @return {number} + */ + count(point) { + let res = 0; + const [x1, y1] = point; + if (!this.ptsCount.has(x1)) return res; + + const x1Points = this.ptsCount.get(x1); + for (const [y2, cnt] of x1Points) { + const side = y2 - y1; + if (side === 0) continue; + + const x3 = x1 + side; + const x4 = x1 - side; + + res += cnt * + (this.ptsCount.get(x3)?.get(y1) || 0) * + (this.ptsCount.get(x3)?.get(y2) || 0); + + res += cnt * + (this.ptsCount.get(x4)?.get(y1) || 0) * + (this.ptsCount.get(x4)?.get(y2) || 0); + } + + return res; + } +} +``` + +```csharp +public class CountSquares { + private Dictionary> ptsCount; + + public CountSquares() { + ptsCount = new Dictionary>(); + } + + public void Add(int[] point) { + int x = point[0], y = point[1]; + if (!ptsCount.ContainsKey(x)) { + ptsCount[x] = new Dictionary(); + } + if (!ptsCount[x].ContainsKey(y)) ptsCount[x][y] = 0; + ptsCount[x][y]++; + } + + public int Count(int[] point) { + int res = 0; + int x1 = point[0], y1 = point[1]; + + if (!ptsCount.ContainsKey(x1)) return res; + + foreach (var kv in ptsCount[x1]) { + int y2 = kv.Key, cnt = kv.Value; + int side = y2 - y1; + if (side == 0) continue; + + int x3 = x1 + side, x4 = x1 - side; + + res += cnt * + (ptsCount.ContainsKey(x3) && + ptsCount[x3].ContainsKey(y1) ? ptsCount[x3][y1] : 0) * + (ptsCount.ContainsKey(x3) && + ptsCount[x3].ContainsKey(y2) ? ptsCount[x3][y2] : 0); + + res += cnt * + (ptsCount.ContainsKey(x4) && + ptsCount[x4].ContainsKey(y1) ? ptsCount[x4][y1] : 0) * + (ptsCount.ContainsKey(x4) && + ptsCount[x4].ContainsKey(y2) ? ptsCount[x4][y2] : 0); + } + + return res; + } +} +``` + +```go +type CountSquares struct { + ptsCount map[int]map[int]int +} + +func Constructor() CountSquares { + return CountSquares{ + ptsCount: make(map[int]map[int]int), + } +} + +func (this *CountSquares) Add(point []int) { + x, y := point[0], point[1] + if this.ptsCount[x] == nil { + this.ptsCount[x] = make(map[int]int) + } + this.ptsCount[x][y]++ +} + +func (this *CountSquares) Count(point []int) int { + res := 0 + x1, y1 := point[0], point[1] + + for y2 := range this.ptsCount[x1] { + side := y2 - y1 + if side == 0 { + continue + } + + x3, x4 := x1+side, x1-side + + if _, exists := this.ptsCount[x3]; exists { + res += this.ptsCount[x1][y2] * this.ptsCount[x3][y1] * this.ptsCount[x3][y2] + } + + if _, exists := this.ptsCount[x4]; exists { + res += this.ptsCount[x1][y2] * this.ptsCount[x4][y1] * this.ptsCount[x4][y2] + } + } + return res +} +``` + +```kotlin +class CountSquares { + private val points = HashMap>() + + fun add(point: IntArray) { + val x = point[0] + val y = point[1] + + if (!points.containsKey(x)) { + points[x] = hashMapOf() + } + + points[x]?.put(y, (points[x]?.get(y) ?: 0) + 1) + } + + fun count(point: IntArray): Int { + var result = 0 + val x1 = point[0] + val y1 = point[1] + + points[x1]?.forEach { (y2, count1) -> + if (y2 == y1) return@forEach + + val side = Math.abs(y2 - y1) + + val x3 = x1 + side + if (points.containsKey(x3)) { + val count2 = points[x3]?.get(y1) ?: 0 + val count3 = points[x3]?.get(y2) ?: 0 + result += count1 * count2 * count3 + } + + val x4 = x1 - side + if (points.containsKey(x4)) { + val count2 = points[x4]?.get(y1) ?: 0 + val count3 = points[x4]?.get(y2) ?: 0 + result += count1 * count2 * count3 + } + } + + return result + } +} +``` + +```swift +class CountSquares { + var ptsCount: [Int: [Int: Int]] + + init() { + ptsCount = [:] + } + + func add(_ point: [Int]) { + let x = point[0] + let y = point[1] + ptsCount[x, default: [:]][y, default: 0] += 1 + } + + func count(_ point: [Int]) -> Int { + var res = 0 + let x1 = point[0] + let y1 = point[1] + + if let yMap = ptsCount[x1] { + for (y2, countXY2) in yMap { + let side = y2 - y1 + if side == 0 { continue } + let x3 = x1 + side + let x4 = x1 - side + res += countXY2 * (ptsCount[x3]?[y1] ?? 0) * (ptsCount[x3]?[y2] ?? 0) + res += countXY2 * (ptsCount[x4]?[y1] ?? 0) * (ptsCount[x4]?[y2] ?? 0) + } + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for $add()$, $O(n)$ for $count()$. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/count-sub-islands.md b/articles/count-sub-islands.md new file mode 100644 index 000000000..22a7598f7 --- /dev/null +++ b/articles/count-sub-islands.md @@ -0,0 +1,564 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def countSubIslands(self, grid1: List[List[int]], grid2: List[List[int]]) -> int: + ROWS, COLS = len(grid1), len(grid1[0]) + visit = set() + + def dfs(r, c): + if (min(r, c) < 0 or r == ROWS or c == COLS or + grid2[r][c] == 0 or (r, c) in visit): + return True + + visit.add((r, c)) + res = grid1[r][c] + + res &= dfs(r - 1, c) + res &= dfs(r + 1, c) + res &= dfs(r, c - 1) + res &= dfs(r, c + 1) + return res + + count = 0 + for r in range(ROWS): + for c in range(COLS): + if grid2[r][c] and (r, c) not in visit: + count += dfs(r, c) + return count +``` + +```java +public class Solution { + private boolean[][] visit; + + public int countSubIslands(int[][] grid1, int[][] grid2) { + int ROWS = grid1.length, COLS = grid1[0].length; + visit = new boolean[ROWS][COLS]; + int count = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid2[r][c] == 1 && !visit[r][c] && dfs(r, c, grid1, grid2)) { + count++; + } + } + } + return count; + } + + private boolean dfs(int r, int c, int[][] grid1, int[][] grid2) { + if (r < 0 || c < 0 || r >= grid1.length || c >= grid1[0].length || + grid2[r][c] == 0 || visit[r][c]) { + return true; + } + visit[r][c] = true; + boolean res = grid1[r][c] == 1; + res &= dfs(r - 1, c, grid1, grid2); + res &= dfs(r + 1, c, grid1, grid2); + res &= dfs(r, c - 1, grid1, grid2); + res &= dfs(r, c + 1, grid1, grid2); + return res; + } + +} +``` + +```cpp +class Solution { + vector> visit; + +public: + int countSubIslands(vector>& grid1, vector>& grid2) { + int ROWS = grid1.size(), COLS = grid1[0].size(); + visit.assign(ROWS, vector(COLS, false)); + + int count = 0; + for (int r = 0; r < ROWS; ++r) { + for (int c = 0; c < COLS; ++c) { + if (grid2[r][c] && !visit[r][c]) { + count += dfs(r, c, grid1, grid2); + } + } + } + return count; + } + +private: + bool dfs(int r, int c, vector>& grid1, vector>& grid2) { + if (r < 0 || c < 0 || r >= grid1.size() || c >= grid1[0].size() || + grid2[r][c] == 0 || visit[r][c]) { + return true; + } + + visit[r][c] = true; + bool res = grid1[r][c] == 1; + res &= dfs(r - 1, c, grid1, grid2); + res &= dfs(r + 1, c, grid1, grid2); + res &= dfs(r, c - 1, grid1, grid2); + res &= dfs(r, c + 1, grid1, grid2); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid1 + * @param {number[][]} grid2 + * @return {number} + */ + countSubIslands(grid1, grid2) { + const ROWS = grid1.length, COLS = grid1[0].length; + const visit = Array.from({ length: ROWS }, () => Array(COLS).fill(false)); + + const dfs = (r, c) => { + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || grid2[r][c] === 0 || visit[r][c]) + return true; + visit[r][c] = true; + let res = grid1[r][c] === 1; + res &= dfs(r - 1, c); + res &= dfs(r + 1, c); + res &= dfs(r, c - 1); + res &= dfs(r, c + 1); + return res; + }; + + let count = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid2[r][c] === 1 && !visit[r][c]) { + if (dfs(r, c)) count++; + } + } + } + return count; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def countSubIslands(self, grid1: List[List[int]], grid2: List[List[int]]) -> int: + ROWS, COLS = len(grid1), len(grid1[0]) + visit = [[False] * COLS for _ in range(ROWS)] + directions = [1, 0, -1, 0, 1] + + def bfs(r, c): + queue = deque([(r, c)]) + visit[r][c] = True + res = True + + while queue: + r, c = queue.popleft() + if grid1[r][c] == 0: + res = False + + for i in range(4): + nr, nc = r + directions[i], c + directions[i + 1] + if 0 <= nr < ROWS and 0 <= nc < COLS and not visit[nr][nc] and grid2[nr][nc]: + visit[nr][nc] = True + queue.append((nr, nc)) + return res + + count = 0 + for r in range(ROWS): + for c in range(COLS): + if grid2[r][c] == 1 and not visit[r][c]: + if bfs(r, c): + count += 1 + return count +``` + +```java +public class Solution { + private boolean[][] visit; + private int[] directions = {1, 0, -1, 0, 1}; + + public int countSubIslands(int[][] grid1, int[][] grid2) { + int ROWS = grid1.length, COLS = grid1[0].length; + visit = new boolean[ROWS][COLS]; + int count = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid2[r][c] == 1 && !visit[r][c]) { + if (bfs(r, c, grid1, grid2)) { + count++; + } + } + } + } + return count; + } + + private boolean bfs(int r, int c, int[][] grid1, int[][] grid2) { + Queue queue = new LinkedList<>(); + queue.add(new int[]{r, c}); + visit[r][c] = true; + boolean res = true; + + while (!queue.isEmpty()) { + int[] cell = queue.poll(); + int cr = cell[0], cc = cell[1]; + if (grid1[cr][cc] == 0) { + res = false; + } + + for (int i = 0; i < 4; i++) { + int nr = cr + directions[i], nc = cc + directions[i + 1]; + if (nr >= 0 && nr < grid1.length && nc >= 0 && nc < grid1[0].length && + grid2[nr][nc] == 1 && !visit[nr][nc]) { + visit[nr][nc] = true; + queue.add(new int[]{nr, nc}); + } + } + } + return res; + } +} +``` + +```cpp +class Solution { + vector> visit; + vector directions = {1, 0, -1, 0, 1}; + +public: + int countSubIslands(vector>& grid1, vector>& grid2) { + int ROWS = grid1.size(), COLS = grid1[0].size(); + visit.assign(ROWS, vector(COLS, false)); + int count = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid2[r][c] == 1 && !visit[r][c]) { + if (bfs(r, c, grid1, grid2)) { + count++; + } + } + } + } + return count; + } + +private: + bool bfs(int r, int c, vector>& grid1, vector>& grid2) { + queue> q; + q.push({r, c}); + visit[r][c] = true; + bool res = true; + + while (!q.empty()) { + auto [cr, cc] = q.front(); q.pop(); + + if (grid1[cr][cc] == 0) res = false; + + for (int i = 0; i < 4; i++) { + int nr = cr + directions[i], nc = cc + directions[i + 1]; + if (nr >= 0 && nr < grid1.size() && nc >= 0 && nc < grid1[0].size() && + grid2[nr][nc] == 1 && !visit[nr][nc]) { + visit[nr][nc] = true; + q.push({nr, nc}); + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid1 + * @param {number[][]} grid2 + * @return {number} + */ + countSubIslands(grid1, grid2) { + const ROWS = grid1.length, COLS = grid1[0].length; + const visit = Array.from({ length: ROWS }, () => Array(COLS).fill(false)); + const directions = [1, 0, -1, 0, 1]; + let count = 0; + + const bfs = (sr, sc) => { + const queue = new Queue([[sr, sc]]); + visit[sr][sc] = true; + let res = true; + + while (!queue.isEmpty()) { + const [r, c] = queue.pop(); + if (grid1[r][c] === 0) res = false; + + for (let i = 0; i < 4; i++) { + const nr = r + directions[i], nc = c + directions[i + 1]; + if (nr >= 0 && nr < ROWS && nc >= 0 && nc < COLS && + grid2[nr][nc] === 1 && !visit[nr][nc]) { + visit[nr][nc] = true; + queue.push([nr, nc]); + } + } + } + return res; + }; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid2[r][c] === 1 && !visit[r][c]) { + if (bfs(r, c)) count++; + } + } + } + return count; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 3. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu = self.find(u) + pv = self.find(v) + if pu == pv: + return False + if self.Size[pu] < self.Size[pv]: + pu, pv = pv, pu + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + return True + +class Solution: + def countSubIslands(self, grid1, grid2): + ROWS, COLS = len(grid1), len(grid1[0]) + N = ROWS * COLS + dsu = DSU(N) + + def getId(r, c): + return r * COLS + c + + land = unions = 0 + for r in range(ROWS): + for c in range(COLS): + if not grid2[r][c]: + continue + land += 1 + if r + 1 < ROWS and grid2[r + 1][c]: + unions += dsu.union(getId(r, c), getId(r + 1, c)) + if c + 1 < COLS and grid2[r][c + 1]: + unions += dsu.union(getId(r, c), getId(r, c + 1)) + if not grid1[r][c]: + unions += dsu.union(getId(r, c), N) + + return land - unions +``` + +```java +class DSU { + int[] Parent, Size; + + DSU(int n) { + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) Parent[i] = i; + } + + int find(int node) { + if (Parent[node] != node) Parent[node] = find(Parent[node]); + return Parent[node]; + } + + int union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return 0; + if (Size[pu] < Size[pv]) { + int temp = pu; pu = pv; pv = temp; + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + return 1; + } +} + +public class Solution { + public int countSubIslands(int[][] grid1, int[][] grid2) { + int ROWS = grid1.length, COLS = grid1[0].length, N = ROWS * COLS; + DSU dsu = new DSU(N); + + int land = 0, unions = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid2[r][c] == 0) continue; + land++; + if (r + 1 < ROWS && grid2[r + 1][c] == 1) + unions += dsu.union(r * COLS + c, (r + 1) * COLS + c); + if (c + 1 < COLS && grid2[r][c + 1] == 1) + unions += dsu.union(r * COLS + c, r * COLS + c + 1); + if (grid1[r][c] == 0) + unions += dsu.union(r * COLS + c, N); + } + } + return land - unions; + } +} +``` + +```cpp +class DSU { + vector Parent, Size; + +public: + DSU(int n) { + Parent.resize(n + 1); + Size.assign(n + 1, 1); + for (int i = 0; i <= n; i++) Parent[i] = i; + } + + int find(int node) { + if (Parent[node] != node) Parent[node] = find(Parent[node]); + return Parent[node]; + } + + bool unionSets(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (Size[pu] < Size[pv]) swap(pu, pv); + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } +}; + +class Solution { +public: + int countSubIslands(vector>& grid1, vector>& grid2) { + int ROWS = grid1.size(), COLS = grid1[0].size(), N = ROWS * COLS; + DSU dsu(N); + + int land = 0, unions = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (!grid2[r][c]) continue; + land++; + if (r + 1 < ROWS && grid2[r + 1][c]) + unions += dsu.unionSets(r * COLS + c, (r + 1) * COLS + c); + if (c + 1 < COLS && grid2[r][c + 1]) + unions += dsu.unionSets(r * COLS + c, r * COLS + c + 1); + if (!grid1[r][c]) + unions += dsu.unionSets(r * COLS + c, N); + } + } + return land - unions; + } +}; +``` + +```javascript +class DSU { + constructor(n) { + this.Parent = Array.from({ length: n + 1 }, (_, i) => i); + this.Size = Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.Parent[node] !== node) { + this.Parent[node] = this.find(this.Parent[node]); + } + return this.Parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u), pv = this.find(v); + if (pu === pv) return false; + if (this.Size[pu] < this.Size[pv]) [pu, pv] = [pv, pu]; + this.Size[pu] += this.Size[pv]; + this.Parent[pv] = pu; + return true; + } +} + +class Solution { + /** + * @param {number[][]} grid1 + * @param {number[][]} grid2 + * @return {number} + */ + countSubIslands(grid1, grid2) { + const ROWS = grid1.length, COLS = grid1[0].length, N = ROWS * COLS; + const dsu = new DSU(N); + + const getId = (r, c) => r * COLS + c; + + let land = 0, unions = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid2[r][c] === 0) continue; + land++; + if (r + 1 < ROWS && grid2[r + 1][c] === 1) + unions += dsu.union(getId(r, c), getId(r + 1, c)); + if (c + 1 < COLS && grid2[r][c + 1] === 1) + unions += dsu.union(getId(r, c), getId(r, c + 1)); + if (grid1[r][c] === 0) + unions += dsu.union(getId(r, c), N); + } + } + return land - unions; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. \ No newline at end of file diff --git a/articles/count-subarrays-where-max-element-appears-at-least-k-times.md b/articles/count-subarrays-where-max-element-appears-at-least-k-times.md new file mode 100644 index 000000000..c4d2a5914 --- /dev/null +++ b/articles/count-subarrays-where-max-element-appears-at-least-k-times.md @@ -0,0 +1,589 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def countSubarrays(self, nums: List[int], k: int) -> int: + n, res = len(nums), 0 + maxi = max(nums) + + for i in range(n): + cnt = 0 + for j in range(i, n): + if nums[j] == maxi: + cnt += 1 + + if cnt >= k: + res += 1 + + return res +``` + +```java +public class Solution { + public long countSubarrays(int[] nums, int k) { + int n = nums.length; + long res = 0; + int maxi = Integer.MIN_VALUE; + + for (int num : nums) { + maxi = Math.max(maxi, num); + } + + for (int i = 0; i < n; i++) { + int cnt = 0; + for (int j = i; j < n; j++) { + if (nums[j] == maxi) { + cnt++; + } + + if (cnt >= k) { + res++; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long countSubarrays(vector& nums, int k) { + int n = nums.size(); + long long res = 0; + int maxi = *max_element(nums.begin(), nums.end()); + + for (int i = 0; i < n; i++) { + int cnt = 0; + for (int j = i; j < n; j++) { + if (nums[j] == maxi) { + cnt++; + } + + if (cnt >= k) { + res++; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + countSubarrays(nums, k) { + let n = nums.length, res = 0; + let maxi = Math.max(...nums); + + for (let i = 0; i < n; i++) { + let cnt = 0; + for (let j = i; j < n; j++) { + if (nums[j] === maxi) { + cnt++; + } + + if (cnt >= k) { + res++; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Variable Size Sliding Window + +::tabs-start + +```python +class Solution: + def countSubarrays(self, nums: List[int], k: int) -> int: + max_n, max_cnt = max(nums), 0 + l = 0 + res = 0 + + for r in range(len(nums)): + if nums[r] == max_n: + max_cnt += 1 + + while max_cnt > k or (l <= r and max_cnt == k and nums[l] != max_n): + if nums[l] == max_n: + max_cnt -= 1 + l += 1 + + if max_cnt == k: + res += l + 1 + + return res +``` + +```java +public class Solution { + public long countSubarrays(int[] nums, int k) { + int maxN = Integer.MIN_VALUE, maxCnt = 0, l = 0; + long res = 0; + for (int num : nums) { + maxN = Math.max(maxN, num); + } + + for (int r = 0; r < nums.length; r++) { + if (nums[r] == maxN) { + maxCnt++; + } + + while (maxCnt > k || (l <= r && maxCnt == k && nums[l] != maxN)) { + if (nums[l] == maxN) { + maxCnt--; + } + l++; + } + + if (maxCnt == k) { + res += l + 1; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long countSubarrays(vector& nums, int k) { + int maxN = *max_element(nums.begin(), nums.end()); + int maxCnt = 0, l = 0; + long long res = 0; + + for (int r = 0; r < nums.size(); r++) { + if (nums[r] == maxN) { + maxCnt++; + } + + while (maxCnt > k || (l <= r && maxCnt == k && nums[l] != maxN)) { + if (nums[l] == maxN) { + maxCnt--; + } + l++; + } + + if (maxCnt == k) { + res += l + 1; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + countSubarrays(nums, k) { + let maxN = Math.max(...nums); + let maxCnt = 0, l = 0, res = 0; + + for (let r = 0; r < nums.length; r++) { + if (nums[r] === maxN) { + maxCnt++; + } + + while (maxCnt > k || (l <= r && maxCnt === k && nums[l] !== maxN)) { + if (nums[l] === maxN) { + maxCnt--; + } + l++; + } + + if (maxCnt === k) { + res += l + 1; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Variable Size Sliding Window (Optimal) + +::tabs-start + +```python +class Solution: + def countSubarrays(self, nums: List[int], k: int) -> int: + max_n, max_cnt = max(nums), 0 + l = res = 0 + + for r in range(len(nums)): + if nums[r] == max_n: + max_cnt += 1 + while max_cnt == k: + if nums[l] == max_n: + max_cnt -= 1 + l += 1 + res += l + + return res +``` + +```java +public class Solution { + public long countSubarrays(int[] nums, int k) { + int max_n = Integer.MIN_VALUE, max_cnt = 0, l = 0; + long res = 0; + for (int num : nums) { + max_n = Math.max(max_n, num); + } + + for (int r = 0; r < nums.length; r++) { + if (nums[r] == max_n) { + max_cnt++; + } + while (max_cnt == k) { + if (nums[l] == max_n) { + max_cnt--; + } + l++; + } + res += l; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long countSubarrays(vector& nums, int k) { + int max_n = *max_element(nums.begin(), nums.end()); + int max_cnt = 0, l = 0; + long long res = 0; + + for (int r = 0; r < nums.size(); r++) { + if (nums[r] == max_n) { + max_cnt++; + } + while (max_cnt == k) { + if (nums[l] == max_n) { + max_cnt--; + } + l++; + } + res += l; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + countSubarrays(nums, k) { + let max_n = Math.max(...nums), max_cnt = 0, l = 0, res = 0; + + for (let r = 0; r < nums.length; r++) { + if (nums[r] === max_n) { + max_cnt++; + } + while (max_cnt === k) { + if (nums[l] === max_n) { + max_cnt--; + } + l++; + } + res += l; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Fixed Size Sliding Window + Math + +::tabs-start + +```python +class Solution: + def countSubarrays(self, nums: List[int], k: int) -> int: + n = len(nums) + max_n = max(nums) + max_indexes = [-1] + for i, num in enumerate(nums): + if num == max_n: + max_indexes.append(i) + + res = 0 + for i in range(1, len(max_indexes) - k + 1): + cur = (max_indexes[i] - max_indexes[i - 1]) + cur *= (n - max_indexes[i + k - 1]) + res += cur + + return res +``` + +```java +public class Solution { + public long countSubarrays(int[] nums, int k) { + int n = nums.length; + int max_n = Integer.MIN_VALUE; + for (int num : nums) { + max_n = Math.max(max_n, num); + } + + List max_indexes = new ArrayList<>(); + max_indexes.add(-1); + for (int i = 0; i < n; i++) { + if (nums[i] == max_n) { + max_indexes.add(i); + } + } + + long res = 0; + for (int i = 1; i <= max_indexes.size() - k; i++) { + long cur = (max_indexes.get(i) - max_indexes.get(i - 1)); + cur *= (n - max_indexes.get(i + k - 1)); + res += cur; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long countSubarrays(vector& nums, int k) { + int n = nums.size(); + int max_n = *max_element(nums.begin(), nums.end()); + vector max_indexes = {-1}; + + for (int i = 0; i < n; i++) { + if (nums[i] == max_n) { + max_indexes.push_back(i); + } + } + + long long res = 0; + for (int i = 1; i <= int(max_indexes.size()) - k; i++) { + long long cur = (max_indexes[i] - max_indexes[i - 1]); + cur *= (n - max_indexes[i + k - 1]); + res += cur; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + countSubarrays(nums, k) { + const n = nums.length; + const max_n = Math.max(...nums); + const max_indexes = [-1]; + + for (let i = 0; i < n; i++) { + if (nums[i] === max_n) { + max_indexes.push(i); + } + } + + let res = 0; + for (let i = 1; i <= max_indexes.length - k; i++) { + res += (max_indexes[i] - max_indexes[i - 1]) * (n - max_indexes[i + k - 1]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Fixed Size Sliding Window (Optimal) + +::tabs-start + +```python +class Solution: + def countSubarrays(self, nums: List[int], k: int) -> int: + max_n = max(nums) + max_indexes = deque() + res = 0 + + for i, num in enumerate(nums): + if num == max_n: + max_indexes.append(i) + + if len(max_indexes) > k: + max_indexes.popleft() + + if len(max_indexes) == k: + res += max_indexes[0] + 1 + + return res +``` + +```java +public class Solution { + public long countSubarrays(int[] nums, int k) { + int maxN = Integer.MIN_VALUE; + for (int num : nums) { + maxN = Math.max(maxN, num); + } + + Queue maxIndexes = new LinkedList<>(); + long res = 0; + + for (int i = 0; i < nums.length; i++) { + if (nums[i] == maxN) { + maxIndexes.add(i); + } + + if (maxIndexes.size() > k) { + maxIndexes.poll(); + } + + if (maxIndexes.size() == k) { + res += maxIndexes.peek() + 1; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long countSubarrays(vector& nums, int k) { + int maxN = *max_element(nums.begin(), nums.end()); + queue maxIndexes; + long long res = 0; + + for (int i = 0; i < nums.size(); i++) { + if (nums[i] == maxN) { + maxIndexes.push(i); + } + + if (maxIndexes.size() > k) { + maxIndexes.pop(); + } + + if (maxIndexes.size() == k) { + res += maxIndexes.front() + 1; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + countSubarrays(nums, k) { + const maxN = Math.max(...nums); + const maxIndexes = new Queue(); + let res = 0; + + for (let i = 0; i < nums.length; i++) { + if (nums[i] === maxN) { + maxIndexes.push(i); + } + + if (maxIndexes.size() > k) { + maxIndexes.pop(); + } + + if (maxIndexes.size() === k) { + res += maxIndexes.front() + 1; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/count-subsequences.md b/articles/count-subsequences.md new file mode 100644 index 000000000..241876a84 --- /dev/null +++ b/articles/count-subsequences.md @@ -0,0 +1,1111 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def numDistinct(self, s: str, t: str) -> int: + if len(t) > len(s): + return 0 + + def dfs(i, j): + if j == len(t): + return 1 + if i == len(s): + return 0 + + res = dfs(i + 1, j) + if s[i] == t[j]: + res += dfs(i + 1, j + 1) + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + public int numDistinct(String s, String t) { + if (t.length() > s.length()) { + return 0; + } + return dfs(s, t, 0, 0); + } + + private int dfs(String s, String t, int i, int j) { + if (j == t.length()) { + return 1; + } + if (i == s.length()) { + return 0; + } + + int res = dfs(s, t, i + 1, j); + if (s.charAt(i) == t.charAt(j)) { + res += dfs(s, t, i + 1, j + 1); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numDistinct(string s, string t) { + if (t.length() > s.length()) { + return 0; + } + return dfs(s, t, 0, 0); + } + +private: + int dfs(const string &s, const string &t, int i, int j) { + if (j == t.length()) { + return 1; + } + if (i == s.length()) { + return 0; + } + + int res = dfs(s, t, i + 1, j); + if (s[i] == t[j]) { + res += dfs(s, t, i + 1, j + 1); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {number} + */ + numDistinct(s, t) { + if (t.length > s.length) { + return 0; + } + + const dfs = (i, j) => { + if (j === t.length) { + return 1; + } + if (i === s.length) { + return 0; + } + + let res = dfs(i + 1, j); + if (s[i] === t[j]) { + res += dfs(i + 1, j + 1); + } + return res; + } + + return dfs(0, 0); + } +} +``` + +```csharp +public class Solution { + public int NumDistinct(string s, string t) { + if (t.Length > s.Length) { + return 0; + } + return Dfs(s, t, 0, 0); + } + + private int Dfs(string s, string t, int i, int j) { + if (j == t.Length) { + return 1; + } + if (i == s.Length) { + return 0; + } + + int res = Dfs(s, t, i + 1, j); + if (s[i] == t[j]) { + res += Dfs(s, t, i + 1, j + 1); + } + return res; + } +} +``` + +```go +func numDistinct(s string, t string) int { + if len(t) > len(s) { + return 0 + } + + var dfs func(i, j int) int + dfs = func(i, j int) int { + if j == len(t) { + return 1 + } + if i == len(s) { + return 0 + } + + res := dfs(i+1, j) + if s[i] == t[j] { + res += dfs(i+1, j+1) + } + return res + } + + return dfs(0, 0) +} +``` + +```kotlin +class Solution { + fun numDistinct(s: String, t: String): Int { + if (t.length > s.length) return 0 + + fun dfs(i: Int, j: Int): Int { + if (j == t.length) return 1 + if (i == s.length) return 0 + + var res = dfs(i + 1, j) + if (s[i] == t[j]) { + res += dfs(i + 1, j + 1) + } + return res + } + + return dfs(0, 0) + } +} +``` + +```swift +class Solution { + func numDistinct(_ s: String, _ t: String) -> Int { + let sArray = Array(s), tArray = Array(t) + let sLen = sArray.count, tLen = tArray.count + if tLen > sLen { return 0 } + + func dfs(_ i: Int, _ j: Int) -> Int { + if j == tLen { return 1 } + if i == sLen { return 0 } + + var res = dfs(i + 1, j) + if sArray[i] == tArray[j] { + res += dfs(i + 1, j + 1) + } + return res + } + + return dfs(0, 0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ m)$ +* Space complexity: $O(m)$ + +> Where $m$ is the length of the string $s$. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def numDistinct(self, s: str, t: str) -> int: + if len(t) > len(s): + return 0 + + dp = {} + def dfs(i, j): + if j == len(t): + return 1 + if i == len(s): + return 0 + if (i, j) in dp: + return dp[(i, j)] + + res = dfs(i + 1, j) + if s[i] == t[j]: + res += dfs(i + 1, j + 1) + dp[(i, j)] = res + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + private int[][] dp; + + public int numDistinct(String s, String t) { + int m = s.length(), n = t.length(); + if (n > m) return 0; + dp = new int[m + 1][n + 1]; + for (int i = 0; i <= m; i++) { + Arrays.fill(dp[i], -1); + } + return dfs(s, t, 0, 0); + } + + private int dfs(String s, String t, int i, int j) { + if (j == t.length()) return 1; + if (i == s.length()) return 0; + if (dp[i][j] != -1) return dp[i][j]; + + int res = dfs(s, t, i + 1, j); + if (s.charAt(i) == t.charAt(j)) { + res += dfs(s, t, i + 1, j + 1); + } + dp[i][j] = res; + return res; + } +} +``` + +```cpp +class Solution { + vector> dp; +public: + int numDistinct(string s, string t) { + int m = s.size(), n = t.size(); + if (n > m) return 0; + dp.assign(m + 1, vector(n + 1, -1)); + return dfs(s, t, 0, 0); + } + +private: + int dfs(const string &s, const string &t, int i, int j) { + if (j == t.size()) return 1; + if (i == s.size()) return 0; + if (dp[i][j] != -1) return dp[i][j]; + + int res = dfs(s, t, i + 1, j); + if (s[i] == t[j]) { + res += dfs(s, t, i + 1, j + 1); + } + dp[i][j] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {number} + */ + numDistinct(s, t) { + let m = s.length, n = t.length; + if (n > m) return 0; + let dp = Array(m + 1).fill().map(() => + Array(n + 1).fill(-1)); + + const dfs = (i, j) => { + if (j === n) return 1; + if (i === m) return 0; + if (dp[i][j] !== -1) return dp[i][j]; + + let res = dfs(i + 1, j); + if (s[i] === t[j]) { + res += dfs(i + 1, j + 1); + } + dp[i][j] = res; + return res; + } + + return dfs(0, 0); + } +} +``` + +```csharp +public class Solution { + private int?[,] dp; + + public int NumDistinct(string s, string t) { + int m = s.Length, n = t.Length; + if (n > m) return 0; + dp = new int?[m + 1, n + 1]; + return Dfs(s, t, 0, 0); + } + + private int Dfs(string s, string t, int i, int j) { + if (j == t.Length) return 1; + if (i == s.Length) return 0; + if (dp[i, j].HasValue) return dp[i, j].Value; + + int res = Dfs(s, t, i + 1, j); + if (s[i] == t[j]) { + res += Dfs(s, t, i + 1, j + 1); + } + dp[i, j] = res; + return res; + } +} +``` + +```go +func numDistinct(s string, t string) int { + if len(t) > len(s) { + return 0 + } + + dp := make([][]int, len(s)+1) + for i := range dp { + dp[i] = make([]int, len(t)+1) + for j := range dp[i] { + dp[i][j] = -1 + } + } + + var dfs func(i, j int) int + dfs = func(i, j int) int { + if j == len(t) { + return 1 + } + if i == len(s) { + return 0 + } + if dp[i][j] != -1 { + return dp[i][j] + } + + res := dfs(i+1, j) + if s[i] == t[j] { + res += dfs(i+1, j+1) + } + + dp[i][j] = res + return res + } + + return dfs(0, 0) +} +``` + +```kotlin +class Solution { + fun numDistinct(s: String, t: String): Int { + if (t.length > s.length) return 0 + + val dp = Array(s.length + 1) { IntArray(t.length + 1) { -1 } } + + fun dfs(i: Int, j: Int): Int { + if (j == t.length) return 1 + if (i == s.length) return 0 + if (dp[i][j] != -1) return dp[i][j] + + var res = dfs(i + 1, j) + if (s[i] == t[j]) { + res += dfs(i + 1, j + 1) + } + + dp[i][j] = res + return res + } + + return dfs(0, 0) + } +} +``` + +```swift +class Solution { + func numDistinct(_ s: String, _ t: String) -> Int { + let sArray = Array(s), tArray = Array(t) + let sLen = sArray.count, tLen = tArray.count + if tLen > sLen { return 0 } + + var dp = [[Int]: Int]() + + func dfs(_ i: Int, _ j: Int) -> Int { + if j == tLen { return 1 } + if i == sLen { return 0 } + if let val = dp[[i, j]] { return val } + + var res = dfs(i + 1, j) + if sArray[i] == tArray[j] { + res += dfs(i + 1, j + 1) + } + dp[[i, j]] = res + return res + } + + return dfs(0, 0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the length of the string $s$ and $n$ is the length of the string $t$. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def numDistinct(self, s: str, t: str) -> int: + m, n = len(s), len(t) + dp = [[0] * (n + 1) for _ in range(m + 1)] + + for i in range(m + 1): + dp[i][n] = 1 + + for i in range(m - 1, -1, -1): + for j in range(n - 1, -1, -1): + dp[i][j] = dp[i + 1][j] + if s[i] == t[j]: + dp[i][j] += dp[i + 1][j + 1] + + return dp[0][0] +``` + +```java +public class Solution { + public int numDistinct(String s, String t) { + int m = s.length(), n = t.length(); + int[][] dp = new int[m + 1][n + 1]; + + for (int i = 0; i <= m; i++) { + dp[i][n] = 1; + } + + for (int i = m - 1; i >= 0; i--) { + for (int j = n - 1; j >= 0; j--) { + dp[i][j] = dp[i + 1][j]; + if (s.charAt(i) == t.charAt(j)) { + dp[i][j] += dp[i + 1][j + 1]; + } + } + } + + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + int numDistinct(string s, string t) { + int m = s.length(), n = t.length(); + vector> dp(m + 1, vector(n + 1, 0)); + + for (int i = 0; i <= m; i++) { + dp[i][n] = 1; + } + + for (int i = m - 1; i >= 0; i--) { + for (int j = n - 1; j >= 0; j--) { + dp[i][j] = dp[i + 1][j]; + if (s[i] == t[j]) { + dp[i][j] += dp[i + 1][j + 1]; + } + } + } + + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {number} + */ + numDistinct(s, t) { + let m = s.length, n = t.length; + let dp = Array.from({ length: m + 1 }, () => + Array(n + 1).fill(0)); + + for (let i = 0; i <= m; i++) { + dp[i][n] = 1; + } + + for (let i = m - 1; i >= 0; i--) { + for (let j = n - 1; j >= 0; j--) { + dp[i][j] = dp[i + 1][j]; + if (s[i] === t[j]) { + dp[i][j] += dp[i + 1][j + 1]; + } + } + } + + return dp[0][0]; + } +} +``` + +```csharp +public class Solution { + public int NumDistinct(string s, string t) { + int m = s.Length, n = t.Length; + int[,] dp = new int[m + 1, n + 1]; + + for (int i = 0; i <= m; i++) { + dp[i, n] = 1; + } + + for (int i = m - 1; i >= 0; i--) { + for (int j = n - 1; j >= 0; j--) { + dp[i, j] = dp[i + 1, j]; + if (s[i] == t[j]) { + dp[i, j] += dp[i + 1, j + 1]; + } + } + } + + return dp[0, 0]; + } +} +``` + +```go +func numDistinct(s string, t string) int { + m, n := len(s), len(t) + dp := make([][]int, m+1) + for i := range dp { + dp[i] = make([]int, n+1) + } + + for i := 0; i <= m; i++ { + dp[i][n] = 1 + } + + for i := m - 1; i >= 0; i-- { + for j := n - 1; j >= 0; j-- { + dp[i][j] = dp[i+1][j] + if s[i] == t[j] { + dp[i][j] += dp[i+1][j+1] + } + } + } + + return dp[0][0] +} +``` + +```kotlin +class Solution { + fun numDistinct(s: String, t: String): Int { + val m = s.length + val n = t.length + val dp = Array(m + 1) { IntArray(n + 1) } + + for (i in 0..m) { + dp[i][n] = 1 + } + + for (i in m - 1 downTo 0) { + for (j in n - 1 downTo 0) { + dp[i][j] = dp[i + 1][j] + if (s[i] == t[j]) { + dp[i][j] += dp[i + 1][j + 1] + } + } + } + + return dp[0][0] + } +} +``` + +```swift +class Solution { + func numDistinct(_ s: String, _ t: String) -> Int { + let m = s.count, n = t.count + var dp = Array( + repeating: Array(repeating: 0, count: n + 1), + count: m + 1 + ) + + let sArray = Array(s) + let tArray = Array(t) + + for i in 0...m { + dp[i][n] = 1 + } + + for i in stride(from: m - 1, through: 0, by: -1) { + for j in stride(from: n - 1, through: 0, by: -1) { + let base = dp[i + 1][j] + dp[i][j] = base + if sArray[i] == tArray[j] { + let addend = dp[i + 1][j + 1] + if base > Int.max - addend { + dp[i][j] = 0 + } else { + dp[i][j] += addend + } + } + } + } + + return dp[0][0] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the length of the string $s$ and $n$ is the length of the string $t$. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def numDistinct(self, s: str, t: str) -> int: + m, n = len(s), len(t) + dp = [0] * (n + 1) + nextDp = [0] * (n + 1) + + dp[n] = nextDp[n] = 1 + for i in range(m - 1, -1, -1): + for j in range(n - 1, -1, -1): + nextDp[j] = dp[j] + if s[i] == t[j]: + nextDp[j] += dp[j + 1] + dp = nextDp[:] + + return dp[0] +``` + +```java +public class Solution { + public int numDistinct(String s, String t) { + int m = s.length(), n = t.length(); + int[] dp = new int[n + 1]; + int[] nextDp = new int[n + 1]; + + dp[n] = nextDp[n] = 1; + for (int i = m - 1; i >= 0; i--) { + for (int j = n - 1; j >= 0; j--) { + nextDp[j] = dp[j]; + if (s.charAt(i) == t.charAt(j)) { + nextDp[j] += dp[j + 1]; + } + } + dp = nextDp.clone(); + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int numDistinct(string s, string t) { + int m = s.size(), n = t.size(); + vector dp(n + 1, 0); + vector nextDp(n + 1, 0); + dp[n] = nextDp[n] = 1; + + for (int i = m - 1; i >= 0; i--) { + for (int j = n - 1; j >= 0; j--) { + nextDp[j] = dp[j]; + if (s[i] == t[j]) { + nextDp[j] += dp[j + 1]; + } + } + dp = nextDp; + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {number} + */ + numDistinct(s, t) { + let m = s.length, n = t.length; + let dp = new Array(n + 1).fill(0); + let nextDp = new Array(n + 1).fill(0); + + dp[n] = nextDp[n] = 1; + for (let i = m - 1; i >= 0; i--) { + for (let j = n - 1; j >= 0; j--) { + nextDp[j] = dp[j]; + if (s[i] === t[j]) { + nextDp[j] += dp[j + 1]; + } + } + dp = nextDp.slice(); + } + + return dp[0]; + } +} +``` + +```csharp +public class Solution { + public int NumDistinct(string s, string t) { + int m = s.Length, n = t.Length; + int[] dp = new int[n + 1]; + int[] nextDp = new int[n + 1]; + + dp[n] = nextDp[n] = 1; + for (int i = m - 1; i >= 0; i--) { + for (int j = n - 1; j >= 0; j--) { + nextDp[j] = dp[j]; + if (s[i] == t[j]) { + nextDp[j] += dp[j + 1]; + } + } + dp = (int[])nextDp.Clone(); + } + + return dp[0]; + } +} +``` + +```go +func numDistinct(s string, t string) int { + m, n := len(s), len(t) + dp := make([]int, n+1) + nextDp := make([]int, n+1) + + dp[n] = 1 + nextDp[n] = 1 + + for i := m - 1; i >= 0; i-- { + for j := n - 1; j >= 0; j-- { + nextDp[j] = dp[j] + if s[i] == t[j] { + nextDp[j] += dp[j+1] + } + } + dp = append([]int(nil), nextDp...) + } + + return dp[0] +} +``` + +```kotlin +class Solution { + fun numDistinct(s: String, t: String): Int { + val m = s.length + val n = t.length + var dp = IntArray(n + 1) + var nextDp = IntArray(n + 1) + + dp[n] = 1 + nextDp[n] = 1 + + for (i in m - 1 downTo 0) { + for (j in n - 1 downTo 0) { + nextDp[j] = dp[j] + if (s[i] == t[j]) { + nextDp[j] += dp[j + 1] + } + } + dp = nextDp.copyOf() + } + + return dp[0] + } +} +``` + +```swift +class Solution { + func numDistinct(_ s: String, _ t: String) -> Int { + let m = s.count, n = t.count + var dp = [Int](repeating: 0, count: n + 1) + var nextDp = [Int](repeating: 0, count: n + 1) + dp[n] = 1 + nextDp[n] = 1 + + let sArr = Array(s) + let tArr = Array(t) + + for i in stride(from: m - 1, through: 0, by: -1) { + for j in stride(from: n - 1, through: 0, by: -1) { + nextDp[j] = dp[j] + if sArr[i] == tArr[j] { + let addend = dp[j + 1] + if nextDp[j] > Int.max - addend { + nextDp[j] = 0 + } else { + nextDp[j] += addend + } + } + } + dp = nextDp + } + + return dp[0] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(n)$ + +> Where $m$ is the length of the string $s$ and $n$ is the length of the string $t$. + +--- + +## 5. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def numDistinct(self, s: str, t: str) -> int: + m, n = len(s), len(t) + dp = [0] * (n + 1) + + dp[n] = 1 + for i in range(m - 1, -1, -1): + prev = 1 + for j in range(n - 1, -1, -1): + res = dp[j] + if s[i] == t[j]: + res += prev + + prev = dp[j] + dp[j] = res + + return dp[0] +``` + +```java +public class Solution { + public int numDistinct(String s, String t) { + int m = s.length(), n = t.length(); + int[] dp = new int[n + 1]; + + dp[n] = 1; + for (int i = m - 1; i >= 0; i--) { + int prev = 1; + for (int j = n - 1; j >= 0; j--) { + int res = dp[j]; + if (s.charAt(i) == t.charAt(j)) { + res += prev; + } + + prev = dp[j]; + dp[j] = res; + } + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int numDistinct(string s, string t) { + int m = s.size(), n = t.size(); + vector dp(n + 1, 0); + dp[n] = 1; + + for (int i = m - 1; i >= 0; i--) { + int prev = 1; + for (int j = n - 1; j >= 0; j--) { + uint res = dp[j]; + if (s[i] == t[j]) { + res += prev; + } + + prev = dp[j]; + dp[j] = res; + } + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {number} + */ + numDistinct(s, t) { + let m = s.length, n = t.length; + let dp = new Array(n + 1).fill(0); + + dp[n] = 1; + for (let i = m - 1; i >= 0; i--) { + let prev = 1; + for (let j = n - 1; j >= 0; j--) { + let res = dp[j]; + if (s[i] === t[j]) { + res += prev; + } + + prev = dp[j]; + dp[j] = res; + } + } + + return dp[0]; + } +} +``` + +```csharp +public class Solution { + public int NumDistinct(string s, string t) { + int m = s.Length, n = t.Length; + int[] dp = new int[n + 1]; + + dp[n] = 1; + for (int i = m - 1; i >= 0; i--) { + int prev = 1; + for (int j = n - 1; j >= 0; j--) { + int res = dp[j]; + if (s[i] == t[j]) { + res += prev; + } + + prev = dp[j]; + dp[j] = res; + } + } + + return dp[0]; + } +} +``` + +```go +func numDistinct(s string, t string) int { + m, n := len(s), len(t) + dp := make([]int, n+1) + + dp[n] = 1 + for i := m - 1; i >= 0; i-- { + prev := 1 + for j := n - 1; j >= 0; j-- { + res := dp[j] + if s[i] == t[j] { + res += prev + } + prev = dp[j] + dp[j] = res + } + } + + return dp[0] +} +``` + +```kotlin +class Solution { + fun numDistinct(s: String, t: String): Int { + val m = s.length + val n = t.length + val dp = IntArray(n + 1) + + dp[n] = 1 + for (i in m - 1 downTo 0) { + var prev = 1 + for (j in n - 1 downTo 0) { + var res = dp[j] + if (s[i] == t[j]) { + res += prev + } + prev = dp[j] + dp[j] = res + } + } + + return dp[0] + } +} +``` + +```swift +class Solution { + func numDistinct(_ s: String, _ t: String) -> Int { + let m = s.count, n = t.count + var dp = [Int](repeating: 0, count: n + 1) + dp[n] = 1 + let sArr = Array(s) + let tArr = Array(t) + + for i in stride(from: m - 1, through: 0, by: -1) { + var prev = 1 + for j in stride(from: n - 1, through: 0, by: -1) { + let base = dp[j] + var res = base + if sArr[i] == tArr[j] { + if base > Int.max - prev { + res = 0 + } else { + res += prev + } + } + prev = dp[j] + dp[j] = res + } + } + + return dp[0] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(n)$ + +> Where $m$ is the length of the string $s$ and $n$ is the length of the string $t$. \ No newline at end of file diff --git a/articles/count-the-number-of-consistent-strings.md b/articles/count-the-number-of-consistent-strings.md new file mode 100644 index 000000000..600698357 --- /dev/null +++ b/articles/count-the-number-of-consistent-strings.md @@ -0,0 +1,415 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def countConsistentStrings(self, allowed: str, words: List[str]) -> int: + res = 0 + + for w in words: + flag = 1 + for c in w: + if c not in allowed: + flag = 0 + break + res += flag + + return res +``` + +```java +public class Solution { + public int countConsistentStrings(String allowed, String[] words) { + int res = 0; + + for (String w : words) { + boolean flag = true; + for (char c : w.toCharArray()) { + if (allowed.indexOf(c) == -1) { + flag = false; + break; + } + } + if (flag) res++; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countConsistentStrings(string allowed, vector& words) { + int res = 0; + + for (string& w : words) { + bool flag = true; + for (char c : w) { + if (allowed.find(c) == string::npos) { + flag = false; + break; + } + } + res += flag; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} allowed + * @param {string[]} words + * @return {number} + */ + countConsistentStrings(allowed, words) { + let res = 0; + + for (let w of words) { + let flag = 1; + for (let c of w) { + if (!allowed.includes(c)) { + flag = 0; + break; + } + } + res += flag; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m * l)$ +* Space complexity: $O(1)$ + +> Where $n$ is the number of words, $m$ is the length of the string $allowed$, and $l$ is the length of the longest word. + +--- + +## 2. Hash Set + +::tabs-start + +```python +class Solution: + def countConsistentStrings(self, allowed: str, words: List[str]) -> int: + allowed = set(allowed) + + res = len(words) + for w in words: + for c in w: + if c not in allowed: + res -= 1 + break + + return res +``` + +```java +public class Solution { + public int countConsistentStrings(String allowed, String[] words) { + Set allowedSet = new HashSet<>(); + for (char c : allowed.toCharArray()) { + allowedSet.add(c); + } + + int res = words.length; + for (String w : words) { + for (char c : w.toCharArray()) { + if (!allowedSet.contains(c)) { + res--; + break; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countConsistentStrings(string allowed, vector& words) { + unordered_set allowedSet(allowed.begin(), allowed.end()); + + int res = words.size(); + for (string& w : words) { + for (char c : w) { + if (allowedSet.find(c) == allowedSet.end()) { + res--; + break; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} allowed + * @param {string[]} words + * @return {number} + */ + countConsistentStrings(allowed, words) { + const allowedSet = new Set(allowed); + let res = words.length; + + for (let w of words) { + for (let c of w) { + if (!allowedSet.has(c)) { + res--; + break; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * l + m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the number of words, $m$ is the length of the string $allowed$, and $l$ is the length of the longest word. + +--- + +## 3. Boolean Array + +::tabs-start + +```python +class Solution: + def countConsistentStrings(self, allowed: str, words: List[str]) -> int: + allowedArr = [False] * 26 + for c in allowed: + allowedArr[ord(c) - ord('a')] = True + + res = len(words) + for w in words: + for c in w: + if not allowedArr[ord(c) - ord('a')]: + res -= 1 + break + + return res +``` + +```java +public class Solution { + public int countConsistentStrings(String allowed, String[] words) { + Set allowedSet = new HashSet<>(); + for (char c : allowed.toCharArray()) { + allowedSet.add(c); + } + + int res = words.length; + for (String w : words) { + for (char c : w.toCharArray()) { + if (!allowedSet.contains(c)) { + res--; + break; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countConsistentStrings(string allowed, vector& words) { + bool allowedArr[26] = {}; + for (char c : allowed) { + allowedArr[c - 'a'] = true; + } + + int res = words.size(); + for (const string& w : words) { + for (char c : w) { + if (!allowedArr[c - 'a']) { + res--; + break; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} allowed + * @param {string[]} words + * @return {number} + */ + countConsistentStrings(allowed, words) { + const allowedArr = new Array(26).fill(false); + for (let c of allowed) { + allowedArr[c.charCodeAt(0) - 97] = true; + } + + let res = words.length; + for (let w of words) { + for (let c of w) { + if (!allowedArr[c.charCodeAt(0) - 97]) { + res--; + break; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * l + m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the number of words, $m$ is the length of the string $allowed$, and $l$ is the length of the longest word. + +--- + +## 4. Bitmask + +::tabs-start + +```python +class Solution: + def countConsistentStrings(self, allowed: str, words: List[str]) -> int: + bit_mask = 0 + for c in allowed: + bit = 1 << (ord(c) - ord('a')) + bit_mask = bit_mask | bit + + res = len(words) + for w in words: + for c in w: + bit = 1 << (ord(c) - ord('a')) + if bit & bit_mask == 0: + res -= 1 + break + + return res +``` + +```java +public class Solution { + public int countConsistentStrings(String allowed, String[] words) { + int bitMask = 0; + for (char c : allowed.toCharArray()) { + bitMask |= 1 << (c - 'a'); + } + + int res = words.length; + for (String w : words) { + for (char c : w.toCharArray()) { + int bit = 1 << (c - 'a'); + if ((bit & bitMask) == 0) { + res--; + break; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countConsistentStrings(string allowed, vector& words) { + int bitMask = 0; + for (char c : allowed) { + bitMask |= (1 << (c - 'a')); + } + + int res = words.size(); + for (const string& w : words) { + for (char c : w) { + int bit = 1 << (c - 'a'); + if ((bit & bitMask) == 0) { + res--; + break; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} allowed + * @param {string[]} words + * @return {number} + */ + countConsistentStrings(allowed, words) { + let bitMask = 0; + for (let c of allowed) { + bitMask |= 1 << (c.charCodeAt(0) - 97); + } + + let res = words.length; + for (let w of words) { + for (let c of w) { + const bit = 1 << (c.charCodeAt(0) - 97); + if ((bit & bitMask) === 0) { + res--; + break; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * l + m)$ +* Space complexity: $O(1)$ + +> Where $n$ is the number of words, $m$ is the length of the string $allowed$, and $l$ is the length of the longest word. \ No newline at end of file diff --git a/articles/count-triplets-that-can-form-two-arrays-of-equal-xor.md b/articles/count-triplets-that-can-form-two-arrays-of-equal-xor.md new file mode 100644 index 000000000..129c14f82 --- /dev/null +++ b/articles/count-triplets-that-can-form-two-arrays-of-equal-xor.md @@ -0,0 +1,432 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def countTriplets(self, arr: List[int]) -> int: + N = len(arr) + res = 0 + + for i in range(N - 1): + for j in range(i + 1, N): + for k in range(j, N): + a = b = 0 + for idx in range(i, j): + a ^= arr[idx] + for idx in range(j, k + 1): + b ^= arr[idx] + if a == b: + res += 1 + + return res +``` + +```java +public class Solution { + public int countTriplets(int[] arr) { + int N = arr.length; + int res = 0; + + for (int i = 0; i < N - 1; i++) { + for (int j = i + 1; j < N; j++) { + for (int k = j; k < N; k++) { + int a = 0, b = 0; + for (int idx = i; idx < j; idx++) { + a ^= arr[idx]; + } + for (int idx = j; idx <= k; idx++) { + b ^= arr[idx]; + } + if (a == b) { + res++; + } + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countTriplets(vector& arr) { + int N = arr.size(); + int res = 0; + + for (int i = 0; i < N - 1; ++i) { + for (int j = i + 1; j < N; ++j) { + for (int k = j; k < N; ++k) { + int a = 0, b = 0; + for (int idx = i; idx < j; ++idx) { + a ^= arr[idx]; + } + for (int idx = j; idx <= k; ++idx) { + b ^= arr[idx]; + } + if (a == b) { + res++; + } + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + countTriplets(arr) { + const N = arr.length; + let res = 0; + + for (let i = 0; i < N - 1; i++) { + for (let j = i + 1; j < N; j++) { + for (let k = j; k < N; k++) { + let a = 0, b = 0; + for (let idx = i; idx < j; idx++) { + a ^= arr[idx]; + } + for (let idx = j; idx <= k; idx++) { + b ^= arr[idx]; + } + if (a === b) { + res++; + } + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 4)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Brute Force (Optimized) + +::tabs-start + +```python +class Solution: + def countTriplets(self, arr: List[int]) -> int: + N = len(arr) + res = 0 + + for i in range(N - 1): + a = 0 + for j in range(i + 1, N): + a ^= arr[j - 1] + b = 0 + for k in range(j, N): + b ^= arr[k] + if a == b: + res += 1 + + return res +``` + +```java +public class Solution { + public int countTriplets(int[] arr) { + int N = arr.length; + int res = 0; + + for (int i = 0; i < N - 1; i++) { + int a = 0; + for (int j = i + 1; j < N; j++) { + a ^= arr[j - 1]; + int b = 0; + for (int k = j; k < N; k++) { + b ^= arr[k]; + if (a == b) { + res++; + } + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countTriplets(vector& arr) { + int N = arr.size(); + int res = 0; + + for (int i = 0; i < N - 1; ++i) { + int a = 0; + for (int j = i + 1; j < N; ++j) { + a ^= arr[j - 1]; + int b = 0; + for (int k = j; k < N; ++k) { + b ^= arr[k]; + if (a == b) { + res++; + } + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + countTriplets(arr) { + const N = arr.length; + let res = 0; + + for (let i = 0; i < N - 1; i++) { + let a = 0; + for (let j = i + 1; j < N; j++) { + a ^= arr[j - 1]; + let b = 0; + for (let k = j; k < N; k++) { + b ^= arr[k]; + if (a === b) { + res++; + } + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Math + Bitwise XOR + +::tabs-start + +```python +class Solution: + def countTriplets(self, arr: List[int]) -> int: + N = len(arr) + res = 0 + + for i in range(N - 1): + cur_xor = arr[i] + for k in range(i + 1, N): + cur_xor ^= arr[k] + if cur_xor == 0: + res += k - i + + return res +``` + +```java +public class Solution { + public int countTriplets(int[] arr) { + int N = arr.length; + int res = 0; + + for (int i = 0; i < N - 1; i++) { + int curXor = arr[i]; + for (int k = i + 1; k < N; k++) { + curXor ^= arr[k]; + if (curXor == 0) { + res += k - i; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countTriplets(vector& arr) { + int N = arr.size(); + int res = 0; + + for (int i = 0; i < N - 1; ++i) { + int curXor = arr[i]; + for (int k = i + 1; k < N; ++k) { + curXor ^= arr[k]; + if (curXor == 0) { + res += k - i; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + countTriplets(arr) { + const N = arr.length; + let res = 0; + + for (let i = 0; i < N - 1; i++) { + let curXor = arr[i]; + for (let k = i + 1; k < N; k++) { + curXor ^= arr[k]; + if (curXor === 0) { + res += k - i; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Math + Bitwise XOR (Optimal) + +::tabs-start + +```python +class Solution: + def countTriplets(self, arr: List[int]) -> int: + N = len(arr) + res = prefix = 0 + count = defaultdict(int) # number of prefixes + index_sum = defaultdict(int) # sum of indices with that prefix + count[0] = 1 + + for i in range(N): + prefix ^= arr[i] + if prefix in count: + res += i * count[prefix] - index_sum[prefix] + count[prefix] += 1 + index_sum[prefix] += i + 1 + + return res +``` + +```java +public class Solution { + public int countTriplets(int[] arr) { + int N = arr.length, res = 0, prefix = 0; + Map count = new HashMap<>(); // number of prefixes + Map indexSum = new HashMap<>(); // sum of indices with that prefix + count.put(0, 1); + + for (int i = 0; i < N; i++) { + prefix ^= arr[i]; + if (count.containsKey(prefix)) { + res += i * count.get(prefix) - indexSum.getOrDefault(prefix, 0); + } + count.put(prefix, count.getOrDefault(prefix, 0) + 1); + indexSum.put(prefix, indexSum.getOrDefault(prefix, 0) + i + 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countTriplets(vector& arr) { + int N = arr.size(), res = 0, prefix = 0; + unordered_map count; // number of prefixes + unordered_map indexSum; // sum of indices with that prefix + count[0] = 1; + + for (int i = 0; i < N; i++) { + prefix ^= arr[i]; + if (count.count(prefix)) { + res += i * count[prefix] - indexSum[prefix]; + } + count[prefix]++; + indexSum[prefix] += i + 1; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + countTriplets(arr) { + const N = arr.length; + let res = 0, prefix = 0; + const count = new Map(); // number of prefixes + const indexSum = new Map(); // sum of indices with that prefix + count.set(0, 1); + + for (let i = 0; i < N; i++) { + prefix ^= arr[i]; + if (count.has(prefix)) { + res += i * count.get(prefix) - (indexSum.get(prefix) || 0); + } + count.set(prefix, (count.get(prefix) || 0) + 1); + indexSum.set(prefix, (indexSum.get(prefix) || 0) + i + 1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/count-vowel-strings-in-ranges.md b/articles/count-vowel-strings-in-ranges.md new file mode 100644 index 000000000..22c92a839 --- /dev/null +++ b/articles/count-vowel-strings-in-ranges.md @@ -0,0 +1,336 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def vowelStrings(self, words: List[str], queries: List[List[int]]) -> List[int]: + vowels = set("aeiou") + res = [] + + for start, end in queries: + cnt = 0 + for i in range(start, end + 1): + if words[i][0] in vowels and words[i][-1] in vowels: + cnt += 1 + res.append(cnt) + + return res +``` + +```java +public class Solution { + public int[] vowelStrings(String[] words, int[][] queries) { + Set vowels = Set.of('a', 'e', 'i', 'o', 'u'); + int[] res = new int[queries.length]; + + for (int k = 0; k < queries.length; k++) { + int start = queries[k][0], end = queries[k][1], count = 0; + + for (int i = start; i <= end; i++) { + String word = words[i]; + if (vowels.contains(word.charAt(0)) && vowels.contains(word.charAt(word.length() - 1))) { + count++; + } + } + + res[k] = count; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector vowelStrings(vector& words, vector>& queries) { + unordered_set vowels = {'a', 'e', 'i', 'o', 'u'}; + vector res; + + for (auto& q : queries) { + int start = q[0], end = q[1], count = 0; + + for (int i = start; i <= end; i++) { + if (vowels.count(words[i][0]) && vowels.count(words[i].back())) { + count++; + } + } + + res.push_back(count); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {number[][]} queries + * @return {number[]} + */ + vowelStrings(words, queries) { + const vowels = new Set(['a', 'e', 'i', 'o', 'u']); + const res = []; + + for (let [start, end] of queries) { + let count = 0; + for (let i = start; i <= end; i++) { + const word = words[i]; + if (vowels.has(word[0]) && vowels.has(word[word.length - 1])) { + count++; + } + } + res.push(count); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: + * $O(1)$ extra space. + * $O(m)$ space for the output list. + +> Where $n$ is the number of words, and $m$ is the number of queries. + +--- + +## 2. Prefix Sum + Hash Set + +::tabs-start + +```python +class Solution: + def vowelStrings(self, words: List[str], queries: List[List[int]]) -> List[int]: + vowel_set = set("aeiou") + prefix_cnt = [0] * (len(words) + 1) + prev = 0 + + for i, w in enumerate(words): + if w[0] in vowel_set and w[-1] in vowel_set: + prev += 1 + prefix_cnt[i + 1] = prev + + res = [0] * len(queries) + for i, q in enumerate(queries): + l, r = q + res[i] = prefix_cnt[r + 1] - prefix_cnt[l] + + return res +``` + +```java +public class Solution { + public int[] vowelStrings(String[] words, int[][] queries) { + Set vowels = Set.of('a', 'e', 'i', 'o', 'u'); + int n = words.length; + int[] prefixCnt = new int[n + 1]; + + for (int i = 0; i < n; i++) { + String w = words[i]; + prefixCnt[i + 1] = prefixCnt[i]; + if (vowels.contains(w.charAt(0)) && vowels.contains(w.charAt(w.length() - 1))) { + prefixCnt[i + 1]++; + } + } + + int[] res = new int[queries.length]; + for (int i = 0; i < queries.length; i++) { + int l = queries[i][0], r = queries[i][1]; + res[i] = prefixCnt[r + 1] - prefixCnt[l]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector vowelStrings(vector& words, vector>& queries) { + unordered_set vowels = {'a', 'e', 'i', 'o', 'u'}; + int n = words.size(); + vector prefixCnt(n + 1, 0); + + for (int i = 0; i < n; i++) { + prefixCnt[i + 1] = prefixCnt[i]; + if (vowels.count(words[i][0]) && vowels.count(words[i].back())) { + prefixCnt[i + 1]++; + } + } + + vector res; + for (auto& q : queries) { + int l = q[0], r = q[1]; + res.push_back(prefixCnt[r + 1] - prefixCnt[l]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {number[][]} queries + * @return {number[]} + */ + vowelStrings(words, queries) { + const vowels = new Set(['a', 'e', 'i', 'o', 'u']); + const n = words.length; + const prefixCnt = new Array(n + 1).fill(0); + + for (let i = 0; i < n; i++) { + prefixCnt[i + 1] = prefixCnt[i]; + const w = words[i]; + if (vowels.has(w[0]) && vowels.has(w[w.length - 1])) { + prefixCnt[i + 1]++; + } + } + + const res = new Array(queries.length); + for (let i = 0; i < queries.length; i++) { + const [l, r] = queries[i]; + res[i] = prefixCnt[r + 1] - prefixCnt[l]; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: + * $O(n)$ extra space. + * $O(m)$ space for the output list. + +> Where $n$ is the number of words, and $m$ is the number of queries. + +--- + +## 3. Prefix Sum + Bitmask + +::tabs-start + +```python +class Solution: + def vowelStrings(self, words: List[str], queries: List[List[int]]) -> List[int]: + vowels = sum(1 << (ord(c) - ord('a')) for c in "aeiou") + prefix = [0] + for w in words: + prefix.append(prefix[-1]) + if (1 << (ord(w[0]) - ord('a'))) & vowels and (1 << (ord(w[-1]) - ord('a'))) & vowels: + prefix[-1] += 1 + return [prefix[r + 1] - prefix[l] for l, r in queries] +``` + +```java +public class Solution { + public int[] vowelStrings(String[] words, int[][] queries) { + int vowels = 0; + for (char c : "aeiou".toCharArray()) { + vowels |= 1 << (c - 'a'); + } + + int[] prefix = new int[words.length + 1]; + for (int i = 0; i < words.length; i++) { + int f = words[i].charAt(0) - 'a'; + int l = words[i].charAt(words[i].length() - 1) - 'a'; + int isVowel = ((1 << f) & vowels) != 0 && ((1 << l) & vowels) != 0 ? 1 : 0; + prefix[i + 1] = prefix[i] + isVowel; + } + + int[] res = new int[queries.length]; + for (int i = 0; i < queries.length; i++) { + int l = queries[i][0], r = queries[i][1]; + res[i] = prefix[r + 1] - prefix[l]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector vowelStrings(vector& words, vector>& queries) { + int vowels = 0; + for (char c : string("aeiou")) { + vowels |= (1 << (c - 'a')); + } + + int n = words.size(); + vector prefix(n + 1); + for (int i = 0; i < n; i++) { + int f = words[i][0] - 'a'; + int l = words[i].back() - 'a'; + int isVowel = ((1 << f) & vowels) && ((1 << l) & vowels); + prefix[i + 1] = prefix[i] + isVowel; + } + + vector res; + for (auto& q : queries) { + int l = q[0], r = q[1]; + res.push_back(prefix[r + 1] - prefix[l]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {number[][]} queries + * @return {number[]} + */ + vowelStrings(words, queries) { + let vowels = 0; + for (let c of "aeiou") { + vowels |= 1 << (c.charCodeAt(0) - 97); + } + + const prefix = [0]; + for (let w of words) { + const f = w.charCodeAt(0) - 97; + const l = w.charCodeAt(w.length - 1) - 97; + const isVowel = ((1 << f) & vowels) && ((1 << l) & vowels) ? 1 : 0; + prefix.push(prefix[prefix.length - 1] + isVowel); + } + + return queries.map(([l, r]) => prefix[r + 1] - prefix[l]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: + * $O(n)$ extra space. + * $O(m)$ space for the output list. + +> Where $n$ is the number of words, and $m$ is the number of queries. \ No newline at end of file diff --git a/articles/count-vowels-permutation.md b/articles/count-vowels-permutation.md new file mode 100644 index 000000000..c2f561f56 --- /dev/null +++ b/articles/count-vowels-permutation.md @@ -0,0 +1,864 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def countVowelPermutation(self, n: int) -> int: + MOD = 10**9 + 7 + follows = { + 'a': ['e'], + 'e': ['a', 'i'], + 'i': ['a', 'e', 'o', 'u'], + 'o': ['i', 'u'], + 'u': ['a'] + } + + def dfs(i, v): + if i == n: + return 1 + + total = 0 + for nxt in follows[v]: + total = (total + dfs(i + 1, nxt)) % MOD + return total + + res = 0 + for vowel in ['a', 'e', 'i', 'o', 'u']: + res = (res + dfs(1, vowel)) % MOD + + return res +``` + +```java +public class Solution { + private final int MOD = 1_000_000_007; + private final Map> follows = Map.of( + 'a', List.of('e'), + 'e', List.of('a', 'i'), + 'i', List.of('a', 'e', 'o', 'u'), + 'o', List.of('i', 'u'), + 'u', List.of('a') + ); + + public int countVowelPermutation(int n) { + int res = 0; + for (char vowel : "aeiou".toCharArray()) { + res = (res + dfs(1, vowel, n)) % MOD; + } + return res; + } + + private int dfs(int i, char v, int n) { + if (i == n) { + return 1; + } + + int total = 0; + for (char next : follows.get(v)) { + total = (total + dfs(i + 1, next, n)) % MOD; + } + return total; + } +} +``` + +```cpp +class Solution { + const int MOD = 1e9 + 7; + unordered_map> follows = { + {'a', {'e'}}, + {'e', {'a', 'i'}}, + {'i', {'a', 'e', 'o', 'u'}}, + {'o', {'i', 'u'}}, + {'u', {'a'}} + }; + +public: + int countVowelPermutation(int n) { + + int res = 0; + for (char vowel : string("aeiou")) { + res = (res + dfs(1, vowel, n)) % MOD; + } + return res; + } + +private: + int dfs(int i, char v, int n) { + if (i == n) { + return 1; + } + + int total = 0; + for (char& next : follows[v]) { + total = (total + dfs(i + 1, next, n)) % MOD; + } + return total; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countVowelPermutation(n) { + const MOD = 1e9 + 7; + + const follows = { + a: ['e'], + e: ['a', 'i'], + i: ['a', 'e', 'o', 'u'], + o: ['i', 'u'], + u: ['a'] + }; + + const dfs = (i, v) => { + if (i === n) { + return 1; + } + + let total = 0; + for (const next of follows[v]) { + total = (total + dfs(i + 1, next)) % MOD; + } + return total; + }; + + let res = 0; + for (const vowel of ['a', 'e', 'i', 'o', 'u']) { + res = (res + dfs(1, vowel)) % MOD; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(4 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def countVowelPermutation(self, n: int) -> int: + MOD = 10**9 + 7 + cache = {} + follows = { + 'a': ['e'], + 'e': ['a', 'i'], + 'i': ['a', 'e', 'o', 'u'], + 'o': ['i', 'u'], + 'u': ['a'] + } + + def dfs(i, v): + if i == n: + return 1 + if (i, v) in cache: + return cache[(i, v)] + + total = 0 + for nxt in follows[v]: + total = (total + dfs(i + 1, nxt)) % MOD + cache[(i, v)] = total + return total + + res = 0 + for vowel in ['a', 'e', 'i', 'o', 'u']: + res = (res + dfs(1, vowel)) % MOD + + return res +``` + +```java +public class Solution { + private final int MOD = 1_000_000_007; + private final Map> follows = Map.of( + 0, List.of(1), // 'a' -> 'e' + 1, List.of(0, 2), // 'e' -> 'a', 'i' + 2, List.of(0, 1, 3, 4), // 'i' -> 'a', 'e', 'o', 'u' + 3, List.of(2, 4), // 'o' -> 'i', 'u' + 4, List.of(0) // 'u' -> 'a' + ); + private int[][] dp; + + public int countVowelPermutation(int n) { + dp = new int[n][5]; + for (int i = 0; i < n; i++) { + Arrays.fill(dp[i], -1); + } + + int res = 0; + for (int vowel = 0; vowel < 5; vowel++) { + res = (res + dfs(1, vowel, n)) % MOD; + } + return res; + } + + private int dfs(int i, int v, int n) { + if (i == n) { + return 1; + } + if (dp[i][v] != -1) return dp[i][v]; + + int total = 0; + for (int next : follows.get(v)) { + total = (total + dfs(i + 1, next, n)) % MOD; + } + return dp[i][v] = total; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + vector> dp; + vector> follows = { + {1}, // 'a' -> 'e' + {0, 2}, // 'e' -> 'a', 'i' + {0, 1, 3, 4}, // 'i' -> 'a', 'e', 'o', 'u' + {2, 4}, // 'o' -> 'i', 'u' + {0} // 'u' -> 'a' + }; + + int dfs(int i, int v, int n) { + if (i == n) return 1; + if (dp[i][v] != -1) return dp[i][v]; + + int total = 0; + for (int next : follows[v]) { + total = (total + dfs(i + 1, next, n)) % MOD; + } + return dp[i][v] = total; + } + +public: + int countVowelPermutation(int n) { + dp.assign(n, vector(5, -1)); + + int res = 0; + for (int vowel = 0; vowel < 5; vowel++) { + res = (res + dfs(1, vowel, n)) % MOD; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countVowelPermutation(n) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: n }, () => Array(5).fill(-1)); + + const follows = { + 0: [1], // 'a' -> 'e' + 1: [0, 2], // 'e' -> 'a', 'i' + 2: [0, 1, 3, 4], // 'i' -> 'a', 'e', 'o', 'u' + 3: [2, 4], // 'o' -> 'i', 'u' + 4: [0], // 'u' -> 'a' + }; + + const dfs = (i, v) => { + if (i === n) return 1; + if (dp[i][v] !== -1) return dp[i][v]; + + let total = 0; + for (const next of follows[v]) { + total = (total + dfs(i + 1, next)) % MOD; + } + dp[i][v] = total; + return total; + }; + + let res = 0; + for (let vowel = 0; vowel < 5; vowel++) { + res = (res + dfs(1, vowel)) % MOD; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def countVowelPermutation(self, n: int) -> int: + MOD = 10**9 + 7 + a, e, i, o, u = 0, 1, 2, 3, 4 + dp = [[0] * 5 for _ in range(n + 1)] + dp[1] = [1, 1, 1, 1, 1] + + for j in range(2, n + 1): + dp[j][a] = (dp[j - 1][e] + dp[j - 1][i] + dp[j - 1][u]) % MOD + dp[j][e] = (dp[j - 1][a] + dp[j - 1][i]) % MOD + dp[j][i] = (dp[j - 1][e] + dp[j - 1][o]) % MOD + dp[j][o] = dp[j - 1][i] % MOD + dp[j][u] = (dp[j - 1][i] + dp[j - 1][o]) % MOD + + return sum(dp[n]) % MOD +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int countVowelPermutation(int n) { + int[][] dp = new int[n + 1][5]; + int[][] follows = { + {1}, // 'a' -> 'e' + {0, 2}, // 'e' -> 'a', 'i' + {0, 1, 3, 4}, // 'i' -> 'a', 'e', 'o', 'u' + {2, 4}, // 'o' -> 'i', 'u' + {0} // 'u' -> 'a' + }; + + for (int v = 0; v < 5; v++) { + dp[1][v] = 1; + } + + for (int i = 2; i <= n; i++) { + for (int v = 0; v < 5; v++) { + for (int nextV : follows[v]) { + dp[i][v] = (dp[i][v] + dp[i - 1][nextV]) % MOD; + } + } + } + + int result = 0; + for (int v = 0; v < 5; v++) { + result = (result + dp[n][v]) % MOD; + } + return result; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + +public: + int countVowelPermutation(int n) { + vector> dp(n + 1, vector(5, 0)); + vector> follows = { + {1}, // 'a' -> 'e' + {0, 2}, // 'e' -> 'a', 'i' + {0, 1, 3, 4}, // 'i' -> 'a', 'e', 'o', 'u' + {2, 4}, // 'o' -> 'i', 'u' + {0} // 'u' -> 'a' + }; + + for (int v = 0; v < 5; v++) { + dp[1][v] = 1; + } + + for (int i = 2; i <= n; i++) { + for (int v = 0; v < 5; v++) { + for (int nextV : follows[v]) { + dp[i][v] = (dp[i][v] + dp[i - 1][nextV]) % MOD; + } + } + } + + int result = 0; + for (int v = 0; v < 5; v++) { + result = (result + dp[n][v]) % MOD; + } + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countVowelPermutation(n) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: n + 1 }, () => Array(5).fill(0)); + const follows = [ + [1], // 'a' -> 'e' + [0, 2], // 'e' -> 'a', 'i' + [0, 1, 3, 4], // 'i' -> 'a', 'e', 'o', 'u' + [2, 4], // 'o' -> 'i', 'u' + [0] // 'u' -> 'a' + ]; + + for (let v = 0; v < 5; v++) { + dp[1][v] = 1; + } + + for (let i = 2; i <= n; i++) { + for (let v = 0; v < 5; v++) { + for (const nextV of follows[v]) { + dp[i][v] = (dp[i][v] + dp[i - 1][nextV]) % MOD; + } + } + } + + return dp[n].reduce((sum, val) => (sum + val) % MOD, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def countVowelPermutation(self, n: int) -> int: + MOD = 10**9 + 7 + follows = [ + [1], # 'a' -> 'e' + [0, 2], # 'e' -> 'a', 'i' + [0, 1, 3, 4], # 'i' -> 'a', 'e', 'o', 'u' + [2, 4], # 'o' -> 'i', 'u' + [0] # 'u' -> 'a' + ] + dp = [1] * 5 + + for _ in range(2, n + 1): + next_dp = [0] * 5 + for v in range(5): + for nextV in follows[v]: + next_dp[v] = (next_dp[v] + dp[nextV]) % MOD + dp = next_dp + + return sum(dp) % MOD +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int countVowelPermutation(int n) { + int[][] follows = { + {1}, // 'a' -> 'e' + {0, 2}, // 'e' -> 'a', 'i' + {0, 1, 3, 4}, // 'i' -> 'a', 'e', 'o', 'u' + {2, 4}, // 'o' -> 'i', 'u' + {0} // 'u' -> 'a' + }; + + int[] dp = {1, 1, 1, 1, 1}; + + for (int i = 2; i <= n; i++) { + int[] nextDp = new int[5]; + for (int v = 0; v < 5; v++) { + for (int nextV : follows[v]) { + nextDp[v] = (nextDp[v] + dp[nextV]) % MOD; + } + } + dp = nextDp; + } + + int result = 0; + for (int count : dp) { + result = (result + count) % MOD; + } + return result; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + +public: + int countVowelPermutation(int n) { + vector> follows = { + {1}, // 'a' -> 'e' + {0, 2}, // 'e' -> 'a', 'i' + {0, 1, 3, 4}, // 'i' -> 'a', 'e', 'o', 'u' + {2, 4}, // 'o' -> 'i', 'u' + {0} // 'u' -> 'a' + }; + + vector dp(5, 1); + + for (int i = 2; i <= n; i++) { + vector nextDp(5, 0); + for (int v = 0; v < 5; v++) { + for (int nextV : follows[v]) { + nextDp[v] = (nextDp[v] + dp[nextV]) % MOD; + } + } + dp = nextDp; + } + + int result = 0; + for (int count : dp) { + result = (result + count) % MOD; + } + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + countVowelPermutation(n) { + const MOD = 1e9 + 7; + const follows = [ + [1], // 'a' -> 'e' + [0, 2], // 'e' -> 'a', 'i' + [0, 1, 3, 4], // 'i' -> 'a', 'e', 'o', 'u' + [2, 4], // 'o' -> 'i', 'u' + [0] // 'u' -> 'a' + ]; + + let dp = [1, 1, 1, 1, 1]; + + for (let i = 2; i <= n; i++) { + const nextDp = [0, 0, 0, 0, 0]; + for (let v = 0; v < 5; v++) { + for (const nextV of follows[v]) { + nextDp[v] = (nextDp[v] + dp[nextV]) % MOD; + } + } + dp = nextDp; + } + + return dp.reduce((sum, count) => (sum + count) % MOD, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we used array of size $5$. + +--- + +## 5. Matrix Exponentiation + +::tabs-start + +```python +class Solution: + MOD = 10**9 + 7 + + class M: + def __init__(self, n): + self.a = [[0] * n for _ in range(n)] + + def __mul__(self, other): + n = len(self.a) + product = Solution.M(n) + for i in range(n): + for j in range(n): + for k in range(n): + product.a[i][k] = (product.a[i][k] + self.a[i][j] * other.a[j][k]) % Solution.MOD + return product + + def matrixExpo(self, base, exp): + n = len(base.a) + result = Solution.M(n) + for i in range(n): + result.a[i][i] = 1 + while exp > 0: + if exp % 2 == 1: + result = result * base + base = base * base + exp //= 2 + return result + + def countVowelPermutation(self, n: int) -> int: + follows = [ + [0, 1, 0, 0, 0], # 'a' -> 'e' + [1, 0, 1, 0, 0], # 'e' -> 'a', 'i' + [1, 1, 0, 1, 1], # 'i' -> 'a', 'e', 'o', 'u' + [0, 0, 1, 0, 1], # 'o' -> 'i', 'u' + [1, 0, 0, 0, 0] # 'u' -> 'a' + ] + + base = Solution.M(5) + base.a = follows + + result = self.matrixExpo(base, n - 1) + + return sum(sum(row) for row in result.a) % self.MOD +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + static class M { + int[][] a; + + M(int n) { + a = new int[n][n]; + } + + M multiply(M other) { + int n = a.length; + M product = new M(n); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + for (int k = 0; k < n; k++) { + product.a[i][k] = (int) ((product.a[i][k] + 1L * a[i][j] * other.a[j][k]) % MOD); + } + } + } + return product; + } + } + + private M matrixExpo(M base, int exp) { + int n = base.a.length; + M result = new M(n); + for (int i = 0; i < n; i++) { + result.a[i][i] = 1; + } + while (exp > 0) { + if (exp % 2 == 1) { + result = result.multiply(base); + } + base = base.multiply(base); + exp /= 2; + } + return result; + } + + public int countVowelPermutation(int n) { + int[][] follows = { + {0, 1, 0, 0, 0}, // 'a' -> 'e' + {1, 0, 1, 0, 0}, // 'e' -> 'a', 'i' + {1, 1, 0, 1, 1}, // 'i' -> 'a', 'e', 'o', 'u' + {0, 0, 1, 0, 1}, // 'o' -> 'i', 'u' + {1, 0, 0, 0, 0} // 'u' -> 'a' + }; + + M base = new M(5); + base.a = follows; + + M result = matrixExpo(base, n - 1); + + int ans = 0; + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 5; j++) { + ans = (ans + result.a[i][j]) % MOD; + } + } + return ans; + } +} +``` + +```cpp +class Solution { + static const int MOD = 1e9 + 7; + + struct M { + vector> a; + + M(int n) { + a.resize(n, vector(n, 0)); + } + + M operator*(const M& other) const { + int n = a.size(); + M product(n); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + for (int k = 0; k < n; k++) { + product.a[i][k] = (product.a[i][k] + a[i][j] * 1LL * other.a[j][k]) % MOD; + } + } + } + return product; + } + }; + + M matrixExpo(M base, int exp) { + int n = base.a.size(); + M result(n); + + for (int i = 0; i < n; i++) { + result.a[i][i] = 1; + } + + while (exp > 0) { + if (exp % 2 == 1) { + result = result * base; + } + base = base * base; + exp /= 2; + } + + return result; + } + +public: + int countVowelPermutation(int n) { + vector> follows = { + {0, 1, 0, 0, 0}, // 'a' -> 'e' + {1, 0, 1, 0, 0}, // 'e' -> 'a', 'i' + {1, 1, 0, 1, 1}, // 'i' -> 'a', 'e', 'o', 'u' + {0, 0, 1, 0, 1}, // 'o' -> 'i', 'u' + {1, 0, 0, 0, 0} // 'u' -> 'a' + }; + + M base(5); + base.a = follows; + + M result = matrixExpo(base, n - 1); + + int ans = 0; + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 5; j++) { + ans = (ans + result.a[i][j]) % MOD; + } + } + + return ans; + } +}; +``` + +```javascript +class M { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.MOD = 1e9 + 7; + this.a = Array.from({ length: n }, () => Array(n).fill(0)); + } + + /** + * @param {M} + * @return {M} + */ + multiply(other) { + const n = this.a.length; + const product = new M(n); + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + let sum = 0n; + for (let k = 0; k < n; k++) { + sum += BigInt(this.a[i][k]) * BigInt(other.a[k][j]); + } + product.a[i][j] = Number(sum % BigInt(this.MOD)); + } + } + return product; + } +} + +class Solution { + + /** + * @param {M} + * @param {number} exp + * @return {M} + */ + matrixExpo(base, exp) { + const n = base.a.length; + let result = new M(n); + for (let i = 0; i < n; i++) { + result.a[i][i] = 1; + } + while (exp > 0) { + if (exp & 1) { + result = result.multiply(base); + } + base = base.multiply(base); + exp >>= 1; + } + return result; + } + + /** + * @param {number} n + * @return {number} + */ + countVowelPermutation(n) { + const MOD = 1e9 + 7; + const follows = [ + [0, 1, 0, 0, 0], // 'a' -> 'e' + [1, 0, 1, 0, 0], // 'e' -> 'a', 'i' + [1, 1, 0, 1, 1], // 'i' -> 'a', 'e', 'o', 'u' + [0, 0, 1, 0, 1], // 'o' -> 'i', 'u' + [1, 0, 0, 0, 0] // 'u' -> 'a' + ]; + const base = new M(5); + base.a = follows; + const result = this.matrixExpo(base, n - 1); + let ans = 0; + for (let i = 0; i < 5; i++) { + for (let j = 0; j < 5; j++) { + ans = (ans + result.a[i][j]) % MOD; + } + } + return ans; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m ^ 3 \log n)$ +* Space complexity: $O(m ^ 2)$ + +> Where $m$ is the size of the matrix used in matrix exponentiation $(5 X 5)$ and $n$ is the length of the permutation. \ No newline at end of file diff --git a/articles/count-ways-to-build-good-strings.md b/articles/count-ways-to-build-good-strings.md new file mode 100644 index 000000000..a18b90526 --- /dev/null +++ b/articles/count-ways-to-build-good-strings.md @@ -0,0 +1,283 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def countGoodStrings(self, low: int, high: int, zero: int, one: int) -> int: + mod = 10**9 + 7 + + def dfs(length): + if length > high: + return 0 + res = 1 if length >= low else 0 + res += dfs(length + zero) + dfs(length + one) + return res % mod + + return dfs(0) +``` + +```java +public class Solution { + final int mod = 1_000_000_007; + + public int countGoodStrings(int low, int high, int zero, int one) { + return dfs(low, high, zero, one, 0); + } + + private int dfs(int low, int high, int zero, int one, int length) { + if (length > high) return 0; + int res = (length >= low) ? 1 : 0; + res = (res + dfs(low, high, zero, one, length + zero)) % mod; + res = (res + dfs(low, high, zero, one, length + one)) % mod; + return res; + } +} +``` + +```cpp +class Solution { +public: + int countGoodStrings(int low, int high, int zero, int one) { + const int mod = 1e9 + 7; + + function dfs = [&](int length) { + if (length > high) return 0; + int res = (length >= low) ? 1 : 0; + res = (res + dfs(length + zero)) % mod; + res = (res + dfs(length + one)) % mod; + return res; + }; + + return dfs(0); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} low + * @param {number} high + * @param {number} zero + * @param {number} one + * @return {number} + */ + countGoodStrings(low, high, zero, one) { + const mod = 1e9 + 7; + + const dfs = length => { + if (length > high) return 0; + let res = length >= low ? 1 : 0; + res = (res + dfs(length + zero)) % mod; + res = (res + dfs(length + one)) % mod; + return res; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +> Where $n$ is equal to the given $high$ value. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def countGoodStrings(self, low: int, high: int, zero: int, one: int) -> int: + mod = 10**9 + 7 + dp = {} + + def dfs(length): + if length > high: + return 0 + if length in dp: + return dp[length] + + dp[length] = 1 if length >= low else 0 + dp[length] += dfs(length + zero) + dfs(length + one) + return dp[length] % mod + + return dfs(0) +``` + +```java +public class Solution { + final int mod = 1_000_000_007; + private int[] dp; + + public int countGoodStrings(int low, int high, int zero, int one) { + dp = new int[high + 1]; + Arrays.fill(dp, -1); + return dfs(low, high, zero, one, 0); + } + + private int dfs(int low, int high, int zero, int one, int length) { + if (length > high) return 0; + if (dp[length] != -1) return dp[length]; + + dp[length] = (length >= low) ? 1 : 0; + dp[length] = (dp[length] + dfs(low, high, zero, one, length + zero)) % mod; + dp[length] = (dp[length] + dfs(low, high, zero, one, length + one)) % mod; + return dp[length]; + } +} +``` + +```cpp +class Solution { + const int mod = 1e9 + 7; + vector dp; + +public: + int countGoodStrings(int low, int high, int zero, int one) { + dp.assign(high + 1, -1); + return dfs(low, high, zero, one, 0); + } + +private: + int dfs(int low, int high, int zero, int one, int length) { + if (length > high) return 0; + if (dp[length] != -1) return dp[length]; + dp[length] = (length >= low) ? 1 : 0; + dp[length] = (dp[length] + dfs(low, high, zero, one, length + zero)) % mod; + dp[length] = (dp[length] + dfs(low, high, zero, one, length + one)) % mod; + return dp[length]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} low + * @param {number} high + * @param {number} zero + * @param {number} one + * @return {number} + */ + countGoodStrings(low, high, zero, one) { + const mod = 1e9 + 7; + const dp = new Array(high + 1).fill(-1); + + const dfs = length => { + if (length > high) return 0; + if (dp[length] !== -1) return dp[length]; + dp[length] = length >= low ? 1 : 0; + dp[length] = (dp[length] + dfs(length + zero)) % mod; + dp[length] = (dp[length] + dfs(length + one)) % mod; + return dp[length]; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +> Where $n$ is equal to the given $high$ value. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def countGoodStrings(self, low: int, high: int, zero: int, one: int) -> int: + dp = { 0 : 1 } + mod = 10**9 + 7 + + for i in range(1, high + 1): + dp[i] = (dp.get(i - one, 0) + dp.get(i - zero, 0)) % mod + + return sum(dp[i] for i in range(low, high + 1)) % mod +``` + +```java +public class Solution { + public int countGoodStrings(int low, int high, int zero, int one) { + int[] dp = new int[high + 1]; + int mod = 1_000_000_007, res = 0; + dp[0] = 1; + + for (int i = 1; i <= high; i++) { + if (i >= zero) dp[i] = (dp[i] + dp[i - zero]) % mod; + if (i >= one) dp[i] = (dp[i] + dp[i - one]) % mod; + if (i >= low) res = (res + dp[i]) % mod; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int countGoodStrings(int low, int high, int zero, int one) { + vector dp(high + 1); + int mod = 1e9 + 7, res = 0; + dp[0] = 1; + + for (int i = 1; i <= high; i++) { + if (i >= zero) dp[i] = (dp[i] + dp[i - zero]) % mod; + if (i >= one) dp[i] = (dp[i] + dp[i - one]) % mod; + if (i >= low) res = (res + dp[i]) % mod; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} low + * @param {number} high + * @param {number} zero + * @param {number} one + * @return {number} + */ + countGoodStrings(low, high, zero, one) { + const mod = 1e9 + 7; + const dp = new Int32Array(high + 1); + let res = 0; + dp[0] = 1; + + for (let i = 1; i <= high; i++) { + if (i >= zero) dp[i] = (dp[i] + dp[i - zero]) % mod; + if (i >= one) dp[i] = (dp[i] + dp[i - one]) % mod; + if (i >= low) res = (res + dp[i]) % mod; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +> Where $n$ is equal to the given $high$ value. \ No newline at end of file diff --git a/articles/counting-bits.md b/articles/counting-bits.md new file mode 100644 index 000000000..66b5db885 --- /dev/null +++ b/articles/counting-bits.md @@ -0,0 +1,655 @@ +## 1. Bit Manipulation - I + +::tabs-start + +```python +class Solution: + def countBits(self, n: int) -> List[int]: + res = [] + for num in range(n + 1): + one = 0 + for i in range(32): + if num & (1 << i): + one += 1 + res.append(one) + return res +``` + +```java +public class Solution { + public int[] countBits(int n) { + int[] res = new int[n + 1]; + for (int num = 1; num <= n; num++) { + for (int i = 0; i < 32; i++) { + if ((num & (1 << i)) != 0) { + res[num]++; + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector countBits(int n) { + vector res(n + 1); + for (int num = 1; num <= n; num++) { + for (int i = 0; i < 32; i++) { + if (num & (1 << i)) { + res[num]++; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number[]} + */ + countBits(n) { + let res = []; + for (let num = 0; num <= n; num++) { + let one = 0; + for (let i = 0; i < 32; i++) { + if ((num & (1 << i)) != 0) { + one++; + } + } + res.push(one); + } + return res; + } +} +``` + +```csharp +public class Solution { + public int[] CountBits(int n) { + int[] res = new int[n + 1]; + for (int num = 1; num <= n; num++) { + for (int i = 0; i < 32; i++) { + if ((num & (1 << i)) != 0) { + res[num]++; + } + } + } + return res; + } +} +``` + +```go +func countBits(n int) []int { + res := make([]int, n+1) + for num := 0; num <= n; num++ { + one := 0 + for i := 0; i < 32; i++ { + if num&(1< [Int] { + var res = [Int]() + for num in 0...n { + var one = 0 + for i in 0..<32 { + if num & (1 << i) != 0 { + one += 1 + } + } + res.append(one) + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. + +--- + +## 2. Bit Manipulation - II + +::tabs-start + +```python +class Solution: + def countBits(self, n: int) -> List[int]: + res = [0] * (n + 1) + for i in range(1, n + 1): + num = i + while num != 0: + res[i] += 1 + num &= (num - 1) + return res +``` + +```java +public class Solution { + public int[] countBits(int n) { + int[] res = new int[n + 1]; + for (int i = 1; i <= n; i++) { + int num = i; + while (num != 0) { + res[i]++; + num &= (num - 1); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector countBits(int n) { + vector res(n + 1, 0); + for (int i = 1; i <= n; i++) { + int num = i; + while (num != 0) { + res[i]++; + num &= (num - 1); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number[]} + */ + countBits(n) { + let res = new Array(n + 1).fill(0); + for (let i = 1; i <= n; i++) { + let num = i; + while (num !== 0) { + res[i]++; + num &= (num - 1); + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int[] CountBits(int n) { + int[] res = new int[n + 1]; + for (int i = 1; i <= n; i++) { + int num = i; + while (num != 0) { + res[i]++; + num &= (num - 1); + } + } + return res; + } +} +``` + +```go +func countBits(n int) []int { + res := make([]int, n+1) + for i := 1; i <= n; i++ { + num := i + for num != 0 { + res[i]++ + num &= (num - 1) + } + } + return res +} +``` + +```kotlin +class Solution { + fun countBits(n: Int): IntArray { + val res = IntArray(n + 1) + for (i in 1..n) { + var num = i + while (num != 0) { + res[i]++ + num = num and (num - 1) + } + } + return res + } +} +``` + +```swift +class Solution { + func countBits(_ n: Int) -> [Int] { + var res = [Int](repeating: 0, count: n + 1) + for i in 1..<(n + 1) { + var num = i + while num != 0 { + res[i] += 1 + num &= (num - 1) + } + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. + +--- + +## 3. In-Built Function + +::tabs-start + +```python +class Solution: + def countBits(self, n: int) -> List[int]: + return [bin(i).count('1') for i in range(n + 1)] +``` + +```java +public class Solution { + public int[] countBits(int n) { + int[] res = new int[n + 1]; + for (int i = 0; i <= n; i++) { + res[i] = Integer.bitCount(i); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector countBits(int n) { + vector res(n + 1, 0); + for (int i = 0; i <= n; i++) { + res[i] = __builtin_popcount(i); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number[]} + */ + countBits(n) { + let res = []; + for (let i = 0; i <= n; i++) { + res.push(i.toString(2).split('1').length - 1); + } + return res; + } +} +``` + +```csharp +public class Solution { + public int[] CountBits(int n) { + int[] res = new int[n + 1]; + for (int i = 0; i <= n; i++) { + res[i] = Convert.ToString(i, 2).Count(c => c == '1'); + } + return res; + } +} +``` + +```go +func countBits(n int) []int { + res := make([]int, n+1) + for i := 0; i <= n; i++ { + res[i] = bits.OnesCount(uint(i)) + } + return res +} +``` + +```kotlin +class Solution { + fun countBits(n: Int): IntArray { + return IntArray(n + 1) { it.countOneBits() } + } +} +``` + +```swift +class Solution { + func countBits(_ n: Int) -> [Int] { + var res = [Int](repeating: 0, count: n + 1) + for num in 1..<(n + 1) { + res[num] = num.nonzeroBitCount + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. + +--- + +## 4. Bit Manipulation (DP) + +::tabs-start + +```python +class Solution: + def countBits(self, n: int) -> List[int]: + dp = [0] * (n + 1) + offset = 1 + + for i in range(1, n + 1): + if offset * 2 == i: + offset = i + dp[i] = 1 + dp[i - offset] + return dp +``` + +```java +public class Solution { + public int[] countBits(int n) { + int[] dp = new int[n + 1]; + int offset = 1; + + for (int i = 1; i <= n; i++) { + if (offset * 2 == i) { + offset = i; + } + dp[i] = 1 + dp[i - offset]; + } + return dp; + } +} +``` + +```cpp +class Solution { +public: + vector countBits(int n) { + vector dp(n + 1); + int offset = 1; + + for (int i = 1; i <= n; i++) { + if (offset * 2 == i) { + offset = i; + } + dp[i] = 1 + dp[i - offset]; + } + return dp; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number[]} + */ + countBits(n) { + const dp = new Array(n + 1).fill(0); + let offset = 1; + + for (let i = 1; i <= n; i++) { + if (offset * 2 == i) { + offset = i; + } + dp[i] = 1 + dp[i - offset]; + } + return dp; + } +} +``` + +```csharp +public class Solution { + public int[] CountBits(int n) { + int[] dp = new int[n + 1]; + int offset = 1; + + for (int i = 1; i <= n; i++) { + if (offset * 2 == i) { + offset = i; + } + dp[i] = 1 + dp[i - offset]; + } + return dp; + } +} +``` + +```go +func countBits(n int) []int { + dp := make([]int, n+1) + offset := 1 + + for i := 1; i <= n; i++ { + if offset*2 == i { + offset = i + } + dp[i] = 1 + dp[i - offset] + } + return dp +} +``` + +```kotlin +class Solution { + fun countBits(n: Int): IntArray { + val dp = IntArray(n + 1) + var offset = 1 + + for (i in 1..n) { + if (offset * 2 == i) { + offset = i + } + dp[i] = 1 + dp[i - offset] + } + return dp + } +} +``` + +```swift +class Solution { + func countBits(_ n: Int) -> [Int] { + var dp = [Int](repeating: 0, count: n + 1) + var offset = 1 + + for i in 1..<(n + 1) { + if offset * 2 == i { + offset = i + } + dp[i] = 1 + dp[i - offset] + } + return dp + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. + +--- + +## 5. Bit Manipulation (Optimal) + +::tabs-start + +```python +class Solution: + def countBits(self, n: int) -> List[int]: + dp = [0] * (n + 1) + for i in range(n + 1): + dp[i] = dp[i >> 1] + (i & 1) + return dp +``` + +```java +public class Solution { + public int[] countBits(int n) { + int[] dp = new int[n + 1]; + for (int i = 1; i <= n; i++) { + dp[i] = dp[i >> 1] + (i & 1); + } + return dp; + } +} +``` + +```cpp +class Solution { +public: + vector countBits(int n) { + vector dp(n + 1); + for (int i = 1; i <= n; i++) { + dp[i] = dp[i >> 1] + (i & 1); + } + return dp; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number[]} + */ + countBits(n) { + let dp = new Array(n + 1).fill(0); + for (let i = 1; i <= n; i++) { + dp[i] = dp[i >> 1] + (i & 1); + } + return dp; + } +} +``` + +```csharp +public class Solution { + public int[] CountBits(int n) { + int[] dp = new int[n + 1]; + for (int i = 1; i <= n; i++) { + dp[i] = dp[i >> 1] + (i & 1); + } + return dp; + } +} +``` + +```go +func countBits(n int) []int { + dp := make([]int, n+1) + for i := 1; i <= n; i++ { + dp[i] = dp[i >> 1] + (i&1); + } + return dp +} +``` + +```kotlin +class Solution { + fun countBits(n: Int): IntArray { + val dp = IntArray(n + 1) + for (i in 1..n) { + dp[i] = dp[i shr 1] + (i and 1) + } + return dp + } +} +``` + +```swift +class Solution { + func countBits(_ n: Int) -> [Int] { + var dp = [Int](repeating: 0, count: n + 1) + for i in 0..<(n + 1) { + dp[i] = dp[i >> 1] + (i & 1) + } + return dp + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. \ No newline at end of file diff --git a/articles/course-schedule-ii.md b/articles/course-schedule-ii.md new file mode 100644 index 000000000..6a3e85924 --- /dev/null +++ b/articles/course-schedule-ii.md @@ -0,0 +1,1029 @@ +## 1. Cycle Detection (DFS) + +::tabs-start + +```python +class Solution: + def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -> List[int]: + prereq = {c: [] for c in range(numCourses)} + for crs, pre in prerequisites: + prereq[crs].append(pre) + + output = [] + visit, cycle = set(), set() + + def dfs(crs): + if crs in cycle: + return False + if crs in visit: + return True + + cycle.add(crs) + for pre in prereq[crs]: + if dfs(pre) == False: + return False + cycle.remove(crs) + visit.add(crs) + output.append(crs) + return True + + for c in range(numCourses): + if dfs(c) == False: + return [] + return output +``` + +```java +public class Solution { + public int[] findOrder(int numCourses, int[][] prerequisites) { + Map> prereq = new HashMap<>(); + for (int[] pair : prerequisites) { + prereq.computeIfAbsent(pair[0], + k -> new ArrayList<>()).add(pair[1]); + } + + List output = new ArrayList<>(); + Set visit = new HashSet<>(); + Set cycle = new HashSet<>(); + + for (int course = 0; course < numCourses; course++) { + if (!dfs(course, prereq, visit, cycle, output)) { + return new int[0]; + } + } + + int[] result = new int[numCourses]; + for (int i = 0; i < numCourses; i++) { + result[i] = output.get(i); + } + return result; + } + + private boolean dfs(int course, Map> prereq, + Set visit, Set cycle, + List output) { + + if (cycle.contains(course)) { + return false; + } + if (visit.contains(course)) { + return true; + } + + cycle.add(course); + for (int pre : prereq.getOrDefault(course, Collections.emptyList())) { + if (!dfs(pre, prereq, visit, cycle, output)) { + return false; + } + } + cycle.remove(course); + visit.add(course); + output.add(course); + return true; + } +} +``` + +```cpp +class Solution { +public: + vector findOrder(int numCourses, vector>& prerequisites) { + unordered_map> prereq; + for (const auto& pair : prerequisites) { + prereq[pair[0]].push_back(pair[1]); + } + + vector output; + unordered_set visit; + unordered_set cycle; + + for (int course = 0; course < numCourses; course++) { + if (!dfs(course, prereq, visit, cycle, output)) { + return {}; + } + } + + return output; + } + +private: + bool dfs(int course, const unordered_map>& prereq, + unordered_set& visit, unordered_set& cycle, + vector& output) { + + if (cycle.count(course)) { + return false; + } + if (visit.count(course)) { + return true; + } + + cycle.insert(course); + if (prereq.count(course)) { + for (int pre : prereq.at(course)) { + if (!dfs(pre, prereq, visit, cycle, output)) { + return false; + } + } + } + cycle.erase(course); + visit.insert(course); + output.push_back(course); + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} numCourses + * @param {number[][]} prerequisites + * @return {number[]} + */ + findOrder(numCourses, prerequisites) { + const prereq = new Map(); + for (const [course, pre] of prerequisites) { + if (!prereq.has(course)) { + prereq.set(course, []); + } + prereq.get(course).push(pre); + } + + const output = []; + const visit = new Set(); + const cycle = new Set(); + + for (let c = 0; c < numCourses; c++) { + if (!this.dfs(c, prereq, visit, cycle, output)) { + return []; + } + } + + return output; + } + + /** + * @param {number} course + * @param {Map} prereq + * @param {Set} visit + * @param {Set} cycle + * @param {number[]} output + * @return {boolean} + */ + dfs(course, prereq, visit, cycle, output) { + if (cycle.has(course)) { + return false; + } + if (visit.has(course)) { + return true; + } + + cycle.add(course); + for (const pre of prereq.get(course) || []) { + if (!this.dfs(pre, prereq, visit, cycle, output)) { + return false; + } + } + cycle.delete(course); + visit.add(course); + output.push(course); + return true; + } +} +``` + +```csharp +public class Solution { + public int[] FindOrder(int numCourses, int[][] prerequisites) { + Dictionary> prereq = new Dictionary>(); + foreach (var pair in prerequisites) { + if (!prereq.ContainsKey(pair[0])) { + prereq[pair[0]] = new List(); + } + prereq[pair[0]].Add(pair[1]); + } + + List output = new List(); + HashSet visit = new HashSet(); + HashSet cycle = new HashSet(); + + for (int course = 0; course < numCourses; course++) { + if (!Dfs(course, prereq, visit, cycle, output)) { + return new int[0]; + } + } + + return output.ToArray(); + } + + private bool Dfs(int course, Dictionary> prereq, + HashSet visit, HashSet cycle, + List output) { + + if (cycle.Contains(course)) { + return false; + } + if (visit.Contains(course)) { + return true; + } + + cycle.Add(course); + if (prereq.ContainsKey(course)) { + foreach (int pre in prereq[course]) { + if (!Dfs(pre, prereq, visit, cycle, output)) { + return false; + } + } + } + cycle.Remove(course); + visit.Add(course); + output.Add(course); + return true; + } +} +``` + +```go +func findOrder(numCourses int, prerequisites [][]int) []int { + prereq := make(map[int][]int) + for i := 0; i < numCourses; i++ { + prereq[i] = []int{} + } + for _, pair := range prerequisites { + crs, pre := pair[0], pair[1] + prereq[crs] = append(prereq[crs], pre) + } + + output := []int{} + visit := make(map[int]bool) + cycle := make(map[int]bool) + + var dfs func(int) bool + dfs = func(crs int) bool { + if cycle[crs] { + return false + } + if visit[crs] { + return true + } + + cycle[crs] = true + for _, pre := range prereq[crs] { + if !dfs(pre) { + return false + } + } + cycle[crs] = false + visit[crs] = true + output = append(output, crs) + return true + } + + for i := 0; i < numCourses; i++ { + if !dfs(i) { + return []int{} + } + } + + return output +} +``` + +```kotlin +class Solution { + fun findOrder(numCourses: Int, prerequisites: Array): IntArray { + val prereq = HashMap>() + for (i in 0 until numCourses) { + prereq[i] = mutableListOf() + } + for (pair in prerequisites) { + val (crs, pre) = pair + prereq[crs]?.add(pre) + } + + val output = mutableListOf() + val visit = HashSet() + val cycle = HashSet() + + fun dfs(crs: Int): Boolean { + if (crs in cycle) return false + if (crs in visit) return true + + cycle.add(crs) + for (pre in prereq[crs]!!) { + if (!dfs(pre)) return false + } + cycle.remove(crs) + visit.add(crs) + output.add(crs) + return true + } + + for (i in 0 until numCourses) { + if (!dfs(i)) return intArrayOf() + } + + return output.toIntArray() + } +} +``` + +```swift +class Solution { + func findOrder(_ numCourses: Int, _ prerequisites: [[Int]]) -> [Int] { + var prereq = [Int: [Int]]() + for c in 0..() + var cycle = Set() + + func dfs(_ crs: Int) -> Bool { + if cycle.contains(crs) { + return false + } + if visit.contains(crs) { + return true + } + + cycle.insert(crs) + for pre in prereq[crs]! { + if !dfs(pre) { + return false + } + } + cycle.remove(crs) + visit.insert(crs) + output.append(crs) + return true + } + + for c in 0.. Where $V$ is the number of courses and $E$ is the number of prerequisites. + +--- + +## 2. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -> List[int]: + indegree = [0] * numCourses + adj = [[] for i in range(numCourses)] + for src, dst in prerequisites: + indegree[dst] += 1 + adj[src].append(dst) + + q = deque() + for n in range(numCourses): + if indegree[n] == 0: + q.append(n) + + finish, output = 0, [] + while q: + node = q.popleft() + output.append(node) + finish += 1 + for nei in adj[node]: + indegree[nei] -= 1 + if indegree[nei] == 0: + q.append(nei) + + if finish != numCourses: + return [] + return output[::-1] +``` + +```java +public class Solution { + public int[] findOrder(int numCourses, int[][] prerequisites) { + int[] indegree = new int[numCourses]; + List> adj = new ArrayList<>(); + for (int i = 0; i < numCourses; i++) { + adj.add(new ArrayList<>()); + } + for (int[] pre : prerequisites) { + indegree[pre[1]]++; + adj.get(pre[0]).add(pre[1]); + } + + Queue q = new LinkedList<>(); + for (int i = 0; i < numCourses; i++) { + if (indegree[i] == 0) { + q.add(i); + } + } + + int finish = 0; + int[] output = new int[numCourses]; + while (!q.isEmpty()) { + int node = q.poll(); + output[numCourses - finish - 1] = node; + finish++; + for (int nei : adj.get(node)) { + indegree[nei]--; + if (indegree[nei] == 0) { + q.add(nei); + } + } + } + + if (finish != numCourses) { + return new int[0]; + } + return output; + } +} +``` + +```cpp +class Solution { +public: + vector findOrder(int numCourses, vector>& prerequisites) { + vector indegree(numCourses, 0); + vector> adj(numCourses); + + for (auto& pre : prerequisites) { + indegree[pre[1]]++; + adj[pre[0]].push_back(pre[1]); + } + + queue q; + for (int i = 0; i < numCourses; ++i) { + if (indegree[i] == 0) { + q.push(i); + } + } + + int finish = 0; + vector output(numCourses); + while (!q.empty()) { + int node = q.front();q.pop(); + output[numCourses - finish - 1] = node; + finish++; + for (int nei : adj[node]) { + indegree[nei]--; + if (indegree[nei] == 0) { + q.push(nei); + } + } + } + + if (finish != numCourses) { + return {}; + } + return output; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} numCourses + * @param {number[][]} prerequisites + * @return {number[]} + */ + findOrder(numCourses, prerequisites) { + let indegree = Array(numCourses).fill(0); + let adj = Array.from({ length: numCourses }, () => []); + for (let [src, dst] of prerequisites) { + indegree[dst]++; + adj[src].push(dst); + } + + let q = new Queue(); + for (let i = 0; i < numCourses; i++) { + if (indegree[i] === 0) { + q.push(i); + } + } + + let finish = 0; + let output = Array(numCourses); + while (!q.isEmpty()) { + let node = q.pop(); + output[numCourses - finish - 1] = node; + finish++; + for (let nei of adj[node]) { + indegree[nei]--; + if (indegree[nei] === 0) { + q.push(nei); + } + } + } + + if (finish !== numCourses) { + return []; + } + return output; + } +} +``` + +```csharp +public class Solution { + public int[] FindOrder(int numCourses, int[][] prerequisites) { + int[] indegree = new int[numCourses]; + List> adj = new List>(); + for (int i = 0; i < numCourses; i++) { + adj.Add(new List()); + } + foreach (var pre in prerequisites) { + indegree[pre[1]]++; + adj[pre[0]].Add(pre[1]); + } + + Queue q = new Queue(); + for (int i = 0; i < numCourses; i++) { + if (indegree[i] == 0) { + q.Enqueue(i); + } + } + + int finish = 0; + int[] output = new int[numCourses]; + while (q.Count > 0) { + int node = q.Dequeue(); + output[numCourses - finish - 1] = node; + finish++; + foreach (var nei in adj[node]) { + indegree[nei]--; + if (indegree[nei] == 0) { + q.Enqueue(nei); + } + } + } + + if (finish != numCourses) { + return new int[0]; + } + return output; + } +} +``` + +```go +func findOrder(numCourses int, prerequisites [][]int) []int { + indegree := make([]int, numCourses) + adj := make([][]int, numCourses) + for _, pair := range prerequisites { + src, dst := pair[0], pair[1] + indegree[dst]++ + adj[src] = append(adj[src], dst) + } + + q := []int{} + for i := 0; i < numCourses; i++ { + if indegree[i] == 0 { + q = append(q, i) + } + } + + output := []int{} + finish := 0 + for len(q) > 0 { + node := q[0] + q = q[1:] + output = append(output, node) + finish++ + for _, nei := range adj[node] { + indegree[nei]-- + if indegree[nei] == 0 { + q = append(q, nei) + } + } + } + + if finish != numCourses { + return []int{} + } + + for i, j := 0, len(output)-1; i < j; i, j = i+1, j-1 { + output[i], output[j] = output[j], output[i] + } + return output +} +``` + +```kotlin +class Solution { + fun findOrder(numCourses: Int, prerequisites: Array): IntArray { + val indegree = IntArray(numCourses) + val adj = Array(numCourses) { mutableListOf() } + + for (pair in prerequisites) { + val (src, dst) = pair + indegree[dst]++ + adj[src].add(dst) + } + + val q = ArrayDeque() + for (i in 0 until numCourses) { + if (indegree[i] == 0) q.add(i) + } + + val output = mutableListOf() + var finish = 0 + while (q.isNotEmpty()) { + val node = q.removeFirst() + output.add(node) + finish++ + for (nei in adj[node]) { + indegree[nei]-- + if (indegree[nei] == 0) { + q.add(nei) + } + } + } + + if (finish != numCourses) return intArrayOf() + + output.reverse() + return output.toIntArray() + } +} +``` + +```swift +class Solution { + func findOrder(_ numCourses: Int, _ prerequisites: [[Int]]) -> [Int] { + var indegree = Array(repeating: 0, count: numCourses) + var adj = Array(repeating: [Int](), count: numCourses) + + for pair in prerequisites { + let src = pair[0] + let dst = pair[1] + indegree[dst] += 1 + adj[src].append(dst) + } + + var queue = Deque() + for n in 0.. Where $V$ is the number of courses and $E$ is the number of prerequisites. + +--- + +## 3. Topological Sort (DFS) + +::tabs-start + +```python +class Solution: + def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -> List[int]: + adj = [[] for i in range(numCourses)] + indegree = [0] * numCourses + for nxt, pre in prerequisites: + indegree[nxt] += 1 + adj[pre].append(nxt) + + output = [] + + def dfs(node): + output.append(node) + indegree[node] -= 1 + for nei in adj[node]: + indegree[nei] -= 1 + if indegree[nei] == 0: + dfs(nei) + + for i in range(numCourses): + if indegree[i] == 0: + dfs(i) + + return output if len(output) == numCourses else [] +``` + +```java +public class Solution { + private List output = new ArrayList<>(); + private int[] indegree; + private List> adj; + + private void dfs(int node) { + output.add(node); + indegree[node]--; + for (int nei : adj.get(node)) { + indegree[nei]--; + if (indegree[nei] == 0) { + dfs(nei); + } + } + } + + public int[] findOrder(int numCourses, int[][] prerequisites) { + adj = new ArrayList<>(); + for (int i = 0; i < numCourses; i++) { + adj.add(new ArrayList<>()); + } + indegree = new int[numCourses]; + for (int[] pre : prerequisites) { + indegree[pre[0]]++; + adj.get(pre[1]).add(pre[0]); + } + + for (int i = 0; i < numCourses; i++) { + if (indegree[i] == 0) { + dfs(i); + } + } + + if (output.size() != numCourses) return new int[0]; + int[] res = new int[output.size()]; + for (int i = 0; i < output.size(); i++) { + res[i] = output.get(i); + } + return res; + } +} +``` + +```cpp +class Solution { + vector output; + vector indegree; + vector> adj; + + void dfs(int node) { + output.push_back(node); + indegree[node]--; + for (int nei : adj[node]) { + indegree[nei]--; + if (indegree[nei] == 0) { + dfs(nei); + } + } + } + +public: + vector findOrder(int numCourses, vector>& prerequisites) { + adj = vector>(numCourses); + indegree = vector(numCourses, 0); + for (auto& pre : prerequisites) { + indegree[pre[0]]++; + adj[pre[1]].push_back(pre[0]); + } + + for (int i = 0; i < numCourses; i++) { + if (indegree[i] == 0) { + dfs(i); + } + } + + if (output.size() != numCourses) return {}; + return output; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} numCourses + * @param {number[][]} prerequisites + * @return {number[]} + */ + findOrder(numCourses, prerequisites) { + let adj = Array.from({ length: numCourses }, () => []); + let indegree = Array(numCourses).fill(0); + + for (let [nxt, pre] of prerequisites) { + indegree[nxt]++; + adj[pre].push(nxt); + } + + let output = []; + + const dfs = (node) => { + output.push(node); + indegree[node]--; + for (let nei of adj[node]) { + indegree[nei]--; + if (indegree[nei] === 0) { + dfs(nei); + } + } + }; + + for (let i = 0; i < numCourses; i++) { + if (indegree[i] === 0) { + dfs(i); + } + } + + return output.length === numCourses ? output : []; + } +} +``` + +```csharp +public class Solution { + private List output = new List(); + private int[] indegree; + private List> adj; + + private void Dfs(int node) { + output.Add(node); + indegree[node]--; + foreach (var nei in adj[node]) { + indegree[nei]--; + if (indegree[nei] == 0) { + Dfs(nei); + } + } + } + + public int[] FindOrder(int numCourses, int[][] prerequisites) { + adj = new List>(); + for (int i = 0; i < numCourses; i++) { + adj.Add(new List()); + } + indegree = new int[numCourses]; + foreach (var pre in prerequisites) { + indegree[pre[0]]++; + adj[pre[1]].Add(pre[0]); + } + + for (int i = 0; i < numCourses; i++) { + if (indegree[i] == 0) { + Dfs(i); + } + } + + if (output.Count != numCourses) return new int[0]; + return output.ToArray(); + } +} +``` + +```go +func findOrder(numCourses int, prerequisites [][]int) []int { + adj := make([][]int, numCourses) + indegree := make([]int, numCourses) + for _, pair := range prerequisites { + nxt, pre := pair[0], pair[1] + indegree[nxt]++ + adj[pre] = append(adj[pre], nxt) + } + + output := []int{} + + var dfs func(int) + dfs = func(node int) { + output = append(output, node) + indegree[node]-- + for _, nei := range adj[node] { + indegree[nei]-- + if indegree[nei] == 0 { + dfs(nei) + } + } + } + + for i := 0; i < numCourses; i++ { + if indegree[i] == 0 { + dfs(i) + } + } + + if len(output) == numCourses { + return output + } + return []int{} +} +``` + +```kotlin +class Solution { + fun findOrder(numCourses: Int, prerequisites: Array): IntArray { + val adj = Array(numCourses) { mutableListOf() } + val indegree = IntArray(numCourses) + for ((nxt, pre) in prerequisites) { + indegree[nxt]++ + adj[pre].add(nxt) + } + + val output = mutableListOf() + + fun dfs(node: Int) { + output.add(node) + indegree[node]-- + for (nei in adj[node]) { + indegree[nei]-- + if (indegree[nei] == 0) { + dfs(nei) + } + } + } + + for (i in 0 until numCourses) { + if (indegree[i] == 0) { + dfs(i) + } + } + + return if (output.size == numCourses) output.toIntArray() else intArrayOf() + } +} +``` + +```swift +class Solution { + func findOrder(_ numCourses: Int, _ prerequisites: [[Int]]) -> [Int] { + var adj = Array(repeating: [Int](), count: numCourses) + var indegree = Array(repeating: 0, count: numCourses) + + for pair in prerequisites { + let nxt = pair[0] + let pre = pair[1] + indegree[nxt] += 1 + adj[pre].append(nxt) + } + + var output = [Int]() + + func dfs(_ node: Int) { + output.append(node) + indegree[node] -= 1 + for nei in adj[node] { + indegree[nei] -= 1 + if indegree[nei] == 0 { + dfs(nei) + } + } + } + + for i in 0.. Where $V$ is the number of courses and $E$ is the number of prerequisites. \ No newline at end of file diff --git a/articles/course-schedule-iv.md b/articles/course-schedule-iv.md new file mode 100644 index 000000000..0738453e4 --- /dev/null +++ b/articles/course-schedule-iv.md @@ -0,0 +1,900 @@ +## 1. Brute Force (DFS) + +::tabs-start + +```python +class Solution: + def checkIfPrerequisite(self, numCourses: int, prerequisites: List[List[int]], queries: List[List[int]]) -> List[bool]: + adj = [[] for _ in range(numCourses)] + for u, v in prerequisites: + adj[u].append(v) + + def dfs(node, target): + if node == target: + return True + for nei in adj[node]: + if dfs(nei, target): + return True + return False + + res = [] + for u, v in queries: + res.append(dfs(u, v)) + return res +``` + +```java +public class Solution { + private List[] adj; + + public List checkIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) { + adj = new ArrayList[numCourses]; + for (int i = 0; i < numCourses; i++) adj[i] = new ArrayList<>(); + for (int[] pre : prerequisites) adj[pre[0]].add(pre[1]); + + List res = new ArrayList<>(); + for (int[] query : queries) { + res.add(dfs(query[0], query[1])); + } + return res; + } + + private boolean dfs(int node, int target) { + if (node == target) return true; + for (int nei : adj[node]) { + if (dfs(nei, target)) return true; + } + return false; + } +} +``` + +```cpp +class Solution { + vector> adj; + +public: + vector checkIfPrerequisite(int numCourses, vector>& prerequisites, vector>& queries) { + adj.assign(numCourses, vector()); + for (auto& pre : prerequisites) { + adj[pre[0]].push_back(pre[1]); + } + + vector res; + for (auto& query : queries) { + res.push_back(dfs(query[0], query[1])); + } + return res; + } + +private: + bool dfs(int node, int target) { + if (node == target) return true; + for (int nei : adj[node]) { + if (dfs(nei, target)) return true; + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} numCourses + * @param {number[][]} prerequisites + * @param {number[][]} queries + * @return {boolean[]} + */ + checkIfPrerequisite(numCourses, prerequisites, queries) { + const adj = Array.from({ length: numCourses }, () => []); + for (const [u, v] of prerequisites) { + adj[u].push(v); + } + + const dfs = (node, target) => { + if (node === target) return true; + for (const nei of adj[node]) { + if (dfs(nei, target)) return true; + } + return false; + }; + + const res = []; + for (const [u, v] of queries) { + res.push(dfs(u, v)); + } + return res; + } +} +``` + +```csharp +public class Solution { + public List CheckIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) { + List[] adj = new List[numCourses]; + for (int i = 0; i < numCourses; i++) { + adj[i] = new List(); + } + + foreach (var pre in prerequisites) { + adj[pre[0]].Add(pre[1]); + } + + bool Dfs(int node, int target) { + if (node == target) return true; + foreach (var nei in adj[node]) { + if (Dfs(nei, target)) return true; + } + return false; + } + + var res = new List(); + foreach (var q in queries) { + res.Add(Dfs(q[0], q[1])); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((V + E) * m)$ +* Space complexity: $O(V + E + m)$ + +> Where $m$ is the number of queries, $V$ is the number of courses, and $E$ is the number of prerequisites. + +--- + +## 2. Depth First Search (Hash Set) + +::tabs-start + +```python +class Solution: + def checkIfPrerequisite(self, numCourses: int, prerequisites: List[List[int]], queries: List[List[int]]) -> List[bool]: + adj = defaultdict(list) + for prereq, crs in prerequisites: + adj[crs].append(prereq) + + def dfs(crs): + if crs not in prereqMap: + prereqMap[crs] = set() + for prereq in adj[crs]: + prereqMap[crs] |= dfs(prereq) + prereqMap[crs].add(crs) + return prereqMap[crs] + + prereqMap = {} + for crs in range(numCourses): + dfs(crs) + + res = [] + for u, v in queries: + res.append(u in prereqMap[v]) + return res +``` + +```java +public class Solution { + private List[] adj; + private Map> prereqMap; + + public List checkIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) { + adj = new ArrayList[numCourses]; + prereqMap = new HashMap<>(); + for (int i = 0; i < numCourses; i++) adj[i] = new ArrayList<>(); + for (int[] pre : prerequisites) adj[pre[1]].add(pre[0]); + + for (int crs = 0; crs < numCourses; crs++) dfs(crs); + + List res = new ArrayList<>(); + for (int[] query : queries) { + res.add(prereqMap.get(query[1]).contains(query[0])); + } + return res; + } + + private Set dfs(int crs) { + if (prereqMap.containsKey(crs)) return prereqMap.get(crs); + Set prereqs = new HashSet<>(); + for (int pre : adj[crs]) { + prereqs.addAll(dfs(pre)); + } + prereqs.add(crs); + prereqMap.put(crs, prereqs); + return prereqs; + } +} +``` + +```cpp + +class Solution { + vector> adj; + unordered_map> prereqMap; + +public: + vector checkIfPrerequisite(int numCourses, vector>& prerequisites, vector>& queries) { + adj.assign(numCourses, vector()); + for (auto& pre : prerequisites) { + adj[pre[1]].push_back(pre[0]); + } + for (int crs = 0; crs < numCourses; crs++) { + dfs(crs); + } + + vector res; + for (auto& query : queries) { + res.push_back(prereqMap[query[1]].count(query[0])); + } + return res; + } + +private: + unordered_set& dfs(int crs) { + if (prereqMap.count(crs)) { + return prereqMap[crs]; + } + prereqMap[crs] = unordered_set(); + for (int pre : adj[crs]) { + auto& cur = dfs(pre); + prereqMap[crs].insert(cur.begin(), cur.end()); + } + prereqMap[crs].insert(crs); + return prereqMap[crs]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} numCourses + * @param {number[][]} prerequisites + * @param {number[][]} queries + * @return {boolean[]} + */ + checkIfPrerequisite(numCourses, prerequisites, queries) { + const adj = Array.from({ length: numCourses }, () => []); + const prereqMap = new Map(); + + for (const [pre, crs] of prerequisites) { + adj[crs].push(pre); + } + + + const dfs = (crs) => { + if (prereqMap.has(crs)) { + return prereqMap.get(crs); + } + const prereqs = new Set(); + for (const pre of adj[crs]) { + for (const p of dfs(pre)) prereqs.add(p); + } + prereqs.add(crs); + prereqMap.set(crs, prereqs); + return prereqs; + }; + + for (let crs = 0; crs < numCourses; crs++) { + dfs(crs); + } + return queries.map(([u, v]) => prereqMap.get(v).has(u)); + } +} +``` + +```csharp +public class Solution { + public List CheckIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) { + Dictionary> adj = new Dictionary>(); + for (int i = 0; i < numCourses; i++) { + adj[i] = new List(); + } + + foreach (var pair in prerequisites) { + int prereq = pair[0], crs = pair[1]; + adj[crs].Add(prereq); + } + + Dictionary> prereqMap = new Dictionary>(); + + HashSet Dfs(int crs) { + if (!prereqMap.ContainsKey(crs)) { + prereqMap[crs] = new HashSet(); + foreach (var prereq in adj[crs]) { + prereqMap[crs].UnionWith(Dfs(prereq)); + } + prereqMap[crs].Add(crs); + } + return prereqMap[crs]; + } + + for (int crs = 0; crs < numCourses; crs++) { + Dfs(crs); + } + + List res = new List(); + foreach (var q in queries) { + res.Add(prereqMap.ContainsKey(q[1]) && prereqMap[q[1]].Contains(q[0])); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V * (V + E) + m)$ +* Space complexity: $O(V ^ 2 + E + m)$ + +> Where $m$ is the number of queries, $V$ is the number of courses, and $E$ is the number of prerequisites. + +--- + +## 3. Depth First Search (Memoization) + +::tabs-start + +```python +class Solution: + def checkIfPrerequisite(self, numCourses: int, prerequisites: List[List[int]], queries: List[List[int]]) -> List[bool]: + adj = [[] for _ in range(numCourses)] + isPrereq = [[-1] * numCourses for _ in range(numCourses)] + for prereq, crs in prerequisites: + adj[crs].append(prereq) + isPrereq[crs][prereq] = True + + def dfs(crs, prereq): + if isPrereq[crs][prereq] != -1: + return isPrereq[crs][prereq] == 1 + + for pre in adj[crs]: + if pre == prereq or dfs(pre, prereq): + isPrereq[crs][prereq] = 1 + return True + + isPrereq[crs][prereq] = 0 + return False + + res = [] + for u, v in queries: + res.append(dfs(v, u)) + return res +``` + +```java +public class Solution { + private List[] adj; + private int[][] isPrereq; + + public List checkIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) { + adj = new ArrayList[numCourses]; + isPrereq = new int[numCourses][numCourses]; + for (int i = 0; i < numCourses; i++) { + adj[i] = new ArrayList<>(); + Arrays.fill(isPrereq[i], -1); + } + + for (int[] pre : prerequisites) { + adj[pre[1]].add(pre[0]); + isPrereq[pre[1]][pre[0]] = 1; + } + + List res = new ArrayList<>(); + for (int[] query : queries) { + res.add(dfs(query[1], query[0])); + } + return res; + } + + private boolean dfs(int crs, int prereq) { + if (isPrereq[crs][prereq] != -1) { + return isPrereq[crs][prereq] == 1; + } + for (int pre : adj[crs]) { + if (pre == prereq || dfs(pre, prereq)) { + isPrereq[crs][prereq] = 1; + return true; + } + } + isPrereq[crs][prereq] = 0; + return false; + } +} +``` + +```cpp +class Solution { + vector> adj; + vector> isPrereq; + +public: + vector checkIfPrerequisite(int numCourses, vector>& prerequisites, vector>& queries) { + adj.assign(numCourses, vector()); + isPrereq.assign(numCourses, vector(numCourses, -1)); + + for (auto& pre : prerequisites) { + adj[pre[1]].push_back(pre[0]); + isPrereq[pre[1]][pre[0]] = 1; + } + + vector res; + for (auto& query : queries) { + res.push_back(dfs(query[1], query[0])); + } + return res; + } + +private: + bool dfs(int crs, int prereq) { + if (isPrereq[crs][prereq] != -1) { + return isPrereq[crs][prereq] == 1; + } + for (int pre : adj[crs]) { + if (pre == prereq || dfs(pre, prereq)) { + isPrereq[crs][prereq] = 1; + return true; + } + } + isPrereq[crs][prereq] = 0; + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} numCourses + * @param {number[][]} prerequisites + * @param {number[][]} queries + * @return {boolean[]} + */ + checkIfPrerequisite(numCourses, prerequisites, queries) { + const adj = Array.from({ length: numCourses }, () => []); + const isPrereq = Array.from({ length: numCourses }, () => Array(numCourses).fill(-1)); + for (const [prereq, crs] of prerequisites) { + adj[crs].push(prereq); + isPrereq[crs][prereq] = 1; + } + + const dfs = (crs, prereq) => { + if (isPrereq[crs][prereq] !== -1) { + return isPrereq[crs][prereq] === 1; + } + for (const pre of adj[crs]) { + if (pre === prereq || dfs(pre, prereq)) { + isPrereq[crs][prereq] = 1; + return true; + } + } + isPrereq[crs][prereq] = 0; + return false; + }; + + return queries.map(([u, v]) => dfs(v, u)); + } +} +``` + +```csharp +public class Solution { + public List CheckIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) { + List[] adj = new List[numCourses]; + for (int i = 0; i < numCourses; i++) { + adj[i] = new List(); + } + + int[,] isPrereq = new int[numCourses, numCourses]; + for (int i = 0; i < numCourses; i++) { + for (int j = 0; j < numCourses; j++) { + isPrereq[i, j] = -1; + } + } + + foreach (var pair in prerequisites) { + int prereq = pair[0], crs = pair[1]; + adj[crs].Add(prereq); + isPrereq[crs, prereq] = 1; + } + + bool Dfs(int crs, int prereq) { + if (isPrereq[crs, prereq] != -1) { + return isPrereq[crs, prereq] == 1; + } + + foreach (int pre in adj[crs]) { + if (pre == prereq || Dfs(pre, prereq)) { + isPrereq[crs, prereq] = 1; + return true; + } + } + + isPrereq[crs, prereq] = 0; + return false; + } + + List res = new List(); + foreach (var q in queries) { + res.Add(Dfs(q[1], q[0])); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V * (V + E) + m)$ +* Space complexity: $O(V ^ 2 + E + m)$ + +> Where $m$ is the number of queries, $V$ is the number of courses, and $E$ is the number of prerequisites. + +--- + +## 4. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def checkIfPrerequisite(self, numCourses: int, prerequisites: List[List[int]], queries: List[List[int]]) -> List[bool]: + adj = [set() for _ in range(numCourses)] + indegree = [0] * numCourses + isPrereq = [set() for _ in range(numCourses)] + + for pre, crs in prerequisites: + adj[pre].add(crs) + indegree[crs] += 1 + + q = deque([i for i in range(numCourses) if indegree[i] == 0]) + + while q: + node = q.popleft() + for neighbor in adj[node]: + isPrereq[neighbor].add(node) + isPrereq[neighbor].update(isPrereq[node]) + indegree[neighbor] -= 1 + if indegree[neighbor] == 0: + q.append(neighbor) + + return [u in isPrereq[v] for u, v in queries] +``` + +```java +public class Solution { + public List checkIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) { + List> adj = new ArrayList<>(); + List> isPrereq = new ArrayList<>(); + int[] indegree = new int[numCourses]; + for (int i = 0; i < numCourses; i++) { + adj.add(new HashSet<>()); + isPrereq.add(new HashSet<>()); + } + + for (int[] pre : prerequisites) { + adj.get(pre[0]).add(pre[1]); + indegree[pre[1]]++; + } + + Queue q = new LinkedList<>(); + for (int i = 0; i < numCourses; i++) { + if (indegree[i] == 0) q.offer(i); + } + + while (!q.isEmpty()) { + int node = q.poll(); + for (int neighbor : adj.get(node)) { + isPrereq.get(neighbor).add(node); + isPrereq.get(neighbor).addAll(isPrereq.get(node)); + indegree[neighbor]--; + if (indegree[neighbor] == 0) q.offer(neighbor); + } + } + + List res = new ArrayList<>(); + for (int[] query : queries) { + res.add(isPrereq.get(query[1]).contains(query[0])); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector checkIfPrerequisite(int numCourses, vector>& prerequisites, vector>& queries) { + vector> adj(numCourses), isPrereq(numCourses); + vector indegree(numCourses, 0); + + for (auto& pre : prerequisites) { + adj[pre[0]].insert(pre[1]); + indegree[pre[1]]++; + } + + queue q; + for (int i = 0; i < numCourses; i++) { + if (indegree[i] == 0) q.push(i); + } + + while (!q.empty()) { + int node = q.front(); q.pop(); + for (int neighbor : adj[node]) { + isPrereq[neighbor].insert(node); + isPrereq[neighbor].insert(isPrereq[node].begin(), isPrereq[node].end()); + indegree[neighbor]--; + if (indegree[neighbor] == 0) q.push(neighbor); + } + } + + vector res; + for (auto& query : queries) { + res.push_back(isPrereq[query[1]].count(query[0])); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} numCourses + * @param {number[][]} prerequisites + * @param {number[][]} queries + * @return {boolean[]} + */ + checkIfPrerequisite(numCourses, prerequisites, queries) { + const adj = Array.from({ length: numCourses }, () => new Set()); + const isPrereq = Array.from({ length: numCourses }, () => new Set()); + const indegree = Array(numCourses).fill(0); + + for (const [pre, crs] of prerequisites) { + adj[pre].add(crs); + indegree[crs]++; + } + + const q = new Queue(); + for (let i = 0; i < numCourses; i++) { + if (indegree[i] === 0) q.push(i); + } + + while (!q.isEmpty()) { + const node = q.pop(); + for (const neighbor of adj[node]) { + isPrereq[neighbor].add(node); + for (const it of isPrereq[node]) { + isPrereq[neighbor].add(it); + } + indegree[neighbor]--; + if (indegree[neighbor] === 0) q.push(neighbor); + } + } + + return queries.map(([u, v]) => isPrereq[v].has(u)); + } +} +``` + +```csharp +public class Solution { + public List CheckIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) { + List> adj = new List>(); + List> isPrereq = new List>(); + int[] indegree = new int[numCourses]; + + for (int i = 0; i < numCourses; i++) { + adj.Add(new HashSet()); + isPrereq.Add(new HashSet()); + } + + foreach (var pair in prerequisites) { + int pre = pair[0], crs = pair[1]; + adj[pre].Add(crs); + indegree[crs]++; + } + + Queue q = new Queue(); + for (int i = 0; i < numCourses; i++) { + if (indegree[i] == 0) { + q.Enqueue(i); + } + } + + while (q.Count > 0) { + int node = q.Dequeue(); + foreach (int neighbor in adj[node]) { + isPrereq[neighbor].Add(node); + foreach (int p in isPrereq[node]) { + isPrereq[neighbor].Add(p); + } + indegree[neighbor]--; + if (indegree[neighbor] == 0) { + q.Enqueue(neighbor); + } + } + } + + List result = new List(); + foreach (var query in queries) { + int u = query[0], v = query[1]; + result.Add(isPrereq[v].Contains(u)); + } + + return result; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V * (V + E) + m)$ +* Space complexity: $O(V ^ 2 + E + m)$ + +> Where $m$ is the number of queries, $V$ is the number of courses, and $E$ is the number of prerequisites. + +--- + +## 5. Floyd Warshall Algorithm + +::tabs-start + +```python +class Solution: + def checkIfPrerequisite(self, numCourses: int, prerequisites: List[List[int]], queries: List[List[int]]) -> List[bool]: + res = [] + adj = [[False] * numCourses for _ in range(numCourses)] + + for pre, crs in prerequisites: + adj[pre][crs] = True + + for k in range(numCourses): + for i in range(numCourses): + for j in range(numCourses): + adj[i][j] = adj[i][j] or (adj[i][k] and adj[k][j]) + + for u, v in queries: + res.append(adj[u][v]) + + return res +``` + +```java +public class Solution { + public List checkIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) { + boolean[][] adj = new boolean[numCourses][numCourses]; + List res = new ArrayList<>(); + + for (int[] pre : prerequisites) { + adj[pre[0]][pre[1]] = true; + } + + for (int k = 0; k < numCourses; k++) { + for (int i = 0; i < numCourses; i++) { + for (int j = 0; j < numCourses; j++) { + adj[i][j] = adj[i][j] || (adj[i][k] && adj[k][j]); + } + } + } + + for (int[] q : queries) { + res.add(adj[q[0]][q[1]]); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector checkIfPrerequisite(int numCourses, vector>& prerequisites, vector>& queries) { + vector> adj(numCourses, vector(numCourses, false)); + vector res; + + for (auto& pre : prerequisites) { + adj[pre[0]][pre[1]] = true; + } + + for (int k = 0; k < numCourses; k++) { + for (int i = 0; i < numCourses; i++) { + for (int j = 0; j < numCourses; j++) { + adj[i][j] = adj[i][j] || (adj[i][k] && adj[k][j]); + } + } + } + + for (auto& q : queries) { + res.push_back(adj[q[0]][q[1]]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} numCourses + * @param {number[][]} prerequisites + * @param {number[][]} queries + * @return {boolean[]} + */ + checkIfPrerequisite(numCourses, prerequisites, queries) { + let adj = Array.from({ length: numCourses }, () => Array(numCourses).fill(false)); + let res = []; + + for (let [pre, crs] of prerequisites) { + adj[pre][crs] = true; + } + + for (let k = 0; k < numCourses; k++) { + for (let i = 0; i < numCourses; i++) { + for (let j = 0; j < numCourses; j++) { + adj[i][j] = adj[i][j] || (adj[i][k] && adj[k][j]); + } + } + } + + for (let [u, v] of queries) { + res.push(adj[u][v]); + } + + return res; + } +} +``` + +```csharp +public class Solution { + public List CheckIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) { + bool[,] adj = new bool[numCourses, numCourses]; + + foreach (var pair in prerequisites) { + int pre = pair[0], crs = pair[1]; + adj[pre, crs] = true; + } + + for (int k = 0; k < numCourses; k++) { + for (int i = 0; i < numCourses; i++) { + for (int j = 0; j < numCourses; j++) { + adj[i, j] = adj[i, j] || (adj[i, k] && adj[k, j]); + } + } + } + + List res = new List(); + foreach (var query in queries) { + int u = query[0], v = query[1]; + res.Add(adj[u, v]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V ^ 3 + E + m)$ +* Space complexity: $O(V ^ 2 + E + m)$ + +> Where $m$ is the number of queries, $V$ is the number of courses, and $E$ is the number of prerequisites. \ No newline at end of file diff --git a/articles/course-schedule.md b/articles/course-schedule.md new file mode 100644 index 000000000..d57ce12c1 --- /dev/null +++ b/articles/course-schedule.md @@ -0,0 +1,667 @@ +## 1. Cycle Detection (DFS) + +::tabs-start + +```python +class Solution: + def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool: + # Map each course to its prerequisites + preMap = {i: [] for i in range(numCourses)} + for crs, pre in prerequisites: + preMap[crs].append(pre) + + # Store all courses along the current DFS path + visiting = set() + + def dfs(crs): + if crs in visiting: + # Cycle detected + return False + if preMap[crs] == []: + return True + + visiting.add(crs) + for pre in preMap[crs]: + if not dfs(pre): + return False + visiting.remove(crs) + preMap[crs] = [] + return True + + for c in range(numCourses): + if not dfs(c): + return False + return True +``` + +```java +public class Solution { + // Map each course to its prerequisites + private Map> preMap = new HashMap<>(); + // Store all courses along the current DFS path + private Set visiting = new HashSet<>(); + + public boolean canFinish(int numCourses, int[][] prerequisites) { + for (int i = 0; i < numCourses; i++) { + preMap.put(i, new ArrayList<>()); + } + for (int[] prereq : prerequisites) { + preMap.get(prereq[0]).add(prereq[1]); + } + + for (int c = 0; c < numCourses; c++) { + if (!dfs(c)) { + return false; + } + } + return true; + } + + private boolean dfs(int crs) { + if (visiting.contains(crs)) { + // Cycle detected + return false; + } + if (preMap.get(crs).isEmpty()) { + return true; + } + + visiting.add(crs); + for (int pre : preMap.get(crs)) { + if (!dfs(pre)) { + return false; + } + } + visiting.remove(crs); + preMap.put(crs, new ArrayList<>()); + return true; + } +} +``` + +```cpp +class Solution { + // Map each course to its prerequisites + unordered_map> preMap; + // Store all courses along the current DFS path + unordered_set visiting; + +public: + bool canFinish(int numCourses, vector>& prerequisites) { + for (int i = 0; i < numCourses; i++) { + preMap[i] = {}; + } + for (const auto& prereq : prerequisites) { + preMap[prereq[0]].push_back(prereq[1]); + } + + for (int c = 0; c < numCourses; c++) { + if (!dfs(c)) { + return false; + } + } + return true; + } + + bool dfs(int crs) { + if (visiting.count(crs)) { + // Cycle detected + return false; + } + if (preMap[crs].empty()) { + return true; + } + + visiting.insert(crs); + for (int pre : preMap[crs]) { + if (!dfs(pre)) { + return false; + } + } + visiting.erase(crs); + preMap[crs].clear(); + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} numCourses + * @param {number[][]} prerequisites + * @return {boolean} + */ + canFinish(numCourses, prerequisites) { + const preMap = new Map(); + for (let i = 0; i < numCourses; i++) { + preMap.set(i, []); + } + for (let [crs, pre] of prerequisites) { + preMap.get(crs).push(pre); + } + + // Store all courses along the current DFS path + const visiting = new Set(); + + const dfs = (crs) => { + if (visiting.has(crs)) { + // Cycle detected + return false; + } + if (preMap.get(crs).length === 0) { + return true; + } + + visiting.add(crs); + for (let pre of preMap.get(crs)) { + if (!dfs(pre)) { + return false; + } + } + visiting.delete(crs); + preMap.set(crs, []); + return true; + } + + for (let c = 0; c < numCourses; c++) { + if (!dfs(c)) { + return false; + } + } + return true; + } +} +``` + +```csharp +public class Solution { + // Map each course to its prerequisites + private Dictionary> preMap = new Dictionary>(); + // Store all courses along the current DFS path + private HashSet visiting = new HashSet(); + + public bool CanFinish(int numCourses, int[][] prerequisites) { + for (int i = 0; i < numCourses; i++) { + preMap[i] = new List(); + } + foreach (var prereq in prerequisites) { + preMap[prereq[0]].Add(prereq[1]); + } + + for (int c = 0; c < numCourses; c++) { + if (!Dfs(c)) { + return false; + } + } + return true; + } + + private bool Dfs(int crs) { + if (visiting.Contains(crs)) { + // Cycle detected + return false; + } + if (preMap[crs].Count == 0) { + return true; + } + + visiting.Add(crs); + foreach (int pre in preMap[crs]) { + if (!Dfs(pre)) { + return false; + } + } + visiting.Remove(crs); + preMap[crs].Clear(); + return true; + } +} +``` + +```go +func canFinish(numCourses int, prerequisites [][]int) bool { + // Map each course to its prerequisites + preMap := make(map[int][]int) + for i := 0; i < numCourses; i++ { + preMap[i] = []int{} + } + for _, prereq := range prerequisites { + crs, pre := prereq[0], prereq[1] + preMap[crs] = append(preMap[crs], pre) + } + + // Store all courses along the current DFS path + visiting := make(map[int]bool) + + var dfs func(int) bool + dfs = func(crs int) bool { + if visiting[crs] { + // Cycle detected + return false + } + if len(preMap[crs]) == 0 { + return true + } + + visiting[crs] = true + for _, pre := range preMap[crs] { + if !dfs(pre) { + return false + } + } + visiting[crs] = false + preMap[crs] = []int{} + return true + } + + for c := 0; c < numCourses; c++ { + if !dfs(c) { + return false + } + } + return true +} +``` + +```kotlin +class Solution { + fun canFinish(numCourses: Int, prerequisites: Array): Boolean { + // Map each course to its prerequisites + val preMap = HashMap>() + for (i in 0 until numCourses) { + preMap[i] = mutableListOf() + } + for (prereq in prerequisites) { + val (crs, pre) = prereq + preMap[crs]!!.add(pre) + } + + // Store all courses along the current DFS path + val visiting = HashSet() + + fun dfs(crs: Int): Boolean { + if (crs in visiting) { + // Cycle detected + return false + } + if (preMap[crs]!!.isEmpty()) { + return true + } + + visiting.add(crs) + for (pre in preMap[crs]!!) { + if (!dfs(pre)) { + return false + } + } + visiting.remove(crs) + preMap[crs] = mutableListOf() + return true + } + + for (c in 0 until numCourses) { + if (!dfs(c)) { + return false + } + } + return true + } +} +``` + +```swift +class Solution { + func canFinish(_ numCourses: Int, _ prerequisites: [[Int]]) -> Bool { + // Map each course to its prerequisites + var preMap = [Int: [Int]]() + for i in 0..() + + func dfs(_ crs: Int) -> Bool { + if visiting.contains(crs) { + // Cycle detected + return false + } + if preMap[crs]!.isEmpty { + return true + } + + visiting.insert(crs) + for pre in preMap[crs]! { + if !dfs(pre) { + return false + } + } + visiting.remove(crs) + preMap[crs] = [] + return true + } + + for c in 0.. Where $V$ is the number of courses and $E$ is the number of prerequisites. + +--- + +## 2. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool: + indegree = [0] * numCourses + adj = [[] for i in range(numCourses)] + for src, dst in prerequisites: + indegree[dst] += 1 + adj[src].append(dst) + + q = deque() + for n in range(numCourses): + if indegree[n] == 0: + q.append(n) + + finish = 0 + while q: + node = q.popleft() + finish += 1 + for nei in adj[node]: + indegree[nei] -= 1 + if indegree[nei] == 0: + q.append(nei) + + return finish == numCourses +``` + +```java +public class Solution { + public boolean canFinish(int numCourses, int[][] prerequisites) { + int[] indegree = new int[numCourses]; + List> adj = new ArrayList<>(); + for (int i = 0; i < numCourses; i++) { + adj.add(new ArrayList<>()); + } + for (int[] pre : prerequisites) { + indegree[pre[1]]++; + adj.get(pre[0]).add(pre[1]); + } + + Queue q = new LinkedList<>(); + for (int i = 0; i < numCourses; i++) { + if (indegree[i] == 0) { + q.add(i); + } + } + + int finish = 0; + while (!q.isEmpty()) { + int node = q.poll(); + finish++; + for (int nei : adj.get(node)) { + indegree[nei]--; + if (indegree[nei] == 0) { + q.add(nei); + } + } + } + + return finish == numCourses; + } +} +``` + +```cpp +class Solution { +public: + bool canFinish(int numCourses, vector>& prerequisites) { + vector indegree(numCourses, 0); + vector> adj(numCourses); + + for (auto& pre : prerequisites) { + indegree[pre[1]]++; + adj[pre[0]].push_back(pre[1]); + } + + queue q; + for (int i = 0; i < numCourses; ++i) { + if (indegree[i] == 0) { + q.push(i); + } + } + + int finish = 0; + while (!q.empty()) { + int node = q.front(); + q.pop(); + finish++; + for (int nei : adj[node]) { + indegree[nei]--; + if (indegree[nei] == 0) { + q.push(nei); + } + } + } + + return finish == numCourses; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} numCourses + * @param {number[][]} prerequisites + * @return {boolean} + */ + canFinish(numCourses, prerequisites) { + let indegree = Array(numCourses).fill(0); + let adj = Array.from({ length: numCourses }, () => []); + for (let [src, dst] of prerequisites) { + indegree[dst]++; + adj[src].push(dst); + } + + let q = new Queue(); + for (let i = 0; i < numCourses; i++) { + if (indegree[i] === 0) { + q.push(i); + } + } + + let finish = 0; + while (!q.isEmpty()) { + let node = q.pop(); + finish++; + for (let nei of adj[node]) { + indegree[nei]--; + if (indegree[nei] === 0) { + q.push(nei); + } + } + } + + return finish === numCourses; + } +} +``` + +```csharp +public class Solution { + public bool CanFinish(int numCourses, int[][] prerequisites) { + int[] indegree = new int[numCourses]; + List> adj = new List>(); + for (int i = 0; i < numCourses; i++) { + adj.Add(new List()); + } + foreach (var pre in prerequisites) { + indegree[pre[1]]++; + adj[pre[0]].Add(pre[1]); + } + + Queue q = new Queue(); + for (int i = 0; i < numCourses; i++) { + if (indegree[i] == 0) { + q.Enqueue(i); + } + } + + int finish = 0; + while (q.Count > 0) { + int node = q.Dequeue(); + finish++; + foreach (var nei in adj[node]) { + indegree[nei]--; + if (indegree[nei] == 0) { + q.Enqueue(nei); + } + } + } + + return finish == numCourses; + } +} +``` + +```go +func canFinish(numCourses int, prerequisites [][]int) bool { + indegree := make([]int, numCourses) + adj := make([][]int, numCourses) + for i := 0; i < numCourses; i++ { + adj[i] = []int{} + } + + for _, prereq := range prerequisites { + src, dst := prereq[0], prereq[1] + indegree[dst]++ + adj[src] = append(adj[src], dst) + } + + q := []int{} + for n := 0; n < numCourses; n++ { + if indegree[n] == 0 { + q = append(q, n) + } + } + + finish := 0 + for len(q) > 0 { + node := q[0] + q = q[1:] + finish++ + for _, nei := range adj[node] { + indegree[nei]-- + if indegree[nei] == 0 { + q = append(q, nei) + } + } + } + + return finish == numCourses +} +``` + +```kotlin +class Solution { + fun canFinish(numCourses: Int, prerequisites: Array): Boolean { + val indegree = IntArray(numCourses) { 0 } + val adj = Array(numCourses) { mutableListOf() } + + for (prereq in prerequisites) { + val (src, dst) = prereq + indegree[dst]++ + adj[src].add(dst) + } + + val q: Queue = LinkedList() + for (n in 0 until numCourses) { + if (indegree[n] == 0) { + q.add(n) + } + } + + var finish = 0 + while (q.isNotEmpty()) { + val node = q.poll() + finish++ + for (nei in adj[node]) { + indegree[nei]-- + if (indegree[nei] == 0) { + q.add(nei) + } + } + } + + return finish == numCourses + } +} +``` + +```swift +class Solution { + func canFinish(_ numCourses: Int, _ prerequisites: [[Int]]) -> Bool { + var indegree = Array(repeating: 0, count: numCourses) + var adj = Array(repeating: [Int](), count: numCourses) + + for pair in prerequisites { + let src = pair[0] + let dst = pair[1] + indegree[dst] += 1 + adj[src].append(dst) + } + + var queue = Deque() + for n in 0.. Where $V$ is the number of courses and $E$ is the number of prerequisites. \ No newline at end of file diff --git a/articles/daily-temperatures.md b/articles/daily-temperatures.md new file mode 100644 index 000000000..f004aac40 --- /dev/null +++ b/articles/daily-temperatures.md @@ -0,0 +1,587 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def dailyTemperatures(self, temperatures: List[int]) -> List[int]: + n = len(temperatures) + res = [] + + for i in range(n): + count = 1 + j = i + 1 + while j < n: + if temperatures[j] > temperatures[i]: + break + j += 1 + count += 1 + count = 0 if j == n else count + res.append(count) + return res +``` + +```java +public class Solution { + public int[] dailyTemperatures(int[] temperatures) { + int n = temperatures.length; + int[] res = new int[n]; + + for (int i = 0; i < n; i++) { + int count = 1; + int j = i + 1; + while (j < n) { + if (temperatures[j] > temperatures[i]) { + break; + } + j++; + count++; + } + count = (j == n) ? 0 : count; + res[i] = count; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector dailyTemperatures(vector& temperatures) { + int n = temperatures.size(); + vector res(n); + + for (int i = 0; i < n; i++) { + int count = 1; + int j = i + 1; + while (j < n) { + if (temperatures[j] > temperatures[i]) { + break; + } + j++; + count++; + } + count = (j == n) ? 0 : count; + res[i] = count; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} temperatures + * @return {number[]} + */ + dailyTemperatures(temperatures) { + const n = temperatures.length; + const res = new Array(n).fill(0); + + for (let i = 0; i < n; i++) { + let count = 1; + let j = i + 1; + while (j < n) { + if (temperatures[j] > temperatures[i]) { + break; + } + j++; + count++; + } + count = (j === n) ? 0 : count; + res[i] = count; + } + return res; + } +} +``` + +```csharp +public class Solution { + public int[] DailyTemperatures(int[] temperatures) { + int n = temperatures.Length; + int[] res = new int[n]; + + for (int i = 0; i < n; i++) { + int count = 1; + int j = i + 1; + while (j < n) { + if (temperatures[j] > temperatures[i]) { + break; + } + j++; + count++; + } + count = (j == n) ? 0 : count; + res[i] = count; + } + return res; + } +} +``` + +```go +func dailyTemperatures(temperatures []int) []int { + n := len(temperatures) + res := make([]int, 0) + + for i := 0; i < n; i++ { + count := 1 + j := i + 1 + + for j < n { + if temperatures[j] > temperatures[i] { + break + } + j++ + count++ + } + + if j == n { + count = 0 + } + + res = append(res, count) + } + + return res +} +``` + +```kotlin +class Solution { + fun dailyTemperatures(temperatures: IntArray): IntArray { + val n = temperatures.size + val res = mutableListOf() + + for (i in 0 until n) { + var count = 1 + var j = i + 1 + + while (j < n) { + if (temperatures[j] > temperatures[i]) { + break + } + j++ + count++ + } + + count = if (j == n) 0 else count + res.add(count) + } + + return res.toIntArray() + } +} +``` + +```swift +class Solution { + func dailyTemperatures(_ temperatures: [Int]) -> [Int] { + let n = temperatures.count + var res = [Int]() + + for i in 0.. temperatures[i] { + break + } + j += 1 + count += 1 + } + count = (j == n) ? 0 : count + res.append(count) + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. + +--- + +## 2. Stack + +::tabs-start + +```python +class Solution: + def dailyTemperatures(self, temperatures: List[int]) -> List[int]: + res = [0] * len(temperatures) + stack = [] # pair: [temp, index] + + for i, t in enumerate(temperatures): + while stack and t > stack[-1][0]: + stackT, stackInd = stack.pop() + res[stackInd] = i - stackInd + stack.append((t, i)) + return res +``` + +```java +public class Solution { + public int[] dailyTemperatures(int[] temperatures) { + int[] res = new int[temperatures.length]; + Stack stack = new Stack<>(); // pair: [temp, index] + + for (int i = 0; i < temperatures.length; i++) { + int t = temperatures[i]; + while (!stack.isEmpty() && t > stack.peek()[0]) { + int[] pair = stack.pop(); + res[pair[1]] = i - pair[1]; + } + stack.push(new int[]{t, i}); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector dailyTemperatures(vector& temperatures) { + vector res(temperatures.size(), 0); + stack> stack; // pair: {temp, index} + + for (int i = 0; i < temperatures.size(); i++) { + int t = temperatures[i]; + while (!stack.empty() && t > stack.top().first) { + auto pair = stack.top(); + stack.pop(); + res[pair.second] = i - pair.second; + } + stack.push({t, i}); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} temperatures + * @return {number[]} + */ + dailyTemperatures(temperatures) { + const res = new Array(temperatures.length).fill(0); + const stack = []; // pair: [temp, index] + + for (let i = 0; i < temperatures.length; i++) { + const t = temperatures[i]; + while (stack.length > 0 && t > stack[stack.length - 1][0]) { + const [stackT, stackInd] = stack.pop(); + res[stackInd] = i - stackInd; + } + stack.push([t, i]); + } + return res; + } +} +``` + +```csharp +public class Solution { + public int[] DailyTemperatures(int[] temperatures) { + int[] res = new int[temperatures.Length]; + Stack stack = new Stack(); // pair: [temp, index] + + for (int i = 0; i < temperatures.Length; i++) { + int t = temperatures[i]; + while (stack.Count > 0 && t > stack.Peek()[0]) { + int[] pair = stack.Pop(); + res[pair[1]] = i - pair[1]; + } + stack.Push(new int[] { t, i }); + } + return res; + } +} +``` + +```go +func dailyTemperatures(temperatures []int) []int { + res := make([]int, len(temperatures)) + stack := []int{} + + for i, t := range temperatures { + for len(stack) > 0 && t > temperatures[stack[len(stack)-1]] { + stackInd := stack[len(stack)-1] + stack = stack[:len(stack)-1] + res[stackInd] = i - stackInd + } + stack = append(stack, i) + } + + return res +} +``` + +```kotlin +class Solution { + fun dailyTemperatures(temperatures: IntArray): IntArray { + val res = IntArray(temperatures.size) { 0 } + val stack = mutableListOf() + + for (i in temperatures.indices) { + while (stack.isNotEmpty() && temperatures[i] > temperatures[stack.last()]) { + val stackInd = stack.removeAt(stack.size - 1) + res[stackInd] = i - stackInd + } + stack.add(i) + } + + return res + } +} +``` + +```swift +class Solution { + func dailyTemperatures(_ temperatures: [Int]) -> [Int] { + var res = [Int](repeating: 0, count: temperatures.count) + var stack = [(Int, Int)]() // Pair: (temperature, index) + + for (i, t) in temperatures.enumerated() { + while !stack.isEmpty && t > stack.last!.0 { + let (stackT, stackInd) = stack.removeLast() + res[stackInd] = i - stackInd + } + stack.append((t, i)) + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming + +::tabs-start + +```python +class Solution: + def dailyTemperatures(self, temperatures: List[int]) -> List[int]: + n = len(temperatures) + res = [0] * n + + for i in range(n - 2, -1, -1): + j = i + 1 + while j < n and temperatures[j] <= temperatures[i]: + if res[j] == 0: + j = n + break + j += res[j] + + if j < n: + res[i] = j - i + return res +``` + +```java +public class Solution { + public int[] dailyTemperatures(int[] temperatures) { + int n = temperatures.length; + int[] res = new int[n]; + + for (int i = n - 2; i >= 0; i--) { + int j = i + 1; + while (j < n && temperatures[j] <= temperatures[i]) { + if (res[j] == 0) { + j = n; + break; + } + j += res[j]; + } + + if (j < n) { + res[i] = j - i; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector dailyTemperatures(vector& temperatures) { + int n = temperatures.size(); + vector res(n, 0); + + for (int i = n - 2; i >= 0; i--) { + int j = i + 1; + while (j < n && temperatures[j] <= temperatures[i]) { + if (res[j] == 0) { + j = n; + break; + } + j += res[j]; + } + + if (j < n) { + res[i] = j - i; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} temperatures + * @return {number[]} + */ + dailyTemperatures(temperatures) { + const n = temperatures.length; + const res = new Array(n).fill(0); + + for (let i = n - 2; i >= 0; i--) { + let j = i + 1; + while (j < n && temperatures[j] <= temperatures[i]) { + if (res[j] === 0) { + j = n; + break; + } + j += res[j]; + } + + if (j < n) { + res[i] = j - i; + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int[] DailyTemperatures(int[] temperatures) { + int n = temperatures.Length; + int[] res = new int[n]; + + for (int i = n - 2; i >= 0; i--) { + int j = i + 1; + while (j < n && temperatures[j] <= temperatures[i]) { + if (res[j] == 0) { + j = n; + break; + } + j += res[j]; + } + + if (j < n) { + res[i] = j - i; + } + } + return res; + } +} +``` + +```go +func dailyTemperatures(temperatures []int) []int { + n := len(temperatures) + res := make([]int, n) + + for i := n - 2; i >= 0; i-- { + j := i + 1 + for j < n && temperatures[j] <= temperatures[i] { + if res[j] == 0 { + j = n + break + } + j += res[j] + } + + if j < n { + res[i] = j - i + } + } + return res +} +``` + +```kotlin +class Solution { + fun dailyTemperatures(temperatures: IntArray): IntArray { + val n = temperatures.size + val res = IntArray(n) + + for (i in n - 2 downTo 0) { + var j = i + 1 + while (j < n && temperatures[j] <= temperatures[i]) { + if (res[j] == 0) { + j = n + break + } + j += res[j] + } + + if (j < n) { + res[i] = j - i + } + } + return res + } +} +``` + +```swift +class Solution { + func dailyTemperatures(_ temperatures: [Int]) -> [Int] { + let n = temperatures.count + var res = [Int](repeating: 0, count: n) + + for i in stride(from: n - 2, through: 0, by: -1) { + var j = i + 1 + while j < n && temperatures[j] <= temperatures[i] { + if res[j] == 0 { + j = n + break + } + j += res[j] + } + + if j < n { + res[i] = j - i + } + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. \ No newline at end of file diff --git a/articles/data-stream-as-disjoint-intervals.md b/articles/data-stream-as-disjoint-intervals.md new file mode 100644 index 000000000..a39dc70f3 --- /dev/null +++ b/articles/data-stream-as-disjoint-intervals.md @@ -0,0 +1,450 @@ +## 1. Brute Force (Sorting) + +::tabs-start + +```python +class SummaryRanges: + + def __init__(self): + self.arr = [] + + def addNum(self, value: int) -> None: + self.arr.append(value) + + def getIntervals(self) -> List[List[int]]: + if not self.arr: + return [] + + self.arr.sort() + n = len(self.arr) + start = self.arr[0] + res = [] + for i in range(1, n): + if self.arr[i] - self.arr[i - 1] > 1: + res.append([start, self.arr[i - 1]]) + start = self.arr[i] + + res.append([start, self.arr[n - 1]]) + return res +``` + +```java +public class SummaryRanges { + private List arr; + + public SummaryRanges() { + arr = new ArrayList<>(); + } + + public void addNum(int value) { + arr.add(value); + } + + public List getIntervals() { + List res = new ArrayList<>(); + if (arr.isEmpty()) return res; + + Collections.sort(arr); + int start = arr.get(0); + for (int i = 1; i < arr.size(); i++) { + if (arr.get(i) - arr.get(i - 1) > 1) { + res.add(new int[]{start, arr.get(i - 1)}); + start = arr.get(i); + } + } + res.add(new int[]{start, arr.get(arr.size() - 1)}); + return res; + } +} +``` + +```cpp +class SummaryRanges { +private: + vector arr; + +public: + SummaryRanges() {} + + void addNum(int value) { + arr.push_back(value); + } + + vector> getIntervals() { + vector> res; + if (arr.empty()) return res; + + sort(arr.begin(), arr.end()); + int start = arr[0]; + for (int i = 1; i < arr.size(); i++) { + if (arr[i] - arr[i - 1] > 1) { + res.push_back({start, arr[i - 1]}); + start = arr[i]; + } + } + res.push_back({start, arr.back()}); + return res; + } +}; +``` + +```javascript +class SummaryRanges { + constructor() { + this.arr = []; + } + + /** + * @param {number} value + * @return {void} + */ + addNum(value) { + this.arr.push(value); + } + + /** + * @return {number[][]} + */ + getIntervals() { + if (this.arr.length === 0) return []; + + this.arr.sort((a, b) => a - b); + let start = this.arr[0]; + let res = []; + + for (let i = 1; i < this.arr.length; i++) { + if (this.arr[i] - this.arr[i - 1] > 1) { + res.push([start, this.arr[i - 1]]); + start = this.arr[i]; + } + } + res.push([start, this.arr[this.arr.length - 1]]); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $addNum()$ function call. + * $O(n \log n)$ time for each $getIntervals()$ function call. +* Space complexity: $O(n)$ + +--- + +## 2. Hash Set + Sorting + +::tabs-start + +```python +class SummaryRanges: + + def __init__(self): + self.arr = set() + + def addNum(self, value: int) -> None: + self.arr.add(value) + + def getIntervals(self) -> List[List[int]]: + if not self.arr: + return [] + + lst = sorted(list(self.arr)) + n = len(lst) + start = lst[0] + res = [] + for i in range(1, n): + if lst[i] - lst[i - 1] > 1: + res.append([start, lst[i - 1]]) + start = lst[i] + + res.append([start, lst[n - 1]]) + return res +``` + +```java +public class SummaryRanges { + private Set arr; + + public SummaryRanges() { + arr = new TreeSet<>(); + } + + public void addNum(int value) { + arr.add(value); + } + + public List getIntervals() { + List res = new ArrayList<>(); + if (arr.isEmpty()) return res; + + List lst = new ArrayList<>(arr); + int start = lst.get(0); + for (int i = 1; i < lst.size(); i++) { + if (lst.get(i) - lst.get(i - 1) > 1) { + res.add(new int[]{start, lst.get(i - 1)}); + start = lst.get(i); + } + } + res.add(new int[]{start, lst.get(lst.size() - 1)}); + return res; + } +} +``` + +```cpp +class SummaryRanges { +private: + set arr; + +public: + SummaryRanges() {} + + void addNum(int value) { + arr.insert(value); + } + + vector> getIntervals() { + vector> res; + if (arr.empty()) return res; + + vector lst(arr.begin(), arr.end()); + int start = lst[0]; + + for (int i = 1; i < lst.size(); i++) { + if (lst[i] - lst[i - 1] > 1) { + res.push_back({start, lst[i - 1]}); + start = lst[i]; + } + } + res.push_back({start, lst.back()}); + return res; + } +}; +``` + +```javascript +class SummaryRanges { + constructor() { + this.arr = new Set(); + } + + /** + * @param {number} value + * @return {number[][]} + */ + addNum(value) { + this.arr.add(value); + } + + /** + * @return {number[][]} + */ + getIntervals() { + if (this.arr.size === 0) return []; + + let lst = Array.from(this.arr).sort((a, b) => a - b); + let start = lst[0]; + let res = []; + + for (let i = 1; i < lst.length; i++) { + if (lst[i] - lst[i - 1] > 1) { + res.push([start, lst[i - 1]]); + start = lst[i]; + } + } + res.push([start, lst[lst.length - 1]]); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $addNum()$ function call. + * $O(n \log n)$ time for each $getIntervals()$ function call. +* Space complexity: $O(n)$ + +--- + +## 3. Ordered Map + +::tabs-start + +```python +from sortedcontainers import SortedDict + +class SummaryRanges: + def __init__(self): + self.treeMap = SortedDict() + + def addNum(self, value: int) -> None: + self.treeMap[value] = True + + def getIntervals(self) -> List[List[int]]: + res = [] + for n in self.treeMap: + if res and res[-1][1] + 1 == n: + res[-1][1] = n + else: + res.append([n, n]) + return res +``` + +```java +public class SummaryRanges { + private TreeMap treeMap; + + public SummaryRanges() { + treeMap = new TreeMap<>(); + } + + public void addNum(int value) { + treeMap.put(value, true); + } + + public List getIntervals() { + List res = new ArrayList<>(); + for (int n : treeMap.keySet()) { + if (!res.isEmpty() && res.get(res.size() - 1)[1] + 1 == n) { + res.get(res.size() - 1)[1] = n; + } else { + res.add(new int[]{n, n}); + } + } + return res; + } +} +``` + +```cpp +class SummaryRanges { +private: + map treeMap; + +public: + SummaryRanges() {} + + void addNum(int value) { + treeMap[value] = true; + } + + vector> getIntervals() { + vector> res; + for (auto& [n, _] : treeMap) { + if (!res.empty() && res.back()[1] + 1 == n) { + res.back()[1] = n; + } else { + res.push_back({n, n}); + } + } + return res; + } +}; +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(\log n)$ time for each $addNum()$ function call. + * $O(n)$ time for each $getIntervals()$ function call. +* Space complexity: $O(n)$ + +--- + +## 4. Ordered Set + +::tabs-start + +```python +from sortedcontainers import SortedSet + +class SummaryRanges: + def __init__(self): + self.orderedSet = SortedSet() + + def addNum(self, value: int) -> None: + self.orderedSet.add(value) + + def getIntervals(self) -> List[List[int]]: + res = [] + for n in self.orderedSet: + if res and res[-1][1] + 1 == n: + res[-1][1] = n + else: + res.append([n, n]) + return res +``` + +```java +public class SummaryRanges { + private TreeSet orderedSet; + + public SummaryRanges() { + orderedSet = new TreeSet<>(); + } + + public void addNum(int value) { + orderedSet.add(value); + } + + public List getIntervals() { + List res = new ArrayList<>(); + for (int n : orderedSet) { + if (!res.isEmpty() && res.get(res.size() - 1)[1] + 1 == n) { + res.get(res.size() - 1)[1] = n; + } else { + res.add(new int[]{n, n}); + } + } + return res; + } +} +``` + +```cpp +class SummaryRanges { +private: + set orderedSet; + +public: + SummaryRanges() {} + + void addNum(int value) { + orderedSet.insert(value); + } + + vector> getIntervals() { + vector> res; + for (int n : orderedSet) { + if (!res.empty() && res.back()[1] + 1 == n) { + res.back()[1] = n; + } else { + res.push_back({n, n}); + } + } + return res; + } +}; +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(\log n)$ time for each $addNum()$ function call. + * $O(n)$ time for each $getIntervals()$ function call. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/decode-string.md b/articles/decode-string.md new file mode 100644 index 000000000..c86b9b8f1 --- /dev/null +++ b/articles/decode-string.md @@ -0,0 +1,554 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def decodeString(self, s: str) -> str: + self.i = 0 + + def helper(): + res = "" + k = 0 + + while self.i < len(s): + c = s[self.i] + + if c.isdigit(): + k = k * 10 + int(c) + elif c == "[": + self.i += 1 + res += k * helper() + k = 0 + elif c == "]": + return res + else: + res += c + + self.i += 1 + return res + + return helper() +``` + +```java +public class Solution { + private int i = 0; + + public String decodeString(String s) { + return helper(s); + } + + private String helper(String s) { + StringBuilder res = new StringBuilder(); + int k = 0; + + while (i < s.length()) { + char c = s.charAt(i); + + if (Character.isDigit(c)) { + k = k * 10 + (c - '0'); + } else if (c == '[') { + i++; + String subRes = helper(s); + while (k-- > 0) res.append(subRes); + k = 0; + } else if (c == ']') { + return res.toString(); + } else { + res.append(c); + } + + i++; + } + + return res.toString(); + } +} +``` + +```cpp +class Solution { +private: + string helper(int& i, string& s) { + string res; + int k = 0; + + while (i < s.size()) { + char c = s[i]; + + if (isdigit(c)) { + k = k * 10 + (c - '0'); + } else if (c == '[') { + i++; + string subRes = helper(i, s); + while (k-- > 0) res += subRes; + k = 0; + } else if (c == ']') { + return res; + } else { + res += c; + } + + i++; + } + + return res; + } + +public: + string decodeString(string s) { + int i = 0; + return helper(i, s); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + decodeString(s) { + let i = 0; + + const helper = () => { + let res = ""; + let k = 0; + + while (i < s.length) { + const c = s[i]; + + if (!isNaN(c)) { + k = k * 10 + parseInt(c, 10); + } else if (c === "[") { + i++; + res += helper().repeat(k); + k = 0; + } else if (c === "]") { + return res; + } else { + res += c; + } + + i++; + } + + return res; + }; + + return helper(); + } +} +``` + +```csharp +public class Solution { + private int i; + + public string DecodeString(string s) { + i = 0; + return Helper(s); + } + + private string Helper(string s) { + string res = ""; + int k = 0; + + while (i < s.Length) { + char c = s[i]; + + if (char.IsDigit(c)) { + k = k * 10 + (c - '0'); + } else if (c == '[') { + i++; + string inner = Helper(s); + res += new string(' ', 0).PadLeft(0); + for (int j = 0; j < k; j++) { + res += inner; + } + k = 0; + } else if (c == ']') { + return res; + } else { + res += c; + } + + i++; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + N)$ +* Space complexity: $O(n + N)$ + +> Where $n$ is the length of the input string and $N$ is the length of the output string. + +--- + +## 2. One Stack + +::tabs-start + +```python +class Solution: + def decodeString(self, s: str) -> str: + stack = [] + + for i in range(len(s)): + if s[i] != "]": + stack.append(s[i]) + else: + substr = "" + while stack[-1] != "[": + substr = stack.pop() + substr + stack.pop() + + k = "" + while stack and stack[-1].isdigit(): + k = stack.pop() + k + stack.append(int(k) * substr) + + return "".join(stack) +``` + +```java +public class Solution { + public String decodeString(String s) { + Stack stack = new Stack<>(); + + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) != ']') { + stack.push(String.valueOf(s.charAt(i))); + } else { + StringBuilder substr = new StringBuilder(); + while (!stack.peek().equals("[")) { + substr.insert(0, stack.pop()); + } + stack.pop(); + + StringBuilder k = new StringBuilder(); + while (!stack.isEmpty() && Character.isDigit(stack.peek().charAt(0))) { + k.insert(0, stack.pop()); + } + int count = Integer.parseInt(k.toString()); + String repeatedStr = substr.toString().repeat(count); + stack.push(repeatedStr); + } + } + + StringBuilder res = new StringBuilder(); + while (!stack.isEmpty()) { + res.insert(0, stack.pop()); + } + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string decodeString(string s) { + vector stack; + + for (char& c : s) { + if (c != ']') { + stack.push_back(string(1, c)); + } else { + string substr = ""; + while (stack.back() != "[") { + substr = stack.back() + substr; + stack.pop_back(); + } + stack.pop_back(); + + string k = ""; + while (!stack.empty() && isdigit(stack.back()[0])) { + k = stack.back() + k; + stack.pop_back(); + } + int repeatCount = stoi(k); + + string repeated = ""; + for (int i = 0; i < repeatCount; ++i) { + repeated += substr; + } + stack.push_back(repeated); + } + } + + string res = ""; + for (const string& part : stack) { + res += part; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + decodeString(s) { + const stack = []; + + for (let i = 0; i < s.length; i++) { + const char = s[i]; + + if (char !== "]") { + stack.push(char); + } else { + let substr = ""; + while (stack[stack.length - 1] !== "[") { + substr = stack.pop() + substr; + } + stack.pop(); + + let k = ""; + while (stack.length > 0 && !isNaN(stack[stack.length - 1])) { + k = stack.pop() + k; + } + stack.push(substr.repeat(parseInt(k, 10))); + } + } + + return stack.join(""); + } +} +``` + +```csharp +public class Solution { + public string DecodeString(string s) { + Stack stack = new Stack(); + + for (int i = 0; i < s.Length; i++) { + if (s[i] != ']') { + stack.Push(s[i].ToString()); + } else { + string substr = ""; + while (stack.Peek() != "[") { + substr = stack.Pop() + substr; + } + stack.Pop(); // remove '[' + + string k = ""; + while (stack.Count > 0 && char.IsDigit(stack.Peek()[0])) { + k = stack.Pop() + k; + } + + int repeat = int.Parse(k); + string expanded = new StringBuilder().Insert(0, substr, repeat).ToString(); + stack.Push(expanded); + } + } + + var result = new StringBuilder(); + foreach (string part in stack) { + result.Insert(0, part); + } + return result.ToString(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + N ^ 2)$ +* Space complexity: $O(n + N)$ + +> Where $n$ is the length of the input string and $N$ is the length of the output string. + +--- + +## 3. Two Stacks + +::tabs-start + +```python +class Solution: + def decodeString(self, s: str) -> str: + string_stack = [] + count_stack = [] + cur = "" + k = 0 + + for c in s: + if c.isdigit(): + k = k * 10 + int(c) + elif c == "[": + string_stack.append(cur) + count_stack.append(k) + cur = "" + k = 0 + elif c == "]": + temp = cur + cur = string_stack.pop() + count = count_stack.pop() + cur += temp * count + else: + cur += c + + return cur +``` + +```java +public class Solution { + public String decodeString(String s) { + Stack stringStack = new Stack<>(); + Stack countStack = new Stack<>(); + StringBuilder cur = new StringBuilder(); + int k = 0; + + for (char c : s.toCharArray()) { + if (Character.isDigit(c)) { + k = k * 10 + (c - '0'); + } else if (c == '[') { + stringStack.push(cur.toString()); + countStack.push(k); + cur = new StringBuilder(); + k = 0; + } else if (c == ']') { + String temp = cur.toString(); + cur = new StringBuilder(stringStack.pop()); + int count = countStack.pop(); + for (int i = 0; i < count; i++) { + cur.append(temp); + } + } else { + cur.append(c); + } + } + + return cur.toString(); + } +} +``` + +```cpp +class Solution { +public: + string decodeString(string s) { + vector stringStack; + vector countStack; + string cur = ""; + int k = 0; + + for (char c : s) { + if (isdigit(c)) { + k = k * 10 + (c - '0'); + } else if (c == '[') { + stringStack.push_back(cur); + countStack.push_back(k); + cur = ""; + k = 0; + } else if (c == ']') { + string temp = cur; + cur = stringStack.back(); + stringStack.pop_back(); + int count = countStack.back(); + countStack.pop_back(); + for (int i = 0; i < count; i++) { + cur += temp; + } + } else { + cur += c; + } + } + + return cur; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + decodeString(s) { + const stringStack = []; + const countStack = []; + let cur = ""; + let k = 0; + + for (const c of s) { + if (!isNaN(c)) { + k = k * 10 + parseInt(c, 10); + } else if (c === "[") { + stringStack.push(cur); + countStack.push(k); + cur = ""; + k = 0; + } else if (c === "]") { + const temp = cur; + cur = stringStack.pop(); + const count = countStack.pop(); + cur += temp.repeat(count); + } else { + cur += c; + } + } + + return cur; + } +} +``` + +```csharp +public class Solution { + public string DecodeString(string s) { + Stack stringStack = new Stack(); + Stack countStack = new Stack(); + string cur = ""; + int k = 0; + + foreach (char c in s) { + if (char.IsDigit(c)) { + k = k * 10 + (c - '0'); + } else if (c == '[') { + stringStack.Push(cur); + countStack.Push(k); + cur = ""; + k = 0; + } else if (c == ']') { + string temp = cur; + cur = stringStack.Pop(); + int count = countStack.Pop(); + for (int i = 0; i < count; i++) { + cur += temp; + } + } else { + cur += c; + } + } + + return cur; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + N)$ +* Space complexity: $O(n + N)$ + +> Where $n$ is the length of the input string and $N$ is the length of the output string. \ No newline at end of file diff --git a/articles/decode-ways.md b/articles/decode-ways.md new file mode 100644 index 000000000..c735d1d3f --- /dev/null +++ b/articles/decode-ways.md @@ -0,0 +1,824 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def numDecodings(self, s: str) -> int: + + def dfs(i): + if i == len(s): + return 1 + if s[i] == '0': + return 0 + + res = dfs(i + 1) + if i < len(s) - 1: + if (s[i] == '1' or + (s[i] == '2' and s[i + 1] < '7')): + res += dfs(i + 2) + + return res + + return dfs(0) +``` + +```java +public class Solution { + private int dfs(int i, String s) { + if (i == s.length()) return 1; + if (s.charAt(i) == '0') return 0; + + int res = dfs(i + 1, s); + if (i < s.length() - 1) { + if (s.charAt(i) == '1' || + (s.charAt(i) == '2' && s.charAt(i + 1) < '7')) { + res += dfs(i + 2, s); + } + } + return res; + } + + public int numDecodings(String s) { + return dfs(0, s); + } +} +``` + +```cpp +class Solution { +public: + int dfs(int i, string& s) { + if (i == s.size()) return 1; + if (s[i] == '0') return 0; + + int res = dfs(i + 1, s); + if (i < s.size() - 1) { + if (s[i] == '1' || + (s[i] == '2' && s[i + 1] < '7')) { + res += dfs(i + 2, s); + } + } + return res; + } + + int numDecodings(string s) { + return dfs(0, s); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + numDecodings(s) { + + const dfs = (i) => { + if (i === s.length) return 1; + if (s[i] === '0') return 0; + + let res = dfs(i + 1); + if (i < s.length - 1) { + if (s[i] === '1' || + (s[i] === '2' && s[i + 1] < '7')) { + res += dfs(i + 2); + } + } + return res; + }; + + return dfs(0); + } +} +``` + +```csharp +public class Solution { + public int NumDecodings(string s) { + int Dfs(int i) { + if (i == s.Length) return 1; + if (s[i] == '0') return 0; + + int res = Dfs(i + 1); + if (i < s.Length - 1) { + if (s[i] == '1' || + (s[i] == '2' && s[i + 1] < '7')) { + res += Dfs(i + 2); + } + } + return res; + } + + return Dfs(0); + } +} +``` + +```go +func numDecodings(s string) int { + return dfs(s, 0) +} + +func dfs(s string, i int) int { + if i == len(s) { + return 1 + } + if s[i] == '0' { + return 0 + } + res := dfs(s, i+1) + if i < len(s)-1 { + if s[i] == '1' || (s[i] == '2' && s[i+1] < '7') { + res += dfs(s, i+2) + } + } + return res +} +``` + +```kotlin +class Solution { + fun numDecodings(s: String): Int { + return dfs(s, 0) + } + + fun dfs(s: String, i: Int): Int { + if (i == s.length) { + return 1 + } + if (s[i] == '0') { + return 0 + } + var res = dfs(s, i + 1) + if (i < s.length - 1) { + if (s[i] == '1' || (s[i] == '2' && s[i + 1] < '7')) { + res += dfs(s, i + 2) + } + } + return res + } +} +``` + +```swift +class Solution { + func numDecodings(_ s: String) -> Int { + let chars = Array(s) + + func dfs(_ i: Int) -> Int { + if i == chars.count { + return 1 + } + if chars[i] == "0" { + return 0 + } + + var res = dfs(i + 1) + if i < chars.count - 1 { + if chars[i] == "1" || (chars[i] == "2" && chars[i + 1] < "7") { + res += dfs(i + 2) + } + } + + return res + } + + return dfs(0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def numDecodings(self, s: str) -> int: + dp = {len(s) : 1} + + def dfs(i): + if i in dp: + return dp[i] + if s[i] == "0": + return 0 + + res = dfs(i + 1) + if i + 1 < len(s) and ( + s[i] == "1" or s[i] == "2" and + s[i + 1] in "0123456" + ): + res += dfs(i + 2) + dp[i] = res + return res + + return dfs(0) +``` + +```java +public class Solution { + + public int numDecodings(String s) { + Map dp = new HashMap<>(); + dp.put(s.length(), 1); + + return dfs(s, 0, dp); + } + + private int dfs(String s, int i, Map dp) { + if (dp.containsKey(i)) { + return dp.get(i); + } + if (s.charAt(i) == '0') { + return 0; + } + + int res = dfs(s, i + 1, dp); + if (i + 1 < s.length() && (s.charAt(i) == '1' || + s.charAt(i) == '2' && s.charAt(i + 1) < '7')) { + res += dfs(s, i + 2, dp); + } + dp.put(i, res); + return res; + } +} +``` + +```cpp +class Solution { +public: + int numDecodings(string s) { + unordered_map dp; + dp[s.size()] = 1; + return dfs(s, 0, dp); + } + +private: + int dfs(string s, int i, unordered_map& dp) { + if (dp.count(i)) { + return dp[i]; + } + if (s[i] == '0') { + return 0; + } + + int res = dfs(s, i + 1, dp); + if (i + 1 < s.size() && (s[i] == '1' || + s[i] == '2' && s[i + 1] < '7')) { + res += dfs(s, i + 2, dp); + } + dp[i] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + numDecodings(s) { + const dp = new Map(); + dp.set(s.length, 1); + + return this.dfs(s, 0, dp); + } + + /** + * @param {string} s + * @param {number} i + * @param {Map} dp + * @return {number} + */ + dfs(s, i, dp) { + if (dp.has(i)) { + return dp.get(i); + } + if (s.charAt(i) === '0') { + return 0; + } + + let res = this.dfs(s, i + 1, dp); + if (i + 1 < s.length && (s.charAt(i) === '1' || + (s.charAt(i) === '2' && s.charAt(i + 1) < '7'))) { + res += this.dfs(s, i + 2, dp); + } + dp.set(i, res); + return res; + } +} +``` + +```csharp +public class Solution { + + public int NumDecodings(string s) { + Dictionary dp = new Dictionary(); + dp[s.Length] = 1; + return Dfs(s, 0, dp); + } + + private int Dfs(string s, int i, Dictionary dp) { + if (dp.ContainsKey(i)) { + return dp[i]; + } + if (s[i] == '0') { + return 0; + } + + int res = Dfs(s, i + 1, dp); + if (i + 1 < s.Length && (s[i] == '1' || + s[i] == '2' && s[i + 1] < '7')) { + res += Dfs(s, i + 2, dp); + } + dp[i] = res; + return res; + } +} +``` + +```go +func numDecodings(s string) int { + return dfs(s, 0, map[int]int{len(s): 1}) +} + +func dfs(s string, i int, dp map[int]int) int { + if val, ok := dp[i]; ok { + return val + } + if i == len(s) { + return 1 + } + if s[i] == '0' { + return 0 + } + res := dfs(s, i+1, dp) + if i+1 < len(s) && (s[i] == '1' || + (s[i] == '2' && s[i+1] <= '6')) { + res += dfs(s, i+2, dp) + } + dp[i] = res + return res +} +``` + +```kotlin +class Solution { + fun numDecodings(s: String): Int { + return dfs(s, 0, hashMapOf(s.length to 1)) + } + + fun dfs(s: String, i: Int, dp: HashMap): Int { + if (i in dp) { + return dp[i]!! + } + if (i == s.length) { + return 1 + } + if (s[i] == '0') { + return 0 + } + var res = dfs(s, i + 1, dp) + if (i + 1 < s.length && (s[i] == '1' || + (s[i] == '2' && s[i + 1] <= '6'))) { + res += dfs(s, i + 2, dp) + } + dp[i] = res + return res + } +} +``` + +```swift +class Solution { + func numDecodings(_ s: String) -> Int { + let chars = Array(s) + var dp = [Int: Int]() + dp[chars.count] = 1 + + func dfs(_ i: Int) -> Int { + if let cached = dp[i] { + return cached + } + if chars[i] == "0" { + return 0 + } + + var res = dfs(i + 1) + if i + 1 < chars.count, + chars[i] == "1" || (chars[i] == "2" && "0123456".contains(chars[i + 1])) { + res += dfs(i + 2) + } + dp[i] = res + return res + } + + return dfs(0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def numDecodings(self, s: str) -> int: + dp = {len(s): 1} + for i in range(len(s) - 1, -1, -1): + if s[i] == "0": + dp[i] = 0 + else: + dp[i] = dp[i + 1] + + if i + 1 < len(s) and (s[i] == "1" or + s[i] == "2" and s[i + 1] in "0123456" + ): + dp[i] += dp[i + 2] + return dp[0] +``` + +```java +public class Solution { + public int numDecodings(String s) { + int[] dp = new int[s.length() + 1]; + dp[s.length()] = 1; + for (int i = s.length() - 1; i >= 0; i--) { + if (s.charAt(i) == '0') { + dp[i] = 0; + } else { + dp[i] = dp[i + 1]; + if (i + 1 < s.length() && (s.charAt(i) == '1' || + s.charAt(i) == '2' && s.charAt(i + 1) < '7')) { + dp[i] += dp[i + 2]; + } + } + } + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int numDecodings(string s) { + vector dp(s.size() + 1); + dp[s.size()] = 1; + for (int i = s.size() - 1; i >= 0; i--) { + if (s[i] == '0') { + dp[i] = 0; + } else { + dp[i] = dp[i + 1]; + if (i + 1 < s.size() && (s[i] == '1' || + s[i] == '2' && s[i + 1] < '7')) { + dp[i] += dp[i + 2]; + } + } + } + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + numDecodings(s) { + let dp = new Array(s.length + 1).fill(0); + dp[s.length] = 1; + for (let i = s.length - 1; i >= 0; i--) { + if (s.charAt(i) === '0') { + dp[i] = 0; + } else { + dp[i] = dp[i + 1]; + if (i + 1 < s.length && (s.charAt(i) === '1' || + (s.charAt(i) === '2' && s.charAt(i + 1) < '7'))) { + dp[i] += dp[i + 2]; + } + } + } + return dp[0]; + } +} +``` + +```csharp +public class Solution { + public int NumDecodings(string s) { + int[] dp = new int[s.Length + 1]; + dp[s.Length] = 1; + for (int i = s.Length - 1; i >= 0; i--) { + if (s[i] == '0') { + dp[i] = 0; + } else { + dp[i] = dp[i + 1]; + if (i + 1 < s.Length && (s[i] == '1' || + s[i] == '2' && s[i + 1] < '7')) { + dp[i] += dp[i + 2]; + } + } + } + return dp[0]; + } +} +``` + +```go +func numDecodings(s string) int { + dp := make(map[int]int) + dp[len(s)] = 1 + for i := len(s) - 1; i >= 0; i-- { + if s[i] == '0' { + dp[i] = 0 + } else { + dp[i] = dp[i+1] + if i+1 < len(s) && (s[i] == '1' || + (s[i] == '2' && s[i+1] <= '6')) { + dp[i] += dp[i+2] + } + } + } + return dp[0] +} +``` + +```kotlin +class Solution { + fun numDecodings(s: String): Int { + val dp = mutableMapOf(s.length to 1) + for (i in s.length - 1 downTo 0) { + if (s[i] == '0') { + dp[i] = 0 + } else { + dp[i] = dp[i + 1] ?: 0 + if (i + 1 < s.length && (s[i] == '1' || + (s[i] == '2' && s[i + 1] <= '6'))) { + dp[i] = dp[i]!! + (dp[i + 2] ?: 0) + } + } + } + return dp[0] ?: 0 + } +} +``` + +```swift +class Solution { + func numDecodings(_ s: String) -> Int { + let chars = Array(s) + var dp = [Int: Int]() + dp[chars.count] = 1 + + for i in stride(from: chars.count - 1, through: 0, by: -1) { + if chars[i] == "0" { + dp[i] = 0 + } else { + dp[i] = dp[i + 1] ?? 0 + } + + if i + 1 < chars.count, + chars[i] == "1" || (chars[i] == "2" && "0123456".contains(chars[i + 1])) { + dp[i]! += dp[i + 2] ?? 0 + } + } + return dp[0] ?? 0 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def numDecodings(self, s: str) -> int: + dp = dp2 = 0 + dp1 = 1 + for i in range(len(s) - 1, -1, -1): + if s[i] == "0": + dp = 0 + else: + dp = dp1 + + if i + 1 < len(s) and (s[i] == "1" or + s[i] == "2" and s[i + 1] in "0123456" + ): + dp += dp2 + dp, dp1, dp2 = 0, dp, dp1 + return dp1 +``` + +```java +public class Solution { + public int numDecodings(String s) { + int dp = 0, dp2 = 0; + int dp1 = 1; + for (int i = s.length() - 1; i >= 0; i--) { + if (s.charAt(i) == '0') { + dp = 0; + } else { + dp = dp1; + if (i + 1 < s.length() && (s.charAt(i) == '1' || + s.charAt(i) == '2' && s.charAt(i + 1) < '7')) { + dp += dp2; + } + } + dp2 = dp1; + dp1 = dp; + dp = 0; + } + return dp1; + } +} +``` + +```cpp +class Solution { +public: + int numDecodings(string s) { + int dp = 0, dp2 = 0; + int dp1 = 1; + for (int i = s.size() - 1; i >= 0; i--) { + if (s[i] == '0') { + dp = 0; + } else { + dp = dp1; + if (i + 1 < s.size() && (s[i] == '1' || + s[i] == '2' && s[i + 1] < '7')) { + dp += dp2; + } + } + dp2 = dp1; + dp1 = dp; + dp = 0; + } + return dp1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + numDecodings(s) { + let dp2 = 0, dp = 0; + let dp1 = 1; + for (let i = s.length - 1; i >= 0; i--) { + if (s.charAt(i) === '0') { + dp = 0; + } else { + dp = dp1; + if (i + 1 < s.length && (s.charAt(i) === '1' || + (s.charAt(i) === '2' && s.charAt(i + 1) < '7'))) { + dp += dp2; + } + } + dp2 = dp1; + dp1 = dp; + dp = 0; + } + return dp1; + } +} +``` + +```csharp +public class Solution { + public int NumDecodings(string s) { + int dp = 0, dp1 = 1, dp2 = 0; + for (int i = s.Length - 1; i >= 0; i--) { + if (s[i] == '0') { + dp = 0; + } else { + dp = dp1; + if (i + 1 < s.Length && (s[i] == '1' || + s[i] == '2' && s[i + 1] < '7')) { + dp += dp2; + } + } + dp2 = dp1; + dp1 = dp; + dp = 0; + } + return dp1; + } +} +``` + +```go +func numDecodings(s string) int { + dp, dp2 := 0, 0 + dp1 := 1 + for i := len(s) - 1; i >= 0; i-- { + if s[i] == '0' { + dp = 0 + } else { + dp = dp1 + } + if i+1 < len(s) && (s[i] == '1' || + s[i] == '2' && s[i+1] <= '6') { + dp += dp2 + } + dp2 = dp1 + dp1 = dp + dp = 0 + } + return dp1 +} +``` + +```kotlin +class Solution { + fun numDecodings(s: String): Int { + var dp = 0 + var dp2 = 0 + var dp1 = 1 + for (i in s.length - 1 downTo 0) { + if (s[i] == '0') { + dp = 0 + } else { + dp = dp1 + } + if (i + 1 < s.length && (s[i] == '1' || + (s[i] == '2' && s[i + 1] <= '6'))) { + dp += dp2 + } + dp2 = dp1 + dp1 = dp + } + return dp1 + } +} +``` + +```swift +class Solution { + func numDecodings(_ s: String) -> Int { + let chars = Array(s) + var dp = 0, dp1 = 1, dp2 = 0 + + for i in stride(from: chars.count - 1, through: 0, by: -1) { + if chars[i] == "0" { + dp = 0 + } else { + dp = dp1 + } + + if i + 1 < chars.count, + chars[i] == "1" || (chars[i] == "2" && "0123456".contains(chars[i + 1])) { + dp += dp2 + } + (dp, dp1, dp2) = (0, dp, dp1) + } + return dp1 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/delete-and-earn.md b/articles/delete-and-earn.md new file mode 100644 index 000000000..ae00ea5db --- /dev/null +++ b/articles/delete-and-earn.md @@ -0,0 +1,586 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def deleteAndEarn(self, nums: List[int]) -> int: + nums.sort() + + def dfs(i): + if i >= len(nums): + return 0 + + cur = nums[i] + pick = 0 + while i < len(nums) and nums[i] == cur: + pick += nums[i] + i += 1 + + res = dfs(i) + while i < len(nums) and nums[i] == 1 + cur: + i += 1 + + res = max(res, pick + dfs(i)) + return res + + return dfs(0) +``` + +```java +public class Solution { + public int deleteAndEarn(int[] nums) { + Arrays.sort(nums); + return dfs(nums, 0); + } + + private int dfs(int[] nums, int i) { + if (i >= nums.length) return 0; + + int cur = nums[i], pick = 0; + while (i < nums.length && nums[i] == cur) { + pick += nums[i]; + i++; + } + + int res = dfs(nums, i); + while (i < nums.length && nums[i] == cur + 1) { + i++; + } + + res = Math.max(res, pick + dfs(nums, i)); + return res; + } +} +``` + +```cpp +class Solution { +public: + int deleteAndEarn(vector& nums) { + sort(nums.begin(), nums.end()); + return dfs(nums, 0); + } + +private: + int dfs(const vector& nums, int i) { + if (i >= nums.size()) return 0; + + int cur = nums[i], pick = 0; + while (i < nums.size() && nums[i] == cur) { + pick += nums[i]; + i++; + } + + int res = dfs(nums, i); + while (i < nums.size() && nums[i] == cur + 1) { + i++; + } + + res = max(res, pick + dfs(nums, i)); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + deleteAndEarn(nums) { + nums.sort((a, b) => a - b); + + const dfs = (i) => { + if (i >= nums.length) return 0; + + let cur = nums[i], pick = 0; + while (i < nums.length && nums[i] === cur) { + pick += nums[i]; + i++; + } + + let res = dfs(i); + while (i < nums.length && nums[i] === cur + 1) { + i++; + } + + res = Math.max(res, pick + dfs(i)); + return res; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def deleteAndEarn(self, nums: List[int]) -> int: + val = defaultdict(int) + for num in nums: + val[num] += num + nums = sorted(list(set(nums))) + memo = [-1] * len(nums) + + def dfs(i): + if i >= len(nums): + return 0 + if memo[i] != -1: + return memo[i] + + res = val[nums[i]] + if i + 1 < len(nums) and nums[i] + 1 == nums[i + 1]: + res += dfs(i + 2) + else: + res += dfs(i + 1) + + res = max(res, dfs(i + 1)) + memo[i] = res + return res + + return dfs(0) +``` + +```java +public class Solution { + private Map val; + private int[] memo; + + public int deleteAndEarn(int[] nums) { + val = new HashMap<>(); + for (int num : nums) { + val.put(num, val.getOrDefault(num, 0) + num); + } + + List uniqueNums = new ArrayList<>(val.keySet()); + Collections.sort(uniqueNums); + memo = new int[uniqueNums.size()]; + Arrays.fill(memo, -1); + + return dfs(uniqueNums, 0); + } + + private int dfs(List nums, int i) { + if (i >= nums.size()) return 0; + if (memo[i] != -1) return memo[i]; + + int res = val.get(nums.get(i)); + if (i + 1 < nums.size() && nums.get(i) + 1 == nums.get(i + 1)) { + res += dfs(nums, i + 2); + } else { + res += dfs(nums, i + 1); + } + + res = Math.max(res, dfs(nums, i + 1)); + memo[i] = res; + return res; + } +} +``` + +```cpp +class Solution { + unordered_map val; + vector memo; + +public: + int deleteAndEarn(vector& nums) { + for (int num : nums) { + val[num] += num; + } + + vector uniqueNums; + for (auto& pair : val) { + uniqueNums.push_back(pair.first); + } + sort(uniqueNums.begin(), uniqueNums.end()); + memo.resize(uniqueNums.size(), -1); + + return dfs(uniqueNums, 0); + } + +private: + int dfs(vector& nums, int i) { + if (i >= nums.size()) return 0; + if (memo[i] != -1) return memo[i]; + + int res = val[nums[i]]; + if (i + 1 < nums.size() && nums[i] + 1 == nums[i + 1]) { + res += dfs(nums, i + 2); + } else { + res += dfs(nums, i + 1); + } + + res = max(res, dfs(nums, i + 1)); + memo[i] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + deleteAndEarn(nums) { + const val = new Map(); + nums.forEach(num => { + val.set(num, (val.get(num) || 0) + num); + }); + const uniqueNums = Array.from(new Set(nums)).sort((a, b) => a - b); + const memo = Array(uniqueNums.length).fill(-1); + + const dfs = (nums, i) => { + if (i >= nums.length) return 0; + if (memo[i] !== -1) return memo[i]; + + let res = val.get(nums[i]); + if (i + 1 < nums.length && nums[i] + 1 === nums[i + 1]) { + res += dfs(nums, i + 2); + } else { + res += dfs(nums, i + 1); + } + + res = Math.max(res, dfs(nums, i + 1)); + memo[i] = res; + return res; + }; + + return dfs(uniqueNums, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) - I + +::tabs-start + +```python +class Solution: + def deleteAndEarn(self, nums: List[int]) -> int: + val = defaultdict(int) + for num in nums: + val[num] += num + nums = sorted(list(set(nums))) + + dp = [0] * (len(nums) + 1) + for i in range(len(nums) - 1, -1, -1): + take = val[nums[i]] + if i + 1 < len(nums) and nums[i + 1] == nums[i] + 1: + take += dp[i + 2] + else: + take += dp[i + 1] + dp[i] = max(dp[i + 1], take) + + return dp[0] +``` + +```java +public class Solution { + public int deleteAndEarn(int[] nums) { + Map val = new HashMap<>(); + for (int num : nums) val.put(num, val.getOrDefault(num, 0) + num); + List sortedNums = new ArrayList<>(val.keySet()); + Collections.sort(sortedNums); + + int[] dp = new int[sortedNums.size() + 1]; + for (int i = sortedNums.size() - 1; i >= 0; i--) { + int take = val.get(sortedNums.get(i)); + if (i + 1 < sortedNums.size() && sortedNums.get(i + 1) == sortedNums.get(i) + 1) { + take += dp[i + 2]; + } else { + take += dp[i + 1]; + } + dp[i] = Math.max(dp[i + 1], take); + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int deleteAndEarn(vector& nums) { + unordered_map val; + for (int num : nums) val[num] += num; + vector sortedNums; + for (auto& [key, _] : val) sortedNums.push_back(key); + sort(sortedNums.begin(), sortedNums.end()); + + vector dp(sortedNums.size() + 1); + for (int i = sortedNums.size() - 1; i >= 0; i--) { + int take = val[sortedNums[i]]; + if (i + 1 < sortedNums.size() && sortedNums[i + 1] == sortedNums[i] + 1) { + take += dp[i + 2]; + } else { + take += dp[i + 1]; + } + dp[i] = max(dp[i + 1], take); + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + deleteAndEarn(nums) { + const val = new Map(); + nums.forEach(num => val.set(num, (val.get(num) || 0) + num)); + const sortedNums = Array.from(val.keys()).sort((a, b) => a - b); + + const dp = Array(sortedNums.length + 1).fill(0); + for (let i = sortedNums.length - 1; i >= 0; i--) { + let take = val.get(sortedNums[i]); + if (i + 1 < sortedNums.length && sortedNums[i + 1] === sortedNums[i] + 1) { + take += dp[i + 2]; + } else { + take += dp[i + 1]; + } + dp[i] = Math.max(dp[i + 1], take); + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Bottom-Up) - II + +::tabs-start + +```python +class Solution: + def deleteAndEarn(self, nums: List[int]) -> int: + m = max(nums) + dp = [0] * (m + 2) + + for num in nums: + dp[num] += num + for i in range(m - 1, 0, -1): + dp[i] = max(dp[i + 1], dp[i + 2] + dp[i]) + + return dp[1] +``` + +```java +public class Solution { + public int deleteAndEarn(int[] nums) { + int m = 0; + for (int num : nums) m = Math.max(m, num); + + int[] dp = new int[m + 2]; + for (int num : nums) dp[num] += num; + for (int i = m - 1; i > 0; i--) { + dp[i] = Math.max(dp[i + 1], dp[i + 2] + dp[i]); + } + return dp[1]; + } +} +``` + +```cpp +class Solution { +public: + int deleteAndEarn(vector& nums) { + int m = *max_element(nums.begin(), nums.end()); + vector dp(m + 2); + for (auto& num : nums) { + dp[num] += num; + } + + for (int i = m - 1; i > 0; i--) { + dp[i] = max(dp[i + 1], dp[i + 2] + dp[i]); + } + return dp[1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + deleteAndEarn(nums) { + const m = Math.max(...nums); + const dp = new Int32Array(m + 2); + for (let num of nums) { + dp[num] += num; + } + + for (let i = m - 1; i > 0; i--) { + dp[i] = Math.max(dp[i + 1], dp[i + 2] + dp[i]); + } + return dp[1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(m)$ + +> Where $m$ is the maximum element in the array and $n$ is the size of the array. + +--- + +## 5. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def deleteAndEarn(self, nums: List[int]) -> int: + count = Counter(nums) + nums = sorted(list(set(nums))) + + earn1, earn2 = 0, 0 + for i in range(len(nums)): + curEarn = nums[i] * count[nums[i]] + if i > 0 and nums[i] == nums[i - 1] + 1: + temp = earn2 + earn2 = max(curEarn + earn1, earn2) + earn1 = temp + else: + temp = earn2 + earn2 = curEarn + earn2 + earn1 = temp + return earn2 +``` + +```java +public class Solution { + public int deleteAndEarn(int[] nums) { + Map count = new HashMap<>(); + for (int num : nums) count.put(num, count.getOrDefault(num, 0) + num); + List uniqueNums = new ArrayList<>(count.keySet()); + Collections.sort(uniqueNums); + + int earn1 = 0, earn2 = 0; + for (int i = 0; i < uniqueNums.size(); i++) { + int curEarn = count.get(uniqueNums.get(i)); + if (i > 0 && uniqueNums.get(i) == uniqueNums.get(i - 1) + 1) { + int temp = earn2; + earn2 = Math.max(curEarn + earn1, earn2); + earn1 = temp; + } else { + int temp = earn2; + earn2 = curEarn + earn2; + earn1 = temp; + } + } + return earn2; + } +} +``` + +```cpp +class Solution { +public: + int deleteAndEarn(vector& nums) { + unordered_map count; + for (int num : nums) count[num] += num; + vector uniqueNums; + for (auto& pair : count) uniqueNums.push_back(pair.first); + sort(uniqueNums.begin(), uniqueNums.end()); + + int earn1 = 0, earn2 = 0; + for (int i = 0; i < uniqueNums.size(); i++) { + int curEarn = count[uniqueNums[i]]; + if (i > 0 && uniqueNums[i] == uniqueNums[i - 1] + 1) { + int temp = earn2; + earn2 = max(curEarn + earn1, earn2); + earn1 = temp; + } else { + int temp = earn2; + earn2 = curEarn + earn2; + earn1 = temp; + } + } + return earn2; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + deleteAndEarn(nums) { + const count = new Map(); + nums.forEach(num => count.set(num, (count.get(num) || 0) + num)); + const uniqueNums = [...count.keys()].sort((a, b) => a - b); + + let earn1 = 0, earn2 = 0; + for (let i = 0; i < uniqueNums.length; i++) { + const curEarn = count.get(uniqueNums[i]); + if (i > 0 && uniqueNums[i] === uniqueNums[i - 1] + 1) { + const temp = earn2; + earn2 = Math.max(curEarn + earn1, earn2); + earn1 = temp; + } else { + const temp = earn2; + earn2 = curEarn + earn2; + earn1 = temp; + } + } + return earn2; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/delete-leaves-with-a-given-value.md b/articles/delete-leaves-with-a-given-value.md new file mode 100644 index 000000000..add3a0494 --- /dev/null +++ b/articles/delete-leaves-with-a-given-value.md @@ -0,0 +1,719 @@ +## 1. Recursion (Postorder Traversal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def removeLeafNodes(self, root: Optional[TreeNode], target: int) -> Optional[TreeNode]: + if not root: + return None + + root.left = self.removeLeafNodes(root.left, target) + root.right = self.removeLeafNodes(root.right, target) + + if not root.left and not root.right and root.val == target: + return None + + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode removeLeafNodes(TreeNode root, int target) { + if (root == null) { + return null; + } + + root.left = removeLeafNodes(root.left, target); + root.right = removeLeafNodes(root.right, target); + + if (root.left == null && root.right == null && root.val == target) { + return null; + } + + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* removeLeafNodes(TreeNode* root, int target) { + if (!root) { + return nullptr; + } + + root->left = removeLeafNodes(root->left, target); + root->right = removeLeafNodes(root->right, target); + + if (!root->left && !root->right && root->val == target) { + return nullptr; + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} target + * @return {TreeNode} + */ + removeLeafNodes(root, target) { + if (!root) { + return null; + } + + root.left = this.removeLeafNodes(root.left, target); + root.right = this.removeLeafNodes(root.right, target); + + if (!root.left && !root.right && root.val === target) { + return null; + } + + return root; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode RemoveLeafNodes(TreeNode root, int target) { + if (root == null) return null; + + root.left = RemoveLeafNodes(root.left, target); + root.right = RemoveLeafNodes(root.right, target); + + if (root.left == null && root.right == null && root.val == target) { + return null; + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Iterative Postorder Traversal + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def removeLeafNodes(self, root: Optional[TreeNode], target: int) -> Optional[TreeNode]: + stack = [root] + visit = set() + parents = {root: None} + + while stack: + node = stack.pop() + if not node.left and not node.right: + if node.val == target: + p = parents[node] + if not p: + return None + if p.left == node: + p.left = None + if p.right == node: + p.right = None + elif node not in visit: + visit.add(node) + stack.append(node) + if node.left: + stack.append(node.left) + parents[node.left] = node + if node.right: + stack.append(node.right) + parents[node.right] = node + + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode removeLeafNodes(TreeNode root, int target) { + Stack stack = new Stack<>(); + Set visit = new HashSet<>(); + Map parents = new HashMap<>(); + parents.put(root, null); + stack.push(root); + + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + if (node.left == null && node.right == null) { + if (node.val == target) { + TreeNode p = parents.get(node); + if (p == null) { + return null; + } + if (p.left == node) { + p.left = null; + } + if (p.right == node) { + p.right = null; + } + } + } else if (!visit.contains(node)) { + visit.add(node); + stack.push(node); + if (node.left != null) { + stack.push(node.left); + parents.put(node.left, node); + } + if (node.right != null) { + stack.push(node.right); + parents.put(node.right, node); + } + } + } + + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* removeLeafNodes(TreeNode* root, int target) { + stack stack; + unordered_set visit; + unordered_map parents; + parents[root] = nullptr; + stack.push(root); + + while (!stack.empty()) { + TreeNode* node = stack.top(); + stack.pop(); + if (!node->left && !node->right) { + if (node->val == target) { + TreeNode* p = parents[node]; + if (!p) { + return nullptr; + } + if (p->left == node) { + p->left = nullptr; + } + if (p->right == node) { + p->right = nullptr; + } + } + } else if (visit.find(node) == visit.end()) { + visit.insert(node); + stack.push(node); + if (node->left) { + stack.push(node->left); + parents[node->left] = node; + } + if (node->right) { + stack.push(node->right); + parents[node->right] = node; + } + } + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} target + * @return {TreeNode} + */ + removeLeafNodes(root, target) { + const stack = [root]; + const visit = new Set(); + const parents = new Map(); + parents.set(root, null); + + while (stack.length > 0) { + const node = stack.pop(); + if (!node.left && !node.right) { + if (node.val === target) { + const p = parents.get(node); + if (!p) { + return null; + } + if (p.left === node) { + p.left = null; + } + if (p.right === node) { + p.right = null; + } + } + } else if (!visit.has(node)) { + visit.add(node); + stack.push(node); + if (node.left) { + stack.push(node.left); + parents.set(node.left, node); + } + if (node.right) { + stack.push(node.right); + parents.set(node.right, node); + } + } + } + + return root; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode RemoveLeafNodes(TreeNode root, int target) { + if (root == null) return null; + + Stack stack = new Stack(); + HashSet visited = new HashSet(); + Dictionary parents = new Dictionary(); + + stack.Push(root); + parents[root] = null; + + while (stack.Count > 0) { + TreeNode node = stack.Pop(); + + if (node.left == null && node.right == null) { + if (node.val == target) { + TreeNode parent = parents[node]; + if (parent == null) { + return null; + } + if (parent.left == node) parent.left = null; + if (parent.right == node) parent.right = null; + } + } else if (!visited.Contains(node)) { + visited.Add(node); + stack.Push(node); + if (node.left != null) { + stack.Push(node.left); + parents[node.left] = node; + } + if (node.right != null) { + stack.Push(node.right); + parents[node.right] = node; + } + } + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Iterative Postorder Traversal (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def removeLeafNodes(self, root: Optional[TreeNode], target: int) -> Optional[TreeNode]: + if not root: + return None + + stack = [] + cur = root + visited = None + + while stack or cur: + while cur: + stack.append(cur) + cur = cur.left + + cur = stack[-1] + if cur.right and cur.right != visited: + cur = cur.right + continue + + stack.pop() + if not cur.left and not cur.right and cur.val == target: + if not stack: + return None + + parent = stack[-1] + if parent.left == cur: + parent.left = None + elif parent.right == cur: + parent.right = None + else: + visited = cur + + cur = None + + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode removeLeafNodes(TreeNode root, int target) { + if (root == null) return null; + + Stack stack = new Stack<>(); + TreeNode cur = root, visited = null; + + while (!stack.isEmpty() || cur != null) { + while (cur != null) { + stack.push(cur); + cur = cur.left; + } + + cur = stack.peek(); + if (cur.right != null && cur.right != visited) { + cur = cur.right; + continue; + } + + stack.pop(); + if (cur.left == null && cur.right == null && cur.val == target) { + if (stack.isEmpty()) return null; + + TreeNode parent = stack.peek(); + if (parent.left == cur) { + parent.left = null; + } else if (parent.right == cur) { + parent.right = null; + } + } else { + visited = cur; + } + + cur = null; + } + + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* removeLeafNodes(TreeNode* root, int target) { + if (!root) return nullptr; + + stack stack; + TreeNode* cur = root; + TreeNode* visited = nullptr; + + while (!stack.empty() || cur) { + while (cur) { + stack.push(cur); + cur = cur->left; + } + + cur = stack.top(); + if (cur->right && cur->right != visited) { + cur = cur->right; + continue; + } + + stack.pop(); + if (!cur->left && !cur->right && cur->val == target) { + if (stack.empty()) return nullptr; + + TreeNode* parent = stack.top(); + if (parent->left == cur) { + parent->left = nullptr; + } else if (parent->right == cur) { + parent->right = nullptr; + } + } else { + visited = cur; + } + + cur = nullptr; + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} target + * @return {TreeNode} + */ + removeLeafNodes(root, target) { + if (!root) return null; + + const stack = []; + let cur = root, visited = null; + + while (stack.length > 0 || cur !== null) { + while (cur !== null) { + stack.push(cur); + cur = cur.left; + } + + cur = stack[stack.length - 1]; + if (cur.right && cur.right !== visited) { + cur = cur.right; + continue; + } + + stack.pop(); + if (!cur.left && !cur.right && cur.val === target) { + if (stack.length === 0) return null; + + const parent = stack[stack.length - 1]; + if (parent.left === cur) { + parent.left = null; + } else if (parent.right === cur) { + parent.right = null; + } + } else { + visited = cur; + } + + cur = null; + } + + return root; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode RemoveLeafNodes(TreeNode root, int target) { + if (root == null) return null; + + Stack stack = new Stack(); + TreeNode cur = root; + TreeNode visited = null; + + while (stack.Count > 0 || cur != null) { + while (cur != null) { + stack.Push(cur); + cur = cur.left; + } + + cur = stack.Peek(); + if (cur.right != null && cur.right != visited) { + cur = cur.right; + continue; + } + + stack.Pop(); + if (cur.left == null && cur.right == null && cur.val == target) { + if (stack.Count == 0) return null; + + TreeNode parent = stack.Peek(); + if (parent.left == cur) { + parent.left = null; + } else if (parent.right == cur) { + parent.right = null; + } + } else { + visited = cur; + } + + cur = null; + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/delete-node-in-a-bst.md b/articles/delete-node-in-a-bst.md new file mode 100644 index 000000000..0a9617193 --- /dev/null +++ b/articles/delete-node-in-a-bst.md @@ -0,0 +1,790 @@ +## 1. Recursion - I + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]: + if not root: + return root + + if key > root.val: + root.right = self.deleteNode(root.right, key) + elif key < root.val: + root.left = self.deleteNode(root.left, key) + else: + if not root.left: + return root.right + elif not root.right: + return root.left + + cur = root.right + while cur.left: + cur = cur.left + root.val = cur.val + root.right = self.deleteNode(root.right, root.val) + + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode deleteNode(TreeNode root, int key) { + if (root == null) return root; + + if (key > root.val) { + root.right = deleteNode(root.right, key); + } else if (key < root.val) { + root.left = deleteNode(root.left, key); + } else { + if (root.left == null) return root.right; + if (root.right == null) return root.left; + + TreeNode cur = root.right; + while (cur.left != null) { + cur = cur.left; + } + root.val = cur.val; + root.right = deleteNode(root.right, root.val); + } + + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* deleteNode(TreeNode* root, int key) { + if (!root) return root; + + if (key > root->val) { + root->right = deleteNode(root->right, key); + } else if (key < root->val) { + root->left = deleteNode(root->left, key); + } else { + if (!root->left) return root->right; + if (!root->right) return root->left; + + TreeNode* cur = root->right; + while (cur->left) { + cur = cur->left; + } + root->val = cur->val; + root->right = deleteNode(root->right, root->val); + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} key + * @return {TreeNode} + */ + deleteNode(root, key) { + if (!root) return root; + + if (key > root.val) { + root.right = this.deleteNode(root.right, key); + } else if (key < root.val) { + root.left = this.deleteNode(root.left, key); + } else { + if (!root.left) return root.right; + if (!root.right) return root.left; + + let cur = root.right; + while (cur.left) { + cur = cur.left; + } + root.val = cur.val; + root.right = this.deleteNode(root.right, root.val); + } + + return root; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode DeleteNode(TreeNode root, int key) { + if (root == null) return null; + + if (key > root.val) { + root.right = DeleteNode(root.right, key); + } else if (key < root.val) { + root.left = DeleteNode(root.left, key); + } else { + if (root.left == null) return root.right; + if (root.right == null) return root.left; + + TreeNode cur = root.right; + while (cur.left != null) { + cur = cur.left; + } + root.val = cur.val; + root.right = DeleteNode(root.right, root.val); + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(h)$ +* Space complexity: $O(h)$ for the recursion stack. + +> Where $h$ is the height of the given binary search tree. + +--- + +## 2. Recursion - II + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]: + if not root: + return root + + if key > root.val: + root.right = self.deleteNode(root.right, key) + elif key < root.val: + root.left = self.deleteNode(root.left, key) + else: + if not root.left: + return root.right + elif not root.right: + return root.left + + cur = root.right + while cur.left: + cur = cur.left + cur.left = root.left + res = root.right + del root + return res + + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode deleteNode(TreeNode root, int key) { + if (root == null) return root; + + if (key > root.val) { + root.right = deleteNode(root.right, key); + } else if (key < root.val) { + root.left = deleteNode(root.left, key); + } else { + if (root.left == null) return root.right; + if (root.right == null) return root.left; + + TreeNode cur = root.right; + while (cur.left != null) { + cur = cur.left; + } + cur.left = root.left; + TreeNode res = root.right; + root = null; + return res; + } + + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* deleteNode(TreeNode* root, int key) { + if (!root) return root; + + if (key > root->val) { + root->right = deleteNode(root->right, key); + } else if (key < root->val) { + root->left = deleteNode(root->left, key); + } else { + if (!root->left) return root->right; + if (!root->right) return root->left; + + TreeNode* cur = root->right; + while (cur->left) { + cur = cur->left; + } + cur->left = root->left; + TreeNode* res = root->right; + delete root; + return res; + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} key + * @return {TreeNode} + */ + deleteNode(root, key) { + if (!root) return root; + + if (key > root.val) { + root.right = this.deleteNode(root.right, key); + } else if (key < root.val) { + root.left = this.deleteNode(root.left, key); + } else { + if (!root.left) return root.right; + if (!root.right) return root.left; + + let cur = root.right; + while (cur.left) { + cur = cur.left; + } + cur.left = root.left; + let res = root.right; + root = null; + return res; + } + + return root; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode DeleteNode(TreeNode root, int key) { + if (root == null) return null; + + if (key > root.val) { + root.right = DeleteNode(root.right, key); + } else if (key < root.val) { + root.left = DeleteNode(root.left, key); + } else { + if (root.left == null) return root.right; + if (root.right == null) return root.left; + + TreeNode cur = root.right; + while (cur.left != null) { + cur = cur.left; + } + cur.left = root.left; + TreeNode res = root.right; + return res; + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(h)$ +* Space complexity: $O(h)$ for the recursion stack. + +> Where $h$ is the height of the given binary search tree. + +--- + +## 3. Iteration + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]: + if not root: + return root + + parent = None + cur = root + + # Find the node to delete + while cur and cur.val != key: + parent = cur + if key > cur.val: + cur = cur.right + else: + cur = cur.left + + if not cur: + return root + + # Node with only one child or no child + if not cur.left or not cur.right: + child = cur.left if cur.left else cur.right + if not parent: + return child + if parent.left == cur: + parent.left = child + else: + parent.right = child + else: + # Node with two children + par = None # parent of right subTree min node + delNode = cur + cur = cur.right + while cur.left: + par = cur + cur = cur.left + + if par: # if there was a left traversal + par.left = cur.right + cur.right = delNode.right + + cur.left = delNode.left + + if not parent: # if we're deleting root + return cur + + if parent.left == delNode: + parent.left = cur + else: + parent.right = cur + + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode deleteNode(TreeNode root, int key) { + if (root == null) return root; + + TreeNode parent = null; + TreeNode cur = root; + + // Find the node to delete + while (cur != null && cur.val != key) { + parent = cur; + if (key > cur.val) { + cur = cur.right; + } else { + cur = cur.left; + } + } + + if (cur == null) return root; + + // Node with only one child or no child + if (cur.left == null || cur.right == null) { + TreeNode child = (cur.left != null) ? cur.left : cur.right; + if (parent == null) return child; + if (parent.left == cur) { + parent.left = child; + } else { + parent.right = child; + } + } else { + // Node with two children + TreeNode par = null; // parent of right subtree's min node + TreeNode delNode = cur; + cur = cur.right; + while (cur.left != null) { + par = cur; + cur = cur.left; + } + + if (par != null) { // if there was a left traversal + par.left = cur.right; + cur.right = delNode.right; + } + cur.left = delNode.left; + + if (parent == null) return cur; // if deleting root + + if (parent.left == delNode) { + parent.left = cur; + } else { + parent.right = cur; + } + } + + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* deleteNode(TreeNode* root, int key) { + if (!root) return root; + + TreeNode* parent = nullptr; + TreeNode* cur = root; + + // Find the node to delete + while (cur && cur->val != key) { + parent = cur; + if (key > cur->val) { + cur = cur->right; + } else { + cur = cur->left; + } + } + + if (!cur) return root; + + // Node with only one child or no child + if (!cur->left || !cur->right) { + TreeNode* child = cur->left ? cur->left : cur->right; + if (!parent) return child; + if (parent->left == cur) { + parent->left = child; + } else { + parent->right = child; + } + } else { + // Node with two children + TreeNode* par = nullptr; // parent of right subtree's min node + TreeNode* delNode = cur; + cur = cur->right; + while (cur->left) { + par = cur; + cur = cur->left; + } + + if (par) { // if there was a left traversal + par->left = cur->right; + cur->right = delNode->right; + } + cur->left = delNode->left; + + if (!parent) return cur; // if deleting root + + if (parent->left == delNode) { + parent->left = cur; + } else { + parent->right = cur; + } + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} key + * @return {TreeNode} + */ + deleteNode(root, key) { + if (!root) return root; + + let parent = null; + let cur = root; + + // Find the node to delete + while (cur && cur.val !== key) { + parent = cur; + if (key > cur.val) { + cur = cur.right; + } else { + cur = cur.left; + } + } + + if (!cur) return root; + + // Node with only one child or no child + if (!cur.left || !cur.right) { + const child = cur.left || cur.right; + if (!parent) return child; + if (parent.left === cur) { + parent.left = child; + } else { + parent.right = child; + } + } else { + // Node with two children + let par = null; // parent of right subtree's min node + const delNode = cur; + cur = cur.right; + while (cur.left) { + par = cur; + cur = cur.left; + } + + if (par) { // if there was a left traversal + par.left = cur.right; + cur.right = delNode.right; + } + cur.left = delNode.left; + + if (!parent) return cur; // if deleting root + + if (parent.left === delNode) { + parent.left = cur; + } else { + parent.right = cur; + } + } + + return root; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode DeleteNode(TreeNode root, int key) { + if (root == null) return null; + + TreeNode parent = null; + TreeNode cur = root; + + // Find the node to delete + while (cur != null && cur.val != key) { + parent = cur; + if (key > cur.val) { + cur = cur.right; + } else { + cur = cur.left; + } + } + + if (cur == null) return root; + + // Node with one or no child + if (cur.left == null || cur.right == null) { + TreeNode child = cur.left != null ? cur.left : cur.right; + + if (parent == null) { + return child; + } + + if (parent.left == cur) { + parent.left = child; + } else { + parent.right = child; + } + } else { + // Node with two children + TreeNode par = null; + TreeNode delNode = cur; + cur = cur.right; + while (cur.left != null) { + par = cur; + cur = cur.left; + } + + if (par != null) { + par.left = cur.right; + cur.right = delNode.right; + } + + cur.left = delNode.left; + + if (parent == null) { + return cur; + } + + if (parent.left == delNode) { + parent.left = cur; + } else { + parent.right = cur; + } + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(h)$ +* Space complexity: $O(1)$ extra space. + +> Where $h$ is the height of the given binary search tree. \ No newline at end of file diff --git a/articles/depth-of-binary-tree.md b/articles/depth-of-binary-tree.md new file mode 100644 index 000000000..88a35751d --- /dev/null +++ b/articles/depth-of-binary-tree.md @@ -0,0 +1,831 @@ +## 1. Recursive DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def maxDepth(self, root: Optional[TreeNode]) -> int: + if not root: + return 0 + + return 1 + max(self.maxDepth(root.left), self.maxDepth(root.right)) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public int maxDepth(TreeNode root) { + if (root == null) { + return 0; + } + + return 1 + Math.max(maxDepth(root.left), maxDepth(root.right)); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + int maxDepth(TreeNode* root) { + if (root == nullptr) { + return 0; + } + + return 1 + max(maxDepth(root->left), maxDepth(root->right)); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + maxDepth(root) { + if (root === null) { + return 0; + } + + return ( + 1 + Math.max(this.maxDepth(root.left), this.maxDepth(root.right)) + ); + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public int MaxDepth(TreeNode root) { + if (root == null) { + return 0; + } + + return 1 + Math.Max(MaxDepth(root.left), MaxDepth(root.right)); + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func maxDepth(root *TreeNode) int { + if root == nil { + return 0 + } + return 1 + max(maxDepth(root.Left), maxDepth(root.Right)) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun maxDepth(root: TreeNode?): Int { + if (root == null) { + return 0 + } + return 1 + maxOf(maxDepth(root.left), maxDepth(root.right)) + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func maxDepth(_ root: TreeNode?) -> Int { + guard let root = root else { return 0 } + return 1 + max(maxDepth(root.left), maxDepth(root.right)) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(h)$ + * Best Case ([balanced tree](https://www.geeksforgeeks.org/balanced-binary-tree/)): $O(log(n))$ + * Worst Case ([degenerate tree](https://www.geeksforgeeks.org/introduction-to-degenerate-binary-tree/)): $O(n)$ + +> Where $n$ is the number of nodes in the tree and $h$ is the height of the tree. + +--- + +## 2. Iterative DFS (Stack) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def maxDepth(self, root: Optional[TreeNode]) -> int: + stack = [[root, 1]] + res = 0 + + while stack: + node, depth = stack.pop() + + if node: + res = max(res, depth) + stack.append([node.left, depth + 1]) + stack.append([node.right, depth + 1]) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + + public class Solution { + public int maxDepth(TreeNode root) { + Stack> stack = new Stack<>(); + stack.push(new Pair<>(root, 1)); + int res = 0; + + while (!stack.isEmpty()) { + Pair current = stack.pop(); + TreeNode node = current.getKey(); + int depth = current.getValue(); + + if (node != null) { + res = Math.max(res, depth); + stack.push(new Pair<>(node.left, depth + 1)); + stack.push(new Pair<>(node.right, depth + 1)); + } + } + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + int maxDepth(TreeNode* root) { + stack> stack; + stack.push({root, 1}); + int res = 0; + + while (!stack.empty()) { + pair current = stack.top(); + stack.pop(); + TreeNode* node = current.first; + int depth = current.second; + + if (node != nullptr) { + res = max(res, depth); + stack.push({node->left, depth + 1}); + stack.push({node->right, depth + 1}); + } + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + maxDepth(root) { + const stack = [[root, 1]]; + let res = 0; + + while (stack.length > 0) { + const current = stack.pop(); + const node = current[0]; + const depth = current[1]; + + if (node !== null) { + res = Math.max(res, depth); + stack.push([node.left, depth + 1]); + stack.push([node.right, depth + 1]); + } + } + return res; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public int MaxDepth(TreeNode root) { + Stack> stack = new Stack>(); + stack.Push(new Tuple(root, 1)); + int res = 0; + + while (stack.Count > 0) { + Tuple current = stack.Pop(); + TreeNode node = current.Item1; + int depth = current.Item2; + + if (node != null) { + res = Math.Max(res, depth); + stack.Push(new Tuple(node.left, depth + 1)); + stack.Push(new Tuple(node.right, depth + 1)); + } + } + return res; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func maxDepth(root *TreeNode) int { + if root == nil { + return 0 + } + + stack := list.New() + stack.PushBack([]interface{}{root, 1}) + res := 0 + + for stack.Len() > 0 { + back := stack.Back() + stack.Remove(back) + pair := back.Value.([]interface{}) + node := pair[0].(*TreeNode) + depth := pair[1].(int) + + if node != nil { + res = max(res, depth) + stack.PushBack([]interface{}{node.Left, depth + 1}) + stack.PushBack([]interface{}{node.Right, depth + 1}) + } + } + + return res +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun maxDepth(root: TreeNode?): Int { + if (root == null) { + return 0 + } + + val stack = ArrayDeque>() + stack.addLast(Pair(root, 1)) + var res = 0 + + while (stack.isNotEmpty()) { + val (node, depth) = stack.removeLast() + + if (node != null) { + res = maxOf(res, depth) + stack.addLast(Pair(node.left, depth + 1)) + stack.addLast(Pair(node.right, depth + 1)) + } + } + + return res + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func maxDepth(_ root: TreeNode?) -> Int { + var stack: [(TreeNode?, Int)] = [(root, 1)] + var res = 0 + + while !stack.isEmpty { + let (node, depth) = stack.removeLast() + + if let node = node { + res = max(res, depth) + stack.append((node.left, depth + 1)) + stack.append((node.right, depth + 1)) + } + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def maxDepth(self, root: Optional[TreeNode]) -> int: + q = deque() + if root: + q.append(root) + + level = 0 + while q: + for i in range(len(q)): + node = q.popleft() + if node.left: + q.append(node.left) + if node.right: + q.append(node.right) + level += 1 + return level +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public int maxDepth(TreeNode root) { + Queue q = new LinkedList<>(); + if (root != null) { + q.add(root); + } + + int level = 0; + while (!q.isEmpty()) { + int size = q.size(); + for (int i = 0; i < size; i++) { + TreeNode node = q.poll(); + if (node.left != null) { + q.add(node.left); + } + if (node.right != null) { + q.add(node.right); + } + } + level++; + } + return level; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + int maxDepth(TreeNode* root) { + queue q; + if (root != nullptr) { + q.push(root); + } + + int level = 0; + while (!q.empty()) { + int size = q.size(); + for (int i = 0; i < size; i++) { + TreeNode* node = q.front(); + q.pop(); + if (node->left != nullptr) { + q.push(node->left); + } + if (node->right != nullptr) { + q.push(node->right); + } + } + level++; + } + return level; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + maxDepth(root) { + const q = new Queue(); + if (root !== null) { + q.push(root); + } + + let level = 0; + while (q.size() > 0) { + const size = q.size(); + for (let i = 0; i < size; i++) { + const node = q.pop(); + if (node.left !== null) { + q.push(node.left); + } + if (node.right !== null) { + q.push(node.right); + } + } + level++; + } + return level; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public int MaxDepth(TreeNode root) { + Queue q = new Queue(); + if (root != null) { + q.Enqueue(root); + } + + int level = 0; + while (q.Count > 0) { + int size = q.Count; + for (int i = 0; i < size; i++) { + TreeNode node = q.Dequeue(); + if (node.left != null) { + q.Enqueue(node.left); + } + if (node.right != null) { + q.Enqueue(node.right); + } + } + level++; + } + return level; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func maxDepth(root *TreeNode) int { + if root == nil { + return 0 + } + + q := linkedlistqueue.New() + q.Enqueue(root) + level := 0 + + for !q.Empty() { + size := q.Size() + + for i := 0; i < size; i++ { + val, _ := q.Dequeue() + node := val.(*TreeNode) + + if node.Left != nil { + q.Enqueue(node.Left) + } + if node.Right != nil { + q.Enqueue(node.Right) + } + } + level++ + } + + return level +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun maxDepth(root: TreeNode?): Int { + if (root == null) { + return 0 + } + + val q = ArrayDeque() + q.addLast(root) + var level = 0 + + while (q.isNotEmpty()) { + val size = q.size + + repeat(size) { + val node = q.removeFirst() + + node.left?.let { q.addLast(it) } + node.right?.let { q.addLast(it) } + } + level++ + } + + return level + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func maxDepth(_ root: TreeNode?) -> Int { + var queue = Deque() + if let root = root { + queue.append(root) + } + + var level = 0 + while !queue.isEmpty { + for _ in 0.. rating + self.cuisineToFood = defaultdict(list) # cuisine -> [food] + for i in range(len(foods)): + self.foodToRating[foods[i]] = ratings[i] + self.cuisineToFood[cuisines[i]].append(foods[i]) + + def changeRating(self, food: str, newRating: int) -> None: + self.foodToRating[food] = newRating + + def highestRated(self, cuisine: str) -> str: + maxR, res = 0, "" + for food in self.cuisineToFood[cuisine]: + r = self.foodToRating[food] + if r > maxR or (r == maxR and food < res): + res = food + maxR = r + return res +``` + +```java +public class FoodRatings { + private Map foodToRating; + private Map> cuisineToFood; + + public FoodRatings(String[] foods, String[] cuisines, int[] ratings) { + foodToRating = new HashMap<>(); + cuisineToFood = new HashMap<>(); + for (int i = 0; i < foods.length; i++) { + foodToRating.put(foods[i], ratings[i]); + cuisineToFood.computeIfAbsent(cuisines[i], k -> new ArrayList<>()).add(foods[i]); + } + } + + public void changeRating(String food, int newRating) { + foodToRating.put(food, newRating); + } + + public String highestRated(String cuisine) { + int maxR = 0; + String res = ""; + for (String food : cuisineToFood.get(cuisine)) { + int r = foodToRating.get(food); + if (r > maxR || (r == maxR && food.compareTo(res) < 0)) { + res = food; + maxR = r; + } + } + return res; + } +} +``` + +```cpp +class FoodRatings { +private: + unordered_map foodToRating; + unordered_map> cuisineToFood; + +public: + FoodRatings(vector& foods, vector& cuisines, vector& ratings) { + for (size_t i = 0; i < foods.size(); i++) { + foodToRating[foods[i]] = ratings[i]; + cuisineToFood[cuisines[i]].push_back(foods[i]); + } + } + + void changeRating(string food, int newRating) { + foodToRating[food] = newRating; + } + + string highestRated(string cuisine) { + int maxR = 0; + string res = ""; + for (const string& food : cuisineToFood[cuisine]) { + int r = foodToRating[food]; + if (r > maxR || (r == maxR && food < res)) { + res = food; + maxR = r; + } + } + return res; + } +}; +``` + +```javascript +class FoodRatings { + /** + * @param {string[]} foods + * @param {string[]} cuisines + * @param {number[]} ratings + */ + constructor(foods, cuisines, ratings) { + this.foodToRating = new Map(); + this.cuisineToFood = new Map(); + + for (let i = 0; i < foods.length; i++) { + this.foodToRating.set(foods[i], ratings[i]); + if (!this.cuisineToFood.has(cuisines[i])) { + this.cuisineToFood.set(cuisines[i], []); + } + this.cuisineToFood.get(cuisines[i]).push(foods[i]); + } + } + + /** + * @param {string} food + * @param {number} newRating + * @return {void} + */ + changeRating(food, newRating) { + this.foodToRating.set(food, newRating); + } + + /** + * @param {string} cuisine + * @return {string} + */ + highestRated(cuisine) { + let maxR = 0, res = ""; + for (let food of this.cuisineToFood.get(cuisine)) { + let r = this.foodToRating.get(food); + if (r > maxR || (r === maxR && food < res)) { + res = food; + maxR = r; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(1)$ time for each $changeRating()$ function call. + * $O(n)$ time for each $highestRated()$ function call. +* Space complexity: $O(n)$ + +--- + +## 2. Heap + +::tabs-start + +```python +class FoodRatings: + + def __init__(self, foods: List[str], cuisines: List[str], ratings: List[int]): + self.foodToRating = {} # food -> rating + self.foodToCuisine = {} # food -> cuisine + self.cuisineToHeap = defaultdict(list) # cuisine -> max_heap + + for i in range(len(foods)): + self.foodToRating[foods[i]] = ratings[i] + self.foodToCuisine[foods[i]] = cuisines[i] + heappush(self.cuisineToHeap[cuisines[i]], (-ratings[i], foods[i])) + + def changeRating(self, food: str, newRating: int) -> None: + cuisine = self.foodToCuisine[food] + self.foodToRating[food] = newRating + heappush(self.cuisineToHeap[cuisine], (-newRating, food)) + + def highestRated(self, cuisine: str) -> str: + heap = self.cuisineToHeap[cuisine] + while heap: + rating, food = heap[0] + if -rating == self.foodToRating[food]: + return food + heappop(heap) +``` + +```java +public class FoodRatings { + private Map foodToRating; + private Map foodToCuisine; + private Map> cuisineToHeap; + + private static class Food { + int rating; + String name; + + Food(int rating, String name) { + this.rating = rating; + this.name = name; + } + } + + public FoodRatings(String[] foods, String[] cuisines, int[] ratings) { + foodToRating = new HashMap<>(); + foodToCuisine = new HashMap<>(); + cuisineToHeap = new HashMap<>(); + + for (int i = 0; i < foods.length; i++) { + foodToRating.put(foods[i], ratings[i]); + foodToCuisine.put(foods[i], cuisines[i]); + cuisineToHeap + .computeIfAbsent(cuisines[i], k -> new PriorityQueue<>( + (a, b) -> a.rating == b.rating ? a.name.compareTo(b.name) : b.rating - a.rating)) + .offer(new Food(ratings[i], foods[i])); + } + } + + public void changeRating(String food, int newRating) { + String cuisine = foodToCuisine.get(food); + foodToRating.put(food, newRating); + cuisineToHeap.get(cuisine).offer(new Food(newRating, food)); + } + + public String highestRated(String cuisine) { + PriorityQueue heap = cuisineToHeap.get(cuisine); + while (!heap.isEmpty()) { + Food top = heap.peek(); + if (foodToRating.get(top.name) == top.rating) { + return top.name; + } + heap.poll(); + } + return ""; + } +} +``` + +```cpp +class FoodRatings { + unordered_map foodToRating; + unordered_map foodToCuisine; + struct cmp { + bool operator()(const pair& a, const pair& b) { + if (a.first == b.first) return a.second > b.second; + return a.first < b.first; + } + }; + unordered_map, + vector>, cmp>> cuisineToHeap; + +public: + FoodRatings(vector& foods, vector& cuisines, vector& ratings) { + for (int i = 0; i < foods.size(); i++) { + foodToRating[foods[i]] = ratings[i]; + foodToCuisine[foods[i]] = cuisines[i]; + cuisineToHeap[cuisines[i]].push({ratings[i], foods[i]}); + } + } + + void changeRating(string food, int newRating) { + string cuisine = foodToCuisine[food]; + foodToRating[food] = newRating; + cuisineToHeap[cuisine].push({newRating, food}); + } + + string highestRated(string cuisine) { + auto &heap = cuisineToHeap[cuisine]; + while (!heap.empty()) { + auto [rating, food] = heap.top(); + if (foodToRating[food] == rating) return food; + heap.pop(); + } + return ""; + } +}; +``` + +```javascript +class FoodRatings { + /** + * @param {string[]} foods + * @param {string[]} cuisines + * @param {number[]} ratings + */ + constructor(foods, cuisines, ratings) { + this.foodToRating = new Map(); + this.foodToCuisine = new Map(); + this.cuisineToHeap = new Map(); + + for (let i = 0; i < foods.length; i++) { + this.foodToRating.set(foods[i], ratings[i]); + this.foodToCuisine.set(foods[i], cuisines[i]); + if (!this.cuisineToHeap.has(cuisines[i])) { + this.cuisineToHeap.set(cuisines[i], new PriorityQueue( + (a, b) => b.rating - a.rating || a.name.localeCompare(b.name) + )); + } + this.cuisineToHeap.get(cuisines[i]).enqueue( + { rating: ratings[i], name: foods[i] } + ); + } + } + + /** + * @param {string} food + * @param {number} newRating + * @return {void} + */ + changeRating(food, newRating) { + let cuisine = this.foodToCuisine.get(food); + this.foodToRating.set(food, newRating); + this.cuisineToHeap.get(cuisine).enqueue( + { rating: newRating, name: food } + ); + } + + /** + * @param {string} cuisine + * @return {string} + */ + highestRated(cuisine) { + let heap = this.cuisineToHeap.get(cuisine); + while (!heap.isEmpty()) { + let top = heap.front(); + if (this.foodToRating.get(top.name) === top.rating) { + return top.name; + } + heap.dequeue(); + } + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n \log n)$ time for initialization. + * $O(\log n)$ time for each $changeRating()$ function call. + * $O(\log n)$ time for each $highestRated()$ function call. +* Space complexity: $O(n)$ + +--- + +## 3. Sorted Set + +::tabs-start + +```python +class FoodRatings: + + def __init__(self, foods: List[str], cuisines: List[str], ratings: List[int]): + self.foodToRating = {} # food -> rating + self.foodToCuisine = {} # food -> cuisine + self.cuisineToSortedSet = defaultdict(SortedSet) # cuisine -> SortedSet[(rating, food)] + + for i in range(len(foods)): + self.foodToRating[foods[i]] = ratings[i] + self.foodToCuisine[foods[i]] = cuisines[i] + self.cuisineToSortedSet[cuisines[i]].add((-ratings[i], foods[i])) + + def changeRating(self, food: str, newRating: int) -> None: + cuisine = self.foodToCuisine[food] + oldRating = self.foodToRating[food] + + self.cuisineToSortedSet[cuisine].remove((-oldRating, food)) + self.foodToRating[food] = newRating + self.cuisineToSortedSet[cuisine].add((-newRating, food)) + + def highestRated(self, cuisine: str) -> str: + return self.cuisineToSortedSet[cuisine][0][1] +``` + +```java +public class FoodRatings { + private Map foodToRating; + private Map foodToCuisine; + private Map> cuisineToSortedSet; + + private static class FoodPair { + int rating; + String food; + + FoodPair(int rating, String food) { + this.rating = rating; + this.food = food; + } + } + + public FoodRatings(String[] foods, String[] cuisines, int[] ratings) { + foodToRating = new HashMap<>(); + foodToCuisine = new HashMap<>(); + cuisineToSortedSet = new HashMap<>(); + + for (int i = 0; i < foods.length; i++) { + foodToRating.put(foods[i], ratings[i]); + foodToCuisine.put(foods[i], cuisines[i]); + cuisineToSortedSet.computeIfAbsent(cuisines[i], k -> new TreeSet<>((a, b) -> { + if (a.rating != b.rating) return b.rating - a.rating; + return a.food.compareTo(b.food); + })).add(new FoodPair(ratings[i], foods[i])); + } + } + + public void changeRating(String food, int newRating) { + String cuisine = foodToCuisine.get(food); + int oldRating = foodToRating.get(food); + TreeSet set = cuisineToSortedSet.get(cuisine); + set.remove(new FoodPair(oldRating, food)); + foodToRating.put(food, newRating); + set.add(new FoodPair(newRating, food)); + } + + public String highestRated(String cuisine) { + return cuisineToSortedSet.get(cuisine).first().food; + } +} +``` + +```cpp +class FoodRatings { + unordered_map foodToRating; + unordered_map foodToCuisine; + unordered_map>> cuisineToSet; + +public: + FoodRatings(vector& foods, vector& cuisines, vector& ratings) { + for (int i = 0; i < foods.size(); i++) { + foodToRating[foods[i]] = ratings[i]; + foodToCuisine[foods[i]] = cuisines[i]; + cuisineToSet[cuisines[i]].insert({-ratings[i], foods[i]}); + } + } + + void changeRating(string food, int newRating) { + string cuisine = foodToCuisine[food]; + auto& s = cuisineToSet[cuisine]; + + s.erase({-foodToRating[food], food}); + foodToRating[food] = newRating; + s.insert({-newRating, food}); + } + + string highestRated(string cuisine) { + return begin(cuisineToSet[cuisine])->second; + } +}; +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n \log n)$ time for initialization. + * $O(\log n)$ time for each $changeRating()$ function call. + * $O(1)$ in Python and $O(\log n)$ in other languages for each $highestRated()$ function call. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/design-browser-history.md b/articles/design-browser-history.md new file mode 100644 index 000000000..a557f9151 --- /dev/null +++ b/articles/design-browser-history.md @@ -0,0 +1,640 @@ +## 1. Two Stacks + +::tabs-start + +```python +class BrowserHistory: + + def __init__(self, homepage: str): + self.back_history = [homepage] + self.front_history = [] + + def visit(self, url: str) -> None: + self.back_history.append(url) + self.front_history = [] + + def back(self, steps: int) -> str: + while steps and len(self.back_history) > 1: + self.front_history.append(self.back_history.pop()) + steps -= 1 + return self.back_history[-1] + + def forward(self, steps: int) -> str: + while steps and self.front_history: + self.back_history.append(self.front_history.pop()) + steps -= 1 + return self.back_history[-1] +``` + +```java +public class BrowserHistory { + private Stack backHistory; + private Stack frontHistory; + + public BrowserHistory(String homepage) { + backHistory = new Stack<>(); + frontHistory = new Stack<>(); + backHistory.push(homepage); + } + + public void visit(String url) { + backHistory.push(url); + frontHistory = new Stack<>(); + } + + public String back(int steps) { + while (steps > 0 && backHistory.size() > 1) { + frontHistory.push(backHistory.pop()); + steps--; + } + return backHistory.peek(); + } + + public String forward(int steps) { + while (steps > 0 && !frontHistory.isEmpty()) { + backHistory.push(frontHistory.pop()); + steps--; + } + return backHistory.peek(); + } +} +``` + +```cpp +class BrowserHistory { +private: + stack backHistory, frontHistory; + +public: + BrowserHistory(string homepage) { + backHistory.push(homepage); + } + + void visit(string url) { + backHistory.push(url); + frontHistory = stack(); + } + + string back(int steps) { + while (steps-- && backHistory.size() > 1) { + frontHistory.push(backHistory.top()); + backHistory.pop(); + } + return backHistory.top(); + } + + string forward(int steps) { + while (steps-- && !frontHistory.empty()) { + backHistory.push(frontHistory.top()); + frontHistory.pop(); + } + return backHistory.top(); + } +}; +``` + +```javascript +class BrowserHistory { + /** + * @constructor + * @param {string} homepage + */ + constructor(homepage) { + this.backHistory = [homepage]; + this.frontHistory = []; + } + + /** + * @param {string} url + * @return {void} + */ + visit(url) { + this.backHistory.push(url); + this.frontHistory = []; + } + + /** + * @param {number} steps + * @return {string} + */ + back(steps) { + while (steps-- > 0 && this.backHistory.length > 1) { + this.frontHistory.push(this.backHistory.pop()); + } + return this.backHistory[this.backHistory.length - 1]; + } + + /** + * @param {number} steps + * @return {string} + */ + forward(steps) { + while (steps-- > 0 && this.frontHistory.length > 0) { + this.backHistory.push(this.frontHistory.pop()); + } + return this.backHistory[this.backHistory.length - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $visit()$ function call. + * $O(min(n, steps))$ time for each $back()$ and $forward()$ function calls. +* Space complexity: $O(m * n)$ + +> Where $n$ is the number of visited urls, $m$ is the average length of each url, and $steps$ is the number of steps we go forward or back. + +--- + +## 2. Dynamic Array + +::tabs-start + +```python +class BrowserHistory: + + def __init__(self, homepage: str): + self.history = [homepage] + self.cur = 0 + + def visit(self, url: str) -> None: + self.cur += 1 + self.history = self.history[:self.cur] + self.history.append(url) + + def back(self, steps: int) -> str: + self.cur = max(0, self.cur - steps) + return self.history[self.cur] + + def forward(self, steps: int) -> str: + self.cur = min(len(self.history) - 1, self.cur + steps) + return self.history[self.cur] +``` + +```java +public class BrowserHistory { + private List history; + private int cur; + + public BrowserHistory(String homepage) { + history = new ArrayList<>(); + history.add(homepage); + cur = 0; + } + + public void visit(String url) { + cur++; + history = history.subList(0, cur); + history.add(url); + } + + public String back(int steps) { + cur = Math.max(0, cur - steps); + return history.get(cur); + } + + public String forward(int steps) { + cur = Math.min(history.size() - 1, cur + steps); + return history.get(cur); + } +} +``` + +```cpp +class BrowserHistory { +private: + vector history; + int cur; + +public: + BrowserHistory(string homepage) { + history.push_back(homepage); + cur = 0; + } + + void visit(string url) { + cur++; + history.resize(cur); + history.push_back(url); + } + + string back(int steps) { + cur = max(0, cur - steps); + return history[cur]; + } + + string forward(int steps) { + cur = min((int)history.size() - 1, cur + steps); + return history[cur]; + } +}; +``` + +```javascript +class BrowserHistory { + /** + * @constructor + * @param {string} homepage + */ + constructor(homepage) { + this.history = [homepage]; + this.cur = 0; + } + + /** + * @param {string} url + * @return {void} + */ + visit(url) { + this.cur++; + this.history = this.history.slice(0, this.cur); + this.history.push(url); + } + + /** + * @param {number} steps + * @return {string} + */ + back(steps) { + this.cur = Math.max(0, this.cur - steps); + return this.history[this.cur]; + } + + /** + * @param {number} steps + * @return {string} + */ + forward(steps) { + this.cur = Math.min(this.history.length - 1, this.cur + steps); + return this.history[this.cur]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(n)$ time for each $visit()$ function call. + * $O(1)$ time for each $back()$ and $forward()$ function calls. +* Space complexity: $O(m * n)$ + +> Where $n$ is the number of visited urls and $m$ is the average length of each url. + +--- + +## 3. Dynamic Array (Optimal) + +::tabs-start + +```python +class BrowserHistory: + + def __init__(self, homepage: str): + self.history = [homepage] + self.cur = 0 + self.n = 1 + + def visit(self, url: str) -> None: + self.cur += 1 + if self.cur == len(self.history): + self.history.append(url) + self.n += 1 + else: + self.history[self.cur] = url + self.n = self.cur + 1 + + def back(self, steps: int) -> str: + self.cur = max(0, self.cur - steps) + return self.history[self.cur] + + def forward(self, steps: int) -> str: + self.cur = min(self.n - 1, self.cur + steps) + return self.history[self.cur] +``` + +```java +public class BrowserHistory { + private List history; + private int cur; + private int n; + + public BrowserHistory(String homepage) { + history = new ArrayList<>(); + history.add(homepage); + cur = 0; + n = 1; + } + + public void visit(String url) { + cur++; + if (cur == history.size()) { + history.add(url); + n++; + } else { + history.set(cur, url); + n = cur + 1; + } + } + + public String back(int steps) { + cur = Math.max(0, cur - steps); + return history.get(cur); + } + + public String forward(int steps) { + cur = Math.min(n - 1, cur + steps); + return history.get(cur); + } +} +``` + +```cpp +class BrowserHistory { +private: + vector history; + int cur, n; + +public: + BrowserHistory(string homepage) { + history.push_back(homepage); + cur = 0; + n = 1; + } + + void visit(string url) { + cur++; + if (cur == history.size()) { + history.push_back(url); + n++; + } else { + history[cur] = url; + n = cur + 1; + } + } + + string back(int steps) { + cur = max(0, cur - steps); + return history[cur]; + } + + string forward(int steps) { + cur = min(n - 1, cur + steps); + return history[cur]; + } +}; +``` + +```javascript +class BrowserHistory { + /** + * @constructor + * @param {string} homepage + */ + constructor(homepage) { + this.history = [homepage]; + this.cur = 0; + this.n = 1; + } + + /** + * @param {string} url + * @return {void} + */ + visit(url) { + this.cur++; + if (this.cur === this.history.length) { + this.history.push(url); + this.n++; + } else { + this.history[this.cur] = url; + this.n = this.cur + 1; + } + } + + /** + * @param {number} steps + * @return {string} + */ + back(steps) { + this.cur = Math.max(0, this.cur - steps); + return this.history[this.cur]; + } + + /** + * @param {number} steps + * @return {string} + */ + forward(steps) { + this.cur = Math.min(this.n - 1, this.cur + steps); + return this.history[this.cur]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $visit()$ function call. + * $O(1)$ time for each $back()$ and $forward()$ function calls. +* Space complexity: $O(m * n)$ + +> Where $n$ is the number of visited urls and $m$ is the average length of each url. + +--- + +## 4. Doubly Linked List + +::tabs-start + +```python +class ListNode: + def __init__(self, val, prev=None, next=None): + self.val = val + self.prev = prev + self.next = next + +class BrowserHistory: + + def __init__(self, homepage: str): + self.cur = ListNode(homepage) + + def visit(self, url: str) -> None: + self.cur.next = ListNode(url, self.cur) + self.cur = self.cur.next + + def back(self, steps: int) -> str: + while self.cur.prev and steps > 0: + self.cur = self.cur.prev + steps -= 1 + return self.cur.val + + def forward(self, steps: int) -> str: + while self.cur.next and steps > 0: + self.cur = self.cur.next + steps -= 1 + return self.cur.val +``` + +```java +class ListNode { + String val; + ListNode prev, next; + + public ListNode(String val, ListNode prev, ListNode next) { + this.val = val; + this.prev = prev; + this.next = next; + } + + public ListNode(String val) { + this(val, null, null); + } +} + +public class BrowserHistory { + private ListNode cur; + + public BrowserHistory(String homepage) { + cur = new ListNode(homepage); + } + + public void visit(String url) { + cur.next = new ListNode(url, cur, null); + cur = cur.next; + } + + public String back(int steps) { + while (cur.prev != null && steps > 0) { + cur = cur.prev; + steps--; + } + return cur.val; + } + + public String forward(int steps) { + while (cur.next != null && steps > 0) { + cur = cur.next; + steps--; + } + return cur.val; + } +} +``` + +```cpp +class BrowserHistory { + struct ListNode { + public: + string val; + ListNode* prev; + ListNode* next; + + ListNode(string val, ListNode* prev = nullptr, ListNode* next = nullptr) + : val(val), prev(prev), next(next) {} + }; + + ListNode* cur; + +public: + BrowserHistory(string homepage) { + cur = new ListNode(homepage); + } + + void visit(string url) { + cur->next = new ListNode(url, cur, nullptr); + cur = cur->next; + } + + string back(int steps) { + while (cur->prev != nullptr && steps > 0) { + cur = cur->prev; + steps--; + } + return cur->val; + } + + string forward(int steps) { + while (cur->next != nullptr && steps > 0) { + cur = cur->next; + steps--; + } + return cur->val; + } +}; +``` + +```javascript +class ListNode { + constructor(val, prev = null, next = null) { + this.val = val; + this.prev = prev; + this.next = next; + } +} + +class BrowserHistory { + /** + * @constructor + * @param {string} homepage + */ + constructor(homepage) { + this.cur = new ListNode(homepage); + } + + /** + * @param {string} url + * @return {void} + */ + visit(url) { + this.cur.next = new ListNode(url, this.cur, null); + this.cur = this.cur.next; + } + + /** + * @param {number} steps + * @return {string} + */ + back(steps) { + while (this.cur.prev !== null && steps > 0) { + this.cur = this.cur.prev; + steps--; + } + return this.cur.val; + } + + /** + * @param {number} steps + * @return {string} + */ + forward(steps) { + while (this.cur.next !== null && steps > 0) { + this.cur = this.cur.next; + steps--; + } + return this.cur.val; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $visit()$ function call. + * $O(min(n, steps))$ time for each $back()$ and $forward()$ function calls. +* Space complexity: $O(m * n)$ + +> Where $n$ is the number of visited urls, $m$ is the average length of each url, and $steps$ is the number of steps we go forward or back. \ No newline at end of file diff --git a/articles/design-circular-queue.md b/articles/design-circular-queue.md new file mode 100644 index 000000000..033e2cfd6 --- /dev/null +++ b/articles/design-circular-queue.md @@ -0,0 +1,990 @@ +## 1. Brute Force + +::tabs-start + +```python +class MyCircularQueue: + + def __init__(self, k: int): + self.q = [] + self.k = k + + def enQueue(self, value: int) -> bool: + if len(self.q) == self.k: + return False + self.q.append(value) + return True + + def deQueue(self) -> bool: + if not self.q: + return False + self.q.pop(0) + return True + + def Front(self) -> int: + if self.q: + return self.q[0] + return -1 + + def Rear(self) -> int: + if self.q: + return self.q[-1] + return -1 + + def isEmpty(self) -> bool: + return len(self.q) == 0 + + def isFull(self) -> bool: + return len(self.q) == self.k +``` + +```java +public class MyCircularQueue { + private List queue; + private int capacity; + + public MyCircularQueue(int k) { + queue = new ArrayList<>(); + capacity = k; + } + + public boolean enQueue(int value) { + if (queue.size() == capacity) { + return false; + } + queue.add(value); + return true; + } + + public boolean deQueue() { + if (queue.isEmpty()) { + return false; + } + queue.remove(0); + return true; + } + + public int Front() { + return queue.isEmpty() ? -1 : queue.get(0); + } + + public int Rear() { + return queue.isEmpty() ? -1 : queue.get(queue.size() - 1); + } + + public boolean isEmpty() { + return queue.isEmpty(); + } + + public boolean isFull() { + return queue.size() == capacity; + } +} +``` + +```cpp +class MyCircularQueue { +private: + vector queue; + int capacity; + +public: + MyCircularQueue(int k) { + capacity = k; + } + + bool enQueue(int value) { + if (queue.size() == capacity) { + return false; + } + queue.push_back(value); + return true; + } + + bool deQueue() { + if (queue.empty()) { + return false; + } + queue.erase(queue.begin()); + return true; + } + + int Front() { + return queue.empty() ? -1 : queue.front(); + } + + int Rear() { + return queue.empty() ? -1 : queue.back(); + } + + bool isEmpty() { + return queue.empty(); + } + + bool isFull() { + return queue.size() == capacity; + } +}; +``` + +```javascript +class MyCircularQueue { + /** + * @param {number} k + */ + constructor(k) { + this.queue = []; + this.capacity = k; + } + + /** + * @param {number} value + * @return {boolean} + */ + enQueue(value) { + if (this.queue.length === this.capacity) { + return false; + } + this.queue.push(value); + return true; + } + + /** + * @return {boolean} + */ + deQueue() { + if (this.queue.length === 0) { + return false; + } + this.queue.shift(); + return true; + } + + /** + * @return {number} + */ + Front() { + return this.queue.length === 0 ? -1 : this.queue[0]; + } + + /** + * @return {number} + */ + Rear() { + return this.queue.length === 0 ? -1 : this.queue[this.queue.length - 1]; + } + + /** + * @return {boolean} + */ + isEmpty() { + return this.queue.length === 0; + } + + /** + * @return {boolean} + */ + isFull() { + return this.queue.length === this.capacity; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $enQueue()$, $Front()$, $Rear()$, $isEmpty()$ and $isFull()$ function calls. + * $O(n)$ time for each $deQueue()$ function call. +* Space complexity: $O(n)$ + +> Where $n$ is the size of the queue. + +--- + +## 2. Array + +::tabs-start + +```python +class MyCircularQueue: + + def __init__(self, k: int): + self.q = [0] * k + self.k = k + self.front = 0 + self.rear = -1 + self.size = 0 + + def enQueue(self, value: int) -> bool: + if self.isFull(): + return False + self.rear = (self.rear + 1) % self.k + self.q[self.rear] = value + self.size += 1 + return True + + def deQueue(self) -> bool: + if self.isEmpty(): + return False + self.front = (self.front + 1) % self.k + self.size -= 1 + return True + + def Front(self) -> int: + if self.isEmpty(): + return -1 + return self.q[self.front] + + def Rear(self) -> int: + if self.isEmpty(): + return -1 + return self.q[self.rear] + + def isEmpty(self) -> bool: + return self.size == 0 + + def isFull(self) -> bool: + return self.size == self.k +``` + +```java +public class MyCircularQueue { + private int[] queue; + private int size; + private int front; + private int rear; + + public MyCircularQueue(int k) { + queue = new int[k]; + size = 0; + front = 0; + rear = -1; + } + + public boolean enQueue(int value) { + if (isFull()) { + return false; + } + rear = (rear + 1) % queue.length; + queue[rear] = value; + size++; + return true; + } + + public boolean deQueue() { + if (isEmpty()) { + return false; + } + front = (front + 1) % queue.length; + size--; + return true; + } + + public int Front() { + return isEmpty() ? -1 : queue[front]; + } + + public int Rear() { + return isEmpty() ? -1 : queue[rear]; + } + + public boolean isEmpty() { + return size == 0; + } + + public boolean isFull() { + return size == queue.length; + } +} +``` + +```cpp +class MyCircularQueue { +private: + vector queue; + int size; + int front; + int rear; + int capacity; + +public: + MyCircularQueue(int k) { + queue = vector(k); + size = 0; + front = 0; + rear = -1; + capacity = k; + } + + bool enQueue(int value) { + if (isFull()) { + return false; + } + rear = (rear + 1) % capacity; + queue[rear] = value; + size++; + return true; + } + + bool deQueue() { + if (isEmpty()) { + return false; + } + front = (front + 1) % capacity; + size--; + return true; + } + + int Front() { + return isEmpty() ? -1 : queue[front]; + } + + int Rear() { + return isEmpty() ? -1 : queue[rear]; + } + + bool isEmpty() { + return size == 0; + } + + bool isFull() { + return size == capacity; + } +}; +``` + +```javascript +class MyCircularQueue { + /** + * @param {number} k + */ + constructor(k) { + this.queue = new Array(k); + this.size = 0; + this.front = 0; + this.rear = -1; + this.capacity = k; + } + + /** + * @param {number} value + * @return {boolean} + */ + enQueue(value) { + if (this.isFull()) { + return false; + } + this.rear = (this.rear + 1) % this.capacity; + this.queue[this.rear] = value; + this.size++; + return true; + } + + /** + * @return {boolean} + */ + deQueue() { + if (this.isEmpty()) { + return false; + } + this.front = (this.front + 1) % this.capacity; + this.size--; + return true; + } + + /** + * @return {number} + */ + Front() { + return this.isEmpty() ? -1 : this.queue[this.front]; + } + + /** + * @return {number} + */ + Rear() { + return this.isEmpty() ? -1 : this.queue[this.rear]; + } + + /** + * @return {boolean} + */ + isEmpty() { + return this.size === 0; + } + + /** + * @return {boolean} + */ + isFull() { + return this.size === this.capacity; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(1)$ time for each $enQueue()$, $deQueue()$, $Front()$, $Rear()$, $isEmpty()$ and $isFull()$ function calls. +* Space complexity: $O(n)$ + +> Where $n$ is the size of the queue. + +--- + +## 3. Doubly Linked List + +::tabs-start + +```python +class ListNode: + + def __init__(self, val, nxt, prev): + self.val, self.next, self.prev = val, nxt, prev + +class MyCircularQueue: + + def __init__(self, k: int): + self.space = k + self.left = ListNode(0, None, None) + self.right = ListNode(0, None, self.left) + self.left.next = self.right + + def enQueue(self, value: int) -> bool: + if self.isFull(): return False + cur = ListNode(value, self.right, self.right.prev) + self.right.prev.next = cur + self.right.prev = cur + self.space -= 1 + return True + + def deQueue(self) -> bool: + if self.isEmpty(): return False + self.left.next = self.left.next.next + self.left.next.prev = self.left + self.space += 1 + return True + + def Front(self) -> int: + if self.isEmpty(): return -1 + return self.left.next.val + + def Rear(self) -> int: + if self.isEmpty(): return -1 + return self.right.prev.val + + def isEmpty(self) -> bool: + return self.left.next == self.right + + def isFull(self) -> bool: + return self.space == 0 +``` + +```java +class ListNode { + int val; + ListNode next, prev; + + ListNode(int val, ListNode next, ListNode prev) { + this.val = val; + this.next = next; + this.prev = prev; + } +} + +public class MyCircularQueue { + private int space; + private ListNode left, right; + + public MyCircularQueue(int k) { + space = k; + left = new ListNode(0, null, null); + right = new ListNode(0, null, left); + left.next = right; + } + + public boolean enQueue(int value) { + if (isFull()) { + return false; + } + ListNode cur = new ListNode(value, right, right.prev); + right.prev.next = cur; + right.prev = cur; + space--; + return true; + } + + public boolean deQueue() { + if (isEmpty()) { + return false; + } + left.next = left.next.next; + left.next.prev = left; + space++; + return true; + } + + public int Front() { + return isEmpty() ? -1 : left.next.val; + } + + public int Rear() { + return isEmpty() ? -1 : right.prev.val; + } + + public boolean isEmpty() { + return left.next == right; + } + + public boolean isFull() { + return space == 0; + } +} +``` + +```cpp +class MyCircularQueue { +private: + struct ListNode { + int val; + ListNode* next; + ListNode* prev; + ListNode(int v, ListNode* n = nullptr, ListNode* p = nullptr) + : val(v), next(n), prev(p) {} + }; + + int space; + ListNode* left; + ListNode* right; + +public: + MyCircularQueue(int k) { + space = k; + left = new ListNode(0); + right = new ListNode(0, nullptr, left); + left->next = right; + } + + bool enQueue(int value) { + if (isFull()) return false; + ListNode* cur = new ListNode(value, right, right->prev); + right->prev->next = cur; + right->prev = cur; + space--; + return true; + } + + bool deQueue() { + if (isEmpty()) return false; + ListNode* tmp = left->next; + left->next = left->next->next; + left->next->prev = left; + delete tmp; + space++; + return true; + } + + int Front() { + return isEmpty() ? -1 : left->next->val; + } + + int Rear() { + return isEmpty() ? -1 : right->prev->val; + } + + bool isEmpty() { + return left->next == right; + } + + bool isFull() { + return space == 0; + } +}; +``` + +```javascript +class ListNode { + /** + * @param {number} val + * @param {ListNode} next + * @param {ListNode} prev + */ + constructor(val, next = null, prev = null) { + this.val = val; + this.next = next; + this.prev = prev; + } +} + +class MyCircularQueue { + /** + * @param {number} k + */ + constructor(k) { + this.space = k; + this.left = new ListNode(0); + this.right = new ListNode(0, null, this.left); + this.left.next = this.right; + } + + /** + * @param {number} value + * @return {boolean} + */ + enQueue(value) { + if (this.isFull()) return false; + const cur = new ListNode(value, this.right, this.right.prev); + this.right.prev.next = cur; + this.right.prev = cur; + this.space--; + return true; + } + + /** + * @return {boolean} + */ + deQueue() { + if (this.isEmpty()) return false; + this.left.next = this.left.next.next; + this.left.next.prev = this.left; + this.space++; + return true; + } + + /** + * @return {number} + */ + Front() { + return this.isEmpty() ? -1 : this.left.next.val; + } + + /** + * @return {number} + */ + Rear() { + return this.isEmpty() ? -1 : this.right.prev.val; + } + + /** + * @return {boolean} + */ + isEmpty() { + return this.left.next === this.right; + } + + /** + * @return {boolean} + */ + isFull() { + return this.space === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(1)$ time for each $enQueue()$, $deQueue()$, $Front()$, $Rear()$, $isEmpty()$ and $isFull()$ function calls. +* Space complexity: $O(n)$ + +> Where $n$ is the size of the queue. + +--- + +## 4. Singly Linked List + +::tabs-start + +```python +class ListNode: + def __init__(self, val, nxt=None): + self.val = val + self.next = nxt + +class MyCircularQueue: + def __init__(self, k: int): + self.space = k + self.left = ListNode(0) + self.right = self.left + + def enQueue(self, value: int) -> bool: + if self.isFull(): return False + + cur = ListNode(value) + if self.isEmpty(): + self.left.next = cur + self.right = cur + else: + self.right.next = cur + self.right = cur + + self.space -= 1 + return True + + def deQueue(self) -> bool: + if self.isEmpty(): return False + + self.left.next = self.left.next.next + if self.left.next is None: + self.right = self.left + + self.space += 1 + return True + + def Front(self) -> int: + if self.isEmpty(): return -1 + return self.left.next.val + + def Rear(self) -> int: + if self.isEmpty(): return -1 + return self.right.val + + def isEmpty(self) -> bool: + return self.left.next is None + + def isFull(self) -> bool: + return self.space == 0 +``` + +```java +class ListNode { + int val; + ListNode next; + + ListNode(int val) { + this.val = val; + this.next = null; + } +} + +public class MyCircularQueue { + private int space; + private ListNode left; + private ListNode right; + + public MyCircularQueue(int k) { + this.space = k; + this.left = new ListNode(0); + this.right = this.left; + } + + public boolean enQueue(int value) { + if (isFull()) return false; + + ListNode cur = new ListNode(value); + if (isEmpty()) { + this.left.next = cur; + this.right = cur; + } else { + this.right.next = cur; + this.right = cur; + } + + this.space--; + return true; + } + + public boolean deQueue() { + if (isEmpty()) return false; + + this.left.next = this.left.next.next; + if (this.left.next == null) { + this.right = this.left; + } + + this.space++; + return true; + } + + public int Front() { + return isEmpty() ? -1 : this.left.next.val; + } + + public int Rear() { + return isEmpty() ? -1 : this.right.val; + } + + public boolean isEmpty() { + return this.left.next == null; + } + + public boolean isFull() { + return this.space == 0; + } +} +``` + +```cpp +class MyCircularQueue { +private: + struct ListNode { + int val; + ListNode* next; + ListNode(int v) : val(v), next(nullptr) {} + }; + + int space; + ListNode* left; + ListNode* right; + +public: + MyCircularQueue(int k) { + space = k; + left = new ListNode(0); + right = left; + } + + bool enQueue(int value) { + if (isFull()) return false; + + ListNode* cur = new ListNode(value); + if (isEmpty()) { + left->next = cur; + right = cur; + } else { + right->next = cur; + right = cur; + } + + space--; + return true; + } + + bool deQueue() { + if (isEmpty()) return false; + + ListNode* tmp = left->next; + left->next = left->next->next; + delete tmp; + if (!left->next) { + right = left; + } + + space++; + return true; + } + + int Front() { + return isEmpty() ? -1 : left->next->val; + } + + int Rear() { + return isEmpty() ? -1 : right->val; + } + + bool isEmpty() { + return left->next == nullptr; + } + + bool isFull() { + return space == 0; + } +}; +``` + +```javascript +class ListNode { + /** + * @param {number} val + * @param {ListNode} next + */ + constructor(val, next = null) { + this.val = val; + this.next = next; + } +} + +class MyCircularQueue { + /** + * @param {number} k + */ + constructor(k) { + this.space = k; + this.left = new ListNode(0); + this.right = this.left; + } + + /** + * @param {number} value + * @return {boolean} + */ + enQueue(value) { + if (this.isFull()) return false; + + const cur = new ListNode(value); + if (this.isEmpty()) { + this.left.next = cur; + this.right = cur; + } else { + this.right.next = cur; + this.right = cur; + } + + this.space--; + return true; + } + + /** + * @return {boolean} + */ + deQueue() { + if (this.isEmpty()) return false; + + this.left.next = this.left.next.next; + if (!this.left.next) { + this.right = this.left; + } + + this.space++; + return true; + } + + /** + * @return {number} + */ + Front() { + return this.isEmpty() ? -1 : this.left.next.val; + } + + /** + * @return {number} + */ + Rear() { + return this.isEmpty() ? -1 : this.right.val; + } + + /** + * @return {boolean} + */ + isEmpty() { + return this.left.next === null; + } + + /** + * @return {boolean} + */ + isFull() { + return this.space === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(1)$ time for each $enQueue()$, $deQueue()$, $Front()$, $Rear()$, $isEmpty()$ and $isFull()$ function calls. +* Space complexity: $O(n)$ + +> Where $n$ is the size of the queue. \ No newline at end of file diff --git a/articles/design-hashmap.md b/articles/design-hashmap.md new file mode 100644 index 000000000..4a64bdee1 --- /dev/null +++ b/articles/design-hashmap.md @@ -0,0 +1,365 @@ +## 1. Array + +::tabs-start + +```python +class MyHashMap: + + def __init__(self): + self.map = [-1] * 1000001 + + def put(self, key: int, value: int) -> None: + self.map[key] = value + + def get(self, key: int) -> int: + return self.map[key] + + def remove(self, key: int) -> None: + self.map[key] = -1 +``` + +```java +public class MyHashMap { + private int[] map; + + public MyHashMap() { + map = new int[1000001]; + Arrays.fill(map, -1); + } + + public void put(int key, int value) { + map[key] = value; + } + + public int get(int key) { + return map[key]; + } + + public void remove(int key) { + map[key] = -1; + } +} +``` + +```cpp +class MyHashMap { +private: + vector map; + +public: + MyHashMap() : map(1000001, -1) {} + + void put(int key, int value) { + map[key] = value; + } + + int get(int key) { + return map[key]; + } + + void remove(int key) { + map[key] = -1; + } +}; +``` + +```javascript +class MyHashMap { + constructor() { + this.map = new Array(1000001).fill(-1); + } + + /** + * @param {number} key + * @param {number} value + * @return {void} + */ + put(key, value) { + this.map[key] = value; + } + + /** + * @param {number} key + * @return {number} + */ + get(key) { + return this.map[key]; + } + + /** + * @param {number} key + * @return {void} + */ + remove(key) { + this.map[key] = -1; + } +} + +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for each function call. +* Space complexity: $O(1000000)$ since the key is in the range $[0, 1000000]$. + +--- + +## 2. Linked List + +::tabs-start + +```python +class ListNode: + def __init__(self, key = -1, val = -1, next = None): + self.key = key + self.val = val + self.next = next + +class MyHashMap: + + def __init__(self): + self.map = [ListNode() for _ in range(1000)] + + def hash(self, key: int) -> int: + return key % len(self.map) + + def put(self, key: int, value: int) -> None: + cur = self.map[self.hash(key)] + while cur.next: + if cur.next.key == key: + cur.next.val = value + return + cur = cur.next + cur.next = ListNode(key, value) + + def get(self, key: int) -> int: + cur = self.map[self.hash(key)].next + while cur: + if cur.key == key: + return cur.val + cur = cur.next + return -1 + + def remove(self, key: int) -> None: + cur = self.map[self.hash(key)] + while cur.next: + if cur.next.key == key: + cur.next = cur.next.next + return + cur = cur.next +``` + +```java +class ListNode { + int key, val; + ListNode next; + + public ListNode(int key, int val, ListNode next) { + this.key = key; + this.val = val; + this.next = next; + } + + public ListNode() { + this(-1, -1, null); + } +} + +public class MyHashMap { + private ListNode[] map; + + public MyHashMap() { + map = new ListNode[1000]; + for (int i = 0; i < 1000; i++) { + map[i] = new ListNode(); + } + } + + private int hash(int key) { + return key % map.length; + } + + public void put(int key, int value) { + ListNode cur = map[hash(key)]; + while (cur.next != null) { + if (cur.next.key == key) { + cur.next.val = value; + return; + } + cur = cur.next; + } + cur.next = new ListNode(key, value, null); + } + + public int get(int key) { + ListNode cur = map[hash(key)].next; + while (cur != null) { + if (cur.key == key) { + return cur.val; + } + cur = cur.next; + } + return -1; + } + + public void remove(int key) { + ListNode cur = map[hash(key)]; + while (cur.next != null) { + if (cur.next.key == key) { + cur.next = cur.next.next; + return; + } + cur = cur.next; + } + } +} +``` + +```cpp +class MyHashMap { +private: + struct ListNode { + int key, val; + ListNode* next; + + ListNode(int key = -1, int val = -1, ListNode* next = nullptr) + : key(key), val(val), next(next) {} + }; + + vector map; + int hash(int key) { + return key % map.size(); + } + +public: + MyHashMap() { + map.resize(1000); + for (auto& bucket : map) { + bucket = new ListNode(0); + } + } + + void put(int key, int value) { + ListNode* cur = map[hash(key)]; + while (cur->next) { + if (cur->next->key == key) { + cur->next->val = value; + return; + } + cur = cur->next; + } + cur->next = new ListNode(key, value); + } + + int get(int key) { + ListNode* cur = map[hash(key)]->next; + while (cur) { + if (cur->key == key) { + return cur->val; + } + cur = cur->next; + } + return -1; + } + + void remove(int key) { + ListNode* cur = map[hash(key)]; + while (cur->next) { + if (cur->next->key == key) { + ListNode* tmp = cur->next; + cur->next = cur->next->next; + delete tmp; + return; + } + cur = cur->next; + } + } +}; +``` + +```javascript +class ListNode { + /** + * @param {number} key + * @param {number} val + * @param {ListNode} next + */ + constructor(key = -1, val = -1, next = null) { + this.key = key; + this.val = val; + this.next = next; + } +} + +class MyHashMap { + constructor() { + this.map = Array.from({ length: 1000 }, () => new ListNode()); + } + + /** + * @param {number} key + * @return {number} + */ + hash(key) { + return key % this.map.length; + } + + /** + * @param {number} key + * @param {number} value + * @return {void} + */ + put(key, value) { + let cur = this.map[this.hash(key)]; + while (cur.next) { + if (cur.next.key === key) { + cur.next.val = value; + return; + } + cur = cur.next; + } + cur.next = new ListNode(key, value); + } + + /** + * @param {number} key + * @return {number} + */ + get(key) { + let cur = this.map[this.hash(key)].next; + while (cur) { + if (cur.key === key) { + return cur.val; + } + cur = cur.next; + } + return -1; + } + + /** + * @param {number} key + * @return {void} + */ + remove(key) { + let cur = this.map[this.hash(key)]; + while (cur.next) { + if (cur.next.key === key) { + cur.next = cur.next.next; + return; + } + cur = cur.next; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\frac{n}{k})$ for each function call. +* Space complexity: $O(k + m)$ + +> Where $n$ is the number of keys, $k$ is the size of the map ($1000$) and $m$ is the number of unique keys. \ No newline at end of file diff --git a/articles/design-hashset.md b/articles/design-hashset.md new file mode 100644 index 000000000..9ee3f0884 --- /dev/null +++ b/articles/design-hashset.md @@ -0,0 +1,1028 @@ +## 1. Brute Force + +::tabs-start + +```python +class MyHashSet: + + def __init__(self): + self.data = [] + + def add(self, key: int) -> None: + if key not in self.data: + self.data.append(key) + + def remove(self, key: int) -> None: + if key in self.data: + self.data.remove(key) + + def contains(self, key: int) -> bool: + return key in self.data +``` + +```java +public class MyHashSet { + private List data; + + public MyHashSet() { + data = new ArrayList<>(); + } + + public void add(int key) { + if (!data.contains(key)) { + data.add(key); + } + } + + public void remove(int key) { + data.remove(Integer.valueOf(key)); + } + + public boolean contains(int key) { + return data.contains(key); + } +} +``` + +```cpp +class MyHashSet { +private: + vector data; +public: + MyHashSet() {} + + void add(int key) { + if (find(data.begin(), data.end(), key) == data.end()) { + data.push_back(key); + } + } + + void remove(int key) { + auto it = find(data.begin(), data.end(), key); + if (it != data.end()) { + data.erase(it); + } + } + + bool contains(int key) { + return find(data.begin(), data.end(), key) != data.end(); + } +}; +``` + +```javascript +class MyHashSet { + constructor() { + this.data = []; + } + + /** + * @param {number} key + * @return {void} + */ + add(key) { + if (!this.data.includes(key)) { + this.data.push(key); + } + } + + /** + * @param {number} key + * @return {void} + */ + remove(key) { + const index = this.data.indexOf(key); + if (index !== -1) { + this.data.splice(index, 1); + } + } + + /** + * @param {number} key + * @return {boolean} + */ + contains(key) { + return this.data.includes(key); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ for each function call. +* Space complexity: $O(n)$ + +--- + +## 2. Boolean Array + +::tabs-start + +```python +class MyHashSet: + + def __init__(self): + self.data = [False] * 1000001 + + def add(self, key: int) -> None: + self.data[key] = True + + def remove(self, key: int) -> None: + self.data[key] = False + + def contains(self, key: int) -> bool: + return self.data[key] +``` + +```java +public class MyHashSet { + private boolean[] data; + + public MyHashSet() { + data = new boolean[1000001]; + } + + public void add(int key) { + data[key] = true; + } + + public void remove(int key) { + data[key] = false; + } + + public boolean contains(int key) { + return data[key]; + } +} +``` + +```cpp +class MyHashSet { +private: + vector data; +public: + MyHashSet() : data(1000001, false) {} + + void add(int key) { + data[key] = true; + } + + void remove(int key) { + data[key] = false; + } + + bool contains(int key) { + return data[key]; + } +}; +``` + +```javascript +class MyHashSet { + constructor() { + this.data = new Array(1000001).fill(false); + } + + /** + * @param {number} key + * @return {void} + */ + add(key) { + this.data[key] = true; + } + + /** + * @param {number} key + * @return {void} + */ + remove(key) { + this.data[key] = false; + } + + /** + * @param {number} key + * @return {boolean} + */ + contains(key) { + return this.data[key]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for each function call. +* Space complexity: $O(1000000)$ since the key is in the range $[0, 1000000]$. + +--- + +## 3. Linked List + +::tabs-start + +```python +class ListNode: + def __init__(self, key: int): + self.key = key + self.next = None + +class MyHashSet: + + def __init__(self): + self.set = [ListNode(0) for _ in range(10**4)] + + def add(self, key: int) -> None: + cur = self.set[key % len(self.set)] + while cur.next: + if cur.next.key == key: + return + cur = cur.next + cur.next = ListNode(key) + + def remove(self, key: int) -> None: + cur = self.set[key % len(self.set)] + while cur.next: + if cur.next.key == key: + cur.next = cur.next.next + return + cur = cur.next + + def contains(self, key: int) -> bool: + cur = self.set[key % len(self.set)] + while cur.next: + if cur.next.key == key: + return True + cur = cur.next + return False +``` + +```java +public class MyHashSet { + + private static class ListNode { + int key; + ListNode next; + + ListNode(int key) { + this.key = key; + } + } + + private final ListNode[] set; + + public MyHashSet() { + set = new ListNode[10000]; + for (int i = 0; i < set.length; i++) { + set[i] = new ListNode(0); + } + } + + public void add(int key) { + ListNode cur = set[key % set.length]; + while (cur.next != null) { + if (cur.next.key == key) { + return; + } + cur = cur.next; + } + cur.next = new ListNode(key); + } + + public void remove(int key) { + ListNode cur = set[key % set.length]; + while (cur.next != null) { + if (cur.next.key == key) { + cur.next = cur.next.next; + return; + } + cur = cur.next; + } + } + + public boolean contains(int key) { + ListNode cur = set[key % set.length]; + while (cur.next != null) { + if (cur.next.key == key) { + return true; + } + cur = cur.next; + } + return false; + } +} +``` + +```cpp +class MyHashSet { +private: + struct ListNode { + int key; + ListNode* next; + ListNode(int k) : key(k), next(nullptr) {} + }; + + vector set; + + int hash(int key) { + return key % set.size(); + } + +public: + MyHashSet() { + set.resize(10000); + for (auto& bucket : set) { + bucket = new ListNode(0); + } + } + + void add(int key) { + ListNode* cur = set[hash(key)]; + while (cur->next) { + if (cur->next->key == key) { + return; + } + cur = cur->next; + } + cur->next = new ListNode(key); + } + + void remove(int key) { + ListNode* cur = set[hash(key)]; + while (cur->next) { + if (cur->next->key == key) { + ListNode* temp = cur->next; + cur->next = temp->next; + delete temp; + return; + } + cur = cur->next; + } + } + + bool contains(int key) { + ListNode* cur = set[hash(key)]; + while (cur->next) { + if (cur->next->key == key) { + return true; + } + cur = cur->next; + } + return false; + } +}; +``` + +```javascript +class ListNode { + /** + * @param {number} key + */ + constructor(key) { + this.key = key; + this.next = null; + } +} + +class MyHashSet { + constructor() { + this.set = Array.from({ length: 10000 }, () => new ListNode(0)); + } + + /** + * @param {number} key + * @return {number} + */ + hash(key) { + return key % this.set.length; + } + + /** + * @param {number} key + * @return {void} + */ + add(key) { + let cur = this.set[this.hash(key)]; + while (cur.next) { + if (cur.next.key === key) { + return; + } + cur = cur.next; + } + cur.next = new ListNode(key); + } + + /** + * @param {number} key + * @return {void} + */ + remove(key) { + let cur = this.set[this.hash(key)]; + while (cur.next) { + if (cur.next.key === key) { + cur.next = cur.next.next; + return; + } + cur = cur.next; + } + } + + /** + * @param {number} key + * @return {boolean} + */ + contains(key) { + let cur = this.set[this.hash(key)]; + while (cur.next) { + if (cur.next.key === key) { + return true; + } + cur = cur.next; + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\frac{n}{k})$ for each function call. +* Space complexity: $O(k + m)$ + +> Where $n$ is the number of keys, $k$ is the size of the set ($10000$) and $m$ is the number of unique keys. + +--- + +## 4. Binary Search Tree + +::tabs-start + +```python +class TreeNode: + def __init__(self, key): + self.key = key + self.left = None + self.right = None + +class BST: + def __init__(self): + self.root = None + + def insert(self, root, key): + if not root: + return TreeNode(key) + if key < root.key: + root.left = self.insert(root.left, key) + elif key > root.key: + root.right = self.insert(root.right, key) + return root + + def delete(self, root, key): + if not root: + return None + if key < root.key: + root.left = self.delete(root.left, key) + elif key > root.key: + root.right = self.delete(root.right, key) + else: + if not root.left: + return root.right + if not root.right: + return root.left + temp = self.minValueNode(root.right) + root.key = temp.key + root.right = self.delete(root.right, temp.key) + return root + + def minValueNode(self, root): + while root.left: + root = root.left + return root + + def search(self, root, key): + if not root: + return False + if key == root.key: + return True + elif key < root.key: + return self.search(root.left, key) + else: + return self.search(root.right, key) + + def add(self, key): + self.root = self.insert(self.root, key) + + def remove(self, key): + self.root = self.delete(self.root, key) + + def contains(self, key): + return self.search(self.root, key) + +class MyHashSet: + def __init__(self): + self.size = 10000 + self.buckets = [BST() for _ in range(self.size)] + + def _hash(self, key): + return key % self.size + + def add(self, key: int) -> None: + idx = self._hash(key) + if not self.contains(key): + self.buckets[idx].add(key) + + def remove(self, key: int) -> None: + idx = self._hash(key) + self.buckets[idx].remove(key) + + def contains(self, key: int) -> bool: + idx = self._hash(key) + return self.buckets[idx].contains(key) +``` + +```java +class TreeNode { + int key; + TreeNode left, right; + + TreeNode(int key) { + this.key = key; + } +} + +class BST { + private TreeNode root; + + private TreeNode insert(TreeNode node, int key) { + if (node == null) return new TreeNode(key); + if (key < node.key) node.left = insert(node.left, key); + else if (key > node.key) node.right = insert(node.right, key); + return node; + } + + private TreeNode delete(TreeNode node, int key) { + if (node == null) return null; + if (key < node.key) node.left = delete(node.left, key); + else if (key > node.key) node.right = delete(node.right, key); + else { + if (node.left == null) return node.right; + if (node.right == null) return node.left; + TreeNode temp = minValueNode(node.right); + node.key = temp.key; + node.right = delete(node.right, temp.key); + } + return node; + } + + private TreeNode minValueNode(TreeNode node) { + while (node.left != null) node = node.left; + return node; + } + + private boolean search(TreeNode node, int key) { + if (node == null) return false; + if (key == node.key) return true; + return key < node.key ? search(node.left, key) : search(node.right, key); + } + + public void add(int key) { + root = insert(root, key); + } + + public void remove(int key) { + root = delete(root, key); + } + + public boolean contains(int key) { + return search(root, key); + } +} + +public class MyHashSet { + private final int size = 10000; + private BST[] buckets; + + public MyHashSet() { + buckets = new BST[size]; + for (int i = 0; i < size; i++) { + buckets[i] = new BST(); + } + } + + private int hash(int key) { + return key % size; + } + + public void add(int key) { + int idx = hash(key); + if (!buckets[idx].contains(key)) { + buckets[idx].add(key); + } + } + + public void remove(int key) { + int idx = hash(key); + buckets[idx].remove(key); + } + + public boolean contains(int key) { + int idx = hash(key); + return buckets[idx].contains(key); + } +} +``` + +```cpp +class BST { +private: + struct TreeNode { + int key; + TreeNode* left; + TreeNode* right; + TreeNode(int k) : key(k), left(nullptr), right(nullptr) {} + }; + + TreeNode* insert(TreeNode* root, int key) { + if (!root) return new TreeNode(key); + if (key < root->key) + root->left = insert(root->left, key); + else if (key > root->key) + root->right = insert(root->right, key); + return root; + } + + TreeNode* deleteNode(TreeNode* root, int key) { + if (!root) return nullptr; + if (key < root->key) + root->left = deleteNode(root->left, key); + else if (key > root->key) + root->right = deleteNode(root->right, key); + else { + if (!root->left) { + TreeNode* temp = root->right; + delete root; + return temp; + } + if (!root->right) { + TreeNode* temp = root->left; + delete root; + return temp; + } + TreeNode* temp = minValueNode(root->right); + root->key = temp->key; + root->right = deleteNode(root->right, temp->key); + } + return root; + } + + TreeNode* minValueNode(TreeNode* root) { + while (root->left) root = root->left; + return root; + } + + bool search(TreeNode* root, int key) { + if (!root) return false; + if (key == root->key) return true; + return key < root->key ? search(root->left, key) : search(root->right, key); + } + + TreeNode* root; + +public: + BST() : root(nullptr) {} + + void add(int key) { + root = insert(root, key); + } + + void remove(int key) { + root = deleteNode(root, key); + } + + bool contains(int key) { + return search(root, key); + } +}; + +class MyHashSet { +private: + const int size = 10000; + vector buckets; + + int hash(int key) { + return key % size; + } + +public: + MyHashSet() : buckets(size) {} + + void add(int key) { + int idx = hash(key); + if (!contains(key)) { + buckets[idx].add(key); + } + } + + void remove(int key) { + int idx = hash(key); + buckets[idx].remove(key); + } + + bool contains(int key) { + int idx = hash(key); + return buckets[idx].contains(key); + } +}; +``` + +```javascript +class TreeNode { + /** + * @param {number} key + */ + constructor(key) { + this.key = key; + this.left = null; + this.right = null; + } +} + +class BST { + constructor() { + this.root = null; + } + + /** + * @param {number} key + * @return {void} + */ + add(key) { + this.root = this._insert(this.root, key); + } + + /** + * @param {TreeNode} node + * @param {number} key + * @return {TreeNode} + */ + _insert(node, key) { + if (!node) return new TreeNode(key); + if (key < node.key) node.left = this._insert(node.left, key); + else if (key > node.key) node.right = this._insert(node.right, key); + return node; + } + + /** + * @param {number} key + * @return {void} + */ + remove(key) { + this.root = this._deleteNode(this.root, key); + } + + /** + * @param {TreeNode} node + * @param {number} key + * @return {TreeNode} + */ + _deleteNode(node, key) { + if (!node) return null; + if (key < node.key) node.left = this._deleteNode(node.left, key); + else if (key > node.key) node.right = this._deleteNode(node.right, key); + else { + if (!node.left) return node.right; + if (!node.right) return node.left; + let minNode = this._minValueNode(node.right); + node.key = minNode.key; + node.right = this._deleteNode(node.right, minNode.key); + } + return node; + } + + /** + * @param {TreeNode} node + * @return {TreeNode} + */ + _minValueNode(node) { + while (node.left) node = node.left; + return node; + } + + /** + * @param {number} key + * @return {boolean} + */ + contains(key) { + return this._search(this.root, key); + } + + /** + * @param {TreeNode} node + * @param {number} key + * @return {boolean} + */ + _search(node, key) { + if (!node) return false; + if (key === node.key) return true; + if (key < node.key) return this._search(node.left, key); + return this._search(node.right, key); + } +} + +class MyHashSet { + constructor() { + this.size = 10000; + this.buckets = Array.from({ length: this.size }, () => new BST()); + } + + _hash(key) { + return key % this.size; + } + + /** + * @param {number} key + * @return {void} + */ + add(key) { + const idx = this._hash(key); + if (!this.buckets[idx].contains(key)) { + this.buckets[idx].add(key); + } + } + + /** + * @param {number} key + * @return {void} + */ + remove(key) { + const idx = this._hash(key); + this.buckets[idx].remove(key); + } + + /** + * @param {number} key + * @return {boolean} + */ + contains(key) { + const idx = this._hash(key); + return this.buckets[idx].contains(key); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log (\frac{n}{k}))$ in average case, $O(\frac{n}{k})$ in worst case for each function call. +* Space complexity: $O(k + m)$ + +> Where $n$ is the number of keys, $k$ is the size of the set ($10000$) and $m$ is the number of unique keys. + +--- + +## 5. Bit Manipulation + +::tabs-start + +```python +class MyHashSet: + + def __init__(self): + # key is in the range [1, 1000000] + # 31251 * 32 = 1000032 + self.set = [0] * 31251 + + def add(self, key: int) -> None: + self.set[key // 32] |= self.getMask(key) + + def remove(self, key: int) -> None: + if self.contains(key): + self.set[key // 32] ^= self.getMask(key) + + def contains(self, key: int) -> bool: + return self.set[key // 32] & self.getMask(key) != 0 + + def getMask(self, key: int) -> int: + return 1 << (key % 32) +``` + +```java +public class MyHashSet { + private int[] set; + + public MyHashSet() { + // key is in the range [1, 1000000] + // 31251 * 32 = 1000032 + set = new int[31251]; + } + + public void add(int key) { + set[key / 32] |= getMask(key); + } + + public void remove(int key) { + if (contains(key)) { + set[key / 32] ^= getMask(key); + } + } + + public boolean contains(int key) { + return (set[key / 32] & getMask(key)) != 0; + } + + private int getMask(int key) { + return 1 << (key % 32); + } +} +``` + +```cpp +class MyHashSet { +private: + int set[31251]; + + int getMask(int key) { + return 1 << (key % 32); + } + +public: + MyHashSet() { + // key is in the range [1, 1000000] + // 31251 * 32 = 1000032 + memset(set, 0, sizeof(set)); + } + + void add(int key) { + set[key / 32] |= getMask(key); + } + + void remove(int key) { + if (contains(key)) { + set[key / 32] ^= getMask(key); + } + } + + bool contains(int key) { + return (set[key / 32] & getMask(key)) != 0; + } +}; +``` + +```javascript +class MyHashSet { + constructor() { + // key is in the range [1, 1000000] + // 31251 * 32 = 1000032 + this.set = new Array(31251).fill(0); + } + + /** + * @param {number} key + * @return {void} + */ + add(key) { + this.set[Math.floor(key / 32)] |= this.getMask(key); + } + + /** + * @param {number} key + * @return {void} + */ + remove(key) { + if (this.contains(key)) { + this.set[Math.floor(key / 32)] ^= this.getMask(key); + } + } + + /** + * @param {number} key + * @return {boolean} + */ + contains(key) { + return (this.set[Math.floor(key / 32)] & this.getMask(key)) !== 0; + } + + /** + * @param {number} key + * @return {number} + */ + getMask(key) { + return 1 << (key % 32); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for each function call. +* Space complexity: $O(k)$ + +> Where $k$ is the size of the set $(31251)$. \ No newline at end of file diff --git a/articles/design-linked-list.md b/articles/design-linked-list.md new file mode 100644 index 000000000..618c2335c --- /dev/null +++ b/articles/design-linked-list.md @@ -0,0 +1,1262 @@ +## 1. Singly Linked List + +::tabs-start + +```python +class ListNode: + def __init__(self, val: int): + self.val = val + self.next = None + +class MyLinkedList: + def __init__(self): + self.head = ListNode(0) + self.size = 0 + + def get(self, index: int) -> int: + if index >= self.size: + return -1 + cur = self.head.next + for _ in range(index): + cur = cur.next + return cur.val + + def addAtHead(self, val: int) -> None: + node = ListNode(val) + node.next = self.head.next + self.head.next = node + self.size += 1 + + def addAtTail(self, val: int) -> None: + node = ListNode(val) + cur = self.head + while cur.next: + cur = cur.next + cur.next = node + self.size += 1 + + def addAtIndex(self, index: int, val: int) -> None: + if index > self.size: + return + cur = self.head + for _ in range(index): + cur = cur.next + node = ListNode(val) + node.next = cur.next + cur.next = node + self.size += 1 + + def deleteAtIndex(self, index: int) -> None: + if index >= self.size: + return + cur = self.head + for _ in range(index): + cur = cur.next + cur.next = cur.next.next + self.size -= 1 +``` + +```java +class ListNode { + int val; + ListNode next; + ListNode(int val) { + this.val = val; + this.next = null; + } +} + +public class MyLinkedList { + private ListNode head; + private int size; + MyLinkedList() { + head = new ListNode(0); + size = 0; + } + public int get(int index) { + if (index >= size) return -1; + ListNode cur = head.next; + for (int i = 0; i < index; i++) { + cur = cur.next; + } + return cur.val; + } + public void addAtHead(int val) { + ListNode node = new ListNode(val); + node.next = head.next; + head.next = node; + size++; + } + public void addAtTail(int val) { + ListNode node = new ListNode(val); + ListNode cur = head; + while (cur.next != null) { + cur = cur.next; + } + cur.next = node; + size++; + } + public void addAtIndex(int index, int val) { + if (index > size) return; + ListNode cur = head; + for (int i = 0; i < index; i++) { + cur = cur.next; + } + ListNode node = new ListNode(val); + node.next = cur.next; + cur.next = node; + size++; + } + public void deleteAtIndex(int index) { + if (index >= size) return; + ListNode cur = head; + for (int i = 0; i < index; i++) { + cur = cur.next; + } + cur.next = cur.next.next; + size--; + } +} +``` + +```cpp +class MyLinkedList { + struct ListNode { + int val; + ListNode* next; + ListNode(int val) : val(val), next(nullptr) {} + }; + +public: + ListNode* head; + int size; + MyLinkedList() { + head = new ListNode(0); + size = 0; + } + int get(int index) { + if (index >= size) return -1; + ListNode* cur = head->next; + for (int i = 0; i < index; i++) { + cur = cur->next; + } + return cur->val; + } + void addAtHead(int val) { + ListNode* node = new ListNode(val); + node->next = head->next; + head->next = node; + size++; + } + void addAtTail(int val) { + ListNode* node = new ListNode(val); + ListNode* cur = head; + while (cur->next != nullptr) { + cur = cur->next; + } + cur->next = node; + size++; + } + void addAtIndex(int index, int val) { + if (index > size) return; + ListNode* cur = head; + for (int i = 0; i < index; i++) { + cur = cur->next; + } + ListNode* node = new ListNode(val); + node->next = cur->next; + cur->next = node; + size++; + } + void deleteAtIndex(int index) { + if (index >= size) return; + ListNode* cur = head; + for (int i = 0; i < index; i++) { + cur = cur->next; + } + ListNode* temp = cur->next; + cur->next = cur->next->next; + delete temp; + size--; + } +}; +``` + +```javascript +class ListNode { + /** + * @constructor + * @param {number} val + */ + constructor(val) { + this.val = val; + this.next = null; + } +} + +class MyLinkedList { + constructor() { + this.head = new ListNode(0); + this.size = 0; + } + + /** + * @param {number} index + * @return {number} + */ + get(index) { + if (index >= this.size) return -1; + let cur = this.head.next; + for (let i = 0; i < index; i++) { + cur = cur.next; + } + return cur.val; + } + + /** + * @param {number} val + * @return {void} + */ + addAtHead(val) { + const node = new ListNode(val); + node.next = this.head.next; + this.head.next = node; + this.size++; + } + + /** + * @param {number} val + * @return {void} + */ + addAtTail(val) { + const node = new ListNode(val); + let cur = this.head; + while (cur.next !== null) { + cur = cur.next; + } + cur.next = node; + this.size++; + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + addAtIndex(index, val) { + if (index > this.size) return; + let cur = this.head; + for (let i = 0; i < index; i++) { + cur = cur.next; + } + const node = new ListNode(val); + node.next = cur.next; + cur.next = node; + this.size++; + } + + /** + * @param {number} index + * @return {void} + */ + deleteAtIndex(index) { + if (index >= this.size) return; + let cur = this.head; + for (let i = 0; i < index; i++) { + cur = cur.next; + } + cur.next = cur.next.next; + this.size--; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for $addAtHead()$. + * $O(n)$ time for $get()$, $addAtTail()$, $addAtIndex()$, $deleteAtIndex()$. +* Space complexity: $O(n)$ + +--- + +## 2. Singly Linked List (Optimal) + +::tabs-start + +```python +class ListNode: + def __init__(self, val=0, next=None): + self.val = val + self.next = next + +class MyLinkedList: + def __init__(self): + self.head = ListNode(0) + self.size = 0 + + def getPrev(self, index: int) -> ListNode: + cur = self.head + for _ in range(index): + cur = cur.next + return cur + + def get(self, index: int) -> int: + if index >= self.size: + return -1 + return self.getPrev(index).next.val + + def addAtHead(self, val: int) -> None: + self.addAtIndex(0, val) + + def addAtTail(self, val: int) -> None: + self.addAtIndex(self.size, val) + + def addAtIndex(self, index: int, val: int) -> None: + if index > self.size: + return + prev = self.getPrev(index) + node = ListNode(val, prev.next) + prev.next = node + self.size += 1 + + def deleteAtIndex(self, index: int) -> None: + if index >= self.size: + return + prev = self.getPrev(index) + prev.next = prev.next.next + self.size -= 1 +``` + +```java +class ListNode { + int val; + ListNode next; + ListNode(int val, ListNode next) { + this.val = val; + this.next = next; + } + ListNode(int val) { + this(val, null); + } +} + +public class MyLinkedList { + ListNode head; + int size; + + public MyLinkedList() { + head = new ListNode(0, null); + size = 0; + } + + private ListNode getPrev(int index) { + ListNode cur = head; + for (int i = 0; i < index; i++) { + cur = cur.next; + } + return cur; + } + + public int get(int index) { + if (index >= size) { + return -1; + } + return getPrev(index).next.val; + } + + public void addAtHead(int val) { + addAtIndex(0, val); + } + + public void addAtTail(int val) { + addAtIndex(size, val); + } + + public void addAtIndex(int index, int val) { + if (index > size) { + return; + } + ListNode prev = getPrev(index); + ListNode node = new ListNode(val, prev.next); + prev.next = node; + size++; + } + + public void deleteAtIndex(int index) { + if (index >= size) { + return; + } + ListNode prev = getPrev(index); + prev.next = prev.next.next; + size--; + } +} +``` + +```cpp + +class MyLinkedList { + struct ListNode { + int val; + ListNode* next; + ListNode(int val, ListNode* next) : val(val), next(next) {} + ListNode(int val) : val(val), next(nullptr) {} + }; + +public: + MyLinkedList() { + head = new ListNode(0, nullptr); + size = 0; + } + + int get(int index) { + if (index >= size) return -1; + return getPrev(index)->next->val; + } + + void addAtHead(int val) { + addAtIndex(0, val); + } + + void addAtTail(int val) { + addAtIndex(size, val); + } + + void addAtIndex(int index, int val) { + if (index > size) return; + ListNode* prev = getPrev(index); + ListNode* node = new ListNode(val, prev->next); + prev->next = node; + size++; + } + + void deleteAtIndex(int index) { + if (index >= size) return; + ListNode* prev = getPrev(index); + ListNode* toDelete = prev->next; + prev->next = prev->next->next; + delete toDelete; + size--; + } + +private: + ListNode* head; + int size; + + ListNode* getPrev(int index) { + ListNode* cur = head; + for (int i = 0; i < index; i++) { + cur = cur->next; + } + return cur; + } +}; +``` + +```javascript +class ListNode { + /** + * @constructor + * @param {number} + * @param {ListNode|null} + */ + constructor(val = 0, next = null) { + this.val = val; + this.next = next; + } +} + +class MyLinkedList { + constructor() { + this.head = new ListNode(0); + this.size = 0; + } + + /** + * @param {number} index + * @return {ListNode} + */ + getPrev(index) { + let cur = this.head; + for (let i = 0; i < index; i++) { + cur = cur.next; + } + return cur; + } + + /** + * @param {number} index + * @return {number} + */ + get(index) { + if (index >= this.size) { + return -1; + } + return this.getPrev(index).next.val; + } + + /** + * @param {number} val + * @return {void} + */ + addAtHead(val) { + this.addAtIndex(0, val); + } + + /** + * @param {number} val + * @return {void} + */ + addAtTail(val) { + this.addAtIndex(this.size, val); + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + addAtIndex(index, val) { + if (index > this.size) { + return; + } + let prev = this.getPrev(index); + let node = new ListNode(val, prev.next); + prev.next = node; + this.size++; + } + + /** + * @param {number} index + * @return {void} + */ + deleteAtIndex(index) { + if (index >= this.size) { + return; + } + let prev = this.getPrev(index); + prev.next = prev.next.next; + this.size--; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for $addAtHead()$. + * $O(n)$ time for $get()$, $addAtTail()$, $addAtIndex()$, $deleteAtIndex()$. +* Space complexity: $O(n)$ + +--- + +## 3. Doubly Linked List + +::tabs-start + +```python +class ListNode: + def __init__(self, val): + self.val = val + self.prev = None + self.next = None + +class MyLinkedList: + + def __init__(self): + self.head = ListNode(0) + self.tail = ListNode(0) + self.head.next = self.tail + self.tail.prev = self.head + + def get(self, index: int) -> int: + cur = self.head.next + while cur and index > 0: + cur = cur.next + index -= 1 + if cur and cur != self.tail and index == 0: + return cur.val + return -1 + + def addAtHead(self, val: int) -> None: + node, next, prev = ListNode(val), self.head.next, self.head + prev.next = node + next.prev = node + node.next = next + node.prev = prev + + def addAtTail(self, val: int) -> None: + node, next, prev = ListNode(val), self.tail, self.tail.prev + prev.next = node + next.prev = node + node.next = next + node.prev = prev + + def addAtIndex(self, index: int, val: int) -> None: + cur = self.head.next + while cur and index > 0: + cur = cur.next + index -= 1 + if cur and index == 0: + node, next, prev = ListNode(val), cur, cur.prev + prev.next = node + next.prev = node + node.next = next + node.prev = prev + + + def deleteAtIndex(self, index: int) -> None: + cur = self.head.next + while cur and index > 0: + cur = cur.next + index -= 1 + if cur and cur != self.tail and index == 0: + next, prev = cur.next, cur.prev + next.prev = prev + prev.next = next +``` + +```java +class ListNode { + int val; + ListNode prev; + ListNode next; + + ListNode(int val) { + this.val = val; + this.prev = null; + this.next = null; + } +} + +public class MyLinkedList { + ListNode head; + ListNode tail; + + MyLinkedList() { + head = new ListNode(0); + tail = new ListNode(0); + head.next = tail; + tail.prev = head; + } + + int get(int index) { + ListNode cur = head.next; + while (cur != null && index > 0) { + cur = cur.next; + index--; + } + if (cur != null && cur != tail && index == 0) { + return cur.val; + } + return -1; + } + + void addAtHead(int val) { + ListNode node = new ListNode(val); + ListNode next = head.next; + ListNode prev = head; + prev.next = node; + next.prev = node; + node.next = next; + node.prev = prev; + } + + void addAtTail(int val) { + ListNode node = new ListNode(val); + ListNode next = tail; + ListNode prev = tail.prev; + prev.next = node; + next.prev = node; + node.next = next; + node.prev = prev; + } + + void addAtIndex(int index, int val) { + ListNode cur = head.next; + while (cur != null && index > 0) { + cur = cur.next; + index--; + } + if (cur != null && index == 0) { + ListNode node = new ListNode(val); + ListNode next = cur; + ListNode prev = cur.prev; + prev.next = node; + next.prev = node; + node.next = next; + node.prev = prev; + } + } + + void deleteAtIndex(int index) { + ListNode cur = head.next; + while (cur != null && index > 0) { + cur = cur.next; + index--; + } + if (cur != null && cur != tail && index == 0) { + ListNode next = cur.next; + ListNode prev = cur.prev; + next.prev = prev; + prev.next = next; + } + } +} +``` + +```cpp +class MyLinkedList { + struct ListNode { + int val; + ListNode* prev; + ListNode* next; + ListNode(int val) : val(val), prev(nullptr), next(nullptr) {} + }; +public: + ListNode* head; + ListNode* tail; + + MyLinkedList() { + head = new ListNode(0); + tail = new ListNode(0); + head->next = tail; + tail->prev = head; + } + + int get(int index) { + ListNode* cur = head->next; + while (cur && index > 0) { + cur = cur->next; + index--; + } + if (cur && cur != tail && index == 0) { + return cur->val; + } + return -1; + } + + void addAtHead(int val) { + ListNode* node = new ListNode(val); + ListNode* next = head->next; + ListNode* prev = head; + prev->next = node; + next->prev = node; + node->next = next; + node->prev = prev; + } + + void addAtTail(int val) { + ListNode* node = new ListNode(val); + ListNode* next = tail; + ListNode* prev = tail->prev; + prev->next = node; + next->prev = node; + node->next = next; + node->prev = prev; + } + + void addAtIndex(int index, int val) { + ListNode* cur = head->next; + while (cur && index > 0) { + cur = cur->next; + index--; + } + if (cur && index == 0) { + ListNode* node = new ListNode(val); + ListNode* next = cur; + ListNode* prev = cur->prev; + prev->next = node; + next->prev = node; + node->next = next; + node->prev = prev; + } + } + + void deleteAtIndex(int index) { + ListNode* cur = head->next; + while (cur && index > 0) { + cur = cur->next; + index--; + } + if (cur && cur != tail && index == 0) { + ListNode* next = cur->next; + ListNode* prev = cur->prev; + next->prev = prev; + prev->next = next; + delete cur; + } + } +}; +``` + +```javascript +class ListNode { + /** + * @constructor + * @param {number} val + */ + constructor(val) { + this.val = val; + this.prev = null; + this.next = null; + } +} + +class MyLinkedList { + constructor() { + this.head = new ListNode(0); + this.tail = new ListNode(0); + this.head.next = this.tail; + this.tail.prev = this.head; + } + + /** + * @param {number} index + * @return {number} + */ + get(index) { + let cur = this.head.next; + while (cur && index > 0) { + cur = cur.next; + index--; + } + if (cur && cur !== this.tail && index === 0) { + return cur.val; + } + return -1; + } + + /** + * @param {number} val + * @return {void} + */ + addAtHead(val) { + const node = new ListNode(val); + const next = this.head.next; + const prev = this.head; + prev.next = node; + next.prev = node; + node.next = next; + node.prev = prev; + } + + /** + * @param {number} val + * @return {void} + */ + addAtTail(val) { + const node = new ListNode(val); + const next = this.tail; + const prev = this.tail.prev; + prev.next = node; + next.prev = node; + node.next = next; + node.prev = prev; + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + addAtIndex(index, val) { + let cur = this.head.next; + while (cur && index > 0) { + cur = cur.next; + index--; + } + if (cur && index === 0) { + const node = new ListNode(val); + const next = cur; + const prev = cur.prev; + prev.next = node; + next.prev = node; + node.next = next; + node.prev = prev; + } + } + + /** + * @param {number} index + * @return {void} + */ + deleteAtIndex(index) { + let cur = this.head.next; + while (cur && index > 0) { + cur = cur.next; + index--; + } + if (cur && cur !== this.tail && index === 0) { + const next = cur.next; + const prev = cur.prev; + next.prev = prev; + prev.next = next; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for $addAtHead()$, $addAtTail()$. + * $O(n)$ time for $get()$, $addAtIndex()$, $deleteAtIndex()$. +* Space complexity: $O(n)$ + +--- + +## 4. Doubly Linked List (Optimal) + +::tabs-start + +```python +class ListNode: + def __init__(self, val=0, next=None, prev=None): + self.val = val + self.next = next + self.prev = prev + +class MyLinkedList: + def __init__(self): + self.head = ListNode(0) + self.tail = ListNode(0) + self.head.next = self.tail + self.tail.prev = self.head + self.size = 0 + + def getPrev(self, index: int) -> ListNode: + if index <= self.size // 2: + cur = self.head + for _ in range(index): + cur = cur.next + else: + cur = self.tail + for _ in range(self.size - index + 1): + cur = cur.prev + return cur + + def get(self, index: int) -> int: + if index >= self.size: + return -1 + return self.getPrev(index).next.val + + def addAtHead(self, val: int) -> None: + self.addAtIndex(0, val) + + def addAtTail(self, val: int) -> None: + self.addAtIndex(self.size, val) + + def addAtIndex(self, index: int, val: int) -> None: + if index > self.size: + return + node = ListNode(val) + prev = self.getPrev(index) + next = prev.next + prev.next = node + node.prev = prev + node.next = next + next.prev = node + self.size += 1 + + def deleteAtIndex(self, index: int) -> None: + if index >= self.size: + return + prev = self.getPrev(index) + cur = prev.next + next = cur.next + prev.next = next + next.prev = prev + self.size -= 1 +``` + +```java +class ListNode { + int val; + ListNode next; + ListNode prev; + + ListNode(int val) { + this(val, null, null); + } + + ListNode(int val, ListNode next, ListNode prev) { + this.val = val; + this.next = next; + this.prev = prev; + } +} + +public class MyLinkedList { + ListNode head; + ListNode tail; + int size; + + public MyLinkedList() { + head = new ListNode(0); + tail = new ListNode(0); + head.next = tail; + tail.prev = head; + size = 0; + } + + private ListNode getPrev(int index) { + if (index <= size / 2) { + ListNode cur = head; + for (int i = 0; i < index; i++) { + cur = cur.next; + } + return cur; + } else { + ListNode cur = tail; + for (int i = 0; i < size - index + 1; i++) { + cur = cur.prev; + } + return cur; + } + } + + public int get(int index) { + if (index >= size) return -1; + return getPrev(index).next.val; + } + + public void addAtHead(int val) { + addAtIndex(0, val); + } + + public void addAtTail(int val) { + addAtIndex(size, val); + } + + public void addAtIndex(int index, int val) { + if (index > size) return; + ListNode node = new ListNode(val); + ListNode prev = getPrev(index); + ListNode next = prev.next; + prev.next = node; + node.prev = prev; + node.next = next; + next.prev = node; + size++; + } + + public void deleteAtIndex(int index) { + if (index >= size) return; + ListNode prev = getPrev(index); + ListNode cur = prev.next; + ListNode next = cur.next; + prev.next = next; + next.prev = prev; + size--; + } +} +``` + +```cpp +class MyLinkedList { + struct ListNode { + int val; + ListNode* next; + ListNode* prev; + ListNode(int val = 0, ListNode* next = nullptr, ListNode* prev = nullptr) { + this->val = val; + this->next = next; + this->prev = prev; + } + }; + +public: + ListNode* head; + ListNode* tail; + int size; + + MyLinkedList() { + head = new ListNode(0); + tail = new ListNode(0); + head->next = tail; + tail->prev = head; + size = 0; + } + + ListNode* getPrev(int index) { + if (index <= size / 2) { + ListNode* cur = head; + for (int i = 0; i < index; i++) { + cur = cur->next; + } + return cur; + } else { + ListNode* cur = tail; + for (int i = 0; i < size - index + 1; i++) { + cur = cur->prev; + } + return cur; + } + } + + int get(int index) { + if (index >= size) return -1; + return getPrev(index)->next->val; + } + + void addAtHead(int val) { + addAtIndex(0, val); + } + + void addAtTail(int val) { + addAtIndex(size, val); + } + + void addAtIndex(int index, int val) { + if (index > size) return; + ListNode* node = new ListNode(val); + ListNode* prev = getPrev(index); + ListNode* next = prev->next; + prev->next = node; + node->prev = prev; + node->next = next; + next->prev = node; + size++; + } + + void deleteAtIndex(int index) { + if (index >= size) return; + ListNode* prev = getPrev(index); + ListNode* cur = prev->next; + ListNode* next = cur->next; + prev->next = next; + next->prev = prev; + delete cur; + size--; + } +}; +``` + +```javascript +class ListNode { + /** + * @constructor + * @param {number} + * @param {ListNode|null} + * @param {ListNode|null} + */ + constructor(val = 0, next = null, prev = null) { + this.val = val; + this.next = next; + this.prev = prev; + } +} + +class MyLinkedList { + constructor() { + this.head = new ListNode(0); + this.tail = new ListNode(0); + this.head.next = this.tail; + this.tail.prev = this.head; + this.size = 0; + } + + /** + * @param {number} index + * @return {ListNode} + */ + getPrev(index) { + let cur; + if (index <= this.size / 2) { + cur = this.head; + for (let i = 0; i < index; i++) { + cur = cur.next; + } + } else { + cur = this.tail; + for (let i = 0; i < this.size - index + 1; i++) { + cur = cur.prev; + } + } + return cur; + } + + /** + * @param {number} index + * @return {number} + */ + get(index) { + if (index >= this.size) { + return -1; + } + return this.getPrev(index).next.val; + } + + /** + * @param {number} val + * @return {void} + */ + addAtHead(val) { + this.addAtIndex(0, val); + } + + /** + * @param {number} val + * @return {void} + */ + addAtTail(val) { + this.addAtIndex(this.size, val); + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + addAtIndex(index, val) { + if (index > this.size) { + return; + } + const node = new ListNode(val); + const prev = this.getPrev(index); + const next = prev.next; + prev.next = node; + node.prev = prev; + node.next = next; + next.prev = node; + this.size++; + } + + /** + * @param {number} index + * @return {void} + */ + deleteAtIndex(index) { + if (index >= this.size) { + return; + } + const prev = this.getPrev(index); + const cur = prev.next; + const next = cur.next; + prev.next = next; + next.prev = prev; + this.size--; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for $addAtHead()$, $addAtTail()$. + * $O(n)$ time for $get()$, $addAtIndex()$, $deleteAtIndex()$. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/design-parking-system.md b/articles/design-parking-system.md new file mode 100644 index 000000000..4abdb9aa3 --- /dev/null +++ b/articles/design-parking-system.md @@ -0,0 +1,167 @@ +## 1. Array - I + +::tabs-start + +```python +class ParkingSystem: + + def __init__(self, big: int, medium: int, small: int): + self.spaces = [big, medium, small] + + def addCar(self, carType: int) -> bool: + if self.spaces[carType - 1] > 0: + self.spaces[carType - 1] -= 1 + return True + return False +``` + +```java +public class ParkingSystem { + private int[] spaces; + + public ParkingSystem(int big, int medium, int small) { + spaces = new int[]{big, medium, small}; + } + + public boolean addCar(int carType) { + if (spaces[carType - 1] > 0) { + spaces[carType - 1]--; + return true; + } + return false; + } +} +``` + +```cpp +class ParkingSystem { + int spaces[3]; + +public: + ParkingSystem(int big, int medium, int small) { + spaces[0] = big; + spaces[1] = medium; + spaces[2] = small; + } + + bool addCar(int carType) { + if (spaces[carType - 1] > 0) { + spaces[carType - 1]--; + return true; + } + return false; + } +}; +``` + +```javascript +class ParkingSystem { + /** + * @constructor + * @param {number} big + * @param {number} medium + * @param {number} small + */ + constructor(big, medium, small) { + this.spaces = [big, medium, small]; + } + + /** + * @param {number} carType + * @return {boolean} + */ + addCar(carType) { + if (this.spaces[carType - 1] > 0) { + this.spaces[carType - 1]--; + return true; + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $addCar()$ function call. +* Space complexity: $O(1)$ + +--- + +## 2. Array - II + +::tabs-start + +```python +class ParkingSystem: + + def __init__(self, big: int, medium: int, small: int): + self.spaces = [big, medium, small] + + def addCar(self, carType: int) -> bool: + self.spaces[carType - 1] -= 1 + return self.spaces[carType - 1] >= 0 +``` + +```java +public class ParkingSystem { + int[] spaces; + + public ParkingSystem(int big, int medium, int small) { + spaces = new int[]{big, medium, small}; + } + + public boolean addCar(int carType) { + return spaces[carType - 1]-- > 0; + } +} +``` + +```cpp +class ParkingSystem { + int spaces[3]; + +public: + ParkingSystem(int big, int medium, int small) { + spaces[0] = big, spaces[1] = medium, spaces[2] = small; + } + + bool addCar(int carType) { + return spaces[carType - 1]-- > 0; + } +}; +``` + +```javascript +class ParkingSystem { + /** + * @constructor + * @param {number} big + * @param {number} medium + * @param {number} small + */ + constructor(big, medium, small) { + this.spaces = [big, medium, small]; + } + + /** + * @param {number} carType + * @return {boolean} + */ + addCar(carType) { + return this.spaces[carType - 1]-- > 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $addCar()$ function call. +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/design-twitter-feed.md b/articles/design-twitter-feed.md new file mode 100644 index 000000000..75eade212 --- /dev/null +++ b/articles/design-twitter-feed.md @@ -0,0 +1,1664 @@ +## 1. Sorting + +::tabs-start + +```python +class Twitter: + + def __init__(self): + self.time = 0 + self.followMap = defaultdict(set) + self.tweetMap = defaultdict(list) + + def postTweet(self, userId: int, tweetId: int) -> None: + self.tweetMap[userId].append((self.time, tweetId)) + self.time += 1 + + def getNewsFeed(self, userId: int) -> List[int]: + feed = self.tweetMap[userId][:] + for followeeId in self.followMap[userId]: + feed.extend(self.tweetMap[followeeId]) + + feed.sort(key=lambda x: -x[0]) + return [tweetId for _, tweetId in feed[:10]] + + def follow(self, followerId: int, followeeId: int) -> None: + if followerId != followeeId: + self.followMap[followerId].add(followeeId) + + def unfollow(self, followerId: int, followeeId: int) -> None: + self.followMap[followerId].discard(followeeId) +``` + +```java +public class Twitter { + private int time; + private Map> followMap; + private Map> tweetMap; + + public Twitter() { + time = 0; + followMap = new HashMap<>(); + tweetMap = new HashMap<>(); + } + + public void postTweet(int userId, int tweetId) { + tweetMap.putIfAbsent(userId, new ArrayList<>()); + tweetMap.get(userId).add(new int[]{time++, tweetId}); + } + + public List getNewsFeed(int userId) { + List feed = new ArrayList<>(tweetMap.getOrDefault(userId, new ArrayList<>())); + for (int followeeId : followMap.getOrDefault(userId, new HashSet<>())) { + feed.addAll(tweetMap.getOrDefault(followeeId, new ArrayList<>())); + } + feed.sort((a, b) -> b[0] - a[0]); + List res = new ArrayList<>(); + for (int i = 0; i < Math.min(10, feed.size()); i++) { + res.add(feed.get(i)[1]); + } + return res; + } + + public void follow(int followerId, int followeeId) { + if (followerId != followeeId) { + followMap.putIfAbsent(followerId, new HashSet<>()); + followMap.get(followerId).add(followeeId); + } + } + + public void unfollow(int followerId, int followeeId) { + followMap.getOrDefault(followerId, new HashSet<>()).remove(followeeId); + } +} +``` + +```cpp +class Twitter { + int time; + unordered_map> followMap; + unordered_map>> tweetMap; +public: + Twitter() : time(0) {} + + void postTweet(int userId, int tweetId) { + tweetMap[userId].push_back({time++, tweetId}); + } + + vector getNewsFeed(int userId) { + vector> feed = tweetMap[userId]; + for (int followeeId : followMap[userId]) { + feed.insert(feed.end(), tweetMap[followeeId].begin(), + tweetMap[followeeId].end()); + } + sort(feed.begin(), feed.end(), [](auto &a, auto &b) { + return a.first > b.first; + }); + vector res; + for (int i = 0; i < min(10, (int)feed.size()); ++i) { + res.push_back(feed[i].second); + } + return res; + } + + void follow(int followerId, int followeeId) { + if (followerId != followeeId) { + followMap[followerId].insert(followeeId); + } + } + + void unfollow(int followerId, int followeeId) { + followMap[followerId].erase(followeeId); + } +}; +``` + +```javascript +class Twitter { + constructor() { + this.time = 0; + this.followMap = new Map(); + this.tweetMap = new Map(); + } + + /** + * @param {number} userId + * @param {number} tweetId + * @return {void} + */ + postTweet(userId, tweetId) { + if (!this.tweetMap.has(userId)) this.tweetMap.set(userId, []); + this.tweetMap.get(userId).push([this.time++, tweetId]); + } + + /** + * @param {number} userId + * @return {number[]} + */ + getNewsFeed(userId) { + let feed = [...(this.tweetMap.get(userId) || [])]; + (this.followMap.get(userId) || new Set()).forEach(followeeId => { + feed.push(...(this.tweetMap.get(followeeId) || [])); + }); + feed.sort((a, b) => b[0] - a[0]); + return feed.slice(0, 10).map(x => x[1]); + } + + /** + * @param {number} followerId + * @param {number} followeeId + * @return {void} + */ + follow(followerId, followeeId) { + if (followerId !== followeeId) { + if (!this.followMap.has(followerId)) this.followMap.set(followerId, new Set()); + this.followMap.get(followerId).add(followeeId); + } + } + + /** + * @param {number} followerId + * @param {number} followeeId + * @return {void} + */ + unfollow(followerId, followeeId) { + if (this.followMap.has(followerId)) { + this.followMap.get(followerId).delete(followeeId); + } + } +} +``` + +```csharp +class Twitter { + private int time; + private Dictionary> followMap; + private Dictionary> tweetMap; + + public Twitter() { + time = 0; + followMap = new Dictionary>(); + tweetMap = new Dictionary>(); + } + + public void PostTweet(int userId, int tweetId) { + if (!tweetMap.ContainsKey(userId)) { + tweetMap[userId] = new List<(int, int)>(); + } + tweetMap[userId].Add((time++, tweetId)); + } + + public List GetNewsFeed(int userId) { + var feed = new List<(int, int)>(tweetMap.GetValueOrDefault(userId, new List<(int, int)>())); + foreach (var followeeId in followMap.GetValueOrDefault(userId, new HashSet())) { + feed.AddRange(tweetMap.GetValueOrDefault(followeeId, new List<(int, int)>())); + } + feed.Sort((a, b) => b.Item1 - a.Item1); + var res = new List(); + for (int i = 0; i < Math.Min(10, feed.Count); i++) { + res.Add(feed[i].Item2); + } + return res; + } + + public void Follow(int followerId, int followeeId) { + if (followerId != followeeId) { + if (!followMap.ContainsKey(followerId)) { + followMap[followerId] = new HashSet(); + } + followMap[followerId].Add(followeeId); + } + } + + public void Unfollow(int followerId, int followeeId) { + if (followMap.ContainsKey(followerId)) { + followMap[followerId].Remove(followeeId); + } + } +} +``` + +```go +type Twitter struct { + time int + followMap map[int]map[int]bool + tweetMap map[int][]Tweet +} + +type Tweet struct { + time int + tweetId int +} + +func Constructor() Twitter { + return Twitter{ + time: 0, + followMap: make(map[int]map[int]bool), + tweetMap: make(map[int][]Tweet), + } +} + +func (this *Twitter) PostTweet(userId int, tweetId int) { + this.tweetMap[userId] = append(this.tweetMap[userId], Tweet{this.time, tweetId}) + this.time++ +} + +func (this *Twitter) GetNewsFeed(userId int) []int { + feed := make([]Tweet, 0) + feed = append(feed, this.tweetMap[userId]...) + + if follows, ok := this.followMap[userId]; ok { + for followeeId := range follows { + feed = append(feed, this.tweetMap[followeeId]...) + } + } + + sort.Slice(feed, func(i, j int) bool { + return feed[i].time > feed[j].time + }) + + result := make([]int, 0) + for i := 0; i < len(feed) && i < 10; i++ { + result = append(result, feed[i].tweetId) + } + return result +} + +func (this *Twitter) Follow(followerId int, followeeId int) { + if followerId != followeeId { + if this.followMap[followerId] == nil { + this.followMap[followerId] = make(map[int]bool) + } + this.followMap[followerId][followeeId] = true + } +} + +func (this *Twitter) Unfollow(followerId int, followeeId int) { + if follows, ok := this.followMap[followerId]; ok { + delete(follows, followeeId) + } +} +``` + +```kotlin +class Twitter { + private var time = 0 + private val followMap = HashMap>() + private val tweetMap = HashMap>>() + + fun postTweet(userId: Int, tweetId: Int) { + if (!tweetMap.containsKey(userId)) { + tweetMap[userId] = mutableListOf() + } + tweetMap[userId]?.add(Pair(time, tweetId)) + time++ + } + + fun getNewsFeed(userId: Int): List { + val feed = mutableListOf>() + tweetMap[userId]?.let { feed.addAll(it) } + followMap[userId]?.forEach { followeeId -> + tweetMap[followeeId]?.let { feed.addAll(it) } + } + + return feed.sortedByDescending { it.first } + .take(10) + .map { it.second } + } + + fun follow(followerId: Int, followeeId: Int) { + if (followerId != followeeId) { + if (!followMap.containsKey(followerId)) { + followMap[followerId] = HashSet() + } + followMap[followerId]?.add(followeeId) + } + } + + fun unfollow(followerId: Int, followeeId: Int) { + followMap[followerId]?.remove(followeeId) + } +} +``` + +```swift +class Twitter { + private var time: Int + private var followMap: [Int: Set] + private var tweetMap: [Int: [(Int, Int)]] + + init() { + self.time = 0 + self.followMap = [:] + self.tweetMap = [:] + } + + func postTweet(_ userId: Int, _ tweetId: Int) { + if tweetMap[userId] == nil { + tweetMap[userId] = [] + } + tweetMap[userId]!.append((time, tweetId)) + time += 1 + } + + func getNewsFeed(_ userId: Int) -> [Int] { + var feed = tweetMap[userId] ?? [] + if let followees = followMap[userId] { + for followeeId in followees { + if let tweets = tweetMap[followeeId] { + feed.append(contentsOf: tweets) + } + } + } + feed.sort { $0.0 > $1.0 } + return feed.prefix(10).map { $0.1 } + } + + func follow(_ followerId: Int, _ followeeId: Int) { + if followerId != followeeId { + followMap[followerId, default: Set()].insert(followeeId) + } + } + + func unfollow(_ followerId: Int, _ followeeId: Int) { + followMap[followerId]?.remove(followeeId) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m + t\log t)$ for each $getNewsFeed()$ call and $O(1)$ for remaining methods. +* Space complexity: $O(N * m + N * M)$ + +> Where $n$ is the total number of $followeeIds$ associated with the $userId$, $m$ is the maximum number of tweets by any user, $t$ is the total number of tweets associated with the $userId$ and its $followeeIds$, $N$ is the total number of $userIds$ and $M$ is the maximum number of followees for any user. + +--- + +## 2. Heap + +::tabs-start + +```python +class Twitter: + def __init__(self): + self.count = 0 + self.tweetMap = defaultdict(list) # userId -> list of [count, tweetIds] + self.followMap = defaultdict(set) # userId -> set of followeeId + + def postTweet(self, userId: int, tweetId: int) -> None: + self.tweetMap[userId].append([self.count, tweetId]) + self.count -= 1 + + def getNewsFeed(self, userId: int) -> List[int]: + res = [] + minHeap = [] + + self.followMap[userId].add(userId) + for followeeId in self.followMap[userId]: + if followeeId in self.tweetMap: + index = len(self.tweetMap[followeeId]) - 1 + count, tweetId = self.tweetMap[followeeId][index] + heapq.heappush(minHeap, [count, tweetId, followeeId, index - 1]) + + while minHeap and len(res) < 10: + count, tweetId, followeeId, index = heapq.heappop(minHeap) + res.append(tweetId) + if index >= 0: + count, tweetId = self.tweetMap[followeeId][index] + heapq.heappush(minHeap, [count, tweetId, followeeId, index - 1]) + return res + + def follow(self, followerId: int, followeeId: int) -> None: + self.followMap[followerId].add(followeeId) + + def unfollow(self, followerId: int, followeeId: int) -> None: + if followeeId in self.followMap[followerId]: + self.followMap[followerId].remove(followeeId) +``` + +```java +public class Twitter { + + private int count; + private Map> tweetMap; + private Map> followMap; + + public Twitter() { + count = 0; + tweetMap = new HashMap<>(); + followMap = new HashMap<>(); + } + + public void postTweet(int userId, int tweetId) { + tweetMap.computeIfAbsent(userId, k -> new ArrayList<>()).add(new int[]{count--, tweetId}); + } + + public List getNewsFeed(int userId) { + List res = new ArrayList<>(); + PriorityQueue minHeap = new PriorityQueue<>(Comparator.comparingInt(a -> a[0])); + + followMap.computeIfAbsent(userId, k -> new HashSet<>()).add(userId); + for (int followeeId : followMap.get(userId)) { + if (tweetMap.containsKey(followeeId)) { + List tweets = tweetMap.get(followeeId); + int index = tweets.size() - 1; + int[] tweet = tweets.get(index); + minHeap.offer(new int[]{tweet[0], tweet[1], followeeId, index}); + } + } + + while (!minHeap.isEmpty() && res.size() < 10) { + int[] curr = minHeap.poll(); + res.add(curr[1]); + int index = curr[3]; + if (index > 0) { + int[] tweet = tweetMap.get(curr[2]).get(index - 1); + minHeap.offer(new int[]{tweet[0], tweet[1], curr[2], index - 1}); + } + } + return res; + } + + public void follow(int followerId, int followeeId) { + followMap.computeIfAbsent(followerId, k -> new HashSet<>()).add(followeeId); + } + + public void unfollow(int followerId, int followeeId) { + followMap.computeIfPresent(followerId, (k, v) -> { + v.remove(followeeId); + return v; + }); + } +} +``` + +```cpp +class Twitter { + int count; + unordered_map>> tweetMap; + unordered_map> followMap; + +public: + Twitter() { + count = 0; + } + + void postTweet(int userId, int tweetId) { + tweetMap[userId].push_back({count++, tweetId}); + } + + vector getNewsFeed(int userId) { + vector res; + auto compare = [](const vector& a, const vector& b) { + return a[0] < b[0]; + }; + priority_queue, vector>, decltype(compare)> minHeap(compare); + + followMap[userId].insert(userId); + for (int followeeId : followMap[userId]) { + if (tweetMap.count(followeeId)) { + const vector>& tweets = tweetMap[followeeId]; + int index = tweets.size() - 1; + minHeap.push({tweets[index][0], tweets[index][1], followeeId, index}); + } + } + + while (!minHeap.empty() && res.size() < 10) { + vector curr = minHeap.top(); + minHeap.pop(); + res.push_back(curr[1]); + int index = curr[3]; + if (index > 0) { + const vector& tweet = tweetMap[curr[2]][index - 1]; + minHeap.push({tweet[0], tweet[1], curr[2], index - 1}); + } + } + return res; + } + + void follow(int followerId, int followeeId) { + followMap[followerId].insert(followeeId); + } + + void unfollow(int followerId, int followeeId) { + followMap[followerId].erase(followeeId); + } +}; +``` + +```javascript +/** + * const { PriorityQueue } = require('@datastructures-js/priority-queue'); + */ + +class Twitter { + constructor() { + this.count = 0; + this.tweetMap = new Map(); // userId -> array of [count, tweetId] + this.followMap = new Map(); // userId -> set of followeeIds + } + + /** + * @param {number} userId + * @param {number} tweetId + * @return {void} + */ + postTweet(userId, tweetId) { + if (!this.tweetMap.has(userId)) { + this.tweetMap.set(userId, []); + } + this.tweetMap.get(userId).push([this.count, tweetId]); + this.count -= 1; + } + + /** + * @param {number} userId + * @return {number[]} + */ + getNewsFeed(userId) { + const res = []; + if (!this.followMap.has(userId)) { + this.followMap.set(userId, new Set()); + } + this.followMap.get(userId).add(userId); + const minHeap = new PriorityQueue( + (a, b) => a[0] - b[0] + ); + + for (const followeeId of this.followMap.get(userId)) { + if (this.tweetMap.has(followeeId)) { + const tweets = this.tweetMap.get(followeeId); + const index = tweets.length - 1; + const [count, tweetId] = tweets[index]; + minHeap.enqueue([count, tweetId, followeeId, index - 1]); + } + } + + while (!minHeap.isEmpty() && res.length < 10) { + const [count, tweetId, followeeId, nextIndex] = minHeap.dequeue(); + res.push(tweetId); + if (nextIndex >= 0) { + const [olderCount, olderTweetId] = this.tweetMap.get(followeeId)[nextIndex]; + minHeap.enqueue([olderCount, olderTweetId, followeeId, nextIndex - 1]); + } + } + + return res; + } + + /** + * @param {number} followerId + * @param {number} followeeId + * @return {void} + */ + follow(followerId, followeeId) { + if (!this.followMap.has(followerId)) { + this.followMap.set(followerId, new Set()); + } + this.followMap.get(followerId).add(followeeId); + } + + /** + * @param {number} followerId + * @param {number} followeeId + * @return {void} + */ + unfollow(followerId, followeeId) { + if (this.followMap.has(followerId)) { + this.followMap.get(followerId).delete(followeeId); + } + } +} +``` + +```csharp +class Twitter { + private int count; + private Dictionary> tweetMap; + private Dictionary> followMap; + + public Twitter() { + count = 0; + tweetMap = new Dictionary>(); + followMap = new Dictionary>(); + } + + public void PostTweet(int userId, int tweetId) { + if (!tweetMap.ContainsKey(userId)) { + tweetMap[userId] = new List(); + } + tweetMap[userId].Add(new int[] { count++, tweetId }); + } + + public List GetNewsFeed(int userId) { + List res = new List(); + PriorityQueue minHeap = new PriorityQueue(); + + if (!followMap.ContainsKey(userId)) { + followMap[userId] = new HashSet(); + } + followMap[userId].Add(userId); + + foreach (int followeeId in followMap[userId]) { + if (tweetMap.ContainsKey(followeeId) && tweetMap[followeeId].Count > 0) { + List tweets = tweetMap[followeeId]; + int index = tweets.Count - 1; + int[] latestTweet = tweets[index]; + minHeap.Enqueue(new int[] { latestTweet[0], latestTweet[1], followeeId, index }, -latestTweet[0]); + } + } + + while (minHeap.Count > 0 && res.Count < 10) { + int[] curr = minHeap.Dequeue(); + res.Add(curr[1]); + int index = curr[3]; + if (index > 0) { + int[] tweet = tweetMap[curr[2]][index - 1]; + minHeap.Enqueue(new int[] { tweet[0], tweet[1], curr[2], index - 1 }, -tweet[0]); + } + } + + return res; + } + + public void Follow(int followerId, int followeeId) { + if (!followMap.ContainsKey(followerId)) { + followMap[followerId] = new HashSet(); + } + followMap[followerId].Add(followeeId); + } + + public void Unfollow(int followerId, int followeeId) { + if (followMap.ContainsKey(followerId)) { + followMap[followerId].Remove(followeeId); + } + } +} +``` + +```go +type Twitter struct { + count int + tweetMap map[int][][]int // userId -> list of [count, tweetId] + followMap map[int]map[int]bool // userId -> set of followeeId +} + +func Constructor() Twitter { + return Twitter{ + count: 0, + tweetMap: make(map[int][][]int), + followMap: make(map[int]map[int]bool), + } +} + +func (this *Twitter) PostTweet(userId int, tweetId int) { + if this.tweetMap[userId] == nil { + this.tweetMap[userId] = make([][]int, 0) + } + this.tweetMap[userId] = append(this.tweetMap[userId], []int{this.count, tweetId}) + this.count-- +} + +func (this *Twitter) GetNewsFeed(userId int) []int { + res := make([]int, 0) + + minHeap := priorityqueue.NewWith(func(a, b interface{}) int { + return a.([]int)[0] - b.([]int)[0] + }) + + if this.followMap[userId] == nil { + this.followMap[userId] = make(map[int]bool) + } + this.followMap[userId][userId] = true + + for followeeId := range this.followMap[userId] { + tweets := this.tweetMap[followeeId] + if len(tweets) > 0 { + index := len(tweets) - 1 + count, tweetId := tweets[index][0], tweets[index][1] + minHeap.Enqueue([]int{count, tweetId, followeeId, index - 1}) + } + } + + for minHeap.Size() > 0 && len(res) < 10 { + item, _ := minHeap.Dequeue() + curr := item.([]int) + count, tweetId, followeeId, index := curr[0], curr[1], curr[2], curr[3] + + res = append(res, tweetId) + + if index >= 0 { + tweets := this.tweetMap[followeeId] + count, tweetId = tweets[index][0], tweets[index][1] + minHeap.Enqueue([]int{count, tweetId, followeeId, index - 1}) + } + } + + return res +} + +func (this *Twitter) Follow(followerId int, followeeId int) { + if this.followMap[followerId] == nil { + this.followMap[followerId] = make(map[int]bool) + } + this.followMap[followerId][followeeId] = true +} + +func (this *Twitter) Unfollow(followerId int, followeeId int) { + if this.followMap[followerId] != nil { + delete(this.followMap[followerId], followeeId) + } +} +``` + +```kotlin +class Twitter { + private var count = 0 + private val tweetMap = HashMap>() // userId -> list of [count, tweetId] + private val followMap = HashMap>() // userId -> set of followeeId + + fun postTweet(userId: Int, tweetId: Int) { + if (!tweetMap.containsKey(userId)) { + tweetMap[userId] = mutableListOf() + } + tweetMap[userId]?.add(intArrayOf(count, tweetId)) + count-- + } + + fun getNewsFeed(userId: Int): List { + val res = mutableListOf() + val minHeap = PriorityQueue(compareBy { it[0] }) + + if (!followMap.containsKey(userId)) { + followMap[userId] = HashSet() + } + followMap[userId]?.add(userId) + + followMap[userId]?.forEach { followeeId -> + tweetMap[followeeId]?.let { tweets -> + if (tweets.isNotEmpty()) { + val index = tweets.size - 1 + val (count, tweetId) = tweets[index] + minHeap.add(intArrayOf(count, tweetId, followeeId, index - 1)) + } + } + } + + while (minHeap.isNotEmpty() && res.size < 10) { + val (count, tweetId, followeeId, index) = minHeap.poll() + res.add(tweetId) + + if (index >= 0) { + val tweets = tweetMap[followeeId]!! + val (nextCount, nextTweetId) = tweets[index] + minHeap.add(intArrayOf(nextCount, nextTweetId, followeeId, index - 1)) + } + } + + return res + } + + fun follow(followerId: Int, followeeId: Int) { + if (!followMap.containsKey(followerId)) { + followMap[followerId] = HashSet() + } + followMap[followerId]?.add(followeeId) + } + + fun unfollow(followerId: Int, followeeId: Int) { + followMap[followerId]?.remove(followeeId) + } +} +``` + +```swift +class Twitter { + private var count: Int + private var tweetMap: [Int: [(Int, Int)]] // userId -> list of (count, tweetId) + private var followMap: [Int: Set] // userId -> set of followeeId + + init() { + self.count = 0 + self.tweetMap = [:] + self.followMap = [:] + } + + func postTweet(_ userId: Int, _ tweetId: Int) { + tweetMap[userId, default: []].append((count, tweetId)) + count -= 1 + } + + func getNewsFeed(_ userId: Int) -> [Int] { + var res = [Int]() + var minHeap = Heap() + + followMap[userId, default: Set()].insert(userId) + if let followees = followMap[userId] { + for followee in followees { + if let tweets = tweetMap[followee], !tweets.isEmpty { + let index = tweets.count - 1 + let (cnt, tweetId) = tweets[index] + minHeap.insert( + Item( + count: cnt, tweetId: tweetId, + followeeId: followee, index: index - 1 + ) + ) + } + } + } + + while !minHeap.isEmpty && res.count < 10 { + let entry = minHeap.popMin()! + res.append(entry.tweetId) + if entry.index >= 0, let tweets = tweetMap[entry.followeeId] { + let (cnt, tweetId) = tweets[entry.index] + minHeap.insert( + Item( + count: cnt, tweetId: tweetId, + followeeId: entry.followeeId, index: entry.index - 1 + ) + ) + } + } + return res + } + + func follow(_ followerId: Int, _ followeeId: Int) { + followMap[followerId, default: Set()].insert(followeeId) + } + + func unfollow(_ followerId: Int, _ followeeId: Int) { + followMap[followerId]?.remove(followeeId) + } +} + +struct Item: Comparable { + let count: Int + let tweetId: Int + let followeeId: Int + let index: Int + + static func < (lhs: Item, rhs: Item) -> Bool { + return lhs.count < rhs.count + } + + static func == (lhs: Item, rhs: Item) -> Bool { + return lhs.count == rhs.count + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ for each $getNewsFeed()$ call and $O(1)$ for remaining methods. +* Space complexity: $O(N * m + N * M + n)$ + +> Where $n$ is the total number of $followeeIds$ associated with the $userId$, $m$ is the maximum number of tweets by any user, $N$ is the total number of $userIds$ and $M$ is the maximum number of followees for any user. + +--- + +## 3. Heap (Optimal) + +::tabs-start + +```python +class Twitter: + + def __init__(self): + self.count = 0 + self.tweetMap = defaultdict(list) # userId -> list of [count, tweetIds] + self.followMap = defaultdict(set) # userId -> set of followeeId + + def postTweet(self, userId: int, tweetId: int) -> None: + self.tweetMap[userId].append([self.count, tweetId]) + if len(self.tweetMap[userId]) > 10: + self.tweetMap[userId].pop(0) + self.count -= 1 + + def getNewsFeed(self, userId: int) -> List[int]: + res = [] + minHeap = [] + self.followMap[userId].add(userId) + if len(self.followMap[userId]) >= 10: + maxHeap = [] + for followeeId in self.followMap[userId]: + if followeeId in self.tweetMap: + index = len(self.tweetMap[followeeId]) - 1 + count, tweetId = self.tweetMap[followeeId][index] + heapq.heappush(maxHeap, [-count, tweetId, followeeId, index - 1]) + if len(maxHeap) > 10: + heapq.heappop(maxHeap) + while maxHeap: + count, tweetId, followeeId, index = heapq.heappop(maxHeap) + heapq.heappush(minHeap, [-count, tweetId, followeeId, index]) + else: + for followeeId in self.followMap[userId]: + if followeeId in self.tweetMap: + index = len(self.tweetMap[followeeId]) - 1 + count, tweetId = self.tweetMap[followeeId][index] + heapq.heappush(minHeap, [count, tweetId, followeeId, index - 1]) + + while minHeap and len(res) < 10: + count, tweetId, followeeId, index = heapq.heappop(minHeap) + res.append(tweetId) + if index >= 0: + count, tweetId = self.tweetMap[followeeId][index] + heapq.heappush(minHeap, [count, tweetId, followeeId, index - 1]) + + return res + + def follow(self, followerId: int, followeeId: int) -> None: + self.followMap[followerId].add(followeeId) + + def unfollow(self, followerId: int, followeeId: int) -> None: + if followeeId in self.followMap[followerId]: + self.followMap[followerId].remove(followeeId) +``` + +```java +public class Twitter { + + private int count; + private Map> tweetMap; + private Map> followMap; + + public Twitter() { + this.count = 0; + this.tweetMap = new HashMap<>(); + this.followMap = new HashMap<>(); + } + + public void postTweet(int userId, int tweetId) { + tweetMap.computeIfAbsent(userId, k -> new ArrayList<>()) + .add(new int[]{count, tweetId}); + if (tweetMap.get(userId).size() > 10) { + tweetMap.get(userId).remove(0); + } + count--; + } + + public List getNewsFeed(int userId) { + List res = new ArrayList<>(); + PriorityQueue minHeap = new PriorityQueue<>( + (a, b) -> Integer.compare(a[0], b[0]) + ); + followMap.computeIfAbsent(userId, k -> new HashSet<>()).add(userId); + if (followMap.get(userId).size() >= 10) { + PriorityQueue maxHeap = new PriorityQueue<>( + (a, b) -> Integer.compare(a[0], b[0]) + ); + for (int followeeId : followMap.get(userId)) { + if (!tweetMap.containsKey(followeeId)) continue; + List tweets = tweetMap.get(followeeId); + int index = tweets.size() - 1; + int[] tweet = tweets.get(index); + maxHeap.offer(new int[]{-tweet[0], tweet[1], followeeId, index - 1}); + if (maxHeap.size() > 10) { + maxHeap.poll(); + } + } + while (!maxHeap.isEmpty()) { + int[] top = maxHeap.poll(); + minHeap.offer(new int[]{-top[0], top[1], top[2], top[3]}); + } + } else { + for (int followeeId : followMap.get(userId)) { + if (!tweetMap.containsKey(followeeId)) continue; + List tweets = tweetMap.get(followeeId); + int index = tweets.size() - 1; + int[] tweet = tweets.get(index); + minHeap.offer(new int[]{tweet[0], tweet[1], followeeId, index - 1}); + } + } + + while (!minHeap.isEmpty() && res.size() < 10) { + int[] top = minHeap.poll(); + res.add(top[1]); + int nextIndex = top[3]; + if (nextIndex >= 0) { + List tweets = tweetMap.get(top[2]); + int[] nextTweet = tweets.get(nextIndex); + minHeap.offer(new int[]{nextTweet[0], nextTweet[1], top[2], nextIndex - 1}); + } + } + return res; + } + + public void follow(int followerId, int followeeId) { + followMap.computeIfAbsent(followerId, k -> new HashSet<>()).add(followeeId); + } + + public void unfollow(int followerId, int followeeId) { + if (followMap.containsKey(followerId)) { + followMap.get(followerId).remove(followeeId); + } + } +} +``` + +```cpp +class Twitter { +public: + int count; + unordered_map>> tweetMap; + unordered_map> followMap; + + Twitter() { + count = 0; + } + + void postTweet(int userId, int tweetId) { + tweetMap[userId].push_back({count, tweetId}); + if (tweetMap[userId].size() > 10) { + tweetMap[userId].erase(tweetMap[userId].begin()); + } + count--; + } + + vector getNewsFeed(int userId) { + vector res; + followMap[userId].insert(userId); + priority_queue, vector>, greater>> minHeap; + if (followMap[userId].size() >= 10) { + priority_queue> maxHeap; + for (auto f : followMap[userId]) { + if (!tweetMap.count(f)) continue; + int idx = tweetMap[f].size() - 1; + auto &p = tweetMap[f][idx]; + maxHeap.push({-p.first, p.second, f, idx - 1}); + if (maxHeap.size() > 10) maxHeap.pop(); + } + while (!maxHeap.empty()) { + auto t = maxHeap.top(); + maxHeap.pop(); + minHeap.push({-t[0], t[1], t[2], t[3]}); + } + } else { + for (auto f : followMap[userId]) { + if (!tweetMap.count(f)) continue; + int idx = tweetMap[f].size() - 1; + auto &p = tweetMap[f][idx]; + minHeap.push({p.first, p.second, f, idx - 1}); + } + } + while (!minHeap.empty() && res.size() < 10) { + auto t = minHeap.top(); + minHeap.pop(); + res.push_back(t[1]); + int idx = t[3]; + if (idx >= 0) { + auto &p = tweetMap[t[2]][idx]; + minHeap.push({p.first, p.second, t[2], idx - 1}); + } + } + return res; + } + + void follow(int followerId, int followeeId) { + followMap[followerId].insert(followeeId); + } + + void unfollow(int followerId, int followeeId) { + if (followMap[followerId].count(followeeId)) { + followMap[followerId].erase(followeeId); + } + } +}; +``` + +```javascript +/** + * const { PriorityQueue } = require('@datastructures-js/priority-queue'); + */ +class Twitter { + constructor() { + this.count = 0; + this.tweetMap = new Map(); + this.followMap = new Map(); + } + + /** + * @param {number} userId + * @param {number} tweetId + * @return {void} + */ + postTweet(userId, tweetId) { + if (!this.tweetMap.has(userId)) { + this.tweetMap.set(userId, []); + } + const tweets = this.tweetMap.get(userId); + tweets.push([this.count, tweetId]); + if (tweets.length > 10) { + tweets.shift(); + } + this.count--; + } + + /** + * @param {number} userId + * @return {number[]} + */ + getNewsFeed(userId) { + const res = []; + if (!this.followMap.has(userId)) { + this.followMap.set(userId, new Set()); + } + this.followMap.get(userId).add(userId); + const minHeap = new PriorityQueue((a, b) => a[0] - b[0]); + + if (this.followMap.get(userId).size >= 10) { + const maxHeap = new PriorityQueue((a, b) => a[0] - b[0]); + for (const followeeId of this.followMap.get(userId)) { + if (!this.tweetMap.has(followeeId)) continue; + const tweets = this.tweetMap.get(followeeId); + const idx = tweets.length - 1; + const [cnt, tId] = tweets[idx]; + maxHeap.enqueue([-cnt, tId, followeeId, idx - 1]); + if (maxHeap.size() > 10) { + maxHeap.dequeue(); + } + } + while (maxHeap.size() > 0) { + const [negCount, tId, fId, idx] = maxHeap.dequeue(); + minHeap.enqueue([-negCount, tId, fId, idx]); + } + + } else { + for (const followeeId of this.followMap.get(userId)) { + if (!this.tweetMap.has(followeeId)) continue; + const tweets = this.tweetMap.get(followeeId); + const idx = tweets.length - 1; + const [cnt, tId] = tweets[idx]; + minHeap.enqueue([cnt, tId, followeeId, idx - 1]); + } + } + + while (minHeap.size() > 0 && res.length < 10) { + const [cnt, tId, fId, idx] = minHeap.dequeue(); + res.push(tId); + if (idx >= 0) { + const [olderCount, olderTId] = this.tweetMap.get(fId)[idx]; + minHeap.enqueue([olderCount, olderTId, fId, idx - 1]); + } + } + return res; + } + + /** + * @param {number} followerId + * @param {number} followeeId + * @return {void} + */ + follow(followerId, followeeId) { + if (!this.followMap.has(followerId)) { + this.followMap.set(followerId, new Set()); + } + this.followMap.get(followerId).add(followeeId); + } + + /** + * @param {number} followerId + * @param {number} followeeId + * @return {void} + */ + unfollow(followerId, followeeId) { + if (this.followMap.has(followerId)) { + this.followMap.get(followerId).delete(followeeId); + } + } +} +``` + +```csharp +public class Twitter +{ + private int count; + private Dictionary> tweetMap; // userId -> list of (count, tweetId) + private Dictionary> followMap; // userId -> set of followeeId + + public Twitter() + { + count = 0; + tweetMap = new Dictionary>(); + followMap = new Dictionary>(); + } + + public void PostTweet(int userId, int tweetId) + { + if (!tweetMap.ContainsKey(userId)) + { + tweetMap[userId] = new List<(int, int)>(); + } + tweetMap[userId].Add((count, tweetId)); + if (tweetMap[userId].Count > 10) + { + tweetMap[userId].RemoveAt(0); + } + count--; + } + + public List GetNewsFeed(int userId) + { + var res = new List(); + if (!followMap.ContainsKey(userId)) + { + followMap[userId] = new HashSet(); + } + followMap[userId].Add(userId); + var minHeap = new PriorityQueue<(int, int, int, int), int>(); + if (followMap[userId].Count >= 10) + { + var maxHeap = new PriorityQueue<(int, int, int, int), int>(); + foreach (var fId in followMap[userId]) + { + if (tweetMap.ContainsKey(fId)) + { + var tweets = tweetMap[fId]; + int idx = tweets.Count - 1; + var (c, tId) = tweets[idx]; + maxHeap.Enqueue( + ( -c, tId, fId, idx - 1 ), + -c + ); + if (maxHeap.Count > 10) + { + maxHeap.Dequeue(); + } + } + } + + while (maxHeap.Count > 0) + { + var item = maxHeap.Dequeue(); + var negCount = item.Item1; + var tId = item.Item2; + var fId = item.Item3; + var idx = item.Item4; + + int originalCount = -negCount; + minHeap.Enqueue( + ( originalCount, tId, fId, idx ), + originalCount + ); + } + } + else + { + foreach (var fId in followMap[userId]) + { + if (tweetMap.ContainsKey(fId)) + { + var tweets = tweetMap[fId]; + int idx = tweets.Count - 1; + var (c, tId) = tweets[idx]; + minHeap.Enqueue( + ( c, tId, fId, idx - 1 ), + c + ); + } + } + } + + while (minHeap.Count > 0 && res.Count < 10) + { + var (c, tId, fId, idx) = minHeap.Dequeue(); + res.Add(tId); + if (idx >= 0) + { + var (olderCount, olderTid) = tweetMap[fId][idx]; + minHeap.Enqueue( + ( olderCount, olderTid, fId, idx - 1 ), + olderCount + ); + } + } + + return res; + } + + public void Follow(int followerId, int followeeId) + { + if (!followMap.ContainsKey(followerId)) + { + followMap[followerId] = new HashSet(); + } + followMap[followerId].Add(followeeId); + } + + public void Unfollow(int followerId, int followeeId) + { + if (followMap.ContainsKey(followerId)) + { + followMap[followerId].Remove(followeeId); + } + } +} +``` + +```go +type Twitter struct { + count int + tweetMap map[int][][2]int // userId -> [count, tweetId] + followMap map[int]map[int]bool // userId -> set of followeeIds +} + +func Constructor() Twitter { + return Twitter{ + count: 0, + tweetMap: make(map[int][][2]int), + followMap: make(map[int]map[int]bool), + } +} + +func (t *Twitter) PostTweet(userId int, tweetId int) { + if _, exists := t.tweetMap[userId]; !exists { + t.tweetMap[userId] = make([][2]int, 0, 10) + } + t.tweetMap[userId] = append(t.tweetMap[userId], [2]int{t.count, tweetId}) + if len(t.tweetMap[userId]) > 10 { + t.tweetMap[userId] = t.tweetMap[userId][1:] + } + t.count-- +} + +func maxHeapComparator(a, b interface{}) int { + A := a.([]int) + B := b.([]int) + switch { + case A[0] < B[0]: + return -1 + case A[0] > B[0]: + return 1 + default: + return 0 + } +} + +func minHeapComparator(a, b interface{}) int { + A := a.([]int) + B := b.([]int) + switch { + case A[0] < B[0]: + return -1 + case A[0] > B[0]: + return 1 + default: + return 0 + } +} + +func (t *Twitter) GetNewsFeed(userId int) []int { + res := []int{} + if _, ok := t.followMap[userId]; !ok { + t.followMap[userId] = make(map[int]bool) + } + t.followMap[userId][userId] = true + minHeap := priorityqueue.NewWith(minHeapComparator) + + if len(t.followMap[userId]) >= 10 { + maxHeap := priorityqueue.NewWith(maxHeapComparator) + for fId := range t.followMap[userId] { + if tweets, exists := t.tweetMap[fId]; exists && len(tweets) > 0 { + idx := len(tweets) - 1 + c := tweets[idx][0] + tId := tweets[idx][1] + maxHeap.Enqueue([]int{-c, tId, fId, idx - 1}) + if maxHeap.Size() > 10 { + maxHeap.Dequeue() + } + } + } + + for !maxHeap.Empty() { + item, _ := maxHeap.Dequeue() + arr := item.([]int) + negCount := arr[0] + tId := arr[1] + fId := arr[2] + nextIdx := arr[3] + realCount := -negCount + minHeap.Enqueue([]int{realCount, tId, fId, nextIdx}) + } + } else { + for fId := range t.followMap[userId] { + if tweets, exists := t.tweetMap[fId]; exists && len(tweets) > 0 { + idx := len(tweets) - 1 + c := tweets[idx][0] + tId := tweets[idx][1] + minHeap.Enqueue([]int{c, tId, fId, idx - 1}) + } + } + } + + for !minHeap.Empty() && len(res) < 10 { + top, _ := minHeap.Dequeue() + arr := top.([]int) + tId := arr[1] + fId := arr[2] + nextIdx := arr[3] + + res = append(res, tId) + if nextIdx >= 0 { + older := t.tweetMap[fId][nextIdx] + minHeap.Enqueue([]int{older[0], older[1], fId, nextIdx - 1}) + } + } + + return res +} + +func (t *Twitter) Follow(followerId, followeeId int) { + if _, ok := t.followMap[followerId]; !ok { + t.followMap[followerId] = make(map[int]bool) + } + t.followMap[followerId][followeeId] = true +} + +func (t *Twitter) Unfollow(followerId, followeeId int) { + if _, ok := t.followMap[followerId]; ok { + delete(t.followMap[followerId], followeeId) + } +} +``` + +```kotlin +class Twitter { + private var count = 0 + private val tweetMap = mutableMapOf>>() + private val followMap = mutableMapOf>() + + fun postTweet(userId: Int, tweetId: Int) { + if (!tweetMap.containsKey(userId)) { + tweetMap[userId] = mutableListOf() + } + val tweets = tweetMap[userId]!! + tweets.add(Pair(count, tweetId)) + if (tweets.size > 10) { + tweets.removeAt(0) + } + count-- + } + + fun getNewsFeed(userId: Int): List { + val res = mutableListOf() + if (!followMap.containsKey(userId)) { + followMap[userId] = mutableSetOf() + } + followMap[userId]!!.add(userId) + val minHeap = PriorityQueue> { a, b -> a[0].compareTo(b[0]) } + if (followMap[userId]!!.size >= 10) { + val maxHeap = PriorityQueue> { a, b -> a[0].compareTo(b[0]) } + for (fId in followMap[userId]!!) { + if (!tweetMap.containsKey(fId)) continue + val tweets = tweetMap[fId]!! + if (tweets.isEmpty()) continue + val idx = tweets.size - 1 + val (c, tId) = tweets[idx] + maxHeap.offer(listOf(-c, tId, fId, idx - 1)) + if (maxHeap.size > 10) { + maxHeap.poll() + } + } + while (maxHeap.isNotEmpty()) { + val (negCount, tId, fId, nextIdx) = maxHeap.poll() + val realCount = -negCount + minHeap.offer(listOf(realCount, tId, fId, nextIdx)) + } + } else { + for (fId in followMap[userId]!!) { + if (!tweetMap.containsKey(fId)) continue + val tweets = tweetMap[fId]!! + if (tweets.isEmpty()) continue + val idx = tweets.size - 1 + val (c, tId) = tweets[idx] + minHeap.offer(listOf(c, tId, fId, idx - 1)) + } + } + + while (minHeap.isNotEmpty() && res.size < 10) { + val (c, tId, fId, idx) = minHeap.poll() + res.add(tId) + if (idx >= 0) { + val (olderCount, olderTid) = tweetMap[fId]!![idx] + minHeap.offer(listOf(olderCount, olderTid, fId, idx - 1)) + } + } + + return res + } + + fun follow(followerId: Int, followeeId: Int) { + if (!followMap.containsKey(followerId)) { + followMap[followerId] = mutableSetOf() + } + followMap[followerId]!!.add(followeeId) + } + + fun unfollow(followerId: Int, followeeId: Int) { + if (followMap.containsKey(followerId)) { + followMap[followerId]!!.remove(followeeId) + } + } +} +``` + +```swift +struct Item: Comparable { + let count: Int + let tweetId: Int + let followeeId: Int + let index: Int + + static func < (lhs: Item, rhs: Item) -> Bool { + return lhs.count < rhs.count + } + + static func == (lhs: Item, rhs: Item) -> Bool { + return lhs.count == rhs.count && + lhs.tweetId == rhs.tweetId && + lhs.followeeId == rhs.followeeId && + lhs.index == rhs.index + } +} + +class Twitter { + private var count: Int + private var tweetMap: [Int: [(Int, Int)]] // userId -> list of (count, tweetId) + private var followMap: [Int: Set] // userId -> set of followeeId + + init() { + self.count = 0 + self.tweetMap = [:] + self.followMap = [:] + } + + func postTweet(_ userId: Int, _ tweetId: Int) { + tweetMap[userId, default: []].append((count, tweetId)) + if tweetMap[userId]!.count > 10 { + tweetMap[userId]!.removeFirst() + } + count -= 1 + } + + func getNewsFeed(_ userId: Int) -> [Int] { + var res = [Int]() + var minHeap = Heap() + followMap[userId, default: Set()].insert(userId) + + if followMap[userId]!.count >= 10 { + var maxHeap = Heap() + for followeeId in followMap[userId]! { + if let tweets = tweetMap[followeeId], !tweets.isEmpty { + let index = tweets.count - 1 + let (cnt, tweetId) = tweets[index] + maxHeap.insert( + Item( + count: cnt, tweetId: tweetId, + followeeId: followeeId, index: index - 1 + ) + ) + if maxHeap.count > 10 { + maxHeap.removeMax() + } + } + } + while !maxHeap.isEmpty { + let item = maxHeap.popMax()! + minHeap.insert(item) + } + } else { + for followeeId in followMap[userId]! { + if let tweets = tweetMap[followeeId], !tweets.isEmpty { + let index = tweets.count - 1 + let (cnt, tweetId) = tweets[index] + minHeap.insert( + Item( + count: cnt, tweetId: tweetId, + followeeId: followeeId, index: index - 1 + ) + ) + } + } + } + + while !minHeap.isEmpty && res.count < 10 { + let item = minHeap.popMin()! + res.append(item.tweetId) + if item.index >= 0, let tweets = tweetMap[item.followeeId] { + let (cnt, tweetId) = tweets[item.index] + minHeap.insert( + Item( + count: cnt, tweetId: tweetId, + followeeId: item.followeeId, index: item.index - 1 + ) + ) + } + } + + return res + } + + func follow(_ followerId: Int, _ followeeId: Int) { + followMap[followerId, default: Set()].insert(followeeId) + } + + func unfollow(_ followerId: Int, _ followeeId: Int) { + followMap[followerId]?.remove(followeeId) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ for each $getNewsFeed()$ call and $O(1)$ for remaining methods. +* Space complexity: $O(N * m + N * M + n)$ + +> Where $n$ is the total number of $followeeIds$ associated with the $userId$, $m$ is the maximum number of tweets by any user ($m$ can be at most $10$), $N$ is the total number of $userIds$ and $M$ is the maximum number of followees for any user. \ No newline at end of file diff --git a/articles/design-underground-system.md b/articles/design-underground-system.md new file mode 100644 index 000000000..d03c0c9e8 --- /dev/null +++ b/articles/design-underground-system.md @@ -0,0 +1,357 @@ +## 1. Two HashMaps + +::tabs-start + +```python +class UndergroundSystem: + + def __init__(self): + self.checkInMap = {} # id -> (startStation, time) + self.routeMap = {} # (start, end) -> [totalTime, count] + + def checkIn(self, id: int, startStation: str, t: int) -> None: + self.checkInMap[id] = (startStation, t) + + def checkOut(self, id: int, endStation: str, t: int) -> None: + startStation, time = self.checkInMap[id] + route = (startStation, endStation) + if route not in self.routeMap: + self.routeMap[route] = [0, 0] + self.routeMap[route][0] += t - time + self.routeMap[route][1] += 1 + + def getAverageTime(self, startStation: str, endStation: str) -> float: + totalTime, count = self.routeMap[(startStation, endStation)] + return totalTime / count +``` + +```java +public class UndergroundSystem { + private Map> checkInMap; + private Map routeMap; + + public UndergroundSystem() { + checkInMap = new HashMap<>(); + routeMap = new HashMap<>(); + } + + public void checkIn(int id, String startStation, int t) { + checkInMap.put(id, new Pair<>(startStation, t)); + } + + public void checkOut(int id, String endStation, int t) { + Pair entry = checkInMap.get(id); + String route = entry.getKey() + "," + endStation; + routeMap.putIfAbsent(route, new int[]{0, 0}); + routeMap.get(route)[0] += t - entry.getValue(); + routeMap.get(route)[1] += 1; + } + + public double getAverageTime(String startStation, String endStation) { + int[] data = routeMap.get(startStation + "," + endStation); + return (double) data[0] / data[1]; + } +} +``` + +```cpp +class UndergroundSystem { + unordered_map> checkInMap; + unordered_map> routeMap; + +public: + UndergroundSystem() {} + + void checkIn(int id, string startStation, int t) { + checkInMap[id] = {startStation, t}; + } + + void checkOut(int id, string endStation, int t) { + auto [startStation, time] = checkInMap[id]; + string route = startStation + "," + endStation; + if (!routeMap.count(route)) + routeMap[route] = {0, 0}; + routeMap[route].first += t - time; + routeMap[route].second += 1; + } + + double getAverageTime(string startStation, string endStation) { + string route = startStation + "," + endStation; + auto [totalTime, count] = routeMap[route]; + return (double) totalTime / count; + } +}; +``` + +```javascript +class UndergroundSystem { + /** + * @constructor + */ + constructor() { + this.checkInMap = new Map(); + this.routeMap = new Map(); + } + + /** + * @param {number} id + * @param {string} startStation + * @param {number} t + * @return {void} + */ + checkIn(id, startStation, t) { + this.checkInMap.set(id, [startStation, t]); + } + + /** + * @param {number} id + * @param {string} endStation + * @param {number} t + * @return {void} + */ + checkOut(id, endStation, t) { + const [startStation, time] = this.checkInMap.get(id); + const route = `${startStation},${endStation}`; + if (!this.routeMap.has(route)) this.routeMap.set(route, [0, 0]); + this.routeMap.get(route)[0] += t - time; + this.routeMap.get(route)[1] += 1; + } + + /** + * @param {string} startStation + * @param {string} endStation + * @return {number} + */ + getAverageTime(startStation, endStation) { + const [totalTime, count] = this.routeMap.get(`${startStation},${endStation}`); + return totalTime / count; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $checkIn()$ function call. + * $O(m)$ time for each $checkOut()$ and $getAverageTime()$ function calls. +* Space complexity: $O(n + N ^ 2)$ + +> Where $n$ is the number of passengers, $N$ is the total number of stations, and $m$ is the average length of station name. + +--- + +## 2. Two HashMaps + Hashing + +::tabs-start + +```python +class UndergroundSystem: + MOD1, MOD2 = 768258391, 685683731 + BASE1, BASE2 = 37, 31 + + def __init__(self): + self.checkInMap = {} # id -> (startStation, time) + self.routeMap = {} # hash(route) -> (totalTime, count) + + def getHash(self, s1: str, s2: str) -> int: + h1, h2, p1, p2 = 0, 0, 1, 1 + for c in s1 + ',' + s2: + h1 = (h1 + (ord(c) - 96) * p1) % self.MOD1 + h2 = (h2 + (ord(c) - 96) * p2) % self.MOD2 + p1 = (p1 * self.BASE1) % self.MOD1 + p2 = (p2 * self.BASE2) % self.MOD2 + return (h1 << 32) | h2 + + def checkIn(self, id: int, startStation: str, t: int) -> None: + self.checkInMap[id] = (startStation, t) + + def checkOut(self, id: int, endStation: str, t: int) -> None: + startStation, time = self.checkInMap[id] + routeHash = self.getHash(startStation, endStation) + if routeHash not in self.routeMap: + self.routeMap[routeHash] = [0, 0] + self.routeMap[routeHash][0] += t - time + self.routeMap[routeHash][1] += 1 + + def getAverageTime(self, startStation: str, endStation: str) -> float: + routeHash = self.getHash(startStation, endStation) + totalTime, count = self.routeMap[routeHash] + return totalTime / count +``` + +```java +public class UndergroundSystem { + private static final int MOD1 = 768258391, MOD2 = 685683731; + private static final int BASE1 = 37, BASE2 = 31; + private Map> checkInMap; + private Map routeMap; + + public UndergroundSystem() { + checkInMap = new HashMap<>(); + routeMap = new HashMap<>(); + } + + private long getHash(String s1, String s2) { + long h1 = 0, h2 = 0, p1 = 1, p2 = 1; + String combined = s1 + "," + s2; + + for (char c : combined.toCharArray()) { + h1 = (h1 + (c - 96) * p1) % MOD1; + h2 = (h2 + (c - 96) * p2) % MOD2; + p1 = (p1 * BASE1) % MOD1; + p2 = (p2 * BASE2) % MOD2; + } + return (h1 << 32) | h2; + } + + public void checkIn(int id, String startStation, int t) { + checkInMap.put(id, new Pair<>(startStation, t)); + } + + public void checkOut(int id, String endStation, int t) { + Pair checkInData = checkInMap.get(id); + long routeHash = getHash(checkInData.getKey(), endStation); + routeMap.putIfAbsent(routeHash, new int[]{0, 0}); + int[] data = routeMap.get(routeHash); + data[0] += (t - checkInData.getValue()); + data[1]++; + } + + public double getAverageTime(String startStation, String endStation) { + long routeHash = getHash(startStation, endStation); + int[] data = routeMap.get(routeHash); + return (double) data[0] / data[1]; + } +} +``` + +```cpp +class UndergroundSystem { +private: + static constexpr int MOD1 = 768258391, MOD2 = 685683731; + static constexpr int BASE1 = 37, BASE2 = 31; + unordered_map> checkInMap; + unordered_map> routeMap; + + unsigned long long getHash(const string& s1, const string& s2) { + long long h1 = 0, h2 = 0, p1 = 1, p2 = 1; + string combined = s1 + "," + s2; + + for (char c : combined) { + h1 = (h1 + (c - 96) * p1) % MOD1; + h2 = (h2 + (c - 96) * p2) % MOD2; + p1 = (p1 * BASE1) % MOD1; + p2 = (p2 * BASE2) % MOD2; + } + return (h1 << 32) | h2; + } + +public: + UndergroundSystem() {} + + void checkIn(int id, string startStation, int t) { + checkInMap[id] = {startStation, t}; + } + + void checkOut(int id, string endStation, int t) { + auto [startStation, time] = checkInMap[id]; + unsigned long long routeHash = getHash(startStation, endStation); + routeMap[routeHash].first += (t - time); + routeMap[routeHash].second++; + } + + double getAverageTime(string startStation, string endStation) { + unsigned long long routeHash = getHash(startStation, endStation); + auto [totalTime, count] = routeMap[routeHash]; + return (double) totalTime / count; + } +}; +``` + +```javascript +class UndergroundSystem { + /** + * @constructor + */ + constructor() { + this.MOD1 = 768258391; + this.MOD2 = 685683731; + this.BASE1 = 37; + this.BASE2 = 31; + this.checkInMap = new Map(); + this.routeMap = new Map(); + } + + /** + * @param {string} s1 + * @param {string} s2 + * @return {number} + */ + getHash(s1, s2) { + let h1 = 0, h2 = 0, p1 = 1, p2 = 1; + let combined = s1 + "," + s2; + + for (let i = 0; i < combined.length; i++) { + let c = combined.charCodeAt(i) - 96; + h1 = (h1 + c * p1) % this.MOD1; + h2 = (h2 + c * p2) % this.MOD2; + p1 = (p1 * this.BASE1) % this.MOD1; + p2 = (p2 * this.BASE2) % this.MOD2; + } + return BigInt(h1) << BigInt(32) | BigInt(h2); + } + + /** + * @param {number} id + * @param {string} startStation + * @param {number} t + * @return {void} + */ + checkIn(id, startStation, t) { + this.checkInMap.set(id, [startStation, t]); + } + + /** + * @param {number} id + * @param {string} endStation + * @param {number} t + * @return {void} + */ + checkOut(id, endStation, t) { + let [startStation, time] = this.checkInMap.get(id); + let routeHash = this.getHash(startStation, endStation); + if (!this.routeMap.has(routeHash)) { + this.routeMap.set(routeHash, [0, 0]); + } + let data = this.routeMap.get(routeHash); + data[0] += t - time; + data[1]++; + } + + /** + * @param {string} startStation + * @param {string} endStation + * @return {number} + */ + getAverageTime(startStation, endStation) { + let routeHash = this.getHash(startStation, endStation); + let [totalTime, count] = this.routeMap.get(routeHash); + return totalTime / count; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $checkIn()$ function call. + * $O(m)$ time for each $checkOut()$ and $getAverageTime()$ function calls. +* Space complexity: $O(n + N ^ 2)$ + +> Where $n$ is the number of passengers, $N$ is the total number of stations, and $m$ is the average length of station name. \ No newline at end of file diff --git a/articles/design-word-search-data-structure.md b/articles/design-word-search-data-structure.md new file mode 100644 index 000000000..033096234 --- /dev/null +++ b/articles/design-word-search-data-structure.md @@ -0,0 +1,731 @@ +## 1. Brute Force + +::tabs-start + +```python +class WordDictionary: + + def __init__(self): + self.store = [] + + def addWord(self, word: str) -> None: + self.store.append(word) + + def search(self, word: str) -> bool: + for w in self.store: + if len(w) != len(word): + continue + i = 0 + while i < len(w): + if w[i] == word[i] or word[i] == '.': + i += 1 + else: + break + if i == len(w): + return True + return False +``` + +```java +public class WordDictionary { + + private List store; + + public WordDictionary() { + store = new ArrayList<>(); + } + + public void addWord(String word) { + store.add(word); + } + + public boolean search(String word) { + for (String w : store) { + if (w.length() != word.length()) continue; + int i = 0; + while (i < w.length()) { + if (w.charAt(i) == word.charAt(i) || + word.charAt(i) == '.') { + i++; + } else { + break; + } + } + if (i == w.length()) { + return true; + } + } + return false; + } +} +``` + +```cpp +class WordDictionary { +public: + vector store; + + WordDictionary() {} + + void addWord(string word) { + store.push_back(word); + } + + bool search(string word) { + for (string w : store) { + if (w.length() != word.length()) continue; + int i = 0; + while (i < w.length()) { + if (w[i] == word[i] || word[i] == '.') { + i++; + } else { + break; + } + } + if (i == w.length()) { + return true; + } + } + return false; + } +}; +``` + +```javascript +class WordDictionary { + constructor() { + this.store = []; + } + + /** + * @param {string} word + * @return {void} + */ + addWord(word) { + this.store.push(word); + } + + /** + * @param {string} word + * @return {boolean} + */ + search(word) { + for (let w of this.store) { + if (w.length !== word.length) continue; + let i = 0; + while (i < w.length) { + if (w[i] === word[i] || + word[i] === '.') { + i++; + } else { + break; + } + } + if (i === w.length) { + return true; + } + } + return false; + } +} +``` + +```csharp +public class WordDictionary { + + private List store; + + public WordDictionary() { + store = new List(); + } + + public void AddWord(string word) { + store.Add(word); + } + + public bool Search(string word) { + foreach (string w in store) { + if (w.Length != word.Length) continue; + int i = 0; + while (i < w.Length) { + if (w[i] == word[i] || word[i] == '.') { + i++; + } else { + break; + } + } + if (i == w.Length) { + return true; + } + } + return false; + } +} +``` + +```go +type WordDictionary struct { + store []string +} + +func Constructor() WordDictionary { + return WordDictionary{store: []string{}} +} + +func (this *WordDictionary) AddWord(word string) { + this.store = append(this.store, word) +} + +func (this *WordDictionary) Search(word string) bool { + for _, w := range this.store { + if len(w) != len(word) { + continue + } + match := true + for i := 0; i < len(w); i++ { + if w[i] != word[i] && word[i] != '.' { + match = false + break + } + } + if match { + return true + } + } + return false +} +``` + +```kotlin +class WordDictionary { + + private val store = mutableListOf() + + fun addWord(word: String) { + store.add(word) + } + + fun search(word: String): Boolean { + for (w in store) { + if (w.length != word.length) continue + var match = true + for (i in w.indices) { + if (w[i] != word[i] && word[i] != '.') { + match = false + break + } + } + if (match) return true + } + return false + } +} +``` + +```swift +class WordDictionary { + private var store: [String] + + init() { + self.store = [] + } + + func addWord(_ word: String) { + store.append(word) + } + + func search(_ word: String) -> Bool { + for w in store { + if w.count != word.count { + continue + } + var i = 0 + let wArray = Array(w) + let wordArray = Array(word) + while i < wArray.count { + if wArray[i] == wordArray[i] || wordArray[i] == "." { + i += 1 + } else { + break + } + } + if i == wArray.count { + return true + } + } + return false + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for $addWord()$, $O(m * n)$ for $search()$. +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of words added and $n$ is the length of the string. + +--- + +## 2. Depth First Search (Trie) + +::tabs-start + +```python +class TrieNode: + def __init__(self): + self.children = {} + self.word = False + + +class WordDictionary: + def __init__(self): + self.root = TrieNode() + + def addWord(self, word: str) -> None: + cur = self.root + for c in word: + if c not in cur.children: + cur.children[c] = TrieNode() + cur = cur.children[c] + cur.word = True + + def search(self, word: str) -> bool: + def dfs(j, root): + cur = root + + for i in range(j, len(word)): + c = word[i] + if c == ".": + for child in cur.children.values(): + if dfs(i + 1, child): + return True + return False + else: + if c not in cur.children: + return False + cur = cur.children[c] + return cur.word + + return dfs(0, self.root) +``` + +```java +public class TrieNode { + + TrieNode[] children; + boolean word; + + public TrieNode() { + children = new TrieNode[26]; + word = false; + } +} + +public class WordDictionary { + + private TrieNode root; + + public WordDictionary() { + root = new TrieNode(); + } + + public void addWord(String word) { + TrieNode cur = root; + for (char c : word.toCharArray()) { + if (cur.children[c - 'a'] == null) { + cur.children[c - 'a'] = new TrieNode(); + } + cur = cur.children[c - 'a']; + } + cur.word = true; + } + + public boolean search(String word) { + return dfs(word, 0, root); + } + + private boolean dfs(String word, int j, TrieNode root) { + TrieNode cur = root; + + for (int i = j; i < word.length(); i++) { + char c = word.charAt(i); + if (c == '.') { + for (TrieNode child : cur.children) { + if (child != null && dfs(word, i + 1, child)) { + return true; + } + } + return false; + } else { + if (cur.children[c - 'a'] == null) { + return false; + } + cur = cur.children[c - 'a']; + } + } + return cur.word; + } +} +``` + +```cpp +class TrieNode { +public: + vector children; + bool word; + + TrieNode() : children(26, nullptr), word(false) {} +}; + +class WordDictionary { +public: + TrieNode* root; + + WordDictionary() : root(new TrieNode()) {} + + void addWord(string word) { + TrieNode* cur = root; + for (char c : word) { + if (cur->children[c - 'a'] == nullptr) { + cur->children[c - 'a'] = new TrieNode(); + } + cur = cur->children[c - 'a']; + } + cur->word = true; + } + + bool search(string word) { + return dfs(word, 0, root); + } + +private: + bool dfs(string word, int j, TrieNode* root) { + TrieNode* cur = root; + + for (int i = j; i < word.size(); i++) { + char c = word[i]; + if (c == '.') { + for (TrieNode* child : cur->children) { + if (child != nullptr && dfs(word, i + 1, child)) { + return true; + } + } + return false; + } else { + if (cur->children[c - 'a'] == nullptr) { + return false; + } + cur = cur->children[c - 'a']; + } + } + return cur->word; + } +}; +``` + +```javascript +class TrieNode { + constructor() { + this.children = Array(26).fill(null); + this.word = false; + } +} + +class WordDictionary { + constructor() { + this.root = new TrieNode(); + } + + /** + * @param {string} c + * @return {number} + */ + getIndex(c) { + return c.charCodeAt(0) - 'a'.charCodeAt(0); + } + + /** + * @param {string} word + * @return {void} + */ + addWord(word) { + let cur = this.root; + for (const c of word) { + const idx = this.getIndex(c); + if (cur.children[idx] === null) { + cur.children[idx] = new TrieNode(); + } + cur = cur.children[idx]; + } + cur.word = true; + } + + /** + * @param {string} word + * @return {boolean} + */ + search(word) { + return this.dfs(word, 0, this.root); + } + + /** + * @param {string} word + * @param {number} j + * @param {TrieNode} root + * @return {boolean} + */ + dfs(word, j, root) { + let cur = root; + + for (let i = j; i < word.length; i++) { + const c = word[i]; + if (c === '.') { + for (const child of cur.children) { + if (child !== null && + this.dfs(word, i + 1, child)) { + return true; + } + } + return false; + } else { + const idx = this.getIndex(c); + if (cur.children[idx] === null) { + return false; + } + cur = cur.children[idx]; + } + } + return cur.word; + } +} +``` + +```csharp +public class TrieNode { + public TrieNode[] children = new TrieNode[26]; + public bool word = false; +} + +public class WordDictionary { + + private TrieNode root; + + public WordDictionary() { + root = new TrieNode(); + } + + public void AddWord(string word) { + TrieNode cur = root; + foreach (char c in word) { + if (cur.children[c - 'a'] == null) { + cur.children[c - 'a'] = new TrieNode(); + } + cur = cur.children[c - 'a']; + } + cur.word = true; + } + + public bool Search(string word) { + return Dfs(word, 0, root); + } + + private bool Dfs(string word, int j, TrieNode root) { + TrieNode cur = root; + + for (int i = j; i < word.Length; i++) { + char c = word[i]; + if (c == '.') { + foreach (TrieNode child in cur.children) { + if (child != null && Dfs(word, i + 1, child)) { + return true; + } + } + return false; + } else { + if (cur.children[c - 'a'] == null) { + return false; + } + cur = cur.children[c - 'a']; + } + } + return cur.word; + } +} +``` + +```go +type TrieNode struct { + children [26]*TrieNode + word bool +} + +func NewTrieNode() *TrieNode { + return &TrieNode{} +} + +type WordDictionary struct { + root *TrieNode +} + +func Constructor() WordDictionary { + return WordDictionary{root: NewTrieNode()} +} + +func (this *WordDictionary) AddWord(word string) { + cur := this.root + for _, c := range word { + index := c - 'a' + if cur.children[index] == nil { + cur.children[index] = NewTrieNode() + } + cur = cur.children[index] + } + cur.word = true +} + +func (this *WordDictionary) Search(word string) bool { + return this.dfs(word, 0, this.root) +} + +func (this *WordDictionary) dfs(word string, j int, root *TrieNode) bool { + cur := root + for i := j; i < len(word); i++ { + c := word[i] + if c == '.' { + for _, child := range cur.children { + if child != nil && this.dfs(word, i+1, child) { + return true + } + } + return false + } else { + index := c - 'a' + if cur.children[index] == nil { + return false + } + cur = cur.children[index] + } + } + return cur.word +} +``` + +```kotlin +class TrieNode { + val children = Array(26) { null } + var word: Boolean = false +} + +class WordDictionary { + + private val root = TrieNode() + + fun addWord(word: String) { + var cur = root + for (c in word) { + val index = c - 'a' + if (cur.children[index] == null) { + cur.children[index] = TrieNode() + } + cur = cur.children[index]!! + } + cur.word = true + } + + fun search(word: String): Boolean { + return dfs(word, 0, root) + } + + private fun dfs(word: String, j: Int, root: TrieNode): Boolean { + var cur = root + for (i in j until word.length) { + val c = word[i] + if (c == '.') { + for (child in cur.children) { + if (child != null && dfs(word, i + 1, child)) { + return true + } + } + return false + } else { + val index = c - 'a' + if (cur.children[index] == null) { + return false + } + cur = cur.children[index]!! + } + } + return cur.word + } +} +``` + +```swift +class TrieNode { + var children: [Character: TrieNode] + var word: Bool + + init() { + self.children = [:] + self.word = false + } +} + +class WordDictionary { + private let root: TrieNode + + init() { + self.root = TrieNode() + } + + func addWord(_ word: String) { + var cur = root + for c in word { + if cur.children[c] == nil { + cur.children[c] = TrieNode() + } + cur = cur.children[c]! + } + cur.word = true + } + + func search(_ word: String) -> Bool { + func dfs(_ j: Int, _ root: TrieNode) -> Bool { + var cur = root + let wordArray = Array(word) + + for i in j.. Where $n$ is the length of the string and $t$ is the total number of TrieNodes created in the Trie. \ No newline at end of file diff --git a/articles/destination-city.md b/articles/destination-city.md new file mode 100644 index 000000000..0dc585b2b --- /dev/null +++ b/articles/destination-city.md @@ -0,0 +1,253 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def destCity(self, paths: List[List[str]]) -> str: + for i in range(len(paths)): + flag = True + for j in range(len(paths)): + if paths[i][1] == paths[j][0]: + flag = False + break + if flag: + return paths[i][1] + return "" +``` + +```java +public class Solution { + public String destCity(List> paths) { + for (int i = 0; i < paths.size(); i++) { + boolean flag = true; + for (int j = 0; j < paths.size(); j++) { + if (paths.get(i).get(1).equals(paths.get(j).get(0))) { + flag = false; + break; + } + } + if (flag) { + return paths.get(i).get(1); + } + } + return ""; + } +} +``` + +```cpp +class Solution { +public: + string destCity(vector>& paths) { + for (int i = 0; i < paths.size(); i++) { + bool flag = true; + for (int j = 0; j < paths.size(); j++) { + if (paths[i][1] == paths[j][0]) { + flag = false; + break; + } + } + if (flag) { + return paths[i][1]; + } + } + return ""; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[][]} paths + * @return {string} + */ + destCity(paths) { + for (let i = 0; i < paths.length; i++) { + let flag = true; + for (let j = 0; j < paths.length; j++) { + if (paths[i][1] === paths[j][0]) { + flag = false; + break; + } + } + if (flag) { + return paths[i][1]; + } + } + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Hash Set + +::tabs-start + +```python +class Solution: + def destCity(self, paths: List[List[str]]) -> str: + s = set() + for p in paths: + s.add(p[0]) + + for p in paths: + if p[1] not in s: + return p[1] +``` + +```java +public class Solution { + public String destCity(List> paths) { + Set s = new HashSet<>(); + for (List p : paths) { + s.add(p.get(0)); + } + + for (List p : paths) { + if (!s.contains(p.get(1))) { + return p.get(1); + } + } + return ""; + } +} +``` + +```cpp +class Solution { +public: + string destCity(vector>& paths) { + unordered_set s; + for (auto& p : paths) { + s.insert(p[0]); + } + + for (auto& p : paths) { + if (s.find(p[1]) == s.end()) { + return p[1]; + } + } + return ""; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[][]} paths + * @return {string} + */ + destCity(paths) { + const s = new Set(); + for (const p of paths) { + s.add(p[0]); + } + + for (const p of paths) { + if (!s.has(p[1])) { + return p[1]; + } + } + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Hash Map + +::tabs-start + +```python +class Solution: + def destCity(self, paths: List[List[str]]) -> str: + mp = {p[0]: p[1] for p in paths} + + start = paths[0][0] + while start in mp: + start = mp[start] + return start +``` + +```java +public class Solution { + public String destCity(List> paths) { + Map mp = new HashMap<>(); + for (List p : paths) { + mp.put(p.get(0), p.get(1)); + } + + String start = paths.get(0).get(0); + while (mp.containsKey(start)) { + start = mp.get(start); + } + return start; + } +} +``` + +```cpp +class Solution { +public: + string destCity(vector>& paths) { + unordered_map mp; + for (auto& p : paths) { + mp[p[0]] = p[1]; + } + + string start = paths[0][0]; + while (mp.find(start) != mp.end()) { + start = mp[start]; + } + return start; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[][]} paths + * @return {string} + */ + destCity(paths) { + const mp = new Map(); + for (const p of paths) { + mp.set(p[0], p[1]); + } + + let start = paths[0][0]; + while (mp.has(start)) { + start = mp.get(start); + } + return start; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/detonate-the-maximum-bombs.md b/articles/detonate-the-maximum-bombs.md new file mode 100644 index 000000000..bc1a75fc9 --- /dev/null +++ b/articles/detonate-the-maximum-bombs.md @@ -0,0 +1,522 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def maximumDetonation(self, bombs: list[list[int]]) -> int: + adj = [[] for _ in range(len(bombs))] + + for i in range(len(bombs)): + x1, y1, r1 = bombs[i] + for j in range(i + 1, len(bombs)): + x2, y2, r2 = bombs[j] + d = (x1 - x2) ** 2 + (y1 - y2) ** 2 + + if d <= r1 ** 2: + adj[i].append(j) + if d <= r2 ** 2: + adj[j].append(i) + + def dfs(i, visit): + if i in visit: + return 0 + visit.add(i) + for nei in adj[i]: + dfs(nei, visit) + return len(visit) + + res = 0 + for i in range(len(bombs)): + res = max(res, dfs(i, set())) + return res +``` + +```java +public class Solution { + public int maximumDetonation(int[][] bombs) { + int n = bombs.length; + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + int x1 = bombs[i][0], y1 = bombs[i][1], r1 = bombs[i][2]; + int x2 = bombs[j][0], y2 = bombs[j][1], r2 = bombs[j][2]; + long d = (long) (x1 - x2) * (x1 - x2) + (long) (y1 - y2) * (y1 - y2); + + if (d <= (long) r1 * r1) { + adj[i].add(j); + } + if (d <= (long) r2 * r2) { + adj[j].add(i); + } + } + } + + int res = 0; + for (int i = 0; i < n; i++) { + res = Math.max(res, dfs(i, new HashSet<>(), adj)); + } + return res; + } + + private int dfs(int i, Set visit, List[] adj) { + if (!visit.add(i)) return 0; + for (int nei : adj[i]) { + dfs(nei, visit, adj); + } + return visit.size(); + } +} +``` + +```cpp +class Solution { +public: + int maximumDetonation(vector>& bombs) { + int n = bombs.size(); + vector> adj(n); + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + long long x1 = bombs[i][0], y1 = bombs[i][1], r1 = bombs[i][2]; + long long x2 = bombs[j][0], y2 = bombs[j][1], r2 = bombs[j][2]; + long long d = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2); + + if (d <= r1 * r1) { + adj[i].push_back(j); + } + if (d <= r2 * r2) { + adj[j].push_back(i); + } + } + } + + int res = 0; + for (int i = 0; i < n; i++) { + unordered_set visit; + res = max(res, dfs(i, visit, adj)); + } + return res; + } + +private: + int dfs(int i, unordered_set& visit, vector>& adj) { + if (!visit.insert(i).second) return 0; + for (int nei : adj[i]) { + dfs(nei, visit, adj); + } + return visit.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} bombs + * @return {number} + */ + maximumDetonation(bombs) { + let n = bombs.length; + let adj = Array.from({ length: n }, () => []); + + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + let [x1, y1, r1] = bombs[i]; + let [x2, y2, r2] = bombs[j]; + let d = (x1 - x2) ** 2 + (y1 - y2) ** 2; + + if (d <= r1 ** 2) adj[i].push(j); + if (d <= r2 ** 2) adj[j].push(i); + } + } + + const dfs = (i, visit) => { + if (visit.has(i)) return 0; + visit.add(i); + for (let nei of adj[i]) { + dfs(nei, visit); + } + return visit.size; + }; + + let res = 0; + for (let i = 0; i < n; i++) { + res = Math.max(res, dfs(i, new Set())); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def maximumDetonation(self, bombs: list[list[int]]) -> int: + n = len(bombs) + adj = [[] for _ in range(n)] + + for i in range(n): + x1, y1, r1 = bombs[i] + for j in range(i + 1, n): + x2, y2, r2 = bombs[j] + d = (x1 - x2) ** 2 + (y1 - y2) ** 2 + + if d <= r1 ** 2: + adj[i].append(j) + if d <= r2 ** 2: + adj[j].append(i) + + res = 0 + for i in range(n): + q = deque([i]) + visit = [False] * n + visit[i] = True + count = 1 + while q: + node = q.popleft() + for nei in adj[node]: + if not visit[nei]: + visit[nei] = True + count += 1 + q.append(nei) + res = max(res, count) + return res +``` + +```java +public class Solution { + public int maximumDetonation(int[][] bombs) { + int n = bombs.length; + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + + for (int i = 0; i < n; i++) { + int x1 = bombs[i][0], y1 = bombs[i][1], r1 = bombs[i][2]; + for (int j = i + 1; j < n; j++) { + int x2 = bombs[j][0], y2 = bombs[j][1], r2 = bombs[j][2]; + long d = (long) (x1 - x2) * (x1 - x2) + (long) (y1 - y2) * (y1 - y2); + + if (d <= (long) r1 * r1) adj[i].add(j); + if (d <= (long) r2 * r2) adj[j].add(i); + } + } + + int res = 0; + for (int i = 0; i < n; i++) { + Queue q = new LinkedList<>(); + boolean[] visit = new boolean[n]; + q.offer(i); + visit[i] = true; + int count = 1; + + while (!q.isEmpty()) { + int node = q.poll(); + for (int nei : adj[node]) { + if (!visit[nei]) { + visit[nei] = true; + count++; + q.offer(nei); + } + } + } + res = Math.max(res, count); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maximumDetonation(vector>& bombs) { + int n = bombs.size(); + vector> adj(n); + + for (int i = 0; i < n; i++) { + int x1 = bombs[i][0], y1 = bombs[i][1], r1 = bombs[i][2]; + for (int j = i + 1; j < n; j++) { + int x2 = bombs[j][0], y2 = bombs[j][1], r2 = bombs[j][2]; + long long d = (x1 - x2) * 1LL * (x1 - x2) + (y1 - y2) * 1LL * (y1 - y2); + + if (d <= (long long) r1 * r1) adj[i].push_back(j); + if (d <= (long long) r2 * r2) adj[j].push_back(i); + } + } + + int res = 0; + for (int i = 0; i < n; i++) { + queue q; + vector visit(n, false); + q.push(i); + visit[i] = true; + int count = 1; + + while (!q.empty()) { + int node = q.front();q.pop(); + for (int& nei : adj[node]) { + if (!visit[nei]) { + visit[nei] = true; + count++; + q.push(nei); + } + } + } + res = max(res, count); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} bombs + * @return {number} + */ + maximumDetonation(bombs) { + let n = bombs.length; + let adj = Array.from({ length: n }, () => []); + + for (let i = 0; i < n; i++) { + let [x1, y1, r1] = bombs[i]; + for (let j = i + 1; j < n; j++) { + let [x2, y2, r2] = bombs[j]; + let d = (x1 - x2) ** 2 + (y1 - y2) ** 2; + + if (d <= r1 ** 2) adj[i].push(j); + if (d <= r2 ** 2) adj[j].push(i); + } + } + + let res = 0; + for (let i = 0; i < n; i++) { + let q = new Queue([i]); + let visit = new Array(n).fill(false); + visit[i] = true; + let count = 1; + + while (!q.isEmpty()) { + let node = q.pop(); + for (let nei of adj[node]) { + if (!visit[nei]) { + visit[nei] = true; + count++; + q.push(nei); + } + } + } + res = Math.max(res, count); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +class Solution: + def maximumDetonation(self, bombs: list[list[int]]) -> int: + n = len(bombs) + adj = [[] for _ in range(n)] + + for i in range(n): + x1, y1, r1 = bombs[i] + for j in range(i + 1, n): + x2, y2, r2 = bombs[j] + d = (x1 - x2) ** 2 + (y1 - y2) ** 2 + + if d <= r1 ** 2: + adj[i].append(j) + if d <= r2 ** 2: + adj[j].append(i) + + res = 0 + for i in range(n): + stack = [i] + visit = [False] * n + visit[i] = True + count = 1 + + while stack: + node = stack.pop() + for nei in adj[node]: + if not visit[nei]: + visit[nei] = True + count += 1 + stack.append(nei) + res = max(res, count) + return res +``` + +```java +public class Solution { + public int maximumDetonation(int[][] bombs) { + int n = bombs.length; + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + + for (int i = 0; i < n; i++) { + int x1 = bombs[i][0], y1 = bombs[i][1], r1 = bombs[i][2]; + for (int j = i + 1; j < n; j++) { + int x2 = bombs[j][0], y2 = bombs[j][1], r2 = bombs[j][2]; + long d = (long) (x1 - x2) * (x1 - x2) + (long) (y1 - y2) * (y1 - y2); + + if (d <= (long) r1 * r1) adj[i].add(j); + if (d <= (long) r2 * r2) adj[j].add(i); + } + } + + int res = 0; + for (int i = 0; i < n; i++) { + Stack stack = new Stack<>(); + boolean[] visit = new boolean[n]; + stack.push(i); + visit[i] = true; + int count = 1; + + while (!stack.isEmpty()) { + int node = stack.pop(); + for (int nei : adj[node]) { + if (!visit[nei]) { + visit[nei] = true; + count++; + stack.push(nei); + } + } + } + res = Math.max(res, count); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maximumDetonation(vector>& bombs) { + int n = bombs.size(); + vector> adj(n); + + for (int i = 0; i < n; i++) { + int x1 = bombs[i][0], y1 = bombs[i][1], r1 = bombs[i][2]; + for (int j = i + 1; j < n; j++) { + int x2 = bombs[j][0], y2 = bombs[j][1], r2 = bombs[j][2]; + long long d = (long long)(x1 - x2) * (x1 - x2) + (long long)(y1 - y2) * (y1 - y2); + + if (d <= (long long) r1 * r1) adj[i].push_back(j); + if (d <= (long long) r2 * r2) adj[j].push_back(i); + } + } + + int res = 0; + for (int i = 0; i < n; i++) { + stack stk; + vector visit(n, false); + stk.push(i); + visit[i] = true; + int count = 1; + + while (!stk.empty()) { + int node = stk.top();stk.pop(); + for (int& nei : adj[node]) { + if (!visit[nei]) { + visit[nei] = true; + count++; + stk.push(nei); + } + } + } + res = max(res, count); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} bombs + * @return {number} + */ + maximumDetonation(bombs) { + let n = bombs.length; + let adj = Array.from({ length: n }, () => []); + + for (let i = 0; i < n; i++) { + let [x1, y1, r1] = bombs[i]; + for (let j = i + 1; j < n; j++) { + let [x2, y2, r2] = bombs[j]; + let d = (x1 - x2) ** 2 + (y1 - y2) ** 2; + + if (d <= r1 ** 2) adj[i].push(j); + if (d <= r2 ** 2) adj[j].push(i); + } + } + + let res = 0; + for (let i = 0; i < n; i++) { + let stack = [i]; + let visit = new Array(n).fill(false); + visit[i] = true; + let count = 1; + + while (stack.length) { + let node = stack.pop(); + for (let nei of adj[node]) { + if (!visit[nei]) { + visit[nei] = true; + count++; + stack.push(nei); + } + } + } + res = Math.max(res, count); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/distribute-coins-in-binary-tree.md b/articles/distribute-coins-in-binary-tree.md new file mode 100644 index 000000000..4e26d25e9 --- /dev/null +++ b/articles/distribute-coins-in-binary-tree.md @@ -0,0 +1,723 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def distributeCoins(self, root: Optional[TreeNode]) -> int: + self.res = 0 + + def dfs(cur): + if not cur: + return [0, 0] # [size, coins] + + l_size, l_coins = dfs(cur.left) + r_size, r_coins = dfs(cur.right) + + size = 1 + l_size + r_size + coins = cur.val + l_coins + r_coins + self.res += abs(size - coins) + + return [size, coins] + + dfs(root) + return self.res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private int res; + + public int distributeCoins(TreeNode root) { + res = 0; + dfs(root); + return res; + } + + private int[] dfs(TreeNode cur) { + if (cur == null) { + return new int[]{0, 0}; // [size, coins] + } + + int[] left = dfs(cur.left); + int[] right = dfs(cur.right); + + int size = 1 + left[0] + right[0]; + int coins = cur.val + left[1] + right[1]; + res += Math.abs(size - coins); + + return new int[]{size, coins}; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +private: + int res; + + vector dfs(TreeNode* cur) { + if (!cur) { + return {0, 0}; // [size, coins] + } + + vector left = dfs(cur->left); + vector right = dfs(cur->right); + + int size = 1 + left[0] + right[0]; + int coins = cur->val + left[1] + right[1]; + res += abs(size - coins); + + return {size, coins}; + } + +public: + int distributeCoins(TreeNode* root) { + res = 0; + dfs(root); + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + distributeCoins(root) { + let res = 0; + + const dfs = (cur) => { + if (!cur) { + return [0, 0]; // [size, coins] + } + + let [lSize, lCoins] = dfs(cur.left); + let [rSize, rCoins] = dfs(cur.right); + + let size = 1 + lSize + rSize; + let coins = cur.val + lCoins + rCoins; + res += Math.abs(size - coins); + + return [size, coins]; + }; + + dfs(root); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Depth First Search (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def distributeCoins(self, root: Optional[TreeNode]) -> int: + self.res = 0 + + def dfs(cur): + if not cur: + return 0 # extra_coins + + l_extra = dfs(cur.left) + r_extra = dfs(cur.right) + + extra_coins = cur.val - 1 + l_extra + r_extra + self.res += abs(extra_coins) + return extra_coins + + dfs(root) + return self.res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private int res; + + public int distributeCoins(TreeNode root) { + res = 0; + dfs(root); + return res; + } + + private int dfs(TreeNode cur) { + if (cur == null) { + return 0; // extra_coins + } + + int lExtra = dfs(cur.left); + int rExtra = dfs(cur.right); + + int extraCoins = cur.val - 1 + lExtra + rExtra; + res += Math.abs(extraCoins); + return extraCoins; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +private: + int res; + + int dfs(TreeNode* cur) { + if (!cur) { + return 0; // extra_coins + } + + int lExtra = dfs(cur->left); + int rExtra = dfs(cur->right); + + int extraCoins = cur->val - 1 + lExtra + rExtra; + res += abs(extraCoins); + return extraCoins; + } + +public: + int distributeCoins(TreeNode* root) { + res = 0; + dfs(root); + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + distributeCoins(root) { + let res = 0; + + const dfs = (cur) => { + if (!cur) { + return 0; // extra_coins + } + + let lExtra = dfs(cur.left); + let rExtra = dfs(cur.right); + + let extraCoins = cur.val - 1 + lExtra + rExtra; + res += Math.abs(extraCoins); + return extraCoins; + }; + + dfs(root); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ fo recursion stack. + +--- + +## 3. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def distributeCoins(self, root: Optional[TreeNode]) -> int: + res = 0 + q = deque([root]) + parent_map = {} + + nodes = [] + while q: + node = q.popleft() + nodes.append(node) + if node.left: + parent_map[node.left] = node + q.append(node.left) + if node.right: + parent_map[node.right] = node + q.append(node.right) + + while nodes: + node = nodes.pop() + if node in parent_map: + parent = parent_map[node] + parent.val += node.val - 1 + res += abs(node.val - 1) + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int distributeCoins(TreeNode root) { + int res = 0; + Queue q = new LinkedList<>(); + Map parentMap = new HashMap<>(); + List nodes = new ArrayList<>(); + + q.offer(root); + while (!q.isEmpty()) { + TreeNode node = q.poll(); + nodes.add(node); + if (node.left != null) { + parentMap.put(node.left, node); + q.offer(node.left); + } + if (node.right != null) { + parentMap.put(node.right, node); + q.offer(node.right); + } + } + + for (int i = nodes.size() - 1; i >= 0; i--) { + TreeNode node = nodes.get(i); + if (parentMap.containsKey(node)) { + TreeNode parent = parentMap.get(node); + parent.val += node.val - 1; + res += Math.abs(node.val - 1); + } + } + + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int distributeCoins(TreeNode* root) { + int res = 0; + queue q; + unordered_map parentMap; + vector nodes; + + q.push(root); + while (!q.empty()) { + TreeNode* node = q.front(); + q.pop(); + nodes.push_back(node); + if (node->left) { + parentMap[node->left] = node; + q.push(node->left); + } + if (node->right) { + parentMap[node->right] = node; + q.push(node->right); + } + } + + for (int i = nodes.size() - 1; i >= 0; i--) { + TreeNode* node = nodes[i]; + if (parentMap.count(node)) { + TreeNode* parent = parentMap[node]; + parent->val += node->val - 1; + res += abs(node->val - 1); + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + distributeCoins(root) { + let res = 0; + let q = new Queue(); + let parentMap = new Map(); + let nodes = []; + + q.push(root); + while (!q.isEmpty()) { + let node = q.pop(); + nodes.push(node); + if (node.left) { + parentMap.set(node.left, node); + q.push(node.left); + } + if (node.right) { + parentMap.set(node.right, node); + q.push(node.right); + } + } + + for (let i = nodes.length - 1; i >= 0; i--) { + let node = nodes[i]; + if (parentMap.has(node)) { + let parent = parentMap.get(node); + parent.val += node.val - 1; + res += Math.abs(node.val - 1); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def distributeCoins(self, root: Optional[TreeNode]) -> int: + stack = [root] + res = 0 + visit = set() + + while stack: + node = stack.pop() + + if node not in visit: + stack.append(node) + visit.add(node) + + if node.right: + stack.append(node.right) + if node.left: + stack.append(node.left) + else: + if node.left: + node.val += node.left.val + if node.right: + node.val += node.right.val + + node.val -= 1 + res += abs(node.val) + visit.remove(node) + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int distributeCoins(TreeNode root) { + Stack stack = new Stack<>(); + Set visit = new HashSet<>(); + stack.push(root); + int res = 0; + + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + + if (!visit.contains(node)) { + stack.push(node); + visit.add(node); + + if (node.right != null) { + stack.push(node.right); + } + if (node.left != null) { + stack.push(node.left); + } + } else { + if (node.left != null) { + node.val += node.left.val; + } + if (node.right != null) { + node.val += node.right.val; + } + + node.val -= 1; + res += Math.abs(node.val); + visit.remove(node); + } + } + + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int distributeCoins(TreeNode* root) { + stack stack; + unordered_set visit; + stack.push(root); + int res = 0; + + while (!stack.empty()) { + TreeNode* node = stack.top(); + stack.pop(); + + if (visit.find(node) == visit.end()) { + stack.push(node); + visit.insert(node); + + if (node->right) { + stack.push(node->right); + } + if (node->left) { + stack.push(node->left); + } + } else { + if (node->left) { + node->val += node->left->val; + } + if (node->right) { + node->val += node->right->val; + } + + visit.erase(node); + node->val -= 1; + res += abs(node->val); + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + distributeCoins(root) { + let stack = [root]; + let visit = new Set(); + let res = 0; + + while (stack.length) { + let node = stack.pop(); + + if (!visit.has(node)) { + stack.push(node); + visit.add(node); + + if (node.right) { + stack.push(node.right); + } + if (node.left) { + stack.push(node.left); + } + } else { + if (node.left) { + node.val += node.left.val; + } + if (node.right) { + node.val += node.right.val; + } + + visit.delete(node); + node.val -= 1; + res += Math.abs(node.val); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/divide-array-into-arrays-with-max-difference.md b/articles/divide-array-into-arrays-with-max-difference.md new file mode 100644 index 000000000..d969ca851 --- /dev/null +++ b/articles/divide-array-into-arrays-with-max-difference.md @@ -0,0 +1,238 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def divideArray(self, nums: List[int], k: int) -> List[List[int]]: + nums.sort() + res = [] + + for i in range(0, len(nums), 3): + if nums[i + 2] - nums[i] > k: + return [] + res.append(nums[i: i + 3]) + + return res +``` + +```java +public class Solution { + public int[][] divideArray(int[] nums, int k) { + Arrays.sort(nums); + int[][] res = new int[nums.length / 3][3]; + + for (int i = 0, idx = 0; i < nums.length; i += 3, idx++) { + if (nums[i + 2] - nums[i] > k) { + return new int[][]{}; + } + res[idx][0] = nums[i]; + res[idx][1] = nums[i + 1]; + res[idx][2] = nums[i + 2]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> divideArray(vector& nums, int k) { + sort(nums.begin(), nums.end()); + vector> res; + + for (int i = 0; i < nums.size(); i += 3) { + if (nums[i + 2] - nums[i] > k) { + return {}; + } + res.push_back({nums[i], nums[i + 1], nums[i + 2]}); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number[][]} + */ + divideArray(nums, k) { + nums.sort((a, b) => a - b); + const res = []; + + for (let i = 0; i < nums.length; i += 3) { + if (nums[i + 2] - nums[i] > k) { + return []; + } + res.push([nums[i], nums[i + 1], nums[i + 2]]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ for the output array. + +--- + +## 2. Counting Sort + +::tabs-start + +```python +class Solution: + def divideArray(self, nums: List[int], k: int) -> List[List[int]]: + max_num = max(nums) + count = [0] * (max_num + 1) + for num in nums: + count[num] += 1 + + res = [] + group = [] + + for num in range(max_num + 1): + while count[num] > 0: + group.append(num) + count[num] -= 1 + + if len(group) == 3: + if group[2] - group[0] > k: + return [] + res.append(group) + group = [] + + return res +``` + +```java +public class Solution { + public int[][] divideArray(int[] nums, int k) { + int max = 0; + for (int num : nums) { + max = Math.max(max, num); + } + + int[] count = new int[max + 1]; + for (int num : nums) { + count[num]++; + } + + int[][] res = new int[nums.length / 3][3]; + int[] group = new int[3]; + int i = 0; + + for (int num = 0, idx = 0; num <= max; num++) { + while (count[num] > 0) { + group[i++] = num; + count[num]--; + + if (i == 3) { + if (group[2] - group[0] > k) { + return new int[][]{}; + } + for (int j = 0; j < 3; j++) { + res[idx][j] = group[j]; + } + i = 0; + idx++; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> divideArray(vector& nums, int k) { + int maxNum = *max_element(nums.begin(), nums.end()); + vector count(maxNum + 1, 0); + + for (int& num : nums) { + count[num]++; + } + + vector> res; + vector group; + + for (int num = 0; num <= maxNum; num++) { + while (count[num] > 0) { + group.push_back(num); + count[num]--; + + if (group.size() == 3) { + if (group[2] - group[0] > k) { + return {}; + } + res.push_back(group); + group.clear(); + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number[][]} + */ + divideArray(nums, k) { + const max = Math.max(...nums); + const count = new Array(max + 1).fill(0); + + for (const num of nums) { + count[num]++; + } + + const res = []; + let group = []; + + for (let num = 0; num <= max; num++) { + while (count[num] > 0) { + group.push(num); + count[num]--; + + if (group.length === 3) { + if (group[2] - group[0] > k) { + return []; + } + res.push(group); + group = []; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum element in $nums$. \ No newline at end of file diff --git a/articles/divide-array-into-equal-pairs.md b/articles/divide-array-into-equal-pairs.md new file mode 100644 index 000000000..bc8a01fb1 --- /dev/null +++ b/articles/divide-array-into-equal-pairs.md @@ -0,0 +1,290 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def divideArray(self, nums: List[int]) -> bool: + N = len(nums) + nums.sort() + + i = 0 + while i < N: + j = i + while j < N and nums[i] == nums[j]: + j += 1 + + if (j - i) % 2 != 0: + return False + + i = j + + return True +``` + +```java +public class Solution { + public boolean divideArray(int[] nums) { + int N = nums.length; + Arrays.sort(nums); + + int i = 0; + while (i < N) { + int j = i; + while (j < N && nums[i] == nums[j]) { + j++; + } + + if ((j - i) % 2 != 0) { + return false; + } + + i = j; + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool divideArray(vector& nums) { + int N = nums.size(); + sort(nums.begin(), nums.end()); + + int i = 0; + while (i < N) { + int j = i; + while (j < N && nums[i] == nums[j]) { + j++; + } + + if ((j - i) % 2 != 0) { + return false; + } + + i = j; + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + divideArray(nums) { + const N = nums.length; + nums.sort((a, b) => a - b); + + let i = 0; + while (i < N) { + let j = i; + while (j < N && nums[i] === nums[j]) { + j++; + } + + if ((j - i) % 2 !== 0) { + return false; + } + + i = j; + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def divideArray(self, nums: List[int]) -> bool: + count = {} + for num in nums: + if num not in count: + count[num] = 0 + count[num] += 1 + + for cnt in count.values(): + if cnt % 2 == 1: + return False + + return True +``` + +```java +public class Solution { + public boolean divideArray(int[] nums) { + Map count = new HashMap<>(); + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + for (int cnt : count.values()) { + if (cnt % 2 == 1) { + return false; + } + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool divideArray(vector& nums) { + unordered_map count; + for (int num : nums) { + count[num]++; + } + + for (auto& [key, cnt] : count) { + if (cnt % 2 == 1) { + return false; + } + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + divideArray(nums) { + const count = {}; + for (let num of nums) { + if (!(num in count)) { + count[num] = 0; + } + count[num]++; + } + + for (let key in count) { + if (count[key] % 2 === 1) { + return false; + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Hash Set + +::tabs-start + +```python +class Solution: + def divideArray(self, nums: List[int]) -> bool: + odd_set = set() + + for num in nums: + if num not in odd_set: + odd_set.add(num) + else: + odd_set.remove(num) + + return not len(odd_set) +``` + +```java +public class Solution { + public boolean divideArray(int[] nums) { + Set oddSet = new HashSet<>(); + + for (int num : nums) { + if (!oddSet.contains(num)) { + oddSet.add(num); + } else { + oddSet.remove(num); + } + } + + return oddSet.isEmpty(); + } +} +``` + +```cpp +class Solution { +public: + bool divideArray(vector& nums) { + unordered_set oddSet; + + for (int num : nums) { + if (oddSet.count(num)) { + oddSet.erase(num); + } else { + oddSet.insert(num); + } + } + + return oddSet.empty(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + divideArray(nums) { + const oddSet = new Set(); + + for (let num of nums) { + if (oddSet.has(num)) { + oddSet.delete(num); + } else { + oddSet.add(num); + } + } + + return oddSet.size === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/dota2-senate.md b/articles/dota2-senate.md new file mode 100644 index 000000000..4e30f1d55 --- /dev/null +++ b/articles/dota2-senate.md @@ -0,0 +1,423 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def predictPartyVictory(self, senate: str) -> str: + s = list(senate) + while True: + if 'R' not in s: + return "Dire" + if 'D' not in s: + return "Radiant" + + i = 0 + while i < len(s): + if s[i] == 'R': + j = (i + 1) % len(s) + while s[j] == 'R': + j = (j + 1) % len(s) + s.pop(j) + if j < i: + i -= 1 + else: + j = (i + 1) % len(s) + while s[j] == 'D': + j = (j + 1) % len(s) + s.pop(j) + if j < i: + i -= 1 + i += 1 +``` + +```java +public class Solution { + public String predictPartyVictory(String senate) { + List s = new ArrayList<>(); + for (char c : senate.toCharArray()) { + s.add(c); + } + + while (true) { + if (!s.contains('R')) { + return "Dire"; + } + if (!s.contains('D')) { + return "Radiant"; + } + + int i = 0; + while (i < s.size()) { + if (s.get(i) == 'R') { + int j = (i + 1) % s.size(); + while (s.get(j) == 'R') { + j = (j + 1) % s.size(); + } + s.remove(j); + if (j < i) { + i--; + } + } else { + int j = (i + 1) % s.size(); + while (s.get(j) == 'D') { + j = (j + 1) % s.size(); + } + s.remove(j); + if (j < i) { + i--; + } + } + i++; + } + } + } +} +``` + +```cpp +class Solution { +public: + string predictPartyVictory(string senate) { + vector s(senate.begin(), senate.end()); + + while (true) { + if (find(s.begin(), s.end(), 'R') == s.end()) { + return "Dire"; + } + if (find(s.begin(), s.end(), 'D') == s.end()) { + return "Radiant"; + } + + int i = 0; + while (i < s.size()) { + if (s[i] == 'R') { + int j = (i + 1) % s.size(); + while (s[j] == 'R') { + j = (j + 1) % s.size(); + } + s.erase(s.begin() + j); + if (j < i) { + i--; + } + } else { + int j = (i + 1) % s.size(); + while (s[j] == 'D') { + j = (j + 1) % s.size(); + } + s.erase(s.begin() + j); + if (j < i) { + i--; + } + } + i++; + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} senate + * @return {string} + */ + predictPartyVictory(senate) { + const s = senate.split(""); + + while (true) { + if (!s.includes("R")) { + return "Dire"; + } + if (!s.includes("D")) { + return "Radiant"; + } + + let i = 0; + while (i < s.length) { + if (s[i] === "R") { + let j = (i + 1) % s.length; + while (s[j] === "R") { + j = (j + 1) % s.length; + } + s.splice(j, 1); + if (j < i) { + i--; + } + } else { + let j = (i + 1) % s.length; + while (s[j] === "D") { + j = (j + 1) % s.length; + } + s.splice(j, 1); + if (j < i) { + i--; + } + } + i++; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Greedy (Two Queues) + +::tabs-start + +```python +class Solution: + def predictPartyVictory(self, senate: str) -> str: + D, R = deque(), deque() + n = len(senate) + + for i, c in enumerate(senate): + if c == 'R': + R.append(i) + else: + D.append(i) + + while D and R: + dTurn = D.popleft() + rTurn = R.popleft() + + if rTurn < dTurn: + R.append(rTurn + n) + else: + D.append(dTurn + n) + + return "Radiant" if R else "Dire" +``` + +```java +public class Solution { + public String predictPartyVictory(String senate) { + Queue R = new LinkedList<>(); + Queue D = new LinkedList<>(); + int n = senate.length(); + + for (int i = 0; i < n; i++) { + if (senate.charAt(i) == 'R') { + R.add(i); + } else { + D.add(i); + } + } + + while (!R.isEmpty() && !D.isEmpty()) { + int rTurn = R.poll(); + int dTurn = D.poll(); + + if (rTurn < dTurn) { + R.add(rTurn + n); + } else { + D.add(dTurn + n); + } + } + + return R.isEmpty() ? "Dire" : "Radiant"; + } +} +``` + +```cpp +class Solution { +public: + string predictPartyVictory(string senate) { + queue R, D; + int n = senate.size(); + + for (int i = 0; i < n; i++) { + if (senate[i] == 'R') { + R.push(i); + } else { + D.push(i); + } + } + + while (!R.empty() && !D.empty()) { + int rTurn = R.front(); R.pop(); + int dTurn = D.front(); D.pop(); + + if (rTurn < dTurn) { + R.push(rTurn + n); + } else { + D.push(dTurn + n); + } + } + + return R.empty() ? "Dire" : "Radiant"; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} senate + * @return {string} + */ + predictPartyVictory(senate) { + const R = new Queue(); + const D = new Queue(); + const n = senate.length; + + for (let i = 0; i < n; i++) { + if (senate[i] === "R") { + R.push(i); + } else { + D.push(i); + } + } + + while (!R.isEmpty() && !D.isEmpty()) { + const rTurn = R.pop(); + const dTurn = D.pop(); + + if (rTurn < dTurn) { + R.push(rTurn + n); + } else { + D.push(dTurn + n); + } + } + + return !R.isEmpty() ? "Radiant" : "Dire"; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Greedy + +::tabs-start + +```python +class Solution: + def predictPartyVictory(self, senate: str) -> str: + senate = list(senate) + cnt = i = 0 + + while i < len(senate): + c = senate[i] + if c == 'R': + if cnt < 0: + senate.append('D') + cnt += 1 + else: + if cnt > 0: + senate.append('R') + cnt -= 1 + i += 1 + + return "Radiant" if cnt > 0 else "Dire" +``` + +```java +public class Solution { + public String predictPartyVictory(String senate) { + StringBuilder sb = new StringBuilder(senate); + int cnt = 0, i = 0; + + while (i < sb.length()) { + char c = sb.charAt(i); + if (c == 'R') { + if (cnt < 0) { + sb.append('D'); + } + cnt++; + } else { + if (cnt > 0) { + sb.append('R'); + } + cnt--; + } + i++; + } + + return cnt > 0 ? "Radiant" : "Dire"; + } +} +``` + +```cpp +class Solution { +public: + string predictPartyVictory(string senate) { + int cnt = 0, i = 0; + + while (i < senate.size()) { + char c = senate[i]; + if (c == 'R') { + if (cnt < 0) { + senate.push_back('D'); + } + cnt++; + } else { + if (cnt > 0) { + senate.push_back('R'); + } + cnt--; + } + i++; + } + + return cnt > 0 ? "Radiant" : "Dire"; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} senate + * @return {string} + */ + predictPartyVictory(senate) { + let s = senate.split(''); + let cnt = 0, i = 0; + + while (i < s.length) { + const c = s[i]; + if (c === 'R') { + if (cnt < 0) { + s.push('D'); + } + cnt++; + } else { + if (cnt > 0) { + s.push('R'); + } + cnt--; + } + i++; + } + + return cnt > 0 ? "Radiant" : "Dire"; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/duplicate-integer.md b/articles/duplicate-integer.md new file mode 100644 index 000000000..882486892 --- /dev/null +++ b/articles/duplicate-integer.md @@ -0,0 +1,466 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def hasDuplicate(self, nums: List[int]) -> bool: + for i in range(len(nums)): + for j in range(i + 1, len(nums)): + if nums[i] == nums[j]: + return True + return False +``` + +```java +public class Solution { + public boolean hasDuplicate(int[] nums) { + for (int i = 0; i < nums.length; i++) { + for (int j = i + 1; j < nums.length; j++) { + if (nums[i] == nums[j]) { + return true; + } + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool hasDuplicate(vector& nums) { + for (int i = 0; i < nums.size(); i++) { + for (int j = i + 1; j < nums.size(); j++) { + if (nums[i] == nums[j]) { + return true; + } + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + hasDuplicate(nums) { + for (let i = 0; i < nums.length; i++) { + for (let j = i + 1; j < nums.length; j++) { + if (nums[i] === nums[j]) { + return true; + } + } + } + return false; + } +} +``` + +```csharp +public class Solution { + public bool HasDuplicate(int[] nums) { + for (int i = 0; i < nums.Length; i++) { + for (int j = i + 1; j < nums.Length; j++) { + if (nums[i] == nums[j]) { + return true; + } + } + } + return false; + } +} +``` + +```go +func hasDuplicate(nums []int) bool { + for i := 0; i < len(nums); i++ { + for j := i + 1; j < len(nums); j++ { + if nums[i] == nums[j] { + return true + } + } + } + return false +} +``` + +```kotlin +class Solution { + fun hasDuplicate(nums: IntArray): Boolean { + for (i in nums.indices) { + for (j in i + 1 until nums.size) { + if (nums[i] == nums[j]) { + return true + } + } + } + return false + } +} +``` + +```swift +class Solution { + func hasDuplicate(_ nums: [Int]) -> Bool { + for i in 0.. bool: + nums.sort() + for i in range(1, len(nums)): + if nums[i] == nums[i - 1]: + return True + return False +``` + +```java +public class Solution { + public boolean hasDuplicate(int[] nums) { + Arrays.sort(nums); + for (int i = 1; i < nums.length; i++) { + if (nums[i] == nums[i - 1]) { + return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool hasDuplicate(vector& nums) { + sort(nums.begin(), nums.end()); + for (int i = 1; i < nums.size(); i++) { + if (nums[i] == nums[i - 1]) { + return true; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + hasDuplicate(nums) { + nums.sort((a, b) => a - b); + for (let i = 1; i < nums.length; i++) { + if (nums[i] === nums[i - 1]) { + return true; + } + } + return false; + } +} +``` + +```csharp +public class Solution { + public bool HasDuplicate(int[] nums) { + Array.Sort(nums); + for (int i = 1; i < nums.Length; i++) { + if (nums[i] == nums[i - 1]) { + return true; + } + } + return false; + } +} +``` + +```go +func hasDuplicate(nums []int) bool { + sort.Ints(nums) + for i := 1; i < len(nums); i++ { + if nums[i] == nums[i-1] { + return true + } + } + return false +} +``` + +```kotlin +class Solution { + fun hasDuplicate(nums: IntArray): Boolean { + nums.sort() + for (i in 1 until nums.size) { + if (nums[i] == nums[i - 1]) { + return true + } + } + return false + } +} +``` + +```swift +class Solution { + func hasDuplicate(_ nums: [Int]) -> Bool { + var nums = nums.sorted() + for i in 1.. bool: + seen = set() + for num in nums: + if num in seen: + return True + seen.add(num) + return False +``` + +```java +public class Solution { + public boolean hasDuplicate(int[] nums) { + Set seen = new HashSet<>(); + for (int num : nums) { + if (seen.contains(num)) { + return true; + } + seen.add(num); + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool hasDuplicate(vector& nums) { + unordered_set seen; + for (int num : nums) { + if (seen.count(num)) { + return true; + } + seen.insert(num); + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + hasDuplicate(nums) { + const seen = new Set(); + for (const num of nums) { + if (seen.has(num)) { + return true; + } + seen.add(num); + } + return false; + } +} +``` + +```csharp +public class Solution { + public bool HasDuplicate(int[] nums) { + HashSet seen = new HashSet(); + foreach (int num in nums) { + if (seen.Contains(num)) { + return true; + } + seen.Add(num); + } + return false; + } +} +``` + +```go +func hasDuplicate(nums []int) bool { + seen := make(map[int]bool) + for _, num := range nums { + if seen[num] { + return true + } + seen[num] = true + } + return false +} +``` + +```kotlin +class Solution { + fun hasDuplicate(nums: IntArray): Boolean { + val seen = HashSet() + for (num in nums) { + if (num in seen) { + return true + } + seen.add(num) + } + return false + } +} +``` + +```swift +class Solution { + func hasDuplicate(_ nums: [Int]) -> Bool { + var seen = Set() + for num in nums { + if seen.contains(num) { + return true + } + seen.insert(num) + } + return false + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Hash Set Length + +::tabs-start + +```python +class Solution: + def hasDuplicate(self, nums: List[int]) -> bool: + return len(set(nums)) < len(nums) +``` + +```java +public class Solution { + public boolean hasDuplicate(int[] nums) { + return Arrays.stream(nums).distinct().count() < nums.length; + } +} +``` + +```cpp +class Solution { +public: + bool hasDuplicate(vector& nums) { + return unordered_set(nums.begin(), nums.end()).size() < nums.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + hasDuplicate(nums) { + return new Set(nums).size < nums.length; + } +} +``` + +```csharp +public class Solution { + public bool HasDuplicate(int[] nums) { + return new HashSet(nums).Count < nums.Length; + } +} +``` + +```go +func hasDuplicate(nums []int) bool { + seen := make(map[int]struct{}) + for _, num := range nums { + seen[num] = struct{}{} + } + return len(seen) < len(nums) +} +``` + +```kotlin +class Solution { + fun hasDuplicate(nums: IntArray): Boolean { + return nums.toSet().size < nums.size + } +} +``` + +```swift +class Solution { + func hasDuplicate(_ nums: [Int]) -> Bool { + return Set(nums).count < nums.count + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/eating-bananas.md b/articles/eating-bananas.md new file mode 100644 index 000000000..4acd82fb0 --- /dev/null +++ b/articles/eating-bananas.md @@ -0,0 +1,395 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def minEatingSpeed(self, piles: List[int], h: int) -> int: + speed = 1 + while True: + totalTime = 0 + for pile in piles: + totalTime += math.ceil(pile / speed) + + if totalTime <= h: + return speed + speed += 1 + return speed +``` + +```java +public class Solution { + public int minEatingSpeed(int[] piles, int h) { + int speed = 1; + while (true) { + long totalTime = 0; + for (int pile : piles) { + totalTime += (int) Math.ceil((double) pile / speed); + } + + if (totalTime <= h) { + return speed; + } + speed++; + } + } +} +``` + +```cpp +class Solution { +public: + int minEatingSpeed(vector& piles, int h) { + int speed = 1; + while (true) { + long long totalTime = 0; + for (int pile : piles) { + totalTime += (pile + speed - 1) / speed; + } + + if (totalTime <= h) { + return speed; + } + speed++; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} piles + * @param {number} h + * @return {number} + */ + minEatingSpeed(piles, h) { + let speed = 1; + while (true) { + let totalTime = 0; + for (let pile of piles) { + totalTime += Math.ceil(pile / speed); + } + + if (totalTime <= h) { + return speed; + } + speed++; + } + } +} +``` + +```csharp +public class Solution { + public int MinEatingSpeed(int[] piles, int h) { + int speed = 1; + while (true) { + long totalTime = 0; + foreach (int pile in piles) { + totalTime += (int) Math.Ceiling((double) pile / speed); + } + + if (totalTime <= h) { + return speed; + } + speed++; + } + } +} +``` + +```go +func minEatingSpeed(piles []int, h int) int { + speed := 1 + for { + totalTime := 0 + for _, pile := range piles { + totalTime += int(math.Ceil(float64(pile) / float64(speed))) + } + + if totalTime <= h { + return speed + } + speed += 1 + } + return speed +} +``` + +```kotlin +class Solution { + fun minEatingSpeed(piles: IntArray, h: Int): Int { + var speed = 1 + while (true) { + var totalTime = 0L + for (pile in piles) { + totalTime += Math.ceil(pile.toDouble() / speed).toLong() + } + + if (totalTime <= h) { + return speed + } + speed += 1 + } + return speed + } +} +``` + +```swift +class Solution { + func minEatingSpeed(_ piles: [Int], _ h: Int) -> Int { + var speed = 1 + + while true { + var totalTime = 0 + for pile in piles { + totalTime += Int(ceil(Double(pile) / Double(speed))) + } + + if totalTime <= h { + return speed + } + speed += 1 + } + return speed + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(1)$ + +> Where $n$ is the length of the input array $piles$ and $m$ is the maximum number of bananas in a pile. + +--- + +## 2. Binary Search + +::tabs-start + +```python +class Solution: + def minEatingSpeed(self, piles: List[int], h: int) -> int: + l, r = 1, max(piles) + res = r + + while l <= r: + k = (l + r) // 2 + + totalTime = 0 + for p in piles: + totalTime += math.ceil(float(p) / k) + if totalTime <= h: + res = k + r = k - 1 + else: + l = k + 1 + return res +``` + +```java +public class Solution { + public int minEatingSpeed(int[] piles, int h) { + int l = 1; + int r = Arrays.stream(piles).max().getAsInt(); + int res = r; + + while (l <= r) { + int k = (l + r) / 2; + + long totalTime = 0; + for (int p : piles) { + totalTime += Math.ceil((double) p / k); + } + if (totalTime <= h) { + res = k; + r = k - 1; + } else { + l = k + 1; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minEatingSpeed(vector& piles, int h) { + int l = 1; + int r = *max_element(piles.begin(), piles.end()); + int res = r; + + while (l <= r) { + int k = (l + r) / 2; + + long long totalTime = 0; + for (int p : piles) { + totalTime += ceil(static_cast(p) / k); + } + if (totalTime <= h) { + res = k; + r = k - 1; + } else { + l = k + 1; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} piles + * @param {number} h + * @return {number} + */ + minEatingSpeed(piles, h) { + let l = 1; + let r = Math.max(...piles); + let res = r; + + while (l <= r) { + const k = Math.floor((l + r) / 2); + + let totalTime = 0; + for (const p of piles) { + totalTime += Math.ceil(p / k); + } + if (totalTime <= h) { + res = k; + r = k - 1; + } else { + l = k + 1; + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int MinEatingSpeed(int[] piles, int h) { + int l = 1; + int r = piles.Max(); + int res = r; + + while (l <= r) { + int k = (l + r) / 2; + + long totalTime = 0; + foreach (int p in piles) { + totalTime += (int)Math.Ceiling((double)p / k); + } + if (totalTime <= h) { + res = k; + r = k - 1; + } else { + l = k + 1; + } + } + return res; + } +} +``` + +```go +func minEatingSpeed(piles []int, h int) int { + l, r := 1, 0 + for _, p := range piles { + if p > r { + r = p + } + } + res := r + + for l <= r { + k := (l + r) / 2 + totalTime := 0 + + for _, p := range piles { + totalTime += int(math.Ceil(float64(p) / float64(k))) + } + + if totalTime <= h { + res = k + r = k - 1 + } else { + l = k + 1 + } + } + return res +} +``` + +```kotlin +class Solution { + fun minEatingSpeed(piles: IntArray, h: Int): Int { + var l = 1 + var r = piles.max()!! + var res = r + + while (l <= r) { + val k = (l + r) / 2 + var totalTime = 0L + + for (p in piles) { + totalTime += Math.ceil(p.toDouble() / k).toLong() + } + + if (totalTime <= h) { + res = k + r = k - 1 + } else { + l = k + 1 + } + } + return res + } +} +``` + +```swift +class Solution { + func minEatingSpeed(_ piles: [Int], _ h: Int) -> Int { + var l = 1, r = piles.max()! + var res = r + + while l <= r { + let k = (l + r) / 2 + + var totalTime = 0 + for p in piles { + totalTime += Int(ceil(Double(p) / Double(k))) + } + + if totalTime <= h { + res = k + r = k - 1 + } else { + l = k + 1 + } + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * \log m)$ +* Space complexity: $O(1)$ + +> Where $n$ is the length of the input array $piles$ and $m$ is the maximum number of bananas in a pile. \ No newline at end of file diff --git a/articles/edit-distance.md b/articles/edit-distance.md new file mode 100644 index 000000000..7c4b6bfeb --- /dev/null +++ b/articles/edit-distance.md @@ -0,0 +1,1285 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def minDistance(self, word1: str, word2: str) -> int: + m, n = len(word1), len(word2) + + def dfs(i, j): + if i == m: + return n - j + if j == n: + return m - i + if word1[i] == word2[j]: + return dfs(i + 1, j + 1) + res = min(dfs(i + 1, j), dfs(i, j + 1)) + res = min(res, dfs(i + 1, j + 1)) + return res + 1 + + return dfs(0, 0) +``` + +```java +public class Solution { + public int minDistance(String word1, String word2) { + int m = word1.length(), n = word2.length(); + + return dfs(0, 0, word1, word2, m, n); + } + + private int dfs(int i, int j, String word1, String word2, int m, int n) { + if (i == m) return n - j; + if (j == n) return m - i; + + if (word1.charAt(i) == word2.charAt(j)) { + return dfs(i + 1, j + 1, word1, word2, m, n); + } + + int res = Math.min(dfs(i + 1, j, word1, word2, m, n), + dfs(i, j + 1, word1, word2, m, n)); + res = Math.min(res, dfs(i + 1, j + 1, word1, word2, m, n)); + return res + 1; + } +} +``` + +```cpp +class Solution { +public: + int minDistance(string word1, string word2) { + int m = word1.size(), n = word2.size(); + return dfs(0, 0, word1, word2, m, n); + } + + int dfs(int i, int j, string& word1, string& word2, int m, int n) { + if (i == m) return n - j; + if (j == n) return m - i; + if (word1[i] == word2[j]){ + return dfs(i + 1, j + 1, word1, word2, m, n); + } + + int res = min(dfs(i + 1, j, word1, word2, m, n), + dfs(i, j + 1, word1, word2, m, n)); + res = min(res, dfs(i + 1, j + 1, word1, word2, m, n)); + return res + 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} word1 + * @param {string} word2 + * @return {number} + */ + minDistance(word1, word2) { + const m = word1.length, n = word2.length; + + const dfs = (i, j) => { + if (i === m) return n - j; + if (j === n) return m - i; + if (word1[i] === word2[j]) { + return dfs(i + 1, j + 1); + } + let res = Math.min(dfs(i + 1, j), dfs(i, j + 1)); + res = Math.min(res, dfs(i + 1, j + 1)); + return res + 1; + }; + + return dfs(0, 0); + } +} +``` + +```csharp +public class Solution { + public int MinDistance(string word1, string word2) { + int m = word1.Length, n = word2.Length; + return Dfs(0, 0, word1, word2, m, n); + } + + private int Dfs(int i, int j, string word1, string word2, int m, int n) { + if (i == m) return n - j; + if (j == n) return m - i; + + if (word1[i] == word2[j]) { + return Dfs(i + 1, j + 1, word1, word2, m, n); + } + + int res = Math.Min(Dfs(i + 1, j, word1, word2, m, n), + Dfs(i, j + 1, word1, word2, m, n)); + res = Math.Min(res, Dfs(i + 1, j + 1, word1, word2, m, n)); + return res + 1; + } +} +``` + +```go +func minDistance(word1 string, word2 string) int { + m, n := len(word1), len(word2) + + var dfs func(i, j int) int + dfs = func(i, j int) int { + if i == m { + return n - j + } + if j == n { + return m - i + } + if word1[i] == word2[j] { + return dfs(i+1, j+1) + } + res := min(dfs(i+1, j), dfs(i, j+1)) + res = min(res, dfs(i+1, j+1)) + return res + 1 + } + + return dfs(0, 0) +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun minDistance(word1: String, word2: String): Int { + val m = word1.length + val n = word2.length + + fun dfs(i: Int, j: Int): Int { + if (i == m) return n - j + if (j == n) return m - i + if (word1[i] == word2[j]) return dfs(i + 1, j + 1) + + var res = minOf(dfs(i + 1, j), dfs(i, j + 1)) + res = minOf(res, dfs(i + 1, j + 1)) + return res + 1 + } + + return dfs(0, 0) + } +} +``` + +```swift +class Solution { + func minDistance(_ word1: String, _ word2: String) -> Int { + let m = word1.count, n = word2.count + let word1Array = Array(word1), word2Array = Array(word2) + + func dfs(_ i: Int, _ j: Int) -> Int { + if i == m { return n - j } + if j == n { return m - i } + if word1Array[i] == word2Array[j] { + return dfs(i + 1, j + 1) + } + let res = min(dfs(i + 1, j), dfs(i, j + 1)) + return min(res, dfs(i + 1, j + 1)) + 1 + } + + return dfs(0, 0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(3 ^ {m + n})$ +* Space complexity: $O(m + n)$ + +> Where $m$ is the length of $word1$ and $n$ is the length of $word2$. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minDistance(self, word1: str, word2: str) -> int: + m, n = len(word1), len(word2) + + dp = {} + def dfs(i, j): + if i == m: + return n - j + if j == n: + return m - i + if (i, j) in dp: + return dp[(i, j)] + + if word1[i] == word2[j]: + dp[(i, j)] = dfs(i + 1, j + 1) + else: + res = min(dfs(i + 1, j), dfs(i, j + 1)) + res = min(res, dfs(i + 1, j + 1)) + dp[(i, j)] = res + 1 + return dp[(i, j)] + + return dfs(0, 0) +``` + +```java +public class Solution { + private int[][] dp; + public int minDistance(String word1, String word2) { + int m = word1.length(), n = word2.length(); + dp = new int[m][n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + dp[i][j] = -1; + } + } + return dfs(0, 0, word1, word2, m, n); + } + + private int dfs(int i, int j, String word1, String word2, int m, int n) { + if (i == m) return n - j; + if (j == n) return m - i; + if (dp[i][j] != -1) return dp[i][j]; + + if (word1.charAt(i) == word2.charAt(j)) { + dp[i][j] = dfs(i + 1, j + 1, word1, word2, m, n); + } else { + int res = Math.min(dfs(i + 1, j, word1, word2, m, n), + dfs(i, j + 1, word1, word2, m, n)); + res = Math.min(res, dfs(i + 1, j + 1, word1, word2, m, n)); + dp[i][j] = res + 1; + } + return dp[i][j]; + } +} +``` + +```cpp +class Solution { + vector> dp; +public: + int minDistance(string word1, string word2) { + int m = word1.size(), n = word2.size(); + dp = vector>(m, vector(n, -1)); + return dfs(0, 0, word1, word2, m, n); + } + + int dfs(int i, int j, string& word1, string& word2, int m, int n) { + if (i == m) return n - j; + if (j == n) return m - i; + if (dp[i][j] != -1) return dp[i][j]; + if (word1[i] == word2[j]){ + dp[i][j] = dfs(i + 1, j + 1, word1, word2, m, n); + } else { + int res = min(dfs(i + 1, j, word1, word2, m, n), + dfs(i, j + 1, word1, word2, m, n)); + res = min(res, dfs(i + 1, j + 1, word1, word2, m, n)); + dp[i][j] = res + 1; + } + return dp[i][j]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} word1 + * @param {string} word2 + * @return {number} + */ + minDistance(word1, word2) { + const m = word1.length, n = word2.length; + let dp = Array.from({ length: m + 1 }, () => + Array(n + 1).fill(-1)); + const dfs = (i, j) => { + if (i === m) return n - j; + if (j === n) return m - i; + if (dp[i][j] != -1) return dp[i][j]; + + if (word1[i] === word2[j]) { + dp[i][j] = dfs(i + 1, j + 1); + } else { + let res = Math.min(dfs(i + 1, j), dfs(i, j + 1)); + res = Math.min(res, dfs(i + 1, j + 1)); + dp[i][j] = res + 1; + } + return dp[i][j]; + }; + + return dfs(0, 0); + } +} +``` + +```csharp +public class Solution { + private int?[,] dp; + public int MinDistance(string word1, string word2) { + int m = word1.Length, n = word2.Length; + dp = new int?[m + 1, n + 1]; + return Dfs(0, 0, word1, word2, m, n); + } + + private int Dfs(int i, int j, string word1, string word2, int m, int n) { + if (i == m) return n - j; + if (j == n) return m - i; + + if (dp[i, j].HasValue) { + return dp[i, j].Value; + } + + if (word1[i] == word2[j]) { + dp[i, j] = Dfs(i + 1, j + 1, word1, word2, m, n); + } else { + int res = Math.Min(Dfs(i + 1, j, word1, word2, m, n), + Dfs(i, j + 1, word1, word2, m, n)); + res = Math.Min(res, Dfs(i + 1, j + 1, word1, word2, m, n)); + dp[i, j] = res + 1; + } + + return dp[i, j].Value; + } +} +``` + +```go +func minDistance(word1 string, word2 string) int { + m, n := len(word1), len(word2) + dp := make([][]int, m+1) + for i := range dp { + dp[i] = make([]int, n+1) + for j := range dp[i] { + dp[i][j] = -1 + } + } + + var dfs func(i, j int) int + dfs = func(i, j int) int { + if i == m { + return n - j + } + if j == n { + return m - i + } + if dp[i][j] != -1 { + return dp[i][j] + } + + if word1[i] == word2[j] { + dp[i][j] = dfs(i+1, j+1) + } else { + insert := dfs(i, j+1) + delete := dfs(i+1, j) + replace := dfs(i+1, j+1) + dp[i][j] = 1 + min(insert, min(delete, replace)) + } + return dp[i][j] + } + + return dfs(0, 0) +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun minDistance(word1: String, word2: String): Int { + val m = word1.length + val n = word2.length + val dp = Array(m + 1) { IntArray(n + 1) { -1 } } + + fun dfs(i: Int, j: Int): Int { + if (i == m) return n - j + if (j == n) return m - i + if (dp[i][j] != -1) return dp[i][j] + + dp[i][j] = if (word1[i] == word2[j]) { + dfs(i + 1, j + 1) + } else { + val insert = dfs(i, j + 1) + val delete = dfs(i + 1, j) + val replace = dfs(i + 1, j + 1) + 1 + minOf(insert, delete, replace) + } + return dp[i][j] + } + + return dfs(0, 0) + } +} +``` + +```swift +class Solution { + func minDistance(_ word1: String, _ word2: String) -> Int { + let m = word1.count, n = word2.count + let word1Array = Array(word1), word2Array = Array(word2) + var dp = [[Int?]](repeating: [Int?](repeating: nil, count: n + 1), count: m + 1) + + func dfs(_ i: Int, _ j: Int) -> Int { + if i == m { return n - j } + if j == n { return m - i } + if let val = dp[i][j] { return val } + + if word1Array[i] == word2Array[j] { + dp[i][j] = dfs(i + 1, j + 1) + } else { + let res = min(dfs(i + 1, j), dfs(i, j + 1), dfs(i + 1, j + 1)) + 1 + dp[i][j] = res + } + return dp[i][j]! + } + + return dfs(0, 0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the length of $word1$ and $n$ is the length of $word2$. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minDistance(self, word1: str, word2: str) -> int: + dp = [[float("inf")] * (len(word2) + 1) for i in range(len(word1) + 1)] + + for j in range(len(word2) + 1): + dp[len(word1)][j] = len(word2) - j + for i in range(len(word1) + 1): + dp[i][len(word2)] = len(word1) - i + + for i in range(len(word1) - 1, -1, -1): + for j in range(len(word2) - 1, -1, -1): + if word1[i] == word2[j]: + dp[i][j] = dp[i + 1][j + 1] + else: + dp[i][j] = 1 + min(dp[i + 1][j], dp[i][j + 1], dp[i + 1][j + 1]) + return dp[0][0] +``` + +```java +public class Solution { + public int minDistance(String word1, String word2) { + int[][] dp = new int[word1.length() + 1][word2.length() + 1]; + + for (int j = 0; j <= word2.length(); j++) { + dp[word1.length()][j] = word2.length() - j; + } + for (int i = 0; i <= word1.length(); i++) { + dp[i][word2.length()] = word1.length() - i; + } + + for (int i = word1.length() - 1; i >= 0; i--) { + for (int j = word2.length() - 1; j >= 0; j--) { + if (word1.charAt(i) == word2.charAt(j)) { + dp[i][j] = dp[i + 1][j + 1]; + } else { + dp[i][j] = 1 + Math.min(dp[i + 1][j], + Math.min(dp[i][j + 1], dp[i + 1][j + 1])); + } + } + } + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + int minDistance(string word1, string word2) { + vector> dp(word1.length() + 1, + vector(word2.length() + 1, 0)); + + for (int j = 0; j <= word2.length(); j++) { + dp[word1.length()][j] = word2.length() - j; + } + for (int i = 0; i <= word1.length(); i++) { + dp[i][word2.length()] = word1.length() - i; + } + + for (int i = word1.length() - 1; i >= 0; i--) { + for (int j = word2.length() - 1; j >= 0; j--) { + if (word1[i] == word2[j]) { + dp[i][j] = dp[i + 1][j + 1]; + } else { + dp[i][j] = 1 + min(dp[i + 1][j], + min(dp[i][j + 1], dp[i + 1][j + 1])); + } + } + } + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} word1 + * @param {string} word2 + * @return {number} + */ + minDistance(word1, word2) { + const dp = new Array(word1.length + 1) + .fill(0) + .map(() => new Array(word2.length + 1).fill(0)); + + for (let j = 0; j <= word2.length; j++) { + dp[word1.length][j] = word2.length - j; + } + for (let i = 0; i <= word1.length; i++) { + dp[i][word2.length] = word1.length - i; + } + + for (let i = word1.length - 1; i >= 0; i--) { + for (let j = word2.length - 1; j >= 0; j--) { + if (word1[i] === word2[j]) { + dp[i][j] = dp[i + 1][j + 1]; + } else { + dp[i][j] = + 1 + + Math.min( + dp[i + 1][j], + Math.min(dp[i][j + 1], dp[i + 1][j + 1]), + ); + } + } + } + return dp[0][0]; + } +} +``` + +```csharp +public class Solution { + public int MinDistance(string word1, string word2) { + int[,] dp = new int[word1.Length + 1, word2.Length + 1]; + + for (int j = 0; j <= word2.Length; j++) { + dp[word1.Length, j] = word2.Length - j; + } + for (int i = 0; i <= word1.Length; i++) { + dp[i, word2.Length] = word1.Length - i; + } + + for (int i = word1.Length - 1; i >= 0; i--) { + for (int j = word2.Length - 1; j >= 0; j--) { + if (word1[i] == word2[j]) { + dp[i, j] = dp[i + 1, j + 1]; + } else { + dp[i, j] = 1 + Math.Min(dp[i + 1, j], + Math.Min(dp[i, j + 1], dp[i + 1, j + 1])); + } + } + } + return dp[0, 0]; + } +} +``` + +```go +func minDistance(word1, word2 string) int { + m, n := len(word1), len(word2) + dp := make([][]int, m+1) + for i := range dp { + dp[i] = make([]int, n+1) + } + + for j := 0; j <= n; j++ { + dp[m][j] = n - j + } + for i := 0; i <= m; i++ { + dp[i][n] = m - i + } + + for i := m - 1; i >= 0; i-- { + for j := n - 1; j >= 0; j-- { + if word1[i] == word2[j] { + dp[i][j] = dp[i+1][j+1] + } else { + dp[i][j] = 1 + min(dp[i+1][j], + min(dp[i][j+1], dp[i+1][j+1])) + } + } + } + + return dp[0][0] +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun minDistance(word1: String, word2: String): Int { + val m = word1.length + val n = word2.length + val dp = Array(m + 1) { IntArray(n + 1) } + + for (j in 0..n) { + dp[m][j] = n - j + } + for (i in 0..m) { + dp[i][n] = m - i + } + + for (i in m - 1 downTo 0) { + for (j in n - 1 downTo 0) { + if (word1[i] == word2[j]) { + dp[i][j] = dp[i + 1][j + 1] + } else { + dp[i][j] = 1 + minOf(dp[i + 1][j], + minOf(dp[i][j + 1], dp[i + 1][j + 1])) + } + } + } + return dp[0][0] + } +} +``` + +```swift +class Solution { + func minDistance(_ word1: String, _ word2: String) -> Int { + let m = word1.count, n = word2.count + let word1Array = Array(word1), word2Array = Array(word2) + var dp = [[Int]](repeating: [Int](repeating: Int.max, count: n + 1), count: m + 1) + + for j in 0...n { + dp[m][j] = n - j + } + for i in 0...m { + dp[i][n] = m - i + } + + for i in stride(from: m - 1, through: 0, by: -1) { + for j in stride(from: n - 1, through: 0, by: -1) { + if word1Array[i] == word2Array[j] { + dp[i][j] = dp[i + 1][j + 1] + } else { + dp[i][j] = 1 + min(dp[i + 1][j], dp[i][j + 1], dp[i + 1][j + 1]) + } + } + } + return dp[0][0] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the length of $word1$ and $n$ is the length of $word2$. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def minDistance(self, word1: str, word2: str) -> int: + m, n = len(word1), len(word2) + if m < n: + m, n = n, m + word1, word2 = word2, word1 + + dp = [0] * (n + 1) + nextDp = [0] * (n + 1) + + for j in range(n + 1): + dp[j] = n - j + + for i in range(m - 1, -1, -1): + nextDp[n] = m - i + for j in range(n - 1, -1, -1): + if word1[i] == word2[j]: + nextDp[j] = dp[j + 1] + else: + nextDp[j] = 1 + min(dp[j], nextDp[j + 1], dp[j + 1]) + dp = nextDp[:] + + return dp[0] +``` + +```java +class Solution { + public int minDistance(String word1, String word2) { + int m = word1.length(), n = word2.length(); + if (m < n) { + int temp = m; + m = n; + n = temp; + String t = word1; + word1 = word2; + word2 = t; + } + + int[] dp = new int[n + 1]; + int[] nextDp = new int[n + 1]; + + for (int j = 0; j <= n; j++) { + dp[j] = n - j; + } + + for (int i = m - 1; i >= 0; i--) { + nextDp[n] = m - i; + for (int j = n - 1; j >= 0; j--) { + if (word1.charAt(i) == word2.charAt(j)) { + nextDp[j] = dp[j + 1]; + } else { + nextDp[j] = 1 + Math.min(dp[j], + Math.min(nextDp[j + 1], dp[j + 1])); + } + } + System.arraycopy(nextDp, 0, dp, 0, n + 1); + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int minDistance(string word1, string word2) { + int m = word1.size(), n = word2.size(); + if (m < n) { + swap(m, n); + swap(word1, word2); + } + + vector dp(n + 1), nextDp(n + 1); + + for (int j = 0; j <= n; ++j) { + dp[j] = n - j; + } + + for (int i = m - 1; i >= 0; --i) { + nextDp[n] = m - i; + for (int j = n - 1; j >= 0; --j) { + if (word1[i] == word2[j]) { + nextDp[j] = dp[j + 1]; + } else { + nextDp[j] = 1 + min({dp[j], nextDp[j + 1], dp[j + 1]}); + } + } + dp = nextDp; + } + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} word1 + * @param {string} word2 + * @return {number} + */ + minDistance(word1, word2) { + let m = word1.length, n = word2.length; + if (m < n) { + [m, n] = [n, m]; + [word1, word2] = [word2, word1]; + } + + let dp = new Array(n + 1).fill(0); + let nextDp = new Array(n + 1).fill(0); + + for (let j = 0; j <= n; j++) { + dp[j] = n - j; + } + + for (let i = m - 1; i >= 0; i--) { + nextDp[n] = m - i; + for (let j = n - 1; j >= 0; j--) { + if (word1[i] === word2[j]) { + nextDp[j] = dp[j + 1]; + } else { + nextDp[j] = 1 + Math.min(dp[j], + Math.min(nextDp[j + 1], dp[j + 1])); + } + } + dp = [...nextDp]; + } + + return dp[0]; + } +} +``` + +```csharp +public class Solution { + public int MinDistance(string word1, string word2) { + int m = word1.Length, n = word2.Length; + if (m < n) { + var temp = m; + m = n; + n = temp; + var t = word1; + word1 = word2; + word2 = t; + } + + int[] dp = new int[n + 1]; + int[] nextDp = new int[n + 1]; + + for (int j = 0; j <= n; j++) { + dp[j] = n - j; + } + + for (int i = m - 1; i >= 0; i--) { + nextDp[n] = m - i; + for (int j = n - 1; j >= 0; j--) { + if (word1[i] == word2[j]) { + nextDp[j] = dp[j + 1]; + } else { + nextDp[j] = 1 + Math.Min(dp[j], + Math.Min(nextDp[j + 1], dp[j + 1])); + } + } + Array.Copy(nextDp, dp, n + 1); + } + + return dp[0]; + } +} +``` + +```go +func minDistance(word1, word2 string) int { + m, n := len(word1), len(word2) + if m < n { + word1, word2 = word2, word1 + m, n = n, m + } + + dp := make([]int, n+1) + nextDp := make([]int, n+1) + + for j := 0; j <= n; j++ { + dp[j] = n - j + } + + for i := m - 1; i >= 0; i-- { + nextDp[n] = m - i + for j := n - 1; j >= 0; j-- { + if word1[i] == word2[j] { + nextDp[j] = dp[j+1] + } else { + nextDp[j] = 1 + min(dp[j], + min(nextDp[j+1], dp[j+1])) + } + } + dp, nextDp = nextDp, dp + } + + return dp[0] +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun minDistance(word1: String, word2: String): Int { + var m = word1.length + var n = word2.length + var word1Mod = word1 + var word2Mod = word2 + + if (m < n) { + word1Mod = word2 + word2Mod = word1 + m = word1Mod.length + n = word2Mod.length + } + + val dp = IntArray(n + 1) + val nextDp = IntArray(n + 1) + + for (j in 0..n) { + dp[j] = n - j + } + + for (i in m - 1 downTo 0) { + nextDp[n] = m - i + for (j in n - 1 downTo 0) { + if (word1Mod[i] == word2Mod[j]) { + nextDp[j] = dp[j + 1] + } else { + nextDp[j] = 1 + minOf(dp[j], nextDp[j + 1], dp[j + 1]) + } + } + dp.indices.forEach { dp[it] = nextDp[it] } + } + + return dp[0] + } +} +``` + +```swift +class Solution { + func minDistance(_ word1: String, _ word2: String) -> Int { + var m = word1.count, n = word2.count + var word1Array = Array(word1), word2Array = Array(word2) + + if m < n { + swap(&m, &n) + swap(&word1Array, &word2Array) + } + + var dp = [Int](repeating: 0, count: n + 1) + var nextDp = [Int](repeating: 0, count: n + 1) + + for j in 0...n { + dp[j] = n - j + } + + for i in stride(from: m - 1, through: 0, by: -1) { + nextDp[n] = m - i + for j in stride(from: n - 1, through: 0, by: -1) { + if word1Array[i] == word2Array[j] { + nextDp[j] = dp[j + 1] + } else { + nextDp[j] = 1 + min(dp[j], nextDp[j + 1], dp[j + 1]) + } + } + dp = nextDp + } + + return dp[0] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(min(m, n))$ + +> Where $m$ is the length of $word1$ and $n$ is the length of $word2$. + +--- + +## 5. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def minDistance(self, word1: str, word2: str) -> int: + m, n = len(word1), len(word2) + if m < n: + m, n = n, m + word1, word2 = word2, word1 + + dp = [n - i for i in range(n + 1)] + + for i in range(m - 1, -1, -1): + nextDp = dp[n] + dp[n] = m - i + for j in range(n - 1, -1, -1): + temp = dp[j] + if word1[i] == word2[j]: + dp[j] = nextDp + else: + dp[j] = 1 + min(dp[j], dp[j + 1], nextDp) + nextDp = temp + return dp[0] +``` + +```java +public class Solution { + public int minDistance(String word1, String word2) { + int m = word1.length(), n = word2.length(); + if (m < n) { + String temp = word1; word1 = word2; word2 = temp; + m = word1.length(); n = word2.length(); + } + + int[] dp = new int[n + 1]; + for (int i = 0; i <= n; i++) dp[i] = n - i; + + for (int i = m - 1; i >= 0; i--) { + int nextDp = dp[n]; + dp[n] = m - i; + for (int j = n - 1; j >= 0; j--) { + int temp = dp[j]; + if (word1.charAt(i) == word2.charAt(j)) { + dp[j] = nextDp; + } else { + dp[j] = 1 + Math.min(dp[j], + Math.min(dp[j + 1], nextDp)); + } + nextDp = temp; + } + } + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int minDistance(string word1, string word2) { + int m = word1.size(), n = word2.size(); + if (m < n) { + swap(m, n); + swap(word1, word2); + } + + vector dp(n + 1); + for (int i = 0; i <= n; i++) dp[i] = n - i; + + for (int i = m - 1; i >= 0; i--) { + int nextDp = dp[n]; + dp[n] = m - i; + for (int j = n - 1; j >= 0; j--) { + int temp = dp[j]; + if (word1[i] == word2[j]) { + dp[j] = nextDp; + } else { + dp[j] = 1 + min({dp[j], dp[j + 1], nextDp}); + } + nextDp = temp; + } + } + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} word1 + * @param {string} word2 + * @return {number} + */ + minDistance(word1, word2) { + let m = word1.length, n = word2.length; + if (m < n) { + [m, n] = [n, m]; + [word1, word2] = [word2, word1]; + } + + let dp = new Array(n + 1).fill(0); + for (let j = 0; j <= n; j++) { + dp[j] = n - j; + } + + for (let i = m - 1; i >= 0; i--) { + let nextDp = dp[n]; + dp[n] = m - i; + for (let j = n - 1; j >= 0; j--) { + let temp = dp[j]; + if (word1[i] === word2[j]) { + dp[j] = nextDp; + } else { + dp[j] = 1 + Math.min(dp[j], dp[j + 1], nextDp); + } + nextDp = temp; + } + } + + return dp[0]; + } +} +``` + +```csharp +public class Solution { + public int MinDistance(string word1, string word2) { + int m = word1.Length, n = word2.Length; + if (m < n) { + string temp = word1; word1 = word2; word2 = temp; + m = word1.Length; n = word2.Length; + } + + int[] dp = new int[n + 1]; + for (int i = 0; i <= n; i++) dp[i] = n - i; + + for (int i = m - 1; i >= 0; i--) { + int nextDp = dp[n]; + dp[n] = m - i; + for (int j = n - 1; j >= 0; j--) { + int temp = dp[j]; + if (word1[i] == word2[j]) { + dp[j] = nextDp; + } else { + dp[j] = 1 + Math.Min(dp[j], + Math.Min(dp[j + 1], nextDp)); + } + nextDp = temp; + } + } + return dp[0]; + } +} +``` + +```go +func minDistance(word1, word2 string) int { + m, n := len(word1), len(word2) + if m < n { + word1, word2 = word2, word1 + m, n = n, m + } + + dp := make([]int, n+1) + for j := 0; j <= n; j++ { + dp[j] = n - j + } + + for i := m - 1; i >= 0; i-- { + nextDp := dp[n] + dp[n] = m - i + for j := n - 1; j >= 0; j-- { + temp := dp[j] + if word1[i] == word2[j] { + dp[j] = nextDp + } else { + dp[j] = 1 + min(dp[j], + min(dp[j+1], nextDp)) + } + nextDp = temp + } + } + + return dp[0] +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun minDistance(word1: String, word2: String): Int { + var m = word1.length + var n = word2.length + var word1Mod = word1 + var word2Mod = word2 + + if (m < n) { + word1Mod = word2 + word2Mod = word1 + m = word1Mod.length + n = word2Mod.length + } + + val dp = IntArray(n + 1) { n - it } + + for (i in m - 1 downTo 0) { + var nextDp = dp[n] + dp[n] = m - i + for (j in n - 1 downTo 0) { + val temp = dp[j] + if (word1Mod[i] == word2Mod[j]) { + dp[j] = nextDp + } else { + dp[j] = 1 + minOf(dp[j], dp[j + 1], nextDp) + } + nextDp = temp + } + } + + return dp[0] + } +} +``` + +```swift +class Solution { + func minDistance(_ word1: String, _ word2: String) -> Int { + var m = word1.count, n = word2.count + var word1Array = Array(word1), word2Array = Array(word2) + + if m < n { + swap(&m, &n) + swap(&word1Array, &word2Array) + } + + var dp = (0...n).map { n - $0 } + + for i in stride(from: m - 1, through: 0, by: -1) { + var nextDp = dp[n] + dp[n] = m - i + for j in stride(from: n - 1, through: 0, by: -1) { + let temp = dp[j] + if word1Array[i] == word2Array[j] { + dp[j] = nextDp + } else { + dp[j] = 1 + min(dp[j], dp[j + 1], nextDp) + } + nextDp = temp + } + } + return dp[0] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(min(m, n))$ + +> Where $m$ is the length of $word1$ and $n$ is the length of $word2$. \ No newline at end of file diff --git a/articles/eliminate-maximum-number-of-monsters.md b/articles/eliminate-maximum-number-of-monsters.md new file mode 100644 index 000000000..551c82473 --- /dev/null +++ b/articles/eliminate-maximum-number-of-monsters.md @@ -0,0 +1,298 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def eliminateMaximum(self, dist: List[int], speed: List[int]) -> int: + minReach = [math.ceil(d / s) for d, s in zip(dist, speed)] + minReach.sort() + + res = 0 + for minute in range(len(minReach)): + if minute >= minReach[minute]: + return res + res += 1 + + return res +``` + +```java +public class Solution { + public int eliminateMaximum(int[] dist, int[] speed) { + int n = dist.length; + int[] minReach = new int[n]; + + for (int i = 0; i < n; i++) { + minReach[i] = (int) Math.ceil((double) dist[i] / speed[i]); + } + + Arrays.sort(minReach); + + int res = 0; + for (int minute = 0; minute < n; minute++) { + if (minute >= minReach[minute]) { + return res; + } + res++; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int eliminateMaximum(vector& dist, vector& speed) { + int n = dist.size(); + vector minReach(n); + + for (int i = 0; i < n; i++) { + minReach[i] = ceil((double)dist[i] / speed[i]); + } + + sort(minReach.begin(), minReach.end()); + + int res = 0; + for (int minute = 0; minute < n; minute++) { + if (minute >= minReach[minute]) { + return res; + } + res++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} dist + * @param {number[]} speed + * @return {number} + */ + eliminateMaximum(dist, speed) { + let n = dist.length; + let minReach = new Array(n); + + for (let i = 0; i < n; i++) { + minReach[i] = Math.ceil(dist[i] / speed[i]); + } + + minReach.sort((a, b) => a - b); + + let res = 0; + for (let minute = 0; minute < n; minute++) { + if (minute >= minReach[minute]) { + return res; + } + res++; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Sorting (Overwrting Input Array) + +::tabs-start + +```python +class Solution: + def eliminateMaximum(self, dist: List[int], speed: List[int]) -> int: + for i in range(len(dist)): + dist[i] = math.ceil(dist[i] / speed[i]) + + dist.sort() + for minute in range(len(dist)): + if minute >= dist[minute]: + return minute + + return len(dist) +``` + +```java +public class Solution { + public int eliminateMaximum(int[] dist, int[] speed) { + int n = dist.length; + for (int i = 0; i < n; i++) { + dist[i] = (int) Math.ceil((double) dist[i] / speed[i]); + } + + Arrays.sort(dist); + for (int minute = 0; minute < n; minute++) { + if (minute >= dist[minute]) { + return minute; + } + } + + return n; + } +} +``` + +```cpp +class Solution { +public: + int eliminateMaximum(vector& dist, vector& speed) { + int n = dist.size(); + for (int i = 0; i < n; i++) { + dist[i] = ceil((double)dist[i] / speed[i]); + } + + sort(dist.begin(), dist.end()); + for (int minute = 0; minute < n; minute++) { + if (minute >= dist[minute]) { + return minute; + } + } + + return n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} dist + * @param {number[]} speed + * @return {number} + */ + eliminateMaximum(dist, speed) { + let n = dist.length; + for (let i = 0; i < n; i++) { + dist[i] = Math.ceil(dist[i] / speed[i]); + } + + dist.sort((a, b) => a - b); + for (let minute = 0; minute < n; minute++) { + if (minute >= dist[minute]) { + return minute; + } + } + + return n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Min-Heap + +::tabs-start + +```python +class Solution: + def eliminateMaximum(self, dist: List[int], speed: List[int]) -> int: + minHeap = [] + for i in range(len(dist)): + heapq.heappush(minHeap, dist[i] / speed[i]) + + res = 0 + while minHeap: + if res >= heapq.heappop(minHeap): + return res + res += 1 + + return res +``` + +```java +public class Solution { + public int eliminateMaximum(int[] dist, int[] speed) { + PriorityQueue minHeap = new PriorityQueue<>(); + for (int i = 0; i < dist.length; i++) { + minHeap.add((double) dist[i] / speed[i]); + } + + int res = 0; + while (!minHeap.isEmpty()) { + if (res >= minHeap.poll()) { + return res; + } + res++; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int eliminateMaximum(vector& dist, vector& speed) { + priority_queue, greater> minHeap; + for (int i = 0; i < dist.size(); i++) { + minHeap.push((double)dist[i] / speed[i]); + } + + int res = 0; + while (!minHeap.empty()) { + if (res >= minHeap.top()) { + return res; + } + minHeap.pop(); + res++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} dist + * @param {number[]} speed + * @return {number} + */ + eliminateMaximum(dist, speed) { + const minHeap = new MinPriorityQueue(); + for (let i = 0; i < dist.length; i++) { + minHeap.enqueue(dist[i] / speed[i]); + } + + let res = 0; + while (!minHeap.isEmpty()) { + if (res >= minHeap.dequeue().element) { + return res; + } + res++; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/encode-and-decode-tinyurl.md b/articles/encode-and-decode-tinyurl.md new file mode 100644 index 000000000..0da60014f --- /dev/null +++ b/articles/encode-and-decode-tinyurl.md @@ -0,0 +1,312 @@ +## 1. List + +::tabs-start + +```python +class Codec: + + def __init__(self): + self.urls = [] + + def encode(self, longUrl: str) -> str: + self.urls.append(longUrl) + return 'http://tinyurl.com/' + str(len(self.urls) - 1) + + def decode(self, shortUrl: str) -> str: + return self.urls[int(shortUrl.split('/')[-1])] +``` + +```java +public class Codec { + private List urls; + + public Codec() { + urls = new ArrayList<>(); + } + + public String encode(String longUrl) { + urls.add(longUrl); + return "http://tinyurl.com/" + (urls.size() - 1); + } + + public String decode(String shortUrl) { + int index = Integer.parseInt(shortUrl.substring(shortUrl.lastIndexOf('/') + 1)); + return urls.get(index); + } +} +``` + +```cpp +class Solution { +public: + vector urls; + + string encode(string longUrl) { + urls.push_back(longUrl); + return "http://tinyurl.com/" + to_string(urls.size() - 1); + } + + string decode(string shortUrl) { + int index = stoi(shortUrl.substr(shortUrl.find_last_of('/') + 1)); + return urls[index]; + } +}; +``` + +```javascript +class Codec { + constructor() { + this.urls = []; + } + + /** + * Encodes a URL to a shortened URL. + * + * @param {string} longUrl + * @return {string} + */ + encode(longUrl) { + this.urls.push(longUrl); + return 'http://tinyurl.com/' + (this.urls.length - 1); + } + + /** + * Decodes a shortened URL to its original URL. + * + * @param {string} shortUrl + * @return {string} + */ + decode(shortUrl) { + const index = parseInt(shortUrl.split('/').pop()); + return this.urls[index]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for $encode()$ and $decode()$. +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of $longUrls$, $m$ is the average length of the URLs. + +--- + +## 2. Hash Map - I + +::tabs-start + +```python +class Codec: + + def __init__(self): + self.url_map = {} + self.id = 0 + + def encode(self, longUrl: str) -> str: + self.url_map[self.id] = longUrl + short_url = f"http://tinyurl.com/{self.id}" + self.id += 1 + return short_url + + def decode(self, shortUrl: str) -> str: + url_id = int(shortUrl.split('/')[-1]) + return self.url_map[url_id] +``` + +```java +public class Codec { + private HashMap urlMap; + private int id; + + public Codec() { + urlMap = new HashMap<>(); + id = 0; + } + + public String encode(String longUrl) { + urlMap.put(id, longUrl); + return "http://tinyurl.com/" + id++; + } + + public String decode(String shortUrl) { + int urlId = Integer.parseInt(shortUrl.substring(shortUrl.lastIndexOf('/') + 1)); + return urlMap.get(urlId); + } +} +``` + +```cpp +class Solution { + unordered_map urlMap; + int id; + +public: + Solution() : id(0) {} + + string encode(string longUrl) { + urlMap[id] = longUrl; + return "http://tinyurl.com/" + to_string(id++); + } + + string decode(string shortUrl) { + int urlId = stoi(shortUrl.substr(shortUrl.find_last_of('/') + 1)); + return urlMap[urlId]; + } +}; +``` + +```javascript +class Codec { + constructor() { + this.urlMap = {}; + this.id = 0; + } + + /** + * Encodes a URL to a shortened URL. + * + * @param {string} longUrl + * @return {string} + */ + encode(longUrl) { + this.urlMap[this.id] = longUrl; + return `http://tinyurl.com/${this.id++}`; + } + + /** + * Decodes a shortened URL to its original URL. + * + * @param {string} shortUrl + * @return {string} + */ + decode(shortUrl) { + const urlId = parseInt(shortUrl.split('/').pop(), 10); + return this.urlMap[urlId]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for $encode()$ and $decode()$. +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of $longUrls$, $m$ is the average length of the URLs. + +--- + +## 3. Hash Map - II + +::tabs-start + +```python +class Codec: + + def __init__(self): + self.encodeMap = {} + self.decodeMap = {} + self.base = "http://tinyurl.com/" + + def encode(self, longUrl: str) -> str: + if longUrl not in self.encodeMap: + shortUrl = self.base + str(len(self.encodeMap) + 1) + self.encodeMap[longUrl] = shortUrl + self.decodeMap[shortUrl] = longUrl + return self.encodeMap[longUrl] + + def decode(self, shortUrl: str) -> str: + return self.decodeMap[shortUrl] +``` + +```java +public class Codec { + private HashMap encodeMap = new HashMap<>(); + private HashMap decodeMap = new HashMap<>(); + private String base = "http://tinyurl.com/"; + + public String encode(String longUrl) { + if (!encodeMap.containsKey(longUrl)) { + String shortUrl = base + (encodeMap.size() + 1); + encodeMap.put(longUrl, shortUrl); + decodeMap.put(shortUrl, longUrl); + } + return encodeMap.get(longUrl); + } + + public String decode(String shortUrl) { + return decodeMap.get(shortUrl); + } +} +``` + +```cpp +class Solution { +private: + unordered_map encodeMap; + unordered_map decodeMap; + string base = "http://tinyurl.com/"; + +public: + string encode(string longUrl) { + if (encodeMap.find(longUrl) == encodeMap.end()) { + string shortUrl = base + to_string(encodeMap.size() + 1); + encodeMap[longUrl] = shortUrl; + decodeMap[shortUrl] = longUrl; + } + return encodeMap[longUrl]; + } + + string decode(string shortUrl) { + return decodeMap[shortUrl]; + } +}; +``` + +```javascript +class Codec { + constructor() { + this.encodeMap = new Map(); + this.decodeMap = new Map(); + this.base = "http://tinyurl.com/"; + } + + /** + * Encodes a URL to a shortened URL. + * + * @param {string} longUrl + * @return {string} + */ + encode(longUrl) { + if (!this.encodeMap.has(longUrl)) { + const shortUrl = this.base + (this.encodeMap.size + 1); + this.encodeMap.set(longUrl, shortUrl); + this.decodeMap.set(shortUrl, longUrl); + } + return this.encodeMap.get(longUrl); + } + + /** + * Decodes a shortened URL to its original URL. + * + * @param {string} shortUrl + * @return {string} + */ + decode(shortUrl) { + return this.decodeMap.get(shortUrl); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for $encode()$ and $decode()$. +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of $longUrls$, $m$ is the average length of the URLs. \ No newline at end of file diff --git a/articles/evaluate-boolean-binary-tree.md b/articles/evaluate-boolean-binary-tree.md new file mode 100644 index 000000000..4b8fd67ca --- /dev/null +++ b/articles/evaluate-boolean-binary-tree.md @@ -0,0 +1,309 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def evaluateTree(self, root: Optional[TreeNode]) -> bool: + if not root.left: + return root.val == 1 + + if root.val == 2: + return self.evaluateTree(root.left) or self.evaluateTree(root.right) + + if root.val == 3: + return self.evaluateTree(root.left) and self.evaluateTree(root.right) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean evaluateTree(TreeNode root) { + if (root.left == null) { + return root.val == 1; + } + + if (root.val == 2) { + return evaluateTree(root.left) || evaluateTree(root.right); + } + + if (root.val == 3) { + return evaluateTree(root.left) && evaluateTree(root.right); + } + + return false; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool evaluateTree(TreeNode* root) { + if (!root->left) { + return root->val == 1; + } + + if (root->val == 2) { + return evaluateTree(root->left) || evaluateTree(root->right); + } + + if (root->val == 3) { + return evaluateTree(root->left) && evaluateTree(root->right); + } + + return false; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + evaluateTree(root) { + if (!root.left) { + return root.val === 1; + } + + if (root.val === 2) { + return this.evaluateTree(root.left) || this.evaluateTree(root.right); + } + + if (root.val === 3) { + return this.evaluateTree(root.left) && this.evaluateTree(root.right); + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def evaluateTree(self, root: Optional[TreeNode]) -> bool: + stack = [root] + value = {} # map (node -> value) + + while stack: + node = stack.pop() + + if not node.left: + value[node] = node.val == 1 + elif node.left in value: + if node.val == 2: + value[node] = value[node.left] or value[node.right] + if node.val == 3: + value[node] = value[node.left] and value[node.right] + else: + stack.extend([node, node.left, node.right]) + + return value[root] +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean evaluateTree(TreeNode root) { + Stack stack = new Stack<>(); + Map value = new HashMap<>(); + stack.push(root); + + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + + if (node.left == null) { + value.put(node, node.val == 1); + } else if (value.containsKey(node.left)) { + boolean leftValue = value.get(node.left); + boolean rightValue = value.get(node.right); + + if (node.val == 2) { + value.put(node, leftValue || rightValue); + } else if (node.val == 3) { + value.put(node, leftValue && rightValue); + } + } else { + stack.push(node); + stack.push(node.right); + stack.push(node.left); + } + } + + return value.get(root); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool evaluateTree(TreeNode* root) { + stack stk; + unordered_map value; + stk.push(root); + + while (!stk.empty()) { + TreeNode* node = stk.top(); + stk.pop(); + + if (!node->left) { + value[node] = node->val == 1; + } else if (value.count(node->left)) { + bool leftValue = value[node->left]; + bool rightValue = value[node->right]; + + if (node->val == 2) { + value[node] = leftValue || rightValue; + } else if (node->val == 3) { + value[node] = leftValue && rightValue; + } + } else { + stk.push(node); + stk.push(node->right); + stk.push(node->left); + } + } + + return value[root]; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + evaluateTree(root) { + const stack = [root]; + const value = new Map(); + + while (stack.length) { + const node = stack.pop(); + + if (!node.left) { + value.set(node, node.val === 1); + } else if (value.has(node.left)) { + const leftValue = value.get(node.left); + const rightValue = value.get(node.right); + + if (node.val === 2) { + value.set(node, leftValue || rightValue); + } else if (node.val === 3) { + value.set(node, leftValue && rightValue); + } + } else { + stack.push(node, node.right, node.left); + } + } + + return value.get(root); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/evaluate-division.md b/articles/evaluate-division.md new file mode 100644 index 000000000..9be40ad06 --- /dev/null +++ b/articles/evaluate-division.md @@ -0,0 +1,1037 @@ +## 1. Breadth First Search + +::tabs-start + +```python +class Solution: + def calcEquation(self, equations: List[List[str]], values: List[float], queries: List[List[str]]) -> List[float]: + adj = collections.defaultdict(list) # Map a -> list of [b, a/b] + + for i, eq in enumerate(equations): + a, b = eq + adj[a].append((b, values[i])) + adj[b].append((a, 1 / values[i])) + + def bfs(src, target): + if src not in adj or target not in adj: + return -1 + q, visit = deque([(src, 1)]), set() + visit.add(src) + + while q: + node, w = q.popleft() + if node == target: + return w + for nei, weight in adj[node]: + if nei not in visit: + q.append((nei, w * weight)) + visit.add(nei) + return -1 + + return [bfs(q[0], q[1]) for q in queries] +``` + +```java +public class Solution { + public double[] calcEquation(List> equations, double[] values, List> queries) { + Map> adj = new HashMap<>(); // Map a -> list of [b, a/b] + + for (int i = 0; i < equations.size(); i++) { + String a = equations.get(i).get(0); + String b = equations.get(i).get(1); + adj.putIfAbsent(a, new ArrayList<>()); + adj.putIfAbsent(b, new ArrayList<>()); + adj.get(a).add(new Pair(b, values[i])); + adj.get(b).add(new Pair(a, 1 / values[i])); + } + + double[] res = new double[queries.size()]; + for (int i = 0; i < queries.size(); i++) { + String src = queries.get(i).get(0); + String target = queries.get(i).get(1); + res[i] = bfs(src, target, adj); + } + + return res; + } + + private double bfs(String src, String target, Map> adj) { + if (!adj.containsKey(src) || !adj.containsKey(target)) { + return -1.0; + } + + Queue q = new LinkedList<>(); + Set visited = new HashSet<>(); + q.offer(new Pair(src, 1.0)); + visited.add(src); + + while (!q.isEmpty()) { + Pair current = q.poll(); + String node = current.node; + double weight = current.weight; + + if (node.equals(target)) { + return weight; + } + + for (Pair neighbor : adj.get(node)) { + if (!visited.contains(neighbor.node)) { + visited.add(neighbor.node); + q.offer(new Pair(neighbor.node, weight * neighbor.weight)); + } + } + } + + return -1.0; + } + + class Pair { + String node; + double weight; + + Pair(String node, double weight) { + this.node = node; + this.weight = weight; + } + } +} +``` + +```cpp +class Solution { +public: + vector calcEquation(vector>& equations, vector& values, vector>& queries) { + unordered_map>> adj; // Map a -> list of [b, a/b] + + for (int i = 0; i < equations.size(); i++) { + string a = equations[i][0]; + string b = equations[i][1]; + adj[a].emplace_back(b, values[i]); + adj[b].emplace_back(a, 1.0 / values[i]); + } + + vector res; + for (const auto& query : queries) { + string src = query[0]; + string target = query[1]; + res.push_back(bfs(src, target, adj)); + } + + return res; + } + +private: + double bfs(const string& src, const string& target, unordered_map>>& adj) { + if (!adj.count(src) || !adj.count(target)) { + return -1.0; + } + + queue> q; + unordered_set visited; + q.emplace(src, 1.0); + visited.insert(src); + + while (!q.empty()) { + auto [node, weight] = q.front(); + q.pop(); + + if (node == target) { + return weight; + } + + for (const auto& [nei, neiWeight] : adj[node]) { + if (!visited.count(nei)) { + visited.insert(nei); + q.emplace(nei, weight * neiWeight); + } + } + } + + return -1.0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[][]} equations + * @param {number[]} values + * @param {string[][]} queries + * @return {number[]} + */ + calcEquation(equations, values, queries) { + const adj = new Map(); // Map a -> list of [b, a/b] + + for (let i = 0; i < equations.length; i++) { + const [a, b] = equations[i]; + if (!adj.has(a)) adj.set(a, []); + if (!adj.has(b)) adj.set(b, []); + adj.get(a).push([b, values[i]]); + adj.get(b).push([a, 1 / values[i]]); + } + + const bfs = (src, target) => { + if (!adj.has(src) || !adj.has(target)) return -1; + + const queue = new Queue([[src, 1.0]]); + const visited = new Set(); + visited.add(src); + + while (!queue.isEmpty()) { + const [node, weight] = queue.pop(); + + if (node === target) return weight; + + for (const [nei, neiWeight] of adj.get(node)) { + if (!visited.has(nei)) { + visited.add(nei); + queue.push([nei, weight * neiWeight]); + } + } + } + + return -1; + }; + + return queries.map(([src, target]) => bfs(src, target)); + } +} +``` + +```csharp +public class Solution { + public double[] CalcEquation(List> equations, double[] values, List> queries) { + var adj = new Dictionary>(); + + for (int i = 0; i < equations.Count; i++) { + string a = equations[i][0]; + string b = equations[i][1]; + double val = values[i]; + + if (!adj.ContainsKey(a)) adj[a] = new List<(string, double)>(); + if (!adj.ContainsKey(b)) adj[b] = new List<(string, double)>(); + + adj[a].Add((b, val)); + adj[b].Add((a, 1.0 / val)); + } + + double Bfs(string src, string target) { + if (!adj.ContainsKey(src) || !adj.ContainsKey(target)) return -1.0; + var queue = new Queue<(string, double)>(); + var visited = new HashSet(); + + queue.Enqueue((src, 1.0)); + visited.Add(src); + + while (queue.Count > 0) { + var (node, weight) = queue.Dequeue(); + if (node == target) return weight; + + foreach (var (nei, w) in adj[node]) { + if (!visited.Contains(nei)) { + visited.Add(nei); + queue.Enqueue((nei, weight * w)); + } + } + } + + return -1.0; + } + + double[] res = new double[queries.Count]; + for (int i = 0; i < queries.Count; i++) { + res[i] = Bfs(queries[i][0], queries[i][1]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the number of unique strings and $m$ is the number of queries. + +--- + +## 2. Depth First Search + +::tabs-start + +```python +class Solution: + def calcEquation(self, equations: List[List[str]], values: List[float], queries: List[List[str]]) -> List[float]: + adj = collections.defaultdict(list) # Map a -> list of [b, a/b] + + for i, eq in enumerate(equations): + a, b = eq + adj[a].append((b, values[i])) + adj[b].append((a, 1 / values[i])) + + def dfs(src, target, visited): + if src not in adj or target not in adj: + return -1 + if src == target: + return 1 + + visited.add(src) + + for nei, weight in adj[src]: + if nei not in visited: + result = dfs(nei, target, visited) + if result != -1: + return weight * result + + return -1 + + return [dfs(q[0], q[1], set()) for q in queries] +``` + +```java +public class Solution { + public double[] calcEquation(List> equations, double[] values, List> queries) { + Map> adj = new HashMap<>(); // Map a -> list of [b, a/b] + + for (int i = 0; i < equations.size(); i++) { + String a = equations.get(i).get(0); + String b = equations.get(i).get(1); + adj.putIfAbsent(a, new ArrayList<>()); + adj.putIfAbsent(b, new ArrayList<>()); + adj.get(a).add(new Pair(b, values[i])); + adj.get(b).add(new Pair(a, 1 / values[i])); + } + + double[] res = new double[queries.size()]; + for (int i = 0; i < queries.size(); i++) { + String src = queries.get(i).get(0); + String target = queries.get(i).get(1); + res[i] = dfs(src, target, adj, new HashSet<>()); + } + + return res; + } + + private double dfs(String src, String target, Map> adj, Set visited) { + if (!adj.containsKey(src) || !adj.containsKey(target)) { + return -1.0; + } + if (src.equals(target)) { + return 1.0; + } + + visited.add(src); + + for (Pair neighbor : adj.get(src)) { + if (!visited.contains(neighbor.node)) { + double result = dfs(neighbor.node, target, adj, visited); + if (result != -1.0) { + return neighbor.weight * result; + } + } + } + + return -1.0; + } + + class Pair { + String node; + double weight; + + Pair(String node, double weight) { + this.node = node; + this.weight = weight; + } + } +} +``` + +```cpp +class Solution { +public: + vector calcEquation(vector>& equations, vector& values, vector>& queries) { + unordered_map>> adj; // Map a -> list of [b, a/b] + + for (int i = 0; i < equations.size(); i++) { + string a = equations[i][0]; + string b = equations[i][1]; + adj[a].emplace_back(b, values[i]); + adj[b].emplace_back(a, 1.0 / values[i]); + } + + vector res; + for (const auto& query : queries) { + string src = query[0]; + string target = query[1]; + res.push_back(dfs(src, target, adj, unordered_set())); + } + + return res; + } + +private: + double dfs(const string& src, const string& target, unordered_map>>& adj, unordered_set visited) { + if (!adj.count(src) || !adj.count(target)) { + return -1.0; + } + if (src == target) { + return 1.0; + } + + visited.insert(src); + + for (const auto& [nei, weight] : adj[src]) { + if (!visited.count(nei)) { + double result = dfs(nei, target, adj, visited); + if (result != -1.0) { + return weight * result; + } + } + } + + return -1.0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[][]} equations + * @param {number[]} values + * @param {string[][]} queries + * @return {number[]} + */ + calcEquation(equations, values, queries) { + const adj = new Map(); // Map a -> list of [b, a/b] + + for (let i = 0; i < equations.length; i++) { + const [a, b] = equations[i]; + if (!adj.has(a)) adj.set(a, []); + if (!adj.has(b)) adj.set(b, []); + adj.get(a).push([b, values[i]]); + adj.get(b).push([a, 1 / values[i]]); + } + + const dfs = (src, target, visited) => { + if (!adj.has(src) || !adj.has(target)) return -1; + if (src === target) return 1; + + visited.add(src); + + for (const [nei, weight] of adj.get(src)) { + if (!visited.has(nei)) { + const result = dfs(nei, target, visited); + if (result !== -1) { + return weight * result; + } + } + } + + return -1; + }; + + return queries.map(([src, target]) => dfs(src, target, new Set())); + } +} +``` + +```csharp +public class Solution { + public double[] CalcEquation(List> equations, double[] values, List> queries) { + var adj = new Dictionary>(); + + for (int i = 0; i < equations.Count; i++) { + string a = equations[i][0], b = equations[i][1]; + double val = values[i]; + + if (!adj.ContainsKey(a)) adj[a] = new List<(string, double)>(); + if (!adj.ContainsKey(b)) adj[b] = new List<(string, double)>(); + + adj[a].Add((b, val)); + adj[b].Add((a, 1.0 / val)); + } + + double Dfs(string src, string target, HashSet visited) { + if (!adj.ContainsKey(src) || !adj.ContainsKey(target)) return -1.0; + if (src == target) return 1.0; + + visited.Add(src); + + foreach (var (nei, weight) in adj[src]) { + if (!visited.Contains(nei)) { + double result = Dfs(nei, target, visited); + if (result != -1.0) { + return weight * result; + } + } + } + + return -1.0; + } + + double[] res = new double[queries.Count]; + for (int i = 0; i < queries.Count; i++) { + var visited = new HashSet(); + res[i] = Dfs(queries[i][0], queries[i][1], visited); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the number of unique strings and $m$ is the number of queries. + +--- + +## 3. Disjoint Set Union + +::tabs-start + +```python +class UnionFind: + def __init__(self): + self.parent = {} + self.weight = {} + + def add(self, x): + if x not in self.parent: + self.parent[x] = x + self.weight[x] = 1.0 + + def find(self, x): + if x != self.parent[x]: + orig_parent = self.parent[x] + self.parent[x] = self.find(self.parent[x]) + self.weight[x] *= self.weight[orig_parent] + return self.parent[x] + + def union(self, x, y, value): + self.add(x) + self.add(y) + root_x = self.find(x) + root_y = self.find(y) + + if root_x != root_y: + self.parent[root_x] = root_y + self.weight[root_x] = value * self.weight[y] / self.weight[x] + + def get_ratio(self, x, y): + if x not in self.parent or y not in self.parent or self.find(x) != self.find(y): + return -1.0 + return self.weight[x] / self.weight[y] + +class Solution: + def calcEquation(self, equations: List[List[str]], values: List[float], queries: List[List[str]]) -> List[float]: + uf = UnionFind() + + for (a, b), value in zip(equations, values): + uf.union(a, b, value) + + return [uf.get_ratio(a, b) for a, b in queries] +``` + +```java +class UnionFind { + private final Map parent; + private final Map weight; + + public UnionFind() { + parent = new HashMap<>(); + weight = new HashMap<>(); + } + + public void add(String x) { + if (!parent.containsKey(x)) { + parent.put(x, x); + weight.put(x, 1.0); + } + } + + public String find(String x) { + if (!x.equals(parent.get(x))) { + String origParent = parent.get(x); + parent.put(x, find(origParent)); + weight.put(x, weight.get(x) * weight.get(origParent)); + } + return parent.get(x); + } + + public void union(String x, String y, double value) { + add(x); + add(y); + String rootX = find(x); + String rootY = find(y); + + if (!rootX.equals(rootY)) { + parent.put(rootX, rootY); + weight.put(rootX, value * weight.get(y) / weight.get(x)); + } + } + + public double getRatio(String x, String y) { + if (!parent.containsKey(x) || !parent.containsKey(y) || !find(x).equals(find(y))) { + return -1.0; + } + return weight.get(x) / weight.get(y); + } +} + +public class Solution { + public double[] calcEquation(List> equations, double[] values, List> queries) { + UnionFind uf = new UnionFind(); + + for (int i = 0; i < equations.size(); i++) { + List equation = equations.get(i); + String a = equation.get(0); + String b = equation.get(1); + uf.union(a, b, values[i]); + } + + double[] result = new double[queries.size()]; + for (int i = 0; i < queries.size(); i++) { + List query = queries.get(i); + String a = query.get(0); + String b = query.get(1); + result[i] = uf.getRatio(a, b); + } + + return result; + } +} +``` + +```cpp +class UnionFind { + unordered_map parent; + unordered_map weight; + +public: + void add(const string& x) { + if (parent.find(x) == parent.end()) { + parent[x] = x; + weight[x] = 1.0; + } + } + + string find(const string& x) { + if (x != parent[x]) { + string origParent = parent[x]; + parent[x] = find(parent[x]); + weight[x] *= weight[origParent]; + } + return parent[x]; + } + + void unionSets(const string& x, const string& y, double value) { + add(x); + add(y); + string rootX = find(x); + string rootY = find(y); + + if (rootX != rootY) { + parent[rootX] = rootY; + weight[rootX] = value * weight[y] / weight[x]; + } + } + + double getRatio(const string& x, const string& y) { + if (parent.find(x) == parent.end() || parent.find(y) == parent.end() || find(x) != find(y)) { + return -1.0; + } + return weight[x] / weight[y]; + } +}; + +class Solution { +public: + vector calcEquation(vector>& equations, vector& values, vector>& queries) { + UnionFind uf; + + for (int i = 0; i < equations.size(); i++) { + string a = equations[i][0]; + string b = equations[i][1]; + uf.unionSets(a, b, values[i]); + } + + vector result; + for (const auto& query : queries) { + string a = query[0]; + string b = query[1]; + result.push_back(uf.getRatio(a, b)); + } + + return result; + } +}; +``` + +```javascript +class UnionFind { + constructor() { + this.parent = new Map(); + this.weight = new Map(); + } + + /** + * @param {string} x + * @return {void} + */ + add(x) { + if (!this.parent.has(x)) { + this.parent.set(x, x); + this.weight.set(x, 1.0); + } + } + + /** + * @param {string} x + * @return {string} + */ + find(x) { + if (x !== this.parent.get(x)) { + const origParent = this.parent.get(x); + this.parent.set(x, this.find(origParent)); + this.weight.set(x, this.weight.get(x) * this.weight.get(origParent)); + } + return this.parent.get(x); + } + + /** + * @param {string} x + * @param {string} y + * @param {number} value + * @return {number} + */ + union(x, y, value) { + this.add(x); + this.add(y); + const rootX = this.find(x); + const rootY = this.find(y); + + if (rootX !== rootY) { + this.parent.set(rootX, rootY); + this.weight.set(rootX, (value * this.weight.get(y)) / this.weight.get(x)); + } + } + + /** + * @param {string} x + * @param {string} y + * @return {number} + */ + getRatio(x, y) { + if (!this.parent.has(x) || !this.parent.has(y) || this.find(x) !== this.find(y)) { + return -1.0; + } + return this.weight.get(x) / this.weight.get(y); + } +} + +class Solution { + /** + * @param {string[][]} equations + * @param {number[]} values + * @param {string[][]} queries + * @return {number[]} + */ + calcEquation(equations, values, queries) { + const uf = new UnionFind(); + + for (let i = 0; i < equations.length; i++) { + const [a, b] = equations[i]; + uf.union(a, b, values[i]); + } + + return queries.map(([a, b]) => uf.getRatio(a, b)); + } +} +``` + +```csharp +public class UnionFind { + private Dictionary parent = new Dictionary(); + private Dictionary weight = new Dictionary(); + + public void Add(string x) { + if (!parent.ContainsKey(x)) { + parent[x] = x; + weight[x] = 1.0; + } + } + + public string Find(string x) { + if (parent[x] != x) { + string origParent = parent[x]; + parent[x] = Find(origParent); + weight[x] *= weight[origParent]; + } + return parent[x]; + } + + public void Union(string x, string y, double value) { + Add(x); + Add(y); + string rootX = Find(x); + string rootY = Find(y); + + if (rootX != rootY) { + parent[rootX] = rootY; + weight[rootX] = value * weight[y] / weight[x]; + } + } + + public double GetRatio(string x, string y) { + if (!parent.ContainsKey(x) || !parent.ContainsKey(y) || Find(x) != Find(y)) { + return -1.0; + } + return weight[x] / weight[y]; + } +} + +public class Solution { + public double[] CalcEquation(List> equations, double[] values, List> queries) { + var uf = new UnionFind(); + + for (int i = 0; i < equations.Count; i++) { + string a = equations[i][0]; + string b = equations[i][1]; + uf.Union(a, b, values[i]); + } + + double[] res = new double[queries.Count]; + for (int i = 0; i < queries.Count; i++) { + string a = queries[i][0]; + string b = queries[i][1]; + res[i] = uf.GetRatio(a, b); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((m + n)\log n)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the number of unique strings and $m$ is the number of queries. + +--- + +## 4. Floyd Warshall + +::tabs-start + +```python +class Solution: + def calcEquation(self, equations, values, queries): + graph = collections.defaultdict(dict) + + for (a, b), value in zip(equations, values): + graph[a][b] = value + graph[b][a] = 1.0 / value + + for k in graph: + for i in graph[k]: + for j in graph[k]: + if j not in graph[i]: + graph[i][j] = graph[i][k] * graph[k][j] + + result = [] + for a, b in queries: + if a in graph and b in graph[a]: + result.append(graph[a][b]) + else: + result.append(-1.0) + return result +``` + +```java +public class Solution { + public double[] calcEquation(List> equations, double[] values, List> queries) { + Map> graph = new HashMap<>(); + + for (int i = 0; i < equations.size(); i++) { + String a = equations.get(i).get(0); + String b = equations.get(i).get(1); + double value = values[i]; + graph.computeIfAbsent(a, k -> new HashMap<>()).put(b, value); + graph.computeIfAbsent(b, k -> new HashMap<>()).put(a, 1.0 / value); + } + + for (String k : graph.keySet()) { + for (String i : graph.get(k).keySet()) { + for (String j : graph.get(k).keySet()) { + graph.computeIfAbsent(i, x -> new HashMap<>()).putIfAbsent(j, graph.get(i).get(k) * graph.get(k).get(j)); + } + } + } + + double[] result = new double[queries.size()]; + for (int i = 0; i < queries.size(); i++) { + String a = queries.get(i).get(0); + String b = queries.get(i).get(1); + if (graph.containsKey(a) && graph.get(a).containsKey(b)) { + result[i] = graph.get(a).get(b); + } else { + result[i] = -1.0; + } + } + + return result; + } +} +``` + +```cpp +class Solution { +public: + vector calcEquation(vector>& equations, vector& values, vector>& queries) { + unordered_map> graph; + + for (int i = 0; i < equations.size(); i++) { + string a = equations[i][0]; + string b = equations[i][1]; + double value = values[i]; + graph[a][b] = value; + graph[b][a] = 1.0 / value; + } + + for (const auto& pair : graph) { + const string& k = pair.first; + for (const auto& pair1 : graph[k]) { + const string& i = pair1.first; + for (const auto& pair2 : graph[k]) { + const string& j = pair2.first; + if (!graph[i].count(j)) { + graph[i][j] = graph[i][k] * graph[k][j]; + } + } + } + } + + vector result; + for (const auto& query : queries) { + const string& a = query[0]; + const string& b = query[1]; + if (!graph.count(a) || !graph[a].count(b)) { + result.push_back(-1.0); + } else { + result.push_back(graph[a][b]); + } + } + + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[][]} equations + * @param {number[]} values + * @param {string[][]} queries + * @return {number[]} + */ + calcEquation(equations, values, queries) { + const graph = new Map(); + + for (let i = 0; i < equations.length; i++) { + const [a, b] = equations[i]; + const value = values[i]; + if (!graph.has(a)) graph.set(a, new Map()); + if (!graph.has(b)) graph.set(b, new Map()); + graph.get(a).set(b, value); + graph.get(b).set(a, 1 / value); + } + + for (const k of graph.keys()) { + for (const i of graph.get(k).keys()) { + for (const j of graph.get(k).keys()) { + if (!graph.get(i).has(j)) { + graph.get(i).set(j, graph.get(i).get(k) * graph.get(k).get(j)); + } + } + } + } + + return queries.map(([a, b]) => { + if (graph.has(a) && graph.get(a).has(b)) { + return graph.get(a).get(b); + } else { + return -1.0; + } + }); + } +} +``` + +```csharp +public class Solution { + public double[] CalcEquation(List> equations, double[] values, List> queries) { + var graph = new Dictionary>(); + + for (int i = 0; i < equations.Count; i++) { + string a = equations[i][0]; + string b = equations[i][1]; + double val = values[i]; + + if (!graph.ContainsKey(a)) graph[a] = new Dictionary(); + if (!graph.ContainsKey(b)) graph[b] = new Dictionary(); + + graph[a][b] = val; + graph[b][a] = 1.0 / val; + } + + foreach (var k in graph.Keys.ToList()) { + foreach (var i in graph[k].Keys.ToList()) { + foreach (var j in graph[k].Keys.ToList()) { + if (!graph[i].ContainsKey(j)) { + graph[i][j] = graph[i][k] * graph[k][j]; + } + } + } + } + + double[] result = new double[queries.Count]; + for (int i = 0; i < queries.Count; i++) { + string a = queries[i][0]; + string b = queries[i][1]; + if (graph.ContainsKey(a) && graph[a].ContainsKey(b)) { + result[i] = graph[a][b]; + } else { + result[i] = -1.0; + } + } + + return result; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n ^ 3)$ +* Space complexity: $O(n ^ 2 + m)$ + +> Where $n$ is the number of unique strings and $m$ is the number of queries. \ No newline at end of file diff --git a/articles/evaluate-reverse-polish-notation.md b/articles/evaluate-reverse-polish-notation.md new file mode 100644 index 000000000..3ad96eff8 --- /dev/null +++ b/articles/evaluate-reverse-polish-notation.md @@ -0,0 +1,1197 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def evalRPN(self, tokens: List[str]) -> int: + while len(tokens) > 1: + for i in range(len(tokens)): + if tokens[i] in "+-*/": + a = int(tokens[i-2]) + b = int(tokens[i-1]) + if tokens[i] == '+': + result = a + b + elif tokens[i] == '-': + result = a - b + elif tokens[i] == '*': + result = a * b + elif tokens[i] == '/': + result = int(a / b) + tokens = tokens[:i-2] + [str(result)] + tokens[i+1:] + break + return int(tokens[0]) +``` + +```java +public class Solution { + public int evalRPN(String[] tokens) { + List tokenList = new ArrayList<>(Arrays.asList(tokens)); + + while (tokenList.size() > 1) { + for (int i = 0; i < tokenList.size(); i++) { + String token = tokenList.get(i); + + if ("+-*/".contains(token)) { + int a = Integer.parseInt(tokenList.get(i - 2)); + int b = Integer.parseInt(tokenList.get(i - 1)); + int result = 0; + + if (token.equals("+")) { + result = a + b; + } else if (token.equals("-")) { + result = a - b; + } else if (token.equals("*")) { + result = a * b; + } else if (token.equals("/")) { + result = a / b; + } + + tokenList.set(i - 2, String.valueOf(result)); + tokenList.remove(i); + tokenList.remove(i - 1); + break; + } + } + } + return Integer.parseInt(tokenList.get(0)); + } +} +``` + +```cpp +class Solution { +public: + int evalRPN(vector& tokens) { + while (tokens.size() > 1) { + for (int i = 0; i < tokens.size(); i++) { + if (tokens[i] == "+" + || tokens[i] == "-" + || tokens[i] == "*" + || tokens[i] == "/") + { + int a = stoi(tokens[i - 2]); + int b = stoi(tokens[i - 1]); + int result = 0; + if (tokens[i] == "+") result = a + b; + else if (tokens[i] == "-") result = a - b; + else if (tokens[i] == "*") result = a * b; + else if (tokens[i] == "/") result = a / b; + + tokens.erase(tokens.begin() + i - 2, tokens.begin() + i + 1); + tokens.insert(tokens.begin() + i - 2, to_string(result)); + break; + } + } + } + return stoi(tokens[0]); + } +}; +``` + +```javascript +class Solution { + evalRPN(tokens) { + while (tokens.length > 1) { + for (let i = 0; i < tokens.length; i++) { + if ("+-*/".includes(tokens[i])) { + const a = parseInt(tokens[i - 2]); + const b = parseInt(tokens[i - 1]); + let result; + if (tokens[i] === "+") result = a + b; + else if (tokens[i] === "-") result = a - b; + else if (tokens[i] === "*") result = a * b; + else if (tokens[i] === "/") result = Math.trunc(a / b); + + tokens.splice(i - 2, 3, result.toString()); + break; + } + } + } + return parseInt(tokens[0]); + } +} +``` + +```csharp +public class Solution { + public int EvalRPN(string[] tokens) { + List tokenList = new List(tokens); + + while (tokenList.Count > 1) { + for (int i = 0; i < tokenList.Count; i++) { + if ("+-*/".Contains(tokenList[i])) { + int a = int.Parse(tokenList[i - 2]); + int b = int.Parse(tokenList[i - 1]); + int result = 0; + switch (tokenList[i]) { + case "+": + result = a + b; + break; + case "-": + result = a - b; + break; + case "*": + result = a * b; + break; + case "/": + result = a / b; + break; + } + tokenList.RemoveRange(i - 2, 3); + tokenList.Insert(i - 2, result.ToString()); + break; + } + } + } + return int.Parse(tokenList[0]); + } +} +``` + +```go +func evalRPN(tokens []string) int { + for len(tokens) > 1 { + for i := 0; i < len(tokens); i++ { + if tokens[i] == "+" || tokens[i] == "-" || tokens[i] == "*" || tokens[i] == "/" { + a, _ := strconv.Atoi(tokens[i-2]) + b, _ := strconv.Atoi(tokens[i-1]) + var result int + + switch tokens[i] { + case "+": + result = a + b + case "-": + result = a - b + case "*": + result = a * b + case "/": + result = a / b + } + + temp := make([]string, 0) + temp = append(temp, tokens[:i-2]...) + temp = append(temp, strconv.Itoa(result)) + temp = append(temp, tokens[i+1:]...) + tokens = temp + break + } + } + } + + result, _ := strconv.Atoi(tokens[0]) + return result +} +``` + +```kotlin +class Solution { + fun evalRPN(tokens: Array): Int { + val A = tokens.toMutableList() + + while (A.size > 1) { + for (i in A.indices) { + if (A[i] in setOf("+", "-", "*", "/")) { + val a = A[i-2].toInt() + val b = A[i-1].toInt() + val result = when (A[i]) { + "+" -> a + b + "-" -> a - b + "*" -> a * b + else -> a / b + } + + val temp = A.slice(0..i-3) + + result.toString() + + A.slice(i+1..A.lastIndex) + A.clear() + A.addAll(temp) + break + } + } + } + + return A[0].toInt() + } +} +``` + +```swift +class Solution { + func evalRPN(_ tokens: [String]) -> Int { + var tokens = tokens + + while tokens.count > 1 { + for i in 0.. int: + head = DoublyLinkedList(tokens[0]) + curr = head + + for i in range(1, len(tokens)): + curr.next = DoublyLinkedList(tokens[i], prev=curr) + curr = curr.next + + while head is not None: + if head.val in "+-*/": + l = int(head.prev.prev.val) + r = int(head.prev.val) + if head.val == '+': + res = l + r + elif head.val == '-': + res = l - r + elif head.val == '*': + res = l * r + else: + res = int(l / r) + + head.val = str(res) + head.prev = head.prev.prev.prev + if head.prev is not None: + head.prev.next = head + + ans = int(head.val) + head = head.next + + return ans +``` + +```java +public class DoublyLinkedList { + String val; + DoublyLinkedList next; + DoublyLinkedList prev; + + DoublyLinkedList(String val, DoublyLinkedList next, DoublyLinkedList prev) { + this.val = val; + this.next = next; + this.prev = prev; + } +} + +public class Solution { + public int evalRPN(String[] tokens) { + DoublyLinkedList head = new DoublyLinkedList(tokens[0], null, null); + DoublyLinkedList curr = head; + + for (int i = 1; i < tokens.length; i++) { + curr.next = new DoublyLinkedList(tokens[i], null, curr); + curr = curr.next; + } + + int ans = 0; + while (head != null) { + if ("+-*/".contains(head.val)) { + int l = Integer.parseInt(head.prev.prev.val); + int r = Integer.parseInt(head.prev.val); + int res = 0; + if (head.val.equals("+")) { + res = l + r; + } else if (head.val.equals("-")) { + res = l - r; + } else if (head.val.equals("*")) { + res = l * r; + } else { + res = l / r; + } + + head.val = String.valueOf(res); + head.prev = head.prev.prev.prev; + if (head.prev != null) { + head.prev.next = head; + } + } + + ans = Integer.parseInt(head.val); + head = head.next; + } + + return ans; + } +} +``` + +```cpp +class DoublyLinkedList { +public: + string val; + DoublyLinkedList* next; + DoublyLinkedList* prev; + + DoublyLinkedList(string val, DoublyLinkedList* next = nullptr, + DoublyLinkedList* prev = nullptr) { + this->val = val; + this->next = next; + this->prev = prev; + } +}; + +class Solution { +public: + int evalRPN(vector& tokens) { + DoublyLinkedList* head = new DoublyLinkedList(tokens[0]); + DoublyLinkedList* curr = head; + + for (int i = 1; i < tokens.size(); i++) { + curr->next = new DoublyLinkedList(tokens[i], nullptr, curr); + curr = curr->next; + } + + int ans = 0; + while (head != nullptr) { + if (head->val == "+" || + head->val == "-" || + head->val == "*" || + head->val == "/") + { + int l = stoi(head->prev->prev->val); + int r = stoi(head->prev->val); + int res = 0; + if (head->val == "+") { + res = l + r; + } else if (head->val == "-") { + res = l - r; + } else if (head->val == "*") { + res = l * r; + } else { + res = l / r; + } + + head->val = to_string(res); + head->prev = head->prev->prev->prev; + if (head->prev != nullptr) { + head->prev->next = head; + } + } + + ans = stoi(head->val); + head = head->next; + } + + return ans; + } +}; +``` + +```javascript +class DoublyLinkedList { + constructor(val, next = null, prev = null) { + this.val = val; + this.next = next; + this.prev = prev; + } +} + +class Solution { + /** + * @param {string[]} tokens + * @return {number} + */ + evalRPN(tokens) { + let head = new DoublyLinkedList(tokens[0]); + let curr = head; + + for (let i = 1; i < tokens.length; i++) { + curr.next = new DoublyLinkedList(tokens[i], null, curr); + curr = curr.next; + } + + let ans = 0; + while (head !== null) { + if ("+-*/".includes(head.val)) { + let l = parseInt(head.prev.prev.val); + let r = parseInt(head.prev.val); + let res = 0; + if (head.val === "+") { + res = l + r; + } else if (head.val === "-") { + res = l - r; + } else if (head.val === "*") { + res = l * r; + } else { + res = Math.trunc(l / r); + } + + head.val = res.toString(); + head.prev = head.prev.prev.prev; + if (head.prev !== null) { + head.prev.next = head; + } + } + + ans = parseInt(head.val); + head = head.next; + } + + return ans; + } +} +``` + +```csharp +public class DoublyLinkedList { + public string val; + public DoublyLinkedList next; + public DoublyLinkedList prev; + + public DoublyLinkedList(string val, DoublyLinkedList next = null, + DoublyLinkedList prev = null) { + this.val = val; + this.next = next; + this.prev = prev; + } +} + +public class Solution { + public int EvalRPN(string[] tokens) { + DoublyLinkedList head = new DoublyLinkedList(tokens[0]); + DoublyLinkedList curr = head; + + for (int i = 1; i < tokens.Length; i++) { + curr.next = new DoublyLinkedList(tokens[i], null, curr); + curr = curr.next; + } + + int ans = 0; + while (head != null) { + if ("+-*/".Contains(head.val)) { + int l = int.Parse(head.prev.prev.val); + int r = int.Parse(head.prev.val); + int res = 0; + if (head.val == "+") { + res = l + r; + } else if (head.val == "-") { + res = l - r; + } else if (head.val == "*") { + res = l * r; + } else { + res = l / r; + } + + head.val = res.ToString(); + head.prev = head.prev.prev.prev; + if (head.prev != null) { + head.prev.next = head; + } + } + + ans = int.Parse(head.val); + head = head.next; + } + + return ans; + } +} +``` + +```go +type DoublyLinkedList struct { + val string + next *DoublyLinkedList + prev *DoublyLinkedList +} + +func evalRPN(tokens []string) int { + head := &DoublyLinkedList{val: tokens[0]} + curr := head + + for i := 1; i < len(tokens); i++ { + newNode := &DoublyLinkedList{val: tokens[i], prev: curr} + curr.next = newNode + curr = curr.next + } + + for head != nil { + if head.val == "+" || head.val == "-" || head.val == "*" || head.val == "/" { + l, _ := strconv.Atoi(head.prev.prev.val) + r, _ := strconv.Atoi(head.prev.val) + + var res int + switch head.val { + case "+": + res = l + r + case "-": + res = l - r + case "*": + res = l * r + case "/": + res = l / r + } + + head.val = strconv.Itoa(res) + head.prev = head.prev.prev.prev + if head.prev != nil { + head.prev.next = head + } + } + + head = head.next + } + + ans, _ := strconv.Atoi(curr.val) + return ans +} +``` + +```kotlin +class DoublyLinkedList( + var value: String, + var next: DoublyLinkedList? = null, + var prev: DoublyLinkedList? = null +) + +class Solution { + fun evalRPN(tokens: Array): Int { + val head = DoublyLinkedList(tokens[0]) + var curr = head + + for (i in 1 until tokens.size) { + val newNode = DoublyLinkedList(tokens[i], prev = curr) + curr.next = newNode + curr = newNode + } + + var ptr: DoublyLinkedList? = head + while (ptr != null) { + if (ptr.value in setOf("+", "-", "*", "/")) { + val l = ptr.prev!!.prev!!.value.toInt() + val r = ptr.prev!!.value.toInt() + + val res = when (ptr.value) { + "+" -> l + r + "-" -> l - r + "*" -> l * r + else -> l / r + } + + ptr.value = res.toString() + ptr.prev = ptr.prev!!.prev!!.prev + ptr.prev?.next = ptr + } + + ptr = ptr.next + } + + return curr.value.toInt() + } +} +``` + +```swift +class DoublyLinkedList { + var val: String + var next: DoublyLinkedList? + var prev: DoublyLinkedList? + + init(val: String, next: DoublyLinkedList? = nil, prev: DoublyLinkedList? = nil) { + self.val = val + self.next = next + self.prev = prev + } +} + +class Solution { + func evalRPN(_ tokens: [String]) -> Int { + var head: DoublyLinkedList? = DoublyLinkedList(val: tokens[0]) + var curr = head + + for i in 1.. int: + def dfs(): + token = tokens.pop() + if token not in "+-*/": + return int(token) + + right = dfs() + left = dfs() + + if token == '+': + return left + right + elif token == '-': + return left - right + elif token == '*': + return left * right + elif token == '/': + return int(left / right) + + return dfs() +``` + +```java +public class Solution { + public int evalRPN(String[] tokens) { + List tokenList = new ArrayList<>(Arrays.asList(tokens)); + return dfs(tokenList); + } + + private int dfs(List tokens) { + String token = tokens.remove(tokens.size() - 1); + + if (!"+-*/".contains(token)) { + return Integer.parseInt(token); + } + + int right = dfs(tokens); + int left = dfs(tokens); + + switch (token) { + case "+": + return left + right; + case "-": + return left - right; + case "*": + return left * right; + case "/": + return left / right; + } + + return 0; + } +} +``` + +```cpp +class Solution { +public: + int dfs(vector& tokens) { + string token = tokens.back(); + tokens.pop_back(); + + if (token != "+" && token != "-" && + token != "*" && token != "/") + { + return stoi(token); + } + + int right = dfs(tokens); + int left = dfs(tokens); + + if (token == "+") { + return left + right; + } else if (token == "-") { + return left - right; + } else if (token == "*") { + return left * right; + } else { + return left / right; + } + } + + int evalRPN(vector& tokens) { + return dfs(tokens); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} tokens + * @return {number} + */ + evalRPN(tokens) { + + /** + * @return {number} + */ + const dfs = () => { + const token = tokens.pop(); + if (!"+-*/".includes(token)) { + return parseInt(token); + } + + const right = dfs(); + const left = dfs(); + + if (token === '+') { + return left + right; + } else if (token === '-') { + return left - right; + } else if (token === '*') { + return left * right; + } else { + return Math.trunc(left / right); + } + }; + + return dfs(); + } +} +``` + +```csharp +public class Solution { + public int EvalRPN(string[] tokens) { + List tokenList = new List(tokens); + return DFS(tokenList); + } + + public int DFS(List tokens) { + string token = tokens[tokens.Count - 1]; + tokens.RemoveAt(tokens.Count - 1); + + if (token != "+" && token != "-" && + token != "*" && token != "/") { + return int.Parse(token); + } + + int right = DFS(tokens); + int left = DFS(tokens); + + if (token == "+") { + return left + right; + } else if (token == "-") { + return left - right; + } else if (token == "*") { + return left * right; + } else { + return left / right; + } + } +} +``` + +```go +func evalRPN(tokens []string) int { + index := len(tokens) - 1 + + var dfs func() int + dfs = func() int { + token := tokens[index] + index-- + + if token != "+" && token != "-" && token != "*" && token != "/" { + val, _ := strconv.Atoi(token) + return val + } + + right := dfs() + left := dfs() + + switch token { + case "+": + return left + right + case "-": + return left - right + case "*": + return left * right + default: + return left / right + } + } + + return dfs() +} +``` + +```kotlin +class Solution { + private var index = 0 + + fun evalRPN(tokens: Array): Int { + index = tokens.size - 1 + + fun dfs(): Int { + val token = tokens[index] + index-- + + if (token !in setOf("+", "-", "*", "/")) { + return token.toInt() + } + + val right = dfs() + val left = dfs() + + return when (token) { + "+" -> left + right + "-" -> left - right + "*" -> left * right + else -> left / right + } + } + + return dfs() + } +} +``` + +```swift +class Solution { + func evalRPN(_ tokens: [String]) -> Int { + var tokens = tokens + + func dfs() -> Int { + let token = tokens.removeLast() + if let num = Int(token) { + return num + } + + let right = dfs() + let left = dfs() + + switch token { + case "+": return left + right + case "-": return left - right + case "*": return left * right + case "/": return left / right + default: return 0 + } + } + + return dfs() + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Stack + +::tabs-start + +```python +class Solution: + def evalRPN(self, tokens: List[str]) -> int: + stack = [] + for c in tokens: + if c == "+": + stack.append(stack.pop() + stack.pop()) + elif c == "-": + a, b = stack.pop(), stack.pop() + stack.append(b - a) + elif c == "*": + stack.append(stack.pop() * stack.pop()) + elif c == "/": + a, b = stack.pop(), stack.pop() + stack.append(int(float(b) / a)) + else: + stack.append(int(c)) + return stack[0] +``` + +```java +class Solution { + public int evalRPN(String[] tokens) { + Stack stack = new Stack<>(); + for (String c : tokens) { + if (c.equals("+")) { + stack.push(stack.pop() + stack.pop()); + } else if (c.equals("-")) { + int a = stack.pop(); + int b = stack.pop(); + stack.push(b - a); + } else if (c.equals("*")) { + stack.push(stack.pop() * stack.pop()); + } else if (c.equals("/")) { + int a = stack.pop(); + int b = stack.pop(); + stack.push(b / a); + } else { + stack.push(Integer.parseInt(c)); + } + } + return stack.pop(); + } +} +``` + +```cpp +class Solution { +public: + int evalRPN(vector& tokens) { + stack stack; + for (const string& c : tokens) { + if (c == "+") { + int a = stack.top(); stack.pop(); + int b = stack.top(); stack.pop(); + stack.push(b + a); + } else if (c == "-") { + int a = stack.top(); stack.pop(); + int b = stack.top(); stack.pop(); + stack.push(b - a); + } else if (c == "*") { + int a = stack.top(); stack.pop(); + int b = stack.top(); stack.pop(); + stack.push(b * a); + } else if (c == "/") { + int a = stack.top(); stack.pop(); + int b = stack.top(); stack.pop(); + stack.push(b / a); + } else { + stack.push(stoi(c)); + } + } + return stack.top(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} tokens + * @return {number} + */ + evalRPN(tokens) { + const stack = []; + for (const c of tokens) { + if (c === '+') { + stack.push(stack.pop() + stack.pop()); + } else if (c === '-') { + const a = stack.pop(); + const b = stack.pop(); + stack.push(b - a); + } else if (c === '*') { + stack.push(stack.pop() * stack.pop()); + } else if (c === '/') { + const a = stack.pop(); + const b = stack.pop(); + stack.push(Math.trunc(b / a)); + } else { + stack.push(parseInt(c)); + } + } + return stack.pop(); + } +} +``` + +```csharp +public class Solution { + public int EvalRPN(string[] tokens) { + Stack stack = new Stack(); + foreach (string c in tokens) { + if (c == "+") { + stack.Push(stack.Pop() + stack.Pop()); + } else if (c == "-") { + int a = stack.Pop(); + int b = stack.Pop(); + stack.Push(b - a); + } else if (c == "*") { + stack.Push(stack.Pop() * stack.Pop()); + } else if (c == "/") { + int a = stack.Pop(); + int b = stack.Pop(); + stack.Push((int) ((double) b / a)); + } else { + stack.Push(int.Parse(c)); + } + } + return stack.Pop(); + } +} +``` + +```go +func evalRPN(tokens []string) int { + stack := make([]int, 0) + + for _, token := range tokens { + switch token { + case "+": + a := stack[len(stack)-1] + b := stack[len(stack)-2] + stack = stack[:len(stack)-2] + stack = append(stack, b+a) + case "-": + a := stack[len(stack)-1] + b := stack[len(stack)-2] + stack = stack[:len(stack)-2] + stack = append(stack, b-a) + case "*": + a := stack[len(stack)-1] + b := stack[len(stack)-2] + stack = stack[:len(stack)-2] + stack = append(stack, b*a) + case "/": + a := stack[len(stack)-1] + b := stack[len(stack)-2] + stack = stack[:len(stack)-2] + stack = append(stack, b/a) + default: + num, _ := strconv.Atoi(token) + stack = append(stack, num) + } + } + + return stack[0] +} +``` + +```kotlin +class Solution { + fun evalRPN(tokens: Array): Int { + val stack = ArrayDeque() + + for (token in tokens) { + when (token) { + "+" -> { + val a = stack.removeLast() + val b = stack.removeLast() + stack.addLast(b + a) + } + "-" -> { + val a = stack.removeLast() + val b = stack.removeLast() + stack.addLast(b - a) + } + "*" -> { + val a = stack.removeLast() + val b = stack.removeLast() + stack.addLast(b * a) + } + "/" -> { + val a = stack.removeLast() + val b = stack.removeLast() + stack.addLast(b / a) + } + else -> stack.addLast(token.toInt()) + } + } + + return stack.last() + } +} +``` + +```swift +class Solution { + func evalRPN(_ tokens: [String]) -> Int { + var stack = [Int]() + + for c in tokens { + switch c { + case "+": + stack.append(stack.removeLast() + stack.removeLast()) + case "-": + let a = stack.removeLast() + let b = stack.removeLast() + stack.append(b - a) + case "*": + stack.append(stack.removeLast() * stack.removeLast()) + case "/": + let a = stack.removeLast() + let b = stack.removeLast() + stack.append(b / a) + default: + stack.append(Int(c)!) + } + } + return stack[0] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/even-odd-tree.md b/articles/even-odd-tree.md new file mode 100644 index 000000000..4c5b25687 --- /dev/null +++ b/articles/even-odd-tree.md @@ -0,0 +1,543 @@ +## 1. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def isEvenOddTree(self, root: Optional[TreeNode]) -> bool: + even = True + q = deque([root]) + + while q: + prev = float("-inf") if even else float("inf") + for _ in range(len(q)): + node = q.popleft() + + if even and (node.val % 2 == 0 or node.val <= prev): + return False + elif not even and (node.val % 2 == 1 or node.val >= prev): + return False + + if node.left: + q.append(node.left) + if node.right: + q.append(node.right) + + prev = node.val + + even = not even + + return True +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean isEvenOddTree(TreeNode root) { + boolean even = true; + Queue q = new LinkedList<>(); + q.offer(root); + + while (!q.isEmpty()) { + int prev = even ? Integer.MIN_VALUE : Integer.MAX_VALUE; + for (int i = q.size(); i > 0; i--) { + TreeNode node = q.poll(); + + if (even && (node.val % 2 == 0 || node.val <= prev)) return false; + if (!even && (node.val % 2 == 1 || node.val >= prev)) return false; + + if (node.left != null) q.offer(node.left); + if (node.right != null) q.offer(node.right); + + prev = node.val; + } + even = !even; + } + return true; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool isEvenOddTree(TreeNode* root) { + bool even = true; + queue q; + q.push(root); + + while (!q.empty()) { + int prev = even ? INT_MIN : INT_MAX; + for (int i = q.size(); i > 0; i--) { + TreeNode* node = q.front();q.pop(); + + if (even && (node->val % 2 == 0 || node->val <= prev)) return false; + if (!even && (node->val % 2 == 1 || node->val >= prev)) return false; + + if (node->left) q.push(node->left); + if (node->right) q.push(node->right); + + prev = node->val; + } + even = !even; + } + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isEvenOddTree(root) { + let even = true; + const q = new Queue([root]); + + while (!q.isEmpty()) { + let prev = even ? -Infinity : Infinity; + for (let i = q.size(); i > 0; i--) { + let node = q.pop(); + + if (even && (node.val % 2 === 0 || node.val <= prev)) return false; + if (!even && (node.val % 2 === 1 || node.val >= prev)) return false; + + if (node.left) q.push(node.left); + if (node.right) q.push(node.right); + + prev = node.val; + } + even = !even; + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def isEvenOddTree(self, root: Optional[TreeNode]) -> bool: + levels = [] + + def dfs(node, level): + if not node: + return True + + even = level % 2 == 0 + if ((even and node.val % 2 == 0) or + (not even and (node.val % 2 == 1)) + ): + return False + + if len(levels) == level: + levels.append(node.val) + else: + if ((even and node.val <= levels[level]) or + (not even and node.val >= levels[level]) + ): + return False + levels[level] = node.val + + return dfs(node.left, level + 1) and dfs(node.right, level + 1) + + return dfs(root, 0) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + List levels = new ArrayList<>(); + + private boolean dfs(TreeNode node, int level) { + if (node == null) return true; + + boolean even = level % 2 == 0; + if ((even && node.val % 2 == 0) || + (!even && node.val % 2 == 1)) { + return false; + } + + if (levels.size() == level) { + levels.add(node.val); + } else { + if ((even && node.val <= levels.get(level)) || + (!even && node.val >= levels.get(level))) { + return false; + } + levels.set(level, node.val); + } + + return dfs(node.left, level + 1) && dfs(node.right, level + 1); + } + + public boolean isEvenOddTree(TreeNode root) { + return dfs(root, 0); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector levels; + + bool dfs(TreeNode* node, int level) { + if (!node) return true; + + bool even = level % 2 == 0; + if ((even && node->val % 2 == 0) || + (!even && node->val % 2 == 1)) { + return false; + } + + if (levels.size() == level) { + levels.push_back(node->val); + } else { + if ((even && node->val <= levels[level]) || + (!even && node->val >= levels[level])) { + return false; + } + levels[level] = node->val; + } + + return dfs(node->left, level + 1) && dfs(node->right, level + 1); + } + + bool isEvenOddTree(TreeNode* root) { + return dfs(root, 0); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isEvenOddTree(root) { + const levels = []; + + const dfs = (node, level) => { + if (!node) return true; + + const even = level % 2 === 0; + if ((even && node.val % 2 === 0) || + (!even && node.val % 2 === 1)) { + return false; + } + + if (levels.length === level) { + levels.push(node.val); + } else { + if ((even && node.val <= levels[level]) || + (!even && node.val >= levels[level])) { + return false; + } + levels[level] = node.val; + } + + return dfs(node.left, level + 1) && dfs(node.right, level + 1); + }; + + return dfs(root, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def isEvenOddTree(self, root: Optional[TreeNode]) -> bool: + stack = [(root, 0)] + levels = [] + + while stack: + node, level = stack.pop() + + even = level % 2 == 0 + if ((even and node.val % 2 == 0) or + (not even and node.val % 2 == 1) + ): + return False + + if len(levels) == level: + levels.append(node.val) + else: + if ((even and node.val <= levels[level]) or + (not even and node.val >= levels[level]) + ): + return False + levels[level] = node.val + + if node.right: + stack.append((node.right, level + 1)) + if node.left: + stack.append((node.left, level + 1)) + + return True +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean isEvenOddTree(TreeNode root) { + Stack> stack = new Stack<>(); + stack.push(new Pair<>(root, 0)); + List levels = new ArrayList<>(); + + while (!stack.isEmpty()) { + Pair pair = stack.pop(); + TreeNode node = pair.getKey(); + int level = pair.getValue(); + + boolean even = level % 2 == 0; + if ((even && node.val % 2 == 0) || + (!even && node.val % 2 == 1)) + return false; + + if (levels.size() == level) { + levels.add(node.val); + } else { + if ((even && node.val <= levels.get(level)) || + (!even && node.val >= levels.get(level))) + return false; + levels.set(level, node.val); + } + + if (node.right != null) stack.push(new Pair<>(node.right, level + 1)); + if (node.left != null) stack.push(new Pair<>(node.left, level + 1)); + } + + return true; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool isEvenOddTree(TreeNode* root) { + stack> stack; + stack.push({root, 0}); + vector levels; + + while (!stack.empty()) { + auto [node, level] = stack.top(); + stack.pop(); + + bool even = level % 2 == 0; + if ((even && node->val % 2 == 0) || + (!even && node->val % 2 == 1)) + return false; + + if (levels.size() == level) { + levels.push_back(node->val); + } else { + if ((even && node->val <= levels[level]) || + (!even && node->val >= levels[level])) + return false; + levels[level] = node->val; + } + + if (node->right) stack.push({node->right, level + 1}); + if (node->left) stack.push({node->left, level + 1}); + } + + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isEvenOddTree(root) { + const stack = [[root, 0]]; + const levels = []; + + while (stack.length) { + const [node, level] = stack.pop(); + + const even = level % 2 === 0; + if ((even && node.val % 2 === 0) || + (!even && node.val % 2 === 1)) + return false; + + if (levels.length === level) { + levels.push(node.val); + } else { + if ((even && node.val <= levels[level]) || + (!even && node.val >= levels[level])) + return false; + levels[level] = node.val; + } + + if (node.right) stack.push([node.right, level + 1]); + if (node.left) stack.push([node.left, level + 1]); + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/excel-sheet-column-title.md b/articles/excel-sheet-column-title.md new file mode 100644 index 000000000..98ae4f249 --- /dev/null +++ b/articles/excel-sheet-column-title.md @@ -0,0 +1,142 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def convertToTitle(self, columnNumber: int) -> str: + if columnNumber == 0: + return "" + + n = columnNumber - 1 + return self.convertToTitle(n // 26) + chr(n % 26 + ord('A')) +``` + +```java +public class Solution { + public String convertToTitle(int columnNumber) { + if (columnNumber == 0) { + return ""; + } + int n = columnNumber - 1; + return convertToTitle(n / 26) + (char) ('A' + n % 26); + } +} +``` + +```cpp +class Solution { +public: + string convertToTitle(int columnNumber) { + if (columnNumber == 0) { + return ""; + } + int n = columnNumber - 1; + return convertToTitle(n / 26) + char('A' + n % 26); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} columnNumber + * @return {string} + */ + convertToTitle(columnNumber) { + if (columnNumber === 0) { + return ""; + } + const n = columnNumber - 1; + return this.convertToTitle(Math.floor(n / 26)) + String.fromCharCode('A'.charCodeAt(0) + n % 26); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ for recursion stack. + +> Where $n$ is the given column number. + +--- + +## 2. Iteration + +::tabs-start + +```python +class Solution: + def convertToTitle(self, columnNumber: int) -> str: + res = [] + while columnNumber > 0: + columnNumber -= 1 + offset = columnNumber % 26 + res += chr(ord('A') + offset) + columnNumber //= 26 + + return ''.join(reversed(res)) +``` + +```java +public class Solution { + public String convertToTitle(int columnNumber) { + StringBuilder res = new StringBuilder(); + while (columnNumber > 0) { + columnNumber--; + int offset = columnNumber % 26; + res.append((char) ('A' + offset)); + columnNumber /= 26; + } + return res.reverse().toString(); + } +} +``` + +```cpp +class Solution { +public: + string convertToTitle(int columnNumber) { + string res; + while (columnNumber > 0) { + columnNumber--; + int offset = columnNumber % 26; + res += ('A' + offset); + columnNumber /= 26; + } + reverse(res.begin(), res.end()); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} columnNumber + * @return {string} + */ + convertToTitle(columnNumber) { + let res = []; + while (columnNumber > 0) { + columnNumber--; + const offset = columnNumber % 26; + res.push(String.fromCharCode('A'.charCodeAt(0) + offset)); + columnNumber = Math.floor(columnNumber / 26); + } + return res.reverse().join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ extra space. + +> Where $n$ is the given column number. \ No newline at end of file diff --git a/articles/extra-characters-in-a-string.md b/articles/extra-characters-in-a-string.md new file mode 100644 index 000000000..6b2052693 --- /dev/null +++ b/articles/extra-characters-in-a-string.md @@ -0,0 +1,1300 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def minExtraChar(self, s: str, dictionary: List[str]) -> int: + words = set(dictionary) + + def dfs(i): + if i == len(s): + return 0 + + res = 1 + dfs(i + 1) + for j in range(i, len(s)): + if s[i:j + 1] in words: + res = min(res, dfs(j + 1)) + + return res + + return dfs(0) +``` + +```java +public class Solution { + public int minExtraChar(String s, String[] dictionary) { + Set words = new HashSet<>(); + for (String word : dictionary) { + words.add(word); + } + return dfs(0, s, words); + } + + private int dfs(int i, String s, Set words) { + if (i == s.length()) { + return 0; + } + + int res = 1 + dfs(i + 1, s, words); + for (int j = i; j < s.length(); j++) { + if (words.contains(s.substring(i, j + 1))) { + res = Math.min(res, dfs(j + 1, s, words)); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minExtraChar(string s, vector& dictionary) { + unordered_set words(dictionary.begin(), dictionary.end()); + return dfs(0, s, words); + } + +private: + int dfs(int i, const string& s, unordered_set& words) { + if (i == s.size()) { + return 0; + } + + int res = 1 + dfs(i + 1, s, words); + for (int j = i; j < s.size(); j++) { + if (words.count(s.substr(i, j - i + 1))) { + res = min(res, dfs(j + 1, s, words)); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string[]} dictionary + * @return {number} + */ + minExtraChar(s, dictionary) { + const words = new Set(dictionary); + + const dfs = (i) => { + if (i === s.length) { + return 0; + } + + let res = 1 + dfs(i + 1); + for (let j = i; j < s.length; j++) { + if (words.has(s.slice(i, j + 1))) { + res = Math.min(res, dfs(j + 1)); + } + } + + return res; + }; + + return dfs(0); + } +} +``` + +```csharp +public class Solution { + public int MinExtraChar(string s, string[] dictionary) { + HashSet words = new HashSet(dictionary); + return Dfs(0, s, words); + } + + private int Dfs(int i, string s, HashSet words) { + if (i == s.Length) { + return 0; + } + + int res = 1 + Dfs(i + 1, s, words); + for (int j = i; j < s.Length; j++) { + if (words.Contains(s.Substring(i, j - i + 1))) { + res = Math.Min(res, Dfs(j + 1, s, words)); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n + m * k)$ +* Space complexity: $O(n + m * k)$ + +> Where $n$ is the length of the string $s$, $m$ is the number of words in the dictionary, and $k$ is the average length of a word in the dictionary. + +--- + +## 2. Dynamic Programming (Top-Down) Using Hash Set + +::tabs-start + +```python +class Solution: + def minExtraChar(self, s: str, dictionary: List[str]) -> int: + words = set(dictionary) + dp = {len(s): 0} + + def dfs(i): + if i in dp: + return dp[i] + res = 1 + dfs(i + 1) + for j in range(i, len(s)): + if s[i:j + 1] in words: + res = min(res, dfs(j + 1)) + dp[i] = res + return res + + return dfs(0) +``` + +```java +public class Solution { + public int minExtraChar(String s, String[] dictionary) { + Set words = new HashSet<>(Arrays.asList(dictionary)); + int n = s.length(); + int[] dp = new int[n + 1]; + Arrays.fill(dp, -1); + dp[n] = 0; + + return dfs(0, s, words, dp); + } + + private int dfs(int i, String s, Set words, int[] dp) { + if (dp[i] != -1) return dp[i]; + int res = 1 + dfs(i + 1, s, words, dp); + for (int j = i; j < s.length(); j++) { + if (words.contains(s.substring(i, j + 1))) { + res = Math.min(res, dfs(j + 1, s, words, dp)); + } + } + dp[i] = res; + return res; + } +} +``` + +```cpp +class Solution { +public: + int minExtraChar(string s, vector& dictionary) { + unordered_set words(dictionary.begin(), dictionary.end()); + int n = s.size(); + vector dp(n + 1, -1); + dp[n] = 0; + return dfs(0, s, words, dp); + } + +private: + int dfs(int i, string& s, unordered_set& words, vector& dp) { + if (dp[i] != -1) return dp[i]; + int res = 1 + dfs(i + 1, s, words, dp); + for (int j = i; j < s.size(); j++) { + if (words.count(s.substr(i, j - i + 1))) { + res = min(res, dfs(j + 1, s, words, dp)); + } + } + dp[i] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string[]} dictionary + * @return {number} + */ + minExtraChar(s, dictionary) { + const words = new Set(dictionary); + const n = s.length; + const dp = new Array(n + 1).fill(-1); + dp[n] = 0; + + const dfs = (i) => { + if (dp[i] !== -1) return dp[i]; + let res = 1 + dfs(i + 1); + for (let j = i; j < n; j++) { + if (words.has(s.slice(i, j + 1))) { + res = Math.min(res, dfs(j + 1)); + } + } + dp[i] = res; + return res; + }; + + return dfs(0); + } +} +``` + +```csharp +public class Solution { + public int MinExtraChar(string s, string[] dictionary) { + HashSet words = new HashSet(dictionary); + int n = s.Length; + int[] dp = new int[n + 1]; + Array.Fill(dp, -1); + dp[n] = 0; + + return Dfs(0, s, words, dp); + } + + private int Dfs(int i, string s, HashSet words, int[] dp) { + if (dp[i] != -1) return dp[i]; + + int res = 1 + Dfs(i + 1, s, words, dp); + for (int j = i; j < s.Length; j++) { + if (words.Contains(s.Substring(i, j - i + 1))) { + res = Math.Min(res, Dfs(j + 1, s, words, dp)); + } + } + + dp[i] = res; + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3 + m * k)$ +* Space complexity: $O(n + m * k)$ + +> Where $n$ is the length of the string $s$, $m$ is the number of words in the dictionary, and $k$ is the average length of a word in the dictionary. + +--- + +## 3. Dynamic Programming (Bottom-Up) Using Hash Set + +::tabs-start + +```python +class Solution: + def minExtraChar(self, s: str, dictionary: List[str]) -> int: + words = set(dictionary) + n = len(s) + dp = [0] * (n + 1) + + for i in range(n - 1, -1, -1): + dp[i] = 1 + dp[i + 1] + for j in range(i, n): + if s[i:j + 1] in words: + dp[i] = min(dp[i], dp[j + 1]) + return dp[0] +``` + +```java +public class Solution { + public int minExtraChar(String s, String[] dictionary) { + Set words = new HashSet<>(Arrays.asList(dictionary)); + int n = s.length(); + int[] dp = new int[n + 1]; + + for (int i = n - 1; i >= 0; i--) { + dp[i] = 1 + dp[i + 1]; + for (int j = i; j < n; j++) { + if (words.contains(s.substring(i, j + 1))) { + dp[i] = Math.min(dp[i], dp[j + 1]); + } + } + } + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int minExtraChar(string s, vector& dictionary) { + unordered_set words(dictionary.begin(), dictionary.end()); + int n = s.size(); + vector dp(n + 1, 0); + + for (int i = n - 1; i >= 0; i--) { + dp[i] = 1 + dp[i + 1]; + for (int j = i; j < n; j++) { + if (words.count(s.substr(i, j - i + 1))) { + dp[i] = min(dp[i], dp[j + 1]); + } + } + } + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string[]} dictionary + * @return {number} + */ + minExtraChar(s, dictionary) { + const words = new Set(dictionary); + const n = s.length; + const dp = new Array(n + 1).fill(0); + + for (let i = n - 1; i >= 0; i--) { + dp[i] = 1 + dp[i + 1]; + for (let j = i; j < n; j++) { + if (words.has(s.slice(i, j + 1))) { + dp[i] = Math.min(dp[i], dp[j + 1]); + } + } + } + return dp[0]; + } +} +``` + +```csharp +public class Solution { + public int MinExtraChar(string s, string[] dictionary) { + HashSet words = new HashSet(dictionary); + int n = s.Length; + int[] dp = new int[n + 1]; + + for (int i = n - 1; i >= 0; i--) { + dp[i] = 1 + dp[i + 1]; + for (int j = i; j < n; j++) { + if (words.Contains(s.Substring(i, j - i + 1))) { + dp[i] = Math.Min(dp[i], dp[j + 1]); + } + } + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3 + m * k)$ +* Space complexity: $O(n + m * k)$ + +> Where $n$ is the length of the string $s$, $m$ is the number of words in the dictionary, and $k$ is the average length of a word in the dictionary. + +--- + +## 4. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minExtraChar(self, s: str, dictionary: List[str]) -> int: + dp = {len(s) : 0} + + def dfs(i): + if i in dp: + return dp[i] + + res = 1 + dfs(i + 1) + for word in dictionary: + if i + len(word) > len(s): + continue + + flag = True + for j in range(len(word)): + if s[i + j] != word[j]: + flag = False + break + if flag: + res = min(res, dfs(i + len(word))) + + dp[i] = res + return res + + return dfs(0) +``` + +```java +public class Solution { + public int minExtraChar(String s, String[] dictionary) { + Map dp = new HashMap<>(); + dp.put(s.length(), 0); + return dfs(0, s, dictionary, dp); + } + + private int dfs(int i, String s, String[] dictionary, Map dp) { + if (dp.containsKey(i)) { + return dp.get(i); + } + + int res = 1 + dfs(i + 1, s, dictionary, dp); + for (String word : dictionary) { + if (i + word.length() > s.length()) continue; + + boolean flag = true; + for (int j = 0; j < word.length(); j++) { + if (s.charAt(i + j) != word.charAt(j)) { + flag = false; + break; + } + } + if (flag) { + res = Math.min(res, dfs(i + word.length(), s, dictionary, dp)); + } + } + dp.put(i, res); + return res; + } +} +``` + +```cpp +class Solution { + unordered_map dp; + +public: + int minExtraChar(string s, vector& dictionary) { + dp[s.size()] = 0; + return dfs(0, s, dictionary); + } + +private: + int dfs(int i, string& s, vector& dictionary) { + if (dp.count(i)) { + return dp[i]; + } + + int res = 1 + dfs(i + 1, s, dictionary); + for (const string& word : dictionary) { + if (i + word.size() > s.size()) continue; + + bool flag = true; + for (int j = 0; j < word.size(); j++) { + if (s[i + j] != word[j]) { + flag = false; + break; + } + } + if (flag) { + res = min(res, dfs(i + word.size(), s, dictionary)); + } + } + return dp[i] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string[]} dictionary + * @return {number} + */ + minExtraChar(s, dictionary) { + const dp = new Map(); + dp.set(s.length, 0); + + const dfs = (i) => { + if (dp.has(i)) return dp.get(i); + + let res = 1 + dfs(i + 1); + for (const word of dictionary) { + if (i + word.length > s.length) continue; + + let flag = true; + for (let j = 0; j < word.length; j++) { + if (s[i + j] !== word[j]) { + flag = false; + break; + } + } + if (flag) { + res = Math.min(res, dfs(i + word.length)); + } + } + dp.set(i, res); + return res; + }; + + return dfs(0); + } +} +``` + +```csharp +public class Solution { + public int MinExtraChar(string s, string[] dictionary) { + Dictionary dp = new Dictionary(); + dp[s.Length] = 0; + return Dfs(0, s, dictionary, dp); + } + + private int Dfs(int i, string s, string[] dictionary, Dictionary dp) { + if (dp.ContainsKey(i)) { + return dp[i]; + } + + int res = 1 + Dfs(i + 1, s, dictionary, dp); + + foreach (string word in dictionary) { + if (i + word.Length > s.Length) continue; + + bool flag = true; + for (int j = 0; j < word.Length; j++) { + if (s[i + j] != word[j]) { + flag = false; + break; + } + } + + if (flag) { + res = Math.Min(res, Dfs(i + word.Length, s, dictionary, dp)); + } + } + + dp[i] = res; + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m * k)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the string $s$, $m$ is the number of words in the dictionary, and $k$ is the average length of a word in the dictionary. + +--- + +## 5. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minExtraChar(self, s: str, dictionary: List[str]) -> int: + n = len(s) + dp = [0] * (n + 1) + + for i in range(n - 1, -1, -1): + dp[i] = 1 + dp[i + 1] + for word in dictionary: + if i + len(word) <= n and s[i:i + len(word)] == word: + dp[i] = min(dp[i], dp[i + len(word)]) + + return dp[0] +``` + +```java +public class Solution { + public int minExtraChar(String s, String[] dictionary) { + int n = s.length(); + int[] dp = new int[n + 1]; + + for (int i = n - 1; i >= 0; i--) { + dp[i] = 1 + dp[i + 1]; + for (String word : dictionary) { + if (i + word.length() <= n && s.startsWith(word, i)) { + dp[i] = Math.min(dp[i], dp[i + word.length()]); + } + } + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int minExtraChar(string s, vector& dictionary) { + int n = s.size(); + vector dp(n + 1, 0); + + for (int i = n - 1; i >= 0; i--) { + dp[i] = 1 + dp[i + 1]; + for (const string& word : dictionary) { + if (i + word.size() <= n && s.substr(i, word.size()) == word) { + dp[i] = min(dp[i], dp[i + word.size()]); + } + } + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string[]} dictionary + * @return {number} + */ + minExtraChar(s, dictionary) { + const n = s.length; + const dp = new Array(n + 1).fill(0); + + for (let i = n - 1; i >= 0; i--) { + dp[i] = 1 + dp[i + 1]; + for (const word of dictionary) { + if (i + word.length <= n && s.slice(i, i + word.length) === word) { + dp[i] = Math.min(dp[i], dp[i + word.length]); + } + } + } + + return dp[0]; + } +} +``` + +```csharp +public class Solution { + public int MinExtraChar(string s, string[] dictionary) { + int n = s.Length; + int[] dp = new int[n + 1]; + + for (int i = n - 1; i >= 0; i--) { + dp[i] = 1 + dp[i + 1]; + foreach (string word in dictionary) { + if (i + word.Length <= n && s.Substring(i, word.Length) == word) { + dp[i] = Math.Min(dp[i], dp[i + word.Length]); + } + } + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m * k)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the string $s$, $m$ is the number of words in the dictionary, and $k$ is the average length of a word in the dictionary. + +--- + +## 6. Dynamic Programming (Top-Down) Using Trie + +::tabs-start + +```python +class TrieNode: + def __init__(self): + self.children = {} + self.isWord = False + +class Trie: + def __init__(self): + self.root = TrieNode() + + def addWord(self, word): + curr = self.root + for c in word: + if c not in curr.children: + curr.children[c] = TrieNode() + curr = curr.children[c] + curr.isWord = True + +class Solution: + def minExtraChar(self, s: str, dictionary: List[str]) -> int: + trie = Trie() + for w in dictionary: + trie.addWord(w) + + dp = {len(s): 0} + + def dfs(i): + if i in dp: + return dp[i] + res = 1 + dfs(i + 1) + curr = trie.root + for j in range(i, len(s)): + if s[j] not in curr.children: + break + curr = curr.children[s[j]] + if curr.isWord: + res = min(res, dfs(j + 1)) + + dp[i] = res + return res + + return dfs(0) +``` + +```java +class TrieNode { + TrieNode[] children; + boolean isWord; + + TrieNode() { + children = new TrieNode[26]; + isWord = false; + } +} + +class Trie { + TrieNode root; + + Trie() { + root = new TrieNode(); + } + + void addWord(String word) { + TrieNode curr = root; + for (char c : word.toCharArray()) { + if (curr.children[c - 'a'] == null) { + curr.children[c - 'a'] = new TrieNode(); + } + curr = curr.children[c - 'a']; + } + curr.isWord = true; + } +} + +public class Solution { + public int minExtraChar(String s, String[] dictionary) { + Trie trie = new Trie(); + for (String word : dictionary) { + trie.addWord(word); + } + + int[] dp = new int[s.length() + 1]; + Arrays.fill(dp, -1); + + return dfs(0, s, trie, dp); + } + + private int dfs(int i, String s, Trie trie, int[] dp) { + if (i == s.length()) return 0; + if (dp[i] != -1) return dp[i]; + + int res = 1 + dfs(i + 1, s, trie, dp); + TrieNode curr = trie.root; + + for (int j = i; j < s.length(); j++) { + if (curr.children[s.charAt(j) - 'a'] == null) break; + curr = curr.children[s.charAt(j) - 'a']; + if (curr.isWord) { + res = Math.min(res, dfs(j + 1, s, trie, dp)); + } + } + + dp[i] = res; + return res; + } +} +``` + +```cpp +class TrieNode { +public: + TrieNode* children[26]; + bool isWord; + + TrieNode() { + for (int i = 0; i < 26; ++i) children[i] = nullptr; + isWord = false; + } +}; + +class Trie { +public: + TrieNode* root; + + Trie() { + root = new TrieNode(); + } + + void addWord(const string& word) { + TrieNode* curr = root; + for (char c : word) { + if (!curr->children[c - 'a']) { + curr->children[c - 'a'] = new TrieNode(); + } + curr = curr->children[c - 'a']; + } + curr->isWord = true; + } +}; + +class Solution { +public: + int minExtraChar(string s, vector& dictionary) { + Trie trie; + for (const string& word : dictionary) { + trie.addWord(word); + } + + vector dp(s.size() + 1, -1); + return dfs(0, s, trie, dp); + } + +private: + int dfs(int i, const string& s, Trie& trie, vector& dp) { + if (i == s.size()) return 0; + if (dp[i] != -1) return dp[i]; + + int res = 1 + dfs(i + 1, s, trie, dp); + TrieNode* curr = trie.root; + + for (int j = i; j < s.size(); ++j) { + if (!curr->children[s[j] - 'a']) break; + curr = curr->children[s[j] - 'a']; + if (curr->isWord) { + res = min(res, dfs(j + 1, s, trie, dp)); + } + } + + dp[i] = res; + return res; + } +}; +``` + +```javascript +class TrieNode { + constructor() { + this.children = {}; + this.isWord = false; + } +} + +class Trie { + constructor() { + this.root = new TrieNode(); + } + + /** + * @param {string} word + * @return {void} + */ + addWord(word) { + let curr = this.root; + for (const c of word) { + if (!curr.children[c]) { + curr.children[c] = new TrieNode(); + } + curr = curr.children[c]; + } + curr.isWord = true; + } +} + +class Solution { + /** + * @param {string} s + * @param {string[]} dictionary + * @return {number} + */ + minExtraChar(s, dictionary) { + const trie = new Trie(); + for (const word of dictionary) { + trie.addWord(word); + } + + const dp = Array(s.length + 1).fill(-1); + + const dfs = (i) => { + if (i === s.length) return 0; + if (dp[i] !== -1) return dp[i]; + + let res = 1 + dfs(i + 1); + let curr = trie.root; + + for (let j = i; j < s.length; j++) { + if (!curr.children[s[j]]) break; + curr = curr.children[s[j]]; + if (curr.isWord) { + res = Math.min(res, dfs(j + 1)); + } + } + + dp[i] = res; + return res; + }; + + return dfs(0); + } +} +``` + +```csharp +public class TrieNode { + public TrieNode[] Children = new TrieNode[26]; + public bool IsWord = false; +} + +public class Trie { + public TrieNode Root = new TrieNode(); + + public void AddWord(string word) { + TrieNode curr = Root; + foreach (char c in word) { + int idx = c - 'a'; + if (curr.Children[idx] == null) { + curr.Children[idx] = new TrieNode(); + } + curr = curr.Children[idx]; + } + curr.IsWord = true; + } +} + +public class Solution { + public int MinExtraChar(string s, string[] dictionary) { + Trie trie = new Trie(); + foreach (string word in dictionary) { + trie.AddWord(word); + } + + int[] dp = new int[s.Length + 1]; + Array.Fill(dp, -1); + + return Dfs(0, s, trie, dp); + } + + private int Dfs(int i, string s, Trie trie, int[] dp) { + if (i == s.Length) return 0; + if (dp[i] != -1) return dp[i]; + + int res = 1 + Dfs(i + 1, s, trie, dp); + TrieNode curr = trie.Root; + + for (int j = i; j < s.Length; j++) { + int idx = s[j] - 'a'; + if (curr.Children[idx] == null) break; + curr = curr.Children[idx]; + if (curr.IsWord) { + res = Math.Min(res, Dfs(j + 1, s, trie, dp)); + } + } + + dp[i] = res; + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 + m * k)$ +* Space complexity: $O(n + m * k)$ + +> Where $n$ is the length of the string $s$, $m$ is the number of words in the dictionary, and $k$ is the average length of a word in the dictionary. + +--- + +## 7. Dynamic Programming (Bottom-Up) Using Trie + +::tabs-start + +```python +class TrieNode: + def __init__(self): + self.children = {} + self.isWord = False + +class Trie: + def __init__(self): + self.root = TrieNode() + + def addWord(self, word): + curr = self.root + for c in word: + if c not in curr.children: + curr.children[c] = TrieNode() + curr = curr.children[c] + curr.isWord = True + +class Solution: + def minExtraChar(self, s: str, dictionary: List[str]) -> int: + trie = Trie() + for w in dictionary: + trie.addWord(w) + + n = len(s) + dp = [0] * (n + 1) + + for i in range(n - 1, -1, -1): + dp[i] = 1 + dp[i + 1] + curr = trie.root + for j in range(i, n): + if s[j] not in curr.children: + break + curr = curr.children[s[j]] + if curr.isWord: + dp[i] = min(dp[i], dp[j + 1]) + + return dp[0] +``` + +```java +class TrieNode { + TrieNode[] children; + boolean isWord; + + TrieNode() { + children = new TrieNode[26]; + isWord = false; + } +} + +class Trie { + TrieNode root; + + Trie() { + root = new TrieNode(); + } + + void addWord(String word) { + TrieNode curr = root; + for (char c : word.toCharArray()) { + if (curr.children[c - 'a'] == null) { + curr.children[c - 'a'] = new TrieNode(); + } + curr = curr.children[c - 'a']; + } + curr.isWord = true; + } +} + +public class Solution { + public int minExtraChar(String s, String[] dictionary) { + Trie trie = new Trie(); + for (String word : dictionary) { + trie.addWord(word); + } + + int n = s.length(); + int[] dp = new int[n + 1]; + for (int i = n - 1; i >= 0; i--) { + dp[i] = 1 + dp[i + 1]; + TrieNode curr = trie.root; + + for (int j = i; j < n; j++) { + if (curr.children[s.charAt(j) - 'a'] == null) break; + curr = curr.children[s.charAt(j) - 'a']; + if (curr.isWord) { + dp[i] = Math.min(dp[i], dp[j + 1]); + } + } + } + + return dp[0]; + } +} +``` + +```cpp +class TrieNode { +public: + TrieNode* children[26]; + bool isWord; + + TrieNode() { + for (int i = 0; i < 26; ++i) children[i] = nullptr; + isWord = false; + } +}; + +class Trie { +public: + TrieNode* root; + + Trie() { + root = new TrieNode(); + } + + void addWord(const string& word) { + TrieNode* curr = root; + for (char c : word) { + if (!curr->children[c - 'a']) { + curr->children[c - 'a'] = new TrieNode(); + } + curr = curr->children[c - 'a']; + } + curr->isWord = true; + } +}; + +class Solution { +public: + int minExtraChar(string s, vector& dictionary) { + Trie trie; + for (const string& word : dictionary) { + trie.addWord(word); + } + + int n = s.size(); + vector dp(n + 1); + for (int i = n - 1; i >= 0; --i) { + dp[i] = 1 + dp[i + 1]; + TrieNode* curr = trie.root; + + for (int j = i; j < n; ++j) { + if (!curr->children[s[j] - 'a']) break; + curr = curr->children[s[j] - 'a']; + if (curr->isWord) { + dp[i] = min(dp[i], dp[j + 1]); + } + } + } + + return dp[0]; + } +}; +``` + +```javascript +class TrieNode { + constructor() { + this.children = {}; + this.isWord = false; + } +} + +class Trie { + constructor() { + this.root = new TrieNode(); + } + + /** + * @param {string} word + * @return {void} + */ + addWord(word) { + let curr = this.root; + for (const c of word) { + if (!curr.children[c]) { + curr.children[c] = new TrieNode(); + } + curr = curr.children[c]; + } + curr.isWord = true; + } +} + +class Solution { + /** + * @param {string} s + * @param {string[]} dictionary + * @return {number} + */ + minExtraChar(s, dictionary) { + const trie = new Trie(); + for (const word of dictionary) { + trie.addWord(word); + } + + const n = s.length; + const dp = new Int32Array(n + 1); + for (let i = n - 1; i >= 0; i--) { + dp[i] = 1 + dp[i + 1]; + let curr = trie.root; + + for (let j = i; j < n; j++) { + if (!curr.children[s[j]]) break; + curr = curr.children[s[j]]; + if (curr.isWord) { + dp[i] = Math.min(dp[i], dp[j + 1]); + } + } + } + + return dp[0]; + } +} +``` + +```csharp +public class TrieNode { + public TrieNode[] Children = new TrieNode[26]; + public bool IsWord = false; +} + +public class Trie { + public TrieNode Root = new TrieNode(); + + public void AddWord(string word) { + TrieNode curr = Root; + foreach (char c in word) { + int idx = c - 'a'; + if (curr.Children[idx] == null) { + curr.Children[idx] = new TrieNode(); + } + curr = curr.Children[idx]; + } + curr.IsWord = true; + } +} + +public class Solution { + public int MinExtraChar(string s, string[] dictionary) { + Trie trie = new Trie(); + foreach (string word in dictionary) { + trie.AddWord(word); + } + + int n = s.Length; + int[] dp = new int[n + 1]; + + for (int i = n - 1; i >= 0; i--) { + dp[i] = 1 + dp[i + 1]; + TrieNode curr = trie.Root; + + for (int j = i; j < n; j++) { + int idx = s[j] - 'a'; + if (curr.Children[idx] == null) break; + curr = curr.Children[idx]; + if (curr.IsWord) { + dp[i] = Math.Min(dp[i], dp[j + 1]); + } + } + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 + m * k)$ +* Space complexity: $O(n + m * k)$ + +> Where $n$ is the length of the string $s$, $m$ is the number of words in the dictionary, and $k$ is the average length of a word in the dictionary. \ No newline at end of file diff --git a/articles/find-all-anagrams-in-a-string.md b/articles/find-all-anagrams-in-a-string.md new file mode 100644 index 000000000..4201b94ed --- /dev/null +++ b/articles/find-all-anagrams-in-a-string.md @@ -0,0 +1,581 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def findAnagrams(self, s: str, p: str) -> List[int]: + n, m = len(s), len(p) + p = sorted(p) + res = [] + for i in range(n - m + 1): + sub = sorted(s[i : i + m]) + if sub == p: + res.append(i) + return res +``` + +```java +public class Solution { + public List findAnagrams(String s, String p) { + int n = s.length(), m = p.length(); + List res = new ArrayList<>(); + char[] pArr = p.toCharArray(); + Arrays.sort(pArr); + String sortedP = new String(pArr); + + for (int i = 0; i <= n - m; i++) { + char[] subArr = s.substring(i, i + m).toCharArray(); + Arrays.sort(subArr); + if (new String(subArr).equals(sortedP)) { + res.add(i); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findAnagrams(string s, string p) { + int n = s.size(), m = p.size(); + vector res; + sort(p.begin(), p.end()); + + for (int i = 0; i <= n - m; i++) { + string sub = s.substr(i, m); + sort(sub.begin(), sub.end()); + if (sub == p) { + res.push_back(i); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} p + * @return {number[]} + */ + findAnagrams(s, p) { + const n = s.length, m = p.length; + const res = []; + const sortedP = p.split('').sort().join(''); + + for (let i = 0; i <= n - m; i++) { + const sub = s.substring(i, i + m).split('').sort().join(''); + if (sub === sortedP) { + res.push(i); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m \log m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $p$. + +--- + +## 2. Prefix Count + Sliding Window + +::tabs-start + +```python +class Solution: + def findAnagrams(self, s: str, p: str) -> List[int]: + n, m = len(s), len(p) + if m > n: + return [] + pCount = [0] * 26 + for c in p: + pCount[ord(c) - ord('a')] += 1 + + prefix = [[0] * 26 for _ in range(n + 1)] + for i in range(1, n + 1): + for j in range(26): + prefix[i][j] = prefix[i - 1][j] + prefix[i][ord(s[i - 1]) - ord('a')] += 1 + + i, j = 0, m - 1 + res = [] + while j < n: + isValid = True + for c in range(26): + if prefix[j + 1][c] - prefix[i][c] != pCount[c]: + isValid = False + break + if isValid: + res.append(i) + i += 1 + j += 1 + + return res +``` + +```java +public class Solution { + public List findAnagrams(String s, String p) { + int n = s.length(), m = p.length(); + if (m > n) return new ArrayList<>(); + + int[] pCount = new int[26]; + for (char c : p.toCharArray()) { + pCount[c - 'a']++; + } + + int[][] prefix = new int[n + 1][26]; + for (int i = 1; i <= n; i++) { + System.arraycopy(prefix[i - 1], 0, prefix[i], 0, 26); + prefix[i][s.charAt(i - 1) - 'a']++; + } + + List res = new ArrayList<>(); + int i = 0, j = m - 1; + while (j < n) { + boolean isValid = true; + for (int c = 0; c < 26; c++) { + if (prefix[j + 1][c] - prefix[i][c] != pCount[c]) { + isValid = false; + break; + } + } + if (isValid) res.add(i); + i++; + j++; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findAnagrams(string s, string p) { + int n = s.size(), m = p.size(); + if (m > n) return {}; + + vector pCount(26, 0); + for (char c : p) pCount[c - 'a']++; + + vector> prefix(n + 1, vector(26, 0)); + for (int i = 1; i <= n; i++) { + prefix[i] = prefix[i - 1]; + prefix[i][s[i - 1] - 'a']++; + } + + vector res; + int i = 0, j = m - 1; + while (j < n) { + bool isValid = true; + for (int c = 0; c < 26; c++) { + if (prefix[j + 1][c] - prefix[i][c] != pCount[c]) { + isValid = false; + break; + } + } + if (isValid) res.push_back(i); + i++; + j++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} p + * @return {number[]} + */ + findAnagrams(s, p) { + const n = s.length, m = p.length; + if (m > n) return []; + + const pCount = Array(26).fill(0); + for (const c of p) { + pCount[c.charCodeAt(0) - 97]++; + } + + const prefix = Array.from({ length: n + 1 }, () => Array(26).fill(0)); + for (let i = 1; i <= n; i++) { + for (let j = 0; j < 26; j++) { + prefix[i][j] = prefix[i - 1][j]; + } + prefix[i][s.charCodeAt(i - 1) - 97]++; + } + + const res = []; + let i = 0, j = m - 1; + while (j < n) { + let isValid = true; + for (let c = 0; c < 26; c++) { + if (prefix[j + 1][c] - prefix[i][c] !== pCount[c]) { + isValid = false; + break; + } + } + if (isValid) res.push(i); + i++; + j++; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $p$. + +--- + +## 3. Sliding Window + +::tabs-start + +```python +class Solution: + def findAnagrams(self, s: str, p: str) -> List[int]: + if len(p) > len(s): return [] + pCount, sCount = {}, {} + for i in range(len(p)): + pCount[p[i]] = 1 + pCount.get(p[i], 0) + sCount[s[i]] = 1+ sCount.get(s[i], 0) + + res = [0] if sCount == pCount else [] + l = 0 + for r in range(len(p), len(s)): + sCount[s[r]] = 1+ sCount.get(s[r], 0) + sCount[s[l]] -= 1 + + if sCount[s[l]] == 0: + sCount.pop(s[l]) + l += 1 + if sCount == pCount: + res.append(l) + return res +``` + +```java +public class Solution { + public List findAnagrams(String s, String p) { + if (p.length() > s.length()) return new ArrayList<>(); + + int[] pCount = new int[26]; + int[] sCount = new int[26]; + + for (char c : p.toCharArray()) { + pCount[c - 'a']++; + } + for (int i = 0; i < p.length(); i++) { + sCount[s.charAt(i) - 'a']++; + } + + List res = new ArrayList<>(); + if (Arrays.equals(pCount, sCount)) res.add(0); + + int l = 0; + for (int r = p.length(); r < s.length(); r++) { + sCount[s.charAt(r) - 'a']++; + sCount[s.charAt(l) - 'a']--; + l++; + if (Arrays.equals(pCount, sCount)) { + res.add(l); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findAnagrams(string s, string p) { + if (p.size() > s.size()) return {}; + + vector pCount(26, 0), sCount(26, 0); + for (char c : p) { + pCount[c - 'a']++; + } + for (int i = 0; i < p.size(); i++) { + sCount[s[i] - 'a']++; + } + + vector res; + if (pCount == sCount) res.push_back(0); + + int l = 0; + for (int r = p.size(); r < s.size(); r++) { + sCount[s[r] - 'a']++; + sCount[s[l] - 'a']--; + l++; + if (pCount == sCount) { + res.push_back(l); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} p + * @return {number[]} + */ + findAnagrams(s, p) { + if (p.length > s.length) return []; + + const pCount = new Array(26).fill(0); + const sCount = new Array(26).fill(0); + + for (const char of p) { + pCount[char.charCodeAt(0) - 97]++; + } + for (let i = 0; i < p.length; i++) { + sCount[s.charCodeAt(i) - 97]++; + } + + const res = []; + if (pCount.toString() === sCount.toString()) res.push(0); + + let l = 0; + for (let r = p.length; r < s.length; r++) { + sCount[s.charCodeAt(r) - 97]++; + sCount[s.charCodeAt(l) - 97]--; + l++; + if (pCount.toString() === sCount.toString()) { + res.push(l); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $p$. + +--- + +## 4. Sliding Window (Optimal) + +::tabs-start + +```python +class Solution: + def findAnagrams(self, s: str, p: str) -> List[int]: + n, m = len(s), len(p) + if m > n: + return [] + + pCount = [0] * 26 + sCount = [0] * 26 + for i in range(m): + pCount[ord(p[i]) - ord('a')] += 1 + sCount[ord(s[i]) - ord('a')] += 1 + + match = sum(1 for i in range(26) if pCount[i] == sCount[i]) + res = [] + if match == 26: + res.append(0) + + l = 0 + for r in range(m, n): + c = ord(s[l]) - ord('a') + if sCount[c] == pCount[c]: + match -= 1 + sCount[c] -= 1 + l += 1 + if sCount[c] == pCount[c]: + match += 1 + + c = ord(s[r]) - ord('a') + if sCount[c] == pCount[c]: + match -= 1 + sCount[c] += 1 + if sCount[c] == pCount[c]: + match += 1 + + if match == 26: + res.append(l) + + return res +``` + +```java +public class Solution { + public List findAnagrams(String s, String p) { + int n = s.length(), m = p.length(); + if (m > n) return new ArrayList<>(); + + int[] pCount = new int[26], sCount = new int[26]; + for (int i = 0; i < m; i++) { + pCount[p.charAt(i) - 'a']++; + sCount[s.charAt(i) - 'a']++; + } + + int match = 0; + for (int i = 0; i < 26; i++) { + if (pCount[i] == sCount[i]) match++; + } + + List res = new ArrayList<>(); + if (match == 26) res.add(0); + + int l = 0; + for (int r = m; r < n; r++) { + int c = s.charAt(l) - 'a'; + if (sCount[c] == pCount[c]) match--; + sCount[c]--; + l++; + if (sCount[c] == pCount[c]) match++; + + c = s.charAt(r) - 'a'; + if (sCount[c] == pCount[c]) match--; + sCount[c]++; + if (sCount[c] == pCount[c]) match++; + + if (match == 26) res.add(l); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findAnagrams(string s, string p) { + int n = s.size(), m = p.size(); + if (m > n) return {}; + + vector pCount(26, 0), sCount(26, 0); + for (int i = 0; i < m; i++) { + pCount[p[i] - 'a']++; + sCount[s[i] - 'a']++; + } + + int match = 0; + for (int i = 0; i < 26; i++) { + if (pCount[i] == sCount[i]) match++; + } + + vector res; + if (match == 26) res.push_back(0); + + int l = 0; + for (int r = m; r < n; r++) { + int c = s[l] - 'a'; + if (sCount[c] == pCount[c]) match--; + sCount[c]--; + l++; + if (sCount[c] == pCount[c]) match++; + + c = s[r] - 'a'; + if (sCount[c] == pCount[c]) match--; + sCount[c]++; + if (sCount[c] == pCount[c]) match++; + + if (match == 26) res.push_back(l); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} p + * @return {number[]} + */ + findAnagrams(s, p) { + const n = s.length, m = p.length; + if (m > n) return []; + + const pCount = new Array(26).fill(0); + const sCount = new Array(26).fill(0); + + for (let i = 0; i < m; i++) { + pCount[p.charCodeAt(i) - 97]++; + sCount[s.charCodeAt(i) - 97]++; + } + + let match = 0; + for (let i = 0; i < 26; i++) { + if (pCount[i] === sCount[i]) match++; + } + + const res = []; + if (match === 26) res.push(0); + + let l = 0; + for (let r = m; r < n; r++) { + let c = s.charCodeAt(l) - 97; + if (sCount[c] === pCount[c]) match--; + sCount[c]--; + l++; + if (sCount[c] === pCount[c]) match++; + + c = s.charCodeAt(r) - 97; + if (sCount[c] === pCount[c]) match--; + sCount[c]++; + if (sCount[c] === pCount[c]) match++; + + if (match === 26) res.push(l); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $p$. \ No newline at end of file diff --git a/articles/find-all-duplicates-in-an-array.md b/articles/find-all-duplicates-in-an-array.md new file mode 100644 index 000000000..3d1f99fde --- /dev/null +++ b/articles/find-all-duplicates-in-an-array.md @@ -0,0 +1,440 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def findDuplicates(self, nums: List[int]) -> List[int]: + n = len(nums) + res = [] + + for i in range(n): + for j in range(i + 1, n): + if nums[i] == nums[j]: + res.append(nums[i]) + break + + return res +``` + +```java +public class Solution { + public List findDuplicates(int[] nums) { + int n = nums.length; + List res = new ArrayList<>(); + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if (nums[i] == nums[j]) { + res.add(nums[i]); + break; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findDuplicates(vector& nums) { + int n = nums.size(); + vector res; + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if (nums[i] == nums[j]) { + res.push_back(nums[i]); + break; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findDuplicates(nums) { + const n = nums.length; + const res = []; + + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + if (nums[i] === nums[j]) { + res.push(nums[i]); + break; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def findDuplicates(self, nums: List[int]) -> List[int]: + nums.sort() + res = [] + + for i in range(len(nums) - 1): + if nums[i] == nums[i + 1]: + res.append(nums[i]) + + return res +``` + +```java +public class Solution { + public List findDuplicates(int[] nums) { + Arrays.sort(nums); + List res = new ArrayList<>(); + + for (int i = 0; i < nums.length - 1; i++) { + if (nums[i] == nums[i + 1]) { + res.add(nums[i]); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findDuplicates(vector& nums) { + sort(nums.begin(), nums.end()); + vector res; + + for (int i = 0; i < nums.size() - 1; i++) { + if (nums[i] == nums[i + 1]) { + res.push_back(nums[i]); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findDuplicates(nums) { + nums.sort((a, b) => a - b); + const res = []; + + for (let i = 0; i < nums.length - 1; i++) { + if (nums[i] === nums[i + 1]) { + res.push(nums[i]); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Hash Set + +::tabs-start + +```python +class Solution: + def findDuplicates(self, nums: List[int]) -> List[int]: + seen = set() + res = [] + for num in nums: + if num in seen: + res.append(num) + else: + seen.add(num) + return res +``` + +```java +public class Solution { + public List findDuplicates(int[] nums) { + Set seen = new HashSet<>(); + List res = new ArrayList<>(); + + for (int num : nums) { + if (seen.contains(num)) { + res.add(num); + } else { + seen.add(num); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findDuplicates(vector& nums) { + unordered_set seen; + vector res; + + for (int num : nums) { + if (seen.count(num)) { + res.push_back(num); + } else { + seen.insert(num); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findDuplicates(nums) { + const seen = new Set(); + const res = []; + + for (const num of nums) { + if (seen.has(num)) { + res.push(num); + } else { + seen.add(num); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Hash Map + +::tabs-start + +```python +class Solution: + def findDuplicates(self, nums: List[int]) -> List[int]: + count = Counter(nums) + res = [] + + for num in count: + if count[num] == 2: + res.append(num) + + return res +``` + +```java +public class Solution { + public List findDuplicates(int[] nums) { + Map count = new HashMap<>(); + List res = new ArrayList<>(); + + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + for (int num : count.keySet()) { + if (count.get(num) == 2) { + res.add(num); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findDuplicates(vector& nums) { + unordered_map count; + vector res; + + for (int num : nums) { + count[num]++; + } + + for (auto& [num, freq] : count) { + if (freq == 2) { + res.push_back(num); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findDuplicates(nums) { + const count = new Map(); + const res = []; + + for (const num of nums) { + count.set(num, (count.get(num) || 0) + 1); + } + + for (const [num, freq] of count.entries()) { + if (freq === 2) { + res.push(num); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Negative Marking + +::tabs-start + +```python +class Solution: + def findDuplicates(self, nums: List[int]) -> List[int]: + res = [] + + for num in nums: + idx = abs(num) - 1 + if nums[idx] < 0: + res.append(abs(num)) + nums[idx] = -nums[idx] + + return res +``` + +```java +public class Solution { + public List findDuplicates(int[] nums) { + List res = new ArrayList<>(); + + for (int num : nums) { + int idx = Math.abs(num) - 1; + if (nums[idx] < 0) { + res.add(Math.abs(num)); + } + nums[idx] = -nums[idx]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findDuplicates(vector& nums) { + vector res; + + for (int num : nums) { + int idx = abs(num) - 1; + if (nums[idx] < 0) { + res.push_back(abs(num)); + } + nums[idx] = -nums[idx]; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findDuplicates(nums) { + const res = []; + + for (let num of nums) { + const idx = Math.abs(num) - 1; + if (nums[idx] < 0) { + res.push(Math.abs(num)); + } + nums[idx] = -nums[idx]; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/find-all-numbers-disappeared-in-an-array.md b/articles/find-all-numbers-disappeared-in-an-array.md new file mode 100644 index 000000000..f0462dc4f --- /dev/null +++ b/articles/find-all-numbers-disappeared-in-an-array.md @@ -0,0 +1,358 @@ +## 1. Hash Set + +::tabs-start + +```python +class Solution: + def findDisappearedNumbers(self, nums: List[int]) -> List[int]: + n = len(nums) + store = set(range(1, n + 1)) + + for num in nums: + store.discard(num) + + return list(store) +``` + +```java +public class Solution { + public List findDisappearedNumbers(int[] nums) { + int n = nums.length; + Set store = new HashSet<>(); + for (int i = 1; i <= n; i++) store.add(i); + + for (int num : nums) { + store.remove(num); + } + + return new ArrayList<>(store); + } +} +``` + +```cpp +class Solution { +public: + vector findDisappearedNumbers(vector& nums) { + int n = nums.size(); + unordered_set store; + for (int i = 1; i <= n; i++) store.insert(i); + + for (int num : nums) { + store.erase(num); + } + + vector result(store.begin(), store.end()); + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findDisappearedNumbers(nums) { + const n = nums.length; + const store = new Set(); + for (let i = 1; i <= n; i++) store.add(i); + + for (let num of nums) { + store.delete(num); + } + + return Array.from(store); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Boolean Array + +::tabs-start + +```python +class Solution: + def findDisappearedNumbers(self, nums: List[int]) -> List[int]: + n = len(nums) + mark = [False] * n + + for num in nums: + mark[num - 1] = True + + res = [] + for i in range(1, n + 1): + if not mark[i - 1]: + res.append(i) + return res +``` + +```java +public class Solution { + public List findDisappearedNumbers(int[] nums) { + int n = nums.length; + boolean[] mark = new boolean[n]; + + for (int num : nums) { + mark[num - 1] = true; + } + + List res = new ArrayList<>(); + for (int i = 1; i <= n; i++) { + if (!mark[i - 1]) { + res.add(i); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findDisappearedNumbers(vector& nums) { + int n = nums.size(); + vector mark(n, false); + + for (int num : nums) { + mark[num - 1] = true; + } + + vector res; + for (int i = 1; i <= n; i++) { + if (!mark[i - 1]) { + res.push_back(i); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findDisappearedNumbers(nums) { + const n = nums.length; + const mark = new Array(n).fill(false); + + for (let num of nums) { + mark[num - 1] = true; + } + + const res = []; + for (let i = 1; i <= n; i++) { + if (!mark[i - 1]) { + res.push(i); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sorting + +::tabs-start + +```python +class Solution: + def findDisappearedNumbers(self, nums: List[int]) -> List[int]: + n = len(nums) + nums.sort() + + res = [] + idx = 0 + for num in range(1, n + 1): + while idx < n and nums[idx] < num: + idx += 1 + if idx == n or nums[idx] > num: + res.append(num) + return res +``` + +```java +public class Solution { + public List findDisappearedNumbers(int[] nums) { + int n = nums.length; + Arrays.sort(nums); + + List res = new ArrayList<>(); + int idx = 0; + for (int num = 1; num <= n; num++) { + while (idx < n && nums[idx] < num) { + idx++; + } + if (idx == n || nums[idx] > num) { + res.add(num); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findDisappearedNumbers(vector& nums) { + int n = nums.size(); + sort(nums.begin(), nums.end()); + + vector res; + int idx = 0; + for (int num = 1; num <= n; num++) { + while (idx < n && nums[idx] < num) { + idx++; + } + if (idx == n || nums[idx] > num) { + res.push_back(num); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findDisappearedNumbers(nums) { + nums.sort((a, b) => a - b); + + const res = []; + let idx = 0; + for (let num = 1; num <= nums.length; num++) { + while (idx < nums.length && nums[idx] < num) { + idx++; + } + if (idx === nums.length || nums[idx] > num) { + res.push(num); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 4. Negative Marking + +::tabs-start + +```python +class Solution: + def findDisappearedNumbers(self, nums: List[int]) -> List[int]: + for num in nums: + i = abs(num) - 1 + nums[i] = -1 * abs(nums[i]) + + res = [] + for i, num in enumerate(nums): + if num > 0: + res.append(i + 1) + return res +``` + +```java +public class Solution { + public List findDisappearedNumbers(int[] nums) { + for (int num : nums) { + int i = Math.abs(num) - 1; + nums[i] = -Math.abs(nums[i]); + } + + List res = new ArrayList<>(); + for (int i = 0; i < nums.length; i++) { + if (nums[i] > 0) { + res.add(i + 1); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findDisappearedNumbers(vector& nums) { + for (int num : nums) { + int i = abs(num) - 1; + nums[i] = -abs(nums[i]); + } + + vector res; + for (int i = 0; i < nums.size(); i++) { + if (nums[i] > 0) { + res.push_back(i + 1); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findDisappearedNumbers(nums) { + for (let num of nums) { + let i = Math.abs(num) - 1; + nums[i] = -Math.abs(nums[i]); + } + + const res = []; + for (let i = 0; i < nums.length; i++) { + if (nums[i] > 0) { + res.push(i + 1); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we modified the input array without using extra space. \ No newline at end of file diff --git a/articles/find-all-people-with-secret.md b/articles/find-all-people-with-secret.md new file mode 100644 index 000000000..1d1b58e20 --- /dev/null +++ b/articles/find-all-people-with-secret.md @@ -0,0 +1,834 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def findAllPeople(self, n: int, meetings: list[list[int]], firstPerson: int) -> list[int]: + secrets = set([0, firstPerson]) # People with secret + time_map = {} # time -> adjacency list meetings + + for src, dst, t in meetings: + if t not in time_map: + time_map[t] = defaultdict(list) + time_map[t][src].append(dst) + time_map[t][dst].append(src) + + def dfs(src, adj): + if src in visit: + return + visit.add(src) + secrets.add(src) + for nei in adj[src]: + dfs(nei, adj) + + for t in sorted(time_map.keys()): + visit = set() + for src in time_map[t]: + if src in secrets: + dfs(src, time_map[t]) + + return list(secrets) +``` + +```java +public class Solution { + private Set secrets, visit; + + public List findAllPeople(int n, int[][] meetings, int firstPerson) { + secrets = new HashSet<>(); + visit = new HashSet<>(); + secrets.add(0); + secrets.add(firstPerson); + Map>> time_map = new HashMap<>(); + + for (int[] meet : meetings) { + int src = meet[0], dst = meet[1], t = meet[2]; + time_map.putIfAbsent(t, new HashMap<>()); + time_map.get(t).putIfAbsent(src, new ArrayList<>()); + time_map.get(t).putIfAbsent(dst, new ArrayList<>()); + time_map.get(t).get(src).add(dst); + time_map.get(t).get(dst).add(src); + } + + List timeKeys = new ArrayList<>(time_map.keySet()); + Collections.sort(timeKeys); + for (int t : timeKeys) { + visit = new HashSet<>(); + for (int src : time_map.get(t).keySet()) { + if (secrets.contains(src)) { + dfs(src, time_map.get(t)); + } + } + } + return new ArrayList<>(secrets); + } + + private void dfs(int src, Map> adj) { + if (!visit.add(src)) return; + secrets.add(src); + for (int nei : adj.getOrDefault(src, new ArrayList<>())) { + dfs(nei, adj); + } + } +} +``` + +```cpp +class Solution { +public: + unordered_set secrets, visit; + + vector findAllPeople(int n, vector>& meetings, int firstPerson) { + secrets = {0, firstPerson}; + unordered_map>> time_map; + + for (auto& meet : meetings) { + int src = meet[0], dst = meet[1], t = meet[2]; + time_map[t][src].push_back(dst); + time_map[t][dst].push_back(src); + } + + vector timeKeys; + for (auto& [t, _] : time_map) { + timeKeys.push_back(t); + } + sort(timeKeys.begin(), timeKeys.end()); + + for (int& t : timeKeys) { + visit.clear(); + for (auto& [src, _] : time_map[t]) { + if (secrets.count(src)) { + dfs(src, time_map[t]); + } + } + } + + return vector(secrets.begin(), secrets.end()); + } + +private: + void dfs(int src, unordered_map>& adj) { + if (!visit.insert(src).second) return; + secrets.insert(src); + for (int& nei : adj[src]) { + dfs(nei, adj); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} meetings + * @param {number} firstPerson + * @return {number[]} + */ + findAllPeople(n, meetings, firstPerson) { + const secrets = new Set([0, firstPerson]); + let visit = new Set(); + let time_map = new Map(); + + for (let [src, dst, t] of meetings) { + if (!time_map.has(t)) time_map.set(t, new Map()); + if (!time_map.get(t).has(src)) time_map.get(t).set(src, []); + if (!time_map.get(t).has(dst)) time_map.get(t).set(dst, []); + time_map.get(t).get(src).push(dst); + time_map.get(t).get(dst).push(src); + } + const dfs = (src, adj) => { + if (visit.has(src)) return; + visit.add(src); + secrets.add(src); + for (let nei of (adj.get(src) || [])) { + dfs(nei, adj); + } + }; + + let timeKeys = [...time_map.keys()].sort((a, b) => a - b); + for (let t of timeKeys) { + visit.clear(); + for (let src of time_map.get(t).keys()) { + if (secrets.has(src)) { + dfs(src, time_map.get(t)); + } + } + } + + return [...secrets]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m \log m + n)$ +* Space complexity: $O(m + n)$ + +> Where $m$ is the number of meetings and $n$ is the number of people. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def findAllPeople(self, n: int, meetings: list[list[int]], firstPerson: int) -> list[int]: + secrets = set([0, firstPerson]) # People with secret + time_map = {} # time -> adjacency list meetings + + for src, dst, t in meetings: + if t not in time_map: + time_map[t] = defaultdict(list) + time_map[t][src].append(dst) + time_map[t][dst].append(src) + + for t in sorted(time_map.keys()): + visit = set() + q = deque() + + for src in time_map[t]: + if src in secrets: + q.append(src) + visit.add(src) + + while q: + node = q.popleft() + secrets.add(node) + for nei in time_map[t][node]: + if nei not in visit: + visit.add(nei) + q.append(nei) + + return list(secrets) +``` + +```java +public class Solution { + public List findAllPeople(int n, int[][] meetings, int firstPerson) { + Set secrets = new HashSet<>(); + secrets.add(0); + secrets.add(firstPerson); + TreeMap>> time_map = new TreeMap<>(); + + for (int[] meet : meetings) { + int src = meet[0], dst = meet[1], t = meet[2]; + time_map.putIfAbsent(t, new HashMap<>()); + time_map.get(t).putIfAbsent(src, new ArrayList<>()); + time_map.get(t).putIfAbsent(dst, new ArrayList<>()); + time_map.get(t).get(src).add(dst); + time_map.get(t).get(dst).add(src); + } + + for (int t : time_map.keySet()) { + Set visit = new HashSet<>(); + Queue q = new LinkedList<>(); + + for (int src : time_map.get(t).keySet()) { + if (secrets.contains(src)) { + q.offer(src); + visit.add(src); + } + } + + while (!q.isEmpty()) { + int node = q.poll(); + secrets.add(node); + for (int nei : time_map.get(t).get(node)) { + if (!visit.contains(nei)) { + visit.add(nei); + q.offer(nei); + } + } + } + } + + return new ArrayList<>(secrets); + } +} +``` + +```cpp +class Solution { +public: + vector findAllPeople(int n, vector>& meetings, int firstPerson) { + unordered_set secrets = {0, firstPerson}; + map>> time_map; + + for (auto& meet : meetings) { + int src = meet[0], dst = meet[1], t = meet[2]; + time_map[t][src].push_back(dst); + time_map[t][dst].push_back(src); + } + + for (auto& [t, adj] : time_map) { + unordered_set visit; + queue q; + + for (auto& [src, _] : adj) { + if (secrets.count(src)) { + q.push(src); + visit.insert(src); + } + } + + while (!q.empty()) { + int node = q.front(); + q.pop(); + secrets.insert(node); + for (int nei : adj[node]) { + if (!visit.count(nei)) { + visit.insert(nei); + q.push(nei); + } + } + } + } + + return vector(secrets.begin(), secrets.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} meetings + * @param {number} firstPerson + * @return {number[]} + */ + findAllPeople(n, meetings, firstPerson) { + const secrets = new Set([0, firstPerson]); + const time_map = new Map(); + + for (let [src, dst, t] of meetings) { + if (!time_map.has(t)) time_map.set(t, new Map()); + if (!time_map.get(t).has(src)) time_map.get(t).set(src, []); + if (!time_map.get(t).has(dst)) time_map.get(t).set(dst, []); + time_map.get(t).get(src).push(dst); + time_map.get(t).get(dst).push(src); + } + + for (let t of [...time_map.keys()].sort((a, b) => a - b)) { + let visit = new Set(); + const q = new Queue(); + + for (let src of time_map.get(t).keys()) { + if (secrets.has(src)) { + q.push(src); + visit.add(src); + } + } + + while (!q.isEmpty()) { + let node = q.pop(); + secrets.add(node); + for (let nei of time_map.get(t).get(node)) { + if (!visit.has(nei)) { + visit.add(nei); + q.push(nei); + } + } + } + } + + return [...secrets]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m \log m + n)$ +* Space complexity: $O(m + n)$ + +> Where $m$ is the number of meetings and $n$ is the number of people. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +class Solution: + def findAllPeople(self, n: int, meetings: list[list[int]], firstPerson: int) -> list[int]: + meetings.sort(key=lambda x: x[2]) # Sort by time + secrets = [False] * n + secrets[0] = secrets[firstPerson] = True + + for _, group in groupby(meetings, key=lambda x: x[2]): + adj = defaultdict(list) + visited = set() + + for u, v, _ in group: + adj[u].append(v) + adj[v].append(u) + if secrets[u]: + visited.add(u) + if secrets[v]: + visited.add(v) + + stack = list(visited) + while stack: + node = stack.pop() + for nei in adj[node]: + if nei not in visited: + visited.add(nei) + stack.append(nei) + secrets[nei] = True + + return [i for i in range(n) if secrets[i]] +``` + +```java +public class Solution { + public List findAllPeople(int n, int[][] meetings, int firstPerson) { + Arrays.sort(meetings, Comparator.comparingInt(a -> a[2])); + boolean[] secrets = new boolean[n]; + secrets[0] = secrets[firstPerson] = true; + + int i = 0, m = meetings.length; + while (i < m) { + int time = meetings[i][2]; + Map> adj = new HashMap<>(); + Set visited = new HashSet<>(); + + while (i < m && meetings[i][2] == time) { + int u = meetings[i][0], v = meetings[i][1]; + adj.computeIfAbsent(u, k -> new ArrayList<>()).add(v); + adj.computeIfAbsent(v, k -> new ArrayList<>()).add(u); + if (secrets[u]) visited.add(u); + if (secrets[v]) visited.add(v); + i++; + } + + Stack stack = new Stack<>(); + stack.addAll(visited); + while (!stack.isEmpty()) { + int node = stack.pop(); + for (int nei : adj.getOrDefault(node, Collections.emptyList())) { + if (!visited.contains(nei)) { + visited.add(nei); + stack.push(nei); + secrets[nei] = true; + } + } + } + } + + List res = new ArrayList<>(); + for (int j = 0; j < n; j++) { + if (secrets[j]) res.add(j); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findAllPeople(int n, vector>& meetings, int firstPerson) { + sort(meetings.begin(), meetings.end(), [](auto& a, auto& b) { + return a[2] < b[2]; + }); + + vector secrets(n, false); + secrets[0] = secrets[firstPerson] = true; + + int i = 0, m = meetings.size(); + while (i < m) { + int time = meetings[i][2]; + unordered_map> adj; + unordered_set visited; + + while (i < m && meetings[i][2] == time) { + int u = meetings[i][0], v = meetings[i][1]; + adj[u].push_back(v); + adj[v].push_back(u); + if (secrets[u]) visited.insert(u); + if (secrets[v]) visited.insert(v); + i++; + } + + stack stack(visited.begin(), visited.end()); + while (!stack.empty()) { + int node = stack.top(); stack.pop(); + for (int& nei : adj[node]) { + if (!visited.count(nei)) { + visited.insert(nei); + stack.push(nei); + secrets[nei] = true; + } + } + } + } + + vector res; + for (int j = 0; j < n; j++) { + if (secrets[j]) res.push_back(j); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} meetings + * @param {number} firstPerson + * @return {number[]} + */ + findAllPeople(n, meetings, firstPerson) { + meetings.sort((a, b) => a[2] - b[2]); + const secrets = new Array(n).fill(false); + secrets[0] = secrets[firstPerson] = true; + + let i = 0, m = meetings.length; + while (i < m) { + const time = meetings[i][2]; + const adj = new Map(); + const visited = new Set(); + + while (i < m && meetings[i][2] === time) { + const [u, v] = meetings[i]; + if (!adj.has(u)) adj.set(u, []); + if (!adj.has(v)) adj.set(v, []); + adj.get(u).push(v); + adj.get(v).push(u); + if (secrets[u]) visited.add(u); + if (secrets[v]) visited.add(v); + i++; + } + + const stack = [...visited]; + while (stack.length) { + const node = stack.pop(); + for (const nei of (adj.get(node) || [])) { + if (!visited.has(nei)) { + visited.add(nei); + stack.push(nei); + secrets[nei] = true; + } + } + } + } + + let res = []; + for (let i = 0; i < n; i++) { + if (secrets[i]) res.push(i); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m \log m + n)$ +* Space complexity: $O(m + n)$ + +> Where $m$ is the number of meetings and $n$ is the number of people. + +--- + +## 4. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu, pv = self.find(u), self.find(v) + if pu == pv: + return False + if self.Size[pu] < self.Size[pv]: + pu, pv = pv, pu + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + return True + + def reset(self, node): + self.Parent[node] = node + self.Size[node] = 1 + +class Solution: + def findAllPeople(self, n: int, meetings: list[list[int]], firstPerson: int) -> list[int]: + meetings.sort(key=lambda x: x[2]) # Sort by time + dsu = DSU(n) + dsu.union(0, firstPerson) + + for _, group in groupby(meetings, key=lambda x: x[2]): + group_nodes = set() + for u, v, _ in group: + dsu.union(u, v) + group_nodes.add(u) + group_nodes.add(v) + + for node in group_nodes: + if dsu.find(node) != dsu.find(0): + dsu.reset(node) + + return [i for i in range(n) if dsu.find(i) == dsu.find(0)] +``` + +```java +class DSU { + int[] Parent, Size; + + DSU(int n) { + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + void union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return; + if (Size[pu] < Size[pv]) { + int temp = pu; + pu = pv; + pv = temp; + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + } + + void reset(int node) { + Parent[node] = node; + Size[node] = 1; + } +} + +public class Solution { + public List findAllPeople(int n, int[][] meetings, int firstPerson) { + Arrays.sort(meetings, Comparator.comparingInt(a -> a[2])); + DSU dsu = new DSU(n); + dsu.union(0, firstPerson); + + for (int i = 0; i < meetings.length; ) { + int time = meetings[i][2]; + Set group = new HashSet<>(); + + for (; i < meetings.length && meetings[i][2] == time; i++) { + int u = meetings[i][0], v = meetings[i][1]; + dsu.union(u, v); + group.add(u); + group.add(v); + } + + for (int node : group) { + if (dsu.find(node) != dsu.find(0)) { + dsu.reset(node); + } + } + } + + List result = new ArrayList<>(); + for (int i = 0; i < n; i++) { + if (dsu.find(i) == dsu.find(0)) result.add(i); + } + return result; + } +} +``` + +```cpp +class DSU { +public: + vector Parent, Size; + + DSU(int n) { + Parent.resize(n + 1); + Size.resize(n + 1, 1); + for (int i = 0; i <= n; i++) { + Parent[i] = i; + } + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + void unionSets(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return; + if (Size[pu] < Size[pv]) swap(pu, pv); + Size[pu] += Size[pv]; + Parent[pv] = pu; + } + + void reset(int node) { + Parent[node] = node; + Size[node] = 1; + } +}; + +class Solution { +public: + vector findAllPeople(int n, vector>& meetings, int firstPerson) { + sort(meetings.begin(), meetings.end(), [](auto &a, auto &b) { + return a[2] < b[2]; + }); + DSU dsu(n); + dsu.unionSets(0, firstPerson); + + for (int i = 0; i < meetings.size(); ) { + int time = meetings[i][2]; + unordered_set group; + + for (; i < meetings.size() && meetings[i][2] == time; i++) { + int u = meetings[i][0], v = meetings[i][1]; + dsu.unionSets(u, v); + group.insert(u); + group.insert(v); + } + + for (int node : group) { + if (dsu.find(node) != dsu.find(0)) { + dsu.reset(node); + } + } + } + + vector result; + for (int i = 0; i < n; i++) { + if (dsu.find(i) == dsu.find(0)) result.push_back(i); + } + return result; + } +}; +``` + +```javascript +class DSU { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.Parent = Array.from({ length: n + 1 }, (_, i) => i); + this.Size = Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.Parent[node] !== node) { + this.Parent[node] = this.find(this.Parent[node]); + } + return this.Parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u); + let pv = this.find(v); + if (pu === pv) return false; + if (this.Size[pu] >= this.Size[pv]) { + this.Size[pu] += this.Size[pv]; + this.Parent[pv] = pu; + } else { + this.Size[pv] += this.Size[pu]; + this.Parent[pu] = pv; + } + return true; + } + + /** + * @param {number} node + * @return {void} + */ + reset(node) { + this.Parent[node] = node; + this.Size[node] = 1; + } +} + +class Solution { + /** + * @param {number} n + * @param {number[][]} meetings + * @param {number} firstPerson + * @return {number[]} + */ + findAllPeople(n, meetings, firstPerson) { + meetings.sort((a, b) => a[2] - b[2]); + let dsu = new DSU(n); + dsu.union(0, firstPerson); + + for (let i = 0; i < meetings.length; ) { + let time = meetings[i][2]; + let group = new Set(); + + for (; i < meetings.length && meetings[i][2] === time; i++) { + let [u, v] = meetings[i]; + dsu.union(u, v); + group.add(u); + group.add(v); + } + + group.forEach(node => { + if (dsu.find(node) !== dsu.find(0)) { + dsu.reset(node); + } + }); + } + + return [...Array(n).keys()].filter(i => dsu.find(i) === dsu.find(0)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m \log m + (m * α(n)))$ +* Space complexity: + * $O(n)$ extra space. + * $O(m)$ space depending on the sorting algorithm. + +> Where $m$ is the number of meetings and $n$ is the number of people. \ No newline at end of file diff --git a/articles/find-bottom-left-tree-value.md b/articles/find-bottom-left-tree-value.md new file mode 100644 index 000000000..c04645674 --- /dev/null +++ b/articles/find-bottom-left-tree-value.md @@ -0,0 +1,633 @@ +## 1. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def findBottomLeftValue(self, root: Optional[TreeNode]) -> int: + q = deque([root]) + + while q: + node = q.popleft() + if node.right: + q.append(node.right) + if node.left: + q.append(node.left) + + return node.val +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int findBottomLeftValue(TreeNode root) { + Queue q = new LinkedList<>(); + q.offer(root); + TreeNode node = null; + + while (!q.isEmpty()) { + node = q.poll(); + if (node.right != null) q.offer(node.right); + if (node.left != null) q.offer(node.left); + } + return node.val; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int findBottomLeftValue(TreeNode* root) { + queue q; + q.push(root); + TreeNode* node = nullptr; + + while (!q.empty()) { + node = q.front(); + q.pop(); + if (node->right) q.push(node->right); + if (node->left) q.push(node->left); + } + return node->val; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + findBottomLeftValue(root) { + const q = new Queue([root]); + let node = null; + + while (!q.isEmpty()) { + node = q.pop(); + if (node.right) q.push(node.right); + if (node.left) q.push(node.left); + } + return node.val; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def findBottomLeftValue(self, root: Optional[TreeNode]) -> int: + self.maxDepth, self.res = -1, root.val + + def dfs(node, depth): + if not node: + return + if depth > self.maxDepth: + self.maxDepth, self.res = depth, node.val + + dfs(node.left, depth + 1) + dfs(node.right, depth + 1) + + dfs(root, 0) + return self.res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private int maxDepth = -1; + private int res; + + public int findBottomLeftValue(TreeNode root) { + res = root.val; + dfs(root, 0); + return res; + } + + private void dfs(TreeNode node, int depth) { + if (node == null) return; + if (depth > maxDepth) { + maxDepth = depth; + res = node.val; + } + + dfs(node.left, depth + 1); + dfs(node.right, depth + 1); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int maxDepth = -1, res; + + int findBottomLeftValue(TreeNode* root) { + res = root->val; + dfs(root, 0); + return res; + } + +private: + void dfs(TreeNode* node, int depth) { + if (!node) return; + if (depth > maxDepth) { + maxDepth = depth; + res = node->val; + } + + dfs(node->left, depth + 1); + dfs(node->right, depth + 1); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + findBottomLeftValue(root) { + let maxDepth = -1, res = root.val; + + const dfs = (node, depth) => { + if (!node) return; + if (depth > maxDepth) { + maxDepth = depth; + res = node.val; + } + + dfs(node.left, depth + 1); + dfs(node.right, depth + 1); + }; + + dfs(root, 0); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def findBottomLeftValue(self, root: Optional[TreeNode]) -> int: + res, maxDepth = root.val, -1 + stack = [(root, 0)] + + while stack: + node, depth = stack.pop() + if depth > maxDepth: + maxDepth = depth + res = node.val + + if node.right: + stack.append((node.right, depth + 1)) + if node.left: + stack.append((node.left, depth + 1)) + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int findBottomLeftValue(TreeNode root) { + int res = root.val, maxDepth = -1; + Stack> stack = new Stack<>(); + stack.push(new Pair<>(root, 0)); + + while (!stack.isEmpty()) { + Pair p = stack.pop(); + TreeNode node = p.getKey(); + int depth = p.getValue(); + if (depth > maxDepth) { + maxDepth = depth; + res = node.val; + } + + if (node.right != null) { + stack.push(new Pair<>(node.right, depth + 1)); + } + if (node.left != null) { + stack.push(new Pair<>(node.left, depth + 1)); + } + } + + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int findBottomLeftValue(TreeNode* root) { + int res = root->val, maxDepth = -1; + stack> stack; + stack.push({root, 0}); + + while (!stack.empty()) { + auto [node, depth] = stack.top();stack.pop(); + if (depth > maxDepth) { + maxDepth = depth; + res = node->val; + } + + if (node->right) { + stack.push({node->right, depth + 1}); + } + if (node->left) { + stack.push({node->left, depth + 1}); + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + findBottomLeftValue(root) { + let res = root.val, maxDepth = -1; + const stack = [[root, 0]]; + + while (stack.length) { + const [node, depth] = stack.pop(); + if (depth > maxDepth) { + maxDepth = depth; + res = node.val; + } + + if (node.right) stack.push([node.right, depth + 1]); + if (node.left) stack.push([node.left, depth + 1]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Morris Traversal + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def findBottomLeftValue(self, root: Optional[TreeNode]) -> int: + res, maxDepth, curDepth = root.val, -1, 0 + cur = root + + while cur: + if not cur.left: + if curDepth > maxDepth: + maxDepth, res = curDepth, cur.val + cur = cur.right + curDepth += 1 + else: + prev = cur.left + steps = 1 + while prev.right and prev.right != cur: + prev = prev.right + steps += 1 + + if not prev.right: + prev.right = cur + cur = cur.left + curDepth += 1 + else: + prev.right = None + curDepth -= steps + cur = cur.right + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int findBottomLeftValue(TreeNode root) { + int res = root.val, maxDepth = -1, curDepth = 0; + TreeNode cur = root; + + while (cur != null) { + if (cur.left == null) { + if (curDepth > maxDepth) { + maxDepth = curDepth; + res = cur.val; + } + cur = cur.right; + curDepth++; + } else { + TreeNode prev = cur.left; + int steps = 1; + while (prev.right != null && prev.right != cur) { + prev = prev.right; + steps++; + } + + if (prev.right == null) { + prev.right = cur; + cur = cur.left; + curDepth++; + } else { + prev.right = null; + curDepth -= steps; + cur = cur.right; + } + } + } + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int findBottomLeftValue(TreeNode* root) { + int res = root->val, maxDepth = -1, curDepth = 0; + TreeNode* cur = root; + + while (cur) { + if (!cur->left) { + if (curDepth > maxDepth) { + maxDepth = curDepth; + res = cur->val; + } + cur = cur->right; + curDepth++; + } else { + TreeNode* prev = cur->left; + int steps = 1; + while (prev->right && prev->right != cur) { + prev = prev->right; + steps++; + } + + if (!prev->right) { + prev->right = cur; + cur = cur->left; + curDepth++; + } else { + prev->right = nullptr; + curDepth -= steps; + cur = cur->right; + } + } + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + findBottomLeftValue(root) { + let res = root.val, maxDepth = -1, curDepth = 0; + let cur = root; + + while (cur) { + if (!cur.left) { + if (curDepth > maxDepth) { + maxDepth = curDepth; + res = cur.val; + } + cur = cur.right; + curDepth++; + } else { + let prev = cur.left, steps = 1; + while (prev.right && prev.right !== cur) { + prev = prev.right; + steps++; + } + + if (!prev.right) { + prev.right = cur; + cur = cur.left; + curDepth++; + } else { + prev.right = null; + curDepth -= steps; + cur = cur.right; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/find-closest-node-to-given-two-nodes.md b/articles/find-closest-node-to-given-two-nodes.md new file mode 100644 index 000000000..52cb6d365 --- /dev/null +++ b/articles/find-closest-node-to-given-two-nodes.md @@ -0,0 +1,669 @@ +## 1. Breadth First Search + +::tabs-start + +```python +class Solution: + def closestMeetingNode(self, edges: list[int], node1: int, node2: int) -> int: + adj = defaultdict(list) + for i, nei in enumerate(edges): + adj[i].append(nei) + + def bfs(src, distMap): + q = deque([(src, 0)]) + distMap[src] = 0 + while q: + node, dist = q.popleft() + for nei in adj[node]: + if nei not in distMap: + q.append((nei, dist + 1)) + distMap[nei] = dist + 1 + + node1Dist, node2Dist = {}, {} + bfs(node1, node1Dist) + bfs(node2, node2Dist) + + res, resDist = -1, float("inf") + for i in range(len(edges)): + if i in node1Dist and i in node2Dist: + dist = max(node1Dist[i], node2Dist[i]) + if dist < resDist: + resDist, res = dist, i + + return res +``` + +```java +public class Solution { + public int closestMeetingNode(int[] edges, int node1, int node2) { + int n = edges.length; + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + for (int i = 0; i < n; i++) { + if (edges[i] != -1) adj[i].add(edges[i]); + } + + int[] node1Dist = bfs(node1, n, adj); + int[] node2Dist = bfs(node2, n, adj); + + int res = -1, resDist = Integer.MAX_VALUE; + for (int i = 0; i < n; i++) { + if (node1Dist[i] != -1 && node2Dist[i] != -1) { + int dist = Math.max(node1Dist[i], node2Dist[i]); + if (dist < resDist) { + resDist = dist; + res = i; + } + } + } + return res; + } + + private int[] bfs(int src, int n, List[] adj) { + int[] distMap = new int[n]; + Arrays.fill(distMap, -1); + Queue q = new LinkedList<>(); + q.offer(new int[]{src, 0}); + distMap[src] = 0; + + while (!q.isEmpty()) { + int[] cur = q.poll(); + int node = cur[0], dist = cur[1]; + + for (int nei : adj[node]) { + if (distMap[nei] == -1) { + q.offer(new int[]{nei, dist + 1}); + distMap[nei] = dist + 1; + } + } + } + return distMap; + } +} +``` + +```cpp +class Solution { +public: + int closestMeetingNode(vector& edges, int node1, int node2) { + int n = edges.size(); + vector> adj(n); + for (int i = 0; i < n; i++) { + if (edges[i] != -1) adj[i].push_back(edges[i]); + } + + vector node1Dist = bfs(node1, n, adj); + vector node2Dist = bfs(node2, n, adj); + + int res = -1, resDist = INT_MAX; + for (int i = 0; i < n; i++) { + if (node1Dist[i] != -1 && node2Dist[i] != -1) { + int dist = max(node1Dist[i], node2Dist[i]); + if (dist < resDist) { + resDist = dist; + res = i; + } + } + } + return res; + } + +private: + vector bfs(int src, int n, vector>& adj) { + vector distMap(n, -1); + queue> q; + q.push({src, 0}); + distMap[src] = 0; + + while (!q.empty()) { + auto [node, dist] = q.front(); + q.pop(); + + for (int nei : adj[node]) { + if (distMap[nei] == -1) { + q.push({nei, dist + 1}); + distMap[nei] = dist + 1; + } + } + } + return distMap; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} edges + * @param {number} node1 + * @param {number} node2 + * @return {number} + */ + closestMeetingNode(edges, node1, node2) { + const n = edges.length; + const adj = Array.from({ length: n }, () => []); + for (let i = 0; i < n; i++) { + if (edges[i] !== -1) adj[i].push(edges[i]); + } + + const bfs = (src) => { + const distMap = Array(n).fill(-1); + const q = new Queue([[src, 0]]); + distMap[src] = 0; + + while (!q.isEmpty()) { + const [node, dist] = q.pop(); + for (const nei of adj[node]) { + if (distMap[nei] === -1) { + q.push([nei, dist + 1]); + distMap[nei] = dist + 1; + } + } + } + return distMap; + }; + + const node1Dist = bfs(node1); + const node2Dist = bfs(node2); + + let res = -1, resDist = Infinity; + for (let i = 0; i < n; i++) { + if (node1Dist[i] !== -1 && node2Dist[i] !== -1) { + let dist = Math.max(node1Dist[i], node2Dist[i]); + if (dist < resDist) { + resDist = dist; + res = i; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Breadth First Search (Optimal) + +::tabs-start + +```python +class Solution: + def closestMeetingNode(self, edges: list[int], node1: int, node2: int) -> int: + n = len(edges) + + def bfs(src): + dist = [-1] * n + q = deque([src]) + dist[src] = 0 + + while q: + node = q.popleft() + nei = edges[node] + if nei == -1 or dist[nei] >= 0: + continue + q.append(nei) + dist[nei] = dist[node] + 1 + return dist + + node1Dist, node2Dist = bfs(node1), bfs(node2) + + res, resDist = -1, float("inf") + for i in range(n): + if node1Dist[i] != -1 and node2Dist[i] != -1: + dist = max(node1Dist[i], node2Dist[i]) + if dist < resDist: + resDist, res = dist, i + + return res +``` + +```java +public class Solution { + public int closestMeetingNode(int[] edges, int node1, int node2) { + int n = edges.length; + int[] node1Dist = bfs(node1, edges, n); + int[] node2Dist = bfs(node2, edges, n); + + int res = -1, resDist = Integer.MAX_VALUE; + for (int i = 0; i < n; i++) { + if (node1Dist[i] != -1 && node2Dist[i] != -1) { + int dist = Math.max(node1Dist[i], node2Dist[i]); + if (dist < resDist) { + resDist = dist; + res = i; + } + } + } + + return res; + } + + private int[] bfs(int src, int[] edges, int n) { + int[] dist = new int[n]; + Arrays.fill(dist, -1); + Queue q = new LinkedList<>(); + q.offer(src); + dist[src] = 0; + + while (!q.isEmpty()) { + int node = q.poll(); + int nei = edges[node]; + if (nei == -1 || dist[nei] != -1) { + continue; + } + + q.offer(nei); + dist[nei] = dist[node] + 1; + } + return dist; + } +} +``` + +```cpp +class Solution { +public: + int closestMeetingNode(vector& edges, int node1, int node2) { + int n = edges.size(); + vector node1Dist = bfs(node1, edges, n); + vector node2Dist = bfs(node2, edges, n); + + int res = -1, resDist = INT_MAX; + for (int i = 0; i < n; i++) { + if (node1Dist[i] != -1 && node2Dist[i] != -1) { + int dist = max(node1Dist[i], node2Dist[i]); + if (dist < resDist) { + resDist = dist; + res = i; + } + } + } + return res; + } + +private: + vector bfs(int src, vector& edges, int n) { + vector dist(n, -1); + queue q; + q.push(src); + dist[src] = 0; + + while (!q.empty()) { + int node = q.front(); + q.pop(); + int nei = edges[node]; + if (nei == -1 || dist[nei] != -1) { + continue; + } + + q.push(nei); + dist[nei] = dist[node] + 1; + } + return dist; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} edges + * @param {number} node1 + * @param {number} node2 + * @return {number} + */ + closestMeetingNode(edges, node1, node2) { + const n = edges.length; + + const bfs = (src) => { + const dist = Array(n).fill(-1); + const q = new Queue([src]); + dist[src] = 0; + + while (!q.isEmpty()) { + const node = q.pop(); + const nei = edges[node]; + if (nei === -1 || dist[nei] !== -1) { + continue; + } + + q.push(nei); + dist[nei] = dist[node] + 1; + } + return dist; + }; + + const node1Dist = bfs(node1); + const node2Dist = bfs(node2); + + let res = -1, resDist = Infinity; + for (let i = 0; i < n; i++) { + if (node1Dist[i] !== -1 && node2Dist[i] !== -1) { + let dist = Math.max(node1Dist[i], node2Dist[i]); + if (dist < resDist) { + resDist = dist; + res = i; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Depth First Search + +::tabs-start + +```python +class Solution: + def closestMeetingNode(self, edges: List[int], node1: int, node2: int) -> int: + n = len(edges) + + def dfs(node, dist): + nei = edges[node] + if nei != -1 and dist[nei] == -1: + dist[nei] = dist[node] + 1 + dfs(nei, dist) + + node1Dist = [-1] * n + node2Dist = [-1] * n + node1Dist[node1] = node2Dist[node2] = 0 + + dfs(node1, node1Dist) + dfs(node2, node2Dist) + + res, resDist = -1, float("inf") + for i in range(n): + if min(node1Dist[i], node2Dist[i]) != -1: + dist = max(node1Dist[i], node2Dist[i]) + if dist < resDist: + resDist, res = dist, i + + return res +``` + +```java +public class Solution { + public int closestMeetingNode(int[] edges, int node1, int node2) { + int n = edges.length; + int[] node1Dist = new int[n]; + int[] node2Dist = new int[n]; + Arrays.fill(node1Dist, -1); + Arrays.fill(node2Dist, -1); + node1Dist[node1] = 0; + node2Dist[node2] = 0; + + dfs(node1, edges, node1Dist); + dfs(node2, edges, node2Dist); + + int res = -1, resDist = Integer.MAX_VALUE; + for (int i = 0; i < n; i++) { + if (Math.min(node1Dist[i], node2Dist[i]) != -1) { + int dist = Math.max(node1Dist[i], node2Dist[i]); + if (dist < resDist) { + resDist = dist; + res = i; + } + } + } + return res; + } + + private void dfs(int node, int[] edges, int[] dist) { + int nei = edges[node]; + if (nei != -1 && dist[nei] == -1) { + dist[nei] = dist[node] + 1; + dfs(nei, edges, dist); + } + } +} +``` + +```cpp +class Solution { +public: + int closestMeetingNode(vector& edges, int node1, int node2) { + int n = edges.size(); + vector node1Dist(n, -1), node2Dist(n, -1); + node1Dist[node1] = node2Dist[node2] = 0; + + dfs(node1, edges, node1Dist); + dfs(node2, edges, node2Dist); + + int res = -1, resDist = INT_MAX; + for (int i = 0; i < n; i++) { + if (min(node1Dist[i], node2Dist[i]) != -1) { + int dist = max(node1Dist[i], node2Dist[i]); + if (dist < resDist) { + resDist = dist; + res = i; + } + } + } + return res; + } + +private: + void dfs(int node, vector& edges, vector& dist) { + int nei = edges[node]; + if (nei != -1 && dist[nei] == -1) { + dist[nei] = dist[node] + 1; + dfs(nei, edges, dist); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} edges + * @param {number} node1 + * @param {number} node2 + * @return {number} + */ + closestMeetingNode(edges, node1, node2) { + const n = edges.length; + + const dfs = (node, dist) => { + const nei = edges[node]; + if (nei !== -1 && dist[nei] === -1) { + dist[nei] = dist[node] + 1; + dfs(nei, dist); + } + }; + + const node1Dist = Array(n).fill(-1); + const node2Dist = Array(n).fill(-1); + node1Dist[node1] = 0; + node2Dist[node2] = 0; + + dfs(node1, node1Dist); + dfs(node2, node2Dist); + + let res = -1, resDist = Infinity; + for (let i = 0; i < n; i++) { + if (Math.min(node1Dist[i], node2Dist[i]) !== -1) { + const dist = Math.max(node1Dist[i], node2Dist[i]); + if (dist < resDist) { + resDist = dist; + res = i; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Iterative Depth First Search + +::tabs-start + +```python +class Solution: + def closestMeetingNode(self, edges: list[int], node1: int, node2: int) -> int: + n = len(edges) + + def dfs(node): + dist = [-1] * n + dist[node] = 0 + while edges[node] != -1 and dist[edges[node]] == -1: + nei = edges[node] + dist[nei] = dist[node] + 1 + node = nei + return dist + + + node1Dist, node2Dist = dfs(node1), dfs(node2) + res, resDist = -1, float("inf") + for i in range(n): + if min(node1Dist[i], node2Dist[i]) != -1: + dist = max(node1Dist[i], node2Dist[i]) + if dist < resDist: + resDist, res = dist, i + return res +``` + +```java +public class Solution { + public int closestMeetingNode(int[] edges, int node1, int node2) { + int n = edges.length; + int[] node1Dist = dfs(node1, edges, n); + int[] node2Dist = dfs(node2, edges, n); + + int res = -1, resDist = Integer.MAX_VALUE; + for (int i = 0; i < n; i++) { + if (Math.min(node1Dist[i], node2Dist[i]) != -1) { + int dist = Math.max(node1Dist[i], node2Dist[i]); + if (dist < resDist) { + resDist = dist; + res = i; + } + } + } + return res; + } + + private int[] dfs(int node, int[] edges, int n) { + int[] dist = new int[n]; + Arrays.fill(dist, -1); + dist[node] = 0; + while (edges[node] != -1 && dist[edges[node]] == -1) { + int nei = edges[node]; + dist[nei] = dist[node] + 1; + node = nei; + } + return dist; + } +} +``` + +```cpp +class Solution { +public: + int closestMeetingNode(vector& edges, int node1, int node2) { + int n = edges.size(); + vector node1Dist = dfs(node1, edges, n); + vector node2Dist = dfs(node2, edges, n); + + int res = -1, resDist = INT_MAX; + for (int i = 0; i < n; i++) { + if (min(node1Dist[i], node2Dist[i]) != -1) { + int dist = max(node1Dist[i], node2Dist[i]); + if (dist < resDist) { + resDist = dist; + res = i; + } + } + } + return res; + } + +private: + vector dfs(int node, vector& edges, int n) { + vector dist(n, -1); + dist[node] = 0; + while (edges[node] != -1 && dist[edges[node]] == -1) { + int nei = edges[node]; + dist[nei] = dist[node] + 1; + node = nei; + } + return dist; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} edges + * @param {number} node1 + * @param {number} node2 + * @return {number} + */ + closestMeetingNode(edges, node1, node2) { + const n = edges.length; + + const dfs = (node) => { + const dist = Array(n).fill(-1); + dist[node] = 0; + while (edges[node] !== -1 && dist[edges[node]] === -1) { + const nei = edges[node]; + dist[nei] = dist[node] + 1; + node = nei; + } + return dist; + }; + + const node1Dist = dfs(node1); + const node2Dist = dfs(node2); + + let res = -1, resDist = Infinity; + for (let i = 0; i < n; i++) { + if (Math.min(node1Dist[i], node2Dist[i]) !== -1) { + const dist = Math.max(node1Dist[i], node2Dist[i]); + if (dist < resDist) { + resDist = dist; + res = i; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/find-common-characters.md b/articles/find-common-characters.md new file mode 100644 index 000000000..4a6cfe1e8 --- /dev/null +++ b/articles/find-common-characters.md @@ -0,0 +1,122 @@ +## 1. Frequency Count + +::tabs-start + +```python +class Solution: + def commonChars(self, words: List[str]) -> List[str]: + cnt = Counter(words[0]) + + for w in words: + cur_cnt = Counter(w) + for c in cnt: + cnt[c] = min(cnt[c], cur_cnt[c]) + + res = [] + for c in cnt: + for i in range(cnt[c]): + res.append(c) + + return res +``` + +```java +public class Solution { + public List commonChars(String[] words) { + int[] cnt = new int[26]; + Arrays.fill(cnt, Integer.MAX_VALUE); + + for (String word : words) { + int[] curCnt = new int[26]; + for (char c : word.toCharArray()) { + curCnt[c - 'a']++; + } + + for (int i = 0; i < 26; i++) { + cnt[i] = Math.min(cnt[i], curCnt[i]); + } + } + + List res = new ArrayList<>(); + for (int i = 0; i < 26; i++) { + for (int j = 0; j < cnt[i]; j++) { + res.add(String.valueOf((char) (i + 'a'))); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector commonChars(vector& words) { + vector cnt(26, INT_MAX); + + for (string& word : words) { + vector curCnt(26, 0); + for (char c : word) { + curCnt[c - 'a']++; + } + + for (int i = 0; i < 26; i++) { + cnt[i] = min(cnt[i], curCnt[i]); + } + } + + vector res; + for (int i = 0; i < 26; i++) { + for (int j = 0; j < cnt[i]; j++) { + res.push_back(string(1, i + 'a')); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + commonChars(words) { + const cnt = new Array(26).fill(Infinity); + + for (let word of words) { + const curCnt = new Array(26).fill(0); + for (let c of word) { + curCnt[c.charCodeAt(0) - 97]++; + } + + for (let i = 0; i < 26; i++) { + cnt[i] = Math.min(cnt[i], curCnt[i]); + } + } + + const res = []; + for (let i = 0; i < 26; i++) { + for (let j = 0; j < cnt[i]; j++) { + res.push(String.fromCharCode(i + 97)); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: + * $O(1)$ extra space, since we have at most $26$ different characters. + * $O(n * m)$ space for the output list. + +> Where $n$ is the number of words and $m$ is the length of the longest word. \ No newline at end of file diff --git a/articles/find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.md b/articles/find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.md new file mode 100644 index 000000000..b20fb8a09 --- /dev/null +++ b/articles/find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.md @@ -0,0 +1,1282 @@ +## 1. Kruskal's Algorithm - I + +::tabs-start + +```python +class UnionFind: + def __init__(self, n): + self.par = [i for i in range(n)] + self.rank = [1] * n + + def find(self, v1): + while v1 != self.par[v1]: + self.par[v1] = self.par[self.par[v1]] + v1 = self.par[v1] + return v1 + + def union(self, v1, v2): + p1, p2 = self.find(v1), self.find(v2) + if p1 == p2: + return False + if self.rank[p1] > self.rank[p2]: + self.par[p2] = p1 + self.rank[p1] += self.rank[p2] + else: + self.par[p1] = p2 + self.rank[p2] += self.rank[p1] + return True + + +class Solution: + def findCriticalAndPseudoCriticalEdges(self, n: int, edges: List[List[int]]) -> List[List[int]]: + for i, e in enumerate(edges): + e.append(i) # [v1, v2, weight, original_index] + + edges.sort(key=lambda e: e[2]) + + mst_weight = 0 + uf = UnionFind(n) + for v1, v2, w, i in edges: + if uf.union(v1, v2): + mst_weight += w + + critical, pseudo = [], [] + for n1, n2, e_weight, i in edges: + # Try without curr edge + weight = 0 + uf = UnionFind(n) + for v1, v2, w, j in edges: + if i != j and uf.union(v1, v2): + weight += w + if max(uf.rank) != n or weight > mst_weight: + critical.append(i) + continue + + # Try with curr edge + uf = UnionFind(n) + uf.union(n1, n2) + weight = e_weight + for v1, v2, w, j in edges: + if uf.union(v1, v2): + weight += w + if weight == mst_weight: + pseudo.append(i) + return [critical, pseudo] +``` + +```java +class UnionFind { + int[] par, rank; + + public UnionFind(int n) { + par = new int[n]; + rank = new int[n]; + for (int i = 0; i < n; i++) { + par[i] = i; + rank[i] = 1; + } + } + + public int find(int v) { + if (par[v] != v) { + par[v] = find(par[v]); + } + return par[v]; + } + + public boolean union(int v1, int v2) { + int p1 = find(v1), p2 = find(v2); + if (p1 == p2) return false; + if (rank[p1] > rank[p2]) { + par[p2] = p1; + rank[p1] += rank[p2]; + } else { + par[p1] = p2; + rank[p2] += rank[p1]; + } + return true; + } +} + +public class Solution { + public List> findCriticalAndPseudoCriticalEdges(int n, int[][] edges) { + List edgeList = new ArrayList<>(); + for (int i = 0; i < edges.length; i++) { + edgeList.add(new int[] { edges[i][0], edges[i][1], edges[i][2], i }); + } + + edgeList.sort(Comparator.comparingInt(a -> a[2])); + + int mstWeight = 0; + UnionFind uf = new UnionFind(n); + for (int[] edge : edgeList) { + if (uf.union(edge[0], edge[1])) { + mstWeight += edge[2]; + } + } + + List critical = new ArrayList<>(); + List pseudo = new ArrayList<>(); + + for (int[] edge : edgeList) { + // Try without current edge + UnionFind ufWithout = new UnionFind(n); + int weight = 0; + for (int[] other : edgeList) { + if (other[3] != edge[3] && ufWithout.union(other[0], other[1])) { + weight += other[2]; + } + } + if (Arrays.stream(ufWithout.rank).max().getAsInt() != n || weight > mstWeight) { + critical.add(edge[3]); + continue; + } + + // Try with current edge + UnionFind ufWith = new UnionFind(n); + ufWith.union(edge[0], edge[1]); + weight = edge[2]; + for (int[] other : edgeList) { + if (ufWith.union(other[0], other[1])) { + weight += other[2]; + } + } + if (weight == mstWeight) { + pseudo.add(edge[3]); + } + } + + return Arrays.asList(critical, pseudo); + } +} +``` + +```cpp +class UnionFind { +public: + vector par, rank; + + UnionFind(int n) : par(n), rank(n, 1) { + iota(par.begin(), par.end(), 0); + } + + int find(int v) { + if (v != par[v]) { + par[v] = find(par[v]); + } + return par[v]; + } + + bool unionSets(int v1, int v2) { + int p1 = find(v1), p2 = find(v2); + if (p1 == p2) return false; + if (rank[p1] > rank[p2]) { + par[p2] = p1; + rank[p1] += rank[p2]; + } else { + par[p1] = p2; + rank[p2] += rank[p1]; + } + return true; + } +}; + +class Solution { +public: + vector> findCriticalAndPseudoCriticalEdges(int n, vector>& edges) { + vector> edgeList; + for (int i = 0; i < edges.size(); ++i) { + edgeList.push_back({ edges[i][0], edges[i][1], edges[i][2], i }); + } + + sort(edgeList.begin(), edgeList.end(), [](auto& a, auto& b) { + return a[2] < b[2]; + }); + + int mstWeight = 0; + UnionFind uf(n); + for (auto& edge : edgeList) { + if (uf.unionSets(edge[0], edge[1])) { + mstWeight += edge[2]; + } + } + + vector critical, pseudo; + for (auto& edge : edgeList) { + // Try without current edge + UnionFind ufWithout(n); + int weight = 0; + for (auto& other : edgeList) { + if (other[3] != edge[3] && ufWithout.unionSets(other[0], other[1])) { + weight += other[2]; + } + } + if (*max_element(ufWithout.rank.begin(), ufWithout.rank.end()) != n || weight > mstWeight) { + critical.push_back(edge[3]); + continue; + } + + // Try with current edge + UnionFind ufWith(n); + ufWith.unionSets(edge[0], edge[1]); + weight = edge[2]; + for (auto& other : edgeList) { + if (ufWith.unionSets(other[0], other[1])) { + weight += other[2]; + } + } + if (weight == mstWeight) { + pseudo.push_back(edge[3]); + } + } + + return { critical, pseudo }; + } +}; +``` + +```javascript +class UnionFind { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.par = Array.from({ length: n }, (_, i) => i); + this.rank = Array(n).fill(1); + } + + /** + * @param {number} v1 + * @return {number} + */ + find(v1) { + if (this.par[v1] !== v1) { + this.par[v1] = this.find(this.par[v1]); + } + return this.par[v1]; + } + + /** + * @param {number} v1 + * @param {number} v2 + * @return {boolean} + */ + union(v1, v2) { + const p1 = this.find(v1), p2 = this.find(v2); + if (p1 === p2) return false; + if (this.rank[p1] > this.rank[p2]) { + this.par[p2] = p1; + this.rank[p1] += this.rank[p2]; + } else { + this.par[p1] = p2; + this.rank[p2] += this.rank[p1]; + } + return true; + } +} + +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[][]} + */ + findCriticalAndPseudoCriticalEdges(n, edges) { + edges = edges.map((edge, idx) => [...edge, idx]); + edges.sort((a, b) => a[2] - b[2]); + + const uf = new UnionFind(n); + let mstWeight = 0; + for (const [v1, v2, w] of edges) { + if (uf.union(v1, v2)) { + mstWeight += w; + } + } + + const critical = []; + const pseudo = []; + + for (const [n1, n2, weight, i] of edges) { + // Try without current edge + const ufWithout = new UnionFind(n); + let weightWithout = 0; + for (const [v1, v2, w, j] of edges) { + if (j !== i && ufWithout.union(v1, v2)) { + weightWithout += w; + } + } + if (Math.max(...ufWithout.rank) !== n || weightWithout > mstWeight) { + critical.push(i); + continue; + } + + // Try with current edge + const ufWith = new UnionFind(n); + ufWith.union(n1, n2); + let weightWith = weight; + for (const [v1, v2, w, j] of edges) { + if (ufWith.union(v1, v2)) { + weightWith += w; + } + } + if (weightWith === mstWeight) { + pseudo.push(i); + } + } + + return [critical, pseudo]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(E ^ 2)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 2. Kruskal's Algorithm - II + +::tabs-start + +```python +class UnionFind: + def __init__(self, n): + self.n = n + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu = self.find(u) + pv = self.find(v) + if pu == pv: + return False + self.n -= 1 + if self.Size[pu] < self.Size[pv]: + pu, pv = pv, pu + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + return True + + def isConnected(self): + return self.n == 1 + +class Solution: + def findCriticalAndPseudoCriticalEdges(self, n: int, edges: List[List[int]]) -> List[List[int]]: + for i, e in enumerate(edges): + e.append(i) + edges.sort(key = lambda e : e[2]) + + def findMST(index, include): + uf = UnionFind(n) + wgt = 0 + if include: + wgt += edges[index][2] + uf.union(edges[index][0], edges[index][1]) + + for i, e in enumerate(edges): + if i == index: + continue + if uf.union(e[0], e[1]): + wgt += e[2] + return wgt if uf.isConnected() else float("inf") + + mst_wgt = findMST(-1, False) + critical, pseudo = [], [] + for i, e in enumerate(edges): + if mst_wgt < findMST(i, False): + critical.append(e[3]) + elif mst_wgt == findMST(i, True): + pseudo.append(e[3]) + + return [critical, pseudo] +``` + +```java +class UnionFind { + private int n; + private int[] Parent, Size; + + public UnionFind(int n) { + this.n = n; + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + public int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) { + return false; + } + n--; + if (Size[pu] < Size[pv]) { + int temp = pu; + pu = pv; + pv = temp; + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } + + public boolean isConnected() { + return n == 1; + } +} + +public class Solution { + public List> findCriticalAndPseudoCriticalEdges(int n, int[][] edges) { + for (int i = 0; i < edges.length; i++) { + edges[i] = Arrays.copyOf(edges[i], edges[i].length + 1); + edges[i][3] = i; + } + + Arrays.sort(edges, Comparator.comparingInt(a -> a[2])); + + + int mst_wgt = findMST(n, edges, -1, false); + List critical = new ArrayList<>(); + List pseudo = new ArrayList<>(); + + for (int i = 0; i < edges.length; i++) { + if (mst_wgt < findMST(n, edges, i, false)) { + critical.add(edges[i][3]); + } else if (mst_wgt == findMST(n, edges, i, true)) { + pseudo.add(edges[i][3]); + } + } + + return Arrays.asList(critical, pseudo); + } + + public int findMST(int n, int[][] edges, int index, boolean include) { + UnionFind uf = new UnionFind(n); + int wgt = 0; + if (include) { + wgt += edges[index][2]; + uf.union(edges[index][0], edges[index][1]); + } + for (int i = 0; i < edges.length; i++) { + if (i == index) { + continue; + } + if (uf.union(edges[i][0], edges[i][1])) { + wgt += edges[i][2]; + } + } + return uf.isConnected() ? wgt : Integer.MAX_VALUE; + } +} +``` + +```cpp +class UnionFind { +private: + int n; + vector Parent, Size; + +public: + UnionFind(int n) : n(n), Parent(n + 1), Size(n + 1, 1) { + for (int i = 0; i <= n; ++i) { + Parent[i] = i; + } + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + bool unionSets(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + n--; + if (Size[pu] < Size[pv]) { + swap(pu, pv); + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } + + bool isConnected() { + return n == 1; + } +}; + +class Solution { +public: + vector> findCriticalAndPseudoCriticalEdges(int n, vector>& edges) { + for (int i = 0; i < edges.size(); ++i) { + edges[i].push_back(i); + } + + sort(edges.begin(), edges.end(), [](const vector& a, const vector& b) { + return a[2] < b[2]; + }); + + auto findMST = [&](int index, bool include) -> int { + UnionFind uf(n); + int wgt = 0; + if (include) { + wgt += edges[index][2]; + uf.unionSets(edges[index][0], edges[index][1]); + } + for (int i = 0; i < edges.size(); ++i) { + if (i == index) continue; + if (uf.unionSets(edges[i][0], edges[i][1])) { + wgt += edges[i][2]; + } + } + return uf.isConnected() ? wgt : INT_MAX; + }; + + int mst_wgt = findMST(-1, false); + vector critical, pseudo; + + for (int i = 0; i < edges.size(); ++i) { + if (mst_wgt < findMST(i, false)) { + critical.push_back(edges[i][3]); + } else if (mst_wgt == findMST(i, true)) { + pseudo.push_back(edges[i][3]); + } + } + + return { critical, pseudo }; + } +}; +``` + +```javascript +class UnionFind { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.Parent = Array.from({ length: n + 1 }, (_, i) => i); + this.Size = Array(n + 1).fill(1); + this.n = n; + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.Parent[node] !== node) { + this.Parent[node] = this.find(this.Parent[node]); + } + return this.Parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u); + let pv = this.find(v); + if (pu === pv) return false; + this.n--; + if (this.Size[pu] < this.Size[pv]) { + [pu, pv] = [pv, pu]; + } + this.Size[pu] += this.Size[pv]; + this.Parent[pv] = pu; + return true; + } + + /** + * @return {number} + */ + isConnected() { + return this.n === 1; + } +} + +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[][]} + */ + findCriticalAndPseudoCriticalEdges(n, edges) { + edges.forEach((edge, i) => edge.push(i)); + edges.sort((a, b) => a[2] - b[2]); + + const findMST = (index, include) => { + const uf = new UnionFind(n); + let wgt = 0; + if (include) { + wgt += edges[index][2]; + uf.union(edges[index][0], edges[index][1]); + } + for (let i = 0; i < edges.length; i++) { + if (i === index) continue; + if (uf.union(edges[i][0], edges[i][1])) { + wgt += edges[i][2]; + } + } + return uf.isConnected() ? wgt : Infinity; + }; + + const mst_wgt = findMST(-1, false); + const critical = []; + const pseudo = []; + + edges.forEach((edge, i) => { + if (mst_wgt < findMST(i, false)) { + critical.push(edge[3]); + } else if (mst_wgt === findMST(i, true)) { + pseudo.push(edge[3]); + } + }); + + return [critical, pseudo]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(E ^ 2)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 3. Dijkstra's Algorithm + +::tabs-start + +```python +class Solution: + def findCriticalAndPseudoCriticalEdges(self, n: int, edges: List[List[int]]) -> List[List[int]]: + for i, edge in enumerate(edges): + edge.append(i) + + adj = defaultdict(list) + for u, v, w, idx in edges: + adj[u].append((v, w, idx)) + adj[v].append((u, w, idx)) + + def minimax(src, dst, exclude_idx): + dist = [float('inf')] * n + dist[src] = 0 + pq = [(0, src)] + + while pq: + max_w, u = heappop(pq) + if u == dst: + return max_w + + for v, weight, edge_idx in adj[u]: + if edge_idx == exclude_idx: + continue + new_w = max(max_w, weight) + if new_w < dist[v]: + dist[v] = new_w + heappush(pq, (new_w, v)) + + return float('inf') + + critical, pseudo = [], [] + for i, (u, v, w, idx) in enumerate(edges): + if w < minimax(u, v, idx): + critical.append(idx) + elif w == minimax(u, v, -1): + pseudo.append(idx) + + return [critical, pseudo] +``` + +```java +public class Solution { + private List[] adj; + + public List> findCriticalAndPseudoCriticalEdges(int n, int[][] edges) { + for (int i = 0; i < edges.length; i++) { + edges[i] = Arrays.copyOf(edges[i], edges[i].length + 1); + edges[i][3] = i; + } + + adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (int[] edge : edges) { + adj[edge[0]].add(new int[]{edge[1], edge[2], edge[3]}); + adj[edge[1]].add(new int[]{edge[0], edge[2], edge[3]}); + } + + + List critical = new ArrayList<>(); + List pseudo = new ArrayList<>(); + + for (int[] edge : edges) { + int u = edge[0], v = edge[1], w = edge[2], idx = edge[3]; + if (w < minimax(n, u, v, idx)) { + critical.add(idx); + } else if (w == minimax(n, u, v, -1)) { + pseudo.add(idx); + } + } + + return Arrays.asList(critical, pseudo); + } + + public int minimax(int n, int src, int dst, int excludeIdx) { + int[] dist = new int[n]; + Arrays.fill(dist, Integer.MAX_VALUE); + dist[src] = 0; + + PriorityQueue pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[0])); + pq.offer(new int[]{0, src}); + + while (!pq.isEmpty()) { + int[] curr = pq.poll(); + int maxW = curr[0], u = curr[1]; + if (u == dst) return maxW; + + for (int[] neighbor : adj[u]) { + int v = neighbor[0], weight = neighbor[1], edgeIdx = neighbor[2]; + if (edgeIdx == excludeIdx) continue; + int newW = Math.max(maxW, weight); + if (newW < dist[v]) { + dist[v] = newW; + pq.offer(new int[]{newW, v}); + } + } + } + return Integer.MAX_VALUE; + } +} +``` + +```cpp +class Solution { +public: + vector> findCriticalAndPseudoCriticalEdges(int n, vector>& edges) { + for (int i = 0; i < edges.size(); ++i) { + edges[i].push_back(i); + } + + vector>> adj(n); + for (const auto& edge : edges) { + adj[edge[0]].push_back({edge[1], edge[2], edge[3]}); + adj[edge[1]].push_back({edge[0], edge[2], edge[3]}); + } + + auto minimax = [&](int src, int dst, int excludeIdx) -> int { + vector dist(n, INT_MAX); + dist[src] = 0; + + priority_queue, vector>, greater<>> pq; + pq.push({0, src}); + + while (!pq.empty()) { + auto [maxW, u] = pq.top(); + pq.pop(); + if (u == dst) return maxW; + + for (const auto& neighbor : adj[u]) { + int v = neighbor[0], weight = neighbor[1], edgeIdx = neighbor[2]; + if (edgeIdx == excludeIdx) continue; + int newW = max(maxW, weight); + if (newW < dist[v]) { + dist[v] = newW; + pq.push({newW, v}); + } + } + } + return INT_MAX; + }; + + vector critical, pseudo; + for (const auto& edge : edges) { + int u = edge[0], v = edge[1], w = edge[2], idx = edge[3]; + if (w < minimax(u, v, idx)) { + critical.push_back(idx); + } else if (w == minimax(u, v, -1)) { + pseudo.push_back(idx); + } + } + + return {critical, pseudo}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[][]} + */ + findCriticalAndPseudoCriticalEdges(n, edges) { + edges.forEach((edge, i) => edge.push(i)); + + const adj = Array.from({ length: n }, () => []); + for (const [u, v, w, idx] of edges) { + adj[u].push([v, w, idx]); + adj[v].push([u, w, idx]); + } + + const minimax = (src, dst, excludeIdx) => { + const dist = Array(n).fill(Infinity); + dist[src] = 0; + + const pq = new MinPriorityQueue({ compare: (a, b) => a[0] - b[0] }); + pq.enqueue([0, src]); + + while (!pq.isEmpty()) { + const [maxW, u] = pq.dequeue(); + if (u === dst) return maxW; + + for (const [v, weight, edgeIdx] of adj[u]) { + if (edgeIdx === excludeIdx) continue; + const newW = Math.max(maxW, weight); + if (newW < dist[v]) { + dist[v] = newW; + pq.enqueue([newW, v]); + } + } + } + return Infinity; + }; + + const critical = []; + const pseudo = []; + + for (const [u, v, w, idx] of edges) { + if (w < minimax(u, v, idx)) { + critical.push(idx); + } else if (w === minimax(u, v, -1)) { + pseudo.push(idx); + } + } + + return [critical, pseudo]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(E ^ 2 \log V)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 4. Kruskal's Algorithm + DFS + +::tabs-start + +```python +class UnionFind: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu = self.find(u) + pv = self.find(v) + if pu == pv: + return False + if self.Size[pu] < self.Size[pv]: + pu, pv = pv, pu + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + return True + +class Solution: + def findCriticalAndPseudoCriticalEdges(self, n: int, edges: List[List[int]]) -> List[List[int]]: + mst = [[] for _ in range(n)] + mstEdge = [] + + edge_list = [(w, u, v, i) for i, (u, v, w) in enumerate(edges)] + edge_list.sort() + + uf = UnionFind(n) + for w, u, v, i in edge_list: + if uf.union(u, v): + mst[u].append((v, i)) + mst[v].append((u, i)) + mstEdge.append(i) + + def dfs(node): + for next, ind in mst[node]: + if path and ind == path[-1]: + continue + path.append(ind) + if next == dst or dfs(next): + return True + path.pop() + return False + + pseudo, mstEdge = set(), set(mstEdge) + for ind in range(len(edges)): + if ind in mstEdge: + continue + path, dst = [], edges[ind][1] + dfs(edges[ind][0]) + for i in path: + if edges[i][2] == edges[ind][2]: + pseudo.add(i) + pseudo.add(ind) + + return [list(mstEdge - pseudo), list(pseudo)] +``` + +```java +class UnionFind { + private int[] parent; + private int[] size; + + public UnionFind(int n) { + parent = new int[n]; + size = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + size[i] = 1; + } + } + + public int find(int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u); + int pv = find(v); + if (pu == pv) { + return false; + } + if (size[pu] < size[pv]) { + int temp = pu; + pu = pv; + pv = temp; + } + size[pu] += size[pv]; + parent[pv] = pu; + return true; + } +} + +public class Solution { + private List> mst; + private Set mstEdges; + private Set pseudoCriticalEdges; + private int destination; + private List path; + + public List> findCriticalAndPseudoCriticalEdges(int n, int[][] edges) { + mst = new ArrayList<>(); + mstEdges = new HashSet<>(); + pseudoCriticalEdges = new HashSet<>(); + + for (int i = 0; i < n; i++) { + mst.add(new ArrayList<>()); + } + + List edgeList = new ArrayList<>(); + for (int i = 0; i < edges.length; i++) { + edgeList.add(new int[]{edges[i][2], edges[i][0], edges[i][1], i}); + } + edgeList.sort(Comparator.comparingInt(a -> a[0])); + + UnionFind uf = new UnionFind(n); + for (int[] edge : edgeList) { + int weight = edge[0], u = edge[1], v = edge[2], index = edge[3]; + if (uf.union(u, v)) { + mst.get(u).add(index); + mst.get(v).add(index); + mstEdges.add(index); + } + } + + for (int i = 0; i < edges.length; i++) { + if (mstEdges.contains(i)) { + continue; + } + path = new ArrayList<>(); + destination = edges[i][1]; + if (dfs(edges[i][0], -1, edges)) { + for (int p : path) { + if (edges[p][2] == edges[i][2]) { + pseudoCriticalEdges.add(i); + pseudoCriticalEdges.add(p); + } + } + } + } + + List critical = new ArrayList<>(); + for (int edge : mstEdges) { + if (!pseudoCriticalEdges.contains(edge)) { + critical.add(edge); + } + } + + return Arrays.asList(critical, new ArrayList<>(pseudoCriticalEdges)); + } + + private boolean dfs(int node, int parent, int[][] edges) { + if (node == destination) { + return true; + } + for (int edgeIndex : mst.get(node)) { + if (edgeIndex == parent) { + continue; + } + path.add(edgeIndex); + int nextNode = edges[edgeIndex][0] == node ? edges[edgeIndex][1] : edges[edgeIndex][0]; + if (dfs(nextNode, edgeIndex, edges)) { + return true; + } + path.remove(path.size() - 1); + } + return false; + } +} +``` + +```cpp +class UnionFind { + vector parent, size; + +public: + UnionFind(int n) { + parent.resize(n); + size.resize(n, 1); + for (int i = 0; i < n; ++i) { + parent[i] = i; + } + } + + int find(int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + bool unionSets(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (size[pu] < size[pv]) swap(pu, pv); + size[pu] += size[pv]; + parent[pv] = pu; + return true; + } +}; + +class Solution { + vector> mst; + set mstEdges, pseudoCriticalEdges; + vector path; + int destination; + +public: + vector> findCriticalAndPseudoCriticalEdges(int n, vector>& edges) { + mst.resize(n); + vector> edgeList; + for (int i = 0; i < edges.size(); ++i) { + edgeList.push_back({edges[i][2], edges[i][0], edges[i][1], i}); + } + sort(edgeList.begin(), edgeList.end()); + + UnionFind uf(n); + for (auto& [w, u, v, idx] : edgeList) { + if (uf.unionSets(u, v)) { + mst[u].push_back(idx); + mst[v].push_back(idx); + mstEdges.insert(idx); + } + } + + for (int i = 0; i < edges.size(); ++i) { + if (mstEdges.count(i)) continue; + path.clear(); + destination = edges[i][1]; + if (dfs(edges[i][0], -1, edges)) { + for (int p : path) { + if (edges[p][2] == edges[i][2]) { + pseudoCriticalEdges.insert(p); + pseudoCriticalEdges.insert(i); + } + } + } + } + + vector critical; + for (int e : mstEdges) { + if (!pseudoCriticalEdges.count(e)) critical.push_back(e); + } + + return {critical, vector(pseudoCriticalEdges.begin(), pseudoCriticalEdges.end())}; + } + + bool dfs(int node, int parent, vector>& edges) { + if (node == destination) return true; + for (int& edgeIdx : mst[node]) { + if (edgeIdx == parent) continue; + path.push_back(edgeIdx); + int next = edges[edgeIdx][0] == node ? edges[edgeIdx][1] : edges[edgeIdx][0]; + if (dfs(next, edgeIdx, edges)) return true; + path.pop_back(); + } + return false; + } +}; +``` + +```javascript +class UnionFind { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.Parent = Array.from({ length: n + 1 }, (_, i) => i); + this.Size = Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.Parent[node] !== node) { + this.Parent[node] = this.find(this.Parent[node]); + } + return this.Parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u); + let pv = this.find(v); + if (pu === pv) return false; + if (this.Size[pu] < this.Size[pv]) { + [pu, pv] = [pv, pu]; + } + this.Size[pu] += this.Size[pv]; + this.Parent[pv] = pu; + return true; + } +} + +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[][]} + */ + findCriticalAndPseudoCriticalEdges(n, edges) { + const mst = Array.from({ length: n }, () => []); + const mstEdges = new Set(); + const pseudoCriticalEdges = new Set(); + const edgeList = edges.map((e, i) => [...e, i]).sort((a, b) => a[2] - b[2]); + + const uf = new UnionFind(n); + + for (const [u, v, w, idx] of edgeList) { + if (uf.union(u, v)) { + mst[u].push([v, idx]); + mst[v].push([u, idx]); + mstEdges.add(idx); + } + } + + let path = []; + let destination = null; + + const dfs = (node, parent) => { + if (node === destination) return true; + for (const [next, edgeIdx] of mst[node]) { + if (edgeIdx === parent) continue; + path.push(edgeIdx); + if (dfs(next, edgeIdx)) return true; + path.pop(); + } + return false; + }; + + for (let i = 0; i < edges.length; i++) { + if (mstEdges.has(i)) continue; + + path = []; + destination = edges[i][1]; + if (dfs(edges[i][0], -1)) { + for (const edgeIdx of path) { + if (edges[edgeIdx][2] === edges[i][2]) { + pseudoCriticalEdges.add(i); + pseudoCriticalEdges.add(edgeIdx); + } + } + } + } + + const critical = [...mstEdges].filter(edgeIdx => !pseudoCriticalEdges.has(edgeIdx)); + const pseudo = [...pseudoCriticalEdges]; + + return [critical, pseudo]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(E ^ 2)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. \ No newline at end of file diff --git a/articles/find-duplicate-integer.md b/articles/find-duplicate-integer.md new file mode 100644 index 000000000..ef3317436 --- /dev/null +++ b/articles/find-duplicate-integer.md @@ -0,0 +1,1197 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def findDuplicate(self, nums: List[int]) -> int: + nums.sort() + for i in range(len(nums) - 1): + if nums[i] == nums[i + 1]: + return nums[i] + return -1 +``` + +```java +public class Solution { + public int findDuplicate(int[] nums) { + Arrays.sort(nums); + for (int i = 0; i < nums.length - 1; i++) { + if (nums[i] == nums[i + 1]) { + return nums[i]; + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int findDuplicate(std::vector& nums) { + sort(nums.begin(), nums.end()); + for (int i = 0; i < nums.size() - 1; i++) { + if (nums[i] == nums[i + 1]) { + return nums[i]; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findDuplicate(nums) { + nums.sort((a, b) => a - b); + for (let i = 0; i < nums.length - 1; i++) { + if (nums[i] === nums[i + 1]) { + return nums[i]; + } + } + return -1; + } +} +``` + +```csharp +public class Solution { + public int FindDuplicate(int[] nums) { + Array.Sort(nums); + for (int i = 0; i < nums.Length - 1; i++) { + if (nums[i] == nums[i + 1]) { + return nums[i]; + } + } + return -1; + } +} +``` + +```go +func findDuplicate(nums []int) int { + sort.Ints(nums) + for i := 0; i < len(nums)-1; i++ { + if nums[i] == nums[i+1] { + return nums[i] + } + } + return -1 +} +``` + +```kotlin +class Solution { + fun findDuplicate(nums: IntArray): Int { + nums.sort() + for (i in 0 until nums.size - 1) { + if (nums[i] == nums[i + 1]) { + return nums[i] + } + } + return -1 + } +} +``` + +```swift +class Solution { + func findDuplicate(_ nums: [Int]) -> Int { + var nums = nums.sorted() + for i in 0.. int: + seen = set() + for num in nums: + if num in seen: + return num + seen.add(num) + return -1 +``` + +```java +public class Solution { + public int findDuplicate(int[] nums) { + Set seen = new HashSet<>(); + for (int num : nums) { + if (seen.contains(num)) { + return num; + } + seen.add(num); + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int findDuplicate(std::vector& nums) { + unordered_set seen; + for (int num : nums) { + if (seen.find(num) != seen.end()) { + return num; + } + seen.insert(num); + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findDuplicate(nums) { + let seen = new Set(); + for (let num of nums) { + if (seen.has(num)) { + return num; + } + seen.add(num); + } + return -1; + } +} +``` + +```csharp +public class Solution { + public int FindDuplicate(int[] nums) { + HashSet seen = new HashSet(); + foreach (int num in nums) { + if (seen.Contains(num)) { + return num; + } + seen.Add(num); + } + return -1; + } +} +``` + +```go +func findDuplicate(nums []int) int { + seen := make(map[int]struct{}) + for _, num := range nums { + if _, exists := seen[num]; exists { + return num + } + seen[num] = struct{}{} + } + return -1 +} +``` + +```kotlin +class Solution { + fun findDuplicate(nums: IntArray): Int { + val seen = HashSet() + for (num in nums) { + if (num in seen) { + return num + } + seen.add(num) + } + return -1 + } +} +``` + +```swift +class Solution { + func findDuplicate(_ nums: [Int]) -> Int { + var seen = Set() + for num in nums { + if seen.contains(num) { + return num + } + seen.insert(num) + } + return -1 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Array + +::tabs-start + +```python +class Solution: + def findDuplicate(self, nums: List[int]) -> int: + seen = [0] * len(nums) + for num in nums: + if seen[num - 1]: + return num + seen[num - 1] = 1 + return -1 +``` + +```java +public class Solution { + public int findDuplicate(int[] nums) { + int[] seen = new int[nums.length]; + for (int num : nums) { + if (seen[num - 1] == 1) { + return num; + } + seen[num - 1] = 1; + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int findDuplicate(vector& nums) { + vector seen(nums.size(), 0); + for (int num : nums) { + if (seen[num - 1] == 1) { + return num; + } + seen[num - 1] = 1; + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findDuplicate(nums) { + let seen = new Array(nums.length).fill(0); + for (let num of nums) { + if (seen[num - 1]) { + return num; + } + seen[num - 1] = 1; + } + return -1; + } +} +``` + +```csharp +public class Solution { + public int FindDuplicate(int[] nums) { + int[] seen = new int[nums.Length]; + foreach (int num in nums) { + if (seen[num - 1] == 1) { + return num; + } + seen[num - 1] = 1; + } + return -1; + } +} +``` + +```go +func findDuplicate(nums []int) int { + seen := make([]int, len(nums)) + for _, num := range nums { + if seen[num-1] == 1 { + return num + } + seen[num-1] = 1 + } + return -1 +} +``` + +```kotlin +class Solution { + fun findDuplicate(nums: IntArray): Int { + val seen = IntArray(nums.size) + for (num in nums) { + if (seen[num - 1] == 1) { + return num + } + seen[num - 1] = 1 + } + return -1 + } +} +``` + +```swift +class Solution { + func findDuplicate(_ nums: [Int]) -> Int { + var seen = [Int](repeating: 0, count: nums.count) + for num in nums { + if seen[num - 1] == 1 { + return num + } + seen[num - 1] = 1 + } + return -1 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Negative Marking + +::tabs-start + +```python +class Solution: + def findDuplicate(self, nums: List[int]) -> int: + for num in nums : + idx = abs(num) - 1 + if nums[idx] < 0 : + return abs(num) + nums[idx] *= -1 + return -1 +``` + +```java +public class Solution { + public int findDuplicate(int[] nums) { + for (int num : nums) { + int idx = Math.abs(num) - 1; + if (nums[idx] < 0) { + return Math.abs(num); + } + nums[idx] *= -1; + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int findDuplicate(vector& nums) { + for (int num : nums) { + int idx = abs(num) - 1; + if (nums[idx] < 0) { + return abs(num); + } + nums[idx] *= -1; + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findDuplicate(nums) { + for (let num of nums) { + let idx = Math.abs(num) - 1; + if (nums[idx] < 0) { + return Math.abs(num); + } + nums[idx] *= -1; + } + return -1; + } +} +``` + +```csharp +public class Solution { + public int FindDuplicate(int[] nums) { + foreach (int num in nums) { + int idx = Math.Abs(num) - 1; + if (nums[idx] < 0) { + return Math.Abs(num); + } + nums[idx] *= -1; + } + return -1; + } +} +``` + +```go +func findDuplicate(nums []int) int { + for _, num := range nums { + idx := abs(num) - 1 + if nums[idx] < 0 { + return abs(num) + } + nums[idx] *= -1 + } + return -1 +} + +func abs(num int) int { + if num < 0 { + return -num + } + return num +} +``` + +```kotlin +class Solution { + fun findDuplicate(nums: IntArray): Int { + for (num in nums) { + val idx = Math.abs(num) - 1 + if (nums[idx] < 0) { + return Math.abs(num) + } + nums[idx] *= -1 + } + return -1 + } +} +``` + +```swift +class Solution { + func findDuplicate(_ nums: [Int]) -> Int { + var nums = nums + for num in nums { + let idx = abs(num) - 1 + if nums[idx] < 0 { + return abs(num) + } + nums[idx] *= -1 + } + return -1 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Binary Search + +::tabs-start + +```python +class Solution: + def findDuplicate(self, nums: List[int]) -> int: + n = len(nums) + low, high = 1, n - 1 + while low < high: + mid = low + (high - low) // 2 + lessOrEqual = sum(1 for num in nums if num <= mid) + + if lessOrEqual <= mid: + low = mid + 1 + else: + high = mid + + return low +``` + +```java +public class Solution { + public int findDuplicate(int[] nums) { + int n = nums.length; + int low = 1; + int high = n - 1; + while (low < high) { + int mid = low + (high - low) / 2; + int lessOrEqual = 0; + for (int i = 0; i < n; i++) { + if (nums[i] <= mid) { + lessOrEqual++; + } + } + + if (lessOrEqual <= mid) { + low = mid + 1; + } else { + high = mid; + } + } + + return low; + } +} +``` + +```cpp +class Solution { +public: + int findDuplicate(vector& nums) { + int n = nums.size(); + int low = 1, high = n - 1; + while (low < high) { + int mid = low + (high - low) / 2; + int lessOrEqual = 0; + for (int i = 0; i < n; i++) { + if (nums[i] <= mid) { + lessOrEqual++; + } + } + + if (lessOrEqual <= mid) { + low = mid + 1; + } else { + high = mid; + } + } + + return low; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findDuplicate(nums) { + let n = nums.length; + let low = 1, high = n - 1; + + while (low < high) { + let mid = Math.floor(low + (high - low) / 2); + let lessOrEqual = 0; + + for (let i = 0; i < n; i++) { + if (nums[i] <= mid) { + lessOrEqual++; + } + } + + if (lessOrEqual <= mid) { + low = mid + 1; + } else { + high = mid; + } + } + + return low; + } +} +``` + +```csharp +public class Solution { + public int FindDuplicate(int[] nums) { + int n = nums.Length; + int low = 1, high = n - 1; + + while (low < high) { + int mid = low + (high - low) / 2; + int lessOrEqual = 0; + + for (int i = 0; i < n; i++) { + if (nums[i] <= mid) { + lessOrEqual++; + } + } + + if (lessOrEqual <= mid) { + low = mid + 1; + } else { + high = mid; + } + } + + return low; + } +} +``` + +```go +func findDuplicate(nums []int) int { + n := len(nums) + low, high := 1, n-1 + + for low < high { + mid := low + (high-low)/2 + lessOrEqual := 0 + + for _, num := range nums { + if num <= mid { + lessOrEqual++ + } + } + + if lessOrEqual <= mid { + low = mid + 1 + } else { + high = mid + } + } + + return low +} +``` + +```kotlin +class Solution { + fun findDuplicate(nums: IntArray): Int { + val n = nums.size + var low = 1 + var high = n - 1 + + while (low < high) { + val mid = low + (high - low) / 2 + var lessOrEqual = 0 + + for (num in nums) { + if (num <= mid) { + lessOrEqual++ + } + } + + if (lessOrEqual <= mid) { + low = mid + 1 + } else { + high = mid + } + } + + return low + } +} +``` + +```swift +class Solution { + func findDuplicate(_ nums: [Int]) -> Int { + let n = nums.count + var low = 1, high = n - 1 + + while low < high { + let mid = low + (high - low) / 2 + let lessOrEqual = nums.filter { $0 <= mid }.count + + if lessOrEqual <= mid { + low = mid + 1 + } else { + high = mid + } + } + return low + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ + +--- + +## 6. Bit Manipulation + +::tabs-start + +```python +class Solution: + def findDuplicate(self, nums: List[int]) -> int: + n = len(nums) + res = 0 + for b in range(32): + x = y = 0 + mask = 1 << b + for num in nums: + if num & mask: + x += 1 + + for num in range(1, n): + if num & mask: + y += 1 + + if x > y: + res |= mask + return res +``` + +```java +public class Solution { + public int findDuplicate(int[] nums) { + int n = nums.length; + int res = 0; + for (int b = 0; b < 32; b++) { + int x = 0, y = 0; + int mask = 1 << b; + for (int num : nums) { + if ((num & mask) != 0) { + x++; + } + } + for (int num = 1; num < n; num++) { + if ((num & mask) != 0) { + y++; + } + } + if (x > y) { + res |= mask; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int findDuplicate(vector& nums) { + int n = nums.size(); + int res = 0; + for (int b = 0; b < 32; b++) { + int x = 0, y = 0; + int mask = 1 << b; + for (int num : nums) { + if (num & mask) { + x++; + } + } + for (int num = 1; num < n; num++) { + if (num & mask) { + y++; + } + } + if (x > y) { + res |= mask; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findDuplicate(nums) { + let n = nums.length; + let res = 0; + for (let b = 0; b < 32; b++) { + let x = 0, y = 0; + let mask = 1 << b; + for (let num of nums) { + if (num & mask) { + x++; + } + } + for (let num = 1; num < n; num++) { + if (num & mask) { + y++; + } + } + if (x > y) { + res |= mask; + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int FindDuplicate(IList nums) { + int n = nums.Count; + int res = 0; + for (int b = 0; b < 32; b++) { + int x = 0, y = 0; + int mask = 1 << b; + foreach (int num in nums) { + if ((num & mask) != 0) { + x++; + } + } + for (int num = 1; num < n; num++) { + if ((num & mask) != 0) { + y++; + } + } + if (x > y) { + res |= mask; + } + } + return res; + } +} +``` + +```go +func findDuplicate(nums []int) int { + n := len(nums) + res := 0 + + for b := 0; b < 32; b++ { + x, y := 0, 0 + mask := 1 << b + + for _, num := range nums { + if num&mask != 0 { + x++ + } + } + + for num := 1; num < n; num++ { + if num&mask != 0 { + y++ + } + } + + if x > y { + res |= mask + } + } + + return res +} +``` + +```kotlin +class Solution { + fun findDuplicate(nums: IntArray): Int { + val n = nums.size + var res = 0 + + for (b in 0 until 32) { + var x = 0 + var y = 0 + val mask = 1 shl b + + for (num in nums) { + if (num and mask != 0) { + x++ + } + } + + for (num in 1 until n) { + if (num and mask != 0) { + y++ + } + } + + if (x > y) { + res = res or mask + } + } + + return res + } +} +``` + +```swift +class Solution { + func findDuplicate(_ nums: [Int]) -> Int { + let n = nums.count + var res = 0 + + for b in 0..<32 { + var x = 0, y = 0 + let mask = 1 << b + + for num in nums { + if num & mask != 0 { + x += 1 + } + } + + for num in 1.. y { + res |= mask + } + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(32 * n)$ +* Space complexity: $O(1)$ + +--- + +## 7. Fast And Slow Pointers + +::tabs-start + +```python +class Solution: + def findDuplicate(self, nums: List[int]) -> int: + slow, fast = 0, 0 + while True: + slow = nums[slow] + fast = nums[nums[fast]] + if slow == fast: + break + + slow2 = 0 + while True: + slow = nums[slow] + slow2 = nums[slow2] + if slow == slow2: + return slow +``` + +```java +class Solution { + public int findDuplicate(int[] nums) { + int slow = 0, fast = 0; + while (true) { + slow = nums[slow]; + fast = nums[nums[fast]]; + if (slow == fast) { + break; + } + } + + int slow2 = 0; + while (true) { + slow = nums[slow]; + slow2 = nums[slow2]; + if (slow == slow2) { + return slow; + } + } + } +} +``` + +```cpp +class Solution { +public: + int findDuplicate(vector& nums) { + int slow = 0, fast = 0; + while (true) { + slow = nums[slow]; + fast = nums[nums[fast]]; + if (slow == fast) { + break; + } + } + + int slow2 = 0; + while (true) { + slow = nums[slow]; + slow2 = nums[slow2]; + if (slow == slow2) { + return slow; + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findDuplicate(nums) { + let slow = 0; + let fast = 0; + while (true) { + slow = nums[slow]; + fast = nums[nums[fast]]; + if (slow === fast) { + break; + } + } + + let slow2 = 0; + while (true) { + slow = nums[slow]; + slow2 = nums[slow2]; + if (slow === slow2) { + return slow; + } + } + } +} +``` + +```csharp +public class Solution { + public int FindDuplicate(int[] nums) { + int slow = 0, fast = 0; + while (true) { + slow = nums[slow]; + fast = nums[nums[fast]]; + if (slow == fast) { + break; + } + } + + int slow2 = 0; + while (true) { + slow = nums[slow]; + slow2 = nums[slow2]; + if (slow == slow2) { + return slow; + } + } + } +} +``` + +```go +func findDuplicate(nums []int) int { + slow, fast := 0, 0 + + for { + slow = nums[slow] + fast = nums[nums[fast]] + + if slow == fast { + break + } + } + + slow2 := 0 + for { + slow = nums[slow] + slow2 = nums[slow2] + + if slow == slow2 { + return slow + } + } +} +``` + +```kotlin +class Solution { + fun findDuplicate(nums: IntArray): Int { + var slow = 0 + var fast = 0 + + while (true) { + slow = nums[slow] + fast = nums[nums[fast]] + + if (slow == fast) { + break + } + } + + var slow2 = 0 + while (true) { + slow = nums[slow] + slow2 = nums[slow2] + + if (slow == slow2) { + return slow + } + } + } +} +``` + +```swift +class Solution { + func findDuplicate(_ nums: [Int]) -> Int { + var slow = 0, fast = 0 + + while true { + slow = nums[slow] + fast = nums[nums[fast]] + if slow == fast { + break + } + } + + var slow2 = 0 + while true { + slow = nums[slow] + slow2 = nums[slow2] + if slow == slow2 { + return slow + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/find-duplicate-subtrees.md b/articles/find-duplicate-subtrees.md new file mode 100644 index 000000000..bf3d6d7e9 --- /dev/null +++ b/articles/find-duplicate-subtrees.md @@ -0,0 +1,550 @@ +## 1. Brute Force (DFS) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def findDuplicateSubtrees(self, root: Optional[TreeNode]) -> List[Optional[TreeNode]]: + def same(node1, node2): + if not node1 and not node2: + return True + if not node1 or not node2: + return False + return (node1.val == node2.val and + same(node1.left, node2.left) and + same(node1.right, node2.right)) + + subTree = [] + def dfs(root): + if not root: + return + subTree.append(root) + dfs(root.left) + dfs(root.right) + + dfs(root) + res = [] + seen = set() + + for i in range(len(subTree)): + if subTree[i] in seen: + continue + for j in range(i + 1, len(subTree)): + if subTree[j] in seen: + continue + + if same(subTree[i], subTree[j]): + if subTree[i] not in seen: + res.append(subTree[i]) + seen.add(subTree[i]) + seen.add(subTree[j]) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List findDuplicateSubtrees(TreeNode root) { + List res = new ArrayList<>(); + Set seen = new HashSet<>(); + List subTree = new ArrayList<>(); + dfs(root, subTree); + + for (int i = 0; i < subTree.size(); i++) { + if (seen.contains(subTree.get(i))) continue; + for (int j = i + 1; j < subTree.size(); j++) { + if (seen.contains(subTree.get(j))) continue; + + if (same(subTree.get(i), subTree.get(j))) { + if (!seen.contains(subTree.get(i))) { + res.add(subTree.get(i)); + seen.add(subTree.get(i)); + } + seen.add(subTree.get(j)); + } + } + } + return res; + } + + private boolean same(TreeNode node1, TreeNode node2) { + if (node1 == null && node2 == null) return true; + if (node1 == null || node2 == null) return false; + return node1.val == node2.val && + same(node1.left, node2.left) && + same(node1.right, node2.right); + } + + private void dfs(TreeNode root, List subTree) { + if (root == null) return; + subTree.add(root); + dfs(root.left, subTree); + dfs(root.right, subTree); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector findDuplicateSubtrees(TreeNode* root) { + vector res; + unordered_set seen; + vector subTree; + dfs(root, subTree); + + for (int i = 0; i < subTree.size(); i++) { + if (seen.count(subTree[i])) continue; + for (int j = i + 1; j < subTree.size(); j++) { + if (seen.count(subTree[j])) continue; + + if (same(subTree[i], subTree[j])) { + if (!seen.count(subTree[i])) { + res.push_back(subTree[i]); + seen.insert(subTree[i]); + } + seen.insert(subTree[j]); + } + } + } + return res; + } + +private: + bool same(TreeNode* node1, TreeNode* node2) { + if (!node1 && !node2) return true; + if (!node1 || !node2) return false; + return node1->val == node2->val && + same(node1->left, node2->left) && + same(node1->right, node2->right); + } + + void dfs(TreeNode* root, vector& subTree) { + if (!root) return; + subTree.push_back(root); + dfs(root->left, subTree); + dfs(root->right, subTree); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {TreeNode[]} + */ + findDuplicateSubtrees(root) { + const res = []; + const seen = new Set(); + + const subTree = []; + const dfs = (root) => { + if (!root) return; + subTree.push(root); + dfs(root.left); + dfs(root.right); + }; + dfs(root); + + const same = (node1, node2) => { + if (!node1 && !node2) return true; + if (!node1 || !node2) return false; + return node1.val === node2.val && + same(node1.left, node2.left) && + same(node1.right, node2.right); + }; + + + for (let i = 0; i < subTree.length; i++) { + if (seen.has(subTree[i])) continue; + for (let j = i + 1; j < subTree.length; j++) { + if (seen.has(subTree[j])) continue; + + if (same(subTree[i], subTree[j])) { + if (!seen.has(subTree[i])) { + res.push(subTree[i]); + seen.add(subTree[i]); + } + seen.add(subTree[j]); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n)$ + +--- + +## 2. DFS + Serialization + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def findDuplicateSubtrees(self, root: Optional[TreeNode]) -> List[Optional[TreeNode]]: + subtrees = defaultdict(list) + res = [] + + def dfs(node): + if not node: + return "null" + s = ",".join([str(node.val), dfs(node.left), dfs(node.right)]) + if len(subtrees[s]) == 1: + res.append(node) + subtrees[s].append(node) + return s + + dfs(root) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private Map> subtrees; + private List res; + + public List findDuplicateSubtrees(TreeNode root) { + subtrees = new HashMap<>(); + res = new ArrayList<>(); + dfs(root); + return res; + } + + private String dfs(TreeNode node) { + if (node == null) return "null"; + String s = node.val + "," + dfs(node.left) + "," + dfs(node.right); + subtrees.putIfAbsent(s, new ArrayList<>()); + if (subtrees.get(s).size() == 1) { + res.add(node); + } + subtrees.get(s).add(node); + return s; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { + unordered_map> subtrees; + vector res; + +public: + vector findDuplicateSubtrees(TreeNode* root) { + dfs(root); + return res; + } + +private: + string dfs(TreeNode* node) { + if (!node) return "null"; + string s = to_string(node->val) + "," + dfs(node->left) + "," + dfs(node->right); + if (subtrees[s].size() == 1) { + res.push_back(node); + } + subtrees[s].push_back(node); + return s; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {TreeNode[]} + */ + findDuplicateSubtrees(root) { + const subtrees = new Map(); + const res = []; + + const dfs = (node) => { + if (!node) return "null"; + const s = `${node.val},${dfs(node.left)},${dfs(node.right)}`; + if (!subtrees.has(s)) { + subtrees.set(s, []); + } + if (subtrees.get(s).length === 1) { + res.push(node); + } + subtrees.get(s).push(node); + return s; + }; + + dfs(root); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Depth First Search (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def findDuplicateSubtrees(self, root: Optional[TreeNode]) -> List[Optional[TreeNode]]: + id_map = {} + count = defaultdict(int) + res = [] + + def dfs(node): + if not node: + return -1 + cur = (dfs(node.left), node.val, dfs(node.right)) + if cur not in id_map: + id_map[cur] = len(id_map) + 1 + + curId = id_map[cur] + if count[curId] == 1: + res.append(node) + count[curId] += 1 + return curId + + dfs(root) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private Map idMap; + private Map count; + private List res; + + public List findDuplicateSubtrees(TreeNode root) { + idMap = new HashMap<>(); + count = new HashMap<>(); + res = new ArrayList<>(); + + dfs(root); + return res; + } + + private int dfs(TreeNode node) { + if (node == null) return -1; + String cur = dfs(node.left) + "," + node.val + "," + dfs(node.right); + idMap.putIfAbsent(cur, idMap.size()); + int curId = idMap.get(cur); + count.put(curId, count.getOrDefault(curId, 0) + 1); + if (count.get(curId) == 2) { + res.add(node); + } + return curId; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { + unordered_map idMap; + unordered_map count; + vector res; + +public: + vector findDuplicateSubtrees(TreeNode* root) { + dfs(root); + return res; + } + +private: + int dfs(TreeNode* node) { + if (!node) return -1; + string cur = to_string(dfs(node->left)) + "," + + to_string(node->val) + "," + + to_string(dfs(node->right)); + if (idMap.find(cur) == idMap.end()) { + idMap[cur] = idMap.size(); + } + int curId = idMap[cur]; + count[curId]++; + if (count[curId] == 2) { + res.push_back(node); + } + return curId; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {TreeNode[]} + */ + findDuplicateSubtrees(root) { + const idMap = new Map(); + const count = new Map(); + const res = []; + + const dfs = (node) => { + if (!node) return -1; + + const cur = `${dfs(node.left)},${node.val},${dfs(node.right)}`; + if (!idMap.has(cur)) { + idMap.set(cur, idMap.size + 1); + } + + const curId = idMap.get(cur); + count.set(curId, (count.get(curId) || 0) + 1); + if (count.get(curId) === 2) { + res.push(node); + } + + return curId; + }; + + dfs(root); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/find-eventual-safe-states.md b/articles/find-eventual-safe-states.md new file mode 100644 index 000000000..df245c8a2 --- /dev/null +++ b/articles/find-eventual-safe-states.md @@ -0,0 +1,308 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def eventualSafeNodes(self, graph: List[List[int]]) -> List[int]: + n = len(graph) + safe = {} + + def dfs(node): + if node in safe: + return safe[node] + safe[node] = False + for nei in graph[node]: + if not dfs(nei): + return safe[node] + safe[node] = True + return safe[node] + + res = [] + for node in range(n): + if dfs(node): + res.append(node) + return res +``` + +```java +public class Solution { + private Boolean[] safe; + + public List eventualSafeNodes(int[][] graph) { + int n = graph.length; + safe = new Boolean[n]; + List res = new ArrayList<>(); + for (int node = 0; node < n; node++) { + if (dfs(graph, node)) { + res.add(node); + } + } + return res; + } + + private boolean dfs(int[][] graph, int node) { + if (safe[node] != null) { + return safe[node]; + } + + safe[node] = false; + for (int nei : graph[node]) { + if (!dfs(graph, nei)) { + return false; + } + } + safe[node] = true; + return true; + } +} +``` + +```cpp +class Solution { + vector safe; + +public: + vector eventualSafeNodes(vector>& graph) { + int n = graph.size(); + vector res; + safe.assign(n, -1); + for (int node = 0; node < n; node++) { + if (dfs(graph, node)) { + res.push_back(node); + } + } + return res; + } + +private: + bool dfs(vector>& graph, int node) { + if (safe[node] != -1) { + return safe[node]; + } + safe[node] = 0; + for (int nei : graph[node]) { + if (!dfs(graph, nei)) { + return false; + } + } + safe[node] = 1; + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} graph + * @return {number[]} + */ + eventualSafeNodes(graph) { + const n = graph.length; + const safe = Array(n).fill(undefined); + const res = []; + + const dfs = (node) => { + if (safe[node] !== undefined) { + return safe[node]; + } + safe[node] = false; + for (let nei of graph[node]) { + if (!dfs(nei)) { + return false; + } + } + safe[node] = true; + return true; + }; + + for (let node = 0; node < n; node++) { + if (dfs(node)) { + res.push(node); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges in the given graph. + +--- + +## 2. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def eventualSafeNodes(self, graph: List[List[int]]) -> List[int]: + n = len(graph) + outdegree = [0] * n + parents = [[] for _ in range(n)] + queue = deque() + + for node in range(n): + outdegree[node] = len(graph[node]) + if outdegree[node] == 0: + queue.append(node) + for nei in graph[node]: + parents[nei].append(node) + + while queue: + node = queue.popleft() + for parent in parents[node]: + outdegree[parent] -= 1 + if outdegree[parent] == 0: + queue.append(parent) + + res = [] + for node in range(n): + if outdegree[node] <= 0: + res.append(node) + return res +``` + +```java +public class Solution { + public List eventualSafeNodes(int[][] graph) { + int n = graph.length; + int[] outdegree = new int[n]; + List[] parents = new ArrayList[n]; + Queue queue = new LinkedList<>(); + + for (int i = 0; i < n; i++) { + parents[i] = new ArrayList<>(); + } + + for (int node = 0; node < n; node++) { + outdegree[node] = graph[node].length; + if (outdegree[node] == 0) { + queue.add(node); + } + for (int nei : graph[node]) { + parents[nei].add(node); + } + } + + while (!queue.isEmpty()) { + int node = queue.poll(); + for (int parent : parents[node]) { + outdegree[parent]--; + if (outdegree[parent] == 0) { + queue.add(parent); + } + } + } + + List res = new ArrayList<>(); + for (int node = 0; node < n; node++) { + if (outdegree[node] <= 0) { + res.add(node); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector eventualSafeNodes(vector>& graph) { + int n = graph.size(); + vector outdegree(n, 0); + vector> parents(n); + queue q; + + for (int node = 0; node < n; node++) { + outdegree[node] = graph[node].size(); + if (outdegree[node] == 0) { + q.push(node); + } + for (int nei : graph[node]) { + parents[nei].push_back(node); + } + } + + while (!q.empty()) { + int node = q.front(); + q.pop(); + for (int parent : parents[node]) { + outdegree[parent]--; + if (outdegree[parent] == 0) { + q.push(parent); + } + } + } + + vector res; + for (int node = 0; node < n; node++) { + if (outdegree[node] <= 0) { + res.push_back(node); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} graph + * @return {number[]} + */ + eventualSafeNodes(graph) { + const n = graph.length; + const outdegree = Array(n).fill(0); + const parents = Array.from({ length: n }, () => []); + const queue = new Queue(); + + for (let node = 0; node < n; node++) { + outdegree[node] = graph[node].length; + if (outdegree[node] === 0) { + queue.push(node); + } + for (let nei of graph[node]) { + parents[nei].push(node); + } + } + + while (!queue.isEmpty()) { + const node = queue.pop(); + for (let parent of parents[node]) { + outdegree[parent]--; + if (outdegree[parent] === 0) { + queue.push(parent); + } + } + } + + const res = []; + for (let node = 0; node < n; node++) { + if (outdegree[node] <= 0) { + res.push(node); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges in the given graph. \ No newline at end of file diff --git a/articles/find-first-and-last-position-of-element-in-sorted-array.md b/articles/find-first-and-last-position-of-element-in-sorted-array.md new file mode 100644 index 000000000..e2d66f092 --- /dev/null +++ b/articles/find-first-and-last-position-of-element-in-sorted-array.md @@ -0,0 +1,449 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def searchRange(self, nums: List[int], target: int) -> List[int]: + res = [-1, -1] + + for i, num in enumerate(nums): + if num == target: + if res[0] == -1: + res[0] = res[1] = i + else: + res[1] = i + + return res +``` + +```java +public class Solution { + public int[] searchRange(int[] nums, int target) { + int[] res = {-1, -1}; + + for (int i = 0; i < nums.length; i++) { + if (nums[i] == target) { + if (res[0] == -1) { + res[0] = res[1] = i; + } else { + res[1] = i; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector searchRange(vector& nums, int target) { + vector res = {-1, -1}; + + for (int i = 0; i < nums.size(); i++) { + if (nums[i] == target) { + if (res[0] == -1) { + res[0] = res[1] = i; + } else { + res[1] = i; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ + searchRange(nums, target) { + let res = [-1, -1]; + + for (let i = 0; i < nums.length; i++) { + if (nums[i] === target) { + if (res[0] === -1) { + res[0] = res[1] = i; + } else { + res[1] = i; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Binary Search - I + +::tabs-start + +```python +class Solution: + def searchRange(self, nums: List[int], target: int) -> List[int]: + left = self.binarySearch(nums, target, True) + right = self.binarySearch(nums, target, False) + return [left, right] + + def binarySearch(self, nums, target, leftBias): + l, r = 0, len(nums) - 1 + i = -1 + while l <= r: + m = (l + r) // 2 + if target > nums[m]: + l = m + 1 + elif target < nums[m]: + r = m - 1 + else: + i = m + if leftBias: + r = m - 1 + else: + l = m + 1 + return i +``` + +```java +public class Solution { + public int[] searchRange(int[] nums, int target) { + int left = binarySearch(nums, target, true); + int right = binarySearch(nums, target, false); + return new int[]{left, right}; + } + + private int binarySearch(int[] nums, int target, boolean leftBias) { + int l = 0, r = nums.length - 1, i = -1; + while (l <= r) { + int m = (l + r) / 2; + if (target > nums[m]) { + l = m + 1; + } else if (target < nums[m]) { + r = m - 1; + } else { + i = m; + if (leftBias) { + r = m - 1; + } else { + l = m + 1; + } + } + } + return i; + } +} +``` + +```cpp +class Solution { +public: + vector searchRange(vector& nums, int target) { + int left = binarySearch(nums, target, true); + int right = binarySearch(nums, target, false); + return {left, right}; + } + +private: + int binarySearch(vector& nums, int target, bool leftBias) { + int l = 0, r = nums.size() - 1, i = -1; + while (l <= r) { + int m = (l + r) / 2; + if (target > nums[m]) { + l = m + 1; + } else if (target < nums[m]) { + r = m - 1; + } else { + i = m; + if (leftBias) { + r = m - 1; + } else { + l = m + 1; + } + } + } + return i; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ + searchRange(nums, target) { + let left = this.binarySearch(nums, target, true); + let right = this.binarySearch(nums, target, false); + return [left, right]; + } + + /** + * @param {number[]} nums + * @param {number} target + * @param {boolean} leftBias + * @return {number} + */ + binarySearch(nums, target, leftBias) { + let l = 0, r = nums.length - 1, i = -1; + while (l <= r) { + let m = Math.floor((l + r) / 2); + if (target > nums[m]) { + l = m + 1; + } else if (target < nums[m]) { + r = m - 1; + } else { + i = m; + if (leftBias) { + r = m - 1; + } else { + l = m + 1; + } + } + } + return i; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Binary Search - II + +::tabs-start + +```python +class Solution: + def searchRange(self, nums: List[int], target: int) -> List[int]: + n = len(nums) + + def binarySearch(target): + l, r = 0, n + while l < r: + m = l + (r - l) // 2 + if nums[m] >= target: + r = m + else: + l = m + 1 + return l + + start = binarySearch(target) + if start == n or nums[start] != target: + return [-1, -1] + + return [start, binarySearch(target + 1) - 1] +``` + +```java +public class Solution { + public int[] searchRange(int[] nums, int target) { + int n = nums.length; + + int start = binarySearch(nums, target, n); + if (start == n || nums[start] != target) { + return new int[]{-1, -1}; + } + + return new int[]{start, binarySearch(nums, target + 1, n) - 1}; + } + + private int binarySearch(int[] nums, int target, int n) { + int l = 0, r = n; + while (l < r) { + int m = l + (r - l) / 2; + if (nums[m] >= target) { + r = m; + } else { + l = m + 1; + } + } + return l; + } +} +``` + +```cpp +class Solution { +public: + vector searchRange(vector& nums, int target) { + int n = nums.size(); + + int start = binarySearch(nums, target, n); + if (start == n || nums[start] != target) { + return {-1, -1}; + } + + return {start, binarySearch(nums, target + 1, n) - 1}; + } + +private: + int binarySearch(vector& nums, int target, int n) { + int l = 0, r = n; + while (l < r) { + int m = l + (r - l) / 2; + if (nums[m] >= target) { + r = m; + } else { + l = m + 1; + } + } + return l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ + searchRange(nums, target) { + const n = nums.length; + + const binarySearch = (target) => { + let l = 0, r = n; + while (l < r) { + let m = Math.floor(l + (r - l) / 2); + if (nums[m] >= target) { + r = m; + } else { + l = m + 1; + } + } + return l; + }; + + let start = binarySearch(target); + if (start === n || nums[start] !== target) { + return [-1, -1]; + } + + return [start, binarySearch(target + 1) - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 4. In-Built Function + +::tabs-start + +```python +class Solution: + def searchRange(self, nums: List[int], target: int) -> List[int]: + left = bisect.bisect_left(nums, target) + if left >= len(nums) or nums[left] != target: + return [-1, -1] + + right = bisect.bisect_right(nums, target) - 1 + return [left, right] +``` + +```java +public class Solution { + public int[] searchRange(int[] nums, int target) { + int left = findLeft(nums, target); + if (left == -1) { + return new int[]{-1, -1}; + } + + int right = findRight(nums, target); + return new int[]{left, right}; + } + + private int findLeft(int[] nums, int target) { + // O(n) time in worst case + int index = Arrays.binarySearch(nums, target); + if (index < 0) return -1; + + while (index > 0 && nums[index - 1] == target) { + index--; + } + return index; + } + + private int findRight(int[] nums, int target) { + // O(n) time in worst case + int index = Arrays.binarySearch(nums, target); + if (index < 0) return -1; + + while (index < nums.length - 1 && nums[index + 1] == target) { + index++; + } + return index; + } +} +``` + +```cpp +class Solution { +public: + vector searchRange(vector& nums, int target) { + int left = lower_bound(nums.begin(), nums.end(), target) - nums.begin(); + if (left == nums.size() || nums[left] != target) { + return {-1, -1}; + } + + int right = upper_bound(nums.begin(), nums.end(), target) - nums.begin() - 1; + return {left, right}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ + searchRange(nums, target) { + let left = nums.indexOf(target); + let right = nums.lastIndexOf(target); + + return left === -1 ? [-1, -1] : [left, right]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/find-first-palindromic-string-in-the-array.md b/articles/find-first-palindromic-string-in-the-array.md new file mode 100644 index 000000000..03dd3683d --- /dev/null +++ b/articles/find-first-palindromic-string-in-the-array.md @@ -0,0 +1,147 @@ +## 1. Reverse String + +::tabs-start + +```python +class Solution: + def firstPalindrome(self, words: List[str]) -> str: + for w in words: + if w == w[::-1]: + return w + return "" +``` + +```java +public class Solution { + public String firstPalindrome(String[] words) { + for (String w : words) { + if (w.equals(new StringBuilder(w).reverse().toString())) { + return w; + } + } + return ""; + } +} +``` + +```cpp +class Solution { +public: + string firstPalindrome(vector& words) { + for (const string& w : words) { + string rev = w; + reverse(rev.begin(), rev.end()); + if (w == rev) { + return w; + } + } + return ""; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string} + */ + firstPalindrome(words) { + for (let w of words) { + if (w === w.split('').reverse().join('')) { + return w; + } + } + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the size of the string array $words$ and $m$ is the average length of a word in the array. + +--- + +## 2. Two Pointers + +::tabs-start + +```python +class Solution: + def firstPalindrome(self, words: List[str]) -> str: + for w in words: + l, r = 0, len(w) - 1 + while w[l] == w[r]: + if l >= r: + return w + l, r = l + 1, r - 1 + return "" +``` + +```java +public class Solution { + public String firstPalindrome(String[] words) { + for (String w : words) { + int l = 0, r = w.length() - 1; + while (w.charAt(l) == w.charAt(r)) { + if (l >= r) return w; + l++; + r--; + } + } + return ""; + } +} +``` + +```cpp +class Solution { +public: + string firstPalindrome(vector& words) { + for (const string& w : words) { + int l = 0, r = w.length() - 1; + while (w[l] == w[r]) { + if (l >= r) return w; + l++; + r--; + } + } + return ""; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string} + */ + firstPalindrome(words) { + for (let w of words) { + let l = 0, r = w.length - 1; + while (w.charAt(l) === w.charAt(r)) { + if (l >= r) return w; + l++; + r--; + } + } + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(1)$ extra space. + +> Where $n$ is the size of the string array $words$ and $m$ is the average length of a word in the array. \ No newline at end of file diff --git a/articles/find-in-mountain-array.md b/articles/find-in-mountain-array.md new file mode 100644 index 000000000..c917a7507 --- /dev/null +++ b/articles/find-in-mountain-array.md @@ -0,0 +1,842 @@ +## 1. Brute Force + +::tabs-start + +```python +# """ +# This is MountainArray's API interface. +# You should not implement it, or speculate about its implementation +# """ +#class MountainArray: +# def get(self, index: int) -> int: +# def length(self) -> int: + +class Solution: + def findInMountainArray(self, target: int, mountainArr: 'MountainArray') -> int: + n = mountainArr.length() + + for i in range(n): + if mountainArr.get(i) == target: + return i + + return -1 +``` + +```java +/** + * // This is MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * interface MountainArray { + * public int get(int index) {} + * public int length() {} + * } + */ + +public class Solution { + public int findInMountainArray(int target, MountainArray mountainArr) { + int n = mountainArr.length(); + + for (int i = 0; i < n; i++) { + if (mountainArr.get(i) == target) { + return i; + } + } + + return -1; + } +} +``` + +```cpp +/** + * // This is the MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * class MountainArray { + * public: + * int get(int index); + * int length(); + * }; + */ + +class Solution { +public: + int findInMountainArray(int target, MountainArray &mountainArr) { + int n = mountainArr.length(); + + for (int i = 0; i < n; i++) { + if (mountainArr.get(i) == target) { + return i; + } + } + + return -1; + } +}; +``` + +```javascript +/** + * // This is the MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * class MountainArray { + * @param {number} index + * @return {number} + * get(index) { + * ... + * } + * + * @return {number} + * length() { + * ... + * } + * } + */ + +class Solution { + /** + * @param {number} target + * @param {MountainArray} mountainArr + * @return {number} + */ + findInMountainArray(target, mountainArr) { + let n = mountainArr.length(); + + for (let i = 0; i < n; i++) { + if (mountainArr.get(i) === target) { + return i; + } + } + + return -1; + } +} +``` + +```csharp +/** + * // This is MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * class MountainArray { + * public int Get(int index) {} + * public int Length() {} + * } + */ + +class Solution { + public int FindInMountainArray(int target, MountainArray mountainArr) { + int n = mountainArr.Length(); + + for (int i = 0; i < n; i++) { + if (mountainArr.Get(i) == target) { + return i; + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Binary Search + +::tabs-start + +```python +# """ +# This is MountainArray's API interface. +# You should not implement it, or speculate about its implementation +# """ +#class MountainArray: +# def get(self, index: int) -> int: +# def length(self) -> int: + +class Solution: + def findInMountainArray(self, target: int, mountainArr: 'MountainArray') -> int: + length = mountainArr.length() + + # Find Peak + l, r = 1, length - 2 + while l <= r: + m = (l + r) // 2 + left, mid, right = mountainArr.get(m - 1), mountainArr.get(m), mountainArr.get(m + 1) + if left < mid < right: + l = m + 1 + elif left > mid > right: + r = m - 1 + else: + break + peak = m + + # Search left portion + l, r = 0, peak - 1 + while l <= r: + m = (l + r) // 2 + val = mountainArr.get(m) + if val < target: + l = m + 1 + elif val > target: + r = m - 1 + else: + return m + + # Search right portion + l, r = peak, length - 1 + while l <= r: + m = (l + r) // 2 + val = mountainArr.get(m) + if val > target: + l = m + 1 + elif val < target: + r = m - 1 + else: + return m + + return -1 +``` + +```java +/** + * // This is MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * interface MountainArray { + * public int get(int index) {} + * public int length() {} + * } + */ + +public class Solution { + public int findInMountainArray(int target, MountainArray mountainArr) { + int length = mountainArr.length(); + + // Find Peak + int l = 1, r = length - 2, peak = 0; + while (l <= r) { + int m = (l + r) / 2; + int left = mountainArr.get(m - 1); + int mid = mountainArr.get(m); + int right = mountainArr.get(m + 1); + if (left < mid && mid < right) { + l = m + 1; + } else if (left > mid && mid > right) { + r = m - 1; + } else { + peak = m; + break; + } + } + + // Search left portion + l = 0; + r = peak - 1; + while (l <= r) { + int m = (l + r) / 2; + int val = mountainArr.get(m); + if (val < target) { + l = m + 1; + } else if (val > target) { + r = m - 1; + } else { + return m; + } + } + + // Search right portion + l = peak; + r = length - 1; + while (l <= r) { + int m = (l + r) / 2; + int val = mountainArr.get(m); + if (val > target) { + l = m + 1; + } else if (val < target) { + r = m - 1; + } else { + return m; + } + } + + return -1; + } +} +``` + +```cpp +/** + * // This is the MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * class MountainArray { + * public: + * int get(int index); + * int length(); + * }; + */ + +class Solution { +public: + int findInMountainArray(int target, MountainArray &mountainArr) { + int length = mountainArr.length(); + + // Find Peak + int l = 1, r = length - 2, peak = 0; + while (l <= r) { + int m = (l + r) / 2; + int left = mountainArr.get(m - 1); + int mid = mountainArr.get(m); + int right = mountainArr.get(m + 1); + if (left < mid && mid < right) { + l = m + 1; + } else if (left > mid && mid > right) { + r = m - 1; + } else { + peak = m; + break; + } + } + + // Search left portion + l = 0; + r = peak - 1; + while (l <= r) { + int m = (l + r) / 2; + int val = mountainArr.get(m); + if (val < target) { + l = m + 1; + } else if (val > target) { + r = m - 1; + } else { + return m; + } + } + + // Search right portion + l = peak; + r = length - 1; + while (l <= r) { + int m = (l + r) / 2; + int val = mountainArr.get(m); + if (val > target) { + l = m + 1; + } else if (val < target) { + r = m - 1; + } else { + return m; + } + } + + return -1; + } +}; +``` + +```javascript +/** + * // This is the MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * class MountainArray { + * @param {number} index + * @return {number} + * get(index) { + * ... + * } + * + * @return {number} + * length() { + * ... + * } + * } + */ + +class Solution { + /** + * @param {number} target + * @param {MountainArray} mountainArr + * @return {number} + */ + findInMountainArray(target, mountainArr) { + const length = mountainArr.length(); + + // Find Peak + let l = 1, r = length - 2, peak = 0; + while (l <= r) { + const m = Math.floor((l + r) / 2); + const left = mountainArr.get(m - 1); + const mid = mountainArr.get(m); + const right = mountainArr.get(m + 1); + if (left < mid && mid < right) { + l = m + 1; + } else if (left > mid && mid > right) { + r = m - 1; + } else { + peak = m; + break; + } + } + + // Search left portion + l = 0; + r = peak - 1; + while (l <= r) { + const m = Math.floor((l + r) / 2); + const val = mountainArr.get(m); + if (val < target) { + l = m + 1; + } else if (val > target) { + r = m - 1; + } else { + return m; + } + } + + // Search right portion + l = peak; + r = length - 1; + while (l <= r) { + const m = Math.floor((l + r) / 2); + const val = mountainArr.get(m); + if (val > target) { + l = m + 1; + } else if (val < target) { + r = m - 1; + } else { + return m; + } + } + + return -1; + } +} +``` + +```csharp +/** + * // This is MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * class MountainArray { + * public int Get(int index) {} + * public int Length() {} + * } + */ + +class Solution { + public int FindInMountainArray(int target, MountainArray mountainArr) { + int length = mountainArr.Length(); + + // Find Peak + int l = 1, r = length - 2, peak = 0; + while (l <= r) { + int m = (l + r) / 2; + int left = mountainArr.Get(m - 1); + int mid = mountainArr.Get(m); + int right = mountainArr.Get(m + 1); + if (left < mid && mid < right) { + l = m + 1; + } else if (left > mid && mid > right) { + r = m - 1; + } else { + peak = m; + break; + } + } + + // Search left portion + l = 0; + r = peak - 1; + while (l <= r) { + int m = (l + r) / 2; + int val = mountainArr.Get(m); + if (val < target) { + l = m + 1; + } else if (val > target) { + r = m - 1; + } else { + return m; + } + } + + // Search right portion + l = peak; + r = length - 1; + while (l <= r) { + int m = (l + r) / 2; + int val = mountainArr.Get(m); + if (val > target) { + l = m + 1; + } else if (val < target) { + r = m - 1; + } else { + return m; + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Binary Search + Caching + +::tabs-start + +```python +# """ +# This is MountainArray's API interface. +# You should not implement it, or speculate about its implementation +# """ +#class MountainArray: +# def get(self, index: int) -> int: +# def length(self) -> int: + +class Solution: + def findInMountainArray(self, target: int, mountainArr: 'MountainArray') -> int: + length = mountainArr.length() + cache = {} + + def get(i): + if i not in cache: + cache[i] = mountainArr.get(i) + return cache[i] + + # Find Peak + l, r = 1, length - 2 + while l <= r: + m = (l + r) >> 1 + left, mid, right = get(m - 1), get(m), get(m + 1) + if left < mid < right: + l = m + 1 + elif left > mid > right: + r = m - 1 + else: + break + peak = m + + def binary_search(l, r, ascending): + while l <= r: + m = (l + r) >> 1 + val = get(m) + if val == target: + return m + if ascending == (val < target): + l = m + 1 + else: + r = m - 1 + return -1 + + # Search left portion + res = binary_search(0, peak, True) + if res != -1: + return res + + # Search right portion + return binary_search(peak, length - 1, False) +``` + +```java +/** + * // This is MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * interface MountainArray { + * public int get(int index) {} + * public int length() {} + * } + */ + +public class Solution { + private Map cache = new HashMap<>(); + + private int get(int index, MountainArray mountainArr) { + if (!cache.containsKey(index)) { + cache.put(index, mountainArr.get(index)); + } + return cache.get(index); + } + + private int binarySearch(int l, int r, boolean ascending, MountainArray mountainArr, int target) { + while (l <= r) { + int m = (l + r) >> 1; + int val = get(m, mountainArr); + if (val == target) { + return m; + } + if (ascending == (val < target)) { + l = m + 1; + } else { + r = m - 1; + } + } + return -1; + } + + public int findInMountainArray(int target, MountainArray mountainArr) { + int length = mountainArr.length(); + + // Find Peak + int l = 1, r = length - 2, peak = 0; + while (l <= r) { + int m = (l + r) >> 1; + int left = get(m - 1, mountainArr); + int mid = get(m, mountainArr); + int right = get(m + 1, mountainArr); + if (left < mid && mid < right) { + l = m + 1; + } else if (left > mid && mid > right) { + r = m - 1; + } else { + peak = m; + break; + } + } + + // Search left portion + int res = binarySearch(0, peak, true, mountainArr, target); + if (res != -1) { + return res; + } + + // Search right portion + return binarySearch(peak, length - 1, false, mountainArr, target); + } +} +``` + +```cpp +/** + * // This is the MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * class MountainArray { + * public: + * int get(int index); + * int length(); + * }; + */ + +class Solution { +private: + unordered_map cache; + + int get(int index, MountainArray &mountainArr) { + if (cache.find(index) == cache.end()) { + cache[index] = mountainArr.get(index); + } + return cache[index]; + } + + int binarySearch(int l, int r, bool ascending, int target, MountainArray &mountainArr) { + while (l <= r) { + int m = (l + r) >> 1; + int val = get(m, mountainArr); + if (val == target) { + return m; + } + if (ascending == (val < target)) { + l = m + 1; + } else { + r = m - 1; + } + } + return -1; + } + +public: + int findInMountainArray(int target, MountainArray &mountainArr) { + int length = mountainArr.length(); + + // Find Peak + int l = 1, r = length - 2, peak = 0; + while (l <= r) { + int m = (l + r) >> 1; + int left = get(m - 1, mountainArr); + int mid = get(m, mountainArr); + int right = get(m + 1, mountainArr); + if (left < mid && mid < right) { + l = m + 1; + } else if (left > mid && mid > right) { + r = m - 1; + } else { + peak = m; + break; + } + } + + // Search left portion + int res = binarySearch(0, peak, true, target, mountainArr); + if (res != -1) { + return res; + } + + // Search right portion + return binarySearch(peak, length - 1, false, target, mountainArr); + } +}; +``` + +```javascript +/** + * // This is the MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * class MountainArray { + * @param {number} index + * @return {number} + * get(index) { + * ... + * } + * + * @return {number} + * length() { + * ... + * } + * } + */ + +class Solution { + /** + * @param {number} target + * @param {MountainArray} mountainArr + * @return {number} + */ + findInMountainArray(target, mountainArr) { + const cache = new Map(); + const get = (index) => { + if (!cache.has(index)) { + cache.set(index, mountainArr.get(index)); + } + return cache.get(index); + }; + + const binarySearch = (l, r, ascending) => { + while (l <= r) { + const m = Math.floor((l + r) / 2); + const val = get(m); + if (val === target) { + return m; + } + if (ascending === (val < target)) { + l = m + 1; + } else { + r = m - 1; + } + } + return -1; + }; + + const length = mountainArr.length(); + + // Find Peak + let l = 1, r = length - 2, peak = 0; + while (l <= r) { + const m = Math.floor((l + r) / 2); + const left = get(m - 1); + const mid = get(m); + const right = get(m + 1); + if (left < mid && mid < right) { + l = m + 1; + } else if (left > mid && mid > right) { + r = m - 1; + } else { + peak = m; + break; + } + } + + // Search left portion + let res = binarySearch(0, peak, true); + if (res !== -1) { + return res; + } + + // Search right portion + return binarySearch(peak, length - 1, false); + } +} +``` + +```csharp +/** + * // This is MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * class MountainArray { + * public int Get(int index) {} + * public int Length() {} + * } + */ + +class Solution { + private Dictionary cache = new Dictionary(); + + private int Get(int index, MountainArray mountainArr) { + if (!cache.ContainsKey(index)) { + cache[index] = mountainArr.Get(index); + } + return cache[index]; + } + + private int BinarySearch(int l, int r, bool ascending, MountainArray mountainArr, int target) { + while (l <= r) { + int m = (l + r) >> 1; + int val = Get(m, mountainArr); + if (val == target) { + return m; + } + if ((ascending && val < target) || (!ascending && val > target)) { + l = m + 1; + } else { + r = m - 1; + } + } + return -1; + } + + public int FindInMountainArray(int target, MountainArray mountainArr) { + int length = mountainArr.Length(); + + // Find Peak + int l = 1, r = length - 2, peak = 0; + while (l <= r) { + int m = (l + r) >> 1; + int left = Get(m - 1, mountainArr); + int mid = Get(m, mountainArr); + int right = Get(m + 1, mountainArr); + + if (left < mid && mid < right) { + l = m + 1; + } else if (left > mid && mid > right) { + r = m - 1; + } else { + peak = m; + break; + } + } + + // Search left portion + int res = BinarySearch(0, peak, true, mountainArr, target); + if (res != -1) return res; + + // Search right portion + return BinarySearch(peak, length - 1, false, mountainArr, target); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ \ No newline at end of file diff --git a/articles/find-k-closest-elements.md b/articles/find-k-closest-elements.md new file mode 100644 index 000000000..d4a177c89 --- /dev/null +++ b/articles/find-k-closest-elements.md @@ -0,0 +1,555 @@ +## 1. Sorting (Custom Comparator) + +::tabs-start + +```python +class Solution: + def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]: + arr.sort(key=lambda num: (abs(num - x), num)) + return sorted(arr[:k]) +``` + +```java +public class Solution { + public List findClosestElements(int[] arr, int k, int x) { + List list = new ArrayList<>(); + for (int num : arr) { + list.add(num); + } + + list.sort((a, b) -> { + int diff = Math.abs(a - x) - Math.abs(b - x); + return diff == 0 ? Integer.compare(a, b) : diff; + }); + + List result = list.subList(0, k); + Collections.sort(result); + return result; + } +} +``` + +```cpp +class Solution { +public: + vector findClosestElements(vector& arr, int k, int x) { + sort(arr.begin(), arr.end(), [x](int a, int b) { + int diff = abs(a - x) - abs(b - x); + return diff == 0 ? a < b : diff < 0; + }); + vector result(arr.begin(), arr.begin() + k); + sort(result.begin(), result.end()); + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @param {number} x + * @return {number[]} + */ + findClosestElements(arr, k, x) { + arr.sort((a, b) => { + const diff = Math.abs(a - x) - Math.abs(b - x); + return diff === 0 ? a - b : diff; + }); + const result = arr.slice(0, k); + return result.sort((a, b) => a - b); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + k \log k)$ +* Space complexity: + * $O(1)$ or $O(n)$ space depending on the sorting algorithm. + * $O(k)$ space for the output array. + +> Where $n$ is the size of the input array and $k$ is the number of closest elements to find. + +--- + +## 2. Linear Scan + Two Pointers + +::tabs-start + +```python +class Solution: + def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]: + n = len(arr) + idx = 0 + for i in range(1, n): + if abs(x - arr[idx]) > abs(x - arr[i]): + idx = i + + res = [arr[idx]] + l, r = idx - 1, idx + 1 + + while len(res) < k: + if l >= 0 and r < n: + if abs(x - arr[l]) <= abs(x - arr[r]): + res.append(arr[l]) + l -= 1 + else: + res.append(arr[r]) + r += 1 + elif l >= 0: + res.append(arr[l]) + l -= 1 + elif r < n: + res.append(arr[r]) + r += 1 + + return sorted(res) +``` + +```java +public class Solution { + public List findClosestElements(int[] arr, int k, int x) { + int n = arr.length; + int idx = 0; + for (int i = 1; i < n; i++) { + if (Math.abs(x - arr[idx]) > Math.abs(x - arr[i])) { + idx = i; + } + } + + List res = new ArrayList<>(); + res.add(arr[idx]); + int l = idx - 1, r = idx + 1; + + while (res.size() < k) { + if (l >= 0 && r < n) { + if (Math.abs(x - arr[l]) <= Math.abs(x - arr[r])) { + res.add(arr[l--]); + } else { + res.add(arr[r++]); + } + } else if (l >= 0) { + res.add(arr[l--]); + } else if (r < n) { + res.add(arr[r++]); + } + } + + Collections.sort(res); + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findClosestElements(vector& arr, int k, int x) { + int n = arr.size(); + int idx = 0; + for (int i = 1; i < n; i++) { + if (abs(x - arr[idx]) > abs(x - arr[i])) { + idx = i; + } + } + + vector res = {arr[idx]}; + int l = idx - 1, r = idx + 1; + + while (res.size() < k) { + if (l >= 0 && r < n) { + if (abs(x - arr[l]) <= abs(x - arr[r])) { + res.push_back(arr[l--]); + } else { + res.push_back(arr[r++]); + } + } else if (l >= 0) { + res.push_back(arr[l--]); + } else if (r < n) { + res.push_back(arr[r++]); + } + } + + sort(res.begin(), res.end()); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @param {number} x + * @return {number[]} + */ + findClosestElements(arr, k, x) { + const n = arr.length; + let idx = 0; + for (let i = 1; i < n; i++) { + if (Math.abs(x - arr[idx]) > Math.abs(x - arr[i])) { + idx = i; + } + } + + const res = [arr[idx]]; + let l = idx - 1, r = idx + 1; + + while (res.length < k) { + if (l >= 0 && r < n) { + if (Math.abs(x - arr[l]) <= Math.abs(x - arr[r])) { + res.push(arr[l--]); + } else { + res.push(arr[r++]); + } + } else if (l >= 0) { + res.push(arr[l--]); + } else if (r < n) { + res.push(arr[r++]); + } + } + + return res.sort((a, b) => a - b); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + k \log k)$ +* Space complexity: + * $O(1)$ or $O(k)$ space depending on the sorting algorithm. + * $O(k)$ space for the output array. + +> Where $n$ is the size of the input array and $k$ is the number of closest elements to find. + +--- + +## 3. Two Pointers + +::tabs-start + +```python +class Solution: + def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]: + l, r = 0, len(arr) - 1 + while r - l >= k: + if abs(x - arr[l]) <= abs(x - arr[r]): + r -= 1 + else: + l += 1 + + return arr[l: r + 1] +``` + +```java +public class Solution { + public List findClosestElements(int[] arr, int k, int x) { + int l = 0, r = arr.length - 1; + while (r - l >= k) { + if (Math.abs(x - arr[l]) <= Math.abs(x - arr[r])) { + r--; + } else { + l++; + } + } + List result = new ArrayList<>(); + for (int i = l; i <= r; i++) { + result.add(arr[i]); + } + return result; + } +} +``` + +```cpp +class Solution { +public: + vector findClosestElements(vector& arr, int k, int x) { + int l = 0, r = arr.size() - 1; + while (r - l >= k) { + if (abs(x - arr[l]) <= abs(x - arr[r])) { + r--; + } else { + l++; + } + } + return vector(arr.begin() + l, arr.begin() + r + 1); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @param {number} x + * @return {number[]} + */ + findClosestElements(arr, k, x) { + let l = 0, r = arr.length - 1; + while (r - l >= k) { + if (Math.abs(x - arr[l]) <= Math.abs(x - arr[r])) { + r--; + } else { + l++; + } + } + return arr.slice(l, r + 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n - k)$ +* Space complexity: $O(k)$ for the output array. + +> Where $n$ is the size of the input array and $k$ is the number of closest elements to find. + +--- + +## 4. Binary Search + Two Pointers + +::tabs-start + +```python +class Solution: + def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]: + l, r = 0, len(arr) - 1 + while l < r: + mid = (l + r) // 2 + if arr[mid] < x: + l = mid + 1 + else: + r = mid + + l, r = l - 1, l + while r - l - 1 < k: + if l < 0: + r += 1 + elif r >= len(arr): + l -= 1 + elif abs(arr[l] - x) <= abs(arr[r] - x): + l -= 1 + else: + r += 1 + + return arr[l + 1:r] +``` + +```java +public class Solution { + public List findClosestElements(int[] arr, int k, int x) { + int l = 0, r = arr.length - 1; + while (l < r) { + int mid = (l + r) / 2; + if (arr[mid] < x) { + l = mid + 1; + } else { + r = mid; + } + } + + l = l - 1; + r = l + 1; + while (r - l - 1 < k) { + if (l < 0) { + r++; + } else if (r >= arr.length) { + l--; + } else if (Math.abs(arr[l] - x) <= Math.abs(arr[r] - x)) { + l--; + } else { + r++; + } + } + + List result = new ArrayList<>(); + for (int i = l + 1; i < r; i++) { + result.add(arr[i]); + } + return result; + } +} +``` + +```cpp +class Solution { +public: + vector findClosestElements(vector& arr, int k, int x) { + int l = 0, r = arr.size() - 1; + while (l < r) { + int mid = (l + r) / 2; + if (arr[mid] < x) { + l = mid + 1; + } else { + r = mid; + } + } + + l = l - 1; + r = l + 1; + while (r - l - 1 < k) { + if (l < 0) { + r++; + } else if (r >= arr.size()) { + l--; + } else if (abs(arr[l] - x) <= abs(arr[r] - x)) { + l--; + } else { + r++; + } + } + + return vector(arr.begin() + l + 1, arr.begin() + r); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @param {number} x + * @return {number[]} + */ + findClosestElements(arr, k, x) { + let l = 0, r = arr.length - 1; + while (l < r) { + const mid = Math.floor((l + r) / 2); + if (arr[mid] < x) { + l = mid + 1; + } else { + r = mid; + } + } + + l = l - 1; + r = l + 1; + while (r - l - 1 < k) { + if (l < 0) { + r++; + } else if (r >= arr.length) { + l--; + } else if (Math.abs(arr[l] - x) <= Math.abs(arr[r] - x)) { + l--; + } else { + r++; + } + } + + return arr.slice(l + 1, r); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n + k)$ +* Space complexity: $O(k)$ for the output array. + +> Where $n$ is the size of the input array and $k$ is the number of closest elements to find. + +--- + +## 5. Binary Search + +::tabs-start + +```python +class Solution: + def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]: + l, r = 0, len(arr) - k + while l < r: + m = (l + r) // 2 + if x - arr[m] > arr[m + k] - x: + l = m + 1 + else: + r = m + return arr[l:l + k] +``` + +```java +public class Solution { + public List findClosestElements(int[] arr, int k, int x) { + int l = 0, r = arr.length - k; + while (l < r) { + int m = (l + r) / 2; + if (x - arr[m] > arr[m + k] - x) { + l = m + 1; + } else { + r = m; + } + } + List result = new ArrayList<>(); + for (int i = l; i < l + k; i++) { + result.add(arr[i]); + } + return result; + } +} +``` + +```cpp +class Solution { +public: + vector findClosestElements(vector& arr, int k, int x) { + int l = 0, r = arr.size() - k; + while (l < r) { + int m = (l + r) / 2; + if (x - arr[m] > arr[m + k] - x) { + l = m + 1; + } else { + r = m; + } + } + return vector(arr.begin() + l, arr.begin() + l + k); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @param {number} x + * @return {number[]} + */ + findClosestElements(arr, k, x) { + let l = 0, r = arr.length - k; + while (l < r) { + const m = Math.floor((l + r) / 2); + if (x - arr[m] > arr[m + k] - x) { + l = m + 1; + } else { + r = m; + } + } + return arr.slice(l, l + k); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log (n - k) + k)$ +* Space complexity: $O(k)$ for the output array. + +> Where $n$ is the size of the input array and $k$ is the number of closest elements to find. \ No newline at end of file diff --git a/articles/find-largest-value-in-each-tree-row.md b/articles/find-largest-value-in-each-tree-row.md new file mode 100644 index 000000000..d54ce9dfd --- /dev/null +++ b/articles/find-largest-value-in-each-tree-row.md @@ -0,0 +1,462 @@ +## 1. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def largestValues(self, root: Optional[TreeNode]) -> List[int]: + if not root: + return [] + + res = [] + q = deque([root]) + while q: + row_max = q[0].val + for _ in range(len(q)): + node = q.popleft() + row_max = max(row_max, node.val) + if node.left: + q.append(node.left) + if node.right: + q.append(node.right) + res.append(row_max) + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List largestValues(TreeNode root) { + if (root == null) return new ArrayList<>(); + + List res = new ArrayList<>(); + Queue q = new LinkedList<>(); + q.offer(root); + + while (!q.isEmpty()) { + int rowMax = q.peek().val; + for (int i = q.size(); i > 0; i--) { + TreeNode node = q.poll(); + rowMax = Math.max(rowMax, node.val); + if (node.left != null) q.offer(node.left); + if (node.right != null) q.offer(node.right); + } + res.add(rowMax); + } + + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector largestValues(TreeNode* root) { + if (!root) return {}; + + vector res; + queue q; + q.push(root); + + while (!q.empty()) { + int rowMax = q.front()->val; + for (int i = q.size(); i > 0; i--) { + TreeNode* node = q.front(); q.pop(); + rowMax = max(rowMax, node->val); + if (node->left) q.push(node->left); + if (node->right) q.push(node->right); + } + res.push_back(rowMax); + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + largestValues(root) { + if (!root) return []; + + const res = []; + const q = new Queue([root]); + + while (!q.isEmpty()) { + let rowMax = q.front().val; + for (let i = q.size(); i > 0; i--) { + const node = q.pop(); + rowMax = Math.max(rowMax, node.val); + if (node.left) q.push(node.left); + if (node.right) q.push(node.right); + } + res.push(rowMax); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def largestValues(self, root: Optional[TreeNode]) -> List[int]: + if not root: + return [] + + res = [] + def dfs(node, level): + if not node: + return + if level == len(res): + res.append(node.val) + else: + res[level] = max(res[level], node.val) + + dfs(node.left, level + 1) + dfs(node.right, level + 1) + + dfs(root, 0) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List largestValues(TreeNode root) { + List res = new ArrayList<>(); + dfs(root, 0, res); + return res; + } + + private void dfs(TreeNode node, int level, List res) { + if (node == null) return; + if (level == res.size()) { + res.add(node.val); + } else { + res.set(level, Math.max(res.get(level), node.val)); + } + + dfs(node.left, level + 1, res); + dfs(node.right, level + 1, res); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector largestValues(TreeNode* root) { + vector res; + dfs(root, 0, res); + return res; + } + +private: + void dfs(TreeNode* node, int level, vector& res) { + if (!node) return; + if (level == res.size()) { + res.push_back(node->val); + } else { + res[level] = max(res[level], node->val); + } + + dfs(node->left, level + 1, res); + dfs(node->right, level + 1, res); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + largestValues(root) { + if (!root) return []; + + const res = []; + const dfs = (node, level) => { + if (!node) return; + if (level === res.length) { + res.push(node.val); + } else { + res[level] = Math.max(res[level], node.val); + } + dfs(node.left, level + 1); + dfs(node.right, level + 1); + }; + + dfs(root, 0); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def largestValues(self, root: Optional[TreeNode]) -> List[int]: + if not root: + return [] + + res = [] + stack = [(root, 0)] + while stack: + node, level = stack.pop() + if level == len(res): + res.append(node.val) + else: + res[level] = max(res[level], node.val) + + if node.right: + stack.append((node.right, level + 1)) + if node.left: + stack.append((node.left, level + 1)) + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List largestValues(TreeNode root) { + if (root == null) return new ArrayList<>(); + + List res = new ArrayList<>(); + Stack> stack = new Stack<>(); + stack.push(new Pair<>(root, 0)); + while (!stack.isEmpty()) { + Pair p = stack.pop(); + TreeNode node = p.getKey(); + int level = p.getValue(); + if (level == res.size()) { + res.add(node.val); + } else { + res.set(level, Math.max(res.get(level), node.val)); + } + + if (node.right != null) stack.push(new Pair<>(node.right, level + 1)); + if (node.left != null) stack.push(new Pair<>(node.left, level + 1)); + } + + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector largestValues(TreeNode* root) { + if (!root) return {}; + + vector res; + stack> stk; + stk.push({root, 0}); + while (!stk.empty()) { + auto [node, level] = stk.top();stk.pop(); + if (level == res.size()) { + res.push_back(node->val); + } else { + res[level] = max(res[level], node->val); + } + + if (node->right) stk.push({node->right, level + 1}); + if (node->left) stk.push({node->left, level + 1}); + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number[]} + */ + largestValues(root) { + if (!root) return []; + + const res = []; + const stack = [[root, 0]]; + while (stack.length) { + const [node, level] = stack.pop(); + if (level === res.length) { + res.push(node.val); + } else { + res[level] = Math.max(res[level], node.val); + } + + if (node.right) stack.push([node.right, level + 1]); + if (node.left) stack.push([node.left, level + 1]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/find-median-in-a-data-stream.md b/articles/find-median-in-a-data-stream.md new file mode 100644 index 000000000..ad27aa749 --- /dev/null +++ b/articles/find-median-in-a-data-stream.md @@ -0,0 +1,522 @@ +## 1. Sorting + +::tabs-start + +```python +class MedianFinder: + + def __init__(self): + self.data = [] + + def addNum(self, num: int) -> None: + self.data.append(num) + + def findMedian(self) -> float: + self.data.sort() + n = len(self.data) + return (self.data[n // 2] if (n & 1) else + (self.data[n // 2] + self.data[n // 2 - 1]) / 2) +``` + +```java +public class MedianFinder { + private ArrayList data; + + public MedianFinder() { + data = new ArrayList<>(); + } + + public void addNum(int num) { + data.add(num); + } + + public double findMedian() { + Collections.sort(data); + int n = data.size(); + if ((n & 1) == 1) { + return data.get(n / 2); + } else { + return (data.get(n / 2) + data.get(n / 2 - 1)) / 2.0; + } + } +} +``` + +```cpp +class MedianFinder { + vector data; + +public: + MedianFinder() {} + + void addNum(int num) { + data.push_back(num); + } + + double findMedian() { + sort(data.begin(), data.end()); + int n = data.size(); + if (n & 1) { + return data[n / 2]; + } else { + return (data[n / 2] + data[n / 2 - 1]) / 2.0; + } + } +}; +``` + +```javascript +class MedianFinder { + constructor() { + this.data = []; + } + + /** + * + * @param {number} num + * @return {void} + */ + addNum(num) { + this.data.push(num); + } + + /** + * @return {number} + */ + findMedian() { + this.data.sort((a, b) => a - b); + let n = this.data.length; + if (n & 1) { + return this.data[Math.floor(n / 2)]; + } else { + return (this.data[n / 2] + this.data[n / 2 - 1]) / 2; + } + } +} +``` + +```csharp +class MedianFinder { + private List data; + + public MedianFinder() { + data = new List(); + } + + public void AddNum(int num) { + data.Add(num); + } + + public double FindMedian() { + data.Sort(); + int n = data.Count; + if ((n & 1) == 1) { + return data[n / 2]; + } else { + return (data[n / 2] + data[n / 2 - 1]) / 2.0; + } + } +} +``` + +```go +type MedianFinder struct { + data []int +} + +func Constructor() MedianFinder { + return MedianFinder{} +} + +func (this *MedianFinder) AddNum(num int) { + this.data = append(this.data, num) +} + +func (this *MedianFinder) FindMedian() float64 { + sort.Ints(this.data) + n := len(this.data) + if n%2 == 1 { + return float64(this.data[n/2]) + } + return float64(this.data[n/2-1]+this.data[n/2]) / 2.0 +} +``` + +```kotlin +class MedianFinder() { + private val data = mutableListOf() + + fun addNum(num: Int) { + data.add(num) + } + + fun findMedian(): Double { + data.sort() + val n = data.size + return if (n % 2 == 1) { + data[n / 2].toDouble() + } else { + (data[n / 2] + data[n / 2 - 1]) / 2.0 + } + } +} +``` + +```swift +class MedianFinder { + private var data: [Int] + + init() { + self.data = [] + } + + func addNum(_ num: Int) { + data.append(num) + } + + func findMedian() -> Double { + data.sort() + let n = data.count + if n % 2 == 1 { + return Double(data[n / 2]) + } else { + return (Double(data[n / 2]) + Double(data[n / 2 - 1])) / 2.0 + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m)$ for $addNum()$, $O(m * n \log n)$ for $findMedian()$. +* Space complexity: $O(n)$ + +> Where $m$ is the number of function calls and $n$ is the length of the array. + +--- + +## 2. Heap + +::tabs-start + +```python +class MedianFinder: + def __init__(self): + # two heaps, large, small, minheap, maxheap + # heaps should be equal size + self.small, self.large = [], [] + + def addNum(self, num: int) -> None: + if self.large and num > self.large[0]: + heapq.heappush(self.large, num) + else: + heapq.heappush(self.small, -1 * num) + + if len(self.small) > len(self.large) + 1: + val = -1 * heapq.heappop(self.small) + heapq.heappush(self.large, val) + if len(self.large) > len(self.small) + 1: + val = heapq.heappop(self.large) + heapq.heappush(self.small, -1 * val) + + def findMedian(self) -> float: + if len(self.small) > len(self.large): + return -1 * self.small[0] + elif len(self.large) > len(self.small): + return self.large[0] + return (-1 * self.small[0] + self.large[0]) / 2.0 +``` + +```java +public class MedianFinder { + + private Queue smallHeap; //small elements - maxHeap + private Queue largeHeap; //large elements - minHeap + + public MedianFinder() { + smallHeap = new PriorityQueue<>((a, b) -> b - a); + largeHeap = new PriorityQueue<>((a, b) -> a - b); + } + + public void addNum(int num) { + smallHeap.add(num); + if ( + smallHeap.size() - largeHeap.size() > 1 || + !largeHeap.isEmpty() && + smallHeap.peek() > largeHeap.peek() + ) { + largeHeap.add(smallHeap.poll()); + } + if (largeHeap.size() - smallHeap.size() > 1) { + smallHeap.add(largeHeap.poll()); + } + } + + public double findMedian() { + if (smallHeap.size() == largeHeap.size()) { + return (double) (largeHeap.peek() + smallHeap.peek()) / 2; + } else if (smallHeap.size() > largeHeap.size()) { + return (double) smallHeap.peek(); + } else { + return (double) largeHeap.peek(); + } + } +} +``` + +```cpp +class MedianFinder { + priority_queue, less> smallHeap; + priority_queue, greater> largeHeap; + +public: + MedianFinder() {} + + void addNum(int num) { + smallHeap.push(num); + if (!largeHeap.empty() && smallHeap.top() > largeHeap.top()) { + largeHeap.push(smallHeap.top()); + smallHeap.pop(); + } + if (smallHeap.size() > largeHeap.size() + 1) { + largeHeap.push(smallHeap.top()); + smallHeap.pop(); + } + if (largeHeap.size() > smallHeap.size() + 1) { + smallHeap.push(largeHeap.top()); + largeHeap.pop(); + } + } + + double findMedian() { + if (smallHeap.size() == largeHeap.size()) { + return (largeHeap.top() + smallHeap.top()) / 2.0; + } else if (smallHeap.size() > largeHeap.size()) { + return smallHeap.top(); + } else { + return largeHeap.top(); + } + } +}; +``` + +```javascript +/** + * const { PriorityQueue, MaxPriorityQueue, MinPriorityQueue } = require('@datastructures-js/priority-queue'); + */ + +class MedianFinder { + constructor() { + this.small = new PriorityQueue((a, b) => b - a); // Max heap for smaller half + this.large = new PriorityQueue((a, b) => a - b); // Min heap for larger half + } + + /** + * @param {number} num + */ + addNum(num) { + if (this.large.isEmpty() || num > this.large.front()) { + this.large.enqueue(num); + } else { + this.small.enqueue(num); + } + + if (this.small.size() > this.large.size() + 1) { + this.large.enqueue(this.small.dequeue()); + } else if (this.large.size() > this.small.size() + 1) { + this.small.enqueue(this.large.dequeue()); + } + } + + /** + * @return {number} + */ + findMedian() { + if (this.small.size() > this.large.size()) { + return this.small.front(); + } else if (this.large.size() > this.small.size()) { + return this.large.front(); + } else { + return (this.small.front() + this.large.front()) / 2.0; + } + } +} +``` + +```csharp +public class MedianFinder { + + private PriorityQueue small; // Max heap for the smaller half + private PriorityQueue large; // Min heap for the larger half + + public MedianFinder() { + small = new PriorityQueue(Comparer.Create((a, b) => b.CompareTo(a))); + large = new PriorityQueue(); + } + + public void AddNum(int num) { + if (large.Count != 0 && num > large.Peek()) { + large.Enqueue(num, num); + } else { + small.Enqueue(num, num); + } + + if (small.Count > large.Count + 1) { + int val = small.Dequeue(); + large.Enqueue(val, val); + } else if (large.Count > small.Count + 1) { + int val = large.Dequeue(); + small.Enqueue(val, val); + } + } + + public double FindMedian() { + if (small.Count > large.Count) { + return small.Peek(); + } else if (large.Count > small.Count) { + return large.Peek(); + } + + int smallTop = small.Peek(); + return (smallTop + large.Peek()) / 2.0; + } +} +``` + +```go +type MedianFinder struct { + small *priorityqueue.Queue // maxHeap + large *priorityqueue.Queue // minHeap +} + +func Constructor() MedianFinder { + small := priorityqueue.NewWith(func(a, b interface{}) int { + return b.(int) - a.(int) // maxHeap + }) + large := priorityqueue.NewWith(func(a, b interface{}) int { + return a.(int) - b.(int) // minHeap + }) + return MedianFinder{small: small, large: large} +} + +func (this *MedianFinder) AddNum(num int) { + if this.large.Size() > 0 { + largeTop, _ := this.large.Peek() + if num > largeTop.(int) { + this.large.Enqueue(num) + } else { + this.small.Enqueue(num) + } + } else { + this.small.Enqueue(num) + } + + // Rebalance + if this.small.Size() > this.large.Size()+1 { + val, _ := this.small.Dequeue() + this.large.Enqueue(val) + } + if this.large.Size() > this.small.Size()+1 { + val, _ := this.large.Dequeue() + this.small.Enqueue(val) + } +} + +func (this *MedianFinder) FindMedian() float64 { + if this.small.Size() > this.large.Size() { + val, _ := this.small.Peek() + return float64(val.(int)) + } + if this.large.Size() > this.small.Size() { + val, _ := this.large.Peek() + return float64(val.(int)) + } + smallVal, _ := this.small.Peek() + largeVal, _ := this.large.Peek() + return float64(smallVal.(int)+largeVal.(int)) / 2.0 +} +``` + +```kotlin +class MedianFinder() { + // small is maxHeap, large is minHeap + private val small = PriorityQueue(compareByDescending { it }) + private val large = PriorityQueue() + + fun addNum(num: Int) { + if (large.isNotEmpty() && num > large.peek()) { + large.add(num) + } else { + small.add(num) + } + + // Rebalance + if (small.size > large.size + 1) { + large.add(small.poll()) + } + if (large.size > small.size + 1) { + small.add(large.poll()) + } + } + + fun findMedian(): Double { + return when { + small.size > large.size -> small.peek().toDouble() + large.size > small.size -> large.peek().toDouble() + else -> (small.peek() + large.peek()) / 2.0 + } + } +} +``` + +```swift +class MedianFinder { + // two heaps, large, small, minheap, maxheap + // heaps should be equal size + private var small: Heap + private var large: Heap + + init() { + self.small = Heap() + self.large = Heap() + } + + func addNum(_ num: Int) { + if let top = large.min, num > top { + large.insert(num) + } else { + small.insert(num) + } + if small.count > large.count + 1 { + if let val = small.popMax() { + large.insert(val) + } + } + if large.count > small.count + 1 { + if let val = large.popMin() { + small.insert(val) + } + } + } + + func findMedian() -> Double { + if small.count > large.count { + return Double(small.max!) + } else if large.count > small.count { + return Double(large.min!) + } + return (Double(small.max!) + Double(large.min!)) / 2.0 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * \log n)$ for $addNum()$, $O(m)$ for $findMedian()$. +* Space complexity: $O(n)$ + +> Where $m$ is the number of function calls and $n$ is the length of the array. \ No newline at end of file diff --git a/articles/find-minimum-in-rotated-sorted-array.md b/articles/find-minimum-in-rotated-sorted-array.md new file mode 100644 index 000000000..9443959d2 --- /dev/null +++ b/articles/find-minimum-in-rotated-sorted-array.md @@ -0,0 +1,455 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def findMin(self, nums: List[int]) -> int: + return min(nums) +``` + +```java +public class Solution { + public int findMin(int[] nums) { + return Arrays.stream(nums).min().getAsInt(); + } +} +``` + +```cpp +class Solution { +public: + int findMin(vector& nums) { + return *min_element(nums.begin(), nums.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findMin(nums) { + return Math.min(...nums); + } +} +``` + +```csharp +public class Solution { + public int FindMin(int[] nums) { + return nums.Min(); + } +} +``` + +```go +func findMin(nums []int) int { + minVal := nums[0] + for _, num := range nums { + if num < minVal { + minVal = num + } + } + return minVal +} +``` + +```kotlin +class Solution { + fun findMin(nums: IntArray): Int { + return nums.min() + } +} +``` + +```swift +class Solution { + func findMin(_ nums: [Int]) -> Int { + return nums.min()! + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Binary Search + +::tabs-start + +```python +class Solution: + def findMin(self, nums: List[int]) -> int: + res = nums[0] + l, r = 0, len(nums) - 1 + + while l <= r: + if nums[l] < nums[r]: + res = min(res, nums[l]) + break + + m = (l + r) // 2 + res = min(res, nums[m]) + if nums[m] >= nums[l]: + l = m + 1 + else: + r = m - 1 + return res +``` + +```java +public class Solution { + public int findMin(int[] nums) { + int l = 0; + int r = nums.length - 1; + int res = nums[0]; + + while (l <= r) { + if (nums[l] < nums[r]) { + res = Math.min(res, nums[l]); + break; + } + + int m = l + (r - l) / 2; + res = Math.min(res, nums[m]); + if (nums[m] >= nums[l]) { + l = m + 1; + } else { + r = m - 1; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int findMin(vector &nums) { + int res = nums[0]; + int l = 0; + int r = nums.size() - 1; + + while (l <= r) { + if (nums[l] < nums[r]) { + res = min(res, nums[l]); + break; + } + int m = l + (r - l) / 2; + res = min(res, nums[m]); + + if (nums[m] >= nums[l]) { + l = m + 1; + } else { + r = m - 1; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findMin(nums) { + let l = 0; + let r = nums.length - 1; + let res = nums[0]; + + while (l <= r) { + if (nums[l] <= nums[r]) { + res = Math.min(res, nums[l]); + break; + } + + let m = l + Math.floor((r - l) / 2); + res = Math.min(res, nums[m]); + if (nums[m] >= nums[l]) { + l = m + 1; + } else { + r = m - 1; + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int FindMin(int[] nums) { + int l = 0, r = nums.Length - 1; + int res = nums[0]; + + while (l <= r) { + if (nums[l] < nums[r]) { + res = Math.Min(res, nums[l]); + break; + } + + int m = l + (r - l) / 2; + res = Math.Min(res, nums[m]); + if (nums[m] >= nums[l]) { + l = m + 1; + } else { + r = m - 1; + } + } + return res; + } +} +``` + +```go +func findMin(nums []int) int { + res := nums[0] + l, r := 0, len(nums)-1 + + for l <= r { + if nums[l] < nums[r] { + if nums[l] < res { + res = nums[l] + } + break + } + + m := l + (r-l)/2 + if nums[m] < res { + res = nums[m] + } + + if nums[m] >= nums[l] { + l = m + 1 + } else { + r = m - 1 + } + } + return res +} +``` + +```kotlin +class Solution { + fun findMin(nums: IntArray): Int { + var res = nums[0] + var l = 0 + var r = nums.size - 1 + + while (l <= r) { + if (nums[l] < nums[r]) { + res = minOf(res, nums[l]) + break + } + + val m = l + (r - l) / 2 + res = minOf(res, nums[m]) + + if (nums[m] >= nums[l]) { + l = m + 1 + } else { + r = m - 1 + } + } + return res + } +} +``` + +```swift +class Solution { + func findMin(_ nums: [Int]) -> Int { + var res = nums[0] + var l = 0, r = nums.count - 1 + + while l <= r { + if nums[l] < nums[r] { + res = min(res, nums[l]) + break + } + + let m = (l + r) / 2 + res = min(res, nums[m]) + + if nums[m] >= nums[l] { + l = m + 1 + } else { + r = m - 1 + } + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Binary Search (Lower Bound) + +::tabs-start + +```python +class Solution: + def findMin(self, nums: List[int]) -> int: + l, r = 0, len(nums) - 1 + while l < r: + m = l + (r - l) // 2 + if nums[m] < nums[r]: + r = m + else: + l = m + 1 + return nums[l] +``` + +```java +public class Solution { + public int findMin(int[] nums) { + int l = 0; + int r = nums.length - 1; + + while (l < r) { + int m = l + (r - l) / 2; + if (nums[m] < nums[r]) { + r = m; + } else { + l = m + 1; + } + } + return nums[l]; + } +} +``` + +```cpp +class Solution { +public: + int findMin(vector& nums) { + int l = 0, r = nums.size() - 1; + while (l < r) { + int m = l + (r - l) / 2; + if (nums[m] < nums[r]) { + r = m; + } else { + l = m + 1; + } + } + return nums[l]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findMin(nums) { + let l = 0, r = nums.length - 1; + while (l < r) { + let m = l + Math.floor((r - l) / 2); + if (nums[m] < nums[r]) { + r = m; + } else { + l = m + 1; + } + } + return nums[l]; + } +} +``` + +```csharp +public class Solution { + public int FindMin(int[] nums) { + int l = 0; + int r = nums.Length - 1; + while (l < r) { + int m = l + (r - l) / 2; + if (nums[m] < nums[r]) { + r = m; + } else { + l = m + 1; + } + } + return nums[l]; + } +} +``` + +```go +func findMin(nums []int) int { + l, r := 0, len(nums)-1 + for l < r { + m := l + (r-l)/2 + if nums[m] < nums[r] { + r = m + } else { + l = m + 1 + } + } + return nums[l] +} +``` + +```kotlin +class Solution { + fun findMin(nums: IntArray): Int { + var l = 0 + var r = nums.size - 1 + while (l < r) { + val m = l + (r - l) / 2 + if (nums[m] < nums[r]) { + r = m + } else { + l = m + 1 + } + } + return nums[l] + } +} +``` + +```swift +class Solution { + func findMin(_ nums: [Int]) -> Int { + var l = 0, r = nums.count - 1 + while l < r { + let m = l + (r - l) / 2 + if nums[m] < nums[r] { + r = m + } else { + l = m + 1 + } + } + return nums[l] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/find-missing-and-repeated-values.md b/articles/find-missing-and-repeated-values.md new file mode 100644 index 000000000..58753bb0f --- /dev/null +++ b/articles/find-missing-and-repeated-values.md @@ -0,0 +1,492 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def findMissingAndRepeatedValues(self, grid: List[List[int]]) -> List[int]: + n = len(grid) + double = missing = 0 + + for num in range(1, n * n + 1): + cnt = 0 + for i in range(n): + for j in range(n): + if num == grid[i][j]: + cnt += 1 + + if cnt == 2: + double = num + elif cnt == 0: + missing = num + + return [double, missing] +``` + +```java +public class Solution { + public int[] findMissingAndRepeatedValues(int[][] grid) { + int n = grid.length; + int doubleVal = 0, missing = 0; + + for (int num = 1; num <= n * n; num++) { + int cnt = 0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == num) { + cnt++; + } + } + } + + if (cnt == 2) { + doubleVal = num; + } else if (cnt == 0) { + missing = num; + } + } + + return new int[]{doubleVal, missing}; + } +} +``` + +```cpp +class Solution { +public: + vector findMissingAndRepeatedValues(vector>& grid) { + int n = grid.size(); + int doubleVal = 0, missing = 0; + + for (int num = 1; num <= n * n; num++) { + int cnt = 0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == num) { + cnt++; + } + } + } + + if (cnt == 2) { + doubleVal = num; + } else if (cnt == 0) { + missing = num; + } + } + + return {doubleVal, missing}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number[]} + */ + findMissingAndRepeatedValues(grid) { + const n = grid.length; + let doubleVal = 0, missing = 0; + + for (let num = 1; num <= n * n; num++) { + let cnt = 0; + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + if (grid[i][j] === num) { + cnt++; + } + } + } + + if (cnt === 2) { + doubleVal = num; + } else if (cnt === 0) { + missing = num; + } + } + + return [doubleVal, missing]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 4)$ +* Space complexity: $O(1)$ + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def findMissingAndRepeatedValues(self, grid: List[List[int]]) -> List[int]: + N = len(grid) + count = defaultdict(int) + + for i in range(N): + for j in range(N): + count[grid[i][j]] += 1 + + double = missing = 0 + + for num in range(1, N * N + 1): + if count[num] == 0: + missing = num + if count[num] == 2: + double = num + + return [double, missing] +``` + +```java +public class Solution { + public int[] findMissingAndRepeatedValues(int[][] grid) { + int N = grid.length; + Map count = new HashMap<>(); + + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + count.put(grid[i][j], count.getOrDefault(grid[i][j], 0) + 1); + } + } + + int doubleVal = 0, missing = 0; + + for (int num = 1; num <= N * N; num++) { + int freq = count.getOrDefault(num, 0); + if (freq == 0) missing = num; + if (freq == 2) doubleVal = num; + } + + return new int[]{doubleVal, missing}; + } +} +``` + +```cpp +class Solution { +public: + vector findMissingAndRepeatedValues(vector>& grid) { + int N = grid.size(); + unordered_map count; + + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + count[grid[i][j]]++; + } + } + + int doubleVal = 0, missing = 0; + + for (int num = 1; num <= N * N; num++) { + int freq = count[num]; + if (freq == 0) missing = num; + if (freq == 2) doubleVal = num; + } + + return {doubleVal, missing}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number[]} + */ + findMissingAndRepeatedValues(grid) { + const N = grid.length; + const count = {}; + + for (let i = 0; i < N; i++) { + for (let j = 0; j < N; j++) { + let val = grid[i][j]; + count[val] = (count[val] || 0) + 1; + } + } + + let doubleVal = 0, missing = 0; + + for (let num = 1; num <= N * N; num++) { + let freq = count[num] || 0; + if (freq === 0) missing = num; + if (freq === 2) doubleVal = num; + } + + return [doubleVal, missing]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Hash Set + +::tabs-start + +```python +class Solution: + def findMissingAndRepeatedValues(self, grid: List[List[int]]) -> List[int]: + N = len(grid) + seen = set() + double = missing = 0 + + for i in range(N): + for j in range(N): + if grid[i][j] in seen: + double = grid[i][j] + seen.add(grid[i][j]) + + for num in range(1, N * N + 1): + if num not in seen: + missing = num + break + + return [double, missing] +``` + +```java +public class Solution { + public int[] findMissingAndRepeatedValues(int[][] grid) { + int N = grid.length; + Set seen = new HashSet<>(); + int doubleVal = 0, missing = 0; + + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + if (seen.contains(grid[i][j])) { + doubleVal = grid[i][j]; + } + seen.add(grid[i][j]); + } + } + + for (int num = 1; num <= N * N; num++) { + if (!seen.contains(num)) { + missing = num; + break; + } + } + + return new int[]{doubleVal, missing}; + } +} +``` + +```cpp +class Solution { +public: + vector findMissingAndRepeatedValues(vector>& grid) { + int N = grid.size(); + unordered_set seen; + int doubleVal = 0, missing = 0; + + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + if (seen.count(grid[i][j])) { + doubleVal = grid[i][j]; + } + seen.insert(grid[i][j]); + } + } + + for (int num = 1; num <= N * N; num++) { + if (!seen.count(num)) { + missing = num; + break; + } + } + + return {doubleVal, missing}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number[]} + */ + findMissingAndRepeatedValues(grid) { + const N = grid.length; + const seen = new Set(); + let doubleVal = 0, missing = 0; + + for (let i = 0; i < N; i++) { + for (let j = 0; j < N; j++) { + const val = grid[i][j]; + if (seen.has(val)) { + doubleVal = val; + } + seen.add(val); + } + } + + for (let num = 1; num <= N * N; num++) { + if (!seen.has(num)) { + missing = num; + break; + } + } + + return [doubleVal, missing]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Math + +::tabs-start + +```python +class Solution: + def findMissingAndRepeatedValues(self, grid: List[List[int]]) -> List[int]: + N = len(grid) + gridSum = 0 + gridSqSum = 0 + + for i in range(N): + for j in range(N): + gridSum += grid[i][j] + gridSqSum += grid[i][j] * grid[i][j] + + totSum = (N * N * (N * N + 1)) // 2 + diff = gridSum - totSum # a - b + + totSqSum = (N * N * (N * N + 1) * (2 * N * N + 1)) // 6 + sqDiff = gridSqSum - totSqSum # (a^2) - (b^2) + + sum = sqDiff // diff # a + b + + a = (sum + diff) // 2 + b = sum - a + return [a, b] +``` + +```java +public class Solution { + public int[] findMissingAndRepeatedValues(int[][] grid) { + int N = grid.length; + long gridSum = 0; + long gridSqSum = 0; + + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + gridSum += grid[i][j]; + gridSqSum += 1L * grid[i][j] * grid[i][j]; + } + } + + long totSum = (long) N * N * (N * N + 1) / 2; + long diff = gridSum - totSum; // a - b + + long totSqSum = (long) N * N * (N * N + 1) * (2L * N * N + 1) / 6; + long sqDiff = gridSqSum - totSqSum; // (a^2) - (b^2) + + long sum = sqDiff / diff; // a + b + + long a = (sum + diff) / 2; + long b = sum - a; + + return new int[]{(int) a, (int) b}; + } +} +``` + +```cpp +class Solution { +public: + vector findMissingAndRepeatedValues(vector>& grid) { + int N = grid.size(); + long long gridSum = 0; + long long gridSqSum = 0; + + for (int i = 0; i < N; i++) { + for (int j = 0; j < N; j++) { + gridSum += grid[i][j]; + gridSqSum += 1LL * grid[i][j] * grid[i][j]; + } + } + + long long totSum = 1LL * N * N * (N * N + 1) / 2; + long long diff = gridSum - totSum; // a - b + + long long totSqSum = 1LL * N * N * (N * N + 1) * (2 * N * N + 1) / 6; + long long sqDiff = gridSqSum - totSqSum; // (a^2) - (b^2) + + long long sum = sqDiff / diff; // a + b + + int a = (sum + diff) / 2; + int b = sum - a; + + return {a, b}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number[]} + */ + findMissingAndRepeatedValues(grid) { + const N = grid.length; + let gridSum = 0; + let gridSqSum = 0; + + for (let i = 0; i < N; i++) { + for (let j = 0; j < N; j++) { + gridSum += grid[i][j]; + gridSqSum += grid[i][j] * grid[i][j]; + } + } + + let totSum = N * N * (N * N + 1) / 2; + let diff = gridSum - totSum; // a - b + + let totSqSum = N * N * (N * N + 1) * (2 * N * N + 1) / 6; + let sqDiff = gridSqSum - totSqSum; // (a^2) - (b^2) + + let sum = sqDiff / diff; // a + b + + let a = (sum + diff) / 2; + let b = sum - a; + + return [a, b]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/find-missing-observations.md b/articles/find-missing-observations.md new file mode 100644 index 000000000..218ffed24 --- /dev/null +++ b/articles/find-missing-observations.md @@ -0,0 +1,210 @@ +## 1. Math - I + +::tabs-start + +```python +class Solution: + def missingRolls(self, rolls: List[int], mean: int, n: int) -> List[int]: + m = len(rolls) + nTotal = (mean * (n + m)) - sum(rolls) + + if nTotal < n or nTotal > n * 6: + return [] + + res = [] + while nTotal: + dice = min(nTotal - n + 1, 6) + res.append(dice) + nTotal -= dice + n -= 1 + return res +``` + +```java +public class Solution { + public int[] missingRolls(int[] rolls, int mean, int n) { + int m = rolls.length; + int nTotal = (mean * (n + m)) - Arrays.stream(rolls).sum(); + + if (nTotal < n || nTotal > n * 6) { + return new int[0]; + } + + int[] res = new int[n]; + for (int i = 0; i < n; i++) { + int dice = Math.min(nTotal - (n - i - 1), 6); + res[i] = dice; + nTotal -= dice; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector missingRolls(vector& rolls, int mean, int n) { + int m = rolls.size(); + int nTotal = (mean * (n + m)) - accumulate(rolls.begin(), rolls.end(), 0); + + if (nTotal < n || nTotal > n * 6) { + return {}; + } + + vector res; + for (int i = 0; i < n; ++i) { + int dice = min(nTotal - (n - i - 1), 6); + res.push_back(dice); + nTotal -= dice; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} rolls + * @param {number} mean + * @param {number} n + * @return {number[]} + */ + missingRolls(rolls, mean, n) { + const m = rolls.length; + let nTotal = (mean * (n + m)) - rolls.reduce((sum, roll) => sum + roll, 0); + + if (nTotal < n || nTotal > n * 6) { + return []; + } + + const res = []; + while (nTotal > 0) { + const dice = Math.min(nTotal - n + 1, 6); + res.push(dice); + nTotal -= dice; + n--; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. + +> Where $m$ is the size of the array $rolls$ and $n$ is the number of missing observations. + +--- + +## 2. Math - II + +::tabs-start + +```python +class Solution: + def missingRolls(self, rolls: List[int], mean: int, n: int) -> List[int]: + m = len(rolls) + nTotal = (mean * (n + m)) - sum(rolls) + + if nTotal < n or nTotal > n * 6: + return [] + + avg = nTotal // n + rem = nTotal - (avg * n) + return [avg] * (n - rem) + [avg + 1] * rem +``` + +```java +public class Solution { + public int[] missingRolls(int[] rolls, int mean, int n) { + int m = rolls.length; + int nTotal = (mean * (n + m)) - Arrays.stream(rolls).sum(); + + if (nTotal < n || nTotal > n * 6) { + return new int[0]; + } + + int avg = nTotal / n; + int rem = nTotal - (avg * n); + int[] res = new int[n]; + + for (int i = 0; i < n - rem; i++) { + res[i] = avg; + } + for (int i = n - rem; i < n; i++) { + res[i] = avg + 1; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector missingRolls(vector& rolls, int mean, int n) { + int m = rolls.size(); + int nTotal = (mean * (n + m)) - accumulate(rolls.begin(), rolls.end(), 0); + + if (nTotal < n || nTotal > n * 6) { + return {}; + } + + int avg = nTotal / n; + int rem = nTotal - (avg * n); + vector res; + + for (int i = 0; i < n - rem; ++i) { + res.push_back(avg); + } + for (int i = 0; i < rem; ++i) { + res.push_back(avg + 1); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} rolls + * @param {number} mean + * @param {number} n + * @return {number[]} + */ + missingRolls(rolls, mean, n) { + const m = rolls.length; + const nTotal = (mean * (n + m)) - rolls.reduce((sum, roll) => sum + roll, 0); + + if (nTotal < n || nTotal > n * 6) { + return []; + } + + const avg = Math.floor(nTotal / n); + const rem = nTotal - (avg * n); + return Array(n - rem).fill(avg).concat(Array(rem).fill(avg + 1)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. + +> Where $m$ is the size of the array $rolls$ and $n$ is the number of missing observations. \ No newline at end of file diff --git a/articles/find-peak-element.md b/articles/find-peak-element.md new file mode 100644 index 000000000..9d341f185 --- /dev/null +++ b/articles/find-peak-element.md @@ -0,0 +1,341 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def findPeakElement(self, nums: List[int]) -> int: + for i in range(len(nums) - 1): + if nums[i] > nums[i + 1]: + return i + + return len(nums) - 1 +``` + +```java +public class Solution { + public int findPeakElement(int[] nums) { + for (int i = 0; i < nums.length - 1; i++) { + if (nums[i] > nums[i + 1]) { + return i; + } + } + return nums.length - 1; + } +} +``` + +```cpp +class Solution { +public: + int findPeakElement(vector& nums) { + for (int i = 0; i < nums.size() - 1; i++) { + if (nums[i] > nums[i + 1]) { + return i; + } + } + return nums.size() - 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findPeakElement(nums) { + for (let i = 0; i < nums.length - 1; i++) { + if (nums[i] > nums[i + 1]) { + return i; + } + } + return nums.length - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Binary Search + +::tabs-start + +```python +class Solution: + def findPeakElement(self, nums: List[int]) -> int: + l, r = 0, len(nums) - 1 + + while l <= r: + m = l + (r - l) // 2 + if m > 0 and nums[m] < nums[m - 1]: + r = m - 1 + elif m < len(nums) - 1 and nums[m] < nums[m + 1]: + l = m + 1 + else: + return m +``` + +```java +public class Solution { + public int findPeakElement(int[] nums) { + int l = 0, r = nums.length - 1; + + while (l <= r) { + int m = l + (r - l) / 2; + if (m > 0 && nums[m] < nums[m - 1]) { + r = m - 1; + } else if (m < nums.length - 1 && nums[m] < nums[m + 1]) { + l = m + 1; + } else { + return m; + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int findPeakElement(vector& nums) { + int l = 0, r = nums.size() - 1; + + while (l <= r) { + int m = l + (r - l) / 2; + if (m > 0 && nums[m] < nums[m - 1]) { + r = m - 1; + } else if (m < nums.size() - 1 && nums[m] < nums[m + 1]) { + l = m + 1; + } else { + return m; + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findPeakElement(nums) { + let l = 0, r = nums.length - 1; + + while (l <= r) { + let m = Math.floor(l + (r - l) / 2); + if (m > 0 && nums[m] < nums[m - 1]) { + r = m - 1; + } else if (m < nums.length - 1 && nums[m] < nums[m + 1]) { + l = m + 1; + } else { + return m; + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Recursive Binary Search + +::tabs-start + +```python +class Solution: + def findPeakElement(self, nums: List[int]) -> int: + def binary_search(l, r): + if l == r: + return l + m = l + (r - l) // 2 + if nums[m] > nums[m + 1]: + return binary_search(l, m) + return binary_search(m + 1, r) + + return binary_search(0, len(nums) - 1) +``` + +```java +public class Solution { + public int findPeakElement(int[] nums) { + return binarySearch(nums, 0, nums.length - 1); + } + + private int binarySearch(int[] nums, int l, int r) { + if (l == r) { + return l; + } + int m = l + (r - l) / 2; + if (nums[m] > nums[m + 1]) { + return binarySearch(nums, l, m); + } + return binarySearch(nums, m + 1, r); + } +} +``` + +```cpp +class Solution { +public: + int findPeakElement(vector& nums) { + return binarySearch(nums, 0, nums.size() - 1); + } + +private: + int binarySearch(vector& nums, int l, int r) { + if (l == r) { + return l; + } + int m = l + (r - l) / 2; + if (nums[m] > nums[m + 1]) { + return binarySearch(nums, l, m); + } + return binarySearch(nums, m + 1, r); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findPeakElement(nums) { + const binarySearch = (l, r) => { + if (l === r) { + return l; + } + let m = Math.floor((l + r) / 2); + if (nums[m] > nums[m + 1]) { + return binarySearch(l, m); + } else { + return binarySearch(m + 1, r); + } + }; + + return binarySearch(0, nums.length - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ for recursion stack. + +--- + +## 4. Binary Search (Optimal) + +::tabs-start + +```python +class Solution: + def findPeakElement(self, nums: List[int]) -> int: + l, r = 0, len(nums) - 1 + + while l < r: + m = (l + r) >> 1 + if nums[m] > nums[m + 1]: + r = m + else: + l = m + 1 + + return l +``` + +```java +public class Solution { + public int findPeakElement(int[] nums) { + int l = 0, r = nums.length - 1; + + while (l < r) { + int m = (l + r) >> 1; + if (nums[m] > nums[m + 1]) { + r = m; + } else { + l = m + 1; + } + } + + return l; + } +} +``` + +```cpp +class Solution { +public: + int findPeakElement(vector& nums) { + int l = 0, r = nums.size() - 1; + + while (l < r) { + int m = (l + r) >> 1; + if (nums[m] > nums[m + 1]) { + r = m; + } else { + l = m + 1; + } + } + + return l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findPeakElement(nums) { + let l = 0, r = nums.length - 1; + + while (l < r) { + let m = (l + r) >> 1; + if (nums[m] > nums[m + 1]) { + r = m; + } else { + l = m + 1; + } + } + + return l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/find-pivot-index.md b/articles/find-pivot-index.md new file mode 100644 index 000000000..7e8568d02 --- /dev/null +++ b/articles/find-pivot-index.md @@ -0,0 +1,284 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def pivotIndex(self, nums: List[int]) -> int: + n = len(nums) + for i in range(n): + leftSum = rightSum = 0 + for l in range(i): + leftSum += nums[l] + for r in range(i + 1, n): + rightSum += nums[r] + if leftSum == rightSum: + return i + return -1 +``` + +```java +public class Solution { + public int pivotIndex(int[] nums) { + int n = nums.length; + for (int i = 0; i < n; i++) { + int leftSum = 0, rightSum = 0; + for (int l = 0; l < i; l++) { + leftSum += nums[l]; + } + for (int r = i + 1; r < n; r++) { + rightSum += nums[r]; + } + if (leftSum == rightSum) { + return i; + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int pivotIndex(vector& nums) { + int n = nums.size(); + for (int i = 0; i < n; i++) { + int leftSum = 0, rightSum = 0; + for (int l = 0; l < i; l++) { + leftSum += nums[l]; + } + for (int r = i + 1; r < n; r++) { + rightSum += nums[r]; + } + if (leftSum == rightSum) { + return i; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + pivotIndex(nums) { + const n = nums.length; + for (let i = 0; i < n; i++) { + let leftSum = 0, rightSum = 0; + for (let l = 0; l < i; l++) { + leftSum += nums[l]; + } + for (let r = i + 1; r < n; r++) { + rightSum += nums[r]; + } + if (leftSum === rightSum) { + return i; + } + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Prefix Sum + +::tabs-start + +```python +class Solution: + def pivotIndex(self, nums: List[int]) -> int: + n = len(nums) + prefixSum = [0] * (n + 1) + for i in range(n): + prefixSum[i + 1] = prefixSum[i] + nums[i] + + for i in range(n): + leftSum = prefixSum[i] + rightSum = prefixSum[n] - prefixSum[i + 1] + if leftSum == rightSum: + return i + return -1 +``` + +```java +public class Solution { + public int pivotIndex(int[] nums) { + int n = nums.length; + int[] prefixSum = new int[n + 1]; + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + for (int i = 0; i < n; i++) { + int leftSum = prefixSum[i]; + int rightSum = prefixSum[n] - prefixSum[i + 1]; + if (leftSum == rightSum) { + return i; + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int pivotIndex(vector& nums) { + int n = nums.size(); + vector prefixSum(n + 1, 0); + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + for (int i = 0; i < n; i++) { + int leftSum = prefixSum[i]; + int rightSum = prefixSum[n] - prefixSum[i + 1]; + if (leftSum == rightSum) { + return i; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + pivotIndex(nums) { + const n = nums.length; + const prefixSum = new Array(n + 1).fill(0); + for (let i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + for (let i = 0; i < n; i++) { + const leftSum = prefixSum[i]; + const rightSum = prefixSum[n] - prefixSum[i + 1]; + if (leftSum === rightSum) { + return i; + } + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Prefix Sum (Optimal) + +::tabs-start + +```python +class Solution: + def pivotIndex(self, nums: List[int]) -> int: + total = sum(nums) + leftSum = 0 + for i in range(len(nums)): + rightSum = total - nums[i] - leftSum + if leftSum == rightSum: + return i + leftSum += nums[i] + return -1 +``` + +```java +public class Solution { + public int pivotIndex(int[] nums) { + int total = 0; + for (int num : nums) { + total += num; + } + + int leftSum = 0; + for (int i = 0; i < nums.length; i++) { + int rightSum = total - leftSum - nums[i]; + if (leftSum == rightSum) { + return i; + } + leftSum += nums[i]; + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int pivotIndex(vector& nums) { + int total = 0; + for (int num : nums) { + total += num; + } + + int leftSum = 0; + for (int i = 0; i < nums.size(); i++) { + int rightSum = total - leftSum - nums[i]; + if (leftSum == rightSum) { + return i; + } + leftSum += nums[i]; + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + pivotIndex(nums) { + let total = 0; + for (let num of nums) { + total += num; + } + + let leftSum = 0; + for (let i = 0; i < nums.length; i++) { + let rightSum = total - leftSum - nums[i]; + if (leftSum === rightSum) { + return i; + } + leftSum += nums[i]; + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/find-polygon-with-the-largest-perimeter.md b/articles/find-polygon-with-the-largest-perimeter.md new file mode 100644 index 000000000..f736cb463 --- /dev/null +++ b/articles/find-polygon-with-the-largest-perimeter.md @@ -0,0 +1,295 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def largestPerimeter(self, nums: List[int]) -> int: + n = len(nums) + res = -1 + + for i, large in enumerate(nums): + cur = 0 + for j, side in enumerate(nums): + if i != j and side <= large: + cur += side + if cur > large: + res = max(res, cur + large) + + return res +``` + +```java +public class Solution { + public long largestPerimeter(int[] nums) { + int n = nums.length; + long res = -1; + + for (int i = 0; i < n; i++) { + int large = nums[i]; + long cur = 0; + + for (int j = 0; j < n; j++) { + if (i != j && nums[j] <= large) { + cur += nums[j]; + } + } + + if (cur > large) { + res = Math.max(res, cur + large); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long largestPerimeter(vector& nums) { + int n = nums.size(); + long long res = -1; + + for (int i = 0; i < n; i++) { + long long large = nums[i]; + long long cur = 0; + + for (int j = 0; j < n; j++) { + if (i != j && nums[j] <= large) { + cur += nums[j]; + } + } + + if (cur > large) { + res = max(res, cur + large); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + largestPerimeter(nums) { + const n = nums.length; + let res = -1; + + for (let i = 0; i < n; i++) { + const large = nums[i]; + let cur = 0; + + for (let j = 0; j < n; j++) { + if (i !== j && nums[j] <= large) { + cur += nums[j]; + } + } + + if (cur > large) { + res = Math.max(res, cur + large); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def largestPerimeter(self, nums: List[int]) -> int: + nums.sort() + res = -1 + total = 0 + for num in nums: + if total > num: + res = total + num + total += num + return res +``` + +```java +public class Solution { + public long largestPerimeter(int[] nums) { + Arrays.sort(nums); + long res = -1; + long total = 0; + + for (int num : nums) { + if (total > num) { + res = total + num; + } + total += num; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long largestPerimeter(vector& nums) { + sort(nums.begin(), nums.end()); + long long res = -1; + long long total = 0; + + for (int& num : nums) { + if (total > num) { + res = total + num; + } + total += num; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + largestPerimeter(nums) { + nums.sort((a, b) => a - b); + let res = -1; + let total = 0; + + for (let num of nums) { + if (total > num) { + res = total + num; + } + total += num; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Max Heap + +::tabs-start + +```python +class Solution: + def largestPerimeter(self, nums: List[int]) -> int: + nums = [-num for num in nums] + heapq.heapify(nums) + total = -sum(nums) + + while len(nums) > 2: + largest = -heapq.heappop(nums) + total -= largest + if largest < total: + return total + largest + return -1 +``` + +```java +public class Solution { + public long largestPerimeter(int[] nums) { + PriorityQueue maxHeap = new PriorityQueue<>((a, b) -> b - a); + long total = 0; + for (int num : nums) { + maxHeap.add(num); + total += num; + } + + while (maxHeap.size() > 2) { + int largest = maxHeap.poll(); + total -= largest; + if (largest < total) { + return total + largest; + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + long long largestPerimeter(vector& nums) { + priority_queue maxHeap(nums.begin(), nums.end()); + long long total = accumulate(nums.begin(), nums.end(), 0LL); + + while (maxHeap.size() > 2) { + int largest = maxHeap.top(); + maxHeap.pop(); + total -= largest; + if (largest < total) { + return total + largest; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + largestPerimeter(nums) { + const maxHeap = new MaxPriorityQueue(); + let total = 0; + + nums.forEach(num => { + total += num; + maxHeap.enqueue(num); + }); + + while (maxHeap.size() > 2) { + const largest = maxHeap.dequeue().element; + total -= largest; + if (largest < total) return total + largest; + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n + (30\log n))$ in Python, C++, JS. + * $O(n \log n)$ in Java. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/find-target-in-rotated-sorted-array.md b/articles/find-target-in-rotated-sorted-array.md new file mode 100644 index 000000000..b406838bb --- /dev/null +++ b/articles/find-target-in-rotated-sorted-array.md @@ -0,0 +1,1040 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def search(self, nums: List[int], target: int) -> int: + for i in range(len(nums)): + if nums[i] == target: + return i + return -1 +``` + +```java +class Solution { + public int search(int[] nums, int target) { + for (int i = 0; i < nums.length; i++) { + if (nums[i] == target) { + return i; + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int search(vector& nums, int target) { + for (int i = 0; i < nums.size(); i++) { + if (nums[i] == target) { + return i; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + search(nums, target) { + for (let i = 0; i < nums.length; i++) { + if (nums[i] == target) { + return i; + } + } + return -1; + } +} +``` + +```csharp +public class Solution { + public int Search(int[] nums, int target) { + for (int i = 0; i < nums.Length; i++) { + if (nums[i] == target) { + return i; + } + } + return -1; + } +} +``` + +```go +func search(nums []int, target int) int { + for i := 0; i < len(nums); i++ { + if nums[i] == target { + return i + } + } + return -1 +} +``` + +```kotlin +class Solution { + fun search(nums: IntArray, target: Int): Int { + for (i in nums.indices) { + if (nums[i] == target) { + return i + } + } + return -1 + } +} +``` + +```swift +class Solution { + func search(_ nums: [Int], _ target: Int) -> Int { + for i in 0.. int: + l, r = 0, len(nums) - 1 + + while l < r: + m = (l + r) // 2 + if nums[m] > nums[r]: + l = m + 1 + else: + r = m + + pivot = l + + def binary_search(left: int, right: int) -> int: + while left <= right: + mid = (left + right) // 2 + if nums[mid] == target: + return mid + elif nums[mid] < target: + left = mid + 1 + else: + right = mid - 1 + return -1 + + result = binary_search(0, pivot - 1) + if result != -1: + return result + + return binary_search(pivot, len(nums) - 1) +``` + +```java +public class Solution { + public int search(int[] nums, int target) { + int l = 0, r = nums.length - 1; + + while (l < r) { + int m = (l + r) / 2; + if (nums[m] > nums[r]) { + l = m + 1; + } else { + r = m; + } + } + + int pivot = l; + + int result = binarySearch(nums, target, 0, pivot - 1); + if (result != -1) { + return result; + } + + return binarySearch(nums, target, pivot, nums.length - 1); + } + + public int binarySearch(int[] nums, int target, int left, int right) { + while (left <= right) { + int mid = (left + right) / 2; + if (nums[mid] == target) { + return mid; + } else if (nums[mid] < target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int search(vector& nums, int target) { + int l = 0, r = nums.size() - 1; + + while (l < r) { + int m = (l + r) / 2; + if (nums[m] > nums[r]) { + l = m + 1; + } else { + r = m; + } + } + + int pivot = l; + + int result = binarySearch(nums, target, 0, pivot - 1); + if (result != -1) { + return result; + } + + return binarySearch(nums, target, pivot, nums.size() - 1); + } + + int binarySearch(vector& nums, int target, int left, int right) { + while (left <= right) { + int mid = (left + right) / 2; + if (nums[mid] == target) { + return mid; + } else if (nums[mid] < target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + search(nums, target) { + let l = 0; + let r = nums.length - 1; + + while (l < r) { + const m = Math.floor((l + r) / 2); + if (nums[m] > nums[r]) { + l = m + 1; + } else { + r = m; + } + } + + const pivot = l; + + const result = this.binarySearch(nums, target, 0, pivot - 1); + if (result !== -1) { + return result; + } + + return this.binarySearch(nums, target, pivot, nums.length - 1); + } + + /** + * @param {number[]} nums + * @param {number} target + * @param {number} left + * @param {number} right + * @return {number} + */ + binarySearch(nums, target, left, right) { + while (left <= right) { + const mid = Math.floor((left + right) / 2); + if (nums[mid] === target) { + return mid; + } else if (nums[mid] < target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return -1; + } +} +``` + +```csharp +public class Solution { + public int Search(int[] nums, int target) { + int l = 0, r = nums.Length - 1; + + while (l < r) { + int m = (l + r) / 2; + if (nums[m] > nums[r]) { + l = m + 1; + } else { + r = m; + } + } + + int pivot = l; + + int result = BinarySearch(nums, target, 0, pivot - 1); + if (result != -1) { + return result; + } + + return BinarySearch(nums, target, pivot, nums.Length - 1); + } + + public int BinarySearch(int[] nums, int target, int left, int right) { + while (left <= right) { + int mid = (left + right) / 2; + if (nums[mid] == target) { + return mid; + } else if (nums[mid] < target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return -1; + } +} +``` + +```go +func search(nums []int, target int) int { + l, r := 0, len(nums)-1 + + for l < r { + m := (l + r) / 2 + if nums[m] > nums[r] { + l = m + 1 + } else { + r = m + } + } + + pivot := l + + var binarySearch func(left, right int) int + binarySearch = func(left, right int) int { + for left <= right { + mid := (left + right) / 2 + if nums[mid] == target { + return mid + } else if nums[mid] < target { + left = mid + 1 + } else { + right = mid - 1 + } + } + return -1 + } + + result := binarySearch(0, pivot-1) + if result != -1 { + return result + } + + return binarySearch(pivot, len(nums)-1) +} +``` + +```kotlin +class Solution { + fun search(nums: IntArray, target: Int): Int { + var l = 0 + var r = nums.size - 1 + + while (l < r) { + val m = (l + r) / 2 + if (nums[m] > nums[r]) { + l = m + 1 + } else { + r = m + } + } + + val pivot = l + + fun binarySearch(left: Int, right: Int): Int { + var left = left + var right = right + while (left <= right) { + val mid = (left + right) / 2 + when { + nums[mid] == target -> return mid + nums[mid] < target -> left = mid + 1 + else -> right = mid - 1 + } + } + return -1 + } + + var result = binarySearch(0, pivot - 1) + if (result != -1) { + return result + } + + return binarySearch(pivot, nums.size - 1) + } +} +``` + +```swift +class Solution { + func search(_ nums: [Int], _ target: Int) -> Int { + var l = 0, r = nums.count - 1 + + while l < r { + let m = (l + r) / 2 + if nums[m] > nums[r] { + l = m + 1 + } else { + r = m + } + } + + let pivot = l + + func binarySearch(_ left: Int, _ right: Int) -> Int { + var l = left, r = right + while l <= r { + let mid = (l + r) / 2 + if nums[mid] == target { + return mid + } else if nums[mid] < target { + l = mid + 1 + } else { + r = mid - 1 + } + } + return -1 + } + + let result = binarySearch(0, pivot - 1) + if result != -1 { + return result + } + + return binarySearch(pivot, nums.count - 1) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Binary Search (Two Pass) + +::tabs-start + +```python +class Solution: + def search(self, nums: List[int], target: int) -> int: + l, r = 0, len(nums) - 1 + + while l < r: + m = (l + r) // 2 + if nums[m] > nums[r]: + l = m + 1 + else: + r = m + + pivot = l + l, r = 0, len(nums) - 1 + + if target >= nums[pivot] and target <= nums[r]: + l = pivot + else: + r = pivot - 1 + + while l <= r: + m = (l + r) // 2 + if nums[m] == target: + return m + elif nums[m] < target: + l = m + 1 + else: + r = m - 1 + + return -1 +``` + +```java +public class Solution { + public int search(int[] nums, int target) { + int l = 0, r = nums.length - 1; + + while (l < r) { + int m = (l + r) / 2; + if (nums[m] > nums[r]) { + l = m + 1; + } else { + r = m; + } + } + + int pivot = l; + l = 0; + r = nums.length - 1; + + if (target >= nums[pivot] && target <= nums[r]) { + l = pivot; + } else { + r = pivot - 1; + } + + while (l <= r) { + int m = (l + r) / 2; + if (nums[m] == target) { + return m; + } else if (nums[m] < target) { + l = m + 1; + } else { + r = m - 1; + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int search(vector& nums, int target) { + int l = 0, r = nums.size() - 1; + + while (l < r) { + int m = (l + r) / 2; + if (nums[m] > nums[r]) { + l = m + 1; + } else { + r = m; + } + } + + int pivot = l; + l = 0; + r = nums.size() - 1; + + if (target >= nums[pivot] && target <= nums[r]) { + l = pivot; + } else { + r = pivot - 1; + } + + while (l <= r) { + int m = (l + r) / 2; + if (nums[m] == target) { + return m; + } else if (nums[m] < target) { + l = m + 1; + } else { + r = m - 1; + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + search(nums, target) { + let l = 0, r = nums.length - 1; + + while (l < r) { + let m = Math.floor((l + r) / 2); + if (nums[m] > nums[r]) { + l = m + 1; + } else { + r = m; + } + } + + let pivot = l; + l = 0; + r = nums.length - 1; + + if (target >= nums[pivot] && target <= nums[r]) { + l = pivot; + } else { + r = pivot - 1; + } + + while (l <= r) { + let m = Math.floor((l + r) / 2); + if (nums[m] === target) { + return m; + } else if (nums[m] < target) { + l = m + 1; + } else { + r = m - 1; + } + } + + return -1; + } +} +``` + +```csharp +public class Solution { + public int Search(int[] nums, int target) { + int l = 0, r = nums.Length - 1; + + while (l < r) { + int m = (l + r) / 2; + if (nums[m] > nums[r]) { + l = m + 1; + } else { + r = m; + } + } + + int pivot = l; + l = 0; + r = nums.Length - 1; + + if (target >= nums[pivot] && target <= nums[r]) { + l = pivot; + } else { + r = pivot - 1; + } + + while (l <= r) { + int m = (l + r) / 2; + if (nums[m] == target) { + return m; + } else if (nums[m] < target) { + l = m + 1; + } else { + r = m - 1; + } + } + + return -1; + } +} +``` + +```go +func search(nums []int, target int) int { + l, r := 0, len(nums)-1 + + for l < r { + m := (l + r) / 2 + if nums[m] > nums[r] { + l = m + 1 + } else { + r = m + } + } + + pivot := l + l, r = 0, len(nums)-1 + + if target >= nums[pivot] && target <= nums[r] { + l = pivot + } else { + r = pivot - 1 + } + + for l <= r { + m := (l + r) / 2 + if nums[m] == target { + return m + } else if nums[m] < target { + l = m + 1 + } else { + r = m - 1 + } + } + + return -1 +} +``` + +```kotlin +class Solution { + fun search(nums: IntArray, target: Int): Int { + var l = 0 + var r = nums.size - 1 + + while (l < r) { + val m = (l + r) / 2 + if (nums[m] > nums[r]) { + l = m + 1 + } else { + r = m + } + } + + val pivot = l + l = 0 + r = nums.size - 1 + + if (target >= nums[pivot] && target <= nums[r]) { + l = pivot + } else { + r = pivot - 1 + } + + while (l <= r) { + val m = (l + r) / 2 + if (nums[m] == target) { + return m + } else if (nums[m] < target) { + l = m + 1 + } else { + r = m - 1 + } + } + + return -1 + } +} +``` + +```swift +class Solution { + func search(_ nums: [Int], _ target: Int) -> Int { + var l = 0, r = nums.count - 1 + + while l < r { + let m = (l + r) / 2 + if nums[m] > nums[r] { + l = m + 1 + } else { + r = m + } + } + + let pivot = l + l = 0 + r = nums.count - 1 + + if target >= nums[pivot] && target <= nums[r] { + l = pivot + } else { + r = pivot - 1 + } + + while l <= r { + let m = (l + r) / 2 + if nums[m] == target { + return m + } else if nums[m] < target { + l = m + 1 + } else { + r = m - 1 + } + } + + return -1 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Binary Search (One Pass) + +::tabs-start + +```python +class Solution: + def search(self, nums: List[int], target: int) -> int: + l, r = 0, len(nums) - 1 + + while l <= r: + mid = (l + r) // 2 + if target == nums[mid]: + return mid + + if nums[l] <= nums[mid]: + if target > nums[mid] or target < nums[l]: + l = mid + 1 + else: + r = mid - 1 + + else: + if target < nums[mid] or target > nums[r]: + r = mid - 1 + else: + l = mid + 1 + return -1 +``` + +```java +class Solution { + public int search(int[] nums, int target) { + int l = 0; + int r = nums.length - 1; + + while(l <= r) { + + int mid = (l + r) / 2; + + if (nums[mid] == target) { + return mid; + } + + if (nums[l] <= nums[mid]) { + if (target > nums[mid] || target < nums[l]) { + l = mid + 1; + } else { + r = mid - 1; + } + } else { + if (target < nums[mid] || target > nums [r]) { + r = mid - 1; + } else { + l = mid + 1; + } + } + + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int search(std::vector& nums, int target) { + int l = 0, r = nums.size() - 1; + + while (l <= r) { + int mid = (l + r) / 2; + if (target == nums[mid]) { + return mid; + } + + if (nums[l] <= nums[mid]) { + if (target > nums[mid] || target < nums[l]) { + l = mid + 1; + } else { + r = mid - 1; + } + } else { + if (target < nums[mid] || target > nums[r]) { + r = mid - 1; + } else { + l = mid + 1; + } + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + search(nums, target) { + let l = 0, r = nums.length - 1; + + while (l <= r) { + const mid = Math.floor((l + r) / 2); + if (target === nums[mid]) { + return mid; + } + + if (nums[l] <= nums[mid]) { + if (target > nums[mid] || target < nums[l]) { + l = mid + 1; + } else { + r = mid - 1; + } + } else { + if (target < nums[mid] || target > nums[r]) { + r = mid - 1; + } else { + l = mid + 1; + } + } + } + return -1; + } +} +``` + +```csharp +public class Solution { + public int Search(int[] nums, int target) { + int l = 0, r = nums.Length - 1; + + while (l <= r) { + int mid = (l + r) / 2; + if (target == nums[mid]) { + return mid; + } + + if (nums[l] <= nums[mid]) { + if (target > nums[mid] || target < nums[l]) { + l = mid + 1; + } else { + r = mid - 1; + } + } else { + if (target < nums[mid] || target > nums[r]) { + r = mid - 1; + } else { + l = mid + 1; + } + } + } + return -1; + } +} +``` + +```go +func search(nums []int, target int) int { + l, r := 0, len(nums)-1 + + for l <= r { + mid := (l + r) / 2 + if target == nums[mid] { + return mid + } + + if nums[l] <= nums[mid] { + if target > nums[mid] || target < nums[l] { + l = mid + 1 + } else { + r = mid - 1 + } + } else { + if target < nums[mid] || target > nums[r] { + r = mid - 1 + } else { + l = mid + 1 + } + } + } + return -1 +} +``` + +```kotlin +class Solution { + fun search(nums: IntArray, target: Int): Int { + var l = 0 + var r = nums.size - 1 + + while (l <= r) { + val mid = (l + r) / 2 + if (target == nums[mid]) { + return mid + } + + if (nums[l] <= nums[mid]) { + if (target > nums[mid] || target < nums[l]) { + l = mid + 1 + } else { + r = mid - 1 + } + } else { + if (target < nums[mid] || target > nums[r]) { + r = mid - 1 + } else { + l = mid + 1 + } + } + } + return -1 + } +} +``` + +```swift +class Solution { + func search(_ nums: [Int], _ target: Int) -> Int { + var l = 0, r = nums.count - 1 + + while l <= r { + let mid = (l + r) / 2 + if target == nums[mid] { + return mid + } + + if nums[l] <= nums[mid] { + if target > nums[mid] || target < nums[l] { + l = mid + 1 + } else { + r = mid - 1 + } + } else { + if target < nums[mid] || target > nums[r] { + r = mid - 1 + } else { + l = mid + 1 + } + } + } + return -1 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/find-the-difference-of-two-arrays.md b/articles/find-the-difference-of-two-arrays.md new file mode 100644 index 000000000..853638267 --- /dev/null +++ b/articles/find-the-difference-of-two-arrays.md @@ -0,0 +1,467 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def findDifference(self, nums1: list[int], nums2: list[int]) -> list[list[int]]: + res = [set(), set()] + + for num1 in nums1: + found = False + for num2 in nums2: + if num1 == num2: + found = True + break + if not found: + res[0].add(num1) + + for num2 in nums2: + found = False + for num1 in nums1: + if num1 == num2: + found = True + break + if not found: + res[1].add(num2) + + return [list(res[0]), list(res[1])] +``` + +```java +public class Solution { + public List> findDifference(int[] nums1, int[] nums2) { + Set res1 = new HashSet<>(); + Set res2 = new HashSet<>(); + + for (int num1 : nums1) { + boolean found = false; + for (int num2 : nums2) { + if (num1 == num2) { + found = true; + break; + } + } + if (!found) { + res1.add(num1); + } + } + + for (int num2 : nums2) { + boolean found = false; + for (int num1 : nums1) { + if (num1 == num2) { + found = true; + break; + } + } + if (!found) { + res2.add(num2); + } + } + + List> result = new ArrayList<>(); + result.add(new ArrayList<>(res1)); + result.add(new ArrayList<>(res2)); + return result; + } +} +``` + +```cpp +class Solution { +public: + vector> findDifference(vector& nums1, vector& nums2) { + set res1, res2; + + for (int num1 : nums1) { + bool found = false; + for (int num2 : nums2) { + if (num1 == num2) { + found = true; + break; + } + } + if (!found) { + res1.insert(num1); + } + } + + for (int num2 : nums2) { + bool found = false; + for (int num1 : nums1) { + if (num1 == num2) { + found = true; + break; + } + } + if (!found) { + res2.insert(num2); + } + } + + return {vector(res1.begin(), res1.end()), + vector(res2.begin(), res2.end())}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[][]} + */ + findDifference(nums1, nums2) { + const res1 = new Set(); + const res2 = new Set(); + + for (const num1 of nums1) { + let found = false; + for (const num2 of nums2) { + if (num1 === num2) { + found = true; + break; + } + } + if (!found) { + res1.add(num1); + } + } + + for (const num2 of nums2) { + let found = false; + for (const num1 of nums1) { + if (num1 === num2) { + found = true; + break; + } + } + if (!found) { + res2.add(num2); + } + } + + return [Array.from(res1), Array.from(res2)]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums1$ and $m$ is the size of the array $nums2$. + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def findDifference(self, nums1: list[int], nums2: list[int]) -> list[list[int]]: + nums1.sort() + nums2.sort() + + def helper(A, B): + n, m = len(A), len(B) + res = [] + + j = 0 + prev = float('-inf') + for num in A: + if prev == num: + continue + while j < m and B[j] < num: + j += 1 + if j == m or B[j] != num: + res.append(num) + prev = num + return res + + return [helper(nums1, nums2), helper(nums2, nums1)] +``` + +```java +public class Solution { + public List> findDifference(int[] nums1, int[] nums2) { + Arrays.sort(nums1); + Arrays.sort(nums2); + + List diff1 = helper(nums1, nums2); + List diff2 = helper(nums2, nums1); + + List> result = new ArrayList<>(); + result.add(diff1); + result.add(diff2); + + return result; + } + + private List helper(int[] A, int[] B) { + int n = A.length, m = B.length, j = 0; + List res = new ArrayList<>(); + int prev = Integer.MIN_VALUE; + + for (int num : A) { + if (num == prev) continue; + while (j < m && B[j] < num) j++; + if (j == m || B[j] != num) res.add(num); + prev = num; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> findDifference(vector& nums1, vector& nums2) { + sort(nums1.begin(), nums1.end()); + sort(nums2.begin(), nums2.end()); + + return {helper(nums1, nums2), helper(nums2, nums1)}; + } + +private: + vector helper(vector& A, vector& B) { + vector res; + int n = A.size(), m = B.size(), j = 0, prev = INT_MIN; + + for (int num : A) { + if (num == prev) continue; + while (j < m && B[j] < num) j++; + if (j == m || B[j] != num) res.push_back(num); + prev = num; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[][]} + */ + findDifference(nums1, nums2) { + nums1.sort((a, b) => a - b); + nums2.sort((a, b) => a - b); + + const helper = (A, B) => { + const res = []; + let j = 0; + let prev = -Infinity; + + for (const num of A) { + if (num === prev) continue; + while (j < B.length && B[j] < num) j++; + if (j === B.length || B[j] !== num) res.push(num); + prev = num; + } + + return res; + }; + + return [helper(nums1, nums2), helper(nums2, nums1)]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + m \log m)$ +* Space complexity: $O(1)$ or $O(n + m)$ depending on the sorting algorithm. + +> Where $n$ is the size of the array $nums1$ and $m$ is the size of the array $nums2$. + +--- + +## 3. Hash Set + +::tabs-start + +```python +class Solution: + def findDifference(self, nums1: List[int], nums2: List[int]) -> List[List[int]]: + num1Set, num2Set = set(nums1), set(nums2) + res1, res2 = [], [] + + for num in num1Set: + if num not in num2Set: + res1.append(num) + + for num in num2Set: + if num not in num1Set: + res2.append(num) + + return [res1, res2] +``` + +```java +public class Solution { + public List> findDifference(int[] nums1, int[] nums2) { + Set num1Set = new HashSet<>(); + Set num2Set = new HashSet<>(); + for (int num : nums1) num1Set.add(num); + for (int num : nums2) num2Set.add(num); + + List res1 = new ArrayList<>(); + List res2 = new ArrayList<>(); + + for (int num : num1Set) { + if (!num2Set.contains(num)) res1.add(num); + } + + for (int num : num2Set) { + if (!num1Set.contains(num)) res2.add(num); + } + + return Arrays.asList(res1, res2); + } +} +``` + +```cpp +class Solution { +public: + vector> findDifference(vector& nums1, vector& nums2) { + unordered_set num1Set(nums1.begin(), nums1.end()); + unordered_set num2Set(nums2.begin(), nums2.end()); + vector res1, res2; + + for (int num : num1Set) { + if (num2Set.find(num) == num2Set.end()) res1.push_back(num); + } + + for (int num : num2Set) { + if (num1Set.find(num) == num1Set.end()) res2.push_back(num); + } + + return {res1, res2}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[][]} + */ + findDifference(nums1, nums2) { + const num1Set = new Set(nums1); + const num2Set = new Set(nums2); + const res1 = []; + const res2 = []; + + for (const num of num1Set) { + if (!num2Set.has(num)) res1.push(num); + } + + for (const num of num2Set) { + if (!num1Set.has(num)) res2.push(num); + } + + return [res1, res2]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums1$ and $m$ is the size of the array $nums2$. + +--- + +## 4. Hash Set Difference + +::tabs-start + +```python +class Solution: + def findDifference(self, nums1: List[int], nums2: List[int]) -> List[List[int]]: + numSet1, numSet2 = set(nums1), set(nums2) + return [list(numSet1 - numSet2), list(numSet2 - numSet1)] +``` + +```java +public class Solution { + public List> findDifference(int[] nums1, int[] nums2) { + Set numSet1 = new HashSet<>(); + Set numSet2 = new HashSet<>(); + for (int num : nums1) numSet1.add(num); + for (int num : nums2) numSet2.add(num); + + List res1 = new ArrayList<>(numSet1); + res1.removeAll(numSet2); + + List res2 = new ArrayList<>(numSet2); + res2.removeAll(numSet1); + + return Arrays.asList(res1, res2); + } +} +``` + +```cpp +class Solution { +public: + vector> findDifference(vector& nums1, vector& nums2) { + vector res1, res2; + set numSet1(begin(nums1), end(nums1)), numSet2(begin(nums2), end(nums2)); + + set_difference(begin(numSet1), end(numSet1), begin(numSet2), end(numSet2), back_inserter(res1)); + set_difference(begin(numSet2), end(numSet2), begin(numSet1), end(numSet1), back_inserter(res2)); + + return {res1, res2}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[][]} + */ + findDifference(nums1, nums2) { + const numSet1 = new Set(nums1); + const numSet2 = new Set(nums2); + + const res1 = Array.from(numSet1).filter(num => !numSet2.has(num)); + const res2 = Array.from(numSet2).filter(num => !numSet1.has(num)); + + return [res1, res2]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums1$ and $m$ is the size of the array $nums2$. \ No newline at end of file diff --git a/articles/find-the-difference.md b/articles/find-the-difference.md new file mode 100644 index 000000000..b06b2803f --- /dev/null +++ b/articles/find-the-difference.md @@ -0,0 +1,466 @@ +## 1. Two Hash Maps + +::tabs-start + +```python +class Solution: + def findTheDifference(self, s: str, t: str) -> str: + count_s, count_t = Counter(s), Counter(t) + for c in count_t: + if c not in count_s or count_s[c] < count_t[c]: + return c +``` + +```java +public class Solution { + public char findTheDifference(String s, String t) { + int[] countS = new int[26]; + int[] countT = new int[26]; + + for (char c : s.toCharArray()) countS[c - 'a']++; + for (char c : t.toCharArray()) countT[c - 'a']++; + + for (int i = 0; i < 26; i++) { + if (countT[i] > countS[i]) { + return (char) (i + 'a'); + } + } + return ' '; + } +} +``` + +```cpp +class Solution { +public: + char findTheDifference(string s, string t) { + vector countS(26, 0), countT(26, 0); + + for (char c : s) countS[c - 'a']++; + for (char c : t) countT[c - 'a']++; + + for (int i = 0; i < 26; i++) { + if (countT[i] > countS[i]) { + return i + 'a'; + } + } + return ' '; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {character} + */ + findTheDifference(s, t) { + let countS = Array(26).fill(0); + let countT = Array(26).fill(0); + + for (let char of s) countS[char.charCodeAt(0) - 'a'.charCodeAt(0)]++; + for (let char of t) countT[char.charCodeAt(0) - 'a'.charCodeAt(0)]++; + + for (let i = 0; i < 26; i++) { + if (countT[i] > countS[i]) { + return String.fromCharCode(i + 'a'.charCodeAt(0)); + } + } + return ''; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +--- + +## 2. One Hash Map + +::tabs-start + +```python +class Solution: + def findTheDifference(self, s: str, t: str) -> str: + count = Counter(t) + for c in s: + count[c] -= 1 + for c in count: + if count[c] == 1: + return c +``` + +```java +public class Solution { + public char findTheDifference(String s, String t) { + int[] count = new int[26]; + + for (char c : t.toCharArray()) count[c - 'a']++; + for (char c : s.toCharArray()) count[c - 'a']--; + + for (int i = 0; i < 26; i++) { + if (count[i] == 1) { + return (char) (i + 'a'); + } + } + return ' '; + } +} +``` + +```cpp +class Solution { +public: + char findTheDifference(string s, string t) { + vector count(26); + + for (char c : t) count[c - 'a']++; + for (char c : s) count[c - 'a']--; + + for (int i = 0; i < 26; i++) { + if (count[i] == 1) { + return i + 'a'; + } + } + return ' '; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {character} + */ + findTheDifference(s, t) { + let count = Array(26).fill(0); + + for (let char of t) count[char.charCodeAt(0) - 'a'.charCodeAt(0)]++; + for (let char of s) count[char.charCodeAt(0) - 'a'.charCodeAt(0)]--; + + for (let i = 0; i < 26; i++) { + if (count[i] === 1) { + return String.fromCharCode(i + 'a'.charCodeAt(0)); + } + } + return ''; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +--- + +## 3. Sorting + +::tabs-start + +```python +class Solution: + def findTheDifference(self, s: str, t: str) -> str: + s, t = sorted(s), sorted(t) + for c1, c2 in zip(s, t): + if c1 != c2: + return c2 + return t[-1] +``` + +```java +public class Solution { + public char findTheDifference(String s, String t) { + char[] sArr = s.toCharArray(); + char[] tArr = t.toCharArray(); + Arrays.sort(sArr); + Arrays.sort(tArr); + for (int i = 0; i < sArr.length; i++) { + if (sArr[i] != tArr[i]) { + return tArr[i]; + } + } + return tArr[tArr.length - 1]; + } +} +``` + +```cpp +class Solution { +public: + char findTheDifference(string s, string t) { + sort(s.begin(), s.end()); + sort(t.begin(), t.end()); + for (int i = 0; i < s.size(); i++) { + if (s[i] != t[i]) { + return t[i]; + } + } + return t[t.size() - 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {character} + */ + findTheDifference(s, t) { + s = s.split('').sort(); + t = t.split('').sort(); + for (let i = 0; i < s.length; i++) { + if (s[i] !== t[i]) { + return t[i]; + } + } + return t[t.length - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 4. Difference Between ASCII Values + +::tabs-start + +```python +class Solution: + def findTheDifference(self, s: str, t: str) -> str: + sum_s, sum_t = 0, 0 + for c in s: + sum_s += ord(c) + for c in t: + sum_t += ord(c) + return chr(sum_t - sum_s) +``` + +```java +public class Solution { + public char findTheDifference(String s, String t) { + int sumS = 0, sumT = 0; + for (int i = 0; i < s.length(); i++) { + sumS += s.charAt(i); + } + for (int i = 0; i < t.length(); i++) { + sumT += t.charAt(i); + } + return (char) (sumT - sumS); + } +} +``` + +```cpp +class Solution { +public: + char findTheDifference(string s, string t) { + int sumS = 0, sumT = 0; + for (char c : s) { + sumS += c; + } + for (char c : t) { + sumT += c; + } + return (char) (sumT - sumS); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {character} + */ + findTheDifference(s, t) { + let sumS = 0, sumT = 0; + for (let char of s) { + sumS += char.charCodeAt(0); + } + for (let char of t) { + sumT += char.charCodeAt(0); + } + return String.fromCharCode(sumT - sumS); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 5. Difference Between ASCII Values (Optimal) + +::tabs-start + +```python +class Solution: + def findTheDifference(self, s: str, t: str) -> str: + res = 0 + for c in s: + res -= ord(c) + for c in t: + res += ord(c) + return chr(res) +``` + +```java +public class Solution { + public char findTheDifference(String s, String t) { + int res = 0; + for (int i = 0; i < s.length(); i++) { + res -= s.charAt(i); + } + for (int i = 0; i < t.length(); i++) { + res += t.charAt(i); + } + return (char) (res); + } +} +``` + +```cpp +class Solution { +public: + char findTheDifference(string s, string t) { + int res = 0; + for (char c : s) { + res -= c; + } + for (char c : t) { + res += c; + } + return (char) (res); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {character} + */ + findTheDifference(s, t) { + let res = 0; + for (let char of s) { + res -= char.charCodeAt(0); + } + for (let char of t) { + res += char.charCodeAt(0); + } + return String.fromCharCode(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 6. Bitwise XOR + +::tabs-start + +```python +class Solution: + def findTheDifference(self, s: str, t: str) -> str: + res = 0 + for c in s: + res ^= ord(c) + for c in t: + res ^= ord(c) + return chr(res) +``` + +```java +public class Solution { + public char findTheDifference(String s, String t) { + int res = 0; + for (int i = 0; i < s.length(); i++) { + res ^= s.charAt(i); + } + for (int i = 0; i < t.length(); i++) { + res ^= t.charAt(i); + } + return (char) (res); + } +} +``` + +```cpp +class Solution { +public: + char findTheDifference(string s, string t) { + int res = 0; + for (char c : s) { + res ^= c; + } + for (char c : t) { + res ^= c; + } + return (char) (res); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {character} + */ + findTheDifference(s, t) { + let res = 0; + for (let char of s) { + res ^= char.charCodeAt(0); + } + for (let char of t) { + res ^= char.charCodeAt(0); + } + return String.fromCharCode(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/find-the-index-of-the-first-occurrence-in-a-string.md b/articles/find-the-index-of-the-first-occurrence-in-a-string.md new file mode 100644 index 000000000..beb5b5893 --- /dev/null +++ b/articles/find-the-index-of-the-first-occurrence-in-a-string.md @@ -0,0 +1,633 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def strStr(self, haystack: str, needle: str) -> int: + n, m = len(haystack), len(needle) + for i in range(n - m + 1): + j = 0 + while j < m: + if haystack[i + j] != needle[j]: + break + j += 1 + if j == m: + return i + return -1 +``` + +```java +public class Solution { + public int strStr(String haystack, String needle) { + int n = haystack.length(), m = needle.length(); + for (int i = 0; i < n - m + 1; i++) { + int j = 0; + while (j < m) { + if (haystack.charAt(i + j) != needle.charAt(j)) { + break; + } + j++; + } + if (j == m) return i; + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int strStr(string haystack, string needle) { + int n = haystack.length(), m = needle.length(); + for (int i = 0; i < n - m + 1; i++) { + int j = 0; + while (j < m) { + if (haystack[i + j] != needle[j]) { + break; + } + j++; + } + if (j == m) return i; + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} haystack + * @param {string} needle + * @return {number} + */ + strStr(haystack, needle) { + let n = haystack.length, m = needle.length; + for (let i = 0; i < n - m + 1; i++) { + let j = 0; + while (j < m) { + if (haystack[i + j] !== needle[j]) { + break; + } + j++; + } + if (j === m) return i; + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(1)$ + +> Where $n$ is the length of the string $heystack$ and $m$ is the length of the string $needle$. + +--- + +## 2. Knuth-Morris-Pratt (KMP) Algorithm + +::tabs-start + +```python +class Solution: + def strStr(self, haystack: str, needle: str) -> int: + if needle == "": return 0 + lps = [0] * len(needle) + + prevLPS, i = 0, 1 + while i < len(needle): + if needle[i] == needle[prevLPS]: + lps[i] = prevLPS + 1 + prevLPS += 1 + i += 1 + elif prevLPS == 0: + lps[i] = 0 + i += 1 + else: + prevLPS = lps[prevLPS - 1] + + i = 0 # ptr for haystack + j = 0 # ptr for needle + while i < len(haystack): + if haystack[i] == needle[j]: + i, j = i + 1, j + 1 + else: + if j == 0: + i += 1 + else: + j = lps[j - 1] + + if j == len(needle): + return i - len(needle) + + return -1 +``` + +```java +public class Solution { + public int strStr(String haystack, String needle) { + if (needle.isEmpty()) return 0; + + int m = needle.length(); + int[] lps = new int[m]; + int prevLPS = 0, i = 1; + + while (i < m) { + if (needle.charAt(i) == needle.charAt(prevLPS)) { + lps[i] = prevLPS + 1; + prevLPS++; + i++; + } else if (prevLPS == 0) { + lps[i] = 0; + i++; + } else { + prevLPS = lps[prevLPS - 1]; + } + } + + i = 0; // ptr for haystack + int j = 0; // ptr for needle + while (i < haystack.length()) { + if (haystack.charAt(i) == needle.charAt(j)) { + i++; + j++; + } else { + if (j == 0) { + i++; + } else { + j = lps[j - 1]; + } + } + + if (j == m) { + return i - m; + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int strStr(string haystack, string needle) { + if (needle.empty()) return 0; + + int m = needle.size(); + vector lps(m, 0); + int prevLPS = 0, i = 1; + + while (i < m) { + if (needle[i] == needle[prevLPS]) { + lps[i] = prevLPS + 1; + prevLPS++; + i++; + } else if (prevLPS == 0) { + lps[i] = 0; + i++; + } else { + prevLPS = lps[prevLPS - 1]; + } + } + + i = 0; // ptr for haystack + int j = 0; // ptr for needle + while (i < haystack.size()) { + if (haystack[i] == needle[j]) { + i++; + j++; + } else { + if (j == 0) { + i++; + } else { + j = lps[j - 1]; + } + } + + if (j == m) { + return i - m; + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} haystack + * @param {string} needle + * @return {number} + */ + strStr(haystack, needle) { + if (needle === "") return 0; + + const m = needle.length; + const lps = new Array(m).fill(0); + + let prevLPS = 0, i = 1; + while (i < m) { + if (needle[i] === needle[prevLPS]) { + lps[i] = prevLPS + 1; + prevLPS++; + i++; + } else if (prevLPS === 0) { + lps[i] = 0; + i++; + } else { + prevLPS = lps[prevLPS - 1]; + } + } + + i = 0; // ptr for haystack + let j = 0; // ptr for needle + while (i < haystack.length) { + if (haystack[i] === needle[j]) { + i++; + j++; + } else { + if (j === 0) { + i++; + } else { + j = lps[j - 1]; + } + } + + if (j === m) { + return i - m; + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $heystack$ and $m$ is the length of the string $needle$. + +--- + +## 3. Z-Algorithm + +::tabs-start + +```python +class Solution: + def strStr(self, haystack: str, needle: str) -> int: + if not needle: + return 0 + + s = needle + "$" + haystack + n = len(s) + z = [0] * n + l, r = 0, 0 + + for i in range(1, n): + if i <= r: + z[i] = min(r - i + 1, z[i - l]) + while i + z[i] < n and s[z[i]] == s[i + z[i]]: + z[i] += 1 + if i + z[i] - 1 > r: + l, r = i, i + z[i] - 1 + + for i in range(len(needle) + 1, n): + if z[i] == len(needle): + return i - len(needle) - 1 + + return -1 +``` + +```java +public class Solution { + public int strStr(String haystack, String needle) { + if (needle.isEmpty()) return 0; + + String s = needle + "$" + haystack; + int n = s.length(); + int[] z = new int[n]; + int l = 0, r = 0; + + for (int i = 1; i < n; i++) { + if (i <= r) { + z[i] = Math.min(r - i + 1, z[i - l]); + } + while (i + z[i] < n && s.charAt(z[i]) == s.charAt(i + z[i])) { + z[i]++; + } + if (i + z[i] - 1 > r) { + l = i; + r = i + z[i] - 1; + } + } + + for (int i = needle.length() + 1; i < n; i++) { + if (z[i] == needle.length()) { + return i - needle.length() - 1; + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int strStr(string haystack, string needle) { + if (needle.empty()) return 0; + + string s = needle + "$" + haystack; + int n = s.size(); + vector z(n, 0); + int l = 0, r = 0; + + for (int i = 1; i < n; i++) { + if (i <= r) { + z[i] = min(r - i + 1, z[i - l]); + } + while (i + z[i] < n && s[z[i]] == s[i + z[i]]) { + z[i]++; + } + if (i + z[i] - 1 > r) { + l = i; + r = i + z[i] - 1; + } + } + + for (int i = needle.size() + 1; i < n; i++) { + if (z[i] == needle.size()) { + return i - needle.size() - 1; + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} haystack + * @param {string} needle + * @return {number} + */ + strStr(haystack, needle) { + if (needle === "") return 0; + + const s = needle + "$" + haystack; + const n = s.length; + const z = new Array(n).fill(0); + let l = 0, r = 0; + + for (let i = 1; i < n; i++) { + if (i <= r) { + z[i] = Math.min(r - i + 1, z[i - l]); + } + while (i + z[i] < n && s[z[i]] === s[i + z[i]]) { + z[i]++; + } + if (i + z[i] - 1 > r) { + l = i; + r = i + z[i] - 1; + } + } + + for (let i = needle.length + 1; i < n; i++) { + if (z[i] === needle.length) { + return i - needle.length - 1; + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the length of the string $heystack$ and $m$ is the length of the string $needle$. + +--- + +## 4. Rabin-Karp Algorithm (Rolling Hash) + +::tabs-start + +```python +class Solution: + def strStr(self, haystack: str, needle: str) -> int: + if not needle: + return 0 + + base1, mod1 = 31, 768258391 + base2, mod2 = 37, 685683731 + + n, m = len(haystack), len(needle) + if m > n: + return -1 + + power1, power2 = 1, 1 + for _ in range(m): + power1 = (power1 * base1) % mod1 + power2 = (power2 * base2) % mod2 + + needle_hash1, needle_hash2 = 0, 0 + haystack_hash1, haystack_hash2 = 0, 0 + + for i in range(m): + needle_hash1 = (needle_hash1 * base1 + ord(needle[i])) % mod1 + needle_hash2 = (needle_hash2 * base2 + ord(needle[i])) % mod2 + haystack_hash1 = (haystack_hash1 * base1 + ord(haystack[i])) % mod1 + haystack_hash2 = (haystack_hash2 * base2 + ord(haystack[i])) % mod2 + + for i in range(n - m + 1): + if haystack_hash1 == needle_hash1 and haystack_hash2 == needle_hash2: + return i + + if i + m < n: + haystack_hash1 = (haystack_hash1 * base1 - ord(haystack[i]) * power1 + ord(haystack[i + m])) % mod1 + haystack_hash2 = (haystack_hash2 * base2 - ord(haystack[i]) * power2 + ord(haystack[i + m])) % mod2 + + haystack_hash1 = (haystack_hash1 + mod1) % mod1 + haystack_hash2 = (haystack_hash2 + mod2) % mod2 + + return -1 +``` + +```java +public class Solution { + public int strStr(String haystack, String needle) { + if (needle.isEmpty()) return 0; + + int base1 = 31, mod1 = 768258391; + int base2 = 37, mod2 = 685683731; + + int n = haystack.length(), m = needle.length(); + if (m > n) return -1; + + long power1 = 1, power2 = 1; + for (int i = 0; i < m; i++) { + power1 = (power1 * base1) % mod1; + power2 = (power2 * base2) % mod2; + } + + long needleHash1 = 0, needleHash2 = 0; + long haystackHash1 = 0, haystackHash2 = 0; + + for (int i = 0; i < m; i++) { + needleHash1 = (needleHash1 * base1 + needle.charAt(i)) % mod1; + needleHash2 = (needleHash2 * base2 + needle.charAt(i)) % mod2; + haystackHash1 = (haystackHash1 * base1 + haystack.charAt(i)) % mod1; + haystackHash2 = (haystackHash2 * base2 + haystack.charAt(i)) % mod2; + } + + for (int i = 0; i <= n - m; i++) { + if (haystackHash1 == needleHash1 && haystackHash2 == needleHash2) { + return i; + } + + if (i + m < n) { + haystackHash1 = (haystackHash1 * base1 - haystack.charAt(i) * power1 + haystack.charAt(i + m)) % mod1; + haystackHash2 = (haystackHash2 * base2 - haystack.charAt(i) * power2 + haystack.charAt(i + m)) % mod2; + + if (haystackHash1 < 0) haystackHash1 += mod1; + if (haystackHash2 < 0) haystackHash2 += mod2; + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int strStr(string haystack, string needle) { + if (needle.empty()) return 0; + + long long base1 = 31, mod1 = 768258391; + long long base2 = 37, mod2 = 685683731; + + int n = haystack.size(), m = needle.size(); + if (m > n) return -1; + + long long power1 = 1, power2 = 1; + for (int i = 0; i < m; i++) { + power1 = (power1 * base1) % mod1; + power2 = (power2 * base2) % mod2; + } + + long long needleHash1 = 0, needleHash2 = 0; + long long haystackHash1 = 0, haystackHash2 = 0; + + for (int i = 0; i < m; i++) { + needleHash1 = (needleHash1 * base1 + needle[i]) % mod1; + needleHash2 = (needleHash2 * base2 + needle[i]) % mod2; + haystackHash1 = (haystackHash1 * base1 + haystack[i]) % mod1; + haystackHash2 = (haystackHash2 * base2 + haystack[i]) % mod2; + } + + for (int i = 0; i <= n - m; i++) { + if (haystackHash1 == needleHash1 && haystackHash2 == needleHash2) { + return i; + } + + if (i + m < n) { + haystackHash1 = (haystackHash1 * base1 - haystack[i] * power1 + haystack[i + m]) % mod1; + haystackHash2 = (haystackHash2 * base2 - haystack[i] * power2 + haystack[i + m]) % mod2; + + if (haystackHash1 < 0) haystackHash1 += mod1; + if (haystackHash2 < 0) haystackHash2 += mod2; + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} haystack + * @param {string} needle + * @return {number} + */ + strStr(haystack, needle) { + if (needle === "") return 0; + + const base1 = 31, mod1 = 768258391; + const base2 = 37, mod2 = 685683731; + + const n = haystack.length, m = needle.length; + if (m > n) return -1; + + let power1 = 1, power2 = 1; + for (let i = 0; i < m; i++) { + power1 = (power1 * base1) % mod1; + power2 = (power2 * base2) % mod2; + } + + let needleHash1 = 0, needleHash2 = 0; + let haystackHash1 = 0, haystackHash2 = 0; + + for (let i = 0; i < m; i++) { + needleHash1 = (needleHash1 * base1 + needle.charCodeAt(i)) % mod1; + needleHash2 = (needleHash2 * base2 + needle.charCodeAt(i)) % mod2; + haystackHash1 = (haystackHash1 * base1 + haystack.charCodeAt(i)) % mod1; + haystackHash2 = (haystackHash2 * base2 + haystack.charCodeAt(i)) % mod2; + } + + for (let i = 0; i <= n - m; i++) { + if (haystackHash1 === needleHash1 && haystackHash2 === needleHash2) { + return i; + } + + if (i + m < n) { + haystackHash1 = (haystackHash1 * base1 - haystack.charCodeAt(i) * power1 + haystack.charCodeAt(i + m)) % mod1; + haystackHash2 = (haystackHash2 * base2 - haystack.charCodeAt(i) * power2 + haystack.charCodeAt(i + m)) % mod2; + + if (haystackHash1 < 0) haystackHash1 += mod1; + if (haystackHash2 < 0) haystackHash2 += mod2; + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(1)$ + +> Where $n$ is the length of the string $heystack$ and $m$ is the length of the string $needle$. \ No newline at end of file diff --git a/articles/find-the-kth-largest-integer-in-the-array.md b/articles/find-the-kth-largest-integer-in-the-array.md new file mode 100644 index 000000000..ba70f2ed8 --- /dev/null +++ b/articles/find-the-kth-largest-integer-in-the-array.md @@ -0,0 +1,543 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def kthLargestNumber(self, nums: List[str], k: int) -> str: + return sorted(nums, key=lambda x: (len(x), x), reverse=True)[k - 1] +``` + +```java +public class Solution { + public String kthLargestNumber(String[] nums, int k) { + Arrays.sort(nums, + (a, b) -> a.length() == b.length() ? b.compareTo(a) : b.length() - a.length() + ); + return nums[k - 1]; + } +} +``` + +```cpp +class Solution { +public: + string kthLargestNumber(vector& nums, int k) { + sort(nums.begin(), nums.end(), [](const string& a, const string& b) { + return a.size() == b.size() ? a > b : a.size() > b.size(); + }); + return nums[k - 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} nums + * @param {number} k + * @return {string} + */ + kthLargestNumber(nums, k) { + nums.sort( + (a, b) => a.length === b.length ? b.localeCompare(a) : b.length - a.length + ); + return nums[k - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +> Where $n$ is the number of strings and $m$ is the average length of a string. + +--- + +## 2. Max-Heap + +::tabs-start + +```python +class Num: + def __init__(self, s: str): + self.s = s + + def __lt__(self, other: "Num") -> bool: + if len(self.s) != len(other.s): + return len(self.s) > len(other.s) + return self.s > other.s + +class Solution: + def kthLargestNumber(self, nums: List[str], k: int) -> str: + maxHeap = [Num(s) for s in nums] + heapq.heapify(maxHeap) + + for _ in range(k - 1): + heapq.heappop(maxHeap) + + return heapq.heappop(maxHeap).s +``` + +```java +public class Solution { + public String kthLargestNumber(String[] nums, int k) { + PriorityQueue maxHeap = new PriorityQueue<>((a, b) -> + a.length() == b.length() ? b.compareTo(a) : Integer.compare(b.length(), a.length()) + ); + + for (String num : nums) { + maxHeap.offer(num); + } + + while (--k > 0) { + maxHeap.poll(); + } + + return maxHeap.poll(); + } +} +``` + +```cpp +class Solution { +public: + string kthLargestNumber(vector& nums, int k) { + auto cmp = [](const string& a, const string& b) { + return a.size() == b.size() ? a < b : a.size() < b.size(); + }; + + priority_queue, decltype(cmp)> maxHeap(cmp); + + for (const string& num : nums) { + maxHeap.push(num); + } + + while (--k > 0) { + maxHeap.pop(); + } + + return maxHeap.top(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} nums + * @param {number} k + * @return {string} + */ + kthLargestNumber(nums, k) { + const maxHeap = new PriorityQueue( + (a, b) => a.length === b.length ? b.localeCompare(a) : b.length - a.length + ); + + for (const num of nums) { + maxHeap.enqueue(num); + } + + while (--k > 0) { + maxHeap.dequeue(); + } + + return maxHeap.dequeue(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * (n + k) * \log n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of strings and $m$ is the average length of a string. + +--- + +## 3. Min-Heap + +::tabs-start + +```python +class Num: + def __init__(self, s: str): + self.s = s + + def __lt__(self, other: "Num") -> bool: + if len(self.s) != len(other.s): + return len(self.s) < len(other.s) + return self.s < other.s + +class Solution: + def kthLargestNumber(self, nums: List[str], k: int) -> str: + minHeap = [] + for num in nums: + heapq.heappush(minHeap, Num(num)) + if len(minHeap) > k: + heapq.heappop(minHeap) + return minHeap[0].s +``` + +```java +public class Solution { + public String kthLargestNumber(String[] nums, int k) { + PriorityQueue minHeap = new PriorityQueue<>((a, b) -> + a.length() == b.length() ? a.compareTo(b) : Integer.compare(a.length(), b.length()) + ); + + for (String num : nums) { + minHeap.offer(num); + if (minHeap.size() > k) { + minHeap.poll(); + } + } + + return minHeap.peek(); + } +} +``` + +```cpp +class Solution { +public: + string kthLargestNumber(vector& nums, int k) { + auto cmp = [](const string& a, const string& b) { + return a.size() == b.size() ? a > b : a.size() > b.size(); + }; + + priority_queue, decltype(cmp)> minHeap(cmp); + + for (const string& num : nums) { + minHeap.push(num); + if (minHeap.size() > k) { + minHeap.pop(); + } + } + + return minHeap.top(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} nums + * @param {number} k + * @return {string} + */ + kthLargestNumber(nums, k) { + const minHeap = new PriorityQueue( + (a, b) => a.length === b.length ? a.localeCompare(b) : a.length - b.length + ); + + for (const num of nums) { + minHeap.enqueue(num); + if (minHeap.size() > k) { + minHeap.dequeue(); + } + } + + return minHeap.front(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n * \log k)$ +* Space complexity: $O(k)$ + +> Where $n$ is the number of strings and $m$ is the average length of a string. + +--- + +## 4. Quick Select + +::tabs-start + +```python +class Solution: + def greater(self, x: str, y: str) -> bool: + if len(x) != len(y): + return len(x) > len(y) + return x > y + + def less(self, x: str, y: str) -> bool: + if len(x) != len(y): + return len(x) < len(y) + return x < y + + def partition(self, nums: List[str], left: int, right: int) -> int: + mid = (left + right) >> 1 + nums[mid], nums[left + 1] = nums[left + 1], nums[mid] + + if self.less(nums[left], nums[right]): + nums[left], nums[right] = nums[right], nums[left] + if self.less(nums[left + 1], nums[right]): + nums[left + 1], nums[right] = nums[right], nums[left + 1] + if self.less(nums[left], nums[left + 1]): + nums[left], nums[left + 1] = nums[left + 1], nums[left] + + pivot = nums[left + 1] + i = left + 1 + j = right + + while True: + while True: + i += 1 + if not self.greater(nums[i], pivot): + break + while True: + j -= 1 + if not self.less(nums[j], pivot): + break + if i > j: + break + nums[i], nums[j] = nums[j], nums[i] + + nums[left + 1], nums[j] = nums[j], nums[left + 1] + return j + + def quickSelect(self, nums: List[str], k: int) -> str: + left = 0 + right = len(nums) - 1 + + while True: + if right <= left + 1: + if right == left + 1 and self.greater(nums[right], nums[left]): + nums[left], nums[right] = nums[right], nums[left] + return nums[k] + + j = self.partition(nums, left, right) + if j >= k: + right = j - 1 + if j <= k: + left = j + 1 + + def kthLargestNumber(self, nums: List[str], k: int) -> str: + return self.quickSelect(nums, k - 1) +``` + +```java +public class Solution { + public String kthLargestNumber(String[] nums, int k) { + return quickSelect(nums, k - 1); + } + + private boolean greater(String x, String y) { + if (x.length() != y.length()) { + return x.length() > y.length(); + } + return x.compareTo(y) > 0; + } + + private boolean less(String x, String y) { + if (x.length() != y.length()) { + return x.length() < y.length(); + } + return x.compareTo(y) < 0; + } + + private int partition(String[] nums, int left, int right) { + int mid = (left + right) >> 1; + swap(nums, mid, left + 1); + + if (less(nums[left], nums[right])) { + swap(nums, left, right); + } + if (less(nums[left + 1], nums[right])) { + swap(nums, left + 1, right); + } + if (less(nums[left], nums[left + 1])) { + swap(nums, left, left + 1); + } + + String pivot = nums[left + 1]; + int i = left + 1, j = right; + + while (true) { + while (greater(nums[++i], pivot)); + while (less(nums[--j], pivot)); + if (i > j) break; + swap(nums, i, j); + } + + swap(nums, left + 1, j); + return j; + } + + private String quickSelect(String[] nums, int k) { + int left = 0, right = nums.length - 1; + + while (true) { + if (right <= left + 1) { + if (right == left + 1 && greater(nums[right], nums[left])) { + swap(nums, left, right); + } + return nums[k]; + } + + int j = partition(nums, left, right); + if (j >= k) { + right = j - 1; + } + if (j <= k) { + left = j + 1; + } + } + } + + private void swap(String[] nums, int i, int j) { + String temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } +} +``` + +```cpp +class Solution { +public: + string kthLargestNumber(vector& nums, int k) { + return quickSelect(nums, k - 1); + } + +private: + bool greater(const string& x, const string& y) { + if (x.size() != y.size()) { + return x.size() > y.size(); + } + return x > y; + } + + bool less(const string& x, const string& y) { + if (x.size() != y.size()) { + return x.size() < y.size(); + } + return x < y; + } + + int partition(vector& nums, int left, int right) { + int mid = (left + right) >> 1; + swap(nums[mid], nums[left + 1]); + + if (less(nums[left], nums[right])) { + swap(nums[left], nums[right]); + } + if (less(nums[left + 1], nums[right])) { + swap(nums[left + 1], nums[right]); + } + if (less(nums[left], nums[left + 1])) { + swap(nums[left], nums[left + 1]); + } + + string pivot = nums[left + 1]; + int i = left + 1, j = right; + + while (true) { + while (greater(nums[++i], pivot)); + while (less(nums[--j], pivot)); + if (i > j) break; + swap(nums[i], nums[j]); + } + + swap(nums[left + 1], nums[j]); + return j; + } + + string quickSelect(vector& nums, int k) { + int left = 0, right = nums.size() - 1; + + while (true) { + if (right <= left + 1) { + if (right == left + 1 && greater(nums[right], nums[left])) { + swap(nums[left], nums[right]); + } + return nums[k]; + } + + int j = partition(nums, left, right); + if (j >= k) { + right = j - 1; + } + if (j <= k) { + left = j + 1; + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} nums + * @param {number} k + * @return {string} + */ + kthLargestNumber(nums, k) { + const greater = (x, y) => x.length !== y.length ? x.length > y.length : x > y; + const less = (x, y) => x.length !== y.length ? x.length < y.length : x < y; + const swap = (arr, i, j) => [arr[i], arr[j]] = [arr[j], arr[i]]; + + const partition = (nums, left, right) => { + const mid = Math.floor((left + right) / 2); + swap(nums, mid, left + 1); + + if (less(nums[left], nums[right])) swap(nums, left, right); + if (less(nums[left + 1], nums[right])) swap(nums, left + 1, right); + if (less(nums[left], nums[left + 1])) swap(nums, left, left + 1); + + const pivot = nums[left + 1]; + let i = left + 1, j = right; + + while (true) { + while (greater(nums[++i], pivot)); + while (less(nums[--j], pivot)); + if (i > j) break; + swap(nums, i, j); + } + + swap(nums, left + 1, j); + return j; + }; + + const quickSelect = (nums, k) => { + let left = 0, right = nums.length - 1; + + while (true) { + if (right <= left + 1) { + if (right === left + 1 && greater(nums[right], nums[left])) { + swap(nums, left, right); + } + return nums[k]; + } + + const j = partition(nums, left, right); + if (j >= k) right = j - 1; + if (j <= k) left = j + 1; + } + }; + + return quickSelect(nums, k - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ in average case, $O(m * n ^ 2)$ in worst case. +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/find-the-longest-valid-obstacle-course-at-each-position.md b/articles/find-the-longest-valid-obstacle-course-at-each-position.md new file mode 100644 index 000000000..ff2346555 --- /dev/null +++ b/articles/find-the-longest-valid-obstacle-course-at-each-position.md @@ -0,0 +1,387 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def longestObstacleCourseAtEachPosition(self, obstacles: List[int]) -> List[int]: + n = len(obstacles) + dp = [[-1] * (n + 1) for _ in range(n)] + + def dfs(i, prev): + if i < 0: + return 0 + if dp[i][prev] != -1: + return dp[i][prev] + + res = dfs(i - 1, prev) + if prev == n or obstacles[prev] >= obstacles[i]: + res = max(res, 1 + dfs(i - 1, i)) + dp[i][prev] = res + return res + + dfs(n - 1, n) + return [1] + [1 + dp[i - 1][i] for i in range(1, n)] +``` + +```java +public class Solution { + private int[][] dp; + + public int[] longestObstacleCourseAtEachPosition(int[] obstacles) { + int n = obstacles.length; + this.dp = new int[n][n + 1]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + + dfs(n - 1, n, obstacles); + + int[] res = new int[n]; + res[0] = 1; + for (int i = 1; i < n; i++) { + res[i] = 1 + dp[i - 1][i]; + } + return res; + } + + private int dfs(int i, int prev, int[] obstacles) { + if (i < 0) { + return 0; + } + if (dp[i][prev] != -1) { + return dp[i][prev]; + } + + int res = dfs(i - 1, prev, obstacles); + if (prev == obstacles.length || obstacles[prev] >= obstacles[i]) { + res = Math.max(res, 1 + dfs(i - 1, i, obstacles)); + } + return dp[i][prev] = res; + } +} +``` + +```cpp +class Solution { +public: + vector> dp; + + vector longestObstacleCourseAtEachPosition(vector& obstacles) { + int n = obstacles.size(); + this->dp = vector>(n, vector(n + 1, -1)); + + dfs(n - 1, n, obstacles); + + vector res(n, 1); + for (int i = 1; i < n; i++) { + res[i] = 1 + dp[i - 1][i]; + } + return res; + } + +private: + int dfs(int i, int prev, vector& obstacles) { + if (i < 0) { + return 0; + } + if (dp[i][prev] != -1) { + return dp[i][prev]; + } + + int res = dfs(i - 1, prev, obstacles); + if (prev == obstacles.size() || obstacles[prev] >= obstacles[i]) { + res = max(res, 1 + dfs(i - 1, i, obstacles)); + } + return dp[i][prev] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} obstacles + * @return {number[]} + */ + longestObstacleCourseAtEachPosition(obstacles) { + const n = obstacles.length; + const dp = Array.from({ length: n }, () => new Array(n + 1).fill(-1)); + + const dfs = (i, prev) => { + if (i < 0) { + return 0; + } + if (dp[i][prev] !== -1) { + return dp[i][prev]; + } + + let res = dfs(i - 1, prev); + if (prev === n || obstacles[prev] >= obstacles[i]) { + res = Math.max(res, 1 + dfs(i - 1, i)); + } + dp[i][prev] = res; + return res; + }; + + dfs(n - 1, n); + + const res = new Array(n).fill(1); + for (let i = 1; i < n; i++) { + res[i] = 1 + dp[i - 1][i]; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Dynamic Programming (Binary Search) - I + +::tabs-start + +```python +class Solution: + def longestObstacleCourseAtEachPosition(self, obstacles: List[int]) -> List[int]: + res = [] + dp = [10**8] * (len(obstacles) + 1) + + for num in obstacles: + index = bisect.bisect(dp, num) + res.append(index + 1) + dp[index] = num + + return res +``` + +```java +public class Solution { + public int[] longestObstacleCourseAtEachPosition(int[] obstacles) { + int n = obstacles.length; + int[] res = new int[n]; + int[] dp = new int[n + 1]; + Arrays.fill(dp, (int) 1e8); + + for (int i = 0; i < n; i++) { + int index = upperBound(dp, obstacles[i]); + res[i] = index + 1; + dp[index] = obstacles[i]; + } + + return res; + } + + private int upperBound(int[] dp, int target) { + int left = 0, right = dp.length; + while (left < right) { + int mid = left + (right - left) / 2; + if (dp[mid] > target) { + right = mid; + } else { + left = mid + 1; + } + } + return left; + } +} +``` + +```cpp +class Solution { +public: + vector longestObstacleCourseAtEachPosition(vector& obstacles) { + int n = obstacles.size(); + vector res(n); + vector dp(n + 1, 1e8); + + for (int i = 0; i < n; i++) { + int index = upper_bound(dp.begin(), dp.end(), obstacles[i]) - dp.begin(); + res[i] = index + 1; + dp[index] = obstacles[i]; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} obstacles + * @return {number[]} + */ + longestObstacleCourseAtEachPosition(obstacles) { + let n = obstacles.length; + let res = new Array(n).fill(0); + let dp = new Array(n + 1).fill(1e8); + + const upperBound = (dp, target) => { + let left = 0, right = dp.length; + while (left < right) { + let mid = Math.floor((left + right) / 2); + if (dp[mid] > target) { + right = mid; + } else { + left = mid + 1; + } + } + return left; + }; + + for (let i = 0; i < n; i++) { + let index = upperBound(dp, obstacles[i]); + res[i] = index + 1; + dp[index] = obstacles[i]; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Binary Search) - II + +::tabs-start + +```python +class Solution: + def longestObstacleCourseAtEachPosition(self, obstacles: List[int]) -> List[int]: + res = [] + dp = [] + + for num in obstacles: + index = bisect.bisect_right(dp, num) + res.append(index + 1) + + if index == len(dp): + dp.append(num) + else: + dp[index] = num + + return res +``` + +```java +public class Solution { + public int[] longestObstacleCourseAtEachPosition(int[] obstacles) { + int n = obstacles.length; + int[] res = new int[n]; + List dp = new ArrayList<>(); + + for (int i = 0; i < n; i++) { + int index = upperBound(dp, obstacles[i]); + res[i] = index + 1; + + if (index == dp.size()) { + dp.add(obstacles[i]); + } else { + dp.set(index, obstacles[i]); + } + } + + return res; + } + + private int upperBound(List dp, int target) { + int left = 0, right = dp.size(); + while (left < right) { + int mid = left + (right - left) / 2; + if (dp.get(mid) > target) { + right = mid; + } else { + left = mid + 1; + } + } + return left; + } +} +``` + +```cpp +class Solution { +public: + vector longestObstacleCourseAtEachPosition(vector& obstacles) { + int n = obstacles.size(); + vector res(n); + vector dp; + + for (int i = 0; i < n; i++) { + int index = upper_bound(dp.begin(), dp.end(), obstacles[i]) - dp.begin(); + res[i] = index + 1; + + if (index == dp.size()) { + dp.push_back(obstacles[i]); + } else { + dp[index] = obstacles[i]; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} obstacles + * @return {number[]} + */ + longestObstacleCourseAtEachPosition(obstacles) { + let n = obstacles.length; + let res = new Array(n).fill(0); + let dp = []; + + const upperBound = (dp, target) => { + let left = 0, right = dp.length; + while (left < right) { + let mid = Math.floor((left + right) / 2); + if (dp[mid] > target) { + right = mid; + } else { + left = mid + 1; + } + } + return left; + }; + + for (let i = 0; i < n; i++) { + let index = upperBound(dp, obstacles[i]); + res[i] = index + 1; + + if (index === dp.length) { + dp.push(obstacles[i]); + } else { + dp[index] = obstacles[i]; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/find-the-maximum-sum-of-node-values.md b/articles/find-the-maximum-sum-of-node-values.md new file mode 100644 index 000000000..d48258892 --- /dev/null +++ b/articles/find-the-maximum-sum-of-node-values.md @@ -0,0 +1,637 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def maximumValueSum(self, nums: List[int], k: int, edges: List[List[int]]) -> int: + adj = [[] for _ in range(len(nums))] + for u, v in edges: + adj[u].append(v) + adj[v].append(u) + + def dfs(node, par): + res = [nums[node], nums[node] ^ k] + for child in adj[node]: + if child == par: + continue + + cur = dfs(child, node) + tmp = [] + tmp.append(max(res[0] + cur[0], res[1] + cur[1])) + tmp.append(max(res[1] + cur[0], res[0] + cur[1])) + res = tmp + + return res + + return dfs(0, -1)[0] +``` + +```java +public class Solution { + public long maximumValueSum(int[] nums, int k, int[][] edges) { + int n = nums.length; + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + adj[edge[1]].add(edge[0]); + } + + return dfs(0, -1, nums, k, adj)[0]; + } + + private long[] dfs(int node, int parent, int[] nums, int k, List[] adj) { + long[] res = { nums[node], nums[node] ^ k }; + for (int child : adj[node]) { + if (child == parent) continue; + + long[] cur = dfs(child, node, nums, k, adj); + long[] tmp = new long[2]; + tmp[0] = Math.max(res[0] + cur[0], res[1] + cur[1]); + tmp[1] = Math.max(res[1] + cur[0], res[0] + cur[1]); + res = tmp; + } + return res; + } +} +``` + +```cpp +class Solution { + vector> adj; + +public: + long long maximumValueSum(vector& nums, int k, vector>& edges) { + int n = nums.size(); + adj.resize(n); + for (const auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + return dfs(0, -1, nums, k)[0]; + } + +private: + vector dfs(int node, int parent, vector& nums, int k) { + vector res = { nums[node], nums[node] ^ k }; + for (int child : adj[node]) { + if (child == parent) continue; + + vector cur = dfs(child, node, nums, k); + vector tmp(2); + tmp[0] = max(res[0] + cur[0], res[1] + cur[1]); + tmp[1] = max(res[1] + cur[0], res[0] + cur[1]); + res = tmp; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @param {number[][]} edges + * @return {number} + */ + maximumValueSum(nums, k, edges) { + const n = nums.length; + const adj = Array.from({ length: n }, () => []); + + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + } + + const dfs = (node, parent) => { + let res = [nums[node], nums[node] ^ k]; + + for (const child of adj[node]) { + if (child === parent) continue; + + const cur = dfs(child, node); + const tmp = []; + tmp[0] = Math.max(res[0] + cur[0], res[1] + cur[1]); + tmp[1] = Math.max(res[1] + cur[0], res[0] + cur[1]); + res = tmp; + } + + return res; + }; + + return dfs(0, -1)[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def maximumValueSum(self, nums: List[int], k: int, edges: List[List[int]]) -> int: + dp = [[None] * 2 for _ in range(len(nums))] + [[0, float("-inf")]] + + def dfs(i, xorCnt): + if dp[i][xorCnt] is not None: + return dp[i][xorCnt] + + res = nums[i] + dfs(i + 1, xorCnt) + res = max(res, (nums[i] ^ k) + dfs(i + 1, xorCnt ^ 1)) + dp[i][xorCnt] = res + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + private long[][] dp; + + public long maximumValueSum(int[] nums, int k, int[][] edges) { + int n = nums.length; + dp = new long[n + 1][2]; + for (long[] row : dp) Arrays.fill(row, Long.MIN_VALUE); + dp[n][0] = 0; + dp[n][1] = Integer.MIN_VALUE; + + return dfs(0, 0, nums, k); + } + + private long dfs(int i, int xorCnt, int[] nums, int k) { + if (dp[i][xorCnt] != Long.MIN_VALUE) { + return dp[i][xorCnt]; + } + + long res = nums[i] + dfs(i + 1, xorCnt, nums, k); + res = Math.max(res, (nums[i] ^ k) + dfs(i + 1, xorCnt ^ 1, nums, k)); + + return dp[i][xorCnt] = res; + } +} +``` + +```cpp +class Solution { + vector> dp; + +public: + long long maximumValueSum(vector& nums, int k, vector>& edges) { + int n = nums.size(); + dp.assign(n + 1, vector(2, LLONG_MIN)); + dp[n][0] = 0; + dp[n][1] = INT_MIN; + + return dfs(0, 0, nums, k); + } + +private: + long long dfs(int i, int xorCnt, vector& nums, int k) { + if (dp[i][xorCnt] != LLONG_MIN) { + return dp[i][xorCnt]; + } + + long long res = nums[i] + dfs(i + 1, xorCnt, nums, k); + res = max(res, (nums[i] ^ k) + dfs(i + 1, xorCnt ^ 1, nums, k)); + return dp[i][xorCnt] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @param {number[][]} edges + * @return {number} + */ + maximumValueSum(nums, k, edges) { + const n = nums.length; + const dp = Array.from({ length: n + 1 }, () => [null, null]); + dp[n][0] = 0; + dp[n][1] = -Infinity; + + const dfs = (i, xorCnt) => { + if (dp[i][xorCnt] !== null) return dp[i][xorCnt]; + + let res = nums[i] + dfs(i + 1, xorCnt); + res = Math.max(res, (nums[i] ^ k) + dfs(i + 1, xorCnt ^ 1)); + return dp[i][xorCnt] = res; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def maximumValueSum(self, nums: List[int], k: int, edges: List[List[int]]) -> int: + n = len(nums) + dp = [[0, 0] for _ in range(n + 1)] + dp[n][1] = float("-inf") + + for i in range(n - 1, -1, -1): + dp[i][0] = max(nums[i] + dp[i + 1][0], (nums[i] ^ k) + dp[i + 1][1]) + dp[i][1] = max(nums[i] + dp[i + 1][1], (nums[i] ^ k) + dp[i + 1][0]) + + return dp[0][0] +``` + +```java +public class Solution { + public long maximumValueSum(int[] nums, int k, int[][] edges) { + int n = nums.length; + long[][] dp = new long[n + 1][2]; + dp[n][1] = Integer.MIN_VALUE; + + for (int i = n - 1; i >= 0; i--) { + dp[i][0] = Math.max(nums[i] + dp[i + 1][0], (nums[i] ^ k) + dp[i + 1][1]); + dp[i][1] = Math.max(nums[i] + dp[i + 1][1], (nums[i] ^ k) + dp[i + 1][0]); + } + + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + long long maximumValueSum(vector& nums, int k, vector>& edges) { + int n = nums.size(); + vector> dp(n + 1, vector(2)); + dp[n][1] = INT_MIN; + + for (int i = n - 1; i >= 0; i--) { + dp[i][0] = max(nums[i] + dp[i + 1][0], (nums[i] ^ k) + dp[i + 1][1]); + dp[i][1] = max(nums[i] + dp[i + 1][1], (nums[i] ^ k) + dp[i + 1][0]); + } + + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @param {number[][]} edges + * @return {number} + */ + maximumValueSum(nums, k, edges) { + const n = nums.length; + const dp = Array.from({ length: n + 1 }, () => [0, 0]); + dp[n][1] = -Infinity; + + for (let i = n - 1; i >= 0; i--) { + dp[i][0] = Math.max(nums[i] + dp[i + 1][0], (nums[i] ^ k) + dp[i + 1][1]); + dp[i][1] = Math.max(nums[i] + dp[i + 1][1], (nums[i] ^ k) + dp[i + 1][0]); + } + + return dp[0][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def maximumValueSum(self, nums: List[int], k: int, edges: List[List[int]]) -> int: + dp = [0, float("-inf")] + + for i in range(len(nums) - 1, -1, -1): + next_dp = [0, 0] + next_dp[0] = max(nums[i] + dp[0], (nums[i] ^ k) + dp[1]) + next_dp[1] = max(nums[i] + dp[1], (nums[i] ^ k) + dp[0]) + dp = next_dp + + return dp[0] +``` + +```java +public class Solution { + public long maximumValueSum(int[] nums, int k, int[][] edges) { + int n = nums.length; + long[] dp = {0, Long.MIN_VALUE}; + + for (int i = n - 1; i >= 0; i--) { + long[] nextDp = new long[2]; + nextDp[0] = Math.max(nums[i] + dp[0], (nums[i] ^ k) + dp[1]); + nextDp[1] = Math.max(nums[i] + dp[1], (nums[i] ^ k) + dp[0]); + dp = nextDp; + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + long long maximumValueSum(vector& nums, int k, vector>& edges) { + int n = nums.size(); + vector dp = {0, LLONG_MIN}; + + for (int i = n - 1; i >= 0; i--) { + vector nextDp(2); + nextDp[0] = max(nums[i] + dp[0], (nums[i] ^ k) + dp[1]); + nextDp[1] = max(nums[i] + dp[1], (nums[i] ^ k) + dp[0]); + dp = nextDp; + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @param {number[][]} edges + * @return {number} + */ + maximumValueSum(nums, k, edges) { + const n = nums.length; + let dp = [0, -Infinity]; + + for (let i = n - 1; i >= 0; i--) { + let nextDp = [0, 0]; + nextDp[0] = Math.max(nums[i] + dp[0], (nums[i] ^ k) + dp[1]); + nextDp[1] = Math.max(nums[i] + dp[1], (nums[i] ^ k) + dp[0]); + dp = nextDp; + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 5. Greedy + +::tabs-start + +```python +class Solution: + def maximumValueSum(self, nums: List[int], k: int, edges: List[List[int]]) -> int: + delta = [(num ^ k) - num for num in nums] + delta.sort(reverse=True) + res = sum(nums) + + for i in range(0, len(nums), 2): + if i == len(nums) - 1: + break + path_delta = delta[i] + delta[i + 1] + if path_delta <= 0: + break + res += path_delta + + return res +``` + +```java +public class Solution { + public long maximumValueSum(int[] nums, int k, int[][] edges) { + int n = nums.length; + int[] delta = new int[n]; + long res = 0; + for (int i = 0; i < n; i++) { + res += nums[i]; + delta[i] = (nums[i] ^ k) - nums[i]; + } + + Arrays.sort(delta); + for (int i = n - 1; i > 0; i -= 2) { + int pathDelta = delta[i] + delta[i - 1]; + if (pathDelta <= 0) { + break; + } + res += pathDelta; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long maximumValueSum(vector& nums, int k, vector>& edges) { + int n = nums.size(); + vector delta(n); + long long res = 0; + for (int i = 0; i < n; i++) { + res += nums[i]; + delta[i] = (nums[i] ^ k) - nums[i]; + } + + sort(delta.rbegin(), delta.rend()); + + for (int i = 0; i + 1 < n; i += 2) { + int pathDelta = delta[i] + delta[i + 1]; + if (pathDelta <= 0) { + break; + } + res += pathDelta; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @param {number[][]} edges + * @return {number} + */ + maximumValueSum(nums, k, edges) { + const n = nums.length; + let res = 0; + let delta = []; + for (let i = 0; i < n; i++) { + res += nums[i]; + delta.push((nums[i] ^ k) - nums[i]); + } + + delta.sort((a, b) => b - a); + for (let i = 0; i + 1 < n; i += 2) { + let pathDelta = delta[i] + delta[i + 1]; + if (pathDelta <= 0) { + break; + } + res += pathDelta; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 6. Greedy (Optimal) + +::tabs-start + +```python +class Solution: + def maximumValueSum(self, nums: List[int], k: int, edges: List[List[int]]) -> int: + xorCnt = res = 0 + minDiff = 1 << 30 + + for num in nums: + xorNum = num ^ k + if xorNum > num: + res += xorNum + xorCnt ^= 1 + else: + res += num + minDiff = min(minDiff, abs(xorNum - num)) + + return res - xorCnt * minDiff +``` + +```java +public class Solution { + public long maximumValueSum(int[] nums, int k, int[][] edges) { + int xorCnt = 0, minDiff = 1 << 30; + long res = 0; + + for (int num : nums) { + int xorNum = num ^ k; + if (xorNum > num) { + res += xorNum; + xorCnt ^= 1; + } else { + res += num; + } + minDiff = Math.min(minDiff, Math.abs(xorNum - num)); + } + + return res - xorCnt * minDiff; + } +} +``` + +```cpp +class Solution { +public: + long long maximumValueSum(vector& nums, int k, vector>& edges) { + int xorCnt = 0, minDiff = 1 << 30; + long long res = 0; + + for (int& num : nums) { + int xorNum = num ^ k; + if (xorNum > num) { + res += xorNum; + xorCnt ^= 1; + } else { + res += num; + } + minDiff = min(minDiff, abs(xorNum - num)); + } + + return res - (xorCnt * minDiff); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @param {number[][]} edges + * @return {number} + */ + maximumValueSum(nums, k, edges) { + let xorCnt = 0, res = 0, minDiff = 1 << 30; + + for (let num of nums) { + let xorNum = num ^ k; + if (xorNum > num) { + res += xorNum; + xorCnt ^= 1; + } else { + res += num; + } + minDiff = Math.min(minDiff, Math.abs(xorNum - num)); + } + + return res - (xorCnt * minDiff); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/find-the-safest-path-in-a-grid.md b/articles/find-the-safest-path-in-a-grid.md new file mode 100644 index 000000000..f547fa423 --- /dev/null +++ b/articles/find-the-safest-path-in-a-grid.md @@ -0,0 +1,1054 @@ +## 1. Multi Source BFS + Dijkstra's Algorithm + +::tabs-start + +```python +class Solution: + def maximumSafenessFactor(self, grid: List[List[int]]) -> int: + N = len(grid) + + def in_bounds(r, c): + return min(r, c) >= 0 and max(r, c) < N + + def precompute(): + q = deque() + min_dist = {} + for r in range(N): + for c in range(N): + if grid[r][c]: + q.append([r, c, 0]) + min_dist[(r, c)] = 0 + while q: + r, c, dist = q.popleft() + nei = [[r+1, c], [r-1, c], [r, c+1], [r, c-1]] + for r2, c2 in nei: + if in_bounds(r2, c2) and (r2, c2) not in min_dist: + min_dist[(r2, c2)] = dist + 1 + q.append([r2, c2, dist + 1]) + return min_dist + + min_dist = precompute() + maxHeap = [(-min_dist[(0, 0)], 0, 0)] # (dist, r, c) + visit = set() + visit.add((0, 0)) + + while maxHeap: + dist, r, c = heapq.heappop(maxHeap) + dist = -dist + if (r, c) == (N-1, N-1): + return dist + nei = [[r+1, c], [r-1, c], [r, c+1], [r, c-1]] + for r2, c2 in nei: + if in_bounds(r2, c2) and (r2, c2) not in visit: + visit.add((r2, c2)) + dist2 = min(dist, min_dist[(r2, c2)]) + heapq.heappush(maxHeap, (-dist2, r2, c2)) +``` + +```java +public class Solution { + public int maximumSafenessFactor(List> grid) { + int N = grid.size(); + int[][] minDist = precompute(grid, N); + PriorityQueue maxHeap = new PriorityQueue<>((a, b) -> b[0] - a[0]); + boolean[][] visit = new boolean[N][N]; + + maxHeap.offer(new int[]{minDist[0][0], 0, 0}); + visit[0][0] = true; + + while (!maxHeap.isEmpty()) { + int[] curr = maxHeap.poll(); + int dist = curr[0], r = curr[1], c = curr[2]; + + if (r == N - 1 && c == N - 1) { + return dist; + } + + for (int[] dir : new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}) { + int r2 = r + dir[0], c2 = c + dir[1]; + if (inBounds(r2, c2, N) && !visit[r2][c2]) { + visit[r2][c2] = true; + int dist2 = Math.min(dist, minDist[r2][c2]); + maxHeap.offer(new int[]{dist2, r2, c2}); + } + } + } + return 0; + } + + private int[][] precompute(List> grid, int N) { + int[][] minDist = new int[N][N]; + for (int[] row : minDist) Arrays.fill(row, -1); + Queue q = new LinkedList<>(); + + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid.get(r).get(c) == 1) { + q.offer(new int[]{r, c, 0}); + minDist[r][c] = 0; + } + } + } + + while (!q.isEmpty()) { + int[] curr = q.poll(); + int r = curr[0], c = curr[1], dist = curr[2]; + + for (int[] dir : new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}) { + int r2 = r + dir[0], c2 = c + dir[1]; + if (inBounds(r2, c2, N) && minDist[r2][c2] == -1) { + minDist[r2][c2] = dist + 1; + q.offer(new int[]{r2, c2, dist + 1}); + } + } + } + return minDist; + } + + private boolean inBounds(int r, int c, int N) { + return r >= 0 && c >= 0 && r < N && c < N; + } +} +``` + +```cpp +class Solution { + static constexpr int directions[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + +public: + int maximumSafenessFactor(vector>& grid) { + int N = grid.size(); + vector> minDist = precompute(grid, N); + priority_queue> maxHeap; + vector> visit(N, vector(N, false)); + + maxHeap.push({minDist[0][0], 0, 0}); + visit[0][0] = true; + + while (!maxHeap.empty()) { + vector cur = maxHeap.top(); maxHeap.pop(); + int dist = cur[0], r = cur[1], c = cur[2]; + + if (r == N - 1 && c == N - 1) { + return dist; + } + + for (const auto& dir : directions) { + int r2 = r + dir[0], c2 = c + dir[1]; + if (inBounds(r2, c2, N) && !visit[r2][c2]) { + visit[r2][c2] = true; + int dist2 = min(dist, minDist[r2][c2]); + maxHeap.push({dist2, r2, c2}); + } + } + } + return 0; + } + +private: + vector> precompute(vector>& grid, int N) { + vector> minDist(N, vector(N, -1)); + queue> q; + + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + q.push({r, c, 0}); + minDist[r][c] = 0; + } + } + } + + while (!q.empty()) { + vector cur = q.front(); + q.pop(); + int r = cur[0], c = cur[1], dist = cur[2]; + + for (const auto& dir : directions) { + int r2 = r + dir[0], c2 = c + dir[1]; + if (inBounds(r2, c2, N) && minDist[r2][c2] == -1) { + minDist[r2][c2] = dist + 1; + q.push({r2, c2, dist + 1}); + } + } + } + return minDist; + } + + bool inBounds(int r, int c, int N) { + return r >= 0 && c >= 0 && r < N && c < N; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + maximumSafenessFactor(grid) { + const N = grid.length; + const directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]; + + const inBounds = (r, c) => { + return r >= 0 && c >= 0 && r < N && c < N; + }; + + const precompute = () => { + const q = new Queue(); + const minDist = Array.from({ length: N }, () => Array(N).fill(-1)); + + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) { + q.push([r, c, 0]); + minDist[r][c] = 0; + } + } + } + + while (!q.isEmpty()) { + let [r, c, dist] = q.pop(); + + for (let [dr, dc] of directions) { + let r2 = r + dr, c2 = c + dc; + if (inBounds(r2, c2) && minDist[r2][c2] === -1) { + minDist[r2][c2] = dist + 1; + q.push([r2, c2, dist + 1]); + } + } + } + return minDist; + }; + + const minDist = precompute(); + const maxHeap = new MaxPriorityQueue({ priority: x => x[0] }); + const visit = Array.from({ length: N }, () => Array(N).fill(false)); + + maxHeap.enqueue([minDist[0][0], 0, 0]); + visit[0][0] = true; + + while (!maxHeap.isEmpty()) { + let [dist, r, c] = maxHeap.dequeue().element; + + if (r === N - 1 && c === N - 1) { + return dist; + } + + for (let [dr, dc] of directions) { + let r2 = r + dr, c2 = c + dc; + if (inBounds(r2, c2) && !visit[r2][c2]) { + visit[r2][c2] = true; + let dist2 = Math.min(dist, minDist[r2][c2]); + maxHeap.enqueue([dist2, r2, c2]); + } + } + } + return 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 \log n)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Multi Source BFS + Dijkstra's Algorithm (Overwriting the Input) + +::tabs-start + +```python +class Solution: + def maximumSafenessFactor(self, grid): + N = len(grid) + minDist = grid + directions = [0, 1, 0, -1, 0] + + q = deque() + for r in range(N): + for c in range(N): + if grid[r][c] == 1: + q.append(r * N + c) + minDist[r][c] = 0 + else: + minDist[r][c] = -1 + + while q: + node = q.popleft() + r, c = divmod(node, N) + for i in range(4): + r2, c2 = r + directions[i], c + directions[i + 1] + if 0 <= r2 < N and 0 <= c2 < N and minDist[r2][c2] == -1: + minDist[r2][c2] = minDist[r][c] + 1 + q.append(r2 * N + c2) + + maxHeap = [(-minDist[0][0], 0)] + safeFactor = [0] * (N * N) + safeFactor[0] = minDist[0][0] + + while maxHeap: + dist, node = heapq.heappop(maxHeap) + dist = -dist + r, c = divmod(node, N) + if r == N - 1 and c == N - 1: + return dist + if safeFactor[node] > dist: + continue + + for i in range(4): + r2, c2 = r + directions[i], c + directions[i + 1] + node2 = r2 * N + c2 + if 0 <= r2 < N and 0 <= c2 < N: + dist2 = min(dist, minDist[r2][c2]) + if dist2 > safeFactor[node2]: + safeFactor[node2] = dist2 + heapq.heappush(maxHeap, (-dist2, node2)) + + return 0 +``` + +```java +public class Solution { + public int maximumSafenessFactor(List> grid) { + int N = grid.size(); + int[][] minDist = new int[N][N]; + int[] directions = {0, 1, 0, -1, 0}; + + Queue q = new LinkedList<>(); + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid.get(r).get(c) == 1) { + q.offer(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.isEmpty()) { + int node = q.poll(); + int r = node / N, c = node % N; + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] == -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.offer(r2 * N + c2); + } + } + } + + PriorityQueue maxHeap = new PriorityQueue<>((a, b) -> Integer.compare(b[0], a[0])); + int[] safeFactor = new int[N * N]; + Arrays.fill(safeFactor, -1); + safeFactor[0] = minDist[0][0]; + maxHeap.offer(new int[]{safeFactor[0], 0}); + + while (!maxHeap.isEmpty()) { + int[] top = maxHeap.poll(); + int dist = top[0], node = top[1]; + int r = node / N, c = node % N; + if (r == N - 1 && c == N - 1) { + return dist; + } + if (safeFactor[node] > dist) { + continue; + } + + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1]; + int node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N) { + int dist2 = Math.min(dist, minDist[r2][c2]); + if (dist2 > safeFactor[node2]) { + safeFactor[node2] = dist2; + maxHeap.offer(new int[]{dist2, node2}); + } + } + } + } + return 0; + } +} +``` + +```cpp +class Solution { +public: + int maximumSafenessFactor(vector>& grid) { + int N = grid.size(); + vector directions = {0, 1, 0, -1, 0}; + vector>& minDist = grid; + + queue q; + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + q.push(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.empty()) { + int node = q.front(); q.pop(); + int r = node / N, c = node % N; + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] == -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.push(r2 * N + c2); + } + } + } + + priority_queue> maxHeap; + vector safeFactor(N * N, 0); + safeFactor[0] = minDist[0][0]; + maxHeap.push({safeFactor[0], 0}); + + while (!maxHeap.empty()) { + auto [dist, node] = maxHeap.top(); maxHeap.pop(); + int r = node / N, c = node % N; + if (r == N - 1 && c == N - 1) { + return dist; + } + if (safeFactor[node] > dist) { + continue; + } + + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1], node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N) { + int dist2 = min(dist, minDist[r2][c2]); + if (dist2 > safeFactor[node2]) { + safeFactor[node2] = dist2; + maxHeap.push({dist2, node2}); + } + } + } + } + return 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + maximumSafenessFactor(grid) { + const N = grid.length; + const directions = [0, 1, 0, -1, 0]; + const minDist = grid; + + const q = new Queue(); + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) { + q.push(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.isEmpty()) { + let node = q.pop(); + let r = Math.floor(node / N), c = node % N; + for (let i = 0; i < 4; i++) { + let r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] === -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.push(r2 * N + c2); + } + } + } + + let maxHeap = new MaxPriorityQueue({ priority: x => x[0] }); + let safeFactor = new Array(N * N).fill(0); + safeFactor[0] = minDist[0][0]; + maxHeap.enqueue([safeFactor[0], 0]); + + while (!maxHeap.isEmpty()) { + let [dist, node] = maxHeap.dequeue().element; + let r = Math.floor(node / N), c = node % N; + if (r === N - 1 && c === N - 1) { + return dist; + } + if (safeFactor[node] > dist) { + continue; + } + + for (let i = 0; i < 4; i++) { + let r2 = r + directions[i], c2 = c + directions[i + 1], node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N) { + let dist2 = Math.min(dist, minDist[r2][c2]); + if (dist2 > safeFactor[node2]) { + safeFactor[node2] = dist2; + maxHeap.enqueue([dist2, node2]); + } + } + } + } + return 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 \log n)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Multi Source BFS + Binary Search + +::tabs-start + +```python +class Solution: + def maximumSafenessFactor(self, grid): + N = len(grid) + minDist = grid + directions = [0, 1, 0, -1, 0] + + q = deque() + for r in range(N): + for c in range(N): + if grid[r][c] == 1: + q.append(r * N + c) + minDist[r][c] = 0 + else: + minDist[r][c] = -1 + + while q: + node = q.popleft() + r, c = divmod(node, N) + for i in range(4): + r2, c2 = r + directions[i], c + directions[i + 1] + if 0 <= r2 < N and 0 <= c2 < N and minDist[r2][c2] == -1: + minDist[r2][c2] = minDist[r][c] + 1 + q.append(r2 * N + c2) + + def canReach(threshold): + q = deque([0]) + visited = [False] * (N * N) + visited[0] = True + + while q: + node = q.popleft() + r, c = divmod(node, N) + if r == N - 1 and c == N - 1: + return True + + for i in range(4): + r2, c2 = r + directions[i], c + directions[i + 1] + node2 = r2 * N + c2 + if (0 <= r2 < N and 0 <= c2 < N and not visited[node2] and + minDist[r2][c2] >= threshold + ): + visited[node2] = True + q.append(node2) + + return False + + l, r = 0, min(minDist[0][0], minDist[N - 1][N - 1]) + while l <= r: + mid = (l + r) // 2 + if canReach(mid): + l = mid + 1 + else: + r = mid - 1 + + return l - 1 +``` + +```java +public class Solution { + private static int[] directions = {0, 1, 0, -1, 0}; + + public int maximumSafenessFactor(List> grid) { + int N = grid.size(); + int[][] minDist = new int[N][N]; + + Queue q = new LinkedList<>(); + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid.get(r).get(c) == 1) { + q.offer(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.isEmpty()) { + int node = q.poll(); + int r = node / N, c = node % N; + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] == -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.offer(r2 * N + c2); + } + } + } + + int l = 0, r = Math.min(minDist[0][0], minDist[N - 1][N - 1]); + while (l <= r) { + int mid = (l + r) / 2; + if (canReach(minDist, N, mid)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + return l - 1; + } + + private boolean canReach(int[][] minDist, int N, int threshold) { + Queue q = new LinkedList<>(); + boolean[] visited = new boolean[N * N]; + q.offer(0); + visited[0] = true; + + while (!q.isEmpty()) { + int node = q.poll(); + int r = node / N, c = node % N; + if (r == N - 1 && c == N - 1) { + return true; + } + + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1]; + int node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && !visited[node2] && + minDist[r2][c2] >= threshold) { + visited[node2] = true; + q.offer(node2); + } + } + } + return false; + } +} +``` + +```cpp +class Solution { + static constexpr int directions[5] = {0, 1, 0, -1, 0}; + +public: + int maximumSafenessFactor(vector>& grid) { + int N = grid.size(); + vector>& minDist = grid; + + queue q; + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + q.push(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.empty()) { + int node = q.front(); q.pop(); + int r = node / N, c = node % N; + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] == -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.push(r2 * N + c2); + } + } + } + + int l = 0, r = min(minDist[0][0], minDist[N - 1][N - 1]); + while (l <= r) { + int mid = (l + r) / 2; + if (canReach(minDist, N, mid)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + return l - 1; + } + +private: + bool canReach(vector>& minDist, int N, int threshold) { + queue q; + vector visited(N * N, false); + q.push(0); + visited[0] = true; + + while (!q.empty()) { + int node = q.front(); q.pop(); + int r = node / N, c = node % N; + if (r == N - 1 && c == N - 1) { + return true; + } + + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1], node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && !visited[node2] && + minDist[r2][c2] >= threshold) { + visited[node2] = true; + q.push(node2); + } + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + maximumSafenessFactor(grid) { + const N = grid.length; + const minDist = grid; + const directions = [0, 1, 0, -1, 0]; + + let q = new Queue(); + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) { + q.push(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.isEmpty()) { + let node = q.pop(); + let r = Math.floor(node / N), c = node % N; + for (let i = 0; i < 4; i++) { + let r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] === -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.push(r2 * N + c2); + } + } + } + + const canReach = (threshold) => { + q = new Queue([0]); + let visited = new Array(N * N).fill(false); + visited[0] = true; + + while (!q.isEmpty()) { + let node = q.pop(); + let r = Math.floor(node / N), c = node % N; + if (r === N - 1 && c === N - 1) return true; + + for (let i = 0; i < 4; i++) { + let r2 = r + directions[i], c2 = c + directions[i + 1], node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && !visited[node2] && + minDist[r2][c2] >= threshold) { + visited[node2] = true; + q.push(node2); + } + } + } + return false; + }; + + let l = 0, r = Math.min(minDist[0][0], minDist[N - 1][N - 1]); + while (l <= r) { + let mid = Math.floor((l + r) / 2); + if (canReach(mid)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + return l - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 \log n)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Breadth First Search (0-1 BFS) + +::tabs-start + +```python +class Solution: + def maximumSafenessFactor(self, grid): + N = len(grid) + minDist = grid + directions = [0, 1, 0, -1, 0] + + q = deque() + for r in range(N): + for c in range(N): + if grid[r][c] == 1: + q.append(r * N + c) + minDist[r][c] = 0 + else: + minDist[r][c] = -1 + + while q: + node = q.popleft() + r, c = divmod(node, N) + for i in range(4): + r2, c2 = r + directions[i], c + directions[i + 1] + if 0 <= r2 < N and 0 <= c2 < N and minDist[r2][c2] == -1: + minDist[r2][c2] = minDist[r][c] + 1 + q.append(r2 * N + c2) + + safeFactor = [-1] * (N * N) + res = safeFactor[0] = min(minDist[N - 1][N - 1], minDist[0][0]) + q.append(0) + + while q: + node = q.popleft() + r, c = divmod(node, N) + res = min(res, safeFactor[node]) + if r == N - 1 and c == N - 1: + break + + for i in range(4): + r2, c2 = r + directions[i], c + directions[i + 1] + node2 = r2 * N + c2 + if 0 <= r2 < N and 0 <= c2 < N and safeFactor[node2] == -1: + safeFactor[node2] = min(safeFactor[node], minDist[r2][c2]) + if safeFactor[node2] < res: + q.append(node2) + else: + q.appendleft(node2) + + return res +``` + +```java +public class Solution { + private static final int[] directions = {0, 1, 0, -1, 0}; + + public int maximumSafenessFactor(List> grid) { + int N = grid.size(); + int[][] minDist = new int[N][N]; + + Deque q = new ArrayDeque<>(); + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid.get(r).get(c) == 1) { + q.offerLast(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.isEmpty()) { + int node = q.pollFirst(); + int r = node / N, c = node % N; + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] == -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.offerLast(r2 * N + c2); + } + } + } + + int[] safeFactor = new int[N * N]; + Arrays.fill(safeFactor, -1); + int res = safeFactor[0] = Math.min(minDist[N - 1][N - 1], minDist[0][0]); + q.offerLast(0); + + while (!q.isEmpty()) { + int node = q.pollFirst(); + int r = node / N, c = node % N; + res = Math.min(res, safeFactor[node]); + if (r == N - 1 && c == N - 1) { + break; + } + + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1], node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && safeFactor[node2] == -1) { + safeFactor[node2] = Math.min(safeFactor[node], minDist[r2][c2]); + if (safeFactor[node2] < res) { + q.offerLast(node2); + } else { + q.offerFirst(node2); + } + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maximumSafenessFactor(vector>& grid) { + int N = grid.size(); + vector>& minDist = grid; + constexpr int directions[5] = {0, 1, 0, -1, 0}; + + deque q; + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + q.push_back(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.empty()) { + int node = q.front(); q.pop_front(); + int r = node / N, c = node % N; + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] == -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.push_back(r2 * N + c2); + } + } + } + + vector safeFactor(N * N, -1); + int res = safeFactor[0] = min(minDist[N - 1][N - 1], minDist[0][0]); + q.push_back(0); + + while (!q.empty()) { + int node = q.front(); q.pop_front(); + int r = node / N, c = node % N; + res = min(res, safeFactor[node]); + if (r == N - 1 && c == N - 1) { + break; + } + + for (int i = 0; i < 4; i++) { + int r2 = r + directions[i], c2 = c + directions[i + 1], node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && safeFactor[node2] == -1) { + safeFactor[node2] = min(safeFactor[node], minDist[r2][c2]); + if (safeFactor[node2] < res) { + q.push_back(node2); + } else { + q.push_front(node2); + } + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + maximumSafenessFactor(grid) { + const N = grid.length; + const minDist = grid; + const directions = [0, 1, 0, -1, 0]; + + const q = new Deque(); + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) { + q.pushBack(r * N + c); + minDist[r][c] = 0; + } else { + minDist[r][c] = -1; + } + } + } + + while (!q.isEmpty()) { + let node = q.popFront(); + let r = Math.floor(node / N), c = node % N; + for (let i = 0; i < 4; i++) { + let r2 = r + directions[i], c2 = c + directions[i + 1]; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && minDist[r2][c2] === -1) { + minDist[r2][c2] = minDist[r][c] + 1; + q.pushBack(r2 * N + c2); + } + } + } + + let safeFactor = new Array(N * N).fill(-1); + let res = safeFactor[0] = Math.min(minDist[N - 1][N - 1], minDist[0][0]); + q.pushBack(0); + + while (!q.isEmpty()) { + let node = q.popFront(); + let r = Math.floor(node / N), c = node % N; + res = Math.min(res, safeFactor[node]); + if (r === N - 1 && c === N - 1) { + break; + } + + for (let i = 0; i < 4; i++) { + let r2 = r + directions[i], c2 = c + directions[i + 1], node2 = r2 * N + c2; + if (r2 >= 0 && c2 >= 0 && r2 < N && c2 < N && safeFactor[node2] === -1) { + safeFactor[node2] = Math.min(safeFactor[node], minDist[r2][c2]); + if (safeFactor[node2] < res) { + q.pushBack(node2); + } else { + q.pushFront(node2); + } + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/find-the-town-judge.md b/articles/find-the-town-judge.md new file mode 100644 index 000000000..a1343bd35 --- /dev/null +++ b/articles/find-the-town-judge.md @@ -0,0 +1,245 @@ +## 1. Indegree & Outdegree + +::tabs-start + +```python +class Solution: + def findJudge(self, n: int, trust: List[List[int]]) -> int: + incoming = defaultdict(int) + outgoing = defaultdict(int) + + for src, dst in trust: + outgoing[src] += 1 + incoming[dst] += 1 + + for i in range(1, n + 1): + if outgoing[i] == 0 and incoming[i] == n - 1: + return i + + return -1 +``` + +```java +public class Solution { + public int findJudge(int n, int[][] trust) { + int[] incoming = new int[n + 1]; + int[] outgoing = new int[n + 1]; + + for (int[] t : trust) { + outgoing[t[0]]++; + incoming[t[1]]++; + } + + for (int i = 1; i <= n; i++) { + if (outgoing[i] == 0 && incoming[i] == n - 1) { + return i; + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int findJudge(int n, vector>& trust) { + vector incoming(n + 1, 0), outgoing(n + 1, 0); + + for (auto& t : trust) { + outgoing[t[0]]++; + incoming[t[1]]++; + } + + for (int i = 1; i <= n; i++) { + if (outgoing[i] == 0 && incoming[i] == n - 1) + return i; + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} trust + * @return {number} + */ + findJudge(n, trust) { + let incoming = new Array(n + 1).fill(0); + let outgoing = new Array(n + 1).fill(0); + + for (let [src, dst] of trust) { + outgoing[src]++; + incoming[dst]++; + } + + for (let i = 1; i <= n; i++) { + if (outgoing[i] === 0 && incoming[i] === n - 1) { + return i; + } + } + + return -1; + } +} +``` + +```csharp +public class Solution { + public int FindJudge(int n, int[][] trust) { + int[] incoming = new int[n + 1]; + int[] outgoing = new int[n + 1]; + + foreach (int[] t in trust) { + int a = t[0]; + int b = t[1]; + outgoing[a]++; + incoming[b]++; + } + + for (int i = 1; i <= n; i++) { + if (outgoing[i] == 0 && incoming[i] == n - 1) { + return i; + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 2. Indegree & Outdegree (Optimal) + +::tabs-start + +```python +class Solution: + def findJudge(self, n: int, trust: List[List[int]]) -> int: + delta = defaultdict(int) + + for src, dst in trust: + delta[src] -= 1 + delta[dst] += 1 + + for i in range(1, n + 1): + if delta[i] == n - 1: + return i + + return -1 +``` + +```java +public class Solution { + public int findJudge(int n, int[][] trust) { + int[] delta = new int[n + 1]; + + for (int[] t : trust) { + delta[t[0]] -= 1; + delta[t[1]] += 1; + } + + for (int i = 1; i <= n; i++) { + if (delta[i] == n - 1) { + return i; + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int findJudge(int n, vector>& trust) { + vector delta(n + 1, 0); + + for (auto& t : trust) { + delta[t[0]]--; + delta[t[1]]++; + } + + for (int i = 1; i <= n; i++) { + if (delta[i] == n - 1) { + return i; + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} trust + * @return {number} + */ + findJudge(n, trust) { + let delta = new Array(n + 1).fill(0); + + for (let [src, dst] of trust) { + delta[src]--; + delta[dst]++; + } + + for (let i = 1; i <= n; i++) { + if (delta[i] === n - 1) { + return i; + } + } + + return -1; + } +} +``` + +```csharp +public class Solution { + public int FindJudge(int n, int[][] trust) { + int[] delta = new int[n + 1]; + + foreach (int[] t in trust) { + int a = t[0]; + int b = t[1]; + delta[a]--; + delta[b]++; + } + + for (int i = 1; i <= n; i++) { + if (delta[i] == n - 1) { + return i; + } + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. \ No newline at end of file diff --git a/articles/find-unique-binary-string.md b/articles/find-unique-binary-string.md new file mode 100644 index 000000000..ea24db9f9 --- /dev/null +++ b/articles/find-unique-binary-string.md @@ -0,0 +1,685 @@ +## 1. Backtracking (Recursion) + +::tabs-start + +```python +class Solution: + def findDifferentBinaryString(self, nums: List[str]) -> str: + strSet = {s for s in nums} + + def backtrack(i, cur): + if i == len(nums): + res = "".join(cur) + return None if res in strSet else res + + res = backtrack(i + 1, cur) + if res: return res + + cur[i] = "1" + return backtrack(i + 1, cur) + + return backtrack(0, ["0" for _ in nums]) +``` + +```java +public class Solution { + public String findDifferentBinaryString(String[] nums) { + Set strSet = new HashSet<>(); + for (String s : nums) { + strSet.add(s); + } + return backtrack(0, new char[nums.length], strSet, nums.length); + } + + private String backtrack(int i, char[] cur, Set strSet, int n) { + if (i == n) { + String res = new String(cur); + return strSet.contains(res) ? null : res; + } + + cur[i] = '0'; + String res = backtrack(i + 1, cur, strSet, n); + if (res != null) return res; + + cur[i] = '1'; + return backtrack(i + 1, cur, strSet, n); + } +} +``` + +```cpp +class Solution { +public: + string findDifferentBinaryString(vector& nums) { + unordered_set strSet(nums.begin(), nums.end()); + string cur(nums.size(), '0'); + return backtrack(0, cur, strSet, nums.size()); + } + +private: + string backtrack(int i, string& cur, unordered_set& strSet, int n) { + if (i == n) { + return strSet.count(cur) ? "" : cur; + } + + string res = backtrack(i + 1, cur, strSet, n); + if (!res.empty()) return res; + + cur[i] = '1'; + return backtrack(i + 1, cur, strSet, n); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} nums + * @return {string} + */ + findDifferentBinaryString(nums) { + const strSet = new Set(nums); + const n = nums.length; + + const backtrack = (i, cur) => { + if (i === n) { + const res = cur.join(""); + return strSet.has(res) ? null : res; + } + + let res = backtrack(i + 1, cur); + if (res) return res; + + cur[i] = "1"; + return backtrack(i + 1, cur); + }; + + return backtrack(0, Array(n).fill("0")); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Backtracking (Iteration) + +::tabs-start + +```python +class Solution: + def findDifferentBinaryString(self, nums: List[str]) -> str: + strSet = set(nums) + n = len(nums) + + for num in range(1 << n): + res = bin(num)[2:].zfill(n) + if res not in strSet: + return res + + return "" +``` + +```java +public class Solution { + public String findDifferentBinaryString(String[] nums) { + Set strSet = new HashSet<>(); + for (String s : nums) { + strSet.add(s); + } + int n = nums.length; + + for (int num = 0; num < (n + 1); num++) { + String res = String.format("%" + n + "s", + Integer.toBinaryString(num)).replace(' ', '0'); + if (!strSet.contains(res)) { + return res; + } + } + + return ""; + } +} +``` + +```cpp +class Solution { +public: + string findDifferentBinaryString(vector& nums) { + unordered_set strSet(nums.begin(), nums.end()); + int n = nums.size(); + + for (int num = 0; num < (n + 1); num++) { + string res = toBinaryString(num, n); + if (strSet.find(res) == strSet.end()) { + return res; + } + } + + return ""; + } + +private: + string toBinaryString(int num, int length) { + string res = ""; + for (int i = length - 1; i >= 0; i--) { + res += (num & (1 << i)) ? '1' : '0'; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} nums + * @return {string} + */ + findDifferentBinaryString(nums) { + const strSet = new Set(nums); + const n = nums.length; + + for (let num = 0; num < (n + 1); num++) { + let res = num.toString(2).padStart(n, '0'); + if (!strSet.has(res)) { + return res; + } + } + + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Cantor's Diagonal Argument + +::tabs-start + +```python +class Solution: + def findDifferentBinaryString(self, nums: List[str]) -> str: + res = [] + for i in range(len(nums)): + if nums[i][i] == '0': + res.append('1') + else: + res.append('0') + return "".join(res) +``` + +```java +public class Solution { + public String findDifferentBinaryString(String[] nums) { + StringBuilder res = new StringBuilder(); + for (int i = 0; i < nums.length; i++) { + res.append(nums[i].charAt(i) == '0' ? '1' : '0'); + } + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string findDifferentBinaryString(vector& nums) { + string res; + for (int i = 0; i < nums.size(); i++) { + res += (nums[i][i] == '0') ? '1' : '0'; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} nums + * @return {string} + */ + findDifferentBinaryString(nums) { + let res = []; + for (let i = 0; i < nums.length; i++) { + res.push(nums[i][i] === '0' ? '1' : '0'); + } + return res.join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output string. + +--- + +## 4. Randomization + +::tabs-start + +```python +class Solution: + def findDifferentBinaryString(self, nums: List[str]) -> str: + strSet = set(nums) + n = len(nums) + + while True: + res = "".join(random.choice("01") for _ in range(n)) + if res not in strSet: + return res +``` + +```java +public class Solution { + public String findDifferentBinaryString(String[] nums) { + Set strSet = new HashSet<>(); + for (String s : nums) { + strSet.add(s); + } + int n = nums.length; + Random random = new Random(); + + while (true) { + StringBuilder res = new StringBuilder(); + for (int i = 0; i < n; i++) { + res.append(random.nextBoolean() ? '1' : '0'); + } + String result = res.toString(); + if (!strSet.contains(result)) { + return result; + } + } + } +} +``` + +```cpp +class Solution { +public: + string findDifferentBinaryString(vector& nums) { + unordered_set strSet(nums.begin(), nums.end()); + int n = nums.size(); + + while (true) { + string res = ""; + for (int i = 0; i < n; i++) { + res += (rand() % 2) ? '1' : '0'; + } + if (strSet.find(res) == strSet.end()) { + return res; + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} nums + * @return {string} + */ + findDifferentBinaryString(nums) { + const strSet = new Set(nums); + const n = nums.length; + + while (true) { + let res = Array.from({ length: n }, () => + Math.random() < 0.5 ? '0' : '1').join(""); + if (!strSet.has(res)) { + return res; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(∞)$ in worst case. +* Space complexity: $O(n)$ + +--- + +## 5. Trie + +::tabs-start + +```python +class Node: + def __init__(self): + self.children = [None, None] + + def contains_bit(self, bit: int) -> bool: + return self.children[bit] is not None + + def put(self, bit: int): + self.children[bit] = Node() + + def get(self, bit: int): + return self.children[bit] + +class Trie: + def __init__(self): + self.root = Node() + + def insert(self, s: str): + curr = self.root + for c in s: + bit = int(c) + if not curr.contains_bit(bit): + curr.put(bit) + curr = curr.get(bit) + + def search(self, res: str, curr) -> bool: + while curr.contains_bit(0) or curr.contains_bit(1): + if not curr.contains_bit(0): + res.append('0') + return True + if not curr.contains_bit(1): + res.append('1') + return True + + res.append('1') + curr = curr.get(1) + + return False + +class Solution: + def findDifferentBinaryString(self, nums: List[str]) -> str: + trie = Trie() + for s in nums: + trie.insert(s) + + res = [] + trie.search(res, trie.root) + + while len(res) < len(nums): + res.append('1') + + return ''.join(res) +``` + +```java +class Node { + Node[] children; + + Node() { + this.children = new Node[2]; + } + + boolean containsBit(int bit) { + return this.children[bit] != null; + } + + void put(int bit) { + this.children[bit] = new Node(); + } + + Node get(int bit) { + return this.children[bit]; + } +} + +class Trie { + Node root; + + Trie() { + this.root = new Node(); + } + + void insert(String s) { + Node curr = root; + for (char c : s.toCharArray()) { + int bit = c - '0'; + if (!curr.containsBit(bit)) { + curr.put(bit); + } + curr = curr.get(bit); + } + } + + boolean search(StringBuilder res, Node curr) { + while (curr.containsBit(0) || curr.containsBit(1)) { + if (!curr.containsBit(0)) { + res.append('0'); + return true; + } + if (!curr.containsBit(1)) { + res.append('1'); + return true; + } + + res.append('1'); + curr = curr.get(1); + } + + return false; + } +} + +public class Solution { + public String findDifferentBinaryString(String[] nums) { + Trie trie = new Trie(); + for (String s : nums) { + trie.insert(s); + } + + StringBuilder res = new StringBuilder(); + trie.search(res, trie.root); + + while (res.length() < nums.length) { + res.append('1'); + } + + return res.toString(); + } +} +``` + +```cpp +class Node { +public: + Node *children[2]; + + Node() { + this->children[0] = nullptr; + this->children[1] = nullptr; + } + + bool containsBit(int bit) { + return this->children[bit] != nullptr; + } + + void put(int bit) { + this->children[bit] = new Node(); + } + + Node* get(int bit) { + return this->children[bit]; + } +}; + +class Trie { +public: + Node* root; + + Trie() { + this->root = new Node(); + } + + void insert(const string& s) { + Node* curr = root; + for (char c : s) { + int bit = c - '0'; + if (!curr->containsBit(bit)) { + curr->put(bit); + } + curr = curr->get(bit); + } + } + + bool search(string& res, Node* curr) { + while (curr->containsBit(0) || curr->containsBit(1)) { + if (!curr->containsBit(0)) { + res += '0'; + return true; + } + if (!curr->containsBit(1)) { + res += '1'; + return true; + } + + res += '1'; + curr = curr->get(1); + } + + return false; + } +}; + +class Solution { +public: + string findDifferentBinaryString(vector& nums) { + Trie trie; + for (const string& s : nums) { + trie.insert(s); + } + + string res; + trie.search(res, trie.root); + + while (res.length() < nums.size()) { + res += '1'; + } + + return res; + } +}; +``` + +```javascript +class Node { + constructor() { + this.children = [null, null]; + } + + /** + * @param {number} bit + * @return {boolean} + */ + containsBit(bit) { + return this.children[bit] !== null; + } + + /** + * @param {number} bit + */ + put(bit) { + this.children[bit] = new Node(); + } + + /** + * @param {number} bit + * @return {Node} + */ + get(bit) { + return this.children[bit]; + } +} + +class Trie { + constructor() { + this.root = new Node(); + } + + /** + * @param {string} s + */ + insert(s) { + let curr = this.root; + for (const c of s) { + let bit = c === '1' ? 1 : 0; + if (!curr.containsBit(bit)) { + curr.put(bit); + } + curr = curr.get(bit); + } + } + + /** + * @param {string[]} res + * @param {Node} curr + * @return {boolean} + */ + search(res, curr) { + while (curr.containsBit(0) || curr.containsBit(1)) { + if (!curr.containsBit(0)) { + res.push('0'); + return true; + } + if (!curr.containsBit(1)) { + res.push('1'); + return true; + } + + res.push('1'); + curr = curr.get(1); + } + return false; + } +} + +class Solution { + /** + * @param {string[]} nums + * @return {string} + */ + findDifferentBinaryString(nums) { + let trie = new Trie(); + for (const s of nums) { + trie.insert(s); + } + + let res = []; + trie.search(res, trie.root); + + while (res.length < nums.length) { + res.push('1'); + } + + return res.join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/find-words-that-can-be-formed-by-characters.md b/articles/find-words-that-can-be-formed-by-characters.md new file mode 100644 index 000000000..4dfdf7b44 --- /dev/null +++ b/articles/find-words-that-can-be-formed-by-characters.md @@ -0,0 +1,391 @@ +## 1. Hash Map (Two Pass) + +::tabs-start + +```python +class Solution: + def countCharacters(self, words: List[str], chars: str) -> int: + count = Counter(chars) + res = 0 + + for w in words: + cur_word = Counter(w) + good = True + for c in cur_word: + if cur_word[c] > count[c]: + good = False + break + if good: + res += len(w) + return res +``` + +```java +public class Solution { + public int countCharacters(String[] words, String chars) { + Map count = new HashMap<>(); + for (char c : chars.toCharArray()) { + count.put(c, count.getOrDefault(c, 0) + 1); + } + int res = 0; + for (String w : words) { + Map curWord = new HashMap<>(); + for (char c : w.toCharArray()) { + curWord.put(c, curWord.getOrDefault(c, 0) + 1); + } + boolean good = true; + for (char c : curWord.keySet()) { + if (curWord.get(c) > count.getOrDefault(c, 0)) { + good = false; + break; + } + } + if (good) { + res += w.length(); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int countCharacters(vector& words, string chars) { + unordered_map count; + for (char c : chars) { + count[c]++; + } + int res = 0; + for (const string& w : words) { + unordered_map curWord; + for (char c : w) { + curWord[c]++; + } + bool good = true; + for (const auto& p : curWord) { + if (p.second > count[p.first]) { + good = false; + break; + } + } + if (good) { + res += w.size(); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string} chars + * @return {number} + */ + countCharacters(words, chars) { + const count = {}; + for (const c of chars) { + count[c] = (count[c] || 0) + 1; + } + let res = 0; + for (const w of words) { + const curWord = {}; + for (const c of w) { + curWord[c] = (curWord[c] || 0) + 1; + } + let good = true; + for (const c in curWord) { + if (curWord[c] > (count[c] || 0)) { + good = false; + break; + } + } + if (good) { + res += w.length; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + (m * k))$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +> Where $n$ is the length of $chars$, $m$ is the number of words and $k$ is the average length of each word. + +--- + +## 2. Hash Map (One Pass) + +::tabs-start + +```python +class Solution: + def countCharacters(self, words: List[str], chars: str) -> int: + count = Counter(chars) + res = 0 + + for w in words: + cur_word = defaultdict(int) + good = True + for c in w: + cur_word[c] += 1 + if cur_word[c] > count[c]: + good = False + break + if good: + res += len(w) + return res +``` + +```java +public class Solution { + public int countCharacters(String[] words, String chars) { + Map count = new HashMap<>(); + for (char c : chars.toCharArray()) { + count.put(c, count.getOrDefault(c, 0) + 1); + } + int res = 0; + for (String w : words) { + Map curWord = new HashMap<>(); + boolean good = true; + for (char c : w.toCharArray()) { + curWord.put(c, curWord.getOrDefault(c, 0) + 1); + if (curWord.get(c) > count.getOrDefault(c, 0)) { + good = false; + break; + } + } + if (good) { + res += w.length(); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int countCharacters(vector& words, string chars) { + unordered_map count; + for (char c : chars) { + count[c]++; + } + int res = 0; + for (const string& w : words) { + unordered_map curWord; + bool good = true; + for (char c : w) { + curWord[c]++; + if (curWord[c] > count[c]) { + good = false; + break; + } + } + if (good) { + res += w.size(); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string} chars + * @return {number} + */ + countCharacters(words, chars) { + const count = {}; + for (const c of chars) { + count[c] = (count[c] || 0) + 1; + } + let res = 0; + for (const w of words) { + const curWord = {}; + let good = true; + for (const c of w) { + curWord[c] = (curWord[c] || 0) + 1; + if (curWord[c] > (count[c] || 0)) { + good = false; + break; + } + } + if (good) { + res += w.length; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + (m * k))$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +> Where $n$ is the length of $chars$, $m$ is the number of words and $k$ is the average length of each word. + +--- + +## 3. Hash Table + +::tabs-start + +```python +class Solution: + def countCharacters(self, words: List[str], chars: str) -> int: + count = [0] * 26 + for c in chars: + count[ord(c) - ord('a')] += 1 + + org = count[:] + res = 0 + + for w in words: + good = True + for c in w: + i = ord(c) - ord('a') + count[i] -= 1 + if count[i] < 0: + good = False + break + if good: + res += len(w) + + for i in range(26): + count[i] = org[i] + return res +``` + +```java +public class Solution { + public int countCharacters(String[] words, String chars) { + int[] count = new int[26]; + for (char c : chars.toCharArray()) { + count[c - 'a']++; + } + + int[] org = count.clone(); + int res = 0; + + for (String w : words) { + boolean good = true; + for (int i = 0; i < w.length(); i++) { + int j = w.charAt(i) - 'a'; + count[j]--; + if (count[j] < 0) { + good = false; + break; + } + } + if (good) { + res += w.length(); + } + for (int i = 0; i < 26; i++) { + count[i] = org[i]; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int countCharacters(vector& words, string chars) { + vector count(26, 0); + for (char c : chars) { + count[c - 'a']++; + } + + vector org = count; + int res = 0; + + for (string& w : words) { + bool good = true; + for (char& c : w) { + int i = c - 'a'; + count[i]--; + if (count[i] < 0) { + good = false; + break; + } + } + if (good) { + res += w.length(); + } + for (int i = 0; i < 26; i++) { + count[i] = org[i]; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string} chars + * @return {number} + */ + countCharacters(words, chars) { + const count = new Array(26).fill(0); + for (let c of chars) { + count[c.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + + const org = [...count]; + let res = 0; + + for (let w of words) { + let good = true; + for (let c of w) { + const i = c.charCodeAt(0) - 'a'.charCodeAt(0); + count[i]--; + if (count[i] < 0) { + good = false; + break; + } + } + + if (good) { + res += w.length; + } + for (let i = 0; i < 26; i++) { + count[i] = org[i]; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + (m * k))$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +> Where $n$ is the length of $chars$, $m$ is the number of words and $k$ is the average length of each word. \ No newline at end of file diff --git a/articles/first-bad-version.md b/articles/first-bad-version.md new file mode 100644 index 000000000..12305c833 --- /dev/null +++ b/articles/first-bad-version.md @@ -0,0 +1,373 @@ +## 1. Brute Force (Linear Search) + +::tabs-start + +```python +# The isBadVersion API is already defined for you. +# def isBadVersion(version: int) -> bool: + +class Solution: + def firstBadVersion(self, n: int) -> int: + for i in range(1, n): + if isBadVersion(i): + return i + return n +``` + +```java +/* The isBadVersion API is defined in the parent class VersionControl. + boolean isBadVersion(int version); */ + +public class Solution extends VersionControl { + public int firstBadVersion(int n) { + for (int i = 1; i < n; i++) { + if (isBadVersion(i)) { + return i; + } + } + return n; + } +} +``` + +```cpp +// The API isBadVersion is defined for you. +// bool isBadVersion(int version); + +class Solution { +public: + int firstBadVersion(int n) { + for (int i = 1; i < n; i++) { + if (isBadVersion(i)) { + return i; + } + } + return n; + } +}; +``` + +```javascript +// The isBadVersion API is already defined in the VersionControl class. +// isBadVersion(version: number): boolean + +class Solution extends VersionControl { + /** + * @param {number} n Total versions + * @return {number} The first bad version + */ + firstBadVersion(n) { + for (let i = 1; i < n; i++) { + if (this.isBadVersion(i)) { + return i; + } + } + return n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Recursive Binary Search + +::tabs-start + +```python +# The isBadVersion API is already defined for you. +# def isBadVersion(version: int) -> bool: + +class Solution: + def firstBadVersion(self, n: int) -> int: + def helper(l, r): + if l > r: + return l + m = l + (r - l) // 2 + if isBadVersion(m): + return helper(l, m - 1) + else: + return helper(m + 1, r) + + return helper(1, n) +``` + +```java +/* The isBadVersion API is defined in the parent class VersionControl. + boolean isBadVersion(int version); */ + +public class Solution extends VersionControl { + public int firstBadVersion(int n) { + return helper(1, n); + } + + private int helper(int l, int r) { + if (l > r) { + return l; + } + int m = l + (r - l) / 2; + if (isBadVersion(m)) { + return helper(l, m - 1); + } else { + return helper(m + 1, r); + } + } +} +``` + +```cpp +// The API isBadVersion is defined for you. +// bool isBadVersion(int version); + +class Solution { +public: + int firstBadVersion(int n) { + return helper(1, n); + } + +private: + int helper(int l, int r) { + if (l > r) { + return l; + } + int m = l + (r - l) / 2; + if (isBadVersion(m)) { + return helper(l, m - 1); + } else { + return helper(m + 1, r); + } + } +}; +``` + +```javascript +// The isBadVersion API is already defined in the VersionControl class. +// isBadVersion(version: number): boolean + +class Solution extends VersionControl { + /** + * @param {number} n Total versions + * @return {number} The first bad version + */ + firstBadVersion(n) { + const helper = (l, r) => { + if (l > r) { + return l; + } + const m = Math.floor(l + (r - l) / 2); + if (this.isBadVersion(m)) { + return helper(l, m - 1); + } else { + return helper(m + 1, r); + } + }; + return helper(1, n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ for recursion stack. + +--- + +## 3. Iterative Binary Search + +::tabs-start + +```python +# The isBadVersion API is already defined for you. +# def isBadVersion(version: int) -> bool: + +class Solution: + def firstBadVersion(self, n: int) -> int: + l, r = 1, n + res = -1 + while l <= r: + m = l + (r - l) // 2 + if isBadVersion(m): + res = m + r = m - 1 + else: + l = m + 1 + return res +``` + +```java +/* The isBadVersion API is defined in the parent class VersionControl. + boolean isBadVersion(int version); */ + +public class Solution extends VersionControl { + public int firstBadVersion(int n) { + int l = 1, r = n, res = -1; + while (l <= r) { + int m = l + (r - l) / 2; + if (isBadVersion(m)) { + res = m; + r = m - 1; + } else { + l = m + 1; + } + } + return res; + } +} +``` + +```cpp +// The API isBadVersion is defined for you. +// bool isBadVersion(int version); + +class Solution { +public: + int firstBadVersion(int n) { + int l = 1, r = n, res = -1; + while (l <= r) { + int m = l + (r - l) / 2; + if (isBadVersion(m)) { + res = m; + r = m - 1; + } else { + l = m + 1; + } + } + return res; + } +}; +``` + +```javascript +// The isBadVersion API is already defined in the VersionControl class. +// isBadVersion(version: number): boolean + +class Solution extends VersionControl { + /** + * @param {number} n Total versions + * @return {number} The first bad version + */ + firstBadVersion(n) { + let l = 1, r = n, res = -1; + while (l <= r) { + const m = Math.floor(l + (r - l) / 2); + if (this.isBadVersion(m)) { + res = m; + r = m - 1; + } else { + l = m + 1; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Iterative Binary Search (Lower Bound) + +::tabs-start + +```python +# The isBadVersion API is already defined for you. +# def isBadVersion(version: int) -> bool: + +class Solution: + def firstBadVersion(self, n: int) -> int: + l, r = 1, n + while l < r: + m = l + (r - l) // 2 + if isBadVersion(m): + r = m + else: + l = m + 1 + return l +``` + +```java +/* The isBadVersion API is defined in the parent class VersionControl. + boolean isBadVersion(int version); */ + +public class Solution extends VersionControl { + public int firstBadVersion(int n) { + int l = 1, r = n; + while (l < r) { + int m = l + (r - l) / 2; + if (isBadVersion(m)) { + r = m; + } else { + l = m + 1; + } + } + return r; + } +} +``` + +```cpp +// The API isBadVersion is defined for you. +// bool isBadVersion(int version); + +class Solution { +public: + int firstBadVersion(int n) { + int l = 1, r = n; + while (l < r) { + int m = l + (r - l) / 2; + if (isBadVersion(m)) { + r = m; + } else { + l = m + 1; + } + } + return r; + } +}; +``` + +```javascript +// The isBadVersion API is already defined in the VersionControl class. +// isBadVersion(version: number): boolean + +class Solution extends VersionControl { + /** + * @param {number} n Total versions + * @return {number} The first bad version + */ + firstBadVersion(n) { + let l = 1, r = n; + while (l < r) { + const m = Math.floor(l + (r - l) / 2); + if (this.isBadVersion(m)) { + r = m; + } else { + l = m + 1; + } + } + return r; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/first-missing-positive.md b/articles/first-missing-positive.md new file mode 100644 index 000000000..09f5f9678 --- /dev/null +++ b/articles/first-missing-positive.md @@ -0,0 +1,667 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def firstMissingPositive(self, nums: List[int]) -> int: + missing = 1 + while True: + flag = True + for num in nums: + if missing == num: + flag = False + break + + if flag: + return missing + missing += 1 +``` + +```java +public class Solution { + public int firstMissingPositive(int[] nums) { + int missing = 1; + while (true) { + boolean flag = true; + for (int num : nums) { + if (missing == num) { + flag = false; + break; + } + } + if (flag) return missing; + missing++; + } + } +} +``` + +```cpp +class Solution { +public: + int firstMissingPositive(vector& nums) { + int missing = 1; + while (true) { + bool flag = true; + for (int& num : nums) { + if (missing == num) { + flag = false; + break; + } + } + if (flag) return missing; + missing++; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + firstMissingPositive(nums) { + let missing = 1; + while (true) { + let flag = true; + for (let num of nums) { + if (missing === num) { + flag = false; + break; + } + } + if (flag) return missing; + missing++; + } + } +} +``` + +```csharp +public class Solution { + public int FirstMissingPositive(int[] nums) { + int missing = 1; + + while (true) { + bool found = false; + + foreach (int num in nums) { + if (num == missing) { + found = true; + break; + } + } + + if (!found) { + return missing; + } + + missing++; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Boolean Array + +::tabs-start + +```python +class Solution: + def firstMissingPositive(self, nums: list[int]) -> int: + n = len(nums) + seen = [False] * n + for num in nums: + if num > 0 and num <= n: + seen[num - 1] = True + + for num in range(1, n + 1): + if not seen[num - 1]: + return num + + return n + 1 +``` + +```java +public class Solution { + public int firstMissingPositive(int[] nums) { + int n = nums.length; + boolean[] seen = new boolean[n]; + + for (int num : nums) { + if (num > 0 && num <= n) { + seen[num - 1] = true; + } + } + + for (int i = 0; i < n; i++) { + if (!seen[i]) { + return i + 1; + } + } + + return n + 1; + } +} +``` + +```cpp +class Solution { +public: + int firstMissingPositive(vector& nums) { + int n = nums.size(); + vector seen(n, false); + + for (int num : nums) { + if (num > 0 && num <= n) { + seen[num - 1] = true; + } + } + + for (int i = 0; i < n; i++) { + if (!seen[i]) { + return i + 1; + } + } + + return n + 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + firstMissingPositive(nums) { + const n = nums.length; + const seen = new Array(n).fill(false); + + for (const num of nums) { + if (num > 0 && num <= n) { + seen[num - 1] = true; + } + } + + for (let i = 0; i < n; i++) { + if (!seen[i]) { + return i + 1; + } + } + + return n + 1; + } +} +``` + +```csharp +public class Solution { + public int FirstMissingPositive(int[] nums) { + int n = nums.Length; + bool[] seen = new bool[n]; + + foreach (int num in nums) { + if (num > 0 && num <= n) { + seen[num - 1] = true; + } + } + + for (int num = 1; num <= n; num++) { + if (!seen[num - 1]) { + return num; + } + } + + return n + 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sorting + +::tabs-start + +```python +class Solution: + def firstMissingPositive(self, nums: list[int]) -> int: + nums.sort() + missing = 1 + for num in nums: + if num > 0 and missing == num: + missing += 1 + return missing +``` + +```java +public class Solution { + public int firstMissingPositive(int[] nums) { + Arrays.sort(nums); + int missing = 1; + for (int num : nums) { + if (num > 0 && missing == num) { + missing++; + } + } + return missing; + } +} +``` + +```cpp +class Solution { +public: + int firstMissingPositive(vector& nums) { + sort(nums.begin(), nums.end()); + int missing = 1; + for (int num : nums) { + if (num > 0 && missing == num) { + missing++; + } + } + return missing; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + firstMissingPositive(nums) { + nums.sort((a, b) => a - b); + let missing = 1; + for (const num of nums) { + if (num > 0 && missing === num) { + missing++; + } + } + return missing; + } +} +``` + +```csharp +public class Solution { + public int FirstMissingPositive(int[] nums) { + Array.Sort(nums); + int missing = 1; + + foreach (int num in nums) { + if (num > 0 && num == missing) { + missing++; + } + } + + return missing; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 4. Negative Marking + +::tabs-start + +```python +class Solution: + def firstMissingPositive(self, nums: list[int]) -> int: + for i in range(len(nums)): + if nums[i] < 0: + nums[i] = 0 + + for i in range(len(nums)): + val = abs(nums[i]) + if 1 <= val <= len(nums): + if nums[val - 1] > 0: + nums[val - 1] *= -1 + elif nums[val - 1] == 0: + nums[val - 1] = -1 * (len(nums) + 1) + + for i in range(1, len(nums) + 1): + if nums[i - 1] >= 0: + return i + + return len(nums) + 1 +``` + +```java +public class Solution { + public int firstMissingPositive(int[] nums) { + int n = nums.length; + + for (int i = 0; i < n; i++) { + if (nums[i] < 0) { + nums[i] = 0; + } + } + + for (int i = 0; i < n; i++) { + int val = Math.abs(nums[i]); + if (val >= 1 && val <= n) { + if (nums[val - 1] > 0) { + nums[val - 1] *= -1; + } else if (nums[val - 1] == 0) { + nums[val - 1] = -1 * (n + 1); + } + } + } + + for (int i = 1; i <= n; i++) { + if (nums[i - 1] >= 0) { + return i; + } + } + + return n + 1; + } +} +``` + +```cpp +class Solution { +public: + int firstMissingPositive(vector& nums) { + int n = nums.size(); + + for (int i = 0; i < n; i++) { + if (nums[i] < 0) { + nums[i] = 0; + } + } + + for (int i = 0; i < n; i++) { + int val = abs(nums[i]); + if (val >= 1 && val <= n) { + if (nums[val - 1] > 0) { + nums[val - 1] *= -1; + } else if (nums[val - 1] == 0) { + nums[val - 1] = -1 * (n + 1); + } + } + } + + for (int i = 1; i <= n; i++) { + if (nums[i - 1] >= 0) { + return i; + } + } + + return n + 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + firstMissingPositive(nums) { + const n = nums.length; + + for (let i = 0; i < n; i++) { + if (nums[i] < 0) { + nums[i] = 0; + } + } + + for (let i = 0; i < n; i++) { + const val = Math.abs(nums[i]); + if (val >= 1 && val <= n) { + if (nums[val - 1] > 0) { + nums[val - 1] *= -1; + } else if (nums[val - 1] === 0) { + nums[val - 1] = -1 * (n + 1); + } + } + } + + for (let i = 1; i <= n; i++) { + if (nums[i - 1] >= 0) { + return i; + } + } + + return n + 1; + } +} +``` + +```csharp +public class Solution { + public int FirstMissingPositive(int[] nums) { + int n = nums.Length; + + for (int i = 0; i < n; i++) { + if (nums[i] < 0) { + nums[i] = 0; + } + } + + for (int i = 0; i < n; i++) { + int val = Math.Abs(nums[i]); + if (val >= 1 && val <= n) { + if (nums[val - 1] > 0) { + nums[val - 1] *= -1; + } else if (nums[val - 1] == 0) { + nums[val - 1] = -(n + 1); + } + } + } + + for (int i = 0; i < n; i++) { + if (nums[i] >= 0) { + return i + 1; + } + } + + return n + 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 5. Cycle Sort + +::tabs-start + +```python +class Solution: + def firstMissingPositive(self, nums: list[int]) -> int: + n = len(nums) + i = 0 + while i < n: + if nums[i] <= 0 or nums[i] > n: + i += 1 + continue + + index = nums[i] - 1 + if nums[i] != nums[index]: + nums[i], nums[index] = nums[index], nums[i] + else: + i += 1 + + for i in range(n): + if nums[i] != i + 1: + return i + 1 + + return n + 1 +``` + +```java +public class Solution { + public int firstMissingPositive(int[] nums) { + int n = nums.length; + int i = 0; + + while (i < n) { + if (nums[i] <= 0 || nums[i] > n) { + i++; + continue; + } + int index = nums[i] - 1; + if (nums[i] != nums[index]) { + int temp = nums[i]; + nums[i] = nums[index]; + nums[index] = temp; + } else { + i++; + } + } + + for (i = 0; i < n; i++) { + if (nums[i] != i + 1) { + return i + 1; + } + } + + return n + 1; + } +} +``` + +```cpp +class Solution { +public: + int firstMissingPositive(vector& nums) { + int n = nums.size(); + int i = 0; + + while (i < n) { + if (nums[i] <= 0 || nums[i] > n) { + i++; + continue; + } + int index = nums[i] - 1; + if (nums[i] != nums[index]) { + swap(nums[i], nums[index]); + } else { + i++; + } + } + + for (i = 0; i < n; i++) { + if (nums[i] != i + 1) { + return i + 1; + } + } + + return n + 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + firstMissingPositive(nums) { + let n = nums.length; + let i = 0; + while (i < n) { + if (nums[i] <= 0 || nums[i] > n) { + i++; + continue; + } + let index = nums[i] - 1; + if (nums[i] != nums[index]) { + [nums[i], nums[index]] = [nums[index], nums[i]]; + } else { + i++; + } + } + + for (let i = 0; i < n; i++) { + if (nums[i] != i + 1) { + return i + 1; + } + } + + return n + 1; + } +} +``` + +```csharp +public class Solution { + public int FirstMissingPositive(int[] nums) { + int n = nums.Length; + int i = 0; + + while (i < n) { + if (nums[i] <= 0 || nums[i] > n) { + i++; + continue; + } + + int index = nums[i] - 1; + if (nums[i] != nums[index]) { + int temp = nums[i]; + nums[i] = nums[index]; + nums[index] = temp; + } else { + i++; + } + } + + for (i = 0; i < n; i++) { + if (nums[i] != i + 1) { + return i + 1; + } + } + + return n + 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/first-unique-character-in-a-string.md b/articles/first-unique-character-in-a-string.md new file mode 100644 index 000000000..b60c2e4f6 --- /dev/null +++ b/articles/first-unique-character-in-a-string.md @@ -0,0 +1,365 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def firstUniqChar(self, s: str) -> int: + for i in range(len(s)): + flag = True + for j in range(len(s)): + if i == j: + continue + if s[i] == s[j]: + flag = False + break + if flag: + return i + return -1 +``` + +```java +public class Solution { + public int firstUniqChar(String s) { + for (int i = 0; i < s.length(); i++) { + boolean flag = true; + for (int j = 0; j < s.length(); j++) { + if (i == j) continue; + if (s.charAt(i) == s.charAt(j)) { + flag = false; + break; + } + } + if (flag) return i; + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int firstUniqChar(string s) { + for (int i = 0; i < s.size(); i++) { + bool flag = true; + for (int j = 0; j < s.size(); j++) { + if (i == j) continue; + if (s[i] == s[j]) { + flag = false; + break; + } + } + if (flag) return i; + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + firstUniqChar(s) { + for (let i = 0; i < s.length; i++) { + let flag = true; + for (let j = 0; j < s.length; j++) { + if (i === j) continue; + if (s[i] === s[j]) { + flag = false; + break; + } + } + if (flag) return i; + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def firstUniqChar(self, s: str) -> int: + count = defaultdict(int) + for c in s: + count[c] += 1 + + for i, c in enumerate(s): + if count[c] == 1: + return i + return -1 +``` + +```java +public class Solution { + public int firstUniqChar(String s) { + Map count = new HashMap<>(); + for (char c : s.toCharArray()) { + count.put(c, count.getOrDefault(c, 0) + 1); + } + + for (int i = 0; i < s.length(); i++) { + if (count.get(s.charAt(i)) == 1) { + return i; + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int firstUniqChar(string s) { + unordered_map count; + for (char c : s) { + count[c]++; + } + + for (int i = 0; i < s.size(); i++) { + if (count[s[i]] == 1) { + return i; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + firstUniqChar(s) { + const count = new Map(); + for (const c of s) { + count.set(c, (count.get(c) || 0) + 1); + } + + for (let i = 0; i < s.length; i++) { + if (count.get(s[i]) === 1) { + return i; + } + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +--- + +## 3. Hash Map (Optimal) + +::tabs-start + +```python +class Solution: + def firstUniqChar(self, s: str) -> int: + n = len(s) + count = defaultdict(int) + for i, c in enumerate(s): + if c not in count: + count[c] = i + else: + count[c] = n + + res = n + for c in count: + res = min(res, count[c]) + + return -1 if res == n else res +``` + +```java +public class Solution { + public int firstUniqChar(String s) { + int n = s.length(); + Map count = new HashMap<>(); + + for (int i = 0; i < n; i++) { + char c = s.charAt(i); + if (!count.containsKey(c)) { + count.put(c, i); + } else { + count.put(c, n); + } + } + + int res = n; + for (int index : count.values()) { + res = Math.min(res, index); + } + + return res == n ? -1 : res; + } +} +``` + +```cpp +class Solution { +public: + int firstUniqChar(string s) { + int n = s.size(); + unordered_map count; + + for (int i = 0; i < n; i++) { + if (count.find(s[i]) == count.end()) { + count[s[i]] = i; + } else { + count[s[i]] = n; + } + } + + int res = n; + for (auto& [key, index] : count) { + res = min(res, index); + } + + return res == n ? -1 : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + firstUniqChar(s) { + const n = s.length; + const count = new Map(); + + for (let i = 0; i < n; i++) { + const c = s[i]; + if (!count.has(c)) { + count.set(c, i); + } else { + count.set(c, n); + } + } + + let res = n; + for (const index of count.values()) { + res = Math.min(res, index); + } + + return res === n ? -1 : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +--- + +## 4. Iteration + +::tabs-start + +```python +class Solution: + def firstUniqChar(self, s: str) -> int: + res = n = len(s) + for ch in range(ord('a'), ord('z') + 1): + index = s.find(chr(ch)) + if index != -1 and s.rfind(chr(ch)) == index: + res = min(res, index) + + return -1 if res == n else res +``` + +```java +public class Solution { + public int firstUniqChar(String s) { + int res = s.length(); + + for (char ch = 'a'; ch <= 'z'; ch++) { + int firstIndex = s.indexOf(ch); + if (firstIndex != -1 && s.lastIndexOf(ch) == firstIndex) { + res = Math.min(res, firstIndex); + } + } + + return res == s.length() ? -1 : res; + } +} +``` + +```cpp +class Solution { +public: + int firstUniqChar(string s) { + int res = s.size(); + + for (char ch = 'a'; ch <= 'z'; ch++) { + int firstIndex = s.find(ch); + if (firstIndex != string::npos && s.rfind(ch) == firstIndex) { + res = min(res, firstIndex); + } + } + + return res == s.size() ? -1 : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + firstUniqChar(s) { + let res = s.length; + + for (let ch = 'a'.charCodeAt(0); ch <= 'z'.charCodeAt(0); ch++) { + const char = String.fromCharCode(ch); + const firstIndex = s.indexOf(char); + if (firstIndex !== -1 && s.lastIndexOf(char) === firstIndex) { + res = Math.min(res, firstIndex); + } + } + + return res === s.length ? -1 : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(26 * n)$ since we have at most $26$ different characters. +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/flatten-nested-list-iterator.md b/articles/flatten-nested-list-iterator.md new file mode 100644 index 000000000..6ac2342a2 --- /dev/null +++ b/articles/flatten-nested-list-iterator.md @@ -0,0 +1,579 @@ +## 1. Recursion (Flatten And Store Into Global List ) + +::tabs-start + +```python +class NestedIterator: + def __init__(self, nestedList): + self.arr = [] + self.ptr = 0 + self.dfs(nestedList) + + def next(self): + val = self.arr[self.ptr] + self.ptr += 1 + return val + + def hasNext(self): + return self.ptr < len(self.arr) + + def dfs(self, nestedArr): + for num in nestedArr: + if num.isInteger(): + self.arr.append(num.getInteger()) + else: + self.dfs(num.getList()) +``` + +```java +public class NestedIterator implements Iterator { + + private List arr; + private int ptr; + + public NestedIterator(List nestedList) { + arr = new ArrayList<>(); + ptr = 0; + dfs(nestedList); + } + + @Override + public Integer next() { + return arr.get(ptr++); + } + + @Override + public boolean hasNext() { + return ptr < arr.size(); + } + + private void dfs(List nestedArr) { + for (NestedInteger num : nestedArr) { + if (num.isInteger()) { + arr.add(num.getInteger()); + } else { + dfs(num.getList()); + } + } + } +} +``` + +```cpp +class NestedIterator { +private: + vector arr; + int ptr; + + void dfs(const vector &nestedArr) { + for (const auto &num : nestedArr) { + if (num.isInteger()) { + arr.push_back(num.getInteger()); + } else { + dfs(num.getList()); + } + } + } + +public: + NestedIterator(vector &nestedList) { + ptr = 0; + dfs(nestedList); + } + + int next() { + return arr[ptr++]; + } + + bool hasNext() { + return ptr < arr.size(); + } +}; +``` + +```javascript +class NestedIterator { + /** + * @constructor + * @param {NestedInteger[]} nestedList + */ + constructor(nestedList) { + this.arr = []; + this.ptr = 0; + this.dfs(nestedList); + } + + /** + * @param {NestedInteger[]} nestedArr + */ + dfs(nestedArr) { + for (let num of nestedArr) { + if (num.isInteger()) { + this.arr.push(num.getInteger()); + } else { + this.dfs(num.getList()); + } + } + } + + /** + * @this NestedIterator + * @returns {integer} + */ + next() { + return this.arr[this.ptr++]; + } + + /** + * @this NestedIterator + * @returns {boolean} + */ + hasNext() { + return this.ptr < this.arr.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + d)$ +* Space complexity: $O(n + d)$ + +> Where $n$ is the number of integers and $d$ is the nesting depth. + +--- + +## 2. Recursion (Flatten And Return) + +::tabs-start + +```python +class NestedIterator: + def __init__(self, nestedList): + self.arr = self.dfs(nestedList) + self.ptr = 0 + + def dfs(self, nestedArr): + res = [] + for num in nestedArr: + if num.isInteger(): + res.append(num.getInteger()) + else: + res.extend(self.dfs(num.getList())) + return res + + def next(self): + val = self.arr[self.ptr] + self.ptr += 1 + return val + + def hasNext(self): + return self.ptr < len(self.arr) +``` + +```java +public class NestedIterator implements Iterator { + private List arr; + private int ptr; + + public NestedIterator(List nestedList) { + arr = dfs(nestedList); + ptr = 0; + } + + private List dfs(List nestedArr) { + List res = new ArrayList<>(); + for (NestedInteger num : nestedArr) { + if (num.isInteger()) { + res.add(num.getInteger()); + } else { + res.addAll(dfs(num.getList())); + } + } + return res; + } + + @Override + public Integer next() { + return arr.get(ptr++); + } + + @Override + public boolean hasNext() { + return ptr < arr.size(); + } +} +``` + +```cpp +class NestedIterator { +private: + vector arr; + int ptr; + + vector dfs(const vector &nestedArr) { + vector res; + for (const auto &num : nestedArr) { + if (num.isInteger()) { + res.push_back(num.getInteger()); + } else { + vector temp = dfs(num.getList()); + res.insert(res.end(), temp.begin(), temp.end()); + } + } + return res; + } + +public: + NestedIterator(vector &nestedList) { + arr = dfs(nestedList); + ptr = 0; + } + + int next() { + return arr[ptr++]; + } + + bool hasNext() { + return ptr < arr.size(); + } +}; +``` + +```javascript +class NestedIterator { + /** + * @constructor + * @param {NestedInteger[]} nestedList + */ + constructor(nestedList) { + this.arr = this.dfs(nestedList); + this.ptr = 0; + } + + /** + * @param {NestedInteger[]} nestedArr + */ + dfs(nestedArr) { + let res = []; + for (let num of nestedArr) { + if (num.isInteger()) { + res.push(num.getInteger()); + } else { + res.push(...this.dfs(num.getList())); + } + } + return res; + } + + /** + * @this NestedIterator + * @returns {integer} + */ + next() { + return this.arr[this.ptr++]; + } + + /** + * @this NestedIterator + * @returns {boolean} + */ + hasNext() { + return this.ptr < this.arr.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + d)$ +* Space complexity: $O(n + d)$ + +> Where $n$ is the number of integers and $d$ is the nesting depth. + +--- + +## 3. Recursion + Stack + +::tabs-start + +```python +class NestedIterator: + def __init__(self, nestedList: [NestedInteger]): + self.stack = [] + self.dfs(nestedList) + self.stack.reverse() + + def next(self) -> int: + return self.stack.pop() + + def hasNext(self) -> bool: + return len(self.stack) > 0 + + def dfs(self, nested): + for num in nested: + if num.isInteger(): + self.stack.append(num.getInteger()) + else: + self.dfs(num.getList()) +``` + +```java +public class NestedIterator implements Iterator { + private Stack stack; + + public NestedIterator(List nestedList) { + stack = new Stack<>(); + dfs(nestedList); + Collections.reverse(stack); + } + + private void dfs(List nested) { + for (NestedInteger num : nested) { + if (num.isInteger()) { + stack.push(num.getInteger()); + } else { + dfs(num.getList()); + } + } + } + + @Override + public Integer next() { + return stack.pop(); + } + + @Override + public boolean hasNext() { + return !stack.isEmpty(); + } +} +``` + +```cpp +class NestedIterator { +private: + vector stack; + + void dfs(const vector &nested) { + for (const auto &num : nested) { + if (num.isInteger()) { + stack.push_back(num.getInteger()); + } else { + dfs(num.getList()); + } + } + } + +public: + NestedIterator(vector &nestedList) { + dfs(nestedList); + reverse(stack.begin(), stack.end()); + } + + int next() { + int val = stack.back(); + stack.pop_back(); + return val; + } + + bool hasNext() { + return !stack.empty(); + } +}; +``` + +```javascript +class NestedIterator { + /** + * @constructor + * @param {NestedInteger[]} nestedList + */ + constructor(nestedList) { + this.stack = []; + this.dfs(nestedList); + this.stack.reverse(); + } + + /** + * @param {NestedInteger[]} nested + */ + dfs(nested) { + for (let num of nested) { + if (num.isInteger()) { + this.stack.push(num.getInteger()); + } else { + this.dfs(num.getList()); + } + } + } + + /** + * @this NestedIterator + * @returns {number} + */ + next() { + return this.stack.pop(); + } + + /** + * @this NestedIterator + * @returns {boolean} + */ + hasNext() { + return this.stack.length > 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + d)$ +* Space complexity: $O(n + d)$ + +> Where $n$ is the number of integers and $d$ is the nesting depth. + +--- + +## 4. Stack + +::tabs-start + +```python +class NestedIterator: + def __init__(self, nestedList: [NestedInteger]): + self.stack = nestedList + self.stack.reverse() + + def next(self) -> int: + return self.stack.pop().getInteger() + + def hasNext(self) -> bool: + while self.stack: + top = self.stack[-1] + if top.isInteger(): + return True + + self.stack.pop() + self.stack.extend(reversed(top.getList())) + return False +``` + +```java +public class NestedIterator implements Iterator { + private List stack; + + public NestedIterator(List nestedList) { + stack = new ArrayList<>(nestedList); + Collections.reverse(stack); + } + + @Override + public Integer next() { + return stack.remove(stack.size() - 1).getInteger(); + } + + @Override + public boolean hasNext() { + while (!stack.isEmpty()) { + NestedInteger top = stack.get(stack.size() - 1); + if (top.isInteger()) { + return true; + } + stack.remove(stack.size() - 1); + List nestedList = top.getList(); + Collections.reverse(nestedList); + stack.addAll(nestedList); + } + return false; + } +} +``` + +```cpp +class NestedIterator { +private: + vector stack; + +public: + NestedIterator(vector &nestedList) { + stack = nestedList; + reverse(stack.begin(), stack.end()); + } + + int next() { + int val = stack.back().getInteger(); + stack.pop_back(); + return val; + } + + bool hasNext() { + while (!stack.empty()) { + NestedInteger top = stack.back(); + if (top.isInteger()) { + return true; + } + stack.pop_back(); + vector nestedList = top.getList(); + for (auto it = nestedList.rbegin(); it != nestedList.rend(); ++it) { + stack.push_back(*it); + } + } + return false; + } +}; +``` + +```javascript +class NestedIterator { + /** + * @constructor + * @param {NestedInteger[]} nestedList + */ + constructor(nestedList) { + this.stack = nestedList; + this.stack.reverse(); + } + + /** + * @this NestedIterator + * @returns {number} + */ + next() { + return this.stack.pop().getInteger(); + } + + /** + * @this NestedIterator + * @returns {boolean} + */ + hasNext() { + while (this.stack.length > 0) { + const top = this.stack[this.stack.length - 1]; + if (top.isInteger()) { + return true; + } + this.stack.pop(); + const nestedList = top.getList(); + nestedList.reverse(); + this.stack.push(...nestedList); + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + d)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of integers and $d$ is the nesting depth. \ No newline at end of file diff --git a/articles/flip-equivalent-binary-trees.md b/articles/flip-equivalent-binary-trees.md new file mode 100644 index 000000000..2a2549798 --- /dev/null +++ b/articles/flip-equivalent-binary-trees.md @@ -0,0 +1,492 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def flipEquiv(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> bool: + if not root1 or not root2: + return not root1 and not root2 + if root1.val != root2.val: + return False + + return ( + self.flipEquiv(root1.left, root2.left) and + self.flipEquiv(root1.right, root2.right) or + self.flipEquiv(root1.left, root2.right) and + self.flipEquiv(root1.right, root2.left) + ) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean flipEquiv(TreeNode root1, TreeNode root2) { + if (root1 == null || root2 == null) + return root1 == null && root2 == null; + if (root1.val != root2.val) + return false; + + return (flipEquiv(root1.left, root2.left) && + flipEquiv(root1.right, root2.right) || + flipEquiv(root1.left, root2.right) && + flipEquiv(root1.right, root2.left)); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool flipEquiv(TreeNode* root1, TreeNode* root2) { + if (!root1 || !root2) + return !root1 && !root2; + if (root1->val != root2->val) + return false; + + return (flipEquiv(root1->left, root2->left) && + flipEquiv(root1->right, root2->right) || + flipEquiv(root1->left, root2->right) && + flipEquiv(root1->right, root2->left)); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {boolean} + */ + flipEquiv(root1, root2) { + if (!root1 || !root2) + return !root1 && !root2; + if (root1.val !== root2.val) + return false; + + return (this.flipEquiv(root1.left, root2.left) && + this.flipEquiv(root1.right, root2.right) || + this.flipEquiv(root1.left, root2.right) && + this.flipEquiv(root1.right, root2.left)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def flipEquiv(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> bool: + q = deque([(root1, root2)]) + + while q: + node1, node2 = q.popleft() + if not node1 or not node2: + if node1 != node2: + return False + continue + + if node1.val != node2.val: + return False + + if ((node1.right and node2.right and + node1.right.val == node2.right.val) or + (not node1.right and not node2.right) + ): + q.append((node1.left, node2.left)) + q.append((node1.right, node2.right)) + else: + q.append((node1.left, node2.right)) + q.append((node1.right, node2.left)) + + return True +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean flipEquiv(TreeNode root1, TreeNode root2) { + Queue q = new LinkedList<>(); + q.offer(new TreeNode[]{root1, root2}); + + while (!q.isEmpty()) { + TreeNode[] pair = q.poll(); + TreeNode node1 = pair[0], node2 = pair[1]; + + if (node1 == null || node2 == null) { + if (node1 != node2) return false; + continue; + } + + if (node1.val != node2.val) return false; + + if ((node1.left != null && node2.left != null && + node1.left.val == node2.left.val) || + (node1.left == null && node2.left == null)) { + q.offer(new TreeNode[]{node1.left, node2.left}); + q.offer(new TreeNode[]{node1.right, node2.right}); + } else { + q.offer(new TreeNode[]{node1.left, node2.right}); + q.offer(new TreeNode[]{node1.right, node2.left}); + } + } + + return true; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool flipEquiv(TreeNode* root1, TreeNode* root2) { + queue> q; + q.push({root1, root2}); + + while (!q.empty()) { + auto [node1, node2] = q.front(); + q.pop(); + + if (!node1 || !node2) { + if (node1 != node2) return false; + continue; + } + + if (node1->val != node2->val) return false; + + if ((node1->left && node2->left && node1->left->val == node2->left->val) || + (!node1->left && !node2->left)) { + q.push({node1->left, node2->left}); + q.push({node1->right, node2->right}); + } else { + q.push({node1->left, node2->right}); + q.push({node1->right, node2->left}); + } + } + + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {boolean} + */ + flipEquiv(root1, root2) { + let q = new Queue([[root1, root2]]); + + while (!q.isEmpty()) { + let [node1, node2] = q.pop(); + + if (!node1 || !node2) { + if (node1 !== node2) return false; + continue; + } + + if (node1.val !== node2.val) return false; + + if ((node1.left && node2.left && + node1.left.val === node2.left.val) || + (!node1.left && !node2.left)) { + q.push([node1.left, node2.left]); + q.push([node1.right, node2.right]); + } else { + q.push([node1.left, node2.right]); + q.push([node1.right, node2.left]); + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def flipEquiv(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> bool: + stack = [(root1, root2)] + + while stack: + node1, node2 = stack.pop() + + if not node1 or not node2: + if node1 != node2: + return False + continue + + if node1.val != node2.val: + return False + + if ((node1.left and node2.left and + node1.left.val == node2.left.val) or + (not node1.left and not node2.left) + ): + stack.append((node1.left, node2.left)) + stack.append((node1.right, node2.right)) + else: + stack.append((node1.left, node2.right)) + stack.append((node1.right, node2.left)) + + return True +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean flipEquiv(TreeNode root1, TreeNode root2) { + Stack stack = new Stack<>(); + stack.push(new TreeNode[]{root1, root2}); + + while (!stack.isEmpty()) { + TreeNode[] pair = stack.pop(); + TreeNode node1 = pair[0], node2 = pair[1]; + + if (node1 == null || node2 == null) { + if (node1 != node2) return false; + continue; + } + + if (node1.val != node2.val) return false; + + if ((node1.left != null && node2.left != null && + node1.left.val == node2.left.val) || + (node1.left == null && node2.left == null)) { + stack.push(new TreeNode[]{node1.left, node2.left}); + stack.push(new TreeNode[]{node1.right, node2.right}); + } else { + stack.push(new TreeNode[]{node1.left, node2.right}); + stack.push(new TreeNode[]{node1.right, node2.left}); + } + } + + return true; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool flipEquiv(TreeNode* root1, TreeNode* root2) { + stack> stk; + stk.push({root1, root2}); + + while (!stk.empty()) { + auto [node1, node2] = stk.top();stk.pop(); + + if (!node1 || !node2) { + if (node1 != node2) return false; + continue; + } + + if (node1->val != node2->val) return false; + + if ((node1->left && node2->left && node1->left->val == node2->left->val) || + (!node1->left && !node2->left)) { + stk.push({node1->left, node2->left}); + stk.push({node1->right, node2->right}); + } else { + stk.push({node1->left, node2->right}); + stk.push({node1->right, node2->left}); + } + } + + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {boolean} + */ + flipEquiv(root1, root2) { + let stack = [[root1, root2]]; + + while (stack.length > 0) { + let [node1, node2] = stack.pop(); + + if (!node1 || !node2) { + if (node1 !== node2) return false; + continue; + } + + if (node1.val !== node2.val) return false; + + if ((node1.left && node2.left && + node1.left.val === node2.left.val) || + (!node1.left && !node2.left)) { + stack.push([node1.left, node2.left]); + stack.push([node1.right, node2.right]); + } else { + stack.push([node1.left, node2.right]); + stack.push([node1.right, node2.left]); + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/flip-string-to-monotone-increasing.md b/articles/flip-string-to-monotone-increasing.md new file mode 100644 index 000000000..74c357f57 --- /dev/null +++ b/articles/flip-string-to-monotone-increasing.md @@ -0,0 +1,523 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minFlipsMonoIncr(self, s: str) -> int: + dp = {} + + def dfs(i, mono): + if (i, mono) in dp: + return dp[(i, mono)] + if i == len(s): + return 0 + + if mono and s[i] == "0": + dp[(i, mono)] = min(1 + dfs(i + 1, False), dfs(i + 1, mono)) + elif mono and s[i] == "1": + dp[(i, mono)] = min(1 + dfs(i + 1, mono), dfs(i + 1, False)) + elif not mono and s[i] == "1": + dp[(i, mono)] = dfs(i + 1, mono) + else: + dp[(i, mono)] = 1 + dfs(i + 1, mono) + + return dp[(i, mono)] + + return dfs(0, True) +``` + +```java +public class Solution { + private int[][] dp; + + public int minFlipsMonoIncr(String s) { + int n = s.length(); + dp = new int[n][2]; + for (int i = 0; i < n; i++) { + dp[i][0] = dp[i][1] = -1; + } + return dfs(0, 1, s); + } + + private int dfs(int i, int mono, String s) { + if (i == s.length()) return 0; + if (dp[i][mono] != -1) return dp[i][mono]; + + if (mono == 1 && s.charAt(i) == '0') { + dp[i][mono] = Math.min(1 + dfs(i + 1, 0, s), dfs(i + 1, mono, s)); + } else if (mono == 1 && s.charAt(i) == '1') { + dp[i][mono] = Math.min(1 + dfs(i + 1, mono, s), dfs(i + 1, 0, s)); + } else if (mono == 0 && s.charAt(i) == '1') { + dp[i][mono] = dfs(i + 1, mono, s); + } else { + dp[i][mono] = 1 + dfs(i + 1, mono, s); + } + return dp[i][mono]; + } +} +``` + +```cpp +class Solution { +public: + int minFlipsMonoIncr(string s) { + int n = s.length(); + vector> dp(n, vector(2, -1)); + return dfs(0, 1, s, dp); + } + +private: + int dfs(int i, int mono, const string& s, vector>& dp) { + if (i == s.length()) return 0; + if (dp[i][mono] != -1) return dp[i][mono]; + + if (mono == 1 && s[i] == '0') { + dp[i][mono] = min(1 + dfs(i + 1, 0, s, dp), dfs(i + 1, mono, s, dp)); + } else if (mono == 1 && s[i] == '1') { + dp[i][mono] = min(1 + dfs(i + 1, mono, s, dp), dfs(i + 1, 0, s, dp)); + } else if (mono == 0 && s[i] == '1') { + dp[i][mono] = dfs(i + 1, mono, s, dp); + } else { + dp[i][mono] = 1 + dfs(i + 1, mono, s, dp); + } + return dp[i][mono]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minFlipsMonoIncr(s) { + const n = s.length; + const dp = Array.from({ length: n }, () => Array(2).fill(-1)); + + const dfs = (i, mono) => { + if (i === n) return 0; + if (dp[i][mono] !== -1) return dp[i][mono]; + + if (mono === 1 && s[i] === '0') { + dp[i][mono] = Math.min(1 + dfs(i + 1, 0), dfs(i + 1, mono)); + } else if (mono === 1 && s[i] === '1') { + dp[i][mono] = Math.min(1 + dfs(i + 1, mono), dfs(i + 1, 0)); + } else if (mono === 0 && s[i] === '1') { + dp[i][mono] = dfs(i + 1, mono); + } else { + dp[i][mono] = 1 + dfs(i + 1, mono); + } + return dp[i][mono]; + }; + + return dfs(0, 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minFlipsMonoIncr(self, s: str) -> int: + n = len(s) + dp = [[0] * 2 for _ in range(n + 1)] + + for i in range(n - 1, -1, -1): + if s[i] == '0': + dp[i][1] = min(1 + dp[i + 1][0], dp[i + 1][1]) + dp[i][0] = 1 + dp[i + 1][0] + else: # s[i] == '1' + dp[i][1] = min(1 + dp[i + 1][1], dp[i + 1][0]) + dp[i][0] = dp[i + 1][0] + + return dp[0][1] +``` + +```java +public class Solution { + public int minFlipsMonoIncr(String s) { + int n = s.length(); + int[][] dp = new int[n + 1][2]; + + for (int i = n - 1; i >= 0; i--) { + if (s.charAt(i) == '0') { + dp[i][1] = Math.min(1 + dp[i + 1][0], dp[i + 1][1]); + dp[i][0] = 1 + dp[i + 1][0]; + } else { // s.charAt(i) == '1' + dp[i][1] = Math.min(1 + dp[i + 1][1], dp[i + 1][0]); + dp[i][0] = dp[i + 1][0]; + } + } + + return dp[0][1]; + } +} +``` + +```cpp +class Solution { +public: + int minFlipsMonoIncr(string s) { + int n = s.size(); + vector> dp(n + 1, vector(2, 0)); + + for (int i = n - 1; i >= 0; i--) { + if (s[i] == '0') { + dp[i][1] = min(1 + dp[i + 1][0], dp[i + 1][1]); + dp[i][0] = 1 + dp[i + 1][0]; + } else { // s[i] == '1' + dp[i][1] = min(1 + dp[i + 1][1], dp[i + 1][0]); + dp[i][0] = dp[i + 1][0]; + } + } + + return dp[0][1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minFlipsMonoIncr(s) { + const n = s.length; + const dp = Array.from({ length: n + 1 }, () => [0, 0]); + + for (let i = n - 1; i >= 0; i--) { + if (s[i] === '0') { + dp[i][1] = Math.min(1 + dp[i + 1][0], dp[i + 1][1]); + dp[i][0] = 1 + dp[i + 1][0]; + } else { // s[i] === '1' + dp[i][1] = Math.min(1 + dp[i + 1][1], dp[i + 1][0]); + dp[i][0] = dp[i + 1][0]; + } + } + + return dp[0][1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def minFlipsMonoIncr(self, s: str) -> int: + n = len(s) + dp = [0, 0] + + for i in range(n - 1, -1, -1): + if s[i] == '0': + new_dp_1 = min(1 + dp[0], dp[1]) + new_dp_0 = dp[0] + 1 + else: # s[i] == '1' + new_dp_1 = min(dp[1] + 1, dp[0]) + new_dp_0 = dp[0] + + dp[1] = new_dp_1 + dp[0] = new_dp_0 + + return dp[1] +``` + +```java +public class Solution { + public int minFlipsMonoIncr(String s) { + int[] dp = new int[2]; + + for (int i = s.length() - 1; i >= 0; i--) { + int newDp1, newDp0; + if (s.charAt(i) == '0') { + newDp1 = Math.min(1 + dp[0], dp[1]); + newDp0 = 1 + dp[0]; + } else { // s[i] == '1' + newDp1 = Math.min(1 + dp[1], dp[0]); + newDp0 = dp[0]; + } + + dp[1] = newDp1; + dp[0] = newDp0; + } + + return dp[1]; + } +} +``` + +```cpp +class Solution { +public: + int minFlipsMonoIncr(string s) { + vector dp(2, 0); + + for (int i = s.length() - 1; i >= 0; i--) { + int newDp1, newDp0; + if (s[i] == '0') { + newDp1 = min(1 + dp[0], dp[1]); + newDp0 = dp[0] + 1; + } else { // s[i] == '1' + newDp1 = min(1 + dp[1], dp[0]); + newDp0 = dp[0]; + } + + dp[1] = newDp1; + dp[0] = newDp0; + } + + return dp[1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minFlipsMonoIncr(s) { + let dp = [0, 0]; + + for (let i = s.length - 1; i >= 0; i--) { + let newDp1, newDp0; + if (s[i] === '0') { + newDp1 = Math.min(1 + dp[0], dp[1]); + newDp0 = dp[0] + 1; + } else { // s[i] === '1' + newDp1 = Math.min(1 + dp[1], dp[0]); + newDp0 = dp[0]; + } + + dp[1] = newDp1; + dp[0] = newDp0; + } + + return dp[1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Prefix & Suffix Arrays + +::tabs-start + +```python +class Solution: + def minFlipsMonoIncr(self, s: str) -> int: + n = len(s) + left_ones = [0] * (n + 1) + right_zeros = [0] * (n + 1) + + for i in range(n): + left_ones[i + 1] = left_ones[i] + (1 if s[i] == '1' else 0) + + for i in range(n - 1, -1, -1): + right_zeros[i] = right_zeros[i + 1] + (1 if s[i] == '0' else 0) + + res = float('inf') + for i in range(n + 1): + res = min(res, left_ones[i] + right_zeros[i]) + + return res +``` + +```java +public class Solution { + public int minFlipsMonoIncr(String s) { + int n = s.length(); + int[] leftOnes = new int[n + 1]; + int[] rightZeros = new int[n + 1]; + + for (int i = 0; i < n; i++) { + leftOnes[i + 1] = leftOnes[i] + (s.charAt(i) == '1' ? 1 : 0); + } + + for (int i = n - 1; i >= 0; i--) { + rightZeros[i] = rightZeros[i + 1] + (s.charAt(i) == '0' ? 1 : 0); + } + + int res = Integer.MAX_VALUE; + for (int i = 0; i <= n; i++) { + res = Math.min(res, leftOnes[i] + rightZeros[i]); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minFlipsMonoIncr(string s) { + int n = s.size(); + vector leftOnes(n + 1, 0), rightZeros(n + 1, 0); + + for (int i = 0; i < n; i++) { + leftOnes[i + 1] = leftOnes[i] + (s[i] == '1' ? 1 : 0); + } + + for (int i = n - 1; i >= 0; i--) { + rightZeros[i] = rightZeros[i + 1] + (s[i] == '0' ? 1 : 0); + } + + int res = INT_MAX; + for (int i = 0; i <= n; i++) { + res = min(res, leftOnes[i] + rightZeros[i]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minFlipsMonoIncr(s) { + const n = s.length; + const leftOnes = Array(n + 1).fill(0); + const rightZeros = Array(n + 1).fill(0); + + for (let i = 0; i < n; i++) { + leftOnes[i + 1] = leftOnes[i] + (s[i] === '1' ? 1 : 0); + } + + for (let i = n - 1; i >= 0; i--) { + rightZeros[i] = rightZeros[i + 1] + (s[i] === '0' ? 1 : 0); + } + + let res = Infinity; + for (let i = 0; i <= n; i++) { + res = Math.min(res, leftOnes[i] + rightZeros[i]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def minFlipsMonoIncr(self, s: str) -> int: + res = cntOne = 0 + for c in s: + if c == '1': + cntOne += 1 + else: + res = min(res + 1, cntOne) + return res +``` + +```java +public class Solution { + public int minFlipsMonoIncr(String s) { + int res = 0, cntOne = 0; + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == '1') { + cntOne++; + } else { + res = Math.min(res + 1, cntOne); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minFlipsMonoIncr(string s) { + int res = 0, cntOne = 0; + for (char& c : s) { + if (c == '1') { + cntOne++; + } else { + res = min(res + 1, cntOne); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minFlipsMonoIncr(s) { + let res = 0, cntOne = 0; + for (let c of s) { + if (c === '1') { + cntOne++; + } else { + res = Math.min(res + 1, cntOne); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/foreign-dictionary.md b/articles/foreign-dictionary.md new file mode 100644 index 000000000..1bb32e486 --- /dev/null +++ b/articles/foreign-dictionary.md @@ -0,0 +1,955 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def foreignDictionary(self, words: List[str]) -> str: + adj = {c: set() for w in words for c in w} + + for i in range(len(words) - 1): + w1, w2 = words[i], words[i + 1] + minLen = min(len(w1), len(w2)) + if len(w1) > len(w2) and w1[:minLen] == w2[:minLen]: + return "" + for j in range(minLen): + if w1[j] != w2[j]: + adj[w1[j]].add(w2[j]) + break + + visited = {} + res = [] + + def dfs(char): + if char in visited: + return visited[char] + + visited[char] = True + + for neighChar in adj[char]: + if dfs(neighChar): + return True + + visited[char] = False + res.append(char) + + for char in adj: + if dfs(char): + return "" + + res.reverse() + return "".join(res) +``` + +```java +public class Solution { + private Map> adj; + private Map visited; + private List result; + + public String foreignDictionary(String[] words) { + adj = new HashMap<>(); + for (String word : words) { + for (char c : word.toCharArray()) { + adj.putIfAbsent(c, new HashSet<>()); + } + } + + for (int i = 0; i < words.length - 1; i++) { + String w1 = words[i], w2 = words[i + 1]; + int minLen = Math.min(w1.length(), w2.length()); + if (w1.length() > w2.length() && + w1.substring(0, minLen).equals(w2.substring(0, minLen))) { + return ""; + } + for (int j = 0; j < minLen; j++) { + if (w1.charAt(j) != w2.charAt(j)) { + adj.get(w1.charAt(j)).add(w2.charAt(j)); + break; + } + } + } + + visited = new HashMap<>(); + result = new ArrayList<>(); + for (char c : adj.keySet()) { + if (dfs(c)) { + return ""; + } + } + + Collections.reverse(result); + StringBuilder sb = new StringBuilder(); + for (char c : result) { + sb.append(c); + } + return sb.toString(); + } + + private boolean dfs(char ch) { + if (visited.containsKey(ch)) { + return visited.get(ch); + } + + visited.put(ch, true); + for (char next : adj.get(ch)) { + if (dfs(next)) { + return true; + } + } + visited.put(ch, false); + result.add(ch); + return false; + } +} +``` + +```cpp +class Solution { +public: + unordered_map> adj; + unordered_map visited; + string result; + + string foreignDictionary(vector& words) { + for (const auto& word : words) { + for (char ch : word) { + adj[ch]; + } + } + + for (size_t i = 0; i < words.size() - 1; ++i) { + const string& w1 = words[i], & w2 = words[i + 1]; + size_t minLen = min(w1.length(), w2.length()); + if (w1.length() > w2.length() && + w1.substr(0, minLen) == w2.substr(0, minLen)) { + return ""; + } + for (size_t j = 0; j < minLen; ++j) { + if (w1[j] != w2[j]) { + adj[w1[j]].insert(w2[j]); + break; + } + } + } + + for (const auto& pair : adj) { + if (dfs(pair.first)) { + return ""; + } + } + + reverse(result.begin(), result.end()); + return result; + } + + bool dfs(char ch) { + if (visited.find(ch) != visited.end()) { + return visited[ch]; + } + + visited[ch] = true; + for (char next : adj[ch]) { + if (dfs(next)) { + return true; + } + } + visited[ch] = false; + result.push_back(ch); + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @returns {string} + */ + foreignDictionary(words) { + const adj = {}; + for (const word of words) { + for (const char of word) { + adj[char] = new Set(); + } + } + + for (let i = 0; i < words.length - 1; i++) { + const w1 = words[i]; + const w2 = words[i + 1]; + const minLen = Math.min(w1.length, w2.length); + if (w1.length > w2.length && + w1.slice(0, minLen) === w2.slice(0, minLen)) { + return ""; + } + for (let j = 0; j < minLen; j++) { + if (w1[j] !== w2[j]) { + adj[w1[j]].add(w2[j]); + break; + } + } + } + + const visited = {}; + const res = []; + + const dfs = (char) => { + if (char in visited) return visited[char]; + visited[char] = true; + + for (const neighChar of adj[char]) { + if (dfs(neighChar)) return true; + } + + visited[char] = false; + res.push(char); + return false; + }; + + for (const char in adj) { + if (dfs(char)) return ""; + } + + res.reverse(); + return res.join(""); + } +} +``` + +```csharp +public class Solution { + private Dictionary> adj; + private Dictionary visited; + private List result; + + public string foreignDictionary(string[] words) { + adj = new Dictionary>(); + foreach (var word in words) { + foreach (var c in word) { + if (!adj.ContainsKey(c)) { + adj[c] = new HashSet(); + } + } + } + + for (int i = 0; i < words.Length - 1; i++) { + var w1 = words[i]; + var w2 = words[i + 1]; + int minLen = Math.Min(w1.Length, w2.Length); + if (w1.Length > w2.Length && w1.Substring(0, minLen) == w2.Substring(0, minLen)) { + return ""; + } + for (int j = 0; j < minLen; j++) { + if (w1[j] != w2[j]) { + adj[w1[j]].Add(w2[j]); + break; + } + } + } + + visited = new Dictionary(); + result = new List(); + foreach (var c in adj.Keys) { + if (dfs(c)) { + return ""; + } + } + + result.Reverse(); + var sb = new StringBuilder(); + foreach (var c in result) { + sb.Append(c); + } + return sb.ToString(); + } + + private bool dfs(char ch) { + if (visited.ContainsKey(ch)) { + return visited[ch]; + } + + visited[ch] = true; + foreach (var next in adj[ch]) { + if (dfs(next)) { + return true; + } + } + visited[ch] = false; + result.Add(ch); + return false; + } +} +``` + +```go +func foreignDictionary(words []string) string { + adj := make(map[rune]map[rune]struct{}) + for _, w := range words { + for _, c := range w { + if _, exists := adj[c]; !exists { + adj[c] = make(map[rune]struct{}) + } + } + } + + for i := 0; i < len(words)-1; i++ { + w1, w2 := words[i], words[i+1] + minLen := len(w1) + if len(w2) < minLen { + minLen = len(w2) + } + if len(w1) > len(w2) && w1[:minLen] == w2[:minLen] { + return "" + } + for j := 0; j < minLen; j++ { + if w1[j] != w2[j] { + adj[rune(w1[j])][rune(w2[j])] = struct{}{} + break + } + } + } + + visited := make(map[rune]int) + var res []rune + + var dfs func(char rune) bool + dfs = func(char rune) bool { + if status, exists := visited[char]; exists { + return status == 1 + } + + visited[char] = 1 + + for neighChar := range adj[char] { + if dfs(neighChar) { + return true + } + } + + visited[char] = -1 + res = append(res, char) + return false + } + + for char := range adj { + if dfs(char) { + return "" + } + } + + var result []byte + for i := len(res) - 1; i >= 0; i-- { + result = append(result, byte(res[i])) + } + + return string(result) +} +``` + +```kotlin +class Solution { + fun foreignDictionary(words: Array): String { + val adj = HashMap>() + for (w in words) { + for (c in w) { + adj.putIfAbsent(c, hashSetOf()) + } + } + + for (i in 0 until words.size - 1) { + val w1 = words[i] + val w2 = words[i + 1] + val minLen = minOf(w1.length, w2.length) + if (w1.length > w2.length && + w1.substring(0, minLen) == w2.substring(0, minLen)) { + return "" + } + for (j in 0 until minLen) { + if (w1[j] != w2[j]) { + adj[w1[j]]?.add(w2[j]) + break + } + } + } + + val visited = HashMap() + val res = mutableListOf() + + fun dfs(char: Char): Boolean { + if (char in visited) { + return visited[char] == 1 + } + + visited[char] = 1 + + for (neighChar in adj[char] ?: emptySet()) { + if (dfs(neighChar)) { + return true + } + } + + visited[char] = -1 + res.add(char) + return false + } + + for (char in adj.keys) { + if (dfs(char)) { + return "" + } + } + + return res.reversed().joinToString("") + } +} +``` + +```swift +class Solution { + func foreignDictionary(_ words: [String]) -> String { + var adj = [Character: Set]() + for word in words { + for char in word { + if adj[char] == nil { + adj[char] = Set() + } + } + } + + for i in 0.. w2.count && String(w1.prefix(minLen)) == String(w2.prefix(minLen)) { + return "" + } + for j in 0.. Bool { + if let flag = visited[char] { + return flag + } + visited[char] = true + for neigh in adj[char]! { + if dfs(neigh) { + return true + } + } + visited[char] = false + res.append(char) + return false + } + + for char in adj.keys { + if dfs(char) { + return "" + } + } + + res.reverse() + return String(res) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N + V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of unique characters, $E$ is the number of edges and $N$ is the sum of lengths of all the strings. + +--- + +## 2. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def foreignDictionary(self, words): + adj = {c: set() for w in words for c in w} + indegree = {c: 0 for c in adj} + + for i in range(len(words) - 1): + w1, w2 = words[i], words[i + 1] + minLen = min(len(w1), len(w2)) + if len(w1) > len(w2) and w1[:minLen] == w2[:minLen]: + return "" + for j in range(minLen): + if w1[j] != w2[j]: + if w2[j] not in adj[w1[j]]: + adj[w1[j]].add(w2[j]) + indegree[w2[j]] += 1 + break + + q = deque([c for c in indegree if indegree[c] == 0]) + res = [] + + while q: + char = q.popleft() + res.append(char) + for neighbor in adj[char]: + indegree[neighbor] -= 1 + if indegree[neighbor] == 0: + q.append(neighbor) + + if len(res) != len(indegree): + return "" + + return "".join(res) +``` + +```java +public class Solution { + public String foreignDictionary(String[] words) { + Map> adj = new HashMap<>(); + Map indegree = new HashMap<>(); + + for (String word : words) { + for (char c : word.toCharArray()) { + adj.putIfAbsent(c, new HashSet<>()); + indegree.putIfAbsent(c, 0); + } + } + + for (int i = 0; i < words.length - 1; i++) { + String w1 = words[i]; + String w2 = words[i + 1]; + int minLen = Math.min(w1.length(), w2.length()); + if (w1.length() > w2.length() && + w1.substring(0, minLen).equals(w2.substring(0, minLen))) { + return ""; + } + for (int j = 0; j < minLen; j++) { + if (w1.charAt(j) != w2.charAt(j)) { + if (!adj.get(w1.charAt(j)).contains(w2.charAt(j))) { + adj.get(w1.charAt(j)).add(w2.charAt(j)); + indegree.put(w2.charAt(j), + indegree.get(w2.charAt(j)) + 1); + } + break; + } + } + } + + Queue q = new LinkedList<>(); + for (char c : indegree.keySet()) { + if (indegree.get(c) == 0) { + q.offer(c); + } + } + + StringBuilder res = new StringBuilder(); + while (!q.isEmpty()) { + char char_ = q.poll(); + res.append(char_); + for (char neighbor : adj.get(char_)) { + indegree.put(neighbor, indegree.get(neighbor) - 1); + if (indegree.get(neighbor) == 0) { + q.offer(neighbor); + } + } + } + + if (res.length() != indegree.size()) { + return ""; + } + + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string foreignDictionary(vector& words) { + unordered_map> adj; + unordered_map indegree; + for (string w : words) { + for (char c : w) { + adj[c] = unordered_set(); + indegree[c] = 0; + } + } + + for (int i = 0; i < words.size() - 1; i++) { + string w1 = words[i], w2 = words[i + 1]; + int minLen = min(w1.size(), w2.size()); + if (w1.size() > w2.size() && + w1.substr(0, minLen) == w2.substr(0, minLen)) { + return ""; + } + for (int j = 0; j < minLen; j++) { + if (w1[j] != w2[j]) { + if (!adj[w1[j]].count(w2[j])) { + adj[w1[j]].insert(w2[j]); + indegree[w2[j]]++; + } + break; + } + } + } + + queue q; + for (auto &[c, deg] : indegree) { + if (deg == 0) { + q.push(c); + } + } + + string res; + while (!q.empty()) { + char char_ = q.front(); + q.pop(); + res += char_; + for (char neighbor : adj[char_]) { + indegree[neighbor]--; + if (indegree[neighbor] == 0) { + q.push(neighbor); + } + } + } + + return res.size() == indegree.size() ? res : ""; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @returns {string} + */ + foreignDictionary(words) { + let adj = {}; + let indegree = {}; + for (let w of words) { + for (let c of w) { + adj[c] = new Set(); + indegree[c] = 0; + } + } + + for (let i = 0; i < words.length - 1; i++) { + let w1 = words[i], w2 = words[i + 1]; + let minLen = Math.min(w1.length, w2.length); + if (w1.length > w2.length && + w1.slice(0, minLen) === w2.slice(0, minLen)) { + return ""; + } + for (let j = 0; j < minLen; j++) { + if (w1[j] !== w2[j]) { + if (!adj[w1[j]].has(w2[j])) { + adj[w1[j]].add(w2[j]); + indegree[w2[j]] += 1; + } + break; + } + } + } + + let q = new Queue(); + for (let c in indegree) { + if (indegree[c] === 0) { + q.push(c); + } + } + + let res = []; + while (!q.isEmpty()) { + let char = q.pop(); + res.push(char); + for (let neighbor of adj[char]) { + indegree[neighbor] -= 1; + if (indegree[neighbor] === 0) { + q.push(neighbor); + } + } + } + + if (res.length !== Object.keys(indegree).length) { + return ""; + } + + return res.join(""); + } +} +``` + +```csharp +public class Solution { + public string foreignDictionary(string[] words) { + var adj = new Dictionary>(); + var indegree = new Dictionary(); + + foreach (var word in words) { + foreach (var c in word) { + if (!adj.ContainsKey(c)) { + adj[c] = new HashSet(); + } + if (!indegree.ContainsKey(c)) { + indegree[c] = 0; + } + } + } + + for (int i = 0; i < words.Length - 1; i++) { + var w1 = words[i]; + var w2 = words[i + 1]; + int minLen = Math.Min(w1.Length, w2.Length); + if (w1.Length > w2.Length && + w1.Substring(0, minLen) == w2.Substring(0, minLen)) { + return ""; + } + for (int j = 0; j < minLen; j++) { + if (w1[j] != w2[j]) { + if (!adj[w1[j]].Contains(w2[j])) { + adj[w1[j]].Add(w2[j]); + indegree[w2[j]]++; + } + break; + } + } + } + + var q = new Queue(); + foreach (var c in indegree.Keys) { + if (indegree[c] == 0) { + q.Enqueue(c); + } + } + + var res = new StringBuilder(); + while (q.Count > 0) { + var char_ = q.Dequeue(); + res.Append(char_); + foreach (var neighbor in adj[char_]) { + indegree[neighbor]--; + if (indegree[neighbor] == 0) { + q.Enqueue(neighbor); + } + } + } + + if (res.Length != indegree.Count) { + return ""; + } + + return res.ToString(); + } +} +``` + +```go +func foreignDictionary(words []string) string { + adj := make(map[byte]map[byte]struct{}) + indegree := make(map[byte]int) + + for _, word := range words { + for i := 0; i < len(word); i++ { + char := word[i] + if _, exists := adj[char]; !exists { + adj[char] = make(map[byte]struct{}) + } + indegree[char] = 0 + } + } + + for i := 0; i < len(words)-1; i++ { + w1, w2 := words[i], words[i+1] + minLen := len(w1) + if len(w2) < minLen { + minLen = len(w2) + } + + if len(w1) > len(w2) && w1[:minLen] == w2[:minLen] { + return "" + } + + for j := 0; j < minLen; j++ { + if w1[j] != w2[j] { + if _, exists := adj[w1[j]][w2[j]]; !exists { + adj[w1[j]][w2[j]] = struct{}{} + indegree[w2[j]]++ + } + break + } + } + } + + q := []byte{} + for char := range indegree { + if indegree[char] == 0 { + q = append(q, char) + } + } + + res := []byte{} + for len(q) > 0 { + char := q[0] + q = q[1:] + res = append(res, char) + + for neighbor := range adj[char] { + indegree[neighbor]-- + if indegree[neighbor] == 0 { + q = append(q, neighbor) + } + } + } + + if len(res) != len(indegree) { + return "" + } + + return string(res) +} +``` + +```kotlin +class Solution { + fun foreignDictionary(words: Array): String { + val adj = HashMap>() + val indegree = HashMap() + + for (word in words) { + for (c in word) { + adj.computeIfAbsent(c) { hashSetOf() } + indegree[c] = 0 + } + } + + for (i in 0 until words.size - 1) { + val w1 = words[i] + val w2 = words[i + 1] + val minLen = minOf(w1.length, w2.length) + + if (w1.length > w2.length && + w1.substring(0, minLen) == w2.substring(0, minLen)) { + return "" + } + + for (j in 0 until minLen) { + if (w1[j] != w2[j]) { + if (w2[j] !in adj[w1[j]]!!) { + adj[w1[j]]!!.add(w2[j]) + indegree[w2[j]] = indegree[w2[j]]!! + 1 + } + break + } + } + } + + val q: Queue = LinkedList() + for ((char, degree) in indegree) { + if (degree == 0) { + q.add(char) + } + } + + val res = StringBuilder() + while (q.isNotEmpty()) { + val char = q.poll() + res.append(char) + + for (neighbor in adj[char]!!) { + indegree[neighbor] = indegree[neighbor]!! - 1 + if (indegree[neighbor] == 0) { + q.add(neighbor) + } + } + } + + return if (res.length != indegree.size) "" else res.toString() + } +} +``` + +```swift +class Solution { + func foreignDictionary(_ words: [String]) -> String { + var adj = [Character: Set]() + for word in words { + for char in word { + adj[char] = Set() + } + } + + var indegree = [Character: Int]() + for key in adj.keys { + indegree[key] = 0 + } + + for i in 0.. w2.count && String(w1.prefix(minLen)) == String(w2.prefix(minLen)) { + return "" + } + let w1Arr = Array(w1) + let w2Arr = Array(w2) + for j in 0..() + for (c, deg) in indegree { + if deg == 0 { + q.append(c) + } + } + + var res = [Character]() + while !q.isEmpty { + let char = q.removeFirst() + res.append(char) + for neighbor in adj[char]! { + indegree[neighbor]! -= 1 + if indegree[neighbor]! == 0 { + q.append(neighbor) + } + } + } + + if res.count != indegree.count { + return "" + } + + return String(res) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N + V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of unique characters, $E$ is the number of edges and $N$ is the sum of lengths of all the strings. \ No newline at end of file diff --git a/articles/freedom-trail.md b/articles/freedom-trail.md new file mode 100644 index 000000000..9c7ef1c89 --- /dev/null +++ b/articles/freedom-trail.md @@ -0,0 +1,845 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def findRotateSteps(self, ring: str, key: str) -> int: + def dfs(r, k): + if k == len(key): + return 0 + + res = float("inf") + for i, c in enumerate(ring): + if c == key[k]: + min_dist = min(abs(r - i), len(ring) - abs(r - i)) + res = min(res, min_dist + 1 + dfs(i, k + 1)) + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + public int findRotateSteps(String ring, String key) { + return dfs(0, 0, ring, key); + } + + private int dfs(int r, int k, String ring, String key) { + if (k == key.length()) return 0; + + int res = Integer.MAX_VALUE; + for (int i = 0; i < ring.length(); i++) { + if (ring.charAt(i) == key.charAt(k)) { + int minDist = Math.min(Math.abs(r - i), ring.length() - Math.abs(r - i)); + res = Math.min(res, minDist + 1 + dfs(i, k + 1, ring, key)); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int findRotateSteps(string ring, string key) { + return dfs(0, 0, ring, key); + } + +private: + int dfs(int r, int k, const string& ring, const string& key) { + if (k == key.size()) return 0; + + int res = INT_MAX; + for (int i = 0; i < ring.size(); i++) { + if (ring[i] == key[k]) { + int minDist = min(abs(r - i), int(ring.size()) - abs(r - i)); + res = min(res, minDist + 1 + dfs(i, k + 1, ring, key)); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} ring + * @param {string} key + * @return {number} + */ + findRotateSteps(ring, key) { + const dfs = (r, k) => { + if (k === key.length) return 0; + + let res = Infinity; + for (let i = 0; i < ring.length; i++) { + if (ring[i] === key[k]) { + const minDist = Math.min( + Math.abs(r - i), + ring.length - Math.abs(r - i) + ); + res = Math.min(res, minDist + 1 + dfs(i, k + 1)); + } + } + return res; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ m)$ +* Space complexity: $O(m)$ for recursion stack. + +> Where $n$ is the length of the $ring$ and $m$ is the length of the $key$. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def findRotateSteps(self, ring: str, key: str) -> int: + n = len(ring) + m = len(key) + dp = {} + + def dfs(r, k): + if k == m: + return 0 + if (r, k) in dp: + return dp[(r, k)] + + res = float("inf") + for i, c in enumerate(ring): + if c == key[k]: + min_dist = min(abs(r - i), n - abs(r - i)) + res = min(res, min_dist + 1 + dfs(i, k + 1)) + dp[(r, k)] = res + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + private int[][] dp; + + public int findRotateSteps(String ring, String key) { + int n = ring.length(); + int m = key.length(); + dp = new int[n][m]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + return dfs(0, 0, ring, key); + } + + private int dfs(int r, int k, String ring, String key) { + if (k == key.length()) return 0; + if (dp[r][k] != -1) return dp[r][k]; + + int res = Integer.MAX_VALUE; + for (int i = 0; i < ring.length(); i++) { + if (ring.charAt(i) == key.charAt(k)) { + int minDist = Math.min(Math.abs(r - i), ring.length() - Math.abs(r - i)); + res = Math.min(res, minDist + 1 + dfs(i, k + 1, ring, key)); + } + } + + dp[r][k] = res; + return res; + } +} +``` + +```cpp +class Solution { + vector> dp; + +public: + int findRotateSteps(string ring, string key) { + int n = ring.size(); + int m = key.size(); + dp.assign(n, vector(m, -1)); + return dfs(0, 0, ring, key); + } + +private: + int dfs(int r, int k, string& ring, string& key) { + if (k == key.size()) return 0; + if (dp[r][k] != -1) return dp[r][k]; + + int res = INT_MAX; + for (int i = 0; i < ring.size(); i++) { + if (ring[i] == key[k]) { + int minDist = min(abs(r - i), int(ring.size()) - abs(r - i)); + res = min(res, minDist + 1 + dfs(i, k + 1, ring, key)); + } + } + + dp[r][k] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} ring + * @param {string} key + * @return {number} + */ + findRotateSteps(ring, key) { + const n = ring.length; + const m = key.length; + const dp = Array.from({ length: n }, () => Array(m).fill(-1)); + + const dfs = (r, k) => { + if (k === key.length) return 0; + if (dp[r][k] !== -1) return dp[r][k]; + + let res = Infinity; + for (let i = 0; i < ring.length; i++) { + if (ring[i] === key[k]) { + const minDist = Math.min( + Math.abs(r - i), + ring.length - Math.abs(r - i) + ); + res = Math.min(res, minDist + 1 + dfs(i, k + 1)); + } + } + + dp[r][k] = res; + return res; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the length of the $ring$ and $m$ is the length of the $key$. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def findRotateSteps(self, ring: str, key: str) -> int: + n = len(ring) + m = len(key) + dp = [[float("inf")] * n for _ in range(m + 1)] + + for i in range(n): + dp[m][i] = 0 + + for k in range(m - 1, -1, -1): + for r in range(n): + for i in range(n): + if ring[i] == key[k]: + min_dist = min(abs(r - i), n - abs(r - i)) + dp[k][r] = min(dp[k][r], min_dist + 1 + dp[k + 1][i]) + + return dp[0][0] +``` + +```java +public class Solution { + public int findRotateSteps(String ring, String key) { + int n = ring.length(); + int m = key.length(); + int[][] dp = new int[m + 1][n]; + for (int i = 0; i <= m; i++) { + for (int j = 0; j < n; j++) { + dp[i][j] = Integer.MAX_VALUE; + } + } + + for (int i = 0; i < n; i++) { + dp[m][i] = 0; + } + + for (int k = m - 1; k >= 0; k--) { + for (int r = 0; r < n; r++) { + for (int i = 0; i < n; i++) { + if (ring.charAt(i) == key.charAt(k)) { + int minDist = Math.min(Math.abs(r - i), n - Math.abs(r - i)); + dp[k][r] = Math.min(dp[k][r], minDist + 1 + dp[k + 1][i]); + } + } + } + } + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + int findRotateSteps(string ring, string key) { + int n = ring.size(); + int m = key.size(); + vector> dp(m + 1, vector(n, INT_MAX)); + + for (int i = 0; i < n; ++i) { + dp[m][i] = 0; + } + + for (int k = m - 1; k >= 0; --k) { + for (int r = 0; r < n; ++r) { + for (int i = 0; i < n; ++i) { + if (ring[i] == key[k]) { + int minDist = min(abs(r - i), n - abs(r - i)); + dp[k][r] = min(dp[k][r], minDist + 1 + dp[k + 1][i]); + } + } + } + } + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} ring + * @param {string} key + * @return {number} + */ + findRotateSteps(ring, key) { + const n = ring.length; + const m = key.length; + const dp = Array.from({ length: m + 1 }, () => + Array(n).fill(Infinity) + ); + + for (let i = 0; i < n; i++) { + dp[m][i] = 0; + } + + for (let k = m - 1; k >= 0; k--) { + for (let r = 0; r < n; r++) { + for (let i = 0; i < n; i++) { + if (ring[i] === key[k]) { + const minDist = Math.min(Math.abs(r - i), n - Math.abs(r - i)); + dp[k][r] = Math.min(dp[k][r], minDist + 1 + dp[k + 1][i]); + } + } + } + } + return dp[0][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the length of the $ring$ and $m$ is the length of the $key$. + +--- + +## 4. Dynamic Programming (Space Optimized) - I + +::tabs-start + +```python +class Solution: + def findRotateSteps(self, ring: str, key: str) -> int: + n = len(ring) + m = len(key) + dp = [0] * n + + adj = [[] for _ in range(26)] + for i in range(n): + adj[ord(ring[i]) - ord('a')].append(i) + + for k in range(m - 1, -1, -1): + next_dp = [float("inf")] * n + for r in range(n): + for i in adj[ord(key[k]) - ord('a')]: + min_dist = min(abs(r - i), n - abs(r - i)) + next_dp[r] = min(next_dp[r], min_dist + 1 + dp[i]) + dp = next_dp + + return dp[0] +``` + +```java +public class Solution { + public int findRotateSteps(String ring, String key) { + int n = ring.length(); + int m = key.length(); + int[] dp = new int[n]; + + List[] adj = new ArrayList[26]; + for (int i = 0; i < 26; i++) { + adj[i] = new ArrayList<>(); + } + for (int i = 0; i < n; i++) { + adj[ring.charAt(i) - 'a'].add(i); + } + + for (int k = m - 1; k >= 0; k--) { + int[] nextDp = new int[n]; + Arrays.fill(nextDp, Integer.MAX_VALUE); + for (int r = 0; r < n; r++) { + for (int i : adj[key.charAt(k) - 'a']) { + int minDist = Math.min(Math.abs(r - i), n - Math.abs(r - i)); + nextDp[r] = Math.min(nextDp[r], minDist + 1 + dp[i]); + } + } + dp = nextDp; + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int findRotateSteps(string ring, string key) { + int n = ring.size(); + int m = key.size(); + vector dp(n, 0); + + vector> adj(26); + for (int i = 0; i < n; ++i) { + adj[ring[i] - 'a'].push_back(i); + } + + for (int k = m - 1; k >= 0; --k) { + vector nextDp(n, INT_MAX); + for (int r = 0; r < n; ++r) { + for (int& i : adj[key[k] - 'a']) { + int minDist = min(abs(r - i), n - abs(r - i)); + nextDp[r] = min(nextDp[r], minDist + 1 + dp[i]); + } + } + dp = nextDp; + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} ring + * @param {string} key + * @return {number} + */ + findRotateSteps(ring, key) { + const n = ring.length; + const m = key.length; + let dp = new Array(n).fill(0); + + const adj = Array.from({ length: 26 }, () => []); + for (let i = 0; i < n; i++) { + adj[ring.charCodeAt(i) - 97].push(i); + } + + for (let k = m - 1; k >= 0; k--) { + const nextDp = new Array(n).fill(Infinity); + for (let r = 0; r < n; r++) { + for (const i of adj[key.charCodeAt(k) - 97]) { + const minDist = Math.min(Math.abs(r - i), n - Math.abs(r - i)); + nextDp[r] = Math.min(nextDp[r], minDist + 1 + dp[i]); + } + } + dp = nextDp; + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the $ring$ and $m$ is the length of the $key$. + +--- + +## 5. Dynamic Programming (Space optimized) - II + +::tabs-start + +```python +class Solution: + def findRotateSteps(self, ring: str, key: str) -> int: + n = len(ring) + m = len(key) + dp = [min(i, n - i) for i in range(n)] + + adj = [[] for _ in range(26)] + for i in range(n): + adj[ord(ring[i]) - ord('a')].append(i) + + for k in range(1, m): + for r in adj[ord(key[k]) - ord('a')]: + min_dist = float("inf") + for i in adj[ord(key[k - 1]) - ord('a')]: + min_dist = min( + min_dist, + min(abs(r - i), n - abs(r - i)) + dp[i] + ) + dp[r] = min_dist + + return min(dp[i] for i in adj[ord(key[-1]) - ord('a')]) + m +``` + +```java +public class Solution { + public int findRotateSteps(String ring, String key) { + int n = ring.length(); + int m = key.length(); + int[] dp = new int[n]; + + for (int i = 0; i < n; i++) { + dp[i] = Math.min(i, n - i); + } + + List[] adj = new ArrayList[26]; + for (int i = 0; i < 26; i++) { + adj[i] = new ArrayList<>(); + } + + for (int i = 0; i < n; i++) { + adj[ring.charAt(i) - 'a'].add(i); + } + + for (int k = 1; k < m; k++) { + for (int r : adj[key.charAt(k) - 'a']) { + int minDist = Integer.MAX_VALUE; + for (int i : adj[key.charAt(k - 1) - 'a']) { + minDist = Math.min(minDist, + Math.min(Math.abs(r - i), n - Math.abs(r - i)) + dp[i] + ); + } + dp[r] = minDist; + } + } + + int result = Integer.MAX_VALUE; + for (int i : adj[key.charAt(m - 1) - 'a']) { + result = Math.min(result, dp[i]); + } + + return result + m; + } +} +``` + +```cpp +class Solution { +public: + int findRotateSteps(string ring, string key) { + int n = ring.size(), m = key.size(); + vector dp(n); + + for (int i = 0; i < n; i++) { + dp[i] = min(i, n - i); + } + + vector> adj(26); + for (int i = 0; i < n; i++) { + adj[ring[i] - 'a'].push_back(i); + } + + for (int k = 1; k < m; k++) { + for (int r : adj[key[k] - 'a']) { + int minDist = INT_MAX; + for (int i : adj[key[k - 1] - 'a']) { + minDist = min(minDist, min(abs(r - i), n - abs(r - i)) + dp[i]); + } + dp[r] = minDist; + } + } + + int result = INT_MAX; + for (int& i : adj[key[m - 1] - 'a']) { + result = min(result, dp[i]); + } + + return result + m; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} ring + * @param {string} key + * @return {number} + */ + findRotateSteps(ring, key) { + const n = ring.length; + const m = key.length; + const dp = Array(n).fill(0).map((_, i) => Math.min(i, n - i)); + + const adj = Array.from({ length: 26 }, () => []); + for (let i = 0; i < n; i++) { + adj[ring.charCodeAt(i) - 97].push(i); + } + + for (let k = 1; k < m; k++) { + for (let r of adj[key.charCodeAt(k) - 97]) { + let minDist = Infinity; + for (let i of adj[key.charCodeAt(k - 1) - 97]) { + minDist = Math.min(minDist, + Math.min(Math.abs(r - i), n - Math.abs(r - i)) + dp[i] + ); + } + dp[r] = minDist; + } + } + + return Math.min(...adj[key.charCodeAt(m - 1) - 97].map(i => dp[i])) + m; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the $ring$ and $m$ is the length of the $key$. + +--- + +## 6. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def findRotateSteps(self, ring: str, key: str) -> int: + n = len(ring) + m = len(key) + + dp = [0] * n + next_dp = [0] * n + + adj = [[] for _ in range(26)] + for i, c in enumerate(ring): + adj[ord(c) - ord('a')].append(i) + + for k in range(m - 1, -1, -1): + c = ord(key[k]) - ord('a') + it, N = 0, len(adj[c]) + + for r in range(n): + if ord(ring[r]) - ord('a') != c: + next_dp[r] = float('inf') + while it < N and adj[c][it] < r: + it += 1 + + nextIdx = adj[c][it] if it < N else adj[c][0] + prevIdx = adj[c][it - 1] if it > 0 else adj[c][-1] + + next_dp[r] = min( + (r - prevIdx if r > prevIdx else n - (prevIdx - r)) + dp[prevIdx], + (nextIdx - r if nextIdx > r else n - (r - nextIdx)) + dp[nextIdx] + ) + else: + next_dp[r] = dp[r] + + dp, next_dp = next_dp, dp + + return dp[0] + m +``` + +```java +public class Solution { + public int findRotateSteps(String ring, String key) { + int n = ring.length(); + int m = key.length(); + + int[] dp = new int[n]; + int[] nextDp = new int[n]; + List[] adj = new ArrayList[26]; + + for (int i = 0; i < 26; i++) { + adj[i] = new ArrayList<>(); + } + for (int i = 0; i < n; i++) { + adj[ring.charAt(i) - 'a'].add(i); + } + + for (int k = m - 1; k >= 0; k--) { + int c = key.charAt(k) - 'a'; + int it = 0, N = adj[c].size(); + + for (int r = 0; r < n; r++) { + if (ring.charAt(r) - 'a' != c) { + nextDp[r] = Integer.MAX_VALUE; + while (it < N && adj[c].get(it) < r) { + it++; + } + + int nextIdx = it < N ? adj[c].get(it) : adj[c].get(0); + int prevIdx = it > 0 ? adj[c].get(it - 1) : adj[c].get(N - 1); + + nextDp[r] = Math.min( + (r > prevIdx ? r - prevIdx : n - (prevIdx - r)) + dp[prevIdx], + (nextIdx > r ? nextIdx - r : n - (r - nextIdx)) + dp[nextIdx] + ); + } else { + nextDp[r] = dp[r]; + } + } + + int[] temp = dp; + dp = nextDp; + nextDp = temp; + } + + return dp[0] + m; + } +} +``` + +```cpp +class Solution { +public: + int findRotateSteps(string ring, string key) { + int n = ring.size(), m = key.size(); + + vector dp(n, 0); + vector nextDp(n, 0); + vector> adj(26); + + for (int i = 0; i < n; i++) { + adj[ring[i] - 'a'].push_back(i); + } + + for (int k = m - 1; k >= 0; k--) { + int c = key[k] - 'a'; + int it = 0, N = adj[c].size(); + + for (int r = 0; r < n; r++) { + if (ring[r] - 'a' != c) { + nextDp[r] = INT_MAX; + while (it < N && adj[c][it] < r) { + it++; + } + + int nextIdx = it < N ? adj[c][it] : adj[c][0]; + int prevIdx = it > 0 ? adj[c][it - 1] : adj[c][N - 1]; + + nextDp[r] = min( + (r > prevIdx ? r - prevIdx : n - (prevIdx - r)) + dp[prevIdx], + (nextIdx > r ? nextIdx - r : n - (r - nextIdx)) + dp[nextIdx] + ); + } else { + nextDp[r] = dp[r]; + } + } + + dp.swap(nextDp); + } + + return dp[0] + m; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} ring + * @param {string} key + * @return {number} + */ + findRotateSteps(ring, key) { + const n = ring.length, m = key.length; + + let dp = Array(n).fill(0); + let nextDp = Array(n).fill(0); + const adj = Array.from({ length: 26 }, () => []); + + for (let i = 0; i < n; i++) { + adj[ring.charCodeAt(i) - 97].push(i); + } + + for (let k = m - 1; k >= 0; k--) { + const c = key.charCodeAt(k) - 97; + let it = 0, N = adj[c].length; + + for (let r = 0; r < n; r++) { + if (ring.charCodeAt(r) - 97 !== c) { + nextDp[r] = Infinity; + while (it < N && adj[c][it] < r) { + it++; + } + + const nextIdx = it < N ? adj[c][it] : adj[c][0]; + const prevIdx = it > 0 ? adj[c][it - 1] : adj[c][N - 1]; + + nextDp[r] = Math.min( + (r > prevIdx ? r - prevIdx : n - (prevIdx - r)) + dp[prevIdx], + (nextIdx > r ? nextIdx - r : n - (r - nextIdx)) + dp[nextIdx] + ); + } else { + nextDp[r] = dp[r]; + } + } + + [dp, nextDp] = [nextDp, dp]; + } + + return dp[0] + m; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the $ring$ and $m$ is the length of the $key$. \ No newline at end of file diff --git a/articles/frequency-of-the-most-frequent-element.md b/articles/frequency-of-the-most-frequent-element.md new file mode 100644 index 000000000..51c727d97 --- /dev/null +++ b/articles/frequency-of-the-most-frequent-element.md @@ -0,0 +1,420 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxFrequency(self, nums: List[int], k: int) -> int: + nums.sort() + res = 1 + for i in range(len(nums)): + j = i - 1 + tmpK = k + while j >= 0 and (tmpK - (nums[i] - nums[j])) >= 0: + tmpK -= (nums[i] - nums[j]) + j -= 1 + res = max(res, i - j) + return res +``` + +```java +public class Solution { + public int maxFrequency(int[] nums, int k) { + Arrays.sort(nums); + int res = 1; + + for (int i = 0; i < nums.length; i++) { + int j = i - 1; + long tmpK = k; + + while (j >= 0 && (tmpK - (nums[i] - nums[j])) >= 0) { + tmpK -= (nums[i] - nums[j]); + j--; + } + res = Math.max(res, i - j); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxFrequency(vector& nums, int k) { + sort(nums.begin(), nums.end()); + int res = 1; + + for (int i = 0; i < nums.size(); i++) { + int j = i - 1; + long long tmpK = k; + + while (j >= 0 && (tmpK - (nums[i] - nums[j])) >= 0) { + tmpK -= (nums[i] - nums[j]); + j--; + } + res = max(res, i - j); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maxFrequency(nums, k) { + nums.sort((a, b) => a - b); + let res = 1; + + for (let i = 0; i < nums.length; i++) { + let j = i - 1; + let tmpK = k; + + while (j >= 0 && (tmpK - (nums[i] - nums[j])) >= 0) { + tmpK -= (nums[i] - nums[j]); + j--; + } + res = Math.max(res, i - j); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 + n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Prefix Sum + Binary Search + +::tabs-start + +```python +class Solution: + def maxFrequency(self, nums: List[int], k: int) -> int: + nums.sort() + n = len(nums) + prefix_sum = [0] * (n + 1) + for i in range(n): + prefix_sum[i + 1] = prefix_sum[i] + nums[i] + + res = 1 + for i in range(n): + l, r = 0, i + while l <= r: + m = (l + r) // 2 + curSum = prefix_sum[i + 1] - prefix_sum[m] + need = (i - m + 1) * nums[i] - curSum + if need <= k: + r = m - 1 + res = max(res, i - m + 1) + else: + l = m + 1 + return res +``` + +```java +public class Solution { + public int maxFrequency(int[] nums, int k) { + Arrays.sort(nums); + int n = nums.length; + long[] prefixSum = new long[n + 1]; + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + int res = 1; + for (int i = 0; i < n; i++) { + int left = 0, right = i; + while (left <= right) { + int mid = (left + right) / 2; + long curSum = prefixSum[i + 1] - prefixSum[mid]; + long need = (i - mid + 1) * 1L * nums[i] - curSum; + if (need <= k) { + right = mid - 1; + res = Math.max(res, i - mid + 1); + } else { + left = mid + 1; + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxFrequency(vector& nums, int k) { + sort(nums.begin(), nums.end()); + int n = nums.size(); + vector prefixSum(n + 1, 0); + for (int i = 0; i < n; ++i) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + int res = 1; + for (int i = 0; i < n; ++i) { + int l = 0, r = i; + while (l <= r) { + int m = (l + r) / 2; + long long curSum = prefixSum[i + 1] - prefixSum[m]; + long long need = (i - m + 1) * 1LL * nums[i] - curSum; + if (need <= k) { + r = m - 1; + res = max(res, i - m + 1); + } else { + l = m + 1; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maxFrequency(nums, k) { + nums.sort((a, b) => a - b); + const prefixSum = new Array(nums.length + 1).fill(0); + for (let i = 0; i < nums.length; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + let res = 1; + for (let i = 0; i < nums.length; i++) { + let left = 0, right = i; + while (left <= right) { + const mid = Math.floor((left + right) / 2); + const curSum = prefixSum[i + 1] - prefixSum[mid]; + const need = (i - mid + 1) * nums[i] - curSum; + if (need <= k) { + right = mid - 1; + res = Math.max(res, i - mid + 1); + } else { + left = mid + 1; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sliding Window + +::tabs-start + +```python +class Solution: + def maxFrequency(self, nums: List[int], k: int) -> int: + nums.sort() + total = res = 0 + l = 0 + + for r in range(len(nums)): + total += nums[r] + while nums[r] * (r - l + 1) > total + k: + total -= nums[l] + l += 1 + res = max(res, r - l + 1) + + return res +``` + +```java +public class Solution { + public int maxFrequency(int[] nums, int k) { + Arrays.sort(nums); + long total = 0; + int res = 0; + int l = 0; + + for (int r = 0; r < nums.length; r++) { + total += nums[r]; + while ((long) nums[r] * (r - l + 1) > total + k) { + total -= nums[l]; + l++; + } + res = Math.max(res, r - l + 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxFrequency(vector& nums, int k) { + sort(nums.begin(), nums.end()); + long long total = 0; + int res = 0, l = 0; + + for (int r = 0; r < nums.size(); ++r) { + total += nums[r]; + while ((long long)nums[r] * (r - l + 1) > total + k) { + total -= nums[l]; + l++; + } + res = max(res, r - l + 1); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maxFrequency(nums, k) { + nums.sort((a, b) => a - b); + let total = 0, res = 0, l = 0; + + for (let r = 0; r < nums.length; r++) { + total += nums[r]; + while (nums[r] * (r - l + 1) > total + k) { + total -= nums[l]; + l++; + } + res = Math.max(res, r - l + 1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 4. Advanced Sliding Window + +::tabs-start + +```python +class Solution: + def maxFrequency(self, nums: List[int], k: int) -> int: + nums.sort() + l = 0 + total = 0 + + for r in range(len(nums)): + total += nums[r] + + if (r - l + 1) * nums[r] > total + k: + total -= nums[l] + l += 1 + + return len(nums) - l +``` + +```java +public class Solution { + public int maxFrequency(int[] nums, int k) { + Arrays.sort(nums); + long total = 0; + int l = 0; + + for (int r = 0; r < nums.length; r++) { + total += nums[r]; + if ((r - l + 1) * 1L * nums[r] > total + k) { + total -= nums[l]; + l++; + } + } + + return nums.length - l; + } +} +``` + +```cpp +class Solution { +public: + int maxFrequency(vector& nums, int k) { + sort(nums.begin(), nums.end()); + long long total = 0; + int l = 0; + + for (int r = 0; r < nums.size(); ++r) { + total += nums[r]; + if ((r - l + 1) * 1L * nums[r] > total + k) { + total -= nums[l]; + l++; + } + } + + return nums.size() - l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maxFrequency(nums, k) { + nums.sort((a, b) => a - b); + let total = 0, l = 0; + + for (let r = 0; r < nums.length; r++) { + total += nums[r]; + if (nums[r] * (r - l + 1) > total + k) { + total -= nums[l]; + l++; + } + } + + return nums.length - l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file diff --git a/articles/fruit-into-baskets.md b/articles/fruit-into-baskets.md new file mode 100644 index 000000000..7aa1b5bbe --- /dev/null +++ b/articles/fruit-into-baskets.md @@ -0,0 +1,468 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def totalFruit(self, fruits: List[int]) -> int: + n = len(fruits) + res = 0 + for i in range(n): + types = set() + j = i + while j < n and (len(types) < 2 or fruits[j] in types): + types.add(fruits[j]) + j += 1 + res = max(res, j - i) + return res +``` + +```java +public class Solution { + public int totalFruit(int[] fruits) { + int n = fruits.length, res = 0; + + for (int i = 0; i < n; i++) { + Set types = new HashSet<>(); + int j = i; + + while (j < n && (types.size() < 2 || types.contains(fruits[j]))) { + types.add(fruits[j]); + j++; + } + res = Math.max(res, j - i); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int totalFruit(vector& fruits) { + int n = fruits.size(), res = 0; + + for (int i = 0; i < n; i++) { + unordered_set types; + int j = i; + + while (j < n && (types.size() < 2 || types.count(fruits[j]))) { + types.insert(fruits[j]); + j++; + } + res = max(res, j - i); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} fruits + * @return {number} + */ + totalFruit(fruits) { + let n = fruits.length, res = 0; + + for (let i = 0; i < n; i++) { + let types = new Set(); + let j = i; + + while (j < n && (types.size < 2 || types.has(fruits[j]))) { + types.add(fruits[j]); + j++; + } + res = Math.max(res, j - i); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Sliding Window - I + +::tabs-start + +```python +class Solution: + def totalFruit(self, fruits: List[int]) -> int: + count = defaultdict(int) + l, total, res = 0, 0, 0 + + for r in range(len(fruits)): + count[fruits[r]] += 1 + total += 1 + + while len(count) > 2: + f = fruits[l] + count[f] -= 1 + total -= 1 + l += 1 + if not count[f]: + count.pop(f) + + res = max(res, total) + + return res +``` + +```java +public class Solution { + public int totalFruit(int[] fruits) { + HashMap count = new HashMap<>(); + int l = 0, total = 0, res = 0; + + for (int r = 0; r < fruits.length; r++) { + count.put(fruits[r], count.getOrDefault(fruits[r], 0) + 1); + total++; + + while (count.size() > 2) { + int f = fruits[l]; + count.put(f, count.get(f) - 1); + total--; + if (count.get(f) == 0) { + count.remove(f); + } + l++; + } + res = Math.max(res, total); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int totalFruit(vector& fruits) { + unordered_map count; + int l = 0, total = 0, res = 0; + + for (int r = 0; r < fruits.size(); r++) { + count[fruits[r]]++; + total++; + + while (count.size() > 2) { + int f = fruits[l]; + count[f]--; + total--; + if (count[f] == 0) { + count.erase(f); + } + l++; + } + res = max(res, total); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} fruits + * @return {number} + */ + totalFruit(fruits) { + let count = new Map(); + let l = 0, total = 0, res = 0; + + for (let r = 0; r < fruits.length; r++) { + count.set(fruits[r], (count.get(fruits[r]) || 0) + 1); + total++; + + while (count.size > 2) { + let f = fruits[l]; + count.set(f, count.get(f) - 1); + total--; + if (count.get(f) === 0) { + count.delete(f); + } + l++; + } + res = Math.max(res, total); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Sliding Window - II + +::tabs-start + +```python +class Solution: + def totalFruit(self, fruits: List[int]) -> int: + count = defaultdict(int) + l = 0 + + for r in range(len(fruits)): + count[fruits[r]] += 1 + + if len(count) > 2: + count[fruits[l]] -= 1 + if count[fruits[l]] == 0: + count.pop(fruits[l]) + l += 1 + + return len(fruits) - l +``` + +```java +public class Solution { + public int totalFruit(int[] fruits) { + HashMap count = new HashMap<>(); + int l = 0; + + for (int r = 0; r < fruits.length; r++) { + count.put(fruits[r], count.getOrDefault(fruits[r], 0) + 1); + + if (count.size() > 2) { + count.put(fruits[l], count.get(fruits[l]) - 1); + if (count.get(fruits[l]) == 0) { + count.remove(fruits[l]); + } + l++; + } + } + + return fruits.length - l; + } +} +``` + +```cpp +class Solution { +public: + int totalFruit(vector& fruits) { + unordered_map count; + int l = 0; + + for (int r = 0; r < fruits.size(); r++) { + count[fruits[r]]++; + + if (count.size() > 2) { + count[fruits[l]]--; + if (count[fruits[l]] == 0) { + count.erase(fruits[l]); + } + l++; + } + } + + return fruits.size() - l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} fruits + * @return {number} + */ + totalFruit(fruits) { + let count = new Map(); + let l = 0; + + for (let r = 0; r < fruits.length; r++) { + count.set(fruits[r], (count.get(fruits[r]) || 0) + 1); + + if (count.size > 2) { + count.set(fruits[l], count.get(fruits[l]) - 1); + if (count.get(fruits[l]) === 0) { + count.delete(fruits[l]); + } + l++; + } + } + + return fruits.length - l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Sliding Window - III + +::tabs-start + +```python +class Solution: + def totalFruit(self, fruits: list[int]) -> int: + l = 0 + fruit1_lastIdx = 0 + fruit2_lastIdx = -1 + fruit1 = fruits[0] + fruit2 = -1 + total = res = 1 + + for r in range(len(fruits)): + f = fruits[r] + if f == fruit1: + total += 1 + fruit1_lastIdx = r + elif f == fruit2 or fruit2 == -1: + total += 1 + fruit2_lastIdx = r + fruit2 = f + else: + if fruit2_lastIdx == min(fruit1_lastIdx, fruit2_lastIdx): + fruit1_lastIdx, fruit2_lastIdx = fruit2_lastIdx, fruit1_lastIdx + fruit1, fruit2 = fruit2, fruit1 + + total -= (fruit1_lastIdx - l + 1) + l = fruit1_lastIdx + 1 + fruit1 = f + fruit1_lastIdx = r + res = max(res, r - l + 1) + + return res +``` + +```java +public class Solution { + public int totalFruit(int[] fruits) { + int l = 0, fruit1_lastIdx = 0, fruit2_lastIdx = -1; + int fruit1 = fruits[0], fruit2 = -1, total = 1, res = 1; + + for (int r = 0; r < fruits.length; r++) { + int f = fruits[r]; + if (f == fruit1) { + total++; + fruit1_lastIdx = r; + } else if (f == fruit2 || fruit2 == -1) { + total++; + fruit2_lastIdx = r; + fruit2 = f; + } else { + if (fruit2_lastIdx == Math.min(fruit1_lastIdx, fruit2_lastIdx)) { + int tempIdx = fruit1_lastIdx; + fruit1_lastIdx = fruit2_lastIdx; + fruit2_lastIdx = tempIdx; + int tempFruit = fruit1; + fruit1 = fruit2; + fruit2 = tempFruit; + } + total -= (fruit1_lastIdx - l + 1); + l = fruit1_lastIdx + 1; + fruit1 = f; + fruit1_lastIdx = r; + } + res = Math.max(res, r - l + 1); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int totalFruit(vector& fruits) { + int l = 0, fruit1_lastIdx = 0, fruit2_lastIdx = -1; + int fruit1 = fruits[0], fruit2 = -1, total = 1, res = 1; + + for (int r = 0; r < fruits.size(); r++) { + int f = fruits[r]; + if (f == fruit1) { + total++; + fruit1_lastIdx = r; + } else if (f == fruit2 || fruit2 == -1) { + total++; + fruit2_lastIdx = r; + fruit2 = f; + } else { + if (fruit2_lastIdx == min(fruit1_lastIdx, fruit2_lastIdx)) { + swap(fruit1_lastIdx, fruit2_lastIdx); + swap(fruit1, fruit2); + } + total -= (fruit1_lastIdx - l + 1); + l = fruit1_lastIdx + 1; + fruit1 = f; + fruit1_lastIdx = r; + } + res = max(res, r - l + 1); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} fruits + * @return {number} + */ + totalFruit(fruits) { + let l = 0, fruit1_lastIdx = 0, fruit2_lastIdx = -1; + let fruit1 = fruits[0], fruit2 = -1, total = 1, res = 1; + + for (let r = 0; r < fruits.length; r++) { + let f = fruits[r]; + if (f === fruit1) { + total++; + fruit1_lastIdx = r; + } else if (f === fruit2 || fruit2 === -1) { + total++; + fruit2_lastIdx = r; + fruit2 = f; + } else { + if (fruit2_lastIdx === Math.min(fruit1_lastIdx, fruit2_lastIdx)) { + [fruit1_lastIdx, fruit2_lastIdx] = [fruit2_lastIdx, fruit1_lastIdx]; + [fruit1, fruit2] = [fruit2, fruit1]; + } + total -= (fruit1_lastIdx - l + 1); + l = fruit1_lastIdx + 1; + fruit1 = f; + fruit1_lastIdx = r; + } + res = Math.max(res, r - l + 1); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/furthest-building-you-can-reach.md b/articles/furthest-building-you-can-reach.md new file mode 100644 index 000000000..24ca0ef3b --- /dev/null +++ b/articles/furthest-building-you-can-reach.md @@ -0,0 +1,723 @@ +## 1. Brute Force (Greedy) + +::tabs-start + +```python +class Solution: + def furthestBuilding(self, heights: List[int], bricks: int, ladders: int) -> int: + n = len(heights) + + for i in range(1, n): + if ladders >= i: + continue + + diffs = [] + for j in range(i): + if heights[j + 1] > heights[j]: + diffs.append(heights[j + 1] - heights[j]) + + diffs.sort() + brickSum = 0 + for j in range(len(diffs) - ladders): + brickSum += diffs[j] + + if brickSum > bricks: + return i - 1 + + return n - 1 +``` + +```java +public class Solution { + public int furthestBuilding(int[] heights, int bricks, int ladders) { + int n = heights.length; + + for (int i = 1; i < n; i++) { + if (ladders >= i) { + continue; + } + + List diffs = new ArrayList<>(); + for (int j = 0; j < i; j++) { + if (heights[j + 1] > heights[j]) { + diffs.add(heights[j + 1] - heights[j]); + } + } + + Collections.sort(diffs); + long brickSum = 0; + for (int j = 0; j < diffs.size() - ladders; j++) { + brickSum += diffs.get(j); + } + + if (brickSum > bricks) { + return i - 1; + } + } + + return n - 1; + } +} +``` + +```cpp +class Solution { +public: + int furthestBuilding(vector& heights, int bricks, int ladders) { + int n = heights.size(); + + for (int i = 1; i < n; i++) { + if (ladders >= i) { + continue; + } + + vector diffs; + for (int j = 0; j < i; j++) { + if (heights[j + 1] > heights[j]) { + diffs.push_back(heights[j + 1] - heights[j]); + } + } + + sort(diffs.begin(), diffs.end()); + long long brickSum = 0; + for (int j = 0; j < int(diffs.size()) - ladders; j++) { + brickSum += diffs[j]; + } + + if (brickSum > bricks) { + return i - 1; + } + } + + return n - 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @param {number} bricks + * @param {number} ladders + * @return {number} + */ + furthestBuilding(heights, bricks, ladders) { + let n = heights.length; + + for (let i = 1; i < n; i++) { + if (ladders >= i) { + continue; + } + + let diffs = []; + for (let j = 0; j < i; j++) { + if (heights[j + 1] > heights[j]) { + diffs.push(heights[j + 1] - heights[j]); + } + } + + diffs.sort((a, b) => a - b); + let brickSum = 0; + for (let j = 0; j < diffs.length - ladders; j++) { + brickSum += diffs[j]; + } + + if (brickSum > bricks) { + return i - 1; + } + } + + return n - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Binary Search On Buildings + +::tabs-start + +```python +class Solution: + def furthestBuilding(self, heights: List[int], bricks: int, ladders: int) -> int: + def canReach(mid): + diffs = [] + for i in range(mid): + if heights[i + 1] > heights[i]: + diffs.append(heights[i + 1] - heights[i]) + + diffs.sort() + brickSum = 0 + for j in range(len(diffs) - ladders): + brickSum += diffs[j] + if brickSum > bricks: + return False + + return True + + l, r = ladders - 1, len(heights) - 1 + while l <= r: + mid = (l + r) // 2 + if canReach(mid): + l = mid + 1 + else: + r = mid - 1 + + return l - 1 +``` + +```java +public class Solution { + public int furthestBuilding(int[] heights, int bricks, int ladders) { + int l = ladders - 1, r = heights.length - 1; + + while (l <= r) { + int mid = (l + r) / 2; + if (canReach(heights, mid, bricks, ladders)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + return l - 1; + } + + private boolean canReach(int[] heights, int mid, int bricks, int ladders) { + List diffs = new ArrayList<>(); + + for (int i = 0; i < mid; i++) { + if (heights[i + 1] > heights[i]) { + diffs.add(heights[i + 1] - heights[i]); + } + } + + Collections.sort(diffs); + int brickSum = 0; + + for (int j = 0; j < diffs.size() - ladders; j++) { + brickSum += diffs.get(j); + if (brickSum > bricks) { + return false; + } + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + int furthestBuilding(vector& heights, int bricks, int ladders) { + int l = ladders - 1, r = heights.size() - 1; + + while (l <= r) { + int mid = (l + r) / 2; + if (canReach(heights, mid, bricks, ladders)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + return l - 1; + } + +private: + bool canReach(vector& heights, int mid, int bricks, int ladders) { + vector diffs; + + for (int i = 0; i < mid; i++) { + if (heights[i + 1] > heights[i]) { + diffs.push_back(heights[i + 1] - heights[i]); + } + } + + sort(diffs.begin(), diffs.end()); + long long brickSum = 0; + + for (int j = 0; j < int(diffs.size()) - ladders; j++) { + brickSum += diffs[j]; + if (brickSum > bricks) { + return false; + } + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @param {number} bricks + * @param {number} ladders + * @return {number} + */ + furthestBuilding(heights, bricks, ladders) { + let l = ladders - 1, r = heights.length - 1; + + const canReach = (mid) => { + let diffs = []; + for (let i = 0; i < mid; i++) { + if (heights[i + 1] > heights[i]) { + diffs.push(heights[i + 1] - heights[i]); + } + } + + diffs.sort((a, b) => a - b); + let brickSum = 0; + + for (let j = 0; j < diffs.length - ladders; j++) { + brickSum += diffs[j]; + if (brickSum > bricks) { + return false; + } + } + + return true; + }; + + while (l <= r) { + let mid = Math.floor((l + r) / 2); + if (canReach(mid)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + return l - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log ^ 2 n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Binary Search On Buildings (Optimal) + +::tabs-start + +```python +class Solution: + def furthestBuilding(self, heights: List[int], bricks: int, ladders: int) -> int: + diffs = [] + for i in range(1, len(heights)): + if heights[i] > heights[i - 1]: + diffs.append((heights[i] - heights[i - 1], i)) + + diffs.sort(reverse=True) + + def canReach(index): + useLadders = useBricks = 0 + for diff, i in diffs: + if i > index: + continue + + if useLadders < ladders: + useLadders += 1 + else: + useBricks += diff + if useBricks > bricks: + return False + return True + + l, r = 1, len(heights) - 1 + while l <= r: + mid = (l + r) >> 1 + if canReach(mid): + l = mid + 1 + else: + r = mid - 1 + return l - 1 +``` + +```java +public class Solution { + public int furthestBuilding(int[] heights, int bricks, int ladders) { + List diffs = new ArrayList<>(); + for (int i = 1; i < heights.length; i++) { + if (heights[i] > heights[i - 1]) { + diffs.add(new int[]{heights[i] - heights[i - 1], i}); + } + } + + diffs.sort((a, b) -> Integer.compare(b[0], a[0])); + + int l = 1, r = heights.length - 1; + while (l <= r) { + int mid = (l + r) >> 1; + if (canReach(diffs, mid, bricks, ladders)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + return l - 1; + } + + private boolean canReach(List diffs, int index, int bricks, int ladders) { + int useLadders = 0; + long useBricks = 0; + for (int[] diff : diffs) { + int jump = diff[0], i = diff[1]; + + if (i > index) continue; + + if (useLadders < ladders) { + useLadders++; + } else { + useBricks += jump; + if (useBricks > bricks) { + return false; + } + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + int furthestBuilding(vector& heights, int bricks, int ladders) { + vector> diffs; + for (int i = 1; i < heights.size(); i++) { + if (heights[i] > heights[i - 1]) { + diffs.emplace_back(heights[i] - heights[i - 1], i); + } + } + + sort(diffs.rbegin(), diffs.rend()); // Sort in descending order + + int l = 1, r = heights.size() - 1; + while (l <= r) { + int mid = (l + r) >> 1; + if (canReach(diffs, mid, bricks, ladders)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + return l - 1; + } + +private: + bool canReach(vector>& diffs, int index, int bricks, int ladders) { + int useLadders = 0; + long long useBricks = 0; + for (auto& diff : diffs) { + int jump = diff.first, i = diff.second; + + if (i > index) continue; + + if (useLadders < ladders) { + useLadders++; + } else { + useBricks += jump; + if (useBricks > bricks) { + return false; + } + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @param {number} bricks + * @param {number} ladders + * @return {number} + */ + furthestBuilding(heights, bricks, ladders) { + let diffs = []; + for (let i = 1; i < heights.length; i++) { + if (heights[i] > heights[i - 1]) { + diffs.push([heights[i] - heights[i - 1], i]); + } + } + + diffs.sort((a, b) => b[0] - a[0]); + + const canReach = (index) => { + let useLadders = 0, useBricks = 0; + for (let [diff, i] of diffs) { + if (i > index) continue; + + if (useLadders < ladders) { + useLadders++; + } else { + useBricks += diff; + if (useBricks > bricks) { + return false; + } + } + } + return true; + }; + + let l = 1, r = heights.length - 1; + while (l <= r) { + let mid = (l + r) >> 1; + if (canReach(mid)) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + return l - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Max-Heap + +::tabs-start + +```python +class Solution: + def furthestBuilding(self, heights: List[int], bricks: int, ladders: int) -> int: + heap = [] # Max heap of bricks used + + for i in range(len(heights) - 1): + diff = heights[i + 1] - heights[i] + if diff <= 0: + continue + + bricks -= diff + heapq.heappush(heap, -diff) + + if bricks < 0: + if ladders == 0: + return i + ladders -= 1 + bricks += -heapq.heappop(heap) + + return len(heights) - 1 +``` + +```java +public class Solution { + public int furthestBuilding(int[] heights, int bricks, int ladders) { + PriorityQueue maxHeap = new PriorityQueue<>((a, b) -> b - a); + + for (int i = 0; i < heights.length - 1; i++) { + int diff = heights[i + 1] - heights[i]; + if (diff <= 0) continue; + + bricks -= diff; + maxHeap.offer(diff); + + if (bricks < 0) { + if (ladders == 0) return i; + ladders--; + bricks += maxHeap.poll(); + } + } + + return heights.length - 1; + } +} +``` + +```cpp +class Solution { +public: + int furthestBuilding(vector& heights, int bricks, int ladders) { + priority_queue maxHeap; + + for (int i = 0; i < heights.size() - 1; i++) { + int diff = heights[i + 1] - heights[i]; + if (diff <= 0) continue; + + bricks -= diff; + maxHeap.push(diff); + + if (bricks < 0) { + if (ladders == 0) return i; + ladders--; + bricks += maxHeap.top(); + maxHeap.pop(); + } + } + + return heights.size() - 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @param {number} bricks + * @param {number} ladders + * @return {number} + */ + furthestBuilding(heights, bricks, ladders) { + const maxHeap = new MaxPriorityQueue(); + + for (let i = 0; i < heights.length - 1; i++) { + let diff = heights[i + 1] - heights[i]; + if (diff <= 0) continue; + + bricks -= diff; + maxHeap.enqueue(diff); + + if (bricks < 0) { + if (ladders === 0) return i; + ladders--; + bricks += maxHeap.dequeue().element; + } + } + + return heights.length - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Min-Heap + +::tabs-start + +```python +class Solution: + def furthestBuilding(self, heights: List[int], bricks: int, ladders: int) -> int: + minHeap = [] + + for i in range(len(heights) - 1): + diff = heights[i + 1] - heights[i] + if diff <= 0: + continue + + heapq.heappush(minHeap, diff) + if len(minHeap) > ladders: + bricks -= heapq.heappop(minHeap) + if bricks < 0: + return i + + return len(heights) - 1 +``` + +```java +public class Solution { + public int furthestBuilding(int[] heights, int bricks, int ladders) { + PriorityQueue minHeap = new PriorityQueue<>(); + + for (int i = 0; i < heights.length - 1; i++) { + int diff = heights[i + 1] - heights[i]; + if (diff <= 0) continue; + + minHeap.offer(diff); + if (minHeap.size() > ladders) { + bricks -= minHeap.poll(); + if (bricks < 0) return i; + } + } + + return heights.length - 1; + } +} +``` + +```cpp +class Solution { +public: + int furthestBuilding(vector& heights, int bricks, int ladders) { + priority_queue, greater> minHeap; + + for (int i = 0; i < int(heights.size()) - 1; i++) { + int diff = heights[i + 1] - heights[i]; + if (diff <= 0) continue; + + minHeap.push(diff); + if (minHeap.size() > ladders) { + bricks -= minHeap.top(); minHeap.pop(); + if (bricks < 0) return i; + } + } + + return int(heights.size()) - 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @param {number} bricks + * @param {number} ladders + * @return {number} + */ + furthestBuilding(heights, bricks, ladders) { + const minHeap = new MinPriorityQueue(); + + for (let i = 0; i < heights.length - 1; i++) { + let diff = heights[i + 1] - heights[i]; + if (diff <= 0) continue; + + minHeap.enqueue(diff); + if (minHeap.size() > ladders) { + bricks -= minHeap.dequeue().element; + if (bricks < 0) return i; + } + } + + return heights.length - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/gas-station.md b/articles/gas-station.md new file mode 100644 index 000000000..9e779b1d8 --- /dev/null +++ b/articles/gas-station.md @@ -0,0 +1,611 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int: + n = len(gas) + + for i in range(n): + tank = gas[i] - cost[i] + if tank < 0: + continue + + j = (i + 1) % n + while j != i: + tank += gas[j] + tank -= cost[j] + if tank < 0: + break + j += 1 + j %= n + + if j == i: + return i + return -1 +``` + +```java +public class Solution { + public int canCompleteCircuit(int[] gas, int[] cost) { + int n = gas.length; + + for (int i = 0; i < n; i++) { + int tank = gas[i] - cost[i]; + if (tank < 0) continue; + + int j = (i + 1) % n; + while (j != i) { + tank += gas[j] - cost[j]; + if (tank < 0) break; + j = (j + 1) % n; + } + + if (j == i) return i; + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int canCompleteCircuit(vector& gas, vector& cost) { + int n = gas.size(); + + for (int i = 0; i < n; i++) { + int tank = gas[i] - cost[i]; + if (tank < 0) continue; + + int j = (i + 1) % n; + while (j != i) { + tank += gas[j] - cost[j]; + if (tank < 0) break; + j = (j + 1) % n; + } + + if (j == i) return i; + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} gas + * @param {number[]} cost + * @return {number} + */ + canCompleteCircuit(gas, cost) { + const n = gas.length; + + for (let i = 0; i < n; i++) { + let tank = gas[i] - cost[i]; + if (tank < 0) continue; + + let j = (i + 1) % n; + while (j !== i) { + tank += gas[j] - cost[j]; + if (tank < 0) break; + j = (j + 1) % n; + } + + if (j === i) return i; + } + return -1; + } +} +``` + +```csharp +public class Solution { + public int CanCompleteCircuit(int[] gas, int[] cost) { + int n = gas.Length; + + for (int i = 0; i < n; i++) { + int tank = gas[i] - cost[i]; + if (tank < 0) continue; + + int j = (i + 1) % n; + while (j != i) { + tank += gas[j] - cost[j]; + if (tank < 0) break; + j = (j + 1) % n; + } + + if (j == i) return i; + } + + return -1; + } +} +``` + +```go +func canCompleteCircuit(gas []int, cost []int) int { + n := len(gas) + + for i := 0; i < n; i++ { + tank := gas[i] - cost[i] + if tank < 0 { + continue + } + + j := (i + 1) % n + for j != i { + tank += gas[j] + tank -= cost[j] + if tank < 0 { + break + } + j = (j + 1) % n + } + + if j == i { + return i + } + } + + return -1 +} +``` + +```kotlin +class Solution { + fun canCompleteCircuit(gas: IntArray, cost: IntArray): Int { + val n = gas.size + for (i in 0 until n) { + var tank = gas[i] - cost[i] + if (tank < 0) { + continue + } + var j = (i + 1) % n + while (j != i) { + tank += gas[j] + tank -= cost[j] + if (tank < 0) { + break + } + j = (j + 1) % n + } + if (j == i) { + return i + } + } + return -1 + } +} +``` + +```swift +class Solution { + func canCompleteCircuit(_ gas: [Int], _ cost: [Int]) -> Int { + let n = gas.count + + for i in 0.. int: + n = len(gas) + start, end = n - 1, 0 + tank = gas[start] - cost[start] + while start > end: + if tank < 0: + start -= 1 + tank += gas[start] - cost[start] + else: + tank += gas[end] - cost[end] + end += 1 + return start if tank >= 0 else -1 +``` + +```java +public class Solution { + public int canCompleteCircuit(int[] gas, int[] cost) { + int n = gas.length; + int start = n - 1, end = 0; + int tank = gas[start] - cost[start]; + while (start > end) { + if (tank < 0) { + start--; + tank += gas[start] - cost[start]; + } else { + tank += gas[end] - cost[end]; + end++; + } + } + return tank >= 0 ? start : -1; + } +} +``` + +```cpp +class Solution { +public: + int canCompleteCircuit(vector& gas, vector& cost) { + int n = gas.size(); + int start = n - 1, end = 0; + int tank = gas[start] - cost[start]; + while (start > end) { + if (tank < 0) { + start--; + tank += gas[start] - cost[start]; + } else { + tank += gas[end] - cost[end]; + end++; + } + } + return tank >= 0 ? start : -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} gas + * @param {number[]} cost + * @return {number} + */ + canCompleteCircuit(gas, cost) { + const n = gas.length; + let start = n - 1, end = 0; + let tank = gas[start] - cost[start]; + while (start > end) { + if (tank < 0) { + start--; + tank += gas[start] - cost[start]; + } else { + tank += gas[end] - cost[end]; + end++; + } + } + return tank >= 0 ? start : -1; + } +} +``` + +```csharp +public class Solution { + public int CanCompleteCircuit(int[] gas, int[] cost) { + int n = gas.Length; + int start = n - 1, end = 0; + int tank = gas[start] - cost[start]; + while (start > end) { + if (tank < 0) { + start--; + tank += gas[start] - cost[start]; + } else { + tank += gas[end] - cost[end]; + end++; + } + } + return tank >= 0 ? start : -1; + } +} +``` + +```go +func canCompleteCircuit(gas []int, cost []int) int { + n := len(gas) + start, end := n-1, 0 + tank := gas[start] - cost[start] + + for start > end { + if tank < 0 { + start-- + tank += gas[start] - cost[start] + } else { + tank += gas[end] - cost[end] + end++ + } + } + + if tank >= 0 { + return start + } + return -1 +} +``` + +```kotlin +class Solution { + fun canCompleteCircuit(gas: IntArray, cost: IntArray): Int { + val n = gas.size + var start = n - 1 + var end = 0 + var tank = gas[start] - cost[start] + + while (start > end) { + if (tank < 0) { + start-- + tank += gas[start] - cost[start] + } else { + tank += gas[end] - cost[end] + end++ + } + } + + return if (tank >= 0) start else -1 + } +} +``` + +```swift +class Solution { + func canCompleteCircuit(_ gas: [Int], _ cost: [Int]) -> Int { + let n = gas.count + var start = n - 1 + var end = 0 + var tank = gas[start] - cost[start] + + while start > end { + if tank < 0 { + start -= 1 + tank += gas[start] - cost[start] + } else { + tank += gas[end] - cost[end] + end += 1 + } + } + + return tank >= 0 ? start : -1 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Greedy + +::tabs-start + +```python +class Solution: + def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int: + if sum(gas) < sum(cost): + return -1 + + total = 0 + res = 0 + for i in range(len(gas)): + total += (gas[i] - cost[i]) + + if total < 0: + total = 0 + res = i + 1 + + return res +``` + +```java +public class Solution { + public int canCompleteCircuit(int[] gas, int[] cost) { + if (Arrays.stream(gas).sum() < Arrays.stream(cost).sum()) { + return -1; + } + + int total = 0; + int res = 0; + for (int i = 0; i < gas.length; i++) { + total += (gas[i] - cost[i]); + + if (total < 0) { + total = 0; + res = i + 1; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int canCompleteCircuit(vector& gas, vector& cost) { + if (accumulate(gas.begin(), gas.end(), 0) < + accumulate(cost.begin(), cost.end(), 0)) { + return -1; + } + + int total = 0; + int res = 0; + for (int i = 0; i < gas.size(); i++) { + total += (gas[i] - cost[i]); + + if (total < 0) { + total = 0; + res = i + 1; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} gas + * @param {number[]} cost + * @return {number} + */ + canCompleteCircuit(gas, cost) { + if (gas.reduce((acc, val) => acc + val, 0) < + cost.reduce((acc, val) => acc + val, 0)) { + return -1; + } + + let total = 0; + let res = 0; + for (let i = 0; i < gas.length; i++) { + total += gas[i] - cost[i]; + + if (total < 0) { + total = 0; + res = i + 1; + } + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int CanCompleteCircuit(int[] gas, int[] cost) { + if (gas.Sum() < cost.Sum()) { + return -1; + } + + int total = 0; + int res = 0; + for (int i = 0; i < gas.Length; i++) { + total += (gas[i] - cost[i]); + + if (total < 0) { + total = 0; + res = i + 1; + } + } + + return res; + } +} +``` + +```go +func canCompleteCircuit(gas []int, cost []int) int { + if sum(gas) < sum(cost) { + return -1 + } + + total := 0 + res := 0 + + for i := range gas { + total += gas[i] - cost[i] + if total < 0 { + total = 0 + res = i + 1 + } + } + + return res +} + +func sum(nums []int) int { + var total int + for _, num := range nums { + total += num + } + return total +} +``` + +```kotlin +class Solution { + fun canCompleteCircuit(gas: IntArray, cost: IntArray): Int { + if (gas.sum() < cost.sum()) { + return -1 + } + + var total = 0 + var res = 0 + + for (i in gas.indices) { + total += gas[i] - cost[i] + if (total < 0) { + total = 0 + res = i + 1 + } + } + + return res + } +} +``` + +```swift +class Solution { + func canCompleteCircuit(_ gas: [Int], _ cost: [Int]) -> Int { + if gas.reduce(0, +) < cost.reduce(0, +) { + return -1 + } + + var total = 0 + var res = 0 + for i in 0.. List[str]: + res = [] + + def valid(s: str): + open = 0 + for c in s: + open += 1 if c == '(' else -1 + if open < 0: + return False + return not open + + def dfs(s: str): + if n * 2 == len(s): + if valid(s): + res.append(s) + return + + dfs(s + '(') + dfs(s + ')') + + dfs("") + return res +``` + +```java +public class Solution { + public boolean valid(String s) { + int open = 0; + for (char c : s.toCharArray()) { + open += c == '(' ? 1 : -1; + if (open < 0) return false; + } + return open == 0; + } + + void dfs(String s, List res, int n) { + if (n * 2 == s.length()) { + if (valid(s)) res.add(s); + return; + } + dfs(s + '(', res, n); + dfs(s + ')', res, n); + } + + public List generateParenthesis(int n) { + List res = new ArrayList<>(); + dfs("", res, n); + return res; + } +} +``` + +```cpp +class Solution { +public: + bool valid(const string& s) { + int open = 0; + for (char c : s) { + open += (c == '(') ? 1 : -1; + if (open < 0) return false; + } + return open == 0; + } + + void dfs(string s, vector& res, int n) { + if (s.length() == 2 * n) { + if (valid(s)) res.push_back(s); + return; + } + dfs(s + '(', res, n); + dfs(s + ')', res, n); + } + + vector generateParenthesis(int n) { + vector res; + dfs("", res, n); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + valid(s) { + let open = 0; + for (const c of s) { + open += c === '(' ? 1 : -1; + if (open < 0) return false; + } + return open === 0; + } + + /** + * @param {string} s + * @param {string[]} + * @param {number} n + */ + dfs(s, res, n) { + if (s.length === 2 * n) { + if (this.valid(s)) res.push(s); + return; + } + this.dfs(s + '(', res, n); + this.dfs(s + ')', res, n); + } + + /** + * @param {number} n + * @return {string[]} + */ + generateParenthesis(n) { + const res = []; + this.dfs("", res, n); + return res; + } +} +``` + +```csharp +public class Solution { + public bool Valid(string s) { + int open = 0; + foreach (char c in s) { + open += (c == '(') ? 1 : -1; + if (open < 0) return false; + } + return open == 0; + } + + public void Dfs(string s, List res, int n) { + if (s.Length == 2 * n) { + if (Valid(s)) res.Add(s); + return; + } + Dfs(s + '(', res, n); + Dfs(s + ')', res, n); + } + + public List GenerateParenthesis(int n) { + List res = new List(); + Dfs("", res, n); + return res; + } +} +``` + +```go +func generateParenthesis(n int) []string { + res := make([]string, 0) + + var valid func(string) bool + valid = func(s string) bool { + open := 0 + for _, c := range s { + if c == '(' { + open++ + } else { + open-- + } + if open < 0 { + return false + } + } + return open == 0 + } + + var dfs func(string) + dfs = func(s string) { + if len(s) == n*2 { + if valid(s) { + res = append(res, s) + } + return + } + + dfs(s + "(") + dfs(s + ")") + } + + dfs("") + return res +} +``` + +```kotlin +class Solution { + fun generateParenthesis(n: Int): List { + val res = mutableListOf() + + fun valid(s: String): Boolean { + var open = 0 + for (c in s) { + if (c == '(') open++ else open-- + if (open < 0) return false + } + return open == 0 + } + + fun dfs(s: String) { + if (s.length == n * 2) { + if (valid(s)) { + res.add(s) + } + return + } + + dfs(s + "(") + dfs(s + ")") + } + + dfs("") + return res + } +} +``` + +```swift +class Solution { + func generateParenthesis(_ n: Int) -> [String] { + var res = [String]() + + func isValid(_ s: String) -> Bool { + var open = 0 + for c in s { + open += (c == "(") ? 1 : -1 + if open < 0 { + return false + } + } + return open == 0 + } + + func dfs(_ s: String) { + if s.count == n * 2 { + if isValid(s) { + res.append(s) + } + return + } + dfs(s + "(") + dfs(s + ")") + } + + dfs("") + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ {2n} * n)$ +* Space complexity: $O(2 ^ {2n} * n)$ + +--- + +## 2. Backtracking + +::tabs-start + +```python +class Solution: + def generateParenthesis(self, n: int) -> List[str]: + stack = [] + res = [] + + def backtrack(openN, closedN): + if openN == closedN == n: + res.append("".join(stack)) + return + + if openN < n: + stack.append("(") + backtrack(openN + 1, closedN) + stack.pop() + if closedN < openN: + stack.append(")") + backtrack(openN, closedN + 1) + stack.pop() + + backtrack(0, 0) + return res +``` + +```java +public class Solution { + private void backtrack(int openN, int closedN, int n, List res, StringBuilder stack) { + if (openN == closedN && openN == n) { + res.add(stack.toString()); + return; + } + + if (openN < n) { + stack.append('('); + backtrack(openN + 1, closedN, n, res, stack); + stack.deleteCharAt(stack.length() - 1); + } + if (closedN < openN) { + stack.append(')'); + backtrack(openN, closedN + 1, n, res, stack); + stack.deleteCharAt(stack.length() - 1); + } + } + + public List generateParenthesis(int n) { + List res = new ArrayList<>(); + StringBuilder stack = new StringBuilder(); + backtrack(0, 0, n, res, stack); + return res; + } +} +``` + +```cpp +class Solution { +public: + void backtrack(int openN, int closedN, int n, vector& res, string& stack) { + if (openN == closedN && openN == n) { + res.push_back(stack); + return; + } + + if (openN < n) { + stack += '('; + backtrack(openN + 1, closedN, n, res, stack); + stack.pop_back(); + } + if (closedN < openN) { + stack += ')'; + backtrack(openN, closedN + 1, n, res, stack); + stack.pop_back(); + } + } + + vector generateParenthesis(int n) { + vector res; + string stack; + backtrack(0, 0, n, res, stack); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} openN + * @param {number} closeN + * @param {number} n + * @param {string[]} res + * @param {string} stack + */ + backtrack(openN, closedN, n, res, stack) { + if (openN === closedN && openN === n) { + res.push(stack); + return; + } + + if (openN < n) { + this.backtrack(openN + 1, closedN, n, res, stack + '('); + } + if (closedN < openN) { + this.backtrack(openN, closedN + 1, n, res, stack + ')'); + } + } + + /** + * @param {number} n + * @return {string[]} + */ + generateParenthesis(n) { + const res = []; + this.backtrack(0, 0, n, res, ''); + return res; + } +} +``` + +```csharp +public class Solution { + public void Backtrack(int openN, int closedN, int n, List res, string stack) { + if (openN == closedN && openN == n) { + res.Add(stack); + return; + } + + if (openN < n) { + Backtrack(openN + 1, closedN, n, res, stack + '('); + } + + if (closedN < openN) { + Backtrack(openN, closedN + 1, n, res, stack + ')'); + } + } + + public List GenerateParenthesis(int n) { + List res = new List(); + string stack = ""; + Backtrack(0, 0, n, res, stack); + return res; + } +} +``` + +```go +func generateParenthesis(n int) []string { + stack := make([]string, 0) + res := make([]string, 0) + + var backtrack func(int, int) + backtrack = func(openN, closedN int) { + if openN == n && closedN == n { + res = append(res, strings.Join(stack, "")) + return + } + + if openN < n { + stack = append(stack, "(") + backtrack(openN+1, closedN) + stack = stack[:len(stack)-1] + } + + if closedN < openN { + stack = append(stack, ")") + backtrack(openN, closedN+1) + stack = stack[:len(stack)-1] + } + } + + backtrack(0, 0) + return res +} +``` + +```kotlin +class Solution { + fun generateParenthesis(n: Int): List { + val stack = mutableListOf() + val res = mutableListOf() + + fun backtrack(openN: Int, closedN: Int) { + if (openN == n && closedN == n) { + res.add(stack.joinToString("")) + return + } + + if (openN < n) { + stack.add("(") + backtrack(openN + 1, closedN) + stack.removeAt(stack.lastIndex) + } + + if (closedN < openN) { + stack.add(")") + backtrack(openN, closedN + 1) + stack.removeAt(stack.lastIndex) + } + } + + backtrack(0, 0) + return res + } +} +``` + +```swift +class Solution { + func generateParenthesis(_ n: Int) -> [String] { + var stack = [Character]() + var res = [String]() + + func backtrack(_ openN: Int, _ closedN: Int) { + if openN == n && closedN == n { + res.append(String(stack)) + return + } + + if openN < n { + stack.append("(") + backtrack(openN + 1, closedN) + stack.removeLast() + } + + if closedN < openN { + stack.append(")") + backtrack(openN, closedN + 1) + stack.removeLast() + } + } + + backtrack(0, 0) + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\frac{4^n}{\sqrt{n}})$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming + +::tabs-start + +```python +class Solution: + def generateParenthesis(self, n): + res = [[] for _ in range(n+1)] + res[0] = [""] + + for k in range(n + 1): + for i in range(k): + for left in res[i]: + for right in res[k-i-1]: + res[k].append("(" + left + ")" + right) + + return res[-1] +``` + +```java +public class Solution { + public List generateParenthesis(int n) { + List> res = new ArrayList<>(); + for (int i = 0; i <= n; i++) { + res.add(new ArrayList<>()); + } + res.get(0).add(""); + + for (int k = 0; k <= n; k++) { + for (int i = 0; i < k; i++) { + for (String left : res.get(i)) { + for (String right : res.get(k - i - 1)) { + res.get(k).add("(" + left + ")" + right); + } + } + } + } + + return res.get(n); + } +} +``` + +```cpp +class Solution { +public: + vector generateParenthesis(int n) { + vector> res(n + 1); + res[0] = {""}; + + for (int k = 0; k <= n; ++k) { + for (int i = 0; i < k; ++i) { + for (const string& left : res[i]) { + for (const string& right : res[k - i - 1]) { + res[k].push_back("(" + left + ")" + right); + } + } + } + } + + return res[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {string[]} + */ + generateParenthesis(n) { + const res = Array.from({ length: n + 1 }, () => []); + res[0] = [""]; + + for (let k = 0; k <= n; k++) { + for (let i = 0; i < k; i++) { + for (const left of res[i]) { + for (const right of res[k - i - 1]) { + res[k].push("(" + left + ")" + right); + } + } + } + } + + return res[n]; + } +} +``` + +```csharp +public class Solution { + public List GenerateParenthesis(int n) { + List> res = new List>(); + for (int i = 0; i <= n; i++) { + res.Add(new List()); + } + res[0].Add(""); + + for (int k = 0; k <= n; k++) { + for (int i = 0; i < k; i++) { + foreach (string left in res[i]) { + foreach (string right in res[k - i - 1]) { + res[k].Add("(" + left + ")" + right); + } + } + } + } + + return res[n]; + } +} +``` + +```go +func generateParenthesis(n int) []string { + res := make([][]string, n+1) + res[0] = []string{""} + + for k := 1; k <= n; k++ { + res[k] = make([]string, 0) + for i := 0; i < k; i++ { + for _, left := range res[i] { + for _, right := range res[k-i-1] { + res[k] = append(res[k], "(" + left + ")" + right) + } + } + } + } + + return res[n] +} +``` + +```kotlin +class Solution { + fun generateParenthesis(n: Int): List { + val res = Array(n + 1) { mutableListOf() } + res[0] = mutableListOf("") + + for (k in 1..n) { + for (i in 0 until k) { + for (left in res[i]) { + for (right in res[k-i-1]) { + res[k].add("(" + left + ")" + right) + } + } + } + } + + return res[n] + } +} +``` + +```swift +class Solution { + func generateParenthesis(_ n: Int) -> [String] { + var res = [[String]](repeating: [], count: n + 1) + res[0] = [""] + + for k in 0...n { + for i in 0.. int: + n = len(s) + res = 0 + + for i in range(n): + cur_cost = 0 + for j in range(i, n): + cur_cost += abs(ord(t[j]) - ord(s[j])) + if cur_cost > maxCost: + break + res = max(res, j - i + 1) + + return res +``` + +```java +public class Solution { + public int equalSubstring(String s, String t, int maxCost) { + int n = s.length(); + int res = 0; + + for (int i = 0; i < n; i++) { + int curCost = 0; + for (int j = i; j < n; j++) { + curCost += Math.abs(t.charAt(j) - s.charAt(j)); + if (curCost > maxCost) { + break; + } + res = Math.max(res, j - i + 1); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int equalSubstring(string s, string t, int maxCost) { + int n = s.size(); + int res = 0; + + for (int i = 0; i < n; i++) { + int curCost = 0; + for (int j = i; j < n; j++) { + curCost += abs(t[j] - s[j]); + if (curCost > maxCost) { + break; + } + res = max(res, j - i + 1); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @param {number} maxCost + * @return {number} + */ + equalSubstring(s, t, maxCost) { + const n = s.length; + let res = 0; + + for (let i = 0; i < n; i++) { + let curCost = 0; + for (let j = i; j < n; j++) { + curCost += Math.abs(t.charCodeAt(j) - s.charCodeAt(j)); + if (curCost > maxCost) { + break; + } + res = Math.max(res, j - i + 1); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Sliding Window + +::tabs-start + +```python +class Solution: + def equalSubstring(self, s: str, t: str, maxCost: int) -> int: + curCost = 0 + l = 0 + res = 0 + + for r in range(len(s)): + curCost += abs(ord(s[r]) - ord(t[r])) + while curCost > maxCost: + curCost -= abs(ord(s[l]) - ord(t[l])) + l += 1 + res = max(res, r - l + 1) + + return res +``` + +```java +public class Solution { + public int equalSubstring(String s, String t, int maxCost) { + int curCost = 0, l = 0, res = 0; + + for (int r = 0; r < s.length(); r++) { + curCost += Math.abs(s.charAt(r) - t.charAt(r)); + while (curCost > maxCost) { + curCost -= Math.abs(s.charAt(l) - t.charAt(l)); + l++; + } + res = Math.max(res, r - l + 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int equalSubstring(string s, string t, int maxCost) { + int curCost = 0, l = 0, res = 0; + + for (int r = 0; r < s.length(); r++) { + curCost += abs(s[r] - t[r]); + while (curCost > maxCost) { + curCost -= abs(s[l] - t[l]); + l++; + } + res = max(res, r - l + 1); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @param {number} maxCost + * @return {number} + */ + equalSubstring(s, t, maxCost) { + let curCost = 0, l = 0, res = 0; + + for (let r = 0; r < s.length; r++) { + curCost += Math.abs(s.charCodeAt(r) - t.charCodeAt(r)); + while (curCost > maxCost) { + curCost -= Math.abs(s.charCodeAt(l) - t.charCodeAt(l)); + l++; + } + res = Math.max(res, r - l + 1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Sliding Window (Optimal) + +::tabs-start + +```python +class Solution: + def equalSubstring(self, s: str, t: str, maxCost: int) -> int: + l = 0 + for r in range(len(s)): + maxCost -= abs(ord(s[r]) - ord(t[r])) + if maxCost < 0: + maxCost += abs(ord(s[l]) - ord(t[l])) + l += 1 + return len(s) - l +``` + +```java +public class Solution { + public int equalSubstring(String s, String t, int maxCost) { + int l = 0; + for (int r = 0; r < s.length(); r++) { + maxCost -= Math.abs(s.charAt(r) - t.charAt(r)); + if (maxCost < 0) { + maxCost += Math.abs(s.charAt(l) - t.charAt(l)); + l++; + } + } + return s.length() - l; + } +} +``` + +```cpp +class Solution { +public: + int equalSubstring(string s, string t, int maxCost) { + int l = 0; + for (int r = 0; r < s.length(); r++) { + maxCost -= abs(s[r] - t[r]); + if (maxCost < 0) { + maxCost += abs(s[l] - t[l]); + l++; + } + } + return s.length() - l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @param {number} maxCost + * @return {number} + */ + equalSubstring(s, t, maxCost) { + let l = 0; + for (let r = 0; r < s.length; r++) { + maxCost -= Math.abs(s.charCodeAt(r) - t.charCodeAt(r)); + if (maxCost < 0) { + maxCost += Math.abs(s.charCodeAt(l) - t.charCodeAt(l)); + l++; + } + } + return s.length - l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/greatest-common-divisor-of-strings.md b/articles/greatest-common-divisor-of-strings.md new file mode 100644 index 000000000..ae138e7d6 --- /dev/null +++ b/articles/greatest-common-divisor-of-strings.md @@ -0,0 +1,459 @@ +## 1. Iteration + +::tabs-start + +```python +class Solution: + def gcdOfStrings(self, str1: str, str2: str) -> str: + len1, len2 = len(str1), len(str2) + + def isDivisor(l): + if len1 % l != 0 or len2 % l != 0: + return False + f1, f2 = len1 // l, len2 // l + return str1[:l] * f1 == str1 and str1[:l] * f2 == str2 + + for l in range(min(len1, len2), 0, -1): + if isDivisor(l): + return str1[:l] + + return "" +``` + +```java +public class Solution { + public String gcdOfStrings(String str1, String str2) { + int len1 = str1.length(), len2 = str2.length(); + + for (int l = Math.min(len1, len2); l > 0; l--) { + if (isDivisor(l, len1, len2, str1, str2)) { + return str1.substring(0, l); + } + } + + return ""; + } + + public boolean isDivisor(int l, int len1, int len2, String str1, String str2) { + if (len1 % l != 0 || len2 % l != 0) { + return false; + } + String sub = str1.substring(0, l); + int f1 = len1 / l, f2 = len2 / l; + return sub.repeat(f1).equals(str1) && sub.repeat(f2).equals(str2); + } +} +``` + +```cpp +class Solution { +public: + string gcdOfStrings(string str1, string str2) { + int len1 = str1.size(), len2 = str2.size(); + + auto isDivisor = [&](int l) { + if (len1 % l != 0 || len2 % l != 0) { + return false; + } + string sub = str1.substr(0, l); + int f1 = len1 / l, f2 = len2 / l; + string repeated1 = "", repeated2 = ""; + for (int i = 0; i < f1; ++i) repeated1 += sub; + for (int i = 0; i < f2; ++i) repeated2 += sub; + return repeated1 == str1 && repeated2 == str2; + }; + + for (int l = min(len1, len2); l > 0; l--) { + if (isDivisor(l)) { + return str1.substr(0, l); + } + } + + return ""; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} str1 + * @param {string} str2 + * @return {string} + */ + gcdOfStrings(str1, str2) { + const len1 = str1.length, len2 = str2.length; + + const isDivisor = (l) => { + if (len1 % l !== 0 || len2 % l !== 0) { + return false; + } + const sub = str1.slice(0, l); + const f1 = len1 / l, f2 = len2 / l; + return sub.repeat(f1) === str1 && sub.repeat(f2) === str2; + }; + + for (let l = Math.min(len1, len2); l > 0; l--) { + if (isDivisor(l)) { + return str1.slice(0, l); + } + } + + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(min(m, n) * (m + n))$ +* Space complexity: $O(m + n)$ + +> Where $m$ and $n$ are the lengths of the strings $str1$ and $str2$ respectively. + +--- + +## 2. Iteration (Space Optimized) + +::tabs-start + +```python +class Solution: + def gcdOfStrings(self, str1: str, str2: str) -> str: + m, n = len(str1), len(str2) + if m < n: + m, n = n, m + str1, str2 = str2, str1 + + for l in range(n, 0, -1): + if m % l != 0 or n % l != 0: + continue + + valid = True + for i in range(m): + if str1[i] != str2[i % l]: + valid = False + break + if not valid: continue + + for i in range(l, n): + if str2[i] != str2[i % l]: + valid = False + break + if valid: return str2[:l] + + return "" +``` + +```java +public class Solution { + public String gcdOfStrings(String str1, String str2) { + int m = str1.length(), n = str2.length(); + if (m < n) { + String temp = str1; + str1 = str2; + str2 = temp; + int tempLen = m; + m = n; + n = tempLen; + } + + for (int l = n; l > 0; l--) { + if (m % l != 0 || n % l != 0) { + continue; + } + + boolean valid = true; + for (int i = 0; i < m; i++) { + if (str1.charAt(i) != str2.charAt(i % l)) { + valid = false; + break; + } + } + if (!valid) continue; + + for (int i = l; i < n; i++) { + if (str2.charAt(i) != str2.charAt(i % l)) { + valid = false; + break; + } + } + if (valid) { + return str2.substring(0, l); + } + } + + return ""; + } +} +``` + +```cpp +class Solution { +public: + string gcdOfStrings(string str1, string str2) { + int m = str1.size(), n = str2.size(); + if (m < n) { + swap(m, n); + swap(str1, str2); + } + + for (int l = n; l > 0; l--) { + if (m % l != 0 || n % l != 0) { + continue; + } + + bool valid = true; + for (int i = 0; i < m; i++) { + if (str1[i] != str2[i % l]) { + valid = false; + break; + } + } + if (!valid) continue; + + for (int i = l; i < n; i++) { + if (str2[i] != str2[i % l]) { + valid = false; + break; + } + } + if (valid) { + return str2.substr(0, l); + } + } + + return ""; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} str1 + * @param {string} str2 + * @return {string} + */ + gcdOfStrings(str1, str2) { + let m = str1.length, n = str2.length; + if (m < n) { + [m, n] = [n, m]; + [str1, str2] = [str2, str1]; + } + + for (let l = n; l > 0; l--) { + if (m % l !== 0 || n % l !== 0) { + continue; + } + + let valid = true; + for (let i = 0; i < m; i++) { + if (str1[i] !== str2[i % l]) { + valid = false; + break; + } + } + if (!valid) continue; + + for (let i = l; i < n; i++) { + if (str2[i] !== str2[i % l]) { + valid = false; + break; + } + } + if (valid) { + return str2.slice(0, l); + } + } + + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(min(m, n) * (m + n))$ +* Space complexity: $O(g)$ for the output string. + +> Where $m$ is the length of the string $str1$, $n$ is the length of the string $str2$, and $g$ is the length of the output string. + +--- + +## 3. Greatest Common Divisor + +::tabs-start + +```python +class Solution: + def gcdOfStrings(self, str1: str, str2: str) -> str: + if str1 + str2 != str2 + str1: + return "" + + g = gcd(len(str1), len(str2)) + return str1[:g] +``` + +```java +public class Solution { + public String gcdOfStrings(String str1, String str2) { + if (!(str1 + str2).equals(str2 + str1)) { + return ""; + } + int g = gcd(str1.length(), str2.length()); + return str1.substring(0, g); + } + + private int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +} +``` + +```cpp +class Solution { +public: + string gcdOfStrings(string str1, string str2) { + if (str1 + str2 != str2 + str1) { + return ""; + } + int g = __gcd((int)str1.size(), (int)str2.size()); + return str1.substr(0, g); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} str1 + * @param {string} str2 + * @return {string} + */ + gcdOfStrings(str1, str2) { + if (str1 + str2 !== str2 + str1) { + return ""; + } + const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b)); + const g = gcd(str1.length, str2.length); + return str1.slice(0, g); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(m + n)$. + +> Where $m$ and $n$ are the lengths of the strings $str1$ and $str2$ respectively. + +--- + +## 4. Greatest Common Divisor (Space Optimized) + +::tabs-start + +```python +class Solution: + def gcdOfStrings(self, str1: str, str2: str) -> str: + g = gcd(len(str1), len(str2)) + + if all(str1[i] == str1[i % g] for i in range(len(str1))) and \ + all(str2[i] == str1[i % g] for i in range(len(str2))): + return str1[:g] + return "" +``` + +```java +public class Solution { + public String gcdOfStrings(String str1, String str2) { + int g = gcd(str1.length(), str2.length()); + + for (int i = 0; i < str1.length(); i++) { + if (str1.charAt(i) != str1.charAt(i % g)) { + return ""; + } + } + + for (int i = 0; i < str2.length(); i++) { + if (str2.charAt(i) != str1.charAt(i % g)) { + return ""; + } + } + + return str1.substring(0, g); + } + + private int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +} +``` + +```cpp +class Solution { +public: + string gcdOfStrings(string str1, string str2) { + int g = __gcd((int)str1.size(), (int)str2.size()); + + for (int i = 0; i < str1.size(); i++) { + if (str1[i] != str1[i % g]) { + return ""; + } + } + + for (int i = 0; i < str2.size(); i++) { + if (str2[i] != str1[i % g]) { + return ""; + } + } + + return str1.substr(0, g); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} str1 + * @param {string} str2 + * @return {string} + */ + gcdOfStrings(str1, str2) { + const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b)); + const g = gcd(str1.length, str2.length); + + for (let i = 0; i < str1.length; i++) { + if (str1[i] !== str1[i % g]) { + return ""; + } + } + + for (let i = 0; i < str2.length; i++) { + if (str2[i] !== str1[i % g]) { + return ""; + } + } + + return str1.slice(0, g); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(g)$ for the output string. + +> Where $m$ is the length of the string $str1$, $n$ is the length of the string $str2$, and $g$ is the GCD of $m$ and $n$. \ No newline at end of file diff --git a/articles/greatest-common-divisor-traversal.md b/articles/greatest-common-divisor-traversal.md new file mode 100644 index 000000000..57628c98a --- /dev/null +++ b/articles/greatest-common-divisor-traversal.md @@ -0,0 +1,1645 @@ +## 1. Brute Force (DFS) + +::tabs-start + +```python +class Solution: + def canTraverseAllPairs(self, nums: List[int]) -> bool: + n = len(nums) + visit = [False] * n + adj = [[] for _ in range(n)] + for i in range(n): + for j in range(i + 1, n): + if gcd(nums[i], nums[j]) > 1: + adj[i].append(j) + adj[j].append(i) + + def dfs(node): + visit[node] = True + for nei in adj[node]: + if not visit[nei]: + dfs(nei) + + dfs(0) + for node in visit: + if not node: + return False + return True +``` + +```java +public class Solution { + public boolean canTraverseAllPairs(int[] nums) { + int n = nums.length; + boolean[] visit = new boolean[n]; + List> adj = new ArrayList<>(); + for (int i = 0; i < n; i++) { + adj.add(new ArrayList<>()); + } + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if (gcd(nums[i], nums[j]) > 1) { + adj.get(i).add(j); + adj.get(j).add(i); + } + } + } + + dfs(0, adj, visit); + for (boolean node : visit) { + if (!node) { + return false; + } + } + return true; + } + + private void dfs(int node, List> adj, boolean[] visit) { + visit[node] = true; + for (int nei : adj.get(node)) { + if (!visit[nei]) { + dfs(nei, adj, visit); + } + } + } + + private int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +} +``` + +```cpp +class Solution { +public: + bool canTraverseAllPairs(vector& nums) { + int n = nums.size(); + vector visit(n, false); + vector> adj(n); + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if (__gcd(nums[i], nums[j]) > 1) { + adj[i].push_back(j); + adj[j].push_back(i); + } + } + } + + dfs(0, adj, visit); + for (bool node : visit) { + if (!node) { + return false; + } + } + return true; + } + +private: + void dfs(int node, vector>& adj, vector& visit) { + visit[node] = true; + for (int& nei : adj[node]) { + if (!visit[nei]) { + dfs(nei, adj, visit); + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + canTraverseAllPairs(nums) { + const n = nums.length; + const visit = new Array(n).fill(false); + const adj = Array.from({ length: n }, () => []); + + const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b)); + + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + if (gcd(nums[i], nums[j]) > 1) { + adj[i].push(j); + adj[j].push(i); + } + } + } + + const dfs = (node) => { + visit[node] = true; + for (const nei of adj[node]) { + if (!visit[nei]) { + dfs(nei); + } + } + }; + + dfs(0); + return visit.every((node) => node); + } +} +``` + +```csharp +public class Solution { + public bool CanTraverseAllPairs(int[] nums) { + int n = nums.Length; + bool[] visited = new bool[n]; + List[] adj = new List[n]; + for (int i = 0; i < n; i++) { + adj[i] = new List(); + } + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if (GCD(nums[i], nums[j]) > 1) { + adj[i].Add(j); + adj[j].Add(i); + } + } + } + + void DFS(int node) { + visited[node] = true; + foreach (int neighbor in adj[node]) { + if (!visited[neighbor]) { + DFS(neighbor); + } + } + } + + DFS(0); + foreach (bool v in visited) { + if (!v) return false; + } + + return true; + } + + private int GCD(int a, int b) { + while (b != 0) { + int temp = b; + b = a % b; + a = temp; + } + return a; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 \log n)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Disjoint Set Union + +::tabs-start + +```python +class UnionFind: + def __init__(self, n): + self.n = n + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu = self.find(u) + pv = self.find(v) + if pu == pv: + return False + self.n -= 1 + if self.Size[pu] < self.Size[pv]: + pu, pv = pv, pu + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + return True + + def isConnected(self): + return self.n == 1 + +class Solution: + def canTraverseAllPairs(self, nums: List[int]) -> bool: + uf = UnionFind(len(nums)) + + factor_index = {} # f -> index of value with factor f + for i, n in enumerate(nums): + f = 2 + while f * f <= n: + if n % f == 0: + if f in factor_index: + uf.union(i, factor_index[f]) + else: + factor_index[f] = i + while n % f == 0: + n = n // f + f += 1 + if n > 1: + if n in factor_index: + uf.union(i, factor_index[n]) + else: + factor_index[n] = i + + return uf.isConnected() +``` + +```java +class UnionFind { + private int n; + private int[] Parent; + private int[] Size; + + public UnionFind(int n) { + this.n = n; + this.Parent = new int[n + 1]; + this.Size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + this.Parent[i] = i; + this.Size[i] = 1; + } + } + + public int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u); + int pv = find(v); + if (pu == pv) return false; + n--; + if (Size[pu] < Size[pv]) { + int temp = pu; + pu = pv; + pv = temp; + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } + + public boolean isConnected() { + return n == 1; + } +} + +public class Solution { + public boolean canTraverseAllPairs(int[] nums) { + int n = nums.length; + UnionFind uf = new UnionFind(n); + Map factorIndex = new HashMap<>(); + + for (int i = 0; i < n; i++) { + int num = nums[i]; + int f = 2; + while (f * f <= num) { + if (num % f == 0) { + if (factorIndex.containsKey(f)) { + uf.union(i, factorIndex.get(f)); + } else { + factorIndex.put(f, i); + } + while (num % f == 0) { + num /= f; + } + } + f++; + } + if (num > 1) { + if (factorIndex.containsKey(num)) { + uf.union(i, factorIndex.get(num)); + } else { + factorIndex.put(num, i); + } + } + } + + return uf.isConnected(); + } +} +``` + +```cpp +class UnionFind { +private: + int n; + vector Parent; + vector Size; + +public: + UnionFind(int n) : n(n), Parent(n + 1), Size(n + 1, 1) { + for (int i = 0; i <= n; i++) { + Parent[i] = i; + } + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + bool unionNodes(int u, int v) { + int pu = find(u); + int pv = find(v); + if (pu == pv) return false; + n--; + if (Size[pu] < Size[pv]) swap(pu, pv); + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } + + bool isConnected() { + return n == 1; + } +}; + +class Solution { +public: + bool canTraverseAllPairs(vector& nums) { + int n = nums.size(); + UnionFind uf(n); + + unordered_map factor_index; + + for (int i = 0; i < n; i++) { + int num = nums[i]; + int f = 2; + while (f * f <= num) { + if (num % f == 0) { + if (factor_index.count(f)) { + uf.unionNodes(i, factor_index[f]); + } else { + factor_index[f] = i; + } + while (num % f == 0) { + num /= f; + } + } + f++; + } + if (num > 1) { + if (factor_index.count(num)) { + uf.unionNodes(i, factor_index[num]); + } else { + factor_index[num] = i; + } + } + } + + return uf.isConnected(); + } +}; +``` + +```javascript +class UnionFind { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.Parent = Array.from({ length: n + 1 }, (_, i) => i); + this.Size = Array(n + 1).fill(1); + this.n = n; + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.Parent[node] !== node) { + this.Parent[node] = this.find(this.Parent[node]); + } + return this.Parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u); + let pv = this.find(v); + if (pu === pv) return false; + this.n--; + if (this.Size[pu] < this.Size[pv]) { + [pu, pv] = [pv, pu]; + } + this.Size[pu] += this.Size[pv]; + this.Parent[pv] = pu; + return true; + } + + /** + * @return {number} + */ + isConnected() { + return this.n === 1; + } +} + +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + canTraverseAllPairs(nums) { + const n = nums.length; + const uf = new UnionFind(n); + const factor_index = new Map(); + + for (let i = 0; i < n; i++) { + let num = nums[i]; + let f = 2; + while (f * f <= num) { + if (num % f === 0) { + if (factor_index.has(f)) { + uf.union(i, factor_index.get(f)); + } else { + factor_index.set(f, i); + } + while (num % f === 0) { + num = Math.floor(num / f); + } + } + f++; + } + if (num > 1) { + if (factor_index.has(num)) { + uf.union(i, factor_index.get(num)); + } else { + factor_index.set(num, i); + } + } + } + + return uf.isConnected(); + } +} +``` + +```csharp +public class Solution { + public class UnionFind { + public int Count; + private int[] Parent; + private int[] Size; + + public UnionFind(int n) { + Count = n; + Parent = new int[n]; + Size = new int[n]; + for (int i = 0; i < n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + public int Find(int x) { + if (Parent[x] != x) { + Parent[x] = Find(Parent[x]); + } + return Parent[x]; + } + + public bool Union(int x, int y) { + int px = Find(x); + int py = Find(y); + if (px == py) return false; + Count--; + if (Size[px] < Size[py]) { + int temp = px; + px = py; + py = temp; + } + Size[px] += Size[py]; + Parent[py] = px; + return true; + } + + public bool IsConnected() { + return Count == 1; + } + } + + public bool CanTraverseAllPairs(int[] nums) { + int n = nums.Length; + if (n == 1) return true; + if (Array.Exists(nums, x => x == 1)) return false; + + UnionFind uf = new UnionFind(n); + Dictionary factorIndex = new Dictionary(); + + for (int i = 0; i < n; i++) { + int num = nums[i]; + int original = num; + for (int f = 2; f * f <= num; f++) { + if (num % f == 0) { + if (factorIndex.ContainsKey(f)) { + uf.Union(i, factorIndex[f]); + } else { + factorIndex[f] = i; + } + while (num % f == 0) { + num /= f; + } + } + } + if (num > 1) { + if (factorIndex.ContainsKey(num)) { + uf.Union(i, factorIndex[num]); + } else { + factorIndex[num] = i; + } + } + } + + return uf.IsConnected(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n\sqrt {m})$ +* Space complexity: $O(n \log m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum value in the array. + +--- + +## 3. Sieve of Eratosthenes + DSU + +::tabs-start + +```python +class UnionFind: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu = self.find(u) + pv = self.find(v) + if pu == pv: + return False + if self.Size[pu] < self.Size[pv]: + pu, pv = pv, pu + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + return True + +class Solution: + def canTraverseAllPairs(self, nums: List[int]) -> bool: + N = len(nums) + if N == 1: + return True + if any(num == 1 for num in nums): + return False + + MAX = max(nums) + sieve = [0] * (MAX + 1) + p = 2 + while p * p <= MAX: + if sieve[p] == 0: + for composite in range(p * p, MAX + 1, p): + sieve[composite] = p + p += 1 + + uf = UnionFind(N + MAX + 1) + for i in range(N): + num = nums[i] + if sieve[num] == 0: # num is prime + uf.union(i, N + num) + continue + + while num > 1: + prime = sieve[num] if sieve[num] != 0 else num + uf.union(i, N + prime) + while num % prime == 0: + num //= prime + + root = uf.find(0) + for i in range(1, N): + if uf.find(i) != root: + return False + return True +``` + +```java +class UnionFind { + private int[] Parent; + private int[] Size; + + public UnionFind(int n) { + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + public int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u); + int pv = find(v); + if (pu == pv) { + return false; + } + if (Size[pu] < Size[pv]) { + int temp = pu; + pu = pv; + pv = temp; + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } +} + +public class Solution { + public boolean canTraverseAllPairs(int[] nums) { + int N = nums.length; + if (N == 1) { + return true; + } + int MAX = 0; + for (int num : nums) { + MAX = Math.max(MAX, num); + if (num == 1) { + return false; + } + } + + int[] sieve = new int[MAX + 1]; + for (int p = 2; p * p <= MAX; p++) { + if (sieve[p] == 0) { + for (int composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + } + + UnionFind uf = new UnionFind(N + MAX + 1); + for (int i = 0; i < N; i++) { + int num = nums[i]; + if (sieve[num] == 0) { // num is prime + uf.union(i, N + num); + continue; + } + + while (num > 1) { + int prime = sieve[num] != 0 ? sieve[num] : num; + uf.union(i, N + prime); + while (num % prime == 0) { + num /= prime; + } + } + } + + int root = uf.find(0); + for (int i = 1; i < N; i++) { + if (uf.find(i) != root) { + return false; + } + } + return true; + } +} +``` + +```cpp +class UnionFind { +private: + vector Parent, Size; + +public: + UnionFind(int n) { + Parent.resize(n + 1); + Size.resize(n + 1, 1); + for (int i = 0; i <= n; i++) { + Parent[i] = i; + } + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + bool unionSet(int u, int v) { + int pu = find(u); + int pv = find(v); + if (pu == pv) { + return false; + } + if (Size[pu] < Size[pv]) { + swap(pu, pv); + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } +}; + +class Solution { +public: + bool canTraverseAllPairs(vector& nums) { + int N = nums.size(); + if (N == 1) { + return true; + } + for (int num : nums) { + if (num == 1) { + return false; + } + } + + int MAX = *max_element(nums.begin(), nums.end()); + vector sieve(MAX + 1, 0); + for (int p = 2; p * p <= MAX; p++) { + if (sieve[p] == 0) { + for (int composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + } + + UnionFind uf(N + MAX + 1); + for (int i = 0; i < N; i++) { + int num = nums[i]; + if (sieve[num] == 0) { // num is prime + uf.unionSet(i, N + num); + continue; + } + + while (num > 1) { + int prime = sieve[num] != 0 ? sieve[num] : num; + uf.unionSet(i, N + prime); + while (num % prime == 0) { + num /= prime; + } + } + } + + int root = uf.find(0); + for (int i = 1; i < N; i++) { + if (uf.find(i) != root) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class UnionFind { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.Parent = Array.from({ length: n + 1 }, (_, i) => i); + this.Size = Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.Parent[node] !== node) { + this.Parent[node] = this.find(this.Parent[node]); + } + return this.Parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u); + let pv = this.find(v); + if (pu === pv) return false; + if (this.Size[pu] < this.Size[pv]) { + [pu, pv] = [pv, pu]; + } + this.Size[pu] += this.Size[pv]; + this.Parent[pv] = pu; + return true; + } +} + +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + canTraverseAllPairs(nums) { + const N = nums.length; + if (N === 1) return true; + if (nums.includes(1)) return false; + + const MAX = Math.max(...nums); + const sieve = Array(MAX + 1).fill(0); + for (let p = 2; p * p <= MAX; p++) { + if (sieve[p] === 0) { + for (let composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + } + + const uf = new UnionFind(N + MAX + 1); + for (let i = 0; i < N; i++) { + let num = nums[i]; + if (sieve[num] === 0) { // num is prime + uf.union(i, N + num); + continue; + } + + while (num > 1) { + const prime = sieve[num] !== 0 ? sieve[num] : num; + uf.union(i, N + prime); + while (num % prime === 0) { + num = Math.floor(num / prime); + } + } + } + + const root = uf.find(0); + for (let i = 1; i < N; i++) { + if (uf.find(i) !== root) { + return false; + } + } + return true; + } +} +``` + +```csharp +public class UnionFind { + public int[] Parent; + public int[] Size; + + public UnionFind(int n) { + Parent = new int[n]; + Size = new int[n]; + for (int i = 0; i < n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + public int Find(int x) { + if (Parent[x] != x) { + Parent[x] = Find(Parent[x]); + } + return Parent[x]; + } + + public bool Union(int x, int y) { + int px = Find(x); + int py = Find(y); + if (px == py) return false; + if (Size[px] < Size[py]) { + int temp = px; + px = py; + py = temp; + } + Parent[py] = px; + Size[px] += Size[py]; + return true; + } +} + +public class Solution { + public bool CanTraverseAllPairs(int[] nums) { + int n = nums.Length; + if (n == 1) return true; + if (Array.Exists(nums, x => x == 1)) return false; + + int maxVal = nums.Max(); + int[] sieve = new int[maxVal + 1]; + for (int i = 2; i * i <= maxVal; i++) { + if (sieve[i] == 0) { + for (int j = i * i; j <= maxVal; j += i) { + if (sieve[j] == 0) sieve[j] = i; + } + } + } + + UnionFind uf = new UnionFind(n + maxVal + 1); + + for (int i = 0; i < n; i++) { + int num = nums[i]; + if (sieve[num] == 0) { + uf.Union(i, n + num); + continue; + } + + while (num > 1) { + int prime = sieve[num] != 0 ? sieve[num] : num; + uf.Union(i, n + prime); + while (num % prime == 0) { + num /= prime; + } + } + } + + int root = uf.Find(0); + for (int i = 1; i < n; i++) { + if (uf.Find(i) != root) return false; + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n \log m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum value in the array. + +--- + +## 4. Sieve of Eratosthenes + DFS + +::tabs-start + +```python +class Solution: + def canTraverseAllPairs(self, nums: List[int]) -> bool: + N = len(nums) + if N == 1: + return True + if any(num == 1 for num in nums): + return False + + MAX = max(nums) + sieve = [0] * (MAX + 1) + p = 2 + while p * p <= MAX: + if sieve[p] == 0: + for composite in range(p * p, MAX + 1, p): + sieve[composite] = p + p += 1 + + adj = defaultdict(list) + for i in range(N): + num = nums[i] + if sieve[num] == 0: # num is prime + adj[i].append(N + num) + adj[N + num].append(i) + continue + + while num > 1: + prime = sieve[num] if sieve[num] != 0 else num + adj[i].append(N + prime) + adj[N + prime].append(i) + while num % prime == 0: + num //= prime + + visited = set() + def dfs(node): + visited.add(node) + for nei in adj[node]: + if nei not in visited: + dfs(nei) + + dfs(0) + for i in range(N): + if i not in visited: + return False + return True +``` + +```java +public class Solution { + public boolean canTraverseAllPairs(int[] nums) { + int N = nums.length; + if (N == 1) return true; + for (int num : nums) { + if (num == 1) return false; + } + + int MAX = Arrays.stream(nums).max().getAsInt(); + int[] sieve = new int[MAX + 1]; + for (int p = 2; p * p <= MAX; p++) { + if (sieve[p] == 0) { + for (int composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + } + + Map> adj = new HashMap<>(); + for (int i = 0; i < N; i++) { + int num = nums[i]; + adj.putIfAbsent(i, new ArrayList<>()); + + if (sieve[num] == 0) { + adj.putIfAbsent(N + num, new ArrayList<>()); + adj.get(i).add(N + num); + adj.get(N + num).add(i); + continue; + } + + while (num > 1) { + int prime = (sieve[num] == 0) ? num : sieve[num]; + adj.putIfAbsent(N + prime, new ArrayList<>()); + adj.get(i).add(N + prime); + adj.get(N + prime).add(i); + while (num % prime == 0) num /= prime; + } + } + + Set visited = new HashSet<>(); + dfs(0, adj, visited); + for (int i = 0; i < N; i++) { + if (!visited.contains(i)) return false; + } + return true; + } + + private void dfs(int node, Map> adj, Set visited) { + visited.add(node); + for (int neighbor : adj.get(node)) { + if (!visited.contains(neighbor)) { + dfs(neighbor, adj, visited); + } + } + } +} +``` + +```cpp +class Solution { +public: + bool canTraverseAllPairs(vector& nums) { + int N = nums.size(); + if (N == 1) return true; + if (find(nums.begin(), nums.end(), 1) != nums.end()) return false; + + int MAX = *max_element(nums.begin(), nums.end()); + vector sieve(MAX + 1, 0); + for (int p = 2; p * p <= MAX; p++) { + if (sieve[p] == 0) { + for (int composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + } + + unordered_map> adj; + for (int i = 0; i < N; i++) { + int num = nums[i]; + if (!adj.count(i)) adj[i] = {}; + + if (sieve[num] == 0) { + adj[N + num].push_back(i); + adj[i].push_back(N + num); + continue; + } + + while (num > 1) { + int prime = (sieve[num] == 0) ? num : sieve[num]; + adj[N + prime].push_back(i); + adj[i].push_back(N + prime); + while (num % prime == 0) num /= prime; + } + } + + unordered_set visited; + dfs(0, adj, visited); + for (int i = 0; i < N; i++) { + if (visited.find(i) == visited.end()) return false; + } + return true; + } + +private: + void dfs(int node, unordered_map>& adj, unordered_set& visited) { + visited.insert(node); + for (int& neighbor : adj[node]) { + if (visited.find(neighbor) == visited.end()) { + dfs(neighbor, adj, visited); + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + canTraverseAllPairs(nums) { + const N = nums.length; + if (N === 1) return true; + if (nums.includes(1)) return false; + + const MAX = Math.max(...nums); + const sieve = new Array(MAX + 1).fill(0); + for (let p = 2; p * p <= MAX; p++) { + if (sieve[p] === 0) { + for (let composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + } + + const adj = new Map(); + for (let i = 0; i < N; i++) { + if (!adj.has(i)) adj.set(i, []); + let num = nums[i]; + + if (sieve[num] === 0) { + if (!adj.has(N + num)) adj.set(N + num, []); + adj.get(i).push(N + num); + adj.get(N + num).push(i); + continue; + } + + while (num > 1) { + const prime = sieve[num] === 0 ? num : sieve[num]; + if (!adj.has(N + prime)) adj.set(N + prime, []); + adj.get(i).push(N + prime); + adj.get(N + prime).push(i); + while (num % prime === 0) num = Math.floor(num / prime); + } + } + + const visited = new Set(); + const dfs = (node) => { + visited.add(node); + for (const neighbor of adj.get(node) || []) { + if (!visited.has(neighbor)) { + dfs(neighbor); + } + } + }; + + dfs(0); + for (let i = 0; i < N; i++) { + if (!visited.has(i)) return false; + } + return true; + } +} +``` + +```csharp +public class Solution { + public bool CanTraverseAllPairs(int[] nums) { + int N = nums.Length; + if (N == 1) return true; + if (Array.Exists(nums, num => num == 1)) return false; + + int MAX = nums.Max(); + int[] sieve = new int[MAX + 1]; + for (int p = 2; p * p <= MAX; p++) { + if (sieve[p] == 0) { + for (int mult = p * p; mult <= MAX; mult += p) { + if (sieve[mult] == 0) { + sieve[mult] = p; + } + } + } + } + + Dictionary> adj = new Dictionary>(); + for (int i = 0; i < N; i++) { + int num = nums[i]; + if (sieve[num] == 0) { + AddEdge(adj, i, N + num); + continue; + } + while (num > 1) { + int prime = sieve[num] != 0 ? sieve[num] : num; + AddEdge(adj, i, N + prime); + while (num % prime == 0) { + num /= prime; + } + } + } + + HashSet visited = new HashSet(); + DFS(0, adj, visited); + + for (int i = 0; i < N; i++) { + if (!visited.Contains(i)) return false; + } + + return true; + } + + private void AddEdge(Dictionary> adj, int u, int v) { + if (!adj.ContainsKey(u)) adj[u] = new List(); + if (!adj.ContainsKey(v)) adj[v] = new List(); + adj[u].Add(v); + adj[v].Add(u); + } + + private void DFS(int node, Dictionary> adj, HashSet visited) { + visited.Add(node); + if (!adj.ContainsKey(node)) return; + foreach (int nei in adj[node]) { + if (!visited.Contains(nei)) { + DFS(nei, adj, visited); + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n \log m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum value in the array. + +--- + +## 5. Sieve of Eratosthenes + BFS + +::tabs-start + +```python +class Solution: + def canTraverseAllPairs(self, nums: List[int]) -> bool: + N = len(nums) + if N == 1: + return True + if any(num == 1 for num in nums): + return False + + MAX = max(nums) + sieve = [0] * (MAX + 1) + p = 2 + while p * p <= MAX: + if sieve[p] == 0: + for composite in range(p * p, MAX + 1, p): + sieve[composite] = p + p += 1 + + adj = defaultdict(list) + for i in range(N): + num = nums[i] + if sieve[num] == 0: # num is prime + adj[i].append(N + num) + adj[N + num].append(i) + continue + + while num > 1: + prime = sieve[num] if sieve[num] != 0 else num + adj[i].append(N + prime) + adj[N + prime].append(i) + while num % prime == 0: + num //= prime + + visited = set() + queue = deque([0]) + visited.add(0) + while queue: + node = queue.popleft() + for nei in adj[node]: + if nei not in visited: + visited.add(nei) + queue.append(nei) + + for i in range(N): + if i not in visited: + return False + return True +``` + +```java +public class Solution { + public boolean canTraverseAllPairs(int[] nums) { + int N = nums.length; + if (N == 1) return true; + for (int num : nums) { + if (num == 1) return false; + } + + int MAX = Arrays.stream(nums).max().getAsInt(); + int[] sieve = new int[MAX + 1]; + int p = 2; + while (p * p <= MAX) { + if (sieve[p] == 0) { + for (int composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + p++; + } + + Map> adj = new HashMap<>(); + for (int i = 0; i < N; i++) { + int num = nums[i]; + if (sieve[num] == 0) { // num is prime + adj.computeIfAbsent(i, k -> new ArrayList<>()).add(N + num); + adj.computeIfAbsent(N + num, k -> new ArrayList<>()).add(i); + continue; + } + + while (num > 1) { + int prime = (sieve[num] != 0) ? sieve[num] : num; + adj.computeIfAbsent(i, k -> new ArrayList<>()).add(N + prime); + adj.computeIfAbsent(N + prime, k -> new ArrayList<>()).add(i); + while (num % prime == 0) { + num /= prime; + } + } + } + + Set visited = new HashSet<>(); + Queue queue = new LinkedList<>(); + queue.add(0); + visited.add(0); + + while (!queue.isEmpty()) { + int node = queue.poll(); + for (int nei : adj.getOrDefault(node, new ArrayList<>())) { + if (!visited.contains(nei)) { + visited.add(nei); + queue.add(nei); + } + } + } + + for (int i = 0; i < N; i++) { + if (!visited.contains(i)) { + return false; + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool canTraverseAllPairs(vector& nums) { + int N = nums.size(); + if (N == 1) return true; + if (find(nums.begin(), nums.end(), 1) != nums.end()) return false; + + int MAX = *max_element(nums.begin(), nums.end()); + vector sieve(MAX + 1, 0); + int p = 2; + while (p * p <= MAX) { + if (sieve[p] == 0) { + for (int composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + p++; + } + + unordered_map> adj; + for (int i = 0; i < N; i++) { + int num = nums[i]; + if (sieve[num] == 0) { // num is prime + adj[i].push_back(N + num); + adj[N + num].push_back(i); + continue; + } + + while (num > 1) { + int prime = (sieve[num] != 0) ? sieve[num] : num; + adj[i].push_back(N + prime); + adj[N + prime].push_back(i); + while (num % prime == 0) { + num /= prime; + } + } + } + + unordered_set visited; + queue q; + q.push(0); + visited.insert(0); + + while (!q.empty()) { + int node = q.front(); + q.pop(); + for (int& nei : adj[node]) { + if (visited.find(nei) == visited.end()) { + visited.insert(nei); + q.push(nei); + } + } + } + + for (int i = 0; i < N; i++) { + if (visited.find(i) == visited.end()) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + canTraverseAllPairs(nums) { + const N = nums.length; + if (N === 1) return true; + if (nums.includes(1)) return false; + + const MAX = Math.max(...nums); + const sieve = Array(MAX + 1).fill(0); + let p = 2; + while (p * p <= MAX) { + if (sieve[p] === 0) { + for (let composite = p * p; composite <= MAX; composite += p) { + sieve[composite] = p; + } + } + p++; + } + + const adj = new Map(); + for (let i = 0; i < N; i++) { + let num = nums[i]; + if (sieve[num] === 0) { // num is prime + if (!adj.has(i)) adj.set(i, []); + if (!adj.has(N + num)) adj.set(N + num, []); + adj.get(i).push(N + num); + adj.get(N + num).push(i); + continue; + } + + while (num > 1) { + const prime = sieve[num] !== 0 ? sieve[num] : num; + if (!adj.has(i)) adj.set(i, []); + if (!adj.has(N + prime)) adj.set(N + prime, []); + adj.get(i).push(N + prime); + adj.get(N + prime).push(i); + while (num % prime === 0) { + num = Math.floor(num / prime); + } + } + } + + const visited = new Set(); + const queue = new Queue([0]); + visited.add(0); + + while (!queue.isEmpty()) { + const node = queue.pop(); + if (adj.has(node)) { + for (const nei of adj.get(node)) { + if (!visited.has(nei)) { + visited.add(nei); + queue.push(nei); + } + } + } + } + + for (let i = 0; i < N; i++) { + if (!visited.has(i)) { + return false; + } + } + return true; + } +} +``` + +```csharp +public class Solution { + public bool CanTraverseAllPairs(int[] nums) { + int N = nums.Length; + if (N == 1) return true; + if (nums.Contains(1)) return false; + + int MAX = nums.Max(); + int[] sieve = new int[MAX + 1]; + for (int p = 2; p * p <= MAX; p++) { + if (sieve[p] == 0) { + for (int composite = p * p; composite <= MAX; composite += p) { + if (sieve[composite] == 0) { + sieve[composite] = p; + } + } + } + } + + Dictionary> adj = new Dictionary>(); + for (int i = 0; i < N; i++) { + int num = nums[i]; + if (sieve[num] == 0) { + AddEdge(adj, i, N + num); + continue; + } + + while (num > 1) { + int prime = sieve[num] != 0 ? sieve[num] : num; + AddEdge(adj, i, N + prime); + while (num % prime == 0) { + num /= prime; + } + } + } + + HashSet visited = new HashSet(); + Queue queue = new Queue(); + queue.Enqueue(0); + visited.Add(0); + + while (queue.Count > 0) { + int node = queue.Dequeue(); + if (!adj.ContainsKey(node)) continue; + foreach (int nei in adj[node]) { + if (!visited.Contains(nei)) { + visited.Add(nei); + queue.Enqueue(nei); + } + } + } + + for (int i = 0; i < N; i++) { + if (!visited.Contains(i)) return false; + } + + return true; + } + + private void AddEdge(Dictionary> adj, int u, int v) { + if (!adj.ContainsKey(u)) adj[u] = new List(); + if (!adj.ContainsKey(v)) adj[v] = new List(); + adj[u].Add(v); + adj[v].Add(u); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n \log m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum value in the array. \ No newline at end of file diff --git a/articles/grid-game.md b/articles/grid-game.md new file mode 100644 index 000000000..236f53012 --- /dev/null +++ b/articles/grid-game.md @@ -0,0 +1,354 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def gridGame(self, grid: List[List[int]]) -> int: + cols = len(grid[0]) + res = float('inf') + + top1 = 0 + for i in range(cols): + top1 += grid[0][i] + bottom1 = 0 + for j in range(i, cols): + bottom1 += grid[1][j] + + top2 = robot2 = 0 + for j in range(cols): + if j > i: + top2 += grid[0][j] + + bottom2 = 0 + for k in range(j, i): + bottom2 += grid[1][k] + robot2 = max(robot2, top2 + bottom2) + + res = min(res, robot2) + + return res +``` + +```java +public class Solution { + public long gridGame(int[][] grid) { + int cols = grid[0].length; + long res = Long.MAX_VALUE; + + long top1 = 0; + for (int i = 0; i < cols; i++) { + top1 += grid[0][i]; + long bottom1 = 0; + for (int j = i; j < cols; j++) { + bottom1 += grid[1][j]; + } + + long top2 = 0, robot2 = 0; + for (int j = 0; j < cols; j++) { + if (j > i) { + top2 += grid[0][j]; + } + + long bottom2 = 0; + for (int k = j; k < i; k++) { + bottom2 += grid[1][k]; + } + robot2 = Math.max(robot2, top2 + bottom2); + } + + res = Math.min(res, robot2); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long gridGame(vector>& grid) { + int cols = grid[0].size(); + long long res = LLONG_MAX; + + long long top1 = 0; + for (int i = 0; i < cols; i++) { + top1 += grid[0][i]; + long long bottom1 = 0; + for (int j = i; j < cols; j++) { + bottom1 += grid[1][j]; + } + + long long top2 = 0, robot2 = 0; + for (int j = 0; j < cols; j++) { + if (j > i) { + top2 += grid[0][j]; + } + + long long bottom2 = 0; + for (int k = j; k < i; k++) { + bottom2 += grid[1][k]; + } + robot2 = max(robot2, top2 + bottom2); + } + + res = min(res, robot2); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + gridGame(grid) { + let cols = grid[0].length; + let res = Infinity; + + let top1 = 0; + for (let i = 0; i < cols; i++) { + top1 += grid[0][i]; + let bottom1 = 0; + for (let j = i; j < cols; j++) { + bottom1 += grid[1][j]; + } + + let top2 = 0, robot2 = 0; + for (let j = 0; j < cols; j++) { + if (j > i) { + top2 += grid[0][j]; + } + + let bottom2 = 0; + for (let k = j; k < i; k++) { + bottom2 += grid[1][k]; + } + robot2 = Math.max(robot2, top2 + bottom2); + } + + res = Math.min(res, robot2); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(1)$ + +--- + +## 2. Prefix Sum + +::tabs-start + +```python +class Solution: + def gridGame(self, grid: List[List[int]]) -> int: + N = len(grid[0]) + preRow1, preRow2 = grid[0].copy(), grid[1].copy() + + for i in range(1, N): + preRow1[i] += preRow1[i - 1] + preRow2[i] += preRow2[i - 1] + + res = float("inf") + for i in range(N): + top = preRow1[-1] - preRow1[i] + bottom = preRow2[i - 1] if i > 0 else 0 + secondRobot = max(top, bottom) + res = min(res, secondRobot) + return res +``` + +```java +class Solution { + public long gridGame(int[][] grid) { + int N = grid[0].length; + long[] preRow1 = new long[N]; + long[] preRow2 = new long[N]; + for (int i = 0; i < N; i++) { + preRow1[i] = (long)grid[0][i]; + preRow2[i] = (long)grid[1][i]; + } + + for (int i = 1; i < N; i++) { + preRow1[i] += preRow1[i - 1]; + preRow2[i] += preRow2[i - 1]; + } + + long res = Long.MAX_VALUE; + for (int i = 0; i < N; i++) { + long top = preRow1[N - 1] - preRow1[i]; + long bottom = i > 0 ? preRow2[i - 1] : 0; + long secondRobot = Math.max(top, bottom); + res = Math.min(res, secondRobot); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + long long gridGame(vector>& grid) { + int N = grid[0].size(); + vector preRow1, preRow2; + for (int i = 0; i < N; i++) { + preRow1.push_back((long)grid[0][i]); + preRow2.push_back((long)grid[1][i]); + } + + for (int i = 1; i < N; i++) { + preRow1[i] += preRow1[i - 1]; + preRow2[i] += preRow2[i - 1]; + } + + long long res = LLONG_MAX; + for (int i = 0; i < N; i++) { + long long top = preRow1[N - 1] - preRow1[i]; + long long bottom = i > 0 ? preRow2[i - 1] : 0; + long long secondRobot = max(top, bottom); + res = min(res, secondRobot); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + gridGame(grid) { + const N = grid[0].length; + const preRow1 = [...grid[0]]; + const preRow2 = [...grid[1]]; + + for (let i = 1; i < N; i++) { + preRow1[i] += preRow1[i - 1]; + preRow2[i] += preRow2[i - 1]; + } + + let res = Infinity; + for (let i = 0; i < N; i++) { + const top = preRow1[N - 1] - preRow1[i]; + const bottom = i > 0 ? preRow2[i - 1] : 0; + const secondRobot = Math.max(top, bottom); + res = Math.min(res, secondRobot); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Prefix Sum (Space Optimized) + +::tabs-start + +```python +class Solution: + def gridGame(self, grid: List[List[int]]) -> int: + res = float("inf") + topSum = sum(grid[0]) + bottomSum = 0 + + for i in range(len(grid[0])): + topSum -= grid[0][i] + res = min(res, max(topSum, bottomSum)) + bottomSum += grid[1][i] + + return res +``` + +```java +public class Solution { + public long gridGame(int[][] grid) { + long res = Long.MAX_VALUE; + long topSum = 0, bottomSum = 0; + + for (int i = 0; i < grid[0].length; i++) { + topSum += grid[0][i]; + } + + for (int i = 0; i < grid[0].length; i++) { + topSum -= grid[0][i]; + res = Math.min(res, Math.max(topSum, bottomSum)); + bottomSum += grid[1][i]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long gridGame(vector>& grid) { + long long res = LLONG_MAX; + long long topSum = accumulate(grid[0].begin(), grid[0].end(), 0LL); + long long bottomSum = 0; + + for (int i = 0; i < grid[0].size(); i++) { + topSum -= grid[0][i]; + res = min(res, max(topSum, bottomSum)); + bottomSum += grid[1][i]; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + gridGame(grid) { + let res = Infinity; + let topSum = grid[0].reduce((a, b) => a + b, 0); + let bottomSum = 0; + + for (let i = 0; i < grid[0].length; i++) { + topSum -= grid[0][i]; + res = Math.min(res, Math.max(topSum, bottomSum)); + bottomSum += grid[1][i]; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/guess-number-higher-or-lower.md b/articles/guess-number-higher-or-lower.md new file mode 100644 index 000000000..4946e4968 --- /dev/null +++ b/articles/guess-number-higher-or-lower.md @@ -0,0 +1,427 @@ +## 1. Linear Search + +::tabs-start + +```python +# The guess API is already defined for you. +# @param num, your guess +# @return -1 if num is higher than the picked number +# 1 if num is lower than the picked number +# otherwise return 0 +# def guess(num: int) -> int: + +class Solution: + def guessNumber(self, n: int) -> int: + for num in range(1, n + 1): + if guess(num) == 0: + return num +``` + +```java +/** + * Forward declaration of guess API. + * @param num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * int guess(int num); + */ + +public class Solution extends GuessGame { + public int guessNumber(int n) { + for (int num = 1; num <= n; num++) { + if (guess(num) == 0) return num; + } + return n; + } +} +``` + +```cpp +/** + * Forward declaration of guess API. + * @param num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * int guess(int num); + */ + +class Solution { +public: + int guessNumber(int n) { + for (int num = 1; num <= n; num++) { + if (guess(num) == 0) return num; + } + return n; + } +}; +``` + +```javascript +/** + * Forward declaration of guess API. + * @param {number} num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * function guess(num) {} + */ + +class Solution { + /** + * @param {number} n + * @return {number} + */ + guessNumber(n) { + for (let num = 1; num <= n; num++) { + if (guess(num) === 0) return num; + } + return n; + } +} +``` + +```csharp +/** + * Forward declaration of guess API. + * @param num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * int guess(int num); + */ + +public class Solution : GuessGame { + public int GuessNumber(int n) { + for (int num = 1; num <= n; num++) { + if (guess(num) == 0) return num; + } + return n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Binary Search + +::tabs-start + +```python +# The guess API is already defined for you. +# @param num, your guess +# @return -1 if num is higher than the picked number +# 1 if num is lower than the picked number +# otherwise return 0 +# def guess(num: int) -> int: + +class Solution: + def guessNumber(self, n: int) -> int: + l, r = 1, n + while True: + m = (l + r) // 2 + res = guess(m) + if res > 0: + l = m + 1 + elif res < 0: + r = m - 1 + else: + return m +``` + +```java +/** + * Forward declaration of guess API. + * @param num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * int guess(int num); + */ + +public class Solution extends GuessGame { + public int guessNumber(int n) { + int l = 1, r = n; + while (true) { + int m = l + (r - l) / 2; + int res = guess(m); + if (res > 0) { + l = m + 1; + } else if (res < 0) { + r = m - 1; + } else { + return m; + } + } + } +} +``` + +```cpp +/** + * Forward declaration of guess API. + * @param num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * int guess(int num); + */ + +class Solution { +public: + int guessNumber(int n) { + int l = 1, r = n; + while (true) { + int m = l + (r - l) / 2; + int res = guess(m); + if (res > 0) { + l = m + 1; + } else if (res < 0) { + r = m - 1; + } else { + return m; + } + } + } +}; +``` + +```javascript +/** + * Forward declaration of guess API. + * @param {number} num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * function guess(num) {} + */ + +class Solution { + /** + * @param {number} n + * @return {number} + */ + guessNumber(n) { + let l = 1, r = n; + while (true) { + let m = Math.floor((l + r) / 2); + let res = guess(m); + if (res > 0) { + l = m + 1; + } else if (res < 0) { + r = m - 1; + } else { + return m; + } + } + } +} +``` + +```csharp +/** + * Forward declaration of guess API. + * @param num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * int guess(int num); + */ + +public class Solution : GuessGame { + public int GuessNumber(int n) { + for (int num = 1; num <= n; num++) { + if (guess(num) == 0) return num; + } + return n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Ternary Search + +::tabs-start + +```python +# The guess API is already defined for you. +# @param num, your guess +# @return -1 if num is higher than the picked number +# 1 if num is lower than the picked number +# otherwise return 0 +# def guess(num: int) -> int: + +class Solution: + def guessNumber(self, n: int) -> int: + l, r = 1, n + while True: + m1 = l + (r - l) // 3 + m2 = r - (r - l) // 3 + if guess(m1) == 0: + return m1 + if guess(m2) == 0: + return m2 + if guess(m1) + guess(m2) == 0: + l = m1 + 1 + r = m2 - 1 + elif guess(m1) == -1: + r = m1 - 1 + else: + l = m2 + 1 +``` + +```java +/** + * Forward declaration of guess API. + * @param num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * int guess(int num); + */ + +public class Solution extends GuessGame { + public int guessNumber(int n) { + int l = 1, r = n; + while (true) { + int m1 = l + (r - l) / 3; + int m2 = r - (r - l) / 3; + if (guess(m1) == 0) return m1; + if (guess(m2) == 0) return m2; + if (guess(m1) + guess(m2) == 0) { + l = m1 + 1; + r = m2 - 1; + } else if (guess(m1) == -1) { + r = m1 - 1; + } else { + l = m2 + 1; + } + } + } +} +``` + +```cpp +/** + * Forward declaration of guess API. + * @param num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * int guess(int num); + */ + +class Solution { +public: + int guessNumber(int n) { + int l = 1, r = n; + while (true) { + int m1 = l + (r - l) / 3; + int m2 = r - (r - l) / 3; + if (guess(m1) == 0) return m1; + if (guess(m2) == 0) return m2; + if (guess(m1) + guess(m2) == 0) { + l = m1 + 1; + r = m2 - 1; + } else if (guess(m1) == -1) { + r = m1 - 1; + } else { + l = m2 + 1; + } + } + } +}; +``` + +```javascript +/** + * Forward declaration of guess API. + * @param {number} num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * function guess(num) {} + */ + +class Solution { + /** + * @param {number} n + * @return {number} + */ + guessNumber(n) { + let l = 1, r = n; + while (true) { + let m1 = l + Math.floor((r - l) / 3); + let m2 = r - Math.floor((r - l) / 3); + if (guess(m1) === 0) return m1; + if (guess(m2) === 0) return m2; + if (guess(m1) + guess(m2) === 0) { + l = m1 + 1; + r = m2 - 1; + } else if (guess(m1) === -1) { + r = m1 - 1; + } else { + l = m2 + 1; + } + } + } +} +``` + +```csharp +/** + * Forward declaration of guess API. + * @param num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * int guess(int num); + */ + +public class Solution : GuessGame { + public int GuessNumber(int n) { + int l = 1, r = n; + while (true) { + int m1 = l + (r - l) / 3; + int m2 = r - (r - l) / 3; + + if (guess(m1) == 0) return m1; + if (guess(m2) == 0) return m2; + + if (guess(m1) + guess(m2) == 0) { + l = m1 + 1; + r = m2 - 1; + } + else if (guess(m1) == -1) { + r = m1 - 1; + } + else { + l = m2 + 1; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log_3 n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/hand-of-straights.md b/articles/hand-of-straights.md new file mode 100644 index 000000000..773278b86 --- /dev/null +++ b/articles/hand-of-straights.md @@ -0,0 +1,1022 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def isNStraightHand(self, hand, groupSize): + if len(hand) % groupSize: + return False + + count = Counter(hand) + hand.sort() + for num in hand: + if count[num]: + for i in range(num, num + groupSize): + if not count[i]: + return False + count[i] -= 1 + return True +``` + +```java +public class Solution { + public boolean isNStraightHand(int[] hand, int groupSize) { + if (hand.length % groupSize != 0) return false; + + Map count = new HashMap<>(); + for (int num : hand) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + Arrays.sort(hand); + for (int num : hand) { + if (count.get(num) > 0) { + for (int i = num; i < num + groupSize; i++) { + if (count.getOrDefault(i, 0) == 0) return false; + count.put(i, count.get(i) - 1); + } + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isNStraightHand(vector& hand, int groupSize) { + if (hand.size() % groupSize != 0) return false; + + unordered_map count; + for (int num : hand) count[num]++; + + sort(hand.begin(), hand.end()); + for (int num : hand) { + if (count[num] > 0) { + for (int i = num; i < num + groupSize; i++) { + if (count[i] == 0) return false; + count[i]--; + } + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} hand + * @param {number} groupSize + * @return {boolean} + */ + isNStraightHand(hand, groupSize) { + if (hand.length % groupSize !== 0) { + return false; + } + + const count = {}; + for (const num of hand) { + count[num] = (count[num] || 0) + 1; + } + + hand.sort((a, b) => a - b); + for (const num of hand) { + if (count[num] > 0) { + for (let i = num; i < num + groupSize; i++) { + if (!count[i]) return false; + count[i] -= 1; + } + } + } + return true; + } +} +``` + +```csharp +public class Solution { + public bool IsNStraightHand(int[] hand, int groupSize) { + if (hand.Length % groupSize != 0) return false; + + var count = new Dictionary(); + foreach (var num in hand) { + count[num] = count.GetValueOrDefault(num, 0) + 1; + } + + Array.Sort(hand); + foreach (var num in hand) { + if (count[num] > 0) { + for (int i = num; i < num + groupSize; i++) { + if (!count.ContainsKey(i) || count[i] == 0) { + return false; + } + count[i]--; + } + } + } + return true; + } +} +``` + +```go +func isNStraightHand(hand []int, groupSize int) bool { + if len(hand)%groupSize != 0 { + return false + } + + count := map[int]int{} + for _, num := range hand { + count[num]++ + } + + sort.Ints(hand) + for _, num := range hand { + if count[num] > 0 { + for i := num; i < num+groupSize; i++ { + if count[i] == 0 { + return false + } + count[i]-- + } + } + } + return true +} +``` + +```kotlin +class Solution { + fun isNStraightHand(hand: IntArray, groupSize: Int): Boolean { + if (hand.size % groupSize != 0) return false + + val count = HashMap() + hand.forEach { count[it] = count.getOrDefault(it, 0) + 1 } + hand.sort() + + for (num in hand) { + if (count[num]!! > 0) { + for (i in num until num + groupSize) { + if (count.getOrDefault(i, 0) == 0) { + return false + } + count[i] = count[i]!! - 1 + } + } + } + return true + } +} +``` + +```swift +class Solution { + func isNStraightHand(_ hand: [Int], _ groupSize: Int) -> Bool { + if hand.count % groupSize != 0 { + return false + } + + var count = [Int: Int]() + for num in hand { + count[num, default: 0] += 1 + } + + let sortedHand = hand.sorted() + for num in sortedHand { + if let freq = count[num], freq > 0 { + for i in num..<(num + groupSize) { + if let f = count[i], f > 0 { + count[i] = f - 1 + } else { + return false + } + } + } + } + + return true + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Heap + +::tabs-start + +```python +class Solution: + def isNStraightHand(self, hand: List[int], groupSize: int) -> bool: + if len(hand) % groupSize: + return False + + count = {} + for n in hand: + count[n] = 1 + count.get(n, 0) + + minH = list(count.keys()) + heapq.heapify(minH) + while minH: + first = minH[0] + for i in range(first, first + groupSize): + if i not in count: + return False + count[i] -= 1 + if count[i] == 0: + if i != minH[0]: + return False + heapq.heappop(minH) + return True +``` + +```java +public class Solution { + public boolean isNStraightHand(int[] hand, int groupSize) { + if (hand.length % groupSize != 0) + return false; + + Map count = new HashMap<>(); + for (int n : hand) + count.put(n, 1 + count.getOrDefault(n, 0)); + + PriorityQueue minH = new PriorityQueue<>(count.keySet()); + while (!minH.isEmpty()) { + int first = minH.peek(); + for (int i = first; i < first + groupSize; i++) { + if (!count.containsKey(i)) + return false; + count.put(i, count.get(i) - 1); + if (count.get(i) == 0) { + if (i != minH.peek()) + return false; + minH.poll(); + } + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isNStraightHand(vector& hand, int groupSize) { + if (hand.size() % groupSize != 0) + return false; + + unordered_map count; + for (int n : hand) + count[n] = 1 + count[n]; + + priority_queue, greater> minH; + for (auto& pair : count) + minH.push(pair.first); + + while (!minH.empty()) { + int first = minH.top(); + for (int i = first; i < first + groupSize; i++) { + if (count.find(i) == count.end()) + return false; + count[i] -= 1; + if (count[i] == 0) { + if (i != minH.top()) + return false; + minH.pop(); + } + } + } + return true; + } +}; +``` + +```javascript +/** + * const { MinPriorityQueue } = require('@datastructures-js/priority-queue'); + */ + +class Solution { + /** + * @param {number[]} hand + * @param {number} groupSize + * @return {boolean} + */ + isNStraightHand(hand, groupSize) { + if (hand.length % groupSize !== 0) { + return false; + } + + const count = {}; + for (const n of hand) { + count[n] = (count[n] || 0) + 1; + } + + const minPQ = new MinPriorityQueue(); + for (const key in count) { + minPQ.push(Number(key)); + } + + while (!minPQ.isEmpty()) { + const first = minPQ.front(); + for (let i = first; i < first + groupSize; i++) { + if (!(i in count) || count[i] === 0) { + return false; + } + count[i] -= 1; + if (count[i] === 0) { + if (i !== minPQ.front()) { + return false; + } + minPQ.pop(); + } + } + } + return true; + } +} +``` + +```csharp +public class Solution { + public bool IsNStraightHand(int[] hand, int groupSize) { + if (hand.Length % groupSize != 0) + return false; + + var count = new Dictionary(); + foreach (int num in hand) { + if (count.ContainsKey(num)) + count[num]++; + else + count[num] = 1; + } + + var minH = new PriorityQueue(); + foreach (var num in count.Keys) + minH.Enqueue(num, num); + + while (minH.Count > 0) { + int first = minH.Peek(); + for (int i = first; i < first + groupSize; i++) { + if (!count.ContainsKey(i) || count[i] == 0) + return false; + + count[i]--; + if (count[i] == 0) { + if (i != minH.Peek()) + return false; + minH.Dequeue(); + } + } + } + return true; + } +} +``` + +```go +func isNStraightHand(hand []int, groupSize int) bool { + if len(hand)%groupSize != 0 { + return false + } + + count := map[int]int{} + for _, n := range hand { + count[n]++ + } + + minH := priorityqueue.NewWith(utils.IntComparator) + for k := range count { + minH.Enqueue(k) + } + + for !minH.Empty() { + first, _ := minH.Peek() + firstKey := first.(int) + for i := firstKey; i < firstKey+groupSize; i++ { + if count[i] == 0 { + return false + } + count[i]-- + if count[i] == 0 { + if val, _ := minH.Peek(); val.(int) != i { + return false + } + minH.Dequeue() + } + } + } + return true +} +``` + +```kotlin +class Solution { + fun isNStraightHand(hand: IntArray, groupSize: Int): Boolean { + if (hand.size % groupSize != 0) return false + + val count = mutableMapOf() + for (n in hand) { + count[n] = count.getOrDefault(n, 0) + 1 + } + + val minH = PriorityQueue(count.keys) + while (minH.isNotEmpty()) { + val first = minH.peek() + for (i in first until first + groupSize) { + if (count.getOrDefault(i, 0) == 0) return false + count[i] = count[i]!! - 1 + if (count[i] == 0) { + if (i != minH.peek()) return false + minH.poll() + } + } + } + return true + } +} +``` + +```swift +class Solution { + func isNStraightHand(_ hand: [Int], _ groupSize: Int) -> Bool { + if hand.count % groupSize != 0 { + return false + } + + var count = [Int: Int]() + for n in hand { + count[n, default: 0] += 1 + } + + var minH = Heap(Array(count.keys)) + + while !minH.isEmpty { + guard let first = minH.min else { return false } + + for i in first..<(first + groupSize) { + guard let freq = count[i] else { return false } + count[i] = freq - 1 + if count[i] == 0 { + if i != minH.min { + return false + } + minH.removeMin() + } + } + } + + return true + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Ordered Map + +::tabs-start + +```python +class Solution: + def isNStraightHand(self, hand, groupSize): + if len(hand) % groupSize != 0: + return False + + count = Counter(hand) + q = deque() + last_num, open_groups = -1, 0 + + for num in sorted(count): + if ((open_groups > 0 and num > last_num + 1) or + open_groups > count[num] + ): + return False + + q.append(count[num] - open_groups) + last_num = num + open_groups = count[num] + + if len(q) == groupSize: + open_groups -= q.popleft() + + return open_groups == 0 +``` + +```java +public class Solution { + public boolean isNStraightHand(int[] hand, int groupSize) { + if (hand.length % groupSize != 0) return false; + + Map count = new TreeMap<>(); + for (int num : hand) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + Queue q = new LinkedList<>(); + int lastNum = -1, openGroups = 0; + + for (int num : count.keySet()) { + if ((openGroups > 0 && num > lastNum + 1) || + openGroups > count.get(num)) { + return false; + } + + q.add(count.get(num) - openGroups); + lastNum = num; + openGroups = count.get(num); + + if (q.size() == groupSize) { + openGroups -= q.poll(); + } + } + return openGroups == 0; + } +} +``` + +```cpp +class Solution { +public: + bool isNStraightHand(vector& hand, int groupSize) { + if (hand.size() % groupSize != 0) return false; + + map count; + for (int num : hand) count[num]++; + + queue q; + int lastNum = -1, openGroups = 0; + + for (auto& entry : count) { + int num = entry.first; + if ((openGroups > 0 && num > lastNum + 1) || + openGroups > count[num]) { + return false; + } + + q.push(count[num] - openGroups); + lastNum = num; + openGroups = count[num]; + + if (q.size() == groupSize) { + openGroups -= q.front(); + q.pop(); + } + } + return openGroups == 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} hand + * @param {number} groupSize + * @return {boolean} + */ + isNStraightHand(hand, groupSize) { + if (hand.length % groupSize !== 0) return false; + + let count = new Map(); + hand.forEach(num => count.set(num, (count.get(num) || 0) + 1)); + + let q = new Queue(); + let lastNum = -1, openGroups = 0; + + Array.from(count.keys()).sort((a, b) => a - b).forEach(num => { + if ((openGroups > 0 && num > lastNum + 1) || + openGroups > count.get(num)) { + return false; + } + + q.push(count.get(num) - openGroups); + lastNum = num; + openGroups = count.get(num); + + if (q.size() === groupSize) { + openGroups -= q.pop(); + } + }); + + return openGroups === 0; + } +} +``` + +```csharp +public class Solution { + public bool IsNStraightHand(int[] hand, int groupSize) { + if (hand.Length % groupSize != 0) return false; + + var count = new SortedDictionary(); + foreach (int num in hand) { + if (!count.ContainsKey(num)) count[num] = 0; + count[num]++; + } + + var q = new Queue(); + int lastNum = -1, openGroups = 0; + + foreach (int num in count.Keys) { + if ((openGroups > 0 && num > lastNum + 1) || + openGroups > count[num]) { + return false; + } + + q.Enqueue(count[num] - openGroups); + lastNum = num; + openGroups = count[num]; + + if (q.Count == groupSize) { + openGroups -= q.Dequeue(); + } + } + return openGroups == 0; + } +} +``` + +```go +func isNStraightHand(hand []int, groupSize int) bool { + if len(hand)%groupSize != 0 { + return false + } + + count := make(map[int]int) + for _, num := range hand { + count[num]++ + } + + keys := make([]int, 0, len(count)) + for k := range count { + keys = append(keys, k) + } + sort.Ints(keys) + + q := []int{} + lastNum, openGroups := -1, 0 + + for _, num := range keys { + if (openGroups > 0 && num > lastNum+1) || + openGroups > count[num] { + return false + } + + q = append(q, count[num]-openGroups) + lastNum = num + openGroups = count[num] + + if len(q) == groupSize { + openGroups -= q[0] + q = q[1:] + } + } + return openGroups == 0 +} +``` + +```kotlin +class Solution { + fun isNStraightHand(hand: IntArray, groupSize: Int): Boolean { + if (hand.size % groupSize != 0) return false + + val count = TreeMap() + for (num in hand) { + count[num] = count.getOrDefault(num, 0) + 1 + } + + val q: Queue = LinkedList() + var lastNum = -1 + var openGroups = 0 + + for (num in count.keys) { + if ((openGroups > 0 && num > lastNum + 1) || + openGroups > count[num]!!) { + return false + } + + q.add(count[num]!! - openGroups) + lastNum = num + openGroups = count[num]!! + + if (q.size == groupSize) { + openGroups -= q.poll() + } + } + return openGroups == 0 + } +} +``` + +```swift +class Solution { + func isNStraightHand(_ hand: [Int], _ groupSize: Int) -> Bool { + if hand.count % groupSize != 0 { + return false + } + + var count = [Int: Int]() + for num in hand { + count[num, default: 0] += 1 + } + + var queue = Deque() + var lastNum = -1 + var openGroups = 0 + + for (num, numCount) in count.sorted(by: { $0.key < $1.key }) { + if (openGroups > 0 && num > lastNum + 1) || openGroups > numCount { + return false + } + + queue.append(numCount - openGroups) + lastNum = num + openGroups = numCount + + if queue.count == groupSize { + openGroups -= queue.removeFirst() + } + } + return openGroups == 0 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Hash Map + +::tabs-start + +```python +class Solution: + def isNStraightHand(self, hand: List[int], groupSize: int) -> bool: + if len(hand) % groupSize != 0: + return False + + count = Counter(hand) + for num in hand: + start = num + while count[start - 1]: + start -= 1 + while start <= num: + while count[start]: + for i in range(start, start + groupSize): + if not count[i]: + return False + count[i] -= 1 + start += 1 + return True +``` + +```java +public class Solution { + public boolean isNStraightHand(int[] hand, int groupSize) { + if (hand.length % groupSize != 0) return false; + + Map count = new HashMap<>(); + for (int num : hand) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + for (int num : hand) { + int start = num; + while (count.getOrDefault(start - 1, 0) > 0) start--; + while (start <= num) { + while (count.getOrDefault(start, 0) > 0) { + for (int i = start; i < start + groupSize; i++) { + if (count.getOrDefault(i, 0) == 0) return false; + count.put(i, count.get(i) - 1); + } + } + start++; + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isNStraightHand(vector& hand, int groupSize) { + if (hand.size() % groupSize != 0) return false; + + unordered_map count; + for (int num : hand) count[num]++; + + for (int num : hand) { + int start = num; + while (count[start - 1] > 0) start--; + while (start <= num) { + while (count[start] > 0) { + for (int i = start; i < start + groupSize; i++) { + if (count[i] == 0) return false; + count[i]--; + } + } + start++; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} hand + * @param {number} groupSize + * @return {boolean} + */ + isNStraightHand(hand, groupSize) { + if (hand.length % groupSize !== 0) return false; + + const count = new Map(); + hand.forEach(num => count.set(num, (count.get(num) || 0) + 1)); + + for (const num of hand) { + let start = num; + while (count.get(start - 1) > 0) start--; + while (start <= num) { + while (count.get(start) > 0) { + for (let i = start; i < start + groupSize; i++) { + if (!count.get(i)) return false; + count.set(i, count.get(i) - 1); + } + } + start++; + } + } + return true; + } +} +``` + +```csharp +public class Solution { + public bool IsNStraightHand(int[] hand, int groupSize) { + if (hand.Length % groupSize != 0) return false; + + Dictionary count = new Dictionary(); + foreach (int num in hand) { + if (!count.ContainsKey(num)) count[num] = 0; + count[num]++; + } + + foreach (int num in hand) { + int start = num; + while (count.ContainsKey(start - 1) && count[start - 1] > 0) { + start--; + } + while (start <= num) { + while (count.ContainsKey(start) && count[start] > 0) { + for (int i = start; i < start + groupSize; i++) { + if (!count.ContainsKey(i) || count[i] == 0) { + return false; + } + count[i]--; + } + } + start++; + } + } + return true; + } +} +``` + +```go +func isNStraightHand(hand []int, groupSize int) bool { + if len(hand)%groupSize != 0 { + return false + } + + count := map[int]int{} + for _, num := range hand { + count[num]++ + } + + for _, num := range hand { + start := num + for count[start-1] > 0 { + start-- + } + + for start <= num { + for count[start] > 0 { + for i := start; i < start+groupSize; i++ { + if count[i] == 0 { + return false + } + count[i]-- + } + } + start++ + } + } + return true +} +``` + +```kotlin +class Solution { + fun isNStraightHand(hand: IntArray, groupSize: Int): Boolean { + if (hand.size % groupSize != 0) return false + + val count = HashMap() + hand.forEach { count[it] = count.getOrDefault(it, 0) + 1 } + + for (num in hand) { + var start = num + while (count.getOrDefault(start - 1, 0) > 0) { + start-- + } + + while (start <= num) { + while (count.getOrDefault(start, 0) > 0) { + for (i in start until start + groupSize) { + if (count.getOrDefault(i, 0) == 0) { + return false + } + count[i] = count[i]!! - 1 + } + } + start++ + } + } + return true + } +} +``` + +```swift +class Solution { + func isNStraightHand(_ hand: [Int], _ groupSize: Int) -> Bool { + if hand.count % groupSize != 0 { + return false + } + + var count = [Int: Int]() + for num in hand { + count[num, default: 0] += 1 + } + + for num in hand { + var start = num + while (count[start - 1] ?? 0) > 0 { + start -= 1 + } + while start <= num { + while (count[start] ?? 0) > 0 { + for i in start..<(start + groupSize) { + if (count[i] ?? 0) == 0 { + return false + } + count[i]! -= 1 + } + } + start += 1 + } + } + + return true + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/height-checker.md b/articles/height-checker.md new file mode 100644 index 000000000..1e0919676 --- /dev/null +++ b/articles/height-checker.md @@ -0,0 +1,206 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def heightChecker(self, heights: List[int]) -> int: + expected = sorted(heights) + + res = 0 + for i in range(len(heights)): + if heights[i] != expected[i]: + res += 1 + + return res +``` + +```java +public class Solution { + public int heightChecker(int[] heights) { + int[] expected = Arrays.copyOf(heights, heights.length); + Arrays.sort(expected); + + int res = 0; + for (int i = 0; i < heights.length; i++) { + if (heights[i] != expected[i]) { + res++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int heightChecker(vector& heights) { + vector expected = heights; + sort(expected.begin(), expected.end()); + + int res = 0; + for (int i = 0; i < heights.size(); i++) { + if (heights[i] != expected[i]) { + res++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @return {number} + */ + heightChecker(heights) { + const expected = [...heights].sort((a, b) => a - b); + + let res = 0; + for (let i = 0; i < heights.length; i++) { + if (heights[i] !== expected[i]) { + res++; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Counting Sort + +::tabs-start + +```python +class Solution: + def heightChecker(self, heights: List[int]) -> int: + count = [0] * 101 + for h in heights: + count[h] += 1 + + expected = [] + for h in range(1, 101): + c = count[h] + for _ in range(c): + expected.append(h) + + res = 0 + for i in range(len(heights)): + if heights[i] != expected[i]: + res += 1 + + return res +``` + +```java +public class Solution { + public int heightChecker(int[] heights) { + int[] count = new int[101]; + for (int h : heights) { + count[h]++; + } + + List expected = new ArrayList<>(); + for (int h = 1; h <= 100; h++) { + int c = count[h]; + for (int i = 0; i < c; i++) { + expected.add(h); + } + } + + int res = 0; + for (int i = 0; i < heights.length; i++) { + if (heights[i] != expected.get(i)) { + res++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int heightChecker(vector& heights) { + int count[101] = {}; + for (int h : heights) { + count[h]++; + } + + vector expected; + for (int h = 1; h <= 100; h++) { + int c = count[h]; + for (int i = 0; i < c; i++) { + expected.push_back(h); + } + } + + int res = 0; + for (int i = 0; i < heights.size(); i++) { + if (heights[i] != expected[i]) { + res++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @return {number} + */ + heightChecker(heights) { + const count = new Array(101).fill(0); + for (let h of heights) { + count[h]++; + } + + const expected = []; + for (let h = 1; h <= 100; h++) { + let c = count[h]; + for (let i = 0; i < c; i++) { + expected.push(h); + } + } + + let res = 0; + for (let i = 0; i < heights.length; i++) { + if (heights[i] !== expected[i]) { + res++; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + k)$ +* Space complexity: $O(n + k)$ + +> Where $n$ is the size of the input array, and $k$ is the range of numbers. \ No newline at end of file diff --git a/articles/house-robber-ii.md b/articles/house-robber-ii.md new file mode 100644 index 000000000..0469d73df --- /dev/null +++ b/articles/house-robber-ii.md @@ -0,0 +1,852 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def rob(self, nums: List[int]) -> int: + if len(nums) == 1: + return nums[0] + def dfs(i, flag): + if i >= len(nums) or (flag and i == len(nums) - 1): + return 0 + + return max(dfs(i + 1, flag), + nums[i] + dfs(i + 2, flag or i == 0)) + return max(dfs(0, True), dfs(1, False)) +``` + +```java +public class Solution { + public int rob(int[] nums) { + if (nums.length == 1) return nums[0]; + return Math.max(dfs(0, true, nums), dfs(1, false, nums)); + } + + private int dfs(int i, boolean flag, int[] nums) { + if (i >= nums.length || (flag && i == nums.length - 1)) + return 0; + + return Math.max(dfs(i + 1, flag, nums), + nums[i] + dfs(i + 2, flag || i == 0, nums)); + } +} +``` + +```cpp +class Solution { +public: + int rob(vector& nums) { + if (nums.size() == 1) return nums[0]; + return max(dfs(0, true, nums), dfs(1, false, nums)); + } + +private: + int dfs(int i, bool flag, vector& nums) { + if (i >= nums.size() || (flag && i == nums.size() - 1)) + return 0; + + return max(dfs(i + 1, flag, nums), + nums[i] + dfs(i + 2, flag || i == 0, nums)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + rob(nums) { + if (nums.length === 1) return nums[0]; + + const dfs = (i, flag) => { + if (i >= nums.length || (flag && i === nums.length - 1)) + return 0; + + return Math.max(dfs(i + 1, flag), + nums[i] + dfs(i + 2, flag || i === 0)); + } + + return Math.max(dfs(0, true), dfs(1, false)); + } +} +``` + +```csharp +public class Solution { + public int Rob(int[] nums) { + if (nums.Length == 1) return nums[0]; + return Math.Max(Dfs(0, true, nums), Dfs(1, false, nums)); + } + + private int Dfs(int i, bool flag, int[] nums) { + if (i >= nums.Length || (flag && i == nums.Length - 1)) + return 0; + + return Math.Max(Dfs(i + 1, flag, nums), + nums[i] + Dfs(i + 2, flag || i == 0, nums)); + } +} +``` + +```go +func rob(nums []int) int { + if len(nums) == 1 { + return nums[0] + } + var dfs func(i int, flag bool) int + dfs = func(i int, flag bool) int { + if i >= len(nums) || (flag && i == len(nums)-1) { + return 0 + } + return max(dfs(i+1, flag), nums[i] + dfs(i+2, flag || i == 0)) + } + return max(dfs(0, true), dfs(1, false)) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun rob(nums: IntArray): Int { + val n = nums.size + if (n == 1) return nums[0] + fun dfs(i: Int, flag: Boolean): Int { + if (i >= nums.size || (flag && i == nums.size - 1)) { + return 0 + } + return maxOf(dfs(i + 1, flag), + nums[i] + dfs(i + 2, flag || i == 0)) + } + return maxOf(dfs(0, true), dfs(1, false)) + } +} +``` + +```swift +class Solution { + func rob(_ nums: [Int]) -> Int { + if nums.count == 1 { + return nums[0] + } + + func dfs(_ i: Int, _ flag: Bool) -> Int { + if i >= nums.count || (flag && i == nums.count - 1) { + return 0 + } + return max(dfs(i + 1, flag), nums[i] + dfs(i + 2, flag || i == 0)) + } + + return max(dfs(0, true), dfs(1, false)) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def rob(self, nums: List[int]) -> int: + if len(nums) == 1: + return nums[0] + + memo = [[-1] * 2 for _ in range(len(nums))] + + def dfs(i, flag): + if i >= len(nums) or (flag and i == len(nums) - 1): + return 0 + if memo[i][flag] != -1: + return memo[i][flag] + memo[i][flag] = max(dfs(i + 1, flag), + nums[i] + dfs(i + 2, flag or (i == 0))) + return memo[i][flag] + + return max(dfs(0, True), dfs(1, False)) +``` + +```java +public class Solution { + private int[][] memo; + + public int rob(int[] nums) { + if (nums.length == 1) return nums[0]; + + memo = new int[nums.length][2]; + for (int i = 0; i < nums.length; i++) { + memo[i][0] = -1; + memo[i][1] = -1; + } + + return Math.max(dfs(0, 1, nums), dfs(1, 0, nums)); + } + + private int dfs(int i, int flag, int[] nums) { + if (i >= nums.length || (flag == 1 && i == nums.length - 1)) + return 0; + if (memo[i][flag] != -1) + return memo[i][flag]; + memo[i][flag] = Math.max(dfs(i + 1, flag, nums), + nums[i] + dfs(i + 2, flag | (i == 0 ? 1 : 0), nums)); + return memo[i][flag]; + } +} +``` + +```cpp +class Solution { + vector> memo; + +public: + int rob(vector& nums) { + if (nums.size() == 1) return nums[0]; + + memo.resize(nums.size(), vector(2, -1)); + return max(dfs(0, 1, nums), dfs(1, 0, nums)); + } + +private: + int dfs(int i, int flag, vector& nums) { + if (i >= nums.size() || (flag == 1 && i == nums.size() - 1)) + return 0; + if (memo[i][flag] != -1) + return memo[i][flag]; + memo[i][flag] = max(dfs(i + 1, flag, nums), + nums[i] + dfs(i + 2, flag | (i == 0 ? 1 : 0), nums)); + return memo[i][flag]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + rob(nums) { + if (nums.length === 1) return nums[0]; + + const n = nums.length; + const memo = Array.from({ length: n }, () => Array(2).fill(-1)); + + const dfs = (i, flag) => { + if (i >= n || (flag && (i === n - 1))) + return 0; + if (memo[i][flag] !== -1) + return memo[i][flag]; + + memo[i][flag] = Math.max( + dfs(i + 1, flag), + nums[i] + dfs(i + 2, flag | (i === 0)) + ); + return memo[i][flag]; + } + + return Math.max(dfs(0, 1), dfs(1, 0)); + } +} +``` + +```csharp +public class Solution { + private int[][] memo; + + public int Rob(int[] nums) { + if (nums.Length == 1) return nums[0]; + + memo = new int[nums.Length][]; + for (int i = 0; i < nums.Length; i++) { + memo[i] = new int[] { -1, -1 }; + } + + return Math.Max(Dfs(0, 1, nums), Dfs(1, 0, nums)); + } + + private int Dfs(int i, int flag, int[] nums) { + if (i >= nums.Length || (flag == 1 && i == nums.Length - 1)) + return 0; + if (memo[i][flag] != -1) + return memo[i][flag]; + memo[i][flag] = Math.Max(Dfs(i + 1, flag, nums), + nums[i] + Dfs(i + 2, flag | (i == 0 ? 1 : 0), nums)); + return memo[i][flag]; + } +} +``` + +```go +func rob(nums []int) int { + if len(nums) == 1 { + return nums[0] + } + memo := make([][2]int, len(nums)) + for i := range memo { + memo[i][0] = -1 + memo[i][1] = -1 + } + + var dfs func(i, flag int) int + dfs = func(i, flag int) int { + if i >= len(nums) || (flag == 1 && i == len(nums)-1) { + return 0 + } + if memo[i][flag] != -1 { + return memo[i][flag] + } + + tmp := flag + if i == 0 { + tmp = 1 + } + memo[i][flag] = max(dfs(i+1, flag), nums[i] + dfs(i+2, tmp)) + return memo[i][flag] + } + + return max(dfs(0, 1), dfs(1, 0)) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun rob(nums: IntArray): Int { + if (nums.size == 1) return nums[0] + val memo = Array(nums.size) { IntArray(2) { -1 } } + + fun dfs(i: Int, flag: Int): Int { + if (i >= nums.size || (flag == 1 && i == nums.size - 1)) { + return 0 + } + if (memo[i][flag] != -1) { + return memo[i][flag] + } + memo[i][flag] = maxOf(dfs(i + 1, flag), + nums[i] + dfs(i + 2, flag or if (i == 0) 1 else 0)) + return memo[i][flag] + } + + return maxOf(dfs(0, 1), dfs(1, 0)) + } +} +``` + +```swift +class Solution { + func rob(_ nums: [Int]) -> Int { + if nums.count == 1 { + return nums[0] + } + + var memo = Array(repeating: Array(repeating: -1, count: 2), count: nums.count) + + func dfs(_ i: Int, _ flag: Bool) -> Int { + if i >= nums.count || (flag && i == nums.count - 1) { + return 0 + } + if memo[i][flag ? 1 : 0] != -1 { + return memo[i][flag ? 1 : 0] + } + + memo[i][flag ? 1 : 0] = max( + dfs(i + 1, flag), nums[i] + dfs(i + 2, flag || i == 0) + ) + + return memo[i][flag ? 1 : 0] + } + + return max(dfs(0, true), dfs(1, false)) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def rob(self, nums: List[int]) -> int: + if len(nums) == 1: + return nums[0] + return max(self.helper(nums[1:]), + self.helper(nums[:-1])) + + def helper(self, nums: List[int]) -> int: + if not nums: + return 0 + if len(nums) == 1: + return nums[0] + + dp = [0] * len(nums) + dp[0] = nums[0] + dp[1] = max(nums[0], nums[1]) + + for i in range(2, len(nums)): + dp[i] = max(dp[i - 1], nums[i] + dp[i - 2]) + + return dp[-1] +``` + +```java +public class Solution { + public int rob(int[] nums) { + if (nums.length == 1) return nums[0]; + + return Math.max(helper(Arrays.copyOfRange(nums, 1, nums.length)), + helper(Arrays.copyOfRange(nums, 0, nums.length - 1))); + } + + private int helper(int[] nums) { + if (nums.length == 0) return 0; + if (nums.length == 1) return nums[0]; + + int[] dp = new int[nums.length]; + dp[0] = nums[0]; + dp[1] = Math.max(nums[0], nums[1]); + + for (int i = 2; i < nums.length; i++) { + dp[i] = Math.max(dp[i - 1], nums[i] + dp[i - 2]); + } + + return dp[nums.length - 1]; + } +} +``` + +```cpp +class Solution { +public: + int rob(std::vector& nums) { + if (nums.size() == 1) return nums[0]; + + return max(helper(vector(nums.begin() + 1, nums.end())), + helper(vector(nums.begin(), nums.end() - 1))); + } + + int helper(vector nums) { + if (nums.empty()) return 0; + if (nums.size() == 1) return nums[0]; + + vector dp(nums.size()); + dp[0] = nums[0]; + dp[1] = max(nums[0], nums[1]); + + for (int i = 2; i < nums.size(); i++) { + dp[i] = max(dp[i - 1], nums[i] + dp[i - 2]); + } + + return dp.back(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + rob(nums) { + if (nums.length === 0) return 0; + if (nums.length === 1) return nums[0]; + + return Math.max(this.helper(nums.slice(1)), + this.helper(nums.slice(0, -1))); + } + + /** + * @param {number[]} nums + * @return {number} + */ + helper(nums) { + if (nums.length === 0) return 0; + if (nums.length === 1) return nums[0]; + + const dp = new Array(nums.length); + dp[0] = nums[0]; + dp[1] = Math.max(nums[0], nums[1]); + + for (let i = 2; i < nums.length; i++) { + dp[i] = Math.max(dp[i - 1], nums[i] + dp[i - 2]); + } + + return dp[nums.length - 1]; + } +} +``` + +```csharp +public class Solution { + public int Rob(int[] nums) { + if (nums.Length == 0) return 0; + if (nums.Length == 1) return nums[0]; + + return Math.Max(Helper(nums[1..]), Helper(nums[..^1])); + } + + private int Helper(int[] nums) { + if (nums.Length == 0) return 0; + if (nums.Length == 1) return nums[0]; + + int[] dp = new int[nums.Length]; + dp[0] = nums[0]; + dp[1] = Math.Max(nums[0], nums[1]); + + for (int i = 2; i < nums.Length; i++) { + dp[i] = Math.Max(dp[i - 1], nums[i] + dp[i - 2]); + } + + return dp[^1]; + } +} +``` + +```go +func rob(nums []int) int { + if len(nums) == 1 { + return nums[0] + } + return max(helper(nums[1:]), helper(nums[:len(nums)-1])) +} + +func helper(nums []int) int { + if len(nums) == 0 { + return 0 + } + if len(nums) == 1 { + return nums[0] + } + + dp := make([]int, len(nums)) + dp[0] = nums[0] + dp[1] = max(nums[0], nums[1]) + + for i := 2; i < len(nums); i++ { + dp[i] = max(dp[i-1], nums[i] + dp[i-2]) + } + + return dp[len(nums)-1] +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun rob(nums: IntArray): Int { + if (nums.size == 1) { + return nums[0] + } + return max(helper(nums.copyOfRange(1, nums.size)), + helper(nums.copyOfRange(0, nums.size - 1))) + } + + fun helper(nums: IntArray): Int { + if (nums.isEmpty()) { + return 0 + } + if (nums.size == 1) { + return nums[0] + } + + val dp = IntArray(nums.size) + dp[0] = nums[0] + dp[1] = maxOf(nums[0], nums[1]) + + for (i in 2 until nums.size) { + dp[i] = maxOf(dp[i - 1], nums[i] + dp[i - 2]) + } + + return dp[nums.size - 1] + } +} +``` + +```swift +class Solution { + func rob(_ nums: [Int]) -> Int { + if nums.count == 1 { + return nums[0] + } + return max(helper(Array(nums[1...])), helper(Array(nums[..<(nums.count - 1)]))) + } + + func helper(_ nums: [Int]) -> Int { + if nums.isEmpty { + return 0 + } + if nums.count == 1 { + return nums[0] + } + + var dp = [Int](repeating: 0, count: nums.count) + dp[0] = nums[0] + dp[1] = max(nums[0], nums[1]) + + for i in 2.. int: + return max(nums[0], self.helper(nums[1:]), + self.helper(nums[:-1])) + + def helper(self, nums): + rob1, rob2 = 0, 0 + + for num in nums: + newRob = max(rob1 + num, rob2) + rob1 = rob2 + rob2 = newRob + return rob2 +``` + +```java +public class Solution { + + public int rob(int[] nums) { + return Math.max(nums[0], + Math.max(helper(Arrays.copyOfRange(nums, 1, nums.length)), + helper(Arrays.copyOfRange(nums, 0, nums.length - 1)))); + } + + private int helper(int[] nums) { + int rob1 = 0, rob2 = 0; + + for (int num : nums) { + int newRob = Math.max(rob1 + num, rob2); + rob1 = rob2; + rob2 = newRob; + } + return rob2; + } +} +``` + +```cpp +class Solution { +public: + int rob(vector& nums) { + vector nums1(nums.begin() + 1, nums.end()); + vector nums2(nums.begin(), nums.end() - 1); + return max(nums[0], + max(helper(nums1), helper(nums2))); + } + +private: + int helper(vector& nums) { + int rob1 = 0, rob2 = 0; + for (int num : nums) { + int newRob = max(rob1 + num, rob2); + rob1 = rob2; + rob2 = newRob; + } + return rob2; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + rob(nums) { + return Math.max( + nums[0], + Math.max( + this.helper(nums.slice(1)), + this.helper(nums.slice(0, -1)), + ), + ); + } + + /** + * @param {number[]} nums + * @return {number} + */ + helper(nums) { + let rob1 = 0; + let rob2 = 0; + for (const num of nums) { + const newRob = Math.max(rob1 + num, rob2); + rob1 = rob2; + rob2 = newRob; + } + return rob2; + } +} +``` + +```csharp +public class Solution { + + public int Rob(int[] nums) { + if (nums.Length == 1) + return nums[0]; + + return Math.Max(Helper(nums[1..]), + Helper(nums[..^1])); + } + + private int Helper(int[] nums) { + int rob1 = 0, rob2 = 0; + foreach (int num in nums) { + int newRob = Math.Max(rob1 + num, rob2); + rob1 = rob2; + rob2 = newRob; + } + return rob2; + } +} +``` + +```go +func rob(nums []int) int { + if len(nums) == 0 { + return 0 + } + return max(nums[0], max(helper(nums[1:]), + helper(nums[:len(nums)-1]))) +} + +func helper(nums []int) int { + rob1, rob2 := 0, 0 + + for _, num := range nums { + newRob := max(rob1+num, rob2) + rob1 = rob2 + rob2 = newRob + } + return rob2 +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun rob(nums: IntArray): Int { + return maxOf(nums[0], + maxOf(helper(nums.copyOfRange(1, nums.size)), + helper(nums.copyOfRange(0, nums.size - 1)))) + } + + private fun helper(nums: IntArray): Int { + var rob1 = 0 + var rob2 = 0 + + for (num in nums) { + val newRob = maxOf(rob1 + num, rob2) + rob1 = rob2 + rob2 = newRob + } + return rob2 + } +} +``` + +```swift +class Solution { + func rob(_ nums: [Int]) -> Int { + if nums.isEmpty { + return 0 + } + if nums.count == 1 { + return nums[0] + } + + let candidate1 = nums[0] + let candidate2 = helper(Array(nums[1.. Int { + var rob1 = 0 + var rob2 = 0 + + for num in nums { + let newRob = max(rob1 + num, rob2) + rob1 = rob2 + rob2 = newRob + } + + return rob2 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/house-robber-iii.md b/articles/house-robber-iii.md new file mode 100644 index 000000000..a09238c6b --- /dev/null +++ b/articles/house-robber-iii.md @@ -0,0 +1,563 @@ +## 1. Recursion + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def rob(self, root: Optional[TreeNode]) -> int: + if not root: + return 0 + + res = root.val + if root.left: + res += self.rob(root.left.left) + self.rob(root.left.right) + if root.right: + res += self.rob(root.right.left) + self.rob(root.right.right) + + res = max(res, self.rob(root.left) + self.rob(root.right)) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int rob(TreeNode root) { + if (root == null) { + return 0; + } + + int res = root.val; + if (root.left != null) { + res += rob(root.left.left) + rob(root.left.right); + } + if (root.right != null) { + res += rob(root.right.left) + rob(root.right.right); + } + + res = Math.max(res, rob(root.left) + rob(root.right)); + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int rob(TreeNode* root) { + if (!root) { + return 0; + } + + int res = root->val; + if (root->left) { + res += rob(root->left->left) + rob(root->left->right); + } + if (root->right) { + res += rob(root->right->left) + rob(root->right->right); + } + + res = max(res, rob(root->left) + rob(root->right)); + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + rob(root) { + if (!root) { + return 0; + } + + let res = root.val; + if (root.left) { + res += this.rob(root.left.left) + this.rob(root.left.right); + } + if (root.right) { + res += this.rob(root.right.left) + this.rob(root.right.right); + } + + res = Math.max(res, this.rob(root.left) + this.rob(root.right)); + return res; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int Rob(TreeNode root) { + if (root == null) return 0; + + int res = root.val; + if (root.left != null) { + res += Rob(root.left.left) + Rob(root.left.right); + } + if (root.right != null) { + res += Rob(root.right.left) + Rob(root.right.right); + } + + res = Math.Max(res, Rob(root.left) + Rob(root.right)); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Memoization) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def rob(self, root: Optional[TreeNode]) -> int: + cache = { None : 0 } + + def dfs(root): + if root in cache: + return cache[root] + + cache[root] = root.val + if root.left: + cache[root] += dfs(root.left.left) + dfs(root.left.right) + if root.right: + cache[root] += dfs(root.right.left) + dfs(root.right.right) + + cache[root] = max(cache[root], dfs(root.left) + dfs(root.right)) + return cache[root] + + return dfs(root) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private Map cache; + + public int rob(TreeNode root) { + cache = new HashMap<>(); + cache.put(null, 0); + return dfs(root); + } + + private int dfs(TreeNode root) { + if (cache.containsKey(root)) { + return cache.get(root); + } + + int res = root.val; + if (root.left != null) { + res += dfs(root.left.left) + dfs(root.left.right); + } + if (root.right != null) { + res += dfs(root.right.left) + dfs(root.right.right); + } + + res = Math.max(res, dfs(root.left) + dfs(root.right)); + cache.put(root, res); + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { + unordered_map cache; + +public: + int rob(TreeNode* root) { + cache[nullptr] = 0; + return dfs(root); + } + +private: + int dfs(TreeNode* root) { + if (cache.find(root) != cache.end()) { + return cache[root]; + } + + int res = root->val; + if (root->left) { + res += rob(root->left->left) + rob(root->left->right); + } + if (root->right) { + res += rob(root->right->left) + rob(root->right->right); + } + + res = max(res, rob(root->left) + rob(root->right)); + cache[root] = res; + return res; + + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + rob(root) { + const cache = new Map(); + cache.set(null, 0); + + const dfs = (root) => { + if (cache.has(root)) { + return cache.get(root); + } + + let res = root.val; + if (root.left) { + res += dfs(root.left.left) + dfs(root.left.right); + } + if (root.right) { + res += dfs(root.right.left) + dfs(root.right.right); + } + + res = Math.max(res, dfs(root.left) + dfs(root.right)); + cache.set(root, res); + return res; + }; + + return dfs(root); + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private Dictionary cache = new(); + + public int Rob(TreeNode root) { + return Dfs(root); + } + + private int Dfs(TreeNode root) { + if (root == null) return 0; + if (cache.ContainsKey(root)) return cache[root]; + + int res = root.val; + if (root.left != null) { + res += Dfs(root.left.left) + Dfs(root.left.right); + } + if (root.right != null) { + res += Dfs(root.right.left) + Dfs(root.right.right); + } + + res = Math.Max(res, Dfs(root.left) + Dfs(root.right)); + cache[root] = res; + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def rob(self, root: TreeNode) -> int: + def dfs(root): + if not root: + return [0, 0] + + leftPair = dfs(root.left) + rightPair = dfs(root.right) + + withRoot = root.val + leftPair[1] + rightPair[1] + withoutRoot = max(leftPair) + max(rightPair) + + return [withRoot, withoutRoot] + + return max(dfs(root)) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int rob(TreeNode root) { + int[] result = dfs(root); + return Math.max(result[0], result[1]); + } + + private int[] dfs(TreeNode root) { + if (root == null) { + return new int[]{0, 0}; + } + + int[] leftPair = dfs(root.left); + int[] rightPair = dfs(root.right); + + int withRoot = root.val + leftPair[1] + rightPair[1]; + int withoutRoot = Math.max(leftPair[0], leftPair[1]) + + Math.max(rightPair[0], rightPair[1]); + + return new int[]{withRoot, withoutRoot}; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int rob(TreeNode* root) { + auto result = dfs(root); + return max(result.first, result.second); + } + +private: + pair dfs(TreeNode* root) { + if (!root) { + return {0, 0}; + } + + auto leftPair = dfs(root->left); + auto rightPair = dfs(root->right); + + int withRoot = root->val + leftPair.second + rightPair.second; + int withoutRoot = max(leftPair.first, leftPair.second) + + max(rightPair.first, rightPair.second); + + return {withRoot, withoutRoot}; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + rob(root) { + const dfs = (node) => { + if (!node) { + return [0, 0]; + } + + const leftPair = dfs(node.left); + const rightPair = dfs(node.right); + + const withRoot = node.val + leftPair[1] + rightPair[1]; + const withoutRoot = Math.max(...leftPair) + Math.max(...rightPair); + + return [withRoot, withoutRoot]; + }; + + const result = dfs(root); + return Math.max(...result); + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int Rob(TreeNode root) { + var result = Dfs(root); + return Math.Max(result.withRoot, result.withoutRoot); + } + + private (int withRoot, int withoutRoot) Dfs(TreeNode root) { + if (root == null) return (0, 0); + + var left = Dfs(root.left); + var right = Dfs(root.right); + + int withRoot = root.val + left.withoutRoot + right.withoutRoot; + int withoutRoot = Math.Max(left.withRoot, left.withoutRoot) + Math.Max(right.withRoot, right.withoutRoot); + + return (withRoot, withoutRoot); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. \ No newline at end of file diff --git a/articles/house-robber.md b/articles/house-robber.md new file mode 100644 index 000000000..22be9ba02 --- /dev/null +++ b/articles/house-robber.md @@ -0,0 +1,663 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def rob(self, nums: List[int]) -> int: + + def dfs(i): + if i >= len(nums): + return 0 + return max(dfs(i + 1), + nums[i] + dfs(i + 2)) + + return dfs(0) +``` + +```java +public class Solution { + public int rob(int[] nums) { + return dfs(nums, 0); + } + + private int dfs(int[] nums, int i) { + if (i >= nums.length) { + return 0; + } + return Math.max(dfs(nums, i + 1), + nums[i] + dfs(nums, i + 2)); + } +} +``` + +```cpp +class Solution { +public: + int rob(vector& nums) { + return dfs(nums, 0); + } + + int dfs(vector& nums, int i) { + if (i >= nums.size()) { + return 0; + } + return max(dfs(nums, i + 1), + nums[i] + dfs(nums, i + 2)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + rob(nums) { + + const dfs = (i) => { + if (i >= nums.length) { + return 0; + } + return Math.max(dfs(i + 1), + nums[i] + dfs(i + 2)); + } + return dfs(0); + } +} +``` + +```csharp +public class Solution { + public int Rob(int[] nums) { + return Dfs(nums, 0); + } + + private int Dfs(int[] nums, int i) { + if (i >= nums.Length) { + return 0; + } + return Math.Max(Dfs(nums, i + 1), + nums[i] + Dfs(nums, i + 2)); + } +} +``` + +```go +func rob(nums []int) int { + var dfs func(i int) int + dfs = func(i int) int { + if i >= len(nums) { + return 0 + } + return max(dfs(i+1), nums[i] + dfs(i+2)) + } + + return dfs(0) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun rob(nums: IntArray): Int { + val n = nums.size + fun dfs(i: Int): Int { + if (i >= n) return 0 + return maxOf(dfs(i + 1), nums[i] + dfs(i + 2)) + } + return dfs(0) + } +} +``` + +```swift +class Solution { + func rob(_ nums: [Int]) -> Int { + func dfs(_ i: Int) -> Int { + if i >= nums.count { + return 0 + } + return max(dfs(i + 1), nums[i] + dfs(i + 2)) + } + + return dfs(0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def rob(self, nums: List[int]) -> int: + memo = [-1] * len(nums) + + def dfs(i): + if i >= len(nums): + return 0 + if memo[i] != -1: + return memo[i] + memo[i] = max(dfs(i + 1), nums[i] + dfs(i + 2)) + return memo[i] + + return dfs(0) +``` + +```java +public class Solution { + private int[] memo; + + public int rob(int[] nums) { + memo = new int[nums.length]; + Arrays.fill(memo, -1); + return dfs(nums, 0); + } + + private int dfs(int[] nums, int i) { + if (i >= nums.length) { + return 0; + } + if (memo[i] != -1) { + return memo[i]; + } + memo[i] = Math.max(dfs(nums, i + 1), + nums[i] + dfs(nums, i + 2)); + return memo[i]; + } +} +``` + +```cpp +class Solution { +public: + vector memo; + + int rob(vector& nums) { + memo.resize(nums.size(), -1); + return dfs(nums, 0); + } + + int dfs(vector& nums, int i) { + if (i >= nums.size()) { + return 0; + } + if (memo[i] != -1) { + return memo[i]; + } + memo[i] = max(dfs(nums, i + 1), + nums[i] + dfs(nums, i + 2)); + return memo[i]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + rob(nums) { + const memo = new Int32Array(nums.length).fill(-1); + const dfs = (i) => { + if (i >= nums.length) { + return 0; + } + if (memo[i] !== -1) { + return memo[i]; + } + return memo[i] = Math.max(dfs(i + 1), + nums[i] + dfs(i + 2)); + } + return dfs(0); + } +} +``` + +```csharp +public class Solution { + private int[] memo; + + public int Rob(int[] nums) { + memo = new int[nums.Length]; + for (int i = 0; i < nums.Length; i++) { + memo[i] = -1; + } + return Dfs(nums, 0); + } + + private int Dfs(int[] nums, int i) { + if (i >= nums.Length) { + return 0; + } + if (memo[i] != -1) { + return memo[i]; + } + memo[i] = Math.Max(Dfs(nums, i + 1), + nums[i] + Dfs(nums, i + 2)); + return memo[i]; + } +} +``` + +```go +func rob(nums []int) int { + n := len(nums) + memo := make([]int, n+1) + for i := 0; i <= n; i++ { + memo[i] = -1 + } + + var dfs func(i int) int + dfs = func(i int) int { + if i >= len(nums) { + return 0 + } + if memo[i] != -1 { + return memo[i] + } + memo[i] = max(dfs(i+1), nums[i] + dfs(i+2)) + return memo[i] + } + + return dfs(0) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun rob(nums: IntArray): Int { + val n = nums.size + var memo = IntArray(n+1){-1} + fun dfs(i: Int): Int { + if (i >= n) return 0 + if (memo[i] != -1) return memo[i] + memo[i] = maxOf(dfs(i + 1), nums[i] + dfs(i + 2)) + return memo[i] + } + return dfs(0) + } +} +``` + +```swift +class Solution { + func rob(_ nums: [Int]) -> Int { + var memo = Array(repeating: -1, count: nums.count) + + func dfs(_ i: Int) -> Int { + if i >= nums.count { + return 0 + } + if memo[i] != -1 { + return memo[i] + } + memo[i] = max(dfs(i + 1), nums[i] + dfs(i + 2)) + return memo[i] + } + + return dfs(0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def rob(self, nums: List[int]) -> int: + if not nums: + return 0 + if len(nums) == 1: + return nums[0] + + dp = [0] * len(nums) + dp[0] = nums[0] + dp[1] = max(nums[0], nums[1]) + + for i in range(2, len(nums)): + dp[i] = max(dp[i - 1], nums[i] + dp[i - 2]) + + return dp[-1] +``` + +```java +public class Solution { + public int rob(int[] nums) { + if (nums.length == 0) return 0; + if (nums.length == 1) return nums[0]; + + int[] dp = new int[nums.length]; + dp[0] = nums[0]; + dp[1] = Math.max(nums[0], nums[1]); + + for (int i = 2; i < nums.length; i++) { + dp[i] = Math.max(dp[i - 1], nums[i] + dp[i - 2]); + } + + return dp[nums.length - 1]; + } +} +``` + +```cpp +class Solution { +public: + int rob(vector& nums) { + if (nums.empty()) return 0; + if (nums.size() == 1) return nums[0]; + + vector dp(nums.size()); + dp[0] = nums[0]; + dp[1] = max(nums[0], nums[1]); + + for (int i = 2; i < nums.size(); i++) { + dp[i] = max(dp[i - 1], nums[i] + dp[i - 2]); + } + + return dp[nums.size() - 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + rob(nums) { + if (nums.length === 0) return 0; + if (nums.length === 1) return nums[0]; + + const dp = new Array(nums.length).fill(0); + dp[0] = nums[0]; + dp[1] = Math.max(nums[0], nums[1]); + + for (let i = 2; i < nums.length; i++) { + dp[i] = Math.max(dp[i - 1], nums[i] + dp[i - 2]); + } + + return dp[nums.length - 1]; + } +} +``` + +```csharp +public class Solution { + public int Rob(int[] nums) { + if (nums.Length == 0) return 0; + if (nums.Length == 1) return nums[0]; + + int[] dp = new int[nums.Length]; + dp[0] = nums[0]; + dp[1] = Math.Max(nums[0], nums[1]); + + for (int i = 2; i < nums.Length; i++) { + dp[i] = Math.Max(dp[i - 1], nums[i] + dp[i - 2]); + } + + return dp[nums.Length - 1]; + } +} +``` + +```go +func rob(nums []int) int { + n := len(nums) + if n == 0 { + return 0 + } + if n == 1 { + return nums[0] + } + + dp := make([]int, n) + dp[0] = nums[0] + dp[1] = max(nums[0], nums[1]) + + for i := 2; i < n; i++ { + dp[i] = max(dp[i-1], nums[i] + dp[i-2]) + } + + return dp[n-1] +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun rob(nums: IntArray): Int { + if (nums.isEmpty()) { + return 0 + } + if (nums.size == 1) { + return nums[0] + } + + val dp = IntArray(nums.size) + dp[0] = nums[0] + dp[1] = maxOf(nums[0], nums[1]) + + for (i in 2 until nums.size) { + dp[i] = maxOf(dp[i - 1], nums[i] + dp[i - 2]) + } + + return dp[nums.size - 1] + } +} +``` + +```swift +class Solution { + func rob(_ nums: [Int]) -> Int { + if nums.isEmpty { + return 0 + } + if nums.count == 1 { + return nums[0] + } + + var dp = Array(repeating: 0, count: nums.count) + dp[0] = nums[0] + dp[1] = max(nums[0], nums[1]) + + for i in 2.. int: + rob1, rob2 = 0, 0 + + for num in nums: + temp = max(num + rob1, rob2) + rob1 = rob2 + rob2 = temp + return rob2 +``` + +```java +public class Solution { + public int rob(int[] nums) { + int rob1 = 0, rob2 = 0; + + for (int num : nums) { + int temp = Math.max(num + rob1, rob2); + rob1 = rob2; + rob2 = temp; + } + return rob2; + } +} +``` + +```cpp +class Solution { +public: + int rob(vector& nums) { + int rob1 = 0, rob2 = 0; + + for (int num : nums) { + int temp = max(num + rob1, rob2); + rob1 = rob2; + rob2 = temp; + } + return rob2; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + rob(nums) { + let rob1 = 0; + let rob2 = 0; + + for (const num of nums) { + const temp = Math.max(num + rob1, rob2); + rob1 = rob2; + rob2 = temp; + } + return rob2; + } +} +``` + +```csharp +public class Solution { + public int Rob(int[] nums) { + int rob1 = 0, rob2 = 0; + + foreach (int num in nums) { + int temp = Math.Max(num + rob1, rob2); + rob1 = rob2; + rob2 = temp; + } + return rob2; + } +} +``` + +```go +func rob(nums []int) int { + rob1, rob2 := 0, 0 + for _, num := range nums { + temp := max(num+rob1, rob2) + rob1 = rob2 + rob2 = temp + } + return rob2 +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun rob(nums: IntArray): Int { + var rob1 = 0 + var rob2 = 0 + for (num in nums) { + val temp = maxOf(num + rob1, rob2) + rob1 = rob2 + rob2 = temp + } + return rob2 + } +} +``` + +```swift +class Solution { + func rob(_ nums: [Int]) -> Int { + var rob1 = 0, rob2 = 0 + + for num in nums { + let temp = max(num + rob1, rob2) + rob1 = rob2 + rob2 = temp + } + + return rob2 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/image-smoother.md b/articles/image-smoother.md new file mode 100644 index 000000000..767d82e16 --- /dev/null +++ b/articles/image-smoother.md @@ -0,0 +1,530 @@ +## 1. Iteration (Using Extra Matrix) + +::tabs-start + +```python +class Solution: + def imageSmoother(self, img: List[List[int]]) -> List[List[int]]: + ROWS, COLS = len(img), len(img[0]) + res = [[0] * COLS for _ in range(ROWS)] + + for r in range(ROWS): + for c in range(COLS): + total, cnt = 0, 0 + for i in range(r - 1, r + 2): + for j in range(c - 1, c + 2): + if 0 <= i < ROWS and 0 <= j < COLS: + total += img[i][j] + cnt += 1 + res[r][c] = total // cnt + + return res +``` + +```java +public class Solution { + public int[][] imageSmoother(int[][] img) { + int ROWS = img.length, COLS = img[0].length; + int[][] res = new int[ROWS][COLS]; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + int total = 0, count = 0; + for (int i = r - 1; i <= r + 1; i++) { + for (int j = c - 1; j <= c + 1; j++) { + if (i >= 0 && i < ROWS && j >= 0 && j < COLS) { + total += img[i][j]; + count++; + } + } + } + res[r][c] = total / count; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> imageSmoother(vector>& img) { + int ROWS = img.size(), COLS = img[0].size(); + vector> res(ROWS, vector(COLS, 0)); + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + int total = 0, count = 0; + for (int i = r - 1; i <= r + 1; i++) { + for (int j = c - 1; j <= c + 1; j++) { + if (i >= 0 && i < ROWS && j >= 0 && j < COLS) { + total += img[i][j]; + count++; + } + } + } + res[r][c] = total / count; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} img + * @return {number[][]} + */ + imageSmoother(img) { + const ROWS = img.length, COLS = img[0].length; + const res = Array.from({ length: ROWS }, () => Array(COLS).fill(0)); + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + let total = 0, count = 0; + for (let i = r - 1; i <= r + 1; i++) { + for (let j = c - 1; j <= c + 1; j++) { + if (i >= 0 && i < ROWS && j >= 0 && j < COLS) { + total += img[i][j]; + count++; + } + } + } + res[r][c] = Math.floor(total / count); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of rows and $m$ is the number of columns of the matrix. + +--- + +## 2. Iteration (Using Extra Row) + +::tabs-start + +```python +class Solution: + def imageSmoother(self, img: List[List[int]]) -> List[List[int]]: + ROWS, COLS = len(img), len(img[0]) + prev_row = img[0][:] + + for r in range(ROWS): + curr_row = img[r][:] + + for c in range(COLS): + total, cnt = 0, 0 + for i in range(max(0, r - 1), min(ROWS, r + 2)): + for j in range(max(0, c - 1), min(COLS, c + 2)): + if i == r: + total += curr_row[j] + elif i == r - 1: + total += prev_row[j] + else: + total += img[i][j] + cnt += 1 + img[r][c] = total // cnt + + prev_row = curr_row + + return img +``` + +```java +public class Solution { + public int[][] imageSmoother(int[][] img) { + int ROWS = img.length, COLS = img[0].length; + int[] prevRow = img[0].clone(); + + for (int r = 0; r < ROWS; r++) { + int[] currRow = img[r].clone(); + + for (int c = 0; c < COLS; c++) { + int total = 0, count = 0; + for (int i = Math.max(0, r - 1); i < Math.min(ROWS, r + 2); i++) { + for (int j = Math.max(0, c - 1); j < Math.min(COLS, c + 2); j++) { + if (i == r) { + total += currRow[j]; + } else if (i == r - 1) { + total += prevRow[j]; + } else { + total += img[i][j]; + } + count++; + } + } + img[r][c] = total / count; + } + + prevRow = currRow; + } + + return img; + } +} +``` + +```cpp +class Solution { +public: + vector> imageSmoother(vector>& img) { + int ROWS = img.size(), COLS = img[0].size(); + vector prevRow = img[0]; + + for (int r = 0; r < ROWS; r++) { + vector currRow = img[r]; + + for (int c = 0; c < COLS; c++) { + int total = 0, count = 0; + for (int i = max(0, r - 1); i < min(ROWS, r + 2); i++) { + for (int j = max(0, c - 1); j < min(COLS, c + 2); j++) { + if (i == r) { + total += currRow[j]; + } else if (i == r - 1) { + total += prevRow[j]; + } else { + total += img[i][j]; + } + count++; + } + } + img[r][c] = total / count; + } + + prevRow = currRow; + } + + return img; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} img + * @return {number[][]} + */ + imageSmoother(img) { + const ROWS = img.length, COLS = img[0].length; + let prevRow = [...img[0]]; + + for (let r = 0; r < ROWS; r++) { + let currRow = [...img[r]]; + + for (let c = 0; c < COLS; c++) { + let total = 0, count = 0; + for (let i = Math.max(0, r - 1); i < Math.min(ROWS, r + 2); i++) { + for (let j = Math.max(0, c - 1); j < Math.min(COLS, c + 2); j++) { + if (i === r) { + total += currRow[j]; + } else if (i === r - 1) { + total += prevRow[j]; + } else { + total += img[i][j]; + } + count++; + } + } + img[r][c] = Math.floor(total / count); + } + + prevRow = currRow; + } + + return img; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ extra space. + +> Where $n$ is the number of rows and $m$ is the number of columns of the matrix. + +--- + +## 3. Iteration (Without Extra Space) + +::tabs-start + +```python +class Solution: + def imageSmoother(self, img: List[List[int]]) -> List[List[int]]: + ROWS, COLS = len(img), len(img[0]) + LIMIT = 256 + + for r in range(ROWS): + for c in range(COLS): + total, cnt = 0, 0 + for i in range(max(0, r - 1), min(ROWS, r + 2)): + for j in range(max(0, c - 1), min(COLS, c + 2)): + total += img[i][j] % LIMIT + cnt += 1 + img[r][c] += (total // cnt) * LIMIT + + for r in range(ROWS): + for c in range(COLS): + img[r][c] //= LIMIT + + return img +``` + +```java +public class Solution { + public int[][] imageSmoother(int[][] img) { + int ROWS = img.length, COLS = img[0].length; + int LIMIT = 256; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + int total = 0, count = 0; + for (int i = Math.max(0, r - 1); i < Math.min(ROWS, r + 2); i++) { + for (int j = Math.max(0, c - 1); j < Math.min(COLS, c + 2); j++) { + total += img[i][j] % LIMIT; + count++; + } + } + img[r][c] += (total / count) * LIMIT; + } + } + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + img[r][c] /= LIMIT; + } + } + + return img; + } +} +``` + +```cpp +class Solution { +public: + vector> imageSmoother(vector>& img) { + int ROWS = img.size(), COLS = img[0].size(); + int LIMIT = 256; + + for (int r = 0; r < ROWS; ++r) { + for (int c = 0; c < COLS; ++c) { + int total = 0, count = 0; + for (int i = max(0, r - 1); i < min(ROWS, r + 2); ++i) { + for (int j = max(0, c - 1); j < min(COLS, c + 2); ++j) { + total += img[i][j] % LIMIT; + count++; + } + } + img[r][c] += (total / count) * LIMIT; + } + } + + for (int r = 0; r < ROWS; ++r) { + for (int c = 0; c < COLS; ++c) { + img[r][c] /= LIMIT; + } + } + + return img; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} img + * @return {number[][]} + */ + imageSmoother(img) { + const ROWS = img.length, COLS = img[0].length; + const LIMIT = 256; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + let total = 0, count = 0; + for (let i = Math.max(0, r - 1); i < Math.min(ROWS, r + 2); i++) { + for (let j = Math.max(0, c - 1); j < Math.min(COLS, c + 2); j++) { + total += img[i][j] % LIMIT; + count++; + } + } + img[r][c] += Math.floor(total / count) * LIMIT; + } + } + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + img[r][c] = Math.floor(img[r][c] / LIMIT); + } + } + + return img; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(1)$ extra space. + +> Where $n$ is the number of rows and $m$ is the number of columns of the matrix. + +--- + +## 4. Bit Mask + +::tabs-start + +```python +class Solution: + def imageSmoother(self, img: List[List[int]]) -> List[List[int]]: + ROWS, COLS = len(img), len(img[0]) + + for r in range(ROWS): + for c in range(COLS): + total, cnt = 0, 0 + for i in range(r - 1, r + 2): + for j in range(c - 1, c + 2): + if i < 0 or i == ROWS or j < 0 or j == COLS: + continue + total += img[i][j] % 256 + cnt += 1 + img[r][c] ^= ((total // cnt) << 8) + + for r in range(ROWS): + for c in range(COLS): + img[r][c] >>= 8 + return img +``` + +```java +public class Solution { + public int[][] imageSmoother(int[][] img) { + int ROWS = img.length, COLS = img[0].length; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + int total = 0, cnt = 0; + for (int i = r - 1; i <= r + 1; i++) { + for (int j = c - 1; j <= c + 1; j++) { + if (i < 0 || i >= ROWS || j < 0 || j >= COLS) { + continue; + } + total += img[i][j] % 256; + cnt++; + } + } + img[r][c] ^= ((total / cnt) << 8); + } + } + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + img[r][c] >>= 8; + } + } + return img; + } +} +``` + +```cpp +class Solution { +public: + vector> imageSmoother(vector>& img) { + int ROWS = img.size(), COLS = img[0].size(); + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + int total = 0, cnt = 0; + for (int i = r - 1; i <= r + 1; i++) { + for (int j = c - 1; j <= c + 1; j++) { + if (i < 0 || i >= ROWS || j < 0 || j >= COLS) { + continue; + } + total += img[i][j] % 256; + cnt++; + } + } + img[r][c] ^= ((total / cnt) << 8); + } + } + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + img[r][c] >>= 8; + } + } + return img; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} img + * @return {number[][]} + */ + imageSmoother(img) { + const ROWS = img.length, COLS = img[0].length; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + let total = 0, cnt = 0; + for (let i = r - 1; i <= r + 1; i++) { + for (let j = c - 1; j <= c + 1; j++) { + if (i < 0 || i >= ROWS || j < 0 || j >= COLS) { + continue; + } + total += img[i][j] % 256; + cnt++; + } + } + img[r][c] ^= ((total / cnt) << 8); + } + } + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + img[r][c] >>= 8; + } + } + return img; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(1)$ extra space. + +> Where $n$ is the number of rows and $m$ is the number of columns of the matrix. \ No newline at end of file diff --git a/articles/implement-prefix-tree.md b/articles/implement-prefix-tree.md new file mode 100644 index 000000000..3e4a303f2 --- /dev/null +++ b/articles/implement-prefix-tree.md @@ -0,0 +1,827 @@ +## 1. Prefix Tree (Array) + +::tabs-start + +```python +class TrieNode: + def __init__(self): + self.children = [None] * 26 + self.endOfWord = False + +class PrefixTree: + def __init__(self): + self.root = TrieNode() + + def insert(self, word: str) -> None: + cur = self.root + for c in word: + i = ord(c) - ord("a") + if cur.children[i] == None: + cur.children[i] = TrieNode() + cur = cur.children[i] + cur.endOfWord = True + + def search(self, word: str) -> bool: + cur = self.root + for c in word: + i = ord(c) - ord("a") + if cur.children[i] == None: + return False + cur = cur.children[i] + return cur.endOfWord + + def startsWith(self, prefix: str) -> bool: + cur = self.root + for c in prefix: + i = ord(c) - ord("a") + if cur.children[i] == None: + return False + cur = cur.children[i] + return True +``` + +```java +public class TrieNode { + TrieNode[] children = new TrieNode[26]; + boolean endOfWord = false; +} + +public class PrefixTree { + private TrieNode root; + + public PrefixTree() { + root = new TrieNode(); + } + + public void insert(String word) { + TrieNode cur = root; + for (char c : word.toCharArray()) { + int i = c - 'a'; + if (cur.children[i] == null) { + cur.children[i] = new TrieNode(); + } + cur = cur.children[i]; + } + cur.endOfWord = true; + } + + public boolean search(String word) { + TrieNode cur = root; + for (char c : word.toCharArray()) { + int i = c - 'a'; + if (cur.children[i] == null) { + return false; + } + cur = cur.children[i]; + } + return cur.endOfWord; + } + + public boolean startsWith(String prefix) { + TrieNode cur = root; + for (char c : prefix.toCharArray()) { + int i = c - 'a'; + if (cur.children[i] == null) { + return false; + } + cur = cur.children[i]; + } + return true; + } +} +``` + +```cpp +class TrieNode { +public: + TrieNode* children[26]; + bool endOfWord; + + TrieNode() { + for (int i = 0; i < 26; i++) { + children[i] = nullptr; + } + endOfWord = false; + } +}; + +class PrefixTree { + TrieNode* root; + +public: + PrefixTree() { + root = new TrieNode(); + } + + void insert(string word) { + TrieNode* cur = root; + for (char c : word) { + int i = c - 'a'; + if (cur->children[i] == nullptr) { + cur->children[i] = new TrieNode(); + } + cur = cur->children[i]; + } + cur->endOfWord = true; + } + + bool search(string word) { + TrieNode* cur = root; + for (char c : word) { + int i = c - 'a'; + if (cur->children[i] == nullptr) { + return false; + } + cur = cur->children[i]; + } + return cur->endOfWord; + } + + bool startsWith(string prefix) { + TrieNode* cur = root; + for (char c : prefix) { + int i = c - 'a'; + if (cur->children[i] == nullptr) { + return false; + } + cur = cur->children[i]; + } + return true; + } +}; +``` + +```javascript +class TrieNode { + constructor() { + this.children = new Array(26).fill(null); + this.endOfWord = false; + } +} + +class PrefixTree { + constructor() { + this.root = new TrieNode(); + } + + /** + * @param {string} word + * @return {void} + */ + insert(word) { + let cur = this.root; + for (let c of word) { + let i = c.charCodeAt(0) - 97; + if (cur.children[i] === null) { + cur.children[i] = new TrieNode(); + } + cur = cur.children[i]; + } + cur.endOfWord = true; + } + + /** + * @param {string} word + * @return {boolean} + */ + search(word) { + let cur = this.root; + for (let c of word) { + let i = c.charCodeAt(0) - 97; + if (cur.children[i] === null) { + return false; + } + cur = cur.children[i]; + } + return cur.endOfWord; + } + + /** + * @param {string} prefix + * @return {boolean} + */ + startsWith(prefix) { + let cur = this.root; + for (let c of prefix) { + let i = c.charCodeAt(0) - 97; + if (cur.children[i] === null) { + return false; + } + cur = cur.children[i]; + } + return true; + } +} +``` + +```csharp +public class TrieNode { + public TrieNode[] children = new TrieNode[26]; + public bool endOfWord = false; +} + +public class PrefixTree { + private TrieNode root; + + public PrefixTree() { + root = new TrieNode(); + } + + public void Insert(string word) { + TrieNode cur = root; + foreach (char c in word) { + int i = c - 'a'; + if (cur.children[i] == null) { + cur.children[i] = new TrieNode(); + } + cur = cur.children[i]; + } + cur.endOfWord = true; + } + + public bool Search(string word) { + TrieNode cur = root; + foreach (char c in word) { + int i = c - 'a'; + if (cur.children[i] == null) { + return false; + } + cur = cur.children[i]; + } + return cur.endOfWord; + } + + public bool StartsWith(string prefix) { + TrieNode cur = root; + foreach (char c in prefix) { + int i = c - 'a'; + if (cur.children[i] == null) { + return false; + } + cur = cur.children[i]; + } + return true; + } +} +``` + +```go +type TrieNode struct { + children [26]*TrieNode + endOfWord bool +} + +type PrefixTree struct { + root *TrieNode +} + +func Constructor() PrefixTree { + return PrefixTree{root: &TrieNode{}} +} + +func (this *PrefixTree) Insert(word string) { + cur := this.root + for _, c := range word { + i := c - 'a' + if cur.children[i] == nil { + cur.children[i] = &TrieNode{} + } + cur = cur.children[i] + } + cur.endOfWord = true +} + +func (this *PrefixTree) Search(word string) bool { + cur := this.root + for _, c := range word { + i := c - 'a' + if cur.children[i] == nil { + return false + } + cur = cur.children[i] + } + return cur.endOfWord +} + +func (this *PrefixTree) StartsWith(prefix string) bool { + cur := this.root + for _, c := range prefix { + i := c - 'a' + if cur.children[i] == nil { + return false + } + cur = cur.children[i] + } + return true +} +``` + +```kotlin +class TrieNode { + val children = arrayOfNulls(26) + var endOfWord = false +} + +class PrefixTree { + private val root = TrieNode() + + fun insert(word: String) { + var cur = root + for (c in word) { + val i = c - 'a' + if (cur.children[i] == null) { + cur.children[i] = TrieNode() + } + cur = cur.children[i]!! + } + cur.endOfWord = true + } + + fun search(word: String): Boolean { + var cur = root + for (c in word) { + val i = c - 'a' + if (cur.children[i] == null) { + return false + } + cur = cur.children[i]!! + } + return cur.endOfWord + } + + fun startsWith(prefix: String): Boolean { + var cur = root + for (c in prefix) { + val i = c - 'a' + if (cur.children[i] == null) { + return false + } + cur = cur.children[i]!! + } + return true + } +} +``` + +```swift +class TrieNode { + var children: [TrieNode?] + var endOfWord: Bool + + init() { + self.children = Array(repeating: nil, count: 26) + self.endOfWord = false + } +} + +class PrefixTree { + private let root: TrieNode + + init() { + self.root = TrieNode() + } + + func insert(_ word: String) { + var cur = root + for c in word { + let i = Int(c.asciiValue! - Character("a").asciiValue!) + if cur.children[i] == nil { + cur.children[i] = TrieNode() + } + cur = cur.children[i]! + } + cur.endOfWord = true + } + + func search(_ word: String) -> Bool { + var cur = root + for c in word { + let i = Int(c.asciiValue! - Character("a").asciiValue!) + if cur.children[i] == nil { + return false + } + cur = cur.children[i]! + } + return cur.endOfWord + } + + func startsWith(_ prefix: String) -> Bool { + var cur = root + for c in prefix { + let i = Int(c.asciiValue! - Character("a").asciiValue!) + if cur.children[i] == nil { + return false + } + cur = cur.children[i]! + } + return true + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ for each function call. +* Space complexity: $O(t)$ + +> Where $n$ is the length of the string and $t$ is the total number of TrieNodes created in the Trie. + +--- + +## 2. Prefix Tree (Hash Map) + +::tabs-start + +```python +class TrieNode: + def __init__(self): + self.children = {} + self.endOfWord = False + +class PrefixTree: + def __init__(self): + self.root = TrieNode() + + def insert(self, word: str) -> None: + cur = self.root + for c in word: + if c not in cur.children: + cur.children[c] = TrieNode() + cur = cur.children[c] + cur.endOfWord = True + + def search(self, word: str) -> bool: + cur = self.root + for c in word: + if c not in cur.children: + return False + cur = cur.children[c] + return cur.endOfWord + + def startsWith(self, prefix: str) -> bool: + cur = self.root + for c in prefix: + if c not in cur.children: + return False + cur = cur.children[c] + return True +``` + +```java +public class TrieNode { + HashMap children = new HashMap<>(); + boolean endOfWord = false; +} + +public class PrefixTree { + private TrieNode root; + + public PrefixTree() { + root = new TrieNode(); + } + + public void insert(String word) { + TrieNode cur = root; + for (char c : word.toCharArray()) { + cur.children.putIfAbsent(c, new TrieNode()); + cur = cur.children.get(c); + } + cur.endOfWord = true; + } + + public boolean search(String word) { + TrieNode cur = root; + for (char c : word.toCharArray()) { + if (!cur.children.containsKey(c)) { + return false; + } + cur = cur.children.get(c); + } + return cur.endOfWord; + } + + public boolean startsWith(String prefix) { + TrieNode cur = root; + for (char c : prefix.toCharArray()) { + if (!cur.children.containsKey(c)) { + return false; + } + cur = cur.children.get(c); + } + return true; + } +} +``` + +```cpp +class TrieNode { +public: + unordered_map children; + bool endOfWord = false; +}; + +class PrefixTree { + TrieNode* root; + +public: + PrefixTree() { + root = new TrieNode(); + } + + void insert(string word) { + TrieNode* cur = root; + for (char c : word) { + if (cur->children.find(c) == cur->children.end()) { + cur->children[c] = new TrieNode(); + } + cur = cur->children[c]; + } + cur->endOfWord = true; + } + + bool search(string word) { + TrieNode* cur = root; + for (char c : word) { + if (cur->children.find(c) == cur->children.end()) { + return false; + } + cur = cur->children[c]; + } + return cur->endOfWord; + } + + bool startsWith(string prefix) { + TrieNode* cur = root; + for (char c : prefix) { + if (cur->children.find(c) == cur->children.end()) { + return false; + } + cur = cur->children[c]; + } + return true; + } +}; +``` + +```javascript +class TrieNode { + constructor() { + this.children = new Map(); + this.endOfWord = false; + } +} + +class PrefixTree { + constructor() { + this.root = new TrieNode(); + } + + /** + * @param {string} word + * @return {void} + */ + insert(word) { + let cur = this.root; + for (let c of word) { + if (!cur.children.has(c)) { + cur.children.set(c, new TrieNode()); + } + cur = cur.children.get(c); + } + cur.endOfWord = true; + } + + /** + * @param {string} word + * @return {boolean} + */ + search(word) { + let cur = this.root; + for (let c of word) { + if (!cur.children.has(c)) { + return false; + } + cur = cur.children.get(c); + } + return cur.endOfWord; + } + + /** + * @param {string} prefix + * @return {boolean} + */ + startsWith(prefix) { + let cur = this.root; + for (let c of prefix) { + if (!cur.children.has(c)) { + return false; + } + cur = cur.children.get(c); + } + return true; + } +} +``` + +```csharp +public class TrieNode { + public Dictionary children = + new Dictionary(); + public bool endOfWord = false; +} + +public class PrefixTree { + private TrieNode root; + + public PrefixTree() { + root = new TrieNode(); + } + + public void Insert(string word) { + TrieNode cur = root; + foreach (char c in word) { + if (!cur.children.ContainsKey(c)) { + cur.children[c] = new TrieNode(); + } + cur = cur.children[c]; + } + cur.endOfWord = true; + } + + public bool Search(string word) { + TrieNode cur = root; + foreach (char c in word) { + if (!cur.children.ContainsKey(c)) { + return false; + } + cur = cur.children[c]; + } + return cur.endOfWord; + } + + public bool StartsWith(string prefix) { + TrieNode cur = root; + foreach (char c in prefix) { + if (!cur.children.ContainsKey(c)) { + return false; + } + cur = cur.children[c]; + } + return true; + } +} +``` + +```go +type TrieNode struct { + children map[rune]*TrieNode + endOfWord bool +} + +type PrefixTree struct { + root *TrieNode +} + +func Constructor() PrefixTree { + return PrefixTree{root: &TrieNode{children: make(map[rune]*TrieNode)}} +} + +func (this *PrefixTree) Insert(word string) { + cur := this.root + for _, c := range word { + if cur.children[c] == nil { + cur.children[c] = &TrieNode{children: make(map[rune]*TrieNode)} + } + cur = cur.children[c] + } + cur.endOfWord = true +} + +func (this *PrefixTree) Search(word string) bool { + cur := this.root + for _, c := range word { + if cur.children[c] == nil { + return false + } + cur = cur.children[c] + } + return cur.endOfWord +} + +func (this *PrefixTree) StartsWith(prefix string) bool { + cur := this.root + for _, c := range prefix { + if cur.children[c] == nil { + return false + } + cur = cur.children[c] + } + return true +} +``` + +```kotlin +class TrieNode { + val children = mutableMapOf() + var endOfWord = false +} + +class PrefixTree { + private val root = TrieNode() + + fun insert(word: String) { + var cur = root + for (c in word) { + cur.children.putIfAbsent(c, TrieNode()) + cur = cur.children[c]!! + } + cur.endOfWord = true + } + + fun search(word: String): Boolean { + var cur = root + for (c in word) { + if (c !in cur.children) { + return false + } + cur = cur.children[c]!! + } + return cur.endOfWord + } + + fun startsWith(prefix: String): Boolean { + var cur = root + for (c in prefix) { + if (c !in cur.children) { + return false + } + cur = cur.children[c]!! + } + return true + } +} +``` + +```swift +class TrieNode { + var children: [Character: TrieNode] + var endOfWord: Bool + + init() { + self.children = [:] + self.endOfWord = false + } +} + +class PrefixTree { + private let root: TrieNode + + init() { + self.root = TrieNode() + } + + func insert(_ word: String) { + var cur = root + for c in word { + if cur.children[c] == nil { + cur.children[c] = TrieNode() + } + cur = cur.children[c]! + } + cur.endOfWord = true + } + + func search(_ word: String) -> Bool { + var cur = root + for c in word { + if cur.children[c] == nil { + return false + } + cur = cur.children[c]! + } + return cur.endOfWord + } + + func startsWith(_ prefix: String) -> Bool { + var cur = root + for c in prefix { + if cur.children[c] == nil { + return false + } + cur = cur.children[c]! + } + return true + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ for each function call. +* Space complexity: $O(t)$ + +> Where $n$ is the length of the string and $t$ is the total number of TrieNodes created in the Trie. \ No newline at end of file diff --git a/articles/implement-queue-using-stacks.md b/articles/implement-queue-using-stacks.md new file mode 100644 index 000000000..9d51b2ad4 --- /dev/null +++ b/articles/implement-queue-using-stacks.md @@ -0,0 +1,351 @@ +## 1. Using Two Stacks (Brute Force) + +::tabs-start + +```python +class MyQueue: + + def __init__(self): + self.stack1 = [] + self.stack2 = [] + + def push(self, x: int) -> None: + self.stack1.append(x) + + def pop(self) -> int: + while len(self.stack1) > 1: + self.stack2.append(self.stack1.pop()) + res = self.stack1.pop() + while self.stack2: + self.stack1.append(self.stack2.pop()) + return res + + def peek(self) -> int: + while len(self.stack1) > 1: + self.stack2.append(self.stack1.pop()) + res = self.stack1[-1] + while self.stack2: + self.stack1.append(self.stack2.pop()) + return res + + def empty(self) -> bool: + return not self.stack1 +``` + +```java +public class MyQueue { + private Stack stack1; + private Stack stack2; + + public MyQueue() { + stack1 = new Stack<>(); + stack2 = new Stack<>(); + } + + public void push(int x) { + stack1.push(x); + } + + public int pop() { + while (stack1.size() > 1) { + stack2.push(stack1.pop()); + } + int res = stack1.pop(); + while (!stack2.isEmpty()) { + stack1.push(stack2.pop()); + } + return res; + } + + public int peek() { + while (stack1.size() > 1) { + stack2.push(stack1.pop()); + } + int res = stack1.peek(); + while (!stack2.isEmpty()) { + stack1.push(stack2.pop()); + } + return res; + } + + public boolean empty() { + return stack1.isEmpty(); + } +} +``` + +```cpp +class MyQueue { +private: + stack stack1; + stack stack2; + +public: + MyQueue() {} + + void push(int x) { + stack1.push(x); + } + + int pop() { + while (stack1.size() > 1) { + stack2.push(stack1.top()); + stack1.pop(); + } + int res = stack1.top(); + stack1.pop(); + while (!stack2.empty()) { + stack1.push(stack2.top()); + stack2.pop(); + } + return res; + } + + int peek() { + while (stack1.size() > 1) { + stack2.push(stack1.top()); + stack1.pop(); + } + int res = stack1.top(); + while (!stack2.empty()) { + stack1.push(stack2.top()); + stack2.pop(); + } + return res; + } + + bool empty() { + return stack1.empty(); + } +}; +``` + +```javascript +class MyQueue { + constructor() { + this.stack1 = []; + this.stack2 = []; + } + + /** + * @param {number} x + * @return {void} + */ + push(x) { + this.stack1.push(x); + } + + /** + * @return {number} + */ + pop() { + while (this.stack1.length > 1) { + this.stack2.push(this.stack1.pop()); + } + const res = this.stack1.pop(); + while (this.stack2.length) { + this.stack1.push(this.stack2.pop()); + } + return res; + } + + /** + * @return {number} + */ + peek() { + while (this.stack1.length > 1) { + this.stack2.push(this.stack1.pop()); + } + const res = this.stack1[this.stack1.length - 1]; + while (this.stack2.length) { + this.stack1.push(this.stack2.pop()); + } + return res; + } + + /** + * @return {boolean} + */ + empty() { + return this.stack1.length === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $push()$ and $empty()$ function calls. + * $O(n)$ time for each $pop()$ and $peek()$ function calls. +* Space complexity: $O(n)$ + +--- + +## 2. Using Two Stacks (Amortized Complexity) + +::tabs-start + +```python +class MyQueue: + + def __init__(self): + self.s1 = [] + self.s2 = [] + + def push(self, x: int) -> None: + self.s1.append(x) + + def pop(self) -> int: + if not self.s2: + while self.s1: + self.s2.append(self.s1.pop()) + return self.s2.pop() + + def peek(self) -> int: + if not self.s2: + while self.s1: + self.s2.append(self.s1.pop()) + return self.s2[-1] + + def empty(self) -> bool: + return max(len(self.s1), len(self.s2)) == 0 +``` + +```java +public class MyQueue { + private Stack s1; + private Stack s2; + + public MyQueue() { + s1 = new Stack<>(); + s2 = new Stack<>(); + } + + public void push(int x) { + s1.push(x); + } + + public int pop() { + if (s2.isEmpty()) { + while (!s1.isEmpty()) { + s2.push(s1.pop()); + } + } + return s2.pop(); + } + + public int peek() { + if (s2.isEmpty()) { + while (!s1.isEmpty()) { + s2.push(s1.pop()); + } + } + return s2.peek(); + } + + public boolean empty() { + return s1.isEmpty() && s2.isEmpty(); + } +} +``` + +```cpp +class MyQueue { +private: + stack s1, s2; + +public: + MyQueue() {} + + void push(int x) { + s1.push(x); + } + + int pop() { + if (s2.empty()) { + while (!s1.empty()) { + s2.push(s1.top()); + s1.pop(); + } + } + int res = s2.top(); + s2.pop(); + return res; + } + + int peek() { + if (s2.empty()) { + while (!s1.empty()) { + s2.push(s1.top()); + s1.pop(); + } + } + return s2.top(); + } + + bool empty() { + return s1.empty() && s2.empty(); + } +}; +``` + +```javascript +class MyQueue { + constructor() { + this.s1 = []; + this.s2 = []; + } + + /** + * @param {number} x + * @return {void} + */ + push(x) { + this.s1.push(x); + } + + /** + * @return {number} + */ + pop() { + if (this.s2.length === 0) { + while (this.s1.length > 0) { + this.s2.push(this.s1.pop()); + } + } + return this.s2.pop(); + } + + /** + * @return {number} + */ + peek() { + if (this.s2.length === 0) { + while (this.s1.length > 0) { + this.s2.push(this.s1.pop()); + } + } + return this.s2[this.s2.length - 1]; + } + + /** + * @return {boolean} + */ + empty() { + return this.s1.length === 0 && this.s2.length === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $push()$ and $empty()$ function calls. + * $O(1)$ amortized time for each $pop()$ and $peek()$ function calls. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/implement-stack-using-queues.md b/articles/implement-stack-using-queues.md new file mode 100644 index 000000000..ee6199220 --- /dev/null +++ b/articles/implement-stack-using-queues.md @@ -0,0 +1,438 @@ +## 1. Using Two Queues + +::tabs-start + +```python +class MyStack: + + def __init__(self): + self.q1 = deque() + self.q2 = deque() + + def push(self, x: int) -> None: + self.q2.append(x) + while self.q1: + self.q2.append(self.q1.popleft()) + + self.q1, self.q2 = self.q2, self.q1 + + def pop(self) -> int: + return self.q1.popleft() + + def top(self) -> int: + return self.q1[0] + + def empty(self) -> bool: + return len(self.q1) == 0 +``` + +```java +public class MyStack { + private Queue q1; + private Queue q2; + + public MyStack() { + q1 = new LinkedList<>(); + q2 = new LinkedList<>(); + } + + public void push(int x) { + q2.offer(x); + while (!q1.isEmpty()) { + q2.offer(q1.poll()); + } + Queue temp = q1; + q1 = q2; + q2 = temp; + } + + public int pop() { + return q1.poll(); + } + + public int top() { + return q1.peek(); + } + + public boolean empty() { + return q1.isEmpty(); + } +} +``` + +```cpp +class MyStack { +private: + queue q1; + queue q2; + +public: + MyStack() {} + + void push(int x) { + q2.push(x); + while (!q1.empty()) { + q2.push(q1.front()); + q1.pop(); + } + swap(q1, q2); + } + + int pop() { + int top = q1.front(); + q1.pop(); + return top; + } + + int top() { + return q1.front(); + } + + bool empty() { + return q1.empty(); + } +}; +``` + +```javascript +class MyStack { + constructor() { + this.q1 = new Queue(); + this.q2 = new Queue(); + } + + /** + * @param {number} x + * @return {void} + */ + push(x) { + this.q2.push(x); + + while (!this.q1.isEmpty()) { + this.q2.push(this.q1.pop()); + } + + [this.q1, this.q2] = [this.q2, this.q1]; + } + + /** + * @return {number} + */ + pop() { + return this.q1.pop(); + } + + /** + * @return {number} + */ + top() { + return this.q1.front(); + } + + /** + * @return {boolean} + */ + empty() { + return this.q1.isEmpty(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(n)$ time for each $push()$ function call. + * $O(1)$ time for each $pop()$ function call. +* Space complexity: $O(n)$ + +--- + +## 2. Using One Queue + +::tabs-start + +```python +class MyStack: + + def __init__(self): + self.q = deque() + + def push(self, x: int) -> None: + self.q.append(x) + for _ in range(len(self.q) - 1): + self.q.append(self.q.popleft()) + + def pop(self) -> int: + return self.q.popleft() + + def top(self) -> int: + return self.q[0] + + def empty(self) -> bool: + return len(self.q) == 0 +``` + +```java +public class MyStack { + private Queue q; + + public MyStack() { + q = new LinkedList<>(); + } + + public void push(int x) { + q.offer(x); + for (int i = q.size() - 1; i > 0; i--) { + q.offer(q.poll()); + } + } + + public int pop() { + return q.poll(); + } + + public int top() { + return q.peek(); + } + + public boolean empty() { + return q.isEmpty(); + } +} +``` + +```cpp +class MyStack { + queue q; + +public: + MyStack() {} + + void push(int x) { + q.push(x); + for (int i = q.size() - 1; i > 0; i--) { + q.push(q.front()); + q.pop(); + } + } + + int pop() { + int top = q.front(); + q.pop(); + return top; + } + + int top() { + return q.front(); + } + + bool empty() { + return q.empty(); + } +}; +``` + +```javascript +class MyStack { + constructor() { + this.q = new Queue(); + } + + /** + * @param {number} x + * @return {void} + */ + push(x) { + this.q.push(x); + + for (let i = this.q.size() - 1; i > 0; i--) { + this.q.push(this.q.pop()); + } + } + + /** + * @return {number} + */ + pop() { + return this.q.pop(); + } + + /** + * @return {number} + */ + top() { + return this.q.front(); + } + + /** + * @return {boolean} + */ + empty() { + return this.q.isEmpty(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(n)$ time for each $push()$ function call. + * $O(1)$ time for each $pop()$ function call. +* Space complexity: $O(n)$ + +--- + +## 3. Queue Of Queues + +::tabs-start + +```python +class MyStack: + + def __init__(self): + self.q = None + + def push(self, x: int) -> None: + self.q = deque([x, self.q]) + + def pop(self) -> int: + top = self.q.popleft() + self.q = self.q.popleft() + return top + + def top(self) -> int: + return self.q[0] + + def empty(self) -> bool: + return not self.q +``` + +```java +public class MyStack { + private Queue q; + + public MyStack() { + q = null; + } + + public void push(int x) { + Queue newQueue = new LinkedList<>(); + newQueue.add(x); + newQueue.add(q); + q = newQueue; + } + + public int pop() { + if (q == null) return -1; + + int top = (int) q.poll(); + q = (Queue) q.poll(); + return top; + } + + public int top() { + if (q == null) return -1; + return (int) q.peek(); + } + + public boolean empty() { + return q == null; + } +} +``` + +```cpp +class MyStack { +private: + struct Node { + int val; + shared_ptr next; + Node(int v, shared_ptr n) : val(v), next(n) {} + }; + shared_ptr q; + +public: + MyStack() : q(nullptr) {} + + void push(int x) { + q = make_shared(x, q); + } + + int pop() { + if (!q) return -1; + int top = q->val; + q = q->next; + return top; + } + + int top() { + if (!q) return -1; + return q->val; + } + + bool empty() { + return q == nullptr; + } +}; +``` + +```javascript +class MyStack { + constructor() { + this.q = null; + } + + /** + * @param {number} x + * @return {void} + */ + push(x) { + const newQueue = new Queue(); + newQueue.enqueue(x); + newQueue.enqueue(this.q); + this.q = newQueue; + } + + /** + * @return {number} + */ + pop() { + if (this.q === null) return -1; + + const top = this.q.dequeue(); + this.q = this.q.dequeue(); + return top; + } + + /** + * @return {number} + */ + top() { + if (this.q === null) return -1; + return this.q.front(); + } + + /** + * @return {boolean} + */ + empty() { + return this.q === null; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $push()$ function call. + * $O(1)$ time for each $pop()$ function call. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/insert-delete-getrandom-o1.md b/articles/insert-delete-getrandom-o1.md new file mode 100644 index 000000000..000979641 --- /dev/null +++ b/articles/insert-delete-getrandom-o1.md @@ -0,0 +1,300 @@ +## 1. Hash Map + +::tabs-start + +```python +class RandomizedSet: + + def __init__(self): + self.numMap = {} + self.size = 0 + + def insert(self, val: int) -> bool: + if val in self.numMap: + return False + self.numMap[val] = 1 + self.size += 1 + return True + + def remove(self, val: int) -> bool: + if val not in self.numMap: + return False + del self.numMap[val] + self.size -= 1 + return True + + def getRandom(self) -> int: + idx = random.randint(0, self.size - 1) + return list(self.numMap.keys())[idx] +``` + +```java +public class RandomizedSet { + private HashMap numMap; + private int size; + + public RandomizedSet() { + numMap = new HashMap<>(); + size = 0; + } + + public boolean insert(int val) { + if (numMap.containsKey(val)) { + return false; + } + numMap.put(val, 1); + size++; + return true; + } + + public boolean remove(int val) { + if (!numMap.containsKey(val)) { + return false; + } + numMap.remove(val); + size--; + return true; + } + + public int getRandom() { + int idx = new Random().nextInt(size); + Iterator it = numMap.keySet().iterator(); + while (idx-- > 0) { + it.next(); + } + return it.next(); + } +} +``` + +```cpp +class RandomizedSet { +private: + unordered_map numMap; + int size; + +public: + RandomizedSet() : size(0) {} + + bool insert(int val) { + if (numMap.count(val)) return false; + numMap[val] = 1; + size++; + return true; + } + + bool remove(int val) { + if (!numMap.count(val)) return false; + numMap.erase(val); + size--; + return true; + } + + int getRandom() { + int idx = rand() % size; + auto it = numMap.begin(); + advance(it, idx); + return it->first; + } +}; +``` + +```javascript +class RandomizedSet { + constructor() { + this.numMap = new Map(); + this.size = 0; + } + + /** + * @param {number} val + * @return {boolean} + */ + insert(val) { + if (this.numMap.has(val)) return false; + this.numMap.set(val, 1); + this.size++; + return true; + } + + /** + * @param {number} val + * @return {boolean} + */ + remove(val) { + if (!this.numMap.has(val)) return false; + this.numMap.delete(val); + this.size--; + return true; + } + + /** + * @return {number} + */ + getRandom() { + const keys = Array.from(this.numMap.keys()); + const idx = Math.floor(Math.random() * this.size); + return keys[idx]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ for $getRandom()$, $O(1)$ for other function calls. +* Space complexity: $O(n)$ + +--- + +## 2. Hash Map + List + +::tabs-start + +```python +class RandomizedSet: + + def __init__(self): + self.numMap = {} + self.nums = [] + + def insert(self, val: int) -> bool: + if val in self.numMap: + return False + self.numMap[val] = len(self.nums) + self.nums.append(val) + return True + + def remove(self, val: int) -> bool: + if val not in self.numMap: + return False + idx = self.numMap[val] + last = self.nums[-1] + self.nums[idx] = last + self.numMap[last] = idx + self.nums.pop() + del self.numMap[val] + return True + + def getRandom(self) -> int: + return random.choice(self.nums) +``` + +```java +public class RandomizedSet { + private Map numMap; + private List nums; + private Random rand; + + public RandomizedSet() { + numMap = new HashMap<>(); + nums = new ArrayList<>(); + rand = new Random(); + } + + public boolean insert(int val) { + if (numMap.containsKey(val)) return false; + numMap.put(val, nums.size()); + nums.add(val); + return true; + } + + public boolean remove(int val) { + if (!numMap.containsKey(val)) return false; + int idx = numMap.get(val); + int last = nums.get(nums.size() - 1); + nums.set(idx, last); + numMap.put(last, idx); + nums.remove(nums.size() - 1); + numMap.remove(val); + return true; + } + + public int getRandom() { + return nums.get(rand.nextInt(nums.size())); + } +} +``` + +```cpp +class RandomizedSet { +private: + unordered_map numMap; + vector nums; + +public: + RandomizedSet() {} + + bool insert(int val) { + if (numMap.count(val)) return false; + numMap[val] = nums.size(); + nums.push_back(val); + return true; + } + + bool remove(int val) { + if (!numMap.count(val)) return false; + int idx = numMap[val]; + int last = nums.back(); + nums[idx] = last; + numMap[last] = idx; + nums.pop_back(); + numMap.erase(val); + return true; + } + + int getRandom() { + return nums[rand() % nums.size()]; + } +}; +``` + +```javascript +class RandomizedSet { + constructor() { + this.numMap = new Map(); + this.nums = []; + } + + /** + * @param {number} val + * @return {boolean} + */ + insert(val) { + if (this.numMap.has(val)) return false; + this.numMap.set(val, this.nums.length); + this.nums.push(val); + return true; + } + + /** + * @param {number} val + * @return {boolean} + */ + remove(val) { + if (!this.numMap.has(val)) return false; + const idx = this.numMap.get(val); + const last = this.nums[this.nums.length - 1]; + this.nums[idx] = last; + this.numMap.set(last, idx); + this.nums.pop(); + this.numMap.delete(val); + return true; + } + + /** + * @return {number} + */ + getRandom() { + return this.nums[Math.floor(Math.random() * this.nums.length)]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for each function call. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/insert-greatest-common-divisors-in-linked-list.md b/articles/insert-greatest-common-divisors-in-linked-list.md new file mode 100644 index 000000000..ffa61c7ce --- /dev/null +++ b/articles/insert-greatest-common-divisors-in-linked-list.md @@ -0,0 +1,155 @@ +## 1. Simulation + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def insertGreatestCommonDivisors(self, head: Optional[ListNode]) -> Optional[ListNode]: + def gcd(a, b): + while b > 0: + a, b = b, a % b + return a + + cur = head + while cur.next: + n1, n2 = cur.val, cur.next.val + cur.next = ListNode(gcd(n1, n2), cur.next) + cur = cur.next.next + + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode insertGreatestCommonDivisors(ListNode head) { + if (head == null) return null; + + ListNode cur = head; + + while (cur.next != null) { + int n1 = cur.val, n2 = cur.next.val; + int gcdValue = gcd(n1, n2); + ListNode newNode = new ListNode(gcdValue, cur.next); + cur.next = newNode; + cur = newNode.next; + } + + return head; + } + + private int gcd(int a, int b) { + while (b > 0) { + int temp = b; + b = a % b; + a = temp; + } + return a; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* insertGreatestCommonDivisors(ListNode* head) { + if (!head) return nullptr; + + ListNode* cur = head; + + while (cur->next) { + int n1 = cur->val, n2 = cur->next->val; + int gcdValue = gcd(n1, n2); + ListNode* newNode = new ListNode(gcdValue, cur->next); + cur->next = newNode; + cur = newNode->next; + } + + return head; + } + +private: + int gcd(int a, int b) { + while (b > 0) { + int temp = b; + b = a % b; + a = temp; + } + return a; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + insertGreatestCommonDivisors(head) { + if (!head) return null; + const gcd = (a, b) => { + while (b > 0) { + [a, b] = [b, a % b]; + } + return a; + }; + + let cur = head; + + while (cur.next) { + const n1 = cur.val, n2 = cur.next.val; + const gcdValue = gcd(n1, n2); + const newNode = new ListNode(gcdValue, cur.next); + cur.next = newNode; + cur = newNode.next; + } + + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * \log (min(a, b)))$ +* Space complexity: + * $O(n)$ space for the gcd ListNodes. + * $O(1)$ extra space. + +> Where $n$ is the length of the given list, and $a$ and $b$ are two numbers passed to the $gcd()$ function. \ No newline at end of file diff --git a/articles/insert-into-a-binary-search-tree.md b/articles/insert-into-a-binary-search-tree.md new file mode 100644 index 000000000..fe0604c3a --- /dev/null +++ b/articles/insert-into-a-binary-search-tree.md @@ -0,0 +1,363 @@ +## 1. Recursion + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]: + if not root: + return TreeNode(val) + + if val > root.val: + root.right = self.insertIntoBST(root.right, val) + else: + root.left = self.insertIntoBST(root.left, val) + + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode insertIntoBST(TreeNode root, int val) { + if (root == null) { + return new TreeNode(val); + } + + if (val > root.val) { + root.right = insertIntoBST(root.right, val); + } else { + root.left = insertIntoBST(root.left, val); + } + + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* insertIntoBST(TreeNode* root, int val) { + if (!root) { + return new TreeNode(val); + } + + if (val > root->val) { + root->right = insertIntoBST(root->right, val); + } else { + root->left = insertIntoBST(root->left, val); + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} val + * @return {TreeNode} + */ + insertIntoBST(root, val) { + if (!root) { + return new TreeNode(val); + } + + if (val > root.val) { + root.right = this.insertIntoBST(root.right, val); + } else { + root.left = this.insertIntoBST(root.left, val); + } + + return root; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode InsertIntoBST(TreeNode root, int val) { + if (root == null) { + return new TreeNode(val); + } + + if (val > root.val) { + root.right = InsertIntoBST(root.right, val); + } else { + root.left = InsertIntoBST(root.left, val); + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(h)$ +* Space complexity: $O(h)$ for the recursion stack. + +> Where $h$ is the height of the given binary search tree. + +--- + +## 2. Iteration + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]: + if not root: + return TreeNode(val) + + cur = root + while True: + if val > cur.val: + if not cur.right: + cur.right = TreeNode(val) + return root + cur = cur.right + else: + if not cur.left: + cur.left = TreeNode(val) + return root + cur = cur.left +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode insertIntoBST(TreeNode root, int val) { + if (root == null) { + return new TreeNode(val); + } + + TreeNode cur = root; + while (true) { + if (val > cur.val) { + if (cur.right == null) { + cur.right = new TreeNode(val); + return root; + } + cur = cur.right; + } else { + if (cur.left == null) { + cur.left = new TreeNode(val); + return root; + } + cur = cur.left; + } + } + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* insertIntoBST(TreeNode* root, int val) { + if (!root) { + return new TreeNode(val); + } + + TreeNode* cur = root; + while (true) { + if (val > cur->val) { + if (!cur->right) { + cur->right = new TreeNode(val); + return root; + } + cur = cur->right; + } else { + if (!cur->left) { + cur->left = new TreeNode(val); + return root; + } + cur = cur->left; + } + } + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} val + * @return {TreeNode} + */ + insertIntoBST(root, val) { + if (!root) { + return new TreeNode(val); + } + + let cur = root; + while (true) { + if (val > cur.val) { + if (!cur.right) { + cur.right = new TreeNode(val); + return root; + } + cur = cur.right; + } else { + if (!cur.left) { + cur.left = new TreeNode(val); + return root; + } + cur = cur.left; + } + } + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode InsertIntoBST(TreeNode root, int val) { + if (root == null) { + return new TreeNode(val); + } + + TreeNode cur = root; + while (true) { + if (val > cur.val) { + if (cur.right == null) { + cur.right = new TreeNode(val); + return root; + } + cur = cur.right; + } else { + if (cur.left == null) { + cur.left = new TreeNode(val); + return root; + } + cur = cur.left; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(h)$ +* Space complexity: $O(1)$ extra space. + +> Where $h$ is the height of the given binary search tree. \ No newline at end of file diff --git a/articles/insert-new-interval.md b/articles/insert-new-interval.md new file mode 100644 index 000000000..13f29a037 --- /dev/null +++ b/articles/insert-new-interval.md @@ -0,0 +1,808 @@ +## 1. Linear Search + +::tabs-start + +```python +class Solution: + def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]: + n = len(intervals) + i = 0 + res = [] + + while i < n and intervals[i][1] < newInterval[0]: + res.append(intervals[i]) + i += 1 + + while i < n and newInterval[1] >= intervals[i][0]: + newInterval[0] = min(newInterval[0], intervals[i][0]) + newInterval[1] = max(newInterval[1], intervals[i][1]) + i += 1 + res.append(newInterval) + + while i < n: + res.append(intervals[i]) + i += 1 + + return res +``` + +```java +public class Solution { + public int[][] insert(int[][] intervals, int[] newInterval) { + int n = intervals.length, i = 0; + List res = new ArrayList<>(); + + while (i < n && intervals[i][1] < newInterval[0]) { + res.add(intervals[i]); + i++; + } + + while (i < n && newInterval[1] >= intervals[i][0]) { + newInterval[0] = Math.min(newInterval[0], intervals[i][0]); + newInterval[1] = Math.max(newInterval[1], intervals[i][1]); + i++; + } + res.add(newInterval); + + while (i < n) { + res.add(intervals[i]); + i++; + } + + return res.toArray(new int[res.size()][]); + } +} +``` + +```cpp +class Solution { +public: + vector> insert(vector>& intervals, vector& newInterval) { + int n = intervals.size(), i = 0; + vector> res; + + while (i < n && intervals[i][1] < newInterval[0]) { + res.push_back(intervals[i]); + i++; + } + + while (i < n && newInterval[1] >= intervals[i][0]) { + newInterval[0] = min(newInterval[0], intervals[i][0]); + newInterval[1] = max(newInterval[1], intervals[i][1]); + i++; + } + res.push_back(newInterval); + + while (i < n) { + res.push_back(intervals[i]); + i++; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} intervals + * @param {number[]} newInterval + * @return {number[][]} + */ + insert(intervals, newInterval) { + let n = intervals.length, + i = 0, + res = []; + + while (i < n && intervals[i][1] < newInterval[0]) { + res.push(intervals[i]); + i++; + } + + while (i < n && newInterval[1] >= intervals[i][0]) { + newInterval[0] = Math.min(newInterval[0], intervals[i][0]); + newInterval[1] = Math.max(newInterval[1], intervals[i][1]); + i++; + } + res.push(newInterval); + + while (i < n) { + res.push(intervals[i]); + i++; + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int[][] Insert(int[][] intervals, int[] newInterval) { + var result = new List(); + + for(var i = 0; i < intervals.Length; i++) { + if(newInterval[1] < intervals[i][0]) { + result.Add(newInterval); + result.AddRange( + intervals.AsEnumerable().Skip(i).ToArray()); + + return result.ToArray(); + } + else if(newInterval[0] > intervals[i][1]) { + result.Add(intervals[i]); + } else { + newInterval[0] = Math.Min(intervals[i][0], newInterval[0]); + newInterval[1] = Math.Max(intervals[i][1], newInterval[1]); + } + } + + result.Add(newInterval); + + return result.ToArray(); + } +} +``` + +```go +func insert(intervals [][]int, newInterval []int) [][]int { + n := len(intervals) + i := 0 + var res [][]int + + for i < n && intervals[i][1] < newInterval[0] { + res = append(res, intervals[i]) + i++ + } + + for i < n && newInterval[1] >= intervals[i][0] { + newInterval[0] = min(newInterval[0], intervals[i][0]) + newInterval[1] = max(newInterval[1], intervals[i][1]) + i++ + } + res = append(res, newInterval) + + for i < n { + res = append(res, intervals[i]) + i++ + } + + return res +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun insert(intervals: Array, newInterval: IntArray): Array { + val res = mutableListOf() + var i = 0 + val n = intervals.size + + while (i < n && intervals[i][1] < newInterval[0]) { + res.add(intervals[i]) + i++ + } + + var newStart = newInterval[0] + var newEnd = newInterval[1] + while (i < n && newEnd >= intervals[i][0]) { + newStart = minOf(newStart, intervals[i][0]) + newEnd = maxOf(newEnd, intervals[i][1]) + i++ + } + res.add(intArrayOf(newStart, newEnd)) + + while (i < n) { + res.add(intervals[i]) + i++ + } + + return res.toTypedArray() + } +} +``` + +```swift +class Solution { + func insert(_ intervals: [[Int]], _ newInterval: [Int]) -> [[Int]] { + var intervals = intervals + var newInterval = newInterval + var res: [[Int]] = [] + var i = 0 + let n = intervals.count + + while i < n && intervals[i][1] < newInterval[0] { + res.append(intervals[i]) + i += 1 + } + + while i < n && newInterval[1] >= intervals[i][0] { + newInterval[0] = min(newInterval[0], intervals[i][0]) + newInterval[1] = max(newInterval[1], intervals[i][1]) + i += 1 + } + res.append(newInterval) + + while i < n { + res.append(intervals[i]) + i += 1 + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output list. + +--- + +## 2. Binary Search + +::tabs-start + +```python +class Solution: + def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]: + if not intervals: + return [newInterval] + + n = len(intervals) + target = newInterval[0] + left, right = 0, n - 1 + + while left <= right: + mid = (left + right) // 2 + if intervals[mid][0] < target: + left = mid + 1 + else: + right = mid - 1 + + intervals.insert(left, newInterval) + + res = [] + for interval in intervals: + if not res or res[-1][1] < interval[0]: + res.append(interval) + else: + res[-1][1] = max(res[-1][1], interval[1]) + return res +``` + +```java +public class Solution { + public int[][] insert(int[][] intervals, int[] newInterval) { + if (intervals.length == 0) { + return new int[][] { newInterval }; + } + + int n = intervals.length; + int target = newInterval[0]; + int left = 0, right = n - 1; + + while (left <= right) { + int mid = (left + right) / 2; + if (intervals[mid][0] < target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + + List result = new ArrayList<>(); + for (int i = 0; i < left; i++) { + result.add(intervals[i]); + } + result.add(newInterval); + for (int i = left; i < n; i++) { + result.add(intervals[i]); + } + + List merged = new ArrayList<>(); + for (int[] interval : result) { + if (merged.isEmpty() || + merged.get(merged.size() - 1)[1] < interval[0]) { + merged.add(interval); + } else { + merged.get(merged.size() - 1)[1] = Math.max( + merged.get(merged.size() - 1)[1], + interval[1] + ); + } + } + + return merged.toArray(new int[0][]); + } +} +``` + +```cpp +class Solution { +public: + vector> insert(vector>& intervals, vector& newInterval) { + if (intervals.empty()) { + return {newInterval}; + } + + int n = intervals.size(); + int target = newInterval[0]; + int left = 0, right = n - 1; + + while (left <= right) { + int mid = (left + right) / 2; + if (intervals[mid][0] < target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + + intervals.insert(intervals.begin() + left, newInterval); + + vector> res; + for (const auto& interval : intervals) { + if (res.empty() || res.back()[1] < interval[0]) { + res.push_back(interval); + } else { + res.back()[1] = max(res.back()[1], interval[1]); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} intervals + * @param {number[]} newInterval + * @return {number[][]} + */ + insert(intervals, newInterval) { + if (intervals.length === 0) { + return [newInterval]; + } + + let n = intervals.length; + let target = newInterval[0]; + let left = 0, + right = n - 1; + + while (left <= right) { + let mid = Math.floor((left + right) / 2); + if (intervals[mid][0] < target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + + intervals.splice(left, 0, newInterval); + + let res = []; + for (let interval of intervals) { + if (res.length === 0 || + res[res.length - 1][1] < interval[0]) { + res.push(interval); + } else { + res[res.length - 1][1] = Math.max( + res[res.length - 1][1], + interval[1], + ); + } + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int[][] Insert(int[][] intervals, int[] newInterval) { + if (intervals.Length == 0) { + return new int[][] { newInterval }; + } + + int n = intervals.Length; + int target = newInterval[0]; + int left = 0, right = n - 1; + + while (left <= right) { + int mid = (left + right) / 2; + if (intervals[mid][0] < target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + + List result = new List(); + for (int i = 0; i < left; i++) { + result.Add(intervals[i]); + } + + result.Add(newInterval); + for (int i = left; i < n; i++) { + result.Add(intervals[i]); + } + + List merged = new List(); + foreach (int[] interval in result) { + if (merged.Count == 0 || + merged[merged.Count - 1][1] < interval[0]) { + merged.Add(interval); + } else { + merged[merged.Count - 1][1] = + Math.Max(merged[merged.Count - 1][1], interval[1]); + } + } + + return merged.ToArray(); + } +} +``` + +```go +func insert(intervals [][]int, newInterval []int) [][]int { + if len(intervals) == 0 { + return [][]int{newInterval} + } + + n := len(intervals) + target := newInterval[0] + left, right := 0, n-1 + + for left <= right { + mid := (left + right) / 2 + if intervals[mid][0] < target { + left = mid + 1 + } else { + right = mid - 1 + } + } + + intervals = append(intervals[:left], append( + [][]int{newInterval}, intervals[left:]...)...) + + var res [][]int + for _, interval := range intervals { + if len(res) == 0 || res[len(res)-1][1] < interval[0] { + res = append(res, interval) + } else { + res[len(res)-1][1] = max(res[len(res)-1][1], interval[1]) + } + } + return res +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun insert(intervals: Array, newInterval: IntArray): Array { + if (intervals.isEmpty()) { + return arrayOf(newInterval) + } + + var left = 0 + var right = intervals.size - 1 + val target = newInterval[0] + + while (left <= right) { + val mid = (left + right) / 2 + if (intervals[mid][0] < target) { + left = mid + 1 + } else { + right = mid - 1 + } + } + + val result = intervals.toMutableList() + result.add(left, newInterval) + + val res = mutableListOf() + for (interval in result) { + if (res.isEmpty() || res.last()[1] < interval[0]) { + res.add(interval) + } else { + res[res.size - 1][1] = maxOf(res.last()[1], interval[1]) + } + } + return res.toTypedArray() + } +} +``` + +```swift +class Solution { + func insert(_ intervals: [[Int]], _ newInterval: [Int]) -> [[Int]] { + if intervals.isEmpty { + return [newInterval] + } + + var intervals = intervals + let target = newInterval[0] + var left = 0, right = intervals.count - 1 + + while left <= right { + let mid = (left + right) / 2 + if intervals[mid][0] < target { + left = mid + 1 + } else { + right = mid - 1 + } + } + + intervals.insert(newInterval, at: left) + + var res: [[Int]] = [] + for interval in intervals { + if res.isEmpty || res.last![1] < interval[0] { + res.append(interval) + } else { + res[res.count - 1][1] = max(res.last![1], interval[1]) + } + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output list. + +--- + +## 3. Greedy + +::tabs-start + +```python +class Solution: + def insert(self, intervals: List[List[int]], newInterval: List[int]) -> List[List[int]]: + res = [] + + for i in range(len(intervals)): + if newInterval[1] < intervals[i][0]: + res.append(newInterval) + return res + intervals[i:] + elif newInterval[0] > intervals[i][1]: + res.append(intervals[i]) + else: + newInterval = [ + min(newInterval[0], intervals[i][0]), + max(newInterval[1], intervals[i][1]), + ] + res.append(newInterval) + return res +``` + +```java +public class Solution { + public int[][] insert(int[][] intervals, int[] newInterval) { + List res = new ArrayList<>(); + for (int[] interval : intervals) { + if (newInterval == null || interval[1] < newInterval[0]) { + res.add(interval); + } else if (interval[0] > newInterval[1]) { + res.add(newInterval); + res.add(interval); + newInterval = null; + } else { + newInterval[0] = Math.min(interval[0], newInterval[0]); + newInterval[1] = Math.max(interval[1], newInterval[1]); + } + } + if (newInterval != null) res.add(newInterval); + return res.toArray(new int[res.size()][]); + } +} +``` + +```cpp +class Solution { +public: + vector> insert(vector>& intervals, vector& newInterval) { + vector> res; + int newStart = newInterval[0]; + int newEnd = newInterval[1]; + int n = intervals.size(); + for (int i = 0; i < n; i++) { + if (intervals[i][0] > newEnd) { + res.push_back(newInterval); + copy(intervals.begin() + i, intervals.end(), back_inserter(ans)); + return ans; + } else if (intervals[i][1] < newStart) { + res.push_back(intervals[i]); + } else { + newInterval[0] = min(newInterval[0], intervals[i][0]); + newInterval[1] = max(newInterval[1], intervals[i][1]); + } + } + res.push_back(newInterval); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} intervals + * @param {number[]} newInterval + * @return {number[][]} + */ + insert(intervals, newInterval) { + const res = []; + for (const interval of intervals) { + if (newInterval === null || interval[1] < newInterval[0]) { + res.push(interval); + } else if (interval[0] > newInterval[1]) { + res.push(newInterval); + res.push(interval); + newInterval = null; + } else { + newInterval[0] = Math.min(interval[0], newInterval[0]); + newInterval[1] = Math.max(interval[1], newInterval[1]); + } + } + if (newInterval !== null) res.push(newInterval); + return res; + } +} +``` + +```csharp +public class Solution { + public int[][] Insert(int[][] intervals, int[] newInterval) { + var result = new List(); + + for(var i = 0; i < intervals.Length; i++) { + if(newInterval[1] < intervals[i][0]) { + result.Add(newInterval); + result.AddRange(intervals.AsEnumerable().Skip(i).ToArray()); + return result.ToArray(); + } else if(newInterval[0] > intervals[i][1]) { + result.Add(intervals[i]); + } else { + newInterval[0] = Math.Min(intervals[i][0], newInterval[0]); + newInterval[1] = Math.Max(intervals[i][1], newInterval[1]); + } + } + + result.Add(newInterval); + return result.ToArray(); + } +} +``` + +```go +func insert(intervals [][]int, newInterval []int) [][]int { + var res [][]int + + for i := 0; i < len(intervals); i++ { + if newInterval[1] < intervals[i][0] { + res = append(res, newInterval) + return append(res, intervals[i:]...) + } else if newInterval[0] > intervals[i][1] { + res = append(res, intervals[i]) + } else { + newInterval = []int{ + min(newInterval[0], intervals[i][0]), + max(newInterval[1], intervals[i][1]), + } + } + } + res = append(res, newInterval) + return res +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun insert(intervals: Array, newInterval: IntArray): Array { + val res = mutableListOf() + + for (interval in intervals) { + if (newInterval[1] < interval[0]) { + res.add(newInterval) + return (res + + intervals.sliceArray( + intervals.indexOf(interval) until intervals.size + )).toTypedArray() + } else if (newInterval[0] > interval[1]) { + res.add(interval) + } else { + newInterval[0] = minOf(newInterval[0], interval[0]) + newInterval[1] = maxOf(newInterval[1], interval[1]) + } + } + res.add(newInterval) + return res.toTypedArray() + } +} +``` + +```swift +class Solution { + func insert(_ intervals: [[Int]], _ newInterval: [Int]) -> [[Int]] { + var res = [[Int]]() + var newInterval = newInterval + + for i in 0.. intervals[i][1] { + res.append(intervals[i]) + } else { + newInterval[0] = min(newInterval[0], intervals[i][0]) + newInterval[1] = max(newInterval[1], intervals[i][1]) + } + } + + res.append(newInterval) + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output list. \ No newline at end of file diff --git a/articles/insertion-sort-list.md b/articles/insertion-sort-list.md new file mode 100644 index 000000000..a567be9b0 --- /dev/null +++ b/articles/insertion-sort-list.md @@ -0,0 +1,411 @@ +## 1. Convert To Array + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def insertionSortList(self, head: Optional[ListNode]) -> Optional[ListNode]: + arr = [] + cur = head + while cur: + arr.append(cur.val) + cur = cur.next + + arr.sort() + cur = head + for val in arr: + cur.val = val + cur = cur.next + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode insertionSortList(ListNode head) { + List arr = new ArrayList<>(); + ListNode cur = head; + + while (cur != null) { + arr.add(cur.val); + cur = cur.next; + } + + Collections.sort(arr); + cur = head; + + for (int val : arr) { + cur.val = val; + cur = cur.next; + } + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* insertionSortList(ListNode* head) { + vector arr; + ListNode* cur = head; + + while (cur) { + arr.push_back(cur->val); + cur = cur->next; + } + + sort(arr.begin(), arr.end()); + cur = head; + + for (int val : arr) { + cur->val = val; + cur = cur->next; + } + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + insertionSortList(head) { + let arr = []; + let cur = head; + + while (cur) { + arr.push(cur.val); + cur = cur.next; + } + + arr.sort((a, b) => a - b); + cur = head; + + for (let val of arr) { + cur.val = val; + cur = cur.next; + } + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Swapping Values + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def insertionSortList(self, head: Optional[ListNode]) -> Optional[ListNode]: + cur = head.next + while cur: + tmp = head + while tmp != cur: + if tmp.val > cur.val: + tmp.val, cur.val = cur.val, tmp.val + tmp = tmp.next + cur = cur.next + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode insertionSortList(ListNode head) { + for (ListNode cur = head.next; cur != null; cur = cur.next) { + for (ListNode tmp = head; tmp != cur; tmp = tmp.next) { + if (tmp.val > cur.val) { + int swap = tmp.val; + tmp.val = cur.val; + cur.val = swap; + } + } + } + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* insertionSortList(ListNode* head) { + for (ListNode* cur = head->next; cur; cur = cur->next) { + for (ListNode* tmp = head; tmp != cur; tmp = tmp->next) { + if (tmp->val > cur->val) { + swap(tmp->val, cur->val); + } + } + } + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + insertionSortList(head) { + for (let cur = head.next; cur; cur = cur.next) { + for (let tmp = head; tmp !== cur; tmp = tmp.next) { + if (tmp.val > cur.val) { + [tmp.val, cur.val] = [cur.val, tmp.val]; + } + } + } + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Swapping Nodes + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def insertionSortList(self, head: Optional[ListNode]) -> Optional[ListNode]: + dummy = ListNode(0, head) + prev, cur = head, head.next + + while cur: + if cur.val >= prev.val: + prev, cur = cur, cur.next + continue + + tmp = dummy + while cur.val > tmp.next.val: + tmp = tmp.next + + prev.next = cur.next + cur.next = tmp.next + tmp.next = cur + cur = prev.next + + return dummy.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode insertionSortList(ListNode head) { + ListNode dummy = new ListNode(0, head); + ListNode prev = head, cur = head.next; + + while (cur != null) { + if (cur.val >= prev.val) { + prev = cur; + cur = cur.next; + continue; + } + + ListNode tmp = dummy; + while (tmp.next.val < cur.val) { + tmp = tmp.next; + } + + prev.next = cur.next; + cur.next = tmp.next; + tmp.next = cur; + cur = prev.next; + } + + return dummy.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* insertionSortList(ListNode* head) { + ListNode* dummy = new ListNode(0, head); + ListNode* prev = head; + ListNode* cur = head->next; + + while (cur) { + if (cur->val >= prev->val) { + prev = cur; + cur = cur->next; + continue; + } + + ListNode* tmp = dummy; + while (tmp->next->val < cur->val) { + tmp = tmp->next; + } + + prev->next = cur->next; + cur->next = tmp->next; + tmp->next = cur; + cur = prev->next; + } + + return dummy->next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + insertionSortList(head) { + let dummy = new ListNode(0, head); + let prev = head, cur = head.next; + + while (cur) { + if (cur.val >= prev.val) { + prev = cur; + cur = cur.next; + continue; + } + + let tmp = dummy; + while (tmp.next.val < cur.val) { + tmp = tmp.next; + } + + prev.next = cur.next; + cur.next = tmp.next; + tmp.next = cur; + cur = prev.next; + } + + return dummy.next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/integer-break.md b/articles/integer-break.md new file mode 100644 index 000000000..2330d8828 --- /dev/null +++ b/articles/integer-break.md @@ -0,0 +1,806 @@ +## 1. Recursion (Brute Force) + +::tabs-start + +```python +class Solution: + def integerBreak(self, n: int) -> int: + def dfs(num): + if num == 1: + return 1 + res = 0 if num == n else num + for i in range(1, num): + val = dfs(i) * dfs(num - i) + res = max(res, val) + return res + return dfs(n) +``` + +```java +public class Solution { + public int integerBreak(int n) { + return dfs(n, n); + } + + private int dfs(int num, int original) { + if (num == 1) return 1; + + int res = (num == original) ? 0 : num; + for (int i = 1; i < num; i++) { + int val = dfs(i, original) * dfs(num - i, original); + res = Math.max(res, val); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int integerBreak(int n) { + return dfs(n, n); + } + +private: + int dfs(int num, int original) { + if (num == 1) return 1; + + int res = (num == original) ? 0 : num; + for (int i = 1; i < num; i++) { + int val = dfs(i, original) * dfs(num - i, original); + res = max(res, val); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + integerBreak(n) { + const dfs = (num) => { + if (num === 1) return 1; + + let res = (num === n) ? 0 : num; + for (let i = 1; i < num; i++) { + const val = dfs(i) * dfs(num - i); + res = Math.max(res, val); + } + return res; + }; + + return dfs(n, n); + } +} +``` + +```csharp +public class Solution { + private int n; + + public int IntegerBreak(int n) { + this.n = n; + + int Dfs(int num) { + if (num == 1) return 1; + int res = num == n ? 0 : num; + for (int i = 1; i < num; i++) { + int val = Dfs(i) * Dfs(num - i); + res = Math.Max(res, val); + } + return res; + } + + return Dfs(n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Recursion + +::tabs-start + +```python +class Solution: + def integerBreak(self, n: int) -> int: + def dfs(num, i): + if min(num, i) == 0: + return 1 + + if i > num: + return dfs(num, num) + + return max(i * dfs(num - i, i), dfs(num, i - 1)) + + return dfs(n, n - 1) +``` + +```java +public class Solution { + public int integerBreak(int n) { + return dfs(n, n - 1); + } + + private int dfs(int num, int i) { + if (Math.min(num, i) == 0) { + return 1; + } + + if (i > num) { + return dfs(num, num); + } + + return Math.max(i * dfs(num - i, i), dfs(num, i - 1)); + } +} +``` + +```cpp +class Solution { +public: + int integerBreak(int n) { + return dfs(n, n - 1); + } + +private: + int dfs(int num, int i) { + if (min(num, i) == 0) { + return 1; + } + + if (i > num) { + return dfs(num, num); + } + + return max(i * dfs(num - i, i), dfs(num, i - 1)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + integerBreak(n) { + const dfs = (num, i) => { + if (Math.min(num, i) === 0) { + return 1; + } + + if (i > num) { + return dfs(num, num); + } + + return Math.max(i * dfs(num - i, i), dfs(num, i - 1)); + }; + + return dfs(n, n - 1); + } +} +``` + +```csharp +public class Solution { + public int IntegerBreak(int n) { + int Dfs(int num, int i) { + if (Math.Min(num, i) == 0) { + return 1; + } + + if (i > num) { + return Dfs(num, num); + } + + return Math.Max(i * Dfs(num - i, i), Dfs(num, i - 1)); + } + + return Dfs(n, n - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Dynamic Programming (Top-Down) - I + +::tabs-start + +```python +class Solution: + def integerBreak(self, n: int) -> int: + dp = {1: 1} + + def dfs(num): + if num in dp: + return dp[num] + + dp[num] = 0 if num == n else num + for i in range(1, num): + val = dfs(i) * dfs(num - i) + dp[num] = max(dp[num], val) + return dp[num] + + return dfs(n) +``` + +```java +public class Solution { + private Map dp; + + public int integerBreak(int n) { + dp = new HashMap<>(); + dp.put(1, 1); + return dfs(n, n); + } + + private int dfs(int num, int n) { + if (dp.containsKey(num)) { + return dp.get(num); + } + + int res = (num == n) ? 0 : num; + for (int i = 1; i < num; i++) { + int val = dfs(i, n) * dfs(num - i, n); + res = Math.max(res, val); + } + + dp.put(num, res); + return res; + } +} +``` + +```cpp +class Solution { + unordered_map dp; + +public: + int integerBreak(int n) { + dp[1] = 1; + return dfs(n, n); + } + +private: + int dfs(int num, int n) { + if (dp.find(num) != dp.end()) { + return dp[num]; + } + + int res = (num == n) ? 0 : num; + for (int i = 1; i < num; i++) { + int val = dfs(i, n) * dfs(num - i, n); + res = max(res, val); + } + + dp[num] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + integerBreak(n) { + const dp = new Map(); + dp.set(1, 1); + + const dfs = (num) => { + if (dp.has(num)) { + return dp.get(num); + } + + let res = (num === n) ? 0 : num; + for (let i = 1; i < num; i++) { + const val = dfs(i) * dfs(num - i); + res = Math.max(res, val); + } + + dp.set(num, res); + return res; + }; + + return dfs(n); + } +} +``` + +```csharp +public class Solution { + public int IntegerBreak(int n) { + Dictionary dp = new Dictionary(); + dp[1] = 1; + + int Dfs(int num) { + if (dp.ContainsKey(num)) { + return dp[num]; + } + + dp[num] = (num == n) ? 0 : num; + for (int i = 1; i < num; i++) { + int val = Dfs(i) * Dfs(num - i); + dp[num] = Math.Max(dp[num], val); + } + + return dp[num]; + } + + return Dfs(n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Top-Down) - II + +::tabs-start + +```python +class Solution: + def integerBreak(self, n: int) -> int: + dp = {} + def dfs(num, i): + if min(num, i) == 0: + return 1 + if (num, i) in dp: + return dp[(num, i)] + if i > num: + dp[(num, i)] = dfs(num, num) + return dp[(num, i)] + + dp[(num, i)] = max(i * dfs(num - i, i), dfs(num, i - 1)) + return dp[(num, i)] + + return dfs(n, n - 1) +``` + +```java +public class Solution { + private int[][] dp; + + public int integerBreak(int n) { + dp = new int[n + 1][n]; + for (int i = 0; i <= n; i++) { + for (int j = 0; j < n; j++) { + dp[i][j] = -1; + } + } + return dfs(n, n - 1); + } + + private int dfs(int num, int i) { + if (Math.min(num, i) == 0) { + return 1; + } + if (dp[num][i] != -1) { + return dp[num][i]; + } + if (i > num) { + dp[num][i] = dfs(num, num); + return dp[num][i]; + } + dp[num][i] = Math.max(i * dfs(num - i, i), dfs(num, i - 1)); + return dp[num][i]; + } +} +``` + +```cpp +class Solution { + vector> dp; + +public: + int integerBreak(int n) { + dp.assign(n + 1, vector(n, -1)); + return dfs(n, n - 1); + } + +private: + int dfs(int num, int i) { + if (min(num, i) == 0) { + return 1; + } + if (dp[num][i] != -1) { + return dp[num][i]; + } + if (i > num) { + dp[num][i] = dfs(num, num); + return dp[num][i]; + } + dp[num][i] = max(i * dfs(num - i, i), dfs(num, i - 1)); + return dp[num][i]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + integerBreak(n) { + const dp = Array.from({ length: n + 1 }, () => Array(n).fill(-1)); + + const dfs = (num, i) => { + if (Math.min(num, i) === 0) { + return 1; + } + if (dp[num][i] !== -1) { + return dp[num][i]; + } + if (i > num) { + dp[num][i] = dfs(num, num); + return dp[num][i]; + } + dp[num][i] = Math.max(i * dfs(num - i, i), dfs(num, i - 1)); + return dp[num][i]; + }; + + return dfs(n, n - 1); + } +} +``` + +```csharp +public class Solution { + public int IntegerBreak(int n) { + Dictionary<(int, int), int> dp = new Dictionary<(int, int), int>(); + + int Dfs(int num, int i) { + if (Math.Min(num, i) == 0) return 1; + + if (dp.ContainsKey((num, i))) return dp[(num, i)]; + + if (i > num) { + dp[(num, i)] = Dfs(num, num); + return dp[(num, i)]; + } + + dp[(num, i)] = Math.Max(i * Dfs(num - i, i), Dfs(num, i - 1)); + return dp[(num, i)]; + } + + return Dfs(n, n - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 5. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def integerBreak(self, n: int) -> int: + dp = [0] * (n + 1) + dp[1] = 1 + + for num in range(2, n + 1): + dp[num] = 0 if num == n else num + for i in range(1, num): + dp[num] = max(dp[num], dp[i] * dp[num - i]) + + return dp[n] +``` + +```java +public class Solution { + public int integerBreak(int n) { + int[] dp = new int[n + 1]; + dp[1] = 1; + + for (int num = 2; num <= n; num++) { + dp[num] = (num == n) ? 0 : num; + for (int i = 1; i < num; i++) { + dp[num] = Math.max(dp[num], dp[i] * dp[num - i]); + } + } + + return dp[n]; + } +} +``` + +```cpp +class Solution { +public: + int integerBreak(int n) { + vector dp(n + 1, 0); + dp[1] = 1; + + for (int num = 2; num <= n; num++) { + dp[num] = (num == n) ? 0 : num; + for (int i = 1; i < num; i++) { + dp[num] = max(dp[num], dp[i] * dp[num - i]); + } + } + + return dp[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + integerBreak(n) { + const dp = new Array(n + 1).fill(0); + dp[1] = 1; + + for (let num = 2; num <= n; num++) { + dp[num] = (num === n) ? 0 : num; + for (let i = 1; i < num; i++) { + dp[num] = Math.max(dp[num], dp[i] * dp[num - i]); + } + } + + return dp[n]; + } +} +``` + +```csharp +public class Solution { + public int IntegerBreak(int n) { + int[] dp = new int[n + 1]; + dp[1] = 1; + + for (int num = 2; num <= n; num++) { + dp[num] = num == n ? 0 : num; + for (int i = 1; i < num; i++) { + dp[num] = Math.Max(dp[num], dp[i] * dp[num - i]); + } + } + + return dp[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 6. Math + +::tabs-start + +```python +class Solution: + def integerBreak(self, n: int) -> int: + if n <= 3: + return n - 1 + + res = 1 + while n > 4: + res *= 3 + n -= 3 + return res * n +``` + +```java +public class Solution { + public int integerBreak(int n) { + if (n <= 3) return n - 1; + + int res = 1; + while (n > 4) { + res *= 3; + n -= 3; + } + return res * n; + } +} +``` + +```cpp +class Solution { +public: + int integerBreak(int n) { + if (n <= 3) return n - 1; + + int res = 1; + while (n > 4) { + res *= 3; + n -= 3; + } + return res * n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + integerBreak(n) { + if (n <= 3) return n - 1; + + let res = 1; + while (n > 4) { + res *= 3; + n -= 3; + } + return res * n; + } +} +``` + +```csharp +public class Solution { + public int IntegerBreak(int n) { + if (n <= 3) { + return n - 1; + } + + int res = 1; + while (n > 4) { + res *= 3; + n -= 3; + } + + return res * n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 7. Math (Optimal) + +::tabs-start + +```python +class Solution: + def integerBreak(self, n: int) -> int: + if n <= 3: + return n - 1 + + res = 3 ** (n // 3) + if n % 3 == 1: + return (res // 3) * 4 + + return res * max(1, (n % 3)) +``` + +```java +public class Solution { + public int integerBreak(int n) { + if (n <= 3) { + return n - 1; + } + + int res = (int) Math.pow(3, n / 3); + if (n % 3 == 1) { + return (res / 3) * 4; + } + + return res * Math.max(1, n % 3); + } +} +``` + +```cpp +class Solution { +public: + int integerBreak(int n) { + if (n <= 3) { + return n - 1; + } + + int res = pow(3, n / 3); + if (n % 3 == 1) { + return (res / 3) * 4; + } + + return res * max(1, n % 3); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + integerBreak(n) { + if (n <= 3) { + return n - 1; + } + + let res = Math.pow(3, Math.floor(n / 3)); + if (n % 3 === 1) { + return Math.floor(res / 3) * 4; + } + + return res * Math.max(1, n % 3); + } +} +``` + +```csharp +public class Solution { + public int IntegerBreak(int n) { + if (n <= 3) { + return n - 1; + } + + int res = (int)Math.Pow(3, n / 3); + + if (n % 3 == 1) { + return (res / 3) * 4; + } + + return res * Math.Max(1, n % 3); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/integer-to-roman.md b/articles/integer-to-roman.md new file mode 100644 index 000000000..6bb15bb30 --- /dev/null +++ b/articles/integer-to-roman.md @@ -0,0 +1,195 @@ +## 1. Math - I + +::tabs-start + +```python +class Solution: + def intToRoman(self, num: int) -> str: + symList = [ + ["I", 1], ["IV", 4], ["V", 5], ["IX", 9], + ["X", 10], ["XL", 40], ["L", 50], ["XC", 90], + ["C", 100], ["CD", 400], ["D", 500], ["CM", 900], + ["M", 1000] + ] + + res = "" + for sym, val in reversed(symList): + count = num // val + if count: + res += sym * count + num %= val + + return res +``` + +```java +public class Solution { + public String intToRoman(int num) { + String[][] symList = { + {"I", "1"}, {"IV", "4"}, {"V", "5"}, {"IX", "9"}, + {"X", "10"}, {"XL", "40"}, {"L", "50"}, {"XC", "90"}, + {"C", "100"}, {"CD", "400"}, {"D", "500"}, {"CM", "900"}, + {"M", "1000"} + }; + + StringBuilder res = new StringBuilder(); + for (int i = symList.length - 1; i >= 0; i--) { + String sym = symList[i][0]; + int val = Integer.parseInt(symList[i][1]); + int count = num / val; + if (count > 0) { + res.append(sym.repeat(count)); + num %= val; + } + } + + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string intToRoman(int num) { + vector> symList = { + {"I", 1}, {"IV", 4}, {"V", 5}, {"IX", 9}, + {"X", 10}, {"XL", 40}, {"L", 50}, {"XC", 90}, + {"C", 100}, {"CD", 400}, {"D", 500}, {"CM", 900}, + {"M", 1000} + }; + + string res = ""; + for (int i = symList.size() - 1; i >= 0; i--) { + string sym = symList[i].first; + int val = symList[i].second; + int count = num / val; + if (count > 0) { + res.append(count, sym[0]); + if (sym.size() == 2) res.append(1, sym[1]); + num %= val; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} num + * @return {string} + */ + intToRoman(num) { + const symList = [ + ["I", 1], ["IV", 4], ["V", 5], ["IX", 9], + ["X", 10], ["XL", 40], ["L", 50], ["XC", 90], + ["C", 100], ["CD", 400], ["D", 500], ["CM", 900], + ["M", 1000] + ]; + + let res = ""; + for (let i = symList.length - 1; i >= 0; i--) { + const [sym, val] = symList[i]; + let count = Math.floor(num / val); + if (count > 0) { + res += sym.repeat(count); + num %= val; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 2. Math - II + +::tabs-start + +```python +class Solution: + def intToRoman(self, num: int) -> str: + thousands = ["", "M", "MM", "MMM"] + hundreds = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"] + tens = ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"] + ones = ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"] + + return ( + thousands[num // 1000] + + hundreds[(num % 1000) // 100] + + tens[(num % 100) // 10] + + ones[num % 10] + ) +``` + +```java +public class Solution { + public String intToRoman(int num) { + String[] thousands = {"", "M", "MM", "MMM"}; + String[] hundreds = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"}; + String[] tens = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"}; + String[] ones = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}; + + return thousands[num / 1000] + + hundreds[(num % 1000) / 100] + + tens[(num % 100) / 10] + + ones[num % 10]; + } +} +``` + +```cpp +class Solution { +public: + string intToRoman(int num) { + string thousands[] = {"", "M", "MM", "MMM"}; + string hundreds[] = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"}; + string tens[] = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"}; + string ones[] = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}; + + return thousands[num / 1000] + + hundreds[(num % 1000) / 100] + + tens[(num % 100) / 10] + + ones[num % 10]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} num + * @return {string} + */ + intToRoman(num) { + const thousands = ["", "M", "MM", "MMM"]; + const hundreds = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"]; + const tens = ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"]; + const ones = ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"]; + + return thousands[Math.floor(num / 1000)] + + hundreds[Math.floor((num % 1000) / 100)] + + tens[Math.floor((num % 100) / 10)] + + ones[num % 10]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/interleaving-string.md b/articles/interleaving-string.md new file mode 100644 index 000000000..3d124cd01 --- /dev/null +++ b/articles/interleaving-string.md @@ -0,0 +1,1322 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def isInterleave(self, s1: str, s2: str, s3: str) -> bool: + + def dfs(i, j, k): + if k == len(s3): + return (i == len(s1)) and (j == len(s2)) + + if i < len(s1) and s1[i] == s3[k]: + if dfs(i + 1, j, k + 1): + return True + + if j < len(s2) and s2[j] == s3[k]: + if dfs(i, j + 1, k + 1): + return True + + return False + + return dfs(0, 0, 0) +``` + +```java +public class Solution { + public boolean isInterleave(String s1, String s2, String s3) { + + return dfs(0, 0, 0, s1, s2, s3); + } + + private boolean dfs(int i, int j, int k, String s1, String s2, String s3) { + if (k == s3.length()) { + return (i == s1.length()) && (j == s2.length()); + } + + if (i < s1.length() && s1.charAt(i) == s3.charAt(k)) { + if (dfs(i + 1, j, k + 1, s1, s2, s3)) { + return true; + } + } + + if (j < s2.length() && s2.charAt(j) == s3.charAt(k)) { + if (dfs(i, j + 1, k + 1, s1, s2, s3)) { + return true; + } + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool isInterleave(string s1, string s2, string s3) { + return dfs(0, 0, 0, s1, s2, s3); + } + + bool dfs(int i, int j, int k, string& s1, string& s2, string& s3) { + if (k == s3.length()) { + return (i == s1.length()) && (j == s2.length()); + } + + if (i < s1.length() && s1[i] == s3[k]) { + if (dfs(i + 1, j, k + 1, s1, s2, s3)) { + return true; + } + } + + if (j < s2.length() && s2[j] == s3[k]) { + if (dfs(i, j + 1, k + 1, s1, s2, s3)) { + return true; + } + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s1 + * @param {string} s2 + * @param {string} s3 + * @return {boolean} + */ + isInterleave(s1, s2, s3) { + + const dfs = (i, j, k) => { + if (k === s3.length) { + return (i === s1.length) && (j === s2.length); + } + + if (i < s1.length && s1[i] === s3[k]) { + if (dfs(i + 1, j, k + 1)) { + return true; + } + } + + if (j < s2.length && s2[j] === s3[k]) { + if (dfs(i, j + 1, k + 1)) { + return true; + } + } + + return false; + } + + return dfs(0, 0, 0); + } +} +``` + +```csharp +public class Solution { + public bool IsInterleave(string s1, string s2, string s3) { + return dfs(0, 0, 0, s1, s2, s3); + } + + private bool dfs(int i, int j, int k, string s1, string s2, string s3) { + if (k == s3.Length) { + return (i == s1.Length) && (j == s2.Length); + } + + if (i < s1.Length && s1[i] == s3[k]) { + if (dfs(i + 1, j, k + 1, s1, s2, s3)) { + return true; + } + } + + if (j < s2.Length && s2[j] == s3[k]) { + if (dfs(i, j + 1, k + 1, s1, s2, s3)) { + return true; + } + } + + return false; + } +} +``` + +```go +func isInterleave(s1, s2, s3 string) bool { + var dfs func(i, j, k int) bool + dfs = func(i, j, k int) bool { + if k == len(s3) { + return i == len(s1) && j == len(s2) + } + + if i < len(s1) && s1[i] == s3[k] { + if dfs(i+1, j, k+1) { + return true + } + } + + if j < len(s2) && s2[j] == s3[k] { + if dfs(i, j+1, k+1) { + return true + } + } + + return false + } + + return dfs(0, 0, 0) +} +``` + +```kotlin +class Solution { + fun isInterleave(s1: String, s2: String, s3: String): Boolean { + fun dfs(i: Int, j: Int, k: Int): Boolean { + if (k == s3.length) { + return i == s1.length && j == s2.length + } + + if (i < s1.length && s1[i] == s3[k]) { + if (dfs(i + 1, j, k + 1)) { + return true + } + } + + if (j < s2.length && s2[j] == s3[k]) { + if (dfs(i, j + 1, k + 1)) { + return true + } + } + + return false + } + + return dfs(0, 0, 0) + } +} +``` + +```swift +class Solution { + func isInterleave(_ s1: String, _ s2: String, _ s3: String) -> Bool { + let n1 = s1.count, n2 = s2.count, n3 = s3.count + if n1 + n2 != n3 { return false } + + let s1 = Array(s1), s2 = Array(s2), s3 = Array(s3) + + func dfs(_ i: Int, _ j: Int, _ k: Int) -> Bool { + if k == n3 { + return i == n1 && j == n2 + } + + if i < n1 && s1[i] == s3[k] { + if dfs(i + 1, j, k + 1) { + return true + } + } + + if j < n2 && s2[j] == s3[k] { + if dfs(i, j + 1, k + 1) { + return true + } + } + + return false + } + + return dfs(0, 0, 0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ {m + n})$ +* Space complexity: $O(m + n)$ + +> Where $m$ is the length of the string $s1$ and $n$ is the length of the string $s2$. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def isInterleave(self, s1: str, s2: str, s3: str) -> bool: + if len(s1) + len(s2) != len(s3): + return False + + dp = {} + def dfs(i, j, k): + if k == len(s3): + return (i == len(s1)) and (j == len(s2)) + if (i, j) in dp: + return dp[(i, j)] + + res = False + if i < len(s1) and s1[i] == s3[k]: + res = dfs(i + 1, j, k + 1) + if not res and j < len(s2) and s2[j] == s3[k]: + res = dfs(i, j + 1, k + 1) + + dp[(i, j)] = res + return res + + return dfs(0, 0, 0) +``` + +```java +public class Solution { + private Boolean[][] dp; + + public boolean isInterleave(String s1, String s2, String s3) { + int m = s1.length(), n = s2.length(); + if (m + n != s3.length()) return false; + dp = new Boolean[m + 1][n + 1]; + return dfs(0, 0, 0, s1, s2, s3); + } + + private boolean dfs(int i, int j, int k, String s1, String s2, String s3) { + if (k == s3.length()) { + return (i == s1.length()) && (j == s2.length()); + } + if (dp[i][j] != null) { + return dp[i][j]; + } + + boolean res = false; + if (i < s1.length() && s1.charAt(i) == s3.charAt(k)) { + res = dfs(i + 1, j, k + 1, s1, s2, s3); + } + if (!res && j < s2.length() && s2.charAt(j) == s3.charAt(k)) { + res = dfs(i, j + 1, k + 1, s1, s2, s3); + } + + dp[i][j] = res; + return res; + } +} +``` + +```cpp +class Solution { + vector> dp; + +public: + bool isInterleave(string s1, string s2, string s3) { + int m = s1.length(), n = s2.length(); + if (m + n != s3.length()) return false; + dp = vector>(m + 1, vector(n + 1, -1)); + return dfs(0, 0, 0, s1, s2, s3); + } + + bool dfs(int i, int j, int k, string& s1, string& s2, string& s3) { + if (k == s3.length()) { + return (i == s1.length()) && (j == s2.length()); + } + if (dp[i][j] != -1) { + return dp[i][j]; + } + + bool res = false; + if (i < s1.length() && s1[i] == s3[k]) { + res = dfs(i + 1, j, k + 1, s1, s2, s3); + } + if (!res && j < s2.length() && s2[j] == s3[k]) { + res = dfs(i, j + 1, k + 1, s1, s2, s3); + } + + dp[i][j] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s1 + * @param {string} s2 + * @param {string} s3 + * @return {boolean} + */ + isInterleave(s1, s2, s3) { + const m = s1.length, n = s2.length; + if (m + n !== s3.length) return false; + + const dp = Array.from({ length: m + 1 }, () => + Array(n + 1).fill(-1)); + const dfs = (i, j, k) => { + if (k === s3.length) { + return (i === s1.length) && (j === s2.length); + } + if (dp[i][j] !== -1) { + return dp[i][j]; + } + + let res = false; + if (i < s1.length && s1[i] === s3[k]) { + res = dfs(i + 1, j, k + 1); + } + + if (!res && j < s2.length && s2[j] === s3[k]) { + res = dfs(i, j + 1, k + 1); + } + dp[i][j] = res; + return res; + } + + return dfs(0, 0, 0); + } +} +``` + +```csharp +public class Solution { + private bool?[,] dp; + + public bool IsInterleave(string s1, string s2, string s3) { + int m = s1.Length, n = s2.Length; + if (m + n != s3.Length) return false; + dp = new bool?[m + 1, n + 1]; + return dfs(0, 0, 0, s1, s2, s3); + } + + private bool dfs(int i, int j, int k, string s1, string s2, string s3) { + if (k == s3.Length) { + return (i == s1.Length) && (j == s2.Length); + } + if (dp[i, j].HasValue) { + return dp[i, j].Value; + } + + bool res = false; + if (i < s1.Length && s1[i] == s3[k]) { + res = dfs(i + 1, j, k + 1, s1, s2, s3); + } + if (!res && j < s2.Length && s2[j] == s3[k]) { + res = dfs(i, j + 1, k + 1, s1, s2, s3); + } + + dp[i, j] = res; + return res; + } +} +``` + +```go +func isInterleave(s1, s2, s3 string) bool { + if len(s1)+len(s2) != len(s3) { + return false + } + + m, n := len(s1), len(s2) + dp := make([][]int, m+1) + for i := range dp { + dp[i] = make([]int, n+1) + for j := range dp[i] { + dp[i][j] = -1 + } + } + + var dfs func(i, j, k int) bool + dfs = func(i, j, k int) bool { + if k == len(s3) { + return i == len(s1) && j == len(s2) + } + + if dp[i][j] != -1 { + return dp[i][j] == 1 + } + + res := false + if i < len(s1) && s1[i] == s3[k] { + res = dfs(i+1, j, k+1) + } + if !res && j < len(s2) && s2[j] == s3[k] { + res = dfs(i, j+1, k+1) + } + + if res { + dp[i][j] = 1 + } else { + dp[i][j] = 0 + } + + return res + } + + return dfs(0, 0, 0) +} +``` + +```kotlin +class Solution { + fun isInterleave(s1: String, s2: String, s3: String): Boolean { + if (s1.length + s2.length != s3.length) { + return false + } + + val m = s1.length + val n = s2.length + val dp = Array(m + 1) { IntArray(n + 1) { -1 } } + + fun dfs(i: Int, j: Int, k: Int): Boolean { + if (k == s3.length) { + return i == m && j == n + } + + if (dp[i][j] != -1) { + return dp[i][j] == 1 + } + + var res = false + if (i < s1.length && s1[i] == s3[k]) { + res = dfs(i + 1, j, k + 1) + } + if (!res && j < s2.length && s2[j] == s3[k]) { + res = dfs(i, j + 1, k + 1) + } + + dp[i][j] = if (res) 1 else 0 + + return res + } + + return dfs(0, 0, 0) + } +} +``` + +```swift +class Solution { + func isInterleave(_ s1: String, _ s2: String, _ s3: String) -> Bool { + let m = s1.count, n = s2.count, l = s3.count + if m + n != l { return false } + + let s1 = Array(s1), s2 = Array(s2), s3 = Array(s3) + var dp = Array(repeating: Array(repeating: nil as Bool?, count: n + 1), count: m + 1) + + func dfs(_ i: Int, _ j: Int, _ k: Int) -> Bool { + if k == l { + return i == m && j == n + } + if let cached = dp[i][j] { + return cached + } + + var res = false + if i < m && s1[i] == s3[k] { + res = dfs(i + 1, j, k + 1) + } + if !res && j < n && s2[j] == s3[k] { + res = dfs(i, j + 1, k + 1) + } + + dp[i][j] = res + return res + } + + return dfs(0, 0, 0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the length of the string $s1$ and $n$ is the length of the string $s2$. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def isInterleave(self, s1: str, s2: str, s3: str) -> bool: + if len(s1) + len(s2) != len(s3): + return False + + dp = [[False] * (len(s2) + 1) for i in range(len(s1) + 1)] + dp[len(s1)][len(s2)] = True + + for i in range(len(s1), -1, -1): + for j in range(len(s2), -1, -1): + if i < len(s1) and s1[i] == s3[i + j] and dp[i + 1][j]: + dp[i][j] = True + if j < len(s2) and s2[j] == s3[i + j] and dp[i][j + 1]: + dp[i][j] = True + return dp[0][0] +``` + +```java +public class Solution { + public boolean isInterleave(String s1, String s2, String s3) { + int m = s1.length(), n = s2.length(); + if (m + n != s3.length()) { + return false; + } + + boolean[][] dp = new boolean[m + 1][n + 1]; + dp[m][n] = true; + + for (int i = m; i >= 0; i--) { + for (int j = n; j >= 0; j--) { + if (i < m && s1.charAt(i) == s3.charAt(i + j) && dp[i + 1][j]) { + dp[i][j] = true; + } + if (j < n && s2.charAt(j) == s3.charAt(i + j) && dp[i][j + 1]) { + dp[i][j] = true; + } + } + } + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + bool isInterleave(string s1, string s2, string s3) { + int m = s1.length(), n = s2.length(); + if (m + n != s3.length()) { + return false; + } + + vector> dp(m + 1, vector(n + 1, false)); + dp[m][n] = true; + + for (int i = m; i >= 0; i--) { + for (int j = n; j >= 0; j--) { + if (i < m && s1[i] == s3[i + j] && dp[i + 1][j]) { + dp[i][j] = true; + } + if (j < n && s2[j] == s3[i + j] && dp[i][j + 1]) { + dp[i][j] = true; + } + } + } + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s1 + * @param {string} s2 + * @param {string} s3 + * @return {boolean} + */ + isInterleave(s1, s2, s3) { + let m = s1.length, n = s2.length; + if (m + n !== s3.length) { + return false; + } + + const dp = Array.from({ length: m + 1 }, () => + Array(n + 1).fill(false)); + dp[m][n] = true; + + for (let i = m; i >= 0; i--) { + for (let j = n; j >= 0; j--) { + if (i < m && s1[i] === s3[i + j] && dp[i + 1][j]) { + dp[i][j] = true; + } + if (j < n && s2[j] === s3[i + j] && dp[i][j + 1]) { + dp[i][j] = true; + } + } + } + return dp[0][0]; + } +} +``` + +```csharp +public class Solution { + public bool IsInterleave(string s1, string s2, string s3) { + int m = s1.Length, n = s2.Length; + if (m + n != s3.Length) { + return false; + } + + bool[,] dp = new bool[m + 1, n + 1]; + dp[m, n] = true; + + for (int i = m; i >= 0; i--) { + for (int j = n; j >= 0; j--) { + if (i < m && s1[i] == s3[i + j] && dp[i + 1, j]) { + dp[i, j] = true; + } + if (j < n && s2[j] == s3[i + j] && dp[i, j + 1]) { + dp[i, j] = true; + } + } + } + return dp[0, 0]; + } +} +``` + +```go +func isInterleave(s1 string, s2 string, s3 string) bool { + if len(s1) + len(s2) != len(s3) { + return false + } + + dp := make([][]bool, len(s1)+1) + for i := range dp { + dp[i] = make([]bool, len(s2)+1) + } + dp[len(s1)][len(s2)] = true + + for i := len(s1); i >= 0; i-- { + for j := len(s2); j >= 0; j-- { + if i < len(s1) && s1[i] == s3[i+j] && dp[i+1][j] { + dp[i][j] = true + } + if j < len(s2) && s2[j] == s3[i+j] && dp[i][j+1] { + dp[i][j] = true + } + } + } + + return dp[0][0] +} +``` + +```kotlin +class Solution { + fun isInterleave(s1: String, s2: String, s3: String): Boolean { + if (s1.length + s2.length != s3.length) { + return false + } + + val dp = Array(s1.length + 1) { BooleanArray(s2.length + 1) } + dp[s1.length][s2.length] = true + + for (i in s1.length downTo 0) { + for (j in s2.length downTo 0) { + if (i < s1.length && s1[i] == s3[i + j] && dp[i + 1][j]) { + dp[i][j] = true + } + if (j < s2.length && s2[j] == s3[i + j] && dp[i][j + 1]) { + dp[i][j] = true + } + } + } + + return dp[0][0] + } +} +``` + +```swift +class Solution { + func isInterleave(_ s1: String, _ s2: String, _ s3: String) -> Bool { + let m = s1.count, n = s2.count, l = s3.count + if m + n != l { return false } + + let s1 = Array(s1), s2 = Array(s2), s3 = Array(s3) + var dp = Array(repeating: Array(repeating: false, count: n + 1), count: m + 1) + dp[m][n] = true + + for i in stride(from: m, through: 0, by: -1) { + for j in stride(from: n, through: 0, by: -1) { + if i < m && s1[i] == s3[i + j] && dp[i + 1][j] { + dp[i][j] = true + } + if j < n && s2[j] == s3[i + j] && dp[i][j + 1] { + dp[i][j] = true + } + } + } + return dp[0][0] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the length of the string $s1$ and $n$ is the length of the string $s2$. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def isInterleave(self, s1: str, s2: str, s3: str) -> bool: + m, n = len(s1), len(s2) + if m + n != len(s3): + return False + if n < m: + s1, s2 = s2, s1 + m, n = n, m + + dp = [False for _ in range(n + 1)] + dp[n] = True + for i in range(m, -1, -1): + nextDp = [False for _ in range(n + 1)] + nextDp[n] = True + for j in range(n, -1, -1): + if i < m and s1[i] == s3[i + j] and dp[j]: + nextDp[j] = True + if j < n and s2[j] == s3[i + j] and nextDp[j + 1]: + nextDp[j] = True + dp = nextDp + return dp[0] +``` + +```java +public class Solution { + public boolean isInterleave(String s1, String s2, String s3) { + int m = s1.length(), n = s2.length(); + if (m + n != s3.length()) return false; + if (n < m) { + String temp = s1; + s1 = s2; + s2 = temp; + int tempLength = m; + m = n; + n = tempLength; + } + + boolean[] dp = new boolean[n + 1]; + dp[n] = true; + for (int i = m; i >= 0; i--) { + boolean[] nextDp = new boolean[n + 1]; + nextDp[n] = true; + for (int j = n; j >= 0; j--) { + if (i < m && s1.charAt(i) == s3.charAt(i + j) && dp[j]) { + nextDp[j] = true; + } + if (j < n && s2.charAt(j) == s3.charAt(i + j) && nextDp[j + 1]) { + nextDp[j] = true; + } + } + dp = nextDp; + } + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + bool isInterleave(string s1, string s2, string s3) { + int m = s1.size(), n = s2.size(); + if (m + n != s3.size()) return false; + if (n < m) { + swap(s1, s2); + swap(m, n); + } + + vector dp(n + 1); + dp[n] = true; + for (int i = m; i >= 0; --i) { + vector nextDp(n + 1); + nextDp[n] = true; + for (int j = n; j >= 0; --j) { + if (i < m && s1[i] == s3[i + j] && dp[j]) { + nextDp[j] = true; + } + if (j < n && s2[j] == s3[i + j] && nextDp[j + 1]) { + nextDp[j] = true; + } + } + dp = nextDp; + } + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s1 + * @param {string} s2 + * @param {string} s3 + * @return {boolean} + */ + isInterleave(s1, s2, s3) { + let m = s1.length, n = s2.length; + if (m + n !== s3.length) return false; + if (n < m) { + [s1, s2] = [s2, s1]; + [m, n] = [n, m]; + } + + let dp = Array(n + 1).fill(false); + dp[n] = true; + for (let i = m; i >= 0; i--) { + let nextDp = Array(n + 1).fill(false); + nextDp[n] = true; + for (let j = n; j >= 0; j--) { + if (i < m && s1[i] === s3[i + j] && dp[j]) { + nextDp[j] = true; + } + if (j < n && s2[j] === s3[i + j] && nextDp[j + 1]) { + nextDp[j] = true; + } + } + dp = nextDp; + } + return dp[0]; + } +} +``` + +```csharp +public class Solution { + public bool IsInterleave(string s1, string s2, string s3) { + int m = s1.Length, n = s2.Length; + if (m + n != s3.Length) return false; + if (n < m) { + var temp = s1; + s1 = s2; + s2 = temp; + int tempLength = m; + m = n; + n = tempLength; + } + + bool[] dp = new bool[n + 1]; + dp[n] = true; + for (int i = m; i >= 0; i--) { + bool[] nextDp = new bool[n + 1]; + nextDp[n] = true; + for (int j = n; j >= 0; j--) { + if (i < m && s1[i] == s3[i + j] && dp[j]) { + nextDp[j] = true; + } + if (j < n && s2[j] == s3[i + j] && nextDp[j + 1]) { + nextDp[j] = true; + } + } + dp = nextDp; + } + return dp[0]; + } +} +``` + +```go +func isInterleave(s1 string, s2 string, s3 string) bool { + m, n := len(s1), len(s2) + if m + n != len(s3) { + return false + } + if n < m { + s1, s2 = s2, s1 + m, n = n, m + } + + dp := make([]bool, n+1) + dp[n] = true + for i := m; i >= 0; i-- { + nextDp := make([]bool, n+1) + nextDp[n] = true + for j := n; j >= 0; j-- { + if i < m && s1[i] == s3[i+j] && dp[j] { + nextDp[j] = true + } + if j < n && s2[j] == s3[i+j] && nextDp[j+1] { + nextDp[j] = true + } + } + dp = nextDp + } + return dp[0] +} +``` + +```kotlin +class Solution { + fun isInterleave(s1: String, s2: String, s3: String): Boolean { + var s1 = s1 + var s2 = s2 + var m = s1.length + var n = s2.length + if (m + n != s3.length) { + return false + } + if (n < m) { + val temp = s1 + s1 = s2 + s2 = temp + + val temp1 = m + m = n + n = temp1 + } + + val dp = BooleanArray(n + 1) + dp[n] = true + for (i in m downTo 0) { + val nextDp = BooleanArray(n + 1) + nextDp[n] = true + for (j in n downTo 0) { + if (i < m && s1[i] == s3[i + j] && dp[j]) { + nextDp[j] = true + } + if (j < n && s2[j] == s3[i + j] && nextDp[j + 1]) { + nextDp[j] = true + } + } + System.arraycopy(nextDp, 0, dp, 0, n + 1) + } + return dp[0] + } +} +``` + +```swift +class Solution { + func isInterleave(_ s1: String, _ s2: String, _ s3: String) -> Bool { + var s1 = Array(s1), s2 = Array(s2), s3 = Array(s3) + var m = s1.count, n = s2.count + if m + n != s3.count { return false } + if n < m { + swap(&s1, &s2) + swap(&m, &n) + } + + var dp = Array(repeating: false, count: n + 1) + dp[n] = true + + for i in stride(from: m, through: 0, by: -1) { + var nextDp = Array(repeating: false, count: n + 1) + nextDp[n] = true + for j in stride(from: n, through: 0, by: -1) { + if i < m && s1[i] == s3[i + j] && dp[j] { + nextDp[j] = true + } + if j < n && s2[j] == s3[i + j] && nextDp[j + 1] { + nextDp[j] = true + } + } + dp = nextDp + } + return dp[0] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(min(m, n))$ + +> Where $m$ is the length of the string $s1$ and $n$ is the length of the string $s2$. + +--- + +## 5. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def isInterleave(self, s1: str, s2: str, s3: str) -> bool: + m, n = len(s1), len(s2) + if m + n != len(s3): + return False + if n < m: + s1, s2 = s2, s1 + m, n = n, m + + dp = [False for _ in range(n + 1)] + dp[n] = True + for i in range(m, -1, -1): + nextDp = True + for j in range(n - 1, -1, -1): + res = False + if i < m and s1[i] == s3[i + j] and dp[j]: + res = True + if j < n and s2[j] == s3[i + j] and nextDp: + res = True + dp[j] = res + nextDp = dp[j] + return dp[0] +``` + +```java +public class Solution { + public boolean isInterleave(String s1, String s2, String s3) { + int m = s1.length(), n = s2.length(); + if (m + n != s3.length()) return false; + if (n < m) { + String temp = s1; + s1 = s2; + s2 = temp; + int tempLen = m; + m = n; + n = tempLen; + } + + boolean[] dp = new boolean[n + 1]; + dp[n] = true; + for (int i = m; i >= 0; i--) { + boolean nextDp = true; + for (int j = n - 1; j >= 0; j--) { + boolean res = false; + if (i < m && s1.charAt(i) == s3.charAt(i + j) && dp[j]) { + res = true; + } + if (j < n && s2.charAt(j) == s3.charAt(i + j) && nextDp) { + res = true; + } + dp[j] = res; + nextDp = dp[j]; + } + } + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + bool isInterleave(string s1, string s2, string s3) { + int m = s1.size(), n = s2.size(); + if (m + n != s3.size()) return false; + if (n < m) { + swap(s1, s2); + swap(m, n); + } + + vector dp(n + 1, false); + dp[n] = true; + for (int i = m; i >= 0; i--) { + bool nextDp = true; + for (int j = n - 1; j >= 0; j--) { + bool res = false; + if (i < m && s1[i] == s3[i + j] && dp[j]) { + res = true; + } + if (j < n && s2[j] == s3[i + j] && nextDp) { + res = true; + } + dp[j] = res; + nextDp = dp[j]; + } + } + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s1 + * @param {string} s2 + * @param {string} s3 + * @return {boolean} + */ + isInterleave(s1, s2, s3) { + let m = s1.length, n = s2.length; + if (m + n !== s3.length) return false; + if (n < m) { + [s1, s2] = [s2, s1]; + [m, n] = [n, m]; + } + + let dp = Array(n + 1).fill(false); + dp[n] = true; + for (let i = m; i >= 0; i--) { + let nextDp = true; + for (let j = n - 1; j >= 0; j--) { + let res = false; + if (i < m && s1[i] === s3[i + j] && dp[j]) { + res = true; + } + if (j < n && s2[j] === s3[i + j] && nextDp) { + res = true; + } + dp[j] = res; + nextDp = dp[j]; + } + } + return dp[0]; + } +} +``` + +```csharp +public class Solution { + public bool IsInterleave(string s1, string s2, string s3) { + int m = s1.Length, n = s2.Length; + if (m + n != s3.Length) return false; + if (n < m) { + var temp = s1; + s1 = s2; + s2 = temp; + int tempLen = m; + m = n; + n = tempLen; + } + + bool[] dp = new bool[n + 1]; + dp[n] = true; + for (int i = m; i >= 0; i--) { + bool nextDp = true; + for (int j = n - 1; j >= 0; j--) { + bool res = false; + if (i < m && s1[i] == s3[i + j] && dp[j]) { + res = true; + } + if (j < n && s2[j] == s3[i + j] && nextDp) { + res = true; + } + dp[j] = res; + nextDp = dp[j]; + } + } + return dp[0]; + } +} +``` + +```go +func isInterleave(s1, s2, s3 string) bool { + m, n := len(s1), len(s2) + if m+n != len(s3) { + return false + } + if n < m { + s1, s2 = s2, s1 + m, n = n, m + } + + dp := make([]bool, n+1) + dp[n] = true + for i := m; i >= 0; i-- { + nextDp := true + for j := n - 1; j >= 0; j-- { + res := false + if i < m && s1[i] == s3[i+j] && dp[j] { + res = true + } + if j < n && s2[j] == s3[i+j] && nextDp { + res = true + } + dp[j] = res + nextDp = dp[j] + } + } + return dp[0] +} +``` + +```kotlin +class Solution { + fun isInterleave(s1: String, s2: String, s3: String): Boolean { + var s1 = s1 + var s2 = s2 + var m = s1.length + var n = s2.length + if (m + n != s3.length) { + return false + } + if (n < m) { + val temp = s1 + s1 = s2 + s2 = temp + + val temp1 = m + m = n + n = temp1 + } + + val dp = BooleanArray(n + 1) + dp[n] = true + for (i in m downTo 0) { + var nextDp = true + for (j in n - 1 downTo 0) { + var res = false + if (i < m && s1[i] == s3[i + j] && dp[j]) { + res = true + } + if (j < n && s2[j] == s3[i + j] && nextDp) { + res = true + } + dp[j] = res + nextDp = dp[j] + } + } + return dp[0] + } +} +``` + +```swift +class Solution { + func isInterleave(_ s1: String, _ s2: String, _ s3: String) -> Bool { + var s1 = Array(s1), s2 = Array(s2), s3 = Array(s3) + var m = s1.count, n = s2.count + if m + n != s3.count { return false } + if n < m { + swap(&s1, &s2) + swap(&m, &n) + } + + var dp = Array(repeating: false, count: n + 1) + dp[n] = true + + for i in stride(from: m, through: 0, by: -1) { + var nextDp = true + for j in stride(from: n - 1, through: 0, by: -1) { + var res = false + if i < m && s1[i] == s3[i + j] && dp[j] { + res = true + } + if j < n && s2[j] == s3[i + j] && nextDp { + res = true + } + dp[j] = res + nextDp = dp[j] + } + } + return dp[0] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(min(m, n))$ + +> Where $m$ is the length of the string $s1$ and $n$ is the length of the string $s2$. \ No newline at end of file diff --git a/articles/intersection-of-two-arrays.md b/articles/intersection-of-two-arrays.md new file mode 100644 index 000000000..5892704ec --- /dev/null +++ b/articles/intersection-of-two-arrays.md @@ -0,0 +1,566 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]: + res = set() + for i in nums1: + for j in nums2: + if i == j: + res.add(i) + break + return list(res) +``` + +```java +public class Solution { + public int[] intersection(int[] nums1, int[] nums2) { + Set res = new HashSet<>(); + for (int i : nums1) { + for (int j : nums2) { + if (i == j) { + res.add(i); + break; + } + } + } + int[] result = new int[res.size()]; + int idx = 0; + for (int num : res) { + result[idx++] = num; + } + return result; + } +} +``` + +```cpp +class Solution { +public: + vector intersection(vector& nums1, vector& nums2) { + unordered_set res; + for (int i : nums1) { + for (int j : nums2) { + if (i == j) { + res.insert(i); + break; + } + } + } + return vector(res.begin(), res.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[]} + */ + intersection(nums1, nums2) { + const res = new Set(); + for (const i of nums1) { + for (const j of nums2) { + if (i === j) { + res.add(i); + break; + } + } + } + return Array.from(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums1$ and $m$ is the size of the array $nums2$. + +--- + +## 2. Sorting + Two Pointers + +::tabs-start + +```python +class Solution: + def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]: + nums1.sort() + nums2.sort() + + n, m = len(nums1), len(nums2) + res, i, j = [], 0, 0 + + while i < n and j < m: + while j < m and nums2[j] < nums1[i]: + j += 1 + if j < m: + if nums1[i] == nums2[j]: + res.append(nums1[i]) + i += 1 + while i < n and nums1[i] == nums1[i - 1]: + i += 1 + + return res +``` + +```java +public class Solution { + public int[] intersection(int[] nums1, int[] nums2) { + Arrays.sort(nums1); + Arrays.sort(nums2); + + List res = new ArrayList<>(); + int i = 0, j = 0; + + while (i < nums1.length && j < nums2.length) { + while (j < nums2.length && nums2[j] < nums1[i]) { + j++; + } + if (j < nums2.length) { + if (nums1[i] == nums2[j]) { + res.add(nums1[i]); + } + i++; + while (i < nums1.length && nums1[i] == nums1[i - 1]) { + i++; + } + } + } + + return res.stream().mapToInt(Integer::intValue).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector intersection(vector& nums1, vector& nums2) { + sort(nums1.begin(), nums1.end()); + sort(nums2.begin(), nums2.end()); + + int n = nums1.size(), m = nums2.size(); + vector res; + int i = 0, j = 0; + + while (i < n && j < m) { + while (j < m && nums2[j] < nums1[i]) { + ++j; + } + if (j < m) { + if (nums1[i] == nums2[j]) { + res.push_back(nums1[i]); + } + ++i; + while (i < n && nums1[i] == nums1[i - 1]) { + ++i; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[]} + */ + intersection(nums1, nums2) { + nums1.sort((a, b) => a - b); + nums2.sort((a, b) => a - b); + + const res = []; + let i = 0, j = 0; + + while (i < nums1.length && j < nums2.length) { + while (j < nums2.length && nums2[j] < nums1[i]) { + j++; + } + if (j < nums2.length) { + if (nums1[i] === nums2[j]) { + res.push(nums1[i]); + } + i++; + while (i < nums1.length && nums1[i] === nums1[i - 1]) { + i++; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + m \log m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums1$ and $m$ is the size of the array $nums2$. + +--- + +## 3. Hash Set + +::tabs-start + +```python +class Solution: + def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]: + set1 = set(nums1) + set2 = set(nums2) + + res = [] + for num in set1: + if num in set2: + res.append(num) + return res +``` + +```java +public class Solution { + public int[] intersection(int[] nums1, int[] nums2) { + Set set1 = new HashSet<>(); + for (int num : nums1) { + set1.add(num); + } + + Set set2 = new HashSet<>(); + for (int num : nums2) { + set2.add(num); + } + + List res = new ArrayList<>(); + for (int num : set1) { + if (set2.contains(num)) { + res.add(num); + } + } + + return res.stream().mapToInt(Integer::intValue).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector intersection(vector& nums1, vector& nums2) { + unordered_set set1(nums1.begin(), nums1.end()); + unordered_set set2(nums2.begin(), nums2.end()); + + vector res; + for (int num : set1) { + if (set2.find(num) != set2.end()) { + res.push_back(num); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[]} + */ + intersection(nums1, nums2) { + const set1 = new Set(nums1); + const set2 = new Set(nums2); + + const res = []; + for (const num of set1) { + if (set2.has(num)) { + res.push(num); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums1$ and $m$ is the size of the array $nums2$. + +--- + +## 4. Hash Map + +::tabs-start + +```python +class Solution: + def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]: + seen = defaultdict(int) + for num in nums1: + seen[num] = 1 + + res = [] + for num in nums2: + if seen[num] == 1: + seen[num] = 0 + res.append(num) + return res +``` + +```java +public class Solution { + public int[] intersection(int[] nums1, int[] nums2) { + Map seen = new HashMap<>(); + for (int num : nums1) { + seen.put(num, 1); + } + + List res = new ArrayList<>(); + for (int num : nums2) { + if (seen.getOrDefault(num, 0) == 1) { + seen.put(num, 0); + res.add(num); + } + } + + return res.stream().mapToInt(Integer::intValue).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector intersection(vector& nums1, vector& nums2) { + unordered_map seen; + for (int num : nums1) { + seen[num] = 1; + } + + vector res; + for (int num : nums2) { + if (seen[num] == 1) { + seen[num] = 0; + res.push_back(num); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[]} + */ + intersection(nums1, nums2) { + const seen = {}; + for (const num of nums1) { + seen[num] = 1; + } + + const res = []; + for (const num of nums2) { + if (seen[num] === 1) { + seen[num] = 0; + res.push(num); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums1$ and $m$ is the size of the array $nums2$. + +--- + +## 5. Hash Set (Optimal) + +::tabs-start + +```python +class Solution: + def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]: + seen = set(nums1) + + res = [] + for num in nums2: + if num in seen: + res.append(num) + seen.remove(num) + return res +``` + +```java +public class Solution { + public int[] intersection(int[] nums1, int[] nums2) { + Set seen = new HashSet<>(); + for (int num : nums1) { + seen.add(num); + } + + List res = new ArrayList<>(); + for (int num : nums2) { + if (seen.contains(num)) { + res.add(num); + seen.remove(num); + } + } + + return res.stream().mapToInt(Integer::intValue).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector intersection(vector& nums1, vector& nums2) { + unordered_set seen(nums1.begin(), nums1.end()); + vector res; + + for (int num : nums2) { + if (seen.count(num)) { + res.push_back(num); + seen.erase(num); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[]} + */ + intersection(nums1, nums2) { + const seen = new Set(nums1); + const res = []; + + for (const num of nums2) { + if (seen.has(num)) { + res.push(num); + seen.delete(num); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums1$ and $m$ is the size of the array $nums2$. + +--- + +## 6. Built-In Functions + +::tabs-start + +```python +class Solution: + def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]: + return list(set(nums1) & set(nums2)) +``` + +```java +public class Solution { + public int[] intersection(int[] nums1, int[] nums2) { + Set set1 = new HashSet<>(); + for (Integer n : nums1) { + set1.add(n); + } + + Set set2 = new HashSet<>(); + for (Integer n : nums2) { + set2.add(n); + } + + set1.retainAll(set2); + int[] res= new int[set1.size()]; + int idx = 0; + for (int s : set1) { + res[idx++] = s; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector intersection(vector& nums1, vector& nums2) { + set set1(nums1.begin(), nums1.end()), set2(nums2.begin(), nums2.end()); + vector res; + set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(), back_inserter(res)); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[]} + */ + intersection(nums1, nums2) { + const set2 = new Set(nums2); + return [...new Set(nums1)].filter(num => set2.has(num)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ in average case, $O(n * m)$ in worst case. +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums1$ and $m$ is the size of the array $nums2$. \ No newline at end of file diff --git a/articles/intersection-of-two-linked-lists.md b/articles/intersection-of-two-linked-lists.md new file mode 100644 index 000000000..855f2a43f --- /dev/null +++ b/articles/intersection-of-two-linked-lists.md @@ -0,0 +1,554 @@ +## 1. Brute Force + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None +class Solution: + def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]: + while headA: + cur = headB + while cur: + if headA == cur: + return headA + cur = cur.next + headA = headA.next + return None +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ +public class Solution { + public ListNode getIntersectionNode(ListNode headA, ListNode headB) { + while (headA != null) { + ListNode cur = headB; + while (cur != null) { + if (headA == cur) { + return headA; + } + cur = cur.next; + } + headA = headA.next; + } + return null; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) { + while (headA) { + ListNode* cur = headB; + while (cur) { + if (headA == cur) { + return headA; + } + cur = cur->next; + } + headA = headA->next; + } + return nullptr; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} headA + * @param {ListNode} headB + * @return {ListNode} + */ + getIntersectionNode(headA, headB) { + while (headA) { + let cur = headB; + while (cur) { + if (headA === cur) { + return headA; + } + cur = cur.next; + } + headA = headA.next; + } + return null; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ is the length of the first list and $n$ is the length of the second list. + +--- + +## 2. Hash Set + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None +class Solution: + def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]: + nodeSet = set() + cur = headA + while cur: + nodeSet.add(cur) + cur = cur.next + + cur = headB + while cur: + if cur in nodeSet: + return cur + cur = cur.next + + return None +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ +public class Solution { + public ListNode getIntersectionNode(ListNode headA, ListNode headB) { + HashSet nodeSet = new HashSet<>(); + + ListNode cur = headA; + while (cur != null) { + nodeSet.add(cur); + cur = cur.next; + } + + cur = headB; + while (cur != null) { + if (nodeSet.contains(cur)) { + return cur; + } + cur = cur.next; + } + + return null; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) { + unordered_set nodeSet; + + ListNode* cur = headA; + while (cur) { + nodeSet.insert(cur); + cur = cur->next; + } + + cur = headB; + while (cur) { + if (nodeSet.find(cur) != nodeSet.end()) { + return cur; + } + cur = cur->next; + } + + return nullptr; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} headA + * @param {ListNode} headB + * @return {ListNode} + */ + getIntersectionNode(headA, headB) { + const nodeSet = new Set(); + + let cur = headA; + while (cur) { + nodeSet.add(cur); + cur = cur.next; + } + + cur = headB; + while (cur) { + if (nodeSet.has(cur)) { + return cur; + } + cur = cur.next; + } + + return null; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(m)$ + +> Where $m$ is the length of the first list and $n$ is the length of the second list. + +--- + +## 3. Two Pointers - I + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None +class Solution: + def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]: + def getLength(head): + length, cur = 0, head + while cur: + length += 1 + cur = cur.next + return length + + m = getLength(headA) + n = getLength(headB) + l1, l2 = headA, headB + + if m < n: + m, n = n, m + l1, l2 = headB, headA + + while m - n: + m -= 1 + l1 = l1.next + + while l1 != l2: + l1 = l1.next + l2 = l2.next + + return l1 +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ +public class Solution { + private int getLength(ListNode head) { + int length = 0; + while (head != null) { + length++; + head = head.next; + } + return length; + } + + public ListNode getIntersectionNode(ListNode headA, ListNode headB) { + int m = getLength(headA); + int n = getLength(headB); + ListNode l1 = headA, l2 = headB; + + if (m < n) { + int temp = m; m = n; n = temp; + ListNode tempNode = l1; l1 = l2; l2 = tempNode; + } + + while (m > n) { + l1 = l1.next; + m--; + } + + while (l1 != null && l1 != l2) { + l1 = l1.next; + l2 = l2.next; + } + + return l1; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { + int getLength(ListNode* head) { + int length = 0; + while (head) { + length++; + head = head->next; + } + return length; + } + +public: + ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) { + int m = getLength(headA), n = getLength(headB); + ListNode* l1 = headA, *l2 = headB; + + if (m < n) { + swap(m, n); + swap(l1, l2); + } + + while (m-- > n) { + l1 = l1->next; + } + + while (l1 && l1 != l2) { + l1 = l1->next; + l2 = l2->next; + } + + return l1; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} headA + * @param {ListNode} headB + * @return {ListNode} + */ + getIntersectionNode(headA, headB) { + const getLength = (head) => { + let length = 0, cur = head; + while (cur) { + length++; + cur = cur.next; + } + return length; + }; + + let m = getLength(headA); + let n = getLength(headB); + let l1 = headA, l2 = headB; + + if (m < n) { + [m, n] = [n, m]; + [l1, l2] = [l2, l1]; + } + + while (m-- > n) { + l1 = l1.next; + } + + while (l1 && l1 !== l2) { + l1 = l1.next; + l2 = l2.next; + } + + return l1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ is the length of the first list and $n$ is the length of the second list. + +--- + +## 4. Two Pointers - II + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None +class Solution: + def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]: + l1, l2 = headA, headB + while l1 != l2: + l1 = l1.next if l1 else headB + l2 = l2.next if l2 else headA + return l1 +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ +public class Solution { + public ListNode getIntersectionNode(ListNode headA, ListNode headB) { + ListNode l1 = headA, l2 = headB; + while (l1 != l2) { + l1 = (l1 != null) ? l1.next : headB; + l2 = (l2 != null) ? l2.next : headA; + } + return l1; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) { + ListNode* l1 = headA; + ListNode* l2 = headB; + while (l1 != l2) { + l1 = l1 ? l1->next : headB; + l2 = l2 ? l2->next : headA; + } + return l1; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} headA + * @param {ListNode} headB + * @return {ListNode} + */ + getIntersectionNode(headA, headB) { + let l1 = headA, l2 = headB; + while (l1 !== l2) { + l1 = l1 ? l1.next : headB; + l2 = l2 ? l2.next : headA; + } + return l1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ is the length of the first list and $n$ is the length of the second list. \ No newline at end of file diff --git a/articles/invert-a-binary-tree.md b/articles/invert-a-binary-tree.md new file mode 100644 index 000000000..33ee15f84 --- /dev/null +++ b/articles/invert-a-binary-tree.md @@ -0,0 +1,758 @@ +## 1. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]: + if not root: + return None + queue = deque([root]) + while queue: + node = queue.popleft() + node.left, node.right = node.right, node.left + if node.left: + queue.append(node.left) + if node.right: + queue.append(node.right) + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public TreeNode invertTree(TreeNode root) { + if (root == null) { + return null; + } + Queue queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()) { + TreeNode node = queue.poll(); + TreeNode temp = node.left; + node.left = node.right; + node.right = temp; + if (node.left != null) { + queue.offer(node.left); + } + if (node.right != null) { + queue.offer(node.right); + } + } + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + TreeNode* invertTree(TreeNode* root) { + if (!root) return nullptr; + queue queue; + queue.push(root); + while (!queue.empty()) { + TreeNode* node = queue.front(); + queue.pop(); + swap(node->left, node->right); + if (node->left) queue.push(node->left); + if (node->right) queue.push(node->right); + } + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @return {TreeNode} + */ + invertTree(root) { + if (root == null) return null; + const queue = new Queue([root]); + while (!queue.isEmpty()) { + let node = queue.pop(); + [node.left, node.right] = [node.right, node.left]; + if (node.left != null) queue.push(node.left); + if (node.right != null) queue.push(node.right); + } + return root; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public TreeNode InvertTree(TreeNode root) { + if (root == null) return null; + Queue queue = new Queue(); + queue.Enqueue(root); + while (queue.Count > 0) { + TreeNode node = queue.Dequeue(); + TreeNode temp = node.left; + node.left = node.right; + node.right = temp; + if (node.left != null) queue.Enqueue(node.left); + if (node.right != null) queue.Enqueue(node.right); + } + return root; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func invertTree(root *TreeNode) *TreeNode { + if root == nil { + return nil + } + queue := arrayqueue.New() + queue.Enqueue(root) + + for queue.Size() > 0 { + node, _ := queue.Dequeue() + current := node.(*TreeNode) + current.Left, current.Right = current.Right, current.Left + + if current.Left != nil { + queue.Enqueue(current.Left) + } + if current.Right != nil { + queue.Enqueue(current.Right) + } + } + return root +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun invertTree(root: TreeNode?): TreeNode? { + if (root == null) { + return null + } + val queue: ArrayDeque = ArrayDeque() + queue.add(root) + + while (queue.isNotEmpty()) { + val node = queue.removeFirst() + node?.let { + val temp = it.left + it.left = it.right + it.right = temp + queue.add(it.left) + queue.add(it.right) + } + } + return root + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func invertTree(_ root: TreeNode?) -> TreeNode? { + guard let root = root else { return nil } + var queue = Deque() + queue.append(root) + + while !queue.isEmpty { + let node = queue.removeFirst() + (node.left, node.right) = (node.right, node.left) + + if let left = node.left { + queue.append(left) + } + if let right = node.right { + queue.append(right) + } + } + return root + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]: + if not root: return None + + root.left, root.right = root.right, root.left + + self.invertTree(root.left) + self.invertTree(root.right) + + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public TreeNode invertTree(TreeNode root) { + if (root == null) return null; + + TreeNode temp = root.left; + root.left = root.right; + root.right = temp; + + invertTree(root.left); + invertTree(root.right); + + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + TreeNode* invertTree(TreeNode* root) { + if (!root) return nullptr; + + swap(root->left, root->right); + invertTree(root->left); + invertTree(root->right); + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @return {TreeNode} + */ + invertTree(root) { + if (!root) return null; + + [root.left, root.right] = [root.right, root.left]; + this.invertTree(root.left); + this.invertTree(root.right); + + return root; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public TreeNode InvertTree(TreeNode root) { + if (root == null) return null; + + TreeNode temp = root.left; + root.left = root.right; + root.right = temp; + + InvertTree(root.left); + InvertTree(root.right); + + return root; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ + +func invertTree(root *TreeNode) *TreeNode { + if root == nil { + return nil + } + + root.Left, root.Right = root.Right, root.Left + invertTree(root.Left) + invertTree(root.Right) + + return root +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ + +class Solution { + fun invertTree(root: TreeNode?): TreeNode? { + if (root == null) return null + + val temp = root.left + root.left = root.right + root.right = temp + + invertTree(root.left) + invertTree(root.right) + + return root + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func invertTree(_ root: TreeNode?) -> TreeNode? { + guard let root = root else { return nil } + + (root.left, root.right) = (root.right, root.left) + + invertTree(root.left) + invertTree(root.right) + + return root + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]: + if not root: + return None + stack = [root] + while stack: + node = stack.pop() + node.left, node.right = node.right, node.left + if node.left: + stack.append(node.left) + if node.right: + stack.append(node.right) + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public TreeNode invertTree(TreeNode root) { + if (root == null) return null; + Stack stack = new Stack<>(); + stack.push(root); + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + TreeNode temp = node.left; + node.left = node.right; + node.right = temp; + if (node.left != null) stack.push(node.left); + if (node.right != null) stack.push(node.right); + } + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + TreeNode* invertTree(TreeNode* root) { + if (!root) return nullptr; + stack stack; + stack.push(root); + while (!stack.empty()) { + TreeNode* node = stack.top(); + stack.pop(); + swap(node->left, node->right); + if (node->left) stack.push(node->left); + if (node->right) stack.push(node->right); + } + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @return {TreeNode} + */ + invertTree(root) { + if (!root) return null; + const stack = [root]; + while (stack.length) { + const node = stack.pop(); + [node.left, node.right] = [node.right, node.left]; + if (node.left) stack.push(node.left); + if (node.right) stack.push(node.right); + } + return root; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public TreeNode InvertTree(TreeNode root) { + if (root == null) return null; + Stack stack = new Stack(); + stack.Push(root); + while (stack.Count > 0) { + TreeNode node = stack.Pop(); + TreeNode temp = node.left; + node.left = node.right; + node.right = temp; + if (node.left != null) stack.Push(node.left); + if (node.right != null) stack.Push(node.right); + } + return root; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func invertTree(root *TreeNode) *TreeNode { + if root == nil { + return nil + } + + root.Left, root.Right = root.Right, root.Left + + invertTree(root.Left) + invertTree(root.Right) + + return root +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun invertTree(root: TreeNode?): TreeNode? { + if (root == null) return null + + root.left = root.right.also { root.right = root.left } + + invertTree(root.left) + invertTree(root.right) + + return root + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func invertTree(_ root: TreeNode?) -> TreeNode? { + guard let root = root else { return nil } + var stack: [TreeNode] = [root] + + while !stack.isEmpty { + let node = stack.removeLast() + (node.left, node.right) = (node.right, node.left) + + if let left = node.left { + stack.append(left) + } + if let right = node.right { + stack.append(right) + } + } + return root + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/ipo.md b/articles/ipo.md new file mode 100644 index 000000000..87670000f --- /dev/null +++ b/articles/ipo.md @@ -0,0 +1,467 @@ +## 1. Two Heaps + +::tabs-start + +```python +class Solution: + def findMaximizedCapital(self, k: int, w: int, profits: List[int], capital: List[int]) -> int: + maxProfit = [] # Max heap + minCapital = [(c, p) for c, p in zip(capital, profits)] # Min heap + heapq.heapify(minCapital) + + for _ in range(k): + while minCapital and minCapital[0][0] <= w: + c, p = heapq.heappop(minCapital) + heapq.heappush(maxProfit, -p) + + if not maxProfit: + break + + w += -heapq.heappop(maxProfit) + + return w +``` + +```java +public class Solution { + public int findMaximizedCapital(int k, int w, int[] profits, int[] capital) { + PriorityQueue minCapital = new PriorityQueue<>((a, b) -> a[0] - b[0]); // Min heap + PriorityQueue maxProfit = new PriorityQueue<>((a, b) -> b - a); // Max heap + + for (int i = 0; i < capital.length; i++) { + minCapital.offer(new int[]{capital[i], profits[i]}); + } + + for (int i = 0; i < k; i++) { + while (!minCapital.isEmpty() && minCapital.peek()[0] <= w) { + maxProfit.offer(minCapital.poll()[1]); + } + if (maxProfit.isEmpty()) { + break; + } + w += maxProfit.poll(); + } + + return w; + } +} +``` + +```cpp +class Solution { +public: + int findMaximizedCapital(int k, int w, vector& profits, vector& capital) { + priority_queue maxProfit; // Max heap + priority_queue, vector>, greater<>> minCapital; // Min heap + + for (int i = 0; i < capital.size(); i++) { + minCapital.emplace(capital[i], profits[i]); + } + + for (int i = 0; i < k; i++) { + while (!minCapital.empty() && minCapital.top().first <= w) { + maxProfit.push(minCapital.top().second); + minCapital.pop(); + } + if (maxProfit.empty()) { + break; + } + w += maxProfit.top(); + maxProfit.pop(); + } + + return w; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} k + * @param {number} w + * @param {number[]} profits + * @param {number[]} capital + * @return {number} + */ + findMaximizedCapital(k, w, profits, capital) { + const minCapital = new PriorityQueue((a, b) => a[0] - b[0]); // Min heap + const maxProfit = new PriorityQueue((a, b) => b - a); // Max heap + + for (let i = 0; i < capital.length; i++) { + minCapital.enqueue([capital[i], profits[i]]); + } + + for (let i = 0; i < k; i++) { + while (!minCapital.isEmpty() && minCapital.front()[0] <= w) { + maxProfit.enqueue(minCapital.dequeue()[1]); + } + if (maxProfit.isEmpty()) { + break; + } + w += maxProfit.dequeue(); + } + + return w; + } +} +``` + +```csharp +public class Solution { + public int FindMaximizedCapital(int k, int w, int[] profits, int[] capital) { + var minCapital = new List<(int c, int p)>(); + for (int i = 0; i < capital.Length; i++) { + minCapital.Add((capital[i], profits[i])); + } + + // Min-heap by capital + minCapital.Sort((a, b) => a.c.CompareTo(b.c)); + + // Max-heap by profit + var maxProfit = new PriorityQueue(Comparer.Create((a, b) => b.CompareTo(a))); + int iPtr = 0; + + for (int i = 0; i < k; i++) { + while (iPtr < minCapital.Count && minCapital[iPtr].c <= w) { + maxProfit.Enqueue(minCapital[iPtr].p, minCapital[iPtr].p); + iPtr++; + } + + if (maxProfit.Count == 0) { + break; + } + + w += maxProfit.Dequeue(); + } + + return w; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Two Heaps (Optimal) + +::tabs-start + +```python +class Solution: + def findMaximizedCapital(self, k: int, w: int, profits: List[int], capital: List[int]) -> int: + class Node: + def __init__(self, idx): + self.idx = idx + + def __lt__(self, other): + if capital[self.idx] != capital[other.idx]: + return capital[self.idx] < capital[other.idx] + return self.idx < other.idx + + minCapital = [] + maxProfit = [] + for i in range(len(capital)): + heapq.heappush(minCapital, Node(i)) + + for _ in range(k): + while minCapital and capital[minCapital[0].idx] <= w: + idx = heapq.heappop(minCapital).idx + heapq.heappush(maxProfit, -profits[idx]) + + if not maxProfit: + break + w += -heapq.heappop(maxProfit) + + return w +``` + +```java +public class Solution { + public int findMaximizedCapital(int k, int w, int[] profits, int[] capital) { + PriorityQueue minCapital = new PriorityQueue<>((a, b) -> capital[a] - capital[b]); + PriorityQueue maxProfit = new PriorityQueue<>((a, b) -> profits[b] - profits[a]); + + for (int i = 0; i < capital.length; i++) { + minCapital.offer(i); + } + + for (int i = 0; i < k; i++) { + while (!minCapital.isEmpty() && capital[minCapital.peek()] <= w) { + maxProfit.offer(minCapital.poll()); + } + if (maxProfit.isEmpty()) { + break; + } + w += profits[maxProfit.poll()]; + } + + return w; + } +} +``` + +```cpp +class Solution { +public: + int findMaximizedCapital(int k, int w, vector& profits, vector& capital) { + auto cmpCapital = [&](int a, int b) { return capital[a] > capital[b]; }; + auto cmpProfit = [&](int a, int b) { return profits[a] < profits[b]; }; + + priority_queue, decltype(cmpCapital)> minCapital(cmpCapital); + priority_queue, decltype(cmpProfit)> maxProfit(cmpProfit); + + for (int i = 0; i < capital.size(); i++) { + minCapital.push(i); + } + + for (int i = 0; i < k; i++) { + while (!minCapital.empty() && capital[minCapital.top()] <= w) { + maxProfit.push(minCapital.top()); + minCapital.pop(); + } + if (maxProfit.empty()) { + break; + } + w += profits[maxProfit.top()]; + maxProfit.pop(); + } + + return w; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} k + * @param {number} w + * @param {number[]} profits + * @param {number[]} capital + * @return {number} + */ + findMaximizedCapital(k, w, profits, capital) { + const minCapital = new PriorityQueue( + (a, b) => capital[a] - capital[b], + ); + const maxProfit = new PriorityQueue( + (a, b) => profits[b] - profits[a], + ); + + for (let i = 0; i < capital.length; i++) { + minCapital.enqueue(i); + } + + for (let i = 0; i < k; i++) { + while (!minCapital.isEmpty() && capital[minCapital.front()] <= w) { + maxProfit.enqueue(minCapital.dequeue()); + } + if (maxProfit.isEmpty()) { + break; + } + w += profits[maxProfit.dequeue()]; + } + + return w; + } +} +``` + +```csharp +public class Solution { + public int FindMaximizedCapital(int k, int w, int[] profits, int[] capital) { + var minCapital = new PriorityQueue(); // index with capital as priority + var maxProfit = new PriorityQueue(Comparer.Create((a, b) => b.CompareTo(a))); // max heap by profit + + for (int i = 0; i < capital.Length; i++) { + minCapital.Enqueue(i, capital[i]); + } + + for (int i = 0; i < k; i++) { + while (minCapital.Count > 0 && capital[minCapital.Peek()] <= w) { + int idx = minCapital.Dequeue(); + maxProfit.Enqueue(profits[idx], profits[idx]); + } + + if (maxProfit.Count == 0) { + break; + } + + w += maxProfit.Dequeue(); + } + + return w; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sorting + Max-Heap + +::tabs-start + +```python +class Solution: + def findMaximizedCapital(self, k: int, w: int, profits: List[int], capital: List[int]) -> int: + n = len(profits) + indices = list(range(n)) + indices.sort(key=lambda i: capital[i]) + + maxProfit, idx = [], 0 + for _ in range(k): + while idx < n and capital[indices[idx]] <= w: + heapq.heappush(maxProfit, -profits[indices[idx]]) + idx += 1 + + if not maxProfit: + break + w += -heapq.heappop(maxProfit) + + return w +``` + +```java +public class Solution { + public int findMaximizedCapital(int k, int w, int[] profits, int[] capital) { + int n = profits.length; + Integer[] indices = new Integer[n]; + for (int i = 0; i < n; i++) { + indices[i] = i; + } + Arrays.sort(indices, (a, b) -> Integer.compare(capital[a], capital[b])); + + PriorityQueue maxProfit = new PriorityQueue<>(Collections.reverseOrder()); + int idx = 0; + for (int i = 0; i < k; i++) { + while (idx < n && capital[indices[idx]] <= w) { + maxProfit.add(profits[indices[idx]]); + idx++; + } + if (maxProfit.isEmpty()) { + break; + } + w += maxProfit.poll(); + } + + return w; + } +} +``` + +```cpp +class Solution { +public: + int findMaximizedCapital(int k, int w, vector& profits, vector& capital) { + int n = profits.size(); + vector indices(n); + for (int i = 0; i < n; i++) { + indices[i] = i; + } + sort(indices.begin(), indices.end(), [&](int a, int b) { + return capital[a] < capital[b]; + }); + + priority_queue maxProfit; + int idx = 0; + for (int i = 0; i < k; i++) { + while (idx < n && capital[indices[idx]] <= w) { + maxProfit.push(profits[indices[idx]]); + idx++; + } + if (maxProfit.empty()) { + break; + } + w += maxProfit.top(); + maxProfit.pop(); + } + + return w; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} k + * @param {number} w + * @param {number[]} profits + * @param {number[]} capital + * @return {number} + */ + findMaximizedCapital(k, w, profits, capital) { + const n = profits.length; + const indices = Array.from({ length: n }, (_, i) => i); + indices.sort((a, b) => capital[a] - capital[b]); + + const maxProfit = new MaxPriorityQueue(); + let idx = 0; + for (let i = 0; i < k; i++) { + while (idx < n && capital[indices[idx]] <= w) { + maxProfit.enqueue(profits[indices[idx]]); + idx++; + } + if (maxProfit.isEmpty()) { + break; + } + w += maxProfit.dequeue(); + } + + return w; + } +} +``` + +```csharp +public class Solution { + public int FindMaximizedCapital(int k, int w, int[] profits, int[] capital) { + int n = profits.Length; + int[] indices = new int[n]; + for (int i = 0; i < n; i++) { + indices[i] = i; + } + + Array.Sort(indices, (a, b) => capital[a].CompareTo(capital[b])); + + var maxProfit = new PriorityQueue(Comparer.Create((a, b) => b.CompareTo(a))); + int idx = 0; + + for (int i = 0; i < k; i++) { + while (idx < n && capital[indices[idx]] <= w) { + maxProfit.Enqueue(profits[indices[idx]], profits[indices[idx]]); + idx++; + } + + if (maxProfit.Count == 0) { + break; + } + + w += maxProfit.Dequeue(); + } + + return w; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/is-anagram.md b/articles/is-anagram.md new file mode 100644 index 000000000..eb996634a --- /dev/null +++ b/articles/is-anagram.md @@ -0,0 +1,501 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def isAnagram(self, s: str, t: str) -> bool: + if len(s) != len(t): + return False + + return sorted(s) == sorted(t) +``` + +```java +public class Solution { + public boolean isAnagram(String s, String t) { + if (s.length() != t.length()) { + return false; + } + + char[] sSort = s.toCharArray(); + char[] tSort = t.toCharArray(); + Arrays.sort(sSort); + Arrays.sort(tSort); + return Arrays.equals(sSort, tSort); + } +} +``` + +```cpp +class Solution { +public: + bool isAnagram(string s, string t) { + if (s.length() != t.length()) { + return false; + } + + sort(s.begin(), s.end()); + sort(t.begin(), t.end()); + return s == t; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + isAnagram(s, t) { + if (s.length !== t.length) { + return false; + } + + let sSort = s.split("").sort().join(); + let tSort = t.split("").sort().join(); + return sSort == tSort + } +} +``` + +```csharp +public class Solution { + public bool IsAnagram(string s, string t) { + if (s.Length != t.Length) { + return false; + } + + char[] sSort = s.ToCharArray(); + char[] tSort = t.ToCharArray(); + Array.Sort(sSort); + Array.Sort(tSort); + return sSort.SequenceEqual(tSort); + } +} +``` + +```go +func isAnagram(s string, t string) bool { + if len(s) != len(t) { + return false + } + + sRunes, tRunes := []rune(s), []rune(t) + sort.Slice(sRunes, func(i, j int) bool { + return sRunes[i] < sRunes[j] + }) + sort.Slice(tRunes, func(i, j int) bool { + return tRunes[i] < tRunes[j] + }) + + for i := range sRunes { + if sRunes[i] != tRunes[i] { + return false + } + } + return true +} +``` + +```kotlin +class Solution { + fun isAnagram(s: String, t: String): Boolean { + if (s.length != t.length) { + return false + } + + return s.toCharArray().sorted() == t.toCharArray().sorted() + } +} +``` + +```swift +class Solution { + func isAnagram(_ s: String, _ t: String) -> Bool { + return s.count == t.count && s.sorted() == t.sorted() + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + m \log m)$ +* Space complexity: $O(1)$ or $O(n + m)$ depending on the sorting algorithm. + +> Where $n$ is the length of string $s$ and $m$ is the length of string $t$. + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def isAnagram(self, s: str, t: str) -> bool: + if len(s) != len(t): + return False + + countS, countT = {}, {} + + for i in range(len(s)): + countS[s[i]] = 1 + countS.get(s[i], 0) + countT[t[i]] = 1 + countT.get(t[i], 0) + return countS == countT +``` + +```java +public class Solution { + public boolean isAnagram(String s, String t) { + if (s.length() != t.length()) { + return false; + } + + HashMap countS = new HashMap<>(); + HashMap countT = new HashMap<>(); + for (int i = 0; i < s.length(); i++) { + countS.put(s.charAt(i), countS.getOrDefault(s.charAt(i), 0) + 1); + countT.put(t.charAt(i), countT.getOrDefault(t.charAt(i), 0) + 1); + } + return countS.equals(countT); + } +} +``` + +```cpp +class Solution { +public: + bool isAnagram(string s, string t) { + if (s.length() != t.length()) { + return false; + } + + unordered_map countS; + unordered_map countT; + for (int i = 0; i < s.length(); i++) { + countS[s[i]]++; + countT[t[i]]++; + } + return countS == countT; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + isAnagram(s, t) { + if (s.length !== t.length) { + return false; + } + + const countS = {}; + const countT = {}; + for (let i = 0; i < s.length; i++) { + countS[s[i]] = (countS[s[i]] || 0) + 1; + countT[t[i]] = (countT[t[i]] || 0) + 1; + } + + for (const key in countS) { + if (countS[key] !== countT[key]) { + return false; + } + } + return true; + } +} +``` + +```csharp +public class Solution { + public bool IsAnagram(string s, string t) { + if (s.Length != t.Length) { + return false; + } + + Dictionary countS = new Dictionary(); + Dictionary countT = new Dictionary(); + for (int i = 0; i < s.Length; i++) { + countS[s[i]] = countS.GetValueOrDefault(s[i], 0) + 1; + countT[t[i]] = countT.GetValueOrDefault(t[i], 0) + 1; + } + return countS.Count == countT.Count && !countS.Except(countT).Any(); + } +} +``` + +```go +func isAnagram(s string, t string) bool { + if len(s) != len(t) { + return false + } + + countS, countT := make(map[rune]int), make(map[rune]int) + for i, ch := range s { + countS[ch]++ + countT[rune(t[i])]++ + } + + for k, v := range countS { + if countT[k] != v { + return false + } + } + return true +} +``` + +```kotlin +class Solution { + fun isAnagram(s: String, t: String): Boolean { + if (s.length != t.length) { + return false + } + + val countS = mutableMapOf() + val countT = mutableMapOf() + + for (i in s.indices) { + countS[s[i]] = countS.getOrDefault(s[i], 0) + 1 + countT[t[i]] = countT.getOrDefault(t[i], 0) + 1 + } + + return countS == countT + } +} +``` + +```swift +class Solution { + func isAnagram(_ s: String, _ t: String) -> Bool { + if s.count != t.count { + return false + } + + var countS = [Character: Int]() + var countT = [Character: Int]() + + let sArray = Array(s) + let tArray = Array(t) + + for i in 0.. Where $n$ is the length of string $s$ and $m$ is the length of string $t$. + +--- + +## 3. Hash Table (Using Array) + +::tabs-start + +```python +class Solution: + def isAnagram(self, s: str, t: str) -> bool: + if len(s) != len(t): + return False + + count = [0] * 26 + for i in range(len(s)): + count[ord(s[i]) - ord('a')] += 1 + count[ord(t[i]) - ord('a')] -= 1 + + for val in count: + if val != 0: + return False + return True +``` + +```java +public class Solution { + public boolean isAnagram(String s, String t) { + if (s.length() != t.length()) { + return false; + } + + int[] count = new int[26]; + for (int i = 0; i < s.length(); i++) { + count[s.charAt(i) - 'a']++; + count[t.charAt(i) - 'a']--; + } + + for (int val : count) { + if (val != 0) { + return false; + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isAnagram(string s, string t) { + if (s.length() != t.length()) { + return false; + } + + vector count(26, 0); + for (int i = 0; i < s.length(); i++) { + count[s[i] - 'a']++; + count[t[i] - 'a']--; + } + + for (int val : count) { + if (val != 0) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + isAnagram(s, t) { + if (s.length !== t.length) { + return false; + } + + const count = new Array(26).fill(0); + for (let i = 0; i < s.length; i++) { + count[s.charCodeAt(i) - 'a'.charCodeAt(0)]++; + count[t.charCodeAt(i) - 'a'.charCodeAt(0)]--; + } + return count.every(val => val === 0); + } +} +``` + +```csharp +public class Solution { + public bool IsAnagram(string s, string t) { + if (s.Length != t.Length) { + return false; + } + + int[] count = new int[26]; + for (int i = 0; i < s.Length; i++) { + count[s[i] - 'a']++; + count[t[i] - 'a']--; + } + + foreach (int val in count) { + if (val != 0) { + return false; + } + } + return true; + } +} +``` + +```go +func isAnagram(s string, t string) bool { + if len(s) != len(t) { + return false + } + + count := [26]int{} + for i := 0; i < len(s); i++ { + count[s[i]-'a']++ + count[t[i]-'a']-- + } + + for _, val := range count { + if val != 0 { + return false + } + } + return true +} +``` + +```kotlin +class Solution { + fun isAnagram(s: String, t: String): Boolean { + if (s.length != t.length) { + return false + } + + val count = IntArray(26) + for (i in s.indices) { + count[s[i] - 'a']++ + count[t[i] - 'a']-- + } + + for (value in count) { + if (value != 0) { + return false + } + } + return true + } +} +``` + +```swift +class Solution { + func isAnagram(_ s: String, _ t: String) -> Bool { + if s.count != t.count { + return false + } + + var count = [Int](repeating: 0, count: 26) + let sArray = Array(s) + let tArray = Array(t) + + for i in 0.. Where $n$ is the length of string $s$ and $m$ is the length of string $t$. \ No newline at end of file diff --git a/articles/is-graph-bipartite.md b/articles/is-graph-bipartite.md new file mode 100644 index 000000000..2db1c9d94 --- /dev/null +++ b/articles/is-graph-bipartite.md @@ -0,0 +1,611 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def isBipartite(self, graph: List[List[int]]) -> bool: + color = [0] * len(graph) # Map node i -> odd=1, even=-1 + + def dfs(i, c): + color[i] = c + for nei in graph[i]: + if color[nei] == c: + return False + if color[nei] == 0 and not dfs(nei, -c): + return False + return True + + for i in range(len(graph)): + if color[i] == 0 and not dfs(i, 1): + return False + return True +``` + +```java +public class Solution { + private int[] color; + + public boolean isBipartite(int[][] graph) { + int n = graph.length; + color = new int[n]; // Map node i -> odd=1, even=-1 + + for (int i = 0; i < n; i++) { + if (color[i] == 0 && !dfs(graph, i, 1)) { + return false; + } + } + return true; + } + + private boolean dfs(int[][] graph, int i, int c) { + color[i] = c; + for (int nei : graph[i]) { + if (color[nei] == c) { + return false; + } + if (color[nei] == 0 && !dfs(graph, nei, -c)) { + return false; + } + } + return true; + } +} +``` + +```cpp +class Solution { +private: + vector color; + + bool dfs(vector>& graph, int i, int c) { + color[i] = c; + for (int nei : graph[i]) { + if (color[nei] == c) { + return false; + } + if (color[nei] == 0 && !dfs(graph, nei, -c)) { + return false; + } + } + return true; + } + +public: + bool isBipartite(vector>& graph) { + int n = graph.size(); + color.assign(n, 0); // Map node i -> odd=1, even=-1 + + for (int i = 0; i < n; i++) { + if (color[i] == 0 && !dfs(graph, i, 1)) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} graph + * @return {boolean} + */ + isBipartite(graph) { + let n = graph.length; + let color = new Array(n).fill(0); // Map node i -> odd=1, even=-1 + + const dfs = (i, c) => { + color[i] = c; + for (let nei of graph[i]) { + if (color[nei] === c) { + return false; + } + if (color[nei] === 0 && !dfs(nei, -c)) { + return false; + } + } + return true; + }; + + for (let i = 0; i < n; i++) { + if (color[i] === 0 && !dfs(i, 1)) { + return false; + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def isBipartite(self, graph: List[List[int]]) -> bool: + color = [0] * len(graph) # Map node i -> odd=1, even=-1 + + def bfs(i): + if color[i]: + return True + q = deque([i]) + color[i] = -1 + while q: + i = q.popleft() + for nei in graph[i]: + if color[i] == color[nei]: + return False + elif not color[nei]: + q.append(nei) + color[nei] = -1 * color[i] + return True + + for i in range(len(graph)): + if not bfs(i): + return False + return True +``` + +```java +public class Solution { + public boolean isBipartite(int[][] graph) { + int n = graph.length; + int[] color = new int[n]; // Map node i -> odd=1, even=-1 + + for (int i = 0; i < n; i++) { + if (color[i] != 0) continue; + Queue q = new LinkedList<>(); + q.offer(i); + color[i] = -1; + + while (!q.isEmpty()) { + int node = q.poll(); + for (int nei : graph[node]) { + if (color[nei] == color[node]) { + return false; + } else if (color[nei] == 0) { + q.offer(nei); + color[nei] = -color[node]; + } + } + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isBipartite(vector>& graph) { + int n = graph.size(); + vector color(n, 0); // Map node i -> odd=1, even=-1 + + for (int i = 0; i < n; i++) { + if (color[i] != 0) continue; + queue q; + q.push(i); + color[i] = -1; + + while (!q.empty()) { + int node = q.front(); + q.pop(); + for (int nei : graph[node]) { + if (color[nei] == color[node]) { + return false; + } else if (color[nei] == 0) { + q.push(nei); + color[nei] = -color[node]; + } + } + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} graph + * @return {boolean} + */ + isBipartite(graph) { + let n = graph.length; + let color = new Array(n).fill(0); // Map node i -> odd=1, even=-1 + + for (let i = 0; i < n; i++) { + if (color[i] !== 0) continue; + let q = new Queue([i]); + color[i] = -1; + + while (!q.isEmpty()) { + let node = q.pop(); + for (let nei of graph[node]) { + if (color[nei] === color[node]) { + return false; + } else if (color[nei] === 0) { + q.push(nei); + color[nei] = -color[node]; + } + } + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +class Solution: + def isBipartite(self, graph: List[List[int]]) -> bool: + color = [0] * len(graph) # Map node i -> odd=1, even=-1 + stack = [] + + for i in range(len(graph)): + if color[i] != 0: + continue + color[i] = -1 + stack.append(i) + while stack: + node = stack.pop() + for nei in graph[node]: + if color[node] == color[nei]: + return False + elif not color[nei]: + stack.append(nei) + color[nei] = -1 * color[node] + + return True +``` + +```java +public class Solution { + public boolean isBipartite(int[][] graph) { + int n = graph.length; + int[] color = new int[n]; // Map node i -> odd=1, even=-1 + Stack stack = new Stack<>(); + + for (int i = 0; i < n; i++) { + if (color[i] != 0) continue; + color[i] = -1; + stack.push(i); + while (!stack.isEmpty()) { + int node = stack.pop(); + for (int nei : graph[node]) { + if (color[node] == color[nei]) return false; + if (color[nei] == 0) { + stack.push(nei); + color[nei] = -color[node]; + } + } + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isBipartite(vector>& graph) { + int n = graph.size(); + vector color(n); // Map node i -> odd=1, even=-1 + stack stack; + + for (int i = 0; i < n; i++) { + if (color[i] != 0) continue; + color[i] = -1; + stack.push(i); + while (!stack.empty()) { + int node = stack.top(); + stack.pop(); + for (int nei : graph[node]) { + if (color[node] == color[nei]) return false; + if (color[nei] == 0) { + stack.push(nei); + color[nei] = -color[node]; + } + } + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} graph + * @return {boolean} + */ + isBipartite(graph) { + let n = graph.length; + let color = new Array(n).fill(0); // Map node i -> odd=1, even=-1 + let stack = []; + + for (let i = 0; i < n; i++) { + if (color[i] !== 0) continue; + color[i] = -1; + stack.push(i); + while (stack.length > 0) { + let node = stack.pop(); + for (let nei of graph[node]) { + if (color[node] === color[nei]) return false; + if (color[nei] === 0) { + stack.push(nei); + color[nei] = -color[node]; + } + } + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 4. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n)) + self.Size = [0] * n + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu, pv = self.find(u), self.find(v) + if pu == pv: + return False + if self.Size[pu] > self.Size[pv]: + self.Parent[pv] = pu + elif self.Size[pu] < self.Size[pv]: + self.Parent[pu] = pv + else: + self.Parent[pv] = pu + self.Size[pu] += 1 + return True + +class Solution: + def isBipartite(self, graph: List[List[int]]) -> bool: + n = len(graph) + dsu = DSU(n) + + for node in range(n): + for nei in graph[node]: + if dsu.find(node) == dsu.find(nei): + return False + dsu.union(graph[node][0], nei) + + return True +``` + +```java +public class DSU { + private int[] Parent, Size; + + public DSU(int n) { + Parent = new int[n]; + Size = new int[n]; + for (int i = 0; i < n; i++) { + Parent[i] = i; + Size[i] = 0; + } + } + + public int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (Size[pu] > Size[pv]) { + Parent[pv] = pu; + } else if (Size[pu] < Size[pv]) { + Parent[pu] = pv; + } else { + Parent[pv] = pu; + Size[pu]++; + } + return true; + } +} + +class Solution { + public boolean isBipartite(int[][] graph) { + int n = graph.length; + DSU dsu = new DSU(n); + + for (int node = 0; node < n; node++) { + for (int nei : graph[node]) { + if (dsu.find(node) == dsu.find(nei)) { + return false; + } + dsu.union(graph[node][0], nei); + } + } + return true; + } +} +``` + +```cpp +class DSU { +private: + vector Parent, Size; + +public: + DSU(int n) { + Parent.resize(n); + Size.resize(n, 0); + for (int i = 0; i < n; i++) { + Parent[i] = i; + } + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + bool unionSet(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (Size[pu] > Size[pv]) { + Parent[pv] = pu; + } else if (Size[pu] < Size[pv]) { + Parent[pu] = pv; + } else { + Parent[pv] = pu; + Size[pu]++; + } + return true; + } +}; + +class Solution { +public: + bool isBipartite(vector>& graph) { + int n = graph.size(); + DSU dsu(n); + + for (int node = 0; node < n; node++) { + for (int& nei : graph[node]) { + if (dsu.find(node) == dsu.find(nei)) { + return false; + } + dsu.unionSet(graph[node][0], nei); + } + } + return true; + } +}; +``` + +```javascript +class DSU { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.parent = Array.from({ length: n + 1 }, (_, i) => i); + this.size = new Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.parent[node] !== node) { + this.parent[node] = this.find(this.parent[node]); + } + return this.parent[node]; + } + + /** + * @param {number} u + * @param {number} u=v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u), pv = this.find(v); + if (pu === pv) return false; + if (this.size[pu] >= this.size[pv]) { + this.size[pu] += this.size[pv]; + this.parent[pv] = pu; + } else { + this.size[pv] += this.size[pu]; + this.parent[pu] = pv; + } + return true; + } +} + +class Solution { + /** + * @param {number[][]} graph + * @return {boolean} + */ + isBipartite(graph) { + let n = graph.length; + let dsu = new DSU(n); + + for (let node = 0; node < n; node++) { + for (let nei of graph[node]) { + if (dsu.find(node) === dsu.find(nei)) { + return false; + } + dsu.union(graph[node][0], nei); + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + (E * α(V)))$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. $α()$ is used for amortized complexity. \ No newline at end of file diff --git a/articles/is-palindrome.md b/articles/is-palindrome.md new file mode 100644 index 000000000..9aea0c833 --- /dev/null +++ b/articles/is-palindrome.md @@ -0,0 +1,379 @@ +## 1. Reverse String + +::tabs-start + +```python +class Solution: + def isPalindrome(self, s: str) -> bool: + newStr = '' + for c in s: + if c.isalnum(): + newStr += c.lower() + return newStr == newStr[::-1] +``` + +```java +public class Solution { + public boolean isPalindrome(String s) { + StringBuilder newStr = new StringBuilder(); + for (char c : s.toCharArray()) { + if (Character.isLetterOrDigit(c)) { + newStr.append(Character.toLowerCase(c)); + } + } + return newStr.toString().equals(newStr.reverse().toString()); + } +} +``` + +```cpp +class Solution { +public: + bool isPalindrome(string s) { + string newStr = ""; + for (char c : s) { + if (isalnum(c)) { + newStr += tolower(c); + } + } + return newStr == string(newStr.rbegin(), newStr.rend()); + } +}; +``` + +```javascript +class Solution { + /** + * Check if a character is alphanumeric + * @param {char} char + * @return {boolean} + */ + isAlphanumeric(char) { + return (char >= 'a' && char <= 'z') || + (char >= 'A' && char <= 'Z') || + (char >= '0' && char <= '9'); + } + + /** + * @param {string} s + * @return {boolean} + */ + isPalindrome(s) { + let newStr = ''; + for (let c of s) { + if (this.isAlphanumeric(c)) { + newStr += c.toLowerCase(); + } + } + return newStr === newStr.split('').reverse().join(''); + } +} +``` + +```csharp +public class Solution { + public bool IsPalindrome(string s) { + string newStr = ""; + foreach (char c in s) { + if (char.IsLetterOrDigit(c)) { + newStr += char.ToLower(c); + } + } + return newStr == new string(newStr.Reverse().ToArray()); + } +} +``` + +```go +func isPalindrome(s string) bool { + newStr := "" + for _, c := range s { + if ('a' <= c && c <= 'z') || ('0' <= c && c <= '9') { + newStr += string(c) + } else if 'A' <= c && c <= 'Z' { + newStr += string(c + 'a' - 'A') + } + } + + reversedStr := reverse(newStr) + return newStr == reversedStr +} + +func reverse(s string) string { + runes := []rune(s) + n := len(runes) + for i := 0; i < n/2; i++ { + runes[i], runes[n-1-i] = runes[n-1-i], runes[i] + } + return string(runes) +} +``` + +```kotlin +class Solution { + fun isPalindrome(s: String): Boolean { + var newStr = "" + for (c in s) { + if (c.isLetterOrDigit()) { + newStr += c.lowercaseChar() + } + } + return newStr == newStr.reversed() + } +} +``` + +```swift +class Solution { + func isPalindrome(_ s: String) -> Bool { + var newStr = "" + + for c in s { + if c.isLetter || c.isNumber { + newStr.append(c.lowercased()) + } + } + + return newStr == String(newStr.reversed()) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Two Pointers + +::tabs-start + +```python +class Solution: + def isPalindrome(self, s: str) -> bool: + l, r = 0, len(s) - 1 + + while l < r: + while l < r and not self.alphaNum(s[l]): + l += 1 + while r > l and not self.alphaNum(s[r]): + r -= 1 + if s[l].lower() != s[r].lower(): + return False + l, r = l + 1, r - 1 + return True + + def alphaNum(self, c): + return (ord('A') <= ord(c) <= ord('Z') or + ord('a') <= ord(c) <= ord('z') or + ord('0') <= ord(c) <= ord('9')) +``` + +```java +public class Solution { + public boolean isPalindrome(String s) { + int l = 0, r = s.length() - 1; + + while (l < r) { + while (l < r && !alphaNum(s.charAt(l))) { + l++; + } + while (r > l && !alphaNum(s.charAt(r))) { + r--; + } + if (Character.toLowerCase(s.charAt(l)) != Character.toLowerCase(s.charAt(r))) { + return false; + } + l++; r--; + } + return true; + } + + public boolean alphaNum(char c) { + return (c >= 'A' && c <= 'Z' || + c >= 'a' && c <= 'z' || + c >= '0' && c <= '9'); + } +} +``` + +```cpp +class Solution { +public: + bool isPalindrome(string s) { + int l = 0, r = s.length() - 1; + + while (l < r) { + while (l < r && !alphaNum(s[l])) { + l++; + } + while (r > l && !alphaNum(s[r])) { + r--; + } + if (tolower(s[l]) != tolower(s[r])) { + return false; + } + l++; r--; + } + return true; + } + + bool alphaNum(char c) { + return (c >= 'A' && c <= 'Z' || + c >= 'a' && c <= 'z' || + c >= '0' && c <= '9'); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + isPalindrome(s) { + let l = 0, r = s.length - 1; + + while (l < r) { + while (l < r && !this.alphaNum(s[l])) { + l++; + } + while (r > l && !this.alphaNum(s[r])) { + r--; + } + if (s[l].toLowerCase() !== s[r].toLowerCase()) { + return false; + } + l++; r--; + } + return true; + } + + /** + * @param {char} c + * @return {boolean} + */ + alphaNum(c) { + return (c >= 'A' && c <= 'Z' || + c >= 'a' && c <= 'z' || + c >= '0' && c <= '9'); + } +} +``` + +```csharp +public class Solution { + public bool IsPalindrome(string s) { + int l = 0, r = s.Length - 1; + + while (l < r) { + while (l < r && !AlphaNum(s[l])) { + l++; + } + while (r > l && !AlphaNum(s[r])) { + r--; + } + if (char.ToLower(s[l]) != char.ToLower(s[r])) { + return false; + } + l++; r--; + } + return true; + } + + public bool AlphaNum(char c) { + return (c >= 'A' && c <= 'Z' || + c >= 'a' && c <= 'z' || + c >= '0' && c <= '9'); + } +} +``` + +```go +func isPalindrome(s string) bool { + l, r := 0, len(s)-1 + + for l < r { + for l < r && !isAlphaNum(rune(s[l])) { + l++ + } + for r > l && !isAlphaNum(rune(s[r])) { + r-- + } + if unicode.ToLower(rune(s[l])) != unicode.ToLower(rune(s[r])) { + return false + } + l++ + r-- + } + return true +} + +func isAlphaNum(c rune) bool { + return unicode.IsLetter(c) || unicode.IsDigit(c) +} +``` + +```kotlin +class Solution { + fun isPalindrome(s: String): Boolean { + var l = 0 + var r = s.length - 1 + + while (l < r) { + while (l < r && !s[l].isLetterOrDigit()) { + l++ + } + while (r > l && !s[r].isLetterOrDigit()) { + r-- + } + if (s[l].lowercase() != s[r].lowercase()) { + return false + } + l++ + r-- + } + return true + } +} +``` + +```swift +class Solution { + func isPalindrome(_ s: String) -> Bool { + let chars = Array(s) + var l = 0, r = chars.count - 1 + + while l < r { + while l < r && !isAlphaNum(chars[l]) { + l += 1 + } + while r > l && !isAlphaNum(chars[r]) { + r -= 1 + } + if chars[l].lowercased() != chars[r].lowercased() { + return false + } + l += 1 + r -= 1 + } + return true + } + + private func isAlphaNum(_ c: Character) -> Bool { + return c.isLetter || c.isNumber + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/is-subsequence.md b/articles/is-subsequence.md new file mode 100644 index 000000000..fde7628a6 --- /dev/null +++ b/articles/is-subsequence.md @@ -0,0 +1,513 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def isSubsequence(self, s: str, t: str) -> bool: + def rec(i, j): + if i == len(s): + return True + if j == len(t): + return False + + if s[i] == t[j]: + return rec(i + 1, j + 1) + return rec(i, j + 1) + return rec(0, 0) +``` + +```java +public class Solution { + public boolean isSubsequence(String s, String t) { + return rec(s, t, 0, 0); + } + + private boolean rec(String s, String t, int i, int j) { + if (i == s.length()) return true; + if (j == t.length()) return false; + if (s.charAt(i) == t.charAt(j)) { + return rec(s, t, i + 1, j + 1); + } + return rec(s, t, i, j + 1); + } +} +``` + +```cpp +class Solution { +public: + bool isSubsequence(string s, string t) { + return rec(s, t, 0, 0); + } + +private: + bool rec(string& s, string& t, int i, int j) { + if (i == s.size()) return true; + if (j == t.size()) return false; + if (s[i] == t[j]) { + return rec(s, t, i + 1, j + 1); + } + return rec(s, t, i, j + 1); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + isSubsequence(s, t) { + const rec = (i, j) => { + if (i === s.length) return true; + if (j === t.length) return false; + if (s[i] === t[j]) return rec(i + 1, j + 1); + return rec(i, j + 1); + }; + return rec(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $t$. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def isSubsequence(self, s: str, t: str) -> bool: + n, m = len(s), len(t) + memo = [[-1] * m for _ in range(n)] + + def rec(i, j): + if i == n: + return True + if j == m: + return False + if memo[i][j] != -1: + return memo[i][j] == 1 + if s[i] == t[j]: + memo[i][j] = 1 if rec(i + 1, j + 1) else 0 + else: + memo[i][j] = 1 if rec(i, j + 1) else 0 + return memo[i][j] == 1 + + return rec(0, 0) +``` + +```java +public class Solution { + public boolean isSubsequence(String s, String t) { + int n = s.length(), m = t.length(); + int[][] memo = new int[n][m]; + for (int[] row : memo) { + Arrays.fill(row, -1); + } + return rec(s, t, 0, 0, memo); + } + + private boolean rec(String s, String t, int i, int j, int[][] memo) { + if (i == s.length()) return true; + if (j == t.length()) return false; + if (memo[i][j] != -1) return memo[i][j] == 1; + if (s.charAt(i) == t.charAt(j)) { + memo[i][j] = rec(s, t, i + 1, j + 1, memo) ? 1 : 0; + } else { + memo[i][j] = rec(s, t, i, j + 1, memo) ? 1 : 0; + } + return memo[i][j] == 1; + } +} +``` + +```cpp +class Solution { +public: + bool isSubsequence(string s, string t) { + int n = s.size(), m = t.size(); + vector> memo(n, vector(m, -1)); + return rec(s, t, 0, 0, memo); + } + +private: + bool rec(string& s, string& t, int i, int j, vector>& memo) { + if (i == s.size()) return true; + if (j == t.size()) return false; + if (memo[i][j] != -1) return memo[i][j] == 1; + if (s[i] == t[j]) { + memo[i][j] = rec(s, t, i + 1, j + 1, memo) ? 1 : 0; + } else { + memo[i][j] = rec(s, t, i, j + 1, memo) ? 1 : 0; + } + return memo[i][j] == 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + isSubsequence(s, t) { + const n = s.length, m = t.length; + const memo = Array.from({ length: n }, () => Array(m).fill(-1)); + + const rec = (i, j) => { + if (i === n) return true; + if (j === m) return false; + if (memo[i][j] !== -1) return memo[i][j] === 1; + if (s[i] === t[j]) { + memo[i][j] = rec(i + 1, j + 1) ? 1 : 0; + } else { + memo[i][j] = rec(i, j + 1) ? 1 : 0; + } + return memo[i][j] === 1; + }; + + return rec(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $t$. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def isSubsequence(self, s: str, t: str) -> bool: + n, m = len(s), len(t) + dp = [[False] * (m + 1) for _ in range(n + 1)] + + for j in range(m + 1): + dp[n][j] = True + + for i in range(n - 1, -1, -1): + for j in range(m - 1, -1, -1): + if s[i] == t[j]: + dp[i][j] = dp[i + 1][j + 1] + else: + dp[i][j] = dp[i][j + 1] + + return dp[0][0] +``` + +```java +public class Solution { + public boolean isSubsequence(String s, String t) { + int n = s.length(), m = t.length(); + boolean[][] dp = new boolean[n + 1][m + 1]; + + for (int j = 0; j <= m; j++) { + dp[n][j] = true; + } + + for (int i = n - 1; i >= 0; i--) { + for (int j = m - 1; j >= 0; j--) { + if (s.charAt(i) == t.charAt(j)) { + dp[i][j] = dp[i + 1][j + 1]; + } else { + dp[i][j] = dp[i][j + 1]; + } + } + } + + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + bool isSubsequence(string s, string t) { + int n = s.size(), m = t.size(); + vector> dp(n + 1, vector(m + 1, false)); + + for (int j = 0; j <= m; ++j) { + dp[n][j] = true; + } + + for (int i = n - 1; i >= 0; --i) { + for (int j = m - 1; j >= 0; --j) { + if (s[i] == t[j]) { + dp[i][j] = dp[i + 1][j + 1]; + } else { + dp[i][j] = dp[i][j + 1]; + } + } + } + + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + isSubsequence(s, t) { + const n = s.length, m = t.length; + const dp = Array.from({ length: n + 1 }, () => Array(m + 1).fill(false)); + + for (let j = 0; j <= m; j++) { + dp[n][j] = true; + } + + for (let i = n - 1; i >= 0; i--) { + for (let j = m - 1; j >= 0; j--) { + if (s[i] === t[j]) { + dp[i][j] = dp[i + 1][j + 1]; + } else { + dp[i][j] = dp[i][j + 1]; + } + } + } + + return dp[0][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $t$. + +--- + +## 4. Two Pointers + +::tabs-start + +```python +class Solution: + def isSubsequence(self, s: str, t: str) -> bool: + i = j = 0 + while i < len(s) and j < len(t): + if s[i] == t[j]: + i += 1 + j += 1 + return i == len(s) +``` + +```java +public class Solution { + public boolean isSubsequence(String s, String t) { + int i = 0, j = 0; + while (i < s.length() && j < t.length()) { + if (s.charAt(i) == t.charAt(j)) { + i++; + } + j++; + } + return i == s.length(); + } +} +``` + +```cpp +class Solution { +public: + bool isSubsequence(string s, string t) { + int i = 0, j = 0; + while (i < s.length() && j < t.length()) { + if (s[i] == t[j]) { + i++; + } + j++; + } + return i == s.length(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + isSubsequence(s, t) { + let i = 0, j = 0; + while (i < s.length && j < t.length) { + if (s.charAt(i) == t.charAt(j)) { + i++; + } + j++; + } + return i == s.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(1)$ + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $t$. + +## 5. Follow-Up Solution (Index Jumping) + +::tabs-start + +```python +class Solution: + def isSubsequence(self, s: str, t: str) -> bool: + n, m = len(s), len(t) + if m == 0: + return n == 0 + + store = [[m + 1] * 26 for _ in range(m)] + store[m - 1][ord(t[m - 1]) - ord('a')] = m - 1 + + for i in range(m - 2, -1, -1): + store[i] = store[i + 1][:] + store[i][ord(t[i]) - ord('a')] = i + + i, j = 0, 0 + while i < n and j < m: + j = store[j][ord(s[i]) - ord('a')] + 1 + if j > m: + return False + i += 1 + + return i == n +``` + +```java +public class Solution { + public boolean isSubsequence(String s, String t) { + int n = s.length(), m = t.length(); + if (m == 0) return n == 0; + + int[][] store = new int[m][26]; + for (int i = 0; i < m; i++) { + Arrays.fill(store[i], m + 1); + } + + store[m - 1][t.charAt(m - 1) - 'a'] = m - 1; + + for (int i = m - 2; i >= 0; i--) { + store[i] = store[i + 1].clone(); + store[i][t.charAt(i) - 'a'] = i; + } + + int i = 0, j = 0; + while (i < n && j < m) { + j = store[j][s.charAt(i) - 'a'] + 1; + if (j > m) return false; + i++; + } + + return i == n; + } +} +``` + +```cpp +class Solution { +public: + bool isSubsequence(string s, string t) { + int n = s.length(), m = t.length(); + if (m == 0) return n == 0; + + vector> store(m, vector(26, m + 1)); + store[m - 1][t[m - 1] - 'a'] = m - 1; + + for (int i = m - 2; i >= 0; i--) { + store[i] = store[i + 1]; + store[i][t[i] - 'a'] = i; + } + + int i = 0, j = 0; + while (i < n && j < m) { + j = store[j][s[i] - 'a'] + 1; + if (j > m) return false; + i++; + } + + return i == n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + isSubsequence(s, t) { + const n = s.length, m = t.length; + if (m === 0) return n === 0; + + const store = Array.from({ length: m }, () => Array(26).fill(m + 1)); + store[m - 1][t.charCodeAt(m - 1) - 'a'.charCodeAt(0)] = m - 1; + + for (let i = m - 2; i >= 0; i--) { + store[i] = [...store[i + 1]]; + store[i][t.charCodeAt(i) - 'a'.charCodeAt(0)] = i; + } + + let i = 0, j = 0; + while (i < n && j < m) { + j = store[j][s.charCodeAt(i) - 'a'.charCodeAt(0)] + 1; + if (j > m) return false; + i++; + } + + return i === n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the length of the string $t$. \ No newline at end of file diff --git a/articles/island-perimeter.md b/articles/island-perimeter.md new file mode 100644 index 000000000..bb87939fe --- /dev/null +++ b/articles/island-perimeter.md @@ -0,0 +1,651 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def islandPerimeter(self, grid: List[List[int]]) -> int: + rows, cols = len(grid), len(grid[0]) + visit = set() + + def dfs(i, j): + if i < 0 or j < 0 or i >= rows or j >= cols or grid[i][j] == 0: + return 1 + if (i, j) in visit: + return 0 + + visit.add((i, j)) + perim = dfs(i, j + 1) + dfs(i + 1, j) + dfs(i, j - 1) + dfs(i - 1, j) + return perim + + for i in range(rows): + for j in range(cols): + if grid[i][j]: + return dfs(i, j) + return 0 +``` + +```java +public class Solution { + private int[][] grid; + private boolean[][] visited; + private int rows, cols; + + public int islandPerimeter(int[][] grid) { + this.grid = grid; + this.rows = grid.length; + this.cols = grid[0].length; + this.visited = new boolean[rows][cols]; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + if (grid[i][j] == 1) { + return dfs(i, j); + } + } + } + return 0; + } + + private int dfs(int i, int j) { + if (i < 0 || j < 0 || i >= rows || + j >= cols || grid[i][j] == 0) { + return 1; + } + if (visited[i][j]) { + return 0; + } + + visited[i][j] = true; + return dfs(i, j + 1) + dfs(i + 1, j) + + dfs(i, j - 1) + dfs(i - 1, j); + } +} +``` + +```cpp +class Solution { +private: + vector> grid; + vector> visited; + int rows, cols; + + int dfs(int i, int j) { + if (i < 0 || j < 0 || i >= rows || + j >= cols || grid[i][j] == 0) { + return 1; + } + if (visited[i][j]) { + return 0; + } + + visited[i][j] = true; + return dfs(i, j + 1) + dfs(i + 1, j) + + dfs(i, j - 1) + dfs(i - 1, j); + } + +public: + int islandPerimeter(vector>& grid) { + this->grid = grid; + rows = grid.size(); + cols = grid[0].size(); + visited = vector>(rows, vector(cols, false)); + + for (int i = 0; i < rows; ++i) { + for (int j = 0; j < cols; ++j) { + if (grid[i][j] == 1) { + return dfs(i, j); + } + } + } + return 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + islandPerimeter(grid) { + const rows = grid.length, cols = grid[0].length; + const visited = Array.from({ length: rows }, () => Array(cols).fill(false)); + + const dfs = (i, j) => { + if (i < 0 || j < 0 || i >= rows || j >= cols || grid[i][j] === 0) { + return 1; + } + if (visited[i][j]) { + return 0; + } + + visited[i][j] = true; + return dfs(i, j + 1) + dfs(i + 1, j) + + dfs(i, j - 1) + dfs(i - 1, j); + }; + + for (let i = 0; i < rows; i++) { + for (let j = 0; j < cols; j++) { + if (grid[i][j] === 1) { + return dfs(i, j); + } + } + } + return 0; + } +} +``` + +```csharp +public class Solution { + private int rows, cols; + private HashSet<(int, int)> visit; + + public int IslandPerimeter(int[][] grid) { + rows = grid.Length; + cols = grid[0].Length; + visit = new HashSet<(int, int)>(); + + int Dfs(int i, int j) { + if (i < 0 || j < 0 || i >= rows || j >= cols || grid[i][j] == 0) { + return 1; + } + if (visit.Contains((i, j))) { + return 0; + } + + visit.Add((i, j)); + int perim = Dfs(i, j + 1) + Dfs(i + 1, j) + Dfs(i, j - 1) + Dfs(i - 1, j); + return perim; + } + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + if (grid[i][j] == 1) { + return Dfs(i, j); + } + } + } + + return 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the grid. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def islandPerimeter(self, grid: List[List[int]]) -> int: + rows, cols = len(grid), len(grid[0]) + visited = set() + directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] + + def bfs(r, c): + queue = deque([(r, c)]) + visited.add((r, c)) + perimeter = 0 + + while queue: + x, y = queue.popleft() + for dx, dy in directions: + nx, ny = x + dx, y + dy + if (nx < 0 or ny < 0 or nx >= rows or + ny >= cols or grid[nx][ny] == 0 + ): + perimeter += 1 + elif (nx, ny) not in visited: + visited.add((nx, ny)) + queue.append((nx, ny)) + return perimeter + + for i in range(rows): + for j in range(cols): + if grid[i][j] == 1: + return bfs(i, j) + return 0 +``` + +```java +public class Solution { + public int islandPerimeter(int[][] grid) { + int rows = grid.length, cols = grid[0].length; + boolean[][] visited = new boolean[rows][cols]; + int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + if (grid[i][j] == 1) { + Queue queue = new LinkedList<>(); + queue.offer(new int[]{i, j}); + visited[i][j] = true; + int perimeter = 0; + + while (!queue.isEmpty()) { + int[] cell = queue.poll(); + int x = cell[0], y = cell[1]; + + for (int[] dir : directions) { + int nx = x + dir[0], ny = y + dir[1]; + if (nx < 0 || ny < 0 || nx >= rows || + ny >= cols || grid[nx][ny] == 0) { + perimeter++; + } else if (!visited[nx][ny]) { + visited[nx][ny] = true; + queue.offer(new int[]{nx, ny}); + } + } + } + return perimeter; + } + } + } + return 0; + } +} +``` + +```cpp +class Solution { +public: + int islandPerimeter(vector>& grid) { + int rows = grid.size(), cols = grid[0].size(); + vector> visited(rows, vector(cols, false)); + int directions[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; + + for (int i = 0; i < rows; ++i) { + for (int j = 0; j < cols; ++j) { + if (grid[i][j] == 1) { + queue> q; + q.push({i, j}); + visited[i][j] = true; + int perimeter = 0; + + while (!q.empty()) { + auto [x, y] = q.front(); + q.pop(); + + for (auto& dir : directions) { + int nx = x + dir[0], ny = y + dir[1]; + if (nx < 0 || ny < 0 || nx >= rows || + ny >= cols || grid[nx][ny] == 0) { + perimeter++; + } else if (!visited[nx][ny]) { + visited[nx][ny] = true; + q.push({nx, ny}); + } + } + } + return perimeter; + } + } + } + return 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + islandPerimeter(grid) { + const rows = grid.length, cols = grid[0].length; + const visited = Array.from({ length: rows }, () => Array(cols).fill(false)); + const directions = [[0, 1], [1, 0], [0, -1], [-1, 0]]; + + const bfs = (r, c) => { + const queue = new Queue([[r, c]]); + visited[r][c] = true; + let perimeter = 0; + while (!queue.isEmpty()) { + const [x, y] = queue.pop(); + + for (const [dx, dy] of directions) { + const nx = x + dx, ny = y + dy; + + if (nx < 0 || ny < 0 || nx >= rows || + ny >= cols || grid[nx][ny] === 0) { + perimeter++; + } else if (!visited[nx][ny]) { + visited[nx][ny] = true; + queue.push([nx, ny]); + } + } + } + return perimeter; + }; + + for (let i = 0; i < rows; i++) { + for (let j = 0; j < cols; j++) { + if (grid[i][j] === 1) { + return bfs(i, j); + } + } + } + return 0; + } +} +``` + +```csharp +public class Solution { + public int IslandPerimeter(int[][] grid) { + int rows = grid.Length; + int cols = grid[0].Length; + var visited = new HashSet<(int, int)>(); + int[][] directions = new int[][] { + new int[] { 0, 1 }, + new int[] { 1, 0 }, + new int[] { 0, -1 }, + new int[] { -1, 0 } + }; + + int Bfs(int r, int c) { + var queue = new Queue<(int, int)>(); + queue.Enqueue((r, c)); + visited.Add((r, c)); + int perimeter = 0; + + while (queue.Count > 0) { + var (x, y) = queue.Dequeue(); + foreach (var dir in directions) { + int nx = x + dir[0]; + int ny = y + dir[1]; + + if (nx < 0 || ny < 0 || nx >= rows || ny >= cols || grid[nx][ny] == 0) { + perimeter++; + } else if (!visited.Contains((nx, ny))) { + visited.Add((nx, ny)); + queue.Enqueue((nx, ny)); + } + } + } + + return perimeter; + } + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + if (grid[i][j] == 1) { + return Bfs(i, j); + } + } + } + + return 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the grid. + +--- + +## 3. Iteration - I + +::tabs-start + +```python +class Solution: + def islandPerimeter(self, grid: List[List[int]]) -> int: + m, n, res = len(grid), len(grid[0]), 0 + for i in range(m): + for j in range(n): + if grid[i][j] == 1: + res += (i + 1 >= m or grid[i + 1][j] == 0) + res += (j + 1 >= n or grid[i][j + 1] == 0) + res += (i - 1 < 0 or grid[i - 1][j] == 0) + res += (j - 1 < 0 or grid[i][j - 1] == 0) + return res +``` + +```java +public class Solution { + public int islandPerimeter(int[][] grid) { + int m = grid.length, n = grid[0].length, res = 0; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == 1) { + res += (i + 1 >= m || grid[i + 1][j] == 0) ? 1 : 0; + res += (j + 1 >= n || grid[i][j + 1] == 0) ? 1 : 0; + res += (i - 1 < 0 || grid[i - 1][j] == 0) ? 1 : 0; + res += (j - 1 < 0 || grid[i][j - 1] == 0) ? 1 : 0; + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int islandPerimeter(vector>& grid) { + int m = grid.size(), n = grid[0].size(), res = 0; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == 1) { + res += (i + 1 >= m || grid[i + 1][j] == 0) ? 1 : 0; + res += (j + 1 >= n || grid[i][j + 1] == 0) ? 1 : 0; + res += (i - 1 < 0 || grid[i - 1][j] == 0) ? 1 : 0; + res += (j - 1 < 0 || grid[i][j - 1] == 0) ? 1 : 0; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + islandPerimeter(grid) { + const m = grid.length, n = grid[0].length; + let res = 0; + for (let i = 0; i < m; i++) { + for (let j = 0; j < n; j++) { + if (grid[i][j] === 1) { + res += (i + 1 >= m || grid[i + 1][j] === 0) ? 1 : 0; + res += (j + 1 >= n || grid[i][j + 1] === 0) ? 1 : 0; + res += (i - 1 < 0 || grid[i - 1][j] === 0) ? 1 : 0; + res += (j - 1 < 0 || grid[i][j - 1] === 0) ? 1 : 0; + } + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int IslandPerimeter(int[][] grid) { + int m = grid.Length; + int n = grid[0].Length; + int res = 0; + + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == 1) { + if (i + 1 >= m || grid[i + 1][j] == 0) res++; + if (j + 1 >= n || grid[i][j + 1] == 0) res++; + if (i - 1 < 0 || grid[i - 1][j] == 0) res++; + if (j - 1 < 0 || grid[i][j - 1] == 0) res++; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ is the number of rows and $n$ is the number of columns in the grid. + +--- + +## 4. Iteration - II + +::tabs-start + +```python +class Solution: + def islandPerimeter(self, grid: List[List[int]]) -> int: + m, n = len(grid), len(grid[0]) + res = 0 + for r in range(m): + for c in range(n): + if grid[r][c] == 1: + res += 4 + if r and grid[r - 1][c]: + res -= 2 + if c and grid[r][c - 1] == 1: + res -= 2 + return res +``` + +```java +public class Solution { + public int islandPerimeter(int[][] grid) { + int m = grid.length, n = grid[0].length; + int res = 0;; + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + if (grid[r][c] == 1) { + res += 4; + if (r > 0 && grid[r - 1][c] == 1) { + res -= 2; + } + if (c > 0 && grid[r][c - 1] == 1) { + res -= 2; + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int islandPerimeter(vector>& grid) { + int m = grid.size(), n = grid[0].size(); + int res = 0; + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + if (grid[r][c]) { + res += 4; + if (r && grid[r - 1][c]) { + res -= 2; + } + if (c && grid[r][c - 1]) { + res -= 2; + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + islandPerimeter(grid) { + const m = grid.length, n = grid[0].length; + let res = 0; + for (let r = 0; r < m; r++) { + for (let c = 0; c < n; c++) { + if (grid[r][c] == 1) { + res += 4; + if (r > 0 && grid[r - 1][c] == 1) { + res -= 2; + } + if (c > 0 && grid[r][c - 1] == 1) { + res -= 2; + } + } + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int IslandPerimeter(int[][] grid) { + int m = grid.Length; + int n = grid[0].Length; + int res = 0; + + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + if (grid[r][c] == 1) { + res += 4; + if (r > 0 && grid[r - 1][c] == 1) { + res -= 2; + } + if (c > 0 && grid[r][c - 1] == 1) { + res -= 2; + } + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ is the number of rows and $n$ is the number of columns in the grid. \ No newline at end of file diff --git a/articles/islands-and-treasure.md b/articles/islands-and-treasure.md new file mode 100644 index 000000000..86e92192f --- /dev/null +++ b/articles/islands-and-treasure.md @@ -0,0 +1,1080 @@ +## 1. Brute Force (Backtracking) + +::tabs-start + +```python +class Solution: + def islandsAndTreasure(self, grid: List[List[int]]) -> None: + ROWS, COLS = len(grid), len(grid[0]) + directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + INF = 2147483647 + visit = [[False for _ in range(COLS)] for _ in range(ROWS)] + + def dfs(r, c): + if (r < 0 or c < 0 or r >= ROWS or + c >= COLS or grid[r][c] == -1 or + visit[r][c]): + return INF + if grid[r][c] == 0: + return 0 + + visit[r][c] = True + res = INF + for dx, dy in directions: + res = min(res, 1 + dfs(r + dx, c + dy)) + visit[r][c] = False + return res + + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] == INF: + grid[r][c] = dfs(r, c) +``` + +```java +public class Solution { + private int[][] directions = {{1, 0}, {-1, 0}, + {0, 1}, {0, -1}}; + private int INF = 2147483647; + private boolean[][] visit; + private int ROWS, COLS; + + private int dfs(int[][] grid, int r, int c) { + if (r < 0 || c < 0 || r >= ROWS || + c >= COLS || grid[r][c] == -1 || visit[r][c]) { + return INF; + } + if (grid[r][c] == 0) { + return 0; + } + visit[r][c] = true; + int res = INF; + for (int[] dir : directions) { + int cur = dfs(grid, r + dir[0], c + dir[1]); + if (cur != INF) { + res = Math.min(res, 1 + cur); + } + } + visit[r][c] = false; + return res; + } + + public void islandsAndTreasure(int[][] grid) { + ROWS = grid.length; + COLS = grid[0].length; + visit = new boolean[ROWS][COLS]; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == INF) { + grid[r][c] = dfs(grid, r, c); + } + } + } + } +} +``` + +```cpp +class Solution { +public: + vector> directions = {{1, 0}, {-1, 0}, + {0, 1}, {0, -1}}; + int INF = 2147483647; + vector> visit; + int ROWS, COLS; + + int dfs(vector>& grid, int r, int c) { + if (r < 0 || c < 0 || r >= ROWS || + c >= COLS || grid[r][c] == -1 || visit[r][c]) { + return INF; + } + if (grid[r][c] == 0) { + return 0; + } + visit[r][c] = true; + int res = INF; + for (auto& dir : directions) { + int cur = dfs(grid, r + dir[0], c + dir[1]); + if (cur != INF) { + res = min(res, 1 + cur); + } + } + visit[r][c] = false; + return res; + } + + void islandsAndTreasure(vector>& grid) { + ROWS = grid.size(); + COLS = grid[0].size(); + visit.assign(ROWS, vector(COLS, false)); + + for (int r = 0; r < ROWS; ++r) { + for (int c = 0; c < COLS; ++c) { + if (grid[r][c] == INF) { + grid[r][c] = dfs(grid, r, c); + } + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {void} + */ + islandsAndTreasure(grid) { + let ROWS = grid.length; + let COLS = grid[0].length; + const directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]; + const INF = 2147483647; + let visit = Array.from({ length: ROWS }, () => + Array(COLS).fill(false)); + + const dfs = (r, c) => { + if (r < 0 || c < 0 || r >= ROWS || + c >= COLS || grid[r][c] === -1 || visit[r][c]) { + return INF; + } + if (grid[r][c] === 0) { + return 0; + } + visit[r][c] = true; + let res = INF; + for (let [dx, dy] of directions) { + res = Math.min(res, 1 + dfs(r + dx, c + dy)); + } + visit[r][c] = false; + return res; + }; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] === INF) { + grid[r][c] = dfs(r, c); + } + } + } + } +} +``` + +```csharp +public class Solution { + private int[][] directions = new int[][] { + new int[] {1, 0}, new int[] {-1, 0}, + new int[] {0, 1}, new int[] {0, -1} + }; + private int INF = 2147483647; + private bool[,] visit; + private int ROWS, COLS; + + private int Dfs(int[][] grid, int r, int c) { + if (r < 0 || c < 0 || r >= ROWS || + c >= COLS || grid[r][c] == -1 || visit[r, c]) { + return INF; + } + if (grid[r][c] == 0) { + return 0; + } + visit[r, c] = true; + int res = INF; + foreach (var dir in directions) { + int cur = Dfs(grid, r + dir[0], c + dir[1]); + if (cur != INF) { + res = Math.Min(res, 1 + cur); + } + } + visit[r, c] = false; + return res; + } + + public void islandsAndTreasure(int[][] grid) { + ROWS = grid.Length; + COLS = grid[0].Length; + visit = new bool[ROWS, COLS]; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == INF) { + grid[r][c] = Dfs(grid, r, c); + } + } + } + } +} +``` + +```go +func islandsAndTreasure(grid [][]int) { + rows, cols := len(grid), len(grid[0]) + directions := [][]int{{1, 0}, {-1, 0}, {0, 1}, {0, -1}} + INF := 2147483647 + visit := make([][]bool, rows) + for i := range visit { + visit[i] = make([]bool, cols) + } + + var dfs func(r, c int) int + dfs = func(r, c int) int { + if r < 0 || c < 0 || r >= rows || c >= cols || + grid[r][c] == -1 || visit[r][c] { + return INF + } + if grid[r][c] == 0 { + return 0 + } + + visit[r][c] = true + res := INF + for _, d := range directions { + dx, dy := d[0], d[1] + res = min(res, 1+dfs(r+dx, c+dy)) + } + visit[r][c] = false + return res + } + + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + if grid[r][c] == INF { + grid[r][c] = dfs(r, c) + } + } + } +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + private val directions = arrayOf( + intArrayOf(1, 0), intArrayOf(-1, 0), + intArrayOf(0, 1), intArrayOf(0, -1) + ) + private val INF = 2147483647 + private lateinit var visit: Array + private var rows = 0 + private var cols = 0 + + private fun dfs(grid: Array, r: Int, c: Int): Int { + if (r < 0 || c < 0 || r >= rows || c >= cols || + grid[r][c] == -1 || visit[r][c]) { + return INF + } + if (grid[r][c] == 0) { + return 0 + } + visit[r][c] = true + var res = INF + for (dir in directions) { + val cur = dfs(grid, r + dir[0], c + dir[1]) + if (cur != INF) { + res = minOf(res, 1 + cur) + } + } + visit[r][c] = false + return res + } + + fun islandsAndTreasure(grid: Array): Unit { + rows = grid.size + cols = grid[0].size + visit = Array(rows) { BooleanArray(cols) } + + for (r in 0 until rows) { + for (c in 0 until cols) { + if (grid[r][c] == INF) { + grid[r][c] = dfs(grid, r, c) + } + } + } + } +} +``` + +```swift +class Solution { + func islandsAndTreasure(_ grid: inout [[Int]]) { + let ROWS = grid.count + let COLS = grid[0].count + let INF = 2147483647 + let directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + var visit = Array(repeating: Array(repeating: false, count: COLS), count: ROWS) + + func dfs(_ r: Int, _ c: Int) -> Int { + if r < 0 || c < 0 || r >= ROWS || c >= COLS || grid[r][c] == -1 || visit[r][c] { + return INF + } + if grid[r][c] == 0 { + return 0 + } + + visit[r][c] = true + var res = INF + for (dx, dy) in directions { + res = min(res, 1 + dfs(r + dx, c + dy)) + } + visit[r][c] = false + return res + } + + for r in 0.. Where $m$ is the number of rows and $n$ is the number of columns in the $grid$. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def islandsAndTreasure(self, grid: List[List[int]]) -> None: + ROWS, COLS = len(grid), len(grid[0]) + directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + INF = 2147483647 + + def bfs(r, c): + q = deque([(r, c)]) + visit = [[False] * COLS for _ in range(ROWS)] + visit[r][c] = True + steps = 0 + while q: + for _ in range(len(q)): + row, col = q.popleft() + if grid[row][col] == 0: + return steps + for dr, dc in directions: + nr, nc = row + dr, col + dc + if (0 <= nr < ROWS and 0 <= nc < COLS and + not visit[nr][nc] and grid[nr][nc] != -1 + ): + visit[nr][nc] = True + q.append((nr, nc)) + steps += 1 + return INF + + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] == INF: + grid[r][c] = bfs(r, c) +``` + +```java +public class Solution { + private int[][] directions = {{1, 0}, {-1, 0}, + {0, 1}, {0, -1}}; + private int INF = 2147483647; + private int ROWS, COLS; + + private int bfs(int[][] grid, int r, int c) { + Queue q = new LinkedList<>(); + q.add(new int[]{r, c}); + boolean[][] visit = new boolean[ROWS][COLS]; + visit[r][c] = true; + int steps = 0; + + while (!q.isEmpty()) { + int size = q.size(); + for (int i = 0; i < size; i++) { + int[] curr = q.poll(); + int row = curr[0], col = curr[1]; + if (grid[row][col] == 0) return steps; + for (int[] dir : directions) { + int nr = row + dir[0], nc = col + dir[1]; + if (nr >= 0 && nr < ROWS && nc >= 0 && nc < COLS && + !visit[nr][nc] && grid[nr][nc] != -1) { + visit[nr][nc] = true; + q.add(new int[]{nr, nc}); + } + } + } + steps++; + } + return INF; + } + + public void islandsAndTreasure(int[][] grid) { + ROWS = grid.length; + COLS = grid[0].length; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == INF) { + grid[r][c] = bfs(grid, r, c); + } + } + } + } +} +``` + +```cpp +class Solution { +public: + int ROWS, COLS; + vector> directions = {{1, 0}, {-1, 0}, + {0, 1}, {0, -1}}; + int INF = INT_MAX; + + int bfs(vector>& grid, int r, int c) { + queue> q; + q.push({r, c}); + vector> visit(ROWS, vector(COLS, false)); + visit[r][c] = true; + int steps = 0; + + while (!q.empty()) { + int size = q.size(); + for (int i = 0; i < size; i++) { + auto [row, col] = q.front(); + q.pop(); + if (grid[row][col] == 0) return steps; + for (auto& dir : directions) { + int nr = row + dir[0], nc = col + dir[1]; + if (nr >= 0 && nr < ROWS && nc >= 0 && nc < COLS && + !visit[nr][nc] && grid[nr][nc] != -1) { + visit[nr][nc] = true; + q.push({nr, nc}); + } + } + } + steps++; + } + return INF; + } + + void islandsAndTreasure(vector>& grid) { + ROWS = grid.size(); + COLS = grid[0].size(); + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == INF) { + grid[r][c] = bfs(grid, r, c); + } + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {void} + */ + islandsAndTreasure(grid) { + let ROWS = grid.length; + let COLS = grid[0].length; + const directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]; + const INF = 2147483647; + let visit = Array.from({ length: ROWS }, () => + Array(COLS).fill(false)); + + const bfs = (r, c) => { + const q = new Queue([[r, c]]); + const visit = Array.from({ length: ROWS }, () => + Array(COLS).fill(false)); + visit[r][c] = true; + let steps = 0; + + while (!q.isEmpty()) { + let size = q.size(); + for (let i = 0; i < size; i++) { + const [row, col] = q.pop(); + if (grid[row][col] === 0) return steps; + for (let [dr, dc] of directions) { + const nr = row + dr, nc = col + dc; + if (nr >= 0 && nr < ROWS && nc >= 0 && nc < COLS && + !visit[nr][nc] && grid[nr][nc] !== -1) { + visit[nr][nc] = true; + q.push([nr, nc]); + } + } + } + steps++; + } + return INF; + }; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] === INF) { + grid[r][c] = bfs(r, c); + } + } + } + } +} +``` + +```csharp +public class Solution { + private int[][] directions = new int[][] { + new int[] { 1, 0 }, new int[] { -1, 0 }, + new int[] { 0, 1 }, new int[] { 0, -1 } + }; + private int INF = int.MaxValue; + private int ROWS, COLS; + + private int Bfs(int[][] grid, int r, int c) { + var q = new Queue(); + q.Enqueue(new int[] { r, c }); + bool[][] visit = new bool[ROWS][]; + for (int i = 0; i < ROWS; i++) visit[i] = new bool[COLS]; + visit[r][c] = true; + int steps = 0; + + while (q.Count > 0) { + int size = q.Count; + for (int i = 0; i < size; i++) { + var curr = q.Dequeue(); + int row = curr[0], col = curr[1]; + if (grid[row][col] == 0) return steps; + foreach (var dir in directions) { + int nr = row + dir[0], nc = col + dir[1]; + if (nr >= 0 && nr < ROWS && nc >= 0 && nc < COLS && + !visit[nr][nc] && grid[nr][nc] != -1) { + visit[nr][nc] = true; + q.Enqueue(new int[] { nr, nc }); + } + } + } + steps++; + } + return INF; + } + + public void islandsAndTreasure(int[][] grid) { + ROWS = grid.Length; + COLS = grid[0].Length; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == INF) { + grid[r][c] = Bfs(grid, r, c); + } + } + } + } +} +``` + +```go +func islandsAndTreasure(grid [][]int) { + rows, cols := len(grid), len(grid[0]) + directions := [][]int{{1, 0}, {-1, 0}, {0, 1}, {0, -1}} + INF := 2147483647 + + bfs := func(r, c int) int { + q := [][2]int{{r, c}} + visit := make([][]bool, rows) + for i := range visit { + visit[i] = make([]bool, cols) + } + visit[r][c] = true + steps := 0 + + for len(q) > 0 { + size := len(q) + for i := 0; i < size; i++ { + current := q[0] + q = q[1:] + row, col := current[0], current[1] + if grid[row][col] == 0 { + return steps + } + for _, dir := range directions { + nr, nc := row+dir[0], col+dir[1] + if nr >= 0 && nc >= 0 && nr < rows && nc < cols && + !visit[nr][nc] && grid[nr][nc] != -1 { + visit[nr][nc] = true + q = append(q, [2]int{nr, nc}) + } + } + } + steps++ + } + return INF + } + + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + if grid[r][c] == INF { + grid[r][c] = bfs(r, c) + } + } + } +} +``` + +```kotlin +class Solution { + private val directions = arrayOf( + intArrayOf(1, 0), intArrayOf(-1, 0), + intArrayOf(0, 1), intArrayOf(0, -1) + ) + private val INF = 2147483647 + + fun islandsAndTreasure(grid: Array): Unit { + val rows = grid.size + val cols = grid[0].size + + fun bfs(r: Int, c: Int): Int { + val q = ArrayDeque>() + q.add(Pair(r, c)) + val visit = Array(rows) { BooleanArray(cols) } + visit[r][c] = true + var steps = 0 + + while (q.isNotEmpty()) { + repeat(q.size) { + val (row, col) = q.removeFirst() + if (grid[row][col] == 0) { + return steps + } + for (dir in directions) { + val nr = row + dir[0] + val nc = col + dir[1] + if (nr in 0 until rows && nc in 0 until cols && + !visit[nr][nc] && grid[nr][nc] != -1) { + visit[nr][nc] = true + q.add(Pair(nr, nc)) + } + } + } + steps++ + } + return INF + } + + for (r in 0 until rows) { + for (c in 0 until cols) { + if (grid[r][c] == INF) { + grid[r][c] = bfs(r, c) + } + } + } + } +} +``` + +```swift +class Solution { + func islandsAndTreasure(_ grid: inout [[Int]]) { + let ROWS = grid.count + let COLS = grid[0].count + let directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + let INF = 2147483647 + + func bfs(_ r: Int, _ c: Int) -> Int { + var q = Deque<(Int, Int)>() + q.append((r, c)) + var visit = Array(repeating: Array(repeating: false, count: COLS), count: ROWS) + visit[r][c] = true + var steps = 0 + while !q.isEmpty { + let levelCount = q.count + for _ in 0..= 0 && nr < ROWS && nc >= 0 && nc < COLS && + !visit[nr][nc] && grid[nr][nc] != -1) { + visit[nr][nc] = true + q.append((nr, nc)) + } + } + } + steps += 1 + } + return INF + } + + for r in 0.. Where $m$ is the number of rows and $n$ is the number of columns in the $grid$. + +--- + +## 3. Multi Source BFS + +::tabs-start + +```python +class Solution: + def islandsAndTreasure(self, grid: List[List[int]]) -> None: + ROWS, COLS = len(grid), len(grid[0]) + visit = set() + q = deque() + + def addCell(r, c): + if (min(r, c) < 0 or r == ROWS or c == COLS or + (r, c) in visit or grid[r][c] == -1 + ): + return + visit.add((r, c)) + q.append([r, c]) + + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] == 0: + q.append([r, c]) + visit.add((r, c)) + + dist = 0 + while q: + for i in range(len(q)): + r, c = q.popleft() + grid[r][c] = dist + addCell(r + 1, c) + addCell(r - 1, c) + addCell(r, c + 1) + addCell(r, c - 1) + dist += 1 +``` + +```java +public class Solution { + public void islandsAndTreasure(int[][] grid) { + Queue q = new LinkedList<>(); + int m = grid.length; + int n = grid[0].length; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == 0) { + q.add(new int[] { i, j }); + } + } + } + if (q.size() == 0) return; + + int[][] dirs = { { -1, 0 }, { 0, -1 }, + { 1, 0 }, { 0, 1 } }; + while (!q.isEmpty()) { + int[] node = q.poll(); + int row = node[0]; + int col = node[1]; + for (int[] dir : dirs) { + int r = row + dir[0]; + int c = col + dir[1]; + if (r >= m || c >= n || r < 0 || + c < 0 || grid[r][c] != Integer.MAX_VALUE) { + continue; + } + q.add(new int[] { r, c }); + + grid[r][c] = grid[row][col] + 1; + } + } + } +} +``` + +```cpp +class Solution { +public: + void islandsAndTreasure(vector>& grid) { + int m = grid.size(); + int n = grid[0].size(); + + queue> q; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == 0) { + q.push({i, j}); + } + } + } + + vector> dirs = {{-1, 0}, {1, 0}, + {0, -1}, {0, 1}}; + while (!q.empty()) { + int row = q.front().first; + int col = q.front().second; + q.pop(); + + for (int i = 0; i < 4; i++) { + int r = row + dirs[i][0]; + int c = col + dirs[i][1]; + + if (r < 0 || r >= m || c < 0 || + c >= n || grid[r][c] != INT_MAX) { + continue; + } + + grid[r][c] = grid[row][col] + 1; + q.push({r, c}); + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + */ + islandsAndTreasure(grid) { + let ROWS = grid.length; + let COLS = grid[0].length; + let visit = new Set(); + let q = new Queue(); + + /** + * @param {number} r + * @param {number} c + */ + function addCell(r, c) { + if (Math.min(r, c) < 0 || r === ROWS || c === COLS || + visit.has(r + ',' + c) || grid[r][c] === -1 + ) { + return; + } + visit.add(r + ',' + c); + q.push([r, c]); + } + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] === 0) { + q.push([r, c]); + visit.add(r + ',' + c); + } + } + } + + let dist = 0; + while (!q.isEmpty()) { + for (let i = q.size(); i > 0; i--) { + let [r, c] = q.pop(); + grid[r][c] = dist; + addCell(r + 1, c); + addCell(r - 1, c); + addCell(r, c + 1); + addCell(r, c - 1); + } + dist += 1; + } + } +} +``` + +```csharp +public class Solution { + public void islandsAndTreasure(int[][] grid) { + Queue q = new Queue(); + int m = grid.Length; + int n = grid[0].Length; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == 0) q.Enqueue(new int[] { i, j }); + } + } + + if (q.Count == 0) return; + + int[][] dirs = { + new int[] { -1, 0 }, new int[] { 0, -1 }, + new int[] { 1, 0 }, new int[] { 0, 1 } + }; + while (q.Count > 0) { + int[] cur = q.Dequeue(); + int row = cur[0]; + int col = cur[1]; + foreach (int[] dir in dirs) { + int r = row + dir[0]; + int c = col + dir[1]; + if (r >= m || c >= n || r < 0 || + c < 0 || grid[r][c] != int.MaxValue) { + continue; + } + q.Enqueue(new int[] { r, c }); + + grid[r][c] = grid[row][col] + 1; + } + } + } +} +``` + +```go +func islandsAndTreasure(grid [][]int) { + m, n := len(grid), len(grid[0]) + q := [][2]int{} + + for i := 0; i < m; i++ { + for j := 0; j < n; j++ { + if grid[i][j] == 0 { + q = append(q, [2]int{i, j}) + } + } + } + if len(q) == 0 { + return + } + + dirs := [][]int{{-1, 0}, {0, -1}, {1, 0}, {0, 1}} + + for len(q) > 0 { + node := q[0] + q = q[1:] + row, col := node[0], node[1] + + for _, dir := range dirs { + r, c := row+dir[0], col+dir[1] + if r >= m || c >= n || r < 0 || c < 0 || + grid[r][c] != 2147483647 { + continue + } + q = append(q, [2]int{r, c}) + grid[r][c] = grid[row][col] + 1 + } + } +} +``` + +```kotlin +class Solution { + fun islandsAndTreasure(grid: Array): Unit { + val m = grid.size + val n = grid[0].size + val q: Queue> = LinkedList() + + for (i in 0 until m) { + for (j in 0 until n) { + if (grid[i][j] == 0) { + q.add(Pair(i, j)) + } + } + } + if (q.isEmpty()) return + + val dirs = arrayOf( + intArrayOf(-1, 0), intArrayOf(0, -1), + intArrayOf(1, 0), intArrayOf(0, 1) + ) + + while (q.isNotEmpty()) { + val (row, col) = q.poll() + for (dir in dirs) { + val r = row + dir[0] + val c = col + dir[1] + if (r !in 0 until m || c !in 0 until n || + grid[r][c] != Int.MAX_VALUE) { + continue + } + q.add(Pair(r, c)) + grid[r][c] = grid[row][col] + 1 + } + } + } +} +``` + +```swift +struct Item: Hashable { + let r: Int + let c: Int +} + +class Solution { + func islandsAndTreasure(_ grid: inout [[Int]]) { + let ROWS = grid.count + let COLS = grid[0].count + var visit = Set() + var q = Deque() + + func addCell(_ r: Int, _ c: Int) { + if r < 0 || c < 0 || r >= ROWS || c >= COLS { return } + let item = Item(r: r, c: c) + if visit.contains(item) || grid[r][c] == -1 { return } + visit.insert(item) + q.append(item) + } + + for r in 0.. Where $m$ is the number of rows and $n$ is the number of columns in the $grid$. \ No newline at end of file diff --git a/articles/isomorphic-strings.md b/articles/isomorphic-strings.md new file mode 100644 index 000000000..afbaa6c10 --- /dev/null +++ b/articles/isomorphic-strings.md @@ -0,0 +1,201 @@ +## 1. Hash Map (Two Pass) + +::tabs-start + +```python +class Solution: + def helper(self, s: str, t: str) -> bool: + mp = {} + for i in range(len(s)): + if (s[i] in mp) and (mp[s[i]] != t[i]): + return False + mp[s[i]] = t[i] + return True + + def isIsomorphic(self, s: str, t: str) -> bool: + return self.helper(s, t) and self.helper(t, s) +``` + +```java +public class Solution { + private boolean helper(String s, String t) { + HashMap map = new HashMap<>(); + for (int i = 0; i < s.length(); i++) { + char sc = s.charAt(i); + char tc = t.charAt(i); + if (map.containsKey(sc) && map.get(sc) != tc) { + return false; + } + map.put(sc, tc); + } + return true; + } + + public boolean isIsomorphic(String s, String t) { + return helper(s, t) && helper(t, s); + } +} +``` + +```cpp +class Solution { +private: + bool helper(const string& s, const string& t) { + unordered_map map; + for (int i = 0; i < s.length(); i++) { + if (map.count(s[i]) && map[s[i]] != t[i]) { + return false; + } + map[s[i]] = t[i]; + } + return true; + } + +public: + bool isIsomorphic(string s, string t) { + return helper(s, t) && helper(t, s); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + helper(s, t) { + const map = new Map(); + for (let i = 0; i < s.length; i++) { + if (map.has(s[i]) && map.get(s[i]) !== t[i]) { + return false; + } + map.set(s[i], t[i]); + } + return true; + } + + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + isIsomorphic(s, t) { + return this.helper(s, t) && this.helper(t, s); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the input string and $m$ is the number of unique characters in the strings. + +--- + +## 2. Hash Map (One Pass) + +::tabs-start + +```python +class Solution: + def isIsomorphic(self, s: str, t: str) -> bool: + mapST, mapTS = {}, {} + + for i in range(len(s)): + c1, c2 = s[i], t[i] + if ((c1 in mapST and mapST[c1] != c2) or + (c2 in mapTS and mapTS[c2] != c1)): + return False + mapST[c1] = c2 + mapTS[c2] = c1 + + return True +``` + +```java +public class Solution { + public boolean isIsomorphic(String s, String t) { + HashMap mapST = new HashMap<>(); + HashMap mapTS = new HashMap<>(); + + for (int i = 0; i < s.length(); i++) { + char c1 = s.charAt(i), c2 = t.charAt(i); + + if ((mapST.containsKey(c1) && mapST.get(c1) != c2) || + (mapTS.containsKey(c2) && mapTS.get(c2) != c1)) { + return false; + } + + mapST.put(c1, c2); + mapTS.put(c2, c1); + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isIsomorphic(string s, string t) { + unordered_map mapST, mapTS; + + for (int i = 0; i < s.size(); i++) { + char c1 = s[i], c2 = t[i]; + + if ((mapST.count(c1) && mapST[c1] != c2) || + (mapTS.count(c2) && mapTS[c2] != c1)) { + return false; + } + + mapST[c1] = c2; + mapTS[c2] = c1; + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + isIsomorphic(s, t) { + const mapTS = new Map(); + + for (let i = 0; i < s.length; i++) { + const c1 = s[i], c2 = t[i]; + + if ((mapST.has(c1) && mapST.get(c1) !== c2) || + (mapTS.has(c2) && mapTS.get(c2) !== c1)) { + return false; + } + + mapST.set(c1, c2); + mapTS.set(c2, c1); + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the input string and $m$ is the number of unique characters in the strings. \ No newline at end of file diff --git a/articles/jump-game-ii.md b/articles/jump-game-ii.md new file mode 100644 index 000000000..db4a1b445 --- /dev/null +++ b/articles/jump-game-ii.md @@ -0,0 +1,799 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def jump(self, nums: List[int]) -> int: + def dfs(i): + if i == len(nums) - 1: + return 0 + if nums[i] == 0: + return float('inf') + + end = min(len(nums) - 1, i + nums[i]) + res = float('inf') + for j in range(i + 1, end + 1): + res = min(res, 1 + dfs(j)) + return res + + return dfs(0) +``` + +```java +public class Solution { + public int jump(int[] nums) { + return dfs(nums, 0); + } + + private int dfs(int[] nums, int i) { + if (i == nums.length - 1) { + return 0; + } + if (nums[i] == 0) { + return 1000000; + } + int res = 1000000; + int end = Math.min(nums.length - 1, i + nums[i]); + for (int j = i + 1; j <= end; j++) { + res = Math.min(res, 1 + dfs(nums, j)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int jump(vector& nums) { + return dfs(nums, 0); + } + +private: + int dfs(vector& nums, int i) { + if (i == nums.size() - 1) { + return 0; + } + if (nums[i] == 0) { + return 1000000; + } + + int res = 1000000; + int end = min((int)nums.size() - 1, i + nums[i]); + for (int j = i + 1; j <= end; ++j) { + res = min(res, 1 + dfs(nums, j)); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + jump(nums) { + const dfs = (i) => { + if (i === nums.length - 1) { + return 0; + } + if (nums[i] === 0) return 1000000; + let res = 1000000; + const end = Math.min(nums.length - 1, i + nums[i]); + for (let j = i + 1; j <= end; j++) { + res = Math.min(res, 1 + dfs(j)); + } + return res; + } + + return dfs(0); + } +} +``` + +```csharp +public class Solution { + public int Jump(int[] nums) { + return Dfs(nums, 0); + } + + private int Dfs(int[] nums, int i) { + if (i == nums.Length - 1) { + return 0; + } + if (nums[i] == 0) return 1000000; + int res = 1000000; + int end = Math.Min(nums.Length - 1, i + nums[i]); + for (int j = i + 1; j <= end; j++) { + res = Math.Min(res, 1 + Dfs(nums, j)); + } + return res; + } +} +``` + +```go +func jump(nums []int) int { + var dfs func(int) int + dfs = func(i int) int { + if i == len(nums)-1 { + return 0 + } + if nums[i] == 0 { + return math.MaxInt32 + } + end := min(len(nums)-1, i+nums[i]) + res := math.MaxInt32 + for j := i + 1; j <= end; j++ { + res = min(res, 1+dfs(j)) + } + return res + } + return dfs(0) +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun jump(nums: IntArray): Int { + fun dfs(i: Int): Int { + if (i == nums.size - 1) { + return 0 + } + if (nums[i] == 0) { + return 1000000 + } + val end = minOf(nums.size - 1, i + nums[i]) + var minJumps = 1000000 + for (j in i + 1..end) { + minJumps = minOf(minJumps, 1 + dfs(j)) + } + return minJumps + } + return dfs(0) + } +} +``` + +```swift +class Solution { + func jump(_ nums: [Int]) -> Int { + func dfs(_ i: Int) -> Int { + if i == nums.count - 1 { + return 0 + } + if nums[i] == 0 { + return 1000000 + } + + let end = min(nums.count - 1, i + nums[i]) + var res = 1000000 + for j in i + 1..<(end + 1) { + res = min(res, 1 + dfs(j)) + } + + return res + } + + return dfs(0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n!)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def jump(self, nums: List[int]) -> int: + memo = {} + + def dfs(i): + if i in memo: + return memo[i] + if i == len(nums) - 1: + return 0 + if nums[i] == 0: + return 1000000 + + res = 1000000 + end = min(len(nums), i + nums[i] + 1) + for j in range(i + 1, end): + res = min(res, 1 + dfs(j)) + memo[i] = res + return res + + return dfs(0) +``` + +```java +public class Solution { + public int jump(int[] nums) { + Map memo = new HashMap<>(); + return dfs(nums, 0, memo); + } + + private int dfs(int[] nums, int i, Map memo) { + if (memo.containsKey(i)) { + return memo.get(i); + } + if (i == nums.length - 1) { + return 0; + } + if (nums[i] == 0) { + return 1000000; + } + + int res = 1000000; + int end = Math.min(nums.length, i + nums[i] + 1); + for (int j = i + 1; j < end; j++) { + res = Math.min(res, 1 + dfs(nums, j, memo)); + } + memo.put(i, res); + return res; + } +} +``` + +```cpp +class Solution { +public: + int jump(vector& nums) { + unordered_map memo; + return dfs(nums, 0, memo); + } + +private: + int dfs(vector& nums, int i, unordered_map& memo) { + if (memo.count(i)) { + return memo[i]; + } + if (i == nums.size() - 1) { + return 0; + } + if (nums[i] == 0) { + return 1000000; + } + + int res = 1000000; + int end = min((int)nums.size(), i + nums[i] + 1); + for (int j = i + 1; j < end; j++) { + res = min(res, 1 + dfs(nums, j, memo)); + } + memo[i] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + jump(nums) { + const memo = new Map(); + const dfs = (i) => { + if (memo.has(i)) { + return memo.get(i); + } + if (i == nums.length - 1) { + return 0; + } + if (nums[i] === 0) { + return 1000000; + } + let res = 1000000; + const end = Math.min(nums.length - 1, i + nums[i]); + for (let j = i + 1; j <= end; j++) { + res = Math.min(res, 1 + dfs(j)); + } + memo.set(i, res); + return res; + } + + return dfs(0); + } +} +``` + +```csharp +public class Solution { + public int Jump(int[] nums) { + var memo = new Dictionary(); + return Dfs(nums, 0, memo); + } + + private int Dfs(int[] nums, int i, Dictionary memo) { + if (memo.ContainsKey(i)) { + return memo[i]; + } + if (i == nums.Length - 1) { + return 0; + } + if (nums[i] == 0) { + return 1000000; + } + + int res = 1000000; + int end = Math.Min(nums.Length, i + nums[i] + 1); + for (int j = i + 1; j < end; j++) { + res = Math.Min(res, 1 + Dfs(nums, j, memo)); + } + memo[i] = res; + return res; + } +} +``` + +```go +func jump(nums []int) int { + memo := make(map[int]int) + + var dfs func(int) int + dfs = func(i int) int { + if val, ok := memo[i]; ok { + return val + } + if i == len(nums)-1 { + return 0 + } + if nums[i] == 0 { + return 1000000 + } + res := 1000000 + end := min(len(nums), i+nums[i]+1) + for j := i + 1; j < end; j++ { + res = min(res, 1+dfs(j)) + } + memo[i] = res + return res + } + return dfs(0) +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun jump(nums: IntArray): Int { + val memo = HashMap() + + fun dfs(i: Int): Int { + if (i in memo) { + return memo[i]!! + } + if (i == nums.size - 1) { + return 0 + } + if (nums[i] == 0) { + return 1000000 + } + var res = 1000000 + val end = minOf(nums.size, i + nums[i] + 1) + for (j in i + 1 until end) { + res = minOf(res, 1 + dfs(j)) + } + memo[i] = res + return res + } + + return dfs(0) + } +} +``` + +```swift +class Solution { + var memo: [Int: Int] = [:] + + func jump(_ nums: [Int]) -> Int { + return dfs(nums, 0) + } + + private func dfs(_ nums: [Int], _ i: Int) -> Int { + if let cachedResult = memo[i] { + return cachedResult + } + if i == nums.count - 1 { + return 0 + } + if nums[i] == 0 { + return 1000000 + } + + var res = 1000000 + let end = min(nums.count, i + nums[i] + 1) + for j in i + 1.. int: + n = len(nums) + dp = [1000000] * n + dp[-1] = 0 + + for i in range(n - 2, -1, -1): + end = min(n, i + nums[i] + 1) + for j in range(i + 1, end): + dp[i] = min(dp[i], 1 + dp[j]) + return dp[0] +``` + +```java +public class Solution { + public int jump(int[] nums) { + int n = nums.length; + int[] dp = new int[n]; + Arrays.fill(dp, 1000000); + dp[n - 1] = 0; + + for (int i = n - 2; i >= 0; i--) { + int end = Math.min(nums.length, i + nums[i] + 1); + for (int j = i + 1; j < end; j++) { + dp[i] = Math.min(dp[i], 1 + dp[j]); + } + } + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int jump(vector& nums) { + int n = nums.size(); + vector dp(n, 1000000); + dp[n - 1] = 0; + + for (int i = n - 2; i >= 0; i--) { + int end = min((int)nums.size(), i + nums[i] + 1); + for (int j = i + 1; j < end; j++) { + dp[i] = min(dp[i], 1 + dp[j]); + } + } + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + jump(nums) { + const n = nums.length; + const dp = new Array(n).fill(1000000); + dp[n - 1] = 0; + + for (let i = n - 2; i >= 0; i--) { + const end = Math.min(nums.length, i + nums[i] + 1); + for (let j = i + 1; j < end; j++) { + dp[i] = Math.min(dp[i], 1 + dp[j]); + } + } + return dp[0]; + } +} +``` + +```csharp +public class Solution { + public int Jump(int[] nums) { + int n = nums.Length; + int[] dp = new int[n]; + Array.Fill(dp, 1000000); + dp[n - 1] = 0; + + for (int i = n - 2; i >= 0; i--) { + int end = Math.Min(nums.Length, i + nums[i] + 1); + for (int j = i + 1; j < end; j++) { + dp[i] = Math.Min(dp[i], 1 + dp[j]); + } + } + return dp[0]; + } +} +``` + +```go +func jump(nums []int) int { + n := len(nums) + dp := make([]int, n) + for i := range dp { + dp[i] = 1000000 + } + dp[n-1] = 0 + + for i := n - 2; i >= 0; i-- { + end := min(n, i+nums[i]+1) + for j := i + 1; j < end; j++ { + dp[i] = min(dp[i], 1+dp[j]) + } + } + return dp[0] +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun jump(nums: IntArray): Int { + val n = nums.size + val dp = IntArray(n) { 1000000 } + dp[n - 1] = 0 + + for (i in n - 2 downTo 0) { + val end = minOf(n, i + nums[i] + 1) + for (j in i + 1 until end) { + dp[i] = minOf(dp[i], 1 + dp[j]) + } + } + return dp[0] + } +} +``` + +```swift +class Solution { + func jump(_ nums: [Int]) -> Int { + let n = nums.count + var dp = Array(repeating: 1000000, count: n) + dp[n - 1] = 0 + + for i in stride(from: n - 2, through: 0, by: -1) { + let end = min(n, i + nums[i] + 1) + for j in i + 1.. int: + res = 0 + l = r = 0 + + while r < len(nums) - 1: + farthest = 0 + for i in range(l, r + 1): + farthest = max(farthest, i + nums[i]) + l = r + 1 + r = farthest + res += 1 + return res +``` + +```java +public class Solution { + public int jump(int[] nums) { + int res = 0, l = 0, r = 0; + + while (r < nums.length - 1) { + int farthest = 0; + for (int i = l; i <= r; i++) { + farthest = Math.max(farthest, i + nums[i]); + } + l = r + 1; + r = farthest; + res++; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int jump(vector& nums) { + int res = 0, l = 0, r = 0; + + while (r < nums.size() - 1) { + int farthest = 0; + for (int i = l; i <= r; i++) { + farthest = max(farthest, i + nums[i]); + } + l = r + 1; + r = farthest; + res++; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + jump(nums) { + let res = 0, l = 0, r = 0; + + while (r < nums.length - 1) { + let farthest = 0; + for (let i = l; i <= r; i++) { + farthest = Math.max(farthest, i + nums[i]); + } + l = r + 1; + r = farthest; + res++; + } + return res; + } +} +``` + +```csharp +public class Solution { + public int Jump(int[] nums) { + int res = 0, l = 0, r = 0; + + while (r < nums.Length - 1) { + int farthest = 0; + for (int i = l; i <= r; i++) { + farthest = Math.Max(farthest, i + nums[i]); + } + l = r + 1; + r = farthest; + res++; + } + return res; + } +} +``` + +```go +func jump(nums []int) int { + res := 0 + l, r := 0, 0 + + for r < len(nums)-1 { + farthest := 0 + for i := l; i <= r; i++ { + farthest = max(farthest, i+nums[i]) + } + l = r + 1 + r = farthest + res++ + } + + return res +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun jump(nums: IntArray): Int { + var res = 0 + var l = 0 + var r = 0 + + while (r < nums.size - 1) { + var farthest = 0 + for (i in l..r) { + farthest = maxOf(farthest, i + nums[i]) + } + l = r + 1 + r = farthest + res++ + } + + return res + } +} +``` + +```swift +class Solution { + func jump(_ nums: [Int]) -> Int { + var res = 0 + var l = 0 + var r = 0 + + while r < nums.count - 1 { + var farthest = 0 + for i in l...r { + farthest = max(farthest, i + nums[i]) + } + l = r + 1 + r = farthest + res += 1 + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/jump-game-vii.md b/articles/jump-game-vii.md new file mode 100644 index 000000000..d2be241f9 --- /dev/null +++ b/articles/jump-game-vii.md @@ -0,0 +1,526 @@ +## 1. Brute Force (Memoization) + +::tabs-start + +```python +class Solution: + def canReach(self, s: str, minJump: int, maxJump: int) -> bool: + n = len(s) + dp = [None] * n + dp[n - 1] = True + + def dfs(i): + if dp[i] is not None: + return dp[i] + + dp[i] = False + for j in range(i + minJump, min(n, i + maxJump + 1)): + if s[j] == '0' and dfs(j): + dp[i] = True + break + + return dp[i] + + if s[-1] == '1': + return False + return dfs(0) +``` + +```java +public class Solution { + private Boolean[] dp; + private int n; + + public boolean canReach(String s, int minJump, int maxJump) { + this.n = s.length(); + this.dp = new Boolean[n]; + dp[n - 1] = true; + + if (s.charAt(n - 1) == '1') { + return false; + } + + return dfs(0, s, minJump, maxJump); + } + + private boolean dfs(int i, String s, int minJump, int maxJump) { + if (dp[i] != null) { + return dp[i]; + } + + dp[i] = false; + for (int j = i + minJump; j <= Math.min(n - 1, i + maxJump); j++) { + if (s.charAt(j) == '0' && dfs(j, s, minJump, maxJump)) { + dp[i] = true; + break; + } + } + return dp[i]; + } +} +``` + +```cpp +class Solution { +public: + int n; + vector> dp; + + bool canReach(string s, int minJump, int maxJump) { + this->n = s.size(); + dp.resize(n, nullopt); + dp[n - 1] = true; + + if (s[n - 1] == '1') { + return false; + } + + return dfs(0, s, minJump, maxJump); + } + +private: + bool dfs(int i, string& s, int& minJump, int& maxJump) { + if (dp[i].has_value()) { + return dp[i].value(); + } + + dp[i] = false; + for (int j = i + minJump; j <= min(n - 1, i + maxJump); ++j) { + if (s[j] == '0' && dfs(j, s, minJump, maxJump)) { + dp[i] = true; + break; + } + } + return dp[i].value(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} minJump + * @param {number} maxJump + * @return {boolean} + */ + canReach(s, minJump, maxJump) { + const n = s.length; + const dp = new Array(n).fill(null); + dp[n - 1] = true; + + const dfs = (i) => { + if (dp[i] !== null) { + return dp[i]; + } + + dp[i] = false; + for (let j = i + minJump; j <= Math.min(n - 1, i + maxJump); j++) { + if (s[j] === '0' && dfs(j)) { + dp[i] = true; + break; + } + } + return dp[i]; + }; + + if (s[n - 1] === '1') { + return false; + } + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the string $s$ and $m$ is the given range of the jump $(maxJump - minJump + 1)$. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def canReach(self, s: str, minJump: int, maxJump: int) -> bool: + q = deque([0]) + farthest = 0 + + while q: + i = q.popleft() + start = max(i + minJump, farthest + 1) + for j in range(start, min(i + maxJump + 1, len(s))): + if s[j] == "0": + q.append(j) + if j == len(s) - 1: + return True + farthest = i + maxJump + + return False +``` + +```java +public class Solution { + public boolean canReach(String s, int minJump, int maxJump) { + Queue q = new LinkedList<>(); + q.add(0); + int farthest = 0; + int n = s.length(); + + while (!q.isEmpty()) { + int i = q.poll(); + int start = Math.max(i + minJump, farthest + 1); + + for (int j = start; j < Math.min(i + maxJump + 1, n); j++) { + if (s.charAt(j) == '0') { + q.add(j); + if (j == n - 1) { + return true; + } + } + } + farthest = i + maxJump; + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool canReach(string s, int minJump, int maxJump) { + queue q; + q.push(0); + int farthest = 0; + int n = s.size(); + + while (!q.empty()) { + int i = q.front(); + q.pop(); + int start = max(i + minJump, farthest + 1); + + for (int j = start; j < min(i + maxJump + 1, n); ++j) { + if (s[j] == '0') { + q.push(j); + if (j == n - 1) { + return true; + } + } + } + farthest = i + maxJump; + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} minJump + * @param {number} maxJump + * @return {boolean} + */ + canReach(s, minJump, maxJump) { + const q = new Queue(); + q.push(0); + let farthest = 0; + + while (!q.isEmpty()) { + const i = q.pop(); + const start = Math.max(i + minJump, farthest + 1); + + for (let j = start; j < Math.min(i + maxJump + 1, s.length); j++) { + if (s[j] === '0') { + q.push(j); + if (j === s.length - 1) { + return true; + } + } + } + farthest = i + maxJump; + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Sliding Window) + +::tabs-start + +```python +class Solution: + def canReach(self, s: str, minJump: int, maxJump: int) -> bool: + n = len(s) + if s[n - 1] == '1': + return False + + dp = [False] * n + dp[0] = True + cnt = 0 + for i in range(1, n): + if i >= minJump and dp[i - minJump]: + cnt += 1 + if i > maxJump and dp[i - maxJump - 1]: + cnt -= 1 + if cnt > 0 and s[i] == '0': + dp[i] = True + + return dp[n - 1] +``` + +```java +public class Solution { + public boolean canReach(String s, int minJump, int maxJump) { + int n = s.length(); + if (s.charAt(n - 1) == '1') { + return false; + } + + boolean[] dp = new boolean[n]; + dp[0] = true; + int cnt = 0; + + for (int i = 1; i < n; i++) { + if (i >= minJump && dp[i - minJump]) { + cnt++; + } + if (i > maxJump && dp[i - maxJump - 1]) { + cnt--; + } + if (cnt > 0 && s.charAt(i) == '0') { + dp[i] = true; + } + } + + return dp[n - 1]; + } +} +``` + +```cpp +class Solution { +public: + bool canReach(string s, int minJump, int maxJump) { + int n = s.size(); + if (s[n - 1] == '1') { + return false; + } + + vector dp(n, false); + dp[0] = true; + int cnt = 0; + + for (int i = 1; i < n; i++) { + if (i >= minJump && dp[i - minJump]) { + cnt++; + } + if (i > maxJump && dp[i - maxJump - 1]) { + cnt--; + } + if (cnt > 0 && s[i] == '0') { + dp[i] = true; + } + } + + return dp[n - 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} minJump + * @param {number} maxJump + * @return {boolean} + */ + canReach(s, minJump, maxJump) { + const n = s.length; + if (s[n - 1] === '1') { + return false; + } + + const dp = new Array(n).fill(false); + dp[0] = true; + let cnt = 0; + + for (let i = 1; i < n; i++) { + if (i >= minJump && dp[i - minJump]) { + cnt++; + } + if (i > maxJump && dp[i - maxJump - 1]) { + cnt--; + } + if (cnt > 0 && s[i] === '0') { + dp[i] = true; + } + } + + return dp[n - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Two Pointers) + +::tabs-start + +```python +class Solution: + def canReach(self, s: str, minJump: int, maxJump: int) -> bool: + n = len(s) + if s[n - 1] == '1': + return False + + dp = [False] * n + dp[0] = True + j = 0 + for i in range(n): + if dp[i] == False: + continue + + j = max(j, i + minJump) + while j < min(i + maxJump + 1, n): + if s[j] == '0': + dp[j] = True + j += 1 + + return dp[n - 1] +``` + +```java +public class Solution { + public boolean canReach(String s, int minJump, int maxJump) { + int n = s.length(); + if (s.charAt(n - 1) == '1') { + return false; + } + + boolean[] dp = new boolean[n]; + dp[0] = true; + int j = 0; + + for (int i = 0; i < n; i++) { + if (!dp[i]) { + continue; + } + j = Math.max(j, i + minJump); + while (j < Math.min(i + maxJump + 1, n)) { + if (s.charAt(j) == '0') { + dp[j] = true; + } + j++; + } + } + + return dp[n - 1]; + } +} +``` + +```cpp +class Solution { +public: + bool canReach(string s, int minJump, int maxJump) { + int n = s.size(); + if (s[n - 1] == '1') { + return false; + } + + vector dp(n, false); + dp[0] = true; + int j = 0; + + for (int i = 0; i < n; i++) { + if (!dp[i]) { + continue; + } + j = max(j, i + minJump); + while (j < min(i + maxJump + 1, n)) { + if (s[j] == '0') { + dp[j] = true; + } + j++; + } + } + + return dp[n - 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} minJump + * @param {number} maxJump + * @return {boolean} + */ + canReach(s, minJump, maxJump) { + const n = s.length; + if (s[n - 1] === '1') { + return false; + } + + const dp = new Array(n).fill(false); + dp[0] = true; + let j = 0; + + for (let i = 0; i < n; i++) { + if (!dp[i]) { + continue; + } + j = Math.max(j, i + minJump); + while (j < Math.min(i + maxJump + 1, n)) { + if (s[j] === '0') { + dp[j] = true; + } + j++; + } + } + + return dp[n - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/jump-game.md b/articles/jump-game.md new file mode 100644 index 000000000..c5048055e --- /dev/null +++ b/articles/jump-game.md @@ -0,0 +1,784 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def canJump(self, nums: List[int]) -> bool: + def dfs(i): + if i == len(nums) - 1: + return True + end = min(len(nums) - 1, i + nums[i]) + for j in range(i + 1, end + 1): + if dfs(j): + return True + return False + + return dfs(0) +``` + +```java +public class Solution { + public boolean canJump(int[] nums) { + return dfs(nums, 0); + } + + private boolean dfs(int[] nums, int i) { + if (i == nums.length - 1) { + return true; + } + int end = Math.min(nums.length - 1, i + nums[i]); + for (int j = i + 1; j <= end; j++) { + if (dfs(nums, j)) { + return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool canJump(vector& nums) { + return dfs(nums, 0); + } + +private: + bool dfs(vector& nums, int i) { + if (i == nums.size() - 1) { + return true; + } + int end = min((int)nums.size() - 1, i + nums[i]); + for (int j = i + 1; j <= end; ++j) { + if (dfs(nums, j)) { + return true; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + canJump(nums) { + const dfs = (i) => { + if (i === nums.length - 1) { + return true; + } + const end = Math.min(nums.length - 1, i + nums[i]); + for (let j = i + 1; j <= end; j++) { + if (dfs(j)) { + return true; + } + } + return false; + } + + return dfs(0); + } +} +``` + +```csharp +public class Solution { + public bool CanJump(int[] nums) { + return Dfs(nums, 0); + } + + private bool Dfs(int[] nums, int i) { + if (i == nums.Length - 1) { + return true; + } + int end = Math.Min(nums.Length - 1, i + nums[i]); + for (int j = i + 1; j <= end; j++) { + if (Dfs(nums, j)) { + return true; + } + } + return false; + } +} +``` + +```go +func canJump(nums []int) bool { + var dfs func(i int) bool + dfs = func(i int) bool { + if i == len(nums)-1 { + return true + } + + end := min(len(nums)-1, i+nums[i]) + for j := i + 1; j <= end; j++ { + if dfs(j) { + return true + } + } + return false + } + + return dfs(0) +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun canJump(nums: IntArray): Boolean { + fun dfs(i: Int): Boolean { + if (i == nums.size - 1) { + return true + } + + val end = minOf(nums.size - 1, i + nums[i]) + for (j in (i + 1)..end) { + if (dfs(j)) { + return true + } + } + return false + } + + return dfs(0) + } +} +``` + +```swift +class Solution { + func canJump(_ nums: [Int]) -> Bool { + + func dfs(_ i: Int) -> Bool { + if i == nums.count - 1 { + return true + } + + let end = min(nums.count - 1, i + nums[i]) + for j in i + 1..<(end + 1) { + if dfs(j) { + return true + } + } + return false + } + + return dfs(0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n!)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def canJump(self, nums: List[int]) -> bool: + memo = {} + + def dfs(i): + if i in memo: + return memo[i] + if i == len(nums) - 1: + return True + if nums[i] == 0: + return False + + end = min(len(nums), i + nums[i] + 1) + for j in range(i + 1, end): + if dfs(j): + memo[i] = True + return True + memo[i] = False + return False + + return dfs(0) +``` + +```java +public class Solution { + public boolean canJump(int[] nums) { + Map memo = new HashMap<>(); + return dfs(nums, 0, memo); + } + + private boolean dfs(int[] nums, int i, Map memo) { + if (memo.containsKey(i)) { + return memo.get(i); + } + if (i == nums.length - 1) { + return true; + } + if (nums[i] == 0) { + return false; + } + + int end = Math.min(nums.length, i + nums[i] + 1); + for (int j = i + 1; j < end; j++) { + if (dfs(nums, j, memo)) { + memo.put(i, true); + return true; + } + } + memo.put(i, false); + return false; + } +} +``` + +```cpp +class Solution { +public: + bool canJump(vector& nums) { + unordered_map memo; + return dfs(nums, 0, memo); + } + +private: + bool dfs(vector& nums, int i, unordered_map& memo) { + if (memo.count(i)) { + return memo[i]; + } + if (i == nums.size() - 1) { + return true; + } + if (nums[i] == 0) { + return false; + } + + int end = min((int)nums.size(), i + nums[i] + 1); + for (int j = i + 1; j < end; j++) { + if (dfs(nums, j, memo)) { + memo[i] = true; + return true; + } + } + memo[i] = false; + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + canJump(nums) { + const memo = new Map(); + const dfs = (i) => { + if (memo.has(i)) { + return memo.get(i); + } + if (i == nums.length - 1) { + return true; + } + if (nums[i] === 0) { + return false; + } + const end = Math.min(nums.length - 1, i + nums[i]); + for (let j = i + 1; j <= end; j++) { + if (dfs(j)) { + memo.set(i, true); + return true; + } + } + memo.set(i, false); + return false; + } + + return dfs(0); + } +} +``` + +```csharp +public class Solution { + public bool CanJump(int[] nums) { + var memo = new Dictionary(); + return Dfs(nums, 0, memo); + } + + private bool Dfs(int[] nums, int i, Dictionary memo) { + if (memo.ContainsKey(i)) { + return memo[i]; + } + if (i >= nums.Length - 1) { + return true; + } + if (nums[i] == 0) { + return false; + } + + int end = Math.Min(nums.Length, i + nums[i] + 1); + for (int j = i + 1; j < end; j++) { + if (Dfs(nums, j, memo)) { + memo[i] = true; + return true; + } + } + memo[i] = false; + return false; + } +} +``` + +```go +func canJump(nums []int) bool { + memo := make(map[int]bool) + + var dfs func(i int) bool + dfs = func(i int) bool { + if result, exists := memo[i]; exists { + return result + } + + if i == len(nums)-1 { + return true + } + if nums[i] == 0 { + memo[i] = false + return false + } + + end := min(len(nums), i+nums[i]+1) + for j := i + 1; j < end; j++ { + if dfs(j) { + memo[i] = true + return true + } + } + + memo[i] = false + return false + } + + return dfs(0) +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun canJump(nums: IntArray): Boolean { + val memo = HashMap() + + fun dfs(i: Int): Boolean { + memo[i]?.let { return it } + + if (i == nums.size - 1) { + return true + } + if (nums[i] == 0) { + memo[i] = false + return false + } + + val end = minOf(nums.size, i + nums[i] + 1) + for (j in (i + 1) until end) { + if (dfs(j)) { + memo[i] = true + return true + } + } + + memo[i] = false + return false + } + + return dfs(0) + } +} +``` + +```swift +class Solution { + func canJump(_ nums: [Int]) -> Bool { + var memo: [Int: Bool] = [:] + + func dfs(_ i: Int) -> Bool { + if let cachedResult = memo[i] { + return cachedResult + } + if i == nums.count - 1 { + return true + } + if nums[i] == 0 { + return false + } + + let end = min(nums.count, i + nums[i] + 1) + for j in i + 1.. bool: + n = len(nums) + dp = [False] * n + dp[-1] = True + + for i in range(n - 2, -1, -1): + end = min(n, i + nums[i] + 1) + for j in range(i + 1, end): + if dp[j]: + dp[i] = True + break + return dp[0] +``` + +```java +public class Solution { + public boolean canJump(int[] nums) { + int n = nums.length; + boolean[] dp = new boolean[n]; + dp[n - 1] = true; + + for (int i = n - 2; i >= 0; i--) { + int end = Math.min(nums.length, i + nums[i] + 1); + for (int j = i + 1; j < end; j++) { + if (dp[j]) { + dp[i] = true; + break; + } + } + } + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + bool canJump(vector& nums) { + int n = nums.size(); + vector dp(n, false); + dp[n - 1] = true; + + for (int i = n - 2; i >= 0; i--) { + int end = min((int)nums.size(), i + nums[i] + 1); + for (int j = i + 1; j < end; j++) { + if (dp[j]) { + dp[i] = true; + break; + } + } + } + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + canJump(nums) { + const n = nums.length; + const dp = new Array(n).fill(false); + dp[n - 1] = true; + + for (let i = n - 2; i >= 0; i--) { + const end = Math.min(nums.length, i + nums[i] + 1); + for (let j = i + 1; j < end; j++) { + if (dp[j]) { + dp[i] = true; + break; + } + } + } + return dp[0]; + } +} +``` + +```csharp +public class Solution { + public bool CanJump(int[] nums) { + int n = nums.Length; + bool[] dp = new bool[n]; + dp[n - 1] = true; + + for (int i = n - 2; i >= 0; i--) { + int end = Math.Min(nums.Length, i + nums[i] + 1); + for (int j = i + 1; j < end; j++) { + if (dp[j]) { + dp[i] = true; + break; + } + } + } + return dp[0]; + } +} +``` + +```go +func canJump(nums []int) bool { + n := len(nums) + dp := make([]bool, n) + dp[n-1] = true + + for i := n - 2; i >= 0; i-- { + end := min(n, i + nums[i] + 1) + for j := i + 1; j < end; j++ { + if dp[j] { + dp[i] = true + break + } + } + } + return dp[0] +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun canJump(nums: IntArray): Boolean { + val n = nums.size + val dp = BooleanArray(n) + dp[n - 1] = true + + for (i in n - 2 downTo 0) { + val end = minOf(n, i + nums[i] + 1) + for (j in i + 1 until end) { + if (dp[j]) { + dp[i] = true + break + } + } + } + return dp[0] + } +} +``` + +```swift +class Solution { + func canJump(_ nums: [Int]) -> Bool { + let n = nums.count + var dp = [Bool](repeating: false, count: n) + dp[n - 1] = true + + for i in (0.. bool: + goal = len(nums) - 1 + + for i in range(len(nums) - 2, -1, -1): + if i + nums[i] >= goal: + goal = i + return goal == 0 +``` + +```java +public class Solution { + public boolean canJump(int[] nums) { + int goal = nums.length - 1; + + for (int i = nums.length - 2; i >= 0; i--) { + if (i + nums[i] >= goal) { + goal = i; + } + } + + return goal == 0; + } +} +``` + +```cpp +class Solution { +public: + bool canJump(vector& nums) { + int goal = nums.size() - 1; + + for (int i = nums.size() - 2; i >= 0; i--) { + if (i + nums[i] >= goal) { + goal = i; + } + } + + return goal == 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + canJump(nums) { + let goal = nums.length - 1; + + for (let i = nums.length - 2; i >= 0; i--) { + if (i + nums[i] >= goal) { + goal = i; + } + } + + return goal === 0; + } +} +``` + +```csharp +public class Solution { + public bool CanJump(int[] nums) { + int goal = nums.Length - 1; + + for (int i = nums.Length - 2; i >= 0; i--) { + if (i + nums[i] >= goal) { + goal = i; + } + } + + return goal == 0; + } +} +``` + +```go +func canJump(nums []int) bool { + goal := len(nums) - 1 + + for i := len(nums) - 2; i >= 0; i-- { + if i + nums[i] >= goal { + goal = i + } + } + return goal == 0 +} +``` + +```kotlin +class Solution { + fun canJump(nums: IntArray): Boolean { + var goal = nums.size - 1 + + for (i in nums.size - 2 downTo 0) { + if (i + nums[i] >= goal) { + goal = i + } + } + return goal == 0 + } +} +``` + +```swift +class Solution { + func canJump(_ nums: [Int]) -> Bool { + var goal = nums.count - 1 + + for i in stride(from: nums.count - 2, through: 0, by: -1) { + if i + nums[i] >= goal { + goal = i + } + } + + return goal == 0 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/k-closest-points-to-origin.md b/articles/k-closest-points-to-origin.md new file mode 100644 index 000000000..7882e7c18 --- /dev/null +++ b/articles/k-closest-points-to-origin.md @@ -0,0 +1,848 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]: + points.sort(key=lambda p: p[0]**2 + p[1]**2) + return points[:k] +``` + +```java +public class Solution { + public int[][] kClosest(int[][] points, int k) { + Arrays.sort(points, (a, b) -> (a[0] * a[0] + a[1] * a[1]) - + (b[0] * b[0] + b[1] * b[1])); + return Arrays.copyOfRange(points, 0, k); + } +} +``` + +```cpp +class Solution { +public: + vector> kClosest(vector>& points, int k) { + sort(points.begin(), points.end(), [](const auto& a, const auto& b) { + return (a[0] * a[0] + a[1] * a[1]) < (b[0] * b[0] + b[1] * b[1]); + }); + return vector>(points.begin(), points.begin() + k); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} points + * @param {number} k + * @return {number[][]} + */ + kClosest(points, k) { + points.sort((a, b) => (a[0] ** 2 + a[1] ** 2) - + (b[0] ** 2 + b[1] ** 2)); + return points.slice(0, k); + } +} +``` + +```csharp +public class Solution { + public int[][] KClosest(int[][] points, int k) { + Array.Sort(points, (a, b) => + (a[0] * a[0] + a[1] * a[1]).CompareTo(b[0] * b[0] + b[1] * b[1])); + return points[..k]; + } +} +``` + +```go +func kClosest(points [][]int, k int) [][]int { + sort.Slice(points, func(i, j int) bool { + return points[i][0]*points[i][0] + points[i][1]*points[i][1] < + points[j][0]*points[j][0] + points[j][1]*points[j][1] + }) + return points[:k] +} +``` + +```kotlin +class Solution { + fun kClosest(points: Array, k: Int): Array { + points.sortBy { it[0] * it[0] + it[1] * it[1] } + return points.take(k).toTypedArray() + } +} +``` + +```swift +class Solution { + func kClosest(_ points: [[Int]], _ k: Int) -> [[Int]] { + return points.sorted { ($0[0] * $0[0] + $0[1] * $0[1]) < ($1[0] * $1[0] + $1[1] * $1[1]) } + .prefix(k) + .map { $0 } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Min-Heap + +::tabs-start + +```python +class Solution: + def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]: + minHeap = [] + for x, y in points: + dist = (x ** 2) + (y ** 2) + minHeap.append([dist, x, y]) + + heapq.heapify(minHeap) + res = [] + while k > 0: + dist, x, y = heapq.heappop(minHeap) + res.append([x, y]) + k -= 1 + + return res +``` + +```java +public class Solution { + public int[][] kClosest(int[][] points, int K) { + PriorityQueue minHeap = new PriorityQueue<>(Comparator.comparing(a -> a[0])); + for (int[] point : points) { + int dist = point[0] * point[0] + point[1] * point[1]; + minHeap.offer(new int[]{dist, point[0], point[1]}); + } + + int[][] result = new int[K][2]; + for (int i = 0; i < K; ++i) { + int[] point = minHeap.poll(); + result[i] = new int[]{point[1], point[2]}; + } + return result; + } +} +``` + +```cpp +class Solution { +public: + vector> kClosest(vector>& points, int K) { + auto comp = [](const vector& a, const vector& b) { + return a[0]*a[0] + a[1]*a[1] > b[0]*b[0] + b[1]*b[1]; + }; + + priority_queue, vector>, decltype(comp)> minHeap(comp); + + for (const auto& point : points) { + minHeap.push({point[0], point[1]}); + } + + vector> result; + for (int i = 0; i < K; ++i) { + result.push_back(minHeap.top()); + minHeap.pop(); + } + return result; + } +}; +``` + +```javascript +/** + * const { MinPriorityQueue } = require('@datastructures-js/priority-queue'); + */ + +class Solution { + /** + * @param {number[][]} points + * @param {number} k + * @return {number[][]} + */ + kClosest(points, k) { + const minHeap = new MinPriorityQueue(point => point[0]); + + for (const [x, y] of points) { + const dist = x ** 2 + y ** 2; + minHeap.enqueue([dist, x, y]); + } + + const res = []; + for (let i = 0; i < k; i++) { + const [_, x, y] = minHeap.dequeue(); + res.push([x, y]); + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int[][] KClosest(int[][] points, int K) { + PriorityQueue minHeap = new PriorityQueue(); + foreach (int[] point in points) { + int dist = point[0] * point[0] + point[1] * point[1]; + minHeap.Enqueue(new int[] { dist, point[0], point[1] }, dist); + } + + int[][] result = new int[K][]; + for (int i = 0; i < K; ++i) { + int[] point = minHeap.Dequeue(); + result[i] = new int[] { point[1], point[2] }; + } + return result; + } +} +``` + +```go +func kClosest(points [][]int, k int) [][]int { + minHeap := priorityqueue.NewWith(func(a, b interface{}) int { + distA := a.([]int)[0] + distB := b.([]int)[0] + return distA - distB + }) + + for _, point := range points { + x, y := point[0], point[1] + dist := x*x + y*y + minHeap.Enqueue([]int{dist, x, y}) + } + + res := [][]int{} + for i := 0; i < k; i++ { + item, _ := minHeap.Dequeue() + point := item.([]int) + res = append(res, []int{point[1], point[2]}) + } + + return res +} +``` + +```kotlin +class Solution { + fun kClosest(points: Array, k: Int): List { + val minHeap = PriorityQueue(compareBy { it[0] * it[0] + it[1] * it[1] }) + + for (point in points) { + minHeap.add(point) + } + + val res = mutableListOf() + repeat(k) { + res.add(minHeap.poll()) + } + + return res + } +} +``` + +```swift +struct Item: Comparable { + let dist: Int + let x: Int + let y: Int + + static func < (lhs: Item, rhs: Item) -> Bool { + return lhs.dist < rhs.dist + } +} + +class Solution { + func kClosest(_ points: [[Int]], _ k: Int) -> [[Int]] { + var minHeap = Heap() + + for point in points { + let x = point[0], y = point[1] + let dist = x * x + y * y + minHeap.insert(Item(dist: dist, x: x, y: y)) + } + + var res = [[Int]]() + for _ in 0.. Where $n$ is the length of the array $points$. + +--- + +## 3. Max Heap + +::tabs-start + +```python +class Solution: + def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]: + maxHeap = [] + for x, y in points: + dist = -(x ** 2 + y ** 2) + heapq.heappush(maxHeap, [dist, x, y]) + if len(maxHeap) > k: + heapq.heappop(maxHeap) + + res = [] + while maxHeap: + dist, x, y = heapq.heappop(maxHeap) + res.append([x, y]) + return res +``` + +```java +public class Solution { + public int[][] kClosest(int[][] points, int k) { + PriorityQueue maxHeap = new PriorityQueue<>( + (a, b) -> Integer.compare(b[0] * b[0] + b[1] * b[1], + a[0] * a[0] + a[1] * a[1]) + ); + + for (int[] point : points) { + maxHeap.offer(point); + if (maxHeap.size() > k) { + maxHeap.poll(); + } + } + + int[][] res = new int[k][2]; + int i = 0; + while (!maxHeap.isEmpty()) { + res[i++] = maxHeap.poll(); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> kClosest(vector>& points, int k) { + priority_queue>> maxHeap; + for (auto& point : points) { + int dist = point[0] * point[0] + point[1] * point[1]; + maxHeap.push({dist, {point[0], point[1]}}); + if (maxHeap.size() > k) { + maxHeap.pop(); + } + } + + vector> res; + while (!maxHeap.empty()) { + res.push_back({maxHeap.top().second.first, + maxHeap.top().second.second}); + maxHeap.pop(); + } + return res; + } +}; +``` + +```javascript +/** + * const { PriorityQueue } = require('@datastructures-js/priority-queue'); + */ + +class Solution { + /** + * @param {number[][]} points + * @param {number} k + * @return {number[][]} + */ + kClosest(points, k) { + const maxHeap = new PriorityQueue((a, b) => b[0] - a[0]); + + for (const [x, y] of points) { + const dist = x ** 2 + y ** 2; + maxHeap.push([dist, x, y]); + if (maxHeap.size() > k) { + maxHeap.pop(); + } + } + + const res = []; + while (maxHeap.size() > 0) { + let tmp = maxHeap.pop(); + res.push([tmp[1], tmp[2]]); + } + return res; + } +} +``` + +```csharp +public class Solution { + public int[][] KClosest(int[][] points, int K) { + PriorityQueue maxHeap = new(); + + foreach (var point in points) { + int dist = point[0] * point[0] + point[1] * point[1]; + maxHeap.Enqueue(point, -dist); + if (maxHeap.Count > K) { + maxHeap.Dequeue(); + } + } + + var res = new List(); + while (maxHeap.Count > 0) { + res.Add(maxHeap.Dequeue()); + } + + return res.ToArray(); + } +} +``` + +```go +func kClosest(points [][]int, k int) [][]int { + maxHeap := priorityqueue.NewWith(func(a, b interface{}) int { + distA := a.([]int)[2] + distB := b.([]int)[2] + if distA > distB { + return -1 + } else if distA < distB { + return 1 + } + return 0 + }) + + for _, point := range points { + x, y := point[0], point[1] + dist := x*x + y*y + maxHeap.Enqueue([]int{x, y, dist}) + if maxHeap.Size() > k { + maxHeap.Dequeue() + } + } + + result := make([][]int, k) + for i := k - 1; i >= 0; i-- { + val, _ := maxHeap.Dequeue() + point := val.([]int) + result[i] = []int{point[0], point[1]} + } + + return result +} +``` + +```kotlin +class Solution { + fun kClosest(points: Array, k: Int): Array { + val maxHeap = PriorityQueue { a, b -> + (b[0] * b[0] + b[1] * b[1]) - (a[0] * a[0] + a[1] * a[1]) + } + + for (point in points) { + maxHeap.offer(point) + if (maxHeap.size > k) { + maxHeap.poll() + } + } + + return Array(k) { maxHeap.poll() } + } +} +``` + +```swift +struct Item: Comparable { + let dist: Int + let x: Int + let y: Int + + static func < (lhs: Item, rhs: Item) -> Bool { + return lhs.dist > rhs.dist + } +} + +class Solution { + func kClosest(_ points: [[Int]], _ k: Int) -> [[Int]] { + var maxHeap = Heap() + + for point in points { + let x = point[0], y = point[1] + let dist = x * x + y * y + maxHeap.insert(Item(dist: dist, x: x, y: y)) + if maxHeap.count > k { + _ = maxHeap.popMin() + } + } + + var res = [[Int]]() + while !maxHeap.isEmpty { + if let item = maxHeap.popMin() { + res.append([item.x, item.y]) + } + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * \log k)$ +* Space complexity: $O(k)$ + +> Where $n$ is the length of the array $points$. + +--- + +## 4. Quick Select + +::tabs-start + +```python +class Solution: + def kClosest(self, points, k): + euclidean = lambda x: x[0] ** 2 + x[1] ** 2 + def partition(l, r): + pivotIdx = r + pivotDist = euclidean(points[pivotIdx]) + i = l + for j in range(l, r): + if euclidean(points[j]) <= pivotDist: + points[i], points[j] = points[j], points[i] + i += 1 + points[i], points[r] = points[r], points[i] + return i + + L, R = 0, len(points) - 1 + pivot = len(points) + + while pivot != k: + pivot = partition(L, R) + if pivot < k: + L = pivot + 1 + else: + R = pivot - 1 + return points[:k] +``` + +```java +public class Solution { + public int[][] kClosest(int[][] points, int k) { + int L = 0, R = points.length - 1; + int pivot = points.length; + + while (pivot != k) { + pivot = partition(points, L, R); + if (pivot < k) { + L = pivot + 1; + } else { + R = pivot - 1; + } + } + int[][] res = new int[k][2]; + System.arraycopy(points, 0, res, 0, k); + return res; + } + + private int partition(int[][] points, int l, int r) { + int pivotIdx = r; + int pivotDist = euclidean(points[pivotIdx]); + int i = l; + for (int j = l; j < r; j++) { + if (euclidean(points[j]) <= pivotDist) { + int[] temp = points[i]; + points[i] = points[j]; + points[j] = temp; + i++; + } + } + int[] temp = points[i]; + points[i] = points[r]; + points[r] = temp; + return i; + } + + private int euclidean(int[] point) { + return point[0] * point[0] + point[1] * point[1]; + } +} +``` + +```cpp +class Solution { +public: + vector> kClosest(vector>& points, int k) { + int L = 0, R = points.size() - 1; + int pivot = points.size(); + + while (pivot != k) { + pivot = partition(points, L, R); + if (pivot < k) { + L = pivot + 1; + } else { + R = pivot - 1; + } + } + return vector>(points.begin(), points.begin() + k); + } + +private: + int partition(vector>& points, int l, int r) { + int pivotIdx = r; + int pivotDist = euclidean(points[pivotIdx]); + int i = l; + for (int j = l; j < r; j++) { + if (euclidean(points[j]) <= pivotDist) { + swap(points[i], points[j]); + i++; + } + } + swap(points[i], points[r]); + return i; + } + + int euclidean(vector& point) { + return point[0] * point[0] + point[1] * point[1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} points + * @param {number} k + * @return {number[][]} + */ + kClosest(points, k) { + let L = 0, R = points.length - 1, pivot = points.length; + + while (pivot !== k) { + pivot = this.partition(points, L, R); + if (pivot < k) { + L = pivot + 1; + } else { + R = pivot - 1; + } + } + return points.slice(0, k); + } + + /** + * @param {number[][]} points + * @param {number} l + * @param {number} r + * @return {number} + */ + partition(points, l, r) { + const pivotIdx = r; + const pivotDist = this.euclidean(points[pivotIdx]); + let i = l; + for (let j = l; j < r; j++) { + if (this.euclidean(points[j]) <= pivotDist) { + [points[i], points[j]] = [points[j], points[i]]; + i++; + } + } + [points[i], points[r]] = [points[r], points[i]]; + return i; + } + + /** + * @param {number[]} point + * @return {number} + */ + euclidean(point) { + return point[0] ** 2 + point[1] ** 2; + } +} +``` + +```csharp +class Solution { + public int[][] KClosest(int[][] points, int k) { + int L = 0, R = points.Length - 1; + int pivot = points.Length; + + while (pivot != k) { + pivot = Partition(points, L, R); + if (pivot < k) { + L = pivot + 1; + } else { + R = pivot - 1; + } + } + int[][] res = new int[k][]; + Array.Copy(points, res, k); + return res; + } + + private int Partition(int[][] points, int l, int r) { + int pivotIdx = r; + int pivotDist = Euclidean(points[pivotIdx]); + int i = l; + for (int j = l; j < r; j++) { + if (Euclidean(points[j]) <= pivotDist) { + Swap(points, i, j); + i++; + } + } + Swap(points, i, r); + return i; + } + + private int Euclidean(int[] point) { + return point[0] * point[0] + point[1] * point[1]; + } + + private void Swap(int[][] points, int i, int j) { + int[] temp = points[i]; + points[i] = points[j]; + points[j] = temp; + } +} +``` + +```go +func kClosest(points [][]int, k int) [][]int { + euclidean := func(x []int) int { + return x[0]*x[0] + x[1]*x[1] + } + + partition := func(points [][]int, l, r int) int { + pivotIdx := r + pivotDist := euclidean(points[pivotIdx]) + i := l + for j := l; j < r; j++ { + if euclidean(points[j]) <= pivotDist { + points[i], points[j] = points[j], points[i] + i++ + } + } + points[i], points[r] = points[r], points[i] + return i + } + + L, R := 0, len(points)-1 + pivot := len(points) + + for pivot != k { + pivot = partition(points, L, R) + if pivot < k { + L = pivot + 1 + } else { + R = pivot - 1 + } + } + return points[:k] +} +``` + +```kotlin +class Solution { + fun kClosest(points: Array, k: Int): Array { + val euclidean = { x: IntArray -> x[0] * x[0] + x[1] * x[1] } + + fun partition(points: Array, l: Int, r: Int): Int { + val pivotIdx = r + val pivotDist = euclidean(points[pivotIdx]) + var i = l + for (j in l until r) { + if (euclidean(points[j]) <= pivotDist) { + points[i] = points[j].also { points[j] = points[i] } + i++ + } + } + points[i] = points[r].also { points[r] = points[i] } + return i + } + + var L = 0 + var R = points.size - 1 + var pivot = points.size + + while (pivot != k) { + pivot = partition(points, L, R) + if (pivot < k) { + L = pivot + 1 + } else { + R = pivot - 1 + } + } + return points.copyOfRange(0, k) + } +} +``` + +```swift +class Solution { + func kClosest(_ points: [[Int]], _ k: Int) -> [[Int]] { + var points = points + + func euclidean(_ point: [Int]) -> Int { + return point[0] * point[0] + point[1] * point[1] + } + + func partition(_ l: Int, _ r: Int) -> Int { + let pivotIdx = r + let pivotDist = euclidean(points[pivotIdx]) + var i = l + for j in l.. int: + MOD = 10**9 + 7 + cache = {} + + def count(n, k): + if n == 0: + return 1 if k == 0 else 0 + if k < 0: + return 0 + if (n, k) in cache: + return cache[(n, k)] + + cache[(n, k)] = 0 + for i in range(n): + cache[(n, k)] = (cache[(n, k)] + count(n - 1, k - i)) % MOD + + return cache[(n, k)] + + return count(n, k) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + private int[][] dp; + + public int kInversePairs(int n, int k) { + dp = new int[n + 1][k + 1]; + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= k; j++) { + dp[i][j] = -1; + } + } + return count(n, k); + } + + private int count(int n, int k) { + if (n == 0) return k == 0 ? 1 : 0; + if (k < 0) return 0; + if (dp[n][k] != -1) return dp[n][k]; + + int res = 0; + for (int i = 0; i < n; i++) { + res = (res + count(n - 1, k - i)) % MOD; + } + + dp[n][k] = res; + return res; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + vector> dp; + + int count(int n, int k) { + if (n == 0) { + return k == 0 ? 1 : 0; + } + if (k < 0) { + return 0; + } + if (dp[n][k] != -1) { + return dp[n][k]; + } + + int res = 0; + for (int i = 0; i < n; ++i) { + res = (res + count(n - 1, k - i)) % MOD; + } + dp[n][k] = res; + return res; + } + +public: + int kInversePairs(int n, int k) { + dp.assign(n + 1, vector(k + 1, -1)); + return count(n, k); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + kInversePairs(n, k) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: n + 1 }, () => Array(k + 1).fill(-1)); + + const count = (n, k) => { + if (n === 0) return k === 0 ? 1 : 0; + if (k < 0) return 0; + if (dp[n][k] !== -1) return dp[n][k]; + + let res = 0; + for (let i = 0; i < n; i++) { + res = (res + count(n - 1, k - i)) % MOD; + } + + dp[n][k] = res; + return res; + }; + + return count(n, k); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ is the size of the permutation and $k$ is the number of inverse pairs in the permutation. + +--- + +## 2. Dynamic Programming (Top-Down Optimized) + +::tabs-start + +```python +class Solution: + def kInversePairs(self, n: int, k: int) -> int: + MOD = 10**9 + 7 + dp = [[-1] * (k + 1) for _ in range(n + 1)] + + def count(n, k): + if k == 0: + return 1 + if n == 1: + return 0 + if n * (n - 1) // 2 < k: + return 0 + if n * (n - 1) // 2 == k: + return 1 + if dp[n][k] != -1: + return dp[n][k] + + res = count(n, k - 1) + if k >= n: + res -= count(n - 1, k - n) + res = (res + count(n - 1, k)) % MOD + + dp[n][k] = res + return res + + return count(n, k) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + private int[][] dp; + + public int kInversePairs(int n, int k) { + dp = new int[n + 1][k + 1]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + return count(n, k); + } + + private int count(int n, int k) { + if (k == 0) return 1; + if (n == 1) return 0; + if (n * (n - 1) / 2 < k) return 0; + if (n * (n - 1) / 2 == k) return 1; + if (dp[n][k] != -1) return dp[n][k]; + + long res = count(n, k - 1); + if (k >= n) { + res -= count(n - 1, k - n); + } + res = (res + count(n - 1, k)) % MOD; + + dp[n][k] = (int) (res + MOD) % MOD; + return dp[n][k]; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + vector> dp; + + int count(int n, int k) { + if (k == 0) return 1; + if (n == 1) return 0; + if (n * (n - 1) / 2 < k) return 0; + if (n * (n - 1) / 2 == k) return 1; + if (dp[n][k] != -1) return dp[n][k]; + + long long res = count(n, k - 1); + if (k >= n) { + res -= count(n - 1, k - n); + } + res = (res + count(n - 1, k) + MOD) % MOD; + + dp[n][k] = int(res); + return dp[n][k]; + } + +public: + int kInversePairs(int n, int k) { + dp.assign(n + 1, vector(k + 1, -1)); + return count(n, k); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + kInversePairs(n, k) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: n + 1 }, () => Array(k + 1).fill(-1)); + + const count = (n, k) => { + if (k === 0) return 1; + if (n === 1) return 0; + if ((n * (n - 1)) / 2 < k) return 0; + if ((n * (n - 1)) / 2 === k) return 1; + if (dp[n][k] !== -1) return dp[n][k]; + + let res = count(n, k - 1); + if (k >= n) { + res -= count(n - 1, k - n); + } + res = (res + count(n - 1, k) + MOD) % MOD; + + dp[n][k] = res; + return res; + }; + + return count(n, k); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ is the size of the permutation and $k$ is the number of inverse pairs in the permutation. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def kInversePairs(self, n: int, k: int) -> int: + MOD = 10**9 + 7 + dp = [[0] * (k + 1) for _ in range(n + 1)] + dp[0][0] = 1 + + for N in range(1, n + 1): + for K in range(k + 1): + for pairs in range(N): + if K - pairs >= 0: + dp[N][K] = (dp[N][K] + dp[N - 1][K - pairs]) % MOD + + return dp[n][k] +``` + +```java +public class Solution { + public int kInversePairs(int n, int k) { + final int MOD = 1000000007; + int[][] dp = new int[n + 1][k + 1]; + dp[0][0] = 1; + + for (int N = 1; N <= n; N++) { + for (int K = 0; K <= k; K++) { + for (int pairs = 0; pairs < N; pairs++) { + if (K - pairs >= 0) { + dp[N][K] = (dp[N][K] + dp[N - 1][K - pairs]) % MOD; + } + } + } + } + + return dp[n][k]; + } +} +``` + +```cpp +class Solution { +public: + int kInversePairs(int n, int k) { + const int MOD = 1e9 + 7; + vector> dp(n + 1, vector(k + 1, 0)); + dp[0][0] = 1; + + for (int N = 1; N <= n; N++) { + for (int K = 0; K <= k; K++) { + for (int pairs = 0; pairs < N; pairs++) { + if (K - pairs >= 0) { + dp[N][K] = (dp[N][K] + dp[N - 1][K - pairs]) % MOD; + } + } + } + } + + return dp[n][k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + kInversePairs(n, k) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: n + 1 }, () => Array(k + 1).fill(0)); + dp[0][0] = 1; + + for (let N = 1; N <= n; N++) { + for (let K = 0; K <= k; K++) { + for (let pairs = 0; pairs < N; pairs++) { + if (K - pairs >= 0) { + dp[N][K] = (dp[N][K] + dp[N - 1][K - pairs]) % MOD; + } + } + } + } + + return dp[n][k]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ is the size of the permutation and $k$ is the number of inverse pairs in the permutation. + +--- + +## 4. Dynamic Programming (Bottom-Up Optimized) + +::tabs-start + +```python +class Solution: + def kInversePairs(self, n: int, k: int) -> int: + MOD = 10**9 + 7 + dp = [[0] * (k + 1) for _ in range(n + 1)] + dp[0][0] = 1 + + for N in range(1, n + 1): + for K in range(k + 1): + dp[N][K] = dp[N - 1][K] + if K > 0: + dp[N][K] = (dp[N][K] + dp[N][K - 1]) % MOD + if K >= N: + dp[N][K] = (dp[N][K] - dp[N - 1][K - N] + MOD) % MOD + + return dp[n][k] +``` + +```java +public class Solution { + public int kInversePairs(int n, int k) { + final int MOD = 1000000007; + int[][] dp = new int[n + 1][k + 1]; + dp[0][0] = 1; + + for (int N = 1; N <= n; N++) { + for (int K = 0; K <= k; K++) { + dp[N][K] = dp[N - 1][K]; + if (K > 0) { + dp[N][K] = (dp[N][K] + dp[N][K - 1]) % MOD; + } + if (K >= N) { + dp[N][K] = (dp[N][K] - dp[N - 1][K - N] + MOD) % MOD; + } + } + } + + return dp[n][k]; + } +} +``` + +```cpp +class Solution { +public: + int kInversePairs(int n, int k) { + const int MOD = 1e9 + 7; + vector> dp(n + 1, vector(k + 1, 0)); + dp[0][0] = 1; + + for (int N = 1; N <= n; N++) { + for (int K = 0; K <= k; K++) { + dp[N][K] = dp[N - 1][K]; + if (K > 0) { + dp[N][K] = (dp[N][K] + dp[N][K - 1]) % MOD; + } + if (K >= N) { + dp[N][K] = (dp[N][K] - dp[N - 1][K - N] + MOD) % MOD; + } + } + } + + return dp[n][k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + kInversePairs(n, k) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: n + 1 }, () => Array(k + 1).fill(0)); + dp[0][0] = 1; + + for (let N = 1; N <= n; N++) { + for (let K = 0; K <= k; K++) { + dp[N][K] = dp[N - 1][K]; + if (K > 0) { + dp[N][K] = (dp[N][K] + dp[N][K - 1]) % MOD; + } + if (K >= N) { + dp[N][K] = (dp[N][K] - dp[N - 1][K - N] + MOD) % MOD; + } + } + } + + return dp[n][k]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ is the size of the permutation and $k$ is the number of inverse pairs in the permutation. + +--- + +## 5. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def kInversePairs(self, n: int, k: int) -> int: + MOD = 10**9 + 7 + prev = [0] * (k + 1) + prev[0] = 1 + + for N in range(1, n + 1): + cur = [0] * (k + 1) + total = 0 + for K in range(0, k + 1): + total = (total + prev[K]) % MOD + if K >= N: + total = (total - prev[K - N] + MOD) % MOD + cur[K] = total + prev = cur + + return prev[k] +``` + +```java +public class Solution { + public int kInversePairs(int n, int k) { + final int MOD = 1000000007; + int[] prev = new int[k + 1]; + prev[0] = 1; + + for (int N = 1; N <= n; N++) { + int[] cur = new int[k + 1]; + int total = 0; + for (int K = 0; K <= k; K++) { + total = (total + prev[K]) % MOD; + if (K >= N) { + total = (total - prev[K - N] + MOD) % MOD; + } + cur[K] = total; + } + prev = cur; + } + + return prev[k]; + } +} +``` + +```cpp +class Solution { +public: + int kInversePairs(int n, int k) { + const int MOD = 1e9 + 7; + vector prev(k + 1, 0); + prev[0] = 1; + + for (int N = 1; N <= n; N++) { + vector cur(k + 1, 0); + int total = 0; + for (int K = 0; K <= k; K++) { + total = (total + prev[K]) % MOD; + if (K >= N) { + total = (total - prev[K - N] + MOD) % MOD; + } + cur[K] = total; + } + prev = cur; + } + + return prev[k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + kInversePairs(n, k) { + const MOD = 1e9 + 7; + let prev = new Array(k + 1).fill(0); + prev[0] = 1; + + for (let N = 1; N <= n; N++) { + const cur = new Array(k + 1).fill(0); + let total = 0; + for (let K = 0; K <= k; K++) { + total = (total + prev[K]) % MOD; + if (K >= N) { + total = (total - prev[K - N] + MOD) % MOD; + } + cur[K] = total; + } + prev = cur; + } + + return prev[k]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(k)$ + +> Where $n$ is the size of the permutation and $k$ is the number of inverse pairs in the permutation. \ No newline at end of file diff --git a/articles/k-th-symbol-in-grammar.md b/articles/k-th-symbol-in-grammar.md new file mode 100644 index 000000000..d3f782b4c --- /dev/null +++ b/articles/k-th-symbol-in-grammar.md @@ -0,0 +1,413 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def kthGrammar(self, n: int, k: int) -> int: + prev = ['0'] + for i in range(2, n + 1): + cur = [] + for c in prev: + if c == '0': + cur.append('0') + cur.append('1') + else: + cur.append('1') + cur.append('0') + prev = cur + return int(prev[k - 1]) +``` + +```java +public class Solution { + public int kthGrammar(int n, int k) { + List prev = new ArrayList<>(); + prev.add('0'); + for (int i = 2; i <= n; i++) { + List cur = new ArrayList<>(); + for (char c : prev) { + if (c == '0') { + cur.add('0'); + cur.add('1'); + } else { + cur.add('1'); + cur.add('0'); + } + } + prev = cur; + } + return prev.get(k - 1) - '0'; + } +} +``` + +```cpp +class Solution { +public: + int kthGrammar(int n, int k) { + vector prev = {'0'}; + for (int i = 2; i <= n; i++) { + vector cur; + for (char c : prev) { + if (c == '0') { + cur.push_back('0'); + cur.push_back('1'); + } else { + cur.push_back('1'); + cur.push_back('0'); + } + } + prev = cur; + } + return prev[k - 1] - '0'; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + kthGrammar(n, k) { + let prev = ['0']; + for (let i = 2; i <= n; i++) { + let cur = []; + for (let c of prev) { + if (c === '0') { + cur.push('0'); + cur.push('1'); + } else { + cur.push('1'); + cur.push('0'); + } + } + prev = cur; + } + return parseInt(prev[k - 1]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(2 ^ n)$ + +--- + +## 2. Binary Tree Traversal (Recursion) + +::tabs-start + +```python +class Solution: + def kthGrammar(self, n: int, k: int) -> int: + def dfs(n, k, root): + if n == 1: + return root + + total = 1 << (n - 1) + if k > (total // 2): + return dfs(n - 1, k - (total // 2), root ^ 1) + else: + return dfs(n - 1, k, root) + + return dfs(n, k, 0) +``` + +```java +public class Solution { + public int kthGrammar(int n, int k) { + return dfs(n, k, 0); + } + + private int dfs(int n, int k, int root) { + if (n == 1) { + return root; + } + + int total = 1 << (n - 1); + if (k > total / 2) { + return dfs(n - 1, k - total / 2, root ^ 1); + } else { + return dfs(n - 1, k, root); + } + } +} +``` + +```cpp +class Solution { +public: + int kthGrammar(int n, int k) { + return dfs(n, k, 0); + } + + int dfs(int n, int k, int root){ + if (n == 1) return root; + + int total = 1 << (n - 1); + if (k > total / 2) { + return dfs(n - 1, k - total / 2, root ^ 1); + } else { + return dfs(n - 1, k, root); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + kthGrammar(n, k) { + const dfs = (n, k, root) => { + if (n === 1) return root; + + const total = 1 << (n - 1); + if (k > total / 2) { + return dfs(n - 1, k - total / 2, root ^ 1); + } else { + return dfs(n - 1, k, root); + } + }; + + return dfs(n, k, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Binary Tree Traversal (Iteration) + +::tabs-start + +```python +class Solution: + def kthGrammar(self, n: int, k: int) -> int: + cur = 0 + left, right = 1, 2 ** (n - 1) + + for _ in range(n - 1): + mid = (left + right) // 2 + if k <= mid: + right = mid + else: + left = mid + 1 + cur = 0 if cur else 1 + + return cur +``` + +```java +public class Solution { + public int kthGrammar(int n, int k) { + int cur = 0; + int left = 1, right = 1 << (n - 1); + + for (int i = 0; i < n - 1; i++) { + int mid = (left + right) / 2; + if (k <= mid) { + right = mid; + } else { + left = mid + 1; + cur = (cur == 0) ? 1 : 0; + } + } + + return cur; + } +} +``` + +```cpp +class Solution { +public: + int kthGrammar(int n, int k) { + int cur = 0; + int left = 1, right = 1 << (n - 1); + + for (int i = 0; i < n - 1; i++) { + int mid = (left + right) / 2; + if (k <= mid) { + right = mid; + } else { + left = mid + 1; + cur = (cur == 0) ? 1 : 0; + } + } + + return cur; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + kthGrammar(n, k) { + let cur = 0; + let left = 1, right = 1 << (n - 1); + + for (let i = 0; i < n - 1; i++) { + let mid = Math.floor((left + right) / 2); + if (k <= mid) { + right = mid; + } else { + left = mid + 1; + cur = cur === 0 ? 1 : 0; + } + } + + return cur; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Recursion (Traverse Towards Root) + +::tabs-start + +```python +class Solution: + def kthGrammar(self, n: int, k: int) -> int: + if n == 1: + return 0 + if k & 1: + return self.kthGrammar(n - 1, (k + 1) // 2) + return self.kthGrammar(n - 1, k // 2) ^ 1 +``` + +```java +public class Solution { + public int kthGrammar(int n, int k) { + if (n == 1) { + return 0; + } + if ((k & 1) == 1) { + return kthGrammar(n - 1, (k + 1) / 2); + } + return kthGrammar(n - 1, k / 2) ^ 1; + } +} +``` + +```cpp +class Solution { +public: + int kthGrammar(int n, int k) { + if (n == 1) { + return 0; + } + if (k & 1) { + return kthGrammar(n - 1, (k + 1) / 2); + } + return kthGrammar(n - 1, k / 2) ^ 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + kthGrammar(n, k) { + if (n === 1) { + return 0; + } + if (k % 2 === 1) { + return this.kthGrammar(n - 1, Math.floor((k + 1) / 2)); + } + return this.kthGrammar(n - 1, Math.floor(k / 2)) ^ 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 5. Math + +::tabs-start + +```python +class Solution: + def kthGrammar(self, n: int, k: int) -> int: + return bin(k - 1).count('1') & 1 +``` + +```java +public class Solution { + public int kthGrammar(int n, int k) { + return Integer.bitCount(k - 1) & 1; + } +} +``` + +```cpp +class Solution { +public: + int kthGrammar(int n, int k) { + return __builtin_popcount(k - 1) & 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + kthGrammar(n, k) { + return (k - 1).toString(2).split('1').length - 1 & 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ or $O(\log n)$ depending on the language. \ No newline at end of file diff --git a/articles/knight-dialer.md b/articles/knight-dialer.md new file mode 100644 index 000000000..9c87f4c13 --- /dev/null +++ b/articles/knight-dialer.md @@ -0,0 +1,946 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def knightDialer(self, n: int) -> int: + if n == 1: + return 10 + + mod = 1000000007 + jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ] + + def dfs(n, d): + if n == 0: + return 1 + + res = 0 + for j in jumps[d]: + res = (res + dfs(n - 1, j)) % mod + return res + + res = 0 + for d in range(10): + res = (res + dfs(n - 1, d)) % mod + return res +``` + +```java +public class Solution { + private static final int MOD = 1000000007; + private static final int[][] jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + + public int knightDialer(int n) { + if (n == 1) return 10; + int res = 0; + + for (int d = 0; d < 10; d++) { + res = (res + dfs(n - 1, d)) % MOD; + } + return res; + } + + private int dfs(int n, int d) { + if (n == 0) return 1; + + int res = 0; + for (int next : jumps[d]) { + res = (res + dfs(n - 1, next)) % MOD; + } + return res; + } +} +``` + +```cpp +class Solution { +private: + static constexpr int MOD = 1000000007; + const vector> jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + +public: + int knightDialer(int n) { + if (n == 1) return 10; + int res = 0; + + for (int d = 0; d < 10; d++) { + res = (res + dfs(n - 1, d)) % MOD; + } + return res; + } + +private: + int dfs(int n, int d) { + if (n == 0) return 1; + + int res = 0; + for (int next : jumps[d]) { + res = (res + dfs(n - 1, next)) % MOD; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + knightDialer(n) { + if (n === 1) return 10; + const MOD = 1000000007; + const jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ]; + + const dfs = (n, d) => { + if (n === 0) return 1; + + let res = 0; + for (const next of jumps[d]) { + res = (res + dfs(n - 1, next)) % MOD; + } + return res; + }; + + let res = 0; + for (let d = 0; d < 10; d++) { + res = (res + dfs(n - 1, d)) % MOD; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(3 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def knightDialer(self, n: int) -> int: + if n == 1: + return 10 + + mod = 1000000007 + jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ] + + dp = [[-1] * (n + 1) for _ in range(10)] + + def dfs(n, d): + if n == 0: + return 1 + if dp[d][n] != -1: + return dp[d][n] + + dp[d][n] = 0 + for j in jumps[d]: + dp[d][n] = (dp[d][n] + dfs(n - 1, j)) % mod + return dp[d][n] + + res = 0 + for d in range(10): + res = (res + dfs(n - 1, d)) % mod + return res +``` + +```java +public class Solution { + private static final int MOD = 1000000007; + private static final int[][] jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + private int[][] dp; + + public int knightDialer(int n) { + if (n == 1) return 10; + dp = new int[10][n + 1]; + for (int[] row : dp) Arrays.fill(row, -1); + + int res = 0; + for (int d = 0; d < 10; d++) { + res = (res + dfs(n - 1, d)) % MOD; + } + return res; + } + + private int dfs(int n, int d) { + if (n == 0) return 1; + if (dp[d][n] != -1) return dp[d][n]; + + dp[d][n] = 0; + for (int next : jumps[d]) { + dp[d][n] = (dp[d][n] + dfs(n - 1, next)) % MOD; + } + return dp[d][n]; + } +} +``` + +```cpp +class Solution { +private: + static constexpr int MOD = 1000000007; + vector> jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + vector> dp; + +public: + int knightDialer(int n) { + if (n == 1) return 10; + dp.assign(10, vector(n + 1, -1)); + + int res = 0; + for (int d = 0; d < 10; d++) { + res = (res + dfs(n - 1, d)) % MOD; + } + return res; + } + +private: + int dfs(int n, int d) { + if (n == 0) return 1; + if (dp[d][n] != -1) return dp[d][n]; + + dp[d][n] = 0; + for (int next : jumps[d]) { + dp[d][n] = (dp[d][n] + dfs(n - 1, next)) % MOD; + } + return dp[d][n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + knightDialer(n) { + if (n === 1) return 10; + const MOD = 1000000007; + const jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ]; + + const dp = Array.from({ length: 10 }, () => Array(n + 1).fill(-1)); + + const dfs = (n, d) => { + if (n === 0) return 1; + if (dp[d][n] !== -1) return dp[d][n]; + + dp[d][n] = 0; + for (const next of jumps[d]) { + dp[d][n] = (dp[d][n] + dfs(n - 1, next)) % MOD; + } + return dp[d][n]; + }; + + let res = 0; + for (let d = 0; d < 10; d++) { + res = (res + dfs(n - 1, d)) % MOD; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def knightDialer(self, n: int) -> int: + if n == 1: + return 10 + + mod = 1000000007 + jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ] + + dp = [[0] * (n + 1) for _ in range(10)] + + for d in range(10): + dp[d][0] = 1 + + for step in range(1, n): + for d in range(10): + dp[d][step] = sum(dp[j][step - 1] for j in jumps[d]) % mod + + return sum(dp[d][n - 1] for d in range(10)) % mod +``` + +```java +public class Solution { + private static final int MOD = 1000000007; + private static final int[][] jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + + public int knightDialer(int n) { + if (n == 1) return 10; + + int[][] dp = new int[10][n + 1]; + for (int d = 0; d < 10; d++) { + dp[d][0] = 1; + } + + for (int step = 1; step < n; step++) { + for (int d = 0; d < 10; d++) { + for (int j : jumps[d]) { + dp[d][step] = (dp[d][step] + dp[j][step - 1]) % MOD; + } + } + } + + int res = 0; + for (int d = 0; d < 10; d++) { + res = (res + dp[d][n - 1]) % MOD; + } + return res; + } +} +``` + +```cpp +class Solution { +private: + static constexpr int MOD = 1000000007; + vector> jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + +public: + int knightDialer(int n) { + if (n == 1) return 10; + + vector> dp(10, vector(n + 1, 0)); + for (int d = 0; d < 10; d++) { + dp[d][0] = 1; + } + + for (int step = 1; step < n; step++) { + for (int d = 0; d < 10; d++) { + for (int j : jumps[d]) { + dp[d][step] = (dp[d][step] + dp[j][step - 1]) % MOD; + } + } + } + + int res = 0; + for (int d = 0; d < 10; d++) { + res = (res + dp[d][n - 1]) % MOD; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + knightDialer(n) { + if (n === 1) return 10; + const MOD = 1000000007; + const jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ]; + + const dp = Array.from({ length: 10 }, () => Array(n + 1).fill(0)); + for (let d = 0; d < 10; d++) { + dp[d][0] = 1; + } + + for (let step = 1; step < n; step++) { + for (let d = 0; d < 10; d++) { + for (const j of jumps[d]) { + dp[d][step] = (dp[d][step] + dp[j][step - 1]) % MOD; + } + } + } + + let res = 0; + for (let d = 0; d < 10; d++) { + res = (res + dp[d][n - 1]) % MOD; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def knightDialer(self, n: int) -> int: + if n == 1: + return 10 + + mod = 1000000007 + jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ] + + dp = [1] * 10 + for step in range(n - 1): + next_dp = [0] * 10 + for d in range(10): + for j in jumps[d]: + next_dp[j] = (next_dp[j] + dp[d]) % mod + dp = next_dp + + res = 0 + for d in dp: + res = (res + d) % mod + return res +``` + +```java +public class Solution { + private static final int MOD = 1000000007; + private static final int[][] jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + + public int knightDialer(int n) { + if (n == 1) return 10; + + int[] dp = new int[10]; + Arrays.fill(dp, 1); + + for (int step = 0; step < n - 1; step++) { + int[] nextDp = new int[10]; + for (int d = 0; d < 10; d++) { + for (int j : jumps[d]) { + nextDp[j] = (nextDp[j] + dp[d]) % MOD; + } + } + dp = nextDp; + } + + int res = 0; + for (int d : dp) { + res = (res + d) % MOD; + } + return res; + } +} +``` + +```cpp +class Solution { +private: + static constexpr int MOD = 1000000007; + vector> jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + +public: + int knightDialer(int n) { + if (n == 1) return 10; + + vector dp(10, 1); + + for (int step = 0; step < n - 1; step++) { + vector nextDp(10, 0); + for (int d = 0; d < 10; d++) { + for (int j : jumps[d]) { + nextDp[j] = (nextDp[j] + dp[d]) % MOD; + } + } + dp = nextDp; + } + + int res = 0; + for (int d : dp) { + res = (res + d) % MOD; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + knightDialer(n) { + if (n === 1) return 10; + const MOD = 1000000007; + const jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ]; + + let dp = new Array(10).fill(1); + + for (let step = 0; step < n - 1; step++) { + let nextDp = new Array(10).fill(0); + for (let d = 0; d < 10; d++) { + for (const j of jumps[d]) { + nextDp[j] = (nextDp[j] + dp[d]) % MOD; + } + } + dp = nextDp; + } + + return dp.reduce((res, d) => (res + d) % MOD, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def knightDialer(self, n: int) -> int: + if n == 1: + return 10 + + mod = 10**9 + 7 + jumps = [1, 4, 2, 2] # [D, A, B, C] + + for _ in range(n - 1): + tmp = [0] * 4 + tmp[0] = jumps[3] + tmp[1] = 2 * jumps[2] + 2 * jumps[3] + tmp[2] = jumps[1] + tmp[3] = 2 * jumps[0] + jumps[1] + jumps = tmp + + return sum(jumps) % mod +``` + +```java +public class Solution { + public int knightDialer(int n) { + if (n == 1) return 10; + + int MOD = 1000000007; + long[] jumps = {1, 4, 2, 2}; // [D, A, B, C] + + for (int i = 0; i < n - 1; i++) { + long[] tmp = new long[4]; + tmp[0] = jumps[3]; + tmp[1] = (2 * jumps[2] + 2 * jumps[3]) % MOD; + tmp[2] = jumps[1]; + tmp[3] = (2 * jumps[0] + jumps[1]) % MOD; + jumps = tmp; + } + + long res = 0; + for (long num : jumps) { + res = (res + num) % MOD; + } + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + int knightDialer(int n) { + if (n == 1) return 10; + + const int MOD = 1000000007; + vector jumps = {1, 4, 2, 2}; // [D, A, B, C] + + for (int i = 0; i < n - 1; i++) { + vector tmp(4); + tmp[0] = jumps[3]; + tmp[1] = (2 * jumps[2] + 2 * jumps[3]) % MOD; + tmp[2] = jumps[1]; + tmp[3] = (2 * jumps[0] + jumps[1]) % MOD; + jumps = tmp; + } + + return (jumps[0] + jumps[1] + jumps[2] + jumps[3]) % MOD; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + knightDialer(n) { + if (n === 1) return 10; + + const MOD = 1000000007; + let jumps = [1, 4, 2, 2]; // [D, A, B, C] + + for (let i = 0; i < n - 1; i++) { + let tmp = new Array(4).fill(0); + tmp[0] = jumps[3]; + tmp[1] = (2 * jumps[2] + 2 * jumps[3]) % MOD; + tmp[2] = jumps[1]; + tmp[3] = (2 * jumps[0] + jumps[1]) % MOD; + jumps = tmp; + } + + return jumps.reduce((sum, num) => (sum + num) % MOD, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 6. Matrix Exponentiation + +::tabs-start + +```python +class Matrix: + def __init__(self, size): + self.n = size + self.a = [[0] * size for _ in range(size)] + + def __mul__(self, other): + n = self.n + MOD = 10**9 + 7 + product = Matrix(n) + for i in range(n): + for j in range(n): + for k in range(n): + product.a[i][k] = (product.a[i][k] + self.a[i][j] * other.a[j][k]) % MOD + return product + + +def matpow(mat, n, size): + res = Matrix(size) + for i in range(size): + res.a[i][i] = 1 # Identity matrix + + while n: + if n & 1: + res = res * mat + mat = mat * mat + n >>= 1 + + return res + + +class Solution: + def knightDialer(self, n: int) -> int: + if n == 1: + return 10 + + MOD = 10**9 + 7 + jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ] + + mat = Matrix(10) + for i in range(10): + for j in jumps[i]: + mat.a[i][j] = 1 + + res = matpow(mat, n - 1, 10) + + ans = sum(sum(res.a[i]) for i in range(10)) % MOD + return ans +``` + +```java +class Matrix { + int[][] a; + int size; + static final int MOD = 1_000_000_007; + + public Matrix(int size) { + this.size = size; + a = new int[size][size]; + } + + public Matrix multiply(Matrix other) { + Matrix product = new Matrix(size); + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + for (int k = 0; k < size; k++) { + product.a[i][k] = (int)((product.a[i][k] + (long) a[i][j] * other.a[j][k]) % MOD); + } + } + } + return product; + } +} + +public class Solution { + private Matrix matpow(Matrix mat, int n, int size) { + Matrix res = new Matrix(size); + for (int i = 0; i < size; i++) { + res.a[i][i] = 1; // Identity matrix + } + + while (n > 0) { + if ((n & 1) == 1) { + res = res.multiply(mat); + } + mat = mat.multiply(mat); + n >>= 1; + } + return res; + } + + public int knightDialer(int n) { + if (n == 1) return 10; + + int[][] jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + + Matrix mat = new Matrix(10); + for (int i = 0; i < 10; i++) { + for (int j : jumps[i]) { + mat.a[i][j] = 1; + } + } + + Matrix res = matpow(mat, n - 1, 10); + + int ans = 0; + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + ans = (ans + res.a[i][j]) % Matrix.MOD; + } + } + return ans; + } +} +``` + +```cpp +class Matrix { +public: + vector> a; + int size; + static const int MOD = 1'000'000'007; + + Matrix(int n) : size(n) { + a.assign(n, vector(n, 0)); + } + + Matrix operator*(const Matrix &other) const { + Matrix product(size); + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + for (int k = 0; k < size; k++) { + product.a[i][k] = (product.a[i][k] + 1LL * a[i][j] * other.a[j][k]) % MOD; + } + } + } + return product; + } +}; + +Matrix matpow(Matrix mat, int n, int size) { + Matrix res(size); + for (int i = 0; i < size; i++) { + res.a[i][i] = 1; // Identity matrix + } + + while (n > 0) { + if (n & 1) res = res * mat; + mat = mat * mat; + n >>= 1; + } + return res; +} + +class Solution { +public: + int knightDialer(int n) { + if (n == 1) return 10; + + vector> jumps = { + {4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, + {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4} + }; + + Matrix mat(10); + for (int i = 0; i < 10; i++) { + for (int j : jumps[i]) { + mat.a[i][j] = 1; + } + } + + Matrix res = matpow(mat, n - 1, 10); + + int ans = 0; + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + ans = (ans + res.a[i][j]) % Matrix::MOD; + } + } + return ans; + } +}; +``` + +```javascript +class Matrix { + constructor(size) { + this.size = size; + this.a = Array.from({ length: size }, () => Array(size).fill(0)); + this.MOD = BigInt(1e9 + 7); + } + + multiply(other) { + const product = new Matrix(this.size); + for (let i = 0; i < this.size; i++) { + for (let j = 0; j < this.size; j++) { + let sum = BigInt(0); + for (let k = 0; k < this.size; k++) { + sum = (sum + BigInt(this.a[i][k]) * BigInt(other.a[k][j])) % this.MOD; + } + product.a[i][j] = Number(sum); + } + } + return product; + } +} + +class Solution { + /** + * @param {number} n + * @return {number} + */ + knightDialer(n) { + if (n === 1) return 10; + + const matpow = (mat, exp, size) => { + let res = new Matrix(size); + for (let i = 0; i < size; i++) { + res.a[i][i] = 1; // Identity matrix + } + + while (exp > 0) { + if (exp & 1) { + res = res.multiply(mat); + } + mat = mat.multiply(mat); + exp >>= 1; + } + return res; + }; + + const jumps = [ + [4, 6], [6, 8], [7, 9], [4, 8], [0, 3, 9], + [], [0, 1, 7], [2, 6], [1, 3], [2, 4] + ]; + + const mat = new Matrix(10); + for (let i = 0; i < 10; i++) { + for (let j of jumps[i]) { + mat.a[i][j] = 1; + } + } + + const res = matpow(mat, n - 1, 10); + const mod = 1e9 + 7 + let ans = 0; + + for (let i = 0; i < 10; i++) { + for (let j = 0; j < 10; j++) { + ans = (ans + res.a[i][j]) % mod; + } + } + + return ans; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/kth-distinct-string-in-an-array.md b/articles/kth-distinct-string-in-an-array.md new file mode 100644 index 000000000..244281490 --- /dev/null +++ b/articles/kth-distinct-string-in-an-array.md @@ -0,0 +1,350 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def kthDistinct(self, arr: List[str], k: int) -> str: + for i in range(len(arr)): + flag = True + for j in range(len(arr)): + if i == j: + continue + + if arr[i] == arr[j]: + flag = False + break + + if flag: + k -= 1 + if k == 0: + return arr[i] + return "" +``` + +```java +public class Solution { + public String kthDistinct(String[] arr, int k) { + for (int i = 0; i < arr.length; i++) { + boolean flag = true; + for (int j = 0; j < arr.length; j++) { + if (i == j) continue; + + if (arr[i].equals(arr[j])) { + flag = false; + break; + } + } + + if (flag) { + k--; + if (k == 0) { + return arr[i]; + } + } + } + return ""; + } +} +``` + +```cpp +class Solution { +public: + string kthDistinct(vector& arr, int k) { + for (int i = 0; i < arr.size(); i++) { + bool flag = true; + for (int j = 0; j < arr.size(); j++) { + if (i == j) continue; + + if (arr[i] == arr[j]) { + flag = false; + break; + } + } + + if (flag) { + k--; + if (k == 0) { + return arr[i]; + } + } + } + return ""; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} arr + * @param {number} k + * @return {string} + */ + kthDistinct(arr, k) { + for (let i = 0; i < arr.length; i++) { + let flag = true; + for (let j = 0; j < arr.length; j++) { + if (i === j) continue; + + if (arr[i] === arr[j]) { + flag = false; + break; + } + } + + if (flag) { + k--; + if (k === 0) { + return arr[i]; + } + } + } + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def kthDistinct(self, arr: List[str], k: int) -> str: + count = {} + + for s in arr: + if s not in count: + count[s] = 0 + count[s] += 1 + + for s in arr: + if count[s] == 1: + k -= 1 + if k == 0: + return s + + return "" +``` + +```java +public class Solution { + public String kthDistinct(String[] arr, int k) { + Map count = new HashMap<>(); + + for (String s : arr) { + count.put(s, count.getOrDefault(s, 0) + 1); + } + + for (String s : arr) { + if (count.get(s) == 1) { + k--; + if (k == 0) { + return s; + } + } + } + + return ""; + } +} +``` + +```cpp +class Solution { +public: + string kthDistinct(vector& arr, int k) { + unordered_map count; + + for (const string& s : arr) { + count[s]++; + } + + for (const string& s : arr) { + if (count[s] == 1) { + k--; + if (k == 0) { + return s; + } + } + } + + return ""; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} arr + * @param {number} k + * @return {string} + */ + kthDistinct(arr, k) { + const count = {}; + + for (let s of arr) { + if (!(s in count)) { + count[s] = 0; + } + count[s]++; + } + + for (let s of arr) { + if (count[s] === 1) { + k--; + if (k === 0) { + return s; + } + } + } + + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Hash Set + +::tabs-start + +```python +class Solution: + def kthDistinct(self, arr: List[str], k: int) -> str: + distinct, seen = set(), set() + + for s in arr: + if s in distinct: + distinct.remove(s) + seen.add(s) + elif s not in seen: + distinct.add(s) + + for s in arr: + if s in distinct: + k -= 1 + if k == 0: + return s + + return "" +``` + +```java +public class Solution { + public String kthDistinct(String[] arr, int k) { + Set distinct = new HashSet<>(); + Set seen = new HashSet<>(); + + for (String s : arr) { + if (distinct.contains(s)) { + distinct.remove(s); + seen.add(s); + } else if (!seen.contains(s)) { + distinct.add(s); + } + } + + for (String s : arr) { + if (distinct.contains(s)) { + k--; + if (k == 0) { + return s; + } + } + } + + return ""; + } +} +``` + +```cpp +class Solution { +public: + string kthDistinct(vector& arr, int k) { + unordered_set distinct, seen; + + for (const string& s : arr) { + if (distinct.count(s)) { + distinct.erase(s); + seen.insert(s); + } else if (!seen.count(s)) { + distinct.insert(s); + } + } + + for (const string& s : arr) { + if (distinct.count(s)) { + k--; + if (k == 0) { + return s; + } + } + } + + return ""; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} arr + * @param {number} k + * @return {string} + */ + kthDistinct(arr, k) { + const distinct = new Set(); + const seen = new Set(); + + for (let s of arr) { + if (distinct.has(s)) { + distinct.delete(s); + seen.add(s); + } else if (!seen.has(s)) { + distinct.add(s); + } + } + + for (let s of arr) { + if (distinct.has(s)) { + k--; + if (k === 0) { + return s; + } + } + } + + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/kth-largest-element-in-an-array.md b/articles/kth-largest-element-in-an-array.md new file mode 100644 index 000000000..28fa1c69b --- /dev/null +++ b/articles/kth-largest-element-in-an-array.md @@ -0,0 +1,952 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def findKthLargest(self, nums: List[int], k: int) -> int: + nums.sort() + return nums[len(nums) - k] +``` + +```java +public class Solution { + public int findKthLargest(int[] nums, int k) { + Arrays.sort(nums); + return nums[nums.length - k]; + } +} +``` + +```cpp +class Solution { +public: + int findKthLargest(vector& nums, int k) { + sort(nums.begin(), nums.end()); + return nums[nums.size() - k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + findKthLargest(nums, k) { + nums.sort((a, b) => a - b); + return nums[nums.length - k]; + } +} +``` + +```csharp +public class Solution { + public int FindKthLargest(int[] nums, int k) { + Array.Sort(nums); + return nums[nums.Length - k]; + } +} +``` + +```go +func findKthLargest(nums []int, k int) int { + sort.Ints(nums) + return nums[len(nums) - k] +} +``` + +```kotlin +class Solution { + fun findKthLargest(nums: IntArray, k: Int): Int { + nums.sort() + return nums[nums.size - k] + } +} +``` + +```swift +class Solution { + func findKthLargest(_ nums: [Int], _ k: Int) -> Int { + let sortedNums = nums.sorted() + return sortedNums[sortedNums.count - k] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Min-Heap + +::tabs-start + +```python +class Solution: + def findKthLargest(self, nums, k): + return heapq.nlargest(k, nums)[-1] +``` + +```java +public class Solution { + public int findKthLargest(int[] nums, int k) { + PriorityQueue minHeap = new PriorityQueue<>(); + for (int num : nums) { + minHeap.offer(num); + if (minHeap.size() > k) { + minHeap.poll(); + } + } + return minHeap.peek(); + } +} +``` + +```cpp +class Solution { +public: + int findKthLargest(vector& nums, int k) { + priority_queue, greater> minHeap; + for (int num : nums) { + minHeap.push(num); + if (minHeap.size() > k) { + minHeap.pop(); + } + } + return minHeap.top(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + findKthLargest(nums, k) { + const minHeap = new MinPriorityQueue(); + for (let num of nums) { + minHeap.push(num); + if (minHeap.size() > k) { + minHeap.pop(); + } + } + return minHeap.front(); + } +} +``` + +```csharp +public class Solution { + public int FindKthLargest(int[] nums, int k) { + PriorityQueue minHeap = new PriorityQueue(); + foreach (int num in nums) { + minHeap.Enqueue(num, num); + if (minHeap.Count > k) { + minHeap.Dequeue(); + } + } + return minHeap.Peek(); + } +} +``` + +```go +func findKthLargest(nums []int, k int) int { + minHeap := priorityqueue.NewWith(utils.IntComparator) + + for _, num := range nums { + minHeap.Enqueue(num) + if minHeap.Size() > k { + minHeap.Dequeue() + } + } + + val, _ := minHeap.Peek() + return val.(int) +} +``` + +```kotlin +class Solution { + fun findKthLargest(nums: IntArray, k: Int): Int { + val minHeap = PriorityQueue() + + for (num in nums) { + minHeap.offer(num) + if (minHeap.size > k) { + minHeap.poll() + } + } + return minHeap.peek() + } +} +``` + +```swift +class Solution { + func findKthLargest(_ nums: [Int], _ k: Int) -> Int { + var minHeap = Heap() + + for num in nums { + minHeap.insert(num) + if minHeap.count > k { + _ = minHeap.removeMin() + } + } + + return minHeap.popMin()! + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log k)$ +* Space complexity: $O(k)$ + +> Where $n$ is the length of the array $nums$. + +--- + +## 3. Quick Select + +::tabs-start + +```python +class Solution: + + def findKthLargest(self, nums: List[int], k: int) -> int: + k = len(nums) - k + + def quickSelect(l, r): + pivot, p = nums[r], l + for i in range(l, r): + if nums[i] <= pivot: + nums[p], nums[i] = nums[i], nums[p] + p += 1 + nums[p], nums[r] = nums[r], nums[p] + + if p > k: + return quickSelect(l, p - 1) + elif p < k: + return quickSelect(p + 1, r) + else: + return nums[p] + + return quickSelect(0, len(nums) - 1) +``` + +```java +public class Solution { + public int findKthLargest(int[] nums, int k) { + k = nums.length - k; + + return quickSelect(nums, 0, nums.length - 1, k); + } + + private int quickSelect(int[] nums, int left, int right, int k) { + int pivot = nums[right]; + int p = left; + + for (int i = left; i < right; i++) { + if (nums[i] <= pivot) { + int temp = nums[p]; + nums[p] = nums[i]; + nums[i] = temp; + p++; + } + } + + int temp = nums[p]; + nums[p] = nums[right]; + nums[right] = temp; + + if (p > k) { + return quickSelect(nums, left, p - 1, k); + } else if (p < k) { + return quickSelect(nums, p + 1, right, k); + } else { + return nums[p]; + } + } +} +``` + +```cpp +class Solution { +public: + int findKthLargest(vector& nums, int k) { + k = nums.size() - k; + return quickSelect(nums, 0, nums.size() - 1, k); + } + + int quickSelect(vector& nums, int left, int right, int k) { + int pivot = nums[right]; + int p = left; + + for (int i = left; i < right; ++i) { + if (nums[i] <= pivot) { + swap(nums[p], nums[i]); + p++; + } + } + swap(nums[p], nums[right]); + + if (p > k) { + return quickSelect(nums, left, p - 1, k); + } else if (p < k) { + return quickSelect(nums, p + 1, right, k); + } else { + return nums[p]; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + findKthLargest(nums, k) { + k = nums.length - k; + + const quickSelect = (left, right) => { + let pivot = nums[right]; + let p = left; + + for (let i = left; i < right; i++) { + if (nums[i] <= pivot) { + [nums[p], nums[i]] = [nums[i], nums[p]]; + p++; + } + } + [nums[p], nums[right]] = [nums[right], nums[p]]; + + if (p > k) { + return quickSelect(left, p - 1); + } else if (p < k) { + return quickSelect(p + 1, right); + } else { + return nums[p]; + } + }; + + return quickSelect(0, nums.length - 1); + } +} +``` + +```csharp +public class Solution { + public int FindKthLargest(int[] nums, int k) { + k = nums.Length - k; + return QuickSelect(nums, 0, nums.Length - 1, k); + } + + private int QuickSelect(int[] nums, int left, int right, int k) { + int pivot = nums[right]; + int p = left; + + for (int i = left; i < right; i++) { + if (nums[i] <= pivot) { + int temp = nums[p]; + nums[p] = nums[i]; + nums[i] = temp; + p++; + } + } + + int tmp = nums[p]; + nums[p] = nums[right]; + nums[right] = tmp; + + if (p > k) { + return QuickSelect(nums, left, p - 1, k); + } else if (p < k) { + return QuickSelect(nums, p + 1, right, k); + } else { + return nums[p]; + } + } +} +``` + +```go +func findKthLargest(nums []int, k int) int { + k = len(nums) - k + + var quickSelect func(l, r int) int + quickSelect = func(l, r int) int { + pivot, p := nums[r], l + for i := l; i < r; i++ { + if nums[i] <= pivot { + nums[p], nums[i] = nums[i], nums[p] + p++ + } + } + nums[p], nums[r] = nums[r], nums[p] + + if p > k { + return quickSelect(l, p-1) + } else if p < k { + return quickSelect(p+1, r) + } else { + return nums[p] + } + } + + return quickSelect(0, len(nums)-1) +} +``` + +```kotlin +class Solution { + fun findKthLargest(nums: IntArray, k: Int): Int { + val target = nums.size - k + + fun quickSelect(l: Int, r: Int): Int { + val pivot = nums[r] + var p = l + for (i in l until r) { + if (nums[i] <= pivot) { + nums[p] = nums[i].also { nums[i] = nums[p] } + p++ + } + } + nums[p] = nums[r].also { nums[r] = nums[p] } + + return when { + p > target -> quickSelect(l, p - 1) + p < target -> quickSelect(p + 1, r) + else -> nums[p] + } + } + + return quickSelect(0, nums.size - 1) + } +} +``` + +```swift +class Solution { + func findKthLargest(_ nums: [Int], _ k: Int) -> Int { + var nums = nums + let k = nums.count - k + + func quickSelect(_ l: Int, _ r: Int) -> Int { + let pivot = nums[r] + var p = l + + for i in l.. k { + return quickSelect(l, p - 1) + } else if p < k { + return quickSelect(p + 1, r) + } else { + return nums[p] + } + } + + return quickSelect(0, nums.count - 1) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ in average case, $O(n ^ 2)$ in worst case. +* Space complexity: $O(n)$ + +--- + +## 4. Quick Select (Optimal) + +::tabs-start + +```python +class Solution: + def partition(self, nums: List[int], left: int, right: int) -> int: + mid = (left + right) >> 1 + nums[mid], nums[left + 1] = nums[left + 1], nums[mid] + + if nums[left] < nums[right]: + nums[left], nums[right] = nums[right], nums[left] + if nums[left + 1] < nums[right]: + nums[left + 1], nums[right] = nums[right], nums[left + 1] + if nums[left] < nums[left + 1]: + nums[left], nums[left + 1] = nums[left + 1], nums[left] + + pivot = nums[left + 1] + i = left + 1 + j = right + + while True: + while True: + i += 1 + if not nums[i] > pivot: + break + while True: + j -= 1 + if not nums[j] < pivot: + break + if i > j: + break + nums[i], nums[j] = nums[j], nums[i] + + nums[left + 1], nums[j] = nums[j], nums[left + 1] + return j + + def quickSelect(self, nums: List[int], k: int) -> int: + left = 0 + right = len(nums) - 1 + + while True: + if right <= left + 1: + if right == left + 1 and nums[right] > nums[left]: + nums[left], nums[right] = nums[right], nums[left] + return nums[k] + + j = self.partition(nums, left, right) + + if j >= k: + right = j - 1 + if j <= k: + left = j + 1 + + def findKthLargest(self, nums: List[int], k: int) -> int: + return self.quickSelect(nums, k - 1) +``` + +```java +public class Solution { + private int partition(int[] nums, int left, int right) { + int mid = (left + right) >> 1; + swap(nums, mid, left + 1); + + if (nums[left] < nums[right]) + swap(nums, left, right); + if (nums[left + 1] < nums[right]) + swap(nums, left + 1, right); + if (nums[left] < nums[left + 1]) + swap(nums, left, left + 1); + + int pivot = nums[left + 1]; + int i = left + 1; + int j = right; + + while (true) { + while (nums[++i] > pivot); + while (nums[--j] < pivot); + if (i > j) break; + swap(nums, i, j); + } + + nums[left + 1] = nums[j]; + nums[j] = pivot; + return j; + } + + private void swap(int[] nums, int i, int j) { + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } + + private int quickSelect(int[] nums, int k) { + int left = 0; + int right = nums.length - 1; + + while (true) { + if (right <= left + 1) { + if (right == left + 1 && nums[right] > nums[left]) + swap(nums, left, right); + return nums[k]; + } + + int j = partition(nums, left, right); + + if (j >= k) right = j - 1; + if (j <= k) left = j + 1; + } + } + + public int findKthLargest(int[] nums, int k) { + return quickSelect(nums, k - 1); + } +} +``` + +```cpp +class Solution { +public: + int partition(vector& nums, int left, int right) { + int mid = (left + right) >> 1; + swap(nums[mid], nums[left + 1]); + + if (nums[left] < nums[right]) + swap(nums[left], nums[right]); + if (nums[left + 1] < nums[right]) + swap(nums[left + 1], nums[right]); + if (nums[left] < nums[left + 1]) + swap(nums[left], nums[left + 1]); + + int pivot = nums[left + 1]; + int i = left + 1; + int j = right; + + while (true) { + while (nums[++i] > pivot); + while (nums[--j] < pivot); + if (i > j) break; + swap(nums[i], nums[j]); + } + + nums[left + 1] = nums[j]; + nums[j] = pivot; + return j; + } + + int quickSelect(vector& nums, int k) { + int left = 0; + int right = nums.size() - 1; + + while (true) { + if (right <= left + 1) { + if (right == left + 1 && nums[right] > nums[left]) + swap(nums[left], nums[right]); + return nums[k]; + } + + int j = partition(nums, left, right); + + if (j >= k) right = j - 1; + if (j <= k) left = j + 1; + } + } + + int findKthLargest(vector& nums, int k) { + return quickSelect(nums, k - 1); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + findKthLargest(nums, k) { + function partition(left, right) { + const mid = (left + right) >> 1; + [nums[mid], nums[left + 1]] = [nums[left + 1], nums[mid]]; + + if (nums[left] < nums[right]) + [nums[left], nums[right]] = [nums[right], nums[left]]; + if (nums[left + 1] < nums[right]) + [nums[left + 1], nums[right]] = [nums[right], nums[left + 1]]; + if (nums[left] < nums[left + 1]) + [nums[left], nums[left + 1]] = [nums[left + 1], nums[left]]; + + const pivot = nums[left + 1]; + let i = left + 1; + let j = right; + + while (true) { + while (nums[++i] > pivot); + while (nums[--j] < pivot); + if (i > j) break; + [nums[i], nums[j]] = [nums[j], nums[i]]; + } + + nums[left + 1] = nums[j]; + nums[j] = pivot; + return j; + } + + function quickSelect(k) { + let left = 0; + let right = nums.length - 1; + + while (true) { + if (right <= left + 1) { + if (right == left + 1 && nums[right] > nums[left]) + [nums[left], nums[right]] = [nums[right], nums[left]]; + return nums[k]; + } + + const j = partition(left, right); + + if (j >= k) right = j - 1; + if (j <= k) left = j + 1; + } + } + + return quickSelect(k - 1); + } +} +``` + +```csharp +public class Solution { + private int Partition(int[] nums, int left, int right) { + int mid = (left + right) >> 1; + (nums[mid], nums[left + 1]) = (nums[left + 1], nums[mid]); + + if (nums[left] < nums[right]) + (nums[left], nums[right]) = (nums[right], nums[left]); + if (nums[left + 1] < nums[right]) + (nums[left + 1], nums[right]) = (nums[right], nums[left + 1]); + if (nums[left] < nums[left + 1]) + (nums[left], nums[left + 1]) = (nums[left + 1], nums[left]); + + int pivot = nums[left + 1]; + int i = left + 1; + int j = right; + + while (true) { + while (nums[++i] > pivot); + while (nums[--j] < pivot); + if (i > j) break; + (nums[i], nums[j]) = (nums[j], nums[i]); + } + + nums[left + 1] = nums[j]; + nums[j] = pivot; + return j; + } + + private int QuickSelect(int[] nums, int k) { + int left = 0; + int right = nums.Length - 1; + + while (true) { + if (right <= left + 1) { + if (right == left + 1 && nums[right] > nums[left]) + (nums[left], nums[right]) = (nums[right], nums[left]); + return nums[k]; + } + + int j = Partition(nums, left, right); + + if (j >= k) right = j - 1; + if (j <= k) left = j + 1; + } + } + + public int FindKthLargest(int[] nums, int k) { + return QuickSelect(nums, k - 1); + } +} +``` + +```go +func findKthLargest(nums []int, k int) int { + var partition func(left, right int) int + partition = func(left, right int) int { + mid := (left + right) >> 1 + nums[mid], nums[left+1] = nums[left+1], nums[mid] + + if nums[left] < nums[right] { + nums[left], nums[right] = nums[right], nums[left] + } + if nums[left+1] < nums[right] { + nums[left+1], nums[right] = nums[right], nums[left+1] + } + if nums[left] < nums[left+1] { + nums[left], nums[left+1] = nums[left+1], nums[left] + } + + pivot := nums[left+1] + i := left + 1 + j := right + + for { + for i++; nums[i] > pivot; i++ {} + for j--; nums[j] < pivot; j-- {} + if i > j { + break + } + nums[i], nums[j] = nums[j], nums[i] + } + + nums[left+1], nums[j] = nums[j], nums[left+1] + return j + } + + quickSelect := func(k int) int { + left := 0 + right := len(nums) - 1 + + for { + if right <= left+1 { + if right == left+1 && nums[right] > nums[left] { + nums[left], nums[right] = nums[right], nums[left] + } + return nums[k] + } + + j := partition(left, right) + + if j >= k { + right = j - 1 + } + if j <= k { + left = j + 1 + } + } + } + + return quickSelect(k - 1) +} +``` + +```kotlin +class Solution { + private fun partition(nums: IntArray, left: Int, right: Int): Int { + val mid = (left + right) shr 1 + nums[mid] = nums[left + 1].also { nums[left + 1] = nums[mid] } + + if (nums[left] < nums[right]) + nums[left] = nums[right].also { nums[right] = nums[left] } + if (nums[left + 1] < nums[right]) + nums[left + 1] = nums[right].also { nums[right] = nums[left + 1] } + if (nums[left] < nums[left + 1]) + nums[left] = nums[left + 1].also { nums[left + 1] = nums[left] } + + val pivot = nums[left + 1] + var i = left + 1 + var j = right + + while (true) { + while (nums[++i] > pivot); + while (nums[--j] < pivot); + if (i > j) break + nums[i] = nums[j].also { nums[j] = nums[i] } + } + + nums[left + 1] = nums[j] + nums[j] = pivot + return j + } + + private fun quickSelect(nums: IntArray, k: Int): Int { + var left = 0 + var right = nums.size - 1 + + while (true) { + if (right <= left + 1) { + if (right == left + 1 && nums[right] > nums[left]) + nums[left] = nums[right].also { nums[right] = nums[left] } + return nums[k] + } + + val j = partition(nums, left, right) + + if (j >= k) right = j - 1 + if (j <= k) left = j + 1 + } + } + + fun findKthLargest(nums: IntArray, k: Int): Int { + return quickSelect(nums, k - 1) + } +} +``` + +```swift +class Solution { + func partition(_ nums: inout [Int], _ left: Int, _ right: Int) -> Int { + let mid = (left + right) >> 1 + nums.swapAt(mid, left + 1) + + if nums[left] < nums[right] { + nums.swapAt(left, right) + } + if nums[left + 1] < nums[right] { + nums.swapAt(left + 1, right) + } + if nums[left] < nums[left + 1] { + nums.swapAt(left, left + 1) + } + + let pivot = nums[left + 1] + var i = left + 1 + var j = right + + while true { + repeat { i += 1 } while nums[i] > pivot + repeat { j -= 1 } while nums[j] < pivot + + if i > j { + break + } + nums.swapAt(i, j) + } + + nums.swapAt(left + 1, j) + return j + } + + func quickSelect(_ nums: inout [Int], _ k: Int) -> Int { + var left = 0 + var right = nums.count - 1 + + while true { + if right <= left + 1 { + if right == left + 1 && nums[right] > nums[left] { + nums.swapAt(left, right) + } + return nums[k] + } + + let j = partition(&nums, left, right) + + if j >= k { + right = j - 1 + } + if j <= k { + left = j + 1 + } + } + } + + func findKthLargest(_ nums: [Int], _ k: Int) -> Int { + var nums = nums + return quickSelect(&nums, k - 1) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ in average case, $O(n ^ 2)$ in worst case. +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/kth-largest-integer-in-a-stream.md b/articles/kth-largest-integer-in-a-stream.md new file mode 100644 index 000000000..42a4f617a --- /dev/null +++ b/articles/kth-largest-integer-in-a-stream.md @@ -0,0 +1,380 @@ +## 1. Sorting + +::tabs-start + +```python +class KthLargest: + + def __init__(self, k: int, nums: List[int]): + self.k = k + self.arr = nums + + def add(self, val: int) -> int: + self.arr.append(val) + self.arr.sort() + return self.arr[len(self.arr) - self.k] +``` + +```java +class KthLargest { + List arr; + int K; + public KthLargest(int k, int[] nums) { + K = k; + arr = new ArrayList(); + for (int i = 0; i < nums.length; i++) { + arr.add(nums[i]); + } + } + + public int add(int val) { + arr.add(val); + Collections.sort(arr); + return arr.get(arr.size() - K); + } +} +``` + +```cpp +class KthLargest { +public: + vector arr; + int k; + KthLargest(int k, vector& nums) { + this->arr = nums; + this->k = k; + } + + int add(int val) { + arr.push_back(val); + sort(arr.begin(), arr.end()); + return arr[arr.size() - k]; + } +}; +``` + +```javascript +class KthLargest { + /** + * @param {number} k + * @param {number[]} nums + */ + constructor(k, nums) { + this.arr = nums; + this.k = k; + } + + /** + * @param {number} val + * @return {number} + */ + add(val) { + this.arr.push(val); + this.arr.sort((a, b) => a - b); + return this.arr[this.arr.length - this.k]; + } +} +``` + +```csharp +public class KthLargest { + private List arr; + private int K; + + public KthLargest(int k, int[] nums) { + arr = new List(nums); + K = k; + } + + public int Add(int val) { + arr.Add(val); + arr.Sort(); + return arr[arr.Count - K]; + } +} +``` + +```go +type KthLargest struct { + k int + arr []int +} + +func Constructor(k int, nums []int) KthLargest { + return KthLargest{k: k, arr: nums} +} + +func (this *KthLargest) Add(val int) int { + this.arr = append(this.arr, val) + sort.Ints(this.arr) + return this.arr[len(this.arr)-this.k] +} +``` + +```kotlin +class KthLargest(k: Int, nums: IntArray) { + private val k = k + private val arr = nums.toMutableList() + + fun add(`val`: Int): Int { + arr.add(`val`) + arr.sort() + return arr[arr.size - k] + } +} +``` + +```swift +class KthLargest { + private var k: Int + private var arr: [Int] + + init(_ k: Int, _ nums: [Int]) { + self.k = k + self.arr = nums + } + + func add(_ val: Int) -> Int { + arr.append(val) + arr.sort() + return arr[arr.count - k] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n\log n)$ +* Space complexity: + * $O(m)$ extra space. + * $O(1)$ or $O(n)$ space depending on the sorting algorithm. + +> Where $m$ is the number of calls made to $add()$ and $n$ is the current size of the array. + +--- + +## 2. Min-Heap + +::tabs-start + +```python +class KthLargest: + + def __init__(self, k: int, nums: List[int]): + self.minHeap, self.k = nums, k + heapq.heapify(self.minHeap) + while len(self.minHeap) > k: + heapq.heappop(self.minHeap) + + def add(self, val: int) -> int: + heapq.heappush(self.minHeap, val) + if len(self.minHeap) > self.k: + heapq.heappop(self.minHeap) + return self.minHeap[0] +``` + +```java +class KthLargest { + + private PriorityQueue minHeap; + private int k; + + public KthLargest(int k, int[] nums) { + this.k = k; + this.minHeap = new PriorityQueue<>(); + for (int num : nums) { + minHeap.offer(num); + if (minHeap.size() > k) { + minHeap.poll(); + } + } + } + + public int add(int val) { + minHeap.offer(val); + if (minHeap.size() > k) { + minHeap.poll(); + } + return minHeap.peek(); + } +} +``` + +```cpp +class KthLargest { +private: + priority_queue, greater> minHeap; + int k; + +public: + KthLargest(int k, vector& nums) { + this->k = k; + for (int num : nums) { + minHeap.push(num); + if (minHeap.size() > k) { + minHeap.pop(); + } + } + } + + int add(int val) { + minHeap.push(val); + if (minHeap.size() > k) { + minHeap.pop(); + } + return minHeap.top(); + } +}; +``` + +```javascript +/** + * const { MinPriorityQueue } = require('@datastructures-js/priority-queue'); + */ + +class KthLargest { + /** + * @param {number} k + * @param {number[]} nums + */ + constructor(k, nums) { + this.minHeap = new MinPriorityQueue(); + this.k = k; + + for (const num of nums) { + this.minHeap.enqueue(num); + } + + while (this.minHeap.size() > k) { + this.minHeap.dequeue(); + } + } + + /** + * @param {number} val + * @return {number} + */ + add(val) { + this.minHeap.enqueue(val); + if (this.minHeap.size() > this.k) { + this.minHeap.dequeue(); + } + return this.minHeap.front(); + } +} +``` + +```csharp +public class KthLargest { + + private PriorityQueue minHeap; + private int k; + + public KthLargest(int k, int[] nums) { + this.k = k; + this.minHeap = new PriorityQueue(); + foreach (int num in nums) { + minHeap.Enqueue(num, num); + if (minHeap.Count > k) { + minHeap.Dequeue(); + } + } + } + + public int Add(int val) { + minHeap.Enqueue(val, val); + if (minHeap.Count > k) { + minHeap.Dequeue(); + } + return minHeap.Peek(); + } +} +``` + +```go +type KthLargest struct { + minHeap *priorityqueue.Queue + k int +} + +func Constructor(k int, nums []int) KthLargest { + minHeap := priorityqueue.NewWith(utils.IntComparator) + for _, num := range nums { + minHeap.Enqueue(num) + } + for minHeap.Size() > k { + minHeap.Dequeue() + } + return KthLargest{minHeap: minHeap, k: k} +} + +func (this *KthLargest) Add(val int) int { + this.minHeap.Enqueue(val) + if this.minHeap.Size() > this.k { + this.minHeap.Dequeue() + } + top, _ := this.minHeap.Peek() + return top.(int) +} +``` + +```kotlin +class KthLargest(k: Int, nums: IntArray) { + private val k = k + private val minHeap = PriorityQueue() + + init { + for (num in nums) { + minHeap.offer(num) + } + while (minHeap.size > k) { + minHeap.poll() + } + } + + fun add(`val`: Int): Int { + minHeap.offer(`val`) + if (minHeap.size > k) { + minHeap.poll() + } + return minHeap.peek() + } +} +``` + +```swift +class KthLargest { + private var minHeap: Heap + private let k: Int + + init(_ k: Int, _ nums: [Int]) { + self.k = k + self.minHeap = Heap() + for num in nums { + minHeap.insert(num) + if minHeap.count > k { + minHeap.popMin() + } + } + } + + func add(_ val: Int) -> Int { + minHeap.insert(val) + if minHeap.count > k { + minHeap.popMin() + } + return minHeap.min! + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * \log k)$ +* Space complexity: $O(k)$ + +> Where $m$ is the number of calls made to $add()$. \ No newline at end of file diff --git a/articles/kth-smallest-integer-in-bst.md b/articles/kth-smallest-integer-in-bst.md new file mode 100644 index 000000000..ec5dfe795 --- /dev/null +++ b/articles/kth-smallest-integer-in-bst.md @@ -0,0 +1,1503 @@ +## 1. Brute Force + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def kthSmallest(self, root: Optional[TreeNode], k: int) -> int: + arr = [] + + def dfs(node): + if not node: + return + + arr.append(node.val) + dfs(node.left) + dfs(node.right) + + dfs(root) + arr.sort() + return arr[k - 1] +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public int kthSmallest(TreeNode root, int k) { + List arr = new ArrayList<>(); + + dfs(root, arr); + Collections.sort(arr); + return arr.get(k - 1); + } + + private void dfs(TreeNode node, List arr) { + if (node == null) { + return; + } + + arr.add(node.val); + dfs(node.left, arr); + dfs(node.right, arr); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + int kthSmallest(TreeNode* root, int k) { + vector arr; + dfs(root, arr); + sort(arr.begin(), arr.end()); + return arr[k - 1]; + } + + void dfs(TreeNode* node, vector& arr) { + if (!node) return; + arr.push_back(node->val); + dfs(node->left, arr); + dfs(node->right, arr); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @param {number} k + * @return {number} + */ + kthSmallest(root, k) { + const arr = []; + this.dfs(root, arr); + arr.sort((a, b) => a - b); + return arr[k - 1]; + } + + /** + * @param {TreeNode} node + * @param {number[]} arr + */ + dfs(node, arr) { + if (!node) return; + arr.push(node.val); + this.dfs(node.left, arr); + this.dfs(node.right, arr); + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public int KthSmallest(TreeNode root, int k) { + List arr = new List(); + Dfs(root, arr); + arr.Sort(); + return arr[k - 1]; + } + + private void Dfs(TreeNode node, List arr) { + if (node == null) return; + arr.Add(node.val); + Dfs(node.left, arr); + Dfs(node.right, arr); + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func kthSmallest(root *TreeNode, k int) int { + var arr []int + + var dfs func(node *TreeNode) + dfs = func(node *TreeNode) { + if node == nil { + return + } + + dfs(node.Left) + arr = append(arr, node.Val) + dfs(node.Right) + } + + dfs(root) + return arr[k-1] +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + private val arr = mutableListOf() + + fun kthSmallest(root: TreeNode?, k: Int): Int { + dfs(root) + return arr[k - 1] + } + + private fun dfs(node: TreeNode?) { + if (node == null) { + return + } + + dfs(node.left) + arr.add(node.`val`) + dfs(node.right) + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func kthSmallest(_ root: TreeNode?, _ k: Int) -> Int { + var arr = [Int]() + + func dfs(_ node: TreeNode?) { + guard let node = node else { return } + arr.append(node.val) + dfs(node.left) + dfs(node.right) + } + + dfs(root) + arr.sort() + return arr[k - 1] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Inorder Traversal + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def kthSmallest(self, root: Optional[TreeNode], k: int) -> int: + arr = [] + + def dfs(node): + if not node: + return + + dfs(node.left) + arr.append(node.val) + dfs(node.right) + + dfs(root) + return arr[k - 1] +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public int kthSmallest(TreeNode root, int k) { + List arr = new ArrayList<>(); + + dfs(root, arr); + return arr.get(k - 1); + } + + private void dfs(TreeNode node, List arr) { + if (node == null) { + return; + } + + dfs(node.left, arr); + arr.add(node.val); + dfs(node.right, arr); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + int kthSmallest(TreeNode* root, int k) { + vector arr; + dfs(root, arr); + return arr[k - 1]; + } + + void dfs(TreeNode* node, vector& arr) { + if (!node) return; + dfs(node->left, arr); + arr.push_back(node->val); + dfs(node->right, arr); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @param {number} k + * @return {number} + */ + kthSmallest(root, k) { + const arr = []; + this.dfs(root, arr); + return arr[k - 1]; + } + + /** + * @param {TreeNode} node + * @param {number[]} arr + */ + dfs(node, arr) { + if (!node) return; + this.dfs(node.left, arr); + arr.push(node.val); + this.dfs(node.right, arr); + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public int KthSmallest(TreeNode root, int k) { + List arr = new List(); + Dfs(root, arr); + return arr[k - 1]; + } + + private void Dfs(TreeNode node, List arr) { + if (node == null) return; + Dfs(node.left, arr); + arr.Add(node.val); + Dfs(node.right, arr); + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func kthSmallest(root *TreeNode, k int) int { + var arr []int + + var dfs func(node *TreeNode) + dfs = func(node *TreeNode) { + if node == nil { + return + } + + dfs(node.Left) + arr = append(arr, node.Val) + dfs(node.Right) + } + + dfs(root) + return arr[k-1] +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + private val arr = mutableListOf() + + fun kthSmallest(root: TreeNode?, k: Int): Int { + dfs(root) + return arr[k - 1] + } + + private fun dfs(node: TreeNode?) { + if (node == null) { + return + } + + dfs(node.left) + arr.add(node.`val`) + dfs(node.right) + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func kthSmallest(_ root: TreeNode?, _ k: Int) -> Int { + var arr = [Int]() + + func dfs(_ node: TreeNode?) { + guard let node = node else { return } + dfs(node.left) + arr.append(node.val) + dfs(node.right) + } + + dfs(root) + return arr[k - 1] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Recursive DFS (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def kthSmallest(self, root: Optional[TreeNode], k: int) -> int: + cnt = k + res = root.val + + def dfs(node): + nonlocal cnt, res + if not node: + return + + dfs(node.left) + cnt -= 1 + if cnt == 0: + res = node.val + return + dfs(node.right) + + dfs(root) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public int kthSmallest(TreeNode root, int k) { + int[] tmp = new int[2]; + tmp[0] = k; + dfs(root, tmp); + return tmp[1]; + } + + private void dfs(TreeNode node, int[] tmp) { + if (node == null) { + return; + } + + dfs(node.left, tmp); + tmp[0] -= 1; + if (tmp[0] == 0) { + tmp[1] = node.val; + return; + } + dfs(node.right, tmp); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + int kthSmallest(TreeNode* root, int k) { + vector tmp(2); + tmp[0] = k; + dfs(root, tmp); + return tmp[1]; + } + + void dfs(TreeNode* node, vector& tmp) { + if (!node) return; + dfs(node->left, tmp); + tmp[0]--; + if (tmp[0] == 0) { + tmp[1] = node->val; + return; + } + dfs(node->right, tmp); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @param {number} k + * @return {number} + */ + kthSmallest(root, k) { + const tmp = new Int32Array(2); + tmp[0] = k; + this.dfs(root, tmp); + return tmp[1]; + } + + /** + * @param {TreeNode} node + * @param {number[]} tmp + */ + dfs(node, tmp) { + if (!node) return; + this.dfs(node.left, tmp); + tmp[0]--; + if (tmp[0] === 0) { + tmp[1] = node.val; + return; + } + this.dfs(node.right, tmp); + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public int KthSmallest(TreeNode root, int k) { + int[] tmp = new int[2]; + tmp[0] = k; + Dfs(root, tmp); + return tmp[1]; + } + + private void Dfs(TreeNode node, int[] tmp) { + if (node == null) return; + Dfs(node.left, tmp); + tmp[0]--; + if (tmp[0] == 0) { + tmp[1] = node.val; + return; + } + Dfs(node.right, tmp); + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func kthSmallest(root *TreeNode, k int) int { + cnt, res := k, 0 + + var dfs func(node *TreeNode) + dfs = func(node *TreeNode) { + if node == nil { + return + } + + dfs(node.Left) + cnt-- + if cnt == 0 { + res = node.Val + return + } + dfs(node.Right) + } + + dfs(root) + return res +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + private var cnt = 0 + private var res = 0 + + fun kthSmallest(root: TreeNode?, k: Int): Int { + cnt = k + dfs(root) + return res + } + + private fun dfs(node: TreeNode?) { + if (node == null) { + return + } + + dfs(node.left) + cnt-- + if (cnt == 0) { + res = node.`val` + return + } + dfs(node.right) + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func kthSmallest(_ root: TreeNode?, _ k: Int) -> Int { + var cnt = k + var res = root!.val + + func dfs(_ node: TreeNode?) { + guard let node = node else { return } + + dfs(node.left) + cnt -= 1 + if cnt == 0 { + res = node.val + return + } + dfs(node.right) + } + + dfs(root) + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Iterative DFS (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def kthSmallest(self, root: Optional[TreeNode], k: int) -> int: + stack = [] + curr = root + + while stack or curr: + while curr: + stack.append(curr) + curr = curr.left + curr = stack.pop() + k -= 1 + if k == 0: + return curr.val + curr = curr.right +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + public int kthSmallest(TreeNode root, int k) { + Stack stack = new Stack<>(); + TreeNode curr = root; + + while (!stack.isEmpty() || curr != null) { + while (curr != null) { + stack.push(curr); + curr = curr.left; + } + curr = stack.pop(); + k--; + if (k == 0) { + return curr.val; + } + curr = curr.right; + } + + return -1; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + int kthSmallest(TreeNode* root, int k) { + stack stack; + TreeNode* curr = root; + + while (!stack.empty() || curr != nullptr) { + while (curr != nullptr) { + stack.push(curr); + curr = curr->left; + } + curr = stack.top(); + stack.pop(); + k--; + if (k == 0) { + return curr->val; + } + curr = curr->right; + } + + return -1; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @param {number} k + * @return {number} + */ + kthSmallest(root, k) { + let stack = []; + let curr = root; + + while (stack.length > 0 || curr !== null) { + while (curr !== null) { + stack.push(curr); + curr = curr.left; + } + curr = stack.pop(); + k--; + if (k === 0) { + return curr.val; + } + curr = curr.right; + } + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public int KthSmallest(TreeNode root, int k) { + Stack stack = new Stack(); + TreeNode curr = root; + + while (stack.Count > 0 || curr != null) { + while (curr != null) { + stack.Push(curr); + curr = curr.left; + } + curr = stack.Pop(); + k--; + if (k == 0) { + return curr.val; + } + curr = curr.right; + } + + return -1; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func kthSmallest(root *TreeNode, k int) int { + stack := []*TreeNode{} + curr := root + + for len(stack) > 0 || curr != nil { + for curr != nil { + stack = append(stack, curr) + curr = curr.Left + } + + curr = stack[len(stack)-1] + stack = stack[:len(stack)-1] + + k-- + if k == 0 { + return curr.Val + } + + curr = curr.Right + } + + return 0 +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun kthSmallest(root: TreeNode?, k: Int): Int { + val stack = mutableListOf() + var curr: TreeNode? = root + var k = k + + while (stack.isNotEmpty() || curr != null) { + while (curr != null) { + stack.add(curr) + curr = curr.left + } + + curr = stack.removeLast() + k-- + if (k == 0) { + return curr.`val` + } + + curr = curr.right + } + + return 0 + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func kthSmallest(_ root: TreeNode?, _ k: Int) -> Int { + var stack = [TreeNode]() + var curr = root + var k = k + + while !stack.isEmpty || curr != nil { + while curr != nil { + stack.append(curr!) + curr = curr?.left + } + curr = stack.removeLast() + k -= 1 + if k == 0 { + return curr!.val + } + curr = curr?.right + } + return -1 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Morris Traversal + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def kthSmallest(self, root: Optional[TreeNode], k: int) -> int: + curr = root + + while curr: + if not curr.left: + k -= 1 + if k == 0: + return curr.val + curr = curr.right + else: + pred = curr.left + while pred.right and pred.right != curr: + pred = pred.right + + if not pred.right: + pred.right = curr + curr = curr.left + else: + pred.right = None + k -= 1 + if k == 0: + return curr.val + curr = curr.right + + return -1 +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public int kthSmallest(TreeNode root, int k) { + TreeNode curr = root; + + while (curr != null) { + if (curr.left == null) { + k--; + if (k == 0) return curr.val; + curr = curr.right; + } else { + TreeNode pred = curr.left; + while (pred.right != null && pred.right != curr) + pred = pred.right; + + if (pred.right == null) { + pred.right = curr; + curr = curr.left; + } else { + pred.right = null; + k--; + if (k == 0) return curr.val; + curr = curr.right; + } + } + } + return -1; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + int kthSmallest(TreeNode* root, int k) { + TreeNode* curr = root; + + while (curr) { + if (!curr->left) { + k--; + if (k == 0) return curr->val; + curr = curr->right; + } else { + TreeNode* pred = curr->left; + while (pred->right && pred->right != curr) + pred = pred->right; + + if (!pred->right) { + pred->right = curr; + curr = curr->left; + } else { + pred->right = nullptr; + k--; + if (k == 0) return curr->val; + curr = curr->right; + } + } + } + return -1; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @param {number} k + * @return {number} + */ + kthSmallest(root, k) { + let curr = root; + + while (curr) { + if (!curr.left) { + k--; + if (k === 0) return curr.val; + curr = curr.right; + } else { + let pred = curr.left; + while (pred.right && pred.right !== curr) + pred = pred.right; + + if (!pred.right) { + pred.right = curr; + curr = curr.left; + } else { + pred.right = null; + k--; + if (k === 0) return curr.val; + curr = curr.right; + } + } + } + return -1; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public int KthSmallest(TreeNode root, int k) { + TreeNode curr = root; + + while (curr != null) { + if (curr.left == null) { + k--; + if (k == 0) return curr.val; + curr = curr.right; + } else { + TreeNode pred = curr.left; + while (pred.right != null && pred.right != curr) + pred = pred.right; + + if (pred.right == null) { + pred.right = curr; + curr = curr.left; + } else { + pred.right = null; + k--; + if (k == 0) return curr.val; + curr = curr.right; + } + } + } + return -1; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func kthSmallest(root *TreeNode, k int) int { + curr := root + for { + if curr.Left == nil { + k-- + if k == 0 { + return curr.Val + } + curr = curr.Right + } else { + pred := curr.Left + for pred.Right != nil && pred.Right != curr { + pred = pred.Right + } + if pred.Right == nil { + pred.Right = curr + curr = curr.Left + } else { + pred.Right = nil + k-- + if k == 0 { + return curr.Val + } + curr = curr.Right + } + } + } +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun kthSmallest(root: TreeNode?, k: Int): Int { + var curr: TreeNode? = root + var k = k + while (true) { + if (curr?.left == null) { + k-- + if (k == 0) { + return curr!!.`val` + } + curr = curr?.right + } else { + var pred = curr.left + while (pred?.right != null && pred.right != curr) { + pred = pred.right + } + if (pred?.right == null) { + pred.right = curr + curr = curr.left + } else { + pred.right = null + k-- + if (k == 0) { + return curr!!.`val` + } + curr = curr.right + } + } + } + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func kthSmallest(_ root: TreeNode?, _ k: Int) -> Int { + var curr = root + var k = k + + while curr != nil { + if curr?.left == nil { + k -= 1 + if k == 0 { + return curr!.val + } + curr = curr?.right + } else { + var pred = curr?.left + while pred?.right != nil && pred?.right !== curr { + pred = pred?.right + } + + if pred?.right == nil { + pred?.right = curr + curr = curr?.left + } else { + pred?.right = nil + k -= 1 + if k == 0 { + return curr!.val + } + curr = curr?.right + } + } + } + return -1 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/largest-3-same-digit-number-in-string.md b/articles/largest-3-same-digit-number-in-string.md new file mode 100644 index 000000000..f96e85ee3 --- /dev/null +++ b/articles/largest-3-same-digit-number-in-string.md @@ -0,0 +1,255 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def largestGoodInteger(self, num: str) -> str: + res = "" + val = 0 + + for i in range(len(num) - 2): + if num[i] == num[i + 1] == num[i + 2]: + tmp = num[i : i + 3] + if val <= int(tmp): + val = int(tmp) + res = tmp + + return res +``` + +```java +public class Solution { + public String largestGoodInteger(String num) { + String res = ""; + int val = 0; + + for (int i = 0; i < num.length() - 2; i++) { + if (num.charAt(i) == num.charAt(i + 1) && + num.charAt(i) == num.charAt(i + 2)) { + String tmp = num.substring(i, i + 3); + if (val <= Integer.parseInt(tmp)) { + val = Integer.parseInt(tmp); + res = tmp; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + string largestGoodInteger(string num) { + string res = ""; + int val = 0; + + for (int i = 0; i < num.length() - 2; i++) { + if (num[i] == num[i + 1] && num[i] == num[i + 2]) { + string tmp = num.substr(i, 3); + if (val <= stoi(tmp)) { + val = stoi(tmp); + res = tmp; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} num + * @return {string} + */ + largestGoodInteger(num) { + let res = ""; + let val = 0; + + for (let i = 0; i < num.length - 2; i++) { + if (num[i] === num[i + 1] && num[i] === num[i + 2]) { + const tmp = num.slice(i, i + 3); + if (val <= parseInt(tmp)) { + val = parseInt(tmp); + res = tmp; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Iteration + +::tabs-start + +```python +class Solution: + def largestGoodInteger(self, num: str) -> str: + res = "0" + + for i in range(len(num) - 2): + if num[i] == num[i + 1] == num[i + 2]: + res = max(res, num[i : i + 3]) + + return "" if res == "0" else res +``` + +```java +public class Solution { + public String largestGoodInteger(String num) { + String res = ""; + + for (int i = 0; i < num.length() - 2; i++) { + if (num.charAt(i) == num.charAt(i + 1) && + num.charAt(i) == num.charAt(i + 2)) { + String curr = num.substring(i, i + 3); + if (curr.compareTo(res) > 0) { + res = curr; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + string largestGoodInteger(string num) { + string res = "0"; + + for (int i = 0; i < num.length() - 2; i++) { + if (num[i] == num[i + 1] && num[i] == num[i + 2]) { + res = max(res, num.substr(i, 3)); + } + } + + return res == "0" ? "" : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} num + * @return {string} + */ + largestGoodInteger(num) { + let res = "0"; + + for (let i = 0; i < num.length - 2; i++) { + if (num[i] === num[i + 1] && num[i] === num[i + 2]) { + res = res > num.slice(i, i + 3) ? res : num.slice(i, i + 3); + } + } + + return res === "0" ? "" : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Iteration (Optimal) + +::tabs-start + +```python +class Solution: + def largestGoodInteger(self, num: str) -> str: + res = -1 + + for i in range(len(num) - 2): + if num[i] == num[i + 1] == num[i + 2]: + res = max(res, int(num[i])) + return str(res) * 3 if res != -1 else "" +``` + +```java +public class Solution { + public String largestGoodInteger(String num) { + int res = -1; + + for (int i = 0; i < num.length() - 2; i++) { + if (num.charAt(i) == num.charAt(i + 1) && + num.charAt(i) == num.charAt(i + 2)) { + res = Math.max(res, num.charAt(i) - '0'); + } + } + + return res != -1 ? String.valueOf(res).repeat(3) : ""; + } +} +``` + +```cpp +class Solution { +public: + string largestGoodInteger(string num) { + int res = -1; + + for (int i = 0; i < num.length() - 2; i++) { + if (num[i] == num[i + 1] && num[i] == num[i + 2]) { + res = max(res, num[i] - '0'); + } + } + + return res != -1 ? string(3, res + '0') : ""; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} num + * @return {string} + */ + largestGoodInteger(num) { + let res = -1; + + for (let i = 0; i < num.length - 2; i++) { + if (num[i] === num[i + 1] && num[i] === num[i + 2]) { + res = Math.max(res, Number(num[i])); + } + } + + return res !== -1 ? String(res).repeat(3) : ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/largest-color-value-in-a-directed-graph.md b/articles/largest-color-value-in-a-directed-graph.md new file mode 100644 index 000000000..dcbf467a4 --- /dev/null +++ b/articles/largest-color-value-in-a-directed-graph.md @@ -0,0 +1,589 @@ +## 1. Brute Force (DFS) + +::tabs-start + +```python +class Solution: + def largestPathValue(self, colors: str, edges: list[list[int]]) -> int: + n = len(colors) + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + + visit = [False] * n + def dfs(node, c): + if visit[node]: + return float("inf") + + visit[node] = True + clrCnt = 0 + for nei in adj[node]: + cur = dfs(nei, c) + if cur == float("inf"): + return cur + clrCnt = max(clrCnt, cur) + visit[node] = False + return clrCnt + (c == (ord(colors[node]) - ord('a'))) + + res = -1 + for i in range(n): + for c in range(26): + cnt = dfs(i, c) + if cnt == float("inf"): + return -1 + res = max(res, cnt) + return res +``` + +```java +public class Solution { + private int n; + private List[] adj; + private boolean[] visit; + + public int largestPathValue(String colors, int[][] edges) { + this.n = colors.length(); + this.adj = new ArrayList[n]; + this.visit = new boolean[n]; + + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + } + + int res = -1; + for (int i = 0; i < n; i++) { + for (int c = 0; c < 26; c++) { + int cnt = dfs(i, c, colors); + if (cnt == Integer.MAX_VALUE) return -1; + res = Math.max(res, cnt); + } + } + return res; + } + + private int dfs(int node, int c, String colors) { + if (visit[node]) return Integer.MAX_VALUE; + + visit[node] = true; + int clrCnt = 0; + for (int nei : adj[node]) { + int cur = dfs(nei, c, colors); + if (cur == Integer.MAX_VALUE) return cur; + clrCnt = Math.max(clrCnt, cur); + } + visit[node] = false; + return clrCnt + ((colors.charAt(node) - 'a') == c ? 1 : 0); + } +} +``` + +```cpp +class Solution { +public: + int n; + vector> adj; + vector visit; + + int largestPathValue(string colors, vector>& edges) { + n = colors.size(); + adj.assign(n, vector()); + visit.assign(n, false); + + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + } + + int res = -1; + for (int i = 0; i < n; i++) { + for (int c = 0; c < 26; c++) { + int cnt = dfs(i, c, colors); + if (cnt == 1e9) return -1; + res = max(res, cnt); + } + } + return res; + } + +private: + int dfs(int node, int c, string& colors) { + if (visit[node]) return 1e9; + + visit[node] = true; + int clrCnt = 0; + for (int nei : adj[node]) { + int cur = dfs(nei, c, colors); + if (cur == 1e9) return cur; + clrCnt = max(clrCnt, cur); + } + visit[node] = false; + return clrCnt + ((colors[node] - 'a') == c ? 1 : 0); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} colors + * @param {number[][]} edges + * @return {number} + */ + largestPathValue(colors, edges) { + const n = colors.length; + const adj = Array.from({ length: n }, () => []); + for (const [u, v] of edges) { + adj[u].push(v); + } + + const visit = new Array(n).fill(false); + const dfs = (node, c) => { + if (visit[node]) return Infinity; + + visit[node] = true; + let clrCnt = 0; + for (const nei of adj[node]) { + const cur = dfs(nei, c); + if (cur === Infinity) return cur; + clrCnt = Math.max(clrCnt, cur); + } + visit[node] = false; + return clrCnt + (c === colors.charCodeAt(node) - 97 ? 1 : 0); + }; + + let res = -1; + for (let i = 0; i < n; i++) { + for (let c = 0; c < 26; c++) { + const cnt = dfs(i, c); + if (cnt === Infinity) return -1; + res = Math.max(res, cnt); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V * (V + E))$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of verticies and $E$ is the number of edges. + +--- + +## 2. Depth First Search + +::tabs-start + +```python +class Solution: + def largestPathValue(self, colors: str, edges: list[list[int]]) -> int: + adj = defaultdict(list) + for src, dst in edges: + adj[src].append(dst) + + def dfs(node): + if node in path: + return float("inf") + if node in visit: + return 0 + + visit.add(node) + path.add(node) + colorIndex = ord(colors[node]) - ord('a') + count[node][colorIndex] = 1 + + for nei in adj[node]: + if dfs(nei) == float("inf"): + return float("inf") + for c in range(26): + count[node][c] = max( + count[node][c], + (1 if c == colorIndex else 0) + count[nei][c] + ) + + path.remove(node) + return 0 + + n, res = len(colors), 0 + visit, path = set(), set() + count = [[0] * 26 for _ in range(n)] + + for i in range(n): + if dfs(i) == float("inf"): + return -1 + res = max(res, max(count[i])) + + return res +``` + +```java +public class Solution { + private int n; + private List[] adj; + private boolean[] visit, path; + private int[][] count; + + public int largestPathValue(String colors, int[][] edges) { + this.n = colors.length(); + this.adj = new ArrayList[n]; + this.visit = new boolean[n]; + this.path = new boolean[n]; + this.count = new int[n][26]; + + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + } + + int res = 0; + for (int i = 0; i < n; i++) { + if (dfs(i, colors) == Integer.MAX_VALUE) return -1; + for (int c = 0; c < 26; c++) { + res = Math.max(res, count[i][c]); + } + } + return res; + } + + private int dfs(int node, String colors) { + if (path[node]) return Integer.MAX_VALUE; + if (visit[node]) return 0; + + visit[node] = true; + path[node] = true; + int colorIndex = colors.charAt(node) - 'a'; + count[node][colorIndex] = 1; + + for (int nei : adj[node]) { + if (dfs(nei, colors) == Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } + for (int c = 0; c < 26; c++) { + count[node][c] = Math.max( + count[node][c], + (c == colorIndex ? 1 : 0) + count[nei][c] + ); + } + } + + path[node] = false; + return 0; + } +} +``` + +```cpp +class Solution { +public: + int n, INF = 1e9; + vector> adj; + vector visit, path; + vector> count; + + int largestPathValue(string colors, vector>& edges) { + this->n = colors.size(); + adj.resize(n); + visit.assign(n, false); + path.assign(n, false); + count.assign(n, vector(26)); + + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + } + + int res = 0; + for (int i = 0; i < n; i++) { + if (dfs(i, colors) == INF) return -1; + for (int c = 0; c < 26; c++) { + res = max(res, count[i][c]); + } + } + return res; + } + +private: + int dfs(int node, string& colors) { + if (path[node]) return INF; + if (visit[node]) return 0; + + visit[node] = true; + path[node] = true; + int colorIndex = colors[node] - 'a'; + count[node][colorIndex] = 1; + + for (int& nei : adj[node]) { + if (dfs(nei, colors) == INF) return INF; + for (int c = 0; c < 26; c++) { + count[node][c] = max( + count[node][c], + (c == colorIndex ? 1 : 0) + count[nei][c] + ); + } + } + + path[node] = false; + return 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} colors + * @param {number[][]} edges + * @return {number} + */ + largestPathValue(colors, edges) { + const n = colors.length; + const adj = Array.from({ length: n }, () => []); + for (const [src, dst] of edges) { + adj[src].push(dst); + } + + const visit = new Array(n).fill(false); + const path = new Array(n).fill(false); + const count = Array.from({ length: n }, () => new Array(26).fill(0)); + + const dfs = (node) => { + if (path[node]) return Infinity; + if (visit[node]) return 0; + + visit[node] = true; + path[node] = true; + const colorIndex = colors.charCodeAt(node) - 'a'.charCodeAt(0); + count[node][colorIndex] = 1; + + for (const nei of adj[node]) { + if (dfs(nei) === Infinity) return Infinity; + for (let c = 0; c < 26; c++) { + count[node][c] = Math.max( + count[node][c], + (c === colorIndex ? 1 : 0) + count[nei][c] + ); + } + } + + path[node] = false; + return 0; + }; + + let res = 0; + for (let i = 0; i < n; i++) { + if (dfs(i) === Infinity) return -1; + for (let c = 0; c < 26; c++) { + res = Math.max(res, count[i][c]); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of verticies and $E$ is the number of edges. + +--- + +## 3. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def largestPathValue(self, colors: str, edges: list[list[int]]) -> int: + n = len(colors) + adj = [[] for _ in range(n)] + indegree = [0] * n + count = [[0] * 26 for _ in range(n)] + + for u, v in edges: + adj[u].append(v) + indegree[v] += 1 + + q = deque() + for i in range(n): + if indegree[i] == 0: + q.append(i) + + visit = res = 0 + while q: + node = q.popleft() + visit += 1 + colorIndex = ord(colors[node]) - ord('a') + count[node][colorIndex] += 1 + res = max(res, count[node][colorIndex]) + + for nei in adj[node]: + for c in range(26): + count[nei][c] = max(count[nei][c], count[node][c]) + + indegree[nei] -= 1 + if indegree[nei] == 0: + q.append(nei) + + return res if visit == n else -1 +``` + +```java +public class Solution { + public int largestPathValue(String colors, int[][] edges) { + int n = colors.length(); + List[] adj = new ArrayList[n]; + int[] indegree = new int[n]; + int[][] count = new int[n][26]; + + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + indegree[edge[1]]++; + } + + Queue q = new LinkedList<>(); + for (int i = 0; i < n; i++) { + if (indegree[i] == 0) { + q.add(i); + } + } + + int visit = 0, res = 0; + while (!q.isEmpty()) { + int node = q.poll(); + visit++; + int colorIndex = colors.charAt(node) - 'a'; + count[node][colorIndex]++; + res = Math.max(res, count[node][colorIndex]); + + for (int nei : adj[node]) { + for (int c = 0; c < 26; c++) { + count[nei][c] = Math.max(count[nei][c], count[node][c]); + } + if (--indegree[nei] == 0) { + q.add(nei); + } + } + } + + return visit == n ? res : -1; + } +} +``` + +```cpp +class Solution { +public: + int largestPathValue(string colors, vector>& edges) { + int n = colors.size(); + vector> adj(n); + vector indegree(n); + vector> count(n, vector(26)); + + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + indegree[edge[1]]++; + } + + queue q; + for (int i = 0; i < n; i++) { + if (indegree[i] == 0) { + q.push(i); + } + } + + int visit = 0, res = 0; + while (!q.empty()) { + int node = q.front();q.pop(); + visit++; + int colorIndex = colors[node] - 'a'; + count[node][colorIndex]++; + res = max(res, count[node][colorIndex]); + + for (int& nei : adj[node]) { + for (int c = 0; c < 26; c++) { + count[nei][c] = max(count[nei][c], count[node][c]); + } + if (--indegree[nei] == 0) { + q.push(nei); + } + } + } + + return visit == n ? res : -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} colors + * @param {number[][]} edges + * @return {number} + */ + largestPathValue(colors, edges) { + const n = colors.length; + const adj = Array.from({ length: n }, () => []); + const indegree = new Array(n).fill(0); + const count = Array.from({ length: n }, () => new Array(26).fill(0)); + + for (const [u, v] of edges) { + adj[u].push(v); + indegree[v]++; + } + + const q = new Queue(); + for (let i = 0; i < n; i++) { + if (indegree[i] === 0) { + q.push(i); + } + } + + let visit = 0, res = 0; + while (!q.isEmpty()) { + const node = q.pop(); + visit++; + const colorIndex = colors.charCodeAt(node) - 'a'.charCodeAt(0); + count[node][colorIndex]++; + res = Math.max(res, count[node][colorIndex]); + + for (const nei of adj[node]) { + for (let c = 0; c < 26; c++) { + count[nei][c] = Math.max(count[nei][c], count[node][c]); + } + if (--indegree[nei] === 0) { + q.push(nei); + } + } + } + + return visit === n ? res : -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of verticies and $E$ is the number of edges. \ No newline at end of file diff --git a/articles/largest-divisible-subset.md b/articles/largest-divisible-subset.md new file mode 100644 index 000000000..c5d8fa381 --- /dev/null +++ b/articles/largest-divisible-subset.md @@ -0,0 +1,738 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def largestDivisibleSubset(self, nums: List[int]) -> List[int]: + nums.sort() + cache = {} # (i, prevIndex) -> List + + def dfs(i, prevIndex): + if i == len(nums): + return [] + if (i, prevIndex) in cache: + return cache[(i, prevIndex)] + + res = dfs(i + 1, prevIndex) # Skip nums[i] + if prevIndex == -1 or nums[i] % nums[prevIndex] == 0: + tmp = [nums[i]] + dfs(i + 1, i) # Include nums[i] + res = tmp if len(tmp) > len(res) else res + + cache[(i, prevIndex)] = res + return res + + return dfs(0, -1) +``` + +```java +public class Solution { + private List[][] cache; + + public List largestDivisibleSubset(int[] nums) { + Arrays.sort(nums); + int n = nums.length; + cache = new ArrayList[n][n + 1]; + return dfs(0, -1, nums); + } + + private List dfs(int i, int prevIndex, int[] nums) { + if (i == nums.length) return new ArrayList<>(); + if (cache[i][prevIndex + 1] != null) return cache[i][prevIndex + 1]; + + List res = dfs(i + 1, prevIndex, nums); + + if (prevIndex == -1 || nums[i] % nums[prevIndex] == 0) { + List tmp = new ArrayList<>(); + tmp.add(nums[i]); + tmp.addAll(dfs(i + 1, i, nums)); + if (tmp.size() > res.size()) res = tmp; + } + + cache[i][prevIndex + 1] = res; + return res; + } +} +``` + +```cpp +class Solution { +private: + vector>> cache; + +public: + vector largestDivisibleSubset(vector& nums) { + sort(nums.begin(), nums.end()); + int n = nums.size(); + cache = vector>>(n, vector>(n + 1)); + return dfs(0, -1, nums); + } + + vector dfs(int i, int prevIndex, vector& nums) { + if (i == nums.size()) return {}; + if (!cache[i][prevIndex + 1].empty()) return cache[i][prevIndex + 1]; + + vector res = dfs(i + 1, prevIndex, nums); + + if (prevIndex == -1 || nums[i] % nums[prevIndex] == 0) { + vector tmp = {nums[i]}; + vector next = dfs(i + 1, i, nums); + tmp.insert(tmp.end(), next.begin(), next.end()); + if (tmp.size() > res.size()) res = tmp; + } + + return cache[i][prevIndex + 1] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + largestDivisibleSubset(nums) { + nums.sort((a, b) => a - b); + const cache = new Map(); + + const dfs = (i, prevIndex) => { + if (i === nums.length) return []; + + let key = `${i},${prevIndex}`; + if (cache.has(key)) return cache.get(key); + + let res = dfs(i + 1, prevIndex); + if (prevIndex === -1 || nums[i] % nums[prevIndex] === 0) { + let tmp = [nums[i], ...dfs(i + 1, i)]; + if (tmp.length > res.length) res = tmp; + } + + cache.set(key, res); + return res; + }; + + return dfs(0, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Dynamic Programming (Top-Down) Space Optimized + +::tabs-start + +```python +class Solution: + def largestDivisibleSubset(self, nums: List[int]) -> List[int]: + nums.sort() + cache = {} + + def dfs(i): + if i in cache: + return cache[i] + + res = [nums[i]] + for j in range(i + 1, len(nums)): + if nums[j] % nums[i] == 0: + tmp = [nums[i]] + dfs(j) + if len(tmp) > len(res): + res = tmp + + cache[i] = res + return res + + res = [] + for i in range(len(nums)): + tmp = dfs(i) + if len(tmp) > len(res): + res = tmp + return res +``` + +```java +public class Solution { + private List[] cache; + + public List largestDivisibleSubset(int[] nums) { + Arrays.sort(nums); + int n = nums.length; + cache = new ArrayList[n]; + + List res = new ArrayList<>(); + for (int i = 0; i < n; i++) { + List tmp = dfs(i, nums); + if (tmp.size() > res.size()) { + res = tmp; + } + } + return res; + } + + private List dfs(int i, int[] nums) { + if (cache[i] != null) return cache[i]; + + List res = new ArrayList<>(); + res.add(nums[i]); + for (int j = i + 1; j < nums.length; j++) { + if (nums[j] % nums[i] == 0) { + List tmp = new ArrayList<>(); + tmp.add(nums[i]); + tmp.addAll(dfs(j, nums)); + + if (tmp.size() > res.size()) { + res = tmp; + } + } + } + return cache[i] = res; + } +} +``` + +```cpp +class Solution { +private: + vector> cache; + +public: + vector largestDivisibleSubset(vector& nums) { + sort(nums.begin(), nums.end()); + int n = nums.size(); + cache.resize(n, vector()); + + vector res; + for (int i = 0; i < n; i++) { + vector tmp = dfs(i, nums); + if (tmp.size() > res.size()) { + res = tmp; + } + } + return res; + } + + vector dfs(int i, vector& nums) { + if (!cache[i].empty()) return cache[i]; + + vector res = {nums[i]}; + for (int j = i + 1; j < nums.size(); j++) { + if (nums[j] % nums[i] == 0) { + vector tmp = {nums[i]}; + vector next = dfs(j, nums); + tmp.insert(tmp.end(), next.begin(), next.end()); + + if (tmp.size() > res.size()) { + res = tmp; + } + } + } + return cache[i] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + largestDivisibleSubset(nums) { + nums.sort((a, b) => a - b); + const n = nums.length; + const cache = new Array(n).fill(null); + + const dfs = (i) => { + if (cache[i] !== null) return cache[i]; + + let res = [nums[i]]; + for (let j = i + 1; j < n; j++) { + if (nums[j] % nums[i] === 0) { + let tmp = [nums[i], ...dfs(j)]; + if (tmp.length > res.length) { + res = tmp; + } + } + } + return (cache[i] = res); + }; + + let res = []; + for (let i = 0; i < n; i++) { + let tmp = dfs(i); + if (tmp.length > res.length) { + res = tmp; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def largestDivisibleSubset(self, nums: List[int]) -> List[int]: + nums.sort() + dp = [[num] for num in nums] # dp[i] = longest start at i + res = [] + for i in range(len(nums) - 1, -1, -1): + for j in range(i + 1, len(nums)): + if nums[j] % nums[i] == 0: + tmp = [nums[i]] + dp[j] + dp[i] = tmp if len(tmp) > len(dp[i]) else dp[i] + res = dp[i] if len(dp[i]) > len(res) else res + return res +``` + +```java +public class Solution { + public List largestDivisibleSubset(int[] nums) { + Arrays.sort(nums); + int n = nums.length; + List[] dp = new ArrayList[n]; + List res = new ArrayList<>(); + + for (int i = n - 1; i >= 0; i--) { + dp[i] = new ArrayList<>(); + dp[i].add(nums[i]); + + for (int j = i + 1; j < n; j++) { + if (nums[j] % nums[i] == 0) { + List tmp = new ArrayList<>(); + tmp.add(nums[i]); + tmp.addAll(dp[j]); + + if (tmp.size() > dp[i].size()) { + dp[i] = tmp; + } + } + } + if (dp[i].size() > res.size()) { + res = dp[i]; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector largestDivisibleSubset(vector& nums) { + sort(nums.begin(), nums.end()); + int n = nums.size(); + vector> dp(n); + vector res; + + for (int i = n - 1; i >= 0; i--) { + dp[i].push_back(nums[i]); + + for (int j = i + 1; j < n; j++) { + if (nums[j] % nums[i] == 0) { + vector tmp = dp[j]; + tmp.insert(tmp.begin(), nums[i]); + + if (tmp.size() > dp[i].size()) { + dp[i] = tmp; + } + } + } + if (dp[i].size() > res.size()) { + res = dp[i]; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + largestDivisibleSubset(nums) { + nums.sort((a, b) => a - b); + const n = nums.length; + const dp = new Array(n).fill(0).map(() => []); + let res = []; + + for (let i = n - 1; i >= 0; i--) { + dp[i] = [nums[i]]; + + for (let j = i + 1; j < n; j++) { + if (nums[j] % nums[i] === 0) { + let tmp = [nums[i], ...dp[j]]; + + if (tmp.length > dp[i].length) { + dp[i] = tmp; + } + } + } + if (dp[i].length > res.length) { + res = dp[i]; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Top-Down) + Tracing + +::tabs-start + +```python +class Solution: + def largestDivisibleSubset(self, nums: List[int]) -> List[int]: + nums.sort() + n = len(nums) + dp = [[-1, -1] for _ in range(n)] # dp[i] = [maxLen, prevIdx] + + def dfs(i): + if dp[i][0] != -1: + return dp[i][0] + + dp[i][0] = 1 + for j in range(i + 1, n): + if nums[j] % nums[i] == 0: + length = dfs(j) + 1 + if length > dp[i][0]: + dp[i][0] = length + dp[i][1] = j + + return dp[i][0] + + max_len, start_index = 1, 0 + for i in range(n): + if dfs(i) > max_len: + max_len = dfs(i) + start_index = i + + subset = [] + while start_index != -1: + subset.append(nums[start_index]) + start_index = dp[start_index][1] + + return subset +``` + +```java +public class Solution { + private int[][] dp; + + public List largestDivisibleSubset(int[] nums) { + Arrays.sort(nums); + int n = nums.length; + dp = new int[n][2]; + for (int i = 0; i < n; i++) { + dp[i][0] = -1; + dp[i][1] = -1; + } + + int maxLen = 1, startIndex = 0; + for (int i = 0; i < n; i++) { + if (dfs(i, nums) > maxLen) { + maxLen = dp[i][0]; + startIndex = i; + } + } + + List subset = new ArrayList<>(); + while (startIndex != -1) { + subset.add(nums[startIndex]); + startIndex = dp[startIndex][1]; + } + return subset; + } + + private int dfs(int i, int[] nums) { + if (dp[i][0] != -1) return dp[i][0]; + + dp[i][0] = 1; + for (int j = i + 1; j < nums.length; j++) { + if (nums[j] % nums[i] == 0) { + int length = dfs(j, nums) + 1; + if (length > dp[i][0]) { + dp[i][0] = length; + dp[i][1] = j; + } + } + } + return dp[i][0]; + } +} +``` + +```cpp +class Solution { + vector> dp; + +public: + vector largestDivisibleSubset(vector& nums) { + sort(nums.begin(), nums.end()); + int n = nums.size(); + dp.assign(n, vector(2, -1)); + + int maxLen = 1, startIndex = 0; + for (int i = 0; i < n; i++) { + if (dfs(i, nums) > maxLen) { + maxLen = dp[i][0]; + startIndex = i; + } + } + + vector subset; + while (startIndex != -1) { + subset.push_back(nums[startIndex]); + startIndex = dp[startIndex][1]; + } + return subset; + } + +private: + int dfs(int i, vector& nums) { + if (dp[i][0] != -1) return dp[i][0]; + + dp[i][0] = 1; + for (int j = i + 1; j < nums.size(); j++) { + if (nums[j] % nums[i] == 0) { + int length = dfs(j, nums) + 1; + if (length > dp[i][0]) { + dp[i][0] = length; + dp[i][1] = j; + } + } + } + return dp[i][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + largestDivisibleSubset(nums) { + nums.sort((a, b) => a - b); + let n = nums.length; + let dp = Array.from({ length: n }, () => [-1, -1]); // dp[i] = [maxLen, prevIdx] + + const dfs = (i) => { + if (dp[i][0] !== -1) return dp[i][0]; + + dp[i][0] = 1; + for (let j = i + 1; j < n; j++) { + if (nums[j] % nums[i] === 0) { + let length = dfs(j) + 1; + if (length > dp[i][0]) { + dp[i][0] = length; + dp[i][1] = j; + } + } + } + return dp[i][0]; + }; + + let maxLen = 1, startIndex = 0; + for (let i = 0; i < n; i++) { + if (dfs(i) > maxLen) { + maxLen = dfs(i); + startIndex = i; + } + } + + let subset = []; + while (startIndex !== -1) { + subset.push(nums[startIndex]); + startIndex = dp[startIndex][1]; + } + return subset; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 5. Dynamic Programming (Bottom-Up) + Tracing + +::tabs-start + +```python +class Solution: + def largestDivisibleSubset(self, nums: List[int]) -> List[int]: + nums.sort() + n = len(nums) + dp = [[1, -1] for _ in range(n)] # dp[i] = [maxLen, prevIdx] + + max_len, start_index = 1, 0 + + for i in range(n): + for j in range(i): + if nums[i] % nums[j] == 0 and dp[j][0] + 1 > dp[i][0]: + dp[i][0] = dp[j][0] + 1 + dp[i][1] = j + + if dp[i][0] > max_len: + max_len = dp[i][0] + start_index = i + + subset = [] + while start_index != -1: + subset.append(nums[start_index]) + start_index = dp[start_index][1] + return subset +``` + +```java +public class Solution { + public List largestDivisibleSubset(int[] nums) { + Arrays.sort(nums); + int n = nums.length; + int[][] dp = new int[n][2]; // dp[i] = {maxLen, prevIdx} + + int maxLen = 1, startIndex = 0; + for (int i = 0; i < n; i++) { + dp[i][0] = 1; + dp[i][1] = -1; + for (int j = 0; j < i; j++) { + if (nums[i] % nums[j] == 0 && dp[j][0] + 1 > dp[i][0]) { + dp[i][0] = dp[j][0] + 1; + dp[i][1] = j; + } + } + + if (dp[i][0] > maxLen) { + maxLen = dp[i][0]; + startIndex = i; + } + } + + List subset = new ArrayList<>(); + while (startIndex != -1) { + subset.add(nums[startIndex]); + startIndex = dp[startIndex][1]; + } + return subset; + } +} +``` + +```cpp +class Solution { +public: + vector largestDivisibleSubset(vector& nums) { + sort(nums.begin(), nums.end()); + int n = nums.size(); + vector> dp(n, vector(2, -1)); // dp[i] = {maxLen, prevIdx} + + int maxLen = 1, startIndex = 0; + for (int i = 0; i < n; i++) { + dp[i][0] = 1; + dp[i][1] = -1; + for (int j = 0; j < i; j++) { + if (nums[i] % nums[j] == 0 && dp[j][0] + 1 > dp[i][0]) { + dp[i][0] = dp[j][0] + 1; + dp[i][1] = j; + } + } + + if (dp[i][0] > maxLen) { + maxLen = dp[i][0]; + startIndex = i; + } + } + + vector subset; + while (startIndex != -1) { + subset.push_back(nums[startIndex]); + startIndex = dp[startIndex][1]; + } + return subset; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + largestDivisibleSubset(nums) { + nums.sort((a, b) => a - b); + let n = nums.length; + let dp = Array.from({ length: n }, () => [1, -1]); // dp[i] = [maxLen, prevIdx] + + let maxLen = 1, startIndex = 0; + for (let i = 0; i < n; i++) { + for (let j = 0; j < i; j++) { + if (nums[i] % nums[j] === 0 && dp[j][0] + 1 > dp[i][0]) { + dp[i][0] = dp[j][0] + 1; + dp[i][1] = j; + } + } + + if (dp[i][0] > maxLen) { + maxLen = dp[i][0]; + startIndex = i; + } + } + + let subset = []; + while (startIndex !== -1) { + subset.push(nums[startIndex]); + startIndex = dp[startIndex][1]; + } + return subset; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/largest-local-values-in-a-matrix.md b/articles/largest-local-values-in-a-matrix.md new file mode 100644 index 000000000..169c4d992 --- /dev/null +++ b/articles/largest-local-values-in-a-matrix.md @@ -0,0 +1,439 @@ +## 1. Iteration + +::tabs-start + +```python +class Solution: + def largestLocal(self, grid: List[List[int]]) -> List[List[int]]: + N = len(grid) + res = [[0] * (N - 2) for _ in range(N - 2)] + + for i in range(N - 2): + for j in range(N - 2): + for r in range(i, i + 3): + for c in range(j, j + 3): + res[i][j] = max(res[i][j], grid[r][c]) + + return res +``` + +```java +public class Solution { + public int[][] largestLocal(int[][] grid) { + int N = grid.length; + int[][] res = new int[N - 2][N - 2]; + + for (int i = 0; i < N - 2; i++) { + for (int j = 0; j < N - 2; j++) { + for (int r = i; r < i + 3; r++) { + for (int c = j; c < j + 3; c++) { + res[i][j] = Math.max(res[i][j], grid[r][c]); + } + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> largestLocal(vector>& grid) { + int N = grid.size(); + vector> res(N - 2, vector(N - 2, 0)); + + for (int i = 0; i < N - 2; i++) { + for (int j = 0; j < N - 2; j++) { + for (int r = i; r < i + 3; r++) { + for (int c = j; c < j + 3; c++) { + res[i][j] = max(res[i][j], grid[r][c]); + } + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number[][]} + */ + largestLocal(grid) { + const N = grid.length; + const res = Array.from({ length: N - 2 }, () => Array(N - 2).fill(0)); + + for (let i = 0; i < N - 2; i++) { + for (let j = 0; j < N - 2; j++) { + for (let r = i; r < i + 3; r++) { + for (let c = j; c < j + 3; c++) { + res[i][j] = Math.max(res[i][j], grid[r][c]); + } + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n ^ 2)$ for the output array. + +--- + +## 2. Generalized Approach (Sparse Table) + +::tabs-start + +```python +class SparseTable: + def __init__(self, grid: List[List[int]]): + self.n = len(grid) + self.LOG = [0] * (self.n + 1) + for i in range(2, self.n + 1): + self.LOG[i] = self.LOG[i // 2] + 1 + + self.sparse_table = [[[[0] * (self.LOG[self.n] + 1) for _ in range(self.LOG[self.n] + 1)] for _ in range(self.n)] for _ in range(self.n)] + + for r in range(self.n): + for c in range(self.n): + self.sparse_table[r][c][0][0] = grid[r][c] + + for i in range(self.LOG[self.n] + 1): + for j in range(self.LOG[self.n] + 1): + for r in range(self.n - (1 << i) + 1): + for c in range(self.n - (1 << j) + 1): + if i == 0 and j == 0: + self.sparse_table[r][c][i][j] = grid[r][c] + elif i == 0: + self.sparse_table[r][c][i][j] = max( + self.sparse_table[r][c][i][j - 1], + self.sparse_table[r][c + (1 << (j - 1))][i][j - 1], + ) + elif j == 0: + self.sparse_table[r][c][i][j] = max( + self.sparse_table[r][c][i - 1][j], + self.sparse_table[r + (1 << (i - 1))][c][i - 1][j], + ) + else: + self.sparse_table[r][c][i][j] = max( + self.sparse_table[r][c][i - 1][j - 1], + self.sparse_table[r + (1 << (i - 1))][c][i - 1][j - 1], + self.sparse_table[r][c + (1 << (j - 1))][i - 1][j - 1], + self.sparse_table[r + (1 << (i - 1))][c + (1 << (j - 1))][i - 1][j - 1], + ) + + def query(self, x1: int, y1: int, x2: int, y2: int) -> int: + lx, ly = self.LOG[x2 - x1 + 1], self.LOG[y2 - y1 + 1] + return max( + self.sparse_table[x1][y1][lx][ly], + self.sparse_table[x2 - (1 << lx) + 1][y1][lx][ly], + self.sparse_table[x1][y2 - (1 << ly) + 1][lx][ly], + self.sparse_table[x2 - (1 << lx) + 1][y2 - (1 << ly) + 1][lx][ly], + ) + + +class Solution: + def largestLocal(self, grid: List[List[int]]) -> List[List[int]]: + N, k = len(grid), 3 + sparse_table = SparseTable(grid) + res = [[0] * (N - k + 1) for _ in range(N - k + 1)] + + for i in range(N - k + 1): + for j in range(N - k + 1): + res[i][j] = sparse_table.query(i, j, i + k - 1, j + k - 1) + + return res +``` + +```java +class SparseTable { + private int[][][][] sparseTable; + private int[] log; + private int n; + + public SparseTable(int[][] grid) { + n = grid.length; + log = new int[n + 1]; + for (int i = 2; i <= n; i++) { + log[i] = log[i >> 1] + 1; + } + + int maxLog = log[n]; + sparseTable = new int[n][n][maxLog + 1][maxLog + 1]; + + for (int r = 0; r < n; r++) { + for (int c = 0; c < n; c++) { + sparseTable[r][c][0][0] = grid[r][c]; + } + } + + for (int i = 0; i <= maxLog; i++) { + for (int j = 0; j <= maxLog; j++) { + for (int r = 0; r + (1 << i) <= n; r++) { + for (int c = 0; c + (1 << j) <= n; c++) { + if (i == 0 && j == 0) continue; + if (i == 0) { + sparseTable[r][c][i][j] = Math.max( + sparseTable[r][c][i][j - 1], + sparseTable[r][c + (1 << (j - 1))][i][j - 1] + ); + } else if (j == 0) { + sparseTable[r][c][i][j] = Math.max( + sparseTable[r][c][i - 1][j], + sparseTable[r + (1 << (i - 1))][c][i - 1][j] + ); + } else { + sparseTable[r][c][i][j] = Math.max( + Math.max(sparseTable[r][c][i - 1][j - 1], sparseTable[r + (1 << (i - 1))][c][i - 1][j - 1]), + Math.max(sparseTable[r][c + (1 << (j - 1))][i - 1][j - 1], + sparseTable[r + (1 << (i - 1))][c + (1 << (j - 1))][i - 1][j - 1]) + ); + } + } + } + } + } + } + + public int query(int x1, int y1, int x2, int y2) { + int lx = log[x2 - x1 + 1]; + int ly = log[y2 - y1 + 1]; + return Math.max( + Math.max(sparseTable[x1][y1][lx][ly], sparseTable[x2 - (1 << lx) + 1][y1][lx][ly]), + Math.max(sparseTable[x1][y2 - (1 << ly) + 1][lx][ly], + sparseTable[x2 - (1 << lx) + 1][y2 - (1 << ly) + 1][lx][ly]) + ); + } +} + +public class Solution { + public int[][] largestLocal(int[][] grid) { + int n = grid.length; + int k = 3; + SparseTable st = new SparseTable(grid); + int[][] res = new int[n - k + 1][n - k + 1]; + + for (int i = 0; i <= n - k; i++) { + for (int j = 0; j <= n - k; j++) { + res[i][j] = st.query(i, j, i + k - 1, j + k - 1); + } + } + + return res; + } +} +``` + +```cpp +class SparseTable { +public: + vector>>> sparseTable; + vector log; + int n; + + SparseTable(vector>& grid) { + n = grid.size(); + log.resize(n + 1, 0); + for (int i = 2; i <= n; i++) { + log[i] = log[i / 2] + 1; + } + + int maxLog = log[n]; + sparseTable.resize(n, vector>>(n, vector>(maxLog + 1, vector(maxLog + 1)))); + + for (int r = 0; r < n; r++) { + for (int c = 0; c < n; c++) { + sparseTable[r][c][0][0] = grid[r][c]; + } + } + + for (int i = 0; i <= maxLog; i++) { + for (int j = 0; j <= maxLog; j++) { + for (int r = 0; r + (1 << i) <= n; r++) { + for (int c = 0; c + (1 << j) <= n; c++) { + if (i == 0 && j == 0) continue; + if (i == 0) { + sparseTable[r][c][i][j] = max( + sparseTable[r][c][i][j - 1], + sparseTable[r][c + (1 << (j - 1))][i][j - 1] + ); + } else if (j == 0) { + sparseTable[r][c][i][j] = max( + sparseTable[r][c][i - 1][j], + sparseTable[r + (1 << (i - 1))][c][i - 1][j] + ); + } else { + sparseTable[r][c][i][j] = max( + max(sparseTable[r][c][i - 1][j - 1], sparseTable[r + (1 << (i - 1))][c][i - 1][j - 1]), + max(sparseTable[r][c + (1 << (j - 1))][i - 1][j - 1], + sparseTable[r + (1 << (i - 1))][c + (1 << (j - 1))][i - 1][j - 1]) + ); + } + } + } + } + } + } + + int query(int x1, int y1, int x2, int y2) { + int lx = log[x2 - x1 + 1]; + int ly = log[y2 - y1 + 1]; + return max( + max(sparseTable[x1][y1][lx][ly], sparseTable[x2 - (1 << lx) + 1][y1][lx][ly]), + max(sparseTable[x1][y2 - (1 << ly) + 1][lx][ly], + sparseTable[x2 - (1 << lx) + 1][y2 - (1 << ly) + 1][lx][ly]) + ); + } +}; + +class Solution { +public: + vector> largestLocal(vector>& grid) { + int n = grid.size(), k = 3; + SparseTable st(grid); + vector> res(n - k + 1, vector(n - k + 1)); + + for (int i = 0; i <= n - k; i++) { + for (int j = 0; j <= n - k; j++) { + res[i][j] = st.query(i, j, i + k - 1, j + k - 1); + } + } + + return res; + } +}; +``` + +```javascript +class SparseTable { + /** + * @constructor + * @param {number[][]} grid + */ + constructor(grid) { + this.n = grid.length; + this.log = Array(this.n + 1).fill(0); + for (let i = 2; i <= this.n; i++) { + this.log[i] = this.log[Math.floor(i / 2)] + 1; + } + + const maxLog = this.log[this.n]; + this.sparseTable = Array.from({ length: this.n }, () => + Array.from({ length: this.n }, () => + Array.from({ length: maxLog + 1 }, () => + Array(maxLog + 1).fill(0) + ) + ) + ); + + for (let r = 0; r < this.n; r++) { + for (let c = 0; c < this.n; c++) { + this.sparseTable[r][c][0][0] = grid[r][c]; + } + } + + for (let i = 0; i <= maxLog; i++) { + for (let j = 0; j <= maxLog; j++) { + for (let r = 0; r + (1 << i) <= this.n; r++) { + for (let c = 0; c + (1 << j) <= this.n; c++) { + if (i === 0 && j === 0) continue; + if (i === 0) { + this.sparseTable[r][c][i][j] = Math.max( + this.sparseTable[r][c][i][j - 1], + this.sparseTable[r][c + (1 << (j - 1))][i][j - 1] + ); + } else if (j === 0) { + this.sparseTable[r][c][i][j] = Math.max( + this.sparseTable[r][c][i - 1][j], + this.sparseTable[r + (1 << (i - 1))][c][i - 1][j] + ); + } else { + this.sparseTable[r][c][i][j] = Math.max( + Math.max( + this.sparseTable[r][c][i - 1][j - 1], + this.sparseTable[r + (1 << (i - 1))][c][i - 1][j - 1] + ), + Math.max( + this.sparseTable[r][c + (1 << (j - 1))][i - 1][j - 1], + this.sparseTable[r + (1 << (i - 1))][c + (1 << (j - 1))][i - 1][j - 1] + ) + ); + } + } + } + } + } + } + + /** + * @param {number} x1 + * @param {number} y1 + * @param {number} x2 + * @param {number} y2 + * @return {number} + */ + query(x1, y1, x2, y2) { + const lx = this.log[x2 - x1 + 1]; + const ly = this.log[y2 - y1 + 1]; + return Math.max( + Math.max( + this.sparseTable[x1][y1][lx][ly], + this.sparseTable[x2 - (1 << lx) + 1][y1][lx][ly] + ), + Math.max( + this.sparseTable[x1][y2 - (1 << ly) + 1][lx][ly], + this.sparseTable[x2 - (1 << lx) + 1][y2 - (1 << ly) + 1][lx][ly] + ) + ); + } +} + +class Solution { + /** + * @param {number[][]} grid + * @return {number[][]} + */ + largestLocal(grid) { + const n = grid.length, k = 3; + const st = new SparseTable(grid); + const res = Array.from({ length: n - k + 1 }, () => + Array(n - k + 1).fill(0) + ); + + for (let i = 0; i <= n - k; i++) { + for (let j = 0; j <= n - k; j++) { + res[i][j] = st.query(i, j, i + k - 1, j + k - 1); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 \log ^ 2 n)$ +* Space complexity: + * $O(n ^ 2 \log ^ 2 n)$ extra space. + * $O((n - k) ^ 2)$ for the output matrix. + +> Where $n$ is the size of the given square grid and $k$ is the fixed size of the submatrix window. \ No newline at end of file diff --git a/articles/largest-number.md b/articles/largest-number.md new file mode 100644 index 000000000..5b1822fb5 --- /dev/null +++ b/articles/largest-number.md @@ -0,0 +1,188 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def largestNumber(self, nums: List[int]) -> str: + arr = [str(num) for num in nums] + + res = [] + while arr: + maxi = 0 + for i in range(1, len(arr)): + if arr[i] + arr[maxi] > arr[maxi] + arr[i]: + maxi = i + res.append(arr[maxi]) + arr.pop(maxi) + + result = "".join(res) + return result if result[0] != '0' else '0' +``` + +```java +public class Solution { + public String largestNumber(int[] nums) { + List arr = new ArrayList<>(); + for (int num : nums) { + arr.add(String.valueOf(num)); + } + + StringBuilder res = new StringBuilder(); + while (!arr.isEmpty()) { + int maxi = 0; + for (int i = 1; i < arr.size(); i++) { + if ((arr.get(i) + arr.get(maxi)).compareTo(arr.get(maxi) + arr.get(i)) > 0) { + maxi = i; + } + } + res.append(arr.get(maxi)); + arr.remove(maxi); + } + + String result = res.toString(); + return result.charAt(0) == '0' ? "0" : result; + } +} +``` + +```cpp +class Solution { +public: + string largestNumber(vector& nums) { + vector arr; + for (int num : nums) { + arr.push_back(to_string(num)); + } + + string res; + while (!arr.empty()) { + int maxi = 0; + for (int i = 1; i < arr.size(); i++) { + if (arr[i] + arr[maxi] > arr[maxi] + arr[i]) { + maxi = i; + } + } + res += arr[maxi]; + arr.erase(arr.begin() + maxi); + } + + return res[0] == '0' ? "0" : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {string} + */ + largestNumber(nums) { + let arr = nums.map(String); + + let res = []; + while (arr.length > 0) { + let maxi = 0; + for (let i = 1; i < arr.length; i++) { + if (arr[i] + arr[maxi] > arr[maxi] + arr[i]) { + maxi = i; + } + } + res.push(arr[maxi]); + arr.splice(maxi, 1); + } + + let result = res.join(""); + return result[0] === "0" ? "0" : result; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * N)$ +* Space complexity: $O(N)$ + +> Where $n$ is the size of the array $nums$ and $N$ is the total number of digits in the array $nums$. + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def largestNumber(self, nums: List[int]) -> str: + for i, num in enumerate(nums): + nums[i] = str(num) + + def compare(n1, n2): + if n1 + n2 > n2 + n1: + return -1 + else: + return 1 + nums = sorted(nums, key=cmp_to_key(compare)) + return str(int("".join(nums))) +``` + +```java +public class Solution { + public String largestNumber(int[] nums) { + String[] arr = Arrays.stream(nums).mapToObj(String::valueOf).toArray(String[]::new); + Arrays.sort(arr, (a, b) -> (b + a).compareTo(a + b)); + String res = String.join("", arr); + return res.charAt(0) == '0' ? "0" : res; + } +} +``` + +```cpp +class Solution { +public: + string largestNumber(vector& nums) { + vector arr; + for (int num : nums) { + arr.push_back(to_string(num)); + } + + sort(arr.begin(), arr.end(), [](string& a, string& b) { + return a + b > b + a; + }); + + string res; + for (string& num : arr) { + res += num; + } + + return res[0] == '0' ? "0" : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {string} + */ + largestNumber(nums) { + let arr = nums.map(String); + arr.sort((a, b) => ((b + a) - (a + b))); + let res = arr.join(""); + return res[0] === "0" ? "0" : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N \log N)$ +* Space complexity: $O(N)$ + +> Where $N$ is the total number of digits in the array $nums$. \ No newline at end of file diff --git a/articles/largest-odd-number-in-string.md b/articles/largest-odd-number-in-string.md new file mode 100644 index 000000000..566fb7c11 --- /dev/null +++ b/articles/largest-odd-number-in-string.md @@ -0,0 +1,166 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def largestOddNumber(self, num: str) -> str: + res = "" + for i in range(len(num)): + for j in range(i, len(num)): + ones_digit = ord(num[j]) - ord('0') + if ones_digit & 1: + cur = num[i:j + 1] + if len(res) < len(cur) or (len(cur) == len(res) and res < cur): + res = cur + return res +``` + +```java +public class Solution { + public String largestOddNumber(String num) { + String res = ""; + int n = num.length(); + + for (int i = 0; i < n; i++) { + for (int j = i; j < n; j++) { + int onesDigit = num.charAt(j) - '0'; + if ((onesDigit & 1) == 1) { + String cur = num.substring(i, j + 1); + if (res.length() < cur.length() || + (res.length() == cur.length() && res.compareTo(cur) < 0)) { + res = cur; + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + string largestOddNumber(string num) { + string res = ""; + int n = num.size(); + + for (int i = 0; i < n; i++) { + for (int j = i; j < n; j++) { + int onesDigit = num[j] - '0'; + if (onesDigit & 1) { + string cur = num.substr(i, j - i + 1); + if (res.size() < cur.size() || + (res.size() == cur.size() && res < cur)) { + res = cur; + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} num + * @return {string} + */ + largestOddNumber(num) { + let res = ""; + const n = num.length; + + for (let i = 0; i < n; i++) { + for (let j = i; j < n; j++) { + const onesDigit = num[j].charCodeAt(0) - '0'.charCodeAt(0); + if (onesDigit & 1) { + const cur = num.slice(i, j + 1); + if (res.length < cur.length || + (res.length === cur.length && res < cur)) { + res = cur; + } + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n)$ + +--- + +## 2. Find The Rightmost Odd Digit + +::tabs-start + +```python +class Solution: + def largestOddNumber(self, num: str) -> str: + for i in range(len(num) - 1, -1, -1): + if int(num[i]) % 2: + return num[:i + 1] + return "" +``` + +```java +public class Solution { + public String largestOddNumber(String num) { + for (int i = num.length() - 1; i >= 0; i--) { + if ((num.charAt(i) - '0') % 2 == 1) { + return num.substring(0, i + 1); + } + } + return ""; + } +} +``` + +```cpp +class Solution { +public: + string largestOddNumber(string num) { + for (int i = num.size() - 1; i >= 0; i--) { + if ((num[i] - '0') % 2 == 1) { + return num.substr(0, i + 1); + } + } + return ""; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} num + * @return {string} + */ + largestOddNumber(num) { + for (let i = num.length - 1; i >= 0; i--) { + if (parseInt(num[i]) % 2 === 1) { + return num.slice(0, i + 1); + } + } + return ""; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ for the output string. \ No newline at end of file diff --git a/articles/largest-rectangle-in-histogram.md b/articles/largest-rectangle-in-histogram.md new file mode 100644 index 000000000..75df88ffe --- /dev/null +++ b/articles/largest-rectangle-in-histogram.md @@ -0,0 +1,1655 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def largestRectangleArea(self, heights: List[int]) -> int: + n = len(heights) + maxArea = 0 + + for i in range(n): + height = heights[i] + + rightMost = i + 1 + while rightMost < n and heights[rightMost] >= height: + rightMost += 1 + + leftMost = i + while leftMost >= 0 and heights[leftMost] >= height: + leftMost -= 1 + + rightMost -= 1 + leftMost += 1 + maxArea = max(maxArea, height * (rightMost - leftMost + 1)) + return maxArea +``` + +```java +public class Solution { + public int largestRectangleArea(int[] heights) { + int n = heights.length; + int maxArea = 0; + + for (int i = 0; i < n; i++) { + int height = heights[i]; + + int rightMost = i + 1; + while (rightMost < n && heights[rightMost] >= height) { + rightMost++; + } + + int leftMost = i; + while (leftMost >= 0 && heights[leftMost] >= height) { + leftMost--; + } + + rightMost--; + leftMost++; + maxArea = Math.max(maxArea, height * (rightMost - leftMost + 1)); + } + return maxArea; + } +} +``` + +```cpp +class Solution { +public: + int largestRectangleArea(vector& heights) { + int n = heights.size(); + int maxArea = 0; + + for (int i = 0; i < n; i++) { + int height = heights[i]; + + int rightMost = i + 1; + while (rightMost < n && heights[rightMost] >= height) { + rightMost++; + } + + int leftMost = i; + while (leftMost >= 0 && heights[leftMost] >= height) { + leftMost--; + } + + rightMost--; + leftMost++; + maxArea = max(maxArea, height * (rightMost - leftMost + 1)); + } + return maxArea; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @return {number} + */ + largestRectangleArea(heights) { + const n = heights.length; + let maxArea = 0; + + for (let i = 0; i < n; i++) { + let height = heights[i]; + + let rightMost = i + 1; + while (rightMost < n && heights[rightMost] >= height) { + rightMost++; + } + + let leftMost = i; + while (leftMost >= 0 && heights[leftMost] >= height) { + leftMost--; + } + + rightMost--; + leftMost++; + maxArea = Math.max(maxArea, height * (rightMost - leftMost + 1)); + } + return maxArea; + } +} +``` + +```csharp +public class Solution { + public int LargestRectangleArea(int[] heights) { + int n = heights.Length; + int maxArea = 0; + + for (int i = 0; i < n; i++) { + int height = heights[i]; + + int rightMost = i + 1; + while (rightMost < n && heights[rightMost] >= height) { + rightMost++; + } + + int leftMost = i; + while (leftMost >= 0 && heights[leftMost] >= height) { + leftMost--; + } + + rightMost--; + leftMost++; + maxArea = Math.Max(maxArea, height * (rightMost - leftMost + 1)); + } + return maxArea; + } +} +``` + +```go +func largestRectangleArea(heights []int) int { + n := len(heights) + maxArea := 0 + + for i := 0; i < n; i++ { + height := heights[i] + + rightMost := i + 1 + for rightMost < n && heights[rightMost] >= height { + rightMost++ + } + + leftMost := i + for leftMost >= 0 && heights[leftMost] >= height { + leftMost-- + } + + rightMost-- + leftMost++ + + area := height * (rightMost - leftMost + 1) + if area > maxArea { + maxArea = area + } + } + + return maxArea +} +``` + +```kotlin +class Solution { + fun largestRectangleArea(heights: IntArray): Int { + val n = heights.size + var maxArea = 0 + + for (i in 0 until n) { + val height = heights[i] + + var rightMost = i + 1 + while (rightMost < n && heights[rightMost] >= height) { + rightMost++ + } + + var leftMost = i + while (leftMost >= 0 && heights[leftMost] >= height) { + leftMost-- + } + + rightMost-- + leftMost++ + + maxArea = maxOf(maxArea, height * (rightMost - leftMost + 1)) + } + + return maxArea + } +} +``` + +```swift +class Solution { + func largestRectangleArea(_ heights: [Int]) -> Int { + let n = heights.count + var maxArea = 0 + + for i in 0..= height { + rightMost += 1 + } + + var leftMost = i + while leftMost >= 0 && heights[leftMost] >= height { + leftMost -= 1 + } + + rightMost -= 1 + leftMost += 1 + maxArea = max(maxArea, height * (rightMost - leftMost + 1)) + } + + return maxArea + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Divide And Conquer (Segment Tree) + +::tabs-start + +```python +class MinIdx_Segtree: + def __init__(self, N, A): + self.n = N + self.INF = int(1e9) + self.A = A + while (self.n & (self.n - 1)) != 0: + self.A.append(self.INF) + self.n += 1 + self.tree = [0] * (2 * self.n) + self.build() + + def build(self): + for i in range(self.n): + self.tree[self.n + i] = i + for j in range(self.n - 1, 0, -1): + a = self.tree[j << 1] + b = self.tree[(j << 1) + 1] + if self.A[a] <= self.A[b]: + self.tree[j] = a + else: + self.tree[j] = b + + def update(self, i, val): + self.A[i] = val + j = (self.n + i) >> 1 + while j >= 1: + a = self.tree[j << 1] + b = self.tree[(j << 1) + 1] + if self.A[a] <= self.A[b]: + self.tree[j] = a + else: + self.tree[j] = b + j >>= 1 + + def query(self, ql, qh): + return self._query(1, 0, self.n - 1, ql, qh) + + def _query(self, node, l, h, ql, qh): + if ql > h or qh < l: + return self.INF + if l >= ql and h <= qh: + return self.tree[node] + a = self._query(node << 1, l, (l + h) >> 1, ql, qh) + b = self._query((node << 1) + 1, ((l + h) >> 1) + 1, h, ql, qh) + if a == self.INF: + return b + if b == self.INF: + return a + return a if self.A[a] <= self.A[b] else b + +class Solution: + def getMaxArea(self, heights, l, r, st): + if l > r: + return 0 + if l == r: + return heights[l] + minIdx = st.query(l, r) + return max(max(self.getMaxArea(heights, l, minIdx - 1, st), + self.getMaxArea(heights, minIdx + 1, r, st)), + (r - l + 1) * heights[minIdx]) + + def largestRectangleArea(self, heights): + n = len(heights) + st = MinIdx_Segtree(n, heights) + return self.getMaxArea(heights, 0, n - 1, st) +``` + +```java +public class MinIdx_Segtree { + int n; + final int INF = (int) 1e9; + int[] A, tree; + + public MinIdx_Segtree(int N, int[] heights) { + this.n = N; + this.A = heights; + while (Integer.bitCount(n) != 1) { + A = java.util.Arrays.copyOf(A, n + 1); + A[n] = INF; + n++; + } + tree = new int[2 * n]; + build(); + } + + public void build() { + for (int i = 0; i < n; i++) { + tree[n + i] = i; + } + for (int j = n - 1; j >= 1; j--) { + int a = tree[j << 1]; + int b = tree[(j << 1) + 1]; + if (A[a] <= A[b]) { + tree[j] = a; + } else { + tree[j] = b; + } + } + } + + public void update(int i, int val) { + A[i] = val; + for (int j = (n + i) >> 1; j >= 1; j >>= 1) { + int a = tree[j << 1]; + int b = tree[(j << 1) + 1]; + if (A[a] <= A[b]) { + tree[j] = a; + } else { + tree[j] = b; + } + } + } + + public int query(int ql, int qh) { + return query(1, 0, n - 1, ql, qh); + } + + public int query(int node, int l, int h, int ql, int qh) { + if (ql > h || qh < l) return INF; + if (l >= ql && h <= qh) return tree[node]; + int a = query(node << 1, l, (l + h) >> 1, ql, qh); + int b = query((node << 1) + 1, ((l + h) >> 1) + 1, h, ql, qh); + if (a == INF) return b; + if (b == INF) return a; + return A[a] <= A[b] ? a : b; + } +} + +public class Solution { + public int getMaxArea(int[] heights, int l, int r, MinIdx_Segtree st) { + if (l > r) return 0; + if (l == r) return heights[l]; + + int minIdx = st.query(l, r); + return Math.max(Math.max(getMaxArea(heights, l, minIdx - 1, st), + getMaxArea(heights, minIdx + 1, r, st)), + (r - l + 1) * heights[minIdx]); + } + + public int largestRectangleArea(int[] heights) { + int n = heights.length; + MinIdx_Segtree st = new MinIdx_Segtree(n, heights); + return getMaxArea(heights, 0, n - 1, st); + } +} +``` + +```cpp +class MinIdx_Segtree { +public: + int n; + const int INF = 1e9; + vector A; + vector tree; + MinIdx_Segtree(int N, vector& a) { + this->n = N; + this->A = a; + while (__builtin_popcount(n) != 1) { + A.push_back(INF); + n++; + } + tree.resize(2 * n); + build(); + } + + void build() { + for (int i = 0; i < n; i++) { + tree[n + i] = i; + } + for (int j = n - 1; j >= 1; j--) { + int a = tree[j<<1]; + int b = tree[(j<<1) + 1]; + if(A[a]<=A[b])tree[j]=a; + else tree[j] = b; + } + } + + void update(int i, int val) { + A[i] = val; + for (int j = (n + i) >> 1; j >= 1; j >>= 1) { + int a = tree[j<<1]; + int b = tree[(j<<1) + 1]; + if(A[a]<=A[b])tree[j]=a; + else tree[j] = b; + } + } + + int query(int ql, int qh) { + return query(1, 0, n - 1, ql, qh); + } + + int query(int node, int l, int h, int ql, int qh) { + if (ql > h || qh < l) return INF; + if (l >= ql && h <= qh) return tree[node]; + int a = query(node << 1, l, (l + h) >> 1, ql, qh); + int b = query((node << 1) + 1, ((l + h) >> 1) + 1, h, ql, qh); + if(a==INF)return b; + if(b==INF)return a; + return A[a]<=A[b]?a:b; + } +}; + +class Solution { +public: + int getMaxArea(vector& heights, int l, int r, MinIdx_Segtree& st) { + if (l > r) return 0; + if (l == r) return heights[l]; + + int minIdx = st.query(l, r); + return max(max(getMaxArea(heights, l, minIdx - 1, st), + getMaxArea(heights, minIdx + 1, r, st)), + (r - l + 1) * heights[minIdx]); + } + int largestRectangleArea(vector& heights) { + int n = heights.size(); + MinIdx_Segtree st(n, heights); + return getMaxArea(heights, 0, n - 1, st); + } +}; +``` + +```javascript +class MinIdx_Segtree { + /** + * @param {number} N + * @param {number[]} heights + */ + constructor(N, heights) { + this.n = N; + this.INF = 1e9; + this.A = heights.slice(); + while ((this.n & (this.n - 1)) !== 0) { + this.A.push(this.INF); + this.n++; + } + this.tree = new Array(2 * this.n).fill(0); + this.build(); + } + + build() { + for (let i = 0; i < this.n; i++) { + this.tree[this.n + i] = i; + } + for (let j = this.n - 1; j >= 1; j--) { + let a = this.tree[j << 1]; + let b = this.tree[(j << 1) + 1]; + this.tree[j] = this.A[a] <= this.A[b] ? a : b; + } + } + + /** + * @param {number} i + * @param {number} val + */ + update(i, val) { + this.A[i] = val; + for (let j = (this.n + i) >> 1; j >= 1; j >>= 1) { + let a = this.tree[j << 1]; + let b = this.tree[(j << 1) + 1]; + this.tree[j] = this.A[a] <= this.A[b] ? a : b; + } + } + + /** + * @param {number} ql + * @param {number} qh + * @return {number} + */ + query(ql, qh) { + return this._query(1, 0, this.n - 1, ql, qh); + } + + _query(node, l, h, ql, qh) { + if (ql > h || qh < l) return this.INF; + if (l >= ql && h <= qh) return this.tree[node]; + let a = this._query(node << 1, l, (l + h) >> 1, ql, qh); + let b = this._query((node << 1) + 1, ((l + h) >> 1) + 1, h, ql, qh); + if (a === this.INF) return b; + if (b === this.INF) return a; + return this.A[a] <= this.A[b] ? a : b; + } +} + +class Solution { + /** + * @param {number[]} heights + * @return {number} + */ + largestRectangleArea(heights) { + const n = heights.length; + const st = new MinIdx_Segtree(n, heights); + return this.getMaxArea(heights, 0, n - 1, st); + } + + /** + * @param {number[]} heights + * @param {number} l + * @param {number} r + * @param {MinIdx_Segtree} st + * @return {number} + */ + getMaxArea(heights, l, r, st) { + if (l > r) return 0; + if (l === r) return heights[l]; + + const minIdx = st.query(l, r); + return Math.max( + this.getMaxArea(heights, l, minIdx - 1, st), + this.getMaxArea(heights, minIdx + 1, r, st), + (r - l + 1) * heights[minIdx] + ); + } +} +``` + +```csharp +public class MinIdx_Segtree { + private int n; + private readonly int INF = (int)1e9; + private int[] A, tree; + + public MinIdx_Segtree(int N, int[] heights) { + this.n = N; + this.A = new int[heights.Length]; + heights.CopyTo(this.A, 0); + while ((n & (n - 1)) != 0) { + Array.Resize(ref A, n + 1); + A[n] = INF; + n++; + } + tree = new int[2 * n]; + Build(); + } + + private void Build() { + for (int i = 0; i < n; i++) { + tree[n + i] = i; + } + for (int j = n - 1; j >= 1; j--) { + int a = tree[j << 1]; + int b = tree[(j << 1) + 1]; + tree[j] = A[a] <= A[b] ? a : b; + } + } + + public int Query(int ql, int qh) { + return Query(1, 0, n - 1, ql, qh); + } + + private int Query(int node, int l, int h, int ql, int qh) { + if (ql > h || qh < l) return INF; + if (l >= ql && h <= qh) return tree[node]; + int a = Query(node << 1, l, (l + h) >> 1, ql, qh); + int b = Query((node << 1) + 1, ((l + h) >> 1) + 1, h, ql, qh); + if (a == INF) return b; + if (b == INF) return a; + return A[a] <= A[b] ? a : b; + } +} + +public class Solution { + public int LargestRectangleArea(int[] heights) { + int n = heights.Length; + MinIdx_Segtree st = new MinIdx_Segtree(n, heights); + return GetMaxArea(heights, 0, n - 1, st); + } + + private int GetMaxArea(int[] heights, int l, int r, MinIdx_Segtree st) { + if (l > r) return 0; + if (l == r) return heights[l]; + + int minIdx = st.Query(l, r); + return Math.Max( + Math.Max(GetMaxArea(heights, l, minIdx - 1, st), + GetMaxArea(heights, minIdx + 1, r, st)), + (r - l + 1) * heights[minIdx]); + } +} +``` + +```go +type MinIdxSegtree struct { + n int + INF int + A []int + tree []int +} + +func NewMinIdxSegtree(N int, A []int) *MinIdxSegtree { + st := &MinIdxSegtree{ + n: N, + INF: 1000000000, + A: make([]int, N), + } + copy(st.A, A) + + for (st.n & (st.n - 1)) != 0 { + st.A = append(st.A, st.INF) + st.n++ + } + + st.tree = make([]int, 2*st.n) + st.build() + return st +} + +func (st *MinIdxSegtree) build() { + for i := 0; i < st.n; i++ { + st.tree[st.n+i] = i + } + + for j := st.n - 1; j > 0; j-- { + a := st.tree[j<<1] + b := st.tree[(j<<1)+1] + if st.A[a] <= st.A[b] { + st.tree[j] = a + } else { + st.tree[j] = b + } + } +} + +func (st *MinIdxSegtree) update(i, val int) { + st.A[i] = val + j := (st.n + i) >> 1 + for j >= 1 { + a := st.tree[j<<1] + b := st.tree[(j<<1)+1] + if st.A[a] <= st.A[b] { + st.tree[j] = a + } else { + st.tree[j] = b + } + j >>= 1 + } +} + +func (st *MinIdxSegtree) query(ql, qh int) int { + return st.queryHelper(1, 0, st.n-1, ql, qh) +} + +func (st *MinIdxSegtree) queryHelper(node, l, h, ql, qh int) int { + if ql > h || qh < l { + return st.INF + } + if l >= ql && h <= qh { + return st.tree[node] + } + a := st.queryHelper(node<<1, l, (l+h)>>1, ql, qh) + b := st.queryHelper((node<<1)+1, ((l+h)>>1)+1, h, ql, qh) + if a == st.INF { + return b + } + if b == st.INF { + return a + } + if st.A[a] <= st.A[b] { + return a + } + return b +} + +func getMaxArea(heights []int, l, r int, st *MinIdxSegtree) int { + if l > r { + return 0 + } + if l == r { + return heights[l] + } + minIdx := st.query(l, r) + area1 := getMaxArea(heights, l, minIdx-1, st) + area2 := getMaxArea(heights, minIdx+1, r, st) + area3 := (r - l + 1) * heights[minIdx] + + maxArea := area1 + if area2 > maxArea { + maxArea = area2 + } + if area3 > maxArea { + maxArea = area3 + } + return maxArea +} + +func largestRectangleArea(heights []int) int { + n := len(heights) + st := NewMinIdxSegtree(n, heights) + return getMaxArea(heights, 0, n-1, st) +} +``` + +```kotlin +class MinIdxSegtree(private var n: Int, input: IntArray) { + private val INF = 1000000000 + private val A: IntArray + private val tree: IntArray + + init { + var size = n + while (size and (size - 1) != 0) size++ + A = IntArray(size) { if (it < n) input[it] else INF } + n = size + tree = IntArray(2 * n) + for (i in 0 until n) tree[n + i] = i + for (j in n - 1 downTo 1) { + val left = tree[j shl 1] + val right = tree[j shl 1 or 1] + tree[j] = if (A[left] <= A[right]) left else right + } + } + + fun query(ql: Int, qh: Int): Int = queryHelper(1, 0, n - 1, ql, qh) + + private fun queryHelper(node: Int, l: Int, h: Int, ql: Int, qh: Int): Int { + if (ql > h || qh < l) return INF + if (l >= ql && h <= qh) return tree[node] + + val mid = (l + h) shr 1 + val a = queryHelper(node shl 1, l, mid, ql, qh) + val b = queryHelper((node shl 1) + 1, mid + 1, h, ql, qh) + + return when { + a == INF -> b + b == INF -> a + A[a] <= A[b] -> a + else -> b + } + } +} + +class Solution { + fun largestRectangleArea(heights: IntArray): Int { + val n = heights.size + val st = MinIdxSegtree(n, heights) + + fun getMaxArea(l: Int, r: Int): Int { + if (l > r) return 0 + if (l == r) return heights[l] + val minIdx = st.query(l, r) + val area = (r - l + 1) * heights[minIdx] + val leftArea = getMaxArea(l, minIdx - 1) + val rightArea = getMaxArea(minIdx + 1, r) + return maxOf(area, leftArea, rightArea) + } + + return getMaxArea(0, n - 1) + } +} +``` + +```swift +class MinIdxSegmentTree { + private var n: Int + private var INF = Int(1e9) + private var A: [Int] + private var tree: [Int] + + init(_ N: Int, _ A: [Int]) { + self.n = N + self.A = A + while (self.n & (self.n - 1)) != 0 { + self.A.append(self.INF) + self.n += 1 + } + self.tree = [Int](repeating: 0, count: 2 * self.n) + build() + } + + private func build() { + for i in 0..> 1 + while j >= 1 { + let a = tree[j << 1] + let b = tree[(j << 1) + 1] + tree[j] = (A[a] <= A[b]) ? a : b + j >>= 1 + } + } + + func query(_ ql: Int, _ qh: Int) -> Int { + return _query(1, 0, n - 1, ql, qh) + } + + private func _query(_ node: Int, _ l: Int, _ h: Int, _ ql: Int, _ qh: Int) -> Int { + if ql > h || qh < l { + return INF + } + if l >= ql && h <= qh { + return tree[node] + } + let a = _query(node << 1, l, (l + h) >> 1, ql, qh) + let b = _query((node << 1) + 1, ((l + h) >> 1) + 1, h, ql, qh) + if a == INF { return b } + if b == INF { return a } + return (A[a] <= A[b]) ? a : b + } +} + +class Solution { + func getMaxArea(_ heights: [Int], _ l: Int, _ r: Int, _ st: MinIdxSegmentTree) -> Int { + if l > r { return 0 } + if l == r { return heights[l] } + + let minIdx = st.query(l, r) + return max( + max(getMaxArea(heights, l, minIdx - 1, st), + getMaxArea(heights, minIdx + 1, r, st)), + (r - l + 1) * heights[minIdx] + ) + } + + func largestRectangleArea(_ heights: [Int]) -> Int { + let n = heights.count + let st = MinIdxSegmentTree(n, heights) + return getMaxArea(heights, 0, n - 1, st) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Stack + +::tabs-start + +```python +class Solution: + def largestRectangleArea(self, heights: List[int]) -> int: + n = len(heights) + stack = [] + + leftMost = [-1] * n + for i in range(n): + while stack and heights[stack[-1]] >= heights[i]: + stack.pop() + if stack: + leftMost[i] = stack[-1] + stack.append(i) + + stack = [] + rightMost = [n] * n + for i in range(n - 1, -1, -1): + while stack and heights[stack[-1]] >= heights[i]: + stack.pop() + if stack: + rightMost[i] = stack[-1] + stack.append(i) + + maxArea = 0 + for i in range(n): + leftMost[i] += 1 + rightMost[i] -= 1 + maxArea = max(maxArea, heights[i] * (rightMost[i] - leftMost[i] + 1)) + return maxArea +``` + +```java +public class Solution { + public int largestRectangleArea(int[] heights) { + int n = heights.length; + int[] leftMost = new int[n]; + int[] rightMost = new int[n]; + Stack stack = new Stack<>(); + + for (int i = 0; i < n; i++) { + leftMost[i] = -1; + while (!stack.isEmpty() && heights[stack.peek()] >= heights[i]) { + stack.pop(); + } + if (!stack.isEmpty()) { + leftMost[i] = stack.peek(); + } + stack.push(i); + } + + stack.clear(); + for (int i = n - 1; i >= 0; i--) { + rightMost[i] = n; + while (!stack.isEmpty() && heights[stack.peek()] >= heights[i]) { + stack.pop(); + } + if (!stack.isEmpty()) { + rightMost[i] = stack.peek(); + } + stack.push(i); + } + + int maxArea = 0; + for (int i = 0; i < n; i++) { + leftMost[i] += 1; + rightMost[i] -= 1; + maxArea = Math.max(maxArea, heights[i] * (rightMost[i] - leftMost[i] + 1)); + } + return maxArea; + } +} +``` + +```cpp +class Solution { +public: + int largestRectangleArea(vector& heights) { + int n = heights.size(); + vector leftMost(n, -1); + vector rightMost(n, n); + stack stack; + + for (int i = 0; i < n; i++) { + while (!stack.empty() && heights[stack.top()] >= heights[i]) { + stack.pop(); + } + if (!stack.empty()) { + leftMost[i] = stack.top(); + } + stack.push(i); + } + + while (!stack.empty()) stack.pop(); + + for (int i = n - 1; i >= 0; i--) { + while (!stack.empty() && heights[stack.top()] >= heights[i]) { + stack.pop(); + } + if (!stack.empty()) { + rightMost[i] = stack.top(); + } + stack.push(i); + } + + int maxArea = 0; + for (int i = 0; i < n; i++) { + leftMost[i] += 1; + rightMost[i] -= 1; + maxArea = max(maxArea, heights[i] * (rightMost[i] - leftMost[i] + 1)); + } + + return maxArea; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @return {number} + */ + largestRectangleArea(heights) { + const n = heights.length; + const leftMost = Array(n).fill(-1); + const rightMost = Array(n).fill(n); + const stack = []; + + for (let i = 0; i < n; i++) { + while (stack.length && heights[stack[stack.length - 1]] >= heights[i]) { + stack.pop(); + } + if (stack.length) { + leftMost[i] = stack[stack.length - 1]; + } + stack.push(i); + } + + stack.length = 0; + for (let i = n - 1; i >= 0; i--) { + while (stack.length && heights[stack[stack.length - 1]] >= heights[i]) { + stack.pop(); + } + if (stack.length) { + rightMost[i] = stack[stack.length - 1]; + } + stack.push(i); + } + + let maxArea = 0; + for (let i = 0; i < n; i++) { + leftMost[i] += 1; + rightMost[i] -= 1; + maxArea = Math.max(maxArea, heights[i] * (rightMost[i] - leftMost[i] + 1)); + } + + return maxArea; + } +} +``` + +```csharp +public class Solution { + public int LargestRectangleArea(int[] heights) { + int n = heights.Length; + int[] leftMost = new int[n]; + int[] rightMost = new int[n]; + Stack stack = new Stack(); + + for (int i = 0; i < n; i++) { + leftMost[i] = -1; + while (stack.Count > 0 && heights[stack.Peek()] >= heights[i]) { + stack.Pop(); + } + if (stack.Count > 0) { + leftMost[i] = stack.Peek(); + } + stack.Push(i); + } + + stack.Clear(); + for (int i = n - 1; i >= 0; i--) { + rightMost[i] = n; + while (stack.Count > 0 && heights[stack.Peek()] >= heights[i]) { + stack.Pop(); + } + if (stack.Count > 0) { + rightMost[i] = stack.Peek(); + } + stack.Push(i); + } + + int maxArea = 0; + for (int i = 0; i < n; i++) { + leftMost[i] += 1; + rightMost[i] -= 1; + maxArea = Math.Max(maxArea, heights[i] * (rightMost[i] - leftMost[i] + 1)); + } + + return maxArea; + } +} +``` + +```go +func largestRectangleArea(heights []int) int { + n := len(heights) + stack := make([]int, 0) + + leftMost := make([]int, n) + for i := range leftMost { + leftMost[i] = -1 + } + + for i := 0; i < n; i++ { + for len(stack) > 0 && heights[stack[len(stack)-1]] >= heights[i] { + stack = stack[:len(stack)-1] + } + if len(stack) > 0 { + leftMost[i] = stack[len(stack)-1] + } + stack = append(stack, i) + } + + stack = stack[:0] + rightMost := make([]int, n) + for i := range rightMost { + rightMost[i] = n + } + + for i := n - 1; i >= 0; i-- { + for len(stack) > 0 && heights[stack[len(stack)-1]] >= heights[i] { + stack = stack[:len(stack)-1] + } + if len(stack) > 0 { + rightMost[i] = stack[len(stack)-1] + } + stack = append(stack, i) + } + + maxArea := 0 + for i := 0; i < n; i++ { + leftMost[i]++ + rightMost[i]-- + area := heights[i] * (rightMost[i] - leftMost[i] + 1) + if area > maxArea { + maxArea = area + } + } + + return maxArea +} +``` + +```kotlin +class Solution { + fun largestRectangleArea(heights: IntArray): Int { + val n = heights.size + val stack = ArrayDeque() + + val leftMost = IntArray(n) { -1 } + for (i in 0 until n) { + while (stack.isNotEmpty() && heights[stack.last()] >= heights[i]) { + stack.removeLast() + } + if (stack.isNotEmpty()) { + leftMost[i] = stack.last() + } + stack.addLast(i) + } + + stack.clear() + val rightMost = IntArray(n) { n } + for (i in n - 1 downTo 0) { + while (stack.isNotEmpty() && heights[stack.last()] >= heights[i]) { + stack.removeLast() + } + if (stack.isNotEmpty()) { + rightMost[i] = stack.last() + } + stack.addLast(i) + } + + var maxArea = 0 + for (i in 0 until n) { + leftMost[i]++ + rightMost[i]-- + maxArea = maxOf(maxArea, heights[i] * (rightMost[i] - leftMost[i] + 1)) + } + + return maxArea + } +} +``` + +```swift +class Solution { + func largestRectangleArea(_ heights: [Int]) -> Int { + let n = heights.count + var stack = [Int]() + + var leftMost = [Int](repeating: -1, count: n) + for i in 0..= heights[i] { + stack.removeLast() + } + if !stack.isEmpty { + leftMost[i] = stack.last! + } + stack.append(i) + } + + stack.removeAll() + var rightMost = [Int](repeating: n, count: n) + for i in stride(from: n - 1, through: 0, by: -1) { + while !stack.isEmpty && heights[stack.last!] >= heights[i] { + stack.removeLast() + } + if !stack.isEmpty { + rightMost[i] = stack.last! + } + stack.append(i) + } + + var maxArea = 0 + for i in 0.. int: + maxArea = 0 + stack = [] # pair: (index, height) + + for i, h in enumerate(heights): + start = i + while stack and stack[-1][1] > h: + index, height = stack.pop() + maxArea = max(maxArea, height * (i - index)) + start = index + stack.append((start, h)) + + for i, h in stack: + maxArea = max(maxArea, h * (len(heights) - i)) + return maxArea +``` + +```java +class Solution { + public int largestRectangleArea(int[] heights) { + int maxArea = 0; + Stack stack = new Stack<>(); // pair: (index, height) + + for (int i = 0; i < heights.length; i++) { + int start = i; + while (!stack.isEmpty() && stack.peek()[1] > heights[i]) { + int[] top = stack.pop(); + int index = top[0]; + int height = top[1]; + maxArea = Math.max(maxArea, height * (i - index)); + start = index; + } + stack.push(new int[]{start, heights[i]}); + } + + for (int[] pair : stack) { + int index = pair[0]; + int height = pair[1]; + maxArea = Math.max(maxArea, height * (heights.length - index)); + } + return maxArea; + } +} +``` + +```cpp +class Solution { +public: + int largestRectangleArea(vector& heights) { + int maxArea = 0; + stack> stack; // pair: (index, height) + + for (int i = 0; i < heights.size(); i++) { + int start = i; + while (!stack.empty() && stack.top().second > heights[i]) { + pair top = stack.top(); + int index = top.first; + int height = top.second; + maxArea = max(maxArea, height * (i - index)); + start = index; + stack.pop(); + } + stack.push({ start, heights[i] }); + } + + while (!stack.empty()) { + int index = stack.top().first; + int height = stack.top().second; + maxArea = max(maxArea, height * (static_cast(heights.size()) - index)); + stack.pop(); + } + return maxArea; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @return {number} + */ + largestRectangleArea(heights) { + let maxArea = 0; + const stack = []; // pair: (index, height) + + for (let i = 0; i < heights.length; i++) { + let start = i; + while ( + stack.length > 0 && + stack[stack.length - 1][1] > heights[i] + ) { + const [index, height] = stack.pop(); + maxArea = Math.max(maxArea, height * (i - index)); + start = index; + } + stack.push([start, heights[i]]); + } + + for (const [index, height] of stack) { + maxArea = Math.max(maxArea, height * (heights.length - index)); + } + return maxArea; + } +} +``` + +```csharp +public class Solution { + public int LargestRectangleArea(int[] heights) { + int maxArea = 0; + Stack stack = new Stack(); // pair: (index, height) + + for (int i = 0; i < heights.Length; i++) { + int start = i; + while (stack.Count > 0 && stack.Peek()[1] > heights[i]) { + int[] top = stack.Pop(); + int index = top[0]; + int height = top[1]; + maxArea = Math.Max(maxArea, height * (i - index)); + start = index; + } + stack.Push(new int[] { start, heights[i] }); + } + + foreach (int[] pair in stack) { + int index = pair[0]; + int height = pair[1]; + maxArea = Math.Max(maxArea, height * (heights.Length - index)); + } + return maxArea; + } +} +``` + +```go +func largestRectangleArea(heights []int) int { + maxArea := 0 + stack := make([][2]int, 0) + + for i, h := range heights { + start := i + for len(stack) > 0 && stack[len(stack)-1][1] > h { + index := stack[len(stack)-1][0] + height := stack[len(stack)-1][1] + stack = stack[:len(stack)-1] + area := height * (i - index) + if area > maxArea { + maxArea = area + } + start = index + } + stack = append(stack, [2]int{start, h}) + } + + n := len(heights) + for _, pair := range stack { + area := pair[1] * (n - pair[0]) + if area > maxArea { + maxArea = area + } + } + + return maxArea +} +``` + +```kotlin +class Solution { + fun largestRectangleArea(heights: IntArray): Int { + var maxArea = 0 + val stack = ArrayDeque>() + + heights.forEachIndexed { i, h -> + var start = i + while (stack.isNotEmpty() && stack.last().second > h) { + val (index, height) = stack.removeLast() + maxArea = maxOf(maxArea, height * (i - index)) + start = index + } + stack.addLast(start to h) + } + + val n = heights.size + for ((i, h) in stack) { + maxArea = maxOf(maxArea, h * (n - i)) + } + + return maxArea + } +} +``` + +```swift +class Solution { + func largestRectangleArea(_ heights: [Int]) -> Int { + var maxArea = 0 + var stack = [(Int, Int)]() // Pair: (index, height) + + for (i, h) in heights.enumerated() { + var start = i + while !stack.isEmpty && stack.last!.1 > h { + let (index, height) = stack.removeLast() + maxArea = max(maxArea, height * (i - index)) + start = index + } + stack.append((start, h)) + } + + for (i, h) in stack { + maxArea = max(maxArea, h * (heights.count - i)) + } + + return maxArea + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Stack (Optimal) + +::tabs-start + +```python +class Solution: + def largestRectangleArea(self, heights: List[int]) -> int: + n = len(heights) + maxArea = 0 + stack = [] + + for i in range(n + 1): + while stack and (i == n or heights[stack[-1]] >= heights[i]): + height = heights[stack.pop()] + width = i if not stack else i - stack[-1] - 1 + maxArea = max(maxArea, height * width) + stack.append(i) + return maxArea +``` + +```java +public class Solution { + public int largestRectangleArea(int[] heights) { + int n = heights.length; + int maxArea = 0; + Stack stack = new Stack<>(); + + for (int i = 0; i <= n; i++) { + while (!stack.isEmpty() && + (i == n || heights[stack.peek()] >= heights[i])) { + int height = heights[stack.pop()]; + int width = stack.isEmpty() ? i : i - stack.peek() - 1; + maxArea = Math.max(maxArea, height * width); + } + stack.push(i); + } + return maxArea; + } +} +``` + +```cpp +class Solution { +public: + int largestRectangleArea(vector& heights) { + int n = heights.size(); + int maxArea = 0; + stack stack; + + for (int i = 0; i <= n; i++) { + while (!stack.empty() && + (i == n || heights[stack.top()] >= heights[i])) { + int height = heights[stack.top()]; + stack.pop(); + int width = stack.empty() ? i : i - stack.top() - 1; + maxArea = max(maxArea, height * width); + } + stack.push(i); + } + return maxArea; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @return {number} + */ + largestRectangleArea(heights) { + const n = heights.length; + let maxArea = 0; + const stack = []; + + for (let i = 0; i <= n; i++) { + while (stack.length && + (i === n || heights[stack[stack.length - 1]] >= heights[i])) { + const height = heights[stack.pop()]; + const width = stack.length === 0 ? i : i - stack[stack.length - 1] - 1; + maxArea = Math.max(maxArea, height * width); + } + stack.push(i); + } + return maxArea; + } +} +``` + +```csharp +public class Solution { + public int LargestRectangleArea(int[] heights) { + int n = heights.Length; + int maxArea = 0; + Stack stack = new Stack(); + + for (int i = 0; i <= n; i++) { + while (stack.Count > 0 && + (i == n || heights[stack.Peek()] >= heights[i])) { + int height = heights[stack.Pop()]; + int width = stack.Count == 0 ? i : i - stack.Peek() - 1; + maxArea = Math.Max(maxArea, height * width); + } + stack.Push(i); + } + return maxArea; + } +} +``` + +```go +func largestRectangleArea(heights []int) int { + n := len(heights) + maxArea := 0 + stack := make([]int, 0) + + for i := 0; i <= n; i++ { + for len(stack) > 0 && (i == n || heights[stack[len(stack)-1]] >= heights[i]) { + height := heights[stack[len(stack)-1]] + stack = stack[:len(stack)-1] + + width := i + if len(stack) > 0 { + width = i - stack[len(stack)-1] - 1 + } + + area := height * width + if area > maxArea { + maxArea = area + } + } + if i < n { + stack = append(stack, i) + } + } + + return maxArea +} +``` + +```kotlin +class Solution { + fun largestRectangleArea(heights: IntArray): Int { + val n = heights.size + var maxArea = 0 + val stack = ArrayDeque() + + for (i in 0..n) { + while (stack.isNotEmpty() && (i == n || heights[stack.last()] >= heights[i])) { + val height = heights[stack.removeLast()] + val width = if (stack.isEmpty()) i else i - stack.last() - 1 + maxArea = maxOf(maxArea, height * width) + } + if (i < n) stack.addLast(i) + } + + return maxArea + } +} +``` + +```swift +class Solution { + func largestRectangleArea(_ heights: [Int]) -> Int { + let n = heights.count + var maxArea = 0 + var stack = [Int]() + + for i in 0...n { + while !stack.isEmpty && (i == n || heights[stack.last!] >= heights[i]) { + let height = heights[stack.removeLast()] + let width = stack.isEmpty ? i : i - stack.last! - 1 + maxArea = max(maxArea, height * width) + } + stack.append(i) + } + return maxArea + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/largest-submatrix-with-rearrangements.md b/articles/largest-submatrix-with-rearrangements.md new file mode 100644 index 000000000..bebc4c94e --- /dev/null +++ b/articles/largest-submatrix-with-rearrangements.md @@ -0,0 +1,522 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def largestSubmatrix(self, matrix: List[List[int]]) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + res = 0 + + for start_row in range(ROWS): + ones = deque(list(range(COLS))) + + for r in range(start_row, ROWS): + if not ones: + break + for _ in range(len(ones)): + c = ones.popleft() + if matrix[r][c] == 1: + ones.append(c) + + res = max(res, len(ones) * (r - start_row + 1)) + + return res +``` + +```java +public class Solution { + public int largestSubmatrix(int[][] matrix) { + int ROWS = matrix.length, COLS = matrix[0].length; + int res = 0; + + for (int startRow = 0; startRow < ROWS; startRow++) { + Queue ones = new LinkedList<>(); + for (int c = 0; c < COLS; c++) { + ones.add(c); + } + + for (int r = startRow; r < ROWS; r++) { + if (ones.isEmpty()) break; + + for (int i = ones.size(); i > 0; i--) { + int c = ones.poll(); + if (matrix[r][c] == 1) { + ones.add(c); + } + } + + res = Math.max(res, ones.size() * (r - startRow + 1)); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int largestSubmatrix(vector>& matrix) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + int res = 0; + + for (int startRow = 0; startRow < ROWS; startRow++) { + queue ones; + for (int c = 0; c < COLS; c++) { + ones.push(c); + } + + for (int r = startRow; r < ROWS; r++) { + if (ones.empty()) break; + + for (int i = ones.size(); i > 0; i--) { + int c = ones.front(); ones.pop(); + if (matrix[r][c] == 1) { + ones.push(c); + } + } + + res = max(res, (int)ones.size() * (r - startRow + 1)); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number} + */ + largestSubmatrix(matrix) { + const ROWS = matrix.length, COLS = matrix[0].length; + let res = 0; + + for (let startRow = 0; startRow < ROWS; startRow++) { + const ones = new Queue(); + for (let c = 0; c < COLS; c++) { + ones.push(c); + } + + for (let r = startRow; r < ROWS; r++) { + if (ones.isEmpty()) break; + + for (let i = ones.size(); i > 0; i--) { + let c = ones.pop(); + if (matrix[r][c] === 1) { + ones.push(c); + } + } + + res = Math.max(res, ones.size() * (r - startRow + 1)); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n ^ 2)$ +* Space complexity: $O(n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 2. Greedy + Sorting + +::tabs-start + +```python +class Solution: + def largestSubmatrix(self, matrix: List[List[int]]) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + res = 0 + prev_heights = [0] * COLS + + for r in range(ROWS): + heights = matrix[r][:] + for c in range(COLS): + if heights[c] > 0: + heights[c] += prev_heights[c] + + sorted_heights = sorted(heights, reverse=True) + for i in range(COLS): + res = max(res, (i + 1) * sorted_heights[i]) + + prev_heights = heights + + return res +``` + +```java +public class Solution { + public int largestSubmatrix(int[][] matrix) { + int ROWS = matrix.length, COLS = matrix[0].length; + int res = 0; + int[] prevHeights = new int[COLS]; + + for (int r = 0; r < ROWS; r++) { + int[] heights = Arrays.copyOf(matrix[r], COLS); + int[] sortedHgts = Arrays.copyOf(matrix[r], COLS); + + for (int c = 0; c < COLS; c++) { + if (heights[c] > 0) { + heights[c] += prevHeights[c]; + sortedHgts[c] = heights[c]; + } + } + + Arrays.sort(sortedHgts); + for (int i = COLS - 1; i >= 0; i--) { + res = Math.max(res, (COLS - i) * sortedHgts[i]); + } + + prevHeights = heights; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int largestSubmatrix(vector>& matrix) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + int res = 0; + vector prevHeights(COLS); + + for (int r = 0; r < ROWS; r++) { + vector heights = matrix[r]; + vector sortedHgts = matrix[r]; + + for (int c = 0; c < COLS; c++) { + if (heights[c] > 0) { + heights[c] += prevHeights[c]; + sortedHgts[c] = heights[c]; + } + } + + sort(sortedHgts.begin(), sortedHgts.end(), greater()); + for (int i = 0; i < COLS; i++) { + res = max(res, (i + 1) * sortedHgts[i]); + } + + prevHeights = heights; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number} + */ + largestSubmatrix(matrix) { + const ROWS = matrix.length, COLS = matrix[0].length; + let res = 0; + let prevHeights = new Array(COLS).fill(0); + + for (let r = 0; r < ROWS; r++) { + let heights = [...matrix[r]]; + let sortedHgts = [...matrix[r]]; + + for (let c = 0; c < COLS; c++) { + if (heights[c] > 0) { + heights[c] += prevHeights[c]; + sortedHgts[c] = heights[c]; + } + } + + sortedHgts.sort((a, b) => b - a); + for (let i = 0; i < COLS; i++) { + res = Math.max(res, (i + 1) * sortedHgts[i]); + } + + prevHeights = heights; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n \log n)$ +* Space complexity: $O(n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 3. Greedy + Sorting (Overwriting the Input) + +::tabs-start + +```python +class Solution: + def largestSubmatrix(self, matrix: List[List[int]]) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + res = 0 + + for r in range(1, ROWS): + for c in range(COLS): + if matrix[r][c]: + matrix[r][c] += matrix[r - 1][c] + + for r in range(ROWS): + matrix[r].sort(reverse=True) + for i in range(COLS): + res = max(res, (i + 1) * matrix[r][i]) + + return res +``` + +```java +public class Solution { + public int largestSubmatrix(int[][] matrix) { + int ROWS = matrix.length, COLS = matrix[0].length; + int res = 0; + + for (int r = 1; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (matrix[r][c] > 0) { + matrix[r][c] += matrix[r - 1][c]; + } + } + } + + for (int r = 0; r < ROWS; r++) { + Arrays.sort(matrix[r]); + for (int i = 0; i < COLS; i++) { + res = Math.max(res, (COLS - i) * matrix[r][i]); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int largestSubmatrix(vector>& matrix) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + int res = 0; + + for (int r = 1; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (matrix[r][c] > 0) { + matrix[r][c] += matrix[r - 1][c]; + } + } + } + + for (int r = 0; r < ROWS; r++) { + sort(matrix[r].begin(), matrix[r].end(), greater()); + for (int i = 0; i < COLS; i++) { + res = max(res, (i + 1) * matrix[r][i]); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number} + */ + largestSubmatrix(matrix) { + const ROWS = matrix.length, COLS = matrix[0].length; + let res = 0; + + for (let r = 1; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (matrix[r][c] > 0) { + matrix[r][c] += matrix[r - 1][c]; + } + } + } + + for (let r = 0; r < ROWS; r++) { + matrix[r].sort((a, b) => b - a); + for (let i = 0; i < COLS; i++) { + res = Math.max(res, (i + 1) * matrix[r][i]); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algoirhtm. + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 4. Greedy + +::tabs-start + +```python +class Solution: + def largestSubmatrix(self, matrix: List[List[int]]) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + res = 0 + prevHeights = [] + + for r in range(ROWS): + heights = [] + for c in prevHeights: + if matrix[r][c]: + matrix[r][c] += matrix[r - 1][c] + heights.append(c) + + for c in range(COLS): + if matrix[r][c] == 1: + heights.append(c) + + for i, c in enumerate(heights): + res = max(res, (i + 1) * matrix[r][c]) + + prevHeights = heights + + return res +``` + +```java +public class Solution { + public int largestSubmatrix(int[][] matrix) { + int ROWS = matrix.length, COLS = matrix[0].length; + int res = 0; + List prevHeights = new ArrayList<>(); + + for (int r = 0; r < ROWS; r++) { + List heights = new ArrayList<>(); + + for (int c : prevHeights) { + if (matrix[r][c] == 1) { + matrix[r][c] += matrix[r - 1][c]; + heights.add(c); + } + } + + for (int c = 0; c < COLS; c++) { + if (matrix[r][c] == 1) { + heights.add(c); + } + } + + for (int i = 0; i < heights.size(); i++) { + res = Math.max(res, (i + 1) * matrix[r][heights.get(i)]); + } + + prevHeights = heights; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int largestSubmatrix(vector>& matrix) { + int ROWS = matrix.size(), COLS = matrix[0].size(), res = 0; + vector prevHeights; + + for (int r = 0; r < ROWS; r++) { + vector heights; + + for (int c : prevHeights) { + if (matrix[r][c] == 1) { + matrix[r][c] += matrix[r - 1][c]; + heights.push_back(c); + } + } + + for (int c = 0; c < COLS; c++) { + if (matrix[r][c] == 1) { + heights.push_back(c); + } + } + + for (int i = 0; i < heights.size(); i++) { + res = max(res, (i + 1) * matrix[r][heights[i]]); + } + + prevHeights = heights; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number} + */ + largestSubmatrix(matrix) { + const ROWS = matrix.length, COLS = matrix[0].length; + let res = 0, prevHeights = []; + + for (let r = 0; r < ROWS; r++) { + let heights = []; + + for (let c of prevHeights) { + if (matrix[r][c] === 1) { + matrix[r][c] += matrix[r - 1][c]; + heights.push(c); + } + } + + for (let c = 0; c < COLS; c++) { + if (matrix[r][c] === 1) { + heights.push(c); + } + } + + for (let i = 0; i < heights.length; i++) { + res = Math.max(res, (i + 1) * matrix[r][heights[i]]); + } + + prevHeights = heights; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. \ No newline at end of file diff --git a/articles/largest-substring-between-two-equal-characters.md b/articles/largest-substring-between-two-equal-characters.md new file mode 100644 index 000000000..dc4108a60 --- /dev/null +++ b/articles/largest-substring-between-two-equal-characters.md @@ -0,0 +1,380 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxLengthBetweenEqualCharacters(self, s: str) -> int: + n = len(s) + res = -1 + + for i in range(n): + for j in range(i + 1, n): + if s[i] == s[j]: + res = max(res, j - i - 1) + return res +``` + +```java +public class Solution { + public int maxLengthBetweenEqualCharacters(String s) { + int n = s.length(); + int res = -1; + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if (s.charAt(i) == s.charAt(j)) { + res = Math.max(res, j - i - 1); + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxLengthBetweenEqualCharacters(string s) { + int n = s.size(); + int res = -1; + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if (s[i] == s[j]) { + res = max(res, j - i - 1); + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxLengthBetweenEqualCharacters(s) { + const n = s.length; + let res = -1; + + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + if (s[i] === s[j]) { + res = Math.max(res, j - i - 1); + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. First And Last Index + +::tabs-start + +```python +class Solution: + def maxLengthBetweenEqualCharacters(self, s: str) -> int: + res = -1 + firstIdx = {} + lastIdx = {} + + for i, c in enumerate(s): + if c not in firstIdx: + firstIdx[c] = i + else: + lastIdx[c] = i + + for c in lastIdx: + res = max(res, lastIdx[c] - firstIdx[c] - 1) + + return res +``` + +```java +public class Solution { + public int maxLengthBetweenEqualCharacters(String s) { + Map firstIdx = new HashMap<>(); + Map lastIdx = new HashMap<>(); + int res = -1; + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (!firstIdx.containsKey(c)) { + firstIdx.put(c, i); + } else { + lastIdx.put(c, i); + } + } + + for (char c : lastIdx.keySet()) { + res = Math.max(res, lastIdx.get(c) - firstIdx.get(c) - 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxLengthBetweenEqualCharacters(string s) { + unordered_map firstIdx, lastIdx; + int res = -1; + + for (int i = 0; i < s.size(); i++) { + if (firstIdx.find(s[i]) == firstIdx.end()) { + firstIdx[s[i]] = i; + } else { + lastIdx[s[i]] = i; + } + } + + for (auto& [c, idx] : lastIdx) { + res = max(res, lastIdx[c] - firstIdx[c] - 1); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxLengthBetweenEqualCharacters(s) { + const firstIdx = new Map(); + const lastIdx = new Map(); + let res = -1; + + for (let i = 0; i < s.length; i++) { + if (!firstIdx.has(s[i])) { + firstIdx.set(s[i], i); + } else { + lastIdx.set(s[i], i); + } + } + + for (const [char, idx] of lastIdx) { + res = Math.max(res, lastIdx.get(char) - firstIdx.get(char) - 1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +--- + +## 3. First Index (Hash Map) + +::tabs-start + +```python +class Solution: + def maxLengthBetweenEqualCharacters(self, s: str) -> int: + char_index = {} # char -> first index + res = -1 + + for i, c in enumerate(s): + if c in char_index: + res = max(res, i - char_index[c] - 1) + else: + char_index[c] = i + + return res +``` + +```java +public class Solution { + public int maxLengthBetweenEqualCharacters(String s) { + Map charIndex = new HashMap<>(); + int res = -1; + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (charIndex.containsKey(c)) { + res = Math.max(res, i - charIndex.get(c) - 1); + } else { + charIndex.put(c, i); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxLengthBetweenEqualCharacters(string s) { + unordered_map charIndex; + int res = -1; + + for (int i = 0; i < s.size(); i++) { + if (charIndex.find(s[i]) != charIndex.end()) { + res = max(res, i - charIndex[s[i]] - 1); + } else { + charIndex[s[i]] = i; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxLengthBetweenEqualCharacters(s) { + const charIndex = new Map(); + let res = -1; + + for (let i = 0; i < s.length; i++) { + if (charIndex.has(s[i])) { + res = Math.max(res, i - charIndex.get(s[i]) - 1); + } else { + charIndex.set(s[i], i); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +--- + +## 4. First Index (Array) + +::tabs-start + +```python +class Solution: + def maxLengthBetweenEqualCharacters(self, s: str) -> int: + firstIdx = [-1] * 26 + res = -1 + + for i, c in enumerate(s): + j = ord(c) - ord('a') + if firstIdx[j] != -1: + res = max(res, i - firstIdx[j] - 1) + else: + firstIdx[j] = i + + return res +``` + +```java +public class Solution { + public int maxLengthBetweenEqualCharacters(String s) { + int[] firstIdx = new int[26]; + for (int i = 0; i < 26; i++) { + firstIdx[i] = -1; + } + int res = -1; + + for (int i = 0; i < s.length(); i++) { + int j = s.charAt(i) - 'a'; + if (firstIdx[j] != -1) { + res = Math.max(res, i - firstIdx[j] - 1); + } else { + firstIdx[j] = i; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxLengthBetweenEqualCharacters(string s) { + int firstIdx[26]; + fill(begin(firstIdx), end(firstIdx), -1); + int res = -1; + + for (int i = 0; i < s.size(); i++) { + int j = s[i] - 'a'; + if (firstIdx[j] != -1) { + res = max(res, i - firstIdx[j] - 1); + } else { + firstIdx[j] = i; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxLengthBetweenEqualCharacters(s) { + const firstIdx = Array(26).fill(-1); + let res = -1; + + for (let i = 0; i < s.length; i++) { + const j = s.charCodeAt(i) - 'a'.charCodeAt(0); + if (firstIdx[j] !== -1) { + res = Math.max(res, i - firstIdx[j] - 1); + } else { + firstIdx[j] = i; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. \ No newline at end of file diff --git a/articles/last-stone-weight-ii.md b/articles/last-stone-weight-ii.md new file mode 100644 index 000000000..f902d45d2 --- /dev/null +++ b/articles/last-stone-weight-ii.md @@ -0,0 +1,619 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def lastStoneWeightII(self, stones: List[int]) -> int: + stoneSum = sum(stones) + target = (stoneSum + 1) // 2 + + def dfs(i, total): + if total >= target or i == len(stones): + return abs(total - (stoneSum - total)) + return min(dfs(i + 1, total), dfs(i + 1, total + stones[i])) + + return dfs(0, 0) +``` + +```java +public class Solution { + public int lastStoneWeightII(int[] stones) { + int stoneSum = 0; + for (int stone : stones) { + stoneSum += stone; + } + int target = (stoneSum + 1) / 2; + + return dfs(0, 0, stones, stoneSum, target); + } + + private int dfs(int i, int total, int[] stones, int stoneSum, int target) { + if (total >= target || i == stones.length) { + return Math.abs(total - (stoneSum - total)); + } + return Math.min( + dfs(i + 1, total, stones, stoneSum, target), + dfs(i + 1, total + stones[i], stones, stoneSum, target) + ); + } +} +``` + +```cpp +class Solution { +public: + int lastStoneWeightII(vector& stones) { + int stoneSum = accumulate(stones.begin(), stones.end(), 0); + int target = (stoneSum + 1) / 2; + return dfs(0, 0, stones, stoneSum, target); + } + +private: + int dfs(int i, int total, const vector& stones, int stoneSum, int target) { + if (total >= target || i == stones.size()) { + return abs(total - (stoneSum - total)); + } + return min( + dfs(i + 1, total, stones, stoneSum, target), + dfs(i + 1, total + stones[i], stones, stoneSum, target) + ); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} stones + * @return {number} + */ + lastStoneWeightII(stones) { + const stoneSum = stones.reduce((a, b) => a + b, 0); + const target = Math.ceil(stoneSum / 2); + + const dfs = (i, total) => { + if (total >= target || i === stones.length) { + return Math.abs(total - (stoneSum - total)); + } + return Math.min( + dfs(i + 1, total), + dfs(i + 1, total + stones[i]) + ); + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(min(n, m))$ for recursion stack. + +> Where $n$ is the number of stones and $m$ is the sum of the weights of the stones. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def lastStoneWeightII(self, stones: List[int]) -> int: + stoneSum = sum(stones) + target = (stoneSum + 1) // 2 + dp = {} + + def dfs(i, total): + if total >= target or i == len(stones): + return abs(total - (stoneSum - total)) + if (i, total) in dp: + return dp[(i, total)] + + dp[(i, total)] = min(dfs(i + 1, total), dfs(i + 1, total + stones[i])) + return dp[(i, total)] + + return dfs(0, 0) +``` + +```java +public class Solution { + private int[][] dp; + + public int lastStoneWeightII(int[] stones) { + int stoneSum = 0; + for (int stone : stones) { + stoneSum += stone; + } + int target = (stoneSum + 1) / 2; + dp = new int[stones.length][target + 1]; + for (int i = 0; i < stones.length; i++) { + for (int j = 0; j <= target; j++) { + dp[i][j] = -1; + } + } + + return dfs(0, 0, stones, stoneSum, target); + } + + private int dfs(int i, int total, int[] stones, int stoneSum, int target) { + if (total >= target || i == stones.length) { + return Math.abs(total - (stoneSum - total)); + } + if (dp[i][total] != -1) { + return dp[i][total]; + } + + dp[i][total] = Math.min( + dfs(i + 1, total, stones, stoneSum, target), + dfs(i + 1, total + stones[i], stones, stoneSum, target) + ); + return dp[i][total]; + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + +public: + int lastStoneWeightII(vector& stones) { + int stoneSum = accumulate(stones.begin(), stones.end(), 0); + int target = (stoneSum + 1) / 2; + dp = vector>(stones.size(), vector(target + 1, -1)); + return dfs(0, 0, stones, stoneSum, target); + } + +private: + int dfs(int i, int total, const vector& stones, int stoneSum, int target) { + if (total >= target || i == stones.size()) { + return abs(total - (stoneSum - total)); + } + if (dp[i][total] != -1) { + return dp[i][total]; + } + + dp[i][total] = min( + dfs(i + 1, total, stones, stoneSum, target), + dfs(i + 1, total + stones[i], stones, stoneSum, target) + ); + return dp[i][total]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} stones + * @return {number} + */ + lastStoneWeightII(stones) { + const stoneSum = stones.reduce((a, b) => a + b, 0); + const target = Math.ceil(stoneSum / 2); + const dp = Array.from({ length: stones.length }, () => Array(target + 1).fill(-1)); + + const dfs = (i, total) => { + if (total >= target || i === stones.length) { + return Math.abs(total - (stoneSum - total)); + } + if (dp[i][total] !== -1) { + return dp[i][total]; + } + + dp[i][total] = Math.min( + dfs(i + 1, total), + dfs(i + 1, total + stones[i]) + ); + return dp[i][total]; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of stones and $m$ is the sum of the weights of the stones. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def lastStoneWeightII(self, stones: List[int]) -> int: + stoneSum = sum(stones) + target = stoneSum // 2 + n = len(stones) + + dp = [[0] * (target + 1) for _ in range(n + 1)] + + for i in range(1, n + 1): + for t in range(target + 1): + if t >= stones[i - 1]: + dp[i][t] = max(dp[i - 1][t], dp[i - 1][t - stones[i - 1]] + stones[i - 1]) + else: + dp[i][t] = dp[i - 1][t] + + return stoneSum - 2 * dp[n][target] +``` + +```java +public class Solution { + public int lastStoneWeightII(int[] stones) { + int stoneSum = 0; + for (int stone : stones) { + stoneSum += stone; + } + int target = stoneSum / 2; + int n = stones.length; + + int[][] dp = new int[n + 1][target + 1]; + + for (int i = 1; i <= n; i++) { + for (int t = 0; t <= target; t++) { + if (t >= stones[i - 1]) { + dp[i][t] = Math.max(dp[i - 1][t], dp[i - 1][t - stones[i - 1]] + stones[i - 1]); + } else { + dp[i][t] = dp[i - 1][t]; + } + } + } + + return stoneSum - 2 * dp[n][target]; + } +} +``` + +```cpp +class Solution { +public: + int lastStoneWeightII(vector& stones) { + int stoneSum = accumulate(stones.begin(), stones.end(), 0); + int target = stoneSum / 2; + int n = stones.size(); + + vector> dp(n + 1, vector(target + 1, 0)); + + for (int i = 1; i <= n; i++) { + for (int t = 0; t <= target; t++) { + if (t >= stones[i - 1]) { + dp[i][t] = max(dp[i - 1][t], dp[i - 1][t - stones[i - 1]] + stones[i - 1]); + } else { + dp[i][t] = dp[i - 1][t]; + } + } + } + + return stoneSum - 2 * dp[n][target]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} stones + * @return {number} + */ + lastStoneWeightII(stones) { + const stoneSum = stones.reduce((a, b) => a + b, 0); + const target = Math.floor(stoneSum / 2); + const n = stones.length; + + const dp = Array.from({ length: n + 1 }, () => Array(target + 1).fill(0)); + + for (let i = 1; i <= n; i++) { + for (let t = 0; t <= target; t++) { + if (t >= stones[i - 1]) { + dp[i][t] = Math.max(dp[i - 1][t], dp[i - 1][t - stones[i - 1]] + stones[i - 1]); + } else { + dp[i][t] = dp[i - 1][t]; + } + } + } + + return stoneSum - 2 * dp[n][target]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of stones and $m$ is the sum of the weights of the stones. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def lastStoneWeightII(self, stones: List[int]) -> int: + stoneSum = sum(stones) + target = stoneSum // 2 + dp = [0] * (target + 1) + + for stone in stones: + for t in range(target, stone - 1, -1): + dp[t] = max(dp[t], dp[t - stone] + stone) + + return stoneSum - 2 * dp[target] +``` + +```java +public class Solution { + public int lastStoneWeightII(int[] stones) { + int stoneSum = 0; + for (int stone : stones) { + stoneSum += stone; + } + int target = stoneSum / 2; + int[] dp = new int[target + 1]; + + for (int stone : stones) { + for (int t = target; t >= stone; t--) { + dp[t] = Math.max(dp[t], dp[t - stone] + stone); + } + } + + return stoneSum - 2 * dp[target]; + } +} +``` + +```cpp +class Solution { +public: + int lastStoneWeightII(vector& stones) { + int stoneSum = accumulate(stones.begin(), stones.end(), 0); + int target = stoneSum / 2; + vector dp(target + 1, 0); + + for (int stone : stones) { + for (int t = target; t >= stone; t--) { + dp[t] = max(dp[t], dp[t - stone] + stone); + } + } + + return stoneSum - 2 * dp[target]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} stones + * @return {number} + */ + lastStoneWeightII(stones) { + const stoneSum = stones.reduce((a, b) => a + b, 0); + const target = Math.floor(stoneSum / 2); + const dp = new Array(target + 1).fill(0); + + for (const stone of stones) { + for (let t = target; t >= stone; t--) { + dp[t] = Math.max(dp[t], dp[t - stone] + stone); + } + } + + return stoneSum - 2 * dp[target]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the number of stones and $m$ is the sum of the weights of the stones. + +--- + +## 5. Dynamic Programming (Hash Set) + +::tabs-start + +```python +class Solution: + def lastStoneWeightII(self, stones: List[int]) -> int: + stoneSum = sum(stones) + target = stoneSum // 2 + dp = {0} + + for stone in stones: + new_dp = set(dp) + for val in dp: + if val + stone == target: + return stoneSum - 2 * target + if val + stone < target: + new_dp.add(val + stone) + dp = new_dp + + return stoneSum - 2 * max(dp) +``` + +```java +public class Solution { + public int lastStoneWeightII(int[] stones) { + int stoneSum = 0; + for (int stone : stones) { + stoneSum += stone; + } + int target = stoneSum / 2; + + Set dp = new HashSet<>(); + dp.add(0); + + for (int stone : stones) { + Set newDp = new HashSet<>(dp); + for (int val : dp) { + if (val + stone == target) { + return stoneSum - 2 * target; + } + if (val + stone < target) { + newDp.add(val + stone); + } + } + dp = newDp; + } + + int maxVal = 0; + for (int val : dp) { + maxVal = Math.max(maxVal, val); + } + + return stoneSum - 2 * maxVal; + } +} +``` + +```cpp +class Solution { +public: + int lastStoneWeightII(vector& stones) { + int stoneSum = accumulate(stones.begin(), stones.end(), 0); + int target = stoneSum / 2; + + unordered_set dp = {0}; + + for (int& stone : stones) { + unordered_set newDp(dp); + for (int val : dp) { + if (val + stone == target) { + return stoneSum - 2 * target; + } + if (val + stone < target) { + newDp.insert(val + stone); + } + } + dp = newDp; + } + + int maxVal = 0; + for (int val : dp) { + maxVal = max(maxVal, val); + } + + return stoneSum - 2 * maxVal; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} stones + * @return {number} + */ + lastStoneWeightII(stones) { + const stoneSum = stones.reduce((a, b) => a + b, 0); + const target = Math.floor(stoneSum / 2); + + let dp = new Set([0]); + + for (const stone of stones) { + const newDp = new Set(dp); + for (const val of dp) { + if (val + stone === target) { + return stoneSum - 2 * target; + } + if (val + stone < target) { + newDp.add(val + stone); + } + } + dp = newDp; + } + + return stoneSum - 2 * Math.max(...dp); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the number of stones and $m$ is the sum of the weights of the stones. + +--- + +## 6. Dynamic Programming (Bitset) + +::tabs-start + +```python +class Solution: + def lastStoneWeightII(self, stones: List[int]) -> int: + stoneSum = sum(stones) + target = stoneSum // 2 + dp = 1 + + for stone in stones: + dp |= dp << stone + + for t in range(target, -1, -1): + if dp & (1 << t): + return stoneSum - 2 * t +``` + +```cpp +class Solution { +public: + int lastStoneWeightII(vector& stones) { + int stoneSum = accumulate(stones.begin(), stones.end(), 0); + int target = stoneSum / 2; + bitset<3001> dp; + dp[0] = true; + + for (int stone : stones) { + dp |= (dp << stone); + } + + for (int t = target; t >= 0; --t) { + if (dp[t]) { + return stoneSum - 2 * t; + } + } + return 0; + } +}; +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the number of stones and $m$ is the sum of the weights of the stones. \ No newline at end of file diff --git a/articles/last-stone-weight.md b/articles/last-stone-weight.md new file mode 100644 index 000000000..69ab3403a --- /dev/null +++ b/articles/last-stone-weight.md @@ -0,0 +1,961 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def lastStoneWeight(self, stones: List[int]) -> int: + + while len(stones) > 1: + stones.sort() + cur = stones.pop() - stones.pop() + if cur: + stones.append(cur) + + return stones[0] if stones else 0 +``` + +```java +public class Solution { + public int lastStoneWeight(int[] stones) { + List stoneList = new ArrayList<>(); + for (int stone : stones) { + stoneList.add(stone); + } + + while (stoneList.size() > 1) { + Collections.sort(stoneList); + int cur = stoneList.remove(stoneList.size() - 1) - + stoneList.remove(stoneList.size() - 1); + if (cur != 0) { + stoneList.add(cur); + } + } + + return stoneList.isEmpty() ? 0 : stoneList.get(0); + } +} +``` + +```cpp +class Solution { +public: + int lastStoneWeight(vector& stones) { + while (stones.size() > 1) { + sort(stones.begin(), stones.end()); + int cur = stones.back() - stones[stones.size() - 2]; + stones.pop_back(); + stones.pop_back(); + if (cur != 0) { + stones.push_back(cur); + } + } + return stones.empty() ? 0 : stones[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} stones + * @return {number} + */ + lastStoneWeight(stones) { + while (stones.length > 1) { + stones.sort((a, b) => a - b); + let cur = stones.pop() - stones.pop(); + if (cur) { + stones.push(cur); + } + } + return stones.length ? stones[0] : 0; + } +} +``` + +```csharp +public class Solution { + public int LastStoneWeight(int[] stones) { + List stoneList = new List(stones); + while (stoneList.Count > 1) { + stoneList.Sort(); + int cur = stoneList[stoneList.Count - 1] - stoneList[stoneList.Count - 2]; + stoneList.RemoveAt(stoneList.Count - 1); + stoneList.RemoveAt(stoneList.Count - 1); + if (cur != 0) { + stoneList.Add(cur); + } + } + + return stoneList.Count == 0 ? 0 : stoneList[0]; + } +} +``` + +```go +func lastStoneWeight(stones []int) int { + for len(stones) > 1 { + sort.Ints(stones) + cur := stones[len(stones)-1] - stones[len(stones)-2] + stones = stones[:len(stones)-2] + if cur > 0 { + stones = append(stones, cur) + } + } + if len(stones) == 0 { + return 0 + } + return stones[0] +} +``` + +```kotlin +class Solution { + fun lastStoneWeight(stones: IntArray): Int { + var stonesList = stones.toMutableList() + + while (stonesList.size > 1) { + stonesList.sort() + val cur = stonesList.removeAt(stonesList.size - 1) - stonesList.removeAt(stonesList.size - 1) + if (cur > 0) { + stonesList.add(cur) + } + } + return if (stonesList.isEmpty()) 0 else stonesList[0] + } +} +``` + +```swift +class Solution { + func lastStoneWeight(_ stones: [Int]) -> Int { + var stones = stones + + while stones.count > 1 { + stones.sort() + let cur = stones.removeLast() - stones.removeLast() + if cur > 0 { + stones.append(cur) + } + } + + return stones.isEmpty ? 0 : stones[0] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Binary Search + +::tabs-start + +```python +class Solution: + def lastStoneWeight(self, stones: List[int]) -> int: + stones.sort() + n = len(stones) + + while n > 1: + cur = stones.pop() - stones.pop() + n -= 2 + if cur > 0: + l, r = 0, n + while l < r: + mid = (l + r) // 2 + if stones[mid] < cur: + l = mid + 1 + else: + r = mid + pos = l + n += 1 + stones.append(0) + for i in range(n - 1, pos, -1): + stones[i] = stones[i - 1] + stones[pos] = cur + + return stones[0] if n > 0 else 0 +``` + +```java +public class Solution { + public int lastStoneWeight(int[] stones) { + Arrays.sort(stones); + int n = stones.length; + + while (n > 1) { + int cur = stones[n - 1] - stones[n - 2]; + n -= 2; + if (cur > 0) { + int l = 0, r = n; + while (l < r) { + int mid = (l + r) / 2; + if (stones[mid] < cur) { + l = mid + 1; + } else { + r = mid; + } + } + int pos = l; + n++; + stones = Arrays.copyOf(stones, n); + for (int i = n - 1; i > pos; i--) { + stones[i] = stones[i - 1]; + } + stones[pos] = cur; + } + } + return n > 0 ? stones[0] : 0; + } +} +``` + +```cpp +class Solution { +public: + int lastStoneWeight(vector& stones) { + sort(stones.begin(), stones.end()); + int n = stones.size(); + + while (n > 1) { + int cur = stones[n - 1] - stones[n - 2]; + n -= 2; + if (cur > 0) { + int l = 0, r = n; + while (l < r) { + int mid = (l + r) / 2; + if (stones[mid] < cur) { + l = mid + 1; + } else { + r = mid; + } + } + int pos = l; + stones.push_back(0); + for (int i = n + 1; i > pos; i--) { + stones[i] = stones[i - 1]; + } + stones[pos] = cur; + n++; + } + } + return n > 0 ? stones[0] : 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} stones + * @return {number} + */ + lastStoneWeight(stones) { + stones.sort((a, b) => a - b); + let n = stones.length; + + while (n > 1) { + let cur = stones.pop() - stones.pop(); + n -= 2; + if (cur > 0) { + let l = 0, r = n; + while (l < r) { + let mid = Math.floor((l + r) / 2); + if (stones[mid] < cur) { + l = mid + 1; + } else { + r = mid; + } + } + let pos = l; + n++; + stones.push(0); + for (let i = n - 1; i > pos; i--) { + stones[i] = stones[i - 1]; + } + stones[pos] = cur; + } + } + return n > 0 ? stones[0] : 0; + } +} +``` + +```csharp +public class Solution { + public int LastStoneWeight(int[] stones) { + Array.Sort(stones); + int n = stones.Length; + + while (n > 1) { + int cur = stones[n - 1] - stones[n - 2]; + n -= 2; + if (cur > 0) { + int l = 0, r = n; + while (l < r) { + int mid = (l + r) / 2; + if (stones[mid] < cur) { + l = mid + 1; + } else { + r = mid; + } + } + int pos = l; + Array.Resize(ref stones, n + 1); + for (int i = n; i > pos; i--) { + stones[i] = stones[i - 1]; + } + stones[pos] = cur; + n++; + } + } + return n > 0 ? stones[0] : 0; + } +} +``` + +```go +func lastStoneWeight(stones []int) int { + sort.Ints(stones) + n := len(stones) + + for n > 1 { + cur := stones[n-1] - stones[n-2] + n -= 2 + + if cur > 0 { + pos := sort.Search(n, func(i int) bool { + return stones[i] >= cur + }) + + for i := n; i > pos; i-- { + stones[i] = stones[i-1] + } + stones[pos] = cur + n++ + } + } + + if n > 0 { + return stones[0] + } + return 0 +} +``` + +```kotlin +class Solution { + fun lastStoneWeight(stones: IntArray): Int { + stones.sort() + var n = stones.size + + while (n > 1) { + val cur = stones[n-1] - stones[n-2] + n -= 2 + + if (cur > 0) { + var l = 0 + var r = n + while (l < r) { + val mid = (l + r) / 2 + if (stones[mid] < cur) { + l = mid + 1 + } else { + r = mid + } + } + + for (i in n downTo l+1) { + stones[i] = stones[i-1] + } + stones[l] = cur + n++ + } + } + + return if (n > 0) stones[0] else 0 + } +} +``` + +```swift +class Solution { + func lastStoneWeight(_ stones: [Int]) -> Int { + var stones = stones.sorted() + var n = stones.count + + while n > 1 { + let cur = stones.removeLast() - stones.removeLast() + n -= 2 + if cur > 0 { + var l = 0, r = n + while l < r { + let mid = (l + r) / 2 + if stones[mid] < cur { + l = mid + 1 + } else { + r = mid + } + } + let pos = l + stones.append(0) + n += 1 + for i in stride(from: n - 1, to: pos, by: -1) { + stones[i] = stones[i - 1] + } + stones[pos] = cur + } + } + + return n > 0 ? stones[0] : 0 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Heap + +::tabs-start + +```python +class Solution: + def lastStoneWeight(self, stones: List[int]) -> int: + stones = [-s for s in stones] + heapq.heapify(stones) + + while len(stones) > 1: + first = heapq.heappop(stones) + second = heapq.heappop(stones) + if second > first: + heapq.heappush(stones, first - second) + + stones.append(0) + return abs(stones[0]) +``` + +```java +class Solution { + public int lastStoneWeight(int[] stones) { + PriorityQueue minHeap = new PriorityQueue<>(); + for (int s : stones) { + minHeap.offer(-s); + } + + while (minHeap.size() > 1) { + int first = minHeap.poll(); + int second = minHeap.poll(); + if (second > first) { + minHeap.offer(first - second); + } + } + + minHeap.offer(0); + return Math.abs(minHeap.peek()); + } +} +``` + +```cpp +class Solution { +public: + int lastStoneWeight(vector& stones) { + priority_queue maxHeap; + for (int s : stones) { + maxHeap.push(s); + } + + while (maxHeap.size() > 1) { + int first = maxHeap.top(); + maxHeap.pop(); + int second = maxHeap.top(); + maxHeap.pop(); + if (second < first) { + maxHeap.push(first - second); + } + } + + maxHeap.push(0); + return maxHeap.top(); + } +}; +``` + +```javascript +/** + * const { MaxPriorityQueue } = require('@datastructures-js/priority-queue'); + */ + +class Solution { + /** + * @param {number[]} stones + * @return {number} + */ + lastStoneWeight(stones) { + const maxPQ = new MaxPriorityQueue(); + + for (const stone of stones) { + maxPQ.enqueue(stone); + } + + while (maxPQ.size() > 1) { + const stone1 = maxPQ.dequeue(); + const stone2 = maxPQ.dequeue(); + + if (stone1 !== stone2) { + maxPQ.enqueue(stone1 - stone2); + } + } + + return maxPQ.size() === 1 ? maxPQ.dequeue() : 0; + } +} +``` + +```csharp +public class Solution { + public int LastStoneWeight(int[] stones) { + PriorityQueue minHeap = new PriorityQueue(); + foreach (int s in stones) { + minHeap.Enqueue(-s, -s); + } + + while (minHeap.Count > 1) { + int first = minHeap.Dequeue(); + int second = minHeap.Dequeue(); + if (second > first) { + minHeap.Enqueue(first - second, first - second); + } + } + + minHeap.Enqueue(0, 0); + return Math.Abs(minHeap.Peek()); + } +} +``` + +```go +func lastStoneWeight(stones []int) int { + pq := priorityqueue.NewWith(func(a, b interface{}) int { + return a.(int) - b.(int) + }) + + for _, s := range stones { + pq.Enqueue(-s) + } + + for pq.Size() > 1 { + first, _ := pq.Dequeue() + second, _ := pq.Dequeue() + if second.(int) > first.(int) { + pq.Enqueue(first.(int) - second.(int)) + } + } + + pq.Enqueue(0) + result, _ := pq.Dequeue() + return -result.(int) +} +``` + +```kotlin +class Solution { + fun lastStoneWeight(stones: IntArray): Int { + val minHeap = PriorityQueue() + for (s in stones) { + minHeap.offer(-s) + } + + while (minHeap.size > 1) { + val first = minHeap.poll() + val second = minHeap.poll() + if (second > first) { + minHeap.offer(first - second) + } + } + + minHeap.offer(0) + return Math.abs(minHeap.peek()) + } +} +``` + +```swift +class Solution { + func lastStoneWeight(_ stones: [Int]) -> Int { + var heap = Heap(stones) + while heap.count > 1 { + let first = heap.popMax()! + let second = heap.popMax()! + if first > second { + heap.insert(first - second) + } + } + return heap.isEmpty ? 0 : heap.popMax()! + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Bucket Sort + +::tabs-start + +```python +class Solution: + def lastStoneWeight(self, stones: List[int]) -> int: + + maxStone = max(stones) + bucket = [0] * (maxStone + 1) + for stone in stones: + bucket[stone] += 1 + + first = second = maxStone + while first > 0: + if bucket[first] % 2 == 0: + first -= 1 + continue + + j = min(first - 1, second) + while j > 0 and bucket[j] == 0: + j -= 1 + + if j == 0: + return first + second = j + bucket[first] -= 1 + bucket[second] -= 1 + bucket[first - second] += 1 + first = max(first - second, second) + return first +``` + +```java +public class Solution { + public int lastStoneWeight(int[] stones) { + int maxStone = 0; + for (int stone : stones) { + maxStone = Math.max(maxStone, stone); + } + + int[] bucket = new int[maxStone + 1]; + for (int stone : stones) { + bucket[stone]++; + } + + int first = maxStone, second = maxStone; + while (first > 0) { + if (bucket[first] % 2 == 0) { + first--; + continue; + } + + int j = Math.min(first - 1, second); + while (j > 0 && bucket[j] == 0) { + j--; + } + + if (j == 0) { + return first; + } + + second = j; + bucket[first]--; + bucket[second]--; + bucket[first - second]++; + first = Math.max(first - second, second); + } + + return first; + } +} +``` + +```cpp +class Solution { +public: + int lastStoneWeight(vector& stones) { + int maxStone = 0; + for (int stone : stones) { + maxStone = max(maxStone, stone); + } + + vector bucket(maxStone + 1, 0); + for (int stone : stones) { + bucket[stone]++; + } + + int first = maxStone, second = maxStone; + while (first > 0) { + if (bucket[first] % 2 == 0) { + first--; + continue; + } + + int j = min(first - 1, second); + while (j > 0 && bucket[j] == 0) { + j--; + } + + if (j == 0) { + return first; + } + + second = j; + bucket[first]--; + bucket[second]--; + bucket[first - second]++; + first = max(first - second, second); + } + + return first; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} stones + * @return {number} + */ + lastStoneWeight(stones) { + let maxStone = 0; + for (let stone of stones) { + maxStone = Math.max(maxStone, stone); + } + + let bucket = Array(maxStone + 1).fill(0); + for (let stone of stones) { + bucket[stone]++; + } + + let first = maxStone, second = maxStone; + while (first > 0) { + if (bucket[first] % 2 === 0) { + first--; + continue; + } + + let j = Math.min(first - 1, second); + while (j > 0 && bucket[j] === 0) { + j--; + } + + if (j === 0) { + return first; + } + + second = j; + bucket[first]--; + bucket[second]--; + bucket[first - second]++; + first = Math.max(first - second, second); + } + + return first; + } +} +``` + +```csharp +public class Solution { + public int LastStoneWeight(int[] stones) { + int maxStone = 0; + foreach (int stone in stones) { + maxStone = Math.Max(maxStone, stone); + } + + int[] bucket = new int[maxStone + 1]; + foreach (int stone in stones) { + bucket[stone]++; + } + + int first = maxStone, second = maxStone; + while (first > 0) { + if (bucket[first] % 2 == 0) { + first--; + continue; + } + + int j = Math.Min(first - 1, second); + while (j > 0 && bucket[j] == 0) { + j--; + } + + if (j == 0) { + return first; + } + + second = j; + bucket[first]--; + bucket[second]--; + bucket[first - second]++; + first = Math.Max(first - second, second); + } + + return first; + } +} +``` + +```go +func lastStoneWeight(stones []int) int { + maxStone := 0 + for _, stone := range stones { + if stone > maxStone { + maxStone = stone + } + } + + bucket := make([]int, maxStone+1) + for _, stone := range stones { + bucket[stone]++ + } + + first, second := maxStone, maxStone + for first > 0 { + if bucket[first]%2 == 0 { + first-- + continue + } + + j := min(first-1, second) + for j > 0 && bucket[j] == 0 { + j-- + } + + if j == 0 { + return first + } + second = j + bucket[first]-- + bucket[second]-- + bucket[first-second]++ + first = max(first-second, second) + } + return first +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun lastStoneWeight(stones: IntArray): Int { + val maxStone = stones.max()!! + val bucket = IntArray(maxStone + 1) + for (stone in stones) { + bucket[stone]++ + } + + var first = maxStone + var second = maxStone + while (first > 0) { + if (bucket[first] % 2 == 0) { + first-- + continue + } + + var j = minOf(first - 1, second) + while (j > 0 && bucket[j] == 0) { + j-- + } + + if (j == 0) { + return first + } + second = j + bucket[first]-- + bucket[second]-- + bucket[first - second]++ + first = maxOf(first - second, second) + } + return first + } +} +``` + +```swift +class Solution { + func lastStoneWeight(_ stones: [Int]) -> Int { + guard let maxStone = stones.max() else { return 0 } + + var bucket = [Int](repeating: 0, count: maxStone + 1) + for stone in stones { + bucket[stone] += 1 + } + + var first = maxStone + var second = maxStone + + while first > 0 { + if bucket[first] % 2 == 0 { + first -= 1 + continue + } + + var j = min(first - 1, second) + while j > 0 && bucket[j] == 0 { + j -= 1 + } + + if j == 0 { + return first + } + + second = j + bucket[first] -= 1 + bucket[second] -= 1 + bucket[first - second] += 1 + first = max(first - second, second) + } + + return first + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + w)$ +* Space complexity: $O(w)$ + +> Where $n$ is the length of the $stones$ array and $w$ is the maximum value in the $stones$ array. \ No newline at end of file diff --git a/articles/leaf-similar-trees.md b/articles/leaf-similar-trees.md new file mode 100644 index 000000000..5e92ef739 --- /dev/null +++ b/articles/leaf-similar-trees.md @@ -0,0 +1,504 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def leafSimilar(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> bool: + def dfs(root, leaf): + if not root: + return + if not root.left and not root.right: + leaf.append(root.val) + return + dfs(root.left, leaf) + dfs(root.right, leaf) + + leaf1, leaf2 = [], [] + dfs(root1, leaf1) + dfs(root2, leaf2) + return leaf1 == leaf2 +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean leafSimilar(TreeNode root1, TreeNode root2) { + List leaf1 = new ArrayList<>(); + List leaf2 = new ArrayList<>(); + + dfs(root1, leaf1); + dfs(root2, leaf2); + + return leaf1.equals(leaf2); + } + + private void dfs(TreeNode root, List leaf) { + if (root == null) return; + if (root.left == null && root.right == null) { + leaf.add(root.val); + return; + } + dfs(root.left, leaf); + dfs(root.right, leaf); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool leafSimilar(TreeNode* root1, TreeNode* root2) { + vector leaf1, leaf2; + dfs(root1, leaf1); + dfs(root2, leaf2); + return leaf1 == leaf2; + } + +private: + void dfs(TreeNode* root, vector& leaf) { + if (!root) return; + if (!root->left && !root->right) { + leaf.push_back(root->val); + return; + } + dfs(root->left, leaf); + dfs(root->right, leaf); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {boolean} + */ + leafSimilar(root1, root2) { + const dfs = (root, leaf) => { + if (!root) return; + if (!root.left && !root.right) { + leaf.push(root.val); + return; + } + dfs(root.left, leaf); + dfs(root.right, leaf); + }; + + const leaf1 = []; + const leaf2 = []; + dfs(root1, leaf1); + dfs(root2, leaf2); + + return JSON.stringify(leaf1) === JSON.stringify(leaf2); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ and $m$ are the number of nodes in the given trees. + +--- + +## 2. Depth First Search (Space Optimized) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def leafSimilar(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> bool: + def dfs(root, leaf): + if not root: + return + if not root.left and not root.right: + leaf.append(root.val) + return + dfs(root.left, leaf) + dfs(root.right, leaf) + + leaf1 = [] + dfs(root1, leaf1) + + def dfs1(root, leaf): + if not root: + return True + if not root.left and not root.right: + if not leaf: + return False + return leaf.pop() == root.val + return dfs1(root.right, leaf) and dfs1(root.left, leaf) + + return dfs1(root2, leaf1) and not leaf1 +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean leafSimilar(TreeNode root1, TreeNode root2) { + Stack leaf1 = new Stack<>(); + dfs(root1, leaf1); + return dfs1(root2, leaf1) && leaf1.isEmpty(); + } + + private void dfs(TreeNode root, Stack leaf) { + if (root == null) return; + if (root.left == null && root.right == null) { + leaf.push(root.val); + return; + } + dfs(root.left, leaf); + dfs(root.right, leaf); + } + + private boolean dfs1(TreeNode root, Stack leaf) { + if (root == null) return true; + if (root.left == null && root.right == null) { + if (leaf.isEmpty()) return false; + return leaf.pop() == root.val; + } + return dfs1(root.right, leaf) && dfs1(root.left, leaf); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool leafSimilar(TreeNode* root1, TreeNode* root2) { + vector leaf1; + dfs(root1, leaf1); + return dfs1(root2, leaf1) && leaf1.empty(); + } + +private: + void dfs(TreeNode* root, vector& leaf) { + if (!root) return; + if (!root->left && !root->right) { + leaf.push_back(root->val); + return; + } + dfs(root->left, leaf); + dfs(root->right, leaf); + } + + bool dfs1(TreeNode* root, vector& leaf) { + if (!root) return true; + if (!root->left && !root->right) { + if (leaf.empty() || leaf.back() != root->val) { + return false; + } + leaf.pop_back(); + return true; + } + return dfs1(root->right, leaf) && dfs1(root->left, leaf); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {boolean} + */ + leafSimilar(root1, root2) { + const dfs = (root, leaf) => { + if (!root) return; + if (!root.left && !root.right) { + leaf.push(root.val); + return; + } + dfs(root.left, leaf); + dfs(root.right, leaf); + }; + + const dfs1 = (root, leaf) => { + if (!root) return true; + if (!root.left && !root.right) { + if (!leaf.length) return false; + return leaf.pop() === root.val; + } + return dfs1(root.right, leaf) && dfs1(root.left, leaf); + }; + + const leaf1 = []; + dfs(root1, leaf1); + return dfs1(root2, leaf1) && leaf1.length === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ and $m$ are the number of nodes in the given trees. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def leafSimilar(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> bool: + def getPathLeaf(stack): + while stack: + node = stack.pop() + if node.right: + stack.append(node.right) + if node.left: + stack.append(node.left) + if not node.left and not node.right: + return node.val + + stack1, stack2 = [root1], [root2] + while stack1 and stack2: + if getPathLeaf(stack1) != getPathLeaf(stack2): + return False + + return not stack1 and not stack2 +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean leafSimilar(TreeNode root1, TreeNode root2) { + Stack stack1 = new Stack<>(); + Stack stack2 = new Stack<>(); + stack1.push(root1); + stack2.push(root2); + + while (!stack1.isEmpty() && !stack2.isEmpty()) { + if (getPathLeaf(stack1) != getPathLeaf(stack2)) { + return false; + } + } + return stack1.isEmpty() && stack2.isEmpty(); + } + + private int getPathLeaf(Stack stack) { + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + if (node.right != null) { + stack.push(node.right); + } + if (node.left != null) { + stack.push(node.left); + } + if (node.left == null && node.right == null) { + return node.val; + } + } + return -1; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool leafSimilar(TreeNode* root1, TreeNode* root2) { + stack stack1, stack2; + stack1.push(root1); + stack2.push(root2); + + while (!stack1.empty() && !stack2.empty()) { + if (getPathLeaf(stack1) != getPathLeaf(stack2)) { + return false; + } + } + return stack1.empty() && stack2.empty(); + } + +private: + int getPathLeaf(stack& stack) { + while (!stack.empty()) { + TreeNode* node = stack.top(); + stack.pop(); + if (node->right) { + stack.push(node->right); + } + if (node->left) { + stack.push(node->left); + } + if (!node->left && !node->right) { + return node->val; + } + } + return -1; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {boolean} + */ + leafSimilar(root1, root2) { + const getPathLeaf = (stack) => { + while (stack.length) { + const node = stack.pop(); + if (node.right) stack.push(node.right); + if (node.left) stack.push(node.left); + if (!node.left && !node.right) return node.val; + } + }; + + const stack1 = [root1], stack2 = [root2]; + while (stack1.length && stack2.length) { + if (getPathLeaf(stack1) !== getPathLeaf(stack2)) { + return false; + } + } + return stack1.length === 0 && stack2.length === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ and $m$ are the number of nodes in the given trees. \ No newline at end of file diff --git a/articles/least-number-of-unique-integers-after-k-removals.md b/articles/least-number-of-unique-integers-after-k-removals.md new file mode 100644 index 000000000..ddffbfb03 --- /dev/null +++ b/articles/least-number-of-unique-integers-after-k-removals.md @@ -0,0 +1,343 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def findLeastNumOfUniqueInts(self, arr: List[int], k: int) -> int: + freq = sorted(Counter(arr).values()) + n = len(freq) + for i in range(n): + if k >= freq[i]: + k -= freq[i] + else: + return n - i + return 0 +``` + +```java +public class Solution { + public int findLeastNumOfUniqueInts(int[] arr, int k) { + Map freqMap = new HashMap<>(); + for (int num : arr) { + freqMap.put(num, freqMap.getOrDefault(num, 0) + 1); + } + + List freq = new ArrayList<>(freqMap.values()); + Collections.sort(freq); + + int n = freq.size(); + for (int i = 0; i < n; i++) { + if (k >= freq.get(i)) { + k -= freq.get(i); + } else { + return n - i; + } + } + return 0; + } +} +``` + +```cpp +class Solution { +public: + int findLeastNumOfUniqueInts(vector& arr, int k) { + unordered_map freqMap; + for (int num : arr) { + freqMap[num]++; + } + + vector freq; + for (auto& [_, count] : freqMap) { + freq.push_back(count); + } + + sort(freq.begin(), freq.end()); + + int n = freq.size(); + for (int i = 0; i < n; i++) { + if (k >= freq[i]) { + k -= freq[i]; + } else { + return n - i; + } + } + return 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @return {number} + */ + findLeastNumOfUniqueInts(arr, k) { + let freqMap = new Map(); + for (let num of arr) { + freqMap.set(num, (freqMap.get(num) || 0) + 1); + } + + let freq = Array.from(freqMap.values()).sort((a, b) => a - b); + + let n = freq.length; + for (let i = 0; i < n; i++) { + if (k >= freq[i]) { + k -= freq[i]; + } else { + return n - i; + } + } + return 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Min-Heap + +::tabs-start + +```python +class Solution: + def findLeastNumOfUniqueInts(self, arr: List[int], k: int) -> int: + freq = Counter(arr) + heap = list(freq.values()) + heapq.heapify(heap) + + res = len(heap) + while k > 0 and heap: + f = heapq.heappop(heap) + if k >= f: + k -= f + res -= 1 + return res +``` + +```java +public class Solution { + public int findLeastNumOfUniqueInts(int[] arr, int k) { + Map freqMap = new HashMap<>(); + for (int num : arr) { + freqMap.put(num, freqMap.getOrDefault(num, 0) + 1); + } + + PriorityQueue minHeap = new PriorityQueue<>(freqMap.values()); + + int res = minHeap.size(); + while (k > 0 && !minHeap.isEmpty()) { + int f = minHeap.poll(); + if (k >= f) { + k -= f; + res--; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int findLeastNumOfUniqueInts(vector& arr, int k) { + unordered_map freqMap; + for (int num : arr) { + freqMap[num]++; + } + + priority_queue, greater> minHeap; + for (auto& [_, count] : freqMap) { + minHeap.push(count); + } + + int res = minHeap.size(); + while (k > 0 && !minHeap.empty()) { + int f = minHeap.top(); + minHeap.pop(); + if (k >= f) { + k -= f; + res--; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @return {number} + */ + findLeastNumOfUniqueInts(arr, k) { + let freqMap = new Map(); + for (let num of arr) { + freqMap.set(num, (freqMap.get(num) || 0) + 1); + } + + const minHeap = MinPriorityQueue.fromArray([...freqMap.values()]); + + let res = minHeap.size(); + while (k > 0 && !minHeap.isEmpty()) { + let f = minHeap.pop(); + if (k >= f) { + k -= f; + res--; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Bucket Sort + +::tabs-start + +```python +class Solution: + def findLeastNumOfUniqueInts(self, arr: List[int], k: int) -> int: + freq = Counter(arr) + freq_list = [0] * (len(arr) + 1) + + for n, f in freq.items(): + freq_list[f] += 1 + + res = len(freq) + for f in range(1, len(freq_list)): + remove = freq_list[f] + if k >= f * remove: + k -= f * remove + res -= remove + else: + remove = k // f + res -= remove + break + return res +``` + +```java +public class Solution { + public int findLeastNumOfUniqueInts(int[] arr, int k) { + Map freqMap = new HashMap<>(); + for (int num : arr) { + freqMap.put(num, freqMap.getOrDefault(num, 0) + 1); + } + + int[] freqList = new int[arr.length + 1]; + for (int f : freqMap.values()) { + freqList[f]++; + } + + int res = freqMap.size(); + for (int f = 1; f < freqList.length; f++) { + int remove = freqList[f]; + if (k >= f * remove) { + k -= f * remove; + res -= remove; + } else { + remove = k / f; + res -= remove; + break; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int findLeastNumOfUniqueInts(vector& arr, int k) { + unordered_map freqMap; + for (int num : arr) { + freqMap[num]++; + } + + vector freqList(arr.size() + 1, 0); + for (auto& [_, f] : freqMap) { + freqList[f]++; + } + + int res = freqMap.size(); + for (int f = 1; f < freqList.size(); f++) { + int remove = freqList[f]; + if (k >= f * remove) { + k -= f * remove; + res -= remove; + } else { + remove = k / f; + res -= remove; + break; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @return {number} + */ + findLeastNumOfUniqueInts(arr, k) { + let freqMap = new Map(); + for (let num of arr) { + freqMap.set(num, (freqMap.get(num) || 0) + 1); + } + + let freqList = new Array(arr.length + 1).fill(0); + for (let f of freqMap.values()) { + freqList[f]++; + } + + let res = freqMap.size; + for (let f = 1; f < freqList.length; f++) { + let remove = freqList[f]; + if (k >= f * remove) { + k -= f * remove; + res -= remove; + } else { + remove = Math.floor(k / f); + res -= remove; + break; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/lemonade-change.md b/articles/lemonade-change.md new file mode 100644 index 000000000..662b4dc91 --- /dev/null +++ b/articles/lemonade-change.md @@ -0,0 +1,241 @@ +## 1. Iteration - I + +::tabs-start + +```python +class Solution: + def lemonadeChange(self, bills: List[int]) -> bool: + five, ten = 0, 0 + for b in bills: + if b == 5: + five += 1 + elif b == 10: + ten += 1 + if five > 0: + five -= 1 + else: + return False + else: + change = b - 5 + if change == 15 and five > 0 and ten > 0: + five -= 1 + ten -= 1 + elif change == 15 and five >= 3: + five -= 3 + else: + return False + return True +``` + +```java +public class Solution { + public boolean lemonadeChange(int[] bills) { + int five = 0, ten = 0; + for (int b : bills) { + if (b == 5) { + five++; + } else if (b == 10) { + ten++; + if (five > 0) { + five--; + } else { + return false; + } + } else { + if (five > 0 && ten > 0) { + five--; + ten--; + } else if (five >= 3) { + five -= 3; + } else { + return false; + } + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool lemonadeChange(vector& bills) { + int five = 0, ten = 0; + for (int b : bills) { + if (b == 5) { + five++; + } else if (b == 10) { + ten++; + if (five > 0) { + five--; + } else { + return false; + } + } else { + if (five > 0 && ten > 0) { + five--; + ten--; + } else if (five >= 3) { + five -= 3; + } else { + return false; + } + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} bills + * @return {boolean} + */ + lemonadeChange(bills) { + let five = 0, ten = 0; + for (let b of bills) { + if (b === 5) { + five++; + } else if (b === 10) { + ten++; + if (five > 0) { + five--; + } else { + return false; + } + } else { + if (five > 0 && ten > 0) { + five--; + ten--; + } else if (five >= 3) { + five -= 3; + } else { + return false; + } + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Iteration - II + +::tabs-start + +```python +class Solution: + def lemonadeChange(self, bills: List[int]) -> bool: + five, ten = 0, 0 + for b in bills: + if b == 5: + five += 1 + elif b == 10: + five, ten = five - 1, ten + 1 + elif ten > 0: + five, ten = five - 1, ten - 1 + else: + five -= 3 + if five < 0: + return False + return True +``` + +```java +public class Solution { + public boolean lemonadeChange(int[] bills) { + int five = 0, ten = 0; + for (int b : bills) { + if (b == 5) { + five++; + } else if (b == 10) { + five--; + ten++; + } else if (ten > 0) { + five--; + ten--; + } else { + five -= 3; + } + if (five < 0) { + return false; + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool lemonadeChange(vector& bills) { + int five = 0, ten = 0; + for (int b : bills) { + if (b == 5) { + five++; + } else if (b == 10) { + five--; + ten++; + } else if (ten > 0) { + five--; + ten--; + } else { + five -= 3; + } + if (five < 0) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} bills + * @return {boolean} + */ + lemonadeChange(bills) { + let five = 0, ten = 0; + for (let b of bills) { + if (b === 5) { + five++; + } else if (b === 10) { + five--; + ten++; + } else if (ten > 0) { + five--; + ten--; + } else { + five -= 3; + } + if (five < 0) { + return false; + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/length-of-last-word.md b/articles/length-of-last-word.md new file mode 100644 index 000000000..baa94e557 --- /dev/null +++ b/articles/length-of-last-word.md @@ -0,0 +1,230 @@ +## 1. Iteration - I + +::tabs-start + +```python +class Solution: + def lengthOfLastWord(self, s: str) -> int: + length = i = 0 + while i < len(s): + if s[i] == ' ': + while i < len(s) and s[i] == ' ': + i += 1 + if i == len(s): + return length + length = 0 + else: + length += 1 + i += 1 + return length +``` + +```java +public class Solution { + public int lengthOfLastWord(String s) { + int length = 0, i = 0; + while (i < s.length()) { + if (s.charAt(i) == ' ') { + while (i < s.length() && s.charAt(i) == ' ') { + i++; + } + if (i == s.length()) { + return length; + } + length = 0; + } else { + length++; + i++; + } + } + return length; + } +} +``` + +```cpp +class Solution { +public: + int lengthOfLastWord(string s) { + int length = 0, i = 0; + while (i < s.length()) { + if (s[i] == ' ') { + while (i < s.length() && s[i] == ' ') { + i++; + } + if (i == s.length()) { + return length; + } + length = 0; + } else { + length++; + i++; + } + } + return length; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + lengthOfLastWord(s) { + let length = 0, i = 0; + while (i < s.length) { + if (s[i] === ' ') { + while (i < s.length && s[i] === ' ') { + i++; + } + if (i === s.length) { + return length; + } + length = 0; + } else { + length++; + i++; + } + } + return length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Iteration - II + +::tabs-start + +```python +class Solution: + def lengthOfLastWord(self, s: str) -> int: + i, length = len(s) - 1, 0 + while s[i] == ' ': + i -= 1 + while i >= 0 and s[i] != ' ': + i -= 1 + length += 1 + return length +``` + +```java +public class Solution { + public int lengthOfLastWord(String s) { + int n = s.length(); + int i = n - 1, length = 0; + while (s.charAt(i) == ' ') { + i--; + } + while (i >= 0 && s.charAt(i) != ' ') { + i--; + length++; + } + return length; + } +} +``` + +```cpp +class Solution { +public: + int lengthOfLastWord(string s) { + int n = s.length(); + int i = n - 1, length = 0; + while (s[i] == ' ') i--; + while (i >= 0 && s[i] != ' ') { + i--; + length++; + } + return length; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + lengthOfLastWord(s) { + let n = s.length; + let i = n - 1, length = 0; + while (s.charAt(i) === ' ') { + i--; + } + while (i >= 0 && s.charAt(i) !== ' ') { + i--; + length++; + } + return length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Built-In Function + +::tabs-start + +```python +class Solution: + def lengthOfLastWord(self, s: str) -> int: + return len(s.split().pop()) +``` + +```java +public class Solution { + public int lengthOfLastWord(String s) { + s = s.trim(); + return s.length() - s.lastIndexOf(" ") - 1; + } +} +``` + +```cpp +class Solution { +public: + int lengthOfLastWord(string s) { + s.erase(s.find_last_not_of(' ') + 1); + return s.substr(s.find_last_of(' ') + 1).length(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + lengthOfLastWord(s) { + return s.trim().split(' ').pop().length + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/length-of-longest-subarray-with-at-most-k-frequency.md b/articles/length-of-longest-subarray-with-at-most-k-frequency.md new file mode 100644 index 000000000..5726525c2 --- /dev/null +++ b/articles/length-of-longest-subarray-with-at-most-k-frequency.md @@ -0,0 +1,278 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxSubarrayLength(self, nums: List[int], k: int) -> int: + n, res = len(nums), 0 + + for i in range(n): + count = defaultdict(int) + for j in range(i, n): + count[nums[j]] += 1 + if count[nums[j]] > k: + break + res = max(res, j - i + 1) + + return res +``` + +```java +public class Solution { + public int maxSubarrayLength(int[] nums, int k) { + int n = nums.length, res = 0; + + for (int i = 0; i < n; i++) { + Map count = new HashMap<>(); + for (int j = i; j < n; j++) { + count.put(nums[j], count.getOrDefault(nums[j], 0) + 1); + if (count.get(nums[j]) > k) { + break; + } + res = Math.max(res, j - i + 1); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxSubarrayLength(vector& nums, int k) { + int n = nums.size(), res = 0; + + for (int i = 0; i < n; i++) { + unordered_map count; + for (int j = i; j < n; j++) { + count[nums[j]]++; + if (count[nums[j]] > k) { + break; + } + res = max(res, j - i + 1); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maxSubarrayLength(nums, k) { + let n = nums.length, res = 0; + + for (let i = 0; i < n; i++) { + let count = new Map(); + for (let j = i; j < n; j++) { + count.set(nums[j], (count.get(nums[j]) || 0) + 1); + if (count.get(nums[j]) > k) { + break; + } + res = Math.max(res, j - i + 1); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Sliding Window + +::tabs-start + +```python +class Solution: + def maxSubarrayLength(self, nums: List[int], k: int) -> int: + res = 0 + count = defaultdict(int) + l = 0 + for r in range(len(nums)): + count[nums[r]] += 1 + while count[nums[r]] > k: + count[nums[l]] -= 1 + l += 1 + res = max(res, r - l + 1) + return res +``` + +```java +public class Solution { + public int maxSubarrayLength(int[] nums, int k) { + int res = 0; + Map count = new HashMap<>(); + int l = 0; + + for (int r = 0; r < nums.length; r++) { + count.put(nums[r], count.getOrDefault(nums[r], 0) + 1); + while (count.get(nums[r]) > k) { + count.put(nums[l], count.get(nums[l]) - 1); + l++; + } + res = Math.max(res, r - l + 1); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxSubarrayLength(vector& nums, int k) { + int res = 0; + unordered_map count; + int l = 0; + + for (int r = 0; r < nums.size(); r++) { + count[nums[r]]++; + while (count[nums[r]] > k) { + count[nums[l]]--; + l++; + } + res = max(res, r - l + 1); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maxSubarrayLength(nums, k) { + let res = 0; + let count = new Map(); + let l = 0; + + for (let r = 0; r < nums.length; r++) { + count.set(nums[r], (count.get(nums[r]) || 0) + 1); + while (count.get(nums[r]) > k) { + count.set(nums[l], count.get(nums[l]) - 1); + l++; + } + res = Math.max(res, r - l + 1); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sliding Window (Optimal) + +::tabs-start + +```python +class Solution: + def maxSubarrayLength(self, nums: List[int], k: int) -> int: + count = defaultdict(int) + l = res = 0 + cnt = 0 # count of numbers with freq > k + for r in range(len(nums)): + count[nums[r]] += 1 + cnt += count[nums[r]] > k + if cnt > 0: + cnt -= count[nums[l]] > k + count[nums[l]] -= 1 + l += 1 + return len(nums) - l +``` + +```java +public class Solution { + public int maxSubarrayLength(int[] nums, int k) { + HashMap count = new HashMap<>(); + int l = 0, cnt = 0; // count of numbers with freq > k + for (int r = 0; r < nums.length; r++) { + count.put(nums[r], count.getOrDefault(nums[r], 0) + 1); + if (count.get(nums[r]) > k) cnt++; + if (cnt > 0) { + if (count.get(nums[l]) > k) cnt--; + count.put(nums[l], count.get(nums[l]) - 1); + l++; + } + } + return nums.length - l; + } +} +``` + +```cpp +class Solution { +public: + int maxSubarrayLength(vector& nums, int k) { + unordered_map count; + int l = 0, cnt = 0; // count of numbers with freq > k + for (int r = 0; r < nums.size(); r++) { + count[nums[r]]++; + cnt += count[nums[r]] > k; + if (cnt > 0) { + cnt -= count[nums[l]] > k; + count[nums[l]]--; + l++; + } + } + return nums.size() - l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maxSubarrayLength(nums, k) { + let count = new Map(); + let l = 0, cnt = 0; // count of numbers with freq > k + for (let r = 0; r < nums.length; r++) { + count.set(nums[r], (count.get(nums[r]) || 0) + 1); + if (count.get(nums[r]) > k) cnt++; + if (cnt > 0) { + if (count.get(nums[l]) > k) cnt--; + count.set(nums[l], count.get(nums[l]) - 1); + l++; + } + } + return nums.length - l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/level-order-traversal-of-binary-tree.md b/articles/level-order-traversal-of-binary-tree.md new file mode 100644 index 000000000..28df7fc5f --- /dev/null +++ b/articles/level-order-traversal-of-binary-tree.md @@ -0,0 +1,637 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + res = [] + + def dfs(node, depth): + if not node: + return None + if len(res) == depth: + res.append([]) + + res[depth].append(node.val) + dfs(node.left, depth + 1) + dfs(node.right, depth + 1) + + dfs(root, 0) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + List> res = new ArrayList<>(); + + public List> levelOrder(TreeNode root) { + dfs(root, 0); + return res; + } + + private void dfs(TreeNode node, int depth) { + if (node == null) { + return; + } + + if (res.size() == depth) { + res.add(new ArrayList<>()); + } + + res.get(depth).add(node.val); + dfs(node.left, depth + 1); + dfs(node.right, depth + 1); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + vector> res; + + vector> levelOrder(TreeNode* root) { + dfs(root, 0); + return res; + } + + void dfs(TreeNode* node, int depth) { + if (!node) return; + + if (res.size() == depth) { + res.push_back(vector()); + } + + res[depth].push_back(node->val); + dfs(node->left, depth + 1); + dfs(node->right, depth + 1); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @return {number[][]} + */ + levelOrder(root) { + let res = []; + + /** + * @param {TreeNode} node + * @param {number} depth + */ + function dfs(node, depth) { + if (!node) return; + + if (res.length === depth) { + res.push([]); + } + + res[depth].push(node.val); + dfs(node.left, depth + 1); + dfs(node.right, depth + 1); + } + + dfs(root, 0); + return res; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + List> res = new List>(); + + public List> LevelOrder(TreeNode root) { + dfs(root, 0); + return res; + } + + private void dfs(TreeNode node, int depth) { + if (node == null) { + return; + } + + if (res.Count == depth) { + res.Add(new List()); + } + + res[depth].Add(node.val); + dfs(node.left, depth + 1); + dfs(node.right, depth + 1); + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func levelOrder(root *TreeNode) [][]int { + res := [][]int{} + + var dfs func(node *TreeNode, depth int) + dfs = func(node *TreeNode, depth int) { + if node == nil { + return + } + + if len(res) == depth { + res = append(res, []int{}) + } + + res[depth] = append(res[depth], node.Val) + dfs(node.Left, depth+1) + dfs(node.Right, depth+1) + } + + dfs(root, 0) + return res +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun levelOrder(root: TreeNode?): List> { + val res = mutableListOf>() + + fun dfs(node: TreeNode?, depth: Int) { + if (node == null) return + + if (res.size == depth) { + res.add(mutableListOf()) + } + + res[depth].add(node.`val`) + dfs(node.left, depth + 1) + dfs(node.right, depth + 1) + } + + dfs(root, 0) + return res + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func levelOrder(_ root: TreeNode?) -> [[Int]] { + var res = [[Int]]() + + func dfs(_ node: TreeNode?, _ depth: Int) { + guard let node = node else { return } + + if res.count == depth { + res.append([]) + } + + res[depth].append(node.val) + dfs(node.left, depth + 1) + dfs(node.right, depth + 1) + } + + dfs(root, 0) + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + res = [] + + q = collections.deque() + q.append(root) + + while q: + qLen = len(q) + level = [] + for i in range(qLen): + node = q.popleft() + if node: + level.append(node.val) + q.append(node.left) + q.append(node.right) + if level: + res.append(level) + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + public List> levelOrder(TreeNode root) { + List> res = new ArrayList<>(); + + Queue q = new LinkedList<>(); + q.add(root); + + while (!q.isEmpty()) { + List level = new ArrayList<>(); + + for (int i = q.size(); i > 0; i--) { + TreeNode node = q.poll(); + if (node != null) { + level.add(node.val); + q.add(node.left); + q.add(node.right); + } + } + if (level.size() > 0) { + res.add(level); + } + } + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + vector> levelOrder(TreeNode* root) { + vector> res; + if (!root) return res; + + queue q; + q.push(root); + + while (!q.empty()) { + vector level; + int size = q.size(); + + for (int i = q.size(); i > 0; i--) { + TreeNode* node = q.front(); + q.pop(); + if (node) { + level.push_back(node->val); + q.push(node->left); + q.push(node->right); + } + } + if (!level.empty()) { + res.push_back(level); + } + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @return {number[][]} + */ + levelOrder(root) { + let res = []; + if (!root) return res; + + const q = new Queue(); + q.push(root); + + while (!q.isEmpty()) { + let level = []; + + for (let i = q.size(); i > 0; i--) { + let node = q.pop(); + if (node !== null) { + level.push(node.val); + q.push(node.left); + q.push(node.right); + } + } + if (level.length > 0) { + res.push(level); + } + } + return res; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public List> LevelOrder(TreeNode root) { + List> res = new List>(); + if (root == null) return res; + + Queue q = new Queue(); + q.Enqueue(root); + + while (q.Count > 0) { + List level = new List(); + + for (int i = q.Count; i > 0; i--) { + TreeNode node = q.Dequeue(); + if (node != null) { + level.Add(node.val); + q.Enqueue(node.left); + q.Enqueue(node.right); + } + } + if (level.Count > 0) { + res.Add(level); + } + } + return res; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func levelOrder(root *TreeNode) [][]int { + res := [][]int{} + if root == nil { + return res + } + + q := []*TreeNode{root} + + for len(q) > 0 { + qLen := len(q) + level := []int{} + + for i := 0; i < qLen; i++ { + node := q[0] + q = q[1:] + level = append(level, node.Val) + + if node.Left != nil { + q = append(q, node.Left) + } + if node.Right != nil { + q = append(q, node.Right) + } + } + + res = append(res, level) + } + + return res +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun levelOrder(root: TreeNode?): List> { + val res = mutableListOf>() + if (root == null) return res + + val q = ArrayDeque() + q.add(root) + + while (q.isNotEmpty()) { + val level = mutableListOf() + val qLen = q.size + + for (i in 0 until qLen) { + val node = q.removeFirst() + level.add(node.`val`) + + node.left?.let { q.add(it) } + node.right?.let { q.add(it) } + } + + res.add(level) + } + + return res + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func levelOrder(_ root: TreeNode?) -> [[Int]] { + var res = [[Int]]() + var q = Deque() + q.append(root) + + while !q.isEmpty { + let qLen = q.count + var level = [Int]() + + for _ in 0.. [value, frequency, timestamp] + self.timestamp = 0 + + def get(self, key: int) -> int: + if key not in self.cache: + return -1 + + self.cache[key][1] += 1 + self.timestamp += 1 + self.cache[key][2] = self.timestamp + return self.cache[key][0] + + def put(self, key: int, value: int) -> None: + if self.capacity <= 0: + return + + self.timestamp += 1 + if key in self.cache: + self.cache[key][0] = value + self.cache[key][1] += 1 + self.cache[key][2] = self.timestamp + return + + if len(self.cache) >= self.capacity: + min_freq = float('inf') + min_timestamp = float('inf') + lfu_key = None + + for k, (_, freq, ts) in self.cache.items(): + if freq < min_freq or (freq == min_freq and ts < min_timestamp): + min_freq = freq + min_timestamp = ts + lfu_key = k + if lfu_key is not None: + del self.cache[lfu_key] + + self.cache[key] = [value, 1, self.timestamp] +``` + +```java +class Node { + int value, freq, timestamp; + + Node(int value, int freq, int timestamp) { + this.value = value; + this.freq = freq; + this.timestamp = timestamp; + } +} + +public class LFUCache { + + private int capacity, timestamp; + private Map cache; + + public LFUCache(int capacity) { + this.capacity = capacity; + this.timestamp = 0; + this.cache = new HashMap<>(); + } + + public int get(int key) { + if (!cache.containsKey(key)) return -1; + + Node node = cache.get(key); + node.freq++; + node.timestamp = ++timestamp; + return node.value; + } + + public void put(int key, int value) { + if (capacity <= 0) return; + + timestamp++; + if (cache.containsKey(key)) { + Node node = cache.get(key); + node.value = value; + node.freq++; + node.timestamp = timestamp; + return; + } + + if (cache.size() >= capacity) { + int minFreq = Integer.MAX_VALUE, minTimestamp = Integer.MAX_VALUE, lfuKey = -1; + + for (Map.Entry entry : cache.entrySet()) { + Node node = entry.getValue(); + if (node.freq < minFreq || (node.freq == minFreq && node.timestamp < minTimestamp)) { + minFreq = node.freq; + minTimestamp = node.timestamp; + lfuKey = entry.getKey(); + } + } + + cache.remove(lfuKey); + } + + cache.put(key, new Node(value, 1, timestamp)); + } +} +``` + +```cpp +class LFUCache { + struct Node { + int value, freq, timestamp; + Node(int v, int f, int t) : value(v), freq(f), timestamp(t) {} + }; + + int capacity, timestamp; + unordered_map cache; + +public: + LFUCache(int capacity) : capacity(capacity), timestamp(0) {} + + int get(int key) { + if (cache.find(key) == cache.end()) return -1; + + cache[key]->freq++; + cache[key]->timestamp = ++timestamp; + return cache[key]->value; + } + + void put(int key, int value) { + if (capacity <= 0) return; + + timestamp++; + if (cache.find(key) != cache.end()) { + cache[key]->value = value; + cache[key]->freq++; + cache[key]->timestamp = timestamp; + return; + } + + if (cache.size() >= capacity) { + int minFreq = INT_MAX, minTimestamp = INT_MAX, lfuKey = -1; + + for (const auto& [k, node] : cache) { + if (node->freq < minFreq || (node->freq == minFreq && node->timestamp < minTimestamp)) { + minFreq = node->freq; + minTimestamp = node->timestamp; + lfuKey = k; + } + } + delete cache[lfuKey]; + cache.erase(lfuKey); + } + + cache[key] = new Node(value, 1, timestamp); + } +}; +``` + +```javascript +class LFUCache { + /** + * @param {number} capacity + */ + constructor(capacity) { + this.capacity = capacity; + this.timestamp = 0; + this.cache = new Map(); + } + + /** + * @param {number} key + * @return {number} + */ + get(key) { + if (!this.cache.has(key)) return -1; + + const node = this.cache.get(key); + node.freq++; + node.timestamp = ++this.timestamp; + return node.value; + } + + /** + * @param {number} key + * @param {number} value + * @return {void} + */ + put(key, value) { + if (this.capacity <= 0) return; + + this.timestamp++; + if (this.cache.has(key)) { + const node = this.cache.get(key); + node.value = value; + node.freq++; + node.timestamp = this.timestamp; + return; + } + + if (this.cache.size >= this.capacity) { + let minFreq = Infinity, minTimestamp = Infinity, lfuKey = null; + + for (const [k, node] of this.cache.entries()) { + if (node.freq < minFreq || (node.freq === minFreq && node.timestamp < minTimestamp)) { + minFreq = node.freq; + minTimestamp = node.timestamp; + lfuKey = k; + } + } + + if (lfuKey !== null) this.cache.delete(lfuKey); + } + + this.cache.set(key, { value, freq: 1, timestamp: this.timestamp }); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $get()$ function call. + * $O(n)$ time for each $put()$ function call. +* Space complexity: $O(n)$ + +--- + +## 2. Doubly Linked List + +::tabs-start + +```python +class ListNode: + + def __init__(self, val, prev=None, next=None): + self.val = val + self.prev = prev + self.next = next + +class LinkedList: + + def __init__(self): + self.left = ListNode(0) + self.right = ListNode(0, self.left) + self.left.next = self.right + self.map = {} + + def length(self): + return len(self.map) + + def pushRight(self, val): + node = ListNode(val, self.right.prev, self.right) + self.map[val] = node + self.right.prev = node + node.prev.next = node + + def pop(self, val): + if val in self.map: + node = self.map[val] + next, prev = node.next, node.prev + next.prev = prev + prev.next = next + self.map.pop(val, None) + + def popLeft(self): + res = self.left.next.val + self.pop(self.left.next.val) + return res + + def update(self, val): + self.pop(val) + self.pushRight(val) + +class LFUCache: + + def __init__(self, capacity: int): + self.cap = capacity + self.lfuCnt = 0 + self.valMap = {} # Map key -> val + self.countMap = defaultdict(int) # Map key -> count + # Map count of key -> linkedlist + self.listMap = defaultdict(LinkedList) + + def counter(self, key): + cnt = self.countMap[key] + self.countMap[key] += 1 + self.listMap[cnt].pop(key) + self.listMap[cnt + 1].pushRight(key) + + if cnt == self.lfuCnt and self.listMap[cnt].length() == 0: + self.lfuCnt += 1 + + + def get(self, key: int) -> int: + if key not in self.valMap: + return -1 + self.counter(key) + return self.valMap[key] + + def put(self, key: int, value: int) -> None: + if self.cap == 0: + return + + if key not in self.valMap and len(self.valMap) == self.cap: + res = self.listMap[self.lfuCnt].popLeft() + self.valMap.pop(res) + self.countMap.pop(res) + + self.valMap[key] = value + self.counter(key) + self.lfuCnt = min(self.lfuCnt, self.countMap[key]) +``` + +```java +class ListNode { + int val; + ListNode prev, next; + + ListNode(int val) { + this.val = val; + } + + ListNode(int val, ListNode prev, ListNode next) { + this.val = val; + this.prev = prev; + this.next = next; + } +} + +class DoublyLinkedList { + private ListNode left, right; + private Map map; + + DoublyLinkedList() { + this.left = new ListNode(0); + this.right = new ListNode(0, this.left, null); + this.left.next = this.right; + this.map = new HashMap<>(); + } + + public int length() { + return map.size(); + } + + public void pushRight(int val) { + ListNode node = new ListNode(val, this.right.prev, this.right); + this.map.put(val, node); + this.right.prev.next = node; + this.right.prev = node; + } + + public void pop(int val) { + if (this.map.containsKey(val)) { + ListNode node = this.map.get(val); + ListNode prev = node.prev, next = node.next; + prev.next = next; + next.prev = prev; + this.map.remove(val); + } + } + + public int popLeft() { + int res = this.left.next.val; + pop(res); + return res; + } + + public void update(int val) { + pop(val); + pushRight(val); + } +} + +public class LFUCache { + private int capacity; + private int lfuCount; + private Map valMap; + private Map countMap; + private Map listMap; + + public LFUCache(int capacity) { + this.capacity = capacity; + this.lfuCount = 0; + this.valMap = new HashMap<>(); + this.countMap = new HashMap<>(); + this.listMap = new HashMap<>(); + } + + private void counter(int key) { + int count = countMap.get(key); + countMap.put(key, count + 1); + listMap.putIfAbsent(count, new DoublyLinkedList()); + listMap.get(count).pop(key); + + listMap.putIfAbsent(count + 1, new DoublyLinkedList()); + listMap.get(count + 1).pushRight(key); + + if (count == lfuCount && listMap.get(count).length() == 0) { + lfuCount++; + } + } + + public int get(int key) { + if (!valMap.containsKey(key)) { + return -1; + } + counter(key); + return valMap.get(key); + } + + public void put(int key, int value) { + if (capacity == 0) { + return; + } + + if (!valMap.containsKey(key) && valMap.size() == capacity) { + int toRemove = listMap.get(lfuCount).popLeft(); + valMap.remove(toRemove); + countMap.remove(toRemove); + } + + valMap.put(key, value); + countMap.putIfAbsent(key, 0); + counter(key); + lfuCount = Math.min(lfuCount, countMap.get(key)); + } +} +``` + +```cpp +class LFUCache { + struct ListNode { + int val; + ListNode* prev; + ListNode* next; + + ListNode(int val) : val(val), prev(nullptr), next(nullptr) {} + ListNode(int val, ListNode* prev, ListNode* next) : val(val), prev(prev), next(next) {} + }; + + struct LinkedList { + ListNode* left; + ListNode* right; + unordered_map map; + + LinkedList() { + left = new ListNode(0); + right = new ListNode(0); + left->next = right; + right->prev = left; + } + + ~LinkedList() { + while (left->next != right) { + ListNode* temp = left->next; + left->next = temp->next; + delete temp; + } + delete left; + delete right; + } + + int length() { + return map.size(); + } + + void pushRight(int val) { + ListNode* node = new ListNode(val, right->prev, right); + map[val] = node; + right->prev->next = node; + right->prev = node; + } + + void pop(int val) { + if (map.find(val) != map.end()) { + ListNode* node = map[val]; + ListNode* prev = node->prev; + ListNode* next = node->next; + prev->next = next; + next->prev = prev; + map.erase(val); + delete node; + } + } + + int popLeft() { + int res = left->next->val; + pop(res); + return res; + } + + void update(int val) { + pop(val); + pushRight(val); + } + }; + + int capacity; + int lfuCount; + unordered_map valMap; // Map key -> value + unordered_map countMap; // Map key -> count + unordered_map listMap; // Map count -> linked list + + void counter(int key) { + int count = countMap[key]; + countMap[key] = count + 1; + + listMap[count]->pop(key); + + if (!listMap.count(count + 1)) { + listMap[count + 1] = new LinkedList(); + } + listMap[count + 1]->pushRight(key); + + if (count == lfuCount && listMap[count]->length() == 0) { + lfuCount++; + } + } + +public: + LFUCache(int capacity) : capacity(capacity), lfuCount(0) { + listMap[0] = new LinkedList(); + } + + ~LFUCache() { + for (auto& pair : listMap) { + delete pair.second; + } + } + + int get(int key) { + if (valMap.find(key) == valMap.end()) { + return -1; + } + counter(key); + return valMap[key]; + } + + void put(int key, int value) { + if (capacity == 0) { + return; + } + + if (valMap.find(key) == valMap.end() && valMap.size() == capacity) { + int toRemove = listMap[lfuCount]->popLeft(); + valMap.erase(toRemove); + countMap.erase(toRemove); + } + + valMap[key] = value; + if (countMap.find(key) == countMap.end()) { + countMap[key] = 0; + listMap[0]->pushRight(key); + lfuCount = 0; + } + counter(key); + } +}; +``` + +```javascript +class ListNode { + /** + * @param {number} val + * @param {ListNode} prev + * @param {ListNode} next + */ + constructor(val, prev = null, next = null) { + this.val = val; + this.prev = prev; + this.next = next; + } +} + +class LinkedList { + constructor() { + this.left = new ListNode(0); + this.right = new ListNode(0); + this.left.next = this.right; + this.right.prev = this.left; + this.map = new Map(); + } + + /** + * @return {number} + */ + length() { + return this.map.size; + } + + /** + * @param {number} val + */ + pushRight(val) { + const node = new ListNode(val, this.right.prev, this.right); + this.map.set(val, node); + this.right.prev.next = node; + this.right.prev = node; + } + + /** + * @param {number} val + */ + pop(val) { + if (this.map.has(val)) { + const node = this.map.get(val); + const prev = node.prev; + const next = node.next; + prev.next = next; + next.prev = prev; + this.map.delete(val); + } + } + + /** + * @return {number} + */ + popLeft() { + const res = this.left.next.val; + this.pop(res); + return res; + } + + /** + * @param {number} val + */ + update(val) { + this.pop(val); + this.pushRight(val); + } +} + +class LFUCache { + /** + * @param {number} capacity + */ + constructor(capacity) { + this.capacity = capacity; + this.lfuCount = 0; + this.valMap = new Map(); + this.countMap = new Map(); + this.listMap = new Map(); + this.listMap.set(0, new LinkedList()); + } + + /** + * @param {number} key + */ + counter(key) { + const count = this.countMap.get(key); + this.countMap.set(key, count + 1); + + this.listMap.get(count).pop(key); + + if (!this.listMap.has(count + 1)) { + this.listMap.set(count + 1, new LinkedList()); + } + this.listMap.get(count + 1).pushRight(key); + + if (count === this.lfuCount && this.listMap.get(count).length() === 0) { + this.lfuCount++; + } + } + + /** + * @param {number} key + * @return {number} + */ + get(key) { + if (!this.valMap.has(key)) { + return -1; + } + this.counter(key); + return this.valMap.get(key); + } + + /** + * @param {number} key + * @param {number} value + */ + put(key, value) { + if (this.capacity === 0) return; + + if (!this.valMap.has(key) && this.valMap.size === this.capacity) { + const toRemove = this.listMap.get(this.lfuCount).popLeft(); + this.valMap.delete(toRemove); + this.countMap.delete(toRemove); + } + + this.valMap.set(key, value); + if (!this.countMap.has(key)) { + this.countMap.set(key, 0); + this.listMap.get(0).pushRight(key); + this.lfuCount = 0; + } + this.counter(key); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(1)$ time for each $get()$ and $put()$ function calls. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/linked-list-cycle-detection.md b/articles/linked-list-cycle-detection.md new file mode 100644 index 000000000..f8d574c77 --- /dev/null +++ b/articles/linked-list-cycle-detection.md @@ -0,0 +1,456 @@ +## 1. Hash Set + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + +class Solution: + def hasCycle(self, head: Optional[ListNode]) -> bool: + seen = set() + cur = head + while cur: + if cur in seen: + return True + seen.add(cur) + cur = cur.next + return False +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +public class Solution { + public boolean hasCycle(ListNode head) { + HashSet seen = new HashSet<>(); + ListNode cur = head; + while (cur != null) { + if (seen.contains(cur)) { + return true; + } + seen.add(cur); + cur = cur.next; + } + return false; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +class Solution { +public: + bool hasCycle(ListNode* head) { + unordered_set seen; + ListNode* cur = head; + while (cur) { + if (seen.find(cur) != seen.end()) { + return true; + } + seen.insert(cur); + cur = cur->next; + } + return false; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {ListNode} head + * @return {boolean} + */ + hasCycle(head) { + let seen = new Set(); + let cur = head; + while (cur) { + if (seen.has(cur)) { + return true; + } + seen.add(cur); + cur = cur.next; + } + return false; + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +public class Solution { + public bool HasCycle(ListNode head) { + HashSet seen = new HashSet(); + ListNode cur = head; + while (cur != null) { + if (seen.Contains(cur)) { + return true; + } + seen.Add(cur); + cur = cur.next; + } + return false; + } +} +``` + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func hasCycle(head *ListNode) bool { + seen := make(map[*ListNode]bool) + cur := head + for cur != nil { + if seen[cur] { + return true + } + seen[cur] = true + cur = cur.Next + } + return false +} +``` + +```kotlin +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ + +class Solution { + fun hasCycle(head: ListNode?): Boolean { + val seen = HashSet() + var cur = head + while (cur != null) { + if (!seen.add(cur)) { + return true + } + cur = cur.next + } + return false + } +} +``` + +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init(_ val: Int) { + * self.val = val + * self.next = nil + * } + * } + */ + +class Solution { + func hasCycle(_ head: ListNode?) -> Bool { + var seen = Set() + var cur = head + + while cur != nil { + let nodeId = ObjectIdentifier(cur!) + if seen.contains(nodeId) { + return true + } + seen.insert(nodeId) + cur = cur?.next + } + return false + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Fast And Slow Pointers + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + +class Solution: + def hasCycle(self, head: Optional[ListNode]) -> bool: + slow, fast = head, head + + while fast and fast.next: + slow = slow.next + fast = fast.next.next + if slow == fast: + return True + return False +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +class Solution { + public boolean hasCycle(ListNode head) { + ListNode fast = head; + ListNode slow = head; + + while (fast != null && fast.next != null) { + fast = fast.next.next; + slow = slow.next; + if (fast == slow) return true; + } + return false; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +class Solution { +public: + bool hasCycle(ListNode* head) { + ListNode* fast = head; + ListNode* slow = head; + + while (fast != nullptr && fast->next != nullptr) { + fast = fast->next->next; + slow = slow->next; + + if (fast == slow) { + return true; + } + } + + return false; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {ListNode} head + * @return {boolean} + */ + hasCycle(head) { + let fast = head; + let slow = head; + + while (fast !== null && fast.next !== null) { + fast = fast.next.next; + slow = slow.next; + + if (fast === slow) { + return true; + } + } + + return false; + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +public class Solution { + public bool HasCycle(ListNode head) { + ListNode slow = head, fast = head; + + while (fast != null && fast.next != null) { + fast = fast.next.next; + slow = slow.next; + if (slow.Equals(fast)) return true; + } + return false; + } +} +``` + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func hasCycle(head *ListNode) bool { + slow := head + fast := head + for fast != nil && fast.Next != nil { + slow = slow.Next + fast = fast.Next.Next + if slow == fast { + return true + } + } + return false +} +``` + +```kotlin +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ + +class Solution { + fun hasCycle(head: ListNode?): Boolean { + var slow = head + var fast = head + while (fast != null && fast?.next != null) { + slow = slow?.next + fast = fast?.next?.next + if (slow == fast) { + return true + } + } + return false + } +} +``` + +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init(_ val: Int) { + * self.val = val + * self.next = nil + * } + * } + */ + +class Solution { + func hasCycle(_ head: ListNode?) -> Bool { + var slow = head + var fast = head + + while fast != nil && fast?.next != nil { + slow = slow?.next + fast = fast?.next?.next + if slow === fast { + return true + } + } + return false + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/linked-list-cycle-ii.md b/articles/linked-list-cycle-ii.md new file mode 100644 index 000000000..2964377b8 --- /dev/null +++ b/articles/linked-list-cycle-ii.md @@ -0,0 +1,264 @@ +## 1. Hash Set + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None + +class Solution: + def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]: + seen = set() + cur = head + while cur: + if cur in seen: + return cur + seen.add(cur) + cur = cur.next + return None +``` + +```java +/** + * Definition for singly-linked list. + * class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ +public class Solution { + public ListNode detectCycle(ListNode head) { + Set seen = new HashSet<>(); + ListNode cur = head; + while (cur != null) { + if (seen.contains(cur)) { + return cur; + } + seen.add(cur); + cur = cur.next; + } + return null; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode* detectCycle(ListNode* head) { + unordered_set seen; + ListNode* cur = head; + while (cur) { + if (seen.find(cur) != seen.end()) { + return cur; + } + seen.insert(cur); + cur = cur->next; + } + return nullptr; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + detectCycle(head) { + const seen = new Set(); + let cur = head; + while (cur) { + if (seen.has(cur)) { + return cur; + } + seen.add(cur); + cur = cur.next; + } + return null; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Fast & Slow Pointers + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None + +class Solution: + def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]: + if not head or not head.next: + return None + + slow, fast = head, head + while fast and fast.next: + slow = slow.next + fast = fast.next.next + if slow == fast: + slow = head + while slow != fast: + slow = slow.next + fast = fast.next + return slow + return None +``` + +```java +/** + * Definition for singly-linked list. + * class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ +public class Solution { + public ListNode detectCycle(ListNode head) { + if (head == null || head.next == null) { + return null; + } + + ListNode slow = head, fast = head; + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + if (slow == fast) { + slow = head; + while (slow != fast) { + slow = slow.next; + fast = fast.next; + } + return slow; + } + } + return null; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + ListNode* detectCycle(ListNode* head) { + if (!head || !head->next) { + return nullptr; + } + + ListNode* slow = head; + ListNode* fast = head; + + while (fast && fast->next) { + slow = slow->next; + fast = fast->next->next; + + if (slow == fast) { + slow = head; + while (slow != fast) { + slow = slow->next; + fast = fast->next; + } + return slow; + } + } + return nullptr; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + detectCycle(head) { + if (!head || !head.next) { + return null; + } + + let slow = head, fast = head; + + while (fast && fast.next) { + slow = slow.next; + fast = fast.next.next; + + if (slow === fast) { + slow = head; + while (slow !== fast) { + slow = slow.next; + fast = fast.next; + } + return slow; + } + } + return null; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/longest-common-prefix.md b/articles/longest-common-prefix.md new file mode 100644 index 000000000..936377d1c --- /dev/null +++ b/articles/longest-common-prefix.md @@ -0,0 +1,621 @@ +## 1. Horizontal Scanning + +::tabs-start + +```python +class Solution: + def longestCommonPrefix(self, strs: List[str]) -> str: + prefix = strs[0] + for i in range(1, len(strs)): + j = 0 + while j < min(len(prefix), len(strs[i])): + if prefix[j] != strs[i][j]: + break + j += 1 + prefix = prefix[:j] + return prefix +``` + +```java +public class Solution { + public String longestCommonPrefix(String[] strs) { + String prefix = strs[0]; + for (int i = 1; i < strs.length; i++) { + int j = 0; + while (j < Math.min(prefix.length(), strs[i].length())) { + if (prefix.charAt(j) != strs[i].charAt(j)) { + break; + } + j++; + } + prefix = prefix.substring(0, j); + } + return prefix; + } +} +``` + +```cpp +class Solution { +public: + string longestCommonPrefix(vector& strs) { + string prefix = strs[0]; + for (int i = 1; i < strs.size(); i++) { + int j = 0; + while (j < min(prefix.length(), strs[i].length())) { + if (prefix[j] != strs[i][j]) { + break; + } + j++; + } + prefix = prefix.substr(0, j); + } + return prefix; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} strs + * @return {string} + */ + longestCommonPrefix(strs) { + let prefix = strs[0]; + for (let i = 1; i < strs.length; i++) { + let j = 0; + while (j < Math.min(prefix.length, strs[i].length)) { + if (prefix[j] !== strs[i][j]) { + break; + } + j++; + } + prefix = prefix.slice(0, j); + } + return prefix; + } +} +``` + +```csharp +public class Solution { + public string LongestCommonPrefix(string[] strs) { + string prefix = strs[0]; + + for (int i = 1; i < strs.Length; i++) { + int j = 0; + while (j < Math.Min(prefix.Length, strs[i].Length)) { + if (prefix[j] != strs[i][j]) { + break; + } + j++; + } + prefix = prefix.Substring(0, j); + } + + return prefix; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(1)$ + +> Where $n$ is the length of the shortest string and $m$ is the number of strings. + +--- + +## 2. Vertical Scanning + +::tabs-start + +```python +class Solution: + def longestCommonPrefix(self, strs: List[str]) -> str: + for i in range(len(strs[0])): + for s in strs: + if i == len(s) or s[i] != strs[0][i]: + return s[:i] + return strs[0] +``` + +```java +public class Solution { + public String longestCommonPrefix(String[] strs) { + for (int i = 0; i < strs[0].length(); i++) { + for (String s : strs) { + if (i == s.length() || s.charAt(i) != strs[0].charAt(i)) { + return s.substring(0, i); + } + } + } + return strs[0]; + } +} +``` + +```cpp +class Solution { +public: + string longestCommonPrefix(vector& strs) { + for (int i = 0; i < strs[0].length(); i++) { + for (const string& s : strs) { + if (i == s.length() || s[i] != strs[0][i]) { + return s.substr(0, i); + } + } + } + return strs[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} strs + * @return {string} + */ + longestCommonPrefix(strs) { + for (let i = 0; i < strs[0].length; i++) { + for (let s of strs) { + if (i === s.length || s[i] !== strs[0][i]) { + return s.slice(0, i); + } + } + } + return strs[0]; + } +} +``` + +```csharp +public class Solution { + public string LongestCommonPrefix(string[] strs) { + for (int i = 0; i < strs[0].Length; i++) { + foreach (string s in strs) { + if (i == s.Length || s[i] != strs[0][i]) { + return s.Substring(0, i); + } + } + } + return strs[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(1)$ since we did not use extra space. + +> Where $n$ is the length of the shortest string and $m$ is the number of strings. + +--- + +## 3. Sorting + +::tabs-start + +```python +class Solution: + def longestCommonPrefix(self, strs: List[str]) -> str: + if len(strs) == 1: + return strs[0] + + strs = sorted(strs) + for i in range(min(len(strs[0]), len(strs[-1]))): + if strs[0][i] != strs[-1][i]: + return strs[0][:i] + return strs[0] +``` + +```java +public class Solution { + public String longestCommonPrefix(String[] strs) { + if (strs.length == 1) { + return strs[0]; + } + + Arrays.sort(strs); + int N = Math.min(strs[0].length(), strs[strs.length - 1].length()); + for (int i = 0; i < N; i++) { + if (strs[0].charAt(i) != strs[strs.length - 1].charAt(i)) { + return strs[0].substring(0, i); + } + } + return strs[0]; + } +} +``` + +```cpp +class Solution { +public: + string longestCommonPrefix(vector& strs) { + if (strs.size() == 1) { + return strs[0]; + } + + sort(strs.begin(), strs.end()); + for (int i = 0; i < min(strs[0].length(), strs.back().length()); i++) { + if (strs[0][i] != strs.back()[i]) { + return strs[0].substr(0, i); + } + } + return strs[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} strs + * @return {string} + */ + longestCommonPrefix(strs) { + if (strs.length === 1) { + return strs[0]; + } + + strs.sort(); + let N = Math.min(strs[0].length, strs[strs.length - 1].length); + for (let i = 0; i < N; i++) { + if (strs[0][i] !== strs[strs.length - 1][i]) { + return strs[0].slice(0, i); + } + } + return strs[0]; + } +} +``` + +```csharp +public class Solution { + public string LongestCommonPrefix(string[] strs) { + if (strs.Length == 1) { + return strs[0]; + } + + Array.Sort(strs); + string first = strs[0]; + string last = strs[strs.Length - 1]; + + int i = 0; + while (i < Math.Min(first.Length, last.Length)) { + if (first[i] != last[i]) { + return first.Substring(0, i); + } + i++; + } + + return first; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m \log m)$ +* Space complexity: $O(1)$ or $O(m)$ depending on the sorting algorithm. + +> Where $n$ is the length of the longest string and $m$ is the number of strings. + +--- + +## 4. Trie + +::tabs-start + +```python +class TrieNode: + def __init__(self): + self.children = {} + +class Trie: + def __init__(self): + self.root = TrieNode() + + def insert(self, word: str) -> None: + node = self.root + for char in word: + if char not in node.children: + node.children[char] = TrieNode() + node = node.children[char] + + def lcp(self, word: str, prefixLen: int) -> int: + node = self.root + for i in range(min(len(word), prefixLen)): + if word[i] not in node.children: + return i + node = node.children[word[i]] + return min(len(word), prefixLen) + +class Solution: + def longestCommonPrefix(self, strs: list[str]) -> str: + if len(strs) == 1: + return strs[0] + + mini = 0 + for i in range(1, len(strs)): + if len(strs[mini]) > len(strs[i]): + mini = i + + trie = Trie() + trie.insert(strs[mini]) + prefixLen = len(strs[mini]) + for i in range(len(strs)): + prefixLen = trie.lcp(strs[i], prefixLen) + return strs[0][:prefixLen] +``` + +```java +class TrieNode { + Map children = new HashMap<>(); +} + +class Trie { + TrieNode root = new TrieNode(); + + void insert(String word) { + TrieNode node = root; + for (char c : word.toCharArray()) { + node.children.putIfAbsent(c, new TrieNode()); + node = node.children.get(c); + } + } + + int lcp(String word, int prefixLen) { + TrieNode node = root; + int i = 0; + while (i < Math.min(word.length(), prefixLen)) { + if (!node.children.containsKey(word.charAt(i))) { + return i; + } + node = node.children.get(word.charAt(i)); + i++; + } + return Math.min(word.length(), prefixLen); + } +} + +public class Solution { + public String longestCommonPrefix(String[] strs) { + if (strs.length == 1) { + return strs[0]; + } + + int mini = 0; + for (int i = 1; i < strs.length; i++) { + if (strs[mini].length() > strs[i].length()) { + mini = i; + } + } + + Trie trie = new Trie(); + trie.insert(strs[mini]); + int prefixLen = strs[mini].length(); + + for (int i = 0; i < strs.length; i++) { + prefixLen = trie.lcp(strs[i], prefixLen); + } + + return strs[0].substring(0, prefixLen); + } +} +``` + +```cpp +class TrieNode { +public: + unordered_map children; +}; + +class Trie { +public: + TrieNode* root; + Trie() { + root = new TrieNode(); + } + + void insert(const string& word) { + TrieNode* node = root; + for (char c : word) { + if (node->children.find(c) == node->children.end()) { + node->children[c] = new TrieNode(); + } + node = node->children[c]; + } + } + + int lcp(const string& word, int prefixLen) { + TrieNode* node = root; + int i = 0; + while (i < min((int)word.length(), prefixLen)) { + if (node->children.find(word[i]) == node->children.end()) { + return i; + } + node = node->children[word[i]]; + i++; + } + return min((int)word.length(), prefixLen); + } +}; + +class Solution { +public: + string longestCommonPrefix(vector& strs) { + if (strs.size() == 1) { + return strs[0]; + } + int mini = 0; + for (int i = 1; i < strs.size(); i++) { + if (strs[mini].size() > strs[i].size()) { + mini = i; + } + } + + Trie trie; + trie.insert(strs[mini]); + int prefixLen = strs[mini].length(); + + for (int i = 0; i < strs.size(); i++) { + prefixLen = trie.lcp(strs[i], prefixLen); + } + + return strs[0].substr(0, prefixLen); + } +}; +``` + +```javascript +class TrieNode { + constructor() { + this.children = {}; + } +} + +class Trie { + constructor() { + this.root = new TrieNode(); + } + + /** + * @param {string} word + * @return {void} + */ + insert(word) { + let node = this.root; + for (let char of word) { + if (!node.children[char]) { + node.children[char] = new TrieNode(); + } + node = node.children[char]; + } + } + + /** + * @param {string} word + * @param {number} prefixLen + * @return {number} + */ + lcp(word, prefixLen) { + let node = this.root; + let i = 0; + while (i < Math.min(word.length, prefixLen)) { + if (!node.children[word[i]]) { + return i; + } + node = node.children[word[i]]; + i++; + } + return Math.min(word.length, prefixLen); + } +} + +class Solution { + /** + * @param {string[]} strs + * @return {string} + */ + longestCommonPrefix(strs) { + if (strs.length === 1) { + return strs[0]; + } + + let mini = 0; + for (let i = 1; i < strs.length; i++) { + if (strs[mini].length > strs[i].length) { + mini = i; + } + } + + const trie = new Trie(); + trie.insert(strs[mini]); + let prefixLen = strs[mini].length; + + for (let i = 0; i < strs.length; i++) { + prefixLen = trie.lcp(strs[i], prefixLen); + } + + return strs[0].substring(0, prefixLen); + } +} +``` + +```csharp +public class TrieNode { + public Dictionary Children = new Dictionary(); +} + +public class Trie { + public TrieNode Root; + + public Trie() { + Root = new TrieNode(); + } + + public void Insert(string word) { + TrieNode node = Root; + foreach (char c in word) { + if (!node.Children.ContainsKey(c)) { + node.Children[c] = new TrieNode(); + } + node = node.Children[c]; + } + } + + public int Lcp(string word, int prefixLen) { + TrieNode node = Root; + for (int i = 0; i < Math.Min(word.Length, prefixLen); i++) { + if (!node.Children.ContainsKey(word[i])) { + return i; + } + node = node.Children[word[i]]; + } + return Math.Min(word.Length, prefixLen); + } +} + +public class Solution { + public string LongestCommonPrefix(string[] strs) { + if (strs.Length == 1) return strs[0]; + + int mini = 0; + for (int i = 1; i < strs.Length; i++) { + if (strs[i].Length < strs[mini].Length) { + mini = i; + } + } + + Trie trie = new Trie(); + trie.Insert(strs[mini]); + + int prefixLen = strs[mini].Length; + for (int i = 0; i < strs.Length; i++) { + prefixLen = trie.Lcp(strs[i], prefixLen); + } + + return strs[0].Substring(0, prefixLen); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the shortest string and $m$ is the number of strings. \ No newline at end of file diff --git a/articles/longest-common-subsequence.md b/articles/longest-common-subsequence.md new file mode 100644 index 000000000..95c23cd86 --- /dev/null +++ b/articles/longest-common-subsequence.md @@ -0,0 +1,1110 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def longestCommonSubsequence(self, text1: str, text2: str) -> int: + + def dfs(i, j): + if i == len(text1) or j == len(text2): + return 0 + if text1[i] == text2[j]: + return 1 + dfs(i + 1, j + 1) + return max(dfs(i + 1, j), dfs(i, j + 1)) + + return dfs(0, 0) +``` + +```java +public class Solution { + public int longestCommonSubsequence(String text1, String text2) { + return dfs(text1, text2, 0, 0); + } + + private int dfs(String text1, String text2, int i, int j) { + if (i == text1.length() || j == text2.length()) { + return 0; + } + if (text1.charAt(i) == text2.charAt(j)) { + return 1 + dfs(text1, text2, i + 1, j + 1); + } + return Math.max(dfs(text1, text2, i + 1, j), + dfs(text1, text2, i, j + 1)); + } +} +``` + +```cpp +class Solution { +public: + int longestCommonSubsequence(string text1, string text2) { + return dfs(text1, text2, 0, 0); + } + +private: + int dfs(const string& text1, const string& text2, int i, int j) { + if (i == text1.size() || j == text2.size()) { + return 0; + } + if (text1[i] == text2[j]) { + return 1 + dfs(text1, text2, i + 1, j + 1); + } + return max(dfs(text1, text2, i + 1, j), + dfs(text1, text2, i, j + 1)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} text1 + * @param {string} text2 + * @return {number} + */ + longestCommonSubsequence(text1, text2) { + + const dfs = (i, j) => { + if (i === text1.length || j === text2.length) { + return 0; + } + if (text1[i] === text2[j]) { + return 1 + dfs(i + 1, j + 1); + } + return Math.max(dfs(i + 1, j), dfs(i, j + 1)); + } + + return dfs(0, 0); + } +} +``` + +```csharp +public class Solution { + public int LongestCommonSubsequence(string text1, string text2) { + return Dfs(text1, text2, 0, 0); + } + + private int Dfs(string text1, string text2, int i, int j) { + if (i == text1.Length || j == text2.Length) { + return 0; + } + if (text1[i] == text2[j]) { + return 1 + Dfs(text1, text2, i + 1, j + 1); + } + return Math.Max(Dfs(text1, text2, i + 1, j), + Dfs(text1, text2, i, j + 1)); + } +} +``` + +```go +func longestCommonSubsequence(text1 string, text2 string) int { + var dfs func(i, j int) int + dfs = func(i, j int) int { + if i == len(text1) || j == len(text2) { + return 0 + } + if text1[i] == text2[j] { + return 1 + dfs(i+1, j+1) + } + return max(dfs(i+1, j), dfs(i, j+1)) + } + return dfs(0, 0) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun longestCommonSubsequence(text1: String, text2: String): Int { + fun dfs(i: Int, j: Int): Int { + if (i == text1.length || j == text2.length) return 0 + if (text1[i] == text2[j]) return 1 + dfs(i + 1, j + 1) + return maxOf(dfs(i + 1, j), dfs(i, j + 1)) + } + + return dfs(0, 0) + } +} +``` + +```swift +class Solution { + func longestCommonSubsequence(_ text1: String, _ text2: String) -> Int { + let arr1 = Array(text1) + let arr2 = Array(text2) + + func dfs(_ i: Int, _ j: Int) -> Int { + if i == arr1.count || j == arr2.count { + return 0 + } + if arr1[i] == arr2[j] { + return 1 + dfs(i + 1, j + 1) + } + return max(dfs(i + 1, j), dfs(i, j + 1)) + } + + return dfs(0, 0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ {m + n})$ +* Space complexity: $O(m + n)$ + +> Where $m$ is the length of the string $text1$ and $n$ is the length of the string $text2$. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def longestCommonSubsequence(self, text1: str, text2: str) -> int: + memo = {} + + def dfs(i, j): + if i == len(text1) or j == len(text2): + return 0 + if (i, j) in memo: + return memo[(i, j)] + + if text1[i] == text2[j]: + memo[(i, j)] = 1 + dfs(i + 1, j + 1) + else: + memo[(i, j)] = max(dfs(i + 1, j), dfs(i, j + 1)) + + return memo[(i, j)] + + return dfs(0, 0) +``` + +```java +public class Solution { + private int[][] memo; + + public int longestCommonSubsequence(String text1, String text2) { + memo = new int[text1.length()][text2.length()]; + for (int i = 0; i < text1.length(); i++) { + for (int j = 0; j < text2.length(); j++) { + memo[i][j] = -1; + } + } + return dfs(text1, text2, 0, 0); + } + + private int dfs(String text1, String text2, int i, int j) { + if (i == text1.length() || j == text2.length()) { + return 0; + } + if (memo[i][j] != -1) { + return memo[i][j]; + } + if (text1.charAt(i) == text2.charAt(j)) { + memo[i][j] = 1 + dfs(text1, text2, i + 1, j + 1); + } else { + memo[i][j] = Math.max(dfs(text1, text2, i + 1, j), + dfs(text1, text2, i, j + 1)); + } + return memo[i][j]; + } +} +``` + +```cpp +class Solution { +public: + vector> memo; + + int longestCommonSubsequence(string text1, string text2) { + int m = text1.size(), n = text2.size(); + memo.assign(m, vector(n, -1)); + return dfs(text1, text2, 0, 0); + } + + int dfs(string& text1, string& text2, int i, int j) { + if (i == text1.size() || j == text2.size()) { + return 0; + } + if (memo[i][j] != -1) { + return memo[i][j]; + } + if (text1[i] == text2[j]) { + memo[i][j] = 1 + dfs(text1, text2, i + 1, j + 1); + } else { + memo[i][j] = max(dfs(text1, text2, i + 1, j), + dfs(text1, text2, i, j + 1)); + } + return memo[i][j]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} text1 + * @param {string} text2 + * @return {number} + */ + longestCommonSubsequence(text1, text2) { + + const memo = Array(text1.length).fill().map(() => + Array(text2.length).fill(-1)); + + const dfs = (i, j) => { + if (i === text1.length || j === text2.length) { + return 0; + } + if (memo[i][j] !== -1) { + return memo[i][j]; + } + if (text1[i] === text2[j]) { + memo[i][j] = 1 + dfs(i + 1, j + 1); + } else { + memo[i][j] = Math.max(dfs(i + 1, j), + dfs(i, j + 1)); + } + return memo[i][j]; + }; + + return dfs(0, 0); + } +} +``` + +```csharp +public class Solution { + private int[,] memo; + + public int LongestCommonSubsequence(string text1, string text2) { + memo = new int[text1.Length, text2.Length]; + for (int i = 0; i < text1.Length; i++) { + for (int j = 0; j < text2.Length; j++) { + memo[i, j] = -1; + } + } + return Dfs(text1, text2, 0, 0); + } + + private int Dfs(string text1, string text2, int i, int j) { + if (i == text1.Length || j == text2.Length) { + return 0; + } + if (memo[i, j] != -1) { + return memo[i, j]; + } + if (text1[i] == text2[j]) { + memo[i, j] = 1 + Dfs(text1, text2, i + 1, j + 1); + } else { + memo[i, j] = Math.Max(Dfs(text1, text2, i + 1, j), + Dfs(text1, text2, i, j + 1)); + } + return memo[i, j]; + } +} +``` + +```go +func longestCommonSubsequence(text1 string, text2 string) int { + m, n := len(text1), len(text2) + memo := make([][]int, m+1) + for i := range memo { + memo[i] = make([]int, n+1) + for j := range memo[i] { + memo[i][j] = -1 + } + } + + var dfs func(i, j int) int + dfs = func(i, j int) int { + if i == m || j == n { + return 0 + } + if memo[i][j] != -1 { + return memo[i][j] + } + + if text1[i] == text2[j] { + memo[i][j] = 1 + dfs(i+1, j+1) + } else { + memo[i][j] = max(dfs(i+1, j), dfs(i, j+1)) + } + + return memo[i][j] + } + + return dfs(0, 0) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun longestCommonSubsequence(text1: String, text2: String): Int { + val m = text1.length + val n = text2.length + val memo = Array(m + 1) { IntArray(n + 1) { -1 } } + + fun dfs(i: Int, j: Int): Int { + if (i == m || j == n) return 0 + if (memo[i][j] != -1) return memo[i][j] + + memo[i][j] = if (text1[i] == text2[j]) { + 1 + dfs(i + 1, j + 1) + } else { + maxOf(dfs(i + 1, j), dfs(i, j + 1)) + } + + return memo[i][j] + } + + return dfs(0, 0) + } +} +``` + +```swift +class Solution { + func longestCommonSubsequence(_ text1: String, _ text2: String) -> Int { + let arr1 = Array(text1) + let arr2 = Array(text2) + var memo = [String: Int]() + + func dfs(_ i: Int, _ j: Int) -> Int { + if i == arr1.count || j == arr2.count { + return 0 + } + let key = "\(i),\(j)" + if let val = memo[key] { + return val + } + + if arr1[i] == arr2[j] { + memo[key] = 1 + dfs(i + 1, j + 1) + } else { + memo[key] = max(dfs(i + 1, j), dfs(i, j + 1)) + } + return memo[key]! + } + + return dfs(0, 0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the length of the string $text1$ and $n$ is the length of the string $text2$. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def longestCommonSubsequence(self, text1: str, text2: str) -> int: + dp = [[0 for j in range(len(text2) + 1)] + for i in range(len(text1) + 1)] + + for i in range(len(text1) - 1, -1, -1): + for j in range(len(text2) - 1, -1, -1): + if text1[i] == text2[j]: + dp[i][j] = 1 + dp[i + 1][j + 1] + else: + dp[i][j] = max(dp[i][j + 1], dp[i + 1][j]) + + return dp[0][0] +``` + +```java +public class Solution { + public int longestCommonSubsequence(String text1, String text2) { + int[][] dp = new int[text1.length() + 1][text2.length() + 1]; + + for (int i = text1.length() - 1; i >= 0; i--) { + for (int j = text2.length() - 1; j >= 0; j--) { + if (text1.charAt(i) == text2.charAt(j)) { + dp[i][j] = 1 + dp[i + 1][j + 1]; + } else { + dp[i][j] = Math.max(dp[i][j + 1], dp[i + 1][j]); + } + } + } + + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + int longestCommonSubsequence(string text1, string text2) { + vector> dp(text1.size() + 1, + vector(text2.size() + 1)); + + for (int i = text1.size() - 1; i >= 0; i--) { + for (int j = text2.size() - 1; j >= 0; j--) { + if (text1[i] == text2[j]) { + dp[i][j] = 1 + dp[i + 1][j + 1]; + } else { + dp[i][j] = max(dp[i][j + 1], dp[i + 1][j]); + } + } + } + + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} text1 + * @param {string} text2 + * @return {number} + */ + longestCommonSubsequence(text1, text2) { + const dp = Array(text1.length + 1) + .fill() + .map(() => Array(text2.length + 1).fill(0)); + + for (let i = text1.length - 1; i >= 0; i--) { + for (let j = text2.length - 1; j >= 0; j--) { + if (text1[i] === text2[j]) { + dp[i][j] = 1 + dp[i + 1][j + 1]; + } else { + dp[i][j] = Math.max(dp[i][j + 1], dp[i + 1][j]); + } + } + } + + return dp[0][0]; + } +} +``` + +```csharp +public class Solution { + public int LongestCommonSubsequence(string text1, string text2) { + int[,] dp = new int[text1.Length + 1, text2.Length + 1]; + + for (int i = text1.Length - 1; i >= 0; i--) { + for (int j = text2.Length - 1; j >= 0; j--) { + if (text1[i] == text2[j]) { + dp[i, j] = 1 + dp[i + 1, j + 1]; + } else { + dp[i, j] = Math.Max(dp[i, j + 1], dp[i + 1, j]); + } + } + } + + return dp[0, 0]; + } +} +``` + +```go +func longestCommonSubsequence(text1 string, text2 string) int { + m, n := len(text1), len(text2) + dp := make([][]int, m+1) + for i := range dp { + dp[i] = make([]int, n+1) + } + + for i := m - 1; i >= 0; i-- { + for j := n - 1; j >= 0; j-- { + if text1[i] == text2[j] { + dp[i][j] = 1 + dp[i+1][j+1] + } else { + dp[i][j] = max(dp[i+1][j], dp[i][j+1]) + } + } + } + + return dp[0][0] +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun longestCommonSubsequence(text1: String, text2: String): Int { + val m = text1.length + val n = text2.length + val dp = Array(m + 1) { IntArray(n + 1) } + + for (i in m - 1 downTo 0) { + for (j in n - 1 downTo 0) { + dp[i][j] = if (text1[i] == text2[j]) { + 1 + dp[i + 1][j + 1] + } else { + maxOf(dp[i + 1][j], dp[i][j + 1]) + } + } + } + + return dp[0][0] + } +} +``` + +```swift +class Solution { + func longestCommonSubsequence(_ text1: String, _ text2: String) -> Int { + let m = text1.count + let n = text2.count + let arr1 = Array(text1) + let arr2 = Array(text2) + + var dp = Array(repeating: Array(repeating: 0, count: n + 1), count: m + 1) + + for i in stride(from: m - 1, through: 0, by: -1) { + for j in stride(from: n - 1, through: 0, by: -1) { + if arr1[i] == arr2[j] { + dp[i][j] = 1 + dp[i + 1][j + 1] + } else { + dp[i][j] = max(dp[i][j + 1], dp[i + 1][j]) + } + } + } + + return dp[0][0] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the length of the string $text1$ and $n$ is the length of the string $text2$. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def longestCommonSubsequence(self, text1: str, text2: str) -> int: + if len(text1) < len(text2): + text1, text2 = text2, text1 + + prev = [0] * (len(text2) + 1) + curr = [0] * (len(text2) + 1) + + for i in range(len(text1) - 1, -1, -1): + for j in range(len(text2) - 1, -1, -1): + if text1[i] == text2[j]: + curr[j] = 1 + prev[j + 1] + else: + curr[j] = max(curr[j + 1], prev[j]) + prev, curr = curr, prev + + return prev[0] +``` + +```java +public class Solution { + public int longestCommonSubsequence(String text1, String text2) { + if (text1.length() < text2.length()) { + String temp = text1; + text1 = text2; + text2 = temp; + } + + int[] prev = new int[text2.length() + 1]; + int[] curr = new int[text2.length() + 1]; + + for (int i = text1.length() - 1; i >= 0; i--) { + for (int j = text2.length() - 1; j >= 0; j--) { + if (text1.charAt(i) == text2.charAt(j)) { + curr[j] = 1 + prev[j + 1]; + } else { + curr[j] = Math.max(curr[j + 1], prev[j]); + } + } + int[] temp = prev; + prev = curr; + curr = temp; + } + + return prev[0]; + } +} +``` + +```cpp +class Solution { +public: + int longestCommonSubsequence(string text1, string text2) { + if (text1.size() < text2.size()) { + swap(text1, text2); + } + + vector prev(text2.size() + 1, 0); + vector curr(text2.size() + 1, 0); + + for (int i = text1.size() - 1; i >= 0; --i) { + for (int j = text2.size() - 1; j >= 0; --j) { + if (text1[i] == text2[j]) { + curr[j] = 1 + prev[j + 1]; + } else { + curr[j] = max(curr[j + 1], prev[j]); + } + } + swap(prev, curr); + } + + return prev[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} text1 + * @param {string} text2 + * @return {number} + */ + longestCommonSubsequence(text1, text2) { + if (text1.length < text2.length) { + [text1, text2] = [text2, text1]; + } + + let prev = new Array(text2.length + 1).fill(0); + let curr = new Array(text2.length + 1).fill(0); + + for (let i = text1.length - 1; i >= 0; i--) { + for (let j = text2.length - 1; j >= 0; j--) { + if (text1[i] === text2[j]) { + curr[j] = 1 + prev[j + 1]; + } else { + curr[j] = Math.max(curr[j + 1], prev[j]); + } + } + [prev, curr] = [curr, prev]; + } + + return prev[0]; + } +} +``` + +```csharp +public class Solution { + public int LongestCommonSubsequence(string text1, string text2) { + if (text1.Length < text2.Length) { + string temp = text1; + text1 = text2; + text2 = temp; + } + + int[] prev = new int[text2.Length + 1]; + int[] curr = new int[text2.Length + 1]; + + for (int i = text1.Length - 1; i >= 0; i--) { + for (int j = text2.Length - 1; j >= 0; j--) { + if (text1[i] == text2[j]) { + curr[j] = 1 + prev[j + 1]; + } else { + curr[j] = Math.Max(curr[j + 1], prev[j]); + } + } + Array.Copy(curr, prev, text2.Length + 1); + } + + return prev[0]; + } +} +``` + +```go +func longestCommonSubsequence(text1 string, text2 string) int { + if len(text1) < len(text2) { + text1, text2 = text2, text1 + } + + prev := make([]int, len(text2)+1) + curr := make([]int, len(text2)+1) + + for i := len(text1) - 1; i >= 0; i-- { + for j := len(text2) - 1; j >= 0; j-- { + if text1[i] == text2[j] { + curr[j] = 1 + prev[j+1] + } else { + curr[j] = max(curr[j+1], prev[j]) + } + } + prev, curr = curr, prev + } + + return prev[0] +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun longestCommonSubsequence(text1: String, text2: String): Int { + var t1 = text1 + var t2 = text2 + + if (t1.length < t2.length) { + t1 = text2 + t2 = text1 + } + + val prev = IntArray(t2.length + 1) + val curr = IntArray(t2.length + 1) + + for (i in t1.length - 1 downTo 0) { + for (j in t2.length - 1 downTo 0) { + curr[j] = if (t1[i] == t2[j]) { + 1 + prev[j + 1] + } else { + maxOf(curr[j + 1], prev[j]) + } + } + val temp = prev + prev.fill(0) + prev.indices.forEach { prev[it] = curr[it] } + curr.fill(0) + } + + return prev[0] + } +} +``` + +```swift +class Solution { + func longestCommonSubsequence(_ text1: String, _ text2: String) -> Int { + var t1 = Array(text1) + var t2 = Array(text2) + + if t1.count < t2.count { + swap(&t1, &t2) + } + + var prev = Array(repeating: 0, count: t2.count + 1) + var curr = Array(repeating: 0, count: t2.count + 1) + + for i in stride(from: t1.count - 1, through: 0, by: -1) { + for j in stride(from: t2.count - 1, through: 0, by: -1) { + if t1[i] == t2[j] { + curr[j] = 1 + prev[j + 1] + } else { + curr[j] = max(curr[j + 1], prev[j]) + } + } + swap(&prev, &curr) + } + + return prev[0] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(min(m, n))$ + +> Where $m$ is the length of the string $text1$ and $n$ is the length of the string $text2$. + +--- + +## 5. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def longestCommonSubsequence(self, text1: str, text2: str) -> int: + if len(text1) < len(text2): + text1, text2 = text2, text1 + + dp = [0] * (len(text2) + 1) + + for i in range(len(text1) - 1, -1, -1): + prev = 0 + for j in range(len(text2) - 1, -1, -1): + temp = dp[j] + if text1[i] == text2[j]: + dp[j] = 1 + prev + else: + dp[j] = max(dp[j], dp[j + 1]) + prev = temp + + return dp[0] +``` + +```java +public class Solution { + public int longestCommonSubsequence(String text1, String text2) { + if (text1.length() < text2.length()) { + String temp = text1; + text1 = text2; + text2 = temp; + } + + int[] dp = new int[text2.length() + 1]; + + for (int i = text1.length() - 1; i >= 0; i--) { + int prev = 0; + for (int j = text2.length() - 1; j >= 0; j--) { + int temp = dp[j]; + if (text1.charAt(i) == text2.charAt(j)) { + dp[j] = 1 + prev; + } else { + dp[j] = Math.max(dp[j], dp[j + 1]); + } + prev = temp; + } + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int longestCommonSubsequence(string text1, string text2) { + if (text1.size() < text2.size()) { + swap(text1, text2); + } + + vector dp(text2.size() + 1, 0); + + for (int i = text1.size() - 1; i >= 0; --i) { + int prev = 0; + for (int j = text2.size() - 1; j >= 0; --j) { + int temp = dp[j]; + if (text1[i] == text2[j]) { + dp[j] = 1 + prev; + } else { + dp[j] = max(dp[j], dp[j + 1]); + } + prev = temp; + } + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} text1 + * @param {string} text2 + * @return {number} + */ + longestCommonSubsequence(text1, text2) { + if (text1.length < text2.length) { + [text1, text2] = [text2, text1]; + } + + const dp = new Array(text2.length + 1).fill(0); + + for (let i = text1.length - 1; i >= 0; i--) { + let prev = 0; + for (let j = text2.length - 1; j >= 0; j--) { + let temp = dp[j]; + if (text1[i] === text2[j]) { + dp[j] = 1 + prev; + } else { + dp[j] = Math.max(dp[j], dp[j + 1]); + } + prev = temp; + } + } + + return dp[0]; + } +} +``` + +```csharp +public class Solution { + public int LongestCommonSubsequence(string text1, string text2) { + if (text1.Length < text2.Length) { + string temp = text1; + text1 = text2; + text2 = temp; + } + + int[] dp = new int[text2.Length + 1]; + + for (int i = text1.Length - 1; i >= 0; i--) { + int prev = 0; + for (int j = text2.Length - 1; j >= 0; j--) { + int temp = dp[j]; + if (text1[i] == text2[j]) { + dp[j] = 1 + prev; + } else { + dp[j] = Math.Max(dp[j], dp[j + 1]); + } + prev = temp; + } + } + + return dp[0]; + } +} +``` + +```go +func longestCommonSubsequence(text1 string, text2 string) int { + if len(text1) < len(text2) { + text1, text2 = text2, text1 + } + + dp := make([]int, len(text2)+1) + + for i := len(text1) - 1; i >= 0; i-- { + prev := 0 + for j := len(text2) - 1; j >= 0; j-- { + temp := dp[j] + if text1[i] == text2[j] { + dp[j] = 1 + prev + } else { + dp[j] = max(dp[j], dp[j+1]) + } + prev = temp + } + } + + return dp[0] +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun longestCommonSubsequence(text1: String, text2: String): Int { + var t1 = text1 + var t2 = text2 + + if (t1.length < t2.length) { + t1 = text2 + t2 = text1 + } + + val dp = IntArray(t2.length + 1) + + for (i in t1.length - 1 downTo 0) { + var prev = 0 + for (j in t2.length - 1 downTo 0) { + val temp = dp[j] + dp[j] = if (t1[i] == t2[j]) { + 1 + prev + } else { + maxOf(dp[j], dp[j + 1]) + } + prev = temp + } + } + + return dp[0] + } +} +``` + +```swift +class Solution { + func longestCommonSubsequence(_ text1: String, _ text2: String) -> Int { + var t1 = Array(text1) + var t2 = Array(text2) + + if t1.count < t2.count { + swap(&t1, &t2) + } + + var dp = Array(repeating: 0, count: t2.count + 1) + + for i in stride(from: t1.count - 1, through: 0, by: -1) { + var prev = 0 + for j in stride(from: t2.count - 1, through: 0, by: -1) { + let temp = dp[j] + if t1[i] == t2[j] { + dp[j] = 1 + prev + } else { + dp[j] = max(dp[j], dp[j + 1]) + } + prev = temp + } + } + + return dp[0] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(min(m, n))$ + +> Where $m$ is the length of the string $text1$ and $n$ is the length of the string $text2$. \ No newline at end of file diff --git a/articles/longest-consecutive-sequence.md b/articles/longest-consecutive-sequence.md new file mode 100644 index 000000000..22cd5db3f --- /dev/null +++ b/articles/longest-consecutive-sequence.md @@ -0,0 +1,771 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def longestConsecutive(self, nums: List[int]) -> int: + res = 0 + store = set(nums) + + for num in nums: + streak, curr = 0, num + while curr in store: + streak += 1 + curr += 1 + res = max(res, streak) + return res +``` + +```java +public class Solution { + public int longestConsecutive(int[] nums) { + int res = 0; + Set store = new HashSet<>(); + for (int num : nums) { + store.add(num); + } + + for (int num : nums) { + int streak = 0, curr = num; + while (store.contains(curr)) { + streak++; + curr++; + } + res = Math.max(res, streak); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int longestConsecutive(vector& nums) { + int res = 0; + unordered_set store(nums.begin(), nums.end()); + + for (int num : nums) { + int streak = 0, curr = num; + while (store.find(curr) != store.end()) { + streak++; + curr++; + } + res = max(res, streak); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + longestConsecutive(nums) { + let res = 0; + const store = new Set(nums); + + for (let num of nums) { + let streak = 0, curr = num; + while (store.has(curr)) { + streak++; + curr++; + } + res = Math.max(res, streak); + } + return res; + } +} +``` + +```csharp +public class Solution { + public int LongestConsecutive(int[] nums) { + int res = 0; + HashSet store = new HashSet(nums); + + foreach (int num in nums) { + int streak = 0, curr = num; + while (store.Contains(curr)) { + streak++; + curr++; + } + res = Math.Max(res, streak); + } + return res; + } +} +``` + +```go +func longestConsecutive(nums []int) int { + res := 0 + store := make(map[int]struct{}) + for _, num := range nums { + store[num] = struct{}{} + } + + for _, num := range nums { + streak, curr := 0, num + for _, ok := store[curr]; ok; _, ok = store[curr] { + streak++ + curr++ + } + if streak > res { + res = streak + } + } + return res +} +``` + +```kotlin +class Solution { + fun longestConsecutive(nums: IntArray): Int { + var res = 0 + val store = nums.toSet() + + for (num in nums) { + var streak = 0 + var curr = num + while (curr in store) { + streak++ + curr++ + } + res = maxOf(res, streak) + } + return res + } +} +``` + +```swift +class Solution { + func longestConsecutive(_ nums: [Int]) -> Int { + var res = 0 + let store = Set(nums) + + for num in nums { + var streak = 0 + var curr = num + + while store.contains(curr) { + streak += 1 + curr += 1 + } + + res = max(res, streak) + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def longestConsecutive(self, nums: List[int]) -> int: + if not nums: + return 0 + res = 0 + nums.sort() + + curr, streak = nums[0], 0 + i = 0 + while i < len(nums): + if curr != nums[i]: + curr = nums[i] + streak = 0 + while i < len(nums) and nums[i] == curr: + i += 1 + streak += 1 + curr += 1 + res = max(res, streak) + return res +``` + +```java +public class Solution { + public int longestConsecutive(int[] nums) { + if (nums.length == 0) { + return 0; + } + Arrays.sort(nums); + int res = 0, curr = nums[0], streak = 0, i = 0; + + while (i < nums.length) { + if (curr != nums[i]) { + curr = nums[i]; + streak = 0; + } + while (i < nums.length && nums[i] == curr) { + i++; + } + streak++; + curr++; + res = Math.max(res, streak); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int longestConsecutive(vector& nums) { + if (nums.empty()) return 0; + sort(nums.begin(), nums.end()); + + int res = 0, curr = nums[0], streak = 0, i = 0; + + while (i < nums.size()) { + if (curr != nums[i]) { + curr = nums[i]; + streak = 0; + } + while (i < nums.size() && nums[i] == curr) { + i++; + } + streak++; + curr++; + res = max(res, streak); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + longestConsecutive(nums) { + if (nums.length === 0) { + return 0; + } + nums.sort((a, b) => a - b); + + let res = 0, curr = nums[0], streak = 0, i = 0; + + while (i < nums.length) { + if (curr !== nums[i]) { + curr = nums[i]; + streak = 0; + } + while (i < nums.length && nums[i] === curr) { + i++; + } + streak++; + curr++; + res = Math.max(res, streak); + } + return res; + } +} +``` + +```csharp +public class Solution { + public int LongestConsecutive(int[] nums) { + if (nums.Length == 0) { + return 0; + } + Array.Sort(nums); + + int res = 0, curr = nums[0], streak = 0, i = 0; + + while (i < nums.Length) { + if (curr != nums[i]) { + curr = nums[i]; + streak = 0; + } + while (i < nums.Length && nums[i] == curr) { + i++; + } + streak++; + curr++; + res = Math.Max(res, streak); + } + return res; + } +} +``` + +```go +func longestConsecutive(nums []int) int { + if len(nums) == 0 { + return 0 + } + sort.Ints(nums) + + res := 0 + curr, streak := nums[0], 0 + i := 0 + for i < len(nums) { + if curr != nums[i] { + curr = nums[i] + streak = 0 + } + for i < len(nums) && nums[i] == curr { + i++ + } + streak++ + curr++ + if streak > res { + res = streak + } + } + return res +} +``` + +```kotlin +class Solution { + fun longestConsecutive(nums: IntArray): Int { + if (nums.isEmpty()) return 0 + nums.sort() + + var res = 0 + var curr = nums[0] + var streak = 0 + var i = 0 + while (i < nums.size) { + if (curr != nums[i]) { + curr = nums[i] + streak = 0 + } + while (i < nums.size && nums[i] == curr) { + i++ + } + streak++ + curr++ + res = maxOf(res, streak) + } + return res + } +} +``` + +```swift +class Solution { + func longestConsecutive(_ nums: [Int]) -> Int { + if nums.isEmpty { + return 0 + } + + var res = 0 + var nums = nums.sorted() + + var curr = nums[0] + var streak = 0 + var i = 0 + + while i < nums.count { + if curr != nums[i] { + curr = nums[i] + streak = 0 + } + while i < nums.count && nums[i] == curr { + i += 1 + } + streak += 1 + curr += 1 + res = max(res, streak) + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Hash Set + +::tabs-start + +```python +class Solution: + def longestConsecutive(self, nums: List[int]) -> int: + numSet = set(nums) + longest = 0 + + for num in numSet: + if (num - 1) not in numSet: + length = 1 + while (num + length) in numSet: + length += 1 + longest = max(length, longest) + return longest +``` + +```java +public class Solution { + public int longestConsecutive(int[] nums) { + Set numSet = new HashSet<>(); + for (int num : nums) { + numSet.add(num); + } + int longest = 0; + + for (int num : numSet) { + if (!numSet.contains(num - 1)) { + int length = 1; + while (numSet.contains(num + length)) { + length++; + } + longest = Math.max(longest, length); + } + } + return longest; + } +} +``` + +```cpp +class Solution { +public: + int longestConsecutive(vector& nums) { + unordered_set numSet(nums.begin(), nums.end()); + int longest = 0; + + for (int num : numSet) { + if (numSet.find(num - 1) == numSet.end()) { + int length = 1; + while (numSet.find(num + length) != numSet.end()) { + length++; + } + longest = max(longest, length); + } + } + return longest; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + longestConsecutive(nums) { + const numSet = new Set(nums); + let longest = 0; + + for (let num of numSet) { + if (!numSet.has(num - 1)) { + let length = 1; + while (numSet.has(num + length)) { + length++; + } + longest = Math.max(longest, length); + } + } + return longest; + } +} +``` + +```csharp +public class Solution { + public int LongestConsecutive(int[] nums) { + HashSet numSet = new HashSet(nums); + int longest = 0; + + foreach (int num in numSet) { + if (!numSet.Contains(num - 1)) { + int length = 1; + while (numSet.Contains(num + length)) { + length++; + } + longest = Math.Max(longest, length); + } + } + return longest; + } +} +``` + +```go +func longestConsecutive(nums []int) int { + numSet := make(map[int]struct{}) + for _, num := range nums { + numSet[num] = struct{}{} + } + + longest := 0 + for num := range numSet { + if _, found := numSet[num-1]; !found { + length := 1 + for { + if _, exists := numSet[num+length]; exists { + length++ + } else { + break + } + } + if length > longest { + longest = length + } + } + } + return longest +} +``` + +```kotlin +class Solution { + fun longestConsecutive(nums: IntArray): Int { + val numSet = nums.toSet() + var longest = 0 + + for (num in numSet) { + if ((num - 1) !in numSet) { + var length = 1 + while ((num + length) in numSet) { + length++ + } + longest = maxOf(longest, length) + } + } + return longest + } +} +``` + +```swift +class Solution { + func longestConsecutive(_ nums: [Int]) -> Int { + let numSet = Set(nums) + var longest = 0 + + for num in numSet { + if !numSet.contains(num - 1) { + var length = 1 + while numSet.contains(num + length) { + length += 1 + } + longest = max(length, longest) + } + } + + return longest + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Hash Map + +::tabs-start + +```python +class Solution: + def longestConsecutive(self, nums: List[int]) -> int: + mp = defaultdict(int) + res = 0 + + for num in nums: + if not mp[num]: + mp[num] = mp[num - 1] + mp[num + 1] + 1 + mp[num - mp[num - 1]] = mp[num] + mp[num + mp[num + 1]] = mp[num] + res = max(res, mp[num]) + return res +``` + +```java +public class Solution { + public int longestConsecutive(int[] nums) { + Map mp = new HashMap<>(); + int res = 0; + + for (int num : nums) { + if (!mp.containsKey(num)) { + mp.put(num, mp.getOrDefault(num - 1, 0) + mp.getOrDefault(num + 1, 0) + 1); + mp.put(num - mp.getOrDefault(num - 1, 0), mp.get(num)); + mp.put(num + mp.getOrDefault(num + 1, 0), mp.get(num)); + res = Math.max(res, mp.get(num)); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int longestConsecutive(vector& nums) { + unordered_map mp; + int res = 0; + + for (int num : nums) { + if (!mp[num]) { + mp[num] = mp[num - 1] + mp[num + 1] + 1; + mp[num - mp[num - 1]] = mp[num]; + mp[num + mp[num + 1]] = mp[num]; + res = max(res, mp[num]); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + longestConsecutive(nums) { + const mp = new Map(); + let res = 0; + + for (let num of nums) { + if (!mp.has(num)) { + mp.set(num, (mp.get(num - 1) || 0) + (mp.get(num + 1) || 0) + 1); + mp.set(num - (mp.get(num - 1) || 0), mp.get(num)); + mp.set(num + (mp.get(num + 1) || 0), mp.get(num)); + res = Math.max(res, mp.get(num)); + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int LongestConsecutive(int[] nums) { + Dictionary mp = new Dictionary(); + int res = 0; + + foreach (int num in nums) { + if (!mp.ContainsKey(num)) { + mp[num] = (mp.ContainsKey(num - 1) ? mp[num - 1] : 0) + + (mp.ContainsKey(num + 1) ? mp[num + 1] : 0) + 1; + + mp[num - (mp.ContainsKey(num - 1) ? mp[num - 1] : 0)] = mp[num]; + mp[num + (mp.ContainsKey(num + 1) ? mp[num + 1] : 0)] = mp[num]; + + res = Math.Max(res, mp[num]); + } + } + return res; + } +} +``` + +```go +func longestConsecutive(nums []int) int { + mp := make(map[int]int) + res := 0 + + for _, num := range nums { + if mp[num] == 0 { + left := mp[num - 1] + right := mp[num + 1] + sum := left + right + 1 + mp[num] = sum + mp[num - left] = sum + mp[num + right] = sum + if sum > res { + res = sum + } + } + } + return res +} +``` + +```kotlin +class Solution { + fun longestConsecutive(nums: IntArray): Int { + val mp = HashMap() + var res = 0 + + for (num in nums) { + if (mp[num] == null) { + val left = mp[num - 1] ?: 0 + val right = mp[num + 1] ?: 0 + val sum = left + right + 1 + mp[num] = sum + mp[num - left] = sum + mp[num + right] = sum + res = maxOf(res, sum) + } + } + return res + } +} +``` + +```swift +class Solution { + func longestConsecutive(_ nums: [Int]) -> Int { + var mp = [Int: Int]() + var res = 0 + + for num in nums { + if mp[num] == nil { + let left = mp[num - 1] ?? 0 + let right = mp[num + 1] ?? 0 + let length = left + right + 1 + + mp[num] = length + mp[num - left] = length + mp[num + right] = length + + res = max(res, length) + } + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/longest-happy-string.md b/articles/longest-happy-string.md new file mode 100644 index 000000000..3b669e0e4 --- /dev/null +++ b/articles/longest-happy-string.md @@ -0,0 +1,567 @@ +## 1. Greedy + +::tabs-start + +```python +class Solution: + def longestDiverseString(self, a: int, b: int, c: int) -> str: + count = [a, b, c] + res = [] + + def getMax(repeated): + idx = -1 + maxCnt = 0 + for i in range(3): + if i == repeated or count[i] == 0: + continue + if maxCnt < count[i]: + maxCnt = count[i] + idx = i + return idx + + repeated = -1 + while True: + maxChar = getMax(repeated) + if maxChar == -1: + break + res.append(chr(maxChar + ord('a'))) + count[maxChar] -= 1 + if len(res) > 1 and res[-1] == res[-2]: + repeated = maxChar + else: + repeated = -1 + + return ''.join(res) +``` + +```java +public class Solution { + public String longestDiverseString(int a, int b, int c) { + int[] count = {a, b, c}; + StringBuilder res = new StringBuilder(); + + int repeated = -1; + while (true) { + int maxChar = getMax(count, repeated); + if (maxChar == -1) { + break; + } + res.append((char) (maxChar + 'a')); + count[maxChar]--; + + if (res.length() > 1 && res.charAt(res.length() - 1) == res.charAt(res.length() - 2)) { + repeated = maxChar; + } else { + repeated = -1; + } + } + + return res.toString(); + } + + private int getMax(int[] count, int repeated) { + int idx = -1, maxCnt = 0; + for (int i = 0; i < 3; i++) { + if (i == repeated || count[i] == 0) { + continue; + } + if (maxCnt < count[i]) { + maxCnt = count[i]; + idx = i; + } + } + return idx; + } +} +``` + +```cpp +class Solution { +public: + string longestDiverseString(int a, int b, int c) { + vector count = {a, b, c}; + string res; + + int repeated = -1; + while (true) { + int maxChar = getMax(count, repeated); + if (maxChar == -1) { + break; + } + res += (char)(maxChar + 'a'); + count[maxChar]--; + + if (res.size() > 1 && res.back() == res[res.size() - 2]) { + repeated = maxChar; + } else { + repeated = -1; + } + } + + return res; + } + +private: + int getMax(const vector& count, int repeated) { + int idx = -1, maxCnt = 0; + for (int i = 0; i < 3; i++) { + if (i == repeated || count[i] == 0) { + continue; + } + if (maxCnt < count[i]) { + maxCnt = count[i]; + idx = i; + } + } + return idx; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} a + * @param {number} b + * @param {number} c + * @return {string} + */ + longestDiverseString(a, b, c) { + const count = [a, b, c]; + const res = []; + + const getMax = (repeated) => { + let idx = -1; + let maxCnt = 0; + for (let i = 0; i < 3; i++) { + if (i === repeated || count[i] === 0) { + continue; + } + if (maxCnt < count[i]) { + maxCnt = count[i]; + idx = i; + } + } + return idx; + }; + + let repeated = -1; + while (true) { + const maxChar = getMax(repeated); + if (maxChar === -1) { + break; + } + res.push(String.fromCharCode(maxChar + 97)); + count[maxChar]--; + + if (res.length > 1 && res[res.length - 1] === res[res.length - 2]) { + repeated = maxChar; + } else { + repeated = -1; + } + } + + return res.join(''); + } +} +``` + +```csharp +public class Solution { + public string LongestDiverseString(int a, int b, int c) { + int[] count = new int[] { a, b, c }; + List res = new List(); + + int GetMax(int repeated) { + int idx = -1; + int maxCnt = 0; + for (int i = 0; i < 3; i++) { + if (i == repeated || count[i] == 0) continue; + if (maxCnt < count[i]) { + maxCnt = count[i]; + idx = i; + } + } + return idx; + } + + int repeated = -1; + while (true) { + int maxChar = GetMax(repeated); + if (maxChar == -1) break; + + res.Add((char)(maxChar + 'a')); + count[maxChar]--; + + if (res.Count > 1 && res[res.Count - 1] == res[res.Count - 2]) { + repeated = maxChar; + } else { + repeated = -1; + } + } + + return new string(res.ToArray()); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output string. + +--- + +## 2. Greedy (Max-Heap) + +::tabs-start + +```python +class Solution: + def longestDiverseString(self, a: int, b: int, c: int) -> str: + res = "" + maxHeap = [] + for count, char in [(-a, "a"), (-b, "b"), (-c, "c")]: + if count != 0: + heapq.heappush(maxHeap, (count, char)) + + while maxHeap: + count, char = heapq.heappop(maxHeap) + if len(res) > 1 and res[-1] == res[-2] == char: + if not maxHeap: + break + count2, char2 = heapq.heappop(maxHeap) + res += char2 + count2 += 1 + if count2: + heapq.heappush(maxHeap, (count2, char2)) + heapq.heappush(maxHeap, (count, char)) + else: + res += char + count += 1 + if count: + heapq.heappush(maxHeap, (count, char)) + + return res +``` + +```java +public class Solution { + public String longestDiverseString(int a, int b, int c) { + StringBuilder res = new StringBuilder(); + PriorityQueue maxHeap = new PriorityQueue<>((x, y) -> y[0] - x[0]); + + if (a > 0) maxHeap.offer(new int[]{a, 'a'}); + if (b > 0) maxHeap.offer(new int[]{b, 'b'}); + if (c > 0) maxHeap.offer(new int[]{c, 'c'}); + + while (!maxHeap.isEmpty()) { + int[] first = maxHeap.poll(); + if (res.length() > 1 && res.charAt(res.length() - 1) == first[1] && res.charAt(res.length() - 2) == first[1]) { + if (maxHeap.isEmpty()) break; + int[] second = maxHeap.poll(); + res.append((char) second[1]); + second[0]--; + if (second[0] > 0) maxHeap.offer(second); + maxHeap.offer(first); + } else { + res.append((char) first[1]); + first[0]--; + if (first[0] > 0) maxHeap.offer(first); + } + } + + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string longestDiverseString(int a, int b, int c) { + string res; + priority_queue> maxHeap; + if (a > 0) maxHeap.push({a, 'a'}); + if (b > 0) maxHeap.push({b, 'b'}); + if (c > 0) maxHeap.push({c, 'c'}); + + while (!maxHeap.empty()) { + auto [count, ch] = maxHeap.top(); + maxHeap.pop(); + + if (res.size() > 1 && res[res.size() - 1] == ch && res[res.size() - 2] == ch) { + if (maxHeap.empty()) break; + auto [count2, ch2] = maxHeap.top(); + maxHeap.pop(); + res += ch2; + if (--count2 > 0) maxHeap.push({count2, ch2}); + maxHeap.push({count, ch}); + } else { + res += ch; + if (--count > 0) maxHeap.push({count, ch}); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} a + * @param {number} b + * @param {number} c + * @return {string} + */ + longestDiverseString(a, b, c) { + const res = []; + const maxHeap = new MaxPriorityQueue(x => x[0]); + + if (a > 0) maxHeap.enqueue([a, 'a']); + if (b > 0) maxHeap.enqueue([b, 'b']); + if (c > 0) maxHeap.enqueue([c, 'c']); + + while (!maxHeap.isEmpty()) { + const [count, char] = maxHeap.dequeue(); + + if (res.length > 1 && res[res.length - 1] === char && res[res.length - 2] === char) { + if (maxHeap.isEmpty()) break; + const [count2, char2] = maxHeap.dequeue(); + res.push(char2); + if (count2 - 1 > 0) maxHeap.enqueue([count2 - 1, char2]); + maxHeap.enqueue([count, char]); + } else { + res.push(char); + if (count - 1 > 0) maxHeap.enqueue([count - 1, char]); + } + } + + return res.join(''); + } +} +``` + +```csharp +public class Solution { + public string LongestDiverseString(int a, int b, int c) { + string res = ""; + PriorityQueue<(int count, char ch), int> maxHeap = new PriorityQueue<(int, char), int>(); + + void AddToHeap(int count, char ch) { + if (count > 0) { + maxHeap.Enqueue((count, ch), -count); + } + } + + AddToHeap(a, 'a'); + AddToHeap(b, 'b'); + AddToHeap(c, 'c'); + + while (maxHeap.Count > 0) { + var (count1, ch1) = maxHeap.Dequeue(); + if (res.Length >= 2 && res[^1] == ch1 && res[^2] == ch1) { + if (maxHeap.Count == 0) break; + var (count2, ch2) = maxHeap.Dequeue(); + res += ch2; + count2--; + if (count2 > 0) { + maxHeap.Enqueue((count2, ch2), -count2); + } + maxHeap.Enqueue((count1, ch1), -count1); + } else { + res += ch1; + count1--; + if (count1 > 0) { + maxHeap.Enqueue((count1, ch1), -count1); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output string. + +--- + +## 3. Greedy (Recursion) + +::tabs-start + +```python +class Solution: + def longestDiverseString(self, a: int, b: int, c: int) -> str: + def rec(max1, max2, max3, char1, char2, char3): + if max1 < max2: + return rec(max2, max1, max3, char2, char1, char3) + if max2 < max3: + return rec(max1, max3, max2, char1, char3, char2) + if max2 == 0: + return [char1] * min(2, max1) + + use1 = min(2, max1) + use2 = 1 if max1 - use1 >= max2 else 0 + res = [char1] * use1 + [char2] * use2 + return res + rec(max1 - use1, max2 - use2, max3, char1, char2, char3) + + return ''.join(rec(a, b, c, 'a', 'b', 'c')) +``` + +```java +public class Solution { + public String longestDiverseString(int a, int b, int c) { + return String.join("", rec(a, b, c, 'a', 'b', 'c')); + } + + private List rec(int max1, int max2, int max3, char char1, char char2, char char3) { + if (max1 < max2) { + return rec(max2, max1, max3, char2, char1, char3); + } + if (max2 < max3) { + return rec(max1, max3, max2, char1, char3, char2); + } + if (max2 == 0) { + List result = new ArrayList<>(); + for (int i = 0; i < Math.min(2, max1); i++) { + result.add(String.valueOf(char1)); + } + return result; + } + + int use1 = Math.min(2, max1); + int use2 = (max1 - use1 >= max2) ? 1 : 0; + + List res = new ArrayList<>(); + for (int i = 0; i < use1; i++) { + res.add(String.valueOf(char1)); + } + for (int i = 0; i < use2; i++) { + res.add(String.valueOf(char2)); + } + + res.addAll(rec(max1 - use1, max2 - use2, max3, char1, char2, char3)); + return res; + } +} +``` + +```cpp +class Solution { +public: + string longestDiverseString(int a, int b, int c) { + vector res = rec(a, b, c, 'a', 'b', 'c'); + return string(res.begin(), res.end()); + } + +private: + vector rec(int max1, int max2, int max3, char char1, char char2, char char3) { + if (max1 < max2) { + return rec(max2, max1, max3, char2, char1, char3); + } + if (max2 < max3) { + return rec(max1, max3, max2, char1, char3, char2); + } + if (max2 == 0) { + vector result(min(2, max1), char1); + return result; + } + + int use1 = min(2, max1); + int use2 = (max1 - use1 >= max2) ? 1 : 0; + + vector res(use1, char1); + res.insert(res.end(), use2, char2); + + vector rest = rec(max1 - use1, max2 - use2, max3, char1, char2, char3); + res.insert(res.end(), rest.begin(), rest.end()); + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} a + * @param {number} b + * @param {number} c + * @return {string} + */ + longestDiverseString(a, b, c) { + const rec = (max1, max2, max3, char1, char2, char3) => { + if (max1 < max2) { + return rec(max2, max1, max3, char2, char1, char3); + } + if (max2 < max3) { + return rec(max1, max3, max2, char1, char3, char2); + } + if (max2 === 0) { + return Array(Math.min(2, max1)).fill(char1); + } + + const use1 = Math.min(2, max1); + const use2 = max1 - use1 >= max2 ? 1 : 0; + + const res = Array(use1).fill(char1).concat(Array(use2).fill(char2)); + return res.concat(rec(max1 - use1, max2 - use2, max3, char1, char2, char3)); + }; + + return rec(a, b, c, 'a', 'b', 'c').join(''); + } +} +``` + +```csharp +public class Solution { + public string LongestDiverseString(int a, int b, int c) { + return string.Join("", Rec(a, b, c, 'a', 'b', 'c')); + } + + private List Rec(int max1, int max2, int max3, char char1, char char2, char char3) { + if (max1 < max2) return Rec(max2, max1, max3, char2, char1, char3); + if (max2 < max3) return Rec(max1, max3, max2, char1, char3, char2); + if (max2 == 0) { + int use = Math.Min(2, max1); + var res = new List(); + for (int i = 0; i < use; i++) res.Add(char1); + return res; + } + + int use1 = Math.Min(2, max1); + int use2 = (max1 - use1 >= max2) ? 1 : 0; + + var result = new List(); + for (int i = 0; i < use1; i++) result.Add(char1); + for (int i = 0; i < use2; i++) result.Add(char2); + + result.AddRange(Rec(max1 - use1, max2 - use2, max3, char1, char2, char3)); + return result; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(n)$ for recursion stack. + * $O(n)$ space for the output string. \ No newline at end of file diff --git a/articles/longest-ideal-subsequence.md b/articles/longest-ideal-subsequence.md new file mode 100644 index 000000000..2162b48ec --- /dev/null +++ b/articles/longest-ideal-subsequence.md @@ -0,0 +1,422 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def longestIdealString(self, s: str, k: int) -> int: + def dfs(i, prev): + if i == len(s): + return 0 + skip = dfs(i + 1, prev) + include = 0 + if prev == "" or abs(ord(s[i]) - ord(prev)) <= k: + include = 1 + dfs(i + 1, s[i]) + return max(skip, include) + + return dfs(0, "") +``` + +```java +public class Solution { + public int longestIdealString(String s, int k) { + return dfs(0, -1, s, k); + } + + private int dfs(int i, int prev, String s, int k) { + if (i == s.length()) { + return 0; + } + int skip = dfs(i + 1, prev, s, k); + int include = 0; + if (prev == -1 || Math.abs(s.charAt(i) - prev) <= k) { + include = 1 + dfs(i + 1, s.charAt(i), s, k); + } + return Math.max(skip, include); + } +} +``` + +```cpp +class Solution { +public: + int longestIdealString(string s, int k) { + return dfs(0, -1, s, k); + } + +private: + int dfs(int i, int prev, const string &s, int k) { + if (i == s.size()) { + return 0; + } + int skip = dfs(i + 1, prev, s, k); + int include = 0; + if (prev == -1 || abs(s[i] - prev) <= k) { + include = 1 + dfs(i + 1, s[i], s, k); + } + return max(skip, include); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + longestIdealString(s, k) { + const dfs = (i, prev) => { + if (i === s.length) { + return 0; + } + const skip = dfs(i + 1, prev); + let include = 0; + if (prev === -1 || Math.abs(s.charCodeAt(i) - prev) <= k) { + include = 1 + dfs(i + 1, s.charCodeAt(i)); + } + return Math.max(skip, include); + }; + + return dfs(0, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def longestIdealString(self, s: str, k: int) -> int: + cache = [[-1] * 27 for _ in range(len(s))] + + def dfs(i, prev): + if i == len(s): + return 0 + if cache[i][prev + 1] != -1: + return cache[i][prev + 1] + + skip = dfs(i + 1, prev) + include = 0 + c = ord(s[i]) - ord('a') + if prev == -1 or abs(c - prev) <= k: + include = 1 + dfs(i + 1, c) + cache[i][prev + 1] = max(skip, include) + return cache[i][prev + 1] + + return dfs(0, -1) +``` + +```java +public class Solution { + private int[][] dp; + + public int longestIdealString(String s, int k) { + dp = new int[s.length()][27]; + for (int i = 0; i < s.length(); i++) { + for (int j = 0; j < 27; j++) { + dp[i][j] = -1; + } + } + return dfs(0, -1, s, k); + } + + private int dfs(int i, int prev, String s, int k) { + if (i == s.length()) { + return 0; + } + if (dp[i][prev + 1] != -1) { + return dp[i][prev + 1]; + } + int skip = dfs(i + 1, prev, s, k); + int include = 0; + if (prev == -1 || Math.abs(s.charAt(i) - ('a' + prev)) <= k) { + include = 1 + dfs(i + 1, s.charAt(i) - 'a', s, k); + } + dp[i][prev + 1] = Math.max(skip, include); + return Math.max(skip, include); + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + +public: + int longestIdealString(string s, int k) { + dp = vector>(s.size(), vector(27, -1)); + return dfs(0, -1, s, k); + } + +private: + int dfs(int i, int prev, const string &s, int k) { + if (i == s.size()) { + return 0; + } + if (dp[i][prev + 1] != -1) { + return dp[i][prev + 1]; + } + int skip = dfs(i + 1, prev, s, k); + int include = 0; + if (prev == -1 || abs(s[i] - ('a' + prev)) <= k) { + include = 1 + dfs(i + 1, s[i] - 'a', s, k); + } + dp[i][prev + 1] = max(skip, include); + return max(skip, include); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + longestIdealString(s, k) { + const dp = Array.from({ length: s.length }, () => Array(27).fill(-1)); + + const dfs = (i, prev) => { + if (i === s.length) { + return 0; + } + if (dp[i][prev + 1] !== -1) { + return dp[i][prev + 1]; + } + const skip = dfs(i + 1, prev); + let include = 0; + if (prev === -1 || Math.abs(s.charCodeAt(i) - ('a'.charCodeAt(0) + prev)) <= k) { + include = 1 + dfs(i + 1, s.charCodeAt(i) - 'a'.charCodeAt(0)); + } + dp[i][prev + 1] = Math.max(skip, include); + return Math.max(skip, include); + }; + + return dfs(0, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def longestIdealString(self, s: str, k: int) -> int: + dp = [[0] * 26 for _ in range(len(s) + 1)] + + for i in range(1, len(s) + 1): + curr = ord(s[i - 1]) - ord('a') + for prev in range(26): + dp[i][prev] = max(dp[i][prev], dp[i - 1][prev]) + if abs(curr - prev) <= k: + dp[i][curr] = max(dp[i][curr], 1 + dp[i - 1][prev]) + + return max(dp[len(s)]) +``` + +```java +public class Solution { + public int longestIdealString(String s, int k) { + int n = s.length(); + int[][] dp = new int[n + 1][26]; + + for (int i = 1; i <= n; i++) { + int curr = s.charAt(i - 1) - 'a'; + for (int prev = 0; prev < 26; prev++) { + dp[i][prev] = Math.max(dp[i][prev], dp[i - 1][prev]); + if (Math.abs(curr - prev) <= k) { + dp[i][curr] = Math.max(dp[i][curr], 1 + dp[i - 1][prev]); + } + } + } + + int max = 0; + for (int val : dp[n]) { + max = Math.max(max, val); + } + return max; + } +} +``` + +```cpp +class Solution { +public: + int longestIdealString(string s, int k) { + int n = s.size(); + vector> dp(n + 1, vector(26, 0)); + + for (int i = 1; i <= n; i++) { + int curr = s[i - 1] - 'a'; + for (int prev = 0; prev < 26; prev++) { + dp[i][prev] = max(dp[i][prev], dp[i - 1][prev]); + if (abs(curr - prev) <= k) { + dp[i][curr] = max(dp[i][curr], 1 + dp[i - 1][prev]); + } + } + } + + return *max_element(dp[n].begin(), dp[n].end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + longestIdealString(s, k) { + const n = s.length; + const dp = Array.from({ length: n + 1 }, () => Array(26).fill(0)); + + for (let i = 1; i <= n; i++) { + const curr = s.charCodeAt(i - 1) - 'a'.charCodeAt(0); + for (let prev = 0; prev < 26; prev++) { + dp[i][prev] = Math.max(dp[i][prev], dp[i - 1][prev]); + if (Math.abs(curr - prev) <= k) { + dp[i][curr] = Math.max(dp[i][curr], 1 + dp[i - 1][prev]); + } + } + } + return Math.max(...dp[n]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def longestIdealString(self, s: str, k: int) -> int: + dp = [0] * 26 + + for c in s: + curr = ord(c) - ord('a') + longest = 1 + for prev in range(26): + if abs(curr - prev) <= k: + longest = max(longest, 1 + dp[prev]) + dp[curr] = max(dp[curr], longest) + + return max(dp) +``` + +```java +public class Solution { + public int longestIdealString(String s, int k) { + int[] dp = new int[26]; + + for (char c : s.toCharArray()) { + int curr = c - 'a'; // 0-25 + int longest = 1; + for (int prev = 0; prev < 26; prev++) { + if (Math.abs(curr - prev) <= k) { + longest = Math.max(longest, 1 + dp[prev]); + } + } + dp[curr] = Math.max(dp[curr], longest); + } + + int max = 0; + for (int val : dp) { + max = Math.max(max, val); + } + return max; + } +} +``` + +```cpp +class Solution { +public: + int longestIdealString(string s, int k) { + vector dp(26, 0); + + for (char c : s) { + int curr = c - 'a'; + int longest = 1; + for (int prev = 0; prev < 26; prev++) { + if (abs(curr - prev) <= k) { + longest = max(longest, 1 + dp[prev]); + } + } + dp[curr] = max(dp[curr], longest); + } + + return *max_element(dp.begin(), dp.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + longestIdealString(s, k) { + const dp = Array(26).fill(0); + + for (const c of s) { + const curr = c.charCodeAt(0) - 'a'.charCodeAt(0); + let longest = 1; + for (let prev = 0; prev < 26; prev++) { + if (Math.abs(curr - prev) <= k) { + longest = Math.max(longest, 1 + dp[prev]); + } + } + dp[curr] = Math.max(dp[curr], longest); + } + + return Math.max(...dp); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we have at most 26 different characters. \ No newline at end of file diff --git a/articles/longest-increasing-path-in-matrix.md b/articles/longest-increasing-path-in-matrix.md new file mode 100644 index 000000000..e529b1ded --- /dev/null +++ b/articles/longest-increasing-path-in-matrix.md @@ -0,0 +1,1022 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def longestIncreasingPath(self, matrix: List[List[int]]) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + directions = [[-1, 0], [1, 0], [0, -1], [0, 1]] + + def dfs(r, c, prevVal): + if (min(r, c) < 0 or r >= ROWS or + c >= COLS or matrix[r][c] <= prevVal + ): + return 0 + + res = 1 + for d in directions: + res = max(res, 1 + dfs(r + d[0], c + d[1], matrix[r][c])) + return res + + LIP = 0 + for r in range(ROWS): + for c in range(COLS): + LIP = max(LIP, dfs(r, c, float('-inf'))) + return LIP +``` + +```java +public class Solution { + int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; + + private int dfs(int[][] matrix, int r, int c, int prevVal) { + int ROWS = matrix.length, COLS = matrix[0].length; + if (r < 0 || r >= ROWS || c < 0 || + c >= COLS || matrix[r][c] <= prevVal) { + return 0; + } + + int res = 1; + for (int[] d : directions) { + res = Math.max(res, 1 + dfs(matrix, r + d[0], + c + d[1], matrix[r][c])); + } + return res; + } + + public int longestIncreasingPath(int[][] matrix) { + int ROWS = matrix.length, COLS = matrix[0].length; + int LIP = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + LIP = Math.max(LIP, dfs(matrix, r, c, Integer.MIN_VALUE)); + } + } + return LIP; + } +} +``` + +```cpp +class Solution { +public: + vector> directions = {{-1, 0}, {1, 0}, + {0, -1}, {0, 1}}; + + int dfs(vector>& matrix, int r, int c, int prevVal) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + if (r < 0 || r >= ROWS || c < 0 || + c >= COLS || matrix[r][c] <= prevVal) + return 0; + + int res = 1; + for (auto d : directions) + res = max(res, 1 + dfs(matrix, r + d[0], + c + d[1], matrix[r][c])); + return res; + } + + int longestIncreasingPath(vector>& matrix) { + int ROWS = matrix.size(), COLS = matrix[0].size(), LIP = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + LIP = max(LIP, dfs(matrix, r, c, INT_MIN)); + } + } + return LIP; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number} + */ + longestIncreasingPath(matrix) { + const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]; + const ROWS = matrix.length, COLS = matrix[0].length; + + const dfs = (r, c, prevVal) => { + if (r < 0 || r >= ROWS || c < 0 || + c >= COLS || matrix[r][c] <= prevVal) { + return 0; + } + + let res = 1; + for (let d of directions) { + res = Math.max(res, 1 + dfs(r + d[0], + c + d[1], matrix[r][c])); + } + return res; + }; + + let LIP = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + LIP = Math.max(LIP, dfs(r, c, -Infinity)); + } + } + return LIP; + } +} +``` + +```csharp +public class Solution { + private static int[][] directions = new int[][] { + new int[] {-1, 0}, new int[] {1, 0}, + new int[] {0, -1}, new int[] {0, 1} + }; + + private int Dfs(int[][] matrix, int r, int c, int prevVal) { + int ROWS = matrix.Length, COLS = matrix[0].Length; + if (r < 0 || r >= ROWS || c < 0 || + c >= COLS || matrix[r][c] <= prevVal) { + return 0; + } + + int res = 1; + foreach (var dir in directions) { + res = Math.Max(res, 1 + Dfs(matrix, r + dir[0], + c + dir[1], matrix[r][c])); + } + return res; + } + + public int LongestIncreasingPath(int[][] matrix) { + int ROWS = matrix.Length, COLS = matrix[0].Length; + int LIP = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + LIP = Math.Max(LIP, Dfs(matrix, r, c, int.MinValue)); + } + } + return LIP; + } +} +``` + +```go +func longestIncreasingPath(matrix [][]int) int { + rows, cols := len(matrix), len(matrix[0]) + directions := [][]int{{-1, 0}, {1, 0}, {0, -1}, {0, 1}} + + var dfs func(r, c, prevVal int) int + dfs = func(r, c, prevVal int) int { + if r < 0 || r >= rows || c < 0 || c >= cols || + matrix[r][c] <= prevVal { + return 0 + } + + res := 1 + for _, d := range directions { + res = max(res, 1 + dfs(r + d[0], c + d[1], matrix[r][c])) + } + return res + } + + LIP := 0 + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + LIP = max(LIP, dfs(r, c, -1<<31)) + } + } + return LIP +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + private val directions = arrayOf( + intArrayOf(-1, 0), intArrayOf(1, 0), + intArrayOf(0, -1), intArrayOf(0, 1) + ) + + fun longestIncreasingPath(matrix: Array): Int { + val rows = matrix.size + val cols = matrix[0].size + + fun dfs(r: Int, c: Int, prevVal: Int): Int { + if (r < 0 || r >= rows || c < 0 || c >= cols || + matrix[r][c] <= prevVal) { + return 0 + } + + var res = 1 + for (d in directions) { + res = maxOf(res, 1 + dfs(r + d[0], c + d[1], matrix[r][c])) + } + return res + } + + var LIP = 0 + for (r in 0 until rows) { + for (c in 0 until cols) { + LIP = maxOf(LIP, dfs(r, c, Int.MIN_VALUE)) + } + } + return LIP + } +} +``` + +```swift +class Solution { + func longestIncreasingPath(_ matrix: [[Int]]) -> Int { + let rows = matrix.count, cols = matrix[0].count + let directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] + + func dfs(_ r: Int, _ c: Int, _ prevVal: Int) -> Int { + if r < 0 || c < 0 || r >= rows || c >= cols || matrix[r][c] <= prevVal { + return 0 + } + + var res = 1 + for (dr, dc) in directions { + res = max(res, 1 + dfs(r + dr, c + dc, matrix[r][c])) + } + return res + } + + var lip = 0 + for r in 0.. Where $m$ is the number of rows and $n$ is the number of columns in the given $matrix$. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def longestIncreasingPath(self, matrix: List[List[int]]) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + dp = {} # (r, c) -> LIP + + def dfs(r, c, prevVal): + if (r < 0 or r == ROWS or c < 0 or + c == COLS or matrix[r][c] <= prevVal + ): + return 0 + if (r, c) in dp: + return dp[(r, c)] + + res = 1 + res = max(res, 1 + dfs(r + 1, c, matrix[r][c])) + res = max(res, 1 + dfs(r - 1, c, matrix[r][c])) + res = max(res, 1 + dfs(r, c + 1, matrix[r][c])) + res = max(res, 1 + dfs(r, c - 1, matrix[r][c])) + dp[(r, c)] = res + return res + + for r in range(ROWS): + for c in range(COLS): + dfs(r, c, -1) + return max(dp.values()) +``` + +```java +public class Solution { + int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; + int[][] dp; + + private int dfs(int[][] matrix, int r, int c, int prevVal) { + int ROWS = matrix.length, COLS = matrix[0].length; + if (r < 0 || r >= ROWS || c < 0 || + c >= COLS || matrix[r][c] <= prevVal) { + return 0; + } + if (dp[r][c] != -1) return dp[r][c]; + + int res = 1; + for (int[] d : directions) { + res = Math.max(res, 1 + dfs(matrix, r + d[0], + c + d[1], matrix[r][c])); + } + return dp[r][c] = res; + } + + public int longestIncreasingPath(int[][] matrix) { + int ROWS = matrix.length, COLS = matrix[0].length; + int LIP = 0; + dp = new int[ROWS][COLS]; + for (int i = 0; i < ROWS; i++) { + for (int j = 0; j < COLS; j++) { + dp[i][j] = -1; + } + } + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + LIP = Math.max(LIP, dfs(matrix, r, c, Integer.MIN_VALUE)); + } + } + return LIP; + } +} +``` + +```cpp +class Solution { +public: + vector> directions = {{-1, 0}, {1, 0}, + {0, -1}, {0, 1}}; + vector> dp; + + int dfs(vector>& matrix, int r, int c, int prevVal) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + if (r < 0 || r >= ROWS || c < 0 || + c >= COLS || matrix[r][c] <= prevVal) { + return 0; + } + if (dp[r][c] != -1) return dp[r][c]; + + int res = 1; + for (vector d : directions) { + res = max(res, 1 + dfs(matrix, r + d[0], + c + d[1], matrix[r][c])); + } + dp[r][c] = res; + return res; + } + + int longestIncreasingPath(vector>& matrix) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + dp = vector>(ROWS, vector(COLS, -1)); + int LIP = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + LIP = max(LIP, dfs(matrix, r, c, INT_MIN)); + } + } + return LIP; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number} + */ + longestIncreasingPath(matrix) { + const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]; + const ROWS = matrix.length, COLS = matrix[0].length; + let dp = Array.from({ length: ROWS }, () => + Array(COLS).fill(-1)); + + const dfs = (r, c, prevVal) => { + if (r < 0 || r >= ROWS || c < 0 || + c >= COLS || matrix[r][c] <= prevVal) { + return 0; + } + if (dp[r][c] !== -1) return dp[r][c]; + + let res = 1; + for (let d of directions) { + res = Math.max(res, 1 + dfs(r + d[0], + c + d[1], matrix[r][c])); + } + dp[r][c] = res; + return res; + }; + + let LIP = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + LIP = Math.max(LIP, dfs(r, c, -Infinity)); + } + } + return LIP; + } +} +``` + +```csharp +public class Solution { + int[][] directions = new int[][] { + new int[] {-1, 0}, new int[] {1, 0}, + new int[] {0, -1}, new int[] {0, 1} + }; + int[,] dp; + + private int Dfs(int[][] matrix, int r, int c, int prevVal) { + int ROWS = matrix.Length, COLS = matrix[0].Length; + if (r < 0 || r >= ROWS || c < 0 || + c >= COLS || matrix[r][c] <= prevVal) { + return 0; + } + if (dp[r, c] != -1) return dp[r, c]; + + int res = 1; + foreach (int[] d in directions) { + res = Math.Max(res, 1 + Dfs(matrix, r + d[0], + c + d[1], matrix[r][c])); + } + + dp[r, c] = res; + return res; + } + + public int LongestIncreasingPath(int[][] matrix) { + int ROWS = matrix.Length, COLS = matrix[0].Length; + dp = new int[ROWS, COLS]; + + for (int i = 0; i < ROWS; i++) { + for (int j = 0; j < COLS; j++) { + dp[i, j] = -1; + } + } + + int LIP = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + LIP = Math.Max(LIP, Dfs(matrix, r, c, int.MinValue)); + } + } + return LIP; + } +} +``` + +```go +func longestIncreasingPath(matrix [][]int) int { + rows, cols := len(matrix), len(matrix[0]) + dp := make([][]int, rows) + for i := range dp { + dp[i] = make([]int, cols) + } + + var dfs func(r, c, prevVal int) int + dfs = func(r, c, prevVal int) int { + if r < 0 || r >= rows || c < 0 || c >= cols || + matrix[r][c] <= prevVal { + return 0 + } + if dp[r][c] != 0 { + return dp[r][c] + } + + res := 1 + res = max(res, 1 + dfs(r+1, c, matrix[r][c])) + res = max(res, 1 + dfs(r-1, c, matrix[r][c])) + res = max(res, 1 + dfs(r, c+1, matrix[r][c])) + res = max(res, 1 + dfs(r, c-1, matrix[r][c])) + dp[r][c] = res + return res + } + + maxPath := 0 + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + maxPath = max(maxPath, dfs(r, c, -1)) + } + } + return maxPath +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun longestIncreasingPath(matrix: Array): Int { + val rows = matrix.size + val cols = matrix[0].size + val dp = Array(rows) { IntArray(cols) } + + fun dfs(r: Int, c: Int, prevVal: Int): Int { + if (r < 0 || r >= rows || c < 0 || c >= cols || + matrix[r][c] <= prevVal) { + return 0 + } + if (dp[r][c] != 0) { + return dp[r][c] + } + + var res = 1 + res = maxOf(res, 1 + dfs(r + 1, c, matrix[r][c])) + res = maxOf(res, 1 + dfs(r - 1, c, matrix[r][c])) + res = maxOf(res, 1 + dfs(r, c + 1, matrix[r][c])) + res = maxOf(res, 1 + dfs(r, c - 1, matrix[r][c])) + dp[r][c] = res + return res + } + + var maxPath = 0 + for (r in 0 until rows) { + for (c in 0 until cols) { + maxPath = maxOf(maxPath, dfs(r, c, -1)) + } + } + return maxPath + } +} +``` + +```swift +class Solution { + func longestIncreasingPath(_ matrix: [[Int]]) -> Int { + let rows = matrix.count, cols = matrix[0].count + var dp = Array(repeating: Array(repeating: -1, count: cols), count: rows) + + func dfs(_ r: Int, _ c: Int, _ prevVal: Int) -> Int { + if r < 0 || r >= rows || c < 0 || c >= cols || matrix[r][c] <= prevVal { + return 0 + } + if dp[r][c] != -1 { + return dp[r][c] + } + + var res = 1 + res = max(res, 1 + dfs(r + 1, c, matrix[r][c])) + res = max(res, 1 + dfs(r - 1, c, matrix[r][c])) + res = max(res, 1 + dfs(r, c + 1, matrix[r][c])) + res = max(res, 1 + dfs(r, c - 1, matrix[r][c])) + + dp[r][c] = res + return res + } + + var maxPath = 0 + for r in 0.. Where $m$ is the number of rows and $n$ is the number of columns in the given $matrix$. + +--- + +## 3. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def longestIncreasingPath(self, matrix: List[List[int]]) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + directions = [[-1, 0], [1, 0], [0, -1], [0, 1]] + indegree = [[0] * COLS for _ in range(ROWS)] + + for r in range(ROWS): + for c in range(COLS): + for d in directions: + nr, nc = d[0] + r, d[1] + c + if (0 <= nr < ROWS and 0 <= nc < COLS and + matrix[nr][nc] < matrix[r][c] + ): + indegree[r][c] += 1 + + q = deque() + for r in range(ROWS): + for c in range(COLS): + if indegree[r][c] == 0: + q.append([r, c]) + + LIS = 0 + while q: + for _ in range(len(q)): + r, c = q.popleft() + for d in directions: + nr, nc = r + d[0], c + d[1] + if (0 <= nr < ROWS and 0 <= nc < COLS and + matrix[nr][nc] > matrix[r][c] + ): + indegree[nr][nc] -= 1 + if indegree[nr][nc] == 0: + q.append([nr, nc]) + LIS += 1 + return LIS +``` + +```java +public class Solution { + public int longestIncreasingPath(int[][] matrix) { + int ROWS = matrix.length, COLS = matrix[0].length; + int[][] indegree = new int[ROWS][COLS]; + int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; + + for (int r = 0; r < ROWS; ++r) { + for (int c = 0; c < COLS; ++c) { + for (int[] d : directions) { + int nr = r + d[0], nc = c + d[1]; + if (nr >= 0 && nr < ROWS && nc >= 0 && + nc < COLS && matrix[nr][nc] < matrix[r][c]) { + indegree[r][c]++; + } + } + } + } + + Queue q = new LinkedList<>(); + for (int r = 0; r < ROWS; ++r) { + for (int c = 0; c < COLS; ++c) { + if (indegree[r][c] == 0) { + q.offer(new int[]{r, c}); + } + } + } + + int LIS = 0; + while (!q.isEmpty()) { + int size = q.size(); + for (int i = 0; i < size; ++i) { + int[] node = q.poll(); + int r = node[0], c = node[1]; + for (int[] d : directions) { + int nr = r + d[0], nc = c + d[1]; + if (nr >= 0 && nr < ROWS && nc >= 0 && + nc < COLS && matrix[nr][nc] > matrix[r][c]) { + if (--indegree[nr][nc] == 0) { + q.offer(new int[]{nr, nc}); + } + } + } + } + LIS++; + } + return LIS; + } +} +``` + +```cpp +class Solution { +public: + int longestIncreasingPath(vector>& matrix) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + vector> indegree(ROWS, vector(COLS, 0)); + vector> directions = {{-1, 0}, {1, 0}, + {0, -1}, {0, 1}}; + + for (int r = 0; r < ROWS; ++r) { + for (int c = 0; c < COLS; ++c) { + for (auto& d : directions) { + int nr = r + d[0], nc = c + d[1]; + if (nr >= 0 && nr < ROWS && nc >= 0 && + nc < COLS && matrix[nr][nc] < matrix[r][c]) { + indegree[r][c]++; + } + } + } + } + + queue> q; + for (int r = 0; r < ROWS; ++r) { + for (int c = 0; c < COLS; ++c) { + if (indegree[r][c] == 0) { + q.push({r, c}); + } + } + } + + int LIS = 0; + while (!q.empty()) { + int size = q.size(); + for (int i = 0; i < size; ++i) { + auto [r, c] = q.front(); + q.pop(); + for (auto& d : directions) { + int nr = r + d[0], nc = c + d[1]; + if (nr >= 0 && nr < ROWS && nc >= 0 && + nc < COLS && matrix[nr][nc] > matrix[r][c]) { + if (--indegree[nr][nc] == 0) { + q.push({nr, nc}); + } + } + } + } + LIS++; + } + return LIS; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number} + */ + longestIncreasingPath(matrix) { + const ROWS = matrix.length, COLS = matrix[0].length; + const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]]; + let indegree = Array.from({ length: ROWS }, () => + Array(COLS).fill(0)); + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + for (const [dr, dc] of directions) { + const nr = r + dr, nc = c + dc; + if (nr >= 0 && nr < ROWS && nc >= 0 && + nc < COLS && matrix[nr][nc] < matrix[r][c]) { + indegree[r][c]++; + } + } + } + } + + let q = new Queue(); + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (indegree[r][c] === 0) { + q.push([r, c]); + } + } + } + + let LIS = 0; + while (!q.isEmpty()) { + const size = q.size(); + for (let i = 0; i < size; i++) { + const [r, c] = q.pop(); + for (const [dr, dc] of directions) { + const nr = r + dr, nc = c + dc; + if (nr >= 0 && nr < ROWS && nc >= 0 && + nc < COLS && matrix[nr][nc] > matrix[r][c]) { + indegree[nr][nc]--; + if (indegree[nr][nc] === 0) { + q.push([nr, nc]); + } + } + } + } + LIS++; + } + return LIS; + } +} +``` + +```csharp +public class Solution { + public int LongestIncreasingPath(int[][] matrix) { + int ROWS = matrix.Length, COLS = matrix[0].Length; + int[][] indegree = new int[ROWS][]; + for (int i = 0; i < ROWS; i++) { + indegree[i] = new int[COLS]; + } + int[][] directions = new int[][] { + new int[] { -1, 0 }, new int[] { 1, 0 }, + new int[] { 0, -1 }, new int[] { 0, 1 } + }; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + foreach (var d in directions) { + int nr = r + d[0], nc = c + d[1]; + if (nr >= 0 && nr < ROWS && nc >= 0 && + nc < COLS && matrix[nr][nc] < matrix[r][c]) { + indegree[r][c]++; + } + } + } + } + + Queue q = new Queue(); + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (indegree[r][c] == 0) { + q.Enqueue(new int[] { r, c }); + } + } + } + + int LIS = 0; + while (q.Count > 0) { + int size = q.Count; + for (int i = 0; i < size; i++) { + int[] node = q.Dequeue(); + int r = node[0], c = node[1]; + foreach (var d in directions) { + int nr = r + d[0], nc = c + d[1]; + if (nr >= 0 && nr < ROWS && nc >= 0 && + nc < COLS && matrix[nr][nc] > matrix[r][c]) { + if (--indegree[nr][nc] == 0) { + q.Enqueue(new int[] { nr, nc }); + } + } + } + } + LIS++; + } + return LIS; + } +} +``` + +```go +func longestIncreasingPath(matrix [][]int) int { + rows, cols := len(matrix), len(matrix[0]) + indegree := make([][]int, rows) + for i := range indegree { + indegree[i] = make([]int, cols) + } + + directions := [][]int{{-1, 0}, {1, 0}, {0, -1}, {0, 1}} + + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + for _, d := range directions { + nr, nc := r + d[0], c + d[1] + if nr >= 0 && nr < rows && nc >= 0 && nc < cols && + matrix[nr][nc] < matrix[r][c] { + indegree[r][c]++ + } + } + } + } + + queue := [][]int{} + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + if indegree[r][c] == 0 { + queue = append(queue, []int{r, c}) + } + } + } + + lis := 0 + for len(queue) > 0 { + size := len(queue) + for i := 0; i < size; i++ { + node := queue[0] + queue = queue[1:] + r, c := node[0], node[1] + for _, d := range directions { + nr, nc := r + d[0], c + d[1] + if nr >= 0 && nr < rows && nc >= 0 && nc < cols && + matrix[nr][nc] > matrix[r][c] { + indegree[nr][nc]-- + if indegree[nr][nc] == 0 { + queue = append(queue, []int{nr, nc}) + } + } + } + } + lis++ + } + + return lis +} +``` + +```kotlin +class Solution { + fun longestIncreasingPath(matrix: Array): Int { + val rows = matrix.size + val cols = matrix[0].size + val indegree = Array(rows) { IntArray(cols) } + val directions = arrayOf(intArrayOf(-1, 0), intArrayOf(1, 0), + intArrayOf(0, -1), intArrayOf(0, 1)) + + for (r in 0 until rows) { + for (c in 0 until cols) { + for (d in directions) { + val nr = r + d[0] + val nc = c + d[1] + if (nr in 0 until rows && nc in 0 until cols && + matrix[nr][nc] < matrix[r][c]) { + indegree[r][c]++ + } + } + } + } + + val queue: Queue = LinkedList() + for (r in 0 until rows) { + for (c in 0 until cols) { + if (indegree[r][c] == 0) { + queue.offer(intArrayOf(r, c)) + } + } + } + + var lis = 0 + while (queue.isNotEmpty()) { + repeat(queue.size) { + val (r, c) = queue.poll() + for (d in directions) { + val nr = r + d[0] + val nc = c + d[1] + if (nr in 0 until rows && nc in 0 until cols && + matrix[nr][nc] > matrix[r][c]) { + if (--indegree[nr][nc] == 0) { + queue.offer(intArrayOf(nr, nc)) + } + } + } + } + lis++ + } + + return lis + } +} +``` + +```swift +class Solution { + func longestIncreasingPath(_ matrix: [[Int]]) -> Int { + let rows = matrix.count, cols = matrix[0].count + let directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] + var indegree = Array(repeating: Array(repeating: 0, count: cols), count: rows) + + for r in 0..= 0 && nr < rows && nc >= 0 && nc < cols && + matrix[nr][nc] < matrix[r][c]) { + indegree[r][c] += 1 + } + } + } + } + + var q = Deque<(Int, Int)>() + for r in 0..= 0 && nr < rows && nc >= 0 && nc < cols && + matrix[nr][nc] > matrix[r][c]) { + indegree[nr][nc] -= 1 + if indegree[nr][nc] == 0 { + q.append((nr, nc)) + } + } + } + } + LIS += 1 + } + return LIS + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the given $matrix$. \ No newline at end of file diff --git a/articles/longest-increasing-subsequence-ii.md b/articles/longest-increasing-subsequence-ii.md new file mode 100644 index 000000000..1d7a0030e --- /dev/null +++ b/articles/longest-increasing-subsequence-ii.md @@ -0,0 +1,746 @@ +## 1. Brute Force (Recursion) + +::tabs-start + +```python +class Solution: + def lengthOfLIS(self, nums: List[int], k: int) -> int: + def dfs(i): + res = 1 + for j in range(i + 1, len(nums)): + if nums[j] <= nums[i]: + continue + if nums[j] - nums[i] <= k: + res = max(res, 1 + dfs(j)) + return res + + res = 0 + for i in range(len(nums)): + res = max(res, dfs(i)) + return res +``` + +```java +public class Solution { + public int lengthOfLIS(int[] nums, int k) { + int n = nums.length; + int res = 0; + + for (int i = 0; i < n; i++) { + res = Math.max(res, dfs(nums, k, i)); + } + + return res; + } + + private int dfs(int[] nums, int k, int i) { + int res = 1; + + for (int j = i + 1; j < nums.length; j++) { + if (nums[j] <= nums[i]) continue; + if (nums[j] - nums[i] <= k) { + res = Math.max(res, 1 + dfs(nums, k, j)); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int lengthOfLIS(vector& nums, int k) { + int n = nums.size(); + int res = 0; + + for (int i = 0; i < n; i++) { + res = max(res, dfs(nums, k, i)); + } + + return res; + } + +private: + int dfs(vector& nums, int k, int i) { + int res = 1; + + for (int j = i + 1; j < nums.size(); j++) { + if (nums[j] <= nums[i]) continue; + if (nums[j] - nums[i] <= k) { + res = max(res, 1 + dfs(nums, k, j)); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + lengthOfLIS(nums, k) { + const dfs = (i) => { + let res = 1; + + for (let j = i + 1; j < nums.length; j++) { + if (nums[j] <= nums[i]) continue; + if (nums[j] - nums[i] <= k) { + res = Math.max(res, 1 + dfs(j)); + } + } + + return res; + }; + + let res = 0; + for (let i = 0; i < nums.length; i++) { + res = Math.max(res, dfs(i)); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def lengthOfLIS(self, nums: List[int], k: int) -> int: + n, res = len(nums), 0 + dp = [1] * n + for i in range(n): + for j in range(i): + if nums[j] >= nums[i]: + continue + if nums[i] - nums[j] <= k: + dp[i] = max(dp[i], 1 + dp[j]) + res = max(res, dp[i]) + return res +``` + +```java +public class Solution { + public int lengthOfLIS(int[] nums, int k) { + int n = nums.length, res = 0; + int[] dp = new int[n]; + Arrays.fill(dp, 1); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + if (nums[j] >= nums[i]) continue; + if (nums[i] - nums[j] <= k) { + dp[i] = Math.max(dp[i], 1 + dp[j]); + } + } + res = Math.max(res, dp[i]); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int lengthOfLIS(vector& nums, int k) { + int n = nums.size(), res = 0; + vector dp(n, 1); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + if (nums[j] >= nums[i]) continue; + if (nums[i] - nums[j] <= k) { + dp[i] = max(dp[i], 1 + dp[j]); + } + } + res = max(res, dp[i]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + lengthOfLIS(nums, k) { + const n = nums.length; + let res = 0; + const dp = new Array(n).fill(1); + + for (let i = 0; i < n; i++) { + for (let j = 0; j < i; j++) { + if (nums[j] >= nums[i]) continue; + if (nums[i] - nums[j] <= k) { + dp[i] = Math.max(dp[i], 1 + dp[j]); + } + } + res = Math.max(res, dp[i]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming + Segment Tree (Coordinate Compression) + +::tabs-start + +```python +class SegmentTree: + def __init__(self, N): + self.n = N + while (self.n & (self.n - 1)) != 0: + self.n += 1 + self.tree = [0] * (2 * self.n) + + def update(self, i, val): + if val <= self.tree[self.n + i]: + return + self.tree[self.n + i] = val + j = (self.n + i) >> 1 + while j >= 1: + self.tree[j] = max(self.tree[j << 1], self.tree[j << 1 | 1]) + j >>= 1 + + def query(self, ql, qh): + l = ql + self.n + r = qh + self.n + 1 + res = 0 + while l < r: + if l & 1: + res = max(res, self.tree[l]) + l += 1 + if r & 1: + r -= 1 + res = max(res, self.tree[r]) + l >>= 1 + r >>= 1 + return res + + +class Solution: + def lengthOfLIS(self, nums: List[int], k: int) -> int: + n = len(nums) + mp = {} + tmp = set([0]) + for num in nums: + if num - k > 0: + tmp.add(num - k) + if num - 1 > 0: + tmp.add(num - 1) + tmp.add(num) + + index = 0 + for value in sorted(tmp): + mp[value] = index + index += 1 + + ST = SegmentTree(index) + res = 0 + for num in nums: + l = mp.get(num - k, 0) + r = mp.get(num - 1, 0) + curr = ST.query(l, r) + 1 + res = max(res, curr) + ST.update(mp[num], curr) + + return res +``` + +```java +class SegmentTree { + private int n; + private int[] tree; + + public SegmentTree(int N) { + this.n = N; + while (Integer.bitCount(n) != 1) { + n++; + } + tree = new int[2 * n]; + } + + public void update(int i, int val) { + if (val <= tree[n + i]) return; + tree[n + i] = val; + for (int j = (n + i) >> 1; j >= 1; j >>= 1) { + tree[j] = Math.max(tree[j << 1], tree[(j << 1) + 1]); + } + } + + public int query(int ql, int qh) { + int l = ql + n, r = qh + n + 1, res = 0; + while (l < r) { + if ((l & 1) == 1) res = Math.max(res, tree[l++]); + if ((r & 1) == 1) res = Math.max(res, tree[--r]); + l >>= 1; + r >>= 1; + } + return res; + } +} + +public class Solution { + public int lengthOfLIS(int[] nums, int k) { + int n = nums.length; + TreeSet tmp = new TreeSet<>(); + tmp.add(0); + for (int num : nums) { + if (num - k > 0) tmp.add(num - k); + if (num - 1 > 0) tmp.add(num - 1); + tmp.add(num); + } + + Map mp = new HashMap<>(); + int index = 0; + for (int val : tmp) { + mp.put(val, index++); + } + + SegmentTree ST = new SegmentTree(index); + int res = 0; + for (int num : nums) { + int l = mp.getOrDefault(num - k, 0); + int r = mp.getOrDefault(num - 1, 0); + int curr = ST.query(l, r) + 1; + res = Math.max(res, curr); + ST.update(mp.get(num), curr); + } + return res; + } +} +``` + +```cpp +class SegmentTree { +public: + int n; + vector tree; + + SegmentTree(int N) { + this->n = N; + while (__builtin_popcount(n) != 1) { + n++; + } + tree.resize(2 * n, 0); + } + + void update(int i, int val) { + if (val <= tree[n + i]) return; + tree[n + i] = val; + for (int j = (n + i) >> 1; j >= 1; j >>= 1) { + tree[j] = max(tree[j << 1], tree[(j << 1) + 1]); + } + } + + int query(int ql, int qh) { + int l = ql + n, r = qh + n + 1, res = 0; + while (l < r) { + if (l & 1) res = max(res, tree[l++]); + if (r & 1) res = max(res, tree[--r]); + l >>= 1; + r >>= 1; + } + return res; + } +}; + +class Solution { +public: + int lengthOfLIS(vector& nums, int k) { + int n = nums.size(); + unordered_map mp; + set tmp = {0}; + + for (int num : nums) { + if (num - k > 0) tmp.insert(num - k); + if (num - 1 > 0) tmp.insert(num - 1); + tmp.insert(num); + } + + int index = 0; + for (const int& val : tmp) { + mp[val] = index++; + } + + SegmentTree ST(index); + int ans = 0; + for (int& num : nums) { + int l = mp[num - k]; + int r = mp[num - 1]; + int curr = ST.query(l, r) + 1; + ans = max(ans, curr); + ST.update(mp[num], curr); + } + + return ans; + } +}; +``` + +```javascript +class SegmentTree { + /** + * @constructor + * @param {number} N + */ + constructor(N) { + this.n = N; + while ((this.n & (this.n - 1)) !== 0) { + this.n++; + } + this.tree = Array(2 * this.n).fill(0); + } + + /** + * @param {number} i + * @param {number} val + * @return {void} + */ + update(i, val) { + if (val <= this.tree[this.n + i]) return; + this.tree[this.n + i] = val; + for (let j = (this.n + i) >> 1; j >= 1; j >>= 1) { + this.tree[j] = Math.max(this.tree[j << 1], this.tree[(j << 1) + 1]); + } + } + + /** + * @param {number} ql + * @param {number} qh + * @return {number} + */ + query(ql, qh) { + let l = ql + this.n, r = qh + this.n + 1, res = 0; + while (l < r) { + if (l & 1) res = Math.max(res, this.tree[l++]); + if (r & 1) res = Math.max(res, this.tree[--r]); + l >>= 1; + r >>= 1; + } + return res; + } +} + +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + lengthOfLIS(nums, k) { + const n = nums.length; + const tmp = new Set([0]); + for (const num of nums) { + if (num - k > 0) tmp.add(num - k); + if (num - 1 > 0) tmp.add(num - 1); + tmp.add(num); + } + + const mp = new Map(); + let index = 0; + Array.from(tmp).sort((a, b) => a - b).forEach(val => mp.set(val, index++)); + + const ST = new SegmentTree(index); + let ans = 0; + for (const num of nums) { + const l = mp.has(num - k) ? mp.get(num - k) : 0; + const r = mp.has(num - 1) ? mp.get(num - 1) : 0; + const curr = ST.query(l, r) + 1; + ans = Math.max(ans, curr); + ST.update(mp.get(num), curr); + } + return ans; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming + Segment Tree + +::tabs-start + +```python +class SegmentTree: + def __init__(self, N): + self.n = N + while (self.n & (self.n - 1)) != 0: + self.n += 1 + self.tree = [0] * (2 * self.n) + + def update(self, i, val): + if val <= self.tree[self.n + i]: + return + self.tree[self.n + i] = val + j = (self.n + i) >> 1 + while j >= 1: + self.tree[j] = max(self.tree[j << 1], self.tree[j << 1 | 1]) + j >>= 1 + + def query(self, ql, qh): + l = ql + self.n + r = qh + self.n + 1 + res = 0 + while l < r: + if l & 1: + res = max(res, self.tree[l]) + l += 1 + if r & 1: + r -= 1 + res = max(res, self.tree[r]) + l >>= 1 + r >>= 1 + return res + + +class Solution: + def lengthOfLIS(self, nums: List[int], k: int) -> int: + max_val = max(nums) + ST = SegmentTree(max_val + 1) + res = 0 + for num in nums: + l = max(0, num - k) + r = max(0, num - 1) + curr = ST.query(l, r) + 1 + res = max(res, curr) + ST.update(num, curr) + + return res +``` + +```java +class SegmentTree { + private int[] tree; + private int n; + + public SegmentTree(int N) { + this.n = N; + while ((this.n & (this.n - 1)) != 0) { + this.n++; + } + this.tree = new int[2 * this.n]; + } + + public void update(int i, int val) { + if (val <= tree[n + i]) return; + tree[n + i] = val; + for (int j = (n + i) >> 1; j >= 1; j >>= 1) { + tree[j] = Math.max(tree[j << 1], tree[(j << 1) + 1]); + } + } + + public int query(int l, int r) { + l += n; + r += n + 1; + int res = 0; + while (l < r) { + if ((l & 1) == 1) { + res = Math.max(res, tree[l++]); + } + if ((r & 1) == 1) { + res = Math.max(res, tree[--r]); + } + l >>= 1; + r >>= 1; + } + return res; + } +} + +public class Solution { + public int lengthOfLIS(int[] nums, int k) { + int maxVal = 0; + for (int num : nums) { + maxVal = Math.max(maxVal, num); + } + + SegmentTree ST = new SegmentTree(maxVal + 1); + int res = 0; + for (int num : nums) { + int l = Math.max(0, num - k); + int r = Math.max(0, num - 1); + int curr = ST.query(l, r) + 1; + res = Math.max(res, curr); + ST.update(num, curr); + } + return res; + } +} +``` + +```cpp +class SegmentTree { +public: + int n; + vector tree; + + SegmentTree(int N) { + n = N; + while ((n & (n - 1)) != 0) { + n++; + } + tree.resize(2 * n, 0); + } + + void update(int i, int val) { + if (val <= tree[n + i]) return; + tree[n + i] = val; + for (int j = (n + i) >> 1; j >= 1; j >>= 1) { + tree[j] = max(tree[j << 1], tree[(j << 1) + 1]); + } + } + + int query(int l, int r) { + l += n; + r += n + 1; + int res = 0; + while (l < r) { + if (l & 1) { + res = max(res, tree[l++]); + } + if (r & 1) { + res = max(res, tree[--r]); + } + l >>= 1; + r >>= 1; + } + return res; + } +}; + +class Solution { +public: + int lengthOfLIS(vector& nums, int k) { + int maxVal = *max_element(nums.begin(), nums.end()); + SegmentTree ST(maxVal + 1); + int res = 0; + for (int& num : nums) { + int l = max(0, num - k); + int r = max(0, num - 1); + int curr = ST.query(l, r) + 1; + res = max(res, curr); + ST.update(num, curr); + } + return res; + } +}; +``` + +```javascript +class SegmentTree { + /** + * @constructor + * @param {number} N + */ + constructor(N) { + this.n = N; + while ((this.n & (this.n - 1)) !== 0) { + this.n++; + } + this.tree = Array(2 * this.n).fill(0); + } + + /** + * @param {number} i + * @param {number} val + * @return {void} + */ + update(i, val) { + if (val <= this.tree[this.n + i]) return; + this.tree[this.n + i] = val; + for (let j = (this.n + i) >> 1; j >= 1; j >>= 1) { + this.tree[j] = Math.max(this.tree[j << 1], this.tree[(j << 1) + 1]); + } + } + + /** + * @param {number} ql + * @param {number} qh + * @return {number} + */ + query(ql, qh) { + let l = ql + this.n, r = qh + this.n + 1, res = 0; + while (l < r) { + if (l & 1) res = Math.max(res, this.tree[l++]); + if (r & 1) res = Math.max(res, this.tree[--r]); + l >>= 1; + r >>= 1; + } + return res; + } +} + +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + lengthOfLIS(nums, k) { + const maxVal = Math.max(...nums); + const ST = new SegmentTree(maxVal + 1); + let res = 0; + for (const num of nums) { + const l = Math.max(0, num - k); + const r = Math.max(0, num - 1); + const curr = ST.query(l, r) + 1; + res = Math.max(res, curr); + ST.update(num, curr); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum element in the array. \ No newline at end of file diff --git a/articles/longest-increasing-subsequence.md b/articles/longest-increasing-subsequence.md new file mode 100644 index 000000000..79391921a --- /dev/null +++ b/articles/longest-increasing-subsequence.md @@ -0,0 +1,1886 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def lengthOfLIS(self, nums: List[int]) -> int: + + def dfs(i, j): + if i == len(nums): + return 0 + + LIS = dfs(i + 1, j) # not include + + if j == -1 or nums[j] < nums[i]: + LIS = max(LIS, 1 + dfs(i + 1, i)) # include + + return LIS + + return dfs(0, -1) +``` + +```java +public class Solution { + public int lengthOfLIS(int[] nums) { + return dfs(nums, 0, -1); + } + + private int dfs(int[] nums, int i, int j) { + if (i == nums.length) { + return 0; + } + + int LIS = dfs(nums, i + 1, j); // not include + + if (j == -1 || nums[j] < nums[i]) { + LIS = Math.max(LIS, 1 + dfs(nums, i + 1, i)); // include + } + + return LIS; + } +} +``` + +```cpp +class Solution { +public: + int lengthOfLIS(vector& nums) { + return dfs(nums, 0, -1); + } + +private: + int dfs(vector& nums, int i, int j) { + if (i == nums.size()) { + return 0; + } + + int LIS = dfs(nums, i + 1, j); // not include + + if (j == -1 || nums[j] < nums[i]) { + LIS = max(LIS, 1 + dfs(nums, i + 1, i)); // include + } + + return LIS; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + lengthOfLIS(nums) { + return this.dfs(nums, 0, -1); + } + + /** + * @param {number[]} nums + * @param {number} i + * @param {number} j + * @return {number} + */ + dfs(nums, i, j) { + if (i === nums.length) { + return 0; + } + + let LIS = this.dfs(nums, i + 1, j); // not include + + if (j === -1 || nums[j] < nums[i]) { + LIS = Math.max(LIS, 1 + this.dfs(nums, i + 1, i)); // include + } + + return LIS; + } +} +``` + +```csharp +public class Solution { + public int LengthOfLIS(int[] nums) { + return Dfs(nums, 0, -1); + } + + private int Dfs(int[] nums, int i, int j) { + if (i == nums.Length) { + return 0; + } + + int LIS = Dfs(nums, i + 1, j); // not include + + if (j == -1 || nums[j] < nums[i]) { + LIS = Math.Max(LIS, 1 + Dfs(nums, i + 1, i)); // include + } + + return LIS; + } +} +``` + +```go +func lengthOfLIS(nums []int) int { + var dfs func(i, j int) int + dfs = func(i, j int) int { + if i == len(nums) { + return 0 + } + + LIS := dfs(i + 1, j) // not include + + if j == -1 || nums[j] < nums[i] { + LIS = max(LIS, 1 + dfs(i + 1, i)) // include + } + + return LIS + } + + return dfs(0, -1) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun lengthOfLIS(nums: IntArray): Int { + fun dfs(i: Int, j: Int): Int { + if (i == nums.size) { + return 0 + } + + var LIS = dfs(i + 1, j) // not include + + if (j == -1 || nums[j] < nums[i]) { + LIS = maxOf(LIS, 1 + dfs(i + 1, i)) // include + } + + return LIS + } + + return dfs(0, -1) + } +} +``` + +```swift +class Solution { + func lengthOfLIS(_ nums: [Int]) -> Int { + func dfs(_ i: Int, _ j: Int) -> Int { + if i == nums.count { + return 0 + } + + var LIS = dfs(i + 1, j) // not include + + if j == -1 || nums[j] < nums[i] { + LIS = max(LIS, 1 + dfs(i + 1, i)) // include + } + + return LIS + } + + return dfs(0, -1) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) - I + +::tabs-start + +```python +class Solution: + def lengthOfLIS(self, nums): + n = len(nums) + memo = [[-1] * (n + 1) for _ in range(n)] + + def dfs(i, j): + if i == n: + return 0 + if memo[i][j + 1] != -1: + return memo[i][j + 1] + + LIS = dfs(i + 1, j) + + if j == -1 or nums[j] < nums[i]: + LIS = max(LIS, 1 + dfs(i + 1, i)) + + memo[i][j + 1] = LIS + return LIS + + return dfs(0, -1) +``` + +```java +public class Solution { + private int[][] memo; + + private int dfs(int i, int j, int[] nums) { + if (i == nums.length) { + return 0; + } + if (memo[i][j + 1] != -1) { + return memo[i][j + 1]; + } + + int LIS = dfs(i + 1, j, nums); + + if (j == -1 || nums[j] < nums[i]) { + LIS = Math.max(LIS, 1 + dfs(i + 1, i, nums)); + } + + memo[i][j + 1] = LIS; + return LIS; + } + + public int lengthOfLIS(int[] nums) { + int n = nums.length; + memo = new int[n][n + 1]; + for (int[] row : memo) { + Arrays.fill(row, -1); + } + return dfs(0, -1, nums); + } +} +``` + +```cpp +class Solution { +public: + vector> memo; + + int dfs(int i, int j, vector& nums) { + if (i == nums.size()) { + return 0; + } + if (memo[i][j + 1] != -1) { + return memo[i][j + 1]; + } + + int LIS = dfs(i + 1, j, nums); + + if (j == -1 || nums[j] < nums[i]) { + LIS = max(LIS, 1 + dfs(i + 1, i, nums)); + } + + memo[i][j + 1] = LIS; + return LIS; + } + + int lengthOfLIS(vector& nums) { + int n = nums.size(); + memo = vector>(n, vector(n + 1, -1)); + return dfs(0, -1, nums); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + lengthOfLIS(nums) { + const n = nums.length; + const memo = Array.from({ length: n }, () => + Array(n + 2).fill(-1)); + + return this.dfs(nums, 0, -1, memo); + } + + /** + * @param {number[]} nums + * @param {number} i + * @param {number} j + * @param {number[][]} memo + * @return {number} + */ + dfs(nums, i, j, memo) { + if (i === nums.length) return 0; + if (memo[i][j + 1] !== -1) + return memo[i][j + 1]; + + let LIS = this.dfs(nums, i + 1, j, memo); + + if (j === -1 || nums[j] < nums[i]) { + LIS = Math.max(LIS, 1 + this.dfs(nums, i + 1, i, memo)); + } + + memo[i][j + 1] = LIS; + return LIS; + } +} +``` + +```csharp +public class Solution { + public int LengthOfLIS(int[] nums) { + int n = nums.Length; + int[,] memo = new int[n, n + 1]; + + for (int i = 0; i < n; i++) + for (int j = 0; j <= n; j++) + memo[i, j] = -1; + + return DFS(nums, 0, -1, memo); + } + + private int DFS(int[] nums, int i, int j, int[,] memo) { + if (i == nums.Length) return 0; + if (memo[i, j + 1] != -1) { + return memo[i, j + 1]; + } + + int LIS = DFS(nums, i + 1, j, memo); + + if (j == -1 || nums[j] < nums[i]) { + LIS = Math.Max(LIS, 1 + DFS(nums, i + 1, i, memo)); + } + + memo[i, j + 1] = LIS; + return LIS; + } +} +``` + +```go +func lengthOfLIS(nums []int) int { + n := len(nums) + memo := make([][]int, n) + for i := range memo { + memo[i] = make([]int, n+1) + for j := range memo[i] { + memo[i][j] = -1 + } + } + + var dfs func(i, j int) int + dfs = func(i, j int) int { + if i == n { + return 0 + } + if memo[i][j+1] != -1 { + return memo[i][j+1] + } + + LIS := dfs(i + 1, j) + + if j == -1 || nums[j] < nums[i] { + LIS = max(LIS, 1 + dfs(i + 1, i)) + } + + memo[i][j+1] = LIS + return LIS + } + + return dfs(0, -1) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + private lateinit var memo: Array + + private fun dfs(i: Int, j: Int, nums: IntArray): Int { + if (i == nums.size) { + return 0 + } + if (memo[i][j + 1] != -1) { + return memo[i][j + 1] + } + + var LIS = dfs(i + 1, j, nums) + + if (j == -1 || nums[j] < nums[i]) { + LIS = maxOf(LIS, 1 + dfs(i + 1, i, nums)) + } + + memo[i][j + 1] = LIS + return LIS + } + + fun lengthOfLIS(nums: IntArray): Int { + val n = nums.size + memo = Array(n) { IntArray(n + 1) { -1 } } + return dfs(0, -1, nums) + } +} +``` + +```swift +class Solution { + func lengthOfLIS(_ nums: [Int]) -> Int { + let n = nums.count + var memo = Array(repeating: Array(repeating: -1, count: n + 1), count: n) + + func dfs(_ i: Int, _ j: Int) -> Int { + if i == n { + return 0 + } + if memo[i][j + 1] != -1 { + return memo[i][j + 1] + } + + var LIS = dfs(i + 1, j) // not include + + if j == -1 || nums[j] < nums[i] { + LIS = max(LIS, 1 + dfs(i + 1, i)) // include + } + + memo[i][j + 1] = LIS + return LIS + } + + return dfs(0, -1) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Top-Down) - II + +::tabs-start + +```python +class Solution: + def lengthOfLIS(self, nums: List[int]) -> int: + n = len(nums) + memo = [-1] * n + + def dfs(i): + if memo[i] != -1: + return memo[i] + + LIS = 1 + for j in range(i + 1, n): + if nums[i] < nums[j]: + LIS = max(LIS, 1 + dfs(j)) + + memo[i] = LIS + return LIS + + return max(dfs(i) for i in range(n)) +``` + +```java +public class Solution { + private int[] memo; + + public int lengthOfLIS(int[] nums) { + int n = nums.length; + memo = new int[n]; + Arrays.fill(memo, -1); + + int maxLIS = 1; + for (int i = 0; i < n; i++) { + maxLIS = Math.max(maxLIS, dfs(nums, i)); + } + return maxLIS; + } + + private int dfs(int[] nums, int i) { + if (memo[i] != -1) { + return memo[i]; + } + + int LIS = 1; + for (int j = i + 1; j < nums.length; j++) { + if (nums[i] < nums[j]) { + LIS = Math.max(LIS, 1 + dfs(nums, j)); + } + } + + memo[i] = LIS; + return LIS; + } +} +``` + +```cpp +class Solution { +private: + vector memo; + + int dfs(vector& nums, int i) { + if (memo[i] != -1) { + return memo[i]; + } + + int LIS = 1; + for (int j = i + 1; j < nums.size(); j++) { + if (nums[i] < nums[j]) { + LIS = max(LIS, 1 + dfs(nums, j)); + } + } + + return memo[i] = LIS; + } + +public: + int lengthOfLIS(vector& nums) { + int n = nums.size(); + memo.assign(n, -1); + + int maxLIS = 1; + for (int i = 0; i < n; i++) { + maxLIS = max(maxLIS, dfs(nums, i)); + } + return maxLIS; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + lengthOfLIS(nums) { + const n = nums.length; + const memo = new Array(n).fill(-1); + + const dfs = (i) => { + if (memo[i] !== -1) { + return memo[i]; + } + + let LIS = 1; + for (let j = i + 1; j < n; j++) { + if (nums[i] < nums[j]) { + LIS = Math.max(LIS, 1 + dfs(j)); + } + } + + memo[i] = LIS; + return LIS; + }; + + return Math.max(...nums.map((_, i) => dfs(i))); + } +} +``` + +```csharp +public class Solution { + private int[] memo; + + public int LengthOfLIS(int[] nums) { + int n = nums.Length; + memo = new int[n]; + Array.Fill(memo, -1); + + int maxLIS = 1; + for (int i = 0; i < n; i++) { + maxLIS = Math.Max(maxLIS, Dfs(nums, i)); + } + return maxLIS; + } + + private int Dfs(int[] nums, int i) { + if (memo[i] != -1) { + return memo[i]; + } + + int LIS = 1; + for (int j = i + 1; j < nums.Length; j++) { + if (nums[i] < nums[j]) { + LIS = Math.Max(LIS, 1 + Dfs(nums, j)); + } + } + + memo[i] = LIS; + return LIS; + } +} +``` + +```go +func lengthOfLIS(nums []int) int { + n := len(nums) + memo := make([]int, n) + for i := range memo { + memo[i] = -1 + } + + var dfs func(int) int + dfs = func(i int) int { + if memo[i] != -1 { + return memo[i] + } + + LIS := 1 + for j := i + 1; j < n; j++ { + if nums[i] < nums[j] { + LIS = max(LIS, 1+dfs(j)) + } + } + + memo[i] = LIS + return LIS + } + + maxLIS := 1 + for i := 0; i < n; i++ { + maxLIS = max(maxLIS, dfs(i)) + } + + return maxLIS +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + private lateinit var memo: IntArray + + fun lengthOfLIS(nums: IntArray): Int { + val n = nums.size + memo = IntArray(n) { -1 } + + var maxLIS = 1 + for (i in nums.indices) { + maxLIS = maxOf(maxLIS, dfs(nums, i)) + } + return maxLIS + } + + private fun dfs(nums: IntArray, i: Int): Int { + if (memo[i] != -1) { + return memo[i] + } + + var LIS = 1 + for (j in i + 1 until nums.size) { + if (nums[i] < nums[j]) { + LIS = maxOf(LIS, 1 + dfs(nums, j)) + } + } + + memo[i] = LIS + return LIS + } +} +``` + +```swift +class Solution { + func lengthOfLIS(_ nums: [Int]) -> Int { + let n = nums.count + var memo = Array(repeating: -1, count: n) + + func dfs(_ i: Int) -> Int { + if memo[i] != -1 { + return memo[i] + } + + var LIS = 1 + for j in (i + 1)..= 0; i--) { + for (int j = i - 1; j >= -1; j--) { + int LIS = dp[i + 1][j + 1]; // Not including nums[i] + + if (j == -1 || nums[j] < nums[i]) { + LIS = Math.max(LIS, 1 + dp[i + 1][i + 1]); // Including nums[i] + } + + dp[i][j + 1] = LIS; + } + } + + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + int lengthOfLIS(vector& nums) { + int n = nums.size(); + vector> dp(n + 1, vector(n + 1, 0)); + + for (int i = n - 1; i >= 0; --i) { + for (int j = i - 1; j >= -1; --j) { + int LIS = dp[i + 1][j + 1]; // Not including nums[i] + + if (j == -1 || nums[j] < nums[i]) { + LIS = max(LIS, 1 + dp[i + 1][i + 1]); // Including nums[i] + } + + dp[i][j + 1] = LIS; + } + } + + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + lengthOfLIS(nums) { + const n = nums.length; + const dp = Array.from({ length: n + 1 }, () => new Array(n + 1).fill(0)); + + for (let i = n - 1; i >= 0; i--) { + for (let j = i - 1; j >= -1; j--) { + let LIS = dp[i + 1][j + 1]; // Not including nums[i] + + if (j === -1 || nums[j] < nums[i]) { + LIS = Math.max(LIS, 1 + dp[i + 1][i + 1]); // Including nums[i] + } + + dp[i][j + 1] = LIS; + } + } + + return dp[0][0]; + } +} +``` + +```csharp +public class Solution { + public int LengthOfLIS(int[] nums) { + int n = nums.Length; + int[,] dp = new int[n + 1, n + 1]; + + for (int i = n - 1; i >= 0; i--) { + for (int j = i - 1; j >= -1; j--) { + int LIS = dp[i + 1, j + 1]; // Not including nums[i] + + if (j == -1 || nums[j] < nums[i]) { + LIS = Math.Max(LIS, 1 + dp[i + 1, i + 1]); // Including nums[i] + } + + dp[i, j + 1] = LIS; + } + } + + return dp[0, 0]; + } +} +``` + +```go +func lengthOfLIS(nums []int) int { + n := len(nums) + dp := make([][]int, n+1) + for i := range dp { + dp[i] = make([]int, n+1) + } + + for i := n - 1; i >= 0; i-- { + for j := i - 1; j >= -1; j-- { + LIS := dp[i+1][j+1] // Not including nums[i] + + if j == -1 || nums[j] < nums[i] { + LIS = max(LIS, 1+dp[i+1][i+1]) // Including nums[i] + } + + dp[i][j+1] = LIS + } + } + + return dp[0][0] +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun lengthOfLIS(nums: IntArray): Int { + val n = nums.size + val dp = Array(n + 1) { IntArray(n + 1) } + + for (i in n - 1 downTo 0) { + for (j in i - 1 downTo -1) { + var LIS = dp[i + 1][j + 1] // Not including nums[i] + + if (j == -1 || nums[j] < nums[i]) { + LIS = maxOf(LIS, 1 + dp[i + 1][i + 1]) // Including nums[i] + } + + dp[i][j + 1] = LIS + } + } + + return dp[0][0] + } +} +``` + +```swift +class Solution { + func lengthOfLIS(_ nums: [Int]) -> Int { + let n = nums.count + var dp = Array(repeating: Array(repeating: 0, count: n + 1), count: n + 1) + + for i in stride(from: n - 1, through: 0, by: -1) { + for j in stride(from: i - 1, through: -1, by: -1) { + var LIS = dp[i + 1][j + 1] // Not including nums[i] + + if j == -1 || nums[j] < nums[i] { + LIS = max(LIS, 1 + dp[i + 1][i + 1]) // Including nums[i] + } + + dp[i][j + 1] = LIS + } + } + + return dp[0][0] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 5. Dynamic Programming (Bottom-Up) - II + +::tabs-start + +```python +class Solution: + def lengthOfLIS(self, nums: List[int]) -> int: + LIS = [1] * len(nums) + + for i in range(len(nums) - 1, -1, -1): + for j in range(i + 1, len(nums)): + if nums[i] < nums[j]: + LIS[i] = max(LIS[i], 1 + LIS[j]) + return max(LIS) +``` + +```java +public class Solution { + public int lengthOfLIS(int[] nums) { + int[] LIS = new int[nums.length]; + Arrays.fill(LIS, 1); + + for (int i = nums.length - 1; i >= 0; i--) { + for (int j = i + 1; j < nums.length; j++) { + if (nums[i] < nums[j]) { + LIS[i] = Math.max(LIS[i], 1 + LIS[j]); + } + } + } + return Arrays.stream(LIS).max().getAsInt(); + } +} +``` + +```cpp +class Solution { +public: + int lengthOfLIS(vector& nums) { + vector LIS(nums.size(), 1); + + for (int i = nums.size() - 1; i >= 0; i--) { + for (int j = i + 1; j < nums.size(); j++) { + if (nums[i] < nums[j]) { + LIS[i] = max(LIS[i], 1 + LIS[j]); + } + } + } + return *max_element(LIS.begin(), LIS.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + lengthOfLIS(nums) { + const LIS = new Array(nums.length).fill(1); + + for (let i = nums.length - 1; i >= 0; i--) { + for (let j = i + 1; j < nums.length; j++) { + if (nums[i] < nums[j]) { + LIS[i] = Math.max(LIS[i], 1 + LIS[j]); + } + } + } + return Math.max(...LIS); + } +} +``` + +```csharp +public class Solution { + public int LengthOfLIS(int[] nums) { + int[] LIS = new int[nums.Length]; + for (int i = 0; i < LIS.Length; i++) LIS[i] = 1; + + for (int i = nums.Length - 1; i >= 0; i--) { + for (int j = i + 1; j < nums.Length; j++) { + if (nums[i] < nums[j]) { + LIS[i] = Math.Max(LIS[i], 1 + LIS[j]); + } + } + } + return LIS.Max(); + } +} +``` + +```go +func lengthOfLIS(nums []int) int { + LIS := make([]int, len(nums)) + for i := range LIS { + LIS[i] = 1 + } + + for i := len(nums) - 1; i >= 0; i-- { + for j := i + 1; j < len(nums); j++ { + if nums[i] < nums[j] { + if 1 + LIS[j] > LIS[i] { + LIS[i] = 1 + LIS[j] + } + } + } + } + + maxLen := 1 + for _, length := range LIS { + if length > maxLen { + maxLen = length + } + } + + return maxLen +} +``` + +```kotlin +class Solution { + fun lengthOfLIS(nums: IntArray): Int { + val LIS = IntArray(nums.size) { 1 } + + for (i in nums.size - 1 downTo 0) { + for (j in (i + 1) until nums.size) { + if (nums[i] < nums[j]) { + LIS[i] = maxOf(LIS[i], 1 + LIS[j]) + } + } + } + + return LIS.maxOrNull() ?: 1 + } +} +``` + +```swift +class Solution { + func lengthOfLIS(_ nums: [Int]) -> Int { + var LIS = Array(repeating: 1, count: nums.count) + + for i in stride(from: nums.count - 1, through: 0, by: -1) { + for j in (i + 1)..> 1 + while j >= 1: + self.tree[j] = max(self.tree[j << 1], self.tree[j << 1 | 1]) + j >>= 1 + + def query(self, l, r): + if l > r: + return 0 + res = float('-inf') + l += self.n + r += self.n + 1 + while l < r: + if l & 1: + res = max(res, self.tree[l]) + l += 1 + if r & 1: + r -= 1 + res = max(res, self.tree[r]) + l >>= 1 + r >>= 1 + return res + +class Solution: + def lengthOfLIS(self, nums: List[int]) -> int: + def compress(arr): + sortedArr = sorted(set(arr)) + order = [] + for num in arr: + order.append(bisect_left(sortedArr, num)) + return order + + nums = compress(nums) + n = len(nums) + segTree = SegmentTree(n) + + LIS = 0 + for num in nums: + curLIS = segTree.query(0, num - 1) + 1 + segTree.update(num, curLIS) + LIS = max(LIS, curLIS) + return LIS +``` + +```java +class SegmentTree { + int n; + int[] tree; + + public SegmentTree(int N) { + n = N; + while ((n & (n - 1)) != 0) { + n++; + } + tree = new int[2 * n]; + } + + void update(int i, int val) { + tree[n + i] = val; + int j = (n + i) >> 1; + while (j >= 1) { + tree[j] = Math.max(tree[j << 1], tree[j << 1 | 1]); + j >>= 1; + } + } + + int query(int l, int r) { + if (l > r) { + return 0; + } + int res = Integer.MIN_VALUE; + l += n; + r += n + 1; + while (l < r) { + if ((l & 1) != 0) { + res = Math.max(res, tree[l]); + l++; + } + if ((r & 1) != 0) { + r--; + res = Math.max(res, tree[r]); + } + l >>= 1; + r >>= 1; + } + return res; + } +} + +public class Solution { + public int lengthOfLIS(int[] nums) { + int[] sortedArr = Arrays.stream(nums).distinct().sorted().toArray(); + int[] order = new int[nums.length]; + for (int i = 0; i < nums.length; i++) { + order[i] = Arrays.binarySearch(sortedArr, nums[i]); + } + int n = sortedArr.length; + SegmentTree segTree = new SegmentTree(n); + + int LIS = 0; + for (int num : order) { + int curLIS = segTree.query(0, num - 1) + 1; + segTree.update(num, curLIS); + LIS = Math.max(LIS, curLIS); + } + return LIS; + } +} +``` + +```cpp +class SegmentTree { +public: + int n; + vector tree; + + SegmentTree(int N) { + n = N; + while (n & (n - 1)) { + n++; + } + tree.resize(2 * n); + } + + void update(int i, int val) { + tree[n + i] = val; + int j = (n + i) >> 1; + while (j >= 1) { + tree[j] = max(tree[j << 1], tree[j << 1 | 1]); + j >>= 1; + } + } + + int query(int l, int r) { + if (l > r) { + return 0; + } + int res = INT_MIN; + l += n; + r += n + 1; + while (l < r) { + if (l & 1) { + res = max(res, tree[l]); + l++; + } + if (r & 1) { + r--; + res = max(res, tree[r]); + } + l >>= 1; + r >>= 1; + } + return res; + } +}; + +class Solution { +public: + int lengthOfLIS(vector& nums) { + vector sortedArr = nums; + sort(sortedArr.begin(), sortedArr.end()); + sortedArr.erase(unique(sortedArr.begin(), + sortedArr.end()), sortedArr.end()); + + vector order(nums.size()); + for (int i = 0; i < nums.size(); i++) { + order[i] = lower_bound(sortedArr.begin(), sortedArr.end(), + nums[i]) - sortedArr.begin(); + } + + int n = sortedArr.size(); + SegmentTree segTree(n); + + int LIS = 0; + for (int num : order) { + int curLIS = segTree.query(0, num - 1) + 1; + segTree.update(num, curLIS); + LIS = max(LIS, curLIS); + } + return LIS; + } +}; +``` + +```javascript +class SegmentTree { + constructor(N, a) { + this.n = N; + while ((this.n & (this.n - 1)) !== 0) { + this.n++; + } + this.tree = new Array(2 * this.n).fill(0); + } + + /** + * @param {number} i + * @param {number} val + */ + update(i, val) { + this.tree[this.n + i] = val; + let j = (this.n + i) >> 1; + while (j >= 1) { + this.tree[j] = Math.max(this.tree[j << 1], this.tree[j << 1 | 1]); + j >>= 1; + } + } + + /** + * @param {number} l + * @param {number} r + * @return {number} + */ + query(l, r) { + if (l > r) { + return 0; + } + let res = -Infinity; + l += this.n; + r += this.n + 1; + while (l < r) { + if (l & 1) { + res = Math.max(res, this.tree[l]); + l++; + } + if (r & 1) { + r--; + res = Math.max(res, this.tree[r]); + } + l >>= 1; + r >>= 1; + } + return res; + } +} + +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + lengthOfLIS(nums) { + const sortedArr = Array.from(new Set(nums)).sort((a, b) => a - b); + const map = new Map(); + + sortedArr.forEach((num, index) => { + map.set(num, index); + }); + + const order = nums.map(num => map.get(num)); + const n = sortedArr.length; + const segTree = new SegmentTree(n, order); + + let LIS = 0; + for (const num of order) { + const curLIS = segTree.query(0, num - 1) + 1; + segTree.update(num, curLIS); + LIS = Math.max(LIS, curLIS); + } + return LIS; + } +} +``` + +```csharp +public class SegmentTree { + private int n; + private int[] tree; + + public SegmentTree(int N) { + n = N; + tree = new int[2 * n]; + Array.Fill(tree, 0); + } + + public void Update(int i, int val) { + tree[n + i] = val; + int j = (n + i) >> 1; + while (j >= 1) { + tree[j] = Math.Max(tree[2 * j], tree[2 * j + 1]); + j >>= 1; + } + } + + public int Query(int l, int r) { + if (l > r) { + return 0; + } + int res = int.MinValue; + l += n; + r += n + 1; + while (l < r) { + if ((l & 1) == 1) { + res = Math.Max(res, tree[l]); + l++; + } + if ((r & 1) == 1) { + r--; + res = Math.Max(res, tree[r]); + } + l >>= 1; + r >>= 1; + } + return res; + } +} + +public class Solution { + public int LengthOfLIS(int[] nums) { + var sortedArr = nums.Distinct().OrderBy(x => x).ToArray(); + var map = new Dictionary(); + + for (int i = 0; i < sortedArr.Length; i++) { + map[sortedArr[i]] = i; + } + + int n = sortedArr.Length; + var segTree = new SegmentTree(n); + + int LIS = 0; + foreach (var num in nums) { + int compressedIndex = map[num]; + int curLIS = segTree.Query(0, compressedIndex - 1) + 1; + segTree.Update(compressedIndex, curLIS); + LIS = Math.Max(LIS, curLIS); + } + return LIS; + } +} +``` + +```go +type SegmentTree struct { + n int + tree []int +} + +func NewSegmentTree(N int) *SegmentTree { + n := N + for (n & (n - 1)) != 0 { + n++ + } + tree := make([]int, 2*n) + return &SegmentTree{n: n, tree: tree} +} + +func (seg *SegmentTree) Update(i, val int) { + seg.tree[seg.n+i] = val + j := (seg.n + i) >> 1 + for j >= 1 { + seg.tree[j] = max(seg.tree[j<<1], seg.tree[j<<1|1]) + j >>= 1 + } +} + +func (seg *SegmentTree) Query(l, r int) int { + if l > r { + return 0 + } + res := -1 << 63 + l += seg.n + r += seg.n + 1 + for l < r { + if l&1 != 0 { + res = max(res, seg.tree[l]) + l++ + } + if r&1 != 0 { + r-- + res = max(res, seg.tree[r]) + } + l >>= 1 + r >>= 1 + } + return res +} + +func compress(arr []int) []int { + sortedArr := make([]int, len(arr)) + copy(sortedArr, arr) + sort.Ints(sortedArr) + order := make([]int, len(arr)) + for i, num := range arr { + order[i] = sort.SearchInts(sortedArr, num) + } + return order +} + +func lengthOfLIS(nums []int) int { + nums = compress(nums) + n := len(nums) + segTree := NewSegmentTree(n) + LIS := 0 + for _, num := range nums { + curLIS := segTree.Query(0, num-1) + 1 + segTree.Update(num, curLIS) + LIS = max(LIS, curLIS) + } + return LIS +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class SegmentTree(N: Int) { + var n: Int = N + var tree: IntArray + + init { + while ((n and (n - 1)) != 0) { + n++ + } + tree = IntArray(2 * n) + } + + fun update(i: Int, value: Int) { + tree[n + i] = value + var j = (n + i) / 2 + while (j >= 1) { + tree[j] = max(tree[j * 2], tree[j * 2 + 1]) + j /= 2 + } + } + + fun query(l: Int, r: Int): Int { + if (l > r) return 0 + var res = Int.MIN_VALUE + var left = l + n + var right = r + n + 1 + while (left < right) { + if (left and 1 != 0) { + res = max(res, tree[left]) + left++ + } + if (right and 1 != 0) { + right-- + res = max(res, tree[right]) + } + left /= 2 + right /= 2 + } + return res + } +} + +class Solution { + fun compress(arr: IntArray): IntArray { + val sortedArr = arr.toSortedSet().toIntArray() + return arr.map { sortedArr.binarySearch(it) }.toIntArray() + } + + fun lengthOfLIS(nums: IntArray): Int { + val compressedNums = compress(nums) + val n = compressedNums.size + val segTree = SegmentTree(n) + var LIS = 0 + for (num in compressedNums) { + val curLIS = segTree.query(0, num - 1) + 1 + segTree.update(num, curLIS) + LIS = max(LIS, curLIS) + } + return LIS + } +} +``` + +```swift +class SegmentTree { + private var n: Int + private var tree: [Int] + + init(_ N: Int) { + self.n = N + while (self.n & (self.n - 1)) != 0 { + self.n += 1 + } + self.tree = Array(repeating: 0, count: 2 * self.n) + } + + func update(_ i: Int, _ val: Int) { + tree[n + i] = val + var j = (n + i) >> 1 + while j >= 1 { + tree[j] = max(tree[j << 1], tree[j << 1 | 1]) + j >>= 1 + } + } + + func query(_ l: Int, _ r: Int) -> Int { + if l > r { + return 0 + } + var res = Int.min + var l = l + n + var r = r + n + 1 + while l < r { + if l & 1 == 1 { + res = max(res, tree[l]) + l += 1 + } + if r & 1 == 1 { + r -= 1 + res = max(res, tree[r]) + } + l >>= 1 + r >>= 1 + } + return res + } +} + +class Solution { + func lengthOfLIS(_ nums: [Int]) -> Int { + func compress(_ arr: [Int]) -> [Int] { + let sortedArr = Array(Set(arr)).sorted() + return arr.map { sortedArr.firstIndex(of: $0)! } + } + + let nums = compress(nums) + let n = nums.count + let segTree = SegmentTree(n) + + var LIS = 0 + for num in nums { + let curLIS = segTree.query(0, num - 1) + 1 + segTree.update(num, curLIS) + LIS = max(LIS, curLIS) + } + return LIS + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 7. Dynamic Programming + Binary Search + +::tabs-start + +```python +from bisect import bisect_left +class Solution: + def lengthOfLIS(self, nums: List[int]) -> int: + dp = [] + dp.append(nums[0]) + + LIS = 1 + for i in range(1, len(nums)): + if dp[-1] < nums[i]: + dp.append(nums[i]) + LIS += 1 + continue + + idx = bisect_left(dp, nums[i]) + dp[idx] = nums[i] + + return LIS +``` + +```java +public class Solution { + public int lengthOfLIS(int[] nums) { + List dp = new ArrayList<>(); + dp.add(nums[0]); + + int LIS = 1; + for (int i = 1; i < nums.length; i++) { + if (dp.get(dp.size() - 1) < nums[i]) { + dp.add(nums[i]); + LIS++; + continue; + } + + int idx = Collections.binarySearch(dp, nums[i]); + if (idx < 0) idx = -idx - 1; + dp.set(idx, nums[i]); + } + + return LIS; + } +} +``` + +```cpp +class Solution { +public: + int lengthOfLIS(vector& nums) { + vector dp; + dp.push_back(nums[0]); + + int LIS = 1; + for (int i = 1; i < nums.size(); i++) { + if (dp.back() < nums[i]) { + dp.push_back(nums[i]); + LIS++; + continue; + } + + int idx = lower_bound(dp.begin(), + dp.end(), nums[i]) - dp.begin(); + dp[idx] = nums[i]; + } + + return LIS; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + lengthOfLIS(nums) { + const dp = []; + dp.push(nums[0]); + + let LIS = 1; + for (let i = 1; i < nums.length; i++) { + if (dp[dp.length - 1] < nums[i]) { + dp.push(nums[i]); + LIS++; + continue; + } + + let left = 0, right = dp.length - 1; + while (left < right) { + const mid = Math.floor((left + right) / 2); + if (dp[mid] < nums[i]) { + left = mid + 1; + } else { + right = mid; + } + } + dp[left] = nums[i]; + } + + return LIS; + } +} +``` + +```csharp +public class Solution { + public int LengthOfLIS(int[] nums) { + List dp = new List(); + dp.Add(nums[0]); + + int LIS = 1; + for (int i = 1; i < nums.Length; i++) { + if (dp[dp.Count - 1] < nums[i]) { + dp.Add(nums[i]); + LIS++; + continue; + } + + int idx = dp.BinarySearch(nums[i]); + if (idx < 0) idx = ~idx; + dp[idx] = nums[i]; + } + + return LIS; + } +} +``` + +```go +func lengthOfLIS(nums []int) int { + dp := []int{} + dp = append(dp, nums[0]) + + LIS := 1 + for i := 1; i < len(nums); i++ { + if dp[len(dp)-1] < nums[i] { + dp = append(dp, nums[i]) + LIS++ + continue + } + + idx := sort.Search(len(dp), func(j int) bool { + return dp[j] >= nums[i] + }) + dp[idx] = nums[i] + } + + return LIS +} +``` + +```kotlin +class Solution { + fun lengthOfLIS(nums: IntArray): Int { + val dp = mutableListOf() + dp.add(nums[0]) + + var LIS = 1 + for (i in 1 until nums.size) { + if (dp[dp.size - 1] < nums[i]) { + dp.add(nums[i]) + LIS++ + continue + } + + val idx = dp.binarySearch(nums[i]).let { if (it < 0) -it - 1 else it } + dp[idx] = nums[i] + } + + return LIS + } +} +``` + +```swift +class Solution { + func lengthOfLIS(_ nums: [Int]) -> Int { + var dp = [nums[0]] + var LIS = 1 + + for i in 1.. Int { + var left = 0, right = dp.count - 1 + while left < right { + let mid = (left + right) / 2 + if dp[mid] < target { + left = mid + 1 + } else { + right = mid + } + } + return left + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/longest-palindrome.md b/articles/longest-palindrome.md new file mode 100644 index 000000000..635b7a163 --- /dev/null +++ b/articles/longest-palindrome.md @@ -0,0 +1,419 @@ +## 1. Hash Map + +::tabs-start + +```python +class Solution: + def longestPalindrome(self, s: str) -> int: + count = defaultdict(int) + res = 0 + + for c in s: + count[c] += 1 + if count[c] % 2 == 0: + res += 2 + + for cnt in count.values(): + if cnt % 2: + res += 1 + break + + return res +``` + +```java +public class Solution { + public int longestPalindrome(String s) { + Map count = new HashMap<>(); + int res = 0; + + for (char c : s.toCharArray()) { + count.put(c, count.getOrDefault(c, 0) + 1); + if (count.get(c) % 2 == 0) { + res += 2; + } + } + + for (int cnt : count.values()) { + if (cnt % 2 == 1) { + res += 1; + break; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int longestPalindrome(string s) { + unordered_map count; + int res = 0; + + for (char c : s) { + count[c]++; + if (count[c] % 2 == 0) { + res += 2; + } + } + + for (auto& [ch, cnt] : count) { + if (cnt % 2 == 1) { + res += 1; + break; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + longestPalindrome(s) { + const count = {}; + let res = 0; + + for (let c of s) { + count[c] = (count[c] || 0) + 1; + if (count[c] % 2 === 0) { + res += 2; + } + } + + for (let key in count) { + if (count[key] % 2 === 1) { + res += 1; + break; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the given string, and $m$ is the number of distinct characters in the string. + +--- + +## 2. Hash Map (Optimal) + +::tabs-start + +```python +class Solution: + def longestPalindrome(self, s: str) -> int: + count = defaultdict(int) + res = 0 + + for c in s: + count[c] += 1 + if count[c] % 2 == 0: + res += 2 + + return res + (res < len(s)) +``` + +```java +public class Solution { + public int longestPalindrome(String s) { + Map count = new HashMap<>(); + int res = 0; + + for (char c : s.toCharArray()) { + count.put(c, count.getOrDefault(c, 0) + 1); + if (count.get(c) % 2 == 0) { + res += 2; + } + } + + return res + (res < s.length() ? 1 : 0); + } +} +``` + +```cpp +class Solution { +public: + int longestPalindrome(string s) { + unordered_map count; + int res = 0; + + for (char c : s) { + count[c]++; + if (count[c] % 2 == 0) { + res += 2; + } + } + + return res + (res < s.size()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + longestPalindrome(s) { + const count = {}; + let res = 0; + + for (let c of s) { + count[c] = (count[c] || 0) + 1; + if (count[c] % 2 === 0) { + res += 2; + } + } + + return res + (res < s.length ? 1 : 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the given string, and $m$ is the number of distinct characters in the string. + +--- + +## 3. Hash Set + +::tabs-start + +```python +class Solution: + def longestPalindrome(self, s: str) -> int: + seen = set() + res = 0 + + for c in s: + if c in seen: + seen.remove(c) + res += 2 + else: + seen.add(c) + + return res + 1 if seen else res +``` + +```java +public class Solution { + public int longestPalindrome(String s) { + Set seen = new HashSet<>(); + int res = 0; + + for (char c : s.toCharArray()) { + if (seen.contains(c)) { + seen.remove(c); + res += 2; + } else { + seen.add(c); + } + } + + return seen.isEmpty() ? res : res + 1; + } +} +``` + +```cpp +class Solution { +public: + int longestPalindrome(string s) { + unordered_set seen; + int res = 0; + + for (char c : s) { + if (seen.count(c)) { + seen.erase(c); + res += 2; + } else { + seen.insert(c); + } + } + + return seen.empty() ? res : res + 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + longestPalindrome(s) { + const seen = new Set(); + let res = 0; + + for (let c of s) { + if (seen.has(c)) { + seen.delete(c); + res += 2; + } else { + seen.add(c); + } + } + + return seen.size === 0 ? res : res + 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the given string, and $m$ is the number of distinct characters in the string. + +--- + +## 4. Bitmask + +::tabs-start + +```python +class Solution: + def longestPalindrome(self, s: str) -> int: + mask1 = 0 # [a - z] + mask2 = 0 # [A - Z] + res = 0 + + for c in s: + if 'a' <= c <= 'z': + bit = 1 << (ord(c) - ord('a')) + if mask1 & bit: + res += 2 + mask1 ^= bit + else: + bit = 1 << (ord(c) - ord('A')) + if mask2 & bit: + res += 2 + mask2 ^= bit + + return res + 1 if mask1 or mask2 else res +``` + +```java +public class Solution { + public int longestPalindrome(String s) { + int mask1 = 0; // [a - z] + int mask2 = 0; // [A - Z] + int res = 0; + + for (char c : s.toCharArray()) { + if (c >= 'a' && c <= 'z') { + int bit = 1 << (c - 'a'); + if ((mask1 & bit) != 0) { + res += 2; + } + mask1 ^= bit; + } else { + int bit = 1 << (c - 'A'); + if ((mask2 & bit) != 0) { + res += 2; + } + mask2 ^= bit; + } + } + + return (mask1 != 0 || mask2 != 0) ? res + 1 : res; + } +} +``` + +```cpp +class Solution { +public: + int longestPalindrome(string s) { + int mask1 = 0; // [a - z] + int mask2 = 0; // [A - Z] + int res = 0; + + for (char c : s) { + if ('a' <= c && c <= 'z') { + int bit = 1 << (c - 'a'); + if (mask1 & bit) { + res += 2; + } + mask1 ^= bit; + } else { + int bit = 1 << (c - 'A'); + if (mask2 & bit) { + res += 2; + } + mask2 ^= bit; + } + } + + return (mask1 || mask2) ? res + 1 : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + longestPalindrome(s) { + let mask1 = 0; // [a - z] + let mask2 = 0; // [A - Z] + let res = 0; + + for (let c of s) { + if (c >= 'a' && c <= 'z') { + let bit = 1 << (c.charCodeAt(0) - 97); + if (mask1 & bit) { + res += 2; + } + mask1 ^= bit; + } else { + let bit = 1 << (c.charCodeAt(0) - 65); + if (mask2 & bit) { + res += 2; + } + mask2 ^= bit; + } + } + + return (mask1 || mask2) ? res + 1 : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/longest-palindromic-subsequence.md b/articles/longest-palindromic-subsequence.md new file mode 100644 index 000000000..c216ab542 --- /dev/null +++ b/articles/longest-palindromic-subsequence.md @@ -0,0 +1,557 @@ +## 1. Dynamic Programming (Top Down) + +::tabs-start + +```python +class Solution: + def longestPalindromeSubseq(self, s: str) -> int: + n = len(s) + dp = [[-1] * n for _ in range(n)] + + def dfs(i, j): + if i < 0 or j == n: + return 0 + if dp[i][j] != -1: + return dp[i][j] + + if s[i] == s[j]: + length = 1 if i == j else 2 + dp[i][j] = length + dfs(i - 1, j + 1) + else: + dp[i][j] = max(dfs(i - 1, j), dfs(i, j + 1)) + + return dp[i][j] + + for i in range(n): + dfs(i, i) # odd length + dfs(i, i + 1) # even length + + return max(max(row) for row in dp if row != -1) +``` + +```java +public class Solution { + private int[][] dp; + + public int longestPalindromeSubseq(String s) { + int n = s.length(); + dp = new int[n][n]; + + for (int[] row : dp) { + Arrays.fill(row, -1); + } + + for (int i = 0; i < n; i++) { + dfs(i, i, s); // Odd length + dfs(i, i + 1, s); // Even length + } + + int maxLength = 0; + for (int[] row : dp) { + for (int val : row) { + maxLength = Math.max(maxLength, val); + } + } + + return maxLength; + } + + private int dfs(int i, int j, String s) { + if (i < 0 || j == s.length()) { + return 0; + } + if (dp[i][j] != -1) { + return dp[i][j]; + } + + if (s.charAt(i) == s.charAt(j)) { + int length = (i == j) ? 1 : 2; + dp[i][j] = length + dfs(i - 1, j + 1, s); + } else { + dp[i][j] = Math.max(dfs(i - 1, j, s), dfs(i, j + 1, s)); + } + + return dp[i][j]; + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + +public: + int longestPalindromeSubseq(string s) { + int n = s.size(); + dp.resize(n, vector(n, -1)); + + for (int i = 0; i < n; i++) { + dfs(i, i, s); // Odd length + dfs(i, i + 1, s); // Even length + } + + int maxLength = 0; + for (const auto& row : dp) { + for (int val : row) { + maxLength = max(maxLength, val); + } + } + + return maxLength; + } + +private: + int dfs(int i, int j, const string& s) { + if (i < 0 || j == s.size()) { + return 0; + } + if (dp[i][j] != -1) { + return dp[i][j]; + } + + if (s[i] == s[j]) { + int length = (i == j) ? 1 : 2; + dp[i][j] = length + dfs(i - 1, j + 1, s); + } else { + dp[i][j] = max(dfs(i - 1, j, s), dfs(i, j + 1, s)); + } + + return dp[i][j]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + longestPalindromeSubseq(s) { + const n = s.length; + const dp = Array.from({ length: n }, () => Array(n).fill(-1)); + + const dfs = (i, j) => { + if (i < 0 || j === n) { + return 0; + } + if (dp[i][j] !== -1) { + return dp[i][j]; + } + + if (s[i] === s[j]) { + const length = i === j ? 1 : 2; + dp[i][j] = length + dfs(i - 1, j + 1); + } else { + dp[i][j] = Math.max(dfs(i - 1, j), dfs(i, j + 1)); + } + + return dp[i][j]; + }; + + for (let i = 0; i < n; i++) { + dfs(i, i); // Odd length + dfs(i, i + 1); // Even length + } + + let maxLength = 0; + for (const row of dp) { + for (const val of row) { + maxLength = Math.max(maxLength, val); + } + } + + return maxLength; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Dynamic Programming (Top-Down Optimized) + +::tabs-start + +```python +class Solution: + def longestPalindromeSubseq(self, s: str) -> int: + cache = {} + + def dfs(i, j): + if i > j: + return 0 + if i == j: + return 1 + if (i, j) in cache: + return cache[(i, j)] + + if s[i] == s[j]: + cache[(i, j)] = dfs(i + 1, j - 1) + 2 + else: + cache[(i, j)] = max(dfs(i + 1, j), dfs(i, j - 1)) + + return cache[(i, j)] + + return dfs(0, len(s) - 1) +``` + +```java +public class Solution { + private int[][] dp; + + public int longestPalindromeSubseq(String s) { + int n = s.length(); + dp = new int[n][n]; + + for (int[] row : dp) { + Arrays.fill(row, -1); + } + + return dfs(0, n - 1, s); + } + + private int dfs(int i, int j, String s) { + if (i > j) { + return 0; + } + if (i == j) { + return 1; + } + if (dp[i][j] != -1) { + return dp[i][j]; + } + + if (s.charAt(i) == s.charAt(j)) { + dp[i][j] = dfs(i + 1, j - 1, s) + 2; + } else { + dp[i][j] = Math.max(dfs(i + 1, j, s), dfs(i, j - 1, s)); + } + + return dp[i][j]; + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + +public: + int longestPalindromeSubseq(string s) { + int n = s.size(); + dp.resize(n, vector(n, -1)); + return dfs(0, n - 1, s); + } + +private: + int dfs(int i, int j, const string& s) { + if (i > j) { + return 0; + } + if (i == j) { + return 1; + } + if (dp[i][j] != -1) { + return dp[i][j]; + } + + if (s[i] == s[j]) { + dp[i][j] = dfs(i + 1, j - 1, s) + 2; + } else { + dp[i][j] = max(dfs(i + 1, j, s), dfs(i, j - 1, s)); + } + + return dp[i][j]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + longestPalindromeSubseq(s) { + const n = s.length; + const dp = Array.from({ length: n }, () => Array(n).fill(-1)); + + const dfs = (i, j) => { + if (i > j) { + return 0; + } + if (i === j) { + return 1; + } + if (dp[i][j] !== -1) { + return dp[i][j]; + } + + if (s[i] === s[j]) { + dp[i][j] = dfs(i + 1, j - 1) + 2; + } else { + dp[i][j] = Math.max(dfs(i + 1, j), dfs(i, j - 1)); + } + + return dp[i][j]; + }; + + return dfs(0, n - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Using LCS Idea) + +::tabs-start + +```python +class Solution: + def longestPalindromeSubseq(self, s: str) -> int: + return self.longestCommonSubsequence(s, s[::-1]) + + def longestCommonSubsequence(self, s1: str, s2: str) -> int: + N, M = len(s1), len(s2) + dp = [[0] * (M + 1) for _ in range(N + 1)] + + for i in range(N): + for j in range(M): + if s1[i] == s2[j]: + dp[i + 1][j + 1] = 1 + dp[i][j] + else: + dp[i + 1][j + 1] = max(dp[i + 1][j], dp[i][j + 1]) + + return dp[N][M] +``` + +```java +public class Solution { + public int longestPalindromeSubseq(String s) { + return longestCommonSubsequence(s, new StringBuilder(s).reverse().toString()); + } + + public int longestCommonSubsequence(String s1, String s2) { + int N = s1.length(), M = s2.length(); + int[][] dp = new int[N + 1][M + 1]; + + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + if (s1.charAt(i) == s2.charAt(j)) { + dp[i + 1][j + 1] = 1 + dp[i][j]; + } else { + dp[i + 1][j + 1] = Math.max(dp[i + 1][j], dp[i][j + 1]); + } + } + } + + return dp[N][M]; + } +} +``` + +```cpp +class Solution { +public: + int longestPalindromeSubseq(string s) { + string reversedS = s; + reverse(reversedS.begin(), reversedS.end()); + return longestCommonSubsequence(s, reversedS); + } + + int longestCommonSubsequence(const string& s1, const string& s2) { + int N = s1.size(), M = s2.size(); + vector> dp(N + 1, vector(M + 1, 0)); + + for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + if (s1[i] == s2[j]) { + dp[i + 1][j + 1] = 1 + dp[i][j]; + } else { + dp[i + 1][j + 1] = max(dp[i + 1][j], dp[i][j + 1]); + } + } + } + + return dp[N][M]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + longestPalindromeSubseq(s) { + return this.longestCommonSubsequence(s, s.split('').reverse().join('')); + } + + /** + * @param {string} s1 + * @param {string} s2 + * @return {number} + */ + longestCommonSubsequence(s1, s2) { + const N = s1.length, M = s2.length; + const dp = Array.from({ length: N + 1 }, () => Array(M + 1).fill(0)); + + for (let i = 0; i < N; i++) { + for (let j = 0; j < M; j++) { + if (s1[i] === s2[j]) { + dp[i + 1][j + 1] = 1 + dp[i][j]; + } else { + dp[i + 1][j + 1] = Math.max(dp[i + 1][j], dp[i][j + 1]); + } + } + } + + return dp[N][M]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def longestPalindromeSubseq(self, s: str) -> int: + n = len(s) + dp = [0] * n + + for i in range(n - 1, -1, -1): + dp[i] = 1 + prev = 0 + for j in range(i + 1, n): + temp = dp[j] + + if s[i] == s[j]: + dp[j] = prev + 2 + else: + dp[j] = max(dp[j], dp[j - 1]) + + prev = temp + + return dp[n - 1] +``` + +```java +public class Solution { + public int longestPalindromeSubseq(String s) { + int n = s.length(); + int[] dp = new int[n]; + + for (int i = n - 1; i >= 0; i--) { + dp[i] = 1; + int prev = 0; + for (int j = i + 1; j < n; j++) { + int temp = dp[j]; + + if (s.charAt(i) == s.charAt(j)) { + dp[j] = prev + 2; + } else { + dp[j] = Math.max(dp[j], dp[j - 1]); + } + prev = temp; + } + } + + return dp[n - 1]; + } +} +``` + +```cpp +class Solution { +public: + int longestPalindromeSubseq(string s) { + int n = s.size(); + vector dp(n, 0); + + for (int i = n - 1; i >= 0; i--) { + dp[i] = 1; + int prev = 0; + for (int j = i + 1; j < n; j++) { + int temp = dp[j]; + + if (s[i] == s[j]) { + dp[j] = prev + 2; + } else { + dp[j] = max(dp[j], dp[j - 1]); + } + + prev = temp; + } + } + + return dp[n - 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + longestPalindromeSubseq(s) { + const n = s.length; + const dp = new Array(n).fill(0); + + for (let i = n - 1; i >= 0; i--) { + dp[i] = 1; + let prev = 0; + for (let j = i + 1; j < n; j++) { + const temp = dp[j]; + + if (s[i] === s[j]) { + dp[j] = prev + 2; + } else { + dp[j] = Math.max(dp[j], dp[j - 1]); + } + + prev = temp; + } + } + + return dp[n - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/longest-palindromic-substring.md b/articles/longest-palindromic-substring.md new file mode 100644 index 000000000..1d0f82c17 --- /dev/null +++ b/articles/longest-palindromic-substring.md @@ -0,0 +1,1054 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def longestPalindrome(self, s: str) -> str: + res, resLen = "", 0 + + for i in range(len(s)): + for j in range(i, len(s)): + l, r = i, j + while l < r and s[l] == s[r]: + l += 1 + r -= 1 + + if l >= r and resLen < (j - i + 1): + res = s[i : j + 1] + resLen = j - i + 1 + return res +``` + +```java +public class Solution { + public String longestPalindrome(String s) { + String res = ""; + int resLen = 0; + + for (int i = 0; i < s.length(); i++) { + for (int j = i; j < s.length(); j++) { + int l = i, r = j; + while (l < r && s.charAt(l) == s.charAt(r)) { + l++; + r--; + } + + if (l >= r && resLen < (j - i + 1)) { + res = s.substring(i, j + 1); + resLen = j - i + 1; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + string longestPalindrome(string s) { + string res = ""; + int resLen = 0; + + for (int i = 0; i < s.size(); i++) { + for (int j = i; j < s.size(); j++) { + int l = i, r = j; + while (l < r && s[l] == s[r]) { + l++; + r--; + } + + if (l >= r && resLen < (j - i + 1)) { + res = s.substr(i, j - i + 1); + resLen = j - i + 1; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + longestPalindrome(s) { + let res = ""; + let resLen = 0; + + for (let i = 0; i < s.length; i++) { + for (let j = i; j < s.length; j++) { + let l = i, r = j; + while (l < r && s[l] === s[r]) { + l++; + r--; + } + + if (l >= r && resLen < (j - i + 1)) { + res = s.slice(i, j + 1); + resLen = j - i + 1; + } + } + } + + return res; + } +} +``` + +```csharp +public class Solution { + public string LongestPalindrome(string s) { + string res = ""; + int resLen = 0; + + for (int i = 0; i < s.Length; i++) { + for (int j = i; j < s.Length; j++) { + int l = i, r = j; + while (l < r && s[l] == s[r]) { + l++; + r--; + } + + if (l >= r && resLen < (j - i + 1)) { + res = s.Substring(i, j - i + 1); + resLen = j - i + 1; + } + } + } + + return res; + } +} +``` + +```go +func longestPalindrome(s string) string { + res := "" + resLen := 0 + + for i := 0; i < len(s); i++ { + for j := i; j < len(s); j++ { + l, r := i, j + for l < r && s[l] == s[r] { + l++ + r-- + } + + if l >= r && resLen < (j-i+1) { + res = s[i : j+1] + resLen = j - i + 1 + } + } + } + + return res +} +``` + +```kotlin +class Solution { + fun longestPalindrome(s: String): String { + var res = "" + var resLen = 0 + + for (i in s.indices) { + for (j in i until s.length) { + var l = i + var r = j + while (l < r && s[l] == s[r]) { + l++ + r-- + } + + if (l >= r && resLen < (j - i + 1)) { + res = s.substring(i, j + 1) + resLen = j - i + 1 + } + } + } + + return res + } +} +``` + +```swift +class Solution { + func longestPalindrome(_ s: String) -> String { + let chars = Array(s) + var res = "" + var resLen = 0 + + for i in 0..= r && resLen < (j - i + 1) { + res = String(chars[i...j]) + resLen = j - i + 1 + } + } + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming + +::tabs-start + +```python +class Solution: + def longestPalindrome(self, s: str) -> str: + resIdx, resLen = 0, 0 + n = len(s) + + dp = [[False] * n for _ in range(n)] + + for i in range(n - 1, -1, -1): + for j in range(i, n): + if s[i] == s[j] and (j - i <= 2 or dp[i + 1][j - 1]): + dp[i][j] = True + if resLen < (j - i + 1): + resIdx = i + resLen = j - i + 1 + + return s[resIdx : resIdx + resLen] +``` + +```java +public class Solution { + public String longestPalindrome(String s) { + int resIdx = 0, resLen = 0; + int n = s.length(); + + boolean[][] dp = new boolean[n][n]; + + for (int i = n - 1; i >= 0; i--) { + for (int j = i; j < n; j++) { + if (s.charAt(i) == s.charAt(j) && + (j - i <= 2 || dp[i + 1][j - 1])) { + + dp[i][j] = true; + if (resLen < (j - i + 1)) { + resIdx = i; + resLen = j - i + 1; + } + } + } + } + + return s.substring(resIdx, resIdx + resLen); + } +} +``` + +```cpp +class Solution { +public: + string longestPalindrome(string s) { + int resIdx = 0, resLen = 0; + int n = s.size(); + + vector> dp(n, vector(n, false)); + + for (int i = n - 1; i >= 0; i--) { + for (int j = i; j < n; j++) { + if (s[i] == s[j] && + (j - i <= 2 || dp[i + 1][j - 1])) { + + dp[i][j] = true; + if (resLen < (j - i + 1)) { + resIdx = i; + resLen = j - i + 1; + } + } + } + } + + return s.substr(resIdx, resLen); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + longestPalindrome(s) { + let resIdx = 0, resLen = 0; + const n = s.length; + + const dp = Array.from({ length: n }, () => Array(n).fill(false)); + + for (let i = n - 1; i >= 0; i--) { + for (let j = i; j < n; j++) { + if (s[i] === s[j] && + (j - i <= 2 || dp[i + 1][j - 1])) { + + dp[i][j] = true; + if (resLen < (j - i + 1)) { + resIdx = i; + resLen = j - i + 1; + } + } + } + } + + return s.slice(resIdx, resIdx + resLen); + } +} +``` + +```csharp +public class Solution { + public string LongestPalindrome(string s) { + int resIdx = 0, resLen = 0; + int n = s.Length; + + bool[,] dp = new bool[n, n]; + + for (int i = n - 1; i >= 0; i--) { + for (int j = i; j < n; j++) { + if (s[i] == s[j] && + (j - i <= 2 || dp[i + 1, j - 1])) { + + dp[i, j] = true; + if (resLen < (j - i + 1)) { + resIdx = i; + resLen = j - i + 1; + } + } + } + } + + return s.Substring(resIdx, resLen); + } +} +``` + +```go +func longestPalindrome(s string) string { + n := len(s) + resIdx, resLen := 0, 0 + + dp := make([][]bool, n) + for i := range dp { + dp[i] = make([]bool, n) + } + + for i := n - 1; i >= 0; i-- { + for j := i; j < n; j++ { + if s[i] == s[j] && (j-i <= 2 || dp[i+1][j-1]) { + dp[i][j] = true + if resLen < (j-i+1) { + resIdx = i + resLen = j - i + 1 + } + } + } + } + + return s[resIdx : resIdx+resLen] +} +``` + +```kotlin +class Solution { + fun longestPalindrome(s: String): String { + val n = s.length + var resIdx = 0 + var resLen = 0 + + val dp = Array(n) { BooleanArray(n) } + + for (i in n - 1 downTo 0) { + for (j in i until n) { + if (s[i] == s[j] && (j - i <= 2 || dp[i + 1][j - 1])) { + dp[i][j] = true + if (resLen < (j - i + 1)) { + resIdx = i + resLen = j - i + 1 + } + } + } + } + + return s.substring(resIdx, resIdx + resLen) + } +} +``` + +```swift +class Solution { + func longestPalindrome(_ s: String) -> String { + let chars = Array(s) + let n = chars.count + var resIdx = 0, resLen = 0 + var dp = Array(repeating: Array(repeating: false, count: n), count: n) + + for i in stride(from: n - 1, through: 0, by: -1) { + for j in i.. str: + resIdx = 0 + resLen = 0 + + for i in range(len(s)): + # odd length + l, r = i, i + while l >= 0 and r < len(s) and s[l] == s[r]: + if (r - l + 1) > resLen: + resIdx = l + resLen = r - l + 1 + l -= 1 + r += 1 + + # even length + l, r = i, i + 1 + while l >= 0 and r < len(s) and s[l] == s[r]: + if (r - l + 1) > resLen: + resIdx = l + resLen = r - l + 1 + l -= 1 + r += 1 + + return s[resIdx : resIdx + resLen] +``` + +```java +class Solution { + public String longestPalindrome(String s) { + int resLen = 0, resIdx = 0; + + for (int i = 0; i < s.length(); i++) { + // odd length + int l = i, r = i; + while (l >= 0 && r < s.length() && + s.charAt(l) == s.charAt(r)) { + if (r - l + 1 > resLen) { + resIdx = l; + resLen = r - l + 1; + } + l--; + r++; + } + + // even length + l = i; + r = i + 1; + while (l >= 0 && r < s.length() && + s.charAt(l) == s.charAt(r)) { + if (r - l + 1 > resLen) { + resIdx = l; + resLen = r - l + 1; + } + l--; + r++; + } + } + + return s.substring(resIdx, resIdx + resLen); + } +} +``` + +```cpp +class Solution { +public: + string longestPalindrome(string s) { + int resLen = 0, resIdx = 0; + + for (int i = 0; i < s.size(); i++) { + // odd length + int l = i, r = i; + while (l >= 0 && r < s.size() && + s[l] == s[r]) { + if (r - l + 1 > resLen) { + resIdx = l; + resLen = r - l + 1; + } + l--; + r++; + } + + // even length + l = i; + r = i + 1; + while (l >= 0 && r < s.size() && + s[l] == s[r]) { + if (r - l + 1 > resLen) { + resIdx = l; + resLen = r - l + 1; + } + l--; + r++; + } + } + + return s.substr(resIdx, resLen); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + longestPalindrome(s) { + let resIdx = 0; + let resLen = 0; + + for (let i = 0; i < s.length; i++) { + // odd length + let l = i; + let r = i; + while (l >= 0 && r < s.length && + s.charAt(l) === s.charAt(r)) { + if (r - l + 1 > resLen) { + resIdx = l; + resLen = r - l + 1; + } + l--; + r++; + } + + // even length + l = i; + r = i + 1; + while (l >= 0 && r < s.length && + s.charAt(l) === s.charAt(r)) { + if (r - l + 1 > resLen) { + resIdx = l; + resLen = r - l + 1; + } + l--; + r++; + } + } + + return s.substring(resIdx, resIdx + resLen); + } +} +``` + +```csharp +public class Solution { + public string LongestPalindrome(string s) { + int resLen = 0, resIdx = 0; + + for (int i = 0; i < s.Length; i++) { + // odd length + int l = i, r = i; + while (l >= 0 && r < s.Length && + s[l] == s[r]) { + if (r - l + 1 > resLen) { + resIdx = l; + resLen = r - l + 1; + } + l--; + r++; + } + + // even length + l = i; + r = i + 1; + while (l >= 0 && r < s.Length && + s[l] == s[r]) { + if (r - l + 1 > resLen) { + resIdx = l; + resLen = r - l + 1; + } + l--; + r++; + } + } + + return s.Substring(resIdx, resLen); + } +} +``` + +```go +func longestPalindrome(s string) string { + resIdx, resLen := 0, 0 + + for i := 0; i < len(s); i++ { + // odd length + l, r := i, i + for l >= 0 && r < len(s) && s[l] == s[r] { + if r-l+1 > resLen { + resIdx = l + resLen = r - l + 1 + } + l-- + r++ + } + + // even length + l, r = i, i+1 + for l >= 0 && r < len(s) && s[l] == s[r] { + if r-l+1 > resLen { + resIdx = l + resLen = r - l + 1 + } + l-- + r++ + } + } + + return s[resIdx : resIdx+resLen] +} +``` + +```kotlin +class Solution { + fun longestPalindrome(s: String): String { + var resIdx = 0 + var resLen = 0 + + for (i in s.indices) { + // odd length + var l = i + var r = i + while (l >= 0 && r < s.length && s[l] == s[r]) { + if (r - l + 1 > resLen) { + resIdx = l + resLen = r - l + 1 + } + l-- + r++ + } + + // even length + l = i + r = i + 1 + while (l >= 0 && r < s.length && s[l] == s[r]) { + if (r - l + 1 > resLen) { + resIdx = l + resLen = r - l + 1 + } + l-- + r++ + } + } + + return s.substring(resIdx, resIdx + resLen) + } +} +``` + +```swift +class Solution { + func longestPalindrome(_ s: String) -> String { + let chars = Array(s) + var resIdx = 0 + var resLen = 0 + + for i in 0..= 0 && r < chars.count && chars[l] == chars[r] { + if (r - l + 1) > resLen { + resIdx = l + resLen = r - l + 1 + } + l -= 1 + r += 1 + } + + // Even length palindrome + l = i + r = i + 1 + while l >= 0 && r < chars.count && chars[l] == chars[r] { + if (r - l + 1) > resLen { + resIdx = l + resLen = r - l + 1 + } + l -= 1 + r += 1 + } + } + + return String(chars[resIdx.. str: + def manacher(s): + t = '#' + '#'.join(s) + '#' + n = len(t) + p = [0] * n + l, r = 0, 0 + for i in range(n): + p[i] = min(r - i, p[l + (r - i)]) if i < r else 0 + while (i + p[i] + 1 < n and i - p[i] - 1 >= 0 + and t[i + p[i] + 1] == t[i - p[i] - 1]): + p[i] += 1 + if i + p[i] > r: + l, r = i - p[i], i + p[i] + return p + + p = manacher(s) + resLen, center_idx = max((v, i) for i, v in enumerate(p)) + resIdx = (center_idx - resLen) // 2 + return s[resIdx : resIdx + resLen] +``` + +```java +public class Solution { + public int[] manacher(String s) { + StringBuilder t = new StringBuilder("#"); + for (char c : s.toCharArray()) { + t.append(c).append("#"); + } + int n = t.length(); + int[] p = new int[n]; + int l = 0, r = 0; + for (int i = 0; i < n; i++) { + p[i] = (i < r) ? Math.min(r - i, p[l + (r - i)]) : 0; + while (i + p[i] + 1 < n && i - p[i] - 1 >= 0 && + t.charAt(i + p[i] + 1) == t.charAt(i - p[i] - 1)) { + p[i]++; + } + if (i + p[i] > r) { + l = i - p[i]; + r = i + p[i]; + } + } + return p; + } + + public String longestPalindrome(String s) { + int[] p = manacher(s); + int resLen = 0, center_idx = 0; + for (int i = 0; i < p.length; i++) { + if (p[i] > resLen) { + resLen = p[i]; + center_idx = i; + } + } + int resIdx = (center_idx - resLen) / 2; + return s.substring(resIdx, resIdx + resLen); + } +} +``` + +```cpp +class Solution { +public: + vector manacher(string& s) { + string t = "#" + string(1, s[0]); + for (int i = 1; i < s.size(); ++i) + t += "#" + string(1, s[i]); + t += "#"; + int n = t.size(); + vector p(n, 0); + int l = 0, r = 0; + for (int i = 0; i < n; i++) { + p[i] = (i < r) ? min(r - i, p[l + (r - i)]) : 0; + while (i + p[i] + 1 < n && i - p[i] - 1 >= 0 && + t[i + p[i] + 1] == t[i - p[i] - 1]) + p[i]++; + if (i + p[i] > r) + l = i - p[i], r = i + p[i]; + } + return p; + } + + string longestPalindrome(string s) { + vector p = manacher(s); + int resLen = 0, center_idx = 0; + for (int i = 0; i < p.size(); i++) { + if (p[i] > resLen) { + resLen = p[i]; + center_idx = i; + } + } + int resIdx = (center_idx - resLen) / 2; + return s.substr(resIdx, resLen); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number[]} + */ + manacher(s) { + const t = '#' + s.split('').join('#') + '#'; + const n = t.length; + const p = new Array(n).fill(0); + let l = 0, r = 0; + for (let i = 0; i < n; i++) { + p[i] = (i < r) ? Math.min(r - i, p[l + (r - i)]) : 0; + while (i + p[i] + 1 < n && i - p[i] - 1 >= 0 && + t[i + p[i] + 1] === t[i - p[i] - 1]) { + p[i]++; + } + if (i + p[i] > r) { + l = i - p[i]; + r = i + p[i]; + } + } + return p; + } + + /** + * @param {string} s + * @return {string} + */ + longestPalindrome(s) { + const p = this.manacher(s); + let resLen = 0, center_idx = 0; + for (let i = 0; i < p.length; i++) { + if (p[i] > resLen) { + resLen = p[i]; + center_idx = i; + } + } + const resIdx = (center_idx - resLen) / 2; + return s.substring(resIdx, resIdx + resLen); + } +} +``` + +```csharp +public class Solution { + public int[] Manacher(string s) { + string t = "#" + string.Join("#", s.ToCharArray()) + "#"; + int n = t.Length; + int[] p = new int[n]; + int l = 0, r = 0; + for (int i = 0; i < n; i++) { + p[i] = (i < r) ? Math.Min(r - i, p[l + (r - i)]) : 0; + while (i + p[i] + 1 < n && i - p[i] - 1 >= 0 && + t[i + p[i] + 1] == t[i - p[i] - 1]) { + p[i]++; + } + if (i + p[i] > r) { + l = i - p[i]; + r = i + p[i]; + } + } + return p; + } + + public string LongestPalindrome(string s) { + int[] p = Manacher(s); + int resLen = 0, center_idx = 0; + for (int i = 0; i < p.Length; i++) { + if (p[i] > resLen) { + resLen = p[i]; + center_idx = i; + } + } + int resIdx = (center_idx - resLen) / 2; + return s.Substring(resIdx, resLen); + } +} +``` + +```go +func longestPalindrome(s string) string { + manacher := func(s string) []int { + t := "#" + for i := 0; i < len(s); i++ { + t += string(s[i]) + "#" + } + n := len(t) + p := make([]int, n) + l, r := 0, 0 + for i := 0; i < n; i++ { + if i < r { + p[i] = min(r-i, p[l+(r-i)]) + } + for i+p[i]+1 < n && i-p[i]-1 >= 0 && t[i+p[i]+1] == t[i-p[i]-1] { + p[i]++ + } + if i + p[i] > r { + l, r = i - p[i], i + p[i] + } + } + return p + } + + p := manacher(s) + resLen, centerIdx := 0, 0 + for i, v := range p { + if v > resLen { + resLen = v + centerIdx = i + } + } + + resIdx := (centerIdx - resLen) / 2 + return s[resIdx : resIdx + resLen] +} +``` + +```kotlin +class Solution { + fun longestPalindrome(s: String): String { + fun manacher(s: String): IntArray { + val t = "#" + s.chunked(1).joinToString("#") + "#" + val n = t.length + val p = IntArray(n) + var l = 0 + var r = 0 + + for (i in 0 until n) { + p[i] = if (i < r) Math.min(r - i, p[l + (r - i)]) else 0 + while (i + p[i] + 1 < n && i - p[i] - 1 >= 0 && + t[i + p[i] + 1] == t[i - p[i] - 1]) { + p[i]++ + } + if (i + p[i] > r) { + l = i - p[i] + r = i + p[i] + } + } + return p + } + + val p = manacher(s) + var resLen = 0 + var centerIdx = 0 + for (i in p.indices) { + if (p[i] > resLen) { + resLen = p[i] + centerIdx = i + } + } + + val resIdx = (centerIdx - resLen) / 2 + return s.substring(resIdx, resIdx + resLen) + } +} +``` + +```swift +class Solution { + func longestPalindrome(_ s: String) -> String { + func manacher(_ s: String) -> [Int] { + let t = "#" + s.map { String($0) }.joined(separator: "#") + "#" + let tArray = Array(t) + let n = tArray.count + var p = [Int](repeating: 0, count: n) + var l = 0, r = 0 + + for i in 0..= 0 && + tArray[i + p[i] + 1] == tArray[i - p[i] - 1]) { + p[i] += 1 + } + if i + p[i] > r { + l = i - p[i] + r = i + p[i] + } + } + return p + } + + let p = manacher(s) + var resLen = 0, centerIndex = 0 + for (i, v) in p.enumerated() { + if v > resLen { + resLen = v + centerIndex = i + } + } + let resIdx = (centerIndex - resLen) / 2 + let start = s.index(s.startIndex, offsetBy: resIdx) + let end = s.index(start, offsetBy: resLen) + return String(s[start.. int: + res = 0 + for i in range(len(s)): + count, maxf = {}, 0 + for j in range(i, len(s)): + count[s[j]] = 1 + count.get(s[j], 0) + maxf = max(maxf, count[s[j]]) + if (j - i + 1) - maxf <= k: + res = max(res, j - i + 1) + return res +``` + +```java +public class Solution { + public int characterReplacement(String s, int k) { + int res = 0; + for (int i = 0; i < s.length(); i++) { + HashMap count = new HashMap<>(); + int maxf = 0; + for (int j = i; j < s.length(); j++) { + count.put(s.charAt(j), count.getOrDefault(s.charAt(j), 0) + 1); + maxf = Math.max(maxf, count.get(s.charAt(j))); + if ((j - i + 1) - maxf <= k) { + res = Math.max(res, j - i + 1); + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int characterReplacement(string s, int k) { + int res = 0; + for (int i = 0; i < s.size(); i++) { + unordered_map count; + int maxf = 0; + for (int j = i; j < s.size(); j++) { + count[s[j]]++; + maxf = max(maxf, count[s[j]]); + if ((j - i + 1) - maxf <= k) { + res = max(res, j - i + 1); + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + characterReplacement(s, k) { + let res = 0; + for (let i = 0; i < s.length; i++) { + let count = new Map(); + let maxf = 0; + for (let j = i; j < s.length; j++) { + count.set(s[j], (count.get(s[j]) || 0) + 1); + maxf = Math.max(maxf, count.get(s[j])); + if ((j - i + 1) - maxf <= k) { + res = Math.max(res, j - i + 1); + } + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int CharacterReplacement(string s, int k) { + int res = 0; + for (int i = 0; i < s.Length; i++) { + Dictionary count = new Dictionary(); + int maxf = 0; + for (int j = i; j < s.Length; j++) { + if (count.ContainsKey(s[j])) { + count[s[j]]++; + } else { + count[s[j]] = 1; + } + maxf = Math.Max(maxf, count[s[j]]); + if ((j - i + 1) - maxf <= k) { + res = Math.Max(res, j - i + 1); + } + } + } + return res; + } +} +``` + +```go +func characterReplacement(s string, k int) int { + res := 0 + for i := 0; i < len(s); i++ { + count := make(map[byte]int) + maxf := 0 + for j := i; j < len(s); j++ { + count[s[j]]++ + maxf = max(maxf, count[s[j]]) + if (j - i + 1) - maxf <= k { + res = max(res, j - i + 1) + } + } + } + return res +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun characterReplacement(s: String, k: Int): Int { + var res = 0 + for (i in s.indices) { + val count = HashMap() + var maxf = 0 + for (j in i until s.length) { + count[s[j]] = count.getOrDefault(s[j], 0) + 1 + maxf = maxOf(maxf, count[s[j]]!!) + if ((j - i + 1) - maxf <= k) { + res = maxOf(res, j - i + 1) + } + } + } + return res + } +} +``` + +```swift +class Solution { + func characterReplacement(_ s: String, _ k: Int) -> Int { + var res = 0 + let chars = Array(s) + + for i in 0.. Where $n$ is the length of the string and $m$ is the total number of unique characters in the string. + +--- + +## 2. Sliding Window + +::tabs-start + +```python +class Solution: + def characterReplacement(self, s: str, k: int) -> int: + res = 0 + charSet = set(s) + + for c in charSet: + count = l = 0 + for r in range(len(s)): + if s[r] == c: + count += 1 + + while (r - l + 1) - count > k: + if s[l] == c: + count -= 1 + l += 1 + + res = max(res, r - l + 1) + return res +``` + +```java +public class Solution { + public int characterReplacement(String s, int k) { + int res = 0; + HashSet charSet = new HashSet<>(); + for (char c : s.toCharArray()) { + charSet.add(c); + } + + for (char c : charSet) { + int count = 0, l = 0; + for (int r = 0; r < s.length(); r++) { + if (s.charAt(r) == c) { + count++; + } + + while ((r - l + 1) - count > k) { + if (s.charAt(l) == c) { + count--; + } + l++; + } + + res = Math.max(res, r - l + 1); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int characterReplacement(std::string s, int k) { + int res = 0; + unordered_set charSet(s.begin(), s.end()); + + for (char c : charSet) { + int count = 0, l = 0; + for (int r = 0; r < s.size(); r++) { + if (s[r] == c) { + count++; + } + + while ((r - l + 1) - count > k) { + if (s[l] == c) { + count--; + } + l++; + } + + res = std::max(res, r - l + 1); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + characterReplacement(s, k) { + let res = 0; + let charSet = new Set(s); + + for (let c of charSet) { + let count = 0, l = 0; + for (let r = 0; r < s.length; r++) { + if (s[r] === c) { + count++; + } + + while ((r - l + 1) - count > k) { + if (s[l] === c) { + count--; + } + l++; + } + + res = Math.max(res, r - l + 1); + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int CharacterReplacement(string s, int k) { + int res = 0; + HashSet charSet = new HashSet(s); + + foreach (char c in charSet) { + int count = 0, l = 0; + for (int r = 0; r < s.Length; r++) { + if (s[r] == c) { + count++; + } + + while ((r - l + 1) - count > k) { + if (s[l] == c) { + count--; + } + l++; + } + + res = Math.Max(res, r - l + 1); + } + } + return res; + } +} +``` + +```go +func characterReplacement(s string, k int) int { + res := 0 + charSet := make(map[byte]bool) + + for i := 0; i < len(s); i++ { + charSet[s[i]] = true + } + + for c := range charSet { + count, l := 0, 0 + for r := 0; r < len(s); r++ { + if s[r] == c { + count++ + } + + for (r - l + 1) - count > k { + if s[l] == c { + count-- + } + l++ + } + + res = max(res, r - l + 1) + } + } + + return res +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun characterReplacement(s: String, k: Int): Int { + var res = 0 + val charSet = s.toSet() + + for (c in charSet) { + var count = 0 + var l = 0 + for (r in s.indices) { + if (s[r] == c) { + count++ + } + + while ((r - l + 1) - count > k) { + if (s[l] == c) { + count-- + } + l++ + } + + res = maxOf(res, r - l + 1) + } + } + + return res + } +} +``` + +```swift +class Solution { + func characterReplacement(_ s: String, _ k: Int) -> Int { + var res = 0 + let charSet = Set(s) + let chars = Array(s) + + for c in charSet { + var count = 0, l = 0 + + for r in 0.. k { + if chars[l] == c { + count -= 1 + } + l += 1 + } + + res = max(res, r - l + 1) + } + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string and $m$ is the total number of unique characters in the string. + +--- + +## 3. Sliding Window (Optimal) + +::tabs-start + +```python +class Solution: + def characterReplacement(self, s: str, k: int) -> int: + count = {} + res = 0 + + l = 0 + maxf = 0 + for r in range(len(s)): + count[s[r]] = 1 + count.get(s[r], 0) + maxf = max(maxf, count[s[r]]) + + while (r - l + 1) - maxf > k: + count[s[l]] -= 1 + l += 1 + res = max(res, r - l + 1) + + return res +``` + +```java +public class Solution { + public int characterReplacement(String s, int k) { + HashMap count = new HashMap<>(); + int res = 0; + + int l = 0, maxf = 0; + for (int r = 0; r < s.length(); r++) { + count.put(s.charAt(r), count.getOrDefault(s.charAt(r), 0) + 1); + maxf = Math.max(maxf, count.get(s.charAt(r))); + + while ((r - l + 1) - maxf > k) { + count.put(s.charAt(l), count.get(s.charAt(l)) - 1); + l++; + } + res = Math.max(res, r - l + 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int characterReplacement(std::string s, int k) { + unordered_map count; + int res = 0; + + int l = 0, maxf = 0; + for (int r = 0; r < s.size(); r++) { + count[s[r]]++; + maxf = max(maxf, count[s[r]]); + + while ((r - l + 1) - maxf > k) { + count[s[l]]--; + l++; + } + res = max(res, r - l + 1); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + characterReplacement(s, k) { + let count = new Map(); + let res = 0; + + let l = 0, maxf = 0; + for (let r = 0; r < s.length; r++) { + count.set(s[r], (count.get(s[r]) || 0) + 1); + maxf = Math.max(maxf, count.get(s[r])); + + while ((r - l + 1) - maxf > k) { + count.set(s[l], count.get(s[l]) - 1); + l++; + } + res = Math.max(res, r - l + 1); + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int CharacterReplacement(string s, int k) { + Dictionary count = new Dictionary(); + int res = 0; + + int l = 0, maxf = 0; + for (int r = 0; r < s.Length; r++) { + if (count.ContainsKey(s[r])) { + count[s[r]]++; + } else { + count[s[r]] = 1; + } + maxf = Math.Max(maxf, count[s[r]]); + + while ((r - l + 1) - maxf > k) { + count[s[l]]--; + l++; + } + res = Math.Max(res, r - l + 1); + } + + return res; + } +} +``` + +```go +func characterReplacement(s string, k int) int { + count := make(map[byte]int) + res, l, maxf := 0, 0, 0 + + for r := 0; r < len(s); r++ { + count[s[r]]++ + if count[s[r]] > maxf { + maxf = count[s[r]] + } + + for (r - l + 1) - maxf > k { + count[s[l]]-- + l++ + } + + if r - l + 1 > res { + res = r - l + 1 + } + } + + return res +} +``` + +```kotlin +class Solution { + fun characterReplacement(s: String, k: Int): Int { + val count = mutableMapOf() + var res = 0 + var l = 0 + var maxf = 0 + + for (r in s.indices) { + count[s[r]] = count.getOrDefault(s[r], 0) + 1 + maxf = maxOf(maxf, count[s[r]]!!) + + while (r - l + 1 - maxf > k) { + count[s[l]] = count[s[l]]!! - 1 + l++ + } + + res = maxOf(res, r - l + 1) + } + + return res + } +} +``` + +```swift +class Solution { + func characterReplacement(_ s: String, _ k: Int) -> Int { + var count = [Character: Int]() + var res = 0 + var l = 0, maxf = 0 + let chars = Array(s) + + for r in 0.. k { + count[chars[l], default: 0] -= 1 + l += 1 + } + res = max(res, r - l + 1) + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string and $m$ is the total number of unique characters in the string. \ No newline at end of file diff --git a/articles/longest-strictly-increasing-or-strictly-decreasing-subarray.md b/articles/longest-strictly-increasing-or-strictly-decreasing-subarray.md new file mode 100644 index 000000000..4c55b7a6b --- /dev/null +++ b/articles/longest-strictly-increasing-or-strictly-decreasing-subarray.md @@ -0,0 +1,454 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def longestMonotonicSubarray(self, nums: List[int]) -> int: + n = len(nums) + res = 1 + + for i in range(n - 1): + curLen = 1 + for j in range(i + 1, n): + if nums[j] == nums[j - 1] or ((nums[i] < nums[i + 1]) != (nums[j - 1] < nums[j])): + break + curLen += 1 + + res = max(res, curLen) + + return res +``` + +```java +public class Solution { + public int longestMonotonicSubarray(int[] nums) { + int n = nums.length; + int res = 1; + + for (int i = 0; i < n - 1; i++) { + int curLen = 1; + for (int j = i + 1; j < n; j++) { + if (nums[j] == nums[j - 1] || ((nums[i] < nums[i + 1]) != (nums[j - 1] < nums[j]))) { + break; + } + curLen++; + } + res = Math.max(res, curLen); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int longestMonotonicSubarray(vector& nums) { + int n = nums.size(); + int res = 1; + + for (int i = 0; i < n - 1; i++) { + int curLen = 1; + for (int j = i + 1; j < n; j++) { + if (nums[j] == nums[j - 1] || ((nums[i] < nums[i + 1]) != (nums[j - 1] < nums[j]))) { + break; + } + curLen++; + } + res = max(res, curLen); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + longestMonotonicSubarray(nums) { + let n = nums.length; + let res = 1; + + for (let i = 0; i < n - 1; i++) { + let curLen = 1; + for (let j = i + 1; j < n; j++) { + if (nums[j] === nums[j - 1] || ((nums[i] < nums[i + 1]) !== (nums[j - 1] < nums[j]))) { + break; + } + curLen++; + } + res = Math.max(res, curLen); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Iteration - I + +::tabs-start + +```python +class Solution: + def longestMonotonicSubarray(self, nums: List[int]) -> int: + cur = 1 + res = 1 + increasing = 0 + + for i in range(1, len(nums)): + if nums[i - 1] < nums[i]: + if increasing > 0: + cur += 1 + else: + cur = 2 + increasing = 1 + elif nums[i - 1] > nums[i]: + if increasing < 0: + cur += 1 + else: + cur = 2 + increasing = -1 + else: + cur = 1 + increasing = 0 + res = max(res, cur) + + return res +``` + +```java +public class Solution { + public int longestMonotonicSubarray(int[] nums) { + int cur = 1; + int res = 1; + int increasing = 0; + + for (int i = 1; i < nums.length; i++) { + if (nums[i - 1] < nums[i]) { + if (increasing > 0) { + cur++; + } else { + cur = 2; + increasing = 1; + } + } else if (nums[i - 1] > nums[i]) { + if (increasing < 0) { + cur++; + } else { + cur = 2; + increasing = -1; + } + } else { + cur = 1; + increasing = 0; + } + res = Math.max(res, cur); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int longestMonotonicSubarray(vector& nums) { + int cur = 1; + int res = 1; + int increasing = 0; + + for (int i = 1; i < nums.size(); i++) { + if (nums[i - 1] < nums[i]) { + if (increasing > 0) { + cur++; + } else { + cur = 2; + increasing = 1; + } + } else if (nums[i - 1] > nums[i]) { + if (increasing < 0) { + cur++; + } else { + cur = 2; + increasing = -1; + } + } else { + cur = 1; + increasing = 0; + } + res = max(res, cur); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + longestMonotonicSubarray(nums) { + let cur = 1; + let res = 1; + let increasing = 0; + + for (let i = 1; i < nums.length; i++) { + if (nums[i - 1] < nums[i]) { + if (increasing > 0) { + cur++; + } else { + cur = 2; + increasing = 1; + } + } else if (nums[i - 1] > nums[i]) { + if (increasing < 0) { + cur++; + } else { + cur = 2; + increasing = -1; + } + } else { + cur = 1; + increasing = 0; + } + res = Math.max(res, cur); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Iteration - II + +::tabs-start + +```python +class Solution: + def longestMonotonicSubarray(self, nums: List[int]) -> int: + inc = dec = 1 + res = 1 + + for i in range(1, len(nums)): + if nums[i] == nums[i - 1]: + inc = dec = 1 + elif nums[i] > nums[i - 1]: + inc, dec = inc + 1, 1 + else: + inc, dec = 1, dec + 1 + + res = max(res, inc, dec) + + return res +``` + +```java +public class Solution { + public int longestMonotonicSubarray(int[] nums) { + int inc = 1, dec = 1, res = 1; + + for (int i = 1; i < nums.length; i++) { + if (nums[i] == nums[i - 1]) { + inc = dec = 1; + } else if (nums[i] > nums[i - 1]) { + inc = inc + 1; + dec = 1; + } else { + inc = 1; + dec = dec + 1; + } + res = Math.max(res, Math.max(inc, dec)); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int longestMonotonicSubarray(vector& nums) { + int inc = 1, dec = 1, res = 1; + + for (int i = 1; i < nums.size(); i++) { + if (nums[i] == nums[i - 1]) { + inc = dec = 1; + } else if (nums[i] > nums[i - 1]) { + inc = inc + 1; + dec = 1; + } else { + inc = 1; + dec = dec + 1; + } + res = max(res, max(inc, dec)); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + longestMonotonicSubarray(nums) { + let inc = 1, dec = 1, res = 1; + + for (let i = 1; i < nums.length; i++) { + if (nums[i] === nums[i - 1]) { + inc = dec = 1; + } else if (nums[i] > nums[i - 1]) { + inc = inc + 1; + dec = 1; + } else { + inc = 1; + dec = dec + 1; + } + res = Math.max(res, inc, dec); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Iteration - III + +::tabs-start + +```python +class Solution: + def longestMonotonicSubarray(self, nums: List[int]) -> int: + curLen = res = 1 + + for i in range(1, len(nums)): + if (nums[i] == nums[i - 1] or + ((nums[i - curLen] < nums[i - curLen + 1]) != (nums[i - 1] < nums[i])) + ): + curLen = 1 if (nums[i] == nums[i - 1]) else 2 + continue + + curLen += 1 + res = max(res, curLen) + + return res +``` + +```java +public class Solution { + public int longestMonotonicSubarray(int[] nums) { + int curLen = 1, res = 1; + + for (int i = 1; i < nums.length; i++) { + if (nums[i] == nums[i - 1] || + ((nums[i - curLen] < nums[i - curLen + 1]) != (nums[i - 1] < nums[i]))) { + curLen = (nums[i] == nums[i - 1]) ? 1 : 2; + continue; + } + + curLen++; + res = Math.max(res, curLen); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int longestMonotonicSubarray(vector& nums) { + int curLen = 1, res = 1; + + for (int i = 1; i < nums.size(); i++) { + if (nums[i] == nums[i - 1] || + ((nums[i - curLen] < nums[i - curLen + 1]) != (nums[i - 1] < nums[i]))) { + curLen = (nums[i] == nums[i - 1]) ? 1 : 2; + continue; + } + + curLen++; + res = max(res, curLen); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + longestMonotonicSubarray(nums) { + let curLen = 1, res = 1; + + for (let i = 1; i < nums.length; i++) { + if ( + nums[i] === nums[i - 1] || + ((nums[i - curLen] < nums[i - curLen + 1]) !== (nums[i - 1] < nums[i])) + ) { + curLen = nums[i] === nums[i - 1] ? 1 : 2; + continue; + } + + curLen++; + res = Math.max(res, curLen); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/longest-string-chain.md b/articles/longest-string-chain.md new file mode 100644 index 000000000..b033bad4a --- /dev/null +++ b/articles/longest-string-chain.md @@ -0,0 +1,435 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def longestStrChain(self, words: List[str]) -> int: + words.sort(key=lambda w: -len(w)) + word_index = {} # map word to index + for i, w in enumerate(words): + word_index[w] = i + + dp = {} # index of word -> length of longest chain + def dfs(i): + if i in dp: + return dp[i] + res = 1 + for j in range(len(words[i])): + w = words[i] + pred = w[:j] + w[j+1:] + if pred in word_index: + res = max(res, 1 + dfs(word_index[pred])) + dp[i] = res + return res + + for i in range(len(words)): + dfs(i) + return max(dp.values()) +``` + +```java +public class Solution { + public int longestStrChain(String[] words) { + Arrays.sort(words, (a, b) -> Integer.compare(b.length(), a.length())); + Map wordIndex = new HashMap<>(); + for (int i = 0; i < words.length; i++) { + wordIndex.put(words[i], i); + } + + int[] dp = new int[words.length]; + Arrays.fill(dp, -1); + + int maxChain = 1; + for (int i = 0; i < words.length; i++) { + maxChain = Math.max(maxChain, dfs(i, words, wordIndex, dp)); + } + return maxChain; + } + + private int dfs(int i, String[] words, Map wordIndex, int[] dp) { + if (dp[i] != -1) { + return dp[i]; + } + + int res = 1; + String w = words[i]; + for (int j = 0; j < w.length(); j++) { + String pred = w.substring(0, j) + w.substring(j + 1); + if (wordIndex.containsKey(pred)) { + res = Math.max(res, 1 + dfs(wordIndex.get(pred), words, wordIndex, dp)); + } + } + dp[i] = res; + return res; + } +} +``` + +```cpp +class Solution { +public: + int longestStrChain(vector& words) { + sort(words.begin(), words.end(), [](const string& a, const string& b) { + return b.length() < a.length(); + }); + + unordered_map wordIndex; + for (int i = 0; i < words.size(); i++) { + wordIndex[words[i]] = i; + } + + vector dp(words.size(), -1); + int maxChain = 1; + for (int i = 0; i < words.size(); i++) { + maxChain = max(maxChain, dfs(i, words, wordIndex, dp)); + } + return maxChain; + } + +private: + int dfs(int i, vector& words, unordered_map& wordIndex, vector& dp) { + if (dp[i] != -1) { + return dp[i]; + } + + int res = 1; + string w = words[i]; + for (int j = 0; j < w.length(); j++) { + string pred = w.substr(0, j) + w.substr(j + 1); + if (wordIndex.find(pred) != wordIndex.end()) { + res = max(res, 1 + dfs(wordIndex[pred], words, wordIndex, dp)); + } + } + dp[i] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {number} + */ + longestStrChain(words) { + words.sort((a, b) => b.length - a.length); + const wordIndex = new Map(); + for (let i = 0; i < words.length; i++) { + wordIndex.set(words[i], i); + } + + const dp = new Array(words.length).fill(-1); + + const dfs = (i) => { + if (dp[i] !== -1) { + return dp[i]; + } + + let res = 1; + const w = words[i]; + for (let j = 0; j < w.length; j++) { + const pred = w.slice(0, j) + w.slice(j + 1); + if (wordIndex.has(pred)) { + res = Math.max(res, 1 + dfs(wordIndex.get(pred))); + } + } + dp[i] = res; + return res; + }; + + let maxChain = 1; + for (let i = 0; i < words.length; i++) { + maxChain = Math.max(maxChain, dfs(i)); + } + return maxChain; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m ^ 2)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of words and $m$ is the average length of each word. + +--- + +## 2. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def longestStrChain(self, words: List[str]) -> int: + def isPred(w1, w2): + i = 0 + for c in w2: + if i == len(w1): + return True + if w1[i] == c: + i += 1 + return i == len(w1) + + words.sort(key=len) + n = len(words) + dp = [1] * n + + for i in range(1, n): + for j in range(i - 1, -1, -1): + if len(words[j]) + 1 < len(words[i]): + break + if len(words[j]) + 1 > len(words[i]) or not isPred(words[j], words[i]): + continue + dp[i] = max(dp[i], 1 + dp[j]) + + return max(dp) +``` + +```java +public class Solution { + public int longestStrChain(String[] words) { + Arrays.sort(words, Comparator.comparingInt(String::length)); + int n = words.length; + int[] dp = new int[n]; + Arrays.fill(dp, 1); + + for (int i = 1; i < n; i++) { + for (int j = i - 1; j >= 0; j--) { + if (words[j].length() + 1 < words[i].length()) { + break; + } + if (words[j].length() + 1 > words[i].length() || !isPred(words[j], words[i])) { + continue; + } + dp[i] = Math.max(dp[i], 1 + dp[j]); + } + } + + int maxChain = 0; + for (int chain : dp) { + maxChain = Math.max(maxChain, chain); + } + return maxChain; + } + + private boolean isPred(String w1, String w2) { + int i = 0; + for (char c : w2.toCharArray()) { + if (i == w1.length()) { + return true; + } + if (w1.charAt(i) == c) { + i++; + } + } + return i == w1.length(); + } +} +``` + +```cpp +class Solution { +public: + int longestStrChain(vector& words) { + sort(words.begin(), words.end(), [](const string& a, const string& b) { + return a.length() < b.length(); + }); + + int n = words.size(); + vector dp(n, 1); + + for (int i = 1; i < n; i++) { + for (int j = i - 1; j >= 0; j--) { + if (words[j].length() + 1 < words[i].length()) { + break; + } + if (words[j].length() + 1 > words[i].length() || !isPred(words[j], words[i])) { + continue; + } + dp[i] = max(dp[i], 1 + dp[j]); + } + } + + return *max_element(dp.begin(), dp.end()); + } + +private: + bool isPred(const string& w1, const string& w2) { + int i = 0; + for (char c : w2) { + if (i == w1.length()) { + return true; + } + if (w1[i] == c) { + i++; + } + } + return i == w1.length(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {number} + */ + longestStrChain(words) { + words.sort((a, b) => a.length - b.length); + const n = words.length; + const dp = new Array(n).fill(1); + + const isPred = (w1, w2) => { + let i = 0; + for (const c of w2) { + if (i === w1.length) { + return true; + } + if (w1[i] === c) { + i++; + } + } + return i === w1.length; + }; + + for (let i = 1; i < n; i++) { + for (let j = i - 1; j >= 0; j--) { + if (words[j].length + 1 < words[i].length) { + break; + } + if (words[j].length + 1 > words[i].length || !isPred(words[j], words[i])) { + continue; + } + dp[i] = Math.max(dp[i], 1 + dp[j]); + } + } + + return Math.max(...dp); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of words and $m$ is the average length of each word. + +--- + +## 3. Dynamic Programming (Bottom-Up Optimized) + +::tabs-start + +```python +class Solution: + def longestStrChain(self, words: List[str]) -> int: + words.sort(key=len) + dp = {} + res = 0 + + for word in words: + dp[word] = 1 + for i in range(len(word)): + pred = word[:i] + word[i+1:] + if pred in dp: + dp[word] = max(dp[word], dp[pred] + 1) + res = max(res, dp[word]) + + return res +``` + +```java +public class Solution { + public int longestStrChain(String[] words) { + Arrays.sort(words, Comparator.comparingInt(String::length)); + Map dp = new HashMap<>(); + int res = 0; + + for (String word : words) { + dp.put(word, 1); + for (int i = 0; i < word.length(); i++) { + String pred = word.substring(0, i) + word.substring(i + 1); + if (dp.containsKey(pred)) { + dp.put(word, Math.max(dp.get(word), dp.get(pred) + 1)); + } + } + res = Math.max(res, dp.get(word)); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int longestStrChain(vector& words) { + sort(words.begin(), words.end(), [](const string& a, const string& b) { + return a.length() < b.length(); + }); + + unordered_map dp; + int res = 0; + + for (const string& word : words) { + dp[word] = 1; + for (int i = 0; i < word.length(); i++) { + string pred = word.substr(0, i) + word.substr(i + 1); + if (dp.find(pred) != dp.end()) { + dp[word] = max(dp[word], dp[pred] + 1); + } + } + res = max(res, dp[word]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {number} + */ + longestStrChain(words) { + words.sort((a, b) => a.length - b.length); + const dp = new Map(); + let res = 0; + + for (const word of words) { + dp.set(word, 1); + for (let i = 0; i < word.length; i++) { + const pred = word.slice(0, i) + word.slice(i + 1); + if (dp.has(pred)) { + dp.set(word, Math.max(dp.get(word), dp.get(pred) + 1)); + } + } + res = Math.max(res, dp.get(word)); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m ^ 2)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of words and $m$ is the average length of each word. \ No newline at end of file diff --git a/articles/longest-substring-without-duplicates.md b/articles/longest-substring-without-duplicates.md new file mode 100644 index 000000000..3d197dc21 --- /dev/null +++ b/articles/longest-substring-without-duplicates.md @@ -0,0 +1,506 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def lengthOfLongestSubstring(self, s: str) -> int: + res = 0 + for i in range(len(s)): + charSet = set() + for j in range(i, len(s)): + if s[j] in charSet: + break + charSet.add(s[j]) + res = max(res, len(charSet)) + return res +``` + +```java +public class Solution { + public int lengthOfLongestSubstring(String s) { + int res = 0; + for (int i = 0; i < s.length(); i++) { + Set charSet = new HashSet<>(); + for (int j = i; j < s.length(); j++) { + if (charSet.contains(s.charAt(j))) { + break; + } + charSet.add(s.charAt(j)); + } + res = Math.max(res, charSet.size()); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int lengthOfLongestSubstring(string s) { + int res = 0; + for (int i = 0; i < s.size(); i++) { + unordered_set charSet; + for (int j = i; j < s.size(); j++) { + if (charSet.find(s[j]) != charSet.end()) { + break; + } + charSet.insert(s[j]); + } + res = max(res, (int)charSet.size()); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + lengthOfLongestSubstring(s) { + let res = 0; + for (let i = 0; i < s.length; i++) { + let charSet = new Set(); + for (let j = i; j < s.length; j++) { + if (charSet.has(s[j])) { + break; + } + charSet.add(s[j]); + } + res = Math.max(res, charSet.size); + } + return res; + } +} +``` + +```csharp +public class Solution { + public int LengthOfLongestSubstring(string s) { + int res = 0; + for (int i = 0; i < s.Length; i++) { + HashSet charSet = new HashSet(); + for (int j = i; j < s.Length; j++) { + if (charSet.Contains(s[j])) { + break; + } + charSet.Add(s[j]); + } + res = Math.Max(res, charSet.Count); + } + return res; + } +} +``` + +```go +func lengthOfLongestSubstring(s string) int { + res := 0 + + for i := 0; i < len(s); i++ { + charSet := make(map[byte]bool) + for j := i; j < len(s); j++ { + if charSet[s[j]] { + break + } + charSet[s[j]] = true + } + if len(charSet) > res { + res = len(charSet) + } + } + return res +} +``` + +```kotlin +class Solution { + fun lengthOfLongestSubstring(s: String): Int { + var res = 0 + + for (i in s.indices) { + val charSet = mutableSetOf() + for (j in i until s.length) { + if (s[j] in charSet) { + break + } + charSet.add(s[j]) + } + res = maxOf(res, charSet.size) + } + return res + } +} +``` + +```kotlin +class Solution { + func lengthOfLongestSubstring(_ s: String) -> Int { + var res = 0 + let chars = Array(s) + + for i in 0..() + for j in i.. Where $n$ is the length of the string and $m$ is the total number of unique characters in the string. + +--- + +## 2. Sliding Window + +::tabs-start + +```python +class Solution: + def lengthOfLongestSubstring(self, s: str) -> int: + charSet = set() + l = 0 + res = 0 + + for r in range(len(s)): + while s[r] in charSet: + charSet.remove(s[l]) + l += 1 + charSet.add(s[r]) + res = max(res, r - l + 1) + return res +``` + +```java +public class Solution { + public int lengthOfLongestSubstring(String s) { + HashSet charSet = new HashSet<>(); + int l = 0; + int res = 0; + + for (int r = 0; r < s.length(); r++) { + while (charSet.contains(s.charAt(r))) { + charSet.remove(s.charAt(l)); + l++; + } + charSet.add(s.charAt(r)); + res = Math.max(res, r - l + 1); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int lengthOfLongestSubstring(string s) { + unordered_set charSet; + int l = 0; + int res = 0; + + for (int r = 0; r < s.size(); r++) { + while (charSet.find(s[r]) != charSet.end()) { + charSet.erase(s[l]); + l++; + } + charSet.insert(s[r]); + res = max(res, r - l + 1); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + lengthOfLongestSubstring(s) { + const charSet = new Set(); + let l = 0; + let res = 0; + + for (let r = 0; r < s.length; r++) { + while (charSet.has(s[r])) { + charSet.delete(s[l]); + l++; + } + charSet.add(s[r]); + res = Math.max(res, r - l + 1); + } + return res; + } +} +``` + +```csharp +public class Solution { + public int LengthOfLongestSubstring(string s) { + HashSet charSet = new HashSet(); + int l = 0; + int res = 0; + + for (int r = 0; r < s.Length; r++) { + while (charSet.Contains(s[r])) { + charSet.Remove(s[l]); + l++; + } + charSet.Add(s[r]); + res = Math.Max(res, r - l + 1); + } + return res; + } +} +``` + +```go +func lengthOfLongestSubstring(s string) int { + charSet := make(map[byte]bool) + l, res := 0, 0 + + for r := 0; r < len(s); r++ { + for charSet[s[r]] { + delete(charSet, s[l]) + l++ + } + charSet[s[r]] = true + if r - l + 1 > res { + res = r - l + 1 + } + } + return res +} +``` + +```kotlin +class Solution { + fun lengthOfLongestSubstring(s: String): Int { + val charSet = HashSet() + var l = 0 + var res = 0 + + for (r in s.indices) { + while (s[r] in charSet) { + charSet.remove(s[l]) + l++ + } + charSet.add(s[r]) + res = maxOf(res, r - l + 1) + } + return res + } +} +``` + +```swift +class Solution { + func lengthOfLongestSubstring(_ s: String) -> Int { + var charSet = Set() + var l = 0, res = 0 + let chars = Array(s) + + for r in 0.. Where $n$ is the length of the string and $m$ is the total number of unique characters in the string. + +--- + +## 3. Sliding Window (Optimal) + +::tabs-start + +```python +class Solution: + def lengthOfLongestSubstring(self, s: str) -> int: + mp = {} + l = 0 + res = 0 + + for r in range(len(s)): + if s[r] in mp: + l = max(mp[s[r]] + 1, l) + mp[s[r]] = r + res = max(res, r - l + 1) + return res +``` + +```java +public class Solution { + public int lengthOfLongestSubstring(String s) { + HashMap mp = new HashMap<>(); + int l = 0, res = 0; + + for (int r = 0; r < s.length(); r++) { + if (mp.containsKey(s.charAt(r))) { + l = Math.max(mp.get(s.charAt(r)) + 1, l); + } + mp.put(s.charAt(r), r); + res = Math.max(res, r - l + 1); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int lengthOfLongestSubstring(string s) { + unordered_map mp; + int l = 0, res = 0; + + for (int r = 0; r < s.size(); r++) { + if (mp.find(s[r]) != mp.end()) { + l = max(mp[s[r]] + 1, l); + } + mp[s[r]] = r; + res = max(res, r - l + 1); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + lengthOfLongestSubstring(s) { + let mp = new Map(); + let l = 0, res = 0; + + for (let r = 0; r < s.length; r++) { + if (mp.has(s[r])) { + l = Math.max(mp.get(s[r]) + 1, l); + } + mp.set(s[r], r); + res = Math.max(res, r - l + 1); + } + return res; + } +} +``` + +```csharp +public class Solution { + public int LengthOfLongestSubstring(string s) { + Dictionary mp = new Dictionary(); + int l = 0, res = 0; + + for (int r = 0; r < s.Length; r++) { + if (mp.ContainsKey(s[r])) { + l = Math.Max(mp[s[r]] + 1, l); + } + mp[s[r]] = r; + res = Math.Max(res, r - l + 1); + } + return res; + } +} +``` + +```go +func lengthOfLongestSubstring(s string) int { + mp := make(map[byte]int) + l, res := 0, 0 + + for r := 0; r < len(s); r++ { + if idx, found := mp[s[r]]; found { + l = max(idx+1, l) + } + mp[s[r]] = r + if r - l + 1 > res { + res = r - l + 1 + } + } + return res +} +``` + +```kotlin +class Solution { + fun lengthOfLongestSubstring(s: String): Int { + val mp = HashMap() + var l = 0 + var res = 0 + + for (r in s.indices) { + if (s[r] in mp) { + l = maxOf(mp[s[r]]!! + 1, l) + } + mp[s[r]] = r + res = maxOf(res, r - l + 1) + } + return res + } +} +``` + +```swift +class Solution { + func lengthOfLongestSubstring(_ s: String) -> Int { + var mp = [Character: Int]() + var l = 0, res = 0 + let chars = Array(s) + + for r in 0.. Where $n$ is the length of the string and $m$ is the total number of unique characters in the string. \ No newline at end of file diff --git a/articles/longest-turbulent-subarray.md b/articles/longest-turbulent-subarray.md new file mode 100644 index 000000000..a7876d5c0 --- /dev/null +++ b/articles/longest-turbulent-subarray.md @@ -0,0 +1,638 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxTurbulenceSize(self, arr: List[int]) -> int: + n = len(arr) + res = 1 + + for i in range(n - 1): + if arr[i] == arr[i + 1]: + continue + + sign = 1 if arr[i] > arr[i + 1] else 0 + j = i + 1 + while j < n - 1: + if arr[j] == arr[j + 1]: + break + curSign = 1 if arr[j] > arr[j + 1] else 0 + if sign == curSign: + break + sign = curSign + j += 1 + + res = max(res, j - i + 1) + + return res +``` + +```java +public class Solution { + public int maxTurbulenceSize(int[] arr) { + int n = arr.length; + int res = 1; + + for (int i = 0; i < n - 1; i++) { + if (arr[i] == arr[i + 1]) continue; + + int sign = arr[i] > arr[i + 1] ? 1 : 0; + int j = i + 1; + + while (j < n - 1) { + if (arr[j] == arr[j + 1]) break; + + int curSign = arr[j] > arr[j + 1] ? 1 : 0; + if (sign == curSign) break; + + sign = curSign; + j++; + } + + res = Math.max(res, j - i + 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxTurbulenceSize(vector& arr) { + int n = arr.size(); + int res = 1; + + for (int i = 0; i < n - 1; i++) { + if (arr[i] == arr[i + 1]) continue; + + int sign = arr[i] > arr[i + 1] ? 1 : 0; + int j = i + 1; + + while (j < n - 1) { + if (arr[j] == arr[j + 1]) break; + + int curSign = arr[j] > arr[j + 1] ? 1 : 0; + if (sign == curSign) break; + + sign = curSign; + j++; + } + + res = max(res, j - i + 1); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + maxTurbulenceSize(arr) { + const n = arr.length; + let res = 1; + + for (let i = 0; i < n - 1; i++) { + if (arr[i] === arr[i + 1]) continue; + + let sign = arr[i] > arr[i + 1] ? 1 : 0; + let j = i + 1; + + while (j < n - 1) { + if (arr[j] === arr[j + 1]) break; + + let curSign = arr[j] > arr[j + 1] ? 1 : 0; + if (sign === curSign) break; + + sign = curSign; + j++; + } + + res = Math.max(res, j - i + 1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def maxTurbulenceSize(self, arr: List[int]) -> int: + n = len(arr) + memo = {} + + def dfs(i, sign): + if i == n - 1: + return 1 + if (i, sign) in memo: + return memo[(i, sign)] + + res = 1 + if ((sign and arr[i] > arr[i + 1]) or + (not sign and arr[i] < arr[i + 1]) + ): + res = 1 + dfs(i + 1, not sign) + + memo[(i, sign)] = res + return res + + max_len = 1 + for i in range(n): + max_len = max(max_len, dfs(i, True), dfs(i, False)) + + return max_len +``` + +```java +public class Solution { + private int[][] memo; + + public int maxTurbulenceSize(int[] arr) { + int n = arr.length; + memo = new int[n][2]; + + for (int i = 0; i < n; i++) { + memo[i][0] = -1; + memo[i][1] = -1; + } + + int maxLen = 1; + for (int i = 0; i < n; i++) { + maxLen = Math.max(maxLen, dfs(i, true, arr)); + maxLen = Math.max(maxLen, dfs(i, false, arr)); + } + + return maxLen; + } + + private int dfs(int i, boolean sign, int[] arr) { + int signIndex = sign ? 1 : 0; + if (i == arr.length - 1) return 1; + if (memo[i][signIndex] != -1) { + return memo[i][signIndex]; + } + + int res = 1; + if ((sign && arr[i] > arr[i + 1]) || + (!sign && arr[i] < arr[i + 1])) { + res = 1 + dfs(i + 1, !sign, arr); + } + + memo[i][signIndex] = res; + return res; + } +} +``` + +```cpp +class Solution { + vector> memo; + +public: + int maxTurbulenceSize(vector& arr) { + int n = arr.size(); + memo.assign(n, vector(2, -1)); + + int maxLen = 1; + for (int i = 0; i < n; i++) { + maxLen = max(maxLen, dfs(i, true, arr)); + maxLen = max(maxLen, dfs(i, false, arr)); + } + + return maxLen; + } + + int dfs(int i, bool sign, vector& arr) { + int signIndex = sign ? 1 : 0; + if (i == arr.size() - 1) return 1; + if (memo[i][signIndex] != -1) { + return memo[i][signIndex]; + } + + int res = 1; + if ((sign && arr[i] > arr[i + 1]) || + (!sign && arr[i] < arr[i + 1])) { + res = 1 + dfs(i + 1, !sign, arr); + } + + memo[i][signIndex] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + maxTurbulenceSize(arr) { + const n = arr.length; + const memo = Array.from({ length: n }, () => [-1, -1]); + + const dfs = (i, sign) => { + const signIndex = sign ? 1 : 0; + if (i === n - 1) return 1; + if (memo[i][signIndex] !== -1) { + return memo[i][signIndex]; + } + + let res = 1; + if ((sign && arr[i] > arr[i + 1]) || + (!sign && arr[i] < arr[i + 1])) { + res = 1 + dfs(i + 1, !sign); + } + + memo[i][signIndex] = res; + return res; + }; + + let maxLen = 1; + for (let i = 0; i < n; i++) { + maxLen = Math.max(maxLen, dfs(i, true), dfs(i, false)); + } + + return maxLen; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def maxTurbulenceSize(self, arr: List[int]) -> int: + n = len(arr) + if n == 1: + return 1 + + dp = [[1] * 2 for _ in range(n)] + + max_len = 1 + for i in range(1, n): + if arr[i] > arr[i - 1]: + dp[i][1] = dp[i - 1][0] + 1 + elif arr[i] < arr[i - 1]: + dp[i][0] = dp[i - 1][1] + 1 + + max_len = max(max_len, dp[i][0], dp[i][1]) + + return max_len +``` + +```java +public class Solution { + public int maxTurbulenceSize(int[] arr) { + int n = arr.length; + if (n == 1) return 1; + + int[][] dp = new int[n][2]; + for (int i = 0; i < n; i++) { + dp[i][0] = dp[i][1] = 1; + } + + int maxLen = 1; + for (int i = 1; i < n; i++) { + if (arr[i] > arr[i - 1]) { + dp[i][1] = dp[i - 1][0] + 1; + } else if (arr[i] < arr[i - 1]) { + dp[i][0] = dp[i - 1][1] + 1; + } + maxLen = Math.max(maxLen, dp[i][0]); + maxLen = Math.max(maxLen, dp[i][1]); + } + + return maxLen; + } +} +``` + +```cpp +class Solution { +public: + int maxTurbulenceSize(vector& arr) { + int n = arr.size(); + if (n == 1) return 1; + + vector> dp(n, vector(2, 1)); + int maxLen = 1; + + for (int i = 1; i < n; ++i) { + if (arr[i] > arr[i - 1]) { + dp[i][1] = dp[i - 1][0] + 1; + } else if (arr[i] < arr[i - 1]) { + dp[i][0] = dp[i - 1][1] + 1; + } + maxLen = max(maxLen, max(dp[i][0], dp[i][1])); + } + + return maxLen; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + maxTurbulenceSize(arr) { + const n = arr.length; + if (n === 1) return 1; + + const dp = Array.from({ length: n }, () => [1, 1]); + + let maxLen = 1; + for (let i = 1; i < n; i++) { + if (arr[i] > arr[i - 1]) { + dp[i][1] = dp[i - 1][0] + 1; + } else if (arr[i] < arr[i - 1]) { + dp[i][0] = dp[i - 1][1] + 1; + } + maxLen = Math.max(maxLen, dp[i][0], dp[i][1]); + } + + return maxLen; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Sliding Window + +::tabs-start + +```python +class Solution: + def maxTurbulenceSize(self, arr: List[int]) -> int: + l, r, res, prev = 0, 1, 1, "" + + while r < len(arr): + if arr[r - 1] > arr[r] and prev != ">": + res = max(res, r - l + 1) + r += 1 + prev = ">" + elif arr[r - 1] < arr[r] and prev != "<": + res = max(res, r - l + 1) + r += 1 + prev = "<" + else: + r = r + 1 if arr[r] == arr[r - 1] else r + l = r - 1 + prev = "" + + return res +``` + +```java +public class Solution { + public int maxTurbulenceSize(int[] arr) { + int l = 0, r = 1, res = 1; + String prev = ""; + + while (r < arr.length) { + if (arr[r - 1] > arr[r] && !">".equals(prev)) { + res = Math.max(res, r - l + 1); + r++; + prev = ">"; + } else if (arr[r - 1] < arr[r] && !"<".equals(prev)) { + res = Math.max(res, r - l + 1); + r++; + prev = "<"; + } else { + r = (arr[r] == arr[r - 1]) ? r + 1 : r; + l = r - 1; + prev = ""; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxTurbulenceSize(vector& arr) { + int l = 0, r = 1, res = 1; + string prev = ""; + + while (r < arr.size()) { + if (arr[r - 1] > arr[r] && prev != ">") { + res = max(res, r - l + 1); + r++; + prev = ">"; + } else if (arr[r - 1] < arr[r] && prev != "<") { + res = max(res, r - l + 1); + r++; + prev = "<"; + } else { + r = (arr[r] == arr[r - 1]) ? r + 1 : r; + l = r - 1; + prev = ""; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + maxTurbulenceSize(arr) { + let l = 0, r = 1, res = 1, prev = ""; + + while (r < arr.length) { + if (arr[r - 1] > arr[r] && prev !== ">") { + res = Math.max(res, r - l + 1); + r++; + prev = ">"; + } else if (arr[r - 1] < arr[r] && prev !== "<") { + res = Math.max(res, r - l + 1); + r++; + prev = "<"; + } else { + r = (arr[r] === arr[r - 1]) ? r + 1 : r; + l = r - 1; + prev = ""; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 5. Iteration + +::tabs-start + +```python +class Solution: + def maxTurbulenceSize(self, arr: List[int]) -> int: + n = len(arr) + res = cnt = 0 + sign = -1 + + for i in range(n - 1): + if arr[i] > arr[i + 1]: + cnt = cnt + 1 if sign == 0 else 1 + sign = 1 + elif arr[i] < arr[i + 1]: + cnt = cnt + 1 if sign == 1 else 1 + sign = 0 + else: + cnt = 0 + sign = -1 + + res = max(res, cnt) + + return res + 1 +``` + +```java +public class Solution { + public int maxTurbulenceSize(int[] arr) { + int n = arr.length; + int res = 0, cnt = 0, sign = -1; + + for (int i = 0; i < n - 1; i++) { + if (arr[i] > arr[i + 1]) { + cnt = (sign == 0) ? cnt + 1 : 1; + sign = 1; + } else if (arr[i] < arr[i + 1]) { + cnt = (sign == 1) ? cnt + 1 : 1; + sign = 0; + } else { + cnt = 0; + sign = -1; + } + + res = Math.max(res, cnt); + } + + return res + 1; + } +} +``` + +```cpp +class Solution { +public: + int maxTurbulenceSize(vector& arr) { + int n = arr.size(); + int res = 0, cnt = 0, sign = -1; + + for (int i = 0; i < n - 1; i++) { + if (arr[i] > arr[i + 1]) { + cnt = (sign == 0) ? cnt + 1 : 1; + sign = 1; + } else if (arr[i] < arr[i + 1]) { + cnt = (sign == 1) ? cnt + 1 : 1; + sign = 0; + } else { + cnt = 0; + sign = -1; + } + + res = max(res, cnt); + } + + return res + 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + maxTurbulenceSize(arr) { + const n = arr.length; + let res = 0, cnt = 0, sign = -1; + + for (let i = 0; i < n - 1; i++) { + if (arr[i] > arr[i + 1]) { + cnt = (sign === 0) ? cnt + 1 : 1; + sign = 1; + } else if (arr[i] < arr[i + 1]) { + cnt = (sign === 1) ? cnt + 1 : 1; + sign = 0; + } else { + cnt = 0; + sign = -1; + } + + res = Math.max(res, cnt); + } + + return res + 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/lowest-common-ancestor-in-binary-search-tree.md b/articles/lowest-common-ancestor-in-binary-search-tree.md new file mode 100644 index 000000000..798316614 --- /dev/null +++ b/articles/lowest-common-ancestor-in-binary-search-tree.md @@ -0,0 +1,513 @@ +## 1. Recursion + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode: + if not root or not p or not q: + return None + if (max(p.val, q.val) < root.val): + return self.lowestCommonAncestor(root.left, p, q) + elif (min(p.val, q.val) > root.val): + return self.lowestCommonAncestor(root.right, p, q) + else: + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + if (root == null || p == null || q == null) { + return null; + } + if (Math.max(p.val, q.val) < root.val) { + return lowestCommonAncestor(root.left, p, q); + } else if (Math.min(p.val, q.val) > root.val) { + return lowestCommonAncestor(root.right, p, q); + } else { + return root; + } + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { + if (!root || !p || !q) { + return nullptr; + } + if (max(p->val, q->val) < root->val) { + return lowestCommonAncestor(root->left, p, q); + } else if (min(p->val, q->val) > root->val) { + return lowestCommonAncestor(root->right, p, q); + } else { + return root; + } + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @param {TreeNode} p + * @param {TreeNode} q + * @return {TreeNode} + */ + lowestCommonAncestor(root, p, q) { + if (!root || !p || !q) { + return null; + } + if (Math.max(p.val, q.val) < root.val) { + return this.lowestCommonAncestor(root.left, p, q); + } else if (Math.min(p.val, q.val) > root.val) { + return this.lowestCommonAncestor(root.right, p, q); + } else { + return root; + } + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public TreeNode LowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + if (root == null || p == null || q == null) { + return null; + } + if (Math.Max(p.val, q.val) < root.val) { + return LowestCommonAncestor(root.left, p, q); + } else if (Math.Min(p.val, q.val) > root.val) { + return LowestCommonAncestor(root.right, p, q); + } else { + return root; + } + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ + +func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode { + if root == nil || p == nil || q == nil { + return nil + } + if max(p.Val, q.Val) < root.Val { + return lowestCommonAncestor(root.Left, p, q) + } else if min(p.Val, q.Val) > root.Val { + return lowestCommonAncestor(root.Right, p, q) + } else { + return root + } +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +/** + * Definition for a binary tree node. + * class TreeNode(var `val`: Int = 0) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ + +class Solution { + fun lowestCommonAncestor(root: TreeNode?, p: TreeNode?, q: TreeNode?): TreeNode? { + if (root == null || p == null || q == null) { + return null + } + return when { + maxOf(p.`val`, q.`val`) < root.`val` -> lowestCommonAncestor(root.left, p, q) + minOf(p.`val`, q.`val`) > root.`val` -> lowestCommonAncestor(root.right, p, q) + else -> root + } + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init(_ val: Int) { + * self.val = val + * self.left = nil + * self.right = nil + * } + * } + */ + +class Solution { + func lowestCommonAncestor(_ root: TreeNode?, _ p: TreeNode?, _ q: TreeNode?) -> TreeNode? { + guard let root = root, let p = p, let q = q else { + return nil + } + + if max(p.val, q.val) < root.val { + return lowestCommonAncestor(root.left, p, q) + } else if min(p.val, q.val) > root.val { + return lowestCommonAncestor(root.right, p, q) + } else { + return root + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(h)$ +* Space complexity: $O(h)$ + +> Where $h$ is the height of the tree. + +--- + +## 2. Iteration + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode: + cur = root + + while cur: + if p.val > cur.val and q.val > cur.val: + cur = cur.right + elif p.val < cur.val and q.val < cur.val: + cur = cur.left + else: + return cur +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + TreeNode cur = root; + + while (cur != null) { + if (p.val > cur.val && q.val > cur.val) { + cur = cur.right; + } else if (p.val < cur.val && q.val < cur.val) { + cur = cur.left; + } else { + return cur; + } + } + return null; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { + TreeNode* cur = root; + + while (cur) { + if (p->val > cur->val && q->val > cur->val) { + cur = cur->right; + } else if (p->val < cur->val && q->val < cur->val) { + cur = cur->left; + } else { + return cur; + } + } + return nullptr; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @param {TreeNode} p + * @param {TreeNode} q + * @return {TreeNode} + */ + lowestCommonAncestor(root, p, q) { + let cur = root; + + while (cur) { + if (p.val > cur.val && q.val > cur.val) { + cur = cur.right; + } else if (p.val < cur.val && q.val < cur.val) { + cur = cur.left; + } else { + return cur; + } + } + return null; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public TreeNode LowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + TreeNode cur = root; + + while (cur != null) { + if (p.val > cur.val && q.val > cur.val) { + cur = cur.right; + } else if (p.val < cur.val && q.val < cur.val) { + cur = cur.left; + } else { + return cur; + } + } + return null; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ + +func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode { + cur := root + + for cur != nil { + if p.Val > cur.Val && q.Val > cur.Val { + cur = cur.Right + } else if p.Val < cur.Val && q.Val < cur.Val { + cur = cur.Left + } else { + return cur + } + } + return nil +} +``` + +```kotlin +/** + * Definition for a binary tree node. + * class TreeNode(var `val`: Int = 0) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ + +class Solution { + fun lowestCommonAncestor(root: TreeNode?, p: TreeNode?, q: TreeNode?): TreeNode? { + var cur = root + + while (cur != null) { + if (p!!.`val` > cur.`val` && q!!.`val` > cur.`val`) { + cur = cur.right + } else if (p!!.`val` < cur.`val` && q!!.`val` < cur.`val`) { + cur = cur.left + } else { + return cur + } + } + return null + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init(_ val: Int) { + * self.val = val + * self.left = nil + * self.right = nil + * } + * } + */ + +class Solution { + func lowestCommonAncestor(_ root: TreeNode?, _ p: TreeNode?, _ q: TreeNode?) -> TreeNode? { + var cur = root + + while let node = cur { + if let p = p, let q = q { + if p.val > node.val && q.val > node.val { + cur = node.right + } else if p.val < node.val && q.val < node.val { + cur = node.left + } else { + return node + } + } + } + return nil + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(h)$ +* Space complexity: $O(1)$ + +> Where $h$ is the height of the tree. \ No newline at end of file diff --git a/articles/lru-cache.md b/articles/lru-cache.md new file mode 100644 index 000000000..2bd939f9c --- /dev/null +++ b/articles/lru-cache.md @@ -0,0 +1,1159 @@ +## 1. Brute Force + +::tabs-start + +```python +class LRUCache: + + def __init__(self, capacity: int): + self.cache = [] + self.capacity = capacity + + def get(self, key: int) -> int: + for i in range(len(self.cache)): + if self.cache[i][0] == key: + tmp = self.cache.pop(i) + self.cache.append(tmp) + return tmp[1] + return -1 + + def put(self, key: int, value: int) -> None: + for i in range(len(self.cache)): + if self.cache[i][0] == key: + tmp = self.cache.pop(i) + tmp[1] = value + self.cache.append(tmp) + return + + if self.capacity == len(self.cache): + self.cache.pop(0) + + self.cache.append([key, value]) +``` + +```java +public class LRUCache { + + private ArrayList cache; + private int capacity; + + public LRUCache(int capacity) { + this.cache = new ArrayList<>(); + this.capacity = capacity; + } + + public int get(int key) { + for (int i = 0; i < cache.size(); i++) { + if (cache.get(i)[0] == key) { + int[] tmp = cache.remove(i); + cache.add(tmp); + return tmp[1]; + } + } + return -1; + } + + public void put(int key, int value) { + for (int i = 0; i < cache.size(); i++) { + if (cache.get(i)[0] == key) { + int[] tmp = cache.remove(i); + tmp[1] = value; + cache.add(tmp); + return; + } + } + + if (capacity == cache.size()) { + cache.remove(0); + } + + cache.add(new int[]{key, value}); + } +} +``` + +```cpp +class LRUCache { +private: + vector> cache; + int capacity; + +public: + LRUCache(int capacity) { + this->capacity = capacity; + } + + int get(int key) { + for (int i = 0; i < cache.size(); i++) { + if (cache[i].first == key) { + pair tmp = cache[i]; + cache.erase(cache.begin() + i); + cache.push_back(tmp); + return tmp.second; + } + } + return -1; + } + + void put(int key, int value) { + for (int i = 0; i < cache.size(); i++) { + if (cache[i].first == key) { + cache.erase(cache.begin() + i); + cache.push_back({key, value}); + return; + } + } + + if (cache.size() == capacity) { + cache.erase(cache.begin()); + } + + cache.push_back({key, value}); + } +}; +``` + +```javascript +class LRUCache { + /** + * @param {number} capacity + */ + constructor(capacity) { + this.cache = []; + this.capacity = capacity; + } + + /** + * @param {number} key + * @return {number} + */ + get(key) { + for (let i = 0; i < this.cache.length; i++) { + if (this.cache[i][0] === key) { + let tmp = this.cache.splice(i, 1)[0]; + this.cache.push(tmp); + return tmp[1]; + } + } + return -1; + } + + /** + * @param {number} key + * @param {number} value + * @return {void} + */ + put(key, value) { + for (let i = 0; i < this.cache.length; i++) { + if (this.cache[i][0] === key) { + this.cache.splice(i, 1); + this.cache.push([key, value]); + return; + } + } + + if (this.cache.length === this.capacity) { + this.cache.shift(); + } + + this.cache.push([key, value]); + } +} +``` + +```csharp +public class LRUCache { + private List> cache; + private int capacity; + + public LRUCache(int capacity) { + this.cache = new List>(); + this.capacity = capacity; + } + + public int Get(int key) { + for (int i = 0; i < cache.Count; i++) { + if (cache[i].Key == key) { + var tmp = cache[i]; + cache.RemoveAt(i); + cache.Add(tmp); + return tmp.Value; + } + } + return -1; + } + + public void Put(int key, int value) { + for (int i = 0; i < cache.Count; i++) { + if (cache[i].Key == key) { + cache.RemoveAt(i); + cache.Add(new KeyValuePair(key, value)); + return; + } + } + + if (cache.Count == capacity) { + cache.RemoveAt(0); + } + + cache.Add(new KeyValuePair(key, value)); + } +} +``` + +```go +type LRUCache struct { + cache [][2]int + capacity int +} + +func Constructor(capacity int) LRUCache { + return LRUCache{ + cache: make([][2]int, 0), + capacity: capacity, + } +} + +func (this *LRUCache) Get(key int) int { + for i := range this.cache { + if this.cache[i][0] == key { + tmp := this.cache[i] + this.cache = append(this.cache[:i], this.cache[i+1:]...) + this.cache = append(this.cache, tmp) + return tmp[1] + } + } + return -1 +} + +func (this *LRUCache) Put(key int, value int) { + for i := range this.cache { + if this.cache[i][0] == key { + tmp := this.cache[i] + this.cache = append(this.cache[:i], this.cache[i+1:]...) + tmp[1] = value + this.cache = append(this.cache, tmp) + return + } + } + + if len(this.cache) == this.capacity { + this.cache = this.cache[1:] + } + + this.cache = append(this.cache, [2]int{key, value}) +} +``` + +```kotlin +class LRUCache(capacity: Int) { + private val capacity = capacity + private val cache = mutableListOf>() + + fun get(key: Int): Int { + for (i in cache.indices) { + if (cache[i].first == key) { + val tmp = cache.removeAt(i) + cache.add(tmp) + return tmp.second + } + } + return -1 + } + + fun put(key: Int, value: Int) { + for (i in cache.indices) { + if (cache[i].first == key) { + cache.removeAt(i) + cache.add(Pair(key, value)) + return + } + } + + if (cache.size == capacity) { + cache.removeAt(0) + } + + cache.add(Pair(key, value)) + } +} +``` + +```swift +class LRUCache { + private var cache: [(Int, Int)] + private let capacity: Int + + init(_ capacity: Int) { + self.cache = [] + self.capacity = capacity + } + + func get(_ key: Int) -> Int { + for i in 0.. int: + if key in self.cache: + self.remove(self.cache[key]) + self.insert(self.cache[key]) + return self.cache[key].val + return -1 + + def put(self, key: int, value: int) -> None: + if key in self.cache: + self.remove(self.cache[key]) + self.cache[key] = Node(key, value) + self.insert(self.cache[key]) + + if len(self.cache) > self.cap: + lru = self.left.next + self.remove(lru) + del self.cache[lru.key] +``` + +```java +public class Node { + int key; + int val; + Node prev; + Node next; + + public Node(int key, int val) { + this.key = key; + this.val = val; + this.prev = null; + this.next = null; + } +} + +public class LRUCache { + + private int cap; + private HashMap cache; + private Node left; + private Node right; + + public LRUCache(int capacity) { + this.cap = capacity; + this.cache = new HashMap<>(); + this.left = new Node(0, 0); + this.right = new Node(0, 0); + this.left.next = this.right; + this.right.prev = this.left; + } + + private void remove(Node node) { + Node prev = node.prev; + Node nxt = node.next; + prev.next = nxt; + nxt.prev = prev; + } + + private void insert(Node node) { + Node prev = this.right.prev; + prev.next = node; + node.prev = prev; + node.next = this.right; + this.right.prev = node; + } + + public int get(int key) { + if (cache.containsKey(key)) { + Node node = cache.get(key); + remove(node); + insert(node); + return node.val; + } + return -1; + } + + public void put(int key, int value) { + if (cache.containsKey(key)) { + remove(cache.get(key)); + } + Node newNode = new Node(key, value); + cache.put(key, newNode); + insert(newNode); + + if (cache.size() > cap) { + Node lru = this.left.next; + remove(lru); + cache.remove(lru.key); + } + } +} +``` + +```cpp +class Node { +public: + int key; + int val; + Node* prev; + Node* next; + + Node(int k, int v) : key(k), val(v), prev(nullptr), next(nullptr) {} +}; + +class LRUCache { +private: + int cap; + unordered_map cache; + Node* left; + Node* right; + + void remove(Node* node) { + Node* prev = node->prev; + Node* nxt = node->next; + prev->next = nxt; + nxt->prev = prev; + } + + void insert(Node* node) { + Node* prev = right->prev; + prev->next = node; + node->prev = prev; + node->next = right; + right->prev = node; + } + +public: + LRUCache(int capacity) { + cap = capacity; + cache.clear(); + left = new Node(0, 0); + right = new Node(0, 0); + left->next = right; + right->prev = left; + } + + int get(int key) { + if (cache.find(key) != cache.end()) { + Node* node = cache[key]; + remove(node); + insert(node); + return node->val; + } + return -1; + } + + void put(int key, int value) { + if (cache.find(key) != cache.end()) { + remove(cache[key]); + } + Node* newNode = new Node(key, value); + cache[key] = newNode; + insert(newNode); + + if (cache.size() > cap) { + Node* lru = left->next; + remove(lru); + cache.erase(lru->key); + delete lru; + } + } +}; +``` + +```javascript +class Node { + /** + * @param {number} key + * @param {number} val + */ + constructor(key, val) { + this.key = key; + this.val = val; + this.prev = null; + this.next = null; + } +} + +class LRUCache { + /** + * @param {number} capacity + */ + constructor(capacity) { + this.cap = capacity; + this.cache = new Map(); + this.left = new Node(0, 0); + this.right = new Node(0, 0); + this.left.next = this.right; + this.right.prev = this.left; + } + + /** + * @param {Node} node + */ + remove(node) { + const prev = node.prev; + const nxt = node.next; + prev.next = nxt; + nxt.prev = prev; + } + + /** + * @param {Node} node + */ + insert(node) { + const prev = this.right.prev; + prev.next = node; + node.prev = prev; + node.next = this.right; + this.right.prev = node; + } + + /** + * @param {number} key + * @return {number} + */ + get(key) { + if (this.cache.has(key)) { + const node = this.cache.get(key); + this.remove(node); + this.insert(node); + return node.val; + } + return -1; + } + + /** + * @param {number} key + * @param {number} value + * @return {void} + */ + put(key, value) { + if (this.cache.has(key)) { + this.remove(this.cache.get(key)); + } + const newNode = new Node(key, value); + this.cache.set(key, newNode); + this.insert(newNode); + + if (this.cache.size > this.cap) { + const lru = this.left.next; + this.remove(lru); + this.cache.delete(lru.key); + } + } +} +``` + +```csharp +public class Node { + public int Key { get; set; } + public int Val { get; set; } + public Node Prev { get; set; } + public Node Next { get; set; } + + public Node(int key, int val) { + Key = key; + Val = val; + Prev = null; + Next = null; + } +} + +public class LRUCache { + + private int cap; + private Dictionary cache; + private Node left; + private Node right; + + public LRUCache(int capacity) { + cap = capacity; + cache = new Dictionary(); + left = new Node(0, 0); + right = new Node(0, 0); + left.Next = right; + right.Prev = left; + } + + private void Remove(Node node) { + Node prev = node.Prev; + Node nxt = node.Next; + prev.Next = nxt; + nxt.Prev = prev; + } + + private void Insert(Node node) { + Node prev = right.Prev; + prev.Next = node; + node.Prev = prev; + node.Next = right; + right.Prev = node; + } + + public int Get(int key) { + if (cache.ContainsKey(key)) { + Node node = cache[key]; + Remove(node); + Insert(node); + return node.Val; + } + return -1; + } + + public void Put(int key, int value) { + if (cache.ContainsKey(key)) { + Remove(cache[key]); + } + Node newNode = new Node(key, value); + cache[key] = newNode; + Insert(newNode); + + if (cache.Count > cap) { + Node lru = left.Next; + Remove(lru); + cache.Remove(lru.Key); + } + } +} +``` + +```go +type Node struct { + key, val int + prev, next *Node +} + +type LRUCache struct { + cap int + cache map[int]*Node + left,right *Node +} + +func Constructor(capacity int) LRUCache { + lru := LRUCache{ + cap: capacity, + cache: make(map[int]*Node), + left: &Node{}, + right: &Node{}, + } + lru.left.next = lru.right + lru.right.prev = lru.left + return lru +} + +func (this *LRUCache) remove(node *Node) { + prev, next := node.prev, node.next + prev.next = next + next.prev = prev +} + +func (this *LRUCache) insert(node *Node) { + prev, next := this.right.prev, this.right + prev.next = node + next.prev = node + node.next = next + node.prev = prev +} + +func (this *LRUCache) Get(key int) int { + if node, ok := this.cache[key]; ok { + this.remove(node) + this.insert(node) + return node.val + } + return -1 +} + +func (this *LRUCache) Put(key int, value int) { + if node, ok := this.cache[key]; ok { + this.remove(node) + delete(this.cache, key) + } + + node := &Node{key: key, val: value} + this.cache[key] = node + this.insert(node) + + if len(this.cache) > this.cap { + lru := this.left.next + this.remove(lru) + delete(this.cache, lru.key) + } +} +``` + +```kotlin +class LRUCache(capacity: Int) { + private val capacity = capacity + private class Node( + val key: Int, + var value: Int, + var prev: Node? = null, + var next: Node? = null + ) + + private val cache = mutableMapOf() + private val left = Node(0, 0) + private val right = Node(0, 0) + + init { + left.next = right + right.prev = left + } + + private fun remove(node: Node) { + val prev = node.prev + val next = node.next + prev?.next = next + next?.prev = prev + } + + private fun insert(node: Node) { + val prev = right.prev + val next = right + prev?.next = node + next.prev = node + node.next = next + node.prev = prev + } + + fun get(key: Int): Int { + return cache[key]?.let { node -> + remove(node) + insert(node) + node.value + } ?: -1 + } + + fun put(key: Int, value: Int) { + cache[key]?.let { node -> + remove(node) + cache.remove(key) + } + + val node = Node(key, value) + cache[key] = node + insert(node) + + if (cache.size > capacity) { + left.next?.let { lru -> + remove(lru) + cache.remove(lru.key) + } + } + } +} +``` + +```swift +class Node { + var key: Int + var val: Int + var prev: Node? + var next: Node? + + init(_ key: Int, _ val: Int) { + self.key = key + self.val = val + } +} + +class LRUCache { + private var cap: Int + private var cache: [Int: Node] = [:] + private var left: Node + private var right: Node + + init(_ capacity: Int) { + self.cap = capacity + self.left = Node(0, 0) + self.right = Node(0, 0) + self.left.next = self.right + self.right.prev = self.left + } + + private func remove(_ node: Node?) { + let prev = node?.prev + let next = node?.next + prev?.next = next + next?.prev = prev + } + + private func insert(_ node: Node?) { + let prev = right.prev + let next = right + prev?.next = node + next.prev = node + node?.prev = prev + node?.next = next + } + + func get(_ key: Int) -> Int { + if let node = cache[key] { + remove(node) + insert(node) + return node.val + } + return -1 + } + + func put(_ key: Int, _ value: Int) { + if let node = cache[key] { + remove(node) + } + let newNode = Node(key, value) + cache[key] = newNode + insert(newNode) + + if cache.count > cap { + if let lru = left.next { + remove(lru) + cache.removeValue(forKey: lru.key) + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for each $put()$ and $get()$ operation. +* Space complexity: $O(n)$ + +--- + +## 3. Built-In Data Structure + +::tabs-start + +```python +class LRUCache: + + def __init__(self, capacity: int): + self.cache = OrderedDict() + self.cap = capacity + + def get(self, key: int) -> int: + if key not in self.cache: + return -1 + self.cache.move_to_end(key) + return self.cache[key] + + def put(self, key: int, value: int) -> None: + if key in self.cache: + self.cache.move_to_end(key) + self.cache[key] = value + + if len(self.cache) > self.cap: + self.cache.popitem(last=False) +``` + +```java +public class LRUCache { + private final Map cache; + private final int capacity; + + public LRUCache(int capacity) { + this.capacity = capacity; + this.cache = new LinkedHashMap<>(capacity, 0.75f, true) { + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > LRUCache.this.capacity; + } + }; + } + + public int get(int key) { + return cache.getOrDefault(key, -1); + } + + public void put(int key, int value) { + cache.put(key, value); + } +} +``` + +```cpp +class LRUCache { +private: + unordered_map::iterator>> cache; + list order; + int capacity; + +public: + LRUCache(int capacity) { + this->capacity = capacity; + } + + int get(int key) { + if (cache.find(key) == cache.end()) return -1; + order.erase(cache[key].second); + order.push_back(key); + cache[key].second = --order.end(); + return cache[key].first; + } + + void put(int key, int value) { + if (cache.find(key) != cache.end()) { + order.erase(cache[key].second); + } else if (cache.size() == capacity) { + int lru = order.front(); + order.pop_front(); + cache.erase(lru); + } + order.push_back(key); + cache[key] = {value, --order.end()}; + } +}; +``` + +```javascript +class LRUCache { + /** + * @param {number} capacity + */ + constructor(capacity) { + this.cache = new Map(); + this.capacity = capacity; + } + + /** + * @param {number} key + * @return {number} + */ + get(key) { + if (!this.cache.has(key)) return -1; + const value = this.cache.get(key); + this.cache.delete(key); + this.cache.set(key, value); + return value; + } + + /** + * @param {number} key + * @param {number} value + * @return {void} + */ + put(key, value) { + if (this.cache.has(key)) { + this.cache.delete(key); + } else if (this.cache.size === this.capacity) { + const firstKey = this.cache.keys().next().value; + this.cache.delete(firstKey); + } + this.cache.set(key, value); + } +} +``` + +```csharp +public class LRUCache { + private Dictionary> cache; + private LinkedList<(int key, int value)> order; + private int capacity; + + public LRUCache(int capacity) { + this.capacity = capacity; + this.cache = new Dictionary>(); + this.order = new LinkedList<(int key, int value)>(); + } + + public int Get(int key) { + if (!cache.ContainsKey(key)) return -1; + var node = cache[key]; + order.Remove(node); + order.AddLast(node); + return node.Value.value; + } + + public void Put(int key, int value) { + if (cache.ContainsKey(key)) { + var node = cache[key]; + order.Remove(node); + node.Value = (key, value); + order.AddLast(node); + } else { + if (cache.Count == capacity) { + var lru = order.First.Value; + order.RemoveFirst(); + cache.Remove(lru.key); + } + var newNode = new LinkedListNode<(int key, int value)>((key, value)); + order.AddLast(newNode); + cache[key] = newNode; + } + } +} +``` + +```go +type LRUCache struct { + capacity int + keys []int + values map[int]int +} + +func Constructor(capacity int) LRUCache { + return LRUCache{ + capacity: capacity, + keys: make([]int, 0, capacity), + values: make(map[int]int), + } +} + +func (this *LRUCache) Get(key int) int { + if val, exists := this.values[key]; exists { + for i := range this.keys { + if this.keys[i] == key { + this.keys = append(this.keys[:i], this.keys[i+1:]...) + break + } + } + this.keys = append(this.keys, key) + return val + } + return -1 +} + +func (this *LRUCache) Put(key int, value int) { + if _, exists := this.values[key]; exists { + for i := range this.keys { + if this.keys[i] == key { + this.keys = append(this.keys[:i], this.keys[i+1:]...) + break + } + } + } else { + if len(this.keys) >= this.capacity { + delete(this.values, this.keys[0]) + this.keys = this.keys[1:] + } + } + this.values[key] = value + this.keys = append(this.keys, key) +} +``` + +```kotlin +class LRUCache(capacity: Int) { + private val capacity = capacity + private val cache = object : LinkedHashMap(capacity, 0.75f, true) { + override fun removeEldestEntry(eldest: MutableMap.MutableEntry): Boolean { + return size > capacity + } + } + + fun get(key: Int): Int { + return cache.getOrDefault(key, -1) + } + + fun put(key: Int, value: Int) { + cache[key] = value + } +} +``` + +```swift +class LRUCache { + private var capacity: Int + private var cache: [Int: Int] = [:] + private var keyOrder: [Int] = [] + + init(_ capacity: Int) { + self.capacity = capacity + } + + func get(_ key: Int) -> Int { + if let value = cache[key] { + if let index = keyOrder.firstIndex(of: key) { + keyOrder.remove(at: index) + } + keyOrder.append(key) + return value + } + return -1 + } + + func put(_ key: Int, _ value: Int) { + if cache[key] != nil { + if let index = keyOrder.firstIndex(of: key) { + keyOrder.remove(at: index) + } + } else if cache.count >= capacity { + if let lruKey = keyOrder.first { + cache.removeValue(forKey: lruKey) + keyOrder.removeFirst() + } + } + + cache[key] = value + keyOrder.append(key) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for each $put()$ and $get()$ operation. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/majority-element-ii.md b/articles/majority-element-ii.md new file mode 100644 index 000000000..aca6e49c3 --- /dev/null +++ b/articles/majority-element-ii.md @@ -0,0 +1,746 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def majorityElement(self, nums: List[int]) -> List[int]: + res = set() + for num in nums: + count = sum(1 for i in nums if i == num) + if count > len(nums) // 3: + res.add(num) + return list(res) +``` + +```java +public class Solution { + public List majorityElement(int[] nums) { + Set res = new HashSet<>(); + for (int num : nums) { + int count = 0; + for (int i : nums) { + if (i == num) count++; + } + if (count > nums.length / 3) { + res.add(num); + } + } + return new ArrayList<>(res); + } +} +``` + +```cpp +class Solution { +public: + vector majorityElement(vector& nums) { + unordered_set res; + for (int num : nums) { + int count = 0; + for (int i : nums) { + if (i == num) count++; + } + if (count > nums.size() / 3) { + res.insert(num); + } + } + return vector(res.begin(), res.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + majorityElement(nums) { + const res = new Set(); + for (const num of nums) { + let count = 0; + for (const i of nums) { + if (i === num) count++; + } + if (count > Math.floor(nums.length / 3)) { + res.add(num); + } + } + return Array.from(res); + } +} +``` + +```csharp +public class Solution { + public List MajorityElement(int[] nums) { + HashSet res = new HashSet(); + int n = nums.Length; + + foreach (int num in nums) { + int count = nums.Count(x => x == num); + if (count > n / 3) { + res.Add(num); + } + } + + return res.ToList(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ since output array size will be at most $2$. + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def majorityElement(self, nums: List[int]) -> List[int]: + nums.sort() + res, n = [], len(nums) + + i = 0 + while i < n: + j = i + 1 + while j < n and nums[i] == nums[j]: + j += 1 + if (j - i) > n // 3: + res.append(nums[i]) + i = j + + return res +``` + +```java +public class Solution { + public List majorityElement(int[] nums) { + Arrays.sort(nums); + List res = new ArrayList<>(); + int n = nums.length; + + int i = 0; + while (i < n) { + int j = i + 1; + while (j < n && nums[i] == nums[j]) { + j++; + } + if (j - i > n / 3) { + res.add(nums[i]); + } + i = j; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector majorityElement(vector& nums) { + sort(nums.begin(), nums.end()); + vector res; + int n = nums.size(); + + int i = 0; + while (i < n) { + int j = i + 1; + while (j < n && nums[i] == nums[j]) { + j++; + } + if (j - i > n / 3) { + res.push_back(nums[i]); + } + i = j; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + majorityElement(nums) { + nums.sort((a, b) => a - b); + const res = []; + const n = nums.length; + + let i = 0; + while (i < n) { + let j = i + 1; + while (j < n && nums[i] === nums[j]) { + j++; + } + if (j - i > Math.floor(n / 3)) { + res.push(nums[i]); + } + i = j; + } + + return res; + } +} +``` + +```csharp +public class Solution { + public List MajorityElement(int[] nums) { + Array.Sort(nums); + List res = new List(); + int n = nums.Length; + + int i = 0; + while (i < n) { + int j = i + 1; + while (j < n && nums[j] == nums[i]) { + j++; + } + if (j - i > n / 3) { + res.Add(nums[i]); + } + i = j; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Frequency Count + +::tabs-start + +```python +class Solution: + def majorityElement(self, nums: List[int]) -> List[int]: + count = Counter(nums) + res = [] + + for key in count: + if count[key] > len(nums) // 3: + res.append(key) + + return res +``` + +```java +public class Solution { + public List majorityElement(int[] nums) { + Map count = new HashMap<>(); + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + List res = new ArrayList<>(); + for (int key : count.keySet()) { + if (count.get(key) > nums.length / 3) { + res.add(key); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector majorityElement(vector& nums) { + unordered_map count; + for (int num : nums) { + count[num]++; + } + + vector res; + for (auto& pair : count) { + if (pair.second > nums.size() / 3) { + res.push_back(pair.first); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + majorityElement(nums) { + const count = new Map(); + for (const num of nums) { + count.set(num, (count.get(num) || 0) + 1); + } + + const res = []; + for (const [key, value] of count.entries()) { + if (value > Math.floor(nums.length / 3)) { + res.push(key); + } + } + + return res; + } +} +``` + +```csharp +public class Solution { + public List MajorityElement(int[] nums) { + Dictionary count = new Dictionary(); + List res = new List(); + int n = nums.Length; + + foreach (int num in nums) { + if (!count.ContainsKey(num)) { + count[num] = 0; + } + count[num]++; + } + + foreach (var kvp in count) { + if (kvp.Value > n / 3) { + res.Add(kvp.Key); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Boyer-Moore Voting Algorithm + +::tabs-start + +```python +class Solution: + def majorityElement(self, nums: List[int]) -> List[int]: + n = len(nums) + num1 = num2 = -1 + cnt1 = cnt2 = 0 + + for num in nums: + if num == num1: + cnt1 += 1 + elif num == num2: + cnt2 += 1 + elif cnt1 == 0: + cnt1 = 1 + num1 = num + elif cnt2 == 0: + cnt2 = 1 + num2 = num + else: + cnt1 -= 1 + cnt2 -= 1 + + cnt1 = cnt2 = 0 + for num in nums: + if num == num1: + cnt1 += 1 + elif num == num2: + cnt2 += 1 + + res = [] + if cnt1 > n // 3: + res.append(num1) + if cnt2 > n // 3: + res.append(num2) + + return res +``` + +```java +public class Solution { + public List majorityElement(int[] nums) { + int n = nums.length; + int num1 = -1, num2 = -1, cnt1 = 0, cnt2 = 0; + + for (int num : nums) { + if (num == num1) { + cnt1++; + } else if (num == num2) { + cnt2++; + } else if (cnt1 == 0) { + cnt1 = 1; + num1 = num; + } else if (cnt2 == 0) { + cnt2 = 1; + num2 = num; + } else { + cnt1--; + cnt2--; + } + } + + cnt1 = cnt2 = 0; + for (int num : nums) { + if (num == num1) { + cnt1++; + } else if (num == num2) { + cnt2++; + } + } + + List res = new ArrayList<>(); + if (cnt1 > n / 3) res.add(num1); + if (cnt2 > n / 3) res.add(num2); + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector majorityElement(vector& nums) { + int n = nums.size(); + int num1 = -1, num2 = -1, cnt1 = 0, cnt2 = 0; + + for (int num : nums) { + if (num == num1) { + cnt1++; + } else if (num == num2) { + cnt2++; + } else if (cnt1 == 0) { + num1 = num; + cnt1 = 1; + } else if (cnt2 == 0) { + num2 = num; + cnt2 = 1; + } else { + cnt1--; + cnt2--; + } + } + + cnt1 = cnt2 = 0; + for (int num : nums) { + if (num == num1) cnt1++; + else if (num == num2) cnt2++; + } + + vector res; + if (cnt1 > n / 3) res.push_back(num1); + if (cnt2 > n / 3) res.push_back(num2); + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + majorityElement(nums) { + const n = nums.length; + let num1 = -1, num2 = -1, cnt1 = 0, cnt2 = 0; + + for (const num of nums) { + if (num === num1) { + cnt1++; + } else if (num === num2) { + cnt2++; + } else if (cnt1 === 0) { + cnt1 = 1; + num1 = num; + } else if (cnt2 === 0) { + cnt2 = 1; + num2 = num; + } else { + cnt1--; + cnt2--; + } + } + + cnt1 = cnt2 = 0; + for (const num of nums) { + if (num === num1) cnt1++; + else if (num === num2) cnt2++; + } + + const res = []; + if (cnt1 > Math.floor(n / 3)) res.push(num1); + if (cnt2 > Math.floor(n / 3)) res.push(num2); + + return res; + } +} +``` + +```csharp +public class Solution { + public List MajorityElement(int[] nums) { + int n = nums.Length; + int num1 = -1, num2 = -1; + int cnt1 = 0, cnt2 = 0; + + foreach (int num in nums) { + if (num == num1) { + cnt1++; + } else if (num == num2) { + cnt2++; + } else if (cnt1 == 0) { + num1 = num; + cnt1 = 1; + } else if (cnt2 == 0) { + num2 = num; + cnt2 = 1; + } else { + cnt1--; + cnt2--; + } + } + + cnt1 = cnt2 = 0; + foreach (int num in nums) { + if (num == num1) cnt1++; + else if (num == num2) cnt2++; + } + + List res = new List(); + if (cnt1 > n / 3) res.Add(num1); + if (cnt2 > n / 3) res.Add(num2); + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since output array size will be at most $2$. + +--- + +## 5. Boyer-Moore Voting Algorithm (Hash Map) + +::tabs-start + +```python +class Solution: + def majorityElement(self, nums: List[int]) -> List[int]: + count = defaultdict(int) + + for num in nums: + count[num] += 1 + + if len(count) <= 2: + continue + + new_count = defaultdict(int) + for num, c in count.items(): + if c > 1: + new_count[num] = c - 1 + count = new_count + + res = [] + for num in count: + if nums.count(num) > len(nums) // 3: + res.append(num) + + return res +``` + +```java +public class Solution { + public List majorityElement(int[] nums) { + Map count = new HashMap<>(); + + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + + if (count.size() > 2) { + Map newCount = new HashMap<>(); + for (Map.Entry entry : count.entrySet()) { + if (entry.getValue() > 1) { + newCount.put(entry.getKey(), entry.getValue() - 1); + } + } + count = newCount; + } + } + + List res = new ArrayList<>(); + for (int key : count.keySet()) { + int frequency = 0; + for (int num : nums) { + if (num == key) frequency++; + } + if (frequency > nums.length / 3) { + res.add(key); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector majorityElement(vector& nums) { + unordered_map count; + + for (int num : nums) { + count[num]++; + + if (count.size() > 2) { + unordered_map newCount; + for (auto& entry : count) { + if (entry.second > 1) { + newCount[entry.first] = entry.second - 1; + } + } + count = newCount; + } + } + + vector res; + for (auto& entry : count) { + int frequency = 0; + for (int num : nums) { + if (num == entry.first) frequency++; + } + if (frequency > nums.size() / 3) { + res.push_back(entry.first); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + majorityElement(nums) { + let count = new Map(); + + for (const num of nums) { + count.set(num, (count.get(num) || 0) + 1); + + if (count.size > 2) { + const newCount = new Map(); + for (const [key, value] of count.entries()) { + if (value > 1) { + newCount.set(key, value - 1); + } + } + count = newCount; + } + } + + const res = []; + for (const [key] of count.entries()) { + const frequency = nums.filter(num => num === key).length; + if (frequency > Math.floor(nums.length / 3)) { + res.push(key); + } + } + + return res; + } +} +``` + +```csharp +public class Solution { + public List MajorityElement(int[] nums) { + Dictionary count = new Dictionary(); + + foreach (int num in nums) { + if (count.ContainsKey(num)) { + count[num]++; + } else { + count[num] = 1; + } + + if (count.Count <= 2) { + continue; + } + + Dictionary newCount = new Dictionary(); + foreach (var kvp in count) { + if (kvp.Value > 1) { + newCount[kvp.Key] = kvp.Value - 1; + } + } + count = newCount; + } + + List res = new List(); + foreach (int candidate in count.Keys) { + int freq = 0; + foreach (int num in nums) { + if (num == candidate) { + freq++; + } + } + if (freq > nums.Length / 3) { + res.Add(candidate); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since output array size will be at most $2$. \ No newline at end of file diff --git a/articles/majority-element.md b/articles/majority-element.md new file mode 100644 index 000000000..225347051 --- /dev/null +++ b/articles/majority-element.md @@ -0,0 +1,606 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def majorityElement(self, nums: List[int]) -> int: + n = len(nums) + for num in nums: + count = sum(1 for i in nums if i == num) + if count > n // 2: + return num +``` + +```java +public class Solution { + public int majorityElement(int[] nums) { + int n = nums.length; + for (int num : nums) { + int count = 0; + for (int i : nums) { + if (i == num) { + count++; + } + } + if (count > n / 2) { + return num; + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int majorityElement(vector& nums) { + int n = nums.size(); + for (int num : nums) { + int count = 0; + for (int i : nums) { + if (i == num) { + count++; + } + } + if (count > n / 2) { + return num; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + majorityElement(nums) { + let n = nums.length; + for (let num of nums) { + let count = nums.reduce((acc, val) => acc + (val === num ? 1 : 0), 0); + if (count > Math.floor(n / 2)) { + return num; + } + } + return -1; + } +} +``` + +```csharp +public class Solution { + public int MajorityElement(int[] nums) { + int n = nums.Length; + foreach (int num in nums) { + int count = 0; + foreach (int i in nums) { + if (i == num) { + count++; + } + } + if (count > n / 2) { + return num; + } + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def majorityElement(self, nums: List[int]) -> int: + count = defaultdict(int) + res = maxCount = 0 + + for num in nums: + count[num] += 1 + if maxCount < count[num]: + res = num + maxCount = count[num] + return res +``` + +```java +public class Solution { + public int majorityElement(int[] nums) { + HashMap count = new HashMap<>(); + int res = 0, maxCount = 0; + + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + if (count.get(num) > maxCount) { + res = num; + maxCount = count.get(num); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int majorityElement(vector& nums) { + unordered_map count; + int res = 0, maxCount = 0; + + for (int num : nums) { + count[num]++; + if (count[num] > maxCount) { + res = num; + maxCount = count[num]; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + majorityElement(nums) { + const count = new Map(); + let res = 0, maxCount = 0; + + for (let num of nums) { + count.set(num, (count.get(num) || 0) + 1); + if (count.get(num) > maxCount) { + res = num; + maxCount = count.get(num); + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int MajorityElement(int[] nums) { + Dictionary count = new Dictionary(); + int res = 0, maxCount = 0; + + foreach (int num in nums) { + if (!count.ContainsKey(num)) { + count[num] = 0; + } + count[num]++; + + if (count[num] > maxCount) { + res = num; + maxCount = count[num]; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sorting + +::tabs-start + +```python +class Solution: + def majorityElement(self, nums: List[int]) -> int: + nums.sort() + return nums[len(nums) // 2] +``` + +```java +public class Solution { + public int majorityElement(int[] nums) { + Arrays.sort(nums); + return nums[nums.length / 2]; + } +} +``` + +```cpp +class Solution { +public: + int majorityElement(vector& nums) { + sort(nums.begin(), nums.end()); + return nums[nums.size() / 2]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + majorityElement(nums) { + nums.sort(); + return nums[Math.floor(nums.length / 2)]; + } +} +``` + +```csharp +public class Solution { + public int MajorityElement(int[] nums) { + Array.Sort(nums); + return nums[nums.Length / 2]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 4. Bit Manipulation + +::tabs-start + +```python +class Solution: + def majorityElement(self, nums: List[int]) -> int: + n = len(nums) + bit = [0] * 32 + for num in nums: + for i in range(32): + bit[i] += ((num >> i) & 1) + + res = 0 + for i in range(32): + if bit[i] > (n // 2): + if i == 31: + res -= (1 << i) + else: + res |= (1 << i) + return res +``` + +```java +public class Solution { + public int majorityElement(int[] nums) { + int n = nums.length; + int[] bit = new int[32]; + for (int num : nums) { + for (int i = 0; i < 32; i++) { + bit[i] += (num >> i) & 1; + } + } + + int res = 0; + for (int i = 0; i < 32; i++) { + if (bit[i] > n / 2) { + res |= (1 << i); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int majorityElement(vector& nums) { + int n = nums.size(); + vector bit(32, 0); + for (int num : nums) { + for (int i = 0; i < 32; i++) { + bit[i] += (num >> i) & 1; + } + } + + int res = 0; + for (int i = 0; i < 32; i++) { + if (bit[i] > n / 2) { + res |= (1 << i); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + majorityElement(nums) { + const n = nums.length; + const bit = Array(32).fill(0); + for (let num of nums) { + for (let i = 0; i < 32; i++) { + bit[i] += (num >> i) & 1; + } + } + + let res = 0; + for (let i = 0; i < 32; i++) { + if (bit[i] > Math.floor(n / 2)) { + res |= (1 << i); + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int MajorityElement(int[] nums) { + int n = nums.Length; + int[] bit = new int[32]; + + foreach (int num in nums) { + for (int i = 0; i < 32; i++) { + bit[i] += (num >> i) & 1; + } + } + + int res = 0; + for (int i = 0; i < 32; i++) { + if (bit[i] > n / 2) { + res |= (1 << i); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 32)$ +* Space complexity: $O(32)$ + +> $32$ represents the number of bits as the given numbers are integers. + +--- + +## 5. Boyer-Moore Voting Algorithm + +::tabs-start + +```python +class Solution: + def majorityElement(self, nums): + res = count = 0 + + for num in nums: + if count == 0: + res = num + count += (1 if num == res else -1) + return res +``` + +```java +public class Solution { + public int majorityElement(int[] nums) { + int res = 0, count = 0; + + for (int num : nums) { + if (count == 0) { + res = num; + } + count += (num == res) ? 1 : -1; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int majorityElement(vector& nums) { + int res = 0, count = 0; + + for (int num : nums) { + if (count == 0) { + res = num; + } + count += (num == res) ? 1 : -1; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + majorityElement(nums) { + let res = 0, count = 0; + + for (let num of nums) { + if (count === 0) { + res = num; + } + count += (num === res) ? 1 : -1; + } + return res; + } +} +``` + +```csharp +public class Solution { + public int MajorityElement(int[] nums) { + int res = 0, count = 0; + + foreach (int num in nums) { + if (count == 0) { + res = num; + } + count += (num == res) ? 1 : -1; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 6. Randomization + +::tabs-start + +```python +class Solution: + def majorityElement(self, nums): + n = len(nums) + while True: + candidate = random.choice(nums) + if nums.count(candidate) > n // 2: + return candidate +``` + +```java +public class Solution { + public int majorityElement(int[] nums) { + Random rand = new Random(); + int n = nums.length; + + while (true) { + int candidate = nums[rand.nextInt(n)]; + int count = 0; + for (int num : nums) { + if (num == candidate) { + count++; + } + } + if (count > n / 2) { + return candidate; + } + } + } +} +``` + +```cpp +class Solution { +public: + int majorityElement(vector& nums) { + int n = nums.size(); + + while (true) { + int candidate = nums[rand() % n]; + int count = 0; + for (int num : nums) { + if (num == candidate) { + count++; + } + } + if (count > n / 2) { + return candidate; + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + majorityElement(nums) { + const n = nums.length; + while (true) { + const candidate = nums[Math.floor(Math.random() * n)]; + let count = 0; + for (const num of nums) { + if (num === candidate) { + count++; + } + } + if (count > Math.floor(n / 2)) { + return candidate; + } + } + } +} +``` + +```csharp +public class Solution { + private static Random random = new Random(); + + public int MajorityElement(int[] nums) { + int n = nums.Length; + + while (true) { + int candidate = nums[random.Next(n)]; + int count = nums.Count(x => x == candidate); + + if (count > n / 2) { + return candidate; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +> The probability of randomly choosing the majority element is greater than $50\%$, so the expected number of iterations in the outer while loop is constant. \ No newline at end of file diff --git a/articles/make-sum-divisible-by-p.md b/articles/make-sum-divisible-by-p.md new file mode 100644 index 000000000..493f5732c --- /dev/null +++ b/articles/make-sum-divisible-by-p.md @@ -0,0 +1,235 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def minSubarray(self, nums: List[int], p: int) -> int: + n = len(nums) + totSum = sum(nums) + + if totSum % p == 0: + return 0 + + for l in range(1, n): + curSum = 0 + for i in range(n): + curSum += nums[i] + if i >= l: + curSum -= nums[i - l] + + remainSum = totSum - curSum + if remainSum % p == 0: + return l + + return -1 +``` + +```java +public class Solution { + public int minSubarray(int[] nums, int p) { + int n = nums.length; + long totSum = 0; + for (int num : nums) totSum += num; + + if (totSum % p == 0) return 0; + + for (int l = 1; l < n; l++) { + long curSum = 0; + for (int i = 0; i < n; i++) { + curSum += nums[i]; + if (i >= l) curSum -= nums[i - l]; + + long remainSum = totSum - curSum; + if (remainSum % p == 0) return l; + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int minSubarray(vector& nums, int p) { + int n = nums.size(); + long long totSum = 0; + for (int num : nums) totSum += num; + + if (totSum % p == 0) return 0; + + for (int l = 1; l < n; l++) { + long long curSum = 0; + for (int i = 0; i < n; i++) { + curSum += nums[i]; + if (i >= l) curSum -= nums[i - l]; + + long long remainSum = totSum - curSum; + if (remainSum % p == 0) return l; + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} p + * @return {number} + */ + minSubarray(nums, p) { + const n = nums.length; + let totSum = nums.reduce((a, b) => a + b, 0); + + if (totSum % p === 0) return 0; + + for (let l = 1; l < n; l++) { + let curSum = 0; + for (let i = 0; i < n; i++) { + curSum += nums[i]; + if (i >= l) curSum -= nums[i - l]; + + const remainSum = totSum - curSum; + if (remainSum % p === 0) return l; + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Prefix Sum + +::tabs-start + +```python +class Solution: + def minSubarray(self, nums: List[int], p: int) -> int: + total = sum(nums) + remain = total % p + if remain == 0: + return 0 + + res = len(nums) + cur_sum = 0 + remain_to_idx = {0: -1} + + for i, n in enumerate(nums): + cur_sum = (cur_sum + n) % p + prefix = (cur_sum - remain + p) % p + if prefix in remain_to_idx: + length = i - remain_to_idx[prefix] + res = min(res, length) + remain_to_idx[cur_sum] = i + + return -1 if res == len(nums) else res +``` + +```java +public class Solution { + public int minSubarray(int[] nums, int p) { + long total = 0; + for (int num : nums) total += num; + int remain = (int)(total % p); + if (remain == 0) return 0; + + int res = nums.length; + long curSum = 0; + Map map = new HashMap<>(); + map.put(0, -1); + + for (int i = 0; i < nums.length; i++) { + curSum = (curSum + nums[i]) % p; + int prefix = (int)((curSum - remain + p) % p); + if (map.containsKey(prefix)) { + res = Math.min(res, i - map.get(prefix)); + } + map.put((int)curSum, i); + } + + return res == nums.length ? -1 : res; + } +} +``` + +```cpp +class Solution { +public: + int minSubarray(vector& nums, int p) { + long total = 0; + for (int num : nums) total += num; + int remain = total % p; + if (remain == 0) return 0; + + int res = nums.size(); + long curSum = 0; + unordered_map map; + map[0] = -1; + + for (int i = 0; i < nums.size(); i++) { + curSum = (curSum + nums[i]) % p; + int prefix = (curSum - remain + p) % p; + if (map.count(prefix)) { + res = min(res, i - map[prefix]); + } + map[curSum] = i; + } + + return res == nums.size() ? -1 : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} p + * @return {number} + */ + minSubarray(nums, p) { + let total = nums.reduce((a, b) => a + b, 0); + let remain = total % p; + if (remain === 0) return 0; + + let res = nums.length; + let curSum = 0; + const map = new Map(); + map.set(0, -1); + + for (let i = 0; i < nums.length; i++) { + curSum = (curSum + nums[i]) % p; + let prefix = (curSum - remain + p) % p; + if (map.has(prefix)) { + res = Math.min(res, i - map.get(prefix)); + } + map.set(curSum, i); + } + + return res === nums.length ? -1 : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/make-the-string-great.md b/articles/make-the-string-great.md new file mode 100644 index 000000000..f8ee712f7 --- /dev/null +++ b/articles/make-the-string-great.md @@ -0,0 +1,334 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def makeGood(self, s: str) -> str: + n = len(s) + i = 0 + while i < n: + if i and s[i] != s[i - 1] and s[i].lower() == s[i - 1].lower(): + s = s[:i - 1] + s[i + 1:] + n -= 2 + i -= 2 + i += 1 + return s +``` + +```java +public class Solution { + public String makeGood(String s) { + int n = s.length(); + int i = 0; + while (i < n) { + if (i > 0 && s.charAt(i) != s.charAt(i - 1) && + Character.toLowerCase(s.charAt(i)) == Character.toLowerCase(s.charAt(i - 1))) { + s = s.substring(0, i - 1) + s.substring(i + 1); + n -= 2; + i -= 2; + } + i++; + } + return s; + } +} +``` + +```cpp +class Solution { +public: + string makeGood(string s) { + int n = s.length(); + int i = 0; + while (i < n) { + if (i > 0 && s[i] != s[i - 1] && tolower(s[i]) == tolower(s[i - 1])) { + s = s.substr(0, i - 1) + s.substr(i + 1); + n -= 2; + i -= 2; + } + i++; + } + return s; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + makeGood(s) { + let n = s.length; + let i = 0; + while (i < n) { + if (i > 0 && s[i] !== s[i - 1] && s[i].toLowerCase() === s[i - 1].toLowerCase()) { + s = s.slice(0, i - 1) + s.slice(i + 1); + n -= 2; + i -= 2; + } + i++; + } + return s; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Stack - I + +::tabs-start + +```python +class Solution: + def makeGood(self, s: str) -> str: + def lower(c): + if ord(c) < ord('a'): + return chr(ord('a') + ord(c) - ord('A')) + return c + + stack = [] + i = 0 + while i < len(s): + if stack and stack[-1] != s[i] and lower(stack[-1]) == lower(s[i]): + stack.pop() + else: + stack.append(s[i]) + i += 1 + return "".join(stack) +``` + +```java +public class Solution { + public String makeGood(String s) { + StringBuilder stack = new StringBuilder(); + for (char c : s.toCharArray()) { + if (stack.length() > 0 && stack.charAt(stack.length() - 1) != c && + Character.toLowerCase(stack.charAt(stack.length() - 1)) == Character.toLowerCase(c)) { + stack.deleteCharAt(stack.length() - 1); + } else { + stack.append(c); + } + } + return stack.toString(); + } +} +``` + +```cpp +class Solution { +public: + string makeGood(string s) { + string stack; + for (char c : s) { + if (!stack.empty() && stack.back() != c && + tolower(stack.back()) == tolower(c)) { + stack.pop_back(); + } else { + stack.push_back(c); + } + } + return stack; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + makeGood(s) { + const stack = []; + for (const c of s) { + if (stack.length > 0 && stack[stack.length - 1] !== c && + stack[stack.length - 1].toLowerCase() === c.toLowerCase()) { + stack.pop(); + } else { + stack.push(c); + } + } + return stack.join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Stack - II + +::tabs-start + +```python +class Solution: + def makeGood(self, s: str) -> str: + stack = [] + for i in range(len(s)): + if stack and abs(ord(s[i]) - ord(stack[-1])) == 32: + stack.pop() + else: + stack.append(s[i]) + return "".join(stack) +``` + +```java +public class Solution { + public String makeGood(String s) { + StringBuilder stack = new StringBuilder(); + for (int i = 0; i < s.length(); i++) { + if (stack.length() > 0 && + Math.abs(stack.charAt(stack.length() - 1) - s.charAt(i)) == 32) { + stack.deleteCharAt(stack.length() - 1); + } else { + stack.append(s.charAt(i)); + } + } + return stack.toString(); + } +} +``` + +```cpp +class Solution { +public: + string makeGood(string s) { + string stack; + for (char& c : s) { + if (!stack.empty() && abs(stack.back() - c) == 32) { + stack.pop_back(); + } else { + stack.push_back(c); + } + } + return stack; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + makeGood(s) { + const stack = []; + for (const c of s) { + if (stack.length > 0 && + Math.abs(stack[stack.length - 1].charCodeAt(0) - c.charCodeAt(0)) === 32) { + stack.pop(); + } else { + stack.push(c); + } + } + return stack.join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Two Pointers + +::tabs-start + +```python +class Solution: + def makeGood(self, s: str) -> str: + l = 0 + s = list(s) + for r in range(len(s)): + if l > 0 and abs(ord(s[r]) - ord(s[l - 1])) == 32: + l -= 1 + else: + s[l] = s[r] + l += 1 + return ''.join(s[:l]) +``` + +```java +public class Solution { + public String makeGood(String s) { + int l = 0; + char[] arr = s.toCharArray(); + for (int r = 0; r < arr.length; r++) { + if (l > 0 && Math.abs(arr[r] - arr[l - 1]) == 32) { + l--; + } else { + arr[l++] = arr[r]; + } + } + return new String(arr, 0, l); + } +} +``` + +```cpp +class Solution { +public: + string makeGood(string s) { + int l = 0; + for (int r = 0; r < s.length(); r++) { + if (l > 0 && abs(s[r] - s[l - 1]) == 32) { + l--; + } else { + s[l++] = s[r]; + } + } + return s.substr(0, l); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + makeGood(s) { + let l = 0; + let arr = s.split(''); + for (let r = 0; r < arr.length; r++) { + if (l > 0 && Math.abs(arr[r].charCodeAt(0) - arr[l - 1].charCodeAt(0)) === 32) { + l--; + } else { + arr[l++] = arr[r]; + } + } + return arr.slice(0, l).join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the language. \ No newline at end of file diff --git a/articles/matchsticks-to-square.md b/articles/matchsticks-to-square.md new file mode 100644 index 000000000..ae760c5ac --- /dev/null +++ b/articles/matchsticks-to-square.md @@ -0,0 +1,586 @@ +## 1. Backtracking (Brute Force) + +::tabs-start + +```python +class Solution: + def makesquare(self, matchsticks: List[int]) -> bool: + if sum(matchsticks) % 4 != 0: + return False + + sides = [0] * 4 + + def dfs(i): + if i == len(matchsticks): + return sides[0] == sides[1] == sides[2] == sides[3] + + for side in range(4): + sides[side] += matchsticks[i] + if dfs(i + 1): + return True + sides[side] -= matchsticks[i] + + return False + + return dfs(0) +``` + +```java +public class Solution { + public boolean makesquare(int[] matchsticks) { + int sum = Arrays.stream(matchsticks).sum(); + if (sum % 4 != 0) return false; + + int[] sides = new int[4]; + return dfs(matchsticks, sides, 0); + } + + private boolean dfs(int[] matchsticks, int[] sides, int i) { + if (i == matchsticks.length) { + return sides[0] == sides[1] && sides[1] == sides[2] && sides[2] == sides[3]; + } + + for (int j = 0; j < 4; j++) { + sides[j] += matchsticks[i]; + if (dfs(matchsticks, sides, i + 1)) return true; + sides[j] -= matchsticks[i]; + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool makesquare(vector& matchsticks) { + int sum = accumulate(matchsticks.begin(), matchsticks.end(), 0); + if (sum % 4 != 0) return false; + + vector sides(4, 0); + return dfs(matchsticks, sides, 0); + } + +private: + bool dfs(vector& matchsticks, vector& sides, int i) { + if (i == matchsticks.size()) { + return sides[0] == sides[1] && sides[1] == sides[2] && sides[2] == sides[3]; + } + + for (int j = 0; j < 4; j++) { + sides[j] += matchsticks[i]; + if (dfs(matchsticks, sides, i + 1)) return true; + sides[j] -= matchsticks[i]; + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} matchsticks + * @return {boolean} + */ + makesquare(matchsticks) { + const sum = matchsticks.reduce((a, b) => a + b, 0); + if (sum % 4 !== 0) return false; + + const sides = Array(4).fill(0); + const dfs = (i) => { + if (i === matchsticks.length) { + return sides[0] === sides[1] && sides[1] === sides[2] && sides[2] === sides[3]; + } + + for (let j = 0; j < 4; j++) { + sides[j] += matchsticks[i]; + if (dfs(i + 1)) return true; + sides[j] -= matchsticks[i]; + } + + return false; + }; + + return dfs(0); + } +} +``` + +```csharp +public class Solution { + public bool Makesquare(int[] matchsticks) { + int total = 0; + foreach (int stick in matchsticks) { + total += stick; + } + if (total % 4 != 0) return false; + + int target = total / 4; + int[] sides = new int[4]; + + bool Dfs(int i) { + if (i == matchsticks.Length) { + return sides[0] == sides[1] && sides[1] == sides[2] && sides[2] == sides[3]; + } + + for (int side = 0; side < 4; side++) { + sides[side] += matchsticks[i]; + if (sides[side] <= target && Dfs(i + 1)) { + return true; + } + sides[side] -= matchsticks[i]; + } + + return false; + } + + return Dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(4 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Backtracking (Pruning) + +::tabs-start + +```python +class Solution: + def makesquare(self, matchsticks: List[int]) -> bool: + total_length = sum(matchsticks) + if total_length % 4 != 0: + return False + + length = total_length // 4 + sides = [0] * 4 + matchsticks.sort(reverse=True) + + def dfs(i): + if i == len(matchsticks): + return True + + for side in range(4): + if sides[side] + matchsticks[i] <= length: + sides[side] += matchsticks[i] + if dfs(i + 1): + return True + sides[side] -= matchsticks[i] + + if sides[side] == 0: + break + + return False + + return dfs(0) +``` + +```java +public class Solution { + public boolean makesquare(int[] matchsticks) { + int totalLength = Arrays.stream(matchsticks).sum(); + if (totalLength % 4 != 0) return false; + + int length = totalLength / 4; + int[] sides = new int[4]; + Arrays.sort(matchsticks); + reverse(matchsticks); + + return dfs(matchsticks, sides, 0, length); + } + + private boolean dfs(int[] matchsticks, int[] sides, int index, int length) { + if (index == matchsticks.length) { + return true; + } + + for (int i = 0; i < 4; i++) { + if (sides[i] + matchsticks[index] <= length) { + sides[i] += matchsticks[index]; + if (dfs(matchsticks, sides, index + 1, length)) return true; + sides[i] -= matchsticks[index]; + } + + if (sides[i] == 0) break; + } + + return false; + } + + private void reverse(int[] matchsticks) { + for (int i = 0, j = matchsticks.length - 1; i < j; i++, j--) { + int temp = matchsticks[i]; + matchsticks[i] = matchsticks[j]; + matchsticks[j] = temp; + } + } +} +``` + +```cpp +class Solution { +public: + bool makesquare(vector& matchsticks) { + int totalLength = accumulate(matchsticks.begin(), matchsticks.end(), 0); + if (totalLength % 4 != 0) return false; + + int length = totalLength / 4; + vector sides(4, 0); + sort(matchsticks.rbegin(), matchsticks.rend()); + + return dfs(matchsticks, sides, 0, length); + } + +private: + bool dfs(vector& matchsticks, vector& sides, int index, int length) { + if (index == matchsticks.size()) { + return true; + } + + for (int i = 0; i < 4; i++) { + if (sides[i] + matchsticks[index] <= length) { + sides[i] += matchsticks[index]; + if (dfs(matchsticks, sides, index + 1, length)) return true; + sides[i] -= matchsticks[index]; + } + + if (sides[i] == 0) break; + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} matchsticks + * @return {boolean} + */ + makesquare(matchsticks) { + const totalLength = matchsticks.reduce((a, b) => a + b, 0); + if (totalLength % 4 !== 0) return false; + + const length = totalLength / 4; + const sides = Array(4).fill(0); + matchsticks.sort((a, b) => b - a); + + const dfs = (index) => { + if (index === matchsticks.length) { + return true; + } + + for (let i = 0; i < 4; i++) { + if (sides[i] + matchsticks[index] <= length) { + sides[i] += matchsticks[index]; + if (dfs(index + 1)) return true; + sides[i] -= matchsticks[index]; + } + + if (sides[i] === 0) break; + } + + return false; + }; + + return dfs(0); + } +} +``` + +```csharp +public class Solution { + public bool Makesquare(int[] matchsticks) { + int totalLength = 0; + foreach (int stick in matchsticks) { + totalLength += stick; + } + + if (totalLength % 4 != 0) { + return false; + } + + int length = totalLength / 4; + int[] sides = new int[4]; + Array.Sort(matchsticks, (a, b) => b.CompareTo(a)); // Sort in descending order + + bool Dfs(int i) { + if (i == matchsticks.Length) { + return true; + } + + for (int side = 0; side < 4; side++) { + if (sides[side] + matchsticks[i] <= length) { + sides[side] += matchsticks[i]; + if (Dfs(i + 1)) return true; + sides[side] -= matchsticks[i]; + } + + if (sides[side] == 0) break; + } + + return false; + } + + return Dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(4 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Dynamic Programming (Bit Mask) + +::tabs-start + +```python +class Solution: + def makesquare(self, matchsticks: List[int]) -> bool: + total_length = sum(matchsticks) + if total_length % 4 != 0: + return False + + length = total_length // 4 + if max(matchsticks) > length: + return False + + n = len(matchsticks) + dp = [float("-inf")] * (1 << n) + matchsticks.sort(reverse=True) + + def dfs(mask): + if mask == 0: + return 0 + if dp[mask] != float("-inf"): + return dp[mask] + + for i in range(n): + if mask & (1 << i): + res = dfs(mask ^ (1 << i)) + if res >= 0 and res + matchsticks[i] <= length: + dp[mask] = (res + matchsticks[i]) % length + return dp[mask] + if mask == (1 << n) - 1: + dp[mask] = -1 + return -1 + + dp[mask] = -1 + return -1 + + return not dfs((1 << n) - 1) +``` + +```java +public class Solution { + private int[] dp; + private int length; + private int n; + + public boolean makesquare(int[] matchsticks) { + int totalLength = Arrays.stream(matchsticks).sum(); + if (totalLength % 4 != 0) return false; + + length = totalLength / 4; + if (Arrays.stream(matchsticks).max().getAsInt() > length) { + return false; + } + + Arrays.sort(matchsticks); + reverse(matchsticks); + this.n = matchsticks.length; + this.dp = new int[1 << n]; + Arrays.fill(dp, Integer.MIN_VALUE); + + return dfs((1 << n) - 1, matchsticks) == 0; + } + + private int dfs(int mask, int[] matchsticks) { + if (mask == 0) return 0; + if (dp[mask] != Integer.MIN_VALUE) return dp[mask]; + + for (int i = 0; i < n; i++) { + if ((mask & (1 << i)) != 0) { + int res = dfs(mask ^ (1 << i), matchsticks); + if (res >= 0 && res + matchsticks[i] <= length) { + dp[mask] = (res + matchsticks[i]) % length; + return dp[mask]; + } + + if (mask == (1 << n) - 1) { + dp[mask] = -1; + return -1; + } + } + } + + dp[mask] = -1; + return dp[mask]; + } + + private void reverse(int[] matchsticks) { + for (int i = 0, j = matchsticks.length - 1; i < j; i++, j--) { + int temp = matchsticks[i]; + matchsticks[i] = matchsticks[j]; + matchsticks[j] = temp; + } + } +} +``` + +```cpp +class Solution { + vector dp; + int length, n; + +public: + bool makesquare(vector& matchsticks) { + int totalLength = accumulate(matchsticks.begin(), matchsticks.end(), 0); + if (totalLength % 4 != 0) return false; + + length = totalLength / 4; + if (*max_element(matchsticks.begin(), matchsticks.end()) > length) { + return false; + } + + sort(matchsticks.rbegin(), matchsticks.rend()); + n = matchsticks.size(); + dp.resize(1 << n, INT_MIN); + + return dfs((1 << n) - 1, matchsticks) == 0; + } + +private: + int dfs(int mask, vector& matchsticks) { + if (mask == 0) return 0; + if (dp[mask] != INT_MIN) return dp[mask]; + + for (int i = 0; i < n; i++) { + if (mask & (1 << i)) { + int res = dfs(mask ^ (1 << i), matchsticks); + if (res >= 0 && res + matchsticks[i] <= length) { + dp[mask] = (res + matchsticks[i]) % length; + return dp[mask]; + } + + if (mask == (1 << n) - 1) { + dp[mask] = -1; + return -1; + } + } + } + + dp[mask] = -1; + return dp[mask]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} matchsticks + * @return {boolean} + */ + makesquare(matchsticks) { + const totalLength = matchsticks.reduce((a, b) => a + b, 0); + if (totalLength % 4 !== 0) return false; + + const length = totalLength / 4; + if (Math.max(...matchsticks) > length) return false; + + matchsticks.sort((a, b) => b - a); + const n = matchsticks.length; + const dp = new Array(1 << n).fill(-Infinity); + + const dfs = (mask) => { + if (mask === 0) return 0; + if (dp[mask] !== -Infinity) return dp[mask]; + + for (let i = 0; i < n; i++) { + if (mask & (1 << i)) { + const res = dfs(mask ^ (1 << i)); + if (res >= 0 && res + matchsticks[i] <= length) { + dp[mask] = (res + matchsticks[i]) % length; + return dp[mask]; + } + + if (mask === (1 << n) - 1) { + dp[mask] = -1; + return -1; + } + } + } + + dp[mask] = -1; + return dp[mask]; + }; + + return dfs((1 << n) - 1) === 0; + } +} +``` + +```csharp +public class Solution { + public bool Makesquare(int[] matchsticks) { + int totalLength = matchsticks.Sum(); + if (totalLength % 4 != 0) return false; + + int length = totalLength / 4; + if (matchsticks.Max() > length) return false; + + int n = matchsticks.Length; + int[] dp = Enumerable.Repeat(int.MinValue, 1 << n).ToArray(); + Array.Sort(matchsticks, (a, b) => b.CompareTo(a)); // Sort descending + + int Dfs(int mask) { + if (mask == 0) return 0; + if (dp[mask] != int.MinValue) return dp[mask]; + + for (int i = 0; i < n; i++) { + if ((mask & (1 << i)) != 0) { + int res = Dfs(mask ^ (1 << i)); + if (res >= 0 && res + matchsticks[i] <= length) { + dp[mask] = (res + matchsticks[i]) % length; + return dp[mask]; + } + if (mask == (1 << n) - 1) { + dp[mask] = -1; + return -1; + } + } + } + + dp[mask] = -1; + return -1; + } + + return Dfs((1 << n) - 1) != -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: $O(n + 2 ^ n)$ \ No newline at end of file diff --git a/articles/matrix-diagonal-sum.md b/articles/matrix-diagonal-sum.md new file mode 100644 index 000000000..8524ff1a7 --- /dev/null +++ b/articles/matrix-diagonal-sum.md @@ -0,0 +1,184 @@ +## 1. Iteration + +::tabs-start + +```python +class Solution: + def diagonalSum(self, mat: List[List[int]]) -> int: + n = len(mat) + + def helper(matrix): + res = 0 + for i in range(n): + for j in range(n): + if i == j: + res += matrix[i][j] + matrix[i].reverse() + return res + + return helper(mat) + helper(mat) - (mat[n // 2][n // 2] if n & 1 else 0) +``` + +```java +public class Solution { + public int diagonalSum(int[][] mat) { + int n = mat.length; + return helper(mat) + helper(mat) - (n % 2 == 1 ? mat[n / 2][n / 2] : 0); + } + + int helper(int[][] matrix) { + int res = 0, n = matrix.length; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (i == j) { + res += matrix[i][j]; + } + } + reverse(matrix[i]); + } + return res; + } + + void reverse(int[] row) { + int left = 0, right = row.length - 1; + while (left < right) { + int temp = row[left]; + row[left++] = row[right]; + row[right--] = temp; + } + } +} +``` + +```cpp +class Solution { +public: + int diagonalSum(vector>& mat) { + int n = mat.size(); + return helper(mat) + helper(mat) - (n % 2 == 1 ? mat[n / 2][n / 2] : 0); + } + +private: + int helper(vector>& matrix) { + int res = 0, n = matrix.size(); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (i == j) { + res += matrix[i][j]; + } + } + reverse(matrix[i].begin(), matrix[i].end()); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} mat + * @return {number} + */ + diagonalSum(mat) { + const n = mat.length; + + const helper = (matrix) => { + let res = 0; + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + if (i === j) { + res += matrix[i][j]; + } + } + matrix[i].reverse(); + } + return res; + }; + + return helper(mat) + helper(mat) - (n % 2 === 1 ? mat[Math.floor(n / 2)][Math.floor(n / 2)] : 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Iteration (Optimal) + +::tabs-start + +```python +class Solution: + def diagonalSum(self, mat: List[List[int]]) -> int: + res, n = 0, len(mat) + + for r in range(n): + res += mat[r][r] + res += mat[r][n - r - 1] + + return res - (mat[n // 2][n // 2] if n & 1 else 0) +``` + +```java +public class Solution { + public int diagonalSum(int[][] mat) { + int res = 0, n = mat.length; + + for (int r = 0; r < n; r++) { + res += mat[r][r]; + res += mat[r][n - r - 1]; + } + + return res - (n % 2 == 1 ? mat[n / 2][n / 2] : 0); + } +} +``` + +```cpp +class Solution { +public: + int diagonalSum(vector>& mat) { + int res = 0, n = mat.size(); + + for (int r = 0; r < n; r++) { + res += mat[r][r]; + res += mat[r][n - r - 1]; + } + + return res - (n % 2 == 1 ? mat[n / 2][n / 2] : 0); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} mat + * @return {number} + */ + diagonalSum(mat) { + let res = 0, n = mat.length; + + for (let r = 0; r < n; r++) { + res += mat[r][r]; + res += mat[r][n - r - 1]; + } + + return res - (n % 2 == 1 ? mat[Math.floor(n / 2)][Math.floor(n / 2)] : 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/max-area-of-island.md b/articles/max-area-of-island.md new file mode 100644 index 000000000..cb69ca56d --- /dev/null +++ b/articles/max-area-of-island.md @@ -0,0 +1,1237 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def maxAreaOfIsland(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + visit = set() + + def dfs(r, c): + if (r < 0 or r == ROWS or c < 0 or + c == COLS or grid[r][c] == 0 or + (r, c) in visit + ): + return 0 + visit.add((r, c)) + return (1 + dfs(r + 1, c) + + dfs(r - 1, c) + + dfs(r, c + 1) + + dfs(r, c - 1)) + + area = 0 + for r in range(ROWS): + for c in range(COLS): + area = max(area, dfs(r, c)) + return area +``` + +```java +public class Solution { + private static final int[][] directions = {{1, 0}, {-1, 0}, + {0, 1}, {0, -1}}; + + public int maxAreaOfIsland(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int area = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 1) { + area = Math.max(area, dfs(grid, r, c)); + } + } + } + + return area; + } + + private int dfs(int[][] grid, int r, int c) { + if (r < 0 || c < 0 || r >= grid.length || + c >= grid[0].length || grid[r][c] == 0) { + return 0; + } + + grid[r][c] = 0; + int res = 1; + for (int[] dir : directions) { + res += dfs(grid, r + dir[0], c + dir[1]); + } + return res; + } +} +``` + +```cpp +class Solution { + int directions[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; +public: + int maxAreaOfIsland(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + int area = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 1) { + area = max(area, dfs(grid, r, c)); + } + } + } + + return area; + } + + int dfs(vector>& grid, int r, int c) { + if (r < 0 || c < 0 || r >= grid.size() || + c >= grid[0].size() || grid[r][c] == 0) { + return 0; + } + + grid[r][c] = 0; + int res = 1; + for (int i = 0; i < 4; i++) { + res += dfs(grid, r + directions[i][0], + c + directions[i][1]); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + maxAreaOfIsland(grid) { + const directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]; + const ROWS = grid.length, COLS = grid[0].length; + + const dfs = (r, c) => { + if (r < 0 || c < 0 || r >= ROWS || + c >= COLS || grid[r][c] === 0) return 0; + + grid[r][c] = 0; + let res = 1; + for (const [dr, dc] of directions) { + res += dfs(r + dr, c + dc); + } + return res; + }; + + let area = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] === 1) { + area = Math.max(area, dfs(r, c)); + } + } + } + + return area; + } +} +``` + +```csharp +public class Solution { + private static readonly int[][] directions = new int[][] { + new int[] {1, 0}, new int[] {-1, 0}, + new int[] {0, 1}, new int[] {0, -1} + }; + + public int MaxAreaOfIsland(int[][] grid) { + int ROWS = grid.Length, COLS = grid[0].Length; + int area = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 1) { + area = Math.Max(area, Dfs(grid, r, c)); + } + } + } + + return area; + } + + private int Dfs(int[][] grid, int r, int c) { + if (r < 0 || c < 0 || r >= grid.Length || + c >= grid[0].Length || grid[r][c] == 0) { + return 0; + } + + grid[r][c] = 0; + int res = 1; + foreach (var dir in directions) { + res += Dfs(grid, r + dir[0], c + dir[1]); + } + return res; + } +} +``` + +```go +func maxAreaOfIsland(grid [][]int) int { + rows, cols := len(grid), len(grid[0]) + visit := make(map[[2]int]bool) + + var dfs func(r, c int) int + dfs = func(r, c int) int { + if r < 0 || r >= rows || c < 0 || c >= cols || + grid[r][c] == 0 || visit[[2]int{r, c}] { + return 0 + } + visit[[2]int{r, c}] = true + return 1 + dfs(r+1, c) + dfs(r-1, c) + dfs(r, c+1) + dfs(r, c-1) + } + + area := 0 + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + area = max(area, dfs(r, c)) + } + } + return area +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun maxAreaOfIsland(grid: Array): Int { + val rows = grid.size + val cols = grid[0].size + val visit = HashSet>() + + fun dfs(r: Int, c: Int): Int { + if (r < 0 || r >= rows || c < 0 || c >= cols || + grid[r][c] == 0 || visit.contains(r to c)) { + return 0 + } + visit.add(r to c) + return 1 + dfs(r + 1, c) + dfs(r - 1, c) + dfs(r, c + 1) + dfs(r, c - 1) + } + + var area = 0 + for (r in 0 until rows) { + for (c in 0 until cols) { + area = maxOf(area, dfs(r, c)) + } + } + return area + } +} +``` + +```swift +class Solution { + func maxAreaOfIsland(_ grid: [[Int]]) -> Int { + let ROWS = grid.count + let COLS = grid[0].count + var visit = Set<[Int]>() + var grid = grid + + func dfs(_ r: Int, _ c: Int) -> Int { + if (r < 0 || r == ROWS || c < 0 || c == COLS || + grid[r][c] == 0 || visit.contains([r, c])) { + return 0 + } + visit.insert([r, c]) + return 1 + dfs(r + 1, c) + dfs(r - 1, c) + dfs(r, c + 1) + dfs(r, c - 1) + } + + var area = 0 + for r in 0.. Where $m$ is the number of rows and $n$ is the number of columns in the $grid$. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def maxAreaOfIsland(self, grid: List[List[int]]) -> int: + directions = [[1, 0], [-1, 0], [0, 1], [0, -1]] + ROWS, COLS = len(grid), len(grid[0]) + area = 0 + + def bfs(r, c): + q = deque() + grid[r][c] = 0 + q.append((r, c)) + res = 1 + + while q: + row, col = q.popleft() + for dr, dc in directions: + nr, nc = dr + row, dc + col + if (nr < 0 or nc < 0 or nr >= ROWS or + nc >= COLS or grid[nr][nc] == 0 + ): + continue + q.append((nr, nc)) + grid[nr][nc] = 0 + res += 1 + return res + + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] == 1: + area = max(area, bfs(r, c)) + + return area +``` + +```java +public class Solution { + private static final int[][] directions = {{1, 0}, {-1, 0}, + {0, 1}, {0, -1}}; + + public int maxAreaOfIsland(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int area = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 1) { + area = Math.max(area, bfs(grid, r, c)); + } + } + } + + return area; + } + + private int bfs(int[][] grid, int r, int c) { + Queue q = new LinkedList<>(); + grid[r][c] = 0; + q.add(new int[]{r, c}); + int res = 1; + + while (!q.isEmpty()) { + int[] node = q.poll(); + int row = node[0], col = node[1]; + + for (int[] dir : directions) { + int nr = row + dir[0], nc = col + dir[1]; + if (nr >= 0 && nc >= 0 && nr < grid.length && + nc < grid[0].length && grid[nr][nc] == 1) { + q.add(new int[]{nr, nc}); + grid[nr][nc] = 0; + res++; + } + } + } + return res; + } +} +``` + +```cpp +class Solution { + int directions[4][2] = {{1, 0}, {-1, 0}, + {0, 1}, {0, -1}}; +public: + int maxAreaOfIsland(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + int area = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 1) { + area = max(area, bfs(grid, r, c)); + } + } + } + + return area; + } + + int bfs(vector>& grid, int r, int c) { + queue> q; + grid[r][c] = 0; + q.push({r, c}); + int res = 1; + + while (!q.empty()) { + auto node = q.front();q.pop(); + int row = node.first, col = node.second; + for (int i = 0; i < 4; i++) { + int nr = row + directions[i][0]; + int nc = col + directions[i][1]; + if (nr >= 0 && nc >= 0 && nr < grid.size() && + nc < grid[0].size() && grid[nr][nc] == 1) { + q.push({nr, nc}); + grid[nr][nc] = 0; + res++; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + maxAreaOfIsland(grid) { + const directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]; + const ROWS = grid.length, COLS = grid[0].length; + let area = 0; + + const bfs = (r, c) => { + const q = new Queue(); + q.push([r, c]); + grid[r][c] = 0; + let res = 1; + + while (!q.isEmpty()) { + const [row, col] = q.pop(); + for (const [dr, dc] of directions) { + const nr = row + dr, nc = col + dc; + if (nr >= 0 && nc >= 0 && nr < ROWS && + nc < COLS && grid[nr][nc] === 1) { + q.push([nr, nc]); + grid[nr][nc] = 0; + res++; + } + } + } + return res; + }; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] === 1) { + area = Math.max(area, bfs(r, c)); + } + } + } + + return area; + } +} +``` + +```csharp +public class Solution { + private static readonly int[][] directions = new int[][] { + new int[] {1, 0}, new int[] {-1, 0}, + new int[] {0, 1}, new int[] {0, -1} + }; + + public int MaxAreaOfIsland(int[][] grid) { + int ROWS = grid.Length, COLS = grid[0].Length; + int area = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 1) { + area = Math.Max(area, Bfs(grid, r, c)); + } + } + } + + return area; + } + + private int Bfs(int[][] grid, int r, int c) { + Queue q = new Queue(); + grid[r][c] = 0; + q.Enqueue(new int[] { r, c }); + int res = 1; + + while (q.Count > 0) { + var node = q.Dequeue(); + int row = node[0], col = node[1]; + + foreach (var dir in directions) { + int nr = row + dir[0], nc = col + dir[1]; + if (nr >= 0 && nc >= 0 && nr < grid.Length && + nc < grid[0].Length && grid[nr][nc] == 1) { + q.Enqueue(new int[] { nr, nc }); + grid[nr][nc] = 0; + res++; + } + } + } + return res; + } +} +``` + +```go +func maxAreaOfIsland(grid [][]int) int { + directions := [][]int{{1, 0}, {-1, 0}, {0, 1}, {0, -1}} + rows, cols := len(grid), len(grid[0]) + area := 0 + + var bfs func(r, c int) int + bfs = func(r, c int) int { + q := [][]int{{r, c}} + grid[r][c] = 0 + res := 1 + + for len(q) > 0 { + front := q[0] + q = q[1:] + row, col := front[0], front[1] + for _, dir := range directions { + nr, nc := row+dir[0], col+dir[1] + if nr < 0 || nc < 0 || nr >= rows || + nc >= cols || grid[nr][nc] == 0 { + continue + } + q = append(q, []int{nr, nc}) + grid[nr][nc] = 0 + res++ + } + } + return res + } + + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + if grid[r][c] == 1 { + area = max(area, bfs(r, c)) + } + } + } + return area +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun maxAreaOfIsland(grid: Array): Int { + val directions = arrayOf(intArrayOf(1, 0), + intArrayOf(-1, 0), + intArrayOf(0, 1), + intArrayOf(0, -1)) + val rows = grid.size + val cols = grid[0].size + var area = 0 + + fun bfs(r: Int, c: Int): Int { + val queue = ArrayDeque>() + grid[r][c] = 0 + queue.add(Pair(r, c)) + var res = 1 + + while (queue.isNotEmpty()) { + val (row, col) = queue.removeFirst() + + for ((dr, dc) in directions) { + val nr = dr + row + val nc = dc + col + + if (nr < 0 || nc < 0 || nr >= rows || + nc >= cols || grid[nr][nc] == 0) { + continue + } + + queue.add(Pair(nr, nc)) + grid[nr][nc] = 0 + res++ + } + } + + return res + } + + for (r in 0 until rows) { + for (c in 0 until cols) { + if (grid[r][c] == 1) { + area = maxOf(area, bfs(r, c)) + } + } + } + + return area + } +} +``` + +```swift +class Solution { + func maxAreaOfIsland(_ grid: [[Int]]) -> Int { + let directions = [[1, 0], [-1, 0], [0, 1], [0, -1]] + let ROWS = grid.count + let COLS = grid[0].count + var grid = grid + var area = 0 + + func bfs(_ r: Int, _ c: Int) -> Int { + var queue = Deque<(Int, Int)>() + grid[r][c] = 0 + queue.append((r, c)) + var res = 1 + + while !queue.isEmpty { + let (row, col) = queue.popFirst()! + for dir in directions { + let nr = row + dir[0] + let nc = col + dir[1] + if nr < 0 || nc < 0 || nr >= ROWS || nc >= COLS || grid[nr][nc] == 0 { + continue + } + queue.append((nr, nc)) + grid[nr][nc] = 0 + res += 1 + } + } + return res + } + + for r in 0.. Where $m$ is the number of rows and $n$ is the number of columns in the $grid$. + +--- + +## 3. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu = self.find(u) + pv = self.find(v) + if pu == pv: + return False + if self.Size[pu] >= self.Size[pv]: + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + else: + self.Size[pv] += self.Size[pu] + self.Parent[pu] = pv + return True + + def getSize(self, node): + par = self.find(node) + return self.Size[par] + +class Solution: + def maxAreaOfIsland(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + dsu = DSU(ROWS * COLS) + + def index(r, c): + return r * COLS + c + + directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + area = 0 + + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] == 1: + for dr, dc in directions: + nr, nc = r + dr, c + dc + if (nr < 0 or nc < 0 or nr >= ROWS or + nc >= COLS or grid[nr][nc] == 0 + ): + continue + + dsu.union(index(r, c), index(nr, nc)) + + area = max(area, dsu.getSize(index(r, c))) + + return area +``` + +```java +class DSU { + private int[] Parent, Size; + + public DSU(int n) { + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + public int find(int node) { + if (node != Parent[node]) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u); + int pv = find(v); + if (pu == pv) return false; + if (Size[pu] >= Size[pv]) { + Size[pu] += Size[pv]; + Parent[pv] = pu; + } else { + Size[pv] += Size[pu]; + Parent[pu] = pv; + } + return true; + } + + public int getSize(int node) { + return Size[find(node)]; + } +} + +public class Solution { + public int maxAreaOfIsland(int[][] grid) { + int ROWS = grid.length; + int COLS = grid[0].length; + DSU dsu = new DSU(ROWS * COLS); + + int[][] directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + int area = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 1) { + for (int[] d : directions) { + int nr = r + d[0]; + int nc = c + d[1]; + if (nr >= 0 && nc >= 0 && nr < ROWS && + nc < COLS && grid[nr][nc] == 1) { + dsu.union(r * COLS + c, nr * COLS + nc); + } + } + area = Math.max(area, dsu.getSize(r * COLS + c)); + } + } + } + + return area; + } +} +``` + +```cpp +class DSU { + vector Parent, Size; +public: + DSU(int n) { + Parent.resize(n + 1); + Size.resize(n + 1); + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + int find(int node) { + if (node != Parent[node]) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + bool unionBySize(int u, int v) { + int pu = find(u); + int pv = find(v); + if (pu == pv) return false; + if (Size[pu] >= Size[pv]) { + Size[pu] += Size[pv]; + Parent[pv] = pu; + } else { + Size[pv] += Size[pu]; + Parent[pu] = pv; + } + return true; + } + + int getSize(int node) { + return Size[find(node)]; + } +}; + +class Solution { +public: + int maxAreaOfIsland(vector>& grid) { + int ROWS = grid.size(); + int COLS = grid[0].size(); + DSU dsu(ROWS * COLS); + + int directions[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + int area = 0; + + auto index = [&](int r, int c) { + return r * COLS + c; + }; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 1) { + for (auto& d : directions) { + int nr = r + d[0]; + int nc = c + d[1]; + if (nr >= 0 && nc >= 0 && nr < ROWS && + nc < COLS && grid[nr][nc] == 1) { + dsu.unionBySize(index(r, c), index(nr, nc)); + } + } + area = max(area, dsu.getSize(index(r, c))); + } + } + } + + return area; + } +}; +``` + +```javascript +class DSU { + constructor(n) { + this.Parent = Array(n + 1).fill(0).map((_, i) => i); + this.Size = Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.Parent[node] !== node) { + this.Parent[node] = this.find(this.Parent[node]); + } + return this.Parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u); + let pv = this.find(v); + if (pu === pv) return false; + if (this.Size[pu] >= this.Size[pv]) { + this.Size[pu] += this.Size[pv]; + this.Parent[pv] = pu; + } else { + this.Size[pv] += this.Size[pu]; + this.Parent[pu] = pv; + } + return true; + } + + /** + * @param {number} node + * @return {number} + */ + getSize(node) { + return this.Size[this.find(node)]; + } +} + +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + maxAreaOfIsland(grid) { + const ROWS = grid.length; + const COLS = grid[0].length; + const dsu = new DSU(ROWS * COLS); + + const directions = [ + [1, 0], [-1, 0], [0, 1], [0, -1] + ]; + let area = 0; + + const index = (r, c) => r * COLS + c; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] === 1) { + for (let [dr, dc] of directions) { + let nr = r + dr, nc = c + dc; + if (nr >= 0 && nc >= 0 && nr < ROWS && + nc < COLS && grid[nr][nc] === 1) { + dsu.union(index(r, c), index(nr, nc)); + } + } + area = Math.max(area, dsu.getSize(index(r, c))); + } + } + } + + return area; + } +} +``` + +```csharp +public class DSU { + private int[] Parent, Size; + + public DSU(int n) { + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + public int Find(int node) { + if (node != Parent[node]) { + Parent[node] = Find(Parent[node]); + } + return Parent[node]; + } + + public bool Union(int u, int v) { + int pu = Find(u); + int pv = Find(v); + if (pu == pv) return false; + if (Size[pu] >= Size[pv]) { + Size[pu] += Size[pv]; + Parent[pv] = pu; + } else { + Size[pv] += Size[pu]; + Parent[pu] = pv; + } + return true; + } + + public int GetSize(int node) { + return Size[Find(node)]; + } +} + +public class Solution { + public int MaxAreaOfIsland(int[][] grid) { + int ROWS = grid.Length; + int COLS = grid[0].Length; + DSU dsu = new DSU(ROWS * COLS); + + int[][] directions = new int[][] { + new int[] { 1, 0 }, new int[] { -1, 0 }, + new int[] { 0, 1 }, new int[] { 0, -1 } + }; + int area = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 1) { + foreach (var d in directions) { + int nr = r + d[0]; + int nc = c + d[1]; + if (nr >= 0 && nc >= 0 && nr < ROWS && + nc < COLS && grid[nr][nc] == 1) { + dsu.Union(r * COLS + c, nr * COLS + nc); + } + } + area = Math.Max(area, dsu.GetSize(r * COLS + c)); + } + } + } + + return area; + } +} +``` + +```go +type DSU struct { + Parent []int + Size []int +} + +func (dsu *DSU) find(node int) int { + if dsu.Parent[node] != node { + dsu.Parent[node] = dsu.find(dsu.Parent[node]) + } + return dsu.Parent[node] +} + +func (dsu *DSU) union(u, v int) bool { + pu := dsu.find(u) + pv := dsu.find(v) + if pu == pv { + return false + } + if dsu.Size[pu] >= dsu.Size[pv] { + dsu.Size[pu] += dsu.Size[pv] + dsu.Parent[pv] = pu + } else { + dsu.Size[pv] += dsu.Size[pu] + dsu.Parent[pu] = pv + } + return true +} + +func (dsu *DSU) getSize(node int) int { + par := dsu.find(node) + return dsu.Size[par] +} + +func maxAreaOfIsland(grid [][]int) int { + rows, cols := len(grid), len(grid[0]) + dsu := DSU{ + Parent: make([]int, rows*cols+1), + Size: make([]int, rows*cols+1), + } + + for i := 0; i <= rows*cols; i++ { + dsu.Parent[i] = i + dsu.Size[i] = 1 + } + + index := func(r, c int) int { + return r*cols + c + } + + directions := [][]int{{1, 0}, {-1, 0}, {0, 1}, {0, -1}} + area := 0 + + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + if grid[r][c] == 1 { + for _, dir := range directions { + nr, nc := r+dir[0], c+dir[1] + if nr < 0 || nc < 0 || nr >= rows || + nc >= cols || grid[nr][nc] == 0 { + continue + } + dsu.union(index(r, c), index(nr, nc)) + } + area = max(area, dsu.getSize(index(r, c))) + } + } + } + return area +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class DSU(n: Int) { + val Parent = IntArray(n + 1) { it } + val Size = IntArray(n + 1) { 1 } + + fun find(node: Int): Int { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]) + } + return Parent[node] + } + + fun union(u: Int, v: Int): Boolean { + val pu = find(u) + val pv = find(v) + if (pu == pv) { + return false + } + if (Size[pu] >= Size[pv]) { + Size[pu] += Size[pv] + Parent[pv] = pu + } else { + Size[pv] += Size[pu] + Parent[pu] = pv + } + return true + } + + fun getSize(node: Int): Int { + val par = find(node) + return Size[par] + } +} + +class Solution { + fun maxAreaOfIsland(grid: Array): Int { + val rows = grid.size + val cols = grid[0].size + val dsu = DSU(rows * cols) + + val index = { r: Int, c: Int -> r * cols + c } + + val directions = arrayOf( + intArrayOf(1, 0), + intArrayOf(-1, 0), + intArrayOf(0, 1), + intArrayOf(0, -1) + ) + var area = 0 + + for (r in 0 until rows) { + for (c in 0 until cols) { + if (grid[r][c] == 1) { + for (dir in directions) { + val nr = r + dir[0] + val nc = c + dir[1] + if (nr < 0 || nc < 0 || nr >= rows || + nc >= cols || grid[nr][nc] == 0) { + continue + } + dsu.union(index(r, c), index(nr, nc)) + } + area = maxOf(area, dsu.getSize(index(r, c))) + } + } + } + return area + } +} +``` + +```swift +class DSU { + private var parent: [Int] + private var size: [Int] + + init(_ n: Int) { + parent = Array(0...n) + size = Array(repeating: 1, count: n + 1) + } + + func find(_ node: Int) -> Int { + if parent[node] != node { + parent[node] = find(parent[node]) + } + return parent[node] + } + + func union(_ u: Int, _ v: Int) -> Bool { + let pu = find(u) + let pv = find(v) + if pu == pv { + return false + } + if size[pu] >= size[pv] { + size[pu] += size[pv] + parent[pv] = pu + } else { + size[pv] += size[pu] + parent[pu] = pv + } + return true + } + + func getSize(_ node: Int) -> Int { + let par = find(node) + return size[par] + } +} + +class Solution { + func maxAreaOfIsland(_ grid: [[Int]]) -> Int { + var grid = grid + let ROWS = grid.count + let COLS = grid[0].count + let dsu = DSU(ROWS * COLS) + + func index(_ r: Int, _ c: Int) -> Int { + return r * COLS + c + } + + let directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + var area = 0 + + for r in 0..= ROWS || nc >= COLS || grid[nr][nc] == 0 { + continue + } + dsu.union(index(r, c), index(nr, nc)) + } + area = max(area, dsu.getSize(index(r, c))) + } + } + } + + return area + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the $grid$. \ No newline at end of file diff --git a/articles/max-points-on-a-line.md b/articles/max-points-on-a-line.md new file mode 100644 index 000000000..8c87a3369 --- /dev/null +++ b/articles/max-points-on-a-line.md @@ -0,0 +1,384 @@ +## 1. Math + +::tabs-start + +```python +class Solution: + def maxPoints(self, points: List[List[int]]) -> int: + n = len(points) + if n <= 2: + return n + + def get_slope(p1, p2): + if p1[0] == p2[0]: + return float("inf") + return (p2[1] - p1[1]) / (p2[0] - p1[0]) + + res = 1 + for i in range(n): + for j in range(i + 1, n): + slope = get_slope(points[i], points[j]) + cnt = 2 + for k in range(j + 1, n): + if slope == get_slope(points[i], points[k]): + cnt += 1 + res = max(res, cnt) + + return res +``` + +```java +public class Solution { + public int maxPoints(int[][] points) { + int n = points.length; + if (n <= 2) { + return n; + } + + int res = 1; + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + double slope = getSlope(points[i], points[j]); + int cnt = 2; + for (int k = j + 1; k < n; k++) { + if (slope == getSlope(points[i], points[k])) { + cnt++; + } + } + res = Math.max(res, cnt); + } + } + + return res; + } + + private double getSlope(int[] p1, int[] p2) { + if (p1[0] == p2[0]) { + return Double.POSITIVE_INFINITY; + } + return (double) (p2[1] - p1[1]) / (p2[0] - p1[0]); + } +} +``` + +```cpp +class Solution { +public: + int maxPoints(vector>& points) { + int n = points.size(); + if (n <= 2) { + return n; + } + + int res = 1; + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + double slope = getSlope(points[i], points[j]); + int cnt = 2; + for (int k = j + 1; k < n; k++) { + if (slope == getSlope(points[i], points[k])) { + cnt++; + } + } + res = max(res, cnt); + } + } + + return res; + } + +private: + double getSlope(vector& p1, vector& p2) { + if (p1[0] == p2[0]) { + return INFINITY; + } + return (double)(p2[1] - p1[1]) / (p2[0] - p1[0]); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} points + * @return {number} + */ + maxPoints(points) { + const n = points.length; + if (n <= 2) { + return n; + } + + let res = 1; + const getSlope = (p1, p2) => { + if (p1[0] === p2[0]) { + return Infinity; + } + return (p2[1] - p1[1]) / (p2[0] - p1[0]); + }; + + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + const slope = getSlope(points[i], points[j]); + let cnt = 2; + for (let k = j + 1; k < n; k++) { + if (slope === getSlope(points[i], points[k])) { + cnt++; + } + } + res = Math.max(res, cnt); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Math + Hash Map + +::tabs-start + +```python +class Solution: + def maxPoints(self, points: List[List[int]]) -> int: + res = 1 + for i in range(len(points)): + p1 = points[i] + count = defaultdict(int) + for j in range(i + 1, len(points)): + p2 = points[j] + if p2[0] == p1[0]: + slope = float("inf") + else: + slope = (p2[1] - p1[1]) / (p2[0] - p1[0]) + count[slope] += 1 + res = max(res, count[slope] + 1) + return res +``` + +```java +public class Solution { + public int maxPoints(int[][] points) { + int res = 1; + for (int i = 0; i < points.length; i++) { + Map count = new HashMap<>(); + for (int j = i + 1; j < points.length; j++) { + double slope = getSlope(points[i], points[j]); + if (slope == -0.0) slope = 0.0; + + count.put(slope, count.getOrDefault(slope, 0) + 1); + res = Math.max(res, count.get(slope) + 1); + } + } + return res; + } + + private double getSlope(int[] p1, int[] p2) { + if (p1[0] == p2[0]) { + return Double.POSITIVE_INFINITY; + } + return (double) (p2[1] - p1[1]) / (p2[0] - p1[0]); + } +} +``` + +```cpp +class Solution { +public: + int maxPoints(vector>& points) { + int res = 1; + for (int i = 0; i < points.size(); i++) { + vector& p1 = points[i]; + unordered_map count; + for (int j = i + 1; j < points.size(); j++) { + vector& p2 = points[j]; + double slope = (p2[0] == p1[0]) ? INFINITY : + (double)(p2[1] - p1[1]) / (p2[0] - p1[0]); + count[slope]++; + res = max(res, count[slope] + 1); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} points + * @return {number} + */ + maxPoints(points) { + let res = 1; + for (let i = 0; i < points.length; i++) { + const p1 = points[i]; + const count = new Map(); + for (let j = i + 1; j < points.length; j++) { + const p2 = points[j]; + const slope = (p2[0] === p1[0]) ? Infinity : + (p2[1] - p1[1]) / (p2[0] - p1[0]); + count.set(slope, (count.get(slope) || 0) + 1); + res = Math.max(res, count.get(slope) + 1); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Math + Hash Map (Optimal) + +::tabs-start + +```python +class Solution: + def maxPoints(self, points: List[List[int]]) -> int: + if len(points) <= 2: + return len(points) + + def gcd(a, b): + return gcd(b, a % b) if b else a + + res = 1 + for i in range(len(points) - 1): + count = defaultdict(int) + for j in range(i + 1, len(points)): + dx = points[j][0] - points[i][0] + dy = points[j][1] - points[i][1] + g = gcd(dx, dy) + dx //= g + dy //= g + slope = (dx, dy) + count[slope] += 1 + res = max(res, max(count.values()) + 1) + return res +``` + +```java +public class Solution { + public int maxPoints(int[][] points) { + if (points.length <= 2) { + return points.length; + } + + int res = 1; + for (int i = 0; i < points.length - 1; i++) { + Map count = new HashMap<>(); + for (int j = i + 1; j < points.length; j++) { + int dx = points[j][0] - points[i][0]; + int dy = points[j][1] - points[i][1]; + int g = gcd(dx, dy); + dx /= g; + dy /= g; + String slope = dx + ":" + dy; + count.put(slope, count.getOrDefault(slope, 0) + 1); + } + for (int val : count.values()) { + res = Math.max(res, val + 1); + } + } + return res; + } + + private int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +} +``` + +```cpp +class Solution { +public: + int maxPoints(vector>& points) { + if (points.size() <= 2) { + return points.size(); + } + + int res = 1; + for (int i = 0; i < points.size() - 1; i++) { + unordered_map count; + for (int j = i + 1; j < points.size(); j++) { + int dx = points[j][0] - points[i][0]; + int dy = points[j][1] - points[i][1]; + int g = gcd(dx, dy); + dx /= g; + dy /= g; + string slope = to_string(dx) + ":" + to_string(dy); + count[slope]++; + } + for (const auto& [slope, freq] : count) { + res = max(res, freq + 1); + } + } + return res; + } + +private: + int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} points + * @return {number} + */ + maxPoints(points) { + if (points.length <= 2) { + return points.length; + } + + const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b)); + + let res = 1; + for (let i = 0; i < points.length - 1; i++) { + const count = new Map(); + for (let j = i + 1; j < points.length; j++) { + let dx = points[j][0] - points[i][0]; + let dy = points[j][1] - points[i][1]; + const g = gcd(dx, dy); + dx /= g; + dy /= g; + const slope = `${dx}:${dy}`; + count.set(slope, (count.get(slope) || 0) + 1); + } + for (const freq of count.values()) { + res = Math.max(res, freq + 1); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 \log m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of points and $m$ is the maximum value in the points. \ No newline at end of file diff --git a/articles/max-water-container.md b/articles/max-water-container.md new file mode 100644 index 000000000..bb1ea6083 --- /dev/null +++ b/articles/max-water-container.md @@ -0,0 +1,319 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxArea(self, heights: List[int]) -> int: + res = 0 + for i in range(len(heights)): + for j in range(i + 1, len(heights)): + res = max(res, min(heights[i], heights[j]) * (j - i)) + return res +``` + +```java +public class Solution { + public int maxArea(int[] heights) { + int res = 0; + for (int i = 0; i < heights.length; i++) { + for (int j = i + 1; j < heights.length; j++) { + res = Math.max(res, Math.min(heights[i], heights[j]) * (j - i)); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxArea(vector& heights) { + int res = 0; + for (int i = 0; i < heights.size(); i++) { + for (int j = i + 1; j < heights.size(); j++) { + res = max(res, min(heights[i], heights[j]) * (j - i)); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @return {number} + */ + maxArea(heights) { + let res = 0; + for (let i = 0; i < heights.length; i++) { + for (let j = i + 1; j < heights.length; j++) { + res = Math.max(res, Math.min(heights[i], heights[j]) * (j - i)); + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int MaxArea(int[] heights) { + int res = 0; + for (int i = 0; i < heights.Length; i++) { + for (int j = i + 1; j < heights.Length; j++) { + res = Math.Max(res, Math.Min(heights[i], heights[j]) * (j - i)); + } + } + return res; + } +} +``` + +```go +func maxArea(heights []int) int { + res := 0 + for i := 0; i < len(heights); i++ { + for j := i + 1; j < len(heights); j++ { + area := min(heights[i], heights[j]) * (j - i) + if area > res { + res = area + } + } + } + return res +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun maxArea(heights: IntArray): Int { + var res = 0 + for (i in heights.indices) { + for (j in i + 1 until heights.size) { + val area = minOf(heights[i], heights[j]) * (j - i) + res = maxOf(res, area) + } + } + return res + } +} +``` + +```swift +class Solution { + func maxArea(_ heights: [Int]) -> Int { + var res = 0 + for i in 0.. int: + l, r = 0, len(heights) - 1 + res = 0 + + while l < r: + area = min(heights[l], heights[r]) * (r - l) + res = max(res, area) + if heights[l] <= heights[r]: + l += 1 + else: + r -= 1 + return res +``` + +```java +public class Solution { + public int maxArea(int[] heights) { + int l = 0; + int r = heights.length - 1; + int res = 0; + + while (l < r) { + int area = Math.min(heights[l], heights[r]) * (r - l); + res = Math.max(res, area); + if (heights[l] <= heights[r]) { + l++; + } else { + r--; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxArea(vector& heights) { + int l = 0; + int r = heights.size() - 1; + int res = 0; + + while (l < r) { + int area = min(heights[l], heights[r]) * (r - l); + res = max(res, area); + + if (heights[l] <= heights[r]) { + l++; + } else { + r--; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} heights + * @return {number} + */ + maxArea(heights) { + let l = 0; + let r = heights.length - 1; + let res = 0; + + while (l < r) { + const area = Math.min(heights[l], heights[r]) * (r - l); + res = Math.max(res, area); + if (heights[l] <= heights[r]) { + l++; + } else { + r--; + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int MaxArea(int[] heights) { + int res = 0; + int l = 0, r = heights.Length-1; + + while (l < r){ + int area = (Math.Min(heights[l], heights[r])) * (r - l); + res = Math.Max(area, res); + + if (heights[l] <= heights[r]){ + l++; + } else{ + r--; + } + } + return res; + } +} +``` + +```go +func maxArea(heights []int) int { + l, r := 0, len(heights) - 1 + res := 0 + + for l < r { + area := min(heights[l], heights[r]) * (r - l) + if area > res { + res = area + } + if heights[l] <= heights[r] { + l++ + } else { + r-- + } + } + return res +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun maxArea(heights: IntArray): Int { + var l = 0 + var r = heights.size - 1 + var res = 0 + + while (l < r) { + val area = minOf(heights[l], heights[r]) * (r - l) + res = maxOf(res, area) + if (heights[l] <= heights[r]) { + l++ + } else { + r-- + } + } + return res + } +} +``` + +```swift +class Solution { + func maxArea(_ heights: [Int]) -> Int { + var l = 0, r = heights.count - 1 + var res = 0 + + while l < r { + let area = min(heights[l], heights[r]) * (r - l) + res = max(res, area) + if heights[l] <= heights[r] { + l += 1 + } else { + r -= 1 + } + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/maximal-square.md b/articles/maximal-square.md new file mode 100644 index 000000000..ab7d2fce0 --- /dev/null +++ b/articles/maximal-square.md @@ -0,0 +1,560 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maximalSquare(self, matrix: List[List[str]]) -> int: + m, n = len(matrix), len(matrix[0]) + res = 0 + + for r in range(m): + for c in range(n): + if matrix[r][c] == "0": + continue + k = 1 + while True: + if r + k > m or c + k > n: + break + flag = True + + for i in range(r, r + k): + if matrix[i][c + k - 1] == "0": + flag = False + break + for j in range(c, c + k): + if matrix[r + k - 1][j] == "0": + flag = False + break + + if not flag: + break + res = max(res, k * k) + k += 1 + + return res +``` + +```java +public class Solution { + public int maximalSquare(char[][] matrix) { + int m = matrix.length, n = matrix[0].length; + int res = 0; + + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + if (matrix[r][c] == '0') { + continue; + } + int k = 1; + while (true) { + if (r + k > m || c + k > n) { + break; + } + boolean flag = true; + + for (int i = r; i < r + k; i++) { + if (matrix[i][c + k - 1] == '0') { + flag = false; + break; + } + } + for (int j = c; j < c + k; j++) { + if (matrix[r + k - 1][j] == '0') { + flag = false; + break; + } + } + + if (!flag) { + break; + } + res = Math.max(res, k * k); + k++; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maximalSquare(vector>& matrix) { + int m = matrix.size(), n = matrix[0].size(); + int res = 0; + + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + if (matrix[r][c] == '0') { + continue; + } + int k = 1; + while (true) { + if (r + k > m || c + k > n) { + break; + } + bool flag = true; + + for (int i = r; i < r + k; i++) { + if (matrix[i][c + k - 1] == '0') { + flag = false; + break; + } + } + for (int j = c; j < c + k; j++) { + if (matrix[r + k - 1][j] == '0') { + flag = false; + break; + } + } + + if (!flag) { + break; + } + res = max(res, k * k); + k++; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[][]} matrix + * @return {number} + */ + maximalSquare(matrix) { + const m = matrix.length, n = matrix[0].length; + let res = 0; + + for (let r = 0; r < m; r++) { + for (let c = 0; c < n; c++) { + if (matrix[r][c] === "0") { + continue; + } + let k = 1; + while (true) { + if (r + k > m || c + k > n) { + break; + } + let flag = true; + + for (let i = r; i < r + k; i++) { + if (matrix[i][c + k - 1] === "0") { + flag = false; + break; + } + } + for (let j = c; j < c + k; j++) { + if (matrix[r + k - 1][j] === "0") { + flag = false; + break; + } + } + + if (!flag) { + break; + } + res = Math.max(res, k * k); + k++; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((m * n) ^ 2)$ +* Space complexity: $O(1)$ + +> Where $m$ is the number of rows and $n$ is the number columns. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def maximalSquare(self, matrix: List[List[str]]) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + cache = {} + + def dfs(r, c): + if r >= ROWS or c >= COLS: + return 0 + if (r, c) not in cache: + down = dfs(r + 1, c) + right = dfs(r, c + 1) + diag = dfs(r + 1, c + 1) + cache[(r, c)] = 0 + if matrix[r][c] == "1": + cache[(r, c)] = 1 + min(down, right, diag) + return cache[(r, c)] + + dfs(0, 0) + return max(cache.values()) ** 2 +``` + +```java +public class Solution { + private int[][] dp; + + public int maximalSquare(char[][] matrix) { + int ROWS = matrix.length, COLS = matrix[0].length; + dp = new int[ROWS][COLS]; + for (int i = 0; i < ROWS; i++) { + for (int j = 0; j < COLS; j++) { + dp[i][j] = -1; + } + } + + dfs(0, 0, matrix); + int maxSquare = 0; + for (int i = 0; i < ROWS; i++) { + for (int j = 0; j < COLS; j++) { + maxSquare = Math.max(maxSquare, dp[i][j]); + } + } + return maxSquare * maxSquare; + } + + private int dfs(int r, int c, char[][] matrix) { + if (r >= matrix.length || c >= matrix[0].length) { + return 0; + } + if (dp[r][c] != -1) { + return dp[r][c]; + } + int down = dfs(r + 1, c, matrix); + int right = dfs(r, c + 1, matrix); + int diag = dfs(r + 1, c + 1, matrix); + dp[r][c] = 0; + if (matrix[r][c] == '1') { + dp[r][c] = 1 + Math.min(down, Math.min(right, diag)); + } + return dp[r][c]; + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + +public: + int maximalSquare(vector>& matrix) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + dp = vector>(ROWS, vector(COLS, -1)); + + dfs(0, 0, matrix); + int maxSquare = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + maxSquare = max(maxSquare, dp[r][c]); + } + } + return maxSquare * maxSquare; + } + + int dfs(int r, int c, vector>& matrix) { + if (r >= matrix.size() || c >= matrix[0].size()) { + return 0; + } + if (dp[r][c] != -1) { + return dp[r][c]; + } + int down = dfs(r + 1, c, matrix); + int right = dfs(r, c + 1, matrix); + int diag = dfs(r + 1, c + 1, matrix); + dp[r][c] = 0; + if (matrix[r][c] == '1') { + dp[r][c] = 1 + min(down, min(right, diag)); + } + return dp[r][c]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[][]} matrix + * @return {number} + */ + maximalSquare(matrix) { + const ROWS = matrix.length, COLS = matrix[0].length; + const dp = Array.from({ length: ROWS }, () => Array(COLS).fill(-1)); + + const dfs = (r, c) => { + if (r >= ROWS || c >= COLS) { + return 0; + } + if (dp[r][c] !== -1) { + return dp[r][c]; + } + const down = dfs(r + 1, c); + const right = dfs(r, c + 1); + const diag = dfs(r + 1, c + 1); + dp[r][c] = 0; + if (matrix[r][c] === "1") { + dp[r][c] = 1 + Math.min(down, Math.min(right, diag)); + } + return dp[r][c]; + }; + + dfs(0, 0); + let maxSquare = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + maxSquare = Math.max(maxSquare, dp[r][c]); + } + } + return maxSquare * maxSquare; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number columns. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def maximalSquare(self, matrix: List[List[str]]) -> int: + m, n = len(matrix), len(matrix[0]) + dp = [[0] * (n + 1) for _ in range(m + 1)] + max_square = 0 + + for r in range(m - 1, -1, -1): + for c in range(n - 1, -1, -1): + if matrix[r][c] == "1": + dp[r][c] = 1 + min(dp[r + 1][c], dp[r][c + 1], dp[r + 1][c + 1]) + max_square = max(max_square, dp[r][c]) + + return max_square * max_square +``` + +```java +public class Solution { + public int maximalSquare(char[][] matrix) { + int m = matrix.length, n = matrix[0].length; + int[][] dp = new int[m + 1][n + 1]; + int maxSquare = 0; + + for (int r = m - 1; r >= 0; r--) { + for (int c = n - 1; c >= 0; c--) { + if (matrix[r][c] == '1') { + dp[r][c] = 1 + Math.min(dp[r + 1][c], Math.min(dp[r][c + 1], dp[r + 1][c + 1])); + maxSquare = Math.max(maxSquare, dp[r][c]); + } + } + } + + return maxSquare * maxSquare; + } +} +``` + +```cpp +class Solution { +public: + int maximalSquare(vector>& matrix) { + int m = matrix.size(), n = matrix[0].size(); + vector> dp(m + 1, vector(n + 1, 0)); + int maxSquare = 0; + + for (int r = m - 1; r >= 0; r--) { + for (int c = n - 1; c >= 0; c--) { + if (matrix[r][c] == '1') { + dp[r][c] = 1 + min({dp[r + 1][c], dp[r][c + 1], dp[r + 1][c + 1]}); + maxSquare = max(maxSquare, dp[r][c]); + } + } + } + + return maxSquare * maxSquare; + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[][]} matrix + * @return {number} + */ + maximalSquare(matrix) { + const m = matrix.length, n = matrix[0].length; + const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0)); + let maxSquare = 0; + + for (let r = m - 1; r >= 0; r--) { + for (let c = n - 1; c >= 0; c--) { + if (matrix[r][c] === "1") { + dp[r][c] = 1 + Math.min(dp[r + 1][c], dp[r][c + 1], dp[r + 1][c + 1]); + maxSquare = Math.max(maxSquare, dp[r][c]); + } + } + } + + return maxSquare * maxSquare; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number columns. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def maximalSquare(self, matrix: List[List[str]]) -> int: + m, n = len(matrix), len(matrix[0]) + dp = [0] * (n + 1) + max_square = 0 + + for r in range(m - 1, -1, -1): + prev = 0 + for c in range(n - 1, -1, -1): + temp = dp[c] + if matrix[r][c] == "1": + dp[c] = 1 + min(dp[c], dp[c + 1], prev) + max_square = max(max_square, dp[c]) + else: + dp[c] = 0 + prev = temp + + return max_square * max_square +``` + +```java +public class Solution { + public int maximalSquare(char[][] matrix) { + int m = matrix.length, n = matrix[0].length; + int[] dp = new int[n + 1]; + int maxSquare = 0; + + for (int r = m - 1; r >= 0; r--) { + int prev = 0; + for (int c = n - 1; c >= 0; c--) { + int temp = dp[c]; + if (matrix[r][c] == '1') { + dp[c] = 1 + Math.min(dp[c], Math.min(dp[c + 1], prev)); + maxSquare = Math.max(maxSquare, dp[c]); + } else { + dp[c] = 0; + } + prev = temp; + } + } + + return maxSquare * maxSquare; + } +} +``` + +```cpp +class Solution { +public: + int maximalSquare(vector>& matrix) { + int m = matrix.size(), n = matrix[0].size(); + vector dp(n + 1, 0); + int maxSquare = 0; + + for (int r = m - 1; r >= 0; r--) { + int prev = 0; + for (int c = n - 1; c >= 0; c--) { + int temp = dp[c]; + if (matrix[r][c] == '1') { + dp[c] = 1 + min({dp[c], dp[c + 1], prev}); + maxSquare = max(maxSquare, dp[c]); + } else { + dp[c] = 0; + } + prev = temp; + } + } + + return maxSquare * maxSquare; + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[][]} matrix + * @return {number} + */ + maximalSquare(matrix) { + const m = matrix.length, n = matrix[0].length; + const dp = new Array(n + 1).fill(0); + let maxSquare = 0; + let prev = 0; + + for (let r = m - 1; r >= 0; r--) { + for (let c = n - 1; c >= 0; c--) { + const temp = dp[c]; + if (matrix[r][c] === "1") { + dp[c] = 1 + Math.min(dp[c], dp[c + 1], prev); + maxSquare = Math.max(maxSquare, dp[c]); + } else { + dp[c] = 0; + } + prev = temp; + } + } + + return maxSquare * maxSquare; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(n)$ + +> Where $m$ is the number of rows and $n$ is the number columns. \ No newline at end of file diff --git a/articles/maximize-score-after-n-operations.md b/articles/maximize-score-after-n-operations.md new file mode 100644 index 000000000..bf90e9d55 --- /dev/null +++ b/articles/maximize-score-after-n-operations.md @@ -0,0 +1,629 @@ +## 1. Brute Force (Backtracking) + +::tabs-start + +```python +class Solution: + def maxScore(self, nums: List[int]) -> int: + N = len(nums) + visit = [False] * N + + def dfs(n): + if n > (N // 2): + return 0 + + res = 0 + for i in range(N): + if visit[i]: + continue + visit[i] = True + for j in range(i + 1, N): + if visit[j]: + continue + visit[j] = True + g = gcd(nums[i], nums[j]) + res = max(res, n * g + dfs(n + 1)) + visit[j] = False + visit[i] = False + + return res + + return dfs(1) +``` + +```java +public class Solution { + public int maxScore(int[] nums) { + int N = nums.length; + boolean[] visit = new boolean[N]; + return dfs(nums, visit, 1, N); + } + + private int dfs(int[] nums, boolean[] visit, int n, int N) { + if (n > N / 2) { + return 0; + } + + int res = 0; + for (int i = 0; i < N; i++) { + if (visit[i]) continue; + visit[i] = true; + for (int j = i + 1; j < N; j++) { + if (visit[j]) continue; + visit[j] = true; + int g = gcd(nums[i], nums[j]); + res = Math.max(res, n * g + dfs(nums, visit, n + 1, N)); + visit[j] = false; + } + visit[i] = false; + } + + return res; + } + + private int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +} +``` + +```cpp +class Solution { +public: + int maxScore(vector& nums) { + int N = nums.size(); + vector visit(N, false); + return dfs(nums, visit, 1, N); + } + +private: + int dfs(vector& nums, vector& visit, int n, int N) { + if (n > N / 2) { + return 0; + } + + int res = 0; + for (int i = 0; i < N; i++) { + if (visit[i]) continue; + visit[i] = true; + for (int j = i + 1; j < N; j++) { + if (visit[j]) continue; + visit[j] = true; + int g = gcd(nums[i], nums[j]); + res = max(res, n * g + dfs(nums, visit, n + 1, N)); + visit[j] = false; + } + visit[i] = false; + } + + return res; + } + + int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxScore(nums) { + const N = nums.length; + const visit = new Array(N).fill(false); + + const gcd = (a, b) => { + return b === 0 ? a : gcd(b, a % b); + }; + const dfs = (n) => { + if (n > N / 2) { + return 0; + } + + let res = 0; + for (let i = 0; i < N; i++) { + if (visit[i]) continue; + visit[i] = true; + for (let j = i + 1; j < N; j++) { + if (visit[j]) continue; + visit[j] = true; + let g = gcd(nums[i], nums[j]); + res = Math.max(res, n * g + dfs(n + 1)); + visit[j] = false; + } + visit[i] = false; + } + + return res; + }; + + return dfs(1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ n * \log m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum element in the array. + +--- + +## 2. Bitmask DP (Top-Down) - I + +::tabs-start + +```python +class Solution: + def maxScore(self, nums: List[int]) -> int: + cache = collections.defaultdict(int) + + def dfs(mask, op): + if mask in cache: + return cache[mask] + + for i in range(len(nums)): + for j in range(i + 1, len(nums)): + if (1 << i) & mask or (1 << j) & mask: + continue + + newMask = mask | (1 << i) | (1 << j) + score = op * math.gcd(nums[i], nums[j]) + cache[mask] = max( + cache[mask], + score + dfs(newMask, op + 1) + ) + + return cache[mask] + + return dfs(0, 1) +``` + +```java +public class Solution { + private Map cache; + + public int maxScore(int[] nums) { + cache = new HashMap<>(); + return dfs(0, 1, nums); + } + + private int dfs(int mask, int op, int[] nums) { + if (cache.containsKey(mask)) { + return cache.get(mask); + } + + int maxScore = 0; + int n = nums.length; + for (int i = 0; i < n; i++) { + if ((mask & (1 << i)) != 0) continue; + for (int j = i + 1; j < n; j++) { + if ((mask & (1 << j)) != 0) continue; + int newMask = mask | (1 << i) | (1 << j); + int score = op * gcd(nums[i], nums[j]) + dfs(newMask, op + 1, nums); + maxScore = Math.max(maxScore, score); + } + } + + cache.put(mask, maxScore); + return maxScore; + } + + private int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +} +``` + +```cpp +class Solution { +public: + int maxScore(vector& nums) { + return dfs(0, 1, nums); + } + +private: + unordered_map cache; + + int dfs(int mask, int op, vector& nums) { + if (cache.count(mask)) { + return cache[mask]; + } + + int maxScore = 0; + int n = nums.size(); + for (int i = 0; i < n; i++) { + if ((mask & (1 << i)) != 0) continue; + for (int j = i + 1; j < n; j++) { + if ((mask & (1 << j)) != 0) continue; + int newMask = mask | (1 << i) | (1 << j); + int score = op * gcd(nums[i], nums[j]) + dfs(newMask, op + 1, nums); + maxScore = max(maxScore, score); + } + } + + return cache[mask] = maxScore; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxScore(nums) { + const cache = new Map(); + + const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b)); + + const dfs = (mask, op) => { + if (cache.has(mask)) { + return cache.get(mask); + } + + let maxScore = 0; + const n = nums.length; + for (let i = 0; i < n; i++) { + if ((mask & (1 << i)) !== 0) continue; + for (let j = i + 1; j < n; j++) { + if ((mask & (1 << j)) !== 0) continue; + let newMask = mask | (1 << i) | (1 << j); + let score = op * gcd(nums[i], nums[j]) + dfs(newMask, op + 1); + maxScore = Math.max(maxScore, score); + } + } + + cache.set(mask, maxScore); + return maxScore; + }; + + + return dfs(0, 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * 2 ^ n * \log m)$ +* Space complexity: $O(2 ^ n)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum element in the array. + +--- + +## 3. Bitmask DP (Top-Down) - II + +::tabs-start + +```python +class Solution: + def maxScore(self, nums: List[int]) -> int: + n = len(nums) + GCD = [[0] * n for _ in range(n)] + for i in range(n): + for j in range(i + 1, n): + GCD[i][j] = gcd(nums[i], nums[j]) + + dp = [-1] * (1 << n) + def dfs(mask, op): + if dp[mask] != -1: + return dp[mask] + + max_score = 0 + for i in range(n): + if mask & (1 << i): + continue + for j in range(i + 1, n): + if mask & (1 << j): + continue + new_mask = mask | (1 << i) | (1 << j) + max_score = max( + max_score, + op * GCD[i][j] + dfs(new_mask, op + 1) + ) + + dp[mask] = max_score + return max_score + + return dfs(0, 1) +``` + +```java +public class Solution { + private int[][] GCD; + private int[] dp; + + public int maxScore(int[] nums) { + int n = nums.length; + GCD = new int[n][n]; + dp = new int[1 << n]; + Arrays.fill(dp, -1); + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + GCD[i][j] = gcd(nums[i], nums[j]); + } + } + + return (int) dfs(0, 1, nums); + } + + private int dfs(int mask, int op, int[] nums) { + if (dp[mask] != -1) return dp[mask]; + + int maxScore = 0; + for (int i = 0; i < nums.length; i++) { + if ((mask & (1 << i)) != 0) continue; + for (int j = i + 1; j < nums.length; j++) { + if ((mask & (1 << j)) != 0) continue; + int newMask = mask | (1 << i) | (1 << j); + maxScore = Math.max( + maxScore, + op * GCD[i][j] + dfs(newMask, op + 1, nums) + ); + } + } + return dp[mask] = maxScore; + } + + private int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +} +``` + +```cpp +class Solution { +public: + int maxScore(vector& nums) { + int n = nums.size(); + GCD.assign(n, vector(n, 0)); + dp.assign(1 << n, -1); + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + GCD[i][j] = gcd(nums[i], nums[j]); + } + } + + return dfs(0, 1, nums); + } + +private: + vector> GCD; + vector dp; + + int dfs(int mask, int op, vector& nums) { + if (dp[mask] != -1) return dp[mask]; + + int maxScore = 0; + for (int i = 0; i < nums.size(); i++) { + if (mask & (1 << i)) continue; + for (int j = i + 1; j < nums.size(); j++) { + if (mask & (1 << j)) continue; + int newMask = mask | (1 << i) | (1 << j); + maxScore = max( + maxScore, + op * GCD[i][j] + dfs(newMask, op + 1, nums) + ); + } + } + return dp[mask] = maxScore; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxScore(nums) { + const n = nums.length; + const GCD = Array.from({ length: n }, () => Array(n).fill(0)); + const dp = Array(1 << n).fill(-1); + + const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b)); + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + GCD[i][j] = gcd(nums[i], nums[j]); + } + } + + const dfs = (mask, op) => { + if (dp[mask] !== -1) return dp[mask]; + + let maxScore = 0; + for (let i = 0; i < n; i++) { + if (mask & (1 << i)) continue; + for (let j = i + 1; j < n; j++) { + if (mask & (1 << j)) continue; + const newMask = mask | (1 << i) | (1 << j); + maxScore = Math.max( + maxScore, + op * GCD[i][j] + dfs(newMask, op + 1) + ); + } + } + return dp[mask] = maxScore; + }; + + return dfs(0, 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * (2 ^ n + \log m))$ +* Space complexity: $O(n ^ 2 + 2 ^ n)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum element in the array. + +--- + +## 4. Bitmask DP (Bottom-Up) + +::tabs-start + +```python +class Solution: + def maxScore(self, nums: List[int]) -> int: + n = len(nums) + N = 1 << n + GCD = [[0] * n for _ in range(n)] + for i in range(n): + for j in range(i + 1, n): + GCD[i][j] = gcd(nums[i], nums[j]) + + dp = [0] * N + for mask in range(N - 1, -1, -1): + bits = bin(mask).count('1') + if bits % 2 == 1: + continue + op = bits // 2 + 1 + + for i in range(n): + if mask & (1 << i): + continue + for j in range(i + 1, n): + if mask & (1 << j): + continue + new_mask = mask | (1 << i) | (1 << j) + dp[mask] = max(dp[mask], op * GCD[i][j] + dp[new_mask]) + + return dp[0] +``` + +```java +public class Solution { + public int maxScore(int[] nums) { + int n = nums.length; + int N = 1 << n; + int[][] GCD = new int[n][n]; + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + GCD[i][j] = gcd(nums[i], nums[j]); + } + } + + int[] dp = new int[N]; + for (int mask = N - 1; mask >= 0; mask--) { + int bits = Integer.bitCount(mask); + if (bits % 2 == 1) continue; + int op = bits / 2 + 1; + + for (int i = 0; i < n; i++) { + if ((mask & (1 << i)) != 0) continue; + for (int j = i + 1; j < n; j++) { + if ((mask & (1 << j)) != 0) continue; + int newMask = mask | (1 << i) | (1 << j); + dp[mask] = Math.max(dp[mask], op * GCD[i][j] + dp[newMask]); + } + } + } + return dp[0]; + } + + private int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } +} +``` + +```cpp +class Solution { +public: + int maxScore(vector& nums) { + int n = nums.size(); + int N = 1 << n; + vector> GCD(n, vector(n, 0)); + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + GCD[i][j] = __gcd(nums[i], nums[j]); + } + } + + vector dp(N, 0); + for (int mask = N - 1; mask >= 0; mask--) { + int bits = __builtin_popcount(mask); + if (bits % 2 == 1) continue; + int op = bits / 2 + 1; + + for (int i = 0; i < n; i++) { + if (mask & (1 << i)) continue; + for (int j = i + 1; j < n; j++) { + if (mask & (1 << j)) continue; + int newMask = mask | (1 << i) | (1 << j); + dp[mask] = max(dp[mask], op * GCD[i][j] + dp[newMask]); + } + } + } + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxScore(nums) { + const n = nums.length; + const N = 1 << n; + const GCD = Array.from({ length: n }, () => Array(n).fill(0)); + + const gcd = (a, b) => (b === 0 ? a : gcd(b, a % b)); + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + GCD[i][j] = gcd(nums[i], nums[j]); + } + } + + const dp = Array(N).fill(0); + for (let mask = N - 1; mask >= 0; mask--) { + let bits = mask.toString(2).split("0").join("").length; + if (bits % 2 === 1) continue; + let op = bits / 2 + 1; + + for (let i = 0; i < n; i++) { + if ((mask & (1 << i)) !== 0) continue; + for (let j = i + 1; j < n; j++) { + if ((mask & (1 << j)) !== 0) continue; + let newMask = mask | (1 << i) | (1 << j); + dp[mask] = Math.max(dp[mask], op * GCD[i][j] + dp[newMask]); + } + } + } + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * (2 ^ n + \log m))$ +* Space complexity: $O(n ^ 2 + 2 ^ n)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum element in the array. \ No newline at end of file diff --git a/articles/maximum-alternating-subsequence-sum.md b/articles/maximum-alternating-subsequence-sum.md new file mode 100644 index 000000000..0f296306c --- /dev/null +++ b/articles/maximum-alternating-subsequence-sum.md @@ -0,0 +1,351 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def maxAlternatingSum(self, nums: List[int]) -> int: + def dfs(i, even): + if i == len(nums): + return 0 + total = nums[i] if even else -nums[i] + return max(total + dfs(i + 1, not even), dfs(i + 1, even)) + + return dfs(0, True) +``` + +```java +public class Solution { + public long maxAlternatingSum(int[] nums) { + return dfs(nums, 0, true); + } + + private long dfs(int[] nums, int i, boolean even) { + if (i == nums.length) { + return 0; + } + long total = even ? nums[i] : -nums[i]; + return Math.max(total + dfs(nums, i + 1, !even), dfs(nums, i + 1, even)); + } +} +``` + +```cpp +class Solution { +public: + long long maxAlternatingSum(vector& nums) { + return dfs(nums, 0, true); + } + +private: + long long dfs(vector& nums, int i, bool even) { + if (i == nums.size()) { + return 0; + } + long long total = even ? nums[i] : -nums[i]; + return max(total + dfs(nums, i + 1, !even), dfs(nums, i + 1, even)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxAlternatingSum(nums) { + const dfs = (i, even) => { + if (i === nums.length) { + return 0; + } + + const total = even ? nums[i] : -nums[i]; + return Math.max(total + dfs(i + 1, !even), dfs(i + 1, even)); + }; + + return dfs(0, true); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def maxAlternatingSum(self, nums: List[int]) -> int: + dp = {} + + def dfs(i, even): + if i == len(nums): + return 0 + if (i, even) in dp: + return dp[(i, even)] + + total = nums[i] if even else -nums[i] + dp[(i, even)] = max(total + dfs(i + 1, not even), dfs(i + 1, even)) + return dp[(i, even)] + + return dfs(0, True) +``` + +```java +public class Solution { + private long dp[][]; + + public long maxAlternatingSum(int[] nums) { + int n = nums.length; + dp = new long[n][2]; + for (int i = 0; i < n; i++) { + dp[i][0] = -1; + dp[i][1] = -1; + } + return dfs(nums, 0, 1); + } + + private long dfs(int[] nums, int i, int even) { + if (i == nums.length) { + return 0; + } + if (dp[i][even] != -1) { + return dp[i][even]; + } + + long total = even == 1 ? nums[i] : -nums[i]; + dp[i][even] = Math.max(total + dfs(nums, i + 1, 1 - even), dfs(nums, i + 1, even)); + return dp[i][even]; + } +} +``` + +```cpp +class Solution { + vector> dp; + +public: + long long maxAlternatingSum(vector& nums) { + dp.assign(nums.size(), vector(2, -1)); + return dfs(nums, 0, true); + } + +private: + long long dfs(vector& nums, int i, bool even) { + if (i == nums.size()) { + return 0; + } + if (dp[i][even] != -1) { + return dp[i][even]; + } + long long total = even ? nums[i] : -nums[i]; + dp[i][even] = max(total + dfs(nums, i + 1, !even), dfs(nums, i + 1, even)); + return dp[i][even]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxAlternatingSum(nums) { + const n = nums.length; + const dp = Array.from({ length: n }, () => Array(2).fill(-1)); + + const dfs = (i, even) => { + if (i === n) { + return 0; + } + if (dp[i][even] !== -1) { + return dp[i][even]; + } + + const total = even === 1 ? nums[i] : -nums[i]; + dp[i][even] = Math.max(total + dfs(i + 1, 1 - even), dfs(i + 1, even)); + return dp[i][even]; + }; + + return dfs(0, 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def maxAlternatingSum(self, nums: List[int]) -> int: + n = len(nums) + dp = [[0] * 2 for _ in range(n + 1)] # dp[i][0] -> odd, dp[i][1] -> even + + for i in range(n - 1, -1, -1): + dp[i][1] = max(nums[i] + dp[i + 1][0], dp[i + 1][1]) # even + dp[i][0] = max(-nums[i] + dp[i + 1][1], dp[i + 1][0]) # odd + + return dp[0][1] +``` + +```java +public class Solution { + public long maxAlternatingSum(int[] nums) { + int n = nums.length; + long[][] dp = new long[n + 1][2]; // dp[i][0] -> odd, dp[i][1] -> even + + for (int i = n - 1; i >= 0; i--) { + dp[i][1] = Math.max(nums[i] + dp[i + 1][0], dp[i + 1][1]); // even + dp[i][0] = Math.max(-nums[i] + dp[i + 1][1], dp[i + 1][0]); // odd + } + + return dp[0][1]; + } +} +``` + +```cpp +class Solution { +public: + long long maxAlternatingSum(vector& nums) { + int n = nums.size(); + vector> dp(n + 1, vector(2, 0)); // dp[i][0] -> odd, dp[i][1] -> even + + for (int i = n - 1; i >= 0; i--) { + dp[i][1] = max(nums[i] + dp[i + 1][0], dp[i + 1][1]); // even + dp[i][0] = max(-nums[i] + dp[i + 1][1], dp[i + 1][0]); // odd + } + + return dp[0][1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxAlternatingSum(nums) { + const n = nums.length; + const dp = Array.from({ length: n + 1 }, () => [0, 0]); // dp[i][0] -> odd, dp[i][1] -> even + + for (let i = n - 1; i >= 0; i--) { + dp[i][1] = Math.max(nums[i] + dp[i + 1][0], dp[i + 1][1]); // even + dp[i][0] = Math.max(-nums[i] + dp[i + 1][1], dp[i + 1][0]); // odd + } + + return dp[0][1]; // Result starts with even index + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def maxAlternatingSum(self, nums: List[int]) -> int: + sumEven = sumOdd = 0 + + for i in range(len(nums) - 1, -1, -1): + tmpEven = max(sumOdd + nums[i], sumEven) + tmpOdd = max(sumEven - nums[i], sumOdd) + sumEven, sumOdd = tmpEven, tmpOdd + + return sumEven +``` + +```java +public class Solution { + public long maxAlternatingSum(int[] nums) { + long sumEven = 0, sumOdd = 0; + + for (int i = nums.length - 1; i >= 0; i--) { + long tmpEven = Math.max(nums[i] + sumOdd, sumEven); + long tmpOdd = Math.max(-nums[i] + sumEven, sumOdd); + sumEven = tmpEven; + sumOdd = tmpOdd; + } + + return sumEven; + } +} +``` + +```cpp +class Solution { +public: + long long maxAlternatingSum(vector& nums) { + long long sumEven = 0, sumOdd = 0; + + for (int i = nums.size() - 1; i >= 0; i--) { + long long tmpEven = max(nums[i] + sumOdd, sumEven); + long long tmpOdd = max(-nums[i] + sumEven, sumOdd); + sumEven = tmpEven; + sumOdd = tmpOdd; + } + + return sumEven; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxAlternatingSum(nums) { + let sumEven = 0, sumOdd = 0; + + for (let i = nums.length - 1; i >= 0; i--) { + let tmpEven = Math.max(nums[i] + sumOdd, sumEven); + let tmpOdd = Math.max(-nums[i] + sumEven, sumOdd); + sumEven = tmpEven; + sumOdd = tmpOdd; + } + + return sumEven; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/maximum-ascending-subarray-sum.md b/articles/maximum-ascending-subarray-sum.md new file mode 100644 index 000000000..5d4e47a60 --- /dev/null +++ b/articles/maximum-ascending-subarray-sum.md @@ -0,0 +1,173 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxAscendingSum(self, nums: List[int]) -> int: + res = 0 + for i in range(len(nums)): + curSum = nums[i] + for j in range(i + 1, len(nums)): + if nums[j] <= nums[j - 1]: + break + curSum += nums[j] + res = max(res, curSum) + return res +``` + +```java +public class Solution { + public int maxAscendingSum(int[] nums) { + int res = 0; + for (int i = 0; i < nums.length; i++) { + int curSum = nums[i]; + for (int j = i + 1; j < nums.length; j++) { + if (nums[j] <= nums[j - 1]) { + break; + } + curSum += nums[j]; + } + res = Math.max(res, curSum); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxAscendingSum(vector& nums) { + int res = 0; + for (int i = 0; i < nums.size(); i++) { + int curSum = nums[i]; + for (int j = i + 1; j < nums.size(); j++) { + if (nums[j] <= nums[j - 1]) { + break; + } + curSum += nums[j]; + } + res = max(res, curSum); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxAscendingSum(nums) { + let res = 0; + for (let i = 0; i < nums.length; i++) { + let curSum = nums[i]; + for (let j = i + 1; j < nums.length; j++) { + if (nums[j] <= nums[j - 1]) { + break; + } + curSum += nums[j]; + } + res = Math.max(res, curSum); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Iteration + +::tabs-start + +```python +class Solution: + def maxAscendingSum(self, nums: List[int]) -> int: + res = curSum= nums[0] + + for i in range(1, len(nums)): + if nums[i] <= nums[i - 1]: + curSum = 0 + + curSum += nums[i] + res = max(res, curSum) + + return res +``` + +```java +public class Solution { + public int maxAscendingSum(int[] nums) { + int res = nums[0], curSum = nums[0]; + + for (int i = 1; i < nums.length; i++) { + if (nums[i] <= nums[i - 1]) { + curSum = 0; + } + curSum += nums[i]; + res = Math.max(res, curSum); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxAscendingSum(vector& nums) { + int res = nums[0], curSum = nums[0]; + + for (int i = 1; i < nums.size(); i++) { + if (nums[i] <= nums[i - 1]) { + curSum = 0; + } + curSum += nums[i]; + res = max(res, curSum); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxAscendingSum(nums) { + let res = nums[0], curSum = nums[0]; + + for (let i = 1; i < nums.length; i++) { + if (nums[i] <= nums[i - 1]) { + curSum = 0; + } + curSum += nums[i]; + res = Math.max(res, curSum); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/maximum-element-after-decreasing-and-rearranging.md b/articles/maximum-element-after-decreasing-and-rearranging.md new file mode 100644 index 000000000..b7c01b0db --- /dev/null +++ b/articles/maximum-element-after-decreasing-and-rearranging.md @@ -0,0 +1,158 @@ +## 1. Greedy + Sorting + +::tabs-start + +```python +class Solution: + def maximumElementAfterDecrementingAndRearranging(self, arr: List[int]) -> int: + arr.sort() + prev = 0 + for num in arr: + prev = min(prev + 1, num) + return prev +``` + +```java +public class Solution { + public int maximumElementAfterDecrementingAndRearranging(int[] arr) { + Arrays.sort(arr); + int prev = 0; + for (int num : arr) { + prev = Math.min(prev + 1, num); + } + return prev; + } +} +``` + +```cpp +class Solution { +public: + int maximumElementAfterDecrementingAndRearranging(vector& arr) { + sort(arr.begin(), arr.end()); + int prev = 0; + for (int num : arr) { + prev = min(prev + 1, num); + } + return prev; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + maximumElementAfterDecrementingAndRearranging(arr) { + arr.sort((a, b) => a - b); + let prev = 0; + for (let num of arr) { + prev = Math.min(prev + 1, num); + } + return prev; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Greedy + +::tabs-start + +```python +class Solution: + def maximumElementAfterDecrementingAndRearranging(self, arr: List[int]) -> int: + n = len(arr) + count = [0] * (n + 1) + + for num in arr: + count[min(num, n)] += 1 + + prev = 1 + for num in range(2, n + 1): + prev = min(prev + count[num], num) + + return prev +``` + +```java +public class Solution { + public int maximumElementAfterDecrementingAndRearranging(int[] arr) { + int n = arr.length; + int[] count = new int[n + 1]; + + for (int num : arr) { + count[Math.min(num, n)]++; + } + + int prev = 1; + for (int num = 2; num <= n; num++) { + prev = Math.min(prev + count[num], num); + } + + return prev; + } +} +``` + +```cpp +class Solution { +public: + int maximumElementAfterDecrementingAndRearranging(vector& arr) { + int n = arr.size(); + vector count(n + 1, 0); + + for (int num : arr) { + count[min(num, n)]++; + } + + int prev = 1; + for (int num = 2; num <= n; num++) { + prev = min(prev + count[num], num); + } + + return prev; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + maximumElementAfterDecrementingAndRearranging(arr) { + let n = arr.length; + let count = new Array(n + 1).fill(0); + + for (let num of arr) { + count[Math.min(num, n)]++; + } + + let prev = 1; + for (let num = 2; num <= n; num++) { + prev = Math.min(prev + count[num], num); + } + + return prev; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/maximum-frequency-stack.md b/articles/maximum-frequency-stack.md new file mode 100644 index 000000000..317e2546c --- /dev/null +++ b/articles/maximum-frequency-stack.md @@ -0,0 +1,525 @@ +## 1. Brute Force + +::tabs-start + +```python +class FreqStack: + + def __init__(self): + self.cnt = defaultdict(int) + self.stack = [] + + def push(self, val: int) -> None: + self.stack.append(val) + self.cnt[val] += 1 + + def pop(self) -> int: + maxCnt = max(self.cnt.values()) + i = len(self.stack) - 1 + while self.cnt[self.stack[i]] != maxCnt: + i -= 1 + self.cnt[self.stack[i]] -= 1 + return self.stack.pop(i) +``` + +```java +public class FreqStack { + private Map cnt; + private List stack; + + public FreqStack() { + cnt = new HashMap<>(); + stack = new ArrayList<>(); + } + + public void push(int val) { + stack.add(val); + cnt.put(val, cnt.getOrDefault(val, 0) + 1); + } + + public int pop() { + int maxCnt = Collections.max(cnt.values()); + int i = stack.size() - 1; + while (cnt.get(stack.get(i)) != maxCnt) { + i--; + } + int val = stack.remove(i); + cnt.put(val, cnt.get(val) - 1); + return val; + } +} +``` + +```cpp +class FreqStack { +private: + unordered_map cnt; + vector stack; + +public: + FreqStack() {} + + void push(int val) { + stack.push_back(val); + cnt[val]++; + } + + int pop() { + int maxCnt = 0; + for (auto& [_, frequency] : cnt) { + maxCnt = max(maxCnt, frequency); + } + int i = stack.size() - 1; + while (cnt[stack[i]] != maxCnt) { + i--; + } + int val = stack[i]; + stack.erase(stack.begin() + i); + cnt[val]--; + return val; + } +}; +``` + +```javascript +class FreqStack { + constructor() { + this.cnt = new Map(); + this.stack = []; + } + + /** + * @param {number} val + * @return {void} + */ + push(val) { + this.stack.push(val); + this.cnt.set(val, (this.cnt.get(val) || 0) + 1); + } + + /** + * @return {number} + */ + pop() { + const maxCnt = Math.max(...this.cnt.values()); + let i = this.stack.length - 1; + while (this.cnt.get(this.stack[i]) !== maxCnt) { + i--; + } + const val = this.stack.splice(i, 1)[0]; + this.cnt.set(val, this.cnt.get(val) - 1); + return val; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for each $push()$ function call. + * $O(n)$ time for each $pop()$ function call. +* Space complexity: $O(n)$ + +> Where $n$ is the number of elements in the stack. + +--- + +## 2. Heap + +::tabs-start + +```python +class FreqStack: + + def __init__(self): + self.heap = [] + self.cnt = defaultdict(int) + self.index = 0 + + def push(self, val: int) -> None: + self.cnt[val] += 1 + heapq.heappush(self.heap, (-self.cnt[val], -self.index, val)) + self.index += 1 + + def pop(self) -> int: + _, _, val = heapq.heappop(self.heap) + self.cnt[val] -= 1 + return val +``` + +```java +public class FreqStack { + private PriorityQueue heap; + private Map cnt; + private int index; + + public FreqStack() { + heap = new PriorityQueue<>((a, b) -> + a[0] != b[0] ? Integer.compare(b[0], a[0]) : Integer.compare(b[1], a[1]) + ); + cnt = new HashMap<>(); + index = 0; + } + + public void push(int val) { + cnt.put(val, cnt.getOrDefault(val, 0) + 1); + heap.offer(new int[]{cnt.get(val), index++, val}); + } + + public int pop() { + int[] top = heap.poll(); + int val = top[2]; + cnt.put(val, cnt.get(val) - 1); + return val; + } +} +``` + +```cpp +class FreqStack { +private: + priority_queue> heap; // {frequency, index, value} + unordered_map cnt; + int index; + +public: + FreqStack() : index(0) {} + + void push(int val) { + cnt[val]++; + heap.push({cnt[val], index++, val}); + } + + int pop() { + auto top = heap.top(); + heap.pop(); + int val = top[2]; + cnt[val]--; + return val; + } +}; +``` + +```javascript +class FreqStack { + constructor() { + this.heap = new MaxPriorityQueue({ + priority: (element) => element[0] * 100000 + element[1], + }); + this.cnt = new Map(); + this.index = 0; + } + + /** + * @param {number} val + * @return {void} + */ + push(val) { + this.cnt.set(val, (this.cnt.get(val) || 0) + 1); + this.heap.enqueue([this.cnt.get(val), this.index++, val]); + } + + /** + * @return {number} + */ + pop() { + const [, , val] = this.heap.dequeue().element; + this.cnt.set(val, this.cnt.get(val) - 1); + return val; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(\log n)$ time for each $push()$ function call. + * $O(\log n)$ time for each $pop()$ function call. +* Space complexity: $O(n)$ + +> Where $n$ is the number of elements in the stack. + +--- + +## 3. Stack Of Stacks (Hash Map) + +::tabs-start + +```python +class FreqStack: + + def __init__(self): + self.cnt = {} + self.maxCnt = 0 + self.stacks = {} + + def push(self, val: int) -> None: + valCnt = 1 + self.cnt.get(val, 0) + self.cnt[val] = valCnt + if valCnt > self.maxCnt: + self.maxCnt = valCnt + self.stacks[valCnt] = [] + self.stacks[valCnt].append(val) + + def pop(self) -> int: + res = self.stacks[self.maxCnt].pop() + self.cnt[res] -= 1 + if not self.stacks[self.maxCnt]: + self.maxCnt -= 1 + return res +``` + +```java +class FreqStack { + private Map cnt; + private Map> stacks; + private int maxCnt; + + public FreqStack() { + cnt = new HashMap<>(); + stacks = new HashMap<>(); + maxCnt = 0; + } + + public void push(int val) { + int valCnt = cnt.getOrDefault(val, 0) + 1; + cnt.put(val, valCnt); + if (valCnt > maxCnt) { + maxCnt = valCnt; + stacks.putIfAbsent(valCnt, new Stack<>()); + } + stacks.get(valCnt).push(val); + } + + public int pop() { + int res = stacks.get(maxCnt).pop(); + cnt.put(res, cnt.get(res) - 1); + if (stacks.get(maxCnt).isEmpty()) { + maxCnt--; + } + return res; + } +} +``` + +```cpp +class FreqStack { +public: + unordered_map cnt; + unordered_map> stacks; + int maxCnt; + + FreqStack() { + maxCnt = 0; + } + + void push(int val) { + int valCnt = ++cnt[val]; + if (valCnt > maxCnt) { + maxCnt = valCnt; + stacks[valCnt] = stack(); + } + stacks[valCnt].push(val); + } + + int pop() { + int res = stacks[maxCnt].top(); + stacks[maxCnt].pop(); + cnt[res]--; + if (stacks[maxCnt].empty()) { + maxCnt--; + } + return res; + } +}; +``` + +```javascript +class FreqStack { + constructor() { + this.cnt = new Map(); + this.stacks = new Map(); + this.maxCnt = 0; + } + + /** + * @param {number} val + * @return {void} + */ + push(val) { + const valCnt = (this.cnt.get(val) || 0) + 1; + this.cnt.set(val, valCnt); + if (valCnt > this.maxCnt) { + this.maxCnt = valCnt; + if (!this.stacks.has(valCnt)) { + this.stacks.set(valCnt, []); + } + } + this.stacks.get(valCnt).push(val); + } + + /** + * @return {number} + */ + pop() { + const res = this.stacks.get(this.maxCnt).pop(); + this.cnt.set(res, this.cnt.get(res) - 1); + if (this.stacks.get(this.maxCnt).length === 0) { + this.maxCnt--; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for each $push()$ function call. + * $O(1)$ time for each $pop()$ function call. +* Space complexity: $O(n)$ + +> Where $n$ is the number of elements in the stack. + +--- + +## 4. Stack Of Stacks (Dynamic Array) + +::tabs-start + +```python +class FreqStack: + + def __init__(self): + self.cnt = {} + self.stacks = [[]] + + def push(self, val: int) -> None: + valCnt = 1 + self.cnt.get(val, 0) + self.cnt[val] = valCnt + if valCnt == len(self.stacks): + self.stacks.append([]) + self.stacks[valCnt].append(val) + + def pop(self) -> int: + res = self.stacks[-1].pop() + self.cnt[res] -= 1 + if not self.stacks[-1]: + self.stacks.pop() + return res +``` + +```java +public class FreqStack { + private Map cnt; + private List> stacks; + + public FreqStack() { + cnt = new HashMap<>(); + stacks = new ArrayList<>(); + stacks.add(new Stack<>()); + } + + public void push(int val) { + int valCnt = cnt.getOrDefault(val, 0) + 1; + cnt.put(val, valCnt); + if (valCnt == stacks.size()) { + stacks.add(new Stack<>()); + } + stacks.get(valCnt).push(val); + } + + public int pop() { + Stack topStack = stacks.get(stacks.size() - 1); + int res = topStack.pop(); + cnt.put(res, cnt.get(res) - 1); + if (topStack.isEmpty()) { + stacks.remove(stacks.size() - 1); + } + return res; + } +} +``` + +```cpp +class FreqStack { +public: + unordered_map cnt; + vector> stacks; + + FreqStack() { + stacks.push_back(stack()); + } + + void push(int val) { + int valCnt = ++cnt[val]; + if (valCnt == stacks.size()) { + stacks.push_back(stack()); + } + stacks[valCnt].push(val); + } + + int pop() { + stack& topStack = stacks.back(); + int res = topStack.top(); + topStack.pop(); + if (topStack.empty()) { + stacks.pop_back(); + } + cnt[res]--; + return res; + } +}; +``` + +```javascript +class FreqStack { + constructor() { + this.cnt = new Map(); + this.stacks = [[]]; + } + + /** + * @param {number} val + * @return {void} + */ + push(val) { + const valCnt = (this.cnt.get(val) || 0) + 1; + this.cnt.set(val, valCnt); + if (valCnt === this.stacks.length) { + this.stacks.push([]); + } + this.stacks[valCnt].push(val); + } + + /** + * @return {number} + */ + pop() { + const topStack = this.stacks[this.stacks.length - 1]; + const res = topStack.pop(); + this.cnt.set(res, this.cnt.get(res) - 1); + if (topStack.length === 0) { + this.stacks.pop(); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for each $push()$ function call. + * $O(1)$ time for each $pop()$ function call. +* Space complexity: $O(n)$ + +> Where $n$ is the number of elements in the stack. \ No newline at end of file diff --git a/articles/maximum-length-of-a-concatenated-string-with-unique-characters.md b/articles/maximum-length-of-a-concatenated-string-with-unique-characters.md new file mode 100644 index 000000000..f8cdea423 --- /dev/null +++ b/articles/maximum-length-of-a-concatenated-string-with-unique-characters.md @@ -0,0 +1,857 @@ +## 1. Backtracking (Hash Set) + +::tabs-start + +```python +class Solution: + def maxLength(self, arr: List[str]) -> int: + charSet = set() + + def overlap(charSet, s): + prev = set() + for c in s: + if c in charSet or c in prev: + return True + prev.add(c) + return False + + def backtrack(i): + if i == len(arr): + return len(charSet) + + res = 0 + if not overlap(charSet, arr[i]): + for c in arr[i]: + charSet.add(c) + res = backtrack(i + 1) + for c in arr[i]: + charSet.remove(c) + + return max(res, backtrack(i + 1)) + + return backtrack(0) +``` + +```java +public class Solution { + public int maxLength(List arr) { + Set charSet = new HashSet<>(); + return backtrack(0, arr, charSet); + } + + private boolean overlap(Set charSet, String s) { + Set prev = new HashSet<>(); + for (char c : s.toCharArray()) { + if (charSet.contains(c) || prev.contains(c)) { + return true; + } + prev.add(c); + } + return false; + } + + private int backtrack(int i, List arr, Set charSet) { + if (i == arr.size()) { + return charSet.size(); + } + + int res = 0; + if (!overlap(charSet, arr.get(i))) { + for (char c : arr.get(i).toCharArray()) { + charSet.add(c); + } + res = backtrack(i + 1, arr, charSet); + for (char c : arr.get(i).toCharArray()) { + charSet.remove(c); + } + } + + return Math.max(res, backtrack(i + 1, arr, charSet)); + } +} +``` + +```cpp +class Solution { +public: + int maxLength(vector& arr) { + unordered_set charSet; + return backtrack(0, arr, charSet); + } + +private: + bool overlap(unordered_set& charSet, const string& s) { + unordered_set prev; + for (char c : s) { + if (charSet.count(c) || prev.count(c)) { + return true; + } + prev.insert(c); + } + return false; + } + + int backtrack(int i, vector& arr, unordered_set& charSet) { + if (i == arr.size()) { + return charSet.size(); + } + + int res = 0; + if (!overlap(charSet, arr[i])) { + for (char c : arr[i]) { + charSet.insert(c); + } + res = backtrack(i + 1, arr, charSet); + for (char c : arr[i]) { + charSet.erase(c); + } + } + + return max(res, backtrack(i + 1, arr, charSet)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} arr + * @return {number} + */ + maxLength(arr) { + let charSet = new Set(); + + const overlap = (charSet, s) => { + let prev = new Set(); + for (const c of s) { + if (charSet.has(c) || prev.has(c)) { + return true; + } + prev.add(c); + } + return false; + }; + + const backtrack = (i) => { + if (i === arr.length) { + return charSet.size; + } + + let res = 0; + if (!overlap(charSet, arr[i])) { + for (const c of arr[i]) { + charSet.add(c); + } + res = backtrack(i + 1); + for (const c of arr[i]) { + charSet.delete(c); + } + } + + return Math.max(res, backtrack(i + 1)); + }; + + return backtrack(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * 2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +> Where $n$ is the number of strings and $m$ is the maximum length of a string. + +--- + +## 2. Backtracking (Boolean Array) + +::tabs-start + +```python +class Solution: + def maxLength(self, arr: List[str]) -> int: + charSet = [False] * 26 + + def getIdx(c): + return ord(c) - ord('a') + + def overlap(charSet, s): + for i in range(len(s)): + c = getIdx(s[i]) + if charSet[c]: + for j in range(i): + charSet[getIdx(s[j])] = False + return True + charSet[c] = True + return False + + def backtrack(i): + if i == len(arr): + return 0 + + res = 0 + if not overlap(charSet, arr[i]): + res = len(arr[i]) + backtrack(i + 1) + for c in arr[i]: + charSet[getIdx(c)] = False + return max(res, backtrack(i + 1)) + + return backtrack(0) +``` + +```java +public class Solution { + public int maxLength(List arr) { + boolean[] charSet = new boolean[26]; + return backtrack(0, arr, charSet); + } + + private int getIdx(char c) { + return c - 'a'; + } + + private boolean overlap(boolean[] charSet, String s) { + for (int i = 0; i < s.length(); i++) { + int c = getIdx(s.charAt(i)); + if (charSet[c]) { + for (int j = 0; j < i; j++) { + charSet[getIdx(s.charAt(j))] = false; + } + return true; + } + charSet[c] = true; + } + return false; + } + + private int backtrack(int i, List arr, boolean[] charSet) { + if (i == arr.size()) { + return 0; + } + + int res = 0; + if (!overlap(charSet, arr.get(i))) { + res = arr.get(i).length() + backtrack(i + 1, arr, charSet); + for (char c : arr.get(i).toCharArray()) { + charSet[getIdx(c)] = false; + } + } + return Math.max(res, backtrack(i + 1, arr, charSet)); + } +} +``` + +```cpp +class Solution { +public: + int maxLength(vector& arr) { + bool charSet[26] = {false}; + return backtrack(0, arr, charSet); + } + +private: + int getIdx(char c) { + return c - 'a'; + } + + bool overlap(bool charSet[], const string& s) { + for (int i = 0; i < s.length(); i++) { + int c = getIdx(s[i]); + if (charSet[c]) { + for (int j = 0; j < i; j++) { + charSet[getIdx(s[j])] = false; + } + return true; + } + charSet[c] = true; + } + return false; + } + + int backtrack(int i, vector& arr, bool charSet[]) { + if (i == arr.size()) { + return 0; + } + + int res = 0; + if (!overlap(charSet, arr[i])) { + res = arr[i].length() + backtrack(i + 1, arr, charSet); + for (char c : arr[i]) { + charSet[getIdx(c)] = false; + } + } + return max(res, backtrack(i + 1, arr, charSet)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} arr + * @return {number} + */ + maxLength(arr) { + let charSet = new Array(26).fill(false); + + const getIdx = (c) => c.charCodeAt(0) - 'a'.charCodeAt(0); + + const overlap = (charSet, s) => { + for (let i = 0; i < s.length; i++) { + let c = getIdx(s[i]); + if (charSet[c]) { + for (let j = 0; j < i; j++) { + charSet[getIdx(s[j])] = false; + } + return true; + } + charSet[c] = true; + } + return false; + }; + + const backtrack = (i) => { + if (i === arr.length) { + return 0; + } + + let res = 0; + if (!overlap(charSet, arr[i])) { + res = arr[i].length + backtrack(i + 1); + for (const c of arr[i]) { + charSet[getIdx(c)] = false; + } + } + return Math.max(res, backtrack(i + 1)); + }; + + return backtrack(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * 2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +> Where $n$ is the number of strings and $m$ is the maximum length of a string. + +--- + +## 3. Recursion (Bit Mask) - I + +::tabs-start + +```python +class Solution: + def maxLength(self, arr: List[str]) -> int: + def getIdx(c): + return ord(c) - ord('a') + + A = [] + for s in arr: + cur = 0 + valid = True + for c in s: + if cur & (1 << getIdx(c)): + valid = False + break + cur |= (1 << getIdx(c)) + + if valid: + A.append([cur, len(s)]) + + def dfs(i, subSeq): + if i == len(A): + return 0 + + res = dfs(i + 1, subSeq) + + curSeq, length = A[i][0], A[i][1] + if (subSeq & curSeq) == 0: + res = max(res, length + dfs(i + 1, subSeq | curSeq)) + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + public int maxLength(List arr) { + List A = new ArrayList<>(); + + for (String s : arr) { + int cur = 0; + boolean valid = true; + + for (char c : s.toCharArray()) { + if ((cur & (1 << (c - 'a'))) != 0) { + valid = false; + break; + } + cur |= (1 << (c - 'a')); + } + + if (valid) { + A.add(new int[]{cur, s.length()}); + } + } + + return dfs(0, 0, A); + } + + private int dfs(int i, int subSeq, List A) { + if (i == A.size()) { + return 0; + } + + int res = dfs(i + 1, subSeq, A); + + int curSeq = A.get(i)[0], length = A.get(i)[1]; + if ((subSeq & curSeq) == 0) { + res = Math.max(res, length + dfs(i + 1, subSeq | curSeq, A)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxLength(vector& arr) { + vector> A; + + for (const string& s : arr) { + int cur = 0; + bool valid = true; + + for (char c : s) { + if (cur & (1 << (c - 'a'))) { + valid = false; + break; + } + cur |= (1 << (c - 'a')); + } + + if (valid) { + A.emplace_back(cur, s.length()); + } + } + + return dfs(0, 0, A); + } + +private: + int dfs(int i, int subSeq, vector>& A) { + if (i == A.size()) { + return 0; + } + + int res = dfs(i + 1, subSeq, A); + + int curSeq = A[i].first, length = A[i].second; + if ((subSeq & curSeq) == 0) { + res = max(res, length + dfs(i + 1, subSeq | curSeq, A)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} arr + * @return {number} + */ + maxLength(arr) { + let A = []; + + for (const s of arr) { + let cur = 0; + let valid = true; + + for (const c of s) { + if (cur & (1 << (c.charCodeAt(0) - 97))) { + valid = false; + break; + } + cur |= (1 << (c.charCodeAt(0) - 97)); + } + + if (valid) { + A.push([cur, s.length]); + } + } + + const dfs = (i, subSeq) => { + if (i === A.length) { + return 0; + } + + let res = dfs(i + 1, subSeq); + + let [curSeq, length] = A[i]; + if ((subSeq & curSeq) === 0) { + res = Math.max(res, length + dfs(i + 1, subSeq | curSeq)); + } + return res; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n + 2 ^ n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of strings and $m$ is the maximum length of a string. + +--- + +## 4. Recursion (Bit Mask) - II + +::tabs-start + +```python +class Solution: + def maxLength(self, arr: List[str]) -> int: + def getIdx(c): + return ord(c) - ord('a') + + A = [] + for s in arr: + cur = 0 + valid = True + for c in s: + if cur & (1 << getIdx(c)): + valid = False + break + cur |= (1 << getIdx(c)) + + if valid: + A.append([cur, len(s)]) + + def dfs(i, subSeq): + res = 0 + for j in range(i, len(A)): + curSeq, length = A[j][0], A[j][1] + if (subSeq & curSeq) == 0: + res = max(res, length + dfs(j + 1, subSeq | curSeq)) + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + public int maxLength(List arr) { + List A = new ArrayList<>(); + + for (String s : arr) { + int cur = 0; + boolean valid = true; + + for (char c : s.toCharArray()) { + if ((cur & (1 << (c - 'a'))) != 0) { + valid = false; + break; + } + cur |= (1 << (c - 'a')); + } + + if (valid) { + A.add(new int[]{cur, s.length()}); + } + } + + return dfs(0, 0, A); + } + + private int dfs(int i, int subSeq, List A) { + int res = 0; + for (int j = i; j < A.size(); j++) { + int curSeq = A.get(j)[0], length = A.get(j)[1]; + if ((subSeq & curSeq) == 0) { + res = Math.max(res, length + dfs(j + 1, subSeq | curSeq, A)); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxLength(vector& arr) { + vector> A; + + for (const string& s : arr) { + int cur = 0; + bool valid = true; + + for (char c : s) { + if (cur & (1 << (c - 'a'))) { + valid = false; + break; + } + cur |= (1 << (c - 'a')); + } + + if (valid) { + A.emplace_back(cur, s.length()); + } + } + + return dfs(0, 0, A); + } + +private: + int dfs(int i, int subSeq, vector>& A) { + int res = 0; + for (int j = i; j < A.size(); j++) { + int curSeq = A[j].first, length = A[j].second; + if ((subSeq & curSeq) == 0) { + res = max(res, length + dfs(j + 1, subSeq | curSeq, A)); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} arr + * @return {number} + */ + maxLength(arr) { + let A = []; + + for (const s of arr) { + let cur = 0; + let valid = true; + + for (const c of s) { + if (cur & (1 << (c.charCodeAt(0) - 97))) { + valid = false; + break; + } + cur |= (1 << (c.charCodeAt(0) - 97)); + } + + if (valid) { + A.push([cur, s.length]); + } + } + + const dfs = (i, subSeq) => { + let res = 0; + for (let j = i; j < A.length; j++) { + let [curSeq, length] = A[j]; + if ((subSeq & curSeq) === 0) { + res = Math.max(res, length + dfs(j + 1, subSeq | curSeq)); + } + } + return res; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * (m + 2 ^ n))$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of strings and $m$ is the maximum length of a string. + +--- + +## 5. Dynamic Programming + +::tabs-start + +```python +class Solution: + def maxLength(self, arr: List[str]) -> int: + dp = {0} + res = 0 + + for s in arr: + cur = 0 + valid = True + + for c in s: + bit = 1 << (ord(c) - ord('a')) + if cur & bit: + valid = False + break + cur |= bit + + if not valid: + continue + + next_dp = dp.copy() + for seq in dp: + if (seq & cur) or (seq | cur) in dp: + continue + next_dp.add(seq | cur) + res = max(res, bin(seq | cur).count('1')) + dp = next_dp + + return res +``` + +```java +public class Solution { + public int maxLength(List arr) { + Set dp = new HashSet<>(); + dp.add(0); + int res = 0; + + for (String s : arr) { + int cur = 0; + boolean valid = true; + + for (char c : s.toCharArray()) { + int bit = 1 << (c - 'a'); + if ((cur & bit) != 0) { + valid = false; + break; + } + cur |= bit; + } + + if (!valid) { + continue; + } + + Set next_dp = new HashSet<>(dp); + for (int seq : dp) { + if ((seq & cur) != 0 || next_dp.contains(seq | cur)) { + continue; + } + next_dp.add(seq | cur); + res = Math.max(res, Integer.bitCount(seq | cur)); + } + dp = next_dp; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxLength(vector& arr) { + unordered_set dp; + dp.insert(0); + int res = 0; + + for (const string& s : arr) { + int cur = 0; + bool valid = true; + + for (char c : s) { + int bit = 1 << (c - 'a'); + if (cur & bit) { + valid = false; + break; + } + cur |= bit; + } + + if (!valid) { + continue; + } + + unordered_set next_dp(dp); + for (int seq : dp) { + if ((seq & cur) || next_dp.count(seq | cur)) { + continue; + } + next_dp.insert(seq | cur); + res = max(res, __builtin_popcount(seq | cur)); + } + dp = next_dp; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} arr + * @return {number} + */ + maxLength(arr) { + let dp = new Set(); + dp.add(0); + let res = 0; + + for (const s of arr) { + let cur = 0; + let valid = true; + + for (const c of s) { + let bit = 1 << (c.charCodeAt(0) - 97); + if (cur & bit) { + valid = false; + break; + } + cur |= bit; + } + + if (!valid) { + continue; + } + + let next_dp = new Set(dp); + for (let seq of dp) { + if ((seq & cur) || next_dp.has(seq | cur)) { + continue; + } + next_dp.add(seq | cur); + res = Math.max(res, (seq | cur).toString(2).split('0').join('').length); + } + dp = next_dp; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * (m + 2 ^ n))$ +* Space complexity: $O(2 ^ n)$ + +> Where $n$ is the number of strings and $m$ is the maximum length of a string. \ No newline at end of file diff --git a/articles/maximum-length-of-pair-chain.md b/articles/maximum-length-of-pair-chain.md new file mode 100644 index 000000000..2855ede51 --- /dev/null +++ b/articles/maximum-length-of-pair-chain.md @@ -0,0 +1,556 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def findLongestChain(self, pairs: List[List[int]]) -> int: + n = len(pairs) + pairs.sort(key=lambda x: x[1]) + + def dfs(i, j): + if i == n: + return 0 + + res = dfs(i + 1, j) + if j == -1 or pairs[j][1] < pairs[i][0]: + res = max(res, 1 + dfs(i + 1, i)) + + return res + + return dfs(0, -1) +``` + +```java +public class Solution { + public int findLongestChain(int[][] pairs) { + int n = pairs.length; + Arrays.sort(pairs, (a, b) -> Integer.compare(a[1], b[1])); + return dfs(pairs, 0, -1, n); + } + + private int dfs(int[][] pairs, int i, int j, int n) { + if (i == n) { + return 0; + } + + int res = dfs(pairs, i + 1, j, n); + if (j == -1 || pairs[j][1] < pairs[i][0]) { + res = Math.max(res, 1 + dfs(pairs, i + 1, i, n)); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int findLongestChain(vector>& pairs) { + int n = pairs.size(); + sort(pairs.begin(), pairs.end(), [](const auto& a, const auto& b) { + return a[1] < b[1]; + }); + + return dfs(pairs, 0, -1, n); + } + +private: + int dfs(vector>& pairs, int i, int j, int n) { + if (i == n) { + return 0; + } + + int res = dfs(pairs, i + 1, j, n); + if (j == -1 || pairs[j][1] < pairs[i][0]) { + res = max(res, 1 + dfs(pairs, i + 1, i, n)); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} pairs + * @return {number} + */ + findLongestChain(pairs) { + pairs.sort((a, b) => a[1] - b[1]); + let n = pairs.length; + + const dfs = (i, j) => { + if (i === n) { + return 0; + } + + let res = dfs(i + 1, j); + if (j === -1 || pairs[j][1] < pairs[i][0]) { + res = Math.max(res, 1 + dfs(i + 1, i)); + } + + return res; + }; + + return dfs(0, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def findLongestChain(self, pairs: List[List[int]]) -> int: + n = len(pairs) + pairs.sort(key=lambda x: x[1]) + dp = [[-1] * (n + 1) for _ in range(n)] + + def dfs(i, j): + if i == n: + return 0 + if dp[i][j + 1] != -1: + return dp[i][j + 1] + + res = dfs(i + 1, j) + if j == -1 or pairs[j][1] < pairs[i][0]: + res = max(res, 1 + dfs(i + 1, i)) + + dp[i][j + 1] = res + return res + + return dfs(0, -1) +``` + +```java +public class Solution { + private int[][] dp; + + public int findLongestChain(int[][] pairs) { + int n = pairs.length; + Arrays.sort(pairs, (a, b) -> Integer.compare(a[1], b[1])); + dp = new int[n][n + 1]; + + for (int[] row : dp) { + Arrays.fill(row, -1); + } + + return dfs(pairs, 0, -1, n); + } + + private int dfs(int[][] pairs, int i, int j, int n) { + if (i == n) { + return 0; + } + if (dp[i][j + 1] != -1) { + return dp[i][j + 1]; + } + + int res = dfs(pairs, i + 1, j, n); + if (j == -1 || pairs[j][1] < pairs[i][0]) { + res = Math.max(res, 1 + dfs(pairs, i + 1, i, n)); + } + + dp[i][j + 1] = res; + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> dp; + + int findLongestChain(vector>& pairs) { + int n = pairs.size(); + sort(pairs.begin(), pairs.end(), [](const auto& a, const auto& b) { + return a[1] < b[1]; + }); + + dp = vector>(n, vector(n + 1, -1)); + return dfs(pairs, 0, -1, n); + } + +private: + int dfs(vector>& pairs, int i, int j, int n) { + if (i == n) { + return 0; + } + if (dp[i][j + 1] != -1) { + return dp[i][j + 1]; + } + + int res = dfs(pairs, i + 1, j, n); + if (j == -1 || pairs[j][1] < pairs[i][0]) { + res = max(res, 1 + dfs(pairs, i + 1, i, n)); + } + + dp[i][j + 1] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} pairs + * @return {number} + */ + findLongestChain(pairs) { + pairs.sort((a, b) => a[1] - b[1]); + let n = pairs.length; + let dp = Array.from({ length: n }, () => new Array(n + 1).fill(-1)); + + const dfs = (i, j) => { + if (i === n) { + return 0; + } + if (dp[i][j + 1] !== -1) { + return dp[i][j + 1]; + } + + let res = dfs(i + 1, j); + if (j === -1 || pairs[j][1] < pairs[i][0]) { + res = Math.max(res, 1 + dfs(i + 1, i)); + } + + dp[i][j + 1] = res; + return res; + }; + + return dfs(0, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def findLongestChain(self, pairs: List[List[int]]) -> int: + n = len(pairs) + pairs.sort(key=lambda x: x[1]) + dp = [1] * n + + for i in range(n): + for j in range(i): + if pairs[j][1] < pairs[i][0]: + dp[i] = max(dp[i], dp[j] + 1) + + return max(dp) +``` + +```java +public class Solution { + public int findLongestChain(int[][] pairs) { + int n = pairs.length; + Arrays.sort(pairs, (a, b) -> Integer.compare(a[1], b[1])); + int[] dp = new int[n]; + Arrays.fill(dp, 1); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + if (pairs[j][1] < pairs[i][0]) { + dp[i] = Math.max(dp[i], dp[j] + 1); + } + } + } + + return Arrays.stream(dp).max().getAsInt(); + } +} +``` + +```cpp +class Solution { +public: + int findLongestChain(vector>& pairs) { + int n = pairs.size(); + sort(pairs.begin(), pairs.end(), [](const auto& a, const auto& b) { + return a[1] < b[1]; + }); + + vector dp(n, 1); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + if (pairs[j][1] < pairs[i][0]) { + dp[i] = max(dp[i], dp[j] + 1); + } + } + } + + return *max_element(dp.begin(), dp.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} pairs + * @return {number} + */ + findLongestChain(pairs) { + let n = pairs.length; + pairs.sort((a, b) => a[1] - b[1]); + let dp = new Array(n).fill(1); + + for (let i = 0; i < n; i++) { + for (let j = 0; j < i; j++) { + if (pairs[j][1] < pairs[i][0]) { + dp[i] = Math.max(dp[i], dp[j] + 1); + } + } + } + + return Math.max(...dp); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Bianry Search) + +::tabs-start + +```python +class Solution: + def findLongestChain(self, pairs: List[List[int]]) -> int: + pairs.sort(key=lambda x: x[0]) + dp = [] + + for a, b in pairs: + pos = bisect_left(dp, a) + if pos == len(dp): + dp.append(b) + else: + dp[pos] = min(dp[pos], b) + + return len(dp) +``` + +```java +public class Solution { + public int findLongestChain(int[][] pairs) { + Arrays.sort(pairs, Comparator.comparingInt(a -> a[0])); + List dp = new ArrayList<>(); + + for (int[] pair : pairs) { + int pos = binarySearch(dp, pair[0]); + if (pos == dp.size()) { + dp.add(pair[1]); + } else { + dp.set(pos, Math.min(dp.get(pos), pair[1])); + } + } + + return dp.size(); + } + + private int binarySearch(List dp, int target) { + int left = 0, right = dp.size() - 1; + while (left <= right) { + int mid = left + (right - left) / 2; + if (dp.get(mid) < target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return left; + } +} +``` + +```cpp +class Solution { +public: + int findLongestChain(vector>& pairs) { + sort(pairs.begin(), pairs.end()); + vector dp; + + for (auto& pair : pairs) { + auto it = lower_bound(dp.begin(), dp.end(), pair[0]); + if (it == dp.end()) { + dp.push_back(pair[1]); + } else { + *it = min(*it, pair[1]); + } + } + + return dp.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} pairs + * @return {number} + */ + findLongestChain(pairs) { + pairs.sort((a, b) => a[0] - b[0]); + let dp = []; + + const binarySearch = (target) => { + let left = 0, right = dp.length - 1; + while (left <= right) { + let mid = Math.floor((left + right) / 2); + if (dp[mid] < target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return left; + }; + + for (let i = 0; i < pairs.length; i++) { + let pos = binarySearch(pairs[i][0]); + if (pos === dp.length) { + dp.push(pairs[i][1]); + } else { + dp[pos] = Math.min(dp[pos], pairs[i][1]); + } + } + + return dp.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Greedy + +::tabs-start + +```python +class Solution: + def findLongestChain(self, pairs: List[List[int]]) -> int: + pairs.sort(key=lambda p: p[1]) + length = 1 + end = pairs[0][1] + + for i in range(1, len(pairs)): + if end < pairs[i][0]: + length += 1 + end = pairs[i][1] + + return length +``` + +```java +public class Solution { + public int findLongestChain(int[][] pairs) { + Arrays.sort(pairs, (a, b) -> Integer.compare(a[1], b[1])); + int length = 1; + int end = pairs[0][1]; + + for (int i = 1; i < pairs.length; i++) { + if (end < pairs[i][0]) { + length++; + end = pairs[i][1]; + } + } + + return length; + } +} +``` + +```cpp +class Solution { +public: + int findLongestChain(vector>& pairs) { + sort(pairs.begin(), pairs.end(), [](const auto& a, const auto& b) { + return a[1] < b[1]; + }); + + int length = 1, end = pairs[0][1]; + + for (int i = 1; i < pairs.size(); i++) { + if (end < pairs[i][0]) { + length++; + end = pairs[i][1]; + } + } + + return length; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} pairs + * @return {number} + */ + findLongestChain(pairs) { + pairs.sort((a, b) => a[1] - b[1]); + let length = 1; + let end = pairs[0][1]; + + for (let i = 1; i < pairs.length; i++) { + if (end < pairs[i][0]) { + length++; + end = pairs[i][1]; + } + } + + return length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file diff --git a/articles/maximum-nesting-depth-of-the-parentheses.md b/articles/maximum-nesting-depth-of-the-parentheses.md new file mode 100644 index 000000000..f18d24a7f --- /dev/null +++ b/articles/maximum-nesting-depth-of-the-parentheses.md @@ -0,0 +1,305 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def maxDepth(self, s: str) -> int: + res = 0 + + def dfs(i): + nonlocal res + if i == len(s): + return 0 + + cur = dfs(i + 1) + if s[i] == '(': + cur += 1 + elif s[i] == ')': + cur -= 1 + + res = max(res, abs(cur)) + return cur + + dfs(0) + return res +``` + +```java +public class Solution { + private int res = 0; + + public int maxDepth(String s) { + dfs(s, 0); + return res; + } + + private int dfs(String s, int i) { + if (i == s.length()) { + return 0; + } + + int cur = dfs(s, i + 1); + if (s.charAt(i) == '(') { + cur += 1; + } else if (s.charAt(i) == ')') { + cur -= 1; + } + + res = Math.max(res, Math.abs(cur)); + return cur; + } +} +``` + +```cpp +class Solution { +private: + int res = 0; + + int dfs(const string& s, int i) { + if (i == s.length()) { + return 0; + } + + int cur = dfs(s, i + 1); + if (s[i] == '(') { + cur += 1; + } else if (s[i] == ')') { + cur -= 1; + } + + res = max(res, abs(cur)); + return cur; + } + +public: + int maxDepth(string s) { + dfs(s, 0); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxDepth(s) { + let res = 0; + + const dfs = (i) => { + if (i === s.length) { + return 0; + } + + let cur = dfs(i + 1); + if (s[i] === '(') { + cur += 1; + } else if (s[i] === ')') { + cur -= 1; + } + + res = Math.max(res, Math.abs(cur)); + return cur; + }; + + dfs(0); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Stack + +::tabs-start + +```python +class Solution: + def maxDepth(self, s: str) -> int: + res, stack = 0, [] + + for c in s: + if c == "(": + stack.append(c) + res = max(res, len(stack)) + elif c == ")": + stack.pop() + + return res +``` + +```java +public class Solution { + public int maxDepth(String s) { + int res = 0; + Stack stack = new Stack<>(); + + for (char c : s.toCharArray()) { + if (c == '(') { + stack.push(c); + res = Math.max(res, stack.size()); + } else if (c == ')') { + stack.pop(); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxDepth(string s) { + int res = 0; + stack stack; + + for (char c : s) { + if (c == '(') { + stack.push(c); + res = max(res, (int)stack.size()); + } else if (c == ')') { + stack.pop(); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxDepth(s) { + let res = 0; + let stack = []; + + for (let c of s) { + if (c === '(') { + stack.push(c); + res = Math.max(res, stack.length); + } else if (c === ')') { + stack.pop(); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Iteration + +::tabs-start + +```python +class Solution: + def maxDepth(self, s: str) -> int: + res = 0 + cur = 0 + + for c in s: + if c == "(": + cur += 1 + elif c == ")": + cur -= 1 + res = max(res, cur) + + return res +``` + +```java +public class Solution { + public int maxDepth(String s) { + int res = 0, cur = 0; + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '(') { + cur++; + } else if (c == ')') { + cur--; + } + res = Math.max(res, cur); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxDepth(string s) { + int res = 0, cur = 0; + + for (char c : s) { + if (c == '(') { + cur++; + } else if (c == ')') { + cur--; + } + res = max(res, cur); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxDepth(s) { + let res = 0, cur = 0; + + for (let c of s) { + if (c === '(') { + cur++; + } else if (c === ')') { + cur--; + } + res = Math.max(res, cur); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/maximum-number-of-balloons.md b/articles/maximum-number-of-balloons.md new file mode 100644 index 000000000..597cbf295 --- /dev/null +++ b/articles/maximum-number-of-balloons.md @@ -0,0 +1,185 @@ +## 1. Hash Map - I + +::tabs-start + +```python +class Solution: + def maxNumberOfBalloons(self, text: str) -> int: + countText = Counter(text) + balloon = Counter("balloon") + + res = len(text) + for c in balloon: + res = min(res, countText[c] // balloon[c]) + return res +``` + +```java +public class Solution { + public int maxNumberOfBalloons(String text) { + Map countText = new HashMap<>(); + for (char c : text.toCharArray()) { + countText.put(c, countText.getOrDefault(c, 0) + 1); + } + + Map balloon = new HashMap<>(); + for (char c : "balloon".toCharArray()) { + balloon.put(c, balloon.getOrDefault(c, 0) + 1); + } + + int res = text.length(); + for (char c : balloon.keySet()) { + res = Math.min(res, countText.getOrDefault(c, 0) / balloon.get(c)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxNumberOfBalloons(string text) { + unordered_map countText; + for (char c : text) { + countText[c]++; + } + + unordered_map balloon = {{'b', 1}, {'a', 1}, + {'l', 2}, {'o', 2}, {'n', 1}}; + + int res = text.length(); + for (auto& entry : balloon) { + res = min(res, countText[entry.first] / entry.second); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} text + * @return {number} + */ + maxNumberOfBalloons(text) { + const countText = {}; + for (const c of text) { + countText[c] = (countText[c] || 0) + 1; + } + + const balloon = { b: 1, a: 1, l: 2, o: 2, n: 1 }; + + let res = text.length; + for (const c in balloon) { + res = Math.min(res, Math.floor((countText[c] || 0) / balloon[c])); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +--- + +## 2. Hash Map - II + +::tabs-start + +```python +class Solution: + def maxNumberOfBalloons(self, text: str) -> int: + mp = defaultdict(int) + for c in text: + if c in "balon": + mp[c] += 1 + + if len(mp) < 5: + return 0 + + mp['l'] //= 2 + mp['o'] //= 2 + return min(mp.values()) +``` + +```java +public class Solution { + public int maxNumberOfBalloons(String text) { + Map mp = new HashMap<>(); + for (char c : text.toCharArray()) { + if ("balon".indexOf(c) != -1) { + mp.put(c, mp.getOrDefault(c, 0) + 1); + } + } + + if (mp.size() < 5) { + return 0; + } + + mp.put('l', mp.get('l') / 2); + mp.put('o', mp.get('o') / 2); + return Collections.min(mp.values()); + } +} +``` + +```cpp +class Solution { +public: + int maxNumberOfBalloons(string text) { + unordered_map mp; + for (char c : text) { + if (string("balon").find(c) != string::npos) { + mp[c]++; + } + } + + if (mp.size() < 5) { + return 0; + } + + mp['l'] /= 2; + mp['o'] /= 2; + return min({mp['b'], mp['a'], mp['l'], mp['o'], mp['n']}); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} text + * @return {number} + */ + maxNumberOfBalloons(text) { + const mp = new Map(); + for (let c of text) { + if ("balon".includes(c)) { + mp.set(c, (mp.get(c) || 0) + 1); + } + } + + if (mp.size < 5) { + return 0; + } + + mp.set('l', Math.floor(mp.get('l') / 2)); + mp.set('o', Math.floor(mp.get('o') / 2)); + return Math.min(...Array.from(mp.values())); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since $balloon$ has $5$ different characters. \ No newline at end of file diff --git a/articles/maximum-number-of-removable-characters.md b/articles/maximum-number-of-removable-characters.md new file mode 100644 index 000000000..bc22c959f --- /dev/null +++ b/articles/maximum-number-of-removable-characters.md @@ -0,0 +1,451 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maximumRemovals(self, s: str, p: str, removable: List[int]) -> int: + n, m = len(s), len(p) + marked = set() + res = 0 + + for removeIdx in removable: + marked.add(removeIdx) + + sIdx = pIdx = 0 + while pIdx < m and sIdx < n: + if sIdx not in marked and s[sIdx] == p[pIdx]: + pIdx += 1 + sIdx += 1 + if pIdx != m: + break + res += 1 + + return res +``` + +```java +public class Solution { + public int maximumRemovals(String s, String p, int[] removable) { + int n = s.length(), m = p.length(); + Set marked = new HashSet<>(); + int res = 0; + + for (int removeIdx : removable) { + marked.add(removeIdx); + + int sIdx = 0, pIdx = 0; + while (pIdx < m && sIdx < n) { + if (!marked.contains(sIdx) && s.charAt(sIdx) == p.charAt(pIdx)) { + pIdx++; + } + sIdx++; + } + + if (pIdx != m) break; + res++; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maximumRemovals(string s, string p, vector& removable) { + int n = s.size(), m = p.size(); + unordered_set marked; + int res = 0; + + for (int removeIdx : removable) { + marked.insert(removeIdx); + + int sIdx = 0, pIdx = 0; + while (pIdx < m && sIdx < n) { + if (marked.find(sIdx) == marked.end() && s[sIdx] == p[pIdx]) { + pIdx++; + } + sIdx++; + } + + if (pIdx != m) break; + res++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} p + * @param {number[]} removable + * @return {number} + */ + maximumRemovals(s, p, removable) { + let n = s.length, m = p.length; + let marked = new Set(); + let res = 0; + + for (let removeIdx of removable) { + marked.add(removeIdx); + + let sIdx = 0, pIdx = 0; + while (pIdx < m && sIdx < n) { + if (!marked.has(sIdx) && s[sIdx] === p[pIdx]) { + pIdx++; + } + sIdx++; + } + + if (pIdx !== m) break; + res++; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * (n + m))$ +* Space complexity: $O(k)$ + +> Where $n$ and $m$ are the lengths of the given strings $s$ and $p$ respectively. $k$ is the size of the array $removable$. + +--- + +## 2. Binary Search + Hash Set + +::tabs-start + +```python +class Solution: + def maximumRemovals(self, s: str, p: str, removable: List[int]) -> int: + def isSubseq(s, subseq, removed): + i1 = i2 = 0 + while i1 < len(s) and i2 < len(subseq): + if i1 in removed or s[i1] != subseq[i2]: + i1 += 1 + continue + i1 += 1 + i2 += 1 + return i2 == len(subseq) + + res = 0 + l, r = 0, len(removable) - 1 + while l <= r: + m = (l + r) // 2 + removed = set(removable[:m + 1]) + if isSubseq(s, p, removed): + res = max(res, m + 1) + l = m + 1 + else: + r = m - 1 + + return res +``` + +```java +public class Solution { + public int maximumRemovals(String s, String p, int[] removable) { + int res = 0, l = 0, r = removable.length - 1; + + while (l <= r) { + int m = (l + r) / 2; + Set removed = new HashSet<>(); + for (int i = 0; i <= m; i++) { + removed.add(removable[i]); + } + + if (isSubseq(s, p, removed)) { + res = Math.max(res, m + 1); + l = m + 1; + } else { + r = m - 1; + } + } + + return res; + } + + private boolean isSubseq(String s, String subseq, Set removed) { + int i1 = 0, i2 = 0; + while (i1 < s.length() && i2 < subseq.length()) { + if (removed.contains(i1) || s.charAt(i1) != subseq.charAt(i2)) { + i1++; + continue; + } + i1++; + i2++; + } + return i2 == subseq.length(); + } +} +``` + +```cpp +class Solution { +public: + int maximumRemovals(string s, string p, vector& removable) { + int res = 0, l = 0, r = removable.size() - 1; + + while (l <= r) { + int m = (l + r) / 2; + unordered_set removed(removable.begin(), removable.begin() + m + 1); + + if (isSubseq(s, p, removed)) { + res = max(res, m + 1); + l = m + 1; + } else { + r = m - 1; + } + } + + return res; + } + +private: + bool isSubseq(string& s, string& subseq, unordered_set& removed) { + int i1 = 0, i2 = 0; + while (i1 < s.size() && i2 < subseq.size()) { + if (removed.count(i1) || s[i1] != subseq[i2]) { + i1++; + continue; + } + i1++; + i2++; + } + return i2 == subseq.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} p + * @param {number[]} removable + * @return {number} + */ + maximumRemovals(s, p, removable) { + let res = 0, l = 0, r = removable.length - 1; + + while (l <= r) { + let m = Math.floor((l + r) / 2); + let removed = new Set(removable.slice(0, m + 1)); + + if (this.isSubseq(s, p, removed)) { + res = Math.max(res, m + 1); + l = m + 1; + } else { + r = m - 1; + } + } + + return res; + } + + /** + * @param {string} s + * @param {string} subseq + * @param {Set} removed + * @return {boolean} + */ + isSubseq(s, subseq, removed) { + let i1 = 0, i2 = 0; + while (i1 < s.length && i2 < subseq.length) { + if (removed.has(i1) || s[i1] !== subseq[i2]) { + i1++; + continue; + } + i1++; + i2++; + } + return i2 === subseq.length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((n + m) * \log k)$ +* Space complexity: $O(k)$ + +> Where $n$ and $m$ are the lengths of the given strings $s$ and $p$ respectively. $k$ is the size of the array $removable$. + +--- + +## 3. Binary Search + +::tabs-start + +```python +class Solution: + def maximumRemovals(self, s: str, p: str, removable: List[int]) -> int: + l, r = 0, len(removable) + n, m = len(s), len(p) + + def isSubseq(tmpS): + i1 = i2 = 0 + while i1 < n and i2 < m: + if tmpS[i1] == p[i2]: + i2 += 1 + i1 += 1 + return i2 == m + + while l < r: + mid = l + (r - l) // 2 + tmpS = list(s) + + for i in range(mid + 1): + tmpS[removable[i]] = '#' + + if isSubseq(tmpS): + l = mid + 1 + else: + r = mid + + return l +``` + +```java +public class Solution { + public int maximumRemovals(String s, String p, int[] removable) { + int l = 0, r = removable.length; + int n = s.length(), m = p.length(); + + while (l < r) { + int mid = l + (r - l) / 2; + char[] tmpS = s.toCharArray(); + + for (int i = 0; i <= mid; i++) { + tmpS[removable[i]] = '#'; + } + + if (isSubseq(tmpS, p)) { + l = mid + 1; + } else { + r = mid; + } + } + + return l; + } + + private boolean isSubseq(char[] s, String p) { + int i1 = 0, i2 = 0, n = s.length, m = p.length(); + + while (i1 < n && i2 < m) { + if (s[i1] == p.charAt(i2)) { + i2++; + } + i1++; + } + return i2 == m; + } +} +``` + +```cpp +class Solution { +public: + int maximumRemovals(string s, string p, vector& removable) { + int l = 0, r = removable.size(); + int n = s.size(), m = p.size(); + + while (l < r) { + int mid = l + (r - l) / 2; + string tmpS = s; + + for (int i = 0; i <= mid; i++) { + tmpS[removable[i]] = '#'; + } + + if (isSubseq(tmpS, p)) { + l = mid + 1; + } else { + r = mid; + } + } + + return l; + } + +private: + bool isSubseq(const string& s, const string& p) { + int i1 = 0, i2 = 0, n = s.size(), m = p.size(); + + while (i1 < n && i2 < m) { + if (s[i1] == p[i2]) { + i2++; + } + i1++; + } + return i2 == m; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} p + * @param {number[]} removable + * @return {number} + */ + maximumRemovals(s, p, removable) { + let l = 0, r = removable.length; + let n = s.length, m = p.length; + + const isSubseq = (tmpS) => { + let i1 = 0, i2 = 0; + while (i1 < n && i2 < m) { + if (tmpS[i1] === p[i2]) { + i2++; + } + i1++; + } + return i2 === m; + }; + + while (l < r) { + let mid = Math.floor(l + (r - l) / 2); + let tmpS = s.split(""); + + for (let i = 0; i <= mid; i++) { + tmpS[removable[i]] = "#"; + } + + if (isSubseq(tmpS)) { + l = mid + 1; + } else { + r = mid; + } + } + + return l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((n + m) * \log k)$ +* Space complexity: $O(n)$ + +> Where $n$ and $m$ are the lengths of the given strings $s$ and $p$ respectively. $k$ is the size of the array $removable$. \ No newline at end of file diff --git a/articles/maximum-number-of-vowels-in-a-substring-of-given-length.md b/articles/maximum-number-of-vowels-in-a-substring-of-given-length.md new file mode 100644 index 000000000..371aff2b3 --- /dev/null +++ b/articles/maximum-number-of-vowels-in-a-substring-of-given-length.md @@ -0,0 +1,385 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxVowels(self, s: str, k: int) -> int: + vowel = {'a', 'e', 'i', 'o', 'u'} + res = 0 + + for i in range(len(s) - k + 1): + cnt = 0 + for j in range(i, i + k): + cnt += 1 if s[j] in vowel else 0 + res = max(res, cnt) + + return res +``` + +```java +public class Solution { + public int maxVowels(String s, int k) { + Set vowels = Set.of('a', 'e', 'i', 'o', 'u'); + int res = 0; + + for (int i = 0; i <= s.length() - k; i++) { + int cnt = 0; + for (int j = i; j < i + k; j++) { + if (vowels.contains(s.charAt(j))) { + cnt++; + } + } + res = Math.max(res, cnt); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxVowels(string s, int k) { + unordered_set vowels = {'a', 'e', 'i', 'o', 'u'}; + int res = 0; + + for (int i = 0; i <= s.size() - k; i++) { + int cnt = 0; + for (int j = i; j < i + k; j++) { + if (vowels.count(s[j])) { + cnt++; + } + } + res = max(res, cnt); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + maxVowels(s, k) { + const vowels = new Set(['a', 'e', 'i', 'o', 'u']); + let res = 0; + + for (let i = 0; i <= s.length - k; i++) { + let cnt = 0; + for (let j = i; j < i + k; j++) { + if (vowels.has(s[j])) { + cnt++; + } + } + res = Math.max(res, cnt); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Prefix Count + +::tabs-start + +```python +class Solution: + def maxVowels(self, s: str, k: int) -> int: + vowel = {'a', 'e', 'i', 'o', 'u'} + prefix = [0] * (len(s) + 1) + for i in range(len(s)): + prefix[i + 1] = prefix[i] + (1 if s[i] in vowel else 0) + + res = 0 + for i in range(k, len(s) + 1): + res = max(res, prefix[i] - prefix[i - k]) + + return res +``` + +```java +public class Solution { + public int maxVowels(String s, int k) { + Set vowels = Set.of('a', 'e', 'i', 'o', 'u'); + int[] prefix = new int[s.length() + 1]; + + for (int i = 0; i < s.length(); i++) { + prefix[i + 1] = prefix[i] + (vowels.contains(s.charAt(i)) ? 1 : 0); + } + + int res = 0; + for (int i = k; i <= s.length(); i++) { + res = Math.max(res, prefix[i] - prefix[i - k]); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxVowels(string s, int k) { + unordered_set vowels = {'a', 'e', 'i', 'o', 'u'}; + vector prefix(s.size() + 1, 0); + + for (int i = 0; i < s.size(); i++) { + prefix[i + 1] = prefix[i] + (vowels.count(s[i]) ? 1 : 0); + } + + int res = 0; + for (int i = k; i <= s.size(); i++) { + res = max(res, prefix[i] - prefix[i - k]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + maxVowels(s, k) { + const vowels = new Set(['a', 'e', 'i', 'o', 'u']); + const prefix = new Array(s.length + 1).fill(0); + + for (let i = 0; i < s.length; i++) { + prefix[i + 1] = prefix[i] + (vowels.has(s[i]) ? 1 : 0); + } + + let res = 0; + for (let i = k; i <= s.length; i++) { + res = Math.max(res, prefix[i] - prefix[i - k]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sliding Window + +::tabs-start + +```python +class Solution: + def maxVowels(self, s: str, k: int) -> int: + vowel = {'a', 'e', 'i', 'o', 'u'} + + l = cnt = res = 0 + for r in range(len(s)): + cnt += 1 if s[r] in vowel else 0 + if r - l + 1 > k: + cnt -= 1 if s[l] in vowel else 0 + l += 1 + res = max(res, cnt) + return res +``` + +```java +public class Solution { + public int maxVowels(String s, int k) { + Set vowels = Set.of('a', 'e', 'i', 'o', 'u'); + + int l = 0, cnt = 0, res = 0; + for (int r = 0; r < s.length(); r++) { + cnt += (vowels.contains(s.charAt(r)) ? 1 : 0); + if (r - l + 1 > k) { + cnt -= (vowels.contains(s.charAt(l)) ? 1 : 0); + l++; + } + res = Math.max(res, cnt); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxVowels(string s, int k) { + unordered_set vowels = {'a', 'e', 'i', 'o', 'u'}; + + int l = 0, cnt = 0, res = 0; + for (int r = 0; r < s.length(); r++) { + cnt += (vowels.count(s[r]) ? 1 : 0); + if (r - l + 1 > k) { + cnt -= (vowels.count(s[l++]) ? 1 : 0); + } + res = max(res, cnt); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + maxVowels(s, k) { + const vowels = new Set(['a', 'e', 'i', 'o', 'u']); + + let l = 0, cnt = 0, res = 0; + for (let r = 0; r < s.length; r++) { + cnt += (vowels.has(s[r]) ? 1 : 0); + if (r - l + 1 > k) { + cnt -= (vowels.has(s[l++]) ? 1 : 0); + } + res = Math.max(res, cnt); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Sliding Window (Bit Mask) + +::tabs-start + +```python +class Solution: + def maxVowels(self, s: str, k: int) -> int: + def getId(c): + return ord(c) - ord('a') + + mask = (1 << getId('a')) | (1 << getId('e')) | \ + (1 << getId('i')) | (1 << getId('o')) | \ + (1 << getId('u')) + + l = cnt = res = 0 + for r in range(len(s)): + cnt += ((mask >> getId(s[r])) & 1) + if r - l + 1 > k: + cnt -= ((mask >> getId(s[l])) & 1) + l += 1 + res = max(res, cnt) + return res +``` + +```java +public class Solution { + public int maxVowels(String s, int k) { + int mask = (1 << ('a' - 'a')) | (1 << ('e' - 'a')) | + (1 << ('i' - 'a')) | (1 << ('o' - 'a')) | + (1 << ('u' - 'a')); + + int l = 0, cnt = 0, res = 0; + for (int r = 0; r < s.length(); r++) { + cnt += (mask >> (s.charAt(r) - 'a')) & 1; + if (r - l + 1 > k) { + cnt -= (mask >> (s.charAt(l) - 'a')) & 1; + l++; + } + res = Math.max(res, cnt); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxVowels(string s, int k) { + int mask = (1 << ('a' - 'a')) | (1 << ('e' - 'a')) | + (1 << ('i' - 'a')) | (1 << ('o' - 'a')) | + (1 << ('u' - 'a')); + + int l = 0, cnt = 0, res = 0; + for (int r = 0; r < s.size(); r++) { + cnt += (mask >> (s[r] - 'a')) & 1; + if (r - l + 1 > k) { + cnt -= (mask >> (s[l] - 'a')) & 1; + l++; + } + res = max(res, cnt); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + maxVowels(s, k) { + const getId = (c) => { + return c.charCodeAt(0) - 'a'.charCodeAt(0); + }; + const mask = (1 << getId('a')) | (1 << getId('e')) | + (1 << getId('i')) | (1 << getId('o')) | + (1 << getId('u')); + + let l = 0, cnt = 0, res = 0; + for (let r = 0; r < s.length; r++) { + cnt += (mask >> getId(s.charAt(r))) & 1; + if (r - l + 1 > k) { + cnt -= (mask >> getId(s.charAt(l))) & 1; + l++; + } + res = Math.max(res, cnt); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/maximum-odd-binary-number.md b/articles/maximum-odd-binary-number.md new file mode 100644 index 000000000..41846d673 --- /dev/null +++ b/articles/maximum-odd-binary-number.md @@ -0,0 +1,264 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def maximumOddBinaryNumber(self, s: str) -> str: + s = sorted(s) + s.reverse() + i = len(s) - 1 + while i >= 0 and s[i] == "0": + i -= 1 + + s[i], s[len(s) - 1] = s[len(s) - 1], s[i] + return ''.join(s) +``` + +```java +public class Solution { + public String maximumOddBinaryNumber(String s) { + char[] arr = s.toCharArray(); + Arrays.sort(arr); + reverse(arr); + + int n = arr.length; + int i = n - 1; + + while (i >= 0 && arr[i] == '0') { + i--; + } + + char temp = arr[i]; + arr[i] = arr[n - 1]; + arr[n - 1] = temp; + + return new String(arr); + } + + private void reverse(char[] arr) { + int left = 0, right = arr.length - 1; + while (left < right) { + char temp = arr[left]; + arr[left++] = arr[right]; + arr[right--] = temp; + } + } +} +``` + +```cpp +class Solution { +public: + string maximumOddBinaryNumber(string s) { + sort(s.begin(), s.end(), greater()); + + int n = s.size(), i = n - 1; + while (i >= 0 && s[i] == '0') { + i--; + } + + swap(s[i], s[n - 1]); + return s; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + maximumOddBinaryNumber(s) { + let arr = s.split(''); + arr.sort().reverse(); + + let i = arr.length - 1; + while (i >= 0 && arr[i] === '0') { + i--; + } + + [arr[i], arr[arr.length - 1]] = [arr[arr.length - 1], arr[i]]; + return arr.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Greedy + +::tabs-start + +```python +class Solution: + def maximumOddBinaryNumber(self, s: str) -> str: + count = 0 + for c in s: + if c == "1": + count += 1 + + return (count - 1) * "1" + (len(s) - count) * "0" + "1" +``` + +```java +public class Solution { + public String maximumOddBinaryNumber(String s) { + int count = 0; + for (char c : s.toCharArray()) { + if (c == '1') count++; + } + + StringBuilder result = new StringBuilder(); + for (int i = 0; i < count - 1; i++) result.append('1'); + for (int i = 0; i < s.length() - count; i++) result.append('0'); + result.append('1'); + + return result.toString(); + } +} +``` + +```cpp +class Solution { +public: + string maximumOddBinaryNumber(string s) { + int count = 0; + for (char c : s) { + if (c == '1') count++; + } + + string result((count - 1), '1'); + result += string(s.length() - count, '0'); + result += '1'; + + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + maximumOddBinaryNumber(s) { + let count = 0; + for (const c of s) { + if (c === '1') count++; + } + + return '1'.repeat(count - 1) + '0'.repeat(s.length - count) + '1'; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Two Pointers + +::tabs-start + +```python +class Solution: + def maximumOddBinaryNumber(self, s: str) -> str: + s = [c for c in s] + left = 0 + + for i in range(len(s)): + if s[i] == "1": + s[i], s[left] = s[left], s[i] + left += 1 + s[left - 1], s[len(s) - 1] = s[len(s) - 1], s[left - 1] + return "".join(s) +``` + +```java +public class Solution { + public String maximumOddBinaryNumber(String s) { + char[] arr = s.toCharArray(); + int left = 0; + + for (int i = 0; i < arr.length; i++) { + if (arr[i] == '1') { + char temp = arr[left]; + arr[left] = arr[i]; + arr[i] = temp; + left++; + } + } + + char temp = arr[left - 1]; + arr[left - 1] = arr[arr.length - 1]; + arr[arr.length - 1] = temp; + + return new String(arr); + } +} +``` + +```cpp +class Solution { +public: + string maximumOddBinaryNumber(string s) { + vector arr(s.begin(), s.end()); + int left = 0; + + for (int i = 0; i < arr.size(); i++) { + if (arr[i] == '1') { + swap(arr[left], arr[i]); + left++; + } + } + + swap(arr[left - 1], arr[arr.size() - 1]); + return string(arr.begin(), arr.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + maximumOddBinaryNumber(s) { + let arr = s.split(''); + let left = 0; + + for (let i = 0; i < arr.length; i++) { + if (arr[i] === '1') { + [arr[left], arr[i]] = [arr[i], arr[left]]; + left++; + } + } + + [arr[left - 1], arr[arr.length - 1]] = [arr[arr.length - 1], arr[left - 1]]; + return arr.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/maximum-performance-of-a-team.md b/articles/maximum-performance-of-a-team.md new file mode 100644 index 000000000..3fc63e969 --- /dev/null +++ b/articles/maximum-performance-of-a-team.md @@ -0,0 +1,238 @@ +## 1. Brute Force (Recursion) + +::tabs-start + +```python +class Solution: + def maxPerformance(self, n: int, speed: List[int], efficiency: List[int], k: int) -> int: + MOD = 1000000007 + res = 0 + + def dfs(i, k, speedSum, minEff): + nonlocal res + res = max(res, speedSum * minEff) + if i == n or k == 0: + return + + dfs(i + 1, k, speedSum, minEff) + dfs(i + 1, k - 1, speedSum + speed[i], min(minEff, efficiency[i])) + + dfs(0, k, 0, float("inf")) + return res % MOD +``` + +```java +public class Solution { + private static final int MOD = 1000000007; + private int[] speed, efficiency; + private int n; + private long res; + + public int maxPerformance(int n, int[] speed, int[] efficiency, int k) { + this.n = n; + this.speed = speed; + this.efficiency = efficiency; + this.res = 0; + + dfs(0, k, Integer.MAX_VALUE, 0); + return (int) (res % MOD); + } + + private void dfs(int i, int k, int minEff, long speedSum) { + res = Math.max(res, speedSum * minEff); + if (i == n || k == 0) return; + + dfs(i + 1, k, minEff, speedSum); + dfs(i + 1, k - 1, Math.min(minEff, efficiency[i]), speedSum + speed[i]); + } +} +``` + +```cpp +class Solution { +private: + static constexpr int MOD = 1000000007; + vector speed, efficiency; + int n; + long long res; + +public: + int maxPerformance(int n, vector& speed, vector& efficiency, int k) { + this->n = n; + this->speed = speed; + this->efficiency = efficiency; + res = 0; + + dfs(0, k, INT_MAX, 0); + return int(res % MOD); + } + +private: + void dfs(int i, int k, int minEff, long long speedSum) { + res = max(res, speedSum * minEff); + if (i == n || k == 0) return; + + dfs(i + 1, k, minEff, speedSum); + dfs(i + 1, k - 1, min(minEff, efficiency[i]), speedSum + speed[i]); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[]} speed + * @param {number[]} efficiency + * @param {number} k + * @return {number} + */ + maxPerformance(n, speed, efficiency, k) { + const MOD = 1000000007; + let res = 0; + + const dfs = (i, k, minEff, speedSum) => { + res = Math.max(res, minEff === Infinity ? 0 : speedSum * minEff); + if (i === n || k === 0) return; + + dfs(i + 1, k, minEff, speedSum); + dfs(i + 1, k - 1, Math.min(minEff, efficiency[i]), speedSum + speed[i]); + }; + + dfs(0, k, Infinity, 0); + return res % MOD; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Sorting + Min-Heap + +::tabs-start + +```python +class Solution: + def maxPerformance(self, n: int, speed: List[int], efficiency: List[int], k: int) -> int: + MOD = 10**9 + 7 + eng = sorted(zip(efficiency, speed), reverse=True) + + res = speedSum = 0 + minHeap = [] + + for eff, spd in eng: + if len(minHeap) == k: + speedSum -= heapq.heappop(minHeap) + speedSum += spd + heapq.heappush(minHeap, spd) + res = max(res, eff * speedSum) + + return res % MOD +``` + +```java +public class Solution { + public int maxPerformance(int n, int[] speed, int[] efficiency, int k) { + final int MOD = 1_000_000_007; + int[][] engineers = new int[n][2]; + + for (int i = 0; i < n; i++) { + engineers[i] = new int[]{efficiency[i], speed[i]}; + } + + Arrays.sort(engineers, (a, b) -> Integer.compare(b[0], a[0])); + + PriorityQueue minHeap = new PriorityQueue<>(); + long speedSum = 0, res = 0; + + for (int[] eng : engineers) { + if (minHeap.size() == k) { + speedSum -= minHeap.poll(); + } + speedSum += eng[1]; + minHeap.offer(eng[1]); + res = Math.max(res, speedSum * eng[0]); + } + + return (int) (res % MOD); + } +} +``` + +```cpp +class Solution { +public: + int maxPerformance(int n, vector& speed, vector& efficiency, int k) { + constexpr int MOD = 1'000'000'007; + vector> engineers; + + for (int i = 0; i < n; i++) { + engineers.emplace_back(efficiency[i], speed[i]); + } + + sort(engineers.rbegin(), engineers.rend()); + + priority_queue, greater> minHeap; + long long speedSum = 0, res = 0; + + for (const auto& [eff, spd] : engineers) { + if (minHeap.size() == k) { + speedSum -= minHeap.top(); + minHeap.pop(); + } + speedSum += spd; + minHeap.push(spd); + res = max(res, speedSum * eff); + } + + return res % MOD; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[]} speed + * @param {number[]} efficiency + * @param {number} k + * @return {number} + */ + maxPerformance(n, speed, efficiency, k) { + const MOD = BigInt(1e9 + 7); + const engineers = efficiency.map((eff, i) => [eff, speed[i]]); + engineers.sort((a, b) => b[0] - a[0]); + + const minHeap = new MinPriorityQueue(); + let speedSum = BigInt(0), res = BigInt(0); + + for (const [eff, spd] of engineers) { + if (minHeap.size() === k) { + speedSum -= BigInt(minHeap.dequeue()); + } + speedSum += BigInt(spd); + minHeap.enqueue(spd); + res = res > speedSum * BigInt(eff) ? res : speedSum * BigInt(eff); + } + + return Number(res % MOD); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * (\log n + \log k))$ +* Space complexity: $O(n + k)$ + +> Where $n$ is the number of engineers and $k$ is the maximum number of engineers that can be selected. \ No newline at end of file diff --git a/articles/maximum-points-you-can-obtain-from-cards.md b/articles/maximum-points-you-can-obtain-from-cards.md new file mode 100644 index 000000000..8d1b9294a --- /dev/null +++ b/articles/maximum-points-you-can-obtain-from-cards.md @@ -0,0 +1,462 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxScore(self, cardPoints: List[int], k: int) -> int: + n = len(cardPoints) + res = 0 + + for left in range(k + 1): + leftSum = sum(cardPoints[:left]) + rightSum = sum(cardPoints[n - (k - left):]) + res = max(res, leftSum + rightSum) + + return res +``` + +```java +public class Solution { + public int maxScore(int[] cardPoints, int k) { + int n = cardPoints.length; + int res = 0; + + for (int left = 0; left <= k; left++) { + int leftSum = 0; + for (int i = 0; i < left; i++) { + leftSum += cardPoints[i]; + } + + int rightSum = 0; + for (int i = n - (k - left); i < n; i++) { + rightSum += cardPoints[i]; + } + + res = Math.max(res, leftSum + rightSum); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxScore(vector& cardPoints, int k) { + int n = cardPoints.size(); + int res = 0; + + for (int left = 0; left <= k; left++) { + int leftSum = 0; + for (int i = 0; i < left; i++) { + leftSum += cardPoints[i]; + } + + int rightSum = 0; + for (int i = n - (k - left); i < n; i++) { + rightSum += cardPoints[i]; + } + + res = max(res, leftSum + rightSum); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} cardPoints + * @param {number} k + * @return {number} + */ + maxScore(cardPoints, k) { + let n = cardPoints.length; + let res = 0; + + for (let left = 0; left <= k; left++) { + let leftSum = 0; + for (let i = 0; i < left; i++) { + leftSum += cardPoints[i]; + } + + let rightSum = 0; + for (let i = n - (k - left); i < n; i++) { + rightSum += cardPoints[i]; + } + + res = Math.max(res, leftSum + rightSum); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k ^ 2)$ +* Space complexity: $O(1)$ extra space. + +> Where $k$ is the number of cards to pick. + +--- + +## 2. Prefix & Suffix Sums + +::tabs-start + +```python +class Solution: + def maxScore(self, cardPoints: List[int], k: int) -> int: + n = len(cardPoints) + + prefix = [0] * (n + 1) + for i in range(n): + prefix[i + 1] = prefix[i] + cardPoints[i] + + suffix = [0] * (n + 1) + for i in range(n - 1, -1, -1): + suffix[i] = suffix[i + 1] + cardPoints[i] + + res = 0 + for left in range(k + 1): + right = k - left + res = max(res, prefix[left] + suffix[n - right]) + + return res +``` + +```java +public class Solution { + public int maxScore(int[] cardPoints, int k) { + int n = cardPoints.length; + + int[] prefix = new int[n + 1]; + for (int i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + cardPoints[i]; + } + + int[] suffix = new int[n + 1]; + for (int i = n - 1; i >= 0; i--) { + suffix[i] = suffix[i + 1] + cardPoints[i]; + } + + int res = 0; + for (int left = 0; left <= k; left++) { + int right = k - left; + res = Math.max(res, prefix[left] + suffix[n - right]); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxScore(vector& cardPoints, int k) { + int n = cardPoints.size(); + + vector prefix(n + 1, 0); + for (int i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + cardPoints[i]; + } + + vector suffix(n + 1, 0); + for (int i = n - 1; i >= 0; i--) { + suffix[i] = suffix[i + 1] + cardPoints[i]; + } + + int res = 0; + for (int left = 0; left <= k; left++) { + int right = k - left; + res = max(res, prefix[left] + suffix[n - right]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} cardPoints + * @param {number} k + * @return {number} + */ + maxScore(cardPoints, k) { + let n = cardPoints.length; + + let prefix = new Array(n + 1).fill(0); + for (let i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + cardPoints[i]; + } + + let suffix = new Array(n + 1).fill(0); + for (let i = n - 1; i >= 0; i--) { + suffix[i] = suffix[i + 1] + cardPoints[i]; + } + + let res = 0; + for (let left = 0; left <= k; left++) { + let right = k - left; + res = Math.max(res, prefix[left] + suffix[n - right]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sliding Window (Minimum Sum Window) + +::tabs-start + +```python +class Solution: + def maxScore(self, cardPoints: List[int], k: int) -> int: + n = len(cardPoints) + windowSize = n - k + + if windowSize == 0: + return sum(cardPoints) + + total = 0 + minWindowSum = float("inf") + curSum = 0 + + for i in range(n): + total += cardPoints[i] + curSum += cardPoints[i] + if i >= windowSize - 1: + minWindowSum = min(minWindowSum, curSum) + curSum -= cardPoints[i - windowSize + 1] + + return total - minWindowSum +``` + +```java +public class Solution { + public int maxScore(int[] cardPoints, int k) { + int n = cardPoints.length; + int windowSize = n - k; + + if (windowSize == 0) { + int sum = 0; + for (int num : cardPoints) sum += num; + return sum; + } + + int total = 0; + int minWindowSum = Integer.MAX_VALUE; + int curSum = 0; + + for (int i = 0; i < n; i++) { + total += cardPoints[i]; + curSum += cardPoints[i]; + if (i >= windowSize - 1) { + minWindowSum = Math.min(minWindowSum, curSum); + curSum -= cardPoints[i - windowSize + 1]; + } + } + + return total - minWindowSum; + } +} +``` + +```cpp +class Solution { +public: + int maxScore(vector& cardPoints, int k) { + int n = cardPoints.size(); + int windowSize = n - k; + + if (windowSize == 0) { + return accumulate(cardPoints.begin(), cardPoints.end(), 0); + } + + int total = 0; + int minWindowSum = INT_MAX; + int curSum = 0; + + for (int i = 0; i < n; i++) { + total += cardPoints[i]; + curSum += cardPoints[i]; + if (i >= windowSize - 1) { + minWindowSum = min(minWindowSum, curSum); + curSum -= cardPoints[i - windowSize + 1]; + } + } + + return total - minWindowSum; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} cardPoints + * @param {number} k + * @return {number} + */ + maxScore(cardPoints, k) { + let n = cardPoints.length; + let windowSize = n - k; + + if (windowSize === 0) { + return cardPoints.reduce((sum, num) => sum + num, 0); + } + + let total = 0; + let minWindowSum = Infinity; + let curSum = 0; + + for (let i = 0; i < n; i++) { + total += cardPoints[i]; + curSum += cardPoints[i]; + if (i >= windowSize - 1) { + minWindowSum = Math.min(minWindowSum, curSum); + curSum -= cardPoints[i - windowSize + 1]; + } + } + + return total - minWindowSum; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Sliding Window + +::tabs-start + +```python +class Solution: + def maxScore(self, cardPoints: List[int], k: int) -> int: + l, r = 0, len(cardPoints) - k + total = sum(cardPoints[r:]) + res = total + + while r < len(cardPoints): + total += cardPoints[l] - cardPoints[r] + res = max(res, total) + l += 1 + r += 1 + + return res +``` + +```java +public class Solution { + public int maxScore(int[] cardPoints, int k) { + int l = 0, r = cardPoints.length - k; + int total = 0; + + for (int i = r; i < cardPoints.length; i++) { + total += cardPoints[i]; + } + + int res = total; + + while (r < cardPoints.length) { + total += cardPoints[l] - cardPoints[r]; + res = Math.max(res, total); + l++; + r++; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxScore(vector& cardPoints, int k) { + int l = 0, r = cardPoints.size() - k; + int total = 0; + + for (int i = r; i < cardPoints.size(); i++) { + total += cardPoints[i]; + } + + int res = total; + + while (r < cardPoints.size()) { + total += cardPoints[l] - cardPoints[r]; + res = max(res, total); + l++; + r++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} cardPoints + * @param {number} k + * @return {number} + */ + maxScore(cardPoints, k) { + let l = 0, r = cardPoints.length - k; + let total = 0; + + for (let i = r; i < cardPoints.length; i++) { + total += cardPoints[i]; + } + + let res = total; + + while (r < cardPoints.length) { + total += cardPoints[l] - cardPoints[r]; + res = Math.max(res, total); + l++; + r++; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k)$ +* Space complexity: $O(1)$ extra space. + +> Where $k$ is the number of cards to pick. \ No newline at end of file diff --git a/articles/maximum-product-difference-between-two-pairs.md b/articles/maximum-product-difference-between-two-pairs.md new file mode 100644 index 000000000..e5425c2f8 --- /dev/null +++ b/articles/maximum-product-difference-between-two-pairs.md @@ -0,0 +1,255 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxProductDifference(self, nums: List[int]) -> int: + n, res = len(nums), 0 + for a in range(n): + for b in range(n): + if a == b: continue + for c in range(n): + if a == c or b == c: continue + for d in range(n): + if a == d or b == d or c == d: continue + res = max(res, nums[a] * nums[b] - nums[c] * nums[d]) + return res +``` + +```java +public class Solution { + public int maxProductDifference(int[] nums) { + int n = nums.length, res = 0; + for (int a = 0; a < n; a++) { + for (int b = 0; b < n; b++) { + if (a == b) continue; + for (int c = 0; c < n; c++) { + if (a == c || b == c) continue; + for (int d = 0; d < n; d++) { + if (a == d || b == d || c == d) continue; + res = Math.max(res, nums[a] * nums[b] - nums[c] * nums[d]); + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxProductDifference(vector& nums) { + int n = nums.size(), res = 0; + for (int a = 0; a < n; a++) { + for (int b = 0; b < n; b++) { + if (a == b) continue; + for (int c = 0; c < n; c++) { + if (a == c || b == c) continue; + for (int d = 0; d < n; d++) { + if (a == d || b == d || c == d) continue; + res = max(res, nums[a] * nums[b] - nums[c] * nums[d]); + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxProductDifference(nums) { + const n = nums.length; + let res = 0; + for (let a = 0; a < n; a++) { + for (let b = 0; b < n; b++) { + if (a === b) continue; + for (let c = 0; c < n; c++) { + if (a === c || b === c) continue; + for (let d = 0; d < n; d++) { + if (a === d || b === d || c === d) continue; + res = Math.max(res, nums[a] * nums[b] - nums[c] * nums[d]); + } + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 4)$ +* Space complexity: $O(1)$ + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def maxProductDifference(self, nums: List[int]) -> int: + nums.sort() + return nums[-1] * nums[-2] - nums[0] * nums[1] +``` + +```java +public class Solution { + public int maxProductDifference(int[] nums) { + Arrays.sort(nums); + return nums[nums.length - 1] * nums[nums.length - 2] - nums[0] * nums[1]; + } +} +``` + +```cpp +class Solution { +public: + int maxProductDifference(vector& nums) { + sort(nums.begin(), nums.end()); + return nums[nums.size() - 1] * nums[nums.size() - 2] - nums[0] * nums[1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxProductDifference(nums) { + nums.sort((a, b) => a - b); + return nums[nums.length - 1] * nums[nums.length - 2] - nums[0] * nums[1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Two Maximums and Two Minimums + +::tabs-start + +```python +class Solution: + def maxProductDifference(self, nums: List[int]) -> int: + max1 = max2 = 0 + min1 = min2 = float('inf') + + for num in nums: + if num > max1: + max1, max2 = num, max1 + elif num > max2: + max2 = num + if num < min1: + min1, min2 = num, min1 + elif num < min2: + min2 = num + + return (max1 * max2) - (min1 * min2) +``` + +```java +public class Solution { + public int maxProductDifference(int[] nums) { + int max1 = 0, max2 = 0; + int min1 = Integer.MAX_VALUE, min2 = Integer.MAX_VALUE; + for (int num : nums) { + if (num > max1) { + max2 = max1; + max1 = num; + } else if (num > max2) { + max2 = num; + } + if (num < min1) { + min2 = min1; + min1 = num; + } else if (num < min2) { + min2 = num; + } + } + return (max1 * max2) - (min1 * min2); + } +} +``` + +```cpp +class Solution { +public: + int maxProductDifference(vector& nums) { + int max1 = 0, max2 = 0; + int min1 = INT_MAX, min2 = INT_MAX; + for (int num : nums) { + if (num > max1) { + max2 = max1; + max1 = num; + } else if (num > max2) { + max2 = num; + } + if (num < min1) { + min2 = min1; + min1 = num; + } else if (num < min2) { + min2 = num; + } + } + return (max1 * max2) - (min1 * min2); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxProductDifference(nums) { + let max1 = 0, max2 = 0; + let min1 = Infinity, min2 = Infinity; + for (const num of nums) { + if (num > max1) { + max2 = max1; + max1 = num; + } else if (num > max2) { + max2 = num; + } + if (num < min1) { + min2 = min1; + min1 = num; + } else if (num < min2) { + min2 = num; + } + } + return (max1 * max2) - (min1 * min2); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/maximum-product-of-the-length-of-two-palindromic-subsequences.md b/articles/maximum-product-of-the-length-of-two-palindromic-subsequences.md new file mode 100644 index 000000000..bd17730e3 --- /dev/null +++ b/articles/maximum-product-of-the-length-of-two-palindromic-subsequences.md @@ -0,0 +1,854 @@ +## 1. Recursion (Backtracking) + +::tabs-start + +```python +class Solution: + def maxProduct(self, s: str) -> int: + def isPal(s): + i, j = 0, len(s) - 1 + while i < j: + if s[i] != s[j]: + return False + i += 1 + j -= 1 + return True + + res = 0 + def rec(i, seq1, seq2): + nonlocal res + if i == len(s): + if isPal(seq1) and isPal(seq2): + res = max(res, len(seq1) * len(seq2)) + return + + rec(i + 1, seq1, seq2) + rec(i + 1, seq1 + s[i], seq2) + rec(i + 1, seq1, seq2 + s[i]) + + rec(0, "", "") + return res +``` + +```java +public class Solution { + private boolean isPal(String s) { + int i = 0, j = s.length() - 1; + while (i < j) { + if (s.charAt(i) != s.charAt(j)) return false; + i++; + j--; + } + return true; + } + + public void rec(int i, String s, StringBuilder seq1, StringBuilder seq2, int[] res) { + if (i == s.length()) { + if (isPal(seq1.toString()) && isPal(seq2.toString())) { + res[0] = Math.max(res[0], seq1.length() * seq2.length()); + } + return; + } + + rec(i + 1, s, seq1, seq2, res); + seq1.append(s.charAt(i)); + rec(i + 1, s, seq1, seq2, res); + seq1.deleteCharAt(seq1.length() - 1); + + seq2.append(s.charAt(i)); + rec(i + 1, s, seq1, seq2, res); + seq2.deleteCharAt(seq2.length() - 1); + } + + public int maxProduct(String s) { + int[] res = new int[1]; + rec(0, s, new StringBuilder(), new StringBuilder(), res); + return res[0]; + } +} +``` + +```cpp +class Solution { +public: + bool isPal(const string &s) { + int i = 0, j = s.length() - 1; + while (i < j) { + if (s[i] != s[j]) return false; + i++; + j--; + } + return true; + } + + void rec(int i, string& s, string& seq1, string& seq2, int &res) { + if (i == s.length()) { + if (isPal(seq1) && isPal(seq2)) { + res = max(res, (int)seq1.length() * (int)seq2.length()); + } + return; + } + + rec(i + 1, s, seq1, seq2, res); + seq1.push_back(s[i]); + rec(i + 1, s, seq1, seq2, res); + seq1.pop_back(); + seq2.push_back(s[i]); + rec(i + 1, s, seq1, seq2, res); + seq2.pop_back(); + } + + int maxProduct(string s) { + int res = 0; + string seq1 = "", seq2 = ""; + rec(0, s, seq1, seq2, res); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxProduct(s) { + const isPal = (str) => { + let i = 0, j = str.length - 1; + while (i < j) { + if (str[i] !== str[j]) return false; + i++; + j--; + } + return true; + }; + + let res = 0; + + const rec = (i, seq1, seq2) => { + if (i === s.length) { + if (isPal(seq1) && isPal(seq2)) { + res = Math.max(res, seq1.length * seq2.length); + } + return; + } + + rec(i + 1, seq1, seq2); + rec(i + 1, seq1 + s[i], seq2); + rec(i + 1, seq1, seq2 + s[i]); + }; + + rec(0, "", ""); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 3 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Bit Mask + +::tabs-start + +```python +class Solution: + def maxProduct(self, s: str) -> int: + def isPal(s): + i, j = 0, len(s) - 1 + while i < j: + if s[i] != s[j]: + return False + i += 1 + j -= 1 + return True + + N, pali = len(s), {} + + for mask in range(1, 1 << N): + subseq = "" + for i in range(N): + if mask & (1 << i): + subseq += s[i] + + if isPal(subseq): + pali[mask] = len(subseq) + + res = 0 + for m1 in pali: + for m2 in pali: + if m1 & m2 == 0: + res = max(res, pali[m1] * pali[m2]) + + return res +``` + +```java +public class Solution { + public int maxProduct(String s) { + int N = s.length(); + int res = 0; + Map pali = new HashMap<>(); + + for (int mask = 1; mask < (1 << N); mask++) { + StringBuilder subseq = new StringBuilder(); + for (int i = 0; i < N; i++) { + if ((mask & (1 << i)) != 0) { + subseq.append(s.charAt(i)); + } + } + + if (isPal(subseq.toString())) { + pali.put(mask, subseq.length()); + } + } + + for (int m1 : pali.keySet()) { + for (int m2 : pali.keySet()) { + if ((m1 & m2) == 0) { + res = Math.max(res, pali.get(m1) * pali.get(m2)); + } + } + } + + return res; + } + + private boolean isPal(String s) { + int i = 0, j = s.length() - 1; + while (i < j) { + if (s.charAt(i) != s.charAt(j)) { + return false; + } + i++; + j--; + } + return true; + } +} +``` + +```cpp +class Solution { +public: + int maxProduct(string s) { + int N = s.length(); + int res = 0; + unordered_map pali; + + for (int mask = 1; mask < (1 << N); mask++) { + string subseq = ""; + for (int i = 0; i < N; i++) { + if (mask & (1 << i)) { + subseq += s[i]; + } + } + + if (isPal(subseq)) { + pali[mask] = subseq.length(); + } + } + + for (auto& m1 : pali) { + for (auto& m2 : pali) { + if ((m1.first & m2.first) == 0) { + res = max(res, m1.second * m2.second); + } + } + } + + return res; + } + +private: + bool isPal(const string& s) { + int i = 0, j = s.length() - 1; + while (i < j) { + if (s[i] != s[j]) { + return false; + } + i++; + j--; + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxProduct(s) { + const isPal = (str) => { + let i = 0, j = str.length - 1; + while (i < j) { + if (str[i] !== str[j]) return false; + i++; + j--; + } + return true; + }; + + const N = s.length; + let res = 0; + const pali = new Map(); + + for (let mask = 1; mask < (1 << N); mask++) { + let subseq = ""; + for (let i = 0; i < N; i++) { + if ((mask & (1 << i)) !== 0) { + subseq += s[i]; + } + } + + if (isPal(subseq)) { + pali.set(mask, subseq.length); + } + } + + for (let [m1, len1] of pali) { + for (let [m2, len2] of pali) { + if ((m1 & m2) === 0) { + res = Math.max(res, len1 * len2); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(4 ^ n)$ +* Space complexity: $O(2 ^ n)$ + +--- + +## 3. Bit Mask + Longest Pallindromic Subsequence + +::tabs-start + +```python +class Solution: + def longestPalindromeSubseq(self, s: str) -> int: + n = len(s) + if n == 0: + return 0 + + dp = [1] * n + for i in range(n - 1, -1, -1): + prev = 0 + for j in range(i + 1, n): + tmp = dp[j] + if s[i] == s[j]: + dp[j] = 2 + prev + else: + dp[j] = max(dp[j - 1], dp[j]) + prev = tmp + return dp[n - 1] + + def maxProduct(self, s: str) -> int: + n = len(s) + res = 0 + + for i in range(1, 1 << n): + seq1, seq2 = [], [] + + for j in range(n): + if (i & (1 << j)) != 0: + seq1.append(s[j]) + else: + seq2.append(s[j]) + + if not self.isPal(seq1): + continue + + lps = self.longestPalindromeSubseq(''.join(seq2)) + res = max(res, len(seq1) * lps) + + return res + + def isPal(self, s: str) -> bool: + i, j = 0, len(s) - 1 + while i < j: + if s[i] != s[j]: + return False + i += 1 + j -= 1 + return True +``` + +```java +public class Solution { + public int longestPalindromeSubseq(String s) { + int n = s.length(); + if (n == 0) return 0; + + int[] dp = new int[n]; + Arrays.fill(dp, 1); + for (int i = n - 1; i >= 0; i--) { + int prev = 0; + for (int j = i + 1; j < n; j++) { + int tmp = dp[j]; + if (s.charAt(i) == s.charAt(j)) { + dp[j] = 2 + prev; + } else { + dp[j] = Math.max(dp[j - 1], dp[j]); + } + prev = tmp; + } + } + return dp[n - 1]; + } + + public int maxProduct(String s) { + int n = s.length(); + int res = 0; + + for (int i = 1; i < (1 << n); i++) { + StringBuilder seq1 = new StringBuilder(), seq2 = new StringBuilder(); + + for (int j = 0; j < n; j++) { + char c = s.charAt(j); + if ((i & (1 << j)) != 0) seq1.append(c); + else seq2.append(c); + } + + if (!isPal(seq1.toString())) continue; + int lps = longestPalindromeSubseq(seq2.toString()); + res = Math.max(res, seq1.length() * lps); + } + + return res; + } + + private boolean isPal(String s) { + int i = 0, j = s.length() - 1; + while (i < j) { + if (s.charAt(i) != s.charAt(j)) { + return false; + } + i++; + j--; + } + return true; + } +} +``` + +```cpp +class Solution { +public: + int longestPalindromeSubseq(string& s) { + int n = s.length(); + if (n == 0) return 0; + + vector dp(n, 1); + for (int i = n - 1; i >= 0; i--) { + int prev = 0; + for (int j = i + 1; j < n; j++) { + int tmp = dp[j]; + if (s[i] == s[j]) { + dp[j] = 2 + prev; + } else { + dp[j] = max(dp[j - 1], dp[j]); + } + prev = tmp; + } + } + return dp[n - 1]; + } + + int maxProduct(string s) { + int n = s.length(); + int res = 0; + + for (int i = 1; i < (1 << n); i++) { + string seq1 = "", seq2 = ""; + + for (int j = 0; j < n; j++) { + if ((i & (1 << j)) != 0) seq1 += s[j]; + else seq2 += s[j]; + } + + if (!isPal(seq1)) continue; + + int lps = longestPalindromeSubseq(seq2); + res = max(res, int(seq1.length()) * lps); + } + + return res; + } + + bool isPal(string& s) { + int i = 0, j = s.length() - 1; + while (i < j) { + if (s[i] != s[j]) { + return false; + } + i++; + j--; + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + longestPalindromeSubseq(s) { + const n = s.length; + if (n === 0) return 0; + + const dp = new Array(n).fill(1); + for (let i = n - 1; i >= 0; i--) { + let prev = 0; + for (let j = i + 1; j < n; j++) { + let tmp = dp[j]; + if (s[i] === s[j]) { + dp[j] = 2 + prev; + } else { + dp[j] = Math.max(dp[j - 1], dp[j]); + } + prev = tmp; + } + } + return dp[n - 1]; + } + + /** + * @param {string} s + * @return {number} + */ + maxProduct(s) { + const n = s.length; + let res = 0; + + for (let i = 1; i < (1 << n); i++) { + let seq1 = "", seq2 = ""; + + for (let j = 0; j < n; j++) { + if ((i & (1 << j)) !== 0) seq1 += s[j]; + else seq2 += s[j]; + } + + if (!this.isPal(seq1)) continue; + + const lps = this.longestPalindromeSubseq(seq2); + res = Math.max(res, seq1.length * lps); + } + + return res; + } + + /** + * @param {string} s + * @return {boolean} + */ + isPal(s) { + let i = 0, j = s.length - 1; + while (i < j) { + if (s[i] !== s[j]) { + return false; + } + i++; + j--; + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * 2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Bit Mask + LPS (Optimal) + +::tabs-start + +```python +class Solution: + def longestPalindromeSubseq(self, s: str, mask: int, dp: List[int]) -> int: + n = len(s) + for i in range(n - 1, -1, -1): + if (mask & (1 << i)) != 0: + continue + + prev = 0 + for j in range(i + 1, n): + tmp = dp[j] + if (mask & (1 << j)) == 0 and s[i] == s[j]: + dp[j] = 2 + prev + else: + dp[j] = max(dp[j - 1], dp[j]) + prev = tmp + return dp[-1] + + def maxProduct(self, s: str) -> int: + n = len(s) + res = 0 + dp = [1] * n + + for i in range(1, 1 << n): + m1 = self.palsize(s, i) + if m1 == 0: + continue + + for j in range(n): + if (i & (1 << j)) == 0: + dp[j] = 1 + else: + dp[j] = 0 + + m2 = self.longestPalindromeSubseq(s, i, dp) + res = max(res, m1 * m2) + + return res + + def palsize(self, s: str, mask: int) -> int: + i, j = 0, len(s) - 1 + res = 0 + while i <= j: + if (mask & (1 << i)) == 0: + i += 1 + elif (mask & (1 << j)) == 0: + j -= 1 + else: + if s[i] != s[j]: + return 0 + res += 1 if i == j else 2 + i += 1 + j -= 1 + return res +``` + +```java +public class Solution { + public int longestPalindromeSubseq(String s, int mask, int[] dp) { + int n = s.length(); + for (int i = n - 1; i >= 0; i--) { + if ((mask & (1 << i)) != 0) continue; + + int prev = 0; + for (int j = i + 1; j < n; j++) { + int tmp = dp[j]; + if ((mask & (1 << j)) == 0 && s.charAt(i) == s.charAt(j)) { + dp[j] = 2 + prev; + } else { + dp[j] = Math.max(dp[j - 1], dp[j]); + } + prev = tmp; + } + } + return dp[n - 1]; + } + + public int maxProduct(String s) { + int n = s.length(); + int res = 0; + int[] dp = new int[n]; + + for (int i = 1; i < (1 << n); i++) { + int m1 = palsize(s, i); + if (m1 == 0) continue; + + for (int j = 0; j < n; j++) { + if ((i & (1 << j)) == 0) { + dp[j] = 1; + } else { + dp[j] = 0; + } + } + int m2 = longestPalindromeSubseq(s, i, dp); + res = Math.max(res, m1 * m2); + } + return res; + } + + public int palsize(String s, int mask) { + int i = 0, j = s.length() - 1; + int res = 0; + while (i <= j) { + if ((mask & (1 << i)) == 0) i++; + else if ((mask & (1 << j)) == 0) j--; + else { + if (s.charAt(i) != s.charAt(j)) return 0; + res += (i == j) ? 1 : 2; + i++; + j--; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int longestPalindromeSubseq(string& s, int mask, vector& dp) { + int n = s.length(); + for (int i = n - 1; i >= 0; i--) { + if ((mask & (1 << i)) != 0) continue; + + int prev = 0; + for (int j = i + 1; j < n; j++) { + int tmp = dp[j]; + if ((mask & (1 << j)) == 0 && s[i] == s[j]) { + dp[j] = 2 + prev; + } else { + dp[j] = max(dp[j - 1], dp[j]); + } + prev = tmp; + } + } + return dp[n - 1]; + } + + int maxProduct(string s) { + int n = s.length(); + int res = 0; + vector dp(n, 1); + + for (int i = 1; i < (1 << n); i++) { + int m1 = palsize(s, i); + if (m1 == 0) continue; + + for (int j = 0; j < n; j++) { + if ((i & (1 << j)) == 0) { + dp[j] = 1; + } else { + dp[j] = 0; + } + } + int m2 = longestPalindromeSubseq(s, i, dp); + res = max(res, m1 * m2); + } + + return res; + } + + int palsize(string& s, int mask) { + int i = 0, j = s.length() - 1; + int res = 0; + while (i <= j) { + if ((mask & (1 << i)) == 0) i++; + else if ((mask & (1 << j)) == 0) j--; + else { + if (s[i] != s[j]) return 0; + res += (i == j) ? 1 : 2; + i++; + j--; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} mask + * @param {number[]} dp + * @return {number} + */ + longestPalindromeSubseq(s, mask, dp) { + const n = s.length; + for (let i = n - 1; i >= 0; i--) { + if ((mask & (1 << i)) !== 0) continue; + + let prev = 0; + for (let j = i + 1; j < n; j++) { + const tmp = dp[j]; + if ((mask & (1 << j)) === 0 && s[i] === s[j]) { + dp[j] = 2 + prev; + } else { + dp[j] = Math.max(dp[j - 1], dp[j]); + } + prev = tmp; + } + } + return dp[n - 1]; + } + + /** + * @param {string} s + * @return {boolean} + */ + maxProduct(s) { + const n = s.length; + let res = 0; + const dp = Array(n).fill(1); + + for (let i = 1; i < (1 << n); i++) { + const m1 = this.palsize(s, i); + if (m1 === 0) continue; + + for (let j = 0; j < n; j++) { + if ((i & (1 << j)) === 0) { + dp[j] = 1; + } else { + dp[j] = 0; + } + } + const m2 = this.longestPalindromeSubseq(s, i, dp); + res = Math.max(res, m1 * m2); + } + return res; + } + + /** + * @param {string} s + * @param {number} mask + * @return {number} + */ + palsize(s, mask) { + let i = 0, j = s.length - 1; + let res = 0; + while (i <= j) { + if ((mask & (1 << i)) === 0) i++; + else if ((mask & (1 << j)) === 0) j--; + else { + if (s[i] !== s[j]) return 0; + res += (i === j) ? 1 : 2; + i++; + j--; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * 2 ^ n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/maximum-product-subarray.md b/articles/maximum-product-subarray.md new file mode 100644 index 000000000..f163b341f --- /dev/null +++ b/articles/maximum-product-subarray.md @@ -0,0 +1,905 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxProduct(self, nums: List[int]) -> int: + res = nums[0] + + for i in range(len(nums)): + cur = nums[i] + res = max(res, cur) + for j in range(i + 1, len(nums)): + cur *= nums[j] + res = max(res, cur) + + return res +``` + +```java +public class Solution { + public int maxProduct(int[] nums) { + int res = nums[0]; + + for (int i = 0; i < nums.length; i++) { + int cur = nums[i]; + res = Math.max(res, cur); + for (int j = i + 1; j < nums.length; j++) { + cur *= nums[j]; + res = Math.max(res, cur); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxProduct(vector& nums) { + int res = nums[0]; + + for (int i = 0; i < nums.size(); i++) { + int cur = nums[i]; + res = max(res, cur); + for (int j = i + 1; j < nums.size(); j++) { + cur *= nums[j]; + res = max(res, cur); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxProduct(nums) { + let res = nums[0]; + + for (let i = 0; i < nums.length; i++) { + let cur = nums[i]; + res = Math.max(res, cur); + for (let j = i + 1; j < nums.length; j++) { + cur *= nums[j]; + res = Math.max(res, cur); + } + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int MaxProduct(int[] nums) { + int res = nums[0]; + + for (int i = 0; i < nums.Length; i++) { + int cur = nums[i]; + res = Math.Max(res, cur); + for (int j = i + 1; j < nums.Length; j++) { + cur *= nums[j]; + res = Math.Max(res, cur); + } + } + + return res; + } +} +``` + +```go +func maxProduct(nums []int) int { + res := nums[0] + for i := 0; i < len(nums); i++ { + cur := nums[i] + res = max(res, cur) + for j := i + 1; j < len(nums); j++ { + cur *= nums[j] + res = max(res, cur) + } + } + return res +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun maxProduct(nums: IntArray): Int { + var res = nums[0] + for (i in nums.indices) { + var cur = nums[i] + res = maxOf(res, cur) + for (j in i + 1 until nums.size) { + cur *= nums[j] + res = maxOf(res, cur) + } + } + return res + } +} +``` + +```swift +class Solution { + func maxProduct(_ nums: [Int]) -> Int { + var res = nums[0] + + for i in 0.. int: + A = [] + cur = [] + res = float('-inf') + + for num in nums: + res = max(res, num) + if num == 0: + if cur: + A.append(cur) + cur = [] + else: + cur.append(num) + + if cur: + A.append(cur) + + for sub in A: + negs = sum(1 for i in sub if i < 0) + prod = 1 + need = negs if negs % 2 == 0 else negs - 1 + negs = 0 + j = 0 + + for i in range(len(sub)): + prod *= sub[i] + if sub[i] < 0: + negs += 1 + while negs > need: + prod //= sub[j] + if sub[j] < 0: + negs -= 1 + j += 1 + if j <= i: + res = max(res, prod) + + return res +``` + +```java +public class Solution { + public int maxProduct(int[] nums) { + List> A = new ArrayList<>(); + List cur = new ArrayList<>(); + int res = Integer.MIN_VALUE; + + for (int num : nums) { + res = Math.max(res, num); + if (num == 0) { + if (!cur.isEmpty()) A.add(cur); + cur = new ArrayList<>(); + } else cur.add(num); + } + if (!cur.isEmpty()) A.add(cur); + + for (List sub : A) { + int negs = 0; + for (int i : sub) { + if (i < 0) negs++; + } + + int prod = 1; + int need = (negs % 2 == 0) ? negs : (negs - 1); + negs = 0; + for (int i = 0, j = 0; i < sub.size(); i++) { + prod *= sub.get(i); + if (sub.get(i) < 0) { + negs++; + while (negs > need) { + prod /= sub.get(j); + if (sub.get(j) < 0) negs--; + j++; + } + } + if (j <= i) res = Math.max(res, prod); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxProduct(vector& nums) { + vector> A; + vector cur; + int res = INT_MIN; + for (auto& num : nums) { + res = max(res, num); + if (num == 0) { + if (!cur.empty()) A.push_back(cur); + cur.clear(); + } else cur.push_back(num); + } + if (!cur.empty()) { + A.push_back(cur); + } + + for (auto& sub : A) { + int negs = 0; + for (auto& i : sub) { + if (i < 0) negs++; + } + + int prod = 1; + int need = (negs % 2 == 0) ? negs : (negs - 1); + negs = 0; + for (int i = 0, j = 0; i < sub.size(); i++) { + prod *= sub[i]; + if (sub[i] < 0) { + negs++; + while (negs > need) { + prod /= sub[j]; + if (sub[j] < 0) negs--; + j++; + } + } + if (j <= i) res = max(res, prod); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxProduct(nums) { + let A = []; + let cur = []; + let res = -Infinity; + + nums.forEach(num => { + res = Math.max(res, num); + if (num === 0) { + if (cur.length) A.push(cur); + cur = []; + } else { + cur.push(num); + } + }); + if (cur.length) A.push(cur); + + A.forEach(sub => { + let negs = 0; + sub.forEach(i => { + if (i < 0) negs++; + }); + + let prod = 1; + let need = (negs % 2 === 0) ? negs : (negs - 1); + negs = 0; + for (let i = 0, j = 0; i < sub.length; i++) { + prod *= sub[i]; + if (sub[i] < 0) { + negs++; + while (negs > need) { + prod /= sub[j]; + if (sub[j] < 0) negs--; + j++; + } + } + if (j <= i) res = Math.max(res, prod); + } + }); + + return res; + } +} +``` + +```csharp +public class Solution { + public int MaxProduct(int[] nums) { + List> A = new List>(); + List cur = new List(); + int res = int.MinValue; + + foreach (int num in nums) { + res = Math.Max(res, num); + if (num == 0) { + if (cur.Count > 0) { + A.Add(new List(cur)); + } + cur.Clear(); + } else cur.Add(num); + } + if (cur.Count > 0) A.Add(new List(cur)); + + foreach (var sub in A) { + int negs = 0; + foreach (var i in sub) { + if (i < 0) negs++; + } + + int prod = 1; + int need = (negs % 2 == 0) ? negs : (negs - 1); + negs = 0; + for (int i = 0, j = 0; i < sub.Count; i++) { + prod *= sub[i]; + if (sub[i] < 0) { + negs++; + while (negs > need) { + prod /= sub[j]; + if (sub[j] < 0) negs--; + j++; + } + } + if (j <= i) res = Math.Max(res, prod); + } + } + return res; + } +} +``` + +```go +func maxProduct(nums []int) int { + res := math.MinInt32 + for _, num := range nums { + res = max(res, num) + } + + var A [][]int + var cur []int + for _, num := range nums { + if num == 0 { + if len(cur) > 0 { + A = append(A, cur) + } + cur = nil + } else { + cur = append(cur, num) + } + } + if len(cur) > 0 { + A = append(A, cur) + } + + for _, sub := range A { + negs := 0 + for _, i := range sub { + if i < 0 { + negs++ + } + } + prod := 1 + need := negs + if negs%2 != 0 { + need = negs - 1 + } + negs = 0 + j := 0 + for i := range sub { + prod *= sub[i] + if sub[i] < 0 { + negs++ + for negs > need { + prod /= sub[j] + if sub[j] < 0 { + negs-- + } + j++ + } + } + if j <= i { + res = max(res, prod) + } + } + } + return res +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun maxProduct(nums: IntArray): Int { + var res = Int.MIN_VALUE + for (num in nums) { + res = maxOf(res, num) + } + + val A = mutableListOf>() + var cur = mutableListOf() + for (num in nums) { + if (num == 0) { + if (cur.isNotEmpty()) { + A.add(cur.toMutableList()) + } + cur.clear() + } else { + cur.add(num) + } + } + if (cur.isNotEmpty()) { + A.add(cur.toMutableList()) + } + + for (sub in A) { + var negs = 0 + for (i in sub) { + if (i < 0) { + negs++ + } + } + var prod = 1 + var need = if (negs % 2 == 0) negs else negs - 1 + negs = 0 + var j = 0 + for (i in sub.indices) { + prod *= sub[i] + if (sub[i] < 0) { + negs++ + while (negs > need) { + prod /= sub[j] + if (sub[j] < 0) { + negs-- + } + j++ + } + } + if (j <= i) { + res = maxOf(res, prod) + } + } + } + return res + } +} +``` + +```swift +class Solution { + func maxProduct(_ nums: [Int]) -> Int { + var A = [[Int]]() + var cur = [Int]() + var res = Int.min + + for num in nums { + res = max(res, num) + if num == 0 { + if !cur.isEmpty { + A.append(cur) + } + cur = [] + } else { + cur.append(num) + } + } + + if !cur.isEmpty { + A.append(cur) + } + + for sub in A { + let negsCount = sub.filter { $0 < 0 }.count + var prod = 1 + var need = negsCount % 2 == 0 ? negsCount : negsCount - 1 + var negs = 0 + var j = 0 + + for i in 0.. need { + prod /= sub[j] + if sub[j] < 0 { + negs -= 1 + } + j += 1 + } + } + if j <= i { + res = max(res, prod) + } + } + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Kadane's Algorithm + +::tabs-start + +```python +class Solution: + def maxProduct(self, nums: List[int]) -> int: + res = nums[0] + curMin, curMax = 1, 1 + + for num in nums: + tmp = curMax * num + curMax = max(num * curMax, num * curMin, num) + curMin = min(tmp, num * curMin, num) + res = max(res, curMax) + return res +``` + +```java +public class Solution { + public int maxProduct(int[] nums) { + int res = nums[0]; + int curMin = 1, curMax = 1; + + for (int num : nums) { + int tmp = curMax * num; + curMax = Math.max(Math.max(num * curMax, num * curMin), num); + curMin = Math.min(Math.min(tmp, num * curMin), num); + res = Math.max(res, curMax); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxProduct(vector& nums) { + int res = nums[0]; + int curMin = 1, curMax = 1; + + for (int num : nums) { + int tmp = curMax * num; + curMax = max(max(num * curMax, num * curMin), num); + curMin = min(min(tmp, num * curMin), num); + res = max(res, curMax); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxProduct(nums) { + let res = nums[0]; + let curMin = 1; + let curMax = 1; + + for (const num of nums) { + const tmp = curMax * num; + curMax = Math.max(Math.max(num * curMax, num * curMin), num); + curMin = Math.min(Math.min(tmp, num * curMin), num); + res = Math.max(res, curMax); + } + return res; + } +} +``` + +```csharp +public class Solution { + public int MaxProduct(int[] nums) { + int res = nums[0]; + int curMin = 1, curMax = 1; + + foreach (int num in nums) { + int tmp = curMax * num; + curMax = Math.Max(Math.Max(num * curMax, num * curMin), num); + curMin = Math.Min(Math.Min(tmp, num * curMin), num); + res = Math.Max(res, curMax); + } + return res; + } +} +``` + +```go +func maxProduct(nums []int) int { + res := nums[0] + curMin, curMax := 1, 1 + for _, num := range nums { + tmp := curMax * num + curMax = max(num*curMax, max(num*curMin, num)) + curMin = min(tmp, min(num*curMin, num)) + res = max(res, curMax) + } + return res +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun maxProduct(nums: IntArray): Int { + var res = nums[0] + var curMin = 1 + var curMax = 1 + for (num in nums) { + val tmp = curMax * num + curMax = maxOf(num * curMax, maxOf(num * curMin, num)) + curMin = minOf(tmp, minOf(num * curMin, num)) + res = maxOf(res, curMax) + } + return res + } +} +``` + +```swift +class Solution { + func maxProduct(_ nums: [Int]) -> Int { + var res = nums[0] + var curMin = 1, curMax = 1 + + for num in nums { + let tmp = curMax * num + curMax = max(num * curMax, num * curMin, num) + curMin = min(tmp, num * curMin, num) + res = max(res, curMax) + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Prefix & Suffix + +::tabs-start + +```python +class Solution: + def maxProduct(self, nums: List[int]) -> int: + n, res = len(nums), nums[0] + prefix = suffix = 0 + + for i in range(n): + prefix = nums[i] * (prefix or 1) + suffix = nums[n - 1 - i] * (suffix or 1) + res = max(res, max(prefix, suffix)) + return res +``` + +```java +public class Solution { + public int maxProduct(int[] nums) { + int n = nums.length; + int res = nums[0]; + int prefix = 0, suffix = 0; + + for (int i = 0; i < n; i++) { + prefix = nums[i] * (prefix == 0 ? 1 : prefix); + suffix = nums[n - 1 - i] * (suffix == 0 ? 1 : suffix); + res = Math.max(res, Math.max(prefix, suffix)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxProduct(vector& nums) { + int n = nums.size(), res = nums[0]; + int prefix = 0, suffix = 0; + + for (int i = 0; i < n; i++) { + prefix = nums[i] * (prefix == 0 ? 1 : prefix); + suffix = nums[n - 1 - i] * (suffix == 0 ? 1 : suffix); + res = max(res, max(prefix, suffix)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxProduct(nums) { + let n = nums.length, res = nums[0]; + let prefix = 0, suffix = 0; + + for (let i = 0; i < n; i++) { + prefix = nums[i] * (prefix === 0 ? 1 : prefix); + suffix = nums[n - 1 - i] * (suffix === 0 ? 1 : suffix); + res = Math.max(res, Math.max(prefix, suffix)); + } + return res === -0 ? 0 : res; + } +} +``` + +```csharp +public class Solution { + public int MaxProduct(int[] nums) { + int n = nums.Length; + int res = nums[0]; + int prefix = 0, suffix = 0; + + for (int i = 0; i < n; i++) { + prefix = nums[i] * (prefix == 0 ? 1 : prefix); + suffix = nums[n - 1 - i] * (suffix == 0 ? 1 : suffix); + res = Math.Max(res, Math.Max(prefix, suffix)); + } + return res; + } +} +``` + +```go +func maxProduct(nums []int) int { + n := len(nums) + res := nums[0] + prefix, suffix := 0, 0 + + for i := 0; i < n; i++ { + if prefix == 0 { + prefix = 1 + } + if suffix == 0 { + suffix = 1 + } + + prefix *= nums[i] + suffix *= nums[n-1-i] + res = max(res, max(prefix, suffix)) + } + + return res +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun maxProduct(nums: IntArray): Int { + val n = nums.size + var res = nums[0] + var prefix = 0 + var suffix = 0 + + for (i in 0 until n) { + if (prefix == 0) prefix = 1 + if (suffix == 0) suffix = 1 + + prefix *= nums[i] + suffix *= nums[n - 1 - i] + res = maxOf(res, prefix, suffix) + } + + return res + } +} +``` + +```swift +class Solution { + func maxProduct(_ nums: [Int]) -> Int { + let n = nums.count + var res = nums[0] + var prefix = 0, suffix = 0 + + for i in 0.. int: + intervals = sorted(zip(startTime, endTime, profit)) + cache = {} + + def dfs(i): + if i == len(intervals): + return 0 + if i in cache: + return cache[i] + + # don't include + res = dfs(i + 1) + + # include + j = i + 1 + while j < len(intervals): + if intervals[i][1] <= intervals[j][0]: + break + j += 1 + + cache[i] = res = max(res, intervals[i][2] + dfs(j)) + return res + + return dfs(0) +``` + +```java +public class Solution { + private int[][] intervals; + private int[] cache; + + public int jobScheduling(int[] startTime, int[] endTime, int[] profit) { + int n = startTime.length; + intervals = new int[n][3]; + cache = new int[n]; + Arrays.fill(cache, -1); + + for (int i = 0; i < n; i++) { + intervals[i] = new int[]{startTime[i], endTime[i], profit[i]}; + } + Arrays.sort(intervals, Comparator.comparingInt(a -> a[0])); + + return dfs(0); + } + + private int dfs(int i) { + if (i == intervals.length) { + return 0; + } + if (cache[i] != -1) { + return cache[i]; + } + + // Don't include + int res = dfs(i + 1); + + // Include + int j = i + 1; + while (j < intervals.length && intervals[i][1] > intervals[j][0]) { + j++; + } + + return cache[i] = Math.max(res, intervals[i][2] + dfs(j)); + } +} +``` + +```cpp +class Solution { +public: + vector> intervals; + vector cache; + + int jobScheduling(vector& startTime, vector& endTime, vector& profit) { + int n = startTime.size(); + intervals.resize(n, vector(3)); + cache.assign(n, -1); + + for (int i = 0; i < n; i++) { + intervals[i] = {startTime[i], endTime[i], profit[i]}; + } + sort(intervals.begin(), intervals.end()); + + return dfs(0); + } + +private: + int dfs(int i) { + if (i == intervals.size()) { + return 0; + } + if (cache[i] != -1) { + return cache[i]; + } + + // Don't include + int res = dfs(i + 1); + + // Include + int j = i + 1; + while (j < intervals.size() && intervals[i][1] > intervals[j][0]) { + j++; + } + + return cache[i] = max(res, intervals[i][2] + dfs(j)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} startTime + * @param {number[]} endTime + * @param {number[]} profit + * @return {number} + */ + jobScheduling(startTime, endTime, profit) { + let n = startTime.length; + let intervals = new Array(n).fill(null).map((_, i) => + [startTime[i], endTime[i], profit[i]] + ); + intervals.sort((a, b) => a[0] - b[0]); + + let cache = new Array(n).fill(-1); + + const dfs = (i) => { + if (i === n) { + return 0; + } + if (cache[i] !== -1) { + return cache[i]; + } + + // Don't include + let res = dfs(i + 1); + + // Include + let j = i + 1; + while (j < n && intervals[i][1] > intervals[j][0]) { + j++; + } + + return (cache[i] = Math.max(res, intervals[i][2] + dfs(j))); + }; + + return dfs(0); + } +} +``` + +```csharp +public class Solution { + public int JobScheduling(int[] startTime, int[] endTime, int[] profit) { + int n = startTime.Length; + var intervals = new List<(int start, int end, int profit)>(); + + for (int i = 0; i < n; i++) { + intervals.Add((startTime[i], endTime[i], profit[i])); + } + + intervals.Sort((a, b) => a.start.CompareTo(b.start)); + Dictionary cache = new(); + + int Dfs(int i) { + if (i == n) return 0; + if (cache.ContainsKey(i)) return cache[i]; + + // Option 1: don't include + int res = Dfs(i + 1); + + // Option 2: include current job + int j = i + 1; + while (j < n && intervals[j].start < intervals[i].end) { + j++; + } + + res = Math.Max(res, intervals[i].profit + Dfs(j)); + cache[i] = res; + return res; + } + + return Dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + Binary Search + +::tabs-start + +```python +class Solution: + def jobScheduling(self, startTime: List[int], endTime: List[int], profit: List[int]) -> int: + intervals = sorted(zip(startTime, endTime, profit)) + cache = {} + + def dfs(i): + if i == len(intervals): + return 0 + if i in cache: + return cache[i] + + # don't include + res = dfs(i + 1) + + # include + j = bisect.bisect(intervals, (intervals[i][1], -1, -1)) + cache[i] = res = max(res, intervals[i][2] + dfs(j)) + return res + + return dfs(0) +``` + +```java +public class Solution { + private int[][] intervals; + private int[] cache; + + public int jobScheduling(int[] startTime, int[] endTime, int[] profit) { + int n = startTime.length; + intervals = new int[n][3]; + cache = new int[n]; + Arrays.fill(cache, -1); + + for (int i = 0; i < n; i++) { + intervals[i] = new int[]{startTime[i], endTime[i], profit[i]}; + } + Arrays.sort(intervals, Comparator.comparingInt(a -> a[0])); + + return dfs(0); + } + + private int dfs(int i) { + if (i == intervals.length) { + return 0; + } + if (cache[i] != -1) { + return cache[i]; + } + + int res = dfs(i + 1); + + int left = i + 1, right = intervals.length, j = intervals.length; + while (left < right) { + int mid = left + (right - left) / 2; + if (intervals[mid][0] >= intervals[i][1]) { + j = mid; + right = mid; + } else { + left = mid + 1; + } + } + + return cache[i] = Math.max(res, intervals[i][2] + dfs(j)); + } +} +``` + +```cpp +class Solution { +public: + vector> intervals; + vector cache; + + int jobScheduling(vector& startTime, vector& endTime, vector& profit) { + int n = startTime.size(); + intervals.resize(n, vector(3)); + cache.assign(n, -1); + + for (int i = 0; i < n; i++) { + intervals[i] = {startTime[i], endTime[i], profit[i]}; + } + sort(intervals.begin(), intervals.end()); + + return dfs(0); + } + +private: + int dfs(int i) { + if (i == intervals.size()) { + return 0; + } + if (cache[i] != -1) { + return cache[i]; + } + + int res = dfs(i + 1); + + int left = i + 1, right = intervals.size(), j = intervals.size(); + while (left < right) { + int mid = left + (right - left) / 2; + if (intervals[mid][0] >= intervals[i][1]) { + j = mid; + right = mid; + } else { + left = mid + 1; + } + } + + return cache[i] = max(res, intervals[i][2] + dfs(j)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} startTime + * @param {number[]} endTime + * @param {number[]} profit + * @return {number} + */ + jobScheduling(startTime, endTime, profit) { + let n = startTime.length; + let intervals = new Array(n).fill(null).map((_, i) => + [startTime[i], endTime[i], profit[i]] + ); + intervals.sort((a, b) => a[0] - b[0]); + + let cache = new Array(n).fill(-1); + + const dfs = (i) => { + if (i === n) { + return 0; + } + if (cache[i] !== -1) { + return cache[i]; + } + + let res = dfs(i + 1); + + let left = i + 1, right = n, j = n; + while (left < right) { + let mid = Math.floor((left + right) / 2); + if (intervals[mid][0] >= intervals[i][1]) { + j = mid; + right = mid; + } else { + left = mid + 1; + } + } + + return (cache[i] = Math.max(res, intervals[i][2] + dfs(j))); + }; + + return dfs(0); + } +} +``` + +```csharp +public class Solution { + private int[][] intervals; + private int[] cache; + + public int JobScheduling(int[] startTime, int[] endTime, int[] profit) { + int n = startTime.Length; + intervals = new int[n][]; + cache = new int[n]; + Array.Fill(cache, -1); + + for (int i = 0; i < n; i++) { + intervals[i] = new int[] { startTime[i], endTime[i], profit[i] }; + } + + Array.Sort(intervals, (a, b) => a[0].CompareTo(b[0])); + return Dfs(0); + } + + private int Dfs(int i) { + if (i == intervals.Length) return 0; + if (cache[i] != -1) return cache[i]; + + int res = Dfs(i + 1); + + int left = i + 1, right = intervals.Length, j = intervals.Length; + while (left < right) { + int mid = left + (right - left) / 2; + if (intervals[mid][0] >= intervals[i][1]) { + j = mid; + right = mid; + } else { + left = mid + 1; + } + } + + cache[i] = Math.Max(res, intervals[i][2] + Dfs(j)); + return cache[i]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Top-Down) + Binary Search (Optimal) + +::tabs-start + +```python +class Solution: + def jobScheduling(self, startTime: List[int], endTime: List[int], profit: List[int]) -> int: + n = len(startTime) + index = list(range(n)) + index.sort(key=lambda i: startTime[i]) + + cache = [-1] * n + + def dfs(i): + if i == n: + return 0 + if cache[i] != -1: + return cache[i] + + res = dfs(i + 1) + + left, right, j = i + 1, n, n + while left < right: + mid = (left + right) // 2 + if startTime[index[mid]] >= endTime[index[i]]: + j = mid + right = mid + else: + left = mid + 1 + + cache[i] = res = max(res, profit[index[i]] + dfs(j)) + return res + + return dfs(0) +``` + +```java +public class Solution { + private int[] startTime, endTime, profit, cache; + private Integer[] index; + private int n; + + public int jobScheduling(int[] startTime, int[] endTime, int[] profit) { + this.n = startTime.length; + this.startTime = startTime; + this.endTime = endTime; + this.profit = profit; + this.index = new Integer[n]; + this.cache = new int[n]; + Arrays.fill(cache, -1); + + for (int i = 0; i < n; i++) { + index[i] = i; + } + Arrays.sort(index, Comparator.comparingInt(i -> startTime[i])); + + return dfs(0); + } + + private int dfs(int i) { + if (i == n) { + return 0; + } + if (cache[i] != -1) { + return cache[i]; + } + + int res = dfs(i + 1); + + int left = i + 1, right = n, j = n; + while (left < right) { + int mid = left + (right - left) / 2; + if (startTime[index[mid]] >= endTime[index[i]]) { + j = mid; + right = mid; + } else { + left = mid + 1; + } + } + + return cache[i] = Math.max(res, profit[index[i]] + dfs(j)); + } +} +``` + +```cpp +class Solution { +public: + vector startTime, endTime, profit, index, cache; + int n; + + int jobScheduling(vector& startTime, vector& endTime, vector& profit) { + this->n = startTime.size(); + this->startTime = startTime; + this->endTime = endTime; + this->profit = profit; + this->index.resize(n); + this->cache.assign(n, -1); + + for (int i = 0; i < n; i++) { + index[i] = i; + } + sort(index.begin(), index.end(), [&](int i, int j) { + return startTime[i] < startTime[j]; + }); + + return dfs(0); + } + +private: + int dfs(int i) { + if (i == n) { + return 0; + } + if (cache[i] != -1) { + return cache[i]; + } + + int res = dfs(i + 1); + + int left = i + 1, right = n, j = n; + while (left < right) { + int mid = left + (right - left) / 2; + if (startTime[index[mid]] >= endTime[index[i]]) { + j = mid; + right = mid; + } else { + left = mid + 1; + } + } + + return cache[i] = max(res, profit[index[i]] + dfs(j)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} startTime + * @param {number[]} endTime + * @param {number[]} profit + * @return {number} + */ + jobScheduling(startTime, endTime, profit) { + let n = startTime.length; + let index = Array.from({ length: n }, (_, i) => i); + index.sort((a, b) => startTime[a] - startTime[b]); + + let cache = new Array(n).fill(-1); + + const dfs = (i) => { + if (i === n) { + return 0; + } + if (cache[i] !== -1) { + return cache[i]; + } + + let res = dfs(i + 1); + + let left = i + 1, right = n, j = n; + while (left < right) { + let mid = Math.floor((left + right) / 2); + if (startTime[index[mid]] >= endTime[index[i]]) { + j = mid; + right = mid; + } else { + left = mid + 1; + } + } + + return (cache[i] = Math.max(res, profit[index[i]] + dfs(j))); + }; + + return dfs(0); + } +} +``` + +```csharp +public class Solution { + private int[] startTime, endTime, profit, cache; + private int[] index; + private int n; + + public int JobScheduling(int[] startTime, int[] endTime, int[] profit) { + this.n = startTime.Length; + this.startTime = startTime; + this.endTime = endTime; + this.profit = profit; + this.index = new int[n]; + this.cache = new int[n]; + Array.Fill(cache, -1); + + for (int i = 0; i < n; i++) { + index[i] = i; + } + + Array.Sort(index, (a, b) => startTime[a].CompareTo(startTime[b])); + + return Dfs(0); + } + + private int Dfs(int i) { + if (i == n) return 0; + if (cache[i] != -1) return cache[i]; + + int res = Dfs(i + 1); + + int left = i + 1, right = n, j = n; + while (left < right) { + int mid = left + (right - left) / 2; + if (startTime[index[mid]] >= endTime[index[i]]) { + j = mid; + right = mid; + } else { + left = mid + 1; + } + } + + return cache[i] = Math.Max(res, profit[index[i]] + Dfs(j)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Bottom-Up) + Binary Search + +::tabs-start + +```python +class Solution: + def jobScheduling(self, startTime: List[int], endTime: List[int], profit: List[int]) -> int: + n = len(startTime) + index = list(range(n)) + index.sort(key=lambda i: startTime[i]) + + dp = [0] * (n + 1) + + for i in range(n - 1, -1, -1): + left, right, j = i + 1, n, n + while left < right: + mid = (left + right) // 2 + if startTime[index[mid]] >= endTime[index[i]]: + j = mid + right = mid + else: + left = mid + 1 + + dp[i] = max(dp[i + 1], profit[index[i]] + dp[j]) + + return dp[0] +``` + +```java +public class Solution { + public int jobScheduling(int[] startTime, int[] endTime, int[] profit) { + int n = startTime.length; + Integer[] index = new Integer[n]; + for (int i = 0; i < n; i++) index[i] = i; + Arrays.sort(index, Comparator.comparingInt(i -> startTime[i])); + + int[] dp = new int[n + 1]; + + for (int i = n - 1; i >= 0; i--) { + int left = i + 1, right = n, j = n; + while (left < right) { + int mid = left + (right - left) / 2; + if (startTime[index[mid]] >= endTime[index[i]]) { + j = mid; + right = mid; + } else { + left = mid + 1; + } + } + dp[i] = Math.max(dp[i + 1], profit[index[i]] + dp[j]); + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int jobScheduling(vector& startTime, vector& endTime, vector& profit) { + int n = startTime.size(); + vector index(n), dp(n + 1, 0); + for (int i = 0; i < n; i++) index[i] = i; + sort(index.begin(), index.end(), [&](int i, int j) { + return startTime[i] < startTime[j]; + }); + + for (int i = n - 1; i >= 0; i--) { + int left = i + 1, right = n, j = n; + while (left < right) { + int mid = left + (right - left) / 2; + if (startTime[index[mid]] >= endTime[index[i]]) { + j = mid; + right = mid; + } else { + left = mid + 1; + } + } + dp[i] = max(dp[i + 1], profit[index[i]] + dp[j]); + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} startTime + * @param {number[]} endTime + * @param {number[]} profit + * @return {number} + */ + jobScheduling(startTime, endTime, profit) { + let n = startTime.length; + let index = Array.from({ length: n }, (_, i) => i); + index.sort((a, b) => startTime[a] - startTime[b]); + + let dp = new Array(n + 1).fill(0); + + for (let i = n - 1; i >= 0; i--) { + let left = i + 1, right = n, j = n; + while (left < right) { + let mid = Math.floor((left + right) / 2); + if (startTime[index[mid]] >= endTime[index[i]]) { + j = mid; + right = mid; + } else { + left = mid + 1; + } + } + dp[i] = Math.max(dp[i + 1], profit[index[i]] + dp[j]); + } + + return dp[0]; + } +} +``` + +```csharp +public class Solution { + public int JobScheduling(int[] startTime, int[] endTime, int[] profit) { + int n = startTime.Length; + int[] index = new int[n]; + for (int i = 0; i < n; i++) index[i] = i; + + Array.Sort(index, (a, b) => startTime[a].CompareTo(startTime[b])); + + int[] dp = new int[n + 1]; + + for (int i = n - 1; i >= 0; i--) { + int left = i + 1, right = n, j = n; + while (left < right) { + int mid = left + (right - left) / 2; + if (startTime[index[mid]] >= endTime[index[i]]) { + j = mid; + right = mid; + } else { + left = mid + 1; + } + } + + dp[i] = Math.Max(dp[i + 1], profit[index[i]] + dp[j]); + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/maximum-score-after-splitting-a-string.md b/articles/maximum-score-after-splitting-a-string.md new file mode 100644 index 000000000..04d95fb31 --- /dev/null +++ b/articles/maximum-score-after-splitting-a-string.md @@ -0,0 +1,495 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxScore(self, s: str) -> int: + n, res = len(s), 0 + for i in range(1, n): + left_zero = 0 + for j in range(i): + if s[j] == '0': + left_zero += 1 + right_one = 0 + for j in range(i, n): + if s[j] == '1': + right_one += 1 + res = max(res, left_zero + right_one) + return res +``` + +```java +public class Solution { + public int maxScore(String s) { + int n = s.length(), res = 0; + for (int i = 1; i < n; i++) { + int leftZero = 0, rightOne = 0; + for (int j = 0; j < i; j++) { + if (s.charAt(j) == '0') { + leftZero++; + } + } + for (int j = i; j < n; j++) { + if (s.charAt(j) == '1') { + rightOne++; + } + } + res = Math.max(res, leftZero + rightOne); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxScore(string s) { + int n = s.size(), res = 0; + for (int i = 1; i < n; i++) { + int leftZero = 0, rightOne = 0; + for (int j = 0; j < i; j++) { + if (s[j] == '0') { + leftZero++; + } + } + for (int j = i; j < n; j++) { + if (s[j] == '1') { + rightOne++; + } + } + res = max(res, leftZero + rightOne); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxScore(s) { + const n = s.length; + let res = 0; + for (let i = 1; i < n; i++) { + let leftZero = 0, rightOne = 0; + for (let j = 0; j < i; j++) { + if (s[j] === '0') { + leftZero++; + } + } + for (let j = i; j < n; j++) { + if (s[j] === '1') { + rightOne++; + } + } + res = Math.max(res, leftZero + rightOne); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Prefix & Suffix Arrays + +::tabs-start + +```python +class Solution: + def maxScore(self, s: str) -> int: + n = len(s) + left_zero = [0] * n + right_one = [0] * n + + if s[0] == '0': + left_zero[0] = 1 + for i in range(1, n): + left_zero[i] = left_zero[i - 1] + if s[i] == '0': + left_zero[i] += 1 + + if s[n - 1] == '1': + right_one[n - 1] = 1 + for i in range(n - 2, -1, -1): + right_one[i] = right_one[i + 1] + if s[i] == '1': + right_one[i] += 1 + + res = 0 + for i in range(1, n): + res = max(res, left_zero[i - 1] + right_one[i]) + return res +``` + +```java +public class Solution { + public int maxScore(String s) { + int n = s.length(); + int[] leftZero = new int[n]; + int[] rightOne = new int[n]; + + if (s.charAt(0) == '0') { + leftZero[0] = 1; + } + for (int i = 1; i < n; i++) { + leftZero[i] = leftZero[i - 1]; + if (s.charAt(i) == '0') { + leftZero[i]++; + } + } + + if (s.charAt(n - 1) == '1') { + rightOne[n - 1] = 1; + } + for (int i = n - 2; i >= 0; i--) { + rightOne[i] = rightOne[i + 1]; + if (s.charAt(i) == '1') { + rightOne[i]++; + } + } + + int res = 0; + for (int i = 1; i < n; i++) { + res = Math.max(res, leftZero[i - 1] + rightOne[i]); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxScore(string s) { + int n = s.size(); + vector leftZero(n, 0), rightOne(n, 0); + + if (s[0] == '0') { + leftZero[0] = 1; + } + for (int i = 1; i < n; i++) { + leftZero[i] = leftZero[i - 1]; + if (s[i] == '0') { + leftZero[i]++; + } + } + + if (s[n - 1] == '1') { + rightOne[n - 1] = 1; + } + for (int i = n - 2; i >= 0; i--) { + rightOne[i] = rightOne[i + 1]; + if (s[i] == '1') { + rightOne[i]++; + } + } + + int res = 0; + for (int i = 1; i < n; i++) { + res = max(res, leftZero[i - 1] + rightOne[i]); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxScore(s) { + const n = s.length; + let leftZero = new Array(n).fill(0); + let rightOne = new Array(n).fill(0); + + if (s[0] === '0') { + leftZero[0] = 1; + } + for (let i = 1; i < n; i++) { + leftZero[i] = leftZero[i - 1]; + if (s[i] === '0') { + leftZero[i]++; + } + } + + if (s[n - 1] === '1') { + rightOne[n - 1] = 1; + } + for (let i = n - 2; i >= 0; i--) { + rightOne[i] = rightOne[i + 1]; + if (s[i] === '1') { + rightOne[i]++; + } + } + + let res = 0; + for (let i = 1; i < n; i++) { + res = Math.max(res, leftZero[i - 1] + rightOne[i]); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Iteration (Two Pass) + +::tabs-start + +```python +class Solution: + def maxScore(self, s: str) -> int: + zero = 0 + one = s.count('1') + res = 0 + + for i in range(len(s) - 1): + if s[i] == '0': + zero += 1 + else: + one -= 1 + res = max(res, zero + one) + + return res +``` + +```java +public class Solution { + public int maxScore(String s) { + int zero = 0, one = 0, res = 0; + + for (char c : s.toCharArray()) { + if (c == '1') { + one++; + } + } + + for (int i = 0; i < s.length() - 1; i++) { + if (s.charAt(i) == '0') { + zero++; + } else { + one--; + } + res = Math.max(res, zero + one); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxScore(string s) { + int zero = 0, one = 0, res = 0; + + for (char c : s) { + if (c == '1') { + one++; + } + } + + for (int i = 0; i < s.size() - 1; i++) { + if (s[i] == '0') { + zero++; + } else { + one--; + } + res = max(res, zero + one); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxScore(s) { + let zero = 0, one = 0, res = 0; + + for (const c of s) { + if (c === '1') { + one++; + } + } + + for (let i = 0; i < s.length - 1; i++) { + if (s[i] === '0') { + zero++; + } else { + one--; + } + res = Math.max(res, zero + one); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Iteration (One Pass) + +::tabs-start + +```python +class Solution: + def maxScore(self, s: str) -> int: + # res = Max of all (left_zeros + right_ones) + # res = Max of all (left_zeros + (total_ones - left_ones)) + # res = total_ones (constant) + Max of all (left_zeros - left_ones) + + zeros = 0 + ones = 0 + + if s[0] == '0': + zeros += 1 + else: + ones += 1 + + res = float('-inf') + for i in range(1, len(s)): + res = max(res, zeros - ones) + if s[i] == '0': + zeros += 1 + else: + ones += 1 + + return res + ones +``` + +```java +public class Solution { + public int maxScore(String s) { + // res = Max of all (leftZeros + rightOnes) + // res = Max of all (leftZeros + (totalOnes - leftOnes)) + // res = totalOnes (constant) + Max of all (leftZeros - leftOnes) + + int zeros = 0, ones = 0, res = Integer.MIN_VALUE; + + if (s.charAt(0) == '0') { + zeros++; + } else { + ones++; + } + + for (int i = 1; i < s.length(); i++) { + res = Math.max(res, zeros - ones); + if (s.charAt(i) == '0') { + zeros++; + } else { + ones++; + } + } + + return res + ones; + } +} +``` + +```cpp +class Solution { +public: + int maxScore(string s) { + // res = Max of all (leftZeros + rightOnes) + // res = Max of all (leftZeros + (totalOnes - leftOnes)) + // res = totalOnes (constant) + Max of all (leftZeros - leftOnes) + + int zeros = 0, ones = 0, res = INT_MIN; + + if (s[0] == '0') { + zeros++; + } else { + ones++; + } + + for (int i = 1; i < s.size(); i++) { + res = max(res, zeros - ones); + if (s[i] == '0') { + zeros++; + } else { + ones++; + } + } + + return res + ones; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + maxScore(s) { + // res = Max of all (leftZeros + rightOnes) + // res = Max of all (leftZeros + (totalOnes - leftOnes)) + // res = totalOnes (constant) + Max of all (leftZeros - leftOnes) + + let zeros = 0, ones = 0, res = -Infinity; + + if (s[0] === '0') { + zeros++; + } else { + ones++; + } + + for (let i = 1; i < s.length; i++) { + res = Math.max(res, zeros - ones); + if (s[i] === '0') { + zeros++; + } else { + ones++; + } + } + + return res + ones; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/maximum-score-of-a-good-subarray.md b/articles/maximum-score-of-a-good-subarray.md new file mode 100644 index 000000000..f8b6fbdc0 --- /dev/null +++ b/articles/maximum-score-of-a-good-subarray.md @@ -0,0 +1,729 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maximumScore(self, nums: List[int], k: int) -> int: + n, res = len(nums), 0 + + for i in range(k + 1): + minEle = nums[i] + for j in range(i, n): + minEle = min(minEle, nums[j]) + if j >= k: + res = max(res, minEle * (j - i + 1)) + + return res +``` + +```java +public class Solution { + public int maximumScore(int[] nums, int k) { + int n = nums.length, res = 0; + + for (int i = 0; i <= k; i++) { + int minEle = nums[i]; + for (int j = i; j < n; j++) { + minEle = Math.min(minEle, nums[j]); + if (j >= k) { + res = Math.max(res, minEle * (j - i + 1)); + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maximumScore(vector& nums, int k) { + int n = nums.size(), res = 0; + + for (int i = 0; i <= k; i++) { + int minEle = nums[i]; + for (int j = i; j < n; j++) { + minEle = min(minEle, nums[j]); + if (j >= k) { + res = max(res, minEle * (j - i + 1)); + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maximumScore(nums, k) { + let n = nums.length, res = 0; + + for (let i = 0; i <= k; i++) { + let minEle = nums[i]; + for (let j = i; j < n; j++) { + minEle = Math.min(minEle, nums[j]); + if (j >= k) { + res = Math.max(res, minEle * (j - i + 1)); + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Binary Search + +::tabs-start + +```python +class Solution: + def maximumScore(self, nums: List[int], k: int) -> int: + n, res = len(nums), 0 + arr = nums[:] + + for i in range(k - 1, -1, -1): + arr[i] = min(arr[i], arr[i + 1]) + for i in range(k + 1, n): + arr[i] = min(arr[i], arr[i - 1]) + + left_arr = arr[:k+1] + right_arr = arr[k:] + + def find_right(target): + lo, hi = 0, len(right_arr) - 1 + pos = 0 + while lo <= hi: + mid = (lo + hi) // 2 + if right_arr[mid] >= target: + pos = mid + lo = mid + 1 + else: + hi = mid - 1 + return pos + + for minVal in set(arr): + l = bisect_left(left_arr, minVal) + r = find_right(minVal) + res = max(res, minVal * (k - l + 1 + r)) + return res +``` + +```java +public class Solution { + public int maximumScore(int[] nums, int k) { + int n = nums.length, res = 0; + int[] arr = Arrays.copyOf(nums, n); + Set candidates = new HashSet<>(); + candidates.add(arr[k]); + + for (int i = k - 1; i >= 0; i--) { + arr[i] = Math.min(arr[i], arr[i + 1]); + candidates.add(arr[i]); + } + for (int i = k + 1; i < n; i++) { + arr[i] = Math.min(arr[i], arr[i - 1]); + candidates.add(arr[i]); + } + + int[] leftArr = Arrays.copyOfRange(arr, 0, k + 1); + int[] rightArr = Arrays.copyOfRange(arr, k, n); + + for (int minVal : candidates) { + int l = findLeft(leftArr, minVal); + int r = findRight(rightArr, minVal); + res = Math.max(res, minVal * (k - l + 1 + r)); + } + return res; + } + + private int findLeft(int[] arr, int target) { + int lo = 0, hi = arr.length - 1; + while (lo <= hi) { + int mid = (lo + hi) / 2; + if (arr[mid] < target) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return lo; + } + + private int findRight(int[] arr, int target) { + int lo = 0, hi = arr.length - 1, pos = 0; + while (lo <= hi) { + int mid = (lo + hi) / 2; + if (arr[mid] >= target) { + pos = mid; + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return pos; + } +} +``` + +```cpp +class Solution { +public: + int maximumScore(vector& nums, int k) { + int n = nums.size(), res = 0; + vector arr = nums; + + for (int i = k - 1; i >= 0; i--) { + arr[i] = min(arr[i], arr[i + 1]); + } + for (int i = k + 1; i < n; i++) { + arr[i] = min(arr[i], arr[i - 1]); + } + + vector leftArr(arr.begin(), arr.begin() + k + 1); + vector rightArr(arr.begin() + k, arr.end()); + + set candidates(arr.begin(), arr.end()); + for (int minVal : candidates) { + int l = lower_bound(leftArr.begin(), leftArr.end(), minVal) - leftArr.begin(); + int r = findRight(rightArr, minVal); + res = max(res, minVal * (k - l + 1 + r)); + } + return res; + } + +private: + int findRight(vector& arr, int target) { + int lo = 0, hi = arr.size() - 1, pos = 0; + while (lo <= hi) { + int mid = (lo + hi) / 2; + if (arr[mid] >= target) { + pos = mid; + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return pos; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maximumScore(nums, k) { + let n = nums.length, res = 0; + let arr = [...nums]; + + for (let i = k - 1; i >= 0; i--) { + arr[i] = Math.min(arr[i], arr[i + 1]); + } + for (let i = k + 1; i < n; i++) { + arr[i] = Math.min(arr[i], arr[i - 1]); + } + + let leftArr = arr.slice(0, k + 1); + let rightArr = arr.slice(k); + + const findLeft = (target) => { + let lo = 0, hi = leftArr.length - 1; + while (lo <= hi) { + let mid = Math.floor((lo + hi) / 2); + if (leftArr[mid] < target) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return lo; + }; + + const findRight = (target) => { + let lo = 0, hi = rightArr.length - 1, pos = 0; + while (lo <= hi) { + let mid = Math.floor((lo + hi) / 2); + if (rightArr[mid] >= target) { + pos = mid; + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return pos; + }; + + let candidates = [...new Set(arr)]; + for (let minVal of candidates) { + let l = findLeft(minVal); + let r = findRight(minVal); + res = Math.max(res, minVal * (k - l + 1 + r)); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Binary Search (Overwriting the Input) + +::tabs-start + +```python +class Solution: + def maximumScore(self, nums: List[int], k: int) -> int: + n, res = len(nums), 0 + + for i in range(k - 1, -1, -1): + nums[i] = min(nums[i], nums[i + 1]) + for i in range(k + 1, n): + nums[i] = min(nums[i], nums[i - 1]) + + def find_left(target): + lo, hi = 0, k + while lo <= hi: + mid = (lo + hi) // 2 + if nums[mid] < target: + lo = mid + 1 + else: + hi = mid - 1 + return lo + + def find_right(target): + lo, hi = k, n - 1 + while lo <= hi: + mid = (lo + hi) // 2 + if nums[mid] >= target: + lo = mid + 1 + else: + hi = mid - 1 + return hi + + for minVal in set(nums): + i = find_left(minVal) + j = find_right(minVal) + res = max(res, minVal * (j - i + 1)) + return res +``` + +```java +public class Solution { + public int maximumScore(int[] nums, int k) { + int n = nums.length, res = 0; + + for (int i = k - 1; i >= 0; i--) { + nums[i] = Math.min(nums[i], nums[i + 1]); + } + for (int i = k + 1; i < n; i++) { + nums[i] = Math.min(nums[i], nums[i - 1]); + } + + Set candidates = new TreeSet<>(); + for (int num : nums) { + candidates.add(num); + } + + for (int minVal : candidates) { + int i = findLeft(nums, k, minVal); + int j = findRight(nums, k, minVal); + res = Math.max(res, minVal * (j - i + 1)); + } + return res; + } + + int findLeft(int[] nums, int k, int target) { + int lo = 0, hi = k; + while (lo <= hi) { + int mid = (lo + hi) / 2; + if (nums[mid] < target) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return lo; + } + + int findRight(int[] nums, int k, int target) { + int lo = k, hi = nums.length - 1; + while (lo <= hi) { + int mid = (lo + hi) / 2; + if (nums[mid] >= target) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return hi; + } +} +``` + +```cpp +class Solution { +public: + int maximumScore(vector& nums, int k) { + int n = nums.size(), res = 0; + + for (int i = k - 1; i >= 0; i--) { + nums[i] = min(nums[i], nums[i + 1]); + } + for (int i = k + 1; i < n; i++) { + nums[i] = min(nums[i], nums[i - 1]); + } + + auto findLeft = [&](int target) { + int lo = 0, hi = k; + while (lo <= hi) { + int mid = (lo + hi) / 2; + if (nums[mid] < target) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return lo; + }; + + auto findRight = [&](int target) { + int lo = k, hi = n - 1; + while (lo <= hi) { + int mid = (lo + hi) / 2; + if (nums[mid] >= target) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return hi; + }; + + set candidates(nums.begin(), nums.end()); + for (int minVal : candidates) { + int i = findLeft(minVal); + int j = findRight(minVal); + res = max(res, minVal * (j - i + 1)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maximumScore(nums, k) { + let n = nums.length, res = 0; + + for (let i = k - 1; i >= 0; i--) { + nums[i] = Math.min(nums[i], nums[i + 1]); + } + for (let i = k + 1; i < n; i++) { + nums[i] = Math.min(nums[i], nums[i - 1]); + } + + const findLeft = (target) => { + let lo = 0, hi = k; + while (lo <= hi) { + let mid = Math.floor((lo + hi) / 2); + if (nums[mid] < target) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return lo; + }; + + const findRight = (target) => { + let lo = k, hi = n - 1; + while (lo <= hi) { + let mid = Math.floor((lo + hi) / 2); + if (nums[mid] >= target) { + lo = mid + 1; + } else { + hi = mid - 1; + } + } + return hi; + }; + + let candidates = new Set(nums); + for (let minVal of candidates) { + let i = findLeft(minVal); + let j = findRight(minVal); + res = Math.max(res, minVal * (j - i + 1)); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Monotonic Stack + +::tabs-start + +```python +class Solution: + def maximumScore(self, nums: List[int], k: int) -> int: + n, res = len(nums), 0 + stack = [] + + for i in range(n + 1): + while stack and (i == n or nums[stack[-1]] >= nums[i]): + mini = nums[stack.pop()] + j = stack[-1] if stack else -1 + if j < k < i: + res = max(res, mini * (i - j - 1)) + stack.append(i) + + return res +``` + +```java +public class Solution { + public int maximumScore(int[] nums, int k) { + int n = nums.length, res = 0; + Stack stack = new Stack<>(); + + for (int i = 0; i <= n; i++) { + while (!stack.isEmpty() && (i == n || nums[stack.peek()] >= nums[i])) { + int mini = nums[stack.pop()]; + int j = stack.isEmpty() ? -1 : stack.peek(); + if (j < k && k < i) { + res = Math.max(res, mini * (i - j - 1)); + } + } + stack.push(i); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maximumScore(vector& nums, int k) { + int n = nums.size(), res = 0; + stack stk; + + for (int i = 0; i <= n; i++) { + while (!stk.empty() && (i == n || nums[stk.top()] >= nums[i])) { + int mini = nums[stk.top()]; + stk.pop(); + int j = stk.empty() ? -1 : stk.top(); + if (j < k && k < i) { + res = max(res, mini * (i - j - 1)); + } + } + stk.push(i); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maximumScore(nums, k) { + let n = nums.length, res = 0; + let stack = []; + + for (let i = 0; i <= n; i++) { + while (stack.length && (i === n || nums[stack[stack.length - 1]] >= nums[i])) { + let mini = nums[stack.pop()]; + let j = stack.length ? stack[stack.length - 1] : -1; + if (j < k && k < i) { + res = Math.max(res, mini * (i - j - 1)); + } + } + stack.push(i); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Greedy + Two Pointers + +::tabs-start + +```python +class Solution: + def maximumScore(self, nums: List[int], k: int) -> int: + l = r = k + res = nums[k] + cur_min = nums[k] + + while l > 0 or r < len(nums) - 1: + left = nums[l - 1] if l > 0 else 0 + right = nums[r + 1] if r < len(nums) - 1 else 0 + + if left > right: + l -= 1 + cur_min = min(cur_min, left) + else: + r += 1 + cur_min = min(cur_min, right) + + res = max(res, cur_min * (r - l + 1)) + + return res +``` + +```java +public class Solution { + public int maximumScore(int[] nums, int k) { + int l = k, r = k; + int res = nums[k]; + int curMin = nums[k]; + int n = nums.length; + + while (l > 0 || r < n - 1) { + int left = (l > 0) ? nums[l - 1] : 0; + int right = (r < n - 1) ? nums[r + 1] : 0; + + if (left > right) { + l--; + curMin = Math.min(curMin, left); + } else { + r++; + curMin = Math.min(curMin, right); + } + + res = Math.max(res, curMin * (r - l + 1)); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maximumScore(vector& nums, int k) { + int l = k, r = k; + int res = nums[k]; + int curMin = nums[k]; + int n = nums.size(); + + while (l > 0 || r < n - 1) { + int left = (l > 0) ? nums[l - 1] : 0; + int right = (r < n - 1) ? nums[r + 1] : 0; + + if (left > right) { + l--; + curMin = min(curMin, left); + } else { + r++; + curMin = min(curMin, right); + } + + res = max(res, curMin * (r - l + 1)); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + maximumScore(nums, k) { + let l = k, r = k; + let res = nums[k]; + let curMin = nums[k]; + let n = nums.length; + + while (l > 0 || r < n - 1) { + let left = (l > 0) ? nums[l - 1] : 0; + let right = (r < n - 1) ? nums[r + 1] : 0; + + if (left > right) { + l--; + curMin = Math.min(curMin, left); + } else { + r++; + curMin = Math.min(curMin, right); + } + + res = Math.max(res, curMin * (r - l + 1)); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/maximum-score-words-formed-by-letters.md b/articles/maximum-score-words-formed-by-letters.md new file mode 100644 index 000000000..f9b510481 --- /dev/null +++ b/articles/maximum-score-words-formed-by-letters.md @@ -0,0 +1,676 @@ +## 1. Backtracking + +::tabs-start + +```python +class Solution: + def maxScoreWords(self, words: List[str], letters: List[str], score: List[int]) -> int: + def can_form_word(w, letter_cnt): + word_cnt = Counter(w) + for c in word_cnt: + if word_cnt[c] > letter_cnt[c]: + return False + return True + + def get_score(w): + res = 0 + for c in w: + res += score[ord(c) - ord('a')] + return res + + letter_cnt = Counter(letters) + + def backtrack(i): + if i == len(words): + return 0 + + res = backtrack(i + 1) # skip + if can_form_word(words[i], letter_cnt): # include (when possible) + for c in words[i]: + letter_cnt[c] -= 1 + res = max(res, get_score(words[i]) + backtrack(i + 1)) + for c in words[i]: + letter_cnt[c] += 1 + + return res + + return backtrack(0) +``` + +```java +public class Solution { + int[] letterCnt; + int[] score; + + public int maxScoreWords(String[] words, char[] letters, int[] score) { + this.score = score; + this.letterCnt = new int[26]; + + for (char c : letters) { + letterCnt[c - 'a']++; + } + + return backtrack(0, words); + } + + private int canFormWord(String word) { + int[] wordCnt = new int[26]; + for (char c : word.toCharArray()) { + wordCnt[c - 'a']++; + if (wordCnt[c - 'a'] > letterCnt[c - 'a']) { + return 0; + } + } + return 1; + } + + private int getScore(String word) { + int res = 0; + for (char c : word.toCharArray()) { + res += score[c - 'a']; + } + return res; + } + + private int backtrack(int i, String[] words) { + if (i == words.length) { + return 0; + } + + int res = backtrack(i + 1, words); // skip + if (canFormWord(words[i]) == 1) { // include (when possible) + for (char c : words[i].toCharArray()) { + letterCnt[c - 'a']--; + } + res = Math.max(res, getScore(words[i]) + backtrack(i + 1, words)); + for (char c : words[i].toCharArray()) { + letterCnt[c - 'a']++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector letterCnt = vector(26, 0); + vector score; + vector words; + + int maxScoreWords(vector& words, vector& letters, vector& score) { + this->words = words; + this->score = score; + + for (char c : letters) { + letterCnt[c - 'a']++; + } + + return backtrack(0); + } + + bool canFormWord(string& word) { + vector wordCnt(26, 0); + for (char c : word) { + wordCnt[c - 'a']++; + if (wordCnt[c - 'a'] > letterCnt[c - 'a']) { + return false; + } + } + return true; + } + + int getScore(string& word) { + int res = 0; + for (char c : word) { + res += score[c - 'a']; + } + return res; + } + + int backtrack(int i) { + if (i == words.size()) { + return 0; + } + + int res = backtrack(i + 1); // skip + if (canFormWord(words[i])) { // include (when possible) + for (char c : words[i]) { + letterCnt[c - 'a']--; + } + res = max(res, getScore(words[i]) + backtrack(i + 1)); + for (char c : words[i]) { + letterCnt[c - 'a']++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string[]} letters + * @param {number[]} score + * @return {number} + */ + maxScoreWords(words, letters, score) { + const canFormWord = (w, letterCnt) => { + let wordCnt = new Array(26).fill(0); + for (let c of w) { + let idx = c.charCodeAt(0) - 'a'.charCodeAt(0); + wordCnt[idx]++; + if (wordCnt[idx] > letterCnt[idx]) { + return false; + } + } + return true; + }; + + const getScore = (w) => { + let res = 0; + for (let c of w) { + res += score[c.charCodeAt(0) - 'a'.charCodeAt(0)]; + } + return res; + }; + + let letterCnt = new Array(26).fill(0); + for (let c of letters) { + letterCnt[c.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + + const backtrack = (i) => { + if (i === words.length) { + return 0; + } + + let res = backtrack(i + 1); // skip + + if (canFormWord(words[i], letterCnt)) { // include (when possible) + for (let c of words[i]) { + letterCnt[c.charCodeAt(0) - 'a'.charCodeAt(0)]--; + } + res = Math.max(res, getScore(words[i]) + backtrack(i + 1)); + for (let c of words[i]) { + letterCnt[c.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + } + + return res; + }; + + return backtrack(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n * (w + m) + N)$ +* Space complexity: $O(n + w)$ + +> Where $n$ is the number of words, $w$ is the maximum length of a word, $m$ is the size of the array $scores$, and $N$ is the size of the array $letters$. + +--- + +## 2. Bactracking + Precomputation + +::tabs-start + +```python +class Solution: + def maxScoreWords(self, words: List[str], letters: List[str], score: List[int]) -> int: + letter_cnt = [0] * 26 + for c in letters: + letter_cnt[ord(c) - ord('a')] += 1 + + n = len(words) + word_scores = [0] * n + word_freqs = [[0] * 26 for _ in range(n)] + + for i, word in enumerate(words): + for c in word: + idx = ord(c) - ord('a') + word_freqs[i][idx] += 1 + word_scores[i] += score[idx] + + def backtrack(i): + if i == n: + return 0 + + res = backtrack(i + 1) # skip + can_include = all(word_freqs[i][j] <= letter_cnt[j] for j in range(26)) + + if can_include: # include (when possible) + for j in range(26): + letter_cnt[j] -= word_freqs[i][j] + res = max(res, word_scores[i] + backtrack(i + 1)) + for j in range(26): + letter_cnt[j] += word_freqs[i][j] + + return res + + return backtrack(0) +``` + +```java +public class Solution { + private int[] letterCnt = new int[26]; + private int[] wordScores; + private int[][] wordFreqs; + private int n; + + public int maxScoreWords(String[] words, char[] letters, int[] score) { + Arrays.fill(letterCnt, 0); + for (char c : letters) { + letterCnt[c - 'a']++; + } + + n = words.length; + wordScores = new int[n]; + wordFreqs = new int[n][26]; + + for (int i = 0; i < n; i++) { + for (char c : words[i].toCharArray()) { + int idx = c - 'a'; + wordFreqs[i][idx]++; + wordScores[i] += score[idx]; + } + } + + return backtrack(0, words); + } + + private int backtrack(int i, String[] words) { + if (i == n) { + return 0; + } + + int res = backtrack(i + 1, words); // skip + boolean canInclude = true; + + for (int j = 0; j < 26; j++) { + if (wordFreqs[i][j] > letterCnt[j]) { + canInclude = false; + break; + } + } + + if (canInclude) { // include (when possible) + for (int j = 0; j < 26; j++) { + letterCnt[j] -= wordFreqs[i][j]; + } + res = Math.max(res, wordScores[i] + backtrack(i + 1, words)); + for (int j = 0; j < 26; j++) { + letterCnt[j] += wordFreqs[i][j]; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector letterCnt = vector(26, 0); + vector wordScores; + vector> wordFreqs; + int n; + + int maxScoreWords(vector& words, vector& letters, vector& score) { + fill(letterCnt.begin(), letterCnt.end(), 0); + for (char c : letters) { + letterCnt[c - 'a']++; + } + + n = words.size(); + wordScores = vector(n, 0); + wordFreqs = vector>(n, vector(26, 0)); + + for (int i = 0; i < n; i++) { + for (char c : words[i]) { + int idx = c - 'a'; + wordFreqs[i][idx]++; + wordScores[i] += score[idx]; + } + } + + return backtrack(0, words); + } + +private: + int backtrack(int i, vector& words) { + if (i == n) { + return 0; + } + + int res = backtrack(i + 1, words); // skip + bool canInclude = true; + + for (int j = 0; j < 26; j++) { + if (wordFreqs[i][j] > letterCnt[j]) { + canInclude = false; + break; + } + } + + if (canInclude) { // include (when possible) + for (int j = 0; j < 26; j++) { + letterCnt[j] -= wordFreqs[i][j]; + } + res = max(res, wordScores[i] + backtrack(i + 1, words)); + for (int j = 0; j < 26; j++) { + letterCnt[j] += wordFreqs[i][j]; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string[]} letters + * @param {number[]} score + * @return {number} + */ + maxScoreWords(words, letters, score) { + let letterCnt = new Array(26).fill(0); + for (let c of letters) { + letterCnt[c.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + + let n = words.length; + let wordScores = new Array(n).fill(0); + let wordFreqs = Array.from({ length: n }, () => new Array(26).fill(0)); + + for (let i = 0; i < n; i++) { + for (let c of words[i]) { + let idx = c.charCodeAt(0) - 'a'.charCodeAt(0); + wordFreqs[i][idx]++; + wordScores[i] += score[idx]; + } + } + + const backtrack = (i) => { + if (i === n) { + return 0; + } + + let res = backtrack(i + 1); // skip + let canInclude = true; + + for (let j = 0; j < 26; j++) { + if (wordFreqs[i][j] > letterCnt[j]) { + canInclude = false; + break; + } + } + + if (canInclude) { // include (when possible) + for (let j = 0; j < 26; j++) { + letterCnt[j] -= wordFreqs[i][j]; + } + res = Math.max(res, wordScores[i] + backtrack(i + 1)); + for (let j = 0; j < 26; j++) { + letterCnt[j] += wordFreqs[i][j]; + } + } + + return res; + }; + + return backtrack(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * 2 ^ n + N)$ +* Space complexity: $O(n + w)$ + +> Where $n$ is the number of words, $w$ is the maximum length of a word, $m$ is the size of the array $scores$, and $N$ is the size of the array $letters$. + +--- + +## 3. Backtracking (Bit Mask) + +::tabs-start + +```python +class Solution: + def maxScoreWords(self, words: List[str], letters: List[str], score: List[int]) -> int: + letter_cnt = [0] * 26 + for c in letters: + letter_cnt[ord(c) - ord('a')] += 1 + + n = len(words) + word_scores = [0] * n + word_freqs = [[0] * 26 for _ in range(n)] + + for i, word in enumerate(words): + for c in word: + idx = ord(c) - ord('a') + word_freqs[i][idx] += 1 + word_scores[i] += score[idx] + + res = 0 + + for mask in range(1 << n): + cur_score = 0 + cur_letter_cnt = letter_cnt[:] + valid = True + + for i in range(n): + if mask & (1 << i): + for j in range(26): + if word_freqs[i][j] > cur_letter_cnt[j]: + valid = False + break + if not valid: + break + + for j in range(26): + cur_letter_cnt[j] -= word_freqs[i][j] + + cur_score += word_scores[i] + + if valid: + res = max(res, cur_score) + + return res +``` + +```java +public class Solution { + public int maxScoreWords(String[] words, char[] letters, int[] score) { + int[] letterCnt = new int[26]; + for (char c : letters) { + letterCnt[c - 'a']++; + } + + int n = words.length; + int[] wordScores = new int[n]; + int[][] wordFreqs = new int[n][26]; + + for (int i = 0; i < n; i++) { + for (char c : words[i].toCharArray()) { + int idx = c - 'a'; + wordFreqs[i][idx]++; + wordScores[i] += score[idx]; + } + } + + int res = 0; + for (int mask = 0; mask < (1 << n); mask++) { + int curScore = 0; + int[] curLetterCnt = Arrays.copyOf(letterCnt, 26); + boolean valid = true; + + for (int i = 0; i < n; i++) { + if ((mask & (1 << i)) != 0) { + for (int j = 0; j < 26; j++) { + if (wordFreqs[i][j] > curLetterCnt[j]) { + valid = false; + break; + } + } + if (!valid) break; + + for (int j = 0; j < 26; j++) { + curLetterCnt[j] -= wordFreqs[i][j]; + } + + curScore += wordScores[i]; + } + } + + if (valid) { + res = Math.max(res, curScore); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxScoreWords(vector& words, vector& letters, vector& score) { + vector letterCnt(26, 0); + for (char c : letters) { + letterCnt[c - 'a']++; + } + + int n = words.size(); + vector wordScores(n, 0); + vector> wordFreqs(n, vector(26, 0)); + + for (int i = 0; i < n; i++) { + for (char c : words[i]) { + int idx = c - 'a'; + wordFreqs[i][idx]++; + wordScores[i] += score[idx]; + } + } + + int res = 0; + for (int mask = 0; mask < (1 << n); mask++) { + int curScore = 0; + vector curLetterCnt = letterCnt; + bool valid = true; + + for (int i = 0; i < n; i++) { + if (mask & (1 << i)) { + for (int j = 0; j < 26; j++) { + if (wordFreqs[i][j] > curLetterCnt[j]) { + valid = false; + break; + } + } + if (!valid) break; + + for (int j = 0; j < 26; j++) { + curLetterCnt[j] -= wordFreqs[i][j]; + } + + curScore += wordScores[i]; + } + } + + if (valid) { + res = max(res, curScore); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string[]} letters + * @param {number[]} score + * @return {number} + */ + maxScoreWords(words, letters, score) { + let letterCnt = new Array(26).fill(0); + for (let c of letters) { + letterCnt[c.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + + let n = words.length; + let wordScores = new Array(n).fill(0); + let wordFreqs = Array.from({ length: n }, () => new Array(26).fill(0)); + + for (let i = 0; i < n; i++) { + for (let c of words[i]) { + let idx = c.charCodeAt(0) - 'a'.charCodeAt(0); + wordFreqs[i][idx]++; + wordScores[i] += score[idx]; + } + } + + let res = 0; + for (let mask = 0; mask < (1 << n); mask++) { + let curScore = 0; + let curLetterCnt = [...letterCnt]; + let valid = true; + + for (let i = 0; i < n; i++) { + if ((mask & (1 << i)) !== 0) { + for (let j = 0; j < 26; j++) { + if (wordFreqs[i][j] > curLetterCnt[j]) { + valid = false; + break; + } + } + if (!valid) break; + + for (let j = 0; j < 26; j++) { + curLetterCnt[j] -= wordFreqs[i][j]; + } + + curScore += wordScores[i]; + } + } + + if (valid) { + res = Math.max(res, curScore); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n * 2 ^ n + N)$ +* Space complexity: $O(n + w)$ + +> Where $n$ is the number of words, $w$ is the maximum length of a word, $m$ is the size of the array $scores$, and $N$ is the size of the array $letters$. \ No newline at end of file diff --git a/articles/maximum-subarray-min-product.md b/articles/maximum-subarray-min-product.md new file mode 100644 index 000000000..1ac42156b --- /dev/null +++ b/articles/maximum-subarray-min-product.md @@ -0,0 +1,967 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxSumMinProduct(self, nums: List[int]) -> int: + res, MOD = 0, 1000000007 + for i in range(len(nums)): + total_sum = 0 + mini = float("inf") + for j in range(i, len(nums)): + mini = min(mini, nums[j]) + total_sum += nums[j] + cur = (mini * total_sum) % MOD + res = max(res, cur) + return res +``` + +```java +public class Solution { + public int maxSumMinProduct(int[] nums) { + long res = 0, MOD = 1000000007; + for (int i = 0; i < nums.length; i++) { + long totalSum = 0, mini = Long.MAX_VALUE; + for (int j = i; j < nums.length; j++) { + mini = Math.min(mini, nums[j]); + totalSum += nums[j]; + long cur = (mini * totalSum) % MOD; + res = Math.max(res, cur); + } + } + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + int maxSumMinProduct(vector& nums) { + long long res = 0, MOD = 1000000007; + for (int i = 0; i < nums.size(); i++) { + long long total_sum = 0, mini = INT_MAX; + for (int j = i; j < nums.size(); j++) { + mini = min(mini, (long long)nums[j]); + total_sum += nums[j]; + long long cur = (mini * total_sum) % MOD; + res = max(res, cur); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxSumMinProduct(nums) { + let res = 0, MOD = 1000000007; + for (let i = 0; i < nums.length; i++) { + let totalSum = 0, mini = Infinity; + for (let j = i; j < nums.length; j++) { + mini = Math.min(mini, nums[j]); + totalSum += nums[j]; + let cur = (mini * totalSum) % MOD; + res = Math.max(res, cur); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Divide And Conquer (Brute Force) + +::tabs-start + +```python +class Solution: + def maxSumMinProduct(self, nums: List[int]) -> int: + MOD = 10**9 + 7 + + def rec(l, r): + if l > r: + return 0 + + min_idx = l + total_sum = 0 + for i in range(l, r + 1): + total_sum += nums[i] + if nums[i] < nums[min_idx]: + min_idx = i + + cur = total_sum * nums[min_idx] + left = rec(l, min_idx - 1) + right = rec(min_idx + 1, r) + + return max(cur, left, right) + + return rec(0, len(nums) - 1) % MOD +``` + +```java +public class Solution { + public int maxSumMinProduct(int[] nums) { + int MOD = 1_000_000_007; + return (int) (rec(nums, 0, nums.length - 1) % MOD); + } + + private long rec(int[] nums, int l, int r) { + if (l > r) return 0; + + int minIdx = l; + long totalSum = 0; + for (int i = l; i <= r; i++) { + totalSum += nums[i]; + if (nums[i] < nums[minIdx]) { + minIdx = i; + } + } + + long cur = totalSum * nums[minIdx]; + long left = rec(nums, l, minIdx - 1); + long right = rec(nums, minIdx + 1, r); + + return Math.max(cur, Math.max(left, right)); + } +} +``` + +```cpp +class Solution { +public: + int maxSumMinProduct(vector& nums) { + const int MOD = 1e9 + 7; + return rec(nums, 0, nums.size() - 1) % MOD; + } + +private: + long long rec(vector& nums, int l, int r) { + if (l > r) return 0; + + int minIdx = l; + long long totalSum = 0; + for (int i = l; i <= r; i++) { + totalSum += nums[i]; + if (nums[i] < nums[minIdx]) { + minIdx = i; + } + } + + long long cur = totalSum * nums[minIdx]; + long long left = rec(nums, l, minIdx - 1); + long long right = rec(nums, minIdx + 1, r); + + return max(cur, max(left, right)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxSumMinProduct(nums) { + const MOD = 1_000_000_007; + + const rec = (l, r) => { + if (l > r) return 0n; + + let minIdx = l; + let totalSum = 0n; + for (let i = l; i <= r; i++) { + totalSum += BigInt(nums[i]); + if (nums[i] < nums[minIdx]) { + minIdx = i; + } + } + + let cur = totalSum * BigInt(nums[minIdx]); + let left = rec(l, minIdx - 1); + let right = rec(minIdx + 1, r); + + if (cur < left) cur = left; + if (cur < right) cur = right; + return cur; + }; + + return Number(rec(0, nums.length - 1) % BigInt(MOD)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Divide And Conquer (Segment Tree) + +::tabs-start + +```python +class SegmentTree: + def __init__(self, N, A): + self.n = N + while (self.n & (self.n - 1)) != 0: + self.n += 1 + self.build(N, A) + + def build(self, N, A): + self.tree = [(-1, float('inf'))] * (2 * self.n) + for i in range(N): + self.tree[self.n + i] = (i, A[i]) + for i in range(self.n - 1, 0, -1): + self.tree[i] = min(self.tree[i << 1], self.tree[i << 1 | 1], key=lambda x: x[1]) + + def query(self, l, r): + res = (-1, float('inf')) + l += self.n + r += self.n + 1 + while l < r: + if l & 1: + res = min(res, self.tree[l], key=lambda x: x[1]) + l += 1 + if r & 1: + r -= 1 + res = min(res, self.tree[r], key=lambda x: x[1]) + l >>= 1 + r >>= 1 + return res[0] + +class Solution: + def maxSumMinProduct(self, nums: List[int]) -> int: + MOD = 10**9 + 7 + segTree = SegmentTree(len(nums), nums) + prefix_sum = [0] * (len(nums) + 1) + for i in range(0, len(nums)): + prefix_sum[i + 1] += prefix_sum[i] + nums[i] + + def rec(l, r): + if l > r: + return 0 + + min_idx = segTree.query(l, r) + total_sum = prefix_sum[r + 1] - prefix_sum[l] + cur = total_sum * nums[min_idx] + left = rec(l, min_idx - 1) + right = rec(min_idx + 1, r) + + return max(cur, left, right) + + return rec(0, len(nums) - 1) % MOD +``` + +```java +class SegmentTree { + private int n; + private long[][] tree; + + public SegmentTree(int n, int[] nums) { + this.n = n; + while ((this.n & (this.n - 1)) != 0) { + this.n++; + } + build(n, nums); + } + + private void build(int n, int[] nums) { + tree = new long[2 * this.n][2]; + Arrays.fill(tree, new long[]{-1, Long.MAX_VALUE}); + for (int i = 0; i < n; i++) { + tree[this.n + i] = new long[]{i, nums[i]}; + } + for (int i = this.n - 1; i > 0; i--) { + tree[i] = merge(tree[i << 1], tree[i << 1 | 1]); + } + } + + private long[] merge(long[] a, long[] b) { + return a[1] < b[1] ? a : b; + } + + public int query(int l, int r) { + long[] res = new long[]{-1, Long.MAX_VALUE}; + l += this.n; + r += this.n + 1; + while (l < r) { + if ((l & 1) == 1) res = merge(res, tree[l++]); + if ((r & 1) == 1) res = merge(res, tree[--r]); + l >>= 1; + r >>= 1; + } + return (int) res[0]; + } +} + +public class Solution { + public int maxSumMinProduct(int[] nums) { + int MOD = 1_000_000_007; + SegmentTree segTree = new SegmentTree(nums.length, nums); + long[] prefixSum = new long[nums.length + 1]; + for (int i = 0; i < nums.length; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + return (int) (rec(0, nums.length - 1, nums, prefixSum, segTree) % MOD); + } + + private long rec(int l, int r, int[] nums, long[] prefixSum, SegmentTree segTree) { + if (l > r) return 0; + int minIdx = segTree.query(l, r); + long totalSum = prefixSum[r + 1] - prefixSum[l]; + long cur = totalSum * nums[minIdx]; + long left = rec(l, minIdx - 1, nums, prefixSum, segTree); + long right = rec(minIdx + 1, r, nums, prefixSum, segTree); + return Math.max(cur, Math.max(left, right)); + } +} +``` + +```cpp +class SegmentTree { + int n; + vector> tree; + +public: + SegmentTree(int n, vector& nums) : n(n) { + while ((this->n & (this->n - 1)) != 0) this->n++; + build(n, nums); + } + + void build(int n, vector& nums) { + tree.resize(2 * this->n, {-1, LLONG_MAX}); + for (int i = 0; i < n; i++) { + tree[this->n + i] = {i, nums[i]}; + } + for (int i = this->n - 1; i > 0; i--) { + tree[i] = min(tree[i << 1], tree[i << 1 | 1], [](auto& a, auto& b) { return a.second < b.second; }); + } + } + + int query(int l, int r) { + pair res = {-1, LLONG_MAX}; + l += this->n; + r += this->n + 1; + while (l < r) { + if (l & 1) res = min(res, tree[l++], [](auto& a, auto& b) { return a.second < b.second; }); + if (r & 1) res = min(res, tree[--r], [](auto& a, auto& b) { return a.second < b.second; }); + l >>= 1; + r >>= 1; + } + return res.first; + } +}; + +class Solution { +public: + int maxSumMinProduct(vector& nums) { + const int MOD = 1e9 + 7; + SegmentTree segTree(nums.size(), nums); + vector prefixSum(nums.size() + 1, 0); + for (int i = 0; i < nums.size(); i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + return rec(0, nums.size() - 1, nums, prefixSum, segTree) % MOD; + } + +private: + long long rec(int l, int r, vector& nums, vector& prefixSum, SegmentTree& segTree) { + if (l > r) return 0; + int minIdx = segTree.query(l, r); + long long totalSum = prefixSum[r + 1] - prefixSum[l]; + long long cur = totalSum * nums[minIdx]; + long long left = rec(l, minIdx - 1, nums, prefixSum, segTree); + long long right = rec(minIdx + 1, r, nums, prefixSum, segTree); + return max(cur, max(left, right)); + } +}; +``` + +```javascript +class SegmentTree { + /** + * @constructor + * @param {number} n + * @param {number[]} nums + */ + constructor(n, nums) { + this.n = n; + while ((this.n & (this.n - 1)) !== 0) { + this.n++; + } + this.build(n, nums); + } + + /** + * @param {number} n + * @param {number[]} nums + * @return {void} + */ + build(n, nums) { + this.tree = Array(2 * this.n).fill([BigInt(-1), BigInt(Number.MAX_SAFE_INTEGER)]); + for (let i = 0; i < n; i++) { + this.tree[this.n + i] = [BigInt(i), BigInt(nums[i])]; + } + for (let i = this.n - 1; i > 0; i--) { + this.tree[i] = this._merge(this.tree[i << 1], this.tree[i << 1 | 1]); + } + } + + /** + * @param {number} a + * @param {number} b + * @return {number} + */ + _merge(a, b) { + return a[1] < b[1] ? a : b; + } + + /** + * @param {number} l + * @param {number} r + * @return {number} + */ + query(l, r) { + let res = [BigInt(-1), BigInt(Number.MAX_SAFE_INTEGER)]; + l += this.n; + r += this.n + 1; + while (l < r) { + if (l & 1) res = this._merge(res, this.tree[l++]); + if (r & 1) res = this._merge(res, this.tree[--r]); + l >>= 1; + r >>= 1; + } + return Number(res[0]); + } +} + +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxSumMinProduct(nums) { + const MOD = BigInt(1_000_000_007); + const segTree = new SegmentTree(nums.length, nums); + const prefixSum = Array(nums.length + 1).fill(BigInt(0)); + for (let i = 0; i < nums.length; i++) { + prefixSum[i + 1] = prefixSum[i] + BigInt(nums[i]); + } + + const rec = (l, r) => { + if (l > r) return BigInt(0); + const minIdx = segTree.query(l, r); + const totalSum = prefixSum[r + 1] - prefixSum[l]; + let cur = totalSum * BigInt(nums[minIdx]); + let left = rec(l, minIdx - 1); + let right = rec(minIdx + 1, r); + + if (cur < left) cur = left; + if (cur < right) cur = right; + return cur; + }; + + return Number(rec(0, nums.length - 1) % MOD); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Monotonic Stack + +::tabs-start + +```python +class Solution: + def maxSumMinProduct(self, nums: List[int]) -> int: + n = len(nums) + prefix_sum = [0] * (n + 1) + for i in range(n): + prefix_sum[i + 1] = prefix_sum[i] + nums[i] + + prev_min, nxt_min = [-1] * n, [n] * n + stack = [] + + for i in range(n): + while stack and nums[stack[-1]] >= nums[i]: + stack.pop() + if stack: + prev_min[i] = stack[-1] + stack.append(i) + + stack.clear() + + for i in range(n - 1, -1, -1): + while stack and nums[stack[-1]] >= nums[i]: + stack.pop() + if stack: + nxt_min[i] = stack[-1] + stack.append(i) + + res = 0 + + for i in range(n): + l, r = prev_min[i] + 1, nxt_min[i] - 1 + total_sum = prefix_sum[r + 1] - prefix_sum[l] + res = max(res, nums[i] * total_sum) + + return res % (10 ** 9 + 7) +``` + +```java +public class Solution { + public int maxSumMinProduct(int[] nums) { + int n = nums.length; + long[] prefixSum = new long[n + 1]; + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + int[] prevMin = new int[n]; + int[] nxtMin = new int[n]; + Arrays.fill(prevMin, -1); + Arrays.fill(nxtMin, n); + + Stack stack = new Stack<>(); + for (int i = 0; i < n; i++) { + while (!stack.isEmpty() && nums[stack.peek()] >= nums[i]) { + stack.pop(); + } + if (!stack.isEmpty()) { + prevMin[i] = stack.peek(); + } + stack.push(i); + } + + stack.clear(); + for (int i = n - 1; i >= 0; i--) { + while (!stack.isEmpty() && nums[stack.peek()] >= nums[i]) { + stack.pop(); + } + if (!stack.isEmpty()) { + nxtMin[i] = stack.peek(); + } + stack.push(i); + } + + long res = 0; + int MOD = 1_000_000_007; + for (int i = 0; i < n; i++) { + int l = prevMin[i] + 1, r = nxtMin[i] - 1; + long totalSum = prefixSum[r + 1] - prefixSum[l]; + res = Math.max(res, nums[i] * totalSum); + } + + return (int)(res % MOD); + } +} +``` + +```cpp +class Solution { +public: + int maxSumMinProduct(vector& nums) { + int n = nums.size(); + vector prefixSum(n + 1, 0); + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + vector prevMin(n, -1), nxtMin(n, n); + stack stack; + + for (int i = 0; i < n; i++) { + while (!stack.empty() && nums[stack.top()] >= nums[i]) { + stack.pop(); + } + if (!stack.empty()) { + prevMin[i] = stack.top(); + } + stack.push(i); + } + + while (!stack.empty()) stack.pop(); + for (int i = n - 1; i >= 0; i--) { + while (!stack.empty() && nums[stack.top()] >= nums[i]) { + stack.pop(); + } + if (!stack.empty()) { + nxtMin[i] = stack.top(); + } + stack.push(i); + } + + long long res = 0; + int MOD = 1e9 + 7; + for (int i = 0; i < n; i++) { + int l = prevMin[i] + 1, r = nxtMin[i] - 1; + long long totalSum = prefixSum[r + 1] - prefixSum[l]; + res = max(res, nums[i] * totalSum); + } + + return res % MOD; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxSumMinProduct(nums) { + const n = nums.length; + const prefixSum = new Array(n + 1).fill(0); + for (let i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + const prevMin = new Array(n).fill(-1); + const nxtMin = new Array(n).fill(n); + const stack = []; + + for (let i = 0; i < n; i++) { + while (stack.length && nums[stack[stack.length - 1]] >= nums[i]) { + stack.pop(); + } + if (stack.length) { + prevMin[i] = stack[stack.length - 1]; + } + stack.push(i); + } + + stack.length = 0; + for (let i = n - 1; i >= 0; i--) { + while (stack.length && nums[stack[stack.length - 1]] >= nums[i]) { + stack.pop(); + } + if (stack.length) { + nxtMin[i] = stack[stack.length - 1]; + } + stack.push(i); + } + + let res = 0n; + const MOD = 10n ** 9n + 7n; + for (let i = 0; i < n; i++) { + const l = prevMin[i] + 1; + const r = nxtMin[i] - 1; + const totalSum = BigInt(prefixSum[r + 1] - prefixSum[l]); + const tmp = BigInt(nums[i]) * totalSum; + if (tmp > res) res = tmp; + } + + return Number(res % MOD); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Monotonic Stack (Space Optimized) - I + +::tabs-start + +```python +class Solution: + def maxSumMinProduct(self, nums: List[int]) -> int: + n = len(nums) + prefix = [0] * (n + 1) + for i in range(n): + prefix[i + 1] = prefix[i] + nums[i] + + res = 0 + stack = [] + + for i, num in enumerate(nums): + new_start = i + while stack and stack[-1][1] > num: + start, val = stack.pop() + total = prefix[i] - prefix[start] + res = max(res, val * total) + new_start = start + stack.append((new_start, num)) + + while stack: + start, val = stack.pop() + total = prefix[n] - prefix[start] + res = max(res, val * total) + + return res % (10**9 + 7) +``` + +```java +public class Solution { + public int maxSumMinProduct(int[] nums) { + int n = nums.length; + long[] prefix = new long[n + 1]; + for (int i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + nums[i]; + } + + long res = 0; + Stack stack = new Stack<>(); + for (int i = 0; i < n; i++) { + int newStart = i; + while (!stack.isEmpty() && stack.peek()[1] > nums[i]) { + int[] top = stack.pop(); + int start = top[0], val = top[1]; + long total = prefix[i] - prefix[start]; + res = Math.max(res, val * total); + newStart = start; + } + stack.push(new int[]{newStart, nums[i]}); + } + + while (!stack.isEmpty()) { + int[] top = stack.pop(); + int start = top[0], val = top[1]; + long total = prefix[n] - prefix[start]; + res = Math.max(res, val * total); + } + + return (int) (res % (1_000_000_007)); + } +} +``` + +```cpp +class Solution { +public: + int maxSumMinProduct(vector& nums) { + int n = nums.size(); + vector prefix(n + 1, 0); + for (int i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + nums[i]; + } + + long long res = 0; + stack> stack; + for (int i = 0; i < n; i++) { + int newStart = i; + while (!stack.empty() && stack.top().second > nums[i]) { + auto [start, val] = stack.top(); + stack.pop(); + long long total = prefix[i] - prefix[start]; + res = max(res, val * total); + newStart = start; + } + stack.push({newStart, nums[i]}); + } + + while (!stack.empty()) { + auto [start, val] = stack.top(); + stack.pop(); + long long total = prefix[n] - prefix[start]; + res = max(res, val * total); + } + + return res % 1000000007; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxSumMinProduct(nums) { + const n = nums.length; + const prefix = new Array(n + 1).fill(0); + for (let i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + nums[i]; + } + + let res = 0n; + const MOD = 10n ** 9n + 7n; + const stack = []; + + for (let i = 0; i < n; i++) { + let newStart = i; + while (stack.length && stack[stack.length - 1][1] > nums[i]) { + const [start, val] = stack.pop(); + const total = BigInt(prefix[i] - prefix[start]); + const tmp = BigInt(val) * total; + if (tmp > res) res = tmp; + newStart = start; + } + stack.push([newStart, nums[i]]); + } + + while (stack.length) { + const [start, val] = stack.pop(); + const total = BigInt(prefix[n] - prefix[start]); + const tmp = BigInt(val) * total; + if (tmp > res) res = tmp; + } + + return Number(res % MOD); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 6. Monotonic Stack (Space Optimized) - II + +::tabs-start + +```python +class Solution: + def maxSumMinProduct(self, nums: List[int]) -> int: + n = len(nums) + prefix_sum = [0] * (n + 1) + for i in range(n): + prefix_sum[i + 1] = prefix_sum[i] + nums[i] + + res, stack = 0, [] + for i in range(n + 1): + while stack and (i == n or nums[i] < nums[stack[-1]]): + j = stack.pop() + start = 0 if not stack else stack[-1] + 1 + res = max(res, nums[j] * (prefix_sum[i] - prefix_sum[start])) + stack.append(i) + + return res % (10 ** 9 + 7) +``` + +```java +public class Solution { + public int maxSumMinProduct(int[] nums) { + int n = nums.length; + long[] prefixSum = new long[n + 1]; + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + long res = 0; + int mod = 1_000_000_007; + Stack stack = new Stack<>(); + for (int i = 0; i <= n; i++) { + while (!stack.isEmpty() && (i == n || nums[i] < nums[stack.peek()])) { + int j = stack.pop(); + int start = stack.isEmpty() ? 0 : stack.peek() + 1; + res = Math.max(res, (long) nums[j] * (prefixSum[i] - prefixSum[start])); + } + stack.push(i); + } + + return (int) (res % mod); + } +} +``` + +```cpp +class Solution { +public: + int maxSumMinProduct(vector& nums) { + int n = nums.size(); + vector prefixSum(n + 1, 0); + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + long long res = 0; + const int mod = 1e9 + 7; + stack st; + for (int i = 0; i <= n; i++) { + while (!st.empty() && (i == n || nums[i] < nums[st.top()])) { + int j = st.top(); + st.pop(); + int start = st.empty() ? 0 : st.top() + 1; + res = max(res, (long long) nums[j] * (prefixSum[i] - prefixSum[start])); + } + st.push(i); + } + + return res % mod; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxSumMinProduct(nums) { + const n = nums.length; + const prefixSum = new Array(n + 1).fill(0); + for (let i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + let res = 0n; + const MOD = 10n ** 9n + 7n; + const stack = []; + for (let i = 0; i <= n; i++) { + while (stack.length && (i === n || nums[i] < nums[stack[stack.length - 1]])) { + const j = stack.pop(); + const start = stack.length === 0 ? 0 : stack[stack.length - 1] + 1; + const total = BigInt(prefixSum[i] - prefixSum[start]); + const tmp = BigInt(nums[j]) * total; + if (tmp > res) res = tmp; + } + stack.push(i); + } + + return Number(res % MOD); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/maximum-subarray.md b/articles/maximum-subarray.md new file mode 100644 index 000000000..678ab39c8 --- /dev/null +++ b/articles/maximum-subarray.md @@ -0,0 +1,1250 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxSubArray(self, nums: List[int]) -> int: + n, res = len(nums), nums[0] + for i in range(n): + cur = 0 + for j in range(i, n): + cur += nums[j] + res = max(res, cur) + return res +``` + +```java +public class Solution { + public int maxSubArray(int[] nums) { + int n = nums.length, res = nums[0]; + for (int i = 0; i < n; i++) { + int cur = 0; + for (int j = i; j < n; j++) { + cur += nums[j]; + res = Math.max(res, cur); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxSubArray(vector& nums) { + int n = nums.size(), res = nums[0]; + for (int i = 0; i < n; i++) { + int cur = 0; + for (int j = i; j < n; j++) { + cur += nums[j]; + res = max(res, cur); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxSubArray(nums) { + let n = nums.length, res = nums[0]; + for (let i = 0; i < n; i++) { + let cur = 0; + for (let j = i; j < n; j++) { + cur += nums[j]; + res = Math.max(res, cur); + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int MaxSubArray(int[] nums) { + int n = nums.Length, res = nums[0]; + for (int i = 0; i < n; i++) { + int cur = 0; + for (int j = i; j < n; j++) { + cur += nums[j]; + res = Math.Max(res, cur); + } + } + return res; + } +} +``` + +```go +func maxSubArray(nums []int) int { + n := len(nums) + res := nums[0] + + for i := 0; i < n; i++ { + cur := 0 + for j := i; j < n; j++ { + cur += nums[j] + if cur > res { + res = cur + } + } + } + return res +} +``` + +```kotlin +class Solution { + fun maxSubArray(nums: IntArray): Int { + val n = nums.size + var res = nums[0] + + for (i in 0 until n) { + var cur = 0 + for (j in i until n) { + cur += nums[j] + res = maxOf(res, cur) + } + } + return res + } +} +``` + +```swift +class Solution { + func maxSubArray(_ nums: [Int]) -> Int { + let n = nums.count + var res = nums[0] + + for i in 0.. int: + def dfs(i, flag): + if i == len(nums): + return 0 if flag else -1e6 + if flag: + return max(0, nums[i] + dfs(i + 1, True)) + return max(dfs(i + 1, False), nums[i] + dfs(i + 1, True)) + return dfs(0, False) +``` + +```java +public class Solution { + public int maxSubArray(int[] nums) { + return dfs(nums, 0, false); + } + + private int dfs(int[] nums, int i, boolean flag) { + if (i == nums.length) { + return flag ? 0 : (int) -1e6; + } + if (flag) { + return Math.max(0, nums[i] + dfs(nums, i + 1, true)); + } + return Math.max(dfs(nums, i + 1, false), + nums[i] + dfs(nums, i + 1, true)); + } +} +``` + +```cpp +class Solution { +public: + int maxSubArray(vector& nums) { + return dfs(nums, 0, false); + } + +private: + int dfs(vector& nums, int i, bool flag) { + if (i == nums.size()) return flag ? 0 : -1e6; + if (flag) return max(0, nums[i] + dfs(nums, i + 1, true)); + return max(dfs(nums, i + 1, false), + nums[i] + dfs(nums, i + 1, true)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxSubArray(nums) { + const dfs = (i, flag) => { + if (i === nums.length) return flag ? 0 : -1e6; + if (flag) return Math.max(0, nums[i] + dfs(i + 1, true)); + return Math.max(dfs(i + 1, false), + nums[i] + dfs(i + 1, true)); + }; + return dfs(0, false); + } +} +``` + +```csharp +public class Solution { + public int MaxSubArray(int[] nums) { + return Dfs(nums, 0, false); + } + + private int Dfs(int[] nums, int i, bool flag) { + if (i == nums.Length) return flag ? 0 : (int)-1e6; + if (flag) return Math.Max(0, nums[i] + Dfs(nums, i + 1, true)); + return Math.Max(Dfs(nums, i + 1, false), + nums[i] + Dfs(nums, i + 1, true)); + } +} +``` + +```go +func maxSubArray(nums []int) int { + var dfs func(i int, flag bool) int + dfs = func(i int, flag bool) int { + if i == len(nums) { + if flag { + return 0 + } + return -1e6 + } + if flag { + return max(0, nums[i] + dfs(i + 1, true)) + } + return max(dfs(i + 1, false), nums[i] + dfs(i + 1, true)) + } + + return dfs(0, false) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun maxSubArray(nums: IntArray): Int { + fun dfs(i: Int, flag: Boolean): Int { + if (i == nums.size) { + return if (flag) 0 else Int.MIN_VALUE + } + return if (flag) { + maxOf(0, nums[i] + dfs(i + 1, true)) + } else { + maxOf(dfs(i + 1, false), nums[i] + dfs(i + 1, true)) + } + } + + return dfs(0, false) + } +} +``` + +```swift +class Solution { + func maxSubArray(_ nums: [Int]) -> Int { + func dfs(_ i: Int, _ flag: Bool) -> Int { + if i == nums.count { + return flag ? 0 : Int.min + } + + if flag { + return max(0, nums[i] + dfs(i + 1, true)) + } + + return max(dfs(i + 1, false), nums[i] + dfs(i + 1, true)) + } + + return dfs(0, false) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def maxSubArray(self, nums: List[int]) -> int: + memo = [[None] * 2 for _ in range(len(nums) + 1)] + + def dfs(i, flag): + if i == len(nums): + return 0 if flag else -1e6 + if memo[i][flag] is not None: + return memo[i][flag] + if flag: + memo[i][flag] = max(0, nums[i] + dfs(i + 1, True)) + else: + memo[i][flag] = max(dfs(i + 1, False), + nums[i] + dfs(i + 1, True)) + return memo[i][flag] + + return dfs(0, False) +``` + +```java +public class Solution { + private int[][] memo; + + public int maxSubArray(int[] nums) { + memo = new int[nums.length + 1][2]; + for (int[] row : memo) Arrays.fill(row, Integer.MIN_VALUE); + return dfs(nums, 0, false); + } + + private int dfs(int[] nums, int i, boolean flag) { + if (i == nums.length) return flag ? 0 : (int) -1e6; + int f = flag ? 1 : 0; + if (memo[i][f] != Integer.MIN_VALUE) return memo[i][f]; + memo[i][f] = flag ? Math.max(0, nums[i] + dfs(nums, i + 1, true)) + : Math.max(dfs(nums, i + 1, false), + nums[i] + dfs(nums, i + 1, true)); + return memo[i][f]; + } +} +``` + +```cpp +class Solution { +public: + int maxSubArray(vector& nums) { + vector> memo(nums.size() + 1, vector(2, INT_MIN)); + return dfs(nums, 0, false, memo); + } + +private: + int dfs(vector& nums, int i, bool flag, vector>& memo) { + if (i == nums.size()) return flag ? 0 : -1e6; + int f = flag ? 1 : 0; + if (memo[i][f] != INT_MIN) return memo[i][f]; + if (flag) + memo[i][f] = max(0, nums[i] + dfs(nums, i + 1, true, memo)); + else + memo[i][f] = max(dfs(nums, i + 1, false, memo), + nums[i] + dfs(nums, i + 1, true, memo)); + return memo[i][f]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxSubArray(nums) { + const memo = Array(nums.length + 1).fill(null).map( + () => [null, null] + ); + + const dfs = (i, flag) => { + if (i === nums.length) return flag ? 0 : -1e6; + if (memo[i][+flag] !== null) return memo[i][+flag]; + memo[i][+flag] = flag ? Math.max(0, nums[i] + dfs(i + 1, true)) + : Math.max(dfs(i + 1, false), + nums[i] + dfs(i + 1, true)); + return memo[i][+flag]; + } + return dfs(0, false); + } +} +``` + +```csharp +public class Solution { + private int[,] memo; + + public int MaxSubArray(int[] nums) { + memo = new int[nums.Length + 1, 2]; + for (int i = 0; i <= nums.Length; i++) { + memo[i, 0] = memo[i, 1] = int.MinValue; + } + return Dfs(nums, 0, false); + } + + private int Dfs(int[] nums, int i, bool flag) { + if (i == nums.Length) return flag ? 0 : -1000000; + int f = flag ? 1 : 0; + if (memo[i, f] != int.MinValue) return memo[i, f]; + memo[i, f] = flag ? Math.Max(0, nums[i] + Dfs(nums, i + 1, true)) + : Math.Max(Dfs(nums, i + 1, false), + nums[i] + Dfs(nums, i + 1, true)); + return memo[i, f]; + } +} +``` + +```go +func maxSubArray(nums []int) int { + memo := make([][2]*int, len(nums)+1) + for i := range memo { + memo[i] = [2]*int{nil, nil} + } + + var dfs func(int, int) int + dfs = func(i, flag int) int { + if i == len(nums) { + if flag == 1 { + return 0 + } + return -1000000 + } + if memo[i][flag] != nil { + return *memo[i][flag] + } + if flag == 1 { + result := max(0, nums[i]+dfs(i+1, 1)) + memo[i][flag] = &result + } else { + result := max(dfs(i+1, 0), nums[i]+dfs(i+1, 1)) + memo[i][flag] = &result + } + return *memo[i][flag] + } + + return dfs(0, 0) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun maxSubArray(nums: IntArray): Int { + val memo = Array(nums.size + 1) { arrayOfNulls(2) } + + fun dfs(i: Int, flag: Int): Int { + if (i == nums.size) { + return if (flag == 1) 0 else -1000000 + } + if (memo[i][flag] != null) { + return memo[i][flag]!! + } + memo[i][flag] = if (flag == 1) { + maxOf(0, nums[i] + dfs(i + 1, 1)) + } else { + maxOf(dfs(i + 1, 0), nums[i] + dfs(i + 1, 1)) + } + return memo[i][flag]!! + } + + return dfs(0, 0) + } +} +``` + +```swift +class Solution { + func maxSubArray(_ nums: [Int]) -> Int { + var memo = Array(repeating: [Int?](repeating: nil, count: 2), count: nums.count + 1) + + func dfs(_ i: Int, _ flag: Bool) -> Int { + if i == nums.count { + return flag ? 0 : Int.min + } + if let value = memo[i][flag ? 1 : 0] { + return value + } + + if flag { + memo[i][flag ? 1 : 0] = max(0, nums[i] + dfs(i + 1, true)) + } else { + memo[i][flag ? 1 : 0] = max(dfs(i + 1, false), nums[i] + dfs(i + 1, true)) + } + + return memo[i][flag ? 1 : 0]! + } + + return dfs(0, false) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def maxSubArray(self, nums: List[int]) -> int: + n = len(nums) + dp = [[0] * 2 for _ in range(n)] + dp[n - 1][1] = dp[n - 1][0] = nums[n - 1] + for i in range(n - 2, -1, -1): + dp[i][1] = max(nums[i], nums[i] + dp[i + 1][1]) + dp[i][0] = max(dp[i + 1][0], dp[i][1]) + + return dp[0][0] +``` + +```java +public class Solution { + public int maxSubArray(int[] nums) { + int n = nums.length; + int[][] dp = new int[n + 1][2]; + + dp[n - 1][1] = dp[n - 1][0] = nums[n - 1]; + for (int i = n - 2; i >= 0; i--) { + dp[i][1] = Math.max(nums[i], nums[i] + dp[i + 1][1]); + dp[i][0] = Math.max(dp[i + 1][0], dp[i][1]); + } + + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + int maxSubArray(vector& nums) { + int n = nums.size(); + vector> dp(n + 1, vector(2, 0)); + + dp[n - 1][1] = dp[n - 1][0] = nums[n - 1]; + for (int i = n - 2; i >= 0; i--) { + dp[i][1] = max(nums[i], nums[i] + dp[i + 1][1]); + dp[i][0] = max(dp[i + 1][0], dp[i][1]); + } + + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxSubArray(nums) { + const n = nums.length; + const dp = Array.from({ length: n + 1 }, () => Array(2).fill(0)); + + dp[n - 1][1] = dp[n - 1][0] = nums[n - 1]; + for (let i = n - 2; i >= 0; i--) { + dp[i][1] = Math.max(nums[i], nums[i] + dp[i + 1][1]); + dp[i][0] = Math.max(dp[i + 1][0], dp[i][1]); + } + + return dp[0][0]; + } +} +``` + +```csharp +public class Solution { + public int MaxSubArray(int[] nums) { + int n = nums.Length; + int[,] dp = new int[n + 1, 2]; + + dp[n - 1, 1] = dp[n - 1, 0] = nums[n - 1]; + for (int i = n - 2; i >= 0; i--) { + dp[i, 1] = Math.Max(nums[i], nums[i] + dp[i + 1, 1]); + dp[i, 0] = Math.Max(dp[i + 1, 0], dp[i, 1]); + } + + return dp[0, 0]; + } +} +``` + +```go +func maxSubArray(nums []int) int { + n := len(nums) + dp := make([][]int, n) + for i := range dp { + dp[i] = make([]int, 2) + } + + dp[n-1][1] = nums[n-1] + dp[n-1][0] = nums[n-1] + + for i := n-2; i >= 0; i-- { + dp[i][1] = max(nums[i], nums[i] + dp[i+1][1]) + dp[i][0] = max(dp[i+1][0], dp[i][1]) + } + + return dp[0][0] +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun maxSubArray(nums: IntArray): Int { + val n = nums.size + val dp = Array(n) { IntArray(2) } + + dp[n-1][1] = nums[n-1] + dp[n-1][0] = nums[n-1] + + for (i in n-2 downTo 0) { + dp[i][1] = maxOf(nums[i], nums[i] + dp[i+1][1]) + dp[i][0] = maxOf(dp[i+1][0], dp[i][1]) + } + + return dp[0][0] + } +} +``` + +```swift +class Solution { + func maxSubArray(_ nums: [Int]) -> Int { + let n = nums.count + var dp = Array(repeating: [0, 0], count: n) + dp[n - 1][1] = nums[n - 1] + dp[n - 1][0] = nums[n - 1] + + for i in (0..& nums) { + vector dp(nums); + for (int i = 1; i < nums.size(); i++) { + dp[i] = max(nums[i], nums[i] + dp[i - 1]); + } + return *max_element(dp.begin(), dp.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxSubArray(nums) { + let dp = [...nums]; + for (let i = 1; i < nums.length; i++) { + dp[i] = Math.max(nums[i], nums[i] + dp[i - 1]); + } + return Math.max(...dp); + } +} +``` + +```csharp +public class Solution { + public int MaxSubArray(int[] nums) { + int[] dp = (int[])nums.Clone(); + for (int i = 1; i < nums.Length; i++) { + dp[i] = Math.Max(nums[i], nums[i] + dp[i - 1]); + } + int maxSum = dp[0]; + foreach (int sum in dp) { + maxSum = Math.Max(maxSum, sum); + } + return maxSum; + } +} +``` + +```go +func maxSubArray(nums []int) int { + dp := make([]int, len(nums)) + copy(dp, nums) + + for i := 1; i < len(nums); i++ { + dp[i] = max(nums[i], nums[i] + dp[i-1]) + } + + maxSum := dp[0] + for _, v := range dp { + if v > maxSum { + maxSum = v + } + } + + return maxSum +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun maxSubArray(nums: IntArray): Int { + val dp = nums.copyOf() + + for (i in 1 until nums.size) { + dp[i] = maxOf(nums[i], nums[i] + dp[i-1]) + } + + return dp.maxOrNull() ?: nums[0] + } +} +``` + +```swift +class Solution { + func maxSubArray(_ nums: [Int]) -> Int { + var dp = nums + + for i in 1.. int: + maxSub, curSum = nums[0], 0 + for num in nums: + if curSum < 0: + curSum = 0 + curSum += num + maxSub = max(maxSub, curSum) + return maxSub + +``` + +```java +public class Solution { + public int maxSubArray(int[] nums) { + int maxSub = nums[0], curSum = 0; + for (int num : nums) { + if (curSum < 0) { + curSum = 0; + } + curSum += num; + maxSub = Math.max(maxSub, curSum); + } + return maxSub; + } +} +``` + +```cpp +class Solution { +public: + int maxSubArray(vector& nums) { + int maxSub = nums[0], curSum = 0; + for (int num : nums) { + if (curSum < 0) { + curSum = 0; + } + curSum += num; + maxSub = max(maxSub, curSum); + } + return maxSub; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxSubArray(nums) { + let maxSub = nums[0], curSum = 0; + for (const num of nums) { + if (curSum < 0) { + curSum = 0; + } + curSum += num; + maxSub = Math.max(maxSub, curSum); + } + return maxSub; + } +} +``` + +```csharp +public class Solution { + public int MaxSubArray(int[] nums) { + int maxSub = nums[0], curSum = 0; + foreach (int num in nums) { + if (curSum < 0) { + curSum = 0; + } + curSum += num; + maxSub = Math.Max(maxSub, curSum); + } + return maxSub; + } +} +``` + +```go +func maxSubArray(nums []int) int { + maxSub, curSum := nums[0], 0 + for _, num := range nums { + if curSum < 0 { + curSum = 0 + } + curSum += num + maxSub = max(maxSub, curSum) + } + return maxSub +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun maxSubArray(nums: IntArray): Int { + var maxSub = nums[0] + var curSum = 0 + for (num in nums) { + if (curSum < 0) { + curSum = 0 + } + curSum += num + maxSub = maxOf(maxSub, curSum) + } + return maxSub + } +} +``` + +```swift +class Solution { + func maxSubArray(_ nums: [Int]) -> Int { + var maxSub = nums[0] + var curSum = 0 + + for num in nums { + if curSum < 0 { + curSum = 0 + } + curSum += num + maxSub = max(maxSub, curSum) + } + + return maxSub + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 7. Divide & Conquer + +::tabs-start + +```python +class Solution: + def maxSubArray(self, nums: List[int]) -> int: + def dfs(l, r): + if l > r: + return float("-inf") + + m = (l + r) >> 1 + leftSum = rightSum = curSum = 0 + for i in range(m - 1, l - 1, -1): + curSum += nums[i] + leftSum = max(leftSum, curSum) + + curSum = 0 + for i in range(m + 1, r + 1): + curSum += nums[i] + rightSum = max(rightSum, curSum) + + return (max(dfs(l, m - 1), + dfs(m + 1, r), + leftSum + nums[m] + rightSum)) + + return dfs(0, len(nums) - 1) +``` + +```java +public class Solution { + public int maxSubArray(int[] nums) { + return dfs(nums, 0, nums.length - 1); + } + + private int dfs(int[] nums, int l, int r) { + if (l > r) { + return Integer.MIN_VALUE; + } + int m = (l + r) >> 1; + int leftSum = 0, rightSum = 0, curSum = 0; + for (int i = m - 1; i >= l; i--) { + curSum += nums[i]; + leftSum = Math.max(leftSum, curSum); + } + + curSum = 0; + for (int i = m + 1; i <= r; i++) { + curSum += nums[i]; + rightSum = Math.max(rightSum, curSum); + } + + return Math.max(dfs(nums, l, m - 1), + Math.max(dfs(nums, m + 1, r), + leftSum + nums[m] + rightSum)); + } +} +``` + +```cpp +class Solution { +public: + int maxSubArray(vector& nums) { + return dfs(nums, 0, nums.size() - 1); + } + +private: + int dfs(vector& nums, int l, int r) { + if (l > r) { + return INT_MIN; + } + int m = (l + r) >> 1; + int leftSum = 0, rightSum = 0, curSum = 0; + for (int i = m - 1; i >= l; --i) { + curSum += nums[i]; + leftSum = max(leftSum, curSum); + } + curSum = 0; + for (int i = m + 1; i <= r; ++i) { + curSum += nums[i]; + rightSum = max(rightSum, curSum); + } + return max(dfs(nums, l, m - 1), + max(dfs(nums, m + 1, r), + leftSum + nums[m] + rightSum)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxSubArray(nums) { + const dfs = (l, r) => { + if (l > r) { + return -Infinity; + } + let m = (l + r) >> 1; + let leftSum = 0, rightSum = 0, curSum = 0; + for (let i = m - 1; i >= l; i--) { + curSum += nums[i]; + leftSum = Math.max(leftSum, curSum); + } + + curSum = 0; + for (let i = m + 1; i <= r; i++) { + curSum += nums[i]; + rightSum = Math.max(rightSum, curSum); + } + return Math.max(dfs(l, m - 1), + Math.max(dfs(m + 1, r), + leftSum + nums[m] + rightSum)); + } + + return dfs(0, nums.length - 1); + } +} +``` + +```csharp +public class Solution { + public int MaxSubArray(int[] nums) { + return Dfs(nums, 0, nums.Length - 1); + } + + private int Dfs(int[] nums, int l, int r) { + if (l > r) { + return int.MinValue; + } + int m = (l + r) >> 1; + int leftSum = 0, rightSum = 0, curSum = 0; + for (int i = m - 1; i >= l; i--) { + curSum += nums[i]; + leftSum = Math.Max(leftSum, curSum); + } + + curSum = 0; + for (int i = m + 1; i <= r; i++) { + curSum += nums[i]; + rightSum = Math.Max(rightSum, curSum); + } + + return Math.Max(Dfs(nums, l, m - 1), + Math.Max(Dfs(nums, m + 1, r), + leftSum + nums[m] + rightSum)); + } +} +``` + +```go +func maxSubArray(nums []int) int { + var dfs func(l, r int) int + dfs = func(l, r int) int { + if l > r { + return math.MinInt64 + } + + m := (l + r) >> 1 + leftSum, rightSum, curSum := 0, 0, 0 + + for i := m - 1; i >= l; i-- { + curSum += nums[i] + if curSum > leftSum { + leftSum = curSum + } + } + + curSum = 0 + for i := m + 1; i <= r; i++ { + curSum += nums[i] + if curSum > rightSum { + rightSum = curSum + } + } + + maxLeft := dfs(l, m-1) + maxRight := dfs(m+1, r) + crossSum := leftSum + nums[m] + rightSum + + return max(max(maxLeft, maxRight), crossSum) + } + + return dfs(0, len(nums)-1) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun maxSubArray(nums: IntArray): Int { + fun dfs(l: Int, r: Int): Int { + if (l > r) { + return Int.MIN_VALUE + } + + val m = (l + r) shr 1 + var leftSum = 0 + var rightSum = 0 + var curSum = 0 + + for (i in (m - 1) downTo l) { + curSum += nums[i] + leftSum = maxOf(leftSum, curSum) + } + + curSum = 0 + for (i in (m + 1)..r) { + curSum += nums[i] + rightSum = maxOf(rightSum, curSum) + } + + return maxOf( + dfs(l, m - 1), + dfs(m + 1, r), + leftSum + nums[m] + rightSum + ) + } + + return dfs(0, nums.size - 1) + } +} +``` + +```swift +class Solution { + func maxSubArray(_ nums: [Int]) -> Int { + + func dfs(_ l: Int, _ r: Int) -> Int { + if l > r { + return Int.min + } + + let m = (l + r) / 2 + var leftSum = 0 + var rightSum = 0 + var curSum = 0 + + for i in stride(from: m - 1, through: l, by: -1) { + curSum += nums[i] + leftSum = max(leftSum, curSum) + } + + curSum = 0 + for i in stride(from: m + 1, to: r + 1, by: 1) { + curSum += nums[i] + rightSum = max(rightSum, curSum) + } + + return max(dfs(l, m - 1), dfs(m + 1, r), leftSum + nums[m] + rightSum) + } + + return dfs(0, nums.count - 1) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(\log n)$ \ No newline at end of file diff --git a/articles/maximum-subsequence-score.md b/articles/maximum-subsequence-score.md new file mode 100644 index 000000000..97c461caa --- /dev/null +++ b/articles/maximum-subsequence-score.md @@ -0,0 +1,412 @@ +## 1. Brute Force (Recursion) + +::tabs-start + +```python +class Solution: + def maxScore(self, nums1: List[int], nums2: List[int], k: int) -> int: + n = len(nums1) + + def dfs(i, k, minVal, curSum): + if k == 0: + return curSum * minVal + if i == n or (n - i) < k: + return float("-inf") + if minVal == 0: + return 0 + + res = dfs(i + 1, k, minVal, curSum) + res = max(res, dfs(i + 1, k - 1, min(minVal, nums2[i]), curSum + nums1[i])) + return res + + return dfs(0, k, float("inf"), 0) +``` + +```java +public class Solution { + private int[] nums1, nums2; + private int n; + + public long maxScore(int[] nums1, int[] nums2, int k) { + this.nums1 = nums1; + this.nums2 = nums2; + this.n = nums1.length; + return dfs(0, k, Integer.MAX_VALUE, 0); + } + + private long dfs(int i, int k, int minVal, long curSum) { + if (k == 0) { + return curSum * minVal; + } + if (i == n || (n - i) < k) { + return Integer.MIN_VALUE; + } + if (minVal == 0) { + return 0; + } + + long res = dfs(i + 1, k, minVal, curSum); + res = Math.max( + res, + dfs(i + 1, k - 1, Math.min(minVal, nums2[i]), curSum + nums1[i]) + ); + return res; + } +} +``` + +```cpp +class Solution { +private: + vector nums1, nums2; + int n; + +public: + long long maxScore(vector& nums1, vector& nums2, int k) { + this->nums1 = nums1; + this->nums2 = nums2; + this->n = nums1.size(); + return dfs(0, k, INT_MAX, 0); + } + +private: + long long dfs(int i, int k, int minVal, long long curSum) { + if (k == 0) { + return curSum * minVal; + } + if (i == n || (n - i) < k) { + return INT_MIN; + } + if (minVal == 0) { + return 0; + } + + long long res = dfs(i + 1, k, minVal, curSum); + res = max(res, dfs(i + 1, k - 1, min(minVal, nums2[i]), curSum + nums1[i])); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @param {number} k + * @return {number} + */ + maxScore(nums1, nums2, k) { + const n = nums1.length; + + const dfs = (i, k, minVal, curSum) => { + if (k === 0) { + return curSum * minVal; + } + if (i === n || (n - i) < k) { + return -Infinity; + } + if (minVal === 0) { + return 0; + } + + let res = dfs(i + 1, k, minVal, curSum); + res = Math.max( + res, + dfs(i + 1, k - 1, Math.min(minVal, nums2[i]), curSum + nums1[i]) + ); + return res; + }; + + return dfs(0, k, Infinity, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Min-Heap - I + +::tabs-start + +```python +class Solution: + def maxScore(self, nums1: List[int], nums2: List[int], k: int) -> int: + pairs = sorted(zip(nums1, nums2), key=lambda p: p[1], reverse=True) + + minHeap = [] + n1Sum = 0 + res = 0 + + for n1, n2 in pairs: + n1Sum += n1 + heapq.heappush(minHeap, n1) + + if len(minHeap) > k: + n1Sum -= heapq.heappop(minHeap) + + if len(minHeap) == k: + res = max(res, n1Sum * n2) + + return res +``` + +```java +public class Solution { + public long maxScore(int[] nums1, int[] nums2, int k) { + int n = nums1.length; + int[][] pairs = new int[n][2]; + + for (int i = 0; i < n; i++) { + pairs[i][0] = nums1[i]; + pairs[i][1] = nums2[i]; + } + + Arrays.sort(pairs, (a, b) -> Integer.compare(b[1], a[1])); + + PriorityQueue minHeap = new PriorityQueue<>(); + long n1Sum = 0, res = 0; + + for (int[] pair : pairs) { + n1Sum += pair[0]; + minHeap.offer(pair[0]); + + if (minHeap.size() > k) { + n1Sum -= minHeap.poll(); + } + + if (minHeap.size() == k) { + res = Math.max(res, n1Sum * pair[1]); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long maxScore(vector& nums1, vector& nums2, int k) { + int n = nums1.size(); + vector> pairs(n); + + for (int i = 0; i < n; i++) { + pairs[i] = {nums1[i], nums2[i]}; + } + + sort(pairs.begin(), pairs.end(), [](const auto& a, const auto& b) { + return b.second < a.second; + }); + + priority_queue, greater> minHeap; + long long n1Sum = 0, res = 0; + + for (auto& pair : pairs) { + n1Sum += pair.first; + minHeap.push(pair.first); + + if (minHeap.size() > k) { + n1Sum -= minHeap.top(); + minHeap.pop(); + } + + if (minHeap.size() == k) { + res = max(res, n1Sum * (long long)pair.second); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @param {number} k + * @return {number} + */ + maxScore(nums1, nums2, k) { + let pairs = nums1.map((n1, i) => [n1, nums2[i]]); + pairs.sort((a, b) => b[1] - a[1]); + + let minHeap = new MinPriorityQueue(); + let n1Sum = 0, res = 0; + + for (let [n1, n2] of pairs) { + n1Sum += n1; + minHeap.enqueue(n1); + + if (minHeap.size() > k) { + n1Sum -= minHeap.dequeue(); + } + + if (minHeap.size() === k) { + res = Math.max(res, n1Sum * n2); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Min-Heap - II + +::tabs-start + +```python +class Solution: + def maxScore(self, nums1: List[int], nums2: List[int], k: int) -> int: + n = len(nums1) + arr = [(nums2[i] << 30) | nums1[i] for i in range(n)] + arr.sort(reverse=True) + + minHeap = [] + n1Sum = 0 + res = 0 + + for num in arr: + n1, n2 = num & ((1 << 30) - 1), num >> 30 + n1Sum += n1 + heapq.heappush(minHeap, n1) + + if len(minHeap) > k: + n1Sum -= heapq.heappop(minHeap) + + if len(minHeap) == k: + res = max(res, n1Sum * n2) + + return res +``` + +```java +public class Solution { + public long maxScore(int[] nums1, int[] nums2, int k) { + int n = nums1.length; + long[] arr = new long[n]; + for (int i = 0; i < n; i++) { + arr[i] = ((long) nums2[i] << 30) | nums1[i]; + } + + Arrays.sort(arr); + PriorityQueue minHeap = new PriorityQueue<>(); + long n1Sum = 0, res = 0; + + for (int i = n - 1; i >= 0; i--) { + int n1 = (int) (arr[i] & ((1L << 30) - 1)); + int n2 = (int) (arr[i] >> 30); + n1Sum += n1; + minHeap.offer(n1); + + if (minHeap.size() > k) { + n1Sum -= minHeap.poll(); + } + if (minHeap.size() == k) { + res = Math.max(res, n1Sum * (long) n2); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long maxScore(vector& nums1, vector& nums2, int k) { + int n = nums1.size(); + vector arr(n); + for (int i = 0; i < n; i++) { + arr[i] = ((long long) nums2[i] << 30) | nums1[i]; + } + + sort(arr.rbegin(), arr.rend()); + priority_queue, greater> minHeap; + long long n1Sum = 0, res = 0; + + for (long long& num : arr) { + int n1 = num & ((1LL << 30) - 1); + int n2 = num >> 30; + n1Sum += n1; + minHeap.push(n1); + + if (minHeap.size() > k) { + n1Sum -= minHeap.top(); + minHeap.pop(); + } + if (minHeap.size() == k) { + res = max(res, n1Sum * (long long)n2); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @param {number} k + * @return {number} + */ + maxScore(nums1, nums2, k) { + const n = nums1.length; + const arr = []; + for (let i = 0; i < n; i++) { + arr.push((BigInt(nums2[i]) << BigInt(30)) | BigInt(nums1[i])); + } + + arr.sort((a, b) => Number(b - a)); + const minHeap = new MinPriorityQueue(); + let n1Sum = 0n, res = 0n; + + for (let num of arr) { + let n1 = Number(num & ((1n << 30n) - 1n)); + let n2 = Number(num >> 30n); + n1Sum += BigInt(n1); + minHeap.enqueue(n1); + + if (minHeap.size() > k) { + n1Sum -= BigInt(minHeap.dequeue()); + } + if (minHeap.size() === k) { + res = BigInt(Math.max(Number(res), Number(n1Sum * BigInt(n2)))); + } + } + + return Number(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/maximum-sum-circular-subarray.md b/articles/maximum-sum-circular-subarray.md new file mode 100644 index 000000000..d1f717448 --- /dev/null +++ b/articles/maximum-sum-circular-subarray.md @@ -0,0 +1,316 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxSubarraySumCircular(self, nums: List[int]) -> int: + n = len(nums) + res = nums[0] + + for i in range(n): + cur_sum = 0 + for j in range(i, i + n): + cur_sum += nums[j % n] + res = max(res, cur_sum) + + return res +``` + +```java +public class Solution { + public int maxSubarraySumCircular(int[] nums) { + int n = nums.length; + int res = nums[0]; + + for (int i = 0; i < n; i++) { + int curSum = 0; + for (int j = i; j < i + n; j++) { + curSum += nums[j % n]; + res = Math.max(res, curSum); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxSubarraySumCircular(vector& nums) { + int n = nums.size(); + int res = nums[0]; + + for (int i = 0; i < n; i++) { + int curSum = 0; + for (int j = i; j < i + n; j++) { + curSum += nums[j % n]; + res = max(res, curSum); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxSubarraySumCircular(nums) { + const n = nums.length; + let res = nums[0]; + + for (let i = 0; i < n; i++) { + let curSum = 0; + for (let j = i; j < i + n; j++) { + curSum += nums[j % n]; + res = Math.max(res, curSum); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Prefix & Suffix Sums + +::tabs-start + +```python +class Solution: + def maxSubarraySumCircular(self, nums: List[int]) -> int: + n = len(nums) + right_max = [0] * n + right_max[-1] = nums[-1] + suffix_sum = nums[-1] + + for i in range(n - 2, -1, -1): + suffix_sum += nums[i] + right_max[i] = max(right_max[i + 1], suffix_sum) + + max_sum = nums[0] + cur_max = 0 + prefix_sum = 0 + + for i in range(n): + cur_max = max(cur_max, 0) + nums[i] + max_sum = max(max_sum, cur_max) + prefix_sum += nums[i] + if i + 1 < n: + max_sum = max(max_sum, prefix_sum + right_max[i + 1]) + + return max_sum +``` + +```java +public class Solution { + public int maxSubarraySumCircular(int[] nums) { + int n = nums.length; + int[] rightMax = new int[n]; + rightMax[n - 1] = nums[n - 1]; + int suffixSum = nums[n - 1]; + + for (int i = n - 2; i >= 0; i--) { + suffixSum += nums[i]; + rightMax[i] = Math.max(rightMax[i + 1], suffixSum); + } + + int maxSum = nums[0]; + int curMax = 0; + int prefixSum = 0; + + for (int i = 0; i < n; i++) { + curMax = Math.max(curMax, 0) + nums[i]; + maxSum = Math.max(maxSum, curMax); + prefixSum += nums[i]; + if (i + 1 < n) { + maxSum = Math.max(maxSum, prefixSum + rightMax[i + 1]); + } + } + + return maxSum; + } +} +``` + +```cpp +class Solution { +public: + int maxSubarraySumCircular(vector& nums) { + int n = nums.size(); + vector rightMax(n); + rightMax[n - 1] = nums[n - 1]; + int suffixSum = nums[n - 1]; + + for (int i = n - 2; i >= 0; --i) { + suffixSum += nums[i]; + rightMax[i] = max(rightMax[i + 1], suffixSum); + } + + int maxSum = nums[0]; + int curMax = 0; + int prefixSum = 0; + + for (int i = 0; i < n; ++i) { + curMax = max(curMax, 0) + nums[i]; + maxSum = max(maxSum, curMax); + prefixSum += nums[i]; + if (i + 1 < n) { + maxSum = max(maxSum, prefixSum + rightMax[i + 1]); + } + } + + return maxSum; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxSubarraySumCircular(nums) { + const n = nums.length; + const rightMax = new Array(n).fill(0); + rightMax[n - 1] = nums[n - 1]; + let suffixSum = nums[n - 1]; + + for (let i = n - 2; i >= 0; i--) { + suffixSum += nums[i]; + rightMax[i] = Math.max(rightMax[i + 1], suffixSum); + } + + let maxSum = nums[0]; + let curMax = 0; + let prefixSum = 0; + + for (let i = 0; i < n; i++) { + curMax = Math.max(curMax, 0) + nums[i]; + maxSum = Math.max(maxSum, curMax); + prefixSum += nums[i]; + if (i + 1 < n) { + maxSum = Math.max(maxSum, prefixSum + rightMax[i + 1]); + } + } + + return maxSum; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Kadane's Algorithm + +::tabs-start + +```python +class Solution: + def maxSubarraySumCircular(self, nums: List[int]) -> int: + globMax, globMin = nums[0], nums[0] + curMax, curMin = 0, 0 + total = 0 + + for num in nums: + curMax = max(curMax + num, num) + curMin = min(curMin + num, num) + total += num + globMax = max(globMax, curMax) + globMin = min(globMin, curMin) + + return max(globMax, total - globMin) if globMax > 0 else globMax +``` + +```java +public class Solution { + public int maxSubarraySumCircular(int[] nums) { + int globMax = nums[0], globMin = nums[0]; + int curMax = 0, curMin = 0, total = 0; + + for (int num : nums) { + curMax = Math.max(curMax + num, num); + curMin = Math.min(curMin + num, num); + total += num; + globMax = Math.max(globMax, curMax); + globMin = Math.min(globMin, curMin); + } + + return globMax > 0 ? Math.max(globMax, total - globMin) : globMax; + } +} +``` + +```cpp +class Solution { +public: + int maxSubarraySumCircular(vector& nums) { + int globMax = nums[0], globMin = nums[0]; + int curMax = 0, curMin = 0, total = 0; + + for (int& num : nums) { + curMax = max(curMax + num, num); + curMin = min(curMin + num, num); + total += num; + globMax = max(globMax, curMax); + globMin = min(globMin, curMin); + } + + return globMax > 0 ? max(globMax, total - globMin) : globMax; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + maxSubarraySumCircular(nums) { + let globMax = nums[0], globMin = nums[0]; + let curMax = 0, curMin = 0, total = 0; + + for (const num of nums) { + curMax = Math.max(curMax + num, num); + curMin = Math.min(curMin + num, num); + total += num; + globMax = Math.max(globMax, curMax); + globMin = Math.min(globMin, curMin); + } + + return globMax > 0 ? Math.max(globMax, total - globMin) : globMax; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/maximum-twin-sum-of-a-linked-list.md b/articles/maximum-twin-sum-of-a-linked-list.md new file mode 100644 index 000000000..b896b8cae --- /dev/null +++ b/articles/maximum-twin-sum-of-a-linked-list.md @@ -0,0 +1,450 @@ +## 1. Convert To Array + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def pairSum(self, head: Optional[ListNode]) -> int: + arr = [] + cur = head + while cur: + arr.append(cur.val) + cur = cur.next + + i, j = 0, len(arr) - 1 + res = 0 + while i < j: + res = max(res, arr[i] + arr[j]) + i, j = i + 1, j - 1 + + return res +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public int pairSum(ListNode head) { + List arr = new ArrayList<>(); + ListNode cur = head; + + while (cur != null) { + arr.add(cur.val); + cur = cur.next; + } + + int i = 0, j = arr.size() - 1, res = 0; + while (i < j) { + res = Math.max(res, arr.get(i) + arr.get(j)); + i++; + j--; + } + + return res; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + int pairSum(ListNode* head) { + vector arr; + ListNode* cur = head; + + while (cur) { + arr.push_back(cur->val); + cur = cur->next; + } + + int i = 0, j = arr.size() - 1, res = 0; + while (i < j) { + res = max(res, arr[i] + arr[j]); + i++; + j--; + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {number} + */ + pairSum(head) { + let arr = []; + let cur = head; + + while (cur) { + arr.push(cur.val); + cur = cur.next; + } + + let i = 0, j = arr.length - 1, res = 0; + while (i < j) { + res = Math.max(res, arr[i] + arr[j]); + i++; + j--; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Reverse the Second Half + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def pairSum(self, head: Optional[ListNode]) -> int: + slow, fast = head, head + while fast and fast.next: + slow = slow.next + fast = fast.next.next + + prev, cur = None, slow + while cur: + nxt = cur.next + cur.next = prev + prev = cur + cur = nxt + + res = 0 + first, second = head, prev + while second: + res = max(res, first.val + second.val) + first, second = first.next, second.next + + return res +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public int pairSum(ListNode head) { + ListNode slow = head, fast = head; + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + } + + ListNode prev = null, cur = slow; + while (cur != null) { + ListNode nxt = cur.next; + cur.next = prev; + prev = cur; + cur = nxt; + } + + int res = 0; + ListNode first = head, second = prev; + while (second != null) { + res = Math.max(res, first.val + second.val); + first = first.next; + second = second.next; + } + + return res; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + int pairSum(ListNode* head) { + ListNode* slow = head, *fast = head; + while (fast && fast->next) { + slow = slow->next; + fast = fast->next->next; + } + + ListNode* prev = nullptr, *cur = slow; + while (cur) { + ListNode* nxt = cur->next; + cur->next = prev; + prev = cur; + cur = nxt; + } + + int res = 0; + ListNode* first = head, *second = prev; + while (second) { + res = max(res, first->val + second->val); + first = first->next; + second = second->next; + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {number} + */ + pairSum(head) { + let slow = head, fast = head; + while (fast && fast.next) { + slow = slow.next; + fast = fast.next.next; + } + + let prev = null, cur = slow; + while (cur) { + let nxt = cur.next; + cur.next = prev; + prev = cur; + cur = nxt; + } + + let res = 0, first = head, second = prev; + while (second) { + res = Math.max(res, first.val + second.val); + first = first.next; + second = second.next; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Reverse the First Half + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def pairSum(self, head: Optional[ListNode]) -> int: + slow, fast = head, head + prev = None + + while fast and fast.next: + fast = fast.next.next + tmp = slow.next + slow.next = prev + prev = slow + slow = tmp + + res = 0 + while slow: + res = max(res, prev.val + slow.val) + prev = prev.next + slow = slow.next + + return res +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public int pairSum(ListNode head) { + ListNode slow = head, fast = head, prev = null; + + while (fast != null && fast.next != null) { + fast = fast.next.next; + ListNode tmp = slow.next; + slow.next = prev; + prev = slow; + slow = tmp; + } + + int res = 0; + while (slow != null) { + res = Math.max(res, prev.val + slow.val); + prev = prev.next; + slow = slow.next; + } + + return res; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + int pairSum(ListNode* head) { + ListNode* slow = head, *fast = head, *prev = nullptr; + + while (fast && fast->next) { + fast = fast->next->next; + ListNode* tmp = slow->next; + slow->next = prev; + prev = slow; + slow = tmp; + } + + int res = 0; + while (slow) { + res = max(res, prev->val + slow->val); + prev = prev->next; + slow = slow->next; + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {number} + */ + pairSum(head) { + let slow = head, fast = head, prev = null; + + while (fast && fast.next) { + fast = fast.next.next; + let tmp = slow.next; + slow.next = prev; + prev = slow; + slow = tmp; + } + + let res = 0; + while (slow) { + res = Math.max(res, prev.val + slow.val); + prev = prev.next; + slow = slow.next; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/maximum-value-of-k-coins-from-piles.md b/articles/maximum-value-of-k-coins-from-piles.md new file mode 100644 index 000000000..bf108e73c --- /dev/null +++ b/articles/maximum-value-of-k-coins-from-piles.md @@ -0,0 +1,451 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def maxValueOfCoins(self, piles: List[List[int]], k: int) -> int: + n = len(piles) + + def dfs(i, coins): + if i == n: + return 0 + + res = dfs(i + 1, coins) # skip current pile + curPile = 0 + for j in range(min(coins, len(piles[i]))): + curPile += piles[i][j] + res = max(res, curPile + dfs(i + 1, coins - (j + 1))) + return res + + return dfs(0, k) +``` + +```java +public class Solution { + public int maxValueOfCoins(List> piles, int k) { + int n = piles.size(); + return dfs(0, k, piles); + } + + private int dfs(int i, int coins, List> piles) { + if (i == piles.size()) { + return 0; + } + + int res = dfs(i + 1, coins, piles); // skip current pile + int curPile = 0; + for (int j = 0; j < Math.min(coins, piles.get(i).size()); j++) { + curPile += piles.get(i).get(j); + res = Math.max(res, curPile + dfs(i + 1, coins - (j + 1), piles)); + } + return res; + } +} +``` + +```cpp +class Solution { +private: + int dfs(int i, int coins, const vector>& piles) { + if (i == piles.size()) { + return 0; + } + + int res = dfs(i + 1, coins, piles); // skip current pile + int curPile = 0; + for (int j = 0; j < min(coins, (int)piles[i].size()); j++) { + curPile += piles[i][j]; + res = max(res, curPile + dfs(i + 1, coins - (j + 1), piles)); + } + return res; + } + +public: + int maxValueOfCoins(vector>& piles, int k) { + int n = piles.size(); + return dfs(0, k, piles); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} piles + * @param {number} k + * @return {number} + */ + maxValueOfCoins = (piles, k) => { + const n = piles.length; + + const dfs = (i, coins) => { + if (i === n) return 0; + + let res = dfs(i + 1, coins); // skip current pile + let curPile = 0; + for (let j = 0; j < Math.min(coins, piles[i].length); j++) { + curPile += piles[i][j]; + res = Math.max(res, curPile + dfs(i + 1, coins - (j + 1))); + } + return res; + }; + + return dfs(0, k); + }; +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k ^ n)$ +* Space complexity: $O(n)$ for the recursion stack. + +> Where $n$ is the number of piles and $k$ is the number of coins to choose. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def maxValueOfCoins(self, piles: List[List[int]], k: int) -> int: + n = len(piles) + dp = [[-1] * (k + 1) for _ in range(n)] + + def dfs(i, coins): + if i == n: + return 0 + if dp[i][coins] != -1: + return dp[i][coins] + + dp[i][coins] = dfs(i + 1, coins) # skip current pile + curPile = 0 + for j in range(min(coins, len(piles[i]))): + curPile += piles[i][j] + dp[i][coins] = max(dp[i][coins], curPile + dfs(i + 1, coins - (j + 1))) + return dp[i][coins] + + return dfs(0, k) +``` + +```java +public class Solution { + private int[][] dp; + + public int maxValueOfCoins(List> piles, int k) { + int n = piles.size(); + dp = new int[n][k + 1]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + return dfs(0, k, piles); + } + + private int dfs(int i, int coins, List> piles) { + if (i == piles.size()) { + return 0; + } + if (dp[i][coins] != -1) { + return dp[i][coins]; + } + + dp[i][coins] = dfs(i + 1, coins, piles); // skip current pile + int curPile = 0; + for (int j = 0; j < Math.min(coins, piles.get(i).size()); j++) { + curPile += piles.get(i).get(j); + dp[i][coins] = Math.max(dp[i][coins], curPile + dfs(i + 1, coins - (j + 1), piles)); + } + return dp[i][coins]; + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + + int dfs(int i, int coins, const vector>& piles) { + if (i == piles.size()) { + return 0; + } + if (dp[i][coins] != -1) { + return dp[i][coins]; + } + + dp[i][coins] = dfs(i + 1, coins, piles); // skip current pile + int curPile = 0; + for (int j = 0; j < min(coins, (int)piles[i].size()); j++) { + curPile += piles[i][j]; + dp[i][coins] = max(dp[i][coins], curPile + dfs(i + 1, coins - (j + 1), piles)); + } + return dp[i][coins]; + } + +public: + int maxValueOfCoins(vector>& piles, int k) { + int n = piles.size(); + dp = vector>(n, vector(k + 1, -1)); + return dfs(0, k, piles); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} piles + * @param {number} k + * @return {number} + */ + maxValueOfCoins = (piles, k) => { + const n = piles.length; + const dp = Array.from({ length: n }, () => Array(k + 1).fill(-1)); + + const dfs = (i, coins) => { + if (i === n) return 0; + if (dp[i][coins] !== -1) return dp[i][coins]; + + dp[i][coins] = dfs(i + 1, coins); // skip current pile + let curPile = 0; + for (let j = 0; j < Math.min(coins, piles[i].length); j++) { + curPile += piles[i][j]; + dp[i][coins] = Math.max(dp[i][coins], curPile + dfs(i + 1, coins - (j + 1))); + } + return dp[i][coins]; + }; + + return dfs(0, k); + }; +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ is the number of piles, $k$ is the number of coins to choose, and $m$ is the total number of coins among all the piles. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def maxValueOfCoins(self, piles: List[List[int]], k: int) -> int: + n = len(piles) + dp = [[0] * (k + 1) for _ in range(n + 1)] + + for i in range(n - 1, -1, -1): + for coins in range(k + 1): + dp[i][coins] = dp[i + 1][coins] + + curPile = 0 + for j in range(min(coins, len(piles[i]))): + curPile += piles[i][j] + dp[i][coins] = max( + dp[i][coins], + curPile + dp[i + 1][coins - (j + 1)] + ) + + return dp[0][k] +``` + +```java +public class Solution { + public int maxValueOfCoins(List> piles, int k) { + int n = piles.size(); + int[][] dp = new int[n + 1][k + 1]; + + for (int i = n - 1; i >= 0; i--) { + for (int coins = 0; coins <= k; coins++) { + dp[i][coins] = dp[i + 1][coins]; + + int curPile = 0; + for (int j = 0; j < Math.min(coins, piles.get(i).size()); j++) { + curPile += piles.get(i).get(j); + dp[i][coins] = Math.max( + dp[i][coins], + curPile + dp[i + 1][coins - (j + 1)] + ); + } + } + } + + return dp[0][k]; + } +} +``` + +```cpp +class Solution { +public: + int maxValueOfCoins(vector>& piles, int k) { + int n = piles.size(); + vector> dp(n + 1, vector(k + 1, 0)); + + for (int i = n - 1; i >= 0; i--) { + for (int coins = 0; coins <= k; coins++) { + dp[i][coins] = dp[i + 1][coins]; + + int curPile = 0; + for (int j = 0; j < min(coins, (int)piles[i].size()); j++) { + curPile += piles[i][j]; + dp[i][coins] = max( + dp[i][coins], + curPile + dp[i + 1][coins - (j + 1)] + ); + } + } + } + + return dp[0][k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} piles + * @param {number} k + * @return {number} + */ + maxValueOfCoins = (piles, k) => { + const n = piles.length; + const dp = Array.from({ length: n + 1 }, () => Array(k + 1).fill(0)); + + for (let i = n - 1; i >= 0; i--) { + for (let coins = 0; coins <= k; coins++) { + dp[i][coins] = dp[i + 1][coins]; + + let curPile = 0; + for (let j = 0; j < Math.min(coins, piles[i].length); j++) { + curPile += piles[i][j]; + dp[i][coins] = Math.max( + dp[i][coins], + curPile + dp[i + 1][coins - (j + 1)] + ); + } + } + } + + return dp[0][k]; + }; +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ is the number of piles, $k$ is the number of coins to choose, and $m$ is the total number of coins among all the piles. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def maxValueOfCoins(self, piles: List[List[int]], k: int) -> int: + dp = [0] * (k + 1) + + for pile in piles: + for coins in range(k, 0, -1): + curPile = 0 + for j in range(min(coins, len(pile))): + curPile += pile[j] + dp[coins] = max(dp[coins], dp[coins - (j + 1)] + curPile) + + return dp[k] +``` + +```java +public class Solution { + public int maxValueOfCoins(List> piles, int k) { + int[] dp = new int[k + 1]; + + for (List pile : piles) { + for (int coins = k; coins >= 1; coins--) { + int curPile = 0; + for (int j = 0; j < Math.min(coins, pile.size()); j++) { + curPile += pile.get(j); + dp[coins] = Math.max(dp[coins], dp[coins - (j + 1)] + curPile); + } + } + } + + return dp[k]; + } +} +``` + +```cpp +class Solution { +public: + int maxValueOfCoins(vector>& piles, int k) { + vector dp(k + 1, 0); + + for (const auto& pile : piles) { + for (int coins = k; coins >= 1; coins--) { + int curPile = 0; + for (int j = 0; j < min(coins, (int)pile.size()); j++) { + curPile += pile[j]; + dp[coins] = max(dp[coins], dp[coins - (j + 1)] + curPile); + } + } + } + + return dp[k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} piles + * @param {number} k + * @return {number} + */ + maxValueOfCoins = (piles, k) => { + let dp = Array(k + 1).fill(0); + + for (const pile of piles) { + for (let coins = k; coins >= 1; coins--) { + let curPile = 0; + for (let j = 0; j < Math.min(coins, pile.length); j++) { + curPile += pile[j]; + dp[coins] = Math.max(dp[coins], dp[coins - (j + 1)] + curPile); + } + } + } + + return dp[k]; + }; +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ is the number of piles, $k$ is the number of coins to choose, and $m$ is the total number of coins among all the piles. \ No newline at end of file diff --git a/articles/maximum-width-of-binary-tree.md b/articles/maximum-width-of-binary-tree.md new file mode 100644 index 000000000..ba741fb5d --- /dev/null +++ b/articles/maximum-width-of-binary-tree.md @@ -0,0 +1,513 @@ +## 1. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def widthOfBinaryTree(self, root: Optional[TreeNode]) -> int: + res = 0 + q = deque([(root, 1, 0)]) # [node, num, level] + prevLevel, prevNum = 0, 1 + + while q: + node, num, level = q.popleft() + + if level > prevLevel: + prevLevel = level + prevNum = num + + res = max(res, num - prevNum + 1) + if node.left: + q.append((node.left, 2 * num, level + 1)) + if node.right: + q.append((node.right, 2 * num + 1, level + 1)) + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int widthOfBinaryTree(TreeNode root) { + if (root == null) return 0; + + int res = 0; + Queue queue = new LinkedList<>(); + queue.offer(new Tuple(root, 1, 0)); // [node, num, level] + int prevLevel = 0, prevNum = 1; + + while (!queue.isEmpty()) { + Tuple current = queue.poll(); + TreeNode node = current.node; + int num = current.num; + int level = current.level; + + if (level > prevLevel) { + prevLevel = level; + prevNum = num; + } + + res = Math.max(res, num - prevNum + 1); + if (node.left != null) { + queue.offer(new Tuple(node.left, 2 * num, level + 1)); + } + if (node.right != null) { + queue.offer(new Tuple(node.right, 2 * num + 1, level + 1)); + } + } + + return res; + } + + class Tuple { + TreeNode node; + int num, level; + + Tuple(TreeNode node, int num, int level) { + this.node = node; + this.num = num; + this.level = level; + } + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int widthOfBinaryTree(TreeNode* root) { + if (!root) return 0; + + int res = 0; + queue> q; // [node, num, level] + q.push({root, 1, 0}); + int prevLevel = 0, prevNum = 1; + + while (!q.empty()) { + auto [node, num, level] = q.front(); + q.pop(); + + if (level > prevLevel) { + prevLevel = level; + prevNum = num; + } + + res = max(res, int(num - prevNum) + 1); + if (node->left) { + q.push({node->left, 2 * num, level + 1}); + } + if (node->right) { + q.push({node->right, 2 * num + 1, level + 1}); + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + widthOfBinaryTree(root) { + if (!root) return 0; + + let res = 0n; + const queue = new Queue([[root, 1n, 0]]); // [node, num, level] + let prevLevel = 0, prevNum = 1n; + + while (!queue.isEmpty()) { + const [node, num, level] = queue.pop(); + + if (level > prevLevel) { + prevLevel = level; + prevNum = num; + } + + res = res > (num - prevNum + 1n) ? res : (num - prevNum + 1n); + if (node.left) { + queue.push([node.left, 2n * num, level + 1]); + } + if (node.right) { + queue.push([node.right, 2n * num + 1n, level + 1]); + } + } + + return Number(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Breadth First Search (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def widthOfBinaryTree(self, root: Optional[TreeNode]) -> int: + res = 0 + q = deque([(root, 0)]) + + while q: + start = q[0][1] + for _ in range(len(q)): + node, num = q.popleft() + curNum = num - start + res = max(res, curNum + 1) + if node.left: + q.append((node.left, 2 * curNum)) + if node.right: + q.append((node.right, 2 * curNum + 1)) + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int widthOfBinaryTree(TreeNode root) { + int res = 0; + Queue> q = new LinkedList<>(); + q.offer(new Pair<>(root, 0)); + + while (!q.isEmpty()) { + int start = q.peek().getValue(); + for (int i = q.size(); i > 0; i--) { + Pair pair = q.poll(); + TreeNode node = pair.getKey(); + int num = pair.getValue() - start; + + res = Math.max(res, num + 1); + if (node.left != null) { + q.offer(new Pair<>(node.left, 2 * num)); + } + if (node.right != null) { + q.offer(new Pair<>(node.right, 2 * num + 1)); + } + } + } + + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int widthOfBinaryTree(TreeNode* root) { + int res = 0; + queue> q; + q.push({root, 0}); + + while (!q.empty()) { + int start = q.front().second; + + for (int i = q.size(); i > 0; --i) { + auto [node, num] = q.front(); + q.pop(); + uint curNum = num - start; + res = max(res, int(curNum) + 1); + if (node->left) { + q.push({node->left, 2 * curNum}); + } + if (node->right) { + q.push({node->right, 2 * curNum + 1}); + } + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + widthOfBinaryTree(root) { + let res = 0; + const q = new Queue([[root, 0]]); + + while (!q.isEmpty()) { + const start = q.front()[1]; + + for (let i = q.size(); i > 0; i--) { + const [node, num] = q.pop(); + const curNum = num - start; + res = Math.max(res, curNum + 1); + if (node.left) { + q.push([node.left, 2 * curNum]); + } + if (node.right) { + q.push([node.right, 2 * curNum + 1]); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def widthOfBinaryTree(self, root: Optional[TreeNode]) -> int: + first = {} + res = 0 + + def dfs(node, level, num): + nonlocal res + if not node: + return + + if level not in first: + first[level] = num + + res = max(res, num - first[level] + 1) + dfs(node.left, level + 1, 2 * num) + dfs(node.right, level + 1, 2 * num + 1) + + dfs(root, 0, 0) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private Map first; + public int widthOfBinaryTree(TreeNode root) { + first = new HashMap<>(); + int[] res = new int[1]; + + dfs(root, 0, 0, res); + return res[0]; + } + + private void dfs(TreeNode node, int level, int num, int[] res) { + if (node == null) { + return; + } + + first.putIfAbsent(level, num); + res[0] = Math.max(res[0], num - first.get(level) + 1); + dfs(node.left, level + 1, 2 * num, res); + dfs(node.right, level + 1, 2 * num + 1, res); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { + unordered_map first; + +public: + int widthOfBinaryTree(TreeNode* root) { + unsigned long long res = 0; + + dfs(root, 0, 0, res); + return int(res); + } + +private: + void dfs(TreeNode* node, int level, unsigned long long num, unsigned long long& res) { + if (!node) { + return; + } + + if (!first.count(level)) { + first[level] = num; + } + + res = max(res, num - first[level] + 1); + dfs(node->left, level + 1, 2 * (num - first[level]), res); + dfs(node->right, level + 1, 2 * (num - first[level]) + 1, res); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + widthOfBinaryTree(root) { + const first = new Map(); + let res = 0; + + const dfs = (node, level, curNum) => { + if (!node) return; + + if (!first.has(level)) { + first.set(level, curNum); + } + + res = Math.max(res, curNum - first.get(level) + 1); + dfs(node.left, level + 1, 2 * (curNum - first.get(level))); + dfs(node.right, level + 1, 2 * (curNum - first.get(level)) + 1); + }; + + dfs(root, 0, 0); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/median-of-two-sorted-arrays.md b/articles/median-of-two-sorted-arrays.md new file mode 100644 index 000000000..3ac051111 --- /dev/null +++ b/articles/median-of-two-sorted-arrays.md @@ -0,0 +1,1100 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float: + len1 = len(nums1) + len2 = len(nums2) + merged = nums1 + nums2 + merged.sort() + + totalLen = len(merged) + if totalLen % 2 == 0: + return (merged[totalLen // 2 - 1] + merged[totalLen // 2]) / 2.0 + else: + return merged[totalLen // 2] +``` + +```java +public class Solution { + public double findMedianSortedArrays(int[] nums1, int[] nums2) { + int len1 = nums1.length, len2 = nums2.length; + int[] merged = new int[len1 + len2]; + System.arraycopy(nums1, 0, merged, 0, len1); + System.arraycopy(nums2, 0, merged, len1, len2); + Arrays.sort(merged); + + int totalLen = merged.length; + if (totalLen % 2 == 0) { + return (merged[totalLen / 2 - 1] + merged[totalLen / 2]) / 2.0; + } else { + return merged[totalLen / 2]; + } + } +} +``` + +```cpp +class Solution { +public: + double findMedianSortedArrays(vector& nums1, vector& nums2) { + int len1 = nums1.size(); + int len2 = nums2.size(); + vector merged(len1 + len2); + copy(nums1.begin(), nums1.end(), merged.begin()); + copy(nums2.begin(), nums2.end(), merged.begin() + len1); + sort(merged.begin(), merged.end()); + + int totalLen = merged.size(); + if (totalLen % 2 == 0) { + return (merged[totalLen / 2 - 1] + merged[totalLen / 2]) / 2.0; + } else { + return merged[totalLen / 2]; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number} + */ + findMedianSortedArrays(nums1, nums2) { + const len1 = nums1.length; + const len2 = nums2.length; + const merged = nums1.concat(nums2); + merged.sort((a, b) => a - b); + + const totalLen = merged.length; + if (totalLen % 2 === 0) { + return (merged[totalLen / 2 - 1] + merged[totalLen / 2]) / 2.0; + } else { + return merged[Math.floor(totalLen / 2)]; + } + } +} +``` + +```csharp +public class Solution { + public double FindMedianSortedArrays(int[] nums1, int[] nums2) { + int len1 = nums1.Length; + int len2 = nums2.Length; + int[] merged = new int[len1 + len2]; + Array.Copy(nums1, merged, len1); + Array.Copy(nums2, 0, merged, len1, len2); + Array.Sort(merged); + + int totalLen = merged.Length; + if (totalLen % 2 == 0) { + return (merged[totalLen / 2 - 1] + merged[totalLen / 2]) / 2.0; + } else { + return merged[totalLen / 2]; + } + } +} +``` + +```go +func findMedianSortedArrays(nums1 []int, nums2 []int) float64 { + merged := append(nums1, nums2...) + sort.Ints(merged) + + totalLen := len(merged) + if totalLen%2 == 0 { + return float64(merged[totalLen/2-1]+merged[totalLen/2]) / 2.0 + } + return float64(merged[totalLen/2]) +} +``` + +```kotlin +class Solution { + fun findMedianSortedArrays(nums1: IntArray, nums2: IntArray): Double { + val merged = (nums1 + nums2).sorted() + val totalLen = merged.size + return if (totalLen % 2 == 0) { + (merged[totalLen / 2 - 1] + merged[totalLen / 2]) / 2.0 + } else { + merged[totalLen / 2].toDouble() + } + } +} +``` + +```swift +class Solution { + func findMedianSortedArrays(_ nums1: [Int], _ nums2: [Int]) -> Double { + var merged = nums1 + nums2 + merged.sort() + + let totalLen = merged.count + if totalLen % 2 == 0 { + return (Double(merged[totalLen / 2 - 1]) + Double(merged[totalLen / 2])) / 2.0 + } else { + return Double(merged[totalLen / 2]) + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((n + m)\log (n + m))$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the length of $nums1$ and $m$ is the length of $nums2$. + +--- + +## 2. Two Pointers + +::tabs-start + +```python +class Solution: + def findMedianSortedArrays(self, nums1, nums2): + len1, len2 = len(nums1), len(nums2) + i = j = 0 + median1 = median2 = 0 + + for count in range((len1 + len2) // 2 + 1): + median2 = median1 + if i < len1 and j < len2: + if nums1[i] > nums2[j]: + median1 = nums2[j] + j += 1 + else: + median1 = nums1[i] + i += 1 + elif i < len1: + median1 = nums1[i] + i += 1 + else: + median1 = nums2[j] + j += 1 + + if (len1 + len2) % 2 == 1: + return float(median1) + else: + return (median1 + median2) / 2.0 +``` + +```java +public class Solution { + public double findMedianSortedArrays(int[] nums1, int[] nums2) { + int len1 = nums1.length, len2 = nums2.length; + int i = 0, j = 0; + int median1 = 0, median2 = 0; + + for (int count = 0; count < (len1 + len2) / 2 + 1; count++) { + median2 = median1; + if (i < len1 && j < len2) { + if (nums1[i] > nums2[j]) { + median1 = nums2[j]; + j++; + } else { + median1 = nums1[i]; + i++; + } + } else if (i < len1) { + median1 = nums1[i]; + i++; + } else { + median1 = nums2[j]; + j++; + } + } + + if ((len1 + len2) % 2 == 1) { + return (double) median1; + } else { + return (median1 + median2) / 2.0; + } + } +} +``` + +```cpp +class Solution { +public: + double findMedianSortedArrays(vector& nums1, vector& nums2) { + int len1 = nums1.size(), len2 = nums2.size(); + int i = 0, j = 0; + int median1 = 0, median2 = 0; + + for (int count = 0; count < (len1 + len2) / 2 + 1; count++) { + median2 = median1; + if (i < len1 && j < len2) { + if (nums1[i] > nums2[j]) { + median1 = nums2[j]; + j++; + } else { + median1 = nums1[i]; + i++; + } + } else if (i < len1) { + median1 = nums1[i]; + i++; + } else { + median1 = nums2[j]; + j++; + } + } + + if ((len1 + len2) % 2 == 1) { + return (double) median1; + } else { + return (median1 + median2) / 2.0; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number} + */ + findMedianSortedArrays(nums1, nums2) { + let len1 = nums1.length, len2 = nums2.length; + let i = 0, j = 0; + let median1 = 0, median2 = 0; + + for (let count = 0; count < Math.floor((len1 + len2) / 2) + 1; count++) { + median2 = median1; + if (i < len1 && j < len2) { + if (nums1[i] > nums2[j]) { + median1 = nums2[j]; + j++; + } else { + median1 = nums1[i]; + i++; + } + } else if (i < len1) { + median1 = nums1[i]; + i++; + } else { + median1 = nums2[j]; + j++; + } + } + + if ((len1 + len2) % 2 === 1) { + return median1; + } else { + return (median1 + median2) / 2.0; + } + } +} +``` + +```csharp +public class Solution { + public double FindMedianSortedArrays(int[] nums1, int[] nums2) { + int len1 = nums1.Length, len2 = nums2.Length; + int i = 0, j = 0; + int median1 = 0, median2 = 0; + + for (int count = 0; count < (len1 + len2) / 2 + 1; count++) { + median2 = median1; + if (i < len1 && j < len2) { + if (nums1[i] > nums2[j]) { + median1 = nums2[j]; + j++; + } else { + median1 = nums1[i]; + i++; + } + } else if (i < len1) { + median1 = nums1[i]; + i++; + } else { + median1 = nums2[j]; + j++; + } + } + + if ((len1 + len2) % 2 == 1) { + return (double) median1; + } else { + return (median1 + median2) / 2.0; + } + } +} +``` + +```go +func findMedianSortedArrays(nums1 []int, nums2 []int) float64 { + len1, len2 := len(nums1), len(nums2) + i, j := 0, 0 + median1, median2 := 0, 0 + + for count := 0; count < (len1+len2)/2+1; count++ { + median2 = median1 + if i < len1 && j < len2 { + if nums1[i] > nums2[j] { + median1 = nums2[j] + j++ + } else { + median1 = nums1[i] + i++ + } + } else if i < len1 { + median1 = nums1[i] + i++ + } else { + median1 = nums2[j] + j++ + } + } + + if (len1+len2)%2 == 1 { + return float64(median1) + } + return float64(median1+median2) / 2.0 +} +``` + +```kotlin +class Solution { + fun findMedianSortedArrays(nums1: IntArray, nums2: IntArray): Double { + val len1 = nums1.size + val len2 = nums2.size + var i = 0 + var j = 0 + var median1 = 0 + var median2 = 0 + + for (count in 0 until (len1 + len2) / 2 + 1) { + median2 = median1 + when { + i < len1 && j < len2 -> { + if (nums1[i] > nums2[j]) { + median1 = nums2[j] + j++ + } else { + median1 = nums1[i] + i++ + } + } + i < len1 -> { + median1 = nums1[i] + i++ + } + else -> { + median1 = nums2[j] + j++ + } + } + } + + return if ((len1 + len2) % 2 == 1) { + median1.toDouble() + } else { + (median1 + median2) / 2.0 + } + } +} +``` + +```kotlin +class Solution { + func findMedianSortedArrays(_ nums1: [Int], _ nums2: [Int]) -> Double { + let len1 = nums1.count, len2 = nums2.count + var i = 0, j = 0 + var median1 = 0, median2 = 0 + + for _ in 0..<(len1 + len2) / 2 + 1 { + median2 = median1 + if i < len1 && j < len2 { + if nums1[i] > nums2[j] { + median1 = nums2[j] + j += 1 + } else { + median1 = nums1[i] + i += 1 + } + } else if i < len1 { + median1 = nums1[i] + i += 1 + } else { + median1 = nums2[j] + j += 1 + } + } + + if (len1 + len2) % 2 == 1 { + return Double(median1) + } else { + return (Double(median1) + Double(median2)) / 2.0 + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(1)$ + +> Where $n$ is the length of $nums1$ and $m$ is the length of $nums2$. + +--- + +## 3. Binary Search + +::tabs-start + +```python +class Solution: + def get_kth(self, a: List[int], m: int, b: List[int], n: int, k: int, a_start: int = 0, b_start: int = 0) -> int: + if m > n: + return self.get_kth(b, n, a, m, k, b_start, a_start) + if m == 0: + return b[b_start + k - 1] + if k == 1: + return min(a[a_start], b[b_start]) + + i = min(m, k // 2) + j = min(n, k // 2) + + if a[a_start + i - 1] > b[b_start + j - 1]: + return self.get_kth(a, m, b, n - j, k - j, a_start, b_start + j) + else: + return self.get_kth(a, m - i, b, n, k - i, a_start + i, b_start) + + def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float: + left = (len(nums1) + len(nums2) + 1) // 2 + right = (len(nums1) + len(nums2) + 2) // 2 + return (self.get_kth(nums1, len(nums1), nums2, len(nums2), left) + + self.get_kth(nums1, len(nums1), nums2, len(nums2), right)) / 2.0 +``` + +```java +public class Solution { + public double findMedianSortedArrays(int[] nums1, int[] nums2) { + int left = (nums1.length + nums2.length + 1) / 2; + int right = (nums1.length + nums2.length + 2) / 2; + return (getKth(nums1, nums1.length, nums2, nums2.length, left, 0, 0) + + getKth(nums1, nums1.length, nums2, nums2.length, right, 0, 0)) / 2.0; + } + + public int getKth(int[] a, int m, int[] b, int n, int k, int aStart, int bStart) { + if (m > n) { + return getKth(b, n, a, m, k, bStart, aStart); + } + if (m == 0) { + return b[bStart + k - 1]; + } + if (k == 1) { + return Math.min(a[aStart], b[bStart]); + } + + int i = Math.min(m, k / 2); + int j = Math.min(n, k / 2); + + if (a[aStart + i - 1] > b[bStart + j - 1]) { + return getKth(a, m, b, n - j, k - j, aStart, bStart + j); + } else { + return getKth(a, m - i, b, n, k - i, aStart + i, bStart); + } + } +} +``` + +```cpp +class Solution { +public: + double findMedianSortedArrays(vector& nums1, vector& nums2) { + int left = (nums1.size() + nums2.size() + 1) / 2; + int right = (nums1.size() + nums2.size() + 2) / 2; + return (getKth(nums1, nums1.size(), nums2, nums2.size(), left, 0, 0) + + getKth(nums1, nums1.size(), nums2, nums2.size(), right, 0, 0)) / 2.0; + } + + int getKth(vector& a, int m, vector& b, int n, int k, int aStart, int bStart) { + if (m > n) { + return getKth(b, n, a, m, k, bStart, aStart); + } + if (m == 0) { + return b[bStart + k - 1]; + } + if (k == 1) { + return min(a[aStart], b[bStart]); + } + + int i = min(m, k / 2); + int j = min(n, k / 2); + + if (a[aStart + i - 1] > b[bStart + j - 1]) { + return getKth(a, m, b, n - j, k - j, aStart, bStart + j); + } else { + return getKth(a, m - i, b, n, k - i, aStart + i, bStart); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number} + */ + findMedianSortedArrays(nums1, nums2) { + const m = nums1.length; + const n = nums2.length; + const left = Math.floor((m + n + 1) / 2); + const right = Math.floor((m + n + 2) / 2); + + return (this.getKth(nums1, m, nums2, n, left) + + this.getKth(nums1, m, nums2, n, right)) / 2.0; + } + + /** + * @param {number[]} a + * @param {number} m + * @param {number[]} b + * @param {number} n + * @param {number} k + * @param {number} aStart + * @param {number} bStart + * @return {number} + */ + getKth(a, m, b, n, k, aStart = 0, bStart = 0) { + if (m > n) { + return this.getKth(b, n, a, m, k, bStart, aStart); + } + if (m === 0) { + return b[bStart + k - 1]; + } + if (k === 1) { + return Math.min(a[aStart], b[bStart]); + } + + const i = Math.min(m, Math.floor(k / 2)); + const j = Math.min(n, Math.floor(k / 2)); + + if (a[aStart + i - 1] > b[bStart + j - 1]) { + return this.getKth(a, m, b, n - j, k - j, aStart, bStart + j); + } else { + return this.getKth(a, m - i, b, n, k - i, aStart + i, bStart); + } + } +} +``` + +```csharp +public class Solution { + public double FindMedianSortedArrays(int[] nums1, int[] nums2) { + int m = nums1.Length; + int n = nums2.Length; + int left = (m + n + 1) / 2; + int right = (m + n + 2) / 2; + + return (GetKth(nums1, m, nums2, n, left, 0, 0) + + GetKth(nums1, m, nums2, n, right, 0, 0)) / 2.0; + } + + public int GetKth(int[] a, int m, int[] b, int n, int k, int aStart, int bStart) { + if (m > n) { + return GetKth(b, n, a, m, k, bStart, aStart); + } + if (m == 0) { + return b[bStart + k - 1]; + } + if (k == 1) { + return Math.Min(a[aStart], b[bStart]); + } + + int i = Math.Min(m, k / 2); + int j = Math.Min(n, k / 2); + + if (a[aStart + i - 1] > b[bStart + j - 1]) { + return GetKth(a, m, b, n - j, k - j, aStart, bStart + j); + } + else { + return GetKth(a, m - i, b, n, k - i, aStart + i, bStart); + } + } +} +``` + +```go +func getKth(a []int, m int, b []int, n int, k int, aStart int, bStart int) int { + if m > n { + return getKth(b, n, a, m, k, bStart, aStart) + } + if m == 0 { + return b[bStart+k-1] + } + if k == 1 { + if a[aStart] < b[bStart] { + return a[aStart] + } + return b[bStart] + } + + i := min(m, k/2) + j := min(n, k/2) + + if a[aStart+i-1] > b[bStart+j-1] { + return getKth(a, m, b[bStart+j:], n-j, k-j, aStart, 0) + } + return getKth(a[aStart+i:], m-i, b, n, k-i, 0, bStart) +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +func findMedianSortedArrays(nums1 []int, nums2 []int) float64 { + left := (len(nums1) + len(nums2) + 1) / 2 + right := (len(nums1) + len(nums2) + 2) / 2 + return float64(getKth(nums1, len(nums1), nums2, len(nums2), left, 0, 0) + + getKth(nums1, len(nums1), nums2, len(nums2), right, 0, 0)) / 2.0 +} +``` + +```kotlin +class Solution { + private fun getKth(a: IntArray, m: Int, b: IntArray, n: Int, k: Int, aStart: Int = 0, bStart: Int = 0): Int { + if (m > n) { + return getKth(b, n, a, m, k, bStart, aStart) + } + if (m == 0) { + return b[bStart + k - 1] + } + if (k == 1) { + return minOf(a[aStart], b[bStart]) + } + + val i = minOf(m, k / 2) + val j = minOf(n, k / 2) + + return if (a[aStart + i - 1] > b[bStart + j - 1]) { + getKth(a, m, b, n - j, k - j, aStart, bStart + j) + } else { + getKth(a, m - i, b, n, k - i, aStart + i, bStart) + } + } + + fun findMedianSortedArrays(nums1: IntArray, nums2: IntArray): Double { + val left = (nums1.size + nums2.size + 1) / 2 + val right = (nums1.size + nums2.size + 2) / 2 + return (getKth(nums1, nums1.size, nums2, nums2.size, left) + + getKth(nums1, nums1.size, nums2, nums2.size, right)) / 2.0 + } +} +``` + +```swift +class Solution { + func getKth(_ a: [Int], _ m: Int, _ b: [Int], _ n: Int, _ k: Int, _ aStart: Int = 0, _ bStart: Int = 0) -> Int { + if m > n { + return getKth(b, n, a, m, k, bStart, aStart) + } + if m == 0 { + return b[bStart + k - 1] + } + if k == 1 { + return min(a[aStart], b[bStart]) + } + + let i = min(m, k / 2) + let j = min(n, k / 2) + + if a[aStart + i - 1] > b[bStart + j - 1] { + return getKth(a, m, b, n - j, k - j, aStart, bStart + j) + } else { + return getKth(a, m - i, b, n, k - i, aStart + i, bStart) + } + } + + func findMedianSortedArrays(_ nums1: [Int], _ nums2: [Int]) -> Double { + let left = (nums1.count + nums2.count + 1) / 2 + let right = (nums1.count + nums2.count + 2) / 2 + return (Double(getKth(nums1, nums1.count, nums2, nums2.count, left)) + + Double(getKth(nums1, nums1.count, nums2, nums2.count, right))) / 2.0 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log (m + n))$ +* Space complexity: $O(\log (m + n))$ for recursion stack. + +> Where $n$ is the length of $nums1$ and $m$ is the length of $nums2$. + +--- + +## 4. Binary Search (Optimal) + +::tabs-start + +```python +class Solution: + def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float: + A, B = nums1, nums2 + total = len(nums1) + len(nums2) + half = total // 2 + + if len(B) < len(A): + A, B = B, A + + l, r = 0, len(A) - 1 + while True: + i = (l + r) // 2 + j = half - i - 2 + + Aleft = A[i] if i >= 0 else float("-infinity") + Aright = A[i + 1] if (i + 1) < len(A) else float("infinity") + Bleft = B[j] if j >= 0 else float("-infinity") + Bright = B[j + 1] if (j + 1) < len(B) else float("infinity") + + if Aleft <= Bright and Bleft <= Aright: + if total % 2: + return min(Aright, Bright) + return (max(Aleft, Bleft) + min(Aright, Bright)) / 2 + elif Aleft > Bright: + r = i - 1 + else: + l = i + 1 +``` + +```java +public class Solution { + public double findMedianSortedArrays(int[] nums1, int[] nums2) { + int[] A = nums1; + int[] B = nums2; + int total = A.length + B.length; + int half = (total + 1) / 2; + + if (B.length < A.length) { + int[] temp = A; + A = B; + B = temp; + } + + int l = 0; + int r = A.length; + while (l <= r) { + int i = (l + r) / 2; + int j = half - i; + + int Aleft = i > 0 ? A[i - 1] : Integer.MIN_VALUE; + int Aright = i < A.length ? A[i] : Integer.MAX_VALUE; + int Bleft = j > 0 ? B[j - 1] : Integer.MIN_VALUE; + int Bright = j < B.length ? B[j] : Integer.MAX_VALUE; + + if (Aleft <= Bright && Bleft <= Aright) { + if (total % 2 != 0) { + return Math.max(Aleft, Bleft); + } + return (Math.max(Aleft, Bleft) + Math.min(Aright, Bright)) / 2.0; + } else if (Aleft > Bright) { + r = i - 1; + } else { + l = i + 1; + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + double findMedianSortedArrays(vector& nums1, vector& nums2) { + vector& A = nums1; + vector& B = nums2; + int total = A.size() + B.size(); + int half = (total + 1) / 2; + + if (B.size() < A.size()) { + swap(A, B); + } + + int l = 0; + int r = A.size(); + while (l <= r) { + int i = (l + r) / 2; + int j = half - i; + + int Aleft = i > 0 ? A[i - 1] : INT_MIN; + int Aright = i < A.size() ? A[i] : INT_MAX; + int Bleft = j > 0 ? B[j - 1] : INT_MIN; + int Bright = j < B.size() ? B[j] : INT_MAX; + + if (Aleft <= Bright && Bleft <= Aright) { + if (total % 2 != 0) { + return max(Aleft, Bleft); + } + return (max(Aleft, Bleft) + min(Aright, Bright)) / 2.0; + } else if (Aleft > Bright) { + r = i - 1; + } else { + l = i + 1; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number} + */ + findMedianSortedArrays(nums1, nums2) { + let A = nums1; + let B = nums2; + const total = A.length + B.length; + const half = Math.floor((total + 1) / 2); + + if (B.length < A.length) { + [A, B] = [B, A]; + } + + let l = 0; + let r = A.length; + while (l <= r) { + const i = Math.floor((l + r) / 2); + const j = half - i; + + const Aleft = i > 0 ? A[i - 1] : Number.MIN_SAFE_INTEGER; + const Aright = i < A.length ? A[i] : Number.MAX_SAFE_INTEGER; + const Bleft = j > 0 ? B[j - 1] : Number.MIN_SAFE_INTEGER; + const Bright = j < B.length ? B[j] : Number.MAX_SAFE_INTEGER; + + if (Aleft <= Bright && Bleft <= Aright) { + if (total % 2 !== 0) { + return Math.max(Aleft, Bleft); + } + return (Math.max(Aleft, Bleft) + Math.min(Aright, Bright)) / 2; + } else if (Aleft > Bright) { + r = i - 1; + } else { + l = i + 1; + } + } + return -1; + } +} +``` + +```csharp +public class Solution { + public double FindMedianSortedArrays(int[] nums1, int[] nums2) { + int[] A = nums1; + int[] B = nums2; + int total = A.Length + B.Length; + int half = (total + 1) / 2; + + if (B.Length < A.Length) { + int[] temp = A; + A = B; + B = temp; + } + + int l = 0; + int r = A.Length; + while (l <= r) { + int i = (l + r) / 2; + int j = half - i; + + int Aleft = i > 0 ? A[i - 1] : int.MinValue; + int Aright = i < A.Length ? A[i] : int.MaxValue; + int Bleft = j > 0 ? B[j - 1] : int.MinValue; + int Bright = j < B.Length ? B[j] : int.MaxValue; + + if (Aleft <= Bright && Bleft <= Aright) { + if (total % 2 != 0) { + return Math.Max(Aleft, Bleft); + } + return (Math.Max(Aleft, Bleft) + Math.Min(Aright, Bright)) / 2.0; + } + else if (Aleft > Bright) { + r = i - 1; + } + else { + l = i + 1; + } + } + return -1; + } +} +``` + +```go +func findMedianSortedArrays(nums1 []int, nums2 []int) float64 { + A, B := nums1, nums2 + total := len(A) + len(B) + half := (total + 1) / 2 + + if len(B) < len(A) { + A, B = B, A + } + + l, r := 0, len(A) + for l <= r { + i := (l + r) / 2 + j := half - i + + Aleft := math.MinInt64 + if i > 0 { + Aleft = A[i-1] + } + Aright := math.MaxInt64 + if i < len(A) { + Aright = A[i] + } + Bleft := math.MinInt64 + if j > 0 { + Bleft = B[j-1] + } + Bright := math.MaxInt64 + if j < len(B) { + Bright = B[j] + } + + if Aleft <= Bright && Bleft <= Aright { + if total%2 != 0 { + return float64(max(Aleft, Bleft)) + } + return (float64(max(Aleft, Bleft)) + float64(min(Aright, Bright))) / 2.0 + } else if Aleft > Bright { + r = i - 1 + } else { + l = i + 1 + } + } + return -1 +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun findMedianSortedArrays(nums1: IntArray, nums2: IntArray): Double { + var A = nums1 + var B = nums2 + val total = A.size + B.size + val half = (total + 1) / 2 + + if (B.size < A.size) { + A = nums2 + B = nums1 + } + + var l = 0 + var r = A.size + while (l <= r) { + val i = (l + r) / 2 + val j = half - i + + val Aleft = if (i > 0) A[i - 1] else Int.MIN_VALUE + val Aright = if (i < A.size) A[i] else Int.MAX_VALUE + val Bleft = if (j > 0) B[j - 1] else Int.MIN_VALUE + val Bright = if (j < B.size) B[j] else Int.MAX_VALUE + + if (Aleft <= Bright && Bleft <= Aright) { + return if (total % 2 != 0) { + Math.max(Aleft.toDouble(), Bleft.toDouble()) + } else { + (Math.max(Aleft.toDouble(), Bleft.toDouble()) + + Math.min(Aright.toDouble(), Bright.toDouble())) / 2.0 + } + } else if (Aleft > Bright) { + r = i - 1 + } else { + l = i + 1 + } + } + return -1.0 + } +} +``` + +```swift +class Solution { + func findMedianSortedArrays(_ nums1: [Int], _ nums2: [Int]) -> Double { + var A = nums1, B = nums2 + if A.count > B.count { + swap(&A, &B) + } + + let total = A.count + B.count + let half = total / 2 + var l = 0 + var r = A.count + + while true { + let i = (l + r) / 2 + let j = half - i + + let Aleft = i > 0 ? Double(A[i - 1]) : -Double.infinity + let Aright = i < A.count ? Double(A[i]) : Double.infinity + let Bleft = j > 0 ? Double(B[j - 1]) : -Double.infinity + let Bright = j < B.count ? Double(B[j]) : Double.infinity + + if Aleft <= Bright && Bleft <= Aright { + if total % 2 == 1 { + return min(Aright, Bright) + } else { + return (max(Aleft, Bleft) + min(Aright, Bright)) / 2.0 + } + } else if Aleft > Bright { + r = i - 1 + } else { + l = i + 1 + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log (min(n, m)))$ +* Space complexity: $O(1)$ + +> Where $n$ is the length of $nums1$ and $m$ is the length of $nums2$. \ No newline at end of file diff --git a/articles/meeting-rooms-iii.md b/articles/meeting-rooms-iii.md new file mode 100644 index 000000000..d8d33bcdb --- /dev/null +++ b/articles/meeting-rooms-iii.md @@ -0,0 +1,466 @@ +## 1. Sorting + Brute Force + +::tabs-start + +```python +class Solution: + def mostBooked(self, n: int, meetings: List[List[int]]) -> int: + meetings.sort() + rooms = [0] * n # end time of meetings in rooms + meeting_count = [0] * n + + for s, e in meetings: + min_room = 0 + found = False + for i in range(n): + if rooms[i] <= s: + found = True + meeting_count[i] += 1 + rooms[i] = e + break + + if rooms[min_room] > rooms[i]: + min_room = i + if found: + continue + meeting_count[min_room] += 1 + rooms[min_room] += e - s + + return meeting_count.index(max(meeting_count)) +``` + +```java +public class Solution { + public int mostBooked(int n, int[][] meetings) { + Arrays.sort(meetings, (a, b) -> Integer.compare(a[0], b[0])); + long[] rooms = new long[n]; // end times of meetings in rooms + int[] meetingCount = new int[n]; + + for (int[] meeting : meetings) { + int start = meeting[0], end = meeting[1]; + int minRoom = 0; + boolean found = false; + + for (int i = 0; i < n; i++) { + if (rooms[i] <= start) { + meetingCount[i]++; + rooms[i] = end; + found = true; + break; + } + if (rooms[minRoom] > rooms[i]) { + minRoom = i; + } + } + if (found) continue; + + meetingCount[minRoom]++; + rooms[minRoom] += end - start; + } + + int maxIndex = 0; + for (int i = 1; i < n; i++) { + if (meetingCount[i] > meetingCount[maxIndex]) { + maxIndex = i; + } + } + return maxIndex; + } +} +``` + +```cpp +class Solution { +public: + int mostBooked(int n, vector>& meetings) { + sort(meetings.begin(), meetings.end()); + vector rooms(n, 0); // end times of meetings in rooms + vector meetingCount(n, 0); + + for (const auto& meeting : meetings) { + int start = meeting[0], end = meeting[1]; + int minRoom = 0; + bool found = false; + + for (int i = 0; i < n; i++) { + if (rooms[i] <= start) { + meetingCount[i]++; + rooms[i] = end; + found = true; + break; + } + if (rooms[minRoom] > rooms[i]) { + minRoom = i; + } + } + + if (found) continue; + meetingCount[minRoom]++; + rooms[minRoom] += end - start; + } + + int maxIndex = 0; + for (int i = 1; i < n; i++) { + if (meetingCount[i] > meetingCount[maxIndex]) { + maxIndex = i; + } + } + return maxIndex; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} meetings + * @return {number} + */ + mostBooked(n, meetings) { + meetings.sort((a, b) => a[0] - b[0]); + const rooms = new Array(n).fill(0); // end times of meetings in rooms + const meetingCount = new Array(n).fill(0); + + for (const [start, end] of meetings) { + let minRoom = 0; + let found = false; + + for (let i = 0; i < n; i++) { + if (rooms[i] <= start) { + meetingCount[i]++; + rooms[i] = end; + found = true; + break; + } + if (rooms[minRoom] > rooms[i]) { + minRoom = i; + } + } + + if (found) continue; + meetingCount[minRoom]++; + rooms[minRoom] += end - start; + } + + return meetingCount.indexOf(Math.max(...meetingCount)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m\log m + n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of rooms and $m$ is the number of meetings. + +--- + +## 2. Two Min-Heaps + +::tabs-start + +```python +class Solution: + def mostBooked(self, n: int, meetings: List[List[int]]) -> int: + meetings.sort() + available = [i for i in range(n)] # Min heap for available rooms + used = [] # Min heap for used rooms [(end_time, room_number)] + count = [0] * n # Count of meetings for each room + + for start, end in meetings: + while used and used[0][0] <= start: + _, room = heapq.heappop(used) + heapq.heappush(available, room) + + if not available: + end_time, room = heapq.heappop(used) + end = end_time + (end - start) + heapq.heappush(available, room) + + room = heapq.heappop(available) + heapq.heappush(used, (end, room)) + count[room] += 1 + + return count.index(max(count)) +``` + +```java +public class Solution { + public int mostBooked(int n, int[][] meetings) { + Arrays.sort(meetings, (a, b) -> Long.compare(a[0], b[0])); + PriorityQueue available = new PriorityQueue<>(); + PriorityQueue used = new PriorityQueue<>((a, b) -> + a[0] == b[0] ? Long.compare(a[1], b[1]) : Long.compare(a[0], b[0]) + ); + for (int i = 0; i < n; i++) { + available.offer(i); + } + int[] count = new int[n]; + + for (int[] meeting : meetings) { + long start = meeting[0]; + long end = meeting[1]; + while (!used.isEmpty() && used.peek()[0] <= start) { + int room = (int) used.poll()[1]; + available.offer(room); + } + if (available.isEmpty()) { + long[] current = used.poll(); + int room = (int) current[1]; + end = current[0] + (end - start); + available.offer(room); + } + + int room = available.poll(); + used.offer(new long[]{end, room}); + count[room]++; + } + + int maxRoom = 0; + for (int i = 1; i < n; i++) { + if (count[i] > count[maxRoom]) { + maxRoom = i; + } + } + return maxRoom; + } +} +``` + +```cpp +class Solution { +public: + int mostBooked(int n, vector>& meetings) { + sort(meetings.begin(), meetings.end(), [](const vector& a, const vector& b) { + return (long long)a[0] < (long long)b[0]; + }); + priority_queue, greater> available; + priority_queue, vector>, greater>> used; + for (int i = 0; i < n; i++) { + available.push(i); + } + vector count(n); + + for (const auto& meeting : meetings) { + long long start = meeting[0]; + long long end = meeting[1]; + while (!used.empty() && used.top().first <= start) { + int room = used.top().second; + used.pop(); + available.push(room); + } + if (available.empty()) { + auto current = used.top(); + used.pop(); + end = current.first + (end - start); + available.push(current.second); + } + + int room = available.top(); + available.pop(); + used.push({end, room}); + count[room]++; + } + + int maxRoom = 0; + for (int i = 1; i < n; i++) { + if (count[i] > count[maxRoom]) { + maxRoom = i; + } + } + return maxRoom; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} meetings + * @return {number} + */ + mostBooked(n, meetings) { + meetings.sort((a, b) => a[0] - b[0]); + const available = new MinPriorityQueue({ compare: (a, b) => a - b }); + const used = new MinPriorityQueue({ + compare: (a, b) => (a[0] === b[0] ? a[1] - b[1] : a[0] - b[0]), + }); + for (let i = 0; i < n; i++) { + available.enqueue(i); + } + const count = new Array(n).fill(0); + + for (const [start, end] of meetings) { + while (!used.isEmpty() && used.front()[0] <= start) { + const room = used.dequeue()[1]; + available.enqueue(room); + } + + let room; + let newEnd = end; + if (available.isEmpty()) { + const [endTime, usedRoom] = used.dequeue(); + newEnd = endTime + (end - start); + available.enqueue(usedRoom); + } + room = available.dequeue(); + used.enqueue([newEnd, room]); + count[room]++; + } + + return count.indexOf(Math.max(...count)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m\log m + m \log n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of rooms and $m$ is the number of meetings. + +--- + +## 3. One Min-Heap + +::tabs-start + +```python +class Solution: + def mostBooked(self, n: int, meetings: List[List[int]]) -> int: + meetings.sort() + available = [] + count = [0] * n + + for i in range(n): + heapq.heappush(available, (0, i)) + + for start, end in meetings: + while available and available[0][0] < start: + end_time, room = heapq.heappop(available) + heapq.heappush(available, (start, room)) + + end_time, room = heapq.heappop(available) + heapq.heappush(available, (end_time + (end - start), room)) + count[room] += 1 + + return count.index(max(count)) +``` + +```java +public class Solution { + public int mostBooked(int n, int[][] meetings) { + Arrays.sort(meetings, (a, b) -> Integer.compare(a[0], b[0])); + PriorityQueue available = new PriorityQueue<>((a, b) -> + a[0] == b[0] ? Long.compare(a[1], b[1]) : Long.compare(a[0], b[0]) + ); + for (int i = 0; i < n; i++) { + available.offer(new long[]{0, i}); + } + int[] count = new int[n]; + + for (int[] meeting : meetings) { + int start = meeting[0], end = meeting[1]; + while (!available.isEmpty() && available.peek()[0] < start) { + long[] earliest = available.poll(); + available.offer(new long[]{start, earliest[1]}); + } + + long[] room = available.poll(); + long endTime = room[0] + (end - start); + available.offer(new long[]{endTime, room[1]}); + count[(int) room[1]]++; + } + + int maxRoom = 0; + for (int i = 1; i < n; i++) { + if (count[i] > count[maxRoom]) { + maxRoom = i; + } + } + return maxRoom; + } +} +``` + +```cpp +class Solution { +public: + int mostBooked(int n, vector>& meetings) { + sort(meetings.begin(), meetings.end()); + priority_queue, vector>, greater>> available; + for (int i = 0; i < n; i++) { + available.push({0, i}); + } + vector count(n); + + for (const auto& meeting : meetings) { + int start = meeting[0], end = meeting[1]; + while (!available.empty() && available.top().first < start) { + auto [end_time, room] = available.top(); + available.pop(); + available.push({start, room}); + } + + auto [end_time, room] = available.top(); + available.pop(); + available.push({end_time + (end - start), room}); + count[room]++; + } + + return max_element(count.begin(), count.end()) - count.begin(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} meetings + * @return {number} + */ + mostBooked(n, meetings) { + meetings.sort((a, b) => a[0] - b[0]); + const available = new MinPriorityQueue({ + compare: (a, b) => (a[0] === b[0] ? a[1] - b[1] : a[0] - b[0]) + }); + for (let i = 0; i < n; i++) { + available.enqueue([0, i]); + } + const count = new Array(n).fill(0); + + for (const [start, end] of meetings) { + while (!available.isEmpty() && available.front()[0] < start) { + const [endTime, room] = available.dequeue(); + available.enqueue([start, room]); + } + + const [endTime, room] = available.dequeue(); + available.enqueue([endTime + (end - start), room]); + count[room]++; + } + + return count.indexOf(Math.max(...count)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(m \log m + m \log n)$ time in average case. + * $O(m \log m + m * n)$ time in worst case. +* Space complexity: $O(n)$ + +> Where $n$ is the number of rooms and $m$ is the number of meetings. \ No newline at end of file diff --git a/articles/meeting-schedule-ii.md b/articles/meeting-schedule-ii.md new file mode 100644 index 000000000..afd50a48e --- /dev/null +++ b/articles/meeting-schedule-ii.md @@ -0,0 +1,1071 @@ +## 1. Min Heap + +::tabs-start + +```python +""" +Definition of Interval: +class Interval(object): + def __init__(self, start, end): + self.start = start + self.end = end +""" + +class Solution: + def minMeetingRooms(self, intervals: List[Interval]) -> int: + intervals.sort(key=lambda x: x.start) + min_heap = [] + + for interval in intervals: + if min_heap and min_heap[0] <= interval.start: + heapq.heappop(min_heap) + heapq.heappush(min_heap, interval.end) + + return len(min_heap) +``` + +```java +/** + * Definition of Interval: + * public class Interval { + * public int start, end; + * public Interval(int start, int end) { + * this.start = start; + * this.end = end; + * } + * } + */ + +public class Solution { + public int minMeetingRooms(List intervals) { + intervals.sort((a, b) -> a.start - b.start); + PriorityQueue minHeap = new PriorityQueue<>(); + for (Interval interval : intervals) { + if (!minHeap.isEmpty() && minHeap.peek() <= interval.start) { + minHeap.poll(); + } + minHeap.offer(interval.end); + } + return minHeap.size(); + } +} +``` + +```cpp +/** + * Definition of Interval: + * class Interval { + * public: + * int start, end; + * Interval(int start, int end) { + * this->start = start; + * this->end = end; + * } + * } + */ + +class Solution { +public: + int minMeetingRooms(vector& intervals) { + sort(intervals.begin(), intervals.end(), [](auto& a, auto& b) { + return a.start < b.start; + }); + priority_queue, greater> minHeap; + for (const auto& interval : intervals) { + if (!minHeap.empty() && minHeap.top() <= interval.start) { + minHeap.pop(); + } + minHeap.push(interval.end); + } + return minHeap.size(); + } +}; +``` + +```javascript +/** + * Definition of Interval: + * class Interval { + * constructor(start, end) { + * this.start = start; + * this.end = end; + * } + * } + */ + +class Solution { + /** + * @param {Interval[]} intervals + * @returns {number} + */ + minMeetingRooms(intervals) { + intervals.sort((a, b) => a.start - b.start); + const minHeap = new MinPriorityQueue(); + for (const interval of intervals) { + if (!minHeap.isEmpty() && minHeap.front() <= interval.start) { + minHeap.pop(); + } + minHeap.push(interval.end); + } + return minHeap.size(); + } +} +``` + +```csharp +/** + * Definition of Interval: + * public class Interval { + * public int start, end; + * public Interval(int start, int end) { + * this.start = start; + * this.end = end; + * } + * } + */ + +public class Solution { + public int MinMeetingRooms(List intervals) { + intervals.Sort((a, b) => a.start.CompareTo(b.start)); + var minHeap = new PriorityQueue(); + + foreach (var interval in intervals) { + if (minHeap.Count > 0 && minHeap.Peek() <= interval.start) { + minHeap.Dequeue(); + } + minHeap.Enqueue(interval.end, interval.end); + } + + return minHeap.Count; + } +} +``` + +```go +/** + * Definition of Interval: + * type Interval struct { + * start int + * end int + * } + */ + +func minMeetingRooms(intervals []Interval) int { + sort.Slice(intervals, func(i, j int) bool { + return intervals[i].start < intervals[j].start + }) + + pq := priorityqueue.NewWith(utils.IntComparator) + + for _, interval := range intervals { + if pq.Size() > 0 { + if top, _ := pq.Peek(); top.(int) <= interval.start { + pq.Dequeue() + } + } + pq.Enqueue(interval.end) + } + + return pq.Size() +} +``` + +```kotlin +/** + * Definition of Interval: + * class Interval(var start: Int, var end: Int) {} + */ + +class Solution { + fun minMeetingRooms(intervals: List): Int { + intervals.sortBy { it.start } + + val minHeap = PriorityQueue() + for (interval in intervals) { + if (minHeap.isNotEmpty() && minHeap.peek() <= interval.start) { + minHeap.poll() + } + minHeap.add(interval.end) + } + + return minHeap.size + } +} +``` + +```swift +/** + * Definition of Interval: + * class Interval { + * var start: Int + * var end: Int + * init(start: Int, end: Int) { + * self.start = start + * self.end = end + * } + * } + */ + +class Solution { + func minMeetingRooms(_ intervals: [Interval]) -> Int { + let sortedIntervals = intervals.sorted { $0.start < $1.start } + var minHeap = Heap() + for interval in sortedIntervals { + if !minHeap.isEmpty, let earliest = minHeap.min!, earliest <= interval.start { + minHeap.removeMin() + } + minHeap.insert(interval.end) + } + return minHeap.count + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Sweep Line Algorithm + +::tabs-start + +```python +""" +Definition of Interval: +class Interval(object): + def __init__(self, start, end): + self.start = start + self.end = end +""" + +class Solution: + def minMeetingRooms(self, intervals: List[Interval]) -> int: + mp = defaultdict(int) + for i in intervals: + mp[i.start] += 1 + mp[i.end] -= 1 + prev = 0 + res = 0 + for i in sorted(mp.keys()): + prev += mp[i] + res = max(res, prev) + return res +``` + +```java +/** + * Definition of Interval: + * public class Interval { + * public int start, end; + * public Interval(int start, int end) { + * this.start = start; + * this.end = end; + * } + * } + */ + +public class Solution { + public int minMeetingRooms(List intervals) { + TreeMap mp = new TreeMap<>(); + for (Interval i : intervals) { + mp.put(i.start, mp.getOrDefault(i.start, 0) + 1); + mp.put(i.end, mp.getOrDefault(i.end, 0) - 1); + } + int prev = 0; + int res = 0; + for (int key : mp.keySet()) { + prev += mp.get(key); + res = Math.max(res, prev); + } + return res; + } +} +``` + +```cpp +/** + * Definition of Interval: + * class Interval { + * public: + * int start, end; + * Interval(int start, int end) { + * this->start = start; + * this->end = end; + * } + * } + */ + +class Solution { +public: + int minMeetingRooms(vector& intervals) { + map mp; + for (auto& i : intervals) { + mp[i.start]++; + mp[i.end]--; + } + int prev = 0, res = 0; + for (auto& [key, value] : mp) { + prev += value; + res = max(res, prev); + } + return res; + } +}; +``` + +```javascript +/** + * Definition of Interval: + * class Interval { + * constructor(start, end) { + * this.start = start; + * this.end = end; + * } + * } + */ + +class Solution { + /** + * @param {Interval[]} intervals + * @returns {number} + */ + minMeetingRooms(intervals) { + const mp = new Map(); + for (const i of intervals) { + mp.set(i.start, (mp.get(i.start) || 0) + 1); + mp.set(i.end, (mp.get(i.end) || 0) - 1); + } + const sortedKeys = Array.from(mp.keys()).sort((a, b) => a - b); + let prev = 0, res = 0; + for (const key of sortedKeys) { + prev += mp.get(key); + res = Math.max(res, prev); + } + return res; + } +} +``` + +```csharp +/** + * Definition of Interval: + * public class Interval { + * public int start, end; + * public Interval(int start, int end) { + * this.start = start; + * this.end = end; + * } + * } + */ + +public class Solution { + public int MinMeetingRooms(List intervals) { + var mp = new SortedDictionary(); + foreach (var i in intervals) { + if (!mp.ContainsKey(i.start)) mp[i.start] = 0; + if (!mp.ContainsKey(i.end)) mp[i.end] = 0; + mp[i.start]++; + mp[i.end]--; + } + int prev = 0, res = 0; + foreach (var kvp in mp) { + prev += kvp.Value; + res = Math.Max(res, prev); + } + return res; + } +} +``` + +```go +/** + * Definition of Interval: + * type Interval struct { + * start int + * end int + * } + */ + +func minMeetingRooms(intervals []Interval) int { + mp := make(map[int]int) + for _, i := range intervals { + mp[i.start]++ + mp[i.end]-- + } + + keys := make([]int, 0, len(mp)) + for k := range mp { + keys = append(keys, k) + } + sort.Ints(keys) + + prev := 0 + res := 0 + for _, k := range keys { + prev += mp[k] + if prev > res { + res = prev + } + } + return res +} +``` + +```kotlin +/** + * Definition of Interval: + * class Interval(var start: Int, var end: Int) {} + */ + +class Solution { + fun minMeetingRooms(intervals: List): Int { + val mp = HashMap() + for (i in intervals) { + mp[i.start] = mp.getOrDefault(i.start, 0) + 1 + mp[i.end] = mp.getOrDefault(i.end, 0) - 1 + } + + val keys = mp.keys.sorted() + var prev = 0 + var res = 0 + for (k in keys) { + prev += mp[k]!! + res = maxOf(res, prev) + } + return res + } +} +``` + +```swift +/** + * Definition of Interval: + * class Interval { + * var start: Int + * var end: Int + * init(start: Int, end: Int) { + * self.start = start + * self.end = end + * } + * } + */ + +class Solution { + func minMeetingRooms(_ intervals: [Interval]) -> Int { + var mp = [Int: Int]() + for interval in intervals { + mp[interval.start, default: 0] += 1 + mp[interval.end, default: 0] -= 1 + } + var prev = 0 + var res = 0 + for key in mp.keys.sorted() { + prev += mp[key]! + res = max(res, prev) + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Two Pointers + +::tabs-start + +```python +""" +Definition of Interval: +class Interval(object): + def __init__(self, start, end): + self.start = start + self.end = end +""" + +class Solution: + def minMeetingRooms(self, intervals: List[Interval]) -> int: + start = sorted([i.start for i in intervals]) + end = sorted([i.end for i in intervals]) + + res = count = 0 + s = e = 0 + while s < len(intervals): + if start[s] < end[e]: + s += 1 + count += 1 + else: + e += 1 + count -= 1 + res = max(res, count) + return res +``` + +```java +/** + * Definition of Interval: + * public class Interval { + * public int start, end; + * public Interval(int start, int end) { + * this.start = start; + * this.end = end; + * } + * } + */ + +public class Solution { + public int minMeetingRooms(List intervals) { + int n = intervals.size(); + int[] start = new int[n]; + int[] end = new int[n]; + + for (int i = 0; i < n; i++) { + start[i] = intervals.get(i).start; + end[i] = intervals.get(i).end; + } + + Arrays.sort(start); + Arrays.sort(end); + + int res = 0, count = 0, s = 0, e = 0; + while (s < n) { + if (start[s] < end[e]) { + s++; + count++; + } else { + e++; + count--; + } + res = Math.max(res, count); + } + return res; + } +} +``` + +```cpp +/** + * Definition of Interval: + * class Interval { + * public: + * int start, end; + * Interval(int start, int end) { + * this->start = start; + * this->end = end; + * } + * } + */ + +class Solution { +public: + int minMeetingRooms(vector& intervals) { + vector start, end; + + for (const auto& i : intervals) { + start.push_back(i.start); + end.push_back(i.end); + } + + sort(start.begin(), start.end()); + sort(end.begin(), end.end()); + + int res = 0, count = 0, s = 0, e = 0; + while (s < intervals.size()) { + if (start[s] < end[e]) { + s++; + count++; + } else { + e++; + count--; + } + res = max(res, count); + } + return res; + } +}; +``` + +```javascript +/** + * Definition of Interval: + * class Interval { + * constructor(start, end) { + * this.start = start; + * this.end = end; + * } + * } + */ + +class Solution { + /** + * @param {Interval[]} intervals + * @returns {number} + */ + minMeetingRooms(intervals) { + const start = intervals.map(i => i.start).sort((a, b) => a - b); + const end = intervals.map(i => i.end).sort((a, b) => a - b); + + let res = 0, count = 0, s = 0, e = 0; + while (s < intervals.length) { + if (start[s] < end[e]) { + s++; + count++; + } else { + e++; + count--; + } + res = Math.max(res, count); + } + return res; + } +} +``` + +```csharp +/** + * Definition of Interval: + * public class Interval { + * public int start, end; + * public Interval(int start, int end) { + * this.start = start; + * this.end = end; + * } + * } + */ + +public class Solution { + public int MinMeetingRooms(List intervals) { + int n = intervals.Count; + int[] start = new int[n]; + int[] end = new int[n]; + + for (int i = 0; i < n; i++) { + start[i] = intervals[i].start; + end[i] = intervals[i].end; + } + + Array.Sort(start); + Array.Sort(end); + + int res = 0, count = 0, s = 0, e = 0; + while (s < n) { + if (start[s] < end[e]) { + s++; + count++; + } else { + e++; + count--; + } + res = Math.Max(res, count); + } + return res; + } +} +``` + +```go +/** + * Definition of Interval: + * type Interval struct { + * start int + * end int + * } + */ + +func minMeetingRooms(intervals []Interval) int { + start := make([]int, len(intervals)) + end := make([]int, len(intervals)) + + for i, interval := range intervals { + start[i] = interval.start + end[i] = interval.end + } + + sort.Ints(start) + sort.Ints(end) + + res, count := 0, 0 + s, e := 0, 0 + + for s < len(intervals) { + if start[s] < end[e] { + s++ + count++ + } else { + e++ + count-- + } + if count > res { + res = count + } + } + + return res +} +``` + +```kotlin +/** + * Definition of Interval: + * class Interval(var start: Int, var end: Int) {} + */ + +class Solution { + fun minMeetingRooms(intervals: List): Int { + val start = intervals.map { it.start }.sorted() + val end = intervals.map { it.end }.sorted() + + var res = 0 + var count = 0 + var s = 0 + var e = 0 + + while (s < intervals.size) { + if (start[s] < end[e]) { + s++ + count++ + } else { + e++ + count-- + } + res = maxOf(res, count) + } + + return res + } +} +``` + +```swift +/** + * Definition of Interval: + * class Interval { + * var start: Int + * var end: Int + * init(start: Int, end: Int) { + * self.start = start + * self.end = end + * } + * } + */ + +class Solution { + func minMeetingRooms(_ intervals: [Interval]) -> Int { + let starts = intervals.map { $0.start }.sorted() + let ends = intervals.map { $0.end }.sorted() + + var res = 0, count = 0, s = 0, e = 0 + while s < intervals.count { + if starts[s] < ends[e] { + count += 1 + s += 1 + } else { + count -= 1 + e += 1 + } + res = max(res, count) + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Greedy + +::tabs-start + +```python +""" +Definition of Interval: +class Interval(object): + def __init__(self, start, end): + self.start = start + self.end = end +""" + +class Solution: + def minMeetingRooms(self, intervals: List[Interval]) -> int: + time = [] + for i in intervals: + time.append((i.start, 1)) + time.append((i.end, -1)) + + time.sort(key=lambda x: (x[0], x[1])) + + res = count = 0 + for t in time: + count += t[1] + res = max(res, count) + return res +``` + +```java +/** + * Definition of Interval: + * public class Interval { + * public int start, end; + * public Interval(int start, int end) { + * this.start = start; + * this.end = end; + * } + * } + */ + +public class Solution { + public int minMeetingRooms(List intervals) { + List time = new ArrayList<>(); + for (Interval i : intervals) { + time.add(new int[] { i.start, 1 }); + time.add(new int[] { i.end, -1 }); + } + + time.sort((a, b) -> a[0] == b[0] ? a[1] - b[1] : a[0] - b[0]); + + int res = 0, count = 0; + for (int[] t : time) { + count += t[1]; + res = Math.max(res, count); + } + return res; + } +} +``` + +```cpp +/** + * Definition of Interval: + * class Interval { + * public: + * int start, end; + * Interval(int start, int end) { + * this->start = start; + * this->end = end; + * } + * } + */ + +class Solution { +public: + int minMeetingRooms(vector& intervals) { + vector> time; + for (const auto& i : intervals) { + time.push_back({i.start, 1}); + time.push_back({i.end, -1}); + } + + sort(time.begin(), time.end(), [](auto& a, auto& b) { + return a.first == b.first ? a.second < b.second : a.first < b.first; + }); + + int res = 0, count = 0; + for (const auto& t : time) { + count += t.second; + res = max(res, count); + } + return res; + } +}; +``` + +```javascript +/** + * Definition of Interval: + * class Interval { + * constructor(start, end) { + * this.start = start; + * this.end = end; + * } + * } + */ + +class Solution { + /** + * @param {Interval[]} intervals + * @returns {number} + */ + minMeetingRooms(intervals) { + const time = []; + for (const i of intervals) { + time.push([i.start, 1]); + time.push([i.end, -1]); + } + + time.sort((a, b) => a[0] === b[0] ? a[1] - b[1] : a[0] - b[0]); + + let res = 0, count = 0; + for (const t of time) { + count += t[1]; + res = Math.max(res, count); + } + return res; + } +} +``` + +```csharp +/** + * Definition of Interval: + * public class Interval { + * public int start, end; + * public Interval(int start, int end) { + * this.start = start; + * this.end = end; + * } + * } + */ + +public class Solution { + public int MinMeetingRooms(List intervals) { + List time = new List(); + foreach (var i in intervals) { + time.Add(new int[] { i.start, 1 }); + time.Add(new int[] { i.end, -1 }); + } + + time.Sort((a, b) => a[0] == b[0] ? + a[1].CompareTo(b[1]) : a[0].CompareTo(b[0] + )); + + int res = 0, count = 0; + foreach (var t in time) { + count += t[1]; + res = Math.Max(res, count); + } + return res; + } +} +``` + +```go +/** + * Definition of Interval: + * type Interval struct { + * start int + * end int + * } + */ + +func minMeetingRooms(intervals []Interval) int { + var time [][]int + for _, i := range intervals { + time = append(time, []int{i.start, 1}) + time = append(time, []int{i.end, -1}) + } + + sort.Slice(time, func(i, j int) bool { + if time[i][0] == time[j][0] { + return time[i][1] < time[j][1] + } + return time[i][0] < time[j][0] + }) + + res, count := 0, 0 + for _, t := range time { + count += t[1] + if count > res { + res = count + } + } + return res +} +``` + +```kotlin +/** + * Definition of Interval: + * class Interval(var start: Int, var end: Int) {} + */ + +class Solution { + fun minMeetingRooms(intervals: Array): Int { + val time = mutableListOf>() + + for (i in intervals) { + time.add(Pair(i.start, 1)) + time.add(Pair(i.end, -1)) + } + + time.sortWith(compareBy> { it.first } + .thenBy { it.second }) + + var res = 0 + var count = 0 + + for (t in time) { + count += t.second + res = maxOf(res, count) + } + + return res + } +} +``` + +```swift +/** + * Definition of Interval: + * class Interval { + * var start: Int + * var end: Int + * init(start: Int, end: Int) { + * self.start = start + * self.end = end + * } + * } + */ + +class Solution { + func minMeetingRooms(_ intervals: [Interval]) -> Int { + var times = [(Int, Int)]() + for interval in intervals { + times.append((interval.start, 1)) + times.append((interval.end, -1)) + } + + times.sort { + if $0.0 != $1.0 { + return $0.0 < $1.0 + } else { + return $0.1 < $1.1 + } + } + + var count = 0 + var res = 0 + for t in times { + count += t.1 + res = max(res, count) + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/meeting-schedule.md b/articles/meeting-schedule.md new file mode 100644 index 000000000..226a6a70f --- /dev/null +++ b/articles/meeting-schedule.md @@ -0,0 +1,464 @@ +## 1. Brute Force + +::tabs-start + +```python +""" +Definition of Interval: +class Interval(object): + def __init__(self, start, end): + self.start = start + self.end = end +""" + +class Solution: + def canAttendMeetings(self, intervals: List[Interval]) -> bool: + n = len(intervals) + for i in range(n): + A = intervals[i] + for j in range(i + 1, n): + B = intervals[j] + if min(A.end, B.end) > max(A.start, B.start): + return False + return True +``` + +```java +/** + * Definition of Interval: + * public class Interval { + * public int start, end; + * public Interval(int start, int end) { + * this.start = start; + * this.end = end; + * } + * } + */ + +public class Solution { + public boolean canAttendMeetings(List intervals) { + int n = intervals.size(); + for (int i = 0; i < n; i++) { + Interval A = intervals.get(i); + for (int j = i + 1; j < n; j++) { + Interval B = intervals.get(j); + if (Math.min(A.end, B.end) > Math.max(A.start, B.start)) { + return false; + } + } + } + return true; + } +} +``` + +```cpp +/** + * Definition of Interval: + * class Interval { + * public: + * int start, end; + * Interval(int start, int end) { + * this->start = start; + * this->end = end; + * } + * } + */ + +class Solution { +public: + bool canAttendMeetings(vector& intervals) { + int n = intervals.size(); + for (int i = 0; i < n; i++) { + Interval& A =intervals[i]; + for (int j = i + 1; j < n; j++) { + Interval& B =intervals[j]; + if (min(A.end, B.end) > max(A.start, B.start)) { + return false; + } + } + } + return true; + } +}; +``` + +```javascript +/** + * Definition of Interval: + * class Interval { + * constructor(start, end) { + * this.start = start; + * this.end = end; + * } + * } + */ + +class Solution { + /** + * @param {Interval[]} intervals + * @returns {boolean} + */ + canAttendMeetings(intervals) { + const n = intervals.length; + for (let i = 0; i < n; i++) { + const A = intervals[i]; + for (let j = i + 1; j < n; j++) { + const B = intervals[j]; + if (Math.min(A.end, B.end) > Math.max(A.start, B.start)) { + return false; + } + } + } + return true; + } +} +``` + +```csharp +/** + * Definition of Interval: + * public class Interval { + * public int start, end; + * public Interval(int start, int end) { + * this.start = start; + * this.end = end; + * } + * } + */ + +public class Solution { + public bool CanAttendMeetings(List intervals) { + int n = intervals.Count; + for (int i = 0; i < n; i++) { + Interval A = intervals[i]; + for (int j = i + 1; j < n; j++) { + Interval B = intervals[j]; + if (Math.Min(A.end, B.end) > Math.Max(A.start, B.start)) { + return false; + } + } + } + return true; + } +} +``` + +```go +/** + * Definition of Interval: + * type Interval struct { + * start int + * end int + * } + */ + +func canAttendMeetings(intervals []Interval) bool { + n := len(intervals) + for i := 0; i < n; i++ { + A := intervals[i] + for j := i + 1; j < n; j++ { + B := intervals[j] + if min(A.end, B.end) > max(A.start, B.start) { + return false + } + } + } + return true +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +/** + * Definition of Interval: + * class Interval(var start: Int, var end: Int) {} + */ + +class Solution { + fun canAttendMeetings(intervals: List): Boolean { + val n = intervals.size + for (i in 0 until n) { + val A = intervals[i] + for (j in i + 1 until n) { + val B = intervals[j] + if (minOf(A.end, B.end) > maxOf(A.start, B.start)) { + return false + } + } + } + return true + } +} +``` + +```swift +/** + * Definition of Interval: + * class Interval { + * var start: Int + * var end: Int + * init(start: Int, end: Int) { + * self.start = start + * self.end = end + * } + * } + */ + +class Solution { + func canAttendMeetings(_ intervals: [Interval]) -> Bool { + let n = intervals.count + for i in 0.. max(A.start, B.start) { + return false + } + } + } + return true + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Sorting + +::tabs-start + +```python +""" +Definition of Interval: +class Interval(object): + def __init__(self, start, end): + self.start = start + self.end = end +""" + +class Solution: + def canAttendMeetings(self, intervals: List[Interval]) -> bool: + intervals.sort(key=lambda i: i.start) + + for i in range(1, len(intervals)): + i1 = intervals[i - 1] + i2 = intervals[i] + + if i1.end > i2.start: + return False + return True +``` + +```java +/** + * Definition of Interval: + * public class Interval { + * public int start, end; + * public Interval(int start, int end) { + * this.start = start; + * this.end = end; + * } + * } + */ + +public class Solution { + public boolean canAttendMeetings(List intervals) { + Collections.sort(intervals, Comparator.comparingInt(i -> i.start)); + + for (int i = 1; i < intervals.size(); i++) { + Interval i1 = intervals.get(i - 1); + Interval i2 = intervals.get(i); + + if (i1.end > i2.start) { + return false; + } + } + + return true; + } +} +``` + +```cpp +/** + * Definition of Interval: + * class Interval { + * public: + * int start, end; + * Interval(int start, int end) { + * this->start = start; + * this->end = end; + * } + * } + */ + +class Solution { +public: + bool canAttendMeetings(vector& intervals) { + sort(intervals.begin(), intervals.end(), [](auto& x, auto& y) { + return x.start < y.start; + }); + for (int i = 1; i < intervals.size(); ++i) { + if (intervals[i].start < intervals[i - 1].end) { + return false; + } + } + return true; + } +}; +``` + +```javascript +/** + * Definition of Interval: + * class Interval { + * constructor(start, end) { + * this.start = start; + * this.end = end; + * } + * } + */ + +class Solution { + /** + * @param {Interval[]} intervals + * @returns {boolean} + */ + canAttendMeetings(intervals) { + intervals.sort((a, b) => a.start - b.start); + for (let i = 1; i < intervals.length; i++) { + if (intervals[i].start < intervals[i - 1].end) { + return false; + } + } + return true; + } +} +``` + +```csharp +/** + * Definition of Interval: + * public class Interval { + * public int start, end; + * public Interval(int start, int end) { + * this.start = start; + * this.end = end; + * } + * } + */ + +public class Solution { + public bool CanAttendMeetings(List intervals) { + intervals.Sort((i1, i2) => i1.start.CompareTo(i2.start)); + + for (int i = 1; i < intervals.Count; i++) { + Interval i1 = intervals[i - 1]; + Interval i2 = intervals[i]; + + if (i1.end > i2.start) { + return false; + } + } + + return true; + } +} +``` + +```go +/** +* Definition of Interval: +* type Interval struct { +* start int +* end int +* } +*/ + +func canAttendMeetings(intervals []Interval) bool { + sort.Slice(intervals, func(i, j int) bool { + return intervals[i].start < intervals[j].start + }) + + for i := 1; i < len(intervals); i++ { + if intervals[i-1].end > intervals[i].start { + return false + } + } + return true +} +``` + +```kotlin +/** + * Definition of Interval: + * class Interval(var start: Int, var end: Int) {} + */ + +class Solution { + fun canAttendMeetings(intervals: List): Boolean { + intervals.sortedBy { it.start }.let { + for (i in 1 until it.size) { + if (it[i - 1].end > it[i].start) { + return false + } + } + } + return true + } +} +``` + +```swift +/** + * Definition of Interval: + * class Interval { + * var start: Int + * var end: Int + * init(start: Int, end: Int) { + * self.start = start + * self.end = end + * } + * } + */ + +class Solution { + func canAttendMeetings(_ intervals: [Interval]) -> Bool { + let sortedIntervals = intervals.sorted { $0.start < $1.start } + for i in 1.. sortedIntervals[i].start { + return false + } + } + return true + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file diff --git a/articles/merge-in-between-linked-lists.md b/articles/merge-in-between-linked-lists.md new file mode 100644 index 000000000..2d8a1774f --- /dev/null +++ b/articles/merge-in-between-linked-lists.md @@ -0,0 +1,460 @@ +## 1. Convert To Array + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def mergeInBetween(self, list1: ListNode, a: int, b: int, list2: ListNode) -> ListNode: + cur = list1 + arr = [] + while cur: + arr.append(cur) + cur = cur.next + arr[a - 1].next = list2 + + cur = list2 + while cur.next: + cur = cur.next + + cur.next = arr[b + 1] + return list1 +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode mergeInBetween(ListNode list1, int a, int b, ListNode list2) { + ListNode cur = list1; + List arr = new ArrayList<>(); + + while (cur != null) { + arr.add(cur); + cur = cur.next; + } + + arr.get(a - 1).next = list2; + cur = list2; + + while (cur.next != null) { + cur = cur.next; + } + + cur.next = arr.get(b + 1); + return list1; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* mergeInBetween(ListNode* list1, int a, int b, ListNode* list2) { + ListNode* cur = list1; + vector arr; + + while (cur) { + arr.push_back(cur); + cur = cur->next; + } + + arr[a - 1]->next = list2; + cur = list2; + + while (cur->next) { + cur = cur->next; + } + + cur->next = arr[b + 1]; + return list1; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} list1 + * @param {number} a + * @param {number} b + * @param {ListNode} list2 + * @return {ListNode} + */ + mergeInBetween(list1, a, b, list2) { + let cur = list1; + let arr = []; + + while (cur) { + arr.push(cur); + cur = cur.next; + } + + arr[a - 1].next = list2; + cur = list2; + + while (cur.next) { + cur = cur.next; + } + + cur.next = arr[b + 1]; + return list1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the first list and $m$ is the length of the second list. + +--- + +## 2. Two Pointers + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def mergeInBetween(self, list1: ListNode, a: int, b: int, list2: ListNode) -> ListNode: + curr = list1 + i = 0 + + while i < a - 1: + curr = curr.next + i += 1 + head = curr + + while i <= b: + curr = curr.next + i += 1 + + head.next = list2 + + while list2.next: + list2 = list2.next + list2.next = curr + + return list1 +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode mergeInBetween(ListNode list1, int a, int b, ListNode list2) { + ListNode curr = list1; + int i = 0; + + while (i < a - 1) { + curr = curr.next; + i++; + } + ListNode head = curr; + + while (i <= b) { + curr = curr.next; + i++; + } + + head.next = list2; + + while (list2.next != null) { + list2 = list2.next; + } + list2.next = curr; + + return list1; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* mergeInBetween(ListNode* list1, int a, int b, ListNode* list2) { + ListNode* curr = list1; + int i = 0; + + while (i < a - 1) { + curr = curr->next; + i++; + } + ListNode* head = curr; + + while (i <= b) { + curr = curr->next; + i++; + } + head->next = list2; + + while (list2->next) { + list2 = list2->next; + } + list2->next = curr; + + return list1; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} list1 + * @param {number} a + * @param {number} b + * @param {ListNode} list2 + * @return {ListNode} + */ + mergeInBetween(list1, a, b, list2) { + let curr = list1, i = 0; + + while (i < a - 1) { + curr = curr.next; + i++; + } + let head = curr; + + while (i <= b) { + curr = curr.next; + i++; + } + head.next = list2; + + while (list2.next) { + list2 = list2.next; + } + list2.next = curr; + + return list1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(1)$ extra space. + +> Where $n$ is the length of the first list and $m$ is the length of the second list. + +--- + +## 3. Recursion + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def mergeInBetween(self, list1: ListNode, a: int, b: int, list2: ListNode) -> ListNode: + if a == 1 : + nxt = list1.next + list1.next = list2 + while list2.next: + list2 = list2.next + self.mergeInBetween(nxt, 0, b - 1, list2) + return list1 + + if b == 0: + list2.next = list1.next + return list1 + + self.mergeInBetween(list1.next, a - 1, b - 1, list2) + return list1 +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode mergeInBetween(ListNode list1, int a, int b, ListNode list2) { + if (a == 1) { + ListNode nxt = list1.next; + list1.next = list2; + + while (list2.next != null) { + list2 = list2.next; + } + mergeInBetween(nxt, 0, b - 1, list2); + return list1; + } + + if (b == 0) { + list2.next = list1.next; + return list1; + } + + mergeInBetween(list1.next, a - 1, b - 1, list2); + return list1; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* mergeInBetween(ListNode* list1, int a, int b, ListNode* list2) { + if (a == 1) { + ListNode* nxt = list1->next; + list1->next = list2; + + while (list2->next) { + list2 = list2->next; + } + mergeInBetween(nxt, 0, b - 1, list2); + return list1; + } + + if (b == 0) { + list2->next = list1->next; + return list1; + } + + mergeInBetween(list1->next, a - 1, b - 1, list2); + return list1; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} list1 + * @param {number} a + * @param {number} b + * @param {ListNode} list2 + * @return {ListNode} + */ + mergeInBetween(list1, a, b, list2) { + if (a === 1) { + let nxt = list1.next; + list1.next = list2; + + while (list2.next) { + list2 = list2.next; + } + this.mergeInBetween(nxt, 0, b - 1, list2); + return list1; + } + + if (b === 0) { + list2.next = list1.next; + return list1; + } + + this.mergeInBetween(list1.next, a - 1, b - 1, list2); + return list1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n)$ for recursion stack. + +> Where $n$ is the length of the first list and $m$ is the length of the second list. \ No newline at end of file diff --git a/articles/merge-intervals.md b/articles/merge-intervals.md new file mode 100644 index 000000000..100d894b6 --- /dev/null +++ b/articles/merge-intervals.md @@ -0,0 +1,780 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def merge(self, intervals: List[List[int]]) -> List[List[int]]: + intervals.sort(key=lambda pair: pair[0]) + output = [intervals[0]] + + for start, end in intervals: + lastEnd = output[-1][1] + + if start <= lastEnd: + output[-1][1] = max(lastEnd, end) + else: + output.append([start, end]) + return output +``` + +```java +public class Solution { + public int[][] merge(int[][] intervals) { + Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0])); + List output = new ArrayList<>(); + output.add(intervals[0]); + + for (int[] interval : intervals) { + int start = interval[0]; + int end = interval[1]; + int lastEnd = output.get(output.size() - 1)[1]; + + if (start <= lastEnd) { + output.get(output.size() - 1)[1] = Math.max(lastEnd, end); + } else { + output.add(new int[]{start, end}); + } + } + return output.toArray(new int[output.size()][]); + } +} +``` + +```cpp +class Solution { +public: + vector> merge(vector>& intervals) { + sort(intervals.begin(), intervals.end()); + vector> output; + output.push_back(intervals[0]); + + for (auto& interval : intervals) { + int start = interval[0]; + int end = interval[1]; + int lastEnd = output.back()[1]; + + if (start <= lastEnd) { + output.back()[1] = max(lastEnd, end); + } else { + output.push_back({start, end}); + } + } + return output; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} intervals + * @return {number[][]} + */ + merge(intervals) { + intervals.sort((a, b) => a[0] - b[0]); + const output = []; + output.push(intervals[0]); + + for (const interval of intervals) { + const start = interval[0]; + const end = interval[1]; + const lastEnd = output[output.length - 1][1]; + + if (start <= lastEnd) { + output[output.length - 1][1] = Math.max(lastEnd, end); + } else { + output.push([start, end]); + } + } + return output; + } +} +``` + +```csharp +public class Solution { + public int[][] Merge(int[][] intervals) { + Array.Sort(intervals, (a, b) => a[0].CompareTo(b[0])); + List output = new List(); + output.Add(intervals[0]); + + foreach (int[] interval in intervals) { + int start = interval[0]; + int end = interval[1]; + int lastEnd = output[output.Count - 1][1]; + + if (start <= lastEnd) { + output[output.Count - 1][1] = Math.Max(lastEnd, end); + } else { + output.Add(new int[]{start, end}); + } + } + return output.ToArray(); + } +} +``` + +```go +func merge(intervals [][]int) [][]int { + sort.Slice(intervals, func(i, j int) bool { + return intervals[i][0] < intervals[j][0] + }) + output := [][]int{intervals[0]} + + for _, interval := range intervals[1:] { + start, end := interval[0], interval[1] + lastEnd := output[len(output)-1][1] + + if start <= lastEnd { + output[len(output)-1][1] = max(lastEnd, end) + } else { + output = append(output, []int{start, end}) + } + } + return output +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun merge(intervals: Array): Array { + intervals.sortBy { it[0] } + val output = mutableListOf(intervals[0]) + + for (interval in intervals.slice(1 until intervals.size)) { + val (start, end) = interval + val lastEnd = output.last()[1] + + if (start <= lastEnd) { + output[output.size - 1][1] = maxOf(lastEnd, end) + } else { + output.add(interval) + } + } + return output.toTypedArray() + } +} +``` + +```swift +class Solution { + func merge(_ intervals: [[Int]]) -> [[Int]] { + let intervals = intervals.sorted { $0[0] < $1[0] } + var output: [[Int]] = [intervals[0]] + + for interval in intervals { + let start = interval[0] + let end = interval[1] + var lastEnd = output.last![1] + + if start <= lastEnd { + output[output.count - 1][1] = max(lastEnd, end) + } else { + output.append([start, end]) + } + } + return output + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: + * $O(1)$ or $O(n)$ space depending on the sorting algorithm. + * $O(n)$ for the output list. + +--- + +## 2. Sweep Line Algorithm + +::tabs-start + +```python +class Solution: + def merge(self, intervals: List[List[int]]) -> List[List[int]]: + mp = defaultdict(int) + for start, end in intervals: + mp[start] += 1 + mp[end] -= 1 + + res = [] + interval = [] + have = 0 + for i in sorted(mp): + if not interval: + interval.append(i) + have += mp[i] + if have == 0: + interval.append(i) + res.append(interval) + interval = [] + return res +``` + +```java +public class Solution { + public int[][] merge(int[][] intervals) { + TreeMap map = new TreeMap<>(); + + for (int[] interval : intervals) { + map.put(interval[0], map.getOrDefault(interval[0], 0) + 1); + map.put(interval[1], map.getOrDefault(interval[1], 0) - 1); + } + + List res = new ArrayList<>(); + int have = 0; + int[] interval = new int[2]; + + for (int point : map.keySet()) { + if (have == 0) interval[0] = point; + have += map.get(point); + if (have == 0) { + interval[1] = point; + res.add(new int[] {interval[0], interval[1]}); + } + } + + return res.toArray(new int[res.size()][]); + } +} +``` + +```cpp +class Solution { +public: + vector> merge(vector>& intervals) { + map mp; + for (const auto& interval : intervals) { + mp[interval[0]]++; + mp[interval[1]]--; + } + + vector> res; + vector interval; + int have = 0; + for (const auto& [i, count] : mp) { + if (interval.empty()) { + interval.push_back(i); + } + have += count; + if (have == 0) { + interval.push_back(i); + res.push_back(interval); + interval.clear(); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} intervals + * @return {number[][]} + */ + merge(intervals) { + const mp = new Map(); + for (const [start, end] of intervals) { + mp.set(start, (mp.get(start) || 0) + 1); + mp.set(end, (mp.get(end) || 0) - 1); + } + + const sortedKeys = Array.from(mp.keys()).sort((a, b) => a - b); + const res = []; + let interval = []; + let have = 0; + + for (const i of sortedKeys) { + if (interval.length === 0) { + interval.push(i); + } + have += mp.get(i); + if (have === 0) { + interval.push(i); + res.push(interval); + interval = []; + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int[][] Merge(int[][] intervals) { + var mp = new SortedDictionary(); + foreach (var point in intervals) { + if (!mp.ContainsKey(point[0])) mp[point[0]] = 0; + if (!mp.ContainsKey(point[1])) mp[point[1]] = 0; + mp[point[0]]++; + mp[point[1]]--; + } + + var res = new List(); + var interval = new int[2]; + int have = 0; + foreach (var kvp in mp) { + if (have == 0) interval[0] = kvp.Key; + have += kvp.Value; + if (have == 0) { + interval[1] = kvp.Key; + res.Add(new int[] { interval[0], interval[1] }); + } + } + return res.ToArray(); + } +} +``` + +```go + +func merge(intervals [][]int) [][]int { + mp := make(map[int]int) + for _, interval := range intervals { + start, end := interval[0], interval[1] + mp[start]++ + mp[end]-- + } + + res := [][]int{} + interval := []int{} + have := 0 + keys := make([]int, 0, len(mp)) + for key := range mp { + keys = append(keys, key) + } + sort.Ints(keys) + + for _, i := range keys { + if len(interval) == 0 { + interval = append(interval, i) + } + have += mp[i] + if have == 0 { + interval = append(interval, i) + res = append(res, append([]int{}, interval...)) + interval = []int{} + } + } + return res +} +``` + +```kotlin +class Solution { + fun merge(intervals: Array): Array { + val mp = TreeMap() + for (interval in intervals) { + val (start, end) = interval + mp[start] = mp.getOrDefault(start, 0) + 1 + mp[end] = mp.getOrDefault(end, 0) - 1 + } + + val res = mutableListOf() + var interval = mutableListOf() + var have = 0 + + for (i in mp.keys) { + if (interval.isEmpty()) { + interval.add(i) + } + have += mp[i]!! + if (have == 0) { + interval.add(i) + res.add(interval.toIntArray()) + interval = mutableListOf() + } + } + return res.toTypedArray() + } +} +``` + +```swift +class Solution { + func merge(_ intervals: [[Int]]) -> [[Int]] { + var mp = [Int: Int]() + + for interval in intervals { + let start = interval[0] + let end = interval[1] + mp[start, default: 0] += 1 + mp[end, default: 0] -= 1 + } + + var res = [[Int]]() + var interval = [Int]() + var have = 0 + + for i in mp.keys.sorted() { + if interval.isEmpty { + interval.append(i) + } + have += mp[i, default: 0] + if have == 0 { + interval.append(i) + res.append(interval) + interval = [] + } + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Greedy + +::tabs-start + +```python +class Solution: + def merge(self, intervals: List[List[int]]) -> List[List[int]]: + max_val = max(interval[0] for interval in intervals) + + mp = [0] * (max_val + 1) + for start, end in intervals: + mp[start] = max(end + 1, mp[start]) + + res = [] + have = -1 + interval_start = -1 + for i in range(len(mp)): + if mp[i] != 0: + if interval_start == -1: + interval_start = i + have = max(mp[i] - 1, have) + if have == i: + res.append([interval_start, have]) + have = -1 + interval_start = -1 + + if interval_start != -1: + res.append([interval_start, have]) + + return res +``` + +```java +public class Solution { + public int[][] merge(int[][] intervals) { + int max = 0; + for (int i = 0; i < intervals.length; i++) { + max = Math.max(intervals[i][0], max); + } + + int[] mp = new int[max + 1]; + for (int i = 0; i < intervals.length; i++) { + int start = intervals[i][0]; + int end = intervals[i][1]; + mp[start] = Math.max(end + 1, mp[start]); + } + + int r = 0; + int have = -1; + int intervalStart = -1; + for (int i = 0; i < mp.length; i++) { + if (mp[i] != 0) { + if (intervalStart == -1) intervalStart = i; + have = Math.max(mp[i] - 1, have); + } + if (have == i) { + intervals[r++] = new int[] { intervalStart, have }; + have = -1; + intervalStart = -1; + } + } + + if (intervalStart != -1) { + intervals[r++] = new int[] { intervalStart, have }; + } + if (intervals.length == r) { + return intervals; + } + + int[][] res = new int[r][]; + for (int i = 0; i < r; i++) { + res[i] = intervals[i]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> merge(vector>& intervals) { + int max_val = 0; + for (const auto& interval : intervals) { + max_val = max(interval[0], max_val); + } + + vector mp(max_val + 1, 0); + for (const auto& interval : intervals) { + int start = interval[0]; + int end = interval[1]; + mp[start] = max(end + 1, mp[start]); + } + + vector> res; + int have = -1; + int intervalStart = -1; + for (int i = 0; i < mp.size(); i++) { + if (mp[i] != 0) { + if (intervalStart == -1) intervalStart = i; + have = max(mp[i] - 1, have); + } + if (have == i) { + res.push_back({intervalStart, have}); + have = -1; + intervalStart = -1; + } + } + + if (intervalStart != -1) { + res.push_back({intervalStart, have}); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} intervals + * @return {number[][]} + */ + merge(intervals) { + let max = 0; + for (let i = 0; i < intervals.length; i++) { + max = Math.max(intervals[i][0], max); + } + + let mp = new Array(max + 1).fill(0); + for (let i = 0; i < intervals.length; i++) { + let start = intervals[i][0]; + let end = intervals[i][1]; + mp[start] = Math.max(end + 1, mp[start]); + } + + let res = []; + let have = -1; + let intervalStart = -1; + for (let i = 0; i < mp.length; i++) { + if (mp[i] !== 0) { + if (intervalStart === -1) intervalStart = i; + have = Math.max(mp[i] - 1, have); + } + if (have === i) { + res.push([intervalStart, have]); + have = -1; + intervalStart = -1; + } + } + + if (intervalStart !== -1) { + res.push([intervalStart, have]); + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int[][] Merge(int[][] intervals) { + int max = 0; + for (int i = 0; i < intervals.Length; i++) { + max = Math.Max(intervals[i][0], max); + } + + int[] mp = new int[max + 1]; + for (int i = 0; i < intervals.Length; i++) { + int start = intervals[i][0]; + int end = intervals[i][1]; + mp[start] = Math.Max(end + 1, mp[start]); + } + + var res = new List(); + int have = -1; + int intervalStart = -1; + for (int i = 0; i < mp.Length; i++) { + if (mp[i] != 0) { + if (intervalStart == -1) intervalStart = i; + have = Math.Max(mp[i] - 1, have); + } + if (have == i) { + res.Add(new int[] { intervalStart, have }); + have = -1; + intervalStart = -1; + } + } + + if (intervalStart != -1) { + res.Add(new int[] { intervalStart, have }); + } + + return res.ToArray(); + } +} +``` + +```go +func merge(intervals [][]int) [][]int { + maxVal := math.MinInt + for _, interval := range intervals { + maxVal = max(maxVal, interval[0]) + } + + mp := make([]int, maxVal+1) + for _, interval := range intervals { + start, end := interval[0], interval[1] + mp[start] = max(mp[start], end+1) + } + + res := [][]int{} + have := -1 + intervalStart := -1 + for i := 0; i < len(mp); i++ { + if mp[i] != 0 { + if intervalStart == -1 { + intervalStart = i + } + have = max(mp[i]-1, have) + } + if have == i { + res = append(res, []int{intervalStart, have}) + have = -1 + intervalStart = -1 + } + } + + if intervalStart != -1 { + res = append(res, []int{intervalStart, have}) + } + + return res +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun merge(intervals: Array): Array { + var maxVal = Int.MIN_VALUE + for (interval in intervals) { + maxVal = maxOf(maxVal, interval[0]) + } + + val mp = IntArray(maxVal + 1) + for (interval in intervals) { + val (start, end) = interval + mp[start] = maxOf(mp[start], end + 1) + } + + val res = mutableListOf() + var have = -1 + var intervalStart = -1 + for (i in mp.indices) { + if (mp[i] != 0) { + if (intervalStart == -1) { + intervalStart = i + } + have = maxOf(mp[i] - 1, have) + } + if (have == i) { + res.add(intArrayOf(intervalStart, have)) + have = -1 + intervalStart = -1 + } + } + + if (intervalStart != -1) { + res.add(intArrayOf(intervalStart, have)) + } + + return res.toTypedArray() + } +} +``` + +```swift +class Solution { + func merge(_ intervals: [[Int]]) -> [[Int]] { + let maxVal = intervals.map { $0[0] }.max() ?? 0 + var mp = [Int](repeating: 0, count: maxVal + 1) + for interval in intervals { + let start = interval[0] + let end = interval[1] + mp[start] = max(end + 1, mp[start]) + } + + var res = [[Int]]() + var have = -1 + var intervalStart = -1 + + for i in 0.. Where $n$ is the length of the array and $m$ is the maximum start value among all the intervals. \ No newline at end of file diff --git a/articles/merge-k-sorted-linked-lists.md b/articles/merge-k-sorted-linked-lists.md new file mode 100644 index 000000000..6150fa6f5 --- /dev/null +++ b/articles/merge-k-sorted-linked-lists.md @@ -0,0 +1,2280 @@ +## 1. Brute Force + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + +class Solution: + def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]: + nodes = [] + for lst in lists: + while lst: + nodes.append(lst.val) + lst = lst.next + nodes.sort() + + res = ListNode(0) + cur = res + for node in nodes: + cur.next = ListNode(node) + cur = cur.next + return res.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +public class Solution { + public ListNode mergeKLists(ListNode[] lists) { + List nodes = new ArrayList<>(); + for (ListNode lst : lists) { + while (lst != null) { + nodes.add(lst.val); + lst = lst.next; + } + } + Collections.sort(nodes); + + ListNode res = new ListNode(0); + ListNode cur = res; + for (int node : nodes) { + cur.next = new ListNode(node); + cur = cur.next; + } + return res.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +class Solution { +public: + ListNode* mergeKLists(vector& lists) { + vector nodes; + for (ListNode* lst : lists) { + while (lst) { + nodes.push_back(lst->val); + lst = lst->next; + } + } + sort(nodes.begin(), nodes.end()); + + ListNode* res = new ListNode(0); + ListNode* cur = res; + for (int node : nodes) { + cur->next = new ListNode(node); + cur = cur->next; + } + return res->next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {ListNode[]} lists + * @return {ListNode} + */ + mergeKLists(lists) { + let nodes = []; + for (let lst of lists) { + while (lst) { + nodes.push(lst.val); + lst = lst.next; + } + } + nodes.sort((a, b) => a - b); + + let res = new ListNode(0); + let cur = res; + for (let node of nodes) { + cur.next = new ListNode(node); + cur = cur.next; + } + return res.next; + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +public class Solution { + public ListNode MergeKLists(ListNode[] lists) { + List nodes = new List(); + foreach (ListNode lst in lists) { + ListNode curr = lst; + while (curr != null) { + nodes.Add(curr.val); + curr = curr.next; + } + } + nodes.Sort(); + + ListNode res = new ListNode(0); + ListNode cur = res; + foreach (int node in nodes) { + cur.next = new ListNode(node); + cur = cur.next; + } + return res.next; + } +} +``` + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func mergeKLists(lists []*ListNode) *ListNode { + nodes := make([]int, 0) + + for _, list := range lists { + curr := list + for curr != nil { + nodes = append(nodes, curr.Val) + curr = curr.Next + } + } + + sort.Ints(nodes) + + dummy := &ListNode{Val: 0} + curr := dummy + + for _, val := range nodes { + curr.Next = &ListNode{Val: val} + curr = curr.Next + } + + return dummy.Next +} +``` + +```kotlin +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun mergeKLists(lists: Array): ListNode? { + val nodes = mutableListOf() + + for (list in lists) { + var curr = list + while (curr != null) { + nodes.add(curr.`val`) + curr = curr.next + } + } + + nodes.sort() + + val dummy = ListNode(0) + var curr = dummy + + for (value in nodes) { + curr.next = ListNode(value) + curr = curr.next!! + } + + return dummy.next + } +} +``` + +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func mergeKLists(_ lists: [ListNode?]) -> ListNode? { + var nodes: [Int] = [] + + for list in lists { + var lst = list + while lst != nil { + nodes.append(lst!.val) + lst = lst?.next + } + } + + nodes.sort() + + let dummy = ListNode(0) + var cur = dummy + for node in nodes { + cur.next = ListNode(node) + cur = cur.next! + } + + return dummy.next + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Iteration + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + +class Solution: + def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]: + res = ListNode(0) + cur = res + + while True: + minNode = -1 + for i in range(len(lists)): + if not lists[i]: + continue + if minNode == -1 or lists[minNode].val > lists[i].val: + minNode = i + + if minNode == -1: + break + cur.next = lists[minNode] + lists[minNode] = lists[minNode].next + cur = cur.next + + return res.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +public class Solution { + public ListNode mergeKLists(ListNode[] lists) { + ListNode res = new ListNode(0); + ListNode cur = res; + + while (true) { + int minNode = -1; + for (int i = 0; i < lists.length; i++) { + if (lists[i] == null) { + continue; + } + if (minNode == -1 || lists[minNode].val > lists[i].val) { + minNode = i; + } + } + + if (minNode == -1) { + break; + } + cur.next = lists[minNode]; + lists[minNode] = lists[minNode].next; + cur = cur.next; + } + + return res.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +class Solution { +public: + ListNode* mergeKLists(vector& lists) { + ListNode* res = new ListNode(0); + ListNode* cur = res; + + while (true) { + int minNode = -1; + for (int i = 0; i < lists.size(); i++) { + if (!lists[i]) continue; + if (minNode == -1 || lists[minNode]->val > lists[i]->val) { + minNode = i; + } + } + + if (minNode == -1) break; + cur->next = lists[minNode]; + lists[minNode] = lists[minNode]->next; + cur = cur->next; + } + return res->next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {ListNode[]} lists + * @return {ListNode} + */ + mergeKLists(lists) { + let res = new ListNode(0); + let cur = res; + + while (true) { + let minNode = -1; + for (let i = 0; i < lists.length; i++) { + if (!lists[i]) continue; + if (minNode === -1 || lists[minNode].val > lists[i].val) { + minNode = i; + } + } + + if (minNode === -1) break; + cur.next = lists[minNode]; + lists[minNode] = lists[minNode].next; + cur = cur.next; + } + return res.next; + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +public class Solution { + public ListNode MergeKLists(ListNode[] lists) { + ListNode res = new ListNode(0); + ListNode cur = res; + + while (true) { + int minNode = -1; + for (int i = 0; i < lists.Length; i++) { + if (lists[i] == null) continue; + if (minNode == -1 || lists[minNode].val > lists[i].val) { + minNode = i; + } + } + + if (minNode == -1) break; + cur.next = lists[minNode]; + lists[minNode] = lists[minNode].next; + cur = cur.next; + } + return res.next; + } +} +``` + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func mergeKLists(lists []*ListNode) *ListNode { + res := &ListNode{Val: 0} + cur := res + + for { + minNode := -1 + for i := range lists { + if lists[i] == nil { + continue + } + if minNode == -1 || lists[minNode].Val > lists[i].Val { + minNode = i + } + } + + if minNode == -1 { + break + } + + cur.Next = lists[minNode] + lists[minNode] = lists[minNode].Next + cur = cur.Next + } + + return res.Next +} +``` + +```kotlin +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun mergeKLists(lists: Array): ListNode? { + val res = ListNode(0) + var cur = res + + while (true) { + var minNode = -1 + for (i in lists.indices) { + if (lists[i] == null) { + continue + } + if (minNode == -1 || lists[minNode]!!.`val` > lists[i]!!.`val`) { + minNode = i + } + } + + if (minNode == -1) { + break + } + + cur.next = lists[minNode] + lists[minNode] = lists[minNode]!!.next + cur = cur.next!! + } + + return res.next + } +} +``` + +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func mergeKLists(_ lists: [ListNode?]) -> ListNode? { + var lists = lists + let dummy = ListNode(0) + var cur = dummy + + while true { + var minNodeIndex: Int? = nil + for i in 0.. Where $k$ is the total number of lists and $n$ is the total number of nodes across $k$ lists. + +--- + +## 3. Merge Lists One By One + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + +class Solution: + def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]: + if len(lists) == 0: + return None + + for i in range(1, len(lists)): + lists[i] = self.mergeList(lists[i - 1], lists[i]) + + return lists[-1] + + def mergeList(self, l1, l2): + dummy = ListNode() + tail = dummy + + while l1 and l2: + if l1.val < l2.val: + tail.next = l1 + l1 = l1.next + else: + tail.next = l2 + l2 = l2.next + tail = tail.next + if l1: + tail.next = l1 + if l2: + tail.next = l2 + return dummy.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +class Solution { + public ListNode mergeKLists(ListNode[] lists) { + if (lists.length == 0) return null; + + for (int i = 1; i < lists.length; i++) { + lists[i] = merge(lists[i], lists[i - 1]); + } + return lists[lists.length - 1]; + } + + private ListNode merge(ListNode l1, ListNode l2) { + ListNode dummy = new ListNode(0); + ListNode curr = dummy; + + while (l1 != null && l2 != null) { + if (l1.val <= l2.val) { + curr.next = l1; + l1 = l1.next; + } else { + curr.next = l2; + l2 = l2.next; + } + + curr = curr.next; + } + + if (l1 != null) { + curr.next = l1; + } else { + curr.next = l2; + } + + return dummy.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +class Solution { +public: + ListNode* mergeKLists(vector& lists) { + if (lists.empty()) return nullptr; + + for (int i = 1; i < lists.size(); i++) { + lists[i] = merge(lists[i], lists[i - 1]); + } + return lists.back(); + } + +private: + ListNode* merge(ListNode* l1, ListNode* l2) { + ListNode* dummy = new ListNode(0); + ListNode* curr = dummy; + + while (l1 != nullptr && l2 != nullptr) { + if (l1->val <= l2->val) { + curr->next = l1; + l1 = l1->next; + } else { + curr->next = l2; + l2 = l2->next; + } + curr = curr->next; + } + + if (l1 != nullptr) { + curr->next = l1; + } else { + curr->next = l2; + } + + return dummy->next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {ListNode[]} lists + * @return {ListNode} + */ + mergeKLists(lists) { + if (lists.length === 0) return null; + + for (let i = 0; i < lists.length; i++) { + lists[i] = this.mergeList(lists[i], lists[i - 1]); + } + return lists[lists.length - 1]; + } + + /** + * @param {ListNode} l1 + * @param {ListNode} l2 + * @return {ListNode} + */ + mergeList(l1, l2) { + const dummy = new ListNode(); + let tail = dummy; + + while (l1 && l2) { + if (l1.val < l2.val) { + tail.next = l1; + l1 = l1.next; + } else { + tail.next = l2; + l2 = l2.next; + } + tail = tail.next; + } + if (l1) { + tail.next = l1; + } + if (l2) { + tail.next = l2; + } + return dummy.next; + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +public class Solution { + public ListNode MergeKLists(ListNode[] lists) { + if (lists.Length == 0) return null; + + for (int i = 1; i < lists.Length; i++) { + lists[i] = Merge(lists[i], lists[i - 1]); + } + return lists[lists.Length - 1]; + } + + private ListNode Merge(ListNode l1, ListNode l2) { + ListNode dummy = new ListNode(0); + ListNode curr = dummy; + + while (l1 != null && l2 != null) { + if (l1.val <= l2.val) { + curr.next = l1; + l1 = l1.next; + } else { + curr.next = l2; + l2 = l2.next; + } + curr = curr.next; + } + + if (l1 != null) { + curr.next = l1; + } else { + curr.next = l2; + } + + return dummy.next; + } +} +``` + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func mergeList(l1 *ListNode, l2 *ListNode) *ListNode { + dummy := &ListNode{} + tail := dummy + + for l1 != nil && l2 != nil { + if l1.Val < l2.Val { + tail.Next = l1 + l1 = l1.Next + } else { + tail.Next = l2 + l2 = l2.Next + } + tail = tail.Next + } + + if l1 != nil { + tail.Next = l1 + } + if l2 != nil { + tail.Next = l2 + } + + return dummy.Next +} + +func mergeKLists(lists []*ListNode) *ListNode { + if len(lists) == 0 { + return nil + } + + for i := 1; i < len(lists); i++ { + lists[i] = mergeList(lists[i-1], lists[i]) + } + + return lists[len(lists)-1] +} +``` + +```kotlin +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + private fun mergeList(l1: ListNode?, l2: ListNode?): ListNode? { + val dummy = ListNode(0) + var tail = dummy + var first = l1 + var second = l2 + + while (first != null && second != null) { + if (first.`val` < second.`val`) { + tail.next = first + first = first.next + } else { + tail.next = second + second = second.next + } + tail = tail.next!! + } + + if (first != null) { + tail.next = first + } + if (second != null) { + tail.next = second + } + + return dummy.next + } + + fun mergeKLists(lists: Array): ListNode? { + if (lists.isEmpty()) { + return null + } + + for (i in 1 until lists.size) { + lists[i] = mergeList(lists[i-1], lists[i]) + } + + return lists.last() + } +} +``` + +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func mergeKLists(_ lists: [ListNode?]) -> ListNode? { + if lists.isEmpty { + return nil + } + + var lists = lists + for i in 1.. ListNode? { + let dummy = ListNode(0) + var tail = dummy + var l1 = l1, l2 = l2 + + while l1 != nil && l2 != nil { + if l1!.val < l2!.val { + tail.next = l1 + l1 = l1?.next + } else { + tail.next = l2 + l2 = l2?.next + } + tail = tail.next! + } + + if l1 != nil { + tail.next = l1 + } + if l2 != nil { + tail.next = l2 + } + + return dummy.next + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(1)$ + +> Where $k$ is the total number of lists and $n$ is the total number of nodes across $k$ lists. + +--- + +## 4. Heap + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + +class NodeWrapper: + def __init__(self, node): + self.node = node + + def __lt__(self, other): + return self.node.val < other.node.val + +class Solution: + def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]: + if len(lists) == 0: + return None + + res = ListNode(0) + cur = res + minHeap = [] + + for lst in lists: + if lst is not None: + heapq.heappush(minHeap, NodeWrapper(lst)) + + while minHeap: + node_wrapper = heapq.heappop(minHeap) + cur.next = node_wrapper.node + cur = cur.next + + if node_wrapper.node.next: + heapq.heappush(minHeap, NodeWrapper(node_wrapper.node.next)) + + return res.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +class Solution { + public ListNode mergeKLists(ListNode[] lists) { + if (lists.length == 0) return null; + + PriorityQueue minHeap = new PriorityQueue<>((a, b) -> a.val - b.val); + for (ListNode list : lists) { + if (list != null) { + minHeap.offer(list); + } + } + + ListNode res = new ListNode(0); + ListNode cur = res; + while (!minHeap.isEmpty()) { + ListNode node = minHeap.poll(); + cur.next = node; + cur = cur.next; + + node = node.next; + if (node != null) { + minHeap.offer(node); + } + } + return res.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +class Solution { +public: + ListNode* mergeKLists(vector& lists) { + if (lists.empty()) return nullptr; + + auto cmp = [](ListNode* a, ListNode* b) { return a->val > b->val; }; + priority_queue, decltype(cmp)> minHeap(cmp); + + for (ListNode* list : lists) { + if (list != nullptr) { + minHeap.push(list); + } + } + + ListNode* res = new ListNode(0); + ListNode* cur = res; + while (!minHeap.empty()) { + ListNode* node = minHeap.top(); + minHeap.pop(); + cur->next = node; + cur = cur->next; + + node = node->next; + if (node != nullptr) { + minHeap.push(node); + } + } + return res->next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {ListNode[]} lists + * @return {ListNode} + */ + mergeKLists(lists) { + if (lists.length === 0) return null; + const minHeap = new MinPriorityQueue(x => x.val); + for (let list of lists) { + if (list != null) + minHeap.enqueue(list); + } + + let res = new ListNode(0); + let cur = res; + while (minHeap.size() > 0) { + let node = minHeap.dequeue(); + cur.next = node; + cur = cur.next; + + node = node.next; + if (node != null) { + minHeap.enqueue(node); + } + } + return res.next; + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +public class Solution { + public ListNode MergeKLists(ListNode[] lists) { + if (lists.Length == 0) return null; + + var minHeap = new PriorityQueue(); + foreach (var list in lists) { + if (list != null) { + minHeap.Enqueue(list, list.val); + } + } + + var res = new ListNode(0); + var cur = res; + while (minHeap.Count > 0) { + var node = minHeap.Dequeue(); + cur.next = node; + cur = cur.next; + + node = node.next; + if (node != null) { + minHeap.Enqueue(node, node.val); + } + } + return res.next; + } +} +``` + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func mergeKLists(lists []*ListNode) *ListNode { + if len(lists) == 0 { + return nil + } + + minHeap := priorityqueue.NewWith(func(a, b interface{}) int { + return a.(*ListNode).Val - b.(*ListNode).Val + }) + + for _, list := range lists { + if list != nil { + minHeap.Enqueue(list) + } + } + + res := &ListNode{Val: 0} + cur := res + + for !minHeap.Empty() { + node, _ := minHeap.Dequeue() + cur.Next = node.(*ListNode) + cur = cur.Next + + if cur.Next != nil { + minHeap.Enqueue(cur.Next) + } + } + + return res.Next +} +``` + +```kotlin +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun mergeKLists(lists: Array): ListNode? { + if (lists.isEmpty()) return null + + val minHeap = PriorityQueue(compareBy { it.`val` }) + + for (list in lists) { + list?.let { minHeap.offer(it) } + } + + val res = ListNode(0) + var cur = res + + while (minHeap.isNotEmpty()) { + val node = minHeap.poll() + cur.next = node + cur = cur.next!! + + node.next?.let { minHeap.offer(it) } + } + + return res.next + } +} +``` + +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +struct NodeWrapper: Comparable { + let node: ListNode + + init(_ node: ListNode) { + self.node = node + } + + static func < (lhs: NodeWrapper, rhs: NodeWrapper) -> Bool { + return lhs.node.val < rhs.node.val + } + + static func == (lhs: NodeWrapper, rhs: NodeWrapper) -> Bool { + return lhs.node.val == rhs.node.val + } +} + +class Solution { + func mergeKLists(_ lists: [ListNode?]) -> ListNode? { + var heap = Heap() + + for list in lists { + if let node = list { + heap.insert(NodeWrapper(node)) + } + } + + let dummy = ListNode(0) + var tail = dummy + + while let wrapper = heap.popMin() { + tail.next = wrapper.node + tail = tail.next! + + if let next = wrapper.node.next { + heap.insert(NodeWrapper(next)) + } + } + + return dummy.next + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log k)$ +* Space complexity: $O(k)$ + +> Where $k$ is the total number of lists and $n$ is the total number of nodes across $k$ lists. + +--- + +## 5. Divide And Conquer (Recursion) + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + +class Solution: + def mergeKLists(self, lists): + if not lists or len(lists) == 0: + return None + return self.divide(lists, 0, len(lists) - 1) + + def divide(self, lists, l, r): + if l > r: + return None + if l == r: + return lists[l] + + mid = l + (r - l) // 2 + left = self.divide(lists, l, mid) + right = self.divide(lists, mid + 1, r) + + return self.conquer(left, right) + + def conquer(self, l1, l2): + dummy = ListNode(0) + curr = dummy + + while l1 and l2: + if l1.val <= l2.val: + curr.next = l1 + l1 = l1.next + else: + curr.next = l2 + l2 = l2.next + curr = curr.next + + if l1: + curr.next = l1 + else: + curr.next = l2 + + return dummy.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +class Solution { + public ListNode mergeKLists(ListNode[] lists) { + if (lists == null || lists.length == 0) { + return null; + } + return divide(lists, 0, lists.length - 1); + } + + private ListNode divide(ListNode[] lists, int l, int r) { + if (l > r) { + return null; + } + if (l == r) { + return lists[l]; + } + + int mid = l + (r - l) / 2; + ListNode left = divide(lists, l, mid); + ListNode right = divide(lists, mid + 1, r); + + return conquer(left, right); + } + + private ListNode conquer(ListNode l1, ListNode l2) { + ListNode dummy = new ListNode(0); + ListNode curr = dummy; + + while (l1 != null && l2 != null) { + if (l1.val <= l2.val) { + curr.next = l1; + l1 = l1.next; + } else { + curr.next = l2; + l2 = l2.next; + } + + curr = curr.next; + } + + if (l1 != null) { + curr.next = l1; + } else { + curr.next = l2; + } + + return dummy.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +class Solution { +public: + ListNode* mergeKLists(vector& lists) { + if (lists.empty()) { + return nullptr; + } + return divide(lists, 0, lists.size() - 1); + } + +private: + ListNode* divide(vector& lists, int l, int r) { + if (l > r) { + return nullptr; + } + if (l == r) { + return lists[l]; + } + + int mid = l + (r - l) / 2; + ListNode* left = divide(lists, l, mid); + ListNode* right = divide(lists, mid + 1, r); + + return conquer(left, right); + } + + ListNode* conquer(ListNode* l1, ListNode* l2) { + ListNode dummy(0); + ListNode* curr = &dummy; + + while (l1 && l2) { + if (l1->val <= l2->val) { + curr->next = l1; + l1 = l1->next; + } else { + curr->next = l2; + l2 = l2->next; + } + curr = curr->next; + } + + if (l1) { + curr->next = l1; + } else { + curr->next = l2; + } + + return dummy.next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {ListNode[]} lists + * @return {ListNode} + */ + mergeKLists(lists) { + if (!lists || lists.length === 0) { + return null; + } + return this.divide(lists, 0, lists.length - 1); + } + + /** + * @param {ListNode[]} lists + * @param {number} l + * @param {number} r + * @return {ListNode} + */ + divide(lists, l, r) { + if (l > r) { + return null; + } + if (l === r) { + return lists[l]; + } + + const mid = Math.floor(l + (r - l) / 2); + const left = this.divide(lists, l, mid); + const right = this.divide(lists, mid + 1, r); + + return this.conquer(left, right); + } + + /** + * @param {ListNode} l1 + * @param {ListNode} l2 + * @return {ListNode} + */ + conquer(l1, l2) { + const dummy = new ListNode(0); + let curr = dummy; + + while (l1 && l2) { + if (l1.val <= l2.val) { + curr.next = l1; + l1 = l1.next; + } else { + curr.next = l2; + l2 = l2.next; + } + curr = curr.next; + } + + curr.next = l1 ? l1 : l2; + return dummy.next; + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +public class Solution { + public ListNode MergeKLists(ListNode[] lists) { + if (lists == null || lists.Length == 0) { + return null; + } + return Divide(lists, 0, lists.Length - 1); + } + + private ListNode Divide(ListNode[] lists, int l, int r) { + if (l > r) { + return null; + } + if (l == r) { + return lists[l]; + } + + int mid = l + (r - l) / 2; + ListNode left = Divide(lists, l, mid); + ListNode right = Divide(lists, mid + 1, r); + + return Conquer(left, right); + } + + private ListNode Conquer(ListNode l1, ListNode l2) { + ListNode dummy = new ListNode(0); + ListNode curr = dummy; + + while (l1 != null && l2 != null) { + if (l1.val <= l2.val) { + curr.next = l1; + l1 = l1.next; + } else { + curr.next = l2; + l2 = l2.next; + } + curr = curr.next; + } + + if (l1 != null) { + curr.next = l1; + } else { + curr.next = l2; + } + + return dummy.next; + } +} +``` + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func mergeKLists(lists []*ListNode) *ListNode { + if len(lists) == 0 { + return nil + } + return divide(lists, 0, len(lists)-1) +} + +func divide(lists []*ListNode, left, right int) *ListNode { + if left > right { + return nil + } + if left == right { + return lists[left] + } + + mid := left + (right-left)/2 + l1 := divide(lists, left, mid) + l2 := divide(lists, mid+1, right) + + return conquer(l1, l2) +} + +func conquer(l1, l2 *ListNode) *ListNode { + dummy := &ListNode{} + curr := dummy + + for l1 != nil && l2 != nil { + if l1.Val <= l2.Val { + curr.Next = l1 + l1 = l1.Next + } else { + curr.Next = l2 + l2 = l2.Next + } + curr = curr.Next + } + + if l1 != nil { + curr.Next = l1 + } else { + curr.Next = l2 + } + + return dummy.Next +} +``` + +```kotlin +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun mergeKLists(lists: Array): ListNode? { + if (lists.isEmpty()) return null + return divide(lists, 0, lists.size - 1) + } + + private fun divide(lists: Array, left: Int, right: Int): ListNode? { + if (left > right) return null + if (left == right) return lists[left] + + val mid = left + (right - left) / 2 + val l1 = divide(lists, left, mid) + val l2 = divide(lists, mid + 1, right) + + return conquer(l1, l2) + } + + private fun conquer(l1: ListNode?, l2: ListNode?): ListNode? { + val dummy = ListNode(0) + var curr = dummy + var list1 = l1 + var list2 = l2 + + while (list1 != null && list2 != null) { + if (list1.`val` <= list2.`val`) { + curr.next = list1 + list1 = list1.next + } else { + curr.next = list2 + list2 = list2.next + } + curr = curr.next!! + } + + curr.next = list1 ?: list2 + return dummy.next + } +} +``` + +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func mergeKLists(_ lists: [ListNode?]) -> ListNode? { + if lists.isEmpty { + return nil + } + return divide(lists, 0, lists.count - 1) + } + + private func divide(_ lists: [ListNode?], _ l: Int, _ r: Int) -> ListNode? { + if l > r { + return nil + } + if l == r { + return lists[l] + } + + let mid = l + (r - l) / 2 + let left = divide(lists, l, mid) + let right = divide(lists, mid + 1, r) + return conquer(left, right) + } + + private func conquer(_ l1: ListNode?, _ l2: ListNode?) -> ListNode? { + let dummy = ListNode(0) + var curr = dummy + var l1 = l1, l2 = l2 + + while let node1 = l1, let node2 = l2 { + if node1.val <= node2.val { + curr.next = node1 + l1 = node1.next + } else { + curr.next = node2 + l2 = node2.next + } + curr = curr.next! + } + + curr.next = l1 ?? l2 + return dummy.next + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log k)$ +* Space complexity: $O(\log k)$ + +> Where $k$ is the total number of lists and $n$ is the total number of nodes across $k$ lists. + +--- + +## 6. Divide And Conquer (Iteration) + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + +class Solution: + + def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]: + if not lists or len(lists) == 0: + return None + + while len(lists) > 1: + mergedLists = [] + for i in range(0, len(lists), 2): + l1 = lists[i] + l2 = lists[i + 1] if (i + 1) < len(lists) else None + mergedLists.append(self.mergeList(l1, l2)) + lists = mergedLists + return lists[0] + + def mergeList(self, l1, l2): + dummy = ListNode() + tail = dummy + + while l1 and l2: + if l1.val < l2.val: + tail.next = l1 + l1 = l1.next + else: + tail.next = l2 + l2 = l2.next + tail = tail.next + + if l1: + tail.next = l1 + if l2: + tail.next = l2 + + return dummy.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +public class Solution { + public ListNode mergeKLists(ListNode[] lists) { + if (lists == null || lists.length == 0) { + return null; + } + + while (lists.length > 1) { + List mergedLists = new ArrayList<>(); + for (int i = 0; i < lists.length; i += 2) { + ListNode l1 = lists[i]; + ListNode l2 = (i + 1) < lists.length ? lists[i + 1] : null; + mergedLists.add(mergeList(l1, l2)); + } + lists = mergedLists.toArray(new ListNode[0]); + } + return lists[0]; + } + + private ListNode mergeList(ListNode l1, ListNode l2) { + ListNode dummy = new ListNode(); + ListNode tail = dummy; + + while (l1 != null && l2 != null) { + if (l1.val < l2.val) { + tail.next = l1; + l1 = l1.next; + } else { + tail.next = l2; + l2 = l2.next; + } + tail = tail.next; + } + + if (l1 != null) { + tail.next = l1; + } + if (l2 != null) { + tail.next = l2; + } + + return dummy.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +class Solution { +public: + ListNode* mergeKLists(vector& lists) { + if (lists.empty()) { + return nullptr; + } + + while (lists.size() > 1) { + vector mergedLists; + for (int i = 0; i < lists.size(); i += 2) { + ListNode* l1 = lists[i]; + ListNode* l2 = (i + 1) < lists.size() ? lists[i + 1] : nullptr; + mergedLists.push_back(mergeList(l1, l2)); + } + lists = mergedLists; + } + return lists[0]; + } + +private: + ListNode* mergeList(ListNode* l1, ListNode* l2) { + ListNode dummy; + ListNode* tail = &dummy; + + while (l1 && l2) { + if (l1->val < l2->val) { + tail->next = l1; + l1 = l1->next; + } else { + tail->next = l2; + l2 = l2->next; + } + tail = tail->next; + } + + if (l1) { + tail->next = l1; + } + if (l2) { + tail->next = l2; + } + + return dummy.next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {ListNode[]} lists + * @return {ListNode} + */ + mergeKLists(lists) { + if (!lists || lists.length === 0) { + return null; + } + + while (lists.length > 1) { + const mergedLists = []; + for (let i = 0; i < lists.length; i += 2) { + const l1 = lists[i]; + const l2 = (i + 1) < lists.length ? lists[i + 1] : null; + mergedLists.push(this.mergeList(l1, l2)); + } + lists = mergedLists; + } + return lists[0]; + } + + /** + * @param {ListNode} l1 + * @param {ListNode} l2 + * @return {ListNode} + */ + mergeList(l1, l2) { + const dummy = new ListNode(0); + let curr = dummy; + + while (l1 && l2) { + if (l1.val <= l2.val) { + curr.next = l1; + l1 = l1.next; + } else { + curr.next = l2; + l2 = l2.next; + } + curr = curr.next; + } + + curr.next = l1 ? l1 : l2; + return dummy.next; + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +public class Solution { + public ListNode MergeKLists(ListNode[] lists) { + if (lists == null || lists.Length == 0) { + return null; + } + + while (lists.Length > 1) { + List mergedLists = new List(); + for (int i = 0; i < lists.Length; i += 2) { + ListNode l1 = lists[i]; + ListNode l2 = (i + 1) < lists.Length ? lists[i + 1] : null; + mergedLists.Add(MergeList(l1, l2)); + } + lists = mergedLists.ToArray(); + } + return lists[0]; + } + + private ListNode MergeList(ListNode l1, ListNode l2) { + ListNode dummy = new ListNode(); + ListNode tail = dummy; + + while (l1 != null && l2 != null) { + if (l1.val < l2.val) { + tail.next = l1; + l1 = l1.next; + } else { + tail.next = l2; + l2 = l2.next; + } + tail = tail.next; + } + + if (l1 != null) { + tail.next = l1; + } + if (l2 != null) { + tail.next = l2; + } + + return dummy.next; + } +} +``` + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func mergeKLists(lists []*ListNode) *ListNode { + if len(lists) == 0 { + return nil + } + + for len(lists) > 1 { + var mergedLists []*ListNode + for i := 0; i < len(lists); i += 2 { + l1 := lists[i] + var l2 *ListNode + if i+1 < len(lists) { + l2 = lists[i+1] + } + mergedLists = append(mergedLists, mergeList(l1, l2)) + } + lists = mergedLists + } + return lists[0] +} + +func mergeList(l1, l2 *ListNode) *ListNode { + dummy := &ListNode{} + tail := dummy + + for l1 != nil && l2 != nil { + if l1.Val < l2.Val { + tail.Next = l1 + l1 = l1.Next + } else { + tail.Next = l2 + l2 = l2.Next + } + tail = tail.Next + } + + if l1 != nil { + tail.Next = l1 + } else { + tail.Next = l2 + } + + return dummy.Next +} +``` + +```kotlin +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun mergeKLists(lists: Array): ListNode? { + if (lists.isEmpty()) return null + + var currentLists = lists.toList() + while (currentLists.size > 1) { + val mergedLists = mutableListOf() + for (i in currentLists.indices step 2) { + val l1 = currentLists[i] + val l2 = if (i + 1 < currentLists.size) currentLists[i + 1] else null + mergedLists.add(mergeList(l1, l2)) + } + currentLists = mergedLists + } + return currentLists[0] + } + + private fun mergeList(l1: ListNode?, l2: ListNode?): ListNode? { + val dummy = ListNode(0) + var tail = dummy + var list1 = l1 + var list2 = l2 + + while (list1 != null && list2 != null) { + if (list1.`val` < list2.`val`) { + tail.next = list1 + list1 = list1.next + } else { + tail.next = list2 + list2 = list2.next + } + tail = tail.next!! + } + + tail.next = list1 ?: list2 + return dummy.next + } +} +``` + +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func mergeKLists(_ lists: [ListNode?]) -> ListNode? { + if lists.isEmpty { + return nil + } + + var lists = lists + + while lists.count > 1 { + var mergedLists: [ListNode?] = [] + + for i in stride(from: 0, to: lists.count, by: 2) { + let l1 = lists[i] + let l2 = i + 1 < lists.count ? lists[i + 1] : nil + mergedLists.append(mergeList(l1, l2)) + } + + lists = mergedLists + } + + return lists[0] + } + + private func mergeList(_ l1: ListNode?, _ l2: ListNode?) -> ListNode? { + let dummy = ListNode(0) + var tail = dummy + var l1 = l1, l2 = l2 + + while let node1 = l1, let node2 = l2 { + if node1.val < node2.val { + tail.next = node1 + l1 = node1.next + } else { + tail.next = node2 + l2 = node2.next + } + tail = tail.next! + } + + tail.next = l1 ?? l2 + return dummy.next + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log k)$ +* Space complexity: $O(k)$ + +> Where $k$ is the total number of lists and $n$ is the total number of nodes across $k$ lists. \ No newline at end of file diff --git a/articles/merge-sorted-array.md b/articles/merge-sorted-array.md new file mode 100644 index 000000000..a0e701ef0 --- /dev/null +++ b/articles/merge-sorted-array.md @@ -0,0 +1,448 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None: + """ + Do not return anything, modify nums1 in-place instead. + """ + nums1[m:] = nums2[:n] + nums1.sort() +``` + +```java +public class Solution { + public void merge(int[] nums1, int m, int[] nums2, int n) { + for (int i = 0; i < n; i++) { + nums1[i + m] = nums2[i]; + } + Arrays.sort(nums1); + } +} +``` + +```cpp +class Solution { +public: + void merge(vector& nums1, int m, vector& nums2, int n) { + for (int i = 0; i < n; i++) { + nums1[i + m] = nums2[i]; + } + sort(nums1.begin(), nums1.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number} m + * @param {number[]} nums2 + * @param {number} n + * @return {void} Do not return anything, modify nums1 in-place instead. + */ + merge(nums1, m, nums2, n) { + for (let i = 0; i < n; i++) { + nums1[i + m] = nums2[i]; + } + nums1.sort((a, b) => a - b); + } +} +``` + +```csharp +public class Solution { + public void Merge(int[] nums1, int m, int[] nums2, int n) { + for (int i = 0; i < n; i++) { + nums1[i + m] = nums2[i]; + } + Array.Sort(nums1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((m + n) \log (m + n))$ +* Space complexity: $O(1)$ or $O(m + n)$ depending on the sorting algorithm. + +> Where $m$ and $n$ represent the number of elements in the arrays $nums1$ and $nums2$, respectively. + +--- + +## 2. Three Pointers With Extra Space + +::tabs-start + +```python +class Solution: + def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None: + """ + Do not return anything, modify nums1 in-place instead. + """ + nums1_copy = nums1[:m] + idx = 0 + i = j = 0 + while idx < m + n: + if j >= n or (i < m and nums1_copy[i] <= nums2[j]): + nums1[idx] = nums1_copy[i] + i += 1 + else: + nums1[idx] = nums2[j] + j += 1 + idx += 1 +``` + +```java +public class Solution { + public void merge(int[] nums1, int m, int[] nums2, int n) { + int[] nums1Copy = Arrays.copyOf(nums1, m); + int idx = 0, i = 0, j = 0; + + while (idx < m + n) { + if (j >= n || (i < m && nums1Copy[i] <= nums2[j])) { + nums1[idx++] = nums1Copy[i++]; + } else { + nums1[idx++] = nums2[j++]; + } + } + } +} +``` + +```cpp +class Solution { +public: + void merge(vector& nums1, int m, vector& nums2, int n) { + vector nums1Copy(nums1.begin(), nums1.begin() + m); + int idx = 0, i = 0, j = 0; + + while (idx < m + n) { + if (j >= n || (i < m && nums1Copy[i] <= nums2[j])) { + nums1[idx++] = nums1Copy[i++]; + } else { + nums1[idx++] = nums2[j++]; + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number} m + * @param {number[]} nums2 + * @param {number} n + * @return {void} Do not return anything, modify nums1 in-place instead. + */ + merge(nums1, m, nums2, n) { + const nums1Copy = nums1.slice(0, m); + let idx = 0, i = 0, j = 0; + + while (idx < m + n) { + if (j >= n || (i < m && nums1Copy[i] <= nums2[j])) { + nums1[idx++] = nums1Copy[i++]; + } else { + nums1[idx++] = nums2[j++]; + } + } + } +} +``` + +```csharp +public class Solution { + public void Merge(int[] nums1, int m, int[] nums2, int n) { + int[] nums1Copy = new int[m]; + Array.Copy(nums1, nums1Copy, m); + + int idx = 0, i = 0, j = 0; + + while (idx < m + n) { + if (j >= n || (i < m && nums1Copy[i] <= nums2[j])) { + nums1[idx++] = nums1Copy[i++]; + } else { + nums1[idx++] = nums2[j++]; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(m)$ + +> Where $m$ and $n$ represent the number of elements in the arrays $nums1$ and $nums2$, respectively. + +--- + +## 3. Three Pointers Without Extra Space - I + +::tabs-start + +```python +class Solution: + def merge(self, nums1: list[int], m: int, nums2: list[int], n: int) -> None: + """ + Do not return anything, modify nums1 in-place instead. + """ + last = m + n - 1 + + # Merge in reverse order + while m > 0 and n > 0: + if nums1[m - 1] > nums2[n - 1]: + nums1[last] = nums1[m - 1] + m -= 1 + else: + nums1[last] = nums2[n - 1] + n -= 1 + last -= 1 + + # Fill nums1 with leftover nums2 elements + while n > 0: + nums1[last] = nums2[n - 1] + n -= 1 + last -= 1 +``` + +```java +public class Solution { + public void merge(int[] nums1, int m, int[] nums2, int n) { + int last = m + n - 1; + + // Merge in reverse order + while (m > 0 && n > 0) { + if (nums1[m - 1] > nums2[n - 1]) { + nums1[last] = nums1[m - 1]; + m--; + } else { + nums1[last] = nums2[n - 1]; + n--; + } + last--; + } + + // Fill nums1 with leftover nums2 elements + while (n > 0) { + nums1[last] = nums2[n - 1]; + n--; + last--; + } + } +} +``` + +```cpp +class Solution { +public: + void merge(vector& nums1, int m, vector& nums2, int n) { + int last = m + n - 1; + + // Merge in reverse order + while (m > 0 && n > 0) { + if (nums1[m - 1] > nums2[n - 1]) { + nums1[last] = nums1[m - 1]; + m--; + } else { + nums1[last] = nums2[n - 1]; + n--; + } + last--; + } + + // Fill nums1 with leftover nums2 elements + while (n > 0) { + nums1[last] = nums2[n - 1]; + n--; + last--; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number} m + * @param {number[]} nums2 + * @param {number} n + * @return {void} Do not return anything, modify nums1 in-place instead. + */ + merge(nums1, m, nums2, n) { + let last = m + n - 1; + + // Merge in reverse order + while (m > 0 && n > 0) { + if (nums1[m - 1] > nums2[n - 1]) { + nums1[last--] = nums1[m-- - 1]; + } else { + nums1[last--] = nums2[n-- - 1]; + } + } + + // Fill nums1 with leftover nums2 elements + while (n > 0) { + nums1[last--] = nums2[n-- - 1]; + } + } +} +``` + +```csharp +public class Solution { + public void Merge(int[] nums1, int m, int[] nums2, int n) { + int last = m + n - 1; + + // Merge in reverse order + while (m > 0 && n > 0) { + if (nums1[m - 1] > nums2[n - 1]) { + nums1[last] = nums1[m - 1]; + m--; + } else { + nums1[last] = nums2[n - 1]; + n--; + } + last--; + } + + // Fill nums1 with leftover nums2 elements + while (n > 0) { + nums1[last] = nums2[n - 1]; + n--; + last--; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ and $n$ represent the number of elements in the arrays $nums1$ and $nums2$, respectively. + +--- + +## 4. Three Pointers Without Extra Space - II + +::tabs-start + +```python +class Solution: + def merge(self, nums1: list[int], m: int, nums2: list[int], n: int) -> None: + """ + Do not return anything, modify nums1 in-place instead. + """ + last = m + n - 1 + i, j = m - 1, n - 1 + + while j >= 0: + if i >= 0 and nums1[i] > nums2[j]: + nums1[last] = nums1[i] + i -= 1 + else: + nums1[last] = nums2[j] + j -= 1 + + last -= 1 +``` + +```java +public class Solution { + public void merge(int[] nums1, int m, int[] nums2, int n) { + int last = m + n - 1; + int i = m - 1, j = n - 1; + + while (j >= 0) { + if (i >= 0 && nums1[i] > nums2[j]) { + nums1[last--] = nums1[i--]; + } else { + nums1[last--] = nums2[j--]; + } + } + } +} +``` + +```cpp +class Solution { +public: + void merge(vector& nums1, int m, vector& nums2, int n) { + int last = m + n - 1; + int i = m - 1, j = n - 1; + + while (j >= 0) { + if (i >= 0 && nums1[i] > nums2[j]) { + nums1[last--] = nums1[i--]; + } else { + nums1[last--] = nums2[j--]; + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number} m + * @param {number[]} nums2 + * @param {number} n + * @return {void} Do not return anything, modify nums1 in-place instead. + */ + merge(nums1, m, nums2, n) { + let last = m + n - 1; + let i = m - 1, j = n - 1; + + while (j >= 0) { + if (i >= 0 && nums1[i] > nums2[j]) { + nums1[last--] = nums1[i--]; + } else { + nums1[last--] = nums2[j--]; + } + } + } +} +``` + +```csharp +public class Solution { + public void Merge(int[] nums1, int m, int[] nums2, int n) { + int last = m + n - 1; + int i = m - 1, j = n - 1; + + while (j >= 0) { + if (i >= 0 && nums1[i] > nums2[j]) { + nums1[last--] = nums1[i--]; + } else { + nums1[last--] = nums2[j--]; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ and $n$ represent the number of elements in the arrays $nums1$ and $nums2$, respectively. \ No newline at end of file diff --git a/articles/merge-strings-alternately.md b/articles/merge-strings-alternately.md new file mode 100644 index 000000000..d61a657cd --- /dev/null +++ b/articles/merge-strings-alternately.md @@ -0,0 +1,314 @@ +## 1. Two Pointers - I + +::tabs-start + +```python +class Solution: + def mergeAlternately(self, word1: str, word2: str) -> str: + i, j = 0, 0 + res = [] + while i < len(word1) and j < len(word2): + res.append(word1[i]) + res.append(word2[j]) + i += 1 + j += 1 + res.append(word1[i:]) + res.append(word2[j:]) + return "".join(res) +``` + +```java +public class Solution { + public String mergeAlternately(String word1, String word2) { + StringBuilder res = new StringBuilder(); + int i = 0, j = 0; + while (i < word1.length() && j < word2.length()) { + res.append(word1.charAt(i++)); + res.append(word2.charAt(j++)); + } + res.append(word1.substring(i)); + res.append(word2.substring(j)); + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string mergeAlternately(string word1, string word2) { + string res; + int i = 0, j = 0; + while (i < word1.size() && j < word2.size()) { + res += word1[i++]; + res += word2[j++]; + } + res += word1.substr(i); + res += word2.substr(j); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} word1 + * @param {string} word2 + * @return {string} + */ + mergeAlternately(word1, word2) { + let res = []; + let i = 0, j = 0; + while (i < word1.length && j < word2.length) { + res.push(word1[i++], word2[j++]); + } + res.push(word1.slice(i)); + res.push(word2.slice(j)); + return res.join(""); + } +} +``` + +```csharp +public class Solution { + public string MergeAlternately(string word1, string word2) { + int i = 0, j = 0; + StringBuilder res = new StringBuilder(); + + while (i < word1.Length && j < word2.Length) { + res.Append(word1[i]); + res.Append(word2[j]); + i++; + j++; + } + + res.Append(word1.Substring(i)); + res.Append(word2.Substring(j)); + + return res.ToString(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ for the output string. + +> Where $n$ and $m$ are the lengths of the strings $word1$ and $word2$ respectively. + +--- + +## 2. Two Pointers - II + +::tabs-start + +```python +class Solution: + def mergeAlternately(self, word1: str, word2: str) -> str: + n, m = len(word1), len(word2) + res = [] + i = j = 0 + while i < n or j < m: + if i < n: + res.append(word1[i]) + if j < m: + res.append(word2[j]) + i += 1 + j += 1 + return "".join(res) +``` + +```java +public class Solution { + public String mergeAlternately(String word1, String word2) { + int n = word1.length(), m = word2.length(); + StringBuilder res = new StringBuilder(); + int i = 0, j = 0; + while (i < n || j < m) { + if (i < n) res.append(word1.charAt(i++)); + if (j < m) res.append(word2.charAt(j++)); + } + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string mergeAlternately(string word1, string word2) { + int n = word1.size(), m = word2.size(); + string res; + int i = 0, j = 0; + while (i < n || j < m) { + if (i < n) res += word1[i++]; + if (j < m) res += word2[j++]; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} word1 + * @param {string} word2 + * @return {string} + */ + mergeAlternately(word1, word2) { + const n = word1.length, m = word2.length; + const res = []; + let i = 0, j = 0; + while (i < n || j < m) { + if (i < n) res.push(word1[i++]); + if (j < m) res.push(word2[j++]); + } + return res.join(""); + } +} +``` + +```csharp +public class Solution { + public string MergeAlternately(string word1, string word2) { + int n = word1.Length, m = word2.Length; + int i = 0, j = 0; + StringBuilder res = new StringBuilder(); + + while (i < n || j < m) { + if (i < n) { + res.Append(word1[i]); + } + if (j < m) { + res.Append(word2[j]); + } + i++; + j++; + } + + return res.ToString(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ for the output string. + +> Where $n$ and $m$ are the lengths of the strings $word1$ and $word2$ respectively. + +--- + +## 3. One Pointer + +::tabs-start + +```python +class Solution: + def mergeAlternately(self, word1: str, word2: str) -> str: + n, m = len(word1), len(word2) + res = [] + for i in range(max(m, n)): + if i < n: + res.append(word1[i]) + if i < m: + res.append(word2[i]) + return "".join(res) +``` + +```java +public class Solution { + public String mergeAlternately(String word1, String word2) { + int n = word1.length(), m = word2.length(); + StringBuilder res = new StringBuilder(); + for (int i = 0; i < n || i < m; i++) { + if (i < n) { + res.append(word1.charAt(i)); + } + if (i < m) { + res.append(word2.charAt(i)); + } + } + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string mergeAlternately(string word1, string word2) { + int n = word1.size(), m = word2.size(); + string res; + for (int i = 0; i < n || i < m; i++) { + if (i < n) { + res += word1[i]; + } + if (i < m) { + res += word2[i]; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} word1 + * @param {string} word2 + * @return {string} + */ + mergeAlternately(word1, word2) { + const n = word1.length, m = word2.length; + const res = []; + for (let i = 0; i < m || i < n; i++) { + if (i < n) { + res.push(word1.charAt(i)); + } + if (i < m) { + res.push(word2.charAt(i)); + } + } + return res.join(""); + } +} +``` + +```csharp +public class Solution { + public string MergeAlternately(string word1, string word2) { + int n = word1.Length, m = word2.Length; + StringBuilder res = new StringBuilder(); + + for (int i = 0; i < Math.Max(n, m); i++) { + if (i < n) { + res.Append(word1[i]); + } + if (i < m) { + res.Append(word2[i]); + } + } + + return res.ToString(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ for the output string. + +> Where $n$ and $m$ are the lengths of the strings $word1$ and $word2$ respectively. \ No newline at end of file diff --git a/articles/merge-triplets-to-form-target.md b/articles/merge-triplets-to-form-target.md new file mode 100644 index 000000000..268d8ae9d --- /dev/null +++ b/articles/merge-triplets-to-form-target.md @@ -0,0 +1,319 @@ +## 1. Greedy + +::tabs-start + +```python +class Solution: + def mergeTriplets(self, triplets: List[List[int]], target: List[int]) -> bool: + good = set() + + for t in triplets: + if t[0] > target[0] or t[1] > target[1] or t[2] > target[2]: + continue + for i, v in enumerate(t): + if v == target[i]: + good.add(i) + return len(good) == 3 +``` + +```java +public class Solution { + public boolean mergeTriplets(int[][] triplets, int[] target) { + Set good = new HashSet<>(); + + for (int[] t : triplets) { + if (t[0] > target[0] || t[1] > target[1] || t[2] > target[2]) { + continue; + } + for (int i = 0; i < t.length; i++) { + if (t[i] == target[i]) { + good.add(i); + } + } + } + return good.size() == 3; + } +} +``` + +```cpp +class Solution { +public: + bool mergeTriplets(vector>& triplets, vector& target) { + unordered_set good; + + for (const auto& t : triplets) { + if (t[0] > target[0] || t[1] > target[1] || t[2] > target[2]) { + continue; + } + for (int i = 0; i < t.size(); i++) { + if (t[i] == target[i]) { + good.insert(i); + } + } + } + return good.size() == 3; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} triplets + * @param {number[]} target + * @return {boolean} + */ + mergeTriplets(triplets, target) { + const good = new Set(); + + for (const t of triplets) { + if (t[0] > target[0] || t[1] > target[1] || t[2] > target[2]) { + continue; + } + for (let i = 0; i < t.length; i++) { + if (t[i] === target[i]) { + good.add(i); + } + } + } + return good.size === 3; + } +} +``` + +```csharp +public class Solution { + public bool MergeTriplets(int[][] triplets, int[] target) { + HashSet good = new HashSet(); + + foreach (var t in triplets) { + if (t[0] > target[0] || t[1] > target[1] || t[2] > target[2]) { + continue; + } + for (int i = 0; i < t.Length; i++) { + if (t[i] == target[i]) { + good.Add(i); + } + } + } + return good.Count == 3; + } +} +``` + +```go +func mergeTriplets(triplets [][]int, target []int) bool { + good := make(map[int]bool) + + for _, t := range triplets { + if t[0] > target[0] || t[1] > target[1] || t[2] > target[2] { + continue + } + for i, v := range t { + if v == target[i] { + good[i] = true + } + } + } + return len(good) == 3 +} +``` + +```kotlin +class Solution { + fun mergeTriplets(triplets: Array, target: IntArray): Boolean { + val good = HashSet() + + for (t in triplets) { + if (t[0] > target[0] || t[1] > target[1] || t[2] > target[2]) continue + for ((i, v) in t.withIndex()) { + if (v == target[i]) { + good.add(i) + } + } + } + return good.size == 3 + } +} +``` + +```swift +class Solution { + func mergeTriplets(_ triplets: [[Int]], _ target: [Int]) -> Bool { + var good = Set() + + for t in triplets { + if t[0] > target[0] || t[1] > target[1] || t[2] > target[2] { + continue + } + for (i, v) in t.enumerated() { + if v == target[i] { + good.insert(i) + } + } + } + + return good.count == 3 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Greedy (Optimal) + +::tabs-start + +```python +class Solution: + def mergeTriplets(self, triplets: List[List[int]], target: List[int]) -> bool: + x = y = z = False + for t in triplets: + x |= (t[0] == target[0] and t[1] <= target[1] and t[2] <= target[2]) + y |= (t[0] <= target[0] and t[1] == target[1] and t[2] <= target[2]) + z |= (t[0] <= target[0] and t[1] <= target[1] and t[2] == target[2]) + if x and y and z: + return True + return False +``` + +```java +public class Solution { + public boolean mergeTriplets(int[][] triplets, int[] target) { + boolean x = false, y = false, z = false; + for (int[] t : triplets) { + x |= (t[0] == target[0] && t[1] <= target[1] && t[2] <= target[2]); + y |= (t[0] <= target[0] && t[1] == target[1] && t[2] <= target[2]); + z |= (t[0] <= target[0] && t[1] <= target[1] && t[2] == target[2]); + if (x && y && z) { + return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool mergeTriplets(vector>& triplets, vector& target) { + bool x = false, y = false, z = false; + for (const auto& t : triplets) { + x |= (t[0] == target[0] && t[1] <= target[1] && t[2] <= target[2]); + y |= (t[0] <= target[0] && t[1] == target[1] && t[2] <= target[2]); + z |= (t[0] <= target[0] && t[1] <= target[1] && t[2] == target[2]); + if (x && y && z) return true; + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} triplets + * @param {number[]} target + * @return {boolean} + */ + mergeTriplets(triplets, target) { + let x = false, y = false, z = false; + for (let t of triplets) { + x |= (t[0] === target[0] && t[1] <= target[1] && t[2] <= target[2]); + y |= (t[0] <= target[0] && t[1] === target[1] && t[2] <= target[2]); + z |= (t[0] <= target[0] && t[1] <= target[1] && t[2] === target[2]); + if (x && y && z) return true; + } + return false; + } +} +``` + +```csharp +public class Solution { + public bool MergeTriplets(int[][] triplets, int[] target) { + bool x = false, y = false, z = false; + foreach (var t in triplets) { + x |= (t[0] == target[0] && t[1] <= target[1] && t[2] <= target[2]); + y |= (t[0] <= target[0] && t[1] == target[1] && t[2] <= target[2]); + z |= (t[0] <= target[0] && t[1] <= target[1] && t[2] == target[2]); + if (x && y && z) return true; + } + return false; + } +} +``` + +```go +func mergeTriplets(triplets [][]int, target []int) bool { + x, y, z := false, false, false + + for _, t := range triplets { + x = x || (t[0] == target[0] && t[1] <= target[1] && t[2] <= target[2]) + y = y || (t[0] <= target[0] && t[1] == target[1] && t[2] <= target[2]) + z = z || (t[0] <= target[0] && t[1] <= target[1] && t[2] == target[2]) + + if x && y && z { + return true + } + } + return false +} +``` + +```kotlin +class Solution { + fun mergeTriplets(triplets: Array, target: IntArray): Boolean { + var x = false + var y = false + var z = false + + for (t in triplets) { + x = x || (t[0] == target[0] && t[1] <= target[1] && t[2] <= target[2]) + y = y || (t[0] <= target[0] && t[1] == target[1] && t[2] <= target[2]) + z = z || (t[0] <= target[0] && t[1] <= target[1] && t[2] == target[2]) + + if (x && y && z) return true + } + return false + } +} +``` + +```swift +class Solution { + func mergeTriplets(_ triplets: [[Int]], _ target: [Int]) -> Bool { + var x = false, y = false, z = false + + for t in triplets { + if t[0] <= target[0] && t[1] <= target[1] && t[2] <= target[2] { + if t[0] == target[0] { x = true } + if t[1] == target[1] { y = true } + if t[2] == target[2] { z = true } + } + + if x && y && z { + return true + } + } + + return false + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/merge-two-binary-trees.md b/articles/merge-two-binary-trees.md new file mode 100644 index 000000000..0b7636434 --- /dev/null +++ b/articles/merge-two-binary-trees.md @@ -0,0 +1,658 @@ +## 1. Depth First Search (Creating New Tree) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]: + if not root1 and not root2: + return None + + v1 = root1.val if root1 else 0 + v2 = root2.val if root2 else 0 + root = TreeNode(v1 + v2) + + root.left = self.mergeTrees(root1.left if root1 else None, root2.left if root2 else None) + root.right = self.mergeTrees(root1.right if root1 else None, root2.right if root2 else None) + + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode mergeTrees(TreeNode root1, TreeNode root2) { + if (root1 == null && root2 == null) { + return null; + } + + int v1 = (root1 != null) ? root1.val : 0; + int v2 = (root2 != null) ? root2.val : 0; + TreeNode root = new TreeNode(v1 + v2); + + root.left = mergeTrees( + (root1 != null) ? root1.left : null, (root2 != null) ? root2.left : null + ); + root.right = mergeTrees( + (root1 != null) ? root1.right : null, (root2 != null) ? root2.right : null + ); + + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) { + if (!root1 && !root2) { + return nullptr; + } + + int v1 = root1 ? root1->val : 0; + int v2 = root2 ? root2->val : 0; + TreeNode* root = new TreeNode(v1 + v2); + + root->left = mergeTrees(root1 ? root1->left : nullptr, root2 ? root2->left : nullptr); + root->right = mergeTrees(root1 ? root1->right : nullptr, root2 ? root2->right : nullptr); + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {TreeNode} + */ + mergeTrees(root1, root2) { + if (!root1 && !root2) { + return null; + } + + const v1 = root1 ? root1.val : 0; + const v2 = root2 ? root2.val : 0; + const root = new TreeNode(v1 + v2); + + root.left = this.mergeTrees(root1 ? root1.left : null, root2 ? root2.left : null); + root.right = this.mergeTrees(root1 ? root1.right : null, root2 ? root2.right : null); + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: + * $O(m + n)$ space for recursion stack. + * $O(m + n)$ space for the output. + +> Where $m$ and $n$ are the number of nodes in the given trees. + +--- + +## 2. Depth First Search (In Place) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]: + if not root1: + return root2 + if not root2: + return root1 + + root1.val += root2.val + root1.left = self.mergeTrees(root1.left, root2.left) + root1.right = self.mergeTrees(root1.right, root2.right) + return root1 +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode mergeTrees(TreeNode root1, TreeNode root2) { + if (root1 == null) return root2; + if (root2 == null) return root1; + + root1.val += root2.val; + root1.left = mergeTrees(root1.left, root2.left); + root1.right = mergeTrees(root1.right, root2.right); + return root1; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) { + if (!root1) return root2; + if (!root2) return root1; + + root1->val += root2->val; + root1->left = mergeTrees(root1->left, root2->left); + root1->right = mergeTrees(root1->right, root2->right); + return root1; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {TreeNode} + */ + mergeTrees(root1, root2) { + if (!root1) return root2; + if (!root2) return root1; + + root1.val += root2.val; + root1.left = this.mergeTrees(root1.left, root2.left); + root1.right = this.mergeTrees(root1.right, root2.right); + return root1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(min(m, n))$ +* Space complexity: $O(min(m, n))$ for recursion stack. + +> Where $m$ and $n$ are the number of nodes in the given trees. + +--- + +## 3. Iterative DFS (Creating New Tree) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]: + if not root1: + return root2 + if not root2: + return root1 + + root = TreeNode(root1.val + root2.val) + stack = [(root1, root2, root)] + + while stack: + node1, node2, node = stack.pop() + + if node1.left and node2.left: + node.left = TreeNode(node1.left.val + node2.left.val) + stack.append((node1.left, node2.left, node.left)) + elif not node1.left: + node.left = node2.left + else: + node.left = node1.left + + if node1.right and node2.right: + node.right = TreeNode(node1.right.val + node2.right.val) + stack.append((node1.right, node2.right, node.right)) + elif not node1.right: + node.right = node2.right + else: + node.right = node1.right + + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode mergeTrees(TreeNode root1, TreeNode root2) { + if (root1 == null && root2 == null) return null; + + int val = (root1 != null ? root1.val : 0) + (root2 != null ? root2.val : 0); + TreeNode root = new TreeNode(val); + Stack stack = new Stack<>(); + stack.push(new TreeNode[]{root1, root2, root}); + + while (!stack.isEmpty()) { + TreeNode[] nodes = stack.pop(); + TreeNode node1 = nodes[0], node2 = nodes[1], node = nodes[2]; + + TreeNode left1 = node1 != null ? node1.left : null; + TreeNode left2 = node2 != null ? node2.left : null; + if (left1 != null || left2 != null) { + int leftVal = (left1 != null ? left1.val : 0) + (left2 != null ? left2.val : 0); + node.left = new TreeNode(leftVal); + stack.push(new TreeNode[]{left1, left2, node.left}); + } + + TreeNode right1 = node1 != null ? node1.right : null; + TreeNode right2 = node2 != null ? node2.right : null; + if (right1 != null || right2 != null) { + int rightVal = (right1 != null ? right1.val : 0) + (right2 != null ? right2.val : 0); + node.right = new TreeNode(rightVal); + stack.push(new TreeNode[]{right1, right2, node.right}); + } + } + + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) { + if (!root1 && !root2) return nullptr; + + int val = (root1 ? root1->val : 0) + (root2 ? root2->val : 0); + TreeNode* root = new TreeNode(val); + stack> st; + st.push({root1, root2, root}); + + while (!st.empty()) { + auto [node1, node2, node] = st.top(); + st.pop(); + + TreeNode* left1 = node1 ? node1->left : nullptr; + TreeNode* left2 = node2 ? node2->left : nullptr; + if (left1 || left2) { + int leftVal = (left1 ? left1->val : 0) + (left2 ? left2->val : 0); + node->left = new TreeNode(leftVal); + st.push({left1, left2, node->left}); + } + + TreeNode* right1 = node1 ? node1->right : nullptr; + TreeNode* right2 = node2 ? node2->right : nullptr; + if (right1 || right2) { + int rightVal = (right1 ? right1->val : 0) + (right2 ? right2->val : 0); + node->right = new TreeNode(rightVal); + st.push({right1, right2, node->right}); + } + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {TreeNode} + */ + mergeTrees(root1, root2) { + if (!root1 && !root2) return null; + + let val = (root1 ? root1.val : 0) + (root2 ? root2.val : 0); + let root = new TreeNode(val); + let stack = [[root1, root2, root]]; + + while (stack.length) { + let [node1, node2, node] = stack.pop(); + + let left1 = node1 ? node1.left : null; + let left2 = node2 ? node2.left : null; + if (left1 || left2) { + let leftVal = (left1 ? left1.val : 0) + (left2 ? left2.val : 0); + node.left = new TreeNode(leftVal); + stack.push([left1, left2, node.left]); + } + + let right1 = node1 ? node1.right : null; + let right2 = node2 ? node2.right : null; + if (right1 || right2) { + let rightVal = (right1 ? right1.val : 0) + (right2 ? right2.val : 0); + node.right = new TreeNode(rightVal); + stack.push([right1, right2, node.right]); + } + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: + * $O(m + n)$ space for the stack. + * $O(m + n)$ space for the output. + +> Where $m$ and $n$ are the number of nodes in the given trees. + +--- + +## 4. Iterative DFS (In Place) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]: + if not root1: + return root2 + if not root2: + return root1 + + stack = [(root1, root2)] + + while stack: + node1, node2 = stack.pop() + if not node1 or not node2: + continue + + node1.val += node2.val + + if node1.left and node2.left: + stack.append((node1.left, node2.left)) + elif not node1.left: + node1.left = node2.left + + if node1.right and node2.right: + stack.append((node1.right, node2.right)) + elif not node1.right: + node1.right = node2.right + + return root1 +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode mergeTrees(TreeNode root1, TreeNode root2) { + if (root1 == null) return root2; + if (root2 == null) return root1; + + Stack stack = new Stack<>(); + stack.push(new TreeNode[] { root1, root2 }); + + while (!stack.isEmpty()) { + TreeNode[] nodes = stack.pop(); + TreeNode node1 = nodes[0]; + TreeNode node2 = nodes[1]; + + if (node2 == null) continue; + + node1.val += node2.val; + + if (node1.left == null) { + node1.left = node2.left; + } else { + stack.push(new TreeNode[] { node1.left, node2.left }); + } + + if (node1.right == null) { + node1.right = node2.right; + } else { + stack.push(new TreeNode[] { node1.right, node2.right }); + } + } + + return root1; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) { + if (!root1) return root2; + if (!root2) return root1; + + stack> stk; + stk.push({root1, root2}); + + while (!stk.empty()) { + auto [node1, node2] = stk.top(); + stk.pop(); + + if (!node2) continue; + + node1->val += node2->val; + + if (!node1->left) { + node1->left = node2->left; + } else { + stk.push({node1->left, node2->left}); + } + + if (!node1->right) { + node1->right = node2->right; + } else { + stk.push({node1->right, node2->right}); + } + } + + return root1; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {TreeNode} + */ + mergeTrees(root1, root2) { + if (!root1) return root2; + if (!root2) return root1; + + let stack = [[root1, root2]]; + + while (stack.length) { + let [node1, node2] = stack.pop(); + if (!node1 || !node2) continue; + + node1.val += node2.val; + + if (node1.left && node2.left) { + stack.push([node1.left, node2.left]); + } else if (!node1.left) { + node1.left = node2.left; + } + + if (node1.right && node2.right) { + stack.push([node1.right, node2.right]); + } else if (!node1.right) { + node1.right = node2.right; + } + } + + return root1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(min(m, n))$ +* Space complexity: $O(min(m, n))$ for the stack. + +> Where $m$ and $n$ are the number of nodes in the given trees. \ No newline at end of file diff --git a/articles/merge-two-sorted-linked-lists.md b/articles/merge-two-sorted-linked-lists.md new file mode 100644 index 000000000..b65407ade --- /dev/null +++ b/articles/merge-two-sorted-linked-lists.md @@ -0,0 +1,549 @@ +## 1. Recursion + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + +class Solution: + def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]: + if list1 is None: + return list2 + if list2 is None: + return list1 + if list1.val <= list2.val: + list1.next = self.mergeTwoLists(list1.next, list2) + return list1 + else: + list2.next = self.mergeTwoLists(list1, list2.next) + return list2 +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +public class Solution { + public ListNode mergeTwoLists(ListNode list1, ListNode list2) { + if (list1 == null) { + return list2; + } + if (list2 == null) { + return list1; + } + if (list1.val <= list2.val) { + list1.next = mergeTwoLists(list1.next, list2); + return list1; + } else { + list2.next = mergeTwoLists(list1, list2.next); + return list2; + } + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +class Solution { +public: + ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) { + if (!list1) { + return list2; + } + if (!list2) { + return list1; + } + if (list1->val <= list2->val) { + list1->next = mergeTwoLists(list1->next, list2); + return list1; + } else { + list2->next = mergeTwoLists(list1, list2->next); + return list2; + } + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {ListNode} list1 + * @param {ListNode} list2 + * @return {ListNode} + */ + mergeTwoLists(list1, list2) { + if (!list1) { + return list2; + } + if (!list2) { + return list1; + } + if (list1.val <= list2.val) { + list1.next = this.mergeTwoLists(list1.next, list2); + return list1; + } else { + list2.next = this.mergeTwoLists(list1, list2.next); + return list2; + } + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +public class Solution { + public ListNode MergeTwoLists(ListNode list1, ListNode list2) { + if (list1 == null) { + return list2; + } + if (list2 == null) { + return list1; + } + if (list1.val <= list2.val) { + list1.next = MergeTwoLists(list1.next, list2); + return list1; + } else { + list2.next = MergeTwoLists(list1, list2.next); + return list2; + } + } +} +``` + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func mergeTwoLists(list1 *ListNode, list2 *ListNode) *ListNode { + if list1 == nil { + return list2 + } + if list2 == nil { + return list1 + } + if list1.Val <= list2.Val { + list1.Next = mergeTwoLists(list1.Next, list2) + return list1 + } + list2.Next = mergeTwoLists(list1, list2.Next) + return list2 +} +``` + +```kotlin +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun mergeTwoLists(list1: ListNode?, list2: ListNode?): ListNode? { + if (list1 == null) return list2 + if (list2 == null) return list1 + + return if (list1.`val` <= list2.`val`) { + list1.next = mergeTwoLists(list1.next, list2) + list1 + } else { + list2.next = mergeTwoLists(list1, list2.next) + list2 + } + } +} +``` + +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func mergeTwoLists(_ list1: ListNode?, _ list2: ListNode?) -> ListNode? { + if list1 == nil { + return list2 + } + if list2 == nil { + return list1 + } + if list1!.val <= list2!.val { + list1!.next = mergeTwoLists(list1!.next, list2) + return list1 + } else { + list2!.next = mergeTwoLists(list1, list2!.next) + return list2 + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the length of $list1$ and $m$ is the length of $list2$. + +--- + +## 2. Iteration + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + +class Solution: + def mergeTwoLists(self, list1: ListNode, list2: ListNode) -> ListNode: + dummy = node = ListNode() + + while list1 and list2: + if list1.val < list2.val: + node.next = list1 + list1 = list1.next + else: + node.next = list2 + list2 = list2.next + node = node.next + + node.next = list1 or list2 + + return dummy.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +public class Solution { + public ListNode mergeTwoLists(ListNode list1, ListNode list2) { + ListNode dummy = new ListNode(0); + ListNode node = dummy; + + while (list1 != null && list2 != null) { + if (list1.val < list2.val) { + node.next = list1; + list1 = list1.next; + } else { + node.next = list2; + list2 = list2.next; + } + node = node.next; + } + + if (list1 != null) { + node.next = list1; + } else { + node.next = list2; + } + + return dummy.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +class Solution { +public: + ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) { + ListNode dummy(0); + ListNode* node = &dummy; + + while (list1 && list2) { + if (list1->val < list2->val) { + node->next = list1; + list1 = list1->next; + } else { + node->next = list2; + list2 = list2->next; + } + node = node->next; + } + + if (list1) { + node->next = list1; + } else { + node->next = list2; + } + + return dummy.next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {ListNode} list1 + * @param {ListNode} list2 + * @return {ListNode} + */ + mergeTwoLists(list1, list2) { + const dummy = { val: 0, next: null }; + let node = dummy; + + while (list1 && list2) { + if (list1.val < list2.val) { + node.next = list1; + list1 = list1.next; + } else { + node.next = list2; + list2 = list2.next; + } + node = node.next; + } + + if (list1) { + node.next = list1; + } else { + node.next = list2; + } + + return dummy.next; + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +public class Solution { + public ListNode MergeTwoLists(ListNode list1, ListNode list2) { + ListNode dummy = new ListNode(0); + ListNode node = dummy; + + while (list1 != null && list2 != null) { + if (list1.val < list2.val) { + node.next = list1; + list1 = list1.next; + } else { + node.next = list2; + list2 = list2.next; + } + node = node.next; + } + + if (list1 != null) { + node.next = list1; + } else { + node.next = list2; + } + + return dummy.next; + } +} +``` + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func mergeTwoLists(list1 *ListNode, list2 *ListNode) *ListNode { + dummy := &ListNode{} + node := dummy + + for list1 != nil && list2 != nil { + if list1.Val < list2.Val { + node.Next = list1 + list1 = list1.Next + } else { + node.Next = list2 + list2 = list2.Next + } + node = node.Next + } + + node.Next = list1 + if list1 == nil { + node.Next = list2 + } + + return dummy.Next +} +``` + +```kotlin +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun mergeTwoLists(list1: ListNode?, list2: ListNode?): ListNode? { + val dummy = ListNode(0) + var node: ListNode? = dummy + + var l1 = list1 + var l2 = list2 + + while (l1 != null && l2 != null) { + if (l1.`val` < l2.`val`) { + node?.next = l1 + l1 = l1.next + } else { + node?.next = l2 + l2 = l2.next + } + node = node?.next + } + + node?.next = l1 ?: l2 + + return dummy.next + } +} +``` + +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func mergeTwoLists(_ list1: ListNode?, _ list2: ListNode?) -> ListNode? { + let dummy = ListNode(0) + var node = dummy + var l1 = list1 + var l2 = list2 + + while l1 != nil && l2 != nil { + if l1!.val < l2!.val { + node.next = l1 + l1 = l1?.next + } else { + node.next = l2 + l2 = l2?.next + } + node = node.next! + } + + node.next = l1 ?? l2 + + return dummy.next + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(1)$ + +> Where $n$ is the length of $list1$ and $m$ is the length of $list2$. \ No newline at end of file diff --git a/articles/middle-of-the-linked-list.md b/articles/middle-of-the-linked-list.md new file mode 100644 index 000000000..935efc3d4 --- /dev/null +++ b/articles/middle-of-the-linked-list.md @@ -0,0 +1,339 @@ +## 1. Convert To Array + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]: + cur = head + arr = [] + while cur: + arr.append(cur) + cur = cur.next + return arr[len(arr) // 2] +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode middleNode(ListNode head) { + ArrayList arr = new ArrayList<>(); + ListNode cur = head; + while (cur != null) { + arr.add(cur); + cur = cur.next; + } + return arr.get(arr.size() / 2); + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* middleNode(ListNode* head) { + vector arr; + ListNode* cur = head; + while (cur) { + arr.push_back(cur); + cur = cur->next; + } + return arr[arr.size() / 2]; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + middleNode(head) { + let arr = []; + let cur = head; + while (cur) { + arr.push(cur); + cur = cur.next; + } + return arr[Math.floor(arr.length / 2)]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Find Length of the List + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]: + n, cur = 0, head + while cur: + cur = cur.next + n += 1 + + n //= 2 + cur = head + while n: + n -= 1 + cur = cur.next + return cur +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode middleNode(ListNode head) { + int n = 0; + ListNode cur = head; + while (cur != null) { + cur = cur.next; + n++; + } + + n /= 2; + cur = head; + while (n != 0) { + n--; + cur = cur.next; + } + return cur; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* middleNode(ListNode* head) { + int n = 0; + ListNode* cur = head; + while (cur) { + cur = cur->next; + n++; + } + + n /= 2; + cur = head; + while (n) { + n--; + cur = cur->next; + } + return cur; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + middleNode(head) { + let n = 0; + let cur = head; + while (cur) { + cur = cur.next; + n++; + } + + n = Math.floor(n / 2); + cur = head; + while (n) { + n--; + cur = cur.next; + } + return cur; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Fast & Slow Pointers + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]: + slow, fast = head, head + + while fast and fast.next: + slow = slow.next + fast = fast.next.next + return slow +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode middleNode(ListNode head) { + ListNode slow = head, fast = head; + + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + } + return slow; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* middleNode(ListNode* head) { + ListNode* slow = head; + ListNode* fast = head; + + while (fast && fast->next) { + slow = slow->next; + fast = fast->next->next; + } + return slow; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + middleNode(head) { + let slow = head, fast = head; + + while (fast && fast.next) { + slow = slow.next; + fast = fast.next.next; + } + return slow; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/min-cost-climbing-stairs.md b/articles/min-cost-climbing-stairs.md new file mode 100644 index 000000000..9433d007b --- /dev/null +++ b/articles/min-cost-climbing-stairs.md @@ -0,0 +1,594 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def minCostClimbingStairs(self, cost: List[int]) -> int: + + def dfs(i): + if i >= len(cost): + return 0 + return cost[i] + min(dfs(i + 1), dfs(i + 2)) + + return min(dfs(0), dfs(1)) +``` + +```java +public class Solution { + public int minCostClimbingStairs(int[] cost) { + + return Math.min(dfs(cost, 0), dfs(cost, 1)); + } + + private int dfs(int[] cost, int i) { + if (i >= cost.length) { + return 0; + } + return cost[i] + Math.min(dfs(cost, i + 1), + dfs(cost, i + 2)); + } +} +``` + +```cpp +class Solution { +public: + int minCostClimbingStairs(vector& cost) { + return min(dfs(cost, 0), dfs(cost, 1)); + } + + int dfs(vector& cost, int i) { + if (i >= cost.size()) { + return 0; + } + return cost[i] + min(dfs(cost, i + 1), + dfs(cost, i + 2)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} cost + * @return {number} + */ + minCostClimbingStairs(cost) { + const dfs = (i) => { + if (i >= cost.length) { + return 0; + } + return cost[i] + Math.min(dfs(i + 1), dfs(i + 2)); + } + return Math.min(dfs(0), dfs(1)); + } +} +``` + +```csharp +public class Solution { + public int MinCostClimbingStairs(int[] cost) { + return Math.Min(Dfs(cost, 0), Dfs(cost, 1)); + } + + private int Dfs(int[] cost, int i) { + if (i >= cost.Length) { + return 0; + } + return cost[i] + Math.Min(Dfs(cost, i + 1), + Dfs(cost, i + 2)); + } +} +``` + +```go +func minCostClimbingStairs(cost []int) int { + var dfs func(i int) int + dfs = func(i int) int { + if i >= len(cost) { + return 0 + } + return cost[i] + min(dfs(i+1), dfs(i+2)) + } + + return min(dfs(0), dfs(1)) +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun minCostClimbingStairs(cost: IntArray): Int { + fun dfs(i: Int): Int { + if (i >= cost.size) { + return 0 + } + return cost[i] + minOf(dfs(i + 1), dfs(i + 2)) + } + + return minOf(dfs(0), dfs(1)) + } +} +``` + +```swift +class Solution { + func minCostClimbingStairs(_ cost: [Int]) -> Int { + func dfs(_ i: Int) -> Int { + if i >= cost.count { + return 0 + } + return cost[i] + min(dfs(i + 1), dfs(i + 2)) + } + + return min(dfs(0), dfs(1)) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minCostClimbingStairs(self, cost: List[int]) -> int: + memo = [-1] * len(cost) + + def dfs(i): + if i >= len(cost): + return 0 + if memo[i] != -1: + return memo[i] + memo[i] = cost[i] + min(dfs(i + 1), dfs(i + 2)) + return memo[i] + + return min(dfs(0), dfs(1)) +``` + +```java +public class Solution { + int[] memo; + + public int minCostClimbingStairs(int[] cost) { + memo = new int[cost.length]; + Arrays.fill(memo, -1); + return Math.min(dfs(cost, 0), dfs(cost, 1)); + } + + private int dfs(int[] cost, int i) { + if (i >= cost.length) { + return 0; + } + if (memo[i] != -1) { + return memo[i]; + } + memo[i] = cost[i] + Math.min(dfs(cost, i + 1), + dfs(cost, i + 2)); + return memo[i]; + } +} +``` + +```cpp +class Solution { +public: + vector memo; + + int minCostClimbingStairs(vector& cost) { + memo.resize(cost.size(), -1); + return min(dfs(cost, 0), dfs(cost, 1)); + } + + int dfs(vector& cost, int i) { + if (i >= cost.size()) { + return 0; + } + if (memo[i] != -1) { + return memo[i]; + } + memo[i] = cost[i] + min(dfs(cost, i + 1), + dfs(cost, i + 2)); + return memo[i]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} cost + * @return {number} + */ + minCostClimbingStairs(cost) { + const memo = new Int32Array(cost.length).fill(-1); + const dfs = (i) => { + if (i >= cost.length) { + return 0; + } + if (memo[i] !== -1) { + return memo[i]; + } + memo[i] = cost[i] + Math.min(dfs(i + 1), + dfs(i + 2)); + return memo[i]; + } + return Math.min(dfs(0), dfs(1)); + } +} +``` + +```csharp +public class Solution { + int[] memo; + + public int MinCostClimbingStairs(int[] cost) { + memo = new int[cost.Length]; + Array.Fill(memo, -1); + return Math.Min(Dfs(cost, 0), Dfs(cost, 1)); + } + + private int Dfs(int[] cost, int i) { + if (i >= cost.Length) { + return 0; + } + if (memo[i] != -1) { + return memo[i]; + } + memo[i] = cost[i] + Math.Min(Dfs(cost, i + 1), + Dfs(cost, i + 2)); + return memo[i]; + } +} +``` + +```go +func minCostClimbingStairs(cost []int) int { + memo := make([]int, len(cost)) + for i := 0; i < len(cost); i++ { + memo[i] = -1 + } + + var dfs func(i int) int + dfs = func(i int) int { + if i >= len(cost) { + return 0 + } + if memo[i] != -1 { + return memo[i] + } + memo[i] = cost[i] + min(dfs(i+1), dfs(i+2)) + return memo[i] + } + + return min(dfs(0), dfs(1)) +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun minCostClimbingStairs(cost: IntArray): Int { + var memo = IntArray(cost.size){-1} + fun dfs(i: Int): Int { + if (i >= cost.size) { + return 0 + } + if (memo[i] != -1) return memo[i] + memo[i] = cost[i] + minOf(dfs(i + 1), dfs(i + 2)) + return memo[i] + } + + return minOf(dfs(0), dfs(1)) + } +} +``` + +```swift +class Solution { + func minCostClimbingStairs(_ cost: [Int]) -> Int { + var memo = Array(repeating: -1, count: cost.count) + + func dfs(_ i: Int) -> Int { + if i >= cost.count { + return 0 + } + if memo[i] != -1 { + return memo[i] + } + memo[i] = cost[i] + min(dfs(i + 1), dfs(i + 2)) + return memo[i] + } + + return min(dfs(0), dfs(1)) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minCostClimbingStairs(self, cost: List[int]) -> int: + n = len(cost) + dp = [0] * (n + 1) + + for i in range(2, n + 1): + dp[i] = min(dp[i - 1] + cost[i - 1], + dp[i - 2] + cost[i - 2]) + + return dp[n] +``` + +```java +public class Solution { + public int minCostClimbingStairs(int[] cost) { + int n = cost.length; + int[] dp = new int[n + 1]; + + for (int i = 2; i <= n; i++) { + dp[i] = Math.min(dp[i - 1] + cost[i - 1], + dp[i - 2] + cost[i - 2]); + } + + return dp[n]; + } +} +``` + +```cpp +class Solution { +public: + int minCostClimbingStairs(vector& cost) { + int n = cost.size(); + vector dp(n + 1); + + for (int i = 2; i <= n; i++) { + dp[i] = min(dp[i - 1] + cost[i - 1], + dp[i - 2] + cost[i - 2]); + } + + return dp[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} cost + * @return {number} + */ + minCostClimbingStairs(cost) { + const n = cost.length; + const dp = new Array(n + 1).fill(0); + + for (let i = 2; i <= n; i++) { + dp[i] = Math.min(dp[i - 1] + cost[i - 1], + dp[i - 2] + cost[i - 2]); + } + + return dp[n]; + } +} +``` + +```csharp +public class Solution { + public int MinCostClimbingStairs(int[] cost) { + int n = cost.Length; + int[] dp = new int[n + 1]; + + for (int i = 2; i <= n; i++) { + dp[i] = Math.Min(dp[i - 1] + cost[i - 1], + dp[i - 2] + cost[i - 2]); + } + + return dp[n]; + } +} +``` + +```go +func minCostClimbingStairs(cost []int) int { + n := len(cost) + dp := make([]int, n+1) + + for i := 2; i <= n; i++ { + dp[i] = min(dp[i-1] + cost[i-1], + dp[i-2] + cost[i-2]) + } + + return dp[n] +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun minCostClimbingStairs(cost: IntArray): Int { + val n = cost.size + var dp = IntArray(n + 1) + + for (i in 2..n) { + dp[i] = minOf(dp[i - 1] + cost[i - 1], + dp[i - 2] + cost[i - 2]) + } + + return dp[n] + } +} +``` + +```swift +class Solution { + func minCostClimbingStairs(_ cost: [Int]) -> Int { + let n = cost.count + var dp = Array(repeating: 0, count: n + 1) + + for i in 2...n { + dp[i] = min(dp[i - 1] + cost[i - 1], + dp[i - 2] + cost[i - 2]) + } + + return dp[n] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def minCostClimbingStairs(self, cost: List[int]) -> int: + for i in range(len(cost) - 3, -1, -1): + cost[i] += min(cost[i + 1], cost[i + 2]) + + return min(cost[0], cost[1]) +``` + +```java +public class Solution { + public int minCostClimbingStairs(int[] cost) { + for (int i = cost.length - 3; i >= 0; i--) { + cost[i] += Math.min(cost[i + 1], cost[i + 2]); + } + return Math.min(cost[0], cost[1]); + } +} +``` + +```cpp +class Solution { +public: + int minCostClimbingStairs(vector& cost) { + for (int i = cost.size() - 3; i >= 0; i--) { + cost[i] += min(cost[i + 1], cost[i + 2]); + } + return min(cost[0], cost[1]); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} cost + * @return {number} + */ + minCostClimbingStairs(cost) { + for (let i = cost.length - 3; i >= 0; i--) { + cost[i] += Math.min(cost[i + 1], cost[i + 2]); + } + return Math.min(cost[0], cost[1]); + } +} +``` + +```csharp +public class Solution { + public int MinCostClimbingStairs(int[] cost) { + for (int i = cost.Length - 3; i >= 0; i--) { + cost[i] += Math.Min(cost[i + 1], cost[i + 2]); + } + return Math.Min(cost[0], cost[1]); + } +} +``` + +```go +func minCostClimbingStairs(cost []int) int { + n := len(cost) + for i := n - 3; i >= 0; i-- { + cost[i] += min(cost[i+1], cost[i+2]) + } + return min(cost[0], cost[1]) +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun minCostClimbingStairs(cost: IntArray): Int { + val n = cost.size + for (i in n - 3 downTo 0) { + cost[i] += minOf(cost[i + 1], cost[i + 2]) + } + return minOf(cost[0], cost[1]) + } +} +``` + +```swift +class Solution { + func minCostClimbingStairs(_ cost: inout [Int]) -> Int { + for i in stride(from: cost.count - 3, through: 0, by: -1) { + cost[i] += min(cost[i + 1], cost[i + 2]) + } + return min(cost[0], cost[1]) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/min-cost-to-connect-points.md b/articles/min-cost-to-connect-points.md new file mode 100644 index 000000000..6688ee3a1 --- /dev/null +++ b/articles/min-cost-to-connect-points.md @@ -0,0 +1,1102 @@ +## 1. Kruskal's Algorithm + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu = self.find(u) + pv = self.find(v) + if pu == pv: + return False + if self.Size[pu] < self.Size[pv]: + pu, pv = pv, pu + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + return True + +class Solution: + def minCostConnectPoints(self, points: List[List[int]]) -> int: + n = len(points) + dsu = DSU(n) + edges = [] + for i in range(n): + x1, y1 = points[i] + for j in range(i + 1, n): + x2, y2 = points[j] + dist = abs(x1 - x2) + abs(y1 - y2) + edges.append((dist, i, j)) + + edges.sort() + res = 0 + for dist, u, v in edges: + if dsu.union(u, v): + res += dist + return res +``` + +```java +class DSU { + int[] Parent, Size; + + public DSU(int n) { + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) Parent[i] = i; + Arrays.fill(Size, 1); + } + + public int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (Size[pu] < Size[pv]) { + int temp = pu; + pu = pv; + pv = temp; + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } +} + +public class Solution { + public int minCostConnectPoints(int[][] points) { + int n = points.length; + DSU dsu = new DSU(n); + List edges = new ArrayList<>(); + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + int dist = Math.abs(points[i][0] - points[j][0]) + + Math.abs(points[i][1] - points[j][1]); + edges.add(new int[] {dist, i, j}); + } + } + + edges.sort((a, b) -> Integer.compare(a[0], b[0])); + int res = 0; + + for (int[] edge : edges) { + if (dsu.union(edge[1], edge[2])) { + res += edge[0]; + } + } + return res; + } +} +``` + +```cpp +class DSU { +public: + vector Parent, Size; + + DSU(int n) : Parent(n + 1), Size(n + 1, 1) { + for (int i = 0; i <= n; ++i) Parent[i] = i; + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + bool unionSets(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (Size[pu] < Size[pv]) swap(pu, pv); + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } +}; + +class Solution { +public: + int minCostConnectPoints(vector>& points) { + int n = points.size(); + DSU dsu(n); + vector> edges; + + for (int i = 0; i < n; ++i) { + for (int j = i + 1; j < n; ++j) { + int dist = abs(points[i][0] - points[j][0]) + + abs(points[i][1] - points[j][1]); + edges.push_back({dist, i, j}); + } + } + + sort(edges.begin(), edges.end()); + int res = 0; + + for (auto& [dist, u, v] : edges) { + if (dsu.unionSets(u, v)) { + res += dist; + } + } + return res; + } +}; +``` + +```javascript +class DSU { + constructor(n) { + this.Parent = Array.from({ length: n + 1 }, (_, i) => i); + this.Size = Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.Parent[node] !== node) { + this.Parent[node] = this.find(this.Parent[node]); + } + return this.Parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u), pv = this.find(v); + if (pu === pv) return false; + if (this.Size[pu] < this.Size[pv]) [pu, pv] = [pv, pu]; + this.Size[pu] += this.Size[pv]; + this.Parent[pv] = pu; + return true; + } +} + +class Solution { + /** + * @param {number[][]} points + * @return {number} + */ + minCostConnectPoints(points) { + const n = points.length; + const dsu = new DSU(n); + const edges = []; + + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + const dist = Math.abs(points[i][0] - points[j][0]) + + Math.abs(points[i][1] - points[j][1]); + edges.push([dist, i, j]); + } + } + + edges.sort((a, b) => a[0] - b[0]); + let res = 0; + + for (const [dist, u, v] of edges) { + if (dsu.union(u, v)) { + res += dist; + } + } + return res; + } +} +``` + +```csharp +public class DSU { + public int[] Parent, Size; + + public DSU(int n) { + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) Parent[i] = i; + Array.Fill(Size, 1); + } + + public int Find(int node) { + if (Parent[node] != node) { + Parent[node] = Find(Parent[node]); + } + return Parent[node]; + } + + public bool Union(int u, int v) { + int pu = Find(u), pv = Find(v); + if (pu == pv) return false; + if (Size[pu] < Size[pv]) { + int temp = pu; + pu = pv; + pv = temp; + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } +} + +public class Solution { + public int MinCostConnectPoints(int[][] points) { + int n = points.Length; + DSU dsu = new DSU(n); + List<(int, int, int)> edges = new List<(int, int, int)>(); + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + int dist = Math.Abs(points[i][0] - points[j][0]) + + Math.Abs(points[i][1] - points[j][1]); + edges.Add((dist, i, j)); + } + } + + edges.Sort((a, b) => a.Item1.CompareTo(b.Item1)); + int res = 0; + + foreach (var edge in edges) { + if (dsu.Union(edge.Item2, edge.Item3)) { + res += edge.Item1; + } + } + return res; + } +} +``` + +```go +type DSU struct { + Parent []int + Size []int +} + +func NewDSU(n int) *DSU { + parent := make([]int, n) + size := make([]int, n) + for i := range parent { + parent[i] = i + size[i] = 1 + } + return &DSU{Parent: parent, Size: size} +} + +func (dsu *DSU) find(node int) int { + if dsu.Parent[node] != node { + dsu.Parent[node] = dsu.find(dsu.Parent[node]) + } + return dsu.Parent[node] +} + +func (dsu *DSU) union(u, v int) bool { + pu, pv := dsu.find(u), dsu.find(v) + if pu == pv { + return false + } + if dsu.Size[pu] < dsu.Size[pv] { + pu, pv = pv, pu + } + dsu.Size[pu] += dsu.Size[pv] + dsu.Parent[pv] = pu + return true +} + +func minCostConnectPoints(points [][]int) int { + n := len(points) + dsu := NewDSU(n) + var edges [][]int + for i := 0; i < n; i++ { + x1, y1 := points[i][0], points[i][1] + for j := i + 1; j < n; j++ { + x2, y2 := points[j][0], points[j][1] + dist := int(math.Abs(float64(x1-x2)) + math.Abs(float64(y1-y2))) + edges = append(edges, []int{dist, i, j}) + } + } + + sort.Slice(edges, func(a, b int) bool { + return edges[a][0] < edges[b][0] + }) + + res := 0 + for _, edge := range edges { + dist, u, v := edge[0], edge[1], edge[2] + if dsu.union(u, v) { + res += dist + } + } + return res +} +``` + +```kotlin +class DSU(n: Int) { + private val parent = IntArray(n) { it } + private val size = IntArray(n) { 1 } + + fun find(node: Int): Int { + if (parent[node] != node) { + parent[node] = find(parent[node]) + } + return parent[node] + } + + fun union(u: Int, v: Int): Boolean { + val pu = find(u) + val pv = find(v) + if (pu == pv) return false + if (size[pu] < size[pv]) { + parent[pu] = pv + size[pv] += size[pu] + } else { + parent[pv] = pu + size[pu] += size[pv] + } + return true + } +} + +class Solution { + fun minCostConnectPoints(points: Array): Int { + val n = points.size + val dsu = DSU(n) + val edges = mutableListOf>() + + for (i in 0 until n) { + val (x1, y1) = points[i] + for (j in i + 1 until n) { + val (x2, y2) = points[j] + val dist = abs(x1 - x2) + abs(y1 - y2) + edges.add(Triple(dist, i, j)) + } + } + + edges.sortBy { it.first } + var res = 0 + for ((dist, u, v) in edges) { + if (dsu.union(u, v)) { + res += dist + } + } + return res + } +} +``` + +```swift +class DSU { + private var parent: [Int] + private var size: [Int] + + init(_ n: Int) { + parent = Array(0...n) + size = Array(repeating: 1, count: n + 1) + } + + func find(_ node: Int) -> Int { + if parent[node] != node { + parent[node] = find(parent[node]) + } + return parent[node] + } + + func union(_ u: Int, _ v: Int) -> Bool { + let pu = find(u) + let pv = find(v) + if pu == pv { + return false + } + if size[pu] < size[pv] { + parent[pu] = pv + size[pv] += size[pu] + } else { + parent[pv] = pu + size[pu] += size[pv] + } + return true + } +} + +class Solution { + func minCostConnectPoints(_ points: [[Int]]) -> Int { + let n = points.count + let dsu = DSU(n) + var edges = [(Int, Int, Int)]() + + for i in 0.. int: + N = len(points) + adj = {i: [] for i in range(N)} + for i in range(N): + x1, y1 = points[i] + for j in range(i + 1, N): + x2, y2 = points[j] + dist = abs(x1 - x2) + abs(y1 - y2) + adj[i].append([dist, j]) + adj[j].append([dist, i]) + + res = 0 + visit = set() + minH = [[0, 0]] + while len(visit) < N: + cost, i = heapq.heappop(minH) + if i in visit: + continue + res += cost + visit.add(i) + for neiCost, nei in adj[i]: + if nei not in visit: + heapq.heappush(minH, [neiCost, nei]) + return res +``` + +```java +public class Solution { + public int minCostConnectPoints(int[][] points) { + int N = points.length; + Map> adj = new HashMap<>(); + for (int i = 0; i < N; i++) { + int x1 = points[i][0]; + int y1 = points[i][1]; + for (int j = i + 1; j < N; j++) { + int x2 = points[j][0]; + int y2 = points[j][1]; + int dist = Math.abs(x1 - x2) + Math.abs(y1 - y2); + adj.computeIfAbsent(i, k -> new ArrayList<>()).add(new int[]{dist, j}); + adj.computeIfAbsent(j, k -> new ArrayList<>()).add(new int[]{dist, i}); + } + } + + int res = 0; + Set visit = new HashSet<>(); + PriorityQueue minH = new PriorityQueue<>(Comparator.comparingInt(a -> a[0])); + minH.offer(new int[]{0, 0}); + while (visit.size() < N) { + int[] curr = minH.poll(); + int cost = curr[0]; + int i = curr[1]; + if (visit.contains(i)) { + continue; + } + res += cost; + visit.add(i); + for (int[] nei : adj.getOrDefault(i, Collections.emptyList())) { + int neiCost = nei[0]; + int neiIndex = nei[1]; + if (!visit.contains(neiIndex)) { + minH.offer(new int[]{neiCost, neiIndex}); + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minCostConnectPoints(vector>& points) { + int N = points.size(); + unordered_map>> adj; + for (int i = 0; i < N; i++) { + int x1 = points[i][0]; + int y1 = points[i][1]; + for (int j = i + 1; j < N; j++) { + int x2 = points[j][0]; + int y2 = points[j][1]; + int dist = abs(x1 - x2) + abs(y1 - y2); + adj[i].push_back({dist, j}); + adj[j].push_back({dist, i}); + } + } + + int res = 0; + unordered_set visit; + priority_queue, vector>, + greater>> minH; + minH.push({0, 0}); + while (visit.size() < N) { + auto curr = minH.top(); + minH.pop(); + int cost = curr.first; + int i = curr.second; + if (visit.count(i)) { + continue; + } + res += cost; + visit.insert(i); + for (const auto& nei : adj[i]) { + int neiCost = nei.first; + int neiIndex = nei.second; + if (!visit.count(neiIndex)) { + minH.push({neiCost, neiIndex}); + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} points + * @return {number} + */ + minCostConnectPoints(points) { + const N = points.length; + const adj = new Map(); + for (let i = 0; i < N; i++) { + adj.set(i, []); + } + + for (let i = 0; i < N; i++) { + const [x1, y1] = points[i]; + for (let j = i + 1; j < N; j++) { + const [x2, y2] = points[j]; + const dist = Math.abs(x1 - x2) + Math.abs(y1 - y2); + adj.get(i).push([dist, j]); + adj.get(j).push([dist, i]); + } + } + + let res = 0; + const visit = new Set(); + const minHeap = new MinPriorityQueue(entry => entry[0]); + minHeap.push([0, 0]); + + while (visit.size < N) { + const [cost, i] = minHeap.pop(); + if (visit.has(i)) continue; + res += cost; + visit.add(i); + for (const [neiCost, nei] of adj.get(i)) { + if (!visit.has(nei)) { + minHeap.push([neiCost, nei]); + } + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int MinCostConnectPoints(int[][] points) { + int N = points.Length; + var adj = new Dictionary>(); + for (int i = 0; i < N; i++) { + int x1 = points[i][0]; + int y1 = points[i][1]; + for (int j = i + 1; j < N; j++) { + int x2 = points[j][0]; + int y2 = points[j][1]; + int dist = Math.Abs(x1 - x2) + Math.Abs(y1 - y2); + if (!adj.ContainsKey(i)) + adj[i] = new List(); + adj[i].Add(new int[] { j, dist }); + + if (!adj.ContainsKey(j)) + adj[j] = new List(); + adj[j].Add(new int[] { i, dist }); + } + } + + int res = 0; + var visit = new HashSet(); + var pq = new PriorityQueue(); + pq.Enqueue(0, 0); + + while (visit.Count < N && pq.Count > 0) { + if (pq.TryPeek(out int i, out int cost)) { + pq.Dequeue(); + + if (visit.Contains(i)) { + continue; + } + + res += cost; + visit.Add(i); + + if (adj.ContainsKey(i)) { + foreach (var edge in adj[i]) { + var nei = edge[0]; + var neiCost = edge[1]; + if (!visit.Contains(nei)) { + pq.Enqueue(nei, neiCost); + } + } + } + } + } + return visit.Count == N ? res : -1; + } +} +``` + +```go +func minCostConnectPoints(points [][]int) int { + n := len(points) + adj := make(map[int][][]int) + for i := 0; i < n; i++ { + x1, y1 := points[i][0], points[i][1] + for j := i + 1; j < n; j++ { + x2, y2 := points[j][0], points[j][1] + dist := int(math.Abs(float64(x1-x2)) + math.Abs(float64(y1-y2))) + adj[i] = append(adj[i], []int{dist, j}) + adj[j] = append(adj[j], []int{dist, i}) + } + } + + res := 0 + visit := make(map[int]bool) + pq := priorityqueue.NewWith(func(a, b interface{}) int { + return utils.IntComparator(a.([]int)[0], b.([]int)[0]) + }) + pq.Enqueue([]int{0, 0}) + + for len(visit) < n { + item, ok := pq.Dequeue() + if !ok { + continue + } + cost, point := item.([]int)[0], item.([]int)[1] + if visit[point] { + continue + } + res += cost + visit[point] = true + + for _, edge := range adj[point] { + neiCost, neiPoint := edge[0], edge[1] + if !visit[neiPoint] { + pq.Enqueue([]int{neiCost, neiPoint}) + } + } + } + return res +} +``` + +```kotlin + +class Solution { + fun minCostConnectPoints(points: Array): Int { + val n = points.size + val adj = HashMap>>() + + for (i in 0 until n) { + val (x1, y1) = points[i] + for (j in i + 1 until n) { + val (x2, y2) = points[j] + val dist = abs(x1 - x2) + abs(y1 - y2) + adj.computeIfAbsent(i) { mutableListOf() }.add(dist to j) + adj.computeIfAbsent(j) { mutableListOf() }.add(dist to i) + } + } + + var res = 0 + val visited = mutableSetOf() + val minHeap = PriorityQueue(compareBy> { it.first }) + + minHeap.add(0 to 0) + + while (visited.size < n) { + val (cost, point) = minHeap.poll() + if (point in visited) continue + res += cost + visited.add(point) + + for ((neiCost, nei) in adj[point] ?: emptyList()) { + if (nei !in visited) { + minHeap.add(neiCost to nei) + } + } + } + return res + } +} +``` + +```swift +struct Item: Comparable { + let cost: Int + let node: Int + + static func < (lhs: Item, rhs: Item) -> Bool { + return lhs.cost < rhs.cost + } +} + +class Solution { + func minCostConnectPoints(_ points: [[Int]]) -> Int { + let N = points.count + var adj = [Int: [(Int, Int)]]() + + for i in 0..() + var minHeap = Heap() + minHeap.insert(Item(cost: 0, node: 0)) + + while visit.count < N { + guard let item = minHeap.popMin() else { break } + let cost = item.cost + let i = item.node + + if visit.contains(i) { + continue + } + + res += cost + visit.insert(i) + + if let neighbors = adj[i] { + for (neiCost, nei) in neighbors { + if !visit.contains(nei) { + minHeap.insert(Item(cost: neiCost, node: nei)) + } + } + } + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 \log n)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Prim's Algorithm (Optimal) + +::tabs-start + +```python +class Solution: + def minCostConnectPoints(self, points: List[List[int]]) -> int: + n, node = len(points), 0 + dist = [100000000] * n + visit = [False] * n + edges, res = 0, 0 + + while edges < n - 1: + visit[node] = True + nextNode = -1 + for i in range(n): + if visit[i]: + continue + curDist = (abs(points[i][0] - points[node][0]) + + abs(points[i][1] - points[node][1])) + dist[i] = min(dist[i], curDist) + if nextNode == -1 or dist[i] < dist[nextNode]: + nextNode = i + + res += dist[nextNode] + node = nextNode + edges += 1 + + return res +``` + +```java +public class Solution { + public int minCostConnectPoints(int[][] points) { + int n = points.length, node = 0; + int[] dist = new int[n]; + boolean[] visit = new boolean[n]; + Arrays.fill(dist, 100000000); + int edges = 0, res = 0; + + while (edges < n - 1) { + visit[node] = true; + int nextNode = -1; + for (int i = 0; i < n; i++) { + if (visit[i]) continue; + int curDist = Math.abs(points[i][0] - points[node][0]) + + Math.abs(points[i][1] - points[node][1]); + dist[i] = Math.min(dist[i], curDist); + if (nextNode == -1 || dist[i] < dist[nextNode]) { + nextNode = i; + } + } + res += dist[nextNode]; + node = nextNode; + edges++; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minCostConnectPoints(vector>& points) { + int n = points.size(), node = 0; + vector dist(n, 100000000); + vector visit(n, false); + int edges = 0, res = 0; + + while (edges < n - 1) { + visit[node] = true; + int nextNode = -1; + for (int i = 0; i < n; i++) { + if (visit[i]) continue; + int curDist = abs(points[i][0] - points[node][0]) + + abs(points[i][1] - points[node][1]); + dist[i] = min(dist[i], curDist); + if (nextNode == -1 || dist[i] < dist[nextNode]) { + nextNode = i; + } + } + res += dist[nextNode]; + node = nextNode; + edges++; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} points + * @return {number} + */ + minCostConnectPoints(points) { + const n = points.length; + let node = 0; + const dist = new Array(n).fill(100000000); + const visit = new Array(n).fill(false); + let edges = 0, res = 0; + + while (edges < n - 1) { + visit[node] = true; + let nextNode = -1; + for (let i = 0; i < n; i++) { + if (visit[i]) continue; + const curDist = Math.abs(points[i][0] - points[node][0]) + + Math.abs(points[i][1] - points[node][1]); + dist[i] = Math.min(dist[i], curDist); + if (nextNode === -1 || dist[i] < dist[nextNode]) { + nextNode = i; + } + } + res += dist[nextNode]; + node = nextNode; + edges++; + } + return res; + } +} +``` + +```csharp +public class Solution { + public int MinCostConnectPoints(int[][] points) { + int n = points.Length, node = 0; + int[] dist = new int[n]; + bool[] visit = new bool[n]; + Array.Fill(dist, 100000000); + int edges = 0, res = 0; + + while (edges < n - 1) { + visit[node] = true; + int nextNode = -1; + for (int i = 0; i < n; i++) { + if (visit[i]) continue; + int curDist = Math.Abs(points[i][0] - points[node][0]) + + Math.Abs(points[i][1] - points[node][1]); + dist[i] = Math.Min(dist[i], curDist); + if (nextNode == -1 || dist[i] < dist[nextNode]) { + nextNode = i; + } + } + res += dist[nextNode]; + node = nextNode; + edges++; + } + return res; + } +} +``` + +```go +func minCostConnectPoints(points [][]int) int { + n := len(points) + node := 0 + dist := make([]int, n) + for i := range dist { + dist[i] = 100000000 + } + visit := make([]bool, n) + edges, res := 0, 0 + + for edges < n-1 { + visit[node] = true + nextNode := -1 + for i := 0; i < n; i++ { + if visit[i] { + continue + } + curDist := int(math.Abs(float64(points[i][0]-points[node][0])) + + math.Abs(float64(points[i][1]-points[node][1]))) + if curDist < dist[i] { + dist[i] = curDist + } + if nextNode == -1 || dist[i] < dist[nextNode] { + nextNode = i + } + } + res += dist[nextNode] + node = nextNode + edges++ + } + return res +} +``` + +```kotlin +class Solution { + fun minCostConnectPoints(points: Array): Int { + val n = points.size + var node = 0 + val dist = IntArray(n) { 100000000 } + val visit = BooleanArray(n) + var edges = 0 + var res = 0 + + while (edges < n - 1) { + visit[node] = true + var nextNode = -1 + for (i in 0 until n) { + if (visit[i]) continue + val curDist = abs(points[i][0] - points[node][0]) + + abs(points[i][1] - points[node][1]) + dist[i] = minOf(dist[i], curDist) + if (nextNode == -1 || dist[i] < dist[nextNode]) { + nextNode = i + } + } + res += dist[nextNode] + node = nextNode + edges++ + } + return res + } +} +``` + +```swift +class Solution { + func minCostConnectPoints(_ points: [[Int]]) -> Int { + let n = points.count + var node = 0 + var dist = Array(repeating: 100000000, count: n) + var visit = Array(repeating: false, count: n) + var edges = 0 + var res = 0 + + while edges < n - 1 { + visit[node] = true + var nextNode = -1 + + for i in 0.. int: + n = len(nums) + arr = [] + for i, num in enumerate(nums): + if num & 1: + arr.append((num, i)) + arr.append((num * 2, i)) + else: + while num % 2 == 0: + arr.append((num, i)) + num //= 2 + arr.append((num, i)) + + arr.sort() + res = float("inf") + + seen = [0] * n + count = i = 0 + for j in range(len(arr)): + seen[arr[j][1]] += 1 + if seen[arr[j][1]] == 1: + count += 1 + while count == n: + res = min(res, arr[j][0] - arr[i][0]) + seen[arr[i][1]] -= 1 + if seen[arr[i][1]] == 0: + count -= 1 + i += 1 + + return res +``` + +```java +public class Solution { + public int minimumDeviation(int[] nums) { + int n = nums.length; + List arr = new ArrayList<>(); + + for (int i = 0; i < n; i++) { + int num = nums[i]; + if (num % 2 == 1) { + arr.add(new int[]{num, i}); + arr.add(new int[]{num * 2, i}); + } else { + while (num % 2 == 0) { + arr.add(new int[]{num, i}); + num /= 2; + } + arr.add(new int[]{num, i}); + } + } + + arr.sort(Comparator.comparingInt(a -> a[0])); + int res = Integer.MAX_VALUE; + + int[] seen = new int[n]; + int count = 0, i = 0; + + for (int j = 0; j < arr.size(); j++) { + seen[arr.get(j)[1]]++; + if (seen[arr.get(j)[1]] == 1) { + count++; + while (count == n) { + res = Math.min(res, arr.get(j)[0] - arr.get(i)[0]); + seen[arr.get(i)[1]]--; + if (seen[arr.get(i)[1]] == 0) { + count--; + } + i++; + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minimumDeviation(vector& nums) { + int n = nums.size(); + vector> arr; + + for (int i = 0; i < n; i++) { + int num = nums[i]; + if (num % 2 == 1) { + arr.emplace_back(num, i); + arr.emplace_back(num * 2, i); + } else { + while (num % 2 == 0) { + arr.emplace_back(num, i); + num /= 2; + } + arr.emplace_back(num, i); + } + } + + sort(arr.begin(), arr.end()); + int res = INT_MAX; + + vector seen(n, 0); + int count = 0, i = 0; + + for (int j = 0; j < arr.size(); j++) { + seen[arr[j].second]++; + if (seen[arr[j].second] == 1) { + count++; + while (count == n) { + res = min(res, arr[j].first - arr[i].first); + seen[arr[i].second]--; + if (seen[arr[i].second] == 0) { + count--; + } + i++; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minimumDeviation(nums) { + let n = nums.length; + let arr = []; + + for (let i = 0; i < n; i++) { + let num = nums[i]; + if (num % 2 === 1) { + arr.push([num, i]); + arr.push([num * 2, i]); + } else { + while (num % 2 === 0) { + arr.push([num, i]); + num /= 2; + } + arr.push([num, i]); + } + } + + arr.sort((a, b) => a[0] - b[0]); + let res = Infinity; + + let seen = new Array(n).fill(0); + let count = 0, i = 0; + + for (let j = 0; j < arr.length; j++) { + seen[arr[j][1]]++; + if (seen[arr[j][1]] === 1) { + count++; + while (count === n) { + res = Math.min(res, arr[j][0] - arr[i][0]); + seen[arr[i][1]]--; + if (seen[arr[i][1]] === 0) { + count--; + } + i++; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((n \log m) * \log (n \log m))$ +* Space complexity: $O(n \log m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum element in $nums$. + +--- + +## 2. Min-Heap + +::tabs-start + +```python +class Solution: + def minimumDeviation(self, nums: List[int]) -> int: + minHeap, heapMax = [], 0 + + for n in nums: + tmp = n + while n % 2 == 0: + n //= 2 + minHeap.append((n, max(tmp, 2 * n))) + heapMax = max(heapMax, n) + + res = float("inf") + heapq.heapify(minHeap) + + while len(minHeap) == len(nums): + n, nMax = heapq.heappop(minHeap) + res = min(res, heapMax - n) + + if n < nMax: + heapq.heappush(minHeap, (n * 2, nMax)) + heapMax = max(heapMax, n * 2) + + return res +``` + +```java +public class Solution { + public int minimumDeviation(int[] nums) { + PriorityQueue minHeap = new PriorityQueue<>((a, b) -> a[0] - b[0]); + int heapMax = 0; + + for (int num : nums) { + int tmp = num; + while (num % 2 == 0) { + num /= 2; + } + minHeap.offer(new int[]{num, Math.max(tmp, 2 * num)}); + heapMax = Math.max(heapMax, num); + } + + int res = Integer.MAX_VALUE; + + while (minHeap.size() == nums.length) { + int[] minElement = minHeap.poll(); + int n = minElement[0], nMax = minElement[1]; + res = Math.min(res, heapMax - n); + + if (n < nMax) { + minHeap.offer(new int[]{n * 2, nMax}); + heapMax = Math.max(heapMax, n * 2); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minimumDeviation(vector& nums) { + priority_queue, vector>, greater<>> minHeap; + int heapMax = 0; + + for (int num : nums) { + int tmp = num; + while (num % 2 == 0) { + num /= 2; + } + minHeap.push({num, max(tmp, 2 * num)}); + heapMax = max(heapMax, num); + } + + int res = INT_MAX; + + while (minHeap.size() == nums.size()) { + auto [n, nMax] = minHeap.top(); + minHeap.pop(); + res = min(res, heapMax - n); + + if (n < nMax) { + minHeap.push({n * 2, nMax}); + heapMax = max(heapMax, n * 2); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minimumDeviation(nums) { + const minHeap = new MinPriorityQueue(x => x[0]); + let heapMax = 0; + + for (let num of nums) { + let tmp = num; + while (num % 2 === 0) { + num /= 2; + } + minHeap.enqueue([num, Math.max(tmp, num * 2)]); + heapMax = Math.max(heapMax, num); + } + + let res = Infinity; + + while (minHeap.size() === nums.length) { + let [n, nMax] = minHeap.dequeue(); + res = Math.min(res, heapMax - n); + + if (n < nMax) { + minHeap.enqueue([n * 2, nMax]); + heapMax = Math.max(heapMax, n * 2); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n *\log n * \log m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum element in $nums$. + +--- + +## 3. Max-Heap + +::tabs-start + +```python +class Solution: + def minimumDeviation(self, nums: List[int]) -> int: + maxHeap = [] + minVal = float("inf") + + for num in nums: + if num % 2 == 1: + num *= 2 + heapq.heappush(maxHeap, -num) + minVal = min(minVal, num) + + res = float("inf") + + while maxHeap: + maxVal = -heapq.heappop(maxHeap) + res = min(res, maxVal - minVal) + if maxVal % 2 == 1: + break + + nextVal = maxVal // 2 + heapq.heappush(maxHeap, -nextVal) + minVal = min(minVal, nextVal) + + return res +``` + +```java +public class Solution { + public int minimumDeviation(int[] nums) { + PriorityQueue maxHeap = new PriorityQueue<>((a, b) -> b - a); + int minVal = Integer.MAX_VALUE; + + for (int num : nums) { + if (num % 2 == 1) num *= 2; + maxHeap.offer(num); + minVal = Math.min(minVal, num); + } + + int res = Integer.MAX_VALUE; + + while (!maxHeap.isEmpty()) { + int maxVal = maxHeap.poll(); + res = Math.min(res, maxVal - minVal); + + if (maxVal % 2 == 1) break; + + int nextVal = maxVal / 2; + maxHeap.offer(nextVal); + minVal = Math.min(minVal, nextVal); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minimumDeviation(vector& nums) { + priority_queue maxHeap; + int minVal = INT_MAX; + + for (int num : nums) { + if (num % 2 == 1) num *= 2; + maxHeap.push(num); + minVal = min(minVal, num); + } + + int res = INT_MAX; + + while (!maxHeap.empty()) { + int maxVal = maxHeap.top(); + maxHeap.pop(); + res = min(res, maxVal - minVal); + + if (maxVal % 2 == 1) break; + + int nextVal = maxVal / 2; + maxHeap.push(nextVal); + minVal = min(minVal, nextVal); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minimumDeviation(nums) { + const maxHeap = new MaxPriorityQueue(); + let minVal = Infinity; + + for (let num of nums) { + if (num % 2 === 1) num *= 2; + maxHeap.enqueue(num); + minVal = Math.min(minVal, num); + } + + let res = Infinity; + + while (!maxHeap.isEmpty()) { + let maxVal = maxHeap.dequeue(); + res = Math.min(res, maxVal - minVal); + + if (maxVal % 2 === 1) break; + + let nextVal = maxVal / 2; + maxHeap.enqueue(nextVal); + minVal = Math.min(minVal, nextVal); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n *\log n * \log m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum element in $nums$. \ No newline at end of file diff --git a/articles/minimize-maximum-of-array.md b/articles/minimize-maximum-of-array.md new file mode 100644 index 000000000..b851e40b5 --- /dev/null +++ b/articles/minimize-maximum-of-array.md @@ -0,0 +1,210 @@ +## 1. Binary Search + +::tabs-start + +```python +class Solution: + def minimizeArrayValue(self, nums: List[int]) -> int: + def isValid(maxVal): + prefix_sum = 0 + for i in range(len(nums)): + prefix_sum += nums[i] + if prefix_sum > maxVal * (i + 1): + return False + return True + + left, right = 0, max(nums) + while left < right: + mid = left + (right - left) // 2 + if isValid(mid): + right = mid + else: + left = mid + 1 + + return left +``` + +```java +public class Solution { + public int minimizeArrayValue(int[] nums) { + int left = 0, right = 0; + for (int num : nums) { + right = Math.max(right, num); + } + + while (left < right) { + int mid = left + (right - left) / 2; + if (isValid(nums, mid)) { + right = mid; + } else { + left = mid + 1; + } + } + + return left; + } + + private boolean isValid(int[] nums, int maxVal) { + long prefixSum = 0; + for (int i = 0; i < nums.length; i++) { + prefixSum += nums[i]; + if (prefixSum > (long) maxVal * (i + 1)) { + return false; + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + int minimizeArrayValue(vector& nums) { + int left = 0, right = *max_element(nums.begin(), nums.end()); + + while (left < right) { + int mid = left + (right - left) / 2; + if (isValid(nums, mid)) { + right = mid; + } else { + left = mid + 1; + } + } + + return left; + } + +private: + bool isValid(vector& nums, int maxVal) { + long long prefixSum = 0; + for (int i = 0; i < nums.size(); i++) { + prefixSum += nums[i]; + if (prefixSum > (long long)maxVal * (i + 1)) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minimizeArrayValue(nums) { + const isValid = (maxVal) => { + let prefixSum = 0; + for (let i = 0; i < nums.length; i++) { + prefixSum += nums[i]; + if (prefixSum > maxVal * (i + 1)) { + return false; + } + } + return true; + }; + + let left = 0, right = Math.max(...nums); + while (left < right) { + let mid = left + Math.floor((right - left) / 2); + if (isValid(mid)) { + right = mid; + } else { + left = mid + 1; + } + } + + return left; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log m)$ +* Space complexity: $O(1)$ extra space. + +> Where $n$ is the size of the array $nums$ and $m$ is the maximum value in the array. + +--- + +## 2. Prefix Sum + Greedy + +::tabs-start + +```python +class Solution: + def minimizeArrayValue(self, nums: List[int]) -> int: + res = total = nums[0] + + for i in range(1, len(nums)): + total += nums[i] + res = max(res, math.ceil(total / (i + 1))) + + return res +``` + +```java +public class Solution { + public int minimizeArrayValue(int[] nums) { + int res = nums[0]; + long total = nums[0]; + + for (int i = 1; i < nums.length; i++) { + total += nums[i]; + res = Math.max(res, (int) Math.ceil((double) total / (i + 1))); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minimizeArrayValue(vector& nums) { + int res = nums[0]; + long long total = nums[0]; + + for (int i = 1; i < nums.size(); i++) { + total += nums[i]; + res = max(res, (int)ceil((double)total / (i + 1))); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minimizeArrayValue(nums) { + let res = nums[0]; + let total = nums[0]; + + for (let i = 1; i < nums.length; i++) { + total += nums[i]; + res = Math.max(res, Math.ceil(total / (i + 1))); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/minimize-the-maximum-difference-of-pairs.md b/articles/minimize-the-maximum-difference-of-pairs.md new file mode 100644 index 000000000..5ad0f31b1 --- /dev/null +++ b/articles/minimize-the-maximum-difference-of-pairs.md @@ -0,0 +1,561 @@ +## 1. Greedy + Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minimizeMax(self, nums: List[int], p: int) -> int: + n = len(nums) + nums.sort() + dp = {} + + def dfs(i, pairs): + if pairs == p: + return 0 + if i >= n - 1: + return float('inf') + if (i, pairs) in dp: + return dp[(i, pairs)] + + take = max(nums[i + 1] - nums[i], dfs(i + 2, pairs + 1)) + skip = dfs(i + 1, pairs) + dp[(i, pairs)] = min(take, skip) + return dp[(i, pairs)] + + return dfs(0, 0) +``` + +```java +public class Solution { + private Map dp; + + public int minimizeMax(int[] nums, int p) { + Arrays.sort(nums); + dp = new HashMap<>(); + return dfs(0, 0, nums, p); + } + + private int dfs(int i, int pairs, int[] nums, int p) { + if (pairs == p) return 0; + if (i >= nums.length - 1) return Integer.MAX_VALUE; + + String key = i + "," + pairs; + if (dp.containsKey(key)) return dp.get(key); + + int take = Math.max(nums[i + 1] - nums[i], dfs(i + 2, pairs + 1, nums, p)); + int skip = dfs(i + 1, pairs, nums, p); + + int res = Math.min(take, skip); + dp.put(key, res); + return res; + } +} +``` + +```cpp +class Solution { + unordered_map dp; + +public: + int minimizeMax(vector& nums, int p) { + int n = nums.size(); + sort(nums.begin(), nums.end()); + return dfs(0, 0, nums, p); + } + +private: + int dfs(int i, int pairs, vector& nums, int p) { + if (pairs == p) return 0; + if (i >= nums.size() - 1) return INT_MAX; + long long key = i; + key = (key << 31) | pairs; + if (dp.count(key)) return dp[key]; + + int take = max(nums[i + 1] - nums[i], dfs(i + 2, pairs + 1, nums, p)); + int skip = dfs(i + 1, pairs, nums, p); + + return dp[key] = min(take, skip); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} p + * @return {number} + */ + minimizeMax(nums, p) { + nums.sort((a, b) => a - b); + const dp = new Map(); + + const dfs = (i, pairs) => { + if (pairs === p) return 0; + if (i >= nums.length - 1) return Infinity; + + let key = `${i},${pairs}`; + if (dp.has(key)) return dp.get(key); + + let take = Math.max(nums[i + 1] - nums[i], dfs(i + 2, pairs + 1)); + let skip = dfs(i + 1, pairs); + + let result = Math.min(take, skip); + dp.set(key, result); + return result; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * p)$ +* Space complexity: $O(n * p)$ + +> Where $n$ is the size of the input array and $p$ is the number of pairs to select. + +--- + +## 2. Greesy + Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minimizeMax(self, nums: List[int], p: int) -> int: + n = len(nums) + nums.sort() + + dp = [[float('inf')] * (p + 1) for _ in range(n + 1)] + for i in range(n + 1): + dp[i][0] = 0 + + for i in range(n - 2, -1, -1): + for pairs in range(1, p + 1): + take = float('inf') + if i + 1 < n: + take = max(nums[i + 1] - nums[i], dp[i + 2][pairs - 1]) + + skip = dp[i + 1][pairs] + dp[i][pairs] = min(take, skip) + + return dp[0][p] +``` + +```java +public class Solution { + public int minimizeMax(int[] nums, int p) { + int n = nums.length; + Arrays.sort(nums); + + int[][] dp = new int[n + 1][p + 1]; + for (int i = 0; i <= n; i++) { + Arrays.fill(dp[i], Integer.MAX_VALUE); + dp[i][0] = 0; + } + + for (int i = n - 2; i >= 0; i--) { + for (int pairs = 1; pairs <= p; pairs++) { + int take = Integer.MAX_VALUE; + if (i + 1 < n) { + take = Math.max(nums[i + 1] - nums[i], dp[i + 2][pairs - 1]); + } + int skip = dp[i + 1][pairs]; + dp[i][pairs] = Math.min(take, skip); + } + } + + return dp[0][p]; + } +} +``` + +```cpp +class Solution { +public: + int minimizeMax(vector& nums, int p) { + int n = nums.size(); + sort(nums.begin(), nums.end()); + + vector> dp(n + 1, vector(p + 1, INT_MAX)); + for (int i = 0; i <= n; i++) { + dp[i][0] = 0; + } + + for (int i = n - 2; i >= 0; i--) { + for (int pairs = 1; pairs <= p; pairs++) { + int take = INT_MAX; + if (i + 1 < n) { + take = max(nums[i + 1] - nums[i], dp[i + 2][pairs - 1]); + } + int skip = dp[i + 1][pairs]; + dp[i][pairs] = min(take, skip); + } + } + + return dp[0][p]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} p + * @return {number} + */ + minimizeMax(nums, p) { + const n = nums.length; + nums.sort((a, b) => a - b); + + const dp = Array.from({ length: n + 1 }, () => + new Array(p + 1).fill(Infinity) + ); + for (let i = 0; i <= n; i++) { + dp[i][0] = 0; + } + + for (let i = n - 2; i >= 0; i--) { + for (let pairs = 1; pairs <= p; pairs++) { + let take = Infinity; + if (i + 1 < n) { + take = Math.max(nums[i + 1] - nums[i], dp[i + 2][pairs - 1]); + } + const skip = dp[i + 1][pairs]; + dp[i][pairs] = Math.min(take, skip); + } + } + + return dp[0][p]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * p)$ +* Space complexity: $O(n * p)$ + +> Where $n$ is the size of the input array and $p$ is the number of pairs to select. + +--- + +## 3. Greesy + Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def minimizeMax(self, nums: List[int], p: int) -> int: + n = len(nums) + nums.sort() + + dp = [float('inf')] * (p + 1) + dp1 = [float('inf')] * (p + 1) + dp2 = [float('inf')] * (p + 1) + + dp[0] = dp1[0] = dp2[0] = 0 + for i in range(n - 1, -1, -1): + for pairs in range(1, p + 1): + take = float('inf') + if i + 1 < n: + take = max(nums[i + 1] - nums[i], dp2[pairs - 1]) + skip = dp1[pairs] + dp[pairs] = min(take, skip) + + dp2 = dp1[:] + dp1 = dp[:] + dp = [float('inf')] * (p + 1) + dp[0] = 0 + + return dp1[p] +``` + +```java +public class Solution { + public int minimizeMax(int[] nums, int p) { + int n = nums.length; + Arrays.sort(nums); + + int[] dp = new int[p + 1]; + int[] dp1 = new int[p + 1]; + int[] dp2 = new int[p + 1]; + Arrays.fill(dp, Integer.MAX_VALUE); + Arrays.fill(dp1, Integer.MAX_VALUE); + Arrays.fill(dp2, Integer.MAX_VALUE); + dp[0] = dp1[0] = dp2[0] = 0; + + for (int i = n - 1; i >= 0; i--) { + for (int pairs = 1; pairs <= p; pairs++) { + int take = Integer.MAX_VALUE; + if (i + 1 < n) { + take = Math.max(nums[i + 1] - nums[i], dp2[pairs - 1]); + } + int skip = dp1[pairs]; + dp[pairs] = Math.min(take, skip); + } + dp2 = dp1.clone(); + dp1 = dp.clone(); + Arrays.fill(dp, Integer.MAX_VALUE); + dp[0] = 0; + } + + return dp1[p]; + } +} +``` + +```cpp +class Solution { +public: + int minimizeMax(vector& nums, int p) { + int n = nums.size(); + sort(nums.begin(), nums.end()); + + vector dp(p + 1, INT_MAX); + vector dp1(p + 1, INT_MAX); + vector dp2(p + 1, INT_MAX); + + dp[0] = dp1[0] = dp2[0] = 0; + + for (int i = n - 1; i >= 0; i--) { + for (int pairs = 1; pairs <= p; pairs++) { + int take = INT_MAX; + if (i + 1 < n) { + take = max(nums[i + 1] - nums[i], dp2[pairs - 1]); + } + int skip = dp1[pairs]; + dp[pairs] = min(take, skip); + } + dp2 = dp1; + dp1 = dp; + fill(dp.begin(), dp.end(), INT_MAX); + dp[0] = 0; + } + + return dp1[p]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} p + * @return {number} + */ + minimizeMax(nums, p) { + const n = nums.length; + nums.sort((a, b) => a - b); + + let dp = new Array(p + 1).fill(Infinity); + let dp1 = new Array(p + 1).fill(Infinity); + let dp2 = new Array(p + 1).fill(Infinity); + + dp[0] = dp1[0] = dp2[0] = 0; + + for (let i = n - 1; i >= 0; i--) { + for (let pairs = 1; pairs <= p; pairs++) { + let take = Infinity; + if (i + 1 < n) { + take = Math.max(nums[i + 1] - nums[i], dp2[pairs - 1]); + } + let skip = dp1[pairs]; + dp[pairs] = Math.min(take, skip); + } + dp2 = dp1.slice(); + dp1 = dp.slice(); + dp.fill(Infinity); + dp[0] = 0; + } + + return dp1[p]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * p)$ +* Space complexity: $O(p)$ + +> Where $n$ is the size of the input array and $p$ is the number of pairs to select. + +--- + +## 4. Greedy + Binary Search + +::tabs-start + +```python +class Solution: + def minimizeMax(self, nums: List[int], p: int) -> int: + if p == 0: + return 0 + + def isValid(threshold): + i, cnt = 0, 0 + while i < len(nums) - 1: + if abs(nums[i] - nums[i + 1]) <= threshold: + cnt += 1 + i += 2 + else: + i += 1 + if cnt == p: + return True + return False + + nums.sort() + l, r = 0, nums[-1] - nums[0] + res = nums[-1] - nums[0] + + while l <= r: + m = l + (r - l) // 2 + if isValid(m): + res = m + r = m - 1 + else: + l = m + 1 + + return res +``` + +```java +public class Solution { + public int minimizeMax(int[] nums, int p) { + if (p == 0) return 0; + + Arrays.sort(nums); + int left = 0, right = nums[nums.length - 1] - nums[0]; + int result = right; + + while (left <= right) { + int mid = left + (right - left) / 2; + if (isValid(nums, mid, p)) { + result = mid; + right = mid - 1; + } else { + left = mid + 1; + } + } + + return result; + } + + private boolean isValid(int[] nums, int threshold, int p) { + int i = 0, count = 0; + while (i < nums.length - 1) { + if (Math.abs(nums[i] - nums[i + 1]) <= threshold) { + count++; + i += 2; + } else { + i++; + } + if (count == p) return true; + } + return false; + } +} +``` + +```cpp +class Solution { +public: + int minimizeMax(vector& nums, int p) { + if (p == 0) return 0; + + sort(nums.begin(), nums.end()); + int left = 0, right = nums.back() - nums[0]; + int result = right; + + while (left <= right) { + int mid = left + (right - left) / 2; + if (isValid(nums, mid, p)) { + result = mid; + right = mid - 1; + } else { + left = mid + 1; + } + } + + return result; + } + +private: + bool isValid(vector& nums, int threshold, int p) { + int i = 0, count = 0; + while (i < nums.size() - 1) { + if (abs(nums[i] - nums[i + 1]) <= threshold) { + count++; + i += 2; + } else { + i++; + } + if (count == p) return true; + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} p + * @return {number} + */ + minimizeMax(nums, p) { + if (p === 0) return 0; + + nums.sort((a, b) => a - b); + let l = 0, r = nums[nums.length - 1] - nums[0], res = r; + + const isValid = (threshold) => { + let i = 0, cnt = 0; + while (i < nums.length - 1) { + if (Math.abs(nums[i] - nums[i + 1]) <= threshold) { + cnt++; + i += 2; + } else { + i++; + } + if (cnt === p) return true; + } + return false; + }; + + while (l <= r) { + let m = Math.floor(l + (r - l) / 2); + if (isValid(m)) { + res = m; + r = m - 1; + } else { + l = m + 1; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log n + n\log m)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +> Where $n$ is the size of the input array and $m$ is the maximum value in the array. \ No newline at end of file diff --git a/articles/minimum-array-end.md b/articles/minimum-array-end.md new file mode 100644 index 000000000..2aba4edba --- /dev/null +++ b/articles/minimum-array-end.md @@ -0,0 +1,320 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def minEnd(self, n: int, x: int) -> int: + res = x + for i in range(n - 1): + res = (res + 1) | x + return res +``` + +```java +public class Solution { + public long minEnd(int n, int x) { + long res = x; + for (int i = 0; i < n - 1; i++) { + res = (res + 1) | x; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + long long minEnd(int n, int x) { + long long res = x; + for (int i = 0; i < n - 1; i++) { + res = (res + 1) | x; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} x + * @return {number} + */ + minEnd(n, x) { + let res = BigInt(x); + for (let i = 0; i < n - 1; i++) { + res = (res + BigInt(1)) | BigInt(x); + } + return Number(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Binary Representation And Bit Manipulation + +::tabs-start + +```python +class Solution: + def minEnd(self, n: int, x: int) -> int: + res = 0 + n -= 1 + + x_bin = [0] * 64 # Binary representation of x + n_bin = [0] * 64 # Binary representation of n-1 + + for i in range(32): + x_bin[i] = (x >> i) & 1 + n_bin[i] = (n >> i) & 1 + + i_x = 0 + i_n = 0 + while i_x < 63: + while i_x < 63 and x_bin[i_x] != 0: + i_x += 1 + x_bin[i_x] = n_bin[i_n] + i_x += 1 + i_n += 1 + + for i in range(64): + if x_bin[i] == 1: + res += (1 << i) + + return res +``` + +```java +public class Solution { + public long minEnd(int n, int x) { + long res = 0; + n -= 1; + + int[] x_bin = new int[64]; // Binary representation of x + int[] n_bin = new int[64]; // Binary representation of n-1 + + for (int i = 0; i < 32; i++) { + x_bin[i] = (x >> i) & 1; + n_bin[i] = (n >> i) & 1; + } + + int i_x = 0; + int i_n = 0; + while (i_x < 63) { + while (i_x < 63 && x_bin[i_x] != 0) { + i_x++; + } + x_bin[i_x] = n_bin[i_n]; + i_x++; + i_n++; + } + + for (int i = 0; i < 64; i++) { + if (x_bin[i] == 1) { + res += (1L << i); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long minEnd(int n, int x) { + long long res = 0; + n -= 1; + + vector x_bin(64, 0); // Binary representation of x + vector n_bin(64, 0); // Binary representation of n-1 + + for (int i = 0; i < 32; i++) { + x_bin[i] = (x >> i) & 1; + n_bin[i] = (n >> i) & 1; + } + + int i_x = 0; + int i_n = 0; + while (i_x < 63) { + while (i_x < 63 && x_bin[i_x] != 0) { + i_x++; + } + x_bin[i_x] = n_bin[i_n]; + i_x++; + i_n++; + } + + for (int i = 0; i < 64; i++) { + if (x_bin[i] == 1) { + res += (1LL << i); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} x + * @return {number} + */ + minEnd(n, x) { + let res = 0n; + n -= 1; + + const x_bin = new Array(64).fill(0); // Binary representation of x + const n_bin = new Array(64).fill(0); // Binary representation of n-1 + + for (let i = 0; i < 32; i++) { + x_bin[i] = (x >> i) & 1; + n_bin[i] = (n >> i) & 1; + } + + let i_x = 0; + let i_n = 0; + while (i_x < 63) { + while (i_x < 63 && x_bin[i_x] !== 0) { + i_x++; + } + x_bin[i_x] = n_bin[i_n]; + i_x++; + i_n++; + } + + for (let i = 0; i < 64; i++) { + if (x_bin[i] === 1) { + res += BigInt(1) << BigInt(i); + } + } + + return Number(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ + +--- + +## 3. Bit Manipulation + +::tabs-start + +```python +class Solution: + def minEnd(self, n: int, x: int) -> int: + res = x + i_x = 1 + i_n = 1 # for n-1 + + while i_n <= n - 1: + if i_x & x == 0: + if i_n & (n - 1): + res = res | i_x + i_n = i_n << 1 + i_x = i_x << 1 + + return res +``` + +```java +public class Solution { + public long minEnd(int n, int x) { + long res = x; + long i_x = 1; + long i_n = 1; // for n - 1 + + while (i_n <= n - 1) { + if ((i_x & x) == 0) { + if ((i_n & (n - 1)) != 0) { + res = res | i_x; + } + i_n = i_n << 1; + } + i_x = i_x << 1; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long minEnd(int n, int x) { + long long res = x; + long long i_x = 1; + long long i_n = 1; // for n - 1 + + while (i_n <= n - 1) { + if ((i_x & x) == 0) { + if (i_n & (n - 1)) { + res = res | i_x; + } + i_n = i_n << 1; + } + i_x = i_x << 1; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} x + * @return {number} + */ + minEnd(n, x) { + let res = BigInt(x); + let i_x = 1n; + let i_n = 1n; + n = BigInt(n - 1); + + while (i_n <= n) { + if ((i_x & res) === 0n) { + if ((i_n & n) !== 0n) { + res = res | i_x; + } + i_n = i_n << 1n; + } + i_x = i_x << 1n; + } + + return Number(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/minimum-changes-to-make-alternating-binary-string.md b/articles/minimum-changes-to-make-alternating-binary-string.md new file mode 100644 index 000000000..05b5629b5 --- /dev/null +++ b/articles/minimum-changes-to-make-alternating-binary-string.md @@ -0,0 +1,207 @@ +## 1. Start with Zero and One + +::tabs-start + +```python +class Solution: + def minOperations(self, s: str) -> int: + cur = cnt1 = 0 + for c in s: + if int(c) != cur: + cnt1 += 1 + cur ^= 1 + + cur = 1 + cnt2 = 0 + for c in s: + if int(c) != cur: + cnt2 += 1 + cur ^= 1 + + return min(cnt1, cnt2) +``` + +```java +public class Solution { + public int minOperations(String s) { + int cur = 0, cnt1 = 0; + for (char c : s.toCharArray()) { + if (c - '0' != cur) { + cnt1++; + } + cur ^= 1; + } + + cur = 1; + int cnt2 = 0; + for (char c : s.toCharArray()) { + if (c - '0' != cur) { + cnt2++; + } + cur ^= 1; + } + + return Math.min(cnt1, cnt2); + } +} +``` + +```cpp +class Solution { +public: + int minOperations(string s) { + int cur = 0, cnt1 = 0; + for (char c : s) { + if (c - '0' != cur) { + cnt1++; + } + cur ^= 1; + } + + cur = 1; + int cnt2 = 0; + for (char c : s) { + if (c - '0' != cur) { + cnt2++; + } + cur ^= 1; + } + + return min(cnt1, cnt2); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minOperations(s) { + let cur = 0, cnt1 = 0; + for (let c of s) { + if (parseInt(c) !== cur) { + cnt1++; + } + cur ^= 1; + } + + cur = 1; + let cnt2 = 0; + for (let c of s) { + if (parseInt(c) !== cur) { + cnt2++; + } + cur ^= 1; + } + + return Math.min(cnt1, cnt2); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Start with Zero or One + +::tabs-start + +```python +class Solution: + def minOperations(self, s: str) -> int: + count = 0 + + for i in range(len(s)): + if i % 2 == 0: + count += 1 if s[i] == '0' else 0 + else: + count += 1 if s[i] == '1' else 0 + + return min(count, len(s) - count) +``` + +```java +public class Solution { + public int minOperations(String s) { + int count = 0; + + for (int i = 0; i < s.length(); i++) { + if (i % 2 == 0) { + if (s.charAt(i) == '0') { + count++; + } + } else { + if (s.charAt(i) == '1') { + count++; + } + } + } + + return Math.min(count, s.length() - count); + } +} +``` + +```cpp +class Solution { +public: + int minOperations(string s) { + int count = 0; + + for (int i = 0; i < s.size(); i++) { + if (i % 2 == 0) { + if (s[i] == '0') { + count++; + } + } else { + if (s[i] == '1') { + count++; + } + } + } + + return min(count, (int)s.size() - count); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minOperations(s) { + let count = 0; + + for (let i = 0; i < s.length; i++) { + if (i % 2 === 0) { + if (s[i] === '0') { + count++; + } + } else { + if (s[i] === '1') { + count++; + } + } + } + + return Math.min(count, s.length - count); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/minimum-cost-for-tickets.md b/articles/minimum-cost-for-tickets.md new file mode 100644 index 000000000..1b32c8980 --- /dev/null +++ b/articles/minimum-cost-for-tickets.md @@ -0,0 +1,953 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def mincostTickets(self, days: List[int], costs: List[int]) -> int: + n = len(days) + + def dfs(i): + if i == n: + return 0 + + res = costs[0] + dfs(i + 1) + j = i + while j < n and days[j] < days[i] + 7: + j += 1 + res = min(res, costs[1] + dfs(j)) + + j = i + while j < n and days[j] < days[i] + 30: + j += 1 + res = min(res, costs[2] + dfs(j)) + + return res + + return dfs(0) +``` + +```java +public class Solution { + public int mincostTickets(int[] days, int[] costs) { + return dfs(0, days.length, days, costs); + } + + private int dfs(int i, int n, int[] days, int[] costs) { + if (i == n) return 0; + + int res = costs[0] + dfs(i + 1, n, days, costs); + int j = i; + while (j < n && days[j] < days[i] + 7) { + j++; + } + res = Math.min(res, costs[1] + dfs(j, n, days, costs)); + + j = i; + while (j < n && days[j] < days[i] + 30) { + j++; + } + res = Math.min(res, costs[2] + dfs(j, n, days, costs)); + + return res; + } +} +``` + +```cpp +class Solution { +public: + int mincostTickets(vector& days, vector& costs) { + int n = days.size(); + + function dfs = [&](int i) -> int { + if (i == n) return 0; + + int res = costs[0] + dfs(i + 1); + + int j = i; + while (j < n && days[j] < days[i] + 7) { + j++; + } + res = min(res, costs[1] + dfs(j)); + + j = i; + while (j < n && days[j] < days[i] + 30) { + j++; + } + res = min(res, costs[2] + dfs(j)); + + return res; + }; + + return dfs(0); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} days + * @param {number[]} costs + * @return {number} + */ + mincostTickets(days, costs) { + const n = days.length; + + const dfs = (i) => { + if (i === n) return 0; + + let res = costs[0] + dfs(i + 1); + + let j = i; + while (j < n && days[j] < days[i] + 7) { + j++; + } + res = Math.min(res, costs[1] + dfs(j)); + + j = i; + while (j < n && days[j] < days[i] + 30) { + j++; + } + res = Math.min(res, costs[2] + dfs(j)); + + return res; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(3 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def mincostTickets(self, days: List[int], costs: List[int]) -> int: + dp = {} + + def dfs(i): + if i == len(days): + return 0 + if i in dp: + return dp[i] + + dp[i] = float("inf") + j = i + for d, c in zip([1, 7, 30], costs): + while j < len(days) and days[j] < days[i] + d: + j += 1 + dp[i] = min(dp[i], c + dfs(j)) + + return dp[i] + + return dfs(0) +``` + +```java +public class Solution { + private int[] dp; + + public int mincostTickets(int[] days, int[] costs) { + dp = new int[days.length]; + Arrays.fill(dp, -1); + return dfs(0, days, costs); + } + + private int dfs(int i, int[] days, int[] costs) { + if (i == days.length) { + return 0; + } + if (dp[i] != -1) { + return dp[i]; + } + + dp[i] = Integer.MAX_VALUE; + int idx = 0, j = i; + for (int d : new int[]{1, 7, 30}) { + while (j < days.length && days[j] < days[i] + d) { + j++; + } + dp[i] = Math.min(dp[i], costs[idx] + dfs(j, days, costs)); + idx++; + } + return dp[i]; + } +} +``` + +```cpp +class Solution { +private: + vector dp; + + int dfs(int i, const vector& days, const vector& costs) { + if (i == days.size()) return 0; + if (dp[i] != -1) return dp[i]; + + dp[i] = INT_MAX; + int idx = 0, j = i; + for (int d : {1, 7, 30}) { + while (j < days.size() && days[j] < days[i] + d) { + j++; + } + dp[i] = min(dp[i], costs[idx] + dfs(j, days, costs)); + idx++; + } + return dp[i]; + } + +public: + int mincostTickets(vector& days, vector& costs) { + dp = vector(days.size(), -1); + return dfs(0, days, costs); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} days + * @param {number[]} costs + * @return {number} + */ + mincostTickets(days, costs) { + const dp = new Array(days.length).fill(-1); + + const dfs = (i) => { + if (i === days.length) return 0; + if (dp[i] !== -1) return dp[i]; + + dp[i] = Infinity; + let j = i; + [1, 7, 30].forEach((d, idx) => { + while (j < days.length && days[j] < days[i] + d) { + j++; + } + dp[i] = Math.min(dp[i], costs[idx] + dfs(j)); + }); + + return dp[i]; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def mincostTickets(self, days: List[int], costs: List[int]) -> int: + n = len(days) + dp = [0] * (n + 1) + + for i in range(n - 1, -1, -1): + dp[i] = float('inf') + j = i + for d, c in zip([1, 7, 30], costs): + while j < n and days[j] < days[i] + d: + j += 1 + dp[i] = min(dp[i], c + dp[j]) + + return dp[0] +``` + +```java +public class Solution { + public int mincostTickets(int[] days, int[] costs) { + int n = days.length; + int[] dp = new int[n + 1]; + + for (int i = n - 1; i >= 0; i--) { + dp[i] = Integer.MAX_VALUE; + int idx = 0, j = i; + for (int d : new int[]{1, 7, 30}) { + while (j < n && days[j] < days[i] + d) { + j++; + } + dp[i] = Math.min(dp[i], costs[idx] + dp[j]); + idx++; + } + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int mincostTickets(vector& days, vector& costs) { + int n = days.size(); + vector dp(n + 1, 0); + + for (int i = n - 1; i >= 0; i--) { + dp[i] = INT_MAX; + int j = i; + for (int k = 0; k < 3; ++k) { + while (j < n && days[j] < days[i] + (k == 0 ? 1 : k == 1 ? 7 : 30)) { + j++; + } + dp[i] = min(dp[i], costs[k] + dp[j]); + } + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} days + * @param {number[]} costs + * @return {number} + */ + mincostTickets(days, costs) { + const n = days.length; + const dp = new Array(n + 1).fill(0); + + for (let i = n - 1; i >= 0; i--) { + dp[i] = Infinity; + let j = i; + [1, 7, 30].forEach((d, idx) => { + while (j < n && days[j] < days[i] + d) { + j++; + } + dp[i] = Math.min(dp[i], costs[idx] + dp[j]); + }); + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Bottom-Up) + Two Pointers + +::tabs-start + +```python +class Solution: + def mincostTickets(self, days: List[int], costs: List[int]) -> int: + days.append(days[-1] + 30) + n = len(days) + dp = [0] * n + last7 = last30 = n + + for i in range(n - 2, -1, -1): + dp[i] = dp[i + 1] + costs[0] + + while last7 > i + 1 and days[last7 - 1] >= days[i] + 7: + last7 -= 1 + dp[i] = min(dp[i], costs[1] + dp[last7]) + + while last30 > i + 1 and days[last30 - 1] >= days[i] + 30: + last30 -= 1 + dp[i] = min(dp[i], costs[2] + dp[last30]) + + return dp[0] +``` + +```java +public class Solution { + public int mincostTickets(int[] days, int[] costs) { + int n = days.length; + int[] dp = new int[n + 1]; + int last7 = n, last30 = n; + int[] extendedDays = Arrays.copyOf(days, n + 1); + extendedDays[n] = days[n - 1] + 30; + + for (int i = n - 1; i >= 0; i--) { + dp[i] = dp[i + 1] + costs[0]; + + while (last7 > i + 1 && extendedDays[last7 - 1] >= days[i] + 7) { + last7--; + } + dp[i] = Math.min(dp[i], costs[1] + dp[last7]); + + while (last30 > i + 1 && extendedDays[last30 - 1] >= days[i] + 30) { + last30--; + } + dp[i] = Math.min(dp[i], costs[2] + dp[last30]); + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int mincostTickets(vector& days, vector& costs) { + days.push_back(days.back() + 30); + int n = days.size(); + vector dp(n, 0); + int last7 = n, last30 = n; + + for (int i = n - 2; i >= 0; --i) { + dp[i] = dp[i + 1] + costs[0]; + + while (last7 > i + 1 && days[last7 - 1] >= days[i] + 7) { + --last7; + } + dp[i] = min(dp[i], costs[1] + dp[last7]); + + while (last30 > i + 1 && days[last30 - 1] >= days[i] + 30) { + --last30; + } + dp[i] = min(dp[i], costs[2] + dp[last30]); + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} days + * @param {number[]} costs + * @return {number} + */ + mincostTickets(days, costs) { + days.push(days[days.length - 1] + 30); + const n = days.length; + const dp = new Array(n).fill(0); + let last7 = n, last30 = n; + + for (let i = n - 2; i >= 0; i--) { + dp[i] = dp[i + 1] + costs[0]; + + while (last7 > i + 1 && days[last7 - 1] >= days[i] + 7) { + last7--; + } + dp[i] = Math.min(dp[i], costs[1] + dp[last7]); + + while (last30 > i + 1 && days[last30 - 1] >= days[i] + 30) { + last30--; + } + dp[i] = Math.min(dp[i], costs[2] + dp[last30]); + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Dynamic Programming (Space Optimized) - I + +::tabs-start + +```python +class Solution: + def mincostTickets(self, days: List[int], costs: List[int]) -> int: + dp7, dp30 = deque(), deque() + dp = 0 + + for d in days: + while dp7 and dp7[0][0] + 7 <= d: + dp7.popleft() + + while dp30 and dp30[0][0] + 30 <= d: + dp30.popleft() + + dp7.append([d, dp + costs[1]]) + dp30.append([d, dp + costs[2]]) + dp = min(dp + costs[0], dp7[0][1], dp30[0][1]) + + return dp +``` + +```java +public class Solution { + public int mincostTickets(int[] days, int[] costs) { + Queue dp7 = new LinkedList<>(); + Queue dp30 = new LinkedList<>(); + int dp = 0; + + for (int d : days) { + while (!dp7.isEmpty() && dp7.peek()[0] + 7 <= d) { + dp7.poll(); + } + + while (!dp30.isEmpty() && dp30.peek()[0] + 30 <= d) { + dp30.poll(); + } + + dp7.offer(new int[]{d, dp + costs[1]}); + dp30.offer(new int[]{d, dp + costs[2]}); + dp = Math.min(dp + costs[0], Math.min(dp7.peek()[1], dp30.peek()[1])); + } + + return dp; + } +} +``` + +```cpp +class Solution { +public: + int mincostTickets(vector& days, vector& costs) { + queue> dp7, dp30; + int dp = 0; + + for (int& d : days) { + while (!dp7.empty() && dp7.front().first + 7 <= d) { + dp7.pop(); + } + + while (!dp30.empty() && dp30.front().first + 30 <= d) { + dp30.pop(); + } + + dp7.emplace(d, dp + costs[1]); + dp30.emplace(d, dp + costs[2]); + dp = min({dp + costs[0], dp7.front().second, dp30.front().second}); + } + + return dp; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} days + * @param {number[]} costs + * @return {number} + */ + mincostTickets(days, costs) { + const dp7 = new Queue; + const dp30 = new Queue; + let dp = 0; + + for (const d of days) { + while (!dp7.isEmpty() && dp7.front()[0] + 7 <= d) { + dp7.pop(); + } + + while (!dp30.isEmpty() && dp30.front()[0] + 30 <= d) { + dp30.pop(); + } + + dp7.push([d, dp + costs[1]]); + dp30.push([d, dp + costs[2]]); + + dp = Math.min(dp + costs[0], dp7.front()[1], dp30.front()[1]); + } + + return dp; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we keep at most $30$ values in the queue. + +--- + +## 6. Dynamic Programming (Space Optimized) - II + +::tabs-start + +```python +class Solution: + def mincostTickets(self, days: List[int], costs: List[int]) -> int: + dp7, dp30 = deque(), deque() + dp = 0 + + last7 = last30 = 0 + for i in range(len(days) - 1, -1, -1): + dp += costs[0] + while dp7 and dp7[-1][0] >= days[i] + 7: + last7 = dp7.pop()[1] + dp = min(dp, costs[1] + last7) + + while dp30 and dp30[-1][0] >= days[i] + 30: + last30 = dp30.pop()[1] + dp = min(dp, costs[2] + last30) + + dp7.appendleft([days[i], dp]) + dp30.appendleft([days[i], dp]) + + return dp +``` + +```java +public class Solution { + public int mincostTickets(int[] days, int[] costs) { + Deque dp7 = new ArrayDeque<>(); + Deque dp30 = new ArrayDeque<>(); + int dp = 0, last7 = 0, last30 = 0; + + for (int i = days.length - 1; i >= 0; i--) { + dp += costs[0]; + + while (!dp7.isEmpty() && dp7.peekLast()[0] >= days[i] + 7) { + last7 = dp7.pollLast()[1]; + } + dp = Math.min(dp, costs[1] + last7); + + while (!dp30.isEmpty() && dp30.peekLast()[0] >= days[i] + 30) { + last30 = dp30.pollLast()[1]; + } + dp = Math.min(dp, costs[2] + last30); + + dp7.offerFirst(new int[]{days[i], dp}); + dp30.offerFirst(new int[]{days[i], dp}); + } + return dp; + } +} +``` + +```cpp +class Solution { +public: + int mincostTickets(vector& days, vector& costs) { + deque> dp7, dp30; + int dp = 0, last7 = 0, last30 = 0; + + for (int i = days.size() - 1; i >= 0; --i) { + dp += costs[0]; + + while (!dp7.empty() && dp7.back().first >= days[i] + 7) { + last7 = dp7.back().second; + dp7.pop_back(); + } + dp = min(dp, costs[1] + last7); + + while (!dp30.empty() && dp30.back().first >= days[i] + 30) { + last30 = dp30.back().second; + dp30.pop_back(); + } + dp = min(dp, costs[2] + last30); + + dp7.push_front({days[i], dp}); + dp30.push_front({days[i], dp}); + } + return dp; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} days + * @param {number[]} costs + * @return {number} + */ + mincostTickets(days, costs) { + const dp7 = new Deque(); + const dp30 = new Deque(); + let dp = 0, last7 = 0, last30 = 0; + + for (let i = days.length - 1; i >= 0; i--) { + dp += costs[0]; + + while (!dp7.isEmpty() && dp7.back()[0] >= days[i] + 7) { + last7 = dp7.popBack()[1]; + } + dp = Math.min(dp, costs[1] + last7); + + while (!dp30.isEmpty() && dp30.back()[0] >= days[i] + 30) { + last30 = dp30.popBack()[1]; + } + dp = Math.min(dp, costs[2] + last30); + + dp7.pushFront([days[i], dp]); + dp30.pushFront([days[i], dp]); + } + return dp; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we keep at most $30$ values in the deque. + +--- + +## 7. Dynamic Programming (Space Optimized) - III + +::tabs-start + +```python +class Solution: + def mincostTickets(self, days: List[int], costs: List[int]) -> int: + dp = [0] * 366 + i = 0 + + for d in range(1, 366): + dp[d] = dp[d - 1] + if i == len(days): + return dp[d] + + if d == days[i]: + dp[d] += costs[0] + dp[d] = min(dp[d], costs[1] + dp[max(0, d - 7)]) + dp[d] = min(dp[d], costs[2] + dp[max(0, d - 30)]) + i += 1 + + return dp[365] +``` + +```java +public class Solution { + public int mincostTickets(int[] days, int[] costs) { + int[] dp = new int[366]; + int i = 0; + + for (int d = 1; d < 366; d++) { + dp[d] = dp[d - 1]; + + if (i == days.length) { + return dp[d]; + } + + if (d == days[i]) { + dp[d] += costs[0]; + dp[d] = Math.min(dp[d], costs[1] + dp[Math.max(0, d - 7)]); + dp[d] = Math.min(dp[d], costs[2] + dp[Math.max(0, d - 30)]); + i++; + } + } + return dp[365]; + } +} +``` + +```cpp +class Solution { +public: + int mincostTickets(vector& days, vector& costs) { + vector dp(366, 0); + int i = 0; + + for (int d = 1; d < 366; d++) { + dp[d] = dp[d - 1]; + + if (i == days.size()) { + return dp[d]; + } + + if (d == days[i]) { + dp[d] += costs[0]; + dp[d] = min(dp[d], costs[1] + dp[max(0, d - 7)]); + dp[d] = min(dp[d], costs[2] + dp[max(0, d - 30)]); + i++; + } + } + return dp[365]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} days + * @param {number[]} costs + * @return {number} + */ + mincostTickets(days, costs) { + const dp = new Array(366).fill(0); + let i = 0; + + for (let d = 1; d < 366; d++) { + dp[d] = dp[d - 1]; + + if (i === days.length) { + return dp[d]; + } + + if (d === days[i]) { + dp[d] += costs[0]; + dp[d] = Math.min(dp[d], costs[1] + dp[Math.max(0, d - 7)]); + dp[d] = Math.min(dp[d], costs[2] + dp[Math.max(0, d - 30)]); + i++; + } + } + return dp[365]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since the size of the $dp$ array is $366$. + + +--- + +## 8. Dynamic Programming (Space Optimized) - IV + +::tabs-start + +```python +class Solution: + def mincostTickets(self, days: List[int], costs: List[int]) -> int: + dp = [0] * 31 + i = 0 + + for d in range(1, 366): + if i >= len(days): + break + + dp[d % 31] = dp[(d - 1) % 31] + + if d == days[i]: + dp[d % 31] += costs[0] + dp[d % 31] = min(dp[d % 31], costs[1] + dp[max(0, d - 7) % 31]) + dp[d % 31] = min(dp[d % 31], costs[2] + dp[max(0, d - 30) % 31]) + i += 1 + + return dp[days[-1] % 31] +``` + +```java +public class Solution { + public int mincostTickets(int[] days, int[] costs) { + int[] dp = new int[31]; + int i = 0; + + for (int d = 1; d <= 365; d++) { + if (i >= days.length) break; + + dp[d % 31] = dp[(d - 1) % 31]; + + if (d == days[i]) { + dp[d % 31] += costs[0]; + dp[d % 31] = Math.min(dp[d % 31], costs[1] + dp[Math.max(0, d - 7) % 31]); + dp[d % 31] = Math.min(dp[d % 31], costs[2] + dp[Math.max(0, d - 30) % 31]); + i++; + } + } + + return dp[days[days.length - 1] % 31]; + } +} +``` + +```cpp +class Solution { +public: + int mincostTickets(vector& days, vector& costs) { + vector dp(31, 0); + int i = 0; + + for (int d = 1; d <= 365; d++) { + if (i >= days.size()) break; + + dp[d % 31] = dp[(d - 1) % 31]; + + if (d == days[i]) { + dp[d % 31] += costs[0]; + dp[d % 31] = min(dp[d % 31], costs[1] + dp[max(0, d - 7) % 31]); + dp[d % 31] = min(dp[d % 31], costs[2] + dp[max(0, d - 30) % 31]); + i++; + } + } + + return dp[days.back() % 31]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} days + * @param {number[]} costs + * @return {number} + */ + mincostTickets(days, costs) { + const dp = new Array(31).fill(0); + let i = 0; + + for (let d = 1; d <= 365; d++) { + if (i >= days.length) break; + + dp[d % 31] = dp[(d - 1) % 31]; + + if (d === days[i]) { + dp[d % 31] += costs[0]; + dp[d % 31] = Math.min(dp[d % 31], costs[1] + dp[Math.max(0, d - 7) % 31]); + dp[d % 31] = Math.min(dp[d % 31], costs[2] + dp[Math.max(0, d - 30) % 31]); + i++; + } + } + + return dp[days[days.length - 1] % 31]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since the size of the $dp$ array is $31$. \ No newline at end of file diff --git a/articles/minimum-cost-to-cut-a-stick.md b/articles/minimum-cost-to-cut-a-stick.md new file mode 100644 index 000000000..0ef0f2484 --- /dev/null +++ b/articles/minimum-cost-to-cut-a-stick.md @@ -0,0 +1,485 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def minCost(self, n: int, cuts: List[int]) -> int: + def dfs(l, r): + if r - l == 1: + return 0 + res = float("inf") + for c in cuts: + if l < c < r: + res = min(res, (r - l) + dfs(l, c) + dfs(c, r)) + res = 0 if res == float("inf") else res + return res + + return dfs(0, n) +``` + +```java +public class Solution { + public int minCost(int n, int[] cuts) { + return dfs(0, n, cuts); + } + + private int dfs(int l, int r, int[] cuts) { + if (r - l == 1) { + return 0; + } + int res = Integer.MAX_VALUE; + for (int c : cuts) { + if (l < c && c < r) { + res = Math.min(res, (r - l) + dfs(l, c, cuts) + dfs(c, r, cuts)); + } + } + return res == Integer.MAX_VALUE ? 0 : res; + } +} +``` + +```cpp +class Solution { +public: + int minCost(int n, vector& cuts) { + return dfs(0, n, cuts); + } + +private: + int dfs(int l, int r, vector& cuts) { + if (r - l == 1) { + return 0; + } + int res = INT_MAX; + for (int c : cuts) { + if (l < c && c < r) { + res = min(res, (r - l) + dfs(l, c, cuts) + dfs(c, r, cuts)); + } + } + return res == INT_MAX ? 0 : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[]} cuts + * @return {number} + */ + minCost(n, cuts) { + const dfs = (l, r) => { + if (r - l === 1) { + return 0; + } + let res = Infinity; + for (const c of cuts) { + if (l < c && c < r) { + res = Math.min(res, (r - l) + dfs(l, c) + dfs(c, r)); + } + } + return res === Infinity ? 0 : res; + }; + + return dfs(0, n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m ^ N)$ +* Space complexity: $O(N)$ for recursion stack. + +> Where $m$ is the size of the $cuts$ array, $n$ is the length of the stick, and $N = min(n, m)$. + +--- + +## 2. Dynamic Programming (Top-Down) - I + +::tabs-start + +```python +class Solution: + def minCost(self, n: int, cuts: List[int]) -> int: + dp = {} + + def dfs(l, r): + if r - l == 1: + return 0 + if (l, r) in dp: + return dp[(l, r)] + + res = float("inf") + for c in cuts: + if l < c < r: + res = min(res, (r - l) + dfs(l, c) + dfs(c, r)) + res = 0 if res == float("inf") else res + dp[(l, r)] = res + return res + + return dfs(0, n) +``` + +```java +public class Solution { + private Map dp; + + public int minCost(int n, int[] cuts) { + dp = new HashMap<>(); + return dfs(0, n, cuts); + } + + private int dfs(int l, int r, int[] cuts) { + if (r - l == 1) { + return 0; + } + String key = l + "," + r; + if (dp.containsKey(key)) { + return dp.get(key); + } + + int res = Integer.MAX_VALUE; + for (int c : cuts) { + if (l < c && c < r) { + res = Math.min(res, (r - l) + dfs(l, c, cuts) + dfs(c, r, cuts)); + } + } + res = res == Integer.MAX_VALUE ? 0 : res; + dp.put(key, res); + return res; + } +} +``` + +```cpp +class Solution { +public: + int minCost(int n, vector& cuts) { + return dfs(0, n, cuts); + } + +private: + unordered_map dp; + + int dfs(int l, int r, vector& cuts) { + if (r - l == 1) { + return 0; + } + string key = to_string(l) + "," + to_string(r); + if (dp.find(key) != dp.end()) { + return dp[key]; + } + + int res = INT_MAX; + for (int c : cuts) { + if (l < c && c < r) { + res = min(res, (r - l) + dfs(l, c, cuts) + dfs(c, r, cuts)); + } + } + res = res == INT_MAX ? 0 : res; + dp[key] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[]} cuts + * @return {number} + */ + minCost(n, cuts) { + const dp = new Map(); + + const dfs = (l, r) => { + if (r - l === 1) { + return 0; + } + const key = `${l},${r}`; + if (dp.has(key)) { + return dp.get(key); + } + + let res = Infinity; + for (const c of cuts) { + if (l < c && c < r) { + res = Math.min(res, (r - l) + dfs(l, c) + dfs(c, r)); + } + } + res = res === Infinity ? 0 : res; + dp.set(key, res); + return res; + }; + + return dfs(0, n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * N ^ 2)$ +* Space complexity: $O(N ^ 2)$ + +> Where $m$ is the size of the $cuts$ array, $n$ is the length of the stick, and $N = min(n, m)$. + +--- + +## 3. Dynamic Programming (Top-Down) - II + +::tabs-start + +```python +class Solution: + def minCost(self, n: int, cuts: List[int]) -> int: + m = len(cuts) + cuts.sort() + dp = [[-1] * (m + 1) for _ in range(m + 1)] + + def dfs(l, r, i, j): + if i > j: + return 0 + if dp[i][j] != -1: + return dp[i][j] + + res = float("inf") + for mid in range(i, j + 1): + cur = (r - l) + dfs(l, cuts[mid], i, mid - 1) + dfs(cuts[mid], r, mid + 1, j) + res = min(res, cur) + + dp[i][j] = res + return res + + return dfs(0, n, 0, m - 1) +``` + +```java +public class Solution { + private int[][] dp; + + public int minCost(int n, int[] cuts) { + int m = cuts.length; + Arrays.sort(cuts); + dp = new int[m + 1][m + 1]; + + for (int[] row : dp) { + Arrays.fill(row, -1); + } + + return dfs(0, n, 0, m - 1, cuts); + } + + private int dfs(int l, int r, int i, int j, int[] cuts) { + if (i > j) return 0; + if (dp[i][j] != -1) return dp[i][j]; + + int res = Integer.MAX_VALUE; + for (int mid = i; mid <= j; mid++) { + int cur = (r - l) + dfs(l, cuts[mid], i, mid - 1, cuts) + dfs(cuts[mid], r, mid + 1, j, cuts); + res = Math.min(res, cur); + } + + dp[i][j] = res; + return res; + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + +public: + int minCost(int n, vector& cuts) { + int m = cuts.size(); + sort(cuts.begin(), cuts.end()); + dp = vector>(m + 1, vector(m + 1, -1)); + return dfs(0, n, 0, m - 1, cuts); + } + +private: + int dfs(int l, int r, int i, int j, vector& cuts) { + if (i > j) return 0; + if (dp[i][j] != -1) return dp[i][j]; + + int res = INT_MAX; + for (int mid = i; mid <= j; mid++) { + int cur = (r - l) + dfs(l, cuts[mid], i, mid - 1, cuts) + dfs(cuts[mid], r, mid + 1, j, cuts); + res = min(res, cur); + } + + dp[i][j] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[]} cuts + * @return {number} + */ + minCost(n, cuts) { + const m = cuts.length; + cuts.sort((a, b) => a - b); + const dp = Array.from({ length: m + 1 }, () => Array(m + 1).fill(-1)); + + const dfs = (l, r, i, j) => { + if (i > j) return 0; + if (dp[i][j] !== -1) return dp[i][j]; + + let res = Infinity; + for (let mid = i; mid <= j; mid++) { + const cur = (r - l) + dfs(l, cuts[mid], i, mid - 1) + dfs(cuts[mid], r, mid + 1, j); + res = Math.min(res, cur); + } + + dp[i][j] = res; + return res; + }; + + return dfs(0, n, 0, m - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m\log m + m ^ 3)$ +* Space complexity: $O(m ^ 2)$ + +> Where $m$ is the size of the $cuts$ array and $n$ is the length of the stick. + +--- + +## 4. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minCost(self, n: int, cuts: List[int]) -> int: + m = len(cuts) + cuts = [0] + sorted(cuts) + [n] + dp = [[0] * (m + 2) for _ in range(m + 2)] + + for length in range(2, m + 2): + for i in range(m + 2 - length): + j = i + length + dp[i][j] = float("inf") + for mid in range(i + 1, j): + dp[i][j] = min( + dp[i][j], + cuts[j] - cuts[i] + dp[i][mid] + dp[mid][j] + ) + + return dp[0][m + 1] +``` + +```java +public class Solution { + public int minCost(int n, int[] cuts) { + int m = cuts.length; + int[] newCuts = new int[m + 2]; + System.arraycopy(cuts, 0, newCuts, 1, m); + newCuts[0] = 0; + newCuts[m + 1] = n; + Arrays.sort(newCuts); + + int[][] dp = new int[m + 2][m + 2]; + + for (int length = 2; length <= m + 1; length++) { + for (int i = 0; i + length <= m + 1; i++) { + int j = i + length; + dp[i][j] = Integer.MAX_VALUE; + for (int mid = i + 1; mid < j; mid++) { + dp[i][j] = Math.min(dp[i][j], + newCuts[j] - newCuts[i] + dp[i][mid] + dp[mid][j]); + } + } + } + + return dp[0][m + 1]; + } +} +``` + +```cpp +class Solution { +public: + int minCost(int n, vector& cuts) { + int m = cuts.size(); + cuts.push_back(0); + cuts.push_back(n); + sort(cuts.begin(), cuts.end()); + + vector> dp(m + 2, vector(m + 2, 0)); + + for (int length = 2; length <= m + 1; length++) { + for (int i = 0; i + length <= m + 1; i++) { + int j = i + length; + dp[i][j] = INT_MAX; + for (int mid = i + 1; mid < j; mid++) { + dp[i][j] = min(dp[i][j], + cuts[j] - cuts[i] + dp[i][mid] + dp[mid][j]); + } + } + } + + return dp[0][m + 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[]} cuts + * @return {number} + */ + minCost(n, cuts) { + const m = cuts.length; + cuts = [0, ...cuts.sort((a, b) => a - b), n]; + const dp = Array.from({ length: m + 2 }, () => Array(m + 2).fill(0)); + + for (let length = 2; length <= m + 1; length++) { + for (let i = 0; i + length <= m + 1; i++) { + const j = i + length; + dp[i][j] = Infinity; + for (let mid = i + 1; mid < j; mid++) { + dp[i][j] = Math.min( + dp[i][j], + cuts[j] - cuts[i] + dp[i][mid] + dp[mid][j] + ); + } + } + } + + return dp[0][m + 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m\log m + m ^ 3)$ +* Space complexity: $O(m ^ 2)$ + +> Where $m$ is the size of the $cuts$ array and $n$ is the length of the stick. \ No newline at end of file diff --git a/articles/minimum-cost-to-hire-k-workers.md b/articles/minimum-cost-to-hire-k-workers.md new file mode 100644 index 000000000..f205b01e7 --- /dev/null +++ b/articles/minimum-cost-to-hire-k-workers.md @@ -0,0 +1,144 @@ +## 1. Greedy + Max-Heap + +::tabs-start + +```python +class Solution: + def mincostToHireWorkers(self, quality: List[int], wage: List[int], k: int) -> float: + pairs = sorted([(w / q, q) for q, w in zip(quality, wage)], key=lambda p: p[0]) + + maxHeap = [] + total_quality = 0 + res = float("inf") + + for rate, q in pairs: + heapq.heappush(maxHeap, -q) + total_quality += q + + if len(maxHeap) > k: + total_quality += heapq.heappop(maxHeap) + + if len(maxHeap) == k: + res = min(res, total_quality * rate) + + return res +``` + +```java +public class Solution { + public double mincostToHireWorkers(int[] quality, int[] wage, int k) { + int n = quality.length; + double res = Double.MAX_VALUE; + double totalQuality = 0; + + double[][] workers = new double[n][2]; + for (int i = 0; i < n; i++) { + workers[i] = new double[]{ + (double) wage[i] / quality[i], (double) quality[i] + }; + } + + Arrays.sort(workers, Comparator.comparingDouble(a -> a[0])); + PriorityQueue maxHeap = new PriorityQueue<>(Collections.reverseOrder()); + + for (double[] worker : workers) { + double ratio = worker[0]; + int q = (int) worker[1]; + + maxHeap.add(q); + totalQuality += q; + + if (maxHeap.size() > k) { + totalQuality -= maxHeap.poll(); + } + + if (maxHeap.size() == k) { + res = Math.min(res, totalQuality * ratio); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + double mincostToHireWorkers(vector& quality, vector& wage, int k) { + int n = quality.size(); + vector> workers(n); + for (int i = 0; i < n; i++) { + workers[i] = { (double)wage[i] / quality[i], quality[i] }; + } + + sort(workers.begin(), workers.end()); + priority_queue maxHeap; + double totalQuality = 0, res = DBL_MAX; + + for (auto& worker : workers) { + double ratio = worker.first; + int q = worker.second; + maxHeap.push(q); + totalQuality += q; + + if (maxHeap.size() > k) { + totalQuality -= maxHeap.top(); + maxHeap.pop(); + } + + if (maxHeap.size() == k) { + res = min(res, totalQuality * ratio); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} quality + * @param {number[]} wage + * @param {number} k + * @return {number} + */ + mincostToHireWorkers(quality, wage, k) { + const n = quality.length; + const workers = []; + for (let i = 0; i < n; i++) { + workers.push([wage[i] / quality[i], quality[i]]); + } + + workers.sort((a, b) => a[0] - b[0]); + const maxHeap = new MaxPriorityQueue(); + let totalQuality = 0, res = Number.MAX_VALUE; + + for (let [ratio, q] of workers) { + maxHeap.enqueue(q); + totalQuality += q; + + if (maxHeap.size() > k) { + totalQuality -= maxHeap.dequeue(); + } + + if (maxHeap.size() === k) { + res = Math.min(res, totalQuality * ratio); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * (\log n + \log k))$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of workers, and $k$ is the number of workers to be hired. \ No newline at end of file diff --git a/articles/minimum-deletions-to-make-character-frequencies-unique.md b/articles/minimum-deletions-to-make-character-frequencies-unique.md new file mode 100644 index 000000000..60c9564fa --- /dev/null +++ b/articles/minimum-deletions-to-make-character-frequencies-unique.md @@ -0,0 +1,348 @@ +## 1. Hash Set + +::tabs-start + +```python +class Solution: + def minDeletions(self, s: str) -> int: + count = [0] * 26 + for c in s: + count[ord(c) - ord('a')] += 1 + + used_freq = set() + res = 0 + for freq in count: + while freq > 0 and freq in used_freq: + freq -= 1 + res += 1 + used_freq.add(freq) + + return res +``` + +```java +class Solution { + public int minDeletions(String s) { + int[] count = new int[26]; + for (int i = 0; i < s.length(); i++) { + count[s.charAt(i) - 'a']++; + } + + Set usedFreq = new HashSet<>(); + int res = 0; + + for (int freq : count) { + while (freq > 0 && usedFreq.contains(freq)) { + freq--; + res++; + } + usedFreq.add(freq); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minDeletions(string s) { + vector count(26, 0); + for (char& c : s) { + count[c - 'a']++; + } + + unordered_set usedFreq; + int res = 0; + + for (int& freq : count) { + while (freq > 0 && usedFreq.count(freq)) { + freq--; + res++; + } + usedFreq.insert(freq); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minDeletions(s) { + let count = new Array(26).fill(0); + for (let c of s) { + count[c.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + + let usedFreq = new Set(); + let res = 0; + + for (let freq of count) { + while (freq > 0 && usedFreq.has(freq)) { + freq--; + res++; + } + usedFreq.add(freq); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m ^ 2)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the total number of unique frequncies possible. + +--- + +## 2. Max-Heap + +::tabs-start + +```python +class Solution: + def minDeletions(self, s: str) -> int: + freq = Counter(s) + maxHeap = [-f for f in freq.values()] + heapq.heapify(maxHeap) + + res = 0 + while len(maxHeap) > 1: + top = -heapq.heappop(maxHeap) + if top == -maxHeap[0]: + if top - 1 > 0: + heapq.heappush(maxHeap, -(top - 1)) + res += 1 + + return res +``` + +```java +public class Solution { + public int minDeletions(String s) { + Map freq = new HashMap<>(); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + freq.put(c, freq.getOrDefault(c, 0) + 1); + } + + PriorityQueue maxHeap = new PriorityQueue<>((a, b) -> b - a); + maxHeap.addAll(freq.values()); + + int res = 0; + while (maxHeap.size() > 1) { + int top = maxHeap.poll(); + if (top == maxHeap.peek()) { + if (top - 1 > 0) { + maxHeap.add(top - 1); + } + res++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minDeletions(string s) { + unordered_map freq; + for (char& c : s) { + freq[c]++; + } + + priority_queue maxHeap; + for (auto& f : freq) { + maxHeap.push(f.second); + } + + int res = 0; + while (maxHeap.size() > 1) { + int top = maxHeap.top(); + maxHeap.pop(); + if (top == maxHeap.top()) { + if (top - 1 > 0) { + maxHeap.push(top - 1); + } + res++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minDeletions(s) { + let freq = new Map(); + for (let c of s) { + freq.set(c, (freq.get(c) || 0) + 1); + } + + const maxHeap = new MaxPriorityQueue(); + for (let value of freq.values()) { + maxHeap.enqueue(value); + } + + let res = 0; + while (maxHeap.size() > 1) { + let top = maxHeap.dequeue().element; + if (maxHeap.front().element === top) { + if (top - 1 > 0) { + maxHeap.enqueue(top - 1); + } + res++; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m ^ 2 \log m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the total number of unique frequncies possible. + +--- + +## 3. Sorting + +::tabs-start + +```python +class Solution: + def minDeletions(self, s: str) -> int: + count = [0] * 26 + for c in s: + count[ord(c) - ord('a')] += 1 + + count.sort(reverse=True) + res = 0 + maxAllowedFreq = count[0] + + for freq in count: + if freq > maxAllowedFreq: + res += freq - maxAllowedFreq + freq = maxAllowedFreq + maxAllowedFreq = max(0, freq - 1) + + return res +``` + +```java +public class Solution { + public int minDeletions(String s) { + int[] count = new int[26]; + for (int i = 0; i < s.length(); i++) { + count[s.charAt(i) - 'a']++; + } + + Arrays.sort(count); + int res = 0; + int maxAllowedFreq = count[25]; + + for (int i = 25; i >= 0; i--) { + if (count[i] > maxAllowedFreq) { + res += count[i] - maxAllowedFreq; + count[i] = maxAllowedFreq; + } + maxAllowedFreq = Math.max(0, count[i] - 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minDeletions(string s) { + vector count(26, 0); + for (char& c : s) { + count[c - 'a']++; + } + + sort(count.begin(), count.end(), greater()); + int res = 0; + int maxAllowedFreq = count[0]; + + for (int& freq : count) { + if (freq > maxAllowedFreq) { + res += freq - maxAllowedFreq; + freq = maxAllowedFreq; + } + maxAllowedFreq = max(0, freq - 1); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minDeletions(s) { + let count = new Array(26).fill(0); + for (let c of s) { + count[c.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + + count.sort((a, b) => b - a); + + let res = 0; + let maxAllowedFreq = count[0]; + + for (let i = 0; i < 26; i++) { + if (count[i] > maxAllowedFreq) { + res += count[i] - maxAllowedFreq; + count[i] = maxAllowedFreq; + } + maxAllowedFreq = Math.max(0, count[i] - 1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m \log m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the total number of unique frequncies possible. \ No newline at end of file diff --git a/articles/minimum-difference-between-highest-and-lowest-of-k-scores.md b/articles/minimum-difference-between-highest-and-lowest-of-k-scores.md new file mode 100644 index 000000000..de7c35615 --- /dev/null +++ b/articles/minimum-difference-between-highest-and-lowest-of-k-scores.md @@ -0,0 +1,74 @@ +## 1. Sorting + Sliding Window + +::tabs-start + +```python +class Solution: + def minimumDifference(self, nums: List[int], k: int) -> int: + nums.sort() + l, r = 0, k - 1 + res = float("inf") + while r < len(nums): + res = min(res, nums[r] - nums[l]) + l += 1 + r += 1 + return res +``` + +```java +public class Solution { + public int minimumDifference(int[] nums, int k) { + Arrays.sort(nums); + int l = 0, r = k - 1, res = Integer.MAX_VALUE; + while (r < nums.length) { + res = Math.min(res, nums[r] - nums[l]); + l++; + r++; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minimumDifference(vector& nums, int k) { + sort(nums.begin(), nums.end()); + int l = 0, r = k - 1, res = INT_MAX; + while (r < nums.size()) { + res = min(res, nums[r] - nums[l]); + l++; + r++; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + minimumDifference(nums, k) { + nums.sort((a, b) => a - b); + let l = 0, r = k - 1, res = Infinity; + while (r < nums.length) { + res = Math.min(res, nums[r] - nums[l]); + l++; + r++; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file diff --git a/articles/minimum-difficulty-of-a-job-schedule.md b/articles/minimum-difficulty-of-a-job-schedule.md new file mode 100644 index 000000000..5738623eb --- /dev/null +++ b/articles/minimum-difficulty-of-a-job-schedule.md @@ -0,0 +1,682 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minDifficulty(self, jobDifficulty: List[int], d: int) -> int: + if len(jobDifficulty) < d: + return -1 + + n = len(jobDifficulty) + dp = {} + + def dfs(i, d, cur_max): + if i == n: + return 0 if d == 0 else float("inf") + if d == 0: + return float("inf") + if (i, d, cur_max) in dp: + return dp[(i, d, cur_max)] + + cur_max = max(cur_max, jobDifficulty[i]) + res = min( + dfs(i + 1, d, cur_max), + cur_max + dfs(i + 1, d - 1, -1) + ) + dp[(i, d, cur_max)] = res + return res + + return dfs(0, d, -1) +``` + +```java +public class Solution { + private int[][][] dp; + + public int minDifficulty(int[] jobDifficulty, int d) { + int n = jobDifficulty.length; + if (n < d) return -1; + int m = 0; + for (int i = 0; i < n; i++) { + m = Math.max(m, jobDifficulty[i]); + } + + dp = new int[n][d + 1][m + 5]; + for (int[][] layer : dp) { + for (int[] row : layer) { + Arrays.fill(row, -1); + } + } + + return dfs(0, d, -1, jobDifficulty); + } + + private int dfs(int i, int d, int curMax, int[] jobDifficulty) { + if (i == jobDifficulty.length) return d == 0 ? 0 : Integer.MAX_VALUE / 2; + if (d == 0) return Integer.MAX_VALUE / 2; + if (dp[i][d][curMax + 1] != -1) return dp[i][d][curMax + 1]; + + int maxSoFar = Math.max(curMax, jobDifficulty[i]); + int res = Math.min( + dfs(i + 1, d, maxSoFar, jobDifficulty), + maxSoFar + dfs(i + 1, d - 1, -1, jobDifficulty) + ); + + dp[i][d][curMax + 1] = res; + return res; + } +} +``` + +```cpp +class Solution { + vector>> dp; + + int dfs(int i, int d, int curMax, const vector& jobDifficulty) { + if (i == jobDifficulty.size()) return d == 0 ? 0 : INT_MAX / 2; + if (d == 0) return INT_MAX / 2; + if (dp[i][d][curMax + 1] != -1) return dp[i][d][curMax + 1]; + + int maxSoFar = max(curMax, jobDifficulty[i]); + int res = min( + dfs(i + 1, d, maxSoFar, jobDifficulty), + maxSoFar + dfs(i + 1, d - 1, -1, jobDifficulty) + ); + + dp[i][d][curMax + 1] = res; + return res; + } + +public: + int minDifficulty(vector& jobDifficulty, int d) { + int n = jobDifficulty.size(); + if (n < d) return -1; + + int m = *max_element(jobDifficulty.begin(), jobDifficulty.end()); + dp = vector>>(n, vector>(d + 1, vector(m + 5, -1))); + return dfs(0, d, -1, jobDifficulty); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} jobDifficulty + * @param {number} d + * @return {number} + */ + minDifficulty(jobDifficulty, d) { + const n = jobDifficulty.length; + if (n < d) return -1; + let m = 0; + for (let it of jobDifficulty) { + m = Math.max(m, it); + } + + const dp = Array.from({ length: n }, () => + Array.from({ length: d + 1 }, () => Array(m + 5).fill(-1)) + ); + + const dfs = (i, d, curMax) => { + if (i === n) return d === 0 ? 0 : Infinity; + if (d === 0) return Infinity; + if (dp[i][d][curMax + 1] !== -1) return dp[i][d][curMax + 1]; + + const maxSoFar = Math.max(curMax, jobDifficulty[i]); + const res = Math.min( + dfs(i + 1, d, maxSoFar), + maxSoFar + dfs(i + 1, d - 1, -1) + ); + + dp[i][d][curMax + 1] = res; + return res; + }; + + return dfs(0, d, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * d * m)$ +* Space complexity: $O(n * d * m)$ + +> Where $n$ is the number of jobs, $d$ is the number of days, and $m$ is the maximum difficulty value among all the job difficulties. + +--- + +## 2. Dynamic Programming (Top-Down Optimized) + +::tabs-start + +```python +class Solution: + def minDifficulty(self, jobDifficulty: List[int], d: int) -> int: + if len(jobDifficulty) < d: + return -1 + + n = len(jobDifficulty) + dp = {} + + def dfs(i, d): + if (i, d) in dp: + return dp[(i, d)] + + maxi = jobDifficulty[i] + if d == 1: + idx = i + while i < n: + maxi = max(maxi, jobDifficulty[i]) + i += 1 + dp[(idx, d)] = maxi + return maxi + + res = float("inf") + for j in range(i + 1, n): + res = min(res, maxi + dfs(j, d - 1)) + maxi = max(maxi, jobDifficulty[j]) + dp[(i, d)] = res + return res + + return dfs(0, d) +``` + +```java +public class Solution { + private int[][] dp; + + public int minDifficulty(int[] jobDifficulty, int d) { + int n = jobDifficulty.length; + if (n < d) return -1; + + dp = new int[n][d + 1]; + for (int[] row : dp) Arrays.fill(row, -1); + + return dfs(0, d, jobDifficulty); + } + + private int dfs(int i, int d, int[] jobDifficulty) { + if (dp[i][d] != -1) return dp[i][d]; + + int n = jobDifficulty.length; + int maxi = jobDifficulty[i]; + if (d == 1) { + for (int j = i; j < n; j++) { + maxi = Math.max(maxi, jobDifficulty[j]); + } + dp[i][d] = maxi; + return maxi; + } + + int res = Integer.MAX_VALUE / 2; + for (int j = i + 1; j < n; j++) { + res = Math.min(res, maxi + dfs(j, d - 1, jobDifficulty)); + maxi = Math.max(maxi, jobDifficulty[j]); + } + dp[i][d] = res; + return res; + } +} +``` + +```cpp +class Solution { + vector> dp; + +public: + int minDifficulty(vector& jobDifficulty, int d) { + int n = jobDifficulty.size(); + if (n < d) return -1; + + dp.assign(n, vector(d + 1, -1)); + return dfs(0, d, jobDifficulty); + } + +private: + int dfs(int i, int d, vector& jobDifficulty) { + if (dp[i][d] != -1) return dp[i][d]; + + int n = jobDifficulty.size(); + int maxi = jobDifficulty[i]; + if (d == 1) { + for (int j = i; j < n; j++) { + maxi = max(maxi, jobDifficulty[j]); + } + dp[i][d] = maxi; + return maxi; + } + + int res = INT_MAX / 2; + for (int j = i + 1; j < n; j++) { + res = min(res, maxi + dfs(j, d - 1, jobDifficulty)); + maxi = max(maxi, jobDifficulty[j]); + } + dp[i][d] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} jobDifficulty + * @param {number} d + * @return {number} + */ + minDifficulty(jobDifficulty, d) { + const n = jobDifficulty.length; + if (n < d) return -1; + + const dp = Array.from({ length: n }, () => Array(d + 1).fill(-1)); + + const dfs = (i, d) => { + if (dp[i][d] !== -1) return dp[i][d]; + + let maxi = jobDifficulty[i]; + if (d === 1) { + for (let j = i; j < n; j++) { + maxi = Math.max(maxi, jobDifficulty[j]); + } + dp[i][d] = maxi; + return maxi; + } + + let res = Infinity; + for (let j = i + 1; j < n; j++) { + res = Math.min(res, maxi + dfs(j, d - 1)); + maxi = Math.max(maxi, jobDifficulty[j]); + } + dp[i][d] = res; + return res; + }; + + return dfs(0, d); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * d)$ +* Space complexity: $O(n * d)$ + +> Where $n$ is the number of jobs and $d$ is the number of days. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minDifficulty(self, jobDifficulty: List[int], d: int) -> int: + if len(jobDifficulty) < d: + return -1 + + n = len(jobDifficulty) + dp = [[float("inf")] * (d + 1) for _ in range(n + 1)] + dp[n][0] = 0 + + for day in range(1, d + 1): + for i in range(n - 1, -1, -1): + maxi = 0 + for j in range(i, n - day + 1): + maxi = max(maxi, jobDifficulty[j]) + dp[i][day] = min(dp[i][day], maxi + dp[j + 1][day - 1]) + + return dp[0][d] +``` + +```java +public class Solution { + public int minDifficulty(int[] jobDifficulty, int d) { + int n = jobDifficulty.length; + if (n < d) return -1; + + int[][] dp = new int[n + 1][d + 1]; + for (int[] row : dp) Arrays.fill(row, Integer.MAX_VALUE / 2); + dp[n][0] = 0; + + for (int day = 1; day <= d; day++) { + for (int i = n - 1; i >= 0; i--) { + int maxi = 0; + for (int j = i; j <= n - day; j++) { + maxi = Math.max(maxi, jobDifficulty[j]); + dp[i][day] = Math.min(dp[i][day], maxi + dp[j + 1][day - 1]); + } + } + } + + return dp[0][d]; + } +} +``` + +```cpp +class Solution { +public: + int minDifficulty(vector& jobDifficulty, int d) { + int n = jobDifficulty.size(); + if (n < d) return -1; + + vector> dp(n + 1, vector(d + 1, INT_MAX / 2)); + dp[n][0] = 0; + + for (int day = 1; day <= d; day++) { + for (int i = n - 1; i >= 0; i--) { + int maxi = 0; + for (int j = i; j <= n - day; j++) { + maxi = max(maxi, jobDifficulty[j]); + dp[i][day] = min(dp[i][day], maxi + dp[j + 1][day - 1]); + } + } + } + + return dp[0][d]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} jobDifficulty + * @param {number} d + * @return {number} + */ + minDifficulty(jobDifficulty, d) { + const n = jobDifficulty.length; + if (n < d) return -1; + + const dp = Array.from({ length: n + 1 }, () => Array(d + 1).fill(Infinity)); + dp[n][0] = 0; + + for (let day = 1; day <= d; day++) { + for (let i = n - 1; i >= 0; i--) { + let maxi = 0; + for (let j = i; j <= n - day; j++) { + maxi = Math.max(maxi, jobDifficulty[j]); + dp[i][day] = Math.min(dp[i][day], maxi + dp[j + 1][day - 1]); + } + } + } + + return dp[0][d]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * d)$ +* Space complexity: $O(n * d)$ + +> Where $n$ is the number of jobs and $d$ is the number of days. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def minDifficulty(self, jobDifficulty: List[int], d: int) -> int: + if len(jobDifficulty) < d: + return -1 + + n = len(jobDifficulty) + dp = [float("inf")] * (n + 1) + dp[n] = 0 + + for day in range(1, d + 1): + for i in range(n - day + 1): + maxi = 0 + dp[i] = float("inf") + for j in range(i, n - day + 1): + maxi = max(maxi, jobDifficulty[j]) + dp[i] = min(dp[i], maxi + dp[j + 1]) + + return dp[0] +``` + +```java +public class Solution { + public int minDifficulty(int[] jobDifficulty, int d) { + int n = jobDifficulty.length; + if (n < d) return -1; + + int INF = Integer.MAX_VALUE / 2; + int[] dp = new int[n + 1]; + Arrays.fill(dp, INF); + dp[n] = 0; + + for (int day = 1; day <= d; day++) { + for (int i = 0; i <= n - day; i++) { + int maxi = 0; + dp[i] = INF; + for (int j = i; j <= n - day; j++) { + maxi = Math.max(maxi, jobDifficulty[j]); + dp[i] = Math.min(dp[i], maxi + dp[j + 1]); + } + } + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int minDifficulty(vector& jobDifficulty, int d) { + int n = jobDifficulty.size(); + if (n < d) return -1; + + int INF = INT_MAX / 2; + vector dp(n + 1, INF); + dp[n] = 0; + + for (int day = 1; day <= d; day++) { + for (int i = 0; i <= n - day; i++) { + int maxi = 0; + dp[i] = INF; + for (int j = i; j <= n - day; j++) { + maxi = max(maxi, jobDifficulty[j]); + dp[i] = min(dp[i], maxi + dp[j + 1]); + } + } + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} jobDifficulty + * @param {number} d + * @return {number} + */ + minDifficulty(jobDifficulty, d) { + const n = jobDifficulty.length; + if (n < d) return -1; + + let dp = new Array(n + 1).fill(Infinity); + dp[n] = 0; + + for (let day = 1; day <= d; day++) { + for (let i = 0; i <= n - day; i++) { + let maxi = 0; + dp[i] = Infinity; + for (let j = i; j <= n - day; j++) { + maxi = Math.max(maxi, jobDifficulty[j]); + dp[i] = Math.min(dp[i], maxi + dp[j + 1]); + } + } + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * d)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of jobs and $d$ is the number of days. + +--- + +## 5. Monotonic Decreasing Stack + +::tabs-start + +```python +class Solution: + def minDifficulty(self, jobDifficulty: List[int], d: int) -> int: + n = len(jobDifficulty) + if n < d: + return -1 + + dp = [float("inf")] * n + for day in range(1, d + 1): + next_dp = [float("inf")] * n + stack = [] + for i in range(day - 1, n): + next_dp[i] = dp[i - 1] + jobDifficulty[i] if i > 0 else jobDifficulty[i] + while stack and jobDifficulty[stack[-1]] <= jobDifficulty[i]: + j = stack.pop() + next_dp[i] = min(next_dp[i], next_dp[j] - jobDifficulty[j] + jobDifficulty[i]) + if stack: + next_dp[i] = min(next_dp[i], next_dp[stack[-1]]) + stack.append(i) + dp = next_dp + + return dp[-1] +``` + +```java +public class Solution { + public int minDifficulty(int[] jobDifficulty, int d) { + int n = jobDifficulty.length; + if (n < d) return -1; + + int[] dp = new int[n]; + Arrays.fill(dp, Integer.MAX_VALUE / 2); + + for (int day = 1; day <= d; day++) { + int[] nextDp = new int[n]; + Arrays.fill(nextDp, Integer.MAX_VALUE / 2); + Stack stack = new Stack<>(); + for (int i = day - 1; i < n; i++) { + nextDp[i] = (i > 0 ? dp[i - 1] : 0) + jobDifficulty[i]; + while (!stack.isEmpty() && jobDifficulty[stack.peek()] <= jobDifficulty[i]) { + int j = stack.pop(); + nextDp[i] = Math.min(nextDp[i], nextDp[j] - jobDifficulty[j] + jobDifficulty[i]); + } + if (!stack.isEmpty()) { + nextDp[i] = Math.min(nextDp[i], nextDp[stack.peek()]); + } + stack.add(i); + } + dp = nextDp; + } + + return dp[n - 1]; + } +} +``` + +```cpp +class Solution { +public: + int minDifficulty(vector& jobDifficulty, int d) { + int n = jobDifficulty.size(); + if (n < d) return -1; + + vector dp(n, INT_MAX / 2); + + for (int day = 1; day <= d; day++) { + vector nextDp(n, INT_MAX / 2); + stack st; + for (int i = day - 1; i < n; i++) { + nextDp[i] = (i > 0 ? dp[i - 1] : 0) + jobDifficulty[i]; + while (!st.empty() && jobDifficulty[st.top()] <= jobDifficulty[i]) { + int j = st.top(); st.pop(); + nextDp[i] = min(nextDp[i], nextDp[j] - jobDifficulty[j] + jobDifficulty[i]); + } + if (!st.empty()) { + nextDp[i] = min(nextDp[i], nextDp[st.top()]); + } + st.push(i); + } + dp = nextDp; + } + + return dp[n - 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} jobDifficulty + * @param {number} d + * @return {number} + */ + minDifficulty(jobDifficulty, d) { + const n = jobDifficulty.length; + if (n < d) return -1; + + let dp = Array(n).fill(Infinity); + + for (let day = 1; day <= d; day++) { + const nextDp = Array(n).fill(Infinity); + const stack = []; + for (let i = day - 1; i < n; i++) { + nextDp[i] = (i > 0 ? dp[i - 1] : 0) + jobDifficulty[i]; + while (stack.length > 0 && jobDifficulty[stack[stack.length - 1]] <= jobDifficulty[i]) { + const j = stack.pop(); + nextDp[i] = Math.min(nextDp[i], nextDp[j] - jobDifficulty[j] + jobDifficulty[i]); + } + if (stack.length > 0) { + nextDp[i] = Math.min(nextDp[i], nextDp[stack[stack.length - 1]]); + } + stack.push(i); + } + dp = nextDp; + } + + return dp[n - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * d)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of jobs and $d$ is the number of days. \ No newline at end of file diff --git a/articles/minimum-distance-between-bst-nodes.md b/articles/minimum-distance-between-bst-nodes.md new file mode 100644 index 000000000..d8cd6bda0 --- /dev/null +++ b/articles/minimum-distance-between-bst-nodes.md @@ -0,0 +1,840 @@ +## 1. Brute Force (DFS) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def minDiffInBST(self, root: Optional[TreeNode]) -> int: + def dfs(node): + if not node: + return float("inf") + res = dfs1(root, node) + res = min(res, dfs(node.left)) + res = min(res, dfs(node.right)) + return res + + def dfs1(root, node): + if not root: + return float("inf") + + res = float("inf") + if root != node: + res = abs(root.val - node.val) + res = min(res, dfs1(root.left, node)) + res = min(res, dfs1(root.right, node)) + return res + + return dfs(root) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int minDiffInBST(TreeNode root) { + return dfs(root, root); + } + + private int dfs(TreeNode root, TreeNode node) { + if (node == null) { + return Integer.MAX_VALUE; + } + int res = dfs1(root, node); + res = Math.min(res, dfs(root, node.left)); + res = Math.min(res, dfs(root, node.right)); + return res; + } + + private int dfs1(TreeNode root, TreeNode node) { + if (root == null) { + return Integer.MAX_VALUE; + } + int res = Integer.MAX_VALUE; + if (root != node) { + res = Math.abs(root.val - node.val); + } + res = Math.min(res, dfs1(root.left, node)); + res = Math.min(res, dfs1(root.right, node)); + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int minDiffInBST(TreeNode* root) { + return dfs(root, root); + } + +private: + int dfs(TreeNode* root, TreeNode* node) { + if (!node) { + return INT_MAX; + } + int res = dfs1(root, node); + res = min(res, dfs(root, node->left)); + res = min(res, dfs(root, node->right)); + return res; + } + + int dfs1(TreeNode* root, TreeNode* node) { + if (!root) { + return INT_MAX; + } + int res = INT_MAX; + if (root != node) { + res = abs(root->val - node->val); + } + res = min(res, dfs1(root->left, node)); + res = min(res, dfs1(root->right, node)); + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + minDiffInBST(root) { + const dfs = (node) => { + if (!node) { + return Infinity; + } + let res = dfs1(root, node); + res = Math.min(res, dfs(node.left)); + res = Math.min(res, dfs(node.right)); + return res; + }; + + const dfs1 = (root, node) => { + if (!root) { + return Infinity; + } + let res = Infinity; + if (root !== node) { + res = Math.abs(root.val - node.val); + } + res = Math.min(res, dfs1(root.left, node)); + res = Math.min(res, dfs1(root.right, node)); + return res; + }; + + return dfs(root); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Inorder Traversal + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def minDiffInBST(self, root: Optional[TreeNode]) -> int: + arr = [] + + def dfs(node): + if not node: + return + dfs(node.left) + arr.append(node.val) + dfs(node.right) + + dfs(root) + res = arr[1] - arr[0] + for i in range(2, len(arr)): + res = min(res, arr[i] - arr[i - 1]) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int minDiffInBST(TreeNode root) { + List arr = new ArrayList<>(); + + dfs(root, arr); + int res = arr.get(1) - arr.get(0); + for (int i = 2; i < arr.size(); i++) { + res = Math.min(res, arr.get(i) - arr.get(i - 1)); + } + return res; + } + + private void dfs(TreeNode node, List arr) { + if (node == null) { + return; + } + dfs(node.left, arr); + arr.add(node.val); + dfs(node.right, arr); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int minDiffInBST(TreeNode* root) { + vector arr; + dfs(root, arr); + + int res = arr[1] - arr[0]; + for (int i = 2; i < arr.size(); i++) { + res = min(res, arr[i] - arr[i - 1]); + } + return res; + } + +private: + void dfs(TreeNode* node, vector& arr) { + if (!node) return; + dfs(node->left, arr); + arr.push_back(node->val); + dfs(node->right, arr); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + minDiffInBST(root) { + const arr = []; + + const dfs = (node) => { + if (!node) return; + dfs(node.left); + arr.push(node.val); + dfs(node.right); + }; + + dfs(root); + let res = arr[1] - arr[0]; + for (let i = 2; i < arr.length; i++) { + res = Math.min(res, arr[i] - arr[i - 1]); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Inorder Traversal (Space Optimized) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def minDiffInBST(self, root: Optional[TreeNode]) -> int: + prev, res = None, float("inf") + + def dfs(node): + nonlocal prev, res + if not node: + return + + dfs(node.left) + if prev: + res = min(res, node.val - prev.val) + prev = node + dfs(node.right) + + dfs(root) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private TreeNode prev = null; + private int res = Integer.MAX_VALUE; + + public int minDiffInBST(TreeNode root) { + dfs(root); + return res; + } + + private void dfs(TreeNode node) { + if (node == null) return; + + dfs(node.left); + if (prev != null) { + res = Math.min(res, node.val - prev.val); + } + prev = node; + dfs(node.right); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int minDiffInBST(TreeNode* root) { + TreeNode* prev = nullptr; + int res = INT_MAX; + + dfs(root, prev, res); + return res; + } + +private: + void dfs(TreeNode* node, TreeNode*& prev, int& res) { + if (!node) return; + + dfs(node->left, prev, res); + if (prev) { + res = min(res, node->val - prev->val); + } + prev = node; + dfs(node->right, prev, res); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + minDiffInBST(root) { + let prev = null; + let res = Infinity; + + const dfs = (node) => { + if (!node) return; + + dfs(node.left); + if (prev !== null) { + res = Math.min(res, node.val - prev.val); + } + prev = node; + dfs(node.right); + }; + + dfs(root); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 4. Iterative DFS (Inorder Traversal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def minDiffInBST(self, root: Optional[TreeNode]) -> int: + stack, prev, res = [], None, float("inf") + cur = root + + while stack or cur: + while cur: + stack.append(cur) + cur = cur.left + + cur = stack.pop() + if prev: + res = min(res, cur.val - prev.val) + prev = cur + cur = cur.right + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int minDiffInBST(TreeNode root) { + Stack stack = new Stack<>(); + TreeNode prev = null; + int res = Integer.MAX_VALUE; + TreeNode cur = root; + + while (!stack.isEmpty() || cur != null) { + while (cur != null) { + stack.push(cur); + cur = cur.left; + } + + cur = stack.pop(); + if (prev != null) { + res = Math.min(res, cur.val - prev.val); + } + prev = cur; + cur = cur.right; + } + + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int minDiffInBST(TreeNode* root) { + stack st; + TreeNode* prev = nullptr; + TreeNode* cur = root; + int res = INT_MAX; + + while (!st.empty() || cur) { + while (cur) { + st.push(cur); + cur = cur->left; + } + + cur = st.top(); + st.pop(); + if (prev) { + res = min(res, cur->val - prev->val); + } + prev = cur; + cur = cur->right; + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + minDiffInBST(root) { + let stack = []; + let prev = null; + let res = Infinity; + let cur = root; + + while (stack.length > 0 || cur !== null) { + while (cur !== null) { + stack.push(cur); + cur = cur.left; + } + + cur = stack.pop(); + if (prev !== null) { + res = Math.min(res, cur.val - prev.val); + } + prev = cur; + cur = cur.right; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Morris Traversal + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def minDiffInBST(self, root: Optional[TreeNode]) -> int: + prevVal = res = float("inf") + cur = root + + while cur: + if not cur.left: + if prevVal != float("inf"): + res = min(res, cur.val - prevVal) + prevVal = cur.val + cur = cur.right + else: + prev = cur.left + while prev.right and prev.right != cur: + prev = prev.right + + if not prev.right: + prev.right = cur + cur = cur.left + else: + prev.right = None + if prevVal != float("inf"): + res = min(res, cur.val - prevVal) + prevVal = cur.val + cur = cur.right + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int minDiffInBST(TreeNode root) { + int prevVal = Integer.MAX_VALUE, res = Integer.MAX_VALUE; + TreeNode cur = root; + + while (cur != null) { + if (cur.left == null) { + if (prevVal != Integer.MAX_VALUE) { + res = Math.min(res, cur.val - prevVal); + } + prevVal = cur.val; + cur = cur.right; + } else { + TreeNode prev = cur.left; + while (prev.right != null && prev.right != cur) { + prev = prev.right; + } + + if (prev.right == null) { + prev.right = cur; + cur = cur.left; + } else { + prev.right = null; + if (prevVal != Integer.MAX_VALUE) { + res = Math.min(res, cur.val - prevVal); + } + prevVal = cur.val; + cur = cur.right; + } + } + } + + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int minDiffInBST(TreeNode* root) { + int prevVal = INT_MAX, res = INT_MAX; + TreeNode* cur = root; + + while (cur) { + if (!cur->left) { + if (prevVal != INT_MAX) { + res = min(res, cur->val - prevVal); + } + prevVal = cur->val; + cur = cur->right; + } else { + TreeNode* prev = cur->left; + while (prev->right && prev->right != cur) { + prev = prev->right; + } + + if (!prev->right) { + prev->right = cur; + cur = cur->left; + } else { + prev->right = nullptr; + if (prevVal != INT_MAX) { + res = min(res, cur->val - prevVal); + } + prevVal = cur->val; + cur = cur->right; + } + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + minDiffInBST(root) { + let prevVal = Infinity, res = Infinity; + let cur = root; + + while (cur !== null) { + if (cur.left === null) { + if (prevVal !== Infinity) { + res = Math.min(res, cur.val - prevVal); + } + prevVal = cur.val; + cur = cur.right; + } else { + let prev = cur.left; + while (prev.right !== null && prev.right !== cur) { + prev = prev.right; + } + + if (prev.right === null) { + prev.right = cur; + cur = cur.left; + } else { + prev.right = null; + if (prevVal !== Infinity) { + res = Math.min(res, cur.val - prevVal); + } + prevVal = cur.val; + cur = cur.right; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/minimum-falling-path-sum-ii.md b/articles/minimum-falling-path-sum-ii.md new file mode 100644 index 000000000..b43db319c --- /dev/null +++ b/articles/minimum-falling-path-sum-ii.md @@ -0,0 +1,854 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, grid: List[List[int]]) -> int: + N = len(grid) + + def helper(r, c): + if r == N - 1: + return grid[r][c] + res = float("inf") + for next_col in range(N): + if c != next_col: + res = min(res, grid[r][c] + helper(r + 1, next_col)) + return res + + res = float("inf") + for c in range(N): + res = min(res, helper(0, c)) + return res +``` + +```java +public class Solution { + private int helper(int[][] grid, int r, int c) { + int N = grid.length; + if (r == N - 1) { + return grid[r][c]; + } + int res = Integer.MAX_VALUE; + for (int nextCol = 0; nextCol < N; nextCol++) { + if (c != nextCol) { + res = Math.min(res, grid[r][c] + helper(grid, r + 1, nextCol)); + } + } + return res; + } + + public int minFallingPathSum(int[][] grid) { + int N = grid.length; + int res = Integer.MAX_VALUE; + for (int c = 0; c < N; c++) { + res = Math.min(res, helper(grid, 0, c)); + } + return res; + } +} +``` + +```cpp +class Solution { + int helper(vector>& grid, int r, int c) { + int N = grid.size(); + if (r == N - 1) { + return grid[r][c]; + } + int res = INT_MAX; + for (int nextCol = 0; nextCol < N; nextCol++) { + if (c != nextCol) { + res = min(res, grid[r][c] + helper(grid, r + 1, nextCol)); + } + } + return res; + } + +public: + int minFallingPathSum(vector>& grid) { + int N = grid.size(); + int res = INT_MAX; + for (int c = 0; c < N; c++) { + res = min(res, helper(grid, 0, c)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minFallingPathSum(grid) { + const N = grid.length; + + const helper = (r, c) => { + if (r === N - 1) return grid[r][c]; + let res = Infinity; + for (let nextCol = 0; nextCol < N; nextCol++) { + if (c !== nextCol) { + res = Math.min(res, grid[r][c] + helper(r + 1, nextCol)); + } + } + return res; + }; + + let res = Infinity; + for (let c = 0; c < N; c++) { + res = Math.min(res, helper(0, c)); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, grid: List[List[int]]) -> int: + N = len(grid) + cache = {} + + def helper(r, c): + if r == N - 1: + return grid[r][c] + if (r, c) in cache: + return cache[(r, c)] + + res = float("inf") + for next_col in range(N): + if c != next_col: + res = min(res, grid[r][c] + helper(r + 1, next_col)) + cache[(r, c)] = res + return res + + res = float("inf") + for c in range(N): + res = min(res, helper(0, c)) + return res +``` + +```java +public class Solution { + private int[][] memo; + + private int helper(int[][] grid, int r, int c) { + int N = grid.length; + if (r == N - 1) { + return grid[r][c]; + } + if (memo[r][c] != Integer.MIN_VALUE) { + return memo[r][c]; + } + + int res = Integer.MAX_VALUE; + for (int nextCol = 0; nextCol < N; nextCol++) { + if (c != nextCol) { + res = Math.min(res, grid[r][c] + helper(grid, r + 1, nextCol)); + } + } + memo[r][c] = res; + return res; + } + + public int minFallingPathSum(int[][] grid) { + int N = grid.length; + memo = new int[N][N]; + for (int[] row : memo) { + Arrays.fill(row, Integer.MIN_VALUE); + } + int res = Integer.MAX_VALUE; + for (int c = 0; c < N; c++) { + res = Math.min(res, helper(grid, 0, c)); + } + return res; + } +} +``` + +```cpp +class Solution { + vector> memo; + + int helper(vector>& grid, int r, int c) { + int N = grid.size(); + if (r == N - 1) { + return grid[r][c]; + } + if (memo[r][c] != INT_MIN) { + return memo[r][c]; + } + int res = INT_MAX; + for (int nextCol = 0; nextCol < N; nextCol++) { + if (c != nextCol) { + res = min(res, grid[r][c] + helper(grid, r + 1, nextCol)); + } + } + memo[r][c] = res; + return res; + } + +public: + int minFallingPathSum(vector>& grid) { + int N = grid.size(); + memo.assign(N, vector(N, INT_MIN)); + int res = INT_MAX; + for (int c = 0; c < N; c++) { + res = min(res, helper(grid, 0, c)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minFallingPathSum(grid) { + const N = grid.length; + const memo = Array.from({ length: N }, () => Array(N).fill(-Infinity)); + + const helper = (r, c) => { + if (r === N - 1) return grid[r][c]; + if (memo[r][c] !== -Infinity) return memo[r][c]; + let res = Infinity; + for (let nextCol = 0; nextCol < N; nextCol++) { + if (c !== nextCol) { + res = Math.min(res, grid[r][c] + helper(r + 1, nextCol)); + } + } + memo[r][c] = res; + return res; + }; + + let res = Infinity; + for (let c = 0; c < N; c++) { + res = Math.min(res, helper(0, c)); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, grid: List[List[int]]) -> int: + N = len(grid) + dp = [[float("inf")] * N for _ in range(N)] + + for c in range(N): + dp[N - 1][c] = grid[N - 1][c] + + for r in range(N - 2, -1, -1): + for c in range(N): + for next_col in range(N): + if c != next_col: + dp[r][c] = min(dp[r][c], grid[r][c] + dp[r + 1][next_col]) + + return min(dp[0]) +``` + +```java +public class Solution { + public int minFallingPathSum(int[][] grid) { + int N = grid.length; + int[][] dp = new int[N][N]; + + for (int c = 0; c < N; c++) { + dp[N - 1][c] = grid[N - 1][c]; + } + + for (int r = N - 2; r >= 0; r--) { + for (int c = 0; c < N; c++) { + dp[r][c] = Integer.MAX_VALUE; + for (int nextCol = 0; nextCol < N; nextCol++) { + if (c != nextCol) { + dp[r][c] = Math.min(dp[r][c], grid[r][c] + dp[r + 1][nextCol]); + } + } + } + } + + int res = Integer.MAX_VALUE; + for (int c = 0; c < N; c++) { + res = Math.min(res, dp[0][c]); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minFallingPathSum(vector>& grid) { + int N = grid.size(); + vector> dp(N, vector(N, INT_MAX)); + + for (int c = 0; c < N; c++) { + dp[N - 1][c] = grid[N - 1][c]; + } + + for (int r = N - 2; r >= 0; r--) { + for (int c = 0; c < N; c++) { + dp[r][c] = INT_MAX; + for (int nextCol = 0; nextCol < N; nextCol++) { + if (c != nextCol) { + dp[r][c] = min(dp[r][c], grid[r][c] + dp[r + 1][nextCol]); + } + } + } + } + + int res = INT_MAX; + for (int c = 0; c < N; c++) { + res = min(res, dp[0][c]); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minFallingPathSum(grid) { + const N = grid.length; + const dp = Array.from({ length: N }, () => Array(N).fill(Infinity)); + + for (let c = 0; c < N; c++) { + dp[N - 1][c] = grid[N - 1][c]; + } + + for (let r = N - 2; r >= 0; r--) { + for (let c = 0; c < N; c++) { + for (let nextCol = 0; nextCol < N; nextCol++) { + if (c !== nextCol) { + dp[r][c] = Math.min(dp[r][c], grid[r][c] + dp[r + 1][nextCol]); + } + } + } + } + + return Math.min(...dp[0]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, grid: List[List[int]]) -> int: + N = len(grid) + dp = grid[0] + + for r in range(1, N): + next_dp = [float("inf")] * N + for curr_c in range(N): + for prev_c in range(N): + if prev_c != curr_c: + next_dp[curr_c] = min( + next_dp[curr_c], + grid[r][curr_c] + dp[prev_c] + ) + dp = next_dp + + return min(dp) +``` + +```java +public class Solution { + public int minFallingPathSum(int[][] grid) { + int N = grid.length; + int[] dp = grid[0]; + + for (int r = 1; r < N; r++) { + int[] nextDp = new int[N]; + Arrays.fill(nextDp, Integer.MAX_VALUE); + + for (int currC = 0; currC < N; currC++) { + for (int prevC = 0; prevC < N; prevC++) { + if (prevC != currC) { + nextDp[currC] = Math.min( + nextDp[currC], + grid[r][currC] + dp[prevC] + ); + } + } + } + dp = nextDp; + } + + int res = Integer.MAX_VALUE; + for (int i : dp) res = Math.min(res, i); + return res; + } +} +``` + +```cpp +class Solution { +public: + int minFallingPathSum(vector>& grid) { + int N = grid.size(); + vector dp = grid[0]; + + for (int r = 1; r < N; r++) { + vector nextDp(N, INT_MAX); + for (int currC = 0; currC < N; currC++) { + for (int prevC = 0; prevC < N; prevC++) { + if (prevC != currC) { + nextDp[currC] = min( + nextDp[currC], + grid[r][currC] + dp[prevC] + ); + } + } + } + dp = nextDp; + } + + return *min_element(dp.begin(), dp.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minFallingPathSum(grid) { + const N = grid.length; + let dp = grid[0]; + + for (let r = 1; r < N; r++) { + const nextDp = Array(N).fill(Infinity); + for (let currC = 0; currC < N; currC++) { + for (let prevC = 0; prevC < N; prevC++) { + if (prevC !== currC) { + nextDp[currC] = Math.min( + nextDp[currC], + grid[r][currC] + dp[prevC] + ); + } + } + } + dp = nextDp; + } + + return Math.min(...dp); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n)$ + +--- + +## 5. Dynamic Programming (Time Optimized) + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, grid: List[List[int]]) -> int: + def get_min_two(row): + two_smallest = [] + for val, idx in row: + if len(two_smallest) < 2: + two_smallest.append((val, idx)) + elif two_smallest[1][0] > val: + two_smallest.pop() + two_smallest.append((val, idx)) + two_smallest.sort() + return two_smallest + + N = len(grid) + first_row = [(val, idx) for idx, val in enumerate(grid[0])] + dp = get_min_two(first_row) + + for r in range(1, N): + next_dp = [] + for curr_c in range(N): + curr_val = grid[r][curr_c] + min_val = float("inf") + for prev_val, prev_c in dp: + if curr_c != prev_c: + min_val = min(min_val, curr_val + prev_val) + next_dp.append((min_val, curr_c)) + dp = get_min_two(next_dp) + + return min(val for val, idx in dp) +``` + +```java +public class Solution { + public List getMinTwo(List row) { + List twoSmallest = new ArrayList<>(); + for (int[] entry : row) { + if (twoSmallest.size() < 2) { + twoSmallest.add(entry); + } else if (twoSmallest.get(1)[0] > entry[0]) { + twoSmallest.remove(1); + twoSmallest.add(entry); + } + twoSmallest.sort((a, b) -> a[0] - b[0]); + } + return twoSmallest; + } + + public int minFallingPathSum(int[][] grid) { + int N = grid.length; + + List firstRow = new ArrayList<>(); + for (int i = 0; i < grid[0].length; i++) { + firstRow.add(new int[]{grid[0][i], i}); + } + + List dp = getMinTwo(firstRow); + + for (int r = 1; r < N; r++) { + List nextDp = new ArrayList<>(); + for (int c = 0; c < grid[0].length; c++) { + int currVal = grid[r][c]; + int minVal = Integer.MAX_VALUE; + for (int[] prev : dp) { + if (prev[1] != c) { + minVal = Math.min(minVal, currVal + prev[0]); + } + } + nextDp.add(new int[]{minVal, c}); + } + dp = getMinTwo(nextDp); + } + + return dp.stream().mapToInt(a -> a[0]).min().getAsInt(); + } +} +``` + +```cpp +class Solution { +public: + int minFallingPathSum(vector>& grid) { + int N = grid.size(); + + auto getMinTwo = [](vector>& row) { + vector> twoSmallest; + for (auto& entry : row) { + if (twoSmallest.size() < 2) { + twoSmallest.push_back(entry); + } else if (twoSmallest[1].first > entry.first) { + twoSmallest.pop_back(); + twoSmallest.push_back(entry); + } + sort(twoSmallest.begin(), twoSmallest.end()); + } + return twoSmallest; + }; + + vector> firstRow; + for (int i = 0; i < grid[0].size(); i++) { + firstRow.push_back({grid[0][i], i}); + } + + vector> dp = getMinTwo(firstRow); + + for (int r = 1; r < N; r++) { + vector> nextDp; + for (int c = 0; c < grid[0].size(); c++) { + int currVal = grid[r][c]; + int minVal = INT_MAX; + for (auto& prev : dp) { + if (prev.second != c) { + minVal = min(minVal, currVal + prev.first); + } + } + nextDp.push_back({minVal, c}); + } + dp = getMinTwo(nextDp); + } + + int result = INT_MAX; + for (auto& entry : dp) { + result = min(result, entry.first); + } + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minFallingPathSum(grid) { + const N = grid.length; + + const getMinTwo = (row) => { + const twoSmallest = []; + for (const [val, idx] of row) { + if (twoSmallest.length < 2) { + twoSmallest.push([val, idx]); + } else if (twoSmallest[1][0] > val) { + twoSmallest.pop(); + twoSmallest.push([val, idx]); + } + twoSmallest.sort((a, b) => a[0] - b[0]); + } + return twoSmallest; + }; + + const firstRow = grid[0].map((val, idx) => [val, idx]); + let dp = getMinTwo(firstRow); + + for (let r = 1; r < N; r++) { + const nextDp = []; + for (let c = 0; c < grid[0].length; c++) { + const currVal = grid[r][c]; + let minVal = Infinity; + for (const [prevVal, prevC] of dp) { + if (c !== prevC) { + minVal = Math.min(minVal, currVal + prevVal); + } + } + nextDp.push([minVal, c]); + } + dp = getMinTwo(nextDp); + } + + return Math.min(...dp.map(([val]) => val)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 6. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, grid: List[List[int]]) -> int: + n = len(grid) + if n == 1: + return grid[0][0] + + dp_idx1 = dp_idx2 = -1 + dp_val1 = dp_val2 = 0 + + for i in range(n): + nextDp_idx1 = nextDp_idx2 = -1 + nextDp_val1 = nextDp_val2 = float("inf") + + for j in range(n): + cur = dp_val1 if j != dp_idx1 else dp_val2 + cur += grid[i][j] + + if nextDp_idx1 == -1 or cur < nextDp_val1: + nextDp_idx2, nextDp_val2 = nextDp_idx1, nextDp_val1 + nextDp_idx1, nextDp_val1 = j, cur + elif nextDp_idx2 == -1 or cur < nextDp_val2: + nextDp_idx2, nextDp_val2 = j, cur + + dp_idx1, dp_idx2, dp_val1, dp_val2 = nextDp_idx1, nextDp_idx2, nextDp_val1, nextDp_val2 + + return dp_val1 +``` + +```java +public class Solution { + public int minFallingPathSum(int[][] grid) { + int n = grid.length; + if (n == 1) { + return grid[0][0]; + } + + int dpIdx1 = -1, dpIdx2 = -1; + int dpVal1 = 0, dpVal2 = 0; + + for (int i = 0; i < n; i++) { + int nextDpIdx1 = -1, nextDpIdx2 = -1; + int nextDpVal1 = Integer.MAX_VALUE, nextDpVal2 = Integer.MAX_VALUE; + + for (int j = 0; j < n; j++) { + int cur = (j != dpIdx1) ? dpVal1 : dpVal2; + cur += grid[i][j]; + + if (nextDpIdx1 == -1 || cur < nextDpVal1) { + nextDpIdx2 = nextDpIdx1; + nextDpVal2 = nextDpVal1; + nextDpIdx1 = j; + nextDpVal1 = cur; + } else if (nextDpIdx2 == -1 || cur < nextDpVal2) { + nextDpIdx2 = j; + nextDpVal2 = cur; + } + } + + dpIdx1 = nextDpIdx1; + dpIdx2 = nextDpIdx2; + dpVal1 = nextDpVal1; + dpVal2 = nextDpVal2; + } + + return dpVal1; + } +} +``` + +```cpp +class Solution { +public: + int minFallingPathSum(vector>& grid) { + int n = grid.size(); + if (n == 1) { + return grid[0][0]; + } + + int dpIdx1 = -1, dpIdx2 = -1; + int dpVal1 = 0, dpVal2 = 0; + + for (int i = 1; i < n; i++) { + int nextDpIdx1 = -1, nextDpIdx2 = -1; + int nextDpVal1 = INT_MAX, nextDpVal2 = INT_MAX; + + for (int j = 0; j < n; j++) { + int cur = (j != dpIdx1) ? dpVal1 : dpVal2; + cur += grid[i][j]; + + if (nextDpIdx1 == -1 || cur < nextDpVal1) { + nextDpIdx2 = nextDpIdx1; + nextDpVal2 = nextDpVal1; + nextDpIdx1 = j; + nextDpVal1 = cur; + } else if (nextDpIdx2 == -1 || cur < nextDpVal2) { + nextDpIdx2 = j; + nextDpVal2 = cur; + } + } + + dpIdx1 = nextDpIdx1; + dpIdx2 = nextDpIdx2; + dpVal1 = nextDpVal1; + dpVal2 = nextDpVal2; + } + + return dpVal1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minFallingPathSum(grid) { + const n = grid.length; + if (n === 1) return grid[0][0]; + + let dpIdx1 = -1, dpIdx2 = -1; + let dpVal1 = 0, dpVal2 = 0; + + for (let i = 0; i < n; i++) { + let nextDpIdx1 = -1, nextDpIdx2 = -1; + let nextDpVal1 = Infinity, nextDpVal2 = Infinity; + + for (let j = 0; j < n; j++) { + let cur = (j !== dpIdx1) ? dpVal1 : dpVal2; + cur += grid[i][j]; + + if (nextDpIdx1 === -1 || cur < nextDpVal1) { + nextDpIdx2 = nextDpIdx1; + nextDpVal2 = nextDpVal1; + nextDpIdx1 = j; + nextDpVal1 = cur; + } else if (nextDpIdx2 === -1 || cur < nextDpVal2) { + nextDpIdx2 = j; + nextDpVal2 = cur; + } + } + + dpIdx1 = nextDpIdx1; + dpIdx2 = nextDpIdx2; + dpVal1 = nextDpVal1; + dpVal2 = nextDpVal2; + } + + return dpVal1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/minimum-falling-path-sum.md b/articles/minimum-falling-path-sum.md new file mode 100644 index 000000000..c12290d15 --- /dev/null +++ b/articles/minimum-falling-path-sum.md @@ -0,0 +1,449 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, matrix: List[List[int]]) -> int: + N = len(matrix) + + def dfs(r, c): + if r == N: + return 0 + if c < 0 or c >= N: + return float("inf") + return matrix[r][c] + min( + dfs(r + 1, c - 1), + dfs(r + 1, c), + dfs(r + 1, c + 1) + ) + + res = float("inf") + for c in range(N): + res = min(res, dfs(0, c)) + return res +``` + +```java +public class Solution { + private int dfs(int r, int c, int[][] matrix, int N) { + if (r == N) return 0; + if (c < 0 || c >= N) return Integer.MAX_VALUE; + return matrix[r][c] + Math.min( + Math.min(dfs(r + 1, c - 1, matrix, N), dfs(r + 1, c, matrix, N)), + dfs(r + 1, c + 1, matrix, N) + ); + } + + public int minFallingPathSum(int[][] matrix) { + int N = matrix.length; + int res = Integer.MAX_VALUE; + for (int c = 0; c < N; c++) { + res = Math.min(res, dfs(0, c, matrix, N)); + } + return res; + } +} +``` + +```cpp +class Solution { +private: + int dfs(int r, int c, vector>& matrix, int N) { + if (r == N) return 0; + if (c < 0 || c >= N) return INT_MAX; + return matrix[r][c] + min({ + dfs(r + 1, c - 1, matrix, N), + dfs(r + 1, c, matrix, N), + dfs(r + 1, c + 1, matrix, N) + }); + } + +public: + int minFallingPathSum(vector>& matrix) { + int N = matrix.size(); + int res = INT_MAX; + for (int c = 0; c < N; c++) { + res = min(res, dfs(0, c, matrix, N)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number} + */ + minFallingPathSum(matrix) { + const N = matrix.length; + + const dfs = (r, c) => { + if (r === N) return 0; + if (c < 0 || c >= N) return Infinity; + return matrix[r][c] + Math.min( + dfs(r + 1, c - 1), + dfs(r + 1, c), + dfs(r + 1, c + 1) + ); + }; + + let res = Infinity; + for (let c = 0; c < N; c++) { + res = Math.min(res, dfs(0, c)); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(3 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, matrix: List[List[int]]) -> int: + N = len(matrix) + cache = {} + + def dfs(r, c): + if r == N: + return 0 + if c < 0 or c >= N: + return float("inf") + if (r, c) in cache: + return cache[(r, c)] + cache[(r, c)] = matrix[r][c] + min( + dfs(r + 1, c - 1), + dfs(r + 1, c), + dfs(r + 1, c + 1) + ) + return cache[(r, c)] + + res = float("inf") + for c in range(N): + res = min(res, dfs(0, c)) + return res +``` + +```java +public class Solution { + private int[][] cache; + + private int dfs(int r, int c, int[][] matrix, int N) { + if (r == N) return 0; + if (c < 0 || c >= N) return Integer.MAX_VALUE; + if (cache[r][c] != Integer.MIN_VALUE) return cache[r][c]; + + cache[r][c] = matrix[r][c] + Math.min( + Math.min(dfs(r + 1, c - 1, matrix, N), dfs(r + 1, c, matrix, N)), + dfs(r + 1, c + 1, matrix, N) + ); + return cache[r][c]; + } + + public int minFallingPathSum(int[][] matrix) { + int N = matrix.length; + cache = new int[N][N]; + for (int[] row : cache) { + Arrays.fill(row, Integer.MIN_VALUE); + } + + int res = Integer.MAX_VALUE; + for (int c = 0; c < N; c++) { + res = Math.min(res, dfs(0, c, matrix, N)); + } + return res; + } +} +``` + +```cpp +class Solution { +private: + vector> cache; + + int dfs(int r, int c, vector>& matrix, int N) { + if (r == N) return 0; + if (c < 0 || c >= N) return INT_MAX; + if (cache[r][c] != INT_MIN) return cache[r][c]; + + cache[r][c] = matrix[r][c] + min({ + dfs(r + 1, c - 1, matrix, N), + dfs(r + 1, c, matrix, N), + dfs(r + 1, c + 1, matrix, N) + }); + return cache[r][c]; + } + +public: + int minFallingPathSum(vector>& matrix) { + int N = matrix.size(); + cache = vector>(N, vector(N, INT_MIN)); + + int res = INT_MAX; + for (int c = 0; c < N; c++) { + res = min(res, dfs(0, c, matrix, N)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number} + */ + minFallingPathSum(matrix) { + const N = matrix.length; + const cache = Array.from({ length: N }, () => Array(N).fill(null)); + + const dfs = (r, c) => { + if (r === N) return 0; + if (c < 0 || c >= N) return Infinity; + if (cache[r][c] !== null) return cache[r][c]; + + cache[r][c] = matrix[r][c] + Math.min( + dfs(r + 1, c - 1), + dfs(r + 1, c), + dfs(r + 1, c + 1) + ); + return cache[r][c]; + }; + + let res = Infinity; + for (let c = 0; c < N; c++) { + res = Math.min(res, dfs(0, c)); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * n)$ +* Space complexity: $O(n * n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, matrix: List[List[int]]) -> int: + N = len(matrix) + dp = matrix[0][:] + + for r in range(1, N): + leftUp = float('inf') + for c in range(N): + midUp = dp[c] + rightUp = dp[c + 1] if c < N - 1 else float('inf') + dp[c] = matrix[r][c] + min(midUp, leftUp, rightUp) + leftUp = midUp + + return min(dp) +``` + +```java +public class Solution { + public int minFallingPathSum(int[][] matrix) { + int N = matrix.length; + int[] dp = new int[N]; + for (int c = 0; c < N; c++) { + dp[c] = matrix[0][c]; + } + + for (int r = 1; r < N; r++) { + int leftUp = Integer.MAX_VALUE; + for (int c = 0; c < N; c++) { + int midUp = dp[c]; + int rightUp = (c < N - 1) ? dp[c + 1] : Integer.MAX_VALUE; + dp[c] = matrix[r][c] + Math.min(midUp, Math.min(leftUp, rightUp)); + leftUp = midUp; + } + } + + int ans = Integer.MAX_VALUE; + for (int val : dp) { + ans = Math.min(ans, val); + } + return ans; + } +} +``` + +```cpp +class Solution { +public: + int minFallingPathSum(vector>& matrix) { + int N = matrix.size(); + vector dp(N); + + for (int c = 0; c < N; c++) { + dp[c] = matrix[0][c]; + } + + for (int r = 1; r < N; r++) { + int leftUp = INT_MAX; + for (int c = 0; c < N; c++) { + int midUp = dp[c]; + int rightUp = (c < N - 1) ? dp[c + 1] : INT_MAX; + dp[c] = matrix[r][c] + min(midUp, min(leftUp, rightUp)); + leftUp = midUp; + } + } + + int ans = INT_MAX; + for (int val : dp) { + ans = min(ans, val); + } + return ans; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number} + */ + minFallingPathSum(matrix) { + const N = matrix.length; + const dp = matrix[0].slice(); + + for (let r = 1; r < N; r++) { + let leftUp = Infinity; + for (let c = 0; c < N; c++) { + const midUp = dp[c]; + const rightUp = (c < N - 1) ? dp[c + 1] : Infinity; + dp[c] = matrix[r][c] + Math.min(midUp, leftUp, rightUp); + leftUp = midUp; + } + } + + return Math.min(...dp); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (In-Place) + +::tabs-start + +```python +class Solution: + def minFallingPathSum(self, matrix: List[List[int]]) -> int: + N = len(matrix) + + for r in range(1, N): + for c in range(N): + mid = matrix[r - 1][c] + left = matrix[r - 1][c - 1] if c > 0 else float("inf") + right = matrix[r - 1][c + 1] if c < N - 1 else float("inf") + matrix[r][c] = matrix[r][c] + min(mid, left, right) + + return min(matrix[-1]) +``` + +```java +public class Solution { + public int minFallingPathSum(int[][] matrix) { + int N = matrix.length; + + for (int r = 1; r < N; r++) { + for (int c = 0; c < N; c++) { + int mid = matrix[r - 1][c]; + int left = (c > 0) ? matrix[r - 1][c - 1] : Integer.MAX_VALUE; + int right = (c < N - 1) ? matrix[r - 1][c + 1] : Integer.MAX_VALUE; + matrix[r][c] = matrix[r][c] + Math.min(mid, Math.min(left, right)); + } + } + + int res = Integer.MAX_VALUE; + for (int c = 0; c < N; c++) { + res = Math.min(res, matrix[N - 1][c]); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minFallingPathSum(vector>& matrix) { + int N = matrix.size(); + + for (int r = 1; r < N; r++) { + for (int c = 0; c < N; c++) { + int mid = matrix[r - 1][c]; + int left = (c > 0) ? matrix[r - 1][c - 1] : INT_MAX; + int right = (c < N - 1) ? matrix[r - 1][c + 1] : INT_MAX; + matrix[r][c] = matrix[r][c] + min({mid, left, right}); + } + } + + return *min_element(matrix[N - 1].begin(), matrix[N - 1].end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number} + */ + minFallingPathSum(matrix) { + const N = matrix.length; + + for (let r = 1; r < N; r++) { + for (let c = 0; c < N; c++) { + const mid = matrix[r - 1][c]; + const left = c > 0 ? matrix[r - 1][c - 1] : Infinity; + const right = c < N - 1 ? matrix[r - 1][c + 1] : Infinity; + matrix[r][c] = matrix[r][c] + Math.min(mid, left, right); + } + } + + return Math.min(...matrix[N - 1]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/minimum-fuel-cost-to-report-to-the-capital.md b/articles/minimum-fuel-cost-to-report-to-the-capital.md new file mode 100644 index 000000000..77dda576e --- /dev/null +++ b/articles/minimum-fuel-cost-to-report-to-the-capital.md @@ -0,0 +1,299 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def minimumFuelCost(self, roads: list[list[int]], seats: int) -> int: + adj = defaultdict(list) + for src, dst in roads: + adj[src].append(dst) + adj[dst].append(src) + + res = 0 + def dfs(node, parent): + nonlocal res + passengers = 0 + for child in adj[node]: + if child != parent: + p = dfs(child, node) + passengers += p + res += ceil(p / seats) + return passengers + 1 + + dfs(0, -1) + return res +``` + +```java +public class Solution { + private List[] adj; + private long res = 0; + + public long minimumFuelCost(int[][] roads, int seats) { + int n = roads.length + 1; + adj = new ArrayList[n]; + + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + + for (int[] road : roads) { + adj[road[0]].add(road[1]); + adj[road[1]].add(road[0]); + } + + dfs(0, -1, seats); + return res; + } + + private int dfs(int node, int parent, int seats) { + int passengers = 0; + for (int child : adj[node]) { + if (child != parent) { + int p = dfs(child, node, seats); + passengers += p; + res += Math.ceil((double) p / seats); + } + } + return passengers + 1; + } +} +``` + +```cpp +class Solution { +private: + vector> adj; + long long res = 0; + +public: + long long minimumFuelCost(vector>& roads, int seats) { + int n = roads.size() + 1; + adj.resize(n); + + for (auto& road : roads) { + adj[road[0]].push_back(road[1]); + adj[road[1]].push_back(road[0]); + } + + dfs(0, -1, seats); + return res; + } + +private: + int dfs(int node, int parent, int seats) { + int passengers = 0; + for (int child : adj[node]) { + if (child != parent) { + int p = dfs(child, node, seats); + passengers += p; + res += ceil((double) p / seats); + } + } + return passengers + 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} roads + * @param {number} seats + * @return {number} + */ + minimumFuelCost(roads, seats) { + const n = roads.length + 1; + const adj = Array.from({ length: n }, () => []); + let res = 0; + + for (const [src, dst] of roads) { + adj[src].push(dst); + adj[dst].push(src); + } + + const dfs = (node, parent) => { + let passengers = 0; + for (const child of adj[node]) { + if (child !== parent) { + let p = dfs(child, node); + passengers += p; + res += Math.ceil(p / seats); + } + } + return passengers + 1; + }; + + dfs(0, -1); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def minimumFuelCost(self, roads: list[list[int]], seats: int) -> int: + n = len(roads) + 1 + adj = [[] for _ in range(n)] + indegree = [0] * n + passengers = [1] * n + res = 0 + + for src, dst in roads: + adj[src].append(dst) + adj[dst].append(src) + indegree[src] += 1 + indegree[dst] += 1 + + q = deque() + for i in range(1, n): + if indegree[i] == 1: + q.append(i) + + while q: + node = q.popleft() + res += math.ceil(passengers[node] / seats) + for parent in adj[node]: + indegree[parent] -= 1 + if indegree[parent] == 1 and parent != 0: + q.append(parent) + passengers[parent] += passengers[node] + + return res +``` + +```java +public class Solution { + public long minimumFuelCost(int[][] roads, int seats) { + int n = roads.length + 1; + List[] adj = new ArrayList[n]; + int[] indegree = new int[n]; + int[] passengers = new int[n]; + Arrays.fill(passengers, 1); + long res = 0; + + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + + for (int[] road : roads) { + int src = road[0], dst = road[1]; + adj[src].add(dst); + adj[dst].add(src); + indegree[src]++; + indegree[dst]++; + } + + Queue q = new LinkedList<>(); + for (int i = 1; i < n; i++) { + if (indegree[i] == 1) q.offer(i); + } + + while (!q.isEmpty()) { + int node = q.poll(); + res += (int) Math.ceil((double) passengers[node] / seats); + for (int parent : adj[node]) { + if (--indegree[parent] == 1 && parent != 0) q.offer(parent); + passengers[parent] += passengers[node]; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long minimumFuelCost(vector>& roads, int seats) { + int n = roads.size() + 1; + vector> adj(n); + vector indegree(n, 0), passengers(n, 1); + long long res = 0; + + for (auto& road : roads) { + int src = road[0], dst = road[1]; + adj[src].push_back(dst); + adj[dst].push_back(src); + indegree[src]++; + indegree[dst]++; + } + + queue q; + for (int i = 1; i < n; i++) { + if (indegree[i] == 1) q.push(i); + } + + while (!q.empty()) { + int node = q.front();q.pop(); + res += ceil((double) passengers[node] / seats); + for (int parent : adj[node]) { + if (--indegree[parent] == 1 && parent != 0) q.push(parent); + passengers[parent] += passengers[node]; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} roads + * @param {number} seats + * @return {number} + */ + minimumFuelCost(roads, seats) { + const n = roads.length + 1; + const adj = Array.from({ length: n }, () => []); + const indegree = new Array(n).fill(0); + const passengers = new Array(n).fill(1); + let res = 0; + + for (const [src, dst] of roads) { + adj[src].push(dst); + adj[dst].push(src); + indegree[src] += 1; + indegree[dst] += 1; + } + + const q = new Queue(); + for (let i = 1; i < n; i++) { + if (indegree[i] === 1) q.push(i); + } + + while (!q.isEmpty()) { + const node = q.pop(); + res += Math.ceil(passengers[node] / seats); + for (const parent of adj[node]) { + if (--indegree[parent] === 1 && parent !== 0) q.push(parent); + passengers[parent] += passengers[node]; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/minimum-height-trees.md b/articles/minimum-height-trees.md new file mode 100644 index 000000000..19f63684e --- /dev/null +++ b/articles/minimum-height-trees.md @@ -0,0 +1,847 @@ +## 1. Brute Force (DFS) + +::tabs-start + +```python +class Solution: + def findMinHeightTrees(self, n: int, edges: List[List[int]]) -> List[int]: + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + adj[v].append(u) + + def dfs(node, parent): + hgt = 0 + for nei in adj[node]: + if nei == parent: + continue + hgt = max(hgt, 1 + dfs(nei, node)) + return hgt + + minHgt = n + res = [] + for i in range(n): + curHgt = dfs(i, -1) + if curHgt == minHgt: + res.append(i) + elif curHgt < minHgt: + res = [i] + minHgt = curHgt + + return res +``` + +```java +public class Solution { + private List> adj; + + public List findMinHeightTrees(int n, int[][] edges) { + adj = new ArrayList<>(); + for (int i = 0; i < n; i++) { + adj.add(new ArrayList<>()); + } + for (int[] edge : edges) { + adj.get(edge[0]).add(edge[1]); + adj.get(edge[1]).add(edge[0]); + } + + int minHgt = n; + List result = new ArrayList<>(); + for (int i = 0; i < n; i++) { + int curHgt = dfs(i, -1); + if (curHgt == minHgt) { + result.add(i); + } else if (curHgt < minHgt) { + result = new ArrayList<>(); + result.add(i); + minHgt = curHgt; + } + } + return result; + } + + private int dfs(int node, int parent) { + int hgt = 0; + for (int nei : adj.get(node)) { + if (nei == parent) { + continue; + } + hgt = Math.max(hgt, 1 + dfs(nei, node)); + } + return hgt; + } +} +``` + +```cpp +class Solution { +private: + vector> adj; + + int dfs(int node, int parent) { + int hgt = 0; + for (int nei : adj[node]) { + if (nei == parent) + continue; + hgt = max(hgt, 1 + dfs(nei, node)); + } + return hgt; + } + +public: + vector findMinHeightTrees(int n, vector>& edges) { + adj.resize(n); + for (const auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + int minHgt = n; + vector result; + for (int i = 0; i < n; i++) { + int curHgt = dfs(i, -1); + if (curHgt == minHgt) { + result.push_back(i); + } else if (curHgt < minHgt) { + result = {i}; + minHgt = curHgt; + } + } + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[]} + */ + findMinHeightTrees(n, edges) { + const adj = Array.from({ length: n }, () => []); + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + } + + const dfs = (node, parent) => { + let hgt = 0; + for (const nei of adj[node]) { + if (nei === parent) continue; + hgt = Math.max(hgt, 1 + dfs(nei, node)); + } + return hgt; + }; + + let minHgt = n; + const result = []; + for (let i = 0; i < n; i++) { + const curHgt = dfs(i, -1); + if (curHgt === minHgt) { + result.push(i); + } else if (curHgt < minHgt) { + result.length = 0; + result.push(i); + minHgt = curHgt; + } + } + return result; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V * (V + E))$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 2. Dynamic Programming On Trees (Rerooting) + +::tabs-start + +```python +class Solution: + def findMinHeightTrees(self, n: int, edges: List[List[int]]) -> List[int]: + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + adj[v].append(u) + + dp = [[0] * 2 for _ in range(n)] # top two heights for each node + + def dfs(node, parent): + for nei in adj[node]: + if nei == parent: + continue + dfs(nei, node) + curHgt = 1 + dp[nei][0] + if curHgt > dp[node][0]: + dp[node][1] = dp[node][0] + dp[node][0] = curHgt + elif curHgt > dp[node][1]: + dp[node][1] = curHgt + + def dfs1(node, parent, topHgt): + if topHgt > dp[node][0]: + dp[node][1] = dp[node][0] + dp[node][0] = topHgt + elif topHgt > dp[node][1]: + dp[node][1] = topHgt + + for nei in adj[node]: + if nei == parent: + continue + toChild = 1 + (dp[node][1] if dp[node][0] == 1 + dp[nei][0] else dp[node][0]) + dfs1(nei, node, toChild) + + dfs(0, -1) + dfs1(0, -1, 0) + + minHgt, res = n, [] + for i in range(n): + minHgt = min(minHgt, dp[i][0]) + for i in range(n): + if minHgt == dp[i][0]: + res.append(i) + return res +``` + +```java +class Solution { + private List> adj; + private int[][] dp; + + public List findMinHeightTrees(int n, int[][] edges) { + adj = new ArrayList<>(); + for (int i = 0; i < n; i++) { + adj.add(new ArrayList<>()); + } + for (int[] edge : edges) { + adj.get(edge[0]).add(edge[1]); + adj.get(edge[1]).add(edge[0]); + } + + dp = new int[n][2]; // top two heights for each node + dfs(0, -1); + dfs1(0, -1, 0); + + int minHgt = n; + List res = new ArrayList<>(); + for (int i = 0; i < n; i++) { + minHgt = Math.min(minHgt, dp[i][0]); + } + for (int i = 0; i < n; i++) { + if (minHgt == dp[i][0]) { + res.add(i); + } + } + return res; + } + + private void dfs(int node, int parent) { + for (int nei : adj.get(node)) { + if (nei == parent) continue; + dfs(nei, node); + int curHgt = 1 + dp[nei][0]; + if (curHgt > dp[node][0]) { + dp[node][1] = dp[node][0]; + dp[node][0] = curHgt; + } else if (curHgt > dp[node][1]) { + dp[node][1] = curHgt; + } + } + } + + private void dfs1(int node, int parent, int topHgt) { + if (topHgt > dp[node][0]) { + dp[node][1] = dp[node][0]; + dp[node][0] = topHgt; + } else if (topHgt > dp[node][1]) { + dp[node][1] = topHgt; + } + + for (int nei : adj.get(node)) { + if (nei == parent) continue; + int toChild = 1 + ((dp[node][0] == 1 + dp[nei][0]) ? dp[node][1] : dp[node][0]); + dfs1(nei, node, toChild); + } + } +} +``` + +```cpp +class Solution { +private: + vector> adj; + vector> dp; + + void dfs(int node, int parent) { + for (int nei : adj[node]) { + if (nei == parent) continue; + dfs(nei, node); + int curHgt = 1 + dp[nei][0]; + if (curHgt > dp[node][0]) { + dp[node][1] = dp[node][0]; + dp[node][0] = curHgt; + } else if (curHgt > dp[node][1]) { + dp[node][1] = curHgt; + } + } + } + + void dfs1(int node, int parent, int topHgt) { + if (topHgt > dp[node][0]) { + dp[node][1] = dp[node][0]; + dp[node][0] = topHgt; + } else if (topHgt > dp[node][1]) { + dp[node][1] = topHgt; + } + + for (int nei : adj[node]) { + if (nei == parent) continue; + int toChild = 1 + ((dp[node][0] == 1 + dp[nei][0]) ? dp[node][1] : dp[node][0]); + dfs1(nei, node, toChild); + } + } + +public: + vector findMinHeightTrees(int n, vector>& edges) { + adj.resize(n); + dp.assign(n, vector(2, 0)); // top two heights for each node + for (const auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + dfs(0, -1); + dfs1(0, -1, 0); + + int minHgt = n; + vector res; + for (int i = 0; i < n; i++) { + minHgt = min(minHgt, dp[i][0]); + } + for (int i = 0; i < n; i++) { + if (minHgt == dp[i][0]) { + res.push_back(i); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[]} + */ + findMinHeightTrees(n, edges) { + const adj = Array.from({ length: n }, () => []); + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + } + + // top two heights for each node + const dp = Array.from({ length: n }, () => [0, 0]); + + const dfs = (node, parent) => { + for (const nei of adj[node]) { + if (nei === parent) continue; + dfs(nei, node); + const curHgt = 1 + dp[nei][0]; + if (curHgt > dp[node][0]) { + dp[node][1] = dp[node][0]; + dp[node][0] = curHgt; + } else if (curHgt > dp[node][1]) { + dp[node][1] = curHgt; + } + } + }; + + const dfs1 = (node, parent, topHgt) => { + if (topHgt > dp[node][0]) { + dp[node][1] = dp[node][0]; + dp[node][0] = topHgt; + } else if (topHgt > dp[node][1]) { + dp[node][1] = topHgt; + } + + for (const nei of adj[node]) { + if (nei === parent) continue; + const toChild = 1 + ((dp[node][0] === 1 + dp[nei][0]) ? dp[node][1] : dp[node][0]); + dfs1(nei, node, toChild); + } + }; + + dfs(0, -1); + dfs1(0, -1, 0); + + let minHgt = n; + const res = []; + for (let i = 0; i < n; i++) { + minHgt = Math.min(minHgt, dp[i][0]); + } + for (let i = 0; i < n; i++) { + if (minHgt === dp[i][0]) { + res.push(i); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 3. Find Centroids of the Tree (DFS) + +::tabs-start + +```python +class Solution: + def findMinHeightTrees(self, n: int, edges: List[List[int]]) -> List[int]: + if n == 1: + return [0] + + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + adj[v].append(u) + + def dfs(node, parent): + farthest_node = node + max_distance = 0 + for nei in adj[node]: + if nei != parent: + nei_node, nei_distance = dfs(nei, node) + if nei_distance + 1 > max_distance: + max_distance = nei_distance + 1 + farthest_node = nei_node + return farthest_node, max_distance + + node_a, _ = dfs(0, -1) + node_b, diameter = dfs(node_a, -1) + + centroids = [] + + def find_centroids(node, parent): + if node == node_b: + centroids.append(node) + return True + for nei in adj[node]: + if nei != parent: + if find_centroids(nei, node): + centroids.append(node) + return True + return False + + find_centroids(node_a, -1) + L = len(centroids) + if diameter % 2 == 0: + return [centroids[L // 2]] + else: + return [centroids[L // 2 - 1], centroids[L // 2]] +``` + +```java +public class Solution { + private List[] adj; + private List centroids; + private int nodeB; + + public List findMinHeightTrees(int n, int[][] edges) { + if (n == 1) + return Collections.singletonList(0); + + adj = new ArrayList[n]; + for (int i = 0; i < n; ++i) + adj[i] = new ArrayList<>(); + + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + adj[edge[1]].add(edge[0]); + } + + int nodeA = dfs(0, -1)[0]; + nodeB = dfs(nodeA, -1)[0]; + centroids = new ArrayList<>(); + findCentroids(nodeA, -1); + + int L = centroids.size(); + if (dfs(nodeA, -1)[1] % 2 == 0) { + return Collections.singletonList(centroids.get(L / 2)); + } else { + return Arrays.asList(centroids.get(L / 2 - 1), centroids.get(L / 2)); + } + } + + private int[] dfs(int node, int parent) { + int farthestNode = node, maxDistance = 0; + for (int neighbor : adj[node]) { + if (neighbor != parent) { + int[] res = dfs(neighbor, node); + if (res[1] + 1 > maxDistance) { + maxDistance = res[1] + 1; + farthestNode = res[0]; + } + } + } + return new int[] { farthestNode, maxDistance }; + } + + private boolean findCentroids(int node, int parent) { + if (node == nodeB) { + centroids.add(node); + return true; + } + for (int neighbor : adj[node]) { + if (neighbor != parent) { + if (findCentroids(neighbor, node)) { + centroids.add(node); + return true; + } + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + vector> adj; + vector centroids; + int nodeB; + + vector findMinHeightTrees(int n, vector>& edges) { + if (n == 1) + return {0}; + + adj.resize(n); + for (const auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + int nodeA = dfs(0, -1).first; + nodeB = dfs(nodeA, -1).first; + findCentroids(nodeA, -1); + + int L = centroids.size(); + if (dfs(nodeA, -1).second % 2 == 0) + return {centroids[L / 2]}; + else + return {centroids[L / 2 - 1], centroids[L / 2]}; + } + +private: + pair dfs(int node, int parent) { + int farthestNode = node, maxDistance = 0; + for (int neighbor : adj[node]) { + if (neighbor != parent) { + auto res = dfs(neighbor, node); + if (res.second + 1 > maxDistance) { + maxDistance = res.second + 1; + farthestNode = res.first; + } + } + } + return {farthestNode, maxDistance}; + } + + bool findCentroids(int node, int parent) { + if (node == nodeB) { + centroids.push_back(node); + return true; + } + for (int neighbor : adj[node]) { + if (neighbor != parent) { + if (findCentroids(neighbor, node)) { + centroids.push_back(node); + return true; + } + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[]} + */ + findMinHeightTrees(n, edges) { + if (n === 1) return [0]; + + const adj = Array.from({ length: n }, () => []); + + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + } + + const dfs = (node, parent) => { + let farthestNode = node; + let maxDistance = 0; + + for (const neighbor of adj[node]) { + if (neighbor !== parent) { + const [farthest, dist] = dfs(neighbor, node); + if (dist + 1 > maxDistance) { + maxDistance = dist + 1; + farthestNode = farthest; + } + } + } + return [farthestNode, maxDistance]; + }; + + const findCentroids = (node, parent, centroids) => { + if (node === nodeB) { + centroids.push(node); + return true; + } + + for (const neighbor of adj[node]) { + if (neighbor !== parent) { + if (findCentroids(neighbor, node, centroids)) { + centroids.push(node); + return true; + } + } + } + return false; + }; + + const [nodeA] = dfs(0, -1); + const [nodeB, diameter] = dfs(nodeA, -1); + this.nodeB = nodeB; + + const centroids = []; + findCentroids(nodeA, -1, centroids); + + const L = centroids.length; + return diameter % 2 === 0 + ? [centroids[Math.floor(L / 2)]] + : [centroids[Math.floor(L / 2) - 1], centroids[Math.floor(L / 2)]]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 4. Topological Sorting (BFS) + +::tabs-start + +```python +class Solution: + def findMinHeightTrees(self, n: int, edges: List[List[int]]) -> List[int]: + if n == 1: + return [0] + + adj = defaultdict(list) + for n1, n2 in edges: + adj[n1].append(n2) + adj[n2].append(n1) + + edge_cnt = {} + leaves = deque() + + for src, neighbors in adj.items(): + edge_cnt[src] = len(neighbors) + if len(neighbors) == 1: + leaves.append(src) + + while leaves: + if n <= 2: + return list(leaves) + for _ in range(len(leaves)): + node = leaves.popleft() + n -= 1 + for nei in adj[node]: + edge_cnt[nei] -= 1 + if edge_cnt[nei] == 1: + leaves.append(nei) +``` + +```java +public class Solution { + public List findMinHeightTrees(int n, int[][] edges) { + if (n == 1) return Collections.singletonList(0); + + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; ++i) + adj[i] = new ArrayList<>(); + + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + adj[edge[1]].add(edge[0]); + } + + int[] edge_cnt = new int[n]; + Queue leaves = new LinkedList<>(); + + for (int i = 0; i < n; i++) { + edge_cnt[i] = adj[i].size(); + if (adj[i].size() == 1) + leaves.offer(i); + } + + while (!leaves.isEmpty()) { + if (n <= 2) return new ArrayList<>(leaves); + int size = leaves.size(); + for (int i = 0; i < size; ++i) { + int node = leaves.poll(); + n--; + for (int nei : adj[node]) { + edge_cnt[nei]--; + if (edge_cnt[nei] == 1) + leaves.offer(nei); + } + } + } + + return new ArrayList<>(); + } +} +``` + +```cpp +class Solution { +public: + vector findMinHeightTrees(int n, vector>& edges) { + if (n == 1) return {0}; + + vector> adj(n); + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + vector edge_cnt(n); + queue leaves; + + for (int i = 0; i < n; ++i) { + edge_cnt[i] = adj[i].size(); + if (adj[i].size() == 1) + leaves.push(i); + } + + while (!leaves.empty()) { + if (n <= 2) { + vector result; + while (!leaves.empty()) { + result.push_back(leaves.front()); + leaves.pop(); + } + return result; + } + int size = leaves.size(); + for (int i = 0; i < size; ++i) { + int node = leaves.front(); + leaves.pop(); + --n; + for (int& nei : adj[node]) { + --edge_cnt[nei]; + if (edge_cnt[nei] == 1) + leaves.push(nei); + } + } + } + + return {}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[]} + */ + findMinHeightTrees(n, edges) { + if (n === 1) return [0]; + + const adj = Array.from({ length: n }, () => []); + + for (const [n1, n2] of edges) { + adj[n1].push(n2); + adj[n2].push(n1); + } + + const edgeCnt = Array(n).fill(0); + const leaves = new Queue(); + + for (let i = 0; i < n; i++) { + edgeCnt[i] = adj[i].length; + if (adj[i].length === 1) { + leaves.push(i); + } + } + + while (!leaves.isEmpty()) { + if (n <= 2) return leaves.toArray(); + const size = leaves.size(); + for (let i = 0; i < size; i++) { + const node = leaves.pop(); + n--; + for (const nei of adj[node]) { + edgeCnt[nei]--; + if (edgeCnt[nei] === 1) { + leaves.push(nei); + } + } + } + } + + return []; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. \ No newline at end of file diff --git a/articles/minimum-index-of-a-valid-split.md b/articles/minimum-index-of-a-valid-split.md new file mode 100644 index 000000000..17548cd54 --- /dev/null +++ b/articles/minimum-index-of-a-valid-split.md @@ -0,0 +1,385 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def minimumIndex(self, nums: List[int]) -> int: + n = len(nums) + + for i in range(n - 1): + left_cnt = defaultdict(int) + for l in range(i + 1): + left_cnt[nums[l]] += 1 + + right_cnt = defaultdict(int) + for r in range(i + 1, n): + right_cnt[nums[r]] += 1 + + for num in left_cnt: + if left_cnt[num] > (i + 1) // 2 and right_cnt[num] > (n - i - 1) // 2: + return i + + return -1 +``` + +```java +public class Solution { + public int minimumIndex(List nums) { + int n = nums.size(); + + for (int i = 0; i < n - 1; i++) { + Map leftCnt = new HashMap<>(); + for (int l = 0; l <= i; l++) { + int val = nums.get(l); + leftCnt.put(val, leftCnt.getOrDefault(val, 0) + 1); + } + + Map rightCnt = new HashMap<>(); + for (int r = i + 1; r < n; r++) { + int val = nums.get(r); + rightCnt.put(val, rightCnt.getOrDefault(val, 0) + 1); + } + + for (int num : leftCnt.keySet()) { + if (leftCnt.get(num) > (i + 1) / 2 && rightCnt.getOrDefault(num, 0) > (n - i - 1) / 2) { + return i; + } + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int minimumIndex(vector& nums) { + int n = nums.size(); + + for (int i = 0; i < n - 1; i++) { + unordered_map leftCnt, rightCnt; + for (int l = 0; l <= i; l++) { + leftCnt[nums[l]]++; + } + for (int r = i + 1; r < n; r++) { + rightCnt[nums[r]]++; + } + + for (auto& [num, cnt] : leftCnt) { + if (cnt > (i + 1) / 2 && rightCnt[num] > (n - i - 1) / 2) { + return i; + } + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minimumIndex(nums) { + const n = nums.length; + + for (let i = 0; i < n - 1; i++) { + const leftCnt = {}; + for (let l = 0; l <= i; l++) { + leftCnt[nums[l]] = (leftCnt[nums[l]] || 0) + 1; + } + + const rightCnt = {}; + for (let r = i + 1; r < n; r++) { + rightCnt[nums[r]] = (rightCnt[nums[r]] || 0) + 1; + } + + for (const num in leftCnt) { + if (leftCnt[num] > Math.floor((i + 1) / 2) && + (rightCnt[num] || 0) > Math.floor((n - i - 1) / 2)) { + return i; + } + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def minimumIndex(self, nums: List[int]) -> int: + left = defaultdict(int) + right = Counter(nums) + + for i in range(len(nums)): + left[nums[i]] += 1 + right[nums[i]] -= 1 + + left_len = i + 1 + right_len = len(nums) - i - 1 + + if 2 * left[nums[i]] > left_len and 2 * right[nums[i]] > right_len: + return i + + return -1 +``` + +```java +public class Solution { + public int minimumIndex(List nums) { + Map left = new HashMap<>(); + Map right = new HashMap<>(); + int n = nums.size(); + + for (int num : nums) { + right.put(num, right.getOrDefault(num, 0) + 1); + } + + for (int i = 0; i < n; i++) { + int num = nums.get(i); + left.put(num, left.getOrDefault(num, 0) + 1); + right.put(num, right.get(num) - 1); + + int leftLen = i + 1; + int rightLen = n - i - 1; + + if (2 * left.get(num) > leftLen && 2 * right.get(num) > rightLen) { + return i; + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int minimumIndex(vector& nums) { + unordered_map left, right; + int n = nums.size(); + + for (int num : nums) { + right[num]++; + } + + for (int i = 0; i < n; i++) { + int num = nums[i]; + left[num]++; + right[num]--; + + int leftLen = i + 1; + int rightLen = n - i - 1; + + if (2 * left[num] > leftLen && 2 * right[num] > rightLen) { + return i; + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minimumIndex(nums) { + const left = {}; + const right = {}; + const n = nums.length; + + for (const num of nums) { + right[num] = (right[num] || 0) + 1; + } + + for (let i = 0; i < n; i++) { + const num = nums[i]; + left[num] = (left[num] || 0) + 1; + right[num] -= 1; + + const leftLen = i + 1; + const rightLen = n - i - 1; + + if (2 * left[num] > leftLen && 2 * right[num] > rightLen) { + return i; + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Boyer-Moore Voting Algorithm + +::tabs-start + +```python +class Solution: + def minimumIndex(self, nums: List[int]) -> int: + majority = count = 0 + for num in nums: + if count == 0: + majority = num + count += (1 if majority == num else -1) + + left_cnt, right_cnt = 0, nums.count(majority) + + for i in range(len(nums)): + if nums[i] == majority: + left_cnt += 1 + right_cnt -= 1 + + left_len = i + 1 + right_len = len(nums) - i - 1 + + if 2 * left_cnt > left_len and 2 * right_cnt > right_len: + return i + + return -1 +``` + +```java +public class Solution { + public int minimumIndex(List nums) { + int majority = 0, count = 0; + for (int num : nums) { + if (count == 0) majority = num; + count += (majority == num) ? 1 : -1; + } + + int leftCnt = 0, rightCnt = 0; + for (int num : nums) { + if (num == majority) rightCnt++; + } + + int n = nums.size(); + for (int i = 0; i < n; i++) { + if (nums.get(i) == majority) { + leftCnt++; + rightCnt--; + } + + int leftLen = i + 1; + int rightLen = n - i - 1; + + if (2 * leftCnt > leftLen && 2 * rightCnt > rightLen) { + return i; + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int minimumIndex(vector& nums) { + int majority = 0, count = 0; + for (int num : nums) { + if (count == 0) majority = num; + count += (num == majority ? 1 : -1); + } + + int leftCnt = 0, rightCnt = count_if(nums.begin(), nums.end(), + [&](int x) { return x == majority; }); + + int n = nums.size(); + for (int i = 0; i < n; i++) { + if (nums[i] == majority) { + leftCnt++; + rightCnt--; + } + + int leftLen = i + 1; + int rightLen = n - i - 1; + + if (2 * leftCnt > leftLen && 2 * rightCnt > rightLen) { + return i; + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minimumIndex(nums) { + let majority = 0, count = 0; + for (let num of nums) { + if (count === 0) majority = num; + count += (num === majority ? 1 : -1); + } + + let leftCnt = 0; + let rightCnt = nums.filter(x => x === majority).length; + const n = nums.length; + + for (let i = 0; i < n; i++) { + if (nums[i] === majority) { + leftCnt++; + rightCnt--; + } + + let leftLen = i + 1; + let rightLen = n - i - 1; + + if (2 * leftCnt > leftLen && 2 * rightCnt > rightLen) { + return i; + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/minimum-interval-including-query.md b/articles/minimum-interval-including-query.md new file mode 100644 index 000000000..3c1cf82d5 --- /dev/null +++ b/articles/minimum-interval-including-query.md @@ -0,0 +1,1727 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def minInterval(self, intervals: List[List[int]], queries: List[int]) -> List[int]: + n = len(intervals) + res = [] + for q in queries: + cur = -1 + for l, r in intervals: + if l <= q <= r: + if cur == -1 or (r - l + 1) < cur: + cur = r - l + 1 + res.append(cur) + return res +``` + +```java +public class Solution { + public int[] minInterval(int[][] intervals, int[] queries) { + int[] res = new int[queries.length]; + int idx = 0; + for (int q : queries) { + int cur = -1; + for (int[] interval : intervals) { + int l = interval[0], r = interval[1]; + if (l <= q && q <= r) { + if (cur == -1 || (r - l + 1) < cur) { + cur = r - l + 1; + } + } + } + res[idx++] = cur; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector minInterval(vector>& intervals, vector& queries) { + vector res; + for (int q : queries) { + int cur = -1; + for (auto& interval : intervals) { + int l = interval[0], r = interval[1]; + if (l <= q && q <= r) { + if (cur == -1 || (r - l + 1) < cur) { + cur = r - l + 1; + } + } + } + res.push_back(cur); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} intervals + * @param {number[]} queries + * @return {number[]} + */ + minInterval(intervals, queries) { + const res = []; + for (const q of queries) { + let cur = -1; + for (const [l, r] of intervals) { + if (l <= q && q <= r) { + if (cur === -1 || (r - l + 1) < cur) { + cur = r - l + 1; + } + } + } + res.push(cur); + } + return res; + } +} +``` + +```csharp +public class Solution { + public int[] MinInterval(int[][] intervals, int[] queries) { + int[] res = new int[queries.Length]; + int idx = 0; + foreach (int q in queries) { + int cur = -1; + foreach (var interval in intervals) { + int l = interval[0], r = interval[1]; + if (l <= q && q <= r) { + if (cur == -1 || (r - l + 1) < cur) { + cur = r - l + 1; + } + } + } + res[idx++] = cur; + } + return res; + } +} +``` + +```go +func minInterval(intervals [][]int, queries []int) []int { + res := make([]int, len(queries)) + for i, q := range queries { + cur := -1 + for _, interval := range intervals { + l, r := interval[0], interval[1] + if l <= q && q <= r { + if cur == -1 || (r - l + 1) < cur { + cur = r - l + 1 + } + } + } + res[i] = cur + } + return res +} +``` + +```kotlin +class Solution { + fun minInterval(intervals: Array, queries: IntArray): IntArray { + val res = IntArray(queries.size) + for ((i, q) in queries.withIndex()) { + var cur = -1 + for (interval in intervals) { + val l = interval[0] + val r = interval[1] + if (l <= q && q <= r) { + if (cur == -1 || (r - l + 1) < cur) { + cur = r - l + 1 + } + } + } + res[i] = cur + } + return res + } +} +``` + +```swift +class Solution { + func minInterval(_ intervals: [[Int]], _ queries: [Int]) -> [Int] { + var res = [Int]() + + for q in queries { + var cur = -1 + for interval in intervals { + let l = interval[0] + let r = interval[1] + + if l <= q && q <= r { + if cur == -1 || (r - l + 1) < cur { + cur = r - l + 1 + } + } + } + res.append(cur) + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(m)$ space for the output array. + +> Where $m$ is the length of the array $queries$ and $n$ is the length of the array $intervals$. + +--- + +## 2. Sweep Line Algorithm + +::tabs-start + +```python +class Solution: + def minInterval(self, intervals: List[List[int]], queries: List[int]) -> List[int]: + events = [] + # Create events for intervals + for idx, (start, end) in enumerate(intervals): + events.append((start, 0, end - start + 1, idx)) + events.append((end, 2, end - start + 1, idx)) + + # Create events for queries + for i, q in enumerate(queries): + events.append((q, 1, i)) + + # Sort by time and type (end before query) + events.sort(key=lambda x: (x[0], x[1])) + + # Min heap storing [size, index] + sizes = [] + ans = [-1] * len(queries) + inactive = [False] * len(intervals) + + for time, type, *rest in events: + if type == 0: # Interval start + interval_size, idx = rest + heapq.heappush(sizes, (interval_size, idx)) + elif type == 2: #Interval end + idx = rest[1] + inactive[idx] = True + else: # Query + query_idx = rest[0] + while sizes and inactive[sizes[0][1]]: + heapq.heappop(sizes) + if sizes: + ans[query_idx] = sizes[0][0] + + return ans +``` + +```java +public class Solution { + public int[] minInterval(int[][] intervals, int[] queries) { + List events = new ArrayList<>(); + for (int i = 0; i < intervals.length; i++) { + events.add(new int[]{intervals[i][0], 0, intervals[i][1] - intervals[i][0] + 1, i}); + events.add(new int[]{intervals[i][1], 2, intervals[i][1] - intervals[i][0] + 1, i}); + } + + for (int i = 0; i < queries.length; i++) { + events.add(new int[]{queries[i], 1, i}); + } + + // Sort by time and type (end before query) + events.sort((a, b) -> a[0] != b[0] ? + Integer.compare(a[0], b[0]) : + Integer.compare(a[1], b[1])); + + int[] ans = new int[queries.length]; + Arrays.fill(ans, -1); + + // Min heap storing [size, index] + PriorityQueue pq = new PriorityQueue<>((a, b) -> Integer.compare(a[0], b[0])); + boolean[] inactive = new boolean[intervals.length]; + + for (int[] event : events) { + if (event[1] == 0) { // Interval start + pq.offer(new int[]{event[2], event[3]}); + } + else if (event[1] == 2) { // Interval end + inactive[event[3]] = true; + } + else { // Query + while (!pq.isEmpty() && inactive[pq.peek()[1]]) { + pq.poll(); + } + if (!pq.isEmpty()) { + ans[event[2]] = pq.peek()[0]; + } + } + } + + return ans; + } +} +``` + +```cpp +class Solution { +public: + vector minInterval(vector>& intervals, vector& queries) { + vector> events; + // Create events for intervals + for (int i = 0; i < intervals.size(); i++) { + events.push_back({intervals[i][0], 0, intervals[i][1] - intervals[i][0] + 1, i}); + events.push_back({intervals[i][1], 2, intervals[i][1] - intervals[i][0] + 1, i}); + } + + // Create events for queries + for (int i = 0; i < queries.size(); i++) { + events.push_back({queries[i], 1, i}); + } + + // Sort by time and type (end before query) + sort(events.begin(), events.end(), [](const vector& a, const vector& b) { + return a[0] == b[0] ? a[1] < b[1] : a[0] < b[0]; + }); + + vector ans(queries.size(), -1); + // Min heap storing [size, index] + priority_queue, vector>, greater>> pq; + vector inactive(intervals.size(), false); + + for (const auto& event : events) { + if (event[1] == 0) { // Interval start + pq.push({event[2], event[3]}); + } else if (event[1] == 2) { // Interval end + inactive[event[3]] = true; + } else { // Query + int queryIdx = event[2]; + while (!pq.empty() && inactive[pq.top().second]) { + pq.pop(); + } + if (!pq.empty()) { + ans[queryIdx] = pq.top().first; + } + } + } + + return ans; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} intervals + * @param {number[]} queries + * @return {number[]} + */ + minInterval(intervals, queries) { + let events = []; + // Create events for intervals + for (let i = 0; i < intervals.length; i++) { + const [start, end] = intervals[i]; + events.push([start, 0, end - start + 1, i]); + events.push([end, 2, end - start + 1, i]); + } + + // Create events for queries + queries.forEach((q, i) => { + events.push([q, 1, i]); + }); + // Sort by time and type (end before query) + events.sort((a, b) => a[0] - b[0] || a[1] - b[1]); + + const ans = Array(queries.length).fill(-1); + // Min heap storing [size, index] + const pq = new PriorityQueue((a, b) => a[0] - b[0]); + const inactive = Array(intervals.length).fill(false); + + for (const [time, type, ...rest] of events) { + if (type === 0) { // Interval start + pq.push([rest[0], rest[1]]); + } else if (type === 2) { // Interval end + inactive[rest[1]] = true; + } else { // Query + while (!pq.isEmpty() && inactive[pq.front()[1]]) { + pq.pop(); + } + if (!pq.isEmpty()) { + ans[rest[0]] = pq.front()[0]; + } + } + } + + return ans; + } +} +``` + +```csharp +public class Solution { + public int[] MinInterval(int[][] intervals, int[] queries) { + var events = new List(); + // Create events for intervals + for (int i = 0; i < intervals.Length; i++) { + events.Add(new int[] { intervals[i][0], 0, intervals[i][1] - intervals[i][0] + 1, i }); + events.Add(new int[] { intervals[i][1], 2, intervals[i][1] - intervals[i][0] + 1, i }); + } + + // Create events for queries + for (int i = 0; i < queries.Length; i++) { + events.Add(new int[] { queries[i], 1, i }); + } + // Sort by time and type (end before query) + events.Sort((a, b) => a[0] == b[0] ? a[1].CompareTo(b[1]) : a[0].CompareTo(b[0])); + + int[] ans = new int[queries.Length]; + Array.Fill(ans, -1); + // Min heap storing [size, index] + var pq = new PriorityQueue<(int size, int idx), int>(); + var inactive = new bool[intervals.Length]; + + foreach (var e in events) { + if (e[1] == 0) { // Interval start + pq.Enqueue((e[2], e[3]), e[2]); + } else if (e[1] == 2) { // Interval end + inactive[e[3]] = true; + } else { + int queryIdx = e[2]; + while (pq.Count > 0 && inactive[pq.Peek().idx]) { + pq.Dequeue(); + } + if (pq.Count > 0) { + ans[queryIdx] = pq.Peek().size; + } + } + } + + return ans; + } +} +``` + +```go +type Event struct { + time int + typ int // 0: interval start, 1: query, 2: interval end + val int // interval size or query index + idx int // interval index +} + +func minInterval(intervals [][]int, queries []int) []int { + events := []Event{} + + // Create events for intervals + for idx, interval := range intervals { + start, end := interval[0], interval[1] + size := end - start + 1 + events = append(events, Event{start, 0, size, idx}) + events = append(events, Event{end, 2, size, idx}) + } + + // Create events for queries + for i, q := range queries { + events = append(events, Event{q, 1, i, -1}) + } + + // Sort events by time and type + sort.Slice(events, func(i, j int) bool { + if events[i].time == events[j].time { + return events[i].typ < events[j].typ + } + return events[i].time < events[j].time + }) + + // Priority queue to store intervals with the smallest size on top + sizes := priorityqueue.NewWith(func(a, b interface{}) int { + return utils.IntComparator(a.([]int)[0], b.([]int)[0]) + }) + ans := make([]int, len(queries)) + for i := range ans { + ans[i] = -1 + } + inactive := make([]bool, len(intervals)) + + for _, event := range events { + switch event.typ { + case 0: // Interval start + sizes.Enqueue([]int{event.val, event.idx}) + case 2: // Interval end + inactive[event.idx] = true + case 1: // Query + queryIdx := event.val + for !sizes.Empty() { + top, _ := sizes.Peek() + if inactive[top.([]int)[1]] { + sizes.Dequeue() + } else { + break + } + } + if !sizes.Empty() { + top, _ := sizes.Peek() + ans[queryIdx] = top.([]int)[0] + } + } + } + return ans +} +``` + +```kotlin +data class Event(val time: Int, val type: Int, val sizeOrQueryIndex: Int, val idx: Int) + +class Solution { + fun minInterval(intervals: Array, queries: IntArray): IntArray { + val events = mutableListOf() + + // Create events for intervals + for ((idx, interval) in intervals.withIndex()) { + val (start, end) = interval + val size = end - start + 1 + events.add(Event(start, 0, size, idx)) + events.add(Event(end, 2, size, idx)) + } + + // Create events for queries + for ((i, q) in queries.withIndex()) { + events.add(Event(q, 1, i, -1)) + } + + // Sort events by time and type + events.sortWith(compareBy({ it.time }, { it.type })) + + val sizes = PriorityQueue>(compareBy { it.first }) + val ans = IntArray(queries.size) { -1 } + val inactive = BooleanArray(intervals.size) + + for (event in events) { + when (event.type) { + 0 -> { // Interval start + sizes.add(Pair(event.sizeOrQueryIndex, event.idx)) + } + 2 -> { // Interval end + inactive[event.idx] = true + } + 1 -> { // Query + val queryIdx = event.sizeOrQueryIndex + while (sizes.isNotEmpty() && inactive[sizes.peek().second]) { + sizes.poll() + } + if (sizes.isNotEmpty()) { + ans[queryIdx] = sizes.peek().first + } + } + } + } + + return ans + } +} +``` + +```swift +struct Event { + let time: Int + let type: Int // 0: interval start, 1: query, 2: interval end + let size: Int? // interval size for start/end events + let idx: Int? // index for interval (for start/end) or query index (for query) +} + +struct Item: Comparable { + let size: Int + let idx: Int + + static func < (lhs: Item, rhs: Item) -> Bool { + return lhs.size < rhs.size + } +} + +class Solution { + func minInterval(_ intervals: [[Int]], _ queries: [Int]) -> [Int] { + var events = [Event]() + + // Create events for intervals + for (idx, interval) in intervals.enumerated() { + let start = interval[0] + let end = interval[1] + let intervalSize = end - start + 1 + events.append(Event(time: start, type: 0, size: intervalSize, idx: idx)) + events.append(Event(time: end, type: 2, size: intervalSize, idx: idx)) + } + + // Create events for queries + for (i, q) in queries.enumerated() { + events.append(Event(time: q, type: 1, size: nil, idx: i)) + } + + // Sort by time and type (end before query) + events.sort { (a, b) in + if a.time != b.time { + return a.time < b.time + } + return a.type < b.type + } + + // Min heap storing [size, index] + var sizes = Heap() + var ans = Array(repeating: -1, count: queries.count) + var inactive = Array(repeating: false, count: intervals.count) + + for event in events { + if event.type == 0 { // Interval start + let intervalSize = event.size! + let idx = event.idx! + sizes.insert(Item(size: intervalSize, idx: idx)) + } else if event.type == 2 { // Interval end + let idx = event.idx! + inactive[idx] = true + } else { // Query + let queryIdx = event.idx! + while !sizes.isEmpty, inactive[sizes.min!.idx] { + sizes.removeMin() + } + if !sizes.isEmpty { + ans[queryIdx] = sizes.min!.size + } + } + } + + return ans + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((n + m) \log (n + m))$ +* Space complexity: $O(n + m)$ + +> Where $m$ is the length of the array $queries$ and $n$ is the length of the array $intervals$. + +--- + +## 3. Min Heap + +::tabs-start + +```python +class Solution: + def minInterval(self, intervals: List[List[int]], queries: List[int]) -> List[int]: + intervals.sort() + minHeap = [] + res = {} + i = 0 + for q in sorted(queries): + while i < len(intervals) and intervals[i][0] <= q: + l, r = intervals[i] + heapq.heappush(minHeap, (r - l + 1, r)) + i += 1 + + while minHeap and minHeap[0][1] < q: + heapq.heappop(minHeap) + res[q] = minHeap[0][0] if minHeap else -1 + return [res[q] for q in queries] +``` + +```java +public class Solution { + public int[] minInterval(int[][] intervals, int[] queries) { + Arrays.sort(intervals, Comparator.comparingInt(a -> a[0])); + PriorityQueue minHeap = new PriorityQueue<>((a, b) -> Integer.compare(a[0], b[0])); + Map res = new HashMap<>(); + int i = 0; + for (int q : Arrays.stream(queries).sorted().toArray()) { + while (i < intervals.length && intervals[i][0] <= q) { + int l = intervals[i][0]; + int r = intervals[i][1]; + minHeap.offer(new int[]{r - l + 1, r}); + i++; + } + + while (!minHeap.isEmpty() && minHeap.peek()[1] < q) { + minHeap.poll(); + } + res.put(q, minHeap.isEmpty() ? -1 : minHeap.peek()[0]); + } + int[] result = new int[queries.length]; + for (int j = 0; j < queries.length; j++) { + result[j] = res.get(queries[j]); + } + return result; + } +} +``` + +```cpp +class Solution { +public: + vector minInterval(vector>& intervals, vector& queries) { + // Sort intervals based on the start value + sort(intervals.begin(), intervals.end(), [](auto& a, auto& b) { + return a[0] < b[0]; + }); + + vector sortedQueries = queries; + sort(sortedQueries.begin(), sortedQueries.end()); + map res; + + auto cmp = [](const vector& a, const vector& b) { + return a[0] > b[0] || (a[0] == b[0] && a[1] > b[1]); + }; + priority_queue, vector>, decltype(cmp)> minHeap(cmp); + + int i = 0; + for (int q : sortedQueries) { + while (i < intervals.size() && intervals[i][0] <= q) { + int l = intervals[i][0]; + int r = intervals[i][1]; + minHeap.push({r - l + 1, r}); + i++; + } + + while (!minHeap.empty() && minHeap.top()[1] < q) { + minHeap.pop(); + } + + res[q] = minHeap.empty() ? -1 : minHeap.top()[0]; + } + + vector result(queries.size()); + for (int j = 0; j < queries.size(); j++) { + result[j] = res[queries[j]]; + } + return result; + } +}; +``` + +```javascript +/** + * const { MinPriorityQueue } = require('@datastructures-js/priority-queue'); + */ + +class Solution { + /** + * @param {number[][]} intervals + * @param {number[]} queries + * @return {number[]} + */ + minInterval(intervals, queries) { + intervals.sort((a, b) => a[0] - b[0]); + const minHeap = new MinPriorityQueue(entry => entry[0]); + const res = {}; + let i = 0; + + const sortedQueries = [...queries].sort((a, b) => a - b); + + for (const q of sortedQueries) { + while (i < intervals.length && intervals[i][0] <= q) { + const [l, r] = intervals[i]; + minHeap.enqueue([r - l + 1, r]); + i += 1; + } + + while (!minHeap.isEmpty() && minHeap.front()[1] < q) { + minHeap.dequeue(); + } + + res[q] = !minHeap.isEmpty() ? minHeap.front()[0] : -1; + } + + return queries.map(q => res[q]); + } +} +``` + +```csharp +public class Solution { + public int[] MinInterval(int[][] intervals, int[] queries) { + Array.Sort(intervals, (a, b) => a[0].CompareTo(b[0])); + + var minHeap = new PriorityQueue<(int Size, int End), int>(); + var res = new Dictionary(); + int i = 0; + + int[] sortedQueries = queries.OrderBy(q => q).ToArray(); + foreach (int q in sortedQueries) { + while (i < intervals.Length && intervals[i][0] <= q) { + int l = intervals[i][0]; + int r = intervals[i][1]; + minHeap.Enqueue((r - l + 1, r), r - l + 1); + i++; + } + + while (minHeap.Count > 0 && minHeap.Peek().End < q) { + minHeap.Dequeue(); + } + + res[q] = minHeap.Count == 0 ? -1 : minHeap.Peek().Size; + } + + int[] result = new int[queries.Length]; + for (int j = 0; j < queries.Length; j++) { + result[j] = res[queries[j]]; + } + + return result; + } +} +``` + +```go +func minInterval(intervals [][]int, queries []int) []int { + sort.Slice(intervals, func(i, j int) bool { + return intervals[i][0] < intervals[j][0] + }) + + queriesWithIdx := make([][2]int, len(queries)) + for i, q := range queries { + queriesWithIdx[i] = [2]int{q, i} + } + sort.Slice(queriesWithIdx, func(i, j int) bool { + return queriesWithIdx[i][0] < queriesWithIdx[j][0] + }) + + comparator := func(a, b interface{}) int { + pair1 := a.([2]int) + pair2 := b.([2]int) + if pair1[0] != pair2[0] { + if pair1[0] < pair2[0] { + return -1 + } + return 1 + } + return 0 + } + + pq := priorityqueue.NewWith(comparator) + res := make([]int, len(queries)) + i := 0 + + for _, qPair := range queriesWithIdx { + q, originalIdx := qPair[0], qPair[1] + + for i < len(intervals) && intervals[i][0] <= q { + size := intervals[i][1] - intervals[i][0] + 1 + pq.Enqueue([2]int{size, intervals[i][1]}) + i++ + } + + for !pq.Empty() { + if top, _ := pq.Peek(); top.([2]int)[1] < q { + pq.Dequeue() + } else { + break + } + } + + if !pq.Empty() { + if top, _ := pq.Peek(); true { + res[originalIdx] = top.([2]int)[0] + } + } else { + res[originalIdx] = -1 + } + } + + return res +} +``` + +```kotlin +class Solution { + fun minInterval(intervals: Array, queries: IntArray): IntArray { + intervals.sortBy { it[0] } + + val queriesWithIndex = queries.withIndex() + .map { it.value to it.index } + .sortedBy { it.first } + + val minHeap = PriorityQueue>(compareBy { it.first }) + val result = IntArray(queries.size) + var i = 0 + + for ((q, originalIdx) in queriesWithIndex) { + while (i < intervals.size && intervals[i][0] <= q) { + val size = intervals[i][1] - intervals[i][0] + 1 + minHeap.offer(size to intervals[i][1]) + i++ + } + + while (minHeap.isNotEmpty() && minHeap.peek().second < q) { + minHeap.poll() + } + + result[originalIdx] = if (minHeap.isNotEmpty()) minHeap.peek().first else -1 + } + + return result + } +} +``` + +```swift +struct HeapItem: Comparable { + let size: Int + let end: Int + static func < (lhs: HeapItem, rhs: HeapItem) -> Bool { + if lhs.size == rhs.size { + return lhs.end < rhs.end + } + return lhs.size < rhs.size + } +} + +class Solution { + func minInterval(_ intervals: [[Int]], _ queries: [Int]) -> [Int] { + let sortedIntervals = intervals.sorted { $0[0] < $1[0] } + var minHeap = Heap() + var res = [Int: Int]() + var i = 0 + + for q in queries.sorted() { + while i < sortedIntervals.count && sortedIntervals[i][0] <= q { + let l = sortedIntervals[i][0] + let r = sortedIntervals[i][1] + minHeap.insert(HeapItem(size: r - l + 1, end: r)) + i += 1 + } + + while !minHeap.isEmpty, minHeap.min!.end < q { + minHeap.removeMin() + } + res[q] = minHeap.isEmpty ? -1 : minHeap.min!.size + } + + return queries.map { res[$0] ?? -1 } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + m \log m)$ +* Space complexity: $O(n + m)$ + +> Where $m$ is the length of the array $queries$ and $n$ is the length of the array $intervals$. + +--- + +## 4. Min Segment Tree (Lazy Propagation) + +::tabs-start + +```python +class SegmentTree: + def __init__(self, N): + self.n = N + self.tree = [float('inf')] * (4 * N) + self.lazy = [float('inf')] * (4 * N) + + def propagate(self, treeidx, lo, hi): + if self.lazy[treeidx] != float('inf'): + self.tree[treeidx] = min(self.tree[treeidx], self.lazy[treeidx]) + if lo != hi: + self.lazy[2 * treeidx + 1] = min(self.lazy[2 * treeidx + 1], self.lazy[treeidx]) + self.lazy[2 * treeidx + 2] = min(self.lazy[2 * treeidx + 2], self.lazy[treeidx]) + self.lazy[treeidx] = float('inf') + + def update(self, treeidx, lo, hi, left, right, val): + self.propagate(treeidx, lo, hi) + if lo > right or hi < left: + return + if lo >= left and hi <= right: + self.lazy[treeidx] = min(self.lazy[treeidx], val) + self.propagate(treeidx, lo, hi) + return + mid = (lo + hi) // 2 + self.update(2 * treeidx + 1, lo, mid, left, right, val) + self.update(2 * treeidx + 2, mid + 1, hi, left, right, val) + self.tree[treeidx] = min(self.tree[2 * treeidx + 1], self.tree[2 * treeidx + 2]) + + def query(self, treeidx, lo, hi, idx): + self.propagate(treeidx, lo, hi) + if lo == hi: + return self.tree[treeidx] + mid = (lo + hi) // 2 + if idx <= mid: + return self.query(2 * treeidx + 1, lo, mid, idx) + else: + return self.query(2 * treeidx + 2, mid + 1, hi, idx) + + def update_range(self, left, right, val): + self.update(0, 0, self.n - 1, left, right, val) + + def query_point(self, idx): + return self.query(0, 0, self.n - 1, idx) + +class Solution: + def minInterval(self, intervals: List[List[int]], queries: List[int]) -> List[int]: + points = [] + for interval in intervals: + points.append(interval[0]) + points.append(interval[1]) + for q in queries: + points.append(q) + + # Compress the coordinates + points = sorted(set(points)) + compress = {points[i]: i for i in range(len(points))} + + # Lazy Segment Tree + segTree = SegmentTree(len(points)) + + for interval in intervals: + start = compress[interval[0]] + end = compress[interval[1]] + length = interval[1] - interval[0] + 1 + segTree.update_range(start, end, length) + + ans = [] + for q in queries: + idx = compress[q] + + # query for minSize + res = segTree.query_point(idx) + ans.append(res if res != float('inf') else -1) + return ans +``` + +```java +class SegmentTree { + int n; + int[] tree; + int[] lazy; + + SegmentTree(int N) { + this.n = N; + tree = new int[4 * N]; + lazy = new int[4 * N]; + Arrays.fill(tree, Integer.MAX_VALUE); + Arrays.fill(lazy, Integer.MAX_VALUE); + } + + void propagate(int treeidx, int lo, int hi) { + if (lazy[treeidx] != Integer.MAX_VALUE) { + tree[treeidx] = Math.min(tree[treeidx], lazy[treeidx]); + if (lo != hi) { + lazy[2 * treeidx + 1] = Math.min(lazy[2 * treeidx + 1], lazy[treeidx]); + lazy[2 * treeidx + 2] = Math.min(lazy[2 * treeidx + 2], lazy[treeidx]); + } + lazy[treeidx] = Integer.MAX_VALUE; + } + } + + void update(int treeidx, int lo, int hi, int left, int right, int val) { + propagate(treeidx, lo, hi); + if (lo > right || hi < left) return; + if (lo >= left && hi <= right) { + lazy[treeidx] = Math.min(lazy[treeidx], val); + propagate(treeidx, lo, hi); + return; + } + int mid = (lo + hi) / 2; + update(2 * treeidx + 1, lo, mid, left, right, val); + update(2 * treeidx + 2, mid + 1, hi, left, right, val); + tree[treeidx] = Math.min(tree[2 * treeidx + 1], tree[2 * treeidx + 2]); + } + + int query(int treeidx, int lo, int hi, int idx) { + propagate(treeidx, lo, hi); + if (lo == hi) return tree[treeidx]; + int mid = (lo + hi) / 2; + if (idx <= mid) return query(2 * treeidx + 1, lo, mid, idx); + else return query(2 * treeidx + 2, mid + 1, hi, idx); + } + + void update(int left, int right, int val) { + update(0, 0, n - 1, left, right, val); + } + + int query(int idx) { + return query(0, 0, n - 1, idx); + } +} + +public class Solution { + public int[] minInterval(int[][] intervals, int[] queries) { + List points = new ArrayList<>(); + for (int[] interval : intervals) { + points.add(interval[0]); + points.add(interval[1]); + } + for (int q : queries) { + points.add(q); + } + points = new ArrayList<>(new HashSet<>(points)); + Collections.sort(points); + + // Compress the points to indices + Map compress = new HashMap<>(); + for (int i = 0; i < points.size(); i++) { + compress.put(points.get(i), i); + } + + // Create the segment tree + SegmentTree segTree = new SegmentTree(points.size()); + + // Update the segment tree with intervals + for (int[] interval : intervals) { + int start = compress.get(interval[0]); + int end = compress.get(interval[1]); + int length = interval[1] - interval[0] + 1; + segTree.update(start, end, length); + } + + // Query the segment tree for each query + int[] ans = new int[queries.length]; + for (int i = 0; i < queries.length; i++) { + int idx = compress.get(queries[i]); + int res = segTree.query(idx); + ans[i] = (res == Integer.MAX_VALUE) ? -1 : res; + } + + return ans; + } +} +``` + +```cpp +class SegmentTree { +public: + int n; + vector tree; + vector lazy; + + SegmentTree(int N) { + this->n = N; + tree.assign(4 * N, INT_MAX); + lazy.assign(4 * N, INT_MAX); + } + + void propagate(int treeidx, int lo, int hi) { + if (lazy[treeidx] != INT_MAX) { + tree[treeidx] = min(tree[treeidx], lazy[treeidx]); + if (lo != hi) { + lazy[2 * treeidx + 1] = min(lazy[2 * treeidx + 1], lazy[treeidx]); + lazy[2 * treeidx + 2] = min(lazy[2 * treeidx + 2], lazy[treeidx]); + } + lazy[treeidx] = INT_MAX; + } + } + + void update(int treeidx, int lo, int hi, int left, int right, int val) { + propagate(treeidx, lo, hi); + + if (lo > right || hi < left) return; + + if (lo >= left && hi <= right) { + lazy[treeidx] = min(lazy[treeidx], val); + propagate(treeidx, lo, hi); + return; + } + + int mid = (lo + hi) / 2; + update(2 * treeidx + 1, lo, mid, left, right, val); + update(2 * treeidx + 2, mid + 1, hi, left, right, val); + + tree[treeidx] = min(tree[2 * treeidx + 1], tree[2 * treeidx + 2]); + } + + int query(int treeidx, int lo, int hi, int idx) { + propagate(treeidx, lo, hi); + if (lo == hi) return tree[treeidx]; + + int mid = (lo + hi) / 2; + if (idx <= mid) return query(2 * treeidx + 1, lo, mid, idx); + else return query(2 * treeidx + 2, mid + 1, hi, idx); + } + + void update(int left, int right, int val) { + update(0, 0, n - 1, left, right, val); + } + + int query(int idx) { + return query(0, 0, n - 1, idx); + } +}; + +class Solution { +public: + vector minInterval(vector>& intervals, vector& queries) { + vector points; + + for (const auto& interval : intervals) { + points.push_back(interval[0]); + points.push_back(interval[1]); + } + for (int q : queries) { + points.push_back(q); + } + + sort(points.begin(), points.end()); + points.erase(unique(points.begin(), points.end()), points.end()); + + // compress the coordinates + unordered_map compress; + for (int i = 0; i < points.size(); ++i) { + compress[points[i]] = i; + } + + SegmentTree segTree(points.size()); + + for (const auto& interval : intervals) { + int start = compress[interval[0]]; + int end = compress[interval[1]]; + int len = interval[1] - interval[0] + 1; + segTree.update(start, end, len); + } + + vector ans; + for (int q : queries) { + int idx = compress[q]; + int res = segTree.query(idx); + ans.push_back(res == INT_MAX ? -1 : res); + } + + return ans; + } +}; +``` + +```javascript +class SegmentTree { + constructor(N) { + this.n = N; + this.tree = new Array(4 * N).fill(Infinity); + this.lazy = new Array(4 * N).fill(Infinity); + } + + /** + * @param {number} treeidx + * @param {number} lo + * @param {number} hi + * @return {void} + */ + propagate(treeidx, lo, hi) { + if (this.lazy[treeidx] !== Infinity) { + this.tree[treeidx] = Math.min(this.tree[treeidx], this.lazy[treeidx]); + if (lo !== hi) { + this.lazy[2 * treeidx + 1] = Math.min(this.lazy[2 * treeidx + 1], this.lazy[treeidx]); + this.lazy[2 * treeidx + 2] = Math.min(this.lazy[2 * treeidx + 2], this.lazy[treeidx]); + } + this.lazy[treeidx] = Infinity; + } + } + + /** + * @param {number} treeidx + * @param {number} lo + * @param {number} hi + * @param {number} left + * @param {number} right + * @param {number} val + * @return {void} + */ + update(treeidx, lo, hi, left, right, val) { + this.propagate(treeidx, lo, hi); + if (lo > right || hi < left) return; + if (lo >= left && hi <= right) { + this.lazy[treeidx] = Math.min(this.lazy[treeidx], val); + this.propagate(treeidx, lo, hi); + return; + } + const mid = Math.floor((lo + hi) / 2); + this.update(2 * treeidx + 1, lo, mid, left, right, val); + this.update(2 * treeidx + 2, mid + 1, hi, left, right, val); + this.tree[treeidx] = Math.min(this.tree[2 * treeidx + 1], this.tree[2 * treeidx + 2]); + } + + /** + * @param {number} treeidx + * @param {number} lo + * @param {number} hi + * @param {number} idx + * @return {number} + */ + query(treeidx, lo, hi, idx) { + this.propagate(treeidx, lo, hi); + if (lo === hi) return this.tree[treeidx]; + const mid = Math.floor((lo + hi) / 2); + if (idx <= mid) return this.query(2 * treeidx + 1, lo, mid, idx); + else return this.query(2 * treeidx + 2, mid + 1, hi, idx); + } + + /** + * @param {number} left + * @param {number} right + * @param {number} val + * @return {number} + */ + updateRange(left, right, val) { + this.update(0, 0, this.n - 1, left, right, val); + } + + /** + * @param {number} idx + * @return {number} + */ + queryPoint(idx) { + return this.query(0, 0, this.n - 1, idx); + } +} + +class Solution { + /** + * @param {number[][]} intervals + * @param {number[]} queries + * @return {number[]} + */ + minInterval(intervals, queries) { + const points = []; + for (const interval of intervals) { + points.push(interval[0]); + points.push(interval[1]); + } + for (const q of queries) { + points.push(q); + } + const uniquePoints = [...new Set(points)].sort((a, b) => a - b); + const compress = new Map(); + uniquePoints.forEach((point, idx) => { + compress.set(point, idx); + }); + const segTree = new SegmentTree(uniquePoints.length); + for (const interval of intervals) { + const start = compress.get(interval[0]); + const end = compress.get(interval[1]); + const length = interval[1] - interval[0] + 1; + segTree.updateRange(start, end, length); + } + const ans = []; + for (const q of queries) { + const idx = compress.get(q); + const res = segTree.queryPoint(idx); + ans.push(res === Infinity ? -1 : res); + } + return ans; + } +} +``` + +```csharp +public class SegmentTree { + public int n; + public int[] tree; + public int[] lazy; + + public SegmentTree(int N) { + this.n = N; + tree = new int[4 * N]; + lazy = new int[4 * N]; + Array.Fill(tree, int.MaxValue); + Array.Fill(lazy, int.MaxValue); + } + + public void Propagate(int treeidx, int lo, int hi) { + if (lazy[treeidx] != int.MaxValue) { + tree[treeidx] = Math.Min(tree[treeidx], lazy[treeidx]); + if (lo != hi) { + lazy[2 * treeidx + 1] = Math.Min(lazy[2 * treeidx + 1], lazy[treeidx]); + lazy[2 * treeidx + 2] = Math.Min(lazy[2 * treeidx + 2], lazy[treeidx]); + } + lazy[treeidx] = int.MaxValue; + } + } + + public void Update(int treeidx, int lo, int hi, int left, int right, int val) { + Propagate(treeidx, lo, hi); + if (lo > right || hi < left) return; + if (lo >= left && hi <= right) { + lazy[treeidx] = Math.Min(lazy[treeidx], val); + Propagate(treeidx, lo, hi); + return; + } + int mid = (lo + hi) / 2; + Update(2 * treeidx + 1, lo, mid, left, right, val); + Update(2 * treeidx + 2, mid + 1, hi, left, right, val); + tree[treeidx] = Math.Min(tree[2 * treeidx + 1], tree[2 * treeidx + 2]); + } + + public int Query(int treeidx, int lo, int hi, int idx) { + Propagate(treeidx, lo, hi); + if (lo == hi) return tree[treeidx]; + int mid = (lo + hi) / 2; + if (idx <= mid) return Query(2 * treeidx + 1, lo, mid, idx); + else return Query(2 * treeidx + 2, mid + 1, hi, idx); + } + + public void Update(int left, int right, int val) { + Update(0, 0, n - 1, left, right, val); + } + + public int Query(int idx) { + return Query(0, 0, n - 1, idx); + } +} + +public class Solution { + public int[] MinInterval(int[][] intervals, int[] queries) { + List points = new List(); + foreach (var interval in intervals) { + points.Add(interval[0]); + points.Add(interval[1]); + } + foreach (var q in queries) { + points.Add(q); + } + points.Sort(); + points = new List(new HashSet(points)); + Dictionary compress = new Dictionary(); + for (int i = 0; i < points.Count; i++) { + compress[points[i]] = i; + } + SegmentTree segTree = new SegmentTree(points.Count); + foreach (var interval in intervals) { + int start = compress[interval[0]]; + int end = compress[interval[1]]; + int length = interval[1] - interval[0] + 1; + segTree.Update(start, end, length); + } + int[] ans = new int[queries.Length]; + for (int i = 0; i < queries.Length; i++) { + int idx = compress[queries[i]]; + int res = segTree.Query(idx); + ans[i] = (res == int.MaxValue) ? -1 : res; + } + return ans; + } +} +``` + +```go +type SegmentTree struct { + n int + tree []int + lazy []int +} + +func NewSegmentTree(n int) *SegmentTree { + tree := make([]int, 4*n) + lazy := make([]int, 4*n) + for i := range tree { + tree[i] = math.MaxInt32 + lazy[i] = math.MaxInt32 + } + return &SegmentTree{n: n, tree: tree, lazy: lazy} +} + +func (st *SegmentTree) propagate(treeIdx, lo, hi int) { + if st.lazy[treeIdx] != math.MaxInt32 { + st.tree[treeIdx] = min(st.tree[treeIdx], st.lazy[treeIdx]) + if lo != hi { + st.lazy[2*treeIdx+1] = min(st.lazy[2*treeIdx+1], st.lazy[treeIdx]) + st.lazy[2*treeIdx+2] = min(st.lazy[2*treeIdx+2], st.lazy[treeIdx]) + } + st.lazy[treeIdx] = math.MaxInt32 + } +} + +func (st *SegmentTree) updateRange(treeIdx, lo, hi, left, right, val int) { + st.propagate(treeIdx, lo, hi) + if lo > right || hi < left { + return + } + if lo >= left && hi <= right { + st.lazy[treeIdx] = min(st.lazy[treeIdx], val) + st.propagate(treeIdx, lo, hi) + return + } + mid := (lo + hi) / 2 + st.updateRange(2*treeIdx+1, lo, mid, left, right, val) + st.updateRange(2*treeIdx+2, mid+1, hi, left, right, val) + st.tree[treeIdx] = min(st.tree[2*treeIdx+1], st.tree[2*treeIdx+2]) +} + +func (st *SegmentTree) queryPoint(treeIdx, lo, hi, idx int) int { + st.propagate(treeIdx, lo, hi) + if lo == hi { + return st.tree[treeIdx] + } + mid := (lo + hi) / 2 + if idx <= mid { + return st.queryPoint(2*treeIdx+1, lo, mid, idx) + } + return st.queryPoint(2*treeIdx+2, mid+1, hi, idx) +} + +func (st *SegmentTree) Update(left, right, val int) { + st.updateRange(0, 0, st.n-1, left, right, val) +} + +func (st *SegmentTree) Query(idx int) int { + return st.queryPoint(0, 0, st.n-1, idx) +} + +func minInterval(intervals [][]int, queries []int) []int { + points := make(map[int]bool) + for _, interval := range intervals { + points[interval[0]] = true + points[interval[1]] = true + } + for _, q := range queries { + points[q] = true + } + + pointsList := make([]int, 0, len(points)) + for point := range points { + pointsList = append(pointsList, point) + } + sort.Ints(pointsList) + + compress := make(map[int]int) + for i, point := range pointsList { + compress[point] = i + } + + segTree := NewSegmentTree(len(pointsList)) + + for _, interval := range intervals { + start := compress[interval[0]] + end := compress[interval[1]] + length := interval[1] - interval[0] + 1 + segTree.Update(start, end, length) + } + + ans := make([]int, len(queries)) + for i, q := range queries { + idx := compress[q] + res := segTree.Query(idx) + if res == math.MaxInt32 { + ans[i] = -1 + } else { + ans[i] = res + } + } + + return ans +} +``` + +```kotlin +class SegmentTree(private val n: Int) { + private val tree = IntArray(4 * n) { Int.MAX_VALUE } + private val lazy = IntArray(4 * n) { Int.MAX_VALUE } + + private fun propagate(treeIdx: Int, lo: Int, hi: Int) { + if (lazy[treeIdx] != Int.MAX_VALUE) { + tree[treeIdx] = minOf(tree[treeIdx], lazy[treeIdx]) + if (lo != hi) { + lazy[2 * treeIdx + 1] = minOf(lazy[2 * treeIdx + 1], lazy[treeIdx]) + lazy[2 * treeIdx + 2] = minOf(lazy[2 * treeIdx + 2], lazy[treeIdx]) + } + lazy[treeIdx] = Int.MAX_VALUE + } + } + + private fun updateRange(treeIdx: Int, lo: Int, hi: Int, left: Int, right: Int, value: Int) { + propagate(treeIdx, lo, hi) + if (lo > right || hi < left) return + if (lo >= left && hi <= right) { + lazy[treeIdx] = minOf(lazy[treeIdx], value) + propagate(treeIdx, lo, hi) + return + } + val mid = (lo + hi) / 2 + updateRange(2 * treeIdx + 1, lo, mid, left, right, value) + updateRange(2 * treeIdx + 2, mid + 1, hi, left, right, value) + tree[treeIdx] = minOf(tree[2 * treeIdx + 1], tree[2 * treeIdx + 2]) + } + + private fun queryPoint(treeIdx: Int, lo: Int, hi: Int, idx: Int): Int { + propagate(treeIdx, lo, hi) + if (lo == hi) return tree[treeIdx] + val mid = (lo + hi) / 2 + return if (idx <= mid) { + queryPoint(2 * treeIdx + 1, lo, mid, idx) + } else { + queryPoint(2 * treeIdx + 2, mid + 1, hi, idx) + } + } + + fun update(left: Int, right: Int, value: Int) { + updateRange(0, 0, n - 1, left, right, value) + } + + fun query(idx: Int): Int { + return queryPoint(0, 0, n - 1, idx) + } +} + +class Solution { + fun minInterval(intervals: Array, queries: IntArray): IntArray { + val points = mutableSetOf() + intervals.forEach { interval -> + points.add(interval[0]) + points.add(interval[1]) + } + queries.forEach { points.add(it) } + + val pointsList = points.sorted() + val compress = pointsList.withIndex().associate { it.value to it.index } + + val segTree = SegmentTree(pointsList.size) + + intervals.forEach { interval -> + val start = compress[interval[0]]!! + val end = compress[interval[1]]!! + val length = interval[1] - interval[0] + 1 + segTree.update(start, end, length) + } + + return queries.map { q -> + val idx = compress[q]!! + val res = segTree.query(idx) + if (res == Int.MAX_VALUE) -1 else res + }.toIntArray() + } +} +``` + +```swift +class SegmentTree { + let n: Int + var tree: [Int] + var lazy: [Int] + let INF = Int.max + + init(_ N: Int) { + self.n = N + self.tree = [Int](repeating: INF, count: 4 * N) + self.lazy = [Int](repeating: INF, count: 4 * N) + } + + // Propagate lazy value at tree index over range [lo, hi] + func propagate(_ treeidx: Int, _ lo: Int, _ hi: Int) { + if lazy[treeidx] != INF { + tree[treeidx] = min(tree[treeidx], lazy[treeidx]) + if lo != hi { + lazy[2 * treeidx + 1] = min(lazy[2 * treeidx + 1], lazy[treeidx]) + lazy[2 * treeidx + 2] = min(lazy[2 * treeidx + 2], lazy[treeidx]) + } + lazy[treeidx] = INF + } + } + + // Update the segment tree over range [left, right] with value val + func update(_ treeidx: Int, _ lo: Int, _ hi: Int, _ left: Int, _ right: Int, _ val: Int) { + propagate(treeidx, lo, hi) + if lo > right || hi < left { + return + } + if lo >= left && hi <= right { + lazy[treeidx] = min(lazy[treeidx], val) + propagate(treeidx, lo, hi) + return + } + let mid = (lo + hi) / 2 + update(2 * treeidx + 1, lo, mid, left, right, val) + update(2 * treeidx + 2, mid + 1, hi, left, right, val) + tree[treeidx] = min(tree[2 * treeidx + 1], tree[2 * treeidx + 2]) + } + + // Query the value at index idx in the original array + func query(_ treeidx: Int, _ lo: Int, _ hi: Int, _ idx: Int) -> Int { + propagate(treeidx, lo, hi) + if lo == hi { + return tree[treeidx] + } + let mid = (lo + hi) / 2 + if idx <= mid { + return query(2 * treeidx + 1, lo, mid, idx) + } else { + return query(2 * treeidx + 2, mid + 1, hi, idx) + } + } + + func update_range(_ left: Int, _ right: Int, _ val: Int) { + update(0, 0, n - 1, left, right, val) + } + + func query_point(_ idx: Int) -> Int { + return query(0, 0, n - 1, idx) + } +} + +class Solution { + func minInterval(_ intervals: [[Int]], _ queries: [Int]) -> [Int] { + var points = [Int]() + // Create events for intervals + for interval in intervals { + points.append(interval[0]) + points.append(interval[1]) + } + // Create events for queries + for q in queries { + points.append(q) + } + + // Compress the coordinates + let sortedPoints = Array(Set(points)).sorted() + var compress = [Int: Int]() + for (i, point) in sortedPoints.enumerated() { + compress[point] = i + } + + // Lazy Segment Tree + let segTree = SegmentTree(sortedPoints.count) + + for interval in intervals { + let start = compress[interval[0]]! + let end = compress[interval[1]]! + let length = interval[1] - interval[0] + 1 + segTree.update_range(start, end, length) + } + + var ans = [Int]() + for q in queries { + let idx = compress[q]! + // Query for minSize + let res = segTree.query_point(idx) + ans.append(res == segTree.INF ? -1 : res) + } + return ans + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((n + m)\log k)$ +* Space complexity: + * $O(k)$ extra space. + * $O(m)$ space for the output array. + +> Where $m$ is the length of the array $queries$, $n$ is the length of the array $intervals$ and $k$ is the number of unique points. \ No newline at end of file diff --git a/articles/minimum-length-of-string-after-deleting-similar-ends.md b/articles/minimum-length-of-string-after-deleting-similar-ends.md new file mode 100644 index 000000000..81155cffe --- /dev/null +++ b/articles/minimum-length-of-string-after-deleting-similar-ends.md @@ -0,0 +1,86 @@ +## 1. Greedy + Two Pointers + +::tabs-start + +```python +class Solution: + def minimumLength(self, s: str) -> int: + l, r = 0, len(s) - 1 + + while l < r and s[l] == s[r]: + tmp = s[l] + while l <= r and s[l] == tmp: + l += 1 + while l <= r and s[r] == tmp: + r -= 1 + return r - l + 1 +``` + +```java +public class Solution { + public int minimumLength(String s) { + int l = 0, r = s.length() - 1; + + while (l < r && s.charAt(l) == s.charAt(r)) { + char tmp = s.charAt(l); + while (l <= r && s.charAt(l) == tmp) { + l++; + } + while (l <= r && s.charAt(r) == tmp) { + r--; + } + } + return r - l + 1; + } +} +``` + +```cpp +class Solution { +public: + int minimumLength(string s) { + int l = 0, r = s.length() - 1; + + while (l < r && s[l] == s[r]) { + char tmp = s[l]; + while (l <= r && s[l] == tmp) { + l++; + } + while (l <= r && s[r] == tmp) { + r--; + } + } + return r - l + 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minimumLength(s) { + let l = 0, r = s.length - 1; + + while (l < r && s[l] === s[r]) { + const tmp = s[l]; + while (l <= r && s[l] === tmp) { + l++; + } + while (l <= r && s[r] === tmp) { + r--; + } + } + return r - l + 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/minimum-number-of-arrows-to-burst-balloons.md b/articles/minimum-number-of-arrows-to-burst-balloons.md new file mode 100644 index 000000000..195e1e3e0 --- /dev/null +++ b/articles/minimum-number-of-arrows-to-burst-balloons.md @@ -0,0 +1,183 @@ +## 1. Greedy (Sort By Start Value) + +::tabs-start + +```python +class Solution: + def findMinArrowShots(self, points: List[List[int]]) -> int: + points.sort() + res, prevEnd = len(points), points[0][1] + + for i in range(1, len(points)): + curr = points[i] + if curr[0] <= prevEnd: + res -= 1 + prevEnd = min(curr[1], prevEnd) + else: + prevEnd = curr[1] + + return res +``` + +```java +public class Solution { + public int findMinArrowShots(int[][] points) { + Arrays.sort(points, (a, b) -> Integer.compare(a[0], b[0])); + int res = points.length, prevEnd = points[0][1]; + + for (int i = 1; i < points.length; i++) { + int[] curr = points[i]; + if (curr[0] <= prevEnd) { + res--; + prevEnd = Math.min(curr[1], prevEnd); + } else { + prevEnd = curr[1]; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int findMinArrowShots(vector>& points) { + sort(points.begin(), points.end()); + int res = points.size(), prevEnd = points[0][1]; + + for (int i = 1; i < points.size(); i++) { + vector& curr = points[i]; + if (curr[0] <= prevEnd) { + res--; + prevEnd = min(curr[1], prevEnd); + } else { + prevEnd = curr[1]; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} points + * @return {number} + */ + findMinArrowShots(points) { + points.sort((a, b) => a[0] - b[0]); + let res = points.length, prevEnd = points[0][1]; + + for (let i = 1; i < points.length; i++) { + let curr = points[i]; + if (curr[0] <= prevEnd) { + res--; + prevEnd = Math.min(curr[1], prevEnd); + } else { + prevEnd = curr[1]; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Greedy (Sort By End Value) + +::tabs-start + +```python +class Solution: + def findMinArrowShots(self, points: List[List[int]]) -> int: + points.sort(key=lambda x: x[1]) + res, prevEnd = 1, points[0][1] + + for i in range(1, len(points)): + if points[i][0] > prevEnd: + prevEnd = points[i][1] + res += 1 + + return res +``` + +```java +public class Solution { + public int findMinArrowShots(int[][] points) { + Arrays.sort(points, (a, b) -> Integer.compare(a[1], b[1])); + int res = 1, prevEnd = points[0][1]; + + for (int i = 1; i < points.length; i++) { + if (points[i][0] > prevEnd) { + prevEnd = points[i][1]; + res++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int findMinArrowShots(vector>& points) { + sort(points.begin(), points.end(), [](const auto& a, const auto& b) { + return a[1] < b[1]; + }); + int res = 1, prevEnd = points[0][1]; + + for (int i = 1; i < points.size(); i++) { + if (points[i][0] > prevEnd) { + prevEnd = points[i][1]; + res++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} points + * @return {number} + */ + findMinArrowShots(points) { + points.sort((a, b) => a[1] - b[1]); + let res = 1, prevEnd = points[0][1]; + + for (let i = 1; i < points.length; i++) { + if (points[i][0] > prevEnd) { + prevEnd = points[i][1]; + res++; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file diff --git a/articles/minimum-number-of-days-to-eat-n-oranges.md b/articles/minimum-number-of-days-to-eat-n-oranges.md new file mode 100644 index 000000000..115da181e --- /dev/null +++ b/articles/minimum-number-of-days-to-eat-n-oranges.md @@ -0,0 +1,354 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minDays(self, n: int) -> int: + dp = {} + + def dfs(n): + if n == 0: + return 0 + if n in dp: + return dp[n] + + res = 1 + dfs(n - 1) + if n % 3 == 0: + res = min(res, 1 + dfs(n // 3)) + if n % 2 == 0: + res = min(res, 1 + dfs(n // 2)) + + dp[n] = res + return res + + return dfs(n) +``` + +```java +public class Solution { + private Map dp = new HashMap<>(); + + public int minDays(int n) { + return dfs(n); + } + + private int dfs(int n) { + if (n == 0) return 0; + if (dp.containsKey(n)) return dp.get(n); + + int res = 1 + dfs(n - 1); + if (n % 3 == 0) res = Math.min(res, 1 + dfs(n / 3)); + if (n % 2 == 0) res = Math.min(res, 1 + dfs(n / 2)); + + dp.put(n, res); + return res; + } +} +``` + +```cpp +class Solution { +public: + unordered_map dp; + + int minDays(int n) { + return dfs(n); + } + + int dfs(int n) { + if (n == 0) return 0; + if (dp.count(n)) return dp[n]; + + int res = 1 + dfs(n - 1); + if (n % 3 == 0) res = min(res, 1 + dfs(n / 3)); + if (n % 2 == 0) res = min(res, 1 + dfs(n / 2)); + + return dp[n] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + minDays(n) { + const dp = new Map(); + + const dfs = (n) => { + if (n === 0) return 0; + if (dp.has(n)) return dp.get(n); + + let res = 1 + dfs(n - 1); + if (n % 3 === 0) res = Math.min(res, 1 + dfs(n / 3)); + if (n % 2 === 0) res = Math.min(res, 1 + dfs(n / 2)); + + dp.set(n, res); + return res; + }; + + return dfs(n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Greedy + Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minDays(self, n: int) -> int: + dp = {0: 0, 1: 1} + + def dfs(n): + if n in dp: + return dp[n] + + res = 1 + (n % 2) + dfs(n // 2) + res = min(res, 1 + (n % 3) + dfs(n // 3)) + dp[n] = res + return res + + return dfs(n) +``` + +```java +public class Solution { + private Map dp = new HashMap<>(); + + public int minDays(int n) { + dp.put(0, 0); + dp.put(1, 1); + return dfs(n); + } + + private int dfs(int n) { + if (dp.containsKey(n)) return dp.get(n); + + int res = 1 + (n % 2) + dfs(n / 2); + res = Math.min(res, 1 + (n % 3) + dfs(n / 3)); + + dp.put(n, res); + return res; + } +} +``` + +```cpp +class Solution { +public: + unordered_map dp; + + int minDays(int n) { + dp[0] = 0; + dp[1] = 1; + return dfs(n); + } + +private: + int dfs(int n) { + if (dp.count(n)) return dp[n]; + + int res = 1 + (n % 2) + dfs(n / 2); + res = min(res, 1 + (n % 3) + dfs(n / 3)); + + return dp[n] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + minDays(n) { + const dp = new Map(); + dp.set(0, 0); + dp.set(1, 1); + + const dfs = (n) => { + if (dp.has(n)) return dp.get(n); + + let res = 1 + (n % 2) + dfs(Math.floor(n / 2)); + res = Math.min(res, 1 + (n % 3) + dfs(Math.floor(n / 3))); + + dp.set(n, res); + return res; + }; + + return dfs(n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ + +--- + +## 3. Breadth First Search + +::tabs-start + +```python +class Solution: + def minDays(self, n: int) -> int: + q = deque([n]) + visit = set() + res = 0 + + while q: + res += 1 + for _ in range(len(q)): + node = q.popleft() + nei = node - 1 + if nei == 0: + return res + if nei not in visit: + visit.add(nei) + q.append(nei) + for d in range(2, 4): + if node % d == 0: + nei = node // d + if nei == 0: + return res + if nei not in visit: + visit.add(nei) + q.append(nei) + return res +``` + +```java +public class Solution { + public int minDays(int n) { + Queue q = new LinkedList<>(); + Set visit = new HashSet<>(); + q.offer(n); + int res = 0; + + while (!q.isEmpty()) { + res++; + for (int i = q.size(); i > 0; i--) { + int node = q.poll(); + int nei = node - 1; + if (nei == 0) return res; + if (!visit.contains(nei)) { + visit.add(nei); + q.offer(nei); + } + for (int d = 2; d <= 3; d++) { + if (node % d == 0) { + nei = node / d; + if (nei == 0) return res; + if (!visit.contains(nei)) { + visit.add(nei); + q.offer(nei); + } + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minDays(int n) { + queue q; + unordered_set visit; + q.push(n); + int res = 0; + + while (!q.empty()) { + res++; + for (int i = q.size(); i > 0; i--) { + int node = q.front(); q.pop(); + int nei = node - 1; + if (nei == 0) return res; + if (visit.find(nei) == visit.end()) { + visit.insert(nei); + q.push(nei); + } + for (int d = 2; d <= 3; d++) { + if (node % d == 0) { + nei = node / d; + if (nei == 0) return res; + if (visit.find(nei) == visit.end()) { + visit.insert(nei); + q.push(nei); + } + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + minDays(n) { + const q = new Queue([n]); + const visit = new Set(); + let res = 0; + + while (!q.isEmpty()) { + res++; + for (let i = q.size(); i > 0; i--) { + let node = q.pop(); + let nei = node - 1; + if (nei === 0) return res; + if (!visit.has(nei)) { + visit.add(nei); + q.push(nei); + } + for (let d = 2; d <= 3; d++) { + if (node % d === 0) { + nei = Math.floor(node / d); + if (nei === 0) return res; + if (!visit.has(nei)) { + visit.add(nei); + q.push(nei); + } + } + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ \ No newline at end of file diff --git a/articles/minimum-number-of-flips-to-make-the-binary-string-alternating.md b/articles/minimum-number-of-flips-to-make-the-binary-string-alternating.md new file mode 100644 index 000000000..b0afa6412 --- /dev/null +++ b/articles/minimum-number-of-flips-to-make-the-binary-string-alternating.md @@ -0,0 +1,701 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def minFlips(self, s: str) -> int: + res = n = len(s) + alt1, alt2 = [], [] + for i in range(n): + alt1.append("0" if i % 2 == 0 else "1") + alt2.append("1" if i % 2 == 0 else "0") + + def diff(A, B): + cnt = 0 + for i in range(n): + cnt += 1 if (A[i] != B[i]) else 0 + return cnt + + for i in range(n): + newS = s[i:] + s[:i] + res = min(res, min(diff(alt1, newS), diff(alt2, newS))) + return res +``` + +```java +public class Solution { + public int minFlips(String s) { + int n = s.length(), res = n; + StringBuilder alt1 = new StringBuilder(); + StringBuilder alt2 = new StringBuilder(); + + for (int i = 0; i < n; i++) { + alt1.append(i % 2 == 0 ? '0' : '1'); + alt2.append(i % 2 == 0 ? '1' : '0'); + } + + for (int i = 0; i < n; i++) { + String newS = s.substring(i) + s.substring(0, i); + res = Math.min(res, Math.min(diff(alt1, newS), diff(alt2, newS))); + } + + return res; + } + + private int diff(StringBuilder a, String b) { + int cnt = 0; + for (int i = 0; i < a.length(); i++) { + if (a.charAt(i) != b.charAt(i)) cnt++; + } + return cnt; + } +} +``` + +```cpp +class Solution { +public: + int minFlips(string s) { + int n = s.size(), res = n; + string alt1, alt2; + + for (int i = 0; i < n; i++) { + alt1 += (i % 2 == 0) ? '0' : '1'; + alt2 += (i % 2 == 0) ? '1' : '0'; + } + + for (int i = 0; i < n; i++) { + string newS = s.substr(i) + s.substr(0, i); + res = min(res, min(diff(alt1, newS), diff(alt2, newS))); + } + + return res; + } + +private: + int diff(const string &a, const string &b) { + int cnt = 0; + for (int i = 0; i < a.size(); i++) { + if (a[i] != b[i]) cnt++; + } + return cnt; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minFlips(s) { + const n = s.length; + let res = n; + let alt1 = "", alt2 = ""; + + for (let i = 0; i < n; i++) { + alt1 += i % 2 === 0 ? '0' : '1'; + alt2 += i % 2 === 0 ? '1' : '0'; + } + + const diff = (a, b) => { + let cnt = 0; + for (let i = 0; i < a.length; i++) { + if (a[i] !== b[i]) cnt++; + } + return cnt; + }; + + for (let i = 0; i < n; i++) { + const newS = s.slice(i) + s.slice(0, i); + res = Math.min(res, Math.min(diff(alt1, newS), diff(alt2, newS))); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Brute Force (Space Optimized) + +::tabs-start + +```python +class Solution: + def minFlips(self, s: str) -> int: + n = res = len(s) + + for i in range(n): + start_0 = 1 if s[i] != '0' else 0 + start_1 = 1 if s[i] != '1' else 0 + c = '0' + j = (i + 1) % n + while j != i: + start_1 += 1 if s[j] != c else 0 + start_0 += 1 if s[j] == c else 0 + c = '0' if c == '1' else '1' + j = (j + 1) % n + + res = min(res, min(start_1, start_0)) + return res +``` + +```java +public class Solution { + public int minFlips(String s) { + int n = s.length(); + int res = n; + + for (int i = 0; i < n; i++) { + int start0 = s.charAt(i) != '0' ? 1 : 0; + int start1 = s.charAt(i) != '1' ? 1 : 0; + char c = '0'; + int j = (i + 1) % n; + + while (j != i) { + start1 += s.charAt(j) != c ? 1 : 0; + start0 += s.charAt(j) == c ? 1 : 0; + c = c == '1' ? '0' : '1'; + j = (j + 1) % n; + } + + res = Math.min(res, Math.min(start1, start0)); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minFlips(string s) { + int n = s.size(), res = n; + + for (int i = 0; i < n; i++) { + int start0 = (s[i] != '0') ? 1 : 0; + int start1 = (s[i] != '1') ? 1 : 0; + char c = '0'; + int j = (i + 1) % n; + + while (j != i) { + start1 += (s[j] != c) ? 1 : 0; + start0 += (s[j] == c) ? 1 : 0; + c = (c == '1') ? '0' : '1'; + j = (j + 1) % n; + } + + res = min(res, min(start1, start0)); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minFlips(s) { + const n = s.length; + let res = n; + + for (let i = 0; i < n; i++) { + let start0 = s[i] !== '0' ? 1 : 0; + let start1 = s[i] !== '1' ? 1 : 0; + let c = '0'; + let j = (i + 1) % n; + + while (j !== i) { + start1 += s[j] !== c ? 1 : 0; + start0 += s[j] === c ? 1 : 0; + c = c === '1' ? '0' : '1'; + j = (j + 1) % n; + } + + res = Math.min(res, Math.min(start1, start0)); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Sliding Window + +::tabs-start + +```python +class Solution: + def minFlips(self, s: str) -> int: + n = len(s) + s = s + s + alt1, alt2 = [], [] + for i in range(len(s)): + alt1.append("0" if i % 2 == 0 else "1") + alt2.append("1" if i % 2 == 0 else "0") + + res = len(s) + diff1, diff2 = 0, 0 + l = 0 + + for r in range(len(s)): + if s[r] != alt1[r]: + diff1 += 1 + if s[r] != alt2[r]: + diff2 += 1 + + if r - l + 1 > n: + if s[l] != alt1[l]: + diff1 -= 1 + if s[l] != alt2[l]: + diff2 -= 1 + l += 1 + + if r - l + 1 == n: + res = min(res, diff1, diff2) + + return res +``` + +```java +public class Solution { + public int minFlips(String s) { + int n = s.length(); + s = s + s; + StringBuilder alt1 = new StringBuilder(); + StringBuilder alt2 = new StringBuilder(); + + for (int i = 0; i < s.length(); i++) { + alt1.append(i % 2 == 0 ? '0' : '1'); + alt2.append(i % 2 == 0 ? '1' : '0'); + } + + int res = n, diff1 = 0, diff2 = 0, l = 0; + + for (int r = 0; r < s.length(); r++) { + if (s.charAt(r) != alt1.charAt(r)) diff1++; + if (s.charAt(r) != alt2.charAt(r)) diff2++; + + if (r - l + 1 > n) { + if (s.charAt(l) != alt1.charAt(l)) diff1--; + if (s.charAt(l) != alt2.charAt(l)) diff2--; + l++; + } + + if (r - l + 1 == n) { + res = Math.min(res, Math.min(diff1, diff2)); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minFlips(string s) { + int n = s.size(); + s += s; + string alt1, alt2; + for (int i = 0; i < s.size(); i++) { + alt1 += (i % 2 == 0) ? '0' : '1'; + alt2 += (i % 2 == 0) ? '1' : '0'; + } + + int res = n, diff1 = 0, diff2 = 0, l = 0; + + for (int r = 0; r < s.size(); r++) { + if (s[r] != alt1[r]) diff1++; + if (s[r] != alt2[r]) diff2++; + + if (r - l + 1 > n) { + if (s[l] != alt1[l]) diff1--; + if (s[l] != alt2[l]) diff2--; + l++; + } + + if (r - l + 1 == n) { + res = min(res, min(diff1, diff2)); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minFlips(s) { + const n = s.length; + s = s + s; + let alt1 = [], alt2 = []; + + for (let i = 0; i < s.length; i++) { + alt1.push(i % 2 === 0 ? "0" : "1"); + alt2.push(i % 2 === 0 ? "1" : "0"); + } + + let res = n, diff1 = 0, diff2 = 0, l = 0; + + for (let r = 0; r < s.length; r++) { + if (s[r] !== alt1[r]) diff1++; + if (s[r] !== alt2[r]) diff2++; + + if (r - l + 1 > n) { + if (s[l] !== alt1[l]) diff1--; + if (s[l] !== alt2[l]) diff2--; + l++; + } + + if (r - l + 1 === n) { + res = Math.min(res, diff1, diff2); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Sliding Window (Space Optimized) + +::tabs-start + +```python +class Solution: + def minFlips(self, s: str) -> int: + res = n = len(s) + diff1 = diff2 = l = 0 + + rstart_0 = lstart_0 = '0' + + for r in range(2 * n): + if s[r % n] != rstart_0: + diff1 += 1 + if s[r % n] == rstart_0: + diff2 += 1 + + if r - l + 1 > n: + if s[l] != lstart_0: + diff1 -= 1 + if s[l] == lstart_0: + diff2 -= 1 + l += 1 + lstart_0 = '1' if lstart_0 == '0' else '0' + + if r - l + 1 == n: + res = min(res, diff1, diff2) + + rstart_0 = '1' if rstart_0 == '0' else '0' + + return res +``` + +```java +public class Solution { + public int minFlips(String s) { + int n = s.length(); + int res = n, diff1 = 0, diff2 = 0, l = 0; + + char rstart_0 = '0', lstart_0 = '0'; + + for (int r = 0; r < 2 * n; r++) { + if (s.charAt(r % n) != rstart_0) diff1++; + if (s.charAt(r % n) == rstart_0) diff2++; + + if (r - l + 1 > n) { + if (s.charAt(l) != lstart_0) diff1--; + if (s.charAt(l) == lstart_0) diff2--; + l++; + lstart_0 = (lstart_0 == '0') ? '1' : '0'; + } + + if (r - l + 1 == n) { + res = Math.min(res, Math.min(diff1, diff2)); + } + + rstart_0 = (rstart_0 == '0') ? '1' : '0'; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minFlips(string s) { + int n = s.size(); + int res = n, diff1 = 0, diff2 = 0, l = 0; + + char rstart_0 = '0', lstart_0 = '0'; + + for (int r = 0; r < 2 * n; r++) { + if (s[r % n] != rstart_0) diff1++; + if (s[r % n] == rstart_0) diff2++; + + if (r - l + 1 > n) { + if (s[l] != lstart_0) diff1--; + if (s[l] == lstart_0) diff2--; + l++; + lstart_0 = (lstart_0 == '0') ? '1' : '0'; + } + + if (r - l + 1 == n) { + res = min(res, min(diff1, diff2)); + } + + rstart_0 = (rstart_0 == '0') ? '1' : '0'; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minFlips(s) { + const n = s.length; + let res = n, diff1 = 0, diff2 = 0, l = 0; + + let rstart_0 = '0', lstart_0 = '0'; + + for (let r = 0; r < 2 * n; r++) { + if (s[r % n] !== rstart_0) diff1++; + if (s[r % n] === rstart_0) diff2++; + + if (r - l + 1 > n) { + if (s[l] !== lstart_0) diff1--; + if (s[l] === lstart_0) diff2--; + l++; + lstart_0 = lstart_0 === '0' ? '1' : '0'; + } + + if (r - l + 1 === n) { + res = Math.min(res, Math.min(diff1, diff2)); + } + + rstart_0 = rstart_0 === '0' ? '1' : '0'; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 5. Dynamic Programming + +::tabs-start + +```python +class Solution: + def minFlips(self, s: str) -> int: + start_1 = 0 + for i in range(len(s)): + if i & 1: + start_1 += s[i] == "1" + else: + start_1 += s[i] == "0" + + start_0 = len(s) - start_1 + ans = min(start_0, start_1) + if len(s) % 2 == 0: + return ans + + dp0, dp1 = start_0, start_1 + for c in s: + dp0, dp1 = dp1, dp0 + if c == "1": + dp0 += 1 + dp1 -= 1 + else: + dp0 -= 1 + dp1 += 1 + ans = min(dp0, dp1, ans) + return ans +``` + +```java +public class Solution { + public int minFlips(String s) { + int start_1 = 0; + int n = s.length(); + + for (int i = 0; i < n; i++) { + if ((i & 1) == 1) { + start_1 += s.charAt(i) == '1' ? 1 : 0; + } else { + start_1 += s.charAt(i) == '0' ? 1 : 0; + } + } + + int start_0 = n - start_1; + int ans = Math.min(start_0, start_1); + if (n % 2 == 0) { + return ans; + } + + int dp0 = start_0, dp1 = start_1; + for (char c : s.toCharArray()) { + int temp = dp0; + dp0 = dp1; + dp1 = temp; + if (c == '1') { + dp0++; + dp1--; + } else { + dp0--; + dp1++; + } + ans = Math.min(ans, Math.min(dp0, dp1)); + } + + return ans; + } +} +``` + +```cpp +class Solution { +public: + int minFlips(string s) { + int start_1 = 0, n = s.size(); + + for (int i = 0; i < n; i++) { + if (i & 1) { + start_1 += (s[i] == '1'); + } else { + start_1 += (s[i] == '0'); + } + } + + int start_0 = n - start_1; + int ans = min(start_0, start_1); + if (n % 2 == 0) { + return ans; + } + + int dp0 = start_0, dp1 = start_1; + for (char c : s) { + int temp = dp0; + dp0 = dp1; + dp1 = temp; + if (c == '1') { + dp0++; + dp1--; + } else { + dp0--; + dp1++; + } + ans = min({ans, dp0, dp1}); + } + + return ans; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minFlips(s) { + let start_1 = 0; + const n = s.length; + + for (let i = 0; i < n; i++) { + if (i & 1) { + start_1 += s[i] === '1' ? 1 : 0; + } else { + start_1 += s[i] === '0' ? 1 : 0; + } + } + + let start_0 = n - start_1; + let ans = Math.min(start_0, start_1); + if (n % 2 === 0) { + return ans; + } + + let dp0 = start_0, dp1 = start_1; + for (const c of s) { + [dp0, dp1] = [dp1, dp0]; + if (c === '1') { + dp0++; + dp1--; + } else { + dp0--; + dp1++; + } + ans = Math.min(ans, dp0, dp1); + } + + return ans; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/minimum-number-of-operations-to-make-array-continuous.md b/articles/minimum-number-of-operations-to-make-array-continuous.md new file mode 100644 index 000000000..fda4f013d --- /dev/null +++ b/articles/minimum-number-of-operations-to-make-array-continuous.md @@ -0,0 +1,449 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def minOperations(self, nums: List[int]) -> int: + N = len(nums) + res = float("inf") + nums = sorted(set(nums)) + n = len(nums) + + for i in range(n): + noChange = 1 + for j in range(i + 1, n): + if nums[i] < nums[j] < nums[i] + N: + noChange += 1 + res = min(res, N - noChange) + + return res +``` + +```java +public class Solution { + public int minOperations(int[] nums) { + int N = nums.length; + int res = Integer.MAX_VALUE; + TreeSet set = new TreeSet<>(); + for (int num : nums) { + set.add(num); + } + Integer[] sortedNums = set.toArray(new Integer[0]); + int n = sortedNums.length; + + for (int i = 0; i < n; i++) { + int noChange = 1; + for (int j = i + 1; j < n; j++) { + if (sortedNums[j] < sortedNums[i] + N) { + noChange++; + } + } + res = Math.min(res, N - noChange); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minOperations(vector& nums) { + int N = nums.size(); + int res = INT_MAX; + set uniqueNums(nums.begin(), nums.end()); + vector sortedNums(uniqueNums.begin(), uniqueNums.end()); + int n = sortedNums.size(); + + for (int i = 0; i < n; i++) { + int noChange = 1; + for (int j = i + 1; j < n; j++) { + if (sortedNums[j] < sortedNums[i] + N) { + noChange++; + } + } + res = min(res, N - noChange); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minOperations(nums) { + const N = nums.length; + let res = Infinity; + const uniqueNums = Array.from(new Set(nums)).sort((a, b) => a - b); + const n = uniqueNums.length; + + for (let i = 0; i < n; i++) { + let noChange = 1; + for (let j = i + 1; j < n; j++) { + if (uniqueNums[j] < uniqueNums[i] + N) { + noChange++; + } + } + res = Math.min(res, N - noChange); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Binary Search + +::tabs-start + +```python +class Solution: + def minOperations(self, nums: List[int]) -> int: + N = len(nums) + res = float("inf") + nums = sorted(set(nums)) + n = len(nums) + + for i in range(n): + l, r = i, n + while l < r: + mid = (l + r) // 2 + if nums[mid] < nums[i] + N: + l = mid + 1 + else: + r = mid + noChange = l - i + res = min(res, N - noChange) + + return res +``` + +```java +public class Solution { + public int minOperations(int[] nums) { + int N = nums.length; + int res = Integer.MAX_VALUE; + TreeSet set = new TreeSet<>(); + for (int num : nums) { + set.add(num); + } + Integer[] sortedNums = set.toArray(new Integer[0]); + int n = sortedNums.length; + + for (int i = 0; i < n; i++) { + int l = i, r = n; + while (l < r) { + int mid = l + (r - l) / 2; + if (sortedNums[mid] < sortedNums[i] + N) { + l = mid + 1; + } else { + r = mid; + } + } + int noChange = l - i; + res = Math.min(res, N - noChange); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minOperations(vector& nums) { + int N = nums.size(); + int res = INT_MAX; + set uniqueNums(nums.begin(), nums.end()); + vector sortedNums(uniqueNums.begin(), uniqueNums.end()); + int n = sortedNums.size(); + + for (int i = 0; i < n; i++) { + int l = i, r = n; + while (l < r) { + int mid = l + (r - l) / 2; + if (sortedNums[mid] < sortedNums[i] + N) { + l = mid + 1; + } else { + r = mid; + } + } + int noChange = l - i; + res = min(res, N - noChange); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minOperations(nums) { + const N = nums.length; + let res = Infinity; + const uniqueNums = Array.from(new Set(nums)).sort((a, b) => a - b); + const n = uniqueNums.length; + + for (let i = 0; i < n; i++) { + let l = i, r = n; + while (l < r) { + const mid = Math.floor((l + r) / 2); + if (uniqueNums[mid] < uniqueNums[i] + N) { + l = mid + 1; + } else { + r = mid; + } + } + const noChange = l - i; + res = Math.min(res, N - noChange); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sliding Window + +::tabs-start + +```python +class Solution: + def minOperations(self, nums: List[int]) -> int: + length = len(nums) + nums = sorted(set(nums)) + res = length + r = 0 + + for l in range(len(nums)): + while r < len(nums) and nums[r] < nums[l] + length: + r += 1 + window = r - l + res = min(res, length - window) + + return res +``` + +```java +public class Solution { + public int minOperations(int[] nums) { + int length = nums.length; + TreeSet set = new TreeSet<>(); + for (int num : nums) { + set.add(num); + } + List sortedNums = new ArrayList<>(set); + int res = length, r = 0; + + for (int l = 0; l < sortedNums.size(); l++) { + while (r < sortedNums.size() && sortedNums.get(r) < sortedNums.get(l) + length) { + r++; + } + int window = r - l; + res = Math.min(res, length - window); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minOperations(vector& nums) { + int length = nums.size(); + set uniqueNums(nums.begin(), nums.end()); + vector sortedNums(uniqueNums.begin(), uniqueNums.end()); + int res = length, r = 0; + + for (int l = 0; l < sortedNums.size(); l++) { + while (r < sortedNums.size() && sortedNums[r] < sortedNums[l] + length) { + r++; + } + int window = r - l; + res = min(res, length - window); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minOperations(nums) { + const length = nums.length; + const uniqueNums = Array.from(new Set(nums)).sort((a, b) => a - b); + let res = length, r = 0; + + for (let l = 0; l < uniqueNums.length; l++) { + while (r < uniqueNums.length && uniqueNums[r] < uniqueNums[l] + length) { + r++; + } + const window = r - l; + res = Math.min(res, length - window); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Sliding Window (Optimal) + +::tabs-start + +```python +class Solution: + def minOperations(self, nums: List[int]) -> int: + length = len(nums) + nums.sort() + res = length + n = 1 + + for i in range(1, length): + if nums[i] != nums[i - 1]: + nums[n] = nums[i] + n += 1 + + l = 0 + for r in range(n): + l += (nums[r] - nums[l] > length - 1) + + return length - (n - l) +``` + +```java +public class Solution { + public int minOperations(int[] nums) { + int length = nums.length; + Arrays.sort(nums); + int n = 1; + + for (int i = 1; i < length; i++) { + if (nums[i] != nums[i - 1]) { + nums[n] = nums[i]; + n++; + } + } + + int l = 0; + for (int r = 0; r < n; r++) { + if (nums[r] - nums[l] > length - 1) { + l++; + } + } + + return length - (n - l); + } +} +``` + +```cpp +class Solution { +public: + int minOperations(vector& nums) { + int length = nums.size(); + sort(nums.begin(), nums.end()); + int n = 1; + + for (int i = 1; i < length; i++) { + if (nums[i] != nums[i - 1]) { + nums[n] = nums[i]; + n++; + } + } + + int l = 0; + for (int r = 0; r < n; r++) { + if (nums[r] - nums[l] > length - 1) { + l++; + } + } + + return length - (n - l); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minOperations(nums) { + const length = nums.length; + nums.sort((a, b) => a - b); + let n = 1; + + for (let i = 1; i < length; i++) { + if (nums[i] !== nums[i - 1]) { + nums[n] = nums[i]; + n++; + } + } + + let l = 0; + for (let r = 0; r < n; r++) { + if (nums[r] - nums[l] > length - 1) { + l++; + } + } + + return length - (n - l); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file diff --git a/articles/minimum-number-of-operations-to-make-array-empty.md b/articles/minimum-number-of-operations-to-make-array-empty.md new file mode 100644 index 000000000..fbb2aa367 --- /dev/null +++ b/articles/minimum-number-of-operations-to-make-array-empty.md @@ -0,0 +1,563 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def minOperations(self, nums: List[int]) -> int: + def dfs(cur): + if cur < 0: + return float('inf') + if cur == 0: + return 0 + + ops = min(dfs(cur - 2), dfs(cur - 3)) + return 1 + ops + + count = Counter(nums) + res = 0 + for num, cnt in count.items(): + op = dfs(cnt) + if op == float("inf"): + return -1 + res += op + + return res +``` + +```java +public class Solution { + public int minOperations(int[] nums) { + Map count = new HashMap<>(); + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + int res = 0; + for (int cnt : count.values()) { + int op = dfs(cnt); + if (op == Integer.MAX_VALUE) { + return -1; + } + res += op; + } + + return res; + } + + private int dfs(int cur) { + if (cur < 0) { + return Integer.MAX_VALUE; + } + if (cur == 0) { + return 0; + } + + int ops = Math.min(dfs(cur - 2), dfs(cur - 3)); + return ops == Integer.MAX_VALUE ? ops : 1 + ops; + } +} +``` + +```cpp +class Solution { +public: + int minOperations(vector& nums) { + unordered_map count; + for (int num : nums) { + count[num]++; + } + + int res = 0; + for (auto& [num, cnt] : count) { + int op = dfs(cnt); + if (op == INT_MAX) { + return -1; + } + res += op; + } + + return res; + } + +private: + int dfs(int cur) { + if (cur < 0) { + return INT_MAX; + } + if (cur == 0) { + return 0; + } + + int ops = min(dfs(cur - 2), dfs(cur - 3)); + return ops == INT_MAX ? ops : 1 + ops; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minOperations(nums) { + const dfs = (cur) => { + if (cur < 0) { + return Infinity; + } + if (cur === 0) { + return 0; + } + + const ops = Math.min(dfs(cur - 2), dfs(cur - 3)); + return isFinite(ops) ? 1 + ops : ops; + }; + + const count = new Map(); + for (const num of nums) { + count.set(num, (count.get(num) || 0) + 1); + } + + let res = 0; + for (const cnt of count.values()) { + const op = dfs(cnt); + if (op === Infinity) { + return -1; + } + res += op; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the average frequency of the array elements. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minOperations(self, nums: List[int]) -> int: + cache = {} + + def dfs(num): + if num < 0: + return float("inf") + if num in [2, 3]: + return 1 + if num in cache: + return cache[num] + + res = min(dfs(num - 2), dfs(num - 3)) + cache[num] = res + 1 + return cache[num] + + count = Counter(nums) + res = 0 + for num, cnt in count.items(): + op = dfs(cnt) + if op == float("inf"): + return -1 + res += op + + return res +``` + +```java +public class Solution { + private Map cache; + + public int minOperations(int[] nums) { + Map count = new HashMap<>(); + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + cache = new HashMap<>(); + int res = 0; + for (int cnt : count.values()) { + int op = dfs(cnt); + if (op == Integer.MAX_VALUE) { + return -1; + } + res += op; + } + + return res; + } + + private int dfs(int cur) { + if (cur < 0) { + return Integer.MAX_VALUE; + } + if (cur == 2 || cur == 3) { + return 1; + } + if (cache.containsKey(cur)) { + return cache.get(cur); + } + + int res = Math.min(dfs(cur - 2), dfs(cur - 3)); + cache.put(cur, res == Integer.MAX_VALUE ? res : res + 1); + return cache.get(cur); + } +} +``` + +```cpp +class Solution { + unordered_map cache; +public: + int minOperations(vector& nums) { + unordered_map count; + for (int num : nums) { + count[num]++; + } + + int res = 0; + for (auto& [num, cnt] : count) { + int op = dfs(cnt); + if (op == INT_MAX) { + return -1; + } + res += op; + } + + return res; + } + +private: + int dfs(int cur) { + if (cur < 0) { + return INT_MAX; + } + if (cur == 2 || cur == 3) { + return 1; + } + if (cache.count(cur)) { + return cache[cur]; + } + + int res = min(dfs(cur - 2), dfs(cur - 3)); + cache[cur] = res == INT_MAX ? res : res + 1; + return cache[cur]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minOperations(nums) { + const cache = new Map(); + const dfs = (cur) => { + if (cur < 0) { + return Infinity; + } + if (cur === 2 || cur === 3) { + return 1; + } + if (cache.has(cur)) { + return cache.get(cur); + } + + const res = Math.min(dfs(cur - 2), dfs(cur - 3)); + cache.set(cur, isFinite(res) ? 1 + res : res); + return cache.get(cur); + }; + + const count = new Map(); + for (const num of nums) { + count.set(num, (count.get(num) || 0) + 1); + } + + let res = 0; + for (const cnt of count.values()) { + const op = dfs(cnt); + if (!isFinite(op)) { + return -1; + } + res += op; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the average frequency of the array elements. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minOperations(self, nums: List[int]) -> int: + count = Counter(nums) + maxf = max(count.values()) + minOps = [0] * (maxf + 1) + minOps[1] = float("inf") + + for i in range(2, maxf + 1): + minOps[i] = minOps[i - 2] + if i - 3 >= 0: + minOps[i] = min(minOps[i], minOps[i - 3]) + if minOps[i] != float("inf"): + minOps[i] += 1 + + res = 0 + for num, cnt in count.items(): + op = minOps[cnt] + if op == float("inf"): + return -1 + res += op + + return res +``` + +```java +public class Solution { + public int minOperations(int[] nums) { + Map count = new HashMap<>(); + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + int maxf = count.values().stream().max(Integer::compare).orElse(0); + int[] minOps = new int[maxf + 1]; + minOps[1] = Integer.MAX_VALUE; + + for (int i = 2; i <= maxf; i++) { + minOps[i] = minOps[i - 2]; + if (i - 3 >= 0) { + minOps[i] = Math.min(minOps[i], minOps[i - 3]); + } + if (minOps[i] != Integer.MAX_VALUE) { + minOps[i] += 1; + } + } + + int res = 0; + for (int cnt : count.values()) { + int op = minOps[cnt]; + if (op == Integer.MAX_VALUE) { + return -1; + } + res += op; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minOperations(vector& nums) { + unordered_map count; + for (int num : nums) { + count[num]++; + } + + int maxf = 0; + for (auto& [num, freq] : count) { + maxf = max(maxf, freq); + } + + vector minOps(maxf + 1, 0); + minOps[1] = INT_MAX; + + for (int i = 2; i <= maxf; i++) { + minOps[i] = minOps[i - 2]; + if (i - 3 >= 0) { + minOps[i] = min(minOps[i], minOps[i - 3]); + } + if (minOps[i] != INT_MAX) { + minOps[i] += 1; + } + } + + int res = 0; + for (auto& [num, cnt] : count) { + int op = minOps[cnt]; + if (op == INT_MAX) { + return -1; + } + res += op; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minOperations(nums) { + const count = new Map(); + for (const num of nums) { + count.set(num, (count.get(num) || 0) + 1); + } + + const maxf = Math.max(...count.values()); + const minOps = Array(maxf + 1).fill(0); + minOps[1] = Infinity; + + for (let i = 2; i <= maxf; i++) { + minOps[i] = minOps[i - 2]; + if (i - 3 >= 0) { + minOps[i] = Math.min(minOps[i], minOps[i - 3]); + } + if (minOps[i] !== Infinity) { + minOps[i] += 1; + } + } + + let res = 0; + for (const cnt of count.values()) { + const op = minOps[cnt]; + if (op === Infinity) { + return -1; + } + res += op; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the size of the array $nums$ and $m$ is the average frequency of the array elements. + +--- + +## 4. Greedy + +::tabs-start + +```python +class Solution: + def minOperations(self, nums: List[int]) -> int: + count = Counter(nums) + res = 0 + + for num, cnt in count.items(): + if cnt == 1: + return -1 + res += math.ceil(cnt / 3) + + return res +``` + +```java +public class Solution { + public int minOperations(int[] nums) { + Map count = new HashMap<>(); + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + int res = 0; + for (int cnt : count.values()) { + if (cnt == 1) { + return -1; + } + res += (cnt + 2) / 3; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minOperations(vector& nums) { + unordered_map count; + for (int num : nums) { + count[num]++; + } + + int res = 0; + for (auto& [num, cnt] : count) { + if (cnt == 1) { + return -1; + } + res += (cnt + 2) / 3; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + minOperations(nums) { + const count = new Map(); + for (const num of nums) { + count.set(num, (count.get(num) || 0) + 1); + } + + let res = 0; + for (const cnt of count.values()) { + if (cnt === 1) { + return -1; + } + res += Math.ceil(cnt / 3); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/minimum-number-of-operations-to-move-all-balls-to-each-box.md b/articles/minimum-number-of-operations-to-move-all-balls-to-each-box.md new file mode 100644 index 000000000..c00bbfdb0 --- /dev/null +++ b/articles/minimum-number-of-operations-to-move-all-balls-to-each-box.md @@ -0,0 +1,325 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def minOperations(self, boxes: str) -> List[int]: + n = len(boxes) + res = [0] * n + + for pos in range(n): + for i in range(n): + if boxes[i] == '1': + res[pos] += abs(pos - i) + + return res +``` + +```java +public class Solution { + public int[] minOperations(String boxes) { + int n = boxes.length(); + int[] res = new int[n]; + + for (int pos = 0; pos < n; pos++) { + for (int i = 0; i < n; i++) { + if (boxes.charAt(i) == '1') { + res[pos] += Math.abs(pos - i); + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector minOperations(string boxes) { + int n = boxes.size(); + vector res(n, 0); + + for (int pos = 0; pos < n; pos++) { + for (int i = 0; i < n; i++) { + if (boxes[i] == '1') { + res[pos] += abs(pos - i); + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} boxes + * @return {number[]} + */ + minOperations(boxes) { + const n = boxes.length; + const res = new Array(n).fill(0); + + for (let pos = 0; pos < n; pos++) { + for (let i = 0; i < n; i++) { + if (boxes[i] === '1') { + res[pos] += Math.abs(pos - i); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output list. + +--- + +## 2. Prefix Sum + +::tabs-start + +```python +class Solution: + def minOperations(self, boxes: str) -> List[int]: + n = len(boxes) + res = [0] * n + + prefix_count = [0] * (n + 1) + index_sum = [0] * (n + 1) + for i in range(n): + prefix_count[i + 1] = prefix_count[i] + (boxes[i] == '1') + index_sum[i + 1] = index_sum[i] + (i if boxes[i] == '1' else 0) + + for i in range(n): + left = prefix_count[i] + left_sum = index_sum[i] + + right = prefix_count[n] - prefix_count[i + 1] + right_sum = index_sum[n] - index_sum[i + 1] + + res[i] = (i * left - left_sum) + (right_sum - i * right) + + return res +``` + +```java +public class Solution { + public int[] minOperations(String boxes) { + int n = boxes.length(); + int[] res = new int[n]; + int[] prefixCount = new int[n + 1]; + int[] indexSum = new int[n + 1]; + + for (int i = 0; i < n; i++) { + prefixCount[i + 1] = prefixCount[i] + (boxes.charAt(i) == '1' ? 1 : 0); + indexSum[i + 1] = indexSum[i] + (boxes.charAt(i) == '1' ? i : 0); + } + + for (int i = 0; i < n; i++) { + int left = prefixCount[i]; + int leftSum = indexSum[i]; + + int right = prefixCount[n] - prefixCount[i + 1]; + int rightSum = indexSum[n] - indexSum[i + 1]; + + res[i] = i * left - leftSum + (rightSum - i * right); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector minOperations(string boxes) { + int n = boxes.size(); + vector res(n), prefixCount(n + 1, 0), indexSum(n + 1, 0); + + for (int i = 0; i < n; i++) { + prefixCount[i + 1] = prefixCount[i] + (boxes[i] == '1' ? 1 : 0); + indexSum[i + 1] = indexSum[i] + (boxes[i] == '1' ? i : 0); + } + + for (int i = 0; i < n; i++) { + int left = prefixCount[i]; + int leftSum = indexSum[i]; + int right = prefixCount[n] - prefixCount[i + 1]; + int rightSum = indexSum[n] - indexSum[i + 1]; + res[i] = i * left - leftSum + (rightSum - i * right); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} boxes + * @return {number[]} + */ + minOperations(boxes) { + const n = boxes.length; + const res = new Array(n).fill(0); + const prefixCount = new Array(n + 1).fill(0); + const indexSum = new Array(n + 1).fill(0); + + for (let i = 0; i < n; i++) { + prefixCount[i + 1] = prefixCount[i] + (boxes[i] === '1' ? 1 : 0); + indexSum[i + 1] = indexSum[i] + (boxes[i] === '1' ? i : 0); + } + + for (let i = 0; i < n; i++) { + const left = prefixCount[i]; + const leftSum = indexSum[i]; + const right = prefixCount[n] - prefixCount[i + 1]; + const rightSum = indexSum[n] - indexSum[i + 1]; + + res[i] = i * left - leftSum + (rightSum - i * right); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Prefix Sum (Optimal) + +::tabs-start + +```python +class Solution: + def minOperations(self, boxes: str) -> List[int]: + n = len(boxes) + res = [0] * n + + balls = moves = 0 + for i in range(n): + res[i] = balls + moves + moves += balls + balls += int(boxes[i]) + + balls = moves = 0 + for i in range(n - 1, -1, -1): + res[i] += balls + moves + moves += balls + balls += int(boxes[i]) + + return res +``` + +```java +public class Solution { + public int[] minOperations(String boxes) { + int n = boxes.length(); + int[] res = new int[n]; + + int balls = 0, moves = 0; + for (int i = 0; i < n; i++) { + res[i] = balls + moves; + moves += balls; + balls += boxes.charAt(i) - '0'; + } + + balls = moves = 0; + for (int i = n - 1; i >= 0; i--) { + res[i] += balls + moves; + moves += balls; + balls += boxes.charAt(i) - '0'; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector minOperations(string boxes) { + int n = boxes.size(); + vector res(n, 0); + + int balls = 0, moves = 0; + for (int i = 0; i < n; i++) { + res[i] = balls + moves; + moves += balls; + balls += boxes[i] - '0'; + } + + balls = moves = 0; + for (int i = n - 1; i >= 0; i--) { + res[i] += balls + moves; + moves += balls; + balls += boxes[i] - '0'; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} boxes + * @return {number[]} + */ + minOperations(boxes) { + const n = boxes.length; + const res = new Array(n).fill(0); + + let balls = 0, moves = 0; + for (let i = 0; i < n; i++) { + res[i] = balls + moves; + moves += balls; + balls += Number(boxes[i]); + } + + balls = moves = 0; + for (let i = n - 1; i >= 0; i--) { + res[i] += balls + moves; + moves += balls; + balls += Number(boxes[i]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output list. \ No newline at end of file diff --git a/articles/minimum-number-of-swaps-to-make-the-string-balanced.md b/articles/minimum-number-of-swaps-to-make-the-string-balanced.md new file mode 100644 index 000000000..8c18a3ae2 --- /dev/null +++ b/articles/minimum-number-of-swaps-to-make-the-string-balanced.md @@ -0,0 +1,219 @@ +## 1. Stack + +::tabs-start + +```python +class Solution: + def minSwaps(self, s: str) -> int: + stack = [] + for c in s: + if c == '[': + stack.append(c) + elif stack: + stack.pop() + return (len(stack) + 1) // 2 +``` + +```java +public class Solution { + public int minSwaps(String s) { + Stack stack = new Stack<>(); + for (char c : s.toCharArray()) { + if (c == '[') { + stack.push(c); + } else if (!stack.isEmpty()) { + stack.pop(); + } + } + return (stack.size() + 1) / 2; + } +} +``` + +```cpp +class Solution { +public: + int minSwaps(string s) { + vector stack; + for (char c : s) { + if (c == '[') { + stack.push_back(c); + } else if (!stack.empty()) { + stack.pop_back(); + } + } + return (stack.size() + 1) / 2; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minSwaps(s) { + let stack = []; + for (const c of s) { + if (c === '[') { + stack.push(c); + } else if (stack.length > 0) { + stack.pop(); + } + } + return Math.floor((stack.length + 1) / 2); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Greedy - I + +::tabs-start + +```python +class Solution: + def minSwaps(self, s: str) -> int: + close = maxClose = 0 + + for c in s: + if c == '[': + close -= 1 + else: + close += 1 + maxClose = max(maxClose, close) + + return (maxClose + 1) // 2 +``` + +```java +public class Solution { + public int minSwaps(String s) { + int close = 0, maxClose = 0; + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == '[') close--; + else close++; + maxClose = Math.max(maxClose, close); + } + return (maxClose + 1) / 2; + } +} +``` + +```cpp +class Solution { +public: + int minSwaps(string s) { + int close = 0, maxClose = 0; + for (auto& c : s) { + if (c == '[') close--; + else close++; + maxClose = max(maxClose, close); + } + return (maxClose + 1) / 2; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minSwaps(s) { + let close = 0, maxClose = 0; + for (let i = 0; i < s.length; i++) { + if (s.charAt(i) == '[') close--; + else close++; + maxClose = Math.max(maxClose, close); + } + return Math.floor((maxClose + 1) / 2); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Greedy - II + +::tabs-start + +```python +class Solution: + def minSwaps(self, s: str) -> int: + stackSize = 0 + for c in s: + if c == '[': + stackSize += 1 + elif stackSize > 0: + stackSize -= 1 + return (stackSize + 1) // 2 +``` + +```java +public class Solution { + public int minSwaps(String s) { + int stackSize = 0; + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == '[') stackSize++; + else if (stackSize > 0) stackSize--; + } + return (stackSize + 1) / 2; + } +} +``` + +```cpp +class Solution { +public: + int minSwaps(string s) { + int stackSize = 0; + for (auto& c : s) { + if (c == '[') stackSize++; + else if (stackSize > 0) stackSize--; + } + return (stackSize + 1) / 2; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + minSwaps(s) { + let stackSize = 0; + for (let i = 0; i < s.length; i++) { + if (s.charAt(i) == '[') stackSize++; + else if (stackSize > 0) stackSize--; + } + return Math.floor((stackSize + 1) / 2); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/minimum-number-of-vertices-to-reach-all-nodes.md b/articles/minimum-number-of-vertices-to-reach-all-nodes.md new file mode 100644 index 000000000..aa09f1c80 --- /dev/null +++ b/articles/minimum-number-of-vertices-to-reach-all-nodes.md @@ -0,0 +1,465 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def findSmallestSetOfVertices(self, n: int, edges: List[List[int]]) -> List[int]: + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + + res = set(range(n)) + visited = [False] * n + + def dfs(node): + visited[node] = True + for nei in adj[node]: + if not visited[nei]: + dfs(nei) + res.discard(nei) + + for i in range(n): + if not visited[i]: + dfs(i) + return list(res) +``` + +```java +public class Solution { + public List findSmallestSetOfVertices(int n, List> edges) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + for (List edge : edges) { + adj[edge.get(0)].add(edge.get(1)); + } + + Set res = new HashSet<>(); + for (int i = 0; i < n; i++) { + res.add(i); + } + + boolean[] visited = new boolean[n]; + for (int i = 0; i < n; i++) { + dfs(i, adj, visited, res); + } + return new ArrayList<>(res); + } + + private void dfs(int node, List[] adj, boolean[] visited, Set res) { + visited[node] = true; + for (int nei : adj[node]) { + if (!visited[nei]) dfs(nei, adj, visited, res); + res.remove(nei); + } + } +} +``` + +```cpp +class Solution { +public: + vector findSmallestSetOfVertices(int n, vector>& edges) { + vector> adj(n); + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + } + + unordered_set res; + vector visited(n, false); + for (int i = 0; i < n; i++) res.insert(i); + + function dfs = [&](int node) { + visited[node] = true; + for (int& nei : adj[node]) { + if (!visited[nei]) dfs(nei); + res.erase(nei); + } + }; + + for (int i = 0; i < n; i++) { + if (!visited[i]) dfs(i); + } + return vector(res.begin(), res.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[]} + */ + findSmallestSetOfVertices(n, edges) { + const adj = Array.from({ length: n }, () => []); + for (const [u, v] of edges) { + adj[u].push(v); + } + + const res = new Set(Array.from({ length: n }, (_, i) => i)); + const visited = new Array(n).fill(false); + + const dfs = (node) => { + visited[node] = true; + for (const nei of adj[node]) { + if (!visited[nei]) dfs(nei); + res.delete(nei); + } + }; + + for (let i = 0; i < n; i++) { + if (!visited[i]) dfs(i); + } + + return Array.from(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 2. Iterative DFS + +::tabs-start + +```python +class Solution: + def findSmallestSetOfVertices(self, n: int, edges: List[List[int]]) -> List[int]: + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + + res = [True] * n + visited = [False] * n + stack = [] + + for i in range(n): + if not visited[i]: + stack.append(i) + while stack: + node = stack.pop() + if visited[node]: + continue + visited[node] = True + for nei in adj[node]: + if not visited[nei]: + stack.append(nei) + res[nei] = False + + return [i for i in range(n) if res[i]] +``` + +```java +public class Solution { + public List findSmallestSetOfVertices(int n, List> edges) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (List edge : edges) { + adj[edge.get(0)].add(edge.get(1)); + } + + boolean[] res = new boolean[n]; + Arrays.fill(res, true); + boolean[] visited = new boolean[n]; + Stack stack = new Stack<>(); + + for (int i = 0; i < n; i++) { + if (!visited[i]) { + stack.push(i); + while (!stack.isEmpty()) { + int node = stack.pop(); + if (visited[node]) continue; + visited[node] = true; + for (int nei : adj[node]) { + if (!visited[nei]) stack.push(nei); + res[nei] = false; + } + } + } + } + + List result = new ArrayList<>(); + for (int i = 0; i < n; i++) { + if (res[i]) result.add(i); + } + return result; + } +} +``` + +```cpp +class Solution { +public: + vector findSmallestSetOfVertices(int n, vector>& edges) { + vector> adj(n); + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + } + + vector res(n, true), visited(n, false); + stack stack; + + for (int i = 0; i < n; i++) { + if (!visited[i]) { + stack.push(i); + while (!stack.empty()) { + int node = stack.top(); + stack.pop(); + if (visited[node]) continue; + visited[node] = true; + for (int nei : adj[node]) { + if (!visited[nei]) stack.push(nei); + res[nei] = false; + } + } + } + } + + vector result; + for (int i = 0; i < n; i++) { + if (res[i]) result.push_back(i); + } + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[]} + */ + findSmallestSetOfVertices(n, edges) { + const adj = Array.from({ length: n }, () => []); + for (const [u, v] of edges) { + adj[u].push(v); + } + + const res = Array(n).fill(true); + const visited = Array(n).fill(false); + const stack = []; + + for (let i = 0; i < n; i++) { + if (!visited[i]) { + stack.push(i); + while (stack.length) { + const node = stack.pop(); + if (visited[node]) continue; + visited[node] = true; + for (const nei of adj[node]) { + if (!visited[nei]) stack.push(nei); + res[nei] = false; + } + } + } + } + + return res.map((val, i) => val ? i : -1).filter(i => i !== -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 3. Indegree Count + +::tabs-start + +```python +class Solution: + def findSmallestSetOfVertices(self, n: int, edges: List[List[int]]) -> List[int]: + incoming = collections.defaultdict(list) + for src, dst in edges: + incoming[dst].append(src) + + res = [] + for i in range(n): + if not incoming[i]: + res.append(i) + return res +``` + +```java +public class Solution { + public List findSmallestSetOfVertices(int n, List> edges) { + List[] incoming = new ArrayList[n]; + for (int i = 0; i < n; i++) { + incoming[i] = new ArrayList<>(); + } + for (List edge : edges) { + incoming[edge.get(1)].add(edge.get(0)); + } + + List res = new ArrayList<>(); + for (int i = 0; i < n; i++) { + if (incoming[i].isEmpty()) { + res.add(i); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findSmallestSetOfVertices(int n, vector>& edges) { + vector> incoming(n); + for (auto& edge : edges) { + incoming[edge[1]].push_back(edge[0]); + } + + vector res; + for (int i = 0; i < n; i++) { + if (incoming[i].empty()) { + res.push_back(i); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[]} + */ + findSmallestSetOfVertices(n, edges) { + const incoming = Array.from({ length: n }, () => []); + + for (const [src, dst] of edges) { + incoming[dst].push(src); + } + + const res = []; + for (let i = 0; i < n; i++) { + if (incoming[i].length === 0) { + res.push(i); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 4. Indegree Count + +::tabs-start + +```python +class Solution: + def findSmallestSetOfVertices(self, n: int, edges: List[List[int]]) -> List[int]: + indegree = [False] * n + for src, dst in edges: + indegree[dst] = True + return [i for i in range(n) if not indegree[i]] +``` + +```java +public class Solution { + public List findSmallestSetOfVertices(int n, List> edges) { + boolean[] indegree = new boolean[n]; + for (List edge : edges) { + indegree[edge.get(1)] = true; + } + + List res = new ArrayList<>(); + for (int i = 0; i < n; i++) { + if (!indegree[i]) { + res.add(i); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findSmallestSetOfVertices(int n, vector>& edges) { + vector indegree(n, false); + for (const auto& edge : edges) { + indegree[edge[1]] = true; + } + + vector res; + for (int i = 0; i < n; i++) { + if (!indegree[i]) { + res.push_back(i); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[]} + */ + findSmallestSetOfVertices(n, edges) { + const indegree = new Array(n).fill(false); + for (const [src, dst] of edges) { + indegree[dst] = true; + } + + let res = []; + for (let i = 0; i < n; i++) { + if (!indegree[i]) res.push(i); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. \ No newline at end of file diff --git a/articles/minimum-one-bit-operations-to-make-integers-zero.md b/articles/minimum-one-bit-operations-to-make-integers-zero.md new file mode 100644 index 000000000..7e4071ce6 --- /dev/null +++ b/articles/minimum-one-bit-operations-to-make-integers-zero.md @@ -0,0 +1,313 @@ +## 1. Math (Recursion) + +::tabs-start + +```python +class Solution: + def minimumOneBitOperations(self, n: int) -> int: + if n == 0: + return 0 + + k = 1 + while (k << 1) <= n: + k <<= 1 + + return (k << 1) - 1 - self.minimumOneBitOperations(k ^ n) +``` + +```java +public class Solution { + public int minimumOneBitOperations(int n) { + if (n == 0) { + return 0; + } + + int k = 1; + while ((k << 1) <= n) { + k <<= 1; + } + + return (k << 1) - 1 - minimumOneBitOperations(k ^ n); + } +} +``` + +```cpp +class Solution { +public: + int minimumOneBitOperations(int n) { + if (n == 0) { + return 0; + } + + int k = 1; + while ((k << 1) <= n) { + k <<= 1; + } + + return (k << 1) - 1 - minimumOneBitOperations(k ^ n); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + minimumOneBitOperations(n) { + if (n === 0) { + return 0; + } + + let k = 1; + while ((k << 1) <= n) { + k <<= 1; + } + + return (k << 1) - 1 - this.minimumOneBitOperations(k ^ n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ for recursion stack. + +--- + +## 2. Math (Iteration) - I + +::tabs-start + +```python +class Solution: + def minimumOneBitOperations(self, n: int) -> int: + res = 0 + k = 1 << 30 + sign = 1 + + while n: + while k > n: + k >>= 1 + + res += (sign * ((k << 1) - 1)) + sign *= -1 + n ^= k + + return res +``` + +```java +public class Solution { + public int minimumOneBitOperations(int n) { + int res = 0, k = 1 << 30, sign = 1; + + while (n != 0) { + while (k > n) { + k >>= 1; + } + + res += (sign * ((k << 1) - 1)); + sign *= -1; + n ^= k; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minimumOneBitOperations(int n) { + int res = 0, k = 1 << 30, sign = 1; + + while (n != 0) { + while (k > n) { + k >>= 1; + } + + res += sign * ((k << 1) - 1); + sign *= -1; + n ^= k; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + minimumOneBitOperations(n) { + let res = 0, k = 1 << 30, sign = 1; + + while (n !== 0) { + while (k > n) { + k >>= 1; + } + + res += sign * ((k << 1) - 1); + sign *= -1; + n ^= k; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Math (Iteration) - II + +::tabs-start + +```python +class Solution: + def minimumOneBitOperations(self, n: int) -> int: + res, sign = 0, 1 + while n: + res += sign * (n ^ (n - 1)) + n &= (n - 1) + sign *= -1 + return abs(res) +``` + +```java +public class Solution { + public int minimumOneBitOperations(int n) { + int res = 0, sign = 1; + while (n != 0) { + res += sign * (n ^ (n - 1)); + n &= (n - 1); + sign *= -1; + } + return Math.abs(res); + } +} +``` + +```cpp +class Solution { +public: + int minimumOneBitOperations(int n) { + int res = 0, sign = 1; + while (n != 0) { + res += sign * (n ^ (n - 1)); + n &= (n - 1); + sign *= -1; + } + return abs(res); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + minimumOneBitOperations(n) { + let res = 0, sign = 1; + while (n !== 0) { + res += sign * (n ^ (n - 1)); + n &= (n - 1); + sign *= -1; + } + return Math.abs(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Math (Grey Code) + +::tabs-start + +```python +class Solution: + def minimumOneBitOperations(self, n: int) -> int: + res = n + while n: + n >>= 1 + res ^= n + return res +``` + +```java +public class Solution { + public int minimumOneBitOperations(int n) { + int res = n; + while (n != 0) { + n >>= 1; + res ^= n; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minimumOneBitOperations(int n) { + int res = n; + while (n != 0) { + n >>= 1; + res ^= n; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + minimumOneBitOperations(n) { + let res = n; + while (n !== 0) { + n >>= 1; + res ^= n; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/minimum-operations-to-reduce-x-to-zero.md b/articles/minimum-operations-to-reduce-x-to-zero.md new file mode 100644 index 000000000..e3b3ca2eb --- /dev/null +++ b/articles/minimum-operations-to-reduce-x-to-zero.md @@ -0,0 +1,577 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def minOperations(self, nums: List[int], x: int) -> int: + n = len(nums) + res = n + 1 + suffixSum = prefixSum = 0 + + for i in range(n - 1, -1, -1): + suffixSum += nums[i] + if suffixSum == x: + res = min(res, n - i) + + for i in range(n): + prefixSum += nums[i] + suffixSum = 0 + if prefixSum == x: + res = min(res, i + 1) + + for j in range(n - 1, i, -1): + suffixSum += nums[j] + if prefixSum + suffixSum == x: + res = min(res, i + 1 + n - j) + + return -1 if res == n + 1 else res +``` + +```java +public class Solution { + public int minOperations(int[] nums, int x) { + int n = nums.length; + int res = n + 1; + int suffixSum = 0, prefixSum = 0; + + for (int i = n - 1; i >= 0; i--) { + suffixSum += nums[i]; + if (suffixSum == x) { + res = Math.min(res, n - i); + } + } + + for (int i = 0; i < n; i++) { + prefixSum += nums[i]; + suffixSum = 0; + if (prefixSum == x) { + res = Math.min(res, i + 1); + } + + for (int j = n - 1; j > i; j--) { + suffixSum += nums[j]; + if (prefixSum + suffixSum == x) { + res = Math.min(res, i + 1 + n - j); + } + } + } + + return res == n + 1 ? -1 : res; + } +} +``` + +```cpp +class Solution { +public: + int minOperations(vector& nums, int x) { + int n = nums.size(); + int res = n + 1, suffixSum = 0, prefixSum = 0; + + for (int i = n - 1; i >= 0; i--) { + suffixSum += nums[i]; + if (suffixSum == x) { + res = min(res, n - i); + } + } + + for (int i = 0; i < n; i++) { + prefixSum += nums[i]; + suffixSum = 0; + if (prefixSum == x) { + res = min(res, i + 1); + } + + for (int j = n - 1; j > i; j--) { + suffixSum += nums[j]; + if (prefixSum + suffixSum == x) { + res = min(res, i + 1 + n - j); + } + } + } + + return res == n + 1 ? -1 : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} x + * @return {number} + */ + minOperations(nums, x) { + const n = nums.length; + let res = n + 1, suffixSum = 0, prefixSum = 0; + + for (let i = n - 1; i >= 0; i--) { + suffixSum += nums[i]; + if (suffixSum === x) { + res = Math.min(res, n - i); + } + } + + for (let i = 0; i < n; i++) { + prefixSum += nums[i]; + suffixSum = 0; + if (prefixSum === x) { + res = Math.min(res, i + 1); + } + + for (let j = n - 1; j > i; j--) { + suffixSum += nums[j]; + if (prefixSum + suffixSum === x) { + res = Math.min(res, i + 1 + n - j); + } + } + } + + return res === n + 1 ? -1 : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Prefix Sum + Binary Search + +::tabs-start + +```python +class Solution: + def minOperations(self, nums: List[int], x: int) -> int: + n = len(nums) + prefixSum = [0] * (n + 1) + for i in range(n): + prefixSum[i + 1] = prefixSum[i] + nums[i] + + if x > prefixSum[n]: + return -1 + + def binarySearch(target, m): + l, r = 1, m + index = n + 1 + + while l <= r: + mid = (l + r) >> 1 + if prefixSum[mid] >= target: + if prefixSum[mid] == target: + index = mid + r = mid - 1 + else: + l = mid + 1 + + return index + + res = binarySearch(x, n) + suffixSum = 0 + for i in range(n - 1, 0, -1): + suffixSum += nums[i] + if suffixSum == x: + res = min(res, n - i) + break + if suffixSum > x: break + res = min(res, binarySearch(x - suffixSum, i) + n - i) + + return -1 if res == n + 1 else res +``` + +```java +public class Solution { + public int minOperations(int[] nums, int x) { + int n = nums.length; + int[] prefixSum = new int[n + 1]; + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + if (x > prefixSum[n]) { + return -1; + } + + int res = binarySearch(prefixSum, x, n); + int suffixSum = 0; + for (int i = n - 1; i > 0; i--) { + suffixSum += nums[i]; + if (suffixSum == x) { + res = Math.min(res, n - i); + break; + } + if (suffixSum > x) break; + res = Math.min(res, binarySearch(prefixSum, x - suffixSum, i) + n - i); + } + + return res == n + 1 ? -1 : res; + } + + private int binarySearch(int[] prefixSum, int target, int m) { + int l = 1, r = m; + int index = prefixSum.length; + + while (l <= r) { + int mid = (l + r) / 2; + if (prefixSum[mid] >= target) { + if (prefixSum[mid] == target) { + index = mid; + } + r = mid - 1; + } else { + l = mid + 1; + } + } + return index; + } +} +``` + +```cpp +class Solution { +public: + int minOperations(vector& nums, int x) { + int n = nums.size(); + vector prefixSum(n + 1, 0); + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + if (x > prefixSum[n]) { + return -1; + } + + auto binarySearch = [&](int target, int m) { + int l = 1, r = m, index = n + 1; + while (l <= r) { + int mid = (l + r) / 2; + if (prefixSum[mid] >= target) { + if (prefixSum[mid] == target) { + index = mid; + } + r = mid - 1; + } else { + l = mid + 1; + } + } + return index; + }; + + int res = binarySearch(x, n); + int suffixSum = 0; + for (int i = n - 1; i > 0; i--) { + suffixSum += nums[i]; + if (suffixSum == x) { + res = min(res, n - i); + break; + } + if (suffixSum > x) break; + res = min(res, binarySearch(x - suffixSum, i) + n - i); + } + + return res == n + 1 ? -1 : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} x + * @return {number} + */ + minOperations(nums, x) { + const n = nums.length; + const prefixSum = new Array(n + 1).fill(0); + for (let i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + if (x > prefixSum[n]) return -1; + + const binarySearch = (target, m) => { + let l = 1, r = m; + let index = n + 1; + while (l <= r) { + let mid = Math.floor((l + r) / 2); + if (prefixSum[mid] >= target) { + if (prefixSum[mid] === target) { + index = mid; + } + r = mid - 1; + } else { + l = mid + 1; + } + } + return index; + }; + + let res = binarySearch(x, n); + let suffixSum = 0; + for (let i = n - 1; i > 0; i--) { + suffixSum += nums[i]; + if (suffixSum === x) { + res = Math.min(res, n - i); + break; + } + if (suffixSum > x) break; + res = Math.min(res, binarySearch(x - suffixSum, i) + n - i); + } + + return res === n + 1 ? -1 : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Prefix Sum + Hash Map + +::tabs-start + +```python +class Solution: + def minOperations(self, nums: List[int], x: int) -> int: + total = sum(nums) + if total == x: + return len(nums) + + target = total - x + if target < 0: + return -1 + + res = -1 + prefixSum = 0 + prefixMap = {0: -1} # prefixSum -> index + + for i, num in enumerate(nums): + prefixSum += num + if prefixSum - target in prefixMap: + res = max(res, i - prefixMap[prefixSum - target]) + prefixMap[prefixSum] = i + + return len(nums) - res if res != -1 else -1 +``` + +```java +public class Solution { + public int minOperations(int[] nums, int x) { + int total = 0; + for (int num : nums) total += num; + if (total == x) return nums.length; + + int target = total - x; + if (target < 0) return -1; + + Map prefixMap = new HashMap<>(); + prefixMap.put(0, -1); + int prefixSum = 0, res = -1; + + for (int i = 0; i < nums.length; i++) { + prefixSum += nums[i]; + if (prefixMap.containsKey(prefixSum - target)) { + res = Math.max(res, i - prefixMap.get(prefixSum - target)); + } + prefixMap.put(prefixSum, i); + } + + return res == -1 ? -1 : nums.length - res; + } +} +``` + +```cpp +class Solution { +public: + int minOperations(vector& nums, int x) { + int total = 0; + for (int& num : nums) total += num; + if (total == x) return nums.size(); + + int target = total - x; + if (target < 0) return -1; + + unordered_map prefixMap; + prefixMap[0] = -1; + int prefixSum = 0, res = -1; + + for (int i = 0; i < nums.size(); i++) { + prefixSum += nums[i]; + if (prefixMap.count(prefixSum - target)) { + res = max(res, i - prefixMap[prefixSum - target]); + } + prefixMap[prefixSum] = i; + } + + return res == -1 ? -1 : nums.size() - res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} x + * @return {number} + */ + minOperations(nums, x) { + const total = nums.reduce((acc, num) => acc + num, 0); + if (total === x) return nums.length; + + const target = total - x; + if (target < 0) return -1; + + const prefixMap = new Map(); + prefixMap.set(0, -1); + let prefixSum = 0, res = -1; + + for (let i = 0; i < nums.length; i++) { + prefixSum += nums[i]; + if (prefixMap.has(prefixSum - target)) { + res = Math.max(res, i - prefixMap.get(prefixSum - target)); + } + prefixMap.set(prefixSum, i); + } + + return res === -1 ? -1 : nums.length - res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Sliding Window + +::tabs-start + +```python +class Solution: + def minOperations(self, nums: List[int], x: int) -> int: + target = sum(nums) - x + cur_sum = 0 + max_window = -1 + l = 0 + + for r in range(len(nums)): + cur_sum += nums[r] + + while l <= r and cur_sum > target: + cur_sum -= nums[l] + l += 1 + + if cur_sum == target: + max_window = max(max_window, r - l + 1) + + return -1 if max_window == -1 else len(nums) - max_window +``` + +```java +public class Solution { + public int minOperations(int[] nums, int x) { + int target = 0; + for (int num : nums) target += num; + target -= x; + + int curSum = 0, maxWindow = -1, l = 0; + + for (int r = 0; r < nums.length; r++) { + curSum += nums[r]; + + while (l <= r && curSum > target) { + curSum -= nums[l]; + l++; + } + + if (curSum == target) { + maxWindow = Math.max(maxWindow, r - l + 1); + } + } + + return maxWindow == -1 ? -1 : nums.length - maxWindow; + } +} +``` + +```cpp +class Solution { +public: + int minOperations(vector& nums, int x) { + int target = accumulate(nums.begin(), nums.end(), 0) - x; + int curSum = 0, maxWindow = -1, l = 0; + + for (int r = 0; r < nums.size(); r++) { + curSum += nums[r]; + + while (l <= r && curSum > target) { + curSum -= nums[l]; + l++; + } + + if (curSum == target) { + maxWindow = max(maxWindow, r - l + 1); + } + } + + return maxWindow == -1 ? -1 : nums.size() - maxWindow; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} x + * @return {number} + */ + minOperations(nums, x) { + const target = nums.reduce((acc, num) => acc + num, 0) - x; + let curSum = 0, maxWindow = -1, l = 0; + + for (let r = 0; r < nums.length; r++) { + curSum += nums[r]; + + while (l <= r && curSum > target) { + curSum -= nums[l]; + l++; + } + + if (curSum === target) { + maxWindow = Math.max(maxWindow, r - l + 1); + } + } + + return maxWindow === -1 ? -1 : nums.length - maxWindow; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/minimum-path-sum.md b/articles/minimum-path-sum.md new file mode 100644 index 000000000..37968cf67 --- /dev/null +++ b/articles/minimum-path-sum.md @@ -0,0 +1,395 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def minPathSum(self, grid: List[List[int]]) -> int: + def dfs(r, c): + if r == len(grid) - 1 and c == len(grid[0]) - 1: + return grid[r][c] + if r == len(grid) or c == len(grid[0]): + return float('inf') + return grid[r][c] + min(dfs(r + 1, c), dfs(r, c + 1)) + + return dfs(0, 0) +``` + +```java +public class Solution { + public int minPathSum(int[][] grid) { + return dfs(0, 0, grid); + } + + public int dfs(int r, int c, int[][] grid) { + if (r == grid.length - 1 && c == grid[0].length - 1) { + return grid[r][c]; + } + if (r == grid.length || c == grid[0].length) { + return Integer.MAX_VALUE; + } + return grid[r][c] + Math.min(dfs(r + 1, c, grid), dfs(r, c + 1, grid)); + } +} +``` + +```cpp +class Solution { +public: + int minPathSum(vector>& grid) { + return dfs(0, 0, grid); + } + + int dfs(int r, int c, vector>& grid) { + if (r == grid.size() - 1 && c == grid[0].size() - 1) { + return grid[r][c]; + } + if (r == grid.size() || c == grid[0].size()) { + return INT_MAX; + } + return grid[r][c] + min(dfs(r + 1, c, grid), dfs(r, c + 1, grid)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minPathSum(grid) { + const dfs = (r, c) => { + if (r === grid.length - 1 && c === grid[0].length - 1) { + return grid[r][c]; + } + if (r === grid.length || c === grid[0].length) { + return Infinity; + } + return grid[r][c] + Math.min(dfs(r + 1, c), dfs(r, c + 1)); + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ {m + n})$ +* Space complexity: $O(m + n)$ for recursion stack. + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minPathSum(self, grid: List[List[int]]) -> int: + m, n = len(grid), len(grid[0]) + dp = [[-1] * n for _ in range(m)] + + def dfs(r, c): + if r == m - 1 and c == n - 1: + return grid[r][c] + if r == m or c == n: + return float('inf') + if dp[r][c] != -1: + return dp[r][c] + + dp[r][c] = grid[r][c] + min(dfs(r + 1, c), dfs(r, c + 1)) + return dp[r][c] + + return dfs(0, 0) +``` + +```java +public class Solution { + private int[][] dp; + + public int minPathSum(int[][] grid) { + int m = grid.length, n = grid[0].length; + dp = new int[m][n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + dp[i][j] = -1; + } + } + return dfs(0, 0, grid); + } + + public int dfs(int r, int c, int[][] grid) { + if (r == grid.length - 1 && c == grid[0].length - 1) { + return grid[r][c]; + } + if (r == grid.length || c == grid[0].length) { + return Integer.MAX_VALUE; + } + if (dp[r][c] != -1) { + return dp[r][c]; + } + + dp[r][c] = grid[r][c] + Math.min(dfs(r + 1, c, grid), dfs(r, c + 1, grid)); + return dp[r][c]; + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + +public: + int minPathSum(vector>& grid) { + int m = grid.size(), n = grid[0].size(); + dp = vector>(m, vector(n, -1)); + return dfs(0, 0, grid); + } + + int dfs(int r, int c, vector>& grid) { + if (r == grid.size() - 1 && c == grid[0].size() - 1) { + return grid[r][c]; + } + if (r == grid.size() || c == grid[0].size()) { + return INT_MAX; + } + if (dp[r][c] != -1) { + return dp[r][c]; + } + + dp[r][c] = grid[r][c] + min(dfs(r + 1, c, grid), dfs(r, c + 1, grid)); + return dp[r][c]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minPathSum(grid) { + const m = grid.length, n = grid[0].length; + const dp = Array.from({ length: m }, () => Array(n).fill(-1)); + + const dfs = (r, c) => { + if (r === m - 1 && c === n - 1) { + return grid[r][c]; + } + if (r === m || c === n) { + return Infinity; + } + if (dp[r][c] !== -1) { + return dp[r][c]; + } + + dp[r][c] = grid[r][c] + Math.min(dfs(r + 1, c), dfs(r, c + 1)); + return dp[r][c]; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minPathSum(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + dp = [[float("inf")] * (COLS + 1) for _ in range(ROWS + 1)] + dp[ROWS - 1][COLS] = 0 + + for r in range(ROWS - 1, -1, -1): + for c in range(COLS - 1, -1, -1): + dp[r][c] = grid[r][c] + min(dp[r + 1][c], dp[r][c + 1]) + + return dp[0][0] +``` + +```java +public class Solution { + public int minPathSum(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int[][] dp = new int[ROWS + 1][COLS + 1]; + + for (int r = 0; r <= ROWS; r++) { + for (int c = 0; c <= COLS; c++) { + dp[r][c] = Integer.MAX_VALUE; + } + } + dp[ROWS - 1][COLS] = 0; + + for (int r = ROWS - 1; r >= 0; r--) { + for (int c = COLS - 1; c >= 0; c--) { + dp[r][c] = grid[r][c] + Math.min(dp[r + 1][c], dp[r][c + 1]); + } + } + + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + int minPathSum(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + vector> dp(ROWS + 1, vector(COLS + 1, INT_MAX)); + dp[ROWS - 1][COLS] = 0; + + for (int r = ROWS - 1; r >= 0; r--) { + for (int c = COLS - 1; c >= 0; c--) { + dp[r][c] = grid[r][c] + min(dp[r + 1][c], dp[r][c + 1]); + } + } + + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minPathSum(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const dp = Array.from({ length: ROWS + 1 }, () => Array(COLS + 1).fill(Infinity)); + dp[ROWS - 1][COLS] = 0; + + for (let r = ROWS - 1; r >= 0; r--) { + for (let c = COLS - 1; c >= 0; c--) { + dp[r][c] = grid[r][c] + Math.min(dp[r + 1][c], dp[r][c + 1]); + } + } + + return dp[0][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def minPathSum(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + dp = [float("inf")] * (COLS + 1) + dp[COLS - 1] = 0 + + for r in range(ROWS - 1, -1, -1): + for c in range(COLS - 1, -1, -1): + dp[c] = grid[r][c] + min(dp[c], dp[c + 1]) + + return dp[0] +``` + +```java +public class Solution { + public int minPathSum(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int[] dp = new int[COLS + 1]; + for (int c = 0; c <= COLS; c++) { + dp[c] = Integer.MAX_VALUE; + } + dp[COLS - 1] = 0; + + for (int r = ROWS - 1; r >= 0; r--) { + for (int c = COLS - 1; c >= 0; c--) { + dp[c] = grid[r][c] + Math.min(dp[c], dp[c + 1]); + } + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int minPathSum(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + vector dp(COLS + 1, INT_MAX); + dp[COLS - 1] = 0; + + for (int r = ROWS - 1; r >= 0; r--) { + for (int c = COLS - 1; c >= 0; c--) { + dp[c] = grid[r][c] + min(dp[c], dp[c + 1]); + } + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + minPathSum(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const dp = new Array(COLS + 1).fill(Infinity); + dp[COLS - 1] = 0; + + for (let r = ROWS - 1; r >= 0; r--) { + for (let c = COLS - 1; c >= 0; c--) { + dp[c] = grid[r][c] + Math.min(dp[c], dp[c + 1]); + } + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. \ No newline at end of file diff --git a/articles/minimum-penalty-for-a-shop.md b/articles/minimum-penalty-for-a-shop.md new file mode 100644 index 000000000..a3e506a88 --- /dev/null +++ b/articles/minimum-penalty-for-a-shop.md @@ -0,0 +1,497 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def bestClosingTime(self, customers: str) -> int: + n = len(customers) + res = n + minPenalty = n + + for i in range(n + 1): + penalty = 0 + for j in range(i): + if customers[j] == 'N': + penalty += 1 + for j in range(i, n): + if customers[j] == 'Y': + penalty += 1 + + if penalty < minPenalty: + minPenalty = penalty + res = i + + return res +``` + +```java +public class Solution { + public int bestClosingTime(String customers) { + int n = customers.length(); + int res = n, minPenalty = n; + + for (int i = 0; i <= n; i++) { + int penalty = 0; + for (int j = 0; j < i; j++) { + if (customers.charAt(j) == 'N') { + penalty++; + } + } + for (int j = i; j < n; j++) { + if (customers.charAt(j) == 'Y') { + penalty++; + } + } + + if (penalty < minPenalty) { + minPenalty = penalty; + res = i; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int bestClosingTime(string customers) { + int n = customers.size(); + int res = n, minPenalty = n; + + for (int i = 0; i <= n; i++) { + int penalty = 0; + for (int j = 0; j < i; j++) { + if (customers[j] == 'N') { + penalty++; + } + } + for (int j = i; j < n; j++) { + if (customers[j] == 'Y') { + penalty++; + } + } + + if (penalty < minPenalty) { + minPenalty = penalty; + res = i; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} customers + * @return {number} + */ + bestClosingTime(customers) { + const n = customers.length; + let res = n, minPenalty = n; + + for (let i = 0; i <= n; i++) { + let penalty = 0; + for (let j = 0; j < i; j++) { + if (customers[j] === 'N') { + penalty++; + } + } + for (let j = i; j < n; j++) { + if (customers[j] === 'Y') { + penalty++; + } + } + + if (penalty < minPenalty) { + minPenalty = penalty; + res = i; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Prefix & Suffix + +::tabs-start + +```python +class Solution: + def bestClosingTime(self, customers: str) -> int: + n = len(customers) + cnt = 0 + + prefixN = [] + for c in customers: + prefixN.append(cnt) + if c == 'N': + cnt += 1 + prefixN.append(cnt) + + suffixY = [0] * (n + 1) + for i in range(n - 1, -1, -1): + suffixY[i] = suffixY[i + 1] + if customers[i] == 'Y': + suffixY[i] += 1 + + res = n + minPenalty = n + for i in range(n + 1): + penalty = prefixN[i] + suffixY[i] + if penalty < minPenalty: + minPenalty = penalty + res = i + + return res +``` + +```java +public class Solution { + public int bestClosingTime(String customers) { + int n = customers.length(); + int cnt = 0; + + int[] prefixN = new int[n + 1]; + for (int i = 0; i < n; i++) { + prefixN[i] = cnt; + if (customers.charAt(i) == 'N') { + cnt++; + } + } + prefixN[n] = cnt; + + int[] suffixY = new int[n + 1]; + for (int i = n - 1; i >= 0; i--) { + suffixY[i] = suffixY[i + 1]; + if (customers.charAt(i) == 'Y') { + suffixY[i]++; + } + } + + int res = n, minPenalty = n; + for (int i = 0; i <= n; i++) { + int penalty = prefixN[i] + suffixY[i]; + if (penalty < minPenalty) { + minPenalty = penalty; + res = i; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int bestClosingTime(string customers) { + int n = customers.size(), cnt = 0; + + vector prefixN(n + 1); + for (int i = 0; i < n; i++) { + prefixN[i] = cnt; + if (customers[i] == 'N') { + cnt++; + } + } + prefixN[n] = cnt; + + vector suffixY(n + 1, 0); + for (int i = n - 1; i >= 0; i--) { + suffixY[i] = suffixY[i + 1]; + if (customers[i] == 'Y') { + suffixY[i]++; + } + } + + int res = n, minPenalty = n; + for (int i = 0; i <= n; i++) { + int penalty = prefixN[i] + suffixY[i]; + if (penalty < minPenalty) { + minPenalty = penalty; + res = i; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} customers + * @return {number} + */ + bestClosingTime(customers) { + const n = customers.length; + let cnt = 0; + + const prefixN = []; + for (const c of customers) { + prefixN.push(cnt); + if (c === 'N') { + cnt++; + } + } + prefixN.push(cnt); + + const suffixY = new Array(n + 1).fill(0); + for (let i = n - 1; i >= 0; i--) { + suffixY[i] = suffixY[i + 1]; + if (customers[i] === 'Y') { + suffixY[i]++; + } + } + + let res = n, minPenalty = n; + for (let i = 0; i <= n; i++) { + const penalty = prefixN[i] + suffixY[i]; + if (penalty < minPenalty) { + minPenalty = penalty; + res = i; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Iteration (Two Pass) + +::tabs-start + +```python +class Solution: + def bestClosingTime(self, customers: str) -> int: + cntY = sum(c == "Y" for c in customers) + + minPenalty = cntY + res = cntN = 0 + for i, c in enumerate(customers): + if c == "Y": + cntY -= 1 + else: + cntN += 1 + + penalty = cntN + cntY + if penalty < minPenalty: + res = i + 1 + minPenalty = penalty + + return res +``` + +```java +public class Solution { + public int bestClosingTime(String customers) { + int cntY = 0; + for (char c : customers.toCharArray()) { + if (c == 'Y') cntY++; + } + + int minPenalty = cntY, res = 0, cntN = 0; + for (int i = 0; i < customers.length(); i++) { + if (customers.charAt(i) == 'Y') { + cntY--; + } else { + cntN++; + } + + int penalty = cntN + cntY; + if (penalty < minPenalty) { + res = i + 1; + minPenalty = penalty; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int bestClosingTime(string customers) { + int cntY = count(customers.begin(), customers.end(), 'Y'); + + int minPenalty = cntY, res = 0, cntN = 0; + for (int i = 0; i < customers.size(); i++) { + if (customers[i] == 'Y') { + cntY--; + } else { + cntN++; + } + + int penalty = cntN + cntY; + if (penalty < minPenalty) { + res = i + 1; + minPenalty = penalty; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} customers + * @return {number} + */ + bestClosingTime(customers) { + let cntY = 0; + for (let c of customers) { + if (c === 'Y') cntY++; + } + + let minPenalty = cntY, res = 0, cntN = 0; + for (let i = 0; i < customers.length; i++) { + if (customers[i] === 'Y') { + cntY--; + } else { + cntN++; + } + + const penalty = cntN + cntY; + if (penalty < minPenalty) { + res = i + 1; + minPenalty = penalty; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Iteration (One Pass) + +::tabs-start + +```python +class Solution: + def bestClosingTime(self, customers: str) -> int: + res = minPenalty = 0 + penalty = 0 + + for i, c in enumerate(customers): + penalty += 1 if c == 'Y' else -1 + + if penalty > minPenalty: + minPenalty = penalty + res = i + 1 + + return res +``` + +```java +public class Solution { + public int bestClosingTime(String customers) { + int res = 0, minPenalty = 0, penalty = 0; + + for (int i = 0; i < customers.length(); i++) { + penalty += customers.charAt(i) == 'Y' ? 1 : -1; + + if (penalty > minPenalty) { + minPenalty = penalty; + res = i + 1; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int bestClosingTime(string customers) { + int res = 0, minPenalty = 0, penalty = 0; + + for (int i = 0; i < customers.size(); i++) { + penalty += customers[i] == 'Y' ? 1 : -1; + + if (penalty > minPenalty) { + minPenalty = penalty; + res = i + 1; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} customers + * @return {number} + */ + bestClosingTime(customers) { + let res = 0, minPenalty = 0, penalty = 0; + + for (let i = 0; i < customers.length; i++) { + penalty += customers[i] === 'Y' ? 1 : -1; + + if (penalty > minPenalty) { + minPenalty = penalty; + res = i + 1; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/minimum-remove-to-make-valid-parentheses.md b/articles/minimum-remove-to-make-valid-parentheses.md new file mode 100644 index 000000000..43c6d5981 --- /dev/null +++ b/articles/minimum-remove-to-make-valid-parentheses.md @@ -0,0 +1,646 @@ +## 1. Stack + +::tabs-start + +```python +class Solution: + def minRemoveToMakeValid(self, s: str) -> str: + res = [] + cnt = 0 # extra ( parentheses + for c in s: + if c == "(": + res.append(c) + cnt += 1 + elif c == ")" and cnt > 0: + res.append(c) + cnt -= 1 + elif c != ")": + res.append(c) + + filtered = [] + for c in reversed(res): + if c == "(" and cnt > 0: + cnt -= 1 + else: + filtered.append(c) + return "".join(reversed(filtered)) +``` + +```java +public class Solution { + public String minRemoveToMakeValid(String s) { + StringBuilder res = new StringBuilder(); + int cnt = 0; + + for (char c : s.toCharArray()) { + if (c == '(') { + res.append(c); + cnt++; + } else if (c == ')' && cnt > 0) { + res.append(c); + cnt--; + } else if (c != ')') { + res.append(c); + } + } + + StringBuilder filtered = new StringBuilder(); + for (int i = res.length() - 1; i >= 0; i--) { + char c = res.charAt(i); + if (c == '(' && cnt > 0) { + cnt--; + } else { + filtered.append(c); + } + } + return filtered.reverse().toString(); + } +} +``` + +```cpp +class Solution { +public: + string minRemoveToMakeValid(string s) { + string res; + int cnt = 0; + + for (char c : s) { + if (c == '(') { + res.push_back(c); + cnt++; + } else if (c == ')' && cnt > 0) { + res.push_back(c); + cnt--; + } else if (c != ')') { + res.push_back(c); + } + } + + string filtered; + for (int i = res.size() - 1; i >= 0; i--) { + char c = res[i]; + if (c == '(' && cnt > 0) { + cnt--; + } else { + filtered.push_back(c); + } + } + reverse(filtered.begin(), filtered.end()); + return filtered; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + minRemoveToMakeValid(s) { + let res = []; + let cnt = 0; + + for (let c of s) { + if (c === '(') { + res.push(c); + cnt++; + } else if (c === ')' && cnt > 0) { + res.push(c); + cnt--; + } else if (c !== ')') { + res.push(c); + } + } + + let filtered = []; + for (let i = res.length - 1; i >= 0; i--) { + let c = res[i]; + if (c === '(' && cnt > 0) { + cnt--; + } else { + filtered.push(c); + } + } + return filtered.reverse().join(''); + } +} +``` + +```csharp +public class Solution { + public string MinRemoveToMakeValid(string s) { + List res = new List(); + int cnt = 0; + + foreach (char c in s) { + if (c == '(') { + res.Add(c); + cnt++; + } else if (c == ')' && cnt > 0) { + res.Add(c); + cnt--; + } else if (c != ')') { + res.Add(c); + } + } + + List filtered = new List(); + for (int i = res.Count - 1; i >= 0; i--) { + if (res[i] == '(' && cnt > 0) { + cnt--; + } else { + filtered.Add(res[i]); + } + } + + filtered.Reverse(); + return new string(filtered.ToArray()); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Without Stack + +::tabs-start + +```python +class Solution: + def minRemoveToMakeValid(self, s: str) -> str: + arr = list(s) + cnt = 0 # extra ( parentheses + for i, c in enumerate(s): + if c == "(": + cnt += 1 + elif c == ")" and cnt > 0: + cnt -= 1 + elif c == ")": + arr[i] = '' + + res = [] + for c in reversed(arr): + if c == '(' and cnt > 0: + cnt -= 1 + else: + res.append(c) + + return ''.join(reversed(res)) +``` + +```java +public class Solution { + public String minRemoveToMakeValid(String s) { + char[] arr = s.toCharArray(); + int cnt = 0; + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '(') { + cnt++; + } else if (c == ')' && cnt > 0) { + cnt--; + } else if (c == ')') { + arr[i] = '\0'; + } + } + + StringBuilder res = new StringBuilder(); + for (int i = arr.length - 1; i >= 0; i--) { + char c = arr[i]; + if (c == '(' && cnt > 0) { + cnt--; + } else if (c != '\0') { + res.append(c); + } + } + + return res.reverse().toString(); + } +} +``` + +```cpp +class Solution { +public: + string minRemoveToMakeValid(string s) { + vector arr(s.begin(), s.end()); + int cnt = 0; + + for (int i = 0; i < s.size(); i++) { + if (s[i] == '(') { + cnt++; + } else if (s[i] == ')' && cnt > 0) { + cnt--; + } else if (s[i] == ')') { + arr[i] = '\0'; + } + } + + string res; + for (int i = arr.size() - 1; i >= 0; i--) { + if (arr[i] == '(' && cnt > 0) { + cnt--; + } else if (arr[i] != '\0') { + res.push_back(arr[i]); + } + } + + reverse(res.begin(), res.end()); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + minRemoveToMakeValid(s) { + let arr = [...s]; + let cnt = 0; + + for (let i = 0; i < s.length; i++) { + let c = s[i]; + if (c === '(') { + cnt++; + } else if (c === ')' && cnt > 0) { + cnt--; + } else if (c === ')') { + arr[i] = ''; + } + } + + let res = []; + for (let i = arr.length - 1; i >= 0; i--) { + let c = arr[i]; + if (c === '(' && cnt > 0) { + cnt--; + } else if (c !== '') { + res.push(c); + } + } + + return res.reverse().join(''); + } +} +``` + +```csharp +public class Solution { + public string MinRemoveToMakeValid(string s) { + char[] arr = s.ToCharArray(); + int cnt = 0; + + for (int i = 0; i < s.Length; i++) { + if (s[i] == '(') { + cnt++; + } else if (s[i] == ')' && cnt > 0) { + cnt--; + } else if (s[i] == ')') { + arr[i] = '\0'; // mark invalid ')' + } + } + + List res = new List(); + for (int i = arr.Length - 1; i >= 0; i--) { + if (arr[i] == '(' && cnt > 0) { + cnt--; + } else if (arr[i] != '\0') { + res.Add(arr[i]); + } + } + + res.Reverse(); + return new string(res.ToArray()); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Stack (Optimal) + +::tabs-start + +```python +class Solution: + def minRemoveToMakeValid(self, s: str) -> str: + s = list(s) + stack = [] + for i, c in enumerate(s): + if c == '(': + stack.append(i) + elif c == ')': + if stack: + stack.pop() + else: + s[i] = '' + + while stack: + s[stack.pop()] = '' + return ''.join(s) +``` + +```java +public class Solution { + public String minRemoveToMakeValid(String s) { + StringBuilder sb = new StringBuilder(s); + Stack stack = new Stack<>(); + + for (int i = 0; i < sb.length(); i++) { + if (sb.charAt(i) == '(') { + stack.push(i); + } else if (sb.charAt(i) == ')') { + if (!stack.isEmpty()) { + stack.pop(); + } else { + sb.setCharAt(i, '\0'); + } + } + } + + while (!stack.isEmpty()) { + sb.setCharAt(stack.pop(), '\0'); + } + + StringBuilder result = new StringBuilder(); + for (int i = 0; i < sb.length(); i++) { + if (sb.charAt(i) != '\0') { + result.append(sb.charAt(i)); + } + } + return result.toString(); + } +} +``` + +```cpp +class Solution { +public: + string minRemoveToMakeValid(string s) { + stack stack; + for (int i = 0; i < s.size(); i++) { + if (s[i] == '(') { + stack.push(i); + } else if (s[i] == ')') { + if (!stack.empty()) { + stack.pop(); + } else { + s[i] = '\0'; + } + } + } + + while (!stack.empty()) { + s[stack.top()] = '\0'; + stack.pop(); + } + + string result; + for (char& c : s) { + if (c != '\0') { + result += c; + } + } + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + minRemoveToMakeValid(s) { + const arr = [...s]; + const stack = []; + + for (let i = 0; i < arr.length; i++) { + if (arr[i] === '(') { + stack.push(i); + } else if (arr[i] === ')') { + if (stack.length > 0) { + stack.pop(); + } else { + arr[i] = ''; + } + } + } + + while (stack.length > 0) { + arr[stack.pop()] = ''; + } + + return arr.join(''); + } +} +``` + +```csharp +public class Solution { + public string MinRemoveToMakeValid(string s) { + char[] arr = s.ToCharArray(); + Stack stack = new Stack(); + + for (int i = 0; i < arr.Length; i++) { + if (arr[i] == '(') { + stack.Push(i); + } else if (arr[i] == ')') { + if (stack.Count > 0) { + stack.Pop(); + } else { + arr[i] = '\0'; + } + } + } + + while (stack.Count > 0) { + arr[stack.Pop()] = '\0'; + } + + StringBuilder result = new StringBuilder(); + foreach (char c in arr) { + if (c != '\0') { + result.Append(c); + } + } + + return result.ToString(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Without Stack (Optimal) + +::tabs-start + +```python +class Solution: + def minRemoveToMakeValid(self, s: str) -> str: + openCnt = closeCnt = 0 + for c in s: + closeCnt += c == ')' + + res = [] + for c in s: + if c == '(': + if openCnt == closeCnt: + continue + openCnt += 1 + elif c == ')': + closeCnt -= 1 + if openCnt == 0: + continue + openCnt -= 1 + res.append(c) + + return ''.join(res) +``` + +```java +public class Solution { + public String minRemoveToMakeValid(String s) { + int openCnt = 0, closeCnt = 0; + for (char c : s.toCharArray()) { + if (c == ')') closeCnt++; + } + + StringBuilder res = new StringBuilder(); + for (char c : s.toCharArray()) { + if (c == '(') { + if (openCnt == closeCnt) continue; + openCnt++; + } else if (c == ')') { + closeCnt--; + if (openCnt == 0) continue; + openCnt--; + } + res.append(c); + } + + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string minRemoveToMakeValid(string s) { + int openCnt = 0, closeCnt = 0; + for (char& c : s) { + if (c == ')') closeCnt++; + } + + string res; + for (char& c : s) { + if (c == '(') { + if (openCnt == closeCnt) continue; + openCnt++; + } else if (c == ')') { + closeCnt--; + if (openCnt == 0) continue; + openCnt--; + } + res.push_back(c); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + minRemoveToMakeValid(s) { + let openCnt = 0, closeCnt = 0; + for (const c of s) { + if (c === ')') closeCnt++; + } + + let res = []; + for (const c of s) { + if (c === '(') { + if (openCnt === closeCnt) continue; + openCnt++; + } else if (c === ')') { + closeCnt--; + if (openCnt === 0) continue; + openCnt--; + } + res.push(c); + } + + return res.join(''); + } +} +``` + +```csharp +public class Solution { + public string MinRemoveToMakeValid(string s) { + int openCnt = 0, closeCnt = 0; + foreach (char c in s) { + if (c == ')') closeCnt++; + } + + StringBuilder res = new StringBuilder(); + foreach (char c in s) { + if (c == '(') { + if (openCnt == closeCnt) continue; + openCnt++; + } else if (c == ')') { + closeCnt--; + if (openCnt == 0) continue; + openCnt--; + } + res.Append(c); + } + + return res.ToString(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output string. \ No newline at end of file diff --git a/articles/minimum-score-of-a-path-between-two-cities.md b/articles/minimum-score-of-a-path-between-two-cities.md new file mode 100644 index 000000000..820b21739 --- /dev/null +++ b/articles/minimum-score-of-a-path-between-two-cities.md @@ -0,0 +1,646 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def minScore(self, n: int, roads: list[list[int]]) -> int: + adj = defaultdict(list) + for src, dst, dist in roads: + adj[src].append((dst, dist)) + adj[dst].append((src, dist)) + + res = float("inf") + visit = set() + + def dfs(node): + nonlocal res + if node in visit: + return + visit.add(node) + for nei, dist in adj[node]: + res = min(res, dist) + dfs(nei) + + dfs(1) + return res +``` + +```java +public class Solution { + private List[] adj; + private boolean[] visit; + private int res; + + public int minScore(int n, int[][] roads) { + adj = new ArrayList[n + 1]; + visit = new boolean[n + 1]; + res = Integer.MAX_VALUE; + + for (int i = 0; i <= n; i++) { + adj[i] = new ArrayList<>(); + } + + for (int[] road : roads) { + adj[road[0]].add(new int[]{road[1], road[2]}); + adj[road[1]].add(new int[]{road[0], road[2]}); + } + + dfs(1); + return res; + } + + private void dfs(int node) { + if (visit[node]) return; + visit[node] = true; + + for (int[] edge : adj[node]) { + res = Math.min(res, edge[1]); + dfs(edge[0]); + } + } +} +``` + +```cpp +class Solution { +public: + vector>> adj; + vector visit; + int res; + + int minScore(int n, vector>& roads) { + adj.resize(n + 1); + visit.resize(n + 1, false); + res = INT_MAX; + + for (auto& road : roads) { + adj[road[0]].push_back({road[1], road[2]}); + adj[road[1]].push_back({road[0], road[2]}); + } + + dfs(1); + return res; + } + + void dfs(int node) { + if (visit[node]) return; + visit[node] = true; + + for (auto& edge : adj[node]) { + res = min(res, edge.second); + dfs(edge.first); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} roads + * @return {number} + */ + minScore(n, roads) { + const adj = Array.from({ length: n + 1 }, () => []); + for (const [src, dst, dist] of roads) { + adj[src].push([dst, dist]); + adj[dst].push([src, dist]); + } + + let res = Infinity; + const visit = new Array(n + 1).fill(false); + + const dfs = (node) => { + if (visit[node]) return; + visit[node] = true; + + for (const [nei, dist] of adj[node]) { + res = Math.min(res, dist); + dfs(nei); + } + }; + + dfs(1); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def minScore(self, n: int, roads: list[list[int]]) -> int: + adj = [[] for _ in range(n + 1)] + for src, dst, dist in roads: + adj[src].append((dst, dist)) + adj[dst].append((src, dist)) + + res = float("inf") + visit = [False] * (n + 1) + q = deque([1]) + visit[1] = True + + while q: + node = q.popleft() + for nei, dist in adj[node]: + res = min(res, dist) + if not visit[nei]: + visit[nei] = True + q.append(nei) + + return res +``` + +```java +public class Solution { + public int minScore(int n, int[][] roads) { + List[] adj = new ArrayList[n + 1]; + for (int i = 0; i <= n; i++) adj[i] = new ArrayList<>(); + + for (int[] road : roads) { + adj[road[0]].add(new int[]{road[1], road[2]}); + adj[road[1]].add(new int[]{road[0], road[2]}); + } + + int res = Integer.MAX_VALUE; + boolean[] visit = new boolean[n + 1]; + Queue q = new LinkedList<>(); + q.offer(1); + visit[1] = true; + + while (!q.isEmpty()) { + int node = q.poll(); + for (int[] edge : adj[node]) { + int nei = edge[0], dist = edge[1]; + res = Math.min(res, dist); + if (!visit[nei]) { + visit[nei] = true; + q.offer(nei); + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minScore(int n, vector>& roads) { + vector>> adj(n + 1); + for (auto& road : roads) { + adj[road[0]].emplace_back(road[1], road[2]); + adj[road[1]].emplace_back(road[0], road[2]); + } + + int res = INT_MAX; + vector visit(n + 1, false); + queue q; + q.push(1); + visit[1] = true; + + while (!q.empty()) { + int node = q.front();q.pop(); + for (auto& [nei, dist] : adj[node]) { + res = min(res, dist); + if (!visit[nei]) { + visit[nei] = true; + q.push(nei); + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} roads + * @return {number} + */ + minScore(n, roads) { + const adj = Array.from({ length: n + 1 }, () => []); + for (const [src, dst, dist] of roads) { + adj[src].push([dst, dist]); + adj[dst].push([src, dist]); + } + + let res = Infinity; + const visit = new Array(n + 1).fill(false); + const q = new Queue([1]); + visit[1] = true; + + while (!q.isEmpty()) { + const node = q.pop(); + for (const [nei, dist] of adj[node]) { + res = Math.min(res, dist); + if (!visit[nei]) { + visit[nei] = true; + q.push(nei); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +class Solution: + def minScore(self, n: int, roads: list[list[int]]) -> int: + adj = [[] for _ in range(n + 1)] + for src, dst, dist in roads: + adj[src].append((dst, dist)) + adj[dst].append((src, dist)) + + res = float("inf") + visit = [False] * (n + 1) + stack = [1] + visit[1] = True + + while stack: + node = stack.pop() + for nei, dist in adj[node]: + res = min(res, dist) + if not visit[nei]: + visit[nei] = True + stack.append(nei) + + return res +``` + +```java +public class Solution { + public int minScore(int n, int[][] roads) { + List[] adj = new ArrayList[n + 1]; + for (int i = 0; i <= n; i++) adj[i] = new ArrayList<>(); + + for (int[] road : roads) { + adj[road[0]].add(new int[]{road[1], road[2]}); + adj[road[1]].add(new int[]{road[0], road[2]}); + } + + int res = Integer.MAX_VALUE; + boolean[] visit = new boolean[n + 1]; + Stack stack = new Stack<>(); + stack.push(1); + visit[1] = true; + + while (!stack.isEmpty()) { + int node = stack.pop(); + for (int[] edge : adj[node]) { + int nei = edge[0], dist = edge[1]; + res = Math.min(res, dist); + if (!visit[nei]) { + visit[nei] = true; + stack.push(nei); + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int minScore(int n, vector>& roads) { + vector>> adj(n + 1); + for (auto& road : roads) { + adj[road[0]].emplace_back(road[1], road[2]); + adj[road[1]].emplace_back(road[0], road[2]); + } + + int res = INT_MAX; + vector visit(n + 1, false); + stack stk; + stk.push(1); + visit[1] = true; + + while (!stk.empty()) { + int node = stk.top();stk.pop(); + for (auto& [nei, dist] : adj[node]) { + res = min(res, dist); + if (!visit[nei]) { + visit[nei] = true; + stk.push(nei); + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} roads + * @return {number} + */ + minScore(n, roads) { + const adj = Array.from({ length: n + 1 }, () => []); + for (const [src, dst, dist] of roads) { + adj[src].push([dst, dist]); + adj[dst].push([src, dist]); + } + + let res = Infinity; + const visit = new Array(n + 1).fill(false); + const stack = [1]; + visit[1] = true; + + while (stack.length) { + const node = stack.pop(); + for (const [nei, dist] of adj[node]) { + res = Math.min(res, dist); + if (!visit[nei]) { + visit[nei] = true; + stack.push(nei); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 4. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu, pv = self.find(u), self.find(v) + if pu == pv: + return False + if self.Size[pu] >= self.Size[pv]: + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + else: + self.Size[pv] += self.Size[pu] + self.Parent[pu] = pv + return True + +class Solution: + def minScore(self, n: int, roads: list[list[int]]) -> int: + dsu = DSU(n) + for src, dst, _ in roads: + dsu.union(src, dst) + + res = float("inf") + root = dsu.find(1) + for src, dst, dist in roads: + if dsu.find(src) == root: + res = min(res, dist) + + return res +``` + +```java +class DSU { + int[] parent, size; + + public DSU(int n) { + parent = new int[n + 1]; + size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + parent[i] = i; + size[i] = 1; + } + } + + public int find(int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (size[pu] >= size[pv]) { + size[pu] += size[pv]; + parent[pv] = pu; + } else { + size[pv] += size[pu]; + parent[pu] = pv; + } + return true; + } +} + +public class Solution { + public int minScore(int n, int[][] roads) { + DSU dsu = new DSU(n); + for (int[] road : roads) { + dsu.union(road[0], road[1]); + } + + int res = Integer.MAX_VALUE; + int root = dsu.find(1); + for (int[] road : roads) { + if (dsu.find(road[0]) == root) { + res = Math.min(res, road[2]); + } + } + + return res; + } +} +``` + +```cpp +class DSU { +public: + vector parent, size; + + DSU(int n) { + parent.resize(n + 1); + size.resize(n + 1, 1); + for (int i = 0; i <= n; i++) { + parent[i] = i; + } + } + + int find(int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + bool unionSets(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (size[pu] >= size[pv]) { + size[pu] += size[pv]; + parent[pv] = pu; + } else { + size[pv] += size[pu]; + parent[pu] = pv; + } + return true; + } +}; + +class Solution { +public: + int minScore(int n, vector>& roads) { + DSU dsu(n); + for (auto& road : roads) { + dsu.unionSets(road[0], road[1]); + } + + int res = INT_MAX; + int root = dsu.find(1); + for (auto& road : roads) { + if (dsu.find(road[0]) == root) { + res = min(res, road[2]); + } + } + + return res; + } +}; +``` + +```javascript +class DSU { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.parent = Array.from({ length: n + 1 }, (_, i) => i); + this.size = new Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.parent[node] !== node) { + this.parent[node] = this.find(this.parent[node]); + } + return this.parent[node]; + } + + /** + * @param {number} u + * @param {number} u=v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u), pv = this.find(v); + if (pu === pv) return false; + if (this.size[pu] >= this.size[pv]) { + this.size[pu] += this.size[pv]; + this.parent[pv] = pu; + } else { + this.size[pv] += this.size[pu]; + this.parent[pu] = pv; + } + return true; + } +} + +class Solution { + /** + * @param {number} n + * @param {number[][]} roads + * @return {number} + */ + minScore(n, roads) { + const dsu = new DSU(n); + for (const [src, dst] of roads) { + dsu.union(src, dst); + } + + let res = Infinity; + const root = dsu.find(1); + for (const [src, dst, dist] of roads) { + if (dsu.find(src) === root) { + res = Math.min(res, dist); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + (E * α(V)))$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges in the graph. $α()$ is used for amortized complexity. \ No newline at end of file diff --git a/articles/minimum-size-subarray-sum.md b/articles/minimum-size-subarray-sum.md new file mode 100644 index 000000000..683582184 --- /dev/null +++ b/articles/minimum-size-subarray-sum.md @@ -0,0 +1,408 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def minSubArrayLen(self, target: int, nums: List[int]) -> int: + n = len(nums) + res = float("inf") + + for i in range(n): + curSum = 0 + for j in range(i, n): + curSum += nums[j] + if curSum >= target: + res = min(res, j - i + 1) + break + + return 0 if res == float("inf") else res +``` + +```java +public class Solution { + public int minSubArrayLen(int target, int[] nums) { + int n = nums.length; + int res = Integer.MAX_VALUE; + + for (int i = 0; i < n; i++) { + int curSum = 0, j = i; + while (j < n) { + curSum += nums[j]; + if (curSum >= target) { + res = Math.min(res, j - i + 1); + break; + } + j++; + } + } + + return res == Integer.MAX_VALUE ? 0 : res; + } +} +``` + +```cpp +class Solution { +public: + int minSubArrayLen(int target, vector& nums) { + int n = nums.size(); + int res = INT_MAX; + + for (int i = 0; i < n; i++) { + int curSum = 0, j = i; + while (j < n) { + curSum += nums[j]; + if (curSum >= target) { + res = min(res, j - i + 1); + break; + } + j++; + } + } + + return res == INT_MAX ? 0 : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} target + * @param {number[]} nums + * @return {number} + */ + minSubArrayLen(target, nums) { + let n = nums.length; + let res = Infinity; + + for (let i = 0; i < n; i++) { + let curSum = 0, j = i; + while (j < n) { + curSum += nums[j]; + if (curSum >= target) { + res = Math.min(res, j - i + 1); + break; + } + j++; + } + } + + return res == Infinity ? 0 : res; + } +} +``` + +```csharp +public class Solution { + public int MinSubArrayLen(int target, int[] nums) { + int n = nums.Length; + int res = int.MaxValue; + + for (int i = 0; i < n; i++) { + int curSum = 0; + for (int j = i; j < n; j++) { + curSum += nums[j]; + if (curSum >= target) { + res = Math.Min(res, j - i + 1); + break; + } + } + } + + return res == int.MaxValue ? 0 : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Sliding Window + +::tabs-start + +```python +class Solution: + def minSubArrayLen(self, target: int, nums: List[int]) -> int: + l, total = 0, 0 + res = float("inf") + + for r in range(len(nums)): + total += nums[r] + while total >= target: + res = min(r - l + 1, res) + total -= nums[l] + l += 1 + + return 0 if res == float("inf") else res +``` + +```java +public class Solution { + public int minSubArrayLen(int target, int[] nums) { + int l = 0, total = 0; + int res = Integer.MAX_VALUE; + + for (int r = 0; r < nums.length; r++) { + total += nums[r]; + while (total >= target) { + res = Math.min(r - l + 1, res); + total -= nums[l]; + l++; + } + } + + return res == Integer.MAX_VALUE ? 0 : res; + } +} +``` + +```cpp +class Solution { +public: + int minSubArrayLen(int target, vector& nums) { + int l = 0, total = 0, res = INT_MAX; + + for (int r = 0; r < nums.size(); r++) { + total += nums[r]; + while (total >= target) { + res = min(r - l + 1, res); + total -= nums[l]; + l++; + } + } + + return res == INT_MAX ? 0 : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} target + * @param {number[]} nums + * @return {number} + */ + minSubArrayLen(target, nums) { + let l = 0, total = 0; + let res = Infinity; + + for (let r = 0; r < nums.length; r++) { + total += nums[r]; + while (total >= target) { + res = Math.min(r - l + 1, res); + total -= nums[l]; + l++; + } + } + + return res === Infinity ? 0 : res; + } +} +``` + +```csharp +public class Solution { + public int MinSubArrayLen(int target, int[] nums) { + int l = 0, total = 0; + int res = int.MaxValue; + + for (int r = 0; r < nums.Length; r++) { + total += nums[r]; + + while (total >= target) { + res = Math.Min(res, r - l + 1); + total -= nums[l]; + l++; + } + } + + return res == int.MaxValue ? 0 : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Prefix Sum + Binary Search + +::tabs-start + +```python +class Solution: + def minSubArrayLen(self, target: int, nums: List[int]) -> int: + n = len(nums) + prefixSum = [0] * (n + 1) + for i in range(n): + prefixSum[i + 1] = prefixSum[i] + nums[i] + + res = n + 1 + for i in range(n): + l, r = i, n + while l < r: + mid = (l + r) // 2 + curSum = prefixSum[mid + 1] - prefixSum[i] + if curSum >= target: + r = mid + else: + l = mid + 1 + if l != n: + res = min(res, l - i + 1) + + return res % (n + 1) +``` + +```java +public class Solution { + public int minSubArrayLen(int target, int[] nums) { + int n = nums.length; + int[] prefixSum = new int[n + 1]; + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + int res = n + 1; + for (int i = 0; i < n; i++) { + int l = i, r = n; + while (l < r) { + int mid = (l + r) / 2; + int curSum = prefixSum[mid + 1] - prefixSum[i]; + if (curSum >= target) { + r = mid; + } else { + l = mid + 1; + } + } + if (l != n) { + res = Math.min(res, l - i + 1); + } + } + + return res % (n + 1); + } +} +``` + +```cpp +class Solution { +public: + int minSubArrayLen(int target, vector& nums) { + int n = nums.size(); + vector prefixSum(n + 1, 0); + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + int res = n + 1; + for (int i = 0; i < n; i++) { + int l = i, r = n; + while (l < r) { + int mid = (l + r) / 2; + int curSum = prefixSum[mid + 1] - prefixSum[i]; + if (curSum >= target) { + r = mid; + } else { + l = mid + 1; + } + } + if (l != n) { + res = min(res, l - i + 1); + } + } + + return res % (n + 1); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} target + * @param {number[]} nums + * @return {number} + */ + minSubArrayLen(target, nums) { + const n = nums.length; + const prefixSum = new Array(n + 1).fill(0); + for (let i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + let res = n + 1; + for (let i = 0; i < n; i++) { + let l = i, r = n; + while (l < r) { + const mid = Math.floor((l + r) / 2); + const curSum = prefixSum[mid + 1] - prefixSum[i]; + if (curSum >= target) { + r = mid; + } else { + l = mid + 1; + } + } + if (l !== n) { + res = Math.min(res, l - i + 1); + } + } + + return res % (n + 1); + } +} +``` + +```csharp +public class Solution { + public int MinSubArrayLen(int target, int[] nums) { + int n = nums.Length; + int[] prefixSum = new int[n + 1]; + + for (int i = 0; i < n; i++) { + prefixSum[i + 1] = prefixSum[i] + nums[i]; + } + + int res = n + 1; + + for (int i = 0; i < n; i++) { + int l = i, r = n; + while (l < r) { + int mid = (l + r) / 2; + int curSum = prefixSum[mid + 1] - prefixSum[i]; + if (curSum >= target) { + r = mid; + } else { + l = mid + 1; + } + } + if (l != n) { + res = Math.Min(res, l - i + 1); + } + } + + return res % (n + 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/minimum-stack.md b/articles/minimum-stack.md new file mode 100644 index 000000000..29041a02c --- /dev/null +++ b/articles/minimum-stack.md @@ -0,0 +1,940 @@ +## 1. Brute Force + +::tabs-start + +```python +class MinStack: + + def __init__(self): + self.stack = [] + + def push(self, val: int) -> None: + self.stack.append(val) + + def pop(self) -> None: + self.stack.pop() + + def top(self) -> int: + return self.stack[-1] + + def getMin(self) -> int: + tmp = [] + mini = self.stack[-1] + + while len(self.stack): + mini = min(mini, self.stack[-1]) + tmp.append(self.stack.pop()) + + while len(tmp): + self.stack.append(tmp.pop()) + + return mini +``` + +```java +class MinStack { + + private Stack stack; + + public MinStack() { + stack = new Stack<>(); + } + + public void push(int val) { + stack.push(val); + } + + public void pop() { + stack.pop(); + } + + public int top() { + return stack.peek(); + } + + public int getMin() { + Stack tmp = new Stack<>(); + int mini = stack.peek(); + + while (!stack.isEmpty()) { + mini = Math.min(mini, stack.peek()); + tmp.push(stack.pop()); + } + + while (!tmp.isEmpty()) { + stack.push(tmp.pop()); + } + + return mini; + } +} +``` + +```cpp +class MinStack { +public: + stack stk; + MinStack() { + + } + + void push(int val) { + stk.push(val); + } + + void pop() { + stk.pop(); + } + + int top() { + return stk.top(); + } + + int getMin() { + stack tmp; + int mini = stk.top(); + while (stk.size()) { + mini = min(mini, stk.top()); + tmp.push(stk.top()); + stk.pop(); + } + + while (tmp.size()) { + stk.push(tmp.top()); + tmp.pop(); + } + + return mini; + } +}; +``` + +```javascript +class MinStack { + constructor() { + this.stack = []; + } + + /** + * @param {number} val + * @return {void} + */ + push(val) { + this.stack.push(val); + } + + /** + * @return {void} + */ + pop() { + this.stack.pop(); + } + + /** + * @return {number} + */ + top() { + return this.stack[this.stack.length - 1]; + } + + /** + * @return {number} + */ + getMin() { + const tmp = []; + let mini = this.stack[this.stack.length - 1]; + + while (this.stack.length > 0) { + mini = Math.min(mini, this.stack[this.stack.length - 1]); + tmp.push(this.stack.pop()); + } + + while (tmp.length > 0) { + this.stack.push(tmp.pop()); + } + + return mini; + } +} +``` + +```csharp +public class MinStack { + private Stack stack; + + public MinStack() { + stack = new Stack(); + } + + public void Push(int val) { + stack.Push(val); + } + + public void Pop() { + stack.Pop(); + } + + public int Top() { + return stack.Peek(); + } + + public int GetMin() { + Stack tmp = new Stack(); + int mini = stack.Peek(); + + while (stack.Count > 0) { + mini = System.Math.Min(mini, stack.Peek()); + tmp.Push(stack.Pop()); + } + + while (tmp.Count > 0) { + stack.Push(tmp.Pop()); + } + + return mini; + } +} +``` + +```go +type MinStack struct { + stack *linkedliststack.Stack +} + +func Constructor() MinStack { + return MinStack{stack: linkedliststack.New()} +} + +func (this *MinStack) Push(val int) { + this.stack.Push(val) +} + +func (this *MinStack) Pop() { + this.stack.Pop() +} + +func (this *MinStack) Top() int { + top, _ := this.stack.Peek() + return top.(int) +} + +func (this *MinStack) GetMin() int { + tmp := linkedliststack.New() + min := this.Top() + + for !this.stack.Empty() { + val, _ := this.stack.Pop() + min = getMin(min, val.(int)) + tmp.Push(val) + } + + for !tmp.Empty() { + val, _ := tmp.Pop() + this.stack.Push(val) + } + + return min +} + +func getMin(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class MinStack() { + private val stack = ArrayDeque() + + fun push(`val`: Int) { + stack.addLast(`val`) + } + + fun pop() { + stack.removeLast() + } + + fun top(): Int { + return stack.last() + } + + fun getMin(): Int { + val tmp = ArrayDeque() + var min = stack.last() + + while (stack.isNotEmpty()) { + min = minOf(min, stack.last()) + tmp.addLast(stack.removeLast()) + } + + while (tmp.isNotEmpty()) { + stack.addLast(tmp.removeLast()) + } + + return min + } +} +``` + +```swift +class MinStack { + private var stack: [Int] = [] + + init() {} + + func push(_ val: Int) { + stack.append(val) + } + + func pop() { + stack.popLast() + } + + func top() -> Int { + return stack.last! + } + + func getMin() -> Int { + var tmp = [Int]() + var mini = stack.last! + + while !stack.isEmpty { + mini = min(mini, stack.last!) + tmp.append(stack.removeLast()) + } + + while !tmp.isEmpty { + stack.append(tmp.removeLast()) + } + + return mini + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ for $getMin()$ and $O(1)$ for other operations. +* Space complexity: $O(n)$ for $getMin()$ and $O(1)$ for other operations. + +--- + +## 2. Two Stacks + +::tabs-start + +```python +class MinStack: + def __init__(self): + self.stack = [] + self.minStack = [] + + def push(self, val: int) -> None: + self.stack.append(val) + val = min(val, self.minStack[-1] if self.minStack else val) + self.minStack.append(val) + + def pop(self) -> None: + self.stack.pop() + self.minStack.pop() + + def top(self) -> int: + return self.stack[-1] + + def getMin(self) -> int: + return self.minStack[-1] +``` + +```java +public class MinStack { + private Stack stack; + private Stack minStack; + + public MinStack() { + stack = new Stack<>(); + minStack = new Stack<>(); + } + + public void push(int val) { + stack.push(val); + if (minStack.isEmpty() || val <= minStack.peek()) { + minStack.push(val); + } + } + + public void pop() { + if (stack.isEmpty()) return; + int top = stack.pop(); + if (top == minStack.peek()) { + minStack.pop(); + } + } + + public int top() { + return stack.peek(); + } + + public int getMin() { + return minStack.peek(); + } +} +``` + +```cpp +class MinStack { +private: + std::stack stack; + std::stack minStack; + +public: + MinStack() {} + + void push(int val) { + stack.push(val); + val = std::min(val, minStack.empty() ? val : minStack.top()); + minStack.push(val); + } + + void pop() { + stack.pop(); + minStack.pop(); + } + + int top() { + return stack.top(); + } + + int getMin() { + return minStack.top(); + } +}; +``` + +```javascript +class MinStack { + constructor() { + this.stack = []; + this.minStack = []; + } + + /** + * @param {number} val + * @return {void} + */ + push(val) { + this.stack.push(val); + val = Math.min( + val, + this.minStack.length === 0 + ? val + : this.minStack[this.minStack.length - 1], + ); + this.minStack.push(val); + } + + /** + * @return {void} + */ + pop() { + this.stack.pop(); + this.minStack.pop(); + } + + /** + * @return {number} + */ + top() { + return this.stack[this.stack.length - 1]; + } + + /** + * @return {number} + */ + getMin() { + return this.minStack[this.minStack.length - 1]; + } +} +``` + +```csharp +public class MinStack { + + private Stack stack; + private Stack minStack; + + public MinStack() { + stack = new Stack(); + minStack = new Stack(); + } + + public void Push(int val) { + stack.Push(val); + val = Math.Min(val, minStack.Count == 0 ? val : minStack.Peek()); + minStack.Push(val); + } + + public void Pop() { + stack.Pop(); + minStack.Pop(); + } + + public int Top() { + return stack.Peek(); + } + + public int GetMin() { + return minStack.Peek(); + } +} +``` + +```go +type MinStack struct { + stack *linkedliststack.Stack + minStack *linkedliststack.Stack +} + +func Constructor() MinStack { + return MinStack{ + stack: linkedliststack.New(), + minStack: linkedliststack.New(), + } +} + +func (this *MinStack) Push(val int) { + this.stack.Push(val) + minVal := val + if !this.minStack.Empty() { + if top, ok := this.minStack.Peek(); ok { + if top.(int) < val { + minVal = top.(int) + } + } + } + this.minStack.Push(minVal) +} + +func (this *MinStack) Pop() { + this.stack.Pop() + this.minStack.Pop() +} + +func (this *MinStack) Top() int { + top, _ := this.stack.Peek() + return top.(int) +} + +func (this *MinStack) GetMin() int { + min, _ := this.minStack.Peek() + return min.(int) +} +``` + +```kotlin +class MinStack() { + private val stack = ArrayDeque() + private val minStack = ArrayDeque() + + fun push(`val`: Int) { + stack.addLast(`val`) + val minVal = if (minStack.isNotEmpty()) minOf(`val`, minStack.last()) else `val` + minStack.addLast(minVal) + } + + fun pop() { + stack.removeLast() + minStack.removeLast() + } + + fun top(): Int { + return stack.last() + } + + fun getMin(): Int { + return minStack.last() + } +} +``` + +```swift +class MinStack { + private var stack: [Int] = [] + private var minStack: [Int] = [] + + init() {} + + func push(_ val: Int) { + stack.append(val) + let minVal = min(val, minStack.last ?? val) + minStack.append(minVal) + } + + func pop() { + stack.popLast() + minStack.popLast() + } + + func top() -> Int { + return stack.last! + } + + func getMin() -> Int { + return minStack.last! + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for all operations. +* Space complexity: $O(n)$ + +--- + +## 3. One Stack + +::tabs-start + +```python +class MinStack: + def __init__(self): + self.min = float('inf') + self.stack = [] + + def push(self, val: int) -> None: + if not self.stack: + self.stack.append(0) + self.min = val + else: + self.stack.append(val - self.min) + if val < self.min: + self.min = val + + def pop(self) -> None: + if not self.stack: + return + + pop = self.stack.pop() + + if pop < 0: + self.min = self.min - pop + + def top(self) -> int: + top = self.stack[-1] + if top > 0: + return top + self.min + else: + return self.min + + def getMin(self) -> int: + return self.min +``` + +```java +public class MinStack { + long min; + Stack stack; + + public MinStack() { + stack = new Stack<>(); + } + + public void push(int val) { + if (stack.isEmpty()) { + stack.push(0L); + min = val; + } else { + stack.push(val - min); + if (val < min) min = val; + } + } + + public void pop() { + if (stack.isEmpty()) return; + + long pop = stack.pop(); + + if (pop < 0) min = min - pop; + } + + public int top() { + long top = stack.peek(); + if (top > 0) { + return (int) (top + min); + } else { + return (int) min; + } + } + + public int getMin() { + return (int) min; + } +} +``` + +```cpp +class MinStack { +private: + long min; + std::stack stack; + +public: + MinStack() {} + + void push(int val) { + if (stack.empty()) { + stack.push(0); + min = val; + } else { + stack.push(val - min); + if (val < min) min = val; + } + } + + void pop() { + if (stack.empty()) return; + + long pop = stack.top(); + stack.pop(); + + if (pop < 0) min = min - pop; + } + + int top() { + long top = stack.top(); + return (top > 0) ? (top + min) : (int)min; + } + + int getMin() { + return (int)min; + } +}; +``` + +```javascript +class MinStack { + constructor() { + this.min = Infinity; + this.stack = []; + } + + /** + * @param {number} val + * @return {void} + */ + push(val) { + if (this.stack.length === 0) { + this.stack.push(0); + this.min = val; + } else { + this.stack.push(val - this.min); + if (val < this.min) this.min = val; + } + } + + /** + * @return {void} + */ + pop() { + if (this.stack.length === 0) return; + + const pop = this.stack.pop(); + + if (pop < 0) this.min -= pop; + } + + /** + * @return {number} + */ + top() { + const top = this.stack[this.stack.length - 1]; + return top > 0 ? top + this.min : this.min; + } + + /** + * @return {number} + */ + getMin() { + return this.min; + } +} +``` + +```csharp +public class MinStack { + private long min; + private Stack stack; + + public MinStack() { + stack = new Stack(); + } + + public void Push(int val) { + if (stack.Count == 0) { + stack.Push(0L); + min = val; + } else { + stack.Push(val - min); + if (val < min) min = val; + } + } + + public void Pop() { + if (stack.Count == 0) return; + + long pop = stack.Pop(); + + if (pop < 0) min -= pop; + } + + public int Top() { + long top = stack.Peek(); + return top > 0 ? (int)(top + min) : (int)(min); + } + + public int GetMin() { + return (int)min; + } +} +``` + +```go +type MinStack struct { + min int + stack []int +} + +func Constructor() MinStack { + return MinStack{ + min: math.MaxInt64, + stack: []int{}, + } +} + +func (this *MinStack) Push(val int) { + if len(this.stack) == 0 { + this.stack = append(this.stack, 0) + this.min = val + } else { + this.stack = append(this.stack, val - this.min) + if val < this.min { + this.min = val + } + } +} + +func (this *MinStack) Pop() { + if len(this.stack) == 0 { + return + } + pop := this.stack[len(this.stack)-1] + this.stack = this.stack[:len(this.stack)-1] + if pop < 0 { + this.min = this.min - pop + } +} + +func (this *MinStack) Top() int { + top := this.stack[len(this.stack)-1] + if top > 0 { + return top + this.min + } + return this.min +} + +func (this *MinStack) GetMin() int { + return this.min +} +``` + +```kotlin +class MinStack() { + private var min: Long = Long.MAX_VALUE + private val stack = ArrayDeque() + + fun push(`val`: Int) { + val valAsLong = `val`.toLong() + if (stack.isEmpty()) { + stack.addLast(0L) + min = valAsLong + } else { + stack.addLast(valAsLong - min) + if (valAsLong < min) { + min = valAsLong + } + } + } + + fun pop() { + if (stack.isEmpty()) return + val pop = stack.removeLast() + if (pop < 0) { + min -= pop + } + } + + fun top(): Int { + val top = stack.last() + return if (top > 0) (top + min).toInt() else min.toInt() + } + + fun getMin(): Int { + return min.toInt() + } +} +``` + +```swift +class MinStack { + private var minVal: Int = Int.max + private var stack: [Int] = [] + + init() {} + + func push(_ val: Int) { + if stack.isEmpty { + stack.append(0) + minVal = val + } else { + stack.append(val - minVal) + if val < minVal { + minVal = val + } + } + } + + func pop() { + if stack.isEmpty { + return + } + + let pop = stack.removeLast() + + if pop < 0 { + minVal -= pop + } + } + + func top() -> Int { + let top = stack.last! + return top > 0 ? top + minVal : minVal + } + + func getMin() -> Int { + return minVal + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for all operations. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/minimum-time-to-collect-all-apples-in-a-tree.md b/articles/minimum-time-to-collect-all-apples-in-a-tree.md new file mode 100644 index 000000000..2ea61fe99 --- /dev/null +++ b/articles/minimum-time-to-collect-all-apples-in-a-tree.md @@ -0,0 +1,312 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def minTime(self, n: int, edges: List[List[int]], hasApple: List[bool]) -> int: + adj = {i: [] for i in range(n)} + for par, child in edges: + adj[par].append(child) + adj[child].append(par) + + def dfs(cur, par): + time = 0 + for child in adj[cur]: + if child == par: + continue + childTime = dfs(child, cur) + if childTime > 0 or hasApple[child]: + time += 2 + childTime + return time + + return dfs(0, -1) +``` + +```java +public class Solution { + public int minTime(int n, int[][] edges, List hasApple) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + adj[edge[1]].add(edge[0]); + } + + return dfs(0, -1, adj, hasApple); + } + + private int dfs(int cur, int parent, List[] adj, List hasApple) { + int time = 0; + for (int child : adj[cur]) { + if (child == parent) continue; + int childTime = dfs(child, cur, adj, hasApple); + if (childTime > 0 || hasApple.get(child)) { + time += 2 + childTime; + } + } + return time; + } +} +``` + +```cpp +class Solution { +public: + int minTime(int n, vector>& edges, vector& hasApple) { + vector> adj(n); + for (const auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + return dfs(0, -1, adj, hasApple); + } + +private: + int dfs(int cur, int parent, vector>& adj, vector& hasApple) { + int time = 0; + for (int child : adj[cur]) { + if (child == parent) continue; + int childTime = dfs(child, cur, adj, hasApple); + if (childTime > 0 || hasApple[child]) { + time += 2 + childTime; + } + } + return time; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @param {boolean[]} hasApple + * @return {number} + */ + minTime(n, edges, hasApple) { + const adj = Array.from({ length: n }, () => []); + for (const [parent, child] of edges) { + adj[parent].push(child); + adj[child].push(parent); + } + + const dfs = (cur, parent) => { + let time = 0; + for (const child of adj[cur]) { + if (child === parent) continue; + const childTime = dfs(child, cur); + if (childTime > 0 || hasApple[child]) { + time += 2 + childTime; + } + } + return time; + }; + + return dfs(0, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 2. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def minTime(self, n: int, edges: List[List[int]], hasApple: List[bool]) -> int: + adj = defaultdict(list) + indegree = [0] * n + for u, v in edges: + adj[u].append(v) + adj[v].append(u) + indegree[u] += 1 + indegree[v] += 1 + + queue = deque() + for i in range(1, n): + if indegree[i] == 1: + queue.append(i) + indegree[i] = 0 + + time = [0] * n + while queue: + node = queue.popleft() + for nei in adj[node]: + if indegree[nei] <= 0: + continue + + indegree[nei] -= 1 + if hasApple[node] or time[node] > 0: + time[nei] += time[node] + 2 + if indegree[nei] == 1 and nei != 0: + queue.append(nei) + + return time[0] +``` + +```java +public class Solution { + public int minTime(int n, int[][] edges, List hasApple) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + int[] indegree = new int[n]; + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + adj[edge[1]].add(edge[0]); + indegree[edge[0]]++; + indegree[edge[1]]++; + } + + Queue queue = new LinkedList<>(); + for (int i = 1; i < n; i++) { + if (indegree[i] == 1) { + queue.offer(i); + indegree[i] = 0; + } + } + + int[] time = new int[n]; + while (!queue.isEmpty()) { + int node = queue.poll(); + for (int neighbor : adj[node]) { + if (indegree[neighbor] <= 0) { + continue; + } + + indegree[neighbor]--; + if (hasApple.get(node) || time[node] > 0) { + time[neighbor] += time[node] + 2; + } + if (indegree[neighbor] == 1 && neighbor != 0) { + queue.offer(neighbor); + } + } + } + + return time[0]; + } +} +``` + +```cpp +class Solution { +public: + int minTime(int n, vector>& edges, vector& hasApple) { + vector> adj(n); + vector indegree(n, 0); + + for (const auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + indegree[edge[0]]++; + indegree[edge[1]]++; + } + + queue q; + for (int i = 1; i < n; ++i) { + if (indegree[i] == 1) { + q.push(i); + indegree[i] = 0; + } + } + + vector time(n, 0); + while (!q.empty()) { + int node = q.front(); + q.pop(); + for (int neighbor : adj[node]) { + if (indegree[neighbor] <= 0) { + continue; + } + + indegree[neighbor]--; + if (hasApple[node] || time[node] > 0) { + time[neighbor] += time[node] + 2; + } + if (indegree[neighbor] == 1 && neighbor != 0) { + q.push(neighbor); + } + } + } + + return time[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @param {boolean[]} hasApple + * @return {number} + */ + minTime(n, edges, hasApple) { + const adj = Array.from({ length: n }, () => []); + const indegree = Array(n).fill(0); + + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + indegree[u]++; + indegree[v]++; + } + + const queue = new Queue(); + for (let i = 1; i < n; i++) { + if (indegree[i] === 1) { + queue.push(i); + indegree[i] = 0; + } + } + + const time = Array(n).fill(0); + while (!queue.isEmpty()) { + const node = queue.pop(); + for (const neighbor of adj[node]) { + if (indegree[neighbor] <= 0) { + continue; + } + + indegree[neighbor]--; + if (hasApple[node] || time[node] > 0) { + time[neighbor] += time[node] + 2; + } + if (indegree[neighbor] === 1 && neighbor !== 0) { + queue.push(neighbor); + } + } + } + + return time[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. \ No newline at end of file diff --git a/articles/minimum-time-to-make-rope-colorful.md b/articles/minimum-time-to-make-rope-colorful.md new file mode 100644 index 000000000..a9d436a9d --- /dev/null +++ b/articles/minimum-time-to-make-rope-colorful.md @@ -0,0 +1,270 @@ +## 1. Two Pointers - I + +::tabs-start + +```python +class Solution: + def minCost(self, colors: str, neededTime: List[int]) -> int: + n = len(neededTime) + res = i = 0 + while i < n: + j = i + maxi = curr = 0 + while j < n and colors[j] == colors[i]: + maxi = max(maxi, neededTime[j]) + curr += neededTime[j] + j += 1 + res += curr - maxi + i = j + return res +``` + +```java +public class Solution { + public int minCost(String colors, int[] neededTime) { + int n = neededTime.length; + int res = 0, i = 0; + while (i < n) { + int j = i, maxi = 0, curr = 0; + while (j < n && colors.charAt(j) == colors.charAt(i)) { + maxi = Math.max(maxi, neededTime[j]); + curr += neededTime[j]; + j++; + } + res += curr - maxi; + i = j; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minCost(string colors, vector& neededTime) { + int n = neededTime.size(); + int res = 0, i = 0; + while (i < n) { + int j = i, maxi = 0, curr = 0; + while (j < n && colors[j] == colors[i]) { + maxi = max(maxi, neededTime[j]); + curr += neededTime[j]; + j++; + } + res += curr - maxi; + i = j; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} colors + * @param {number[]} neededTime + * @return {number} + */ + minCost(colors, neededTime) { + const n = neededTime.length; + let res = 0, i = 0; + while (i < n) { + let j = i, maxi = 0, curr = 0; + while (j < n && colors[j] === colors[i]) { + maxi = Math.max(maxi, neededTime[j]); + curr += neededTime[j]; + j++; + } + res += curr - maxi; + i = j; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Two Pointers - II + +::tabs-start + +```python +class Solution: + def minCost(self, colors: str, neededTime: List[int]) -> int: + l, res = 0, 0 + for r in range(1, len(colors)): + if colors[l] == colors[r]: + if neededTime[l] < neededTime[r]: + res += neededTime[l] + l = r + else: + res += neededTime[r] + else: + l = r + return res +``` + +```java +public class Solution { + public int minCost(String colors, int[] neededTime) { + int l = 0, res = 0; + for (int r = 1; r < colors.length(); r++) { + if (colors.charAt(l) == colors.charAt(r)) { + if (neededTime[l] < neededTime[r]) { + res += neededTime[l]; + l = r; + } else { + res += neededTime[r]; + } + } else { + l = r; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minCost(string colors, vector& neededTime) { + int l = 0, res = 0; + for (int r = 1; r < colors.size(); r++) { + if (colors[l] == colors[r]) { + if (neededTime[l] < neededTime[r]) { + res += neededTime[l]; + l = r; + } else { + res += neededTime[r]; + } + } else { + l = r; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} colors + * @param {number[]} neededTime + * @return {number} + */ + minCost(colors, neededTime) { + let l = 0, res = 0; + for (let r = 1; r < colors.length; r++) { + if (colors[l] === colors[r]) { + if (neededTime[l] < neededTime[r]) { + res += neededTime[l]; + l = r; + } else { + res += neededTime[r]; + } + } else { + l = r; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Two Pointers - III + +::tabs-start + +```python +class Solution: + def minCost(self, colors: str, neededTime: List[int]) -> int: + res = maxi = 0 + for i in range(len(colors)): + if i and colors[i] != colors[i - 1]: + maxi = 0 + res += min(maxi, neededTime[i]) + maxi = max(maxi, neededTime[i]) + return res +``` + +```java +public class Solution { + public int minCost(String colors, int[] neededTime) { + int res = 0, maxi = 0; + for (int i = 0; i < colors.length(); i++) { + if (i > 0 && colors.charAt(i) != colors.charAt(i - 1)) { + maxi = 0; + } + res += Math.min(maxi, neededTime[i]); + maxi = Math.max(maxi, neededTime[i]); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int minCost(string colors, vector& neededTime) { + int res = 0, maxi = 0; + for (int i = 0; i < colors.size(); i++) { + if (i > 0 && colors[i] != colors[i - 1]) { + maxi = 0; + } + res += min(maxi, neededTime[i]); + maxi = max(maxi, neededTime[i]); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} colors + * @param {number[]} neededTime + * @return {number} + */ + minCost(colors, neededTime) { + let res = 0, maxi = 0; + for (let i = 0; i < colors.length; i++) { + if (i > 0 && colors[i] !== colors[i - 1]) { + maxi = 0; + } + res += Math.min(maxi, neededTime[i]); + maxi = Math.max(maxi, neededTime[i]); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/minimum-window-with-characters.md b/articles/minimum-window-with-characters.md new file mode 100644 index 000000000..887cb769c --- /dev/null +++ b/articles/minimum-window-with-characters.md @@ -0,0 +1,707 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def minWindow(self, s: str, t: str) -> str: + if t == "": + return "" + + countT = {} + for c in t: + countT[c] = 1 + countT.get(c, 0) + + res, resLen = [-1, -1], float("infinity") + for i in range(len(s)): + countS = {} + for j in range(i, len(s)): + countS[s[j]] = 1 + countS.get(s[j], 0) + + flag = True + for c in countT: + if countT[c] > countS.get(c, 0): + flag = False + break + + if flag and (j - i + 1) < resLen: + resLen = j - i + 1 + res = [i, j] + + l, r = res + return s[l : r + 1] if resLen != float("infinity") else "" +``` + +```java +public class Solution { + public String minWindow(String s, String t) { + if (t.isEmpty()) return ""; + + Map countT = new HashMap<>(); + for (char c : t.toCharArray()) { + countT.put(c, countT.getOrDefault(c, 0) + 1); + } + + int[] res = {-1, -1}; + int resLen = Integer.MAX_VALUE; + + for (int i = 0; i < s.length(); i++) { + Map countS = new HashMap<>(); + for (int j = i; j < s.length(); j++) { + countS.put(s.charAt(j), countS.getOrDefault(s.charAt(j), 0) + 1); + + boolean flag = true; + for (char c : countT.keySet()) { + if (countS.getOrDefault(c, 0) < countT.get(c)) { + flag = false; + break; + } + } + + if (flag && (j - i + 1) < resLen) { + resLen = j - i + 1; + res[0] = i; + res[1] = j; + } + } + } + + return resLen == Integer.MAX_VALUE ? "" : s.substring(res[0], res[1] + 1); + } +} +``` + +```cpp +class Solution { +public: + string minWindow(string s, string t) { + if (t.empty()) return ""; + + unordered_map countT; + for (char c : t) { + countT[c]++; + } + + pair res = {-1, -1}; + int resLen = INT_MAX; + + for (int i = 0; i < s.length(); i++) { + unordered_map countS; + for (int j = i; j < s.length(); j++) { + countS[s[j]]++; + + bool flag = true; + for (auto &[c, cnt] : countT) { + if (countS[c] < cnt) { + flag = false; + break; + } + } + + if (flag && (j - i + 1) < resLen) { + resLen = j - i + 1; + res = {i, j}; + } + } + } + + return resLen == INT_MAX ? "" : s.substr(res.first, resLen); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {string} + */ + minWindow(s, t) { + if (t === "") return ""; + + let countT = {}; + for (let c of t) { + countT[c] = (countT[c] || 0) + 1; + } + + let res = [-1, -1]; + let resLen = Infinity; + + for (let i = 0; i < s.length; i++) { + let countS = {}; + for (let j = i; j < s.length; j++) { + countS[s[j]] = (countS[s[j]] || 0) + 1; + + let flag = true; + for (let c in countT) { + if ((countS[c] || 0) < countT[c]) { + flag = false; + break; + } + } + + if (flag && (j - i + 1) < resLen) { + resLen = j - i + 1; + res = [i, j]; + } + } + } + + return resLen === Infinity ? "" : s.slice(res[0], res[1] + 1); + } +} +``` + +```csharp +public class Solution { + public string MinWindow(string s, string t) { + if (t == "") return ""; + + Dictionary countT = new Dictionary(); + foreach (char c in t) { + if (countT.ContainsKey(c)) { + countT[c]++; + } else { + countT[c] = 1; + } + } + + int[] res = { -1, -1 }; + int resLen = int.MaxValue; + + for (int i = 0; i < s.Length; i++) { + Dictionary countS = new Dictionary(); + for (int j = i; j < s.Length; j++) { + if (countS.ContainsKey(s[j])) { + countS[s[j]]++; + } else { + countS[s[j]] = 1; + } + + bool flag = true; + foreach (var c in countT.Keys) { + if (!countS.ContainsKey(c) || countS[c] < countT[c]) { + flag = false; + break; + } + } + + if (flag && (j - i + 1) < resLen) { + resLen = j - i + 1; + res[0] = i; + res[1] = j; + } + } + } + + return resLen == int.MaxValue ? "" : s.Substring(res[0], resLen); + } +} +``` + +```go +func minWindow(s string, t string) string { + if t == "" { + return "" + } + + countT := make(map[rune]int) + for _, c := range t { + countT[c]++ + } + + res := []int{-1, -1} + resLen := int(^uint(0) >> 1) + for i := 0; i < len(s); i++ { + countS := make(map[rune]int) + for j := i; j < len(s); j++ { + countS[rune(s[j])]++ + + flag := true + for c, cnt := range countT { + if cnt > countS[c] { + flag = false + break + } + } + + if flag && (j-i+1) < resLen { + resLen = j - i + 1 + res = []int{i, j} + } + } + } + + if res[0] == -1 { + return "" + } + return s[res[0]:res[1]+1] +} +``` + +```kotlin +class Solution { + fun minWindow(s: String, t: String): String { + if (t.isEmpty()) return "" + + val countT = HashMap() + for (c in t) { + countT[c] = countT.getOrDefault(c, 0) + 1 + } + + var res = IntArray(2) {-1} + var resLen = Int.MAX_VALUE + + for (i in s.indices) { + val countS = HashMap() + for (j in i until s.length) { + countS[s[j]] = countS.getOrDefault(s[j], 0) + 1 + + var flag = true + for (c in countT.keys) { + if (countT[c]!! > countS.getOrDefault(c, 0)) { + flag = false + break + } + } + + if (flag && (j - i + 1) < resLen) { + resLen = j - i + 1 + res[0] = i + res[1] = j + } + } + } + + return if (res[0] == -1) "" else s.substring(res[0], res[1] + 1) + } +} +``` + +```swift +class Solution { + func minWindow(_ s: String, _ t: String) -> String { + if t.isEmpty { + return "" + } + + var countT = [Character: Int]() + for c in t { + countT[c, default: 0] += 1 + } + + var res = [-1, -1] + var resLen = Int.max + let chars = Array(s) + + for i in 0.. countS[c, default: 0] { + flag = false + break + } + } + + if flag && (j - i + 1) < resLen { + resLen = j - i + 1 + res = [i, j] + } + } + } + + let (l, r) = (res[0], res[1]) + return resLen != Int.max ? String(chars[l...r]) : "" + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the total number of unique characters in the strings $t$ and $s$. + +--- + +## 2. Sliding Window + +::tabs-start + +```python +class Solution: + def minWindow(self, s: str, t: str) -> str: + if t == "": + return "" + + countT, window = {}, {} + for c in t: + countT[c] = 1 + countT.get(c, 0) + + have, need = 0, len(countT) + res, resLen = [-1, -1], float("infinity") + l = 0 + for r in range(len(s)): + c = s[r] + window[c] = 1 + window.get(c, 0) + + if c in countT and window[c] == countT[c]: + have += 1 + + while have == need: + if (r - l + 1) < resLen: + res = [l, r] + resLen = r - l + 1 + + window[s[l]] -= 1 + if s[l] in countT and window[s[l]] < countT[s[l]]: + have -= 1 + l += 1 + l, r = res + return s[l : r + 1] if resLen != float("infinity") else "" +``` + +```java +public class Solution { + public String minWindow(String s, String t) { + if (t.isEmpty()) return ""; + + Map countT = new HashMap<>(); + Map window = new HashMap<>(); + for (char c : t.toCharArray()) { + countT.put(c, countT.getOrDefault(c, 0) + 1); + } + + int have = 0, need = countT.size(); + int[] res = {-1, -1}; + int resLen = Integer.MAX_VALUE; + int l = 0; + + for (int r = 0; r < s.length(); r++) { + char c = s.charAt(r); + window.put(c, window.getOrDefault(c, 0) + 1); + + if (countT.containsKey(c) && window.get(c).equals(countT.get(c))) { + have++; + } + + while (have == need) { + if ((r - l + 1) < resLen) { + resLen = r - l + 1; + res[0] = l; + res[1] = r; + } + + char leftChar = s.charAt(l); + window.put(leftChar, window.get(leftChar) - 1); + if (countT.containsKey(leftChar) && window.get(leftChar) < countT.get(leftChar)) { + have--; + } + l++; + } + } + + return resLen == Integer.MAX_VALUE ? "" : s.substring(res[0], res[1] + 1); + } +} +``` + +```cpp +class Solution { +public: + string minWindow(string s, string t) { + if (t.empty()) return ""; + + unordered_map countT, window; + for (char c : t) { + countT[c]++; + } + + int have = 0, need = countT.size(); + pair res = {-1, -1}; + int resLen = INT_MAX; + int l = 0; + + for (int r = 0; r < s.length(); r++) { + char c = s[r]; + window[c]++; + + if (countT.count(c) && window[c] == countT[c]) { + have++; + } + + while (have == need) { + if ((r - l + 1) < resLen) { + resLen = r - l + 1; + res = {l, r}; + } + + window[s[l]]--; + if (countT.count(s[l]) && window[s[l]] < countT[s[l]]) { + have--; + } + l++; + } + } + + return resLen == INT_MAX ? "" : s.substr(res.first, resLen); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} t + * @return {string} + */ + minWindow(s, t) { + if (t === "") return ""; + + let countT = {}; + let window = {}; + for (let c of t) { + countT[c] = (countT[c] || 0) + 1; + } + + let have = 0, need = Object.keys(countT).length; + let res = [-1, -1]; + let resLen = Infinity; + let l = 0; + + for (let r = 0; r < s.length; r++) { + let c = s[r]; + window[c] = (window[c] || 0) + 1; + + if (countT[c] && window[c] === countT[c]) { + have++; + } + + while (have === need) { + if ((r - l + 1) < resLen) { + resLen = r - l + 1; + res = [l, r]; + } + + window[s[l]]--; + if (countT[s[l]] && window[s[l]] < countT[s[l]]) { + have--; + } + l++; + } + } + + return resLen === Infinity ? "" : s.slice(res[0], res[1] + 1); + } +} +``` + +```csharp +public class Solution { + public string MinWindow(string s, string t) { + if (t == "") return ""; + + Dictionary countT = new Dictionary(); + Dictionary window = new Dictionary(); + + foreach (char c in t) { + if (countT.ContainsKey(c)) { + countT[c]++; + } else { + countT[c] = 1; + } + } + + int have = 0, need = countT.Count; + int[] res = { -1, -1 }; + int resLen = int.MaxValue; + int l = 0; + + for (int r = 0; r < s.Length; r++) { + char c = s[r]; + if (window.ContainsKey(c)) { + window[c]++; + } else { + window[c] = 1; + } + + if (countT.ContainsKey(c) && window[c] == countT[c]) { + have++; + } + + while (have == need) { + if ((r - l + 1) < resLen) { + resLen = r - l + 1; + res[0] = l; + res[1] = r; + } + + char leftChar = s[l]; + window[leftChar]--; + if (countT.ContainsKey(leftChar) && window[leftChar] < countT[leftChar]) { + have--; + } + l++; + } + } + + return resLen == int.MaxValue ? "" : s.Substring(res[0], resLen); + } +} +``` + +```go +func minWindow(s string, t string) string { + if t == "" { + return "" + } + + countT := make(map[rune]int) + for _, c := range t { + countT[c]++ + } + + have, need := 0, len(countT) + res := []int{-1, -1} + resLen := math.MaxInt32 + l := 0 + window := make(map[rune]int) + + for r := 0; r < len(s); r++ { + c := rune(s[r]) + window[c]++ + + if countT[c] > 0 && window[c] == countT[c] { + have++ + } + + for have == need { + if (r - l + 1) < resLen { + res = []int{l, r} + resLen = r - l + 1 + } + + window[rune(s[l])]-- + if countT[rune(s[l])] > 0 && window[rune(s[l])] < countT[rune(s[l])] { + have-- + } + l++ + } + } + + if res[0] == -1 { + return "" + } + return s[res[0]:res[1]+1] +} +``` + +```kotlin +class Solution { + fun minWindow(s: String, t: String): String { + if (t.isEmpty()) return "" + + val countT = HashMap() + for (c in t) { + countT[c] = countT.getOrDefault(c, 0) + 1 + } + + var have = 0 + val need = countT.size + val res = IntArray(2) {-1} + var resLen = Int.MAX_VALUE + var l = 0 + val window = HashMap() + + for (r in s.indices) { + val c = s[r] + window[c] = window.getOrDefault(c, 0) + 1 + + if (countT.containsKey(c) && window[c] == countT[c]) { + have++ + } + + while (have == need) { + if ((r - l + 1) < resLen) { + res[0] = l + res[1] = r + resLen = r - l + 1 + } + + window[s[l]] = window.getOrDefault(s[l], 0) - 1 + if (countT.containsKey(s[l]) && (window[s[l]] ?: 0) < countT[s[l]]!!) { + have-- + } + l++ + } + } + + return if (res[0] == -1) "" else s.substring(res[0], res[1] + 1) + } +} +``` + +```swift +class Solution { + func minWindow(_ s: String, _ t: String) -> String { + if t.isEmpty { + return "" + } + + var countT = [Character: Int]() + var window = [Character: Int]() + for c in t { + countT[c, default: 0] += 1 + } + + var have = 0, need = countT.count + var res = [-1, -1], resLen = Int.max + let chars = Array(s) + var l = 0 + + for r in 0.. Where $n$ is the length of the string $s$ and $m$ is the total number of unique characters in the strings $t$ and $s$. \ No newline at end of file diff --git a/articles/missing-number.md b/articles/missing-number.md new file mode 100644 index 000000000..24844c099 --- /dev/null +++ b/articles/missing-number.md @@ -0,0 +1,500 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def missingNumber(self, nums: List[int]) -> int: + n = len(nums) + nums.sort() + for i in range(n): + if nums[i] != i: + return i + return n +``` + +```java +public class Solution { + public int missingNumber(int[] nums) { + int n = nums.length; + Arrays.sort(nums); + for (int i = 0; i < n; i++) { + if (nums[i] != i) { + return i; + } + } + return n; + } +} +``` + +```cpp +class Solution { +public: + int missingNumber(vector& nums) { + int n = nums.size(); + sort(nums.begin(), nums.end()); + for (int i = 0; i < n; i++) { + if (nums[i] != i) { + return i; + } + } + return n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + missingNumber(nums) { + let n = nums.length; + nums.sort((a, b) => a - b); + for (let i = 0; i < n; i++) { + if (nums[i] !== i) { + return i; + } + } + return n; + } +} +``` + +```csharp +public class Solution { + public int MissingNumber(int[] nums) { + int n = nums.Length; + Array.Sort(nums); + for (int i = 0; i < n; i++) { + if (nums[i] != i) { + return i; + } + } + return n; + } +} +``` + +```go +func missingNumber(nums []int) int { + n := len(nums) + sort.Ints(nums) + for i := 0; i < n; i++ { + if nums[i] != i { + return i + } + } + return n +} +``` + +```kotlin +class Solution { + fun missingNumber(nums: IntArray): Int { + val n = nums.size + nums.sort() + for (i in 0 until n) { + if (nums[i] != i) { + return i + } + } + return n + } +} +``` + +```swift +class Solution { + func missingNumber(_ nums: [Int]) -> Int { + var nums = nums.sorted() + let n = nums.count + for i in 0.. int: + num_set = set(nums) + n = len(nums) + for i in range(n + 1): + if i not in num_set: + return i +``` + +```java +public class Solution { + public int missingNumber(int[] nums) { + Set numSet = new HashSet<>(); + for (int num : nums) { + numSet.add(num); + } + int n = nums.length; + for (int i = 0; i <= n; i++) { + if (!numSet.contains(i)) { + return i; + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int missingNumber(vector& nums) { + unordered_set num_set(nums.begin(), nums.end()); + int n = nums.size(); + for (int i = 0; i <= n; i++) { + if (num_set.find(i) == num_set.end()) { + return i; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + missingNumber(nums) { + const numSet = new Set(nums); + const n = nums.length; + for (let i = 0; i <= n; i++) { + if (!numSet.has(i)) { + return i; + } + } + } +} +``` + +```csharp +public class Solution { + public int MissingNumber(int[] nums) { + HashSet numSet = new HashSet(nums); + int n = nums.Length; + for (int i = 0; i <= n; i++) { + if (!numSet.Contains(i)) { + return i; + } + } + return -1; + } +} +``` + +```go +func missingNumber(nums []int) int { + numSet := make(map[int]struct{}) + for _, num := range nums { + numSet[num] = struct{}{} + } + n := len(nums) + for i := 0; i <= n; i++ { + if _, exists := numSet[i]; !exists { + return i + } + } + return -1 +} +``` + +```kotlin +class Solution { + fun missingNumber(nums: IntArray): Int { + val numSet = nums.toSet() + val n = nums.size + for (i in 0..n) { + if (i !in numSet) { + return i + } + } + return -1 + } +} +``` + +```swift +class Solution { + func missingNumber(_ nums: [Int]) -> Int { + let numSet = Set(nums) + let n = nums.count + for i in 0...n { + if !numSet.contains(i) { + return i + } + } + return -1 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Bitwise XOR + +::tabs-start + +```python +class Solution: + def missingNumber(self, nums: List[int]) -> int: + n = len(nums) + xorr = n + for i in range(n): + xorr ^= i ^ nums[i] + return xorr +``` + +```java +public class Solution { + public int missingNumber(int[] nums) { + int n = nums.length; + int xorr = n; + for (int i = 0; i < n; i++) { + xorr ^= i ^ nums[i]; + } + return xorr; + } +} +``` + +```cpp +class Solution { +public: + int missingNumber(vector& nums) { + int n = nums.size(); + int xorr = n; + for (int i = 0; i < n; i++) { + xorr ^= i ^ nums[i]; + } + return xorr; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + missingNumber(nums) { + let n = nums.length; + let xorr = n; + for (let i = 0; i < n; i++) { + xorr ^= i ^ nums[i]; + } + return xorr; + } +} +``` + +```csharp +public class Solution { + public int MissingNumber(int[] nums) { + int n = nums.Length; + int xorr = n; + for (int i = 0; i < n; i++) { + xorr ^= i ^ nums[i]; + } + return xorr; + } +} +``` + +```go +func missingNumber(nums []int) int { + n := len(nums) + xorr := n + for i := 0; i < n; i++ { + xorr ^= i ^ nums[i] + } + return xorr +} +``` + +```kotlin +class Solution { + fun missingNumber(nums: IntArray): Int { + val n = nums.size + var xorr = n + for (i in 0 until n) { + xorr = xorr xor i xor nums[i] + } + return xorr + } +} +``` + +```swift +class Solution { + func missingNumber(_ nums: [Int]) -> Int { + let n = nums.count + var xorr = n + + for i in 0.. int: + res = len(nums) + + for i in range(len(nums)): + res += i - nums[i] + return res +``` + +```java +public class Solution { + public int missingNumber(int[] nums) { + int res = nums.length; + + for (int i = 0; i < nums.length; i++) { + res += i - nums[i]; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int missingNumber(vector& nums) { + int res = nums.size(); + + for (int i = 0; i < nums.size(); i++) { + res += i - nums[i]; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + missingNumber(nums) { + let res = nums.length; + + for (let i = 0; i < nums.length; i++) { + res += i - nums[i]; + } + return res; + } +} +``` + +```csharp +public class Solution { + public int MissingNumber(int[] nums) { + int res = nums.Length; + + for (int i = 0; i < nums.Length; i++) { + res += i - nums[i]; + } + return res; + } +} +``` + +```go +func missingNumber(nums []int) int { + res := len(nums) + for i := 0; i < len(nums); i++ { + res += i - nums[i] + } + return res +} +``` + +```kotlin +class Solution { + fun missingNumber(nums: IntArray): Int { + var res = nums.size + for (i in nums.indices) { + res += i - nums[i] + } + return res + } +} +``` + +```swift +class Solution { + func missingNumber(_ nums: [Int]) -> Int { + var res = nums.count + + for i in 0.. bool: + n = len(nums) + increase = True + for i in range(1, n): + if nums[i] < nums[i - 1]: + increase = False + break + + if increase: + return True + + decrease = True + for i in range(1, n): + if nums[i] > nums[i - 1]: + decrease = False + break + return decrease +``` + +```java +public class Solution { + public boolean isMonotonic(int[] nums) { + int n = nums.length; + boolean increase = true; + for (int i = 1; i < n; i++) { + if (nums[i] < nums[i - 1]) { + increase = false; + break; + } + } + if (increase) { + return true; + } + + boolean decrease = true; + for (int i = 1; i < n; i++) { + if (nums[i] > nums[i - 1]) { + decrease = false; + break; + } + } + return decrease; + } +} +``` + +```cpp +class Solution { +public: + bool isMonotonic(vector& nums) { + int n = nums.size(); + bool increase = true; + for (int i = 1; i < n; ++i) { + if (nums[i] < nums[i - 1]) { + increase = false; + break; + } + } + if (increase) { + return true; + } + + bool decrease = true; + for (int i = 1; i < n; ++i) { + if (nums[i] > nums[i - 1]) { + decrease = false; + break; + } + } + return decrease; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + isMonotonic(nums) { + const n = nums.length; + let increase = true; + for (let i = 1; i < n; i++) { + if (nums[i] < nums[i - 1]) { + increase = false; + break; + } + } + if (increase) { + return true; + } + + let decrease = true; + for (let i = 1; i < n; i++) { + if (nums[i] > nums[i - 1]) { + decrease = false; + break; + } + } + return decrease; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. One Pass - I + +::tabs-start + +```python +class Solution: + def isMonotonic(self, nums: List[int]) -> bool: + n = len(nums) + if nums[0] <= nums[-1]: + for i in range(1, n): + if nums[i] < nums[i - 1]: + return False + return True + else: + for i in range(1, n): + if nums[i] > nums[i - 1]: + return False + return True +``` + +```java +public class Solution { + public boolean isMonotonic(int[] nums) { + int n = nums.length; + if (nums[0] <= nums[n - 1]) { + for (int i = 1; i < n; i++) { + if (nums[i] < nums[i - 1]) { + return false; + } + } + return true; + } else { + for (int i = 1; i < n; i++) { + if (nums[i] > nums[i - 1]) { + return false; + } + } + return true; + } + } +} +``` + +```cpp +class Solution { +public: + bool isMonotonic(vector& nums) { + int n = nums.size(); + if (nums[0] <= nums[n - 1]) { + for (int i = 1; i < n; ++i) { + if (nums[i] < nums[i - 1]) { + return false; + } + } + return true; + } else { + for (int i = 1; i < n; ++i) { + if (nums[i] > nums[i - 1]) { + return false; + } + } + return true; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + isMonotonic(nums) { + const n = nums.length; + if (nums[0] <= nums[n - 1]) { + for (let i = 1; i < n; i++) { + if (nums[i] < nums[i - 1]) { + return false; + } + } + return true; + } else { + for (let i = 1; i < n; i++) { + if (nums[i] > nums[i - 1]) { + return false; + } + } + return true; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. One Pass - II + +::tabs-start + +```python +class Solution: + def isMonotonic(self, nums: List[int]) -> bool: + increase, decrease = True, True + + for i in range(len(nums) - 1): + if not (nums[i] <= nums[i + 1]): + increase = False + if not (nums[i] >= nums[i + 1]): + decrease = False + return increase or decrease +``` + +```java +public class Solution { + public boolean isMonotonic(int[] nums) { + boolean increase = true, decrease = true; + + for (int i = 0; i < nums.length - 1; i++) { + if (!(nums[i] <= nums[i + 1])) { + increase = false; + } + if (!(nums[i] >= nums[i + 1])) { + decrease = false; + } + } + return increase || decrease; + } +} +``` + +```cpp +class Solution { +public: + bool isMonotonic(vector& nums) { + bool increase = true, decrease = true; + + for (int i = 0; i < nums.size() - 1; ++i) { + if (!(nums[i] <= nums[i + 1])) { + increase = false; + } + if (!(nums[i] >= nums[i + 1])) { + decrease = false; + } + } + return increase || decrease; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + isMonotonic(nums) { + let increase = true, decrease = true; + + for (let i = 0; i < nums.length - 1; i++) { + if (!(nums[i] <= nums[i + 1])) { + increase = false; + } + if (!(nums[i] >= nums[i + 1])) { + decrease = false; + } + } + return increase || decrease; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/move-zeroes.md b/articles/move-zeroes.md new file mode 100644 index 000000000..46a78c170 --- /dev/null +++ b/articles/move-zeroes.md @@ -0,0 +1,253 @@ +## 1. Extra Space + +::tabs-start + +```python +class Solution: + def moveZeroes(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + tmp = [] + for num in nums: + if num != 0: + tmp.append(num) + + for i in range(len(nums)): + if i < len(tmp): + nums[i] = tmp[i] + else: + nums[i] = 0 +``` + +```java +public class Solution { + public void moveZeroes(int[] nums) { + List tmp = new ArrayList<>(); + for (int num : nums) { + if (num != 0) { + tmp.add(num); + } + } + + for (int i = 0; i < nums.length; i++) { + if (i < tmp.size()) { + nums[i] = tmp.get(i); + } else { + nums[i] = 0; + } + } + } +} +``` + +```cpp +class Solution { +public: + void moveZeroes(vector& nums) { + vector tmp; + for (int num : nums) { + if (num != 0) { + tmp.push_back(num); + } + } + + for (int i = 0; i < nums.size(); ++i) { + if (i < tmp.size()) { + nums[i] = tmp[i]; + } else { + nums[i] = 0; + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ + moveZeroes(nums) { + let tmp = []; + for (let num of nums) { + if (num !== 0) { + tmp.push(num); + } + } + + for (let i = 0; i < nums.length; i++) { + if (i < tmp.length) { + nums[i] = tmp[i]; + } else { + nums[i] = 0; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Two Pointers (Two Pass) +::tabs-start + +```python +class Solution: + def moveZeroes(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + l = 0 + for r in range(len(nums)): + if nums[r] != 0: + nums[l] = nums[r] + l += 1 + + while l < len(nums): + nums[l] = 0 + l += 1 +``` + +```java +public class Solution { + public void moveZeroes(int[] nums) { + int l = 0; + for (int r = 0; r < nums.length; r++) { + if (nums[r] != 0) { + nums[l++] = nums[r]; + } + } + + while (l < nums.length) { + nums[l++] = 0; + } + } +} +``` + +```cpp +class Solution { +public: + void moveZeroes(vector& nums) { + int l = 0; + for (int r = 0; r < nums.size(); r++) { + if (nums[r] != 0) { + nums[l++] = nums[r]; + } + } + + while (l < nums.size()) { + nums[l++] = 0; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ + moveZeroes(nums) { + let l = 0; + for (let r = 0; r < nums.length; r++) { + if (nums[r] != 0) { + nums[l++] = nums[r]; + } + } + + while (l < nums.length) { + nums[l++] = 0; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Two Pointers (One Pass) + +::tabs-start + +```python +class Solution: + def moveZeroes(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + l = 0 + for r in range(len(nums)): + if nums[r]: + nums[l], nums[r] = nums[r], nums[l] + l += 1 +``` + +```java +public class Solution { + public void moveZeroes(int[] nums) { + int l = 0; + for (int r = 0; r < nums.length; r++) { + if (nums[r] != 0) { + int temp = nums[l]; + nums[l] = nums[r]; + nums[r] = temp; + l++; + } + } + } +} +``` + +```cpp +class Solution { +public: + void moveZeroes(vector& nums) { + for (int l = 0, r = 0; r < nums.size(); r++) { + if (nums[r]) { + swap(nums[l++], nums[r]); + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ + moveZeroes(nums) { + for (let l = 0, r = 0; r < nums.length; r++) { + if (nums[r] !== 0) { + [nums[l], nums[r]] = [nums[r], nums[l]]; + l++; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/multiply-strings.md b/articles/multiply-strings.md new file mode 100644 index 000000000..eb53364f1 --- /dev/null +++ b/articles/multiply-strings.md @@ -0,0 +1,774 @@ +## 1. Multiplication & Addition + +::tabs-start + +```python +class Solution: + def multiply(self, num1: str, num2: str) -> str: + if num1 == "0" or num2 == "0": + return "0" + + if len(num1) < len(num2): + return self.multiply(num2, num1) + + res, zero = "", 0 + for i in range(len(num2) - 1, -1, -1): + cur = self.mul(num1, num2[i], zero) + res = self.add(res, cur) + zero += 1 + + return res + + def mul(self, s: str, d: str, zero: int) -> str: + i, carry = len(s) - 1, 0 + d = int(d) + cur = [] + + while i >= 0 or carry: + n = int(s[i]) if i >= 0 else 0 + prod = n * d + carry + cur.append(str(prod % 10)) + carry = prod // 10 + i -= 1 + + return ''.join(cur[::-1]) + '0' * zero + + def add(self, num1: str, num2: str) -> str: + i, j, carry = len(num1) - 1, len(num2) - 1, 0 + res = [] + + while i >= 0 or j >= 0 or carry: + n1 = int(num1[i]) if i >= 0 else 0 + n2 = int(num2[j]) if j >= 0 else 0 + total = n1 + n2 + carry + res.append(str(total % 10)) + carry = total // 10 + i -= 1 + j -= 1 + + return ''.join(res[::-1]) +``` + +```java +public class Solution { + public String multiply(String num1, String num2) { + if (num1.equals("0") || num2.equals("0")) return "0"; + + if (num1.length() < num2.length()) { + return multiply(num2, num1); + } + + String res = ""; + int zero = 0; + for (int i = num2.length() - 1; i >= 0; i--) { + String cur = mul(num1, num2.charAt(i), zero); + res = add(res, cur); + zero++; + } + + return res; + } + + private String mul(String s, char d, int zero) { + int i = s.length() - 1, carry = 0; + int digit = d - '0'; + StringBuilder cur = new StringBuilder(); + + while (i >= 0 || carry > 0) { + int n = (i >= 0) ? s.charAt(i) - '0' : 0; + int prod = n * digit + carry; + cur.append(prod % 10); + carry = prod / 10; + i--; + } + + return cur.reverse().toString() + "0".repeat(zero); + } + + private String add(String num1, String num2) { + int i = num1.length() - 1, j = num2.length() - 1, carry = 0; + StringBuilder res = new StringBuilder(); + + while (i >= 0 || j >= 0 || carry > 0) { + int n1 = (i >= 0) ? num1.charAt(i) - '0' : 0; + int n2 = (j >= 0) ? num2.charAt(j) - '0' : 0; + int total = n1 + n2 + carry; + res.append(total % 10); + carry = total / 10; + i--; + j--; + } + + return res.reverse().toString(); + } +} +``` + +```cpp +class Solution { +public: + string multiply(string num1, string num2) { + if (num1 == "0" || num2 == "0") return "0"; + + if (num1.size() < num2.size()) { + return multiply(num2, num1); + } + + string res = ""; + int zero = 0; + for (int i = num2.size() - 1; i >= 0; --i) { + string cur = mul(num1, num2[i], zero); + res = add(res, cur); + zero++; + } + + return res; + } + + string mul(string s, char d, int zero) { + int i = s.size() - 1, carry = 0; + int digit = d - '0'; + string cur; + + while (i >= 0 || carry) { + int n = (i >= 0) ? s[i] - '0' : 0; + int prod = n * digit + carry; + cur.push_back((prod % 10) + '0'); + carry = prod / 10; + i--; + } + + reverse(cur.begin(), cur.end()); + return cur + string(zero, '0'); + } + + string add(string num1, string num2) { + int i = num1.size() - 1, j = num2.size() - 1, carry = 0; + string res; + + while (i >= 0 || j >= 0 || carry) { + int n1 = (i >= 0) ? num1[i] - '0' : 0; + int n2 = (j >= 0) ? num2[j] - '0' : 0; + int total = n1 + n2 + carry; + res.push_back((total % 10) + '0'); + carry = total / 10; + i--; + j--; + } + + reverse(res.begin(), res.end()); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} num1 + * @param {string} num2 + * @return {string} + */ + multiply(num1, num2) { + if (num1 === "0" || num2 === "0") return "0"; + + if (num1.length < num2.length) { + return this.multiply(num2, num1); + } + + let res = ""; + let zero = 0; + for (let i = num2.length - 1; i >= 0; i--) { + const cur = this.mul(num1, num2[i], zero); + res = this.add(res, cur); + zero++; + } + + return res; + } + + /** + * @param {string} s + * @param {Character} d + * @param {number} zero + * @return {string} + */ + mul(s, d, zero) { + let i = s.length - 1; + let carry = 0; + const digit = Number(d); + let cur = ""; + + while (i >= 0 || carry) { + const n = i >= 0 ? Number(s[i]) : 0; + const prod = n * digit + carry; + cur = (prod % 10) + cur; + carry = Math.floor(prod / 10); + i--; + } + + return cur + "0".repeat(zero); + } + + /** + * @param {string} num1 + * @param {string} num2 + * @return {string} + */ + add(num1, num2) { + let i = num1.length - 1, j = num2.length - 1, carry = 0; + let res = ""; + + while (i >= 0 || j >= 0 || carry) { + const n1 = i >= 0 ? Number(num1[i]) : 0; + const n2 = j >= 0 ? Number(num2[j]) : 0; + const total = n1 + n2 + carry; + res = (total % 10) + res; + carry = Math.floor(total / 10); + i--; + j--; + } + + return res; + } +} +``` + +```csharp +public class Solution { + public string Multiply(string num1, string num2) { + if (num1 == "0" || num2 == "0") return "0"; + + if (num1.Length < num2.Length) { + return Multiply(num2, num1); + } + + string res = ""; + int zero = 0; + for (int i = num2.Length - 1; i >= 0; i--) { + string cur = Mul(num1, num2[i], zero); + res = Add(res, cur); + zero++; + } + + return res; + } + + private string Mul(string s, char d, int zero) { + int i = s.Length - 1, carry = 0; + int digit = d - '0'; + var cur = new List(); + + while (i >= 0 || carry > 0) { + int n = (i >= 0) ? s[i] - '0' : 0; + int prod = n * digit + carry; + cur.Add((char)('0' + (prod % 10))); + carry = prod / 10; + i--; + } + + cur.Reverse(); + return new string(cur.ToArray()) + new string('0', zero); + } + + private string Add(string num1, string num2) { + int i = num1.Length - 1, j = num2.Length - 1, carry = 0; + var res = new List(); + + while (i >= 0 || j >= 0 || carry > 0) { + int n1 = (i >= 0) ? num1[i] - '0' : 0; + int n2 = (j >= 0) ? num2[j] - '0' : 0; + int total = n1 + n2 + carry; + res.Add((char)('0' + (total % 10))); + carry = total / 10; + i--; + j--; + } + + res.Reverse(); + return new string(res.ToArray()); + } +} +``` + +```go +func multiply(num1 string, num2 string) string { + if num1 == "0" || num2 == "0" { + return "0" + } + + if len(num1) < len(num2) { + return multiply(num2, num1) + } + + res, zero := "", 0 + for i := len(num2) - 1; i >= 0; i-- { + cur := mul(num1, num2[i], zero) + res = add(res, cur) + zero++ + } + + return res +} + +func mul(s string, d byte, zero int) string { + i, carry := len(s)-1, 0 + d = d - '0' + cur := make([]byte, 0) + + for i >= 0 || carry > 0 { + var n int + if i >= 0 { + n = int(s[i] - '0') + } + prod := n*int(d) + carry + cur = append(cur, byte(prod%10+'0')) + carry = prod / 10 + i-- + } + + for i := 0; i < len(cur)/2; i++ { + cur[i], cur[len(cur)-1-i] = cur[len(cur)-1-i], cur[i] + } + + result := string(cur) + for i := 0; i < zero; i++ { + result += "0" + } + return result +} + +func add(num1 string, num2 string) string { + i, j, carry := len(num1)-1, len(num2)-1, 0 + res := make([]byte, 0) + + for i >= 0 || j >= 0 || carry > 0 { + var n1, n2 int + if i >= 0 { + n1 = int(num1[i] - '0') + } + if j >= 0 { + n2 = int(num2[j] - '0') + } + total := n1 + n2 + carry + res = append(res, byte(total%10+'0')) + carry = total / 10 + i-- + j-- + } + + for i := 0; i < len(res)/2; i++ { + res[i], res[len(res)-1-i] = res[len(res)-1-i], res[i] + } + + return string(res) +} +``` + +```kotlin +class Solution { + fun multiply(num1: String, num2: String): String { + if (num1 == "0" || num2 == "0") { + return "0" + } + + if (num1.length < num2.length) { + return multiply(num2, num1) + } + + var res = "" + var zero = 0 + for (i in num2.length - 1 downTo 0) { + val cur = mul(num1, num2[i], zero) + res = add(res, cur) + zero++ + } + + return res + } + + private fun mul(s: String, d: Char, zero: Int): String { + var i = s.length - 1 + var carry = 0 + val dInt = d - '0' + val cur = mutableListOf() + + while (i >= 0 || carry > 0) { + val n = if (i >= 0) s[i] - '0' else 0 + val prod = n * dInt + carry + cur.add((prod % 10 + '0'.code).toChar()) + carry = prod / 10 + i-- + } + + return cur.reversed().joinToString("") + "0".repeat(zero) + } + + private fun add(num1: String, num2: String): String { + var i = num1.length - 1 + var j = num2.length - 1 + var carry = 0 + val res = mutableListOf() + + while (i >= 0 || j >= 0 || carry > 0) { + val n1 = if (i >= 0) num1[i] - '0' else 0 + val n2 = if (j >= 0) num2[j] - '0' else 0 + val total = n1 + n2 + carry + res.add((total % 10 + '0'.code).toChar()) + carry = total / 10 + i-- + j-- + } + + return res.reversed().joinToString("") + } +} +``` + +```swift +class Solution { + func multiply(_ num1: String, _ num2: String) -> String { + if num1 == "0" || num2 == "0" { + return "0" + } + if num1.count < num2.count { + return multiply(num2, num1) + } + + var res = "" + var zero = 0 + let num2Arr = Array(num2) + + for i in stride(from: num2Arr.count - 1, through: 0, by: -1) { + let cur = mul(num1, num2Arr[i], zero) + res = add(res, cur) + zero += 1 + } + + return res + } + + func mul(_ s: String, _ d: Character, _ zero: Int) -> String { + var i = s.count - 1 + var carry = 0 + let dInt = Int(String(d))! + let sArr = Array(s) + var cur = [String]() + + while i >= 0 || carry > 0 { + let n = i >= 0 ? Int(String(sArr[i]))! : 0 + let prod = n * dInt + carry + cur.append(String(prod % 10)) + carry = prod / 10 + i -= 1 + } + + let prodStr = cur.reversed().joined() + let zeros = String(repeating: "0", count: zero) + return prodStr + zeros + } + + func add(_ num1: String, _ num2: String) -> String { + let s1 = Array(num1) + let s2 = Array(num2) + var i = s1.count - 1 + var j = s2.count - 1 + var carry = 0 + var res = [String]() + + while i >= 0 || j >= 0 || carry > 0 { + let n1 = i >= 0 ? Int(String(s1[i]))! : 0 + let n2 = j >= 0 ? Int(String(s2[j]))! : 0 + let total = n1 + n2 + carry + res.append(String(total % 10)) + carry = total / 10 + i -= 1 + j -= 1 + } + + return res.reversed().joined() + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(min(m, n) * (m + n))$ +* Space complexity: $O(m + n)$ + +> Where $m$ is the length of the string $num1$ and $n$ is the length of the string $num2$. + +--- + +## 2. Multiplication + +::tabs-start + +```python +class Solution: + def multiply(self, num1: str, num2: str) -> str: + if "0" in [num1, num2]: + return "0" + + res = [0] * (len(num1) + len(num2)) + num1, num2 = num1[::-1], num2[::-1] + for i1 in range(len(num1)): + for i2 in range(len(num2)): + digit = int(num1[i1]) * int(num2[i2]) + res[i1 + i2] += digit + res[i1 + i2 + 1] += res[i1 + i2] // 10 + res[i1 + i2] = res[i1 + i2] % 10 + + res, beg = res[::-1], 0 + while beg < len(res) and res[beg] == 0: + beg += 1 + res = map(str, res[beg:]) + return "".join(res) +``` + +```java +public class Solution { + public String multiply(String num1, String num2) { + if (num1.equals("0") || num2.equals("0")) { + return "0"; + } + + int[] res = new int[num1.length() + num2.length()]; + num1 = new StringBuilder(num1).reverse().toString(); + num2 = new StringBuilder(num2).reverse().toString(); + for (int i1 = 0; i1 < num1.length(); i1++) { + for (int i2 = 0; i2 < num2.length(); i2++) { + int digit = (num1.charAt(i1) - '0') * (num2.charAt(i2) - '0'); + res[i1 + i2] += digit; + res[i1 + i2 + 1] += res[i1 + i2] / 10; + res[i1 + i2] %= 10; + } + } + + StringBuilder result = new StringBuilder(); + int i = res.length - 1; + while (i >= 0 && res[i] == 0) { + i--; + } + while (i >= 0) { + result.append(res[i--]); + } + return result.toString(); + } +} +``` + +```cpp +class Solution { +public: + string multiply(string num1, string num2) { + if (num1 == "0" || num2 == "0") { + return "0"; + } + + vector res(num1.length() + num2.length(), 0); + reverse(num1.begin(), num1.end()); + reverse(num2.begin(), num2.end()); + for (int i1 = 0; i1 < num1.length(); i1++) { + for (int i2 = 0; i2 < num2.length(); i2++) { + int digit = (num1[i1] - '0') * (num2[i2] - '0'); + res[i1 + i2] += digit; + res[i1 + i2 + 1] += res[i1 + i2] / 10; + res[i1 + i2] %= 10; + } + } + + stringstream result; + int i = res.size() - 1; + while (i >= 0 && res[i] == 0) { + i--; + } + while (i >= 0) { + result << res[i--]; + } + return result.str(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} num1 + * @param {string} num2 + * @return {string} + */ + multiply(num1, num2) { + if (num1 === '0' || num2 === '0') { + return '0'; + } + + const res = new Array(num1.length + num2.length).fill(0); + num1 = num1.split('').reverse().join(''); + num2 = num2.split('').reverse().join(''); + for (let i1 = 0; i1 < num1.length; i1++) { + for (let i2 = 0; i2 < num2.length; i2++) { + const digit = parseInt(num1[i1]) * parseInt(num2[i2]); + res[i1 + i2] += digit; + res[i1 + i2 + 1] += Math.floor(res[i1 + i2] / 10); + res[i1 + i2] %= 10; + } + } + + let result = ''; + let i = res.length - 1; + while (i >= 0 && res[i] === 0) { + i--; + } + while (i >= 0) { + result += res[i--]; + } + return result; + } +} +``` + +```csharp +public class Solution { + public string Multiply(string num1, string num2) { + if (new string[] { num1, num2 }.Contains("0")) { + return "0"; + } + + int[] res = new int[num1.Length + num2.Length]; + num1 = new string(num1.Reverse().ToArray()); + num2 = new string(num2.Reverse().ToArray()); + for (int i1 = 0; i1 < num1.Length; i1++) { + for (int i2 = 0; i2 < num2.Length; i2++) { + int digit = (num1[i1] - '0') * (num2[i2] - '0'); + res[i1 + i2] += digit; + res[i1 + i2 + 1] += res[i1 + i2] / 10; + res[i1 + i2] %= 10; + } + } + + Array.Reverse(res); + int beg = 0; + while (beg < res.Length && res[beg] == 0) { + beg++; + } + + string[] result = res.Skip(beg).Select(x => x.ToString()).ToArray(); + return string.Join("", result); + } +} +``` + +```go +func multiply(num1 string, num2 string) string { + if num1 == "0" || num2 == "0" { + return "0" + } + + res := make([]int, len(num1)+len(num2)) + for i1 := len(num1) - 1; i1 >= 0; i1-- { + for i2 := len(num2) - 1; i2 >= 0; i2-- { + pos := len(num1) - 1 - i1 + len(num2) - 1 - i2 + digit := int(num1[i1]-'0') * int(num2[i2]-'0') + + res[pos] += digit + res[pos+1] += res[pos] / 10 + res[pos] = res[pos] % 10 + } + } + + var result strings.Builder + start := len(res) - 1 + for start >= 0 && res[start] == 0 { + start-- + } + if start < 0 { + return "0" + } + + for i := start; i >= 0; i-- { + result.WriteString(strconv.Itoa(res[i])) + } + + return result.String() +} +``` + +```kotlin +class Solution { + fun multiply(num1: String, num2: String): String { + if ("0" in listOf(num1, num2)) { + return "0" + } + + val res = IntArray(num1.length + num2.length) + for (i1 in num1.indices.reversed()) { + for (i2 in num2.indices.reversed()) { + val pos = (num1.length - 1 - i1) + (num2.length - 1 - i2) + val digit = (num1[i1] - '0') * (num2[i2] - '0') + + res[pos] += digit + res[pos + 1] += res[pos] / 10 + res[pos] = res[pos] % 10 + } + } + + var start = res.size - 1 + while (start >= 0 && res[start] == 0) { + start-- + } + + if (start < 0) { + return "0" + } + + return buildString { + for (i in start downTo 0) { + append(res[i]) + } + } + } +} +``` + +```swift +class Solution { + func multiply(_ num1: String, _ num2: String) -> String { + if num1 == "0" || num2 == "0" { + return "0" + } + + let n1 = Array(num1.reversed()).map { Int(String($0))! } + let n2 = Array(num2.reversed()).map { Int(String($0))! } + var res = [Int](repeating: 0, count: num1.count + num2.count) + + for i1 in 0.. Where $m$ is the length of the string $num1$ and $n$ is the length of the string $num2$. \ No newline at end of file diff --git a/articles/my-calendar-i.md b/articles/my-calendar-i.md new file mode 100644 index 000000000..f926c9e13 --- /dev/null +++ b/articles/my-calendar-i.md @@ -0,0 +1,405 @@ +## 1. Iteration + +::tabs-start + +```python +class MyCalendar: + + def __init__(self): + self.events = [] + + def book(self, startTime: int, endTime: int) -> bool: + for start, end in self.events: + if startTime < end and start < endTime: + return False + + self.events.append((startTime, endTime)) + return True +``` + +```java +public class MyCalendar { + private List events; + + public MyCalendar() { + events = new ArrayList<>(); + } + + public boolean book(int startTime, int endTime) { + for (int[] event : events) { + if (startTime < event[1] && event[0] < endTime) { + return false; + } + } + events.add(new int[]{startTime, endTime}); + return true; + } +} +``` + +```cpp +class MyCalendar { +private: + vector> events; + +public: + MyCalendar() {} + + bool book(int startTime, int endTime) { + for (const auto& event : events) { + if (startTime < event.second && event.first < endTime) { + return false; + } + } + events.push_back({startTime, endTime}); + return true; + } +}; +``` + +```javascript +class MyCalendar { + constructor() { + this.events = []; + } + + /** + * @param {number} startTime + * @param {number} endTime + * @return {boolean} + */ + book(startTime, endTime) { + for (const [start, end] of this.events) { + if (startTime < end && start < endTime) { + return false; + } + } + this.events.push([startTime, endTime]); + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ for each $book()$ function call. +* Space complexity: $O(n)$ + +--- + +## 2. Binary Search Tree + +::tabs-start + +```python +class TreeNode: + def __init__(self, start: int, end: int): + self.start = start + self.end = end + self.left = None + self.right = None + +class MyCalendar: + + def __init__(self): + self.root = None + + def _insert(self, node: TreeNode, start: int, end: int) -> bool: + if end <= node.start: + if not node.left: + node.left = TreeNode(start, end) + return True + return self._insert(node.left, start, end) + elif start >= node.end: + if not node.right: + node.right = TreeNode(start, end) + return True + return self._insert(node.right, start, end) + else: + return False + + def book(self, startTime: int, endTime: int) -> bool: + if not self.root: + self.root = TreeNode(startTime, endTime) + return True + return self._insert(self.root, startTime, endTime) +``` + +```java +class TreeNode { + int start, end; + TreeNode left, right; + + TreeNode(int start, int end) { + this.start = start; + this.end = end; + this.left = null; + this.right = null; + } +} + +public class MyCalendar { + private TreeNode root; + + public MyCalendar() { + root = null; + } + + private boolean insert(TreeNode node, int start, int end) { + if (end <= node.start) { + if (node.left == null) { + node.left = new TreeNode(start, end); + return true; + } + return insert(node.left, start, end); + } else if (start >= node.end) { + if (node.right == null) { + node.right = new TreeNode(start, end); + return true; + } + return insert(node.right, start, end); + } + return false; + } + + public boolean book(int startTime, int endTime) { + if (root == null) { + root = new TreeNode(startTime, endTime); + return true; + } + return insert(root, startTime, endTime); + } +} +``` + +```cpp +class MyCalendar { +private: + struct TreeNode { + int start, end; + TreeNode *left, *right; + + TreeNode(int start, int end) : start(start), end(end), left(nullptr), right(nullptr) {} + }; + TreeNode *root; + + bool insert(TreeNode *node, int start, int end) { + if (end <= node->start) { + if (!node->left) { + node->left = new TreeNode(start, end); + return true; + } + return insert(node->left, start, end); + } else if (start >= node->end) { + if (!node->right) { + node->right = new TreeNode(start, end); + return true; + } + return insert(node->right, start, end); + } + return false; + } + +public: + MyCalendar() : root(nullptr) {} + + bool book(int startTime, int endTime) { + if (!root) { + root = new TreeNode(startTime, endTime); + return true; + } + return insert(root, startTime, endTime); + } +}; +``` + +```javascript +class TreeNode { + /** + * @constructor + * @param {number} start + * @param {number} end + */ + constructor(start, end) { + this.start = start; + this.end = end; + this.left = null; + this.right = null; + } +} + +class MyCalendar { + constructor() { + this.root = null; + } + + /** + * @param {TreeNode} node + * @param {number} start + * @param {number} end + * @return {boolean} + */ + insert(node, start, end) { + if (end <= node.start) { + if (!node.left) { + node.left = new TreeNode(start, end); + return true; + } + return this.insert(node.left, start, end); + } else if (start >= node.end) { + if (!node.right) { + node.right = new TreeNode(start, end); + return true; + } + return this.insert(node.right, start, end); + } + return false; + } + + /** + * @param {number} startTime + * @param {number} endTime + * @return {boolean} + */ + book(startTime, endTIme) { + if (!this.root) { + this.root = new TreeNode(startTime, endTime); + return true; + } + return this.insert(this.root, startTime, endTime); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ in average case, $O(n)$ in worst case for each $book()$ function call. +* Space complexity: $O(n)$ + +--- + +## 3. Binary Search + Ordered Set + +::tabs-start + +```python +class MyCalendar: + + def __init__(self): + self.events = SortedList() + + def book(self, startTime: int, endTime: int) -> bool: + idx = self.events.bisect_left((startTime, endTime)) + if idx > 0 and self.events[idx - 1][1] > startTime: + return False + if idx < len(self.events) and self.events[idx][0] < endTime: + return False + self.events.add((startTime, endTime)) + return True +``` + +```java +public class MyCalendar { + private TreeSet events; + + public MyCalendar() { + events = new TreeSet<>((a, b) -> a[0] - b[0]); + } + + public boolean book(int startTime, int endTime) { + int[] event = new int[]{startTime, endTime}; + int[] prev = events.floor(event); + int[] next = events.ceiling(event); + + if ((prev != null && prev[1] > startTime) || (next != null && next[0] < endTime)) { + return false; + } + events.add(event); + return true; + } +} +``` + +```cpp +class MyCalendar { +private: + set> events; + +public: + MyCalendar() {} + + bool book(int startTime, int endTime) { + if (startTime >= endTime) { + return false; + } + + auto next = events.lower_bound({startTime, startTime}); + if (next != events.end() && next->first < endTime) { + return false; + } + if (next != events.begin()) { + auto prev = std::prev(next); + if (prev->second > startTime) { + return false; + } + } + + events.insert({startTime, endTime}); + return true; + } +}; +``` + +```javascript +class MyCalendar { + constructor() { + this.events = []; + } + + /** + * @param {number} startTime + * @param {number} endTime + * @return {boolean} + */ + book(startTime, endTIme) { + if (startTime >= endTime) { + return false; + } + + const binarySearch = (target) => { + let left = 0, right = this.events.length; + + while (left < right) { + let mid = Math.floor((left + right) / 2); + if (this.events[mid][0] < target) { + left = mid + 1; + } else { + right = mid; + } + } + return left; + }; + + const idx = binarySearch(startTime); + if (idx > 0 && this.events[idx - 1][1] > startTime) { + return false; + } + if (idx < this.events.length && this.events[idx][0] < endTime) { + return false; + } + this.events.splice(idx, 0, [startTime, endTime]); + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ for each $book()$ function call. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/n-queens-ii.md b/articles/n-queens-ii.md new file mode 100644 index 000000000..16a94bd75 --- /dev/null +++ b/articles/n-queens-ii.md @@ -0,0 +1,834 @@ +## 1. Backtracking + +::tabs-start + +```python +class Solution: + def totalNQueens(self, n: int) -> int: + res = 0 + board = [["."] * n for i in range(n)] + + def backtrack(r): + nonlocal res + if r == n: + res += 1 + return + for c in range(n): + if self.isSafe(r, c, board): + board[r][c] = "Q" + backtrack(r + 1) + board[r][c] = "." + + backtrack(0) + return res + + def isSafe(self, r: int, c: int, board): + row = r - 1 + while row >= 0: + if board[row][c] == "Q": + return False + row -= 1 + + row, col = r - 1, c - 1 + while row >= 0 and col >= 0: + if board[row][col] == "Q": + return False + row -= 1 + col -= 1 + + row, col = r - 1, c + 1 + while row >= 0 and col < len(board): + if board[row][col] == "Q": + return False + row -= 1 + col += 1 + return True +``` + +```java +public class Solution { + private int res; + + public int totalNQueens(int n) { + res = 0; + char[][] board = new char[n][n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + board[i][j] = '.'; + } + } + backtrack(0, board); + return res; + } + + private void backtrack(int r, char[][] board) { + if (r == board.length) { + res++; + return; + } + for (int c = 0; c < board.length; c++) { + if (isSafe(r, c, board)) { + board[r][c] = 'Q'; + backtrack(r + 1, board); + board[r][c] = '.'; + } + } + } + + private boolean isSafe(int r, int c, char[][] board) { + for (int i = r - 1; i >= 0; i--) { + if (board[i][c] == 'Q') return false; + } + for (int i = r - 1, j = c - 1; i >= 0 && j >= 0; i--, j--) { + if (board[i][j] == 'Q') return false; + } + for (int i = r - 1, j = c + 1; i >= 0 && j < board.length; i--, j++) { + if (board[i][j] == 'Q') return false; + } + return true; + } +} +``` + +```cpp +class Solution { +public: + int totalNQueens(int n) { + int res = 0; + vector board(n, string(n, '.')); + backtrack(0, board, res); + return res; + } + + void backtrack(int r, vector& board, int& res) { + if (r == board.size()) { + res++; + return; + } + for (int c = 0; c < board.size(); c++) { + if (isSafe(r, c, board)) { + board[r][c] = 'Q'; + backtrack(r + 1, board, res); + board[r][c] = '.'; + } + } + } + + bool isSafe(int r, int c, vector& board) { + for (int i = r - 1; i >= 0; i--) { + if (board[i][c] == 'Q') return false; + } + for (int i = r - 1, j = c - 1; i >= 0 && j >= 0; i--, j--) { + if (board[i][j] == 'Q') return false; + } + for (int i = r - 1, j = c + 1; i >= 0 && j < board.size(); i--, j++) { + if (board[i][j] == 'Q') return false; + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + totalNQueens(n) { + let res = 0; + let board = Array.from({length: n}, () => Array(n).fill('.')); + + const backtrack = (r) => { + if (r === n) { + res++; + return; + } + for (let c = 0; c < n; c++) { + if (this.isSafe(r, c, board)) { + board[r][c] = 'Q'; + backtrack(r + 1); + board[r][c] = '.'; + } + } + } + + backtrack(0); + return res; + } + + /** + * @param {number} r + * @param {number} c + * @param {string[][]} board + * @return {boolean} + */ + isSafe(r, c, board) { + for (let i = r - 1; i >= 0; i--) { + if (board[i][c] === 'Q') return false; + } + for (let i = r - 1, j = c - 1; i >= 0 && j >= 0; i--, j--) { + if (board[i][j] === 'Q') return false; + } + for (let i = r - 1, j = c + 1; i >= 0 && j < board.length; i--, j++) { + if (board[i][j] === 'Q') return false; + } + return true; + } +} +``` + +```csharp +public class Solution { + private int res = 0; + + public int TotalNQueens(int n) { + res = 0; + char[][] board = new char[n][]; + for (int i = 0; i < n; i++) { + board[i] = Enumerable.Repeat('.', n).ToArray(); + } + + Backtrack(0, board, n); + return res; + } + + private void Backtrack(int r, char[][] board, int n) { + if (r == n) { + res++; + return; + } + + for (int c = 0; c < n; c++) { + if (IsSafe(r, c, board)) { + board[r][c] = 'Q'; + Backtrack(r + 1, board, n); + board[r][c] = '.'; + } + } + } + + private bool IsSafe(int r, int c, char[][] board) { + // Check column + for (int row = r - 1; row >= 0; row--) { + if (board[row][c] == 'Q') return false; + } + + // Check top-left diagonal + for (int row = r - 1, col = c - 1; row >= 0 && col >= 0; row--, col--) { + if (board[row][col] == 'Q') return false; + } + + // Check top-right diagonal + for (int row = r - 1, col = c + 1; row >= 0 && col < board.Length; row--, col++) { + if (board[row][col] == 'Q') return false; + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n!)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Backtracking (Hash Set) + +::tabs-start + +```python +class Solution: + def totalNQueens(self, n: int) -> int: + col = set() + posDiag = set() + negDiag = set() + + res = 0 + def backtrack(r): + nonlocal res + if r == n: + res += 1 + return + + for c in range(n): + if c in col or (r + c) in posDiag or (r - c) in negDiag: + continue + + col.add(c) + posDiag.add(r + c) + negDiag.add(r - c) + + backtrack(r + 1) + + col.remove(c) + posDiag.remove(r + c) + negDiag.remove(r - c) + + backtrack(0) + return res +``` + +```java +public class Solution { + Set col = new HashSet<>(); + Set posDiag = new HashSet<>(); + Set negDiag = new HashSet<>(); + int res; + + public int totalNQueens(int n) { + res = 0; + backtrack(0, n); + return res; + } + + private void backtrack(int r, int n) { + if (r == n) { + res++; + return; + } + + for (int c = 0; c < n; c++) { + if (col.contains(c) || posDiag.contains(r + c) || negDiag.contains(r - c)) { + continue; + } + + col.add(c); + posDiag.add(r + c); + negDiag.add(r - c); + + backtrack(r + 1, n); + + col.remove(c); + posDiag.remove(r + c); + negDiag.remove(r - c); + } + } +} +``` + +```cpp +class Solution { +public: + unordered_set col; + unordered_set posDiag; + unordered_set negDiag; + + int totalNQueens(int n) { + int res = 0; + backtrack(0, n, res); + return res; + } + +private: + void backtrack(int r, int n, int& res) { + if (r == n) { + res++; + return; + } + + for (int c = 0; c < n; c++) { + if (col.count(c) || posDiag.count(r + c) || negDiag.count(r - c)) { + continue; + } + + col.insert(c); + posDiag.insert(r + c); + negDiag.insert(r - c); + + backtrack(r + 1, n, res); + + col.erase(c); + posDiag.erase(r + c); + negDiag.erase(r - c); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + totalNQueens(n) { + const col = new Set(); + const posDiag = new Set(); + const negDiag = new Set(); + let res = 0; + + /** + * @param {number} r + * @return {void} + */ + function backtrack(r) { + if (r === n) { + res++; + return; + } + + for (let c = 0; c < n; c++) { + if (col.has(c) || posDiag.has(r + c) || + negDiag.has(r - c)) { + continue; + } + + col.add(c); + posDiag.add(r + c); + negDiag.add(r - c); + + backtrack(r + 1); + + col.delete(c); + posDiag.delete(r + c); + negDiag.delete(r - c); + } + } + + backtrack(0); + return res; + } +} +``` + +```csharp +public class Solution { + private HashSet col = new HashSet(); + private HashSet posDiag = new HashSet(); // r + c + private HashSet negDiag = new HashSet(); // r - c + private int res = 0; + + public int TotalNQueens(int n) { + res = 0; + Backtrack(0, n); + return res; + } + + private void Backtrack(int r, int n) { + if (r == n) { + res++; + return; + } + + for (int c = 0; c < n; c++) { + if (col.Contains(c) || posDiag.Contains(r + c) || negDiag.Contains(r - c)) + continue; + + col.Add(c); + posDiag.Add(r + c); + negDiag.Add(r - c); + + Backtrack(r + 1, n); + + col.Remove(c); + posDiag.Remove(r + c); + negDiag.Remove(r - c); + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n!)$ +* Space complexity: $O(n)$ + +--- + +## 3. Backtracking (Boolean Array) + +::tabs-start + +```python +class Solution: + def totalNQueens(self, n: int) -> int: + col = [False] * n + posDiag = [False] * (n * 2) + negDiag = [False] * (n * 2) + res = 0 + + def backtrack(r): + nonlocal res + if r == n: + res += 1 + return + for c in range(n): + if col[c] or posDiag[r + c] or negDiag[r - c + n]: + continue + col[c] = True + posDiag[r + c] = True + negDiag[r - c + n] = True + + backtrack(r + 1) + + col[c] = False + posDiag[r + c] = False + negDiag[r - c + n] = False + + backtrack(0) + return res +``` + +```java +public class Solution { + boolean[] col, posDiag, negDiag; + int res; + + public int totalNQueens(int n) { + col = new boolean[n]; + posDiag = new boolean[2 * n]; + negDiag = new boolean[2 * n]; + res = 0; + + backtrack(0, n); + return res; + } + + private void backtrack(int r, int n) { + if (r == n) { + res++; + return; + } + for (int c = 0; c < n; c++) { + if (col[c] || posDiag[r + c] || negDiag[r - c + n]) { + continue; + } + col[c] = true; + posDiag[r + c] = true; + negDiag[r - c + n] = true; + + backtrack(r + 1, n); + + col[c] = false; + posDiag[r + c] = false; + negDiag[r - c + n] = false; + } + } +} +``` + +```cpp +class Solution { +public: + vector board; + vector col, posDiag, negDiag; + + int totalNQueens(int n) { + col.resize(n, false); + posDiag.resize(2 * n, false); + negDiag.resize(2 * n, false); + + int res = 0; + backtrack(0, n, res); + return res; + } + + void backtrack(int r, int n, int& res) { + if (r == n) { + res++; + return; + } + for (int c = 0; c < n; c++) { + if (col[c] || posDiag[r + c] || negDiag[r - c + n]) { + continue; + } + col[c] = true; + posDiag[r + c] = true; + negDiag[r - c + n] = true; + + backtrack(r + 1, n, res); + + col[c] = false; + posDiag[r + c] = false; + negDiag[r - c + n] = false; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + totalNQueens(n) { + const col = Array(n).fill(false); + const posDiag = Array(2 * n).fill(false); + const negDiag = Array(2 * n).fill(false); + let res = 0; + + /** + * @param {number} r + * @return {void} + */ + function backtrack(r) { + if (r === n) { + res++; + return; + } + for (let c = 0; c < n; c++) { + if (col[c] || posDiag[r + c] || negDiag[r - c + n]) { + continue; + } + col[c] = true; + posDiag[r + c] = true; + negDiag[r - c + n] = true; + + backtrack(r + 1); + + col[c] = false; + posDiag[r + c] = false; + negDiag[r - c + n] = false; + } + } + + backtrack(0); + return res; + } +} +``` + +```csharp +public class Solution { + public int TotalNQueens(int n) { + bool[] col = new bool[n]; + bool[] posDiag = new bool[2 * n]; + bool[] negDiag = new bool[2 * n]; + int res = 0; + + void Backtrack(int r) { + if (r == n) { + res++; + return; + } + + for (int c = 0; c < n; c++) { + if (col[c] || posDiag[r + c] || negDiag[r - c + n]) + continue; + + col[c] = true; + posDiag[r + c] = true; + negDiag[r - c + n] = true; + + Backtrack(r + 1); + + col[c] = false; + posDiag[r + c] = false; + negDiag[r - c + n] = false; + } + } + + Backtrack(0); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n!)$ +* Space complexity: $O(n)$ + +--- + +## 4. Backtracking (Bit Mask) + +::tabs-start + +```python +class Solution: + def totalNQueens(self, n: int) -> int: + col = 0 + posDiag = 0 + negDiag = 0 + res = 0 + + def backtrack(r): + nonlocal col, posDiag, negDiag, res + if r == n: + res += 1 + return + for c in range(n): + if ((col & (1 << c)) or (posDiag & (1 << (r + c))) + or (negDiag & (1 << (r - c + n)))): + continue + col ^= (1 << c) + posDiag ^= (1 << (r + c)) + negDiag ^= (1 << (r - c + n)) + + backtrack(r + 1) + + col ^= (1 << c) + posDiag ^= (1 << (r + c)) + negDiag ^= (1 << (r - c + n)) + + backtrack(0) + return res +``` + +```java +public class Solution { + private int col = 0, posDiag = 0, negDiag = 0, res = 0; + + public int totalNQueens(int n) { + res = 0; + backtrack(0, n); + return res; + } + + private void backtrack(int r, int n) { + if (r == n) { + res++; + return; + } + for (int c = 0; c < n; c++) { + if ((col & (1 << c)) > 0 || (posDiag & (1 << (r + c))) > 0 || + (negDiag & (1 << (r - c + n))) > 0) { + continue; + } + col ^= (1 << c); + posDiag ^= (1 << (r + c)); + negDiag ^= (1 << (r - c + n)); + + backtrack(r + 1, n); + + col ^= (1 << c); + posDiag ^= (1 << (r + c)); + negDiag ^= (1 << (r - c + n)); + } + } +} +``` + +```cpp +class Solution { +public: + int col = 0, posDiag = 0, negDiag = 0; + vector board; + + int totalNQueens(int n) { + int res = 0; + backtrack(0, n, res); + return res; + } + + void backtrack(int r, int n, int& res) { + if (r == n) { + res++; + return; + } + for (int c = 0; c < n; c++) { + if ((col & (1 << c)) || (posDiag & (1 << (r + c))) || + (negDiag & (1 << (r - c + n)))) { + continue; + } + col ^= (1 << c); + posDiag ^= (1 << (r + c)); + negDiag ^= (1 << (r - c + n)); + + backtrack(r + 1, n, res); + + col ^= (1 << c); + posDiag ^= (1 << (r + c)); + negDiag ^= (1 << (r - c + n)); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + totalNQueens(n) { + let col = 0, posDiag = 0, negDiag = 0, res = 0; + + /** + * @param {number} r + * @return {void} + */ + function backtrack(r) { + if (r === n) { + res++; + return; + } + for (let c = 0; c < n; c++) { + if ((col & (1 << c)) > 0 || (posDiag & (1 << (r + c))) > 0 || + (negDiag & (1 << (r - c + n))) > 0) { + continue; + } + col ^= (1 << c); + posDiag ^= (1 << (r + c)); + negDiag ^= (1 << (r - c + n)); + + backtrack(r + 1); + + col ^= (1 << c); + posDiag ^= (1 << (r + c)); + negDiag ^= (1 << (r - c + n)); + } + } + + backtrack(0); + return res; + } +} +``` + +```csharp +public class Solution { + public int TotalNQueens(int n) { + int col = 0; + int posDiag = 0; + int negDiag = 0; + int res = 0; + + void Backtrack(int r) { + if (r == n) { + res++; + return; + } + + for (int c = 0; c < n; c++) { + if (((col & (1 << c)) != 0) || + ((posDiag & (1 << (r + c))) != 0) || + ((negDiag & (1 << (r - c + n))) != 0)) + continue; + + col ^= (1 << c); + posDiag ^= (1 << (r + c)); + negDiag ^= (1 << (r - c + n)); + + Backtrack(r + 1); + + col ^= (1 << c); + posDiag ^= (1 << (r + c)); + negDiag ^= (1 << (r - c + n)); + } + } + + Backtrack(0); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n!)$ +* Space complexity: $O(n)$ for recursion stack. \ No newline at end of file diff --git a/articles/n-queens.md b/articles/n-queens.md new file mode 100644 index 000000000..e75cd714b --- /dev/null +++ b/articles/n-queens.md @@ -0,0 +1,1479 @@ +## 1. Backtracking + +::tabs-start + +```python +class Solution: + def solveNQueens(self, n: int) -> List[List[str]]: + res = [] + board = [["."] * n for i in range(n)] + + def backtrack(r): + if r == n: + copy = ["".join(row) for row in board] + res.append(copy) + return + for c in range(n): + if self.isSafe(r, c, board): + board[r][c] = "Q" + backtrack(r + 1) + board[r][c] = "." + + backtrack(0) + return res + + def isSafe(self, r: int, c: int, board): + row = r - 1 + while row >= 0: + if board[row][c] == "Q": + return False + row -= 1 + + row, col = r - 1, c - 1 + while row >= 0 and col >= 0: + if board[row][col] == "Q": + return False + row -= 1 + col -= 1 + + row, col = r - 1, c + 1 + while row >= 0 and col < len(board): + if board[row][col] == "Q": + return False + row -= 1 + col += 1 + return True +``` + +```java +public class Solution { + public List> solveNQueens(int n) { + List> res = new ArrayList<>(); + char[][] board = new char[n][n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + board[i][j] = '.'; + } + } + backtrack(0, board, res); + return res; + } + + private void backtrack(int r, char[][] board, List> res) { + if (r == board.length) { + List copy = new ArrayList<>(); + for (char[] row : board) { + copy.add(new String(row)); + } + res.add(copy); + return; + } + for (int c = 0; c < board.length; c++) { + if (isSafe(r, c, board)) { + board[r][c] = 'Q'; + backtrack(r + 1, board, res); + board[r][c] = '.'; + } + } + } + + private boolean isSafe(int r, int c, char[][] board) { + for (int i = r - 1; i >= 0; i--) { + if (board[i][c] == 'Q') return false; + } + for (int i = r - 1, j = c - 1; i >= 0 && j >= 0; i--, j--) { + if (board[i][j] == 'Q') return false; + } + for (int i = r - 1, j = c + 1; i >= 0 && j < board.length; i--, j++) { + if (board[i][j] == 'Q') return false; + } + return true; + } +} +``` + +```cpp +class Solution { +public: + vector> solveNQueens(int n) { + vector> res; + vector board(n, string(n, '.')); + backtrack(0, board, res); + return res; + } + + void backtrack(int r, vector& board, vector>& res) { + if (r == board.size()) { + res.push_back(board); + return; + } + for (int c = 0; c < board.size(); c++) { + if (isSafe(r, c, board)) { + board[r][c] = 'Q'; + backtrack(r + 1, board, res); + board[r][c] = '.'; + } + } + } + + bool isSafe(int r, int c, vector& board) { + for (int i = r - 1; i >= 0; i--) { + if (board[i][c] == 'Q') return false; + } + for (int i = r - 1, j = c - 1; i >= 0 && j >= 0; i--, j--) { + if (board[i][j] == 'Q') return false; + } + for (int i = r - 1, j = c + 1; i >= 0 && j < board.size(); i--, j++) { + if (board[i][j] == 'Q') return false; + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {string[][]} + */ + solveNQueens(n) { + let res = []; + let board = Array.from({length: n}, () => Array(n).fill('.')); + + const backtrack = (r) => { + if (r === n) { + res.push(board.map(row => row.join(''))); + return; + } + for (let c = 0; c < n; c++) { + if (this.isSafe(r, c, board)) { + board[r][c] = 'Q'; + backtrack(r + 1); + board[r][c] = '.'; + } + } + } + + backtrack(0); + return res; + } + + /** + * @param {number} r + * @param {number} c + * @param {string[][]} board + * @return {boolean} + */ + isSafe(r, c, board) { + for (let i = r - 1; i >= 0; i--) { + if (board[i][c] === 'Q') return false; + } + for (let i = r - 1, j = c - 1; i >= 0 && j >= 0; i--, j--) { + if (board[i][j] === 'Q') return false; + } + for (let i = r - 1, j = c + 1; i >= 0 && j < board.length; i--, j++) { + if (board[i][j] === 'Q') return false; + } + return true; + } +} +``` + +```csharp +public class Solution { + public List> SolveNQueens(int n) { + var res = new List>(); + var board = new char[n][]; + for (int i = 0; i < n; i++) { + board[i] = new string('.', n).ToCharArray(); + } + Backtrack(0, board, res); + return res; + } + + private void Backtrack(int r, char[][] board, List> res) { + if (r == board.Length) { + var copy = new List(); + foreach (var row in board) { + copy.Add(new string(row)); + } + res.Add(copy); + return; + } + for (int c = 0; c < board.Length; c++) { + if (IsSafe(r, c, board)) { + board[r][c] = 'Q'; + Backtrack(r + 1, board, res); + board[r][c] = '.'; + } + } + } + + private bool IsSafe(int r, int c, char[][] board) { + for (int i = r - 1; i >= 0; i--) { + if (board[i][c] == 'Q') return false; + } + for (int i = r - 1, j = c - 1; i >= 0 && j >= 0; i--, j--) { + if (board[i][j] == 'Q') return false; + } + for (int i = r - 1, j = c + 1; i >= 0 && j < board.Length; i--, j++) { + if (board[i][j] == 'Q') return false; + } + return true; + } +} +``` + +```go +func solveNQueens(n int) [][]string { + res := [][]string{} + board := make([][]string, n) + for i := range board { + board[i] = make([]string, n) + for j := range board[i] { + board[i][j] = "." + } + } + + var backtrack func(r int) + backtrack = func(r int) { + if r == n { + copyBoard := make([]string, n) + for i := range board { + copyBoard[i] = "" + for j := range board[i] { + copyBoard[i] += board[i][j] + } + } + res = append(res, copyBoard) + return + } + for c := 0; c < n; c++ { + if isSafe(r, c, board) { + board[r][c] = "Q" + backtrack(r + 1) + board[r][c] = "." + } + } + } + + backtrack(0) + return res +} + +func isSafe(r int, c int, board [][]string) bool { + for row := r - 1; row >= 0; row-- { + if board[row][c] == "Q" { + return false + } + } + + for row, col := r-1, c-1; row >= 0 && col >= 0; row, col = row-1, col-1 { + if board[row][col] == "Q" { + return false + } + } + + for row, col := r-1, c+1; row >= 0 && col < len(board); row, col = row-1, col+1 { + if board[row][col] == "Q" { + return false + } + } + + return true +} +``` + +```kotlin +class Solution { + fun solveNQueens(n: Int): List> { + val res = mutableListOf>() + val board = Array(n) { CharArray(n) { '.' } } + + fun isSafe(r: Int, c: Int): Boolean { + var row = r - 1 + while (row >= 0) { + if (board[row][c] == 'Q') return false + row-- + } + var col = c - 1 + row = r - 1 + while (row >= 0 && col >= 0) { + if (board[row][col] == 'Q') return false + row-- + col-- + } + col = c + 1 + row = r - 1 + while (row >= 0 && col < n) { + if (board[row][col] == 'Q') return false + row-- + col++ + } + return true + } + + fun backtrack(r: Int) { + if (r == n) { + res.add(board.map { it.joinToString("") }) + return + } + for (c in 0 until n) { + if (isSafe(r, c)) { + board[r][c] = 'Q' + backtrack(r + 1) + board[r][c] = '.' + } + } + } + + backtrack(0) + return res + } +} +``` + +```swift +class Solution { + func solveNQueens(_ n: Int) -> [[String]] { + var res = [[String]]() + var board = Array(repeating: Array(repeating: ".", count: n), count: n) + + func backtrack(_ r: Int) { + if r == n { + let copy = board.map { $0.joined() } + res.append(copy) + return + } + for c in 0.. Bool { + var row = r - 1 + while row >= 0 { + if board[row][c] == "Q" { return false } + row -= 1 + } + + var row1 = r - 1, col1 = c - 1 + while row1 >= 0, col1 >= 0 { + if board[row1][col1] == "Q" { return false } + row1 -= 1 + col1 -= 1 + } + + var row2 = r - 1, col2 = c + 1 + while row2 >= 0, col2 < board.count { + if board[row2][col2] == "Q" { return false } + row2 -= 1 + col2 += 1 + } + + return true + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n!)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Backtracking (Hash Set) + +::tabs-start + +```python +class Solution: + def solveNQueens(self, n: int) -> List[List[str]]: + col = set() + posDiag = set() + negDiag = set() + + res = [] + board = [["."] * n for i in range(n)] + + def backtrack(r): + if r == n: + copy = ["".join(row) for row in board] + res.append(copy) + return + + for c in range(n): + if c in col or (r + c) in posDiag or (r - c) in negDiag: + continue + + col.add(c) + posDiag.add(r + c) + negDiag.add(r - c) + board[r][c] = "Q" + + backtrack(r + 1) + + col.remove(c) + posDiag.remove(r + c) + negDiag.remove(r - c) + board[r][c] = "." + + backtrack(0) + return res +``` + +```java +public class Solution { + Set col = new HashSet<>(); + Set posDiag = new HashSet<>(); + Set negDiag = new HashSet<>(); + List> res = new ArrayList<>(); + + public List> solveNQueens(int n) { + char[][] board = new char[n][n]; + for (char[] row : board) { + Arrays.fill(row, '.'); + } + + backtrack(0, n, board); + return res; + } + + private void backtrack(int r, int n, char[][] board) { + if (r == n) { + List copy = new ArrayList<>(); + for (char[] row : board) { + copy.add(new String(row)); + } + res.add(copy); + return; + } + + for (int c = 0; c < n; c++) { + if (col.contains(c) || posDiag.contains(r + c) + || negDiag.contains(r - c)) { + continue; + } + + col.add(c); + posDiag.add(r + c); + negDiag.add(r - c); + board[r][c] = 'Q'; + + backtrack(r + 1, n, board); + + col.remove(c); + posDiag.remove(r + c); + negDiag.remove(r - c); + board[r][c] = '.'; + } + } +} +``` + +```cpp +class Solution { +public: + unordered_set col; + unordered_set posDiag; + unordered_set negDiag; + vector> res; + + vector> solveNQueens(int n) { + vector board(n, string(n, '.')); + + backtrack(0, n, board); + return res; + } + +private: + void backtrack(int r, int n, vector& board) { + if (r == n) { + res.push_back(board); + return; + } + + for (int c = 0; c < n; c++) { + if (col.count(c) || posDiag.count(r + c) || + negDiag.count(r - c)) { + continue; + } + + col.insert(c); + posDiag.insert(r + c); + negDiag.insert(r - c); + board[r][c] = 'Q'; + + backtrack(r + 1, n, board); + + col.erase(c); + posDiag.erase(r + c); + negDiag.erase(r - c); + board[r][c] = '.'; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {string[][]} + */ + solveNQueens(n) { + const col = new Set(); + const posDiag = new Set(); + const negDiag = new Set(); + + const res = []; + const board = Array.from({ length: n }, + () => Array(n).fill('.')); + + /** + * @param {number} r + * @return {void} + */ + function backtrack(r) { + if (r === n) { + res.push(board.map(row => row.join(''))); + return; + } + + for (let c = 0; c < n; c++) { + if (col.has(c) || posDiag.has(r + c) || + negDiag.has(r - c)) { + continue; + } + + col.add(c); + posDiag.add(r + c); + negDiag.add(r - c); + board[r][c] = 'Q'; + + backtrack(r + 1); + + col.delete(c); + posDiag.delete(r + c); + negDiag.delete(r - c); + board[r][c] = '.'; + } + } + + backtrack(0); + return res; + } +} +``` + +```csharp +public class Solution { + HashSet col = new HashSet(); + HashSet posDiag = new HashSet(); + HashSet negDiag = new HashSet(); + List> res = new List>(); + + public List> SolveNQueens(int n) { + char[][] board = new char[n][]; + for (int i = 0; i < n; i++) { + board[i] = new char[n]; + Array.Fill(board[i], '.'); + } + + Backtrack(0, n, board); + return res; + } + + private void Backtrack(int r, int n, char[][] board) { + if (r == n) { + List copy = new List(); + foreach (char[] row in board) { + copy.Add(new string(row)); + } + res.Add(copy); + return; + } + + for (int c = 0; c < n; c++) { + if (col.Contains(c) || posDiag.Contains(r + c) || + negDiag.Contains(r - c)) { + continue; + } + + col.Add(c); + posDiag.Add(r + c); + negDiag.Add(r - c); + board[r][c] = 'Q'; + + Backtrack(r + 1, n, board); + + col.Remove(c); + posDiag.Remove(r + c); + negDiag.Remove(r - c); + board[r][c] = '.'; + } + } +} +``` + +```go +func solveNQueens(n int) [][]string { + col := make(map[int]bool) + posDiag := make(map[int]bool) + negDiag := make(map[int]bool) + var res [][]string + board := make([][]rune, n) + for i := range board { + board[i] = make([]rune, n) + for j := range board[i] { + board[i][j] = '.' + } + } + + var backtrack func(r int) + backtrack = func(r int) { + if r == n { + solution := make([]string, n) + for i := range board { + solution[i] = string(board[i]) + } + res = append(res, solution) + return + } + + for c := 0; c < n; c++ { + if col[c] || posDiag[r+c] || negDiag[r-c] { + continue + } + + col[c] = true + posDiag[r+c] = true + negDiag[r-c] = true + board[r][c] = 'Q' + + backtrack(r + 1) + + col[c] = false + posDiag[r+c] = false + negDiag[r-c] = false + board[r][c] = '.' + } + } + + backtrack(0) + return res +} +``` + +```kotlin +class Solution { + fun solveNQueens(n: Int): List> { + val col = HashSet() + val posDiag = HashSet() + val negDiag = HashSet() + val res = mutableListOf>() + val board = Array(n) { CharArray(n) { '.' } } + + fun backtrack(r: Int) { + if (r == n) { + res.add(board.map { it.joinToString("") }) + return + } + + for (c in 0 until n) { + if (c in col || (r + c) in posDiag || (r - c) in negDiag) { + continue + } + + col.add(c) + posDiag.add(r + c) + negDiag.add(r - c) + board[r][c] = 'Q' + + backtrack(r + 1) + + col.remove(c) + posDiag.remove(r + c) + negDiag.remove(r - c) + board[r][c] = '.' + } + } + + backtrack(0) + return res + } +} +``` + +```swift +class Solution { + func solveNQueens(_ n: Int) -> [[String]] { + var col = Set() + var posDiag = Set() + var negDiag = Set() + var res = [[String]]() + var board = Array(repeating: Array(repeating: ".", count: n), count: n) + + func backtrack(_ r: Int) { + if r == n { + let copy = board.map { $0.joined() } + res.append(copy) + return + } + + for c in 0.. List[List[str]]: + col = [False] * n + posDiag = [False] * (n * 2) + negDiag = [False] * (n * 2) + res = [] + board = [["."] * n for i in range(n)] + + def backtrack(r): + if r == n: + copy = ["".join(row) for row in board] + res.append(copy) + return + for c in range(n): + if col[c] or posDiag[r + c] or negDiag[r - c + n]: + continue + col[c] = True + posDiag[r + c] = True + negDiag[r - c + n] = True + board[r][c] = "Q" + + backtrack(r + 1) + + col[c] = False + posDiag[r + c] = False + negDiag[r - c + n] = False + board[r][c] = "." + + backtrack(0) + return res +``` + +```java +public class Solution { + boolean[] col, posDiag, negDiag; + List> res; + char[][] board; + + public List> solveNQueens(int n) { + col = new boolean[n]; + posDiag = new boolean[2 * n]; + negDiag = new boolean[2 * n]; + res = new ArrayList<>(); + board = new char[n][n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + board[i][j] = '.'; + } + } + backtrack(0, n); + return res; + } + + private void backtrack(int r, int n) { + if (r == n) { + List copy = new ArrayList<>(); + for (char[] row : board) { + copy.add(new String(row)); + } + res.add(copy); + return; + } + for (int c = 0; c < n; c++) { + if (col[c] || posDiag[r + c] || negDiag[r - c + n]) { + continue; + } + col[c] = true; + posDiag[r + c] = true; + negDiag[r - c + n] = true; + board[r][c] = 'Q'; + + backtrack(r + 1, n); + + col[c] = false; + posDiag[r + c] = false; + negDiag[r - c + n] = false; + board[r][c] = '.'; + } + } +} +``` + +```cpp +class Solution { +public: + vector board; + vector col, posDiag, negDiag; + vector> res; + + vector> solveNQueens(int n) { + col.resize(n, false); + posDiag.resize(2 * n, false); + negDiag.resize(2 * n, false); + board.resize(n, string(n, '.')); + + backtrack(0, n); + return res; + } + + void backtrack(int r, int n) { + if (r == n) { + res.push_back(board); + return; + } + for (int c = 0; c < n; c++) { + if (col[c] || posDiag[r + c] || negDiag[r - c + n]) { + continue; + } + col[c] = true; + posDiag[r + c] = true; + negDiag[r - c + n] = true; + board[r][c] = 'Q'; + + backtrack(r + 1, n); + + col[c] = false; + posDiag[r + c] = false; + negDiag[r - c + n] = false; + board[r][c] = '.'; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {string[][]} + */ + solveNQueens(n) { + const col = Array(n).fill(false); + const posDiag = Array(2 * n).fill(false); + const negDiag = Array(2 * n).fill(false); + const res = []; + const board = Array.from({ length: n }, + () => Array(n).fill('.')); + + /** + * @param {number} r + * @return {void} + */ + function backtrack(r) { + if (r === n) { + res.push(board.map(row => row.join(''))); + return; + } + for (let c = 0; c < n; c++) { + if (col[c] || posDiag[r + c] || negDiag[r - c + n]) { + continue; + } + col[c] = true; + posDiag[r + c] = true; + negDiag[r - c + n] = true; + board[r][c] = 'Q'; + + backtrack(r + 1); + + col[c] = false; + posDiag[r + c] = false; + negDiag[r - c + n] = false; + board[r][c] = '.'; + } + } + + backtrack(0); + return res; + } +} +``` + +```csharp +public class Solution { + bool[] col, posDiag, negDiag; + List> res; + char[][] board; + + public List> SolveNQueens(int n) { + col = new bool[n]; + posDiag = new bool[2 * n]; + negDiag = new bool[2 * n]; + res = new List>(); + board = new char[n][]; + for (int i = 0; i < n; i++) { + board[i] = new string('.', n).ToCharArray(); + } + Backtrack(0, n); + return res; + } + + private void Backtrack(int r, int n) { + if (r == n) { + var copy = new List(); + foreach (var row in board) { + copy.Add(new string(row)); + } + res.Add(copy); + return; + } + for (int c = 0; c < n; c++) { + if (col[c] || posDiag[r + c] || negDiag[r - c + n]) { + continue; + } + col[c] = true; + posDiag[r + c] = true; + negDiag[r - c + n] = true; + board[r][c] = 'Q'; + + Backtrack(r + 1, n); + + col[c] = false; + posDiag[r + c] = false; + negDiag[r - c + n] = false; + board[r][c] = '.'; + } + } +} +``` + +```go +func solveNQueens(n int) [][]string { + col := make([]bool, n) + posDiag := make([]bool, 2*n) + negDiag := make([]bool, 2*n) + var res [][]string + board := make([][]rune, n) + for i := range board { + board[i] = make([]rune, n) + for j := range board[i] { + board[i][j] = '.' + } + } + + var backtrack func(r int) + backtrack = func(r int) { + if r == n { + solution := make([]string, n) + for i := range board { + solution[i] = string(board[i]) + } + res = append(res, solution) + return + } + + for c := 0; c < n; c++ { + if col[c] || posDiag[r+c] || negDiag[r-c+n] { + continue + } + + col[c] = true + posDiag[r+c] = true + negDiag[r-c+n] = true + board[r][c] = 'Q' + + backtrack(r + 1) + + col[c] = false + posDiag[r+c] = false + negDiag[r-c+n] = false + board[r][c] = '.' + } + } + + backtrack(0) + return res +} +``` + +```kotlin +class Solution { + fun solveNQueens(n: Int): List> { + val col = BooleanArray(n) + val posDiag = BooleanArray(2 * n) + val negDiag = BooleanArray(2 * n) + val res = mutableListOf>() + val board = Array(n) { CharArray(n) { '.' } } + + fun backtrack(r: Int) { + if (r == n) { + res.add(board.map { it.joinToString("") }) + return + } + + for (c in 0 until n) { + if (col[c] || posDiag[r + c] || negDiag[r - c + n]) continue + + col[c] = true + posDiag[r + c] = true + negDiag[r - c + n] = true + board[r][c] = 'Q' + + backtrack(r + 1) + + col[c] = false + posDiag[r + c] = false + negDiag[r - c + n] = false + board[r][c] = '.' + } + } + + backtrack(0) + return res + } +} +``` + +```swift +class Solution { + func solveNQueens(_ n: Int) -> [[String]] { + var col = Array(repeating: false, count: n) + var posDiag = Array(repeating: false, count: 2 * n) + var negDiag = Array(repeating: false, count: 2 * n) + var res = [[String]]() + var board = Array(repeating: Array(repeating: ".", count: n), count: n) + + func backtrack(_ r: Int) { + if r == n { + let copy = board.map { $0.joined() } + res.append(copy) + return + } + + for c in 0.. List[List[str]]: + col = 0 + posDiag = 0 + negDiag = 0 + res = [] + board = [["."] * n for i in range(n)] + + def backtrack(r): + nonlocal col, posDiag, negDiag + if r == n: + copy = ["".join(row) for row in board] + res.append(copy) + return + for c in range(n): + if ((col & (1 << c)) or (posDiag & (1 << (r + c))) + or (negDiag & (1 << (r - c + n)))): + continue + col ^= (1 << c) + posDiag ^= (1 << (r + c)) + negDiag ^= (1 << (r - c + n)) + board[r][c] = "Q" + + backtrack(r + 1) + + col ^= (1 << c) + posDiag ^= (1 << (r + c)) + negDiag ^= (1 << (r - c + n)) + board[r][c] = "." + + backtrack(0) + return res +``` + +```java +public class Solution { + int col = 0, posDiag = 0, negDiag = 0; + List> res; + char[][] board; + + public List> solveNQueens(int n) { + res = new ArrayList<>(); + board = new char[n][n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + board[i][j] = '.'; + } + } + backtrack(0, n); + return res; + } + + private void backtrack(int r, int n) { + if (r == n) { + List copy = new ArrayList<>(); + for (char[] row : board) { + copy.add(new String(row)); + } + res.add(copy); + return; + } + for (int c = 0; c < n; c++) { + if ((col & (1 << c)) > 0 || (posDiag & (1 << (r + c))) > 0 + || (negDiag & (1 << (r - c + n))) > 0) { + continue; + } + col ^= (1 << c); + posDiag ^= (1 << (r + c)); + negDiag ^= (1 << (r - c + n)); + board[r][c] = 'Q'; + + backtrack(r + 1, n); + + col ^= (1 << c); + posDiag ^= (1 << (r + c)); + negDiag ^= (1 << (r - c + n)); + board[r][c] = '.'; + } + } +} +``` + +```cpp +class Solution { +public: + int col = 0, posDiag = 0, negDiag = 0; + vector board; + vector> res; + + vector> solveNQueens(int n) { + board.resize(n, string(n, '.')); + + backtrack(0, n); + return res; + } + + void backtrack(int r, int n) { + if (r == n) { + res.push_back(board); + return; + } + for (int c = 0; c < n; c++) { + if ((col & (1 << c)) || (posDiag & (1 << (r + c))) + || (negDiag & (1 << (r - c + n)))) { + continue; + } + col ^= (1 << c); + posDiag ^= (1 << (r + c)); + negDiag ^= (1 << (r - c + n)); + board[r][c] = 'Q'; + + backtrack(r + 1, n); + + col ^= (1 << c); + posDiag ^= (1 << (r + c)); + negDiag ^= (1 << (r - c + n)); + board[r][c] = '.'; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {string[][]} + */ + solveNQueens(n) { + let col = 0, posDiag = 0, negDiag = 0; + const res = []; + const board = Array.from({ length: n }, + () => Array(n).fill('.')); + + /** + * @param {number} r + * @return {void} + */ + function backtrack(r) { + if (r === n) { + res.push(board.map(row => row.join(''))); + return; + } + for (let c = 0; c < n; c++) { + if ((col & (1 << c)) > 0 || (posDiag & (1 << (r + c))) > 0 + || (negDiag & (1 << (r - c + n))) > 0) { + continue; + } + col ^= (1 << c); + posDiag ^= (1 << (r + c)); + negDiag ^= (1 << (r - c + n)); + board[r][c] = 'Q'; + + backtrack(r + 1); + + col ^= (1 << c); + posDiag ^= (1 << (r + c)); + negDiag ^= (1 << (r - c + n)); + board[r][c] = '.'; + } + } + + backtrack(0); + return res; + } +} +``` + +```csharp +public class Solution { + int col = 0, posDiag = 0, negDiag = 0; + List> res; + char[][] board; + + public List> SolveNQueens(int n) { + res = new List>(); + board = new char[n][]; + for (int i = 0; i < n; i++) { + board[i] = new string('.', n).ToCharArray(); + } + Backtrack(0, n); + return res; + } + + private void Backtrack(int r, int n) { + if (r == n) { + var copy = new List(); + foreach (var row in board) { + copy.Add(new string(row)); + } + res.Add(copy); + return; + } + for (int c = 0; c < n; c++) { + if ((col & (1 << c)) > 0 || (posDiag & (1 << (r + c))) > 0 + || (negDiag & (1 << (r - c + n))) > 0) { + continue; + } + col ^= (1 << c); + posDiag ^= (1 << (r + c)); + negDiag ^= (1 << (r - c + n)); + board[r][c] = 'Q'; + + Backtrack(r + 1, n); + + col ^= (1 << c); + posDiag ^= (1 << (r + c)); + negDiag ^= (1 << (r - c + n)); + board[r][c] = '.'; + } + } +} +``` + +```go +func solveNQueens(n int) [][]string { + var res [][]string + board := make([][]rune, n) + for i := range board { + board[i] = make([]rune, n) + for j := range board[i] { + board[i][j] = '.' + } + } + + var backtrack func(r, col, posDiag, negDiag int) + backtrack = func(r, col, posDiag, negDiag int) { + if r == n { + solution := make([]string, n) + for i := range board { + solution[i] = string(board[i]) + } + res = append(res, solution) + return + } + + for c := 0; c < n; c++ { + if (col&(1<> { + val res = mutableListOf>() + val board = Array(n) { CharArray(n) { '.' } } + + fun backtrack(r: Int, col: Int, posDiag: Int, negDiag: Int) { + if (r == n) { + res.add(board.map { it.joinToString("") }) + return + } + + for (c in 0 until n) { + if ((col and (1 shl c)) != 0 || (posDiag and (1 shl (r + c))) != 0 || + (negDiag and (1 shl (r - c + n))) != 0) { + continue + } + + val newCol = col xor (1 shl c) + val newPosDiag = posDiag xor (1 shl (r + c)) + val newNegDiag = negDiag xor (1 shl (r - c + n)) + board[r][c] = 'Q' + + backtrack(r + 1, newCol, newPosDiag, newNegDiag) + + board[r][c] = '.' + } + } + + backtrack(0, 0, 0, 0) + return res + } +} +``` + +```swift +class Solution { + func solveNQueens(_ n: Int) -> [[String]] { + var col = 0 + var posDiag = 0 + var negDiag = 0 + var res = [[String]]() + var board = Array(repeating: Array(repeating: ".", count: n), count: n) + + func backtrack(_ r: Int) { + if r == n { + let copy = board.map { $0.joined() } + res.append(copy) + return + } + + for c in 0.. int: + if n <= 2: + return 1 if n != 0 else 0 + return self.tribonacci(n - 1) + self.tribonacci(n - 2) + self.tribonacci(n - 3) +``` + +```java +public class Solution { + public int tribonacci(int n) { + if (n <= 2) { + return n == 0 ? 0 : 1; + } + return tribonacci(n - 1) + tribonacci(n - 2) + tribonacci(n - 3); + } +} +``` + +```cpp +class Solution { +public: + int tribonacci(int n) { + if (n <= 2) { + return n == 0 ? 0 : 1; + } + return tribonacci(n - 1) + tribonacci(n - 2) + tribonacci(n - 3); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + tribonacci(n) { + if (n <= 2) { + return n === 0 ? 0 : 1; + } + return this.tribonacci(n - 1) + this.tribonacci(n - 2) + this.tribonacci(n - 3); + } +} +``` + +```csharp +public class Solution { + public int Tribonacci(int n) { + if (n == 0) return 0; + if (n <= 2) return 1; + return Tribonacci(n - 1) + Tribonacci(n - 2) + Tribonacci(n - 3); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(3 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def __init__(self): + self.dp = {} + + def tribonacci(self, n: int) -> int: + if n <= 2: + return 1 if n != 0 else 0 + if n in self.dp: + return self.dp[n] + + self.dp[n] = self.tribonacci(n - 1) + self.tribonacci(n - 2) + self.tribonacci(n - 3) + return self.dp[n] +``` + +```java +public class Solution { + private HashMap dp = new HashMap<>(); + + public int tribonacci(int n) { + if (n <= 2) { + return n == 0 ? 0 : 1; + } + if (dp.containsKey(n)) { + return dp.get(n); + } + + dp.put(n, tribonacci(n - 1) + tribonacci(n - 2) + tribonacci(n - 3)); + return dp.get(n); + } +} +``` + +```cpp +class Solution { + unordered_map dp; + +public: + int tribonacci(int n) { + if (n <= 2) { + return n == 0 ? 0 : 1; + } + if (dp.count(n)) return dp[n]; + + dp[n] = tribonacci(n - 1) + tribonacci(n - 2) + tribonacci(n - 3); + return dp[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @constructor + */ + constructor() { + this.dp = new Map(); + } + + /** + * @param {number} n + * @return {number} + */ + tribonacci(n) { + if (n <= 2) { + return n === 0 ? 0 : 1; + } + if (this.dp.has(n)) { + return this.dp.get(n); + } + const result = this.tribonacci(n - 1) + this.tribonacci(n - 2) + this.tribonacci(n - 3); + this.dp.set(n, result); + return result; + } +} +``` + +```csharp +public class Solution { + private Dictionary dp = new Dictionary(); + + public int Tribonacci(int n) { + if (n == 0) return 0; + if (n <= 2) return 1; + if (dp.ContainsKey(n)) return dp[n]; + + dp[n] = Tribonacci(n - 1) + Tribonacci(n - 2) + Tribonacci(n - 3); + return dp[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def tribonacci(self, n: int) -> int: + if n <= 2: + return 1 if n != 0 else 0 + + dp = [0] * (n + 1) + dp[1] = dp[2] = 1 + for i in range(3, n + 1): + dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3] + return dp[n] +``` + +```java +public class Solution { + public int tribonacci(int n) { + if (n <= 2) { + return n == 0 ? 0 : 1; + } + + int[] dp = new int[n + 1]; + dp[1] = dp[2] = 1; + for (int i = 3; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]; + } + return dp[n]; + } +} +``` + +```cpp +class Solution { +public: + int tribonacci(int n) { + if (n <= 2) { + return n == 0 ? 0 : 1; + } + + vector dp(n + 1, 0); + dp[1] = dp[2] = 1; + for (int i = 3; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]; + } + return dp[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + tribonacci(n) { + if (n <= 2) { + return n === 0 ? 0 : 1; + } + + const dp = new Array(n + 1).fill(0); + dp[1] = dp[2] = 1; + for (let i = 3; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]; + } + return dp[n]; + } +} +``` + +```csharp +public class Solution { + public int Tribonacci(int n) { + if (n == 0) return 0; + if (n <= 2) return 1; + + int[] dp = new int[n + 1]; + dp[0] = 0; + dp[1] = dp[2] = 1; + + for (int i = 3; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2] + dp[i - 3]; + } + + return dp[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def tribonacci(self, n: int) -> int: + t = [0, 1, 1] + + if n < 3: + return t[n] + + for i in range(3, n + 1): + t[i % 3] = sum(t) + return t[n % 3] +``` + +```java +public class Solution { + public int tribonacci(int n) { + int t[] = {0, 1, 1}; + if (n < 3) return t[n]; + + for (int i = 3; i <= n; ++i) { + t[i % 3] = t[0] + t[1] + t[2]; + } + return t[n % 3]; + } +} +``` + +```cpp +class Solution { +public: + int tribonacci(int n) { + int t[] = {0, 1, 1}; + if (n < 3) return t[n]; + + for (int i = 3; i <= n; ++i) { + t[i % 3] = t[0] + t[1] + t[2]; + } + return t[n % 3]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + tribonacci(n) { + const t = [0, 1, 1] + if (n < 3) return t[n]; + + for (let i = 3; i <= n; ++i) { + t[i % 3] = t[0] + t[1] + t[2]; + } + return t[n % 3]; + } +} +``` + +```csharp +public class Solution { + public int Tribonacci(int n) { + int[] t = {0, 1, 1}; + if (n < 3) return t[n]; + + for (int i = 3; i <= n; i++) { + t[i % 3] = t[0] + t[1] + t[2]; + } + + return t[n % 3]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/naming-a-company.md b/articles/naming-a-company.md new file mode 100644 index 000000000..dbd85775d --- /dev/null +++ b/articles/naming-a-company.md @@ -0,0 +1,495 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def distinctNames(self, ideas: List[str]) -> int: + n = len(ideas) + res = set() + ideasSet = set(ideas) + + for i in range(n): + for j in range(i + 1, n): + A, B = ideas[j][0] + ideas[i][1:], ideas[i][0] + ideas[j][1:] + if A not in ideasSet and B not in ideasSet: + res.add(A + ' ' + B) + res.add(B + ' ' + A) + + return len(res) +``` + +```java +public class Solution { + public long distinctNames(String[] ideas) { + int n = ideas.length; + Set res = new HashSet<>(); + Set ideasSet = new HashSet<>(Arrays.asList(ideas)); + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + String A = ideas[j].charAt(0) + ideas[i].substring(1); + String B = ideas[i].charAt(0) + ideas[j].substring(1); + + if (!ideasSet.contains(A) && !ideasSet.contains(B)) { + res.add(A + " " + B); + res.add(B + " " + A); + } + } + } + + return res.size(); + } +} +``` + +```cpp +class Solution { +public: + long long distinctNames(vector& ideas) { + int n = ideas.size(); + unordered_set res; + unordered_set ideasSet(ideas.begin(), ideas.end()); + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + string A = ideas[j][0] + ideas[i].substr(1); + string B = ideas[i][0] + ideas[j].substr(1); + + if (!ideasSet.count(A) && !ideasSet.count(B)) { + res.insert(A + " " + B); + res.insert(B + " " + A); + } + } + } + + return res.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} ideas + * @return {number} + */ + distinctNames(ideas) { + let n = ideas.length; + let res = new Set(); + let ideasSet = new Set(ideas); + + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + let A = ideas[j][0] + ideas[i].slice(1); + let B = ideas[i][0] + ideas[j].slice(1); + + if (!ideasSet.has(A) && !ideasSet.has(B)) { + res.add(A + " " + B); + res.add(B + " " + A); + } + } + } + + return res.size; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n ^ 2)$ +* Space complexity: $O(m * n ^ 2)$ + +> Where $n$ is the size of the array $ideas$ and $m$ is the average length of the strings. + +--- + +## 2. Group By First Letter (Hash Map) + +::tabs-start + +```python +class Solution: + def distinctNames(self, ideas: List[str]) -> int: + wordMap = collections.defaultdict(set) + for w in ideas: + wordMap[w[0]].add(w[1:]) + + res = 0 + for char1 in wordMap: + for char2 in wordMap: + if char1 == char2: + continue + + intersect = sum(1 for w in wordMap[char1] if w in wordMap[char2]) + distinct1 = len(wordMap[char1]) - intersect + distinct2 = len(wordMap[char2]) - intersect + res += distinct1 * distinct2 + + return res +``` + +```java +public class Solution { + public long distinctNames(String[] ideas) { + Map> wordMap = new HashMap<>(); + for (String word : ideas) { + wordMap.computeIfAbsent( + word.charAt(0), k -> new HashSet<>()).add(word.substring(1) + ); + } + + long res = 0; + for (char char1 : wordMap.keySet()) { + for (char char2 : wordMap.keySet()) { + if (char1 == char2) continue; + + int intersect = 0; + for (String w : wordMap.get(char1)) { + if (wordMap.get(char2).contains(w)) { + intersect++; + } + } + + int distinct1 = wordMap.get(char1).size() - intersect; + int distinct2 = wordMap.get(char2).size() - intersect; + res += distinct1 * 1L * distinct2; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long distinctNames(vector& ideas) { + unordered_map> wordMap; + for (const string& word : ideas) { + wordMap[word[0]].insert(word.substr(1)); + } + + long long res = 0; + for (auto& [char1, set1] : wordMap) { + for (auto& [char2, set2] : wordMap) { + if (char1 == char2) continue; + + int intersect = 0; + for (const string& w : set1) { + if (set2.count(w)) { + intersect++; + } + } + + int distinct1 = set1.size() - intersect; + int distinct2 = set2.size() - intersect; + res += distinct1 * 1LL * distinct2; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} ideas + * @return {number} + */ + distinctNames(ideas) { + const wordMap = new Map(); + for (let word of ideas) { + let key = word[0]; + if (!wordMap.has(key)) { + wordMap.set(key, new Set()); + } + wordMap.get(key).add(word.slice(1)); + } + + let res = 0; + for (let [char1, set1] of wordMap) { + for (let [char2, set2] of wordMap) { + if (char1 === char2) continue; + + let intersect = 0; + for (let w of set1) { + if (set2.has(w)) { + intersect++; + } + } + + let distinct1 = set1.size - intersect; + let distinct2 = set2.size - intersect; + res += distinct1 * distinct2; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $n$ is the size of the array $ideas$ and $m$ is the average length of the strings. + +--- + +## 3. Group By First Letter (Array) + +::tabs-start + +```python +class Solution: + def distinctNames(self, ideas: List[str]) -> int: + suffixes = [set() for _ in range(26)] + for w in ideas: + suffixes[ord(w[0]) - ord('a')].add(w[1:]) + + res = 0 + for i in range(26): + for j in range(i + 1, 26): + intersect = len(suffixes[i] & suffixes[j]) + res += 2 * (len(suffixes[i]) - intersect) * (len(suffixes[j]) - intersect) + + return res +``` + +```java +public class Solution { + public long distinctNames(String[] ideas) { + Set[] suffixes = new HashSet[26]; + for (int i = 0; i < 26; i++) { + suffixes[i] = new HashSet<>(); + } + for (String w : ideas) { + suffixes[w.charAt(0) - 'a'].add(w.substring(1)); + } + + long res = 0; + for (int i = 0; i < 26; i++) { + for (int j = i + 1; j < 26; j++) { + int intersect = 0; + for (String s : suffixes[i]) { + if (suffixes[j].contains(s)) { + intersect++; + } + } + res += 2L * (suffixes[i].size() - intersect) * (suffixes[j].size() - intersect); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + long long distinctNames(vector& ideas) { + unordered_set suffixes[26]; + for (const string& w : ideas) { + suffixes[w[0] - 'a'].insert(w.substr(1)); + } + + long long res = 0; + for (int i = 0; i < 26; i++) { + for (int j = i + 1; j < 26; j++) { + int intersect = 0; + for (const string& s : suffixes[i]) { + if (suffixes[j].count(s)) { + intersect++; + } + } + res += 2LL * (suffixes[i].size() - intersect) * (suffixes[j].size() - intersect); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} ideas + * @return {number} + */ + distinctNames(ideas) { + const suffixes = Array.from({ length: 26 }, () => new Set()); + for (let w of ideas) { + suffixes[w.charCodeAt(0) - 97].add(w.slice(1)); + } + + let res = 0; + for (let i = 0; i < 26; i++) { + for (let j = i + 1; j < 26; j++) { + let intersect = 0; + for (let s of suffixes[i]) { + if (suffixes[j].has(s)) { + intersect++; + } + } + res += 2 * (suffixes[i].size - intersect) * (suffixes[j].size - intersect); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $n$ is the size of the array $ideas$ and $m$ is the average length of the strings. + +--- + +## 4. Counting + +::tabs-start + +```python +class Solution: + def distinctNames(self, ideas: List[str]) -> int: + mp = defaultdict(lambda: [False] * 26) + count = [[0] * 26 for _ in range(26)] + res = 0 + + for s in ideas: + first_char = ord(s[0]) - ord('a') + suffix = s[1:] + mp[suffix][first_char] = True + + for suffix, arr in mp.items(): + for i in range(26): + if arr[i]: + for j in range(26): + if not arr[j]: + count[i][j] += 1 + res += count[j][i] + + return 2 * res +``` + +```java +public class Solution { + public long distinctNames(String[] ideas) { + Map mp = new HashMap<>(); + int[][] count = new int[26][26]; + long res = 0; + + for (String s : ideas) { + int firstChar = s.charAt(0) - 'a'; + String suffix = s.substring(1); + mp.putIfAbsent(suffix, new boolean[26]); + mp.get(suffix)[firstChar] = true; + } + + for (boolean[] arr : mp.values()) { + for (int i = 0; i < 26; i++) { + if (arr[i]) { + for (int j = 0; j < 26; j++) { + if (!arr[j]) { + count[i][j]++; + res += count[j][i]; + } + } + } + } + } + return 2 * res; + } +} +``` + +```cpp +class Solution { +public: + long long distinctNames(vector& ideas) { + unordered_map> mp; + long long count[26][26] = {}; + long long res = 0; + + for (const string& s : ideas) { + int firstChar = s[0] - 'a'; + string suffix = s.substr(1); + mp[suffix][firstChar] = true; + } + + for (auto& [suffix, arr] : mp) { + for (int i = 0; i < 26; i++) { + if (arr[i]) { + for (int j = 0; j < 26; j++) { + if (!arr[j]) { + count[i][j]++; + res += count[j][i]; + } + } + } + } + } + return 2 * res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} ideas + * @return {number} + */ + distinctNames(ideas) { + const mp = new Map(); + const count = Array.from({ length: 26 }, () => Array(26).fill(0)); + let res = 0; + + for (const s of ideas) { + const firstChar = s.charCodeAt(0) - 97; + const suffix = s.slice(1); + if (!mp.has(suffix)) mp.set(suffix, new Array(26).fill(false)); + mp.get(suffix)[firstChar] = true; + } + + for (const arr of mp.values()) { + for (let i = 0; i < 26; i++) { + if (arr[i]) { + for (let j = 0; j < 26; j++) { + if (!arr[j]) { + count[i][j]++; + res += count[j][i]; + } + } + } + } + } + return 2 * res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $n$ is the size of the array $ideas$ and $m$ is the average length of the strings. \ No newline at end of file diff --git a/articles/network-delay-time.md b/articles/network-delay-time.md new file mode 100644 index 000000000..ae9fb93fc --- /dev/null +++ b/articles/network-delay-time.md @@ -0,0 +1,1359 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def networkDelayTime(self, times: List[List[int]], n: int, k: int) -> int: + adj = defaultdict(list) + for u, v, w in times: + adj[u].append((v, w)) + + dist = {node: float("inf") for node in range(1, n + 1)} + + def dfs(node, time): + if time >= dist[node]: + return + + dist[node] = time + for nei, w in adj[node]: + dfs(nei, time + w) + + dfs(k, 0) + res = max(dist.values()) + return res if res < float('inf') else -1 +``` + +```java +public class Solution { + public int networkDelayTime(int[][] times, int n, int k) { + Map> adj = new HashMap<>(); + for (int[] time : times) { + adj.computeIfAbsent(time[0], + x -> new ArrayList<>()).add(new int[]{time[1], time[2]}); + } + + Map dist = new HashMap<>(); + for (int i = 1; i <= n; i++) dist.put(i, Integer.MAX_VALUE); + + dfs(k, 0, adj, dist); + int res = Collections.max(dist.values()); + return res == Integer.MAX_VALUE ? -1 : res; + } + + private void dfs(int node, int time, + Map> adj, + Map dist) { + if (time >= dist.get(node)) return; + dist.put(node, time); + if (!adj.containsKey(node)) return; + for (int[] edge : adj.get(node)) { + dfs(edge[0], time + edge[1], adj, dist); + } + } +} +``` + +```cpp +class Solution { +public: + int networkDelayTime(vector>& times, int n, int k) { + unordered_map>> adj; + for (auto& time : times) { + adj[time[0]].emplace_back(time[1], time[2]); + } + + vector dist(n + 1, INT_MAX); + dfs(k, 0, adj, dist); + + int res = *max_element(dist.begin() + 1, dist.end()); + return res == INT_MAX ? -1 : res; + } + +private: + void dfs(int node, int time, + unordered_map>>& adj, + vector& dist) { + if (time >= dist[node]) return; + dist[node] = time; + for (auto& [nei, w] : adj[node]) { + dfs(nei, time + w, adj, dist); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} times + * @param {number} n + * @param {number} k + * @return {number} + */ + networkDelayTime(times, n, k) { + const adj = {}; + for (const [u, v, w] of times) { + if (!adj[u]) adj[u] = []; + adj[u].push([v, w]); + } + + const dist = Array(n + 1).fill(Infinity); + const dfs = (node, time) => { + if (time >= dist[node]) return; + dist[node] = time; + if (!adj[node]) return; + for (const [nei, w] of adj[node]) { + dfs(nei, time + w); + } + } + + dfs(k, 0); + const res = Math.max(...dist.slice(1)); + return res === Infinity ? -1 : res; + } +} +``` + +```csharp +public class Solution { + public int NetworkDelayTime(int[][] times, int n, int k) { + var adj = new Dictionary>(); + foreach (var time in times) { + if (!adj.ContainsKey(time[0])) { + adj[time[0]] = new List(); + } + adj[time[0]].Add(new int[] { time[1], time[2] }); + } + + var dist = new Dictionary(); + for (int i = 1; i <= n; i++) dist[i] = int.MaxValue; + + Dfs(k, 0, adj, dist); + int res = dist.Values.Max(); + return res == int.MaxValue ? -1 : res; + } + + private void Dfs(int node, int time, + Dictionary> adj, + Dictionary dist) { + if (time >= dist[node]) return; + dist[node] = time; + if (!adj.ContainsKey(node)) return; + foreach (var edge in adj[node]) { + Dfs(edge[0], time + edge[1], adj, dist); + } + } +} +``` + +```go +func networkDelayTime(times [][]int, n int, k int) int { + adj := make(map[int][][]int) + for _, time := range times { + u, v, w := time[0], time[1], time[2] + adj[u] = append(adj[u], []int{v, w}) + } + + dist := make(map[int]int) + for i := 1; i <= n; i++ { + dist[i] = math.MaxInt32 + } + + var dfs func(int, int) + dfs = func(node int, time int) { + if time >= dist[node] { + return + } + dist[node] = time + for _, edge := range adj[node] { + nei, w := edge[0], edge[1] + dfs(nei, time+w) + } + } + + dfs(k, 0) + res := 0 + for _, time := range dist { + if time == math.MaxInt32 { + return -1 + } + if time > res { + res = time + } + } + return res +} +``` + +```kotlin +class Solution { + fun networkDelayTime(times: Array, n: Int, k: Int): Int { + val adj = HashMap>>() + for (time in times) { + val (u, v, w) = time + adj.computeIfAbsent(u) { mutableListOf() }.add(v to w) + } + + val dist = HashMap().apply { + for (node in 1..n) { + this[node] = Int.MAX_VALUE + } + } + + fun dfs(node: Int, time: Int) { + if (time >= dist[node]!!) return + dist[node] = time + for ((nei, w) in adj[node] ?: emptyList()) { + dfs(nei, time + w) + } + } + + dfs(k, 0) + val res = dist.values.maxOrNull() ?: Int.MAX_VALUE + return if (res == Int.MAX_VALUE) -1 else res + } +} +``` + +```swift +class Solution { + func networkDelayTime(_ times: [[Int]], _ n: Int, _ k: Int) -> Int { + var adj = [Int: [(Int, Int)]]() + for time in times { + let u = time[0], v = time[1], w = time[2] + adj[u, default: []].append((v, w)) + } + + var dist = [Int: Int]() + for node in 1...n { + dist[node] = Int.max + } + + func dfs(_ node: Int, _ time: Int) { + if time >= dist[node]! { + return + } + + dist[node] = time + if let neighbors = adj[node] { + for (nei, w) in neighbors { + dfs(nei, time + w) + } + } + } + + dfs(k, 0) + let res = dist.values.max()! + return res == Int.max ? -1 : res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V * E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 2. Floyd Warshall Algorithm + +::tabs-start + +```python +class Solution: + def networkDelayTime(self, times: List[List[int]], n: int, k: int) -> int: + inf = float('inf') + dist = [[inf] * n for _ in range(n)] + + for u, v, w in times: + dist[u-1][v-1] = w + for i in range(n): + dist[i][i] = 0 + + for mid in range(n): + for i in range(n): + for j in range(n): + dist[i][j] = min(dist[i][j], dist[i][mid] + dist[mid][j]) + + res = max(dist[k-1]) + return res if res < inf else -1 +``` + +```java +public class Solution { + public int networkDelayTime(int[][] times, int n, int k) { + int inf = Integer.MAX_VALUE / 2; + int[][] dist = new int[n][n]; + + for (int i = 0; i < n; i++) { + Arrays.fill(dist[i], inf); + dist[i][i] = 0; + } + + for (int[] time : times) { + int u = time[0] - 1, v = time[1] - 1, w = time[2]; + dist[u][v] = w; + } + + for (int mid = 0; mid < n; mid++) + for (int i = 0; i < n; i++) + for (int j = 0; j < n; j++) + dist[i][j] = Math.min(dist[i][j], + dist[i][mid] + dist[mid][j]); + + int res = Arrays.stream(dist[k-1]).max().getAsInt(); + return res == inf ? -1 : res; + } +} +``` + +```cpp +class Solution { +public: + int networkDelayTime(vector>& times, int n, int k) { + int inf = INT_MAX / 2; + vector> dist(n, vector(n, inf)); + + for (int i = 0; i < n; i++) + dist[i][i] = 0; + + for (auto& time : times) { + int u = time[0] - 1, v = time[1] - 1, w = time[2]; + dist[u][v] = w; + } + + for (int mid = 0; mid < n; mid++) + for (int i = 0; i < n; i++) + for (int j = 0; j < n; j++) + dist[i][j] = min(dist[i][j], + dist[i][mid] + dist[mid][j]); + + int res = *max_element(dist[k-1].begin(), dist[k-1].end()); + return res == inf ? -1 : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} times + * @param {number} n + * @param {number} k + * @return {number} + */ + networkDelayTime(times, n, k) { + const inf = Infinity; + const dist = Array.from({ length: n }, () => + Array(n).fill(inf)); + + for (let i = 0; i < n; i++) { + dist[i][i] = 0; + } + + for (const [u, v, w] of times) { + dist[u - 1][v - 1] = w; + } + + for (let mid = 0; mid < n; mid++) + for (let i = 0; i < n; i++) + for (let j = 0; j < n; j++) + dist[i][j] = Math.min(dist[i][j], + dist[i][mid] + dist[mid][j]); + + const res = Math.max(...dist[k - 1]); + return res === inf ? -1 : res; + } +} +``` + +```csharp +public class Solution { + public int NetworkDelayTime(int[][] times, int n, int k) { + int inf = int.MaxValue / 2; + int[,] dist = new int[n, n]; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + dist[i, j] = i == j ? 0 : inf; + } + } + + foreach (var time in times) { + int u = time[0] - 1, v = time[1] - 1, w = time[2]; + dist[u, v] = w; + } + + for (int mid = 0; mid < n; mid++) + for (int i = 0; i < n; i++) + for (int j = 0; j < n; j++) + dist[i, j] = Math.Min(dist[i, j], + dist[i, mid] + dist[mid, j]); + + int res = Enumerable.Range(0, n).Select(i => dist[k-1, i]).Max(); + return res == inf ? -1 : res; + } +} +``` + +```go +func networkDelayTime(times [][]int, n int, k int) int { + inf := math.MaxInt32 + dist := make([][]int, n) + for i := range dist { + dist[i] = make([]int, n) + for j := range dist[i] { + dist[i][j] = inf + } + } + + for _, time := range times { + u, v, w := time[0]-1, time[1]-1, time[2] + dist[u][v] = w + } + for i := 0; i < n; i++ { + dist[i][i] = 0 + } + + for mid := 0; mid < n; mid++ { + for i := 0; i < n; i++ { + for j := 0; j < n; j++ { + dist[i][j] = min(dist[i][j], dist[i][mid]+dist[mid][j]) + } + } + } + + res := 0 + for i := 0; i < n; i++ { + if dist[k-1][i] == inf { + return -1 + } + res = max(res, dist[k-1][i]) + } + return res +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun networkDelayTime(times: Array, n: Int, k: Int): Int { + val inf = (Int.MAX_VALUE / 2).toInt() + val dist = Array(n) { IntArray(n) { inf } } + + for (i in 0 until n) { + dist[i].fill(inf) + dist[i][i] = 0 + } + + for (time in times) { + val (u, v, w) = time + dist[u - 1][v - 1] = w + } + + for (mid in 0 until n) { + for (i in 0 until n) { + for (j in 0 until n) { + dist[i][j] = minOf(dist[i][j], dist[i][mid] + dist[mid][j]) + } + } + } + + val maxDelay = dist[k - 1].maxOrNull() + return if (maxDelay == inf) -1 else maxDelay!! + } +} +``` + +```swift +class Solution { + func networkDelayTime(_ times: [[Int]], _ n: Int, _ k: Int) -> Int { + let inf = Int.max / 2 + var dist = Array(repeating: Array(repeating: inf, count: n), count: n) + + for time in times { + let u = time[0] - 1, v = time[1] - 1, w = time[2] + dist[u][v] = w + } + for i in 0..= inf ? -1 : res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V ^ 3)$ +* Space complexity: $O(V ^ 2)$ + +> Where $V$ is the number of vertices. + +--- + +## 3. Bellman Ford Algorithm + +::tabs-start + +```python +class Solution: + def networkDelayTime(self, times: List[List[int]], n: int, k: int) -> int: + dist = [float('inf')] * n + dist[k - 1] = 0 + for _ in range(n - 1): + for u, v, w in times: + if dist[u - 1] + w < dist[v - 1]: + dist[v - 1] = dist[u - 1] + w + max_dist = max(dist) + return max_dist if max_dist < float('inf') else -1 +``` + +```java +public class Solution { + public int networkDelayTime(int[][] times, int n, int k) { + int[] dist = new int[n]; + Arrays.fill(dist, Integer.MAX_VALUE); + dist[k - 1] = 0; + + for (int i = 0; i < n - 1; i++) { + for (int[] time : times) { + int u = time[0] - 1, v = time[1] - 1, w = time[2]; + if (dist[u] != Integer.MAX_VALUE && dist[u] + w < dist[v]) { + dist[v] = dist[u] + w; + } + } + } + + int maxDist = Arrays.stream(dist).max().getAsInt(); + return maxDist == Integer.MAX_VALUE ? -1 : maxDist; + } +} +``` + +```cpp +class Solution { +public: + int networkDelayTime(vector>& times, int n, int k) { + vector dist(n, INT_MAX); + dist[k - 1] = 0; + + for (int i = 0; i < n - 1; ++i) { + for (const auto& time : times) { + int u = time[0] - 1, v = time[1] - 1, w = time[2]; + if (dist[u] != INT_MAX && dist[u] + w < dist[v]) { + dist[v] = dist[u] + w; + } + } + } + + int maxDist = *max_element(dist.begin(), dist.end()); + return maxDist == INT_MAX ? -1 : maxDist; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} times + * @param {number} n + * @param {number} k + * @return {number} + */ + networkDelayTime(times, n, k) { + let dist = new Array(n).fill(Infinity); + dist[k - 1] = 0; + + for (let i = 0; i < n - 1; i++) { + for (const [u, v, w] of times) { + if (dist[u - 1] + w < dist[v - 1]) { + dist[v - 1] = dist[u - 1] + w; + } + } + } + + const maxDist = Math.max(...dist); + return maxDist === Infinity ? -1 : maxDist; + } +} +``` + +```csharp +public class Solution { + public int NetworkDelayTime(int[][] times, int n, int k) { + int[] dist = Enumerable.Repeat(int.MaxValue, n).ToArray(); + dist[k - 1] = 0; + + for (int i = 0; i < n - 1; i++) { + foreach (var time in times) { + int u = time[0] - 1, v = time[1] - 1, w = time[2]; + if (dist[u] != int.MaxValue && dist[u] + w < dist[v]) { + dist[v] = dist[u] + w; + } + } + } + + int maxDist = dist.Max(); + return maxDist == int.MaxValue ? -1 : maxDist; + } +} +``` + +```go +func networkDelayTime(times [][]int, n int, k int) int { + dist := make([]int, n) + for i := range dist { + dist[i] = 1 << 31 - 1 + } + dist[k-1] = 0 + + for _ = range n - 1 { + for _, time := range times { + u, v, w := time[0]-1, time[1]-1, time[2] + if dist[u] + w < dist[v] { + dist[v] = dist[u] + w + } + } + } + + maxDist := 0 + for _, d := range dist { + if d == 1<<31-1 { + return -1 + } + if d > maxDist { + maxDist = d + } + } + + return maxDist +} +``` + +```kotlin +class Solution { + fun networkDelayTime(times: Array, n: Int, k: Int): Int { + val dist = IntArray(n) { Int.MAX_VALUE } + dist[k - 1] = 0 + + for (i in 0 until n - 1) { + for (time in times) { + val (u, v, w) = time + if (dist[u - 1] != Int.MAX_VALUE && dist[u - 1] + w < dist[v - 1]) { + dist[v - 1] = dist[u - 1] + w + } + } + } + + val maxDist = dist.maxOrNull() + return if (maxDist == Int.MAX_VALUE) -1 else maxDist!! + } +} +``` + +```swift +class Solution { + func networkDelayTime(_ times: [[Int]], _ n: Int, _ k: Int) -> Int { + var dist = Array(repeating: Int.max, count: n) + dist[k - 1] = 0 + + for _ in 0.. Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 4. Shortest Path Faster Algorithm + +::tabs-start + +```python +class Solution: + def networkDelayTime(self, times, n, k): + adj = defaultdict(list) + for u, v, w in times: + adj[u].append((v, w)) + + dist = {node: float("inf") for node in range(1, n + 1)} + q = deque([(k, 0)]) + dist[k] = 0 + + while q: + node, time = q.popleft() + if dist[node] < time: + continue + for nei, w in adj[node]: + if time + w < dist[nei]: + dist[nei] = time + w + q.append((nei, time + w)) + + res = max(dist.values()) + return res if res < float('inf') else -1 +``` + +```java +public class Solution { + public int networkDelayTime(int[][] times, int n, int k) { + Map> adj = new HashMap<>(); + for (int i = 1; i <= n; i++) adj.put(i, new ArrayList<>()); + for (int[] time : times) { + adj.get(time[0]).add(new int[] {time[1], time[2]}); + } + Map dist = new HashMap<>(); + for (int i = 1; i <= n; i++) dist.put(i, Integer.MAX_VALUE); + dist.put(k, 0); + + Queue q = new LinkedList<>(); + q.offer(new int[] {k, 0}); + + while (!q.isEmpty()) { + int[] curr = q.poll(); + int node = curr[0], time = curr[1]; + if (dist.get(node) < time) { + continue; + } + for (int[] nei : adj.get(node)) { + int nextNode = nei[0], weight = nei[1]; + if (time + weight < dist.get(nextNode)) { + dist.put(nextNode, time + weight); + q.offer(new int[] {nextNode, time + weight}); + } + } + } + + int res = Collections.max(dist.values()); + return res == Integer.MAX_VALUE ? -1 : res; + } +} +``` + +```cpp +class Solution { +public: + int networkDelayTime(vector>& times, int n, int k) { + unordered_map>> adj; + for (const auto& time : times) { + adj[time[0]].emplace_back(time[1], time[2]); + } + + unordered_map dist; + for (int i = 1; i <= n; ++i) dist[i] = INT_MAX; + dist[k] = 0; + + queue> q; + q.emplace(k, 0); + + while (!q.empty()) { + auto [node, time] = q.front(); + q.pop(); + if (dist[node] < time) continue; + for (const auto& [nei, w] : adj[node]) { + if (time + w < dist[nei]) { + dist[nei] = time + w; + q.emplace(nei, time + w); + } + } + } + + int res = 0; + for (const auto& [node, time] : dist) { + res = max(res, time); + } + return res == INT_MAX ? -1 : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} times + * @param {number} n + * @param {number} k + * @return {number} + */ + networkDelayTime(times, n, k) { + const adj = {}; + for (let i = 1; i <= n; i++) adj[i] = []; + for (const [u, v, w] of times) { + adj[u].push([v, w]); + } + + const dist = {}; + for (let i = 1; i <= n; i++) dist[i] = Infinity; + dist[k] = 0; + + const q = new Queue([[k, 0]]); + + while (!q.isEmpty()) { + const [node, time] = q.pop(); + if (dist[node] < time) continue; + for (const [nei, w] of adj[node]) { + if (time + w < dist[nei]) { + dist[nei] = time + w; + q.push([nei, time + w]); + } + } + } + + let res = Math.max(...Object.values(dist)); + return res === Infinity ? -1 : res; + } +} +``` + +```csharp +public class Solution { + public int NetworkDelayTime(int[][] times, int n, int k) { + var adj = new Dictionary>(); + for (int i = 1; i <= n; i++) adj[i] = new List(); + foreach (var time in times) { + adj[time[0]].Add(new int[] {time[1], time[2]}); + } + + var dist = new Dictionary(); + for (int i = 1; i <= n; i++) dist[i] = int.MaxValue; + dist[k] = 0; + + var q = new Queue(); + q.Enqueue(new int[] {k, 0}); + + while (q.Count > 0) { + var curr = q.Dequeue(); + int node = curr[0], time = curr[1]; + if (dist[node] < time) continue; + foreach (var nei in adj[node]) { + int nextNode = nei[0], weight = nei[1]; + if (time + weight < dist[nextNode]) { + dist[nextNode] = time + weight; + q.Enqueue(new int[] {nextNode, time + weight}); + } + } + } + + int res = 0; + foreach (var time in dist.Values) { + res = Math.Max(res, time); + } + return res == int.MaxValue ? -1 : res; + } +} +``` + +```go +func networkDelayTime(times [][]int, n int, k int) int { + adj := make(map[int][][]int) + for _, edge := range times { + u, v, w := edge[0], edge[1], edge[2] + adj[u] = append(adj[u], []int{v, w}) + } + + dist := make(map[int]int) + for i := 1; i <= n; i++ { + dist[i] = math.MaxInt + } + dist[k] = 0 + + q := [][]int{{k, 0}} + + for len(q) > 0 { + node, time := q[0][0], q[0][1] + q = q[1:] + + if time > dist[node] { + continue + } + for _, nei := range adj[node] { + v, w := nei[0], nei[1] + if time+w < dist[v] { + dist[v] = time + w + q = append(q, []int{v, time + w}) + } + } + } + + res := -1 + for _, d := range dist { + if d == math.MaxInt { + return -1 + } + if d > res { + res = d + } + } + return res +} +``` + +```kotlin +class Solution { + fun networkDelayTime(times: Array, n: Int, k: Int): Int { + val adj = HashMap>() + for (i in 1..n) adj[i] = mutableListOf() + for (time in times) { + adj[time[0]]?.add(intArrayOf(time[1], time[2])) + } + val dist = HashMap() + for (i in 1..n) dist[i] = Int.MAX_VALUE + dist[k] = 0 + + val q: Queue = LinkedList() + q.offer(intArrayOf(k, 0)) + + while (q.isNotEmpty()) { + val (node, time) = q.poll() + if (dist[node]!! < time) continue + adj[node]?.forEach { (nextNode, weight) -> + if (time + weight < dist[nextNode]!!) { + dist[nextNode] = time + weight + q.offer(intArrayOf(nextNode, time + weight)) + } + } + } + + val res = dist.values.maxOrNull() ?: Int.MAX_VALUE + return if (res == Int.MAX_VALUE) -1 else res + } +} +``` + +```swift +class Solution { + func networkDelayTime(_ times: [[Int]], _ n: Int, _ k: Int) -> Int { + var adj = [Int: [(Int, Int)]]() + for time in times { + let u = time[0], v = time[1], w = time[2] + adj[u, default: []].append((v, w)) + } + + var dist = [Int: Int]() + for node in 1...n { + dist[node] = Int.max + } + dist[k] = 0 + + var queue = Deque<(Int, Int)>() + queue.append((k, 0)) + + while !queue.isEmpty { + let (node, time) = queue.popFirst()! + if dist[node]! < time { + continue + } + if let neighbors = adj[node] { + for (nei, w) in neighbors { + if time + w < dist[nei]! { + dist[nei] = time + w + queue.append((nei, time + w)) + } + } + } + } + + let res = dist.values.max()! + return res == Int.max ? -1 : res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ in average case, $O(V * E)$ in worst case. +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 5. Dijkstra's Algorithm + +::tabs-start + +```python +class Solution: + def networkDelayTime(self, times: List[List[int]], n: int, k: int) -> int: + edges = collections.defaultdict(list) + for u, v, w in times: + edges[u].append((v, w)) + + minHeap = [(0, k)] + visit = set() + t = 0 + while minHeap: + w1, n1 = heapq.heappop(minHeap) + if n1 in visit: + continue + visit.add(n1) + t = w1 + + for n2, w2 in edges[n1]: + if n2 not in visit: + heapq.heappush(minHeap, (w1 + w2, n2)) + return t if len(visit) == n else -1 +``` + +```java +public class Solution { + public int networkDelayTime(int[][] times, int n, int k) { + Map> edges = new HashMap<>(); + for (int[] time : times) { + edges.computeIfAbsent(time[0], + key -> new ArrayList<>()).add(new int[]{time[1], time[2]}); + } + + PriorityQueue minHeap = new PriorityQueue<>( + Comparator.comparingInt(a -> a[0])); + minHeap.offer(new int[]{0, k}); + + Set visited = new HashSet<>(); + int t = 0; + while (!minHeap.isEmpty()) { + int[] curr = minHeap.poll(); + int w1 = curr[0], n1 = curr[1]; + if (visited.contains(n1)) { + continue; + } + visited.add(n1); + t = w1; + + if (edges.containsKey(n1)) { + for (int[] next : edges.get(n1)) { + int n2 = next[0], w2 = next[1]; + if (!visited.contains(n2)) { + minHeap.offer(new int[]{w1 + w2, n2}); + } + } + } + } + + return visited.size() == n ? t : -1; + } +} +``` + +```cpp +class Solution { +public: + int networkDelayTime(vector>& times, int n, int k) { + unordered_map>> edges; + for (const auto& time : times) { + edges[time[0]].emplace_back(time[1], time[2]); + } + + priority_queue, vector>, greater<>> minHeap; + minHeap.push({0, k}); + + set visited; + int t = 0; + while (!minHeap.empty()) { + auto curr = minHeap.top(); + minHeap.pop(); + int w1 = curr.first, n1 = curr.second; + if (visited.count(n1)) { + continue; + } + visited.insert(n1); + t = w1; + + if (edges.count(n1)) { + for (const auto& next : edges[n1]) { + int n2 = next.first, w2 = next.second; + if (!visited.count(n2)) { + minHeap.push({w1 + w2, n2}); + } + } + } + } + + return visited.size() == n ? t : -1; + } +}; +``` + +```javascript +/** + * const { MinPriorityQueue } = require('@datastructures-js/priority-queue'); + */ + +class Solution { + /** + * @param {number[][]} times + * @param {number} n + * @param {number} k + * @return {number} + */ + networkDelayTime(times, n, k) { + const edges = new Map(); + for (let i = 1; i <= n; i++) { + edges.set(i, []); + } + for (const [u, v, w] of times) { + edges.get(u).push([v, w]); + } + + const minHeap = new MinPriorityQueue(entry => entry[0]); + minHeap.enqueue([0, k]); + + const visit = new Set(); + let t = 0; + + while (!minHeap.isEmpty()) { + const [w1, n1] = minHeap.dequeue(); + if (visit.has(n1)) continue; + visit.add(n1); + t = w1; + + for (const [n2, w2] of edges.get(n1)) { + if (!visit.has(n2)) { + minHeap.enqueue([w1 + w2, n2]); + } + } + } + + return visit.size === n ? t : -1; + } +} +``` + +```csharp +public class Solution { + public int NetworkDelayTime(int[][] times, int n, int k) { + var edges = new Dictionary>(); + foreach (var time in times) { + if (!edges.ContainsKey(time[0])) { + edges[time[0]] = new List(); + } + edges[time[0]].Add(new int[] { time[1], time[2] }); + } + + var pq = new PriorityQueue(); + pq.Enqueue(k, 0); + + var dist = new Dictionary(); + for (int i = 1; i <= n; i++) { + dist[i] = int.MaxValue; + } + dist[k] = 0; + + while (pq.Count > 0) { + // Correctly using TryDequeue to get node and its distance + if (pq.TryDequeue(out int node, out int minDist)) { + if (minDist > dist[node]) { + continue; + } + + if (edges.ContainsKey(node)) { + foreach (var edge in edges[node]) { + var next = edge[0]; + var weight = edge[1]; + var newDist = minDist + weight; + if (newDist < dist[next]) { + dist[next] = newDist; + pq.Enqueue(next, newDist); + } + } + } + } + } + + int result = 0; + for (int i = 1; i <= n; i++) { + if (dist[i] == int.MaxValue) return -1; + result = Math.Max(result, dist[i]); + } + + return result; + } +} +``` + +```go +type Edge struct { + node, weight int +} + +func networkDelayTime(times [][]int, n int, k int) int { + edges := make(map[int][]Edge) + for _, time := range times { + u, v, w := time[0], time[1], time[2] + edges[u] = append(edges[u], Edge{node: v, weight: w}) + } + + pq := priorityqueue.NewWith(func(a, b interface{}) int { + return utils.IntComparator(a.(Edge).weight, b.(Edge).weight) + }) + pq.Enqueue(Edge{node: k, weight: 0}) + + visited := make(map[int]bool) + t := 0 + + for !pq.Empty() { + item, _ := pq.Dequeue() + edge := item.(Edge) + node, time := edge.node, edge.weight + + if visited[node] { + continue + } + visited[node] = true + t = time + + for _, next := range edges[node] { + if !visited[next.node] { + pq.Enqueue(Edge{node: next.node, weight: time + next.weight}) + } + } + } + + if len(visited) == n { + return t + } + return -1 +} +``` + +```kotlin +class Solution { + fun networkDelayTime(times: Array, n: Int, k: Int): Int { + val edges = HashMap>>() + for ((u, v, w) in times) { + edges.computeIfAbsent(u) { mutableListOf() }.add(Pair(v, w)) + } + + val minHeap = PriorityQueue>(compareBy { it.first }) + minHeap.offer(Pair(0, k)) + val visited = HashSet() + var t = 0 + + while (minHeap.isNotEmpty()) { + val (time, node) = minHeap.poll() + if (node in visited) continue + visited.add(node) + t = time + + edges[node]?.forEach { (nextNode, weight) -> + if (nextNode !in visited) { + minHeap.offer(Pair(time + weight, nextNode)) + } + } + } + + return if (visited.size == n) t else -1 + } +} +``` + +```swift +struct Item: Comparable { + let weight: Int + let node: Int + + static func < (lhs: Item, rhs: Item) -> Bool { + return lhs.weight < rhs.weight + } +} + +class Solution { + func networkDelayTime(_ times: [[Int]], _ n: Int, _ k: Int) -> Int { + var edges = [Int: [(Int, Int)]]() + for time in times { + let u = time[0], v = time[1], w = time[2] + edges[u, default: []].append((v, w)) + } + + var minHeap = Heap() + minHeap.insert(Item(weight: 0, node: k)) + var visit = Set() + var t = 0 + + while !minHeap.isEmpty { + let item = minHeap.removeMin() + let w1 = item.weight + let n1 = item.node + + if visit.contains(n1) { + continue + } + visit.insert(n1) + t = w1 + + if let neighbors = edges[n1] { + for (n2, w2) in neighbors { + if !visit.contains(n2) { + minHeap.insert(Item(weight: w1 + w2, node: n2)) + } + } + } + } + return visit.count == n ? t : -1 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(E \log V)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. \ No newline at end of file diff --git a/articles/new-21-game.md b/articles/new-21-game.md new file mode 100644 index 000000000..e65fcd1da --- /dev/null +++ b/articles/new-21-game.md @@ -0,0 +1,485 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def new21Game(self, n: int, k: int, maxPts: int) -> float: + cache = {} + + def dfs(score): + if score >= k: + return 1 if score <= n else 0 + if score in cache: + return cache[score] + + prob = 0 + for i in range(1, maxPts + 1): + prob += dfs(score + i) + + cache[score] = prob / maxPts + return cache[score] + + return dfs(0) +``` + +```java +public class Solution { + private double[] dp; + + public double new21Game(int n, int k, int maxPts) { + dp = new double[k]; + for (int i = 0; i < dp.length; i++) dp[i] = -1.0; + return dfs(0, n, k, maxPts); + } + + private double dfs(int score, int n, int k, int maxPts) { + if (score >= k) { + return score <= n ? 1.0 : 0.0; + } + if (dp[score] != -1.0) { + return dp[score]; + } + + double prob = 0; + for (int i = 1; i <= maxPts; i++) { + prob += dfs(score + i, n, k, maxPts); + } + + dp[score] = prob / maxPts; + return dp[score]; + } +} +``` + +```cpp +class Solution { +private: + vector dp; + +public: + double new21Game(int n, int k, int maxPts) { + dp.resize(k, -1.0); + return dfs(0, n, k, maxPts); + } + +private: + double dfs(int score, int n, int k, int maxPts) { + if (score >= k) { + return score <= n ? 1.0 : 0.0; + } + if (dp[score] != -1.0) { + return dp[score]; + } + + double prob = 0; + for (int i = 1; i <= maxPts; i++) { + prob += dfs(score + i, n, k, maxPts); + } + + dp[score] = prob / maxPts; + return dp[score]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @param {number} maxPts + * @return {number} + */ + new21Game(n, k, maxPts) { + const dp = new Array(k).fill(-1); + + const dfs = (score) => { + if (score >= k) { + return score <= n ? 1 : 0; + } + if (dp[score] !== -1) { + return dp[score]; + } + + let prob = 0; + for (let i = 1; i <= maxPts; i++) { + prob += dfs(score + i); + } + + dp[score] = prob / maxPts; + return dp[score]; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * m)$ +* Space complexity: $O(k)$ + +> Where $k$ is the threshold score, $m$ is the maximum points per draw and $n$ is the upper bound on score. + +--- + +## 2. Dynamic Programming (Top-Down Optimized) + +::tabs-start + +```python +class Solution: + def new21Game(self, n: int, k: int, maxPts: int) -> float: + cache = {} + + def dfs(score): + if score == k - 1: + return min(n - k + 1, maxPts) / maxPts + if score > n: + return 0 + if score >= k: + return 1.0 + + if score in cache: + return cache[score] + + cache[score] = dfs(score + 1) + cache[score] -= (dfs(score + 1 + maxPts) - dfs(score + 1)) / maxPts + return cache[score] + + return dfs(0) +``` + +```java +public class Solution { + private double[] dp; + + public double new21Game(int n, int k, int maxPts) { + dp = new double[k + maxPts]; + for (int i = 0; i < dp.length; i++) dp[i] = -1.0; + return dfs(0, n, k, maxPts); + } + + private double dfs(int score, int n, int k, int maxPts) { + if (score == k - 1) { + return Math.min(n - k + 1, maxPts) / (double) maxPts; + } + if (score > n) { + return 0.0; + } + if (score >= k) { + return 1.0; + } + if (dp[score] != -1.0) { + return dp[score]; + } + + dp[score] = dfs(score + 1, n, k, maxPts); + dp[score] -= (dfs(score + 1 + maxPts, n, k, maxPts) - dfs(score + 1, n, k, maxPts)) / maxPts; + return dp[score]; + } +} +``` + +```cpp +class Solution { +private: + vector dp; + +public: + double new21Game(int n, int k, int maxPts) { + dp.resize(k + maxPts, -1.0); + return dfs(0, n, k, maxPts); + } + +private: + double dfs(int score, int n, int k, int maxPts) { + if (score == k - 1) { + return min(n - k + 1, maxPts) / (double)maxPts; + } + if (score > n) { + return 0.0; + } + if (score >= k) { + return 1.0; + } + if (dp[score] != -1.0) { + return dp[score]; + } + + dp[score] = dfs(score + 1, n, k, maxPts); + dp[score] -= (dfs(score + 1 + maxPts, n, k, maxPts) - dfs(score + 1, n, k, maxPts)) / maxPts; + return dp[score]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @param {number} maxPts + * @return {number} + */ + new21Game(n, k, maxPts) { + const dp = new Array(k + maxPts).fill(-1); + + const dfs = (score) => { + if (score === k - 1) { + return Math.min(n - k + 1, maxPts) / maxPts; + } + if (score > n) { + return 0; + } + if (score >= k) { + return 1.0; + } + if (dp[score] !== -1) { + return dp[score]; + } + + dp[score] = dfs(score + 1); + dp[score] -= (dfs(score + 1 + maxPts) - dfs(score + 1)) / maxPts; + return dp[score]; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k + m)$ +* Space complexity: $O(n)$ + +> Where $k$ is the threshold score, $m$ is the maximum points per draw and $n$ is the upper bound on score. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def new21Game(self, n: int, k: int, maxPts: int) -> float: + dp = [0] * (n + 1) + dp[0] = 1.0 + + for score in range(1, n + 1): + for draw in range(1, maxPts + 1): + if score - draw >= 0 and score - draw < k: + dp[score] += dp[score - draw] / maxPts + + return sum(dp[k:n + 1]) +``` + +```java +public class Solution { + public double new21Game(int n, int k, int maxPts) { + double[] dp = new double[n + 1]; + dp[0] = 1.0; + + for (int score = 1; score <= n; score++) { + for (int draw = 1; draw <= maxPts; draw++) { + if (score - draw >= 0 && score - draw < k) { + dp[score] += dp[score - draw] / maxPts; + } + } + } + + double result = 0.0; + for (int i = k; i <= n; i++) { + result += dp[i]; + } + + return result; + } +} +``` + +```cpp +class Solution { +public: + double new21Game(int n, int k, int maxPts) { + vector dp(n + 1, 0.0); + dp[0] = 1.0; + + for (int score = 1; score <= n; score++) { + for (int draw = 1; draw <= maxPts; draw++) { + if (score - draw >= 0 && score - draw < k) { + dp[score] += dp[score - draw] / maxPts; + } + } + } + + double result = 0.0; + for (int i = k; i <= n; i++) { + result += dp[i]; + } + + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @param {number} maxPts + * @return {number} + */ + new21Game(n, k, maxPts) { + const dp = new Array(n + 1).fill(0); + dp[0] = 1.0; + + for (let score = 1; score <= n; score++) { + for (let draw = 1; draw <= maxPts; draw++) { + if (score - draw >= 0 && score - draw < k) { + dp[score] += dp[score - draw] / maxPts; + } + } + } + + let result = 0.0; + for (let i = k; i <= n; i++) { + result += dp[i]; + } + + return result; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $k$ is the threshold score, $m$ is the maximum points per draw and $n$ is the upper bound on score. + +--- + +## 4. Dynamic Programming (Sliding Window) + +::tabs-start + +```python +class Solution: + def new21Game(self, n: int, k: int, maxPts: int) -> float: + if k == 0: + return 1.0 + + windowSum = 0 + for i in range(k, k + maxPts): + windowSum += 1 if i <= n else 0 + + dp = {} + for i in range(k - 1, -1, -1): + dp[i] = windowSum / maxPts + remove = 0 + if i + maxPts <= n: + remove = dp.get(i + maxPts, 1) + windowSum += dp[i] - remove + return dp[0] +``` + +```java +class Solution { + public double new21Game(int n, int k, int maxPts) { + if (k == 0) { + return 1.0; + } + double windowSum = 0.0; + for (int i = k; i < k + maxPts; i++) { + windowSum += (i <= n) ? 1.0 : 0.0; + } + HashMap dp = new HashMap<>(); + for (int i = k - 1; i >= 0; i--) { + dp.put(i, windowSum / maxPts); + double remove = 0.0; + if (i + maxPts <= n) { + remove = dp.getOrDefault(i + maxPts, 1.0); + } + windowSum += dp.get(i) - remove; + } + return dp.get(0); + } +} +``` + +```cpp +class Solution { +public: + double new21Game(int n, int k, int maxPts) { + if (k == 0) { + return 1.0; + } + double windowSum = 0.0; + for (int i = k; i < k + maxPts; i++) { + windowSum += (i <= n) ? 1.0 : 0.0; + } + unordered_map dp; + for (int i = k - 1; i >= 0; i--) { + dp[i] = windowSum / maxPts; + double remove = 0.0; + if (i + maxPts <= n) { + remove = (dp.find(i + maxPts) != dp.end()) ? dp[i + maxPts] : 1.0; + } + windowSum += dp[i] - remove; + } + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @param {number} maxPts + * @return {number} + */ + new21Game(n, k, maxPts) { + if (k === 0) { + return 1.0; + } + let windowSum = 0.0; + for (let i = k; i < k + maxPts; i++) { + windowSum += (i <= n) ? 1.0 : 0.0; + } + let dp = {}; + for (let i = k - 1; i >= 0; i--) { + dp[i] = windowSum / maxPts; + let remove = 0.0; + if (i + maxPts <= n) { + remove = (dp[i + maxPts] !== undefined) ? dp[i + maxPts] : 1.0; + } + windowSum += dp[i] - remove; + } + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k + m)$ +* Space complexity: $O(n)$ + +> Where $k$ is the threshold score, $m$ is the maximum points per draw and $n$ is the upper bound on score. \ No newline at end of file diff --git a/articles/next-greater-element-i.md b/articles/next-greater-element-i.md new file mode 100644 index 000000000..b81ca977a --- /dev/null +++ b/articles/next-greater-element-i.md @@ -0,0 +1,334 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]: + n = len(nums2) + res = [] + for num in nums1: + nextGreater = -1 + for i in range(n - 1, -1, -1): + if nums2[i] > num: + nextGreater = nums2[i] + elif nums2[i] == num: + break + res.append(nextGreater) + return res +``` + +```java +public class Solution { + public int[] nextGreaterElement(int[] nums1, int[] nums2) { + int n = nums2.length; + int[] res = new int[nums1.length]; + for (int i = 0; i < nums1.length; i++) { + int nextGreater = -1; + for (int j = n - 1; j >= 0; j--) { + if (nums2[j] > nums1[i]) { + nextGreater = nums2[j]; + } else if (nums2[j] == nums1[i]) { + break; + } + } + res[i] = nextGreater; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector nextGreaterElement(vector& nums1, vector& nums2) { + int n = nums2.size(); + vector res; + for (int num : nums1) { + int nextGreater = -1; + for (int i = n - 1; i >= 0; i--) { + if (nums2[i] > num) { + nextGreater = nums2[i]; + } else if (nums2[i] == num) { + break; + } + } + res.push_back(nextGreater); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[]} + */ + nextGreaterElement(nums1, nums2) { + const n = nums2.length; + const res = []; + for (const num of nums1) { + let nextGreater = -1; + for (let i = n - 1; i >= 0; i--) { + if (nums2[i] > num) { + nextGreater = nums2[i]; + } else if (nums2[i] === num) { + break; + } + } + res.push(nextGreater); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(1)$ + +> Where $m$ is the size of the array $nums1$ and $n$ is the size of the array $nums2$. + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]: + nums1Idx = {num : i for i, num in enumerate(nums1)} + res = [-1] * len(nums1) + + for i in range(len(nums2)): + if nums2[i] not in nums1Idx: + continue + for j in range(i + 1, len(nums2)): + if nums2[j] > nums2[i]: + idx = nums1Idx[nums2[i]] + res[idx] = nums2[j] + break + return res +``` + +```java +public class Solution { + public int[] nextGreaterElement(int[] nums1, int[] nums2) { + HashMap nums1Idx = new HashMap<>(); + for (int i = 0; i < nums1.length; i++) { + nums1Idx.put(nums1[i], i); + } + + int[] res = new int[nums1.length]; + Arrays.fill(res, -1); + + for (int i = 0; i < nums2.length; i++) { + if (!nums1Idx.containsKey(nums2[i])) { + continue; + } + for (int j = i + 1; j < nums2.length; j++) { + if (nums2[j] > nums2[i]) { + int idx = nums1Idx.get(nums2[i]); + res[idx] = nums2[j]; + break; + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector nextGreaterElement(vector& nums1, vector& nums2) { + unordered_map nums1Idx; + for (int i = 0; i < nums1.size(); i++) { + nums1Idx[nums1[i]] = i; + } + + vector res(nums1.size(), -1); + + for (int i = 0; i < nums2.size(); i++) { + if (nums1Idx.find(nums2[i]) == nums1Idx.end()) { + continue; + } + for (int j = i + 1; j < nums2.size(); j++) { + if (nums2[j] > nums2[i]) { + int idx = nums1Idx[nums2[i]]; + res[idx] = nums2[j]; + break; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[]} + */ + nextGreaterElement(nums1, nums2) { + const nums1Idx = new Map(); + nums1.forEach((num, i) => nums1Idx.set(num, i)); + + const res = new Array(nums1.length).fill(-1); + + for (let i = 0; i < nums2.length; i++) { + if (!nums1Idx.has(nums2[i])) { + continue; + } + for (let j = i + 1; j < nums2.length; j++) { + if (nums2[j] > nums2[i]) { + const idx = nums1Idx.get(nums2[i]); + res[idx] = nums2[j]; + break; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m)$ + +> Where $m$ is the size of the array $nums1$ and $n$ is the size of the array $nums2$. + +--- + +## 3. Stack + +::tabs-start + +```python +class Solution: + def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]: + nums1Idx = {num : i for i, num in enumerate(nums1)} + res = [-1] * len(nums1) + + stack = [] + for i in range(len(nums2)): + cur = nums2[i] + while stack and cur > stack[-1]: + val = stack.pop() + idx = nums1Idx[val] + res[idx] = cur + if cur in nums1Idx: + stack.append(cur) + return res +``` + +```java +public class Solution { + public int[] nextGreaterElement(int[] nums1, int[] nums2) { + HashMap nums1Idx = new HashMap<>(); + for (int i = 0; i < nums1.length; i++) { + nums1Idx.put(nums1[i], i); + } + + int[] res = new int[nums1.length]; + for (int i = 0; i < res.length; i++) { + res[i] = -1; + } + + Stack stack = new Stack<>(); + for (int num : nums2) { + while (!stack.isEmpty() && num > stack.peek()) { + int val = stack.pop(); + int idx = nums1Idx.get(val); + res[idx] = num; + } + if (nums1Idx.containsKey(num)) { + stack.push(num); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector nextGreaterElement(vector& nums1, vector& nums2) { + unordered_map nums1Idx; + for (int i = 0; i < nums1.size(); i++) { + nums1Idx[nums1[i]] = i; + } + + vector res(nums1.size(), -1); + stack stack; + + for (int num : nums2) { + while (!stack.empty() && num > stack.top()) { + int val = stack.top(); + stack.pop(); + int idx = nums1Idx[val]; + res[idx] = num; + } + if (nums1Idx.find(num) != nums1Idx.end()) { + stack.push(num); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[]} + */ + nextGreaterElement(nums1, nums2) { + const nums1Idx = new Map(); + nums1.forEach((num, i) => nums1Idx.set(num, i)); + + const res = new Array(nums1.length).fill(-1); + const stack = []; + + for (let num of nums2) { + while (stack.length && num > stack[stack.length - 1]) { + const val = stack.pop(); + const idx = nums1Idx.get(val); + res[idx] = num; + } + if (nums1Idx.has(num)) { + stack.push(num); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(m)$ + +> Where $m$ is the size of the array $nums1$ and $n$ is the size of the array $nums2$. \ No newline at end of file diff --git a/articles/non-cyclical-number.md b/articles/non-cyclical-number.md new file mode 100644 index 000000000..6b3053011 --- /dev/null +++ b/articles/non-cyclical-number.md @@ -0,0 +1,736 @@ +## 1. Hash Set + +::tabs-start + +```python +class Solution: + def isHappy(self, n: int) -> bool: + visit = set() + + while n not in visit: + visit.add(n) + n = self.sumOfSquares(n) + if n == 1: + return True + return False + + def sumOfSquares(self, n: int) -> int: + output = 0 + + while n: + digit = n % 10 + digit = digit ** 2 + output += digit + n = n // 10 + return output +``` + +```java +public class Solution { + public boolean isHappy(int n) { + Set visit = new HashSet<>(); + + while (!visit.contains(n)) { + visit.add(n); + n = sumOfSquares(n); + if (n == 1) { + return true; + } + } + return false; + } + + private int sumOfSquares(int n) { + int output = 0; + + while (n > 0) { + int digit = n % 10; + digit = digit * digit; + output += digit; + n /= 10; + } + return output; + } +} +``` + +```cpp +class Solution { +public: + bool isHappy(int n) { + unordered_set visit; + + while (visit.find(n) == visit.end()) { + visit.insert(n); + n = sumOfSquares(n); + if (n == 1) { + return true; + } + } + return false; + } + +private: + int sumOfSquares(int n) { + int output = 0; + + while (n > 0) { + int digit = n % 10; + digit = digit * digit; + output += digit; + n /= 10; + } + return output; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isHappy(n) { + const visit = new Set(); + + while (!visit.has(n)) { + visit.add(n); + n = this.sumOfSquares(n); + if (n === 1) { + return true; + } + } + return false; + } + + /** + * @param {number} n + * @return {number} + */ + sumOfSquares(n) { + let output = 0; + + while (n > 0) { + let digit = n % 10; + digit = digit * digit; + output += digit; + n = Math.floor(n / 10); + } + return output; + } +} +``` + +```csharp +public class Solution { + public bool IsHappy(int n) { + HashSet visit = new HashSet(); + + while (!visit.Contains(n)) { + visit.Add(n); + n = SumOfSquares(n); + if (n == 1) { + return true; + } + } + return false; + } + + private int SumOfSquares(int n) { + int output = 0; + + while (n > 0) { + int digit = n % 10; + digit = digit * digit; + output += digit; + n /= 10; + } + return output; + } +} +``` + +```go +func isHappy(n int) bool { + visit := make(map[int]bool) + + for !visit[n] { + visit[n] = true + n = sumOfSquares(n) + if n == 1 { + return true + } + } + return false +} + +func sumOfSquares(n int) int { + output := 0 + for n > 0 { + digit := n % 10 + digit = digit * digit + output += digit + n = n / 10 + } + return output +} +``` + +```kotlin +class Solution { + fun isHappy(n: Int): Boolean { + val visit = HashSet() + + var num = n + while (num !in visit) { + visit.add(num) + num = sumOfSquares(num) + if (num == 1) { + return true + } + } + return false + } + + fun sumOfSquares(n: Int): Int { + var output = 0 + var num = n + while (num > 0) { + val digit = num % 10 + output += digit * digit + num /= 10 + } + return output + } +} +``` + +```swift +class Solution { + func isHappy(_ n: Int) -> Bool { + var visit = Set() + var num = n + + while !visit.contains(num) { + visit.insert(num) + num = sumOfSquares(num) + if num == 1 { + return true + } + } + return false + } + + private func sumOfSquares(_ n: Int) -> Int { + var num = n + var output = 0 + + while num > 0 { + let digit = num % 10 + output += digit * digit + num /= 10 + } + return output + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ + +--- + +## 2. Fast And Slow Pointers - I + +::tabs-start + +```python +class Solution: + def isHappy(self, n: int) -> bool: + slow, fast = n, self.sumOfSquares(n) + + while slow != fast: + fast = self.sumOfSquares(fast) + fast = self.sumOfSquares(fast) + slow = self.sumOfSquares(slow) + return True if fast == 1 else False + + def sumOfSquares(self, n: int) -> int: + output = 0 + + while n: + digit = n % 10 + digit = digit ** 2 + output += digit + n = n // 10 + return output +``` + +```java +public class Solution { + public boolean isHappy(int n) { + int slow = n, fast = sumOfSquares(n); + + while (slow != fast) { + fast = sumOfSquares(fast); + fast = sumOfSquares(fast); + slow = sumOfSquares(slow); + } + + return fast == 1; + } + + private int sumOfSquares(int n) { + int output = 0; + while (n != 0) { + output += (n % 10) * (n % 10); + n /= 10; + } + return output; + } +} +``` + +```cpp +class Solution { +public: + bool isHappy(int n) { + int slow = n, fast = sumOfSquares(n); + + while (slow != fast) { + fast = sumOfSquares(fast); + fast = sumOfSquares(fast); + slow = sumOfSquares(slow); + } + + return fast == 1; + } + +private: + int sumOfSquares(int n) { + int output = 0; + while (n != 0) { + output += (n % 10) * (n % 10); + n /= 10; + } + return output; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isHappy(n) { + let slow = n; + let fast = this.sumOfSquares(n); + + while (slow !== fast) { + fast = this.sumOfSquares(fast); + fast = this.sumOfSquares(fast); + slow = this.sumOfSquares(slow); + } + + return fast === 1; + } + + /** + * @param {number} n + * @return {number} + */ + sumOfSquares(n) { + let output = 0; + while (n !== 0) { + output += (n % 10) ** 2; + n = Math.floor(n / 10); + } + return output; + } +} +``` + +```csharp +public class Solution { + public bool IsHappy(int n) { + int slow = n, fast = sumOfSquares(n); + + while (slow != fast) { + fast = sumOfSquares(fast); + fast = sumOfSquares(fast); + slow = sumOfSquares(slow); + } + + return fast == 1; + } + + private int sumOfSquares(int n) { + int output = 0; + while (n != 0) { + output += (n % 10) * (n % 10); + n /= 10; + } + return output; + } +} +``` + +```go +func isHappy(n int) bool { + slow, fast := n, sumOfSquares(n) + + for slow != fast { + fast = sumOfSquares(fast) + fast = sumOfSquares(fast) + slow = sumOfSquares(slow) + } + + return fast == 1 +} + +func sumOfSquares(n int) int { + output := 0 + for n > 0 { + digit := n % 10 + output += digit * digit + n = n / 10 + } + return output +} +``` + +```kotlin +class Solution { + fun isHappy(n: Int): Boolean { + var slow = n + var fast = sumOfSquares(n) + + while (slow != fast) { + fast = sumOfSquares(fast) + fast = sumOfSquares(fast) + slow = sumOfSquares(slow) + } + + return fast == 1 + } + + fun sumOfSquares(n: Int): Int { + var output = 0 + var num = n + while (num > 0) { + val digit = num % 10 + output += digit * digit + num /= 10 + } + return output + } +} +``` + +```swift +class Solution { + func isHappy(_ n: Int) -> Bool { + var slow = n + var fast = sumOfSquares(n) + + while slow != fast { + fast = sumOfSquares(fast) + fast = sumOfSquares(fast) + slow = sumOfSquares(slow) + } + return fast == 1 + } + + private func sumOfSquares(_ n: Int) -> Int { + var num = n + var output = 0 + + while num > 0 { + let digit = num % 10 + output += digit * digit + num /= 10 + } + return output + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Fast And Slow Pointers - II + +::tabs-start + +```python +class Solution: + def isHappy(self, n: int) -> bool: + slow, fast = n, self.sumOfSquares(n) + power = lam = 1 + + while slow != fast: + if power == lam: + slow = fast + power *= 2 + lam = 0 + fast = self.sumOfSquares(fast) + lam += 1 + return True if fast == 1 else False + + def sumOfSquares(self, n: int) -> int: + output = 0 + + while n: + digit = n % 10 + digit = digit ** 2 + output += digit + n = n // 10 + return output +``` + +```java +public class Solution { + public boolean isHappy(int n) { + int slow = n, fast = sumOfSquares(n); + int power = 1, lam = 1; + while (slow != fast) { + if (power == lam) { + slow = fast; + power *= 2; + lam = 0; + } + lam++; + fast = sumOfSquares(fast); + } + + return fast == 1; + } + + private int sumOfSquares(int n) { + int output = 0; + while (n != 0) { + output += (n % 10) * (n % 10); + n /= 10; + } + return output; + } +} +``` + +```cpp +class Solution { +public: + bool isHappy(int n) { + int slow = n, fast = sumOfSquares(n); + int power = 1, lam = 1; + + while (slow != fast) { + if (power == lam) { + slow = fast; + power *= 2; + lam = 0; + } + lam++; + fast = sumOfSquares(fast); + } + + return fast == 1; + } + +private: + int sumOfSquares(int n) { + int output = 0; + while (n != 0) { + output += (n % 10) * (n % 10); + n /= 10; + } + return output; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isHappy(n) { + let slow = n; + let fast = this.sumOfSquares(n); + let power = 1, lam = 1; + + while (slow !== fast) { + if (power === lam) { + slow = fast; + power *= 2; + lam = 0; + } + lam++; + fast = this.sumOfSquares(fast); + } + + return fast === 1; + } + + /** + * @param {number} n + * @return {number} + */ + sumOfSquares(n) { + let output = 0; + while (n !== 0) { + output += (n % 10) ** 2; + n = Math.floor(n / 10); + } + return output; + } +} +``` + +```csharp +public class Solution { + public bool IsHappy(int n) { + int slow = n, fast = sumOfSquares(n); + int power = 1, lam = 1; + + while (slow != fast) { + if (power == lam) { + slow = fast; + power *= 2; + lam = 0; + } + lam++; + fast = sumOfSquares(fast); + } + + return fast == 1; + } + + private int sumOfSquares(int n) { + int output = 0; + while (n != 0) { + output += (n % 10) * (n % 10); + n /= 10; + } + return output; + } +} +``` + +```go +func isHappy(n int) bool { + slow, fast := n, sumOfSquares(n) + power, lam := 1, 1 + + for slow != fast { + if power == lam { + slow = fast + power *= 2 + lam = 0 + } + fast = sumOfSquares(fast) + lam++ + } + + return fast == 1 +} + +func sumOfSquares(n int) int { + output := 0 + for n > 0 { + digit := n % 10 + output += digit * digit + n = n / 10 + } + return output +} +``` + +```kotlin +class Solution { + fun isHappy(n: Int): Boolean { + var slow = n + var fast = sumOfSquares(n) + var power = 1 + var lam = 1 + + while (slow != fast) { + if (power == lam) { + slow = fast + power *= 2 + lam = 0 + } + fast = sumOfSquares(fast) + lam++ + } + + return fast == 1 + } + + fun sumOfSquares(n: Int): Int { + var output = 0 + var num = n + while (num > 0) { + val digit = num % 10 + output += digit * digit + num /= 10 + } + return output + } +} +``` + +```swift +class Solution { + func isHappy(_ n: Int) -> Bool { + var slow = n + var fast = sumOfSquares(n) + var power = 1 + var lam = 1 + + while slow != fast { + if power == lam { + slow = fast + power *= 2 + lam = 0 + } + fast = sumOfSquares(fast) + lam += 1 + } + return fast == 1 + } + + private func sumOfSquares(_ n: Int) -> Int { + var num = n + var output = 0 + + while num > 0 { + let digit = num % 10 + output += digit * digit + num /= 10 + } + return output + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/non-decreasing-array.md b/articles/non-decreasing-array.md new file mode 100644 index 000000000..aed007f6c --- /dev/null +++ b/articles/non-decreasing-array.md @@ -0,0 +1,105 @@ +## 1. Greedy + +::tabs-start + +```python +class Solution: + def checkPossibility(self, nums: list[int]) -> bool: + changed = False + + for i in range(len(nums) - 1): + if nums[i] <= nums[i + 1]: + continue + if changed: + return False + if i == 0 or nums[i + 1] >= nums[i - 1]: + nums[i] = nums[i + 1] + else: + nums[i + 1] = nums[i] + changed = True + return True +``` + +```java +public class Solution { + public boolean checkPossibility(int[] nums) { + boolean changed = false; + + for (int i = 0; i < nums.length - 1; i++) { + if (nums[i] <= nums[i + 1]) { + continue; + } + if (changed) { + return false; + } + if (i == 0 || nums[i + 1] >= nums[i - 1]) { + nums[i] = nums[i + 1]; + } else { + nums[i + 1] = nums[i]; + } + changed = true; + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool checkPossibility(vector& nums) { + bool changed = false; + + for (int i = 0; i < nums.size() - 1; i++) { + if (nums[i] <= nums[i + 1]) { + continue; + } + if (changed) { + return false; + } + if (i == 0 || nums[i + 1] >= nums[i - 1]) { + nums[i] = nums[i + 1]; + } else { + nums[i + 1] = nums[i]; + } + changed = true; + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + checkPossibility(nums) { + let changed = false; + + for (let i = 0; i < nums.length - 1; i++) { + if (nums[i] <= nums[i + 1]) { + continue; + } + if (changed) { + return false; + } + if (i === 0 || nums[i + 1] >= nums[i - 1]) { + nums[i] = nums[i + 1]; + } else { + nums[i + 1] = nums[i]; + } + changed = true; + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/non-overlapping-intervals.md b/articles/non-overlapping-intervals.md new file mode 100644 index 000000000..60f0b89a1 --- /dev/null +++ b/articles/non-overlapping-intervals.md @@ -0,0 +1,1305 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int: + intervals.sort() + + def dfs(i, prev): + if i == len(intervals): + return 0 + res = dfs(i + 1, prev) + if prev == -1 or intervals[prev][1] <= intervals[i][0]: + res = max(res, 1 + dfs(i + 1, i)) + return res + + return len(intervals) - dfs(0, -1) +``` + +```java +public class Solution { + public int eraseOverlapIntervals(int[][] intervals) { + Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0])); + return intervals.length - dfs(intervals, 0, -1); + } + + private int dfs(int[][] intervals, int i, int prev) { + if (i == intervals.length) return 0; + int res = dfs(intervals, i + 1, prev); + if (prev == -1 || intervals[prev][1] <= intervals[i][0]) { + res = Math.max(res, 1 + dfs(intervals, i + 1, i)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int eraseOverlapIntervals(vector>& intervals) { + sort(intervals.begin(), intervals.end()); + return intervals.size() - dfs(intervals, 0, -1); + } + +private: + int dfs(const vector>& intervals, int i, int prev) { + if (i == intervals.size()) return 0; + int res = dfs(intervals, i + 1, prev); + if (prev == -1 || intervals[prev][1] <= intervals[i][0]) { + res = max(res, 1 + dfs(intervals, i + 1, i)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} intervals + * @return {number} + */ + eraseOverlapIntervals(intervals) { + intervals.sort((a, b) => a[0] - b[0]); + + const dfs = (i, prev) => { + if (i === intervals.length) return 0; + let res = dfs(i + 1, prev); + if (prev === -1 || intervals[prev][1] <= intervals[i][0]) { + res = Math.max(res, 1 + dfs(i + 1, i)); + } + return res; + }; + + return intervals.length - dfs(0, -1); + } +} +``` + +```csharp +public class Solution { + public int EraseOverlapIntervals(int[][] intervals) { + Array.Sort(intervals, (a, b) => a[0].CompareTo(b[0])); + return intervals.Length - Dfs(intervals, 0, -1); + } + + private int Dfs(int[][] intervals, int i, int prev) { + if (i == intervals.Length) return 0; + int res = Dfs(intervals, i + 1, prev); + if (prev == -1 || intervals[prev][1] <= intervals[i][0]) { + res = Math.Max(res, 1 + Dfs(intervals, i + 1, i)); + } + return res; + } +} +``` + +```go +func eraseOverlapIntervals(intervals [][]int) int { + sort.Slice(intervals, func(i, j int) bool { + return intervals[i][0] < intervals[j][0] + }) + + var dfs func(i, prev int) int + dfs = func(i, prev int) int { + if i == len(intervals) { + return 0 + } + res := dfs(i+1, prev) + if prev == -1 || intervals[prev][1] <= intervals[i][0] { + res = max(res, 1+dfs(i+1, i)) + } + return res + } + + return len(intervals) - dfs(0, -1) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun eraseOverlapIntervals(intervals: Array): Int { + intervals.sortBy { it[0] } + + fun dfs(i: Int, prev: Int): Int { + if (i == intervals.size) { + return 0 + } + var res = dfs(i + 1, prev) + if (prev == -1 || intervals[prev][1] <= intervals[i][0]) { + res = maxOf(res, 1 + dfs(i + 1, i)) + } + return res + } + + return intervals.size - dfs(0, -1) + } +} +``` + +```swift +class Solution { + func eraseOverlapIntervals(_ intervals: [[Int]]) -> Int { + var intervals = intervals + intervals.sort { $0[0] < $1[0] } + + func dfs(_ i: Int, _ prev: Int) -> Int { + if i == intervals.count { + return 0 + } + var res = dfs(i + 1, prev) + if prev == -1 || intervals[prev][1] <= intervals[i][0] { + res = max(res, 1 + dfs(i + 1, i)) + } + return res + } + + return intervals.count - dfs(0, -1) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int: + intervals.sort(key = lambda x: x[1]) + n = len(intervals) + memo = {} + + def dfs(i): + if i in memo: + return memo[i] + + res = 1 + for j in range(i + 1, n): + if intervals[i][1] <= intervals[j][0]: + res = max(res, 1 + dfs(j)) + memo[i] = res + return res + + return n - dfs(0) +``` + +```java +public class Solution { + private int[] memo; + + public int eraseOverlapIntervals(int[][] intervals) { + Arrays.sort(intervals, (a, b) -> Integer.compare(a[1], b[1])); + int n = intervals.length; + memo = new int[n]; + Arrays.fill(memo, -1); + + int maxNonOverlapping = dfs(intervals, 0); + return n - maxNonOverlapping; + } + + private int dfs(int[][] intervals, int i) { + if (i >= intervals.length) return 0; + if (memo[i] != -1) return memo[i]; + + int res = 1; + for (int j = i + 1; j < intervals.length; j++) { + if (intervals[i][1] <= intervals[j][0]) { + res = Math.max(res, 1 + dfs(intervals, j)); + } + } + memo[i] = res; + return res; + } +} +``` + +```cpp +class Solution { +public: + int eraseOverlapIntervals(vector>& intervals) { + sort(intervals.begin(), intervals.end(), [](auto& a, auto& b) { + return a[1] < b[1]; + }); + int n = intervals.size(); + vector memo(n, -1); + + int maxNonOverlapping = dfs(intervals, 0, memo); + return n - maxNonOverlapping; + } + +private: + int dfs(const vector>& intervals, int i, vector& memo) { + if (i >= intervals.size()) return 0; + if (memo[i] != -1) return memo[i]; + + int res = 1; + for (int j = i + 1; j < intervals.size(); j++) { + if (intervals[i][1] <= intervals[j][0]) { + res = max(res, 1 + dfs(intervals, j, memo)); + } + } + memo[i] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} intervals + * @return {number} + */ + eraseOverlapIntervals(intervals) { + intervals.sort((a, b) => a[1] - b[1]); + const n = intervals.length; + let memo = new Array(n).fill(-1); + + const dfs = (i) => { + if (i >= n) return 0; + if (memo[i] !== -1) return memo[i]; + + let res = 1; + for (let j = i + 1; j < n; j++) { + if (intervals[i][1] <= intervals[j][0]) { + res = Math.max(res, 1 + dfs(j)); + } + } + memo[i] = res; + return res; + }; + + const maxNonOverlapping = dfs(0); + return n - maxNonOverlapping; + } +} +``` + +```csharp +public class Solution { + private int[] memo; + + public int EraseOverlapIntervals(int[][] intervals) { + Array.Sort(intervals, (a, b) => a[1].CompareTo(b[1])); + int n = intervals.Length; + memo = new int[n]; + Array.Fill(memo, -1); + + int maxNonOverlapping = Dfs(intervals, 0); + return n - maxNonOverlapping; + } + + private int Dfs(int[][] intervals, int i) { + if (i >= intervals.Length) return 0; + if (memo[i] != -1) return memo[i]; + + int res = 1; + for (int j = i + 1; j < intervals.Length; j++) { + if (intervals[i][1] <= intervals[j][0]) { + res = Math.Max(res, 1 + Dfs(intervals, j)); + } + } + memo[i] = res; + return res; + } +} +``` + +```go +func eraseOverlapIntervals(intervals [][]int) int { + sort.Slice(intervals, func(i, j int) bool { + return intervals[i][1] < intervals[j][1] + }) + n := len(intervals) + memo := make([]int, n) + + var dfs func(i int) int + dfs = func(i int) int { + if memo[i] != 0 { + return memo[i] + } + + res := 1 + for j := i + 1; j < n; j++ { + if intervals[i][1] <= intervals[j][0] { + res = max(res, 1+dfs(j)) + } + } + memo[i] = res + return res + } + + return n - dfs(0) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun eraseOverlapIntervals(intervals: Array): Int { + intervals.sortBy { it[1] } + val n = intervals.size + val memo = IntArray(n) + + fun dfs(i: Int): Int { + if (memo[i] != 0) { + return memo[i] + } + + var res = 1 + for (j in i + 1 until n) { + if (intervals[i][1] <= intervals[j][0]) { + res = maxOf(res, 1 + dfs(j)) + } + } + memo[i] = res + return res + } + + return n - dfs(0) + } +} +``` + +```swift +class Solution { + func eraseOverlapIntervals(_ intervals: [[Int]]) -> Int { + var intervals = intervals + intervals.sort { $0[1] < $1[1] } + let n = intervals.count + var memo = [Int: Int]() + + func dfs(_ i: Int) -> Int { + if let result = memo[i] { + return result + } + + var res = 1 + for j in i + 1.. int: + intervals.sort(key=lambda x: x[1]) + n = len(intervals) + dp = [0] * n + + for i in range(n): + dp[i] = 1 + for j in range(i): + if intervals[j][1] <= intervals[i][0]: + dp[i] = max(dp[i], 1 + dp[j]) + + max_non_overlapping = max(dp) + return n - max_non_overlapping +``` + +```java +public class Solution { + public int eraseOverlapIntervals(int[][] intervals) { + Arrays.sort(intervals, (a, b) -> Integer.compare(a[1], b[1])); + int n = intervals.length; + int[] dp = new int[n]; + + for (int i = 0; i < n; i++) { + dp[i] = 1; + for (int j = 0; j < i; j++) { + if (intervals[j][1] <= intervals[i][0]) { + dp[i] = Math.max(dp[i], 1 + dp[j]); + } + } + } + + int maxNonOverlapping = Arrays.stream(dp).max().getAsInt(); + return n - maxNonOverlapping; + } +} +``` + +```cpp +class Solution { +public: + int eraseOverlapIntervals(vector>& intervals) { + sort(intervals.begin(), intervals.end(), [](auto& a, auto& b) { + return a[1] < b[1]; + }); + int n = intervals.size(); + vector dp(n, 0); + + for (int i = 0; i < n; i++) { + dp[i] = 1; + for (int j = 0; j < i; j++) { + if (intervals[j][1] <= intervals[i][0]) { + dp[i] = max(dp[i], 1 + dp[j]); + } + } + } + + int maxNonOverlapping = *max_element(dp.begin(), dp.end()); + return n - maxNonOverlapping; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} intervals + * @return {number} + */ + eraseOverlapIntervals(intervals) { + intervals.sort((a, b) => a[1] - b[1]); + const n = intervals.length; + const dp = new Array(n).fill(0); + + for (let i = 0; i < n; i++) { + dp[i] = 1; + for (let j = 0; j < i; j++) { + if (intervals[j][1] <= intervals[i][0]) { + dp[i] = Math.max(dp[i], 1 + dp[j]); + } + } + } + + const maxNonOverlapping = Math.max(...dp); + return n - maxNonOverlapping; + } +} +``` + +```csharp +public class Solution { + public int EraseOverlapIntervals(int[][] intervals) { + Array.Sort(intervals, (a, b) => a[1].CompareTo(b[1])); + int n = intervals.Length; + int[] dp = new int[n]; + + for (int i = 0; i < n; i++) { + dp[i] = 1; + for (int j = 0; j < i; j++) { + if (intervals[j][1] <= intervals[i][0]) { + dp[i] = Math.Max(dp[i], 1 + dp[j]); + } + } + } + + int maxNonOverlapping = 0; + foreach (var count in dp) { + maxNonOverlapping = Math.Max(maxNonOverlapping, count); + } + return n - maxNonOverlapping; + } +} +``` + +```go +func eraseOverlapIntervals(intervals [][]int) int { + sort.Slice(intervals, func(i, j int) bool { + return intervals[i][1] < intervals[j][1] + }) + + n := len(intervals) + dp := make([]int, n) + + for i := 0; i < n; i++ { + dp[i] = 1 + for j := 0; j < i; j++ { + if intervals[j][1] <= intervals[i][0] { + dp[i] = max(dp[i], 1+dp[j]) + } + } + } + + maxNonOverlapping := dp[0] + for i := 1; i < n; i++ { + maxNonOverlapping = max(maxNonOverlapping, dp[i]) + } + + return n - maxNonOverlapping +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun eraseOverlapIntervals(intervals: Array): Int { + intervals.sortBy { it[1] } + val n = intervals.size + val dp = IntArray(n) + + for (i in 0 until n) { + dp[i] = 1 + for (j in 0 until i) { + if (intervals[j][1] <= intervals[i][0]) { + dp[i] = maxOf(dp[i], 1 + dp[j]) + } + } + } + + val maxNonOverlapping = dp.maxOrNull() ?: 0 + return n - maxNonOverlapping + } +} +``` + +```swift +class Solution { + func eraseOverlapIntervals(_ intervals: [[Int]]) -> Int { + var intervals = intervals + intervals.sort { $0[1] < $1[1] } + let n = intervals.count + var dp = [Int](repeating: 0, count: n) + + for i in 0.. int: + intervals.sort(key=lambda x: x[1]) + n = len(intervals) + dp = [0] * n + dp[0] = 1 + + def bs(r, target): + l = 0 + while l < r: + m = (l + r) >> 1 + if intervals[m][1] <= target: + l = m + 1 + else: + r = m + return l + + for i in range(1, n): + idx = bs(i, intervals[i][0]) + if idx == 0: + dp[i] = dp[i - 1] + else: + dp[i] = max(dp[i - 1], 1 + dp[idx - 1]) + return n - dp[n - 1] +``` + +```java +public class Solution { + public int eraseOverlapIntervals(int[][] intervals) { + Arrays.sort(intervals, (a, b) -> Integer.compare(a[1], b[1])); + int n = intervals.length; + int[] dp = new int[n]; + dp[0] = 1; + + for (int i = 1; i < n; i++) { + int idx = bs(i, intervals[i][0], intervals); + if (idx == 0) { + dp[i] = dp[i - 1]; + } else { + dp[i] = Math.max(dp[i - 1], 1 + dp[idx - 1]); + } + } + return n - dp[n - 1]; + } + + private int bs(int r, int target, int[][] intervals) { + int l = 0; + while (l < r) { + int m = (l + r) >> 1; + if (intervals[m][1] <= target) { + l = m + 1; + } else { + r = m; + } + } + return l; + } +} +``` + +```cpp +class Solution { +public: + int eraseOverlapIntervals(vector>& intervals) { + sort(intervals.begin(), intervals.end(), [](auto& a, auto& b) { + return a[1] < b[1]; + }); + int n = intervals.size(); + vector dp(n); + dp[0] = 1; + + for (int i = 1; i < n; i++) { + int idx = bs(i, intervals[i][0], intervals); + if (idx == 0) { + dp[i] = dp[i - 1]; + } else { + dp[i] = max(dp[i - 1], 1 + dp[idx - 1]); + } + } + return n - dp[n - 1]; + } + + int bs(int r, int target, vector>& intervals) { + int l = 0; + while (l < r) { + int m = (l + r) >> 1; + if (intervals[m][1] <= target) { + l = m + 1; + } else { + r = m; + } + } + return l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} intervals + * @return {number} + */ + eraseOverlapIntervals(intervals) { + intervals.sort((a, b) => a[1] - b[1]); + const n = intervals.length; + const dp = new Array(n).fill(0); + dp[0] = 1; + + const bs = (r, target) => { + let l = 0; + while (l < r) { + const m = (l + r) >> 1; + if (intervals[m][1] <= target) { + l = m + 1; + } else { + r = m; + } + } + return l; + } + + for (let i = 1; i < n; i++) { + const idx = bs(i, intervals[i][0]); + if (idx === 0) { + dp[i] = dp[i - 1]; + } else { + dp[i] = Math.max(dp[i - 1], 1 + dp[idx - 1]); + } + } + return n - dp[n - 1]; + } +} +``` + +```csharp +public class Solution { + public int EraseOverlapIntervals(int[][] intervals) { + Array.Sort(intervals, (a, b) => a[1].CompareTo(b[1])); + int n = intervals.Length; + int[] dp = new int[n]; + dp[0] = 1; + + for (int i = 1; i < n; i++) { + int idx = Bs(i, intervals[i][0], intervals); + if (idx == 0) { + dp[i] = dp[i - 1]; + } else { + dp[i] = Math.Max(dp[i - 1], 1 + dp[idx - 1]); + } + } + return n - dp[n - 1]; + } + + private int Bs(int r, int target, int[][] intervals) { + int l = 0; + while (l < r) { + int m = (l + r) >> 1; + if (intervals[m][1] <= target) { + l = m + 1; + } else { + r = m; + } + } + return l; + } +} +``` + +```go +func eraseOverlapIntervals(intervals [][]int) int { + sort.Slice(intervals, func(i, j int) bool { + return intervals[i][1] < intervals[j][1] + }) + + n := len(intervals) + dp := make([]int, n) + dp[0] = 1 + + var bs func(r, target int) int + bs = func(r, target int) int { + l := 0 + for l < r { + m := (l + r) >> 1 + if intervals[m][1] <= target { + l = m + 1 + } else { + r = m + } + } + return l + } + + for i := 1; i < n; i++ { + idx := bs(i, intervals[i][0]) + if idx == 0 { + dp[i] = dp[i-1] + } else { + dp[i] = max(dp[i-1], 1+dp[idx-1]) + } + } + + return n - dp[n-1] +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun eraseOverlapIntervals(intervals: Array): Int { + intervals.sortBy { it[1] } + val n = intervals.size + val dp = IntArray(n) + dp[0] = 1 + + fun bs(r: Int, target: Int): Int { + var l = 0 + var r = r + while (l < r) { + val m = (l + r) / 2 + if (intervals[m][1] <= target) { + l = m + 1 + } else { + r = m + } + } + return l + } + + for (i in 1 until n) { + val idx = bs(i, intervals[i][0]) + dp[i] = if (idx == 0) dp[i - 1] else maxOf(dp[i - 1], 1 + dp[idx - 1]) + } + + return n - dp[n - 1] + } +} +``` + +```swift +class Solution { + func eraseOverlapIntervals(_ intervals: [[Int]]) -> Int { + var intervals = intervals + intervals.sort { $0[1] < $1[1] } + let n = intervals.count + var dp = [Int](repeating: 0, count: n) + dp[0] = 1 + + func bs(_ r: Int, _ target: Int) -> Int { + var l = 0 + var r = r + while l < r { + let m = (l + r) >> 1 + if intervals[m][1] <= target { + l = m + 1 + } else { + r = m + } + } + return l + } + + for i in 1.. int: + intervals.sort() + res = 0 + prevEnd = intervals[0][1] + + for start, end in intervals[1:]: + if start >= prevEnd: + prevEnd = end + else: + res += 1 + prevEnd = min(end, prevEnd) + return res +``` + +```java +public class Solution { + public int eraseOverlapIntervals(int[][] intervals) { + Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0])); + int res = 0; + int prevEnd = intervals[0][1]; + + for (int i = 1; i < intervals.length; i++) { + int start = intervals[i][0]; + int end = intervals[i][1]; + if (start >= prevEnd) { + prevEnd = end; + } else { + res++; + prevEnd = Math.min(end, prevEnd); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int eraseOverlapIntervals(vector>& intervals) { + sort(intervals.begin(), intervals.end()); + int res = 0; + int prevEnd = intervals[0][1]; + + for (int i = 1; i < intervals.size(); i++) { + int start = intervals[i][0]; + int end = intervals[i][1]; + if (start >= prevEnd) { + prevEnd = end; + } else { + res++; + prevEnd = min(end, prevEnd); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} intervals + * @return {number} + */ + eraseOverlapIntervals(intervals) { + intervals.sort((a, b) => a[0] - b[0]); + let res = 0; + let prevEnd = intervals[0][1]; + + for (let i = 1; i < intervals.length; i++) { + const start = intervals[i][0]; + const end = intervals[i][1]; + if (start >= prevEnd) { + prevEnd = end; + } else { + res++; + prevEnd = Math.min(end, prevEnd); + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int EraseOverlapIntervals(int[][] intervals) { + Array.Sort(intervals, (a, b) => a[0].CompareTo(b[0])); + int res = 0; + int prevEnd = intervals[0][1]; + + for (int i = 1; i < intervals.Length; i++) { + int start = intervals[i][0]; + int end = intervals[i][1]; + if (start >= prevEnd) { + prevEnd = end; + } else { + res++; + prevEnd = Math.Min(end, prevEnd); + } + } + return res; + } +} +``` + +```go +func eraseOverlapIntervals(intervals [][]int) int { + sort.Slice(intervals, func(i, j int) bool { + return intervals[i][0] < intervals[j][0] + }) + + res := 0 + prevEnd := intervals[0][1] + + for _, interval := range intervals[1:] { + start, end := interval[0], interval[1] + if start >= prevEnd { + prevEnd = end + } else { + res++ + prevEnd = min(end, prevEnd) + } + } + return res +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun eraseOverlapIntervals(intervals: Array): Int { + intervals.sortBy { it[0] } + + var res = 0 + var prevEnd = intervals[0][1] + + for (i in 1 until intervals.size) { + val (start, end) = intervals[i] + if (start >= prevEnd) { + prevEnd = end + } else { + res++ + prevEnd = minOf(end, prevEnd) + } + } + return res + } +} +``` + +```swift +class Solution { + func eraseOverlapIntervals(_ intervals: [[Int]]) -> Int { + var intervals = intervals + intervals.sort { $0[0] < $1[0] } + + var res = 0 + var prevEnd = intervals[0][1] + + for i in 1..= prevEnd { + prevEnd = end + } else { + res += 1 + prevEnd = min(end, prevEnd) + } + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 6. Greedy (Sort By End) + +::tabs-start + +```python +class Solution: + def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int: + intervals.sort(key = lambda pair: pair[1]) + prevEnd = intervals[0][1] + res = 0 + + for i in range(1, len(intervals)): + if prevEnd > intervals[i][0]: + res += 1 + else: + prevEnd = intervals[i][1] + + + return res +``` + +```java +public class Solution { + public int eraseOverlapIntervals(int[][] intervals) { + Arrays.sort(intervals, (a, b) -> Integer.compare(a[1], b[1])); + int res = 0; + int prevEnd = intervals[0][1]; + + for (int i = 1; i < intervals.length; i++) { + int start = intervals[i][0]; + int end = intervals[i][1]; + if (start < prevEnd) { + res++; + } else { + prevEnd = end; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int eraseOverlapIntervals(vector>& intervals) { + sort(intervals.begin(), intervals.end(), [](auto& a, auto& b) { + return a[1] < b[1]; + }); + int res = 0; + int prevEnd = intervals[0][1]; + + for (int i = 1; i < intervals.size(); i++) { + int start = intervals[i][0]; + int end = intervals[i][1]; + if (start < prevEnd) { + res++; + } else { + prevEnd = end; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} intervals + * @return {number} + */ + eraseOverlapIntervals(intervals) { + intervals.sort((a, b) => a[1] - b[1]); + let res = 0; + let prevEnd = intervals[0][1]; + + for (let i = 1; i < intervals.length; i++) { + const start = intervals[i][0]; + const end = intervals[i][1]; + if (start < prevEnd) { + res++; + } else { + prevEnd = end; + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int EraseOverlapIntervals(int[][] intervals) { + Array.Sort(intervals, (a, b) => a[1].CompareTo(b[1])); + int res = 0; + int prevEnd = intervals[0][1]; + + for (int i = 1; i < intervals.Length; i++) { + int start = intervals[i][0]; + int end = intervals[i][1]; + if (start < prevEnd) { + res++; + } else { + prevEnd = end; + } + } + return res; + } +} +``` + +```go +func eraseOverlapIntervals(intervals [][]int) int { + sort.Slice(intervals, func(i, j int) bool { + return intervals[i][1] < intervals[j][1] + }) + + prevEnd := intervals[0][1] + res := 0 + + for i := 1; i < len(intervals); i++ { + if prevEnd > intervals[i][0] { + res++ + } else { + prevEnd = intervals[i][1] + } + } + return res +} +``` + +```kotlin +class Solution { + fun eraseOverlapIntervals(intervals: Array): Int { + intervals.sortBy { it[1] } + + var prevEnd = intervals[0][1] + var res = 0 + + for (i in 1 until intervals.size) { + if (prevEnd > intervals[i][0]) { + res++ + } else { + prevEnd = intervals[i][1] + } + } + return res + } +} +``` + +```swift +class Solution { + func eraseOverlapIntervals(_ intervals: [[Int]]) -> Int { + var intervals = intervals + intervals.sort { $0[1] < $1[1] } + + var prevEnd = intervals[0][1] + var res = 0 + + for i in 1.. intervals[i][0] { + res += 1 + } else { + prevEnd = intervals[i][1] + } + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file diff --git a/articles/number-of-closed-islands.md b/articles/number-of-closed-islands.md new file mode 100644 index 000000000..d3ab68940 --- /dev/null +++ b/articles/number-of-closed-islands.md @@ -0,0 +1,836 @@ +## 1. Depth First Search - I + +::tabs-start + +```python +class Solution: + def closedIsland(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + directions = [[0, 1], [0, -1], [1, 0], [-1, 0]] + visit = set() + + def dfs(r, c): + if r < 0 or c < 0 or r == ROWS or c == COLS: + return 0 # False + if grid[r][c] == 1 or (r, c) in visit: + return 1 # True + + visit.add((r, c)) + res = True + for dx, dy in directions: + if not dfs(r + dx, c + dy): + res = False + return res + + res = 0 + for r in range(ROWS): + for c in range(COLS): + if not grid[r][c] and (r, c) not in visit: + res += dfs(r, c) + + return res +``` + +```java +public class Solution { + private int[][] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + private boolean[][] visit; + private int ROWS, COLS; + + public int closedIsland(int[][] grid) { + ROWS = grid.length; + COLS = grid[0].length; + visit = new boolean[ROWS][COLS]; + + int res = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 0 && !visit[r][c]) { + if (dfs(grid, r, c)) { + res++; + } + } + } + } + return res; + } + + private boolean dfs(int[][] grid, int r, int c) { + if (r < 0 || c < 0 || r == ROWS || c == COLS) { + return false; + } + if (grid[r][c] == 1 || visit[r][c]) { + return true; + } + + visit[r][c] = true; + boolean res = true; + for (int[] d : directions) { + if (!dfs(grid, r + d[0], c + d[1])) { + res = false; + } + } + return res; + } +} +``` + +```cpp +class Solution { + int ROWS, COLS; + vector> directions; + vector> visit; + +public: + int closedIsland(vector>& grid) { + ROWS = grid.size(); + COLS = grid[0].size(); + directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + visit.assign(ROWS, vector(COLS, false)); + + int res = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 0 && !visit[r][c]) { + if (dfs(grid, r, c)) { + res++; + } + } + } + } + return res; + } + +private: + bool dfs(vector>& grid, int r, int c) { + if (r < 0 || c < 0 || r == ROWS || c == COLS) { + return false; + } + if (grid[r][c] == 1 || visit[r][c]) { + return true; + } + + visit[r][c] = true; + bool res = true; + for (auto& d : directions) { + if (!dfs(grid, r + d[0], c + d[1])) { + res = false; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + closedIsland(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const directions = [0, 1, 0, -1, 0]; + const visit = Array.from({ length: ROWS }, () => Array(COLS).fill(false)); + + const dfs = (r, c) => { + if (r < 0 || c < 0 || r === ROWS || c === COLS) return false; + if (grid[r][c] === 1 || visit[r][c]) return true; + + visit[r][c] = true; + let res = true; + for (let d = 0; d < 4; d++) { + if (!dfs(r + directions[d], c + directions[d + 1])) { + res = false; + } + } + return res; + }; + + let res = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] === 0 && !visit[r][c]) { + if (dfs(r, c)) res++; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the given grid. + +--- + +## 2. Depth First Search - II + +::tabs-start + +```python +class Solution: + def closedIsland(self, grid: list[list[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + directions = [0, 1, 0, -1, 0] + + def dfs(r, c): + if r < 0 or c < 0 or r == ROWS or c == COLS or grid[r][c] == 1: + return + grid[r][c] = 1 + for d in range(4): + dfs(r + directions[d], c + directions[d + 1]) + + for r in range(ROWS): + dfs(r, 0) + dfs(r, COLS - 1) + for c in range(COLS): + dfs(0, c) + dfs(ROWS - 1, c) + + res = 0 + for r in range(1, ROWS - 1): + for c in range(1, COLS - 1): + if grid[r][c] == 0: + dfs(r, c) + res += 1 + return res +``` + +```java +public class Solution { + private int ROWS, COLS; + private int[] directions = {0, 1, 0, -1, 0}; + + public int closedIsland(int[][] grid) { + ROWS = grid.length; + COLS = grid[0].length; + + for (int r = 0; r < ROWS; r++) { + dfs(grid, r, 0); + dfs(grid, r, COLS - 1); + } + for (int c = 0; c < COLS; c++) { + dfs(grid, 0, c); + dfs(grid, ROWS - 1, c); + } + + int res = 0; + for (int r = 1; r < ROWS - 1; r++) { + for (int c = 1; c < COLS - 1; c++) { + if (grid[r][c] == 0) { + dfs(grid, r, c); + res++; + } + } + } + return res; + } + + private void dfs(int[][] grid, int r, int c) { + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || grid[r][c] == 1) { + return; + } + grid[r][c] = 1; + for (int d = 0; d < 4; d++) { + dfs(grid, r + directions[d], c + directions[d + 1]); + } + } +} +``` + +```cpp +class Solution { + const int directions[5] = {0, 1, 0, -1, 0}; + int ROWS, COLS; + +public: + int closedIsland(vector>& grid) { + ROWS = grid.size(); + COLS = grid[0].size(); + + for (int r = 0; r < ROWS; r++) { + dfs(grid, r, 0); + dfs(grid, r, COLS - 1); + } + for (int c = 0; c < COLS; c++) { + dfs(grid, 0, c); + dfs(grid, ROWS - 1, c); + } + + int res = 0; + for (int r = 1; r < ROWS - 1; r++) { + for (int c = 1; c < COLS - 1; c++) { + if (grid[r][c] == 0) { + dfs(grid, r, c); + res++; + } + } + } + return res; + } + +private: + void dfs(vector>& grid, int r, int c) { + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || grid[r][c] == 1) { + return; + } + grid[r][c] = 1; + for (int d = 0; d < 4; d++) { + dfs(grid, r + directions[d], c + directions[d + 1]); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + closedIsland(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const directions = [0, 1, 0, -1, 0]; + + const dfs = (r, c) => { + if (r < 0 || c < 0 || r === ROWS || c === COLS || grid[r][c] === 1) return; + grid[r][c] = 1; + for (let d = 0; d < 4; d++) { + dfs(r + directions[d], c + directions[d + 1]); + } + }; + + for (let r = 0; r < ROWS; r++) { + dfs(r, 0); + dfs(r, COLS - 1); + } + for (let c = 0; c < COLS; c++) { + dfs(0, c); + dfs(ROWS - 1, c); + } + + let res = 0; + for (let r = 1; r < ROWS - 1; r++) { + for (let c = 1; c < COLS - 1; c++) { + if (grid[r][c] === 0) { + dfs(r, c); + res++; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the given grid. + +--- + +## 3. Breadth First Search + +::tabs-start + +```python +class Solution: + def closedIsland(self, grid: list[list[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + directions = [[0, 1], [0, -1], [1, 0], [-1, 0]] + visit = set() + res = 0 + + def bfs(r, c): + q = deque([(r, c)]) + visit.add((r, c)) + is_closed = True + + while q: + x, y = q.popleft() + for dx, dy in directions: + nx, ny = x + dx, y + dy + if nx < 0 or ny < 0 or nx >= ROWS or ny >= COLS: + is_closed = False + continue + if grid[nx][ny] == 1 or (nx, ny) in visit: + continue + visit.add((nx, ny)) + q.append((nx, ny)) + + return is_closed + + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] == 0 and (r, c) not in visit: + res += bfs(r, c) + + return res +``` + +```java +public class Solution { + private int[][] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + private int ROWS, COLS; + private boolean[][] visit; + + public int closedIsland(int[][] grid) { + ROWS = grid.length; + COLS = grid[0].length; + visit = new boolean[ROWS][COLS]; + int res = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 0 && !visit[r][c]) { + if (bfs(grid, r, c)) res++; + } + } + } + return res; + } + + private boolean bfs(int[][] grid, int r, int c) { + Queue q = new LinkedList<>(); + q.offer(new int[]{r, c}); + visit[r][c] = true; + boolean isClosed = true; + + while (!q.isEmpty()) { + int[] cell = q.poll(); + int x = cell[0], y = cell[1]; + + for (int[] d : directions) { + int nx = x + d[0], ny = y + d[1]; + if (nx < 0 || ny < 0 || nx >= ROWS || ny >= COLS) { + isClosed = false; + continue; + } + if (grid[nx][ny] == 1 || visit[nx][ny]) continue; + visit[nx][ny] = true; + q.offer(new int[]{nx, ny}); + } + } + return isClosed; + } +} +``` + +```cpp +class Solution { + int directions[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + int ROWS, COLS; + vector> visit; + +public: + int closedIsland(vector>& grid) { + ROWS = grid.size(); + COLS = grid[0].size(); + visit.assign(ROWS, vector(COLS, false)); + int res = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 0 && !visit[r][c]) { + if (bfs(grid, r, c)) res++; + } + } + } + return res; + } + +private: + bool bfs(vector>& grid, int r, int c) { + queue> q; + q.push({r, c}); + visit[r][c] = true; + bool isClosed = true; + + while (!q.empty()) { + auto [x, y] = q.front();q.pop(); + for (auto& d : directions) { + int nx = x + d[0], ny = y + d[1]; + if (nx < 0 || ny < 0 || nx >= ROWS || ny >= COLS) { + isClosed = false; + continue; + } + if (grid[nx][ny] == 1 || visit[nx][ny]) continue; + visit[nx][ny] = true; + q.push({nx, ny}); + } + } + return isClosed; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + closedIsland(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const directions = [[0, 1], [0, -1], [1, 0], [-1, 0]]; + const visit = Array.from({ length: ROWS }, () => Array(COLS).fill(false)); + let res = 0; + + const bfs = (r, c) => { + const q = new Queue([[r, c]]); + visit[r][c] = true; + let isClosed = true; + + while (!q.isEmpty()) { + const [x, y] = q.pop(); + for (const [dx, dy] of directions) { + const nx = x + dx, ny = y + dy; + if (nx < 0 || ny < 0 || nx >= ROWS || ny >= COLS) { + isClosed = false; + continue; + } + if (grid[nx][ny] === 1 || visit[nx][ny]) continue; + visit[nx][ny] = true; + q.push([nx, ny]); + } + } + return isClosed; + }; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] === 0 && !visit[r][c]) { + if (bfs(r, c)) res++; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the given grid. + +--- + +## 4. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu, pv = self.find(u), self.find(v) + if pu == pv: + return False + if self.Size[pu] >= self.Size[pv]: + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + else: + self.Size[pv] += self.Size[pu] + self.Parent[pu] = pv + return True + +class Solution: + def closedIsland(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + N = ROWS * COLS + def index(r, c): + return r * COLS + c + + dsu = DSU(N) + directions = [0, 1, 0, -1, 0] + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] == 0: + for d in range(4): + nr, nc = r + directions[d], c + directions[d + 1] + if min(nr, nc) < 0 or nr == ROWS or nc == COLS: + dsu.union(N, index(r, c)) + elif grid[nr][nc] == 0: + dsu.union(index(r, c), index(nr, nc)) + + res = 0 + rootN = dsu.find(N) + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] == 1: + continue + node = index(r, c) + root = dsu.find(node) + if root == node and root != rootN: + res += 1 + return res +``` + +```java +class DSU { + int[] Parent, Size; + + public DSU(int n) { + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + public int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (Size[pu] >= Size[pv]) { + Size[pu] += Size[pv]; + Parent[pv] = pu; + } else { + Size[pv] += Size[pu]; + Parent[pu] = pv; + } + return true; + } +} + +public class Solution { + public int closedIsland(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int N = ROWS * COLS; + + DSU dsu = new DSU(N); + int[] directions = {0, 1, 0, -1, 0}; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 0) { + for (int d = 0; d < 4; d++) { + int nr = r + directions[d], nc = c + directions[d + 1]; + if (nr < 0 || nc < 0 || nr == ROWS || nc == COLS) { + dsu.union(N, r * COLS + c); + } else if (grid[nr][nc] == 0) { + dsu.union(r * COLS + c, nr * COLS + nc); + } + } + } + } + } + + int res = 0, rootN = dsu.find(N); + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 0) { + int node = r * COLS + c; + int root = dsu.find(node); + if (root == node && root != rootN) { + res++; + } + } + } + } + return res; + } +} +``` + +```cpp +class DSU { +public: + vector Parent, Size; + + DSU(int n) { + Parent.resize(n + 1); + Size.resize(n + 1, 1); + for (int i = 0; i <= n; i++) { + Parent[i] = i; + } + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + bool unionSets(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (Size[pu] >= Size[pv]) { + Size[pu] += Size[pv]; + Parent[pv] = pu; + } else { + Size[pv] += Size[pu]; + Parent[pu] = pv; + } + return true; + } +}; + +class Solution { +public: + int closedIsland(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + int N = ROWS * COLS; + + DSU dsu(N); + int directions[5] = {0, 1, 0, -1, 0}; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 0) { + for (int d = 0; d < 4; d++) { + int nr = r + directions[d], nc = c + directions[d + 1]; + if (nr < 0 || nc < 0 || nr == ROWS || nc == COLS) { + dsu.unionSets(N, r * COLS + c); + } else if (grid[nr][nc] == 0) { + dsu.unionSets(r * COLS + c, nr * COLS + nc); + } + } + } + } + } + + int res = 0, rootN = dsu.find(N); + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 0) { + int node = r * COLS + c; + int root = dsu.find(node); + if (root == node && root != rootN) { + res++; + } + } + } + } + return res; + } +}; +``` + +```javascript +class DSU { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.parent = Array.from({ length: n + 1 }, (_, i) => i); + this.size = new Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.parent[node] !== node) { + this.parent[node] = this.find(this.parent[node]); + } + return this.parent[node]; + } + + /** + * @param {number} u + * @param {number} u=v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u), pv = this.find(v); + if (pu === pv) return false; + if (this.size[pu] >= this.size[pv]) { + this.size[pu] += this.size[pv]; + this.parent[pv] = pu; + } else { + this.size[pv] += this.size[pu]; + this.parent[pu] = pv; + } + return true; + } +} + +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + closedIsland(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const N = ROWS * COLS; + + const dsu = new DSU(N); + const directions = [0, 1, 0, -1, 0]; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] === 0) { + for (let d = 0; d < 4; d++) { + let nr = r + directions[d], nc = c + directions[d + 1]; + if (nr < 0 || nc < 0 || nr === ROWS || nc === COLS) { + dsu.union(N, r * COLS + c); + } else if (grid[nr][nc] === 0) { + dsu.union(r * COLS + c, nr * COLS + nc); + } + } + } + } + } + + let res = 0, rootN = dsu.find(N); + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] === 0) { + let node = r * COLS + c; + let root = dsu.find(node); + if (root === node && root !== rootN) { + res++; + } + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the given grid. \ No newline at end of file diff --git a/articles/number-of-dice-rolls-with-target-sum.md b/articles/number-of-dice-rolls-with-target-sum.md new file mode 100644 index 000000000..442065a4a --- /dev/null +++ b/articles/number-of-dice-rolls-with-target-sum.md @@ -0,0 +1,573 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def numRollsToTarget(self, n: int, k: int, target: int) -> int: + MOD = 10**9 + 7 + + def count(n, target): + if n == 0: + return 1 if target == 0 else 0 + if target < 0: + return 0 + + res = 0 + for val in range(1, k + 1): + res = (res + count(n - 1, target - val)) % MOD + return res + + return count(n, target) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int numRollsToTarget(int n, int k, int target) { + return count(n, target, k); + } + + private int count(int n, int target, int k) { + if (n == 0) { + return target == 0 ? 1 : 0; + } + if (target < 0) { + return 0; + } + + int res = 0; + for (int val = 1; val <= k; val++) { + res = (res + count(n - 1, target - val, k)) % MOD; + } + return res; + } +} +``` + +```cpp +class Solution { +private: + const int MOD = 1e9 + 7; + +public: + int numRollsToTarget(int n, int k, int target) { + return count(n, target, k); + } + +private: + int count(int n, int target, int k) { + if (n == 0) { + return target == 0 ? 1 : 0; + } + if (target < 0) { + return 0; + } + + int res = 0; + for (int val = 1; val <= k; val++) { + res = (res + count(n - 1, target - val, k)) % MOD; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @param {number} target + * @return {number} + */ + numRollsToTarget(n, k, target) { + const MOD = 1e9 + 7; + + const count = (n, target) => { + if (n === 0) { + return target === 0 ? 1 : 0; + } + if (target < 0) { + return 0; + } + + let res = 0; + for (let val = 1; val <= k; val++) { + res = (res + count(n - 1, target - val)) % MOD; + } + return res; + }; + + return count(n, target); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k ^ n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of dices, $k$ is the number of faces each dice have, and $t$ is the target value. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def numRollsToTarget(self, n: int, k: int, target: int) -> int: + MOD = 10**9 + 7 + cache = {} + + def count(n, target): + if n == 0: + return 1 if target == 0 else 0 + if (n, target) in cache: + return cache[(n, target)] + + res = 0 + for val in range(1, k + 1): + if target - val >= 0: + res = (res + count(n - 1, target - val)) % MOD + cache[(n, target)] = res + return res + + return count(n, target) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + private int[][] dp; + + public int numRollsToTarget(int n, int k, int target) { + dp = new int[n + 1][target + 1]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + return count(n, target, k); + } + + private int count(int n, int target, int k) { + if (n == 0) { + return target == 0 ? 1 : 0; + } + if (target < 0) { + return 0; + } + if (dp[n][target] != -1) { + return dp[n][target]; + } + + int res = 0; + for (int val = 1; val <= k; val++) { + if (target - val >= 0) { + res = (res + count(n - 1, target - val, k)) % MOD; + } + } + dp[n][target] = res; + return res; + } +} +``` + +```cpp +class Solution { +private: + const int MOD = 1e9 + 7; + vector> dp; + +public: + int numRollsToTarget(int n, int k, int target) { + dp = vector>(n + 1, vector(target + 1, -1)); + return count(n, target, k); + } + +private: + int count(int n, int target, int k) { + if (n == 0) { + return target == 0 ? 1 : 0; + } + if (target < 0) { + return 0; + } + if (dp[n][target] != -1) { + return dp[n][target]; + } + + int res = 0; + for (int val = 1; val <= k; val++) { + if (target - val >= 0) { + res = (res + count(n - 1, target - val, k)) % MOD; + } + } + dp[n][target] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @param {number} target + * @return {number} + */ + numRollsToTarget(n, k, target) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: n + 1 }, () => Array(target + 1).fill(-1)); + + const count = (n, target) => { + if (n === 0) { + return target === 0 ? 1 : 0; + } + if (dp[n][target] !== -1) { + return dp[n][target]; + } + + let res = 0; + for (let val = 1; val <= k; val++) { + if (target - val >= 0) { + res = (res + count(n - 1, target - val)) % MOD; + } + } + dp[n][target] = res; + return res; + }; + + return count(n, target); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * t * k)$ +* Space complexity: $O(n * t)$ + +> Where $n$ is the number of dices, $k$ is the number of faces each dice have, and $t$ is the target value. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def numRollsToTarget(self, n: int, k: int, target: int) -> int: + MOD = 10**9 + 7 + dp = [[0] * (target + 1) for _ in range(n + 1)] + dp[0][0] = 1 + + for i in range(1, n + 1): + for val in range(1, k + 1): + for t in range(val, target + 1): + dp[i][t] = (dp[i][t] + dp[i - 1][t - val]) % MOD + + return dp[n][target] +``` + +```java +public class Solution { + public int numRollsToTarget(int n, int k, int target) { + int MOD = 1_000_000_007; + int[][] dp = new int[n + 1][target + 1]; + dp[0][0] = 1; + + for (int i = 1; i <= n; i++) { + for (int val = 1; val <= k; val++) { + for (int t = val; t <= target; t++) { + dp[i][t] = (dp[i][t] + dp[i - 1][t - val]) % MOD; + } + } + } + + return dp[n][target]; + } +} +``` + +```cpp +class Solution { +public: + int numRollsToTarget(int n, int k, int target) { + const int MOD = 1e9 + 7; + vector> dp(n + 1, vector(target + 1, 0)); + dp[0][0] = 1; + + for (int i = 1; i <= n; i++) { + for (int val = 1; val <= k; val++) { + for (int t = val; t <= target; t++) { + dp[i][t] = (dp[i][t] + dp[i - 1][t - val]) % MOD; + } + } + } + + return dp[n][target]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @param {number} target + * @return {number} + */ + numRollsToTarget(n, k, target) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: n + 1 }, () => Array(target + 1).fill(0)); + dp[0][0] = 1; + + for (let i = 1; i <= n; i++) { + for (let val = 1; val <= k; val++) { + for (let t = val; t <= target; t++) { + dp[i][t] = (dp[i][t] + dp[i - 1][t - val]) % MOD; + } + } + } + + return dp[n][target]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * t * k)$ +* Space complexity: $O(n * t)$ + +> Where $n$ is the number of dices, $k$ is the number of faces each dice have, and $t$ is the target value. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def numRollsToTarget(self, n: int, k: int, target: int) -> int: + dp = [0] * (target + 1) + dp[0] = 1 + MOD = 10**9 + 7 + + for dice in range(n): + next_dp = [0] * (target + 1) + for val in range(1, k + 1): + for total in range(val, target + 1): + next_dp[total] = (next_dp[total] + dp[total - val]) % MOD + dp = next_dp + + return dp[target] +``` + +```java +public class Solution { + public int numRollsToTarget(int n, int k, int target) { + int[] dp = new int[target + 1]; + dp[0] = 1; + int MOD = 1_000_000_007; + + for (int dice = 0; dice < n; dice++) { + int[] nextDp = new int[target + 1]; + for (int val = 1; val <= k; val++) { + for (int total = val; total <= target; total++) { + nextDp[total] = (nextDp[total] + dp[total - val]) % MOD; + } + } + dp = nextDp; + } + + return dp[target]; + } +} +``` + +```cpp +class Solution { +public: + int numRollsToTarget(int n, int k, int target) { + const int MOD = 1e9 + 7; + vector dp(target + 1, 0); + dp[0] = 1; + + for (int dice = 0; dice < n; dice++) { + vector nextDp(target + 1, 0); + for (int val = 1; val <= k; val++) { + for (int total = val; total <= target; total++) { + nextDp[total] = (nextDp[total] + dp[total - val]) % MOD; + } + } + dp = nextDp; + } + + return dp[target]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @param {number} target + * @return {number} + */ + numRollsToTarget(n, k, target) { + const MOD = 1e9 + 7; + let dp = Array(target + 1).fill(0); + dp[0] = 1; + + for (let dice = 0; dice < n; dice++) { + const nextDp = Array(target + 1).fill(0); + for (let val = 1; val <= k; val++) { + for (let total = val; total <= target; total++) { + nextDp[total] = (nextDp[total] + dp[total - val]) % MOD; + } + } + dp = nextDp; + } + + return dp[target]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * t * k)$ +* Space complexity: $O(t)$ + +> Where $n$ is the number of dices, $k$ is the number of faces each dice have, and $t$ is the target value. + +--- + +## 5. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def numRollsToTarget(self, n: int, k: int, target: int) -> int: + MOD = 10**9 + 7 + dp = [0] * (target + 1) + dp[0] = 1 + + for dice in range(n): + for t in range(target, -1, -1): + ways = dp[t] + dp[t] = 0 + if ways: + for val in range(1, min(k + 1, target - t + 1)): + dp[t + val] = (dp[t + val] + ways) % MOD + + return dp[target] +``` + +```java +public class Solution { + public int numRollsToTarget(int n, int k, int target) { + int MOD = 1_000_000_007; + int[] dp = new int[target + 1]; + dp[0] = 1; + + for (int dice = 0; dice < n; dice++) { + for (int t = target; t >= 0; t--) { + int ways = dp[t]; + dp[t] = 0; + if (ways > 0) { + for (int val = 1; val <= Math.min(k, target - t); val++) { + dp[t + val] = (dp[t + val] + ways) % MOD; + } + } + } + } + + return dp[target]; + } +} +``` + +```cpp +class Solution { +public: + int numRollsToTarget(int n, int k, int target) { + const int MOD = 1e9 + 7; + vector dp(target + 1, 0); + dp[0] = 1; + + for (int dice = 0; dice < n; dice++) { + for (int t = target; t >= 0; t--) { + int ways = dp[t]; + dp[t] = 0; + if (ways > 0) { + for (int val = 1; val <= min(k, target - t); val++) { + dp[t + val] = (dp[t + val] + ways) % MOD; + } + } + } + } + + return dp[target]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @param {number} target + * @return {number} + */ + numRollsToTarget(n, k, target) { + const MOD = 1e9 + 7; + const dp = Array(target + 1).fill(0); + dp[0] = 1; + + for (let dice = 0; dice < n; dice++) { + for (let t = target; t >= 0; t--) { + const ways = dp[t]; + dp[t] = 0; + if (ways > 0) { + for (let val = 1; val <= Math.min(k, target - t); val++) { + dp[t + val] = (dp[t + val] + ways) % MOD; + } + } + } + } + + return dp[target]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * t * k)$ +* Space complexity: $O(t)$ + +> Where $n$ is the number of dices, $k$ is the number of faces each dice have, and $t$ is the target value. \ No newline at end of file diff --git a/articles/number-of-enclaves.md b/articles/number-of-enclaves.md new file mode 100644 index 000000000..a5da06e58 --- /dev/null +++ b/articles/number-of-enclaves.md @@ -0,0 +1,607 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def numEnclaves(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + direct = [[0, 1], [0, -1], [1, 0], [-1, 0]] + + # Return num of land cells + def dfs(r, c): + if (r < 0 or c < 0 or + r == ROWS or c == COLS or + not grid[r][c] or (r, c) in visit): + return 0 + visit.add((r, c)) + res = 1 + for dr, dc in direct: + res += dfs(r + dr, c + dc) + return res + + visit = set() + land, borderLand = 0, 0 + for r in range(ROWS): + for c in range(COLS): + land += grid[r][c] + if (grid[r][c] and (r, c) not in visit and + (c in [0, COLS - 1] or r in [0, ROWS - 1])): + borderLand += dfs(r, c) + + return land - borderLand +``` + +```java +public class Solution { + private int ROWS, COLS; + private boolean[][] visit; + private int[][] direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + public int numEnclaves(int[][] grid) { + this.ROWS = grid.length; + this.COLS = grid[0].length; + this.visit = new boolean[ROWS][COLS]; + + int land = 0, borderLand = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + land += grid[r][c]; + if (grid[r][c] == 1 && !visit[r][c] && + (r == 0 || r == ROWS - 1 || c == 0 || c == COLS - 1)) { + borderLand += dfs(r, c, grid); + } + } + } + return land - borderLand; + } + + private int dfs(int r, int c, int[][] grid) { + if (r < 0 || c < 0 || r == ROWS || c == COLS || + grid[r][c] == 0 || visit[r][c]) { + return 0; + } + visit[r][c] = true; + int res = 1; + for (int[] d : direct) { + res += dfs(r + d[0], c + d[1], grid); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int ROWS, COLS; + vector> visit; + vector> direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + int numEnclaves(vector>& grid) { + this->ROWS = grid.size(); + this->COLS = grid[0].size(); + this->visit = vector>(ROWS, vector(COLS, false)); + + int land = 0, borderLand = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + land += grid[r][c]; + if (grid[r][c] == 1 && !visit[r][c] && + (r == 0 || r == ROWS - 1 || c == 0 || c == COLS - 1)) { + borderLand += dfs(r, c, grid); + } + } + } + return land - borderLand; + } + +private: + int dfs(int r, int c, vector>& grid) { + if (r < 0 || c < 0 || r == ROWS || c == COLS || + grid[r][c] == 0 || visit[r][c]) { + return 0; + } + visit[r][c] = true; + int res = 1; + for (auto& d : direct) { + res += dfs(r + d[0], c + d[1], grid); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + numEnclaves(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const visit = Array.from({ length: ROWS }, () => + Array(COLS).fill(false) + ); + const direct = [0, 1, 0, -1, 0]; + + const dfs = (r, c) => { + if (r < 0 || c < 0 || r === ROWS || c === COLS || + grid[r][c] === 0 || visit[r][c]) { + return 0; + } + visit[r][c] = true; + let res = 1; + for (let d = 0; d < 4; d++) { + res += dfs(r + direct[d], c + direct[d + 1]); + } + return res; + }; + + let land = 0, borderLand = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + land += grid[r][c]; + if (grid[r][c] === 1 && !visit[r][c] && + (r === 0 || r === ROWS - 1 || c === 0 || c === COLS - 1)) { + borderLand += dfs(r, c); + } + } + } + return land - borderLand; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the given grid. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def numEnclaves(self, grid: list[list[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + direct = [[0, 1], [0, -1], [1, 0], [-1, 0]] + visit = [[False] * COLS for _ in range(ROWS)] + q = deque() + + land, borderLand = 0, 0 + for r in range(ROWS): + for c in range(COLS): + land += grid[r][c] + if (grid[r][c] == 1 and + (r in [0, ROWS - 1] or c in [0, COLS - 1]) + ): + q.append((r, c)) + visit[r][c] = True + + while q: + r, c = q.popleft() + borderLand += 1 + for dr, dc in direct: + nr, nc = r + dr, c + dc + if (0 <= nr < ROWS and 0 <= nc < COLS and + grid[nr][nc] == 1 and not visit[nr][nc] + ): + q.append((nr, nc)) + visit[nr][nc] = True + + return land - borderLand +``` + +```java +public class Solution { + public int numEnclaves(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int[][] direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + boolean[][] visit = new boolean[ROWS][COLS]; + Queue q = new LinkedList<>(); + + int land = 0, borderLand = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + land += grid[r][c]; + if (grid[r][c] == 1 && (r == 0 || r == ROWS - 1 || + c == 0 || c == COLS - 1)) { + q.offer(new int[]{r, c}); + visit[r][c] = true; + } + } + } + + while (!q.isEmpty()) { + int[] cell = q.poll(); + int r = cell[0], c = cell[1]; + borderLand++; + + for (int[] d : direct) { + int nr = r + d[0], nc = c + d[1]; + if (nr >= 0 && nc >= 0 && nr < ROWS && nc < COLS && + grid[nr][nc] == 1 && !visit[nr][nc]) { + q.offer(new int[]{nr, nc}); + visit[nr][nc] = true; + } + } + } + + return land - borderLand; + } +} +``` + +```cpp +class Solution { +public: + int ROWS, COLS; + vector> visit; + vector> direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + int numEnclaves(vector>& grid) { + this->ROWS = grid.size(); + this->COLS = grid[0].size(); + this->visit = vector>(ROWS, vector(COLS, false)); + + int land = 0, borderLand = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + land += grid[r][c]; + if (grid[r][c] == 1 && !visit[r][c] && + (r == 0 || r == ROWS - 1 || c == 0 || c == COLS - 1)) { + borderLand += dfs(r, c, grid); + } + } + } + return land - borderLand; + } + +private: + int dfs(int r, int c, vector>& grid) { + if (r < 0 || c < 0 || r == ROWS || c == COLS || + grid[r][c] == 0 || visit[r][c]) { + return 0; + } + visit[r][c] = true; + int res = 1; + for (auto& d : direct) { + res += dfs(r + d[0], c + d[1], grid); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + numEnclaves(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const direct = [0, 1, 0, -1, 0]; + const visit = Array.from({ length: ROWS }, () => + Array(COLS).fill(false) + ); + const q = new Queue(); + + let land = 0, borderLand = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + land += grid[r][c]; + if (grid[r][c] === 1 && (r === 0 || r === ROWS - 1 || + c === 0 || c === COLS - 1)) { + q.push([r, c]); + visit[r][c] = true; + } + } + } + + while (!q.isEmpty()) { + let [r, c] = q.pop(); + borderLand++; + for (let d = 0; d < 4; d++) { + let nr = r + direct[d], nc = c + direct[d + 1]; + if (nr >= 0 && nc >= 0 && nr < ROWS && nc < COLS && + grid[nr][nc] === 1 && !visit[nr][nc]) { + q.push([nr, nc]); + visit[nr][nc] = true; + } + } + } + + return land - borderLand; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the given grid. + +--- + +## 3. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu, pv = self.find(u), self.find(v) + if pu == pv: + return False + if self.Size[pu] >= self.Size[pv]: + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + else: + self.Size[pv] += self.Size[pu] + self.Parent[pu] = pv + return True + +class Solution: + def numEnclaves(self, grid: list[list[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + N = ROWS * COLS + def index(r, c): + return r * COLS + c + + dsu = DSU(N) + directions = [0, 1, 0, -1, 0] + land = 0 + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] == 0: + continue + land += 1 + for d in range(4): + nr, nc = r + directions[d], c + directions[d + 1] + if 0 <= nr < ROWS and 0 <= nc < COLS: + if grid[nr][nc] == 1: + dsu.union(index(r, c), index(nr, nc)) + else: + dsu.union(N, index(r, c)) + + borderLand = dsu.Size[dsu.find(N)] + return land - borderLand + 1 +``` + +```java +class DSU { + int[] Parent, Size; + + public DSU(int n) { + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + public int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (Size[pu] >= Size[pv]) { + Size[pu] += Size[pv]; + Parent[pv] = pu; + } else { + Size[pv] += Size[pu]; + Parent[pu] = pv; + } + return true; + } +} + +public class Solution { + public int numEnclaves(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int N = ROWS * COLS; + DSU dsu = new DSU(N); + int[] directions = {0, 1, 0, -1, 0}; + int land = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 0) continue; + land++; + for (int d = 0; d < 4; d++) { + int nr = r + directions[d], nc = c + directions[d + 1]; + if (nr >= 0 && nc >= 0 && nr < ROWS && nc < COLS) { + if (grid[nr][nc] == 1) { + dsu.union(r * COLS + c, nr * COLS + nc); + } + } else { + dsu.union(N, r * COLS + c); + } + } + } + } + + int borderLand = dsu.Size[dsu.find(N)]; + return land - borderLand + 1; + } +} +``` + +```cpp +class DSU { +public: + vector Parent, Size; + + DSU(int n) { + Parent.resize(n + 1); + Size.resize(n + 1, 1); + for (int i = 0; i <= n; i++) { + Parent[i] = i; + } + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + bool unionSets(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (Size[pu] >= Size[pv]) { + Size[pu] += Size[pv]; + Parent[pv] = pu; + } else { + Size[pv] += Size[pu]; + Parent[pu] = pv; + } + return true; + } +}; + +class Solution { +public: + int numEnclaves(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + int N = ROWS * COLS; + DSU dsu(N); + vector directions = {0, 1, 0, -1, 0}; + int land = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 0) continue; + land++; + for (int d = 0; d < 4; d++) { + int nr = r + directions[d], nc = c + directions[d + 1]; + if (nr >= 0 && nc >= 0 && nr < ROWS && nc < COLS) { + if (grid[nr][nc] == 1) { + dsu.unionSets(r * COLS + c, nr * COLS + nc); + } + } else { + dsu.unionSets(N, r * COLS + c); + } + } + } + } + + int borderLand = dsu.Size[dsu.find(N)]; + return land - borderLand + 1; + } +}; +``` + +```javascript +class DSU { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.parent = Array.from({ length: n + 1 }, (_, i) => i); + this.size = new Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.parent[node] !== node) { + this.parent[node] = this.find(this.parent[node]); + } + return this.parent[node]; + } + + /** + * @param {number} u + * @param {number} u=v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u), pv = this.find(v); + if (pu === pv) return false; + if (this.size[pu] >= this.size[pv]) { + this.size[pu] += this.size[pv]; + this.parent[pv] = pu; + } else { + this.size[pv] += this.size[pu]; + this.parent[pu] = pv; + } + return true; + } +} + +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + numEnclaves(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const N = ROWS * COLS; + const dsu = new DSU(N); + const directions = [0, 1, 0, -1, 0]; + let land = 0; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] === 0) continue; + land++; + for (let d = 0; d < 4; d++) { + let nr = r + directions[d], nc = c + directions[d + 1]; + if (nr >= 0 && nc >= 0 && nr < ROWS && nc < COLS) { + if (grid[nr][nc] === 1) { + dsu.union(r * COLS + c, nr * COLS + nc); + } + } else { + dsu.union(N, r * COLS + c); + } + } + } + } + + const borderLand = dsu.size[dsu.find(N)]; + return land - borderLand + 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the given grid. \ No newline at end of file diff --git a/articles/number-of-flowers-in-full-bloom.md b/articles/number-of-flowers-in-full-bloom.md new file mode 100644 index 000000000..2b60ec927 --- /dev/null +++ b/articles/number-of-flowers-in-full-bloom.md @@ -0,0 +1,675 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def fullBloomFlowers(self, flowers: List[List[int]], people: List[int]) -> List[int]: + res = [] + + for time in people: + cnt = 0 + for start, end in flowers: + if start <= time <= end: + cnt += 1 + res.append(cnt) + + return res +``` + +```java +public class Solution { + public int[] fullBloomFlowers(int[][] flowers, int[] people) { + int m = people.length; + int[] res = new int[m]; + + for (int i = 0; i < m; i++) { + int count = 0; + for (int[] flower : flowers) { + if (flower[0] <= people[i] && people[i] <= flower[1]) { + count++; + } + } + res[i] = count; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector fullBloomFlowers(vector>& flowers, vector& people) { + int m = people.size(); + vector res(m); + + for (int i = 0; i < m; i++) { + int count = 0; + for (auto& flower : flowers) { + if (flower[0] <= people[i] && people[i] <= flower[1]) { + count++; + } + } + res[i] = count; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} flowers + * @param {number[]} people + * @return {number[]} + */ + fullBloomFlowers(flowers, people) { + let res = new Array(people.length).fill(0); + + for (let i = 0; i < people.length; i++) { + let count = 0; + for (let [start, end] of flowers) { + if (start <= people[i] && people[i] <= end) { + count++; + } + } + res[i] = count; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m)$ for the output array. + +> Where $n$ is the size of the array $flowers$, and $m$ is the size of the array $people$. + +--- + +## 2. Two Min-Heaps + +::tabs-start + +```python +class Solution: + def fullBloomFlowers(self, flowers: List[List[int]], people: List[int]) -> List[int]: + people = sorted((p, i) for i, p in enumerate(people)) + res = [0] * len(people) + count = 0 + + start = [f[0] for f in flowers] + end = [f[1] for f in flowers] + + heapq.heapify(start) + heapq.heapify(end) + + for p, i in people: + while start and start[0] <= p: + heapq.heappop(start) + count += 1 + while end and end[0] < p: + heapq.heappop(end) + count -= 1 + res[i] = count + + return res +``` + +```java +public class Solution { + public int[] fullBloomFlowers(int[][] flowers, int[] people) { + int m = people.length; + int[] res = new int[m]; + + List sortedPeople = new ArrayList<>(); + for (int i = 0; i < m; i++) { + sortedPeople.add(new int[]{people[i], i}); + } + sortedPeople.sort(Comparator.comparingInt(a -> a[0])); + + PriorityQueue startHeap = new PriorityQueue<>(); + PriorityQueue endHeap = new PriorityQueue<>(); + for (int[] f : flowers) { + startHeap.offer(f[0]); + endHeap.offer(f[1]); + } + + int count = 0; + for (int[] person : sortedPeople) { + int p = person[0], index = person[1]; + + while (!startHeap.isEmpty() && startHeap.peek() <= p) { + startHeap.poll(); + count++; + } + while (!endHeap.isEmpty() && endHeap.peek() < p) { + endHeap.poll(); + count--; + } + + res[index] = count; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector fullBloomFlowers(vector>& flowers, vector& people) { + int m = people.size(); + vector res(m); + + vector> sortedPeople; + for (int i = 0; i < m; i++) { + sortedPeople.push_back({people[i], i}); + } + sort(sortedPeople.begin(), sortedPeople.end()); + + priority_queue, greater> startHeap, endHeap; + for (const auto& f : flowers) { + startHeap.push(f[0]); + endHeap.push(f[1]); + } + + int count = 0; + for (const auto& person : sortedPeople) { + int p = person.first, index = person.second; + + while (!startHeap.empty() && startHeap.top() <= p) { + startHeap.pop(); + count++; + } + while (!endHeap.empty() && endHeap.top() < p) { + endHeap.pop(); + count--; + } + + res[index] = count; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} flowers + * @param {number[]} people + * @return {number[]} + */ + fullBloomFlowers(flowers, people) { + const m = people.length; + const res = new Array(m).fill(0); + + const sortedPeople = people.map((p, i) => [p, i]); + sortedPeople.sort((a, b) => a[0] - b[0]); + + const startHeap = new MinPriorityQueue(); + const endHeap = new MinPriorityQueue(); + for (const [s, e] of flowers) { + startHeap.enqueue(s); + endHeap.enqueue(e); + } + + let count = 0; + for (const [p, index] of sortedPeople) { + while (!startHeap.isEmpty() && startHeap.front() <= p) { + startHeap.dequeue(); + count++; + } + while (!endHeap.isEmpty() && endHeap.front() < p) { + endHeap.dequeue(); + count--; + } + res[index] = count; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m \log m + n \log n)$ +* Space complexity: $O(m + n)$ + +> Where $n$ is the size of the array $flowers$, and $m$ is the size of the array $people$. + +--- + +## 3. Min-Heap + +::tabs-start + +```python +class Solution: + def fullBloomFlowers(self, flowers: List[List[int]], people: List[int]) -> List[int]: + people = sorted((p, i) for i, p in enumerate(people)) + res = [0] * len(people) + flowers.sort() + end = [] + + j = 0 + for p, i in people: + while j < len(flowers) and flowers[j][0] <= p: + heapq.heappush(end, flowers[j][1]) + j += 1 + while end and end[0] < p: + heapq.heappop(end) + res[i] = len(end) + + return res +``` + +```java +public class Solution { + public int[] fullBloomFlowers(int[][] flowers, int[] people) { + int m = people.length; + int[] res = new int[m]; + int[][] indexedPeople = new int[m][2]; + + for (int i = 0; i < m; i++) { + indexedPeople[i] = new int[]{people[i], i}; + } + Arrays.sort(indexedPeople, Comparator.comparingInt(a -> a[0])); + Arrays.sort(flowers, Comparator.comparingInt(a -> a[0])); + + PriorityQueue endHeap = new PriorityQueue<>(); + int j = 0, n = flowers.length; + + for (int[] person : indexedPeople) { + int p = person[0], index = person[1]; + + while (j < n && flowers[j][0] <= p) { + endHeap.offer(flowers[j][1]); + j++; + } + while (!endHeap.isEmpty() && endHeap.peek() < p) { + endHeap.poll(); + } + res[index] = endHeap.size(); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector fullBloomFlowers(vector>& flowers, vector& people) { + int m = people.size(); + vector res(m); + vector> indexedPeople; + + for (int i = 0; i < m; i++) { + indexedPeople.emplace_back(people[i], i); + } + sort(indexedPeople.begin(), indexedPeople.end()); + sort(flowers.begin(), flowers.end()); + + priority_queue, greater> endHeap; + int j = 0, n = flowers.size(); + + for (auto [p, index] : indexedPeople) { + while (j < n && flowers[j][0] <= p) { + endHeap.push(flowers[j][1]); + j++; + } + while (!endHeap.empty() && endHeap.top() < p) { + endHeap.pop(); + } + res[index] = endHeap.size(); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} flowers + * @param {number[]} people + * @return {number[]} + */ + fullBloomFlowers(flowers, people) { + const m = people.length; + const res = new Array(m).fill(0); + const indexedPeople = people.map((p, i) => [p, i]); + + indexedPeople.sort((a, b) => a[0] - b[0]); + flowers.sort((a, b) => a[0] - b[0]); + + const endHeap = new MinPriorityQueue(); + let j = 0, n = flowers.length; + + for (const [p, index] of indexedPeople) { + while (j < n && flowers[j][0] <= p) { + endHeap.enqueue(flowers[j][1]); + j++; + } + while (!endHeap.isEmpty() && endHeap.front() < p) { + endHeap.dequeue(); + } + res[index] = endHeap.size(); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m \log m + n \log n)$ +* Space complexity: $O(m + n)$ + +> Where $n$ is the size of the array $flowers$, and $m$ is the size of the array $people$. + +--- + +## 4. Sorting + Two Pointers + +::tabs-start + +```python +class Solution: + def fullBloomFlowers(self, flowers: List[List[int]], people: List[int]) -> List[int]: + start = sorted(f[0] for f in flowers) + end = sorted(f[1] for f in flowers) + + res = [0] * len(people) + peopleIndex = sorted((p, i) for i, p in enumerate(people)) + + i = j = count = 0 + for p, index in peopleIndex: + while i < len(start) and start[i] <= p: + count += 1 + i += 1 + while j < len(end) and end[j] < p: + count -= 1 + j += 1 + res[index] = count + + return res +``` + +```java +public class Solution { + public int[] fullBloomFlowers(int[][] flowers, int[] people) { + int m = people.length; + int[] res = new int[m]; + List start = new ArrayList<>(), end = new ArrayList<>(); + for (int[] f : flowers) { + start.add(f[0]); + end.add(f[1]); + } + + Collections.sort(start); + Collections.sort(end); + + int count = 0, i = 0, j = 0; + List peopleIndex = new ArrayList<>(); + for (int k = 0; k < m; k++) { + peopleIndex.add(new int[]{people[k], k}); + } + peopleIndex.sort(Comparator.comparingInt(a -> a[0])); + + for (int[] p : peopleIndex) { + int time = p[0], index = p[1]; + + while (i < start.size() && start.get(i) <= time) { + count++; + i++; + } + while (j < end.size() && end.get(j) < time) { + count--; + j++; + } + res[index] = count; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector fullBloomFlowers(vector>& flowers, vector& people) { + int m = people.size(); + vector res(m), start, end; + for (auto& f : flowers) { + start.push_back(f[0]); + end.push_back(f[1]); + } + + sort(start.begin(), start.end()); + sort(end.begin(), end.end()); + + int count = 0, i = 0, j = 0; + vector> peopleIndex; + for (int k = 0; k < m; k++) { + peopleIndex.emplace_back(people[k], k); + } + sort(peopleIndex.begin(), peopleIndex.end()); + + for (auto& [p, index] : peopleIndex) { + while (i < start.size() && start[i] <= p) { + count++; + i++; + } + while (j < end.size() && end[j] < p) { + count--; + j++; + } + res[index] = count; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} flowers + * @param {number[]} people + * @return {number[]} + */ + fullBloomFlowers(flowers, people) { + const start = [], end = []; + for (let f of flowers) { + start.push(f[0]); + end.push(f[1]); + } + + start.sort((a, b) => a - b); + end.sort((a, b) => a - b); + + let count = 0, i = 0, j = 0; + const peopleIndex = people.map((p, idx) => [p, idx]); + peopleIndex.sort((a, b) => a[0] - b[0]); + + const res = new Array(people.length); + + for (let [p, index] of peopleIndex) { + while (i < start.length && start[i] <= p) { + count++; + i++; + } + while (j < end.length && end[j] < p) { + count--; + j++; + } + res[index] = count; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m \log m + n \log n)$ +* Space complexity: $O(m + n)$ + +> Where $n$ is the size of the array $flowers$, and $m$ is the size of the array $people$. + +--- + +## 5. Line Sweep + +::tabs-start + +```python +class Solution: + def fullBloomFlowers(self, flowers: List[List[int]], people: List[int]) -> List[int]: + events = [] + for start, end in flowers: + events.append((start, 1)) + events.append((end + 1, -1)) + + events.sort() + queries = sorted((p, i) for i, p in enumerate(people)) + res = [0] * len(people) + + count = j = 0 + for time, index in queries: + while j < len(events) and events[j][0] <= time: + count += events[j][1] + j += 1 + res[index] = count + + return res +``` + +```java +public class Solution { + public int[] fullBloomFlowers(int[][] flowers, int[] people) { + List events = new ArrayList<>(); + for (int[] f : flowers) { + events.add(new int[]{f[0], 1}); + events.add(new int[]{f[1] + 1, -1}); + } + + Collections.sort(events, (a, b) -> a[0] - b[0]); + int[][] queries = new int[people.length][2]; + for (int i = 0; i < people.length; i++) { + queries[i] = new int[]{people[i], i}; + } + Arrays.sort(queries, (a, b) -> Integer.compare(a[0], b[0])); + + int[] res = new int[people.length]; + int count = 0, j = 0; + for (int[] query : queries) { + int time = query[0], index = query[1]; + while (j < events.size() && events.get(j)[0] <= time) { + count += events.get(j)[1]; + j++; + } + res[index] = count; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector fullBloomFlowers(vector>& flowers, vector& people) { + vector> events; + for (auto& f : flowers) { + events.emplace_back(f[0], 1); + events.emplace_back(f[1] + 1, -1); + } + + sort(events.begin(), events.end()); + vector> queries; + for (int i = 0; i < people.size(); i++) { + queries.emplace_back(people[i], i); + } + + sort(queries.begin(), queries.end()); + vector res(people.size()); + int count = 0, j = 0; + + for (auto& [time, index] : queries) { + while (j < events.size() && events[j].first <= time) { + count += events[j++].second; + } + res[index] = count; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} flowers + * @param {number[]} people + * @return {number[]} + */ + fullBloomFlowers(flowers, people) { + let events = []; + for (let [start, end] of flowers) { + events.push([start, 1]); + events.push([end + 1, -1]); + } + + events.sort((a, b) => a[0] - b[0]); + let queries = people.map((p, i) => [p, i]).sort((a, b) => a[0] - b[0]); + let res = new Array(people.length).fill(0); + + let count = 0, j = 0; + for (let [time, index] of queries) { + while (j < events.length && events[j][0] <= time) { + count += events[j][1]; + j++; + } + res[index] = count; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m \log m + n \log n)$ +* Space complexity: $O(m + n)$ + +> Where $n$ is the size of the array $flowers$, and $m$ is the size of the array $people$. \ No newline at end of file diff --git a/articles/number-of-good-pairs.md b/articles/number-of-good-pairs.md new file mode 100644 index 000000000..ea3e819e8 --- /dev/null +++ b/articles/number-of-good-pairs.md @@ -0,0 +1,221 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def numIdenticalPairs(self, nums: List[int]) -> int: + res = 0 + for i in range(len(nums)): + for j in range(i + 1, len(nums)): + if nums[i] == nums[j]: + res += 1 + return res +``` + +```java +public class Solution { + public int numIdenticalPairs(int[] nums) { + int res = 0; + for (int i = 0; i < nums.length; i++) { + for (int j = i + 1; j < nums.length; j++) { + if (nums[i] == nums[j]) { + res++; + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numIdenticalPairs(vector& nums) { + int res = 0; + for (int i = 0; i < nums.size(); i++) { + for (int j = i + 1; j < nums.size(); j++) { + if (nums[i] == nums[j]) { + res++; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + numIdenticalPairs(nums) { + let res = 0; + for (let i = 0; i < nums.length; i++) { + for (let j = i + 1; j < nums.length; j++) { + if (nums[i] == nums[j]) { + res++; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Hash Map (Math) + +::tabs-start + +```python +class Solution: + def numIdenticalPairs(self, nums: List[int]) -> int: + count = Counter(nums) + res = 0 + for num, c in count.items(): + res += c * (c - 1) // 2 + return res +``` + +```java +public class Solution { + public int numIdenticalPairs(int[] nums) { + Map count = new HashMap<>(); + int res = 0; + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + for (int c : count.values()) { + res += c * (c - 1) / 2; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numIdenticalPairs(vector& nums) { + unordered_map count; + int res = 0; + for (int num : nums) { + count[num]++; + } + for (auto& [num, c] : count) { + res += c * (c - 1) / 2; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + numIdenticalPairs(nums) { + const count = {}; + let res = 0; + for (const num of nums) { + count[num] = (count[num] || 0) + 1; + } + for (const c of Object.values(count)) { + res += c * (c - 1) / 2; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Hash Map + +::tabs-start + +```python +class Solution: + def numIdenticalPairs(self, nums: List[int]) -> int: + count = defaultdict(int) + res = 0 + for num in nums: + res += count[num] + count[num] += 1 + return res +``` + +```java +public class Solution { + public int numIdenticalPairs(int[] nums) { + Map count = new HashMap<>(); + int res = 0; + for (int num : nums) { + res += count.getOrDefault(num, 0); + count.put(num, count.getOrDefault(num, 0) + 1); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numIdenticalPairs(vector& nums) { + unordered_map count; + int res = 0; + for (int num : nums) { + res += count[num]; + count[num]++; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + numIdenticalPairs(nums) { + const count = {}; + let res = 0; + for (const num of nums) { + res += count[num] || 0; + count[num] = (count[num] || 0) + 1; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/number-of-good-paths.md b/articles/number-of-good-paths.md new file mode 100644 index 000000000..f0e16529f --- /dev/null +++ b/articles/number-of-good-paths.md @@ -0,0 +1,861 @@ +## 1. Brute Force (DFS) + +::tabs-start + +```python +class Solution: + def numberOfGoodPaths(self, vals: List[int], edges: List[List[int]]) -> int: + n = len(vals) + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + adj[v].append(u) + + def dfs(node, startNode, parent): + if vals[node] > vals[startNode]: + return 0 + + res = 0 + if vals[node] == vals[startNode] and node >= startNode: + res += 1 + + for child in adj[node]: + if child == parent: + continue + res += dfs(child, startNode, node) + + return res + + + res = 0 + for node in range(n): + res += dfs(node, node, -1) + return res +``` + +```java +public class Solution { + public int numberOfGoodPaths(int[] vals, int[][] edges) { + int n = vals.length; + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + adj[edge[1]].add(edge[0]); + } + + int res = 0; + for (int node = 0; node < n; node++) { + res += dfs(node, node, -1, vals, adj); + } + return res; + } + + private int dfs(int node, int startNode, int parent, int[] vals, List[] adj) { + if (vals[node] > vals[startNode]) { + return 0; + } + + int res = 0; + if (vals[node] == vals[startNode] && node >= startNode) { + res += 1; + } + + for (int child : adj[node]) { + if (child == parent) { + continue; + } + res += dfs(child, startNode, node, vals, adj); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numberOfGoodPaths(vector& vals, vector>& edges) { + int n = vals.size(); + vector> adj(n); + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + int res = 0; + for (int node = 0; node < n; node++) { + res += dfs(node, node, -1, vals, adj); + } + return res; + } + +private: + int dfs(int node, int startNode, int parent, vector& vals, vector>& adj) { + if (vals[node] > vals[startNode]) { + return 0; + } + + int res = 0; + if (vals[node] == vals[startNode] && node >= startNode) { + res += 1; + } + + for (int child : adj[node]) { + if (child == parent) { + continue; + } + res += dfs(child, startNode, node, vals, adj); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} vals + * @param {number[][]} edges + * @return {number} + */ + numberOfGoodPaths(vals, edges) { + const n = vals.length; + const adj = Array.from({ length: n }, () => []); + + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + } + + const dfs = (node, startNode, parent) => { + if (vals[node] > vals[startNode]) { + return 0; + } + + let res = 0; + if (vals[node] === vals[startNode] && node >= startNode) { + res += 1; + } + + for (const child of adj[node]) { + if (child === parent) continue; + res += dfs(child, startNode, node); + } + return res; + }; + + let res = 0; + for (let node = 0; node < n; node++) { + res += dfs(node, node, -1); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Brute Force (BFS) + +::tabs-start + +```python +class Solution: + def numberOfGoodPaths(self, vals, edges): + n = len(vals) + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + adj[v].append(u) + + res = 0 + for startNode in range(n): + q = deque([startNode]) + visited = set([startNode]) + count = 0 + + while q: + node = q.popleft() + if vals[node] == vals[startNode] and node >= startNode: + count += 1 + + for child in adj[node]: + if child not in visited and vals[child] <= vals[startNode]: + visited.add(child) + q.append(child) + + res += count + + return res +``` + +```java +public class Solution { + public int numberOfGoodPaths(int[] vals, int[][] edges) { + int n = vals.length; + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + adj[edge[1]].add(edge[0]); + } + + int res = 0; + for (int startNode = 0; startNode < n; startNode++) { + Queue q = new LinkedList<>(); + Set visited = new HashSet<>(); + q.offer(startNode); + visited.add(startNode); + int count = 0; + + while (!q.isEmpty()) { + int node = q.poll(); + if (vals[node] == vals[startNode] && node >= startNode) { + count++; + } + + for (int child : adj[node]) { + if (!visited.contains(child) && vals[child] <= vals[startNode]) { + visited.add(child); + q.offer(child); + } + } + } + + res += count; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numberOfGoodPaths(vector& vals, vector>& edges) { + int n = vals.size(); + vector> adj(n); + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + int res = 0; + for (int startNode = 0; startNode < n; startNode++) { + queue q; + unordered_set visited; + q.push(startNode); + visited.insert(startNode); + int count = 0; + + while (!q.empty()) { + int node = q.front(); + q.pop(); + if (vals[node] == vals[startNode] && node >= startNode) { + count++; + } + + for (int child : adj[node]) { + if (visited.find(child) == visited.end() && vals[child] <= vals[startNode]) { + visited.insert(child); + q.push(child); + } + } + } + + res += count; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} vals + * @param {number[][]} edges + * @return {number} + */ + numberOfGoodPaths(vals, edges) { + const n = vals.length; + const adj = Array.from({ length: n }, () => []); + + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + } + + let res = 0; + for (let startNode = 0; startNode < n; startNode++) { + const q = new Queue([startNode]); + const visited = new Set([startNode]); + let count = 0; + + while (q.length) { + let node = q.shift(); + if (vals[node] === vals[startNode] && node >= startNode) { + count++; + } + + for (const child of adj[node]) { + if (!visited.has(child) && vals[child] <= vals[startNode]) { + visited.add(child); + q.push(child); + } + } + } + + res += count; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu, pv = self.find(u), self.find(v) + if pu == pv: + return False + if self.Size[pu] >= self.Size[pv]: + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + else: + self.Size[pv] += self.Size[pu] + self.Parent[pu] = pv + return True + +class Solution: + def numberOfGoodPaths(self, vals: List[int], edges: List[List[int]]) -> int: + adj = collections.defaultdict(list) + for a, b in edges: + adj[a].append(b) + adj[b].append(a) + + valToIndex = collections.defaultdict(list) + for i, val in enumerate(vals): + valToIndex[val].append(i) + + res = 0 + uf = DSU(len(vals)) + + for val in sorted(valToIndex.keys()): + for i in valToIndex[val]: + for nei in adj[i]: + if vals[nei] <= vals[i]: + uf.union(nei, i) + + count = collections.defaultdict(int) + for i in valToIndex[val]: + count[uf.find(i)] += 1 + res += count[uf.find(i)] + + return res +``` + +```java +class DSU { + private int[] parent, size; + + public DSU(int n) { + parent = new int[n + 1]; + size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + parent[i] = i; + size[i] = 1; + } + } + + public int find(int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (size[pu] >= size[pv]) { + size[pu] += size[pv]; + parent[pv] = pu; + } else { + size[pv] += size[pu]; + parent[pu] = pv; + } + return true; + } +} + +public class Solution { + public int numberOfGoodPaths(int[] vals, int[][] edges) { + int n = vals.length; + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + adj[edge[1]].add(edge[0]); + } + + TreeMap> valToIndex = new TreeMap<>(); + for (int i = 0; i < n; i++) { + valToIndex.putIfAbsent(vals[i], new ArrayList<>()); + valToIndex.get(vals[i]).add(i); + } + + DSU dsu = new DSU(n); + int res = 0; + + for (int val : valToIndex.keySet()) { + for (int i : valToIndex.get(val)) { + for (int nei : adj[i]) { + if (vals[nei] <= vals[i]) dsu.union(nei, i); + } + } + + Map count = new HashMap<>(); + for (int i : valToIndex.get(val)) { + int root = dsu.find(i); + count.put(root, count.getOrDefault(root, 0) + 1); + res += count.get(root); + } + } + return res; + } +} +``` + +```cpp +class DSU { +public: + vector parent, size; + + DSU(int n) { + parent.resize(n + 1); + size.resize(n + 1, 1); + for (int i = 0; i <= n; i++) parent[i] = i; + } + + int find(int node) { + if (parent[node] != node) parent[node] = find(parent[node]); + return parent[node]; + } + + bool unite(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (size[pu] >= size[pv]) { + size[pu] += size[pv]; + parent[pv] = pu; + } else { + size[pv] += size[pu]; + parent[pu] = pv; + } + return true; + } +}; + +class Solution { +public: + int numberOfGoodPaths(vector& vals, vector>& edges) { + int n = vals.size(); + vector> adj(n); + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + map> valToIndex; + for (int i = 0; i < n; i++) valToIndex[vals[i]].push_back(i); + + DSU dsu(n); + int res = 0; + + for (auto& [val, nodes] : valToIndex) { + for (int& i : nodes) { + for (int& nei : adj[i]) { + if (vals[nei] <= vals[i]) dsu.unite(nei, i); + } + } + + unordered_map count; + for (int& i : nodes) { + int root = dsu.find(i); + count[root]++; + res += count[root]; + } + } + return res; + } +}; +``` + +```javascript +class DSU { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.parent = Array.from({ length: n + 1 }, (_, i) => i); + this.size = new Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.parent[node] !== node) { + this.parent[node] = this.find(this.parent[node]); + } + return this.parent[node]; + } + + /** + * @param {number} u + * @param {number} u=v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u), pv = this.find(v); + if (pu === pv) return false; + if (this.size[pu] >= this.size[pv]) { + this.size[pu] += this.size[pv]; + this.parent[pv] = pu; + } else { + this.size[pv] += this.size[pu]; + this.parent[pu] = pv; + } + return true; + } +} + +class Solution { + /** + * @param {number[]} vals + * @param {number[][]} edges + * @return {number} + */ + numberOfGoodPaths(vals, edges) { + const n = vals.length; + const adj = Array.from({ length: n }, () => []); + + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + } + + const valToIndex = new Map(); + for (let i = 0; i < n; i++) { + if (!valToIndex.has(vals[i])) valToIndex.set(vals[i], []); + valToIndex.get(vals[i]).push(i); + } + + const dsu = new DSU(n); + let res = 0; + + for (const [val, nodes] of [...valToIndex.entries()].sort((a, b) => a[0] - b[0])) { + for (const i of nodes) { + for (const nei of adj[i]) { + if (vals[nei] <= vals[i]) dsu.union(nei, i); + } + } + + const count = new Map(); + for (const i of nodes) { + const root = dsu.find(i); + count.set(root, (count.get(root) || 0) + 1); + res += count.get(root); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Disjoint Set Union (Union By Value) + +::tabs-start + +```python +class DSU: + def __init__(self, n, vals): + self.parent = list(range(n)) + self.vals = vals + self.count = [1] * n # count of nodes with max value of the component + + def find(self, node): + if self.parent[node] != node: + self.parent[node] = self.find(self.parent[node]) + return self.parent[node] + + def union(self, u, v): + pu, pv = self.find(u), self.find(v) + if pu == pv: + return 0 + if self.vals[pu] < self.vals[pv]: + self.parent[pu] = pv + elif self.vals[pu] > self.vals[pv]: + self.parent[pv] = pu + else: + self.parent[pv] = pu + result = self.count[pu] * self.count[pv] + self.count[pu] += self.count[pv] + return result + + return 0 + + +class Solution: + def numberOfGoodPaths(self, vals: List[int], edges: List[List[int]]) -> int: + n = len(vals) + dsu = DSU(n, vals) + + # Sort edges based on max value of the two nodes + edges.sort(key=lambda edge: max(vals[edge[0]], vals[edge[1]])) + + res = n # Each node alone is a good path + for u, v in edges: + res += dsu.union(u, v) + return res +``` + +```java +class DSU { + private int[] parent, count, vals; + + public DSU(int n, int[] vals) { + this.parent = new int[n]; + this.vals = vals; + this.count = new int[n]; // count of nodes with max value of the component + Arrays.fill(count, 1); + + for (int i = 0; i < n; i++) { + parent[i] = i; + } + } + + public int find(int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + public int union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) { + return 0; + } + if (vals[pu] < vals[pv]) { + parent[pu] = pv; + } else if (vals[pu] > vals[pv]) { + parent[pv] = pu; + } else { + parent[pv] = pu; + int result = count[pu] * count[pv]; + count[pu] += count[pv]; + return result; + } + return 0; + } +} + +public class Solution { + public int numberOfGoodPaths(int[] vals, int[][] edges) { + int n = vals.length; + DSU dsu = new DSU(n, vals); + + // Sort edges based on max value of the two nodes + Arrays.sort(edges, + Comparator.comparingInt(edge -> Math.max(vals[edge[0]], vals[edge[1]])) + ); + + int res = n; // Each node alone is a good path + for (int[] edge : edges) { + res += dsu.union(edge[0], edge[1]); + } + return res; + } +} +``` + +```cpp +class DSU { + vector parent, count, vals; + +public: + DSU(int n, vector& vals) : vals(vals), parent(n), count(n, 1) { + for (int i = 0; i < n; i++) parent[i] = i; + } + + int find(int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + int unionNodes(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) { + return 0; + } + if (vals[pu] < vals[pv]) { + parent[pu] = pv; + } else if (vals[pu] > vals[pv]) { + parent[pv] = pu; + } else { + parent[pv] = pu; + int result = count[pu] * count[pv]; + count[pu] += count[pv]; + return result; + } + return 0; + } +}; + +class Solution { +public: + int numberOfGoodPaths(vector& vals, vector>& edges) { + int n = vals.size(); + DSU dsu(n, vals); + + // Sort edges based on max value of the two nodes + sort(edges.begin(), edges.end(), [&](auto& a, auto& b) { + return max(vals[a[0]], vals[a[1]]) < max(vals[b[0]], vals[b[1]]); + }); + + int res = n; // Each node alone is a good path + for (auto& edge : edges) { + res += dsu.unionNodes(edge[0], edge[1]); + } + return res; + } +}; +``` + +```javascript +class DSU { + /** + * @param {number} n + * @param {number[]} vals + */ + constructor(n, vals) { + this.parent = Array(n).fill(0).map((_, i) => i); + this.vals = vals; + this.count = Array(n).fill(1); // count of nodes with max value of the component + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.parent[node] !== node) { + this.parent[node] = this.find(this.parent[node]); + } + return this.parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {number} + */ + union(u, v) { + let pu = this.find(u), pv = this.find(v); + if (pu === pv) { + return 0; + } + if (this.vals[pu] < this.vals[pv]) { + this.parent[pu] = pv; + } else if (this.vals[pu] > this.vals[pv]) { + this.parent[pv] = pu; + } else { + this.parent[pv] = pu; + let result = this.count[pu] * this.count[pv]; + this.count[pu] += this.count[pv]; + return result; + } + return 0; + } +} + +class Solution { + /** + * @param {number[]} vals + * @param {number[][]} edges + * @return {number} + */ + numberOfGoodPaths(vals, edges) { + let n = vals.length; + let dsu = new DSU(n, vals); + + // Sort edges based on max value of the two nodes + edges.sort((a, b) => + Math.max(vals[a[0]], vals[a[1]]) - Math.max(vals[b[0]], vals[b[1]]) + ); + + let res = n; // Each node alone is a good path + for (let [u, v] of edges) { + res += dsu.union(u, v); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/number-of-laser-beams-in-a-bank.md b/articles/number-of-laser-beams-in-a-bank.md new file mode 100644 index 000000000..598d7fbb2 --- /dev/null +++ b/articles/number-of-laser-beams-in-a-bank.md @@ -0,0 +1,110 @@ +## 1. Counting + +::tabs-start + +```python +class Solution: + def numberOfBeams(self, bank: List[str]) -> int: + prev = bank[0].count("1") + res = 0 + + for i in range(1, len(bank)): + curr = bank[i].count("1") + if curr: + res += prev * curr + prev = curr + + return res +``` + +```java +public class Solution { + public int numberOfBeams(String[] bank) { + int prev = countOnes(bank[0]); + int res = 0; + + for (int i = 1; i < bank.length; i++) { + int curr = countOnes(bank[i]); + if (curr > 0) { + res += prev * curr; + prev = curr; + } + } + + return res; + } + + private int countOnes(String s) { + int count = 0; + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == '1') count++; + } + return count; + } +} +``` + +```cpp +class Solution { +public: + int numberOfBeams(vector& bank) { + int prev = countOnes(bank[0]); + int res = 0; + + for (int i = 1; i < bank.size(); i++) { + int curr = countOnes(bank[i]); + if (curr > 0) { + res += prev * curr; + prev = curr; + } + } + + return res; + } + +private: + int countOnes(const string& s) { + return count(s.begin(), s.end(), '1'); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} bank + * @return {number} + */ + numberOfBeams(bank) { + const countOnes = (s) => { + let cnt = 0; + for (let c of s) { + if (c === '1') cnt += 1; + } + return cnt; + }; + + let prev = countOnes(bank[0]); + let res = 0; + + for (let i = 1; i < bank.length; i++) { + let curr = countOnes(bank[i]); + if (curr > 0) { + res += prev * curr; + prev = curr; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ is the number of rows and $n$ is the number of columns. \ No newline at end of file diff --git a/articles/number-of-longest-increasing-subsequence.md b/articles/number-of-longest-increasing-subsequence.md new file mode 100644 index 000000000..f11ef6599 --- /dev/null +++ b/articles/number-of-longest-increasing-subsequence.md @@ -0,0 +1,699 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def findNumberOfLIS(self, nums: List[int]) -> int: + LIS = 0 + res = 0 + + def dfs(i, length): + nonlocal LIS, res + if LIS < length: + LIS = length + res = 1 + elif LIS == length: + res += 1 + + for j in range(i + 1, len(nums)): + if nums[j] <= nums[i]: + continue + dfs(j, length + 1) + + for i in range(len(nums)): + dfs(i, 1) + return res +``` + +```java +public class Solution { + private int LIS = 0; + private int res = 0; + + public int findNumberOfLIS(int[] nums) { + for (int i = 0; i < nums.length; i++) { + dfs(nums, i, 1); + } + return res; + } + + private void dfs(int[] nums, int i, int length) { + if (LIS < length) { + LIS = length; + res = 1; + } else if (LIS == length) { + res++; + } + + for (int j = i + 1; j < nums.length; j++) { + if (nums[j] <= nums[i]) { + continue; + } + dfs(nums, j, length + 1); + } + } +} +``` + +```cpp +class Solution { + int LIS = 0; + int res = 0; + + void dfs(vector& nums, int i, int length) { + if (LIS < length) { + LIS = length; + res = 1; + } else if (LIS == length) { + res++; + } + + for (int j = i + 1; j < nums.size(); j++) { + if (nums[j] <= nums[i]) { + continue; + } + dfs(nums, j, length + 1); + } + } + +public: + int findNumberOfLIS(vector& nums) { + for (int i = 0; i < nums.size(); i++) { + dfs(nums, i, 1); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findNumberOfLIS(nums) { + let LIS = 0; + let res = 0; + + const dfs = (i, length) => { + if (LIS < length) { + LIS = length; + res = 1; + } else if (LIS === length) { + res++; + } + + for (let j = i + 1; j < nums.length; j++) { + if (nums[j] <= nums[i]) continue; + dfs(j, length + 1); + } + }; + + for (let i = 0; i < nums.length; i++) { + dfs(i, 1); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def findNumberOfLIS(self, nums: List[int]) -> int: + dp = {} + + def dfs(i): + if i in dp: + return + + maxLen = maxCnt = 1 + for j in range(i + 1, len(nums)): + if nums[j] > nums[i]: + dfs(j) + length, count = dp[j] + if 1 + length > maxLen: + maxLen = length + 1 + maxCnt = count + elif 1 + length == maxLen: + maxCnt += count + dp[i] = (maxLen, maxCnt) + + lenLIS = res = 0 + for i in range(len(nums)): + dfs(i) + maxLen, maxCnt = dp[i] + if maxLen > lenLIS: + lenLIS = maxLen + res = maxCnt + elif maxLen == lenLIS: + res += maxCnt + + return res +``` + +```java +public class Solution { + private int[][] dp; + + public int findNumberOfLIS(int[] nums) { + int n = nums.length; + dp = new int[n][2]; // dp[i][0] = maxLen, dp[i][1] = maxCnt + + for (int i = 0; i < n; i++) { + dp[i][0] = dp[i][1] = -1; + } + + int lenLIS = 0, res = 0; + for (int i = 0; i < n; i++) { + dfs(nums, i); + int[] result = dp[i]; + if (result[0] > lenLIS) { + lenLIS = result[0]; + res = result[1]; + } else if (result[0] == lenLIS) { + res += result[1]; + } + } + return res; + } + + private void dfs(int[] nums, int i) { + if (dp[i][0] != -1) return; + + int maxLen = 1, maxCnt = 1; + for (int j = i + 1; j < nums.length; j++) { + if (nums[j] > nums[i]) { + dfs(nums, j); + int[] next = dp[j]; + if (1 + next[0] > maxLen) { + maxLen = 1 + next[0]; + maxCnt = next[1]; + } else if (1 + next[0] == maxLen) { + maxCnt += next[1]; + } + } + } + + dp[i] = new int[]{maxLen, maxCnt}; + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + + void dfs(vector& nums, int i) { + if (dp[i][0] != -1) return; + + int maxLen = 1, maxCnt = 1; + for (int j = i + 1; j < nums.size(); j++) { + if (nums[j] > nums[i]) { + dfs(nums, j); + int length = dp[j][0]; + int count = dp[j][1]; + if (1 + length > maxLen) { + maxLen = 1 + length; + maxCnt = count; + } else if (1 + length == maxLen) { + maxCnt += count; + } + } + } + dp[i] = {maxLen, maxCnt}; + } + +public: + int findNumberOfLIS(vector& nums) { + int n = nums.size(); + dp.assign(n, vector(2, -1)); + + int lenLIS = 0, res = 0; + for (int i = 0; i < n; i++) { + dfs(nums, i); + int maxLen = dp[i][0]; + int maxCnt = dp[i][1]; + if (maxLen > lenLIS) { + lenLIS = maxLen; + res = maxCnt; + } else if (maxLen == lenLIS) { + res += maxCnt; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findNumberOfLIS(nums) { + const dp = new Map(); + + const dfs = (i) => { + if (dp.has(i)) return; + + let maxLen = 1, maxCnt = 1; + for (let j = i + 1; j < nums.length; j++) { + if (nums[j] > nums[i]) { + dfs(j); + const [length, count] = dp.get(j); + if (1 + length > maxLen) { + maxLen = 1 + length; + maxCnt = count; + } else if (1 + length === maxLen) { + maxCnt += count; + } + } + } + dp.set(i, [maxLen, maxCnt]); + }; + + let lenLIS = 0, res = 0; + for (let i = 0; i < nums.length; i++) { + dfs(i); + const [maxLen, maxCnt] = dp.get(i); + if (maxLen > lenLIS) { + lenLIS = maxLen; + res = maxCnt; + } else if (maxLen === lenLIS) { + res += maxCnt; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def findNumberOfLIS(self, nums: List[int]) -> int: + n = len(nums) + dp = [[0, 0] for _ in range(n)] + lenLIS, res = 0, 0 + + for i in range(n - 1, -1, -1): + maxLen, maxCnt = 1, 1 + for j in range(i + 1, n): + if nums[j] > nums[i]: + length, count = dp[j] + if length + 1 > maxLen: + maxLen, maxCnt = length + 1, count + elif length + 1 == maxLen: + maxCnt += count + + if maxLen > lenLIS: + lenLIS, res = maxLen, maxCnt + elif maxLen == lenLIS: + res += maxCnt + dp[i] = [maxLen, maxCnt] + + return res +``` + +```java +public class Solution { + public int findNumberOfLIS(int[] nums) { + int n = nums.length; + int[][] dp = new int[n][2]; + int lenLIS = 0, res = 0; + + for (int i = n - 1; i >= 0; i--) { + int maxLen = 1, maxCnt = 1; + for (int j = i + 1; j < n; j++) { + if (nums[j] > nums[i]) { + int length = dp[j][0]; + int count = dp[j][1]; + if (length + 1 > maxLen) { + maxLen = length + 1; + maxCnt = count; + } else if (length + 1 == maxLen) { + maxCnt += count; + } + } + } + + if (maxLen > lenLIS) { + lenLIS = maxLen; + res = maxCnt; + } else if (maxLen == lenLIS) { + res += maxCnt; + } + dp[i][0] = maxLen; + dp[i][1] = maxCnt; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int findNumberOfLIS(vector& nums) { + int n = nums.size(); + vector> dp(n, vector(2, 0)); + int lenLIS = 0, res = 0; + + for (int i = n - 1; i >= 0; i--) { + int maxLen = 1, maxCnt = 1; + for (int j = i + 1; j < n; j++) { + if (nums[j] > nums[i]) { + int length = dp[j][0]; + int count = dp[j][1]; + if (length + 1 > maxLen) { + maxLen = length + 1; + maxCnt = count; + } else if (length + 1 == maxLen) { + maxCnt += count; + } + } + } + + if (maxLen > lenLIS) { + lenLIS = maxLen; + res = maxCnt; + } else if (maxLen == lenLIS) { + res += maxCnt; + } + dp[i][0] = maxLen; + dp[i][1] = maxCnt; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findNumberOfLIS(nums) { + const n = nums.length; + const dp = Array.from({ length: n }, () => [0, 0]); + let lenLIS = 0, res = 0; + + for (let i = n - 1; i >= 0; i--) { + let maxLen = 1, maxCnt = 1; + for (let j = i + 1; j < n; j++) { + if (nums[j] > nums[i]) { + const [length, count] = dp[j]; + if (length + 1 > maxLen) { + maxLen = length + 1; + maxCnt = count; + } else if (length + 1 === maxLen) { + maxCnt += count; + } + } + } + + if (maxLen > lenLIS) { + lenLIS = maxLen; + res = maxCnt; + } else if (maxLen === lenLIS) { + res += maxCnt; + } + dp[i] = [maxLen, maxCnt]; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Binary Search + Prefix Sum) + +::tabs-start + +```python +class Solution: + def findNumberOfLIS(self, nums: List[int]) -> int: + n = len(nums) + dp = [[[0, 0], [nums[0], 1]]] + + def bs1(num): + l, r = 0, len(dp) - 1 + j = len(dp) - 1 + while l <= r: + mid = (l + r) // 2 + if dp[mid][-1][0] < num: + l = mid + 1 + else: + j = mid + r = mid - 1 + return j + + def bs2(i, num): + if i < 0: + return 1 + l, r = 1, len(dp[i]) - 1 + j = 0 + while l <= r: + mid = (l + r) // 2 + if dp[i][mid][0] >= num: + j = mid + l = mid + 1 + else: + r = mid - 1 + return dp[i][-1][1] - dp[i][j][1] + + LIS = 1 + for i in range(1, n): + num = nums[i] + if num > dp[-1][-1][0]: + count = bs2(LIS - 1, num) + dp.append([[0, 0], [num, count]]) + LIS += 1 + else: + j = bs1(num) + count = bs2(j - 1, num) + dp[j].append([num, dp[j][-1][1] + count]) + + return dp[-1][-1][1] +``` + +```java +public class Solution { + public int findNumberOfLIS(int[] nums) { + int n = nums.length; + List> dp = new ArrayList<>(); + List first = new ArrayList<>(); + first.add(new int[]{0, 0}); + first.add(new int[]{nums[0], 1}); + dp.add(first); + + int LIS = 1; + + for (int i = 1; i < n; i++) { + int num = nums[i]; + if (num > dp.get(dp.size() - 1).get(dp.get(dp.size() - 1).size() - 1)[0]) { + int count = bs2(dp, LIS - 1, num); + List newList = new ArrayList<>(); + newList.add(new int[]{0, 0}); + newList.add(new int[]{num, count}); + dp.add(newList); + LIS++; + } else { + int j = bs1(dp, num); + int count = bs2(dp, j - 1, num); + List list = dp.get(j); + int[] last = list.get(list.size() - 1); + list.add(new int[]{num, last[1] + count}); + } + } + + return dp.get(dp.size() - 1).get(dp.get(dp.size() - 1).size() - 1)[1]; + } + + private int bs1(List> dp, int num) { + int l = 0, r = dp.size() - 1, j = dp.size() - 1; + while (l <= r) { + int mid = (l + r) / 2; + if (dp.get(mid).get(dp.get(mid).size() - 1)[0] < num) { + l = mid + 1; + } else { + j = mid; + r = mid - 1; + } + } + return j; + } + + private int bs2(List> dp, int i, int num) { + if (i < 0) return 1; + int l = 1, r = dp.get(i).size() - 1, j = 0; + while (l <= r) { + int mid = (l + r) / 2; + if (dp.get(i).get(mid)[0] >= num) { + j = mid; + l = mid + 1; + } else { + r = mid - 1; + } + } + return dp.get(i).get(dp.get(i).size() - 1)[1] - dp.get(i).get(j)[1]; + } +} +``` + +```cpp +class Solution { +public: + int findNumberOfLIS(vector& nums) { + vector>> dp = {{{0, 0}, {nums[0], 1}}}; + int LIS = 1; + + for (int i = 1; i < nums.size(); i++) { + int num = nums[i]; + if (num > dp.back().back().first) { + int count = bs2(dp, LIS - 1, num); + dp.push_back({{0, 0}, {num, count}}); + LIS++; + } else { + int j = bs1(dp, num); + int count = bs2(dp, j - 1, num); + dp[j].push_back({num, dp[j].back().second + count}); + } + } + + return dp.back().back().second; + } + +private: + int bs1(vector>>& dp, int num) { + int l = 0, r = dp.size() - 1, j = dp.size() - 1; + while (l <= r) { + int mid = (l + r) / 2; + if (dp[mid].back().first < num) { + l = mid + 1; + } else { + j = mid; + r = mid - 1; + } + } + return j; + } + + int bs2(vector>>& dp, int i, int num) { + if (i < 0) return 1; + int l = 1, r = dp[i].size() - 1, j = 0; + while (l <= r) { + int mid = (l + r) / 2; + if (dp[i][mid].first >= num) { + j = mid; + l = mid + 1; + } else { + r = mid - 1; + } + } + return dp[i].back().second - dp[i][j].second; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + findNumberOfLIS(nums) { + const dp = [[[0, 0], [nums[0], 1]]]; + let LIS = 1; + + const bs1 = (num) => { + let l = 0, r = dp.length - 1, j = dp.length - 1; + while (l <= r) { + const mid = Math.floor((l + r) / 2); + if (dp[mid][dp[mid].length - 1][0] < num) { + l = mid + 1; + } else { + j = mid; + r = mid - 1; + } + } + return j; + }; + + const bs2 = (i, num) => { + if (i < 0) return 1; + let l = 1, r = dp[i].length - 1, j = 0; + while (l <= r) { + const mid = Math.floor((l + r) / 2); + if (dp[i][mid][0] >= num) { + j = mid; + l = mid + 1; + } else { + r = mid - 1; + } + } + return dp[i][dp[i].length - 1][1] - dp[i][j][1]; + }; + + for (let i = 1; i < nums.length; i++) { + const num = nums[i]; + if (num > dp[dp.length - 1][dp[dp.length - 1].length - 1][0]) { + const count = bs2(LIS - 1, num); + dp.push([[0, 0], [num, count]]); + LIS++; + } else { + const j = bs1(num); + const count = bs2(j - 1, num); + dp[j].push([num, dp[j][dp[j].length - 1][1] + count]); + } + } + + return dp[dp.length - 1][dp[dp.length - 1].length - 1][1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/number-of-music-playlists.md b/articles/number-of-music-playlists.md new file mode 100644 index 000000000..284e35bae --- /dev/null +++ b/articles/number-of-music-playlists.md @@ -0,0 +1,340 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def numMusicPlaylists(self, n: int, goal: int, k: int) -> int: + mod = 10**9 + 7 + dp = {} + + def count(cur_goal, old_songs): + if cur_goal == 0 and old_songs == n: + return 1 + if cur_goal == 0 or old_songs > n: + return 0 + if (cur_goal, old_songs) in dp: + return dp[(cur_goal, old_songs)] + + res = (n - old_songs) * count(cur_goal - 1, old_songs + 1) + if old_songs > k: + res += (old_songs - k) * count(cur_goal - 1, old_songs) + dp[(cur_goal, old_songs)] = res % mod + return dp[(cur_goal, old_songs)] + + return count(goal, 0) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + private int[][] dp; + + public int numMusicPlaylists(int n, int goal, int k) { + dp = new int[goal + 1][n + 1]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + return count(goal, 0, n, k); + } + + private int count(int curGoal, int oldSongs, int n, int k) { + if (curGoal == 0 && oldSongs == n) return 1; + if (curGoal == 0 || oldSongs > n) return 0; + if (dp[curGoal][oldSongs] != -1) return dp[curGoal][oldSongs]; + + long res = (long) (n - oldSongs) * count(curGoal - 1, oldSongs + 1, n, k) % MOD; + if (oldSongs > k) { + res = (res + (long) (oldSongs - k) * count(curGoal - 1, oldSongs, n, k)) % MOD; + } + dp[curGoal][oldSongs] = (int) res; + return dp[curGoal][oldSongs]; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + vector> dp; + + int count(int curGoal, int oldSongs, int n, int k) { + if (curGoal == 0 && oldSongs == n) return 1; + if (curGoal == 0 || oldSongs > n) return 0; + if (dp[curGoal][oldSongs] != -1) return dp[curGoal][oldSongs]; + + long long res = (long long)(n - oldSongs) * count(curGoal - 1, oldSongs + 1, n, k) % MOD; + if (oldSongs > k) { + res = (res + (long long)(oldSongs - k) * count(curGoal - 1, oldSongs, n, k)) % MOD; + } + dp[curGoal][oldSongs] = res; + return dp[curGoal][oldSongs]; + } + +public: + int numMusicPlaylists(int n, int goal, int k) { + dp.assign(goal + 1, vector(n + 1, -1)); + return count(goal, 0, n, k); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} goal + * @param {number} k + * @return {number} + */ + numMusicPlaylists(n, goal, k) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: goal + 1 }, () => Array(n + 1).fill(-1)); + + const count = (curGoal, oldSongs) => { + if (curGoal === 0 && oldSongs === n) return 1; + if (curGoal === 0 || oldSongs > n) return 0; + if (dp[curGoal][oldSongs] !== -1) return dp[curGoal][oldSongs]; + + let res = ((n - oldSongs) * count(curGoal - 1, oldSongs + 1)) % MOD; + if (oldSongs > k) { + res = (res + ((oldSongs - k) * count(curGoal - 1, oldSongs)) % MOD) % MOD; + } + dp[curGoal][oldSongs] = res; + return res; + }; + + return count(goal, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(g * n)$ +* Space complexity: $O(g * n)$ + +> Where $g$ is the number of songs to listen and $n$ is the number of different songs. + +--- + +## 2. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def numMusicPlaylists(self, n: int, goal: int, k: int) -> int: + mod = 10**9 + 7 + dp = [[0] * (n + 1) for _ in range(goal + 1)] + dp[0][0] = 1 + + for cur_goal in range(1, goal + 1): + for old_songs in range(1, n + 1): + res = (dp[cur_goal - 1][old_songs - 1] * (n - old_songs + 1)) % mod + if old_songs > k: + res = (res + dp[cur_goal - 1][old_songs] * (old_songs - k)) % mod + dp[cur_goal][old_songs] = res + + return dp[goal][n] +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int numMusicPlaylists(int n, int goal, int k) { + int[][] dp = new int[goal + 1][n + 1]; + dp[0][0] = 1; + + for (int curGoal = 1; curGoal <= goal; curGoal++) { + for (int oldSongs = 1; oldSongs <= n; oldSongs++) { + int res = (int) (((long) dp[curGoal - 1][oldSongs - 1] * (n - oldSongs + 1)) % MOD); + if (oldSongs > k) { + res = (res + (int) (((long) dp[curGoal - 1][oldSongs] * (oldSongs - k)) % MOD)) % MOD; + } + dp[curGoal][oldSongs] = res; + } + } + + return dp[goal][n]; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + +public: + int numMusicPlaylists(int n, int goal, int k) { + vector> dp(goal + 1, vector(n + 1, 0)); + dp[0][0] = 1; + + for (int curGoal = 1; curGoal <= goal; curGoal++) { + for (int oldSongs = 1; oldSongs <= n; oldSongs++) { + int res = (long long) dp[curGoal - 1][oldSongs - 1] * (n - oldSongs + 1) % MOD; + if (oldSongs > k) { + res = (res + (long long) dp[curGoal - 1][oldSongs] * (oldSongs - k) % MOD) % MOD; + } + dp[curGoal][oldSongs] = res; + } + } + + return dp[goal][n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} goal + * @param {number} k + * @return {number} + */ + numMusicPlaylists(n, goal, k) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: goal + 1 }, () => Array(n + 1).fill(0)); + dp[0][0] = 1; + + for (let curGoal = 1; curGoal <= goal; curGoal++) { + for (let oldSongs = 1; oldSongs <= n; oldSongs++) { + let res = (dp[curGoal - 1][oldSongs - 1] * (n - oldSongs + 1)) % MOD; + if (oldSongs > k) { + res = (res + dp[curGoal - 1][oldSongs] * (oldSongs - k)) % MOD; + } + dp[curGoal][oldSongs] = res; + } + } + + return dp[goal][n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(g * n)$ +* Space complexity: $O(g * n)$ + +> Where $g$ is the number of songs to listen and $n$ is the number of different songs. + +--- + +## 3. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def numMusicPlaylists(self, n: int, goal: int, k: int) -> int: + mod = 10**9 + 7 + dp = [0] * (n + 1) + + for cur_goal in range(1, goal + 1): + prev = 1 if cur_goal == 1 else 0 + for old_songs in range(1, n + 1): + res = (prev * (n - old_songs + 1)) % mod + if old_songs > k: + res = (res + dp[old_songs] * (old_songs - k)) % mod + prev = dp[old_songs] + dp[old_songs] = res + + return dp[n] +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int numMusicPlaylists(int n, int goal, int k) { + int[] dp = new int[n + 1]; + + for (int curGoal = 1; curGoal <= goal; curGoal++) { + int prev = curGoal == 1 ? 1 : 0; + for (int oldSongs = 1; oldSongs <= n; oldSongs++) { + int res = (int) (((long) prev * (n - oldSongs + 1)) % MOD); + if (oldSongs > k) { + res = (res + (int) (((long) dp[oldSongs] * (oldSongs - k)) % MOD)) % MOD; + } + prev = dp[oldSongs]; + dp[oldSongs] = res; + } + } + + return dp[n]; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + +public: + int numMusicPlaylists(int n, int goal, int k) { + vector dp(n + 1); + + for (int curGoal = 1; curGoal <= goal; curGoal++) { + int prev = curGoal == 1 ? 1: 0; + for (int oldSongs = 1; oldSongs <= n; oldSongs++) { + int res = (long long) prev * (n - oldSongs + 1) % MOD; + if (oldSongs > k) { + res = (res + (long long) dp[oldSongs] * (oldSongs - k) % MOD) % MOD; + } + prev = dp[oldSongs]; + dp[oldSongs] = res; + } + } + + return dp[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} goal + * @param {number} k + * @return {number} + */ + numMusicPlaylists(n, goal, k) { + const MOD = 1e9 + 7; + const dp = new Array(n + 1).fill(0); + + for (let curGoal = 1; curGoal <= goal; curGoal++) { + let prev = curGoal === 1 ? 1 : 0; + for (let oldSongs = 1; oldSongs <= n; oldSongs++) { + let res = (prev * (n - oldSongs + 1)) % MOD; + if (oldSongs > k) { + res = (res + dp[oldSongs] * (oldSongs - k)) % MOD; + } + prev = dp[oldSongs]; + dp[oldSongs] = res; + } + } + + return dp[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(g * n)$ +* Space complexity: $O(n)$ + +> Where $g$ is the number of songs to listen and $n$ is the number of different songs. \ No newline at end of file diff --git a/articles/number-of-one-bits.md b/articles/number-of-one-bits.md new file mode 100644 index 000000000..4ea545b0d --- /dev/null +++ b/articles/number-of-one-bits.md @@ -0,0 +1,441 @@ +## 1. Bit Mask - I + +::tabs-start + +```python +class Solution: + def hammingWeight(self, n: int) -> int: + res = 0 + for i in range(32): + if (1 << i) & n: + res += 1 + return res +``` + +```java +public class Solution { + public int hammingWeight(int n) { + int res = 0; + for (int i = 0; i < 32; i++) { + if ((1 << i & n) != 0) { + res++; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int hammingWeight(uint32_t n) { + int res = 0; + for (int i = 0; i < 32; i++) { + if ((1 << i) & n) { + res++; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n - a positive integer + * @return {number} + */ + hammingWeight(n) { + let res = 0; + for (let i = 0; i < 32; i++) { + if ((1 << i) & n) { + res++; + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int HammingWeight(uint n) { + int res = 0; + for (int i = 0; i < 32; i++) { + if ((1 << i & n) != 0) { + res++; + } + } + return res; + } +} +``` + +```go +func hammingWeight(n int) int { + res := 0 + for i := 0; i < 32; i++ { + if (1< Int { + var res = 0 + for i in 0..<32 { + if (1 << i) & n != 0 { + res += 1 + } + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 2. Bit Mask - II + +::tabs-start + +```python +class Solution: + def hammingWeight(self, n: int) -> int: + res = 0 + while n: + res += 1 if n & 1 else 0 + n >>= 1 + return res +``` + +```java +public class Solution { + public int hammingWeight(int n) { + int res = 0; + while (n != 0) { + res += (n & 1) == 1 ? 1 : 0; + n >>= 1; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int hammingWeight(uint32_t n) { + int res = 0; + while (n != 0) { + res += (n & 1) ? 1 : 0; + n >>= 1; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n - a positive integer + * @return {number} + */ + hammingWeight(n) { + let res = 0; + while (n !== 0) { + res += (n & 1) === 1 ? 1 : 0; + n >>= 1; + } + return res; + } +} +``` + +```csharp +public class Solution { + public int HammingWeight(uint n) { + int res = 0; + while (n != 0) { + res += (n & 1) == 1 ? 1 : 0; + n >>= 1; + } + return res; + } +} +``` + +```go +func hammingWeight(n int) int { + res := 0 + for n != 0 { + if n&1 != 0 { + res++ + } + n >>= 1 + } + return res +} +``` + +```kotlin +class Solution { + fun hammingWeight(n: Int): Int { + var res = 0 + var num = n + while (num != 0) { + if ((num and 1) != 0) { + res++ + } + num = num shr 1 + } + return res + } +} +``` + +```swift +class Solution { + func hammingWeight(_ n: Int) -> Int { + var n = n + var res = 0 + while n != 0 { + res += (n & 1) != 0 ? 1 : 0 + n >>= 1 + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 3. Bit Mask (Optimal) + +::tabs-start + +```python +class Solution: + def hammingWeight(self, n: int) -> int: + res = 0 + while n: + n &= n - 1 + res += 1 + return res +``` + +```java +public class Solution { + public int hammingWeight(int n) { + int res = 0; + while (n != 0) { + n &= n - 1; + res++; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int hammingWeight(uint32_t n) { + int res = 0; + while (n) { + n &= n - 1; + res++; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n - a positive integer + * @return {number} + */ + hammingWeight(n) { + let res = 0; + while (n !== 0) { + n &= n - 1; + res++; + } + return res; + } +} +``` + +```csharp +public class Solution { + public int HammingWeight(uint n) { + int res = 0; + while (n != 0) { + n = n & (n - 1); + res++; + } + return res; + } +} +``` + +```go +func hammingWeight(n int) int { + res := 0 + for n != 0 { + n &= n - 1 + res++ + } + return res +} +``` + +```kotlin +class Solution { + fun hammingWeight(n: Int): Int { + var res = 0 + var num = n + while (num != 0) { + num = num and (num - 1) + res++ + } + return res + } +} +``` + +```swift +class Solution { + func hammingWeight(_ n: Int) -> Int { + var n = n + var res = 0 + while n != 0 { + n &= (n - 1) + res += 1 + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 4. Built-In Function + +::tabs-start + +```python +class Solution: + def hammingWeight(self, n: int) -> int: + return bin(n).count('1') +``` + +```java +public class Solution { + public int hammingWeight(int n) { + return Integer.bitCount(n); + } +} +``` + +```cpp +class Solution { +public: + int hammingWeight(uint32_t n) { + return __builtin_popcount(n); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n - a positive integer + * @return {number} + */ + hammingWeight(n) { + return n.toString(2).split('0').join('').length; + } +} +``` + +```csharp +public class Solution { + public int HammingWeight(uint n) { + return System.Numerics.BitOperations.PopCount(n); + } +} +``` + +```go +func hammingWeight(n int) int { + return bits.OnesCount(uint(n)) +} +``` + +```kotlin +class Solution { + fun hammingWeight(n: Int): Int { + return n.countOneBits() + } +} +``` + +```swift +class Solution { + func hammingWeight(_ n: Int) -> Int { + return n.nonzeroBitCount + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/number-of-pairs-of-interchangeable-rectangles.md b/articles/number-of-pairs-of-interchangeable-rectangles.md new file mode 100644 index 000000000..bbb73e152 --- /dev/null +++ b/articles/number-of-pairs-of-interchangeable-rectangles.md @@ -0,0 +1,364 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def interchangeableRectangles(self, rectangles: List[List[int]]) -> int: + res = 0 + for i in range(1, len(rectangles)): + for j in range(i): + if rectangles[i][0] / rectangles[i][1] == rectangles[j][0] / rectangles[j][1]: + res += 1 + return res +``` + +```java +public class Solution { + public long interchangeableRectangles(int[][] rectangles) { + long res = 0; + for (int i = 1; i < rectangles.length; i++) { + for (int j = 0; j < i; j++) { + if ((double) rectangles[i][0] / rectangles[i][1] == (double) rectangles[j][0] / rectangles[j][1]) { + res++; + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + long long interchangeableRectangles(vector>& rectangles) { + long long res = 0; + for (int i = 1; i < rectangles.size(); i++) { + for (int j = 0; j < i; j++) { + if ((double) rectangles[i][0] / rectangles[i][1] == (double) rectangles[j][0] / rectangles[j][1]) { + res++; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} rectangles + * @return {number} + */ + interchangeableRectangles(rectangles) { + let res = 0; + for (let i = 1; i < rectangles.length; i++) { + for (let j = 0; j < i; j++) { + if (rectangles[i][0] / rectangles[i][1] === rectangles[j][0] / rectangles[j][1]) { + res++; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Hash Map (Two Pass) + +::tabs-start + +```python +class Solution: + def interchangeableRectangles(self, rectangles: List[List[int]]) -> int: + count = {} + for w, h in rectangles: + count[w / h] = 1 + count.get(w / h, 0) + + res = 0 + for c in count.values(): + if c > 1: + res += (c * (c - 1)) // 2 + return res +``` + +```java +public class Solution { + public long interchangeableRectangles(int[][] rectangles) { + HashMap count = new HashMap<>(); + for (int[] rect : rectangles) { + double ratio = (double) rect[0] / rect[1]; + count.put(ratio, count.getOrDefault(ratio, 0) + 1); + } + + long res = 0; + for (int c : count.values()) { + if (c > 1) { + res += (c * 1L * (c - 1)) / 2; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + long long interchangeableRectangles(vector>& rectangles) { + unordered_map count; + for (const auto& rect : rectangles) { + double ratio = (double) rect[0] / rect[1]; + count[ratio]++; + } + + long long res = 0; + for (const auto& [key, c] : count) { + if (c > 1) { + res += (c * 1LL * (c - 1)) / 2; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} rectangles + * @return {number} + */ + interchangeableRectangles(rectangles) { + const count = new Map(); + for (const [w, h] of rectangles) { + const ratio = w / h; + count.set(ratio, (count.get(ratio) || 0) + 1); + } + + let res = 0; + for (const c of count.values()) { + if (c > 1) { + res += (c * (c - 1)) / 2; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Hash Map (One Pass) + +::tabs-start + +```python +class Solution: + def interchangeableRectangles(self, rectangles: List[List[int]]) -> int: + count = {} + res = 0 + for w, h in rectangles: + res += count.get(w / h, 0) + count[w / h] = 1 + count.get(w / h, 0) + return res +``` + +```java +public class Solution { + public long interchangeableRectangles(int[][] rectangles) { + HashMap count = new HashMap<>(); + long res = 0; + for (int[] rect : rectangles) { + double ratio = (double) rect[0] / rect[1]; + res += count.getOrDefault(ratio, 0); + count.put(ratio, count.getOrDefault(ratio, 0) + 1); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + long long interchangeableRectangles(vector>& rectangles) { + unordered_map count; + long long res = 0; + for (const auto& rect : rectangles) { + double ratio = (double) rect[0] / rect[1]; + res += count[ratio]; + count[ratio]++; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} rectangles + * @return {number} + */ + interchangeableRectangles(rectangles) { + const count = new Map(); + let res = 0; + for (const [w, h] of rectangles) { + const ratio = w / h; + res += count.get(ratio) || 0 + count.set(ratio, (count.get(ratio) || 0) + 1); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Greatest Common Divisor + +::tabs-start + +```python +class Solution: + def hash(self, a: int, b: int) -> int: + mask = a + mask |= (b << 31) + return mask + + def interchangeableRectangles(self, rectangles: List[List[int]]) -> int: + res = 0 + count = {} + for rect in rectangles: + gcd = math.gcd(rect[0], rect[1]) + key = self.hash(rect[0] // gcd, rect[1] // gcd) + res += count.get(key, 0) + count[key] = count.get(key, 0) + 1 + return res +``` + +```java +public class Solution { + public long hash(int a, int b) { + long mask = a; + mask |= ((long)b << 31); + return mask; + } + + public long interchangeableRectangles(int[][] rectangles) { + long res = 0; + Map count = new HashMap<>(); + for (int[] rect : rectangles) { + int gcd = gcd(rect[0], rect[1]); + long key = hash(rect[0] / gcd, rect[1] / gcd); + res += count.getOrDefault(key, 0); + count.put(key, count.getOrDefault(key, 0) + 1); + } + return res; + } + + private int gcd(int a, int b) { + while (b != 0) { + a %= b; + int temp = a; + a = b; + b = temp; + } + return a; + } +} +``` + +```cpp +class Solution { +public: + long long hash(int a, int b) { + long long mask = a; + mask |= ((long long)b << 31); + return mask; + } + + long long interchangeableRectangles(vector>& rectangles) { + long long res = 0; + unordered_map count; + for (const auto& rect : rectangles) { + int gcd = __gcd(rect[0], rect[1]); + long long key = hash(rect[0] / gcd, rect[1] / gcd); + res += count[key]; + count[key]++; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} a + * @param {number} b + * @return {string} + */ + hash(a, b) { + return `${a},${b}`; + } + + /** + * @param {number[][]} rectangles + * @return {number} + */ + interchangeableRectangles(rectangles) { + let res = 0; + const count = new Map(); + + const gcd = (a, b) => { + while (b !== 0) { + a %= b; + [a, b] = [b, a] + } + return a; + }; + + for (const rect of rectangles) { + const g = gcd(rect[0], rect[1]); + const key = this.hash(Math.floor(rect[0] / g), Math.floor(rect[1] / g)); + res += count.get(key) || 0; + count.set(key, (count.get(key) || 0) + 1); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/number-of-senior-citizens.md b/articles/number-of-senior-citizens.md new file mode 100644 index 000000000..11a6b0d29 --- /dev/null +++ b/articles/number-of-senior-citizens.md @@ -0,0 +1,149 @@ +## 1. String Parsing + +::tabs-start + +```python +class Solution: + def countSeniors(self, details: List[str]) -> int: + res = 0 + for d in details: + if int(d[11:13]) > 60: + res += 1 + return res +``` + +```java +public class Solution { + public int countSeniors(String[] details) { + int res = 0; + for (String d : details) { + if (Integer.parseInt(d.substring(11, 13)) > 60) { + res++; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int countSeniors(vector& details) { + int res = 0; + for (const string& d : details) { + if (stoi(d.substr(11, 2)) > 60) { + res++; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} details + * @return {number} + */ + countSeniors(details) { + let res = 0; + for (let d of details) { + if (parseInt(d.slice(11, 13)) > 60) { + res++; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Character-Based Extraction + +::tabs-start + +```python +class Solution: + def countSeniors(self, details: List[str]) -> int: + res = 0 + for d in details: + ten = ord(d[11]) - ord("0") + one = ord(d[12]) - ord("0") + age = one + 10 * ten + if age > 60: + res += 1 + return res +``` + +```java +public class Solution { + public int countSeniors(String[] details) { + int res = 0; + for (String d : details) { + int ten = d.charAt(11) - '0'; + int one = d.charAt(12) - '0'; + int age = one + 10 * ten; + if (age > 60) { + res++; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int countSeniors(vector& details) { + int res = 0; + for (const string& d : details) { + int ten = d[11] - '0'; + int one = d[12] - '0'; + int age = one + 10 * ten; + if (age > 60) { + res++; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} details + * @return {number} + */ + countSeniors(details) { + let res = 0; + for (let d of details) { + let ten = d.charCodeAt(11) - 48; + let one = d.charCodeAt(12) - 48; + let age = one + 10 * ten; + if (age > 60) { + res++; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/number-of-students-unable-to-eat-lunch.md b/articles/number-of-students-unable-to-eat-lunch.md new file mode 100644 index 000000000..6ccf1609d --- /dev/null +++ b/articles/number-of-students-unable-to-eat-lunch.md @@ -0,0 +1,348 @@ +## 1. Queue + +::tabs-start + +```python +class Solution: + def countStudents(self, students: List[int], sandwiches: List[int]) -> int: + n = len(students) + q = deque(students) + + res = n + for sandwich in sandwiches: + cnt = 0 + while cnt < n and q[0] != sandwich: + cur = q.popleft() + q.append(cur) + cnt += 1 + + if q[0] == sandwich: + q.popleft() + res -= 1 + else: + break + return res +``` + +```java +public class Solution { + public int countStudents(int[] students, int[] sandwiches) { + int n = students.length; + Queue q = new LinkedList<>(); + for (int student : students) { + q.offer(student); + } + + int res = n; + for (int sandwich : sandwiches) { + int cnt = 0; + while (cnt < n && q.peek() != sandwich) { + q.offer(q.poll()); + cnt++; + } + if (q.peek() == sandwich) { + q.poll(); + res--; + } else { + break; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int countStudents(vector& students, vector& sandwiches) { + int n = students.size(); + queue q; + + for (int student : students) { + q.push(student); + } + + int res = n; + for (int sandwich : sandwiches) { + int cnt = 0; + while (cnt < n && q.front() != sandwich) { + q.push(q.front()); + q.pop(); + cnt++; + } + if (q.front() == sandwich) { + q.pop(); + res--; + } else { + break; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} students + * @param {number[]} sandwiches + * @return {number} + */ + countStudents(students, sandwiches) { + let n = students.length; + let q = new Queue(); + for (let student of students) { + q.push(student); + } + + let res = n; + for (let sandwich of sandwiches) { + let cnt = 0; + while (cnt < n && q.front() !== sandwich) { + q.push(q.pop()); + cnt++; + } + + if (q.front() === sandwich) { + q.pop(); + res--; + } else { + break; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Iteration + +::tabs-start + +```python +class Solution: + def countStudents(self, students: List[int], sandwiches: List[int]) -> int: + n = len(students) + idx = 0 + + res = n + for sandwich in sandwiches: + cnt = 0 + while cnt < n and students[idx] != sandwich: + idx += 1 + idx %= n + cnt += 1 + + if students[idx] == sandwich: + students[idx] = -1 + res -= 1 + else: + break + return res +``` + +```java +public class Solution { + public int countStudents(int[] students, int[] sandwiches) { + int n = students.length; + int idx = 0; + + int res = n; + for (int sandwich : sandwiches) { + int cnt = 0; + while (cnt < n && students[idx] != sandwich) { + idx++; + idx %= n; + cnt++; + } + if (students[idx] == sandwich) { + students[idx] = -1; + res--; + } else { + break; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int countStudents(vector& students, vector& sandwiches) { + int n = students.size(); + int idx = 0; + + int res = n; + for (int sandwich : sandwiches) { + int cnt = 0; + while (cnt < n && students[idx] != sandwich) { + idx++; + idx %= n; + cnt++; + } + if (students[idx] == sandwich) { + students[idx] = -1; + res--; + } else { + break; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} students + * @param {number[]} sandwiches + * @return {number} + */ + countStudents(students, sandwiches) { + let n = students.length; + let idx = 0; + + let res = n; + for (let sandwich of sandwiches) { + let cnt = 0; + while (cnt < n && students[idx] !== sandwich) { + idx++; + idx %= n; + cnt++; + } + + if (students[idx] === sandwich) { + students[idx] = -1; + res--; + } else { + break; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 3. Frequency Count + +::tabs-start + +```python +class Solution: + def countStudents(self, students: List[int], sandwiches: List[int]) -> int: + res = len(students) + cnt = Counter(students) + + for s in sandwiches: + if cnt[s] > 0: + res -= 1 + cnt[s] -= 1 + else: + break + + return res +``` + +```java +public class Solution { + public int countStudents(int[] students, int[] sandwiches) { + int n = students.length; + int res = n; + int[] cnt = new int[2]; + for (int i = 0; i < n; i++) { + cnt[students[i]]++; + } + + for (int i = 0; i < n; i++) { + if (cnt[sandwiches[i]] > 0) { + res--; + cnt[sandwiches[i]]--; + } else { + break; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countStudents(vector& students, vector& sandwiches) { + int res = students.size(); + vector cnt(2); + for (int& student : students) { + cnt[student]++; + } + + for (int& s : sandwiches) { + if (cnt[s] > 0) { + cnt[s]--; + res--; + } else { + break; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} students + * @param {number[]} sandwiches + * @return {number} + */ + countStudents(students, sandwiches) { + let res = students.length; + const cnt = new Int32Array(2); + for (let student of students) { + cnt[student]++; + } + + for (let s of sandwiches) { + if (cnt[s] > 0) { + cnt[s]--; + res--; + } else { + break; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.md b/articles/number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.md new file mode 100644 index 000000000..f99cc6f13 --- /dev/null +++ b/articles/number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.md @@ -0,0 +1,398 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def numOfSubarrays(self, arr: List[int], k: int, threshold: int) -> int: + res = 0 + l = 0 + + for r in range(k - 1, len(arr)): + sum_ = 0 + for i in range(l, r + 1): + sum_ += arr[i] + + if sum_ / k >= threshold: + res += 1 + l += 1 + + return res +``` + +```java +public class Solution { + public int numOfSubarrays(int[] arr, int k, int threshold) { + int res = 0, l = 0; + + for (int r = k - 1; r < arr.length; r++) { + int sum = 0; + for (int i = l; i <= r; i++) { + sum += arr[i]; + } + if (sum / k >= threshold) { + res++; + } + l++; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numOfSubarrays(vector& arr, int k, int threshold) { + int res = 0, l = 0; + + for (int r = k - 1; r < arr.size(); r++) { + int sum = 0; + for (int i = l; i <= r; i++) { + sum += arr[i]; + } + if (sum / k >= threshold) { + res++; + } + l++; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @param {number} threshold + * @return {number} + */ + numOfSubarrays(arr, k, threshold) { + let res = 0, l = 0; + + for (let r = k - 1; r < arr.length; r++) { + let sum = 0; + for (let i = l; i <= r; i++) { + sum += arr[i]; + } + if (sum / k >= threshold) { + res++; + } + l++; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(1)$ + +> Where $n$ is the size of the array $arr$ and $k$ is the size of the sub-array. + +--- + +## 2. Prefix Sum + +::tabs-start + +```python +class Solution: + def numOfSubarrays(self, arr: List[int], k: int, threshold: int) -> int: + prefix_sum = [0] * (len(arr) + 1) + for i in range(len(arr)): + prefix_sum[i + 1] += prefix_sum[i] + arr[i] + + res = l = 0 + for r in range(k - 1, len(arr)): + sum_ = prefix_sum[r + 1] - prefix_sum[l] + if sum_ / k >= threshold: + res += 1 + l += 1 + + return res +``` + +```java +public class Solution { + public int numOfSubarrays(int[] arr, int k, int threshold) { + int[] prefixSum = new int[arr.length + 1]; + for (int i = 0; i < arr.length; i++) { + prefixSum[i + 1] += prefixSum[i] + arr[i]; + } + + int res = 0, l = 0; + for (int r = k - 1; r < arr.length; r++) { + int sum = prefixSum[r + 1] - prefixSum[l]; + if (sum / k >= threshold) { + res++; + } + l++; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numOfSubarrays(vector& arr, int k, int threshold) { + vector prefixSum(arr.size() + 1); + for (int i = 0; i < arr.size(); i++) { + prefixSum[i + 1] += prefixSum[i] + arr[i]; + } + + int res = 0, l = 0; + for (int r = k - 1; r < arr.size(); r++) { + int sum = prefixSum[r + 1] - prefixSum[l]; + if (sum / k >= threshold) { + res++; + } + l++; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @param {number} threshold + * @return {number} + */ + numOfSubarrays(arr, k, threshold) { + const prefixSum = new Int32Array(arr.length + 1); + for (let i = 0; i < arr.length; i++) { + prefixSum[i + 1] += prefixSum[i] + arr[i]; + } + + let res = 0, l = 0; + for (let r = k - 1; r < arr.length; r++) { + const sum = prefixSum[r + 1] - prefixSum[l]; + if (sum / k >= threshold) { + res++; + } + l++; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $arr$ and $k$ is the size of the sub-array. + +--- + +## 3. Sliding Window - I + +::tabs-start + +```python +class Solution: + def numOfSubarrays(self, arr: List[int], k: int, threshold: int) -> int: + res = 0 + curSum = sum(arr[:k - 1]) + + for L in range(len(arr) - k + 1): + curSum += arr[L + k - 1] + if (curSum / k) >= threshold: + res += 1 + curSum -= arr[L] + return res +``` + +```java +public class Solution { + public int numOfSubarrays(int[] arr, int k, int threshold) { + int res = 0; + int curSum = 0; + + for (int i = 0; i < k - 1; i++) { + curSum += arr[i]; + } + + for (int L = 0; L <= arr.length - k; L++) { + curSum += arr[L + k - 1]; + if ((curSum / k) >= threshold) { + res++; + } + curSum -= arr[L]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numOfSubarrays(vector& arr, int k, int threshold) { + int res = 0, curSum = 0; + + for (int i = 0; i < k - 1; i++) { + curSum += arr[i]; + } + + for (int L = 0; L <= arr.size() - k; L++) { + curSum += arr[L + k - 1]; + if ((curSum / k) >= threshold) { + res++; + } + curSum -= arr[L]; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @param {number} threshold + * @return {number} + */ + numOfSubarrays(arr, k, threshold) { + let res = 0; + let curSum = 0; + + for (let i = 0; i < k - 1; i++) { + curSum += arr[i]; + } + + for (let L = 0; L <= arr.length - k; L++) { + curSum += arr[L + k - 1]; + if ((curSum / k) >= threshold) { + res++; + } + curSum -= arr[L]; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +> Where $n$ is the size of the array $arr$ and $k$ is the size of the sub-array. + +--- + +## 4. Sliding Window - II + +::tabs-start + +```python +class Solution: + def numOfSubarrays(self, arr: List[int], k: int, threshold: int) -> int: + threshold *= k + res = curSum = 0 + for R in range(len(arr)): + curSum += arr[R] + if R >= k - 1: + res += curSum >= threshold + curSum -= arr[R - k + 1] + return res +``` + +```java +public class Solution { + public int numOfSubarrays(int[] arr, int k, int threshold) { + threshold *= k; + int res = 0, curSum = 0; + + for (int R = 0; R < arr.length; R++) { + curSum += arr[R]; + if (R >= k - 1) { + if (curSum >= threshold) { + res++; + } + curSum -= arr[R - k + 1]; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numOfSubarrays(vector& arr, int k, int threshold) { + threshold *= k; + int res = 0, curSum = 0; + + for (int R = 0; R < arr.size(); R++) { + curSum += arr[R]; + if (R >= k - 1) { + if (curSum >= threshold) { + res++; + } + curSum -= arr[R - k + 1]; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @param {number} threshold + * @return {number} + */ + numOfSubarrays(arr, k, threshold) { + threshold *= k; + let res = 0, curSum = 0; + + for (let R = 0; R < arr.length; R++) { + curSum += arr[R]; + if (R >= k - 1) { + if (curSum >= threshold) { + res++; + } + curSum -= arr[R - k + 1]; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +> Where $n$ is the size of the array $arr$ and $k$ is the size of the sub-array. \ No newline at end of file diff --git a/articles/number-of-sub-arrays-with-odd-sum.md b/articles/number-of-sub-arrays-with-odd-sum.md new file mode 100644 index 000000000..e13063014 --- /dev/null +++ b/articles/number-of-sub-arrays-with-odd-sum.md @@ -0,0 +1,521 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def numOfSubarrays(self, arr: List[int]) -> int: + n, res = len(arr), 0 + mod = int(1e9 + 7) + + for i in range(n): + curSum = 0 + for j in range(i, n): + curSum += arr[j] + if curSum % 2: + res = (res + 1) % mod + + return res +``` + +```java +public class Solution { + public int numOfSubarrays(int[] arr) { + int n = arr.length, res = 0; + int mod = (int)1e9 + 7; + + for (int i = 0; i < n; i++) { + int curSum = 0; + for (int j = i; j < n; j++) { + curSum += arr[j]; + if (curSum % 2 != 0) { + res = (res + 1) % mod; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numOfSubarrays(vector& arr) { + int n = arr.size(), res = 0; + int mod = 1e9 + 7; + + for (int i = 0; i < n; i++) { + int curSum = 0; + for (int j = i; j < n; j++) { + curSum += arr[j]; + if (curSum % 2 != 0) { + res = (res + 1) % mod; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + numOfSubarrays(arr) { + const n = arr.length; + let res = 0; + const mod = 1e9 + 7; + + for (let i = 0; i < n; i++) { + let curSum = 0; + for (let j = i; j < n; j++) { + curSum += arr[j]; + if (curSum % 2 !== 0) { + res = (res + 1) % mod; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def numOfSubarrays(self, arr: List[int]) -> int: + mod = 10**9 + 7 + n = len(arr) + memo = {} + + def dp(i: int, parity: int) -> int: + if i == n: + return 0 + + if (i, parity) in memo: + return memo[(i, parity)] + + new_parity = (parity + arr[i]) % 2 + res = new_parity + dp(i + 1, new_parity) + memo[(i, parity)] = res % mod + return memo[(i, parity)] + + ans = 0 + for i in range(n): + ans = (ans + dp(i, 0)) % mod + + return ans +``` + +```java +public class Solution { + int[][] memo; + int[] arr; + int mod = (int)1e9 + 7; + + public int numOfSubarrays(int[] arr) { + int n = arr.length; + this.arr = arr; + memo = new int[n][2]; + for (int i = 0; i < n; i++) { + memo[i][0] = -1; + memo[i][1] = -1; + } + + int res = 0; + for (int i = 0; i < n; i++) { + res = (res + dp(i, 0)) % mod; + } + return res; + } + + private int dp(int i, int parity) { + if (i == arr.length) return 0; + if (memo[i][parity] != -1) return memo[i][parity]; + + int newParity = (parity + arr[i]) % 2; + int res = newParity + dp(i + 1, newParity); + return memo[i][parity] = res % mod; + } +} +``` + +```cpp +class Solution { +public: + int mod = 1e9 + 7; + vector> memo; + vector arr; + + int numOfSubarrays(vector& arr) { + this->arr = arr; + int n = arr.size(); + memo.assign(n, vector(2, -1)); + + int res = 0; + for (int i = 0; i < n; i++) { + res = (res + dp(i, 0)) % mod; + } + return res; + } + + int dp(int i, int parity) { + if (i == arr.size()) return 0; + if (memo[i][parity] != -1) return memo[i][parity]; + + int newParity = (parity + arr[i]) % 2; + int res = newParity + dp(i + 1, newParity); + return memo[i][parity] = res % mod; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + numOfSubarrays(arr) { + const mod = 1e9 + 7; + const n = arr.length; + const memo = Array.from({ length: n }, () => Array(2).fill(-1)); + + const dp = (i, parity) => { + if (i === n) return 0; + if (memo[i][parity] !== -1) return memo[i][parity]; + + const newParity = (parity + arr[i]) % 2; + const res = newParity + dp(i + 1, newParity); + return memo[i][parity] = res % mod; + }; + + let res = 0; + for (let i = 0; i < n; i++) { + res = (res + dp(i, 0)) % mod; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def numOfSubarrays(self, arr: List[int]) -> int: + n = len(arr) + mod = 10**9 + 7 + dp = [[0] * 2 for _ in range(n + 1)] + + for i in range(n - 1, -1, -1): + for parity in range(2): + new_parity = (parity + arr[i]) % 2 + dp[i][parity] = (new_parity + dp[i + 1][new_parity]) % mod + + res = 0 + for i in range(n): + res = (res + dp[i][0]) % mod + return res +``` + +```java +public class Solution { + public int numOfSubarrays(int[] arr) { + int n = arr.length; + int mod = (int)1e9 + 7; + int[][] dp = new int[n + 1][2]; + + for (int i = n - 1; i >= 0; i--) { + for (int parity = 0; parity <= 1; parity++) { + int newParity = (parity + arr[i]) % 2; + dp[i][parity] = (newParity + dp[i + 1][newParity]) % mod; + } + } + + int res = 0; + for (int i = 0; i < n; i++) { + res = (res + dp[i][0]) % mod; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numOfSubarrays(vector& arr) { + int n = arr.size(), mod = 1e9 + 7; + vector> dp(n + 1, vector(2, 0)); + + for (int i = n - 1; i >= 0; i--) { + for (int parity = 0; parity <= 1; parity++) { + int newParity = (parity + arr[i]) % 2; + dp[i][parity] = (newParity + dp[i + 1][newParity]) % mod; + } + } + + int res = 0; + for (int i = 0; i < n; i++) { + res = (res + dp[i][0]) % mod; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + numOfSubarrays(arr) { + const n = arr.length; + const mod = 1e9 + 7; + const dp = Array.from({ length: n + 1 }, () => [0, 0]); + + for (let i = n - 1; i >= 0; i--) { + for (let parity = 0; parity <= 1; parity++) { + const newParity = (parity + arr[i]) % 2; + dp[i][parity] = (newParity + dp[i + 1][newParity]) % mod; + } + } + + let res = 0; + for (let i = 0; i < n; i++) { + res = (res + dp[i][0]) % mod; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Prefix Sum - I + +::tabs-start + +```python +class Solution: + def numOfSubarrays(self, arr: List[int]) -> int: + cur_sum = odd_cnt = even_cnt = res = 0 + MOD = 10**9 + 7 + + for n in arr: + cur_sum += n + if cur_sum % 2: + res = (res + 1 + even_cnt) % MOD + odd_cnt += 1 + else: + res = (res + odd_cnt) % MOD + even_cnt += 1 + + return res +``` + +```java +public class Solution { + public int numOfSubarrays(int[] arr) { + int curSum = 0, oddCnt = 0, evenCnt = 0, res = 0; + int MOD = (int)1e9 + 7; + + for (int n : arr) { + curSum += n; + if (curSum % 2 != 0) { + res = (res + 1 + evenCnt) % MOD; + oddCnt++; + } else { + res = (res + oddCnt) % MOD; + evenCnt++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numOfSubarrays(vector& arr) { + long long curSum = 0, oddCnt = 0, evenCnt = 0, res = 0; + const int MOD = 1e9 + 7; + + for (int n : arr) { + curSum += n; + if (curSum % 2 != 0) { + res = (res + 1 + evenCnt) % MOD; + oddCnt++; + } else { + res = (res + oddCnt) % MOD; + evenCnt++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + numOfSubarrays(arr) { + let curSum = 0, oddCnt = 0, evenCnt = 0, res = 0; + const MOD = 1e9 + 7; + + for (let n of arr) { + curSum += n; + if (curSum % 2 !== 0) { + res = (res + 1 + evenCnt) % MOD; + oddCnt++; + } else { + res = (res + oddCnt) % MOD; + evenCnt++; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Prefix Sum - II + +::tabs-start + +```python +class Solution: + def numOfSubarrays(self, arr: List[int]) -> int: + count = [1, 0] + prefix = res = 0 + MOD = 10**9 + 7 + + for num in arr: + prefix = (prefix + num) % 2 + res = (res + count[1 - prefix]) % MOD + count[prefix] += 1 + + return res +``` + +```java +public class Solution { + public int numOfSubarrays(int[] arr) { + int[] count = {1, 0}; + int prefix = 0, res = 0; + int MOD = (int)1e9 + 7; + + for (int num : arr) { + prefix = (prefix + num) % 2; + res = (res + count[1 - prefix]) % MOD; + count[prefix]++; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numOfSubarrays(vector& arr) { + int count[2] = {1, 0}; + int prefix = 0, res = 0; + const int MOD = 1e9 + 7; + + for (int num : arr) { + prefix = (prefix + num) % 2; + res = (res + count[1 - prefix]) % MOD; + count[prefix]++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + numOfSubarrays(arr) { + const count = [1, 0]; + let prefix = 0, res = 0; + const MOD = 1e9 + 7; + + for (const num of arr) { + prefix = (prefix + num) % 2; + res = (res + count[1 - prefix]) % MOD; + count[prefix]++; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/number-of-submatrices-that-sum-to-target.md b/articles/number-of-submatrices-that-sum-to-target.md new file mode 100644 index 000000000..0fe84feca --- /dev/null +++ b/articles/number-of-submatrices-that-sum-to-target.md @@ -0,0 +1,549 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def numSubmatrixSumTarget(self, matrix: List[List[int]], target: int) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + res = 0 + + for r1 in range(ROWS): + for r2 in range(r1, ROWS): + for c1 in range(COLS): + for c2 in range(c1, COLS): + subSum = 0 + for r in range(r1, r2 + 1): + for c in range(c1, c2 + 1): + subSum += matrix[r][c] + + if subSum == target: + res += 1 + + return res +``` + +```java +public class Solution { + public int numSubmatrixSumTarget(int[][] matrix, int target) { + int ROWS = matrix.length, COLS = matrix[0].length; + int res = 0; + + for (int r1 = 0; r1 < ROWS; r1++) { + for (int r2 = r1; r2 < ROWS; r2++) { + for (int c1 = 0; c1 < COLS; c1++) { + for (int c2 = c1; c2 < COLS; c2++) { + int subSum = 0; + for (int r = r1; r <= r2; r++) { + for (int c = c1; c <= c2; c++) { + subSum += matrix[r][c]; + } + } + if (subSum == target) { + res++; + } + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubmatrixSumTarget(vector>& matrix, int target) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + int res = 0; + + for (int r1 = 0; r1 < ROWS; r1++) { + for (int r2 = r1; r2 < ROWS; r2++) { + for (int c1 = 0; c1 < COLS; c1++) { + for (int c2 = c1; c2 < COLS; c2++) { + int subSum = 0; + for (int r = r1; r <= r2; r++) { + for (int c = c1; c <= c2; c++) { + subSum += matrix[r][c]; + } + } + if (subSum == target) { + res++; + } + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @param {number} target + * @return {number} + */ + numSubmatrixSumTarget(matrix, target) { + const ROWS = matrix.length, COLS = matrix[0].length; + let res = 0; + + for (let r1 = 0; r1 < ROWS; r1++) { + for (let r2 = r1; r2 < ROWS; r2++) { + for (let c1 = 0; c1 < COLS; c1++) { + for (let c2 = c1; c2 < COLS; c2++) { + let subSum = 0; + for (let r = r1; r <= r2; r++) { + for (let c = c1; c <= c2; c++) { + subSum += matrix[r][c]; + } + } + if (subSum === target) { + res++; + } + } + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m ^ 3 * n ^ 3)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ is the number of rows and $n$ is the number of columns of the given matrix. + +--- + +## 2. Two Dimensional Prefix Sum + +::tabs-start + +```python +class Solution: + def numSubmatrixSumTarget(self, matrix: List[List[int]], target: int) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + sub_sum = [[0] * COLS for _ in range(ROWS)] + + for r in range(ROWS): + for c in range(COLS): + top = sub_sum[r - 1][c] if r > 0 else 0 + left = sub_sum[r][c - 1] if c > 0 else 0 + top_left = sub_sum[r - 1][c - 1] if min(r, c) > 0 else 0 + sub_sum[r][c] = matrix[r][c] + top + left - top_left + + res = 0 + for r1 in range(ROWS): + for r2 in range(r1, ROWS): + for c1 in range(COLS): + for c2 in range(c1, COLS): + top = sub_sum[r1 - 1][c2] if r1 > 0 else 0 + left = sub_sum[r2][c1 - 1] if c1 > 0 else 0 + top_left = sub_sum[r1 - 1][c1 - 1] if min(r1, c1) > 0 else 0 + cur_sum = sub_sum[r2][c2] - top - left + top_left + if cur_sum == target: + res += 1 + return res +``` + +```java +public class Solution { + public int numSubmatrixSumTarget(int[][] matrix, int target) { + int ROWS = matrix.length, COLS = matrix[0].length; + int[][] subSum = new int[ROWS][COLS]; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + int top = (r > 0) ? subSum[r - 1][c] : 0; + int left = (c > 0) ? subSum[r][c - 1] : 0; + int topLeft = (Math.min(r, c) > 0) ? subSum[r - 1][c - 1] : 0; + subSum[r][c] = matrix[r][c] + top + left - topLeft; + } + } + + int res = 0; + for (int r1 = 0; r1 < ROWS; r1++) { + for (int r2 = r1; r2 < ROWS; r2++) { + for (int c1 = 0; c1 < COLS; c1++) { + for (int c2 = c1; c2 < COLS; c2++) { + int top = (r1 > 0) ? subSum[r1 - 1][c2] : 0; + int left = (c1 > 0) ? subSum[r2][c1 - 1] : 0; + int topLeft = (Math.min(r1, c1) > 0) ? subSum[r1 - 1][c1 - 1] : 0; + int curSum = subSum[r2][c2] - top - left + topLeft; + if (curSum == target) { + res++; + } + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubmatrixSumTarget(vector>& matrix, int target) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + vector> subSum(ROWS, vector(COLS, 0)); + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + int top = (r > 0) ? subSum[r - 1][c] : 0; + int left = (c > 0) ? subSum[r][c - 1] : 0; + int topLeft = (min(r, c) > 0) ? subSum[r - 1][c - 1] : 0; + subSum[r][c] = matrix[r][c] + top + left - topLeft; + } + } + + int res = 0; + for (int r1 = 0; r1 < ROWS; r1++) { + for (int r2 = r1; r2 < ROWS; r2++) { + for (int c1 = 0; c1 < COLS; c1++) { + for (int c2 = c1; c2 < COLS; c2++) { + int top = (r1 > 0) ? subSum[r1 - 1][c2] : 0; + int left = (c1 > 0) ? subSum[r2][c1 - 1] : 0; + int topLeft = (min(r1, c1) > 0) ? subSum[r1 - 1][c1 - 1] : 0; + int curSum = subSum[r2][c2] - top - left + topLeft; + if (curSum == target) { + res++; + } + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @param {number} target + * @return {number} + */ + numSubmatrixSumTarget(matrix, target) { + const ROWS = matrix.length, COLS = matrix[0].length; + const subSum = Array.from({ length: ROWS }, () => Array(COLS).fill(0)); + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + let top = r > 0 ? subSum[r - 1][c] : 0; + let left = c > 0 ? subSum[r][c - 1] : 0; + let topLeft = Math.min(r, c) > 0 ? subSum[r - 1][c - 1] : 0; + subSum[r][c] = matrix[r][c] + top + left - topLeft; + } + } + + let res = 0; + for (let r1 = 0; r1 < ROWS; r1++) { + for (let r2 = r1; r2 < ROWS; r2++) { + for (let c1 = 0; c1 < COLS; c1++) { + for (let c2 = c1; c2 < COLS; c2++) { + let top = r1 > 0 ? subSum[r1 - 1][c2] : 0; + let left = c1 > 0 ? subSum[r2][c1 - 1] : 0; + let topLeft = Math.min(r1, c1) > 0 ? subSum[r1 - 1][c1 - 1] : 0; + let curSum = subSum[r2][c2] - top - left + topLeft; + if (curSum === target) { + res++; + } + } + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m ^ 2 * n ^ 2)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns of the given matrix. + +--- + +## 3. Horizontal 1D Prefix Sum + +::tabs-start + +```python +class Solution: + def numSubmatrixSumTarget(self, matrix: List[List[int]], target: int) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + sub_sum = [[0] * COLS for _ in range(ROWS)] + + for r in range(ROWS): + for c in range(COLS): + top = sub_sum[r - 1][c] if r > 0 else 0 + left = sub_sum[r][c - 1] if c > 0 else 0 + top_left = sub_sum[r - 1][c - 1] if min(r, c) > 0 else 0 + sub_sum[r][c] = matrix[r][c] + top + left - top_left + + res = 0 + for r1 in range(ROWS): + for r2 in range(r1, ROWS): + count = defaultdict(int) + count[0] = 1 + for c in range(COLS): + cur_sum = sub_sum[r2][c] - (sub_sum[r1 - 1][c] if r1 > 0 else 0) + res += count[cur_sum - target] + count[cur_sum] += 1 + + return res +``` + +```java +public class Solution { + public int numSubmatrixSumTarget(int[][] matrix, int target) { + int ROWS = matrix.length, COLS = matrix[0].length; + int[][] subSum = new int[ROWS][COLS]; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + int top = (r > 0) ? subSum[r - 1][c] : 0; + int left = (c > 0) ? subSum[r][c - 1] : 0; + int topLeft = (Math.min(r, c) > 0) ? subSum[r - 1][c - 1] : 0; + subSum[r][c] = matrix[r][c] + top + left - topLeft; + } + } + + int res = 0; + for (int r1 = 0; r1 < ROWS; r1++) { + for (int r2 = r1; r2 < ROWS; r2++) { + Map count = new HashMap<>(); + count.put(0, 1); + for (int c = 0; c < COLS; c++) { + int curSum = subSum[r2][c] - (r1 > 0 ? subSum[r1 - 1][c] : 0); + res += count.getOrDefault(curSum - target, 0); + count.put(curSum, count.getOrDefault(curSum, 0) + 1); + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubmatrixSumTarget(vector>& matrix, int target) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + vector> subSum(ROWS, vector(COLS, 0)); + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + int top = (r > 0) ? subSum[r - 1][c] : 0; + int left = (c > 0) ? subSum[r][c - 1] : 0; + int topLeft = (min(r, c) > 0) ? subSum[r - 1][c - 1] : 0; + subSum[r][c] = matrix[r][c] + top + left - topLeft; + } + } + + int res = 0; + for (int r1 = 0; r1 < ROWS; r1++) { + for (int r2 = r1; r2 < ROWS; r2++) { + unordered_map count; + count[0] = 1; + for (int c = 0; c < COLS; c++) { + int curSum = subSum[r2][c] - (r1 > 0 ? subSum[r1 - 1][c] : 0); + res += count[curSum - target]; + count[curSum]++; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @param {number} target + * @return {number} + */ + numSubmatrixSumTarget(matrix, target) { + let ROWS = matrix.length, COLS = matrix[0].length; + let subSum = Array.from({ length: ROWS }, () => Array(COLS).fill(0)); + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + let top = r > 0 ? subSum[r - 1][c] : 0; + let left = c > 0 ? subSum[r][c - 1] : 0; + let topLeft = Math.min(r, c) > 0 ? subSum[r - 1][c - 1] : 0; + subSum[r][c] = matrix[r][c] + top + left - topLeft; + } + } + + let res = 0; + for (let r1 = 0; r1 < ROWS; r1++) { + for (let r2 = r1; r2 < ROWS; r2++) { + let count = new Map(); + count.set(0, 1); + for (let c = 0; c < COLS; c++) { + let curSum = subSum[r2][c] - (r1 > 0 ? subSum[r1 - 1][c] : 0); + res += count.get(curSum - target) || 0; + count.set(curSum, (count.get(curSum) || 0) + 1); + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m ^ 2 * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns of the given matrix. + +--- + +## 4. Vertical 1D Prefix Sum + +::tabs-start + +```python +class Solution: + def numSubmatrixSumTarget(self, matrix: List[List[int]], target: int) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + res = 0 + + for c1 in range(COLS): + row_prefix = [0] * ROWS + for c2 in range(c1, COLS): + for r in range(ROWS): + row_prefix[r] += matrix[r][c2] + + count = defaultdict(int) + count[0] = 1 + cur_sum = 0 + for r in range(ROWS): + cur_sum += row_prefix[r] + res += count[cur_sum - target] + count[cur_sum] += 1 + + return res +``` + +```java +public class Solution { + public int numSubmatrixSumTarget(int[][] matrix, int target) { + int ROWS = matrix.length, COLS = matrix[0].length, res = 0; + + for (int c1 = 0; c1 < COLS; c1++) { + int[] rowPrefix = new int[ROWS]; + for (int c2 = c1; c2 < COLS; c2++) { + for (int r = 0; r < ROWS; r++) { + rowPrefix[r] += matrix[r][c2]; + } + + Map count = new HashMap<>(); + count.put(0, 1); + int curSum = 0; + + for (int r = 0; r < ROWS; r++) { + curSum += rowPrefix[r]; + res += count.getOrDefault(curSum - target, 0); + count.put(curSum, count.getOrDefault(curSum, 0) + 1); + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubmatrixSumTarget(vector>& matrix, int target) { + int ROWS = matrix.size(), COLS = matrix[0].size(), res = 0; + + for (int c1 = 0; c1 < COLS; c1++) { + vector rowPrefix(ROWS, 0); + for (int c2 = c1; c2 < COLS; c2++) { + for (int r = 0; r < ROWS; r++) { + rowPrefix[r] += matrix[r][c2]; + } + + unordered_map count; + count[0] = 1; + int curSum = 0; + for (int r = 0; r < ROWS; r++) { + curSum += rowPrefix[r]; + res += count[curSum - target]; + count[curSum]++; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @param {number} target + * @return {number} + */ + numSubmatrixSumTarget(matrix, target) { + let ROWS = matrix.length, COLS = matrix[0].length, res = 0; + + for (let c1 = 0; c1 < COLS; c1++) { + let rowPrefix = new Array(ROWS).fill(0); + for (let c2 = c1; c2 < COLS; c2++) { + for (let r = 0; r < ROWS; r++) { + rowPrefix[r] += matrix[r][c2]; + } + + let count = new Map(); + count.set(0, 1); + let curSum = 0; + + for (let r = 0; r < ROWS; r++) { + curSum += rowPrefix[r]; + res += count.get(curSum - target) || 0; + count.set(curSum, (count.get(curSum) || 0) + 1); + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n ^ 2)$ +* Space complexity: $O(m)$ + +> Where $m$ is the number of rows and $n$ is the number of columns of the given matrix. \ No newline at end of file diff --git a/articles/number-of-subsequences-that-satisfy-the-given-sum-condition.md b/articles/number-of-subsequences-that-satisfy-the-given-sum-condition.md new file mode 100644 index 000000000..cc81b5ad0 --- /dev/null +++ b/articles/number-of-subsequences-that-satisfy-the-given-sum-condition.md @@ -0,0 +1,514 @@ +## 1. Brute Force (Recursion) + +::tabs-start + +```python +class Solution: + def numSubseq(self, nums: List[int], target: int) -> int: + MOD = 1000000007 + + def dfs(maxi, mini, i): + if i == len(nums): + if mini != float("inf") and (maxi + mini) <= target: + return 1 + return 0 + + skip = dfs(maxi, mini, i + 1) + include = dfs(max(maxi, nums[i]), min(mini, nums[i]), i + 1) + return (skip + include) % MOD + + return dfs(float("-inf"), float("inf"), 0) +``` + +```java +public class Solution { + private static final int MOD = 1000000007; + + public int numSubseq(int[] nums, int target) { + return dfs(nums, Integer.MIN_VALUE, Integer.MAX_VALUE, 0, target); + } + + private int dfs(int[] nums, int maxi, int mini, int i, int target) { + if (i == nums.length) { + if (mini != Integer.MAX_VALUE && (maxi + mini) <= target) { + return 1; + } + return 0; + } + + int skip = dfs(nums, maxi, mini, i + 1, target); + int include = dfs(nums, Math.max(maxi, nums[i]), Math.min(mini, nums[i]), i + 1, target); + return (skip + include) % MOD; + } +} +``` + +```cpp +class Solution { +public: + const int MOD = 1e9 + 7; + + int numSubseq(vector& nums, int target) { + return dfs(nums, INT_MIN, INT_MAX, 0, target); + } + +private: + int dfs(vector& nums, int maxi, int mini, int i, int target) { + if (i == nums.size()) { + if (mini != INT_MAX && (maxi + mini) <= target) { + return 1; + } + return 0; + } + + int skip = dfs(nums, maxi, mini, i + 1, target); + int include = dfs(nums, max(maxi, nums[i]), min(mini, nums[i]), i + 1, target); + return (skip + include) % MOD; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + numSubseq(nums, target) { + const MOD = 1000000007; + + const dfs = (maxi, mini, i) => { + if (i === nums.length) { + if (mini !== Infinity && (maxi + mini) <= target) { + return 1; + } + return 0; + } + + const skip = dfs(maxi, mini, i + 1); + const include = dfs(Math.max(maxi, nums[i]), Math.min(mini, nums[i]), i + 1); + return (skip + include) % MOD; + }; + + return dfs(-Infinity, Infinity, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Binary Search + +::tabs-start + +```python +class Solution: + def numSubseq(self, nums: List[int], target: int) -> int: + nums.sort() + MOD = 1000000007 + res = 0 + + for i in range(len(nums)): + if nums[i] * 2 > target: + break + + l, r = i, len(nums) - 1 + while l <= r: + mid = (l + r) // 2 + if nums[i] + nums[mid] <= target: + l = mid + 1 + else: + r = mid - 1 + + count = pow(2, r - i, MOD) + res = (res + count) % MOD + + return res +``` + +```java +public class Solution { + public int numSubseq(int[] nums, int target) { + Arrays.sort(nums); + int MOD = 1000000007; + int res = 0; + + for (int i = 0; i < nums.length; i++) { + if (nums[i] * 2 > target) break; + + int l = i, r = nums.length - 1; + while (l <= r) { + int mid = l + (r - l) / 2; + if (nums[i] + nums[mid] <= target) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + long count = pow(2, r - i, MOD); + res = (int) ((res + count) % MOD); + } + return res; + } + + private long pow(int base, int exp, int mod) { + long result = 1; + long b = base; + while (exp > 0) { + if ((exp & 1) == 1) result = (result * b) % mod; + b = (b * b) % mod; + exp >>= 1; + } + return result; + } +} +``` + +```cpp +class Solution { +public: + int numSubseq(vector& nums, int target) { + sort(nums.begin(), nums.end()); + int MOD = 1000000007; + int res = 0; + + for (int i = 0; i < nums.size(); i++) { + if (nums[i] * 2 > target) break; + + int l = i, r = nums.size() - 1; + while (l <= r) { + int mid = l + (r - l) / 2; + if (nums[i] + nums[mid] <= target) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + long long count = powMod(2, r - i, MOD); + res = (res + count) % MOD; + } + return res; + } + +private: + long long powMod(int base, int exp, int mod) { + long long result = 1, b = base; + while (exp > 0) { + if (exp & 1) result = (result * b) % mod; + b = (b * b) % mod; + exp >>= 1; + } + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + numSubseq(nums, target) { + nums.sort((a, b) => a - b); + const MOD = BigInt(1000000007); + let res = 0n; + + const powerMod = (base, exp, mod) => { + let result = 1n, b = BigInt(base); + while (exp > 0) { + if (exp & 1) result = (result * b) % mod; + b = (b * b) % mod; + exp >>= 1; + } + return result; + }; + + for (let i = 0; i < nums.length; i++) { + if (nums[i] * 2 > target) break; + + let l = i, r = nums.length - 1; + while (l <= r) { + const mid = Math.floor((l + r) / 2); + if (nums[i] + nums[mid] <= target) { + l = mid + 1; + } else { + r = mid - 1; + } + } + + const count = powerMod(2, r - i, MOD); + res = (res + count) % MOD; + } + + return Number(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Two Pointers + +::tabs-start + +```python +class Solution: + def numSubseq(self, nums: List[int], target: int) -> int: + nums.sort() + res = 0 + mod = 10**9 + 7 + + r = len(nums) - 1 + for i, left in enumerate(nums): + while i <= r and left + nums[r] > target: + r -= 1 + if i <= r: + res += pow(2, r - i, mod) + res %= mod + + return res +``` + +```java +public class Solution { + public int numSubseq(int[] nums, int target) { + Arrays.sort(nums); + int res = 0, mod = 1000000007; + int r = nums.length - 1; + + for (int i = 0; i < nums.length; i++) { + while (i <= r && nums[i] + nums[r] > target) { + r--; + } + if (i <= r) { + res = (res + power(2, r - i, mod)) % mod; + } + } + return res; + } + + private int power(int base, int exp, int mod) { + long result = 1, b = base; + while (exp > 0) { + if ((exp & 1) == 1) result = (result * b) % mod; + b = (b * b) % mod; + exp >>= 1; + } + return (int) result; + } +} +``` + +```cpp +class Solution { +public: + int numSubseq(vector& nums, int target) { + sort(nums.begin(), nums.end()); + int res = 0, mod = 1000000007; + int r = nums.size() - 1; + + for (int i = 0; i < nums.size(); i++) { + while (i <= r && nums[i] + nums[r] > target) { + r--; + } + if (i <= r) { + res = (res + power(2, r - i, mod)) % mod; + } + } + return res; + } + +private: + long long power(int base, int exp, int mod) { + long long result = 1, b = base; + while (exp > 0) { + if (exp & 1) result = (result * b) % mod; + b = (b * b) % mod; + exp >>= 1; + } + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + numSubseq(nums, target) { + nums.sort((a, b) => a - b); + const mod = BigInt(1000000007); + let res = 0n; + + const power = (base, exp, mod) => { + let result = 1n, b = BigInt(base); + while (exp > 0) { + if (exp & 1) result = (result * b) % mod; + b = (b * b) % mod; + exp >>= 1; + } + return result; + }; + + for (let i = 0, r = nums.length - 1; i < nums.length; i++) { + while (nums[i] + nums[r] > target && i <= r) { + r--; + } + if (i <= r) { + res = (res + power(2, r - i, mod)) % mod; + } + } + + return Number(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 4. Two Pointers (Optimal) + +::tabs-start + +```python +class Solution: + def numSubseq(self, nums: List[int], target: int) -> int: + nums.sort() + MOD = 1000000007 + res = 0 + l, r = 0, len(nums) - 1 + power = [1] * len(nums) + + for i in range(1, len(nums)): + power[i] = (power[i - 1] * 2) % MOD + + while l <= r: + if nums[l] + nums[r] <= target: + res = (res + power[r - l]) % MOD + l += 1 + else: + r -= 1 + + return res +``` + +```java +public class Solution { + public int numSubseq(int[] nums, int target) { + Arrays.sort(nums); + int MOD = 1000000007; + int res = 0, l = 0, r = nums.length - 1; + int[] power = new int[nums.length]; + power[0] = 1; + + for (int i = 1; i < nums.length; i++) { + power[i] = (power[i - 1] * 2) % MOD; + } + + while (l <= r) { + if (nums[l] + nums[r] <= target) { + res = (res + power[r - l]) % MOD; + l++; + } else { + r--; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubseq(vector& nums, int target) { + sort(nums.begin(), nums.end()); + int MOD = 1000000007; + int res = 0, l = 0, r = nums.size() - 1; + vector power(nums.size(), 1); + + for (int i = 1; i < nums.size(); i++) { + power[i] = (power[i - 1] * 2) % MOD; + } + + while (l <= r) { + if (nums[l] + nums[r] <= target) { + res = (res + power[r - l]) % MOD; + l++; + } else { + r--; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + numSubseq(nums, target) { + nums.sort((a, b) => a - b); + const MOD = 1000000007; + let res = 0, l = 0, r = nums.length - 1; + const power = Array(nums.length).fill(1); + + for (let i = 1; i < nums.length; i++) { + power[i] = (power[i - 1] * 2) % MOD; + } + + while (l <= r) { + if (nums[l] + nums[r] <= target) { + res = (res + power[r - l]) % MOD; + l++; + } else { + r--; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/number-of-ways-to-divide-a-long-corridor.md b/articles/number-of-ways-to-divide-a-long-corridor.md new file mode 100644 index 000000000..cda191a27 --- /dev/null +++ b/articles/number-of-ways-to-divide-a-long-corridor.md @@ -0,0 +1,608 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def numberOfWays(self, corridor: str) -> int: + mod = 10**9 + 7 + cache = [[-1] * 3 for i in range(len(corridor))] # (i, seats) -> count + + def dfs(i, seats): + if i == len(corridor): + return 1 if seats == 2 else 0 + if cache[i][seats] != -1: + return cache[i][seats] + + res = 0 + if seats == 2: + if corridor[i] == "S": + res = dfs(i + 1, 1) + else: + res = (dfs(i + 1, 0) + dfs(i + 1, 2)) % mod + else: + if corridor[i] == "S": + res = dfs(i + 1, seats + 1) + else: + res = dfs(i + 1, seats) + + cache[i][seats] = res + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + private int[][] dp; + + public int numberOfWays(String corridor) { + int n = corridor.length(); + dp = new int[n][3]; + for (int i = 0; i < n; i++) { + Arrays.fill(dp[i], -1); + } + return dfs(0, 0, corridor); + } + + private int dfs(int i, int seats, String corridor) { + if (i == corridor.length()) { + return seats == 2 ? 1 : 0; + } + if (dp[i][seats] != -1) { + return dp[i][seats]; + } + + int res = 0; + if (seats == 2) { + if (corridor.charAt(i) == 'S') { + res = dfs(i + 1, 1, corridor); + } else { + res = (dfs(i + 1, 0, corridor) + dfs(i + 1, 2, corridor)) % MOD; + } + } else { + if (corridor.charAt(i) == 'S') { + res = dfs(i + 1, seats + 1, corridor); + } else { + res = dfs(i + 1, seats, corridor); + } + } + + return dp[i][seats] = res; + } +} +``` + +```cpp +class Solution { +public: + static constexpr int MOD = 1'000'000'007; + vector> dp; + + int numberOfWays(string corridor) { + int n = corridor.size(); + dp.assign(n, vector(3, -1)); + return dfs(0, 0, corridor); + } + + int dfs(int i, int seats, string& corridor) { + if (i == corridor.size()) { + return seats == 2 ? 1 : 0; + } + if (dp[i][seats] != -1) { + return dp[i][seats]; + } + + int res = 0; + if (seats == 2) { + if (corridor[i] == 'S') { + res = dfs(i + 1, 1, corridor); + } else { + res = (dfs(i + 1, 0, corridor) + dfs(i + 1, 2, corridor)) % MOD; + } + } else { + if (corridor[i] == 'S') { + res = dfs(i + 1, seats + 1, corridor); + } else { + res = dfs(i + 1, seats, corridor); + } + } + + return dp[i][seats] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} corridor + * @return {number} + */ + numberOfWays(corridor) { + const MOD = 1_000_000_007; + const n = corridor.length; + const dp = Array.from({ length: n }, () => Array(3).fill(-1)); + + const dfs = (i, seats) => { + if (i === n) return seats === 2 ? 1 : 0; + if (dp[i][seats] !== -1) return dp[i][seats]; + + let res = 0; + if (seats === 2) { + if (corridor[i] === 'S') { + res = dfs(i + 1, 1); + } else { + res = (dfs(i + 1, 0) + dfs(i + 1, 2)) % MOD; + } + } else { + if (corridor[i] === 'S') { + res = dfs(i + 1, seats + 1); + } else { + res = dfs(i + 1, seats); + } + } + + return (dp[i][seats] = res); + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def numberOfWays(self, corridor: str) -> int: + MOD = 1000000007 + n = len(corridor) + dp = [[0] * 3 for _ in range(n + 1)] + dp[n][2] = 1 + + for i in range(n - 1, -1, -1): + for seats in range(3): + if seats == 2: + if corridor[i] == 'S': + dp[i][seats] = dp[i + 1][1] + else: + dp[i][seats] = (dp[i + 1][0] + dp[i + 1][2]) % MOD + else: + if corridor[i] == 'S': + dp[i][seats] = dp[i + 1][seats + 1] + else: + dp[i][seats] = dp[i + 1][seats] + + return dp[0][0] +``` + +```java +public class Solution { + public int numberOfWays(String corridor) { + int MOD = 1000000007; + int n = corridor.length(); + int[][] dp = new int[n + 1][3]; + dp[n][2] = 1; + + for (int i = n - 1; i >= 0; i--) { + for (int seats = 0; seats < 3; seats++) { + if (seats == 2) { + if (corridor.charAt(i) == 'S') { + dp[i][seats] = dp[i + 1][1]; + } else { + dp[i][seats] = (dp[i + 1][0] + dp[i + 1][2]) % MOD; + } + } else { + if (corridor.charAt(i) == 'S') { + dp[i][seats] = dp[i + 1][seats + 1]; + } else { + dp[i][seats] = dp[i + 1][seats]; + } + } + } + } + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + int numberOfWays(string corridor) { + int MOD = 1000000007; + int n = corridor.size(); + vector> dp(n + 1, vector(3, 0)); + dp[n][2] = 1; + + for (int i = n - 1; i >= 0; i--) { + for (int seats = 0; seats < 3; seats++) { + if (seats == 2) { + if (corridor[i] == 'S') { + dp[i][seats] = dp[i + 1][1]; + } else { + dp[i][seats] = (dp[i + 1][0] + dp[i + 1][2]) % MOD; + } + } else { + if (corridor[i] == 'S') { + dp[i][seats] = dp[i + 1][seats + 1]; + } else { + dp[i][seats] = dp[i + 1][seats]; + } + } + } + } + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} corridor + * @return {number} + */ + numberOfWays(corridor) { + const MOD = 1000000007; + const n = corridor.length; + let dp = Array.from({ length: n + 1 }, () => Array(3).fill(0)); + dp[n][2] = 1; + + for (let i = n - 1; i >= 0; i--) { + for (let seats = 0; seats < 3; seats++) { + if (seats === 2) { + if (corridor[i] === 'S') { + dp[i][seats] = dp[i + 1][1]; + } else { + dp[i][seats] = (dp[i + 1][0] + dp[i + 1][2]) % MOD; + } + } else { + if (corridor[i] === 'S') { + dp[i][seats] = dp[i + 1][seats + 1]; + } else { + dp[i][seats] = dp[i + 1][seats]; + } + } + } + } + return dp[0][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def numberOfWays(self, corridor: str) -> int: + MOD = 1000000007 + dp = [0, 0, 1] + + for i in reversed(corridor): + new_dp = [0] * 3 + for seats in range(3): + if seats == 2: + new_dp[seats] = dp[1] if i == 'S' else (dp[0] + dp[2]) % MOD + else: + new_dp[seats] = dp[seats + 1] if i == 'S' else dp[seats] + dp = new_dp + + return dp[0] +``` + +```java +public class Solution { + public int numberOfWays(String corridor) { + int MOD = 1000000007; + int[] dp = {0, 0, 1}; + + for (int i = corridor.length() - 1; i >= 0; i--) { + int[] new_dp = new int[3]; + for (int seats = 0; seats < 3; seats++) { + if (seats == 2) { + new_dp[seats] = corridor.charAt(i) == 'S' ? dp[1] : (dp[0] + dp[2]) % MOD; + } else { + new_dp[seats] = corridor.charAt(i) == 'S' ? dp[seats + 1] : dp[seats]; + } + } + dp = new_dp; + } + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int numberOfWays(string corridor) { + const int MOD = 1000000007; + vector dp = {0, 0, 1}; + + for (int i = corridor.length() - 1; i >= 0; i--) { + vector new_dp(3, 0); + for (int seats = 0; seats < 3; seats++) { + if (seats == 2) { + new_dp[seats] = (corridor[i] == 'S') ? dp[1] : (dp[0] + dp[2]) % MOD; + } else { + new_dp[seats] = (corridor[i] == 'S') ? dp[seats + 1] : dp[seats]; + } + } + dp = new_dp; + } + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} corridor + * @return {number} + */ + numberOfWays(corridor) { + const MOD = 1000000007; + let dp = [0, 0, 1]; + + for (let i = corridor.length - 1; i >= 0; i--) { + let new_dp = [0, 0, 0]; + for (let seats = 0; seats < 3; seats++) { + if (seats === 2) { + new_dp[seats] = corridor[i] === 'S' ? dp[1] : (dp[0] + dp[2]) % MOD; + } else { + new_dp[seats] = corridor[i] === 'S' ? dp[seats + 1] : dp[seats]; + } + } + dp = new_dp; + } + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Combinatorics + +::tabs-start + +```python +class Solution: + def numberOfWays(self, corridor: str) -> int: + mod = 10**9 + 7 + seats = [i for i, c in enumerate(corridor) if c == "S"] + + length = len(seats) + if length < 2 or length % 2 == 1: + return 0 + + res = 1 + for i in range(1, length - 1, 2): + res = (res * (seats[i + 1] - seats[i])) % mod + + return res +``` + +```java +public class Solution { + public int numberOfWays(String corridor) { + int mod = 1_000_000_007; + List seats = new ArrayList<>(); + + for (int i = 0; i < corridor.length(); i++) { + if (corridor.charAt(i) == 'S') { + seats.add(i); + } + } + + int length = seats.size(); + if (length < 2 || length % 2 == 1) { + return 0; + } + + long res = 1; + for (int i = 1; i < length - 1; i += 2) { + res = (res * (seats.get(i + 1) - seats.get(i))) % mod; + } + + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + int numberOfWays(string corridor) { + int mod = 1'000'000'007; + vector seats; + + for (int i = 0; i < corridor.size(); i++) { + if (corridor[i] == 'S') { + seats.push_back(i); + } + } + + int length = seats.size(); + if (length < 2 || length % 2 == 1) { + return 0; + } + + long long res = 1; + for (int i = 1; i < length - 1; i += 2) { + res = (res * (seats[i + 1] - seats[i])) % mod; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} corridor + * @return {number} + */ + numberOfWays(corridor) { + const mod = 1_000_000_007; + const seats = []; + + for (let i = 0; i < corridor.length; i++) { + if (corridor[i] === 'S') { + seats.push(i); + } + } + + const length = seats.length; + if (length < 2 || length % 2 === 1) { + return 0; + } + + let res = 1; + for (let i = 1; i < length - 1; i += 2) { + res = (res * (seats[i + 1] - seats[i])) % mod; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Combinatorics (Optimal) + +::tabs-start + +```python +class Solution: + def numberOfWays(self, corridor: str) -> int: + mod = 1_000_000_007 + count = 0 + res = 1 + prev = -1 + + for i, c in enumerate(corridor): + if c == 'S': + count += 1 + if count > 2 and count % 2 == 1: + res = (res * (i - prev)) % mod + prev = i + + return res if count >= 2 and count % 2 == 0 else 0 +``` + +```java +public class Solution { + public int numberOfWays(String corridor) { + int mod = 1_000_000_007; + int count = 0, res = 1, prev = -1; + + for (int i = 0; i < corridor.length(); i++) { + if (corridor.charAt(i) == 'S') { + count++; + if (count > 2 && count % 2 == 1) { + res = (int)((res * (long)(i - prev)) % mod); + } + prev = i; + } + } + + return (count >= 2 && count % 2 == 0) ? res : 0; + } +} +``` + +```cpp +class Solution { +public: + int numberOfWays(string corridor) { + int mod = 1'000'000'007, count = 0, res = 1, prev = -1; + + for (int i = 0; i < corridor.size(); i++) { + if (corridor[i] == 'S') { + count++; + if (count > 2 && count % 2 == 1) { + res = (1LL * res * (i - prev)) % mod; + } + prev = i; + } + } + + return (count >= 2 && count % 2 == 0) ? res : 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} corridor + * @return {number} + */ + numberOfWays(corridor) { + const mod = 1_000_000_007; + let count = 0, res = 1, prev = -1; + + for (let i = 0; i < corridor.length; i++) { + if (corridor[i] === 'S') { + count++; + if (count > 2 && count % 2 === 1) { + res = (res * (i - prev)) % mod; + } + prev = i; + } + } + + return count >= 2 && count % 2 === 0 ? res : 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/number-of-ways-to-form-a-target-string-given-a-dictionary.md b/articles/number-of-ways-to-form-a-target-string-given-a-dictionary.md new file mode 100644 index 000000000..878690a7c --- /dev/null +++ b/articles/number-of-ways-to-form-a-target-string-given-a-dictionary.md @@ -0,0 +1,568 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def numWays(self, words: List[str], target: str) -> int: + mod = 10**9 + 7 + n, m = len(target), len(words[0]) + + def dfs(i, k): + if i == n: + return 1 + if k == m: + return 0 + + res = dfs(i, k + 1) + for w in words: + if w[k] != target[i]: + continue + res = (res + dfs(i + 1, k + 1)) % mod + return res + + return dfs(0, 0) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int numWays(String[] words, String target) { + return dfs(words, target, 0, 0); + } + + private int dfs(String[] words, String target, int i, int k) { + if (i == target.length()) return 1; + if (k == words[0].length()) return 0; + + int res = dfs(words, target, i, k + 1); + for (String w : words) { + if (w.charAt(k) != target.charAt(i)) { + continue; + } + res = (int) (res + 0L + dfs(words, target, i + 1, k + 1)) % MOD; + } + + return res; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + + int dfs(vector& words, string target, int i, int k) { + if (i == target.length()) return 1; + if (k == words[0].length()) return 0; + + int res = dfs(words, target, i, k + 1); + for (string& w : words) { + if (w[k] != target[i]) { + continue; + } + res = (int) (res + 0LL + dfs(words, target, i + 1, k + 1)) % MOD; + } + + return res; + } + +public: + int numWays(vector& words, string target) { + return dfs(words, target, 0, 0); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string} target + * @return {number} + */ + numWays(words, target) { + const n = target.length; + const m = words[0].length; + const MOD = 1e9 + 7; + + const dfs = (i, k) => { + if (i === n) return 1; + if (k === m) return 0; + + let res = dfs(i, k + 1); + for (const w of words) { + if (w[k] != target[i]) { + continue; + } + res = (res + dfs(i + 1, k + 1)) % MOD; + } + + return res; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N ^ m)$ +* Space complexity: $O(m)$ + +> Where $N$ is the number of words, $m$ is the length of each word, and $n$ is the length of the $target$ string. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def numWays(self, words: List[str], target: str) -> int: + mod = 10**9 + 7 + cnt = defaultdict(int) # (index, char) --> count among all words + for w in words: + for i, c in enumerate(w): + cnt[(i, c)] += 1 + + dp = {} + + def dfs(i, k): + if i == len(target): + return 1 + if k == len(words[0]): + return 0 + if (i, k) in dp: + return dp[(i, k)] + + c = target[i] + dp[(i, k)] = dfs(i, k + 1) # skip k position + dp[(i, k)] += cnt[(k, c)] * dfs(i + 1, k + 1) + dp[(i, k)] %= mod + return dp[(i, k)] + + return dfs(0, 0) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + private int[][] dp; + private int[][] cnt; + + public int numWays(String[] words, String target) { + int n = target.length(), m = words[0].length(); + cnt = new int[m][26]; + + for (String word : words) { + for (int i = 0; i < word.length(); i++) { + cnt[i][word.charAt(i) - 'a']++; + } + } + + dp = new int[n + 1][m + 1]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + + return dfs(0, 0, target, n, m); + } + + private int dfs(int i, int k, String target, int n, int m) { + if (i == n) return 1; + if (k == m) return 0; + if (dp[i][k] != -1) return dp[i][k]; + + int c = target.charAt(i) - 'a'; + dp[i][k] = dfs(i, k + 1, target, n, m); // Skip k position + dp[i][k] = (int) ((dp[i][k] + (long) cnt[k][c] * dfs(i + 1, k + 1, target, n, m)) % MOD); + return dp[i][k]; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + vector> dp; + vector> cnt; + +public: + int numWays(vector& words, string target) { + int n = target.size(), m = words[0].size(); + cnt = vector>(m, vector(26, 0)); + + for (const string& word : words) { + for (int i = 0; i < word.size(); i++) { + cnt[i][word[i] - 'a']++; + } + } + + dp = vector>(n + 1, vector(m + 1, -1)); + return dfs(0, 0, target, n, m); + } + +private: + int dfs(int i, int k, const string& target, int n, int m) { + if (i == n) return 1; + if (k == m) return 0; + if (dp[i][k] != -1) return dp[i][k]; + + int c = target[i] - 'a'; + dp[i][k] = dfs(i, k + 1, target, n, m); // Skip k position + dp[i][k] = (dp[i][k] + (long long) cnt[k][c] * dfs(i + 1, k + 1, target, n, m)) % MOD; + return dp[i][k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string} target + * @return {number} + */ + numWays(words, target) { + const MOD = 1e9 + 7; + const n = target.length, m = words[0].length; + const cnt = Array.from({ length: m }, () => Array(26).fill(0)); + + for (const word of words) { + for (let i = 0; i < word.length; i++) { + cnt[i][word.charCodeAt(i) - 97]++; + } + } + + const dp = Array.from({ length: n + 1 }, () => Array(m + 1).fill(-1)); + + const dfs = (i, k) => { + if (i === n) return 1; + if (k === m) return 0; + if (dp[i][k] !== -1) return dp[i][k]; + + const c = target.charCodeAt(i) - 97; + dp[i][k] = dfs(i, k + 1); // Skip k position + dp[i][k] = (dp[i][k] + cnt[k][c] * dfs(i + 1, k + 1)) % MOD; + return dp[i][k]; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * (n + N))$ +* Space complexity: $O(n * m)$ + +> Where $N$ is the number of words, $m$ is the length of each word, and $n$ is the length of the $target$ string. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def numWays(self, words: List[str], target: str) -> int: + MOD = 10**9 + 7 + n, m = len(target), len(words[0]) + + cnt = [[0] * 26 for _ in range(m)] + for word in words: + for i, c in enumerate(word): + cnt[i][ord(c) - ord('a')] += 1 + + dp = [[0] * (m + 1) for _ in range(n + 1)] + dp[n][m] = 1 + + for i in range(n, -1, -1): + for k in range(m - 1, -1, -1): + dp[i][k] = dp[i][k + 1] + if i < n: + c = ord(target[i]) - ord('a') + dp[i][k] = (dp[i][k] + cnt[k][c] * dp[i + 1][k + 1]) % MOD + + return dp[0][0] +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int numWays(String[] words, String target) { + int n = target.length(), m = words[0].length(); + + int[][] cnt = new int[m][26]; + for (String word : words) { + for (int i = 0; i < word.length(); i++) { + cnt[i][word.charAt(i) - 'a']++; + } + } + + int[][] dp = new int[n + 1][m + 1]; + dp[n][m] = 1; + + for (int i = n; i >= 0; i--) { + for (int k = m - 1; k >= 0; k--) { + dp[i][k] = dp[i][k + 1]; + if (i < n) { + int c = target.charAt(i) - 'a'; + dp[i][k] = (int) ((dp[i][k] + (long) cnt[k][c] * dp[i + 1][k + 1]) % MOD); + } + } + } + + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + +public: + int numWays(vector& words, string target) { + int n = target.size(), m = words[0].size(); + + vector> cnt(m, vector(26, 0)); + for (const string& word : words) { + for (int i = 0; i < word.size(); i++) { + cnt[i][word[i] - 'a']++; + } + } + + vector> dp(n + 1, vector(m + 1, 0)); + dp[n][m] = 1; + + for (int i = n; i >= 0; i--) { + for (int k = m - 1; k >= 0; k--) { + dp[i][k] = dp[i][k + 1]; + if (i < n) { + int c = target[i] - 'a'; + dp[i][k] = (dp[i][k] + (long long) cnt[k][c] * dp[i + 1][k + 1]) % MOD; + } + } + } + + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string} target + * @return {number} + */ + numWays(words, target) { + const MOD = 1e9 + 7; + const n = target.length, m = words[0].length; + + const cnt = Array.from({ length: m }, () => Array(26).fill(0)); + for (const word of words) { + for (let i = 0; i < word.length; i++) { + cnt[i][word.charCodeAt(i) - 'a'.charCodeAt(0)]++; + } + } + + const dp = Array.from({ length: n + 1 }, () => Array(m + 1).fill(0)); + dp[n][m] = 1; + + for (let i = n; i >= 0; i--) { + for (let k = m - 1; k >= 0; k--) { + dp[i][k] = dp[i][k + 1]; + if (i < n) { + const c = target.charCodeAt(i) - 'a'.charCodeAt(0); + dp[i][k] = (dp[i][k] + cnt[k][c] * dp[i + 1][k + 1]) % MOD; + } + } + } + + return dp[0][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * (n + N))$ +* Space complexity: $O(n * m)$ + +> Where $N$ is the number of words, $m$ is the length of each word, and $n$ is the length of the $target$ string. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def numWays(self, words: List[str], target: str) -> int: + MOD = 10**9 + 7 + n, m = len(target), len(words[0]) + + cnt = [[0] * 26 for _ in range(m)] + for word in words: + for i, c in enumerate(word): + cnt[i][ord(c) - ord('a')] += 1 + + dp = [0] * (m + 1) + dp[m] = 1 + + for i in range(n, -1, -1): + nxt = 1 if i == n - 1 else 0 + for k in range(m - 1, -1, -1): + cur = dp[k] + dp[k] = dp[k + 1] + if i < n: + c = ord(target[i]) - ord('a') + dp[k] = (dp[k] + cnt[k][c] * nxt) % MOD + nxt = cur + dp[m] = 0 + + return dp[0] +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int numWays(String[] words, String target) { + int n = target.length(), m = words[0].length(); + + int[][] cnt = new int[m][26]; + for (String word : words) { + for (int i = 0; i < word.length(); i++) { + cnt[i][word.charAt(i) - 'a']++; + } + } + + int[] dp = new int[m + 1]; + dp[m] = 1; + + for (int i = n; i >= 0; i--) { + int nxt = i == n - 1 ? 1 : 0; + for (int k = m - 1; k >= 0; k--) { + int cur = dp[k]; + dp[k] = dp[k + 1]; + if (i < n) { + int c = target.charAt(i) - 'a'; + dp[k] = (int) ((dp[k] + (long) cnt[k][c] * nxt) % MOD); + } + nxt = cur; + } + dp[m] = 0; + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + +public: + int numWays(vector& words, string target) { + int n = target.size(), m = words[0].size(); + + vector> cnt(m, vector(26, 0)); + for (const string& word : words) { + for (int i = 0; i < word.size(); i++) { + cnt[i][word[i] - 'a']++; + } + } + + vector dp(m + 1); + dp[m] = 1; + + for (int i = n; i >= 0; i--) { + int nxt = i == n - 1 ? 1 : 0; + for (int k = m - 1; k >= 0; k--) { + int cur = dp[k]; + dp[k] = dp[k + 1]; + if (i < n) { + int c = target[i] - 'a'; + dp[k] = (dp[k] + (long long) cnt[k][c] * nxt) % MOD; + } + nxt = cur; + } + dp[m] = 0; + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string} target + * @return {number} + */ + numWays(words, target) { + const MOD = 1e9 + 7; + const n = target.length, m = words[0].length; + + const cnt = Array.from({ length: m }, () => Array(26).fill(0)); + for (const word of words) { + for (let i = 0; i < word.length; i++) { + cnt[i][word.charCodeAt(i) - 'a'.charCodeAt(0)]++; + } + } + + const dp = new Array(m + 1).fill(0); + dp[m] = 1; + + for (let i = n; i >= 0; i--) { + let nxt = i === n - 1 ? 1 : 0; + for (let k = m - 1; k >= 0; k--) { + const cur = dp[k]; + dp[k] = dp[k + 1]; + if (i < n) { + const c = target.charCodeAt(i) - 'a'.charCodeAt(0); + dp[k] = (dp[k] + cnt[k][c] * nxt) % MOD; + } + nxt = cur; + } + dp[m] = 0; + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * (n + N))$ +* Space complexity: $O(m)$ + +> Where $N$ is the number of words, $m$ is the length of each word, and $n$ is the length of the $target$ string. \ No newline at end of file diff --git a/articles/number-of-ways-to-rearrange-sticks-with-k-sticks-visible.md b/articles/number-of-ways-to-rearrange-sticks-with-k-sticks-visible.md new file mode 100644 index 000000000..d192c77b6 --- /dev/null +++ b/articles/number-of-ways-to-rearrange-sticks-with-k-sticks-visible.md @@ -0,0 +1,375 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def rearrangeSticks(self, n: int, k: int) -> int: + MOD = 10**9 + 7 + + def dfs(N, K): + if N == K: + return 1 + if N == 0 or K == 0: + return 0 + return (dfs(N - 1, K - 1) + (N - 1) * dfs(N - 1, K)) % MOD + + return dfs(n, k) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int rearrangeSticks(int n, int k) { + return dfs(n, k); + } + + private int dfs(int N, int K) { + if (N == K) return 1; + if (N == 0 || K == 0) return 0; + return (int) ((dfs(N - 1, K - 1) + (long) (N - 1) * dfs(N - 1, K)) % MOD); + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + + int dfs(int N, int K) { + if (N == K) return 1; + if (N == 0 || K == 0) return 0; + return (dfs(N - 1, K - 1) + (long long)(N - 1) * dfs(N - 1, K) % MOD) % MOD; + } + +public: + int rearrangeSticks(int n, int k) { + return dfs(n, k); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + rearrangeSticks(n, k) { + const MOD = 1e9 + 7; + + const dfs = (N, K) => { + if (N === K) return 1; + if (N === 0 || K === 0) return 0; + return (dfs(N - 1, K - 1) + (N - 1) * dfs(N - 1, K)) % MOD; + }; + + return dfs(n, k); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def rearrangeSticks(self, n: int, k: int) -> int: + MOD = 10**9 + 7 + dp = [[-1] * (k + 1) for _ in range(n + 1)] + + def dfs(N, K): + if N == K: + return 1 + if N == 0 or K == 0: + return 0 + if dp[N][K] != -1: + return dp[N][K] + dp[N][K] = (dfs(N - 1, K - 1) + (N - 1) * dfs(N - 1, K)) % MOD + return dp[N][K] + + return dfs(n, k) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + private int[][] dp; + + public int rearrangeSticks(int n, int k) { + dp = new int[n + 1][k + 1]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + return dfs(n, k); + } + + private int dfs(int N, int K) { + if (N == K) return 1; + if (N == 0 || K == 0) return 0; + if (dp[N][K] != -1) return dp[N][K]; + dp[N][K] = (int) ((dfs(N - 1, K - 1) + (long) (N - 1) * dfs(N - 1, K)) % MOD); + return dp[N][K]; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + vector> dp; + + int dfs(int N, int K) { + if (N == K) return 1; + if (N == 0 || K == 0) return 0; + if (dp[N][K] != -1) return dp[N][K]; + dp[N][K] = (dfs(N - 1, K - 1) + (long long)(N - 1) * dfs(N - 1, K) % MOD) % MOD; + return dp[N][K]; + } + +public: + int rearrangeSticks(int n, int k) { + dp = vector>(n + 1, vector(k + 1, -1)); + return dfs(n, k); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + rearrangeSticks(n, k) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: n + 1 }, () => Array(k + 1).fill(-1)); + + const dfs = (N, K) => { + if (N === K) return 1; + if (N === 0 || K === 0) return 0; + if (dp[N][K] !== -1) return dp[N][K]; + dp[N][K] = (dfs(N - 1, K - 1) + (N - 1) * dfs(N - 1, K)) % MOD; + return dp[N][K]; + }; + + return dfs(n, k); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ represents the total number of sticks, and $k$ denotes the number of sticks that must be visible from the left side. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def rearrangeSticks(self, n: int, k: int) -> int: + MOD = 10**9 + 7 + dp = [[0] * (k + 1) for _ in range(n + 1)] + dp[1][1] = 1 + + for N in range(2, n + 1): + for K in range(1, k + 1): + dp[N][K] = (dp[N - 1][K - 1] + (N - 1) * dp[N - 1][K]) % MOD + + return dp[n][k] +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int rearrangeSticks(int n, int k) { + int[][] dp = new int[n + 1][k + 1]; + dp[1][1] = 1; + + for (int N = 2; N <= n; N++) { + for (int K = 1; K <= k; K++) { + dp[N][K] = (int) ((dp[N - 1][K - 1] + (long) (N - 1) * dp[N - 1][K]) % MOD); + } + } + + return dp[n][k]; + } +} +``` + +```cpp +class Solution { +public: + int rearrangeSticks(int n, int k) { + const int MOD = 1e9 + 7; + vector> dp(n + 1, vector(k + 1, 0)); + dp[1][1] = 1; + + for (int N = 2; N <= n; N++) { + for (int K = 1; K <= k; K++) { + dp[N][K] = (dp[N - 1][K - 1] + (N - 1) * 1LL * dp[N - 1][K]) % MOD; + } + } + + return dp[n][k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + rearrangeSticks(n, k) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: n + 1 }, () => Array(k + 1).fill(0)); + dp[1][1] = 1; + + for (let N = 2; N <= n; N++) { + for (let K = 1; K <= k; K++) { + dp[N][K] = (dp[N - 1][K - 1] + (N - 1) * dp[N - 1][K]) % MOD; + } + } + + return dp[n][k]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ represents the total number of sticks, and $k$ denotes the number of sticks that must be visible from the left side. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def rearrangeSticks(self, n: int, k: int) -> int: + MOD = 10**9 + 7 + dp = [0] * (k + 1) + dp[1] = 1 + + for N in range(2, n + 1): + prev = 0 + for K in range(1, k + 1): + tmp = dp[K] + dp[K] = (prev + (N - 1) * dp[K]) % MOD + prev = tmp + + return dp[k] +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int rearrangeSticks(int n, int k) { + int[] dp = new int[k + 1]; + dp[1] = 1; + + for (int N = 2; N <= n; N++) { + int prev = 0; + for (int K = 1; K <= k; K++) { + int tmp = dp[K]; + dp[K] = (int) ((prev + (long) (N - 1) * dp[K]) % MOD); + prev = tmp; + } + } + + return dp[k]; + } +} +``` + +```cpp +class Solution { +public: + int rearrangeSticks(int n, int k) { + const int MOD = 1e9 + 7; + vector dp(k + 1); + dp[1] = 1; + + for (int N = 2; N <= n; N++) { + int prev = 0; + for (int K = 1; K <= k; K++) { + int tmp = dp[K]; + dp[K] = (prev + (N - 1) * 1LL * dp[K]) % MOD; + prev = tmp; + } + } + + return dp[k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} k + * @return {number} + */ + rearrangeSticks(n, k) { + const MOD = 1e9 + 7; + const dp = new Array(k + 1).fill(0); + dp[1] = 1; + + for (let N = 2; N <= n; N++) { + let prev = 0; + for (let K = 1; K <= k; K++) { + const tmp = dp[K]; + dp[K] = (prev + (N - 1) * dp[K]) % MOD; + prev = tmp; + } + } + + return dp[k]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(k)$ + +> Where $n$ represents the total number of sticks, and $k$ denotes the number of sticks that must be visible from the left side. \ No newline at end of file diff --git a/articles/number-of-ways-to-split-array.md b/articles/number-of-ways-to-split-array.md new file mode 100644 index 000000000..ca66c41c0 --- /dev/null +++ b/articles/number-of-ways-to-split-array.md @@ -0,0 +1,320 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def waysToSplitArray(self, nums: List[int]) -> int: + n = len(nums) + res = 0 + + for i in range(n - 1): + leftSum = 0 + for j in range(i + 1): + leftSum += nums[j] + + rightSum = 0 + for j in range(i + 1, n): + rightSum += nums[j] + + res += (1 if leftSum >= rightSum else 0) + + return res +``` + +```java +public class Solution { + public int waysToSplitArray(int[] nums) { + int n = nums.length; + int res = 0; + + for (int i = 0; i < n - 1; i++) { + long leftSum = 0; + for (int j = 0; j <= i; j++) { + leftSum += nums[j]; + } + + long rightSum = 0; + for (int j = i + 1; j < n; j++) { + rightSum += nums[j]; + } + + if (leftSum >= rightSum) { + res++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int waysToSplitArray(vector& nums) { + int n = nums.size(); + int res = 0; + + for (int i = 0; i < n - 1; i++) { + long long leftSum = 0; + for (int j = 0; j <= i; j++) { + leftSum += nums[j]; + } + + long long rightSum = 0; + for (int j = i + 1; j < n; j++) { + rightSum += nums[j]; + } + + if (leftSum >= rightSum) { + res++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + waysToSplitArray(nums) { + let n = nums.length; + let res = 0; + + for (let i = 0; i < n - 1; i++) { + let leftSum = 0; + for (let j = 0; j <= i; j++) { + leftSum += nums[j]; + } + + let rightSum = 0; + for (let j = i + 1; j < n; j++) { + rightSum += nums[j]; + } + + if (leftSum >= rightSum) { + res++; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Prefix Sum + +::tabs-start + +```python +class Solution: + def waysToSplitArray(self, nums: List[int]) -> int: + n = len(nums) + prefix = [0] * (n + 1) + + for i in range(n): + prefix[i + 1] = prefix[i] + nums[i] + + res = 0 + for i in range(1, n): + left = prefix[i] + right = prefix[n] - prefix[i] + if left >= right: + res += 1 + + return res +``` + +```java +public class Solution { + public int waysToSplitArray(int[] nums) { + int n = nums.length; + long[] prefix = new long[n + 1]; + + for (int i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + nums[i]; + } + + int res = 0; + for (int i = 1; i < n; i++) { + long left = prefix[i]; + long right = prefix[n] - prefix[i]; + if (left >= right) { + res++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int waysToSplitArray(vector& nums) { + int n = nums.size(); + vector prefix(n + 1, 0); + + for (int i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + nums[i]; + } + + int res = 0; + for (int i = 1; i < n; i++) { + long long left = prefix[i]; + long long right = prefix[n] - prefix[i]; + if (left >= right) { + res++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + waysToSplitArray(nums) { + const n = nums.length; + const prefix = Array(n + 1).fill(0); + + for (let i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + nums[i]; + } + + let res = 0; + for (let i = 1; i < n; i++) { + const left = prefix[i]; + const right = prefix[n] - prefix[i]; + if (left >= right) { + res++; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Prefix Sum (Optimal) + +::tabs-start + +```python +class Solution: + def waysToSplitArray(self, nums: List[int]) -> int: + right = sum(nums) + left = res = 0 + + for i in range(len(nums) - 1): + left += nums[i] + right -= nums[i] + res += 1 if left >= right else 0 + + return res +``` + +```java +public class Solution { + public int waysToSplitArray(int[] nums) { + long right = 0, left = 0; + for (int num : nums) { + right += num; + } + + int res = 0; + for (int i = 0; i < nums.length - 1; i++) { + left += nums[i]; + right -= nums[i]; + if (left >= right) { + res++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int waysToSplitArray(vector& nums) { + long long right = 0, left = 0; + for (int num : nums) { + right += num; + } + + int res = 0; + for (int i = 0; i < nums.size() - 1; i++) { + left += nums[i]; + right -= nums[i]; + if (left >= right) { + res++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + waysToSplitArray(nums) { + let right = nums.reduce((a, b) => a + b, 0); + let left = 0, res = 0; + + for (let i = 0; i < nums.length - 1; i++) { + left += nums[i]; + right -= nums[i]; + if (left >= right) { + res++; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/number-of-ways-to-stay-in-the-same-place-after-some-steps.md b/articles/number-of-ways-to-stay-in-the-same-place-after-some-steps.md new file mode 100644 index 000000000..2c9577d2a --- /dev/null +++ b/articles/number-of-ways-to-stay-in-the-same-place-after-some-steps.md @@ -0,0 +1,503 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def numWays(self, steps: int, arrLen: int) -> int: + mod = 10**9 + 7 + arrLen = min(arrLen, steps) + dp = {} + + def dfs(i, steps): + if steps == 0: + return i == 0 + if (i, steps) in dp: + return dp[(i, steps)] + + res = dfs(i, steps - 1) + if i > 0: + res = (res + dfs(i - 1, steps - 1)) % mod + if i < arrLen - 1: + res = (res + dfs(i + 1, steps - 1)) % mod + + dp[(i, steps)] = res + return res + + return dfs(0, steps) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + private int[][] dp; + + public int numWays(int steps, int arrLen) { + int maxPos = Math.min(steps, arrLen); + dp = new int[maxPos + 1][steps + 1]; + for (int i = 0; i <= maxPos; i++) { + for (int j = 0; j <= steps; j++) { + dp[i][j] = -1; + } + } + return dfs(0, steps, maxPos); + } + + private int dfs(int i, int steps, int maxPos) { + if (steps == 0) { + return i == 0 ? 1 : 0; + } + if (dp[i][steps] != -1) { + return dp[i][steps]; + } + + int res = dfs(i, steps - 1, maxPos); + if (i > 0) { + res = (res + dfs(i - 1, steps - 1, maxPos)) % MOD; + } + if (i < maxPos - 1) { + res = (res + dfs(i + 1, steps - 1, maxPos)) % MOD; + } + + dp[i][steps] = res; + return res; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + vector> dp; + + int dfs(int i, int steps, int maxPos) { + if (steps == 0) { + return i == 0 ? 1 : 0; + } + if (dp[i][steps] != -1) { + return dp[i][steps]; + } + + int res = dfs(i, steps - 1, maxPos); + if (i > 0) { + res = (res + dfs(i - 1, steps - 1, maxPos)) % MOD; + } + if (i < maxPos - 1) { + res = (res + dfs(i + 1, steps - 1, maxPos)) % MOD; + } + + dp[i][steps] = res; + return res; + } + +public: + int numWays(int steps, int arrLen) { + int maxPos = min(steps, arrLen); + dp.assign(maxPos + 1, vector(steps + 1, -1)); + return dfs(0, steps, maxPos); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} steps + * @param {number} arrLen + * @return {number} + */ + numWays(steps, arrLen) { + const MOD = 1e9 + 7; + const maxPos = Math.min(steps, arrLen); + const dp = Array.from({ length: maxPos + 1 }, () => Array(steps + 1).fill(-1)); + + const dfs = (i, steps) => { + if (steps === 0) return i === 0 ? 1 : 0; + if (dp[i][steps] !== -1) return dp[i][steps]; + + let res = dfs(i, steps - 1); + if (i > 0) res = (res + dfs(i - 1, steps - 1)) % MOD; + if (i < maxPos - 1) res = (res + dfs(i + 1, steps - 1)) % MOD; + + dp[i][steps] = res; + return res; + }; + + return dfs(0, steps); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * min(n, m))$ +* Space complexity: $O(n * min(n, m))$ + +> Where $n$ is the number of steps and $m$ is the size of the array. + +--- + +## 2. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def numWays(self, steps: int, arrLen: int) -> int: + mod = 10**9 + 7 + arrLen = min(arrLen, steps) + dp = [[0] * (arrLen + 1) for _ in range(steps + 1)] + dp[0][0] = 1 + + for step in range(1, steps + 1): + for i in range(arrLen): + res = dp[step - 1][i] + if i > 0: + res = (res + dp[step - 1][i - 1]) % mod + if i < arrLen - 1: + res = (res + dp[step - 1][i + 1]) % mod + dp[step][i] = res + + return dp[steps][0] +``` + +```java +public class Solution { + public int numWays(int steps, int arrLen) { + final int MOD = 1_000_000_007; + arrLen = Math.min(arrLen, steps); + int[][] dp = new int[steps + 1][arrLen + 1]; + dp[0][0] = 1; + + for (int step = 1; step <= steps; step++) { + for (int i = 0; i < arrLen; i++) { + int res = dp[step - 1][i]; + if (i > 0) { + res = (res + dp[step - 1][i - 1]) % MOD; + } + if (i < arrLen - 1) { + res = (res + dp[step - 1][i + 1]) % MOD; + } + dp[step][i] = res; + } + } + + return dp[steps][0]; + } +} +``` + +```cpp +class Solution { +public: + int numWays(int steps, int arrLen) { + const int MOD = 1e9 + 7; + arrLen = min(arrLen, steps); + vector> dp(steps + 1, vector(arrLen + 1, 0)); + dp[0][0] = 1; + + for (int step = 1; step <= steps; step++) { + for (int i = 0; i < arrLen; i++) { + int res = dp[step - 1][i]; + if (i > 0) { + res = (res + dp[step - 1][i - 1]) % MOD; + } + if (i < arrLen - 1) { + res = (res + dp[step - 1][i + 1]) % MOD; + } + dp[step][i] = res; + } + } + + return dp[steps][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} steps + * @param {number} arrLen + * @return {number} + */ + numWays(steps, arrLen) { + const MOD = 1e9 + 7; + arrLen = Math.min(arrLen, steps); + const dp = Array.from({ length: steps + 1 }, () => Array(arrLen + 1).fill(0)); + dp[0][0] = 1; + + for (let step = 1; step <= steps; step++) { + for (let i = 0; i < arrLen; i++) { + let res = dp[step - 1][i]; + if (i > 0) { + res = (res + dp[step - 1][i - 1]) % MOD; + } + if (i < arrLen - 1) { + res = (res + dp[step - 1][i + 1]) % MOD; + } + dp[step][i] = res; + } + } + + return dp[steps][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * min(n, m))$ +* Space complexity: $O(n * min(n, m))$ + +> Where $n$ is the number of steps and $m$ is the size of the array. + +--- + +## 3. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def numWays(self, steps: int, arrLen: int) -> int: + mod = 10**9 + 7 + arrLen = min(steps, arrLen) + dp = [0] * arrLen + dp[0] = 1 + + for _ in range(steps): + next_dp = [0] * arrLen + for i in range(arrLen): + next_dp[i] = dp[i] + if i > 0: + next_dp[i] = (next_dp[i] + dp[i - 1]) % mod + if i < arrLen - 1: + next_dp[i] = (next_dp[i] + dp[i + 1]) % mod + dp = next_dp + + return dp[0] +``` + +```java +public class Solution { + public int numWays(int steps, int arrLen) { + final int MOD = 1_000_000_007; + arrLen = Math.min(steps, arrLen); + int[] dp = new int[arrLen]; + dp[0] = 1; + + for (int step = 0; step < steps; step++) { + int[] nextDp = new int[arrLen]; + for (int i = 0; i < arrLen; i++) { + nextDp[i] = dp[i]; + if (i > 0) { + nextDp[i] = (nextDp[i] + dp[i - 1]) % MOD; + } + if (i < arrLen - 1) { + nextDp[i] = (nextDp[i] + dp[i + 1]) % MOD; + } + } + dp = nextDp; + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int numWays(int steps, int arrLen) { + const int MOD = 1e9 + 7; + arrLen = min(steps, arrLen); + vector dp(arrLen, 0); + dp[0] = 1; + + for (int step = 0; step < steps; step++) { + vector nextDp(arrLen, 0); + for (int i = 0; i < arrLen; i++) { + nextDp[i] = dp[i]; + if (i > 0) { + nextDp[i] = (nextDp[i] + dp[i - 1]) % MOD; + } + if (i < arrLen - 1) { + nextDp[i] = (nextDp[i] + dp[i + 1]) % MOD; + } + } + dp = nextDp; + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} steps + * @param {number} arrLen + * @return {number} + */ + numWays(steps, arrLen) { + const MOD = 1e9 + 7; + arrLen = Math.min(steps, arrLen); + let dp = new Array(arrLen).fill(0); + dp[0] = 1; + + for (let step = 0; step < steps; step++) { + const nextDp = new Array(arrLen).fill(0); + for (let i = 0; i < arrLen; i++) { + nextDp[i] = dp[i]; + if (i > 0) { + nextDp[i] = (nextDp[i] + dp[i - 1]) % MOD; + } + if (i < arrLen - 1) { + nextDp[i] = (nextDp[i] + dp[i + 1]) % MOD; + } + } + dp = nextDp; + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * min(n, m))$ +* Space complexity: $O(min(n, m))$ + +> Where $n$ is the number of steps and $m$ is the size of the array. + +--- + +## 4. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def numWays(self, steps: int, arrLen: int) -> int: + mod = 10**9 + 7 + arrLen = min(steps, arrLen) + dp = [0] * arrLen + dp[0] = 1 + + for _ in range(steps): + prev = 0 + for i in range(arrLen): + cur = dp[i] + if i > 0: + dp[i] = (dp[i] + prev) % mod + if i < arrLen - 1: + dp[i] = (dp[i] + dp[i + 1]) % mod + prev = cur + + return dp[0] +``` + +```java +public class Solution { + public int numWays(int steps, int arrLen) { + final int MOD = 1_000_000_007; + arrLen = Math.min(steps, arrLen); + int[] dp = new int[arrLen]; + dp[0] = 1; + + for (int step = 0; step < steps; step++) { + int prev = 0; + for (int i = 0; i < arrLen; i++) { + int cur = dp[i]; + if (i > 0) { + dp[i] = (dp[i] + prev) % MOD; + } + if (i < arrLen - 1) { + dp[i] = (dp[i] + dp[i + 1]) % MOD; + } + prev = cur; + } + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int numWays(int steps, int arrLen) { + const int MOD = 1e9 + 7; + arrLen = min(steps, arrLen); + vector dp(arrLen, 0); + dp[0] = 1; + + for (int step = 0; step < steps; step++) { + int prev = 0; + for (int i = 0; i < arrLen; i++) { + int cur = dp[i]; + if (i > 0) { + dp[i] = (dp[i] + prev) % MOD; + } + if (i < arrLen - 1) { + dp[i] = (dp[i] + dp[i + 1]) % MOD; + } + prev = cur; + } + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} steps + * @param {number} arrLen + * @return {number} + */ + numWays(steps, arrLen) { + const MOD = 1e9 + 7; + arrLen = Math.min(steps, arrLen); + const dp = new Array(arrLen).fill(0); + dp[0] = 1; + + for (let step = 0; step < steps; step++) { + for (let i = 0; i < arrLen; i++) { + const cur = dp[i]; + if (i > 0) { + dp[i] = (dp[i] + prev) % MOD; + } + if (i < arrLen - 1) { + dp[i] = (dp[i] + dp[i + 1]) % MOD; + } + prev = cur; + } + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * min(n, m))$ +* Space complexity: $O(min(n, m))$ + +> Where $n$ is the number of steps and $m$ is the size of the array. \ No newline at end of file diff --git a/articles/number-of-zero-filled-subarrays.md b/articles/number-of-zero-filled-subarrays.md new file mode 100644 index 000000000..266353c04 --- /dev/null +++ b/articles/number-of-zero-filled-subarrays.md @@ -0,0 +1,338 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def zeroFilledSubarray(self, nums: List[int]) -> int: + res = 0 + for i in range(len(nums)): + for j in range(i, len(nums)): + if nums[j] != 0: + break + res += 1 + return res +``` + +```java +public class Solution { + public long zeroFilledSubarray(int[] nums) { + long res = 0; + for (int i = 0; i < nums.length; i++) { + for (int j = i; j < nums.length; j++) { + if (nums[j] != 0) break; + res++; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + long long zeroFilledSubarray(vector& nums) { + long long res = 0; + for (int i = 0; i < nums.size(); i++) { + for (int j = i; j < nums.size(); j++) { + if (nums[j] != 0) break; + res++; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + zeroFilledSubarray(nums) { + let res = 0; + for (let i = 0; i < nums.length; i++) { + for (let j = i; j < nums.length; j++) { + if (nums[j] != 0) break; + res++; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Count Consecutive Zeros - I + +::tabs-start + +```python +class Solution: + def zeroFilledSubarray(self, nums: List[int]) -> int: + res = i = 0 + while i < len(nums): + count = 0 + while i < len(nums) and nums[i] == 0: + count += 1 + i += 1 + res += count + i += 1 + return res +``` + +```java +public class Solution { + public long zeroFilledSubarray(int[] nums) { + long res = 0; + int i = 0; + while (i < nums.length) { + long count = 0; + while (i < nums.length && nums[i] == 0) { + count++; + i++; + res += count; + } + i++; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + long long zeroFilledSubarray(vector& nums) { + long long res = 0; + int i = 0; + while (i < nums.size()) { + long long count = 0; + while (i < nums.size() && nums[i] == 0) { + count++; + i++; + res += count; + } + i++; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + zeroFilledSubarray(nums) { + let res = 0, i = 0; + while (i < nums.length) { + let count = 0; + while (i < nums.length && nums[i] === 0) { + count++; + i++; + res += count; + } + i++; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Count Consecutive Zeros - II + +::tabs-start + +```python +class Solution: + def zeroFilledSubarray(self, nums: List[int]) -> int: + res = count = 0 + + for num in nums: + if num == 0: + count += 1 + else: + count = 0 + res += count + + return res +``` + +```java +public class Solution { + public long zeroFilledSubarray(int[] nums) { + long res = 0; + int count = 0; + + for (int num : nums) { + if (num == 0) { + count++; + } else { + count = 0; + } + res += count; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + long long zeroFilledSubarray(vector& nums) { + long long res = 0; + int count = 0; + + for (int& num : nums) { + if (num == 0) { + count++; + } else { + count = 0; + } + res += count; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + zeroFilledSubarray(nums) { + let res = 0, count = 0; + + for (let num of nums) { + if (num === 0) { + count++; + } else { + count = 0; + } + res += count; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Count Consecutive Zeros (Math) + +::tabs-start + +```python +class Solution: + def zeroFilledSubarray(self, nums: List[int]) -> int: + res = count = 0 + for num in nums: + if num == 0: + count += 1 + else: + res += (count * (count + 1)) // 2 + count = 0 + res += (count * (count + 1)) // 2 + return res +``` + +```java +public class Solution { + public long zeroFilledSubarray(int[] nums) { + long res = 0, count = 0; + for (int num : nums) { + if (num == 0) { + count++; + } else { + res += count * (count + 1) / 2; + count = 0; + } + } + res += count * (count + 1) / 2; + return res; + } +} +``` + +```cpp +class Solution { +public: + long long zeroFilledSubarray(vector& nums) { + long long res = 0, count = 0; + for (int num : nums) { + if (num == 0) { + count++; + } else { + res += count * (count + 1) / 2; + count = 0; + } + } + res += count * (count + 1) / 2; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + zeroFilledSubarray(nums) { + let res = 0, count = 0; + for (let num of nums) { + if (num === 0) { + count++; + } else { + res += (count * (count + 1)) / 2; + count = 0; + } + } + res += (count * (count + 1)) / 2; + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/ones-and-zeroes.md b/articles/ones-and-zeroes.md new file mode 100644 index 000000000..4d3e53b4d --- /dev/null +++ b/articles/ones-and-zeroes.md @@ -0,0 +1,531 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def findMaxForm(self, strs: List[str], m: int, n: int) -> int: + arr = [[0] * 2 for _ in range(len(strs))] + for i, s in enumerate(strs): + for c in s: + arr[i][ord(c) - ord('0')] += 1 + + def dfs(i, m, n): + if i == len(strs): + return 0 + + res = dfs(i + 1, m, n) + if m >= arr[i][0] and n >= arr[i][1]: + res = max(res, 1 + dfs(i + 1, m - arr[i][0], n - arr[i][1])) + return res + + return dfs(0, m, n) +``` + +```java +public class Solution { + public int findMaxForm(String[] strs, int m, int n) { + int[][] arr = new int[strs.length][2]; + for (int i = 0; i < strs.length; i++) { + for (char c : strs[i].toCharArray()) { + arr[i][c - '0']++; + } + } + + return dfs(0, m, n, arr); + } + + private int dfs(int i, int m, int n, int[][] arr) { + if (i == arr.length) { + return 0; + } + + int res = dfs(i + 1, m, n, arr); + if (m >= arr[i][0] && n >= arr[i][1]) { + res = Math.max(res, 1 + dfs(i + 1, m - arr[i][0], n - arr[i][1], arr)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int findMaxForm(vector& strs, int m, int n) { + vector> arr(strs.size(), vector(2)); + for (int i = 0; i < strs.size(); i++) { + for (char c : strs[i]) { + arr[i][c - '0']++; + } + } + return dfs(0, m, n, arr); + } + +private: + int dfs(int i, int m, int n, vector>& arr) { + if (i == arr.size()) { + return 0; + } + + int res = dfs(i + 1, m, n, arr); + if (m >= arr[i][0] && n >= arr[i][1]) { + res = max(res, 1 + dfs(i + 1, m - arr[i][0], n - arr[i][1], arr)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} strs + * @param {number} m + * @param {number} n + * @return {number} + */ + findMaxForm(strs, m, n) { + const arr = Array.from({ length: strs.length }, () => [0, 0]); + for (let i = 0; i < strs.length; i++) { + for (const c of strs[i]) { + arr[i][c - "0"]++; + } + } + + const dfs = (i, m, n) => { + if (i === strs.length) { + return 0; + } + + let res = dfs(i + 1, m, n); + if (m >= arr[i][0] && n >= arr[i][1]) { + res = Math.max(res, 1 + dfs(i + 1, m - arr[i][0], n - arr[i][1])); + } + return res; + }; + + return dfs(0, m, n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ N)$ +* Space complexity: $O(N)$ for recursion stack. + +> Where $N$ represents the number of binary strings, and $m$ and $n$ are the maximum allowable counts of zeros and ones, respectively. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def findMaxForm(self, strs: List[str], m: int, n: int) -> int: + arr = [[0] * 2 for _ in range(len(strs))] + for i, s in enumerate(strs): + for c in s: + arr[i][ord(c) - ord('0')] += 1 + + dp = {} + + def dfs(i, m, n): + if i == len(strs): + return 0 + if m == 0 and n == 0: + return 0 + if (i, m, n) in dp: + return dp[(i, m, n)] + + res = dfs(i + 1, m, n) + if m >= arr[i][0] and n >= arr[i][1]: + res = max(res, 1 + dfs(i + 1, m - arr[i][0], n - arr[i][1])) + dp[(i, m, n)] = res + return res + + return dfs(0, m, n) +``` + +```java +public class Solution { + private int[][][] dp; + private int[][] arr; + + public int findMaxForm(String[] strs, int m, int n) { + arr = new int[strs.length][2]; + for (int i = 0; i < strs.length; i++) { + for (char c : strs[i].toCharArray()) { + arr[i][c - '0']++; + } + } + + dp = new int[strs.length][m + 1][n + 1]; + for (int i = 0; i < strs.length; i++) { + for (int j = 0; j <= m; j++) { + for (int k = 0; k <= n; k++) { + dp[i][j][k] = -1; + } + } + } + + return dfs(0, m, n); + } + + private int dfs(int i, int m, int n) { + if (i == arr.length) { + return 0; + } + if (m == 0 && n == 0) { + return 0; + } + if (dp[i][m][n] != -1) { + return dp[i][m][n]; + } + + int res = dfs(i + 1, m, n); + if (m >= arr[i][0] && n >= arr[i][1]) { + res = Math.max(res, 1 + dfs(i + 1, m - arr[i][0], n - arr[i][1])); + } + dp[i][m][n] = res; + return res; + } +} +``` + +```cpp +class Solution { +private: + vector>> dp; + vector> arr; + +public: + int findMaxForm(vector& strs, int m, int n) { + arr = vector>(strs.size(), vector(2)); + for (int i = 0; i < strs.size(); i++) { + for (char c : strs[i]) { + arr[i][c - '0']++; + } + } + + dp = vector>>(strs.size(), vector>(m + 1, vector(n + 1, -1))); + return dfs(0, m, n); + } + +private: + int dfs(int i, int m, int n) { + if (i == arr.size()) { + return 0; + } + if (m == 0 && n == 0) { + return 0; + } + if (dp[i][m][n] != -1) { + return dp[i][m][n]; + } + + int res = dfs(i + 1, m, n); + if (m >= arr[i][0] && n >= arr[i][1]) { + res = max(res, 1 + dfs(i + 1, m - arr[i][0], n - arr[i][1])); + } + dp[i][m][n] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} strs + * @param {number} m + * @param {number} n + * @return {number} + */ + findMaxForm(strs, m, n) { + const arr = Array.from({ length: strs.length }, () => [0, 0]); + for (let i = 0; i < strs.length; i++) { + for (const c of strs[i]) { + arr[i][c - "0"]++; + } + } + + const dp = Array.from({ length: strs.length }, () => + Array.from({ length: m + 1 }, () => Array(n + 1).fill(-1)) + ); + + const dfs = (i, m, n) => { + if (i === strs.length) return 0; + if (m === 0 && n === 0) return 0; + if (dp[i][m][n] !== -1) return dp[i][m][n]; + + let res = dfs(i + 1, m, n); + if (m >= arr[i][0] && n >= arr[i][1]) { + res = Math.max(res, 1 + dfs(i + 1, m - arr[i][0], n - arr[i][1])); + } + dp[i][m][n] = res; + return res; + }; + + return dfs(0, m, n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n * N)$ +* Space complexity: $O(m * n * N)$ + +> Where $N$ represents the number of binary strings, and $m$ and $n$ are the maximum allowable counts of zeros and ones, respectively. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def findMaxForm(self, strs: List[str], m: int, n: int) -> int: + arr = [[0] * 2 for _ in range(len(strs))] + for i, s in enumerate(strs): + for c in s: + arr[i][ord(c) - ord('0')] += 1 + + dp = [[[0] * (n + 1) for _ in range(m + 1)] for _ in range(len(strs) + 1)] + + for i in range(1, len(strs) + 1): + for j in range(m + 1): + for k in range(n + 1): + dp[i][j][k] = dp[i - 1][j][k] + if j >= arr[i - 1][0] and k >= arr[i - 1][1]: + dp[i][j][k] = max(dp[i][j][k], 1 + dp[i - 1][j - arr[i - 1][0]][k - arr[i - 1][1]]) + + return dp[len(strs)][m][n] +``` + +```java +public class Solution { + public int findMaxForm(String[] strs, int m, int n) { + int[][] arr = new int[strs.length][2]; + for (int i = 0; i < strs.length; i++) { + for (char c : strs[i].toCharArray()) { + arr[i][c - '0']++; + } + } + + int[][][] dp = new int[strs.length + 1][m + 1][n + 1]; + + for (int i = 1; i <= strs.length; i++) { + for (int j = 0; j <= m; j++) { + for (int k = 0; k <= n; k++) { + dp[i][j][k] = dp[i - 1][j][k]; + if (j >= arr[i - 1][0] && k >= arr[i - 1][1]) { + dp[i][j][k] = Math.max(dp[i][j][k], 1 + dp[i - 1][j - arr[i - 1][0]][k - arr[i - 1][1]]); + } + } + } + } + + return dp[strs.length][m][n]; + } +} +``` + +```cpp +class Solution { +public: + int findMaxForm(vector& strs, int m, int n) { + vector> arr(strs.size(), vector(2)); + for (int i = 0; i < strs.size(); i++) { + for (char c : strs[i]) { + arr[i][c - '0']++; + } + } + + vector>> dp(strs.size() + 1, vector>(m + 1, vector(n + 1, 0))); + + for (int i = 1; i <= strs.size(); i++) { + for (int j = 0; j <= m; j++) { + for (int k = 0; k <= n; k++) { + dp[i][j][k] = dp[i - 1][j][k]; + if (j >= arr[i - 1][0] && k >= arr[i - 1][1]) { + dp[i][j][k] = max(dp[i][j][k], 1 + dp[i - 1][j - arr[i - 1][0]][k - arr[i - 1][1]]); + } + } + } + } + + return dp[strs.size()][m][n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} strs + * @param {number} m + * @param {number} n + * @return {number} + */ + findMaxForm(strs, m, n) { + const arr = strs.map(s => { + const zeros = s.split('').filter(c => c === '0').length; + const ones = s.length - zeros; + return [zeros, ones]; + }); + + const dp = Array.from({ length: strs.length + 1 }, () => + Array.from({ length: m + 1 }, () => Array(n + 1).fill(0)) + ); + + for (let i = 1; i <= strs.length; i++) { + for (let j = 0; j <= m; j++) { + for (let k = 0; k <= n; k++) { + dp[i][j][k] = dp[i - 1][j][k]; + if (j >= arr[i - 1][0] && k >= arr[i - 1][1]) { + dp[i][j][k] = Math.max(dp[i][j][k], 1 + dp[i - 1][j - arr[i - 1][0]][k - arr[i - 1][1]]); + } + } + } + } + + return dp[strs.length][m][n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n * N)$ +* Space complexity: $O(m * n * N)$ + +> Where $N$ represents the number of binary strings, and $m$ and $n$ are the maximum allowable counts of zeros and ones, respectively. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def findMaxForm(self, strs: List[str], m: int, n: int) -> int: + arr = [[0, 0] for _ in range(len(strs))] + for i, s in enumerate(strs): + for c in s: + arr[i][ord(c) - ord('0')] += 1 + + dp = [[0] * (n + 1) for _ in range(m + 1)] + + for zeros, ones in arr: + for j in range(m, zeros - 1, -1): + for k in range(n, ones - 1, -1): + dp[j][k] = max(dp[j][k], 1 + dp[j - zeros][k - ones]) + + return dp[m][n] +``` + +```java +public class Solution { + public int findMaxForm(String[] strs, int m, int n) { + int[][] arr = new int[strs.length][2]; + for (int i = 0; i < strs.length; i++) { + for (char c : strs[i].toCharArray()) { + arr[i][c - '0']++; + } + } + + int[][] dp = new int[m + 1][n + 1]; + + for (int[] pair : arr) { + int zeros = pair[0], ones = pair[1]; + for (int j = m; j >= zeros; j--) { + for (int k = n; k >= ones; k--) { + dp[j][k] = Math.max(dp[j][k], 1 + dp[j - zeros][k - ones]); + } + } + } + + return dp[m][n]; + } +} +``` + +```cpp +class Solution { +public: + int findMaxForm(vector& strs, int m, int n) { + vector> arr(strs.size(), vector(2)); + for (int i = 0; i < strs.size(); i++) { + for (char c : strs[i]) { + arr[i][c - '0']++; + } + } + + vector> dp(m + 1, vector(n + 1, 0)); + + for (const auto& pair : arr) { + int zeros = pair[0], ones = pair[1]; + for (int j = m; j >= zeros; j--) { + for (int k = n; k >= ones; k--) { + dp[j][k] = max(dp[j][k], 1 + dp[j - zeros][k - ones]); + } + } + } + + return dp[m][n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} strs + * @param {number} m + * @param {number} n + * @return {number} + */ + findMaxForm(strs, m, n) { + const arr = Array.from({ length: strs.length }, () => [0, 0]); + for (let i = 0; i < strs.length; i++) { + for (const c of strs[i]) { + arr[i][c - "0"]++; + } + } + + const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0)); + + for (const [zeros, ones] of arr) { + for (let j = m; j >= zeros; j--) { + for (let k = n; k >= ones; k--) { + dp[j][k] = Math.max(dp[j][k], 1 + dp[j - zeros][k - ones]); + } + } + } + + return dp[m][n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n * N)$ +* Space complexity: $O(m * n + N)$ + +> Where $N$ represents the number of binary strings, and $m$ and $n$ are the maximum allowable counts of zeros and ones, respectively. \ No newline at end of file diff --git a/articles/online-stock-span.md b/articles/online-stock-span.md new file mode 100644 index 000000000..9419b89b6 --- /dev/null +++ b/articles/online-stock-span.md @@ -0,0 +1,173 @@ +## 1. Brute Force + +::tabs-start + +```python +class StockSpanner: + + def __init__(self): + self.arr = [] + + def next(self, price: int) -> int: + self.arr.append(price) + i = len(self.arr) - 2 + while i >= 0 and self.arr[i] <= price: + i -= 1 + return len(self.arr) - i - 1 +``` + +```java +public class StockSpanner { + private List arr; + + public StockSpanner() { + arr = new ArrayList<>(); + } + + public int next(int price) { + arr.add(price); + int i = arr.size() - 2; + while (i >= 0 && arr.get(i) <= price) { + i--; + } + return arr.size() - i - 1; + } +} +``` + +```cpp +class StockSpanner { + vector arr; + +public: + StockSpanner() {} + + int next(int price) { + arr.push_back(price); + int i = arr.size() - 2; + while (i >= 0 && arr[i] <= price) { + i--; + } + return arr.size() - i - 1; + } +}; +``` + +```javascript +class StockSpanner { + constructor() { + this.arr = []; + } + + /** + * @param {number} price + * @return {number} + */ + next(price) { + this.arr.push(price); + let i = this.arr.length - 2; + while (i >= 0 && this.arr[i] <= price) { + i--; + } + return this.arr.length - i - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of function calls. + +--- + +## 2. Monotonic Decreasing Stack + +::tabs-start + +```python +class StockSpanner: + + def __init__(self): + self.stack = [] # pair: (price, span) + + def next(self, price: int) -> int: + span = 1 + while self.stack and self.stack[-1][0] <= price: + span += self.stack[-1][1] + self.stack.pop() + self.stack.append((price, span)) + return span +``` + +```java +public class StockSpanner { + private Stack stack; // pair: [price, span] + + public StockSpanner() { + stack = new Stack<>(); + } + + public int next(int price) { + int span = 1; + while (!stack.isEmpty() && stack.peek()[0] <= price) { + span += stack.pop()[1]; + } + stack.push(new int[] {price, span}); + return span; + } +} +``` + +```cpp +class StockSpanner { + stack> stack; // pair: (price, span) + +public: + StockSpanner() {} + + int next(int price) { + int span = 1; + while (!stack.empty() && stack.top().first <= price) { + span += stack.top().second; + stack.pop(); + } + stack.push({price, span}); + return span; + } +}; +``` + +```javascript +class StockSpanner { + constructor() { + this.stack = []; // pair: [price, span] + } + + /** + * @param {number} price + * @return {number} + */ + next(price) { + let span = 1; + while (this.stack.length && this.stack[this.stack.length - 1][0] <= price) { + span += this.stack.pop()[1]; + } + this.stack.push([price, span]); + return span; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of function calls. \ No newline at end of file diff --git a/articles/open-the-lock.md b/articles/open-the-lock.md new file mode 100644 index 000000000..910704fd6 --- /dev/null +++ b/articles/open-the-lock.md @@ -0,0 +1,620 @@ +## 1. Breadth First Search - I + +::tabs-start + +```python +class Solution: + def openLock(self, deadends: List[str], target: str) -> int: + if "0000" in deadends: + return -1 + + def children(lock): + res = [] + for i in range(4): + digit = str((int(lock[i]) + 1) % 10) + res.append(lock[:i] + digit + lock[i+1:]) + digit = str((int(lock[i]) - 1 + 10) % 10) + res.append(lock[:i] + digit + lock[i+1:]) + return res + + q = deque([("0000", 0)]) + visit = set(deadends) + + while q: + lock, turns = q.popleft() + if lock == target: + return turns + for child in children(lock): + if child not in visit: + visit.add(child) + q.append((child, turns + 1)) + return -1 +``` + +```java +public class Solution { + public int openLock(String[] deadends, String target) { + Set visit = new HashSet<>(Arrays.asList(deadends)); + if (visit.contains("0000")) return -1; + + Queue queue = new LinkedList<>(); + queue.offer("0000"); + visit.add("0000"); + + int turns = 0; + while (!queue.isEmpty()) { + int size = queue.size(); + for (int i = 0; i < size; i++) { + String lock = queue.poll(); + if (lock.equals(target)) return turns; + + for (String next : children(lock)) { + if (!visit.contains(next)) { + queue.offer(next); + visit.add(next); + } + } + } + turns++; + } + return -1; + } + + private List children(String lock) { + List res = new ArrayList<>(); + for (int i = 0; i < 4; i++) { + char[] arr = lock.toCharArray(); + arr[i] = (char) (((arr[i] - '0' + 1) % 10) + '0'); + res.add(new String(arr)); + + arr = lock.toCharArray(); + arr[i] = (char) (((arr[i] - '0' - 1 + 10) % 10) + '0'); + res.add(new String(arr)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int openLock(vector& deadends, string target) { + unordered_set visit(deadends.begin(), deadends.end()); + if (visit.count("0000")) return -1; + + queue> q; + q.push({"0000", 0}); + visit.insert("0000"); + + while (!q.empty()) { + auto [lock, turns] = q.front(); + q.pop(); + + if (lock == target) return turns; + for (string child : children(lock)) { + if (!visit.count(child)) { + visit.insert(child); + q.push({child, turns + 1}); + } + } + } + return -1; + } + +private: + vector children(string lock) { + vector res; + for (int i = 0; i < 4; ++i) { + string next = lock; + next[i] = (next[i] - '0' + 1) % 10 + '0'; + res.push_back(next); + + next = lock; + next[i] = (next[i] - '0' - 1 + 10) % 10 + '0'; + res.push_back(next); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} deadends + * @param {string} target + * @return {number} + */ + openLock(deadends, target) { + const visit = new Set(deadends); + if (visit.has("0000")) return -1; + + const children = (lock) => { + const res = []; + for (let i = 0; i < 4; i++) { + const up = lock.slice(0, i) + ((+lock[i] + 1) % 10) + lock.slice(i + 1); + const down = lock.slice(0, i) + ((+lock[i] - 1 + 10) % 10) + lock.slice(i + 1); + res.push(up, down); + } + return res; + }; + + const queue = new Queue([["0000", 0]]); + visit.add("0000"); + + while (!queue.isEmpty()) { + const [lock, turns] = queue.pop(); + if (lock === target) return turns; + + for (const child of children(lock)) { + if (!visit.has(child)) { + visit.add(child); + queue.push([child, turns + 1]); + } + } + } + return -1; + } +} +``` + +```csharp +public class Solution { + public int OpenLock(string[] deadends, string target) { + var dead = new HashSet(deadends); + if (dead.Contains("0000")) return -1; + + List Children(string lockStr) { + var res = new List(); + for (int i = 0; i < 4; i++) { + int digit = lockStr[i] - '0'; + string up = lockStr.Substring(0, i) + ((digit + 1) % 10) + lockStr.Substring(i + 1); + string down = lockStr.Substring(0, i) + ((digit + 9) % 10) + lockStr.Substring(i + 1); + res.Add(up); + res.Add(down); + } + return res; + } + + var q = new Queue<(string, int)>(); + q.Enqueue(("0000", 0)); + var visited = new HashSet(dead); + + while (q.Count > 0) { + var (lockStr, turns) = q.Dequeue(); + if (lockStr == target) return turns; + foreach (var child in Children(lockStr)) { + if (!visited.Contains(child)) { + visited.Add(child); + q.Enqueue((child, turns + 1)); + } + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(d ^ n + m)$ +* Space complexity: $O(d ^ n)$ + +> Where $d$ is the number of digits $(0 - 9)$, $n$ is the number of wheels $(4)$, and $m$ is the number of deadends. + +--- + +## 2. Breadth First Search - II + +::tabs-start + +```python +class Solution: + def openLock(self, deadends: List[str], target: str) -> int: + if target == "0000": + return 0 + + visit = set(deadends) + if "0000" in visit: + return -1 + + q = deque(["0000"]) + visit.add("0000") + steps = 0 + + while q: + steps += 1 + for _ in range(len(q)): + lock = q.popleft() + for i in range(4): + for j in [1, -1]: + digit = str((int(lock[i]) + j + 10) % 10) + nextLock = lock[:i] + digit + lock[i+1:] + if nextLock in visit: + continue + if nextLock == target: + return steps + q.append(nextLock) + visit.add(nextLock) + return -1 +``` + +```java +public class Solution { + public int openLock(String[] deadends, String target) { + if (target.equals("0000")) return 0; + + Set visit = new HashSet<>(Arrays.asList(deadends)); + if (visit.contains("0000")) return -1; + + Queue q = new LinkedList<>(); + q.offer("0000"); + visit.add("0000"); + int steps = 0; + + while (!q.isEmpty()) { + steps++; + for (int i = q.size(); i > 0; i--) { + String lock = q.poll(); + for (int j = 0; j < 4; j++) { + for (int move : new int[]{1, -1}) { + char[] arr = lock.toCharArray(); + arr[j] = (char)((arr[j] - '0' + move + 10) % 10 + '0'); + String nextLock = new String(arr); + if (visit.contains(nextLock)) continue; + if (nextLock.equals(target)) return steps; + q.offer(nextLock); + visit.add(nextLock); + } + } + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int openLock(vector& deadends, string target) { + if (target == "0000") return 0; + + unordered_set visit(deadends.begin(), deadends.end()); + if (visit.count("0000")) return -1; + + queue q; + q.push("0000"); + visit.insert("0000"); + int steps = 0; + + while (!q.empty()) { + steps++; + for (int i = q.size(); i > 0; i--) { + string lock = q.front(); q.pop(); + for (int j = 0; j < 4; j++) { + for (int move : {1, -1}) { + string nextLock = lock; + nextLock[j] = (nextLock[j] - '0' + move + 10) % 10 + '0'; + if (visit.count(nextLock)) continue; + if (nextLock == target) return steps; + q.push(nextLock); + visit.insert(nextLock); + } + } + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} deadends + * @param {string} target + * @return {number} + */ + openLock(deadends, target) { + if (target === "0000") return 0; + + const visit = new Set(deadends); + if (visit.has("0000")) return -1; + + const q = new Queue(["0000"]); + visit.add("0000"); + let steps = 0; + + while (!q.isEmpty()) { + steps++; + for (let i = q.size(); i > 0; i--) { + const lock = q.pop(); + for (let j = 0; j < 4; j++) { + for (let move of [1, -1]) { + const digit = (parseInt(lock[j]) + move + 10) % 10; + const nextLock = lock.slice(0, j) + digit + lock.slice(j + 1); + if (visit.has(nextLock)) continue; + if (nextLock === target) return steps; + q.push(nextLock); + visit.add(nextLock); + } + } + } + } + return -1; + } +} +``` + +```csharp +public class Solution { + public int OpenLock(string[] deadends, string target) { + if (target == "0000") return 0; + + var visited = new HashSet(deadends); + if (visited.Contains("0000")) return -1; + + var q = new Queue(); + q.Enqueue("0000"); + visited.Add("0000"); + int steps = 0; + + while (q.Count > 0) { + steps++; + int size = q.Count; + for (int i = 0; i < size; i++) { + string lockStr = q.Dequeue(); + for (int j = 0; j < 4; j++) { + foreach (int move in new int[] {1, -1}) { + int digit = (lockStr[j] - '0' + move + 10) % 10; + string nextLock = lockStr.Substring(0, j) + digit.ToString() + lockStr.Substring(j + 1); + if (visited.Contains(nextLock)) continue; + if (nextLock == target) return steps; + q.Enqueue(nextLock); + visited.Add(nextLock); + } + } + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(d ^ n + m)$ +* Space complexity: $O(d ^ n)$ + +> Where $d$ is the number of digits $(0 - 9)$, $n$ is the number of wheels $(4)$, and $m$ is the number of deadends. + +--- + +## 3. Bidirectional Breadth First Search + +::tabs-start + +```python +class Solution: + def openLock(self, deadends: List[str], target: str) -> int: + if target == "0000": + return 0 + + visit = set(deadends) + if "0000" in visit: + return -1 + + begin = {"0000"} + end = {target} + steps = 0 + + while begin and end: + if len(begin) > len(end): + begin, end = end, begin + + steps += 1 + temp = set() + for lock in begin: + for i in range(4): + for j in [-1, 1]: + digit = str((int(lock[i]) + j + 10) % 10) + nextLock = lock[:i] + digit + lock[i+1:] + + if nextLock in end: + return steps + if nextLock in visit: + continue + visit.add(nextLock) + temp.add(nextLock) + begin = temp + return -1 +``` + +```java +public class Solution { + public int openLock(String[] deadends, String target) { + if (target.equals("0000")) return 0; + + Set visit = new HashSet<>(Arrays.asList(deadends)); + if (visit.contains("0000")) return -1; + + Set begin = new HashSet<>(); + begin.add("0000"); + Set end = new HashSet<>(); + end.add(target); + int steps = 0; + + while (!begin.isEmpty() && !end.isEmpty()) { + if (begin.size() > end.size()) { + Set temp = begin; + begin = end; + end = temp; + } + + steps++; + Set temp = new HashSet<>(); + for (String lock : begin) { + for (int i = 0; i < 4; i++) { + for (int j : new int[]{-1, 1}) { + char[] chars = lock.toCharArray(); + chars[i] = (char) ((chars[i] - '0' + j + 10) % 10 + '0'); + String nextLock = new String(chars); + + if (end.contains(nextLock)) return steps; + if (visit.contains(nextLock)) continue; + + visit.add(nextLock); + temp.add(nextLock); + } + } + } + begin = temp; + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int openLock(vector& deadends, string target) { + if (target == "0000") return 0; + + unordered_set visit(deadends.begin(), deadends.end()); + if (visit.count("0000")) return -1; + + unordered_set begin = {"0000"}, end = {target}; + int steps = 0; + + while (!begin.empty() && !end.empty()) { + if (begin.size() > end.size()) swap(begin, end); + steps++; + unordered_set temp; + + for (const string& lock : begin) { + for (int i = 0; i < 4; ++i) { + for (int j : {-1, 1}) { + string nextLock = lock; + nextLock[i] = (nextLock[i] - '0' + j + 10) % 10 + '0'; + + if (end.count(nextLock)) return steps; + if (visit.count(nextLock)) continue; + + visit.insert(nextLock); + temp.insert(nextLock); + } + } + } + begin = temp; + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} deadends + * @param {string} target + * @return {number} + */ + openLock(deadends, target) { + if (target === "0000") return 0; + + const visit = new Set(deadends); + if (visit.has("0000")) return -1; + + let begin = new Set(["0000"]); + let end = new Set([target]); + let steps = 0; + + while (begin.size > 0 && end.size > 0) { + if (begin.size > end.size) [begin, end] = [end, begin]; + + steps++; + const temp = new Set(); + + for (const lock of begin) { + for (let i = 0; i < 4; i++) { + for (const j of [-1, 1]) { + const digit = (parseInt(lock[i]) + j + 10) % 10; + const nextLock = lock.slice(0, i) + digit + lock.slice(i + 1); + + if (end.has(nextLock)) return steps; + if (visit.has(nextLock)) continue; + + visit.add(nextLock); + temp.add(nextLock); + } + } + } + begin = temp; + } + return -1; + } +} +``` + +```csharp +public class Solution { + public int OpenLock(string[] deadends, string target) { + if (target == "0000") return 0; + + var visit = new HashSet(deadends); + if (visit.Contains("0000")) return -1; + + var begin = new HashSet { "0000" }; + var end = new HashSet { target }; + int steps = 0; + + while (begin.Count > 0 && end.Count > 0) { + if (begin.Count > end.Count) { + var tempSet = begin; + begin = end; + end = tempSet; + } + + var temp = new HashSet(); + steps++; + + foreach (var lockStr in begin) { + for (int i = 0; i < 4; i++) { + foreach (int j in new int[] { -1, 1 }) { + int digit = (lockStr[i] - '0' + j + 10) % 10; + string nextLock = lockStr.Substring(0, i) + digit.ToString() + lockStr.Substring(i + 1); + + if (end.Contains(nextLock)) return steps; + if (visit.Contains(nextLock)) continue; + + visit.Add(nextLock); + temp.Add(nextLock); + } + } + } + + begin = temp; + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(d ^ n + m)$ +* Space complexity: $O(d ^ n)$ + +> Where $d$ is the number of digits $(0 - 9)$, $n$ is the number of wheels $(4)$, and $m$ is the number of deadends. \ No newline at end of file diff --git a/articles/operations-on-tree.md b/articles/operations-on-tree.md new file mode 100644 index 000000000..682f18874 --- /dev/null +++ b/articles/operations-on-tree.md @@ -0,0 +1,806 @@ +## 1. Depth First Search + +::tabs-start + +```python +class LockingTree: + + def __init__(self, parent: List[int]): + self.parent = parent + self.child = [[] for _ in range(len(parent))] + self.locked = [0] * len(parent) + for node in range(1, len(parent)): + self.child[parent[node]].append(node) + + def lock(self, num: int, user: int) -> bool: + if self.locked[num]: + return False + self.locked[num] = user + return True + + def unlock(self, num: int, user: int) -> bool: + if self.locked[num] != user: + return False + self.locked[num] = 0 + return True + + def upgrade(self, num: int, user: int) -> bool: + node = num + while node != -1: + if self.locked[node]: + return False + node = self.parent[node] + + def dfs(node): + lockedCount = 0 + if self.locked[node]: + lockedCount += 1 + self.locked[node] = 0 + + for nei in self.child[node]: + lockedCount += dfs(nei) + return lockedCount + + if dfs(num) > 0: + self.locked[num] = user + return True + return False +``` + +```java +public class LockingTree { + private int[] parent; + private List[] child; + private int[] locked; + + public LockingTree(int[] parent) { + this.parent = parent; + int n = parent.length; + child = new ArrayList[n]; + locked = new int[n]; + + for (int i = 0; i < n; i++) { + child[i] = new ArrayList<>(); + } + for (int node = 1; node < n; node++) { + child[parent[node]].add(node); + } + } + + public boolean lock(int num, int user) { + if (locked[num] != 0) { + return false; + } + locked[num] = user; + return true; + } + + public boolean unlock(int num, int user) { + if (locked[num] != user) { + return false; + } + locked[num] = 0; + return true; + } + + public boolean upgrade(int num, int user) { + int node = num; + while (node != -1) { + if (locked[node] != 0) { + return false; + } + node = parent[node]; + } + + int lockedCount = dfs(num); + if (lockedCount > 0) { + locked[num] = user; + return true; + } + return false; + } + + private int dfs(int node) { + int lockedCount = 0; + if (locked[node] != 0) { + lockedCount++; + locked[node] = 0; + } + for (int nei : child[node]) { + lockedCount += dfs(nei); + } + return lockedCount; + } +} +``` + +```cpp +class LockingTree { +private: + vector parent; + vector> child; + vector locked; + +public: + LockingTree(vector& parent) : parent(parent), locked(parent.size()) { + int n = parent.size(); + child.resize(n); + for (int node = 1; node < n; node++) { + child[parent[node]].push_back(node); + } + } + + bool lock(int num, int user) { + if (locked[num]) { + return false; + } + locked[num] = user; + return true; + } + + bool unlock(int num, int user) { + if (locked[num] != user) { + return false; + } + locked[num] = 0; + return true; + } + + bool upgrade(int num, int user) { + int node = num; + while (node != -1) { + if (locked[node]) { + return false; + } + node = parent[node]; + } + + int lockedCount = dfs(num); + if (lockedCount > 0) { + locked[num] = user; + return true; + } + return false; + } + +private: + int dfs(int node) { + int lockedCount = 0; + if (locked[node]) { + lockedCount++; + locked[node] = 0; + } + for (int& nei : child[node]) { + lockedCount += dfs(nei); + } + return lockedCount; + } +}; +``` + +```javascript +class LockingTree { + /** + * @constructor + * @param {number[]} parent + */ + constructor(parent) { + this.parent = parent; + this.child = Array.from({ length: parent.length }, () => []); + this.locked = new Array(parent.length).fill(0); + + for (let node = 1; node < parent.length; node++) { + this.child[parent[node]].push(node); + } + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + lock(num, user) { + if (this.locked[num] !== 0) { + return false; + } + this.locked[num] = user; + return true; + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + unlock(num, user) { + if (this.locked[num] !== user) { + return false; + } + this.locked[num] = 0; + return true; + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + upgrade(num, user) { + let node = num; + while (node !== -1) { + if (this.locked[node] !== 0) { + return false; + } + node = this.parent[node]; + } + + const dfs = (node) => { + let lockedCount = 0; + if (this.locked[node] !== 0) { + lockedCount++; + this.locked[node] = 0; + } + for (let nei of this.child[node]) { + lockedCount += dfs(nei); + } + return lockedCount; + }; + + if (dfs(num) > 0) { + this.locked[num] = user; + return true; + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(1)$ time for each $lock()$ and $unlock()$ function call. + * $O(n)$ time for each $upgrade()$ function call. +* Space complexity: $O(n)$ + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class LockingTree: + + def __init__(self, parent: List[int]): + self.parent = parent + self.child = [[] for _ in range(len(parent))] + self.locked = [0] * len(parent) + for node in range(1, len(parent)): + self.child[parent[node]].append(node) + + def lock(self, num: int, user: int) -> bool: + if self.locked[num]: + return False + self.locked[num] = user + return True + + def unlock(self, num: int, user: int) -> bool: + if self.locked[num] != user: + return False + self.locked[num] = 0 + return True + + def upgrade(self, num: int, user: int) -> bool: + node = num + while node != -1: + if self.locked[node]: + return False + node = self.parent[node] + + lockedCount, q = 0, deque([num]) + while q: + node = q.popleft() + if self.locked[node]: + self.locked[node] = 0 + lockedCount += 1 + q.extend(self.child[node]) + + if lockedCount: + self.locked[num] = user + return True + return False +``` + +```java +public class LockingTree { + private int[] parent; + private List[] child; + private int[] locked; + + public LockingTree(int[] parent) { + this.parent = parent; + int n = parent.length; + child = new ArrayList[n]; + locked = new int[n]; + + for (int i = 0; i < n; i++) { + child[i] = new ArrayList<>(); + } + for (int node = 1; node < n; node++) { + child[parent[node]].add(node); + } + } + + public boolean lock(int num, int user) { + if (locked[num] != 0) { + return false; + } + locked[num] = user; + return true; + } + + public boolean unlock(int num, int user) { + if (locked[num] != user) { + return false; + } + locked[num] = 0; + return true; + } + + public boolean upgrade(int num, int user) { + int node = num; + while (node != -1) { + if (locked[node] != 0) { + return false; + } + node = parent[node]; + } + + int lockedCount = 0; + Queue q = new LinkedList<>(); + q.offer(num); + + while (!q.isEmpty()) { + node = q.poll(); + if (locked[node] != 0) { + locked[node] = 0; + lockedCount++; + } + q.addAll(child[node]); + } + + if (lockedCount > 0) { + locked[num] = user; + return true; + } + return false; + } +} +``` + +```cpp +class LockingTree { +private: + vector parent; + vector> child; + vector locked; + +public: + LockingTree(vector& parent) : parent(parent), locked(parent.size()) { + int n = parent.size(); + child.resize(n); + for (int node = 1; node < n; node++) { + child[parent[node]].push_back(node); + } + } + + bool lock(int num, int user) { + if (locked[num]) { + return false; + } + locked[num] = user; + return true; + } + + bool unlock(int num, int user) { + if (locked[num] != user) { + return false; + } + locked[num] = 0; + return true; + } + + bool upgrade(int num, int user) { + int node = num; + while (node != -1) { + if (locked[node]) { + return false; + } + node = parent[node]; + } + + int lockedCount = 0; + queue q; + q.push(num); + + while (!q.empty()) { + node = q.front(); q.pop(); + if (locked[node]) { + locked[node] = 0; + lockedCount++; + } + for (int nei : child[node]) { + q.push(nei); + } + } + + if (lockedCount > 0) { + locked[num] = user; + return true; + } + return false; + } +}; +``` + +```javascript +class LockingTree { + /** + * @constructor + * @param {number[]} parent + */ + constructor(parent) { + this.parent = parent; + this.child = Array.from({ length: parent.length }, () => []); + this.locked = new Array(parent.length).fill(0); + + for (let node = 1; node < parent.length; node++) { + this.child[parent[node]].push(node); + } + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + lock(num, user) { + if (this.locked[num] !== 0) { + return false; + } + this.locked[num] = user; + return true; + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + unlock(num, user) { + if (this.locked[num] !== user) { + return false; + } + this.locked[num] = 0; + return true; + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + upgrade(num, user) { + let node = num; + while (node !== -1) { + if (this.locked[node] !== 0) { + return false; + } + node = this.parent[node]; + } + + let lockedCount = 0; + const q = new Queue([num]); + + while (!q.isEmpty()) { + node = q.pop(); + if (this.locked[node]) { + this.locked[node] = 0; + lockedCount++; + } + for (let nei of this.child[node]) { + q.push(nei); + } + } + + if (lockedCount > 0) { + this.locked[num] = user; + return true; + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(1)$ time for each $lock()$ and $unlock()$ function call. + * $O(n)$ time for each $upgrade()$ function call. +* Space complexity: $O(n)$ + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +class LockingTree: + + def __init__(self, parent: List[int]): + self.parent = parent + self.child = [[] for _ in range(len(parent))] + self.locked = [0] * len(parent) + for node in range(1, len(parent)): + self.child[parent[node]].append(node) + + def lock(self, num: int, user: int) -> bool: + if self.locked[num]: + return False + self.locked[num] = user + return True + + def unlock(self, num: int, user: int) -> bool: + if self.locked[num] != user: + return False + self.locked[num] = 0 + return True + + def upgrade(self, num: int, user: int) -> bool: + node = num + while node != -1: + if self.locked[node]: + return False + node = self.parent[node] + + lockedCount, stack = 0, [num] + while stack: + node = stack.pop() + if self.locked[node]: + self.locked[node] = 0 + lockedCount += 1 + stack.extend(self.child[node]) + + if lockedCount: + self.locked[num] = user + return True + return False +``` + +```java +public class LockingTree { + private int[] parent; + private List[] child; + private int[] locked; + + public LockingTree(int[] parent) { + this.parent = parent; + int n = parent.length; + child = new ArrayList[n]; + locked = new int[n]; + + for (int i = 0; i < n; i++) { + child[i] = new ArrayList<>(); + } + for (int node = 1; node < n; node++) { + child[parent[node]].add(node); + } + } + + public boolean lock(int num, int user) { + if (locked[num] != 0) { + return false; + } + locked[num] = user; + return true; + } + + public boolean unlock(int num, int user) { + if (locked[num] != user) { + return false; + } + locked[num] = 0; + return true; + } + + public boolean upgrade(int num, int user) { + int node = num; + while (node != -1) { + if (locked[node] != 0) { + return false; + } + node = parent[node]; + } + + int lockedCount = 0; + Stack stack = new Stack<>(); + stack.push(num); + + while (!stack.isEmpty()) { + node = stack.pop(); + if (locked[node] != 0) { + locked[node] = 0; + lockedCount++; + } + for (int nei : child[node]) { + stack.push(nei); + } + } + + if (lockedCount > 0) { + locked[num] = user; + return true; + } + return false; + } +} +``` + +```cpp +class LockingTree { +private: + vector parent; + vector> child; + vector locked; + +public: + LockingTree(vector& parent) : parent(parent), locked(parent.size()) { + int n = parent.size(); + child.resize(n); + for (int node = 1; node < n; node++) { + child[parent[node]].push_back(node); + } + } + + bool lock(int num, int user) { + if (locked[num]) { + return false; + } + locked[num] = user; + return true; + } + + bool unlock(int num, int user) { + if (locked[num] != user) { + return false; + } + locked[num] = 0; + return true; + } + + bool upgrade(int num, int user) { + int node = num; + while (node != -1) { + if (locked[node]) { + return false; + } + node = parent[node]; + } + + int lockedCount = 0; + stack stk; + stk.push(num); + + while (!stk.empty()) { + node = stk.top(); stk.pop(); + if (locked[node]) { + locked[node] = 0; + lockedCount++; + } + for (int& nei : child[node]) { + stk.push(nei); + } + } + + if (lockedCount > 0) { + locked[num] = user; + return true; + } + return false; + } +}; +``` + +```javascript +class LockingTree { + /** + * @constructor + * @param {number[]} parent + */ + constructor(parent) { + this.parent = parent; + this.child = Array.from({ length: parent.length }, () => []); + this.locked = new Array(parent.length).fill(0); + + for (let node = 1; node < parent.length; node++) { + this.child[parent[node]].push(node); + } + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + lock(num, user) { + if (this.locked[num] !== 0) { + return false; + } + this.locked[num] = user; + return true; + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + unlock(num, user) { + if (this.locked[num] !== user) { + return false; + } + this.locked[num] = 0; + return true; + } + + /** + * @param {number} num + * @param {number} user + * @return {boolean} + */ + upgrade(num, user) { + let node = num; + while (node !== -1) { + if (this.locked[node] !== 0) { + return false; + } + node = this.parent[node]; + } + + let lockedCount = 0; + let stack = [num]; + + while (stack.length) { + node = stack.pop(); + if (this.locked[node]) { + this.locked[node] = 0; + lockedCount++; + } + stack.push(...this.child[node]); + } + + if (lockedCount > 0) { + this.locked[num] = user; + return true; + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(1)$ time for each $lock()$ and $unlock()$ function call. + * $O(n)$ time for each $upgrade()$ function call. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/optimal-partition-of-string.md b/articles/optimal-partition-of-string.md new file mode 100644 index 000000000..c4d89ab77 --- /dev/null +++ b/articles/optimal-partition-of-string.md @@ -0,0 +1,250 @@ +## 1. Greedy (Hash Set) + +::tabs-start + +```python +class Solution: + def partitionString(self, s: str) -> int: + curSet = set() + res = 1 + for c in s: + if c in curSet: + res += 1 + curSet.clear() + curSet.add(c) + return res +``` + +```java +public class Solution { + public int partitionString(String s) { + Set curSet = new HashSet<>(); + int res = 1; + for (char c : s.toCharArray()) { + if (curSet.contains(c)) { + res++; + curSet.clear(); + } + curSet.add(c); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int partitionString(string s) { + unordered_set curSet; + int res = 1; + for (char c : s) { + if (curSet.count(c)) { + res++; + curSet.clear(); + } + curSet.insert(c); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + partitionString(s) { + let curSet = new Set(); + let res = 1; + for (let c of s) { + if (curSet.has(c)) { + res++; + curSet.clear(); + } + curSet.add(c); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +--- + +## 2. Greedy (Array) + +::tabs-start + +```python +class Solution: + def partitionString(self, s: str) -> int: + lastIdx = [-1] * 26 + res = 1 + start = 0 + for i, c in enumerate(s): + j = ord(c) - ord('a') + if lastIdx[j] >= start: + start = i + res += 1 + lastIdx[j] = i + return res +``` + +```java +public class Solution { + public int partitionString(String s) { + int[] lastIdx = new int[26]; + Arrays.fill(lastIdx, -1); + int res = 1, start = 0; + for (int i = 0; i < s.length(); i++) { + int j = s.charAt(i) - 'a'; + if (lastIdx[j] >= start) { + start = i; + res++; + } + lastIdx[j] = i; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int partitionString(string s) { + vector lastIdx(26, -1); + int res = 1, start = 0; + for (int i = 0; i < s.size(); i++) { + int j = s[i] - 'a'; + if (lastIdx[j] >= start) { + start = i; + res++; + } + lastIdx[j] = i; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + partitionString(s) { + const lastIdx = Array(26).fill(-1); + let res = 1, start = 0; + for (let i = 0; i < s.length; i++) { + const j = s.charCodeAt(i) - 97; + if (lastIdx[j] >= start) { + start = i; + res++; + } + lastIdx[j] = i; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +--- + +## 3. Greedy (Bit Mask) + +::tabs-start + +```python +class Solution: + def partitionString(self, s: str) -> int: + res = 1 + mask = 0 + for c in s: + i = ord(c) - ord('a') + if mask & (1 << i): + mask = 0 + res += 1 + mask |= (1 << i) + return res +``` + +```java +public class Solution { + public int partitionString(String s) { + int res = 1, mask = 0; + for (char c : s.toCharArray()) { + int i = c - 'a'; + if ((mask & (1 << i)) != 0) { + mask = 0; + res++; + } + mask |= (1 << i); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int partitionString(string s) { + int res = 1, mask = 0; + for (char c : s) { + int i = c - 'a'; + if (mask & (1 << i)) { + mask = 0; + res++; + } + mask |= (1 << i); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + partitionString(s) { + let res = 1, mask = 0; + for (const c of s) { + const i = c.charCodeAt(0) - 97; + if (mask & (1 << i)) { + mask = 0; + res++; + } + mask |= (1 << i); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/out-of-boundary-paths.md b/articles/out-of-boundary-paths.md new file mode 100644 index 000000000..ced7639b1 --- /dev/null +++ b/articles/out-of-boundary-paths.md @@ -0,0 +1,525 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def findPaths(self, m: int, n: int, maxMove: int, startRow: int, startColumn: int) -> int: + ROWS, COLS = m, n + MOD = 10**9 + 7 + + def dfs(r, c, moves): + if r < 0 or r >= ROWS or c < 0 or c >= COLS: + return 1 + if moves == 0: + return 0 + + return ( + dfs(r + 1, c, moves - 1) + + dfs(r - 1, c, moves - 1) + + dfs(r, c + 1, moves - 1) + + dfs(r, c - 1, moves - 1) + ) % MOD + + return dfs(startRow, startColumn, maxMove) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + private int dfs(int r, int c, int moves, int m, int n) { + if (r < 0 || r >= m || c < 0 || c >= n) return 1; + if (moves == 0) return 0; + + return ( + (dfs(r + 1, c, moves - 1, m, n) + dfs(r - 1, c, moves - 1, m, n)) % MOD + + (dfs(r, c + 1, moves - 1, m, n) + dfs(r, c - 1, moves - 1, m, n)) % MOD + ) % MOD; + } + + public int findPaths(int m, int n, int maxMove, int startRow, int startColumn) { + return dfs(startRow, startColumn, maxMove, m, n); + } +} +``` + +```cpp +class Solution { +private: + int MOD = 1'000'000'007; + + int dfs(int r, int c, int moves, int m, int n) { + if (r < 0 || r >= m || c < 0 || c >= n) return 1; + if (moves == 0) return 0; + + return ( + (dfs(r + 1, c, moves - 1, m, n) + dfs(r - 1, c, moves - 1, m, n)) % MOD + + (dfs(r, c + 1, moves - 1, m, n) + dfs(r, c - 1, moves - 1, m, n)) % MOD + ) % MOD; + } + +public: + int findPaths(int m, int n, int maxMove, int startRow, int startColumn) { + return dfs(startRow, startColumn, maxMove, m, n); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} m + * @param {number} n + * @param {number} maxMove + * @param {number} startRow + * @param {number} startColumn + * @return {number} + */ + findPaths(m, n, maxMove, startRow, startColumn) { + const MOD = 1_000_000_007; + + const dfs = (r, c, moves) => { + if (r < 0 || r >= m || c < 0 || c >= n) return 1; + if (moves === 0) return 0; + + return ( + (dfs(r + 1, c, moves - 1) + dfs(r - 1, c, moves - 1)) % MOD + + (dfs(r, c + 1, moves - 1) + dfs(r, c - 1, moves - 1)) % MOD + ) % MOD; + }; + + return dfs(startRow, startColumn, maxMove); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(4 ^ N)$ +* Space complexity: $O(N)$ + +> Where $m$ is the number of rows, $n$ is the number of columns, and $N$ is the maximum number of allowed moves. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def findPaths(self, m: int, n: int, maxMove: int, startRow: int, startColumn: int) -> int: + ROWS, COLS = m, n + MOD = 10**9 + 7 + cache = {} + + def dfs(r, c, moves): + if r < 0 or r >= ROWS or c < 0 or c >= COLS: + return 1 + if moves == 0: + return 0 + if (r, c, moves) in cache: + return cache[(r, c, moves)] + + cache[(r, c, moves)] = ( + dfs(r + 1, c, moves - 1) + + dfs(r - 1, c, moves - 1) + + dfs(r, c + 1, moves - 1) + + dfs(r, c - 1, moves - 1) + ) % MOD + return cache[(r, c, moves)] + + return dfs(startRow, startColumn, maxMove) +``` + +```java +public class Solution { + private int[][][] dp; + private static final int MOD = 1_000_000_007; + + private int dfs(int r, int c, int moves, int m, int n) { + if (r < 0 || r >= m || c < 0 || c >= n) return 1; + if (moves == 0) return 0; + if (dp[r][c][moves] != -1) return dp[r][c][moves]; + + dp[r][c][moves] = ( + (dfs(r + 1, c, moves - 1, m, n) + dfs(r - 1, c, moves - 1, m, n)) % MOD + + (dfs(r, c + 1, moves - 1, m, n) + dfs(r, c - 1, moves - 1, m, n)) % MOD + ) % MOD; + return dp[r][c][moves]; + } + + public int findPaths(int m, int n, int maxMove, int startRow, int startColumn) { + dp = new int[m][n][maxMove + 1]; + for (int[][] layer : dp) { + for (int[] row : layer) { + Arrays.fill(row, -1); + } + } + return dfs(startRow, startColumn, maxMove, m, n); + } +} +``` + +```cpp +class Solution { +private: + vector>> dp; + int MOD = 1'000'000'007; + + int dfs(int r, int c, int moves, int m, int n) { + if (r < 0 || r >= m || c < 0 || c >= n) return 1; + if (moves == 0) return 0; + if (dp[r][c][moves] != -1) return dp[r][c][moves]; + + dp[r][c][moves] = ( + (dfs(r + 1, c, moves - 1, m, n) + dfs(r - 1, c, moves - 1, m, n)) % MOD + + (dfs(r, c + 1, moves - 1, m, n) + dfs(r, c - 1, moves - 1, m, n)) % MOD + ) % MOD; + return dp[r][c][moves]; + } + +public: + int findPaths(int m, int n, int maxMove, int startRow, int startColumn) { + dp = vector>>(m, vector>(n, vector(maxMove + 1, -1))); + return dfs(startRow, startColumn, maxMove, m, n); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} m + * @param {number} n + * @param {number} maxMove + * @param {number} startRow + * @param {number} startColumn + * @return {number} + */ + findPaths(m, n, maxMove, startRow, startColumn) { + const MOD = 1_000_000_007; + const dp = Array.from({ length: m }, () => + Array.from({ length: n }, () => Array(maxMove + 1).fill(-1)) + ); + + const dfs = (r, c, moves) => { + if (r < 0 || r >= m || c < 0 || c >= n) return 1; + if (moves === 0) return 0; + if (dp[r][c][moves] !== -1) return dp[r][c][moves]; + + dp[r][c][moves] = ( + (dfs(r + 1, c, moves - 1) + dfs(r - 1, c, moves - 1)) % MOD + + (dfs(r, c + 1, moves - 1) + dfs(r, c - 1, moves - 1)) % MOD + ) % MOD; + return dp[r][c][moves]; + }; + + return dfs(startRow, startColumn, maxMove); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n * N)$ +* Space complexity: $O(m * n * N)$ + +> Where $m$ is the number of rows, $n$ is the number of columns, and $N$ is the maximum number of allowed moves. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def findPaths(self, m: int, n: int, maxMove: int, startRow: int, startColumn: int) -> int: + MOD = 10**9 + 7 + dp = [[[0] * (maxMove + 1) for _ in range(n)] for _ in range(m)] + + for moves in range(1, maxMove + 1): + for r in range(m): + for c in range(n): + dp[r][c][moves] = ( + (dp[r - 1][c][moves - 1] if r > 0 else 1) + + (dp[r + 1][c][moves - 1] if r < m - 1 else 1) + + (dp[r][c - 1][moves - 1] if c > 0 else 1) + + (dp[r][c + 1][moves - 1] if c < n - 1 else 1) + ) % MOD + + return dp[startRow][startColumn][maxMove] +``` + +```java +public class Solution { + public int findPaths(int m, int n, int maxMove, int startRow, int startColumn) { + final int MOD = 1_000_000_007; + int[][][] dp = new int[m][n][maxMove + 1]; + + for (int moves = 1; moves <= maxMove; moves++) { + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + long up = (r > 0) ? dp[r - 1][c][moves - 1] : 1; + long down = (r < m - 1) ? dp[r + 1][c][moves - 1] : 1; + long left = (c > 0) ? dp[r][c - 1][moves - 1] : 1; + long right = (c < n - 1) ? dp[r][c + 1][moves - 1] : 1; + + dp[r][c][moves] = (int) ((up + down + left + right) % MOD); + } + } + } + + return dp[startRow][startColumn][maxMove]; + } +} +``` + +```cpp +class Solution { +public: + int findPaths(int m, int n, int maxMove, int startRow, int startColumn) { + const int MOD = 1'000'000'007; + vector>> dp(m, vector>(n, vector(maxMove + 1, 0))); + + for (int moves = 1; moves <= maxMove; moves++) { + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + dp[r][c][moves] = ( + (r > 0 ? dp[r - 1][c][moves - 1] : 1) + + (r < m - 1 ? dp[r + 1][c][moves - 1] : 1) + + (c > 0 ? dp[r][c - 1][moves - 1] : 1) + + (c < n - 1 ? dp[r][c + 1][moves - 1] : 1) + ) % MOD; + } + } + } + + return dp[startRow][startColumn][maxMove]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} m + * @param {number} n + * @param {number} maxMove + * @param {number} startRow + * @param {number} startColumn + * @return {number} + */ + findPaths(m, n, maxMove, startRow, startColumn) { + const MOD = 1_000_000_007; + const dp = Array.from({ length: m }, () => + Array.from({ length: n }, () => Array(maxMove + 1).fill(0)) + ); + + for (let moves = 1; moves <= maxMove; moves++) { + for (let r = 0; r < m; r++) { + for (let c = 0; c < n; c++) { + dp[r][c][moves] = ( + (r > 0 ? dp[r - 1][c][moves - 1] : 1) + + (r < m - 1 ? dp[r + 1][c][moves - 1] : 1) + + (c > 0 ? dp[r][c - 1][moves - 1] : 1) + + (c < n - 1 ? dp[r][c + 1][moves - 1] : 1) + ) % MOD; + } + } + } + + return dp[startRow][startColumn][maxMove]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n * N)$ +* Space complexity: $O(m * n * N)$ + +> Where $m$ is the number of rows, $n$ is the number of columns, and $N$ is the maximum number of allowed moves. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def findPaths(self, m: int, n: int, maxMove: int, startRow: int, startColumn: int) -> int: + MOD = 10**9 + 7 + dp = [[0] * n for _ in range(m)] + + for moves in range(1, maxMove + 1): + tmp = [[0] * n for _ in range(m)] + for r in range(m): + for c in range(n): + if r + 1 == m: + tmp[r][c] = (tmp[r][c] + 1) % MOD + else: + tmp[r][c] = (tmp[r][c] + dp[r + 1][c]) % MOD + if r - 1 < 0: + tmp[r][c] = (tmp[r][c] + 1) % MOD + else: + tmp[r][c] = (tmp[r][c] + dp[r - 1][c]) % MOD + if c + 1 == n: + tmp[r][c] = (tmp[r][c] + 1) % MOD + else: + tmp[r][c] = (tmp[r][c] + dp[r][c + 1]) % MOD + if c - 1 < 0: + tmp[r][c] = (tmp[r][c] + 1) % MOD + else: + tmp[r][c] = (tmp[r][c] + dp[r][c - 1]) % MOD + dp = tmp + + return dp[startRow][startColumn] +``` + +```java +public class Solution { + public int findPaths(int m, int n, int maxMove, int startRow, int startColumn) { + final int MOD = 1_000_000_007; + int[][] dp = new int[m][n]; + + for (int moves = 1; moves <= maxMove; moves++) { + int[][] tmp = new int[m][n]; + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + if (r + 1 == m) { + tmp[r][c] = (tmp[r][c] + 1) % MOD; + } else { + tmp[r][c] = (tmp[r][c] + dp[r + 1][c]) % MOD; + } + if (r - 1 < 0) { + tmp[r][c] = (tmp[r][c] + 1) % MOD; + } else { + tmp[r][c] = (tmp[r][c] + dp[r - 1][c]) % MOD; + } + if (c + 1 == n) { + tmp[r][c] = (tmp[r][c] + 1) % MOD; + } else { + tmp[r][c] = (tmp[r][c] + dp[r][c + 1]) % MOD; + } + if (c - 1 < 0) { + tmp[r][c] = (tmp[r][c] + 1) % MOD; + } else { + tmp[r][c] = (tmp[r][c] + dp[r][c - 1]) % MOD; + } + } + } + dp = tmp; + } + + return dp[startRow][startColumn]; + } +} +``` + +```cpp +class Solution { +public: + int findPaths(int m, int n, int maxMove, int startRow, int startColumn) { + const int MOD = 1'000'000'007; + vector> dp(m, vector(n, 0)); + + for (int moves = 1; moves <= maxMove; moves++) { + vector> tmp(m, vector(n, 0)); + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + if (r + 1 == m) { + tmp[r][c] = (tmp[r][c] + 1) % MOD; + } else { + tmp[r][c] = (tmp[r][c] + dp[r + 1][c]) % MOD; + } + if (r - 1 < 0) { + tmp[r][c] = (tmp[r][c] + 1) % MOD; + } else { + tmp[r][c] = (tmp[r][c] + dp[r - 1][c]) % MOD; + } + if (c + 1 == n) { + tmp[r][c] = (tmp[r][c] + 1) % MOD; + } else { + tmp[r][c] = (tmp[r][c] + dp[r][c + 1]) % MOD; + } + if (c - 1 < 0) { + tmp[r][c] = (tmp[r][c] + 1) % MOD; + } else { + tmp[r][c] = (tmp[r][c] + dp[r][c - 1]) % MOD; + } + } + } + dp = tmp; + } + + return dp[startRow][startColumn]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} m + * @param {number} n + * @param {number} maxMove + * @param {number} startRow + * @param {number} startColumn + * @return {number} + */ + findPaths(m, n, maxMove, startRow, startColumn) { + const MOD = 1_000_000_007; + let dp = Array.from({ length: m }, () => Array(n).fill(0)); + + for (let moves = 1; moves <= maxMove; moves++) { + const tmp = Array.from({ length: m }, () => Array(n).fill(0)); + for (let r = 0; r < m; r++) { + for (let c = 0; c < n; c++) { + if (r + 1 === m) { + tmp[r][c] = (tmp[r][c] + 1) % MOD; + } else { + tmp[r][c] = (tmp[r][c] + dp[r + 1][c]) % MOD; + } + if (r - 1 < 0) { + tmp[r][c] = (tmp[r][c] + 1) % MOD; + } else { + tmp[r][c] = (tmp[r][c] + dp[r - 1][c]) % MOD; + } + if (c + 1 === n) { + tmp[r][c] = (tmp[r][c] + 1) % MOD; + } else { + tmp[r][c] = (tmp[r][c] + dp[r][c + 1]) % MOD; + } + if (c - 1 < 0) { + tmp[r][c] = (tmp[r][c] + 1) % MOD; + } else { + tmp[r][c] = (tmp[r][c] + dp[r][c - 1]) % MOD; + } + } + } + dp = tmp; + } + + return dp[startRow][startColumn]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n * N)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows, $n$ is the number of columns, and $N$ is the maximum number of allowed moves. \ No newline at end of file diff --git a/articles/pacific-atlantic-water-flow.md b/articles/pacific-atlantic-water-flow.md new file mode 100644 index 000000000..1245a570d --- /dev/null +++ b/articles/pacific-atlantic-water-flow.md @@ -0,0 +1,1224 @@ +## 1. Brute Force (Backtracking) + +::tabs-start + +```python +class Solution: + def pacificAtlantic(self, heights: List[List[int]]) -> List[List[int]]: + ROWS, COLS = len(heights), len(heights[0]) + directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + + pacific = atlantic = False + + def dfs(r, c, prevVal): + nonlocal pacific, atlantic + if r < 0 or c < 0: + pacific = True + return + if r >= ROWS or c >= COLS: + atlantic = True + return + if heights[r][c] > prevVal: + return + + tmp = heights[r][c] + heights[r][c] = float('inf') + for dx, dy in directions: + dfs(r + dx, c + dy, tmp) + if pacific and atlantic: + break + heights[r][c] = tmp + + res = [] + for r in range(ROWS): + for c in range(COLS): + pacific = False + atlantic = False + dfs(r, c, float('inf')) + if pacific and atlantic: + res.append([r, c]) + return res +``` + +```java +public class Solution { + int ROWS, COLS; + boolean pacific, atlantic; + int[][] directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + + public List> pacificAtlantic(int[][] heights) { + ROWS = heights.length; + COLS = heights[0].length; + List> res = new ArrayList<>(); + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + pacific = false; + atlantic = false; + dfs(heights, r, c, Integer.MAX_VALUE); + if (pacific && atlantic) { + res.add(Arrays.asList(r, c)); + } + } + } + return res; + } + + private void dfs(int[][] heights, int r, int c, int prevVal) { + if (r < 0 || c < 0) { + pacific = true; + return; + } + if (r >= ROWS || c >= COLS) { + atlantic = true; + return; + } + if (heights[r][c] > prevVal) { + return; + } + + int tmp = heights[r][c]; + heights[r][c] = Integer.MAX_VALUE; + for (int[] dir : directions) { + dfs(heights, r + dir[0], c + dir[1], tmp); + if (pacific && atlantic) { + break; + } + } + heights[r][c] = tmp; + } +} +``` + +```cpp +class Solution { +public: + int ROWS, COLS; + bool pacific, atlantic; + vector> directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + + vector> pacificAtlantic(vector>& heights) { + ROWS = heights.size(); + COLS = heights[0].size(); + vector> res; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + pacific = false; + atlantic = false; + dfs(heights, r, c, INT_MAX); + if (pacific && atlantic) { + res.push_back({r, c}); + } + } + } + + return res; + } + + void dfs(vector>& heights, int r, int c, int prevVal) { + if (r < 0 || c < 0) { + pacific = true; + return; + } + if (r >= ROWS || c >= COLS) { + atlantic = true; + return; + } + if (heights[r][c] > prevVal) { + return; + } + + int tmp = heights[r][c]; + heights[r][c] = INT_MAX; + for (auto& dir : directions) { + dfs(heights, r + dir[0], c + dir[1], tmp); + if (pacific && atlantic) { + break; + } + } + heights[r][c] = tmp; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} heights + * @return {number[][]} + */ + pacificAtlantic(heights) { + let ROWS = heights.length, COLS = heights[0].length; + let directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]; + + let pacific = false, atlantic = false; + + const dfs = (r, c, prevVal) => { + if (r < 0 || c < 0) { + pacific = true; + return; + } + if (r >= ROWS || c >= COLS) { + atlantic = true; + return; + } + if (heights[r][c] > prevVal) { + return; + } + + let tmp = heights[r][c]; + heights[r][c] = Infinity; + for (let [dx, dy] of directions) { + dfs(r + dx, c + dy, tmp); + if (pacific && atlantic) { + break; + } + } + heights[r][c] = tmp; + }; + + let res = []; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + pacific = false; + atlantic = false; + dfs(r, c, Infinity); + if (pacific && atlantic) { + res.push([r, c]); + } + } + } + + return res; + } +} +``` + +```csharp +public class Solution { + int ROWS, COLS; + bool pacific, atlantic; + int[][] directions = new int[][] { + new int[] {1, 0}, new int[] {-1, 0}, new int[] {0, 1}, new int[] {0, -1} + }; + + public List> PacificAtlantic(int[][] heights) { + ROWS = heights.Length; + COLS = heights[0].Length; + List> res = new List>(); + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + pacific = false; + atlantic = false; + Dfs(heights, r, c, int.MaxValue); + if (pacific && atlantic) { + res.Add(new List{r, c}); + } + } + } + return res; + } + + private void Dfs(int[][] heights, int r, int c, int prevVal) { + if (r < 0 || c < 0) { + pacific = true; + return; + } + if (r >= ROWS || c >= COLS) { + atlantic = true; + return; + } + if (heights[r][c] > prevVal) { + return; + } + + int tmp = heights[r][c]; + heights[r][c] = int.MaxValue; + foreach (var dir in directions) { + Dfs(heights, r + dir[0], c + dir[1], tmp); + if (pacific && atlantic) { + break; + } + } + heights[r][c] = tmp; + } +} +``` + +```go +func pacificAtlantic(heights [][]int) [][]int { + rows, cols := len(heights), len(heights[0]) + directions := [][]int{{1, 0}, {-1, 0}, {0, 1}, {0, -1}} + result := make([][]int, 0) + + var pacific, atlantic bool + + var dfs func(r, c int, prevVal int) + dfs = func(r, c int, prevVal int) { + if r < 0 || c < 0 { + pacific = true + return + } + if r >= rows || c >= cols { + atlantic = true + return + } + if heights[r][c] > prevVal { + return + } + + tmp := heights[r][c] + heights[r][c] = int(^uint(0) >> 1) + + for _, dir := range directions { + dfs(r + dir[0], c + dir[1], tmp) + if pacific && atlantic { + break + } + } + heights[r][c] = tmp + } + + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + pacific = false + atlantic = false + dfs(r, c, int(^uint(0) >> 1)) + if pacific && atlantic { + result = append(result, []int{r, c}) + } + } + } + + return result +} +``` + +```kotlin +class Solution { + private var pacific = false + private var atlantic = false + private val directions = arrayOf( + intArrayOf(1, 0), + intArrayOf(-1, 0), + intArrayOf(0, 1), + intArrayOf(0, -1) + ) + + fun pacificAtlantic(heights: Array): List> { + val rows = heights.size + val cols = heights[0].size + val result = mutableListOf>() + + fun dfs(r: Int, c: Int, prevVal: Int) { + when { + r < 0 || c < 0 -> { + pacific = true + return + } + r >= rows || c >= cols -> { + atlantic = true + return + } + heights[r][c] > prevVal -> return + } + + val tmp = heights[r][c] + heights[r][c] = Int.MAX_VALUE + + for (dir in directions) { + dfs(r + dir[0], c + dir[1], tmp) + if (pacific && atlantic) break + } + heights[r][c] = tmp + } + + for (r in 0 until rows) { + for (c in 0 until cols) { + pacific = false + atlantic = false + dfs(r, c, Int.MAX_VALUE) + if (pacific && atlantic) { + result.add(listOf(r, c)) + } + } + } + + return result + } +} +``` + +```swift +class Solution { + func pacificAtlantic(_ heights: [[Int]]) -> [[Int]] { + var heights = heights + let ROWS = heights.count + let COLS = heights[0].count + let directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + + var pacific = false + var atlantic = false + + func dfs(_ r: Int, _ c: Int, _ prevVal: Int) { + if r < 0 || c < 0 { + pacific = true + return + } + if r >= ROWS || c >= COLS { + atlantic = true + return + } + if heights[r][c] > prevVal { + return + } + + let tmp = heights[r][c] + heights[r][c] = Int.max + for dir in directions { + dfs(r + dir.0, c + dir.1, tmp) + if pacific && atlantic { + break + } + } + heights[r][c] = tmp + } + + var res = [[Int]]() + for r in 0.. Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 2. Depth First Search + +::tabs-start + +```python +class Solution: + def pacificAtlantic(self, heights: List[List[int]]) -> List[List[int]]: + ROWS, COLS = len(heights), len(heights[0]) + pac, atl = set(), set() + + def dfs(r, c, visit, prevHeight): + if ((r, c) in visit or + r < 0 or c < 0 or + r == ROWS or c == COLS or + heights[r][c] < prevHeight + ): + return + visit.add((r, c)) + dfs(r + 1, c, visit, heights[r][c]) + dfs(r - 1, c, visit, heights[r][c]) + dfs(r, c + 1, visit, heights[r][c]) + dfs(r, c - 1, visit, heights[r][c]) + + for c in range(COLS): + dfs(0, c, pac, heights[0][c]) + dfs(ROWS - 1, c, atl, heights[ROWS - 1][c]) + + for r in range(ROWS): + dfs(r, 0, pac, heights[r][0]) + dfs(r, COLS - 1, atl, heights[r][COLS - 1]) + + res = [] + for r in range(ROWS): + for c in range(COLS): + if (r, c) in pac and (r, c) in atl: + res.append([r, c]) + return res +``` + +```java +public class Solution { + private int[][] directions = {{1, 0}, {-1, 0}, + {0, 1}, {0, -1}}; + public List> pacificAtlantic(int[][] heights) { + int ROWS = heights.length, COLS = heights[0].length; + boolean[][] pac = new boolean[ROWS][COLS]; + boolean[][] atl = new boolean[ROWS][COLS]; + + for (int c = 0; c < COLS; c++) { + dfs(0, c, pac, heights); + dfs(ROWS - 1, c, atl, heights); + } + for (int r = 0; r < ROWS; r++) { + dfs(r, 0, pac, heights); + dfs(r, COLS - 1, atl, heights); + } + + List> res = new ArrayList<>(); + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (pac[r][c] && atl[r][c]) { + res.add(Arrays.asList(r, c)); + } + } + } + return res; + } + + private void dfs(int r, int c, boolean[][] ocean, int[][] heights) { + ocean[r][c] = true; + for (int[] d : directions) { + int nr = r + d[0], nc = c + d[1]; + if (nr >= 0 && nr < heights.length && + nc >= 0 && nc < heights[0].length && + !ocean[nr][nc] && heights[nr][nc] >= heights[r][c]) { + dfs(nr, nc, ocean, heights); + } + } + } +} +``` + +```cpp +class Solution { + vector> directions = {{1, 0}, {-1, 0}, + {0, 1}, {0, -1}}; +public: + vector> pacificAtlantic(vector>& heights) { + int ROWS = heights.size(), COLS = heights[0].size(); + vector> pac(ROWS, vector(COLS, false)); + vector> atl(ROWS, vector(COLS, false)); + + for (int c = 0; c < COLS; ++c) { + dfs(0, c, pac, heights); + dfs(ROWS - 1, c, atl, heights); + } + for (int r = 0; r < ROWS; ++r) { + dfs(r, 0, pac, heights); + dfs(r, COLS - 1, atl, heights); + } + + vector> res; + for (int r = 0; r < ROWS; ++r) { + for (int c = 0; c < COLS; ++c) { + if (pac[r][c] && atl[r][c]) { + res.push_back({r, c}); + } + } + } + return res; + } + +private: + void dfs(int r, int c, vector>& ocean, vector>& heights) { + ocean[r][c] = true; + for (auto [dr, dc] : directions) { + int nr = r + dr, nc = c + dc; + if (nr >= 0 && nr < heights.size() && + nc >= 0 && nc < heights[0].size() && + !ocean[nr][nc] && heights[nr][nc] >= heights[r][c]) { + dfs(nr, nc, ocean, heights); + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} heights + * @return {number[][]} + */ + pacificAtlantic(heights) { + let ROWS = heights.length, COLS = heights[0].length; + let pac = Array.from({ length: ROWS }, () => + Array(COLS).fill(false)); + let atl = Array.from({ length: ROWS }, () => + Array(COLS).fill(false)); + + const dfs = (r, c, ocean) => { + ocean[r][c] = true; + let directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]; + for (let [dr, dc] of directions) { + let nr = r + dr, nc = c + dc; + if (nr >= 0 && nr < ROWS && nc >= 0 && + nc < COLS && !ocean[nr][nc] && + heights[nr][nc] >= heights[r][c]) { + dfs(nr, nc, ocean); + } + } + } + + for (let c = 0; c < COLS; c++) { + dfs(0, c, pac); + dfs(ROWS - 1, c, atl); + } + for (let r = 0; r < ROWS; r++) { + dfs(r, 0, pac); + dfs(r, COLS - 1, atl); + } + + let res = []; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (pac[r][c] && atl[r][c]) { + res.push([r, c]); + } + } + } + return res; + } +} +``` + +```csharp +public class Solution { + private int[][] directions = new int[][] { + new int[] { 1, 0 }, new int[] { -1, 0 }, + new int[] { 0, 1 }, new int[] { 0, -1 } + }; + public List> PacificAtlantic(int[][] heights) { + int ROWS = heights.Length, COLS = heights[0].Length; + bool[,] pac = new bool[ROWS, COLS]; + bool[,] atl = new bool[ROWS, COLS]; + + for (int c = 0; c < COLS; c++) { + Dfs(0, c, pac, heights); + Dfs(ROWS - 1, c, atl, heights); + } + for (int r = 0; r < ROWS; r++) { + Dfs(r, 0, pac, heights); + Dfs(r, COLS - 1, atl, heights); + } + + List> res = new List>(); + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (pac[r, c] && atl[r, c]) { + res.Add(new List { r, c }); + } + } + } + return res; + } + + private void Dfs(int r, int c, bool[,] ocean, int[][] heights) { + ocean[r, c] = true; + foreach (var dir in directions) { + int nr = r + dir[0], nc = c + dir[1]; + if (nr >= 0 && nr < heights.Length && nc >= 0 && + nc < heights[0].Length && !ocean[nr, nc] && + heights[nr][nc] >= heights[r][c]) { + Dfs(nr, nc, ocean, heights); + } + } + } +} +``` + +```go +func pacificAtlantic(heights [][]int) [][]int { + rows, cols := len(heights), len(heights[0]) + pac := make(map[[2]int]bool) + atl := make(map[[2]int]bool) + + var dfs func(r, c int, visit map[[2]int]bool, prevHeight int) + dfs = func(r, c int, visit map[[2]int]bool, prevHeight int) { + coord := [2]int{r, c} + if visit[coord] || + r < 0 || c < 0 || + r == rows || c == cols || + heights[r][c] < prevHeight { + return + } + + visit[coord] = true + + dfs(r+1, c, visit, heights[r][c]) + dfs(r-1, c, visit, heights[r][c]) + dfs(r, c+1, visit, heights[r][c]) + dfs(r, c-1, visit, heights[r][c]) + } + + for c := 0; c < cols; c++ { + dfs(0, c, pac, heights[0][c]) + dfs(rows-1, c, atl, heights[rows-1][c]) + } + + for r := 0; r < rows; r++ { + dfs(r, 0, pac, heights[r][0]) + dfs(r, cols-1, atl, heights[r][cols-1]) + } + + result := make([][]int, 0) + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + coord := [2]int{r, c} + if pac[coord] && atl[coord] { + result = append(result, []int{r, c}) + } + } + } + + return result +} +``` + +```kotlin +class Solution { + fun pacificAtlantic(heights: Array): List> { + val rows = heights.size + val cols = heights[0].size + val pac = HashSet>() + val atl = HashSet>() + + fun dfs(r: Int, c: Int, visit: HashSet>, prevHeight: Int) { + val coord = r to c + if (coord in visit || + r < 0 || c < 0 || + r == rows || c == cols || + heights[r][c] < prevHeight + ) { + return + } + + visit.add(coord) + + dfs(r + 1, c, visit, heights[r][c]) + dfs(r - 1, c, visit, heights[r][c]) + dfs(r, c + 1, visit, heights[r][c]) + dfs(r, c - 1, visit, heights[r][c]) + } + + for (c in 0 until cols) { + dfs(0, c, pac, heights[0][c]) + dfs(rows - 1, c, atl, heights[rows - 1][c]) + } + + for (r in 0 until rows) { + dfs(r, 0, pac, heights[r][0]) + dfs(r, cols - 1, atl, heights[r][cols - 1]) + } + + return (0 until rows).flatMap { r -> + (0 until cols).mapNotNull { c -> + if ((r to c) in pac && (r to c) in atl) { + listOf(r, c) + } else null + } + } + } +} +``` + +```swift +class Solution { + func pacificAtlantic(_ heights: [[Int]]) -> [[Int]] { + let ROWS = heights.count + let COLS = heights[0].count + var pac = Set<[Int]>() + var atl = Set<[Int]>() + + func dfs(_ r: Int, _ c: Int, _ visit: inout Set<[Int]>, _ prevHeight: Int) { + if (visit.contains([r, c]) || r < 0 || c < 0 || r == ROWS || + c == COLS || heights[r][c] < prevHeight) { + return + } + visit.insert([r, c]) + dfs(r + 1, c, &visit, heights[r][c]) + dfs(r - 1, c, &visit, heights[r][c]) + dfs(r, c + 1, &visit, heights[r][c]) + dfs(r, c - 1, &visit, heights[r][c]) + } + + for c in 0.. Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 3. Breadth First Search + +::tabs-start + +```python +class Solution: + def pacificAtlantic(self, heights: List[List[int]]) -> List[List[int]]: + ROWS, COLS = len(heights), len(heights[0]) + directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + pac = [[False] * COLS for _ in range(ROWS)] + atl = [[False] * COLS for _ in range(ROWS)] + + def bfs(source, ocean): + q = deque(source) + while q: + r, c = q.popleft() + ocean[r][c] = True + for dr, dc in directions: + nr, nc = r + dr, c + dc + if (0 <= nr < ROWS and 0 <= nc < COLS and + not ocean[nr][nc] and + heights[nr][nc] >= heights[r][c] + ): + q.append((nr, nc)) + + pacific = [] + atlantic = [] + for c in range(COLS): + pacific.append((0, c)) + atlantic.append((ROWS - 1, c)) + + for r in range(ROWS): + pacific.append((r, 0)) + atlantic.append((r, COLS - 1)) + + bfs(pacific, pac) + bfs(atlantic, atl) + + res = [] + for r in range(ROWS): + for c in range(COLS): + if pac[r][c] and atl[r][c]: + res.append([r, c]) + return res +``` + +```java +public class Solution { + private int[][] directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + public List> pacificAtlantic(int[][] heights) { + int ROWS = heights.length, COLS = heights[0].length; + boolean[][] pac = new boolean[ROWS][COLS]; + boolean[][] atl = new boolean[ROWS][COLS]; + + Queue pacQueue = new LinkedList<>(); + Queue atlQueue = new LinkedList<>(); + + for (int c = 0; c < COLS; c++) { + pacQueue.add(new int[]{0, c}); + atlQueue.add(new int[]{ROWS - 1, c}); + } + for (int r = 0; r < ROWS; r++) { + pacQueue.add(new int[]{r, 0}); + atlQueue.add(new int[]{r, COLS - 1}); + } + + bfs(pacQueue, pac, heights); + bfs(atlQueue, atl, heights); + + List> res = new ArrayList<>(); + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (pac[r][c] && atl[r][c]) { + res.add(Arrays.asList(r, c)); + } + } + } + return res; + } + + private void bfs(Queue q, boolean[][] ocean, int[][] heights) { + while (!q.isEmpty()) { + int[] cur = q.poll(); + int r = cur[0], c = cur[1]; + ocean[r][c] = true; + for (int[] d : directions) { + int nr = r + d[0], nc = c + d[1]; + if (nr >= 0 && nr < heights.length && nc >= 0 && + nc < heights[0].length && !ocean[nr][nc] && + heights[nr][nc] >= heights[r][c]) { + q.add(new int[]{nr, nc}); + } + } + } + } +} +``` + +```cpp +class Solution { + vector> directions = {{1, 0}, {-1, 0}, + {0, 1}, {0, -1}}; +public: + vector> pacificAtlantic(vector>& heights) { + int ROWS = heights.size(), COLS = heights[0].size(); + vector> pac(ROWS, vector(COLS, false)); + vector> atl(ROWS, vector(COLS, false)); + + queue> pacQueue, atlQueue; + + for (int c = 0; c < COLS; ++c) { + pacQueue.push({0, c}); + atlQueue.push({ROWS - 1, c}); + } + for (int r = 0; r < ROWS; ++r) { + pacQueue.push({r, 0}); + atlQueue.push({r, COLS - 1}); + } + + bfs(pacQueue, pac, heights); + bfs(atlQueue, atl, heights); + + vector> res; + for (int r = 0; r < ROWS; ++r) { + for (int c = 0; c < COLS; ++c) { + if (pac[r][c] && atl[r][c]) { + res.push_back({r, c}); + } + } + } + return res; + } + +private: + void bfs(queue>& q, vector>& ocean, + vector>& heights) { + while (!q.empty()) { + auto [r, c] = q.front(); q.pop(); + ocean[r][c] = true; + for (auto [dr, dc] : directions) { + int nr = r + dr, nc = c + dc; + if (nr >= 0 && nr < heights.size() && nc >= 0 && + nc < heights[0].size() && !ocean[nr][nc] && + heights[nr][nc] >= heights[r][c]) { + q.push({nr, nc}); + } + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} heights + * @return {number[][]} + */ + pacificAtlantic(heights) { + let ROWS = heights.length, COLS = heights[0].length; + let directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]; + let pac = Array.from({ length: ROWS }, () => + Array(COLS).fill(false)); + let atl = Array.from({ length: ROWS }, () => + Array(COLS).fill(false)); + + let pacQueue = new Queue() + let atlQueue = new Queue(); + for (let c = 0; c < COLS; c++) { + pacQueue.push([0, c]); + atlQueue.push([ROWS - 1, c]); + } + for (let r = 0; r < ROWS; r++) { + pacQueue.push([r, 0]); + atlQueue.push([r, COLS - 1]); + } + + const bfs = (queue, ocean, heights) => { + while (!queue.isEmpty()) { + let [r, c] = queue.pop(); + ocean[r][c] = true; + for (let [dr, dc] of directions) { + let nr = r + dr, nc = c + dc; + if (nr >= 0 && nr < ROWS && nc >= 0 && + nc < COLS && !ocean[nr][nc] && + heights[nr][nc] >= heights[r][c]) { + queue.push([nr, nc]); + } + } + } + } + bfs(pacQueue, pac, heights); + bfs(atlQueue, atl, heights); + + let res = []; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (pac[r][c] && atl[r][c]) { + res.push([r, c]); + } + } + } + return res; + } +} +``` + +```csharp +public class Solution { + private int[][] directions = new int[][] { + new int[] { 1, 0 }, new int[] { -1, 0 }, + new int[] { 0, 1 }, new int[] { 0, -1 } + }; + public List> PacificAtlantic(int[][] heights) { + int ROWS = heights.Length, COLS = heights[0].Length; + bool[,] pac = new bool[ROWS, COLS]; + bool[,] atl = new bool[ROWS, COLS]; + + Queue pacQueue = new Queue(); + Queue atlQueue = new Queue(); + + for (int c = 0; c < COLS; c++) { + pacQueue.Enqueue(new int[] { 0, c }); + atlQueue.Enqueue(new int[] { ROWS - 1, c }); + } + for (int r = 0; r < ROWS; r++) { + pacQueue.Enqueue(new int[] { r, 0 }); + atlQueue.Enqueue(new int[] { r, COLS - 1 }); + } + + Bfs(pacQueue, pac, heights); + Bfs(atlQueue, atl, heights); + + List> res = new List>(); + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (pac[r, c] && atl[r, c]) { + res.Add(new List { r, c }); + } + } + } + return res; + } + + private void Bfs(Queue q, bool[,] ocean, int[][] heights) { + while (q.Count > 0) { + int[] cur = q.Dequeue(); + int r = cur[0], c = cur[1]; + ocean[r, c] = true; + foreach (var dir in directions) { + int nr = r + dir[0], nc = c + dir[1]; + if (nr >= 0 && nr < heights.Length && nc >= 0 && + nc < heights[0].Length && !ocean[nr, nc] && + heights[nr][nc] >= heights[r][c]) { + q.Enqueue(new int[] { nr, nc }); + } + } + } + } +} +``` + +```go +func pacificAtlantic(heights [][]int) [][]int { + rows, cols := len(heights), len(heights[0]) + directions := [][]int{{1, 0}, {-1, 0}, {0, 1}, {0, -1}} + + pac := make([][]bool, rows) + atl := make([][]bool, rows) + for i := range pac { + pac[i] = make([]bool, cols) + atl[i] = make([]bool, cols) + } + + bfs := func(source [][2]int, ocean [][]bool) { + q := list.New() + for _, s := range source { + q.PushBack(s) + } + for q.Len() > 0 { + element := q.Front() + q.Remove(element) + r, c := element.Value.([2]int)[0], element.Value.([2]int)[1] + ocean[r][c] = true + for _, dir := range directions { + nr, nc := r+dir[0], c+dir[1] + if nr >= 0 && nr < rows && nc >= 0 && nc < cols && + !ocean[nr][nc] && heights[nr][nc] >= heights[r][c] { + q.PushBack([2]int{nr, nc}) + } + } + } + } + + pacific := [][2]int{} + atlantic := [][2]int{} + for c := 0; c < cols; c++ { + pacific = append(pacific, [2]int{0, c}) + atlantic = append(atlantic, [2]int{rows - 1, c}) + } + for r := 0; r < rows; r++ { + pacific = append(pacific, [2]int{r, 0}) + atlantic = append(atlantic, [2]int{r, cols - 1}) + } + + bfs(pacific, pac) + bfs(atlantic, atl) + + res := [][]int{} + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + if pac[r][c] && atl[r][c] { + res = append(res, []int{r, c}) + } + } + } + return res +} +``` + +```kotlin +class Solution { + fun pacificAtlantic(heights: Array): List> { + val rows = heights.size + val cols = heights[0].size + val directions = arrayOf(intArrayOf(1, 0), + intArrayOf(-1, 0), + intArrayOf(0, 1), + intArrayOf(0, -1)) + + val pac = Array(rows) { BooleanArray(cols) } + val atl = Array(rows) { BooleanArray(cols) } + + fun bfs(source: List, ocean: Array) { + val q = LinkedList() + q.addAll(source) + while (q.isNotEmpty()) { + val (r, c) = q.poll() + ocean[r][c] = true + for (dir in directions) { + val nr = r + dir[0] + val nc = c + dir[1] + if (nr in 0 until rows && nc in 0 until cols && + !ocean[nr][nc] && heights[nr][nc] >= heights[r][c]) { + q.offer(intArrayOf(nr, nc)) + } + } + } + } + + val pacific = mutableListOf() + val atlantic = mutableListOf() + for (c in 0 until cols) { + pacific.add(intArrayOf(0, c)) + atlantic.add(intArrayOf(rows - 1, c)) + } + for (r in 0 until rows) { + pacific.add(intArrayOf(r, 0)) + atlantic.add(intArrayOf(r, cols - 1)) + } + + bfs(pacific, pac) + bfs(atlantic, atl) + + val result = mutableListOf>() + for (r in 0 until rows) { + for (c in 0 until cols) { + if (pac[r][c] && atl[r][c]) { + result.add(listOf(r, c)) + } + } + } + return result + } +} +``` + +```swift +class Solution { + func pacificAtlantic(_ heights: [[Int]]) -> [[Int]] { + let ROWS = heights.count + let COLS = heights[0].count + let directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + + var pac = Array(repeating: Array(repeating: false, count: COLS), count: ROWS) + var atl = Array(repeating: Array(repeating: false, count: COLS), count: ROWS) + + func bfs(_ source: [(Int, Int)], _ ocean: inout [[Bool]]) { + var queue = Deque(source) + while !queue.isEmpty { + let (r, c) = queue.popFirst()! + ocean[r][c] = true + for dir in directions { + let nr = r + dir.0 + let nc = c + dir.1 + if nr >= 0, nr < ROWS, nc >= 0, nc < COLS, + !ocean[nr][nc], heights[nr][nc] >= heights[r][c] { + queue.append((nr, nc)) + } + } + } + } + + var pacific: [(Int, Int)] = [] + var atlantic: [(Int, Int)] = [] + + for c in 0.. Where $m$ is the number of rows and $n$ is the number of columns. \ No newline at end of file diff --git a/articles/paint-house.md b/articles/paint-house.md new file mode 100644 index 000000000..91b9896cd --- /dev/null +++ b/articles/paint-house.md @@ -0,0 +1,445 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def min_cost(self, costs: List[List[int]]) -> int: + n = len(costs) + + def dfs(i, prevColor): + if i == n: + return 0 + + res = float("inf") + for c in range(3): + if c == prevColor: + continue + res = min(res, costs[i][c] + dfs(i + 1, c)) + return res + + return dfs(0, -1) +``` + +```java +public class Solution { + private int[][] costs; + private int n; + + public int minCost(int[][] costs) { + this.costs = costs; + this.n = costs.length; + return dfs(0, -1); + } + + private int dfs(int i, int prevColor) { + if (i == n) { + return 0; + } + + int res = Integer.MAX_VALUE; + for (int c = 0; c < 3; c++) { + if (c == prevColor) { + continue; + } + res = Math.min(res, costs[i][c] + dfs(i + 1, c)); + } + return res; + } +} +``` + +```cpp +class Solution { +private: + vector> costs; + int n; + + int dfs(int i, int prevColor) { + if (i == n) { + return 0; + } + + int res = INT_MAX; + for (int c = 0; c < 3; c++) { + if (c == prevColor) { + continue; + } + res = min(res, costs[i][c] + dfs(i + 1, c)); + } + return res; + } + +public: + int minCost(vector>& costs) { + this->costs = costs; + this->n = costs.size(); + return dfs(0, -1); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + minCost(costs) { + const n = costs.length; + + const dfs = (i, prevColor) => { + if (i === n) return 0; + + let res = Infinity; + for (let c = 0; c < 3; c++) { + if (c === prevColor) continue; + res = Math.min(res, costs[i][c] + dfs(i + 1, c)); + } + return res; + }; + + return dfs(0, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def min_cost(self, costs: List[List[int]]) -> int: + n = len(costs) + dp = [[-1] * 4 for _ in range(n)] + + def dfs(i, prevColor): + if i == n: + return 0 + if dp[i][prevColor + 1] != -1: + return dp[i][prevColor + 1] + + res = float("inf") + for c in range(3): + if c == prevColor: + continue + res = min(res, costs[i][c] + dfs(i + 1, c)) + + dp[i][prevColor + 1] = res + return res + + return dfs(0, -1) +``` + +```java +public class Solution { + private int[][] dp; + private int[][] costs; + + public int minCost(int[][] costs) { + int n = costs.length; + this.costs = costs; + this.dp = new int[n][4]; + + for (int i = 0; i < n; i++) { + Arrays.fill(dp[i], -1); + } + + return dfs(0, -1); + } + + private int dfs(int i, int prevColor) { + if (i == costs.length) { + return 0; + } + if (dp[i][prevColor + 1] != -1) { + return dp[i][prevColor + 1]; + } + + int res = Integer.MAX_VALUE; + for (int c = 0; c < 3; c++) { + if (c == prevColor) continue; + res = Math.min(res, costs[i][c] + dfs(i + 1, c)); + } + + return dp[i][prevColor + 1] = res; + } +} +``` + +```cpp +class Solution { +public: + vector> dp; + vector> costs; + + int minCost(vector>& costs) { + int n = costs.size(); + this->costs = costs; + dp.assign(n, vector(4, -1)); + return dfs(0, -1); + } + +private: + int dfs(int i, int prevColor) { + if (i == costs.size()) { + return 0; + } + if (dp[i][prevColor + 1] != -1) { + return dp[i][prevColor + 1]; + } + + int res = INT_MAX; + for (int c = 0; c < 3; c++) { + if (c == prevColor) continue; + res = min(res, costs[i][c] + dfs(i + 1, c)); + } + + return dp[i][prevColor + 1] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + minCost(costs) { + const n = costs.length; + const dp = Array.from({ length: n }, () => Array(4).fill(-1)); + + const dfs = (i, prevColor) => { + if (i === n) { + return 0; + } + if (dp[i][prevColor + 1] !== -1) { + return dp[i][prevColor + 1]; + } + + let res = Infinity; + for (let c = 0; c < 3; c++) { + if (c === prevColor) continue; + res = Math.min(res, costs[i][c] + dfs(i + 1, c)); + } + + return (dp[i][prevColor + 1] = res); + }; + + return dfs(0, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def min_cost(self, costs: List[List[int]]) -> int: + n = len(costs) + if n == 0: + return 0 + + dp = [[0] * 3 for _ in range(n)] + for c in range(3): + dp[0][c] = costs[0][c] + + for i in range(1, n): + for c in range(3): + dp[i][c] = ( + costs[i][c] + + min(dp[i - 1][(c + 1) % 3], dp[i - 1][(c + 2) % 3]) + ) + + return min(dp[n - 1]) +``` + +```java +public class Solution { + public int minCost(int[][] costs) { + int n = costs.length; + if (n == 0) return 0; + + int[][] dp = new int[n][3]; + for (int c = 0; c < 3; c++) { + dp[0][c] = costs[0][c]; + } + + for (int i = 1; i < n; i++) { + for (int c = 0; c < 3; c++) { + dp[i][c] = costs[i][c] + + Math.min(dp[i - 1][(c + 1) % 3], dp[i - 1][(c + 2) % 3]); + } + } + + return Math.min(dp[n - 1][0], Math.min(dp[n - 1][1], dp[n - 1][2])); + } +} +``` + +```cpp +class Solution { +public: + int minCost(vector>& costs) { + int n = costs.size(); + if (n == 0) return 0; + + vector> dp(n, vector(3, 0)); + for (int c = 0; c < 3; c++) { + dp[0][c] = costs[0][c]; + } + + for (int i = 1; i < n; i++) { + for (int c = 0; c < 3; c++) { + dp[i][c] = costs[i][c] + + min(dp[i - 1][(c + 1) % 3], dp[i - 1][(c + 2) % 3]); + } + } + + return min({dp[n - 1][0], dp[n - 1][1], dp[n - 1][2]}); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + minCost(costs) { + let n = costs.length; + if (n === 0) return 0; + + let dp = Array.from({ length: n }, () => Array(3).fill(0)); + for (let c = 0; c < 3; c++) { + dp[0][c] = costs[0][c]; + } + + for (let i = 1; i < n; i++) { + for (let c = 0; c < 3; c++) { + dp[i][c] = costs[i][c] + + Math.min(dp[i - 1][(c + 1) % 3], dp[i - 1][(c + 2) % 3]); + } + } + + return Math.min(dp[n - 1][0], dp[n - 1][1], dp[n - 1][2]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def minCost(self, costs: List[List[int]]) -> int: + dp = [0, 0, 0] + + for i in range(len(costs)): + dp0 = costs[i][0] + min(dp[1], dp[2]) + dp1 = costs[i][1] + min(dp[0], dp[2]) + dp2 = costs[i][2] + min(dp[0], dp[1]) + dp = [dp0, dp1, dp2] + + return min(dp) +``` + +```java +public class Solution { + public int minCost(int[][] costs) { + int dp0 = 0, dp1 = 0, dp2 = 0; + + for (int i = 0; i < costs.length; i++) { + int newDp0 = costs[i][0] + Math.min(dp1, dp2); + int newDp1 = costs[i][1] + Math.min(dp0, dp2); + int newDp2 = costs[i][2] + Math.min(dp0, dp1); + dp0 = newDp0; + dp1 = newDp1; + dp2 = newDp2; + } + + return Math.min(dp0, Math.min(dp1, dp2)); + } +} +``` + +```cpp +class Solution { +public: + int minCost(vector>& costs) { + int dp0 = 0, dp1 = 0, dp2 = 0; + + for (const auto& cost : costs) { + int newDp0 = cost[0] + min(dp1, dp2); + int newDp1 = cost[1] + min(dp0, dp2); + int newDp2 = cost[2] + min(dp0, dp1); + dp0 = newDp0; + dp1 = newDp1; + dp2 = newDp2; + } + + return min({dp0, dp1, dp2}); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + minCost(costs) { + let dp = [0, 0, 0]; + + for (let i = 0; i < costs.length; i++) { + let dp0 = costs[i][0] + Math.min(dp[1], dp[2]); + let dp1 = costs[i][1] + Math.min(dp[0], dp[2]); + let dp2 = costs[i][2] + Math.min(dp[0], dp[1]); + dp = [dp0, dp1, dp2]; + } + + return Math.min(dp[0], dp[1], dp[2]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/painting-the-walls.md b/articles/painting-the-walls.md new file mode 100644 index 000000000..1942d0042 --- /dev/null +++ b/articles/painting-the-walls.md @@ -0,0 +1,430 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def paintWalls(self, cost: List[int], time: List[int]) -> int: + + def dfs(i, remain): + if remain <= 0: + return 0 + if i == len(cost): + return float("inf") + + paint = cost[i] + dfs(i + 1, remain - 1 - time[i]) + skip = dfs(i + 1, remain) + return min(paint, skip) + + return dfs(0, len(cost)) +``` + +```java +public class Solution { + public int paintWalls(int[] cost, int[] time) { + return dfs(cost, time, 0, cost.length); + } + + private int dfs(int[] cost, int[] time, int i, int remain) { + if (remain <= 0) { + return 0; + } + if (i == cost.length) { + return Integer.MAX_VALUE; + } + + int paint = dfs(cost, time, i + 1, remain - 1 - time[i]); + if (paint != Integer.MAX_VALUE) paint += cost[i]; + int skip = dfs(cost, time, i + 1, remain); + return Math.min(paint, skip); + } +} +``` + +```cpp +class Solution { +public: + int paintWalls(vector& cost, vector& time) { + return dfs(cost, time, 0, cost.size()); + } + +private: + int dfs(vector& cost, vector& time, int i, int remain) { + if (remain <= 0) { + return 0; + } + if (i == cost.size()) { + return INT_MAX; + } + + int paint = dfs(cost, time, i + 1, remain - 1 - time[i]); + if (paint != INT_MAX) paint += cost[i]; + + int skip = dfs(cost, time, i + 1, remain); + return min(paint, skip); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} cost + * @param {number[]} time + * @return {number} + */ + paintWalls(cost, time) { + const dfs = (i, remain) => { + if (remain <= 0) { + return 0; + } + if (i === cost.length) { + return Infinity; + } + + const paint = cost[i] + dfs(i + 1, remain - 1 - time[i]); + const skip = dfs(i + 1, remain); + return Math.min(paint, skip); + }; + + return dfs(0, cost.length); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def paintWalls(self, cost: List[int], time: List[int]) -> int: + dp = {} + + def dfs(i, remain): + if remain <= 0: + return 0 + if i == len(cost): + return float("inf") + if (i, remain) in dp: + return dp[(i, remain)] + + paint = cost[i] + dfs(i + 1, remain - 1 - time[i]) + skip = dfs(i + 1, remain) + dp[(i, remain)] = min(paint, skip) + return dp[(i, remain)] + + return dfs(0, len(cost)) +``` + +```java +public class Solution { + private int[][] dp; + + public int paintWalls(int[] cost, int[] time) { + dp = new int[cost.length][cost.length + 1]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + return dfs(cost, time, 0, cost.length); + } + + private int dfs(int[] cost, int[] time, int i, int remain) { + if (remain <= 0) { + return 0; + } + if (i == cost.length) { + return Integer.MAX_VALUE; + } + if (dp[i][remain] != -1) { + return dp[i][remain]; + } + + int paint = dfs(cost, time, i + 1, remain - 1 - time[i]); + if (paint != Integer.MAX_VALUE) paint += cost[i]; + + int skip = dfs(cost, time, i + 1, remain); + return dp[i][remain] = Math.min(paint, skip); + } +} +``` + +```cpp +class Solution { + vector> dp; + +public: + int paintWalls(vector& cost, vector& time) { + dp.assign(cost.size(), vector(cost.size() + 1, -1)); + return dfs(cost, time, 0, cost.size()); + } + +private: + int dfs(vector& cost, vector& time, int i, int remain) { + if (remain <= 0) { + return 0; + } + if (i == cost.size()) { + return INT_MAX; + } + if (dp[i][remain] != -1) { + return dp[i][remain]; + } + + int paint = dfs(cost, time, i + 1, remain - 1 - time[i]); + if (paint != INT_MAX) paint += cost[i]; + + int skip = dfs(cost, time, i + 1, remain); + return dp[i][remain] = min(paint, skip); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} cost + * @param {number[]} time + * @return {number} + */ + paintWalls(cost, time) { + const n = cost.length; + const dp = Array.from({ length: n }, () => Array(n + 1).fill(-1)); + const dfs = (i, remain) => { + if (remain <= 0) { + return 0; + } + if (i === n) { + return Infinity; + } + if (dp[i][remain] !== -1) { + return dp[i][remain]; + } + + const paint = cost[i] + dfs(i + 1, remain - 1 - time[i]); + const skip = dfs(i + 1, remain); + return dp[i][remain] = Math.min(paint, skip); + }; + + return dfs(0, cost.length); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def paintWalls(self, cost: List[int], time: List[int]) -> int: + n = len(cost) + dp = [[0] * (n + 2) for _ in range(n + 1)] + for remain in range(1, n + 1): + dp[n][remain] = float("inf") + + for i in range(n - 1, -1, -1): + for remain in range(1, n + 1): + paint = cost[i] + dp[i + 1][max(remain - 1 - time[i], 0)] + skip = dp[i + 1][remain] + dp[i][remain] = min(paint, skip) + + return dp[0][n] +``` + +```java +public class Solution { + public int paintWalls(int[] cost, int[] time) { + int n = cost.length; + int[][] dp = new int[n + 1][n + 2]; + for (int remain = 1; remain <= n; remain++) { + dp[n][remain] = Integer.MAX_VALUE; + } + + for (int i = n - 1; i >= 0; i--) { + for (int remain = 1; remain <= n; remain++) { + int paint = dp[i + 1][Math.max(remain - 1 - time[i], 0)]; + if (paint != Integer.MAX_VALUE) paint += cost[i]; + + int skip = dp[i + 1][remain]; + dp[i][remain] = Math.min(paint, skip); + } + } + + return dp[0][n]; + } +} +``` + +```cpp +class Solution { +public: + int paintWalls(vector& cost, vector& time) { + int n = cost.size(); + vector> dp(n + 1, vector(n + 2, 0)); + + for (int remain = 1; remain <= n; remain++) { + dp[n][remain] = INT_MAX; + } + + for (int i = n - 1; i >= 0; i--) { + for (int remain = 1; remain <= n; remain++) { + int paint = dp[i + 1][max(remain - 1 - time[i], 0)]; + if (paint != INT_MAX) paint += cost[i]; + + int skip = dp[i + 1][remain]; + dp[i][remain] = min(paint, skip); + } + } + + return dp[0][n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} cost + * @param {number[]} time + * @return {number} + */ + paintWalls(cost, time) { + const n = cost.length; + const dp = Array.from({ length: n + 1 }, () => Array(n + 2).fill(0)); + + for (let remain = 1; remain <= n; remain++) { + dp[n][remain] = Infinity; + } + + for (let i = n - 1; i >= 0; i--) { + for (let remain = 1; remain <= n; remain++) { + const paint = cost[i] + dp[i + 1][Math.max(remain - 1 - time[i], 0)]; + const skip = dp[i + 1][remain]; + dp[i][remain] = Math.min(paint, skip); + } + } + + return dp[0][n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def paintWalls(self, cost: List[int], time: List[int]) -> int: + n = len(cost) + dp = [float("inf")] * (n + 2) + dp[0] = 0 + + for i in range(n): + for remain in range(n, 0, -1): + paint = cost[i] + dp[max(remain - 1 - time[i], 0)] + dp[remain] = min(paint, dp[remain]) + + return dp[n] +``` + +```java +public class Solution { + public int paintWalls(int[] cost, int[] time) { + int n = cost.length; + int[] dp = new int[n + 2]; + Arrays.fill(dp, Integer.MAX_VALUE); + dp[0] = 0; + + for (int i = 0; i < n; i++) { + for (int remain = n; remain > 0; remain--) { + int paint = dp[Math.max(remain - 1 - time[i], 0)]; + if (paint != Integer.MAX_VALUE) paint += cost[i]; + dp[remain] = Math.min(paint, dp[remain]); + } + } + + return dp[n]; + } +} +``` + +```cpp +class Solution { +public: + int paintWalls(vector& cost, vector& time) { + int n = cost.size(); + vector dp(n + 2, INT_MAX); + dp[0] = 0; + + for (int i = 0; i < n; i++) { + for (int remain = n; remain > 0; remain--) { + int paint = dp[max(remain - 1 - time[i], 0)]; + if (paint != INT_MAX) paint += cost[i]; + dp[remain] = min(paint, dp[remain]); + } + } + + return dp[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} cost + * @param {number[]} time + * @return {number} + */ + paintWalls(cost, time) { + const n = cost.length; + const dp = Array(n + 2).fill(Infinity); + dp[0] = 0; + + for (let i = 0; i < n; i++) { + for (let remain = n; remain > 0; remain--) { + const paint = cost[i] + dp[Math.max(remain - 1 - time[i], 0)]; + dp[remain] = Math.min(paint, dp[remain]); + } + } + + return dp[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/palindrome-linked-list.md b/articles/palindrome-linked-list.md new file mode 100644 index 000000000..f4f73b7bc --- /dev/null +++ b/articles/palindrome-linked-list.md @@ -0,0 +1,600 @@ +## 1. Convert To Array + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def isPalindrome(self, head: Optional[ListNode]) -> bool: + arr = [] + cur = head + while cur: + arr.append(cur.val) + cur = cur.next + + l, r = 0, len(arr) - 1 + while l < r: + if arr[l] != arr[r]: + return False + l, r = l + 1, r - 1 + + return True +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public boolean isPalindrome(ListNode head) { + List arr = new ArrayList<>(); + ListNode cur = head; + while (cur != null) { + arr.add(cur.val); + cur = cur.next; + } + + int l = 0, r = arr.size() - 1; + while (l < r) { + if (!arr.get(l).equals(arr.get(r))) { + return false; + } + l++; + r--; + } + + return true; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + bool isPalindrome(ListNode* head) { + std::vector arr; + ListNode* cur = head; + while (cur) { + arr.push_back(cur->val); + cur = cur->next; + } + + int l = 0, r = arr.size() - 1; + while (l < r) { + if (arr[l] != arr[r]) { + return false; + } + l++; + r--; + } + + return true; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {boolean} + */ + isPalindrome(head) { + const arr = []; + let cur = head; + + while (cur) { + arr.push(cur.val); + cur = cur.next; + } + + let l = 0, r = arr.length - 1; + while (l < r) { + if (arr[l] !== arr[r]) { + return false; + } + l++; + r--; + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Recursion + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def isPalindrome(self, head: Optional[ListNode]) -> bool: + self.cur = head + + def rec(node): + if node is not None: + if not rec(node.next): + return False + if self.cur.val != node.val: + return False + self.cur = self.cur.next + return True + + return rec(head) +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + private ListNode cur; + + public boolean isPalindrome(ListNode head) { + cur = head; + return rec(head); + } + + private boolean rec(ListNode node) { + if (node != null) { + if (!rec(node.next)) { + return false; + } + if (cur.val != node.val) { + return false; + } + cur = cur.next; + } + return true; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { + ListNode* cur; + + bool rec(ListNode* node) { + if (node != nullptr) { + if (!rec(node->next)) { + return false; + } + if (cur->val != node->val) { + return false; + } + cur = cur->next; + } + return true; + } + +public: + bool isPalindrome(ListNode* head) { + cur = head; + return rec(head); + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {boolean} + */ + isPalindrome(head) { + let cur = head; + + const rec = (node) => { + if (node !== null) { + if (!rec(node.next)) { + return false; + } + if (cur.val !== node.val) { + return false; + } + cur = cur.next; + } + return true; + }; + + return rec(head); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Stack + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def isPalindrome(self, head: Optional[ListNode]) -> bool: + stack = [] + cur = head + + while cur: + stack.append(cur.val) + cur = cur.next + + cur = head + while cur and cur.val == stack.pop(): + cur = cur.next + + return not cur +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public boolean isPalindrome(ListNode head) { + Stack stack = new Stack<>(); + ListNode cur = head; + + while (cur != null) { + stack.push(cur.val); + cur = cur.next; + } + + cur = head; + while (cur != null && cur.val == stack.pop()) { + cur = cur.next; + } + + return cur == null; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + bool isPalindrome(ListNode* head) { + stack stack; + ListNode* cur = head; + + while (cur != nullptr) { + stack.push(cur->val); + cur = cur->next; + } + + cur = head; + while (cur != nullptr && cur->val == stack.top()) { + stack.pop(); + cur = cur->next; + } + + return cur == nullptr; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {boolean} + */ + isPalindrome(head) { + const stack = []; + let cur = head; + + while (cur) { + stack.push(cur.val); + cur = cur.next; + } + + cur = head; + while (cur && cur.val === stack.pop()) { + cur = cur.next; + } + + return cur === null; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Fast & Slow Pointers + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def isPalindrome(self, head: ListNode) -> bool: + fast = head + slow = head + + # find middle (slow) + while fast and fast.next: + fast = fast.next.next + slow = slow.next + + # reverse second half + prev = None + while slow: + tmp = slow.next + slow.next = prev + prev = slow + slow = tmp + + # check palindrome + left, right = head, prev + while right: + if left.val != right.val: + return False + left = left.next + right = right.next + + return True +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public boolean isPalindrome(ListNode head) { + ListNode fast = head, slow = head; + + // find middle (slow) + while (fast != null && fast.next != null) { + fast = fast.next.next; + slow = slow.next; + } + + // reverse second half + ListNode prev = null; + while (slow != null) { + ListNode tmp = slow.next; + slow.next = prev; + prev = slow; + slow = tmp; + } + + // check palindrome + ListNode left = head, right = prev; + while (right != null) { + if (left.val != right.val) { + return false; + } + left = left.next; + right = right.next; + } + + return true; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + bool isPalindrome(ListNode* head) { + ListNode *fast = head, *slow = head; + + // find middle (slow) + while (fast && fast->next) { + fast = fast->next->next; + slow = slow->next; + } + + // reverse second half + ListNode *prev = nullptr; + while (slow) { + ListNode *tmp = slow->next; + slow->next = prev; + prev = slow; + slow = tmp; + } + + // check palindrome + ListNode *left = head, *right = prev; + while (right) { + if (left->val != right->val) { + return false; + } + left = left->next; + right = right->next; + } + + return true; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {boolean} + */ + isPalindrome(head) { + let fast = head, slow = head; + + // find middle (slow) + while (fast && fast.next) { + fast = fast.next.next; + slow = slow.next; + } + + // reverse second half + let prev = null; + while (slow) { + let tmp = slow.next; + slow.next = prev; + prev = slow; + slow = tmp; + } + + // check palindrome + let left = head, right = prev; + while (right) { + if (left.val !== right.val) { + return false; + } + left = left.next; + right = right.next; + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/palindrome-number.md b/articles/palindrome-number.md new file mode 100644 index 000000000..77f44aad6 --- /dev/null +++ b/articles/palindrome-number.md @@ -0,0 +1,420 @@ +## 1. Convert to String + +::tabs-start + +```python +class Solution: + def isPalindrome(self, x: int) -> bool: + s = str(x) + return s == s[::-1] +``` + +```java +public class Solution { + public boolean isPalindrome(int x) { + String s = String.valueOf(x); + return s.equals(new StringBuilder(s).reverse().toString()); + } +} +``` + +```cpp +class Solution { +public: + bool isPalindrome(int x) { + string s = to_string(x); + string rev = s; + reverse(rev.begin(), rev.end()); + return s == rev; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @return {boolean} + */ + isPalindrome(x) { + const s = String(x); + return s === s.split('').reverse().join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of digits in the given integer. + +--- + +## 2. Convert to String (Optimal) + +::tabs-start + +```python +class Solution: + def isPalindrome(self, x: int) -> bool: + s = str(x) + n = len(s) + for i in range(n // 2): + if s[i] != s[n - i - 1]: + return False + return True +``` + +```java +public class Solution { + public boolean isPalindrome(int x) { + String s = String.valueOf(x); + int n = s.length(); + for (int i = 0; i < n / 2; i++) { + if (s.charAt(i) != s.charAt(n - i - 1)) { + return false; + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isPalindrome(int x) { + string s = to_string(x); + int n = s.length(); + for (int i = 0; i < n / 2; i++) { + if (s[i] != s[n - i - 1]) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @return {boolean} + */ + isPalindrome(x) { + const s = String(x); + let n = s.length; + for (let i = 0; i < (n >> 1); i++) { + if (s.charAt(i) != s.charAt(n - i - 1)) { + return false; + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of digits in the given integer. + +--- + +## 3. Reverse the Integer + +::tabs-start + +```python +class Solution: + def isPalindrome(self, x: int) -> bool: + if x < 0: + return False + + rev = 0 + num = x + while num: + rev = (rev * 10) + (num % 10) + num //= 10 + + return rev == x +``` + +```java +public class Solution { + public boolean isPalindrome(int x) { + if (x < 0) { + return false; + } + + long rev = 0, num = x; + while (num != 0) { + rev = (rev * 10) + (num % 10); + num /= 10; + } + + return rev == x; + } +} +``` + +```cpp +class Solution { +public: + bool isPalindrome(int x) { + if (x < 0) { + return false; + } + + long long rev = 0, num = x; + while (num != 0) { + rev = (rev * 10) + (num % 10); + num /= 10; + } + + return rev == x; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @return {boolean} + */ + isPalindrome(x) { + if (x < 0) { + return false; + } + + let rev = 0, num = x; + while (num !== 0) { + rev = (rev * 10) + (num % 10); + num = Math.floor(num / 10); + } + + return rev === x; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +> Where $n$ is the number of digits in the given integer. + +--- + +## 4. Two Pointers + +::tabs-start + +```python +class Solution: + def isPalindrome(self, x: int) -> bool: + if x < 0: + return False + + div = 1 + while x >= 10 * div: + div *= 10 + + while x: + if x // div != x % 10: + return False + x = (x % div) // 10 + div //= 100 + + return True +``` + +```java +public class Solution { + public boolean isPalindrome(int x) { + if (x < 0) { + return false; + } + + long div = 1; + while (x >= 10 * div) { + div *= 10; + } + + while (x != 0) { + if (x / div != x % 10) { + return false; + } + x = (int) (x % div) / 10; + div /= 100; + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isPalindrome(int x) { + if (x < 0) { + return false; + } + + long long div = 1; + while (x >= 10 * div) { + div *= 10; + } + + while (x != 0) { + if (x / div != x % 10) { + return false; + } + x = (x % div) / 10; + div /= 100; + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @return {boolean} + */ + isPalindrome(x) { + if (x < 0) { + return false; + } + + let div = 1; + while (x >= 10 * div) { + div *= 10; + } + + while (x !== 0) { + if (Math.floor(x / div) !== x % 10) { + return false; + } + x = Math.floor((x % div) / 10); + div = Math.floor(div / 100); + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +> Where $n$ is the number of digits in the given integer. + +--- + +## 5. Reverse Half of the Number + +::tabs-start + +```python +class Solution: + def isPalindrome(self, x: int) -> bool: + if x < 0 or (x != 0 and x % 10 == 0): + return False + + rev = 0 + while x > rev: + rev = (rev * 10) + (x % 10) + x //= 10 + + return x == rev or x == rev // 10 +``` + +```java +public class Solution { + public boolean isPalindrome(int x) { + if (x < 0 || (x != 0 && x % 10 == 0)) { + return false; + } + + int rev = 0; + while (x > rev) { + rev = rev * 10 + x % 10; + x /= 10; + } + + return x == rev || x == rev / 10; + } +} +``` + +```cpp +class Solution { +public: + bool isPalindrome(int x) { + if (x < 0 || (x != 0 && x % 10 == 0)) { + return false; + } + + int rev = 0; + while (x > rev) { + rev = rev * 10 + x % 10; + x /= 10; + } + + return x == rev || x == rev / 10; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @return {boolean} + */ + isPalindrome(x) { + if (x < 0 || (x !== 0 && x % 10 === 0)) { + return false; + } + + let rev = 0; + while (x > rev) { + rev = rev * 10 + (x % 10); + x = Math.floor(x / 10); + } + + return x === rev || x === Math.floor(rev / 10); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +> Where $n$ is the number of digits in the given integer. \ No newline at end of file diff --git a/articles/palindrome-partitioning.md b/articles/palindrome-partitioning.md new file mode 100644 index 000000000..3e87916a5 --- /dev/null +++ b/articles/palindrome-partitioning.md @@ -0,0 +1,1267 @@ +## 1. Backtracking - I + +::tabs-start + +```python +class Solution: + def partition(self, s: str) -> List[List[str]]: + res, part = [], [] + + def dfs(j, i): + if i >= len(s): + if i == j: + res.append(part.copy()) + return + + if self.isPali(s, j, i): + part.append(s[j : i + 1]) + dfs(i + 1, i + 1) + part.pop() + + dfs(j, i + 1) + + dfs(0, 0) + return res + + def isPali(self, s, l, r): + while l < r: + if s[l] != s[r]: + return False + l, r = l + 1, r - 1 + return True +``` + +```java +public class Solution { + private List> res = new ArrayList<>(); + private List part = new ArrayList<>(); + + public List> partition(String s) { + dfs(0, 0, s); + return res; + } + + private void dfs(int j, int i, String s) { + if (i >= s.length()) { + if (i == j) { + res.add(new ArrayList<>(part)); + } + return; + } + + if (isPali(s, j, i)) { + part.add(s.substring(j, i + 1)); + dfs(i + 1, i + 1, s); + part.remove(part.size() - 1); + } + + dfs(j, i + 1, s); + } + + private boolean isPali(String s, int l, int r) { + while (l < r) { + if (s.charAt(l) != s.charAt(r)) { + return false; + } + l++; + r--; + } + return true; + } +} +``` + +```cpp +class Solution { + vector> res; +public: + vector> partition(string s) { + vector part; + dfs(0, 0, s, part); + return res; + } + + void dfs(int j, int i, string &s, vector &part) { + if (i >= s.size()) { + if (i == j) { + res.push_back(part); + } + return; + } + + if (isPali(s, j, i)) { + part.push_back(s.substr(j, i - j + 1)); + dfs(i + 1, i + 1, s, part); + part.pop_back(); + } + + dfs(j, i + 1, s, part); + } + + bool isPali(string &s, int l, int r) { + while (l < r) { + if (s[l] != s[r]) { + return false; + } + l++; + r--; + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string[][]} + */ + partition(s) { + const res = []; + const part = []; + + const dfs = (j, i) => { + if (i >= s.length) { + if (i === j) { + res.push([...part]); + } + return; + } + + if (this.isPali(s, j, i)) { + part.push(s.substring(j, i + 1)); + dfs(i + 1, i + 1); + part.pop(); + } + + dfs(j, i + 1); + }; + + dfs(0, 0); + return res; + } + + /** + * @param {string} s + * @param {number} l + * @param {number} r + * @return {boolean} + */ + isPali(s, l, r) { + while (l < r) { + if (s[l] !== s[r]) { + return false; + } + l++; + r--; + } + return true; + } +} +``` + +```csharp +public class Solution { + private List> res = new List>(); + private List part = new List(); + + public List> Partition(string s) { + dfs(0, 0, s); + return res; + } + + private void dfs(int j, int i, string s) { + if (i >= s.Length) { + if (i == j) { + res.Add(new List(part)); + } + return; + } + + if (isPali(s, j, i)) { + part.Add(s.Substring(j, i - j + 1)); + dfs(i + 1, i + 1, s); + part.RemoveAt(part.Count - 1); + } + + dfs(j, i + 1, s); + } + + private bool isPali(string s, int l, int r) { + while (l < r) { + if (s[l] != s[r]) { + return false; + } + l++; + r--; + } + return true; + } +} +``` + +```go +func partition(s string) [][]string { + res := [][]string{} + part := []string{} + + var dfs func(j, i int) + dfs = func(j, i int) { + if i >= len(s) { + if i == j { + res = append(res, append([]string{}, part...)) + } + return + } + + if isPali(s, j, i) { + part = append(part, s[j:i+1]) + dfs(i+1, i+1) + part = part[:len(part)-1] + } + + dfs(j, i+1) + } + + dfs(0, 0) + return res +} + +func isPali(s string, l, r int) bool { + for l < r { + if s[l] != s[r] { + return false + } + l++ + r-- + } + return true +} +``` + +```kotlin +class Solution { + fun partition(s: String): List> { + val res = mutableListOf>() + val part = mutableListOf() + + fun dfs(j: Int, i: Int) { + if (i >= s.length) { + if (i == j) { + res.add(part.toList()) + } + return + } + + if (isPali(s, j, i)) { + part.add(s.substring(j, i + 1)) + dfs(i + 1, i + 1) + part.removeAt(part.size - 1) + } + + dfs(j, i + 1) + } + + dfs(0, 0) + return res + } + + private fun isPali(s: String, l: Int, r: Int): Boolean { + var left = l + var right = r + while (left < right) { + if (s[left] != s[right]) { + return false + } + left++ + right-- + } + return true + } +} +``` + +```swift +class Solution { + func partition(_ s: String) -> [[String]] { + var res = [[String]]() + var part = [String]() + let sArray = Array(s) + + func dfs(_ j: Int, _ i: Int) { + if i >= sArray.count { + if i == j { + res.append(part) + } + return + } + + if isPali(sArray, j, i) { + part.append(String(sArray[j...i])) + dfs(i + 1, i + 1) + part.removeLast() + } + + dfs(j, i + 1) + } + + func isPali(_ s: [Character], _ l: Int, _ r: Int) -> Bool { + var l = l, r = r + while l < r { + if s[l] != s[r] { + return false + } + l += 1 + r -= 1 + } + return true + } + + dfs(0, 0) + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: + * $O(n)$ extra space. + * $O(n * 2 ^ n)$ space for the output list. + +--- + +## 2. Backtracking - II + +::tabs-start + +```python +class Solution: + + def partition(self, s: str) -> List[List[str]]: + res, part = [], [] + + def dfs(i): + if i >= len(s): + res.append(part.copy()) + return + for j in range(i, len(s)): + if self.isPali(s, i, j): + part.append(s[i : j + 1]) + dfs(j + 1) + part.pop() + + dfs(0) + return res + + def isPali(self, s, l, r): + while l < r: + if s[l] != s[r]: + return False + l, r = l + 1, r - 1 + return True +``` + +```java +public class Solution { + + public List> partition(String s) { + List> res = new ArrayList<>(); + List part = new ArrayList<>(); + dfs(0, s, part, res); + return res; + } + + private void dfs(int i, String s, List part, List> res) { + if (i >= s.length()) { + res.add(new ArrayList<>(part)); + return; + } + for (int j = i; j < s.length(); j++) { + if (isPali(s, i, j)) { + part.add(s.substring(i, j + 1)); + dfs(j + 1, s, part, res); + part.remove(part.size() - 1); + } + } + } + + private boolean isPali(String s, int l, int r) { + while (l < r) { + if (s.charAt(l) != s.charAt(r)) { + return false; + } + l++; + r--; + } + return true; + } +} +``` + +```cpp +class Solution { +public: + vector> partition(string s) { + vector> res; + vector part; + dfs(0, s, part, res); + return res; + } + +private: + void dfs(int i, const string& s, vector& part, vector>& res) { + if (i >= s.length()) { + res.push_back(part); + return; + } + for (int j = i; j < s.length(); j++) { + if (isPali(s, i, j)) { + part.push_back(s.substr(i, j - i + 1)); + dfs(j + 1, s, part, res); + part.pop_back(); + } + } + } + + bool isPali(const string& s, int l, int r) { + while (l < r) { + if (s[l] != s[r]) { + return false; + } + l++; + r--; + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string[][]} + */ + partition(s) { + const res = []; + const part = []; + this.dfs(0, s, part, res); + return res; + } + + /** + * @param {number} i + * @param {string} s + * @param {string[]} part + * @param {string[][]} res + * @return {void} + */ + dfs(i, s, part, res) { + if (i >= s.length) { + res.push([...part]); + return; + } + for (let j = i; j < s.length; j++) { + if (this.isPali(s, i, j)) { + part.push(s.substring(i, j + 1)); + this.dfs(j + 1, s, part, res); + part.pop(); + } + } + } + + /** + * @param {string} s + * @param {number} l + * @param {number} r + * @return {boolean} + */ + isPali(s, l, r) { + while (l < r) { + if (s[l] !== s[r]) { + return false; + } + l++; + r--; + } + return true; + } +} +``` + +```csharp +public class Solution { + + public List> Partition(string s) { + List> res = new List>(); + List part = new List(); + Dfs(0, s, part, res); + return res; + } + + private void Dfs(int i, string s, List part, List> res) { + if (i >= s.Length) { + res.Add(new List(part)); + return; + } + for (int j = i; j < s.Length; j++) { + if (IsPali(s, i, j)) { + part.Add(s.Substring(i, j - i + 1)); + Dfs(j + 1, s, part, res); + part.RemoveAt(part.Count - 1); + } + } + } + + private bool IsPali(string s, int l, int r) { + while (l < r) { + if (s[l] != s[r]) { + return false; + } + l++; + r--; + } + return true; + } +} +``` + +```go +func partition(s string) [][]string { + res := [][]string{} + part := []string{} + + var dfs func(i int) + dfs = func(i int) { + if i >= len(s) { + res = append(res, append([]string{}, part...)) + return + } + for j := i; j < len(s); j++ { + if isPali(s, i, j) { + part = append(part, s[i:j+1]) + dfs(j + 1) + part = part[:len(part)-1] + } + } + } + + dfs(0) + return res +} + +func isPali(s string, l, r int) bool { + for l < r { + if s[l] != s[r] { + return false + } + l++ + r-- + } + return true +} +``` + +```kotlin +class Solution { + fun partition(s: String): List> { + val res = mutableListOf>() + val part = mutableListOf() + + fun dfs(i: Int) { + if (i >= s.length) { + res.add(part.toList()) + return + } + for (j in i until s.length) { + if (isPali(s, i, j)) { + part.add(s.substring(i, j + 1)) + dfs(j + 1) + part.removeAt(part.size - 1) + } + } + } + + dfs(0) + return res + } + + private fun isPali(s: String, l: Int, r: Int): Boolean { + var left = l + var right = r + while (left < right) { + if (s[left] != s[right]) { + return false + } + left++ + right-- + } + return true + } +} +``` + +```swift +class Solution { + func partition(_ s: String) -> [[String]] { + var res = [[String]]() + var part = [String]() + let sArray = Array(s) + + func dfs(_ i: Int) { + if i >= sArray.count { + res.append(part) + return + } + for j in i.. Bool { + var l = l, r = r + while l < r { + if s[l] != s[r] { + return false + } + l += 1 + r -= 1 + } + return true + } + + dfs(0) + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: + * $O(n)$ extra space. + * $O(n * 2 ^ n)$ space for the output list. + +--- + +## 3. Backtracking (DP) + +::tabs-start + +```python +class Solution: + + def partition(self, s: str) -> List[List[str]]: + n = len(s) + dp = [[False] * n for _ in range(n)] + for l in range(1, n + 1): + for i in range(n - l + 1): + dp[i][i + l - 1] = (s[i] == s[i + l - 1] and + (i + 1 > (i + l - 2) or + dp[i + 1][i + l - 2])) + + res, part = [], [] + def dfs(i): + if i >= len(s): + res.append(part.copy()) + return + for j in range(i, len(s)): + if dp[i][j]: + part.append(s[i : j + 1]) + dfs(j + 1) + part.pop() + + dfs(0) + return res +``` + +```java +public class Solution { + boolean[][] dp; + public List> partition(String s) { + int n = s.length(); + dp = new boolean[n][n]; + for (int l = 1; l <= n; l++) { + for (int i = 0; i <= n - l; i++) { + dp[i][i + l - 1] = (s.charAt(i) == s.charAt(i + l - 1) && + (i + 1 > (i + l - 2) || + dp[i + 1][i + l - 2])); + } + } + + List> res = new ArrayList<>(); + List part = new ArrayList<>(); + dfs(0, s, part, res); + return res; + } + + private void dfs(int i, String s, List part, List> res) { + if (i >= s.length()) { + res.add(new ArrayList<>(part)); + return; + } + for (int j = i; j < s.length(); j++) { + if (dp[i][j]) { + part.add(s.substring(i, j + 1)); + dfs(j + 1, s, part, res); + part.remove(part.size() - 1); + } + } + } +} +``` + +```cpp +class Solution { + vector> dp; +public: + vector> partition(string s) { + int n = s.length(); + dp.resize(n, vector(n)); + for (int l = 1; l <= n; l++) { + for (int i = 0; i <= n - l; i++) { + dp[i][i + l - 1] = (s[i] == s[i + l - 1] && + (i + 1 > (i + l - 2) || + dp[i + 1][i + l - 2])); + } + } + + vector> res; + vector part; + dfs(0, s, part, res); + return res; + } + +private: + void dfs(int i, const string& s, vector& part, vector>& res) { + if (i >= s.length()) { + res.push_back(part); + return; + } + for (int j = i; j < s.length(); j++) { + if (dp[i][j]) { + part.push_back(s.substr(i, j - i + 1)); + dfs(j + 1, s, part, res); + part.pop_back(); + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string[][]} + */ + partition(s) { + const n = s.length; + const dp = Array.from({ length: n }, () => Array(n).fill(false)); + for (let l = 1; l <= n; l++) { + for (let i = 0; i <= n - l; i++) { + dp[i][i + l - 1] = (s[i] === s[i + l - 1] && + (i + 1 > (i + l - 2) || + dp[i + 1][i + l - 2])); + } + } + + const res = []; + const part = []; + const dfs = (i) => { + if (i >= s.length) { + res.push([...part]); + return; + } + for (let j = i; j < s.length; j++) { + if (dp[i][j]) { + part.push(s.substring(i, j + 1)); + dfs(j + 1, s, part, res); + part.pop(); + } + } + } + dfs(0); + return res; + } +} +``` + +```csharp +public class Solution { + + public List> Partition(string s) { + int n = s.Length; + bool[,] dp = new bool[n, n]; + for (int l = 1; l <= n; l++) { + for (int i = 0; i <= n - l; i++) { + dp[i, i + l - 1] = (s[i] == s[i + l - 1] && + (i + 1 > (i + l - 2) || + dp[i + 1, i + l - 2])); + } + } + + List> res = new List>(); + List part = new List(); + Dfs(0, s, part, res, dp); + return res; + } + + private void Dfs(int i, string s, List part, List> res, bool[,] dp) { + if (i >= s.Length) { + res.Add(new List(part)); + return; + } + for (int j = i; j < s.Length; j++) { + if (dp[i, j]) { + part.Add(s.Substring(i, j - i + 1)); + Dfs(j + 1, s, part, res, dp); + part.RemoveAt(part.Count - 1); + } + } + } +} +``` + +```go +func partition(s string) [][]string { + n := len(s) + dp := make([][]bool, n) + for i := range dp { + dp[i] = make([]bool, n) + } + + for l := 1; l <= n; l++ { + for i := 0; i <= n-l; i++ { + dp[i][i+l-1] = (s[i] == s[i+l-1] && (i+1 > (i+l-2) || dp[i+1][i+l-2])) + } + } + + res := [][]string{} + part := []string{} + + var dfs func(i int) + dfs = func(i int) { + if i >= len(s) { + res = append(res, append([]string{}, part...)) + return + } + for j := i; j < len(s); j++ { + if dp[i][j] { + part = append(part, s[i:j+1]) + dfs(j + 1) + part = part[:len(part)-1] + } + } + } + + dfs(0) + return res +} +``` + +```kotlin +class Solution { + fun partition(s: String): List> { + val n = s.length + val dp = Array(n) { BooleanArray(n) } + + for (l in 1..n) { + for (i in 0..n - l) { + dp[i][i + l - 1] = s[i] == s[i + l - 1] && + (i + 1 > (i + l - 2) || dp[i + 1][i + l - 2]) + } + } + + val res = mutableListOf>() + val part = mutableListOf() + + fun dfs(i: Int) { + if (i >= s.length) { + res.add(part.toList()) + return + } + for (j in i until s.length) { + if (dp[i][j]) { + part.add(s.substring(i, j + 1)) + dfs(j + 1) + part.removeAt(part.size - 1) + } + } + } + + dfs(0) + return res + } +} +``` + +```swift +class Solution { + func partition(_ s: String) -> [[String]] { + let n = s.count + let sArray = Array(s) + var dp = Array(repeating: Array(repeating: false, count: n), count: n) + + for l in 1...n { + for i in 0...(n - l) { + dp[i][i + l - 1] = (sArray[i] == sArray[i + l - 1] && + (i + 1 > (i + l - 2) || dp[i + 1][i + l - 2])) + } + } + + var res = [[String]]() + var part = [String]() + + func dfs(_ i: Int) { + if i >= sArray.count { + res.append(part) + return + } + for j in i.. List[List[str]]: + n = len(s) + dp = [[False] * n for _ in range(n)] + for l in range(1, n + 1): + for i in range(n - l + 1): + dp[i][i + l - 1] = (s[i] == s[i + l - 1] and + (i + 1 > (i + l - 2) or + dp[i + 1][i + l - 2])) + + def dfs(i): + if i >= n: + return [[]] + + ret = [] + for j in range(i, n): + if dp[i][j]: + nxt = dfs(j + 1) + for part in nxt: + cur = [s[i : j + 1]] + part + ret.append(cur) + return ret + + return dfs(0) +``` + +```java +public class Solution { + + public List> partition(String s) { + int n = s.length(); + boolean[][] dp = new boolean[n][n]; + for (int l = 1; l <= n; l++) { + for (int i = 0; i <= n - l; i++) { + dp[i][i + l - 1] = (s.charAt(i) == s.charAt(i + l - 1) && + (i + 1 > (i + l - 2) || + dp[i + 1][i + l - 2])); + } + } + + return dfs(s, dp, 0); + } + + private List> dfs(String s, boolean[][] dp, int i) { + if (i >= s.length()) { + return new ArrayList>() {{ add(new ArrayList<>()); }}; + } + + List> ret = new ArrayList<>(); + for (int j = i; j < s.length(); j++) { + if (dp[i][j]) { + List> nxt = dfs(s, dp, j + 1); + for (List part : nxt) { + List cur = new ArrayList<>(); + cur.add(s.substring(i, j + 1)); + cur.addAll(part); + ret.add(cur); + } + } + } + return ret; + } +} +``` + +```cpp +class Solution { +public: + vector> partition(string s) { + int n = s.size(); + vector> dp(n, vector(n, false)); + for (int l = 1; l <= n; l++) { + for (int i = 0; i <= n - l; i++) { + dp[i][i + l - 1] = (s[i] == s[i + l - 1] && + (i + 1 > (i + l - 2) || + dp[i + 1][i + l - 2])); + } + } + + return dfs(s, dp, 0); + } + + vector> dfs(string& s, vector>& dp, int i) { + if (i >= s.size()) { + return {{}}; + } + + vector> ret; + for (int j = i; j < s.size(); j++) { + if (dp[i][j]) { + auto nxt = dfs(s, dp, j + 1); + for (auto& part : nxt) { + vector cur; + cur.push_back(s.substr(i, j - i + 1)); + cur.insert(cur.end(), part.begin(), part.end()); + ret.push_back(cur); + } + } + } + return ret; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string[][]} + */ + partition(s) { + const n = s.length; + const dp = Array.from({ length: n }, () => Array(n).fill(false)); + for (let l = 1; l <= n; l++) { + for (let i = 0; i <= n - l; i++) { + dp[i][i + l - 1] = (s[i] === s[i + l - 1] && + (i + 1 > (i + l - 2) || + dp[i + 1][i + l - 2])); + } + } + + const dfs = (i) => { + if (i >= s.length) { + return [[]]; + } + + const ret = []; + for (let j = i; j < s.length; j++) { + if (dp[i][j]) { + const nxt = dfs(j + 1); + for (const part of nxt) { + const cur = [s.slice(i, j + 1), ...part]; + ret.push(cur); + } + } + } + return ret; + }; + + return dfs(0); + } +} +``` + +```csharp +public class Solution { + + public List> Partition(string s) { + int n = s.Length; + bool[,] dp = new bool[n, n]; + for (int l = 1; l <= n; l++) { + for (int i = 0; i <= n - l; i++) { + dp[i, i + l - 1] = (s[i] == s[i + l - 1] && + (i + 1 > (i + l - 2) || + dp[i + 1, i + l - 2])); + } + } + + return Dfs(s, dp, 0); + } + + private List> Dfs(string s, bool[,] dp, int i) { + if (i >= s.Length) { + return new List> { new List() }; + } + + var ret = new List>(); + for (int j = i; j < s.Length; j++) { + if (dp[i, j]) { + var nxt = Dfs(s, dp, j + 1); + foreach (var part in nxt) { + var cur = new List { s.Substring(i, j - i + 1) }; + cur.AddRange(part); + ret.Add(cur); + } + } + } + return ret; + } +} +``` + +```go +func partition(s string) [][]string { + n := len(s) + dp := make([][]bool, n) + for i := range dp { + dp[i] = make([]bool, n) + } + + for l := 1; l <= n; l++ { + for i := 0; i <= n-l; i++ { + dp[i][i+l-1] = (s[i] == s[i+l-1] && + (i+1 > (i+l-2) || dp[i+1][i+l-2])) + } + } + + var dfs func(i int) [][]string + dfs = func(i int) [][]string { + if i >= n { + return [][]string{{}} + } + + ret := [][]string{} + for j := i; j < n; j++ { + if dp[i][j] { + nxt := dfs(j + 1) + for _, part := range nxt { + cur := append([]string{s[i : j+1]}, part...) + ret = append(ret, cur) + } + } + } + return ret + } + + return dfs(0) +} +``` + +```kotlin +class Solution { + fun partition(s: String): List> { + val n = s.length + val dp = Array(n) { BooleanArray(n) } + + for (l in 1..n) { + for (i in 0..n - l) { + dp[i][i + l - 1] = s[i] == s[i + l - 1] && + (i + 1 > (i + l - 2) || dp[i + 1][i + l - 2]) + } + } + + fun dfs(i: Int): List> { + if (i >= n) { + return listOf(emptyList()) + } + + val ret = mutableListOf>() + for (j in i until n) { + if (dp[i][j]) { + val nxt = dfs(j + 1) + for (part in nxt) { + val cur = listOf(s.substring(i, j + 1)) + part + ret.add(cur) + } + } + } + return ret + } + + return dfs(0) + } +} +``` + +```swift +class Solution { + func partition(_ s: String) -> [[String]] { + let n = s.count + let sArray = Array(s) + var dp = Array(repeating: Array(repeating: false, count: n), count: n) + + for l in 1...n { + for i in 0...(n - l) { + dp[i][i + l - 1] = (sArray[i] == sArray[i + l - 1] && + (i + 1 > (i + l - 2) || dp[i + 1][i + l - 2])) + } + } + + func dfs(_ i: Int) -> [[String]] { + if i >= n { + return [[]] + } + + var ret = [[String]]() + for j in i.. int: + res = 0 + + for i in range(len(s)): + for j in range(i, len(s)): + l, r = i, j + while l < r and s[l] == s[r]: + l += 1 + r -= 1 + res += (l >= r) + + return res +``` + +```java +public class Solution { + public int countSubstrings(String s) { + int res = 0; + + for (int i = 0; i < s.length(); i++) { + for (int j = i; j < s.length(); j++) { + int l = i, r = j; + while (l < r && s.charAt(l) == s.charAt(r)) { + l++; + r--; + } + res += (l >= r) ? 1 : 0; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countSubstrings(string s) { + int res = 0; + + for (int i = 0; i < s.size(); i++) { + for (int j = i; j < s.size(); j++) { + int l = i, r = j; + while (l < r && s[l] == s[r]) { + l++; + r--; + } + res += (l >= r); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + countSubstrings(s) { + let res = 0; + + for (let i = 0; i < s.length; i++) { + for (let j = i; j < s.length; j++) { + let l = i, r = j; + while (l < r && s[l] === s[r]) { + l++; + r--; + } + res += (l >= r) ? 1 : 0; + } + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int CountSubstrings(string s) { + int res = 0; + + for (int i = 0; i < s.Length; i++) { + for (int j = i; j < s.Length; j++) { + int l = i, r = j; + while (l < r && s[l] == s[r]) { + l++; + r--; + } + res += (l >= r) ? 1 : 0; + } + } + + return res; + } +} +``` + +```go +func countSubstrings(s string) int { + res := 0 + for i := range s { + for j := i; j < len(s); j++ { + l, r := i, j + for l < r && s[l] == s[r] { + l++ + r-- + } + if l >= r { + res++ + } + } + } + return res +} +``` + +```kotlin +class Solution { + fun countSubstrings(s: String): Int { + var res = 0 + for (i in s.indices) { + for (j in i until s.length) { + var l = i + var r = j + while (l < r && s[l] == s[r]) { + l++ + r-- + } + if (l >= r) { + res++ + } + } + } + return res + } +} +``` + +```swift +class Solution { + func countSubstrings(_ s: String) -> Int { + let chars = Array(s) + var res = 0 + + for i in 0..= r { + res += 1 + } + } + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(1)$ + +--- + +## 2. Dynamic Programming + +::tabs-start + +```python +class Solution: + def countSubstrings(self, s: str) -> int: + n, res = len(s), 0 + dp = [[False] * n for _ in range(n)] + + for i in range(n - 1, -1, -1): + for j in range(i, n): + if s[i] == s[j] and (j - i <= 2 or dp[i + 1][j - 1]): + dp[i][j] = True + res += 1 + + return res +``` + +```java +public class Solution { + public int countSubstrings(String s) { + int res = 0, n = s.length(); + boolean[][] dp = new boolean[n][n]; + + for (int i = n - 1; i >= 0; i--) { + for (int j = i; j < n; j++) { + if (s.charAt(i) == s.charAt(j) && + (j - i <= 2 || dp[i + 1][j - 1])) { + + dp[i][j] = true; + res++; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countSubstrings(string s) { + int res = 0, n = s.length(); + vector> dp(n, vector(n, false)); + + for (int i = n - 1; i >= 0; i--) { + for (int j = i; j < n; j++) { + if (s[i] == s[j] && + (j - i <= 2 || dp[i + 1][j - 1])) { + + dp[i][j] = true; + res++; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + countSubstrings(s) { + let res = 0; + const n = s.length; + const dp = Array.from({ length: n }, () => Array(n).fill(false)); + + for (let i = n - 1; i >= 0; i--) { + for (let j = i; j < n; j++) { + if (s[i] === s[j] && + (j - i <= 2 || dp[i + 1][j - 1])) { + + dp[i][j] = true; + res++; + } + } + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int CountSubstrings(string s) { + int res = 0, n = s.Length; + bool[,] dp = new bool[n, n]; + + for (int i = n - 1; i >= 0; i--) { + for (int j = i; j < n; j++) { + if (s[i] == s[j] && + (j - i <= 2 || dp[i + 1, j - 1])) { + + dp[i, j] = true; + res++; + } + } + } + + return res; + } +} +``` + +```go +func countSubstrings(s string) int { + n := len(s) + res := 0 + dp := make([][]bool, n) + for i := range dp { + dp[i] = make([]bool, n) + } + + for i := n - 1; i >= 0; i-- { + for j := i; j < n; j++ { + if s[i] == s[j] && (j-i <= 2 || dp[i+1][j-1]) { + dp[i][j] = true + res++ + } + } + } + + return res +} +``` + +```kotlin +class Solution { + fun countSubstrings(s: String): Int { + val n = s.length + var res = 0 + val dp = Array(n) { BooleanArray(n) } + + for (i in n - 1 downTo 0) { + for (j in i until n) { + if (s[i] == s[j] && (j - i <= 2 || dp[i + 1][j - 1])) { + dp[i][j] = true + res++ + } + } + } + + return res + } +} +``` + +```swift +class Solution { + func countSubstrings(_ s: String) -> Int { + let n = s.count + var res = 0 + var dp = Array(repeating: Array(repeating: false, count: n), count: n) + let chars = Array(s) + + for i in stride(from: n - 1, through: 0, by: -1) { + for j in i.. int: + res = 0 + + for i in range(len(s)): + # odd length + l, r = i, i + while l >= 0 and r < len(s) and s[l] == s[r]: + res += 1 + l -= 1 + r += 1 + + # even length + l, r = i, i + 1 + while l >= 0 and r < len(s) and s[l] == s[r]: + res += 1 + l -= 1 + r += 1 + + return res +``` + +```java +public class Solution { + public int countSubstrings(String s) { + int res = 0; + + for (int i = 0; i < s.length(); i++) { + // odd length + int l = i, r = i; + while (l >= 0 && r < s.length() && + s.charAt(l) == s.charAt(r)) { + res++; + l--; + r++; + } + + // even length + l = i; + r = i + 1; + while (l >= 0 && r < s.length() && + s.charAt(l) == s.charAt(r)) { + res++; + l--; + r++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countSubstrings(string s) { + int res = 0; + + for (int i = 0; i < s.size(); i++) { + // odd length + int l = i, r = i; + while (l >= 0 && r < s.size() && + s[l] == s[r]) { + res++; + l--; + r++; + } + + // even length + l = i; + r = i + 1; + while (l >= 0 && r < s.size() && + s[l] == s[r]) { + res++; + l--; + r++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + countSubstrings(s) { + let res = 0; + + for (let i = 0; i < s.length; i++) { + // odd length + let l = i; + let r = i; + while (l >= 0 && r < s.length && + s.charAt(l) === s.charAt(r)) { + res++; + l--; + r++; + } + + // even length + l = i; + r = i + 1; + while (l >= 0 && r < s.length && + s.charAt(l) === s.charAt(r)) { + res++; + l--; + r++; + } + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int CountSubstrings(string s) { + int res = 0; + + for (int i = 0; i < s.Length; i++) { + // odd length + int l = i, r = i; + while (l >= 0 && r < s.Length && + s[l] == s[r]) { + res++; + l--; + r++; + } + + // even length + l = i; + r = i + 1; + while (l >= 0 && r < s.Length && + s[l] == s[r]) { + res++; + l--; + r++; + } + } + + return res; + } +} +``` + +```go +func countSubstrings(s string) int { + res := 0 + + for i := 0; i < len(s); i++ { + // Odd-length + l, r := i, i + for l >= 0 && r < len(s) && s[l] == s[r] { + res++ + l-- + r++ + } + + // Even-length + l, r = i, i+1 + for l >= 0 && r < len(s) && s[l] == s[r] { + res++ + l-- + r++ + } + } + + return res +} +``` + +```kotlin +class Solution { + fun countSubstrings(s: String): Int { + var res = 0 + + for (i in s.indices) { + // Odd-length + var l = i + var r = i + while (l >= 0 && r < s.length && s[l] == s[r]) { + res++ + l-- + r++ + } + + // Even-length + l = i + r = i + 1 + while (l >= 0 && r < s.length && s[l] == s[r]) { + res++ + l-- + r++ + } + } + + return res + } +} +``` + +```swift +class Solution { + func countSubstrings(_ s: String) -> Int { + let chars = Array(s) + var res = 0 + + for i in 0..= 0 && r < chars.count && chars[l] == chars[r] { + res += 1 + l -= 1 + r += 1 + } + + // Even length palindromes + l = i + r = i + 1 + while l >= 0 && r < chars.count && chars[l] == chars[r] { + res += 1 + l -= 1 + r += 1 + } + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 4. Two Pointers (Optimal) + +::tabs-start + +```python +class Solution: + + def countSubstrings(self, s: str) -> int: + res = 0 + + for i in range(len(s)): + res += self.countPali(s, i, i) + res += self.countPali(s, i, i + 1) + return res + + def countPali(self, s, l, r): + res = 0 + while l >= 0 and r < len(s) and s[l] == s[r]: + res += 1 + l -= 1 + r += 1 + return res +``` + +```java +public class Solution { + + public int countSubstrings(String s) { + int res = 0; + for (int i = 0; i < s.length(); i++) { + res += countPali(s, i, i); + res += countPali(s, i, i + 1); + } + return res; + } + + private int countPali(String s, int l, int r) { + int res = 0; + while (l >= 0 && r < s.length() && + s.charAt(l) == s.charAt(r)) { + res++; + l--; + r++; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int countSubstrings(string s) { + int res = 0; + for (int i = 0; i < s.size(); i++) { + res += countPali(s, i, i); + res += countPali(s, i, i + 1); + } + return res; + } + +private: + int countPali(string s, int l, int r) { + int res = 0; + while (l >= 0 && r < s.size() && s[l] == s[r]) { + res++; + l--; + r++; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + countSubstrings(s) { + let res = 0; + for (let i = 0; i < s.length; i++) { + res += this.countPali(s, i, i); + res += this.countPali(s, i, i + 1); + } + return res; + } + + /** + * @param {string} s + * @param {number} l + * @param {number} r + * @return {number} + */ + countPali(s, l, r) { + let res = 0; + while (l >= 0 && r < s.length && + s.charAt(l) === s.charAt(r)) { + res++; + l--; + r++; + } + return res; + } +} +``` + +```csharp +public class Solution { + + public int CountSubstrings(string s) { + int res = 0; + for (int i = 0; i < s.Length; i++) { + res += CountPali(s, i, i); + res += CountPali(s, i, i + 1); + } + return res; + } + + private int CountPali(string s, int l, int r) { + int res = 0; + while (l >= 0 && r < s.Length && s[l] == s[r]) { + res++; + l--; + r++; + } + return res; + } +} +``` + +```go +func countSubstrings(s string) int { + res := 0 + for i := 0; i < len(s); i++ { + res += countPali(s, i, i) + res += countPali(s, i, i+1) + } + return res +} + +func countPali(s string, l, r int) int { + res := 0 + for l >= 0 && r < len(s) && s[l] == s[r] { + res++ + l-- + r++ + } + return res +} +``` + +```kotlin +class Solution { + fun countSubstrings(s: String): Int { + var res = 0 + for (i in s.indices) { + res += countPali(s, i, i) + res += countPali(s, i, i + 1) + } + return res + } + + private fun countPali(s: String, l: Int, r: Int): Int { + var left = l + var right = r + var res = 0 + while (left >= 0 && right < s.length && s[left] == s[right]) { + res++ + left-- + right++ + } + return res + } +} +``` + +```swift +class Solution { + func countSubstrings(_ s: String) -> Int { + var res = 0 + let chars = Array(s) + + for i in 0.. Int { + var res = 0 + var left = l, right = r + + while left >= 0 && right < s.count && s[left] == s[right] { + res += 1 + left -= 1 + right += 1 + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 5. Manacher's Algorithm + +::tabs-start + +```python +class Solution: + def countSubstrings(self, s: str) -> int: + + def manacher(s): + t = '#' + '#'.join(s) + '#' + n = len(t) + p = [0] * n + l, r = 0, 0 + for i in range(n): + p[i] = min(r - i, p[l + (r - i)]) if i < r else 0 + while (i + p[i] + 1 < n and i - p[i] - 1 >= 0 + and t[i + p[i] + 1] == t[i - p[i] - 1]): + p[i] += 1 + if i + p[i] > r: + l, r = i - p[i], i + p[i] + return p + + p = manacher(s) + res = 0 + for i in p: + res += (i + 1) // 2 + return res +``` + +```java +public class Solution { + + public int[] manacher(String s) { + StringBuilder t = new StringBuilder("#"); + for (char c : s.toCharArray()) { + t.append(c).append("#"); + } + int n = t.length(); + int[] p = new int[n]; + int l = 0, r = 0; + for (int i = 0; i < n; i++) { + p[i] = (i < r) ? Math.min(r - i, p[l + (r - i)]) : 0; + while (i + p[i] + 1 < n && i - p[i] - 1 >= 0 && + t.charAt(i + p[i] + 1) == t.charAt(i - p[i] - 1)) { + p[i]++; + } + if (i + p[i] > r) { + l = i - p[i]; + r = i + p[i]; + } + } + return p; + } + + public int countSubstrings(String s) { + int res = 0; + int[] p = manacher(s); + for (int i : p) { + res += (i + 1) / 2; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector manacher(string& s) { + if (!s.size()) return {}; + string t = "#" + string(1, s[0]); + for (int i = 1; i < s.size(); ++i) + t += "#" + string(1, s[i]); + t += "#"; + int n = t.size(); + vector p(n, 0); + int l = 0, r = 0; + for (int i = 0; i < n; i++) { + p[i] = (i < r) ? min(r - i, p[l + (r - i)]) : 0; + while (i + p[i] + 1 < n && i - p[i] - 1 >= 0 && + t[i + p[i] + 1] == t[i - p[i] - 1]) + p[i]++; + if (i + p[i] > r) + l = i - p[i], r = i + p[i]; + } + return p; + } + + int countSubstrings(string s) { + vector p = manacher(s); + int res = 0; + for (int i : p) { + res += (i + 1) / 2; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number[]} + */ + manacher(s) { + const t = '#' + s.split('').join('#') + '#'; + const n = t.length; + const p = new Array(n).fill(0); + let l = 0, r = 0; + for (let i = 0; i < n; i++) { + p[i] = (i < r) ? Math.min(r - i, p[l + (r - i)]) : 0; + while (i + p[i] + 1 < n && i - p[i] - 1 >= 0 && + t[i + p[i] + 1] === t[i - p[i] - 1]) { + p[i]++; + } + if (i + p[i] > r) { + l = i - p[i]; + r = i + p[i]; + } + } + return p; + } + + /** + * @param {string} s + * @return {number} + */ + countSubstrings(s) { + const p = this.manacher(s); + let res = 0; + for (let i of p) { + res += Math.floor((i + 1) / 2); + } + return res; + } +} +``` + +```csharp +public class Solution { + public int[] Manacher(string s) { + string t = "#" + string.Join("#", s.ToCharArray()) + "#"; + int n = t.Length; + int[] p = new int[n]; + int l = 0, r = 0; + for (int i = 0; i < n; i++) { + p[i] = (i < r) ? Math.Min(r - i, p[l + (r - i)]) : 0; + while (i + p[i] + 1 < n && i - p[i] - 1 >= 0 && + t[i + p[i] + 1] == t[i - p[i] - 1]) { + p[i]++; + } + if (i + p[i] > r) { + l = i - p[i]; + r = i + p[i]; + } + } + return p; + } + + public int CountSubstrings(string s) { + int[] p = Manacher(s); + int res = 0; + foreach (int i in p) { + res += (i + 1) / 2; + } + return res; + } +} +``` + +```go +func countSubstrings(s string) int { + manacher := func(s string) []int { + t := "#" + joinWithSeparator(s, "#") + "#" + n := len(t) + p := make([]int, n) + l, r := 0, 0 + for i := 0; i < n; i++ { + if i < r { + p[i] = min(r-i, p[l+(r-i)]) + } + for i+p[i]+1 < n && i-p[i]-1 >= 0 && t[i+p[i]+1] == t[i-p[i]-1] { + p[i]++ + } + if i+p[i] > r { + l, r = i-p[i], i+p[i] + } + } + return p + } + + p := manacher(s) + res := 0 + for _, val := range p { + res += (val + 1) / 2 + } + return res +} + +func joinWithSeparator(s, sep string) string { + result := "" + for i := 0; i < len(s); i++ { + result += string(s[i]) + sep + } + return result[:len(result)-1] +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun countSubstrings(s: String): Int { + fun manacher(s: String): IntArray { + val t = "#" + s.toCharArray().joinToString("#") + "#" + val n = t.length + val p = IntArray(n) + var l = 0 + var r = 0 + for (i in 0 until n) { + if (i < r) { + p[i] = minOf(r - i, p[l + (r - i)]) + } + while (i + p[i] + 1 < n && i - p[i] - 1 >= 0 && + t[i + p[i] + 1] == t[i - p[i] - 1]) { + p[i]++ + } + if (i + p[i] > r) { + l = i - p[i] + r = i + p[i] + } + } + return p + } + + val p = manacher(s) + var res = 0 + for (i in p) { + res += (i + 1) / 2 + } + return res + } +} +``` + +```swift +class Solution { + func countSubstrings(_ s: String) -> Int { + func manacher(_ s: String) -> [Int] { + let t = "#" + s.map { "\($0)#" }.joined() + let chars = Array(t) + let n = chars.count + var p = Array(repeating: 0, count: n) + var l = 0, r = 0 + + for i in 0..= 0, + chars[i + p[i] + 1] == chars[i - p[i] - 1] { + p[i] += 1 + } + if i + p[i] > r { + l = i - p[i] + r = i + p[i] + } + } + return p + } + + let p = manacher(s) + var res = 0 + for i in p { + res += (i + 1) / 2 + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/parallel-courses-iii.md b/articles/parallel-courses-iii.md new file mode 100644 index 000000000..ca66a0769 --- /dev/null +++ b/articles/parallel-courses-iii.md @@ -0,0 +1,481 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def minimumTime(self, n: int, relations: list[list[int]], time: list[int]) -> int: + adj = defaultdict(list) + for src, dst in relations: + adj[src].append(dst) + + max_time = {} + + def dfs(src): + if src in max_time: + return max_time[src] + res = time[src - 1] + for nei in adj[src]: + res = max(res, time[src - 1] + dfs(nei)) + max_time[src] = res + return res + + for i in range(1, n + 1): + dfs(i) + + return max(max_time.values()) +``` + +```java +public class Solution { + private Map maxTime; + private List[] adj; + private int[] time; + + public int minimumTime(int n, int[][] relations, int[] time) { + this.time = time; + this.maxTime = new HashMap<>(); + this.adj = new ArrayList[n + 1]; + + for (int i = 1; i <= n; i++) { + adj[i] = new ArrayList<>(); + } + for (int[] relation : relations) { + adj[relation[0]].add(relation[1]); + } + + for (int i = 1; i <= n; i++) { + dfs(i); + } + + return Collections.max(maxTime.values()); + } + + private int dfs(int src) { + if (maxTime.containsKey(src)) { + return maxTime.get(src); + } + + int res = time[src - 1]; + for (int nei : adj[src]) { + res = Math.max(res, time[src - 1] + dfs(nei)); + } + maxTime.put(src, res); + return res; + } +} +``` + +```cpp +class Solution { +public: + unordered_map maxTime; + vector> adj; + vector time; + + int minimumTime(int n, vector>& relations, vector& time) { + this->time = time; + adj.resize(n + 1); + + for (auto& relation : relations) { + adj[relation[0]].push_back(relation[1]); + } + + for (int i = 1; i <= n; i++) { + dfs(i); + } + + int res = 0; + for (auto& [key, value] : maxTime) { + res = max(res, value); + } + return res; + } + +private: + int dfs(int src) { + if (maxTime.count(src)) { + return maxTime[src]; + } + + int res = time[src - 1]; + for (int nei : adj[src]) { + res = max(res, time[src - 1] + dfs(nei)); + } + maxTime[src] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} relations + * @param {number[]} time + * @return {number} + */ + minimumTime(n, relations, time) { + let adj = Array.from({ length: n + 1 }, () => []); + let maxTime = new Map(); + + for (let [src, dst] of relations) { + adj[src].push(dst); + } + + const dfs = (src) => { + if (maxTime.has(src)) { + return maxTime.get(src); + } + + let res = time[src - 1]; + for (let nei of adj[src]) { + res = Math.max(res, time[src - 1] + dfs(nei)); + } + maxTime.set(src, res); + return res; + }; + + for (let i = 1; i <= n; i++) { + dfs(i); + } + + return Math.max(...maxTime.values()); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of courses and $E$ is the number of prerequisites. + +--- + +## 2. Iterative DFS + +::tabs-start + +```python +class Solution: + def minimumTime(self, n: int, relations: list[list[int]], time: list[int]) -> int: + adj = [[] for _ in range(n)] + for src, dst in relations: + adj[src - 1].append(dst - 1) + + maxTime = [-1] * n + processed = [False] * n + + for i in range(n): + if maxTime[i] == -1: + stack = [i] + while stack: + node = stack.pop() + if processed[node]: + best = 0 + for nei in adj[node]: + best = max(best, maxTime[nei]) + maxTime[node] = time[node] + best + else: + processed[node] = True + stack.append(node) + for nei in adj[node]: + if maxTime[nei] == -1: + stack.append(nei) + return max(maxTime) +``` + +```java +public class Solution { + public int minimumTime(int n, int[][] relations, int[] time) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + for (int[] rel : relations) { + adj[rel[0] - 1].add(rel[1] - 1); + } + + int[] maxTime = new int[n]; + Arrays.fill(maxTime, -1); + boolean[] processed = new boolean[n]; + + for (int i = 0; i < n; i++) { + if (maxTime[i] == -1) { + Stack stack = new Stack<>(); + stack.push(i); + while (!stack.isEmpty()) { + int node = stack.pop(); + if (processed[node]) { + int best = 0; + for (int nei : adj[node]) { + best = Math.max(best, maxTime[nei]); + } + maxTime[node] = time[node] + best; + } else { + processed[node] = true; + stack.push(node); + for (int nei : adj[node]) { + if (maxTime[nei] == -1) { + stack.push(nei); + } + } + } + } + } + } + return Arrays.stream(maxTime).max().getAsInt(); + } +} +``` + +```cpp +class Solution { +public: + int minimumTime(int n, vector>& relations, vector& time) { + vector> adj(n); + for (auto& rel : relations) { + adj[rel[0] - 1].push_back(rel[1] - 1); + } + + vector maxTime(n, -1); + vector processed(n, false); + + for (int i = 0; i < n; i++) { + if (maxTime[i] == -1) { + stack stk; + stk.push(i); + while (!stk.empty()) { + int node = stk.top(); stk.pop(); + if (processed[node]) { + int best = 0; + for (int nei : adj[node]) { + best = max(best, maxTime[nei]); + } + maxTime[node] = time[node] + best; + } else { + processed[node] = true; + stk.push(node); + for (int nei : adj[node]) { + if (maxTime[nei] == -1) { + stk.push(nei); + } + } + } + } + } + } + return *max_element(maxTime.begin(), maxTime.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} relations + * @param {number[]} time + * @return {number} + */ + minimumTime(n, relations, time) { + let adj = Array.from({ length: n }, () => []); + for (let [src, dst] of relations) { + adj[src - 1].push(dst - 1); + } + + let maxTime = Array(n).fill(-1); + let processed = Array(n).fill(false); + + for (let i = 0; i < n; i++) { + if (maxTime[i] === -1) { + let stack = [i]; + while (stack.length > 0) { + let node = stack.pop(); + if (processed[node]) { + let best = 0; + for (let nei of adj[node]) { + best = Math.max(best, maxTime[nei]); + } + maxTime[node] = time[node] + best; + } else { + processed[node] = true; + stack.push(node); + for (let nei of adj[node]) { + if (maxTime[nei] === -1) { + stack.push(nei); + } + } + } + } + } + } + return Math.max(...maxTime); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of courses and $E$ is the number of prerequisites. + +--- + +## 3. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def minimumTime(self, n: int, relations: list[list[int]], time: list[int]) -> int: + adj = [[] for _ in range(n)] + indegree = [0] * n + maxTime = time[:] + + for src, dst in relations: + adj[src - 1].append(dst - 1) + indegree[dst - 1] += 1 + + queue = deque([i for i in range(n) if indegree[i] == 0]) + while queue: + node = queue.popleft() + for nei in adj[node]: + maxTime[nei] = max(maxTime[nei], maxTime[node] + time[nei]) + indegree[nei] -= 1 + if indegree[nei] == 0: + queue.append(nei) + + return max(maxTime) +``` + +```java +public class Solution { + public int minimumTime(int n, int[][] relations, int[] time) { + List> adj = new ArrayList<>(); + int[] indegree = new int[n]; + int[] maxTime = Arrays.copyOf(time, n); + + for (int i = 0; i < n; i++) { + adj.add(new ArrayList<>()); + } + + for (int[] relation : relations) { + int src = relation[0] - 1, dst = relation[1] - 1; + adj.get(src).add(dst); + indegree[dst]++; + } + + Queue queue = new LinkedList<>(); + for (int i = 0; i < n; i++) { + if (indegree[i] == 0) { + queue.add(i); + } + } + + while (!queue.isEmpty()) { + int node = queue.poll(); + for (int nei : adj.get(node)) { + maxTime[nei] = Math.max(maxTime[nei], maxTime[node] + time[nei]); + if (--indegree[nei] == 0) { + queue.add(nei); + } + } + } + + return Arrays.stream(maxTime).max().getAsInt(); + } +} +``` + +```cpp +class Solution { +public: + int minimumTime(int n, vector>& relations, vector& time) { + vector> adj(n); + vector indegree(n, 0); + vector maxTime(time.begin(), time.end()); + + for (auto& relation : relations) { + int src = relation[0] - 1, dst = relation[1] - 1; + adj[src].push_back(dst); + indegree[dst]++; + } + + queue queue; + for (int i = 0; i < n; i++) { + if (indegree[i] == 0) { + queue.push(i); + } + } + + while (!queue.empty()) { + int node = queue.front(); queue.pop(); + for (int nei : adj[node]) { + maxTime[nei] = max(maxTime[nei], maxTime[node] + time[nei]); + if (--indegree[nei] == 0) { + queue.push(nei); + } + } + } + + return *max_element(maxTime.begin(), maxTime.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} relations + * @param {number[]} time + * @return {number} + */ + minimumTime(n, relations, time) { + let adj = Array.from({ length: n }, () => []); + let indegree = Array(n).fill(0); + let maxTime = [...time]; + + for (let [src, dst] of relations) { + adj[src - 1].push(dst - 1); + indegree[dst - 1]++; + } + + let queue = new Queue(); + for (let i = 0; i < n; i++) { + if (indegree[i] === 0) { + queue.push(i); + } + } + + while (!queue.isEmpty()) { + let node = queue.pop(); + for (let nei of adj[node]) { + maxTime[nei] = Math.max(maxTime[nei], maxTime[node] + time[nei]); + if (--indegree[nei] === 0) { + queue.push(nei); + } + } + } + + return Math.max(...maxTime); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of courses and $E$ is the number of prerequisites. \ No newline at end of file diff --git a/articles/partition-array-for-maximum-sum.md b/articles/partition-array-for-maximum-sum.md new file mode 100644 index 000000000..a2fb52393 --- /dev/null +++ b/articles/partition-array-for-maximum-sum.md @@ -0,0 +1,446 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def maxSumAfterPartitioning(self, arr: List[int], k: int) -> int: + def dfs(i): + if i >= len(arr): + return 0 + + cur_max = 0 + res = 0 + for j in range(i, min(len(arr), i + k)): + cur_max = max(cur_max, arr[j]) + window_size = j - i + 1 + res = max(res, dfs(j + 1) + cur_max * window_size) + + return res + + return dfs(0) +``` + +```java +public class Solution { + public int maxSumAfterPartitioning(int[] arr, int k) { + return dfs(0, arr, k); + } + + private int dfs(int i, int[] arr, int k) { + if (i >= arr.length) { + return 0; + } + + int cur_max = 0; + int res = 0; + for (int j = i; j < Math.min(arr.length, i + k); j++) { + cur_max = Math.max(cur_max, arr[j]); + int window_size = j - i + 1; + res = Math.max(res, dfs(j + 1, arr, k) + cur_max * window_size); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxSumAfterPartitioning(vector& arr, int k) { + return dfs(0, arr, k); + } + +private: + int dfs(int i, vector& arr, int k) { + if (i >= arr.size()) { + return 0; + } + + int cur_max = 0, res = 0; + for (int j = i; j < min((int)arr.size(), i + k); j++) { + cur_max = max(cur_max, arr[j]); + int window_size = j - i + 1; + res = max(res, dfs(j + 1, arr, k) + cur_max * window_size); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @return {number} + */ + maxSumAfterPartitioning(arr, k) { + const dfs = (i) => { + if (i >= arr.length) { + return 0; + } + + let cur_max = 0, res = 0; + for (let j = i; j < Math.min(arr.length, i + k); j++) { + cur_max = Math.max(cur_max, arr[j]); + let window_size = j - i + 1; + res = Math.max(res, dfs(j + 1) + cur_max * window_size); + } + + return res; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +> Where $k$ is the maximum length of the subarray and $n$ is the size of the array $arr$. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def maxSumAfterPartitioning(self, arr: List[int], k: int) -> int: + cache = { len(arr) : 0 } + + def dfs(i): + if i in cache: + return cache[i] + + cur_max = 0 + res = 0 + for j in range(i, min(len(arr), i + k)): + cur_max = max(cur_max, arr[j]) + window_size = j - i + 1 + res = max(res, dfs(j + 1) + cur_max * window_size) + + cache[i] = res + return res + + return dfs(0) +``` + +```java +public class Solution { + public int maxSumAfterPartitioning(int[] arr, int k) { + int[] cache = new int[arr.length + 1]; + Arrays.fill(cache, -1); + cache[arr.length] = 0; + return dfs(0, arr, k, cache); + } + + private int dfs(int i, int[] arr, int k, int[] cache) { + if (cache[i] != -1) { + return cache[i]; + } + + int cur_max = 0, res = 0; + for (int j = i; j < Math.min(arr.length, i + k); j++) { + cur_max = Math.max(cur_max, arr[j]); + int window_size = j - i + 1; + res = Math.max(res, dfs(j + 1, arr, k, cache) + cur_max * window_size); + } + + cache[i] = res; + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxSumAfterPartitioning(vector& arr, int k) { + vector cache(arr.size() + 1, -1); + cache[arr.size()] = 0; + return dfs(0, arr, k, cache); + } + +private: + int dfs(int i, vector& arr, int k, vector& cache) { + if (cache[i] != -1) { + return cache[i]; + } + + int cur_max = 0, res = 0; + for (int j = i; j < min((int)arr.size(), i + k); j++) { + cur_max = max(cur_max, arr[j]); + int window_size = j - i + 1; + res = max(res, dfs(j + 1, arr, k, cache) + cur_max * window_size); + } + + return cache[i] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @return {number} + */ + maxSumAfterPartitioning(arr, k) { + const cache = new Array(arr.length + 1).fill(-1); + cache[arr.length] = 0; + + const dfs = (i) => { + if (cache[i] !== -1) { + return cache[i]; + } + + let cur_max = 0, res = 0; + for (let j = i; j < Math.min(arr.length, i + k); j++) { + cur_max = Math.max(cur_max, arr[j]); + let window_size = j - i + 1; + res = Math.max(res, dfs(j + 1) + cur_max * window_size); + } + + return (cache[i] = res); + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(n)$ + +> Where $k$ is the maximum length of the subarray and $n$ is the size of the array $arr$. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def maxSumAfterPartitioning(self, arr: List[int], k: int) -> int: + n = len(arr) + dp = [0] * (n + 1) + + for i in range(n - 1, -1, -1): + cur_max = 0 + for j in range(i, min(n, i + k)): + cur_max = max(cur_max, arr[j]) + window_size = j - i + 1 + dp[i] = max(dp[i], dp[j + 1] + cur_max * window_size) + + return dp[0] +``` + +```java +public class Solution { + public int maxSumAfterPartitioning(int[] arr, int k) { + int n = arr.length; + int[] dp = new int[n + 1]; + + for (int i = n - 1; i >= 0; i--) { + int cur_max = 0; + for (int j = i; j < Math.min(n, i + k); j++) { + cur_max = Math.max(cur_max, arr[j]); + int window_size = j - i + 1; + dp[i] = Math.max(dp[i], dp[j + 1] + cur_max * window_size); + } + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int maxSumAfterPartitioning(vector& arr, int k) { + int n = arr.size(); + vector dp(n + 1, 0); + + for (int i = n - 1; i >= 0; i--) { + int cur_max = 0; + for (int j = i; j < min(n, i + k); j++) { + cur_max = max(cur_max, arr[j]); + int window_size = j - i + 1; + dp[i] = max(dp[i], dp[j + 1] + cur_max * window_size); + } + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @return {number} + */ + maxSumAfterPartitioning(arr, k) { + let n = arr.length; + let dp = new Array(n + 1).fill(0); + + for (let i = n - 1; i >= 0; i--) { + let cur_max = 0; + for (let j = i; j < Math.min(n, i + k); j++) { + cur_max = Math.max(cur_max, arr[j]); + let window_size = j - i + 1; + dp[i] = Math.max(dp[i], dp[j + 1] + cur_max * window_size); + } + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(n)$ + +> Where $k$ is the maximum length of the subarray and $n$ is the size of the array $arr$. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def maxSumAfterPartitioning(self, arr: List[int], k: int) -> int: + dp = [0] * k + dp[0] = arr[0] + + for i in range(1, len(arr)): + cur_max = 0 + max_at_i = 0 + for j in range(i, i - k, -1): + if j < 0: + break + cur_max = max(cur_max, arr[j]) + window_size = i - j + 1 + cur_sum = cur_max * window_size + sub_sum = dp[(j - 1) % k] if j > 0 else 0 + max_at_i = max(max_at_i, cur_sum + sub_sum) + + dp[i % k] = max_at_i + + return dp[(len(arr) - 1) % k] +``` + +```java +public class Solution { + public int maxSumAfterPartitioning(int[] arr, int k) { + int n = arr.length; + int[] dp = new int[k]; + dp[0] = arr[0]; + + for (int i = 1; i < n; i++) { + int cur_max = 0, max_at_i = 0; + for (int j = i; j > i - k; j--) { + if (j < 0) break; + cur_max = Math.max(cur_max, arr[j]); + int window_size = i - j + 1; + int cur_sum = cur_max * window_size; + int sub_sum = (j > 0) ? dp[(j - 1) % k] : 0; + max_at_i = Math.max(max_at_i, cur_sum + sub_sum); + } + dp[i % k] = max_at_i; + } + + return dp[(n - 1) % k]; + } +} +``` + +```cpp +class Solution { +public: + int maxSumAfterPartitioning(vector& arr, int k) { + int n = arr.size(); + vector dp(k); + dp[0] = arr[0]; + + for (int i = 1; i < n; i++) { + int cur_max = 0, max_at_i = 0; + for (int j = i; j > i - k; j--) { + if (j < 0) break; + cur_max = max(cur_max, arr[j]); + int window_size = i - j + 1; + int cur_sum = cur_max * window_size; + int sub_sum = (j > 0) ? dp[(j - 1) % k] : 0; + max_at_i = max(max_at_i, cur_sum + sub_sum); + } + dp[i % k] = max_at_i; + } + + return dp[(n - 1) % k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @param {number} k + * @return {number} + */ + maxSumAfterPartitioning(arr, k) { + const n = arr.length; + const dp = new Array(k).fill(0); + dp[0] = arr[0]; + + for (let i = 1; i < n; i++) { + let cur_max = 0, max_at_i = 0; + for (let j = i; j > i - k; j--) { + if (j < 0) break; + cur_max = Math.max(cur_max, arr[j]); + let window_size = i - j + 1; + let cur_sum = cur_max * window_size; + let sub_sum = (j > 0) ? dp[(j - 1) % k] : 0; + max_at_i = Math.max(max_at_i, cur_sum + sub_sum); + } + dp[i % k] = max_at_i; + } + + return dp[(n - 1) % k]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(k)$ + +> Where $k$ is the maximum length of the subarray and $n$ is the size of the array $arr$. \ No newline at end of file diff --git a/articles/partition-equal-subset-sum.md b/articles/partition-equal-subset-sum.md new file mode 100644 index 000000000..6d6fd0caa --- /dev/null +++ b/articles/partition-equal-subset-sum.md @@ -0,0 +1,1575 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def canPartition(self, nums: List[int]) -> bool: + if sum(nums) % 2: + return False + + def dfs(i, target): + if i >= len(nums): + return target == 0 + if target < 0: + return False + + return dfs(i + 1, target) or dfs(i + 1, target - nums[i]) + + return dfs(0, sum(nums) // 2) +``` + +```java +public class Solution { + public boolean canPartition(int[] nums) { + int n = nums.length; + int sum = 0; + for (int i = 0; i < n; i++) { + sum += nums[i]; + } + if (sum % 2 != 0) { + return false; + } + + return dfs(nums, 0, sum / 2); + } + + public boolean dfs(int[] nums, int i, int target) { + if (i == nums.length) { + return target == 0; + } + if (target < 0) { + return false; + } + + return dfs(nums, i + 1, target) || + dfs(nums, i + 1, target - nums[i]); + } +} +``` + +```cpp +class Solution { +public: + bool canPartition(vector& nums) { + int sum = 0; + for (int num : nums) { + sum += num; + } + if (sum % 2 != 0) { + return false; + } + + return dfs(nums, 0, sum / 2); + } + + bool dfs(vector& nums, int i, int target) { + if (i == nums.size()) { + return target == 0; + } + if (target < 0) { + return false; + } + + return dfs(nums, i + 1, target) || + dfs(nums, i + 1, target - nums[i]); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + canPartition(nums) { + let sum = nums.reduce((a, b) => a + b, 0); + if (sum % 2 !== 0) { + return false; + } + + return this.dfs(nums, 0, sum / 2); + } + + /** + * @params {number[]} nums + * @params {number} i + * @params {number} target + * @return {boolean} + */ + dfs(nums, i, target) { + if (i === nums.length) { + return target === 0; + } + if (target < 0) { + return false; + } + + return this.dfs(nums, i + 1, target) || + this.dfs(nums, i + 1, target - nums[i]); + } +} +``` + +```csharp +public class Solution { + public bool CanPartition(int[] nums) { + int sum = 0; + for (int i = 0; i < nums.Length; i++) { + sum += nums[i]; + } + if (sum % 2 != 0) { + return false; + } + + return Dfs(nums, 0, sum / 2); + } + + public bool Dfs(int[] nums, int i, int target) { + if (i == nums.Length) { + return target == 0; + } + if (target < 0) { + return false; + } + + return Dfs(nums, i + 1, target) || + Dfs(nums, i + 1, target - nums[i]); + } +} +``` + +```go +func canPartition(nums []int) bool { + sum := 0 + for _, num := range nums { + sum += num + } + if sum%2 != 0 { + return false + } + + target := sum / 2 + + var dfs func(int, int) bool + dfs = func(i int, target int) bool { + if target == 0 { + return true + } + if i >= len(nums) || target < 0 { + return false + } + + return dfs(i+1, target) || dfs(i+1, target-nums[i]) + } + + return dfs(0, target) +} +``` + +```kotlin +class Solution { + fun canPartition(nums: IntArray): Boolean { + val sum = nums.sum() + if (sum % 2 != 0) { + return false + } + + val target = sum / 2 + + fun dfs(i: Int, target: Int): Boolean { + if (target == 0) { + return true + } + if (i >= nums.size || target < 0) { + return false + } + + return dfs(i + 1, target) || dfs(i + 1, target - nums[i]) + } + + return dfs(0, target) + } +} +``` + +```swift +class Solution { + func canPartition(_ nums: [Int]) -> Bool { + let totalSum = nums.reduce(0, +) + if totalSum % 2 != 0 { + return false + } + + let target = totalSum / 2 + let n = nums.count + + func dfs(_ i: Int, _ target: Int) -> Bool { + if i >= n { + return target == 0 + } + if target < 0 { + return false + } + + return dfs(i + 1, target) || dfs(i + 1, target - nums[i]) + } + + return dfs(0, target) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def canPartition(self, nums: List[int]) -> bool: + total = sum(nums) + if total % 2 != 0: + return False + + target = total // 2 + n = len(nums) + memo = [[-1] * (target + 1) for _ in range(n + 1)] + + def dfs(i, target): + if target == 0: + return True + if i >= n or target < 0: + return False + if memo[i][target] != -1: + return memo[i][target] + + memo[i][target] = (dfs(i + 1, target) or + dfs(i + 1, target - nums[i])) + return memo[i][target] + + return dfs(0, target) +``` + +```java +public class Solution { + Boolean[][] memo; + public boolean canPartition(int[] nums) { + int n = nums.length; + int sum = 0; + for (int i = 0; i < n; i++) { + sum += nums[i]; + } + if (sum % 2 != 0) { + return false; + } + memo = new Boolean[n][sum / 2 + 1]; + + return dfs(nums, 0, sum / 2); + } + + public boolean dfs(int[] nums, int i, int target) { + if (i == nums.length) { + return target == 0; + } + if (target < 0) { + return false; + } + if (memo[i][target] != null) { + return memo[i][target]; + } + + memo[i][target] = dfs(nums, i + 1, target) || + dfs(nums, i + 1, target - nums[i]); + return memo[i][target]; + } +} +``` + +```cpp +class Solution { +public: + vector> memo; + bool canPartition(vector& nums) { + int sum = 0; + for (int num : nums) { + sum += num; + } + if (sum % 2 != 0) { + return false; + } + memo.resize(nums.size(), vector(sum / 2 + 1, -1)); + + return dfs(nums, 0, sum / 2); + } + + bool dfs(vector& nums, int i, int target) { + if (i == nums.size()) { + return target == 0; + } + if (target < 0) { + return false; + } + if (memo[i][target] != -1) { + return memo[i][target]; + } + + memo[i][target] = dfs(nums, i + 1, target) || + dfs(nums, i + 1, target - nums[i]); + return memo[i][target]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + canPartition(nums) { + let sum = nums.reduce((a, b) => a + b, 0); + if (sum % 2 !== 0) { + return false; + } + const n = nums.length; + this.memo = Array.from(Array(n + 1), () => + Array(sum / 2 + 1).fill(null)); + + return this.dfs(nums, 0, sum / 2); + } + + /** + * @params {number[]} nums + * @params {number} i + * @params {number} target + * @return {boolean} + */ + dfs(nums, i, target) { + if (i === nums.length) { + return target === 0; + } + if (target < 0) { + return false; + } + if (this.memo[i][target] != null) { + return this.memo[i][target]; + } + + this.memo[i][target] = this.dfs(nums, i + 1, target) || + this.dfs(nums, i + 1, target - nums[i]); + return this.memo[i][target]; + } +} +``` + +```csharp +public class Solution { + private bool?[,] memo; + + public bool CanPartition(int[] nums) { + int sum = 0; + for (int i = 0; i < nums.Length; i++) { + sum += nums[i]; + } + if (sum % 2 != 0) { + return false; + } + + memo = new bool?[nums.Length + 1, sum / 2 + 1]; + return Dfs(nums, 0, sum / 2); + } + + public bool Dfs(int[] nums, int i, int target) { + if (target == 0) { + return true; + } + if (i == nums.Length || target < 0) { + return false; + } + if (memo[i, target] != null) { + return memo[i, target] == true; + } + + bool result = Dfs(nums, i + 1, target) || + Dfs(nums, i + 1, target - nums[i]); + + memo[i, target] = result; + return result; + } +} +``` + +```go +func canPartition(nums []int) bool { + total := 0 + for _, num := range nums { + total += num + } + if total%2 != 0 { + return false + } + + target := total / 2 + n := len(nums) + memo := make([][]int, n+1) + for i := range memo { + memo[i] = make([]int, target+1) + for j := range memo[i] { + memo[i][j] = -1 + } + } + + var dfs func(int, int) bool + dfs = func(i, target int) bool { + if target == 0 { + return true + } + if i >= n || target < 0 { + return false + } + if memo[i][target] != -1 { + return memo[i][target] == 1 + } + + found := dfs(i+1, target) || dfs(i+1, target-nums[i]) + if found { + memo[i][target] = 1 + } else { + memo[i][target] = 0 + } + + return found + } + + return dfs(0, target) +} +``` + +```kotlin +class Solution { + fun canPartition(nums: IntArray): Boolean { + val total = nums.sum() + if (total % 2 != 0) return false + + val target = total / 2 + val n = nums.size + val memo = Array(n + 1) { IntArray(target + 1) { -1 } } + + fun dfs(i: Int, target: Int): Boolean { + if (target == 0) return true + if (i >= n || target < 0) return false + if (memo[i][target] != -1) return memo[i][target] == 1 + + val found = dfs(i + 1, target) || dfs(i + 1, target - nums[i]) + memo[i][target] = if (found) 1 else 0 + + return found + } + + return dfs(0, target) + } +} +``` + +```swift +class Solution { + func canPartition(_ nums: [Int]) -> Bool { + let total = nums.reduce(0, +) + if total % 2 != 0 { + return false + } + + let target = total / 2 + let n = nums.count + var memo = Array(repeating: Array(repeating: -1, count: target + 1), count: n + 1) + + func dfs(_ i: Int, _ target: Int) -> Bool { + if target == 0 { + return true + } + if i >= n || target < 0 { + return false + } + if memo[i][target] != -1 { + return memo[i][target] == 1 + } + + let result = dfs(i + 1, target) || dfs(i + 1, target - nums[i]) + memo[i][target] = result ? 1 : 0 + return result + } + + return dfs(0, target) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * target)$ +* Space complexity: $O(n * target)$ + +> Where $n$ is the length of the array $nums$ and $target$ is the sum of array elements divided by 2. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def canPartition(self, nums: List[int]) -> bool: + total = sum(nums) + if total % 2 != 0: + return False + + target = total // 2 + n = len(nums) + dp = [[False] * (target + 1) for _ in range(n + 1)] + + for i in range(n + 1): + dp[i][0] = True + + for i in range(1, n + 1): + for j in range(1, target + 1): + if nums[i - 1] <= j: + dp[i][j] = (dp[i - 1][j] or + dp[i - 1][j - nums[i - 1]]) + else: + dp[i][j] = dp[i - 1][j] + + return dp[n][target] +``` + +```java +public class Solution { + public boolean canPartition(int[] nums) { + int n = nums.length; + int sum = 0; + for (int num : nums) { + sum += num; + } + if (sum % 2 != 0) { + return false; + } + + int target = sum / 2; + boolean[][] dp = new boolean[n + 1][target + 1]; + + for (int i = 0; i <= n; i++) { + dp[i][0] = true; + } + + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= target; j++) { + if (nums[i - 1] <= j) { + dp[i][j] = dp[i - 1][j] || + dp[i - 1][j - nums[i - 1]]; + } else { + dp[i][j] = dp[i - 1][j]; + } + } + } + + return dp[n][target]; + } +} +``` + +```cpp +class Solution { +public: + bool canPartition(vector& nums) { + int sum = 0; + for (int num : nums) { + sum += num; + } + if (sum % 2 != 0) { + return false; + } + + int target = sum / 2; + int n = nums.size(); + vector> dp(n + 1, vector(target + 1, false)); + + for (int i = 0; i <= n; i++) { + dp[i][0] = true; + } + + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= target; j++) { + if (nums[i - 1] <= j) { + dp[i][j] = dp[i - 1][j] || + dp[i - 1][j - nums[i - 1]]; + } else { + dp[i][j] = dp[i - 1][j]; + } + } + } + + return dp[n][target]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + canPartition(nums) { + let sum = nums.reduce((a, b) => a + b, 0); + if (sum % 2 !== 0) { + return false; + } + const target = sum / 2; + const n = nums.length; + + const dp = Array.from(Array(n + 1), () => + Array(target + 1).fill(false)); + + for (let i = 0; i <= n; i++) { + dp[i][0] = true; + } + + for (let i = 1; i <= n; i++) { + for (let j = 1; j <= target; j++) { + if (nums[i - 1] <= j) { + dp[i][j] = dp[i - 1][j] || + dp[i - 1][j - nums[i - 1]]; + } else { + dp[i][j] = dp[i - 1][j]; + } + } + } + + return dp[n][target]; + } +} +``` + +```csharp +public class Solution { + public bool CanPartition(int[] nums) { + int sum = 0; + foreach (var num in nums) { + sum += num; + } + if (sum % 2 != 0) { + return false; + } + + int target = sum / 2; + int n = nums.Length; + + bool[,] dp = new bool[n + 1, target + 1]; + + for (int i = 0; i <= n; i++) { + dp[i, 0] = true; + } + + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= target; j++) { + if (nums[i - 1] <= j) { + dp[i, j] = dp[i - 1, j] || + dp[i - 1, j - nums[i - 1]]; + } else { + dp[i, j] = dp[i - 1, j]; + } + } + } + + return dp[n, target]; + } +} +``` + +```go +func canPartition(nums []int) bool { + total := 0 + for _, num := range nums { + total += num + } + if total%2 != 0 { + return false + } + + target := total / 2 + n := len(nums) + dp := make([][]bool, n+1) + for i := range dp { + dp[i] = make([]bool, target+1) + } + + for i := 0; i <= n; i++ { + dp[i][0] = true + } + + for i := 1; i <= n; i++ { + for j := 1; j <= target; j++ { + if nums[i-1] <= j { + dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i-1]] + } else { + dp[i][j] = dp[i-1][j] + } + } + } + + return dp[n][target] +} +``` + +```kotlin +class Solution { + fun canPartition(nums: IntArray): Boolean { + val total = nums.sum() + if (total % 2 != 0) return false + + val target = total / 2 + val n = nums.size + val dp = Array(n + 1) { BooleanArray(target + 1) } + + for (i in 0..n) { + dp[i][0] = true + } + + for (i in 1..n) { + for (j in 1..target) { + dp[i][j] = if (nums[i - 1] <= j) { + dp[i - 1][j] || dp[i - 1][j - nums[i - 1]] + } else { + dp[i - 1][j] + } + } + } + + return dp[n][target] + } +} +``` + +```swift +class Solution { + func canPartition(_ nums: [Int]) -> Bool { + let total = nums.reduce(0, +) + if total % 2 != 0 { + return false + } + + let target = total / 2 + let n = nums.count + var dp = Array(repeating: Array(repeating: false, count: target + 1), count: n + 1) + + for i in 0...n { + dp[i][0] = true + } + + for i in 1...n { + for j in 1...target { + if nums[i - 1] <= j { + dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i - 1]] + } else { + dp[i][j] = dp[i - 1][j] + } + } + } + + return dp[n][target] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * target)$ +* Space complexity: $O(n * target)$ + +> Where $n$ is the length of the array $nums$ and $target$ is the sum of array elements divided by 2. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def canPartition(self, nums: List[int]) -> bool: + if sum(nums) % 2: + return False + + target = sum(nums) // 2 + dp = [False] * (target + 1) + nextDp = [False] * (target + 1) + + dp[0] = True + for i in range(len(nums)): + for j in range(1, target + 1): + if j >= nums[i]: + nextDp[j] = dp[j] or dp[j - nums[i]] + else: + nextDp[j] = dp[j] + dp, nextDp = nextDp, dp + + return dp[target] +``` + +```java +public class Solution { + public boolean canPartition(int[] nums) { + if (sum(nums) % 2 != 0) { + return false; + } + + int target = sum(nums) / 2; + boolean[] dp = new boolean[target + 1]; + boolean[] nextDp = new boolean[target + 1]; + + dp[0] = true; + for (int i = 0; i < nums.length; i++) { + for (int j = 1; j <= target; j++) { + if (j >= nums[i]) { + nextDp[j] = dp[j] || dp[j - nums[i]]; + } else { + nextDp[j] = dp[j]; + } + } + boolean[] temp = dp; + dp = nextDp; + nextDp = temp; + } + + return dp[target]; + } + + private int sum(int[] nums) { + int total = 0; + for (int num : nums) { + total += num; + } + return total; + } +} +``` + +```cpp +class Solution { +public: + bool canPartition(vector& nums) { + if (sum(nums) % 2 != 0) { + return false; + } + + int target = sum(nums) / 2; + vector dp(target + 1, false); + vector nextDp(target + 1, false); + + dp[0] = true; + for (int i = 0; i < nums.size(); i++) { + for (int j = 1; j <= target; j++) { + if (j >= nums[i]) { + nextDp[j] = dp[j] || dp[j - nums[i]]; + } else { + nextDp[j] = dp[j]; + } + } + swap(dp, nextDp); + } + + return dp[target]; + } + +private: + int sum(vector& nums) { + int total = 0; + for (int num : nums) { + total += num; + } + return total; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + canPartition(nums) { + if (nums.reduce((a, b) => a + b, 0) % 2) { + return false; + } + + const target = nums.reduce((a, b) => a + b, 0) / 2; + let dp = Array(target + 1).fill(false); + let nextDp = Array(target + 1).fill(false); + + dp[0] = true; + for (let i = 0; i < nums.length; i++) { + for (let j = 1; j <= target; j++) { + if (j >= nums[i]) { + nextDp[j] = dp[j] || dp[j - nums[i]]; + } else { + nextDp[j] = dp[j]; + } + } + [dp, nextDp] = [nextDp, dp]; + } + + return dp[target]; + } +} +``` + +```csharp +public class Solution { + public bool CanPartition(int[] nums) { + if (Sum(nums) % 2 != 0) { + return false; + } + + int target = Sum(nums) / 2; + bool[] dp = new bool[target + 1]; + bool[] nextDp = new bool[target + 1]; + + dp[0] = true; + for (int i = 0; i < nums.Length; i++) { + for (int j = 1; j <= target; j++) { + if (j >= nums[i]) { + nextDp[j] = dp[j] || dp[j - nums[i]]; + } else { + nextDp[j] = dp[j]; + } + } + bool[] temp = dp; + dp = nextDp; + nextDp = temp; + } + + return dp[target]; + } + + private int Sum(int[] nums) { + int total = 0; + foreach (var num in nums) { + total += num; + } + return total; + } +} +``` + +```go +func canPartition(nums []int) bool { + total := 0 + for _, num := range nums { + total += num + } + if total%2 != 0 { + return false + } + + target := total / 2 + dp := make([]bool, target+1) + nextDp := make([]bool, target+1) + dp[0] = true + + for i := 0; i < len(nums); i++ { + for j := 1; j <= target; j++ { + if j >= nums[i] { + nextDp[j] = dp[j] || dp[j-nums[i]] + } else { + nextDp[j] = dp[j] + } + } + dp, nextDp = nextDp, dp + } + + return dp[target] +} +``` + +```kotlin +class Solution { + fun canPartition(nums: IntArray): Boolean { + val total = nums.sum() + if (total % 2 != 0) return false + + val target = total / 2 + var dp = BooleanArray(target + 1) + var nextDp = BooleanArray(target + 1) + dp[0] = true + + for (i in nums.indices) { + for (j in 1..target) { + nextDp[j] = if (j >= nums[i]) dp[j] || dp[j - nums[i]] else dp[j] + } + val temp = dp + dp = nextDp + nextDp = temp + } + + return dp[target] + } +} +``` + +```swift +class Solution { + func canPartition(_ nums: [Int]) -> Bool { + if nums.reduce(0, +) % 2 != 0 { + return false + } + + let target = nums.reduce(0, +) / 2 + var dp = Array(repeating: false, count: target + 1) + var nextDp = Array(repeating: false, count: target + 1) + + dp[0] = true + for num in nums { + for j in stride(from: target, through: 1, by: -1) { + if j >= num { + nextDp[j] = dp[j] || dp[j - num] + } else { + nextDp[j] = dp[j] + } + } + swap(&dp, &nextDp) + } + + return dp[target] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * target)$ +* Space complexity: $O(target)$ + +> Where $n$ is the length of the array $nums$ and $target$ is the sum of array elements divided by 2. + +--- + +## 5. Dynamic Programming (Hash Set) + +::tabs-start + +```python +class Solution: + def canPartition(self, nums: List[int]) -> bool: + if sum(nums) % 2: + return False + + dp = set() + dp.add(0) + target = sum(nums) // 2 + + for i in range(len(nums) - 1, -1, -1): + nextDP = set() + for t in dp: + if (t + nums[i]) == target: + return True + nextDP.add(t + nums[i]) + nextDP.add(t) + dp = nextDP + return False +``` + +```java +public class Solution { + public boolean canPartition(int[] nums) { + if (Arrays.stream(nums).sum() % 2 != 0) { + return false; + } + + Set dp = new HashSet<>(); + dp.add(0); + int target = Arrays.stream(nums).sum() / 2; + + for (int i = nums.length - 1; i >= 0; i--) { + Set nextDP = new HashSet<>(); + for (int t : dp) { + if (t + nums[i] == target) { + return true; + } + nextDP.add(t + nums[i]); + nextDP.add(t); + } + dp = nextDP; + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool canPartition(vector& nums) { + int sum = 0; + for (int num : nums) { + sum += num; + } + if (sum % 2 != 0) { + return false; + } + + unordered_set dp; + dp.insert(0); + int target = sum / 2; + + for (int i = nums.size() - 1; i >= 0; i--) { + unordered_set nextDP; + for (int t : dp) { + if (t + nums[i] == target) { + return true; + } + nextDP.insert(t + nums[i]); + nextDP.insert(t); + } + dp = nextDP; + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + canPartition(nums) { + const sum = nums.reduce((acc, num) => acc + num, 0); + if (sum % 2 !== 0) { + return false; + } + + let dp = new Set(); + dp.add(0); + const target = sum / 2; + + for (let i = nums.length - 1; i >= 0; i--) { + const nextDP = new Set(); + for (const t of dp) { + if (t + nums[i] === target) { + return true; + } + nextDP.add(t + nums[i]); + nextDP.add(t); + } + dp = nextDP; + } + return false; + } +} +``` + +```csharp +public class Solution { + public bool CanPartition(int[] nums) { + if (nums.Sum() % 2 != 0) { + return false; + } + + HashSet dp = new HashSet(); + dp.Add(0); + int target = nums.Sum() / 2; + + for (int i = nums.Length - 1; i >= 0; i--) { + HashSet nextDP = new HashSet(); + foreach (int t in dp) { + if (t + nums[i] == target) { + return true; + } + nextDP.Add(t + nums[i]); + nextDP.Add(t); + } + dp = nextDP; + } + return false; + } +} +``` + +```go +func canPartition(nums []int) bool { + total := 0 + for _, num := range nums { + total += num + } + if total%2 != 0 { + return false + } + + target := total / 2 + dp := map[int]bool{0: true} + + for i := len(nums) - 1; i >= 0; i-- { + nextDP := map[int]bool{} + for t := range dp { + if t+nums[i] == target { + return true + } + nextDP[t+nums[i]] = true + nextDP[t] = true + } + dp = nextDP + } + + return false +} +``` + +```kotlin +class Solution { + fun canPartition(nums: IntArray): Boolean { + val total = nums.sum() + if (total % 2 != 0) return false + + val target = total / 2 + var dp = hashSetOf(0) + + for (i in nums.size - 1 downTo 0) { + val nextDP = HashSet() + for (t in dp) { + if (t + nums[i] == target) return true + nextDP.add(t + nums[i]) + nextDP.add(t) + } + dp = nextDP + } + + return false + } +} +``` + +```swift +class Solution { + func canPartition(_ nums: [Int]) -> Bool { + if nums.reduce(0, +) % 2 != 0 { + return false + } + + var dp: Set = [0] + let target = nums.reduce(0, +) / 2 + + for i in stride(from: nums.count - 1, through: 0, by: -1) { + var nextDP: Set = [] + for t in dp { + if t + nums[i] == target { + return true + } + nextDP.insert(t + nums[i]) + nextDP.insert(t) + } + dp = nextDP + } + return false + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * target)$ +* Space complexity: $O(target)$ + +> Where $n$ is the length of the array $nums$ and $target$ is the sum of array elements divided by 2. + +--- + +## 6. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def canPartition(self, nums: list[int]) -> bool: + if sum(nums) % 2: + return False + + target = sum(nums) // 2 + dp = [False] * (target + 1) + + dp[0] = True + for num in nums: + for j in range(target, num - 1, -1): + dp[j] = dp[j] or dp[j - num] + + return dp[target] +``` + +```java +public class Solution { + public boolean canPartition(int[] nums) { + if (sum(nums) % 2 != 0) { + return false; + } + + int target = sum(nums) / 2; + boolean[] dp = new boolean[target + 1]; + + dp[0] = true; + for (int i = 0; i < nums.length; i++) { + for (int j = target; j >= nums[i]; j--) { + dp[j] = dp[j] || dp[j - nums[i]]; + } + } + + return dp[target]; + } + + private int sum(int[] nums) { + int total = 0; + for (int num : nums) { + total += num; + } + return total; + } +} +``` + +```cpp +class Solution { +public: + bool canPartition(vector& nums) { + if (sum(nums) % 2 != 0) { + return false; + } + + int target = sum(nums) / 2; + vector dp(target + 1, false); + + dp[0] = true; + for (int i = 0; i < nums.size(); i++) { + for (int j = target; j >= nums[i]; j--) { + dp[j] = dp[j] || dp[j - nums[i]]; + } + } + + return dp[target]; + } + +private: + int sum(vector& nums) { + int total = 0; + for (int num : nums) { + total += num; + } + return total; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + canPartition(nums) { + if (nums.reduce((a, b) => a + b, 0) % 2) { + return false; + } + + const target = nums.reduce((a, b) => a + b, 0) / 2; + const dp = Array(target + 1).fill(false); + + dp[0] = true; + for (let i = 0; i < nums.length; i++) { + for (let j = target; j >= nums[i]; j--) { + dp[j] = dp[j] || dp[j - nums[i]]; + } + } + + return dp[target]; + } +} +``` + +```csharp +public class Solution { + public bool CanPartition(int[] nums) { + if (Sum(nums) % 2 != 0) { + return false; + } + + int target = Sum(nums) / 2; + bool[] dp = new bool[target + 1]; + + dp[0] = true; + for (int i = 0; i < nums.Length; i++) { + for (int j = target; j >= nums[i]; j--) { + dp[j] = dp[j] || dp[j - nums[i]]; + } + } + + return dp[target]; + } + + private int Sum(int[] nums) { + int total = 0; + foreach (var num in nums) { + total += num; + } + return total; + } +} +``` + +```go +func canPartition(nums []int) bool { + total := 0 + for _, num := range nums { + total += num + } + if total%2 != 0 { + return false + } + + target := total / 2 + dp := make([]bool, target+1) + dp[0] = true + + for _, num := range nums { + for j := target; j >= num; j-- { + dp[j] = dp[j] || dp[j-num] + } + } + + return dp[target] +} +``` + +```kotlin +class Solution { + fun canPartition(nums: IntArray): Boolean { + val total = nums.sum() + if (total % 2 != 0) return false + + val target = total / 2 + val dp = BooleanArray(target + 1) + dp[0] = true + + for (num in nums) { + for (j in target downTo num) { + dp[j] = dp[j] || dp[j - num] + } + } + + return dp[target] + } +} +``` + +```swift +class Solution { + func canPartition(_ nums: [Int]) -> Bool { + if nums.reduce(0, +) % 2 != 0 { + return false + } + + let target = nums.reduce(0, +) / 2 + var dp = Array(repeating: false, count: target + 1) + + dp[0] = true + for num in nums { + for j in stride(from: target, through: num, by: -1) { + dp[j] = dp[j] || dp[j - num] + } + } + + return dp[target] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * target)$ +* Space complexity: $O(target)$ + +> Where $n$ is the length of the array $nums$ and $target$ is the sum of array elements divided by 2. + +--- + +## 7. Dynamic Programming (Bitset) + +::tabs-start + +```python +class Solution: + def canPartition(self, nums: list[int]) -> bool: + total = sum(nums) + if total % 2 != 0: + return False + + target = total // 2 + dp = 1 << 0 + + for num in nums: + dp |= dp << num + + return (dp & (1 << target)) != 0 +``` + +```cpp +class Solution { +public: + bool canPartition(vector& nums) { + int sum = 0; + for (int num : nums) { + sum += num; + } + if (sum % 2 != 0) { + return false; + } + + int target = sum / 2; + bitset<10001> dp; + dp[0] = 1; + + for (int num : nums) { + dp |= dp << num; + } + + return dp[target]; + } +}; +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * target)$ +* Space complexity: $O(target)$ + +> Where $n$ is the length of the array $nums$ and $target$ is the sum of array elements divided by 2. \ No newline at end of file diff --git a/articles/partition-labels.md b/articles/partition-labels.md new file mode 100644 index 000000000..1e4e2b3a1 --- /dev/null +++ b/articles/partition-labels.md @@ -0,0 +1,204 @@ +## 1. Two Pointers (Greedy) + +::tabs-start + +```python +class Solution: + def partitionLabels(self, s: str) -> List[int]: + lastIndex = {} + for i, c in enumerate(s): + lastIndex[c] = i + + res = [] + size = end = 0 + for i, c in enumerate(s): + size += 1 + end = max(end, lastIndex[c]) + + if i == end: + res.append(size) + size = 0 + return res +``` + +```java +public class Solution { + public List partitionLabels(String s) { + Map lastIndex = new HashMap<>(); + for (int i = 0; i < s.length(); i++) { + lastIndex.put(s.charAt(i), i); + } + + List res = new ArrayList<>(); + int size = 0, end = 0; + for (int i = 0; i < s.length(); i++) { + size++; + end = Math.max(end, lastIndex.get(s.charAt(i))); + + if (i == end) { + res.add(size); + size = 0; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector partitionLabels(string s) { + unordered_map lastIndex; + for (int i = 0; i < s.size(); i++) { + lastIndex[s[i]] = i; + } + + vector res; + int size = 0, end = 0; + for (int i = 0; i < s.size(); i++) { + size++; + end = max(end, lastIndex[s[i]]); + + if (i == end) { + res.push_back(size); + size = 0; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} S + * @return {number[]} + */ + partitionLabels(S) { + let lastIndex = {}; + for (let i = 0; i < S.length; i++) { + lastIndex[S[i]] = i; + } + + let res = []; + let size = 0, end = 0; + for (let i = 0; i < S.length; i++) { + size++; + end = Math.max(end, lastIndex[S[i]]); + + if (i === end) { + res.push(size); + size = 0; + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public List PartitionLabels(string s) { + Dictionary lastIndex = new Dictionary(); + for (int i = 0; i < s.Length; i++) { + lastIndex[s[i]] = i; + } + + List res = new List(); + int size = 0, end = 0; + for (int i = 0; i < s.Length; i++) { + size++; + end = Math.Max(end, lastIndex[s[i]]); + + if (i == end) { + res.Add(size); + size = 0; + } + } + return res; + } +} +``` + +```go +func partitionLabels(s string) []int { + lastIndex := make(map[rune]int) + for i, c := range s { + lastIndex[c] = i + } + + var res []int + size, end := 0, 0 + for i, c := range s { + size++ + if lastIndex[c] > end { + end = lastIndex[c] + } + + if i == end { + res = append(res, size) + size = 0 + } + } + return res +} +``` + +```kotlin +class Solution { + fun partitionLabels(s: String): List { + val lastIndex = HashMap() + s.forEachIndexed { i, c -> lastIndex[c] = i } + + val res = mutableListOf() + var size = 0 + var end = 0 + for (i in s.indices) { + size++ + end = maxOf(end, lastIndex[s[i]] ?: 0) + + if (i == end) { + res.add(size) + size = 0 + } + } + return res + } +} +``` + +```swift +class Solution { + func partitionLabels(_ s: String) -> [Int] { + var lastIndex = [Character: Int]() + for (i, c) in s.enumerated() { + lastIndex[c] = i + } + + var res = [Int]() + var size = 0 + var end = 0 + for (i, c) in s.enumerated() { + size += 1 + end = max(end, lastIndex[c]!) + + if i == end { + res.append(size) + size = 0 + } + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the number of unique characters in the string $s$. \ No newline at end of file diff --git a/articles/partition-list.md b/articles/partition-list.md new file mode 100644 index 000000000..d3e69dd52 --- /dev/null +++ b/articles/partition-list.md @@ -0,0 +1,324 @@ +## 1. Brute Force + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def partition(self, head: Optional[ListNode], x: int) -> Optional[ListNode]: + if not head: + return None + + less, greater = [], [] + cur = head + while cur: + if cur.val < x: + less.append(cur.val) + else: + greater.append(cur.val) + cur = cur.next + + cur = head + for val in less: + cur.val = val + cur = cur.next + + for val in greater: + cur.val = val + cur = cur.next + + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode partition(ListNode head, int x) { + if (head == null) { + return null; + } + + List less = new ArrayList<>(); + List greater = new ArrayList<>(); + ListNode cur = head; + + while (cur != null) { + if (cur.val < x) { + less.add(cur.val); + } else { + greater.add(cur.val); + } + cur = cur.next; + } + + cur = head; + for (int val : less) { + cur.val = val; + cur = cur.next; + } + + for (int val : greater) { + cur.val = val; + cur = cur.next; + } + + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* partition(ListNode* head, int x) { + if (!head) return nullptr; + + vector less, greater; + ListNode* cur = head; + + while (cur) { + if (cur->val < x) { + less.push_back(cur->val); + } else { + greater.push_back(cur->val); + } + cur = cur->next; + } + + cur = head; + for (int val : less) { + cur->val = val; + cur = cur->next; + } + + for (int val : greater) { + cur->val = val; + cur = cur->next; + } + + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} x + * @return {ListNode} + */ + partition(head, x) { + if (!head) return null; + + let less = [], greater = []; + let cur = head; + + while (cur) { + if (cur.val < x) { + less.push(cur.val); + } else { + greater.push(cur.val); + } + cur = cur.next; + } + + cur = head; + for (let val of less) { + cur.val = val; + cur = cur.next; + } + + for (let val of greater) { + cur.val = val; + cur = cur.next; + } + + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Two Pointers + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def partition(self, head: Optional[ListNode], x: int) -> Optional[ListNode]: + left, right = ListNode(), ListNode() + ltail, rtail = left, right + + while head: + if head.val < x: + ltail.next = head + ltail = ltail.next + else: + rtail.next = head + rtail = rtail.next + head = head.next + + ltail.next = right.next + rtail.next = None + return left.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode partition(ListNode head, int x) { + ListNode left = new ListNode(0), right = new ListNode(0); + ListNode ltail = left, rtail = right; + + while (head != null) { + if (head.val < x) { + ltail.next = head; + ltail = ltail.next; + } else { + rtail.next = head; + rtail = rtail.next; + } + head = head.next; + } + + ltail.next = right.next; + rtail.next = null; + return left.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* partition(ListNode* head, int x) { + ListNode leftDummy(0), rightDummy(0); + ListNode *ltail = &leftDummy, *rtail = &rightDummy; + + while (head) { + if (head->val < x) { + ltail->next = head; + ltail = ltail->next; + } else { + rtail->next = head; + rtail = rtail->next; + } + head = head->next; + } + + ltail->next = rightDummy.next; + rtail->next = nullptr; + return leftDummy.next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} x + * @return {ListNode} + */ + partition(head, x) { + let left = new ListNode(0), right = new ListNode(0); + let ltail = left, rtail = right; + + while (head) { + if (head.val < x) { + ltail.next = head; + ltail = ltail.next; + } else { + rtail.next = head; + rtail = rtail.next; + } + head = head.next; + } + + ltail.next = right.next; + rtail.next = null; + return left.next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/partition-to-k-equal-sum-subsets.md b/articles/partition-to-k-equal-sum-subsets.md new file mode 100644 index 000000000..d217736df --- /dev/null +++ b/articles/partition-to-k-equal-sum-subsets.md @@ -0,0 +1,945 @@ +## 1. Backtracking + +::tabs-start + +```python +class Solution: + def canPartitionKSubsets(self, nums: List[int], k: int) -> bool: + if sum(nums) % k != 0: + return False + + nums.sort(reverse=True) + target = sum(nums) // k + used = [False] * len(nums) + + def backtrack(i, k, subsetSum): + if k == 0: + return True + if subsetSum == target: + return backtrack(0, k - 1, 0) + for j in range(i, len(nums)): + if used[j] or subsetSum + nums[j] > target: + continue + used[j] = True + if backtrack(j + 1, k, subsetSum + nums[j]): + return True + used[j] = False + return False + + return backtrack(0, k, 0) +``` + +```java +public class Solution { + private boolean[] used; + private int target; + private int n; + + public boolean canPartitionKSubsets(int[] nums, int k) { + int sum = 0; + for (int num : nums) sum += num; + if (sum % k != 0) return false; + + this.target = sum / k; + this.n = nums.length; + Arrays.sort(nums); + for (int i = 0; i < n / 2; i++) { + int tmp = nums[i]; + nums[i] = nums[n - i - 1]; + nums[n - i - 1] = tmp; + } + used = new boolean[n]; + return backtrack(nums, k, 0, 0); + } + + private boolean backtrack(int[] nums, int k, int currentSum, int start) { + if (k == 0) return true; + if (currentSum == target) return backtrack(nums, k - 1, 0, 0); + + for (int i = start; i < n; i++) { + if (used[i] || currentSum + nums[i] > target) continue; + used[i] = true; + if (backtrack(nums, k, currentSum + nums[i], i + 1)) return true; + used[i] = false; + } + return false; + } +} +``` + +```cpp +class Solution { + vector used; + int target; + +public: + bool canPartitionKSubsets(vector& nums, int k) { + int sum = accumulate(nums.begin(), nums.end(), 0); + if (sum % k != 0) return false; + + target = sum / k; + sort(nums.rbegin(), nums.rend()); + used.assign(nums.size(), false); + return backtrack(nums, k, 0, 0); + } + +private: + bool backtrack(vector& nums, int k, int currentSum, int start) { + if (k == 0) return true; + if (currentSum == target) return backtrack(nums, k - 1, 0, 0); + + for (int i = start; i < nums.size(); i++) { + if (used[i] || currentSum + nums[i] > target) continue; + used[i] = true; + if (backtrack(nums, k, currentSum + nums[i], i + 1)) return true; + used[i] = false; + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {boolean} + */ + canPartitionKSubsets(nums, k) { + const sum = nums.reduce((a, b) => a + b, 0); + if (sum % k !== 0) return false; + + const target = sum / k; + nums.sort((a, b) => b - a); + const used = Array(nums.length).fill(false); + + const backtrack = (i, k, subsetSum) => { + if (k === 0) return true; + if (subsetSum === target) return backtrack(0, k - 1, 0); + + for (let j = i; j < nums.length; j++) { + if (used[j] || subsetSum + nums[j] > target) continue; + used[j] = true; + if (backtrack(j + 1, k, subsetSum + nums[j])) return true; + used[j] = false; + } + return false; + }; + + return backtrack(0, k, 0); + } +} +``` + +```csharp +public class Solution { + public bool CanPartitionKSubsets(int[] nums, int k) { + int totalSum = nums.Sum(); + if (totalSum % k != 0) return false; + + int target = totalSum / k; + Array.Sort(nums); + Array.Reverse(nums); + + bool[] used = new bool[nums.Length]; + + bool Backtrack(int i, int kRemaining, int subsetSum) { + if (kRemaining == 0) return true; + if (subsetSum == target) return Backtrack(0, kRemaining - 1, 0); + + for (int j = i; j < nums.Length; j++) { + if (used[j] || subsetSum + nums[j] > target) continue; + + used[j] = true; + if (Backtrack(j + 1, kRemaining, subsetSum + nums[j])) return true; + used[j] = false; + } + + return false; + } + + return Backtrack(0, k, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * 2 ^ n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums$ and $k$ is the number of subsets. + +--- + +## 2. Backtracking (Pruning) + +::tabs-start + +```python +class Solution: + def canPartitionKSubsets(self, nums: List[int], k: int) -> bool: + total = sum(nums) + if total % k != 0: + return False + + nums.sort(reverse=True) + target = total // k + used = [False] * len(nums) + + def backtrack(i, k, subsetSum): + if k == 0: + return True + if subsetSum == target: + return backtrack(0, k - 1, 0) + for j in range(i, len(nums)): + if used[j] or subsetSum + nums[j] > target: + continue + used[j] = True + if backtrack(j + 1, k, subsetSum + nums[j]): + return True + used[j] = False + + if subsetSum == 0: # Pruning + return False + + return False + + return backtrack(0, k, 0) +``` + +```java +public class Solution { + private boolean[] used; + private int target; + private int n; + + public boolean canPartitionKSubsets(int[] nums, int k) { + int sum = 0; + for (int num : nums) sum += num; + if (sum % k != 0) return false; + + this.target = sum / k; + this.n = nums.length; + Arrays.sort(nums); + for (int i = 0; i < n / 2; i++) { + int tmp = nums[i]; + nums[i] = nums[n - i - 1]; + nums[n - i - 1] = tmp; + } + used = new boolean[n]; + return backtrack(nums, k, 0, 0); + } + + private boolean backtrack(int[] nums, int k, int currentSum, int start) { + if (k == 0) return true; + if (currentSum == target) return backtrack(nums, k - 1, 0, 0); + + for (int i = start; i < n; i++) { + if (used[i] || currentSum + nums[i] > target) continue; + used[i] = true; + if (backtrack(nums, k, currentSum + nums[i], i + 1)) return true; + used[i] = false; + if (currentSum == 0) { // Pruning + return false; + } + } + return false; + } +} +``` + +```cpp +class Solution { + vector used; + int target; + +public: + bool canPartitionKSubsets(vector& nums, int k) { + int sum = accumulate(nums.begin(), nums.end(), 0); + if (sum % k != 0) return false; + + target = sum / k; + sort(nums.rbegin(), nums.rend()); + used.assign(nums.size(), false); + return backtrack(nums, k, 0, 0); + } + +private: + bool backtrack(vector& nums, int k, int currentSum, int start) { + if (k == 0) return true; + if (currentSum == target) return backtrack(nums, k - 1, 0, 0); + + for (int i = start; i < nums.size(); i++) { + if (used[i] || currentSum + nums[i] > target) continue; + used[i] = true; + if (backtrack(nums, k, currentSum + nums[i], i + 1)) return true; + used[i] = false; + if (currentSum == 0) { // Pruning + return false; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {boolean} + */ + canPartitionKSubsets(nums, k) { + const sum = nums.reduce((a, b) => a + b, 0); + if (sum % k !== 0) return false; + + const target = sum / k; + nums.sort((a, b) => b - a); + const used = Array(nums.length).fill(false); + + const backtrack = (i, k, subsetSum) => { + if (k === 0) return true; + if (subsetSum === target) return backtrack(0, k - 1, 0); + + for (let j = i; j < nums.length; j++) { + if (used[j] || subsetSum + nums[j] > target) continue; + used[j] = true; + if (backtrack(j + 1, k, subsetSum + nums[j])) return true; + used[j] = false; + if (subsetSum === 0) { // Pruning + return false; + } + } + return false; + }; + + return backtrack(0, k, 0); + } +} +``` + +```csharp +public class Solution { + public bool CanPartitionKSubsets(int[] nums, int k) { + int total = nums.Sum(); + if (total % k != 0) return false; + + Array.Sort(nums); + Array.Reverse(nums); + int target = total / k; + bool[] used = new bool[nums.Length]; + + bool Backtrack(int i, int kRemaining, int subsetSum) { + if (kRemaining == 0) return true; + if (subsetSum == target) return Backtrack(0, kRemaining - 1, 0); + + for (int j = i; j < nums.Length; j++) { + if (used[j] || subsetSum + nums[j] > target) continue; + + used[j] = true; + if (Backtrack(j + 1, kRemaining, subsetSum + nums[j])) return true; + used[j] = false; + + if (subsetSum == 0) return false; // Pruning + } + + return false; + } + + return Backtrack(0, k, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * 2 ^ n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums$ and $k$ is the number of subsets. + +--- + +## 3. Backtracking (Bit Mask + Pruning) + +::tabs-start + +```python +class Solution: + def canPartitionKSubsets(self, nums: List[int], k: int) -> bool: + total = sum(nums) + if total % k != 0: + return False + + nums.sort(reverse=True) + target = total // k + n = len(nums) + + def backtrack(i, k, subsetSum, mask): + if k == 0: + return True + if subsetSum == target: + return backtrack(0, k - 1, 0, mask) + for j in range(i, n): + if (mask & (1 << j)) == 0 or subsetSum + nums[j] > target: + continue + if backtrack(j + 1, k, subsetSum + nums[j], mask ^ (1 << j)): + return True + if subsetSum == 0: + return False + return False + + return backtrack(0, k, 0, (1 << n) - 1) +``` + +```java +public class Solution { + private int target; + private int n; + + public boolean canPartitionKSubsets(int[] nums, int k) { + int total = 0; + for (int num : nums) total += num; + if (total % k != 0) return false; + + this.target = total / k; + this.n = nums.length; + Arrays.sort(nums); + reverse(nums); + + return backtrack(nums, 0, k, 0, (1 << this.n) - 1); + } + + private boolean backtrack(int[] nums, int i, int k, int subsetSum, int mask) { + if (k == 0) return true; + if (subsetSum == target) return backtrack(nums, 0, k - 1, 0, mask); + for (int j = i; j < n; j++) { + if ((mask & (1 << j)) == 0 || subsetSum + nums[j] > target) continue; + if (backtrack(nums, j + 1, k, subsetSum + nums[j], mask ^ (1 << j))) { + return true; + } + if (subsetSum == 0) return false; + } + return false; + } + + private void reverse(int[] nums) { + int l = 0, r = n - 1; + while (l < r) { + int temp = nums[l]; + nums[l++] = nums[r]; + nums[r--] = temp; + } + } +} +``` + +```cpp +class Solution { + int target, n; + +public: + bool canPartitionKSubsets(vector& nums, int k) { + int total = accumulate(nums.begin(), nums.end(), 0); + if (total % k != 0) return false; + + target = total / k; + n = nums.size(); + sort(nums.rbegin(), nums.rend()); + return backtrack(nums, 0, k, 0, (1 << n) - 1); + } + +private: + bool backtrack(vector& nums, int i, int k, int subsetSum, int mask) { + if (k == 0) return true; + if (subsetSum == target) return backtrack(nums, 0, k - 1, 0, mask); + for (int j = i; j < nums.size(); j++) { + if ((mask & (1 << j)) == 0 || subsetSum + nums[j] > target) continue; + if (backtrack(nums, j + 1, k, subsetSum + nums[j], mask ^ (1 << j))) { + return true; + } + if (subsetSum == 0) return false; + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {boolean} + */ + canPartitionKSubsets(nums, k) { + const total = nums.reduce((a, b) => a + b, 0); + if (total % k !== 0) return false; + + const target = total / k; + const n = nums.length; + nums.sort((a, b) => b - a); + + const backtrack = (i, k, subsetSum, mask) => { + if (k === 0) return true; + if (subsetSum === target) return backtrack(0, k - 1, 0, mask); + for (let j = i; j < n; j++) { + if ((mask & (1 << j)) === 0 || subsetSum + nums[j] > target) { + continue; + } + if (backtrack(j + 1, k, subsetSum + nums[j], mask ^ (1 << j))) { + return true; + } + if (subsetSum === 0) return false; + } + return false; + }; + + return backtrack(0, k, 0, (1 << n) - 1); + } +} +``` + +```csharp +public class Solution { + public bool CanPartitionKSubsets(int[] nums, int k) { + int total = nums.Sum(); + if (total % k != 0) return false; + + Array.Sort(nums); + Array.Reverse(nums); + int target = total / k; + int n = nums.Length; + + bool Backtrack(int i, int kRemaining, int subsetSum, int mask) { + if (kRemaining == 0) return true; + if (subsetSum == target) return Backtrack(0, kRemaining - 1, 0, mask); + + for (int j = i; j < n; j++) { + if ((mask & (1 << j)) == 0 || subsetSum + nums[j] > target) continue; + + if (Backtrack(j + 1, kRemaining, subsetSum + nums[j], mask ^ (1 << j))) + return true; + + if (subsetSum == 0) return false; + } + + return false; + } + + return Backtrack(0, k, 0, (1 << n) - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * 2 ^ n)$ +* Space complexity: + * $O(1)$ or $O(n)$ space depending on the sorting algorithm. + * $O(n)$ for the recursion stack. + +> Where $n$ is the size of the array $nums$ and $k$ is the number of subsets. + +--- + +## 4. Dynamic Programming (Top-Down) + Bit Mask + +::tabs-start + +```python +class Solution: + def canPartitionKSubsets(self, nums: List[int], k: int) -> bool: + total = sum(nums) + if total % k != 0: + return False + + nums.sort(reverse=True) + target = total // k + n = len(nums) + dp = [None] * (1 << n) + + def backtrack(i, k, subsetSum, mask): + if dp[mask] != None: + return dp[mask] + if k == 0: + dp[mask] = True + return True + if subsetSum == target: + dp[mask] = backtrack(0, k - 1, 0, mask) + return dp[mask] + + for j in range(i, n): + if (mask & (1 << j)) == 0 or subsetSum + nums[j] > target: + continue + if backtrack(j + 1, k, subsetSum + nums[j], mask ^ (1 << j)): + dp[mask] = True + return True + if subsetSum == 0: + dp[mask] = False + return dp[mask] + dp[mask] = False + return False + + return backtrack(0, k, 0, (1 << n) - 1) +``` + +```java +public class Solution { + private int target; + private int n; + private Boolean[] dp; + + public boolean canPartitionKSubsets(int[] nums, int k) { + int total = 0; + for (int num : nums) total += num; + if (total % k != 0) return false; + + this.target = total / k; + this.n = nums.length; + Arrays.sort(nums); + reverse(nums); + dp = new Boolean[1 << this.n]; + + return backtrack(nums, 0, k, 0, (1 << this.n) - 1); + } + + private boolean backtrack(int[] nums, int i, int k, int subsetSum, int mask) { + if (dp[mask] != null) return dp[mask]; + if (k == 0) { + dp[mask] = true; + return dp[mask]; + } + if (subsetSum == target) { + dp[mask] = backtrack(nums, 0, k - 1, 0, mask); + return dp[mask]; + } + for (int j = i; j < n; j++) { + if ((mask & (1 << j)) == 0 || subsetSum + nums[j] > target) continue; + if (backtrack(nums, j + 1, k, subsetSum + nums[j], mask ^ (1 << j))) { + dp[mask] = true; + return true; + } + if (subsetSum == 0) { + dp[mask] = false; + return false; + } + } + dp[mask] = false; + return false; + } + + private void reverse(int[] nums) { + int l = 0, r = n - 1; + while (l < r) { + int temp = nums[l]; + nums[l++] = nums[r]; + nums[r--] = temp; + } + } +} +``` + +```cpp +class Solution { + int target, n; + vector dp; + +public: + bool canPartitionKSubsets(vector& nums, int k) { + int total = accumulate(nums.begin(), nums.end(), 0); + if (total % k != 0) return false; + + target = total / k; + n = nums.size(); + dp.assign(1 << n, -1); + sort(nums.rbegin(), nums.rend()); + return backtrack(nums, 0, k, 0, (1 << n) - 1); + } + +private: + int backtrack(vector& nums, int i, int k, int subsetSum, int mask) { + if (dp[mask] != -1) return dp[mask]; + if (k == 0) { + dp[mask] = 1; + return 1; + } + if (subsetSum == target) { + dp[mask] = backtrack(nums, 0, k - 1, 0, mask); + return dp[mask]; + } + for (int j = i; j < nums.size(); j++) { + if ((mask & (1 << j)) == 0 || subsetSum + nums[j] > target) continue; + if (backtrack(nums, j + 1, k, subsetSum + nums[j], mask ^ (1 << j))) { + dp[mask] = 1; + return 1; + } + if (subsetSum == 0) { + break; + } + } + dp[mask] = 0; + return 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {boolean} + */ + canPartitionKSubsets(nums, k) { + const total = nums.reduce((a, b) => a + b, 0); + if (total % k !== 0) return false; + + const target = total / k; + const n = nums.length; + const dp = new Array(1 << n); + nums.sort((a, b) => b - a); + + const backtrack = (i, k, subsetSum, mask) => { + if (dp[mask] !== undefined) return dp[mask]; + if (k === 0) { + dp[mask] = true; + return true; + } + if (subsetSum === target) { + dp[mask] = backtrack(0, k - 1, 0, mask); + return dp[mask]; + } + for (let j = i; j < n; j++) { + if ((mask & (1 << j)) === 0 || subsetSum + nums[j] > target) { + continue; + } + if (backtrack(j + 1, k, subsetSum + nums[j], mask ^ (1 << j))) { + dp[mask] = true; + return true; + } + if (subsetSum === 0) break; + } + dp[mask] = false; + return false; + }; + + return backtrack(0, k, 0, (1 << n) - 1); + } +} +``` + +```csharp +public class Solution { + public bool CanPartitionKSubsets(int[] nums, int k) { + int total = nums.Sum(); + if (total % k != 0) return false; + + Array.Sort(nums); + Array.Reverse(nums); + + int target = total / k; + int n = nums.Length; + bool?[] dp = new bool?[1 << n]; + + bool Backtrack(int i, int kRemaining, int subsetSum, int mask) { + if (dp[mask].HasValue) return (bool)dp[mask]; + if (kRemaining == 0) { + dp[mask] = true; + return true; + } + if (subsetSum == target) { + dp[mask] = Backtrack(0, kRemaining - 1, 0, mask); + return (bool)dp[mask]; + } + + for (int j = i; j < n; j++) { + if ((mask & (1 << j)) == 0 || subsetSum + nums[j] > target) continue; + + if (Backtrack(j + 1, kRemaining, subsetSum + nums[j], mask ^ (1 << j))) { + dp[mask] = true; + return true; + } + + if (subsetSum == 0) { + dp[mask] = false; + return false; + } + } + + dp[mask] = false; + return false; + } + + return Backtrack(0, k, 0, (1 << n) - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: $O(2 ^ n)$ + +--- + +## 5. Dynamic Programming (Bottom-Up) + Bit Mask + +::tabs-start + +```python +class Solution: + def canPartitionKSubsets(self, nums: List[int], k: int) -> bool: + total = sum(nums) + if total % k != 0: + return False + + target = total // k + n = len(nums) + N = 1 << n + dp = [0] + [-1] * (N - 1) + + for mask in range(N): + if dp[mask] == -1: + continue + for i in range(n): + if (mask & (1 << i)) == 0 and dp[mask] + nums[i] <= target: + dp[mask | (1 << i)] = (dp[mask] + nums[i]) % target + + return dp[N - 1] == 0 +``` + +```java +public class Solution { + public boolean canPartitionKSubsets(int[] nums, int k) { + int total = 0; + for (int num : nums) total += num; + if (total % k != 0) return false; + + int target = total / k; + int n = nums.length; + int N = 1 << n; + int[] dp = new int[N]; + Arrays.fill(dp, -1); + dp[0] = 0; + + for (int mask = 0; mask < N; mask++) { + if (dp[mask] == -1) continue; + for (int i = 0; i < n; i++) { + if ((mask & (1 << i)) == 0 && dp[mask] + nums[i] <= target) { + dp[mask | (1 << i)] = (dp[mask] + nums[i]) % target; + } + } + } + + return dp[N - 1] == 0; + } +} +``` + +```cpp +class Solution { +public: + bool canPartitionKSubsets(vector& nums, int k) { + int total = accumulate(nums.begin(), nums.end(), 0); + if (total % k != 0) return false; + + int target = total / k; + int n = nums.size(); + int N = 1 << n; + vector dp(N, -1); + dp[0] = 0; + + for (int mask = 0; mask < N; mask++) { + if (dp[mask] == -1) continue; + for (int i = 0; i < n; i++) { + if ((mask & (1 << i)) == 0 && dp[mask] + nums[i] <= target) { + dp[mask | (1 << i)] = (dp[mask] + nums[i]) % target; + } + } + } + + return dp[N - 1] == 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {boolean} + */ + canPartitionKSubsets(nums, k) { + const total = nums.reduce((a, b) => a + b, 0); + if (total % k !== 0) return false; + + const target = total / k; + nums.sort((a, b) => b - a); + + const n = nums.length; + const N = 1 << n; + const dp = new Array(N).fill(-1); + dp[0] = 0; + + for (let mask = 0; mask < N; mask++) { + if (dp[mask] === -1) continue; + for (let i = 0; i < n; i++) { + if ((mask & (1 << i)) === 0 && dp[mask] + nums[i] <= target) { + dp[mask | (1 << i)] = (dp[mask] + nums[i]) % target; + } + } + } + + return dp[N - 1] === 0; + } +} +``` + +```csharp +public class Solution { + public bool CanPartitionKSubsets(int[] nums, int k) { + int total = nums.Sum(); + if (total % k != 0) return false; + + int target = total / k; + int n = nums.Length; + int N = 1 << n; + int[] dp = new int[N]; + for (int i = 1; i < N; i++) { + dp[i] = -1; + } + + for (int mask = 0; mask < N; mask++) { + if (dp[mask] == -1) continue; + for (int i = 0; i < n; i++) { + if ((mask & (1 << i)) == 0 && dp[mask] + nums[i] <= target) { + int nextMask = mask | (1 << i); + dp[nextMask] = (dp[mask] + nums[i]) % target; + } + } + } + + return dp[N - 1] == 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: $O(2 ^ n)$ \ No newline at end of file diff --git a/articles/pascals-triangle-ii.md b/articles/pascals-triangle-ii.md new file mode 100644 index 000000000..5a675a025 --- /dev/null +++ b/articles/pascals-triangle-ii.md @@ -0,0 +1,375 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def getRow(self, rowIndex: int) -> List[int]: + if rowIndex == 0: + return [1] + + curRow = [1] + prevRow = self.getRow(rowIndex - 1) + + for i in range(1, rowIndex): + curRow.append(prevRow[i - 1] + prevRow[i]) + + curRow.append(1) + return curRow +``` + +```java +public class Solution { + public List getRow(int rowIndex) { + if (rowIndex == 0) return Arrays.asList(1); + + List curRow = new ArrayList<>(Arrays.asList(1)); + List prevRow = getRow(rowIndex - 1); + + for (int i = 1; i < rowIndex; i++) { + curRow.add(prevRow.get(i - 1) + prevRow.get(i)); + } + + curRow.add(1); + return curRow; + } +} +``` + +```cpp +class Solution { +public: + vector getRow(int rowIndex) { + if (rowIndex == 0) return {1}; + + vector curRow = {1}; + vector prevRow = getRow(rowIndex - 1); + + for (int i = 1; i < rowIndex; i++) { + curRow.push_back(prevRow[i - 1] + prevRow[i]); + } + + curRow.push_back(1); + return curRow; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} rowIndex + * @return {number[]} + */ + getRow(rowIndex) { + if (rowIndex === 0) return [1]; + + let curRow = [1]; + let prevRow = this.getRow(rowIndex - 1); + + for (let i = 1; i < rowIndex; i++) { + curRow.push(prevRow[i - 1] + prevRow[i]); + } + + curRow.push(1); + return curRow; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def getRow(self, rowIndex: int) -> List[int]: + res = [[1] * (i + 1) for i in range(rowIndex + 1)] + for i in range(2, rowIndex + 1): + for j in range(1, i): + res[i][j] = res[i - 1][j - 1] + res[i - 1][j] + return res[rowIndex] +``` + +```java +public class Solution { + public List getRow(int rowIndex) { + List> res = new ArrayList<>(); + for (int i = 0; i <= rowIndex; i++) { + List row = new ArrayList<>(Collections.nCopies(i + 1, 1)); + for (int j = 1; j < i; j++) { + row.set(j, res.get(i - 1).get(j - 1) + res.get(i - 1).get(j)); + } + res.add(row); + } + return res.get(rowIndex); + } +} +``` + +```cpp +class Solution { +public: + vector getRow(int rowIndex) { + vector> res(rowIndex + 1); + for (int i = 0; i <= rowIndex; i++) { + res[i] = vector(i + 1, 1); + for (int j = 1; j < i; j++) { + res[i][j] = res[i - 1][j - 1] + res[i - 1][j]; + } + } + return res[rowIndex]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} rowIndex + * @return {number[]} + */ + getRow(rowIndex) { + const res = Array.from({ length: rowIndex + 1 }, (_, i) => Array(i + 1).fill(1)); + for (let i = 2; i <= rowIndex; i++) { + for (let j = 1; j < i; j++) { + res[i][j] = res[i - 1][j - 1] + res[i - 1][j]; + } + } + return res[rowIndex]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Space Optimized - I) + +::tabs-start + +```python +class Solution: + def getRow(self, rowIndex: int) -> List[int]: + res = [1] + for i in range(rowIndex): + next_row = [0] * (len(res) + 1) + for j in range(len(res)): + next_row[j] += res[j] + next_row[j + 1] += res[j] + res = next_row + return res +``` + +```java +public class Solution { + public List getRow(int rowIndex) { + List res = new ArrayList<>(); + res.add(1); + for (int i = 0; i < rowIndex; i++) { + List nextRow = new ArrayList<>(Collections.nCopies(res.size() + 1, 0)); + for (int j = 0; j < res.size(); j++) { + nextRow.set(j, nextRow.get(j) + res.get(j)); + nextRow.set(j + 1, nextRow.get(j + 1) + res.get(j)); + } + res = nextRow; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector getRow(int rowIndex) { + vector res = {1}; + for (int i = 0; i < rowIndex; i++) { + vector nextRow(res.size() + 1, 0); + for (int j = 0; j < res.size(); j++) { + nextRow[j] += res[j]; + nextRow[j + 1] += res[j]; + } + res = nextRow; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} rowIndex + * @return {number[]} + */ + getRow(rowIndex) { + let res = [1]; + for (let i = 0; i < rowIndex; i++) { + const nextRow = Array(res.length + 1).fill(0); + for (let j = 0; j < res.length; j++) { + nextRow[j] += res[j]; + nextRow[j + 1] += res[j]; + } + res = nextRow; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized - II) + +::tabs-start + +```python +class Solution: + def getRow(self, rowIndex: int) -> List[int]: + row = [1] * (rowIndex + 1) + for i in range(1, rowIndex): + for j in range(i, 0, -1): + row[j] += row[j - 1] + return row +``` + +```java +public class Solution { + public List getRow(int rowIndex) { + List row = new ArrayList<>(Collections.nCopies(rowIndex + 1, 1)); + for (int i = 1; i < rowIndex; i++) { + for (int j = i; j > 0; j--) { + row.set(j, row.get(j) + row.get(j - 1)); + } + } + return row; + } +} +``` + +```cpp +class Solution { +public: + vector getRow(int rowIndex) { + vector row(rowIndex + 1, 1); + for (int i = 1; i < rowIndex; i++) { + for (int j = i; j > 0; j--) { + row[j] += row[j - 1]; + } + } + return row; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} rowIndex + * @return {number[]} + */ + getRow(rowIndex) { + const row = Array(rowIndex + 1).fill(1); + for (let i = 1; i < rowIndex; i++) { + for (let j = i; j > 0; j--) { + row[j] += row[j - 1]; + } + } + return row; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 5. Combinatorics + +::tabs-start + +```python +class Solution: + def getRow(self, rowIndex: int) -> List[int]: + row = [1] + for i in range(1, rowIndex + 1): + row.append(row[-1] * (rowIndex - i + 1) // i) + return row +``` + +```java +public class Solution { + public List getRow(int rowIndex) { + List row = new ArrayList<>(); + row.add(1); + for (int i = 1; i <= rowIndex; i++) { + row.add((int)((long)row.get(row.size() - 1) * (rowIndex - i + 1) / i)); + } + return row; + } +} +``` + +```cpp +class Solution { +public: + vector getRow(int rowIndex) { + vector row = {1}; + for (int i = 1; i <= rowIndex; i++) { + row.push_back(int(row.back() * 1LL * (rowIndex - i + 1) / i)); + } + return row; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} rowIndex + * @return {number[]} + */ + getRow(rowIndex) { + const row = [1]; + for (let i = 1; i <= rowIndex; i++) { + row.push(Math.floor(row[row.length - 1] * (rowIndex - i + 1) / i)); + } + return row; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/pascals-triangle.md b/articles/pascals-triangle.md new file mode 100644 index 000000000..2254d3bbf --- /dev/null +++ b/articles/pascals-triangle.md @@ -0,0 +1,260 @@ +## 1. Combinatorics + +::tabs-start + +```python +class Solution: + def generate(self, numRows: int) -> List[List[int]]: + res = [] + for n in range(numRows): + row = [1] + val = 1 + for k in range(1, n + 1): + val = val * (n - k + 1) // k + row.append(val) + res.append(row) + return res +``` + +```java +public class Solution { + public List> generate(int numRows) { + List> res = new ArrayList<>(); + for (int n = 0; n < numRows; n++) { + List row = new ArrayList<>(); + row.add(1); + int val = 1; + for (int k = 1; k <= n; k++) { + val = val * (n - k + 1) / k; + row.add(val); + } + res.add(row); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> generate(int numRows) { + vector> res; + for (int n = 0; n < numRows; n++) { + vector row; + row.push_back(1); + int val = 1; + for (int k = 1; k <= n; k++) { + val = val * (n - k + 1) / k; + row.push_back(val); + } + res.push_back(row); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} numRows + * @return {number[][]} + */ + generate(numRows) { + let res = []; + for (let n = 0; n < numRows; n++) { + let row = [1]; + let val = 1; + for (let k = 1; k <= n; k++) { + val = (val * (n - k + 1)) / k; + row.push(val); + } + res.push(row); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Dynamic Programming - I + +::tabs-start + +```python +class Solution: + def generate(self, numRows: int) -> List[List[int]]: + res = [[1]] + + for i in range(numRows - 1): + temp = [0] + res[-1] + [0] + row = [] + for j in range(len(res[-1]) + 1): + row.append(temp[j] + temp[j + 1]) + res.append(row) + return res +``` + +```java +public class Solution { + public List> generate(int numRows) { + List> res = new ArrayList<>(); + res.add(new ArrayList<>()); + res.get(0).add(1); + + for (int i = 1; i < numRows; i++) { + List temp = new ArrayList<>(res.get(i - 1)); + temp.add(0, 0); + temp.add(0); + List row = new ArrayList<>(); + + for (int j = 0; j < res.get(i - 1).size() + 1; j++) { + row.add(temp.get(j) + temp.get(j + 1)); + } + + res.add(row); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> generate(int numRows) { + vector> res = {{1}}; + + for (int i = 0; i < numRows - 1; i++) { + vector temp = {0}; + temp.insert(temp.end(), res.back().begin(), res.back().end()); + temp.push_back(0); + vector row; + for (size_t j = 0; j < res.back().size() + 1; j++) { + row.push_back(temp[j] + temp[j + 1]); + } + res.push_back(row); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} numRows + * @return {number[][]} + */ + generate(numRows) { + let res = [[1]]; + + for (let i = 0; i < numRows - 1; i++) { + let temp = [0, ...res[res.length - 1], 0]; + let row = []; + for (let j = 0; j < res[res.length - 1].length + 1; j++) { + row.push(temp[j] + temp[j + 1]); + } + res.push(row); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming - II + +::tabs-start + +```python +class Solution: + def generate(self, numRows: int) -> List[List[int]]: + res = [[1] * (i + 1) for i in range(numRows)] + for i in range(2, numRows): + for j in range(1, i): + res[i][j] = res[i - 1][j - 1] + res[i - 1][j] + return res +``` + +```java +public class Solution { + public List> generate(int numRows) { + List> res = new ArrayList<>(); + + for (int i = 0; i < numRows; i++) { + List row = new ArrayList<>(); + for (int j = 0; j <= i; j++) { + if (j == 0 || j == i) { + row.add(1); + } else { + row.add(res.get(i - 1).get(j - 1) + res.get(i - 1).get(j)); + } + } + res.add(row); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> generate(int numRows) { + vector> res(numRows); + + for (int i = 0; i < numRows; i++) { + res[i].resize(i + 1); + res[i][0] = res[i][i] = 1; + for (int j = 1; j < i; j++){ + res[i][j] = res[i - 1][j - 1] + res[i - 1][j]; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} numRows + * @return {number[][]} + */ + generate(numRows) { + let res = Array.from({ length: numRows }, (_, i) => Array(i + 1).fill(1)); + + for (let i = 2; i < numRows; i++) { + for (let j = 1; j < i; j++) { + res[i][j] = res[i - 1][j - 1] + res[i - 1][j]; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/path-crossing.md b/articles/path-crossing.md new file mode 100644 index 000000000..dd4abbaf2 --- /dev/null +++ b/articles/path-crossing.md @@ -0,0 +1,242 @@ +## 1. Hash Set + +::tabs-start + +```python +class Solution: + def isPathCrossing(self, path: str) -> bool: + dir = { + 'N': [0, 1], + 'S': [0, -1], + 'E': [1, 0], + 'W': [-1, 0] + } + + visit = set() + x, y = 0, 0 + + for c in path: + visit.add((x, y)) + dx, dy = dir[c] + x, y = x + dx, y + dy + if (x, y) in visit: + return True + + return False +``` + +```java +public class Solution { + public boolean isPathCrossing(String path) { + Set visit = new HashSet<>(); + int x = 0, y = 0; + visit.add(x + "," + y); + + for (char c : path.toCharArray()) { + if (c == 'N') y++; + else if (c == 'S') y--; + else if (c == 'E') x++; + else if (c == 'W') x--; + + String pos = x + "," + y; + if (visit.contains(pos)) return true; + visit.add(pos); + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool isPathCrossing(string path) { + unordered_set visit; + int x = 0, y = 0; + visit.insert(to_string(x) + "," + to_string(y)); + + for (char c : path) { + if (c == 'N') y++; + else if (c == 'S') y--; + else if (c == 'E') x++; + else if (c == 'W') x--; + + string pos = to_string(x) + "," + to_string(y); + if (visit.count(pos)) return true; + visit.insert(pos); + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} path + * @return {boolean} + */ + isPathCrossing(path) { + const visit = new Set(); + let x = 0, y = 0; + visit.add(`${x},${y}`); + + for (const c of path) { + if (c === 'N') y++; + else if (c === 'S') y--; + else if (c === 'E') x++; + else if (c === 'W') x--; + + const pos = `${x},${y}`; + if (visit.has(pos)) return true; + visit.add(pos); + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Hash Set (Custom Hash) + +::tabs-start + +```python +class Solution: + def isPathCrossing(self, path: str) -> bool: + visit = set() + x, y = 0, 0 + visit.add(self.hash(x, y)) + + for c in path: + if c == 'N': + y += 1 + elif c == 'S': + y -= 1 + elif c == 'E': + x += 1 + elif c == 'W': + x -= 1 + + pos = self.hash(x, y) + if pos in visit: + return True + visit.add(pos) + + return False + + def hash(self, x: int, y: int) -> int: + return (x << 32) + y +``` + +```java +public class Solution { + public boolean isPathCrossing(String path) { + Set visit = new HashSet<>(); + int x = 0, y = 0; + visit.add(hash(x, y)); + + for (char c : path.toCharArray()) { + if (c == 'N') y++; + else if (c == 'S') y--; + else if (c == 'E') x++; + else if (c == 'W') x--; + + long pos = hash(x, y); + if (visit.contains(pos)) return true; + visit.add(pos); + } + + return false; + } + + private long hash(long x, long y) { + return (x << 32) + y; + } +} +``` + +```cpp +class Solution { +public: + bool isPathCrossing(string path) { + unordered_set, pair_hash> visit; + int x = 0, y = 0; + visit.insert({x, y}); + + for (char c : path) { + if (c == 'N') y++; + else if (c == 'S') y--; + else if (c == 'E') x++; + else if (c == 'W') x--; + + if (visit.count({x, y})) return true; + visit.insert({x, y}); + } + + return false; + } + +private: + struct pair_hash { + template + size_t operator()(const pair& p) const { + return (hash()(p.first) << 32) + hash()(p.second); + } + }; +}; +``` + +```javascript +class Solution { + /** + * @param {string} path + * @return {boolean} + */ + isPathCrossing(path) { + const visit = new Set(); + let x = 0, y = 0; + visit.add(this.hash(x, y)); + + for (const c of path) { + if (c === 'N') y++; + else if (c === 'S') y--; + else if (c === 'E') x++; + else if (c === 'W') x--; + + const pos = this.hash(x, y); + if (visit.has(pos)) return true; + visit.add(pos); + } + + return false; + } + + /** + * @param {number} x + * @param {number} x + * @return {number} + */ + hash(x, y) { + return (x << 16) + y; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/path-sum.md b/articles/path-sum.md new file mode 100644 index 000000000..ce01351cf --- /dev/null +++ b/articles/path-sum.md @@ -0,0 +1,581 @@ +## 1. Depth First Search - I + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool: + def dfs(node, curSum): + if not node: + return False + + curSum += node.val + if not node.left and not node.right: + return curSum == targetSum + + return dfs(node.left, curSum) or dfs(node.right, curSum) + + return dfs(root, 0) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean hasPathSum(TreeNode root, int targetSum) { + return dfs(root, 0, targetSum); + } + + private boolean dfs(TreeNode node, int curSum, int targetSum) { + if (node == null) return false; + + curSum += node.val; + if (node.left == null && node.right == null) { + return curSum == targetSum; + } + + return dfs(node.left, curSum, targetSum) || dfs(node.right, curSum, targetSum); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool hasPathSum(TreeNode* root, int targetSum) { + return dfs(root, 0, targetSum); + } + +private: + bool dfs(TreeNode* node, int curSum, int targetSum) { + if (node == nullptr) return false; + + curSum += node->val; + if (node->left == nullptr && node->right == nullptr) { + return curSum == targetSum; + } + + return dfs(node->left, curSum, targetSum) || dfs(node->right, curSum, targetSum); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} targetSum + * @return {boolean} + */ + hasPathSum(root, targetSum) { + const dfs = (node, curSum) => { + if (!node) return false; + + curSum += node.val; + if (!node.left && !node.right) { + return curSum === targetSum; + } + + return dfs(node.left, curSum) || dfs(node.right, curSum); + }; + + return dfs(root, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Depth First Search - II + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool: + if not root: + return False + + targetSum -= root.val + return (self.hasPathSum(root.left, targetSum) or + self.hasPathSum(root.right, targetSum) or + (not targetSum and not root.left and not root.right)) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean hasPathSum(TreeNode root, int targetSum) { + if (root == null) return false; + targetSum -= root.val; + return hasPathSum(root.left, targetSum) || + hasPathSum(root.right, targetSum) || + (targetSum == 0 && root.left == null && root.right == null); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool hasPathSum(TreeNode* root, int targetSum) { + if (!root) return false; + targetSum -= root->val; + return hasPathSum(root->left, targetSum) || + hasPathSum(root->right, targetSum) || + (targetSum == 0 && !root->left && !root->right); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} targetSum + * @return {boolean} + */ + hasPathSum(root, targetSum) { + if (!root) return false; + targetSum -= root.val; + return this.hasPathSum(root.left, targetSum) || + this.hasPathSum(root.right, targetSum) || + (targetSum === 0 && !root.left && !root.right); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool: + if not root: + return False + + stack = [(root, targetSum - root.val)] + while stack: + node, curr_sum = stack.pop() + if not node.left and not node.right and curr_sum == 0: + return True + if node.right: + stack.append((node.right, curr_sum - node.right.val)) + if node.left: + stack.append((node.left, curr_sum - node.left.val)) + return False +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean hasPathSum(TreeNode root, int targetSum) { + if (root == null) return false; + + Stack stack = new Stack<>(); + Stack sumStack = new Stack<>(); + stack.push(root); + sumStack.push(targetSum - root.val); + + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + int currSum = sumStack.pop(); + + if (node.left == null && node.right == null && currSum == 0) { + return true; + } + + if (node.right != null) { + stack.push(node.right); + sumStack.push(currSum - node.right.val); + } + + if (node.left != null) { + stack.push(node.left); + sumStack.push(currSum - node.left.val); + } + } + + return false; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool hasPathSum(TreeNode* root, int targetSum) { + if (!root) return false; + + stack> s; + s.push({root, targetSum - root->val}); + + while (!s.empty()) { + auto [node, currSum] = s.top(); + s.pop(); + + if (!node->left && !node->right && currSum == 0) { + return true; + } + + if (node->right) { + s.push({node->right, currSum - node->right->val}); + } + + if (node->left) { + s.push({node->left, currSum - node->left->val}); + } + } + + return false; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} targetSum + * @return {boolean} + */ + hasPathSum(root, targetSum) { + if (!root) return false; + + const stack = [[root, targetSum - root.val]]; + while (stack.length) { + const [node, currSum] = stack.pop(); + + if (!node.left && !node.right && currSum === 0) { + return true; + } + + if (node.right) { + stack.push([node.right, currSum - node.right.val]); + } + + if (node.left) { + stack.push([node.left, currSum - node.left.val]); + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool: + if not root: + return False + + queue = deque([(root, targetSum - root.val)]) + while queue: + node, curr_sum = queue.popleft() + if not node.left and not node.right and curr_sum == 0: + return True + if node.left: + queue.append((node.left, curr_sum - node.left.val)) + if node.right: + queue.append((node.right, curr_sum - node.right.val)) + return False +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean hasPathSum(TreeNode root, int targetSum) { + if (root == null) return false; + + Queue nodeQueue = new LinkedList<>(); + Queue sumQueue = new LinkedList<>(); + nodeQueue.add(root); + sumQueue.add(targetSum - root.val); + + while (!nodeQueue.isEmpty()) { + TreeNode node = nodeQueue.poll(); + int currSum = sumQueue.poll(); + + if (node.left == null && node.right == null && currSum == 0) { + return true; + } + + if (node.left != null) { + nodeQueue.add(node.left); + sumQueue.add(currSum - node.left.val); + } + + if (node.right != null) { + nodeQueue.add(node.right); + sumQueue.add(currSum - node.right.val); + } + } + + return false; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool hasPathSum(TreeNode* root, int targetSum) { + if (!root) return false; + + queue> q; + q.push({root, targetSum - root->val}); + + while (!q.empty()) { + auto [node, currSum] = q.front(); + q.pop(); + + if (!node->left && !node->right && currSum == 0) { + return true; + } + + if (node->left) { + q.push({node->left, currSum - node->left->val}); + } + + if (node->right) { + q.push({node->right, currSum - node->right->val}); + } + } + + return false; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} targetSum + * @return {boolean} + */ + hasPathSum(root, targetSum) { + if (!root) return false; + + const queue = new Queue([[root, targetSum - root.val]]); + while (!queue.isEmpty()) { + const [node, currSum] = queue.pop(); + + if (!node.left && !node.right && currSum === 0) { + return true; + } + + if (node.left) { + queue.push([node.left, currSum - node.left.val]); + } + + if (node.right) { + queue.push([node.right, currSum - node.right.val]); + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/path-with-maximum-gold.md b/articles/path-with-maximum-gold.md new file mode 100644 index 000000000..81c3d201d --- /dev/null +++ b/articles/path-with-maximum-gold.md @@ -0,0 +1,508 @@ +## 1. Backtracking (DFS) - I + +::tabs-start + +```python +class Solution: + def getMaximumGold(self, grid: list[list[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + + def dfs(r, c, visit): + if min(r, c) < 0 or r == ROWS or c == COLS or grid[r][c] == 0 or (r, c) in visit: + return 0 + + visit.add((r, c)) + res = grid[r][c] + + for dr, dc in directions: + res = max(res, grid[r][c] + dfs(r + dr, c + dc, visit)) + + visit.remove((r, c)) + return res + + res = 0 + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] != 0: + res = max(res, dfs(r, c, set())) + return res +``` + +```java +public class Solution { + private int ROWS, COLS; + private int[][] directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + + public int getMaximumGold(int[][] grid) { + ROWS = grid.length; + COLS = grid[0].length; + int res = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] != 0) { + res = Math.max(res, dfs(grid, r, c, new boolean[ROWS][COLS])); + } + } + } + return res; + } + + private int dfs(int[][] grid, int r, int c, boolean[][] visit) { + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || + grid[r][c] == 0 || visit[r][c]) { + return 0; + } + + visit[r][c] = true; + int res = grid[r][c]; + + for (int[] d : directions) { + res = Math.max(res, grid[r][c] + dfs(grid, r + d[0], c + d[1], visit)); + } + + visit[r][c] = false; + return res; + } +} +``` + +```cpp +class Solution { +public: + int ROWS, COLS; + vector> directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + + int getMaximumGold(vector>& grid) { + ROWS = grid.size(); + COLS = grid[0].size(); + int res = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] != 0) { + vector> visit(ROWS, vector(COLS, false)); + res = max(res, dfs(grid, r, c, visit)); + } + } + } + return res; + } + +private: + int dfs(vector>& grid, int r, int c, vector>& visit) { + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || grid[r][c] == 0 || visit[r][c]) { + return 0; + } + + visit[r][c] = true; + int res = grid[r][c]; + + for (auto& d : directions) { + res = max(res, grid[r][c] + dfs(grid, r + d[0], c + d[1], visit)); + } + + visit[r][c] = false; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + getMaximumGold(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]; + + const dfs = (r, c, visit) => { + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || grid[r][c] === 0 || visit[r][c]) { + return 0; + } + + visit[r][c] = true; + let res = grid[r][c]; + + for (const [dr, dc] of directions) { + res = Math.max(res, grid[r][c] + dfs(r + dr, c + dc, visit)); + } + + visit[r][c] = false; + return res; + }; + + let res = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] !== 0) { + let visit = Array.from({ length: ROWS }, () => Array(COLS).fill(false)); + res = Math.max(res, dfs(r, c, visit)); + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N * 3 ^ N)$ +* Space complexity: $O(N)$ + +> Where $N$ is the number of cells which contain gold. + +--- + +## 2. Backtracking (DFS) - II + +::tabs-start + +```python +class Solution: + def getMaximumGold(self, grid: list[list[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + + def dfs(r, c): + if min(r, c) < 0 or r == ROWS or c == COLS or grid[r][c] == 0: + return 0 + + gold = grid[r][c] + grid[r][c] = 0 + res = 0 + + for dr, dc in directions: + res = max(res, dfs(r + dr, c + dc)) + + grid[r][c] = gold + return gold + res + + res = 0 + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] != 0: + res = max(res, dfs(r, c)) + return res +``` + +```java +public class Solution { + private int ROWS, COLS; + private int[][] directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + + public int getMaximumGold(int[][] grid) { + ROWS = grid.length; + COLS = grid[0].length; + int res = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] != 0) { + res = Math.max(res, dfs(grid, r, c)); + } + } + } + return res; + } + + private int dfs(int[][] grid, int r, int c) { + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || grid[r][c] == 0) { + return 0; + } + + int gold = grid[r][c]; + grid[r][c] = 0; + int res = 0; + + for (int[] d : directions) { + res = Math.max(res, dfs(grid, r + d[0], c + d[1])); + } + + grid[r][c] = gold; + return gold + res; + } +} +``` + +```cpp +class Solution { +public: + int ROWS, COLS; + vector> directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + + int getMaximumGold(vector>& grid) { + ROWS = grid.size(); + COLS = grid[0].size(); + int res = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] != 0) { + res = max(res, dfs(grid, r, c)); + } + } + } + return res; + } + +private: + int dfs(vector>& grid, int r, int c) { + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || grid[r][c] == 0) { + return 0; + } + + int gold = grid[r][c]; + grid[r][c] = 0; + int res = 0; + + for (auto& d : directions) { + res = max(res, dfs(grid, r + d[0], c + d[1])); + } + + grid[r][c] = gold; + return gold + res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + getMaximumGold(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]; + + const dfs = (r, c) => { + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || grid[r][c] === 0) { + return 0; + } + + let gold = grid[r][c]; + grid[r][c] = 0; + let res = 0; + + for (const [dr, dc] of directions) { + res = Math.max(res, dfs(r + dr, c + dc)); + } + + grid[r][c] = gold; + return gold + res; + }; + + let res = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] !== 0) { + res = Math.max(res, dfs(r, c)); + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N * 3 ^ N)$ +* Space complexity: $O(N)$ for recursion stack. + +> Where $N$ is the number of cells which contain gold. + +--- + +## 3. Backtracking (BFS) + +::tabs-start + +```python +class Solution: + def getMaximumGold(self, grid: list[list[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + directions = [1, 0, -1, 0, 1] + index = [[0] * COLS for _ in range(ROWS)] + idx = 0 + + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] != 0: + index[r][c] = idx + idx += 1 + + res = 0 + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] > 0: + q = deque([(r, c, grid[r][c], 1 << index[r][c])]) + while q: + row, col, gold, mask = q.popleft() + res = max(res, gold) + + for i in range(4): + nr, nc = row + directions[i], col + directions[i + 1] + if 0 <= nr < ROWS and 0 <= nc < COLS and grid[nr][nc] > 0: + idx = index[nr][nc] + if not (mask & (1 << idx)): + q.append((nr, nc, gold + grid[nr][nc], mask | (1 << idx))) + + return res +``` + +```java +public class Solution { + public int getMaximumGold(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int[][] index = new int[ROWS][COLS]; + int idx = 0; + int[] directions = {1, 0, -1, 0, 1}; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] != 0) { + index[r][c] = idx++; + } + } + } + + int res = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] > 0) { + Queue q = new LinkedList<>(); + q.offer(new int[]{r, c, grid[r][c], 1 << index[r][c]}); + + while (!q.isEmpty()) { + int[] cur = q.poll(); + int row = cur[0], col = cur[1], gold = cur[2], mask = cur[3]; + res = Math.max(res, gold); + + for (int i = 0; i < 4; i++) { + int nr = row + directions[i], nc = col + directions[i + 1]; + if (nr >= 0 && nr < ROWS && nc >= 0 && nc < COLS && grid[nr][nc] > 0) { + int newIdx = index[nr][nc]; + if ((mask & (1 << newIdx)) == 0) { + q.offer(new int[]{nr, nc, gold + grid[nr][nc], mask | (1 << newIdx)}); + } + } + } + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int getMaximumGold(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + vector> index(ROWS, vector(COLS, 0)); + int idx = 0; + int directions[] = {1, 0, -1, 0, 1}; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] != 0) { + index[r][c] = idx++; + } + } + } + + int res = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] > 0) { + queue> q; + q.push({r, c, grid[r][c], 1 << index[r][c]}); + + while (!q.empty()) { + auto [row, col, gold, mask] = q.front();q.pop(); + res = max(res, gold); + for (int i = 0; i < 4; i++) { + int nr = row + directions[i], nc = col + directions[i + 1]; + if (nr >= 0 && nr < ROWS && nc >= 0 && nc < COLS && grid[nr][nc] > 0) { + int newIdx = index[nr][nc]; + if ((mask & (1 << newIdx)) == 0) { + q.push({nr, nc, gold + grid[nr][nc], mask | (1 << newIdx)}); + } + } + } + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + getMaximumGold(grid) { + const ROWS = grid.length, COLS = grid[0].length; + const index = Array.from({ length: ROWS }, () => Array(COLS).fill(0)); + let idx = 0; + const directions = [1, 0, -1, 0, 1]; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] !== 0) { + index[r][c] = idx++; + } + } + } + + let res = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] > 0) { + const q = new Queue([[r, c, grid[r][c], 1 << index[r][c]]]); + + while (!q.isEmpty()) { + const [row, col, gold, mask] = q.pop(); + res = Math.max(res, gold); + for (let i = 0; i < 4; i++) { + const nr = row + directions[i], nc = col + directions[i + 1]; + if (nr >= 0 && nr < ROWS && nc >= 0 && nc < COLS && grid[nr][nc] > 0) { + const newIdx = index[nr][nc]; + if (!(mask & (1 << newIdx))) { + q.push([nr, nc, gold + grid[nr][nc], mask | (1 << newIdx)]); + } + } + } + } + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N * 3 ^ N)$ +* Space complexity: $O(N)$ + +> Where $N$ is the number of cells which contain gold. \ No newline at end of file diff --git a/articles/path-with-maximum-probability.md b/articles/path-with-maximum-probability.md new file mode 100644 index 000000000..b16d1e43b --- /dev/null +++ b/articles/path-with-maximum-probability.md @@ -0,0 +1,632 @@ +## 1. Dijkstra's Algorithm - I + +::tabs-start + +```python +class Solution: + def maxProbability(self, n: int, edges: List[List[int]], succProb: List[float], start_node: int, end_node: int) -> float: + adj = collections.defaultdict(list) + for i in range(len(edges)): + src, dst = edges[i] + adj[src].append((dst, succProb[i])) + adj[dst].append((src, succProb[i])) + + pq = [(-1, start_node)] + visit = set() + + while pq: + prob, cur = heapq.heappop(pq) + visit.add(cur) + + if cur == end_node: + return -prob + + for nei, edgeProb in adj[cur]: + if nei not in visit: + heapq.heappush(pq, (prob * edgeProb, nei)) + + return 0.0 +``` + +```java +public class Solution { + public double maxProbability(int n, int[][] edges, double[] succProb, int start_node, int end_node) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + + for (int i = 0; i < edges.length; i++) { + int src = edges[i][0], dst = edges[i][1]; + adj[src].add(new Pair(dst, succProb[i])); + adj[dst].add(new Pair(src, succProb[i])); + } + + double[] maxProb = new double[n]; + maxProb[start_node] = 1.0; + PriorityQueue pq = new PriorityQueue<>((a, b) -> Double.compare(b.prob, a.prob)); + pq.offer(new Pair(start_node, 1.0)); + + while (!pq.isEmpty()) { + Pair top = pq.poll(); + int node = top.node; + double curr_prob = top.prob; + + if (node == end_node) return curr_prob; + if (curr_prob > maxProb[node]) continue; + + for (Pair nei : adj[node]) { + double new_prob = curr_prob * nei.prob; + if (new_prob > maxProb[nei.node]) { + maxProb[nei.node] = new_prob; + pq.offer(new Pair(nei.node, new_prob)); + } + } + } + + return 0.0; + } + + static class Pair { + int node; + double prob; + + Pair(int node, double prob) { + this.node = node; + this.prob = prob; + } + } +} +``` + +```cpp +class Solution { +public: + double maxProbability(int n, vector>& edges, vector& succProb, int start_node, int end_node) { + vector>> adj(n); + for (int i = 0; i < edges.size(); i++) { + int src = edges[i][0], dst = edges[i][1]; + adj[src].emplace_back(dst, succProb[i]); + adj[dst].emplace_back(src, succProb[i]); + } + + vector maxProb(n, 0.0); + maxProb[start_node] = 1.0; + priority_queue> pq; + pq.emplace(1.0, start_node); + + while (!pq.empty()) { + auto [curr_prob, node] = pq.top(); pq.pop(); + + if (node == end_node) return curr_prob; + if (curr_prob > maxProb[node]) continue; + + for (auto& [nei, edge_prob] : adj[node]) { + double new_prob = curr_prob * edge_prob; + if (new_prob > maxProb[nei]) { + maxProb[nei] = new_prob; + pq.emplace(new_prob, nei); + } + } + } + + return 0.0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @param {number[]} succProb + * @param {number} start_node + * @param {number} end_node + * @return {number} + */ + maxProbability(n, edges, succProb, start_node, end_node) { + let adj = new Map(); + for (let i = 0; i < n; i++) adj.set(i, []); + + for (let i = 0; i < edges.length; i++) { + let [src, dst] = edges[i]; + adj.get(src).push([dst, succProb[i]]); + adj.get(dst).push([src, succProb[i]]); + } + + let pq = new MaxPriorityQueue({ priority: x => x[0] }); + pq.enqueue([1.0, start_node]); + let visited = new Set(); + + while (!pq.isEmpty()) { + let [prob, cur] = pq.dequeue().element; + visited.add(cur); + + if (cur === end_node) return prob; + + for (let [nei, edgeProb] of adj.get(cur)) { + if (!visited.has(nei)) { + pq.enqueue([prob * edgeProb, nei]); + } + } + } + + return 0.0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((V + E) \log V)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number nodes and $E$ is the number of edges. + +--- + +## 2. Dijkstra's Algorithm - II + +::tabs-start + +```python +class Solution: + def maxProbability(self, n: int, edges: List[List[int]], succProb: List[float], start_node: int, end_node: int) -> float: + adj = [[] for _ in range(n)] + for i in range(len(edges)): + src, dst = edges[i] + adj[src].append((dst, succProb[i])) + adj[dst].append((src, succProb[i])) + + maxProb = [0] * n + maxProb[start_node] = 1.0 + pq = [(-1.0, start_node)] + + while pq: + curr_prob, node = heapq.heappop(pq) + curr_prob *= -1 + + if node == end_node: + return curr_prob + if curr_prob > maxProb[node]: + continue + + for nei, edge_prob in adj[node]: + new_prob = curr_prob * edge_prob + if new_prob > maxProb[nei]: + maxProb[nei] = new_prob + heapq.heappush(pq, (-new_prob, nei)) + + return 0.0 +``` + +```java +public class Solution { + public double maxProbability(int n, int[][] edges, double[] succProb, int start_node, int end_node) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + + for (int i = 0; i < edges.length; i++) { + int src = edges[i][0], dst = edges[i][1]; + adj[src].add(new Pair(dst, succProb[i])); + adj[dst].add(new Pair(src, succProb[i])); + } + + double[] maxProb = new double[n]; + maxProb[start_node] = 1.0; + PriorityQueue pq = new PriorityQueue<>((a, b) -> Double.compare(b.prob, a.prob)); + pq.offer(new Pair(start_node, 1.0)); + + while (!pq.isEmpty()) { + Pair top = pq.poll(); + int node = top.node; + double curr_prob = top.prob; + + if (node == end_node) return curr_prob; + if (curr_prob > maxProb[node]) continue; + + for (Pair nei : adj[node]) { + double new_prob = curr_prob * nei.prob; + if (new_prob > maxProb[nei.node]) { + maxProb[nei.node] = new_prob; + pq.offer(new Pair(nei.node, new_prob)); + } + } + } + + return 0.0; + } + + static class Pair { + int node; + double prob; + + Pair(int node, double prob) { + this.node = node; + this.prob = prob; + } + } +} +``` + +```cpp +class Solution { +public: + double maxProbability(int n, vector>& edges, vector& succProb, int start_node, int end_node) { + vector>> adj(n); + for (int i = 0; i < edges.size(); i++) { + int src = edges[i][0], dst = edges[i][1]; + adj[src].emplace_back(dst, succProb[i]); + adj[dst].emplace_back(src, succProb[i]); + } + + vector maxProb(n, 0.0); + maxProb[start_node] = 1.0; + priority_queue> pq; + pq.emplace(1.0, start_node); + + while (!pq.empty()) { + auto [curr_prob, node] = pq.top(); pq.pop(); + + if (node == end_node) return curr_prob; + if (curr_prob > maxProb[node]) continue; + + for (auto& [nei, edge_prob] : adj[node]) { + double new_prob = curr_prob * edge_prob; + if (new_prob > maxProb[nei]) { + maxProb[nei] = new_prob; + pq.emplace(new_prob, nei); + } + } + } + + return 0.0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @param {number[]} succProb + * @param {number} start_node + * @param {number} end_node + * @return {number} + */ + maxProbability(n, edges, succProb, start_node, end_node) { + let adj = Array.from({ length: n }, () => []); + for (let i = 0; i < edges.length; i++) { + let [src, dst] = edges[i]; + adj[src].push([dst, succProb[i]]); + adj[dst].push([src, succProb[i]]); + } + + let maxProb = Array(n).fill(0); + maxProb[start_node] = 1.0; + let pq = new MaxPriorityQueue({ priority: x => x[1] }); + pq.enqueue([start_node, 1.0]); + + while (!pq.isEmpty()) { + let [node, curr_prob] = pq.dequeue().element; + + if (node === end_node) return curr_prob; + if (curr_prob > maxProb[node]) continue; + + for (let [nei, edge_prob] of adj[node]) { + let new_prob = curr_prob * edge_prob; + if (new_prob > maxProb[nei]) { + maxProb[nei] = new_prob; + pq.enqueue([nei, new_prob]); + } + } + } + + return 0.0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((V + E) \log V)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number nodes and $E$ is the number of edges. + +--- + +## 3. Bellman Ford Algorithm + +::tabs-start + +```python +class Solution: + def maxProbability(self, n: int, edges: List[List[int]], succProb: List[float], start_node: int, end_node: int) -> float: + maxProb = [0.0] * n + maxProb[start_node] = 1.0 + + for i in range(n): + updated = False + for j in range(len(edges)): + src, dst = edges[j] + if maxProb[src] * succProb[j] > maxProb[dst]: + maxProb[dst] = maxProb[src] * succProb[j] + updated = True + + if maxProb[dst] * succProb[j] > maxProb[src]: + maxProb[src] = maxProb[dst] * succProb[j] + updated = True + + if not updated: + break + + return maxProb[end_node] +``` + +```java +public class Solution { + public double maxProbability(int n, int[][] edges, double[] succProb, int start_node, int end_node) { + double[] maxProb = new double[n]; + maxProb[start_node] = 1.0; + + for (int i = 0; i < n; i++) { + boolean updated = false; + for (int j = 0; j < edges.length; j++) { + int src = edges[j][0], dst = edges[j][1]; + + if (maxProb[src] * succProb[j] > maxProb[dst]) { + maxProb[dst] = maxProb[src] * succProb[j]; + updated = true; + } + + if (maxProb[dst] * succProb[j] > maxProb[src]) { + maxProb[src] = maxProb[dst] * succProb[j]; + updated = true; + } + } + if (!updated) break; + } + + return maxProb[end_node]; + } +} +``` + +```cpp +class Solution { +public: + double maxProbability(int n, vector>& edges, vector& succProb, int start_node, int end_node) { + vector maxProb(n, 0.0); + maxProb[start_node] = 1.0; + + for (int i = 0; i < n; i++) { + bool updated = false; + for (int j = 0; j < edges.size(); j++) { + int src = edges[j][0], dst = edges[j][1]; + + if (maxProb[src] * succProb[j] > maxProb[dst]) { + maxProb[dst] = maxProb[src] * succProb[j]; + updated = true; + } + + if (maxProb[dst] * succProb[j] > maxProb[src]) { + maxProb[src] = maxProb[dst] * succProb[j]; + updated = true; + } + } + if (!updated) break; + } + + return maxProb[end_node]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @param {number[]} succProb + * @param {number} start_node + * @param {number} end_node + * @return {number} + */ + maxProbability(n, edges, succProb, start_node, end_node) { + let maxProb = new Array(n).fill(0.0); + maxProb[start_node] = 1.0; + + for (let i = 0; i < n; i++) { + let updated = false; + for (let j = 0; j < edges.length; j++) { + let [src, dst] = edges[j]; + + if (maxProb[src] * succProb[j] > maxProb[dst]) { + maxProb[dst] = maxProb[src] * succProb[j]; + updated = true; + } + + if (maxProb[dst] * succProb[j] > maxProb[src]) { + maxProb[src] = maxProb[dst] * succProb[j]; + updated = true; + } + } + if (!updated) break; + } + + return maxProb[end_node]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V * E)$ +* Space complexity: $O(V)$ + +> Where $V$ is the number nodes and $E$ is the number of edges. + +--- + +## 4. Shortest Path Faster Algorithm + +::tabs-start + +```python +class Solution: + def maxProbability(self, n: int, edges: List[List[int]], succProb: List[float], start_node: int, end_node: int) -> float: + adj = [[] for _ in range(n)] + for i in range(len(edges)): + src, dst = edges[i] + adj[src].append((dst, succProb[i])) + adj[dst].append((src, succProb[i])) + + maxProb = [0.0] * n + maxProb[start_node] = 1.0 + q = deque([start_node]) + + while q: + node = q.popleft() + + for nei, edge_prob in adj[node]: + new_prob = maxProb[node] * edge_prob + if new_prob > maxProb[nei]: + maxProb[nei] = new_prob + q.append(nei) + + return maxProb[end_node] +``` + +```java +public class Solution { + public double maxProbability(int n, int[][] edges, double[] succProb, int start_node, int end_node) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + + for (int i = 0; i < edges.length; i++) { + int src = edges[i][0], dst = edges[i][1]; + adj[src].add(new Pair(dst, succProb[i])); + adj[dst].add(new Pair(src, succProb[i])); + } + + double[] maxProb = new double[n]; + maxProb[start_node] = 1.0; + Queue q = new LinkedList<>(); + q.offer(start_node); + + while (!q.isEmpty()) { + int node = q.poll(); + + for (Pair nei : adj[node]) { + double newProb = maxProb[node] * nei.prob; + if (newProb > maxProb[nei.node]) { + maxProb[nei.node] = newProb; + q.offer(nei.node); + } + } + } + + return maxProb[end_node]; + } + + static class Pair { + int node; + double prob; + + Pair(int node, double prob) { + this.node = node; + this.prob = prob; + } + } +} +``` + +```cpp +class Solution { +public: + double maxProbability(int n, vector>& edges, vector& succProb, int start_node, int end_node) { + vector>> adj(n); + + for (int i = 0; i < edges.size(); i++) { + int src = edges[i][0], dst = edges[i][1]; + adj[src].emplace_back(dst, succProb[i]); + adj[dst].emplace_back(src, succProb[i]); + } + + vector maxProb(n, 0.0); + maxProb[start_node] = 1.0; + queue q; + q.push(start_node); + + while (!q.empty()) { + int node = q.front(); + q.pop(); + + for (auto& [nei, edgeProb] : adj[node]) { + double newProb = maxProb[node] * edgeProb; + if (newProb > maxProb[nei]) { + maxProb[nei] = newProb; + q.push(nei); + } + } + } + + return maxProb[end_node]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @param {number[]} succProb + * @param {number} start_node + * @param {number} end_node + * @return {number} + */ + maxProbability(n, edges, succProb, start_node, end_node) { + let adj = Array.from({ length: n }, () => []); + for (let i = 0; i < edges.length; i++) { + let [src, dst] = edges[i]; + adj[src].push([dst, succProb[i]]); + adj[dst].push([src, succProb[i]]); + } + + let maxProb = new Array(n).fill(0.0); + maxProb[start_node] = 1.0; + const q = new Queue([start_node]); + + while (!q.isEmpty()) { + let node = q.pop(); + + for (let [nei, edgeProb] of adj[node]) { + let newProb = maxProb[node] * edgeProb; + if (newProb > maxProb[nei]) { + maxProb[nei] = newProb; + q.push(nei); + } + } + } + + return maxProb[end_node]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V * E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number nodes and $E$ is the number of edges. \ No newline at end of file diff --git a/articles/path-with-minimum-effort.md b/articles/path-with-minimum-effort.md new file mode 100644 index 000000000..5f151c2c8 --- /dev/null +++ b/articles/path-with-minimum-effort.md @@ -0,0 +1,1066 @@ +## 1. Dijkstra's Algorithm + +::tabs-start + +```python +class Solution: + def minimumEffortPath(self, heights: List[List[int]]) -> int: + ROWS, COLS = len(heights), len(heights[0]) + minHeap = [[0, 0, 0]] # [diff, row, col] + visit = set() + directions = [[0, 1], [0, -1], [1, 0], [-1, 0]] + + while minHeap: + diff, r, c = heapq.heappop(minHeap) + + if (r, c) in visit: + continue + + visit.add((r, c)) + + if (r, c) == (ROWS - 1, COLS - 1): + return diff + + for dr, dc in directions: + newR, newC = r + dr, c + dc + if ( + newR < 0 or newC < 0 or + newR >= ROWS or newC >= COLS or + (newR, newC) in visit + ): + continue + + newDiff = max(diff, abs(heights[r][c] - heights[newR][newC])) + heapq.heappush(minHeap, [newDiff, newR, newC]) + + return 0 +``` + +```java +public class Solution { + public int minimumEffortPath(int[][] heights) { + int rows = heights.length; + int cols = heights[0].length; + int[][] dist = new int[rows][cols]; + for (int[] row : dist) { + Arrays.fill(row, Integer.MAX_VALUE); + } + dist[0][0] = 0; + + PriorityQueue minHeap = new PriorityQueue<>((a, b) -> a[0] - b[0]); + minHeap.offer(new int[]{0, 0, 0}); // {diff, row, col} + + int[][] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + while (!minHeap.isEmpty()) { + int[] curr = minHeap.poll(); + int diff = curr[0], r = curr[1], c = curr[2]; + + if (r == rows - 1 && c == cols - 1) return diff; + if (dist[r][c] < diff) continue; + + for (int[] dir : directions) { + int newR = r + dir[0], newC = c + dir[1]; + if (newR < 0 || newC < 0 || newR >= rows || newC >= cols) { + continue; + } + + int newDiff = Math.max(diff, Math.abs(heights[r][c] - heights[newR][newC])); + if (newDiff < dist[newR][newC]) { + dist[newR][newC] = newDiff; + minHeap.offer(new int[]{newDiff, newR, newC}); + } + } + } + + return 0; + } +} +``` + +```cpp +class Solution { +public: + int minimumEffortPath(vector>& heights) { + int rows = heights.size(), cols = heights[0].size(); + vector> dist(rows, vector(cols, INT_MAX)); + dist[0][0] = 0; + + priority_queue, vector>, greater<>> minHeap; + minHeap.push({0, 0, 0}); // {diff, row, col} + + vector> directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + while (!minHeap.empty()) { + auto curr = minHeap.top(); + minHeap.pop(); + int diff = curr[0], r = curr[1], c = curr[2]; + + if (r == rows - 1 && c == cols - 1) return diff; + if (dist[r][c] < diff) continue; + + for (auto& dir : directions) { + int newR = r + dir[0], newC = c + dir[1]; + if (newR < 0 || newC < 0 || newR >= rows || newC >= cols) { + continue; + } + + int newDiff = max(diff, abs(heights[r][c] - heights[newR][newC])); + if (newDiff < dist[newR][newC]) { + dist[newR][newC] = newDiff; + minHeap.push({newDiff, newR, newC}); + } + } + } + + return 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} heights + * @return {number} + */ + minimumEffortPath(heights) { + const rows = heights.length; + const cols = heights[0].length; + const dist = Array.from({ length: rows }, () => + Array(cols).fill(Infinity) + ); + dist[0][0] = 0; + + const minHeap = new MinPriorityQueue(a => a[0]); + minHeap.enqueue([0, 0, 0]); // [diff, row, col] + + const directions = [ + [0, 1], [0, -1], + [1, 0], [-1, 0], + ]; + + while (!minHeap.isEmpty()) { + const [diff, r, c] = minHeap.dequeue(); + + if (r === rows - 1 && c === cols - 1) return diff; + if (dist[r][c] < diff) continue; + + for (const [dr, dc] of directions) { + const newR = r + dr; + const newC = c + dc; + if (newR < 0 || newC < 0 || newR >= rows || newC >= cols) { + continue; + } + + const newDiff = Math.max( + diff, + Math.abs(heights[r][c] - heights[newR][newC]) + ); + if (newDiff < dist[newR][newC]) { + dist[newR][newC] = newDiff; + minHeap.enqueue([newDiff, newR, newC]); + } + } + } + + return 0; + } +} +``` + +```csharp +public class Solution { + public int MinimumEffortPath(int[][] heights) { + int rows = heights.Length, cols = heights[0].Length; + var directions = new int[][] { + new int[] { 0, 1 }, + new int[] { 0, -1 }, + new int[] { 1, 0 }, + new int[] { -1, 0 } + }; + + var minHeap = new PriorityQueue<(int diff, int r, int c), int>(); + var visited = new HashSet<(int, int)>(); + minHeap.Enqueue((0, 0, 0), 0); + + while (minHeap.Count > 0) { + var current = minHeap.Dequeue(); + int diff = current.diff, r = current.r, c = current.c; + + if (visited.Contains((r, c))) continue; + visited.Add((r, c)); + + if (r == rows - 1 && c == cols - 1) { + return diff; + } + + foreach (var dir in directions) { + int newR = r + dir[0]; + int newC = c + dir[1]; + + if (newR < 0 || newC < 0 || newR >= rows || newC >= cols || visited.Contains((newR, newC))) { + continue; + } + + int newDiff = Math.Max(diff, Math.Abs(heights[r][c] - heights[newR][newC])); + minHeap.Enqueue((newDiff, newR, newC), newDiff); + } + } + + return 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n * \log (m * n))$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the given matrix. + +--- + +## 2. Binary Search + DFS + +::tabs-start + +```python +class Solution: + def minimumEffortPath(self, heights: List[List[int]]) -> int: + ROWS, COLS = len(heights), len(heights[0]) + directions = [[0, 1], [0, -1], [1, 0], [-1, 0]] + + def dfs(r, c, limit, visited): + if r == ROWS - 1 and c == COLS - 1: + return True + + visited.add((r, c)) + for dr, dc in directions: + newR, newC = r + dr, c + dc + if (newR < 0 or newC < 0 or + newR >= ROWS or newC >= COLS or + (newR, newC) in visited or + abs(heights[newR][newC] - heights[r][c]) > limit): + continue + + if dfs(newR, newC, limit, visited): + return True + return False + + l, r = 0, 1000000 + res = r + + while l <= r: + mid = (l + r) // 2 + if dfs(0, 0, mid, set()): + res = mid + r = mid - 1 + else: + l = mid + 1 + + return res +``` + +```java +public class Solution { + private int ROWS, COLS; + private int[][] heights; + private int[][] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + private boolean[][] visited; + + public int minimumEffortPath(int[][] heights) { + this.heights = heights; + this.ROWS = heights.length; + this.COLS = heights[0].length; + this.visited = new boolean[ROWS][COLS]; + + int l = 0, r = 1_000_000, res = r; + + while (l <= r) { + int mid = (l + r) / 2; + for (boolean[] row : visited) Arrays.fill(row, false); + if (dfs(0, 0, mid)) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + + return res; + } + + private boolean dfs(int r, int c, int limit) { + if (r == ROWS - 1 && c == COLS - 1) { + return true; + } + + visited[r][c] = true; + for (int[] dir : directions) { + int newR = r + dir[0]; + int newC = c + dir[1]; + if (newR < 0 || newC < 0 || newR >= ROWS || newC >= COLS || visited[newR][newC]) { + continue; + } + if (Math.abs(heights[newR][newC] - heights[r][c]) > limit) { + continue; + } + if (dfs(newR, newC, limit)) { + return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { +private: + int ROWS, COLS; + vector> heights; + vector> visited; + vector> directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + +public: + int minimumEffortPath(vector>& heights) { + this->heights = heights; + this->ROWS = heights.size(); + this->COLS = heights[0].size(); + this->visited = vector>(ROWS, vector(COLS, false)); + + int l = 0, r = 1'000'000, res = r; + while (l <= r) { + int mid = (l + r) / 2; + for (auto& row : visited) { + fill(row.begin(), row.end(), false); + } + if (dfs(0, 0, mid)) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return res; + } + +private: + bool dfs(int r, int c, int limit) { + if (r == ROWS - 1 && c == COLS - 1) { + return true; + } + + visited[r][c] = true; + for (const auto& dir : directions) { + int newR = r + dir[0]; + int newC = c + dir[1]; + if (newR < 0 || newC < 0 || newR >= ROWS || newC >= COLS || visited[newR][newC]) { + continue; + } + if (abs(heights[newR][newC] - heights[r][c]) > limit) { + continue; + } + if (dfs(newR, newC, limit)) { + return true; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} heights + * @return {number} + */ + minimumEffortPath(heights) { + const ROWS = heights.length; + const COLS = heights[0].length; + const directions = [ + [0, 1], [0, -1], + [1, 0], [-1, 0] + ]; + let visited = Array.from({ length: ROWS }, () => + Array(COLS).fill(false) + ); + + const dfs = (r, c, limit) => { + if (r === ROWS - 1 && c === COLS - 1) { + return true; + } + + visited[r][c] = true; + for (const [dr, dc] of directions) { + const newR = r + dr; + const newC = c + dc; + + if (newR < 0 || newC < 0 || + newR >= ROWS || newC >= COLS || + visited[newR][newC]) { + continue; + } + if (Math.abs(heights[newR][newC] - heights[r][c]) > limit) { + continue; + } + if (dfs(newR, newC, limit)) { + return true; + } + } + return false; + }; + + let l = 0, r = 1_000_000, res = r; + while (l <= r) { + const mid = Math.floor((l + r) / 2); + for (const row of visited) { + row.fill(false); + } + if (dfs(0, 0, mid)) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return res; + } +} +``` + +```csharp +public class Solution { + private int[][] directions = new int[][] { + new int[] { 0, 1 }, + new int[] { 0, -1 }, + new int[] { 1, 0 }, + new int[] { -1, 0 } + }; + + public int MinimumEffortPath(int[][] heights) { + int rows = heights.Length; + int cols = heights[0].Length; + + bool Dfs(int r, int c, int limit, HashSet<(int, int)> visited) { + if (r == rows - 1 && c == cols - 1) + return true; + + visited.Add((r, c)); + + foreach (var dir in directions) { + int newR = r + dir[0]; + int newC = c + dir[1]; + + if (newR < 0 || newC < 0 || newR >= rows || newC >= cols || + visited.Contains((newR, newC)) || + Math.Abs(heights[newR][newC] - heights[r][c]) > limit) + continue; + + if (Dfs(newR, newC, limit, visited)) + return true; + } + + return false; + } + + int left = 0, right = 1000000, res = right; + + while (left <= right) { + int mid = (left + right) / 2; + var visited = new HashSet<(int, int)>(); + if (Dfs(0, 0, mid, visited)) { + res = mid; + right = mid - 1; + } else { + left = mid + 1; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n * \log (m * n))$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the given matrix. + +--- + +## 3. Kruskal's Algorithm + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu = self.find(u) + pv = self.find(v) + if pu == pv: + return False + if self.Size[pu] < self.Size[pv]: + pu, pv = pv, pu + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + return True + + +class Solution: + def minimumEffortPath(self, heights: List[List[int]]) -> int: + ROWS, COLS = len(heights), len(heights[0]) + edges = [] + for r in range(ROWS): + for c in range(COLS): + if r + 1 < ROWS: + edges.append((abs(heights[r][c] - heights[r + 1][c]), r * COLS + c, (r + 1) * COLS + c)) + if c + 1 < COLS: + edges.append((abs(heights[r][c] - heights[r][c + 1]), r * COLS + c, r * COLS + c + 1)) + + edges.sort() + dsu = DSU(ROWS * COLS) + for weight, u, v in edges: + dsu.union(u, v) + if dsu.find(0) == dsu.find(ROWS * COLS - 1): + return weight + return 0 +``` + +```java +class DSU { + private int[] Parent; + private int[] Size; + + public DSU(int n) { + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + public int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u); + int pv = find(v); + if (pu == pv) { + return false; + } + if (Size[pu] < Size[pv]) { + int temp = pu; + pu = pv; + pv = temp; + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } +} + +public class Solution { + public int minimumEffortPath(int[][] heights) { + int ROWS = heights.length; + int COLS = heights[0].length; + List edges = new ArrayList<>(); + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (r + 1 < ROWS) { + edges.add(new int[]{Math.abs(heights[r][c] - heights[r + 1][c]), r * COLS + c, (r + 1) * COLS + c}); + } + if (c + 1 < COLS) { + edges.add(new int[]{Math.abs(heights[r][c] - heights[r][c + 1]), r * COLS + c, r * COLS + c + 1}); + } + } + } + + edges.sort(Comparator.comparingInt(a -> a[0])); + DSU dsu = new DSU(ROWS * COLS); + for (int[] edge : edges) { + int weight = edge[0], u = edge[1], v = edge[2]; + dsu.union(u, v); + if (dsu.find(0) == dsu.find(ROWS * COLS - 1)) { + return weight; + } + } + return 0; + } +} +``` + +```cpp +class DSU { +private: + vector Parent, Size; + +public: + DSU(int n) { + Parent.resize(n + 1); + Size.resize(n + 1, 1); + for (int i = 0; i <= n; i++) { + Parent[i] = i; + } + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + bool unionSets(int u, int v) { + int pu = find(u); + int pv = find(v); + if (pu == pv) { + return false; + } + if (Size[pu] < Size[pv]) { + swap(pu, pv); + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } +}; + +class Solution { +public: + int minimumEffortPath(vector>& heights) { + int ROWS = heights.size(); + int COLS = heights[0].size(); + vector> edges; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (r + 1 < ROWS) { + edges.push_back({abs(heights[r][c] - heights[r + 1][c]), r * COLS + c, (r + 1) * COLS + c}); + } + if (c + 1 < COLS) { + edges.push_back({abs(heights[r][c] - heights[r][c + 1]), r * COLS + c, r * COLS + c + 1}); + } + } + } + + sort(edges.begin(), edges.end()); + DSU dsu(ROWS * COLS); + for (auto& edge : edges) { + int weight, u, v; + tie(weight, u, v) = edge; + dsu.unionSets(u, v); + if (dsu.find(0) == dsu.find(ROWS * COLS - 1)) { + return weight; + } + } + return 0; + } +}; +``` + +```javascript +class DSU { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.Parent = Array.from({ length: n + 1 }, (_, i) => i); + this.Size = Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.Parent[node] !== node) { + this.Parent[node] = this.find(this.Parent[node]); + } + return this.Parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u), pv = this.find(v); + if (pu === pv) return false; + if (this.Size[pu] < this.Size[pv]) [pu, pv] = [pv, pu]; + this.Size[pu] += this.Size[pv]; + this.Parent[pv] = pu; + return true; + } +} + +class Solution { + /** + * @param {number[][]} heights + * @return {number} + */ + minimumEffortPath(heights) { + const ROWS = heights.length; + const COLS = heights[0].length; + const edges = []; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (r + 1 < ROWS) { + edges.push([Math.abs(heights[r][c] - heights[r + 1][c]), r * COLS + c, (r + 1) * COLS + c]); + } + if (c + 1 < COLS) { + edges.push([Math.abs(heights[r][c] - heights[r][c + 1]), r * COLS + c, r * COLS + c + 1]); + } + } + } + + edges.sort((a, b) => a[0] - b[0]); + const dsu = new DSU(ROWS * COLS); + for (const [weight, u, v] of edges) { + dsu.union(u, v); + if (dsu.find(0) === dsu.find(ROWS * COLS - 1)) { + return weight; + } + } + return 0; + } +} +``` + +```csharp +public class DSU { + private int[] parent; + private int[] size; + + public DSU(int n) { + parent = new int[n + 1]; + size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + parent[i] = i; + size[i] = 1; + } + } + + public int Find(int node) { + if (parent[node] != node) { + parent[node] = Find(parent[node]); + } + return parent[node]; + } + + public bool Union(int u, int v) { + int pu = Find(u); + int pv = Find(v); + if (pu == pv) return false; + if (size[pu] < size[pv]) { + (pu, pv) = (pv, pu); + } + size[pu] += size[pv]; + parent[pv] = pu; + return true; + } +} + +public class Solution { + public int MinimumEffortPath(int[][] heights) { + int rows = heights.Length; + int cols = heights[0].Length; + List<(int, int, int)> edges = new List<(int, int, int)>(); + + for (int r = 0; r < rows; r++) { + for (int c = 0; c < cols; c++) { + int id = r * cols + c; + if (r + 1 < rows) { + int downId = (r + 1) * cols + c; + int diff = Math.Abs(heights[r][c] - heights[r + 1][c]); + edges.Add((diff, id, downId)); + } + if (c + 1 < cols) { + int rightId = r * cols + (c + 1); + int diff = Math.Abs(heights[r][c] - heights[r][c + 1]); + edges.Add((diff, id, rightId)); + } + } + } + + edges.Sort((a, b) => a.Item1.CompareTo(b.Item1)); + DSU dsu = new DSU(rows * cols); + + foreach (var (weight, u, v) in edges) { + dsu.Union(u, v); + if (dsu.Find(0) == dsu.Find(rows * cols - 1)) { + return weight; + } + } + + return 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n * \log (m * n))$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the given matrix. + +--- + +## 4. Shortest Path Faster Algorithm + +::tabs-start + +```python +class Solution: + def minimumEffortPath(self, heights: List[List[int]]) -> int: + ROWS, COLS = len(heights), len(heights[0]) + dist = [float('inf')] * (ROWS * COLS) + dist[0] = 0 + in_queue = [False] * (ROWS * COLS) + + def index(r, c): + return r * COLS + c + + queue = deque([0]) + in_queue[0] = True + directions = [(0, 1), (0, -1), (1, 0), (-1, 0)] + + while queue: + u = queue.popleft() + in_queue[u] = False + r, c = divmod(u, COLS) + + for dr, dc in directions: + newR, newC = r + dr, c + dc + if 0 <= newR < ROWS and 0 <= newC < COLS: + v = index(newR, newC) + weight = abs(heights[r][c] - heights[newR][newC]) + new_dist = max(dist[u], weight) + if new_dist < dist[v]: + dist[v] = new_dist + if not in_queue[v]: + queue.append(v) + in_queue[v] = True + + return dist[ROWS * COLS - 1] +``` + +```java +public class Solution { + public int minimumEffortPath(int[][] heights) { + int ROWS = heights.length, COLS = heights[0].length; + int[] dist = new int[ROWS * COLS]; + Arrays.fill(dist, Integer.MAX_VALUE); + dist[0] = 0; + + boolean[] inQueue = new boolean[ROWS * COLS]; + Queue queue = new LinkedList<>(); + queue.offer(0); + inQueue[0] = true; + + int[][] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + while (!queue.isEmpty()) { + int u = queue.poll(); + inQueue[u] = false; + + int r = u / COLS, c = u % COLS; + + for (int[] dir : directions) { + int newR = r + dir[0], newC = c + dir[1]; + if (newR >= 0 && newC >= 0 && newR < ROWS && newC < COLS) { + int v = newR * COLS + newC; + int weight = Math.abs(heights[r][c] - heights[newR][newC]); + int newDist = Math.max(dist[u], weight); + if (newDist < dist[v]) { + dist[v] = newDist; + if (!inQueue[v]) { + queue.offer(v); + inQueue[v] = true; + } + } + } + } + } + + return dist[ROWS * COLS - 1]; + } +} +``` + +```cpp +class Solution { +public: + int minimumEffortPath(vector>& heights) { + int ROWS = heights.size(), COLS = heights[0].size(); + vector dist(ROWS * COLS, INT_MAX); + dist[0] = 0; + + vector inQueue(ROWS * COLS, false); + queue q; + q.push(0); + inQueue[0] = true; + + vector> directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + while (!q.empty()) { + int u = q.front(); + q.pop(); + inQueue[u] = false; + + int r = u / COLS, c = u % COLS; + + for (const auto& dir : directions) { + int newR = r + dir[0], newC = c + dir[1]; + if (newR >= 0 && newC >= 0 && newR < ROWS && newC < COLS) { + int v = newR * COLS + newC; + int weight = abs(heights[r][c] - heights[newR][newC]); + int newDist = max(dist[u], weight); + if (newDist < dist[v]) { + dist[v] = newDist; + if (!inQueue[v]) { + q.push(v); + inQueue[v] = true; + } + } + } + } + } + + return dist[ROWS * COLS - 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} heights + * @return {number} + */ + minimumEffortPath(heights) { + const ROWS = heights.length, COLS = heights[0].length; + const dist = Array(ROWS * COLS).fill(Infinity); + dist[0] = 0; + + const inQueue = Array(ROWS * COLS).fill(false); + const queue = new Queue(); + queue.push(0); + inQueue[0] = true; + + const directions = [ + [0, 1], [0, -1], + [1, 0], [-1, 0] + ]; + + while (!queue.isEmpty()) { + const u = queue.pop(); + inQueue[u] = false; + + const r = Math.floor(u / COLS), c = u % COLS; + for (const [dr, dc] of directions) { + const newR = r + dr, newC = c + dc; + if (newR >= 0 && newC >= 0 && newR < ROWS && newC < COLS) { + const v = newR * COLS + newC; + const weight = Math.abs(heights[r][c] - heights[newR][newC]); + const newDist = Math.max(dist[u], weight); + if (newDist < dist[v]) { + dist[v] = newDist; + if (!inQueue[v]) { + queue.push(v); + inQueue[v] = true; + } + } + } + } + } + + return dist[ROWS * COLS - 1]; + } +} +``` + +```csharp +public class Solution { + public int MinimumEffortPath(int[][] heights) { + int rows = heights.Length; + int cols = heights[0].Length; + int[] dist = Enumerable.Repeat(int.MaxValue, rows * cols).ToArray(); + bool[] inQueue = new bool[rows * cols]; + dist[0] = 0; + + int Index(int r, int c) => r * cols + c; + + Queue queue = new Queue(); + queue.Enqueue(0); + inQueue[0] = true; + + int[][] directions = new int[][] { + new int[] {0, 1}, new int[] {0, -1}, + new int[] {1, 0}, new int[] {-1, 0} + }; + + while (queue.Count > 0) { + int u = queue.Dequeue(); + inQueue[u] = false; + int r = u / cols, c = u % cols; + + foreach (var dir in directions) { + int newR = r + dir[0], newC = c + dir[1]; + if (newR >= 0 && newR < rows && newC >= 0 && newC < cols) { + int v = Index(newR, newC); + int weight = Math.Abs(heights[r][c] - heights[newR][newC]); + int newDist = Math.Max(dist[u], weight); + if (newDist < dist[v]) { + dist[v] = newDist; + if (!inQueue[v]) { + queue.Enqueue(v); + inQueue[v] = true; + } + } + } + } + } + + return dist[rows * cols - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(m * n)$ time in average case. + * $O(m ^ 2 * n ^ 2)$ time in worst case. +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the given matrix. \ No newline at end of file diff --git a/articles/perfect-squares.md b/articles/perfect-squares.md new file mode 100644 index 000000000..eb92e5450 --- /dev/null +++ b/articles/perfect-squares.md @@ -0,0 +1,674 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def numSquares(self, n: int) -> int: + def dfs(target): + if target == 0: + return 0 + + res = target + for i in range(1, target): + if i * i > target: + break + res = min(res, 1 + dfs(target - i * i)) + return res + + return dfs(n) +``` + +```java +public class Solution { + public int numSquares(int n) { + return dfs(n); + } + + private int dfs(int target) { + if (target == 0) { + return 0; + } + + int res = target; + for (int i = 1; i * i <= target; i++) { + res = Math.min(res, 1 + dfs(target - i * i)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSquares(int n) { + return dfs(n); + } + +private: + int dfs(int target) { + if (target == 0) { + return 0; + } + + int res = target; + for (int i = 1; i * i <= target; i++) { + res = min(res, 1 + dfs(target - i * i)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numSquares(n) { + const dfs = (target) => { + if (target === 0) return 0; + + let res = target; + for (let i = 1; i * i <= target; i++) { + res = Math.min(res, 1 + dfs(target - i * i)); + } + return res; + }; + + return dfs(n); + } +} +``` + +```csharp +public class Solution { + public int NumSquares(int n) { + return Dfs(n); + } + + private int Dfs(int target) { + if (target == 0) return 0; + + int res = target; + for (int i = 1; i * i <= target; i++) { + res = Math.Min(res, 1 + Dfs(target - i * i)); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ {\sqrt {n}})$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def numSquares(self, n: int) -> int: + memo = {} + + def dfs(target): + if target == 0: + return 0 + if target in memo: + return memo[target] + + res = target + for i in range(1, target + 1): + if i * i > target: + break + res = min(res, 1 + dfs(target - i * i)) + + memo[target] = res + return res + + return dfs(n) +``` + +```java +public class Solution { + Map memo = new HashMap<>(); + + private int dfs(int target) { + if (target == 0) return 0; + if (memo.containsKey(target)) return memo.get(target); + + int res = target; + for (int i = 1; i * i <= target; i++) { + res = Math.min(res, 1 + dfs(target - i * i)); + } + + memo.put(target, res); + return res; + } + + public int numSquares(int n) { + return dfs(n); + } +} +``` + +```cpp +class Solution { +public: + unordered_map memo; + + int dfs(int target) { + if (target == 0) return 0; + if (memo.count(target)) return memo[target]; + + int res = target; + for (int i = 1; i * i <= target; i++) { + res = min(res, 1 + dfs(target - i * i)); + } + + return memo[target] = res; + } + + int numSquares(int n) { + return dfs(n); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numSquares(n) { + const memo = new Map(); + + const dfs = (target) => { + if (target === 0) return 0; + if (memo.has(target)) { + return memo.get(target); + } + + let res = target; + for (let i = 1; i * i <= target; i++) { + res = Math.min(res, 1 + dfs(target - i * i)); + } + memo.set(target, res); + return res; + }; + + return dfs(n); + } +} +``` + +```csharp +public class Solution { + private Dictionary memo = new Dictionary(); + + public int NumSquares(int n) { + return Dfs(n); + } + + private int Dfs(int target) { + if (target == 0) return 0; + if (memo.ContainsKey(target)) return memo[target]; + + int res = target; + for (int i = 1; i * i <= target; i++) { + res = Math.Min(res, 1 + Dfs(target - i * i)); + } + + memo[target] = res; + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * \sqrt {n})$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def numSquares(self, n: int) -> int: + dp = [n] * (n + 1) + dp[0] = 0 + + for target in range(1, n + 1): + for s in range(1, target + 1): + square = s * s + if target - square < 0: + break + dp[target] = min(dp[target], 1 + dp[target - square]) + + return dp[n] +``` + +```java +public class Solution { + public int numSquares(int n) { + int[] dp = new int[n + 1]; + Arrays.fill(dp, n); + dp[0] = 0; + + for (int target = 1; target <= n; target++) { + for (int s = 1; s * s <= target; s++) { + dp[target] = Math.min(dp[target], 1 + dp[target - s * s]); + } + } + + return dp[n]; + } +} +``` + +```cpp +class Solution { +public: + int numSquares(int n) { + vector dp(n + 1, n); + dp[0] = 0; + + for (int target = 1; target <= n; target++) { + for (int s = 1; s * s <= target; s++) { + dp[target] = min(dp[target], 1 + dp[target - s * s]); + } + } + + return dp[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numSquares(n) { + const dp = Array(n + 1).fill(n); + dp[0] = 0; + + for (let target = 1; target <= n; target++) { + for (let s = 1; s * s <= target; s++) { + dp[target] = Math.min(dp[target], 1 + dp[target - s * s]); + } + } + + return dp[n]; + } +} +``` + +```csharp +public class Solution { + public int NumSquares(int n) { + int[] dp = new int[n + 1]; + Array.Fill(dp, n); + dp[0] = 0; + + for (int target = 1; target <= n; target++) { + for (int s = 1; s * s <= target; s++) { + int square = s * s; + dp[target] = Math.Min(dp[target], 1 + dp[target - square]); + } + } + + return dp[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * \sqrt {n})$ +* Space complexity: $O(n)$ + +--- + +## 4. Breadth First Search + +::tabs-start + +```python +class Solution: + def numSquares(self, n: int) -> int: + q = deque() + seen = set() + + res = 0 + q.append(0) + while q: + res += 1 + for _ in range(len(q)): + cur = q.popleft() + s = 1 + while s * s + cur <= n: + nxt = cur + s * s + if nxt == n: + return res + if nxt not in seen: + seen.add(nxt) + q.append(nxt) + s += 1 + + return res +``` + +```java +public class Solution { + public int numSquares(int n) { + Queue q = new LinkedList<>(); + Set seen = new HashSet<>(); + + int res = 0; + q.offer(0); + while (!q.isEmpty()) { + res++; + for (int i = q.size(); i > 0; i--) { + int cur = q.poll(); + for (int s = 1; s * s + cur <= n; s++) { + int next = cur + s * s; + if (next == n) return res; + if (!seen.contains(next)) { + q.offer(next); + seen.add(next); + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSquares(int n) { + queue q; + unordered_set seen; + + int res = 0; + q.push(0); + while (!q.empty()) { + res++; + for (int i = q.size(); i > 0; i--) { + int cur = q.front(); q.pop(); + for (int s = 1; s * s + cur <= n; s++) { + int next = cur + s * s; + if (next == n) return res; + if (seen.find(next) == seen.end()) { + q.push(next); + seen.insert(next); + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numSquares(n) { + const q = new Queue; + const seen = new Set(); + + let res = 0; + q.push(0); + while (!q.isEmpty()) { + res++; + for (let i = q.size(); i > 0; i--) { + const cur = q.pop(); + for (let s = 1; s * s + cur <= n; s++) { + const next = cur + s * s; + if (next === n) return res; + if (!seen.has(next)) { + q.push(next); + seen.add(next); + } + } + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int NumSquares(int n) { + Queue q = new Queue(); + HashSet seen = new HashSet(); + + int res = 0; + q.Enqueue(0); + + while (q.Count > 0) { + res++; + int size = q.Count; + for (int i = 0; i < size; i++) { + int cur = q.Dequeue(); + int s = 1; + while (s * s + cur <= n) { + int nxt = cur + s * s; + if (nxt == n) { + return res; + } + if (!seen.Contains(nxt)) { + seen.Add(nxt); + q.Enqueue(nxt); + } + s++; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * \sqrt {n})$ +* Space complexity: $O(n)$ + +--- + +## 5. Math + +::tabs-start + +```python +class Solution: + def numSquares(self, n: int) -> int: + while n % 4 == 0: + n //= 4 + + if n % 8 == 7: + return 4 + + def isSquareNum(num): + s = int(math.sqrt(num)) + return s * s == num + + if isSquareNum(n): + return 1 + + i = 1 + while i * i <= n: + if isSquareNum(n - i * i): + return 2 + i += 1 + + return 3 +``` + +```java +public class Solution { + public int numSquares(int n) { + while (n % 4 == 0) { + n /= 4; + } + + if (n % 8 == 7) { + return 4; + } + + if (isSquareNum(n)) { + return 1; + } + + for (int i = 1; i * i <= n; i++) { + if (isSquareNum(n - i * i)) { + return 2; + } + } + + return 3; + } + + private boolean isSquareNum(int num) { + int s = (int) Math.sqrt(num); + return s * s == num; + } +} +``` + +```cpp +class Solution { +public: + int numSquares(int n) { + while (n % 4 == 0) { + n /= 4; + } + + if (n % 8 == 7) { + return 4; + } + + if (isSquareNum(n)) { + return 1; + } + + for (int i = 1; i * i <= n; i++) { + if (isSquareNum(n - i * i)) { + return 2; + } + } + + return 3; + } + +private: + bool isSquareNum(int num) { + int s = (int) sqrt(num); + return s * s == num; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numSquares(n) { + while (n % 4 === 0) { + n = Math.floor(n / 4); + } + + if (n % 8 === 7) { + return 4; + } + + const isSquareNum = (num) => { + const s = Math.floor(Math.sqrt(num)); + return s * s === num; + }; + + if (isSquareNum(n)) { + return 1; + } + + for (let i = 1; i * i <= n; i++) { + if (isSquareNum(n - i * i)) { + return 2; + } + } + + return 3; + } +} +``` + +```csharp +public class Solution { + public int NumSquares(int n) { + while (n % 4 == 0) { + n /= 4; + } + + if (n % 8 == 7) { + return 4; + } + + bool IsSquareNum(int num) { + int s = (int)Math.Sqrt(num); + return s * s == num; + } + + if (IsSquareNum(n)) { + return 1; + } + + for (int i = 1; i * i <= n; i++) { + if (IsSquareNum(n - i * i)) { + return 2; + } + } + + return 3; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\sqrt {n})$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/permutation-string.md b/articles/permutation-string.md new file mode 100644 index 000000000..482890ee4 --- /dev/null +++ b/articles/permutation-string.md @@ -0,0 +1,851 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def checkInclusion(self, s1: str, s2: str) -> bool: + s1 = sorted(s1) + + for i in range(len(s2)): + for j in range(i, len(s2)): + subStr = s2[i : j + 1] + subStr = sorted(subStr) + if subStr == s1: + return True + return False +``` + +```java +public class Solution { + public boolean checkInclusion(String s1, String s2) { + char[] s1Arr = s1.toCharArray(); + Arrays.sort(s1Arr); + String sortedS1 = new String(s1Arr); + + for (int i = 0; i < s2.length(); i++) { + for (int j = i; j < s2.length(); j++) { + char[] subStrArr = s2.substring(i, j + 1).toCharArray(); + Arrays.sort(subStrArr); + String sortedSubStr = new String(subStrArr); + + if (sortedSubStr.equals(sortedS1)) { + return true; + } + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool checkInclusion(std::string s1, std::string s2) { + sort(s1.begin(), s1.end()); + + for (int i = 0; i < s2.length(); i++) { + for (int j = i; j < s2.length(); j++) { + string subStr = s2.substr(i, j - i + 1); + sort(subStr.begin(), subStr.end()); + + if (subStr == s1) { + return true; + } + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s1 + * @param {string} s2 + * @return {boolean} + */ + checkInclusion(s1, s2) { + s1 = s1.split('').sort().join(''); + + for (let i = 0; i < s2.length; i++) { + for (let j = i; j < s2.length; j++) { + let subStr = s2.slice(i, j + 1).split('').sort().join(''); + if (subStr === s1) { + return true; + } + } + } + return false; + } +} +``` + +```csharp +public class Solution { + public bool CheckInclusion(string s1, string s2) { + char[] s1Arr = s1.ToCharArray(); + Array.Sort(s1Arr); + string sortedS1 = new string(s1Arr); + + for (int i = 0; i < s2.Length; i++) { + for (int j = i; j < s2.Length; j++) { + char[] subStrArr = s2.Substring(i, j - i + 1).ToCharArray(); + Array.Sort(subStrArr); + string sortedSubStr = new string(subStrArr); + + if (sortedSubStr == sortedS1) { + return true; + } + } + } + return false; + } +} +``` + +```go +func checkInclusion(s1 string, s2 string) bool { + s1Sorted := []rune(s1) + sort.Slice(s1Sorted, func(i, j int) bool { + return s1Sorted[i] < s1Sorted[j] + }) + s1 = string(s1Sorted) + + for i := 0; i < len(s2); i++ { + for j := i; j < len(s2); j++ { + subStr := s2[i : j+1] + subStrSorted := []rune(subStr) + sort.Slice(subStrSorted, func(a, b int) bool { + return subStrSorted[a] < subStrSorted[b] + }) + if string(subStrSorted) == s1 { + return true + } + } + } + return false +} +``` + +```kotlin +class Solution { + fun checkInclusion(s1: String, s2: String): Boolean { + val sortedS1 = s1.toCharArray().apply { sort() }.concatToString() + + for (i in s2.indices) { + for (j in i until s2.length) { + val subStr = s2.substring(i, j + 1) + val sortedSubStr = subStr.toCharArray().apply { sort() }.concatToString() + if (sortedSubStr == sortedS1) { + return true + } + } + } + return false + } +} +``` + +```swift +class Solution { + func checkInclusion(_ s1: String, _ s2: String) -> Bool { + let s1Sorted = s1.sorted() + let chars = Array(s2) + + for i in 0.. bool: + count1 = {} + for c in s1: + count1[c] = 1 + count1.get(c, 0) + + need = len(count1) + for i in range(len(s2)): + count2, cur = {}, 0 + for j in range(i, len(s2)): + count2[s2[j]] = 1 + count2.get(s2[j], 0) + if count1.get(s2[j], 0) < count2[s2[j]]: + break + if count1.get(s2[j], 0) == count2[s2[j]]: + cur += 1 + if cur == need: + return True + return False +``` + +```java +public class Solution { + public boolean checkInclusion(String s1, String s2) { + Map count1 = new HashMap<>(); + for (char c : s1.toCharArray()) { + count1.put(c, count1.getOrDefault(c, 0) + 1); + } + + int need = count1.size(); + for (int i = 0; i < s2.length(); i++) { + Map count2 = new HashMap<>(); + int cur = 0; + for (int j = i; j < s2.length(); j++) { + char c = s2.charAt(j); + count2.put(c, count2.getOrDefault(c, 0) + 1); + + if (count1.getOrDefault(c, 0) < count2.get(c)) { + break; + } + + if (count1.getOrDefault(c, 0) == count2.get(c)) { + cur++; + } + + if (cur == need) { + return true; + } + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool checkInclusion(string s1, string s2) { + unordered_map count1; + for (char c : s1) { + count1[c]++; + } + + int need = count1.size(); + for (int i = 0; i < s2.length(); i++) { + unordered_map count2; + int cur = 0; + for (int j = i; j < s2.length(); j++) { + char c = s2[j]; + count2[c]++; + + if (count1[c] < count2[c]) { + break; + } + + if (count1[c] == count2[c]) { + cur++; + } + + if (cur == need) { + return true; + } + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s1 + * @param {string} s2 + * @return {boolean} + */ + checkInclusion(s1, s2) { + let count1 = {}; + for (let c of s1) { + count1[c] = (count1[c] || 0) + 1; + } + + let need = Object.keys(count1).length; + for (let i = 0; i < s2.length; i++) { + let count2 = {}; + let cur = 0; + for (let j = i; j < s2.length; j++) { + let c = s2[j]; + count2[c] = (count2[c] || 0) + 1; + + if ((count1[c] || 0) < count2[c]) { + break; + } + + if ((count1[c] || 0) === count2[c]) { + cur++; + } + + if (cur === need) { + return true; + } + } + } + return false; + } +} +``` + +```csharp +public class Solution { + public bool CheckInclusion(string s1, string s2) { + Dictionary count1 = new Dictionary(); + foreach (char c in s1) { + if (count1.ContainsKey(c)) { + count1[c]++; + } else { + count1[c] = 1; + } + } + + int need = count1.Count; + for (int i = 0; i < s2.Length; i++) { + Dictionary count2 = new Dictionary(); + int cur = 0; + for (int j = i; j < s2.Length; j++) { + char c = s2[j]; + if (count2.ContainsKey(c)) { + count2[c]++; + } else { + count2[c] = 1; + } + + if (!count1.ContainsKey(c) || count1[c] < count2[c]) { + break; + } + + if (count1[c] == count2[c]) { + cur++; + } + + if (cur == need) { + return true; + } + } + } + return false; + } +} +``` + +```go +func checkInclusion(s1 string, s2 string) bool { + count1 := make(map[rune]int) + for _, c := range s1 { + count1[c]++ + } + + need := len(count1) + for i := 0; i < len(s2); i++ { + count2 := make(map[rune]int) + cur := 0 + for j := i; j < len(s2); j++ { + count2[rune(s2[j])]++ + if count1[rune(s2[j])] < count2[rune(s2[j])] { + break + } + if count1[rune(s2[j])] == count2[rune(s2[j])] { + cur++ + } + if cur == need { + return true + } + } + } + return false +} +``` + +```kotlin +class Solution { + fun checkInclusion(s1: String, s2: String): Boolean { + val count1 = HashMap() + for (c in s1) { + count1[c] = 1 + count1.getOrDefault(c, 0) + } + + val need = count1.size + for (i in s2.indices) { + val count2 = mutableMapOf() + var cur = 0 + for (j in i until s2.length) { + count2[s2[j]] = 1 + count2.getOrDefault(s2[j], 0) + if (count1.getOrDefault(s2[j], 0) < count2[s2[j]]!!) { + break + } + if (count1.getOrDefault(s2[j], 0) == count2[s2[j]]!!) { + cur++ + } + if (cur == need) { + return true + } + } + } + return false + } +} +``` + +```swift +class Solution { + func checkInclusion(_ s1: String, _ s2: String) -> Bool { + var count1 = [Character: Int]() + for c in s1 { + count1[c, default: 0] += 1 + } + + let need = count1.count + let chars = Array(s2) + + for i in 0.. Where $n$ is the length of the string1 and $m$ is the length of string2. + +--- + +## 3. Sliding Window + +::tabs-start + +```python +class Solution: + def checkInclusion(self, s1: str, s2: str) -> bool: + if len(s1) > len(s2): + return False + + s1Count, s2Count = [0] * 26, [0] * 26 + for i in range(len(s1)): + s1Count[ord(s1[i]) - ord('a')] += 1 + s2Count[ord(s2[i]) - ord('a')] += 1 + + matches = 0 + for i in range(26): + matches += (1 if s1Count[i] == s2Count[i] else 0) + + l = 0 + for r in range(len(s1), len(s2)): + if matches == 26: + return True + + index = ord(s2[r]) - ord('a') + s2Count[index] += 1 + if s1Count[index] == s2Count[index]: + matches += 1 + elif s1Count[index] + 1 == s2Count[index]: + matches -= 1 + + index = ord(s2[l]) - ord('a') + s2Count[index] -= 1 + if s1Count[index] == s2Count[index]: + matches += 1 + elif s1Count[index] - 1 == s2Count[index]: + matches -= 1 + l += 1 + return matches == 26 +``` + +```java +public class Solution { + public boolean checkInclusion(String s1, String s2) { + if (s1.length() > s2.length()) { + return false; + } + + int[] s1Count = new int[26]; + int[] s2Count = new int[26]; + for (int i = 0; i < s1.length(); i++) { + s1Count[s1.charAt(i) - 'a']++; + s2Count[s2.charAt(i) - 'a']++; + } + + int matches = 0; + for (int i = 0; i < 26; i++) { + if (s1Count[i] == s2Count[i]) { + matches++; + } + } + + int l = 0; + for (int r = s1.length(); r < s2.length(); r++) { + if (matches == 26) { + return true; + } + + int index = s2.charAt(r) - 'a'; + s2Count[index]++; + if (s1Count[index] == s2Count[index]) { + matches++; + } else if (s1Count[index] + 1 == s2Count[index]) { + matches--; + } + + index = s2.charAt(l) - 'a'; + s2Count[index]--; + if (s1Count[index] == s2Count[index]) { + matches++; + } else if (s1Count[index] - 1 == s2Count[index]) { + matches--; + } + l++; + } + return matches == 26; + } +} +``` + +```cpp +class Solution { +public: + bool checkInclusion(string s1, string s2) { + if (s1.length() > s2.length()) { + return false; + } + + vector s1Count(26, 0); + vector s2Count(26, 0); + for (int i = 0; i < s1.length(); i++) { + s1Count[s1[i] - 'a']++; + s2Count[s2[i] - 'a']++; + } + + int matches = 0; + for (int i = 0; i < 26; i++) { + if (s1Count[i] == s2Count[i]) { + matches++; + } + } + + int l = 0; + for (int r = s1.length(); r < s2.length(); r++) { + if (matches == 26) { + return true; + } + + int index = s2[r] - 'a'; + s2Count[index]++; + if (s1Count[index] == s2Count[index]) { + matches++; + } else if (s1Count[index] + 1 == s2Count[index]) { + matches--; + } + + index = s2[l] - 'a'; + s2Count[index]--; + if (s1Count[index] == s2Count[index]) { + matches++; + } else if (s1Count[index] - 1 == s2Count[index]) { + matches--; + } + l++; + } + return matches == 26; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s1 + * @param {string} s2 + * @return {boolean} + */ + checkInclusion(s1, s2) { + if (s1.length > s2.length) { + return false; + } + + let s1Count = new Array(26).fill(0); + let s2Count = new Array(26).fill(0); + for (let i = 0; i < s1.length; i++) { + s1Count[s1.charCodeAt(i) - 97]++; + s2Count[s2.charCodeAt(i) - 97]++; + } + + let matches = 0; + for (let i = 0; i < 26; i++) { + if (s1Count[i] === s2Count[i]) { + matches++; + } + } + + let l = 0; + for (let r = s1.length; r < s2.length; r++) { + if (matches === 26) { + return true; + } + + let index = s2.charCodeAt(r) - 97; + s2Count[index]++; + if (s1Count[index] === s2Count[index]) { + matches++; + } else if (s1Count[index] + 1 === s2Count[index]) { + matches--; + } + + index = s2.charCodeAt(l) - 97; + s2Count[index]--; + if (s1Count[index] === s2Count[index]) { + matches++; + } else if (s1Count[index] - 1 === s2Count[index]) { + matches--; + } + l++; + } + return matches === 26; + } +} +``` + +```csharp +public class Solution { + public bool CheckInclusion(string s1, string s2) { + if (s1.Length > s2.Length) { + return false; + } + + int[] s1Count = new int[26]; + int[] s2Count = new int[26]; + for (int i = 0; i < s1.Length; i++) { + s1Count[s1[i] - 'a']++; + s2Count[s2[i] - 'a']++; + } + + int matches = 0; + for (int i = 0; i < 26; i++) { + if (s1Count[i] == s2Count[i]) { + matches++; + } + } + + int l = 0; + for (int r = s1.Length; r < s2.Length; r++) { + if (matches == 26) { + return true; + } + + int index = s2[r] - 'a'; + s2Count[index]++; + if (s1Count[index] == s2Count[index]) { + matches++; + } else if (s1Count[index] + 1 == s2Count[index]) { + matches--; + } + + index = s2[l] - 'a'; + s2Count[index]--; + if (s1Count[index] == s2Count[index]) { + matches++; + } else if (s1Count[index] - 1 == s2Count[index]) { + matches--; + } + l++; + } + + return matches == 26; + } +} +``` + +```go +func checkInclusion(s1 string, s2 string) bool { + if len(s1) > len(s2) { + return false + } + + s1Count := make([]int, 26) + s2Count := make([]int, 26) + for i := 0; i < len(s1); i++ { + s1Count[s1[i]-'a']++ + s2Count[s2[i]-'a']++ + } + + matches := 0 + for i := 0; i < 26; i++ { + if s1Count[i] == s2Count[i] { + matches++ + } + } + + l := 0 + for r := len(s1); r < len(s2); r++ { + if matches == 26 { + return true + } + + index := s2[r] - 'a' + s2Count[index]++ + if s1Count[index] == s2Count[index] { + matches++ + } else if s1Count[index]+1 == s2Count[index] { + matches-- + } + + index = s2[l] - 'a' + s2Count[index]-- + if s1Count[index] == s2Count[index] { + matches++ + } else if s1Count[index]-1 == s2Count[index] { + matches-- + } + l++ + } + return matches == 26 +} +``` + +```kotlin +class Solution { + fun checkInclusion(s1: String, s2: String): Boolean { + if (s1.length > s2.length) return false + + val s1Count = IntArray(26) + val s2Count = IntArray(26) + for (i in s1.indices) { + s1Count[s1[i] - 'a']++ + s2Count[s2[i] - 'a']++ + } + + var matches = 0 + for (i in 0 until 26) { + if (s1Count[i] == s2Count[i]) matches++ + } + + var l = 0 + for (r in s1.length until s2.length) { + if (matches == 26) return true + + val index = s2[r] - 'a' + s2Count[index]++ + if (s1Count[index] == s2Count[index]) { + matches++ + } else if (s1Count[index] + 1 == s2Count[index]) { + matches-- + } + + val leftIndex = s2[l] - 'a' + s2Count[leftIndex]-- + if (s1Count[leftIndex] == s2Count[leftIndex]) { + matches++ + } else if (s1Count[leftIndex] - 1 == s2Count[leftIndex]) { + matches-- + } + l++ + } + return matches == 26 + } +} +``` + +```swift +class Solution { + func checkInclusion(_ s1: String, _ s2: String) -> Bool { + if s1.count > s2.count { + return false + } + + var s1Count = [Int](repeating: 0, count: 26) + var s2Count = [Int](repeating: 0, count: 26) + let aAscii = Int(Character("a").asciiValue!) + + let s1Array = Array(s1) + let s2Array = Array(s2) + + for i in 0.. List[List[int]]: + res = set() + + def backtrack(perm): + if len(perm) == len(nums): + res.add(tuple(perm)) + return + + for i in range(len(nums)): + if nums[i] != float("-inf"): + perm.append(nums[i]) + nums[i] = float("-inf") + backtrack(perm) + nums[i] = perm[-1] + perm.pop() + + backtrack([]) + return list(res) +``` + +```java +public class Solution { + private Set> res; + + public List> permuteUnique(int[] nums) { + res = new HashSet<>(); + List perm = new ArrayList<>(); + backtrack(nums, perm); + return new ArrayList<>(res); + } + + private void backtrack(int[] nums, List perm) { + if (perm.size() == nums.length) { + res.add(new ArrayList<>(perm)); + return; + } + + for (int i = 0; i < nums.length; i++) { + if (nums[i] != Integer.MIN_VALUE) { + int temp = nums[i]; + perm.add(nums[i]); + nums[i] = Integer.MIN_VALUE; + backtrack(nums, perm); + nums[i] = temp; + perm.remove(perm.size() - 1); + } + } + } +} +``` + +```cpp +class Solution { + set> res; +public: + vector> permuteUnique(vector& nums) { + vector perm; + backtrack(nums, perm); + return vector>(res.begin(), res.end()); + } + +private: + void backtrack(vector& nums, vector& perm) { + if (perm.size() == nums.size()) { + res.insert(perm); + return; + } + + for (int i = 0; i < nums.size(); ++i) { + if (nums[i] != INT_MIN) { + int temp = nums[i]; + perm.push_back(temp); + nums[i] = INT_MIN; + backtrack(nums, perm); + nums[i] = temp; + perm.pop_back(); + } + } + + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + permuteUnique(nums) { + const res = new Set(); + const perm = []; + + const backtrack = () => { + if (perm.length === nums.length) { + res.add(JSON.stringify(perm)); + return; + } + + for (let i = 0; i < nums.length; i++) { + if (nums[i] !== -Infinity) { + let temp = nums[i]; + perm.push(nums[i]); + nums[i] = -Infinity; + backtrack(); + nums[i] = temp; + perm.pop(); + } + } + }; + + backtrack(); + return Array.from(res).map(JSON.parse); + } +} +``` + +```csharp +public class Solution { + public List> PermuteUnique(int[] nums) { + HashSet resSet = new HashSet(); + List> result = new List>(); + Backtrack(new List(), nums, resSet, result); + return result; + } + + private void Backtrack(List perm, int[] nums, HashSet resSet, List> result) { + if (perm.Count == nums.Length) { + string key = string.Join(",", perm); + if (resSet.Add(key)) { + result.Add(new List(perm)); + } + return; + } + + for (int i = 0; i < nums.Length; i++) { + if (nums[i] != int.MinValue) { + int temp = nums[i]; + perm.Add(temp); + nums[i] = int.MinValue; + Backtrack(perm, nums, resSet, result); + nums[i] = temp; + perm.RemoveAt(perm.Count - 1); + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n! * n)$ +* Space complexity: $O(n! * n)$ for the hash set. + +--- + +## 2. Backtracking (Hash Map) + +::tabs-start + +```python +class Solution: + def permuteUnique(self, nums: List[int]) -> List[List[int]]: + res = [] + perm = [] + count = {n: 0 for n in nums} + for num in nums: + count[num] += 1 + + def dfs(): + if len(perm) == len(nums): + res.append(perm.copy()) + return + + for num in count: + if count[num] > 0: + perm.append(num) + count[num] -= 1 + dfs() + count[num] += 1 + perm.pop() + + dfs() + return res +``` + +```java +public class Solution { + private Map count; + private List> res; + + public List> permuteUnique(int[] nums) { + res = new ArrayList<>(); + count = new HashMap<>(); + List perm = new ArrayList<>(); + + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + dfs(nums, perm); + return res; + } + + private void dfs(int[] nums, List perm) { + if (perm.size() == nums.length) { + res.add(new ArrayList<>(perm)); + return; + } + + for (int num : count.keySet()) { + if (count.get(num) > 0) { + perm.add(num); + count.put(num, count.get(num) - 1); + dfs(nums, perm); + count.put(num, count.get(num) + 1); + perm.remove(perm.size() - 1); + } + } + } +} +``` + +```cpp +class Solution { + vector> res; + unordered_map count; + +public: + vector> permuteUnique(vector& nums) { + for (int& num : nums) { + count[num]++; + } + vector perm; + dfs(nums, perm); + return res; + } + + void dfs(vector& nums, vector& perm) { + if (perm.size() == nums.size()) { + res.push_back(perm); + return; + } + for (auto& [num, cnt] : count) { + if (cnt > 0) { + perm.push_back(num); + cnt--; + dfs(nums, perm); + cnt++; + perm.pop_back(); + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + permuteUnique(nums) { + const res = []; + const perm = []; + const count = {}; + + for (const num of nums) { + count[num] = (count[num] || 0) + 1; + } + + const dfs = () => { + if (perm.length === nums.length) { + res.push([...perm]); + return; + } + + for (const num in count) { + if (count[num] > 0) { + perm.push(+num); + count[num]--; + dfs(); + count[num]++; + perm.pop(); + } + } + }; + + dfs(); + return res; + } +} +``` + +```csharp +public class Solution { + public List> PermuteUnique(int[] nums) { + var res = new List>(); + var perm = new List(); + var count = new Dictionary(); + + foreach (int num in nums) { + if (!count.ContainsKey(num)) { + count[num] = 0; + } + count[num]++; + } + + void Dfs() { + if (perm.Count == nums.Length) { + res.Add(new List(perm)); + return; + } + + foreach (var kvp in count) { + int num = kvp.Key; + if (count[num] > 0) { + perm.Add(num); + count[num]--; + Dfs(); + count[num]++; + perm.RemoveAt(perm.Count - 1); + } + } + } + + Dfs(); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n! * n)$ +* Space complexity: $O(n! * n)$ for the output list. + +--- + +## 3. Backtracking (Boolean Array) + +::tabs-start + +```python +class Solution: + def permuteUnique(self, nums: List[int]) -> List[List[int]]: + res, n = [], len(nums) + visit = [False] * n + perm = [] + + def dfs(): + if len(perm) == n: + res.append(perm.copy()) + return + + for i in range(n): + if visit[i]: + continue + + if i and nums[i] == nums[i - 1] and not visit[i - 1]: + continue + visit[i] = True + perm.append(nums[i]) + dfs() + visit[i] = False + perm.pop() + + nums.sort() + dfs() + return res +``` + +```java +public class Solution { + private boolean[] visit; + private List> res; + + public List> permuteUnique(int[] nums) { + res = new ArrayList<>(); + visit = new boolean[nums.length]; + Arrays.sort(nums); + List perm = new ArrayList<>(); + dfs(nums, perm); + return res; + } + + private void dfs(int[] nums, List perm) { + if (perm.size() == nums.length) { + res.add(new ArrayList<>(perm)); + return; + } + + for (int i = 0; i < nums.length; i++) { + if (visit[i] || (i > 0 && nums[i] == nums[i - 1] && !visit[i - 1])) + continue; + + visit[i] = true; + perm.add(nums[i]); + dfs(nums, perm); + visit[i] = false; + perm.remove(perm.size() - 1); + } + } +} +``` + +```cpp +class Solution { + vector> res; + vector visit; + +public: + vector> permuteUnique(vector& nums) { + visit.assign(nums.size(), false); + vector perm; + sort(nums.begin(), nums.end()); + dfs(nums, perm); + return res; + } + + void dfs(vector& nums, vector& perm) { + if (perm.size() == nums.size()) { + res.push_back(perm); + return; + } + for (int i = 0; i < nums.size(); i++) { + if (visit[i] || (i > 0 && nums[i] == nums[i - 1] && !visit[i - 1])) + continue; + + visit[i] = true; + perm.push_back(nums[i]); + dfs(nums, perm); + visit[i] = false; + perm.pop_back(); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + permuteUnique(nums) { + const res = []; + const visit = new Array(nums.length).fill(false); + const perm = []; + nums.sort((a, b) => a - b); + + const dfs = () => { + if (perm.length === nums.length) { + res.push([...perm]); + return; + } + + for (let i = 0; i < nums.length; i++) { + if (visit[i] || (i > 0 && nums[i] === nums[i - 1] && !visit[i - 1])) + continue; + + visit[i] = true; + perm.push(nums[i]); + dfs(); + visit[i] = false; + perm.pop(); + } + }; + + dfs(); + return res; + } +} +``` + +```csharp +public class Solution { + public List> PermuteUnique(int[] nums) { + var res = new List>(); + var perm = new List(); + int n = nums.Length; + bool[] visit = new bool[n]; + Array.Sort(nums); + + void Dfs() { + if (perm.Count == n) { + res.Add(new List(perm)); + return; + } + + for (int i = 0; i < n; i++) { + if (visit[i]) continue; + if (i > 0 && nums[i] == nums[i - 1] && !visit[i - 1]) continue; + + visit[i] = true; + perm.Add(nums[i]); + Dfs(); + perm.RemoveAt(perm.Count - 1); + visit[i] = false; + } + } + + Dfs(); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n! * n)$ +* Space complexity: $O(n! * n)$ for the output list. + +--- + +## 4. Backtracking (Optimal) + +::tabs-start + +```python +class Solution: + def permuteUnique(self, nums: List[int]) -> List[List[int]]: + res = [] + + def dfs(i): + if i == len(nums): + res.append(nums.copy()) + return + + for j in range(i, len(nums)): + if j > i and nums[i] == nums[j]: + continue + + nums[i], nums[j] = nums[j], nums[i] + dfs(i + 1) + + for j in range(len(nums) - 1, i, -1): + nums[j], nums[i] = nums[i], nums[j] + + nums.sort() + dfs(0) + return res +``` + +```java +public class Solution { + private List> res; + + public List> permuteUnique(int[] nums) { + res = new ArrayList<>(); + Arrays.sort(nums); + dfs(0, nums); + return res; + } + + private void dfs(int i, int[] nums) { + if (i == nums.length) { + List temp = new ArrayList<>(); + for (int num : nums) temp.add(num); + res.add(temp); + return; + } + + for (int j = i; j < nums.length; j++) { + if (j > i && nums[j] == nums[i]) continue; + swap(nums, i, j); + dfs(i + 1, nums); + } + + for (int j = nums.length - 1; j > i; j--) { + swap(nums, i, j); + } + } + + private void swap(int[] nums, int i, int j) { + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } +} +``` + +```cpp +class Solution { + vector> res; + +public: + vector> permuteUnique(vector& nums) { + sort(nums.begin(), nums.end()); + dfs(0, nums); + return res; + } + + void dfs(int i, vector& nums) { + if (i == nums.size()) { + res.push_back(nums); + return; + } + + for (int j = i; j < nums.size(); ++j) { + if (j > i && nums[j] == nums[i]) continue; + swap(nums[i], nums[j]); + dfs(i + 1, nums); + } + + for (int j = nums.size() - 1; j > i; --j) { + swap(nums[i], nums[j]); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + permuteUnique(nums) { + const res = []; + nums.sort((a, b) => a - b); + + const dfs = (i) => { + if (i === nums.length) { + res.push([...nums]); + return; + } + + for (let j = i; j < nums.length; j++) { + if (j > i && nums[j] === nums[i]) continue; + [nums[i], nums[j]] = [nums[j], nums[i]]; + dfs(i + 1); + } + + for (let j = nums.length - 1; j > i; j--) { + [nums[i], nums[j]] = [nums[j], nums[i]]; + } + }; + + dfs(0); + return res; + } +} +``` + +```csharp +public class Solution { + public List> PermuteUnique(int[] nums) { + var res = new List>(); + Array.Sort(nums); + Dfs(0); + return res; + + void Dfs(int i) { + if (i == nums.Length) { + res.Add(new List(nums)); + return; + } + + for (int j = i; j < nums.Length; j++) { + if (j > i && nums[j] == nums[i]) continue; + + Swap(i, j); + Dfs(i + 1); + } + + for (int j = nums.Length - 1; j > i; j--) { + Swap(i, j); + } + } + + void Swap(int a, int b) { + int temp = nums[a]; + nums[a] = nums[b]; + nums[b] = temp; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n! * n)$ +* Space complexity: $O(n! * n)$ for the output list. + +--- + +## 5. Iteration + +::tabs-start + +```python +class Solution: + def permuteUnique(self, nums: List[int]) -> List[List[int]]: + n = len(nums) + nums.sort() + res = [nums.copy()] + + while True: + i = n - 2 + while i >= 0 and nums[i] >= nums[i + 1]: + i -= 1 + + if i < 0: + break + + j = n - 1 + while nums[j] <= nums[i]: + j -= 1 + nums[i], nums[j] = nums[j], nums[i] + + l, r = i + 1, n - 1 + while l < r: + nums[l], nums[r] = nums[r], nums[l] + l, r = l + 1, r - 1 + + res.append(nums.copy()) + + return res +``` + +```java +public class Solution { + public List> permuteUnique(int[] nums) { + int n = nums.length; + Arrays.sort(nums); + List> res = new ArrayList<>(); + res.add(toList(nums)); + + while (true) { + int i = n - 2; + while (i >= 0 && nums[i] >= nums[i + 1]) i--; + + if (i < 0) break; + + int j = n - 1; + while (nums[j] <= nums[i]) j--; + + swap(nums, i, j); + reverse(nums, i + 1, n - 1); + res.add(toList(nums)); + } + + return res; + } + + private void reverse(int[] nums, int l, int r) { + while (l < r) { + swap(nums, l++, r--); + } + } + + private void swap(int[] nums, int i, int j) { + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } + + private List toList(int[] nums) { + List list = new ArrayList<>(); + for (int num : nums) list.add(num); + return list; + } +} +``` + +```cpp +class Solution { +public: + vector> permuteUnique(vector& nums) { + int n = nums.size(); + sort(nums.begin(), nums.end()); + vector> res = {nums}; + + while (true) { + int i = n - 2; + while (i >= 0 && nums[i] >= nums[i + 1]) i--; + + if (i < 0) break; + + int j = n - 1; + while (nums[j] <= nums[i]) j--; + + swap(nums[i], nums[j]); + reverse(nums.begin() + i + 1, nums.end()); + res.push_back(nums); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + permuteUnique(nums) { + const n = nums.length; + nums.sort((a, b) => a - b); + const res = [nums.slice()]; + + while (true) { + let i = n - 2; + while (i >= 0 && nums[i] >= nums[i + 1]) i--; + + if (i < 0) break; + + let j = n - 1; + while (nums[j] <= nums[i]) j--; + + [nums[i], nums[j]] = [nums[j], nums[i]]; + + let l = i + 1, r = n - 1; + while (l < r) { + [nums[l], nums[r]] = [nums[r], nums[l]]; + l++; + r--; + } + + res.push(nums.slice()); + } + + return res; + } +} +``` + +```csharp +public class Solution { + public List> PermuteUnique(int[] nums) { + int n = nums.Length; + Array.Sort(nums); + var res = new List>(); + res.Add(new List(nums)); + + while (true) { + int i = n - 2; + while (i >= 0 && nums[i] >= nums[i + 1]) { + i--; + } + + if (i < 0) break; + + int j = n - 1; + while (nums[j] <= nums[i]) { + j--; + } + + Swap(nums, i, j); + + int left = i + 1, right = n - 1; + while (left < right) { + Swap(nums, left++, right--); + } + + res.Add(new List(nums)); + } + + return res; + } + + private void Swap(int[] nums, int i, int j) { + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n! * n)$ +* Space complexity: $O(n! * n)$ for the output list. \ No newline at end of file diff --git a/articles/permutations.md b/articles/permutations.md new file mode 100644 index 000000000..83dea1986 --- /dev/null +++ b/articles/permutations.md @@ -0,0 +1,1021 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def permute(self, nums: List[int]) -> List[List[int]]: + if len(nums) == 0: + return [[]] + + perms = self.permute(nums[1:]) + res = [] + for p in perms: + for i in range(len(p) + 1): + p_copy = p.copy() + p_copy.insert(i, nums[0]) + res.append(p_copy) + return res +``` + +```java +public class Solution { + public List> permute(int[] nums) { + if (nums.length == 0) { + return Arrays.asList(new ArrayList<>()); + } + + List> perms = permute(Arrays.copyOfRange(nums, 1, nums.length)); + List> res = new ArrayList<>(); + for (List p : perms) { + for (int i = 0; i <= p.size(); i++) { + List p_copy = new ArrayList<>(p); + p_copy.add(i, nums[0]); + res.add(p_copy); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> permute(vector& nums) { + if (nums.empty()) { + return {{}}; + } + + vector tmp = vector(nums.begin() + 1, nums.end()); + vector> perms = permute(tmp); + vector> res; + for (const auto& p : perms) { + for (int i = 0; i <= p.size(); i++) { + vector p_copy = p; + p_copy.insert(p_copy.begin() + i, nums[0]); + res.push_back(p_copy); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + permute(nums) { + if (nums.length === 0) { + return [[]]; + } + + let perms = this.permute(nums.slice(1)); + let res = []; + for (let p of perms) { + for (let i = 0; i <= p.length; i++) { + let p_copy = p.slice(); + p_copy.splice(i, 0, nums[0]); + res.push(p_copy); + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public List> Permute(int[] nums) { + if (nums.Length == 0) { + return new List> { new List() }; + } + + var perms = Permute(nums[1..]); + var res = new List>(); + foreach (var p in perms) { + for (int i = 0; i <= p.Count; i++) { + var p_copy = new List(p); + p_copy.Insert(i, nums[0]); + res.Add(p_copy); + } + } + return res; + } +} +``` + +```go +func permute(nums []int) [][]int { + if len(nums) == 0 { + return [][]int{{}} + } + + perms := permute(nums[1:]) + var res [][]int + for _, p := range perms { + for i := 0; i <= len(p); i++ { + pCopy := append([]int{}, p...) + pCopy = append(pCopy[:i], append([]int{nums[0]}, pCopy[i:]...)...) + res = append(res, pCopy) + } + } + return res +} +``` + +```kotlin +class Solution { + fun permute(nums: IntArray): List> { + if (nums.isEmpty()) return listOf(listOf()) + + val perms = permute(nums.sliceArray(1 until nums.size)) + val res = mutableListOf>() + for (p in perms) { + for (i in 0..p.size) { + val pCopy = p.toMutableList() + pCopy.add(i, nums[0]) + res.add(pCopy) + } + } + return res + } +} +``` + +```swift +class Solution { + func permute(_ nums: [Int]) -> [[Int]] { + if nums.isEmpty { + return [[]] + } + + let perms = permute(Array(nums.dropFirst())) + var res = [[Int]]() + + for p in perms { + for i in 0...p.count { + var pCopy = p + pCopy.insert(nums[0], at: i) + res.append(pCopy) + } + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n! * n ^ 2)$ +* Space complexity: $O(n! * n)$ for the output list. + +--- + +## 2. Iteration + +::tabs-start + +```python +class Solution: + def permute(self, nums: List[int]) -> List[List[int]]: + perms = [[]] + for num in nums: + new_perms = [] + for p in perms: + for i in range(len(p) + 1): + p_copy = p.copy() + p_copy.insert(i, num) + new_perms.append(p_copy) + perms = new_perms + return perms +``` + +```java +public class Solution { + public List> permute(int[] nums) { + List> perms = new ArrayList<>(); + perms.add(new ArrayList<>()); + + for (int num : nums) { + List> new_perms = new ArrayList<>(); + for (List p : perms) { + for (int i = 0; i <= p.size(); i++) { + List p_copy = new ArrayList<>(p); + p_copy.add(i, num); + new_perms.add(p_copy); + } + } + perms = new_perms; + } + return perms; + } +} +``` + +```cpp +class Solution { +public: + vector> permute(vector& nums) { + vector> perms = {{}}; + for (int num : nums) { + vector> new_perms; + for (const auto& p : perms) { + for (int i = 0; i <= p.size(); i++) { + vector p_copy = p; + p_copy.insert(p_copy.begin() + i, num); + new_perms.push_back(p_copy); + } + } + perms = new_perms; + } + return perms; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + permute(nums) { + let perms = [[]]; + for (let num of nums) { + let new_perms = []; + for (let p of perms) { + for (let i = 0; i <= p.length; i++) { + let p_copy = p.slice(); + p_copy.splice(i, 0, num); + new_perms.push(p_copy); + } + } + perms = new_perms; + } + return perms; + } +} +``` + +```csharp +public class Solution { + public List> Permute(int[] nums) { + var perms = new List>() { new List() }; + foreach (int num in nums) { + var new_perms = new List>(); + foreach (var p in perms) { + for (int i = 0; i <= p.Count; i++) { + var p_copy = new List(p); + p_copy.Insert(i, num); + new_perms.Add(p_copy); + } + } + perms = new_perms; + } + return perms; + } +} +``` + +```go +func permute(nums []int) [][]int { + perms := [][]int{{}} + + for _, num := range nums { + var newPerms [][]int + for _, p := range perms { + for i := 0; i <= len(p); i++ { + pCopy := append([]int{}, p...) + pCopy = append(pCopy[:i], append([]int{num}, pCopy[i:]...)...) + newPerms = append(newPerms, pCopy) + } + } + perms = newPerms + } + + return perms +} +``` + +```kotlin +class Solution { + fun permute(nums: IntArray): List> { + var perms = mutableListOf(listOf()) + + for (num in nums) { + val newPerms = mutableListOf>() + for (p in perms) { + for (i in 0..p.size) { + val pCopy = p.toMutableList() + pCopy.add(i, num) + newPerms.add(pCopy) + } + } + perms = newPerms + } + + return perms + } +} +``` + +```swift +class Solution { + func permute(_ nums: [Int]) -> [[Int]] { + var perms: [[Int]] = [[]] + + for num in nums { + var newPerms = [[Int]]() + for p in perms { + for i in 0...p.count { + var pCopy = p + pCopy.insert(num, at: i) + newPerms.append(pCopy) + } + } + perms = newPerms + } + + return perms + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n! * n ^ 2)$ +* Space complexity: $O(n! * n)$ for the output list. + +--- + +## 3. Backtracking + +::tabs-start + +```python +class Solution: + def permute(self, nums: List[int]) -> List[List[int]]: + self.res = [] + self.backtrack([], nums, [False] * len(nums)) + return self.res + + def backtrack(self, perm: List[int], nums: List[int], pick: List[bool]): + if len(perm) == len(nums): + self.res.append(perm[:]) + return + for i in range(len(nums)): + if not pick[i]: + perm.append(nums[i]) + pick[i] = True + self.backtrack(perm, nums, pick) + perm.pop() + pick[i] = False +``` + +```java +public class Solution { + List> res; + public List> permute(int[] nums) { + res = new ArrayList<>(); + backtrack(new ArrayList<>(), nums, new boolean[nums.length]); + return res; + } + + public void backtrack(List perm, int[] nums, boolean[] pick) { + if (perm.size() == nums.length) { + res.add(new ArrayList<>(perm)); + return; + } + for (int i = 0; i < nums.length; i++) { + if (!pick[i]) { + perm.add(nums[i]); + pick[i] = true; + backtrack(perm, nums, pick); + perm.remove(perm.size() - 1); + pick[i] = false; + } + } + } +} +``` + +```cpp +class Solution { + vector> res; +public: + vector> permute(vector& nums) { + vector pick(nums.size(), false); + vector perm; + backtrack(perm, nums, pick); + return res; + } + + void backtrack(vector& perm, vector& nums, vector& pick) { + if (perm.size() == nums.size()) { + res.push_back(perm); + return; + } + for (int i = 0; i < nums.size(); i++) { + if (!pick[i]) { + perm.push_back(nums[i]); + pick[i] = true; + backtrack(perm, nums, pick); + perm.pop_back(); + pick[i] = false; + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + permute(nums) { + let res = []; + backtrack([], nums, new Array(nums.length).fill(false)); + return res; + + function backtrack(perm, nums, pick) { + if (perm.length === nums.length) { + res.push([...perm]); + return; + } + for (let i = 0; i < nums.length; i++) { + if (!pick[i]) { + perm.push(nums[i]); + pick[i] = true; + backtrack(perm, nums, pick); + perm.pop(); + pick[i] = false; + } + } + } + } +} +``` + +```csharp +public class Solution { + List> res; + public List> Permute(int[] nums) { + res = new List>(); + Backtrack(new List(), nums, new bool[nums.Length]); + return res; + } + + private void Backtrack(List perm, int[] nums, bool[] pick) { + if (perm.Count == nums.Length) { + res.Add(new List(perm)); + return; + } + for (int i = 0; i < nums.Length; i++) { + if (!pick[i]) { + perm.Add(nums[i]); + pick[i] = true; + Backtrack(perm, nums, pick); + perm.RemoveAt(perm.Count - 1); + pick[i] = false; + } + } + } +} +``` + +```go +func permute(nums []int) [][]int { + var res [][]int + backtrack(&res, []int{}, nums, make([]bool, len(nums))) + return res +} + +func backtrack(res *[][]int, perm []int, nums []int, pick []bool) { + if len(perm) == len(nums) { + temp := append([]int{}, perm...) + *res = append(*res, temp) + return + } + for i := 0; i < len(nums); i++ { + if !pick[i] { + perm = append(perm, nums[i]) + pick[i] = true + backtrack(res, perm, nums, pick) + perm = perm[:len(perm)-1] + pick[i] = false + } + } +} +``` + +```kotlin +class Solution { + private val res = mutableListOf>() + + fun permute(nums: IntArray): List> { + backtrack(mutableListOf(), nums, BooleanArray(nums.size)) + return res + } + + private fun backtrack(perm: MutableList, nums: IntArray, pick: BooleanArray) { + if (perm.size == nums.size) { + res.add(ArrayList(perm)) + return + } + for (i in nums.indices) { + if (!pick[i]) { + perm.add(nums[i]) + pick[i] = true + backtrack(perm, nums, pick) + perm.removeAt(perm.size - 1) + pick[i] = false + } + } + } +} +``` + +```swift +class Solution { + func permute(_ nums: [Int]) -> [[Int]] { + var res = [[Int]]() + var pick = [Bool](repeating: false, count: nums.count) + + func backtrack(_ perm: inout [Int]) { + if perm.count == nums.count { + res.append(perm) + return + } + for i in 0.. List[List[int]]: + self.res = [] + self.backtrack([], nums, 0) + return self.res + + def backtrack(self, perm: List[int], nums: List[int], mask: int): + if len(perm) == len(nums): + self.res.append(perm[:]) + return + for i in range(len(nums)): + if not (mask & (1 << i)): + perm.append(nums[i]) + self.backtrack(perm, nums, mask | (1 << i)) + perm.pop() +``` + +```java +public class Solution { + List> res = new ArrayList<>(); + + public List> permute(int[] nums) { + backtrack(new ArrayList<>(), nums, 0); + return res; + } + + private void backtrack(List perm, int[] nums, int mask) { + if (perm.size() == nums.length) { + res.add(new ArrayList<>(perm)); + return; + } + for (int i = 0; i < nums.length; i++) { + if ((mask & (1 << i)) == 0) { + perm.add(nums[i]); + backtrack(perm, nums, mask | (1 << i)); + perm.remove(perm.size() - 1); + } + } + } +} +``` + +```cpp +class Solution { +public: + vector> res; + + vector> permute(vector& nums) { + backtrack({}, nums, 0); + return res; + } + + void backtrack(vector perm, vector& nums, int mask) { + if (perm.size() == nums.size()) { + res.push_back(perm); + return; + } + for (int i = 0; i < nums.size(); i++) { + if (!(mask & (1 << i))) { + perm.push_back(nums[i]); + backtrack(perm, nums, mask | (1 << i)); + perm.pop_back(); + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + permute(nums) { + let res = []; + this.backtrack([], nums, 0, res); + return res; + } + + /** + * @param {number[]} perm + * @param {number[]} nums + * @param {number} mask + * @param {number[][]} res + * @return {void} + */ + backtrack(perm, nums, mask, res) { + if (perm.length === nums.length) { + res.push([...perm]); + return; + } + for (let i = 0; i < nums.length; i++) { + if (!(mask & (1 << i))) { + perm.push(nums[i]); + this.backtrack(perm, nums, mask | (1 << i), res); + perm.pop(); + } + } + } +} +``` + +```csharp +public class Solution { + List> res = new List>(); + + public List> Permute(int[] nums) { + Backtrack(new List(), nums, 0); + return res; + } + + private void Backtrack(List perm, int[] nums, int mask) { + if (perm.Count == nums.Length) { + res.Add(new List(perm)); + return; + } + for (int i = 0; i < nums.Length; i++) { + if ((mask & (1 << i)) == 0) { + perm.Add(nums[i]); + Backtrack(perm, nums, mask | (1 << i)); + perm.RemoveAt(perm.Count - 1); + } + } + } +} +``` + +```go +func permute(nums []int) [][]int { + var res [][]int + backtrack(&res, []int{}, nums, 0) + return res +} + +func backtrack(res *[][]int, perm []int, nums []int, mask int) { + if len(perm) == len(nums) { + temp := append([]int{}, perm...) + *res = append(*res, temp) + return + } + for i := 0; i < len(nums); i++ { + if mask&(1<>() + + fun permute(nums: IntArray): List> { + backtrack(mutableListOf(), nums, 0) + return res + } + + private fun backtrack(perm: MutableList, nums: IntArray, mask: Int) { + if (perm.size == nums.size) { + res.add(ArrayList(perm)) + return + } + for (i in nums.indices) { + if (mask and (1 shl i) == 0) { + perm.add(nums[i]) + backtrack(perm, nums, mask or (1 shl i)) + perm.removeAt(perm.size - 1) + } + } + } +} +``` + +```swift +class Solution { + func permute(_ nums: [Int]) -> [[Int]] { + var res = [[Int]]() + + func backtrack(_ perm: inout [Int], _ mask: Int) { + if perm.count == nums.count { + res.append(perm) + return + } + for i in 0.. List[List[int]]: + self.res = [] + self.backtrack(nums, 0) + return self.res + + def backtrack(self, nums: List[int], idx: int): + if idx == len(nums): + self.res.append(nums[:]) + return + for i in range(idx, len(nums)): + nums[idx], nums[i] = nums[i], nums[idx] + self.backtrack(nums, idx + 1) + nums[idx], nums[i] = nums[i], nums[idx] +``` + +```java +public class Solution { + List> res; + public List> permute(int[] nums) { + res = new ArrayList<>(); + backtrack(nums, 0); + return res; + } + + public void backtrack(int[] nums, int idx) { + if (idx == nums.length) { + List perm = new ArrayList<>(); + for (int num : nums) perm.add(num); + res.add(perm); + return; + } + for (int i = idx; i < nums.length; i++) { + swap(nums, idx, i); + backtrack(nums, idx + 1); + swap(nums, idx, i); + } + } + + private void swap(int[] nums, int i, int j) { + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } +} +``` + +```cpp +class Solution { + vector> res; +public: + vector> permute(vector& nums) { + backtrack(nums, 0); + return res; + } + + void backtrack(vector& nums, int idx) { + if (idx == nums.size()) { + res.push_back(nums); + return; + } + for (int i = idx; i < nums.size(); i++) { + swap(nums[idx], nums[i]); + backtrack(nums, idx + 1); + swap(nums[idx], nums[i]); + } + } +}; +``` + +```javascript +class Solution { + constructor() { + this.res = []; + } + + /** + * @param {number[]} nums + * @return {number[][]} + */ + permute(nums) { + this.backtrack(nums, 0); + return this.res; + } + + /** + * @param {number[]} nums + * @param {number} idx + * @return {void} + */ + backtrack(nums, idx) { + if (idx === nums.length) { + this.res.push([...nums]); + return; + } + for (let i = idx; i < nums.length; i++) { + [nums[idx], nums[i]] = [nums[i], nums[idx]]; + this.backtrack(nums, idx + 1); + [nums[idx], nums[i]] = [nums[i], nums[idx]]; + } + } +} +``` + +```csharp +public class Solution { + private List> res; + + public List> Permute(int[] nums) { + res = new List>(); + Backtrack(nums, 0); + return res; + } + + private void Backtrack(int[] nums, int idx) { + if (idx == nums.Length) { + res.Add(new List(nums)); + return; + } + for (int i = idx; i < nums.Length; i++) { + Swap(nums, idx, i); + Backtrack(nums, idx + 1); + Swap(nums, idx, i); + } + } + + private void Swap(int[] nums, int i, int j) { + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } +} +``` + +```go +func permute(nums []int) [][]int { + var res [][]int + backtrack(&res, nums, 0) + return res +} + +func backtrack(res *[][]int, nums []int, idx int) { + if idx == len(nums) { + temp := append([]int{}, nums...) + *res = append(*res, temp) + return + } + for i := idx; i < len(nums); i++ { + nums[idx], nums[i] = nums[i], nums[idx] + backtrack(res, nums, idx+1) + nums[idx], nums[i] = nums[i], nums[idx] + } +} +``` + +```kotlin +class Solution { + private val res = mutableListOf>() + + fun permute(nums: IntArray): List> { + backtrack(nums, 0) + return res + } + + private fun backtrack(nums: IntArray, idx: Int) { + if (idx == nums.size) { + res.add(nums.toList()) + return + } + for (i in idx until nums.size) { + nums.swap(idx, i) + backtrack(nums, idx + 1) + nums.swap(idx, i) + } + } + + private fun IntArray.swap(i: Int, j: Int) { + val temp = this[i] + this[i] = this[j] + this[j] = temp + } +} +``` + +```swift +class Solution { + func permute(_ nums: [Int]) -> [[Int]] { + var res = [[Int]]() + var nums = nums + + func backtrack(_ idx: Int) { + if idx == nums.count { + res.append(nums) + return + } + for i in idx.. List[int]: + if not digits: + return [1] + + if digits[-1] < 9: + digits[-1] += 1 + return digits + else: + return self.plusOne(digits[:-1]) + [0] +``` + +```java +public class Solution { + public int[] plusOne(int[] digits) { + if (digits.length == 0) + return new int[]{1}; + + if (digits[digits.length - 1] < 9) { + digits[digits.length - 1] += 1; + return digits; + } else { + int[] newDigits = new int[digits.length - 1]; + System.arraycopy(digits, 0, newDigits, 0, digits.length - 1); + int[] result = plusOne(newDigits); + result = java.util.Arrays.copyOf(result, result.length + 1); + result[result.length - 1] = 0; + return result; + } + } +} +``` + +```cpp +class Solution { +public: + vector plusOne(vector& digits) { + if (digits.empty()) + return {1}; + + if (digits.back() < 9) { + digits.back() += 1; + return digits; + } else { + digits.pop_back(); + vector result = plusOne(digits); + result.push_back(0); + return result; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} digits + * @return {number[]} + */ + plusOne(digits) { + if (digits.length === 0) { + return [1]; + } + + if (digits[digits.length - 1] < 9) { + digits[digits.length - 1] += 1; + return digits; + } else { + return [...this.plusOne(digits.slice(0, digits.length - 1)), 0]; + } + } +} +``` + +```csharp +public class Solution { + public int[] PlusOne(int[] digits) { + if (digits.Length == 0) + return new int[] { 1 }; + + if (digits[digits.Length - 1] < 9) { + digits[digits.Length - 1] += 1; + return digits; + } else { + int[] newDigits = new int[digits.Length - 1]; + Array.Copy(digits, 0, newDigits, 0, digits.Length - 1); + int[] result = PlusOne(newDigits); + Array.Resize(ref result, result.Length + 1); + result[result.Length - 1] = 0; + return result; + } + } +} +``` + +```go +func plusOne(digits []int) []int { + if len(digits) == 0 { + return []int{1} + } + + if digits[len(digits)-1] < 9 { + digits[len(digits)-1]++ + return digits + } else { + return append(plusOne(digits[:len(digits)-1]), 0) + } +} +``` + +```kotlin +class Solution { + fun plusOne(digits: IntArray): IntArray { + if (digits.isEmpty()) { + return intArrayOf(1) + } + + if (digits[digits.size - 1] < 9) { + digits[digits.size - 1]++ + return digits + } else { + return plusOne(digits.copyOfRange(0, digits.size - 1)) + intArrayOf(0) + } + } +} +``` + +```swift +class Solution { + func plusOne(_ digits: [Int]) -> [Int] { + if digits.isEmpty { + return [1] + } + + var digits = digits + if digits[digits.count - 1] < 9 { + digits[digits.count - 1] += 1 + return digits + } else { + return plusOne(Array(digits.dropLast())) + [0] + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Iteration - I + +::tabs-start + +```python +class Solution: + def plusOne(self, digits: List[int]) -> List[int]: + one = 1 + i = 0 + digits.reverse() + + while one: + if i < len(digits): + if digits[i] == 9: + digits[i] = 0 + else: + digits[i] += 1 + one = 0 + else: + digits.append(one) + one = 0 + i += 1 + + digits.reverse() + return digits +``` + +```java +public class Solution { + public int[] plusOne(int[] digits) { + int one = 1; + int i = 0; + boolean carry = true; + + for (int j = digits.length - 1; j >= 0; j--) { + if (carry) { + if (digits[j] == 9) { + digits[j] = 0; + } else { + digits[j]++; + carry = false; + } + } + } + if (carry) { + int[] result = new int[digits.length + 1]; + result[0] = 1; + System.arraycopy(digits, 0, result, 1, digits.length); + return result; + } + return digits; + } +} +``` + +```cpp +class Solution { +public: + vector plusOne(vector& digits) { + int one = 1; + int i = 0; + reverse(digits.begin(), digits.end()); + + while (one) { + if (i < digits.size()) { + if (digits[i] == 9) { + digits[i] = 0; + } else { + digits[i] += 1; + one = 0; + } + } else { + digits.push_back(one); + one = 0; + } + i++; + } + reverse(digits.begin(), digits.end()); + return digits; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} digits + * @return {number[]} + */ + plusOne(digits) { + let one = 1; + let i = 0; + digits.reverse(); + + while (one) { + if (i < digits.length) { + if (digits[i] === 9) { + digits[i] = 0; + } else { + digits[i] += 1; + one = 0; + } + } else { + digits.push(one); + one = 0; + } + i++; + } + digits.reverse(); + return digits; + } +} +``` + +```csharp +public class Solution { + public int[] PlusOne(int[] digits) { + int one = 1; + int i = 0; + bool carry = true; + + for (int j = digits.Length - 1; j >= 0; j--) { + if (carry) { + if (digits[j] == 9) { + digits[j] = 0; + } else { + digits[j]++; + carry = false; + } + } + } + if (carry) { + int[] result = new int[digits.Length + 1]; + result[0] = 1; + for (int j = 0; j < digits.Length; j++) { + result[j + 1] = digits[j]; + } + return result; + } + return digits; + } +} +``` + +```go +func plusOne(digits []int) []int { + one := 1 + i := 0 + digits = reverse(digits) + + for one != 0 { + if i < len(digits) { + if digits[i] == 9 { + digits[i] = 0 + } else { + digits[i] += 1 + one = 0 + } + } else { + digits = append(digits, one) + one = 0 + } + i++ + } + return reverse(digits) +} + +func reverse(digits []int) []int { + for i, j := 0, len(digits)-1; i < j; i, j = i+1, j-1 { + digits[i], digits[j] = digits[j], digits[i] + } + return digits +} +``` + +```kotlin +class Solution { + fun plusOne(digits: IntArray): IntArray { + var one = 1 + var i = 0 + val reversedDigits = digits.reversed().toMutableList() + + while (one != 0) { + if (i < reversedDigits.size) { + if (reversedDigits[i] == 9) { + reversedDigits[i] = 0 + } else { + reversedDigits[i] += 1 + one = 0 + } + } else { + reversedDigits.add(one) + one = 0 + } + i++ + } + + return reversedDigits.reversed().toIntArray() + } +} +``` + +```swift +class Solution { + func plusOne(_ digits: [Int]) -> [Int] { + var digits = digits + var one = 1 + var i = 0 + digits.reverse() + + while one > 0 { + if i < digits.count { + if digits[i] == 9 { + digits[i] = 0 + } else { + digits[i] += 1 + one = 0 + } + } else { + digits.append(one) + one = 0 + } + i += 1 + } + + digits.reverse() + return digits + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the language. + +--- + +## 3. Iteration - II + +::tabs-start + +```python +class Solution: + def plusOne(self, digits: List[int]) -> List[int]: + n = len(digits) + for i in range(n - 1, -1, -1): + if digits[i] < 9: + digits[i] += 1 + return digits + digits[i] = 0 + + return [1] + digits +``` + +```java +class Solution { + public int[] plusOne(int[] digits) { + int n = digits.length; + for (int i = n - 1; i >= 0; i--) { + if (digits[i] < 9) { + digits[i]++; + return digits; + } + digits[i] = 0; + } + int[] result = new int[n + 1]; + result[0] = 1; + return result; + } +} +``` + +```cpp +class Solution { +public: + vector plusOne(vector& digits) { + int n = digits.size(); + for (int i = n - 1; i >= 0; i--) { + if (digits[i] < 9) { + digits[i]++; + return digits; + } + digits[i] = 0; + } + vector result(n + 1); + result[0] = 1; + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} digits + * @return {number[]} + */ + plusOne(digits) { + const n = digits.length; + for (let i = n - 1; i >= 0; i--) { + if (digits[i] < 9) { + digits[i]++; + return digits; + } + digits[i] = 0; + } + const result = new Array(n + 1).fill(0); + result[0] = 1; + return result; + } +} +``` + +```csharp +public class Solution { + public int[] PlusOne(int[] digits) { + int n = digits.Length; + for (int i = n - 1; i >= 0; i--) { + if (digits[i] < 9) { + digits[i]++; + return digits; + } + digits[i] = 0; + } + int[] result = new int[n + 1]; + result[0] = 1; + return result; + } +} +``` + +```go +func plusOne(digits []int) []int { + n := len(digits) + for i := n - 1; i >= 0; i-- { + if digits[i] < 9 { + digits[i]++ + return digits + } + digits[i] = 0 + } + + return append([]int{1}, digits...) +} +``` + +```kotlin +class Solution { + fun plusOne(digits: IntArray): IntArray { + val n = digits.size + for (i in n - 1 downTo 0) { + if (digits[i] < 9) { + digits[i]++ + return digits + } + digits[i] = 0 + } + + return intArrayOf(1) + digits + } +} +``` + +```swift +class Solution { + func plusOne(_ digits: [Int]) -> [Int] { + var digits = digits + let n = digits.count + + for i in stride(from: n - 1, through: 0, by: -1) { + if digits[i] < 9 { + digits[i] += 1 + return digits + } + digits[i] = 0 + } + + return [1] + digits + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/populating-next-right-pointers-in-each-node.md b/articles/populating-next-right-pointers-in-each-node.md new file mode 100644 index 000000000..1f7339287 --- /dev/null +++ b/articles/populating-next-right-pointers-in-each-node.md @@ -0,0 +1,700 @@ +## 1. Breadth First Search + +::tabs-start + +```python +""" +# Definition for a Node. +class Node: + def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None): + self.val = val + self.left = left + self.right = right + self.next = next +""" + +class Solution: + def connect(self, root: 'Optional[Node]') -> 'Optional[Node]': + if not root: + return None + + q = deque([root]) + while q: + levelSize = len(q) + while levelSize: + node = q.popleft() + if levelSize > 1: + node.next = q[0] + if node.left: + q.append(node.left) + if node.right: + q.append(node.right) + levelSize -= 1 + + return root +``` + +```java +/* +// Definition for a Node. +class Node { + public int val; + public Node left; + public Node right; + public Node next; + + public Node() {} + + public Node(int _val) { + val = _val; + } + + public Node(int _val, Node _left, Node _right, Node _next) { + val = _val; + left = _left; + right = _right; + next = _next; + } +}; +*/ + +public class Solution { + public Node connect(Node root) { + if (root == null) return null; + + Queue q = new LinkedList<>(); + q.add(root); + + while (!q.isEmpty()) { + int levelSize = q.size(); + while (levelSize > 0) { + Node node = q.poll(); + if (levelSize > 1) { + node.next = q.peek(); + } + if (node.left != null) { + q.add(node.left); + } + if (node.right != null) { + q.add(node.right); + } + levelSize--; + } + } + + return root; + } +} +``` + +```cpp +/* +// Definition for a Node. +class Node { +public: + int val; + Node* left; + Node* right; + Node* next; + + Node() : val(0), left(NULL), right(NULL), next(NULL) {} + + Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {} + + Node(int _val, Node* _left, Node* _right, Node* _next) + : val(_val), left(_left), right(_right), next(_next) {} +}; +*/ + +class Solution { +public: + Node* connect(Node* root) { + if (!root) return nullptr; + + queue q; + q.push(root); + + while (!q.empty()) { + int levelSize = q.size(); + while (levelSize > 0) { + Node* node = q.front(); + q.pop(); + if (levelSize > 1) { + node->next = q.front(); + } + if (node->left) { + q.push(node->left); + } + if (node->right) { + q.push(node->right); + } + levelSize--; + } + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null, next = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {Node} root + * @return {Node} + */ + connect(root) { + if (!root) return null; + + const q = new Queue(); + q.push(root); + + while (!q.isEmpty()) { + let levelSize = q.size(); + while (levelSize > 0) { + let node = q.pop(); + if (levelSize > 1) { + node.next = q.front(); + } + if (node.left) { + q.push(node.left); + } + if (node.right) { + q.push(node.right); + } + levelSize--; + } + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(\log n)$ + +--- + +## 2. Depth First Search + +::tabs-start + +```python +""" +# Definition for a Node. +class Node: + def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None): + self.val = val + self.left = left + self.right = right + self.next = next +""" + +class Solution: + def connect(self, root: 'Optional[Node]') -> 'Optional[Node]': + mp = {} + + def dfs(node, depth): + if not node: + return + + if depth not in mp: + mp[depth] = node + else: + mp[depth].next = node + mp[depth] = node + + dfs(node.left, depth + 1) + dfs(node.right, depth + 1) + + dfs(root, 0) + return root +``` + +```java +/* +// Definition for a Node. +class Node { + public int val; + public Node left; + public Node right; + public Node next; + + public Node() {} + + public Node(int _val) { + val = _val; + } + + public Node(int _val, Node _left, Node _right, Node _next) { + val = _val; + left = _left; + right = _right; + next = _next; + } +}; +*/ + +public class Solution { + public Node connect(Node root) { + Map mp = new HashMap<>(); + dfs(root, 0, mp); + return root; + } + + private void dfs(Node node, int depth, Map mp) { + if (node == null) return; + + if (!mp.containsKey(depth)) { + mp.put(depth, node); + } else { + mp.get(depth).next = node; + mp.put(depth, node); + } + + dfs(node.left, depth + 1, mp); + dfs(node.right, depth + 1, mp); + } +} +``` + +```cpp +/* +// Definition for a Node. +class Node { +public: + int val; + Node* left; + Node* right; + Node* next; + + Node() : val(0), left(NULL), right(NULL), next(NULL) {} + + Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {} + + Node(int _val, Node* _left, Node* _right, Node* _next) + : val(_val), left(_left), right(_right), next(_next) {} +}; +*/ + +class Solution { +public: + Node* connect(Node* root) { + unordered_map mp; + dfs(root, 0, mp); + return root; + } + +private: + void dfs(Node* node, int depth, unordered_map& mp) { + if (!node) return; + + if (mp.find(depth) == mp.end()) { + mp[depth] = node; + } else { + mp[depth]->next = node; + mp[depth] = node; + } + + dfs(node->left, depth + 1, mp); + dfs(node->right, depth + 1, mp); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null, next = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {Node} root + * @return {Node} + */ + connect(root) { + let mp = new Map(); + + const dfs = (node, depth) => { + if (!node) return; + + if (!mp.has(depth)) { + mp.set(depth, node); + } else { + mp.get(depth).next = node; + mp.set(depth, node); + } + + dfs(node.left, depth + 1); + dfs(node.right, depth + 1); + }; + + dfs(root, 0); + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(\log n)$ + +--- + +## 3. Depth First Search (Optimal) + +::tabs-start + +```python +""" +# Definition for a Node. +class Node: + def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None): + self.val = val + self.left = left + self.right = right + self.next = next +""" + +class Solution: + def connect(self, root: 'Optional[Node]') -> 'Optional[Node]': + if not root: + return root + + if root.left: + root.left.next = root.right + if root.next: + root.right.next = root.next.left + + self.connect(root.left) + self.connect(root.right) + + return root +``` + +```java +/* +// Definition for a Node. +class Node { + public int val; + public Node left; + public Node right; + public Node next; + + public Node() {} + + public Node(int _val) { + val = _val; + } + + public Node(int _val, Node _left, Node _right, Node _next) { + val = _val; + left = _left; + right = _right; + next = _next; + } +}; +*/ + +public class Solution { + public Node connect(Node root) { + if (root == null) return root; + + if (root.left != null) { + root.left.next = root.right; + if (root.next != null) { + root.right.next = root.next.left; + } + + connect(root.left); + connect(root.right); + } + + return root; + } +} +``` + +```cpp +/* +// Definition for a Node. +class Node { +public: + int val; + Node* left; + Node* right; + Node* next; + + Node() : val(0), left(NULL), right(NULL), next(NULL) {} + + Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {} + + Node(int _val, Node* _left, Node* _right, Node* _next) + : val(_val), left(_left), right(_right), next(_next) {} +}; +*/ + +class Solution { +public: + Node* connect(Node* root) { + if (!root) return root; + + if (root->left) { + root->left->next = root->right; + if (root->next) { + root->right->next = root->next->left; + } + + connect(root->left); + connect(root->right); + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null, next = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {Node} root + * @return {Node} + */ + connect(root) { + if (!root) return root; + + if (root.left) { + root.left.next = root.right; + if (root.next) { + root.right.next = root.next.left; + } + + this.connect(root.left); + this.connect(root.right); + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(\log n)$ for the recursion stack. + +--- + +## 4. Breadth First Search (Optimal) + +::tabs-start + +```python +""" +# Definition for a Node. +class Node: + def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None): + self.val = val + self.left = left + self.right = right + self.next = next +""" + +class Solution: + def connect(self, root: 'Optional[Node]') -> 'Optional[Node]': + cur, nxt = root, root.left if root else None + + while cur and nxt: + cur.left.next = cur.right + if cur.next: + cur.right.next = cur.next.left + + cur = cur.next + if not cur: + cur = nxt + nxt = cur.left + + return root +``` + +```java +/* +// Definition for a Node. +class Node { + public int val; + public Node left; + public Node right; + public Node next; + + public Node() {} + + public Node(int _val) { + val = _val; + } + + public Node(int _val, Node _left, Node _right, Node _next) { + val = _val; + left = _left; + right = _right; + next = _next; + } +}; +*/ + +public class Solution { + public Node connect(Node root) { + if (root == null) return null; + + Node cur = root, nxt = root.left; + + while (cur != null && nxt != null) { + cur.left.next = cur.right; + if (cur.next != null) { + cur.right.next = cur.next.left; + } + + cur = cur.next; + if (cur == null) { + cur = nxt; + nxt = cur.left; + } + } + + return root; + } +} +``` + +```cpp +/* +// Definition for a Node. +class Node { +public: + int val; + Node* left; + Node* right; + Node* next; + + Node() : val(0), left(NULL), right(NULL), next(NULL) {} + + Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {} + + Node(int _val, Node* _left, Node* _right, Node* _next) + : val(_val), left(_left), right(_right), next(_next) {} +}; +*/ + +class Solution { +public: + Node* connect(Node* root) { + if (!root) return nullptr; + + Node* cur = root, *nxt = root->left; + + while (cur && nxt) { + cur->left->next = cur->right; + if (cur->next) { + cur->right->next = cur->next->left; + } + + cur = cur->next; + if (!cur) { + cur = nxt; + nxt = cur->left; + } + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null, next = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {Node} root + * @return {Node} + */ + connect(root) { + if (!root) return null; + + let cur = root, nxt = root.left; + + while (cur && nxt) { + cur.left.next = cur.right; + if (cur.next) { + cur.right.next = cur.next.left; + } + + cur = cur.next; + if (!cur) { + cur = nxt; + nxt = cur.left; + } + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/pow-x-n.md b/articles/pow-x-n.md new file mode 100644 index 000000000..50beeb0f4 --- /dev/null +++ b/articles/pow-x-n.md @@ -0,0 +1,576 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def myPow(self, x: float, n: int) -> float: + if x == 0: + return 0 + if n == 0: + return 1 + + res = 1 + for i in range(abs(n)): + res *= x + return res if n >= 0 else 1 / res +``` + +```java +public class Solution { + public double myPow(double x, int n) { + if (x == 0) { + return 0; + } + if (n == 0) { + return 1; + } + + double res = 1; + for (int i = 0; i < Math.abs(n); i++) { + res *= x; + } + return n >= 0 ? res : 1 / res; + } +} +``` + +```cpp +class Solution { +public: + double myPow(double x, int n) { + if (x == 0) { + return 0; + } + if (n == 0) { + return 1; + } + + double res = 1; + for (int i = 0; i < abs(n); i++) { + res *= x; + } + return n >= 0 ? res : 1 / res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @param {number} n + * @return {number} + */ + myPow(x, n) { + if (x === 0) { + return 0; + } + if (n === 0) { + return 1; + } + + let res = 1; + for (let i = 0; i < Math.abs(n); i++) { + res *= x; + } + return n >= 0 ? res : 1 / res; + } +} +``` + +```csharp +public class Solution { + public double MyPow(double x, int n) { + if (x == 0) { + return 0; + } + if (n == 0) { + return 1; + } + + double res = 1; + for (int i = 0; i < Math.Abs(n); i++) { + res *= x; + } + return n >= 0 ? res : 1 / res; + } +} +``` + +```go +func myPow(x float64, n int) float64 { + if x == 0 { + return 0 + } + if n == 0 { + return 1 + } + + res := 1.0 + for i := 0; i < int(math.Abs(float64(n))); i++ { + res *= x + } + + if n >= 0 { + return res + } + return 1 / res +} +``` + +```kotlin +class Solution { + fun myPow(x: Double, n: Int): Double { + if (x == 0.0) { + return 0.0 + } + if (n == 0) { + return 1.0 + } + + var res = 1.0 + for (i in 0 until Math.abs(n)) { + res *= x + } + + return if (n >= 0) res else 1 / res + } +} +``` + +```swift +class Solution { + func myPow(_ x: Double, _ n: Int) -> Double { + if x == 0 { + return 0 + } + if n == 0 { + return 1 + } + + var res: Double = 1 + for _ in 0..= 0 ? res : 1 / res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Binary Exponentiation (Recursive) + +::tabs-start + +```python +class Solution: + def myPow(self, x: float, n: int) -> float: + def helper(x, n): + if x == 0: + return 0 + if n == 0: + return 1 + + res = helper(x * x, n // 2) + return x * res if n % 2 else res + + res = helper(x, abs(n)) + return res if n >= 0 else 1 / res +``` + +```java +public class Solution { + public double myPow(double x, int n) { + if (x == 0) { + return 0; + } + if (n == 0) { + return 1; + } + + double res = helper(x, Math.abs((long) n)); + return (n >= 0) ? res : 1 / res; + } + + private double helper(double x, long n) { + if (n == 0) { + return 1; + } + double half = helper(x, n / 2); + return (n % 2 == 0) ? half * half : x * half * half; + } +} +``` + +```cpp +class Solution { +public: + double myPow(double x, int n) { + if (x == 0) { + return 0; + } + if (n == 0) { + return 1; + } + + double res = helper(x, abs(static_cast(n))); + return (n >= 0) ? res : 1 / res; + } + +private: + double helper(double x, long n) { + if (n == 0) { + return 1; + } + double half = helper(x, n / 2); + return (n % 2 == 0) ? half * half : x * half * half; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @param {number} n + * @return {number} + */ + myPow(x, n) { + /** + * @param {number} x + * @param {number} n + * @return {number} + */ + function helper(x, n) { + if (x === 0) { + return 0; + } + if (n === 0) { + return 1; + } + + const res = helper(x * x, Math.floor(n / 2)); + return n % 2 === 0 ? res : x * res; + } + + const res = helper(x, Math.abs(n)); + return n >= 0 ? res : 1 / res; + } +} +``` + +```csharp +public class Solution { + public double MyPow(double x, int n) { + if (x == 0) { + return 0; + } + if (n == 0) { + return 1; + } + + double res = Helper(x, Math.Abs((long) n)); + return (n >= 0) ? res : 1 / res; + } + + private double Helper(double x, long n) { + if (n == 0) { + return 1; + } + double half = Helper(x, n / 2); + return (n % 2 == 0) ? half * half : x * half * half; + } +} +``` + +```go +func myPow(x float64, n int) float64 { + var helper func(x float64, n int) float64 + helper = func(x float64, n int) float64 { + if x == 0 { + return 0 + } + if n == 0 { + return 1 + } + + res := helper(x*x, n/2) + if n%2 != 0 { + return x * res + } + return res + } + + res := helper(x, int(math.Abs(float64(n)))) + if n >= 0 { + return res + } + return 1 / res +} +``` + +```kotlin +class Solution { + fun myPow(x: Double, n: Int): Double { + fun helper(x: Double, n: Int): Double { + if (x == 0.0) { + return 0.0 + } + if (n == 0) { + return 1.0 + } + + val res = helper(x * x, n / 2) + return if (n % 2 != 0) x * res else res + } + + val res = helper(x, Math.abs(n)) + return if (n >= 0) res else 1 / res + } +} +``` + +```swift +class Solution { + func myPow(_ x: Double, _ n: Int) -> Double { + func helper(_ x: Double, _ n: Int) -> Double { + if x == 0 { + return 0 + } + if n == 0 { + return 1 + } + + let res = helper(x * x, n / 2) + return n % 2 == 0 ? res : x * res + } + + let res = helper(x, abs(n)) + return n >= 0 ? res : 1 / res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ for recursion stack. + +--- + +## 3. Binary Exponentiation (Iterative) + +::tabs-start + +```python +class Solution: + def myPow(self, x: float, n: int) -> float: + if x == 0: + return 0 + if n == 0: + return 1 + + res = 1 + power = abs(n) + + while power: + if power & 1: + res *= x + x *= x + power >>= 1 + + return res if n >= 0 else 1 / res +``` + +```java +public class Solution { + public double myPow(double x, int n) { + if (x == 0) return 0; + if (n == 0) return 1; + + double res = 1; + long power = Math.abs((long)n); + + while (power > 0) { + if ((power & 1) == 1) { + res *= x; + } + x *= x; + power >>= 1; + } + + return n >= 0 ? res : 1 / res; + } +} +``` + +```cpp +class Solution { +public: + double myPow(double x, int n) { + if (x == 0) return 0; + if (n == 0) return 1; + + double res = 1; + long power = abs((long)n); + + while (power) { + if (power & 1) { + res *= x; + } + x *= x; + power >>= 1; + } + + return n >= 0 ? res : 1 / res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @param {number} n + * @return {number} + */ + myPow(x, n) { + if (x === 0) return 0; + if (n === 0) return 1; + + let res = 1; + let power = Math.abs(n); + + while (power > 0) { + if (power & 1) { + res *= x; + } + x *= x; + power >>= 1; + } + + return n >= 0 ? res : 1 / res; + } +} +``` + +```csharp +public class Solution { + public double MyPow(double x, int n) { + if (x == 0) return 0; + if (n == 0) return 1; + + double res = 1; + long power = Math.Abs((long)n); + + while (power > 0) { + if ((power & 1) == 1) { + res *= x; + } + x *= x; + power >>= 1; + } + + return n >= 0 ? res : 1 / res; + } +} +``` + +```go +func myPow(x float64, n int) float64 { + if x == 0 { + return 0 + } + if n == 0 { + return 1 + } + + res := 1.0 + power := int(math.Abs(float64(n))) + + for power > 0 { + if power&1 != 0 { + res *= x + } + x *= x + power >>= 1 + } + + if n >= 0 { + return res + } + return 1 / res +} +``` + +```kotlin +class Solution { + fun myPow(x: Double, n: Int): Double { + var x = x + if (x == 0.0) { + return 0.0 + } + if (n == 0) { + return 1.0 + } + + var res = 1.0 + var power = Math.abs(n.toLong()) + + while (power > 0) { + if (power and 1 != 0L) { + res *= x + } + x *= x + power = power shr 1 + } + + return if (n >= 0) res else 1.0 / res + } +} +``` + +```swift +class Solution { + func myPow(_ x: Double, _ n: Int) -> Double { + if x == 0 { + return 0 + } + if n == 0 { + return 1 + } + + var res: Double = 1 + var base = x + var power = abs(n) + + while power > 0 { + if power & 1 == 1 { + res *= base + } + base *= base + power >>= 1 + } + + return n >= 0 ? res : 1 / res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/power-of-four.md b/articles/power-of-four.md new file mode 100644 index 000000000..dc54669cc --- /dev/null +++ b/articles/power-of-four.md @@ -0,0 +1,367 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def isPowerOfFour(self, n: int) -> bool: + if n == 1: + return True + if n <= 0 or n % 4: + return False + return self.isPowerOfFour(n // 4) +``` + +```java +public class Solution { + public boolean isPowerOfFour(int n) { + if (n == 1) { + return true; + } + if (n <= 0 || n % 4 != 0) { + return false; + } + return isPowerOfFour(n / 4); + } +} +``` + +```cpp +class Solution { +public: + bool isPowerOfFour(int n) { + if (n == 1) { + return true; + } + if (n <= 0 || n % 4 != 0) { + return false; + } + return isPowerOfFour(n / 4); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isPowerOfFour(n) { + if (n === 1) { + return true; + } + if (n <= 0 || n % 4 !== 0) { + return false; + } + return this.isPowerOfFour(Math.floor(n / 4)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 2. Iteration + +::tabs-start + +```python +class Solution: + def isPowerOfFour(self, n: int) -> bool: + if n < 0: + return False + + while n > 1: + if n % 4: + return False + n //= 4 + + return n == 1 +``` + +```java +public class Solution { + public boolean isPowerOfFour(int n) { + if (n < 0) return false; + + while (n > 1) { + if (n % 4 != 0) return false; + n /= 4; + } + + return n == 1; + } +} +``` + +```cpp +class Solution { +public: + bool isPowerOfFour(int n) { + if (n < 0) return false; + + while (n > 1) { + if (n % 4 != 0) return false; + n /= 4; + } + + return n == 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isPowerOfFour(n) { + if (n < 0) return false; + + while (n > 1) { + if (n % 4 !== 0) return false; + n = Math.floor(n / 4); + } + + return n === 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 3. Math + +::tabs-start + +```python +class Solution: + def isPowerOfFour(self, n: int) -> bool: + return n > 0 and log(n, 4) % 1 == 0 +``` + +```java +public class Solution { + public boolean isPowerOfFour(int n) { + return n > 0 && Math.log(n) / Math.log(4) % 1 == 0; + } +} +``` + +```cpp +class Solution { +public: + bool isPowerOfFour(int n) { + return n > 0 && fmod(log(n) / log(4), 1) == 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isPowerOfFour(n) { + return n > 0 && (Math.log(n) / Math.log(4)) % 1 === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 4. Bit Manipulation + +::tabs-start + +```python +class Solution: + def isPowerOfFour(self, n: int) -> bool: + if n < 0: + return False + + for i in range(0, 32, 2): + if n == (1 << i): + return True + + return False +``` + +```java +public class Solution { + public boolean isPowerOfFour(int n) { + if (n < 0) return false; + + for (int i = 0; i < 32; i += 2) { + if (n == (1 << i)) { + return true; + } + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool isPowerOfFour(int n) { + if (n < 0) return false; + + for (int i = 0; i < 32; i += 2) { + if (n == (1 << i)) { + return true; + } + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isPowerOfFour(n) { + if (n < 0) return false; + + for (let i = 0; i < 32; i += 2) { + if (n === (1 << i)) { + return true; + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 5. Bit Mask - I + +::tabs-start + +```python +class Solution: + def isPowerOfFour(self, n: int) -> bool: + return n > 0 and (n & (n - 1)) == 0 and (n & 0x55555555) == n +``` + +```java +public class Solution { + public boolean isPowerOfFour(int n) { + return n > 0 && (n & (n - 1)) == 0 && (n & 0x55555555) == n; + } +} +``` + +```cpp +class Solution { +public: + bool isPowerOfFour(int n) { + return n > 0 && (n & (n - 1)) == 0 && (n & 0x55555555) == n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isPowerOfFour(n) { + return n > 0 && (n & (n - 1)) === 0 && (n & 0x55555555) === n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 6. Bit Mask - II + +::tabs-start + +```python +class Solution: + def isPowerOfFour(self, n: int) -> bool: + return n > 0 and (n & (n - 1)) == 0 and (n % 3 == 1) +``` + +```java +public class Solution { + public boolean isPowerOfFour(int n) { + return n > 0 && (n & (n - 1)) == 0 && (n % 3 == 1); + } +} +``` + +```cpp +class Solution { +public: + bool isPowerOfFour(int n) { + return n > 0 && (n & (n - 1)) == 0 && (n % 3 == 1); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isPowerOfFour(n) { + return n > 0 && (n & (n - 1)) === 0 && (n % 3 == 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/power-of-two.md b/articles/power-of-two.md new file mode 100644 index 000000000..5024ea27a --- /dev/null +++ b/articles/power-of-two.md @@ -0,0 +1,351 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def isPowerOfTwo(self, n: int) -> bool: + if n <= 0: + return False + + x = 1 + while x < n: + x *= 2 + return x == n +``` + +```java +public class Solution { + public boolean isPowerOfTwo(int n) { + if (n <= 0) return false; + + long x = 1; + while (x < n) { + x *= 2; + } + return x == n; + } +} +``` + +```cpp +class Solution { +public: + bool isPowerOfTwo(int n) { + if (n <= 0) return false; + + long long x = 1; + while (x < n) { + x *= 2; + } + return x == n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isPowerOfTwo(n) { + if (n <= 0) return false; + + let x = 1; + while (x < n) { + x *= 2; + } + return x === n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Recursion + +::tabs-start + +```python +class Solution: + def isPowerOfTwo(self, n: int) -> bool: + if n == 1: + return True + if n <= 0 or n % 2 == 1: + return False + return self.isPowerOfTwo(n // 2) +``` + +```java +public class Solution { + public boolean isPowerOfTwo(int n) { + if (n == 1) { + return true; + } + if (n <= 0 || n % 2 == 1) { + return false; + } + return isPowerOfTwo(n / 2); + } +} +``` + +```cpp +class Solution { +public: + bool isPowerOfTwo(int n) { + if (n == 1) { + return true; + } + if (n <= 0 || n % 2 == 1) { + return false; + } + return isPowerOfTwo(n / 2); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isPowerOfTwo(n) { + if (n === 1) { + return true; + } + if (n <= 0 || n % 2 === 1) { + return false; + } + return this.isPowerOfTwo(Math.floor(n / 2)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ for recursion stack. + +--- + +## 3. Iteration + +::tabs-start + +```python +class Solution: + def isPowerOfTwo(self, n: int) -> bool: + if n <= 0: + return False + + while n % 2 == 0: + n >>= 1 + return n == 1 +``` + +```java +public class Solution { + public boolean isPowerOfTwo(int n) { + if (n <= 0) return false; + + while (n % 2 == 0) { + n >>= 1; + } + return n == 1; + } +} +``` + +```cpp +class Solution { +public: + bool isPowerOfTwo(int n) { + if (n <= 0) return false; + + while (n % 2 == 0) { + n >>= 1; + } + return n == 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isPowerOfTwo(n) { + if (n <= 0) return 0; + + while (n % 2 === 0) { + n >>= 1; + } + return n === 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Bit Manipulation - I + +::tabs-start + +```python +class Solution: + def isPowerOfTwo(self, n: int) -> bool: + return n > 0 and (n & (-n)) == n +``` + +```java +public class Solution { + public boolean isPowerOfTwo(int n) { + return n > 0 && (n & (-n)) == n; + } +} +``` + +```cpp +class Solution { +public: + bool isPowerOfTwo(int n) { + return n > 0 && (n & (-n)) == n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isPowerOfTwo(n) { + return n > 0 && (n & (-n)) === n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 5. Bit Manipulation - II + +::tabs-start + +```python +class Solution: + def isPowerOfTwo(self, n: int) -> bool: + return n > 0 and (n & (n - 1)) == 0 +``` + +```java +public class Solution { + public boolean isPowerOfTwo(int n) { + return n > 0 && (n & (n - 1)) == 0; + } +} +``` + +```cpp +class Solution { +public: + bool isPowerOfTwo(int n) { + return n > 0 && (n & (n - 1)) == 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isPowerOfTwo(n) { + return n > 0 && (n & (n - 1)) === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 6. Math + +::tabs-start + +```python +class Solution: + def isPowerOfTwo(self, n: int) -> bool: + return n > 0 and ((1 << 30) % n) == 0 +``` + +```java +public class Solution { + public boolean isPowerOfTwo(int n) { + return n > 0 && ((1 << 30) % n) == 0; + } +} +``` + +```cpp +class Solution { +public: + bool isPowerOfTwo(int n) { + return n > 0 && ((1 << 30) % n) == 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isPowerOfTwo(n) { + return n > 0 && ((1 << 30) % n) === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/prefix-and-suffix-search.md b/articles/prefix-and-suffix-search.md new file mode 100644 index 000000000..a2674805b --- /dev/null +++ b/articles/prefix-and-suffix-search.md @@ -0,0 +1,596 @@ +## 1. Brute Force + +::tabs-start + +```python +class WordFilter: + + def __init__(self, words: List[str]): + self.words = words + + def f(self, pref: str, suff: str) -> int: + for i in range(len(self.words) - 1, -1, -1): + w = self.words[i] + if len(w) < len(pref) or len(w) < len(suff): + continue + + j, flag = 0, True + for c in pref: + if w[j] != c: + flag = False + break + j += 1 + + if not flag: + continue + + j = len(w) - len(suff) + for c in suff: + if w[j] != c: + flag = False + break + j += 1 + + if flag: + return i + + return -1 +``` + +```java +public class WordFilter { + private String[] words; + + public WordFilter(String[] words) { + this.words = words; + } + + public int f(String pref, String suff) { + for (int i = words.length - 1; i >= 0; i--) { + String w = words[i]; + if (w.length() < pref.length() || w.length() < suff.length()) { + continue; + } + + boolean flag = true; + for (int j = 0; j < pref.length(); j++) { + if (w.charAt(j) != pref.charAt(j)) { + flag = false; + break; + } + } + + if (!flag) { + continue; + } + + int j = w.length() - suff.length(); + for (int k = 0; k < suff.length(); k++) { + if (w.charAt(j + k) != suff.charAt(k)) { + flag = false; + break; + } + } + + if (flag) { + return i; + } + } + + return -1; + } +} +``` + +```cpp +class WordFilter { +private: + vector words; + +public: + WordFilter(vector& words) { + this->words = words; + } + + int f(string pref, string suff) { + for (int i = words.size() - 1; i >= 0; i--) { + const string& w = words[i]; + if (w.size() < pref.size() || w.size() < suff.size()) { + continue; + } + + bool flag = true; + for (int j = 0; j < pref.size(); j++) { + if (w[j] != pref[j]) { + flag = false; + break; + } + } + + if (!flag) { + continue; + } + + int j = w.size() - suff.size(); + for (int k = 0; k < suff.size(); k++) { + if (w[j + k] != suff[k]) { + flag = false; + break; + } + } + + if (flag) { + return i; + } + } + + return -1; + } +}; +``` + +```javascript +class WordFilter { + /** + * @constructor + * @param {string[]} words + */ + constructor(words) { + this.words = words; + } + + /** + * @param {string} pref + * @param {string} suff + * @return {number} + */ + f(pref, suff) { + for (let i = this.words.length - 1; i >= 0; i--) { + const w = this.words[i]; + if (w.length < pref.length || w.length < suff.length) { + continue; + } + + let flag = true; + for (let j = 0; j < pref.length; j++) { + if (w[j] !== pref[j]) { + flag = false; + break; + } + } + + if (!flag) { + continue; + } + + let j = w.length - suff.length; + for (let k = 0; k < suff.length; k++) { + if (w[j + k] !== suff[k]) { + flag = false; + break; + } + } + + if (flag) { + return i; + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N * m * n)$ +* Space complexity: $O(1)$ extra space. + +> Where $N$ is the number $f()$ function calls, $n$ is the number of words, and $m$ is the average length of each word. + +--- + +## 2. Hash Map + +::tabs-start + +```python +class WordFilter: + + def __init__(self, words: List[str]): + self.mp = {} + for i, w in enumerate(words): + for j in range(len(w)): + pref = w[:j + 1] + for k in range(len(w)): + cur = pref + "$" + w[k:] + self.mp[cur] = i + + def f(self, pref: str, suff: str) -> int: + s = pref + "$" + suff + if s not in self.mp: + return -1 + + return self.mp[s] +``` + +```java +public class WordFilter { + private Map mp; + + public WordFilter(String[] words) { + mp = new HashMap<>(); + for (int i = 0; i < words.length; i++) { + String w = words[i]; + for (int j = 0; j < w.length(); j++) { + String pref = w.substring(0, j + 1); + for (int k = 0; k < w.length(); k++) { + String cur = pref + "$" + w.substring(k); + mp.put(cur, i); + } + } + } + } + + public int f(String pref, String suff) { + String s = pref + "$" + suff; + return mp.getOrDefault(s, -1); + } +} +``` + +```cpp +class WordFilter { +private: + unordered_map mp; + +public: + WordFilter(vector& words) { + for (int i = 0; i < words.size(); i++) { + string w = words[i]; + for (int j = 0; j < w.size(); j++) { + string pref = w.substr(0, j + 1); + for (int k = 0; k < w.size(); k++) { + string cur = pref + "$" + w.substr(k); + mp[cur] = i; + } + } + } + } + + int f(string pref, string suff) { + string s = pref + "$" + suff; + if (mp.find(s) == mp.end()) { + return -1; + } + return mp[s]; + } +}; +``` + +```javascript +class WordFilter { + /** + * @constructor + * @param {string[]} words + */ + constructor(words) { + this.mp = new Map(); + for (let i = 0; i < words.length; i++) { + const w = words[i]; + for (let j = 0; j < w.length; j++) { + const pref = w.slice(0, j + 1); + for (let k = 0; k < w.length; k++) { + const cur = pref + "$" + w.slice(k); + this.mp.set(cur, i); + } + } + } + } + + /** + * @param {string} pref + * @param {string} suff + * @return {number} + */ + f(pref, suff) { + const s = pref + "$" + suff; + return this.mp.has(s) ? this.mp.get(s) : -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n * m ^ 3)$ time for initialization. + * $O(m)$ for each $f()$ function call. +* Space complexity: $O(n * m ^ 3)$ + +> Where $n$ is the number of words and $m$ is the average length of each word. + +--- + +## 3. Trie + +::tabs-start + +```python +class TrieNode: + def __init__(self): + self.children = [None] * 27 + self.index = -1 + +class Trie: + def __init__(self): + self.root = TrieNode() + + def addWord(self, w, i): + cur = self.root + for ch in w: + c = ord(ch) - ord('a') + if not cur.children[c]: + cur.children[c] = TrieNode() + cur = cur.children[c] + cur.index = i + + def search(self, w): + cur = self.root + for ch in w: + c = ord(ch) - ord('a') + if not cur.children[c]: + return -1 + cur = cur.children[c] + return cur.index + +class WordFilter: + def __init__(self, words: List[str]): + self.trie = Trie() + self.CHAR = '{' + for i, w in enumerate(words): + w_len = len(w) + for j in range(w_len): + suffix = w[j:] + for k in range(w_len + 1): + prefix = w[:k] + self.trie.addWord(suffix + self.CHAR + prefix, i) + + def f(self, pref: str, suff: str) -> int: + return self.trie.search(suff + self.CHAR + pref) +``` + +```java +class TrieNode { + TrieNode[] children; + int index; + + TrieNode() { + children = new TrieNode[27]; + index = -1; + } +} + +class Trie { + private TrieNode root; + + Trie() { + root = new TrieNode(); + } + + void addWord(String word, int i) { + TrieNode cur = root; + for (char ch : word.toCharArray()) { + int c = ch == '{' ? 26 : ch - 'a'; + if (cur.children[c] == null) { + cur.children[c] = new TrieNode(); + } + cur = cur.children[c]; + } + cur.index = i; + } + + int search(String word) { + TrieNode cur = root; + for (char ch : word.toCharArray()) { + int c = ch == '{' ? 26 : ch - 'a'; + if (cur.children[c] == null) { + return -1; + } + cur = cur.children[c]; + } + return cur.index; + } +} + +public class WordFilter { + private Trie trie; + private static final char CHAR = '{'; + + public WordFilter(String[] words) { + trie = new Trie(); + for (int i = 0; i < words.length; i++) { + String word = words[i]; + int wLen = word.length(); + for (int j = 0; j < wLen; j++) { + String suffix = word.substring(j); + for (int k = 0; k <= wLen; k++) { + String prefix = word.substring(0, k); + trie.addWord(suffix + CHAR + prefix, i); + } + } + } + } + + public int f(String pref, String suff) { + return trie.search(suff + CHAR + pref); + } +} +``` + +```cpp +class TrieNode { +public: + TrieNode* children[27]; + int index; + + TrieNode() { + for (int i = 0; i < 27; i++) { + children[i] = nullptr; + } + index = -1; + } +}; + +class Trie { +private: + TrieNode* root; + +public: + Trie() { + root = new TrieNode(); + } + + void addWord(const string& word, int i) { + TrieNode* cur = root; + for (char ch : word) { + int c = (ch == '{') ? 26 : (ch - 'a'); + if (cur->children[c] == nullptr) { + cur->children[c] = new TrieNode(); + } + cur = cur->children[c]; + } + cur->index = i; + } + + int search(const string& word) { + TrieNode* cur = root; + for (char ch : word) { + int c = (ch == '{') ? 26 : (ch - 'a'); + if (cur->children[c] == nullptr) { + return -1; + } + cur = cur->children[c]; + } + return cur->index; + } +}; + +class WordFilter { +private: + Trie trie; + const char CHAR = '{'; + +public: + WordFilter(vector& words) { + for (int i = 0; i < words.size(); i++) { + string word = words[i]; + int wLen = word.length(); + for (int j = 0; j < wLen; j++) { + string suffix = word.substr(j); + for (int k = 0; k <= wLen; k++) { + string prefix = word.substr(0, k); + trie.addWord(suffix + CHAR + prefix, i); + } + } + } + } + + int f(string pref, string suff) { + return trie.search(suff + CHAR + pref); + } +}; +``` + +```javascript +class TrieNode { + constructor() { + this.children = Array(27).fill(null); + this.index = -1; + } +} + +class Trie { + constructor() { + this.root = new TrieNode(); + } + + /** + * @param {string} word + * @param {number} i + * @return {void} + */ + addWord(word, i) { + let cur = this.root; + for (const ch of word) { + const c = ch === '{' ? 26 : ch.charCodeAt(0) - 'a'.charCodeAt(0); + if (!cur.children[c]) { + cur.children[c] = new TrieNode(); + } + cur = cur.children[c]; + } + cur.index = i; + } + + /** + * @param {string} word + * @return {number} + */ + search(word) { + let cur = this.root; + for (const ch of word) { + const c = ch === '{' ? 26 : ch.charCodeAt(0) - 'a'.charCodeAt(0); + if (!cur.children[c]) { + return -1; + } + cur = cur.children[c]; + } + return cur.index; + } +} + +class WordFilter { + /** + * @constructor + * @param {string[]} words + */ + constructor(words) { + this.trie = new Trie(); + this.CHAR = '{'; + for (let i = 0; i < words.length; i++) { + const word = words[i]; + const wLen = word.length; + for (let j = 0; j < wLen; j++) { + const suffix = word.substring(j); + for (let k = 0; k <= wLen; k++) { + const prefix = word.substring(0, k); + this.trie.addWord(suffix + this.CHAR + prefix, i); + } + } + } + } + + /** + * @param {string} pref + * @param {string} suff + * @return {number} + */ + f(pref, suff) { + return this.trie.search(suff + this.CHAR + pref); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n * m ^ 3)$ time for initialization. + * $O(m)$ for each $f()$ function call. +* Space complexity: $O(n * m ^ 3)$ + +> Where $n$ is the number of words and $m$ is the average length of each word. \ No newline at end of file diff --git a/articles/process-tasks-using-servers.md b/articles/process-tasks-using-servers.md new file mode 100644 index 000000000..f79a9c753 --- /dev/null +++ b/articles/process-tasks-using-servers.md @@ -0,0 +1,501 @@ +## 1. Brute Force (Simulation) + +::tabs-start + +```python +class Solution: + def assignTasks(self, servers: List[int], tasks: List[int]) -> List[int]: + n, m = len(servers), len(tasks) + available = [True] * n + finishTime = [0] * n + res = [] + time = 0 + + for t in range(m): + time = max(time, t) + + for i in range(n): + if finishTime[i] <= time: + available[i] = True + + if not any(available): + time = min(finishTime) + for i in range(n): + if finishTime[i] <= time: + available[i] = True + + minIdx = -1 + for i in range(n): + if (available[i] and + (minIdx == -1 or servers[i] < servers[minIdx] or + (servers[i] == servers[minIdx] and i < minIdx)) + ): + minIdx = i + + res.append(minIdx) + available[minIdx] = False + finishTime[minIdx] = time + tasks[t] + + return res +``` + +```java +public class Solution { + public int[] assignTasks(int[] servers, int[] tasks) { + int n = servers.length, m = tasks.length; + boolean[] available = new boolean[n]; + Arrays.fill(available, true); + int[] finishTime = new int[n]; + int[] res = new int[m]; + int time = 0; + + for (int t = 0; t < m; t++) { + time = Math.max(time, t); + for (int i = 0; i < n; i++) { + if (finishTime[i] <= time) { + available[i] = true; + } + } + + if (!anyAvailable(available)) { + time = Arrays.stream(finishTime).min().getAsInt(); + for (int i = 0; i < n; i++) { + if (finishTime[i] <= time) { + available[i] = true; + } + } + } + + int minIdx = -1; + for (int i = 0; i < n; i++) { + if (available[i] && (minIdx == -1 || servers[i] < servers[minIdx] || + (servers[i] == servers[minIdx] && i < minIdx))) { + minIdx = i; + } + } + + res[t] = minIdx; + available[minIdx] = false; + finishTime[minIdx] = time + tasks[t]; + } + return res; + } + + private boolean anyAvailable(boolean[] available) { + for (boolean v : available) { + if (v) return true; + } + return false; + } +} +``` + +```cpp +class Solution { +public: + vector assignTasks(vector& servers, vector& tasks) { + int n = servers.size(), m = tasks.size(); + vector available(n, true); + vector finishTime(n, 0), res(m); + int time = 0; + + for (int t = 0; t < m; t++) { + time = max(time, t); + + for (int i = 0; i < n; i++) { + if (finishTime[i] <= time) { + available[i] = true; + } + } + + if (!any_of(available.begin(), available.end(), [](bool v) { return v; })) { + time = *min_element(finishTime.begin(), finishTime.end()); + for (int i = 0; i < n; i++) { + if (finishTime[i] <= time) { + available[i] = true; + } + } + } + + int minIdx = -1; + for (int i = 0; i < n; i++) { + if (available[i] && (minIdx == -1 || servers[i] < servers[minIdx] || + (servers[i] == servers[minIdx] && i < minIdx))) { + minIdx = i; + } + } + + res[t] = minIdx; + available[minIdx] = false; + finishTime[minIdx] = time + tasks[t]; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} servers + * @param {number[]} tasks + * @return {number[]} + */ + assignTasks(servers, tasks) { + const n = servers.length, m = tasks.length; + const available = Array(n).fill(true); + const finishTime = Array(n).fill(0); + const res = []; + let time = 0; + + for (let t = 0; t < m; t++) { + time = Math.max(time, t); + + for (let i = 0; i < n; i++) { + if (finishTime[i] <= time) { + available[i] = true; + } + } + + if (!available.some(v => v)) { + time = Math.min(...finishTime); + for (let i = 0; i < n; i++) { + if (finishTime[i] <= time) { + available[i] = true; + } + } + } + + let minIdx = -1; + for (let i = 0; i < n; i++) { + if (available[i] && (minIdx === -1 || servers[i] < servers[minIdx] || + (servers[i] === servers[minIdx] && i < minIdx))) { + minIdx = i; + } + } + + res.push(minIdx); + available[minIdx] = false; + finishTime[minIdx] = time + tasks[t]; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: + * $O(n)$ extra space. + * $O(m)$ space for the output array. + +> Where $m$ is the number of tasks and $n$ is the number of servers. + +--- + +## 2. Two Min-Heaps - I + +::tabs-start + +```python +class Solution: + def assignTasks(self, servers: List[int], tasks: List[int]) -> List[int]: + res = [0] * len(tasks) + available = [(servers[i], i) for i in range(len(servers))] + heapq.heapify(available) + unavailable = [] + + t = 0 + for i in range(len(tasks)): + t = max(t, i) + + if not available: + t = unavailable[0][0] + + while unavailable and t >= unavailable[0][0]: + timeFree, weight, index = heapq.heappop(unavailable) + heapq.heappush(available, (weight, index)) + + weight, index = heapq.heappop(available) + res[i] = index + heapq.heappush(unavailable, (t + tasks[i], weight, index)) + + return res +``` + +```java +public class Solution { + public int[] assignTasks(int[] servers, int[] tasks) { + int n = servers.length, m = tasks.length; + int[] res = new int[m]; + + PriorityQueue available = new PriorityQueue<>( + (a, b) -> a[0] == b[0] ? Integer.compare(a[1], b[1]) : Integer.compare(a[0], b[0]) + ); + PriorityQueue unavailable = new PriorityQueue<>( + Comparator.comparingInt(a -> a[0]) + ); + + for (int i = 0; i < n; i++) { + available.offer(new int[]{servers[i], i}); + } + + int time = 0; + for (int i = 0; i < m; i++) { + time = Math.max(time, i); + + if (available.isEmpty()) { + time = unavailable.peek()[0]; + } + + while (!unavailable.isEmpty() && unavailable.peek()[0] <= time) { + int[] server = unavailable.poll(); + available.offer(new int[]{server[1], server[2]}); + } + + int[] bestServer = available.poll(); + res[i] = bestServer[1]; + unavailable.offer(new int[]{time + tasks[i], bestServer[0], bestServer[1]}); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector assignTasks(vector& servers, vector& tasks) { + int n = servers.size(), m = tasks.size(); + vector res(m); + + priority_queue, vector>, greater<>> available; + priority_queue, vector>, greater<>> unavailable; + + for (int i = 0; i < n; i++) { + available.emplace(servers[i], i); + } + + int time = 0; + for (int i = 0; i < m; i++) { + time = max(time, i); + + if (available.empty()) { + time = unavailable.top()[0]; + } + + while (!unavailable.empty() && unavailable.top()[0] <= time) { + auto server = unavailable.top(); unavailable.pop(); + available.emplace(server[1], server[2]); + } + + auto [weight, index] = available.top(); available.pop(); + res[i] = index; + unavailable.push({time + tasks[i], weight, index}); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} servers + * @param {number[]} tasks + * @return {number[]} + */ + assignTasks(servers, tasks) { + const n = servers.length; + const available = new PriorityQueue( + (a, b) => a[0] === b[0] ? a[1] - b[1] : a[0] - b[0] + ); + const unavailable = new PriorityQueue((a, b) => a[0] - b[0]); + const res = new Array(tasks.length); + + for (let i = 0; i < n; i++) { + available.enqueue([servers[i], i]); + } + + let time = 0; + for (let i = 0; i < tasks.length; i++) { + time = Math.max(time, i); + if (available.isEmpty()) { + time = unavailable.front()[0]; + } + while (!unavailable.isEmpty() && unavailable.front()[0] <= time) { + const [timeFree, weight, index] = unavailable.dequeue(); + available.enqueue([weight, index]); + } + const [weight, index] = available.dequeue(); + res[i] = index; + unavailable.enqueue([time + tasks[i], weight, index]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((n + m) \log n)$ +* Space complexity: + * $O(n)$ extra space. + * $O(m)$ space for the output array. + +> Where $m$ is the number of tasks and $n$ is the number of servers. + +--- + +## 3. Two Min-Heaps - II + +::tabs-start + +```python +class Solution: + def assignTasks(self, servers: List[int], tasks: List[int]) -> List[int]: + res = [] + available = [[weight, i, 0] for i, weight in enumerate(servers)] + unavailable = [] + heapq.heapify(available) + + for i, task in enumerate(tasks): + while unavailable and unavailable[0][0] <= i or not available: + timeFree, weight, index = heapq.heappop(unavailable) + heapq.heappush(available, [weight, index, timeFree]) + + weight, index, timeFree = heapq.heappop(available) + res.append(index) + heapq.heappush(unavailable, [max(timeFree, i) + task, weight, index]) + + return res +``` + +```java +public class Solution { + public int[] assignTasks(int[] servers, int[] tasks) { + int m = tasks.length, n = servers.length; + int[] res = new int[m]; + + PriorityQueue available = new PriorityQueue<>((a, b) -> { + if(a[0] != b[0]) return Integer.compare(a[0], b[0]); + if(a[1] != b[1]) return Integer.compare(a[1], b[1]); + return Integer.compare(a[2], b[2]); + }); + + PriorityQueue unavailable = new PriorityQueue<>((a, b) -> { + if(a[0] != b[0]) return Integer.compare(a[0], b[0]); + if(a[1] != b[1]) return Integer.compare(a[1], b[1]); + return Integer.compare(a[2], b[2]); + }); + + for (int i = 0; i < n; i++) { + available.offer(new int[]{servers[i], i, 0}); + } + + for (int i = 0; i < m; i++) { + while ((!unavailable.isEmpty() && unavailable.peek()[0] <= i) || + available.isEmpty()) { + int[] server = unavailable.poll(); + available.offer(new int[]{server[1], server[2], server[0]}); + } + int[] server = available.poll(); + res[i] = server[1]; + unavailable.offer(new int[]{ + Math.max(server[2], i) + tasks[i], server[0], server[1]} + ); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector assignTasks(vector& servers, vector& tasks) { + int n = servers.size(), m = tasks.size(); + vector res(m); + + priority_queue, vector>, greater<>> available; + priority_queue, vector>, greater<>> unavailable; + + for (int i = 0; i < n; ++i) { + available.push({servers[i], i, 0}); + } + + for (int i = 0; i < m; ++i) { + while (!unavailable.empty() && (unavailable.top()[0] <= i || + available.empty())) { + auto [timeFree, weight, index] = unavailable.top(); + unavailable.pop(); + available.push({weight, index, timeFree}); + } + + auto [weight, index, timeFree] = available.top(); + available.pop(); + res[i] = index; + unavailable.push({max(timeFree, i) + tasks[i], weight, index}); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} servers + * @param {number[]} tasks + * @return {number[]} + */ + assignTasks(servers, tasks) { + const res = new Array(tasks.length); + const available = new PriorityQueue( + (a, b) => a[0] === b[0] ? (a[1] === b[1] ? a[2] - b[2] : a[1] - b[1]) : a[0] - b[0] + ); + const unavailable = new PriorityQueue( + (a, b) => a[0] === b[0] ? (a[1] === b[1] ? a[2] - b[2] : a[1] - b[1]) : a[0] - b[0] + ); + + for (let i = 0; i < servers.length; i++) { + available.enqueue([servers[i], i, 0]); + } + + for (let i = 0; i < tasks.length; i++) { + while ((!unavailable.isEmpty() && unavailable.front()[0] <= i) || + available.isEmpty()) { + const [timeFree, weight, index] = unavailable.dequeue(); + available.enqueue([weight, index, timeFree]); + } + + const [weight, index, timeFree] = available.dequeue(); + res[i] = index; + unavailable.enqueue([Math.max(timeFree, i) + tasks[i], weight, index]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((n + m) \log n)$ +* Space complexity: + * $O(n)$ extra space. + * $O(m)$ space for the output array. + +> Where $m$ is the number of tasks and $n$ is the number of servers. \ No newline at end of file diff --git a/articles/products-of-array-discluding-self.md b/articles/products-of-array-discluding-self.md new file mode 100644 index 000000000..1370fac97 --- /dev/null +++ b/articles/products-of-array-discluding-self.md @@ -0,0 +1,813 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def productExceptSelf(self, nums: List[int]) -> List[int]: + n = len(nums) + res = [0] * n + + for i in range(n): + prod = 1 + for j in range(n): + if i == j: + continue + prod *= nums[j] + + res[i] = prod + return res +``` + +```java +public class Solution { + public int[] productExceptSelf(int[] nums) { + int n = nums.length; + int[] res = new int[n]; + + for (int i = 0; i < n; i++) { + int prod = 1; + for (int j = 0; j < n; j++) { + if (i != j) { + prod *= nums[j]; + } + } + res[i] = prod; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector productExceptSelf(vector& nums) { + int n = nums.size(); + vector res(n); + + for (int i = 0; i < n; i++) { + int prod = 1; + for (int j = 0; j < n; j++) { + if (i != j) { + prod *= nums[j]; + } + } + res[i] = prod; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + productExceptSelf(nums) { + const n = nums.length; + const res = new Array(n); + + for (let i = 0; i < n; i++) { + let prod = 1; + for (let j = 0; j < n; j++) { + if (i !== j) { + prod *= nums[j]; + } + } + res[i] = prod; + } + return res; + } +} +``` + +```csharp +public class Solution { + public int[] ProductExceptSelf(int[] nums) { + int n = nums.Length; + int[] res = new int[n]; + + for (int i = 0; i < n; i++) { + int prod = 1; + for (int j = 0; j < n; j++) { + if (i != j) { + prod *= nums[j]; + } + } + res[i] = prod; + } + return res; + } +} +``` + +```go +func productExceptSelf(nums []int) []int { + n := len(nums) + res := make([]int, n) + + for i := 0; i < n; i++ { + prod := 1 + for j := 0; j < n; j++ { + if i == j { + continue + } + prod *= nums[j] + } + res[i] = prod + } + return res +} +``` + +```kotlin +class Solution { + fun productExceptSelf(nums: IntArray): IntArray { + val n = nums.size + val res = IntArray(n) + + for (i in 0 until n) { + var prod = 1 + for (j in 0 until n) { + if (i == j) continue + prod *= nums[j] + } + res[i] = prod + } + return res + } +} +``` + +```swift +class Solution { + func productExceptSelf(_ nums: [Int]) -> [Int] { + let n = nums.count + var res = [Int](repeating: 0, count: n) + + for i in 0.. List[int]: + prod, zero_cnt = 1, 0 + for num in nums: + if num: + prod *= num + else: + zero_cnt += 1 + if zero_cnt > 1: return [0] * len(nums) + + res = [0] * len(nums) + for i, c in enumerate(nums): + if zero_cnt: res[i] = 0 if c else prod + else: res[i] = prod // c + return res +``` + +```java +public class Solution { + public int[] productExceptSelf(int[] nums) { + int prod = 1, zeroCount = 0; + for (int num : nums) { + if (num != 0) { + prod *= num; + } else { + zeroCount++; + } + } + + if (zeroCount > 1) { + return new int[nums.length]; + } + + int[] res = new int[nums.length]; + for (int i = 0; i < nums.length; i++) { + if (zeroCount > 0) { + res[i] = (nums[i] == 0) ? prod : 0; + } else { + res[i] = prod / nums[i]; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector productExceptSelf(vector& nums) { + int prod = 1, zeroCount = 0; + for (int num : nums) { + if (num != 0) { + prod *= num; + } else { + zeroCount++; + } + } + + if (zeroCount > 1) { + return vector(nums.size(), 0); + } + + vector res(nums.size()); + for (size_t i = 0; i < nums.size(); i++) { + if (zeroCount > 0) { + res[i] = (nums[i] == 0) ? prod : 0; + } else { + res[i] = prod / nums[i]; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + productExceptSelf(nums) { + let prod = 1; + let zeroCount = 0; + for (let num of nums) { + if (num !== 0) { + prod *= num; + } else { + zeroCount++; + } + } + + if (zeroCount > 1) { + return Array(nums.length).fill(0); + } + + const res = new Array(nums.length); + for (let i = 0; i < nums.length; i++) { + if (zeroCount > 0) { + res[i] = (nums[i] === 0) ? prod : 0; + } else { + res[i] = prod / nums[i]; + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int[] ProductExceptSelf(int[] nums) { + int prod = 1, zeroCount = 0; + foreach (int num in nums) { + if (num != 0) { + prod *= num; + } else { + zeroCount++; + } + } + + if (zeroCount > 1) { + return new int[nums.Length]; + } + + int[] res = new int[nums.Length]; + for (int i = 0; i < nums.Length; i++) { + if (zeroCount > 0) { + res[i] = (nums[i] == 0) ? prod : 0; + } else { + res[i] = prod / nums[i]; + } + } + return res; + } +} +``` + +```go +func productExceptSelf(nums []int) []int { + prod := 1 + zeroCount := 0 + + for _, num := range nums { + if num != 0 { + prod *= num + } else { + zeroCount++ + } + } + + res := make([]int, len(nums)) + if zeroCount > 1 { + return res + } + + for i, num := range nums { + if zeroCount > 0 { + if num == 0 { + res[i] = prod + } else { + res[i] = 0 + } + } else { + res[i] = prod / num + } + } + return res +} +``` + +```kotlin +class Solution { + fun productExceptSelf(nums: IntArray): IntArray { + var prod = 1 + var zeroCount = 0 + + for (num in nums) { + if (num != 0) { + prod *= num + } else { + zeroCount++ + } + } + + val res = IntArray(nums.size) + if (zeroCount > 1) return res + + for (i in nums.indices) { + res[i] = if (zeroCount > 0) { + if (nums[i] == 0) prod else 0 + } else { + prod / nums[i] + } + } + return res + } +} +``` + +```swift +class Solution { + func productExceptSelf(_ nums: [Int]) -> [Int] { + var prod = 1 + var zeroCount = 0 + + for num in nums { + if num != 0 { + prod *= num + } else { + zeroCount += 1 + } + } + + if zeroCount > 1 { + return [Int](repeating: 0, count: nums.count) + } + + var res = [Int](repeating: 0, count: nums.count) + for (i, num) in nums.enumerated() { + if zeroCount > 0 { + res[i] = num == 0 ? prod : 0 + } else { + res[i] = prod / num + } + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. + +--- + +## 3. Prefix & Suffix + +::tabs-start + +```python +class Solution: + def productExceptSelf(self, nums: List[int]) -> List[int]: + n = len(nums) + res = [0] * n + pref = [0] * n + suff = [0] * n + + pref[0] = suff[n - 1] = 1 + for i in range(1, n): + pref[i] = nums[i - 1] * pref[i - 1] + for i in range(n - 2, -1, -1): + suff[i] = nums[i + 1] * suff[i + 1] + for i in range(n): + res[i] = pref[i] * suff[i] + return res +``` + +```java +public class Solution { + public int[] productExceptSelf(int[] nums) { + int n = nums.length; + int[] res = new int[n]; + int[] pref = new int[n]; + int[] suff = new int[n]; + + pref[0] = 1; + suff[n - 1] = 1; + for (int i = 1; i < n; i++) { + pref[i] = nums[i - 1] * pref[i - 1]; + } + for (int i = n - 2; i >= 0; i--) { + suff[i] = nums[i + 1] * suff[i + 1]; + } + for (int i = 0; i < n; i++) { + res[i] = pref[i] * suff[i]; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector productExceptSelf(vector& nums) { + int n = nums.size(); + vector res(n); + vector pref(n); + vector suff(n); + + pref[0] = 1; + suff[n - 1] = 1; + for (int i = 1; i < n; i++) { + pref[i] = nums[i - 1] * pref[i - 1]; + } + for (int i = n - 2; i >= 0; i--) { + suff[i] = nums[i + 1] * suff[i + 1]; + } + for (int i = 0; i < n; i++) { + res[i] = pref[i] * suff[i]; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + productExceptSelf(nums) { + const n = nums.length; + const res = new Array(n); + const pref = new Array(n); + const suff = new Array(n); + + pref[0] = 1; + suff[n - 1] = 1; + for (let i = 1; i < n; i++) { + pref[i] = nums[i - 1] * pref[i - 1]; + } + for (let i = n - 2; i >= 0; i--) { + suff[i] = nums[i + 1] * suff[i + 1]; + } + for (let i = 0; i < n; i++) { + res[i] = pref[i] * suff[i]; + } + return res; + } +} +``` + +```csharp +public class Solution { + public int[] ProductExceptSelf(int[] nums) { + int n = nums.Length; + int[] res = new int[n]; + int[] pref = new int[n]; + int[] suff = new int[n]; + + pref[0] = 1; + suff[n - 1] = 1; + for (int i = 1; i < n; i++) { + pref[i] = nums[i - 1] * pref[i - 1]; + } + for (int i = n - 2; i >= 0; i--) { + suff[i] = nums[i + 1] * suff[i + 1]; + } + for (int i = 0; i < n; i++) { + res[i] = pref[i] * suff[i]; + } + return res; + } +} +``` + +```go +func productExceptSelf(nums []int) []int { + n := len(nums) + res := make([]int, n) + pref := make([]int, n) + suff := make([]int, n) + + pref[0], suff[n-1] = 1, 1 + for i := 1; i < n; i++ { + pref[i] = nums[i-1] * pref[i-1] + } + for i := n - 2; i >= 0; i-- { + suff[i] = nums[i+1] * suff[i+1] + } + for i := 0; i < n; i++ { + res[i] = pref[i] * suff[i] + } + return res +} +``` + +```kotlin +class Solution { + fun productExceptSelf(nums: IntArray): IntArray { + val n = nums.size + val res = IntArray(n) + val pref = IntArray(n) + val suff = IntArray(n) + + pref[0] = 1 + suff[n - 1] = 1 + for (i in 1 until n) { + pref[i] = nums[i - 1] * pref[i - 1] + } + for (i in n - 2 downTo 0) { + suff[i] = nums[i + 1] * suff[i + 1] + } + for (i in 0 until n) { + res[i] = pref[i] * suff[i] + } + return res + } +} +``` + +```swift +class Solution { + func productExceptSelf(_ nums: [Int]) -> [Int] { + let n = nums.count + var res = [Int](repeating: 0, count: n) + var pref = [Int](repeating: 0, count: n) + var suff = [Int](repeating: 0, count: n) + + pref[0] = 1 + suff[n - 1] = 1 + + for i in 1.. List[int]: + res = [1] * (len(nums)) + + prefix = 1 + for i in range(len(nums)): + res[i] = prefix + prefix *= nums[i] + postfix = 1 + for i in range(len(nums) - 1, -1, -1): + res[i] *= postfix + postfix *= nums[i] + return res +``` + +```java +public class Solution { + public int[] productExceptSelf(int[] nums) { + int n = nums.length; + int[] res = new int[n]; + + res[0] = 1; + for (int i = 1; i < n; i++) { + res[i] = res[i - 1] * nums[i - 1]; + } + + int postfix = 1; + for (int i = n - 1; i >= 0; i--) { + res[i] *= postfix; + postfix *= nums[i]; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector productExceptSelf(vector& nums) { + int n = nums.size(); + vector res(n, 1); + + for (int i = 1; i < n; i++) { + res[i] = res[i - 1] * nums[i - 1]; + } + + int postfix = 1; + for (int i = n - 1; i >= 0; i--) { + res[i] *= postfix; + postfix *= nums[i]; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + productExceptSelf(nums) { + const n = nums.length; + const res = new Array(n).fill(1); + + for (let i = 1; i < n; i++) { + res[i] = res[i - 1] * nums[i - 1]; + } + + let postfix = 1; + for (let i = n - 1; i >= 0; i--) { + res[i] *= postfix; + postfix *= nums[i]; + } + return res; + } +} +``` + +```csharp +public class Solution { + public int[] ProductExceptSelf(int[] nums) { + int n = nums.Length; + int[] res = new int[n]; + Array.Fill(res, 1); + + for (int i = 1; i < n; i++) { + res[i] = res[i - 1] * nums[i - 1]; + } + + int postfix = 1; + for (int i = n - 1; i >= 0; i--) { + res[i] *= postfix; + postfix *= nums[i]; + } + return res; + } +} +``` + +```go +func productExceptSelf(nums []int) []int { + res := make([]int, len(nums)) + for i := range res { + res[i] = 1 + } + + prefix := 1 + for i := 0; i < len(nums); i++ { + res[i] = prefix + prefix *= nums[i] + } + + postfix := 1 + for i := len(nums) - 1; i >= 0; i-- { + res[i] *= postfix + postfix *= nums[i] + } + + return res +} +``` + +```kotlin +class Solution { + fun productExceptSelf(nums: IntArray): IntArray { + val res = IntArray(nums.size) { 1 } + + var prefix = 1 + for (i in nums.indices) { + res[i] = prefix + prefix *= nums[i] + } + + var postfix = 1 + for (i in nums.size - 1 downTo 0) { + res[i] *= postfix + postfix *= nums[i] + } + + return res + } +} +``` + +```swift +class Solution { + func productExceptSelf(_ nums: [Int]) -> [Int] { + var res = [Int](repeating: 1, count: nums.count) + + var prefix = 1 + for i in 0.. int: + mod = 10**9 + 7 + + def dfs(i, n, p): + if i == len(group): + return 1 if p >= minProfit else 0 + + res = dfs(i + 1, n, p) + if n - group[i] >= 0: + res = (res + dfs(i + 1, n - group[i], p + profit[i])) % mod + + return res + + return dfs(0, n, 0) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) { + return dfs(0, n, 0, group, profit, minProfit); + } + + private int dfs(int i, int n, int p, int[] group, int[] profit, int minProfit) { + if (i == group.length) { + return p >= minProfit ? 1 : 0; + } + + int res = dfs(i + 1, n, p, group, profit, minProfit); + if (n - group[i] >= 0) { + res = (res + dfs(i + 1, n - group[i], p + profit[i], group, profit, minProfit)) % MOD; + } + + return res; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + +public: + int profitableSchemes(int n, int minProfit, vector& group, vector& profit) { + return dfs(0, n, 0, group, profit, minProfit); + } + +private: + int dfs(int i, int n, int p, const vector& group, const vector& profit, int minProfit) { + if (i == group.size()) { + return p >= minProfit ? 1 : 0; + } + + int res = dfs(i + 1, n, p, group, profit, minProfit); + if (n - group[i] >= 0) { + res = (res + dfs(i + 1, n - group[i], p + profit[i], group, profit, minProfit)) % MOD; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} minProfit + * @param {number[]} group + * @param {number[]} profit + * @return {number} + */ + profitableSchemes(n, minProfit, group, profit) { + const MOD = 1e9 + 7; + + const dfs = (i, n, p) => { + if (i === group.length) { + return p >= minProfit ? 1 : 0; + } + + let res = dfs(i + 1, n, p); + if (n - group[i] >= 0) { + res = (res + dfs(i + 1, n - group[i], p + profit[i])) % MOD; + } + + return res; + }; + + return dfs(0, n, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ N)$ +* Space complexity: $O(N)$ + +> Where $N$ is the size of the $group$ array. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def profitableSchemes(self, n: int, minProfit: int, group: List[int], profit: List[int]) -> int: + mod = 10**9 + 7 + dp = {} + + def dfs(i, n, p): + if i == len(group): + return 1 if p >= minProfit else 0 + if (i, n, p) in dp: + return dp[(i, n, p)] + + res = dfs(i + 1, n, p) + if n - group[i] >= 0: + nxtP = min(p + profit[i], minProfit) + res = (res + dfs(i + 1, n - group[i], nxtP)) % mod + + dp[(i, n, p)] = res + return res + + return dfs(0, n, 0) +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + private int[][][] dp; + + public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) { + dp = new int[group.length][n + 1][minProfit + 1]; + for (int[][] layer : dp) { + for (int[] row : layer) { + Arrays.fill(row, -1); + } + } + return dfs(0, n, 0, group, profit, minProfit); + } + + private int dfs(int i, int n, int p, int[] group, int[] profit, int minProfit) { + if (i == group.length) { + return p >= minProfit ? 1 : 0; + } + if (dp[i][n][p] != -1) { + return dp[i][n][p]; + } + + int res = dfs(i + 1, n, p, group, profit, minProfit); + if (n - group[i] >= 0) { + int nxtP = Math.min(p + profit[i], minProfit); + res = (res + dfs(i + 1, n - group[i], nxtP, group, profit, minProfit)) % MOD; + } + + dp[i][n][p] = res; + return res; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + vector>> dp; + +public: + int profitableSchemes(int n, int minProfit, vector& group, vector& profit) { + dp = vector>>(group.size(), vector>(n + 1, vector(minProfit + 1, -1))); + return dfs(0, n, 0, group, profit, minProfit); + } + +private: + int dfs(int i, int n, int p, vector& group, vector& profit, int minProfit) { + if (i == group.size()) { + return p >= minProfit ? 1 : 0; + } + if (dp[i][n][p] != -1) { + return dp[i][n][p]; + } + + int res = dfs(i + 1, n, p, group, profit, minProfit); + if (n >= group[i]) { + int nxtP = min(p + profit[i], minProfit); + res = (res + dfs(i + 1, n - group[i], nxtP, group, profit, minProfit)) % MOD; + } + + dp[i][n][p] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} minProfit + * @param {number[]} group + * @param {number[]} profit + * @return {number} + */ + profitableSchemes(n, minProfit, group, profit) { + const MOD = 1e9 + 7; + const dp = Array.from({ length: group.length }, () => + Array.from({ length: n + 1 }, () => Array(minProfit + 1).fill(-1)) + ); + + const dfs = (i, n, p) => { + if (i === group.length) { + return p >= minProfit ? 1 : 0; + } + if (dp[i][n][p] !== -1) { + return dp[i][n][p]; + } + + let res = dfs(i + 1, n, p); + if (n >= group[i]) { + const nxtP = Math.min(p + profit[i], minProfit); + res = (res + dfs(i + 1, n - group[i], nxtP)) % MOD; + } + + dp[i][n][p] = res; + return res; + }; + + return dfs(0, n, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N * m * n)$ +* Space complexity: $O(N * m * n)$ + +> Where $N$ is the size of the $group$ array, $m$ is the given minimum profit, and $n$ is the number of group members. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def profitableSchemes(self, n: int, minProfit: int, group: List[int], profit: List[int]) -> int: + mod = 10**9 + 7 + N = len(group) + + dp = [[[0] * (minProfit + 1) for j in range(n + 2)] for i in range(N + 1)] + for j in range(n + 1): + dp[N][j][minProfit] = 1 + + for i in range(N - 1, -1, -1): + for j in range(n + 1): + for p in range(minProfit + 1): + res = dp[i + 1][j][p] + if j >= group[i]: + nxtP = min(profit[i] + p, minProfit) + res = (res + dp[i + 1][j - group[i]][nxtP]) % mod + dp[i][j][p] = res + + return dp[0][n][0] +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) { + int N = group.length; + int[][][] dp = new int[N + 1][n + 2][minProfit + 1]; + + for (int j = 0; j <= n; j++) { + dp[N][j][minProfit] = 1; + } + + for (int i = N - 1; i >= 0; i--) { + for (int j = 0; j <= n; j++) { + for (int p = 0; p <= minProfit; p++) { + int res = dp[i + 1][j][p]; + if (j >= group[i]) { + int nxtP = Math.min(profit[i] + p, minProfit); + res = (res + dp[i + 1][j - group[i]][nxtP]) % MOD; + } + dp[i][j][p] = res; + } + } + } + + return dp[0][n][0]; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + +public: + int profitableSchemes(int n, int minProfit, vector& group, vector& profit) { + int N = group.size(); + vector>> dp(N + 1, vector>(n + 2, vector(minProfit + 1, 0))); + + for (int j = 0; j <= n; j++) { + dp[N][j][minProfit] = 1; + } + + for (int i = N - 1; i >= 0; i--) { + for (int j = 0; j <= n; j++) { + for (int p = 0; p <= minProfit; p++) { + int res = dp[i + 1][j][p]; + if (j >= group[i]) { + int nxtP = min(profit[i] + p, minProfit); + res = (res + dp[i + 1][j - group[i]][nxtP]) % MOD; + } + dp[i][j][p] = res; + } + } + } + + return dp[0][n][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} minProfit + * @param {number[]} group + * @param {number[]} profit + * @return {number} + */ + profitableSchemes(n, minProfit, group, profit) { + const MOD = 1e9 + 7; + const N = group.length; + + const dp = Array.from({ length: N + 1 }, () => + Array.from({ length: n + 2 }, () => Array(minProfit + 1).fill(0)) + ); + + for (let j = 0; j <= n; j++) { + dp[N][j][minProfit] = 1; + } + + for (let i = N - 1; i >= 0; i--) { + for (let j = 0; j <= n; j++) { + for (let p = 0; p <= minProfit; p++) { + let res = dp[i + 1][j][p]; + if (j >= group[i]) { + const nxtP = Math.min(profit[i] + p, minProfit); + res = (res + dp[i + 1][j - group[i]][nxtP]) % MOD; + } + dp[i][j][p] = res; + } + } + } + + return dp[0][n][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N * m * n)$ +* Space complexity: $O(N * m * n)$ + +> Where $N$ is the size of the $group$ array, $m$ is the given minimum profit, and $n$ is the number of group members. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def profitableSchemes(self, n: int, minProfit: int, group: List[int], profit: List[int]) -> int: + mod = 10**9 + 7 + N = len(group) + + dp = [[0] * (minProfit + 1) for j in range(n + 2)] + for j in range(n + 1): + dp[j][minProfit] = 1 + + for i in range(N - 1, -1, -1): + for j in range(n, -1, -1): + for p in range(minProfit + 1): + res = dp[j][p] + if j >= group[i]: + nxtP = min(profit[i] + p, minProfit) + res = (res + dp[j - group[i]][nxtP]) % mod + dp[j][p] = res + + return dp[n][0] +``` + +```java +public class Solution { + private static final int MOD = 1_000_000_007; + + public int profitableSchemes(int n, int minProfit, int[] group, int[] profit) { + int N = group.length; + int[][] dp = new int[n + 2][minProfit + 1]; + + for (int j = 0; j <= n; j++) { + dp[j][minProfit] = 1; + } + + for (int i = N - 1; i >= 0; i--) { + for (int j = n; j >= 0; j--) { + for (int p = 0; p <= minProfit; p++) { + int res = dp[j][p]; + if (j >= group[i]) { + int nxtP = Math.min(profit[i] + p, minProfit); + res = (res + dp[j - group[i]][nxtP]) % MOD; + } + dp[j][p] = res; + } + } + } + + return dp[n][0]; + } +} +``` + +```cpp +class Solution { +private: + static const int MOD = 1e9 + 7; + +public: + int profitableSchemes(int n, int minProfit, vector& group, vector& profit) { + int N = group.size(); + vector> dp(n + 2, vector(minProfit + 1, 0)); + + for (int j = 0; j <= n; j++) { + dp[j][minProfit] = 1; + } + + for (int i = N - 1; i >= 0; i--) { + for (int j = n; j >= 0; j--) { + for (int p = 0; p <= minProfit; p++) { + int res = dp[j][p]; + if (j >= group[i]) { + int nxtP = min(profit[i] + p, minProfit); + res = (res + dp[j - group[i]][nxtP]) % MOD; + } + dp[j][p] = res; + } + } + } + + return dp[n][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} minProfit + * @param {number[]} group + * @param {number[]} profit + * @return {number} + */ + profitableSchemes(n, minProfit, group, profit) { + const MOD = 1e9 + 7; + const N = group.length; + + const dp = Array.from({ length: n + 2 }, () => + Array(minProfit + 1).fill(0) + ); + + for (let j = 0; j <= n; j++) { + dp[j][minProfit] = 1; + } + + for (let i = N - 1; i >= 0; i--) { + for (let j = n; j >= 0; j--) { + for (let p = 0; p <= minProfit; p++) { + let res = dp[j][p]; + if (j >= group[i]) { + const nxtP = Math.min(profit[i] + p, minProfit); + res = (res + dp[j - group[i]][nxtP]) % MOD; + } + dp[j][p] = res; + } + } + } + + return dp[n][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N * m * n)$ +* Space complexity: $O(m * n)$ + +> Where $N$ is the size of the $group$ array, $m$ is the given minimum profit, and $n$ is the number of group members. \ No newline at end of file diff --git a/articles/pseudo-palindromic-paths-in-a-binary-tree.md b/articles/pseudo-palindromic-paths-in-a-binary-tree.md new file mode 100644 index 000000000..96bf58f3b --- /dev/null +++ b/articles/pseudo-palindromic-paths-in-a-binary-tree.md @@ -0,0 +1,806 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def pseudoPalindromicPaths(self, root: Optional[TreeNode]) -> int: + count = defaultdict(int) + odd = 0 + + def dfs(cur): + nonlocal odd + if not cur: + return 0 + + count[cur.val] += 1 + odd_change = 1 if count[cur.val] % 2 == 1 else -1 + odd += odd_change + + if not cur.left and not cur.right: + res = 1 if odd <= 1 else 0 + else: + res = dfs(cur.left) + dfs(cur.right) + + odd -= odd_change + count[cur.val] -= 1 + return res + + return dfs(root) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int pseudoPalindromicPaths(TreeNode root) { + Map count = new HashMap<>(); + int[] odd = new int[1]; + + return dfs(root, count, odd); + } + + private int dfs(TreeNode cur, Map count, int[] odd) { + if (cur == null) return 0; + + count.put(cur.val, count.getOrDefault(cur.val, 0) + 1); + int odd_change = (count.get(cur.val) % 2 == 1) ? 1 : -1; + odd[0] += odd_change; + + int res; + if (cur.left == null && cur.right == null) { + res = (odd[0] <= 1) ? 1 : 0; + } else { + res = dfs(cur.left, count, odd) + dfs(cur.right, count, odd); + } + + odd[0] -= odd_change; + count.put(cur.val, count.get(cur.val) - 1); + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int pseudoPalindromicPaths(TreeNode* root) { + unordered_map count; + int odd = 0; + return dfs(root, count, odd); + } + +private: + int dfs(TreeNode* cur, unordered_map& count, int& odd) { + if (!cur) return 0; + + count[cur->val]++; + int odd_change = (count[cur->val] % 2 == 1) ? 1 : -1; + odd += odd_change; + + int res; + if (!cur->left && !cur->right) { + res = (odd <= 1) ? 1 : 0; + } else { + res = dfs(cur->left, count, odd) + dfs(cur->right, count, odd); + } + + odd -= odd_change; + count[cur->val]--; + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + pseudoPalindromicPaths(root) { + const count = new Map(); + let odd = 0; + + const dfs = (cur) => { + if (!cur) return 0; + + count.set(cur.val, (count.get(cur.val) || 0) + 1); + let odd_change = (count.get(cur.val) % 2 === 1) ? 1 : -1; + odd += odd_change; + + let res; + if (!cur.left && !cur.right) { + res = (odd <= 1) ? 1 : 0; + } else { + res = dfs(cur.left) + dfs(cur.right); + } + + odd -= odd_change; + count.set(cur.val, count.get(cur.val) - 1); + return res; + }; + + return dfs(root); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(h)$ for recursion stack. + +> Where $n$ is the number of nodes and $h$ is the height of the given tree. + +--- + +## 2. Depth First Search (Using Array) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def pseudoPalindromicPaths(self, root: Optional[TreeNode]) -> int: + count = [0] * 10 + odd = 0 + + def dfs(cur): + nonlocal odd + if not cur: + return 0 + + count[cur.val] ^= 1 + odd += 1 if count[cur.val] else -1 + + if not cur.left and not cur.right and odd <= 1: + res = 1 + else: + res = dfs(cur.left) + dfs(cur.right) + + odd -= 1 if count[cur.val] else -1 + count[cur.val] ^= 1 + + return res + + return dfs(root) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int pseudoPalindromicPaths(TreeNode root) { + int[] count = new int[10]; + return dfs(root, count, 0); + } + + private int dfs(TreeNode cur, int[] count, int odd) { + if (cur == null) return 0; + + count[cur.val] ^= 1; + odd += count[cur.val] == 1 ? 1 : -1; + + int res = (cur.left == null && cur.right == null && odd <= 1) ? 1 + : dfs(cur.left, count, odd) + dfs(cur.right, count, odd); + + odd += count[cur.val] == 1 ? 1 : -1; + count[cur.val] ^= 1; + + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int pseudoPalindromicPaths(TreeNode* root) { + int count[10] = {}; + return dfs(root, count, 0); + } + +private: + int dfs(TreeNode* cur, int count[], int odd) { + if (!cur) return 0; + + count[cur->val] ^= 1; + odd += (count[cur->val] == 1) ? 1 : -1; + + int res = (!cur->left && !cur->right && odd <= 1) ? 1 + : dfs(cur->left, count, odd) + dfs(cur->right, count, odd); + + odd += (count[cur->val] == 1) ? 1 : -1; + count[cur->val] ^= 1; + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + pseudoPalindromicPaths(root) { + const count = new Array(10).fill(0); + + const dfs = (cur, odd) => { + if (!cur) return 0; + + count[cur.val] ^= 1; + odd += count[cur.val] === 1 ? 1 : -1; + + let res = (!cur.left && !cur.right && odd <= 1) + ? 1 : dfs(cur.left, odd) + dfs(cur.right, odd); + + odd += count[cur.val] === 1 ? 1 : -1; + count[cur.val] ^= 1; + + return res; + }; + + return dfs(root, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(h)$ for recursion stack. + +> Where $n$ is the number of nodes and $h$ is the height of the given tree. + +--- + +## 3. Depth First Search (Bit Mask) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def pseudoPalindromicPaths(self, root: Optional[TreeNode]) -> int: + def dfs(node, path): + if not node: + return 0 + + path ^= 1 << node.val + if not node.left and not node.right: + return 1 if (path & (path - 1)) == 0 else 0 + + return dfs(node.left, path) + dfs(node.right, path) + + return dfs(root, 0) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int pseudoPalindromicPaths(TreeNode root) { + return dfs(root, 0); + } + + private int dfs(TreeNode node, int path) { + if (node == null) return 0; + + path ^= (1 << node.val); + if (node.left == null && node.right == null) { + return (path & (path - 1)) == 0 ? 1 : 0; + } + + return dfs(node.left, path) + dfs(node.right, path); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int pseudoPalindromicPaths(TreeNode* root) { + return dfs(root, 0); + } + +private: + int dfs(TreeNode* node, int path) { + if (!node) return 0; + + path ^= (1 << node->val); + if (!node->left && !node->right) { + return (path & (path - 1)) == 0 ? 1 : 0; + } + + return dfs(node->left, path) + dfs(node->right, path); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + pseudoPalindromicPaths(root) { + const dfs = (node, path) => { + if (!node) return 0; + + path ^= (1 << node.val); + if (!node.left && !node.right) { + return (path & (path - 1)) === 0 ? 1 : 0; + } + + return dfs(node.left, path) + dfs(node.right, path); + }; + + return dfs(root, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(h)$ for recursion stack. + +> Where $n$ is the number of nodes and $h$ is the height of the given tree. + +--- + +## 4. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def pseudoPalindromicPaths(self, root: Optional[TreeNode]) -> int: + res = 0 + q = deque([(root, 0)]) + while q: + node, path = q.popleft() + path ^= 1 << node.val + + if not node.left and not node.right: + if path & (path - 1) == 0: + res += 1 + continue + + if node.left: + q.append((node.left, path)) + if node.right: + q.append((node.right, path)) + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int pseudoPalindromicPaths(TreeNode root) { + int count = 0; + Queue q = new LinkedList<>(); + q.offer(new Pair(root, 0)); + while (!q.isEmpty()) { + Pair p = q.poll(); + TreeNode node = p.node; + int path = p.path ^ (1 << node.val); + + if (node.left == null && node.right == null) { + if ((path & (path - 1)) == 0) { + count++; + } + } else { + if (node.left != null) q.offer(new Pair(node.left, path)); + if (node.right != null) q.offer(new Pair(node.right, path)); + } + } + + return count; + } + + private static class Pair { + TreeNode node; + int path; + Pair(TreeNode node, int path) { + this.node = node; + this.path = path; + } + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int pseudoPalindromicPaths(TreeNode* root) { + int res = 0; + queue> q; + q.push({root, 0}); + while (!q.empty()) { + auto [node, path] = q.front();q.pop(); + path ^= (1 << node->val); + + if (!node->left && !node->right) { + if ((path & (path - 1)) == 0) res++; + continue; + } + + if (node->left) q.push({node->left, path}); + if (node->right) q.push({node->right, path}); + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + pseudoPalindromicPaths(root) { + let res = 0; + const q = new Queue([[root, 0]]); + + while (!q.isEmpty()) { + const [node, path] = q.pop(); + const newPath = path ^ (1 << node.val); + + if (!node.left && !node.right) { + if ((newPath & (newPath - 1)) === 0) res++; + continue; + } + + if (node.left) q.push([node.left, newPath]); + if (node.right) q.push([node.right, newPath]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def pseudoPalindromicPaths(self, root: Optional[TreeNode]) -> int: + count = 0 + stack = [(root, 0)] + while stack: + node, path = stack.pop() + path ^= (1 << node.val) + + if not node.left and not node.right: + if path & (path - 1) == 0: + count += 1 + else: + if node.right: + stack.append((node.right, path)) + if node.left: + stack.append((node.left, path)) + + return count +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int pseudoPalindromicPaths(TreeNode root) { + int count = 0; + Stack stack = new Stack<>(); + stack.push(new Pair(root, 0)); + + while (!stack.isEmpty()) { + Pair p = stack.pop(); + TreeNode node = p.node; + int path = p.path ^ (1 << node.val); + + if (node.left == null && node.right == null) { + if ((path & (path - 1)) == 0) { + count++; + } + } else { + if (node.right != null) stack.push(new Pair(node.right, path)); + if (node.left != null) stack.push(new Pair(node.left, path)); + } + } + return count; + } + + private static class Pair { + TreeNode node; + int path; + Pair(TreeNode node, int path) { + this.node = node; + this.path = path; + } + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int pseudoPalindromicPaths(TreeNode* root) { + stack> s; + s.push({root, 0}); + int count = 0; + + while (!s.empty()) { + auto [node, path] = s.top(); + s.pop(); + path ^= (1 << node->val); + + if (!node->left && !node->right) { + if ((path & (path - 1)) == 0) count++; + } else { + if (node->right) s.push({node->right, path}); + if (node->left) s.push({node->left, path}); + } + } + + return count; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + pseudoPalindromicPaths(root) { + let count = 0; + const stack = [[root, 0]]; + + while (stack.length) { + const [node, path] = stack.pop(); + const newPath = path ^ (1 << node.val); + + if (!node.left && !node.right) { + if ((newPath & (newPath - 1)) === 0) count++; + } else { + if (node.right) stack.push([node.right, newPath]); + if (node.left) stack.push([node.left, newPath]); + } + } + + return count; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(h)$ + +> Where $n$ is the number of nodes and $h$ is the height of the given tree. \ No newline at end of file diff --git a/articles/push-dominoes.md b/articles/push-dominoes.md new file mode 100644 index 000000000..efe99877f --- /dev/null +++ b/articles/push-dominoes.md @@ -0,0 +1,749 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def pushDominoes(self, dominoes: str) -> str: + n = len(dominoes) + res = list(dominoes) + + for i in range(n): + if dominoes[i] != '.': + continue + + l, r = i - 1, i + 1 + + while l >= 0 and dominoes[l] == '.': + l -= 1 + + while r < n and dominoes[r] == '.': + r += 1 + + left_force = dominoes[l] if l >= 0 else None + right_force = dominoes[r] if r < n else None + + if left_force == 'R' and right_force == 'L': + if (i - l) < (r - i): + res[i] = 'R' + elif (r - i) < (i - l): + res[i] = 'L' + elif left_force == 'R': + res[i] = 'R' + elif right_force == 'L': + res[i] = 'L' + + return "".join(res) +``` + +```java +public class Solution { + public String pushDominoes(String dominoes) { + int n = dominoes.length(); + char[] res = dominoes.toCharArray(); + + for (int i = 0; i < n; i++) { + if (dominoes.charAt(i) != '.') continue; + + int l = i - 1, r = i + 1; + + while (l >= 0 && dominoes.charAt(l) == '.') l--; + while (r < n && dominoes.charAt(r) == '.') r++; + + char leftForce = (l >= 0) ? dominoes.charAt(l) : ' '; + char rightForce = (r < n) ? dominoes.charAt(r) : ' '; + + if (leftForce == 'R' && rightForce == 'L') { + if ((i - l) < (r - i)) res[i] = 'R'; + else if ((r - i) < (i - l)) res[i] = 'L'; + } else if (leftForce == 'R') { + res[i] = 'R'; + } else if (rightForce == 'L') { + res[i] = 'L'; + } + } + + return new String(res); + } +} +``` + +```cpp +class Solution { +public: + string pushDominoes(string dominoes) { + int n = dominoes.size(); + vector res(dominoes.begin(), dominoes.end()); + + for (int i = 0; i < n; i++) { + if (dominoes[i] != '.') continue; + + int l = i - 1, r = i + 1; + + while (l >= 0 && dominoes[l] == '.') l--; + while (r < n && dominoes[r] == '.') r++; + + char leftForce = (l >= 0) ? dominoes[l] : ' '; + char rightForce = (r < n) ? dominoes[r] : ' '; + + if (leftForce == 'R' && rightForce == 'L') { + if ((i - l) < (r - i)) res[i] = 'R'; + else if ((r - i) < (i - l)) res[i] = 'L'; + } else if (leftForce == 'R') { + res[i] = 'R'; + } else if (rightForce == 'L') { + res[i] = 'L'; + } + } + + return string(res.begin(), res.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} dominoes + * @return {string} + */ + pushDominoes(dominoes) { + const n = dominoes.length; + const res = dominoes.split(""); + + for (let i = 0; i < n; i++) { + if (dominoes[i] !== ".") continue; + + let l = i - 1, r = i + 1; + + while (l >= 0 && dominoes[l] === ".") l--; + while (r < n && dominoes[r] === ".") r++; + + const leftForce = l >= 0 ? dominoes[l] : null; + const rightForce = r < n ? dominoes[r] : null; + + if (leftForce === "R" && rightForce === "L") { + if ((i - l) < (r - i)) res[i] = "R"; + else if ((r - i) < (i - l)) res[i] = "L"; + } else if (leftForce === "R") { + res[i] = "R"; + } else if (rightForce === "L") { + res[i] = "L"; + } + } + + return res.join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ for only the output string. + +--- + +## 2. Force From Left & Right + +::tabs-start + +```python +class Solution: + def pushDominoes(self, dominoes: str) -> str: + n = len(dominoes) + left = [float('inf')] * n + right = [float('inf')] * n + res = list(dominoes) + + force = float('inf') + for i in range(n): + if dominoes[i] == 'R': + force = 0 + elif dominoes[i] == 'L': + force = float('inf') + else: + force += 1 + right[i] = force + + force = float('inf') + for i in range(n - 1, -1, -1): + if dominoes[i] == 'L': + force = 0 + elif dominoes[i] == 'R': + force = float('inf') + else: + force += 1 + left[i] = force + + for i in range(n): + if left[i] < right[i]: + res[i] = 'L' + elif right[i] < left[i]: + res[i] = 'R' + + return "".join(res) +``` + +```java +public class Solution { + public String pushDominoes(String dominoes) { + int n = dominoes.length(); + int[] left = new int[n]; + int[] right = new int[n]; + char[] res = dominoes.toCharArray(); + + int force = Integer.MAX_VALUE; + for (int i = 0; i < n; i++) { + if (dominoes.charAt(i) == 'R') { + force = 0; + } else if (dominoes.charAt(i) == 'L') { + force = Integer.MAX_VALUE; + } else { + force = force == Integer.MAX_VALUE ? Integer.MAX_VALUE : force + 1; + } + right[i] = force; + } + + force = Integer.MAX_VALUE; + for (int i = n - 1; i >= 0; i--) { + if (dominoes.charAt(i) == 'L') { + force = 0; + } else if (dominoes.charAt(i) == 'R') { + force = Integer.MAX_VALUE; + } else { + force = force == Integer.MAX_VALUE ? Integer.MAX_VALUE : force + 1; + } + left[i] = force; + } + + for (int i = 0; i < n; i++) { + if (left[i] < right[i]) { + res[i] = 'L'; + } else if (right[i] < left[i]) { + res[i] = 'R'; + } + } + + return new String(res); + } +} +``` + +```cpp +class Solution { +public: + string pushDominoes(string dominoes) { + int n = dominoes.size(); + vector left(n, INT_MAX); + vector right(n, INT_MAX); + vector res(dominoes.begin(), dominoes.end()); + + int force = INT_MAX; + for (int i = 0; i < n; i++) { + if (dominoes[i] == 'R') { + force = 0; + } else if (dominoes[i] == 'L') { + force = INT_MAX; + } else { + force = (force == INT_MAX) ? INT_MAX : force + 1; + } + right[i] = force; + } + + force = INT_MAX; + for (int i = n - 1; i >= 0; i--) { + if (dominoes[i] == 'L') { + force = 0; + } else if (dominoes[i] == 'R') { + force = INT_MAX; + } else { + force = (force == INT_MAX) ? INT_MAX : force + 1; + } + left[i] = force; + } + + for (int i = 0; i < n; i++) { + if (left[i] < right[i]) { + res[i] = 'L'; + } else if (right[i] < left[i]) { + res[i] = 'R'; + } + } + + return string(res.begin(), res.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} dominoes + * @return {string} + */ + pushDominoes(dominoes) { + const n = dominoes.length; + const left = new Array(n).fill(Infinity); + const right = new Array(n).fill(Infinity); + const res = dominoes.split(""); + + let force = Infinity; + for (let i = 0; i < n; i++) { + if (dominoes[i] === "R") { + force = 0; + } else if (dominoes[i] === "L") { + force = Infinity; + } else { + force = force === Infinity ? Infinity : force + 1; + } + right[i] = force; + } + + force = Infinity; + for (let i = n - 1; i >= 0; i--) { + if (dominoes[i] === "L") { + force = 0; + } else if (dominoes[i] === "R") { + force = Infinity; + } else { + force = force === Infinity ? Infinity : force + 1; + } + left[i] = force; + } + + for (let i = 0; i < n; i++) { + if (left[i] < right[i]) { + res[i] = "L"; + } else if (right[i] < left[i]) { + res[i] = "R"; + } + } + + return res.join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Simulation (Queue) + +::tabs-start + +```python +class Solution: + def pushDominoes(self, dominoes: str) -> str: + dom = list(dominoes) + q = deque() + + for i, d in enumerate(dom): + if d != ".": + q.append((i, d)) + + while q: + i, d = q.popleft() + + if d == "L" and i > 0 and dom[i - 1] == ".": + q.append((i - 1, "L")) + dom[i - 1] = "L" + elif d == "R": + if i + 1 < len(dom) and dom[i + 1] == ".": + if i + 2 < len(dom) and dom[i + 2] == "L": + q.popleft() + else: + q.append((i + 1, "R")) + dom[i + 1] = "R" + + return "".join(dom) +``` + +```java +public class Solution { + public String pushDominoes(String dominoes) { + char[] dom = dominoes.toCharArray(); + Queue q = new LinkedList<>(); + + for (int i = 0; i < dom.length; i++) { + if (dom[i] != '.') { + q.add(new int[] {i, dom[i]}); + } + } + + while (!q.isEmpty()) { + int[] curr = q.poll(); + int i = curr[0]; + char d = (char) curr[1]; + + if (d == 'L' && i > 0 && dom[i - 1] == '.') { + q.add(new int[] {i - 1, 'L'}); + dom[i - 1] = 'L'; + } else if (d == 'R') { + if (i + 1 < dom.length && dom[i + 1] == '.') { + if (i + 2 < dom.length && dom[i + 2] == 'L') { + q.poll(); + } else { + q.add(new int[] {i + 1, 'R'}); + dom[i + 1] = 'R'; + } + } + } + } + + return new String(dom); + } +} +``` + +```cpp +class Solution { +public: + string pushDominoes(string dominoes) { + vector dom(dominoes.begin(), dominoes.end()); + queue> q; + + for (int i = 0; i < dom.size(); i++) { + if (dom[i] != '.') { + q.push({i, dom[i]}); + } + } + + while (!q.empty()) { + auto [i, d] = q.front(); + q.pop(); + + if (d == 'L' && i > 0 && dom[i - 1] == '.') { + q.push({i - 1, 'L'}); + dom[i - 1] = 'L'; + } else if (d == 'R') { + if (i + 1 < dom.size() && dom[i + 1] == '.') { + if (i + 2 < dom.size() && dom[i + 2] == 'L') { + q.pop(); + } else { + q.push({i + 1, 'R'}); + dom[i + 1] = 'R'; + } + } + } + } + + return string(dom.begin(), dom.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} dominoes + * @return {string} + */ + pushDominoes(dominoes) { + const dom = dominoes.split(""); + const q = new Queue(); + + for (let i = 0; i < dom.length; i++) { + if (dom[i] !== ".") { + q.push([i, dom[i]]); + } + } + + while (!q.isEmpty()) { + const [i, d] = q.pop(); + + if (d === "L" && i > 0 && dom[i - 1] === ".") { + q.push([i - 1, "L"]); + dom[i - 1] = "L"; + } else if (d === "R") { + if (i + 1 < dom.length && dom[i + 1] === ".") { + if (i + 2 < dom.length && dom[i + 2] === "L") { + q.pop(); + } else { + q.push([i + 1, "R"]); + dom[i + 1] = "R"; + } + } + } + } + + return dom.join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Iteration (Greedy) + +::tabs-start + +```python +class Solution: + def pushDominoes(self, dominoes: str) -> str: + res = [] + dots = 0 + R = False + + for d in dominoes: + if d == '.': + dots += 1 + elif d == 'R': + if R: + # Previous was 'R', and current is also 'R'. + # Append 'R' for all the dots between them. + res.append('R' * (dots + 1)) + elif dots > 0: + # Previous was not 'R'. The previous dots remain unchanged. + res.append('.' * dots) + dots = 0 + R = True # Current is 'R' + else: + if R: + # Append the previous 'R'. + res.append('R') + if dots > 0: + # Half the dots are affected by the previous 'R'. + res.append('R' * (dots // 2)) + + # Add a '.' if there's an odd number of dots. + if (dots % 2) != 0: + res.append('.') + + # Append half the dots as 'L'. + res.append('L' * (dots // 2)) + + # Append the current 'L'. + res.append('L') + R, dots = False, 0 + else: + # There is no 'R' on the left. + # Append 'L' for all the dots and the current 'L'. + res.append('L' * (dots + 1)) + dots = 0 + + if R: + # Trailing dots are affected by the last 'R'. + res.append('R' * (dots + 1)) + else: + # Trailing dots remain unchanged as there is no previous 'R'. + res.append('.' * dots) + + return ''.join(res) +``` + +```java +public class Solution { + public String pushDominoes(String dominoes) { + StringBuilder res = new StringBuilder(); + int dots = 0; + boolean R = false; + + for (char d : dominoes.toCharArray()) { + if (d == '.') { + dots++; + } else if (d == 'R') { + if (R) { + // Previous was 'R', and current is also 'R'. + // Append 'R' for all the dots between them. + res.append("R".repeat(dots + 1)); + } else if (dots > 0) { + // Previous was not 'R'. The previous dots remain unchanged. + res.append(".".repeat(dots)); + } + dots = 0; + R = true; // Current is 'R'. + } else { + if (R) { + // Append the previous 'R'. + res.append("R"); + if (dots > 0) { + // Half the dots are affected by the previous 'R'. + res.append("R".repeat(dots / 2)); + + // Add a '.' if there's an odd number of dots. + if (dots % 2 != 0) { + res.append("."); + } + + // Append half the dots as 'L'. + res.append("L".repeat(dots / 2)); + } + // Append the current 'L'. + res.append("L"); + R = false; + dots = 0; + } else { + // There is no 'R' on the left. + // Append 'L' for all the dots and the current 'L'. + res.append("L".repeat(dots + 1)); + dots = 0; + } + } + } + + if (R) { + // Trailing dots are affected by the last 'R'. + res.append("R".repeat(dots + 1)); + } else { + // Trailing dots remain unchanged as there is no previous 'R'. + res.append(".".repeat(dots)); + } + + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string pushDominoes(string dominoes) { + string res = ""; + int dots = 0; + bool R = false; + + for (char d : dominoes) { + if (d == '.') { + dots++; + } else if (d == 'R') { + if (R) { + // Previous was 'R', and current is also 'R'. + // Append 'R' for all the dots between them. + res += string(dots + 1, 'R'); + } else if (dots > 0) { + // Previous was not 'R'. The previous dots remain unchanged. + res += string(dots, '.'); + } + dots = 0; + R = true; // Current is 'R'. + } else { + if (R) { + // Append the previous 'R'. + res += 'R'; + if (dots > 0) { + // Half the dots are affected by the previous 'R'. + res += string(dots / 2, 'R'); + + // Add a '.' if there's an odd number of dots. + if (dots % 2 != 0) { + res += '.'; + } + + // Append half the dots as 'L'. + res += string(dots / 2, 'L'); + } + // Append the current 'L'. + res += 'L'; + R = false; + dots = 0; + } else { + // There is no 'R' on the left. + // Append 'L' for all the dots and the current 'L'. + res += string(dots + 1, 'L'); + dots = 0; + } + } + } + + if (R) { + // Trailing dots are affected by the last 'R'. + res += string(dots + 1, 'R'); + } else { + // Trailing dots remain unchanged as there is no previous 'R'. + res += string(dots, '.'); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} dominoes + * @return {string} + */ + pushDominoes(dominoes) { + let res = []; + let dots = 0; + let R = false; + + for (let d of dominoes) { + if (d === '.') { + dots++; + } else if (d === 'R') { + if (R) { + // Previous was 'R', and current is also 'R'. + // Append 'R' for all the dots between them. + res.push('R'.repeat(dots + 1)); + } else if (dots > 0) { + // Previous was not 'R'. The previous dots remain unchanged. + res.push('.'.repeat(dots)); + } + dots = 0; + R = true; // Current is 'R'. + } else { + if (R) { + // Append the previous 'R'. + res.push('R'); + if (dots > 0) { + // Half the dots are affected by the previous 'R'. + res.push('R'.repeat(Math.floor(dots / 2))); + + // Add a '.' if there's an odd number of dots. + if (dots % 2 !== 0) { + res.push('.'); + } + + // Append half the dots as 'L'. + res.push('L'.repeat(Math.floor(dots / 2))); + } + // Append the current 'L'. + res.push('L'); + R = false; + dots = 0; + } else { + // There is no 'R' on the left. + // Append 'L' for all the dots and the current 'L'. + res.push('L'.repeat(dots + 1)); + dots = 0; + } + } + } + + if (R) { + // Trailing dots are affected by the last 'R'. + res.push('R'.repeat(dots + 1)); + } else { + // Trailing dots remain unchanged as there is no previous 'R'. + res.push('.'.repeat(dots)); + } + + return res.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for only the output string. \ No newline at end of file diff --git a/articles/put-marbles-in-bags.md b/articles/put-marbles-in-bags.md new file mode 100644 index 000000000..a0abf8a29 --- /dev/null +++ b/articles/put-marbles-in-bags.md @@ -0,0 +1,501 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def putMarbles(self, weights: List[int], k: int) -> int: + n = len(weights) + cache = {} + + def dfs(i, k): + if (i, k) in cache: + return cache[(i, k)] + if k == 0: + return [0, 0] + if i == n - 1 or n - i - 1 < k: + return [-float("inf"), float("inf")] + + res = [0, float("inf")] # [maxScore, minScore] + + # make partition + cur = dfs(i + 1, k - 1) + res[0] = max(res[0], weights[i] + weights[i + 1] + cur[0]) + res[1] = min(res[1], weights[i] + weights[i + 1] + cur[1]) + + # skip + cur = dfs(i + 1, k) + res[0] = max(res[0], cur[0]) + res[1] = min(res[1], cur[1]) + + cache[(i, k)] = res + return res + + ans = dfs(0, k - 1) + return ans[0] - ans[1] +``` + +```java +public class Solution { + Map cache = new HashMap<>(); + + public long putMarbles(int[] weights, int k) { + int n = weights.length; + long[] ans = dfs(0, k - 1, weights, n); + return ans[0] - ans[1]; + } + + private long[] dfs(int i, int k, int[] weights, int n) { + String key = i + "," + k; + if (cache.containsKey(key)) return cache.get(key); + if (k == 0) return new long[]{0L, 0L}; + if (i == n - 1 || n - i - 1 < k) { + return new long[]{(long)-1e15, (long)1e15}; + } + + long[] res = new long[]{0L, (long)1e15}; + + long[] cur = dfs(i + 1, k - 1, weights, n); + res[0] = Math.max(res[0], weights[i] + weights[i + 1] + cur[0]); + res[1] = Math.min(res[1], weights[i] + weights[i + 1] + cur[1]); + + cur = dfs(i + 1, k, weights, n); + res[0] = Math.max(res[0], cur[0]); + res[1] = Math.min(res[1], cur[1]); + + cache.put(key, res); + return res; + } +} +``` + +```cpp +class Solution { +public: + unordered_map> cache; + + long long putMarbles(vector& weights, int k) { + int n = weights.size(); + auto ans = dfs(0, k - 1, weights, n); + return ans.first - ans.second; + } + + pair dfs(int i, int k, vector& weights, int n) { + string key = to_string(i) + "," + to_string(k); + if (cache.count(key)) return cache[key]; + if (k == 0) return {0LL, 0LL}; + if (i == n - 1 || n - i - 1 < k) { + return {-1000000000000000LL, 1000000000000000LL}; + } + + pair res = {0LL, 1000000000000000LL}; + + auto cur = dfs(i + 1, k - 1, weights, n); + res.first = max(res.first, (long long)weights[i] + weights[i + 1] + cur.first); + res.second = min(res.second, (long long)weights[i] + weights[i + 1] + cur.second); + + cur = dfs(i + 1, k, weights, n); + res.first = max(res.first, cur.first); + res.second = min(res.second, cur.second); + + return cache[key] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} weights + * @param {number} k + * @return {number} + */ + putMarbles(weights, k) { + const n = weights.length; + const cache = new Map(); + + const dfs = (i, k) => { + const key = `${i},${k}`; + if (cache.has(key)) return cache.get(key); + if (k === 0) return [0, 0]; + if (i === n - 1 || n - i - 1 < k) return [-1e15, 1e15]; + + let res = [0, 1e15]; + + let cur = dfs(i + 1, k - 1); + res[0] = Math.max(res[0], weights[i] + weights[i + 1] + cur[0]); + res[1] = Math.min(res[1], weights[i] + weights[i + 1] + cur[1]); + + cur = dfs(i + 1, k); + res[0] = Math.max(res[0], cur[0]); + res[1] = Math.min(res[1], cur[1]); + + cache.set(key, res); + return res; + }; + + const ans = dfs(0, k - 1); + return ans[0] - ans[1]; + } +} +``` + +```csharp +public class Solution { + Dictionary cache = new Dictionary(); + + public long PutMarbles(int[] weights, int k) { + int n = weights.Length; + long[] ans = Dfs(0, k - 1, weights, n); + return (int)(ans[0] - ans[1]); + } + + private long[] Dfs(int i, int k, int[] weights, int n) { + string key = $"{i},{k}"; + if (cache.ContainsKey(key)) return cache[key]; + if (k == 0) return new long[] { 0L, 0L }; + if (i == n - 1 || n - i - 1 < k) { + return new long[] { -1000000000000000L, 1000000000000000L }; + } + + long[] res = new long[] { 0L, 1000000000000000L }; + + long[] cur = Dfs(i + 1, k - 1, weights, n); + res[0] = Math.Max(res[0], weights[i] + weights[i + 1] + cur[0]); + res[1] = Math.Min(res[1], weights[i] + weights[i + 1] + cur[1]); + + cur = Dfs(i + 1, k, weights, n); + res[0] = Math.Max(res[0], cur[0]); + res[1] = Math.Min(res[1], cur[1]); + + cache[key] = res; + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ is the number of marbles, and $k$ is the number of bags. + +--- + +## 2. Greedy + Sorting + +::tabs-start + +```python +class Solution: + def putMarbles(self, weights: List[int], k: int) -> int: + if k == 1: + return 0 + + splits = [] + for i in range(len(weights) - 1): + splits.append(weights[i] + weights[i + 1]) + + splits.sort() + i = k - 1 + + max_score = sum(splits[-i:]) + min_score = sum(splits[:i]) + return max_score - min_score +``` + +```java +public class Solution { + public long putMarbles(int[] weights, int k) { + if (k == 1) return 0L; + + int n = weights.length; + List splits = new ArrayList<>(); + + for (int i = 0; i < n - 1; i++) { + splits.add(weights[i] + weights[i + 1]); + } + + Collections.sort(splits); + int i = k - 1; + long maxScore = 0, minScore = 0; + + for (int j = 0; j < i; j++) minScore += splits.get(j); + for (int j = splits.size() - i; j < splits.size(); j++) { + maxScore += splits.get(j); + } + + return maxScore - minScore; + } +} +``` + +```cpp +class Solution { +public: + long long putMarbles(vector& weights, int k) { + if (k == 1) return 0LL; + + int n = weights.size(); + vector splits; + + for (int i = 0; i < n - 1; ++i) { + splits.push_back(weights[i] + weights[i + 1]); + } + + sort(splits.begin(), splits.end()); + int i = k - 1; + long long minScore = 0, maxScore = 0; + + for (int j = 0; j < i; ++j) minScore += splits[j]; + for (int j = splits.size() - i; j < splits.size(); ++j) { + maxScore += splits[j]; + } + + return maxScore - minScore; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} weights + * @param {number} k + * @return {number} + */ + putMarbles(weights, k) { + if (k === 1) return 0; + + const splits = []; + for (let i = 0; i < weights.length - 1; i++) { + splits.push(weights[i] + weights[i + 1]); + } + + splits.sort((a, b) => a - b); + const i = k - 1; + + let minScore = 0, maxScore = 0; + for (let j = 0; j < i; j++) minScore += splits[j]; + for (let j = splits.length - i; j < splits.length; j++) { + maxScore += splits[j]; + } + + return maxScore - minScore; + } +} +``` + +```csharp +public class Solution { + public long PutMarbles(int[] weights, int k) { + if (k == 1) return 0L; + + int n = weights.Length; + List splits = new List(); + + for (int i = 0; i < n - 1; i++) { + splits.Add(weights[i] + weights[i + 1]); + } + + splits.Sort(); + int iVal = k - 1; + long minScore = 0, maxScore = 0; + + for (int j = 0; j < iVal; j++) minScore += splits[j]; + for (int j = splits.Count - iVal; j < splits.Count; j++) { + maxScore += splits[j]; + } + + return maxScore - minScore; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of marbles, and $k$ is the number of bags. + +--- + +## 3. Heap + +::tabs-start + +```python +class Solution: + def putMarbles(self, weights: List[int], k: int) -> int: + if k == 1: + return 0 + + max_heap = [] + min_heap = [] + + for i in range(len(weights) - 1): + split = weights[i] + weights[i + 1] + + if len(max_heap) < k - 1: + heapq.heappush(max_heap, split) + else: + heapq.heappushpop(max_heap, split) + + if len(min_heap) < k - 1: + heapq.heappush(min_heap, -split) + else: + heapq.heappushpop(min_heap, -split) + + max_score = sum(max_heap) + min_score = -sum(min_heap) + return max_score - min_score +``` + +```java +public class Solution { + public long putMarbles(int[] weights, int k) { + if (k == 1) return 0L; + + PriorityQueue maxHeap = new PriorityQueue<>(); + PriorityQueue minHeap = new PriorityQueue<>(Collections.reverseOrder()); + + for (int i = 0; i < weights.length - 1; i++) { + int split = weights[i] + weights[i + 1]; + + if (maxHeap.size() < k - 1) maxHeap.offer(split); + else if (split > maxHeap.peek()) { + maxHeap.poll(); + maxHeap.offer(split); + } + + if (minHeap.size() < k - 1) minHeap.offer(split); + else if (split < minHeap.peek()) { + minHeap.poll(); + minHeap.offer(split); + } + } + + long maxScore = 0, minScore = 0; + for (int val : maxHeap) maxScore += val; + for (int val : minHeap) minScore += val; + + return maxScore - minScore; + } +} +``` + +```cpp +class Solution { +public: + long long putMarbles(vector& weights, int k) { + if (k == 1) return 0LL; + + priority_queue, greater> maxHeap; + priority_queue minHeap; + + for (int i = 0; i < weights.size() - 1; ++i) { + int split = weights[i] + weights[i + 1]; + + if ((int)maxHeap.size() < k - 1) maxHeap.push(split); + else if (split > maxHeap.top()) { + maxHeap.pop(); + maxHeap.push(split); + } + + if ((int)minHeap.size() < k - 1) minHeap.push(split); + else if (split < minHeap.top()) { + minHeap.pop(); + minHeap.push(split); + } + } + + long long maxScore = 0, minScore = 0; + while (!maxHeap.empty()) { + maxScore += maxHeap.top(); + maxHeap.pop(); + } + while (!minHeap.empty()) { + minScore += minHeap.top(); + minHeap.pop(); + } + + return maxScore - minScore; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} weights + * @param {number} k + * @return {number} + */ + putMarbles(weights, k) { + if (k === 1) return 0; + + const minHeap = new MinPriorityQueue(); + const maxHeap = new MaxPriorityQueue(); + + for (let i = 0; i < weights.length - 1; i++) { + const sum = weights[i] + weights[i + 1]; + + minHeap.enqueue(sum); + if (minHeap.size() > k - 1) minHeap.dequeue(); + + maxHeap.enqueue(sum); + if (maxHeap.size() > k - 1) maxHeap.dequeue(); + } + + let maxScore = 0, minScore = 0; + while (!minHeap.isEmpty()) maxScore += minHeap.dequeue(); + while (!maxHeap.isEmpty()) minScore += maxHeap.dequeue(); + + return maxScore - minScore; + } +} +``` + +```csharp +public class Solution { + public long PutMarbles(int[] weights, int k) { + if (k == 1) return 0L; + + var maxHeap = new PriorityQueue(); + var minHeap = new PriorityQueue( + Comparer.Create((a, b) => b.CompareTo(a)) + ); + + for (int i = 0; i < weights.Length - 1; i++) { + int split = weights[i] + weights[i + 1]; + + maxHeap.Enqueue(split, split); + if (maxHeap.Count > k - 1) maxHeap.Dequeue(); + + minHeap.Enqueue(split, split); + if (minHeap.Count > k - 1) minHeap.Dequeue(); + } + + long maxScore = 0, minScore = 0; + while (maxHeap.Count > 0) maxScore += maxHeap.Dequeue(); + while (minHeap.Count > 0) minScore += minHeap.Dequeue(); + + return maxScore - minScore; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log k)$ +* Space complexity: $O(k)$ + +> Where $n$ is the number of marbles, and $k$ is the number of bags. \ No newline at end of file diff --git a/articles/queue-reconstruction-by-height.md b/articles/queue-reconstruction-by-height.md new file mode 100644 index 000000000..72dbfcf5c --- /dev/null +++ b/articles/queue-reconstruction-by-height.md @@ -0,0 +1,1185 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]: + n = len(people) + mp = defaultdict(list) + + for p in people: + mp[p[1]].append(p[0]) + for key in mp: + mp[key].sort(reverse=True) + + res = [] + for i in range(n): + mini = -1 + for k in mp: + if k > i: + continue + cnt = 0 + j = len(res) - 1 + while j >= 0: + if res[j][0] >= mp[k][-1]: + cnt += 1 + j -= 1 + if cnt == k and (mini == -1 or mp[k][-1] < mp[mini][-1]): + mini = k + + res.append([mp[mini].pop(), mini]) + if not mp[mini]: + mp.pop(mini) + return res +``` + +```java +public class Solution { + public int[][] reconstructQueue(int[][] people) { + int n = people.length; + Map> mp = new HashMap<>(); + + for (int[] p : people) { + mp.computeIfAbsent(p[1], k -> new ArrayList<>()).add(p[0]); + } + for (int key : mp.keySet()) { + Collections.sort(mp.get(key), Collections.reverseOrder()); + } + + List res = new ArrayList<>(); + for (int i = 0; i < n; i++) { + int mini = -1; + for (int k : mp.keySet()) { + if (k > i) continue; + + int cnt = 0; + for (int j = res.size() - 1; j >= 0; j--) { + if (res.get(j)[0] >= mp.get(k).get(mp.get(k).size() - 1)) { + cnt++; + } + } + + if (cnt == k && (mini == -1 || mp.get(k).get(mp.get(k).size() - 1) < + mp.get(mini).get(mp.get(mini).size() - 1))) { + mini = k; + } + } + + List list = mp.get(mini); + res.add(new int[]{list.get(list.size() - 1), mini}); + list.remove(list.size() - 1); + if (list.isEmpty()) { + mp.remove(mini); + } + } + + return res.toArray(new int[n][2]); + } +} +``` + +```cpp +class Solution { +public: + vector> reconstructQueue(vector>& people) { + int n = people.size(); + unordered_map> mp; + + for (const auto& p : people) { + mp[p[1]].push_back(p[0]); + } + for (auto& pair : mp) { + sort(pair.second.rbegin(), pair.second.rend()); + } + + vector> res; + for (int i = 0; i < n; i++) { + int mini = -1; + for (const auto& pair : mp) { + int k = pair.first; + if (k > i) continue; + + int cnt = 0; + for (int j = res.size() - 1; j >= 0; j--) { + if (res[j][0] >= mp[k].back()) { + cnt++; + } + } + + if (cnt == k && (mini == -1 || mp[k].back() < mp[mini].back())) { + mini = k; + } + } + + res.push_back({mp[mini].back(), mini}); + mp[mini].pop_back(); + if (mp[mini].empty()) { + mp.erase(mini); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} people + * @return {number[][]} + */ + reconstructQueue(people) { + const n = people.length; + const mp = new Map(); + + for (const [h, k] of people) { + if (!mp.has(k)) mp.set(k, []); + mp.get(k).push(h); + } + for (const [k, heights] of mp) { + heights.sort((a, b) => b - a); + } + + const res = []; + for (let i = 0; i < n; i++) { + let mini = -1; + for (const [k, heights] of mp) { + if (k > i) continue; + + let cnt = 0; + for (let j = res.length - 1; j >= 0; j--) { + if (res[j][0] >= heights[heights.length - 1]) cnt++; + } + + if (cnt === k && (mini === -1 || heights[heights.length - 1] < mp.get(mini)[mp.get(mini).length - 1])) { + mini = k; + } + } + + res.push([mp.get(mini).pop(), mini]); + if (mp.get(mini).length === 0) { + mp.delete(mini); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + n ^ 3)$ +* Space complexity: $O(n)$ + +--- + +## 2. Sorting by Height in Descending Order + +::tabs-start + +```python +class Solution: + def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]: + res = [] + people.sort(key=lambda x: (-x[0], x[1])) + for p in people: + res.insert(p[1], p) + return res +``` + +```java +public class Solution { + public int[][] reconstructQueue(int[][] people) { + Arrays.sort(people, (a, b) -> a[0] == b[0] ? a[1] - b[1] : b[0] - a[0]); + List res = new ArrayList<>(); + for (int[] person : people) { + res.add(person[1], person); + } + return res.toArray(new int[res.size()][]); + } +} +``` + +```cpp +class Solution { +public: + vector> reconstructQueue(vector>& people) { + sort(people.begin(), people.end(), [](auto& a, auto& b) { + return a[0] == b[0] ? a[1] < b[1] : a[0] > b[0]; + }); + + list> res; + for (const auto& p : people) { + auto it = res.begin(); + advance(it, p[1]); + res.insert(it, p); + } + + return vector>(res.begin(), res.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} people + * @return {number[][]} + */ + reconstructQueue(people) { + people.sort((a, b) => (a[0] === b[0] ? a[1] - b[1] : b[0] - a[0])); + const res = []; + for (const [h, k] of people) { + res.splice(k, 0, [h, k]); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log n + n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sorting by Height in Ascending Order + +::tabs-start + +```python +class Solution: + def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]: + n = len(people) + people.sort(key=lambda x: (x[0], -x[1])) + res = [[] for _ in range(n)] + + for p in people: + cnt = i = 0 + while i < n: + if not res[i]: + if cnt == p[1]: + break + cnt += 1 + i += 1 + res[i] = p + + return res +``` + +```java +public class Solution { + public int[][] reconstructQueue(int[][] people) { + int n = people.length; + Arrays.sort(people, (a, b) -> a[0] == b[0] ? b[1] - a[1] : a[0] - b[0]); + int[][] res = new int[n][2]; + boolean[] used = new boolean[n]; + + for (int[] p : people) { + int cnt = 0, i = 0; + while (i < n) { + if (!used[i]) { + if (cnt == p[1]) break; + cnt++; + } + i++; + } + used[i] = true; + res[i] = p; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> reconstructQueue(vector>& people) { + sort(people.begin(), people.end(), [](auto& a, auto& b) { + return a[0] == b[0] ? a[1] > b[1] : a[0] < b[0]; + }); + + vector> res(people.size(), vector()); + for (const auto& p : people) { + int cnt = 0, i = 0; + while (i < people.size()) { + if (res[i].empty()) { + if (cnt == p[1]) break; + cnt++; + } + i++; + } + res[i] = p; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} people + * @return {number[][]} + */ + reconstructQueue(people) { + people.sort((a, b) => a[0] === b[0] ? b[1] - a[1] : a[0] - b[0]); + const res = Array(people.length).fill(null); + + for (const p of people) { + let cnt = 0, i = 0; + + while (i < people.length) { + if (res[i] === null) { + if (cnt === p[1]) break; + cnt++; + } + i++; + } + res[i] = p; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log n + n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 4. Binary Search + Segment Tree + +::tabs-start + +```python +class SegmentTree: + def __init__(self, N): + self.n = N + while (self.n & (self.n - 1)) != 0: + self.n += 1 + self.build(N) + + def build(self, N): + self.tree = [0] * (2 * self.n) + for i in range(N): + self.tree[self.n + i] = 1 + for i in range(self.n - 1, 0, -1): + self.tree[i] = self.tree[i << 1] + self.tree[i << 1 | 1] + + def update(self, i, val): + self.tree[self.n + i] = val + j = (self.n + i) >> 1 + while j >= 1: + self.tree[j] = self.tree[j << 1] + self.tree[j << 1 | 1] + j >>= 1 + + def query(self, l, r): + res = 0 + l += self.n + r += self.n + 1 + while l < r: + if l & 1: + res += self.tree[l] + l += 1 + if r & 1: + r -= 1 + res += self.tree[r] + l >>= 1 + r >>= 1 + return res + +class Solution: + def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]: + n = len(people) + people.sort(key=lambda x: (x[0], -x[1])) + res = [[] for _ in range(n)] + + segTree = SegmentTree(n) + for p in people: + l, r = 0, n - 1 + idx = 0 + while l <= r: + mid = (l + r) >> 1 + cnt = segTree.query(0, mid) + if cnt > p[1]: + idx = mid + r = mid - 1 + else: + l = mid + 1 + + res[idx] = p + segTree.update(idx, 0) + + return res +``` + +```java +class SegmentTree { + int n; + int[] tree; + + SegmentTree(int N) { + this.n = N; + while (Integer.bitCount(n) != 1) { + n++; + } + build(N); + } + + void build(int N) { + tree = new int[2 * n]; + for (int i = 0; i < N; i++) { + tree[n + i] = 1; + } + for (int i = n - 1; i > 0; --i) { + tree[i] = tree[i << 1] + tree[i << 1 | 1]; + } + } + + void update(int i, int val) { + tree[n + i] = val; + for (int j = (n + i) >> 1; j >= 1; j >>= 1) { + tree[j] = tree[j << 1] + tree[j << 1 | 1]; + } + } + + int query(int l, int r) { + int res = 0; + for (l += n, r += n + 1; l < r; l >>= 1, r >>= 1) { + if ((l & 1) == 1) res += tree[l++]; + if ((r & 1) == 1) res += tree[--r]; + } + return res; + } +} + +public class Solution { + public int[][] reconstructQueue(int[][] people) { + int n = people.length; + Arrays.sort(people, (a, b) -> a[0] == b[0] ? b[1] - a[1] : a[0] - b[0]); + int[][] res = new int[n][2]; + + SegmentTree segTree = new SegmentTree(n); + for (int[] p : people) { + int l = 0, r = n - 1, idx = 0; + while (l <= r) { + int mid = (l + r) >> 1; + int cnt = segTree.query(0, mid); + if (cnt > p[1]) { + idx = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + + res[idx] = p; + segTree.update(idx, 0); + } + + return res; + } +} +``` + +```cpp +class SegmentTree { +public: + int n; + vector tree; + + SegmentTree(int N) { + this->n = N; + while (__builtin_popcount(n) != 1) { + n++; + } + build(N); + } + + void build(int N) { + tree.resize(2 * n); + for (int i = 0; i < N; i++) { + tree[n + i] = 1; + } + for (int i = n - 1; i > 0; --i) { + tree[i] = tree[i << 1] + tree[i << 1 | 1]; + } + } + + void update(int i, int val) { + tree[n + i] = val; + for (int j = (n + i) >> 1; j >= 1; j >>= 1) { + tree[j] = tree[j << 1] + tree[j << 1 | 1]; + } + } + + int query(int l, int r) { + int res = 0; + for (l += n, r += n + 1; l < r; l >>= 1, r >>= 1) { + if (l & 1) res += tree[l++]; + if (r & 1) res += tree[--r]; + } + return res; + } +}; + +class Solution { +public: + vector> reconstructQueue(vector>& people) { + int n = people.size(); + sort(people.begin(), people.end(), [](auto& a, auto& b) { + return a[0] == b[0] ? a[1] > b[1] : a[0] < b[0]; + }); + vector> res(n, vector()); + + SegmentTree segTree(n); + for (const auto& p : people) { + int l = 0, r = n - 1, idx = 0; + while (l <= r) { + int mid = (l + r) >> 1; + int cnt = segTree.query(0, mid); + if (cnt > p[1]) { + idx = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + + res[idx] = p; + segTree.update(idx, 0); + } + + return res; + } +}; +``` + +```javascript +class SegmentTree { + /** + * @constructor + * @param {number} N + */ + constructor(N) { + this.n = N; + while ((this.n & (this.n - 1)) !== 0) { + this.n++; + } + this.build(N); + } + + /** + * @param {number} N + * @return {void} + */ + build(N) { + this.tree = new Int32Array(2 * this.n); + for (let i = 0; i < N; i++) { + this.tree[this.n + i] = 1; + } + for (let i = this.n - 1; i > 0; i--) { + this.tree[i] = this.tree[i << 1] + this.tree[i << 1 | 1]; + } + } + + /** + * @param {number} i + * @param {number} val + * @return {void} + */ + update(i, val) { + this.tree[this.n + i] = val; + for (let j = (this.n + i) >> 1; j >= 1; j >>= 1) { + this.tree[j] = this.tree[j << 1] + this.tree[j << 1 | 1]; + } + } + + /** + * @param {number} l + * @param {number} r + * @return {number} + */ + query(l, r) { + let res = 0; + l += this.n; + r += this.n + 1; + + while (l < r) { + if (l & 1) res += this.tree[l++]; + if (r & 1) res += this.tree[--r]; + l >>= 1; + r >>= 1; + } + + return res; + } +} + +class Solution { + /** + * @param {number[][]} people + * @return {number[][]} + */ + reconstructQueue(people) { + const n = people.length; + people.sort((a, b) => a[0] === b[0] ? b[1] - a[1] : a[0] - b[0]); + const res = Array(n).fill(null); + + const segTree = new SegmentTree(n); + for (const p of people) { + let l = 0, r = n - 1, idx = 0; + while (l <= r) { + let mid = (l + r) >> 1; + let cnt = segTree.query(0, mid); + if (cnt > p[1]) { + idx = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + + res[idx] = p; + segTree.update(idx, 0); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log ^ 2 n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Binary Search + Binary Indexed Tree (Fenwick Tree) + +::tabs-start + +```python +class BIT: + def __init__(self, N): + self.n = N + 1 + self.tree = [0] * self.n + for i in range(self.n - 1): + self.update(i, 1) + + def update(self, index, val): + index += 1 + while index < self.n: + self.tree[index] += val + index += index & -index + + def prefix_sum(self, index): + total_sum = 0 + while index > 0: + total_sum += self.tree[index] + index -= index & -index + return total_sum + + def query(self, left, right): + return self.prefix_sum(right + 1) - self.prefix_sum(left) + +class Solution: + def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]: + n = len(people) + people.sort(key=lambda x: (x[0], -x[1])) + res = [[] for _ in range(n)] + + bit = BIT(n) + for p in people: + l, r = 0, n - 1 + idx = 0 + while l <= r: + mid = (l + r) >> 1 + cnt = bit.query(0, mid) + if cnt > p[1]: + idx = mid + r = mid - 1 + else: + l = mid + 1 + + res[idx] = p + bit.update(idx, -1) + + return res +``` + +```java +class BIT { + private int[] tree; + private int n; + + public BIT(int N) { + this.n = N + 1; + this.tree = new int[n]; + for (int i = 0; i < n - 1; i++) { + update(i, 1); + } + } + + public void update(int index, int val) { + index++; + while (index < n) { + tree[index] += val; + index += index & -index; + } + } + + public int prefixSum(int index) { + int totalSum = 0; + while (index > 0) { + totalSum += tree[index]; + index -= index & -index; + } + return totalSum; + } + + public int query(int left, int right) { + return prefixSum(right + 1) - prefixSum(left); + } +} + +public class Solution { + public int[][] reconstructQueue(int[][] people) { + int n = people.length; + Arrays.sort(people, (a, b) -> a[0] == b[0] ? b[1] - a[1] : a[0] - b[0]); + int[][] res = new int[n][2]; + + BIT bit = new BIT(n); + for (int[] p : people) { + int l = 0, r = n - 1, idx = 0; + while (l <= r) { + int mid = (l + r) >> 1; + int cnt = bit.query(0, mid); + if (cnt > p[1]) { + idx = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + + res[idx] = p; + bit.update(idx, -1); + } + + return res; + } +} +``` + +```cpp +class BIT { + vector tree; + int n; + +public: + BIT(int N) { + n = N + 1; + tree.resize(n, 0); + for (int i = 0; i < n - 1; i++) { + update(i, 1); + } + } + + void update(int index, int val) { + index++; + while (index < n) { + tree[index] += val; + index += index & -index; + } + } + + int prefixSum(int index) { + int totalSum = 0; + while (index > 0) { + totalSum += tree[index]; + index -= index & -index; + } + return totalSum; + } + + int query(int left, int right) { + return prefixSum(right + 1) - prefixSum(left); + } +}; + +class Solution { +public: + vector> reconstructQueue(vector>& people) { + int n = people.size(); + sort(people.begin(), people.end(), [](auto& a, auto& b) { + return a[0] == b[0] ? a[1] > b[1] : a[0] < b[0]; + }); + vector> res(n, vector()); + + BIT bit(n); + for (const auto& p : people) { + int l = 0, r = n - 1, idx = 0; + while (l <= r) { + int mid = (l + r) >> 1; + int cnt = bit.query(0, mid); + if (cnt > p[1]) { + idx = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + + res[idx] = p; + bit.update(idx, -1); + } + + return res; + } +}; +``` + +```javascript +class BIT { + /** + * @constructor + * @param {number} N + */ + constructor(N) { + this.n = N + 1; + this.tree = new Int32Array(this.n); + for (let i = 0; i < this.n - 1; i++) { + this.update(i, 1); + } + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + update(index, val) { + index++; + while (index < this.n) { + this.tree[index] += val; + index += index & -index; + } + } + + /** + * @param {number} index + * @return {number} + */ + prefixSum(index) { + let totalSum = 0; + while (index > 0) { + totalSum += this.tree[index]; + index -= index & -index; + } + return totalSum; + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + query(left, right) { + return this.prefixSum(right + 1) - this.prefixSum(left); + } +} + +class Solution { + /** + * @param {number[][]} people + * @return {number[][]} + */ + reconstructQueue(people) { + const n = people.length; + people.sort((a, b) => a[0] === b[0] ? b[1] - a[1] : a[0] - b[0]); + const res = Array(n).fill(null); + + const bit = new BIT(n); + for (const p of people) { + let l = 0, r = n - 1, idx = 0; + while (l <= r) { + let mid = (l + r) >> 1; + let cnt = bit.query(0, mid); + if (cnt > p[1]) { + idx = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + + res[idx] = p; + bit.update(idx, -1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log ^ 2 n)$ +* Space complexity: $O(n)$ + +--- + +## 6. Binary Indexed Tree (Fenwick Tree) + +::tabs-start + +```python +class BIT: + def __init__(self, N): + self.n = N + 1 + self.tree = [0] * self.n + for i in range(self.n - 1): + self.update(i, 1) + + def update(self, index, val): + index += 1 + while index < self.n: + self.tree[index] += val + index += index & -index + + def getIdx(self, cnt, MSB): + idx = 0 + while MSB: + nxtIdx = idx + MSB + if nxtIdx < self.n and cnt >= self.tree[nxtIdx]: + idx = nxtIdx + cnt -= self.tree[nxtIdx] + MSB >>= 1 + return idx + +class Solution: + def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]: + n = len(people) + people.sort(key=lambda x: (x[0], -x[1])) + res = [[] for _ in range(n)] + + bit = BIT(n) + MSB = 1 << int(math.log(n, 2)) + for p in people: + idx = bit.getIdx(p[1], MSB) + res[idx] = p + bit.update(idx, -1) + + return res +``` + +```java +class BIT { + private int[] tree; + private int n; + + public BIT(int N) { + this.n = N + 1; + this.tree = new int[n]; + for (int i = 0; i < n - 1; i++) { + update(i, 1); + } + } + + public void update(int index, int val) { + index++; + while (index < n) { + tree[index] += val; + index += index & -index; + } + } + + public int getIdx(int cnt, int MSB) { + int idx = 0; + while (MSB != 0) { + int nxtIdx = idx + MSB; + if (nxtIdx < n && cnt >= tree[nxtIdx]) { + idx = nxtIdx; + cnt -= tree[nxtIdx]; + } + MSB >>= 1; + } + return idx; + } +} + +public class Solution { + public int[][] reconstructQueue(int[][] people) { + int n = people.length; + Arrays.sort(people, (a, b) -> a[0] == b[0] ? b[1] - a[1] : a[0] - b[0]); + int[][] res = new int[n][2]; + + BIT bit = new BIT(n); + int MSB = 1 << (31 - Integer.numberOfLeadingZeros(n)); + for (int[] p : people) { + int idx = bit.getIdx(p[1], MSB); + res[idx] = p; + bit.update(idx, -1); + } + + return res; + } +} +``` + +```cpp +class BIT { + vector tree; + int n; + +public: + BIT(int N) { + n = N + 1; + tree.resize(n, 0); + for (int i = 0; i < n - 1; i++) { + update(i, 1); + } + } + + void update(int index, int val) { + index++; + while (index < n) { + tree[index] += val; + index += index & -index; + } + } + + int getIdx(int cnt, int MSB) { + int idx = 0; + while (MSB != 0) { + int nxtIdx = idx + MSB; + if (nxtIdx < n && cnt >= tree[nxtIdx]) { + idx = nxtIdx; + cnt -= tree[nxtIdx]; + } + MSB >>= 1; + } + return idx; + } +}; + +class Solution { +public: + vector> reconstructQueue(vector>& people) { + int n = people.size(); + sort(people.begin(), people.end(), [](auto& a, auto& b) { + return a[0] == b[0] ? a[1] > b[1] : a[0] < b[0]; + }); + vector> res(n, vector()); + + BIT bit(n); + int MSB = 1 << (31 - __builtin_clz(n)); + for (const auto& p : people) { + int idx = bit.getIdx(p[1], MSB); + res[idx] = p; + bit.update(idx, -1); + } + + return res; + } +}; +``` + +```javascript +class BIT { + /** + * @constructor + * @param {number} N + */ + constructor(N) { + this.n = N + 1; + this.tree = new Int32Array(this.n); + for (let i = 0; i < this.n - 1; i++) { + this.update(i, 1); + } + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + update(index, val) { + index++; + while (index < this.n) { + this.tree[index] += val; + index += index & -index; + } + } + + /** + * @param {number} cnt + * @param {number} MSB + * @return {number} + */ + getIdx(cnt, MSB) { + let idx = 0; + while (MSB != 0) { + let nxtIdx = idx + MSB; + if (nxtIdx < this.n && cnt >= this.tree[nxtIdx]) { + idx = nxtIdx; + cnt -= this.tree[nxtIdx]; + } + MSB >>= 1; + } + return idx; + } +} + +class Solution { + /** + * @param {number[][]} people + * @return {number[][]} + */ + reconstructQueue(people) { + const n = people.length; + people.sort((a, b) => a[0] === b[0] ? b[1] - a[1] : a[0] - b[0]); + const res = Array(n).fill(null); + + const bit = new BIT(n); + const MSB = 1 << Math.floor(Math.log2(n)); + for (const p of people) { + let idx = bit.getIdx(p[1], MSB); + res[idx] = p; + bit.update(idx, -1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/range-sum-of-bst.md b/articles/range-sum-of-bst.md new file mode 100644 index 000000000..cd0873dec --- /dev/null +++ b/articles/range-sum-of-bst.md @@ -0,0 +1,408 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def rangeSumBST(self, root: Optional[TreeNode], low: int, high: int) -> int: + if not root: + return 0 + + res = root.val if low <= root.val <= high else 0 + res += self.rangeSumBST(root.left, low, high) + res += self.rangeSumBST(root.right, low, high) + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int rangeSumBST(TreeNode root, int low, int high) { + if (root == null) return 0; + + int res = (low <= root.val && root.val <= high) ? root.val : 0; + res += rangeSumBST(root.left, low, high); + res += rangeSumBST(root.right, low, high); + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int rangeSumBST(TreeNode* root, int low, int high) { + if (!root) return 0; + + int res = (low <= root->val && root->val <= high) ? root->val : 0; + res += rangeSumBST(root->left, low, high); + res += rangeSumBST(root->right, low, high); + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} low + * @param {number} high + * @return {number} + */ + rangeSumBST(root, low, high) { + if (!root) return 0; + + let res = (low <= root.val && root.val <= high) ? root.val : 0; + res += this.rangeSumBST(root.left, low, high); + res += this.rangeSumBST(root.right, low, high); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Depth First Search (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def rangeSumBST(self, root: Optional[TreeNode], low: int, high: int) -> int: + if not root: + return 0 + + if root.val > high: + return self.rangeSumBST(root.left, low, high) + if root.val < low: + return self.rangeSumBST(root.right, low, high) + + return ( + root.val + + self.rangeSumBST(root.left, low, high) + + self.rangeSumBST(root.right, low, high) + ) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int rangeSumBST(TreeNode root, int low, int high) { + if (root == null) return 0; + + if (root.val > high) { + return rangeSumBST(root.left, low, high); + } + if (root.val < low) { + return rangeSumBST(root.right, low, high); + } + + return root.val + rangeSumBST(root.left, low, high) + + rangeSumBST(root.right, low, high); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int rangeSumBST(TreeNode* root, int low, int high) { + if (!root) return 0; + + if (root->val > high) { + return rangeSumBST(root->left, low, high); + } + if (root->val < low) { + return rangeSumBST(root->right, low, high); + } + + return root->val + rangeSumBST(root->left, low, high) + + rangeSumBST(root->right, low, high); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} low + * @param {number} high + * @return {number} + */ + rangeSumBST(root, low, high) { + if (!root) return 0; + + if (root.val > high) { + return rangeSumBST(root.left, low, high); + } + if (root.val < low) { + return rangeSumBST(root.right, low, high); + } + + return root.val + this.rangeSumBST(root.left, low, high) + + this.rangeSumBST(root.right, low, high); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def rangeSumBST(self, root: Optional[TreeNode], low: int, high: int) -> int: + stack = [root] + res = 0 + + while stack: + node = stack.pop() + if not node: + continue + + if low <= node.val <= high: + res += node.val + if node.val > low: + stack.append(node.left) + if node.val < high: + stack.append(node.right) + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int rangeSumBST(TreeNode root, int low, int high) { + int res = 0; + Stack stack = new Stack<>(); + stack.push(root); + + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + if (node == null) continue; + + if (low <= node.val && node.val <= high) { + res += node.val; + } + if (node.val > low) { + stack.push(node.left); + } + if (node.val < high) { + stack.push(node.right); + } + } + + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int rangeSumBST(TreeNode* root, int low, int high) { + int res = 0; + stack stack; + stack.push(root); + + while (!stack.empty()) { + TreeNode* node = stack.top(); + stack.pop(); + if (!node) continue; + + if (low <= node->val && node->val <= high) { + res += node->val; + } + if (node->val > low) { + stack.push(node->left); + } + if (node->val < high) { + stack.push(node->right); + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} low + * @param {number} high + * @return {number} + */ + rangeSumBST(root, low, high) { + let res = 0; + let stack = [root]; + + while (stack.length > 0) { + let node = stack.pop(); + if (!node) continue; + + if (low <= node.val && node.val <= high) { + res += node.val; + } + if (node.val > low) { + stack.push(node.left); + } + if (node.val < high) { + stack.push(node.right); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/range-sum-query-2d-immutable.md b/articles/range-sum-query-2d-immutable.md new file mode 100644 index 000000000..3d19e47b0 --- /dev/null +++ b/articles/range-sum-query-2d-immutable.md @@ -0,0 +1,369 @@ +## 1. Brute Force + +::tabs-start + +```python +class NumMatrix: + + def __init__(self, matrix: list[list[int]]): + self.matrix = matrix + + def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int: + res = 0 + for r in range(row1, row2 + 1): + for c in range(col1, col2 + 1): + res += self.matrix[r][c] + return res +``` + +```java +public class NumMatrix { + + private int[][] matrix; + + public NumMatrix(int[][] matrix) { + this.matrix = matrix; + } + + public int sumRegion(int row1, int col1, int row2, int col2) { + int res = 0; + for (int r = row1; r <= row2; r++) { + for (int c = col1; c <= col2; c++) { + res += matrix[r][c]; + } + } + return res; + } +} +``` + +```cpp +class NumMatrix { +private: + vector> matrix; + +public: + NumMatrix(vector>& matrix) { + this->matrix = matrix; + } + + int sumRegion(int row1, int col1, int row2, int col2) { + int res = 0; + for (int r = row1; r <= row2; r++) { + for (int c = col1; c <= col2; c++) { + res += matrix[r][c]; + } + } + return res; + } +}; +``` + +```javascript +class NumMatrix { + /** + * @param {number[][]} matrix + */ + constructor(matrix) { + this.matrix = matrix; + } + + /** + * @param {number} row1 + * @param {number} col1 + * @param {number} row2 + * @param {number} col2 + * @return {number} + */ + sumRegion(row1, col1, row2, col2) { + let res = 0; + for (let r = row1; r <= row2; r++) { + for (let c = col1; c <= col2; c++) { + res += this.matrix[r][c]; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ for each query. +* Space complexity: $O(1)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the matrix. + +--- + +## 2. One Dimensional Prefix Sum + +::tabs-start + +```python +class NumMatrix: + + def __init__(self, matrix: list[list[int]]): + self.prefixSum = [[0] * len(matrix[0]) for _ in range(len(matrix))] + + for row in range(len(matrix)): + self.prefixSum[row][0] = matrix[row][0] + for col in range(1, len(matrix[0])): + self.prefixSum[row][col] = self.prefixSum[row][col - 1] + matrix[row][col] + + def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int: + res = 0 + for row in range(row1, row2 + 1): + if col1 > 0: + res += self.prefixSum[row][col2] - self.prefixSum[row][col1 - 1] + else: + res += self.prefixSum[row][col2] + return res +``` + +```java +public class NumMatrix { + + private int[][] prefixSum; + + public NumMatrix(int[][] matrix) { + int rows = matrix.length, cols = matrix[0].length; + prefixSum = new int[rows][cols]; + + for (int row = 0; row < rows; row++) { + prefixSum[row][0] = matrix[row][0]; + for (int col = 1; col < cols; col++) { + prefixSum[row][col] = prefixSum[row][col - 1] + matrix[row][col]; + } + } + } + + public int sumRegion(int row1, int col1, int row2, int col2) { + int res = 0; + for (int row = row1; row <= row2; row++) { + if (col1 > 0) { + res += prefixSum[row][col2] - prefixSum[row][col1 - 1]; + } else { + res += prefixSum[row][col2]; + } + } + return res; + } +} +``` + +```cpp +class NumMatrix { +private: + vector> prefixSum; + +public: + NumMatrix(vector>& matrix) { + int rows = matrix.size(), cols = matrix[0].size(); + prefixSum = vector>(rows, vector(cols, 0)); + + for (int row = 0; row < rows; row++) { + prefixSum[row][0] = matrix[row][0]; + for (int col = 1; col < cols; col++) { + prefixSum[row][col] = prefixSum[row][col - 1] + matrix[row][col]; + } + } + } + + int sumRegion(int row1, int col1, int row2, int col2) { + int res = 0; + for (int row = row1; row <= row2; row++) { + if (col1 > 0) { + res += prefixSum[row][col2] - prefixSum[row][col1 - 1]; + } else { + res += prefixSum[row][col2]; + } + } + return res; + } +}; +``` + +```javascript +class NumMatrix { + /** + * @param {number[][]} matrix + */ + constructor(matrix) { + this.prefixSum = Array.from({ length: matrix.length }, () => Array(matrix[0].length).fill(0)); + + for (let row = 0; row < matrix.length; row++) { + this.prefixSum[row][0] = matrix[row][0]; + for (let col = 1; col < matrix[0].length; col++) { + this.prefixSum[row][col] = this.prefixSum[row][col - 1] + matrix[row][col]; + } + } + } + + /** + * @param {number} row1 + * @param {number} col1 + * @param {number} row2 + * @param {number} col2 + * @return {number} + */ + sumRegion(row1, col1, row2, col2) { + let res = 0; + for (let row = row1; row <= row2; row++) { + if (col1 > 0) { + res += this.prefixSum[row][col2] - this.prefixSum[row][col1 - 1]; + } else { + res += this.prefixSum[row][col2]; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the matrix. + +--- + +## 3. Two Dimensional Prefix Sum + +::tabs-start + +```python +class NumMatrix: + + def __init__(self, matrix: list[list[int]]): + ROWS, COLS = len(matrix), len(matrix[0]) + self.sumMat = [[0] * (COLS + 1) for _ in range(ROWS + 1)] + + for r in range(ROWS): + prefix = 0 + for c in range(COLS): + prefix += matrix[r][c] + above = self.sumMat[r][c + 1] + self.sumMat[r + 1][c + 1] = prefix + above + + def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int: + row1, col1, row2, col2 = row1 + 1, col1 + 1, row2 + 1, col2 + 1 + bottomRight = self.sumMat[row2][col2] + above = self.sumMat[row1 - 1][col2] + left = self.sumMat[row2][col1 - 1] + topLeft = self.sumMat[row1 - 1][col1 - 1] + return bottomRight - above - left + topLeft +``` + +```java +public class NumMatrix { + + private int[][] sumMat; + + public NumMatrix(int[][] matrix) { + int ROWS = matrix.length, COLS = matrix[0].length; + sumMat = new int[ROWS + 1][COLS + 1]; + + for (int r = 0; r < ROWS; r++) { + int prefix = 0; + for (int c = 0; c < COLS; c++) { + prefix += matrix[r][c]; + int above = sumMat[r][c + 1]; + sumMat[r + 1][c + 1] = prefix + above; + } + } + } + + public int sumRegion(int row1, int col1, int row2, int col2) { + row1++; col1++; row2++; col2++; + int bottomRight = sumMat[row2][col2]; + int above = sumMat[row1 - 1][col2]; + int left = sumMat[row2][col1 - 1]; + int topLeft = sumMat[row1 - 1][col1 - 1]; + return bottomRight - above - left + topLeft; + } +} +``` + +```cpp +class NumMatrix { +private: + vector> sumMat; + +public: + NumMatrix(vector>& matrix) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + sumMat = vector>(ROWS + 1, vector(COLS + 1, 0)); + + for (int r = 0; r < ROWS; r++) { + int prefix = 0; + for (int c = 0; c < COLS; c++) { + prefix += matrix[r][c]; + int above = sumMat[r][c + 1]; + sumMat[r + 1][c + 1] = prefix + above; + } + } + } + + int sumRegion(int row1, int col1, int row2, int col2) { + row1++; col1++; row2++; col2++; + int bottomRight = sumMat[row2][col2]; + int above = sumMat[row1 - 1][col2]; + int left = sumMat[row2][col1 - 1]; + int topLeft = sumMat[row1 - 1][col1 - 1]; + return bottomRight - above - left + topLeft; + } +}; +``` + +```javascript +class NumMatrix { + /** + * @param {number[][]} matrix + */ + constructor(matrix) { + const ROWS = matrix.length, COLS = matrix[0].length; + this.sumMat = Array.from({ length: ROWS + 1 }, () => Array(COLS + 1).fill(0)); + + for (let r = 0; r < ROWS; r++) { + let prefix = 0; + for (let c = 0; c < COLS; c++) { + prefix += matrix[r][c]; + const above = this.sumMat[r][c + 1]; + this.sumMat[r + 1][c + 1] = prefix + above; + } + } + } + + /** + * @param {number} row1 + * @param {number} col1 + * @param {number} row2 + * @param {number} col2 + * @return {number} + */ + sumRegion(row1, col1, row2, col2) { + row1++; col1++; row2++; col2++; + const bottomRight = this.sumMat[row2][col2]; + const above = this.sumMat[row1 - 1][col2]; + const left = this.sumMat[row2][col1 - 1]; + const topLeft = this.sumMat[row1 - 1][col1 - 1]; + return bottomRight - above - left + topLeft; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for each query. +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the matrix. \ No newline at end of file diff --git a/articles/range-sum-query-immutable.md b/articles/range-sum-query-immutable.md new file mode 100644 index 000000000..f53012caa --- /dev/null +++ b/articles/range-sum-query-immutable.md @@ -0,0 +1,452 @@ +## 1. Brute Force + +::tabs-start + +```python +class NumArray: + def __init__(self, nums): + self.nums = nums + + def sumRange(self, left, right): + res = 0 + for i in range(left, right + 1): + res += self.nums[i] + return res +``` + +```java +public class NumArray { + private final int[] nums; + + public NumArray(int[] nums) { + this.nums = nums; + } + + public int sumRange(int left, int right) { + int res = 0; + for (int i = left; i <= right; i++) { + res += nums[i]; + } + return res; + } +} +``` + +```cpp +class NumArray { +private: + const vector& nums; + +public: + NumArray(const vector& nums) : nums(nums) {} + + int sumRange(int left, int right) { + int res = 0; + for (int i = left; i <= right; i++) { + res += nums[i]; + } + return res; + } +}; +``` + +```javascript +class NumArray { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.nums = nums; + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + sumRange(left, right) { + let res = 0; + for (let i = left; i <= right; i++) { + res += this.nums[i]; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ for each $sumRange()$ query. +* Space complexity: $O(1)$ since we only make a reference to the input array. + +--- + +## 2. Prefix Sum - I + +::tabs-start + +```python +class NumArray: + def __init__(self, nums): + self.prefix = [] + cur = 0 + for num in nums: + cur += num + self.prefix.append(cur) + + def sumRange(self, left, right): + rightSum = self.prefix[right] + leftSum = self.prefix[left - 1] if left > 0 else 0 + return rightSum - leftSum +``` + +```java +public class NumArray { + private int[] prefix; + + public NumArray(int[] nums) { + prefix = new int[nums.length]; + int cur = 0; + for (int i = 0; i < nums.length; i++) { + cur += nums[i]; + prefix[i] = cur; + } + } + + public int sumRange(int left, int right) { + int rightSum = prefix[right]; + int leftSum = left > 0 ? prefix[left - 1] : 0; + return rightSum - leftSum; + } +} +``` + +```cpp +class NumArray { +private: + vector prefix; + +public: + NumArray(const vector& nums) { + int cur = 0; + for (int num : nums) { + cur += num; + prefix.push_back(cur); + } + } + + int sumRange(int left, int right) { + int rightSum = prefix[right]; + int leftSum = left > 0 ? prefix[left - 1] : 0; + return rightSum - leftSum; + } +}; +``` + +```javascript +class NumArray { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.prefix = []; + let cur = 0; + for (let num of nums) { + cur += num; + this.prefix.push(cur); + } + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + sumRange(left, right) { + const rightSum = this.prefix[right]; + const leftSum = left > 0 ? this.prefix[left - 1] : 0; + return rightSum - leftSum; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for each $sumRange()$ query, $O(n)$ for building the prefix sum array. +* Space complexity: $O(n)$ + +--- + +## 3. Prefix Sum - II + +::tabs-start + +```python +class NumArray: + def __init__(self, nums): + self.prefix = [0] * (len(nums) + 1) + for i in range(len(nums)): + self.prefix[i + 1] = self.prefix[i] + nums[i] + + + def sumRange(self, left, right): + return self.prefix[right + 1] - self.prefix[left] +``` + +```java +public class NumArray { + private int[] prefix; + + public NumArray(int[] nums) { + prefix = new int[nums.length + 1]; + for (int i = 0; i < nums.length; i++) { + prefix[i + 1] = prefix[i] + nums[i]; + } + } + + public int sumRange(int left, int right) { + return prefix[right + 1] - prefix[left]; + } +} +``` + +```cpp +class NumArray { +private: + vector prefix; + +public: + NumArray(const vector& nums) { + prefix = vector(nums.size() + 1, 0); + for (int i = 0; i < nums.size(); i++) { + prefix[i + 1] = prefix[i] + nums[i]; + } + } + + int sumRange(int left, int right) { + return prefix[right + 1] - prefix[left]; + } +}; +``` + +```javascript +class NumArray { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.prefix = new Array(nums.length + 1).fill(0); + for (let i = 0; i < nums.length; i++) { + this.prefix[i + 1] = this.prefix[i] + nums[i]; + } + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + sumRange(left, right) { + return this.prefix[right + 1] - this.prefix[left]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for each $sumRange()$ query, $O(n)$ for building the prefix sum array. +* Space complexity: $O(n)$ + +--- + +## 4. Segment Tree + +::tabs-start + +```python +class SegmentTree: + def __init__(self, nums): + self.n = len(nums) + self.tree = [0] * (2 * self.n) + for i in range(self.n): + self.tree[self.n + i] = nums[i] + for i in range(self.n - 1, 0, -1): + self.tree[i] = self.tree[2 * i] + self.tree[2 * i + 1] + + def query(self, left, right): + left += self.n + right += self.n + 1 + res = 0 + while left < right: + if left % 2 == 1: + res += self.tree[left] + left += 1 + if right % 2 == 1: + right -= 1 + res += self.tree[right] + left //= 2 + right //= 2 + return res + +class NumArray: + def __init__(self, nums): + self.segTree = SegmentTree(nums) + + def sumRange(self, left, right): + return self.segTree.query(left, right) +``` + +```java +class SegmentTree { + private int[] tree; + private int n; + + public SegmentTree(int[] nums) { + n = nums.length; + tree = new int[2 * n]; + for (int i = 0; i < n; i++) { + tree[n + i] = nums[i]; + } + for (int i = n - 1; i > 0; i--) { + tree[i] = tree[2 * i] + tree[2 * i + 1]; + } + } + + public int query(int left, int right) { + int sum = 0; + left += n; + right += n + 1; + while (left < right) { + if (left % 2 == 1) sum += tree[left++]; + if (right % 2 == 1) sum += tree[--right]; + left /= 2; + right /= 2; + } + return sum; + } +} + +public class NumArray { + private SegmentTree segTree; + + public NumArray(int[] nums) { + segTree = new SegmentTree(nums); + } + + public int sumRange(int left, int right) { + return segTree.query(left, right); + } +} +``` + +```cpp +class SegmentTree { +private: + vector tree; + int n; + +public: + SegmentTree(const vector& nums) { + n = nums.size(); + tree.resize(2 * n); + for (int i = 0; i < n; i++) { + tree[n + i] = nums[i]; + } + for (int i = n - 1; i > 0; i--) { + tree[i] = tree[2 * i] + tree[2 * i + 1]; + } + } + + int query(int left, int right) { + int sum = 0; + left += n; + right += n + 1; + while (left < right) { + if (left % 2 == 1) sum += tree[left++]; + if (right % 2 == 1) sum += tree[--right]; + left /= 2; + right /= 2; + } + return sum; + } +}; + +class NumArray { +private: + SegmentTree segTree; + +public: + NumArray(const vector& nums) : segTree(nums) {} + + int sumRange(int left, int right) { + return segTree.query(left, right); + } +}; +``` + +```javascript +class SegmentTree { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.n = nums.length; + this.tree = Array(this.n * 2).fill(0); + for (let i = 0; i < this.n; i++) { + this.tree[this.n + i] = nums[i]; + } + for (let i = this.n - 1; i > 0; i--) { + this.tree[i] = this.tree[2 * i] + this.tree[2 * i + 1]; + } + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + query(left, right) { + let sum = 0; + left += this.n; + right += this.n + 1; + while (left < right) { + if (left % 2 === 1) sum += this.tree[left++]; + if (right % 2 === 1) sum += this.tree[--right]; + left = Math.floor(left / 2); + right = Math.floor(right / 2); + } + return sum; + } +} + +class NumArray { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.segTree = new SegmentTree(nums); + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + sumRange(left, right) { + return this.segTree.query(left, right); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ for each $sumRange()$ query, $O(n)$ for building the Segment Tree. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/range-sum-query-mutable.md b/articles/range-sum-query-mutable.md new file mode 100644 index 000000000..358921e03 --- /dev/null +++ b/articles/range-sum-query-mutable.md @@ -0,0 +1,1513 @@ +## 1. Brute Force + +::tabs-start + +```python +class NumArray: + + def __init__(self, nums: List[int]): + self.nums = nums + + def update(self, index: int, val: int) -> None: + self.nums[index] = val + + def sumRange(self, left: int, right: int) -> int: + res = 0 + for i in range(left, right + 1): + res += self.nums[i] + return res +``` + +```java +public class NumArray { + private int[] nums; + + public NumArray(int[] nums) { + this.nums = nums; + } + + public void update(int index, int val) { + nums[index] = val; + } + + public int sumRange(int left, int right) { + int res = 0; + for (int i = left; i <= right; i++) { + res += nums[i]; + } + return res; + } +} +``` + +```cpp +class NumArray { +private: + vector nums; + +public: + NumArray(vector& nums) { + this->nums = nums; + } + + void update(int index, int val) { + nums[index] = val; + } + + int sumRange(int left, int right) { + int res = 0; + for (int i = left; i <= right; i++) { + res += nums[i]; + } + return res; + } +}; +``` + +```javascript +class NumArray { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.nums = nums; + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + update(index, val) { + this.nums[index] = val; + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + sumRange(left, right) { + let res = 0; + for (let i = left; i <= right; i++) { + res += this.nums[i]; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ for initializing the input array. + * $O(1)$ for each $update()$ function call. + * $O(n)$ for each $sumRange()$ function call. +* Space complexity: $O(n)$ + +--- + +## 2. Recursive Segment Tree + +::tabs-start + +```python +class SegmentTree: + def __init__(self, N, A): + self.n = N + while (self.n & (self.n - 1)) != 0: + self.n += 1 + self.tree = [0] * (2 * self.n) + self.build(0, 0, self.n - 1, A) + + def build(self, node, start, end, A): + if start == end: + if start < len(A): + self.tree[node] = A[start] + else: + mid = (start + end) // 2 + left = 2 * node + 1 + right = 2 * node + 2 + self.build(left, start, mid, A) + self.build(right, mid + 1, end, A) + self.tree[node] = self.tree[left] + self.tree[right] + + def update(self, i, val): + def _update(node, start, end, idx, value): + if start == end: + self.tree[node] = value + else: + mid = (start + end) // 2 + left = 2 * node + 1 + right = 2 * node + 2 + if idx <= mid: + _update(left, start, mid, idx, value) + else: + _update(right, mid + 1, end, idx, value) + self.tree[node] = self.tree[left] + self.tree[right] + _update(0, 0, self.n - 1, i, val) + + def query(self, l, r): + def _query(node, start, end, L, R): + if R < start or L > end: + return 0 + if L <= start and end <= R: + return self.tree[node] + mid = (start + end) // 2 + left = 2 * node + 1 + right = 2 * node + 2 + left_sum = _query(left, start, mid, L, R) + right_sum = _query(right, mid + 1, end, L, R) + return left_sum + right_sum + return _query(0, 0, self.n - 1, l, r) + +class NumArray: + + def __init__(self, nums: List[int]): + self.segTree = SegmentTree(len(nums), nums) + + def update(self, index: int, val: int) -> None: + self.segTree.update(index, val) + + def sumRange(self, left: int, right: int) -> int: + return self.segTree.query(left, right) +``` + +```java +class SegmentTree { + int n; + int[] tree; + + SegmentTree(int N, int[] A) { + this.n = N; + while (Integer.bitCount(n) != 1) { + n++; + } + tree = new int[2 * n]; + build(0, 0, n - 1, A); + } + + void build(int node, int start, int end, int[] A) { + if (start == end) { + tree[node] = (start < A.length) ? A[start] : 0; + return; + } + int mid = start + (end - start) / 2; + build(node * 2 + 1, start, mid, A); + build(node * 2 + 2, mid + 1, end, A); + tree[node] = tree[node * 2 + 1] + tree[node * 2 + 2]; + } + + void update(int idx, int val) { + update(0, 0, n - 1, idx, val); + } + + void update(int node, int start, int end, int idx, int val) { + if (start == end) { + tree[node] = val; + return; + } + int mid = start + (end - start) / 2; + if (idx <= mid) { + update(node * 2 + 1, start, mid, idx, val); + } else { + update(node * 2 + 2, mid + 1, end, idx, val); + } + tree[node] = tree[node * 2 + 1] + tree[node * 2 + 2]; + } + + int query(int l, int r) { + return query(0, 0, n - 1, l, r); + } + + int query(int node, int start, int end, int l, int r) { + if (start > r || end < l) { + return 0; + } + if (start >= l && end <= r) { + return tree[node]; + } + int mid = start + (end - start) / 2; + int leftSum = query(node * 2 + 1, start, mid, l, r); + int rightSum = query(node * 2 + 2, mid + 1, end, l, r); + return leftSum + rightSum; + } +} + +public class NumArray { + private SegmentTree segTree; + + public NumArray(int[] nums) { + this.segTree = new SegmentTree(nums.length, nums); + } + + public void update(int index, int val) { + this.segTree.update(index, val); + } + + public int sumRange(int left, int right) { + return this.segTree.query(left, right); + } +} +``` + +```cpp +class SegmentTree { +public: + int n; + vector tree; + + SegmentTree(int N, vector& A) { + this->n = N; + while (__builtin_popcount(n) != 1) { + n++; + } + tree.resize(2 * n); + build(0, 0, n - 1, A); + } + + void build(int node, int start, int end, vector& A) { + if (start == end) { + tree[node] = (start < A.size()) ? A[start] : 0; + } else { + int mid = (start + end) / 2; + build(2 * node + 1, start, mid, A); + build(2 * node + 2, mid + 1, end, A); + tree[node] = tree[2 * node + 1] + tree[2 * node + 2]; + } + } + + void update(int node, int start, int end, int idx, int val) { + if (start == end) { + tree[node] = val; + } else { + int mid = (start + end) / 2; + if (idx <= mid) { + update(2 * node + 1, start, mid, idx, val); + } else { + update(2 * node + 2, mid + 1, end, idx, val); + } + tree[node] = tree[2 * node + 1] + tree[2 * node + 2]; + } + } + + int query(int node, int start, int end, int l, int r) { + if (r < start || end < l) { + return 0; + } + if (l <= start && end <= r) { + return tree[node]; + } + int mid = (start + end) / 2; + int leftSum = query(2 * node + 1, start, mid, l, r); + int rightSum = query(2 * node + 2, mid + 1, end, l, r); + return leftSum + rightSum; + } + + void update(int idx, int val) { + update(0, 0, n - 1, idx, val); + } + + int query(int l, int r) { + return query(0, 0, n - 1, l, r); + } +}; + +class NumArray { + SegmentTree* segTree; + +public: + NumArray(vector& nums) { + this->segTree = new SegmentTree(nums.size(), nums); + } + + void update(int index, int val) { + this->segTree->update(index, val); + } + + int sumRange(int left, int right) { + return this->segTree->query(left, right); + } +}; +``` + +```javascript +class SegmentTree { + /** + * @constructor + * @param {number} N + * @param {number[]} A + */ + constructor(N, A) { + this.n = N; + while ((this.n & (this.n - 1)) !== 0) { + this.n++; + } + this.tree = new Int32Array(2 * this.n); + this.build(0, 0, this.n - 1, A); + } + + /** + * @param {number} node + * @param {number} start + * @param {number} end + * @param {number[]} A + */ + build(node, start, end, A) { + if (start === end) { + this.tree[node] = start < A.length ? A[start] : 0; + } else { + const mid = Math.floor((start + end) / 2); + this.build(2 * node + 1, start, mid, A); + this.build(2 * node + 2, mid + 1, end, A); + this.tree[node] = this.tree[2 * node + 1] + this.tree[2 * node + 2]; + } + } + + /** + * @param {number} node + * @param {number} start + * @param {number} end + * @param {number} idx + * @param {number} val + * @return {void} + */ + _update(node, start, end, idx, val) { + if (start === end) { + this.tree[node] = val; + } else { + const mid = Math.floor((start + end) / 2); + if (idx <= mid) { + this._update(2 * node + 1, start, mid, idx, val); + } else { + this._update(2 * node + 2, mid + 1, end, idx, val); + } + this.tree[node] = this.tree[2 * node + 1] + this.tree[2 * node + 2]; + } + } + + /** + * @param {number} node + * @param {number} start + * @param {number} end + * @param {number} l + * @param {number} r + * @return {number} + */ + _query(node, start, end, l, r) { + if (r < start || end < l) { + return 0; + } + if (l <= start && end <= r) { + return this.tree[node]; + } + const mid = Math.floor((start + end) / 2); + const leftSum = this._query(2 * node + 1, start, mid, l, r); + const rightSum = this._query(2 * node + 2, mid + 1, end, l, r); + return leftSum + rightSum; + } + + /** + * @param {number} idx + * @param {number} val + * @return {void} + */ + update(idx, val) { + this._update(0, 0, this.n - 1, idx, val); + } + + /** + * @param {number} l + * @param {number} r + * @return {number} + */ + query(l, r) { + return this._query(0, 0, this.n - 1, l, r); + } +} + +class NumArray { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.segTree = new SegmentTree(nums.length, nums); + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + update(index, val) { + this.segTree.update(index, val); + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + sumRange(left, right) { + return this.segTree.query(left, right); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ for initializing the input array. + * $O(\log n)$ for each $update()$ function call. + * $O(\log n)$ for each $sumRange()$ function call. +* Space complexity: $O(n)$ + +--- + +## 3. Iterative Segement Tree + +::tabs-start + +```python +class SegmentTree: + def __init__(self, N, A): + self.n = N + while (self.n & (self.n - 1)) != 0: + self.n += 1 + self.build(N, A) + + def build(self, N, A): + self.tree = [0] * (2 * self.n) + for i in range(N): + self.tree[self.n + i] = A[i] + for i in range(self.n - 1, 0, -1): + self.tree[i] = self.tree[i << 1] + self.tree[i << 1 | 1] + + def update(self, i, val): + self.tree[self.n + i] = val + j = (self.n + i) >> 1 + while j >= 1: + self.tree[j] = self.tree[j << 1] + self.tree[j << 1 | 1] + j >>= 1 + + def query(self, l, r): + res = 0 + l += self.n + r += self.n + 1 + while l < r: + if l & 1: + res += self.tree[l] + l += 1 + if r & 1: + r -= 1 + res += self.tree[r] + l >>= 1 + r >>= 1 + return res + +class NumArray: + + def __init__(self, nums: List[int]): + self.segTree = SegmentTree(len(nums), nums) + + def update(self, index: int, val: int) -> None: + self.segTree.update(index, val) + + def sumRange(self, left: int, right: int) -> int: + return self.segTree.query(left, right) +``` + +```java +class SegmentTree { + int n; + int[] tree; + + SegmentTree(int N, int[] A) { + this.n = N; + while (Integer.bitCount(n) != 1) { + n++; + } + build(N, A); + } + + void build(int N, int[] A) { + tree = new int[2 * n]; + for (int i = 0; i < N; i++) { + tree[n + i] = A[i]; + } + for (int i = n - 1; i > 0; --i) { + tree[i] = tree[i << 1] + tree[i << 1 | 1]; + } + } + + void update(int i, int val) { + tree[n + i] = val; + for (int j = (n + i) >> 1; j >= 1; j >>= 1) { + tree[j] = tree[j << 1] + tree[j << 1 | 1]; + } + } + + int query(int l, int r) { + int res = 0; + for (l += n, r += n + 1; l < r; l >>= 1, r >>= 1) { + if ((l & 1) == 1) res += tree[l++]; + if ((r & 1) == 1) res += tree[--r]; + } + return res; + } +} + +public class NumArray { + private int[] nums; + private SegmentTree segTree; + + public NumArray(int[] nums) { + this.segTree = new SegmentTree(nums.length, nums); + } + + public void update(int index, int val) { + this.segTree.update(index, val); + } + + public int sumRange(int left, int right) { + return this.segTree.query(left, right); + } +} +``` + +```cpp +class SegmentTree { +public: + int n; + vector tree; + + SegmentTree(int N, vector& A) { + this->n = N; + while (__builtin_popcount(n) != 1) { + n++; + } + build(N, A); + } + + void build(int N, vector& A) { + tree.resize(2 * n); + for (int i = 0; i < N; i++) { + tree[n + i] = A[i]; + } + for (int i = n - 1; i > 0; --i) { + tree[i] = tree[i << 1] + tree[i << 1 | 1]; + } + } + + void update(int i, int val) { + tree[n + i] = val; + for (int j = (n + i) >> 1; j >= 1; j >>= 1) { + tree[j] = tree[j << 1] + tree[j << 1 | 1]; + } + } + + int query(int l, int r) { + int res = 0; + for (l += n, r += n + 1; l < r; l >>= 1, r >>= 1) { + if (l & 1) res += tree[l++]; + if (r & 1) res += tree[--r]; + } + return res; + } +}; + +class NumArray { + SegmentTree* segTree; + +public: + NumArray(vector& nums) { + this->segTree = new SegmentTree(nums.size(), nums); + } + + void update(int index, int val) { + this->segTree->update(index, val); + } + + int sumRange(int left, int right) { + return this->segTree->query(left, right); + } +}; +``` + +```javascript +class SegmentTree { + /** + * @constructor + * @param {number} N + * @param {number[]} A + */ + constructor(N, A) { + this.n = N; + while ((this.n & (this.n - 1)) !== 0) { + this.n++; + } + this.build(N, A); + } + + /** + * @param {number} N + * @param {number[]} A + * @return {void} + */ + build(N, A) { + this.tree = new Int32Array(2 * this.n); + for (let i = 0; i < N; i++) { + this.tree[this.n + i] = A[i]; + } + for (let i = this.n - 1; i > 0; i--) { + this.tree[i] = this.tree[i << 1] + this.tree[i << 1 | 1]; + } + } + + /** + * @param {number} i + * @param {number} val + * @return {void} + */ + update(i, val) { + this.tree[this.n + i] = val; + for (let j = (this.n + i) >> 1; j >= 1; j >>= 1) { + this.tree[j] = this.tree[j << 1] + this.tree[j << 1 | 1]; + } + } + + /** + * @param {number} l + * @param {number} r + * @return {number} + */ + query(l, r) { + let res = 0; + l += this.n; + r += this.n + 1; + + while (l < r) { + if (l & 1) res += this.tree[l++]; + if (r & 1) res += this.tree[--r]; + l >>= 1; + r >>= 1; + } + + return res; + } +} + +class NumArray { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.segTree = new SegmentTree(nums.length, nums); + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + update(index, val) { + this.segTree.update(index, val); + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + sumRange(left, right) { + return this.segTree.query(left, right); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ for initializing the input array. + * $O(\log n)$ for each $update()$ function call. + * $O(\log n)$ for each $sumRange()$ function call. +* Space complexity: $O(n)$ + +--- + +## 4. Square Root Decomposition (Update Optimized) + +::tabs-start + +```python +class SqrtDecomposition: + + def __init__(self, nums): + self.A = nums[:] + self.n = len(nums) + self.block_size = int(self.n ** 0.5) + 1 + self.blocks = [0] * self.block_size + + for i in range(self.n): + self.blocks[i // self.block_size] += self.A[i] + + def query(self, left, right): + total_sum = 0 + while left <= right and left % self.block_size != 0: + total_sum += self.A[left] + left += 1 + + while left + self.block_size - 1 <= right: + total_sum += self.blocks[left // self.block_size] + left += self.block_size + + while left <= right: + total_sum += self.A[left] + left += 1 + + return total_sum + + def update(self, index, val): + block_index = index // self.block_size + self.blocks[block_index] += val - self.A[index] + self.A[index] = val + +class NumArray: + + def __init__(self, nums: List[int]): + self.sq = SqrtDecomposition(nums) + + def update(self, index: int, val: int) -> None: + self.sq.update(index, val) + + def sumRange(self, left: int, right: int) -> int: + return self.sq.query(left, right) +``` + +```java +class SqrtDecomposition { + private int[] nums, blocks; + private int blockSize, n; + + public SqrtDecomposition(int[] nums) { + this.nums = nums.clone(); + this.n = nums.length; + this.blockSize = (int) Math.sqrt(n) + 1; + this.blocks = new int[blockSize]; + + for (int i = 0; i < n; i++) { + blocks[i / blockSize] += nums[i]; + } + } + + public int query(int left, int right) { + int totalSum = 0; + while (left <= right && left % blockSize != 0) { + totalSum += nums[left++]; + } + + while (left + blockSize - 1 <= right) { + totalSum += blocks[left / blockSize]; + left += blockSize; + } + + while (left <= right) { + totalSum += nums[left++]; + } + + return totalSum; + } + + public void update(int index, int val) { + int blockIndex = index / blockSize; + blocks[blockIndex] += val - nums[index]; + nums[index] = val; + } +} + +public class NumArray { + private SqrtDecomposition sq; + + public NumArray(int[] nums) { + sq = new SqrtDecomposition(nums); + } + + public void update(int index, int val) { + sq.update(index, val); + } + + public int sumRange(int left, int right) { + return sq.query(left, right); + } +} +``` + +```cpp +class SqrtDecomposition { +private: + vector nums, blocks; + int blockSize, n; + +public: + SqrtDecomposition(vector& nums) { + this->nums = nums; + this->n = nums.size(); + this->blockSize = sqrt(n) + 1; + this->blocks.resize(blockSize, 0); + + for (int i = 0; i < n; i++) { + blocks[i / blockSize] += nums[i]; + } + } + + int query(int left, int right) { + int totalSum = 0; + while (left <= right && left % blockSize != 0) { + totalSum += nums[left++]; + } + + while (left + blockSize - 1 <= right) { + totalSum += blocks[left / blockSize]; + left += blockSize; + } + + while (left <= right) { + totalSum += nums[left++]; + } + + return totalSum; + } + + void update(int index, int val) { + int blockIndex = index / blockSize; + blocks[blockIndex] += val - nums[index]; + nums[index] = val; + } +}; + +class NumArray { +private: + SqrtDecomposition* sq; + +public: + NumArray(vector& nums) { + sq = new SqrtDecomposition(nums); + } + + void update(int index, int val) { + sq->update(index, val); + } + + int sumRange(int left, int right) { + return sq->query(left, right); + } +}; +``` + +```javascript +class SqrtDecomposition { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.nums = [...nums]; + this.n = nums.length; + this.blockSize = Math.floor(Math.sqrt(this.n)) + 1; + this.blocks = new Array(this.blockSize).fill(0); + + for (let i = 0; i < this.n; i++) { + this.blocks[Math.floor(i / this.blockSize)] += nums[i]; + } + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + query(left, right) { + let totalSum = 0; + while (left <= right && left % this.blockSize !== 0) { + totalSum += this.nums[left++]; + } + + while (left + this.blockSize - 1 <= right) { + totalSum += this.blocks[Math.floor(left / this.blockSize)]; + left += this.blockSize; + } + + while (left <= right) { + totalSum += this.nums[left++]; + } + + return totalSum; + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + update(index, val) { + let blockIndex = Math.floor(index / this.blockSize); + this.blocks[blockIndex] += val - this.nums[index]; + this.nums[index] = val; + } +} + +class NumArray { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.sq = new SqrtDecomposition(nums); + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + update(index, val) { + this.sq.update(index, val); + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + sumRange(left, right) { + return this.sq.query(left, right); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ for initializing the input array. + * $O(1)$ for each $update()$ function call. + * $O(\sqrt {n})$ for each $sumRange()$ function call. +* Space complexity: $O(n)$ + +--- + +## 5. Square Root Decomposition (Query Optimized) + +::tabs-start + +```python +class SqrtDecomposition: + + def __init__(self, nums): + self.A = nums[:] + self.prefix_sums = nums[:] + self.n = len(nums) + self.block_size = int(self.n ** 0.5) + 1 + + for i in range(1, self.n): + if i % self.block_size != 0: + self.prefix_sums[i] += self.prefix_sums[i - 1] + + def query(self, left, right): + total_sum = -(self.prefix_sums[left - 1] if left % self.block_size != 0 else 0) + + while left // self.block_size < right // self.block_size: + block_end = min(self.n - 1, (left // self.block_size) * self.block_size + self.block_size - 1) + total_sum += self.prefix_sums[block_end] + left = block_end + 1 + + total_sum += self.prefix_sums[right] + return total_sum + + total_sum += self.prefix_sums[right] + return total_sum + + def update(self, index, val): + diff = val - self.A[index] + self.A[index] = val + + block_end = min(self.n - 1, (index // self.block_size) * self.block_size + self.block_size - 1) + for i in range(index, block_end + 1): + self.prefix_sums[i] += diff + +class NumArray: + + def __init__(self, nums: List[int]): + self.sq = SqrtDecomposition(nums) + + def update(self, index: int, val: int) -> None: + self.sq.update(index, val) + + def sumRange(self, left: int, right: int) -> int: + return self.sq.query(left, right) +``` + +```java +class SqrtDecomposition { + private int[] nums, prefixSums; + private int blockSize, n; + + public SqrtDecomposition(int[] nums) { + this.nums = nums.clone(); + this.n = nums.length; + this.blockSize = (int) Math.sqrt(n) + 1; + this.prefixSums = nums.clone(); + + for (int i = 1; i < n; i++) { + if (i % blockSize != 0) { + prefixSums[i] += prefixSums[i - 1]; + } + } + } + + public int query(int left, int right) { + int totalSum = (left % blockSize != 0) ? -prefixSums[left - 1] : 0; + + while (left / blockSize < right / blockSize) { + int blockEnd = Math.min(n - 1, (left / blockSize) * blockSize + blockSize - 1); + totalSum += prefixSums[blockEnd]; + left = blockEnd + 1; + } + + totalSum += prefixSums[right]; + return totalSum; + } + + public void update(int index, int val) { + int diff = val - nums[index]; + nums[index] = val; + + int blockEnd = Math.min(n - 1, (index / blockSize) * blockSize + blockSize - 1); + for (int i = index; i <= blockEnd; i++) { + prefixSums[i] += diff; + } + } +} + +public class NumArray { + private SqrtDecomposition sq; + + public NumArray(int[] nums) { + sq = new SqrtDecomposition(nums); + } + + public void update(int index, int val) { + sq.update(index, val); + } + + public int sumRange(int left, int right) { + return sq.query(left, right); + } +} +``` + +```cpp +class SqrtDecomposition { +private: + vector nums, prefixSums; + int blockSize, n; + +public: + SqrtDecomposition(vector& nums) { + this->nums = nums; + this->n = nums.size(); + this->blockSize = sqrt(n) + 1; + this->prefixSums = nums; + + for (int i = 1; i < n; i++) { + if (i % blockSize != 0) { + prefixSums[i] += prefixSums[i - 1]; + } + } + } + + int query(int left, int right) { + int totalSum = (left % blockSize != 0) ? -prefixSums[left - 1] : 0; + + while (left / blockSize < right / blockSize) { + int blockEnd = min(n - 1, (left / blockSize) * blockSize + blockSize - 1); + totalSum += prefixSums[blockEnd]; + left = blockEnd + 1; + } + + totalSum += prefixSums[right]; + return totalSum; + } + + void update(int index, int val) { + int diff = val - nums[index]; + nums[index] = val; + + int blockEnd = min(n - 1, (index / blockSize) * blockSize + blockSize - 1); + for (int i = index; i <= blockEnd; i++) { + prefixSums[i] += diff; + } + } +}; + +class NumArray { +private: + SqrtDecomposition* sq; + +public: + NumArray(vector& nums) { + sq = new SqrtDecomposition(nums); + } + + void update(int index, int val) { + sq->update(index, val); + } + + int sumRange(int left, int right) { + return sq->query(left, right); + } +}; +``` + +```javascript +class SqrtDecomposition { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.nums = [...nums]; + this.n = nums.length; + this.blockSize = Math.floor(Math.sqrt(this.n)) + 1; + this.prefixSums = [...nums]; + + for (let i = 1; i < this.n; i++) { + if (i % this.blockSize !== 0) { + this.prefixSums[i] += this.prefixSums[i - 1]; + } + } + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + query(left, right) { + let totalSum = (left % this.blockSize !== 0) ? -this.prefixSums[left - 1] : 0; + + while (Math.floor(left / this.blockSize) < Math.floor(right / this.blockSize)) { + const blockEnd = Math.min(this.n - 1, Math.floor(left / this.blockSize) * this.blockSize + this.blockSize - 1); + totalSum += this.prefixSums[blockEnd]; + left = blockEnd + 1; + } + + totalSum += this.prefixSums[right]; + return totalSum; + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + update(index, val) { + const diff = val - this.nums[index]; + this.nums[index] = val; + + const blockEnd = Math.min(this.n - 1, Math.floor(index / this.blockSize) * this.blockSize + this.blockSize - 1); + for (let i = index; i <= blockEnd; i++) { + this.prefixSums[i] += diff; + } + } +} + +class NumArray { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.sq = new SqrtDecomposition(nums); + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + update(index, val) { + this.sq.update(index, val); + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + sumRange(left, right) { + return this.sq.query(left, right); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ for initializing the input array. + * $O(\sqrt {n})$ for each $update()$ function call. + * $O(\sqrt {n})$ for each $sumRange()$ function call. +* Space complexity: $O(n)$ + +--- + +## 6. Binary Indexed Tree (Fenwick Tree) + +::tabs-start + +```python +class BIT: + def __init__(self, nums): + self.n = len(nums) + 1 + self.nums = [0] * self.n + self.tree = [0] * self.n + for i in range(self.n - 1): + self.update(i, nums[i]) + + def update(self, index, val): + index += 1 + diff = val - self.nums[index] + self.nums[index] = val + while index < self.n: + self.tree[index] += diff + index += index & -index + + def prefix_sum(self, index): + total_sum = 0 + while index > 0: + total_sum += self.tree[index] + index -= index & -index + return total_sum + + def query(self, left, right): + return self.prefix_sum(right + 1) - self.prefix_sum(left) + + +class NumArray: + def __init__(self, nums: List[int]): + self.bit = BIT(nums) + + def update(self, index: int, val: int) -> None: + self.bit.update(index, val) + + def sumRange(self, left: int, right: int) -> int: + return self.bit.query(left, right) +``` + +```java +class BIT { + private int[] nums; + private int[] tree; + private int n; + + public BIT(int[] nums) { + this.n = nums.length + 1; + this.nums = new int[n]; + this.tree = new int[n]; + for (int i = 0; i < n - 1; i++) { + update(i, nums[i]); + } + } + + public void update(int index, int val) { + index++; + int diff = val - nums[index]; + nums[index] = val; + while (index < n) { + tree[index] += diff; + index += index & -index; + } + } + + public int prefixSum(int index) { + int totalSum = 0; + while (index > 0) { + totalSum += tree[index]; + index -= index & -index; + } + return totalSum; + } + + public int query(int left, int right) { + return prefixSum(right + 1) - prefixSum(left); + } +} + +public class NumArray { + private BIT bit; + + public NumArray(int[] nums) { + this.bit = new BIT(nums); + } + + public void update(int index, int val) { + bit.update(index, val); + } + + public int sumRange(int left, int right) { + return bit.query(left, right); + } +} +``` + +```cpp +class BIT { + vector nums; + vector tree; + int n; + +public: + BIT(vector& nums) { + n = nums.size() + 1; + this->nums.resize(n); + tree.resize(n, 0); + for (int i = 0; i < nums.size(); i++) { + update(i, nums[i]); + } + } + + void update(int index, int val) { + index++; + int diff = val - nums[index]; + nums[index] = val; + while (index < n) { + tree[index] += diff; + index += index & -index; + } + } + + int prefixSum(int index) { + int totalSum = 0; + while (index > 0) { + totalSum += tree[index]; + index -= index & -index; + } + return totalSum; + } + + int query(int left, int right) { + return prefixSum(right + 1) - prefixSum(left); + } +}; + +class NumArray { + BIT bit; + +public: + NumArray(vector& nums) : bit(nums) {} + + void update(int index, int val) { + bit.update(index, val); + } + + int sumRange(int left, int right) { + return bit.query(left, right); + } +}; +``` + +```javascript +class BIT { + /** + * @constructor + * @param {number[]} nums + */ + constructor(nums) { + this.n = nums.length + 1; + this.tree = new Int32Array(this.n); + this.nums = new Int32Array(this.n); + for (let i = 0; i < this.n - 1; i++) { + this.update(i, nums[i]); + } + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + update(index, val) { + index++; + const diff = val - this.nums[index]; + this.nums[index] = val; + while (index < this.n) { + this.tree[index] += diff; + index += index & -index; + } + } + + /** + * @param {number} index + * @return {number} + */ + prefixSum(index) { + let totalSum = 0; + while (index > 0) { + totalSum += this.tree[index]; + index -= index & -index; + } + return totalSum; + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + query(left, right) { + return this.prefixSum(right + 1) - this.prefixSum(left); + } +} + +class NumArray { + /** + * @param {number[]} nums + */ + constructor(nums) { + this.bit = new BIT(nums); + } + + /** + * @param {number} index + * @param {number} val + * @return {void} + */ + update(index, val) { + this.bit.update(index, val); + } + + /** + * @param {number} left + * @param {number} right + * @return {number} + */ + sumRange(left, right) { + return this.bit.query(left, right); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ for initializing the input array. + * $O(\log n)$ for each $update()$ function call. + * $O(\log n)$ for each $sumRange()$ function call. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/rearrange-array-elements-by-sign.md b/articles/rearrange-array-elements-by-sign.md new file mode 100644 index 000000000..a634ed75b --- /dev/null +++ b/articles/rearrange-array-elements-by-sign.md @@ -0,0 +1,310 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def rearrangeArray(self, nums: List[int]) -> List[int]: + n = len(nums) + for i in range(n): + if ((i % 2 == 0 and nums[i] > 0) or + (i % 2 == 1 and nums[i] < 0)): + continue + + j = i + 1 + while j < n and ((nums[j] > 0) == (nums[i] > 0)): + j += 1 + + tmp = nums[j] + while j > i: + nums[j] = nums[j - 1] + j -= 1 + nums[i] = tmp + return nums +``` + +```java +public class Solution { + public int[] rearrangeArray(int[] nums) { + int n = nums.length; + for (int i = 0; i < n; i++) { + if ((i % 2 == 0 && nums[i] > 0) || (i % 2 == 1 && nums[i] < 0)) { + continue; + } + + int j = i + 1; + while (j < n && ((nums[j] > 0) == (nums[i] > 0))) { + j++; + } + + int temp = nums[j]; + while (j > i) { + nums[j] = nums[j - 1]; + j--; + } + nums[i] = temp; + } + return nums; + } +} +``` + +```cpp +class Solution { +public: + vector rearrangeArray(vector& nums) { + int n = nums.size(); + for (int i = 0; i < n; i++) { + if ((i % 2 == 0 && nums[i] > 0) || (i % 2 == 1 && nums[i] < 0)) { + continue; + } + + int j = i + 1; + while (j < n && ((nums[j] > 0) == (nums[i] > 0))) { + j++; + } + + int temp = nums[j]; + while (j > i) { + nums[j] = nums[j - 1]; + j--; + } + nums[i] = temp; + } + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + rearrangeArray(nums) { + let n = nums.length; + for (let i = 0; i < n; i++) { + if ((i % 2 === 0 && nums[i] > 0) || (i % 2 === 1 && nums[i] < 0)) { + continue; + } + + let j = i + 1; + while (j < n && ((nums[j] > 0) === (nums[i] > 0))) { + j++; + } + + let temp = nums[j]; + while (j > i) { + nums[j] = nums[j - 1]; + j--; + } + nums[i] = temp; + } + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Group Numbers Into Two Arrays + +::tabs-start + +```python +class Solution: + def rearrangeArray(self, nums: List[int]) -> List[int]: + pos, neg = [], [] + for num in nums: + if num > 0: + pos.append(num) + else: + neg.append(num) + + i = 0 + while 2 * i < len(nums): + nums[2 * i] = pos[i] + nums[2 * i + 1] = neg[i] + i += 1 + return nums +``` + +```java +public class Solution { + public int[] rearrangeArray(int[] nums) { + List pos = new ArrayList<>(); + List neg = new ArrayList<>(); + for (int num : nums) { + if (num > 0) { + pos.add(num); + } else { + neg.add(num); + } + } + + int i = 0; + while (2 * i < nums.length) { + nums[2 * i] = pos.get(i); + nums[2 * i + 1] = neg.get(i); + i++; + } + return nums; + } +} +``` + +```cpp +class Solution { +public: + vector rearrangeArray(vector& nums) { + vector pos, neg; + for (int num : nums) { + if (num > 0) { + pos.push_back(num); + } else { + neg.push_back(num); + } + } + + int i = 0; + while (2 * i < nums.size()) { + nums[2 * i] = pos[i]; + nums[2 * i + 1] = neg[i]; + i++; + } + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + rearrangeArray(nums) { + const pos = [], neg = []; + for (const num of nums) { + if (num > 0) { + pos.push(num); + } else { + neg.push(num); + } + } + + let i = 0; + while (2 * i < nums.length) { + nums[2 * i] = pos[i]; + nums[2 * i + 1] = neg[i]; + i++; + } + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Two Pointers + +::tabs-start + +```python +class Solution: + def rearrangeArray(self, nums: List[int]) -> List[int]: + i, j = 0, 1 + res = [0] * len(nums) + for k in range(len(nums)): + if nums[k] > 0: + res[i] = nums[k] + i += 2 + else: + res[j] = nums[k] + j += 2 + return res +``` + +```java +public class Solution { + public int[] rearrangeArray(int[] nums) { + int i = 0, j = 1; + int[] res = new int[nums.length]; + for (int k = 0; k < nums.length; k++) { + if (nums[k] > 0) { + res[i] = nums[k]; + i += 2; + } else { + res[j] = nums[k]; + j += 2; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector rearrangeArray(vector& nums) { + int i = 0, j = 1; + vector res(nums.size()); + for (int k = 0; k < nums.size(); k++) { + if (nums[k] > 0) { + res[i] = nums[k]; + i += 2; + } else { + res[j] = nums[k]; + j += 2; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + rearrangeArray(nums) { + let i = 0, j = 1; + const res = new Array(nums.length); + for (let k = 0; k < nums.length; k++) { + if (nums[k] > 0) { + res[i] = nums[k]; + i += 2; + } else { + res[j] = nums[k]; + j += 2; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for the output array. \ No newline at end of file diff --git a/articles/reconstruct-flight-path.md b/articles/reconstruct-flight-path.md new file mode 100644 index 000000000..26ca58784 --- /dev/null +++ b/articles/reconstruct-flight-path.md @@ -0,0 +1,817 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def findItinerary(self, tickets: List[List[str]]) -> List[str]: + adj = {src: [] for src, dst in tickets} + tickets.sort() + for src, dst in tickets: + adj[src].append(dst) + + res = ["JFK"] + def dfs(src): + if len(res) == len(tickets) + 1: + return True + if src not in adj: + return False + + temp = list(adj[src]) + for i, v in enumerate(temp): + adj[src].pop(i) + res.append(v) + if dfs(v): return True + adj[src].insert(i, v) + res.pop() + return False + + dfs("JFK") + return res +``` + +```java +public class Solution { + public List findItinerary(List> tickets) { + Map> adj = new HashMap<>(); + for (List ticket : tickets) { + adj.putIfAbsent(ticket.get(0), new ArrayList<>()); + } + + tickets.sort((a, b) -> a.get(1).compareTo(b.get(1))); + for (List ticket : tickets) { + adj.get(ticket.get(0)).add(ticket.get(1)); + } + + List res = new ArrayList<>(); + res.add("JFK"); + + if (dfs("JFK", res, adj, tickets.size() + 1)) { + return res; + } + return new ArrayList<>(); + } + + private boolean dfs(String src, List res, + Map> adj, int targetLen) { + if (res.size() == targetLen) { + return true; + } + + if (!adj.containsKey(src)) { + return false; + } + + List temp = new ArrayList<>(adj.get(src)); + for (int i = 0; i < temp.size(); i++) { + String v = temp.get(i); + adj.get(src).remove(i); + res.add(v); + if (dfs(v, res, adj, targetLen)) return true; + adj.get(src).add(i, v); + res.remove(res.size() - 1); + } + return false; + } +} +``` + +```cpp +class Solution { +public: + vector findItinerary(vector>& tickets) { + unordered_map> adj; + for (auto& ticket : tickets) { + adj[ticket[0]]; + } + + sort(tickets.begin(), tickets.end()); + for (auto& ticket : tickets) { + adj[ticket[0]].push_back(ticket[1]); + } + + vector res = {"JFK"}; + dfs("JFK", res, adj, tickets.size() + 1); + return res; + } + +private: + bool dfs(const string& src, vector& res, + unordered_map>& adj, int targetLen) { + if (res.size() == targetLen) { + return true; + } + + if (adj.find(src) == adj.end()) { + return false; + } + + vector temp = adj[src]; + for (int i = 0; i < temp.size(); ++i) { + string v = temp[i]; + adj[src].erase(adj[src].begin() + i); + res.push_back(v); + if (dfs(v, res, adj, targetLen)) return true; + adj[src].insert(adj[src].begin() + i, v); + res.pop_back(); + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[][]} tickets + * @return {string[]} + */ + findItinerary(tickets) { + const adj = {}; + for (const [src, dst] of tickets) { + if (!adj[src]) adj[src] = []; + } + + tickets.sort(); + for (const [src, dst] of tickets) { + adj[src].push(dst); + } + + const res = ["JFK"]; + const dfs = (src) => { + if (res.length === tickets.length + 1) return true; + if (!adj[src]) return false; + + const temp = [...adj[src]]; + for (let i = 0; i < temp.length; i++) { + const v = temp[i]; + adj[src].splice(i, 1); + res.push(v); + if (dfs(v)) return true; + res.pop(); + adj[src].splice(i, 0, v); + } + return false; + } + + dfs("JFK"); + return res; + } +} +``` + +```csharp +public class Solution { + public List FindItinerary(List> tickets) { + var adj = new Dictionary>(); + foreach (var ticket in tickets) { + if (!adj.ContainsKey(ticket[0])) { + adj[ticket[0]] = new List(); + } + } + + tickets.Sort((a, b) => string.Compare(a[1], b[1])); + foreach (var ticket in tickets) { + adj[ticket[0]].Add(ticket[1]); + } + + var res = new List { "JFK" }; + Dfs("JFK", res, adj, tickets.Count + 1); + return res; + } + + private bool Dfs(string src, List res, + Dictionary> adj, int targetLen) { + if (res.Count == targetLen) return true; + if (!adj.ContainsKey(src)) return false; + + var temp = new List(adj[src]); + for (int i = 0; i < temp.Count; i++) { + var v = temp[i]; + adj[src].RemoveAt(i); + res.Add(v); + if (Dfs(v, res, adj, targetLen)) return true; + res.RemoveAt(res.Count - 1); + adj[src].Insert(i, v); + } + return false; + } +} +``` + +```go +func findItinerary(tickets [][]string) []string { + adj := make(map[string][]string) + for _, ticket := range tickets { + adj[ticket[0]] = append(adj[ticket[0]], ticket[1]) + } + + for src := range adj { + sort.Strings(adj[src]) + } + + res := []string{"JFK"} + + var dfs func(string) bool + dfs = func(src string) bool { + if len(res) == len(tickets) + 1 { + return true + } + + destinations, exists := adj[src] + if !exists { + return false + } + + temp := make([]string, len(destinations)) + copy(temp, destinations) + + for i, v := range temp { + adj[src] = append(adj[src][:i], adj[src][i+1:]...) + res = append(res, v) + + if dfs(v) { + return true + } + + adj[src] = append(adj[src][:i], append([]string{v}, adj[src][i:]...)...) + res = res[:len(res)-1] + } + return false + } + + dfs("JFK") + return res +} +``` + +```kotlin +class Solution { + fun findItinerary(tickets: List>): List { + val adj = HashMap>() + + tickets.sortedBy { it[1] }.forEach { (src, dst) -> + adj.getOrPut(src) { mutableListOf() }.add(dst) + } + + val res = mutableListOf("JFK") + + fun dfs(src: String): Boolean { + if (res.size == tickets.size + 1) { + return true + } + + val destinations = adj[src] ?: return false + + for (i in destinations.indices) { + val v = destinations.removeAt(i) + res.add(v) + + if (dfs(v)) { + return true + } + + destinations.add(i, v) + res.removeAt(res.lastIndex) + } + return false + } + + dfs("JFK") + return res + } +} +``` + +```swift +class Solution { + func findItinerary(_ tickets: [[String]]) -> [String] { + var adj = [String: [String]]() + for ticket in tickets { + adj[ticket[0], default: []].append(ticket[1]) + } + + for key in adj.keys { + adj[key]?.sort() + } + + var res = ["JFK"] + + func dfs(_ src: String) -> Bool { + if res.count == tickets.count + 1 { + return true + } + guard let destinations = adj[src] else { + return false + } + + var temp = destinations + for i in 0.. Where $E$ is the number of tickets (edges) and $V$ is the number of airports (vertices). + +--- + +## 2. Hierholzer's Algorithm (Recursion) + +::tabs-start + +```python +class Solution: + def findItinerary(self, tickets: List[List[str]]) -> List[str]: + adj = defaultdict(list) + for src, dst in sorted(tickets)[::-1]: + adj[src].append(dst) + + res = [] + def dfs(src): + while adj[src]: + dst = adj[src].pop() + dfs(dst) + res.append(src) + + dfs('JFK') + return res[::-1] +``` + +```java +public class Solution { + public List findItinerary(List> tickets) { + Map> adj = new HashMap<>(); + for (List ticket : tickets) { + String src = ticket.get(0); + String dst = ticket.get(1); + adj.computeIfAbsent(src, k -> new PriorityQueue<>()).offer(dst); + } + + List res = new ArrayList<>(); + dfs(adj, "JFK", res); + + Collections.reverse(res); + return res; + } + + private void dfs(Map> adj, + String src, List res) { + PriorityQueue queue = adj.get(src); + while (queue != null && !queue.isEmpty()) { + String dst = queue.poll(); + dfs(adj, dst, res); + } + res.add(src); + } +} +``` + +```cpp +class Solution { +public: + vector findItinerary(vector>& tickets) { + unordered_map> adj; + for (auto& ticket : tickets) { + adj[ticket[0]].push_back(ticket[1]); + } + for (auto& [src, dests] : adj) { + sort(dests.rbegin(), dests.rend()); + } + + vector res; + dfs("JFK", adj, res); + reverse(res.begin(), res.end()); + return res; + } + +private: + void dfs(const string& src, unordered_map>& adj, vector& res) { + while (!adj[src].empty()) { + string dst = adj[src].back(); + adj[src].pop_back(); + dfs(dst, adj, res); + } + res.push_back(src); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[][]} tickets + * @return {string[]} + */ + findItinerary(tickets) { + const adj = new Map(); + const res = []; + + tickets.sort().reverse().forEach(([src, dst]) => { + if (!adj.has(src)) adj.set(src, []); + adj.get(src).push(dst); + }); + + function dfs(src) { + while (adj.has(src) && adj.get(src).length > 0) { + const dst = adj.get(src).pop(); + dfs(dst); + } + res.push(src); + } + + dfs("JFK"); + return res.reverse(); + } +} +``` + +```csharp +public class Solution { + private Dictionary> adj; + private List res = new List(); + + public List FindItinerary(List> tickets) { + adj = new Dictionary>(); + var sortedTickets = tickets.OrderByDescending(t => t[1]).ToList(); + foreach (var ticket in sortedTickets) { + if (!adj.ContainsKey(ticket[0])) { + adj[ticket[0]] = new List(); + } + adj[ticket[0]].Add(ticket[1]); + } + + Dfs("JFK"); + res.Reverse(); + return res; + } + + private void Dfs(string src) { + while (adj.ContainsKey(src) && adj[src].Count > 0) { + var dst = adj[src][adj[src].Count - 1]; + adj[src].RemoveAt(adj[src].Count - 1); + Dfs(dst); + } + res.Add(src); + } +} +``` + +```go +func findItinerary(tickets [][]string) []string { + adj := make(map[string][]string) + + sort.Slice(tickets, func(i, j int) bool { + if tickets[i][0] == tickets[j][0] { + return tickets[i][1] > tickets[j][1] + } + return tickets[i][0] > tickets[j][0] + }) + + for _, ticket := range tickets { + src, dst := ticket[0], ticket[1] + adj[src] = append(adj[src], dst) + } + + res := make([]string, 0) + + var dfs func(string) + dfs = func(src string) { + for len(adj[src]) > 0 { + last := len(adj[src]) - 1 + dst := adj[src][last] + adj[src] = adj[src][:last] + dfs(dst) + } + res = append(res, src) + } + + dfs("JFK") + + for i := 0; i < len(res)/2; i++ { + res[i], res[len(res)-1-i] = res[len(res)-1-i], res[i] + } + + return res +} +``` + +```kotlin +class Solution { + fun findItinerary(tickets: List>): List { + val adj = HashMap>() + + tickets.sortedWith(compareBy({ it[0] }, { it[1] })) + .reversed() + .forEach { (src, dst) -> + adj.getOrPut(src) { mutableListOf() }.add(dst) + } + + val res = mutableListOf() + + fun dfs(src: String) { + while (adj[src]?.isNotEmpty() == true) { + val dst = adj[src]!!.removeAt(adj[src]!!.lastIndex) + dfs(dst) + } + res.add(src) + } + + dfs("JFK") + return res.reversed() + } +} +``` + +```swift +class Solution { + func findItinerary(_ tickets: [[String]]) -> [String] { + var adj = [String: [String]]() + for ticket in tickets.sorted(by: { $0[1] > $1[1] }) { + adj[ticket[0], default: []].append(ticket[1]) + } + + var res = [String]() + + func dfs(_ src: String) { + while let destinations = adj[src], !destinations.isEmpty { + let dst = adj[src]!.removeLast() + dfs(dst) + } + res.append(src) + } + + dfs("JFK") + return res.reversed() + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(E\log E)$ +* Space complexity: $O(E)$ + +> Where $E$ is the number of tickets (edges) and $V$ is the number of airports (vertices). + +--- + +## 3. Hierholzer's Algorithm (Iteration) + +::tabs-start + +```python +class Solution: + def findItinerary(self, tickets: List[List[str]]) -> List[str]: + adj = defaultdict(list) + for src, dst in sorted(tickets)[::-1]: + adj[src].append(dst) + + stack = ["JFK"] + res = [] + + while stack: + curr = stack[-1] + if not adj[curr]: + res.append(stack.pop()) + else: + stack.append(adj[curr].pop()) + + return res[::-1] +``` + +```java +public class Solution { + public List findItinerary(List> tickets) { + Map> adj = new HashMap<>(); + for (List ticket : tickets) { + adj.computeIfAbsent(ticket.get(0), + k -> new PriorityQueue<>()).add(ticket.get(1)); + } + + LinkedList res = new LinkedList<>(); + Stack stack = new Stack<>(); + stack.push("JFK"); + + while (!stack.isEmpty()) { + String curr = stack.peek(); + if (!adj.containsKey(curr) || adj.get(curr).isEmpty()) { + res.addFirst(stack.pop()); + } else { + stack.push(adj.get(curr).poll()); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findItinerary(vector>& tickets) { + unordered_map> adj; + for (const auto& ticket : tickets) { + adj[ticket[0]].push_back(ticket[1]); + } + for (auto& [src, destinations] : adj) { + sort(destinations.rbegin(), destinations.rend()); + } + + vector res; + stack stk; + stk.push("JFK"); + + while (!stk.empty()) { + string curr = stk.top(); + if (adj[curr].empty()) { + res.push_back(curr); + stk.pop(); + } else { + string next = adj[curr].back(); + adj[curr].pop_back(); + stk.push(next); + } + } + + reverse(res.begin(), res.end()); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[][]} tickets + * @return {string[]} + */ + findItinerary(tickets) { + const adj = new Map(); + tickets.sort().reverse().forEach(([src, dst]) => { + if (!adj.has(src)) adj.set(src, []); + adj.get(src).push(dst); + }); + + const res = []; + const stack = ["JFK"]; + + while (stack.length > 0) { + let curr = stack[stack.length - 1]; + if (!adj.has(curr) || adj.get(curr).length === 0) { + res.unshift(stack.pop()); + } else { + stack.push(adj.get(curr).pop()); + } + } + + return res; + } +} +``` + +```csharp +public class Solution { + public List FindItinerary(List> tickets) { + var adj = new Dictionary>(); + foreach (var ticket in tickets.OrderByDescending(t => t[1])) { + if (!adj.ContainsKey(ticket[0])) { + adj[ticket[0]] = new List(); + } + adj[ticket[0]].Add(ticket[1]); + } + + var res = new List(); + var stack = new Stack(); + stack.Push("JFK"); + + while (stack.Count > 0) { + var curr = stack.Peek(); + if (!adj.ContainsKey(curr) || adj[curr].Count == 0) { + res.Insert(0, stack.Pop()); + } else { + var next = adj[curr][adj[curr].Count - 1]; + adj[curr].RemoveAt(adj[curr].Count - 1); + stack.Push(next); + } + } + + return res; + } +} +``` + +```go +func findItinerary(tickets [][]string) []string { + adj := make(map[string][]string) + for _, ticket := range tickets { + src, dst := ticket[0], ticket[1] + adj[src] = append(adj[src], dst) + } + for src := range adj { + sort.Sort(sort.Reverse(sort.StringSlice(adj[src]))) + } + + stack := []string{"JFK"} + var res []string + + for len(stack) > 0 { + curr := stack[len(stack)-1] + if len(adj[curr]) == 0 { + res = append(res, stack[len(stack)-1]) + stack = stack[:len(stack)-1] + } else { + stack = append(stack, adj[curr][len(adj[curr])-1]) + adj[curr] = adj[curr][:len(adj[curr])-1] + } + } + + for i, j := 0, len(res)-1; i < j; i, j = i+1, j-1 { + res[i], res[j] = res[j], res[i] + } + return res +} +``` + +```kotlin +class Solution { + fun findItinerary(tickets: List>): List { + val adj = HashMap>() + for ((src, dst) in tickets.sortedWith( + compareByDescending> { it[0] }.thenByDescending { it[1] }) + ) { + adj.computeIfAbsent(src) { mutableListOf() }.add(dst) + } + + val stack = ArrayDeque().apply { add("JFK") } + val res = mutableListOf() + + while (stack.isNotEmpty()) { + val curr = stack.last() + if (adj[curr].isNullOrEmpty()) { + res.add(stack.removeLast()) + } else { + stack.add(adj[curr]!!.removeLast()) + } + } + + return res.asReversed() + } +} +``` + +```swift +class Solution { + func findItinerary(_ tickets: [[String]]) -> [String] { + var adj = [String: [String]]() + for ticket in tickets.sorted(by: { $0[1] > $1[1] }) { + adj[ticket[0], default: []].append(ticket[1]) + } + + var stack = ["JFK"] + var res = [String]() + + while !stack.isEmpty { + let curr = stack.last! + if adj[curr] == nil || adj[curr]!.isEmpty { + res.append(stack.removeLast()) + } else { + stack.append(adj[curr]!.removeLast()) + } + } + + return res.reversed() + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(E\log E)$ +* Space complexity: $O(E)$ + +> Where $E$ is the number of tickets (edges) and $V$ is the number of airports (vertices). \ No newline at end of file diff --git a/articles/redistribute-characters-to-make-all-strings-equal.md b/articles/redistribute-characters-to-make-all-strings-equal.md new file mode 100644 index 000000000..b825a3786 --- /dev/null +++ b/articles/redistribute-characters-to-make-all-strings-equal.md @@ -0,0 +1,228 @@ +## 1. Frequency Count (Hash Map) + +::tabs-start + +```python +class Solution: + def makeEqual(self, words: List[str]) -> bool: + char_cnt = defaultdict(int) + + for w in words: + for c in w: + char_cnt[c] += 1 + + for c in char_cnt: + if char_cnt[c] % len(words): + return False + return True +``` + +```java +public class Solution { + public boolean makeEqual(String[] words) { + Map charCnt = new HashMap<>(); + + for (String w : words) { + for (char c : w.toCharArray()) { + charCnt.put(c, charCnt.getOrDefault(c, 0) + 1); + } + } + + for (int count : charCnt.values()) { + if (count % words.length != 0) { + return false; + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool makeEqual(vector& words) { + unordered_map charCnt; + + for (const string& w : words) { + for (char c : w) { + charCnt[c]++; + } + } + + for (const auto& entry : charCnt) { + if (entry.second % words.size() != 0) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {boolean} + */ + makeEqual(words) { + const charCnt = {}; + + for (let w of words) { + for (let c of w) { + charCnt[c] = (charCnt[c] || 0) + 1; + } + } + + for (let count of Object.values(charCnt)) { + if (count % words.length !== 0) { + return false; + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +> Where $n$ is the number of words and $m$ is the average length of each word. + +--- + +## 2. Frequency Count (Array) + +::tabs-start + +```python +class Solution: + def makeEqual(self, words: List[str]) -> bool: + freq = [0] * 26 + flag = 0 + n = len(words) + + for w in words: + for c in w: + i = ord(c) - ord('a') + if freq[i] != 0: + freq[i] += 1 + if freq[i] % n == 0: + flag += 1 + else: + freq[i] += 1 + if freq[i] % n != 0: + flag -= 1 + freq[i] %= n + + return flag == 0 +``` + +```java +public class Solution { + public boolean makeEqual(String[] words) { + int[] freq = new int[26]; + int flag = 0; + int n = words.length; + + for (String w : words) { + for (char c : w.toCharArray()) { + int i = c - 'a'; + if (freq[i] != 0) { + freq[i]++; + if (freq[i] % n == 0) { + flag++; + } + } else { + freq[i]++; + if (freq[i] % n != 0) { + flag--; + } + } + freq[i] %= n; + } + } + + return flag == 0; + } +} +``` + +```cpp +class Solution { +public: + bool makeEqual(vector& words) { + vector freq(26, 0); + int flag = 0; + int n = words.size(); + + for (const string& w : words) { + for (char c : w) { + int i = c - 'a'; + if (freq[i] != 0) { + freq[i]++; + if (freq[i] % n == 0) { + flag++; + } + } else { + freq[i]++; + if (freq[i] % n != 0) { + flag--; + } + } + freq[i] %= n; + } + } + + return flag == 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {boolean} + */ + makeEqual(words) { + const freq = Array(26).fill(0); + let flag = 0; + const n = words.length; + + for (let w of words) { + for (let c of w) { + const i = c.charCodeAt(0) - 'a'.charCodeAt(0); + if (freq[i] !== 0) { + freq[i]++; + if (freq[i] % n === 0) { + flag++; + } + } else { + freq[i]++; + if (freq[i] % n !== 0) { + flag--; + } + } + freq[i] %= n; + } + } + + return flag === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +> Where $n$ is the number of words and $m$ is the average length of each word. \ No newline at end of file diff --git a/articles/redundant-connection.md b/articles/redundant-connection.md new file mode 100644 index 000000000..154493da7 --- /dev/null +++ b/articles/redundant-connection.md @@ -0,0 +1,1407 @@ +## 1. Cycle Detection (DFS) + +::tabs-start + +```python +class Solution: + def findRedundantConnection(self, edges: List[List[int]]) -> List[int]: + n = len(edges) + adj = [[] for _ in range(n + 1)] + + def dfs(node, par): + if visit[node]: + return True + + visit[node] = True + for nei in adj[node]: + if nei == par: + continue + if dfs(nei, node): + return True + return False + + for u, v in edges: + adj[u].append(v) + adj[v].append(u) + visit = [False] * (n + 1) + + if dfs(u, -1): + return [u, v] + return [] +``` + +```java +public class Solution { + public int[] findRedundantConnection(int[][] edges) { + int n = edges.length; + List> adj = new ArrayList<>(); + for (int i = 0; i <= n; i++) { + adj.add(new ArrayList<>()); + } + + for (int[] edge : edges) { + int u = edge[0], v = edge[1]; + adj.get(u).add(v); + adj.get(v).add(u); + boolean[] visit = new boolean[n + 1]; + + if (dfs(u, -1, adj, visit)) { + return edge; + } + } + return new int[0]; + } + + private boolean dfs(int node, int parent, + List> adj, boolean[] visit) { + if (visit[node]) { + return true; + } + + visit[node] = true; + for (int nei : adj.get(node)) { + if (nei == parent) { + continue; + } + if (dfs(nei, node, adj, visit)) { + return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + vector findRedundantConnection(vector>& edges) { + int n = edges.size(); + vector> adj(n + 1); + + for (const auto& edge : edges) { + int u = edge[0], v = edge[1]; + adj[u].push_back(v); + adj[v].push_back(u); + vector visit(n + 1, false); + + if (dfs(u, -1, adj, visit)) { + return {u, v}; + } + } + return {}; + } + +private: + bool dfs(int node, int parent, + vector>& adj, vector& visit) { + if (visit[node]) return true; + visit[node] = true; + for (int nei : adj[node]) { + if (nei == parent) continue; + if (dfs(nei, node, adj, visit)) return true; + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} edges + * @return {number[]} + */ + findRedundantConnection(edges) { + const n = edges.length; + const adj = Array.from({ length: n + 1 }, () => []); + + const dfs = (node, parent, visited) => { + if (visited[node]) return true; + visited[node] = true; + for (const nei of adj[node]) { + if (nei === parent) continue; + if (dfs(nei, node, visited)) return true; + } + return false; + }; + + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + const visited = Array(n + 1).fill(false); + if (dfs(u, -1, visited)) { + return [u, v]; + } + } + return []; + } +} +``` + +```csharp +public class Solution { + public int[] FindRedundantConnection(int[][] edges) { + int n = edges.Length; + List> adj = new List>(); + for (int i = 0; i <= n; i++) { + adj.Add(new List()); + } + + foreach (var edge in edges) { + int u = edge[0], v = edge[1]; + adj[u].Add(v); + adj[v].Add(u); + bool[] visit = new bool[n + 1]; + + if (Dfs(u, -1, adj, visit)) { + return new int[] { u, v }; + } + } + return new int[0]; + } + + private bool Dfs(int node, int parent, + List> adj, bool[] visit) { + if (visit[node]) return true; + visit[node] = true; + foreach (int nei in adj[node]) { + if (nei == parent) continue; + if (Dfs(nei, node, adj, visit)) return true; + } + return false; + } +} +``` + +```go +func findRedundantConnection(edges [][]int) []int { + n := len(edges) + adj := make([][]int, n+1) + visit := make([]bool, n+1) + + var dfs func(node, par int) bool + dfs = func(node, par int) bool { + if visit[node] { + return true + } + visit[node] = true + for _, nei := range adj[node] { + if nei == par { + continue + } + if dfs(nei, node) { + return true + } + } + return false + } + + for _, edge := range edges { + u, v := edge[0], edge[1] + adj[u] = append(adj[u], v) + adj[v] = append(adj[v], u) + for i := 0; i <= n; i++ { + visit[i] = false + } + + if dfs(u, -1) { + return []int{u, v} + } + } + return []int{} +} +``` + +```kotlin +class Solution { + fun findRedundantConnection(edges: Array): IntArray { + val n = edges.size + val adj = Array(n + 1) { mutableListOf() } + + fun dfs(node: Int, par: Int, visit: BooleanArray): Boolean { + if (visit[node]) { + return true + } + visit[node] = true + for (nei in adj[node]) { + if (nei == par) continue + if (dfs(nei, node, visit)) return true + } + return false + } + + for (edge in edges) { + val u = edge[0] + val v = edge[1] + adj[u].add(v) + adj[v].add(u) + val visit = BooleanArray(n + 1) + + if (dfs(u, -1, visit)) { + return intArrayOf(u, v) + } + } + return intArrayOf() + } +} +``` + +```swift +class Solution { + func findRedundantConnection(_ edges: [[Int]]) -> [Int] { + let n = edges.count + var adj = Array(repeating: [Int](), count: n + 1) + + func dfs(_ node: Int, _ par: Int, _ visit: inout [Bool]) -> Bool { + if visit[node] { + return true + } + + visit[node] = true + for nei in adj[node] { + if nei == par { + continue + } + if dfs(nei, node, &visit) { + return true + } + } + return false + } + + for edge in edges { + let u = edge[0] + let v = edge[1] + adj[u].append(v) + adj[v].append(u) + var visit = Array(repeating: false, count: n + 1) + + if dfs(u, -1, &visit) { + return [u, v] + } + } + return [] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(E * (V + E))$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges in the graph. + +--- + +## 2. Depth First Search (Optimal) + +::tabs-start + +```python +class Solution: + def findRedundantConnection(self, edges: List[List[int]]) -> List[int]: + n = len(edges) + adj = [[] for _ in range(n + 1)] + for u, v in edges: + adj[u].append(v) + adj[v].append(u) + + visit = [False] * (n + 1) + cycle = set() + cycleStart = -1 + + def dfs(node, par): + nonlocal cycleStart + if visit[node]: + cycleStart = node + return True + + visit[node] = True + for nei in adj[node]: + if nei == par: + continue + if dfs(nei, node): + if cycleStart != -1: + cycle.add(node) + if node == cycleStart: + cycleStart = -1 + return True + return False + + dfs(1, -1) + + for u, v in reversed(edges): + if u in cycle and v in cycle: + return [u, v] + + return [] +``` + +```java +public class Solution { + private boolean[] visit; + private List> adj; + private Set cycle; + private int cycleStart; + + public int[] findRedundantConnection(int[][] edges) { + int n = edges.length; + adj = new ArrayList<>(); + for (int i = 0; i <= n; i++) + adj.add(new ArrayList<>()); + + for (int[] edge : edges) { + int u = edge[0], v = edge[1]; + adj.get(u).add(v); + adj.get(v).add(u); + } + + visit = new boolean[n + 1]; + cycle = new HashSet<>(); + cycleStart = -1; + dfs(1, -1); + + for (int i = edges.length - 1; i >= 0; i--) { + int u = edges[i][0], v = edges[i][1]; + if (cycle.contains(u) && cycle.contains(v)) { + return new int[]{u, v}; + } + } + return new int[0]; + } + + private boolean dfs(int node, int par) { + if (visit[node]) { + cycleStart = node; + return true; + } + visit[node] = true; + for (int nei : adj.get(node)) { + if (nei == par) continue; + if (dfs(nei, node)) { + if (cycleStart != -1) cycle.add(node); + if (node == cycleStart) { + cycleStart = -1; + } + return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { + vector visit; + vector> adj; + unordered_set cycle; + int cycleStart; +public: + vector findRedundantConnection(vector>& edges) { + int n = edges.size(); + adj.resize(n + 1); + for (auto& edge : edges) { + int u = edge[0], v = edge[1]; + adj[u].push_back(v); + adj[v].push_back(u); + } + + visit.resize(n + 1, false); + cycleStart = -1; + dfs(1, -1); + + for (int i = edges.size() - 1; i >= 0; i--) { + int u = edges[i][0], v = edges[i][1]; + if (cycle.count(u) && cycle.count(v)) { + return {u, v}; + } + } + return {}; + } + +private: + bool dfs(int node, int par) { + if (visit[node]) { + cycleStart = node; + return true; + } + visit[node] = true; + for (int nei : adj[node]) { + if (nei == par) continue; + if (dfs(nei, node)) { + if (cycleStart != -1) cycle.insert(node); + if (node == cycleStart) { + cycleStart = -1; + } + return true; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} edges + * @return {number[]} + */ + findRedundantConnection(edges) { + const n = edges.length; + const adj = Array.from({ length: n + 1 }, () => []); + + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + } + + const visit = Array(n + 1).fill(false); + const cycle = new Set(); + let cycleStart = -1; + + const dfs = (node, par) => { + if (visit[node]) { + cycleStart = node; + return true; + } + visit[node] = true; + for (const nei of adj[node]) { + if (nei === par) continue; + if (dfs(nei, node)) { + if (cycleStart !== -1) cycle.add(node); + if (node === cycleStart) { + cycleStart = -1; + } + return true; + } + } + return false; + }; + + dfs(1, -1); + + for (let i = edges.length - 1; i >= 0; i--) { + const [u, v] = edges[i]; + if (cycle.has(u) && cycle.has(v)) { + return [u, v]; + } + } + return []; + } +} +``` + +```csharp +public class Solution { + public int[] FindRedundantConnection(int[][] edges) { + int n = edges.Length; + List[] adj = new List[n + 1]; + for (int i = 0; i <= n; i++) adj[i] = new List(); + + foreach (var edge in edges) { + int u = edge[0], v = edge[1]; + adj[u].Add(v); + adj[v].Add(u); + } + + bool[] visit = new bool[n + 1]; + HashSet cycle = new HashSet(); + int cycleStart = -1; + + bool Dfs(int node, int par) { + if (visit[node]) { + cycleStart = node; + return true; + } + visit[node] = true; + foreach (int nei in adj[node]) { + if (nei == par) continue; + if (Dfs(nei, node)) { + if (cycleStart != -1) cycle.Add(node); + if (node == cycleStart) { + cycleStart = -1; + } + return true; + } + } + return false; + } + + Dfs(1, -1); + + for (int i = edges.Length - 1; i >= 0; i--) { + int u = edges[i][0], v = edges[i][1]; + if (cycle.Contains(u) && cycle.Contains(v)) { + return new int[] { u, v }; + } + } + return new int[0]; + } +} +``` + +```go +func findRedundantConnection(edges [][]int) []int { + n := len(edges) + adj := make([][]int, n+1) + for _, edge := range edges { + u, v := edge[0], edge[1] + adj[u] = append(adj[u], v) + adj[v] = append(adj[v], u) + } + + visit := make([]bool, n+1) + cycle := make(map[int]bool) + cycleStart := -1 + + var dfs func(node, par int) bool + dfs = func(node, par int) bool { + if visit[node] { + cycleStart = node + return true + } + + visit[node] = true + for _, nei := range adj[node] { + if nei == par { + continue + } + if dfs(nei, node) { + if cycleStart != -1 { + cycle[node] = true + } + if node == cycleStart { + cycleStart = -1 + } + return true + } + } + return false + } + + dfs(1, -1) + + for i := len(edges) - 1; i >= 0; i-- { + u, v := edges[i][0], edges[i][1] + if cycle[u] && cycle[v] { + return []int{u, v} + } + } + return []int{} +} +``` + +```kotlin +class Solution { + fun findRedundantConnection(edges: Array): IntArray { + val n = edges.size + val adj = Array(n + 1) { mutableListOf() } + for ((u, v) in edges) { + adj[u].add(v) + adj[v].add(u) + } + + val visit = BooleanArray(n + 1) + val cycle = HashSet() + var cycleStart = -1 + + fun dfs(node: Int, par: Int): Boolean { + if (visit[node]) { + cycleStart = node + return true + } + visit[node] = true + for (nei in adj[node]) { + if (nei == par) continue + if (dfs(nei, node)) { + if (cycleStart != -1) { + cycle.add(node) + } + if (node == cycleStart) { + cycleStart = -1 + } + return true + } + } + return false + } + + dfs(1, -1) + + for (i in edges.indices.reversed()) { + val (u, v) = edges[i] + if (u in cycle && v in cycle) { + return intArrayOf(u, v) + } + } + return intArrayOf() + } +} +``` + +```swift +class Solution { + func findRedundantConnection(_ edges: [[Int]]) -> [Int] { + let n = edges.count + var adj = Array(repeating: [Int](), count: n + 1) + + for edge in edges { + let u = edge[0] + let v = edge[1] + adj[u].append(v) + adj[v].append(u) + } + + var visit = Array(repeating: false, count: n + 1) + var cycle = Set() + var cycleStart = -1 + + func dfs(_ node: Int, _ par: Int) -> Bool { + if visit[node] { + cycleStart = node + return true + } + + visit[node] = true + for nei in adj[node] { + if nei == par { + continue + } + if dfs(nei, node) { + if cycleStart != -1 { + cycle.insert(node) + } + if node == cycleStart { + cycleStart = -1 + } + return true + } + } + return false + } + + dfs(1, -1) + + for edge in edges.reversed() { + let u = edge[0] + let v = edge[1] + if cycle.contains(u) && cycle.contains(v) { + return [u, v] + } + } + + return [] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges in the graph. + +--- + +## 3. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def findRedundantConnection(self, edges: List[List[int]]) -> List[int]: + n = len(edges) + indegree = [0] * (n + 1) + adj = [[] for _ in range(n + 1)] + for u, v in edges: + adj[u].append(v) + adj[v].append(u) + indegree[u] += 1 + indegree[v] += 1 + + q = deque() + for i in range(1, n + 1): + if indegree[i] == 1: + q.append(i) + + while q: + node = q.popleft() + indegree[node] -= 1 + for nei in adj[node]: + indegree[nei] -= 1 + if indegree[nei] == 1: + q.append(nei) + + for u, v in reversed(edges): + if indegree[u] == 2 and indegree[v]: + return [u, v] + return [] +``` + +```java +public class Solution { + public int[] findRedundantConnection(int[][] edges) { + int n = edges.length; + int[] indegree = new int[n + 1]; + List> adj = new ArrayList<>(n + 1); + for (int i = 0; i <= n; i++) adj.add(new ArrayList<>()); + for (int[] edge : edges) { + int u = edge[0], v = edge[1]; + adj.get(u).add(v); + adj.get(v).add(u); + indegree[u]++; + indegree[v]++; + } + + Queue q = new LinkedList<>(); + for (int i = 1; i <= n; i++) { + if (indegree[i] == 1) q.offer(i); + } + + while (!q.isEmpty()) { + int node = q.poll(); + indegree[node]--; + for (int nei : adj.get(node)) { + indegree[nei]--; + if (indegree[nei] == 1) q.offer(nei); + } + } + + for (int i = edges.length - 1; i >= 0; i--) { + int u = edges[i][0], v = edges[i][1]; + if (indegree[u] == 2 && indegree[v] > 0) + return new int[]{u, v}; + } + return new int[0]; + } +} +``` + +```cpp +class Solution { +public: + vector findRedundantConnection(vector>& edges) { + int n = edges.size(); + vector indegree(n + 1, 0); + vector> adj(n + 1); + for (auto& edge : edges) { + int u = edge[0], v = edge[1]; + adj[u].push_back(v); + adj[v].push_back(u); + indegree[u]++; + indegree[v]++; + } + + queue q; + for (int i = 1; i <= n; i++) { + if (indegree[i] == 1) q.push(i); + } + + while (!q.empty()) { + int node = q.front(); q.pop(); + indegree[node]--; + for (int nei : adj[node]) { + indegree[nei]--; + if (indegree[nei] == 1) q.push(nei); + } + } + + for (int i = edges.size() - 1; i >= 0; i--) { + int u = edges[i][0], v = edges[i][1]; + if (indegree[u] == 2 && indegree[v]) + return {u, v}; + } + return {}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} edges + * @return {number[]} + */ + findRedundantConnection(edges) { + const n = edges.length; + const indegree = new Array(n + 1).fill(0); + const adj = Array.from({ length: n + 1 }, () => []); + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + indegree[u]++; + indegree[v]++; + } + + const q = new Queue(); + for (let i = 1; i <= n; i++) { + if (indegree[i] === 1) q.push(i); + } + + while (!q.isEmpty()) { + const node = q.pop(); + indegree[node]--; + for (const nei of adj[node]) { + indegree[nei]--; + if (indegree[nei] === 1) q.push(nei); + } + } + + for (let i = edges.length - 1; i >= 0; i--) { + const [u, v] = edges[i]; + if (indegree[u] === 2 && indegree[v]) + return [u, v]; + } + return []; + } +} +``` + +```csharp +public class Solution { + public int[] FindRedundantConnection(int[][] edges) { + int n = edges.Length; + int[] indegree = new int[n + 1]; + List[] adj = new List[n + 1]; + for (int i = 0; i <= n; i++) adj[i] = new List(); + foreach (var edge in edges) { + int u = edge[0], v = edge[1]; + adj[u].Add(v); + adj[v].Add(u); + indegree[u]++; + indegree[v]++; + } + + Queue q = new Queue(); + for (int i = 1; i <= n; i++) { + if (indegree[i] == 1) q.Enqueue(i); + } + + while (q.Count > 0) { + int node = q.Dequeue(); + indegree[node]--; + foreach (int nei in adj[node]) { + indegree[nei]--; + if (indegree[nei] == 1) q.Enqueue(nei); + } + } + + for (int i = edges.Length - 1; i >= 0; i--) { + int u = edges[i][0], v = edges[i][1]; + if (indegree[u] == 2 && indegree[v] > 0) + return new int[] {u, v}; + } + return new int[0]; + } +} +``` + +```go +func findRedundantConnection(edges [][]int) []int { + n := len(edges) + indegree := make([]int, n+1) + adj := make([][]int, n+1) + for _, edge := range edges { + u, v := edge[0], edge[1] + adj[u] = append(adj[u], v) + adj[v] = append(adj[v], u) + indegree[u]++ + indegree[v]++ + } + + q := []int{} + for i := 1; i <= n; i++ { + if indegree[i] == 1 { + q = append(q, i) + } + } + + for len(q) > 0 { + node := q[0] + q = q[1:] + indegree[node]-- + for _, nei := range adj[node] { + indegree[nei]-- + if indegree[nei] == 1 { + q = append(q, nei) + } + } + } + + for i := len(edges) - 1; i >= 0; i-- { + u, v := edges[i][0], edges[i][1] + if indegree[u] == 2 && indegree[v] == 2 { + return []int{u, v} + } + } + return []int{} +} +``` + +```kotlin +class Solution { + fun findRedundantConnection(edges: Array): IntArray { + val n = edges.size + val indegree = IntArray(n + 1) + val adj = Array(n + 1) { mutableListOf() } + + for ((u, v) in edges) { + adj[u].add(v) + adj[v].add(u) + indegree[u]++ + indegree[v]++ + } + + val q: Queue = LinkedList() + for (i in 1..n) { + if (indegree[i] == 1) { + q.add(i) + } + } + + while (q.isNotEmpty()) { + val node = q.poll() + indegree[node]-- + for (nei in adj[node]) { + indegree[nei]-- + if (indegree[nei] == 1) { + q.add(nei) + } + } + } + + for (i in edges.indices.reversed()) { + val (u, v) = edges[i] + if (indegree[u] == 2 && indegree[v] == 2) { + return intArrayOf(u, v) + } + } + return intArrayOf() + } +} +``` + +```swift +class Solution { + func findRedundantConnection(_ edges: [[Int]]) -> [Int] { + let n = edges.count + var indegree = Array(repeating: 0, count: n + 1) + var adj = Array(repeating: [Int](), count: n + 1) + + for edge in edges { + let u = edge[0] + let v = edge[1] + adj[u].append(v) + adj[v].append(u) + indegree[u] += 1 + indegree[v] += 1 + } + + var queue = Deque() + for i in 1...n { + if indegree[i] == 1 { + queue.append(i) + } + } + + while !queue.isEmpty { + let node = queue.popFirst()! + indegree[node] -= 1 + for nei in adj[node] { + indegree[nei] -= 1 + if indegree[nei] == 1 { + queue.append(nei) + } + } + } + + for edge in edges.reversed() { + let u = edge[0] + let v = edge[1] + if indegree[u] == 2 && indegree[v] > 0 { + return [u, v] + } + } + return [] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges in the graph. + +--- + +## 4. Disjoint Set Union + +::tabs-start + +```python +class Solution: + def findRedundantConnection(self, edges: List[List[int]]) -> List[int]: + par = [i for i in range(len(edges) + 1)] + rank = [1] * (len(edges) + 1) + + def find(n): + p = par[n] + while p != par[p]: + par[p] = par[par[p]] + p = par[p] + return p + + def union(n1, n2): + p1, p2 = find(n1), find(n2) + + if p1 == p2: + return False + if rank[p1] > rank[p2]: + par[p2] = p1 + rank[p1] += rank[p2] + else: + par[p1] = p2 + rank[p2] += rank[p1] + return True + + for n1, n2 in edges: + if not union(n1, n2): + return [n1, n2] +``` + +```java +public class Solution { + public int[] findRedundantConnection(int[][] edges) { + int[] par = new int[edges.length + 1]; + int[] rank = new int[edges.length + 1]; + for (int i = 0; i < par.length; i++) { + par[i] = i; + rank[i] = 1; + } + + for (int[] edge : edges) { + if (!union(par, rank, edge[0], edge[1])) + return new int[]{edge[0], edge[1]}; + } + return new int[0]; + } + + private int find(int[] par, int n) { + int p = par[n]; + while (p != par[p]) { + par[p] = par[par[p]]; + p = par[p]; + } + return p; + } + + private boolean union(int[] par, int[] rank, int n1, int n2) { + int p1 = find(par, n1); + int p2 = find(par, n2); + + if (p1 == p2) + return false; + if (rank[p1] > rank[p2]) { + par[p2] = p1; + rank[p1] += rank[p2]; + } else { + par[p1] = p2; + rank[p2] += rank[p1]; + } + return true; + } +} +``` + +```cpp +class Solution { +public: + vector findRedundantConnection(vector>& edges) { + int n = edges.size(); + vector par(n + 1), rank(n + 1, 1); + for (int i = 0; i <= n; ++i) + par[i] = i; + + for (const auto& edge : edges) { + if (!Union(par, rank, edge[0], edge[1])) + return vector{ edge[0], edge[1] }; + } + return {}; + } + +private: + int Find(vector& par, int n) { + int p = par[n]; + while (p != par[p]) { + par[p] = par[par[p]]; + p = par[p]; + } + return p; + } + + bool Union(vector& par, vector& rank, int n1, int n2) { + int p1 = Find(par, n1); + int p2 = Find(par, n2); + + if (p1 == p2) + return false; + if (rank[p1] > rank[p2]) { + par[p2] = p1; + rank[p1] += rank[p2]; + } else { + par[p1] = p2; + rank[p2] += rank[p1]; + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} edges + * @return {number[]} + */ + findRedundantConnection(edges) { + const par = new Array(edges.length + 1).fill(0).map((_, i) => i); + const rank = new Array(edges.length + 1).fill(1); + + /** + * @param {number} n + * @return {number} + */ + function find(n) { + let p = par[n]; + while (p !== par[p]) { + par[p] = par[par[p]]; + p = par[p]; + } + return p; + } + + /** + * @param {number} n1 + * @param {number} n2 + * @return {boolean} + */ + function union(n1, n2) { + const p1 = find(n1); + const p2 = find(n2); + + if (p1 === p2) { + return false; + } + if (rank[p1] > rank[p2]) { + par[p2] = p1; + rank[p1] += rank[p2]; + } else { + par[p1] = p2; + rank[p2] += rank[p1]; + } + return true; + } + + for (const [n1, n2] of edges) { + if (!union(n1, n2)) { + return [n1, n2]; + } + } + return []; + } +} +``` + +```csharp +public class Solution { + + public int[] FindRedundantConnection(int[][] edges) { + int[] par = new int[edges.Length + 1]; + int[] rank = new int[edges.Length + 1]; + for (int i = 0; i < par.Length; i++) { + par[i] = i; + rank[i] = 1; + } + + foreach (var edge in edges) { + if (!Union(par, rank, edge[0], edge[1])) + return new int[]{ edge[0], edge[1] }; + } + return new int[0]; + } + + private int Find(int[] par, int n) { + int p = par[n]; + while (p != par[p]) { + par[p] = par[par[p]]; + p = par[p]; + } + return p; + } + + private bool Union(int[] par, int[] rank, int n1, int n2) { + int p1 = Find(par, n1); + int p2 = Find(par, n2); + + if (p1 == p2) + return false; + if (rank[p1] > rank[p2]) { + par[p2] = p1; + rank[p1] += rank[p2]; + } else { + par[p1] = p2; + rank[p2] += rank[p1]; + } + return true; + } +} +``` + +```go +func findRedundantConnection(edges [][]int) []int { + n := len(edges) + par := make([]int, n+1) + rank := make([]int, n+1) + + for i := 0; i <= n; i++ { + par[i] = i + rank[i] = 1 + } + + var find func(int) int + find = func(x int) int { + if par[x] != x { + par[x] = find(par[x]) + } + return par[x] + } + + union := func(x, y int) bool { + rootX, rootY := find(x), find(y) + if rootX == rootY { + return false + } + if rank[rootX] > rank[rootY] { + par[rootY] = rootX + rank[rootX] += rank[rootY] + } else { + par[rootX] = rootY + rank[rootY] += rank[rootX] + } + return true + } + + for _, edge := range edges { + if !union(edge[0], edge[1]) { + return edge + } + } + return []int{} +} +``` + +```kotlin +class Solution { + fun findRedundantConnection(edges: Array): IntArray { + val n = edges.size + val par = IntArray(n + 1) { it } + val rank = IntArray(n + 1) { 1 } + + fun find(x: Int): Int { + if (par[x] != x) { + par[x] = find(par[x]) + } + return par[x] + } + + fun union(x: Int, y: Int): Boolean { + val rootX = find(x) + val rootY = find(y) + if (rootX == rootY) { + return false + } + if (rank[rootX] > rank[rootY]) { + par[rootY] = rootX + rank[rootX] += rank[rootY] + } else { + par[rootX] = rootY + rank[rootY] += rank[rootX] + } + return true + } + + for ((u, v) in edges) { + if (!union(u, v)) { + return intArrayOf(u, v) + } + } + return intArrayOf() + } +} +``` + +```swift +class Solution { + func findRedundantConnection(_ edges: [[Int]]) -> [Int] { + var par = Array(0...edges.count) + var rank = Array(repeating: 1, count: edges.count + 1) + + func find(_ n: Int) -> Int { + var p = par[n] + while p != par[p] { + par[p] = par[par[p]] + p = par[p] + } + return p + } + + func union(_ n1: Int, _ n2: Int) -> Bool { + let p1 = find(n1) + let p2 = find(n2) + + if p1 == p2 { + return false + } + if rank[p1] > rank[p2] { + par[p2] = p1 + rank[p1] += rank[p2] + } else { + par[p1] = p2 + rank[p2] += rank[p1] + } + return true + } + + for edge in edges { + let n1 = edge[0] + let n2 = edge[1] + if !union(n1, n2) { + return [n1, n2] + } + } + return [] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + (E * α(V)))$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges in the graph. $α()$ is used for amortized complexity. \ No newline at end of file diff --git a/articles/regular-expression-matching.md b/articles/regular-expression-matching.md new file mode 100644 index 000000000..fd4bfda86 --- /dev/null +++ b/articles/regular-expression-matching.md @@ -0,0 +1,1264 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def isMatch(self, s: str, p: str) -> bool: + m, n = len(s), len(p) + + def dfs(i, j): + if j == n: + return i == m + + match = i < m and (s[i] == p[j] or p[j] == ".") + if (j + 1) < n and p[j + 1] == "*": + return (dfs(i, j + 2) or # don't use * + (match and dfs(i + 1, j))) # use * + if match: + return dfs(i + 1, j + 1) + return False + + return dfs(0, 0) +``` + +```java +public class Solution { + public boolean isMatch(String s, String p) { + int m = s.length(), n = p.length(); + return dfs(0, 0, s, p, m, n); + } + + private boolean dfs(int i, int j, String s, String p, int m, int n) { + if (j == n) return i == m; + + boolean match = i < m && (s.charAt(i) == p.charAt(j) || + p.charAt(j) == '.'); + if (j + 1 < n && p.charAt(j + 1) == '*') { + return dfs(i, j + 2, s, p, m, n) || + (match && dfs(i + 1, j, s, p, m, n)); + } + + if (match) { + return dfs(i + 1, j + 1, s, p, m, n); + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool isMatch(string s, string p) { + int m = s.size(), n = p.size(); + return dfs(0, 0, s, p, m, n); + } + + bool dfs(int i, int j, const string& s, const string& p, int m, int n) { + if (j == n) return i == m; + + bool match = (i < m && (s[i] == p[j] || p[j] == '.')); + if (j + 1 < n && p[j + 1] == '*') { + return dfs(i, j + 2, s, p, m, n) || + (match && dfs(i + 1, j, s, p, m, n)); + } + + if (match) { + return dfs(i + 1, j + 1, s, p, m, n); + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} p + * @return {boolean} + */ + isMatch(s, p) { + let m = s.length, n = p.length; + + const dfs = (i, j) => { + if (j === n) { + return i === m; + } + + let match = i < m && (s[i] === p[j] || p[j] === '.'); + if (j + 1 < n && p[j + 1] === '*') { + return dfs(i, j + 2) || + (match && dfs(i + 1, j)); + } + + if (match) { + return dfs(i + 1, j + 1); + } + + return false; + } + + return dfs(0, 0); + } +} +``` + +```csharp +public class Solution { + public bool IsMatch(string s, string p) { + int m = s.Length, n = p.Length; + return Dfs(0, 0, s, p, m, n); + } + + private bool Dfs(int i, int j, string s, string p, int m, int n) { + if (j == n) { + return i == m; + } + + bool match = i < m && (s[i] == p[j] || p[j] == '.'); + if (j + 1 < n && p[j + 1] == '*') { + return Dfs(i, j + 2, s, p, m, n) || + (match && Dfs(i + 1, j, s, p, m, n)); + } + + if (match) { + return Dfs(i + 1, j + 1, s, p, m, n); + } + + return false; + } +} +``` + +```go +func isMatch(s string, p string) bool { + m, n := len(s), len(p) + + var dfs func(i, j int) bool + dfs = func(i, j int) bool { + if j == n { + return i == m + } + + match := i < m && (s[i] == p[j] || p[j] == '.') + + if (j+1) < n && p[j+1] == '*' { + return dfs(i, j+2) || (match && dfs(i+1, j)) + } + + if match { + return dfs(i+1, j+1) + } + + return false + } + + return dfs(0, 0) +} +``` + +```kotlin +class Solution { + fun isMatch(s: String, p: String): Boolean { + val m = s.length + val n = p.length + + fun dfs(i: Int, j: Int): Boolean { + if (j == n) return i == m + + val match = i < m && (s[i] == p[j] || p[j] == '.') + + if ((j + 1) < n && p[j + 1] == '*') { + return dfs(i, j + 2) || (match && dfs(i + 1, j)) + } + + return match && dfs(i + 1, j + 1) + } + + return dfs(0, 0) + } +} +``` + +```swift +class Solution { + func isMatch(_ s: String, _ p: String) -> Bool { + let sArr = Array(s), pArr = Array(p) + let m = sArr.count, n = pArr.count + + func dfs(_ i: Int, _ j: Int) -> Bool { + if j == n { + return i == m + } + + let match = i < m && (sArr[i] == pArr[j] || pArr[j] == ".") + + if j + 1 < n && pArr[j + 1] == "*" { + return dfs(i, j + 2) || (match && dfs(i + 1, j)) + } + + if match { + return dfs(i + 1, j + 1) + } + + return false + } + + return dfs(0, 0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ {m + n})$ +* Space complexity: $O(m + n)$ + +> Where $m$ is the length of the string $s$ and $n$ is the length of the string $p$. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def isMatch(self, s: str, p: str) -> bool: + m, n = len(s), len(p) + cache = {} + + def dfs(i, j): + if j == n: + return i == m + if (i, j) in cache: + return cache[(i, j)] + + match = i < m and (s[i] == p[j] or p[j] == ".") + if (j + 1) < n and p[j + 1] == "*": + cache[(i, j)] = (dfs(i, j + 2) or + (match and dfs(i + 1, j))) + return cache[(i, j)] + + if match: + cache[(i, j)] = dfs(i + 1, j + 1) + return cache[(i, j)] + + cache[(i, j)] = False + return False + + return dfs(0, 0) +``` + +```java +public class Solution { + private Boolean[][] dp; + + public boolean isMatch(String s, String p) { + int m = s.length(), n = p.length(); + dp = new Boolean[m + 1][n + 1]; + return dfs(0, 0, s, p, m, n); + } + + private boolean dfs(int i, int j, String s, String p, int m, int n) { + if (j == n) { + return i == m; + } + if (dp[i][j] != null) { + return dp[i][j]; + } + + boolean match = i < m && (s.charAt(i) == p.charAt(j) || + p.charAt(j) == '.'); + if (j + 1 < n && p.charAt(j + 1) == '*') { + dp[i][j] = dfs(i, j + 2, s, p, m, n) || + (match && dfs(i + 1, j, s, p, m, n)); + } else { + dp[i][j] = match && dfs(i + 1, j + 1, s, p, m, n); + } + + return dp[i][j]; + } +} +``` + +```cpp +class Solution { + vector> dp; + +public: + bool isMatch(string s, string p) { + int m = s.length(), n = p.length(); + dp.assign(m + 1, vector(n + 1, -1)); + return dfs(0, 0, s, p, m, n); + } + +private: + bool dfs(int i, int j, string& s, string& p, int m, int n) { + if (j == n) { + return i == m; + } + if (dp[i][j] != -1) { + return dp[i][j]; + } + bool match = i < m && (s[i] == p[j] || p[j] == '.'); + if (j + 1 < n && p[j + 1] == '*') { + dp[i][j] = dfs(i, j + 2, s, p, m, n) || + (match && dfs(i + 1, j, s, p, m, n)); + } else { + dp[i][j] = match && dfs(i + 1, j + 1, s, p, m, n); + } + return dp[i][j]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} p + * @return {boolean} + */ + isMatch(s, p) { + const m = s.length, n = p.length; + let dp = Array(m + 1).fill().map(() => + Array(n + 1).fill(null)); + + const dfs = (i, j) => { + if (j === n) { + return i === m; + } + if (dp[i][j] !== null) { + return dp[i][j]; + } + const match = i < m && (s[i] === p[j] || p[j] === '.'); + if (j + 1 < n && p[j + 1] === '*') { + dp[i][j] = dfs(i, j + 2) || + (match && dfs(i + 1, j)); + } else { + dp[i][j] = match && dfs(i + 1, j + 1); + } + return dp[i][j]; + } + + return dfs(0, 0); + } +} +``` + +```csharp +public class Solution { + private bool?[,] dp; + + public bool IsMatch(string s, string p) { + int m = s.Length, n = p.Length; + dp = new bool?[m + 1, n + 1]; + return Dfs(0, 0, s, p, m, n); + } + + private bool Dfs(int i, int j, string s, string p, int m, int n) { + if (j == n) { + return i == m; + } + if (dp[i, j].HasValue) { + return dp[i, j].Value; + } + bool match = i < m && (s[i] == p[j] || p[j] == '.'); + if (j + 1 < n && p[j + 1] == '*') { + dp[i, j] = Dfs(i, j + 2, s, p, m, n) || + (match && Dfs(i + 1, j, s, p, m, n)); + } else { + dp[i, j] = match && Dfs(i + 1, j + 1, s, p, m, n); + } + return dp[i, j].Value; + } +} +``` + +```go +func isMatch(s string, p string) bool { + m, n := len(s), len(p) + dp := make([][]int, m+1) + for i := range dp { + dp[i] = make([]int, n+1) + for j := range dp[i] { + dp[i][j] = -1 + } + } + + var dfs func(i, j int) bool + dfs = func(i, j int) bool { + if j == n { + return i == m + } + if dp[i][j] != -1 { + return dp[i][j] == 1 + } + + match := i < m && (s[i] == p[j] || p[j] == '.') + + if (j+1) < n && p[j+1] == '*' { + dp[i][j] = boolToInt(dfs(i, j+2) || (match && dfs(i+1, j))) + return dp[i][j] == 1 + } + + if match { + dp[i][j] = boolToInt(dfs(i+1, j+1)) + return dp[i][j] == 1 + } + + dp[i][j] = 0 + return false + } + + return dfs(0, 0) +} + +func boolToInt(b bool) int { + if b { + return 1 + } + return 0 +} +``` + +```kotlin +class Solution { + fun isMatch(s: String, p: String): Boolean { + val m = s.length + val n = p.length + val dp = Array(m + 1) { IntArray(n + 1) { -1 } } + + fun dfs(i: Int, j: Int): Boolean { + if (j == n) return i == m + if (dp[i][j] != -1) return dp[i][j] == 1 + + val match = i < m && (s[i] == p[j] || p[j] == '.') + + if ((j + 1) < n && p[j + 1] == '*') { + dp[i][j] = if (dfs(i, j + 2) || (match && dfs(i + 1, j))) 1 else 0 + return dp[i][j] == 1 + } + + if (match) { + dp[i][j] = if (dfs(i + 1, j + 1)) 1 else 0 + return dp[i][j] == 1 + } + + dp[i][j] = 0 + return false + } + + return dfs(0, 0) + } +} +``` + +```swift +class Solution { + func isMatch(_ s: String, _ p: String) -> Bool { + let sArr = Array(s), pArr = Array(p) + let m = sArr.count, n = pArr.count + var cache = [[Bool?]](repeating: [Bool?](repeating: nil, count: n + 1), count: m + 1) + + func dfs(_ i: Int, _ j: Int) -> Bool { + if j == n { + return i == m + } + if let cached = cache[i][j] { + return cached + } + + let match = i < m && (sArr[i] == pArr[j] || pArr[j] == ".") + + if j + 1 < n && pArr[j + 1] == "*" { + cache[i][j] = dfs(i, j + 2) || (match && dfs(i + 1, j)) + return cache[i][j]! + } + + if match { + cache[i][j] = dfs(i + 1, j + 1) + return cache[i][j]! + } + + cache[i][j] = false + return false + } + + return dfs(0, 0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the length of the string $s$ and $n$ is the length of the string $p$. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def isMatch(self, s: str, p: str) -> bool: + dp = [[False] * (len(p) + 1) for i in range(len(s) + 1)] + dp[len(s)][len(p)] = True + + for i in range(len(s), -1, -1): + for j in range(len(p) - 1, -1, -1): + match = i < len(s) and (s[i] == p[j] or p[j] == ".") + + if (j + 1) < len(p) and p[j + 1] == "*": + dp[i][j] = dp[i][j + 2] + if match: + dp[i][j] = dp[i + 1][j] or dp[i][j] + elif match: + dp[i][j] = dp[i + 1][j + 1] + + return dp[0][0] +``` + +```java +class Solution { + public boolean isMatch(String s, String p) { + int m = s.length(), n = p.length(); + boolean[][] dp = new boolean[m + 1][n + 1]; + dp[m][n] = true; + + for (int i = m; i >= 0; i--) { + for (int j = n - 1; j >= 0; j--) { + boolean match = i < m && (s.charAt(i) == p.charAt(j) || + p.charAt(j) == '.'); + + if ((j + 1) < n && p.charAt(j + 1) == '*') { + dp[i][j] = dp[i][j + 2]; + if (match) { + dp[i][j] = dp[i + 1][j] || dp[i][j]; + } + } else if (match) { + dp[i][j] = dp[i + 1][j + 1]; + } + } + } + + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + bool isMatch(string s, string p) { + int m = s.length(), n = p.length(); + vector> dp(m + 1, vector(n + 1, false)); + dp[m][n] = true; + + for (int i = m; i >= 0; i--) { + for (int j = n - 1; j >= 0; j--) { + bool match = i < m && (s[i] == p[j] || p[j] == '.'); + + if ((j + 1) < n && p[j + 1] == '*') { + dp[i][j] = dp[i][j + 2]; + if (match) { + dp[i][j] = dp[i + 1][j] || dp[i][j]; + } + } else if (match) { + dp[i][j] = dp[i + 1][j + 1]; + } + } + } + + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} p + * @return {boolean} + */ + isMatch(s, p) { + const dp = new Array(s.length + 1) + .fill(false) + .map(() => new Array(p.length + 1).fill(false)); + dp[s.length][p.length] = true; + + for (let i = s.length; i >= 0; i--) { + for (let j = p.length - 1; j >= 0; j--) { + const match = i < s.length && + (s[i] === p[j] || p[j] === '.'); + + if (j + 1 < p.length && p[j + 1] === '*') { + dp[i][j] = dp[i][j + 2]; + if (match) { + dp[i][j] = dp[i + 1][j] || dp[i][j]; + } + } else if (match) { + dp[i][j] = dp[i + 1][j + 1]; + } + } + } + + return dp[0][0]; + } +} +``` + +```csharp +public class Solution { + public bool IsMatch(string s, string p) { + bool[,] dp = new bool[s.Length + 1, p.Length + 1]; + dp[s.Length, p.Length] = true; + + for (int i = s.Length; i >= 0; i--) { + for (int j = p.Length - 1; j >= 0; j--) { + bool match = i < s.Length && + (s[i] == p[j] || p[j] == '.'); + + if ((j + 1) < p.Length && p[j + 1] == '*') { + dp[i, j] = dp[i, j + 2]; + if (match) { + dp[i, j] = dp[i + 1, j] || dp[i, j]; + } + } else if (match) { + dp[i, j] = dp[i + 1, j + 1]; + } + } + } + + return dp[0, 0]; + } +} +``` + +```go +func isMatch(s, p string) bool { + m, n := len(s), len(p) + dp := make([][]bool, m+1) + for i := range dp { + dp[i] = make([]bool, n+1) + } + dp[m][n] = true + + for i := m; i >= 0; i-- { + for j := n - 1; j >= 0; j-- { + match := i < m && (s[i] == p[j] || p[j] == '.') + + if j+1 < n && p[j+1] == '*' { + dp[i][j] = dp[i][j+2] + if match { + dp[i][j] = dp[i][j] || dp[i+1][j] + } + } else if match { + dp[i][j] = dp[i+1][j+1] + } + } + } + return dp[0][0] +} +``` + +```kotlin +class Solution { + fun isMatch(s: String, p: String): Boolean { + val m = s.length + val n = p.length + val dp = Array(m + 1) { BooleanArray(n + 1) } + dp[m][n] = true + + for (i in m downTo 0) { + for (j in n - 1 downTo 0) { + val match = i < m && (s[i] == p[j] || p[j] == '.') + + if (j + 1 < n && p[j + 1] == '*') { + dp[i][j] = dp[i][j + 2] || (match && dp[i + 1][j]) + } else if (match) { + dp[i][j] = dp[i + 1][j + 1] + } + } + } + return dp[0][0] + } +} +``` + +```swift +class Solution { + func isMatch(_ s: String, _ p: String) -> Bool { + let sArr = Array(s), pArr = Array(p) + let m = sArr.count, n = pArr.count + var dp = Array(repeating: Array(repeating: false, count: n + 1), count: m + 1) + dp[m][n] = true + + for i in stride(from: m, through: 0, by: -1) { + for j in stride(from: n - 1, through: 0, by: -1) { + let match = i < m && (sArr[i] == pArr[j] || pArr[j] == ".") + + if j + 1 < n && pArr[j + 1] == "*" { + dp[i][j] = dp[i][j + 2] + if match { + dp[i][j] = dp[i][j] || dp[i + 1][j] + } + } else if match { + dp[i][j] = dp[i + 1][j + 1] + } + } + } + + return dp[0][0] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the length of the string $s$ and $n$ is the length of the string $p$. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def isMatch(self, s: str, p: str) -> bool: + dp = [False] * (len(p) + 1) + dp[len(p)] = True + + for i in range(len(s), -1, -1): + nextDp = [False] * (len(p) + 1) + nextDp[len(p)] = (i == len(s)) + + for j in range(len(p) - 1, -1, -1): + match = i < len(s) and (s[i] == p[j] or p[j] == ".") + + if (j + 1) < len(p) and p[j + 1] == "*": + nextDp[j] = nextDp[j + 2] + if match: + nextDp[j] |= dp[j] + elif match: + nextDp[j] = dp[j + 1] + + dp = nextDp + + return dp[0] +``` + +```java +public class Solution { + public boolean isMatch(String s, String p) { + boolean[] dp = new boolean[p.length() + 1]; + dp[p.length()] = true; + + for (int i = s.length(); i >= 0; i--) { + boolean[] nextDp = new boolean[p.length() + 1]; + nextDp[p.length()] = (i == s.length()); + + for (int j = p.length() - 1; j >= 0; j--) { + boolean match = i < s.length() && + (s.charAt(i) == p.charAt(j) || + p.charAt(j) == '.'); + + if (j + 1 < p.length() && p.charAt(j + 1) == '*') { + nextDp[j] = nextDp[j + 2]; + if (match) { + nextDp[j] |= dp[j]; + } + } else if (match) { + nextDp[j] = dp[j + 1]; + } + } + + dp = nextDp; + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + bool isMatch(string s, string p) { + vector dp(p.length() + 1, false); + dp[p.length()] = true; + + for (int i = s.length(); i >= 0; i--) { + vector nextDp(p.length() + 1, false); + nextDp[p.length()] = (i == s.length()); + + for (int j = p.length() - 1; j >= 0; j--) { + bool match = i < s.length() && + (s[i] == p[j] || p[j] == '.'); + + if (j + 1 < p.length() && p[j + 1] == '*') { + nextDp[j] = nextDp[j + 2]; + if (match) { + nextDp[j] = nextDp[j] || dp[j]; + } + } else if (match) { + nextDp[j] = dp[j + 1]; + } + } + + dp = nextDp; + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} p + * @return {boolean} + */ + isMatch(s, p) { + let dp = new Array(p.length + 1).fill(false); + dp[p.length] = true; + + for (let i = s.length; i >= 0; i--) { + let nextDp = new Array(p.length + 1).fill(false); + nextDp[p.length] = (i === s.length); + + for (let j = p.length - 1; j >= 0; j--) { + const match = i < s.length && + (s[i] === p[j] || p[j] === "."); + + if (j + 1 < p.length && p[j + 1] === "*") { + nextDp[j] = nextDp[j + 2]; + if (match) { + nextDp[j] = nextDp[j] || dp[j]; + } + } else if (match) { + nextDp[j] = dp[j + 1]; + } + } + + dp = nextDp; + } + + return dp[0]; + } +} +``` + +```csharp +public class Solution { + public bool IsMatch(string s, string p) { + bool[] dp = new bool[p.Length + 1]; + dp[p.Length] = true; + + for (int i = s.Length; i >= 0; i--) { + bool[] nextDp = new bool[p.Length + 1]; + nextDp[p.Length] = (i == s.Length); + + for (int j = p.Length - 1; j >= 0; j--) { + bool match = i < s.Length && (s[i] == p[j] || p[j] == '.'); + + if (j + 1 < p.Length && p[j + 1] == '*') { + nextDp[j] = nextDp[j + 2]; + if (match) { + nextDp[j] |= dp[j]; + } + } else if (match) { + nextDp[j] = dp[j + 1]; + } + } + + dp = nextDp; + } + + return dp[0]; + } +} +``` + +```go +func isMatch(s, p string) bool { + m, n := len(s), len(p) + dp := make([]bool, n+1) + dp[n] = true + + for i := m; i >= 0; i-- { + nextDp := make([]bool, n+1) + nextDp[n] = i == m + + for j := n - 1; j >= 0; j-- { + match := i < m && (s[i] == p[j] || p[j] == '.') + + if j+1 < n && p[j+1] == '*' { + nextDp[j] = nextDp[j+2] || (match && dp[j]) + } else if match { + nextDp[j] = dp[j+1] + } + } + dp = nextDp + } + return dp[0] +} +``` + +```kotlin +class Solution { + fun isMatch(s: String, p: String): Boolean { + val m = s.length + val n = p.length + var dp = BooleanArray(n + 1) + dp[n] = true + + for (i in m downTo 0) { + val nextDp = BooleanArray(n + 1) + nextDp[n] = (i == m) + + for (j in n - 1 downTo 0) { + val match = i < m && (s[i] == p[j] || p[j] == '.') + + if (j + 1 < n && p[j + 1] == '*') { + nextDp[j] = nextDp[j + 2] || (match && dp[j]) + } else if (match) { + nextDp[j] = dp[j + 1] + } + } + dp = nextDp + } + return dp[0] + } +} +``` + +```swift +class Solution { + func isMatch(_ s: String, _ p: String) -> Bool { + let sArr = Array(s), pArr = Array(p) + var dp = Array(repeating: false, count: pArr.count + 1) + dp[pArr.count] = true + + for i in stride(from: sArr.count, through: 0, by: -1) { + var nextDp = Array(repeating: false, count: pArr.count + 1) + nextDp[pArr.count] = (i == sArr.count) + + for j in stride(from: pArr.count - 1, through: 0, by: -1) { + let match = i < sArr.count && (sArr[i] == pArr[j] || pArr[j] == ".") + + if j + 1 < pArr.count && pArr[j + 1] == "*" { + nextDp[j] = nextDp[j + 2] + if match { + nextDp[j] = nextDp[j] || dp[j] + } + } else if match { + nextDp[j] = dp[j + 1] + } + } + + dp = nextDp + } + + return dp[0] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(n)$ + +> Where $m$ is the length of the string $s$ and $n$ is the length of the string $p$. + +--- + +## 5. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def isMatch(self, s: str, p: str) -> bool: + dp = [False] * (len(p) + 1) + dp[len(p)] = True + + for i in range(len(s), -1, -1): + dp1 = dp[len(p)] + dp[len(p)] = (i == len(s)) + + for j in range(len(p) - 1, -1, -1): + match = i < len(s) and (s[i] == p[j] or p[j] == ".") + res = False + if (j + 1) < len(p) and p[j + 1] == "*": + res = dp[j + 2] + if match: + res |= dp[j] + elif match: + res = dp1 + dp[j], dp1 = res, dp[j] + + return dp[0] +``` + +```java +public class Solution { + public boolean isMatch(String s, String p) { + boolean[] dp = new boolean[p.length() + 1]; + dp[p.length()] = true; + + for (int i = s.length(); i >= 0; i--) { + boolean dp1 = dp[p.length()]; + dp[p.length()] = (i == s.length()); + + for (int j = p.length() - 1; j >= 0; j--) { + boolean match = i < s.length() && + (s.charAt(i) == p.charAt(j) || + p.charAt(j) == '.'); + boolean res = false; + if (j + 1 < p.length() && p.charAt(j + 1) == '*') { + res = dp[j + 2]; + if (match) { + res |= dp[j]; + } + } else if (match) { + res = dp1; + } + dp1 = dp[j]; + dp[j] = res; + } + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + bool isMatch(string s, string p) { + vector dp(p.length() + 1, false); + dp[p.length()] = true; + + for (int i = s.length(); i >= 0; i--) { + bool dp1 = dp[p.length()]; + dp[p.length()] = (i == s.length()); + + for (int j = p.length() - 1; j >= 0; j--) { + bool match = i < s.length() && + (s[i] == p[j] || p[j] == '.'); + bool res = false; + if (j + 1 < p.length() && p[j + 1] == '*') { + res = dp[j + 2]; + if (match) { + res = res || dp[j]; + } + } else if (match) { + res = dp1; + } + dp1 = dp[j]; + dp[j] = res; + } + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string} p + * @return {boolean} + */ + isMatch(s, p) { + let dp = new Array(p.length + 1).fill(false); + dp[p.length] = true; + + for (let i = s.length; i >= 0; i--) { + let dp1 = dp[p.length]; + dp[p.length] = (i == s.length); + + for (let j = p.length - 1; j >= 0; j--) { + const match = i < s.length && + (s[i] === p[j] || p[j] === "."); + let res = false; + if (j + 1 < p.length && p[j + 1] === "*") { + res = dp[j + 2]; + if (match) { + res = res || dp[j]; + } + } else if (match) { + res = dp1; + } + dp1 = dp[j]; + dp[j] = res; + } + } + + return dp[0]; + } +} +``` + +```csharp +public class Solution { + public bool IsMatch(string s, string p) { + bool[] dp = new bool[p.Length + 1]; + dp[p.Length] = true; + + for (int i = s.Length; i >= 0; i--) { + bool dp1 = dp[p.Length]; + dp[p.Length] = (i == s.Length); + + for (int j = p.Length - 1; j >= 0; j--) { + bool match = i < s.Length && (s[i] == p[j] || p[j] == '.'); + bool res = false; + if (j + 1 < p.Length && p[j + 1] == '*') { + res = dp[j + 2]; + if (match) { + res |= dp[j]; + } + } else if (match) { + res = dp1; + } + dp1 = dp[j]; + dp[j] = res; + } + } + + return dp[0]; + } +} +``` + +```go +func isMatch(s, p string) bool { + m, n := len(s), len(p) + dp := make([]bool, n+1) + dp[n] = true + + for i := m; i >= 0; i-- { + dp1 := dp[n] + dp[n] = (i == m) + + for j := n - 1; j >= 0; j-- { + match := i < m && (s[i] == p[j] || p[j] == '.') + res := false + if j+1 < n && p[j+1] == '*' { + res = dp[j+2] + if match { + res = res || dp[j] + } + } else if match { + res = dp1 + } + dp[j], dp1 = res, dp[j] + } + } + + return dp[0] +} +``` + +```kotlin +class Solution { + fun isMatch(s: String, p: String): Boolean { + val m = s.length + val n = p.length + var dp = BooleanArray(n + 1) + dp[n] = true + + for (i in m downTo 0) { + var dp1 = dp[n] + dp[n] = (i == m) + + for (j in n - 1 downTo 0) { + val match = i < m && (s[i] == p[j] || p[j] == '.') + var res = false + if (j + 1 < n && p[j + 1] == '*') { + res = dp[j + 2] + if (match) { + res = res || dp[j] + } + } else if (match) { + res = dp1 + } + dp1 = dp[j] + dp[j] = res + } + } + return dp[0] + } +} +``` + +```swift +class Solution { + func isMatch(_ s: String, _ p: String) -> Bool { + let sArr = Array(s) + let pArr = Array(p) + var dp = [Bool](repeating: false, count: pArr.count + 1) + dp[pArr.count] = true + + for i in stride(from: sArr.count, through: 0, by: -1) { + var dp1 = dp[pArr.count] + dp[pArr.count] = (i == sArr.count) + + for j in stride(from: pArr.count - 1, through: 0, by: -1) { + let match = i < sArr.count && (sArr[i] == pArr[j] || pArr[j] == ".") + var res = false + if j + 1 < pArr.count && pArr[j + 1] == "*" { + res = dp[j + 2] + if match { + res = res || dp[j] + } + } else if match { + res = dp1 + } + dp1 = dp[j] + dp[j] = res + } + } + + return dp[0] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(n)$ + +> Where $m$ is the length of the string $s$ and $n$ is the length of the string $p$. \ No newline at end of file diff --git a/articles/relative-sort-array.md b/articles/relative-sort-array.md new file mode 100644 index 000000000..8681e3fba --- /dev/null +++ b/articles/relative-sort-array.md @@ -0,0 +1,551 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def relativeSortArray(self, arr1: List[int], arr2: List[int]) -> List[int]: + res = [] + + for num2 in arr2: + for i, num1 in enumerate(arr1): + if num1 == num2: + res.append(num1) + arr1[i] = -1 + + arr1.sort() + for i in range(len(res), len(arr1)): + res.append(arr1[i]) + + return res +``` + +```java +public class Solution { + public int[] relativeSortArray(int[] arr1, int[] arr2) { + List res = new ArrayList<>(); + + for (int num2 : arr2) { + for (int i = 0; i < arr1.length; i++) { + if (arr1[i] == num2) { + res.add(arr1[i]); + arr1[i] = -1; + } + } + } + + Arrays.sort(arr1); + for (int i = res.size(); i < arr1.length; i++) { + res.add(arr1[i]); + } + + return res.stream().mapToInt(i -> i).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector relativeSortArray(vector& arr1, vector& arr2) { + vector res; + + for (int num2 : arr2) { + for (int i = 0; i < arr1.size(); i++) { + if (arr1[i] == num2) { + res.push_back(arr1[i]); + arr1[i] = -1; + } + } + } + + sort(arr1.begin(), arr1.end()); + for (int i = res.size(); i < arr1.size(); i++) { + res.push_back(arr1[i]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr1 + * @param {number[]} arr2 + * @return {number[]} + */ + relativeSortArray(arr1, arr2) { + const res = []; + + for (let num2 of arr2) { + for (let i = 0; i < arr1.length; i++) { + if (arr1[i] === num2) { + res.push(arr1[i]); + arr1[i] = -1; + } + } + } + + arr1.sort((a, b) => a - b); + for (let i = res.length; i < arr1.length; i++) { + res.push(arr1[i]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n + n \log n)$ +* Space complexity: + * $O(1)$ or $O(n)$ depending on the sorting algorithm. + * $O(n)$ space for the output list. + +> Where $n$ is the size of the array $arr1$, and $m$ is the size of the array $arr2$. + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def relativeSortArray(self, arr1: List[int], arr2: List[int]) -> List[int]: + arr2_set = set(arr2) + arr1_count = defaultdict(int) + end = [] + + for num in arr1: + if num not in arr2_set: + end.append(num) + arr1_count[num] += 1 + end.sort() + + res = [] + for num in arr2: + for _ in range(arr1_count[num]): + res.append(num) + return res + end +``` + +```java +public class Solution { + public int[] relativeSortArray(int[] arr1, int[] arr2) { + Set arr2Set = new HashSet<>(); + for (int num : arr2) arr2Set.add(num); + + Map count = new HashMap<>(); + List end = new ArrayList<>(); + for (int num : arr1) { + if (!arr2Set.contains(num)) end.add(num); + count.put(num, count.getOrDefault(num, 0) + 1); + } + Collections.sort(end); + + List res = new ArrayList<>(); + for (int num : arr2) { + int freq = count.get(num); + for (int i = 0; i < freq; i++) res.add(num); + } + res.addAll(end); + + return res.stream().mapToInt(i -> i).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector relativeSortArray(vector& arr1, vector& arr2) { + unordered_set arr2Set(arr2.begin(), arr2.end()); + unordered_map count; + vector end; + + for (int num : arr1) { + if (!arr2Set.count(num)) end.push_back(num); + count[num]++; + } + + sort(end.begin(), end.end()); + vector res; + + for (int num : arr2) { + for (int i = 0; i < count[num]; i++) { + res.push_back(num); + } + } + + res.insert(res.end(), end.begin(), end.end()); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr1 + * @param {number[]} arr2 + * @return {number[]} + */ + relativeSortArray(arr1, arr2) { + const arr2Set = new Set(arr2); + const count = {}; + const end = []; + + for (let num of arr1) { + if (!arr2Set.has(num)) end.push(num); + count[num] = (count[num] || 0) + 1; + } + + end.sort((a, b) => a - b); + const res = []; + + for (let num of arr2) { + for (let i = 0; i < count[num]; i++) { + res.push(num); + } + } + + return res.concat(end); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m + n \log n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $arr1$, and $m$ is the size of the array $arr2$. + +--- + +## 3. Hash Map (Optimal) + +::tabs-start + +```python +class Solution: + def relativeSortArray(self, arr1: List[int], arr2: List[int]) -> List[int]: + count = {} + for num in arr1: + count[num] = count.get(num, 0) + 1 + + res = [] + for num in arr2: + res += [num] * count.pop(num) + + for num in sorted(count): + res += [num] * count[num] + + return res +``` + +```java +public class Solution { + public int[] relativeSortArray(int[] arr1, int[] arr2) { + Map count = new HashMap<>(); + for (int num : arr1) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + List res = new ArrayList<>(); + for (int num : arr2) { + int freq = count.remove(num); + for (int i = 0; i < freq; i++) { + res.add(num); + } + } + + List remaining = new ArrayList<>(count.keySet()); + Collections.sort(remaining); + for (int num : remaining) { + int freq = count.get(num); + for (int i = 0; i < freq; i++) { + res.add(num); + } + } + + return res.stream().mapToInt(i -> i).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector relativeSortArray(vector& arr1, vector& arr2) { + unordered_map count; + for (int num : arr1) { + count[num]++; + } + + vector res; + for (int num : arr2) { + for (int i = 0; i < count[num]; i++) { + res.push_back(num); + } + count.erase(num); + } + + vector remaining; + for (auto& [num, freq] : count) { + for (int i = 0; i < freq; i++) { + remaining.push_back(num); + } + } + + sort(remaining.begin(), remaining.end()); + res.insert(res.end(), remaining.begin(), remaining.end()); + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr1 + * @param {number[]} arr2 + * @return {number[]} + */ + relativeSortArray(arr1, arr2) { + const count = {}; + for (let num of arr1) { + count[num] = (count[num] || 0) + 1; + } + + const res = []; + for (let num of arr2) { + for (let i = 0; i < count[num]; i++) { + res.push(num); + } + delete count[num]; + } + + const remaining = Object.keys(count).map(Number).sort((a, b) => a - b); + for (let num of remaining) { + for (let i = 0; i < count[num]; i++) { + res.push(num); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m + n \log n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $arr1$, and $m$ is the size of the array $arr2$. + +--- + +## 4. Counting Sort + +::tabs-start + +```python +class Solution: + def relativeSortArray(self, arr1: List[int], arr2: List[int]) -> List[int]: + max_val = max(arr1) + count = [0] * (max_val + 1) + + for num in arr1: + count[num] += 1 + + res = [] + for num in arr2: + res += [num] * count[num] + count[num] = 0 + + for num in range(len(count)): + res += [num] * count[num] + + return res +``` + +```java +public class Solution { + public int[] relativeSortArray(int[] arr1, int[] arr2) { + int max = 0; + for (int num : arr1) max = Math.max(max, num); + + int[] count = new int[max + 1]; + for (int num : arr1) count[num]++; + + List res = new ArrayList<>(); + for (int num : arr2) { + while (count[num]-- > 0) res.add(num); + } + + for (int num = 0; num < count.length; num++) { + while (count[num]-- > 0) res.add(num); + } + + return res.stream().mapToInt(i -> i).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector relativeSortArray(vector& arr1, vector& arr2) { + int max_val = *max_element(arr1.begin(), arr1.end()); + vector count(max_val + 1, 0); + + for (int num : arr1) count[num]++; + + vector res; + for (int num : arr2) { + while (count[num]-- > 0) res.push_back(num); + } + + for (int num = 0; num <= max_val; num++) { + while (count[num]-- > 0) res.push_back(num); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr1 + * @param {number[]} arr2 + * @return {number[]} + */ + relativeSortArray(arr1, arr2) { + let max = Math.max(...arr1); + let count = new Array(max + 1).fill(0); + + for (let num of arr1) count[num]++; + + let res = []; + for (let num of arr2) { + while (count[num]-- > 0) res.push(num); + } + + for (let num = 0; num < count.length; num++) { + while (count[num]-- > 0) res.push(num); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m + M)$ +* Space complexity: + * $O(M)$ extra space. + * $O(n)$ space for the output list. + +> Where $n$ is the size of the array $arr1$, $m$ is the size of the array $arr2$, and $M$ is the maximum value in the array $arr1$. + +--- + +## 5. Custom Sort + +::tabs-start + +```python +class Solution: + def relativeSortArray(self, arr1: List[int], arr2: List[int]) -> List[int]: + index = {num: i for i, num in enumerate(arr2)} + return sorted(arr1, key=lambda x: (index.get(x, 1000 + x))) +``` + +```java +public class Solution { + public int[] relativeSortArray(int[] arr1, int[] arr2) { + Map index = new HashMap<>(); + for (int i = 0; i < arr2.length; i++) { + index.put(arr2[i], i); + } + + Integer[] boxed = Arrays.stream(arr1).boxed().toArray(Integer[]::new); + Arrays.sort(boxed, (a, b) -> { + int ia = index.getOrDefault(a, 1000 + a); + int ib = index.getOrDefault(b, 1000 + b); + return Integer.compare(ia, ib); + }); + + return Arrays.stream(boxed).mapToInt(i -> i).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector relativeSortArray(vector& arr1, vector& arr2) { + unordered_map index; + for (int i = 0; i < arr2.size(); i++) { + index[arr2[i]] = i; + } + + sort(arr1.begin(), arr1.end(), [&](int a, int b) { + int ia = index.count(a) ? index[a] : 1000 + a; + int ib = index.count(b) ? index[b] : 1000 + b; + return ia < ib; + }); + + return arr1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr1 + * @param {number[]} arr2 + * @return {number[]} + */ + relativeSortArray(arr1, arr2) { + const index = new Map(); + arr2.forEach((num, i) => index.set(num, i)); + + return arr1.sort((a, b) => { + const ia = index.has(a) ? index.get(a) : 1000 + a; + const ib = index.has(b) ? index.get(b) : 1000 + b; + return ia - ib; + }); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m + n \log n)$ +* Space complexity: + * $O(m)$ extra space. + * $O(n)$ space for the output list. + +> Where $n$ is the size of the array $arr1$, and $m$ is the size of the array $arr2$. \ No newline at end of file diff --git a/articles/remove-all-adjacent-duplicates-in-string-ii.md b/articles/remove-all-adjacent-duplicates-in-string-ii.md new file mode 100644 index 000000000..7c4ae8a82 --- /dev/null +++ b/articles/remove-all-adjacent-duplicates-in-string-ii.md @@ -0,0 +1,467 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def removeDuplicates(self, s: str, k: int) -> str: + while True: + flag = False + cur = s[0] + cnt = 1 + for i in range(1, len(s)): + if cur != s[i]: + cnt = 0 + cur = s[i] + cnt += 1 + if cnt == k: + s = s[:i - cnt + 1] + s[i + 1:] + flag = True + break + + if not flag: + break + + return s +``` + +```java +public class Solution { + public String removeDuplicates(String s, int k) { + while (true) { + boolean flag = false; + char cur = s.charAt(0); + int cnt = 1; + + for (int i = 1; i < s.length(); i++) { + if (cur != s.charAt(i)) { + cnt = 0; + cur = s.charAt(i); + } + cnt++; + if (cnt == k) { + s = s.substring(0, i - cnt + 1) + s.substring(i + 1); + flag = true; + break; + } + } + + if (!flag) { + break; + } + } + + return s; + } +} +``` + +```cpp +class Solution { +public: + string removeDuplicates(string s, int k) { + while (true) { + bool flag = false; + char cur = s[0]; + int cnt = 1; + + for (int i = 1; i < s.size(); i++) { + if (cur != s[i]) { + cnt = 0; + cur = s[i]; + } + cnt++; + if (cnt == k) { + s = s.substr(0, i - cnt + 1) + s.substr(i + 1); + flag = true; + break; + } + } + + if (!flag) { + break; + } + } + + return s; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {string} + */ + removeDuplicates(s, k) { + while (true) { + let flag = false; + let cur = s[0]; + let cnt = 1; + + for (let i = 1; i < s.length; i++) { + if (cur !== s[i]) { + cnt = 0; + cur = s[i]; + } + cnt++; + if (cnt === k) { + s = s.slice(0, i - cnt + 1) + s.slice(i + 1); + flag = true; + break; + } + } + + if (!flag) { + break; + } + } + + return s; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\frac {n ^ 2}{k})$ +* Space complexity: $O(n)$ + +--- + +## 2. Stack + +::tabs-start + +```python +class Solution: + def removeDuplicates(self, s: str, k: int) -> str: + stack = [] + n = len(s) + i = 0 + s = list(s) + + while i < n: + if i == 0 or s[i] != s[i - 1]: + stack.append(1) + else: + stack[-1] += 1 + if stack[-1] == k: + stack.pop() + del s[i - k + 1:i + 1] + i -= k + n -= k + + i += 1 + + return ''.join(s) +``` + +```java +public class Solution { + public String removeDuplicates(String s, int k) { + StringBuilder sb = new StringBuilder(s); + Stack stack = new Stack<>(); + int i = 0; + + while (i < sb.length()) { + if (i == 0 || sb.charAt(i) != sb.charAt(i - 1)) { + stack.push(1); + } else { + stack.push(stack.pop() + 1); + if (stack.peek() == k) { + stack.pop(); + sb.delete(i - k + 1, i + 1); + i -= k; + } + } + i++; + } + + return sb.toString(); + } +} +``` + +```cpp +class Solution { +public: + string removeDuplicates(string s, int k) { + vector stack; + int n = s.length(), i = 0; + + while (i < n) { + if (i == 0 || s[i] != s[i - 1]) { + stack.push_back(1); + } else { + stack.back()++; + if (stack.back() == k) { + stack.pop_back(); + s.erase(i - k + 1, k); + i -= k; + n -= k; + } + } + i++; + } + + return s; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {string} + */ + removeDuplicates(s, k) { + let stack = []; + let arr = s.split(''); + let i = 0; + + while (i < arr.length) { + if (i === 0 || arr[i] !== arr[i - 1]) { + stack.push(1); + } else { + stack[stack.length - 1]++; + if (stack[stack.length - 1] === k) { + stack.pop(); + arr.splice(i - k + 1, k); + i -= k; + } + } + i++; + } + + return arr.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Stack (Optimal) + +::tabs-start + +```python +class Solution: + def removeDuplicates(self, s: str, k: int) -> str: + stack = [] # [char, count] + + for c in s: + if stack and stack[-1][0] == c: + stack[-1][1] += 1 + else: + stack.append([c, 1]) + if stack[-1][1] == k: + stack.pop() + + return ''.join(char * count for char, count in stack) +``` + +```java +public class Solution { + public String removeDuplicates(String s, int k) { + Stack stack = new Stack<>(); // [char, count] + + for (char c : s.toCharArray()) { + if (!stack.isEmpty() && stack.peek()[0] == c) { + stack.peek()[1]++; + } else { + stack.push(new int[] {c, 1}); + } + if (stack.peek()[1] == k) { + stack.pop(); + } + } + + StringBuilder res = new StringBuilder(); + while (!stack.isEmpty()) { + int[] top = stack.pop(); + res.append(String.valueOf((char) top[0]).repeat(top[1])); + } + + return res.reverse().toString(); + } +} +``` + +```cpp +class Solution { +public: + string removeDuplicates(string s, int k) { + vector> stack; + + for (char c : s) { + if (!stack.empty() && stack.back().first == c) { + stack.back().second++; + } else { + stack.push_back({c, 1}); + } + if (stack.back().second == k) { + stack.pop_back(); + } + } + + string res; + for (auto& p : stack) { + res.append(p.second, p.first); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {string} + */ + removeDuplicates(s, k) { + const stack = []; // [char, count] + + for (const c of s) { + if (stack.length && stack[stack.length - 1][0] === c) { + stack[stack.length - 1][1]++; + } else { + stack.push([c, 1]); + } + if (stack[stack.length - 1][1] === k) { + stack.pop(); + } + } + + let res = ""; + for (const [char, count] of stack) { + res += char.repeat(count); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Two Pointers + +::tabs-start + +```python +class Solution: + def removeDuplicates(self, s: str, k: int) -> str: + i = 0 + n = len(s) + count = [0] * n + s = list(s) + for j in range(n): + s[i] = s[j] + count[i] = 1 + if i > 0 and s[i - 1] == s[j]: + count[i] += count[i - 1] + if count[i] == k: + i -= k + i += 1 + return ''.join(s[:i]) +``` + +```java +public class Solution { + public String removeDuplicates(String s, int k) { + int i = 0, n = s.length(); + char[] str = s.toCharArray(); + int[] count = new int[n]; + for (int j = 0; j < n; j++) { + str[i] = str[j]; + count[i] = 1; + if (i > 0 && str[i - 1] == str[j]) { + count[i] += count[i - 1]; + } + if (count[i] == k) { + i -= k; + } + i++; + } + return new String(str, 0, i); + } +} +``` + +```cpp +class Solution { +public: + string removeDuplicates(string s, int k) { + int i = 0, n = s.length(); + vector count(n); + for (int j = 0; j < n; i++, j++) { + s[i] = s[j]; + count[i] = 1; + if (i > 0 && s[i - 1] == s[j]) { + count[i] += count[i - 1]; + } + if (count[i] == k) i -= k; + } + return s.substr(0, i); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {string} + */ + removeDuplicates(s, k) { + let i = 0; + const n = s.length; + const count = new Array(n).fill(0); + s = s.split(''); + for (let j = 0; j < n; j++) { + s[i] = s[j]; + count[i] = 1; + if (i > 0 && s[i - 1] === s[j]) { + count[i] += count[i - 1]; + } + if (count[i] === k) { + i -= k; + } + i++; + } + return s.slice(0, i).join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/remove-colored-pieces-if-both-neighbors-are-the-same-color.md b/articles/remove-colored-pieces-if-both-neighbors-are-the-same-color.md new file mode 100644 index 000000000..156cbb636 --- /dev/null +++ b/articles/remove-colored-pieces-if-both-neighbors-are-the-same-color.md @@ -0,0 +1,322 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def winnerOfGame(self, colors: str) -> bool: + s = list(colors) + + def removeChar(c): + for i in range(1, len(s) - 1): + if s[i] != c: + continue + + if s[i] == s[i + 1] == s[i - 1]: + s.pop(i) + return True + return False + + while True: + if not removeChar('A'): + return False + if not removeChar('B'): + return True + + return False +``` + +```java +public class Solution { + public boolean winnerOfGame(String colors) { + StringBuilder s = new StringBuilder(colors); + + while (true) { + if (!removeChar(s, 'A')) return false; + if (!removeChar(s, 'B')) return true; + } + } + + private boolean removeChar(StringBuilder s, char c) { + for (int i = 1; i < s.length() - 1; i++) { + if (s.charAt(i) != c) continue; + + if (s.charAt(i - 1) == c && s.charAt(i + 1) == c) { + s.deleteCharAt(i); + return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool winnerOfGame(string colors) { + while (true) { + if (!removeChar(colors, 'A')) return false; + if (!removeChar(colors, 'B')) return true; + } + } + +private: + bool removeChar(string& s, char c) { + for (int i = 1; i < s.size() - 1; i++) { + if (s[i] != c) continue; + + if (s[i - 1] == c && s[i + 1] == c) { + s.erase(i, 1); + return true; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} colors + * @return {boolean} + */ + winnerOfGame(colors) { + let s = colors.split(""); + + const removeChar = (c) => { + for (let i = 1; i < s.length - 1; i++) { + if (s[i] !== c) continue; + + if (s[i - 1] === c && s[i + 1] === c) { + s.splice(i, 1); + return true; + } + } + return false; + }; + + while (true) { + if (!removeChar('A')) return false; + if (!removeChar('B')) return true; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Greedy + Two Pointers + +::tabs-start + +```python +class Solution: + def winnerOfGame(self, colors: str) -> bool: + alice = bob = l = 0 + + for r in range(len(colors)): + if colors[l] != colors[r]: + l = r + + extra = r - l - 1 + if extra > 0: + if colors[l] == 'A': + alice += 1 + else: + bob += 1 + + return alice > bob +``` + +```java +public class Solution { + public boolean winnerOfGame(String colors) { + int alice = 0, bob = 0, l = 0; + + for (int r = 0; r < colors.length(); r++) { + if (colors.charAt(l) != colors.charAt(r)) { + l = r; + } + + int extra = r - l - 1; + if (extra > 0) { + if (colors.charAt(l) == 'A') { + alice++; + } else { + bob++; + } + } + } + + return alice > bob; + } +} +``` + +```cpp +class Solution { +public: + bool winnerOfGame(string colors) { + int alice = 0, bob = 0, l = 0; + + for (int r = 0; r < colors.size(); r++) { + if (colors[l] != colors[r]) { + l = r; + } + + int extra = r - l - 1; + if (extra > 0) { + if (colors[l] == 'A') { + alice++; + } else { + bob++; + } + } + } + + return alice > bob; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} colors + * @return {boolean} + */ + winnerOfGame(colors) { + let alice = 0, bob = 0, l = 0; + + for (let r = 0; r < colors.length; r++) { + if (colors[l] !== colors[r]) { + l = r; + } + + let extra = r - l - 1; + if (extra > 0) { + if (colors[l] === 'A') { + alice++; + } else { + bob++; + } + } + } + + return alice > bob; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Greedy + +::tabs-start + +```python +class Solution: + def winnerOfGame(self, colors: str) -> bool: + alice, bob = 0, 0 + + for i in range(1, len(colors) - 1): + if colors[i - 1] == colors[i] == colors[i + 1]: + if colors[i] == 'A': + alice += 1 + if colors[i] == 'B': + bob += 1 + + return alice > bob +``` + +```java +public class Solution { + public boolean winnerOfGame(String colors) { + int alice = 0, bob = 0; + + for (int i = 1; i < colors.length() - 1; i++) { + if (colors.charAt(i - 1) == colors.charAt(i) && + colors.charAt(i) == colors.charAt(i + 1)) { + if (colors.charAt(i) == 'A') { + alice++; + } + if (colors.charAt(i) == 'B') { + bob++; + } + } + } + + return alice > bob; + } +} +``` + +```cpp +class Solution { +public: + bool winnerOfGame(string colors) { + int alice = 0, bob = 0; + + for (int i = 1; i < colors.size() - 1; i++) { + if (colors[i - 1] == colors[i] && colors[i] == colors[i + 1]) { + if (colors[i] == 'A') { + alice++; + } + if (colors[i] == 'B') { + bob++; + } + } + } + + return alice > bob; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} colors + * @return {boolean} + */ + winnerOfGame(colors) { + let alice = 0, bob = 0; + + for (let i = 1; i < colors.length - 1; i++) { + if (colors[i - 1] === colors[i] && colors[i] === colors[i + 1]) { + if (colors[i] === 'A') { + alice++; + } + if (colors[i] === 'B') { + bob++; + } + } + } + + return alice > bob; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/remove-covered-intervals.md b/articles/remove-covered-intervals.md new file mode 100644 index 000000000..f71a0cfcc --- /dev/null +++ b/articles/remove-covered-intervals.md @@ -0,0 +1,275 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def removeCoveredIntervals(self, intervals: List[List[int]]) -> int: + n = len(intervals) + res = n + + for i in range(n): + for j in range(n): + if (i != j and intervals[j][0] <= intervals[i][0] and + intervals[j][1] >= intervals[i][1] + ): + res -= 1 + break + + return res +``` + +```java +public class Solution { + public int removeCoveredIntervals(int[][] intervals) { + int n = intervals.length; + int res = n; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (i != j && intervals[j][0] <= intervals[i][0] && + intervals[j][1] >= intervals[i][1]) { + res--; + break; + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int removeCoveredIntervals(vector>& intervals) { + int n = intervals.size(); + int res = n; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (i != j && intervals[j][0] <= intervals[i][0] && + intervals[j][1] >= intervals[i][1]) { + res--; + break; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} intervals + * @return {number} + */ + removeCoveredIntervals(intervals) { + let n = intervals.length; + let res = n; + + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + if (i !== j && intervals[j][0] <= intervals[i][0] && + intervals[j][1] >= intervals[i][1]) { + res--; + break; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Sorting - I + +::tabs-start + +```python +class Solution: + def removeCoveredIntervals(self, intervals: List[List[int]]) -> int: + intervals.sort(key=lambda x: (x[0], -x[1])) + res = 1 + prevL, prevR = intervals[0][0], intervals[0][1] + for l, r in intervals: + if prevL <= l and prevR >= r: + continue + res += 1 + prevL, prevR = l, r + + return res +``` + +```java +public class Solution { + public int removeCoveredIntervals(int[][] intervals) { + Arrays.sort(intervals, (a, b) -> + a[0] == b[0] ? Integer.compare(b[1], a[1]) : Integer.compare(a[0], b[0]) + ); + int res = 1, prevL = intervals[0][0], prevR = intervals[0][1]; + for (int[] interval : intervals) { + int l = interval[0], r = interval[1]; + if (prevL <= l && prevR >= r) { + continue; + } + res++; + prevL = l; + prevR = r; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int removeCoveredIntervals(vector>& intervals) { + sort(intervals.begin(), intervals.end(), [](const auto& a, const auto& b) { + return a[0] == b[0] ? b[1] < a[1] : a[0] < b[0]; + }); + + int res = 1, prevL = intervals[0][0], prevR = intervals[0][1]; + for (const auto& interval : intervals) { + int l = interval[0], r = interval[1]; + if (prevL <= l && prevR >= r) { + continue; + } + res++; + prevL = l; + prevR = r; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} intervals + * @return {number} + */ + removeCoveredIntervals(intervals) { + intervals.sort((a, b) => a[0] === b[0] ? b[1] - a[1] : a[0] - b[0]); + let res = 1, [prevL, prevR] = intervals[0]; + for (const [l, r] of intervals) { + if (prevL <= l && prevR >= r) { + continue; + } + res++; + prevL = l; + prevR = r; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n\log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Sorting - II + +::tabs-start + +```python +class Solution: + def removeCoveredIntervals(self, intervals: List[List[int]]) -> int: + intervals.sort() + res, start, end = 1, intervals[0][0], intervals[0][1] + + for l, r in intervals: + if start < l and end < r: + start = l + res += 1 + end = max(end, r) + + return res +``` + +```java +public class Solution { + public int removeCoveredIntervals(int[][] intervals) { + Arrays.sort(intervals, (a, b) -> a[0] - b[0]); + int res = 1, start = intervals[0][0], end = intervals[0][1]; + + for (int[] interval : intervals) { + int l = interval[0], r = interval[1]; + if (start < l && end < r) { + start = l; + res++; + } + end = Math.max(end, r); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int removeCoveredIntervals(vector>& intervals) { + sort(intervals.begin(), intervals.end()); + int res = 1, start = intervals[0][0], end = intervals[0][1]; + + for (const auto& interval : intervals) { + int l = interval[0], r = interval[1]; + if (start < l && end < r) { + start = l; + res++; + } + end = max(end, r); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} intervals + * @return {number} + */ + removeCoveredIntervals(intervals) { + intervals.sort((a, b) => a[0] - b[0]); + let res = 1, start = intervals[0][0], end = intervals[0][1]; + + for (const [l, r] of intervals) { + if (start < l && end < r) { + start = l; + res++; + } + end = Math.max(end, r); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file diff --git a/articles/remove-duplicates-from-sorted-array-ii.md b/articles/remove-duplicates-from-sorted-array-ii.md new file mode 100644 index 000000000..8fff8a2d4 --- /dev/null +++ b/articles/remove-duplicates-from-sorted-array-ii.md @@ -0,0 +1,421 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def removeDuplicates(self, nums: List[int]) -> int: + n = len(nums) + if n <= 2: + return n + i = 0 + while i < n - 1: + if nums[i] == nums[i + 1]: + j = i + 2 + cnt = 0 + while j < n and nums[i] == nums[j]: + j += 1 + cnt += 1 + for k in range(i + 2, n): + if j >= n: + break + nums[k] = nums[j] + j += 1 + n -= cnt + i += 2 + else: + i += 1 + return n +``` + +```java +public class Solution { + public int removeDuplicates(int[] nums) { + int n = nums.length; + if (n <= 2) return n; + int i = 0; + while (i < n - 1) { + if (nums[i] == nums[i + 1]) { + int j = i + 2, cnt = 0; + while (j < n && nums[i] == nums[j]) { + j++; + cnt++; + } + for (int k = i + 2; k < n; k++) { + if (j >= n) break; + nums[k] = nums[j++]; + } + n -= cnt; + i += 2; + } else { + i++; + } + } + return n; + } +} +``` + +```cpp +class Solution { +public: + int removeDuplicates(vector& nums) { + int n = nums.size(); + if (n <= 2) return n; + int i = 0; + while (i < n - 1) { + if (nums[i] == nums[i + 1]) { + int j = i + 2, cnt = 0; + while (j < n && nums[i] == nums[j]) { + j++; + cnt++; + } + for (int k = i + 2; k < n; k++) { + if (j >= n) break; + nums[k] = nums[j++]; + } + n -= cnt; + i += 2; + } else { + i++; + } + } + return n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + removeDuplicates(nums) { + let n = nums.length; + if (n <= 2) return n; + let i = 0; + while (i < n - 1) { + if (nums[i] === nums[i + 1]) { + let j = i + 2, cnt = 0; + while (j < n && nums[i] === nums[j]) { + j++; + cnt++; + } + for (let k = i + 2; k < n; k++) { + if (j >= n) break; + nums[k] = nums[j++]; + } + n -= cnt; + i += 2; + } else { + i++; + } + } + return n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def removeDuplicates(self, nums: List[int]) -> int: + n = len(nums) + if n <= 2: + return n + + count = Counter(nums) + i = 0 + for num in count: + nums[i] = num + count[num] -= 1 + i += 1 + if count[num] >= 1: + nums[i] = num + count[num] -= 1 + i += 1 + return i +``` + +```java +public class Solution { + public int removeDuplicates(int[] nums) { + Map count = new HashMap<>(); + List arr = new ArrayList<>(); + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + if (count.get(num) == 1) { + arr.add(num); + } + } + + int i = 0; + for (int num : arr) { + nums[i++] = num; + count.put(num, count.get(num) - 1); + if (count.get(num) >= 1) { + nums[i++] = num; + count.put(num, count.get(num) - 1); + } + } + return i; + } +} +``` + +```cpp +class Solution { +public: + int removeDuplicates(vector& nums) { + unordered_map count; + vector arr; + for (int& num : nums) { + count[num]++; + if (count[num] == 1) { + arr.push_back(num); + } + } + + int i = 0; + for (auto& num : arr) { + int& cnt = count[num]; + nums[i++] = num; + cnt--; + if (cnt >= 1) { + nums[i++] = num; + cnt--; + } + } + return i; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + removeDuplicates(nums) { + const count = new Map(); + for (const num of nums) { + count.set(num, (count.get(num) || 0) + 1); + } + + let i = 0; + for (const [num, cnt] of count) { + nums[i++] = num; + count.set(num, cnt - 1); + if (count.get(num) >= 1) { + nums[i++] = num; + count.set(num, cnt - 1); + } + } + return i; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Two Pointers + +::tabs-start + +```python +class Solution: + def removeDuplicates(self, nums: List[int]) -> int: + l, r = 0, 0 + + while r < len(nums): + count = 1 + while r + 1 < len(nums) and nums[r] == nums[r + 1]: + r += 1 + count += 1 + + for i in range(min(2, count)): + nums[l] = nums[r] + l += 1 + r += 1 + + return l +``` + +```java +public class Solution { + public int removeDuplicates(int[] nums) { + int l = 0, r = 0; + + while (r < nums.length) { + int count = 1; + while (r + 1 < nums.length && nums[r] == nums[r + 1]) { + r++; + count++; + } + + for (int i = 0; i < Math.min(2, count); i++) { + nums[l] = nums[r]; + l++; + } + r++; + } + + return l; + } +} +``` + +```cpp +class Solution { +public: + int removeDuplicates(vector& nums) { + int l = 0, r = 0; + + while (r < nums.size()) { + int count = 1; + while (r + 1 < nums.size() && nums[r] == nums[r + 1]) { + r++; + count++; + } + + for (int i = 0; i < min(2, count); i++) { + nums[l] = nums[r]; + l++; + } + r++; + } + + return l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + removeDuplicates(nums) { + let l = 0, r = 0; + + while (r < nums.length) { + let count = 1; + while (r + 1 < nums.length && nums[r] === nums[r + 1]) { + r++; + count++; + } + + for (let i = 0; i < Math.min(2, count); i++) { + nums[l] = nums[r]; + l++; + } + r++; + } + + return l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Two Pointers (Optimal) + +::tabs-start + +```python +class Solution: + def removeDuplicates(self, nums: List[int]) -> int: + l = 0 + for num in nums: + if l < 2 or num != nums[l - 2]: + nums[l] = num + l += 1 + return l +``` + +```java +public class Solution { + public int removeDuplicates(int[] nums) { + int l = 0; + for (int num : nums) { + if (l < 2 || num != nums[l - 2]) { + nums[l] = num; + l++; + } + } + return l; + } +} +``` + +```cpp +class Solution { +public: + int removeDuplicates(vector& nums) { + int l = 0; + for (int num : nums) { + if (l < 2 || num != nums[l - 2]) { + nums[l] = num; + l++; + } + } + return l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + removeDuplicates(nums) { + let l = 0; + for (let num of nums) { + if (l < 2 || num !== nums[l - 2]) { + nums[l] = num; + l++; + } + } + return l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/remove-duplicates-from-sorted-array.md b/articles/remove-duplicates-from-sorted-array.md new file mode 100644 index 000000000..7629a2ff3 --- /dev/null +++ b/articles/remove-duplicates-from-sorted-array.md @@ -0,0 +1,259 @@ +## 1. Sorted Set + +::tabs-start + +```python +class Solution: + def removeDuplicates(self, nums: list[int]) -> int: + unique = sorted(set(nums)) + nums[:len(unique)] = unique + return len(unique) +``` + +```java +public class Solution { + public int removeDuplicates(int[] nums) { + TreeSet unique = new TreeSet<>(); + for (int num : nums) { + unique.add(num); + } + int i = 0; + for (int num : unique) { + nums[i++] = num; + } + return unique.size(); + } +} +``` + +```cpp +class Solution { +public: + int removeDuplicates(vector& nums) { + set unique(nums.begin(), nums.end()); + int i = 0; + for (int num : unique) { + nums[i++] = num; + } + return unique.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + removeDuplicates(nums) { + const unique = Array.from(new Set(nums)).sort((a, b) => a - b); + for (let i = 0; i < unique.length; i++) { + nums[i] = unique[i]; + } + return unique.length; + } +} +``` + +```csharp +public class Solution { + public int RemoveDuplicates(int[] nums) { + int[] unique = nums.Distinct().OrderBy(x => x).ToArray(); + Array.Copy(unique, nums, unique.Length); + return unique.Length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Two Pointers - I + +::tabs-start + +```python +class Solution: + def removeDuplicates(self, nums: list[int]) -> int: + n = len(nums) + l = r = 0 + while r < n: + nums[l] = nums[r] + while r < n and nums[r] == nums[l]: + r += 1 + l += 1 + return l +``` + +```java +public class Solution { + public int removeDuplicates(int[] nums) { + int n = nums.length, l = 0, r = 0; + while (r < n) { + nums[l] = nums[r]; + while (r < n && nums[r] == nums[l]) { + r++; + } + l++; + } + return l; + } +} +``` + +```cpp +class Solution { +public: + int removeDuplicates(vector& nums) { + int n = nums.size(), l = 0, r = 0; + while (r < n) { + nums[l] = nums[r]; + while (r < n && nums[r] == nums[l]) { + r++; + } + l++; + } + return l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + removeDuplicates(nums) { + let n = nums.length, l = 0, r = 0; + while (r < n) { + nums[l] = nums[r]; + while (r < n && nums[r] === nums[l]) { + r++; + } + l++; + } + return l; + } +} +``` + +```csharp +public class Solution { + public int RemoveDuplicates(int[] nums) { + int n = nums.Length; + int l = 0, r = 0; + + while (r < n) { + nums[l] = nums[r]; + while (r < n && nums[r] == nums[l]) { + r++; + } + l++; + } + + return l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Two Pointers - II + +::tabs-start + +```python +class Solution: + def removeDuplicates(self, nums: list[int]) -> int: + l = 1 + for r in range(1, len(nums)): + if nums[r] != nums[r - 1]: + nums[l] = nums[r] + l += 1 + return l +``` + +```java +public class Solution { + public int removeDuplicates(int[] nums) { + int l = 1; + for (int r = 1; r < nums.length; r++) { + if (nums[r] != nums[r - 1]) { + nums[l++] = nums[r]; + } + } + return l; + } +} +``` + +```cpp +class Solution { +public: + int removeDuplicates(vector& nums) { + int l = 1; + for (int r = 1; r < nums.size(); r++) { + if (nums[r] != nums[r - 1]) { + nums[l++] = nums[r]; + } + } + return l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + removeDuplicates(nums) { + let l = 1; + for (let r = 1; r < nums.length; r++) { + if (nums[r] !== nums[r - 1]) { + nums[l++] = nums[r]; + } + } + return l; + } +} +``` + +```csharp +public class Solution { + public int RemoveDuplicates(int[] nums) { + int l = 1; + for (int r = 1; r < nums.Length; r++) { + if (nums[r] != nums[r - 1]) { + nums[l] = nums[r]; + l++; + } + } + + return l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/remove-duplicates-from-sorted-list.md b/articles/remove-duplicates-from-sorted-list.md new file mode 100644 index 000000000..9184c3e9c --- /dev/null +++ b/articles/remove-duplicates-from-sorted-list.md @@ -0,0 +1,312 @@ +## 1. Recursion + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + if not head or not head.next: + return head + + head.next = self.deleteDuplicates(head.next) + return head if head.val != head.next.val else head.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode deleteDuplicates(ListNode head) { + if (head == null || head.next == null) return head; + + head.next = deleteDuplicates(head.next); + return head.val != head.next.val ? head : head.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* deleteDuplicates(ListNode* head) { + if (!head || !head->next) return head; + + head->next = deleteDuplicates(head->next); + return head->val != head->next->val ? head : head->next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + deleteDuplicates(head) { + if (!head || !head.next) return head; + + head.next = this.deleteDuplicates(head.next); + return head.val !== head.next.val ? head : head.next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Iteration - I + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + cur = head + while cur: + while cur.next and cur.next.val == cur.val: + cur.next = cur.next.next + cur = cur.next + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode deleteDuplicates(ListNode head) { + ListNode cur = head; + while (cur != null) { + while (cur.next != null && cur.next.val == cur.val) { + cur.next = cur.next.next; + } + cur = cur.next; + } + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* deleteDuplicates(ListNode* head) { + ListNode* cur = head; + while (cur) { + while (cur->next && cur->next->val == cur->val) { + cur->next = cur->next->next; + } + cur = cur->next; + } + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + deleteDuplicates(head) { + let cur = head; + while (cur) { + while (cur.next && cur.next.val === cur.val) { + cur.next = cur.next.next; + } + cur = cur.next; + } + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Iteration - II + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + cur = head + while cur and cur.next: + if cur.val == cur.next.val: + cur.next = cur.next.next + else: + cur = cur.next + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode deleteDuplicates(ListNode head) { + ListNode cur = head; + while (cur != null && cur.next != null) { + if (cur.next.val == cur.val) { + cur.next = cur.next.next; + } else { + cur = cur.next; + } + } + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* deleteDuplicates(ListNode* head) { + ListNode* cur = head; + while (cur && cur->next) { + if (cur->next->val == cur->val) { + cur->next = cur->next->next; + } else { + cur = cur->next; + } + } + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + deleteDuplicates(head) { + let cur = head; + while (cur && cur.next) { + if (cur.next.val === cur.val) { + cur.next = cur.next.next; + } else { + cur = cur.next; + } + } + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/remove-element.md b/articles/remove-element.md new file mode 100644 index 000000000..14d2c78b4 --- /dev/null +++ b/articles/remove-element.md @@ -0,0 +1,282 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def removeElement(self, nums: list[int], val: int) -> int: + tmp = [] + for num in nums: + if num == val: + continue + tmp.append(num) + for i in range(len(tmp)): + nums[i] = tmp[i] + return len(tmp) +``` + +```java +public class Solution { + public int removeElement(int[] nums, int val) { + List tmp = new ArrayList<>(); + for (int num : nums) { + if (num != val) { + tmp.add(num); + } + } + for (int i = 0; i < tmp.size(); i++) { + nums[i] = tmp.get(i); + } + return tmp.size(); + } +} +``` + +```cpp +class Solution { +public: + int removeElement(vector& nums, int val) { + vector tmp; + for (int num : nums) { + if (num != val) { + tmp.push_back(num); + } + } + for (int i = 0; i < tmp.size(); i++) { + nums[i] = tmp[i]; + } + return tmp.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} val + * @return {number} + */ + removeElement(nums, val) { + const tmp = []; + for (const num of nums) { + if (num !== val) { + tmp.push(num); + } + } + for (let i = 0; i < tmp.length; i++) { + nums[i] = tmp[i]; + } + return tmp.length; + } +} +``` + +```csharp +public class Solution { + public int RemoveElement(int[] nums, int val) { + List tmp = new List(); + foreach (int num in nums) { + if (num != val) { + tmp.Add(num); + } + } + + for (int i = 0; i < tmp.Count; i++) { + nums[i] = tmp[i]; + } + + return tmp.Count; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Two Pointers - I + +::tabs-start + +```python +class Solution: + def removeElement(self, nums: list[int], val: int) -> int: + k = 0 + for i in range(len(nums)): + if nums[i] != val: + nums[k] = nums[i] + k += 1 + return k +``` + +```java +public class Solution { + public int removeElement(int[] nums, int val) { + int k = 0; + for (int i = 0; i < nums.length; i++) { + if (nums[i] != val) { + nums[k++] = nums[i]; + } + } + return k; + } +} +``` + +```cpp +class Solution { +public: + int removeElement(vector& nums, int val) { + int k = 0; + for (int i = 0; i < nums.size(); i++) { + if (nums[i] != val) { + nums[k++] = nums[i]; + } + } + return k; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} val + * @return {number} + */ + removeElement(nums, val) { + let k = 0; + for (let i = 0; i < nums.length; i++) { + if (nums[i] !== val) { + nums[k++] = nums[i]; + } + } + return k; + } +} +``` + +```csharp +public class Solution { + public int RemoveElement(int[] nums, int val) { + int k = 0; + for (int i = 0; i < nums.Length; i++) { + if (nums[i] != val) { + nums[k++] = nums[i]; + } + } + return k; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Two Pointers - II + +::tabs-start + +```python +class Solution: + def removeElement(self, nums: list[int], val: int) -> int: + i = 0 + n = len(nums) + while i < n: + if nums[i] == val: + n -= 1 + nums[i] = nums[n] + else: + i += 1 + return n +``` + +```java +public class Solution { + public int removeElement(int[] nums, int val) { + int i = 0, n = nums.length; + while (i < n) { + if (nums[i] == val) { + nums[i] = nums[--n]; + } else { + i++; + } + } + return n; + } +} +``` + +```cpp +class Solution { +public: + int removeElement(vector& nums, int val) { + int i = 0, n = nums.size(); + while (i < n) { + if (nums[i] == val) { + nums[i] = nums[--n]; + } else { + i++; + } + } + return n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} val + * @return {number} + */ + removeElement(nums, val) { + let i = 0, n = nums.length; + while (i < n) { + if (nums[i] == val) { + nums[i] = nums[--n]; + } else { + i++; + } + } + return n; + } +} +``` + +```csharp +public class Solution { + public int RemoveElement(int[] nums, int val) { + int i = 0, n = nums.Length; + while (i < n) { + if (nums[i] == val) { + nums[i] = nums[--n]; + } else { + i++; + } + } + return n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/remove-k-digits.md b/articles/remove-k-digits.md new file mode 100644 index 000000000..19ccf59dc --- /dev/null +++ b/articles/remove-k-digits.md @@ -0,0 +1,344 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def removeKdigits(self, num: str, k: int) -> str: + num = list(num) + while k: + i = 1 + while i < len(num) and num[i] >= num[i - 1]: + i += 1 + num.pop(i - 1) + k -= 1 + + i = 0 + while i < len(num) and num[i] == '0': + i += 1 + + num = num[i:] + return ''.join(num) if num else '0' +``` + +```java +public class Solution { + public String removeKdigits(String num, int k) { + StringBuilder sb = new StringBuilder(num); + while (k > 0) { + int i = 1; + while (i < sb.length() && sb.charAt(i) >= sb.charAt(i - 1)) { + i++; + } + sb.deleteCharAt(i - 1); + k--; + } + + int i = 0; + while (i < sb.length() && sb.charAt(i) == '0') { + i++; + } + + sb = new StringBuilder(sb.substring(i)); + return sb.length() == 0 ? "0" : sb.toString(); + } +} +``` + +```cpp +class Solution { +public: + string removeKdigits(string num, int k) { + while (k > 0) { + int i = 1; + while (i < num.size() && num[i] >= num[i - 1]) { + i++; + } + num.erase(i - 1, 1); + k--; + } + + int i = 0; + while (i < num.size() && num[i] == '0') { + i++; + } + + num = num.substr(i); + return num.empty() ? "0" : num; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} num + * @param {number} k + * @return {string} + */ + removeKdigits(num, k) { + num = num.split(''); + while (k > 0) { + let i = 1; + while (i < num.length && num[i] >= num[i - 1]) { + i++; + } + num.splice(i - 1, 1); + k--; + } + + let i = 0; + while (i < num.length && num[i] === '0') { + i++; + } + + num = num.slice(i); + return num.length === 0 ? "0" : num.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the language. + +--- + +## 2. Greedy + Stack + +::tabs-start + +```python +class Solution: + def removeKdigits(self, num: str, k: int) -> str: + stack = [] + for c in num: + while k > 0 and stack and stack[-1] > c: + k -= 1 + stack.pop() + stack.append(c) + + while stack and k: + stack.pop() + k -= 1 + + i = 0 + while i < len(stack) and stack[i] == '0': + i += 1 + + res = stack[i:] + return ''.join(res) if res else "0" +``` + +```java +public class Solution { + public String removeKdigits(String num, int k) { + StringBuilder stack = new StringBuilder(); + for (char c : num.toCharArray()) { + while (k > 0 && stack.length() > 0 && stack.charAt(stack.length() - 1) > c) { + stack.deleteCharAt(stack.length() - 1); + k--; + } + stack.append(c); + } + + while (k > 0 && stack.length() > 0) { + stack.deleteCharAt(stack.length() - 1); + k--; + } + + int i = 0; + while (i < stack.length() && stack.charAt(i) == '0') { + i++; + } + + String res = stack.substring(i); + return res.isEmpty() ? "0" : res; + } +} +``` + +```cpp +class Solution { +public: + string removeKdigits(string num, int k) { + string stack; + for (char c : num) { + while (k > 0 && !stack.empty() && stack.back() > c) { + stack.pop_back(); + k--; + } + stack.push_back(c); + } + + while (k > 0 && !stack.empty()) { + stack.pop_back(); + k--; + } + + int i = 0; + while (i < stack.size() && stack[i] == '0') { + i++; + } + + string res = stack.substr(i); + return res.empty() ? "0" : res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} num + * @param {number} k + * @return {string} + */ + removeKdigits(num, k) { + let stack = []; + for (let c of num) { + while (k > 0 && stack.length > 0 && stack[stack.length - 1] > c) { + stack.pop(); + k--; + } + stack.push(c); + } + + while (k > 0 && stack.length > 0) { + stack.pop(); + k--; + } + + let i = 0; + while (i < stack.length && stack[i] === '0') { + i++; + } + + let res = stack.slice(i).join(''); + return res === '' ? "0" : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Two Pointers + +::tabs-start + +```python +class Solution: + def removeKdigits(self, num: str, k: int) -> str: + l = 0 + num = list(num) + + for r in range(len(num)): + while l > 0 and k > 0 and num[l - 1] > num[r]: + l -= 1 + k -= 1 + num[l] = num[r] + l += 1 + + l -= k + i = 0 + while i < l and num[i] == '0': + i += 1 + res = ''.join(num[i:l]) + return res if res else '0' +``` + +```java +public class Solution { + public String removeKdigits(String num, int k) { + char[] numArray = num.toCharArray(); + int l = 0; + + for (int r = 0; r < numArray.length; r++) { + while (l > 0 && k > 0 && numArray[l - 1] > numArray[r]) { + l--; + k--; + } + numArray[l++] = numArray[r]; + } + + l -= k; + int i = 0; + while (i < l && numArray[i] == '0') { + i++; + } + String res = new String(numArray, i, l - i); + return res.isEmpty() ? "0" : res; + } +} +``` + +```cpp +class Solution { +public: + string removeKdigits(string num, int k) { + int l = 0; + for (int r = 0; r < num.size(); r++) { + while (l > 0 && k > 0 && num[l - 1] > num[r]) { + l--; + k--; + } + num[l++] = num[r]; + } + + l -= k; + int i = 0; + while (i < l && num[i] == '0') { + i++; + } + if (i == l) return "0"; + return num.substr(i, l - i); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} num + * @param {number} k + * @return {string} + */ + removeKdigits(num, k) { + let numArray = num.split(''); + let l = 0; + + for (let r = 0; r < numArray.length; r++) { + while (l > 0 && k > 0 && numArray[l - 1] > numArray[r]) { + l--; + k--; + } + numArray[l++] = numArray[r]; + } + + l -= k; + let i = 0; + while (i < l && numArray[i] === '0') { + i++; + } + let res = numArray.slice(i, l).join(''); + return res.length === 0 ? "0" : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the language. \ No newline at end of file diff --git a/articles/remove-linked-list-elements.md b/articles/remove-linked-list-elements.md new file mode 100644 index 000000000..30e4a5e94 --- /dev/null +++ b/articles/remove-linked-list-elements.md @@ -0,0 +1,514 @@ +## 1. Brute Force + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]: + arr = [] + cur = head + + while cur: + if cur.val != val: + arr.append(cur.val) + cur = cur.next + + if not arr: + return None + + res = ListNode(arr[0]) + cur = res + for i in range(1, len(arr)): + node = ListNode(arr[i]) + cur.next = node + cur = cur.next + + return res +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode removeElements(ListNode head, int val) { + List arr = new ArrayList<>(); + ListNode cur = head; + + while (cur != null) { + if (cur.val != val) { + arr.add(cur.val); + } + cur = cur.next; + } + + if (arr.isEmpty()) { + return null; + } + + ListNode res = new ListNode(arr.get(0)); + cur = res; + for (int i = 1; i < arr.size(); i++) { + ListNode node = new ListNode(arr.get(i)); + cur.next = node; + cur = cur.next; + } + + return res; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* removeElements(ListNode* head, int val) { + vector arr; + ListNode* cur = head; + + while (cur) { + if (cur->val != val) { + arr.push_back(cur->val); + } + cur = cur->next; + } + + if (arr.empty()) { + return nullptr; + } + + ListNode* res = new ListNode(arr[0]); + cur = res; + for (int i = 1; i < arr.size(); i++) { + ListNode* node = new ListNode(arr[i]); + cur->next = node; + cur = cur->next; + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} val + * @return {ListNode} + */ + removeElements(head, val) { + const arr = []; + let cur = head; + + while (cur) { + if (cur.val !== val) { + arr.push(cur.val); + } + cur = cur.next; + } + + if (arr.length === 0) { + return null; + } + + const res = new ListNode(arr[0]); + cur = res; + for (let i = 1; i < arr.length; i++) { + const node = new ListNode(arr[i]); + cur.next = node; + cur = cur.next; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Recursion + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def removeElements(self, head: Optional[ListNode], val: int) -> Optional[ListNode]: + if not head: + return None + head.next = self.removeElements(head.next, val) + return head if head.val != val else head.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode removeElements(ListNode head, int val) { + if (head == null) return null; + head.next = removeElements(head.next, val); + return head.val != val ? head : head.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* removeElements(ListNode* head, int val) { + if (head == nullptr) return nullptr; + head->next = removeElements(head->next, val); + return head->val != val ? head : head->next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} val + * @return {ListNode} + */ + removeElements(head, val) { + if (head === null) return null; + head.next = this.removeElements(head.next, val); + return head.val !== val ? head : head.next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Iteration + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def removeElements(self, head: ListNode, val: int) -> ListNode: + dummy = ListNode(0, head) + prev, curr = dummy, head + + while curr: + nxt = curr.next + if curr.val == val: + prev.next = nxt + else: + prev = curr + curr = nxt + + return dummy.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode removeElements(ListNode head, int val) { + ListNode dummy = new ListNode(0, head); + ListNode prev = dummy, curr = head; + + while (curr != null) { + ListNode nxt = curr.next; + if (curr.val == val) { + prev.next = nxt; + } else { + prev = curr; + } + curr = nxt; + } + + return dummy.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* removeElements(ListNode* head, int val) { + ListNode dummy(0, head); + ListNode *prev = &dummy, *curr = head; + + while (curr) { + ListNode* nxt = curr->next; + if (curr->val == val) { + prev->next = nxt; + } else { + prev = curr; + } + curr = nxt; + } + + return dummy.next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} val + * @return {ListNode} + */ + removeElements(head, val) { + let dummy = new ListNode(0, head); + let prev = dummy, curr = head; + + while (curr) { + let nxt = curr.next; + if (curr.val === val) { + prev.next = nxt; + } else { + prev = curr; + } + curr = nxt; + } + + return dummy.next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. iteration Without Prev Pointer + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def removeElements(self, head: ListNode, val: int) -> ListNode: + dummy = ListNode(-1, head) + curr = dummy + + while curr.next: + if curr.next.val == val: + curr.next = curr.next.next + else: + curr = curr.next + + return dummy.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode removeElements(ListNode head, int val) { + ListNode dummy = new ListNode(-1, head); + ListNode curr = dummy; + + while (curr.next != null) { + if (curr.next.val == val) { + curr.next = curr.next.next; + } else { + curr = curr.next; + } + } + + return dummy.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* removeElements(ListNode* head, int val) { + ListNode dummy(-1, head); + ListNode *curr = &dummy; + + while (curr->next) { + if (curr->next->val == val) { + curr->next = curr->next->next; + } else { + curr = curr->next; + } + } + + return dummy.next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} val + * @return {ListNode} + */ + removeElements(head, val) { + let dummy = new ListNode(-1, head); + let curr = dummy; + + while (curr.next) { + if (curr.next.val === val) { + curr.next = curr.next.next; + } else { + curr = curr.next; + } + } + + return dummy.next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/remove-max-number-of-edges-to-keep-graph-fully-traversable.md b/articles/remove-max-number-of-edges-to-keep-graph-fully-traversable.md new file mode 100644 index 000000000..04c3da1ac --- /dev/null +++ b/articles/remove-max-number-of-edges-to-keep-graph-fully-traversable.md @@ -0,0 +1,274 @@ +## 1. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.n = n + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu = self.find(u) + pv = self.find(v) + if pu == pv: + return 0 + if self.Size[pu] < self.Size[pv]: + pu, pv = pv, pu + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + self.n -= 1 + return 1 + + def isConnected(self): + return self.n == 1 + +class Solution: + def maxNumEdgesToRemove(self, n: int, edges: List[List[int]]) -> int: + alice, bob = DSU(n), DSU(n) + cnt = 0 + + for type, src, dst in edges: + if type == 3: + cnt += (alice.union(src, dst) | bob.union(src, dst)) + + for type, src, dst in edges: + if type == 1: + cnt += alice.union(src, dst) + elif type == 2: + cnt += bob.union(src, dst) + + if alice.isConnected() and bob.isConnected(): + return len(edges) - cnt + return -1 +``` + +```java +class DSU { + private int[] parent, size; + private int n; + + public DSU(int n) { + this.n = n; + parent = new int[n + 1]; + size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + parent[i] = i; + size[i] = 1; + } + } + + public int find(int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + public int union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) { + return 0; + } + if (size[pu] < size[pv]) { + int temp = pu; + pu = pv; + pv = temp; + } + size[pu] += size[pv]; + parent[pv] = pu; + n--; + return 1; + } + + public boolean isConnected() { + return n == 1; + } +} + +public class Solution { + public int maxNumEdgesToRemove(int n, int[][] edges) { + DSU alice = new DSU(n), bob = new DSU(n); + int cnt = 0; + + for (int[] edge : edges) { + if (edge[0] == 3) { + cnt += (alice.union(edge[1], edge[2]) | bob.union(edge[1], edge[2])); + } + } + + for (int[] edge : edges) { + if (edge[0] == 1) { + cnt += alice.union(edge[1], edge[2]); + } else if (edge[0] == 2) { + cnt += bob.union(edge[1], edge[2]); + } + } + + if (alice.isConnected() && bob.isConnected()) { + return edges.length - cnt; + } + return -1; + } +} +``` + +```cpp +class DSU { +private: + vector parent, size; + int n; + +public: + DSU(int n) : n(n), parent(n + 1), size(n + 1, 1) { + for (int i = 0; i <= n; i++) { + parent[i] = i; + } + } + + int find(int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + int unionSets(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) { + return 0; + } + if (size[pu] < size[pv]) { + swap(pu, pv); + } + size[pu] += size[pv]; + parent[pv] = pu; + n--; + return 1; + } + + bool isConnected() { + return n == 1; + } +}; + +class Solution { +public: + int maxNumEdgesToRemove(int n, vector>& edges) { + DSU alice(n), bob(n); + int cnt = 0; + + for (auto& edge : edges) { + if (edge[0] == 3) { + cnt += (alice.unionSets(edge[1], edge[2]) | bob.unionSets(edge[1], edge[2])); + } + } + + for (auto& edge : edges) { + if (edge[0] == 1) { + cnt += alice.unionSets(edge[1], edge[2]); + } else if (edge[0] == 2) { + cnt += bob.unionSets(edge[1], edge[2]); + } + } + + if (alice.isConnected() && bob.isConnected()) { + return edges.size() - cnt; + } + return -1; + } +}; +``` + +```javascript +class DSU { + /** + * @param {number} n + */ + constructor(n) { + this.n = n; + this.parent = Array(n + 1).fill(0).map((_, i) => i); + this.size = Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.parent[node] !== node) { + this.parent[node] = this.find(this.parent[node]); + } + return this.parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {number} + */ + union(u, v) { + let pu = this.find(u), pv = this.find(v); + if (pu === pv) { + return 0; + } + if (this.size[pu] < this.size[pv]) { + [pu, pv] = [pv, pu]; + } + this.size[pu] += this.size[pv]; + this.parent[pv] = pu; + this.n--; + return 1; + } + + /** + * @return {boolean} + */ + isConnected() { + return this.n === 1; + } +} + +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @return {number} + */ + maxNumEdgesToRemove(n, edges) { + let alice = new DSU(n), bob = new DSU(n); + let cnt = 0; + + for (let [type, src, dst] of edges) { + if (type === 3) { + cnt += (alice.union(src, dst) | bob.union(src, dst)); + } + } + + for (let [type, src, dst] of edges) { + if (type === 1) { + cnt += alice.union(src, dst); + } else if (type === 2) { + cnt += bob.union(src, dst); + } + } + + return alice.isConnected() && bob.isConnected() ? edges.length - cnt : -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(E * α(V))$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of verticies and $E$ is the number of edges. \ No newline at end of file diff --git a/articles/remove-node-from-end-of-linked-list.md b/articles/remove-node-from-end-of-linked-list.md new file mode 100644 index 000000000..ee936f416 --- /dev/null +++ b/articles/remove-node-from-end-of-linked-list.md @@ -0,0 +1,1108 @@ +## 1. Brute Force + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + +class Solution: + def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: + nodes = [] + cur = head + while cur: + nodes.append(cur) + cur = cur.next + + removeIndex = len(nodes) - n + if removeIndex == 0: + return head.next + + nodes[removeIndex - 1].next = nodes[removeIndex].next + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +public class Solution { + public ListNode removeNthFromEnd(ListNode head, int n) { + List nodes = new ArrayList<>(); + ListNode cur = head; + while (cur != null) { + nodes.add(cur); + cur = cur.next; + } + + int removeIndex = nodes.size() - n; + if (removeIndex == 0) { + return head.next; + } + + nodes.get(removeIndex - 1).next = nodes.get(removeIndex).next; + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +class Solution { +public: + ListNode* removeNthFromEnd(ListNode* head, int n) { + vector nodes; + ListNode* cur = head; + while (cur != nullptr) { + nodes.push_back(cur); + cur = cur->next; + } + + int removeIndex = nodes.size() - n; + if (removeIndex == 0) { + return head->next; + } + + nodes[removeIndex - 1]->next = nodes[removeIndex]->next; + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {ListNode} head + * @param {number} n + * @return {ListNode} + */ + removeNthFromEnd(head, n) { + const nodes = []; + let cur = head; + while (cur) { + nodes.push(cur); + cur = cur.next; + } + + const removeIndex = nodes.length - n; + if (removeIndex === 0) { + return head.next; + } + + nodes[removeIndex - 1].next = nodes[removeIndex].next; + return head; + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +public class Solution { + public ListNode RemoveNthFromEnd(ListNode head, int n) { + var nodes = new List(); + var cur = head; + while (cur != null) { + nodes.Add(cur); + cur = cur.next; + } + + int removeIndex = nodes.Count - n; + if (removeIndex == 0) { + return head.next; + } + + nodes[removeIndex - 1].next = nodes[removeIndex].next; + return head; + } +} +``` + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func removeNthFromEnd(head *ListNode, n int) *ListNode { + nodes := []*ListNode{} + cur := head + for cur != nil { + nodes = append(nodes, cur) + cur = cur.Next + } + + removeIndex := len(nodes) - n + if removeIndex == 0 { + return head.Next + } + + nodes[removeIndex-1].Next = nodes[removeIndex].Next + return head +} +``` + +```kotlin +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun removeNthFromEnd(head: ListNode?, n: Int): ListNode? { + val nodes = mutableListOf() + var cur = head + + while (cur != null) { + nodes.add(cur) + cur = cur.next + } + + val removeIndex = nodes.size - n + if (removeIndex == 0) { + return head?.next + } + + nodes[removeIndex - 1]?.next = nodes[removeIndex]?.next + return head + } +} +``` + +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func removeNthFromEnd(_ head: ListNode?, _ n: Int) -> ListNode? { + var nodes: [ListNode] = [] + var cur = head + + while cur != nil { + nodes.append(cur!) + cur = cur?.next + } + + let removeIndex = nodes.count - n + if removeIndex == 0 { + return head?.next + } + + nodes[removeIndex - 1].next = nodes[removeIndex].next + return head + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N)$ +* Space complexity: $O(N)$ + +--- + +## 2. Iteration (Two Pass) + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + +class Solution: + def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: + N = 0 + cur = head + while cur: + N += 1 + cur = cur.next + + removeIndex = N - n + if removeIndex == 0: + return head.next + + cur = head + for i in range(N - 1): + if (i + 1) == removeIndex: + cur.next = cur.next.next + break + cur = cur.next + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +public class Solution { + public ListNode removeNthFromEnd(ListNode head, int n) { + int N = 0; + ListNode cur = head; + while (cur != null) { + N++; + cur = cur.next; + } + + int removeIndex = N - n; + if (removeIndex == 0) { + return head.next; + } + + cur = head; + for (int i = 0; i < N - 1; i++) { + if ((i + 1) == removeIndex) { + cur.next = cur.next.next; + break; + } + cur = cur.next; + } + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +class Solution { +public: + ListNode* removeNthFromEnd(ListNode* head, int n) { + int N = 0; + ListNode* cur = head; + while (cur != nullptr) { + N++; + cur = cur->next; + } + + int removeIndex = N - n; + if (removeIndex == 0) { + return head->next; + } + + cur = head; + for (int i = 0; i < N - 1; i++) { + if ((i + 1) == removeIndex) { + cur->next = cur->next->next; + break; + } + cur = cur->next; + } + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {ListNode} head + * @param {number} n + * @return {ListNode} + */ + removeNthFromEnd(head, n) { + let N = 0; + let cur = head; + while (cur) { + N++; + cur = cur.next; + } + + const removeIndex = N - n; + if (removeIndex === 0) { + return head.next; + } + + cur = head; + for (let i = 0; i < N - 1; i++) { + if (i + 1 === removeIndex) { + cur.next = cur.next.next; + break; + } + cur = cur.next; + } + return head; + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +public class Solution { + public ListNode RemoveNthFromEnd(ListNode head, int n) { + int N = 0; + ListNode cur = head; + while (cur != null) { + N++; + cur = cur.next; + } + + int removeIndex = N - n; + if (removeIndex == 0) { + return head.next; + } + + cur = head; + for (int i = 0; i < N - 1; i++) { + if (i + 1 == removeIndex) { + cur.next = cur.next.next; + break; + } + cur = cur.next; + } + return head; + } +} +``` + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func removeNthFromEnd(head *ListNode, n int) *ListNode { + N := 0 + cur := head + for cur != nil { + N++ + cur = cur.Next + } + + removeIndex := N - n + if removeIndex == 0 { + return head.Next + } + + cur = head + for i := 0; i < N-1; i++ { + if (i + 1) == removeIndex { + cur.Next = cur.Next.Next + break + } + cur = cur.Next + } + return head +} +``` + +```kotlin +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun removeNthFromEnd(head: ListNode?, n: Int): ListNode? { + var N = 0 + var cur = head + + while (cur != null) { + N++ + cur = cur.next + } + + val removeIndex = N - n + if (removeIndex == 0) { + return head?.next + } + + cur = head + for (i in 0 until N - 1) { + if (i + 1 == removeIndex) { + cur?.next = cur?.next?.next + break + } + cur = cur?.next + } + return head + } +} +``` + +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func removeNthFromEnd(_ head: ListNode?, _ n: Int) -> ListNode? { + var N = 0 + var cur = head + while cur != nil { + N += 1 + cur = cur?.next + } + + let removeIndex = N - n + if removeIndex == 0 { + return head?.next + } + + cur = head + for i in 0..<(N - 1) { + if (i + 1) == removeIndex { + cur?.next = cur?.next?.next + break + } + cur = cur?.next + } + return head + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N)$ +* Space complexity: $O(1)$ + +--- + +## 3. Recursion + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + +class Solution: + def rec(self, head, n): + if not head: + return None + + head.next = self.rec(head.next, n) + n[0] -= 1 + if n[0] == 0: + return head.next + return head + + def removeNthFromEnd(self, head, n): + return self.rec(head, [n]) +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +public class Solution { + public ListNode rec(ListNode head, int[] n) { + if (head == null) { + return null; + } + + head.next = rec(head.next, n); + n[0]--; + if (n[0] == 0) { + return head.next; + } + return head; + } + + public ListNode removeNthFromEnd(ListNode head, int n) { + return rec(head, new int[]{n}); + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +class Solution { +public: + ListNode* rec(ListNode* head, int& n) { + if (!head) { + return NULL; + } + + head -> next = rec(head -> next, n); + n--; + if (n == 0) { + return head -> next; + } + return head; + } + + ListNode* removeNthFromEnd(ListNode* head, int n) { + return rec(head, n); + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {ListNode} head + * @param {number[]} n + * @return {ListNode} + */ + rec(head, n) { + if (!head) { + return null; + } + + head.next = this.rec(head.next, n); + n[0]--; + if (n[0] === 0) { + return head.next; + } + return head; + } + + /** + * @param {ListNode} head + * @param {number} n + * @return {ListNode} + */ + removeNthFromEnd(head, n) { + return this.rec(head, [n]); + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +public class Solution { + public ListNode rec(ListNode head, ref int n) { + if (head == null) { + return null; + } + + head.next = rec(head.next, ref n); + n--; + if (n == 0) { + return head.next; + } + return head; + } + + public ListNode RemoveNthFromEnd(ListNode head, int n) { + return rec(head, ref n); + } +} +``` + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func rec(head *ListNode, n *int) *ListNode { + if head == nil { + return nil + } + + head.Next = rec(head.Next, n) + (*n)-- + + if *n == 0 { + return head.Next + } + return head +} + +func removeNthFromEnd(head *ListNode, n int) *ListNode { + return rec(head, &n) +} +``` + +```kotlin +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + private fun rec(head: ListNode?, n: IntArray): ListNode? { + if (head == null) { + return null + } + + head.next = rec(head.next, n) + n[0]-- + + return if (n[0] == 0) { + head.next + } else { + head + } + } + + fun removeNthFromEnd(head: ListNode?, n: Int): ListNode? { + return rec(head, intArrayOf(n)) + } +} +``` + +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func rec(_ head: ListNode?, _ n: inout Int) -> ListNode? { + if head == nil { + return nil + } + + head?.next = rec(head?.next, &n) + n -= 1 + if n == 0 { + return head?.next + } + return head + } + + func removeNthFromEnd(_ head: ListNode?, _ n: Int) -> ListNode? { + var n = n + return rec(head, &n) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N)$ +* Space complexity: $O(N)$ for recursion stack. + +--- + +## 4. Two Pointers + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + +class Solution: + def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: + dummy = ListNode(0, head) + left = dummy + right = head + + while n > 0: + right = right.next + n -= 1 + + while right: + left = left.next + right = right.next + + left.next = left.next.next + return dummy.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +class Solution { + public ListNode removeNthFromEnd(ListNode head, int n) { + ListNode dummy = new ListNode(0, head); + ListNode left = dummy; + ListNode right = head; + + while (n > 0) { + right = right.next; + n--; + } + + while (right != null) { + left = left.next; + right = right.next; + } + + left.next = left.next.next; + return dummy.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +class Solution { +public: + ListNode* removeNthFromEnd(ListNode* head, int n) { + ListNode* dummy = new ListNode(0, head); + ListNode* left = dummy; + ListNode* right = head; + + while (n > 0) { + right = right->next; + n--; + } + + while (right != nullptr) { + left = left->next; + right = right->next; + } + + left->next = left->next->next; + return dummy->next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {ListNode} head + * @param {number} n + * @return {ListNode} + */ + removeNthFromEnd(head, n) { + const dummy = new ListNode(0, head); + let left = dummy; + let right = head; + + while (n > 0) { + right = right.next; + n--; + } + + while (right !== null) { + left = left.next; + right = right.next; + } + + left.next = left.next.next; + return dummy.next; + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +public class Solution { + public ListNode RemoveNthFromEnd(ListNode head, int n) { + ListNode dummy = new ListNode(0, head); + ListNode left = dummy; + ListNode right = head; + + while (n > 0) { + right = right.next; + n--; + } + + while (right != null) { + left = left.next; + right = right.next; + } + + left.next = left.next.next; + return dummy.next; + } +} +``` + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func removeNthFromEnd(head *ListNode, n int) *ListNode { + dummy := &ListNode{Next: head} + left := dummy + right := head + + for n > 0 { + right = right.Next + n-- + } + + for right != nil { + left = left.Next + right = right.Next + } + + left.Next = left.Next.Next + return dummy.Next +} +``` + +```kotlin +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun removeNthFromEnd(head: ListNode?, n: Int): ListNode? { + val dummy = ListNode(0).apply { next = head } + var left: ListNode? = dummy + var right: ListNode? = head + + var count = n + while (count > 0) { + right = right?.next + count-- + } + + while (right != null) { + left = left?.next + right = right.next + } + + left?.next = left?.next?.next + return dummy.next + } +} +``` + +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func removeNthFromEnd(_ head: ListNode?, _ n: Int) -> ListNode? { + let dummy = ListNode(0, head) + var left: ListNode? = dummy + var right: ListNode? = head + var n = n + + while n > 0 { + right = right?.next + n -= 1 + } + + while right != nil { + left = left?.next + right = right?.next + } + + left?.next = left?.next?.next + return dummy.next + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/remove-nodes-from-linked-list.md b/articles/remove-nodes-from-linked-list.md new file mode 100644 index 000000000..b629d4760 --- /dev/null +++ b/articles/remove-nodes-from-linked-list.md @@ -0,0 +1,574 @@ +## 1. Convert To Array + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def removeNodes(self, head: Optional[ListNode]) -> Optional[ListNode]: + cur, arr = head, [ListNode(0, head)] + while cur: + arr.append(cur) + cur = cur.next + + rightMaxi = ListNode(0, None) + for i in range(len(arr) - 1, 0, -1): + if rightMaxi.val > arr[i].val: + arr[i - 1].next = rightMaxi + else: + rightMaxi = arr[i] + + return arr[0].next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode removeNodes(ListNode head) { + List arr = new ArrayList<>(); + arr.add(new ListNode(0, head)); + ListNode cur = head; + + while (cur != null) { + arr.add(cur); + cur = cur.next; + } + + ListNode rightMaxi = new ListNode(0, null); + for (int i = arr.size() - 1; i > 0; i--) { + if (rightMaxi.val > arr.get(i).val) { + arr.get(i - 1).next = rightMaxi; + } else { + rightMaxi = arr.get(i); + } + } + + return arr.get(0).next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* removeNodes(ListNode* head) { + vector arr; + arr.push_back(new ListNode(0, head)); + ListNode* cur = head; + + while (cur) { + arr.push_back(cur); + cur = cur->next; + } + + ListNode* rightMaxi = new ListNode(0, nullptr); + for (int i = arr.size() - 1; i > 0; i--) { + if (rightMaxi->val > arr[i]->val) { + arr[i - 1]->next = rightMaxi; + } else { + rightMaxi = arr[i]; + } + } + + return arr[0]->next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + removeNodes(head) { + let cur = head, arr = [{ val: 0, next: head }]; + while (cur) { + arr.push(cur); + cur = cur.next; + } + + let rightMaxi = { val: 0, next: null }; + for (let i = arr.length - 1; i > 0; i--) { + if (rightMaxi.val > arr[i].val) { + arr[i - 1].next = rightMaxi; + } else { + rightMaxi = arr[i]; + } + } + + return arr[0].next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Monotonic Decreasing Stack + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def removeNodes(self, head: Optional[ListNode]) -> Optional[ListNode]: + stack = [] + cur = head + + while cur: + while stack and cur.val > stack[-1]: + stack.pop() + stack.append(cur.val) + cur = cur.next + + dummy = ListNode() + cur = dummy + + for num in stack: + cur.next = ListNode(num) + cur = cur.next + + return dummy.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode removeNodes(ListNode head) { + Stack stack = new Stack<>(); + ListNode cur = head; + + while (cur != null) { + while (!stack.isEmpty() && cur.val > stack.peek()) { + stack.pop(); + } + stack.push(cur.val); + cur = cur.next; + } + + ListNode dummy = new ListNode(); + cur = dummy; + + for (int num : stack) { + cur.next = new ListNode(num); + cur = cur.next; + } + + return dummy.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* removeNodes(ListNode* head) { + vector stack; + ListNode* cur = head; + + while (cur) { + while (!stack.empty() && cur->val > stack.back()) { + stack.pop_back(); + } + stack.push_back(cur->val); + cur = cur->next; + } + + ListNode* dummy = new ListNode(); + cur = dummy; + + for (int num : stack) { + cur->next = new ListNode(num); + cur = cur->next; + } + + return dummy->next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + removeNodes(head) { + let stack = []; + let cur = head; + + while (cur) { + while (stack.length && cur.val > stack[stack.length - 1]) { + stack.pop(); + } + stack.push(cur.val); + cur = cur.next; + } + + let dummy = new ListNode(); + cur = dummy; + + for (let num of stack) { + cur.next = new ListNode(num); + cur = cur.next; + } + + return dummy.next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Recursion + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def removeNodes(self, head: Optional[ListNode]) -> Optional[ListNode]: + if not head: + return None + + head.next = self.removeNodes(head.next) + if head.next and head.val < head.next.val: + return head.next + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode removeNodes(ListNode head) { + if (head == null) return null; + + head.next = removeNodes(head.next); + if (head.next != null && head.val < head.next.val) { + return head.next; + } + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* removeNodes(ListNode* head) { + if (!head) return nullptr; + + head->next = removeNodes(head->next); + if (head->next && head->val < head->next->val) { + return head->next; + } + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + removeNodes(head) { + if (!head) return null; + + head.next = this.removeNodes(head.next); + if (head.next && head.val < head.next.val) { + return head.next; + } + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 4. Reverse Twice + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def removeNodes(self, head: Optional[ListNode]) -> Optional[ListNode]: + def reverse(head): + prev, cur = None, head + while cur: + tmp = cur.next + cur.next = prev + prev, cur = cur, tmp + return prev + + head = reverse(head) + cur = head + cur_max = head.val + + while cur and cur.next: + if cur.next.val < cur_max: + cur.next = cur.next.next + else: + cur_max = cur.next.val + cur = cur.next + + return reverse(head) +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode removeNodes(ListNode head) { + head = reverse(head); + ListNode cur = head; + int curMax = head.val; + + while (cur != null && cur.next != null) { + if (cur.next.val < curMax) { + cur.next = cur.next.next; + } else { + curMax = cur.next.val; + cur = cur.next; + } + } + return reverse(head); + } + + private ListNode reverse(ListNode head) { + ListNode prev = null, cur = head; + while (cur != null) { + ListNode tmp = cur.next; + cur.next = prev; + prev = cur; + cur = tmp; + } + return prev; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* reverse(ListNode* head) { + ListNode* prev = nullptr; + ListNode* cur = head; + while (cur) { + ListNode* tmp = cur->next; + cur->next = prev; + prev = cur; + cur = tmp; + } + return prev; + } + + ListNode* removeNodes(ListNode* head) { + head = reverse(head); + ListNode* cur = head; + int cur_max = head->val; + + while (cur && cur->next) { + if (cur->next->val < cur_max) { + cur->next = cur->next->next; + } else { + cur_max = cur->next->val; + cur = cur->next; + } + } + return reverse(head); + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + removeNodes(head) { + const reverse = (head) => { + let prev = null, cur = head; + while (cur) { + let tmp = cur.next; + cur.next = prev; + prev = cur; + cur = tmp; + } + return prev; + }; + + head = reverse(head); + let cur = head; + let cur_max = head.val; + + while (cur && cur.next) { + if (cur.next.val < cur_max) { + cur.next = cur.next.next; + } else { + cur_max = cur.next.val; + cur = cur.next; + } + } + return reverse(head); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/removing-stars-from-a-string.md b/articles/removing-stars-from-a-string.md new file mode 100644 index 000000000..108454bae --- /dev/null +++ b/articles/removing-stars-from-a-string.md @@ -0,0 +1,357 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def removeStars(self, s: str) -> str: + while True: + flag = False + for i in range(1, len(s)): + if s[i] == '*' and s[i - 1] != '*': + s = s[:i - 1] + s[i + 1:] + flag = True + break + if not flag: + break + return s +``` + +```java +public class Solution { + public String removeStars(String s) { + while (true) { + boolean flag = false; + StringBuilder sb = new StringBuilder(s); + for (int i = 1; i < sb.length(); i++) { + if (sb.charAt(i) == '*' && sb.charAt(i - 1) != '*') { + sb.delete(i - 1, i + 1); + flag = true; + break; + } + } + s = sb.toString(); + if (!flag) { + break; + } + } + return s; + } +} +``` + +```cpp +class Solution { +public: + string removeStars(string s) { + while (true) { + bool flag = false; + for (int i = 1; i < s.size(); ++i) { + if (s[i] == '*' && s[i - 1] != '*') { + s = s.substr(0, i - 1) + s.substr(i + 1); + flag = true; + break; + } + } + if (!flag) { + break; + } + } + return s; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + removeStars(s) { + while (true) { + let flag = false; + for (let i = 1; i < s.length; i++) { + if (s[i] === '*' && s[i - 1] !== '*') { + s = s.slice(0, i - 1) + s.slice(i + 1); + flag = true; + break; + } + } + if (!flag) { + break; + } + } + return s; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Brute Force (Optimized) + +::tabs-start + +```python +class Solution: + def removeStars(self, s: str) -> str: + n = len(s) + i = 0 + while i < n: + if i and s[i] == '*' and s[i - 1] != '*': + s = s[:i - 1] + s[i + 1:] + n -= 2 + i -= 2 + i += 1 + return s +``` + +```java +public class Solution { + public String removeStars(String s) { + int n = s.length(); + int i = 0; + while (i < n) { + if (i > 0 && s.charAt(i) == '*' && s.charAt(i - 1) != '*') { + s = s.substring(0, i - 1) + s.substring(i + 1); + n -= 2; + i -= 2; + } + i++; + } + return s; + } +} +``` + +```cpp +class Solution { +public: + string removeStars(string s) { + int n = s.length(); + int i = 0; + while (i < n) { + if (i > 0 && s[i] == '*' && s[i - 1] != '*') { + s = s.substr(0, i - 1) + s.substr(i + 1); + n -= 2; + i -= 2; + } + i++; + } + return s; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + removeStars(s) { + let n = s.length; + let i = 0; + while (i < n) { + if (i > 0 && s[i] === '*' && s[i - 1] !== '*') { + s = s.slice(0, i - 1) + s.slice(i + 1); + n -= 2; + i -= 2; + } + i++; + } + return s; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Stack + +::tabs-start + +```python +class Solution: + def removeStars(self, s: str) -> str: + stack = [] + for c in s: + if c == "*": + stack and stack.pop() + else: + stack.append(c) + return "".join(stack) +``` + +```java +public class Solution { + public String removeStars(String s) { + Stack stack = new Stack<>(); + for (char c : s.toCharArray()) { + if (c == '*') { + if (!stack.isEmpty()) stack.pop(); + } else { + stack.push(c); + } + } + StringBuilder res = new StringBuilder(); + for (char c : stack) res.append(c); + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string removeStars(string s) { + stack stack; + for (char c : s) { + if (c == '*') { + if (!stack.empty()) stack.pop(); + } else { + stack.push(c); + } + } + string res; + while (!stack.empty()) { + res += stack.top(); + stack.pop(); + } + reverse(res.begin(), res.end()); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + removeStars(s) { + const stack = []; + for (const c of s) { + if (c === "*") { + if (stack.length > 0) stack.pop(); + } else { + stack.push(c); + } + } + return stack.join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Two Pointers + +::tabs-start + +```python +class Solution: + def removeStars(self, s: str) -> str: + l = 0 + s = list(s) + + for r in range(len(s)): + if s[r] == '*': + l -= 1 + else: + s[l] = s[r] + l += 1 + + return ''.join(s[:l]) +``` + +```java +public class Solution { + public String removeStars(String s) { + char[] arr = s.toCharArray(); + int l = 0; + + for (int r = 0; r < arr.length; r++) { + if (arr[r] == '*') { + l--; + } else { + arr[l] = arr[r]; + l++; + } + } + return new String(arr, 0, l); + } +} +``` + +```cpp +class Solution { +public: + string removeStars(string s) { + int l = 0; + + for (int r = 0; r < s.size(); r++) { + if (s[r] == '*') { + l--; + } else { + s[l] = s[r]; + l++; + } + } + return s.substr(0, l); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + removeStars(s) { + const arr = s.split(''); + let l = 0; + + for (let r = 0; r < arr.length; r++) { + if (arr[r] === '*') { + l--; + } else { + arr[l] = arr[r]; + l++; + } + } + return arr.slice(0, l).join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the language. \ No newline at end of file diff --git a/articles/reorder-linked-list.md b/articles/reorder-linked-list.md new file mode 100644 index 000000000..75e0ef82c --- /dev/null +++ b/articles/reorder-linked-list.md @@ -0,0 +1,1004 @@ +## 1. Brute Force + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + +class Solution: + def reorderList(self, head: Optional[ListNode]) -> None: + if not head: + return + + nodes = [] + cur = head + while cur: + nodes.append(cur) + cur = cur.next + + i, j = 0, len(nodes) - 1 + while i < j: + nodes[i].next = nodes[j] + i += 1 + if i >= j: + break + nodes[j].next = nodes[i] + j -= 1 + + nodes[i].next = None +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +public class Solution { + public void reorderList(ListNode head) { + if (head == null) { + return; + } + + List nodes = new ArrayList<>(); + ListNode cur = head; + while (cur != null) { + nodes.add(cur); + cur = cur.next; + } + + int i = 0, j = nodes.size() - 1; + while (i < j) { + nodes.get(i).next = nodes.get(j); + i++; + if (i >= j) { + break; + } + nodes.get(j).next = nodes.get(i); + j--; + } + + nodes.get(i).next = null; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +class Solution { +public: + void reorderList(ListNode* head) { + if (!head) return; + + vector nodes; + ListNode* cur = head; + while (cur) { + nodes.push_back(cur); + cur = cur->next; + } + + int i = 0, j = nodes.size() - 1; + while (i < j) { + nodes[i]->next = nodes[j]; + i++; + if (i >= j) break; + nodes[j]->next = nodes[i]; + j--; + } + + nodes[i]->next = nullptr; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {ListNode} head + * @return {void} + */ + reorderList(head) { + if (!head) return; + + const nodes = []; + let cur = head; + while (cur) { + nodes.push(cur); + cur = cur.next; + } + + let i = 0, j = nodes.length - 1; + while (i < j) { + nodes[i].next = nodes[j]; + i++; + if (i >= j) break; + nodes[j].next = nodes[i]; + j--; + } + + nodes[i].next = null; + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +public class Solution { + public void ReorderList(ListNode head) { + if (head == null) return; + + List nodes = new List(); + ListNode cur = head; + while (cur != null) { + nodes.Add(cur); + cur = cur.next; + } + + int i = 0, j = nodes.Count - 1; + while (i < j) { + nodes[i].next = nodes[j]; + i++; + if (i >= j) break; + nodes[j].next = nodes[i]; + j--; + } + + nodes[i].next = null; + } +} +``` + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func reorderList(head *ListNode) { + if head == nil { + return + } + + nodes := []*ListNode{} + cur := head + for cur != nil { + nodes = append(nodes, cur) + cur = cur.Next + } + + i, j := 0, len(nodes)-1 + for i < j { + nodes[i].Next = nodes[j] + i++ + if i >= j { + break + } + nodes[j].Next = nodes[i] + j-- + } + + nodes[i].Next = nil +} +``` + +```kotlin +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun reorderList(head: ListNode?) { + if (head == null) return + + val nodes = mutableListOf() + var cur: ListNode? = head + while (cur != null) { + nodes.add(cur) + cur = cur.next + } + + var i = 0 + var j = nodes.size - 1 + while (i < j) { + nodes[i].next = nodes[j] + i++ + if (i >= j) break + nodes[j].next = nodes[i] + j-- + } + + nodes[i].next = null + } +} +``` + +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func reorderList(_ head: ListNode?) { + if head == nil { + return + } + + var nodes: [ListNode] = [] + var cur = head + + while cur != nil { + nodes.append(cur!) + cur = cur?.next + } + + var i = 0, j = nodes.count - 1 + while i < j { + nodes[i].next = nodes[j] + i += 1 + if i >= j { + break + } + nodes[j].next = nodes[i] + j -= 1 + } + + nodes[i].next = nil + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Recursion + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + +class Solution: + def reorderList(self, head: Optional[ListNode]) -> None: + + def rec(root: ListNode, cur: ListNode) -> ListNode: + if not cur: + return root + + root = rec(root, cur.next) + if not root: + return None + + tmp = None + if root == cur or root.next == cur: + cur.next = None + else: + tmp = root.next + root.next = cur + cur.next = tmp + + return tmp + + head = rec(head, head.next) +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +public class Solution { + public void reorderList(ListNode head) { + head = rec(head, head.next); + } + + private ListNode rec(ListNode root, ListNode cur) { + if (cur == null) { + return root; + } + + root = rec(root, cur.next); + if (root == null) { + return null; + } + + ListNode tmp = null; + if (root == cur || root.next == cur) { + cur.next = null; + } else { + tmp = root.next; + root.next = cur; + cur.next = tmp; + } + + return tmp; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +class Solution { +public: + void reorderList(ListNode* head) { + head = rec(head, head->next); + } + +private: + ListNode* rec(ListNode* root, ListNode* cur) { + if (cur == nullptr) { + return root; + } + + root = rec(root, cur->next); + if (root == nullptr) { + return nullptr; + } + + ListNode* tmp = nullptr; + if (root == cur || root->next == cur) { + cur->next = nullptr; + } else { + tmp = root->next; + root->next = cur; + cur->next = tmp; + } + + return tmp; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {ListNode} head + * @return {void} + */ + reorderList(head) { + head = this.rec(head, head.next); + } + + /** + * @param {ListNode} root + * @param {ListNode} cur + * @return {ListNode} + */ + rec(root, cur) { + if (cur === null) { + return root; + } + + root = this.rec(root, cur.next); + if (root === null) { + return null; + } + + let tmp = null; + if (root === cur || root.next === cur) { + cur.next = null; + } else { + tmp = root.next; + root.next = cur; + cur.next = tmp; + } + + return tmp; + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +public class Solution { + public void ReorderList(ListNode head) { + head = Rec(head, head.next); + } + + private ListNode Rec(ListNode root, ListNode cur) { + if (cur == null) { + return root; + } + + root = Rec(root, cur.next); + if (root == null) { + return null; + } + + ListNode tmp = null; + if (root == cur || root.next == cur) { + cur.next = null; + } else { + tmp = root.next; + root.next = cur; + cur.next = tmp; + } + + return tmp; + } +} +``` + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func reorderList(head *ListNode) { + if head == nil { + return + } + + var rec func(root, cur *ListNode) *ListNode + rec = func(root, cur *ListNode) *ListNode { + if cur == nil { + return root + } + + root = rec(root, cur.Next) + if root == nil { + return nil + } + + var tmp *ListNode + if root == cur || root.Next == cur { + cur.Next = nil + } else { + tmp = root.Next + root.Next = cur + cur.Next = tmp + } + + return tmp + } + + rec(head, head.Next) +} +``` + +```kotlin +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun reorderList(head: ListNode?) { + if (head == null) return + + fun rec(root: ListNode?, cur: ListNode?): ListNode? { + if (cur == null) { + return root + } + + var updatedRoot = rec(root, cur.next) + if (updatedRoot == null) { + return null + } + + var tmp: ListNode? = null + if (updatedRoot == cur || updatedRoot?.next == cur) { + cur.next = null + } else { + tmp = updatedRoot.next + updatedRoot.next = cur + cur.next = tmp + } + + return tmp + } + + rec(head, head.next) + } +} +``` + +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func reorderList(_ head: ListNode?) { + func rec(_ root: ListNode?, _ cur: ListNode?) -> ListNode? { + if cur == nil { + return root + } + + var root = rec(root, cur?.next) + if root == nil { + return nil + } + + var tmp: ListNode? = nil + if root === cur || root?.next === cur { + cur?.next = nil + } else { + tmp = root?.next + root?.next = cur + cur?.next = tmp + } + + return tmp + } + + rec(head, head?.next) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Reverse And Merge + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + +class Solution: + def reorderList(self, head: Optional[ListNode]) -> None: + slow, fast = head, head.next + while fast and fast.next: + slow = slow.next + fast = fast.next.next + + second = slow.next + prev = slow.next = None + while second: + tmp = second.next + second.next = prev + prev = second + second = tmp + + first, second = head, prev + while second: + tmp1, tmp2 = first.next, second.next + first.next = second + second.next = tmp1 + first, second = tmp1, tmp2 +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +class Solution { + public void reorderList(ListNode head) { + ListNode slow = head; + ListNode fast = head.next; + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + } + + ListNode second = slow.next; + ListNode prev = slow.next = null; + while (second != null) { + ListNode tmp = second.next; + second.next = prev; + prev = second; + second = tmp; + } + + ListNode first = head; + second = prev; + while (second != null) { + ListNode tmp1 = first.next; + ListNode tmp2 = second.next; + first.next = second; + second.next = tmp1; + first = tmp1; + second = tmp2; + } + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +class Solution { +public: + void reorderList(ListNode* head) { + ListNode* slow = head; + ListNode* fast = head->next; + while (fast != nullptr && fast->next != nullptr) { + slow = slow->next; + fast = fast->next->next; + } + + ListNode* second = slow->next; + ListNode* prev = slow->next = nullptr; + while (second != nullptr) { + ListNode* tmp = second->next; + second->next = prev; + prev = second; + second = tmp; + } + + ListNode* first = head; + second = prev; + while (second != nullptr) { + ListNode* tmp1 = first->next; + ListNode* tmp2 = second->next; + first->next = second; + second->next = tmp1; + first = tmp1; + second = tmp2; + } + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {ListNode} head + * @return {void} + */ + reorderList(head) { + let slow = head; + let fast = head.next; + while (fast !== null && fast.next !== null) { + slow = slow.next; + fast = fast.next.next; + } + + let second = slow.next; + let prev = (slow.next = null); + while (second !== null) { + const tmp = second.next; + second.next = prev; + prev = second; + second = tmp; + } + + let first = head; + second = prev; + while (second !== null) { + const tmp1 = first.next; + const tmp2 = second.next; + first.next = second; + second.next = tmp1; + first = tmp1; + second = tmp2; + } + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +public class Solution { + public void ReorderList(ListNode head) { + ListNode slow = head; + ListNode fast = head.next; + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + } + + ListNode second = slow.next; + ListNode prev = slow.next = null; + while (second != null) { + ListNode tmp = second.next; + second.next = prev; + prev = second; + second = tmp; + } + + ListNode first = head; + second = prev; + while (second != null) { + ListNode tmp1 = first.next; + ListNode tmp2 = second.next; + first.next = second; + second.next = tmp1; + first = tmp1; + second = tmp2; + } + } +} +``` + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func reorderList(head *ListNode) { + if head == nil || head.Next == nil { + return + } + + slow, fast := head, head.Next + for fast != nil && fast.Next != nil { + slow = slow.Next + fast = fast.Next.Next + } + + second := slow.Next + slow.Next = nil + var prev *ListNode + for second != nil { + tmp := second.Next + second.Next = prev + prev = second + second = tmp + } + + first := head + second = prev + for second != nil { + tmp1, tmp2 := first.Next, second.Next + first.Next = second + second.Next = tmp1 + first, second = tmp1, tmp2 + } +} +``` + +```kotlin +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun reorderList(head: ListNode?) { + if (head == null || head.next == null) return + + var slow: ListNode? = head + var fast: ListNode? = head.next + while (fast != null && fast.next != null) { + slow = slow?.next + fast = fast.next.next + } + + val second = slow?.next + slow?.next = null + var prev: ListNode? = null + var curr = second + while (curr != null) { + val tmp = curr.next + curr.next = prev + prev = curr + curr = tmp + } + + var first: ListNode? = head + var secondList: ListNode? = prev + while (first != null && secondList != null) { + val tmp1 = first.next + val tmp2 = secondList.next + first.next = secondList + secondList.next = tmp1 + first = tmp1 + secondList = tmp2 + } + } +} +``` + +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func reorderList(_ head: ListNode?) { + var slow = head, fast = head?.next + while fast != nil && fast?.next != nil { + slow = slow?.next + fast = fast?.next?.next + } + + var second = slow?.next + var prev: ListNode? = nil + slow?.next = nil + + while second != nil { + let tmp = second?.next + second?.next = prev + prev = second + second = tmp + } + + var first = head + second = prev + + while second != nil { + let tmp1 = first?.next + let tmp2 = second?.next + first?.next = second + second?.next = tmp1 + first = tmp1 + second = tmp2 + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/reorder-routes-to-make-all-paths-lead-to-the-city-zero.md b/articles/reorder-routes-to-make-all-paths-lead-to-the-city-zero.md new file mode 100644 index 000000000..ae2727f84 --- /dev/null +++ b/articles/reorder-routes-to-make-all-paths-lead-to-the-city-zero.md @@ -0,0 +1,385 @@ +## 1. Depth First Search - I + +::tabs-start + +```python +class Solution: + def minReorder(self, n: int, connections: List[List[int]]) -> int: + edges = {(a, b) for a, b in connections} + neighbors = {city: [] for city in range(n)} + visit = set() + changes = 0 + + for a, b in connections: + neighbors[a].append(b) + neighbors[b].append(a) + + def dfs(city): + nonlocal changes + visit.add(city) + for neighbor in neighbors[city]: + if neighbor in visit: + continue + if (neighbor, city) not in edges: + changes += 1 + dfs(neighbor) + + dfs(0) + return changes +``` + +```java +public class Solution { + private Map> neighbors; + private boolean[] visit; + private Set edges; + + public int minReorder(int n, int[][] connections) { + edges = new HashSet<>(); + neighbors = new HashMap<>(); + visit = new boolean[n]; + int[] changes = {0}; + + for (int[] conn : connections) { + int a = conn[0], b = conn[1]; + edges.add(a + "," + b); + neighbors.computeIfAbsent(a, k -> new ArrayList<>()).add(b); + neighbors.computeIfAbsent(b, k -> new ArrayList<>()).add(a); + } + + dfs(0, changes); + return changes[0]; + } + + private void dfs(int city, int[] changes) { + visit[city] = true; + for (int neighbor : neighbors.get(city)) { + if (visit[neighbor]) continue; + if (!edges.contains(neighbor + "," + city)) { + changes[0]++; + } + dfs(neighbor, changes); + } + } +} +``` + +```cpp +class Solution { +public: + int minReorder(int n, vector>& connections) { + unordered_set edges; + unordered_map> neighbors; + vector visit(n, false); + int changes = 0; + + for (auto& c : connections) { + edges.insert(to_string(c[0]) + "," + to_string(c[1])); + neighbors[c[0]].push_back(c[1]); + neighbors[c[1]].push_back(c[0]); + } + + function dfs = [&](int city) { + visit[city] = true; + for (int neighbor : neighbors[city]) { + if (visit[neighbor]) continue; + if (edges.find(to_string(neighbor) + "," + to_string(city)) == edges.end()) { + changes++; + } + dfs(neighbor); + } + }; + + dfs(0); + return changes; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} connections + * @return {number} + */ + minReorder(n, connections) { + const edges = new Set(); + const neighbors = Array.from({ length: n }, () => []); + const visit = new Array(n).fill(false); + let changes = 0; + + for (const [a, b] of connections) { + edges.add(`${a},${b}`); + neighbors[a].push(b); + neighbors[b].push(a); + } + + const dfs = (city) => { + visit[city] = true; + for (const neighbor of neighbors[city]) { + if (visit[neighbor]) continue; + if (!edges.has(`${neighbor},${city}`)) changes++; + dfs(neighbor); + } + }; + + dfs(0); + return changes; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Depth First Search - II + +::tabs-start + +```python +class Solution: + def minReorder(self, n: int, connections: List[List[int]]) -> int: + adj = [[] for _ in range(n)] + for u, v in connections: + adj[u].append(v) + adj[v].append(-u) + + def dfs(node, parent): + changes = 0 + for nei in adj[node]: + if abs(nei) == parent: + continue + changes += dfs(abs(nei), node) + (nei > 0) + return changes + + return dfs(0, -1) +``` + +```java +public class Solution { + public int minReorder(int n, int[][] connections) { + List> adj = new ArrayList<>(); + for (int i = 0; i < n; i++) adj.add(new ArrayList<>()); + + for (int[] conn : connections) { + adj.get(conn[0]).add(conn[1]); + adj.get(conn[1]).add(-conn[0]); + } + + return dfs(0, -1, adj); + } + + private int dfs(int node, int parent, List> adj) { + int changes = 0; + for (int nei : adj.get(node)) { + if (Math.abs(nei) == parent) continue; + changes += dfs(Math.abs(nei), node, adj) + (nei > 0 ? 1 : 0); + } + return changes; + } +} +``` + +```cpp +class Solution { +public: + int minReorder(int n, vector>& connections) { + vector> adj(n); + for (auto& conn : connections) { + int u = conn[0], v = conn[1]; + adj[u].push_back(v); + adj[v].push_back(-u); + } + + return dfs(0, -1, adj); + } + +private: + int dfs(int node, int parent, vector>& adj) { + int changes = 0; + for (int nei : adj[node]) { + if (abs(nei) == parent) continue; + changes += dfs(abs(nei), node, adj) + (nei > 0); + } + return changes; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} connections + * @return {number} + */ + minReorder(n, connections) { + const adj = Array.from({ length: n }, () => []); + for (const [u, v] of connections) { + adj[u].push(v); + adj[v].push(-u); + } + + const dfs = (node, parent) => { + let changes = 0; + for (const nei of adj[node]) { + if (Math.abs(nei) === parent) continue; + changes += dfs(Math.abs(nei), node) + (nei > 0 ? 1 : 0); + } + return changes; + }; + + return dfs(0, -1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Breadth First Search + +::tabs-start + +```python +class Solution: + def minReorder(self, n: int, connections: List[List[int]]) -> int: + adj = [[] for _ in range(n)] + for u, v in connections: + adj[u].append((v, 1)) + adj[v].append((u, 0)) + + visit = [False] * n + queue = deque([0]) + visit[0] = True + changes = 0 + + while queue: + node = queue.popleft() + for neighbor, isForward in adj[node]: + if not visit[neighbor]: + visit[neighbor] = True + changes += isForward + queue.append(neighbor) + return changes +``` + +```java +public class Solution { + public int minReorder(int n, int[][] connections) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) adj[i] = new ArrayList<>(); + + for (int[] conn : connections) { + adj[conn[0]].add(new int[]{conn[1], 1}); + adj[conn[1]].add(new int[]{conn[0], 0}); + } + + boolean[] visited = new boolean[n]; + Queue queue = new LinkedList<>(); + queue.add(0); + visited[0] = true; + int changes = 0; + + while (!queue.isEmpty()) { + int node = queue.poll(); + for (int[] edge : adj[node]) { + int neighbor = edge[0], isForward = edge[1]; + if (!visited[neighbor]) { + visited[neighbor] = true; + changes += isForward; + queue.add(neighbor); + } + } + } + return changes; + } +} +``` + +```cpp +class Solution { +public: + int minReorder(int n, vector>& connections) { + vector>> adj(n); + for (auto& conn : connections) { + adj[conn[0]].push_back({conn[1], 1}); + adj[conn[1]].push_back({conn[0], 0}); + } + + vector visit(n, false); + queue q; + q.push(0); + visit[0] = true; + int changes = 0; + + while (!q.empty()) { + int node = q.front(); + q.pop(); + for (auto& [neighbor, isForward] : adj[node]) { + if (!visit[neighbor]) { + visit[neighbor] = true; + changes += isForward; + q.push(neighbor); + } + } + } + return changes; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} connections + * @return {number} + */ + minReorder(n, connections) { + const adj = Array.from({ length: n }, () => []); + for (const [u, v] of connections) { + adj[u].push([v, 1]); + adj[v].push([u, 0]); + } + + const visited = Array(n).fill(false); + const queue = new Queue(); + queue.push(0); + visited[0] = true; + let changes = 0; + + while (!queue.isEmpty()) { + const node = queue.pop(); + for (const [neighbor, isForward] of adj[node]) { + if (!visited[neighbor]) { + visited[neighbor] = true; + changes += isForward; + queue.push(neighbor); + } + } + } + return changes; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/reorganize-string.md b/articles/reorganize-string.md new file mode 100644 index 000000000..868c78dca --- /dev/null +++ b/articles/reorganize-string.md @@ -0,0 +1,663 @@ +## 1. Frequency Count + +::tabs-start + +```python +class Solution: + def reorganizeString(self, s: str) -> str: + freq = [0] * 26 + for char in s: + freq[ord(char) - ord('a')] += 1 + + max_freq = max(freq) + if max_freq > (len(s) + 1) // 2: + return "" + + res = [] + while len(res) < len(s): + maxIdx = freq.index(max(freq)) + char = chr(maxIdx + ord('a')) + res.append(char) + freq[maxIdx] -= 1 + if freq[maxIdx] == 0: + continue + + tmp = freq[maxIdx] + freq[maxIdx] = float("-inf") + nextMaxIdx = freq.index(max(freq)) + char = chr(nextMaxIdx + ord('a')) + res.append(char) + freq[maxIdx] = tmp + freq[nextMaxIdx] -= 1 + + return ''.join(res) +``` + +```java +public class Solution { + public String reorganizeString(String s) { + int[] freq = new int[26]; + for (char c : s.toCharArray()) { + freq[c - 'a']++; + } + + int maxFreq = Arrays.stream(freq).max().getAsInt(); + if (maxFreq > (s.length() + 1) / 2) { + return ""; + } + + StringBuilder res = new StringBuilder(); + while (res.length() < s.length()) { + int maxIdx = findMaxIndex(freq); + char maxChar = (char) (maxIdx + 'a'); + res.append(maxChar); + freq[maxIdx]--; + + if (freq[maxIdx] == 0) { + continue; + } + + int tmp = freq[maxIdx]; + freq[maxIdx] = Integer.MIN_VALUE; + int nextMaxIdx = findMaxIndex(freq); + char nextMaxChar = (char) (nextMaxIdx + 'a'); + res.append(nextMaxChar); + freq[maxIdx] = tmp; + freq[nextMaxIdx]--; + } + + return res.toString(); + } + + private int findMaxIndex(int[] freq) { + int maxIdx = 0; + for (int i = 1; i < freq.length; i++) { + if (freq[i] > freq[maxIdx]) { + maxIdx = i; + } + } + return maxIdx; + } +} +``` + +```cpp +class Solution { +public: + string reorganizeString(string s) { + vector freq(26, 0); + for (char c : s) { + freq[c - 'a']++; + } + + int maxFreq = *max_element(freq.begin(), freq.end()); + if (maxFreq > (s.size() + 1) / 2) { + return ""; + } + + string res; + while (res.size() < s.size()) { + int maxIdx = findMaxIndex(freq); + char maxChar = 'a' + maxIdx; + res += maxChar; + freq[maxIdx]--; + + if (freq[maxIdx] == 0) { + continue; + } + + int tmp = freq[maxIdx]; + freq[maxIdx] = INT_MIN; + int nextMaxIdx = findMaxIndex(freq); + char nextMaxChar = 'a' + nextMaxIdx; + res += nextMaxChar; + freq[maxIdx] = tmp; + freq[nextMaxIdx]--; + } + + return res; + } + +private: + int findMaxIndex(const vector& freq) { + int maxIdx = 0; + for (int i = 1; i < freq.size(); i++) { + if (freq[i] > freq[maxIdx]) { + maxIdx = i; + } + } + return maxIdx; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + reorganizeString(s) { + const freq = new Array(26).fill(0); + for (const char of s) { + freq[char.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + + const maxFreq = Math.max(...freq); + if (maxFreq > Math.floor((s.length + 1) / 2)) { + return ""; + } + + const findMaxIndex = () => { + let maxIdx = 0; + for (let i = 1; i < freq.length; i++) { + if (freq[i] > freq[maxIdx]) { + maxIdx = i; + } + } + return maxIdx; + }; + + const res = []; + while (res.length < s.length) { + const maxIdx = findMaxIndex(); + const maxChar = String.fromCharCode(maxIdx + 'a'.charCodeAt(0)); + res.push(maxChar); + freq[maxIdx]--; + + if (freq[maxIdx] === 0) { + continue; + } + + const tmp = freq[maxIdx]; + freq[maxIdx] = -Infinity; + const nextMaxIdx = findMaxIndex(); + const nextMaxChar = String.fromCharCode(nextMaxIdx + 'a'.charCodeAt(0)); + res.push(nextMaxChar); + freq[maxIdx] = tmp; + freq[nextMaxIdx]--; + } + + return res.join(''); + } +} +``` + +```csharp +public class Solution { + public string ReorganizeString(string s) { + int[] freq = new int[26]; + foreach (char c in s) { + freq[c - 'a']++; + } + + int maxFreq = freq.Max(); + if (maxFreq > (s.Length + 1) / 2) { + return ""; + } + + List res = new List(); + while (res.Count < s.Length) { + int maxIdx = Array.IndexOf(freq, freq.Max()); + char ch = (char)(maxIdx + 'a'); + res.Add(ch); + freq[maxIdx]--; + + if (freq[maxIdx] == 0) continue; + + int tmp = freq[maxIdx]; + freq[maxIdx] = int.MinValue; + int nextMaxIdx = Array.IndexOf(freq, freq.Max()); + char nextCh = (char)(nextMaxIdx + 'a'); + res.Add(nextCh); + freq[maxIdx] = tmp; + freq[nextMaxIdx]--; + } + + return new string(res.ToArray()); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space, since we have at most $26$ different characters. + * $O(n)$ space for the output string. + +--- + +## 2. Frequency Count (Max-Heap) + +::tabs-start + +```python +class Solution: + def reorganizeString(self, s: str) -> str: + count = Counter(s) + maxHeap = [[-cnt, char] for char, cnt in count.items()] + heapq.heapify(maxHeap) + + prev = None + res = "" + while maxHeap or prev: + if prev and not maxHeap: + return "" + + cnt, char = heapq.heappop(maxHeap) + res += char + cnt += 1 + + if prev: + heapq.heappush(maxHeap, prev) + prev = None + + if cnt != 0: + prev = [cnt, char] + + return res +``` + +```java +public class Solution { + public String reorganizeString(String s) { + int[] freq = new int[26]; + for (char c : s.toCharArray()) { + freq[c - 'a']++; + } + + PriorityQueue maxHeap = new PriorityQueue<>((a, b) -> b[0] - a[0]); + for (int i = 0; i < 26; i++) { + if (freq[i] > 0) { + maxHeap.offer(new int[]{freq[i], i}); + } + } + + StringBuilder res = new StringBuilder(); + int[] prev = null; + while (!maxHeap.isEmpty() || prev != null) { + if (prev != null && maxHeap.isEmpty()) { + return ""; + } + + int[] curr = maxHeap.poll(); + res.append((char) (curr[1] + 'a')); + curr[0]--; + + if (prev != null) { + maxHeap.offer(prev); + prev = null; + } + + if (curr[0] > 0) { + prev = curr; + } + } + + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string reorganizeString(string s) { + vector freq(26, 0); + for (char& c : s) { + freq[c - 'a']++; + } + + priority_queue> maxHeap; + for (int i = 0; i < 26; i++) { + if (freq[i] > 0) { + maxHeap.push({freq[i], 'a' + i}); + } + } + + string res = ""; + pair prev = {0, ' '}; + while (!maxHeap.empty() || prev.first > 0) { + if (prev.first > 0 && maxHeap.empty()) { + return ""; + } + + auto [count, char_] = maxHeap.top(); + maxHeap.pop(); + res += char_; + count--; + + if (prev.first > 0) { + maxHeap.push(prev); + } + + prev = {count, char_}; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + reorganizeString(s) { + const freq = new Array(26).fill(0); + for (const char of s) { + freq[char.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + + const maxHeap = new MaxPriorityQueue({ + priority: ([count]) => count, + }); + for (let i = 0; i < 26; i++) { + if (freq[i] > 0) { + maxHeap.enqueue([freq[i], String.fromCharCode(i + 'a'.charCodeAt(0))]); + } + } + + let res = ''; + let prev = null; + + while (!maxHeap.isEmpty() || prev) { + if (prev && maxHeap.isEmpty()) { + return ''; + } + + const [count, char] = maxHeap.dequeue().element; + res += char; + + if (prev) { + maxHeap.enqueue(prev); + prev = null; + } + if (count > 1) { + prev = [count - 1, char]; + } + } + + return res; + } +} +``` + +```csharp +public class Solution { + public string ReorganizeString(string s) { + Dictionary count = new(); + foreach (char c in s) { + if (!count.ContainsKey(c)) count[c] = 0; + count[c]++; + } + + PriorityQueue maxHeap = new(); + foreach (var kvp in count) { + maxHeap.Enqueue(new int[] { kvp.Value, kvp.Key }, -kvp.Value); + } + + string res = ""; + int[] prev = null; + + while (maxHeap.Count > 0 || prev != null) { + if (prev != null && maxHeap.Count == 0) return ""; + + int[] curr = maxHeap.Dequeue(); + res += (char)curr[1]; + curr[0]--; + + if (prev != null) { + maxHeap.Enqueue(prev, -prev[0]); + prev = null; + } + + if (curr[0] > 0) { + prev = curr; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space, since we have at most $26$ different characters. + * $O(n)$ space for the output string. + +--- + +## 3. Frequency Count (Greedy) + +::tabs-start + +```python +class Solution: + def reorganizeString(self, s: str) -> str: + freq = [0] * 26 + for char in s: + freq[ord(char) - ord('a')] += 1 + + max_idx = freq.index(max(freq)) + max_freq = freq[max_idx] + if max_freq > (len(s) + 1) // 2: + return "" + + res = [''] * len(s) + idx = 0 + max_char = chr(max_idx + ord('a')) + + while freq[max_idx] > 0: + res[idx] = max_char + idx += 2 + freq[max_idx] -= 1 + + for i in range(26): + while freq[i] > 0: + if idx >= len(s): + idx = 1 + res[idx] = chr(i + ord('a')) + idx += 2 + freq[i] -= 1 + + return ''.join(res) +``` + +```java +public class Solution { + public String reorganizeString(String s) { + int[] freq = new int[26]; + for (char c : s.toCharArray()) { + freq[c - 'a']++; + } + + int maxIdx = 0; + for (int i = 1; i < 26; i++) { + if (freq[i] > freq[maxIdx]) { + maxIdx = i; + } + } + + int maxFreq = freq[maxIdx]; + if (maxFreq > (s.length() + 1) / 2) { + return ""; + } + + char[] res = new char[s.length()]; + int idx = 0; + char maxChar = (char) (maxIdx + 'a'); + + while (freq[maxIdx] > 0) { + res[idx] = maxChar; + idx += 2; + freq[maxIdx]--; + } + + for (int i = 0; i < 26; i++) { + while (freq[i] > 0) { + if (idx >= s.length()) { + idx = 1; + } + res[idx] = (char) (i + 'a'); + idx += 2; + freq[i]--; + } + } + + return new String(res); + } +} +``` + +```cpp +class Solution { +public: + string reorganizeString(string s) { + vector freq(26, 0); + for (char& c : s) { + freq[c - 'a']++; + } + + int maxIdx = max_element(freq.begin(), freq.end()) - freq.begin(); + int maxFreq = freq[maxIdx]; + if (maxFreq > (s.size() + 1) / 2) { + return ""; + } + + string res(s.size(), ' '); + int idx = 0; + char maxChar = 'a' + maxIdx; + + while (freq[maxIdx] > 0) { + res[idx] = maxChar; + idx += 2; + freq[maxIdx]--; + } + + for (int i = 0; i < 26; i++) { + while (freq[i] > 0) { + if (idx >= s.size()) { + idx = 1; + } + res[idx] = 'a' + i; + idx += 2; + freq[i]--; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + reorganizeString(s) { + const freq = new Array(26).fill(0); + for (const char of s) { + freq[char.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + + let maxIdx = 0; + for (let i = 1; i < 26; i++) { + if (freq[i] > freq[maxIdx]) { + maxIdx = i; + } + } + + const maxFreq = freq[maxIdx]; + if (maxFreq > Math.floor((s.length + 1) / 2)) { + return ''; + } + + const res = new Array(s.length).fill(''); + let idx = 0; + const maxChar = String.fromCharCode(maxIdx + 'a'.charCodeAt(0)); + + while (freq[maxIdx] > 0) { + res[idx] = maxChar; + idx += 2; + freq[maxIdx]--; + } + + for (let i = 0; i < 26; i++) { + while (freq[i] > 0) { + if (idx >= s.length) { + idx = 1; + } + res[idx] = String.fromCharCode(i + 'a'.charCodeAt(0)); + idx += 2; + freq[i]--; + } + } + + return res.join(''); + } +} +``` + +```csharp +public class Solution { + public string ReorganizeString(string s) { + int[] freq = new int[26]; + foreach (char c in s) { + freq[c - 'a']++; + } + + int maxIdx = 0; + for (int i = 1; i < 26; i++) { + if (freq[i] > freq[maxIdx]) { + maxIdx = i; + } + } + + int maxFreq = freq[maxIdx]; + if (maxFreq > (s.Length + 1) / 2) return ""; + + char[] res = new char[s.Length]; + int idx = 0; + char maxChar = (char)(maxIdx + 'a'); + + while (freq[maxIdx] > 0) { + res[idx] = maxChar; + idx += 2; + freq[maxIdx]--; + } + + for (int i = 0; i < 26; i++) { + while (freq[i] > 0) { + if (idx >= s.Length) idx = 1; + res[idx] = (char)(i + 'a'); + idx += 2; + freq[i]--; + } + } + + return new string(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space, since we have at most $26$ different characters. + * $O(n)$ space for the output string. \ No newline at end of file diff --git a/articles/repeated-dna-sequences.md b/articles/repeated-dna-sequences.md new file mode 100644 index 000000000..6976f8850 --- /dev/null +++ b/articles/repeated-dna-sequences.md @@ -0,0 +1,470 @@ +## 1. Hash Set + +::tabs-start + +```python +class Solution: + def findRepeatedDnaSequences(self, s: str) -> List[str]: + seen, res = set(), set() + + for l in range(len(s) - 9): + cur = s[l: l + 10] + if cur in seen: + res.add(cur) + seen.add(cur) + return list(res) +``` + +```java +public class Solution { + public List findRepeatedDnaSequences(String s) { + Set seen = new HashSet<>(); + Set res = new HashSet<>(); + + for (int l = 0; l < s.length() - 9; l++) { + String cur = s.substring(l, l + 10); + if (seen.contains(cur)) { + res.add(cur); + } + seen.add(cur); + } + return new ArrayList<>(res); + } +} +``` + +```cpp +class Solution { +public: + vector findRepeatedDnaSequences(string s) { + if (s.size() < 10) return {}; + unordered_set seen, res; + + for (int l = 0; l < s.size() - 9; l++) { + string cur = s.substr(l, 10); + if (seen.count(cur)) { + res.insert(cur); + } + seen.insert(cur); + } + return vector(res.begin(), res.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string[]} + */ + findRepeatedDnaSequences(s) { + const seen = new Set(); + const res = new Set(); + + for (let l = 0; l < s.length - 9; l++) { + const cur = s.substring(l, l + 10); + if (seen.has(cur)) { + res.add(cur); + } + seen.add(cur); + } + return Array.from(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def findRepeatedDnaSequences(self, s: str) -> List[str]: + if len(s) < 10: + return [] + + mp = defaultdict(int) + res = [] + for l in range(len(s) - 9): + cur = s[l: l + 10] + mp[cur] += 1 + if mp[cur] == 2: + res.append(cur) + + return res +``` + +```java +public class Solution { + public List findRepeatedDnaSequences(String s) { + if (s.length() < 10) { + return new ArrayList<>(); + } + + Map mp = new HashMap<>(); + List res = new ArrayList<>(); + + for (int l = 0; l < s.length() - 9; l++) { + String cur = s.substring(l, l + 10); + mp.put(cur, mp.getOrDefault(cur, 0) + 1); + if (mp.get(cur) == 2) { + res.add(cur); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findRepeatedDnaSequences(string s) { + if (s.size() < 10) { + return {}; + } + + unordered_map mp; + vector res; + + for (int l = 0; l < s.size() - 9; l++) { + string cur = s.substr(l, 10); + mp[cur]++; + if (mp[cur] == 2) { + res.push_back(cur); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string[]} + */ + findRepeatedDnaSequences(s) { + if (s.length < 10) { + return []; + } + + const mp = new Map(); + const res = []; + + for (let l = 0; l <= s.length - 10; l++) { + const cur = s.substring(l, l + 10); + mp.set(cur, (mp.get(cur) || 0) + 1); + if (mp.get(cur) === 2) { + res.push(cur); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Rabin-Karp Algorithm (Double Hashing) + +::tabs-start + +```python +class Solution: + def findRepeatedDnaSequences(self, s: str) -> List[str]: + n = len(s) + if n < 10: + return [] + + cnt = defaultdict(int) + res = [] + base1, base2 = 31, 37 + hash1 = hash2 = 0 + power1, power2 = 1, 1 + MOD1, MOD2 = 685683731, 768258391 + + for i in range(9): + power1 *= base1 + power2 *= base2 + power1 %= MOD1 + power2 %= MOD2 + + for i in range(n): + hash1 = (hash1 * base1 + ord(s[i])) % MOD1 + hash2 = (hash2 * base2 + ord(s[i])) % MOD2 + + if i >= 9: + key = (hash1 << 31) | hash2 + cnt[key] += 1 + if cnt[key] == 2: + res.append(s[i - 9 : i + 1]) + + hash1 = (hash1 - power1 * ord(s[i - 9]) % MOD1 + MOD1) % MOD1 + hash2 = (hash2 - power2 * ord(s[i - 9]) % MOD2 + MOD2) + + return res +``` + +```java +public class Solution { + public List findRepeatedDnaSequences(String s) { + int n = s.length(); + if (n < 10) return new ArrayList<>(); + + Map cnt = new HashMap<>(); + List res = new ArrayList<>(); + int base1 = 31, base2 = 37; + long hash1 = 0, hash2 = 0, power1 = 1, power2 = 1; + int MOD1 = 685683731, MOD2 = 768258391; + + for (int i = 0; i < 9; i++) { + power1 = (power1 * base1) % MOD1; + power2 = (power2 * base2) % MOD2; + } + + for (int i = 0; i < n; i++) { + hash1 = (hash1 * base1 + s.charAt(i)) % MOD1; + hash2 = (hash2 * base2 + s.charAt(i)) % MOD2; + + if (i >= 9) { + long key = (hash1 << 31) | hash2; + cnt.put(key, cnt.getOrDefault(key, 0) + 1); + if (cnt.get(key) == 2) { + res.add(s.substring(i - 9, i + 1)); + } + + hash1 = (hash1 - power1 * s.charAt(i - 9) % MOD1 + MOD1) % MOD1; + hash2 = (hash2 - power2 * s.charAt(i - 9) % MOD2 + MOD2) % MOD2; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findRepeatedDnaSequences(string s) { + int n = s.length(); + if (n < 10) return {}; + + unordered_map cnt; + vector res; + int base1 = 31, base2 = 37; + long long hash1 = 0, hash2 = 0, power1 = 1, power2 = 1; + int MOD1 = 685683731, MOD2 = 768258391; + + for (int i = 0; i < 9; i++) { + power1 = (power1 * base1) % MOD1; + power2 = (power2 * base2) % MOD2; + } + + for (int i = 0; i < n; i++) { + hash1 = (hash1 * base1 + s[i]) % MOD1; + hash2 = (hash2 * base2 + s[i]) % MOD2; + + if (i >= 9) { + long long key = (hash1 << 31) | hash2; + cnt[key]++; + if (cnt[key] == 2) { + res.push_back(s.substr(i - 9, 10)); + } + + hash1 = (hash1 - power1 * s[i - 9] % MOD1 + MOD1) % MOD1; + hash2 = (hash2 - power2 * s[i - 9] % MOD2 + MOD2) % MOD2; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string[]} + */ + findRepeatedDnaSequences(s) { + const n = s.length; + if (n < 10) return []; + + const cnt = new Map(); + const res = []; + const base1 = 31, base2 = 37; + let hash1 = 0, hash2 = 0, power1 = 1, power2 = 1; + const MOD1 = 685683731, MOD2 = 768258391; + + for (let i = 0; i < 9; i++) { + power1 = (power1 * base1) % MOD1; + power2 = (power2 * base2) % MOD2; + } + + for (let i = 0; i < n; i++) { + hash1 = (hash1 * base1 + s.charCodeAt(i)) % MOD1; + hash2 = (hash2 * base2 + s.charCodeAt(i)) % MOD2; + + if (i >= 9) { + const key = `${hash1},${hash2}`; + cnt.set(key, (cnt.get(key) || 0) + 1); + if (cnt.get(key) === 2) { + res.push(s.substring(i - 9, i + 1)); + } + + hash1 = (hash1 - power1 * s.charCodeAt(i - 9) % MOD1 + MOD1) % MOD1; + hash2 = (hash2 - power2 * s.charCodeAt(i - 9) % MOD2 + MOD2) % MOD2; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Bit Mask + +::tabs-start + +```python +class Solution: + def findRepeatedDnaSequences(self, s: str) -> list[str]: + if len(s) < 10: + return [] + + mp = {'A': 0, 'C': 1, 'G': 2, 'T': 3} + seen, res = set(), set() + mask = 0 + for i in range(len(s)): + mask = ((mask << 2) | mp[s[i]]) & 0xFFFFF + if i >= 9: + if mask in seen: + res.add(s[i - 9: i + 1]) + else: + seen.add(mask) + + return list(res) +``` + +```java +public class Solution { + public List findRepeatedDnaSequences(String s) { + if (s.length() < 10) return new ArrayList<>(); + + Map mp = Map.of('A', 0, 'C', 1, 'G', 2, 'T', 3); + Map cnt = new HashMap<>(); + List res = new ArrayList<>(); + int mask = 0; + + for (int i = 0; i < s.length(); i++) { + mask = ((mask << 2) | mp.get(s.charAt(i))) & 0xFFFFF; + if (i >= 9) { + cnt.put(mask, cnt.getOrDefault(mask, 0) + 1); + if (cnt.get(mask) == 2) { + res.add(s.substring(i - 9, i + 1)); + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findRepeatedDnaSequences(string s) { + if (s.length() < 10) return {}; + + unordered_map mp = {{'A', 0}, {'C', 1}, + {'G', 2}, {'T', 3}}; + unordered_map cnt; + vector res; + int mask = 0; + + for (int i = 0; i < s.size(); i++) { + mask = ((mask << 2) | mp[s[i]]) & 0xFFFFF; + + if (i >= 9) { + cnt[mask]++; + if (cnt[mask] == 2) { + res.push_back(s.substr(i - 9, 10)); + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string[]} + */ + findRepeatedDnaSequences(s) { + if (s.length < 10) return []; + + const mp = { 'A': 0, 'C': 1, 'G': 2, 'T': 3 }; + const cnt = new Map(); + const res = []; + let mask = 0; + + for (let i = 0; i < s.length; i++) { + mask = ((mask << 2) | mp[s[i]]) & 0xFFFFF; + + if (i >= 9) { + cnt.set(mask, (cnt.get(mask) || 0) + 1); + if (cnt.get(mask) === 2) { + res.push(s.substring(i - 9, i + 1)); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/replace-elements-with-greatest-element-on-right-side.md b/articles/replace-elements-with-greatest-element-on-right-side.md new file mode 100644 index 000000000..1825436a7 --- /dev/null +++ b/articles/replace-elements-with-greatest-element-on-right-side.md @@ -0,0 +1,154 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def replaceElements(self, arr: List[int]) -> List[int]: + n = len(arr) + ans = [0] * n + for i in range(n): + rightMax = -1 + for j in range(i + 1, n): + rightMax = max(rightMax, arr[j]) + ans[i] = rightMax + return ans +``` + +```java +public class Solution { + public int[] replaceElements(int[] arr) { + int n = arr.length; + int[] ans = new int[n]; + for (int i = 0; i < n; i++) { + int rightMax = -1; + for (int j = i + 1; j < n; j++) { + rightMax = Math.max(rightMax, arr[j]); + } + ans[i] = rightMax; + } + return ans; + } +} +``` + +```cpp +class Solution { +public: + vector replaceElements(vector& arr) { + int n = arr.size(); + vector ans(n); + for (int i = 0; i < n; ++i) { + int rightMax = -1; + for (int j = i + 1; j < n; ++j) { + rightMax = max(rightMax, arr[j]); + } + ans[i] = rightMax; + } + return ans; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number[]} + */ + replaceElements(arr) { + let n = arr.length; + let ans = new Array(n); + for (let i = 0; i < n; i++) { + let rightMax = -1; + for (let j = i + 1; j < n; j++) { + rightMax = Math.max(rightMax, arr[j]); + } + ans[i] = rightMax; + } + return ans; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Suffix Max + +::tabs-start + +```python +class Solution: + def replaceElements(self, arr: List[int]) -> List[int]: + n = len(arr) + ans = [0] * n + rightMax = -1 + for i in range(n - 1, -1, -1): + ans[i] = rightMax + rightMax = max(arr[i], rightMax) + return ans +``` + +```java +public class Solution { + public int[] replaceElements(int[] arr) { + int n = arr.length; + int[] ans = new int[n]; + int rightMax = -1; + for (int i = n - 1; i >= 0; i--) { + ans[i] = rightMax; + rightMax = Math.max(rightMax, arr[i]); + } + return ans; + } +} +``` + +```cpp +class Solution { +public: + vector replaceElements(vector& arr) { + int n = arr.size(); + vector ans(n); + int rightMax = -1; + for (int i = n - 1; i >= 0; --i) { + ans[i] = rightMax; + rightMax = max(rightMax, arr[i]); + } + return ans; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number[]} + */ + replaceElements(arr) { + let n = arr.length; + let ans = new Array(n); + let rightMax = -1; + for (let i = n - 1; i >= 0; i--) { + ans[i] = rightMax; + rightMax = Math.max(rightMax, arr[i]); + } + return ans; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/restore-ip-addresses.md b/articles/restore-ip-addresses.md new file mode 100644 index 000000000..a0a6fd2b6 --- /dev/null +++ b/articles/restore-ip-addresses.md @@ -0,0 +1,278 @@ +## 1. Backtracking + +::tabs-start + +```python +class Solution: + def restoreIpAddresses(self, s: str) -> List[str]: + res = [] + if len(s) > 12: + return res + + def backtrack(i, dots, curIP): + if dots == 4 and i == len(s): + res.append(curIP[:-1]) + return + if dots > 4: + return + + for j in range(i, min(i + 3, len(s))): + if i != j and s[i] == "0": + continue + if int(s[i: j + 1]) < 256: + backtrack(j + 1, dots + 1, curIP + s[i: j + 1] + ".") + + backtrack(0, 0, "") + return res +``` + +```java +public class Solution { + public List restoreIpAddresses(String s) { + List res = new ArrayList<>(); + if (s.length() > 12) return res; + + backtrack(0, 0, "", s, res); + return res; + } + + private void backtrack(int i, int dots, String curIP, String s, List res) { + if (dots == 4 && i == s.length()) { + res.add(curIP.substring(0, curIP.length() - 1)); + return; + } + if (dots > 4) return; + + for (int j = i; j < Math.min(i + 3, s.length()); j++) { + if (i != j && s.charAt(i) == '0') continue; + if (Integer.parseInt(s.substring(i, j + 1)) < 256) { + backtrack(j + 1, dots + 1, curIP + s.substring(i, j + 1) + ".", s, res); + } + } + } +} +``` + +```cpp +class Solution { + vector res; + +public: + vector restoreIpAddresses(string s) { + if (s.length() > 12) return res; + backtrack(s, 0, 0, ""); + return res; + } + +private: + void backtrack(string& s, int i, int dots, string curIP) { + if (dots == 4 && i == s.size()) { + res.push_back(curIP.substr(0, curIP.size() - 1)); + return; + } + if (dots > 4) return; + + for (int j = i; j < min(i + 3, (int)s.size()); j++) { + if (i != j && s[i] == '0') continue; + if (stoi(s.substr(i, j - i + 1)) < 256) { + backtrack(s, j + 1, dots + 1, curIP + s.substr(i, j - i + 1) + "."); + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string[]} + */ + restoreIpAddresses(s) { + const res = []; + if (s.length > 12) return res; + + const backtrack = (i, dots, curIP) => { + if (dots === 4 && i === s.length) { + res.push(curIP.slice(0, -1)); + return; + } + if (dots > 4) return; + + for (let j = i; j < Math.min(i + 3, s.length); j++) { + if (i !== j && s[i] === '0') continue; + if (parseInt(s.slice(i, j + 1)) < 256) { + backtrack(j + 1, dots + 1, curIP + s.slice(i, j + 1) + '.'); + } + } + }; + + backtrack(0, 0, ""); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m ^ n * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is equals to $3$ as there are at most three digits in a valid segment and $n$ is equals to $4$ as there are four segments in a valid IP. + +--- + +## 2. Iteration + +::tabs-start + +```python +class Solution: + def restoreIpAddresses(self, s: str) -> List[str]: + res = [] + if len(s) > 12: + return res + + def valid(num): + return len(num) == 1 or (int(num) < 256 and num[0] != "0") + + def add(s1, s2, s3, s4): + if s1 + s2 + s3 + s4 != len(s): + return + + num1 = s[:s1] + num2 = s[s1:s1+s2] + num3 = s[s1+s2:s1+s2+s3] + num4 = s[s1+s2+s3:] + if valid(num1) and valid(num2) and valid(num3) and valid(num4): + res.append(num1 + "." + num2 + "." + num3 + "." + num4) + + for seg1 in range(1, 4): + for seg2 in range(1, 4): + for seg3 in range(1, 4): + for seg4 in range(1, 4): + add(seg1, seg2, seg3, seg4) + + return res +``` + +```java +public class Solution { + public List restoreIpAddresses(String s) { + List res = new ArrayList<>(); + if (s.length() > 12) return res; + + for (int seg1 = 1; seg1 < 4; seg1++) { + for (int seg2 = 1; seg2 < 4; seg2++) { + for (int seg3 = 1; seg3 < 4; seg3++) { + for (int seg4 = 1; seg4 < 4; seg4++) { + if (seg1 + seg2 + seg3 + seg4 != s.length()) continue; + + String num1 = s.substring(0, seg1); + String num2 = s.substring(seg1, seg1 + seg2); + String num3 = s.substring(seg1 + seg2, seg1 + seg2 + seg3); + String num4 = s.substring(seg1 + seg2 + seg3); + + if (isValid(num1) && isValid(num2) && isValid(num3) && isValid(num4)) { + res.add(num1 + "." + num2 + "." + num3 + "." + num4); + } + } + } + } + } + return res; + } + + private boolean isValid(String num) { + if (num.length() > 1 && num.charAt(0) == '0') return false; + int value = Integer.parseInt(num); + return value <= 255; + } +} +``` + +```cpp +class Solution { +public: + vector restoreIpAddresses(string s) { + vector res; + if (s.size() > 12) return res; + + auto valid = [&](string& num) { + if (num.size() > 1 && num[0] == '0') return false; + int value = stoi(num); + return value <= 255; + }; + + for (int seg1 = 1; seg1 < 4; ++seg1) { + for (int seg2 = 1; seg2 < 4; ++seg2) { + for (int seg3 = 1; seg3 < 4; ++seg3) { + for (int seg4 = 1; seg4 < 4; ++seg4) { + if (seg1 + seg2 + seg3 + seg4 != s.size()) continue; + + string num1 = s.substr(0, seg1); + string num2 = s.substr(seg1, seg2); + string num3 = s.substr(seg1 + seg2, seg3); + string num4 = s.substr(seg1 + seg2 + seg3); + + if (valid(num1) && valid(num2) && valid(num3) && valid(num4)) { + res.push_back(num1 + "." + num2 + "." + num3 + "." + num4); + } + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string[]} + */ + restoreIpAddresses(s) { + const res = []; + if (s.length > 12) return res; + + const isValid = (num) => { + if (num.length > 1 && num[0] === '0') return false; + const value = parseInt(num, 10); + return value <= 255; + }; + + for (let seg1 = 1; seg1 < 4; seg1++) { + for (let seg2 = 1; seg2 < 4; seg2++) { + for (let seg3 = 1; seg3 < 4; seg3++) { + for (let seg4 = 1; seg4 < 4; seg4++) { + if (seg1 + seg2 + seg3 + seg4 !== s.length) continue; + + const num1 = s.substring(0, seg1); + const num2 = s.substring(seg1, seg1 + seg2); + const num3 = s.substring(seg1 + seg2, seg1 + seg2 + seg3); + const num4 = s.substring(seg1 + seg2 + seg3); + + if (isValid(num1) && isValid(num2) && isValid(num3) && isValid(num4)) { + res.push(`${num1}.${num2}.${num3}.${num4}`); + } + } + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m ^ n * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is equals to $3$ as there are at most three digits in a valid segment and $n$ is equals to $4$ as there are four segments in a valid IP. \ No newline at end of file diff --git a/articles/reveal-cards-in-increasing-order.md b/articles/reveal-cards-in-increasing-order.md new file mode 100644 index 000000000..4759af658 --- /dev/null +++ b/articles/reveal-cards-in-increasing-order.md @@ -0,0 +1,406 @@ +## 1. Simulation Using Queue - I + +::tabs-start + +```python +class Solution: + def deckRevealedIncreasing(self, deck: List[int]) -> List[int]: + deck.sort() + res = [0] * len(deck) + q = deque(range(len(deck))) + + for num in deck: + i = q.popleft() + res[i] = num + if q: + q.append(q.popleft()) + + return res +``` + +```java +public class Solution { + public int[] deckRevealedIncreasing(int[] deck) { + Arrays.sort(deck); + int n = deck.length; + int[] res = new int[n]; + Queue q = new LinkedList<>(); + + for (int i = 0; i < n; i++) { + q.offer(i); + } + + for (int num : deck) { + int i = q.poll(); + res[i] = num; + if (!q.isEmpty()) { + q.offer(q.poll()); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector deckRevealedIncreasing(vector& deck) { + sort(deck.begin(), deck.end()); + int n = deck.size(); + vector res(n); + queue q; + + for (int i = 0; i < n; i++) { + q.push(i); + } + + for (int num : deck) { + int i = q.front(); + q.pop(); + res[i] = num; + if (!q.empty()) { + q.push(q.front()); + q.pop(); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} deck + * @return {number[]} + */ + deckRevealedIncreasing(deck) { + deck.sort((a, b) => a - b); + let n = deck.length; + let res = new Array(n).fill(0); + const q = new Queue(); + + for (let i = 0; i < n; i++) { + q.push(i); + } + + for (let num of deck) { + let i = q.pop(); + res[i] = num; + if (!q.isEmpty()) { + q.push(q.pop()); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Simuation Using Queue - II + +::tabs-start + +```python +class Solution: + def deckRevealedIncreasing(self, deck: List[int]) -> List[int]: + deck.sort() + q = deque() + for i in range(len(deck) - 1, -1, -1): + if q: + q.append(q.popleft()) + q.append(deck[i]) + return list(q)[::-1] +``` + +```java +public class Solution { + public int[] deckRevealedIncreasing(int[] deck) { + Arrays.sort(deck); + Queue q = new LinkedList<>(); + + for (int i = deck.length - 1; i >= 0; i--) { + if (!q.isEmpty()) { + q.offer(q.poll()); + } + q.offer(deck[i]); + } + + int[] res = new int[deck.length]; + for (int i = deck.length - 1; i >= 0; i--) { + res[i] = q.poll(); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector deckRevealedIncreasing(vector& deck) { + sort(deck.begin(), deck.end()); + queue q; + + for (int i = deck.size() - 1; i >= 0; i--) { + if (!q.empty()) { + q.push(q.front()); + q.pop(); + } + q.push(deck[i]); + } + + vector res(deck.size()); + for (int i = deck.size() - 1; i >= 0; i--) { + res[i] = q.front(); + q.pop(); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} deck + * @return {number[]} + */ + deckRevealedIncreasing(deck) { + deck.sort((a, b) => a - b); + const q = new Queue(); + + for (let i = deck.length - 1; i >= 0; i--) { + if (!q.isEmpty()) { + q.push(q.pop()); + } + q.push(deck[i]); + } + + return q.toArray().reverse(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Simulation Using Deque + +::tabs-start + +```python +class Solution: + def deckRevealedIncreasing(self, deck: List[int]) -> List[int]: + deck.sort() + dq = deque() + dq.append(deck.pop()) + for i in range(len(deck) - 1, -1, -1): + dq.appendleft(dq.pop()) + dq.appendleft(deck[i]) + return list(dq) +``` + +```java +public class Solution { + public int[] deckRevealedIncreasing(int[] deck) { + int n = deck.length; + Arrays.sort(deck); + Deque dq = new LinkedList<>(); + dq.addLast(deck[n - 1]); + + for (int i = n - 2; i >= 0; i--) { + dq.addFirst(dq.removeLast()); + dq.addFirst(deck[i]); + } + + return dq.stream().mapToInt(Integer::intValue).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector deckRevealedIncreasing(vector& deck) { + sort(deck.begin(), deck.end()); + deque dq; + dq.push_back(deck.back()); + + for (int i = deck.size() - 2; i >= 0; i--) { + dq.push_front(dq.back()); + dq.pop_back(); + dq.push_front(deck[i]); + } + + return vector(dq.begin(), dq.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} deck + * @return {number[]} + */ + deckRevealedIncreasing(deck) { + deck.sort((a, b) => a - b); + const dq = new Deque([deck.pop()]); + + for (let i = deck.length - 1; i >= 0; i--) { + let val = dq.popBack(); + dq.pushFront(val); + dq.pushFront(deck[i]); + } + + let res = []; + while (!dq.isEmpty()) { + res.push(dq.popFront()); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Simulation Using Two Pointers + +::tabs-start + +```python +class Solution: + def deckRevealedIncreasing(self, deck: List[int]) -> List[int]: + n = len(deck) + res = [0] * n + skip = False + deckIndex, i = 0, 0 + + deck.sort() + + while deckIndex < n: + if res[i] == 0: + if not skip: + res[i] = deck[deckIndex] + deckIndex += 1 + skip = not skip + i = (i + 1) % n + + return res +``` + +```java +public class Solution { + public int[] deckRevealedIncreasing(int[] deck) { + int n = deck.length; + int[] res = new int[n]; + Arrays.fill(res, 0); + boolean skip = false; + int deckIndex = 0, i = 0; + + Arrays.sort(deck); + + while (deckIndex < n) { + if (res[i] == 0) { + if (!skip) { + res[i] = deck[deckIndex++]; + } + skip = !skip; + } + i = (i + 1) % n; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector deckRevealedIncreasing(vector& deck) { + int n = deck.size(); + vector res(n, 0); + bool skip = false; + int deckIndex = 0, i = 0; + + sort(deck.begin(), deck.end()); + + while (deckIndex < n) { + if (res[i] == 0) { + if (!skip) { + res[i] = deck[deckIndex++]; + } + skip = !skip; + } + i = (i + 1) % n; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} deck + * @return {number[]} + */ + deckRevealedIncreasing(deck) { + let n = deck.length; + let res = new Array(n).fill(0); + let skip = false; + let deckIndex = 0, i = 0; + + deck.sort((a, b) => a - b); + + while (deckIndex < n) { + if (res[i] === 0) { + if (!skip) { + res[i] = deck[deckIndex++]; + } + skip = !skip; + } + i = (i + 1) % n; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: + * $O(1)$ or $O(n)$ space depending on the sorting algorithm. + * $O(n)$ space for the output array. \ No newline at end of file diff --git a/articles/reverse-a-linked-list.md b/articles/reverse-a-linked-list.md new file mode 100644 index 000000000..3a205a543 --- /dev/null +++ b/articles/reverse-a-linked-list.md @@ -0,0 +1,461 @@ +## 1. Recursion + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + +class Solution: + def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]: + if not head: + return None + + newHead = head + if head.next: + newHead = self.reverseList(head.next) + head.next.next = head + head.next = None + + return newHead +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +public class Solution { + public ListNode reverseList(ListNode head) { + if (head == null) { + return null; + } + + ListNode newHead = head; + if (head.next != null) { + newHead = reverseList(head.next); + head.next.next = head; + } + head.next = null; + + return newHead; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +class Solution { +public: + ListNode* reverseList(ListNode* head) { + if (!head) { + return nullptr; + } + + ListNode* newHead = head; + if (head->next) { + newHead = reverseList(head->next); + head->next->next = head; + } + head->next = nullptr; + + return newHead; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + reverseList(head) { + if (!head) { + return null; + } + + let newHead = head; + if (head.next) { + newHead = this.reverseList(head.next); + head.next.next = head; + } + head.next = null; + + return newHead; + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +public class Solution { + public ListNode ReverseList(ListNode head) { + if (head == null) { + return null; + } + + ListNode newHead = head; + if (head.next != null) { + newHead = ReverseList(head.next); + head.next.next = head; + } + head.next = null; + + return newHead; + } +} +``` + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func reverseList(head *ListNode) *ListNode { + if head == nil { + return nil + } + + newHead := head + if head.Next != nil { + newHead = reverseList(head.Next) + head.Next.Next = head + } + head.Next = nil + + return newHead +} +``` + +```kotlin +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun reverseList(head: ListNode?): ListNode? { + if (head == null) { + return null + } + + var newHead = head + if (head.next != null) { + newHead = reverseList(head.next) + head.next?.next = head + } + head.next = null + + return newHead + } +} +``` + +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func reverseList(_ head: ListNode?) -> ListNode? { + if head == nil { + return nil + } + + var newHead = head + if head?.next != nil { + newHead = reverseList(head?.next) + head?.next?.next = head + } + head?.next = nil + + return newHead + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Iteration + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + +class Solution: + def reverseList(self, head: ListNode) -> ListNode: + prev, curr = None, head + + while curr: + temp = curr.next + curr.next = prev + prev = curr + curr = temp + return prev +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +public class Solution { + public ListNode reverseList(ListNode head) { + ListNode prev = null; + ListNode curr = head; + + while (curr != null) { + ListNode temp = curr.next; + curr.next = prev; + prev = curr; + curr = temp; + } + return prev; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +class Solution { +public: + ListNode* reverseList(ListNode* head) { + ListNode* prev = nullptr; + ListNode* curr = head; + + while (curr) { + ListNode* temp = curr->next; + curr->next = prev; + prev = curr; + curr = temp; + } + return prev; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + reverseList(head) { + let prev = null; + let curr = head; + + while (curr) { + let temp = curr.next; + curr.next = prev; + prev = curr; + curr = temp; + } + return prev; + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +public class Solution { + public ListNode ReverseList(ListNode head) { + ListNode prev = null; + ListNode curr = head; + + while (curr != null) { + ListNode temp = curr.next; + curr.next = prev; + prev = curr; + curr = temp; + } + return prev; + } +} +``` + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func reverseList(head *ListNode) *ListNode { + var prev *ListNode + curr := head + + for curr != nil { + temp := curr.Next + curr.Next = prev + prev = curr + curr = temp + } + return prev +} +``` + +```kotlin +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun reverseList(head: ListNode?): ListNode? { + var prev: ListNode? = null + var curr = head + + while (curr != null) { + val temp = curr.next + curr.next = prev + prev = curr + curr = temp + } + return prev + } +} +``` + +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func reverseList(_ head: ListNode?) -> ListNode? { + var prev: ListNode? = nil + var curr = head + + while curr != nil { + let temp = curr?.next + curr?.next = prev + prev = curr + curr = temp + } + return prev + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/reverse-bits.md b/articles/reverse-bits.md new file mode 100644 index 000000000..84cbe88ce --- /dev/null +++ b/articles/reverse-bits.md @@ -0,0 +1,438 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def reverseBits(self, n: int) -> int: + binary = "" + for i in range(32): + if n & (1 << i): + binary += "1" + else: + binary += "0" + + res = 0 + for i, bit in enumerate(binary[::-1]): + if bit == "1": + res |= (1 << i) + + return res +``` + +```java +public class Solution { + public int reverseBits(int n) { + StringBuilder binary = new StringBuilder(); + for (int i = 0; i < 32; i++) { + if ((n & (1 << i)) != 0) { + binary.append("1"); + } else { + binary.append("0"); + } + } + + int res = 0; + String reversedBinary = binary.reverse().toString(); + for (int i = 0; i < 32; i++) { + if (reversedBinary.charAt(i) == '1') { + res |= (1 << i); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + uint32_t reverseBits(uint32_t n) { + string binary = ""; + for (int i = 0; i < 32; i++) { + if (n & (1 << i)) { + binary += '1'; + } else { + binary += '0'; + } + } + + uint32_t res = 0; + for (int i = 0; i < 32; i++) { + if (binary[31 - i] == '1') { + res |= (1 << i); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n - a positive integer + * @return {number} - a positive integer + */ + reverseBits(n) { + let binary = ""; + for (let i = 0; i < 32; i++) { + if (n & (1 << i)) { + binary += "1"; + } else { + binary += "0"; + } + } + + let res = 0; + for (let i = 0; i < 32; i++) { + if (binary[31 - i] === "1") { + res |= (1 << i); + } + } + + return res >>> 0; + } +} +``` + +```csharp +public class Solution { + public uint ReverseBits(uint n) { + string binary = ""; + for (int i = 0; i < 32; i++) { + if ((n & (1 << i)) != 0) { + binary += "1"; + } else { + binary += "0"; + } + } + + uint res = 0; + for (int i = 0; i < 32; i++) { + if (binary[31 - i] == '1') { + res |= (1u << i); + } + } + + return res; + } +} +``` + +```go +func reverseBits(n uint32) uint32 { + binary := "" + for i := 0; i < 32; i++ { + if n&(1< Int { + var binary = "" + for i in 0..<32 { + if (n & (1 << i)) != 0 { + binary += "1" + } else { + binary += "0" + } + } + + var res = 0 + for (i, bit) in binary.reversed().enumerated() { + if bit == "1" { + res |= (1 << i) + } + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 2. Bit Manipulation + +::tabs-start + +```python +class Solution: + def reverseBits(self, n: int) -> int: + res = 0 + for i in range(32): + bit = (n >> i) & 1 + res += (bit << (31 - i)) + return res +``` + +```java +public class Solution { + public int reverseBits(int n) { + int res = 0; + for (int i = 0; i < 32; i++) { + int bit = (n >> i) & 1; + res += (bit << (31 - i)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + uint32_t reverseBits(uint32_t n) { + uint32_t res = 0; + for (int i = 0; i < 32; i++) { + uint32_t bit = (n >> i) & 1; + res += (bit << (31 - i)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n - a positive integer + * @return {number} - a positive integer + */ + reverseBits(n) { + let res = 0; + for (let i = 0; i < 32; i++) { + const bit = (n >>> i) & 1; + res += bit << (31 - i); + } + return res >>> 0; + } +} +``` + +```csharp +public class Solution { + public uint ReverseBits(uint n) { + uint res = 0; + for (int i = 0; i < 32; i++) { + uint bit = (n >> i) & 1; + res += (bit << (31 - i)); + } + return res; + } +} +``` + +```go +func reverseBits(n uint32) uint32 { + var res uint32 = 0 + for i := 0; i < 32; i++ { + bit := (n >> i) & 1 + res |= (bit << (31 - i)) + } + return res +} +``` + +```kotlin +class Solution { + fun reverseBits(n: Int): Int { + var res = 0 + for (i in 0 until 32) { + val bit = (n shr i) and 1 + res = res or (bit shl (31 - i)) + } + return res + } +} +``` + +```swift +class Solution { + func reverseBits(_ n: Int) -> Int { + var res = 0 + var num = n + for i in 0..<32 { + let bit = (num >> i) & 1 + res |= (bit << (31 - i)) + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 3. Bit Manipulation (Optimal) + +::tabs-start + +```python +class Solution: + def reverseBits(self, n: int) -> int: + res = n + res = (res >> 16) | (res << 16) & 0xFFFFFFFF + res = ((res & 0xff00ff00) >> 8) | ((res & 0x00ff00ff) << 8) + res = ((res & 0xf0f0f0f0) >> 4) | ((res & 0x0f0f0f0f) << 4) + res = ((res & 0xcccccccc) >> 2) | ((res & 0x33333333) << 2) + res = ((res & 0xaaaaaaaa) >> 1) | ((res & 0x55555555) << 1) + return res & 0xFFFFFFFF +``` + +```java +public class Solution { + public int reverseBits(int n) { + int ret = n; + ret = ret >>> 16 | ret << 16; + ret = (ret & 0xff00ff00) >>> 8 | (ret & 0x00ff00ff) << 8; + ret = (ret & 0xf0f0f0f0) >>> 4 | (ret & 0x0f0f0f0f) << 4; + ret = (ret & 0xcccccccc) >>> 2 | (ret & 0x33333333) << 2; + ret = (ret & 0xaaaaaaaa) >>> 1 | (ret & 0x55555555) << 1; + return ret; + } +} +``` + +```cpp +class Solution { +public: + uint32_t reverseBits(uint32_t n) { + uint32_t ret = n; + ret = (ret >> 16) | (ret << 16); + ret = ((ret & 0xff00ff00) >> 8) | ((ret & 0x00ff00ff) << 8); + ret = ((ret & 0xf0f0f0f0) >> 4) | ((ret & 0x0f0f0f0f) << 4); + ret = ((ret & 0xcccccccc) >> 2) | ((ret & 0x33333333) << 2); + ret = ((ret & 0xaaaaaaaa) >> 1) | ((ret & 0x55555555) << 1); + return ret; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n - a positive integer + * @return {number} - a positive integer + */ + reverseBits(n) { + let ret = n >>> 0; + ret = (ret >>> 16) | (ret << 16); + ret = ((ret & 0xff00ff00) >>> 8) | ((ret & 0x00ff00ff) << 8); + ret = ((ret & 0xf0f0f0f0) >>> 4) | ((ret & 0x0f0f0f0f) << 4); + ret = ((ret & 0xcccccccc) >>> 2) | ((ret & 0x33333333) << 2); + ret = ((ret & 0xaaaaaaaa) >>> 1) | ((ret & 0x55555555) << 1); + return ret >>> 0; + } +} +``` + +```csharp +public class Solution { + public uint ReverseBits(uint n) { + uint ret = n; + ret = (ret >> 16) | (ret << 16); + ret = ((ret & 0xff00ff00) >> 8) | ((ret & 0x00ff00ff) << 8); + ret = ((ret & 0xf0f0f0f0) >> 4) | ((ret & 0x0f0f0f0f) << 4); + ret = ((ret & 0xcccccccc) >> 2) | ((ret & 0x33333333) << 2); + ret = ((ret & 0xaaaaaaaa) >> 1) | ((ret & 0x55555555) << 1); + return ret; + } +} +``` + +```go +func reverseBits(n uint32) uint32 { + res := n + res = (res >> 16) | (res << 16) + res = ((res & 0xff00ff00) >> 8) | ((res & 0x00ff00ff) << 8) + res = ((res & 0xf0f0f0f0) >> 4) | ((res & 0x0f0f0f0f) << 4) + res = ((res & 0xcccccccc) >> 2) | ((res & 0x33333333) << 2) + res = ((res & 0xaaaaaaaa) >> 1) | ((res & 0x55555555) << 1) + return res +} +``` + +```kotlin +class Solution { + fun reverseBits(n: Int): Int { + var res = n + res = (res ushr 16) or (res shl 16) + res = ((res and 0xff00ff00.toInt()) ushr 8) or ((res and 0x00ff00ff) shl 8) + res = ((res and 0xf0f0f0f0.toInt()) ushr 4) or ((res and 0x0f0f0f0f) shl 4) + res = ((res and 0xcccccccc.toInt()) ushr 2) or ((res and 0x33333333) shl 2) + res = ((res and 0xaaaaaaaa.toInt()) ushr 1) or ((res and 0x55555555) shl 1) + return res + } +} +``` + +```swift +class Solution { + func reverseBits(_ n: Int) -> Int { + var res = n + res = (res >> 16) | (res << 16) & 0xFFFFFFFF + res = ((res & 0xff00ff00) >> 8) | ((res & 0x00ff00ff) << 8) + res = ((res & 0xf0f0f0f0) >> 4) | ((res & 0x0f0f0f0f) << 4) + res = ((res & 0xcccccccc) >> 2) | ((res & 0x33333333) << 2) + res = ((res & 0xaaaaaaaa) >> 1) | ((res & 0x55555555) << 1) + return res & 0xFFFFFFFF + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/reverse-integer.md b/articles/reverse-integer.md new file mode 100644 index 000000000..c388a821f --- /dev/null +++ b/articles/reverse-integer.md @@ -0,0 +1,565 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def reverse(self, x: int) -> int: + org = x + x = abs(x) + res = int(str(x)[::-1]) + if org < 0: + res *= -1 + if res < -(1 << 31) or res > (1 << 31) - 1: + return 0 + return res +``` + +```java +public class Solution { + public int reverse(int x) { + int org = x; + x = Math.abs(x); + long res = Long.parseLong(new StringBuilder( + String.valueOf(x)).reverse().toString() + ); + if (org < 0) { + res *= -1; + } + if (res < -(1 << 31) || res > (1 << 31) - 1) { + return 0; + } + return (int)res; + } +} +``` + +```cpp +class Solution { +public: + int reverse(int x) { + int org = x; + x = abs(x); + string strX = to_string(x); + std::reverse(strX.begin(), strX.end()); + long long res = stoll(strX); + if (org < 0) { + res *= -1; + } + if (res < -(1LL << 31) || res > (1LL << 31) - 1) { + return 0; + } + return static_cast(res); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @return {number} + */ + reverse(x) { + const org = x; + x = Math.abs(x); + let res = parseInt(x.toString().split('').reverse().join('')); + if (org < 0) { + res *= -1; + } + if (res < -(2 ** 31) || res > (2 ** 31) - 1) { + return 0; + } + return res; + } +} +``` + +```csharp +public class Solution { + public int Reverse(int x) { + int org = x; + x = Math.Abs(x); + char[] arr = x.ToString().ToCharArray(); + Array.Reverse(arr); + + long res = long.Parse(new string(arr)); + if (org < 0) { + res *= -1; + } + + if (res < int.MinValue || res > int.MaxValue) { + return 0; + } + return (int)res; + } +} +``` + +```go +func reverse(x int) int { + org := x + x = abs(x) + res := 0 + + for x > 0 { + res = res*10 + x%10 + x /= 10 + } + + if org < 0 { + res = -res + } + if res < -(1 << 31) || res > (1<<31)-1 { + return 0 + } + return res +} + +func abs(x int) int { + if x < 0 { + return -x + } + return x +} +``` + +```kotlin +class Solution { + fun reverse(x: Int): Int { + val org = x + var num = Math.abs(x) + var res = 0 + + while (num > 0) { + if (res > (Int.MAX_VALUE - num % 10) / 10) { + return 0 + } + res = res * 10 + num % 10 + num /= 10 + } + + if (org < 0) { + res = -res + } + return if (res < Int.MIN_VALUE || res > Int.MAX_VALUE) 0 else res + } +} +``` + +```swift +class Solution { + func reverse(_ x: Int) -> Int { + let org = x + var x = abs(x) + var res = Int(String(String(x).reversed()))! + + if org < 0 { + res *= -1 + } + if res < Int32.min || res > Int32.max { + return 0 + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 2. Recursion + +::tabs-start + +```python +class Solution: + def reverse(self, x: int) -> int: + def rec(n: int, rev: int) -> int: + if n == 0: + return rev + + rev = rev * 10 + n % 10 + return rec(n // 10, rev) + + sign = -1 if x < 0 else 1 + x = abs(x) + reversed_num = rec(x, 0) + reversed_num *= sign + if reversed_num < -(1 << 31) or reversed_num > (1 << 31) - 1: + return 0 + + return reversed_num +``` + +```java +public class Solution { + public int reverse(int x) { + long res = rec(Math.abs(x), 0) * (x < 0 ? -1 : 1); + if (res < Integer.MIN_VALUE || res > Integer.MAX_VALUE) { + return 0; + } + return (int) res; + } + + private long rec(int n, long rev) { + if (n == 0) { + return rev; + } + rev = rev * 10 + n % 10; + return rec(n / 10, rev); + } +} +``` + +```cpp +class Solution { +public: + int reverse(int x) { + long res = rec(abs(x), 0) * (x < 0 ? -1 : 1); + if (res < INT_MIN || res > INT_MAX) { + return 0; + } + return (int)res; + } + +private: + long rec(int n, long rev) { + if (n == 0) { + return rev; + } + rev = rev * 10 + n % 10; + return rec(n / 10, rev); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @return {number} + */ + reverse(x) { + let res = this.rec(Math.abs(x), 0) * (x < 0 ? -1 : 1); + if (res < -(2 ** 31) || res > (2 ** 31) - 1) { + return 0; + } + return res; + } + + /** + * @param {number} n + * @param {number} rev + * @return {number} + */ + rec(n, rev) { + if (n === 0) { + return rev; + } + rev = rev * 10 + n % 10; + return this.rec(Math.floor(n / 10), rev); + } +} +``` + +```csharp +public class Solution { + public int Reverse(int x) { + long res = Rec(Math.Abs(x), 0) * (x < 0 ? -1 : 1); + if (res < int.MinValue || res > int.MaxValue) { + return 0; + } + return (int)res; + } + + private long Rec(int n, long rev) { + if (n == 0) { + return rev; + } + rev = rev * 10 + n % 10; + return Rec(n / 10, rev); + } +} +``` + +```go +func reverse(x int) int { + var rec func(int, int) int + rec = func(n, rev int) int { + if n == 0 { + return rev + } + rev = rev*10 + n%10 + return rec(n/10, rev) + } + + sign := 1 + if x < 0 { + sign = -1 + x = -x + } + + reversedNum := rec(x, 0) * sign + if reversedNum < -(1<<31) || reversedNum > (1<<31)-1 { + return 0 + } + return reversedNum +} +``` + +```kotlin +class Solution { + fun reverse(x: Int): Int { + val res = rec(Math.abs(x), 0L) * if (x < 0) -1 else 1 + return if (res < Int.MIN_VALUE || res > Int.MAX_VALUE) 0 else res.toInt() + } + + private fun rec(n: Int, rev: Long): Long { + if (n == 0) return rev + return rec(n / 10, rev * 10 + n % 10) + } +} +``` + +```swift +class Solution { + func reverse(_ x: Int) -> Int { + func rec(_ n: Int, _ rev: Int) -> Int { + if n == 0 { + return rev + } + return rec(n / 10, rev * 10 + n % 10) + } + + let sign = x < 0 ? -1 : 1 + let reversedNum = rec(abs(x), 0) * sign + + if reversedNum < Int32.min || reversedNum > Int32.max { + return 0 + } + + return reversedNum + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 3. Iteration + +::tabs-start + +```python +class Solution: + def reverse(self, x: int) -> int: + MIN = -2147483648 # -2^31, + MAX = 2147483647 # 2^31 - 1 + + res = 0 + while x: + digit = int(math.fmod(x, 10)) + x = int(x / 10) + + if res > MAX // 10 or (res == MAX // 10 and digit > MAX % 10): + return 0 + if res < MIN // 10 or (res == MIN // 10 and digit < MIN % 10): + return 0 + res = (res * 10) + digit + + return res +``` + +```java +public class Solution { + public int reverse(int x) { + final int MIN = -2147483648; // -2^31 + final int MAX = 2147483647; // 2^31 - 1 + + int res = 0; + while (x != 0) { + int digit = x % 10; + x /= 10; + + if (res > MAX / 10 || (res == MAX / 10 && digit > MAX % 10)) + return 0; + if (res < MIN / 10 || (res == MIN / 10 && digit < MIN % 10)) + return 0; + res = (res * 10) + digit; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int reverse(int x) { + const int MIN = -2147483648; // -2^31 + const int MAX = 2147483647; // 2^31 - 1 + + int res = 0; + while (x != 0) { + int digit = x % 10; + x /= 10; + + if (res > MAX / 10 || (res == MAX / 10 && digit > MAX % 10)) + return 0; + if (res < MIN / 10 || (res == MIN / 10 && digit < MIN % 10)) + return 0; + res = (res * 10) + digit; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @return {number} + */ + reverse(x) { + const MIN = -2147483648; // -2^31 + const MAX = 2147483647; // 2^31 - 1 + + let res = 0; + while (x !== 0) { + const digit = x % 10; + x = Math.trunc(x / 10); + + if (res > MAX / 10 || (res === MAX / 10 && digit > MAX % 10)) + return 0; + if (res < MIN / 10 || (res === MIN / 10 && digit < MIN % 10)) + return 0; + res = res * 10 + digit; + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int Reverse(int x) { + const int MIN = -2147483648; // -2^31 + const int MAX = 2147483647; // 2^31 - 1 + + int res = 0; + while (x != 0) { + int digit = x % 10; + x /= 10; + + if (res > MAX / 10 || (res == MAX / 10 && digit > MAX % 10)) + return 0; + if (res < MIN / 10 || (res == MIN / 10 && digit < MIN % 10)) + return 0; + res = (res * 10) + digit; + } + + return res; + } +} +``` + +```go +func reverse(x int) int { + MIN := -2147483648 // -2^31 + MAX := 2147483647 // 2^31 - 1 + + res := 0 + for x != 0 { + digit := int(math.Mod(float64(x), 10)) + x = int(float64(x) / 10) + + if res > MAX/10 || (res == MAX/10 && digit > MAX%10) { + return 0 + } + if res < MIN/10 || (res == MIN/10 && digit < MIN%10) { + return 0 + } + res = (res * 10) + digit + } + + return res +} +``` + +```kotlin +class Solution { + fun reverse(x: Int): Int { + val MIN = -2147483648 // -2^31 + val MAX = 2147483647 // 2^31 - 1 + + var res = 0 + var num = x + while (num != 0) { + val digit = (num % 10).toInt() + num /= 10 + + if (res > MAX / 10 || (res == MAX / 10 && digit > MAX % 10)) { + return 0 + } + if (res < MIN / 10 || (res == MIN / 10 && digit < MIN % 10)) { + return 0 + } + res = res * 10 + digit + } + + return res + } +} +``` + +```swift +class Solution { + func reverse(_ x: Int) -> Int { + let MIN = Int32.min + let MAX = Int32.max + + var res = 0 + var num = x + + while num != 0 { + let digit = num % 10 + num /= 10 + + if res > MAX / 10 || (res == MAX / 10 && digit > MAX % 10) { + return 0 + } + if res < MIN / 10 || (res == MIN / 10 && digit < MIN % 10) { + return 0 + } + res = res * 10 + digit + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/reverse-linked-list-ii.md b/articles/reverse-linked-list-ii.md new file mode 100644 index 000000000..98f00e838 --- /dev/null +++ b/articles/reverse-linked-list-ii.md @@ -0,0 +1,884 @@ +## 1. Recursion - I + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def reverseBetween(self, head: Optional[ListNode], left: int, right: int) -> Optional[ListNode]: + dummy = ListNode(0) + dummy.next = head + prev = dummy + for _ in range(left - 1): + prev = prev.next + + sublist_head = prev.next + sublist_tail = sublist_head + for _ in range(right - left): + sublist_tail = sublist_tail.next + + next_node = sublist_tail.next + sublist_tail.next = None + reversed_sublist = self.reverseList(sublist_head) + prev.next = reversed_sublist + sublist_head.next = next_node + + return dummy.next + + def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]: + if not head: + return None + + newHead = head + if head.next: + newHead = self.reverseList(head.next) + head.next.next = head + head.next = None + + return newHead +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode reverseBetween(ListNode head, int left, int right) { + ListNode dummy = new ListNode(0); + dummy.next = head; + ListNode prev = dummy; + + for (int i = 0; i < left - 1; i++) { + prev = prev.next; + } + + ListNode sublistHead = prev.next; + ListNode sublistTail = sublistHead; + for (int i = 0; i < right - left; i++) { + sublistTail = sublistTail.next; + } + + ListNode nextNode = sublistTail.next; + sublistTail.next = null; + prev.next = reverseList(sublistHead); + sublistHead.next = nextNode; + + return dummy.next; + } + + private ListNode reverseList(ListNode head) { + if (head == null || head.next == null) { + return head; + } + + ListNode newHead = reverseList(head.next); + head.next.next = head; + head.next = null; + + return newHead; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* reverseBetween(ListNode* head, int left, int right) { + ListNode dummy(0); + dummy.next = head; + ListNode* prev = &dummy; + + for (int i = 0; i < left - 1; ++i) { + prev = prev->next; + } + + ListNode* sublistHead = prev->next; + ListNode* sublistTail = sublistHead; + for (int i = 0; i < right - left; ++i) { + sublistTail = sublistTail->next; + } + + ListNode* nextNode = sublistTail->next; + sublistTail->next = nullptr; + prev->next = reverseList(sublistHead); + sublistHead->next = nextNode; + + return dummy.next; + } + +private: + ListNode* reverseList(ListNode* head) { + if (!head || !head->next) { + return head; + } + + ListNode* newHead = reverseList(head->next); + head->next->next = head; + head->next = nullptr; + + return newHead; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} left + * @param {number} right + * @return {ListNode} + */ + reverseBetween(head, left, right) { + const reverseList = (head) => { + if (!head || !head.next) { + return head; + } + + const newHead = reverseList(head.next); + head.next.next = head; + head.next = null; + + return newHead; + }; + + const dummy = new ListNode(0, head); + let prev = dummy; + for (let i = 0; i < left - 1; i++) { + prev = prev.next; + } + + const sublistHead = prev.next; + let sublistTail = sublistHead; + for (let i = 0; i < right - left; i++) { + sublistTail = sublistTail.next; + } + + const nextNode = sublistTail.next; + sublistTail.next = null; + prev.next = reverseList(sublistHead); + sublistHead.next = nextNode; + + return dummy.next; + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ +public class Solution { + public ListNode ReverseBetween(ListNode head, int left, int right) { + ListNode dummy = new ListNode(0, head); + ListNode prev = dummy; + + for (int i = 0; i < left - 1; i++) { + prev = prev.next; + } + + ListNode sublistHead = prev.next; + ListNode sublistTail = sublistHead; + for (int i = 0; i < right - left; i++) { + sublistTail = sublistTail.next; + } + + ListNode nextNode = sublistTail.next; + sublistTail.next = null; + + ListNode reversedSublist = ReverseList(sublistHead); + prev.next = reversedSublist; + sublistHead.next = nextNode; + + return dummy.next; + } + + private ListNode ReverseList(ListNode head) { + if (head == null) return null; + + ListNode newHead = head; + if (head.next != null) { + newHead = ReverseList(head.next); + head.next.next = head; + } + head.next = null; + return newHead; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Recursion - II + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def reverseBetween(self, head: Optional[ListNode], left: int, right: int) -> Optional[ListNode]: + def reverseList(node, n): + if n == 1: + return node, node.next + new_head, next_node = reverseList(node.next, n - 1) + node.next.next = node + node.next = next_node + return new_head, next_node + + if left == 1: + new_head, _ = reverseList(head, right) + return new_head + + head.next = self.reverseBetween(head.next, left - 1, right - 1) + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + private ListNode[] reverseList(ListNode node, int n) { + if (n == 1) { + return new ListNode[] { node, node.next }; + } + ListNode[] result = reverseList(node.next, n - 1); + node.next.next = node; + node.next = result[1]; + return new ListNode[] { result[0], node.next }; + } + + public ListNode reverseBetween(ListNode head, int left, int right) { + if (left == 1) { + return reverseList(head, right)[0]; + } + head.next = reverseBetween(head.next, left - 1, right - 1); + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +private: + pair reverseList(ListNode* node, int n) { + if (n == 1) { + return {node, node->next}; + } + auto result = reverseList(node->next, n - 1); + node->next->next = node; + node->next = result.second; + return {result.first, node->next}; + } + +public: + ListNode* reverseBetween(ListNode* head, int left, int right) { + if (left == 1) { + return reverseList(head, right).first; + } + head->next = reverseBetween(head->next, left - 1, right - 1); + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} left + * @param {number} right + * @return {ListNode} + */ + reverseBetween(head, left, right) { + const reverseList = (node, n) => { + if (n === 1) { + return [node, node.next]; + } + const [newHead, nextNode] = reverseList(node.next, n - 1); + node.next.next = node; + node.next = nextNode; + return [newHead, nextNode]; + }; + + if (left === 1) { + return reverseList(head, right)[0]; + } + head.next = this.reverseBetween(head.next, left - 1, right - 1); + return head; + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ +public class Solution { + private ListNode successor = null; + + private ListNode ReverseList(ListNode node, int n) { + if (n == 1) { + successor = node.next; + return node; + } + ListNode newHead = ReverseList(node.next, n - 1); + node.next.next = node; + node.next = successor; + return newHead; + } + + public ListNode ReverseBetween(ListNode head, int left, int right) { + if (left == 1) { + return ReverseList(head, right); + } + head.next = ReverseBetween(head.next, left - 1, right - 1); + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Iteration - I + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def reverseBetween(self, head: Optional[ListNode], left: int, right: int) -> Optional[ListNode]: + dummy = ListNode(0) + dummy.next = head + prev = dummy + for _ in range(left - 1): + prev = prev.next + + sublist_head = prev.next + sublist_tail = sublist_head + for _ in range(right - left): + sublist_tail = sublist_tail.next + + next_node = sublist_tail.next + sublist_tail.next = None + reversed_sublist = self.reverseList(sublist_head) + prev.next = reversed_sublist + sublist_head.next = next_node + + return dummy.next + + def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]: + prev, curr = None, head + + while curr: + temp = curr.next + curr.next = prev + prev = curr + curr = temp + return prev +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode reverseBetween(ListNode head, int left, int right) { + ListNode dummy = new ListNode(0); + dummy.next = head; + ListNode prev = dummy; + + for (int i = 0; i < left - 1; i++) { + prev = prev.next; + } + + ListNode sublistHead = prev.next; + ListNode sublistTail = sublistHead; + for (int i = 0; i < right - left; i++) { + sublistTail = sublistTail.next; + } + + ListNode nextNode = sublistTail.next; + sublistTail.next = null; + prev.next = reverseList(sublistHead); + sublistHead.next = nextNode; + + return dummy.next; + } + + private ListNode reverseList(ListNode head) { + ListNode prev = null; + ListNode curr = head; + + while (curr != null) { + ListNode temp = curr.next; + curr.next = prev; + prev = curr; + curr = temp; + } + return prev; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* reverseBetween(ListNode* head, int left, int right) { + ListNode dummy(0); + dummy.next = head; + ListNode* prev = &dummy; + + for (int i = 0; i < left - 1; ++i) { + prev = prev->next; + } + + ListNode* sublistHead = prev->next; + ListNode* sublistTail = sublistHead; + for (int i = 0; i < right - left; ++i) { + sublistTail = sublistTail->next; + } + + ListNode* nextNode = sublistTail->next; + sublistTail->next = nullptr; + prev->next = reverseList(sublistHead); + sublistHead->next = nextNode; + + return dummy.next; + } + +private: + ListNode* reverseList(ListNode* head) { + ListNode* prev = nullptr; + ListNode* curr = head; + + while (curr) { + ListNode* temp = curr->next; + curr->next = prev; + prev = curr; + curr = temp; + } + return prev; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} left + * @param {number} right + * @return {ListNode} + */ + reverseBetween(head, left, right) { + const reverseList = (head) => { + let prev = null; + let curr = head; + + while (curr) { + let temp = curr.next; + curr.next = prev; + prev = curr; + curr = temp; + } + return prev; + }; + + const dummy = new ListNode(0, head); + let prev = dummy; + for (let i = 0; i < left - 1; i++) { + prev = prev.next; + } + + const sublistHead = prev.next; + let sublistTail = sublistHead; + for (let i = 0; i < right - left; i++) { + sublistTail = sublistTail.next; + } + + const nextNode = sublistTail.next; + sublistTail.next = null; + prev.next = reverseList(sublistHead); + sublistHead.next = nextNode; + + return dummy.next; + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ +public class Solution { + public ListNode ReverseBetween(ListNode head, int left, int right) { + ListNode dummy = new ListNode(0); + dummy.next = head; + ListNode prev = dummy; + + for (int i = 0; i < left - 1; i++) { + prev = prev.next; + } + + ListNode sublistHead = prev.next; + ListNode sublistTail = sublistHead; + for (int i = 0; i < right - left; i++) { + sublistTail = sublistTail.next; + } + + ListNode nextNode = sublistTail.next; + sublistTail.next = null; + + ListNode reversedSublist = ReverseList(sublistHead); + prev.next = reversedSublist; + + sublistHead.next = nextNode; + return dummy.next; + } + + private ListNode ReverseList(ListNode head) { + ListNode prev = null; + ListNode curr = head; + while (curr != null) { + ListNode temp = curr.next; + curr.next = prev; + prev = curr; + curr = temp; + } + return prev; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Iteration - II + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def reverseBetween(self, head: Optional[ListNode], left: int, right: int) -> Optional[ListNode]: + dummy = ListNode(0, head) + leftPrev, cur = dummy, head + + for _ in range(left - 1): + leftPrev, cur = cur, cur.next + + prev = None + for _ in range(right - left + 1): + tmpNext = cur.next + cur.next = prev + prev, cur = cur, tmpNext + + leftPrev.next.next = cur + leftPrev.next = prev + + return dummy.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode reverseBetween(ListNode head, int left, int right) { + ListNode dummy = new ListNode(0); + dummy.next = head; + ListNode leftPrev = dummy, cur = head; + + for (int i = 0; i < left - 1; i++) { + leftPrev = cur; + cur = cur.next; + } + + ListNode prev = null; + for (int i = 0; i < right - left + 1; i++) { + ListNode tmpNext = cur.next; + cur.next = prev; + prev = cur; + cur = tmpNext; + } + + leftPrev.next.next = cur; + leftPrev.next = prev; + + return dummy.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* reverseBetween(ListNode* head, int left, int right) { + ListNode dummy(0); + dummy.next = head; + ListNode* leftPrev = &dummy; + ListNode* cur = head; + + for (int i = 0; i < left - 1; ++i) { + leftPrev = cur; + cur = cur->next; + } + + ListNode* prev = nullptr; + for (int i = 0; i < right - left + 1; ++i) { + ListNode* tmpNext = cur->next; + cur->next = prev; + prev = cur; + cur = tmpNext; + } + + leftPrev->next->next = cur; + leftPrev->next = prev; + + return dummy.next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} left + * @param {number} right + * @return {ListNode} + */ + reverseBetween(head, left, right) { + const dummy = new ListNode(0, head); + let leftPrev = dummy, cur = head; + + for (let i = 0; i < left - 1; i++) { + leftPrev = cur; + cur = cur.next; + } + + let prev = null; + for (let i = 0; i < right - left + 1; i++) { + const tmpNext = cur.next; + cur.next = prev; + prev = cur; + cur = tmpNext; + } + + leftPrev.next.next = cur; + leftPrev.next = prev; + + return dummy.next; + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ +public class Solution { + public ListNode ReverseBetween(ListNode head, int left, int right) { + ListNode dummy = new ListNode(0, head); + ListNode leftPrev = dummy, curr = head; + + for (int i = 0; i < left - 1; i++) { + leftPrev = curr; + curr = curr.next; + } + + ListNode prev = null; + for (int i = 0; i < right - left + 1; i++) { + ListNode tmpNext = curr.next; + curr.next = prev; + prev = curr; + curr = tmpNext; + } + + leftPrev.next.next = curr; + leftPrev.next = prev; + + return dummy.next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/reverse-nodes-in-k-group.md b/articles/reverse-nodes-in-k-group.md new file mode 100644 index 000000000..f5836142d --- /dev/null +++ b/articles/reverse-nodes-in-k-group.md @@ -0,0 +1,703 @@ +## 1. Recursion + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + +class Solution: + def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]: + cur = head + group = 0 + while cur and group < k: + cur = cur.next + group += 1 + + if group == k: + cur = self.reverseKGroup(cur, k) + while group > 0: + tmp = head.next + head.next = cur + cur = head + head = tmp + group -= 1 + head = cur + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +public class Solution { + public ListNode reverseKGroup(ListNode head, int k) { + ListNode cur = head; + int group = 0; + while (cur != null && group < k) { + cur = cur.next; + group++; + } + + if (group == k) { + cur = reverseKGroup(cur, k); + while (group-- > 0) { + ListNode tmp = head.next; + head.next = cur; + cur = head; + head = tmp; + } + head = cur; + } + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +class Solution { +public: + ListNode* reverseKGroup(ListNode* head, int k) { + ListNode* cur = head; + int group = 0; + while (cur != nullptr && group < k) { + cur = cur->next; + group++; + } + + if (group == k) { + cur = reverseKGroup(cur, k); + while (group-- > 0) { + ListNode* tmp = head->next; + head->next = cur; + cur = head; + head = tmp; + } + head = cur; + } + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {ListNode} head + * @param {number} k + * @return {ListNode} + */ + reverseKGroup(head, k) { + let cur = head; + let group = 0; + while (cur && group < k) { + cur = cur.next; + group++; + } + + if (group === k) { + cur = this.reverseKGroup(cur, k); + while (group-- > 0) { + let tmp = head.next; + head.next = cur; + cur = head; + head = tmp; + } + head = cur; + } + return head; + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +public class Solution { + public ListNode ReverseKGroup(ListNode head, int k) { + ListNode cur = head; + int group = 0; + while (cur != null && group < k) { + cur = cur.next; + group++; + } + + if (group == k) { + cur = ReverseKGroup(cur, k); + while (group-- > 0) { + ListNode tmp = head.next; + head.next = cur; + cur = head; + head = tmp; + } + head = cur; + } + return head; + } +} +``` + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func reverseKGroup(head *ListNode, k int) *ListNode { + cur := head + group := 0 + + for cur != nil && group < k { + cur = cur.Next + group++ + } + + if group == k { + cur = reverseKGroup(cur, k) + for group > 0 { + tmp := head.Next + head.Next = cur + cur = head + head = tmp + group-- + } + head = cur + } + return head +} +``` + +```kotlin +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun reverseKGroup(head: ListNode?, k: Int): ListNode? { + var cur = head + var group = 0 + + while (cur != null && group < k) { + cur = cur.next + group++ + } + + return if (group == k) { + cur = reverseKGroup(cur, k) + var newHead: ListNode? = null + var tempHead = head + + while (group > 0) { + val tmp = tempHead!!.next + tempHead.next = cur + cur = tempHead + tempHead = tmp + group-- + } + cur + } else { + head + } + } +} +``` + +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func reverseKGroup(_ head: ListNode?, _ k: Int) -> ListNode? { + var cur = head + var group = 0 + + while cur != nil && group < k { + cur = cur!.next + group += 1 + } + + if group == k { + cur = reverseKGroup(cur, k) + + var tempHead = head + while group > 0 { + let tmp = tempHead!.next + tempHead!.next = cur + cur = tempHead + tempHead = tmp + group -= 1 + } + + return cur + } + + return head + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(\frac{n}{k})$ + +--- + +## 2. Iteration + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + +class Solution: + def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]: + dummy = ListNode(0, head) + groupPrev = dummy + + while True: + kth = self.getKth(groupPrev, k) + if not kth: + break + groupNext = kth.next + + prev, curr = kth.next, groupPrev.next + while curr != groupNext: + tmp = curr.next + curr.next = prev + prev = curr + curr = tmp + + tmp = groupPrev.next + groupPrev.next = kth + groupPrev = tmp + return dummy.next + + def getKth(self, curr, k): + while curr and k > 0: + curr = curr.next + k -= 1 + return curr +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +class Solution { + + public ListNode reverseKGroup(ListNode head, int k) { + ListNode dummy = new ListNode(0, head); + ListNode groupPrev = dummy; + + while (true) { + ListNode kth = getKth(groupPrev, k); + if (kth == null) { + break; + } + ListNode groupNext = kth.next; + + ListNode prev = kth.next; + ListNode curr = groupPrev.next; + while (curr != groupNext) { + ListNode tmp = curr.next; + curr.next = prev; + prev = curr; + curr = tmp; + } + + ListNode tmp = groupPrev.next; + groupPrev.next = kth; + groupPrev = tmp; + } + return dummy.next; + } + + private ListNode getKth(ListNode curr, int k) { + while (curr != null && k > 0) { + curr = curr.next; + k--; + } + return curr; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +class Solution { +public: + ListNode* reverseKGroup(ListNode* head, int k) { + ListNode* dummy = new ListNode(0, head); + ListNode* groupPrev = dummy; + + while (true) { + ListNode* kth = getKth(groupPrev, k); + if (!kth) { + break; + } + ListNode* groupNext = kth->next; + + ListNode* prev = kth->next; + ListNode* curr = groupPrev->next; + while (curr != groupNext) { + ListNode* tmp = curr->next; + curr->next = prev; + prev = curr; + curr = tmp; + } + + ListNode* tmp = groupPrev->next; + groupPrev->next = kth; + groupPrev = tmp; + } + return dummy->next; + } + +private: + ListNode* getKth(ListNode* curr, int k) { + while (curr && k > 0) { + curr = curr->next; + k--; + } + return curr; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +class Solution { + /** + * @param {ListNode} head + * @param {number} k + * @return {ListNode} + */ + reverseKGroup(head, k) { + const dummy = new ListNode(0, head); + let groupPrev = dummy; + + while (true) { + const kth = this.getKth(groupPrev, k); + if (!kth) { + break; + } + const groupNext = kth.next; + + let prev = kth.next; + let curr = groupPrev.next; + while (curr != groupNext) { + const tmp = curr.next; + curr.next = prev; + prev = curr; + curr = tmp; + } + + const tmp = groupPrev.next; + groupPrev.next = kth; + groupPrev = tmp; + } + return dummy.next; + } + + getKth(curr, k) { + while (curr && k > 0) { + curr = curr.next; + k--; + } + return curr; + } +} +``` + +```csharp +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +public class Solution { + public ListNode ReverseKGroup(ListNode head, int k) { + ListNode dummy = new ListNode(0, head); + ListNode groupPrev = dummy; + + while (true) { + ListNode kth = GetKth(groupPrev, k); + if (kth == null) { + break; + } + ListNode groupNext = kth.next; + + ListNode prev = kth.next; + ListNode curr = groupPrev.next; + while (curr != groupNext) { + ListNode tmp = curr.next; + curr.next = prev; + prev = curr; + curr = tmp; + } + + ListNode tmpNode = groupPrev.next; + groupPrev.next = kth; + groupPrev = tmpNode; + } + return dummy.next; + } + + private ListNode GetKth(ListNode curr, int k) { + while (curr != null && k > 0) { + curr = curr.next; + k--; + } + return curr; + } +} +``` + +```go +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func reverseKGroup(head *ListNode, k int) *ListNode { + dummy := &ListNode{Next: head} + groupPrev := dummy + + for { + kth := getKth(groupPrev, k) + if kth == nil { + break + } + groupNext := kth.Next + + prev, curr := groupNext, groupPrev.Next + for curr != groupNext { + tmp := curr.Next + curr.Next = prev + prev = curr + curr = tmp + } + + tmp := groupPrev.Next + groupPrev.Next = kth + groupPrev = tmp + } + return dummy.Next +} + +func getKth(curr *ListNode, k int) *ListNode { + for curr != nil && k > 0 { + curr = curr.Next + k-- + } + return curr +} +``` + +```kotlin +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun reverseKGroup(head: ListNode?, k: Int): ListNode? { + val dummy = ListNode(0) + dummy.next = head + var groupPrev: ListNode? = dummy + + while (true) { + val kth = getKth(groupPrev, k) + if (kth == null) { + break + } + val groupNext = kth.next + + var prev: ListNode? = groupNext + var curr = groupPrev!!.next + while (curr != groupNext) { + val tmp = curr!!.next + curr.next = prev + prev = curr + curr = tmp + } + + val tmp = groupPrev.next + groupPrev.next = kth + groupPrev = tmp + } + return dummy.next + } + + private fun getKth(curr: ListNode?, k: Int): ListNode? { + var curr = curr + var k = k + while (curr != null && k > 0) { + curr = curr.next + k-- + } + return curr + } +} +``` + +```swift +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func reverseKGroup(_ head: ListNode?, _ k: Int) -> ListNode? { + let dummy = ListNode(0, head) + var groupPrev: ListNode? = dummy + + while true { + guard let kth = getKth(groupPrev, k) else { + break + } + let groupNext = kth.next + + var prev: ListNode? = kth.next + var curr = groupPrev?.next + + while curr !== groupNext { + let tmp = curr?.next + curr?.next = prev + prev = curr + curr = tmp + } + + let tmp = groupPrev?.next + groupPrev?.next = kth + groupPrev = tmp + } + return dummy.next + } + + private func getKth(_ curr: ListNode?, _ k: Int) -> ListNode? { + var curr = curr + var k = k + while curr != nil && k > 0 { + curr = curr?.next + k -= 1 + } + return curr + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/reverse-string.md b/articles/reverse-string.md new file mode 100644 index 000000000..6c671dd5f --- /dev/null +++ b/articles/reverse-string.md @@ -0,0 +1,430 @@ +## 1. Array + +::tabs-start + +```python +class Solution: + def reverseString(self, s: List[str]) -> None: + """ + Do not return anything, modify s in-place instead. + """ + tmp = [] + for i in range(len(s) - 1, -1, -1): + tmp.append(s[i]) + for i in range(len(s)): + s[i] = tmp[i] +``` + +```java +public class Solution { + public void reverseString(char[] s) { + char[] tmp = new char[s.length]; + for (int i = s.length - 1, j = 0; i >= 0; i--, j++) { + tmp[j] = s[i]; + } + for (int i = 0; i < s.length; i++) { + s[i] = tmp[i]; + } + } +} +``` + +```cpp +class Solution { +public: + void reverseString(vector& s) { + vector tmp; + for (int i = s.size() - 1; i >= 0; i--) { + tmp.push_back(s[i]); + } + for (int i = 0; i < s.size(); i++) { + s[i] = tmp[i]; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[]} s + * @return {void} Do not return anything, modify s in-place instead. + */ + reverseString(s) { + const tmp = []; + for (let i = s.length - 1; i >= 0; i--) { + tmp.push(s[i]); + } + for (let i = 0; i < s.length; i++) { + s[i] = tmp[i]; + } + } +} +``` + +```csharp +public class Solution { + public void ReverseString(char[] s) { + char[] tmp = new char[s.Length]; + int n = s.Length; + + for (int i = 0; i < n; i++) { + tmp[i] = s[n - 1 - i]; + } + + for (int i = 0; i < n; i++) { + s[i] = tmp[i]; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Recursion + +::tabs-start + +```python +class Solution: + def reverseString(self, s: List[str]) -> None: + """ + Do not return anything, modify s in-place instead. + """ + def reverse(l, r): + if l < r: + reverse(l + 1, r - 1) + s[l], s[r] = s[r], s[l] + + reverse(0, len(s) - 1) +``` + +```java +public class Solution { + public void reverseString(char[] s) { + reverse(s, 0, s.length - 1); + } + + private void reverse(char[] s, int l, int r) { + if (l < r) { + reverse(s, l + 1, r - 1); + char temp = s[l]; + s[l] = s[r]; + s[r] = temp; + } + } +} +``` + +```cpp +class Solution { +public: + void reverseString(vector& s) { + reverse(s, 0, s.size() - 1); + } + +private: + void reverse(vector& s, int l, int r) { + if (l < r) { + reverse(s, l + 1, r - 1); + swap(s[l], s[r]); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[]} s + * @return {void} Do not return anything, modify s in-place instead. + */ + reverseString(s) { + const reverse = (l, r) => { + if (l < r) { + reverse(l + 1, r - 1); + [s[l], s[r]] = [s[r], s[l]]; + } + }; + reverse(0, s.length - 1); + } +} +``` + +```csharp +public class Solution { + public void ReverseString(char[] s) { + Reverse(s, 0, s.Length - 1); + } + + private void Reverse(char[] s, int left, int right) { + if (left < right) { + Reverse(s, left + 1, right - 1); + char temp = s[left]; + s[left] = s[right]; + s[right] = temp; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Stack + +::tabs-start + +```python +class Solution: + def reverseString(self, s: List[str]) -> None: + """ + Do not return anything, modify s in-place instead. + """ + stack = [] + for c in s: + stack.append(c) + i = 0 + while stack: + s[i] = stack.pop() + i += 1 +``` + +```java +public class Solution { + public void reverseString(char[] s) { + Stack stack = new Stack<>(); + for (char c : s) { + stack.push(c); + } + int i = 0; + while (!stack.isEmpty()) { + s[i++] = stack.pop(); + } + } +} +``` + +```cpp +class Solution { +public: + void reverseString(vector& s) { + stack stk; + for (char& c : s) { + stk.push(c); + } + int i = 0; + while (!stk.empty()) { + s[i++] = stk.top(); + stk.pop(); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[]} s + * @return {void} Do not return anything, modify s in-place instead. + */ + reverseString(s) { + const stack = []; + for (const c of s) { + stack.push(c); + } + let i = 0; + while (stack.length) { + s[i++] = stack.pop(); + } + } +} +``` + +```csharp +public class Solution { + public void ReverseString(char[] s) { + Stack stack = new Stack(); + + foreach (char c in s) { + stack.Push(c); + } + + for (int i = 0; i < s.Length; i++) { + s[i] = stack.Pop(); + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Built-In Function + +::tabs-start + +```python +class Solution: + def reverseString(self, s: List[str]) -> None: + """ + Do not return anything, modify s in-place instead. + """ + s.reverse() +``` + +```java +public class Solution { + public void reverseString(char[] s) { + List list = new ArrayList<>(); + for (char c : s) { + list.add(c); + } + Collections.reverse(list); + + for (int i = 0; i < s.length; i++) { + s[i] = list.get(i); + } + } +} +``` + +```cpp +class Solution { +public: + void reverseString(vector& s) { + reverse(s.begin(), s.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[]} s + * @return {void} Do not return anything, modify s in-place instead. + */ + reverseString(s) { + s.reverse(); + } +} +``` + +```csharp +public class Solution { + public void ReverseString(char[] s) { + Array.Reverse(s); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Two Pointers + +::tabs-start + +```python +class Solution: + def reverseString(self, s: List[str]) -> None: + """ + Do not return anything, modify s in-place instead. + """ + l, r = 0, len(s) - 1 + while l < r: + s[l], s[r] = s[r], s[l] + l += 1 + r -= 1 +``` + +```java +public class Solution { + public void reverseString(char[] s) { + int l = 0, r = s.length - 1; + while (l < r) { + char temp = s[l]; + s[l] = s[r]; + s[r] = temp; + l++; + r--; + } + } +} +``` + +```cpp +class Solution { +public: + void reverseString(vector& s) { + int l = 0, r = s.size() - 1; + while (l < r) { + swap(s[l++], s[r--]); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[]} s + * @return {void} Do not return anything, modify s in-place instead. + */ + reverseString(s) { + let l = 0, r = s.length - 1; + while (l < r) { + [s[l], s[r]] = [s[r], s[l]]; + l++; + r--; + } + } +} +``` + +```csharp +public class Solution { + public void ReverseString(char[] s) { + int l = 0, r = s.Length - 1; + while (l < r) { + char temp = s[l]; + s[l] = s[r]; + s[r] = temp; + l++; + r--; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/reverse-words-in-a-string-iii.md b/articles/reverse-words-in-a-string-iii.md new file mode 100644 index 000000000..b9fb3b3cb --- /dev/null +++ b/articles/reverse-words-in-a-string-iii.md @@ -0,0 +1,387 @@ +## 1. Convert To String Array + +::tabs-start + +```python +class Solution: + def reverseWords(self, s: str) -> str: + return ' '.join([w[::-1] for w in s.split(' ')]) +``` + +```java +public class Solution { + public String reverseWords(String s) { + String[] words = s.split(" "); + for (int i = 0; i < words.length; i++) { + words[i] = new StringBuilder(words[i]).reverse().toString(); + } + return String.join(" ", words); + } +} +``` + +```cpp +class Solution { +public: + string reverseWords(string s) { + stringstream ss(s); + string word, res; + bool first = true; + + while (ss >> word) { + reverse(word.begin(), word.end()); + if (first) { + res += word; + first = false; + } else { + res += " " + word; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + reverseWords(s) { + return s.split(' ').map(w => w.split('').reverse().join('')).join(' '); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. String Manipulation + +::tabs-start + +```python +class Solution: + def reverseWords(self, s: str) -> str: + tmp_str = "" + res = "" + + for r in range(len(s) + 1): + if r == len(s) or s[r] == ' ': + res += tmp_str + tmp_str = "" + if r != len(s): + res += " " + else: + tmp_str = s[r] + tmp_str + return res +``` + +```java +public class Solution { + public String reverseWords(String s) { + String tmpStr = ""; + StringBuilder res = new StringBuilder(); + + for (int r = 0; r <= s.length(); r++) { + if (r == s.length() || s.charAt(r) == ' ') { + res.append(tmpStr); + tmpStr = ""; + if (r != s.length()) { + res.append(" "); + } + } else { + tmpStr = s.charAt(r) + tmpStr; + } + } + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string reverseWords(string s) { + string tmpStr = ""; + string res = ""; + + for (int r = 0; r <= s.size(); r++) { + if (r == s.size() || s[r] == ' ') { + res += tmpStr; + tmpStr = ""; + if (r != s.size()) { + res += " "; + } + } else { + tmpStr = s[r] + tmpStr; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + reverseWords(s) { + let tmpStr = ""; + let res = ""; + + for (let r = 0; r <= s.length; r++) { + if (r === s.length || s[r] === ' ') { + res += tmpStr; + tmpStr = ""; + if (r !== s.length) { + res += " "; + } + } else { + tmpStr = s[r] + tmpStr; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Two Pointers - I + +::tabs-start + +```python +class Solution: + def reverseWords(self, s: str) -> str: + s = list(s) + l = 0 + for r in range(len(s)): + if s[r] == " " or r == len(s) - 1: + temp_l, temp_r = l, r - 1 if s[r] == " " else r + while temp_l < temp_r: + s[temp_l], s[temp_r] = s[temp_r], s[temp_l] + temp_l += 1 + temp_r -= 1 + l = r + 1 + return "".join(s) +``` + +```java +public class Solution { + public String reverseWords(String s) { + char[] chars = s.toCharArray(); + int l = 0; + for (int r = 0; r < chars.length; r++) { + if (chars[r] == ' ' || r == chars.length - 1) { + int tempL = l, tempR = (chars[r] == ' ') ? r - 1 : r; + while (tempL < tempR) { + char temp = chars[tempL]; + chars[tempL] = chars[tempR]; + chars[tempR] = temp; + tempL++; + tempR--; + } + l = r + 1; + } + } + return new String(chars); + } +} +``` + +```cpp +class Solution { +public: + string reverseWords(string s) { + int l = 0; + for (int r = 0; r < s.size(); r++) { + if (r == s.size() - 1 || s[r] == ' ') { + int tempL = l, tempR = s[r] == ' ' ? r - 1 : r; + while (tempL < tempR) { + swap(s[tempL], s[tempR]); + tempL++; + tempR--; + } + l = r + 1; + } + } + return s; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + reverseWords(s) { + let chars = s.split(''); + let l = 0; + for (let r = 0; r <= chars.length; r++) { + if (r === chars.length || chars[r] === ' ') { + let tempL = l, tempR = r - 1; + while (tempL < tempR) { + [chars[tempL], chars[tempR]] = [chars[tempR], chars[tempL]]; + tempL++; + tempR--; + } + l = r + 1; + } + } + return chars.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Two Pointers - II + +::tabs-start + +```python +class Solution: + def reverseWords(self, s: str) -> str: + def reverse(i, j): + while i < j: + s[i], s[j] = s[j], s[i] + i += 1 + j -= 1 + + s = list(s) + i = 0 + while i < len(s): + if s[i] != ' ': + j = i + while j < len(s) and s[j] != ' ': + j += 1 + reverse(i, j - 1) + i = j + 1 + return ''.join(s) +``` + +```java +public class Solution { + public String reverseWords(String s) { + char[] arr = s.toCharArray(); + int n = arr.length; + + for (int i = 0; i < n; i++) { + if (arr[i] != ' ') { + int j = i; + while (j < n && arr[j] != ' ') { + j++; + } + reverse(arr, i, j - 1); + i = j; + } + } + return new String(arr); + } + + private void reverse(char[] arr, int i, int j) { + while (i < j) { + char temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; + i++; + j--; + } + } +} +``` + +```cpp +class Solution { +public: + string reverseWords(string s) { + int n = s.size(); + for (int i = 0; i < n; i++) { + if (s[i] != ' ') { + int j = i; + while (j < n && s[j] != ' ') { + j++; + } + reverse(s, i, j - 1); + i = j; + } + } + return s; + } + +private: + void reverse(string& s, int i, int j) { + while (i < j) { + swap(s[i], s[j]); + i++; + j--; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + reverseWords(s) { + const arr = s.split(''); + const reverse = (i, j) => { + while (i < j) { + [arr[i], arr[j]] = [arr[j], arr[i]]; + i++; + j--; + } + }; + + for (let i = 0; i < arr.length; i++) { + if (arr[i] !== ' ') { + let j = i; + while (j < arr.length && arr[j] !== ' ') { + j++; + } + reverse(i, j - 1); + i = j; + } + } + return arr.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/robot-bounded-in-circle.md b/articles/robot-bounded-in-circle.md new file mode 100644 index 000000000..76ec62fc9 --- /dev/null +++ b/articles/robot-bounded-in-circle.md @@ -0,0 +1,107 @@ +## 1. Simulation + +::tabs-start + +```python +class Solution: + def isRobotBounded(self, instructions: str) -> bool: + dirX, dirY = 0, 1 + x, y = 0, 0 + + for d in instructions: + if d == "G": + x, y = x + dirX, y + dirY + elif d == "L": + dirX, dirY = -dirY, dirX + else: + dirX, dirY = dirY, -dirX + + return (x, y) == (0, 0) or (dirX, dirY) != (0, 1) +``` + +```java +public class Solution { + public boolean isRobotBounded(String instructions) { + int dirX = 0, dirY = 1; + int x = 0, y = 0; + + for (int i = 0; i < instructions.length(); i++) { + char d = instructions.charAt(i); + if (d == 'G') { + x += dirX; + y += dirY; + } else if (d == 'L') { + int temp = dirX; + dirX = -dirY; + dirY = temp; + } else { + int temp = dirX; + dirX = dirY; + dirY = -temp; + } + } + + return (x == 0 && y == 0) || (dirX != 0 || dirY != 1); + } +} +``` + +```cpp +class Solution { +public: + bool isRobotBounded(string instructions) { + int dirX = 0, dirY = 1; + int x = 0, y = 0; + + for (char d : instructions) { + if (d == 'G') { + x += dirX; + y += dirY; + } else if (d == 'L') { + int temp = dirX; + dirX = -dirY; + dirY = temp; + } else { + int temp = dirX; + dirX = dirY; + dirY = -temp; + } + } + + return (x == 0 && y == 0) || (dirX != 0 || dirY != 1); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} instructions + * @return {boolean} + */ + isRobotBounded(instructions) { + let dirX = 0, dirY = 1; + let x = 0, y = 0; + + for (const d of instructions) { + if (d === 'G') { + x += dirX; + y += dirY; + } else if (d === 'L') { + [dirX, dirY] = [-dirY, dirX]; + } else { + [dirX, dirY] = [dirY, -dirX]; + } + } + + return (x === 0 && y === 0) || (dirX !== 0 || dirY !== 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/roman-to-integer.md b/articles/roman-to-integer.md new file mode 100644 index 000000000..b21b20e18 --- /dev/null +++ b/articles/roman-to-integer.md @@ -0,0 +1,95 @@ +## 1. Hash Map + +::tabs-start + +```python +class Solution: + def romanToInt(self, s: str) -> int: + roman = { + "I": 1, "V": 5, "X": 10, + "L": 50, "C": 100, "D": 500, "M": 1000 + } + res = 0 + for i in range(len(s)): + if i + 1 < len(s) and roman[s[i]] < roman[s[i + 1]]: + res -= roman[s[i]] + else: + res += roman[s[i]] + return res +``` + +```java +public class Solution { + public int romanToInt(String s) { + Map roman = new HashMap<>(); + roman.put('I', 1); roman.put('V', 5); + roman.put('X', 10); roman.put('L', 50); + roman.put('C', 100); roman.put('D', 500); + roman.put('M', 1000); + + int res = 0; + for (int i = 0; i < s.length(); i++) { + if (i + 1 < s.length() && roman.get(s.charAt(i)) < roman.get(s.charAt(i + 1))) { + res -= roman.get(s.charAt(i)); + } else { + res += roman.get(s.charAt(i)); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int romanToInt(string s) { + unordered_map roman = { + {'I', 1}, {'V', 5}, {'X', 10}, + {'L', 50}, {'C', 100}, {'D', 500}, {'M', 1000} + }; + + int res = 0; + for (int i = 0; i < s.size(); i++) { + if (i + 1 < s.size() && roman[s[i]] < roman[s[i + 1]]) { + res -= roman[s[i]]; + } else { + res += roman[s[i]]; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + romanToInt(s) { + const roman = { + "I": 1, "V": 5, "X": 10, + "L": 50, "C": 100, "D": 500, "M": 1000 + }; + + let res = 0; + for (let i = 0; i < s.length; i++) { + if (i + 1 < s.length && roman[s[i]] < roman[s[i + 1]]) { + res -= roman[s[i]]; + } else { + res += roman[s[i]]; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ since we have $7$ characters in the hash map. \ No newline at end of file diff --git a/articles/rotate-array.md b/articles/rotate-array.md new file mode 100644 index 000000000..790679e20 --- /dev/null +++ b/articles/rotate-array.md @@ -0,0 +1,529 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def rotate(self, nums: List[int], k: int) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + n = len(nums) + k %= n + while k: + tmp = nums[n - 1] + for i in range(n - 1, 0, -1): + nums[i] = nums[i - 1] + nums[0] = tmp + k -= 1 +``` + +```java +public class Solution { + public void rotate(int[] nums, int k) { + int n = nums.length; + k %= n; + while (k > 0) { + int tmp = nums[n - 1]; + for (int i = n - 1; i > 0; i--) { + nums[i] = nums[i - 1]; + } + nums[0] = tmp; + k--; + } + } +} +``` + +```cpp +class Solution { +public: + void rotate(vector& nums, int k) { + int n = nums.size(); + k %= n; + while (k > 0) { + int tmp = nums[n - 1]; + for (int i = n - 1; i > 0; i--) { + nums[i] = nums[i - 1]; + } + nums[0] = tmp; + k--; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ + rotate(nums, k) { + const n = nums.length; + k %= n; + while (k > 0) { + const tmp = nums[n - 1]; + for (let i = n - 1; i > 0; i--) { + nums[i] = nums[i - 1]; + } + nums[0] = tmp; + k--; + } + } +} +``` + +```csharp +public class Solution { + public void Rotate(int[] nums, int k) { + int n = nums.Length; + k %= n; + + while (k > 0) { + int tmp = nums[n - 1]; + for (int i = n - 1; i > 0; i--) { + nums[i] = nums[i - 1]; + } + nums[0] = tmp; + k--; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Extra Space + +::tabs-start + +```python +class Solution: + def rotate(self, nums: List[int], k: int) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + n = len(nums) + tmp = [0] * n + for i in range(n): + tmp[(i + k) % n] = nums[i] + + nums[:] = tmp +``` + +```java +public class Solution { + public void rotate(int[] nums, int k) { + int n = nums.length; + int[] tmp = new int[n]; + for (int i = 0; i < n; i++) { + tmp[(i + k) % n] = nums[i]; + } + for (int i = 0; i < n; i++) { + nums[i] = tmp[i]; + } + } +} +``` + +```cpp +class Solution { +public: + void rotate(vector& nums, int k) { + int n = nums.size(); + vector tmp(n); + for (int i = 0; i < n; i++) { + tmp[(i + k) % n] = nums[i]; + } + for (int i = 0; i < n; i++) { + nums[i] = tmp[i]; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ + rotate(nums, k) { + const n = nums.length; + const tmp = new Array(n); + for (let i = 0; i < n; i++) { + tmp[(i + k) % n] = nums[i]; + } + for (let i = 0; i < n; i++) { + nums[i] = tmp[i]; + } + } +} +``` + +```csharp +public class Solution { + public void Rotate(int[] nums, int k) { + int n = nums.Length; + int[] tmp = new int[n]; + + for (int i = 0; i < n; i++) { + tmp[(i + k) % n] = nums[i]; + } + + for (int i = 0; i < n; i++) { + nums[i] = tmp[i]; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ extra space. + +--- + +## 3. Cyclic Traversal + +::tabs-start + +```python +class Solution: + def rotate(self, nums: List[int], k: int) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + n = len(nums) + k %= n + count = start = 0 + + while count < n: + current = start + prev = nums[start] + while True: + next_idx = (current + k) % n + nums[next_idx], prev = prev, nums[next_idx] + current = next_idx + count += 1 + + if start == current: + break + start += 1 +``` + +```java +public class Solution { + public void rotate(int[] nums, int k) { + int n = nums.length; + k %= n; + int count = 0; + + for (int start = 0; count < n; start++) { + int current = start; + int prev = nums[start]; + do { + int nextIdx = (current + k) % n; + int temp = nums[nextIdx]; + nums[nextIdx] = prev; + prev = temp; + current = nextIdx; + count++; + } while (start != current); + } + } +} +``` + +```cpp +class Solution { +public: + void rotate(vector& nums, int k) { + int n = nums.size(); + k %= n; + int count = 0; + + for (int start = 0; count < n; start++) { + int current = start; + int prev = nums[start]; + do { + int nextIdx = (current + k) % n; + int temp = nums[nextIdx]; + nums[nextIdx] = prev; + prev = temp; + current = nextIdx; + count++; + } while (start != current); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ + rotate(nums, k) { + const n = nums.length; + k %= n; + let count = 0; + + for (let start = 0; count < n; start++) { + let current = start; + let prev = nums[start]; + do { + const nextIdx = (current + k) % n; + const temp = nums[nextIdx]; + nums[nextIdx] = prev; + prev = temp; + current = nextIdx; + count++; + } while (start !== current); + } + } +} +``` + +```csharp +public class Solution { + public void Rotate(int[] nums, int k) { + int n = nums.Length; + k %= n; + int count = 0; + + for (int start = 0; count < n; start++) { + int current = start; + int prev = nums[start]; + + do { + int nextIdx = (current + k) % n; + int temp = nums[nextIdx]; + nums[nextIdx] = prev; + prev = temp; + current = nextIdx; + count++; + } while (start != current); + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Using Reverse + +::tabs-start + +```python +class Solution: + def rotate(self, nums: List[int], k: int) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + n = len(nums) + k %= n + + def reverse(l: int, r: int) -> None: + while l < r: + nums[l], nums[r] = nums[r], nums[l] + l, r = l + 1, r - 1 + + reverse(0, n - 1) + reverse(0, k - 1) + reverse(k, n - 1) +``` + +```java +public class Solution { + public void rotate(int[] nums, int k) { + int n = nums.length; + k %= n; + + reverse(nums, 0, n - 1); + reverse(nums, 0, k - 1); + reverse(nums, k, n - 1); + } + + private void reverse(int[] nums, int l, int r) { + while (l < r) { + int temp = nums[l]; + nums[l] = nums[r]; + nums[r] = temp; + l++; + r--; + } + } +} +``` + +```cpp +class Solution { +public: + void rotate(vector& nums, int k) { + int n = nums.size(); + k %= n; + + reverse(nums, 0, n - 1); + reverse(nums, 0, k - 1); + reverse(nums, k, n - 1); + } + +private: + void reverse(vector& nums, int l, int r) { + while (l < r) { + swap(nums[l], nums[r]); + l++; + r--; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ + rotate(nums, k) { + const n = nums.length; + k %= n; + + const reverse = (l, r) => { + while (l < r) { + [nums[l], nums[r]] = [nums[r], nums[l]]; + l++; + r--; + } + }; + + reverse(0, n - 1); + reverse(0, k - 1); + reverse(k, n - 1); + } +} +``` + +```csharp +public class Solution { + public void Rotate(int[] nums, int k) { + int n = nums.Length; + k %= n; + + Reverse(nums, 0, n - 1); + Reverse(nums, 0, k - 1); + Reverse(nums, k, n - 1); + } + + private void Reverse(int[] nums, int left, int right) { + while (left < right) { + int temp = nums[left]; + nums[left] = nums[right]; + nums[right] = temp; + left++; + right--; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 5. One Liner + +::tabs-start + +```python +class Solution: + def rotate(self, nums: List[int], k: int) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + nums[:] = nums[-k % len(nums):] + nums[:-k % len(nums)] +``` + +```java +public class Solution { + public void rotate(int[] nums, int k) { + int n = nums.length; + int[] rotated = Arrays.copyOfRange(nums, n - k % n, n); + System.arraycopy(nums, 0, nums, k % n, n - k % n); + System.arraycopy(rotated, 0, nums, 0, rotated.length); + } +} +``` + +```cpp + +class Solution { +public: + void rotate(vector& nums, int k) { + std::rotate(nums.begin(), nums.end() - (k % nums.size()), nums.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ + rotate(nums, k) { + nums.splice(0, 0, ...nums.splice(nums.length - (k % nums.length))); + } +} +``` + +```csharp +public class Solution { + public void Rotate(int[] nums, int k) { + int n = nums.Length; + k %= n; + + int[] rotated = new int[n]; + Array.Copy(nums, n - k, rotated, 0, k); + Array.Copy(nums, 0, rotated, k, n - k); + Array.Copy(rotated, nums, n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the language. \ No newline at end of file diff --git a/articles/rotate-list.md b/articles/rotate-list.md new file mode 100644 index 000000000..675821f82 --- /dev/null +++ b/articles/rotate-list.md @@ -0,0 +1,486 @@ +## 1. Convert To Array + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]: + if not head: + return None + + arr, cur = [], head + while cur: + arr.append(cur.val) + cur = cur.next + + n = len(arr) + k %= n + cur = head + for i in range(n - k, n): + cur.val = arr[i] + cur = cur.next + + for i in range(n - k): + cur.val = arr[i] + cur = cur.next + + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode rotateRight(ListNode head, int k) { + if (head == null) return null; + + ArrayList arr = new ArrayList<>(); + ListNode cur = head; + while (cur != null) { + arr.add(cur.val); + cur = cur.next; + } + + int n = arr.size(); + k %= n; + cur = head; + for (int i = n - k; i < n; i++) { + cur.val = arr.get(i); + cur = cur.next; + } + for (int i = 0; i < n - k; i++) { + cur.val = arr.get(i); + cur = cur.next; + } + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* rotateRight(ListNode* head, int k) { + if (!head) return nullptr; + + vector arr; + ListNode* cur = head; + while (cur) { + arr.push_back(cur->val); + cur = cur->next; + } + + int n = arr.size(); + k %= n; + cur = head; + for (int i = n - k; i < n; i++) { + cur->val = arr[i]; + cur = cur->next; + } + for (int i = 0; i < n - k; i++) { + cur->val = arr[i]; + cur = cur->next; + } + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} k + * @return {ListNode} + */ + rotateRight(head, k) { + if (!head) return null; + + let arr = []; + let cur = head; + while (cur) { + arr.push(cur.val); + cur = cur.next; + } + + let n = arr.length; + k %= n; + cur = head; + for (let i = n - k; i < n; i++) { + cur.val = arr[i]; + cur = cur.next; + } + for (let i = 0; i < n - k; i++) { + cur.val = arr[i]; + cur = cur.next; + } + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Iteration + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def rotateRight(self, head: ListNode, k: int) -> ListNode: + if not head: + return head + + length, tail = 1, head + while tail.next: + tail = tail.next + length += 1 + + k = k % length + if k == 0: + return head + + cur = head + for i in range(length - k - 1): + cur = cur.next + newHead = cur.next + cur.next = None + tail.next = head + + return newHead +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode rotateRight(ListNode head, int k) { + if (head == null) { + return head; + } + + int length = 1; + ListNode tail = head; + while (tail.next != null) { + tail = tail.next; + length++; + } + + k = k % length; + if (k == 0) { + return head; + } + + ListNode cur = head; + for (int i = 0; i < length - k - 1; i++) { + cur = cur.next; + } + ListNode newHead = cur.next; + cur.next = null; + tail.next = head; + + return newHead; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* rotateRight(ListNode* head, int k) { + if (!head) { + return head; + } + + int length = 1; + ListNode* tail = head; + while (tail->next) { + tail = tail->next; + length++; + } + + k = k % length; + if (k == 0) { + return head; + } + + ListNode* cur = head; + for (int i = 0; i < length - k - 1; i++) { + cur = cur->next; + } + ListNode* newHead = cur->next; + cur->next = nullptr; + tail->next = head; + + return newHead; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} k + * @return {ListNode} + */ + rotateRight(head, k) { + if (!head) { + return head; + } + + let length = 1, tail = head; + while (tail.next) { + tail = tail.next; + length++; + } + + k = k % length; + if (k === 0) { + return head; + } + + let cur = head; + for (let i = 0; i < length - k - 1; i++) { + cur = cur.next; + } + let newHead = cur.next; + cur.next = null; + tail.next = head; + + return newHead; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Iteration (Using One Pointer) + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def rotateRight(self, head: ListNode, k: int) -> ListNode: + if not head: + return head + + cur, n = head, 1 + while cur.next: + n += 1 + cur = cur.next + + cur.next = head + k %= n + for i in range(n - k): + cur = cur.next + + head = cur.next + cur.next = None + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode rotateRight(ListNode head, int k) { + if (head == null) { + return head; + } + + ListNode cur = head; + int n = 1; + while (cur.next != null) { + n++; + cur = cur.next; + } + + cur.next = head; + k %= n; + for (int i = 0; i < n - k; i++) { + cur = cur.next; + } + + head = cur.next; + cur.next = null; + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* rotateRight(ListNode* head, int k) { + if (!head) { + return head; + } + + ListNode* cur = head; + int n = 1; + while (cur->next) { + n++; + cur = cur->next; + } + + cur->next = head; + k %= n; + for (int i = 0; i < n - k; i++) { + cur = cur->next; + } + + head = cur->next; + cur->next = nullptr; + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} k + * @return {ListNode} + */ + rotateRight(head, k) { + if (!head) { + return head; + } + + let cur = head, n = 1; + while (cur.next) { + n++; + cur = cur.next; + } + + cur.next = head; + k %= n; + for (let i = 0; i < n - k; i++) { + cur = cur.next; + } + + head = cur.next; + cur.next = null; + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/rotate-matrix.md b/articles/rotate-matrix.md new file mode 100644 index 000000000..c7ea787f1 --- /dev/null +++ b/articles/rotate-matrix.md @@ -0,0 +1,599 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def rotate(self, matrix: List[List[int]]) -> None: + n = len(matrix) + rotated = [[0] * n for _ in range(n)] + + for i in range(n): + for j in range(n): + rotated[j][n - 1 - i] = matrix[i][j] + + for i in range(n): + for j in range(n): + matrix[i][j] = rotated[i][j] +``` + +```java +public class Solution { + public void rotate(int[][] matrix) { + int n = matrix.length; + int[][] rotated = new int[n][n]; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + rotated[j][n - 1 - i] = matrix[i][j]; + } + } + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + matrix[i][j] = rotated[i][j]; + } + } + } +} +``` + +```cpp +class Solution { +public: + void rotate(vector>& matrix) { + int n = matrix.size(); + vector> rotated(n, vector(n)); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + rotated[j][n - 1 - i] = matrix[i][j]; + } + } + + matrix = rotated; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {void} + */ + rotate(matrix) { + const n = matrix.length; + const rotated = Array.from({ length: n }, () => + Array(n).fill(0)); + + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + rotated[j][n - 1 - i] = matrix[i][j]; + } + } + + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + matrix[i][j] = rotated[i][j]; + } + } + } +} +``` + +```csharp +public class Solution { + public void Rotate(int[][] matrix) { + int n = matrix.Length; + int[][] rotated = new int[n][]; + for (int i = 0; i < n; i++) { + rotated[i] = new int[n]; + for (int j = 0; j < n; j++) { + rotated[i][j] = matrix[n - 1 - j][i]; + } + } + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + matrix[i][j] = rotated[i][j]; + } + } + } +} +``` + +```go +func rotate(matrix [][]int) { + n := len(matrix) + rotated := make([][]int, n) + for i := range rotated { + rotated[i] = make([]int, n) + } + + for i := 0; i < n; i++ { + for j := 0; j < n; j++ { + rotated[j][n-1-i] = matrix[i][j] + } + } + + for i := 0; i < n; i++ { + for j := 0; j < n; j++ { + matrix[i][j] = rotated[i][j] + } + } +} +``` + +```kotlin +class Solution { + fun rotate(matrix: Array) { + val n = matrix.size + val rotated = Array(n) { IntArray(n) } + + for (i in 0 until n) { + for (j in 0 until n) { + rotated[j][n - 1 - i] = matrix[i][j] + } + } + + for (i in 0 until n) { + for (j in 0 until n) { + matrix[i][j] = rotated[i][j] + } + } + } +} +``` + +```swift +class Solution { + func rotate(_ matrix: inout [[Int]]) { + let n = matrix.count + var rotated = Array(repeating: Array(repeating: 0, count: n), count: n) + + for i in 0.. None: + l, r = 0, len(matrix) - 1 + while l < r: + for i in range(r - l): + top, bottom = l, r + + # save the topleft + topLeft = matrix[top][l + i] + + # move bottom left into top left + matrix[top][l + i] = matrix[bottom - i][l] + + # move bottom right into bottom left + matrix[bottom - i][l] = matrix[bottom][r - i] + + # move top right into bottom right + matrix[bottom][r - i] = matrix[top + i][r] + + # move top left into top right + matrix[top + i][r] = topLeft + r -= 1 + l += 1 +``` + +```java +public class Solution { + public void rotate(int[][] matrix) { + int l = 0; + int r = matrix.length - 1; + + while ( l < r ) { + for(int i = 0; i < r - l; i++) { + int top = l; + int bottom = r; + //save the topleft + int topLeft = matrix[top][l + i]; + + //move bottom left into top left + matrix[top][l + i] = matrix[bottom - i][l]; + + // move bottom right into bottom left + matrix[bottom - i][l] = matrix[bottom][r - i]; + + // move top right into bottom right + matrix[bottom][r - i] = matrix[top + i][r]; + + // move top left into top right + matrix[top + i][r] = topLeft; + + } + r--; + l++; + } + } +} +``` + +```cpp +class Solution { +public: + void rotate(vector>& matrix) { + int l = 0; + int r = matrix.size() - 1; + + while ( l < r ) { + for(int i = 0; i < r - l; i++) { + int top = l; + int bottom = r; + + //save the topleft + int topLeft = matrix[top][l + i]; + + //move bottom left into top left + matrix[top][l + i] = matrix[bottom - i][l]; + + // move bottom right into bottom left + matrix[bottom - i][l] = matrix[bottom][r - i]; + + // move top right into bottom right + matrix[bottom][r - i] = matrix[top + i][r]; + + // move top left into top right + matrix[top + i][r] = topLeft; + + } + r--; + l++; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {void} + */ + rotate(matrix) { + let l = 0; + let r = matrix.length - 1; + + while (l < r) { + for (let i = 0; i < r - l; i++) { + const top = l; + const bottom = r; + + // save the topleft + const topLeft = matrix[top][l + i]; + + // move bottom left into top left + matrix[top][l + i] = matrix[bottom - i][l]; + + // move bottom right into bottom left + matrix[bottom - i][l] = matrix[bottom][r - i]; + + // move top right into bottom right + matrix[bottom][r - i] = matrix[top + i][r]; + + // move top left into top right + matrix[top + i][r] = topLeft; + } + r--; + l++; + } + } +} +``` + +```csharp +public class Solution { + public void Rotate(int[][] matrix) { + (int left, int right) = (0, matrix.Length -1); + + while(left < right) { + var limit = right - left; + for(var i = 0; i < limit; i++) { + (int top, int bottom) = (left, right); + + // save the top left position + var topLeft = matrix[top][left + i]; + + // put the bottom left value to the top left position + matrix[top][left + i] = matrix[bottom - i][left]; + + // put the bottom right value to the bottom left position + matrix[bottom - i][left] = matrix[bottom][right - i]; + + // put the top right value to the bottom right position + matrix[bottom][right - i] = matrix[top + i][right]; + + // put the saved top left value to the top right position + matrix[top + i][right] = topLeft; + + } + left++; + right--; + } + + return; + } +} +``` + +```go +func rotate(matrix [][]int) { + l, r := 0, len(matrix)-1 + for l < r { + for i := 0; i < r-l; i++ { + top, bottom := l, r + + // save the topleft + topLeft := matrix[top][l+i] + + // move bottom left into top left + matrix[top][l+i] = matrix[bottom-i][l] + + // move bottom right into bottom left + matrix[bottom-i][l] = matrix[bottom][r-i] + + // move top right into bottom right + matrix[bottom][r-i] = matrix[top+i][r] + + // move top left into top right + matrix[top+i][r] = topLeft + } + r-- + l++ + } +} +``` + +```kotlin +class Solution { + fun rotate(matrix: Array) { + var l = 0 + var r = matrix.size - 1 + while (l < r) { + for (i in 0 until r - l) { + val top = l + val bottom = r + + // save the topleft + val topLeft = matrix[top][l + i] + + // move bottom left into top left + matrix[top][l + i] = matrix[bottom - i][l] + + // move bottom right into bottom left + matrix[bottom - i][l] = matrix[bottom][r - i] + + // move top right into bottom right + matrix[bottom][r - i] = matrix[top + i][r] + + // move top left into top right + matrix[top + i][r] = topLeft + } + r-- + l++ + } + } +} +``` + +```swift +class Solution { + func rotate(_ matrix: inout [[Int]]) { + var l = 0 + var r = matrix.count - 1 + while l < r { + for i in 0.. None: + # Reverse the matrix vertically + matrix.reverse() + + # Transpose the matrix + for i in range(len(matrix)): + for j in range(i + 1, len(matrix)): + matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j] +``` + +```java +public class Solution { + public void rotate(int[][] matrix) { + // Reverse the matrix vertically + reverse(matrix); + + // Transpose the matrix + for (int i = 0; i < matrix.length; i++) { + for (int j = i; j < matrix[i].length; j++) { + int temp = matrix[i][j]; + matrix[i][j] = matrix[j][i]; + matrix[j][i] = temp; + } + } + } + + private void reverse(int[][] matrix) { + int n = matrix.length; + for (int i = 0; i < n / 2; i++) { + int[] temp = matrix[i]; + matrix[i] = matrix[n - 1 - i]; + matrix[n - 1 - i] = temp; + } + } +} +``` + +```cpp +class Solution { +public: + void rotate(vector>& matrix) { + // Reverse the matrix vertically + reverse(matrix.begin(), matrix.end()); + + // Transpose the matrix + for (int i = 0; i < matrix.size(); ++i) { + for (int j = i + 1; j < matrix[i].size(); ++j) + swap(matrix[i][j], matrix[j][i]); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {void} + */ + rotate(matrix) { + // Reverse the matrix vertically + matrix.reverse(); + + // Transpose the matrix + for (let i = 0; i < matrix.length; i++) { + for (let j = i; j < matrix[i].length; j++) { + [matrix[i][j], matrix[j][i]] = [matrix[j][i], matrix[i][j]]; + } + } + } +} +``` + +```csharp +public class Solution { + public void Rotate(int[][] matrix) { + // Reverse the matrix vertically + Array.Reverse(matrix); + + // Transpose the matrix + for (int i = 0; i < matrix.Length; i++) { + for (int j = i; j < matrix[i].Length; j++) { + (matrix[i][j], matrix[j][i]) = (matrix[j][i], matrix[i][j]); + } + } + } +} +``` + +```go +func rotate(matrix [][]int) { + // Reverse the matrix vertically + for i, j := 0, len(matrix)-1; i < j; i, j = i+1, j-1 { + matrix[i], matrix[j] = matrix[j], matrix[i] + } + + // Transpose the matrix + for i := 0; i < len(matrix); i++ { + for j := i + 1; j < len(matrix); j++ { + matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j] + } + } +} +``` + +```kotlin +class Solution { + fun rotate(matrix: Array) { + // Reverse the matrix vertically + matrix.reverse() + + // Transpose the matrix + for (i in matrix.indices) { + for (j in i + 1 until matrix.size) { + val temp = matrix[i][j] + matrix[i][j] = matrix[j][i] + matrix[j][i] = temp + } + } + } +} +``` + +```swift +class Solution { + func rotate(_ matrix: inout [[Int]]) { + // Reverse the matrix vertically + matrix.reverse() + + // Transpose the matrix + for i in 0.. int: + q = collections.deque() + fresh = 0 + time = 0 + + for r in range(len(grid)): + for c in range(len(grid[0])): + if grid[r][c] == 1: + fresh += 1 + if grid[r][c] == 2: + q.append((r, c)) + + directions = [[0, 1], [0, -1], [1, 0], [-1, 0]] + while fresh > 0 and q: + length = len(q) + for i in range(length): + r, c = q.popleft() + + for dr, dc in directions: + row, col = r + dr, c + dc + if (row in range(len(grid)) + and col in range(len(grid[0])) + and grid[row][col] == 1 + ): + grid[row][col] = 2 + q.append((row, col)) + fresh -= 1 + time += 1 + return time if fresh == 0 else -1 +``` + +```java +public class Solution { + public int orangesRotting(int[][] grid) { + Queue q = new ArrayDeque<>(); + int fresh = 0; + int time = 0; + + for (int r = 0; r < grid.length; r++) { + for (int c = 0; c < grid[0].length; c++) { + if (grid[r][c] == 1) { + fresh++; + } + if (grid[r][c] == 2) { + q.offer(new int[]{r, c}); + } + } + } + + int[][] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + while (fresh > 0 && !q.isEmpty()) { + int length = q.size(); + for (int i = 0; i < length; i++) { + int[] curr = q.poll(); + int r = curr[0]; + int c = curr[1]; + + for (int[] dir : directions) { + int row = r + dir[0]; + int col = c + dir[1]; + if (row >= 0 && row < grid.length && + col >= 0 && col < grid[0].length && + grid[row][col] == 1) { + grid[row][col] = 2; + q.offer(new int[]{row, col}); + fresh--; + } + } + } + time++; + } + return fresh == 0 ? time : -1; + } +} +``` + +```cpp +class Solution { +public: + int orangesRotting(vector>& grid) { + queue> q; + int fresh = 0; + int time = 0; + + for (int r = 0; r < grid.size(); r++) { + for (int c = 0; c < grid[0].size(); c++) { + if (grid[r][c] == 1) { + fresh++; + } + if (grid[r][c] == 2) { + q.push({r, c}); + } + } + } + + vector> directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + while (fresh > 0 && !q.empty()) { + int length = q.size(); + for (int i = 0; i < length; i++) { + auto curr = q.front(); + q.pop(); + int r = curr.first; + int c = curr.second; + + for (const auto& dir : directions) { + int row = r + dir.first; + int col = c + dir.second; + if (row >= 0 && row < grid.size() && + col >= 0 && col < grid[0].size() && + grid[row][col] == 1) { + grid[row][col] = 2; + q.push({row, col}); + fresh--; + } + } + } + time++; + } + return fresh == 0 ? time : -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + orangesRotting(grid) { + const q = []; + let fresh = 0; + let time = 0; + + for (let r = 0; r < grid.length; r++) { + for (let c = 0; c < grid[0].length; c++) { + if (grid[r][c] === 1) { + fresh++; + } + if (grid[r][c] === 2) { + q.push([r, c]); + } + } + } + + const directions = [ + [0, 1], + [0, -1], + [1, 0], + [-1, 0], + ]; + while (fresh > 0 && q.length > 0) { + const length = q.length; + for (let i = 0; i < length; i++) { + const [currR, currC] = q.shift(); + + for (const [dr, dc] of directions) { + const row = currR + dr; + const col = currC + dc; + if (row >= 0 && row < grid.length && + col >= 0 && col < grid[0].length && + grid[row][col] === 1) { + grid[row][col] = 2; + q.push([row, col]); + fresh--; + } + } + } + time++; + } + return fresh === 0 ? time : -1; + } +} +``` + +```csharp +public class Solution { + public int OrangesRotting(int[][] grid) { + Queue q = new Queue(); + int fresh = 0; + int time = 0; + + for (int r = 0; r < grid.Length; r++) { + for (int c = 0; c < grid[0].Length; c++) { + if (grid[r][c] == 1) { + fresh++; + } + if (grid[r][c] == 2) { + q.Enqueue(new int[] { r, c }); + } + } + } + + int[][] directions = { new int[] { 0, 1 }, new int[] { 0, -1 }, new int[] { 1, 0 }, new int[] { -1, 0 } }; + while (fresh > 0 && q.Count > 0) { + int length = q.Count; + for (int i = 0; i < length; i++) { + int[] curr = q.Dequeue(); + int r = curr[0]; + int c = curr[1]; + + foreach (int[] dir in directions) { + int row = r + dir[0]; + int col = c + dir[1]; + if (row >= 0 && row < grid.Length && + col >= 0 && col < grid[0].Length && + grid[row][col] == 1) { + grid[row][col] = 2; + q.Enqueue(new int[] { row, col }); + fresh--; + } + } + } + time++; + } + return fresh == 0 ? time : -1; + } +} +``` + +```go +type Pair struct { + row, col int +} + +func orangesRotting(grid [][]int) int { + rows, cols := len(grid), len(grid[0]) + queue := make([]Pair, 0) + fresh := 0 + time := 0 + + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + if grid[r][c] == 1 { + fresh++ + } + if grid[r][c] == 2 { + queue = append(queue, Pair{r, c}) + } + } + } + + directions := [][]int{{0, 1}, {0, -1}, {1, 0}, {-1, 0}} + + for fresh > 0 && len(queue) > 0 { + length := len(queue) + + for i := 0; i < length; i++ { + current := queue[0] + queue = queue[1:] + + for _, dir := range directions { + newRow := current.row + dir[0] + newCol := current.col + dir[1] + + if newRow >= 0 && newRow < rows && + newCol >= 0 && newCol < cols && + grid[newRow][newCol] == 1 { + grid[newRow][newCol] = 2 + queue = append(queue, Pair{newRow, newCol}) + fresh-- + } + } + } + time++ + } + + if fresh == 0 { + return time + } + return -1 +} +``` + +```kotlin +class Solution { + data class Pair(val row: Int, val col: Int) + + fun orangesRotting(grid: Array): Int { + val rows = grid.size + val cols = grid[0].size + val queue = ArrayDeque() + var fresh = 0 + var time = 0 + + for (r in 0 until rows) { + for (c in 0 until cols) { + if (grid[r][c] == 1) { + fresh++ + } + if (grid[r][c] == 2) { + queue.addLast(Pair(r, c)) + } + } + } + + val directions = arrayOf( + intArrayOf(0, 1), + intArrayOf(0, -1), + intArrayOf(1, 0), + intArrayOf(-1, 0) + ) + + while (fresh > 0 && queue.isNotEmpty()) { + val length = queue.size + + repeat(length) { + val current = queue.removeFirst() + + for (dir in directions) { + val newRow = current.row + dir[0] + val newCol = current.col + dir[1] + + if (newRow in 0 until rows && + newCol in 0 until cols && + grid[newRow][newCol] == 1) { + grid[newRow][newCol] = 2 + queue.addLast(Pair(newRow, newCol)) + fresh-- + } + } + } + time++ + } + + return if (fresh == 0) time else -1 + } +} +``` + +```swift +class Solution { + func orangesRotting(_ grid: [[Int]]) -> Int { + var grid = grid + var queue = Deque<(Int, Int)>() + var fresh = 0 + var time = 0 + + let ROWS = grid.count + let COLS = grid[0].count + + for r in 0.. 0 && !queue.isEmpty { + let length = queue.count + for _ in 0..= 0 && row < ROWS && col >= 0 && col < COLS && grid[row][col] == 1 { + grid[row][col] = 2 + queue.append((row, col)) + fresh -= 1 + } + } + } + time += 1 + } + + return fresh == 0 ? time : -1 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns in the $grid$. + +--- + +## 2. Breadth First Search (No Queue) + +::tabs-start + +```python +class Solution: + def orangesRotting(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + fresh = 0 + time = 0 + + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] == 1: + fresh += 1 + + directions = [[0, 1], [0, -1], [1, 0], [-1, 0]] + + while fresh > 0: + flag = False + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] == 2: + for dr, dc in directions: + row, col = r + dr, c + dc + if (row in range(ROWS) and + col in range(COLS) and + grid[row][col] == 1): + grid[row][col] = 3 + fresh -= 1 + flag = True + + if not flag: + return -1 + + for r in range(ROWS): + for c in range(COLS): + if grid[r][c] == 3: + grid[r][c] = 2 + + time += 1 + + return time +``` + +```java +public class Solution { + public int orangesRotting(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int fresh = 0, time = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 1) fresh++; + } + } + + int[][] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + while (fresh > 0) { + boolean flag = false; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 2) { + for (int[] d : directions) { + int row = r + d[0], col = c + d[1]; + if (row >= 0 && col >= 0 && + row < ROWS && col < COLS && + grid[row][col] == 1) { + grid[row][col] = 3; + fresh--; + flag = true; + } + } + } + } + } + + if (!flag) return -1; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 3) grid[r][c] = 2; + } + } + + time++; + } + + return time; + } +} +``` + +```cpp +class Solution { +public: + int orangesRotting(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + int fresh = 0, time = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 1) fresh++; + } + } + + vector> directions = {{0, 1}, {0, -1}, + {1, 0}, {-1, 0}}; + + while (fresh > 0) { + bool flag = false; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 2) { + for (auto& d : directions) { + int row = r + d[0], col = c + d[1]; + if (row >= 0 && col >= 0 && + row < ROWS && col < COLS && + grid[row][col] == 1) { + grid[row][col] = 3; + fresh--; + flag = true; + } + } + } + } + } + + if (!flag) return -1; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 3) grid[r][c] = 2; + } + } + + time++; + } + + return time; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + orangesRotting(grid) { + let ROWS = grid.length, COLS = grid[0].length; + let fresh = 0, time = 0; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] === 1) fresh++; + } + } + + let directions = [[0, 1], [0, -1], [1, 0], [-1, 0]]; + + while (fresh > 0) { + let flag = false; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] === 2) { + for (let [dr, dc] of directions) { + let row = r + dr, col = c + dc; + if (row >= 0 && col >= 0 && + row < ROWS && col < COLS && + grid[row][col] === 1) { + grid[row][col] = 3; + fresh--; + flag = true; + } + } + } + } + } + + if (!flag) return -1; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (grid[r][c] === 3) grid[r][c] = 2; + } + } + + time++; + } + + return time; + } +} +``` + +```csharp +public class Solution { + public int OrangesRotting(int[][] grid) { + int ROWS = grid.Length, COLS = grid[0].Length; + int fresh = 0, time = 0; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 1) fresh++; + } + } + + int[][] directions = new int[][] { + new int[] {0, 1}, new int[] {0, -1}, + new int[] {1, 0}, new int[] {-1, 0} + }; + + while (fresh > 0) { + bool flag = false; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 2) { + foreach (var d in directions) { + int row = r + d[0], col = c + d[1]; + if (row >= 0 && col >= 0 && + row < ROWS && col < COLS && + grid[row][col] == 1) { + grid[row][col] = 3; + fresh--; + flag = true; + } + } + } + } + } + + if (!flag) return -1; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (grid[r][c] == 3) grid[r][c] = 2; + } + } + + time++; + } + + return time; + } +} +``` + +```go +func orangesRotting(grid [][]int) int { + rows, cols := len(grid), len(grid[0]) + fresh := 0 + time := 0 + + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + if grid[r][c] == 1 { + fresh++ + } + } + } + + directions := [][]int{{0, 1}, {0, -1}, {1, 0}, {-1, 0}} + + for fresh > 0 { + flag := false + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + if grid[r][c] == 2 { + for _, d := range directions { + row, col := r+d[0], c+d[1] + if row >= 0 && row < rows && + col >= 0 && col < cols && + grid[row][col] == 1 { + grid[row][col] = 3 + fresh-- + flag = true + } + } + } + } + } + + if !flag { + return -1 + } + + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + if grid[r][c] == 3 { + grid[r][c] = 2 + } + } + } + time++ + } + + return time +} +``` + +```kotlin +class Solution { + fun orangesRotting(grid: Array): Int { + val rows = grid.size + val cols = grid[0].size + var fresh = 0 + var time = 0 + + for (r in 0 until rows) { + for (c in 0 until cols) { + if (grid[r][c] == 1) fresh++ + } + } + + val directions = arrayOf( + intArrayOf(0, 1), + intArrayOf(0, -1), + intArrayOf(1, 0), + intArrayOf(-1, 0) + ) + + while (fresh > 0) { + var flag = false + for (r in 0 until rows) { + for (c in 0 until cols) { + if (grid[r][c] == 2) { + for (d in directions) { + val row = r + d[0] + val col = c + d[1] + if (row in 0 until rows && + col in 0 until cols && + grid[row][col] == 1) { + grid[row][col] = 3 + fresh-- + flag = true + } + } + } + } + } + + if (!flag) return -1 + + for (r in 0 until rows) { + for (c in 0 until cols) { + if (grid[r][c] == 3) grid[r][c] = 2 + } + } + time++ + } + + return time + } +} +``` + +```swift +class Solution { + func orangesRotting(_ grid: [[Int]]) -> Int { + var grid = grid + let ROWS = grid.count + let COLS = grid[0].count + var fresh = 0 + var time = 0 + + for r in 0.. 0 { + var flag = false + for r in 0..= 0 && row < ROWS && col >= 0 && + col < COLS && grid[row][col] == 1) { + grid[row][col] = 3 + fresh -= 1 + flag = true + } + } + } + } + } + + if !flag { + return -1 + } + + for r in 0.. Where $m$ is the number of rows and $n$ is the number of columns in the $grid$. \ No newline at end of file diff --git a/articles/same-binary-tree.md b/articles/same-binary-tree.md new file mode 100644 index 000000000..b07d14e35 --- /dev/null +++ b/articles/same-binary-tree.md @@ -0,0 +1,864 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool: + if not p and not q: + return True + if p and q and p.val == q.val: + return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right) + else: + return False +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + public boolean isSameTree(TreeNode p, TreeNode q) { + if (p == null && q == null) { + return true; + } + if (p != null && q != null && p.val == q.val) { + return isSameTree(p.left, q.left) && isSameTree(p.right, q.right); + } else { + return false; + } + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + bool isSameTree(TreeNode* p, TreeNode* q) { + if (!p && !q) { + return true; + } + if (p && q && p->val == q->val) { + return isSameTree(p->left, q->left) && isSameTree(p->right, q->right); + } else { + return false; + } + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} p + * @param {TreeNode} q + * @return {boolean} + */ + isSameTree(p, q) { + if (!p && !q) { + return true; + } + if (p && q && p.val === q.val) { + return ( + this.isSameTree(p.left, q.left) && + this.isSameTree(p.right, q.right) + ); + } else { + return false; + } + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public bool IsSameTree(TreeNode p, TreeNode q) { + if (p == null && q == null) { + return true; + } + if (p != null && q != null && p.val == q.val) { + return IsSameTree(p.left, q.left) && IsSameTree(p.right, q.right); + } else { + return false; + } + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func isSameTree(p *TreeNode, q *TreeNode) bool { + if p == nil && q == nil { + return true + } + if p != nil && q != nil && p.Val == q.Val { + return isSameTree(p.Left, q.Left) && isSameTree(p.Right, q.Right) + } + return false +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun isSameTree(p: TreeNode?, q: TreeNode?): Boolean { + if (p == null && q == null) { + return true + } + if (p != null && q != null && p.`val` == q.`val`) { + return isSameTree(p.left, q.left) && isSameTree(p.right, q.right) + } + return false + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func isSameTree(_ p: TreeNode?, _ q: TreeNode?) -> Bool { + if p == nil && q == nil { + return true + } + if let p = p, let q = q, p.val == q.val { + return isSameTree(p.left, q.left) && isSameTree(p.right, q.right) + } else { + return false + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + * Best Case ([balanced tree](https://www.geeksforgeeks.org/balanced-binary-tree/)): $O(log(n))$ + * Worst Case ([degenerate tree](https://www.geeksforgeeks.org/introduction-to-degenerate-binary-tree/)): $O(n)$ + +> Where $n$ is the number of nodes in the tree and $h$ is the height of the tree. + +--- + +## 2. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool: + stack = [(p, q)] + + while stack: + node1, node2 = stack.pop() + + if not node1 and not node2: + continue + if not node1 or not node2 or node1.val != node2.val: + return False + + stack.append((node1.right, node2.right)) + stack.append((node1.left, node2.left)) + + return True +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public boolean isSameTree(TreeNode p, TreeNode q) { + Stack stack = new Stack<>(); + stack.push(new TreeNode[]{p, q}); + + while (!stack.isEmpty()) { + TreeNode[] nodes = stack.pop(); + TreeNode node1 = nodes[0], node2 = nodes[1]; + + if (node1 == null && node2 == null) continue; + if (node1 == null || node2 == null || node1.val != node2.val) { + return false; + } + stack.push(new TreeNode[]{node1.right, node2.right}); + stack.push(new TreeNode[]{node1.left, node2.left}); + } + + return true; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + bool isSameTree(TreeNode* p, TreeNode* q) { + stack> stk; + stk.push({p, q}); + + while (!stk.empty()) { + auto [node1, node2] = stk.top(); + stk.pop(); + + if (!node1 && !node2) continue; + if (!node1 || !node2 || node1->val != node2->val) return false; + + stk.push({node1->right, node2->right}); + stk.push({node1->left, node2->left}); + } + + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} p + * @param {TreeNode} q + * @return {boolean} + */ + isSameTree(p, q) { + const stack = [[p, q]]; + + while (stack.length) { + const [node1, node2] = stack.pop(); + + if (!node1 && !node2) continue; + if (!node1 || !node2 || node1.val !== node2.val) { + return false; + } + stack.push([node1.right, node2.right]); + stack.push([node1.left, node2.left]); + } + + return true; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public bool IsSameTree(TreeNode p, TreeNode q) { + var stack = new Stack<(TreeNode, TreeNode)>(); + stack.Push((p, q)); + + while (stack.Count > 0) { + var (node1, node2) = stack.Pop(); + + if (node1 == null && node2 == null) continue; + if (node1 == null || node2 == null || node1.val != node2.val) { + return false; + } + stack.Push((node1.right, node2.right)); + stack.Push((node1.left, node2.left)); + } + + return true; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func isSameTree(p *TreeNode, q *TreeNode) bool { + type Pair struct { + first, second *TreeNode + } + + stack := []Pair{{p, q}} + + for len(stack) > 0 { + lastIdx := len(stack) - 1 + node1, node2 := stack[lastIdx].first, stack[lastIdx].second + stack = stack[:lastIdx] + + if node1 == nil && node2 == nil { + continue + } + if node1 == nil || node2 == nil || node1.Val != node2.Val { + return false + } + + stack = append(stack, Pair{node1.Right, node2.Right}) + stack = append(stack, Pair{node1.Left, node2.Left}) + } + + return true +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun isSameTree(p: TreeNode?, q: TreeNode?): Boolean { + val stack = ArrayDeque>() + stack.addLast(Pair(p, q)) + + while (stack.isNotEmpty()) { + val (node1, node2) = stack.removeLast() + + if (node1 == null && node2 == null) continue + if (node1 == null || node2 == null || node1.`val` != node2.`val`) { + return false + } + stack.addLast(Pair(node1.right, node2.right)) + stack.addLast(Pair(node1.left, node2.left)) + } + + return true + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func isSameTree(_ p: TreeNode?, _ q: TreeNode?) -> Bool { + var stack: [(TreeNode?, TreeNode?)] = [(p, q)] + + while !stack.isEmpty { + let (node1, node2) = stack.removeLast() + + if node1 == nil && node2 == nil { + continue + } + if node1 == nil || node2 == nil || node1!.val != node2!.val { + return false + } + + stack.append((node1!.right, node2!.right)) + stack.append((node1!.left, node2!.left)) + } + + return true + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool: + q1 = deque([p]) + q2 = deque([q]) + + while q1 and q2: + for _ in range(len(q1)): + nodeP = q1.popleft() + nodeQ = q2.popleft() + + if nodeP is None and nodeQ is None: + continue + if nodeP is None or nodeQ is None or nodeP.val != nodeQ.val: + return False + + q1.append(nodeP.left) + q1.append(nodeP.right) + q2.append(nodeQ.left) + q2.append(nodeQ.right) + + return True +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public boolean isSameTree(TreeNode p, TreeNode q) { + Queue q1 = new LinkedList<>(); + Queue q2 = new LinkedList<>(); + q1.add(p); + q2.add(q); + + while (!q1.isEmpty() && !q2.isEmpty()) { + for (int i = q1.size(); i > 0; i--) { + TreeNode nodeP = q1.poll(); + TreeNode nodeQ = q2.poll(); + + if (nodeP == null && nodeQ == null) continue; + if (nodeP == null || nodeQ == null || nodeP.val != nodeQ.val) + return false; + + q1.add(nodeP.left); + q1.add(nodeP.right); + q2.add(nodeQ.left); + q2.add(nodeQ.right); + } + } + + return true; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + bool isSameTree(TreeNode* p, TreeNode* q) { + queue q1; + queue q2; + q1.push(p); + q2.push(q); + + while (!q1.empty() && !q2.empty()) { + for (int i = q1.size(); i > 0; i--) { + TreeNode* nodeP = q1.front(); q1.pop(); + TreeNode* nodeQ = q2.front(); q2.pop(); + + if (!nodeP && !nodeQ) continue; + if (!nodeP || !nodeQ || nodeP->val != nodeQ->val) + return false; + + q1.push(nodeP->left); + q1.push(nodeP->right); + q2.push(nodeQ->left); + q2.push(nodeQ->right); + } + } + + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} p + * @param {TreeNode} q + * @return {boolean} + */ + isSameTree(p, q) { + const q1 = new Queue(); + const q2 = new Queue(); + q1.push(p); + q2.push(q); + + while (!q1.isEmpty() && !q2.isEmpty()) { + for (let i = q1.size(); i > 0; i--) { + let nodeP = q1.pop(); + let nodeQ = q2.pop(); + + if (nodeP === null && nodeQ === null) continue; + if (nodeP === null || nodeQ === null || nodeP.val !== nodeQ.val) { + return false; + } + + q1.push(nodeP.left); + q1.push(nodeP.right); + q2.push(nodeQ.left); + q2.push(nodeQ.right); + } + } + + return true; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public bool IsSameTree(TreeNode p, TreeNode q) { + var q1 = new Queue(new[] { p }); + var q2 = new Queue(new[] { q }); + + while (q1.Count > 0 && q2.Count > 0) { + for (int i = q1.Count; i > 0; i--) { + var nodeP = q1.Dequeue(); + var nodeQ = q2.Dequeue(); + + if (nodeP == null && nodeQ == null) continue; + if (nodeP == null || nodeQ == null || nodeP.val != nodeQ.val) { + return false; + } + + q1.Enqueue(nodeP.left); + q1.Enqueue(nodeP.right); + q2.Enqueue(nodeQ.left); + q2.Enqueue(nodeQ.right); + } + } + + return true; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func isSameTree(p *TreeNode, q *TreeNode) bool { + queue1 := []*TreeNode{p} + queue2 := []*TreeNode{q} + + for len(queue1) > 0 && len(queue2) > 0 { + for i := len(queue1); i > 0; i-- { + nodeP := queue1[0] + nodeQ := queue2[0] + queue1 = queue1[1:] + queue2 = queue2[1:] + + if nodeP == nil && nodeQ == nil { + continue + } + if nodeP == nil || nodeQ == nil || nodeP.Val != nodeQ.Val { + return false + } + + queue1 = append(queue1, nodeP.Left, nodeP.Right) + queue2 = append(queue2, nodeQ.Left, nodeQ.Right) + } + } + + return len(queue1) == 0 && len(queue2) == 0 +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun isSameTree(p: TreeNode?, q: TreeNode?): Boolean { + val q1 = ArrayDeque() + val q2 = ArrayDeque() + + q1.add(p) + q2.add(q) + + while (q1.isNotEmpty() && q2.isNotEmpty()) { + for (i in q1.size downTo 1) { + val nodeP = q1.removeFirst() + val nodeQ = q2.removeFirst() + + if (nodeP == null && nodeQ == null) { + continue + } + if (nodeP == null || nodeQ == null || nodeP.`val` != nodeQ.`val`) { + return false + } + + q1.add(nodeP.left) + q1.add(nodeP.right) + q2.add(nodeQ.left) + q2.add(nodeQ.right) + } + } + + return true + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func isSameTree(_ p: TreeNode?, _ q: TreeNode?) -> Bool { + var q1 = Deque() + var q2 = Deque() + q1.append(p) + q2.append(q) + + while !q1.isEmpty && !q2.isEmpty { + let nodeP = q1.removeFirst() + let nodeQ = q2.removeFirst() + + if nodeP == nil && nodeQ == nil { + continue + } + if nodeP == nil || nodeQ == nil || nodeP!.val != nodeQ!.val { + return false + } + + q1.append(nodeP!.left) + q1.append(nodeP!.right) + q2.append(nodeQ!.left) + q2.append(nodeQ!.right) + } + + return true + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/score-after-flipping-matrix.md b/articles/score-after-flipping-matrix.md new file mode 100644 index 000000000..83c222266 --- /dev/null +++ b/articles/score-after-flipping-matrix.md @@ -0,0 +1,253 @@ +## 1. Greedy (Overwriting the Input) + +::tabs-start + +```python +class Solution: + def matrixScore(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + + for r in range(ROWS): + if grid[r][0] == 0: + for c in range(COLS): + grid[r][c] ^= 1 + + for c in range(COLS): + one_cnt = sum(grid[r][c] for r in range(ROWS)) + if one_cnt < ROWS - one_cnt: + for r in range(ROWS): + grid[r][c] ^= 1 + + res = 0 + for r in range(ROWS): + for c in range(COLS): + res += grid[r][c] << (COLS - c - 1) + + return res +``` + +```java +public class Solution { + public int matrixScore(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + + for (int r = 0; r < ROWS; r++) { + if (grid[r][0] == 0) { + for (int c = 0; c < COLS; c++) { + grid[r][c] ^= 1; + } + } + } + + for (int c = 0; c < COLS; c++) { + int oneCnt = 0; + for (int r = 0; r < ROWS; r++) { + oneCnt += grid[r][c]; + } + if (oneCnt < ROWS - oneCnt) { + for (int r = 0; r < ROWS; r++) { + grid[r][c] ^= 1; + } + } + } + + int res = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + res += grid[r][c] << (COLS - c - 1); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int matrixScore(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + + for (int r = 0; r < ROWS; r++) { + if (grid[r][0] == 0) { + for (int c = 0; c < COLS; c++) { + grid[r][c] ^= 1; + } + } + } + + for (int c = 0; c < COLS; c++) { + int oneCnt = 0; + for (int r = 0; r < ROWS; r++) { + oneCnt += grid[r][c]; + } + if (oneCnt < ROWS - oneCnt) { + for (int r = 0; r < ROWS; r++) { + grid[r][c] ^= 1; + } + } + } + + int res = 0; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + res += grid[r][c] << (COLS - c - 1); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + matrixScore(grid) { + let ROWS = grid.length, COLS = grid[0].length; + + for (let r = 0; r < ROWS; r++) { + if (grid[r][0] === 0) { + for (let c = 0; c < COLS; c++) { + grid[r][c] ^= 1; + } + } + } + + for (let c = 0; c < COLS; c++) { + let oneCnt = 0; + for (let r = 0; r < ROWS; r++) { + oneCnt += grid[r][c]; + } + if (oneCnt < ROWS - oneCnt) { + for (let r = 0; r < ROWS; r++) { + grid[r][c] ^= 1; + } + } + } + + let res = 0; + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + res += grid[r][c] << (COLS - c - 1); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 2. Greedy (Optimal) + +::tabs-start + +```python +class Solution: + def matrixScore(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + res = ROWS * (1 << (COLS - 1)) + + for c in range(1, COLS): + cnt = 0 + for r in range(ROWS): + if grid[r][c] != grid[r][0]: + cnt += 1 + + cnt = max(cnt, ROWS - cnt) + res += cnt * (1 << (COLS - c - 1)) + + return res +``` + +```java +public class Solution { + public int matrixScore(int[][] grid) { + int ROWS = grid.length, COLS = grid[0].length; + int res = ROWS * (1 << (COLS - 1)); + + for (int c = 1; c < COLS; c++) { + int cnt = 0; + for (int r = 0; r < ROWS; r++) { + if (grid[r][c] != grid[r][0]) { + cnt++; + } + } + cnt = Math.max(cnt, ROWS - cnt); + res += cnt * (1 << (COLS - c - 1)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int matrixScore(vector>& grid) { + int ROWS = grid.size(), COLS = grid[0].size(); + int res = ROWS * (1 << (COLS - 1)); + + for (int c = 1; c < COLS; c++) { + int cnt = 0; + for (int r = 0; r < ROWS; r++) { + if (grid[r][c] != grid[r][0]) { + cnt++; + } + } + cnt = max(cnt, ROWS - cnt); + res += cnt * (1 << (COLS - c - 1)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + matrixScore(grid) { + const ROWS = grid.length, COLS = grid[0].length; + let res = ROWS * (1 << (COLS - 1)); + + for (let c = 1; c < COLS; c++) { + let cnt = 0; + for (let r = 0; r < ROWS; r++) { + if (grid[r][c] !== grid[r][0]) { + cnt++; + } + } + cnt = Math.max(cnt, ROWS - cnt); + res += cnt * (1 << (COLS - c - 1)); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ is the number of rows and $n$ is the number of columns. \ No newline at end of file diff --git a/articles/score-of-a-string.md b/articles/score-of-a-string.md new file mode 100644 index 000000000..9a5bf9bdf --- /dev/null +++ b/articles/score-of-a-string.md @@ -0,0 +1,60 @@ +## 1. Iteration + +::tabs-start + +```python +class Solution: + def scoreOfString(self, s: str) -> int: + res = 0 + for i in range(len(s) - 1): + res += abs(ord(s[i]) - ord(s[i + 1])) + return res +``` + +```java +public class Solution { + public int scoreOfString(String s) { + int res = 0; + for (int i = 0; i < s.length() - 1; i++) { + res += Math.abs(s.charAt(i) - s.charAt(i + 1)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int scoreOfString(string s) { + int res = 0; + for (int i = 0; i < s.length() - 1; i++) { + res += abs(s[i] - s[i + 1]); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + scoreOfString(s) { + let res = 0; + for (let i = 0; i < s.length - 1; i++) { + res += Math.abs(s.charCodeAt(i) - s.charCodeAt(i + 1)); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/search-2d-matrix.md b/articles/search-2d-matrix.md new file mode 100644 index 000000000..45181e5df --- /dev/null +++ b/articles/search-2d-matrix.md @@ -0,0 +1,827 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def searchMatrix(self, matrix: List[List[int]], target: int) -> bool: + for r in range(len(matrix)): + for c in range(len(matrix[0])): + if matrix[r][c] == target: + return True + return False +``` + +```java +public class Solution { + public boolean searchMatrix(int[][] matrix, int target) { + for (int r = 0; r < matrix.length; r++) { + for (int c = 0; c < matrix[r].length; c++) { + if (matrix[r][c] == target) { + return true; + } + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool searchMatrix(vector>& matrix, int target) { + for (int r = 0; r < matrix.size(); r++) { + for (int c = 0; c < matrix[r].size(); c++) { + if (matrix[r][c] == target) { + return true; + } + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @param {number} target + * @return {boolean} + */ + searchMatrix(matrix, target) { + for (let r = 0; r < matrix.length; r++) { + for (let c = 0; c < matrix[r].length; c++) { + if (matrix[r][c] == target) { + return true; + } + } + } + return false; + } +} +``` + +```csharp +public class Solution { + public bool SearchMatrix(int[][] matrix, int target) { + for (int r = 0; r < matrix.Length; r++) { + for (int c = 0; c < matrix[r].Length; c++) { + if (matrix[r][c] == target) { + return true; + } + } + } + return false; + } +} +``` + +```go +func searchMatrix(matrix [][]int, target int) bool { + for _, row := range matrix { + for _, value := range row { + if value == target { + return true + } + } + } + return false +} +``` + +```kotlin +class Solution { + fun searchMatrix(matrix: Array, target: Int): Boolean { + for (row in matrix) { + for (value in row) { + if (value == target) { + return true + } + } + } + return false + } +} +``` + +```swift +class Solution { + func searchMatrix(_ matrix: [[Int]], _ target: Int) -> Bool { + for r in 0.. Where $m$ is the number of rows and $n$ is the number of columns of matrix. + +--- + +## 2. Staircase Search + +::tabs-start + +```python +class Solution: + def searchMatrix(self, matrix: List[List[int]], target: int) -> bool: + m, n = len(matrix), len(matrix[0]) + r, c = 0, n - 1 + + while r < m and c >= 0: + if matrix[r][c] > target: + c -= 1 + elif matrix[r][c] < target: + r += 1 + else: + return True + return False +``` + +```java +public class Solution { + public boolean searchMatrix(int[][] matrix, int target) { + int m = matrix.length, n = matrix[0].length; + int r = 0, c = n - 1; + + while (r < m && c >= 0) { + if (matrix[r][c] > target) { + c--; + } else if (matrix[r][c] < target) { + r++; + } else { + return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool searchMatrix(vector>& matrix, int target) { + int m = matrix.size(), n = matrix[0].size(); + int r = 0, c = n - 1; + + while (r < m && c >= 0) { + if (matrix[r][c] > target) { + c--; + } else if (matrix[r][c] < target) { + r++; + } else { + return true; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @param {number} target + * @return {boolean} + */ + searchMatrix(matrix, target) { + const m = matrix.length, n = matrix[0].length; + let r = 0, c = n - 1; + + while (r < m && c >= 0) { + if (matrix[r][c] > target) { + c--; + } else if (matrix[r][c] < target) { + r++; + } else { + return true; + } + } + return false; + } +} +``` + +```csharp +public class Solution { + public bool SearchMatrix(int[][] matrix, int target) { + int m = matrix.Length, n = matrix[0].Length; + int r = 0, c = n - 1; + + while (r < m && c >= 0) { + if (matrix[r][c] > target) { + c--; + } else if (matrix[r][c] < target) { + r++; + } else { + return true; + } + } + return false; + } +} +``` + +```go +func searchMatrix(matrix [][]int, target int) bool { + m, n := len(matrix), len(matrix[0]) + r, c := 0, n - 1 + + for r < m && c >= 0 { + if matrix[r][c] > target { + c-- + } else if matrix[r][c] < target { + r++ + } else { + return true + } + } + return false +} +``` + +```kotlin +class Solution { + fun searchMatrix(matrix: Array, target: Int): Boolean { + val m = matrix.size + val n = matrix[0].size + var r = 0 + var c = n - 1 + + while (r < m && c >= 0) { + if (matrix[r][c] > target) { + c-- + } else if (matrix[r][c] < target) { + r++ + } else { + return true + } + } + return false + } +} +``` + +```swift +class Solution { + func searchMatrix(_ matrix: [[Int]], _ target: Int) -> Bool { + let m = matrix.count + let n = matrix[0].count + var r = 0, c = n - 1 + + while r < m && c >= 0 { + if matrix[r][c] > target { + c -= 1 + } else if matrix[r][c] < target { + r += 1 + } else { + return true + } + } + return false + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n)$ +* Space complexity: $O(1)$ + +> Where $m$ is the number of rows and $n$ is the number of columns of matrix. + +--- + +## 3. Binary Search + +::tabs-start + +```python +class Solution: + def searchMatrix(self, matrix: List[List[int]], target: int) -> bool: + ROWS, COLS = len(matrix), len(matrix[0]) + + top, bot = 0, ROWS - 1 + while top <= bot: + row = (top + bot) // 2 + if target > matrix[row][-1]: + top = row + 1 + elif target < matrix[row][0]: + bot = row - 1 + else: + break + + if not (top <= bot): + return False + row = (top + bot) // 2 + l, r = 0, COLS - 1 + while l <= r: + m = (l + r) // 2 + if target > matrix[row][m]: + l = m + 1 + elif target < matrix[row][m]: + r = m - 1 + else: + return True + return False +``` + +```java +public class Solution { + public boolean searchMatrix(int[][] matrix, int target) { + int ROWS = matrix.length; + int COLS = matrix[0].length; + + int top = 0, bot = ROWS - 1; + while (top <= bot) { + int row = (top + bot) / 2; + if (target > matrix[row][COLS - 1]) { + top = row + 1; + } else if (target < matrix[row][0]) { + bot = row - 1; + } else { + break; + } + } + + if (!(top <= bot)) { + return false; + } + int row = (top + bot) / 2; + int l = 0, r = COLS - 1; + while (l <= r) { + int m = (l + r) / 2; + if (target > matrix[row][m]) { + l = m + 1; + } else if (target < matrix[row][m]) { + r = m - 1; + } else { + return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool searchMatrix(vector>& matrix, int target) { + int ROWS = matrix.size(); + int COLS = matrix[0].size(); + + int top = 0, bot = ROWS - 1; + while (top <= bot) { + int row = (top + bot) / 2; + if (target > matrix[row][COLS - 1]) { + top = row + 1; + } else if (target < matrix[row][0]) { + bot = row - 1; + } else { + break; + } + } + + if (!(top <= bot)) { + return false; + } + int row = (top + bot) / 2; + int l = 0, r = COLS - 1; + while (l <= r) { + int m = (l + r) / 2; + if (target > matrix[row][m]) { + l = m + 1; + } else if (target < matrix[row][m]) { + r = m - 1; + } else { + return true; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @param {number} target + * @return {boolean} + */ + searchMatrix(matrix, target) { + const ROWS = matrix.length; + const COLS = matrix[0].length; + + let top = 0; + let bot = ROWS - 1; + while (top <= bot) { + const row = Math.floor((top + bot) / 2); + if (target > matrix[row][COLS - 1]) { + top = row + 1; + } else if (target < matrix[row][0]) { + bot = row - 1; + } else { + break; + } + } + + if (!(top <= bot)) { + return false; + } + const row = Math.floor((top + bot) / 2); + let l = 0; + let r = COLS - 1; + while (l <= r) { + const m = Math.floor((l + r) / 2); + if (target > matrix[row][m]) { + l = m + 1; + } else if (target < matrix[row][m]) { + r = m - 1; + } else { + return true; + } + } + return false; + } +} +``` + +```csharp +public class Solution { + public bool SearchMatrix(int[][] matrix, int target) { + int ROWS = matrix.Length; + int COLS = matrix[0].Length; + + int top = 0, bot = ROWS - 1; + int row = 0; + while (top <= bot) { + row = (top + bot) / 2; + if (target > matrix[row][COLS - 1]) { + top = row + 1; + } + else if (target < matrix[row][0]) { + bot = row - 1; + } + else { + break; + } + } + + if (!(top <= bot)) { + return false; + } + + int l = 0, r = COLS - 1; + while (l <= r) { + int m = (l + r) / 2; + if (target > matrix[row][m]) { + l = m + 1; + } + else if (target < matrix[row][m]) { + r = m - 1; + } + else { + return true; + } + } + return false; + } +} +``` + +```go +func searchMatrix(matrix [][]int, target int) bool { + rows, cols := len(matrix), len(matrix[0]) + top, bot := 0, rows - 1 + + for top <= bot { + row := (top + bot) / 2 + if target > matrix[row][cols-1] { + top = row + 1 + } else if target < matrix[row][0] { + bot = row - 1 + } else { + break + } + } + + if !(top <= bot) { + return false + } + row := (top + bot) / 2 + l, r := 0, cols - 1 + for l <= r { + m := (l + r) / 2 + if target > matrix[row][m] { + l = m + 1 + } else if target < matrix[row][m] { + r = m - 1 + } else { + return true + } + } + return false +} +``` + +```kotlin +class Solution { + fun searchMatrix(matrix: Array, target: Int): Boolean { + val rows = matrix.size + val cols = matrix[0].size + var top = 0 + var bot = rows - 1 + + while (top <= bot) { + val row = (top + bot) / 2 + if (target > matrix[row][cols - 1]) { + top = row + 1 + } else if (target < matrix[row][0]) { + bot = row - 1 + } else { + break + } + } + + if (!(top <= bot)) return false + val row = (top + bot) / 2 + var l = 0 + var r = cols - 1 + while (l <= r) { + val m = (l + r) / 2 + if (target > matrix[row][m]) { + l = m + 1 + } else if (target < matrix[row][m]) { + r = m - 1 + } else { + return true + } + } + return false + } +} +``` + +```swift +class Solution { + func searchMatrix(_ matrix: [[Int]], _ target: Int) -> Bool { + let ROWS = matrix.count + let COLS = matrix[0].count + + var top = 0, bot = ROWS - 1 + while top <= bot { + let row = (top + bot) / 2 + if target > matrix[row][COLS - 1] { + top = row + 1 + } else if target < matrix[row][0] { + bot = row - 1 + } else { + break + } + } + + if !(top <= bot) { + return false + } + let row = (top + bot) / 2 + var l = 0, r = COLS - 1 + while l <= r { + let m = (l + r) / 2 + if target > matrix[row][m] { + l = m + 1 + } else if target < matrix[row][m] { + r = m - 1 + } else { + return true + } + } + return false + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log m + \log n)$ (which reduces to $O(\log(m * n))$) +* Space complexity: $O(1)$ + +> Where $m$ is the number of rows and $n$ is the number of columns of matrix. + +--- + +## 4. Binary Search (One Pass) + +::tabs-start + +```python +class Solution: + def searchMatrix(self, matrix: List[List[int]], target: int) -> bool: + ROWS, COLS = len(matrix), len(matrix[0]) + + l, r = 0, ROWS * COLS - 1 + while l <= r: + m = l + (r - l) // 2 + row, col = m // COLS, m % COLS + if target > matrix[row][col]: + l = m + 1 + elif target < matrix[row][col]: + r = m - 1 + else: + return True + return False +``` + +```java +public class Solution { + public boolean searchMatrix(int[][] matrix, int target) { + int ROWS = matrix.length, COLS = matrix[0].length; + + int l = 0, r = ROWS * COLS - 1; + while (l <= r) { + int m = l + (r - l) / 2; + int row = m / COLS, col = m % COLS; + if (target > matrix[row][col]) { + l = m + 1; + } else if (target < matrix[row][col]) { + r = m - 1; + } else { + return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool searchMatrix(vector>& matrix, int target) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + + int l = 0, r = ROWS * COLS - 1; + while (l <= r) { + int m = l + (r - l) / 2; + int row = m / COLS, col = m % COLS; + if (target > matrix[row][col]) { + l = m + 1; + } else if (target < matrix[row][col]) { + r = m - 1; + } else { + return true; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @param {number} target + * @return {boolean} + */ + searchMatrix(matrix, target) { + let ROWS = matrix.length, COLS = matrix[0].length; + + let l = 0, r = ROWS * COLS - 1; + while (l <= r) { + let m = l + Math.floor((r - l) / 2); + let row = Math.floor(m / COLS), col = m % COLS; + if (target > matrix[row][col]) { + l = m + 1; + } else if (target < matrix[row][col]) { + r = m - 1; + } else { + return true; + } + } + return false; + } +} +``` + +```csharp +public class Solution { + public bool SearchMatrix(int[][] matrix, int target) { + int ROWS = matrix.Length, COLS = matrix[0].Length; + + int l = 0, r = ROWS * COLS - 1; + while (l <= r) { + int m = l + (r - l) / 2; + int row = m / COLS, col = m % COLS; + if (target > matrix[row][col]) { + l = m + 1; + } else if (target < matrix[row][col]) { + r = m - 1; + } else { + return true; + } + } + return false; + } +} +``` + +```go +func searchMatrix(matrix [][]int, target int) bool { + rows, cols := len(matrix), len(matrix[0]) + l, r := 0, rows*cols-1 + + for l <= r { + m := l + (r-l)/2 + row, col := m / cols, m % cols + if matrix[row][col] == target { + return true + } else if matrix[row][col] < target { + l = m + 1 + } else { + r = m - 1 + } + } + return false +} +``` + +```kotlin +class Solution { + fun searchMatrix(matrix: Array, target: Int): Boolean { + val rows = matrix.size + val cols = matrix[0].size + var l = 0 + var r = rows * cols - 1 + + while (l <= r) { + val m = l + (r - l) / 2 + val row = m / cols + val col = m % cols + if (matrix[row][col] == target) { + return true + } else if (matrix[row][col] < target) { + l = m + 1 + } else { + r = m - 1 + } + } + return false + } +} +``` + +```swift +class Solution { + func searchMatrix(_ matrix: [[Int]], _ target: Int) -> Bool { + let ROWS = matrix.count + let COLS = matrix[0].count + + var l = 0, r = ROWS * COLS - 1 + while l <= r { + let m = l + (r - l) / 2 + let row = m / COLS + let col = m % COLS + + if target > matrix[row][col] { + l = m + 1 + } else if target < matrix[row][col] { + r = m - 1 + } else { + return true + } + } + return false + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log(m * n))$ +* Space complexity: $O(1)$ + +> Where $m$ is the number of rows and $n$ is the number of columns of matrix. \ No newline at end of file diff --git a/articles/search-for-word-ii.md b/articles/search-for-word-ii.md new file mode 100644 index 000000000..519307f04 --- /dev/null +++ b/articles/search-for-word-ii.md @@ -0,0 +1,1489 @@ +## 1. Backtracking + +::tabs-start + +```python +class Solution: + def findWords(self, board: List[List[str]], words: List[str]) -> List[str]: + ROWS, COLS = len(board), len(board[0]) + res = [] + + def backtrack(r, c, i): + if i == len(word): + return True + if (r < 0 or c < 0 or r >= ROWS or + c >= COLS or board[r][c] != word[i] + ): + return False + + board[r][c] = '*' + ret = (backtrack(r + 1, c, i + 1) or + backtrack(r - 1, c, i + 1) or + backtrack(r, c + 1, i + 1) or + backtrack(r, c - 1, i + 1)) + board[r][c] = word[i] + return ret + + for word in words: + flag = False + for r in range(ROWS): + if flag: + break + for c in range(COLS): + if board[r][c] != word[0]: + continue + if backtrack(r, c, 0): + res.append(word) + flag = True + break + return res +``` + +```java +public class Solution { + public List findWords(char[][] board, String[] words) { + int ROWS = board.length, COLS = board[0].length; + List res = new ArrayList<>(); + + for (String word : words) { + boolean flag = false; + for (int r = 0; r < ROWS && !flag; r++) { + for (int c = 0; c < COLS; c++) { + if (board[r][c] != word.charAt(0)) continue; + if (backtrack(board, r, c, word, 0)) { + res.add(word); + flag = true; + break; + } + } + } + } + return res; + } + + private boolean backtrack(char[][] board, int r, int c, String word, int i) { + if (i == word.length()) return true; + if (r < 0 || c < 0 || r >= board.length || + c >= board[0].length || board[r][c] != word.charAt(i)) + return false; + + board[r][c] = '*'; + boolean ret = backtrack(board, r + 1, c, word, i + 1) || + backtrack(board, r - 1, c, word, i + 1) || + backtrack(board, r, c + 1, word, i + 1) || + backtrack(board, r, c - 1, word, i + 1); + board[r][c] = word.charAt(i); + return ret; + } +} +``` + +```cpp +class Solution { +public: + vector findWords(vector>& board, vector& words) { + int ROWS = board.size(), COLS = board[0].size(); + vector res; + + for (string& word : words) { + bool flag = false; + for (int r = 0; r < ROWS && !flag; r++) { + for (int c = 0; c < COLS; c++) { + if (board[r][c] != word[0]) continue; + if (backtrack(board, r, c, word, 0)) { + res.push_back(word); + flag = true; + break; + } + } + } + } + return res; + } + +private: + bool backtrack(vector>& board, int r, int c, string& word, int i) { + if (i == word.length()) return true; + if (r < 0 || c < 0 || r >= board.size() || + c >= board[0].size() || board[r][c] != word[i]) + return false; + + board[r][c] = '*'; + bool ret = backtrack(board, r + 1, c, word, i + 1) || + backtrack(board, r - 1, c, word, i + 1) || + backtrack(board, r, c + 1, word, i + 1) || + backtrack(board, r, c - 1, word, i + 1); + board[r][c] = word[i]; + return ret; + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[][]} board + * @param {string[]} words + * @return {string[]} + */ + findWords(board, words) { + const ROWS = board.length, COLS = board[0].length; + const res = []; + + const backtrack = (r, c, word, i) => { + if (i === word.length) return true; + if (r < 0 || c < 0 || r >= ROWS || + c >= COLS || board[r][c] !== word[i]) return false; + + const temp = board[r][c]; + board[r][c] = '*'; + const ret = backtrack(r + 1, c, word, i + 1) || + backtrack(r - 1, c, word, i + 1) || + backtrack(r, c + 1, word, i + 1) || + backtrack(r, c - 1, word, i + 1); + board[r][c] = temp; + return ret; + }; + + for (const word of words) { + let flag = false; + for (let r = 0; r < ROWS; r++) { + if (flag) break; + for (let c = 0; c < COLS; c++) { + if (board[r][c] !== word[0]) continue; + if (backtrack(r, c, word, 0)) { + res.push(word); + flag = true; + break; + } + } + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public List FindWords(char[][] board, string[] words) { + int ROWS = board.Length, COLS = board[0].Length; + List res = new List(); + + foreach (string word in words) { + bool flag = false; + for (int r = 0; r < ROWS && !flag; r++) { + for (int c = 0; c < COLS; c++) { + if (board[r][c] != word[0]) continue; + if (Backtrack(board, r, c, word, 0)) { + res.Add(word); + flag = true; + break; + } + } + } + } + return res; + } + + private bool Backtrack(char[][] board, int r, int c, string word, int i) { + if (i == word.Length) return true; + if (r < 0 || c < 0 || r >= board.Length || + c >= board[0].Length || board[r][c] != word[i]) + return false; + + board[r][c] = '*'; + bool ret = Backtrack(board, r + 1, c, word, i + 1) || + Backtrack(board, r - 1, c, word, i + 1) || + Backtrack(board, r, c + 1, word, i + 1) || + Backtrack(board, r, c - 1, word, i + 1); + board[r][c] = word[i]; + return ret; + } +} +``` + +```go +func findWords(board [][]byte, words []string) []string { + var res []string + rows, cols := len(board), len(board[0]) + + var backtrack func(r, c int, i int, word string) bool + backtrack = func(r, c int, i int, word string) bool { + if i == len(word) { + return true + } + if r < 0 || c < 0 || r >= rows || c >= cols || board[r][c] != word[i] { + return false + } + board[r][c] = '*' + defer func() { board[r][c] = word[i] }() + return backtrack(r+1, c, i+1, word) || + backtrack(r-1, c, i+1, word) || + backtrack(r, c+1, i+1, word) || + backtrack(r, c-1, i+1, word) + } + + for _, word := range words { + found := false + for r := 0; r < rows; r++ { + if found { + break + } + for c := 0; c < cols; c++ { + if board[r][c] == word[0] && backtrack(r, c, 0, word) { + res = append(res, word) + found = true + break + } + } + } + } + return res +} +``` + +```kotlin +class Solution { + fun findWords(board: Array, words: Array): List { + val res = mutableListOf() + val rows = board.size + val cols = board[0].size + + fun backtrack(r: Int, c: Int, i: Int, word: String): Boolean { + if (i == word.length) return true + if (r < 0 || c < 0 || r >= rows || c >= cols || board[r][c] != word[i]) { + return false + } + board[r][c] = '*' + val ret = backtrack(r + 1, c, i + 1, word) || + backtrack(r - 1, c, i + 1, word) || + backtrack(r, c + 1, i + 1, word) || + backtrack(r, c - 1, i + 1, word) + board[r][c] = word[i] + return ret + } + + for (word in words) { + var found = false + for (r in 0 until rows) { + if (found) break + for (c in 0 until cols) { + if (board[r][c] == word[0] && backtrack(r, c, 0, word)) { + res.add(word) + found = true + break + } + } + } + } + return res + } +} +``` + +```swift +class Solution { + func findWords(_ board: [[Character]], _ words: [String]) -> [String] { + let ROWS = board.count + let COLS = board[0].count + var res: [String] = [] + var board = board + + func backtrack(_ r: Int, _ c: Int, _ i: Int, _ word: [Character]) -> Bool { + if i == word.count { + return true + } + if r < 0 || c < 0 || r >= ROWS || c >= COLS || board[r][c] != word[i] { + return false + } + + board[r][c] = "*" + let ret = backtrack(r + 1, c, i + 1, word) || + backtrack(r - 1, c, i + 1, word) || + backtrack(r, c + 1, i + 1, word) || + backtrack(r, c - 1, i + 1, word) + board[r][c] = word[i] + return ret + } + + for word in words { + var flag = false + let wordArray = Array(word) + for r in 0.. Where $m$ is the number of rows, $n$ is the number of columns, $t$ is the maximum length of any word in the array $words$ and $s$ is the sum of the lengths of all the words. + +--- + +## 2. Backtracking (Trie + Hash Set) + +::tabs-start + +```python +class TrieNode: + def __init__(self): + self.children = {} + self.isWord = False + + def addWord(self, word): + cur = self + for c in word: + if c not in cur.children: + cur.children[c] = TrieNode() + cur = cur.children[c] + cur.isWord = True + +class Solution: + def findWords(self, board: List[List[str]], words: List[str]) -> List[str]: + root = TrieNode() + for w in words: + root.addWord(w) + + ROWS, COLS = len(board), len(board[0]) + res, visit = set(), set() + + def dfs(r, c, node, word): + if (r < 0 or c < 0 or r >= ROWS or + c >= COLS or (r, c) in visit or + board[r][c] not in node.children + ): + return + + visit.add((r, c)) + node = node.children[board[r][c]] + word += board[r][c] + if node.isWord: + res.add(word) + + dfs(r + 1, c, node, word) + dfs(r - 1, c, node, word) + dfs(r, c + 1, node, word) + dfs(r, c - 1, node, word) + visit.remove((r, c)) + + for r in range(ROWS): + for c in range(COLS): + dfs(r, c, root, "") + + return list(res) +``` + +```java +class TrieNode { + Map children; + boolean isWord; + + public TrieNode() { + children = new HashMap<>(); + isWord = false; + } + + public void addWord(String word) { + TrieNode cur = this; + for (char c : word.toCharArray()) { + cur.children.putIfAbsent(c, new TrieNode()); + cur = cur.children.get(c); + } + cur.isWord = true; + } +} + +public class Solution { + private Set res; + private boolean[][] visit; + + public List findWords(char[][] board, String[] words) { + TrieNode root = new TrieNode(); + for (String word : words) { + root.addWord(word); + } + + int ROWS = board.length, COLS = board[0].length; + res = new HashSet<>(); + visit = new boolean[ROWS][COLS]; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + dfs(board, r, c, root, ""); + } + } + return new ArrayList<>(res); + } + + private void dfs(char[][] board, int r, int c, TrieNode node, String word) { + int ROWS = board.length, COLS = board[0].length; + if (r < 0 || c < 0 || r >= ROWS || + c >= COLS || visit[r][c] || + !node.children.containsKey(board[r][c])) { + return; + } + + visit[r][c] = true; + node = node.children.get(board[r][c]); + word += board[r][c]; + if (node.isWord) { + res.add(word); + } + + dfs(board, r + 1, c, node, word); + dfs(board, r - 1, c, node, word); + dfs(board, r, c + 1, node, word); + dfs(board, r, c - 1, node, word); + + visit[r][c] = false; + } +} +``` + +```cpp +class TrieNode { +public: + unordered_map children; + bool isWord; + + TrieNode() : isWord(false) {} + + void addWord(const string& word) { + TrieNode* cur = this; + for (char c : word) { + if (!cur->children.count(c)) { + cur->children[c] = new TrieNode(); + } + cur = cur->children[c]; + } + cur->isWord = true; + } +}; + +class Solution { + unordered_set res; + vector> visit; +public: + vector findWords(vector>& board, vector& words) { + TrieNode* root = new TrieNode(); + for (const string& word : words) { + root->addWord(word); + } + + int ROWS = board.size(), COLS = board[0].size(); + visit.assign(ROWS, vector(COLS, false)); + + for (int r = 0; r < ROWS; ++r) { + for (int c = 0; c < COLS; ++c) { + dfs(board, r, c, root, ""); + } + } + return vector(res.begin(), res.end()); + } + +private: + void dfs(vector>& board, int r, int c, TrieNode* node, string word) { + int ROWS = board.size(), COLS = board[0].size(); + if (r < 0 || c < 0 || r >= ROWS || + c >= COLS || visit[r][c] || + !node->children.count(board[r][c])) { + return; + } + + visit[r][c] = true; + node = node->children[board[r][c]]; + word += board[r][c]; + if (node->isWord) { + res.insert(word); + } + + dfs(board, r + 1, c, node, word); + dfs(board, r - 1, c, node, word); + dfs(board, r, c + 1, node, word); + dfs(board, r, c - 1, node, word); + + visit[r][c] = false; + } +}; +``` + +```javascript +class TrieNode { + constructor() { + this.children = {}; + this.isWord = false; + } + + /** + * @param {string} word + * @return {void} + */ + addWord(word) { + let cur = this; + for (const c of word) { + if (!(c in cur.children)) { + cur.children[c] = new TrieNode(); + } + cur = cur.children[c]; + } + cur.isWord = true; + } +} + +class Solution { + /** + * @param {character[][]} board + * @param {string[]} words + * @return {string[]} + */ + findWords(board, words) { + const root = new TrieNode(); + for (const word of words) { + root.addWord(word); + } + + const ROWS = board.length, COLS = board[0].length; + const res = new Set(), visit = new Set(); + + const dfs = (r, c, node, word) => { + if (r < 0 || c < 0 || r >= ROWS || + c >= COLS || visit.has(`${r},${c}`) || + !(board[r][c] in node.children)) { + return; + } + + visit.add(`${r},${c}`); + node = node.children[board[r][c]]; + word += board[r][c]; + if (node.isWord) { + res.add(word); + } + + dfs(r + 1, c, node, word); + dfs(r - 1, c, node, word); + dfs(r, c + 1, node, word); + dfs(r, c - 1, node, word); + + visit.delete(`${r},${c}`); + }; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + dfs(r, c, root, ""); + } + } + + return Array.from(res); + } +} +``` + +```csharp +public class TrieNode { + public Dictionary Children = new Dictionary(); + public bool IsWord = false; + + public void AddWord(string word) { + TrieNode cur = this; + foreach (char c in word) { + if (!cur.Children.ContainsKey(c)) { + cur.Children[c] = new TrieNode(); + } + cur = cur.Children[c]; + } + cur.IsWord = true; + } +} + +public class Solution { + private HashSet res = new HashSet(); + private bool[,] visit; + public List FindWords(char[][] board, string[] words) { + TrieNode root = new TrieNode(); + foreach (string word in words) { + root.AddWord(word); + } + + int ROWS = board.Length, COLS = board[0].Length; + visit = new bool[ROWS, COLS]; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + Dfs(board, r, c, root, ""); + } + } + return new List(res); + } + + private void Dfs(char[][] board, int r, int c, TrieNode node, string word) { + int ROWS = board.Length, COLS = board[0].Length; + if (r < 0 || c < 0 || r >= ROWS || + c >= COLS || visit[r, c] || + !node.Children.ContainsKey(board[r][c])) { + return; + } + + visit[r, c] = true; + node = node.Children[board[r][c]]; + word += board[r][c]; + if (node.IsWord) { + res.Add(word); + } + + Dfs(board, r + 1, c, node, word); + Dfs(board, r - 1, c, node, word); + Dfs(board, r, c + 1, node, word); + Dfs(board, r, c - 1, node, word); + + visit[r, c] = false; + } +} +``` + +```go +type TrieNode struct { + children map[byte]*TrieNode + isWord bool +} + +func NewTrieNode() *TrieNode { + return &TrieNode{children: make(map[byte]*TrieNode)} +} + +func (this *TrieNode) addWord(word string) { + cur := this + for i := 0; i < len(word); i++ { + c := word[i] + if _, found := cur.children[c]; !found { + cur.children[c] = NewTrieNode() + } + cur = cur.children[c] + } + cur.isWord = true +} + +func findWords(board [][]byte, words []string) []string { + root := NewTrieNode() + for _, w := range words { + root.addWord(w) + } + + rows, cols := len(board), len(board[0]) + var res []string + visit := make(map[[2]int]bool) + wordSet := make(map[string]bool) + + var dfs func(r, c int, node *TrieNode, word string) + dfs = func(r, c int, node *TrieNode, word string) { + if r < 0 || c < 0 || r >= rows || c >= cols || visit[[2]int{r, c}] || board[r][c] == 0 { + return + } + + char := board[r][c] + nextNode, found := node.children[char] + if !found { + return + } + + visit[[2]int{r, c}] = true + word += string(char) + if nextNode.isWord { + wordSet[word] = true + } + + dfs(r+1, c, nextNode, word) + dfs(r-1, c, nextNode, word) + dfs(r, c+1, nextNode, word) + dfs(r, c-1, nextNode, word) + + visit[[2]int{r, c}] = false + } + + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + dfs(r, c, root, "") + } + } + + for word := range wordSet { + res = append(res, word) + } + return res +} +``` + +```kotlin +class TrieNode { + val children = HashMap() + var isWord: Boolean = false + + fun addWord(word: String) { + var cur = this + for (c in word) { + cur = cur.children.getOrPut(c) { TrieNode() } + } + cur.isWord = true + } +} + +class Solution { + fun findWords(board: Array, words: Array): List { + val root = TrieNode() + for (w in words) { + root.addWord(w) + } + + val rows = board.size + val cols = board[0].size + val res = HashSet() + val visit = HashSet>() + + fun dfs(r: Int, c: Int, node: TrieNode, word: String) { + if (r < 0 || c < 0 || r >= rows || c >= cols || + (r to c) in visit || board[r][c] !in node.children) { + return + } + + visit.add(r to c) + val nextNode = node.children[board[r][c]]!! + val newWord = word + board[r][c] + if (nextNode.isWord) { + res.add(newWord) + } + + dfs(r + 1, c, nextNode, newWord) + dfs(r - 1, c, nextNode, newWord) + dfs(r, c + 1, nextNode, newWord) + dfs(r, c - 1, nextNode, newWord) + + visit.remove(r to c) + } + + for (r in 0 until rows) { + for (c in 0 until cols) { + dfs(r, c, root, "") + } + } + + return res.toList() + } +} +``` + +```swift +class TrieNode { + var children: [Character: TrieNode] = [:] + var isWord: Bool = false + + func addWord(_ word: String) { + var current = self + for char in word { + if current.children[char] == nil { + current.children[char] = TrieNode() + } + current = current.children[char]! + } + current.isWord = true + } +} + +class Solution { + func findWords(_ board: [[Character]], _ words: [String]) -> [String] { + let root = TrieNode() + for word in words { + root.addWord(word) + } + + let ROWS = board.count + let COLS = board[0].count + var result = Set() + var visited = Set<[Int]>() + + func dfs(_ r: Int, _ c: Int, _ node: TrieNode, _ word: String) { + if r < 0 || c < 0 || r >= ROWS || c >= COLS || + visited.contains([r, c]) || node.children[board[r][c]] == nil { + return + } + + visited.insert([r, c]) + let nextNode = node.children[board[r][c]]! + let newWord = word + String(board[r][c]) + + if nextNode.isWord { + result.insert(newWord) + } + + dfs(r + 1, c, nextNode, newWord) + dfs(r - 1, c, nextNode, newWord) + dfs(r, c + 1, nextNode, newWord) + dfs(r, c - 1, nextNode, newWord) + + visited.remove([r, c]) + } + + for r in 0.. Where $m$ is the number of rows, $n$ is the number of columns, $t$ is the maximum length of any word in the array $words$ and $s$ is the sum of the lengths of all the words. + +--- + +## 3. Backtracking (Trie) + +::tabs-start + +```python +class TrieNode: + def __init__(self): + self.children = [None] * 26 + self.idx = -1 + self.refs = 0 + + def addWord(self, word, i): + cur = self + cur.refs += 1 + for c in word: + index = ord(c) - ord('a') + if not cur.children[index]: + cur.children[index] = TrieNode() + cur = cur.children[index] + cur.refs += 1 + cur.idx = i + +class Solution: + def findWords(self, board: List[List[str]], words: List[str]) -> List[str]: + root = TrieNode() + for i in range(len(words)): + root.addWord(words[i], i) + + ROWS, COLS = len(board), len(board[0]) + res = [] + + def getIndex(c): + index = ord(c) - ord('a') + return index + + def dfs(r, c, node): + if (r < 0 or c < 0 or r >= ROWS or + c >= COLS or board[r][c] == '*' or + not node.children[getIndex(board[r][c])]): + return + + tmp = board[r][c] + board[r][c] = '*' + prev = node + node = node.children[getIndex(tmp)] + if node.idx != -1: + res.append(words[node.idx]) + node.idx = -1 + node.refs -= 1 + if not node.refs: + prev.children[getIndex(tmp)] = None + node = None + board[r][c] = tmp + return + + dfs(r + 1, c, node) + dfs(r - 1, c, node) + dfs(r, c + 1, node) + dfs(r, c - 1, node) + + board[r][c] = tmp + + for r in range(ROWS): + for c in range(COLS): + dfs(r, c, root) + + return res +``` + +```java +class TrieNode { + TrieNode[] children = new TrieNode[26]; + int idx = -1; + int refs = 0; + + public void addWord(String word, int i) { + TrieNode cur = this; + cur.refs++; + for (char c : word.toCharArray()) { + int index = c - 'a'; + if (cur.children[index] == null) { + cur.children[index] = new TrieNode(); + } + cur = cur.children[index]; + cur.refs++; + } + cur.idx = i; + } +} + +public class Solution { + List res = new ArrayList<>(); + + public List findWords(char[][] board, String[] words) { + TrieNode root = new TrieNode(); + for (int i = 0; i < words.length; i++) { + root.addWord(words[i], i); + } + + for (int r = 0; r < board.length; r++) { + for (int c = 0; c < board[0].length; c++) { + dfs(board, root, r, c, words); + } + } + + return res; + } + + private void dfs(char[][] board, TrieNode node, int r, int c, String[] words) { + if (r < 0 || c < 0 || r >= board.length || + c >= board[0].length || board[r][c] == '*' || + node.children[board[r][c] - 'a'] == null) { + return; + } + + char temp = board[r][c]; + board[r][c] = '*'; + TrieNode prev = node; + node = node.children[temp - 'a']; + if (node.idx != -1) { + res.add(words[node.idx]); + node.idx = -1; + node.refs--; + if (node.refs == 0) { + node = null; + prev.children[temp - 'a'] = null; + board[r][c] = temp; + return; + } + } + + dfs(board, node, r + 1, c, words); + dfs(board, node, r - 1, c, words); + dfs(board, node, r, c + 1, words); + dfs(board, node, r, c - 1, words); + + board[r][c] = temp; + } +} +``` + +```cpp +class TrieNode { +public: + TrieNode* children[26]; + int idx; + int refs; + + TrieNode() { + for (int i = 0; i < 26; ++i) { + children[i] = nullptr; + } + idx = -1; + refs = 0; + } + + void addWord(const string& word, int i) { + TrieNode* cur = this; + cur->refs++; + for (char c : word) { + int index = c - 'a'; + if (!cur->children[index]) { + cur->children[index] = new TrieNode(); + } + cur = cur->children[index]; + cur->refs++; + } + cur->idx = i; + } +}; + +class Solution { +public: + vector res; + + vector findWords(vector>& board, vector& words) { + TrieNode* root = new TrieNode(); + for (int i = 0; i < words.size(); ++i) { + root->addWord(words[i], i); + } + + for (int r = 0; r < board.size(); ++r) { + for (int c = 0; c < board[0].size(); ++c) { + dfs(board, root, r, c, words); + } + } + + return res; + } + + void dfs(auto& board, TrieNode* node, int r, int c, auto& words) { + if (r < 0 || c < 0 || r >= board.size() || + c >= board[0].size() || board[r][c] == '*' || + !node->children[board[r][c] - 'a']) { + return; + } + + char temp = board[r][c]; + board[r][c] = '*'; + TrieNode* prev = node; + node = node->children[temp - 'a']; + if (node->idx != -1) { + res.push_back(words[node->idx]); + node->idx = -1; + node->refs--; + if (!node->refs) { + prev->children[temp - 'a'] = nullptr; + node = nullptr; + board[r][c] = temp; + return; + } + } + + dfs(board, node, r + 1, c, words); + dfs(board, node, r - 1, c, words); + dfs(board, node, r, c + 1, words); + dfs(board, node, r, c - 1, words); + + board[r][c] = temp; + } +}; +``` + +```javascript +class TrieNode { + constructor() { + this.children = Array(26).fill(null); + this.idx = -1; + this.refs = 0; + } + + /** + * @param {string} word + * @param {number} i + * @return {void} + */ + addWord(word, i) { + let cur = this; + cur.refs++; + for (const c of word) { + const index = c.charCodeAt(0) - 'a'.charCodeAt(0); + if (cur.children[index] === null) { + cur.children[index] = new TrieNode(); + } + cur = cur.children[index]; + cur.refs++; + } + cur.idx = i; + } +} + +class Solution { + /** + * @param {character[][]} board + * @param {string[]} words + * @return {string[]} + */ + findWords(board, words) { + const root = new TrieNode(); + for (let i = 0; i < words.length; i++) { + root.addWord(words[i], i); + } + + const ROWS = board.length, COLS = board[0].length; + const res = []; + + const dfs = (r, c, node) => { + if (r < 0 || c < 0 || r >= ROWS || + c >= COLS || board[r][c] === '*' || + node.children[this.getId(board[r][c])] === null) { + return; + } + + let tmp = board[r][c]; + board[r][c] = '*'; + let prev = node; + node = node.children[this.getId(tmp)]; + if (node.idx !== -1) { + res.push(words[node.idx]); + node.idx = -1; + node.refs--; + if (node.refs === 0) { + prev.children[this.getId(tmp)] = null; + node = null; + board[r][c] = tmp; + return ; + } + } + + dfs(r + 1, c, node); + dfs(r - 1, c, node); + dfs(r, c + 1, node); + dfs(r, c - 1, node); + + board[r][c] = tmp; + }; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + dfs(r, c, root); + } + } + + return Array.from(res); + } + + /** + * @param {string} c + * @return {number} + */ + getId(c) { + return c.charCodeAt(0) - 'a'.charCodeAt(0); + } +} +``` + +```csharp +class TrieNode { + public TrieNode[] children = new TrieNode[26]; + public int idx = -1; + public int refs = 0; + + public void AddWord(string word, int i) { + TrieNode cur = this; + cur.refs++; + foreach (char c in word) { + int index = c - 'a'; + if (cur.children[index] == null) { + cur.children[index] = new TrieNode(); + } + cur = cur.children[index]; + cur.refs++; + } + cur.idx = i; + } +} + +public class Solution { + private List res = new List(); + + public List FindWords(char[][] board, string[] words) { + TrieNode root = new TrieNode(); + for (int i = 0; i < words.Length; i++) { + root.AddWord(words[i], i); + } + + for (int r = 0; r < board.Length; r++) { + for (int c = 0; c < board[0].Length; c++) { + Dfs(board, root, r, c, words); + } + } + + return res; + } + + private void Dfs(char[][] board, TrieNode node, int r, int c, string[] words) { + if (r < 0 || c < 0 || r >= board.Length || + c >= board[0].Length || board[r][c] == '*' || + node.children[board[r][c] - 'a'] == null) { + return; + } + + char temp = board[r][c]; + board[r][c] = '*'; + TrieNode prev = node; + node = node.children[temp - 'a']; + if (node.idx != -1) { + res.Add(words[node.idx]); + node.idx = -1; + node.refs--; + if (node.refs == 0) { + node = null; + prev.children[temp - 'a'] = null; + board[r][c] = temp; + return; + } + } + + Dfs(board, node, r + 1, c, words); + Dfs(board, node, r - 1, c, words); + Dfs(board, node, r, c + 1, words); + Dfs(board, node, r, c - 1, words); + + board[r][c] = temp; + } +} +``` + +```go +type TrieNode struct { + children [26]*TrieNode + idx int + refs int +} + +func NewTrieNode() *TrieNode { + return &TrieNode{idx: -1} +} + +func (this *TrieNode) addWord(word string, i int) { + cur := this + cur.refs++ + for _, ch := range word { + index := ch - 'a' + if cur.children[index] == nil { + cur.children[index] = NewTrieNode() + } + cur = cur.children[index] + cur.refs++ + } + cur.idx = i +} + +func findWords(board [][]byte, words []string) []string { + root := NewTrieNode() + for i, word := range words { + root.addWord(word, i) + } + + rows, cols := len(board), len(board[0]) + var res []string + + getIndex := func(c byte) int { return int(c - 'a') } + + var dfs func(r, c int, node *TrieNode) + dfs = func(r, c int, node *TrieNode) { + if r < 0 || c < 0 || r >= rows || c >= cols || + board[r][c] == '*' || node.children[getIndex(board[r][c])] == nil { + return + } + + tmp := board[r][c] + board[r][c] = '*' + prev := node + node = node.children[getIndex(tmp)] + if node.idx != -1 { + res = append(res, words[node.idx]) + node.idx = -1 + node.refs-- + if node.refs == 0 { + prev.children[getIndex(tmp)] = nil + board[r][c] = tmp + return + } + } + + dfs(r+1, c, node) + dfs(r-1, c, node) + dfs(r, c+1, node) + dfs(r, c-1, node) + + board[r][c] = tmp + } + + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + dfs(r, c, root) + } + } + + return res +} +``` + +```kotlin +class TrieNode { + val children = Array(26) { null } + var idx = -1 + var refs = 0 + + fun addWord(word: String, i: Int) { + var cur = this + cur.refs++ + for (c in word) { + val index = c - 'a' + if (cur.children[index] == null) { + cur.children[index] = TrieNode() + } + cur = cur.children[index]!! + cur.refs++ + } + cur.idx = i + } +} + +class Solution { + fun findWords(board: Array, words: Array): List { + val root = TrieNode() + words.forEachIndexed { i, word -> root.addWord(word, i) } + + val rows = board.size + val cols = board[0].size + val res = mutableListOf() + + fun getIndex(c: Char): Int = c - 'a' + + fun dfs(r: Int, c: Int, node: TrieNode?) { + if (r < 0 || c < 0 || r >= rows || c >= cols || board[r][c] == '*' || + node?.children?.get(getIndex(board[r][c])) == null) { + return + } + + val tmp = board[r][c] + board[r][c] = '*' + val prev = node + val nextNode = node.children[getIndex(tmp)] + + if (nextNode != null && nextNode.idx != -1) { + res.add(words[nextNode.idx]) + nextNode.idx = -1 + nextNode.refs-- + if (nextNode.refs == 0) { + prev?.children?.set(getIndex(tmp), null) + board[r][c] = tmp + return + } + } + + dfs(r + 1, c, nextNode) + dfs(r - 1, c, nextNode) + dfs(r, c + 1, nextNode) + dfs(r, c - 1, nextNode) + + board[r][c] = tmp + } + + for (r in 0 until rows) { + for (c in 0 until cols) { + dfs(r, c, root) + } + } + + return res + } +} +``` + +```swift +class TrieNode { + var children: [TrieNode?] = Array(repeating: nil, count: 26) + var idx: Int = -1 + var refs: Int = 0 + + func addWord(_ word: String, _ i: Int) { + var cur = self + cur.refs += 1 + for c in word { + let index = Int(c.asciiValue! - Character("a").asciiValue!) + if cur.children[index] == nil { + cur.children[index] = TrieNode() + } + cur = cur.children[index]! + cur.refs += 1 + } + cur.idx = i + } +} + +class Solution { + func findWords(_ board: [[Character]], _ words: [String]) -> [String] { + let root = TrieNode() + for i in 0.. Int { + return Int(c.asciiValue! - Character("a").asciiValue!) + } + + func dfs(_ r: Int, _ c: Int, _ node: TrieNode) { + if r < 0 || c < 0 || r >= ROWS || c >= COLS || + boardCopy[r][c] == "*" || + node.children[getIndex(boardCopy[r][c])] == nil { + return + } + + let tmp = boardCopy[r][c] + boardCopy[r][c] = "*" + let prev = node + let nextNode = node.children[getIndex(tmp)]! + + if nextNode.idx != -1 { + res.append(words[nextNode.idx]) + nextNode.idx = -1 + nextNode.refs -= 1 + if nextNode.refs == 0 { + prev.children[getIndex(tmp)] = nil + boardCopy[r][c] = tmp + return + } + } + + dfs(r + 1, c, nextNode) + dfs(r - 1, c, nextNode) + dfs(r, c + 1, nextNode) + dfs(r, c - 1, nextNode) + + boardCopy[r][c] = tmp + } + + for r in 0.. Where $m$ is the number of rows, $n$ is the number of columns, $t$ is the maximum length of any word in the array $words$ and $s$ is the sum of the lengths of all the words. \ No newline at end of file diff --git a/articles/search-for-word.md b/articles/search-for-word.md new file mode 100644 index 000000000..bb2895568 --- /dev/null +++ b/articles/search-for-word.md @@ -0,0 +1,942 @@ +## 1. Backtracking (Hash Set) + +::tabs-start + +```python +class Solution: + def exist(self, board: List[List[str]], word: str) -> bool: + ROWS, COLS = len(board), len(board[0]) + path = set() + + def dfs(r, c, i): + if i == len(word): + return True + + if (min(r, c) < 0 or + r >= ROWS or c >= COLS or + word[i] != board[r][c] or + (r, c) in path): + return False + + path.add((r, c)) + res = (dfs(r + 1, c, i + 1) or + dfs(r - 1, c, i + 1) or + dfs(r, c + 1, i + 1) or + dfs(r, c - 1, i + 1)) + path.remove((r, c)) + return res + + for r in range(ROWS): + for c in range(COLS): + if dfs(r, c, 0): + return True + return False +``` + +```java +public class Solution { + private int ROWS, COLS; + private Set> path = new HashSet<>(); + + public boolean exist(char[][] board, String word) { + ROWS = board.length; + COLS = board[0].length; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (dfs(board, word, r, c, 0)) { + return true; + } + } + } + return false; + } + + private boolean dfs(char[][] board, String word, int r, int c, int i) { + if (i == word.length()) { + return true; + } + + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || + board[r][c] != word.charAt(i) || + path.contains(new Pair<>(r, c))) { + return false; + } + + path.add(new Pair<>(r, c)); + boolean res = dfs(board, word, r + 1, c, i + 1) || + dfs(board, word, r - 1, c, i + 1) || + dfs(board, word, r, c + 1, i + 1) || + dfs(board, word, r, c - 1, i + 1); + path.remove(new Pair<>(r, c)); + + return res; + } +} +``` + +```cpp +class Solution { +public: + int ROWS, COLS; + set> path; + + bool exist(vector>& board, string word) { + ROWS = board.size(); + COLS = board[0].size(); + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (dfs(board, word, r, c, 0)) { + return true; + } + } + } + return false; + } + + bool dfs(vector>& board, string word, int r, int c, int i) { + if (i == word.length()) { + return true; + } + + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || + board[r][c] != word[i] || path.count({r, c})) { + return false; + } + + path.insert({r, c}); + bool res = dfs(board, word, r + 1, c, i + 1) || + dfs(board, word, r - 1, c, i + 1) || + dfs(board, word, r, c + 1, i + 1) || + dfs(board, word, r, c - 1, i + 1); + path.erase({r, c}); + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[][]} board + * @param {string} word + * @return {boolean} + */ + exist(board, word) { + const ROWS = board.length; + const COLS = board[0].length; + const path = new Set(); + + const dfs = (r, c, i) => { + if (i === word.length) return true; + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || + board[r][c] !== word[i] || path.has(`${r},${c}`)) { + return false; + } + + path.add(`${r},${c}`); + const res = dfs(r + 1, c, i + 1) || + dfs(r - 1, c, i + 1) || + dfs(r, c + 1, i + 1) || + dfs(r, c - 1, i + 1); + path.delete(`${r},${c}`); + return res; + }; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (dfs(r, c, 0)) return true; + } + } + return false; + } +} +``` + +```csharp +public class Solution { + private int ROWS, COLS; + private HashSet<(int, int)> path = new HashSet<(int, int)>(); + + public bool Exist(char[][] board, string word) { + ROWS = board.Length; + COLS = board[0].Length; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (DFS(board, word, r, c, 0)) { + return true; + } + } + } + return false; + } + + private bool DFS(char[][] board, string word, int r, int c, int i) { + if (i == word.Length) { + return true; + } + + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || + board[r][c] != word[i] || path.Contains((r, c))) { + return false; + } + + path.Add((r, c)); + bool res = DFS(board, word, r + 1, c, i + 1) || + DFS(board, word, r - 1, c, i + 1) || + DFS(board, word, r, c + 1, i + 1) || + DFS(board, word, r, c - 1, i + 1); + path.Remove((r, c)); + + return res; + } +} +``` + +```go +func exist(board [][]byte, word string) bool { + rows, cols := len(board), len(board[0]) + path := make(map[[2]int]bool) + + var dfs func(r, c, i int) bool + dfs = func(r, c, i int) bool { + if i == len(word) { + return true + } + if r < 0 || c < 0 || r >= rows || c >= cols || + board[r][c] != word[i] || path[[2]int{r, c}] { + return false + } + + path[[2]int{r, c}] = true + res := dfs(r+1, c, i+1) || + dfs(r-1, c, i+1) || + dfs(r, c+1, i+1) || + dfs(r, c-1, i+1) + delete(path, [2]int{r, c}) + + return res + } + + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + if dfs(r, c, 0) { + return true + } + } + } + return false +} +``` + +```kotlin +class Solution { + fun exist(board: Array, word: String): Boolean { + val rows = board.size + val cols = board[0].size + val path = HashSet>() + + fun dfs(r: Int, c: Int, i: Int): Boolean { + if (i == word.length) return true + if (r < 0 || c < 0 || r >= rows || c >= cols || + board[r][c] != word[i] || Pair(r, c) in path) { + return false + } + + path.add(Pair(r, c)) + val res = dfs(r + 1, c, i + 1) || + dfs(r - 1, c, i + 1) || + dfs(r, c + 1, i + 1) || + dfs(r, c - 1, i + 1) + path.remove(Pair(r, c)) + + return res + } + + for (r in 0 until rows) { + for (c in 0 until cols) { + if (dfs(r, c, 0)) { + return true + } + } + } + return false + } +} +``` + +```swift +class Solution { + func exist(_ board: [[Character]], _ word: String) -> Bool { + let ROWS = board.count + let COLS = board[0].count + var path = Set<[Int]>() + let wordArray = Array(word) + + func dfs(_ r: Int, _ c: Int, _ i: Int) -> Bool { + if i == wordArray.count { + return true + } + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || + board[r][c] != wordArray[i] || path.contains([r, c])) { + return false + } + + path.insert([r, c]) + let res = dfs(r + 1, c, i + 1) || + dfs(r - 1, c, i + 1) || + dfs(r, c + 1, i + 1) || + dfs(r, c - 1, i + 1) + path.remove([r, c]) + return res + } + + for r in 0.. Where $m$ is the number of cells in the $board$ and $n$ is the length of the $word$. + +--- + +## 2. Backtracking (Visited Array) + +::tabs-start + +```python +class Solution: + def exist(self, board: List[List[str]], word: str) -> bool: + ROWS, COLS = len(board), len(board[0]) + visited = [[False for _ in range(COLS)] for _ in range(ROWS)] + + def dfs(r, c, i): + if i == len(word): + return True + if (r < 0 or c < 0 or r >= ROWS or c >= COLS or + word[i] != board[r][c] or visited[r][c]): + return False + + visited[r][c] = True + res = (dfs(r + 1, c, i + 1) or + dfs(r - 1, c, i + 1) or + dfs(r, c + 1, i + 1) or + dfs(r, c - 1, i + 1)) + visited[r][c] = False + return res + + for r in range(ROWS): + for c in range(COLS): + if dfs(r, c, 0): + return True + return False +``` + +```java +public class Solution { + private int ROWS, COLS; + private boolean[][] visited; + + public boolean exist(char[][] board, String word) { + ROWS = board.length; + COLS = board[0].length; + visited = new boolean[ROWS][COLS]; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (dfs(board, word, r, c, 0)) { + return true; + } + } + } + return false; + } + + private boolean dfs(char[][] board, String word, int r, int c, int i) { + if (i == word.length()) { + return true; + } + + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || + board[r][c] != word.charAt(i) || visited[r][c]) { + return false; + } + + visited[r][c] = true; + boolean res = dfs(board, word, r + 1, c, i + 1) || + dfs(board, word, r - 1, c, i + 1) || + dfs(board, word, r, c + 1, i + 1) || + dfs(board, word, r, c - 1, i + 1); + visited[r][c] = false; + + return res; + } +} +``` + +```cpp +class Solution { +public: + int ROWS, COLS; + vector> visited; + + bool exist(vector>& board, string word) { + ROWS = board.size(); + COLS = board[0].size(); + visited = vector>(ROWS, vector(COLS, false)); + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (dfs(board, word, r, c, 0)) { + return true; + } + } + } + return false; + } + + bool dfs(vector>& board, string word, int r, int c, int i) { + if (i == word.length()) { + return true; + } + + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || + board[r][c] != word[i] || visited[r][c]) { + return false; + } + + visited[r][c] = true; + bool res = dfs(board, word, r + 1, c, i + 1) || + dfs(board, word, r - 1, c, i + 1) || + dfs(board, word, r, c + 1, i + 1) || + dfs(board, word, r, c - 1, i + 1); + visited[r][c] = false; + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[][]} board + * @param {string} word + * @return {boolean} + */ + exist(board, word) { + const ROWS = board.length; + const COLS = board[0].length; + const visited = Array.from({ length: ROWS }, () => Array(COLS).fill(false)); + + const dfs = (r, c, i) => { + if (i === word.length) return true; + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || + board[r][c] !== word[i] || visited[r][c]) { + return false; + } + + visited[r][c] = true; + const res = dfs(r + 1, c, i + 1) || + dfs(r - 1, c, i + 1) || + dfs(r, c + 1, i + 1) || + dfs(r, c - 1, i + 1); + visited[r][c] = false; + return res; + }; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (dfs(r, c, 0)) return true; + } + } + return false; + } +} +``` + +```csharp +public class Solution { + private int ROWS, COLS; + private bool[,] visited; + + public bool Exist(char[][] board, string word) { + ROWS = board.Length; + COLS = board[0].Length; + visited = new bool[ROWS, COLS]; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (DFS(board, word, r, c, 0)) { + return true; + } + } + } + return false; + } + + private bool DFS(char[][] board, string word, int r, int c, int i) { + if (i == word.Length) { + return true; + } + + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || + board[r][c] != word[i] || visited[r, c]) { + return false; + } + + visited[r, c] = true; + bool res = DFS(board, word, r + 1, c, i + 1) || + DFS(board, word, r - 1, c, i + 1) || + DFS(board, word, r, c + 1, i + 1) || + DFS(board, word, r, c - 1, i + 1); + visited[r, c] = false; + + return res; + } +} +``` + +```go +func exist(board [][]byte, word string) bool { + rows, cols := len(board), len(board[0]) + visited := make([][]bool, rows) + for i := range visited { + visited[i] = make([]bool, cols) + } + + var dfs func(r, c, i int) bool + dfs = func(r, c, i int) bool { + if i == len(word) { + return true + } + if r < 0 || c < 0 || r >= rows || c >= cols || + board[r][c] != word[i] || visited[r][c] { + return false + } + + visited[r][c] = true + res := dfs(r+1, c, i+1) || + dfs(r-1, c, i+1) || + dfs(r, c+1, i+1) || + dfs(r, c-1, i+1) + visited[r][c] = false + + return res + } + + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + if dfs(r, c, 0) { + return true + } + } + } + return false +} +``` + +```kotlin +class Solution { + fun exist(board: Array, word: String): Boolean { + val rows = board.size + val cols = board[0].size + val visited = Array(rows) { BooleanArray(cols) } + + fun dfs(r: Int, c: Int, i: Int): Boolean { + if (i == word.length) return true + if (r < 0 || c < 0 || r >= rows || c >= cols || + board[r][c] != word[i] || visited[r][c]) { + return false + } + + visited[r][c] = true + val res = dfs(r + 1, c, i + 1) || + dfs(r - 1, c, i + 1) || + dfs(r, c + 1, i + 1) || + dfs(r, c - 1, i + 1) + visited[r][c] = false + + return res + } + + for (r in 0 until rows) { + for (c in 0 until cols) { + if (dfs(r, c, 0)) { + return true + } + } + } + return false + } +} +``` + +```swift +class Solution { + func exist(_ board: [[Character]], _ word: String) -> Bool { + let ROWS = board.count + let COLS = board[0].count + var visited = Array(repeating: Array(repeating: false, count: COLS), count: ROWS) + let wordArray = Array(word) + + func dfs(_ r: Int, _ c: Int, _ i: Int) -> Bool { + if i == wordArray.count { + return true + } + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || + board[r][c] != wordArray[i] || visited[r][c]) { + return false + } + + visited[r][c] = true + let res = dfs(r + 1, c, i + 1) || + dfs(r - 1, c, i + 1) || + dfs(r, c + 1, i + 1) || + dfs(r, c - 1, i + 1) + visited[r][c] = false + return res + } + + for r in 0.. Where $m$ is the number of cells in the $board$ and $n$ is the length of the $word$. + +--- + +## 3. Backtracking (Optimal) + +::tabs-start + +```python +class Solution: + def exist(self, board: List[List[str]], word: str) -> bool: + ROWS, COLS = len(board), len(board[0]) + + def dfs(r, c, i): + if i == len(word): + return True + if (r < 0 or c < 0 or r >= ROWS or c >= COLS or + word[i] != board[r][c] or board[r][c] == '#'): + return False + + board[r][c] = '#' + res = (dfs(r + 1, c, i + 1) or + dfs(r - 1, c, i + 1) or + dfs(r, c + 1, i + 1) or + dfs(r, c - 1, i + 1)) + board[r][c] = word[i] + return res + + for r in range(ROWS): + for c in range(COLS): + if dfs(r, c, 0): + return True + return False +``` + +```java +public class Solution { + private int ROWS, COLS; + + public boolean exist(char[][] board, String word) { + ROWS = board.length; + COLS = board[0].length; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (dfs(board, word, r, c, 0)) { + return true; + } + } + } + return false; + } + + private boolean dfs(char[][] board, String word, int r, int c, int i) { + if (i == word.length()) { + return true; + } + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || + board[r][c] != word.charAt(i) || board[r][c] == '#') { + return false; + } + + board[r][c] = '#'; + boolean res = dfs(board, word, r + 1, c, i + 1) || + dfs(board, word, r - 1, c, i + 1) || + dfs(board, word, r, c + 1, i + 1) || + dfs(board, word, r, c - 1, i + 1); + board[r][c] = word.charAt(i); + return res; + } +} +``` + +```cpp +class Solution { +public: + int ROWS, COLS; + + bool exist(vector>& board, string word) { + ROWS = board.size(); + COLS = board[0].size(); + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (dfs(board, word, r, c, 0)) { + return true; + } + } + } + return false; + } + + bool dfs(vector>& board, string word, int r, int c, int i) { + if (i == word.size()) { + return true; + } + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || + board[r][c] != word[i] || board[r][c] == '#') { + return false; + } + + board[r][c] = '#'; + bool res = dfs(board, word, r + 1, c, i + 1) || + dfs(board, word, r - 1, c, i + 1) || + dfs(board, word, r, c + 1, i + 1) || + dfs(board, word, r, c - 1, i + 1); + board[r][c] = word[i]; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[][]} board + * @param {string} word + * @return {boolean} + */ + exist(board, word) { + const ROWS = board.length; + const COLS = board[0].length; + + const dfs = (r, c, i) => { + if (i === word.length) return true; + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || + board[r][c] !== word[i] || board[r][c] === '#') { + return false; + } + + board[r][c] = '#'; + const res = dfs(r + 1, c, i + 1) || + dfs(r - 1, c, i + 1) || + dfs(r, c + 1, i + 1) || + dfs(r, c - 1, i + 1); + board[r][c] = word[i]; + return res; + }; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (dfs(r, c, 0)) return true; + } + } + return false; + } +} +``` + +```csharp +public class Solution { + private int ROWS, COLS; + + public bool Exist(char[][] board, string word) { + ROWS = board.Length; + COLS = board[0].Length; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (Dfs(board, word, r, c, 0)) { + return true; + } + } + } + return false; + } + + private bool Dfs(char[][] board, string word, int r, int c, int i) { + if (i == word.Length) { + return true; + } + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || + board[r][c] != word[i] || board[r][c] == '#') { + return false; + } + + board[r][c] = '#'; + bool res = Dfs(board, word, r + 1, c, i + 1) || + Dfs(board, word, r - 1, c, i + 1) || + Dfs(board, word, r, c + 1, i + 1) || + Dfs(board, word, r, c - 1, i + 1); + board[r][c] = word[i]; + return res; + } +} +``` + +```go +func exist(board [][]byte, word string) bool { + rows, cols := len(board), len(board[0]) + + var dfs func(r, c, i int) bool + dfs = func(r, c, i int) bool { + if i == len(word) { + return true + } + if r < 0 || c < 0 || r >= rows || c >= cols || + board[r][c] != word[i] || board[r][c] == '#' { + return false + } + + temp := board[r][c] + board[r][c] = '#' + res := dfs(r+1, c, i+1) || + dfs(r-1, c, i+1) || + dfs(r, c+1, i+1) || + dfs(r, c-1, i+1) + board[r][c] = temp + + return res + } + + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + if dfs(r, c, 0) { + return true + } + } + } + return false +} +``` + +```kotlin +class Solution { + fun exist(board: Array, word: String): Boolean { + val rows = board.size + val cols = board[0].size + + fun dfs(r: Int, c: Int, i: Int): Boolean { + if (i == word.length) return true + if (r < 0 || c < 0 || r >= rows || c >= cols || + board[r][c] != word[i] || board[r][c] == '#') { + return false + } + + val temp = board[r][c] + board[r][c] = '#' + val res = dfs(r + 1, c, i + 1) || + dfs(r - 1, c, i + 1) || + dfs(r, c + 1, i + 1) || + dfs(r, c - 1, i + 1) + board[r][c] = temp + + return res + } + + for (r in 0 until rows) { + for (c in 0 until cols) { + if (dfs(r, c, 0)) { + return true + } + } + } + return false + } +} +``` + +```swift +class Solution { + func exist(_ board: [[Character]], _ word: String) -> Bool { + let ROWS = board.count + let COLS = board[0].count + let wordArray = Array(word) + var board = board + + func dfs(_ r: Int, _ c: Int, _ i: Int) -> Bool { + if i == wordArray.count { + return true + } + if (r < 0 || c < 0 || r >= ROWS || c >= COLS || + board[r][c] != wordArray[i] || board[r][c] == "#") { + return false + } + + let temp = board[r][c] + board[r][c] = "#" + let res = dfs(r + 1, c, i + 1) || + dfs(r - 1, c, i + 1) || + dfs(r, c + 1, i + 1) || + dfs(r, c - 1, i + 1) + board[r][c] = temp + return res + } + + for r in 0.. Where $m$ is the number of cells in the $board$ and $n$ is the length of the $word$. \ No newline at end of file diff --git a/articles/search-in-a-binary-search-tree.md b/articles/search-in-a-binary-search-tree.md new file mode 100644 index 000000000..548c56821 --- /dev/null +++ b/articles/search-in-a-binary-search-tree.md @@ -0,0 +1,205 @@ +## 1. Recursion + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def searchBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]: + if not root or root.val == val: + return root + return self.searchBST(root.left, val) if val < root.val else self.searchBST(root.right, val) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode searchBST(TreeNode root, int val) { + if (root == null || root.val == val) { + return root; + } + return val < root.val ? searchBST(root.left, val) : searchBST(root.right, val); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* searchBST(TreeNode* root, int val) { + if (!root || root->val == val) { + return root; + } + return val < root->val ? searchBST(root->left, val) : searchBST(root->right, val); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} val + * @return {TreeNode} + */ + searchBST(root, val) { + if (!root || root.val === val) { + return root; + } + return val < root.val ? this.searchBST(root.left, val) : this.searchBST(root.right, val); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(H)$ +* Space complexity: $O(H)$ for recursion stack. + +> Where $H$ is the height of the given tree. + +--- + +## 2. Iteration + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def searchBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]: + while root and root.val != val: + root = root.left if val < root.val else root.right + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode searchBST(TreeNode root, int val) { + while (root != null && root.val != val) { + root = val < root.val ? root.left : root.right; + } + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* searchBST(TreeNode* root, int val) { + while (root && root->val != val) { + root = val < root->val ? root->left : root->right; + } + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} val + * @return {TreeNode} + */ + searchBST(root, val) { + while (root != null && root.val != val) { + root = val < root.val ? root.left : root.right; + } + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(H)$ +* Space complexity: $O(1)$ extra space. + +> Where $H$ is the height of the given tree. \ No newline at end of file diff --git a/articles/search-in-rotated-sorted-array-ii.md b/articles/search-in-rotated-sorted-array-ii.md new file mode 100644 index 000000000..04af02db0 --- /dev/null +++ b/articles/search-in-rotated-sorted-array-ii.md @@ -0,0 +1,246 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def search(self, nums: List[int], target: int) -> bool: + return target in nums +``` + +```java +public class Solution { + public boolean search(int[] nums, int target) { + for (int num : nums) { + if (num == target) { + return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool search(vector& nums, int target) { + for (int& num : nums) { + if (num == target) { + return true; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {boolean} + */ + search(nums, target) { + for (let num of nums) { + if (num === target) { + return true; + } + } + return false; + } +} +``` + +```csharp +public class Solution { + public bool Search(int[] nums, int target) { + return nums.Contains(target); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Binary Search + +::tabs-start + +```python +class Solution: + def search(self, nums: List[int], target: int) -> bool: + l, r = 0, len(nums) - 1 + while l <= r: + m = l + (r - l) // 2 + if nums[m] == target: + return True + + if nums[l] < nums[m]: # Left portion + if nums[l] <= target < nums[m]: + r = m - 1 + else: + l = m + 1 + elif nums[l] > nums[m]: # Right portion + if nums[m] < target <= nums[r]: + l = m + 1 + else: + r = m - 1 + else: + l += 1 + + return False +``` + +```java +public class Solution { + public boolean search(int[] nums, int target) { + int l = 0, r = nums.length - 1; + + while (l <= r) { + int m = l + (r - l) / 2; + + if (nums[m] == target) { + return true; + } + + if (nums[l] < nums[m]) { // Left portion + if (nums[l] <= target && target < nums[m]) { + r = m - 1; + } else { + l = m + 1; + } + } else if (nums[l] > nums[m]) { // Right portion + if (nums[m] < target && target <= nums[r]) { + l = m + 1; + } else { + r = m - 1; + } + } else { + l++; + } + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool search(vector& nums, int target) { + int l = 0, r = nums.size() - 1; + + while (l <= r) { + int m = l + (r - l) / 2; + + if (nums[m] == target) { + return true; + } + + if (nums[l] < nums[m]) { // Left portion + if (nums[l] <= target && target < nums[m]) { + r = m - 1; + } else { + l = m + 1; + } + } else if (nums[l] > nums[m]) { // Right portion + if (nums[m] < target && target <= nums[r]) { + l = m + 1; + } else { + r = m - 1; + } + } else { + l++; + } + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {boolean} + */ + search(nums, target) { + let l = 0, r = nums.length - 1; + + while (l <= r) { + const m = Math.floor(l + (r - l) / 2); + + if (nums[m] === target) { + return true; + } + + if (nums[l] < nums[m]) { // Left portion + if (nums[l] <= target && target < nums[m]) { + r = m - 1; + } else { + l = m + 1; + } + } else if (nums[l] > nums[m]) { // Right portion + if (nums[m] < target && target <= nums[r]) { + l = m + 1; + } else { + r = m - 1; + } + } else { + l++; + } + } + + return false; + } +} +``` + +```csharp +public class Solution { + public bool Search(int[] nums, int target) { + int l = 0, r = nums.Length - 1; + while (l <= r) { + int m = l + (r - l) / 2; + if (nums[m] == target) { + return true; + } + + if (nums[l] < nums[m]) { + if (nums[l] <= target && target < nums[m]) { + r = m - 1; + } else { + l = m + 1; + } + } else if (nums[l] > nums[m]) { + if (nums[m] < target && target <= nums[r]) { + l = m + 1; + } else { + r = m - 1; + } + } else { + l++; + } + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ in average case, $O(n)$ in worst case. +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/search-insert-position.md b/articles/search-insert-position.md new file mode 100644 index 000000000..7e6cbd60e --- /dev/null +++ b/articles/search-insert-position.md @@ -0,0 +1,483 @@ +## 1. Linear Search + +::tabs-start + +```python +class Solution: + def searchInsert(self, nums: List[int], target: int) -> int: + for i in range(len(nums)): + if nums[i] >= target: + return i + return len(nums) +``` + +```java +public class Solution { + public int searchInsert(int[] nums, int target) { + for (int i = 0; i < nums.length; i++) { + if (nums[i] >= target) { + return i; + } + } + return nums.length; + } +} +``` + +```cpp +class Solution { +public: + int searchInsert(vector& nums, int target) { + for (int i = 0; i < nums.size(); i++) { + if (nums[i] >= target) { + return i; + } + } + return nums.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + searchInsert(nums, target) { + for (let i = 0; i < nums.length; i++) { + if (nums[i] >= target) { + return i; + } + } + return nums.length; + } +} +``` + +```csharp +public class Solution { + public int SearchInsert(int[] nums, int target) { + for (int i = 0; i < nums.Length; i++) { + if (nums[i] >= target) { + return i; + } + } + return nums.Length; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Binary Search - I + +::tabs-start + +```python +class Solution: + def searchInsert(self, nums: List[int], target: int) -> int: + res = len(nums) + l, r = 0, len(nums) - 1 + while l <= r: + mid = (l + r) // 2 + if nums[mid] == target: + return mid + if nums[mid] > target: + res = mid + r = mid - 1 + else: + l = mid + 1 + return res +``` + +```java +public class Solution { + public int searchInsert(int[] nums, int target) { + int res = nums.length; + int l = 0, r = nums.length - 1; + while (l <= r) { + int mid = (l + r) / 2; + if (nums[mid] == target) { + return mid; + } + if (nums[mid] > target) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int searchInsert(vector& nums, int target) { + int res = nums.size(); + int l = 0, r = nums.size() - 1; + while (l <= r) { + int mid = (l + r) / 2; + if (nums[mid] == target) { + return mid; + } + if (nums[mid] > target) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + searchInsert(nums, target) { + let res = nums.length; + let l = 0, r = nums.length - 1; + while (l <= r) { + const mid = Math.floor((l + r) / 2); + if (nums[mid] === target) { + return mid; + } + if (nums[mid] > target) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int SearchInsert(int[] nums, int target) { + int res = nums.Length; + int l = 0, r = nums.Length - 1; + + while (l <= r) { + int mid = (l + r) / 2; + if (nums[mid] == target) { + return mid; + } + if (nums[mid] > target) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Binary Search - II + +::tabs-start + +```python +class Solution: + def searchInsert(self, nums: List[int], target: int) -> int: + l, r = 0, len(nums) - 1 + while l <= r: + mid = (l + r) // 2 + if nums[mid] == target: + return mid + if nums[mid] > target: + r = mid - 1 + else: + l = mid + 1 + return l +``` + +```java +public class Solution { + public int searchInsert(int[] nums, int target) { + int l = 0, r = nums.length - 1; + while (l <= r) { + int mid = (l + r) / 2; + if (nums[mid] == target) { + return mid; + } + if (nums[mid] > target) { + r = mid - 1; + } else { + l = mid + 1; + } + } + return l; + } +} +``` + +```cpp +class Solution { +public: + int searchInsert(vector& nums, int target) { + int l = 0, r = nums.size() - 1; + while (l <= r) { + int mid = (l + r) / 2; + if (nums[mid] == target) { + return mid; + } + if (nums[mid] > target) { + r = mid - 1; + } else { + l = mid + 1; + } + } + return l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + searchInsert(nums, target) { + let l = 0, r = nums.length - 1; + while (l <= r) { + const mid = Math.floor((l + r) / 2); + if (nums[mid] === target) { + return mid; + } + if (nums[mid] > target) { + r = mid - 1; + } else { + l = mid + 1; + } + } + return l; + } +} +``` + +```csharp +public class Solution { + public int SearchInsert(int[] nums, int target) { + int l = 0, r = nums.Length - 1; + + while (l <= r) { + int mid = (l + r) / 2; + if (nums[mid] == target) { + return mid; + } + if (nums[mid] > target) { + r = mid - 1; + } else { + l = mid + 1; + } + } + + return l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Binary Search (Lower Bound) + +::tabs-start + +```python +class Solution: + def searchInsert(self, nums: List[int], target: int) -> int: + l, r = 0, len(nums) + while l < r: + m = l + ((r - l) // 2) + if nums[m] >= target: + r = m + elif nums[m] < target: + l = m + 1 + return l +``` + +```java +public class Solution { + public int searchInsert(int[] nums, int target) { + int l = 0, r = nums.length; + while (l < r) { + int m = l + (r - l) / 2; + if (nums[m] >= target) { + r = m; + } else { + l = m + 1; + } + } + return l; + } +} +``` + +```cpp +class Solution { +public: + int searchInsert(vector& nums, int target) { + int l = 0, r = nums.size(); + while (l < r) { + int m = l + (r - l) / 2; + if (nums[m] >= target) { + r = m; + } else { + l = m + 1; + } + } + return l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + searchInsert(nums, target) { + let l = 0, r = nums.length; + while (l < r) { + let m = l + Math.floor((r - l) / 2); + if (nums[m] >= target) { + r = m; + } else { + l = m + 1; + } + } + return l; + } +} +``` + +```csharp +public class Solution { + public int SearchInsert(int[] nums, int target) { + int l = 0, r = nums.Length; + + while (l < r) { + int m = l + (r - l) / 2; + if (nums[m] >= target) { + r = m; + } else { + l = m + 1; + } + } + + return l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Built-In Binary Search Function + +::tabs-start + +```python +import bisect +class Solution: + def searchInsert(self, nums: List[int], target: int) -> int: + return bisect.bisect_left(nums, target) +``` + +```java +public class Solution { + public int searchInsert(int[] nums, int target) { + int index = Arrays.binarySearch(nums, target); + return index >= 0 ? index : -index - 1; + } +} +``` + +```cpp +class Solution { +public: + int searchInsert(vector& nums, int target) { + return lower_bound(nums.begin(), nums.end(), target) - nums.begin(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + searchInsert(nums, target) { + // There is no built in Binary Search function for JS. + let index = nums.findIndex(x => x >= target); + return index !== -1 ? index : nums.length + } +} +``` + +```csharp +public class Solution { + public int SearchInsert(int[] nums, int target) { + int idx = Array.BinarySearch(nums, target); + return idx >= 0 ? idx : ~idx; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/search-suggestions-system.md b/articles/search-suggestions-system.md new file mode 100644 index 000000000..87325be56 --- /dev/null +++ b/articles/search-suggestions-system.md @@ -0,0 +1,589 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def suggestedProducts(self, products: List[str], searchWord: str) -> List[List[str]]: + res = [] + m = len(searchWord) + products.sort() + + for i in range(m): + cur = [] + for w in products: + if len(w) <= i: + continue + + flag = True + for j in range(i + 1): + if w[j] != searchWord[j]: + flag = False + break + + if flag: + cur.append(w) + if len(cur) == 3: + break + + if not cur: + for j in range(i, m): + res.append([]) + break + res.append(cur) + + return res +``` + +```java +public class Solution { + public List> suggestedProducts(String[] products, String searchWord) { + List> res = new ArrayList<>(); + int m = searchWord.length(); + Arrays.sort(products); + + for (int i = 0; i < m; i++) { + List cur = new ArrayList<>(); + for (String w : products) { + if (w.length() <= i) continue; + + boolean flag = true; + for (int j = 0; j <= i; j++) { + if (w.charAt(j) != searchWord.charAt(j)) { + flag = false; + break; + } + } + + if (flag) { + cur.add(w); + if (cur.size() == 3) break; + } + } + + if (cur.isEmpty()) { + while (i < m) { + res.add(new ArrayList<>()); + i++; + } + break; + } + + res.add(cur); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> suggestedProducts(vector& products, string searchWord) { + vector> res; + int m = searchWord.size(); + sort(products.begin(), products.end()); + + for (int i = 0; i < m; i++) { + vector cur; + for (const string& w : products) { + if (w.size() <= i) continue; + + bool flag = true; + for (int j = 0; j <= i; j++) { + if (w[j] != searchWord[j]) { + flag = false; + break; + } + } + + if (flag) { + cur.push_back(w); + if (cur.size() == 3) break; + } + } + + if (cur.empty()) { + while (i < m) { + res.push_back({}); + i++; + } + break; + } + + res.push_back(cur); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} products + * @param {string} searchWord + * @return {string[][]} + */ + suggestedProducts(products, searchWord) { + let res = []; + let m = searchWord.length; + products.sort(); + + for (let i = 0; i < m; i++) { + let cur = []; + for (let w of products) { + if (w.length <= i) continue; + + let flag = true; + for (let j = 0; j <= i; j++) { + if (w[j] !== searchWord[j]) { + flag = false; + break; + } + } + + if (flag) { + cur.push(w); + if (cur.length === 3) break; + } + } + + if (cur.length === 0) { + while (i < m) { + res.push([]); + i++; + } + break; + } + + res.push(cur); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + m * n)$ +* Space complexity: + * $O(n)$ or $O(1)$ space for the sorting algorithm. + * $O(m * w)$ space for the output array. + +> Where $n$ is the total number of characters in the string array $products$, $m$ is the length of the string $searchWord$, and $w$ is the average length of each word in the given string array. + +--- + +## 2. Sorting + Binary Search + +::tabs-start + +```python +class Solution: + def suggestedProducts(self, products: List[str], searchWord: str) -> List[List[str]]: + res = [] + m = len(searchWord) + products.sort() + + prefix = [] + start = 0 + + def binary_search(target, start): + l, r = start, len(products) + while l < r: + mid = l + (r - l) // 2 + if products[mid] >= target: + r = mid + else: + l = mid + 1 + return l + + for i in range(m): + prefix.append(searchWord[i]) + start = binary_search("".join(prefix), start) + + cur = [] + for j in range(start, min(start + 3, len(products))): + if products[j].startswith("".join(prefix)): + cur.append(products[j]) + else: + break + + res.append(cur) + + return res +``` + +```java +public class Solution { + public List> suggestedProducts(String[] products, String searchWord) { + List> res = new ArrayList<>(); + int m = searchWord.length(); + Arrays.sort(products); + + StringBuilder prefix = new StringBuilder(); + int start = 0; + + for (int i = 0; i < m; i++) { + prefix.append(searchWord.charAt(i)); + start = binarySearch(products, prefix.toString(), start); + + List cur = new ArrayList<>(); + for (int j = start; j < Math.min(start + 3, products.length); j++) { + if (products[j].startsWith(prefix.toString())) { + cur.add(products[j]); + } else { + break; + } + } + + res.add(cur); + } + + return res; + } + + private int binarySearch(String[] products, String target, int start) { + int l = start, r = products.length; + while (l < r) { + int mid = l + (r - l) / 2; + if (products[mid].compareTo(target) >= 0) { + r = mid; + } else { + l = mid + 1; + } + } + return l; + } +} +``` + +```cpp +class Solution { +public: + vector> suggestedProducts(vector& products, string searchWord) { + vector> res; + int m = searchWord.size(); + sort(products.begin(), products.end()); + + string prefix = ""; + int start = 0; + + for (int i = 0; i < m; i++) { + prefix += searchWord[i]; + start = binarySearch(products, prefix, start); + + vector cur; + for (int j = start; j < min(start + 3, (int)products.size()); j++) { + if (products[j].substr(0, prefix.size()) == prefix) { + cur.push_back(products[j]); + } else { + break; + } + } + + res.push_back(cur); + } + + return res; + } + +private: + int binarySearch(vector& products, string target, int start) { + int l = start, r = products.size(); + while (l < r) { + int mid = l + (r - l) / 2; + if (products[mid] >= target) { + r = mid; + } else { + l = mid + 1; + } + } + return l; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} products + * @param {string} searchWord + * @return {string[][]} + */ + suggestedProducts(products, searchWord) { + let res = []; + let m = searchWord.length; + products.sort(); + + let prefix = []; + let start = 0; + + for (let i = 0; i < m; i++) { + prefix.push(searchWord[i]); + start = this.binarySearch(products, prefix.join(""), start); + + let cur = []; + for (let j = start; j < Math.min(start + 3, products.length); j++) { + if (products[j].startsWith(prefix.join(""))) { + cur.push(products[j]); + } else { + break; + } + } + + res.push(cur); + } + + return res; + } + + /** + * @param {string[]} products + * @param {string} target + * @param {number} start + * @return {number} + */ + binarySearch(products, target, start) { + let l = start, r = products.length; + while (l < r) { + let mid = Math.floor(l + (r - l) / 2); + if (products[mid] >= target) { + r = mid; + } else { + l = mid + 1; + } + } + return l; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + m * w * \log N)$ +* Space complexity: + * $O(n)$ or $O(1)$ space for the sorting algorithm. + * $O(m * w)$ space for the output array. + +> Where $n$ is the total number of characters in the string array $products$, $N$ is the size of the array $products$, $m$ is the length of the string $searchWord$, and $w$ is the average length of each word in the given string array. + +--- + +## 3. Sorting + Binary Search (Built-In Function) + +::tabs-start + +```python +class Solution: + def suggestedProducts(self, products: List[str], searchWord: str) -> List[List[str]]: + res = [] + m = len(searchWord) + products.sort() + + prefix = "" + start = 0 + for i in range(m): + prefix += searchWord[i] + start = bisect_left(products, prefix, start) + + cur = [] + for j in range(start, min(start + 3, len(products))): + if products[j].startswith(prefix): + cur.append(products[j]) + else: + break + + res.append(cur) + + return res +``` + +```cpp +class Solution { +public: + vector> suggestedProducts(vector& products, string searchWord) { + vector> res; + int m = searchWord.size(); + sort(products.begin(), products.end()); + + string prefix = ""; + int start = 0; + for (int i = 0; i < m; i++) { + prefix += searchWord[i]; + start = lower_bound(products.begin() + start, products.end(), prefix) - products.begin(); + + vector cur; + for (int j = start; j < min(start + 3, (int)products.size()); j++) { + if (products[j].find(prefix) == 0) { + cur.push_back(products[j]); + } else { + break; + } + } + + res.push_back(cur); + } + + return res; + } +}; +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + m * w * \log N)$ +* Space complexity: + * $O(n)$ or $O(1)$ space for the sorting algorithm. + * $O(m * w)$ space for the output array. + +> Where $n$ is the total number of characters in the string array $products$, $N$ is the size of the array $products$, $m$ is the length of the string $searchWord$, and $w$ is the average length of each word in the given string array. + +--- + +## 4. Sorting + Two Pointers + +::tabs-start + +```python +class Solution: + def suggestedProducts(self, products: List[str], searchWord: str) -> List[List[str]]: + res = [] + products.sort() + + l, r = 0, len(products) - 1 + for i in range(len(searchWord)): + c = searchWord[i] + + while l <= r and (len(products[l]) <= i or products[l][i] != c): + l += 1 + while l <= r and (len(products[r]) <= i or products[r][i] != c): + r -= 1 + + res.append([]) + remain = r - l + 1 + for j in range(min(3, remain)): + res[-1].append(products[l + j]) + + return res +``` + +```java +public class Solution { + public List> suggestedProducts(String[] products, String searchWord) { + List> res = new ArrayList<>(); + Arrays.sort(products); + + int l = 0, r = products.length - 1; + for (int i = 0; i < searchWord.length(); i++) { + char c = searchWord.charAt(i); + + while (l <= r && (products[l].length() <= i || products[l].charAt(i) != c)) { + l++; + } + while (l <= r && (products[r].length() <= i || products[r].charAt(i) != c)) { + r--; + } + + List cur = new ArrayList<>(); + int remain = r - l + 1; + for (int j = 0; j < Math.min(3, remain); j++) { + cur.add(products[l + j]); + } + + res.add(cur); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> suggestedProducts(vector& products, string searchWord) { + vector> res; + sort(products.begin(), products.end()); + + int l = 0, r = products.size() - 1; + for (int i = 0; i < searchWord.size(); i++) { + char c = searchWord[i]; + + while (l <= r && (products[l].size() <= i || products[l][i] != c)) { + l++; + } + while (l <= r && (products[r].size() <= i || products[r][i] != c)) { + r--; + } + + vector cur; + int remain = r - l + 1; + for (int j = 0; j < min(3, remain); j++) { + cur.push_back(products[l + j]); + } + + res.push_back(cur); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} products + * @param {string} searchWord + * @return {string[][]} + */ + suggestedProducts(products, searchWord) { + let res = []; + products.sort(); + + let l = 0, r = products.length - 1; + for (let i = 0; i < searchWord.length; i++) { + let c = searchWord[i]; + + while (l <= r && (products[l].length <= i || products[l][i] !== c)) { + l++; + } + while (l <= r && (products[r].length <= i || products[r][i] !== c)) { + r--; + } + + let cur = []; + let remain = r - l + 1; + for (let j = 0; j < Math.min(3, remain); j++) { + cur.push(products[l + j]); + } + + res.push(cur); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + m * w + N)$ +* Space complexity: + * $O(n)$ or $O(1)$ space for the sorting algorithm. + * $O(m * w)$ space for the output array. + +> Where $n$ is the total number of characters in the string array $products$, $N$ is the size of the array $products$, $m$ is the length of the string $searchWord$, and $w$ is the average length of each word in the given string array. \ No newline at end of file diff --git a/articles/seat-reservation-manager.md b/articles/seat-reservation-manager.md new file mode 100644 index 000000000..c546e1be8 --- /dev/null +++ b/articles/seat-reservation-manager.md @@ -0,0 +1,407 @@ +## 1. Brute Force + +::tabs-start + +```python +class SeatManager: + + def __init__(self, n: int): + self.seats = [False] * n + + def reserve(self) -> int: + for i in range(len(self.seats)): + if not self.seats[i]: + self.seats[i] = True + return i + 1 + + def unreserve(self, seatNumber: int) -> None: + self.seats[seatNumber - 1] = False +``` + +```java +public class SeatManager { + private boolean[] seats; + + public SeatManager(int n) { + seats = new boolean[n]; + } + + public int reserve() { + for (int i = 0; i < seats.length; i++) { + if (!seats[i]) { + seats[i] = true; + return i + 1; + } + } + return -1; + } + + public void unreserve(int seatNumber) { + seats[seatNumber - 1] = false; + } +} +``` + +```cpp +class SeatManager { +private: + vector seats; + +public: + SeatManager(int n) : seats(n, false) {} + + int reserve() { + for (int i = 0; i < seats.size(); i++) { + if (!seats[i]) { + seats[i] = true; + return i + 1; + } + } + return -1; + } + + void unreserve(int seatNumber) { + seats[seatNumber - 1] = false; + } +}; +``` + +```javascript +class SeatManager { + /** + * @param {number} n + */ + constructor(n) { + this.seats = new Array(n).fill(false); + } + + /** + * @return {number} + */ + reserve() { + for (let i = 0; i < this.seats.length; i++) { + if (!this.seats[i]) { + this.seats[i] = true; + return i + 1; + } + } + } + + /** + * @param {number} seatNumber + * @return {void} + */ + unreserve(seatNumber) { + this.seats[seatNumber - 1] = false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n)$ time for initialization. + * $O(n)$ time for each $reserve()$ function call. + * $O(1)$ time for each $unreserve()$ function call. +* Space complexity: $O(n)$ + +--- + +## 2. Min-Heap + +::tabs-start + +```python +class SeatManager: + def __init__(self, n: int): + self.unres = list(range(1, n + 1)) + heapq.heapify(self.unres) + + def reserve(self) -> int: + return heapq.heappop(self.unres) + + def unreserve(self, seatNumber: int) -> None: + heapq.heappush(self.unres, seatNumber) +``` + +```java +public class SeatManager { + private PriorityQueue unres; + + public SeatManager(int n) { + unres = new PriorityQueue<>(); + for (int i = 1; i <= n; i++) { + unres.offer(i); + } + } + + public int reserve() { + return unres.poll(); + } + + public void unreserve(int seatNumber) { + unres.offer(seatNumber); + } +} +``` + +```cpp +class SeatManager { +private: + priority_queue, greater> unres; + +public: + SeatManager(int n) { + for (int i = 1; i <= n; i++) { + unres.push(i); + } + } + + int reserve() { + int seat = unres.top(); + unres.pop(); + return seat; + } + + void unreserve(int seatNumber) { + unres.push(seatNumber); + } +}; +``` + +```javascript +class SeatManager { + /** + * @param {number} n + */ + constructor(n) { + this.unres = new MinPriorityQueue(); + for (let i = 1; i <= n; i++) { + this.unres.enqueue(i); + } + } + + /** + * @return {number} + */ + reserve() { + return this.unres.dequeue(); + } + + /** + * @param {number} seatNumber + * @return {void} + */ + unreserve(seatNumber) { + this.unres.enqueue(seatNumber); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(n \log n)$ time for initialization. + * $O(\log n)$ time for each $reserve()$ function call. + * $O(\log n)$ time for each $unreserve()$ function call. +* Space complexity: $O(n)$ + +--- + +## 3. Min-Heap (Optimal) + +::tabs-start + +```python +class SeatManager: + def __init__(self, n: int): + self.minHeap = [] + self.nextSeat = 1 + + def reserve(self) -> int: + if self.minHeap: + return heapq.heappop(self.minHeap) + + seat = self.nextSeat + self.nextSeat += 1 + return seat + + def unreserve(self, seatNumber: int) -> None: + heapq.heappush(self.minHeap, seatNumber) +``` + +```java +public class SeatManager { + private PriorityQueue minHeap; + private int nextSeat; + + public SeatManager(int n) { + minHeap = new PriorityQueue<>(); + nextSeat = 1; + } + + public int reserve() { + if (!minHeap.isEmpty()) { + return minHeap.poll(); + } + return nextSeat++; + } + + public void unreserve(int seatNumber) { + minHeap.offer(seatNumber); + } +} +``` + +```cpp +class SeatManager { +private: + priority_queue, greater> minHeap; + int nextSeat; + +public: + SeatManager(int n) { + nextSeat = 1; + } + + int reserve() { + if (!minHeap.empty()) { + int seat = minHeap.top(); + minHeap.pop(); + return seat; + } + return nextSeat++; + } + + void unreserve(int seatNumber) { + minHeap.push(seatNumber); + } +}; +``` + +```javascript +class SeatManager { + /** + * @param {number} n + */ + constructor(n) { + this.minHeap = new MinPriorityQueue(); + this.nextSeat = 1; + } + + /** + * @return {number} + */ + reserve() { + if (!this.minHeap.isEmpty()) { + return this.minHeap.dequeue(); + } + return this.nextSeat++; + } + + /** + * @param {number} seatNumber + * @return {void} + */ + unreserve(seatNumber) { + this.minHeap.enqueue(seatNumber); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(\log n)$ time for each $reserve()$ function call. + * $O(\log n)$ time for each $unreserve()$ function call. +* Space complexity: $O(n)$ + +--- + +## 4. Ordered Set + +::tabs-start + +```python +class SeatManager: + def __init__(self, n: int): + self.available = SortedSet() + self.nextSeat = 1 + + def reserve(self) -> int: + if self.available: + return self.available.pop(0) + + seat = self.nextSeat + self.nextSeat += 1 + return seat + + def unreserve(self, seatNumber: int) -> None: + self.available.add(seatNumber) +``` + +```java +public class SeatManager { + private TreeSet available; + private int nextSeat; + + public SeatManager(int n) { + available = new TreeSet<>(); + nextSeat = 1; + } + + public int reserve() { + if (!available.isEmpty()) { + return available.pollFirst(); + } + return nextSeat++; + } + + public void unreserve(int seatNumber) { + available.add(seatNumber); + } +} +``` + +```cpp +class SeatManager { +private: + set available; + int nextSeat; + +public: + SeatManager(int n) { + nextSeat = 1; + } + + int reserve() { + if (!available.empty()) { + int seat = *available.begin(); + available.erase(available.begin()); + return seat; + } + return nextSeat++; + } + + void unreserve(int seatNumber) { + available.insert(seatNumber); + } +}; +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: + * $O(1)$ time for initialization. + * $O(\log n)$ time for each $reserve()$ function call. + * $O(\log n)$ time for each $unreserve()$ function call. +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/sequential-digits.md b/articles/sequential-digits.md new file mode 100644 index 000000000..b1f6bb8b8 --- /dev/null +++ b/articles/sequential-digits.md @@ -0,0 +1,579 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def sequentialDigits(self, low: int, high: int) -> List[int]: + res = [] + + for num in range(low, high + 1): + s = str(num) + flag = True + for i in range(1, len(s)): + if ord(s[i]) - ord(s[i - 1]) != 1: + flag = False + break + if flag: + res.append(num) + + return res +``` + +```java +public class Solution { + public List sequentialDigits(int low, int high) { + List res = new ArrayList<>(); + for (int num = low; num <= high; num++) { + String s = String.valueOf(num); + boolean flag = true; + for (int i = 1; i < s.length(); i++) { + if (s.charAt(i) - s.charAt(i - 1) != 1) { + flag = false; + break; + } + } + if (flag) { + res.add(num); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector sequentialDigits(int low, int high) { + vector res; + for (int num = low; num <= high; num++) { + string s = to_string(num); + bool flag = true; + for (int i = 1; i < s.size(); i++) { + if (s[i] - s[i - 1] != 1) { + flag = false; + break; + } + } + if (flag) { + res.push_back(num); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} low + * @param {number} high + * @return {number[]} + */ + sequentialDigits(low, high) { + const res = []; + for (let num = low; num <= high; num++) { + const s = num.toString(); + let flag = true; + for (let i = 1; i < s.length; i++) { + if (s.charCodeAt(i) - s.charCodeAt(i - 1) !== 1) { + flag = false; + break; + } + } + if (flag) { + res.push(num); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Simulation + +::tabs-start + +```python +class Solution: + def sequentialDigits(self, low: int, high: int) -> List[int]: + res = [] + low_digit, high_digit = len(str(low)), len(str(high)) + + for digits in range(low_digit, high_digit + 1): + for start in range(1, 10): + if start + digits > 10: + break + num = start + prev = start + for i in range(digits - 1): + num = num * 10 + prev += 1 + num += prev + if low <= num <= high: + res.append(num) + return res +``` + +```java +public class Solution { + public List sequentialDigits(int low, int high) { + List res = new ArrayList<>(); + int lowDigit = String.valueOf(low).length(); + int highDigit = String.valueOf(high).length(); + + for (int digits = lowDigit; digits <= highDigit; digits++) { + for (int start = 1; start < 10; start++) { + if (start + digits > 10) { + break; + } + int num = start; + int prev = start; + for (int i = 1; i < digits; i++) { + num = num * 10 + (++prev); + } + if (num >= low && num <= high) { + res.add(num); + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector sequentialDigits(int low, int high) { + vector res; + int lowDigit = to_string(low).length(); + int highDigit = to_string(high).length(); + + for (int digits = lowDigit; digits <= highDigit; digits++) { + for (int start = 1; start < 10; start++) { + if (start + digits > 10) { + break; + } + int num = start; + int prev = start; + for (int i = 1; i < digits; i++) { + num = num * 10 + (++prev); + } + if (num >= low && num <= high) { + res.push_back(num); + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} low + * @param {number} high + * @return {number[]} + */ + sequentialDigits(low, high) { + const res = []; + const lowDigit = low.toString().length; + const highDigit = high.toString().length; + + for (let digits = lowDigit; digits <= highDigit; digits++) { + for (let start = 1; start < 10; start++) { + if (start + digits > 10) { + break; + } + let num = start; + let prev = start; + for (let i = 1; i < digits; i++) { + num = num * 10 + (++prev); + } + if (num >= low && num <= high) { + res.push(num); + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +> Since, we have at most $36$ valid numbers as per the given constraints. + +--- + +## 3. Breadth First Search + +::tabs-start + +```python +class Solution: + def sequentialDigits(self, low: int, high: int) -> List[int]: + res = [] + queue = deque(range(1, 10)) + + while queue: + n = queue.popleft() + if n > high: + continue + if low <= n <= high: + res.append(n) + ones = n % 10 + if ones < 9: + queue.append(n * 10 + (ones + 1)) + + return res +``` + +```java +public class Solution { + public List sequentialDigits(int low, int high) { + List res = new ArrayList<>(); + Queue queue = new LinkedList<>(); + + for (int i = 1; i < 10; i++) { + queue.add(i); + } + + while (!queue.isEmpty()) { + int n = queue.poll(); + if (n > high) { + continue; + } + if (n >= low && n <= high) { + res.add(n); + } + int ones = n % 10; + if (ones < 9) { + queue.add(n * 10 + (ones + 1)); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector sequentialDigits(int low, int high) { + vector res; + queue queue; + + for (int i = 1; i < 10; i++) { + queue.push(i); + } + + while (!queue.empty()) { + int n = queue.front(); + queue.pop(); + + if (n > high) { + continue; + } + if (n >= low && n <= high) { + res.push_back(n); + } + int ones = n % 10; + if (ones < 9) { + queue.push(n * 10 + (ones + 1)); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} low + * @param {number} high + * @return {number[]} + */ + sequentialDigits(low, high) { + const res = []; + const queue = new Queue(); + for (let i = 1; i < 9; i++) { + queue.push(i); + } + + while (!queue.isEmpty()) { + const n = queue.pop(); + if (n > high) { + continue; + } + if (n >= low && n <= high) { + res.push(n); + } + const ones = n % 10; + if (ones < 9) { + queue.push(n * 10 + (ones + 1)); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +> Since, we have at most $36$ valid numbers as per the given constraints. + +--- + +## 4. Depth First Search + +::tabs-start + +```python +class Solution: + def sequentialDigits(self, low: int, high: int) -> List[int]: + res = [] + + def dfs(num): + if num > high: + return + if low <= num <= high: + res.append(num) + last_digit = num % 10 + if last_digit < 9: + dfs(num * 10 + (last_digit + 1)) + + for i in range(1, 10): + dfs(i) + + return sorted(res) +``` + +```java +public class Solution { + public List sequentialDigits(int low, int high) { + List res = new ArrayList<>(); + + for (int i = 1; i < 10; i++) { + dfs(i, low, high, res); + } + + Collections.sort(res); + return res; + } + + private void dfs(int num, int low, int high, List res) { + if (num > high) { + return; + } + if (num >= low) { + res.add(num); + } + int lastDigit = num % 10; + if (lastDigit < 9) { + dfs(num * 10 + (lastDigit + 1), low, high, res); + } + } +} +``` + +```cpp +class Solution { +public: + vector sequentialDigits(int low, int high) { + vector res; + for (int i = 1; i < 10; i++) { + dfs(i, low, high, res); + } + sort(res.begin(), res.end()); + return res; + } + +private: + void dfs(int num, int low, int high, vector& res) { + if (num > high) { + return; + } + if (num >= low) { + res.push_back(num); + } + int lastDigit = num % 10; + if (lastDigit < 9) { + dfs(num * 10 + (lastDigit + 1), low, high, res); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} low + * @param {number} high + * @return {number[]} + */ + sequentialDigits(low, high) { + const res = []; + + const dfs = (num) => { + if (num > high) { + return; + } + if (num >= low) { + res.push(num); + } + const lastDigit = num % 10; + if (lastDigit < 9) { + dfs(num * 10 + (lastDigit + 1)); + } + }; + + for (let i = 1; i < 10; i++) { + dfs(i); + } + + return res.sort((a, b) => a - b); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +> Since, we have at most $36$ valid numbers as per the given constraints. + +--- + +## 5. Sliding Window + +::tabs-start + +```python +class Solution: + def sequentialDigits(self, low: int, high: int) -> List[int]: + nums = "123456789" + res = [] + for d in range(2, 10): + for i in range(9 - d + 1): + num = int(nums[i: i + d]) + if num > high: + break + if low <= num <= high: + res.append(num) + return res +``` + +```java +public class Solution { + public List sequentialDigits(int low, int high) { + String nums = "123456789"; + List res = new ArrayList<>(); + + for (int d = 2; d <= 9; d++) { + for (int i = 0; i <= 9 - d; i++) { + int num = Integer.parseInt(nums.substring(i, i + d)); + if (num > high) { + break; + } + if (num >= low && num <= high) { + res.add(num); + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector sequentialDigits(int low, int high) { + string nums = "123456789"; + vector res; + + for (int d = 2; d <= 9; d++) { + for (int i = 0; i <= 9 - d; i++) { + int num = stoi(nums.substr(i, d)); + if (num > high) { + break; + } + if (num >= low && num <= high) { + res.push_back(num); + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} low + * @param {number} high + * @return {number[]} + */ + sequentialDigits(low, high) { + const nums = "123456789"; + const res = []; + + for (let d = 2; d <= 9; d++) { + for (let i = 0; i <= 9 - d; i++) { + const num = parseInt(nums.substring(i, i + d)); + if (num > high) { + break; + } + if (num >= low && num <= high) { + res.push(num); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +> Since, we have at most $36$ valid numbers as per the given constraints. \ No newline at end of file diff --git a/articles/serialize-and-deserialize-binary-tree.md b/articles/serialize-and-deserialize-binary-tree.md new file mode 100644 index 000000000..ad965e002 --- /dev/null +++ b/articles/serialize-and-deserialize-binary-tree.md @@ -0,0 +1,996 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Codec: + + # Encodes a tree to a single string. + def serialize(self, root: Optional[TreeNode]) -> str: + res = [] + + def dfs(node): + if not node: + res.append("N") + return + res.append(str(node.val)) + dfs(node.left) + dfs(node.right) + + dfs(root) + return ",".join(res) + + # Decodes your encoded data to tree. + def deserialize(self, data: str) -> Optional[TreeNode]: + vals = data.split(",") + self.i = 0 + + def dfs(): + if vals[self.i] == "N": + self.i += 1 + return None + node = TreeNode(int(vals[self.i])) + self.i += 1 + node.left = dfs() + node.right = dfs() + return node + + return dfs() +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Codec { + + // Encodes a tree to a single string. + public String serialize(TreeNode root) { + List res = new ArrayList<>(); + dfsSerialize(root, res); + return String.join(",", res); + } + + private void dfsSerialize(TreeNode node, List res) { + if (node == null) { + res.add("N"); + return; + } + res.add(String.valueOf(node.val)); + dfsSerialize(node.left, res); + dfsSerialize(node.right, res); + } + + // Decodes your encoded data to tree. + public TreeNode deserialize(String data) { + String[] vals = data.split(","); + int[] i = {0}; + return dfsDeserialize(vals, i); + } + + private TreeNode dfsDeserialize(String[] vals, int[] i) { + if (vals[i[0]].equals("N")) { + i[0]++; + return null; + } + TreeNode node = new TreeNode(Integer.parseInt(vals[i[0]])); + i[0]++; + node.left = dfsDeserialize(vals, i); + node.right = dfsDeserialize(vals, i); + return node; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Codec { +public: + // Encodes a tree to a single string. + string serialize(TreeNode* root) { + vector res; + dfsSerialize(root, res); + return join(res, ","); + } + + // Decodes your encoded data to tree. + TreeNode* deserialize(string data) { + vector vals = split(data, ','); + int i = 0; + return dfsDeserialize(vals, i); + } + +private: + void dfsSerialize(TreeNode* node, vector& res) { + if (!node) { + res.push_back("N"); + return; + } + res.push_back(to_string(node->val)); + dfsSerialize(node->left, res); + dfsSerialize(node->right, res); + } + + TreeNode* dfsDeserialize(vector& vals, int& i) { + if (vals[i] == "N") { + i++; + return NULL; + } + TreeNode* node = new TreeNode(stoi(vals[i])); + i++; + node->left = dfsDeserialize(vals, i); + node->right = dfsDeserialize(vals, i); + return node; + } + + vector split(const string &s, char delim) { + vector elems; + stringstream ss(s); + string item; + while (getline(ss, item, delim)) { + elems.push_back(item); + } + return elems; + } + + string join(const vector &v, const string &delim) { + ostringstream s; + for (const auto &i : v) { + if (&i != &v[0]) + s << delim; + s << i; + } + return s.str(); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Codec { + /** + * Encodes a tree to a single string. + * + * @param {TreeNode} root + * @return {string} + */ + serialize(root) { + const res = []; + this.dfsSerialize(root, res); + return res.join(','); + } + + dfsSerialize(node, res) { + if (node === null) { + res.push('N'); + return; + } + res.push(node.val.toString()); + this.dfsSerialize(node.left, res); + this.dfsSerialize(node.right, res); + } + + /** + * Decodes your encoded data to tree. + * + * @param {string} data + * @return {TreeNode} + */ + deserialize(data) { + const vals = data.split(','); + const i = { val: 0 }; + return this.dfsDeserialize(vals, i); + } + + dfsDeserialize(vals, i) { + if (vals[i.val] === 'N') { + i.val++; + return null; + } + const node = new TreeNode(parseInt(vals[i.val])); + i.val++; + node.left = this.dfsDeserialize(vals, i); + node.right = this.dfsDeserialize(vals, i); + return node; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Codec { + + // Encodes a tree to a single string. + public string Serialize(TreeNode root) { + List res = new List(); + dfsSerialize(root, res); + return String.Join(",", res); + } + + private void dfsSerialize(TreeNode node, List res) { + if (node == null) { + res.Add("N"); + return; + } + res.Add(node.val.ToString()); + dfsSerialize(node.left, res); + dfsSerialize(node.right, res); + } + + // Decodes your encoded data to tree. + public TreeNode Deserialize(string data) { + string[] vals = data.Split(','); + int i = 0; + return dfsDeserialize(vals, ref i); + } + + private TreeNode dfsDeserialize(string[] vals, ref int i) { + if (vals[i] == "N") { + i++; + return null; + } + TreeNode node = new TreeNode(Int32.Parse(vals[i])); + i++; + node.left = dfsDeserialize(vals, ref i); + node.right = dfsDeserialize(vals, ref i); + return node; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ + +type Codec struct{} + +func Constructor() Codec { + return Codec{} +} + +// Encodes a tree to a single string. +func (this *Codec) serialize(root *TreeNode) string { + var res []string + + var dfs func(node *TreeNode) + dfs = func(node *TreeNode) { + if node == nil { + res = append(res, "N") + return + } + res = append(res, strconv.Itoa(node.Val)) + dfs(node.Left) + dfs(node.Right) + } + + dfs(root) + return strings.Join(res, ",") +} + +// Decodes your encoded data to tree. +func (this *Codec) deserialize(data string) *TreeNode { + vals := strings.Split(data, ",") + i := 0 + + var dfs func() *TreeNode + dfs = func() *TreeNode { + if vals[i] == "N" { + i++ + return nil + } + val, _ := strconv.Atoi(vals[i]) + node := &TreeNode{Val: val} + i++ + node.Left = dfs() + node.Right = dfs() + return node + } + + return dfs() +} +``` + +```kotlin +/** + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ + +class Codec { + + // Encodes a tree to a single string. + fun serialize(root: TreeNode?): String { + val res = mutableListOf() + + fun dfs(node: TreeNode?) { + if (node == null) { + res.add("N") + return + } + res.add(node.`val`.toString()) + dfs(node.left) + dfs(node.right) + } + + dfs(root) + return res.joinToString(",") + } + + // Decodes your encoded data to tree. + fun deserialize(data: String): TreeNode? { + val vals = data.split(",") + var i = 0 + + fun dfs(): TreeNode? { + if (vals[i] == "N") { + i++ + return null + } + val node = TreeNode(vals[i].toInt()) + i++ + node.left = dfs() + node.right = dfs() + return node + } + + return dfs() + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init(_ val: Int) { + * self.val = val + * self.left = nil + * self.right = nil + * } + * } + */ + +class Codec { + + func serialize(_ root: TreeNode?) -> String { + var res = [String]() + + func dfs(_ node: TreeNode?) { + guard let node = node else { + res.append("N") + return + } + res.append("\(node.val)") + dfs(node.left) + dfs(node.right) + } + + dfs(root) + return res.joined(separator: ",") + } + + func deserialize(_ data: String) -> TreeNode? { + var vals = data.split(separator: ",").map { String($0) } + var i = 0 + + func dfs() -> TreeNode? { + if vals[i] == "N" { + i += 1 + return nil + } + let node = TreeNode(Int(vals[i])!) + i += 1 + node.left = dfs() + node.right = dfs() + return node + } + + return dfs() + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Codec: + + # Encodes a tree to a single string. + def serialize(self, root: Optional[TreeNode]) -> str: + if not root: + return "N" + res = [] + queue = deque([root]) + while queue: + node = queue.popleft() + if not node: + res.append("N") + else: + res.append(str(node.val)) + queue.append(node.left) + queue.append(node.right) + return ",".join(res) + + # Decodes your encoded data to tree. + def deserialize(self, data: str) -> Optional[TreeNode]: + vals = data.split(",") + if vals[0] == "N": + return None + root = TreeNode(int(vals[0])) + queue = deque([root]) + index = 1 + while queue: + node = queue.popleft() + if vals[index] != "N": + node.left = TreeNode(int(vals[index])) + queue.append(node.left) + index += 1 + if vals[index] != "N": + node.right = TreeNode(int(vals[index])) + queue.append(node.right) + index += 1 + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Codec { + + // Encodes a tree to a single string. + public String serialize(TreeNode root) { + if (root == null) return "N"; + StringBuilder res = new StringBuilder(); + Queue queue = new LinkedList<>(); + queue.add(root); + + while (!queue.isEmpty()) { + TreeNode node = queue.poll(); + if (node == null) { + res.append("N,"); + } else { + res.append(node.val).append(","); + queue.add(node.left); + queue.add(node.right); + } + } + return res.toString(); + } + + // Decodes your encoded data to tree. + public TreeNode deserialize(String data) { + String[] vals = data.split(","); + if (vals[0].equals("N")) return null; + TreeNode root = new TreeNode(Integer.parseInt(vals[0])); + Queue queue = new LinkedList<>(); + queue.add(root); + int index = 1; + + while (!queue.isEmpty()) { + TreeNode node = queue.poll(); + if (!vals[index].equals("N")) { + node.left = new TreeNode(Integer.parseInt(vals[index])); + queue.add(node.left); + } + index++; + if (!vals[index].equals("N")) { + node.right = new TreeNode(Integer.parseInt(vals[index])); + queue.add(node.right); + } + index++; + } + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Codec { +public: + + // Encodes a tree to a single string. + string serialize(TreeNode* root) { + if (!root) return "N"; + string res; + queue queue; + queue.push(root); + + while (!queue.empty()) { + TreeNode* node = queue.front(); + queue.pop(); + if (!node) { + res += "N,"; + } else { + res += to_string(node->val) + ","; + queue.push(node->left); + queue.push(node->right); + } + } + return res; + } + + // Decodes your encoded data to tree. + TreeNode* deserialize(string data) { + stringstream ss(data); + string val; + getline(ss, val, ','); + if (val == "N") return nullptr; + TreeNode* root = new TreeNode(stoi(val)); + queue queue; + queue.push(root); + + while (getline(ss, val, ',')) { + TreeNode* node = queue.front(); + queue.pop(); + if (val != "N") { + node->left = new TreeNode(stoi(val)); + queue.push(node->left); + } + getline(ss, val, ','); + if (val != "N") { + node->right = new TreeNode(stoi(val)); + queue.push(node->right); + } + } + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Codec { + /** + * Encodes a tree to a single string. + * + * @param {TreeNode} root + * @return {string} + */ + serialize(root) { + if (!root) return "N"; + const res = []; + const queue = new Queue(); + queue.push(root); + + while (!queue.isEmpty()) { + const node = queue.pop(); + if (!node) { + res.push("N"); + } else { + res.push(node.val); + queue.push(node.left); + queue.push(node.right); + } + } + return res.join(","); + } + + /** + * Decodes your encoded data to tree. + * + * @param {string} data + * @return {TreeNode} + */ + deserialize(data) { + const vals = data.split(","); + if (vals[0] === "N") return null; + const root = new TreeNode(parseInt(vals[0])); + const queue = new Queue([root]); + let index = 1; + + while (!queue.isEmpty()) { + const node = queue.pop(); + if (vals[index] !== "N") { + node.left = new TreeNode(parseInt(vals[index])); + queue.push(node.left); + } + index++; + if (vals[index] !== "N") { + node.right = new TreeNode(parseInt(vals[index])); + queue.push(node.right); + } + index++; + } + return root; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Codec { + + // Encodes a tree to a single string. + public string Serialize(TreeNode root) { + if (root == null) return "N"; + var res = new List(); + var queue = new Queue(); + queue.Enqueue(root); + + while (queue.Count > 0) { + var node = queue.Dequeue(); + if (node == null) { + res.Add("N"); + } else { + res.Add(node.val.ToString()); + queue.Enqueue(node.left); + queue.Enqueue(node.right); + } + } + return string.Join(",", res); + } + + // Decodes your encoded data to tree. + public TreeNode Deserialize(string data) { + var vals = data.Split(','); + if (vals[0] == "N") return null; + var root = new TreeNode(int.Parse(vals[0])); + var queue = new Queue(); + queue.Enqueue(root); + int index = 1; + + while (queue.Count > 0) { + var node = queue.Dequeue(); + if (vals[index] != "N") { + node.left = new TreeNode(int.Parse(vals[index])); + queue.Enqueue(node.left); + } + index++; + if (vals[index] != "N") { + node.right = new TreeNode(int.Parse(vals[index])); + queue.Enqueue(node.right); + } + index++; + } + return root; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ + +type Codec struct{} + +func Constructor() Codec { + return Codec{} +} + +// Encodes a tree to a single string. +func (this *Codec) serialize(root *TreeNode) string { + if root == nil { + return "N" + } + var res []string + queue := []*TreeNode{root} + + for len(queue) > 0 { + node := queue[0] + queue = queue[1:] + + if node == nil { + res = append(res, "N") + } else { + res = append(res, strconv.Itoa(node.Val)) + queue = append(queue, node.Left) + queue = append(queue, node.Right) + } + } + + return strings.Join(res, ",") +} + +// Decodes your encoded data to tree. +func (this *Codec) deserialize(data string) *TreeNode { + vals := strings.Split(data, ",") + if vals[0] == "N" { + return nil + } + + rootVal, _ := strconv.Atoi(vals[0]) + root := &TreeNode{Val: rootVal} + queue := []*TreeNode{root} + index := 1 + + for len(queue) > 0 && index < len(vals) { + node := queue[0] + queue = queue[1:] + + if vals[index] != "N" { + leftVal, _ := strconv.Atoi(vals[index]) + node.Left = &TreeNode{Val: leftVal} + queue = append(queue, node.Left) + } + index++ + + if index < len(vals) && vals[index] != "N" { + rightVal, _ := strconv.Atoi(vals[index]) + node.Right = &TreeNode{Val: rightVal} + queue = append(queue, node.Right) + } + index++ + } + + return root +} +``` + +```kotlin +/** + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ + +class Codec { + + // Encodes a tree to a single string. + fun serialize(root: TreeNode?): String { + if (root == null) return "N" + val res = mutableListOf() + val queue: Queue = LinkedList() + queue.add(root) + + while (queue.isNotEmpty()) { + val node = queue.poll() + if (node == null) { + res.add("N") + } else { + res.add(node.`val`.toString()) + queue.add(node.left) + queue.add(node.right) + } + } + + return res.joinToString(",") + } + + // Decodes your encoded data to tree. + fun deserialize(data: String): TreeNode? { + val vals = data.split(",") + if (vals[0] == "N") return null + + val root = TreeNode(vals[0].toInt()) + val queue: Queue = LinkedList() + queue.add(root) + var index = 1 + + while (queue.isNotEmpty() && index < vals.size) { + val node = queue.poll() + + if (vals[index] != "N") { + node.left = TreeNode(vals[index].toInt()) + queue.add(node.left!!) + } + index++ + + if (index < vals.size && vals[index] != "N") { + node.right = TreeNode(vals[index].toInt()) + queue.add(node.right!!) + } + index++ + } + + return root + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init(_ val: Int) { + * self.val = val + * self.left = nil + * self.right = nil + * } + * } + */ + +class Codec { + + func serialize(_ root: TreeNode?) -> String { + guard let root = root else { + return "N" + } + + var res = [String]() + var queue: Deque = [root] + + while !queue.isEmpty { + let node = queue.removeFirst() + if let node = node { + res.append("\(node.val)") + queue.append(node.left) + queue.append(node.right) + } else { + res.append("N") + } + } + + return res.joined(separator: ",") + } + + func deserialize(_ data: String) -> TreeNode? { + let vals = data.split(separator: ",").map { String($0) } + guard vals[0] != "N" else { + return nil + } + + let root = TreeNode(Int(vals[0])!) + var queue: Deque = [root] + var index = 1 + + while !queue.isEmpty { + let node = queue.popFirst()! + + if vals[index] != "N" { + node.left = TreeNode(Int(vals[index])!) + queue.append(node.left!) + } + index += 1 + + if vals[index] != "N" { + node.right = TreeNode(Int(vals[index])!) + queue.append(node.right!) + } + index += 1 + } + + return root + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/set-mismatch.md b/articles/set-mismatch.md new file mode 100644 index 000000000..f29e54444 --- /dev/null +++ b/articles/set-mismatch.md @@ -0,0 +1,721 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def findErrorNums(self, nums: List[int]) -> List[int]: + res = [0, 0] + n = len(nums) + + for i in range(1, n + 1): + cnt = 0 + for num in nums: + if num == i: + cnt += 1 + + if cnt == 0: + res[1] = i + elif cnt == 2: + res[0] = i + + return res +``` + +```java +public class Solution { + public int[] findErrorNums(int[] nums) { + int[] res = new int[2]; + int n = nums.length; + + for (int i = 1; i <= n; i++) { + int cnt = 0; + for (int num : nums) { + if (num == i) { + cnt++; + } + } + + if (cnt == 0) { + res[1] = i; + } else if (cnt == 2) { + res[0] = i; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findErrorNums(vector& nums) { + vector res(2, 0); + int n = nums.size(); + + for (int i = 1; i <= n; i++) { + int cnt = 0; + for (int num : nums) { + if (num == i) { + cnt++; + } + } + + if (cnt == 0) { + res[1] = i; + } else if (cnt == 2) { + res[0] = i; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findErrorNums(nums) { + const res = [0, 0]; + const n = nums.length; + + for (let i = 1; i <= n; i++) { + let cnt = 0; + for (const num of nums) { + if (num === i) { + cnt++; + } + } + + if (cnt === 0) { + res[1] = i; + } else if (cnt === 2) { + res[0] = i; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def findErrorNums(self, nums: List[int]) -> List[int]: + res = [0, 1] + nums.sort() + + for i in range(1, len(nums)): + if nums[i] == nums[i - 1]: + res[0] = nums[i] + elif nums[i] - nums[i - 1] == 2: + res[1] = nums[i] - 1 + + if nums[-1] != len(nums): + res[1] = len(nums) + return res +``` + +```java +public class Solution { + public int[] findErrorNums(int[] nums) { + int[] res = {0, 1}; + Arrays.sort(nums); + + for (int i = 1; i < nums.length; i++) { + if (nums[i] == nums[i - 1]) { + res[0] = nums[i]; + } else if (nums[i] - nums[i - 1] == 2) { + res[1] = nums[i] - 1; + } + } + + if (nums[nums.length - 1] != nums.length) { + res[1] = nums.length; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findErrorNums(vector& nums) { + vector res = {0, 1}; + sort(nums.begin(), nums.end()); + + for (int i = 1; i < nums.size(); i++) { + if (nums[i] == nums[i - 1]) { + res[0] = nums[i]; + } else if (nums[i] - nums[i - 1] == 2) { + res[1] = nums[i] - 1; + } + } + + if (nums.back() != nums.size()) { + res[1] = nums.size(); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findErrorNums(nums) { + const res = [0, 1]; + nums.sort((a, b) => a - b); + + for (let i = 1; i < nums.length; i++) { + if (nums[i] === nums[i - 1]) { + res[0] = nums[i]; + } else if (nums[i] - nums[i - 1] === 2) { + res[1] = nums[i] - 1; + } + } + + if (nums[nums.length - 1] !== nums.length) { + res[1] = nums.length; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Frequency Count (Hash Table) + +::tabs-start + +```python +class Solution: + def findErrorNums(self, nums: List[int]) -> List[int]: + res = [0, 0] # [duplicate, missing] + count = Counter(nums) + + for i in range(1, len(nums) + 1): + if count[i] == 0: + res[1] = i + if count[i] == 2: + res[0] = i + + return res +``` + +```java +public class Solution { + public int[] findErrorNums(int[] nums) { + int n = nums.length; + int[] count = new int[n + 1]; + int[] res = new int[2]; + + for (int num : nums) { + count[num]++; + } + + for (int i = 1; i <= n; i++) { + if (count[i] == 0) { + res[1] = i; + } + if (count[i] == 2) { + res[0] = i; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findErrorNums(vector& nums) { + int n = nums.size(); + vector count(n + 1, 0); + vector res(2, 0); + + for (int num : nums) { + count[num]++; + } + + for (int i = 1; i <= n; i++) { + if (count[i] == 0) { + res[1] = i; + } + if (count[i] == 2) { + res[0] = i; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findErrorNums(nums) { + const n = nums.length; + const count = Array(n + 1).fill(0); + const res = [0, 0]; + + for (const num of nums) { + count[num]++; + } + + for (let i = 1; i <= n; i++) { + if (count[i] === 0) { + res[1] = i; + } + if (count[i] === 2) { + res[0] = i; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Negative Marking + +::tabs-start + +```python +class Solution: + def findErrorNums(self, nums: List[int]) -> List[int]: + res = [0, 0] + + for num in nums: + num = abs(num) + nums[num - 1] *= -1 + if nums[num - 1] > 0: + res[0] = num + + for i, num in enumerate(nums): + if num > 0 and i + 1 != res[0]: + res[1] = i + 1 + return res +``` + +```java +public class Solution { + public int[] findErrorNums(int[] nums) { + int[] res = new int[2]; + + for (int num : nums) { + int absNum = Math.abs(num); + if (nums[absNum - 1] < 0) { + res[0] = absNum; + } else { + nums[absNum - 1] *= -1; + } + } + + for (int i = 0; i < nums.length; i++) { + if (nums[i] > 0 && i + 1 != res[0]) { + res[1] = i + 1; + return res; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector findErrorNums(vector& nums) { + vector res(2); + + for (int num : nums) { + int absNum = abs(num); + if (nums[absNum - 1] < 0) { + res[0] = absNum; + } else { + nums[absNum - 1] *= -1; + } + } + + for (int i = 0; i < nums.size(); i++) { + if (nums[i] > 0 && i + 1 != res[0]) { + res[1] = i + 1; + return res; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findErrorNums(nums) { + const res = [0, 0]; + + for (let num of nums) { + const absNum = Math.abs(num); + if (nums[absNum - 1] < 0) { + res[0] = absNum; + } else { + nums[absNum - 1] *= -1; + } + } + + for (let i = 0; i < nums.length; i++) { + if (nums[i] > 0 && i + 1 !== res[0]) { + res[1] = i + 1; + return res; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Math + +::tabs-start + +```python +class Solution: + def findErrorNums(self, nums: List[int]) -> List[int]: + N = len(nums) + x = 0 # duplicate - missing + y = 0 # duplicate^2 - missing^2 + + for i in range(1, N + 1): + x += nums[i - 1] - i + y += nums[i - 1]**2 - i**2 + + missing = (y - x**2) // (2 * x) + duplicate = missing + x + return [duplicate, missing] +``` + +```java +public class Solution { + public int[] findErrorNums(int[] nums) { + int N = nums.length; + int x = 0; // duplicate - missing + int y = 0; // duplicate^2 - missing^2 + + for (int i = 1; i <= N; i++) { + x += nums[i - 1] - i; + y += nums[i - 1] * nums[i - 1] - i * i; + } + + int missing = (y - x * x) / (2 * x); + int duplicate = missing + x; + return new int[]{duplicate, missing}; + } +} +``` + +```cpp +class Solution { +public: + vector findErrorNums(vector& nums) { + int N = nums.size(); + long long x = 0; // duplicate - missing + long long y = 0; // duplicate^2 - missing^2 + + for (int i = 1; i <= N; i++) { + x += nums[i - 1] - i; + y += (long long)nums[i - 1] * nums[i - 1] - (long long)i * i; + } + + int missing = (y - x * x) / (2 * x); + int duplicate = missing + x; + return {duplicate, missing}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findErrorNums(nums) { + const N = nums.length; + let x = 0; // duplicate - missing + let y = 0; // duplicate^2 - missing^2 + + for (let i = 1; i <= N; i++) { + x += nums[i - 1] - i; + y += nums[i - 1] ** 2 - i ** 2; + } + + const missing = (y - x ** 2) / (2 * x); + const duplicate = missing + x; + return [duplicate, missing]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + + +--- + +## 6. Bitwise XOR + +::tabs-start + +```python +class Solution: + def findErrorNums(self, nums: List[int]) -> List[int]: + N = len(nums) + # a ^ a = 0 + # xorr = (1 ^ 2 ^ ... N) ^ (nums[0] ^ nums[1] ^ ... nums[N - 1]) + # xorr = missing ^ duplicate + xorr = 0 + for i in range(1, N + 1): + xorr ^= i + xorr ^= nums[i - 1] + + # bit that is set in only one number among (duplicate, missing), + # will be set in (duplicate ^ missing) + # take rightMost set bit for simplicity + rightMostBit = xorr & ~(xorr - 1) + + # divide numbers (from nums, from [1, N]) into two sets w.r.t the rightMostBit + # xorr the numbers of these sets independently + x = y = 0 + for i in range(1, N + 1): + if i & rightMostBit: + x ^= i + else: + y ^= i + + if nums[i - 1] & rightMostBit: + x ^= nums[i - 1] + else: + y ^= nums[i - 1] + + # identify the duplicate number from x and y + for num in nums: + if num == x: + return [x, y] + return [y, x] +``` + +```java +public class Solution { + public int[] findErrorNums(int[] nums) { + int N = nums.length; + // a ^ a = 0 + // xorr = (1 ^ 2 ^ ... N) ^ (nums[0] ^ nums[1] ^ ... nums[N - 1]) + // xorr = missing ^ duplicate + int xorr = 0; + for (int i = 1; i <= N; i++) { + xorr ^= i; + xorr ^= nums[i - 1]; + } + + // bit that is set in only one number among (duplicate, missing), + // will be set in (duplicate ^ missing) + // take rightMost set bit for simplicity + int rightMostBit = xorr & ~(xorr - 1); + + // divide numbers (from nums, from [1, N]) into two sets w.r.t the rightMostBit + // xorr the numbers of these sets independently + int x = 0, y = 0; + for (int i = 1; i <= N; i++) { + if ((i & rightMostBit) != 0) { + x ^= i; + } else { + y ^= i; + } + + if ((nums[i - 1] & rightMostBit) != 0) { + x ^= nums[i - 1]; + } else { + y ^= nums[i - 1]; + } + } + + // identify the duplicate number from x and y + for (int num : nums) { + if (num == x) { + return new int[]{x, y}; + } + } + return new int[]{y, x}; + } +} +``` + +```cpp +class Solution { +public: + vector findErrorNums(vector& nums) { + int N = nums.size(); + // a ^ a = 0 + // xorr = (1 ^ 2 ^ ... N) ^ (nums[0] ^ nums[1] ^ ... nums[N - 1]) + // xorr = missing ^ duplicate + int xorr = 0; + for (int i = 1; i <= N; i++) { + xorr ^= i; + xorr ^= nums[i - 1]; + } + + // bit that is set in only one number among (duplicate, missing), + // will be set in (duplicate ^ missing) + // take rightMost set bit for simplicity + int rightMostBit = xorr & ~(xorr - 1); + + // divide numbers (from nums, from [1, N]) into two sets w.r.t the rightMostBit + // xorr the numbers of these sets independently + int x = 0, y = 0; + for (int i = 1; i <= N; i++) { + if (i & rightMostBit) { + x ^= i; + } else { + y ^= i; + } + + if (nums[i - 1] & rightMostBit) { + x ^= nums[i - 1]; + } else { + y ^= nums[i - 1]; + } + } + + // identify the duplicate number from x and y + for (int num : nums) { + if (num == x) { + return {x, y}; + } + } + return {y, x}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + findErrorNums(nums) { + const N = nums.length; + // a ^ a = 0 + // xorr = (1 ^ 2 ^ ... N) ^ (nums[0] ^ nums[1] ^ ... nums[N - 1]) + // xorr = missing ^ duplicate + let xorr = 0; + for (let i = 1; i <= N; i++) { + xorr ^= i; + xorr ^= nums[i - 1]; + } + + // bit that is set in only one number among (duplicate, missing), + // will be set in (duplicate ^ missing) + // take rightMost set bit for simplicity + const rightMostBit = xorr & ~(xorr - 1); + + // divide numbers (from nums, from [1, N]) into two sets w.r.t the rightMostBit + // xorr the numbers of these sets independently + let x = 0, y = 0; + for (let i = 1; i <= N; i++) { + if (i & rightMostBit) { + x ^= i; + } else { + y ^= i; + } + + if (nums[i - 1] & rightMostBit) { + x ^= nums[i - 1]; + } else { + y ^= nums[i - 1]; + } + } + + // identify the duplicate number from x and y + for (let num of nums) { + if (num === x) { + return [x, y]; + } + } + return [y, x]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/set-zeroes-in-matrix.md b/articles/set-zeroes-in-matrix.md new file mode 100644 index 000000000..72cef4283 --- /dev/null +++ b/articles/set-zeroes-in-matrix.md @@ -0,0 +1,818 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def setZeroes(self, matrix: List[List[int]]) -> None: + ROWS, COLS = len(matrix), len(matrix[0]) + mark = [[matrix[r][c] for c in range(COLS)] for r in range(ROWS)] + + for r in range(ROWS): + for c in range(COLS): + if matrix[r][c] == 0: + for col in range(COLS): + mark[r][col] = 0 + for row in range(ROWS): + mark[row][c] = 0 + + for r in range(ROWS): + for c in range(COLS): + matrix[r][c] = mark[r][c] +``` + +```java +public class Solution { + public void setZeroes(int[][] matrix) { + int ROWS = matrix.length, COLS = matrix[0].length; + int[][] mark = new int[ROWS][COLS]; + + for (int r = 0; r < ROWS; r++) { + System.arraycopy(matrix[r], 0, mark[r], 0, COLS); + } + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (matrix[r][c] == 0) { + for (int col = 0; col < COLS; col++) { + mark[r][col] = 0; + } + for (int row = 0; row < ROWS; row++) { + mark[row][c] = 0; + } + } + } + } + + for (int r = 0; r < ROWS; r++) { + System.arraycopy(mark[r], 0, matrix[r], 0, COLS); + } + } +} +``` + +```cpp +class Solution { +public: + void setZeroes(vector>& matrix) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + vector> mark = matrix; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (matrix[r][c] == 0) { + for (int col = 0; col < COLS; col++) { + mark[r][col] = 0; + } + for (int row = 0; row < ROWS; row++) { + mark[row][c] = 0; + } + } + } + } + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + matrix[r][c] = mark[r][c]; + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {void} + */ + setZeroes(matrix) { + const ROWS = matrix.length, COLS = matrix[0].length; + const mark = matrix.map(row => [...row]); + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (matrix[r][c] === 0) { + for (let col = 0; col < COLS; col++) { + mark[r][col] = 0; + } + for (let row = 0; row < ROWS; row++) { + mark[row][c] = 0; + } + } + } + } + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + matrix[r][c] = mark[r][c]; + } + } + } +} +``` + +```csharp +public class Solution { + public void SetZeroes(int[][] matrix) { + int ROWS = matrix.Length, COLS = matrix[0].Length; + int[][] mark = new int[ROWS][]; + for (int r = 0; r < ROWS; r++) { + mark[r] = (int[]) matrix[r].Clone(); + } + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (matrix[r][c] == 0) { + for (int col = 0; col < COLS; col++) { + mark[r][col] = 0; + } + for (int row = 0; row < ROWS; row++) { + mark[row][c] = 0; + } + } + } + } + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + matrix[r][c] = mark[r][c]; + } + } + } +} +``` + +```go +func setZeroes(matrix [][]int) { + ROWS, COLS := len(matrix), len(matrix[0]) + mark := make([][]int, ROWS) + for i := 0; i < ROWS; i++ { + mark[i] = make([]int, COLS) + copy(mark[i], matrix[i]) + } + + for r := 0; r < ROWS; r++ { + for c := 0; c < COLS; c++ { + if matrix[r][c] == 0 { + for col := 0; col < COLS; col++ { + mark[r][col] = 0 + } + for row := 0; row < ROWS; row++ { + mark[row][c] = 0 + } + } + } + } + + for r := 0; r < ROWS; r++ { + for c := 0; c < COLS; c++ { + matrix[r][c] = mark[r][c] + } + } +} +``` + +```kotlin +class Solution { + fun setZeroes(matrix: Array) { + val ROWS = matrix.size + val COLS = matrix[0].size + val mark = Array(ROWS) { IntArray(COLS) } + + for (r in 0 until ROWS) { + for (c in 0 until COLS) { + mark[r][c] = matrix[r][c] + } + } + + for (r in 0 until ROWS) { + for (c in 0 until COLS) { + if (matrix[r][c] == 0) { + for (col in 0 until COLS) { + mark[r][col] = 0 + } + for (row in 0 until ROWS) { + mark[row][c] = 0 + } + } + } + } + + for (r in 0 until ROWS) { + for (c in 0 until COLS) { + matrix[r][c] = mark[r][c] + } + } + } +} +``` + +```swift +class Solution { + func setZeroes(_ matrix: inout [[Int]]) { + let rows = matrix.count + let cols = matrix[0].count + var mark = matrix + + for r in 0.. Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 2. Iteration + +::tabs-start + +```python +class Solution: + def setZeroes(self, matrix: List[List[int]]) -> None: + ROWS, COLS = len(matrix), len(matrix[0]) + rows, cols = [False] * ROWS, [False] * COLS + + for r in range(ROWS): + for c in range(COLS): + if matrix[r][c] == 0: + rows[r] = True + cols[c] = True + + for r in range(ROWS): + for c in range(COLS): + if rows[r] or cols[c]: + matrix[r][c] = 0 +``` + +```java +public class Solution { + public void setZeroes(int[][] matrix) { + int rows = matrix.length, cols = matrix[0].length; + boolean[] rowZero = new boolean[rows]; + boolean[] colZero = new boolean[cols]; + + for (int r = 0; r < rows; r++) { + for (int c = 0; c < cols; c++) { + if (matrix[r][c] == 0) { + rowZero[r] = true; + colZero[c] = true; + } + } + } + + for (int r = 0; r < rows; r++) { + for (int c = 0; c < cols; c++) { + if (rowZero[r] || colZero[c]) { + matrix[r][c] = 0; + } + } + } + } +} +``` + +```cpp +class Solution { +public: + void setZeroes(vector>& matrix) { + int rows = matrix.size(), cols = matrix[0].size(); + vector rowZero(rows, false); + vector colZero(cols, false); + + for (int r = 0; r < rows; ++r) { + for (int c = 0; c < cols; ++c) { + if (matrix[r][c] == 0) { + rowZero[r] = true; + colZero[c] = true; + } + } + } + + for (int r = 0; r < rows; ++r) { + for (int c = 0; c < cols; ++c) { + if (rowZero[r] || colZero[c]) { + matrix[r][c] = 0; + } + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {void} + */ + setZeroes(matrix) { + const rows = matrix.length, cols = matrix[0].length; + const rowZero = Array(rows).fill(false); + const colZero = Array(cols).fill(false); + + for (let r = 0; r < rows; r++) { + for (let c = 0; c < cols; c++) { + if (matrix[r][c] === 0) { + rowZero[r] = true; + colZero[c] = true; + } + } + } + + for (let r = 0; r < rows; r++) { + for (let c = 0; c < cols; c++) { + if (rowZero[r] || colZero[c]) { + matrix[r][c] = 0; + } + } + } + } +} +``` + +```csharp +public class Solution { + public void SetZeroes(int[][] matrix) { + int rows = matrix.Length, cols = matrix[0].Length; + bool[] rowZero = new bool[rows]; + bool[] colZero = new bool[cols]; + + for (int r = 0; r < rows; r++) { + for (int c = 0; c < cols; c++) { + if (matrix[r][c] == 0) { + rowZero[r] = true; + colZero[c] = true; + } + } + } + + for (int r = 0; r < rows; r++) { + for (int c = 0; c < cols; c++) { + if (rowZero[r] || colZero[c]) { + matrix[r][c] = 0; + } + } + } + } +} +``` + +```go +func setZeroes(matrix [][]int) { + ROWS, COLS := len(matrix), len(matrix[0]) + rows := make([]bool, ROWS) + cols := make([]bool, COLS) + + for r := 0; r < ROWS; r++ { + for c := 0; c < COLS; c++ { + if matrix[r][c] == 0 { + rows[r] = true + cols[c] = true + } + } + } + + for r := 0; r < ROWS; r++ { + for c := 0; c < COLS; c++ { + if rows[r] || cols[c] { + matrix[r][c] = 0 + } + } + } +} +``` + +```kotlin +class Solution { + fun setZeroes(matrix: Array) { + val ROWS = matrix.size + val COLS = matrix[0].size + val rows = BooleanArray(ROWS) + val cols = BooleanArray(COLS) + + for (r in 0 until ROWS) { + for (c in 0 until COLS) { + if (matrix[r][c] == 0) { + rows[r] = true + cols[c] = true + } + } + } + + for (r in 0 until ROWS) { + for (c in 0 until COLS) { + if (rows[r] || cols[c]) { + matrix[r][c] = 0 + } + } + } + } +} +``` + +```swift +class Solution { + func setZeroes(_ matrix: inout [[Int]]) { + let rows = matrix.count + let cols = matrix[0].count + var rowFlags = Array(repeating: false, count: rows) + var colFlags = Array(repeating: false, count: cols) + + for r in 0.. Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 3. Iteration (Space Optimized) + +::tabs-start + +```python +class Solution: + def setZeroes(self, matrix: List[List[int]]) -> None: + ROWS, COLS = len(matrix), len(matrix[0]) + rowZero = False + + for r in range(ROWS): + for c in range(COLS): + if matrix[r][c] == 0: + matrix[0][c] = 0 + if r > 0: + matrix[r][0] = 0 + else: + rowZero = True + + for r in range(1, ROWS): + for c in range(1, COLS): + if matrix[0][c] == 0 or matrix[r][0] == 0: + matrix[r][c] = 0 + + if matrix[0][0] == 0: + for r in range(ROWS): + matrix[r][0] = 0 + + if rowZero: + for c in range(COLS): + matrix[0][c] = 0 +``` + +```java +public class Solution { + public void setZeroes(int[][] matrix) { + int ROWS = matrix.length, COLS = matrix[0].length; + boolean rowZero = false; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (matrix[r][c] == 0) { + matrix[0][c] = 0; + if (r > 0) { + matrix[r][0] = 0; + } else { + rowZero = true; + } + } + } + } + + for (int r = 1; r < ROWS; r++) { + for (int c = 1; c < COLS; c++) { + if (matrix[0][c] == 0 || matrix[r][0] == 0) { + matrix[r][c] = 0; + } + } + } + + if (matrix[0][0] == 0) { + for (int r = 0; r < ROWS; r++) { + matrix[r][0] = 0; + } + } + + if (rowZero) { + for (int c = 0; c < COLS; c++) { + matrix[0][c] = 0; + } + } + } +} +``` + +```cpp +class Solution { +public: + void setZeroes(vector>& matrix) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + bool rowZero = false; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (matrix[r][c] == 0) { + matrix[0][c] = 0; + if (r > 0) { + matrix[r][0] = 0; + } else { + rowZero = true; + } + } + } + } + + for (int r = 1; r < ROWS; r++) { + for (int c = 1; c < COLS; c++) { + if (matrix[0][c] == 0 || matrix[r][0] == 0) { + matrix[r][c] = 0; + } + } + } + + if (matrix[0][0] == 0) { + for (int r = 0; r < ROWS; r++) { + matrix[r][0] = 0; + } + } + + if (rowZero) { + for (int c = 0; c < COLS; c++) { + matrix[0][c] = 0; + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {void} + */ + setZeroes(matrix) { + const ROWS = matrix.length; + const COLS = matrix[0].length; + let rowZero = false; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (matrix[r][c] == 0) { + matrix[0][c] = 0; + if (r > 0) { + matrix[r][0] = 0; + } else { + rowZero = true; + } + } + } + } + + for (let r = 1; r < ROWS; r++) { + for (let c = 1; c < COLS; c++) { + if (matrix[0][c] == 0 || matrix[r][0] == 0) { + matrix[r][c] = 0; + } + } + } + + if (matrix[0][0] == 0) { + for (let r = 0; r < ROWS; r++) { + matrix[r][0] = 0; + } + } + + if (rowZero) { + for (let c = 0; c < COLS; c++) { + matrix[0][c] = 0; + } + } + } +} +``` + +```csharp +public class Solution { + public void SetZeroes(int[][] matrix) { + int ROWS = matrix.Length, COLS = matrix[0].Length; + bool rowZero = false; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (matrix[r][c] == 0) { + matrix[0][c] = 0; + if (r > 0) { + matrix[r][0] = 0; + } else { + rowZero = true; + } + } + } + } + + for (int r = 1; r < ROWS; r++) { + for (int c = 1; c < COLS; c++) { + if (matrix[0][c] == 0 || matrix[r][0] == 0) { + matrix[r][c] = 0; + } + } + } + + if (matrix[0][0] == 0) { + for (int r = 0; r < ROWS; r++) { + matrix[r][0] = 0; + } + } + + if (rowZero) { + for (int c = 0; c < COLS; c++) { + matrix[0][c] = 0; + } + } + } +} +``` + +```go +func setZeroes(matrix [][]int) { + ROWS, COLS := len(matrix), len(matrix[0]) + rowZero := false + + for r := 0; r < ROWS; r++ { + for c := 0; c < COLS; c++ { + if matrix[r][c] == 0 { + matrix[0][c] = 0 + if r > 0 { + matrix[r][0] = 0 + } else { + rowZero = true + } + } + } + } + + for r := 1; r < ROWS; r++ { + for c := 1; c < COLS; c++ { + if matrix[0][c] == 0 || matrix[r][0] == 0 { + matrix[r][c] = 0 + } + } + } + + if matrix[0][0] == 0 { + for r := 0; r < ROWS; r++ { + matrix[r][0] = 0 + } + } + + if rowZero { + for c := 0; c < COLS; c++ { + matrix[0][c] = 0 + } + } +} +``` + +```kotlin +class Solution { + fun setZeroes(matrix: Array) { + val ROWS = matrix.size + val COLS = matrix[0].size + var rowZero = false + + for (r in 0 until ROWS) { + for (c in 0 until COLS) { + if (matrix[r][c] == 0) { + matrix[0][c] = 0 + if (r > 0) { + matrix[r][0] = 0 + } else { + rowZero = true + } + } + } + } + + for (r in 1 until ROWS) { + for (c in 1 until COLS) { + if (matrix[0][c] == 0 || matrix[r][0] == 0) { + matrix[r][c] = 0 + } + } + } + + if (matrix[0][0] == 0) { + for (r in 0 until ROWS) { + matrix[r][0] = 0 + } + } + + if (rowZero) { + for (c in 0 until COLS) { + matrix[0][c] = 0 + } + } + } +} +``` + +```swift +class Solution { + func setZeroes(_ matrix: inout [[Int]]) { + let ROWS = matrix.count + let COLS = matrix[0].count + var rowZero = false + + for r in 0.. 0 { + matrix[r][0] = 0 + } else { + rowZero = true + } + } + } + } + + for r in 1.. Where $m$ is the number of rows and $n$ is the number of columns. \ No newline at end of file diff --git a/articles/shift-2d-grid.md b/articles/shift-2d-grid.md new file mode 100644 index 000000000..78f570a31 --- /dev/null +++ b/articles/shift-2d-grid.md @@ -0,0 +1,502 @@ +## 1. Simulation (Extra Space) + +::tabs-start + +```python +class Solution: + def shiftGrid(self, grid: List[List[int]], k: int) -> List[List[int]]: + m, n = len(grid), len(grid[0]) + + while k: + cur = [[0] * n for _ in range(m)] + + for r in range(m): + for c in range(n - 1): + cur[r][c + 1] = grid[r][c] + + for r in range(m): + cur[(r + 1) % m][0] = grid[r][n - 1] + + grid = cur + k -= 1 + + return grid +``` + +```java +public class Solution { + public List> shiftGrid(int[][] grid, int k) { + int m = grid.length, n = grid[0].length; + + while (k > 0) { + int[][] cur = new int[m][n]; + + for (int r = 0; r < m; r++) { + for (int c = 0; c < n - 1; c++) { + cur[r][c + 1] = grid[r][c]; + } + } + + for (int r = 0; r < m; r++) { + cur[(r + 1) % m][0] = grid[r][n - 1]; + } + + grid = cur; + k--; + } + + List> res = new ArrayList<>(); + for (int r = 0; r < m; r++) { + List tmp = new ArrayList<>(); + for (int c = 0; c < n; c++) { + tmp.add(grid[r][c]); + } + res.add(tmp); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> shiftGrid(vector>& grid, int k) { + int m = grid.size(), n = grid[0].size(); + + while (k > 0) { + vector> cur(m, vector(n, 0)); + + for (int r = 0; r < m; r++) { + for (int c = 0; c < n - 1; c++) { + cur[r][c + 1] = grid[r][c]; + } + } + + for (int r = 0; r < m; r++) { + cur[(r + 1) % m][0] = grid[r][n - 1]; + } + + grid = cur; + k--; + } + + return grid; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @param {number} k + * @return {number[][]} + */ + shiftGrid(grid, k) { + const m = grid.length, n = grid[0].length; + + while (k > 0) { + const cur = Array.from({ length: m }, () => Array(n).fill(0)); + + for (let r = 0; r < m; r++) { + for (let c = 0; c < n - 1; c++) { + cur[r][c + 1] = grid[r][c]; + } + } + + for (let r = 0; r < m; r++) { + cur[(r + 1) % m][0] = grid[r][n - 1]; + } + + grid = cur; + k--; + } + + return grid; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows in the grid, $n$ is the number of columns in the grid, and $k$ is the shift count. + +--- + +## 2. Simulation + +::tabs-start + +```python +class Solution: + def shiftGrid(self, grid: List[List[int]], k: int) -> List[List[int]]: + m, n = len(grid), len(grid[0]) + + while k: + prev = grid[m - 1][n - 1] + for r in range(m): + for c in range(n): + grid[r][c], prev = prev, grid[r][c] + k -= 1 + + return grid +``` + +```java +public class Solution { + public List> shiftGrid(int[][] grid, int k) { + int m = grid.length, n = grid[0].length; + + while (k > 0) { + int prev = grid[m - 1][n - 1]; + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + int temp = grid[r][c]; + grid[r][c] = prev; + prev = temp; + } + } + k--; + } + + List> res = new ArrayList<>(); + for (int r = 0; r < m; r++) { + List tmp = new ArrayList<>(); + for (int c = 0; c < n; c++) { + tmp.add(grid[r][c]); + } + res.add(tmp); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> shiftGrid(vector>& grid, int k) { + int m = grid.size(), n = grid[0].size(); + + while (k > 0) { + int prev = grid[m - 1][n - 1]; + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + swap(grid[r][c], prev); + } + } + k--; + } + + return grid; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @param {number} k + * @return {number[][]} + */ + shiftGrid(grid, k) { + const m = grid.length, n = grid[0].length; + + while (k > 0) { + let prev = grid[m - 1][n - 1]; + for (let r = 0; r < m; r++) { + for (let c = 0; c < n; c++) { + [prev, grid[r][c]] = [grid[r][c], prev]; + } + } + k--; + } + + return grid; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * m * n)$ +* Space complexity: $O(m * n)$ for the output matrix. + +> Where $m$ is the number of rows in the grid, $n$ is the number of columns in the grid, and $k$ is the shift count. + +--- + +## 3. Convert to One Dimensional Array + +::tabs-start + +```python +class Solution: + def shiftGrid(self, grid: List[List[int]], k: int) -> List[List[int]]: + m, n = len(grid), len(grid[0]) + N = m * n + k %= N + + arr = [0] * N + for r in range(m): + for c in range(n): + arr[r * n + c] = grid[r][c] + + def reverse(l, r): + while l < r: + arr[l], arr[r] = arr[r], arr[l] + l += 1 + r -= 1 + + reverse(0, N - 1) + reverse(0, k - 1) + reverse(k, N - 1) + + for r in range(m): + for c in range(n): + grid[r][c] = arr[r * n + c] + + return grid +``` + +```java +public class Solution { + public List> shiftGrid(int[][] grid, int k) { + int m = grid.length, n = grid[0].length; + int N = m * n; + k %= N; + + int[] arr = new int[N]; + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + arr[r * n + c] = grid[r][c]; + } + } + + reverse(arr, 0, N - 1); + reverse(arr, 0, k - 1); + reverse(arr, k, N - 1); + + List> res = new ArrayList<>(); + for (int r = 0; r < m; r++) { + List tmp = new ArrayList<>(); + for (int c = 0; c < n; c++) { + tmp.add(arr[r * n + c]); + } + res.add(tmp); + } + return res; + } + + private void reverse(int[] arr, int l, int r) { + while (l < r) { + int temp = arr[l]; + arr[l] = arr[r]; + arr[r] = temp; + l++; + r--; + } + } +} +``` + +```cpp +class Solution { +public: + vector> shiftGrid(vector>& grid, int k) { + int m = grid.size(), n = grid[0].size(); + int N = m * n; + k %= N; + + vector arr(N); + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + arr[r * n + c] = grid[r][c]; + } + } + + reverse(arr.begin(), arr.end()); + reverse(arr.begin(), arr.begin() + k); + reverse(arr.begin() + k, arr.end()); + + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + grid[r][c] = arr[r * n + c]; + } + } + + return grid; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @param {number} k + * @return {number[][]} + */ + shiftGrid(grid, k) { + const m = grid.length, n = grid[0].length; + const N = m * n; + k %= N; + + const arr = new Array(N); + for (let r = 0; r < m; r++) { + for (let c = 0; c < n; c++) { + arr[r * n + c] = grid[r][c]; + } + } + + const reverse = (l, r) => { + while (l < r) { + [arr[l], arr[r]] = [arr[r], arr[l]]; + l++; + r--; + } + }; + + reverse(0, N - 1); + reverse(0, k - 1); + reverse(k, N - 1); + + for (let r = 0; r < m; r++) { + for (let c = 0; c < n; c++) { + grid[r][c] = arr[r * n + c]; + } + } + + return grid; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows in the grid and $n$ is the number of columns in the grid. + +--- + +## 4. Iteration + +::tabs-start + +```python +class Solution: + def shiftGrid(self, grid: List[List[int]], k: int) -> List[List[int]]: + M, N = len(grid), len(grid[0]) + + def posToVal(r, c): + return r * N + c + + def valToPos(v): + return [v // N, v % N] + + res = [[0] * N for _ in range(M)] + for r in range(M): + for c in range(N): + newVal = (posToVal(r, c) + k) % (M * N) + newR, newC = valToPos(newVal) + res[newR][newC] = grid[r][c] + + return res +``` + +```java +public class Solution { + public List> shiftGrid(int[][] grid, int k) { + int M = grid.length, N = grid[0].length; + int[][] arr = new int[M][N]; + + for (int r = 0; r < M; r++) { + for (int c = 0; c < N; c++) { + int newVal = (r * N + c + k) % (M * N); + int newR = newVal / N, newC = newVal % N; + arr[newR][newC] = grid[r][c]; + } + } + + List> res = new ArrayList<>(); + for (int r = 0; r < M; r++) { + List tmp = new ArrayList<>(); + for (int c = 0; c < N; c++) { + tmp.add(arr[r][c]); + } + res.add(tmp); + } + return res; + } + +} +``` + +```cpp +class Solution { +public: + vector> shiftGrid(vector>& grid, int k) { + int M = grid.size(), N = grid[0].size(); + vector> res(M, vector(N)); + + for (int r = 0; r < M; r++) { + for (int c = 0; c < N; c++) { + int newVal = (r * N + c + k) % (M * N); + int newR = newVal / N, newC = newVal % N; + res[newR][newC] = grid[r][c]; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @param {number} k + * @return {number[][]} + */ + shiftGrid(grid, k) { + const M = grid.length, N = grid[0].length; + + const posToVal = (r, c) => r * N + c; + const valToPos = (v) => [Math.floor(v / N), v % N]; + + const res = Array.from({ length: M }, () => Array(N).fill(0)); + for (let r = 0; r < M; r++) { + for (let c = 0; c < N; c++) { + const newVal = (posToVal(r, c) + k) % (M * N); + const [newR, newC] = valToPos(newVal); + res[newR][newC] = grid[r][c]; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows in the grid and $n$ is the number of columns in the grid. \ No newline at end of file diff --git a/articles/shifting-letters-ii.md b/articles/shifting-letters-ii.md new file mode 100644 index 000000000..12d3e9499 --- /dev/null +++ b/articles/shifting-letters-ii.md @@ -0,0 +1,481 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def shiftingLetters(self, s: str, shifts: List[List[int]]) -> str: + s = [ord(c) - ord('a') for c in s] + + for l, r, d in shifts: + for i in range(l, r + 1): + s[i] += 1 if d else -1 + s[i] %= 26 + + s = [chr(ord('a') + c) for c in s] + return "".join(s) +``` + +```java +class Solution { + public String shiftingLetters(String s, int[][] shifts) { + char[] arr = s.toCharArray(); + int[] letters = new int[arr.length]; + + for (int i = 0; i < arr.length; i++) { + letters[i] = arr[i] - 'a'; + } + + for (int[] shift : shifts) { + int l = shift[0], r = shift[1], d = shift[2]; + for (int i = l; i <= r; i++) { + letters[i] = (letters[i] + (d == 1 ? 1 : -1) + 26) % 26; + } + } + + for (int i = 0; i < arr.length; i++) { + arr[i] = (char) (letters[i] + 'a'); + } + + return new String(arr); + } +} +``` + +```cpp +class Solution { +public: + string shiftingLetters(string s, vector>& shifts) { + vector letters(s.size()); + for (int i = 0; i < s.size(); i++) { + letters[i] = s[i] - 'a'; + } + + for (const auto& shift : shifts) { + int l = shift[0], r = shift[1], d = shift[2]; + for (int i = l; i <= r; i++) { + letters[i] = (letters[i] + (d == 1 ? 1 : -1) + 26) % 26; + } + } + + for (int i = 0; i < s.size(); i++) { + s[i] = letters[i] + 'a'; + } + + return s; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number[][]} shifts + * @return {string} + */ + shiftingLetters(s, shifts) { + let arr = Array.from(s).map(c => c.charCodeAt(0) - 97); + + for (const [l, r, d] of shifts) { + for (let i = l; i <= r; i++) { + arr[i] = (arr[i] + (d === 1 ? 1 : -1) + 26) % 26; + } + } + + return arr.map(c => String.fromCharCode(c + 97)).join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the string $s$ and $m$ is the size of the array $shifts$. + +--- + +## 2. Sweep Line Algorithm + +::tabs-start + +```python +class Solution: + def shiftingLetters(self, s: str, shifts: List[List[int]]) -> str: + prefix_diff = [0] * (len(s) + 1) + + for left, right, d in shifts: + val = 1 if d == 1 else -1 + prefix_diff[left] += val + prefix_diff[right + 1] -= val + + diff = 0 + res = [ord(c) - ord("a") for c in s] + + for i in range(len(s)): + diff += prefix_diff[i] + res[i] = (diff + res[i] + 26) % 26 + + s = [chr(ord("a") + n) for n in res] + return "".join(s) +``` + +```java +class Solution { + public String shiftingLetters(String s, int[][] shifts) { + int n = s.length(); + int[] prefix_diff = new int[n + 1]; + + for (int[] shift : shifts) { + int left = shift[0], right = shift[1], d = shift[2]; + int val = d == 1 ? 1 : -1; + prefix_diff[left] += val; + prefix_diff[right + 1] -= val; + } + + int[] res = new int[n]; + for (int i = 0; i < n; i++) { + res[i] = s.charAt(i) - 'a'; + } + + int diff = 0; + for (int i = 0; i < n; i++) { + diff += prefix_diff[i]; + res[i] = (res[i] + diff % 26 + 26) % 26; + } + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < n; i++) { + sb.append((char) ('a' + res[i])); + } + + return sb.toString(); + } +} +``` + +```cpp +class Solution { +public: + string shiftingLetters(string s, vector>& shifts) { + int n = s.size(); + vector prefix_diff(n + 1, 0); + + for (auto& shift : shifts) { + int left = shift[0], right = shift[1], d = shift[2]; + int val = d == 1 ? 1 : -1; + prefix_diff[left] += val; + prefix_diff[right + 1] -= val; + } + + int diff = 0; + vector res(n); + for (int i = 0; i < n; ++i) { + res[i] = s[i] - 'a'; + } + + for (int i = 0; i < n; ++i) { + diff += prefix_diff[i]; + res[i] = (diff % 26 + res[i] + 26) % 26; + } + + for (int i = 0; i < n; ++i) { + s[i] = 'a' + res[i]; + } + + return s; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number[][]} shifts + * @return {string} + */ + shiftingLetters(s, shifts) { + const n = s.length; + const prefix_diff = Array(n + 1).fill(0); + + for (const [left, right, d] of shifts) { + const val = d === 1 ? 1 : -1; + prefix_diff[left] += val; + prefix_diff[right + 1] -= val; + } + + let diff = 0; + const res = Array.from(s).map(c => c.charCodeAt(0) - 'a'.charCodeAt(0)); + + for (let i = 0; i < n; i++) { + diff += prefix_diff[i]; + res[i] = (diff % 26 + res[i] + 26) % 26; + } + + return res.map(x => String.fromCharCode('a'.charCodeAt(0) + x)).join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the string $s$ and $m$ is the size of the array $shifts$. + +--- + +## 3. Binary Indexed Tree (Fenwick Tree) + +::tabs-start + +```python +class BIT: + def __init__(self, size): + self.n = size + 2 + self.tree = [0] * self.n + + def update(self, index, delta): + index += 1 + while index < self.n: + self.tree[index] += delta + index += index & -index + + def prefix_sum(self, index): + index += 1 + total = 0 + while index > 0: + total += self.tree[index] + index -= index & -index + return total + + def range_update(self, left, right, delta): + self.update(left, delta) + self.update(right + 1, -delta) + + +class Solution: + def shiftingLetters(self, s: str, shifts: List[List[int]]) -> str: + n = len(s) + bit = BIT(n) + + for left, right, d in shifts: + delta = 1 if d == 1 else -1 + bit.range_update(left, right, delta) + + res = [] + for i in range(n): + shift = bit.prefix_sum(i) % 26 + code = (ord(s[i]) - ord('a') + shift + 26) % 26 + res.append(chr(ord('a') + code)) + + return ''.join(res) +``` + +```java +class BIT { + int[] tree; + int n; + + public BIT(int size) { + n = size + 2; + tree = new int[n]; + } + + public void update(int index, int delta) { + index++; + while (index < n) { + tree[index] += delta; + index += index & -index; + } + } + + public int prefixSum(int index) { + index++; + int sum = 0; + while (index > 0) { + sum += tree[index]; + index -= index & -index; + } + return sum; + } + + public void rangeUpdate(int left, int right, int delta) { + update(left, delta); + update(right + 1, -delta); + } +} + +public class Solution { + public String shiftingLetters(String s, int[][] shifts) { + int n = s.length(); + BIT bit = new BIT(n); + + for (int[] shift : shifts) { + int left = shift[0], right = shift[1], d = shift[2]; + int delta = d == 1 ? 1 : -1; + bit.rangeUpdate(left, right, delta); + } + + StringBuilder res = new StringBuilder(); + for (int i = 0; i < n; i++) { + int shift = bit.prefixSum(i) % 26; + int code = (s.charAt(i) - 'a' + shift + 26) % 26; + res.append((char) ('a' + code)); + } + + return res.toString(); + } +} +``` + +```cpp +class BIT { + vector tree; + int n; +public: + BIT(int size) { + n = size + 2; + tree.assign(n, 0); + } + + void update(int index, int delta) { + index++; + while (index < n) { + tree[index] += delta; + index += index & -index; + } + } + + int prefixSum(int index) { + index++; + int sum = 0; + while (index > 0) { + sum += tree[index]; + index -= index & -index; + } + return sum; + } + + void rangeUpdate(int left, int right, int delta) { + update(left, delta); + update(right + 1, -delta); + } +}; + +class Solution { +public: + string shiftingLetters(string s, vector>& shifts) { + int n = s.size(); + BIT bit(n); + + for (auto& shift : shifts) { + int left = shift[0], right = shift[1], d = shift[2]; + int delta = d == 1 ? 1 : -1; + bit.rangeUpdate(left, right, delta); + } + + string res; + for (int i = 0; i < n; i++) { + int shift = bit.prefixSum(i) % 26; + int code = (s[i] - 'a' + shift + 26) % 26; + res += char('a' + code); + } + + return res; + } +}; +``` + +```javascript +class BIT { + /** + * @constructor + * @param {number} size + */ + constructor(size) { + this.n = size + 2; + this.tree = new Array(this.n).fill(0); + } + + /** + * @param {number} index + * @param {number} delta + * @return {void} + */ + update(index, delta) { + index++; + while (index < this.n) { + this.tree[index] += delta; + index += index & -index; + } + } + + /** + * @param {number} index + * @return {number} + */ + prefixSum(index) { + index++; + let sum = 0; + while (index > 0) { + sum += this.tree[index]; + index -= index & -index; + } + return sum; + } + + /** + * @param {number} left + * @param {number} right + * @param {number} delta + * @return {void} + */ + rangeUpdate(left, right, delta) { + this.update(left, delta); + this.update(right + 1, -delta); + } +} + +class Solution { + /** + * @param {string} s + * @param {number[][]} shifts + * @return {string} + */ + shiftingLetters(s, shifts) { + const n = s.length; + const bit = new BIT(n); + for (const [left, right, d] of shifts) { + const delta = d === 1 ? 1 : -1; + bit.rangeUpdate(left, right, delta); + } + + let res = ""; + for (let i = 0; i < n; i++) { + const shift = bit.prefixSum(i) % 26; + const code = (s.charCodeAt(i) - 97 + shift + 26) % 26; + res += String.fromCharCode(97 + code); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((m + n) * \log n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the length of the string $s$ and $m$ is the size of the array $shifts$. \ No newline at end of file diff --git a/articles/shortest-bridge.md b/articles/shortest-bridge.md new file mode 100644 index 000000000..14f78e190 --- /dev/null +++ b/articles/shortest-bridge.md @@ -0,0 +1,1068 @@ +## 1. Depth First Search + Breadth First Search - I + +::tabs-start + +```python +class Solution: + def shortestBridge(self, grid: List[List[int]]) -> int: + N = len(grid) + direct = [[0, 1], [0, -1], [1, 0], [-1, 0]] + + def invalid(r, c): + return r < 0 or c < 0 or r == N or c == N + + visit = set() + + def dfs(r, c): + if invalid(r, c) or not grid[r][c] or (r, c) in visit: + return + visit.add((r, c)) + for dr, dc in direct: + dfs(r + dr, c + dc) + + def bfs(): + res, q = 0, deque(visit) + while q: + for _ in range(len(q)): + r, c = q.popleft() + for dr, dc in direct: + curR, curC = r + dr, c + dc + if invalid(curR, curC) or (curR, curC) in visit: + continue + if grid[curR][curC]: + return res + q.append((curR, curC)) + visit.add((curR, curC)) + res += 1 + + for r in range(N): + for c in range(N): + if grid[r][c]: + dfs(r, c) + return bfs() +``` + +```java +public class Solution { + private int N; + private boolean[][] visited; + private final int[][] direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + public int shortestBridge(int[][] grid) { + N = grid.length; + visited = new boolean[N][N]; + + boolean found = false; + for (int r = 0; r < N; r++) { + if (found) break; + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + dfs(grid, r, c); + found = true; + break; + } + } + } + + return bfs(grid); + } + + private void dfs(int[][] grid, int r, int c) { + if (r < 0 || c < 0 || r >= N || c >= N || grid[r][c] == 0 || visited[r][c]) + return; + + visited[r][c] = true; + + for (int[] d : direct) { + dfs(grid, r + d[0], c + d[1]); + } + } + + private int bfs(int[][] grid) { + Queue q = new LinkedList<>(); + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (visited[r][c]) { + q.offer(new int[]{r, c}); + } + } + } + + int res = 0; + while (!q.isEmpty()) { + for (int i = q.size(); i > 0; i--) { + int[] cell = q.poll(); + int r = cell[0], c = cell[1]; + + for (int[] d : direct) { + int curR = r + d[0], curC = c + d[1]; + + if (curR < 0 || curC < 0 || curR >= N || curC >= N || visited[curR][curC]) + continue; + + if (grid[curR][curC] == 1) return res; + + q.offer(new int[]{curR, curC}); + visited[curR][curC] = true; + } + } + res++; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int N; + vector> visited; + vector> direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + int shortestBridge(vector>& grid) { + N = grid.size(); + visited = vector>(N, vector(N, false)); + + bool found = false; + for (int r = 0; r < N; r++) { + if (found) break; + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + dfs(grid, r, c); + found = true; + break; + } + } + } + + return bfs(grid); + } + +private: + void dfs(vector>& grid, int r, int c) { + if (r < 0 || c < 0 || r >= N || c >= N || grid[r][c] == 0 || visited[r][c]) + return; + + visited[r][c] = true; + for (auto& d : direct) { + dfs(grid, r + d[0], c + d[1]); + } + } + + int bfs(vector>& grid) { + queue> q; + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (visited[r][c]) { + q.push({r, c}); + } + } + } + + int res = 0; + while (!q.empty()) { + for (int i = q.size(); i > 0; i--) { + auto [r, c] = q.front(); q.pop(); + + for (auto& d : direct) { + int curR = r + d[0], curC = c + d[1]; + + if (curR < 0 || curC < 0 || curR >= N || curC >= N || visited[curR][curC]) + continue; + + if (grid[curR][curC] == 1) return res; + q.push({curR, curC}); + visited[curR][curC] = true; + } + } + res++; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + shortestBridge(grid) { + const N = grid.length; + const direct = [[0, 1], [0, -1], [1, 0], [-1, 0]]; + const visited = Array.from({ length: N }, () => Array(N).fill(false)); + const q = new Queue(); + + const dfs = (r, c) => { + if (r < 0 || c < 0 || r >= N || c >= N || grid[r][c] === 0 || visited[r][c]) + return; + visited[r][c] = true; + q.push([r, c]); + + for (const [dr, dc] of direct) { + dfs(r + dr, c + dc); + } + }; + + const bfs = () => { + let res = 0; + while (!q.isEmpty()) { + for (let i = q.size(); i > 0; i--) { + const [r, c] = q.pop(); + for (const [dr, dc] of direct) { + const curR = r + dr, curC = c + dc; + + if (curR < 0 || curC < 0 || curR >= N || curC >= N || visited[curR][curC]) + continue; + if (grid[curR][curC] === 1) return res; + + q.push([curR, curC]); + visited[curR][curC] = true; + } + } + res++; + } + }; + + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) { + dfs(r, c); + return bfs(); + } + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Depth First Search + Breadth First Search - II + +::tabs-start + +```python +class Solution: + def shortestBridge(self, grid: list[list[int]]) -> int: + N, direct = len(grid), [(0, 1), (0, -1), (1, 0), (-1, 0)] + + def dfs(r, c): + if 0 <= r < N and 0 <= c < N and grid[r][c] == 1: + grid[r][c] = 2 + q.append((r, c)) + for dr, dc in direct: + dfs(r + dr, c + dc) + + q = deque() + for r in range(N): + for c in range(N): + if grid[r][c]: + dfs(r, c) + break + if q: break + + res = 0 + while q: + for _ in range(len(q)): + r, c = q.popleft() + for dr, dc in direct: + nr, nc = r + dr, c + dc + if 0 <= nr < N and 0 <= nc < N: + if grid[nr][nc] == 1: + return res + if grid[nr][nc] == 0: + grid[nr][nc] = 2 + q.append((nr, nc)) + res += 1 +``` + +```java +public class Solution { + private int[][] direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + public int shortestBridge(int[][] grid) { + int N = grid.length; + Queue q = new LinkedList<>(); + + boolean found = false; + for (int r = 0; r < N; r++) { + if (found) break; + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + dfs(grid, r, c, q); + found = true; + break; + } + } + } + + int res = 0; + while (!q.isEmpty()) { + for (int i = q.size(); i > 0; i--) { + int[] cell = q.poll(); + int r = cell[0], c = cell[1]; + + for (int[] d : direct) { + int nr = r + d[0], nc = c + d[1]; + + if (nr < 0 || nc < 0 || nr >= N || nc >= N) continue; + if (grid[nr][nc] == 1) return res; + + if (grid[nr][nc] == 0) { + grid[nr][nc] = 2; + q.offer(new int[]{nr, nc}); + } + } + } + res++; + } + return res; + } + + private void dfs(int[][] grid, int r, int c, Queue q) { + if (r < 0 || c < 0 || r >= grid.length || c >= grid.length || grid[r][c] != 1) + return; + + grid[r][c] = 2; + q.offer(new int[]{r, c}); + for (int[] d : direct) { + dfs(grid, r + d[0], c + d[1], q); + } + } +} +``` + +```cpp +class Solution { + vector> direct; + +public: + int shortestBridge(vector>& grid) { + int N = grid.size(); + direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + queue> q; + + bool found = false; + for (int r = 0; r < N; r++) { + if (found) break; + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + dfs(grid, r, c, q); + found = true; + break; + } + } + } + + int res = 0; + while (!q.empty()) { + for (int i = q.size(); i > 0; i--) { + auto [r, c] = q.front(); q.pop(); + + for (auto& d : direct) { + int nr = r + d[0], nc = c + d[1]; + + if (nr < 0 || nc < 0 || nr >= N || nc >= N) continue; + if (grid[nr][nc] == 1) return res; + + if (grid[nr][nc] == 0) { + grid[nr][nc] = 2; + q.push({nr, nc}); + } + } + } + res++; + } + return res; + } + +private: + void dfs(vector>& grid, int r, int c, queue>& q) { + if (r < 0 || c < 0 || r >= grid.size() || c >= grid.size() || grid[r][c] != 1) + return; + + grid[r][c] = 2; + q.push({r, c}); + for (auto& d : direct) { + dfs(grid, r + d[0], c + d[1], q); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + shortestBridge(grid) { + const N = grid.length; + const direct = [[0, 1], [0, -1], [1, 0], [-1, 0]]; + const q = new Queue(); + + const dfs = (r, c) => { + if (r < 0 || c < 0 || r >= N || c >= N || grid[r][c] !== 1) return; + grid[r][c] = 2; + q.push([r, c]); + for (const [dr, dc] of direct) { + dfs(r + dr, c + dc); + } + }; + + let found = false; + for (let r = 0; r < N; r++) { + if (found) break; + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) { + dfs(r, c); + found = true; + break; + } + } + } + + let res = 0; + while (!q.isEmpty()) { + for (let i = q.size(); i > 0; i--) { + const [r, c] = q.pop(); + for (const [dr, dc] of direct) { + let nr = r + dr, nc = c + dc; + if (nr < 0 || nc < 0 || nr >= N || nc >= N) continue; + if (grid[nr][nc] === 1) return res; + if (grid[nr][nc] === 0) { + grid[nr][nc] = 2; + q.push([nr, nc]); + } + } + } + res++; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Breadth First Search + +::tabs-start + +```python +class Solution: + def shortestBridge(self, grid: list[list[int]]) -> int: + N, direct = len(grid), [(0, 1), (0, -1), (1, 0), (-1, 0)] + q2 = deque() + + found = False + for r in range(N): + if found: break + for c in range(N): + if grid[r][c] == 1: + q1 = deque([(r, c)]) + grid[r][c] = 2 + while q1: + x, y = q1.popleft() + q2.append((x, y)) + for dx, dy in direct: + nx, ny = x + dx, y + dy + if 0 <= nx < N and 0 <= ny < N and grid[nx][ny] == 1: + grid[nx][ny] = 2 + q1.append((nx, ny)) + found = True + break + + res = 0 + while q2: + for _ in range(len(q2)): + x, y = q2.popleft() + for dx, dy in direct: + nx, ny = x + dx, y + dy + if 0 <= nx < N and 0 <= ny < N: + if grid[nx][ny] == 1: + return res + if grid[nx][ny] == 0: + grid[nx][ny] = 2 + q2.append((nx, ny)) + res += 1 + + return res +``` + +```java +public class Solution { + public int shortestBridge(int[][] grid) { + int N = grid.length; + int[][] direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + Queue q2 = new LinkedList<>(); + + boolean found = false; + for (int r = 0; r < N; r++) { + if (found) break; + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + Queue q1 = new LinkedList<>(); + q1.offer(new int[]{r, c}); + grid[r][c] = 2; + + while (!q1.isEmpty()) { + int[] cell = q1.poll(); + int x = cell[0], y = cell[1]; + q2.offer(new int[]{x, y}); + + for (int[] d : direct) { + int nx = x + d[0], ny = y + d[1]; + if (nx >= 0 && ny >= 0 && nx < N && ny < N && grid[nx][ny] == 1) { + grid[nx][ny] = 2; + q1.offer(new int[]{nx, ny}); + } + } + } + found = true; + break; + } + } + } + + int res = 0; + while (!q2.isEmpty()) { + for (int i = q2.size(); i > 0; i--) { + int[] cell = q2.poll(); + int x = cell[0], y = cell[1]; + + for (int[] d : direct) { + int nx = x + d[0], ny = y + d[1]; + + if (nx >= 0 && ny >= 0 && nx < N && ny < N) { + if (grid[nx][ny] == 1) return res; + if (grid[nx][ny] == 0) { + grid[nx][ny] = 2; + q2.offer(new int[]{nx, ny}); + } + } + } + } + res++; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int shortestBridge(vector>& grid) { + int N = grid.size(); + vector> direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + queue> q2; + + bool found = false; + for (int r = 0; r < N; r++) { + if (found) break; + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + queue> q1; + q1.push({r, c}); + grid[r][c] = 2; + + while (!q1.empty()) { + auto [x, y] = q1.front(); q1.pop(); + q2.push({x, y}); + + for (auto& d : direct) { + int nx = x + d[0], ny = y + d[1]; + if (nx >= 0 && ny >= 0 && nx < N && ny < N && grid[nx][ny] == 1) { + grid[nx][ny] = 2; + q1.push({nx, ny}); + } + } + } + found = true; + break; + } + } + } + + int res = 0; + while (!q2.empty()) { + for (int i = q2.size(); i > 0; i--) { + auto [x, y] = q2.front(); q2.pop(); + + for (auto& d : direct) { + int nx = x + d[0], ny = y + d[1]; + + if (nx >= 0 && ny >= 0 && nx < N && ny < N) { + if (grid[nx][ny] == 1) return res; + if (grid[nx][ny] == 0) { + grid[nx][ny] = 2; + q2.push({nx, ny}); + } + } + } + } + res++; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + shortestBridge(grid) { + const N = grid.length; + const direct = [[0, 1], [0, -1], [1, 0], [-1, 0]]; + const q2 = new Queue(); + + let found = false; + for (let r = 0; r < N; r++) { + if (found) break; + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) { + const q1 = new Queue([[r, c]]); + grid[r][c] = 2; + + while (!q1.isEmpty()) { + let [x, y] = q1.pop(); + q2.push([x, y]); + + for (let [dx, dy] of direct) { + let nx = x + dx, ny = y + dy; + if (nx >= 0 && ny >= 0 && nx < N && ny < N && grid[nx][ny] === 1) { + grid[nx][ny] = 2; + q1.push([nx, ny]); + } + } + } + found = true; + break; + } + } + } + + let res = 0; + while (!q2.isEmpty()) { + for (let i = q2.size(); i > 0; i--) { + const [x, y] = q2.pop(); + + for (let [dx, dy] of direct) { + let nx = x + dx, ny = y + dy; + if (nx >= 0 && ny >= 0 && nx < N && ny < N) { + if (grid[nx][ny] === 1) return res; + if (grid[nx][ny] === 0) { + grid[nx][ny] = 2; + q2.push([nx, ny]); + } + } + } + } + res++; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Disjoint Set Union + Breadth First Search + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.parent = list(range(n)) + self.rank = [1] * n + + def find(self, node): + cur = node + while cur != self.parent[cur]: + self.parent[cur] = self.parent[self.parent[cur]] + cur = self.parent[cur] + return cur + + def union(self, u, v): + pu, pv = self.find(u), self.find(v) + if pu == pv: + return False + if self.rank[pv] > self.rank[pu]: + pu, pv = pv, pu + self.parent[pv] = pu + self.rank[pu] += self.rank[pv] + return True + +class Solution: + def shortestBridge(self, grid: list[list[int]]) -> int: + n, direct = len(grid), [(0, 1), (0, -1), (1, 0), (-1, 0)] + dsu = DSU(n * n + 1) + + def idx(r, c): + return r * n + c + 1 + + for r in range(n): + for c in range(n): + if grid[r][c] == 1: + first_island = dsu.find(idx(r, c)) + if c + 1 < n and grid[r][c + 1] == 1: + dsu.union(idx(r, c), idx(r, c + 1)) + if r + 1 < n and grid[r + 1][c] == 1: + dsu.union(idx(r, c), idx(r + 1, c)) + + q = deque() + for r in range(n): + for c in range(n): + if grid[r][c] == 1: + if dsu.find(idx(r, c)) != first_island: + continue + for dx, dy in direct: + nr, nc = r + dx, c + dy + if 0 <= nr < n and 0 <= nc < n and grid[nr][nc] == 0: + q.append((r,c)) + break + + res = 0 + while q: + for _ in range(len(q)): + r, c = q.popleft() + for dx, dy in direct: + nr, nc = r + dx, c + dy + if 0 <= nr < n and 0 <= nc < n: + if grid[nr][nc] == 1 and dsu.union(idx(r, c), idx(nr, nc)): + return res + if grid[nr][nc] == 0: + grid[nr][nc] = 1 + dsu.union(idx(r, c), idx(nr, nc)) + q.append((nr, nc)) + res += 1 +``` + +```java +class DSU { + private int[] parent, rank; + + public DSU(int n) { + parent = new int[n]; + rank = new int[n]; + for (int i = 0; i < n; i++) parent[i] = i; + Arrays.fill(rank, 1); + } + + public int find(int node) { + if (parent[node] != node) { + parent[node] = find(parent[node]); + } + return parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + + if (rank[pv] > rank[pu]) { + int temp = pu; + pu = pv; + pv = temp; + } + parent[pv] = pu; + rank[pu] += rank[pv]; + return true; + } +} + +public class Solution { + private int n; + private int idx(int r, int c) { + return r * n + c + 1; + } + + public int shortestBridge(int[][] grid) { + n = grid.length; + int[][] direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + DSU dsu = new DSU(n * n + 1); + Queue q = new LinkedList<>(); + + + int firstIsland = -1; + for (int r = 0; r < n; r++) { + for (int c = 0; c < n; c++) { + if (grid[r][c] == 1) { + firstIsland = dsu.find(idx(r, c)); + if (c + 1 < n && grid[r][c + 1] == 1) + dsu.union(idx(r, c), idx(r, c + 1)); + if (r + 1 < n && grid[r + 1][c] == 1) + dsu.union(idx(r, c), idx(r + 1, c)); + } + } + } + + for (int r = 0; r < n; r++) { + for (int c = 0; c < n; c++) { + if (grid[r][c] == 1 && dsu.find(idx(r, c)) == firstIsland) { + for (int[] d : direct) { + int nr = r + d[0], nc = c + d[1]; + if (nr >= 0 && nc >= 0 && nr < n && nc < n && grid[nr][nc] == 0) { + q.offer(new int[]{r, c}); + break; + } + } + } + } + } + + int res = 0; + while (!q.isEmpty()) { + for (int i = q.size(); i > 0; i--) { + int[] cell = q.poll(); + int r = cell[0], c = cell[1]; + + for (int[] d : direct) { + int nr = r + d[0], nc = c + d[1]; + if (nr >= 0 && nc >= 0 && nr < n && nc < n) { + if (grid[nr][nc] == 1 && dsu.union(idx(r, c), idx(nr, nc))) { + return res; + } + if (grid[nr][nc] == 0) { + grid[nr][nc] = 1; + dsu.union(idx(r, c), idx(nr, nc)); + q.offer(new int[]{nr, nc}); + } + } + } + } + res++; + } + return res; + } +} +``` + +```cpp +class DSU { +public: + vector parent, rank; + + DSU(int n) : parent(n), rank(n, 1) { + for (int i = 0; i < n; i++) parent[i] = i; + } + + int find(int node) { + if (parent[node] != node) + parent[node] = find(parent[node]); + return parent[node]; + } + + bool unionSet(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (rank[pv] > rank[pu]) swap(pu, pv); + parent[pv] = pu; + rank[pu] += rank[pv]; + return true; + } +}; + +class Solution { +public: + int shortestBridge(vector>& grid) { + int N = grid.size(); + vector> direct = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + DSU dsu(N * N + 1); + queue> q; + + auto idx = [&](int r, int c) { + return r * N + c + 1; + }; + + int firstIsland = -1; + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1) { + firstIsland = dsu.find(idx(r, c)); + if (c + 1 < N && grid[r][c + 1] == 1) + dsu.unionSet(idx(r, c), idx(r, c + 1)); + if (r + 1 < N && grid[r + 1][c] == 1) + dsu.unionSet(idx(r, c), idx(r + 1, c)); + } + } + } + + for (int r = 0; r < N; r++) { + for (int c = 0; c < N; c++) { + if (grid[r][c] == 1 && dsu.find(idx(r, c)) == firstIsland) { + for (auto& d : direct) { + int nr = r + d[0], nc = c + d[1]; + if (nr >= 0 && nc >= 0 && nr < N && nc < N && grid[nr][nc] == 0) { + q.push({r, c}); + break; + } + } + } + } + } + + int res = 0; + while (!q.empty()) { + for (int i = q.size(); i > 0; i--) { + auto [r, c] = q.front();q.pop(); + for (auto& d : direct) { + int nr = r + d[0], nc = c + d[1]; + if (nr >= 0 && nc >= 0 && nr < N && nc < N) { + if (grid[nr][nc] == 1 && dsu.unionSet(idx(r, c), idx(nr, nc))) + return res; + if (grid[nr][nc] == 0) { + grid[nr][nc] = 1; + dsu.unionSet(idx(r, c), idx(nr, nc)); + q.push({nr, nc}); + } + } + } + } + res++; + } + return res; + } +}; +``` + +```javascript +class DSU { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.parent = Array.from({ length: n }, (_, i) => i); + this.rank = Array(n).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.parent[node] !== node) { + this.parent[node] = this.find(this.parent[node]); + } + return this.parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u), + pv = this.find(v); + if (pu === pv) return false; + + if (this.rank[pv] > this.rank[pu]) [pu, pv] = [pv, pu]; + this.parent[pv] = pu; + this.rank[pu] += this.rank[pv]; + return true; + } +} + +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + shortestBridge(grid) { + const N = grid.length; + const direct = [[0, 1], [0, -1], [1, 0], [-1, 0]]; + const dsu = new DSU(N * N + 1); + const q = new Queue(); + + const idx = (r, c) => r * N + c + 1; + + let firstIsland = -1; + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1) { + firstIsland = dsu.find(idx(r, c)); + if (c + 1 < N && grid[r][c + 1] === 1) + dsu.union(idx(r, c), idx(r, c + 1)); + if (r + 1 < N && grid[r + 1][c] === 1) + dsu.union(idx(r, c), idx(r + 1, c)); + } + } + } + + + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + if (grid[r][c] === 1 && dsu.find(idx(r, c)) === firstIsland) { + for (const [dx, dy] of direct) { + let nr = r + dx, + nc = c + dy; + if (nr >= 0 && nc >= 0 && nr < N && nc < N && grid[nr][nc] === 0) { + q.push([r, c]); + break; + } + } + } + } + } + + let res = 0; + while (!q.isEmpty()) { + for (let i = q.size(); i > 0; i--) { + const [r, c] = q.pop(); + for (let [dx, dy] of direct) { + let nr = r + dx, + nc = c + dy; + if (nr >= 0 && nc >= 0 && nr < N && nc < N) { + if (grid[nr][nc] === 1 && dsu.union(idx(r, c), idx(nr, nc))) { + return res; + } + if (grid[nr][nc] === 0) { + grid[nr][nc] = 1; + dsu.union(idx(r, c), idx(nr, nc)); + q.push([nr, nc]); + } + } + } + } + res++; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/shortest-common-supersequence.md b/articles/shortest-common-supersequence.md new file mode 100644 index 000000000..6390ec39d --- /dev/null +++ b/articles/shortest-common-supersequence.md @@ -0,0 +1,803 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def shortestCommonSupersequence(self, str1: str, str2: str) -> str: + cache = [[None] * (len(str2) + 1) for _ in range(len(str1) + 1)] + str1 = list(str1) + str2 = list(str2) + + def dfs(i: int, j: int) -> list: + if cache[i][j] is not None: + return cache[i][j] + if i == len(str1): + cache[i][j] = str2[j:][::-1] + return cache[i][j] + if j == len(str2): + cache[i][j] = str1[i:][::-1] + return cache[i][j] + + if str1[i] == str2[j]: + res = dfs(i + 1, j + 1) + [str1[i]] + else: + s1 = dfs(i + 1, j) + s2 = dfs(i, j + 1) + if len(s1) < len(s2): + res = s1 + [str1[i]] + else: + res = s2 + [str2[j]] + + cache[i][j] = res + return res + + return ''.join(reversed(dfs(0, 0))) +``` + +```java +public class Solution { + private List[][] cache; + private int n, m; + + public String shortestCommonSupersequence(String str1, String str2) { + n = str1.length(); + m = str2.length(); + cache = new ArrayList[n + 1][m + 1]; + + List res = dfs(0, 0, str1, str2); + StringBuilder sb = new StringBuilder(); + for (int k = res.size() - 1; k >= 0; k--) sb.append(res.get(k)); + return sb.toString(); + } + + private List dfs(int i, int j, String str1, String str2) { + if (cache[i][j] != null) return cache[i][j]; + if (i == n) { + List res = new ArrayList<>(); + for (int k = m - 1; k >= j; k--) { + res.add(str2.charAt(k)); + } + cache[i][j] = res; + return res; + } + if (j == m) { + List res = new ArrayList<>(); + for (int k = n - 1; k >= i; k--) { + res.add(str1.charAt(k)); + } + cache[i][j] = res; + return res; + } + + List res; + if (str1.charAt(i) == str2.charAt(j)) { + res = new ArrayList<>(dfs(i + 1, j + 1, str1, str2)); + res.add(str1.charAt(i)); + } else { + List s1 = dfs(i + 1, j, str1, str2); + List s2 = dfs(i, j + 1, str1, str2); + + if (s1.size() < s2.size()) { + res = new ArrayList<>(s1); + res.add(str1.charAt(i)); + } else { + res = new ArrayList<>(s2); + res.add(str2.charAt(j)); + } + } + + cache[i][j] = res; + return res; + } +} +``` + +```cpp +class Solution { +public: + string shortestCommonSupersequence(const string &str1, const string &str2) { + n = str1.size(); + m = str2.size(); + cache.resize(n + 1, vector(m + 1, "")); + cacheUsed.resize(n + 1, vector(m + 1, false)); + + string res = dfs(0, 0, str1, str2); + reverse(res.begin(), res.end()); + return res; + } + +private: + int n, m; + vector> cache; + vector> cacheUsed; + + string dfs(int i, int j, const string &str1, const string &str2) { + if (cacheUsed[i][j]) { + return cache[i][j]; + } + cacheUsed[i][j] = true; + + if (i == n) { + string tail = str2.substr(j); + reverse(tail.begin(), tail.end()); + cache[i][j] = tail; + return tail; + } + if (j == m) { + string tail = str1.substr(i); + reverse(tail.begin(), tail.end()); + cache[i][j] = tail; + return tail; + } + + if (str1[i] == str2[j]) { + string temp = dfs(i + 1, j + 1, str1, str2); + temp.push_back(str1[i]); + cache[i][j] = temp; + } else { + string s1 = dfs(i + 1, j, str1, str2); + string s2 = dfs(i, j + 1, str1, str2); + if (s1.size() < s2.size()) { + s1.push_back(str1[i]); + cache[i][j] = s1; + } else { + s2.push_back(str2[j]); + cache[i][j] = s2; + } + } + return cache[i][j]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} str1 + * @param {string} str2 + * @return {string} + */ + shortestCommonSupersequence(str1, str2) { + const n = str1.length, m = str2.length; + const cache = Array.from({ length: n + 1 }, () => + Array(m + 1).fill(null) + ); + + const dfs = (i, j) => { + if (cache[i][j] !== null) return cache[i][j]; + if (i === n) { + let arr = str2.slice(j).split(''); + arr.reverse(); + cache[i][j] = arr; + return arr; + } + if (j === m) { + let arr = str1.slice(i).split(''); + arr.reverse(); + cache[i][j] = arr; + return arr; + } + let res; + if (str1[i] === str2[j]) { + res = [...dfs(i + 1, j + 1)]; + res.push(str1[i]); + } else { + const s1 = dfs(i + 1, j); + const s2 = dfs(i, j + 1); + if (s1.length < s2.length) { + res = [...s1]; + res.push(str1[i]); + } else { + res = [...s2]; + res.push(str2[j]); + } + } + cache[i][j] = res; + return res; + }; + + return dfs(0, 0).reverse().join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m * min(n, m))$ +* Space complexity: $O(n * m * min(n, m))$ + +> Where $n$ and $m$ are the lengths of the strings $str1$ and $str2$ respectively. + +--- + +## 2. Dynamic Programming (Top-Down) + Tracing + +::tabs-start + +```python +class Solution: + def shortestCommonSupersequence(self, str1: str, str2: str) -> str: + n, m = len(str1), len(str2) + dp = [[-1] * (m + 1) for _ in range(n + 1)] + + def dfs(i, j): + if dp[i][j] != -1: + return dp[i][j] + if i == n: + dp[i][j] = m - j + return dp[i][j] + if j == m: + dp[i][j] = n - i + return dp[i][j] + if str1[i] == str2[j]: + dp[i][j] = 1 + dfs(i + 1, j + 1) + else: + dp[i][j] = 1 + min(dfs(i + 1, j), dfs(i, j + 1)) + return dp[i][j] + + dfs(0, 0) + + def build_scs(i, j): + res = [] + while i < n or j < m: + if i == n: + res.extend(str2[j:]) + break + if j == m: + res.extend(str1[i:]) + break + if str1[i] == str2[j]: + res.append(str1[i]) + i += 1 + j += 1 + elif dp[i + 1][j] < dp[i][j + 1]: + res.append(str1[i]) + i += 1 + else: + res.append(str2[j]) + j += 1 + return res + + return ''.join(build_scs(0, 0)) +``` + +```java +public class Solution { + private int[][] dp; + private int n, m; + + public String shortestCommonSupersequence(String str1, String str2) { + n = str1.length(); + m = str2.length(); + dp = new int[n + 1][m + 1]; + + for (int[] row : dp) { + Arrays.fill(row, -1); + } + + dfs(0, 0, str1, str2); + + return buildSCS(str1, str2); + } + + private int dfs(int i, int j, String str1, String str2) { + if (dp[i][j] != -1) return dp[i][j]; + if (i == n) return dp[i][j] = m - j; + if (j == m) return dp[i][j] = n - i; + + if (str1.charAt(i) == str2.charAt(j)) { + dp[i][j] = 1 + dfs(i + 1, j + 1, str1, str2); + } else { + dp[i][j] = 1 + Math.min(dfs(i + 1, j, str1, str2), dfs(i, j + 1, str1, str2)); + } + return dp[i][j]; + } + + private String buildSCS(String str1, String str2) { + StringBuilder res = new StringBuilder(); + int i = 0, j = 0; + + while (i < n || j < m) { + if (i == n) { + res.append(str2.substring(j)); + break; + } + if (j == m) { + res.append(str1.substring(i)); + break; + } + if (str1.charAt(i) == str2.charAt(j)) { + res.append(str1.charAt(i)); + i++; + j++; + } else if (dp[i + 1][j] < dp[i][j + 1]) { + res.append(str1.charAt(i)); + i++; + } else { + res.append(str2.charAt(j)); + j++; + } + } + + return res.toString(); + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + int n, m; + + int dfs(int i, int j, const string& str1, const string& str2) { + if (dp[i][j] != -1) return dp[i][j]; + if (i == n) return dp[i][j] = m - j; + if (j == m) return dp[i][j] = n - i; + + if (str1[i] == str2[j]) { + dp[i][j] = 1 + dfs(i + 1, j + 1, str1, str2); + } else { + dp[i][j] = 1 + min(dfs(i + 1, j, str1, str2), dfs(i, j + 1, str1, str2)); + } + return dp[i][j]; + } + + string buildSCS(const string& str1, const string& str2) { + string res; + int i = 0, j = 0; + + while (i < n || j < m) { + if (i == n) { + res += str2.substr(j); + break; + } + if (j == m) { + res += str1.substr(i); + break; + } + if (str1[i] == str2[j]) { + res += str1[i]; + i++; + j++; + } else if (dp[i + 1][j] < dp[i][j + 1]) { + res += str1[i]; + i++; + } else { + res += str2[j]; + j++; + } + } + + return res; + } + +public: + string shortestCommonSupersequence(string str1, string str2) { + n = str1.size(); + m = str2.size(); + dp = vector>(n + 1, vector(m + 1, -1)); + + dfs(0, 0, str1, str2); + return buildSCS(str1, str2); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} str1 + * @param {string} str2 + * @return {string} + */ + shortestCommonSupersequence(str1, str2) { + const n = str1.length, m = str2.length; + const dp = Array.from({ length: n + 1 }, () => + Array(m + 1).fill(-1) + ); + + const dfs = (i, j) => { + if (dp[i][j] !== -1) return dp[i][j]; + if (i === n) return (dp[i][j] = m - j); + if (j === m) return (dp[i][j] = n - i); + + if (str1[i] === str2[j]) { + dp[i][j] = 1 + dfs(i + 1, j + 1); + } else { + dp[i][j] = 1 + Math.min(dfs(i + 1, j), dfs(i, j + 1)); + } + return dp[i][j]; + }; + + dfs(0, 0); + + const buildSCS = () => { + const res = []; + let i = 0, j = 0; + + while (i < n || j < m) { + if (i === n) { + res.push(...str2.slice(j)); + break; + } + if (j === m) { + res.push(...str1.slice(i)); + break; + } + if (str1[i] === str2[j]) { + res.push(str1[i]); + i++; + j++; + } else if (dp[i + 1][j] < dp[i][j + 1]) { + res.push(str1[i]); + i++; + } else { + res.push(str2[j]); + j++; + } + } + + return res.join(''); + }; + + return buildSCS(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ and $m$ are the lengths of the strings $str1$ and $str2$ respectively. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def shortestCommonSupersequence(self, str1: str, str2: str) -> str: + n, m = len(str1), len(str2) + dp = [[""] * (m + 1) for _ in range(n + 1)] + + for i in range(n + 1): + for j in range(m + 1): + if i == 0: + dp[i][j] = str2[:j] + elif j == 0: + dp[i][j] = str1[:i] + elif str1[i - 1] == str2[j - 1]: + dp[i][j] = dp[i - 1][j - 1] + str1[i - 1] + else: + if len(dp[i - 1][j]) < len(dp[i][j - 1]): + dp[i][j] = dp[i - 1][j] + str1[i - 1] + else: + dp[i][j] = dp[i][j - 1] + str2[j - 1] + + return dp[n][m] +``` + +```java +public class Solution { + public String shortestCommonSupersequence(String str1, String str2) { + int n = str1.length(), m = str2.length(); + String[][] dp = new String[n + 1][m + 1]; + + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= m; j++) { + if (i == 0) { + dp[i][j] = str2.substring(0, j); + } else if (j == 0) { + dp[i][j] = str1.substring(0, i); + } else if (str1.charAt(i - 1) == str2.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1] + str1.charAt(i - 1); + } else { + dp[i][j] = dp[i - 1][j].length() < dp[i][j - 1].length() ? + dp[i - 1][j] + str1.charAt(i - 1) : + dp[i][j - 1] + str2.charAt(j - 1); + } + } + } + + return dp[n][m]; + } +} +``` + +```cpp +class Solution { +public: + string shortestCommonSupersequence(string str1, string str2) { + int n = str1.size(), m = str2.size(); + vector> dp(n + 1, vector(m + 1)); + + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= m; j++) { + if (i == 0) { + dp[i][j] = str2.substr(0, j); + } else if (j == 0) { + dp[i][j] = str1.substr(0, i); + } else if (str1[i - 1] == str2[j - 1]) { + dp[i][j] = dp[i - 1][j - 1] + str1[i - 1]; + } else { + dp[i][j] = dp[i - 1][j].size() < dp[i][j - 1].size() ? + dp[i - 1][j] + str1[i - 1] : + dp[i][j - 1] + str2[j - 1]; + } + } + } + + return dp[n][m]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} str1 + * @param {string} str2 + * @return {string} + */ + shortestCommonSupersequence(str1, str2) { + const n = str1.length, m = str2.length; + const dp = Array.from({ length: n + 1 }, () => + Array(m + 1).fill("") + ); + + for (let i = 0; i <= n; i++) { + for (let j = 0; j <= m; j++) { + if (i === 0) { + dp[i][j] = str2.slice(0, j); + } else if (j === 0) { + dp[i][j] = str1.slice(0, i); + } else if (str1[i - 1] === str2[j - 1]) { + dp[i][j] = dp[i - 1][j - 1] + str1[i - 1]; + } else { + dp[i][j] = dp[i - 1][j].length < dp[i][j - 1].length ? + dp[i - 1][j] + str1[i - 1] : + dp[i][j - 1] + str2[j - 1]; + } + } + } + + return dp[n][m]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m * min(n, m))$ +* Space complexity: $O(n * m * min(n, m))$ + +> Where $n$ and $m$ are the lengths of the strings $str1$ and $str2$ respectively. + +--- + +## 4. Dynamic Programming (Bottom-Up) + Tracing + +::tabs-start + +```python +class Solution: + def shortestCommonSupersequence(self, str1: str, str2: str) -> str: + n, m = len(str1), len(str2) + dp = [[0] * (m + 1) for _ in range(n + 1)] + + for i in range(n + 1): + for j in range(m + 1): + if i == 0: + dp[i][j] = j + elif j == 0: + dp[i][j] = i + elif str1[i - 1] == str2[j - 1]: + dp[i][j] = 1 + dp[i - 1][j - 1] + else: + dp[i][j] = 1 + min(dp[i - 1][j], dp[i][j - 1]) + + res = [] + i, j = n, m + while i > 0 and j > 0: + if str1[i - 1] == str2[j - 1]: + res.append(str1[i - 1]) + i -= 1 + j -= 1 + elif dp[i - 1][j] < dp[i][j - 1]: + res.append(str1[i - 1]) + i -= 1 + else: + res.append(str2[j - 1]) + j -= 1 + + while i > 0: + res.append(str1[i - 1]) + i -= 1 + + while j > 0: + res.append(str2[j - 1]) + j -= 1 + + return ''.join(reversed(res)) +``` + +```java +public class Solution { + public String shortestCommonSupersequence(String str1, String str2) { + int n = str1.length(), m = str2.length(); + int[][] dp = new int[n + 1][m + 1]; + + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= m; j++) { + if (i == 0) { + dp[i][j] = j; + } else if (j == 0) { + dp[i][j] = i; + } else if (str1.charAt(i - 1) == str2.charAt(j - 1)) { + dp[i][j] = 1 + dp[i - 1][j - 1]; + } else { + dp[i][j] = 1 + Math.min(dp[i - 1][j], dp[i][j - 1]); + } + } + } + + StringBuilder res = new StringBuilder(); + int i = n, j = m; + while (i > 0 && j > 0) { + if (str1.charAt(i - 1) == str2.charAt(j - 1)) { + res.append(str1.charAt(i - 1)); + i--; + j--; + } else if (dp[i - 1][j] < dp[i][j - 1]) { + res.append(str1.charAt(i - 1)); + i--; + } else { + res.append(str2.charAt(j - 1)); + j--; + } + } + while (i > 0) { + res.append(str1.charAt(i - 1)); + i--; + } + while (j > 0) { + res.append(str2.charAt(j - 1)); + j--; + } + + return res.reverse().toString(); + } +} +``` + +```cpp +class Solution { +public: + string shortestCommonSupersequence(string str1, string str2) { + int n = str1.size(), m = str2.size(); + vector> dp(n + 1, vector(m + 1, 0)); + + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= m; j++) { + if (i == 0) { + dp[i][j] = j; + } else if (j == 0) { + dp[i][j] = i; + } else if (str1[i - 1] == str2[j - 1]) { + dp[i][j] = 1 + dp[i - 1][j - 1]; + } else { + dp[i][j] = 1 + min(dp[i - 1][j], dp[i][j - 1]); + } + } + } + + string res; + int i = n, j = m; + while (i > 0 && j > 0) { + if (str1[i - 1] == str2[j - 1]) { + res.push_back(str1[i - 1]); + i--; + j--; + } else if (dp[i - 1][j] < dp[i][j - 1]) { + res.push_back(str1[i - 1]); + i--; + } else { + res.push_back(str2[j - 1]); + j--; + } + } + + while (i > 0) { + res.push_back(str1[i - 1]); + i--; + } + + while (j > 0) { + res.push_back(str2[j - 1]); + j--; + } + + reverse(res.begin(), res.end()); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} str1 + * @param {string} str2 + * @return {string} + */ + shortestCommonSupersequence(str1, str2) { + const n = str1.length, m = str2.length; + const dp = Array.from({ length: n + 1 }, () => + Array(m + 1).fill(0) + ); + + for (let i = 0; i <= n; i++) { + for (let j = 0; j <= m; j++) { + if (i === 0) { + dp[i][j] = j; + } else if (j === 0) { + dp[i][j] = i; + } else if (str1[i - 1] === str2[j - 1]) { + dp[i][j] = 1 + dp[i - 1][j - 1]; + } else { + dp[i][j] = 1 + Math.min(dp[i - 1][j], dp[i][j - 1]); + } + } + } + + const res = []; + let i = n, j = m; + while (i > 0 && j > 0) { + if (str1[i - 1] === str2[j - 1]) { + res.push(str1[i - 1]); + i--; + j--; + } else if (dp[i - 1][j] < dp[i][j - 1]) { + res.push(str1[i - 1]); + i--; + } else { + res.push(str2[j - 1]); + j--; + } + } + + while (i > 0) { + res.push(str1[i - 1]); + i--; + } + + while (j > 0) { + res.push(str2[j - 1]); + j--; + } + + return res.reverse().join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ and $m$ are the lengths of the strings $str1$ and $str2$ respectively. \ No newline at end of file diff --git a/articles/shortest-path-in-binary-matrix.md b/articles/shortest-path-in-binary-matrix.md new file mode 100644 index 000000000..c0a87d383 --- /dev/null +++ b/articles/shortest-path-in-binary-matrix.md @@ -0,0 +1,475 @@ +## 1. Breadth First Search + +::tabs-start + +```python +class Solution: + def shortestPathBinaryMatrix(self, grid: list[list[int]]) -> int: + N = len(grid) + if grid[0][0] or grid[N - 1][N - 1]: + return -1 + + q = deque([(0, 0, 1)]) + visit = set((0, 0)) + direct = [(0, 1), (1, 0), (0, -1), (-1, 0), + (1, 1), (-1, -1), (1, -1), (-1, 1)] + + while q: + r, c, length = q.popleft() + if r == N - 1 and c == N - 1: + return length + + for dr, dc in direct: + nr, nc = r + dr, c + dc + if (0 <= nr < N and 0 <= nc < N and grid[nr][nc] == 0 and + (nr, nc) not in visit): + q.append((nr, nc, length + 1)) + visit.add((nr, nc)) + + return -1 +``` + +```java +public class Solution { + public int shortestPathBinaryMatrix(int[][] grid) { + int N = grid.length; + if (grid[0][0] == 1 || grid[N - 1][N - 1] == 1) return -1; + + int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}, + {1, 1}, {-1, -1}, {1, -1}, {-1, 1}}; + boolean[][] visit = new boolean[N][N]; + + Queue q = new LinkedList<>(); + q.offer(new int[]{0, 0, 1}); + visit[0][0] = true; + + while (!q.isEmpty()) { + int[] cell = q.poll(); + int r = cell[0], c = cell[1], length = cell[2]; + + if (r == N - 1 && c == N - 1) return length; + + for (int[] d : directions) { + int nr = r + d[0], nc = c + d[1]; + if (nr >= 0 && nc >= 0 && nr < N && nc < N && + grid[nr][nc] == 0 && !visit[nr][nc]) { + q.offer(new int[]{nr, nc, length + 1}); + visit[nr][nc] = true; + } + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int shortestPathBinaryMatrix(vector>& grid) { + int N = grid.size(); + if (grid[0][0] == 1 || grid[N - 1][N - 1] == 1) return -1; + + vector> directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}, + {1, 1}, {-1, -1}, {1, -1}, {-1, 1}}; + vector> visit(N, vector(N, false)); + + queue> q; + q.push({0, 0, 1}); + visit[0][0] = true; + + while (!q.empty()) { + auto [r, c, length] = q.front(); + q.pop(); + + if (r == N - 1 && c == N - 1) return length; + + for (auto [dr, dc] : directions) { + int nr = r + dr, nc = c + dc; + if (nr >= 0 && nc >= 0 && nr < N && nc < N && + grid[nr][nc] == 0 && !visit[nr][nc]) { + q.push({nr, nc, length + 1}); + visit[nr][nc] = true; + } + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + shortestPathBinaryMatrix(grid) { + const N = grid.length; + if (grid[0][0] === 1 || grid[N - 1][N - 1] === 1) return -1; + + const directions = [ + [0, 1], [1, 0], [0, -1], [-1, 0], + [1, 1], [-1, -1], [1, -1], [-1, 1] + ]; + const visit = Array.from({ length: N }, () => + Array(N).fill(false) + ); + + const q = new Queue([[0, 0, 1]]); + visit[0][0] = true; + + while (!q.isEmpty()) { + const [r, c, length] = q.pop(); + if (r === N - 1 && c === N - 1) return length; + + for (const [dr, dc] of directions) { + const nr = r + dr, nc = c + dc; + if (nr >= 0 && nc >= 0 && nr < N && nc < N && + grid[nr][nc] === 0 && !visit[nr][nc]) { + q.push([nr, nc, length + 1]); + visit[nr][nc] = true; + } + } + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Breadth First Search (Overwriting the Input) + +::tabs-start + +```python +class Solution: + def shortestPathBinaryMatrix(self, grid: list[list[int]]) -> int: + N = len(grid) + direct = [0, 1, 0, -1, 0, 1, 1, -1, -1, 1] + + if grid[0][0] or grid[N - 1][N - 1]: + return -1 + + q = deque([(0, 0)]) + grid[0][0] = 1 + + while q: + r, c = q.popleft() + dist = grid[r][c] + + if r == N - 1 and c == N - 1: + return dist + + for d in range(9): + nr, nc = r + direct[d], c + direct[d + 1] + if 0 <= nr < N and 0 <= nc < N and grid[nr][nc] == 0: + grid[nr][nc] = dist + 1 + q.append((nr, nc)) + + return -1 +``` + +```java +public class Solution { + public int shortestPathBinaryMatrix(int[][] grid) { + int N = grid.length; + int[] direct = {0, 1, 0, -1, 0, 1, 1, -1, -1, 1}; + + if (grid[0][0] == 1 || grid[N - 1][N - 1] == 1) + return -1; + + Queue q = new LinkedList<>(); + q.offer(new int[]{0, 0}); + grid[0][0] = 1; + + while (!q.isEmpty()) { + int[] cell = q.poll(); + int r = cell[0], c = cell[1]; + int dist = grid[r][c]; + + if (r == N - 1 && c == N - 1) + return dist; + + for (int d = 0; d < 9; d++) { + int nr = r + direct[d], nc = c + direct[d + 1]; + + if (nr >= 0 && nc >= 0 && nr < N && nc < N && grid[nr][nc] == 0) { + grid[nr][nc] = dist + 1; + q.offer(new int[]{nr, nc}); + } + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int shortestPathBinaryMatrix(vector>& grid) { + int N = grid.size(); + int direct[10] = {0, 1, 0, -1, 0, 1, 1, -1, -1, 1}; + + if (grid[0][0] || grid[N - 1][N - 1]) + return -1; + + queue> q; + q.push({0, 0}); + grid[0][0] = 1; + + while (!q.empty()) { + auto [r, c] = q.front(); + q.pop(); + int dist = grid[r][c]; + + if (r == N - 1 && c == N - 1) + return dist; + + for (int d = 0; d < 9; d++) { + int nr = r + direct[d], nc = c + direct[d + 1]; + + if (nr >= 0 && nc >= 0 && nr < N && nc < N && grid[nr][nc] == 0) { + grid[nr][nc] = dist + 1; + q.push({nr, nc}); + } + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + shortestPathBinaryMatrix(grid) { + const N = grid.length; + const direct = [0, 1, 0, -1, 0, 1, 1, -1, -1, 1]; + + if (grid[0][0] || grid[N - 1][N - 1]) + return -1; + + let q = [[0, 0]]; + grid[0][0] = 1; + + while (q.length) { + let [r, c] = q.shift(); + let dist = grid[r][c]; + + if (r === N - 1 && c === N - 1) + return dist; + + for (let d = 0; d < 9; d++) { + let nr = r + direct[d], nc = c + direct[d + 1]; + + if (nr >= 0 && nc >= 0 && nr < N && nc < N && grid[nr][nc] === 0) { + grid[nr][nc] = dist + 1; + q.push([nr, nc]); + } + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Bidirectional Breadth First Search + +::tabs-start + +```python +class Solution: + def shortestPathBinaryMatrix(self, grid: list[list[int]]) -> int: + N = len(grid) + if grid[0][0] or grid[N - 1][N - 1]: + return -1 + if N == 1: + return 1 + + direct = [0, 1, 0, -1, 0, 1, 1, -1, -1, 1] + q1 = deque([(0, 0)]) + q2 = deque([(N - 1, N - 1)]) + grid[0][0] = -1 + grid[N - 1][N - 1] = -2 + + res = 2 + start, end = -1, -2 + while q1 and q2: + for _ in range(len(q1)): + r, c = q1.popleft() + for d in range(9): + nr, nc = r + direct[d], c + direct[d + 1] + if 0 <= nr < N and 0 <= nc < N: + if grid[nr][nc] == end: + return res + if grid[nr][nc] == 0: + grid[nr][nc] = start + q1.append((nr, nc)) + + q1, q2 = q2, q1 + start, end = end, start + res += 1 + + return -1 +``` + +```java +public class Solution { + public int shortestPathBinaryMatrix(int[][] grid) { + int N = grid.length; + if (grid[0][0] == 1 || grid[N - 1][N - 1] == 1) return -1; + if (N == 1) return 1; + + int[] direct = {0, 1, 0, -1, 0, 1, 1, -1, -1, 1}; + Queue q1 = new LinkedList<>(), q2 = new LinkedList<>(); + q1.offer(new int[]{0, 0}); + q2.offer(new int[]{N - 1, N - 1}); + grid[0][0] = -1; + grid[N - 1][N - 1] = -2; + + int res = 2, start = -1, end = -2; + while (!q1.isEmpty() && !q2.isEmpty()) { + for (int i = q1.size(); i > 0; i--) { + int[] cell = q1.poll(); + int r = cell[0], c = cell[1]; + + for (int d = 0; d < 9; d++) { + int nr = r + direct[d], nc = c + direct[d + 1]; + if (nr >= 0 && nc >= 0 && nr < N && nc < N) { + if (grid[nr][nc] == end) return res; + if (grid[nr][nc] == 0) { + grid[nr][nc] = start; + q1.offer(new int[]{nr, nc}); + } + } + } + } + Queue temp = q1; + q1 = q2; + q2 = temp; + int tempVal = start; + start = end; + end = tempVal; + res++; + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int shortestPathBinaryMatrix(vector>& grid) { + int N = grid.size(); + if (grid[0][0] || grid[N - 1][N - 1]) return -1; + if (N == 1) return 1; + + int direct[10] = {0, 1, 0, -1, 0, 1, 1, -1, -1, 1}; + queue> q1, q2; + q1.push({0, 0}); + q2.push({N - 1, N - 1}); + grid[0][0] = -1; + grid[N - 1][N - 1] = -2; + + int res = 2, start = -1, end = -2; + while (!q1.empty() && !q2.empty()) { + for (int i = q1.size(); i > 0; i--) { + auto [r, c] = q1.front(); + q1.pop(); + + for (int d = 0; d < 9; d++) { + int nr = r + direct[d], nc = c + direct[d + 1]; + if (nr >= 0 && nc >= 0 && nr < N && nc < N) { + if (grid[nr][nc] == end) return res; + if (grid[nr][nc] == 0) { + grid[nr][nc] = start; + q1.push({nr, nc}); + } + } + } + } + swap(q1, q2); + swap(start, end); + res++; + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + shortestPathBinaryMatrix(grid) { + const N = grid.length; + if (grid[0][0] || grid[N - 1][N - 1]) return -1; + if (N === 1) return 1; + + const direct = [0, 1, 0, -1, 0, 1, 1, -1, -1, 1]; + let q1 = new Queue([[0, 0]]); + let q2 = new Queue([[N - 1, N - 1]]); + grid[0][0] = -1; + grid[N - 1][N - 1] = -2; + + let res = 2, start = -1, end = -2; + while (!q1.isEmpty() && !q2.isEmpty()) { + for (let i = q1.size(); i > 0; i--) { + const [r, c] = q1.pop(); + for (let d = 0; d < 9; d++) { + let nr = r + direct[d], nc = c + direct[d + 1]; + if (nr >= 0 && nc >= 0 && nr < N && nc < N) { + if (grid[nr][nc] === end) return res; + if (grid[nr][nc] === 0) { + grid[nr][nc] = start; + q1.push([nr, nc]); + } + } + } + } + [q1, q2] = [q2, q1]; + [start, end] = [end, start]; + res++; + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/shortest-path-with-alternating-colors.md b/articles/shortest-path-with-alternating-colors.md new file mode 100644 index 000000000..f5d927069 --- /dev/null +++ b/articles/shortest-path-with-alternating-colors.md @@ -0,0 +1,543 @@ +## 1. Breadth First Search - I + +::tabs-start + +```python +class Solution: + def shortestAlternatingPaths(self, n: int, redEdges: list[list[int]], blueEdges: list[list[int]]) -> list[int]: + red, blue = defaultdict(list), defaultdict(list) + + for src, dst in redEdges: + red[src].append(dst) + + for src, dst in blueEdges: + blue[src].append(dst) + + answer = [-1 for _ in range(n)] + q = deque() + q.append((0, 0, None)) # [node, length, prev_edge_color] + visit = set() + visit.add((0, None)) + + while q: + node, length, edgeColor = q.popleft() + if answer[node] == -1: + answer[node] = length + + if edgeColor != "RED": + for nei in red[node]: + if (nei, "RED") not in visit: + visit.add((nei, "RED")) + q.append((nei, length + 1, "RED")) + + if edgeColor != "BLUE": + for nei in blue[node]: + if (nei, "BLUE") not in visit: + visit.add((nei, "BLUE")) + q.append((nei, length + 1, "BLUE")) + + return answer +``` + +```java +public class Solution { + public int[] shortestAlternatingPaths(int n, int[][] redEdges, int[][] blueEdges) { + List[] red = new ArrayList[n], blue = new ArrayList[n]; + for (int i = 0; i < n; i++) { + red[i] = new ArrayList<>(); + blue[i] = new ArrayList<>(); + } + for (int[] edge : redEdges) red[edge[0]].add(edge[1]); + for (int[] edge : blueEdges) blue[edge[0]].add(edge[1]); + + int[] answer = new int[n]; + Arrays.fill(answer, -1); + Queue q = new LinkedList<>(); + q.offer(new int[]{0, 0, -1}); + Set visit = new HashSet<>(); + visit.add("0,-1"); + + while (!q.isEmpty()) { + int[] nodeData = q.poll(); + int node = nodeData[0], length = nodeData[1], edgeColor = nodeData[2]; + + if (answer[node] == -1) answer[node] = length; + + if (edgeColor != 0) { + for (int nei : red[node]) { + if (visit.add(nei + ",0")) { + q.offer(new int[]{nei, length + 1, 0}); + } + } + } + if (edgeColor != 1) { + for (int nei : blue[node]) { + if (visit.add(nei + ",1")) { + q.offer(new int[]{nei, length + 1, 1}); + } + } + } + } + return answer; + } +} +``` + +```cpp +class Solution { +public: + vector shortestAlternatingPaths(int n, vector>& redEdges, vector>& blueEdges) { + vector> red(n), blue(n); + for (auto& edge : redEdges) red[edge[0]].push_back(edge[1]); + for (auto& edge : blueEdges) blue[edge[0]].push_back(edge[1]); + + vector answer(n, -1); + queue> q; + q.push({0, 0, -1}); + unordered_set visit; + visit.insert("0,-1"); + + while (!q.empty()) { + vector nodeData = q.front(); + q.pop(); + int node = nodeData[0], length = nodeData[1], edgeColor = nodeData[2]; + + if (answer[node] == -1) answer[node] = length; + + if (edgeColor != 0) { + for (int nei : red[node]) { + string key = to_string(nei) + ",0"; + if (visit.insert(key).second) { + q.push({nei, length + 1, 0}); + } + } + } + if (edgeColor != 1) { + for (int nei : blue[node]) { + string key = to_string(nei) + ",1"; + if (visit.insert(key).second) { + q.push({nei, length + 1, 1}); + } + } + } + } + return answer; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} redEdges + * @param {number[][]} blueEdges + * @return {number[]} + */ + shortestAlternatingPaths(n, redEdges, blueEdges) { + const red = Array.from({ length: n }, () => []); + const blue = Array.from({ length: n }, () => []); + + for (const [src, dst] of redEdges) red[src].push(dst); + for (const [src, dst] of blueEdges) blue[src].push(dst); + + const answer = new Array(n).fill(-1); + const q = new Queue([[0, 0, null]]); + const visit = new Set(["0,null"]); + + while (!q.isEmpty()) { + const [node, length, edgeColor] = q.pop(); + if (answer[node] === -1) answer[node] = length; + + if (edgeColor !== "RED") { + for (const nei of red[node]) { + if (!visit.has(`${nei},RED`)) { + visit.add(`${nei},RED`); + q.push([nei, length + 1, "RED"]); + } + } + } + if (edgeColor !== "BLUE") { + for (const nei of blue[node]) { + if (!visit.has(`${nei},BLUE`)) { + visit.add(`${nei},BLUE`); + q.push([nei, length + 1, "BLUE"]); + } + } + } + } + return answer; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 2. Breadth First Search - II + +::tabs-start + +```python +class Solution: + def shortestAlternatingPaths(self, n: int, redEdges: list[list[int]], blueEdges: list[list[int]]) -> list[int]: + def buildGraph(edges): + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + return adj + + red, blue = buildGraph(redEdges), buildGraph(blueEdges) + adj = [red, blue] + INF = float("inf") + dist = [[INF] * 2 for _ in range(n)] + dist[0][0] = dist[0][1] = 0 + + q = deque([(0, 0), (0, 1)]) + while q: + node, color = q.popleft() + for nei in adj[color][node]: + if dist[nei][color ^ 1] > dist[node][color] + 1: + dist[nei][color ^ 1] = dist[node][color] + 1 + q.append((nei, color ^ 1)) + + answer = [0] + [-1] * (n - 1) + for i in range(1, n): + answer[i] = min(dist[i][0], dist[i][1]) + if answer[i] == INF: + answer[i] = -1 + return answer +``` + +```java +public class Solution { + public int[] shortestAlternatingPaths(int n, int[][] redEdges, int[][] blueEdges) { + List[][] adj = new ArrayList[2][n]; + adj[0] = buildGraph(n, redEdges); + adj[1] = buildGraph(n, blueEdges); + + int INF = Integer.MAX_VALUE; + int[][] dist = new int[n][2]; + + for (int i = 0; i < n; i++) Arrays.fill(dist[i], INF); + dist[0][0] = dist[0][1] = 0; + + Queue q = new LinkedList<>(); + q.offer(new int[]{0, 0}); + q.offer(new int[]{0, 1}); + + while (!q.isEmpty()) { + int[] cur = q.poll(); + int node = cur[0], color = cur[1]; + + for (int nei : adj[color][node]) { + if (dist[nei][color ^ 1] > dist[node][color] + 1) { + dist[nei][color ^ 1] = dist[node][color] + 1; + q.offer(new int[]{nei, color ^ 1}); + } + } + } + + int[] answer = new int[n]; + for (int i = 0; i < n; i++) { + answer[i] = Math.min(dist[i][0], dist[i][1]); + if (answer[i] == INF) answer[i] = -1; + } + return answer; + } + + private List[] buildGraph(int n, int[][] edges) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + } + return adj; + } +} +``` + +```cpp +class Solution { +public: + vector shortestAlternatingPaths(int n, vector>& redEdges, vector>& blueEdges) { + vector> red = buildGraph(n, redEdges); + vector> blue = buildGraph(n, blueEdges); + vector> adj[] = {red, blue}; + + const int INF = 1e6; + vector> dist(n, vector(2, INF)); + dist[0][0] = dist[0][1] = 0; + + queue> q; + q.push({0, 0}); + q.push({0, 1}); + + while (!q.empty()) { + auto [node, color] = q.front();q.pop(); + for (int nei : adj[color][node]) { + if (dist[nei][color ^ 1] > dist[node][color] + 1) { + dist[nei][color ^ 1] = dist[node][color] + 1; + q.push({nei, color ^ 1}); + } + } + } + + vector answer(n, -1); + for (int i = 0; i < n; i++) { + answer[i] = min(dist[i][0], dist[i][1]); + if (answer[i] == INF) answer[i] = -1; + } + return answer; + } + +private: + vector> buildGraph(int n, vector>& edges) { + vector> adj(n); + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + } + return adj; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} redEdges + * @param {number[][]} blueEdges + * @return {number[]} + */ + shortestAlternatingPaths(n, redEdges, blueEdges) { + const red = this.buildGraph(n, redEdges); + const blue = this.buildGraph(n, blueEdges); + const adj = [red, blue]; + const INF = 1e6; + const dist = Array.from({ length: n }, () => [INF, INF]); + dist[0][0] = dist[0][1] = 0; + + const q = new Queue([[0, 0], [0, 1]]); + while (!q.isEmpty()) { + const [node, color] = q.pop(); + for (const nei of adj[color][node]) { + if (dist[nei][color ^ 1] > dist[node][color] + 1) { + dist[nei][color ^ 1] = dist[node][color] + 1; + q.push([nei, color ^ 1]); + } + } + } + + return Array.from({ length: n }, (_, i) => { + let minDist = Math.min(dist[i][0], dist[i][1]); + return minDist === INF ? -1 : minDist; + }); + } + + /** + * @param {number} n + * @param {number[][]} edges + * @return {number[][]} + */ + buildGraph(n, edges) { + const adj = Array.from({ length: n }, () => []); + for (const [u, v] of edges) { + adj[u].push(v); + } + return adj; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. + +--- + +## 3. Depth First Search + +::tabs-start + +```python +class Solution: + def shortestAlternatingPaths(self, n: int, redEdges: list[list[int]], blueEdges: list[list[int]]) -> list[int]: + def buildGraph(edges): + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + return adj + + red, blue = buildGraph(redEdges), buildGraph(blueEdges) + adj = [red, blue] + INF = float("inf") + dist = [[INF] * 2 for _ in range(n)] + dist[0][0] = dist[0][1] = 0 + + def dfs(node, color): + for nei in adj[color][node]: + if dist[nei][color ^ 1] > dist[node][color] + 1: + dist[nei][color ^ 1] = dist[node][color] + 1 + dfs(nei, color ^ 1) + + dfs(0, 0) + dfs(0, 1) + + answer = [0] + [-1] * (n - 1) + for i in range(1, n): + answer[i] = min(dist[i][0], dist[i][1]) + if answer[i] == INF: + answer[i] = -1 + + return answer +``` + +```java +public class Solution { + public int[] shortestAlternatingPaths(int n, int[][] redEdges, int[][] blueEdges) { + List[][] adj = new ArrayList[2][n]; + adj[0] = buildGraph(n, redEdges); + adj[1] = buildGraph(n, blueEdges); + + int INF = Integer.MAX_VALUE; + int[][] dist = new int[n][2]; + for (int i = 0; i < n; i++) Arrays.fill(dist[i], INF); + dist[0][0] = dist[0][1] = 0; + + dfs(0, 0, adj, dist); + dfs(0, 1, adj, dist); + + int[] answer = new int[n]; + for (int i = 0; i < n; i++) { + answer[i] = Math.min(dist[i][0], dist[i][1]); + if (answer[i] == INF) answer[i] = -1; + } + return answer; + } + + private void dfs(int node, int color, List[][] adj, int[][] dist) { + for (int nei : adj[color][node]) { + if (dist[nei][color ^ 1] > dist[node][color] + 1) { + dist[nei][color ^ 1] = dist[node][color] + 1; + dfs(nei, color ^ 1, adj, dist); + } + } + } + + private List[] buildGraph(int n, int[][] edges) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (int[] edge : edges) { + adj[edge[0]].add(edge[1]); + } + return adj; + } +} +``` + +```cpp +class Solution { +public: + vector shortestAlternatingPaths(int n, vector>& redEdges, vector>& blueEdges) { + vector> adj[2] = {buildGraph(n, redEdges), buildGraph(n, blueEdges)}; + + int INF = numeric_limits::max(); + vector> dist(n, vector(2, INF)); + dist[0][0] = dist[0][1] = 0; + + dfs(0, 0, adj, dist); + dfs(0, 1, adj, dist); + + vector answer(n, -1); + for (int i = 0; i < n; i++) { + answer[i] = min(dist[i][0], dist[i][1]); + if (answer[i] == INF) answer[i] = -1; + } + return answer; + } + +private: + void dfs(int node, int color, vector> adj[], vector>& dist) { + for (int nei : adj[color][node]) { + if (dist[nei][color ^ 1] > dist[node][color] + 1) { + dist[nei][color ^ 1] = dist[node][color] + 1; + dfs(nei, color ^ 1, adj, dist); + } + } + } + + vector> buildGraph(int n, vector>& edges) { + vector> adj(n); + for (auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + } + return adj; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} redEdges + * @param {number[][]} blueEdges + * @return {number[]} + */ + shortestAlternatingPaths(n, redEdges, blueEdges) { + const INF = Number.MAX_SAFE_INTEGER; + const adj = [Array.from({ length: n }, () => []), + Array.from({ length: n }, () => [])]; + + redEdges.forEach(([u, v]) => adj[0][u].push(v)); + blueEdges.forEach(([u, v]) => adj[1][u].push(v)); + + const dist = Array.from({ length: n }, () => [INF, INF]); + dist[0][0] = dist[0][1] = 0; + + const dfs = (node, color) => { + adj[color][node].forEach(nei => { + if (dist[nei][color ^ 1] > dist[node][color] + 1) { + dist[nei][color ^ 1] = dist[node][color] + 1; + dfs(nei, color ^ 1); + } + }); + }; + + dfs(0, 0); + dfs(0, 1); + + return dist.map(([red, blue]) => { + let res = Math.min(red, blue); + return res === INF ? -1 : res; + }); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges. \ No newline at end of file diff --git a/articles/shuffle-the-array.md b/articles/shuffle-the-array.md new file mode 100644 index 000000000..eded033b7 --- /dev/null +++ b/articles/shuffle-the-array.md @@ -0,0 +1,257 @@ +## 1. Iteration (Extra Space) + +::tabs-start + +```python +class Solution: + def shuffle(self, nums: List[int], n: int) -> List[int]: + res = [] + for i in range(n): + res.append(nums[i]) + res.append(nums[i + n]) + return res +``` + +```java +public class Solution { + public int[] shuffle(int[] nums, int n) { + int[] res = new int[2 * n]; + int idx = 0; + for (int i = 0; i < n; i++) { + res[idx++] = nums[i]; + res[idx++] = nums[i + n]; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector shuffle(vector& nums, int n) { + vector res; + for (int i = 0; i < n; i++) { + res.push_back(nums[i]); + res.push_back(nums[i + n]); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} n + * @return {number[]} + */ + shuffle(nums) { + const res = []; + for (let i = 0; i < n; i++) { + res.push(nums[i]); + res.push(nums[i + n]); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ extra space. + +--- + +## 2. Multiplication And Modulo + +::tabs-start + +```python +class Solution: + def shuffle(self, nums: List[int], n: int) -> List[int]: + M = max(nums) + 1 + for i in range(2 * n): + if i % 2 == 0: + nums[i] += (nums[i // 2] % M) * M + else: + nums[i] += (nums[n + i // 2] % M) * M + + for i in range(2 * n): + nums[i] //= M + + return nums +``` + +```java +public class Solution { + public int[] shuffle(int[] nums, int n) { + int M = 1001; + for (int i = 0; i < 2 * n; i++) { + if (i % 2 == 0) { + nums[i] += (nums[i / 2] % M) * M; + } else { + nums[i] += (nums[n + i / 2] % M) * M; + } + } + for (int i = 0; i < 2 * n; i++) { + nums[i] /= M; + } + return nums; + } +} +``` + +```cpp +class Solution { +public: + vector shuffle(vector& nums, int n) { + int M = *max_element(nums.begin(), nums.end()) + 1; + for (int i = 0; i < 2 * n; i++) { + if (i % 2 == 0) { + nums[i] += (nums[i / 2] % M) * M; + } else { + nums[i] += (nums[n + i / 2] % M) * M; + } + } + for (int i = 0; i < 2 * n; i++) { + nums[i] /= M; + } + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} n + * @return {number[]} + */ + shuffle(nums) { + const M = Math.max(...nums) + 1; + for (let i = 0; i < 2 * n; i++) { + if (i % 2 === 0) { + nums[i] += (nums[i >> 1] % M) * M; + } else { + nums[i] += (nums[n + (i >> 1)] % M) * M; + } + } + for (let i = 0; i < 2 * n; i++) { + nums[i] = Math.floor(nums[i] / M); + } + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 3. Bit Manipulation + +::tabs-start + +```python +class Solution: + def shuffle(self, nums: List[int], n: int) -> List[int]: + for i in range(n): + nums[i] = (nums[i] << 10) | nums[i + n] # Store x, y in nums[i] + + j = 2 * n - 1 + for i in range(n - 1, -1, -1): + y = nums[i] & ((1 << 10) - 1) + x = nums[i] >> 10 + nums[j] = y + nums[j - 1] = x + j -= 2 + + return nums +``` + +```java +public class Solution { + public int[] shuffle(int[] nums, int n) { + for (int i = 0; i < n; i++) { + nums[i] = (nums[i] << 10) | nums[i + n]; // Store x, y in nums[i] + } + + int j = 2 * n - 1; + for (int i = n - 1; i >= 0; i--) { + int y = nums[i] & ((1 << 10) - 1); + int x = nums[i] >> 10; + nums[j] = y; + nums[j - 1] = x; + j -= 2; + } + + return nums; + } +} +``` + +```cpp +class Solution { +public: + vector shuffle(vector& nums, int n) { + for (int i = 0; i < n; i++) { + nums[i] = (nums[i] << 10) | nums[i + n]; // Store x, y in nums[i] + } + + int j = 2 * n - 1; + for (int i = n - 1; i >= 0; i--) { + int y = nums[i] & ((1 << 10) - 1); + int x = nums[i] >> 10; + nums[j] = y; + nums[j - 1] = x; + j -= 2; + } + + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} n + * @return {number[]} + */ + shuffle(nums) { + for (let i = 0; i < n; i++) { + nums[i] = (nums[i] << 10) | nums[i + n]; // Store x, y in nums[i] + } + + let j = 2 * n - 1; + for (let i = n - 1; i >= 0; i--) { + let y = nums[i] & ((1 << 10) - 1); + let x = nums[i] >> 10; + nums[j] = y; + nums[j - 1] = x; + j -= 2; + } + + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/sign-of-the-product-of-an-array.md b/articles/sign-of-the-product-of-an-array.md new file mode 100644 index 000000000..76922c8d0 --- /dev/null +++ b/articles/sign-of-the-product-of-an-array.md @@ -0,0 +1,158 @@ +## 1. Count Negative Numbers + +::tabs-start + +```python +class Solution: + def arraySign(self, nums: list[int]) -> int: + neg = 0 + for num in nums: + if num == 0: + return 0 + neg += (1 if num < 0 else 0) + return -1 if neg % 2 else 1 +``` + +```java +public class Solution { + public int arraySign(int[] nums) { + int neg = 0; + for (int num : nums) { + if (num == 0) { + return 0; + } + if (num < 0) { + neg++; + } + } + return neg % 2 == 0 ? 1 : -1; + } +} +``` + +```cpp +class Solution { +public: + int arraySign(vector& nums) { + int neg = 0; + for (int num : nums) { + if (num == 0) { + return 0; + } + if (num < 0) { + neg++; + } + } + return neg % 2 == 0 ? 1 : -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + arraySign(nums) { + let neg = 0; + for (const num of nums) { + if (num === 0) { + return 0; + } + if (num < 0) { + neg++; + } + } + return neg % 2 === 0 ? 1 : -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Track the Sign of the Product + +::tabs-start + +```python +class Solution: + def arraySign(self, nums: list[int]) -> int: + sign = 1 + for num in nums: + if num == 0: + return 0 + if num < 0: + sign *= -1 + return sign +``` + +```java +public class Solution { + public int arraySign(int[] nums) { + int sign = 1; + for (int num : nums) { + if (num == 0) { + return 0; + } + if (num < 0) { + sign *= -1; + } + } + return sign; + } +} +``` + +```cpp +class Solution { +public: + int arraySign(vector& nums) { + int sign = 1; + for (int num : nums) { + if (num == 0) { + return 0; + } + if (num < 0) { + sign *= -1; + } + } + return sign; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + arraySign(nums) { + let sign = 1; + for (const num of nums) { + if (num === 0) { + return 0; + } + if (num < 0) { + sign *= -1; + } + } + return sign; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/simplify-path.md b/articles/simplify-path.md new file mode 100644 index 000000000..ae0ecb3ee --- /dev/null +++ b/articles/simplify-path.md @@ -0,0 +1,262 @@ +## 1. Stack - I + +::tabs-start + +```python +class Solution: + def simplifyPath(self, path: str) -> str: + stack = [] + cur = "" + + for c in path + "/": + if c == "/": + if cur == "..": + if stack: + stack.pop() + elif cur != "" and cur != ".": + stack.append(cur) + cur = "" + else: + cur += c + + return "/" + "/".join(stack) +``` + +```java +public class Solution { + public String simplifyPath(String path) { + Stack stack = new Stack<>(); + StringBuilder cur = new StringBuilder(); + + for (char c : (path + "/").toCharArray()) { + if (c == '/') { + if (cur.toString().equals("..")) { + if (!stack.isEmpty()) stack.pop(); + } else if (!cur.toString().equals("") && !cur.toString().equals(".")) { + stack.push(cur.toString()); + } + cur.setLength(0); + } else { + cur.append(c); + } + } + + return "/" + String.join("/", stack); + } +} +``` + +```cpp +class Solution { +public: + string simplifyPath(string path) { + vector stack; + string cur; + + for (char c : path + "/") { + if (c == '/') { + if (cur == "..") { + if (!stack.empty()) stack.pop_back(); + } else if (!cur.empty() && cur != ".") { + stack.push_back(cur); + } + cur.clear(); + } else { + cur += c; + } + } + + string result = "/"; + for (int i = 0; i < stack.size(); ++i) { + if (i > 0) result += "/"; + result += stack[i]; + } + + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} path + * @return {string} + */ + simplifyPath(path) { + const stack = []; + let cur = ""; + + for (const c of path + "/") { + if (c === "/") { + if (cur === "..") { + if (stack.length) stack.pop(); + } else if (cur !== "" && cur !== ".") { + stack.push(cur); + } + cur = ""; + } else { + cur += c; + } + } + + return "/" + stack.join("/"); + } +} +``` + +```csharp +public class Solution { + public string SimplifyPath(string path) { + Stack stack = new Stack(); + string cur = ""; + + foreach (char c in path + "/") { + if (c == '/') { + if (cur == "..") { + if (stack.Count > 0) stack.Pop(); + } else if (cur != "" && cur != ".") { + stack.Push(cur); + } + cur = ""; + } else { + cur += c; + } + } + + var result = new List(stack); + result.Reverse(); + return "/" + string.Join("/", result); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Stack - II + +::tabs-start + +```python +class Solution: + def simplifyPath(self, path: str) -> str: + stack = [] + paths = path.split("/") + + for cur in paths: + if cur == "..": + if stack: + stack.pop() + elif cur != "" and cur != ".": + stack.append(cur) + + return "/" + "/".join(stack) +``` + +```java +public class Solution { + public String simplifyPath(String path) { + Stack stack = new Stack<>(); + String[] paths = path.split("/"); + + for (String cur : paths) { + if (cur.equals("..")) { + if (!stack.isEmpty()) stack.pop(); + } else if (!cur.equals("") && !cur.equals(".")) { + stack.push(cur); + } + } + + return "/" + String.join("/", stack); + } +} +``` + +```cpp +class Solution { +public: + string simplifyPath(string path) { + vector stack; + string cur; + stringstream ss(path); + while (getline(ss, cur, '/')) { + if (cur.empty()) continue; + if (cur == "..") { + if (!stack.empty()) stack.pop_back(); + } else if (!cur.empty() && cur != ".") { + stack.push_back(cur); + } + } + + string result = "/"; + for (int i = 0; i < stack.size(); ++i) { + if (i > 0) result += "/"; + result += stack[i]; + } + + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} path + * @return {string} + */ + simplifyPath(path) { + const stack = []; + const paths = path.split("/"); + + for (const cur of paths) { + if (cur === "..") { + if (stack.length) { + stack.pop(); + } + } else if (cur !== "" && cur !== ".") { + stack.push(cur); + } + } + + return "/" + stack.join("/"); + } +} +``` + +```csharp +public class Solution { + public string SimplifyPath(string path) { + Stack stack = new Stack(); + string[] parts = path.Split('/'); + + foreach (string part in parts) { + if (part == "..") { + if (stack.Count > 0) { + stack.Pop(); + } + } else if (part != "" && part != ".") { + stack.Push(part); + } + } + + var result = new List(stack); + result.Reverse(); + return "/" + string.Join("/", result); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/single-element-in-a-sorted-array.md b/articles/single-element-in-a-sorted-array.md new file mode 100644 index 000000000..e2a5a8b0e --- /dev/null +++ b/articles/single-element-in-a-sorted-array.md @@ -0,0 +1,438 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def singleNonDuplicate(self, nums: List[int]) -> int: + n = len(nums) + for i in range(n): + if ((i and nums[i] == nums[i - 1]) or + (i < n - 1 and nums[i] == nums[i + 1]) + ): + continue + return nums[i] +``` + +```java +public class Solution { + public int singleNonDuplicate(int[] nums) { + int n = nums.length; + for (int i = 0; i < n; i++) { + if ((i > 0 && nums[i] == nums[i - 1]) || + (i < n - 1 && nums[i] == nums[i + 1])) { + continue; + } + return nums[i]; + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int singleNonDuplicate(vector& nums) { + int n = nums.size(); + for (int i = 0; i < n; i++) { + if ((i > 0 && nums[i] == nums[i - 1]) || + (i < n - 1 && nums[i] == nums[i + 1])) { + continue; + } + return nums[i]; + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + singleNonDuplicate(nums) { + const n = nums.length; + for (let i = 0; i < n; i++) { + if ((i > 0 && nums[i] === nums[i - 1]) || + (i < n - 1 && nums[i] === nums[i + 1])) { + continue; + } + return nums[i]; + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Brute Force (Bitwise Xor) + +::tabs-start + +```python +class Solution: + def singleNonDuplicate(self, nums: List[int]) -> int: + xorr = 0 + for num in nums: + xorr ^= num + return xorr +``` + +```java +public class Solution { + public int singleNonDuplicate(int[] nums) { + int xorr = 0; + for (int num : nums) { + xorr ^= num; + } + return xorr; + } +} +``` + +```cpp +class Solution { +public: + int singleNonDuplicate(vector& nums) { + int xorr = 0; + for (int num : nums) { + xorr ^= num; + } + return xorr; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + singleNonDuplicate(nums) { + let xorr = 0; + for (const num of nums) { + xorr ^= num; + } + return xorr; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Binary Search + +::tabs-start + +```python +class Solution: + def singleNonDuplicate(self, nums: List[int]) -> int: + l, r = 0, len(nums) - 1 + + while l <= r: + m = l + ((r - l) // 2) + if ((m - 1 < 0 or nums[m - 1] != nums[m]) and + (m + 1 == len(nums) or nums[m] != nums[m + 1])): + return nums[m] + + leftSize = m - 1 if nums[m - 1] == nums[m] else m + if leftSize % 2: + r = m - 1 + else: + l = m + 1 +``` + +```java +public class Solution { + public int singleNonDuplicate(int[] nums) { + int l = 0, r = nums.length - 1; + + while (l <= r) { + int m = l + (r - l) / 2; + if ((m - 1 < 0 || nums[m - 1] != nums[m]) && + (m + 1 == nums.length || nums[m] != nums[m + 1])) { + return nums[m]; + } + + int leftSize = (m - 1 >= 0 && nums[m - 1] == nums[m]) ? m - 1 : m; + if (leftSize % 2 == 1) { + r = m - 1; + } else { + l = m + 1; + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int singleNonDuplicate(vector& nums) { + int l = 0, r = nums.size() - 1; + + while (l <= r) { + int m = l + (r - l) / 2; + if ((m - 1 < 0 || nums[m - 1] != nums[m]) && + (m + 1 == nums.size() || nums[m] != nums[m + 1])) { + return nums[m]; + } + + int leftSize = (m - 1 >= 0 && nums[m - 1] == nums[m]) ? m - 1 : m; + if (leftSize % 2 == 1) { + r = m - 1; + } else { + l = m + 1; + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + singleNonDuplicate(nums) { + let l = 0, r = nums.length - 1; + + while (l <= r) { + let m = l + Math.floor((r - l) / 2); + if ((m - 1 < 0 || nums[m - 1] !== nums[m]) && + (m + 1 === nums.length || nums[m] !== nums[m + 1])) { + return nums[m]; + } + + let leftSize = (m - 1 >= 0 && nums[m - 1] === nums[m]) ? m - 1 : m; + if (leftSize % 2 === 1) { + r = m - 1; + } else { + l = m + 1; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Binary Search On Even Indexes + +::tabs-start + +```python +class Solution: + def singleNonDuplicate(self, nums: List[int]) -> int: + l, r = 0, len(nums) - 1 + + while l < r: + m = l + (r - l) // 2 + if m & 1: + m -= 1 + if nums[m] != nums[m + 1]: + r = m + else: + l = m + 2 + + return nums[l] +``` + +```java +public class Solution { + public int singleNonDuplicate(int[] nums) { + int l = 0, r = nums.length - 1; + + while (l < r) { + int m = l + (r - l) / 2; + if ((m & 1) == 1) { + m--; + } + if (nums[m] != nums[m + 1]) { + r = m; + } else { + l = m + 2; + } + } + + return nums[l]; + } +} +``` + +```cpp +class Solution { +public: + int singleNonDuplicate(vector& nums) { + int l = 0, r = nums.size() - 1; + + while (l < r) { + int m = l + (r - l) / 2; + if (m & 1) { + m--; + } + if (nums[m] != nums[m + 1]) { + r = m; + } else { + l = m + 2; + } + } + + return nums[l]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + singleNonDuplicate(nums) { + let l = 0, r = nums.length - 1; + + while (l < r) { + let m = Math.floor(l + (r - l) / 2); + if (m & 1) { + m--; + } + if (nums[m] !== nums[m + 1]) { + r = m; + } else { + l = m + 2; + } + } + + return nums[l]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Binary Search + Bit Manipulation + +::tabs-start + +```python +class Solution: + def singleNonDuplicate(self, nums: List[int]) -> int: + l, r = 0, len(nums) - 1 + + while l < r: + m = (l + r) >> 1 + if nums[m] != nums[m ^ 1]: + r = m + else: + l = m + 1 + + return nums[l] +``` + +```java +public class Solution { + public int singleNonDuplicate(int[] nums) { + int l = 0, r = nums.length - 1; + + while (l < r) { + int m = (l + r) >> 1; + if (nums[m] != nums[m ^ 1]) { + r = m; + } else { + l = m + 1; + } + } + + return nums[l]; + } +} +``` + +```cpp +class Solution { +public: + int singleNonDuplicate(vector& nums) { + int l = 0, r = nums.size() - 1; + + while (l < r) { + int m = (l + r) >> 1; + if (nums[m] != nums[m ^ 1]) { + r = m; + } else { + l = m + 1; + } + } + + return nums[l]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + singleNonDuplicate(nums) { + let l = 0, r = nums.length - 1; + + while (l < r) { + let m = (l + r) >> 1; + if (nums[m] !== nums[m ^ 1]) { + r = m; + } else { + l = m + 1; + } + } + + return nums[l]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/single-number-iii.md b/articles/single-number-iii.md new file mode 100644 index 000000000..5e714092d --- /dev/null +++ b/articles/single-number-iii.md @@ -0,0 +1,608 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def singleNumber(self, nums: List[int]) -> List[int]: + n, res = len(nums), [] + + for i in range(n): + flag = True + for j in range(n): + if i != j and nums[i] == nums[j]: + flag = False + break + + if flag: + res.append(nums[i]) + if len(res) == 2: + break + + return res +``` + +```java +public class Solution { + public int[] singleNumber(int[] nums) { + int n = nums.length; + List res = new ArrayList<>(); + + for (int i = 0; i < n; i++) { + boolean flag = true; + for (int j = 0; j < n; j++) { + if (i != j && nums[i] == nums[j]) { + flag = false; + break; + } + } + + if (flag) { + res.add(nums[i]); + if (res.size() == 2) { + break; + } + } + } + + return new int[] {res.get(0), res.get(1)}; + } +} +``` + +```cpp +class Solution { +public: + vector singleNumber(vector& nums) { + int n = nums.size(); + vector res; + + for (int i = 0; i < n; i++) { + bool flag = true; + for (int j = 0; j < n; j++) { + if (i != j && nums[i] == nums[j]) { + flag = false; + break; + } + } + + if (flag) { + res.push_back(nums[i]); + if (res.size() == 2) { + break; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + singleNumber(nums) { + const n = nums.length; + const res = []; + + for (let i = 0; i < n; i++) { + let flag = true; + for (let j = 0; j < n; j++) { + if (i !== j && nums[i] === nums[j]) { + flag = false; + break; + } + } + + if (flag) { + res.push(nums[i]); + if (res.length === 2) { + break; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def singleNumber(self, nums: List[int]) -> List[int]: + count = {} + for num in nums: + count[num] = 1 + count.get(num, 0) + + return [k for k in count if count[k] == 1] +``` + +```java +public class Solution { + public int[] singleNumber(int[] nums) { + Map count = new HashMap<>(); + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + ArrayList res = new ArrayList<>(); + for (int key : count.keySet()) { + if (count.get(key) == 1) { + res.add(key); + } + } + + return new int[] {res.get(0), res.get(1)}; + } +} +``` + +```cpp +class Solution { +public: + vector singleNumber(vector& nums) { + unordered_map count; + for (int num : nums) { + count[num]++; + } + + vector res; + for (const auto& pair : count) { + if (pair.second == 1) { + res.push_back(pair.first); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + singleNumber(nums) { + const count = new Map(); + for (const num of nums) { + count.set(num, (count.get(num) || 0) + 1); + } + + const res = []; + for (const [key, value] of count) { + if (value === 1) { + res.push(key); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Hash Set + +::tabs-start + +```python +class Solution: + def singleNumber(self, nums: List[int]) -> List[int]: + seen = set() + for num in nums: + if num in seen: + seen.remove(num) + else: + seen.add(num) + return list(seen) +``` + +```java +public class Solution { + public int[] singleNumber(int[] nums) { + HashSet seen = new HashSet<>(); + for (int num : nums) { + if (seen.contains(num)) { + seen.remove(num); + } else { + seen.add(num); + } + } + + int[] res = new int[2]; + int index = 0; + for (int num : seen) { + res[index++] = num; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector singleNumber(vector& nums) { + unordered_set seen; + for (int& num : nums) { + if (seen.count(num)) { + seen.erase(num); + } else { + seen.insert(num); + } + } + + return vector(seen.begin(), seen.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + singleNumber(nums) { + const seen = new Set(); + for (const num of nums) { + if (seen.has(num)) { + seen.delete(num); + } else { + seen.add(num); + } + } + + return Array.from(seen); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Sorting + +::tabs-start + +```python +class Solution: + def singleNumber(self, nums: List[int]) -> List[int]: + res, n = [], len(nums) + nums.sort() + + for i in range(n): + if ((i > 0 and nums[i] == nums[i - 1]) or + (i + 1 < n and nums[i] == nums[i + 1])): + continue + res.append(nums[i]) + + return res +``` + +```java +public class Solution { + public int[] singleNumber(int[] nums) { + Arrays.sort(nums); + List res = new ArrayList<>(); + int n = nums.length; + + for (int i = 0; i < n; i++) { + if ((i > 0 && nums[i] == nums[i - 1]) || + (i + 1 < n && nums[i] == nums[i + 1])) { + continue; + } + res.add(nums[i]); + } + + return res.stream().mapToInt(i -> i).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector singleNumber(vector& nums) { + sort(nums.begin(), nums.end()); + vector res; + int n = nums.size(); + + for (int i = 0; i < n; i++) { + if ((i > 0 && nums[i] == nums[i - 1]) || + (i + 1 < n && nums[i] == nums[i + 1])) { + continue; + } + res.push_back(nums[i]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + singleNumber(nums) { + nums.sort((a, b) => a - b); + const res = []; + const n = nums.length; + + for (let i = 0; i < n; i++) { + if ((i > 0 && nums[i] === nums[i - 1]) || + (i + 1 < n && nums[i] === nums[i + 1])) { + continue; + } + res.push(nums[i]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 5. Bitwise XOR (Least Significant Bit) + +::tabs-start + +```python +class Solution: + def singleNumber(self, nums: List[int]) -> List[int]: + xor = 0 + for num in nums: + xor ^= num + + diff_bit = 1 + while not (xor & diff_bit): + diff_bit <<= 1 + + a = b = 0 + for num in nums: + if diff_bit & num: + a ^= num + else: + b ^= num + return [a, b] +``` + +```java +public class Solution { + public int[] singleNumber(int[] nums) { + int xor = 0; + for (int num : nums) { + xor ^= num; + } + + int diff_bit = 1; + while ((xor & diff_bit) == 0) { + diff_bit <<= 1; + } + + int a = 0, b = 0; + for (int num : nums) { + if ((num & diff_bit) != 0) { + a ^= num; + } else { + b ^= num; + } + } + return new int[]{a, b}; + } +} +``` + +```cpp +class Solution { +public: + vector singleNumber(vector& nums) { + int xor_all = 0; + for (int& num : nums) { + xor_all ^= num; + } + + int diff_bit = 1; + while ((xor_all & diff_bit) == 0) { + diff_bit <<= 1; + } + + int a = 0, b = 0; + for (int& num : nums) { + if (num & diff_bit) { + a ^= num; + } else { + b ^= num; + } + } + return {a, b}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + singleNumber(nums) { + let xor = 0; + for (const num of nums) { + xor ^= num; + } + + let diff_bit = 1; + while ((xor & diff_bit) === 0) { + diff_bit <<= 1; + } + + let a = 0, b = 0; + for (const num of nums) { + if (num & diff_bit) { + a ^= num; + } else { + b ^= num; + } + } + return [a, b]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 6. Bitwise XOR (Most Significant Bit) + +::tabs-start + +```python +class Solution: + def singleNumber(self, nums: List[int]) -> List[int]: + xor = 0 + for num in nums: + xor ^= num + + diff_bit = xor & (-xor) + + a = b = 0 + for num in nums: + if diff_bit & num: + a ^= num + else: + b ^= num + return [a, b] +``` + +```java +public class Solution { + public int[] singleNumber(int[] nums) { + int xor = 0; + for (int num : nums) { + xor ^= num; + } + + int diff_bit = xor & (-xor); + + int a = 0, b = 0; + for (int num : nums) { + if ((num & diff_bit) != 0) { + a ^= num; + } else { + b ^= num; + } + } + return new int[]{a, b}; + } +} +``` + +```cpp +class Solution { +public: + vector singleNumber(vector& nums) { + uint xor_all = 0; + for (int& num : nums) { + xor_all ^= num; + } + + int diff_bit = xor_all & (-xor_all); + + int a = 0, b = 0; + for (int& num : nums) { + if (num & diff_bit) { + a ^= num; + } else { + b ^= num; + } + } + return {a, b}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + singleNumber(nums) { + let xor = 0; + for (const num of nums) { + xor ^= num; + } + + let diff_bit = xor & (-xor); + + let a = 0, b = 0; + for (const num of nums) { + if (num & diff_bit) { + a ^= num; + } else { + b ^= num; + } + } + return [a, b]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/single-number.md b/articles/single-number.md new file mode 100644 index 000000000..8919e9baa --- /dev/null +++ b/articles/single-number.md @@ -0,0 +1,571 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def singleNumber(self, nums: List[int]) -> int: + for i in range(len(nums)): + flag = True + for j in range(len(nums)): + if i != j and nums[i] == nums[j]: + flag = False + break + if flag: + return nums[i] +``` + +```java +public class Solution { + public int singleNumber(int[] nums) { + for (int i = 0; i < nums.length; i++) { + boolean flag = true; + for (int j = 0; j < nums.length; j++) { + if (i != j && nums[i] == nums[j]) { + flag = false; + break; + } + } + if (flag) { + return nums[i]; + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int singleNumber(vector& nums) { + for (int i = 0; i < nums.size(); i++) { + bool flag = true; + for (int j = 0; j < nums.size(); j++) { + if (i != j && nums[i] == nums[j]) { + flag = false; + break; + } + } + if (flag) { + return nums[i]; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + singleNumber(nums) { + for (let i = 0; i < nums.length; i++) { + let flag = true; + for (let j = 0; j < nums.length; j++) { + if (i !== j && nums[i] === nums[j]) { + flag = false; + break; + } + } + if (flag) { + return nums[i]; + } + } + return -1; + } +} +``` + +```csharp +public class Solution { + public int SingleNumber(int[] nums) { + for (int i = 0; i < nums.Length; i++) { + bool flag = true; + for (int j = 0; j < nums.Length; j++) { + if (i != j && nums[i] == nums[j]) { + flag = false; + break; + } + } + if (flag) { + return nums[i]; + } + } + return -1; + } +} +``` + +```go +func singleNumber(nums []int) int { + for i := 0; i < len(nums); i++ { + flag := true + for j := 0; j < len(nums); j++ { + if i != j && nums[i] == nums[j] { + flag = false + break + } + } + if flag { + return nums[i] + } + } + return 0 +} +``` + +```kotlin +class Solution { + fun singleNumber(nums: IntArray): Int { + for (i in 0..nums.size - 1) { + var flag = true + for (j in 0..nums.size - 1) { + if (i != j && nums[i] == nums[j]) { + flag = false + break + } + } + if (flag) return nums[i] + } + return 0 + } +} +``` + +```swift +class Solution { + func singleNumber(_ nums: [Int]) -> Int { + for i in 0.. int: + seen = set() + for num in nums: + if num in seen: + seen.remove(num) + else: + seen.add(num) + return list(seen)[0] +``` + +```java +public class Solution { + public int singleNumber(int[] nums) { + Set seen = new HashSet<>(); + for (int num : nums) { + if (seen.contains(num)) { + seen.remove(num); + } else { + seen.add(num); + } + } + return seen.iterator().next(); + } +} +``` + +```cpp +class Solution { +public: + int singleNumber(vector& nums) { + unordered_set seen; + for (int num : nums) { + if (seen.count(num)) { + seen.erase(num); + } else { + seen.insert(num); + } + } + return *seen.begin(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + singleNumber(nums) { + const seen = new Set(); + for (const num of nums) { + if (seen.has(num)) { + seen.delete(num); + } else { + seen.add(num); + } + } + return Array.from(seen)[0]; + } +} +``` + +```csharp +public class Solution { + public int SingleNumber(int[] nums) { + var seen = new HashSet(); + foreach (int num in nums) { + if (seen.Contains(num)) { + seen.Remove(num); + } else { + seen.Add(num); + } + } + foreach (int num in seen) { + return num; + } + return -1; + } +} +``` + +```go +func singleNumber(nums []int) int { + seen := make(map[int]bool) + for _, num := range nums { + if seen[num] { + delete(seen, num) + } else { + seen[num] = true + } + } + for num := range seen { + return num + } + return -1 +} +``` + +```kotlin +class Solution { + fun singleNumber(nums: IntArray): Int { + val seen = HashSet() + for (num in nums) { + if (num in seen) { + seen.remove(num) + } else { + seen.add(num) + } + } + return seen.first() + } +} +``` + +```swift +class Solution { + func singleNumber(_ nums: [Int]) -> Int { + var seen = Set() + + for num in nums { + if seen.contains(num) { + seen.remove(num) + } else { + seen.insert(num) + } + } + + return seen.first! + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sorting + +::tabs-start + +```python +class Solution: + def singleNumber(self, nums: List[int]) -> int: + nums.sort() + i = 0 + while i < len(nums) - 1: + if nums[i] == nums[i + 1]: + i += 2 + else: + return nums[i] + return nums[i] +``` + +```java +public class Solution { + public int singleNumber(int[] nums) { + Arrays.sort(nums); + int i = 0; + while (i < nums.length - 1) { + if (nums[i] == nums[i + 1]) { + i += 2; + } else { + return nums[i]; + } + } + return nums[i]; + } +} +``` + +```cpp +class Solution { +public: + int singleNumber(vector& nums) { + sort(begin(nums), end(nums)); + int i = 0; + while (i < nums.size() - 1) { + if (nums[i] == nums[i + 1]) { + i += 2; + } else { + return nums[i]; + } + } + return nums[i]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + singleNumber(nums) { + nums.sort((a, b) => a - b); + let i = 0; + while (i < nums.length - 1) { + if (nums[i] === nums[i + 1]) { + i += 2; + } else { + return nums[i]; + } + } + return nums[i]; + } +} +``` + +```csharp +public class Solution { + public int SingleNumber(int[] nums) { + Array.Sort(nums); + int i = 0; + while (i < nums.Length - 1) { + if (nums[i] == nums[i + 1]) { + i += 2; + } else { + return nums[i]; + } + } + return nums[i]; + } +} +``` + +```go +func singleNumber(nums []int) int { + sort.Ints(nums) + i := 0 + for i < len(nums)-1 { + if nums[i] == nums[i+1] { + i += 2 + } else { + return nums[i] + } + } + return nums[i] +} +``` + +```kotlin +class Solution { + fun singleNumber(nums: IntArray): Int { + nums.sort() + var i = 0 + while (i < nums.size - 1) { + if (nums[i] == nums[i + 1]) { + i += 2 + } else { + return nums[i] + } + } + return nums[i] + } +} +``` + +```swift +class Solution { + func singleNumber(_ nums: [Int]) -> Int { + var nums = nums.sorted() + var i = 0 + + while i < nums.count - 1 { + if nums[i] == nums[i + 1] { + i += 2 + } else { + return nums[i] + } + } + + return nums[i] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 4. Bit Manipulation + +::tabs-start + +```python +class Solution: + def singleNumber(self, nums: List[int]) -> int: + res = 0 + for num in nums: + res = num ^ res + return res +``` + +```java +public class Solution { + public int singleNumber(int[] nums) { + int res = 0; + for (int num : nums) { + res ^= num; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int singleNumber(vector& nums) { + int res = 0; + for (int num : nums) { + res ^= num; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + singleNumber(nums) { + let res = 0; + for (const num of nums) { + res ^= num; + } + return res; + } +} +``` + +```csharp +public class Solution { + public int SingleNumber(int[] nums) { + int res = 0; + foreach (int num in nums) { + res ^= num; + } + return res; + } +} +``` + +```go +func singleNumber(nums []int) int { + res := 0 + for _, num := range nums { + res ^= num + } + return res +} +``` + +```kotlin +class Solution { + fun singleNumber(nums: IntArray): Int { + var res = 0 + for (num in nums) { + res = res xor num + } + return res + } +} +``` + +```swift +class Solution { + func singleNumber(_ nums: [Int]) -> Int { + var res = 0 + for num in nums { + res ^= num + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/single-threaded-cpu.md b/articles/single-threaded-cpu.md new file mode 100644 index 000000000..16f487b94 --- /dev/null +++ b/articles/single-threaded-cpu.md @@ -0,0 +1,587 @@ +## 1. Two Min-Heaps + +::tabs-start + +```python +class Solution: + def getOrder(self, tasks: List[List[int]]) -> List[int]: + available = [] + pending = [] + for i, (enqueueTime, processTime) in enumerate(tasks): + heapq.heappush(pending, (enqueueTime, processTime, i)) + + time = 0 + res = [] + while pending or available: + while pending and pending[0][0] <= time: + enqueueTime, processTime, i = heapq.heappop(pending) + heapq.heappush(available, (processTime, i)) + + if not available: + time = pending[0][0] + continue + + processTime, i = heapq.heappop(available) + time += processTime + res.append(i) + + return res +``` + +```java +public class Solution { + public int[] getOrder(int[][] tasks) { + PriorityQueue available = new PriorityQueue<>((a, b) -> + a[0] == b[0] ? Integer.compare(a[1], b[1]) : Integer.compare(a[0], b[0]) + ); + PriorityQueue pending = new PriorityQueue<>(Comparator.comparingInt(a -> a[0])); + + int n = tasks.length; + for (int i = 0; i < n; i++) { + pending.offer(new int[]{tasks[i][0], tasks[i][1], i}); + } + + long time = 0; + int idx = 0; + int[] res = new int[n]; + while (!pending.isEmpty() || !available.isEmpty()) { + while (!pending.isEmpty() && pending.peek()[0] <= time) { + int[] task = pending.poll(); + available.offer(new int[]{task[1], task[2]}); + } + + if (available.isEmpty()) { + time = pending.peek()[0]; + continue; + } + + int[] task = available.poll(); + time += task[0]; + res[idx++] = task[1]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector getOrder(vector>& tasks) { + priority_queue, vector>, greater<>> available; + priority_queue, vector>, greater<>> pending; + + int n = tasks.size(); + for (int i = 0; i < n; ++i) { + pending.push({tasks[i][0], tasks[i][1], i}); + } + + vector res; + long long time = 0; + while (!pending.empty() || !available.empty()) { + while (!pending.empty() && pending.top()[0] <= time) { + auto [enqueueTime, processTime, index] = pending.top(); + pending.pop(); + available.push({processTime, index}); + } + + if (available.empty()) { + time = pending.top()[0]; + continue; + } + + auto [processTime, index] = available.top(); + available.pop(); + time += processTime; + res.push_back(index); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} tasks + * @return {number[]} + */ + getOrder(tasks) { + const available = new PriorityQueue( + (a, b) => a[0] === b[0] ? a[1] - b[1] : a[0] - b[0] + ); + const pending = new PriorityQueue( + (a, b) => a[0] - b[0] + ); + + tasks.forEach(([enqueueTime, processTime], i) => { + pending.enqueue([enqueueTime, processTime, i]); + }); + + let time = 0; + const res = []; + while (!pending.isEmpty() || !available.isEmpty()) { + while (!pending.isEmpty() && pending.front()[0] <= time) { + const [enqueueTime, processTime, i] = pending.dequeue(); + available.enqueue([processTime, i]); + } + + if (available.isEmpty()) { + time = pending.front()[0]; + continue; + } + + const [processTime, i] = available.dequeue(); + time += processTime; + res.push(i); + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int[] GetOrder(int[][] tasks) { + var available = new PriorityQueue<(int procTime, int index), (int procTime, int index)>(); + var pending = new PriorityQueue<(int startTime, int procTime, int index), int>(); + + int n = tasks.Length; + for (int i = 0; i < n; i++) { + pending.Enqueue((tasks[i][0], tasks[i][1], i), tasks[i][0]); + } + + long time = 0; + int[] res = new int[n]; + int idx = 0; + + while (pending.Count > 0 || available.Count > 0) { + while (pending.Count > 0 && pending.Peek().startTime <= time) { + var task = pending.Dequeue(); + available.Enqueue((task.procTime, task.index), (task.procTime, task.index)); + } + + if (available.Count == 0) { + time = pending.Peek().startTime; + continue; + } + + var next = available.Dequeue(); + time += next.procTime; + res[idx++] = next.index; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Sorting + Min-Heap + +::tabs-start + +```python +class Solution: + def getOrder(self, tasks: List[List[int]]) -> List[int]: + for i, t in enumerate(tasks): + t.append(i) + tasks.sort(key=lambda t: t[0]) + + res, minHeap = [], [] + i, time = 0, tasks[0][0] + + while minHeap or i < len(tasks): + while i < len(tasks) and time >= tasks[i][0]: + heapq.heappush(minHeap, [tasks[i][1], tasks[i][2]]) + i += 1 + if not minHeap: + time = tasks[i][0] + else: + procTime, index = heapq.heappop(minHeap) + time += procTime + res.append(index) + return res +``` + +```java +public class Solution { + public int[] getOrder(int[][] tasks) { + int n = tasks.length; + for (int i = 0; i < n; i++) { + tasks[i] = new int[] {tasks[i][0], tasks[i][1], i}; + } + Arrays.sort(tasks, Comparator.comparingInt(t -> t[0])); + + int[] res = new int[n]; + PriorityQueue minHeap = new PriorityQueue<>((a, b) -> + a[0] == b[0] ? Integer.compare(a[1], b[1]) : Integer.compare(a[0], b[0]) + ); + + int i = 0, idx = 0; + long time = tasks[0][0]; + while (!minHeap.isEmpty() || i < n) { + while (i < n && time >= tasks[i][0]) { + minHeap.offer(new int[]{tasks[i][1], tasks[i][2]}); + i++; + } + if (minHeap.isEmpty()) { + time = tasks[i][0]; + } else { + int[] task = minHeap.poll(); + time += task[0]; + res[idx++] = task[1]; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector getOrder(vector>& tasks) { + int n = tasks.size(); + for (int i = 0; i < n; ++i) { + tasks[i].push_back(i); + } + sort(tasks.begin(), tasks.end()); + + vector res; + priority_queue, vector>, greater<>> minHeap; + + int i = 0; + long long time = tasks[0][0]; + while (!minHeap.empty() || i < n) { + while (i < n && time >= tasks[i][0]) { + minHeap.push({tasks[i][1], tasks[i][2]}); + i++; + } + if (minHeap.empty()) { + time = tasks[i][0]; + } else { + auto [procTime, index] = minHeap.top(); + minHeap.pop(); + time += procTime; + res.push_back(index); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} tasks + * @return {number[]} + */ + getOrder(tasks) { + const n = tasks.length; + tasks = tasks.map((t, i) => [...t, i]); + tasks.sort((a, b) => a[0] - b[0]); + + const res = []; + const minHeap = new PriorityQueue((a, b) => + a[0] === b[0] ? a[1] - b[1] : a[0] - b[0] + ); + + let i = 0, time = tasks[0][0]; + while (minHeap.size() || i < n) { + while (i < n && time >= tasks[i][0]) { + minHeap.enqueue([tasks[i][1], tasks[i][2]]); + i++; + } + if (minHeap.isEmpty()) { + time = tasks[i][0]; + } else { + const [procTime, index] = minHeap.dequeue(); + time += procTime; + res.push(index); + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int[] GetOrder(int[][] tasks) { + int n = tasks.Length; + int i = 0; + for (; i < n; i++) { + tasks[i] = new int[] { tasks[i][0], tasks[i][1], i }; // {enqueueTime, processingTime, index} + } + + Array.Sort(tasks, (a, b) => a[0].CompareTo(b[0])); // sort by enqueueTime + + int[] res = new int[n]; + var minHeap = new PriorityQueue<(int procTime, int index), (int procTime, int index)>(); + + int idx = 0; + i = 0; + long time = tasks[0][0]; + + while (minHeap.Count > 0 || i < n) { + while (i < n && tasks[i][0] <= time) { + minHeap.Enqueue((tasks[i][1], tasks[i][2]), (tasks[i][1], tasks[i][2])); + i++; + } + + if (minHeap.Count == 0) { + time = tasks[i][0]; + } else { + var task = minHeap.Dequeue(); + time += task.procTime; + res[idx++] = task.index; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sorting + Min-Heap (Optimal) + +::tabs-start + +```python +class Solution: + def getOrder(self, tasks: List[List[int]]) -> List[int]: + n = len(tasks) + indices = list(range(n)) + indices.sort(key=lambda i: (tasks[i][0], i)) + + class Task: + def __init__(self, idx): + self.idx = idx + + def __lt__(self, other): + if tasks[self.idx][1] != tasks[other.idx][1]: + return tasks[self.idx][1] < tasks[other.idx][1] + return self.idx < other.idx + + minHeap = [] + res = [] + time = i = 0 + while minHeap or i < n: + while i < n and tasks[indices[i]][0] <= time: + heapq.heappush(minHeap, Task(indices[i])) + i += 1 + + if not minHeap: + time = tasks[indices[i]][0] + else: + next_task = heapq.heappop(minHeap) + time += tasks[next_task.idx][1] + res.append(next_task.idx) + + return res +``` + +```java +public class Solution { + public int[] getOrder(int[][] tasks) { + int n = tasks.length; + Integer[] indices = new Integer[n]; + for (int i = 0; i < n; i++) { + indices[i] = i; + } + + Arrays.sort(indices, (a, b) -> + tasks[a][0] != tasks[b][0] ? tasks[a][0] - tasks[b][0] : a - b + ); + + PriorityQueue minHeap = new PriorityQueue<>((a, b) -> + tasks[a][1] != tasks[b][1] ? tasks[a][1] - tasks[b][1] : a - b + ); + + int[] result = new int[n]; + long time = 0; + int i = 0, resIndex = 0; + while (!minHeap.isEmpty() || i < n) { + while (i < n && tasks[indices[i]][0] <= time) { + minHeap.offer(indices[i]); + i++; + } + + if (minHeap.isEmpty()) { + time = tasks[indices[i]][0]; + } else { + int nextIndex = minHeap.poll(); + time += tasks[nextIndex][1]; + result[resIndex++] = nextIndex; + } + } + + return result; + } +} +``` + +```cpp +// C++ Solution +class Solution { +public: + vector getOrder(vector>& tasks) { + int n = tasks.size(); + vector indices(n); + iota(indices.begin(), indices.end(), 0); + + sort(indices.begin(), indices.end(), [&](int a, int b) { + return tasks[a][0] < tasks[b][0] || + (tasks[a][0] == tasks[b][0] && a < b); + }); + + auto comp = [&](int a, int b) { + return tasks[a][1] > tasks[b][1] || + (tasks[a][1] == tasks[b][1] && a > b); + }; + priority_queue, decltype(comp)> minHeap(comp); + + vector result; + long long time = 0; + int i = 0; + + while (!minHeap.empty() || i < n) { + while (i < n && tasks[indices[i]][0] <= time) { + minHeap.push(indices[i]); + i++; + } + + if (minHeap.empty()) { + time = tasks[indices[i]][0]; + } else { + int nextIndex = minHeap.top(); + minHeap.pop(); + time += tasks[nextIndex][1]; + result.push_back(nextIndex); + } + } + + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} tasks + * @return {number[]} + */ + getOrder(tasks) { + const n = tasks.length; + const indices = Array.from({ length: n }, (_, i) => i); + indices.sort((a, b) => { + if (tasks[a][0] !== tasks[b][0]) { + return tasks[a][0] - tasks[b][0]; + } + return a - b; + }); + + const minHeap = new PriorityQueue( + (a, b) => { + if (tasks[a][1] !== tasks[b][1]) { + return tasks[a][1] - tasks[b][1]; + } + return a - b; + } + ); + + const res = []; + let time = 0; + let i = 0; + + while (!minHeap.isEmpty() || i < n) { + while (i < n && tasks[indices[i]][0] <= time) { + minHeap.enqueue(indices[i]); + i++; + } + + if (minHeap.size() === 0) { + time = tasks[indices[i]][0]; + } else { + const nextIndex = minHeap.dequeue(); + time += tasks[nextIndex][1]; + res.push(nextIndex); + } + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int[] GetOrder(int[][] tasks) { + int n = tasks.Length; + int[] indices = new int[n]; + int i = 0; + for (; i < n; i++) { + indices[i] = i; + } + + Array.Sort(indices, (a, b) => + tasks[a][0] != tasks[b][0] ? tasks[a][0].CompareTo(tasks[b][0]) : a.CompareTo(b) + ); + + var minHeap = new PriorityQueue(); + + int[] result = new int[n]; + long time = 0; + int resIndex = 0; + i = 0; + + while (minHeap.Count > 0 || i < n) { + while (i < n && tasks[indices[i]][0] <= time) { + int idx = indices[i]; + minHeap.Enqueue(idx, (tasks[idx][1], idx)); + i++; + } + + if (minHeap.Count == 0) { + time = tasks[indices[i]][0]; + } else { + int nextIndex = minHeap.Dequeue(); + time += tasks[nextIndex][1]; + result[resIndex++] = nextIndex; + } + } + + return result; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/sliding-window-maximum.md b/articles/sliding-window-maximum.md new file mode 100644 index 000000000..a7af08296 --- /dev/null +++ b/articles/sliding-window-maximum.md @@ -0,0 +1,1363 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]: + output = [] + + for i in range(len(nums) - k + 1): + maxi = nums[i] + for j in range(i, i + k): + maxi = max(maxi, nums[j]) + output.append(maxi) + + return output +``` + +```java +public class Solution { + public int[] maxSlidingWindow(int[] nums, int k) { + int n = nums.length; + int[] output = new int[n - k + 1]; + + for (int i = 0; i <= n - k; i++) { + int maxi = nums[i]; + for (int j = i; j < i + k; j++) { + maxi = Math.max(maxi, nums[j]); + } + output[i] = maxi; + } + + return output; + } +} +``` + +```cpp +class Solution { +public: + vector maxSlidingWindow(vector& nums, int k) { + vector output; + int n = nums.size(); + + for (int i = 0; i <= n - k; i++) { + int maxi = nums[i]; + for (int j = i; j < i + k; j++) { + maxi = max(maxi, nums[j]); + } + output.push_back(maxi); + } + + return output; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ + maxSlidingWindow(nums, k) { + let output = []; + + for (let i = 0; i <= nums.length - k; i++) { + let maxi = nums[i]; + for (let j = i; j < i + k; j++) { + maxi = Math.max(maxi, nums[j]); + } + output.push(maxi); + } + + return output; + } +} +``` + +```csharp +public class Solution { + public int[] MaxSlidingWindow(int[] nums, int k) { + int n = nums.Length; + int[] output = new int[n - k + 1]; + + for (int i = 0; i <= n - k; i++) { + int maxi = nums[i]; + for (int j = i; j < i + k; j++) { + maxi = Math.Max(maxi, nums[j]); + } + output[i] = maxi; + } + + return output; + } +} +``` + +```go +func maxSlidingWindow(nums []int, k int) []int { + output := make([]int, 0, len(nums)-k+1) + + for i := 0; i <= len(nums)-k; i++ { + maxi := nums[i] + for j := i; j < i+k; j++ { + if nums[j] > maxi { + maxi = nums[j] + } + } + output = append(output, maxi) + } + + return output +} +``` + +```kotlin +class Solution { + fun maxSlidingWindow(nums: IntArray, k: Int): IntArray { + val output = mutableListOf() + + for (i in 0..nums.size - k) { + var maxi = nums[i] + for (j in i until i + k) { + maxi = maxOf(maxi, nums[j]) + } + output.add(maxi) + } + + return output.toIntArray() + } +} +``` + +```swift +class Solution { + func maxSlidingWindow(_ nums: [Int], _ k: Int) -> [Int] { + var output = [Int]() + + for i in 0...(nums.count - k) { + var maxi = nums[i] + for j in i..<(i + k) { + maxi = max(maxi, nums[j]) + } + output.append(maxi) + } + + return output + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n - k + 1)$ space for the output array. + +> Where $n$ is the length of the array and $k$ is the size of the window. + +--- + +## 2. Segment Tree + +::tabs-start + +```python +class SegmentTree: + def __init__(self, N, A): + self.n = N + while (self.n & (self.n - 1)) != 0: + self.n += 1 + self.build(N, A) + + def build(self, N, A): + self.tree = [float('-inf')] * (2 * self.n) + for i in range(N): + self.tree[self.n + i] = A[i] + for i in range(self.n - 1, 0, -1): + self.tree[i] = max(self.tree[i << 1], self.tree[i << 1 | 1]) + + def query(self, l, r): + res = float('-inf') + l += self.n + r += self.n + 1 + while l < r: + if l & 1: + res = max(res, self.tree[l]) + l += 1 + if r & 1: + r -= 1 + res = max(res, self.tree[r]) + l >>= 1 + r >>= 1 + return res + + +class Solution: + def maxSlidingWindow(self, nums, k): + n = len(nums) + segTree = SegmentTree(n, nums) + output = [] + for i in range(n - k + 1): + output.append(segTree.query(i, i + k - 1)) + return output +``` + +```java +class SegmentTree { + int n; + int[] tree; + + SegmentTree(int N, int[] A) { + this.n = N; + while (Integer.bitCount(n) != 1) { + n++; + } + build(N, A); + } + + void build(int N, int[] A) { + tree = new int[2 * n]; + Arrays.fill(tree, Integer.MIN_VALUE); + for (int i = 0; i < N; i++) { + tree[n + i] = A[i]; + } + for (int i = n - 1; i > 0; --i) { + tree[i] = Math.max(tree[i << 1], tree[i << 1 | 1]); + } + } + + int query(int l, int r) { + int res = Integer.MIN_VALUE; + for (l += n, r += n + 1; l < r; l >>= 1, r >>= 1) { + if ((l & 1) == 1) res = Math.max(res, tree[l++]); + if ((r & 1) == 1) res = Math.max(res, tree[--r]); + } + return res; + } +} + +public class Solution { + public int[] maxSlidingWindow(int[] nums, int k) { + int n = nums.length; + SegmentTree segTree = new SegmentTree(n, nums); + int[] output = new int[n - k + 1]; + for (int i = 0; i <= n - k; i++) { + output[i] = segTree.query(i, i + k - 1); + } + return output; + } +} +``` + +```cpp +class SegmentTree { +public: + int n; + vector tree; + + SegmentTree(int N, vector& A) { + this->n = N; + while (__builtin_popcount(n) != 1) { + n++; + } + build(N, A); + } + + void build(int N, vector& A) { + tree.resize(2 * n, INT_MIN); + for (int i = 0; i < N; i++) { + tree[n + i] = A[i]; + } + for (int i = n - 1; i > 0; --i) { + tree[i] = max(tree[i << 1], tree[i << 1 | 1]); + } + } + + int query(int l, int r) { + int res = INT_MIN; + for (l += n, r += n + 1; l < r; l >>= 1, r >>= 1) { + if (l & 1) res = max(res, tree[l++]); + if (r & 1) res = max(res, tree[--r]); + } + return res; + } +}; + +class Solution { +public: + vector maxSlidingWindow(vector& nums, int k) { + int n = nums.size(); + SegmentTree segTree(n, nums); + vector output; + for (int i = 0; i <= n - k; i++) { + output.push_back(segTree.query(i, i + k - 1)); + } + return output; + } +}; +``` + +```javascript +class SegmentTree { + /** + * @constructor + * @param {number} N + * @param {number[]} A + */ + constructor(N, A) { + this.n = N; + while ((this.n & (this.n - 1)) !== 0) { + this.n++; + } + this.build(N, A); + } + + /** + * @param {number} N + * @param {number[]} A + * @return {void} + */ + build(N, A) { + this.tree = new Array(2 * this.n).fill(-Infinity); + for (let i = 0; i < N; i++) { + this.tree[this.n + i] = A[i]; + } + for (let i = this.n - 1; i > 0; i--) { + this.tree[i] = Math.max(this.tree[i << 1], this.tree[i << 1 | 1]); + } + } + + /** + * @param {number} l + * @param {number} r + * @return {number} + */ + query(l, r) { + let res = -Infinity; + l += this.n; + r += this.n + 1; + + while (l < r) { + if (l & 1) res = Math.max(res, this.tree[l++]); + if (r & 1) res = Math.max(res, this.tree[--r]); + l >>= 1; + r >>= 1; + } + + return res; + } +} + +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ + maxSlidingWindow(nums, k) { + let n = nums.length; + let segTree = new SegmentTree(n, nums); + let output = []; + + for (let i = 0; i <= n - k; i++) { + output.push(segTree.query(i, i + k - 1)); + } + + return output; + } +} +``` + +```csharp +public class SegmentTree { + public int n; + public int[] tree; + + public SegmentTree(int N, int[] A) { + this.n = N; + while (System.Numerics.BitOperations.PopCount((uint)n) != 1) { + n++; + } + Build(N, A); + } + + public void Build(int N, int[] A) { + tree = new int[2 * n]; + Array.Fill(tree, int.MinValue); + for (int i = 0; i < N; i++) { + tree[n + i] = A[i]; + } + for (int i = n - 1; i > 0; --i) { + tree[i] = Math.Max(tree[i << 1], tree[i << 1 | 1]); + } + } + + public int Query(int l, int r) { + int res = int.MinValue; + l += n; + r += n + 1; + while (l < r) { + if ((l & 1) == 1) res = Math.Max(res, tree[l++]); + if ((r & 1) == 1) res = Math.Max(res, tree[--r]); + l >>= 1; + r >>= 1; + } + return res; + } +} + +public class Solution { + public int[] MaxSlidingWindow(int[] nums, int k) { + int n = nums.Length; + SegmentTree segTree = new SegmentTree(n, nums); + int[] output = new int[n - k + 1]; + for (int i = 0; i <= n - k; i++) { + output[i] = segTree.Query(i, i + k - 1); + } + return output; + } +} +``` + +```go +type SegmentTree struct { + n int + tree []int +} + +func NewSegmentTree(N int, A []int) *SegmentTree { + n := N + for bits.OnesCount(uint(n)) != 1 { + n++ + } + + st := &SegmentTree{ + n: n, + } + st.build(N, A) + return st +} + +func (st *SegmentTree) build(N int, A []int) { + st.tree = make([]int, 2*st.n) + for i := range st.tree { + st.tree[i] = math.MinInt + } + for i := 0; i < N; i++ { + st.tree[st.n+i] = A[i] + } + for i := st.n - 1; i > 0; i-- { + st.tree[i] = max(st.tree[i<<1], st.tree[i<<1|1]) + } +} + +func (st *SegmentTree) Query(l, r int) int { + res := math.MinInt + l += st.n + r += st.n + 1 + + for l < r { + if l&1 == 1 { + res = max(res, st.tree[l]) + l++ + } + if r&1 == 1 { + r-- + res = max(res, st.tree[r]) + } + l >>= 1 + r >>= 1 + } + return res +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func maxSlidingWindow(nums []int, k int) []int { + n := len(nums) + segTree := NewSegmentTree(n, nums) + output := make([]int, n-k+1) + + for i := 0; i <= n-k; i++ { + output[i] = segTree.Query(i, i+k-1) + } + + return output +} +``` + +```kotlin +class SegmentTree(N: Int, A: IntArray) { + private var n: Int = N + private var tree: IntArray = IntArray(0) + + init { + var size = N + while (Integer.bitCount(size) != 1) { + size++ + } + n = size + build(N, A) + } + + private fun build(N: Int, A: IntArray) { + tree = IntArray(2 * n) + tree.fill(Int.MIN_VALUE) + for (i in 0 until N) { + tree[n + i] = A[i] + } + for (i in n - 1 downTo 1) { + tree[i] = maxOf(tree[i * 2], tree[i * 2 + 1]) + } + } + + fun query(l: Int, r: Int): Int { + var res = Int.MIN_VALUE + var left = l + n + var right = r + n + 1 + + while (left < right) { + if (left % 2 == 1) { + res = maxOf(res, tree[left]) + left++ + } + if (right % 2 == 1) { + right-- + res = maxOf(res, tree[right]) + } + left /= 2 + right /= 2 + } + return res + } +} + +class Solution { + fun maxSlidingWindow(nums: IntArray, k: Int): IntArray { + val n = nums.size + val segTree = SegmentTree(n, nums) + val result = IntArray(n - k + 1) + for (i in 0..n - k) { + result[i] = segTree.query(i, i + k - 1) + } + return result + } +} +``` + +```swift +class SegmentTree { + private var n: Int + private var tree: [Int] + + init(_ N: Int, _ A: [Int]) { + self.n = N + while (self.n & (self.n - 1)) != 0 { + self.n += 1 + } + self.tree = [Int](repeating: Int.min, count: 2 * self.n) + build(N, A) + } + + private func build(_ N: Int, _ A: [Int]) { + for i in 0.. Int { + var res = Int.min + var l = l + n + var r = r + n + 1 + while l < r { + if l & 1 != 0 { + res = max(res, tree[l]) + l += 1 + } + if r & 1 != 0 { + r -= 1 + res = max(res, tree[r]) + } + l >>= 1 + r >>= 1 + } + return res + } +} + +class Solution { + func maxSlidingWindow(_ nums: [Int], _ k: Int) -> [Int] { + let n = nums.count + let segTree = SegmentTree(n, nums) + var output = [Int]() + + for i in 0...(n - k) { + output.append(segTree.query(i, i + k - 1)) + } + + return output + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Heap + +::tabs-start + +```python +class Solution: + def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]: + heap = [] + output = [] + for i in range(len(nums)): + heapq.heappush(heap, (-nums[i], i)) + if i >= k - 1: + while heap[0][1] <= i - k: + heapq.heappop(heap) + output.append(-heap[0][0]) + return output +``` + +```java +public class Solution { + public int[] maxSlidingWindow(int[] nums, int k) { + PriorityQueue heap = new PriorityQueue<>((a, b) -> b[0] - a[0]); + int[] output = new int[nums.length - k + 1]; + int idx = 0; + for (int i = 0; i < nums.length; i++) { + heap.offer(new int[]{nums[i], i}); + if (i >= k - 1) { + while (heap.peek()[1] <= i - k) { + heap.poll(); + } + output[idx++] = heap.peek()[0]; + } + } + return output; + } +} +``` + +```cpp +class Solution { +public: + vector maxSlidingWindow(vector& nums, int k) { + priority_queue> heap; + vector output; + for (int i = 0; i < nums.size(); i++) { + heap.push({nums[i], i}); + if (i >= k - 1) { + while (heap.top().second <= i - k) { + heap.pop(); + } + output.push_back(heap.top().first); + } + } + return output; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ + maxSlidingWindow(nums, k) { + const heap = new MaxPriorityQueue(x => x[0]); + const output = []; + const length = nums.length; + + for (let i = 0; i < length; i++) { + heap.enqueue([nums[i], i]); + + if (i >= k - 1) { + while (heap.front()[1] <= i - k) { + heap.dequeue(); + } + output.push(heap.front()[0]); + } + } + + return output; + } +} +``` + +```csharp +public class Solution { + public int[] MaxSlidingWindow(int[] nums, int k) { + PriorityQueue<(int val, int idx), int> pq = new PriorityQueue<(int val, int idx), int>( + Comparer.Create((a, b) => b.CompareTo(a)) + ); + + int[] output = new int[nums.Length - k + 1]; + int idx = 0; + + for (int i = 0; i < nums.Length; i++) { + pq.Enqueue((nums[i], i), nums[i]); + + if (i >= k - 1) { + while (pq.Peek().idx <= i - k) { + pq.Dequeue(); + } + output[idx++] = pq.Peek().val; + } + } + + return output; + } +} +``` + +```go +func maxSlidingWindow(nums []int, k int) []int { + heap := priorityqueue.NewWith(func(a, b interface{}) int { + return b.([2]int)[0] - a.([2]int)[0] + }) + + output := []int{} + for i := 0; i < len(nums); i++ { + heap.Enqueue([2]int{nums[i], i}) + if i >= k - 1 { + peek, _ := heap.Peek() + for peek.([2]int)[1] <= i - k { + heap.Dequeue() + peek, _ = heap.Peek() + } + val, _ := heap.Peek() + output = append(output, val.([2]int)[0]) + } + } + return output +} +``` + +```kotlin +class Solution { + fun maxSlidingWindow(nums: IntArray, k: Int): IntArray { + val output = mutableListOf() + val heap = PriorityQueue>(compareByDescending { it.first }) + + for (i in nums.indices) { + heap.add(Pair(nums[i], i)) + if (i >= k - 1) { + while (heap.peek().second <= i - k) { + heap.poll() + } + output.add(heap.peek().first) + } + } + return output.toIntArray() + } +} +``` + +```swift +class Solution { + func maxSlidingWindow(_ nums: [Int], _ k: Int) -> [Int] { + var heap = Heap() + var output = [Int]() + + for i in 0..= k - 1 { + while heap.max!.index <= i - k { + heap.removeMax() + } + output.append(heap.max!.num) + } + } + return output + } +} + +struct Item: Comparable { + let num: Int + let index: Int + + static func < (lhs: Item, rhs: Item) -> Bool { + return lhs.num < rhs.num + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming + +::tabs-start + +```python +class Solution: + def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]: + n = len(nums) + leftMax = [0] * n + rightMax = [0] * n + + leftMax[0] = nums[0] + rightMax[n - 1] = nums[n - 1] + + for i in range(1, n): + if i % k == 0: + leftMax[i] = nums[i] + else: + leftMax[i] = max(leftMax[i - 1], nums[i]) + + if (n - 1 - i) % k == 0: + rightMax[n - 1 - i] = nums[n - 1 - i] + else: + rightMax[n - 1 - i] = max(rightMax[n - i], nums[n - 1 - i]) + + output = [0] * (n - k + 1) + + for i in range(n - k + 1): + output[i] = max(leftMax[i + k - 1], rightMax[i]) + + return output +``` + +```java +public class Solution { + public int[] maxSlidingWindow(int[] nums, int k) { + int n = nums.length; + int[] leftMax = new int[n]; + int[] rightMax = new int[n]; + + leftMax[0] = nums[0]; + rightMax[n - 1] = nums[n - 1]; + + for (int i = 1; i < n; i++) { + if (i % k == 0) { + leftMax[i] = nums[i]; + } else { + leftMax[i] = Math.max(leftMax[i - 1], nums[i]); + } + + if ((n - 1 - i) % k == 0) { + rightMax[n - 1 - i] = nums[n - 1 - i]; + } else { + rightMax[n - 1 - i] = Math.max(rightMax[n - i], nums[n - 1 - i]); + } + } + + int[] output = new int[n - k + 1]; + + for (int i = 0; i < n - k + 1; i++) { + output[i] = Math.max(leftMax[i + k - 1], rightMax[i]); + } + + return output; + } +} +``` + +```cpp +class Solution { +public: + vector maxSlidingWindow(vector& nums, int k) { + int n = nums.size(); + vector leftMax(n); + vector rightMax(n); + + leftMax[0] = nums[0]; + rightMax[n - 1] = nums[n - 1]; + + for (int i = 1; i < n; i++) { + if (i % k == 0) { + leftMax[i] = nums[i]; + } else { + leftMax[i] = max(leftMax[i - 1], nums[i]); + } + + if ((n - 1 - i) % k == 0) { + rightMax[n - 1 - i] = nums[n - 1 - i]; + } else { + rightMax[n - 1 - i] = max(rightMax[n - i], nums[n - 1 - i]); + } + } + + vector output(n - k + 1); + + for (int i = 0; i < n - k + 1; i++) { + output[i] = max(leftMax[i + k - 1], rightMax[i]); + } + + return output; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ + maxSlidingWindow(nums, k) { + const n = nums.length; + const leftMax = new Array(n); + const rightMax = new Array(n); + + leftMax[0] = nums[0]; + rightMax[n - 1] = nums[n - 1]; + + for (let i = 1; i < n; i++) { + if (i % k === 0) { + leftMax[i] = nums[i]; + } else { + leftMax[i] = Math.max(leftMax[i - 1], nums[i]); + } + + if ((n - 1 - i) % k === 0) { + rightMax[n - 1 - i] = nums[n - 1 - i]; + } else { + rightMax[n - 1 - i] = Math.max(rightMax[n - i], nums[n - 1 - i]); + } + } + + const output = new Array(n - k + 1); + + for (let i = 0; i < n - k + 1; i++) { + output[i] = Math.max(leftMax[i + k - 1], rightMax[i]); + } + + return output; + } +} +``` + +```csharp +public class Solution { + public int[] MaxSlidingWindow(int[] nums, int k) { + int n = nums.Length; + int[] leftMax = new int[n]; + int[] rightMax = new int[n]; + + leftMax[0] = nums[0]; + rightMax[n - 1] = nums[n - 1]; + + for (int i = 1; i < n; i++) { + if (i % k == 0) { + leftMax[i] = nums[i]; + } else { + leftMax[i] = Math.Max(leftMax[i - 1], nums[i]); + } + + if ((n - 1 - i) % k == 0) { + rightMax[n - 1 - i] = nums[n - 1 - i]; + } else { + rightMax[n - 1 - i] = Math.Max(rightMax[n - i], nums[n - 1 - i]); + } + } + + int[] output = new int[n - k + 1]; + + for (int i = 0; i < n - k + 1; i++) { + output[i] = Math.Max(leftMax[i + k - 1], rightMax[i]); + } + + return output; + } +} +``` + +```go +func maxSlidingWindow(nums []int, k int) []int { + n := len(nums) + leftMax := make([]int, n) + rightMax := make([]int, n) + + leftMax[0] = nums[0] + rightMax[n-1] = nums[n-1] + + for i := 1; i < n; i++ { + if i%k == 0 { + leftMax[i] = nums[i] + } else { + leftMax[i] = max(leftMax[i-1], nums[i]) + } + } + + for i := 1; i < n; i++ { + if (n-1-i)%k == 0 { + rightMax[n-1-i] = nums[n-1-i] + } else { + rightMax[n-1-i] = max(rightMax[n-i], nums[n-1-i]) + } + } + + output := make([]int, n-k+1) + + for i := 0; i < n-k+1; i++ { + output[i] = max(leftMax[i+k-1], rightMax[i]) + } + return output +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun maxSlidingWindow(nums: IntArray, k: Int): IntArray { + val n = nums.size + val leftMax = IntArray(n) + val rightMax = IntArray(n) + + leftMax[0] = nums[0] + rightMax[n - 1] = nums[n - 1] + + for (i in 1 until n) { + if (i % k == 0) { + leftMax[i] = nums[i] + } else { + leftMax[i] = maxOf(leftMax[i - 1], nums[i]) + } + + if ((n - 1 - i) % k == 0) { + rightMax[n - 1 - i] = nums[n - 1 - i] + } else { + rightMax[n - 1 - i] = maxOf(rightMax[n - i], nums[n - 1 - i]) + } + } + + val output = IntArray(n - k + 1) + for (i in 0..n - k) { + output[i] = maxOf(leftMax[i + k - 1], rightMax[i]) + } + + return output + } +} +``` + +```swift +class Solution { + func maxSlidingWindow(_ nums: [Int], _ k: Int) -> [Int] { + let n = nums.count + var leftMax = [Int](repeating: 0, count: n) + var rightMax = [Int](repeating: 0, count: n) + + leftMax[0] = nums[0] + rightMax[n - 1] = nums[n - 1] + + for i in 1.. List[int]: + output = [] + q = deque() # index + l = r = 0 + + while r < len(nums): + while q and nums[q[-1]] < nums[r]: + q.pop() + q.append(r) + + if l > q[0]: + q.popleft() + + if (r + 1) >= k: + output.append(nums[q[0]]) + l += 1 + r += 1 + + return output +``` + +```java +public class Solution { + public int[] maxSlidingWindow(int[] nums, int k) { + int n = nums.length; + int[] output = new int[n - k + 1]; + Deque q = new LinkedList<>(); + int l = 0, r = 0; + + while (r < n) { + while (!q.isEmpty() && nums[q.getLast()] < nums[r]) { + q.removeLast(); + } + q.addLast(r); + + if (l > q.getFirst()) { + q.removeFirst(); + } + + if ((r + 1) >= k) { + output[l] = nums[q.getFirst()]; + l++; + } + r++; + } + + return output; + } +} +``` + +```cpp +class Solution { +public: + vector maxSlidingWindow(vector& nums, int k) { + int n = nums.size(); + vector output(n - k + 1); + deque q; + int l = 0, r = 0; + + while (r < n) { + while (!q.empty() && nums[q.back()] < nums[r]) { + q.pop_back(); + } + q.push_back(r); + + if (l > q.front()) { + q.pop_front(); + } + + if ((r + 1) >= k) { + output[l] = nums[q.front()]; + l++; + } + r++; + } + + return output; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ + maxSlidingWindow(nums, k) { + const n = nums.length; + const output = new Array(n - k + 1); + const q = new Deque(); + let l = 0, r = 0; + + while (r < n) { + while (q.size() && nums[q.back()] < nums[r]) { + q.popBack(); + } + q.pushBack(r); + + if (l > q.front()) { + q.popFront(); + } + + if ((r + 1) >= k) { + output[l] = nums[q.front()]; + l++; + } + r++; + } + + return output; + } +} +``` + +```csharp +public class Solution { + public int[] MaxSlidingWindow(int[] nums, int k) { + int n = nums.Length; + int[] output = new int[n - k + 1]; + var q = new LinkedList(); + int l = 0, r = 0; + + while (r < n) { + while (q.Count > 0 && nums[q.Last.Value] < nums[r]) { + q.RemoveLast(); + } + q.AddLast(r); + + if (l > q.First.Value) { + q.RemoveFirst(); + } + + if ((r + 1) >= k) { + output[l] = nums[q.First.Value]; + l++; + } + r++; + } + + return output; + } +} +``` + +```go +func maxSlidingWindow(nums []int, k int) []int { + output := []int{} + q := []int{} + l, r := 0, 0 + + for r < len(nums) { + for len(q) > 0 && nums[q[len(q)-1]] < nums[r] { + q = q[:len(q)-1] + } + q = append(q, r) + + if l > q[0] { + q = q[1:] + } + + if (r + 1) >= k { + output = append(output, nums[q[0]]) + l += 1 + } + r += 1 + } + + return output +} +``` + +```kotlin +class Solution { + fun maxSlidingWindow(nums: IntArray, k: Int): IntArray { + val output = mutableListOf() + val q = ArrayDeque() + var l = 0 + var r = 0 + + while (r < nums.size) { + while (q.isNotEmpty() && nums[q.last()] < nums[r]) { + q.removeLast() + } + q.addLast(r) + + if (l > q.first()) { + q.removeFirst() + } + + if ((r + 1) >= k) { + output.add(nums[q.first()]) + l += 1 + } + r += 1 + } + + return output.toIntArray() + } +} +``` + +```swift +class Solution { + func maxSlidingWindow(_ nums: [Int], _ k: Int) -> [Int] { + var output = [Int]() + var deque = [Int]() // Index + var l = 0, r = 0 + + while r < nums.count { + while !deque.isEmpty && nums[deque.last!] < nums[r] { + deque.removeLast() + } + deque.append(r) + + if l > deque.first! { + deque.removeFirst() + } + + if (r + 1) >= k { + output.append(nums[deque.first!]) + l += 1 + } + r += 1 + } + + return output + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/sliding-window-median.md b/articles/sliding-window-median.md new file mode 100644 index 000000000..b49ca4071 --- /dev/null +++ b/articles/sliding-window-median.md @@ -0,0 +1,473 @@ +## 1. Brute Force (Sorting) + +::tabs-start + +```python +class Solution: + def medianSlidingWindow(self, nums: List[int], k: int) -> List[float]: + res = [] + for i in range(len(nums) - k + 1): + tmp = nums[i:i + k][:] + tmp.sort() + if k & 1: + res.append(tmp[k // 2]) + else: + res.append((tmp[k // 2] + tmp[(k - 1) // 2]) / 2) + return res +``` + +```java +public class Solution { + public double[] medianSlidingWindow(int[] nums, int k) { + int n = nums.length - k + 1; + double[] res = new double[n]; + for (int i = 0; i < n; i++) { + int[] tmp = Arrays.copyOfRange(nums, i, i + k); + Arrays.sort(tmp); + if (k % 2 == 1) { + res[i] = tmp[k / 2]; + } else { + res[i] = (tmp[k / 2] + 0L + tmp[(k - 1) / 2]) / 2.0; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector medianSlidingWindow(vector& nums, int k) { + vector res; + for (int i = 0; i <= nums.size() - k; ++i) { + vector tmp(nums.begin() + i, nums.begin() + i + k); + sort(tmp.begin(), tmp.end()); + if (k % 2 == 1) { + res.push_back(tmp[k / 2]); + } else { + res.push_back((tmp[k / 2] + 0LL + tmp[(k - 1) / 2]) / 2.0); + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ + medianSlidingWindow(nums, k) { + const res = []; + for (let i = 0; i <= nums.length - k; i++) { + const tmp = nums.slice(i, i + k).sort((a, b) => a - b); + if (k % 2 === 1) { + res.push(tmp[Math.floor(k / 2)]); + } else { + res.push((tmp[Math.floor(k / 2)] + tmp[Math.floor((k - 1) / 2)]) / 2); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * k\log k)$ +* Space complexity: + * $O(k)$ extra space. + * $O(n - k + 1)$ space for output array. + +> Where $n$ is the size of the array $nums$ and $k$ is the size of the sliding window. + +--- + +## 2. Two Heaps + +::tabs-start + +```python +class Solution: + def medianSlidingWindow(self, nums: List[int], k: int) -> List[float]: + small, large = [], [] + d = defaultdict(int) + + for i in range(k): + heapq.heappush(small, -nums[i]) + for i in range(k // 2): + heapq.heappush(large, -heapq.heappop(small)) + + res = [-small[0] if k & 1 else (large[0] - small[0]) / 2] + for i in range(k, len(nums)): + d[nums[i - k]] += 1 + balance = -1 if small and nums[i - k] <= -small[0] else 1 + + if small and nums[i] <= -small[0]: + heapq.heappush(small, -nums[i]) + balance += 1 + else: + heapq.heappush(large, nums[i]) + balance -= 1 + + if balance > 0: + heapq.heappush(large, -heapq.heappop(small)) + if balance < 0: + heapq.heappush(small, -heapq.heappop(large)) + + while small and d[-small[0]] > 0: + d[-heapq.heappop(small)] -= 1 + + while large and d[large[0]] > 0: + d[heapq.heappop(large)] -= 1 + + res.append(-small[0] if k & 1 else (large[0] - small[0]) / 2) + + return res +``` + +```java +public class Solution { + public double[] medianSlidingWindow(int[] nums, int k) { + PriorityQueue small = new PriorityQueue<>(Collections.reverseOrder()); + PriorityQueue large = new PriorityQueue<>(); + Map d = new HashMap<>(); + + for (int i = 0; i < k; i++) { + small.add(nums[i]); + } + for (int i = 0; i < k / 2; i++) { + large.add(small.poll()); + } + + double[] res = new double[nums.length - k + 1]; + res[0] = k % 2 == 1 ? small.peek() : (large.peek() + 0L + small.peek()) / 2.0; + for (int i = k; i < nums.length; i++) { + d.put(nums[i - k], d.getOrDefault(nums[i - k], 0) + 1); + int balance = (small.size() > 0 && nums[i - k] <= small.peek()) ? -1 : 1; + + if (nums[i] <= small.peek()) { + small.add(nums[i]); + balance++; + } else { + large.add(nums[i]); + balance--; + } + + if (balance > 0) { + large.add(small.poll()); + } + if (balance < 0) { + small.add(large.poll()); + } + + while (!small.isEmpty() && d.getOrDefault(small.peek(), 0) > 0) { + d.put(small.peek(), d.get(small.peek()) - 1); + small.poll(); + } + while (!large.isEmpty() && d.getOrDefault(large.peek(), 0) > 0) { + d.put(large.peek(), d.get(large.peek()) - 1); + large.poll(); + } + + res[i - k + 1] = k % 2 == 1 ? small.peek() : (large.peek() + 0L + small.peek()) / 2.0; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector medianSlidingWindow(vector& nums, int k) { + priority_queue small; + priority_queue, greater> large; + unordered_map d; + + for (int i = 0; i < k; ++i) { + small.push(nums[i]); + } + for (int i = 0; i < k / 2; ++i) { + large.push(small.top()); + small.pop(); + } + + vector res; + res.push_back(k & 1 ? small.top() : (large.top() + 0LL + small.top()) / 2.0); + for (int i = k; i < nums.size(); ++i) { + d[nums[i - k]]++; + int balance = small.size() > 0 && nums[i - k] <= small.top() ? -1 : 1; + + if (nums[i] <= small.top()) { + small.push(nums[i]); + balance++; + } else { + large.push(nums[i]); + balance--; + } + + if (balance > 0) { + large.push(small.top()); + small.pop(); + } + if (balance < 0) { + small.push(large.top()); + large.pop(); + } + + while (!small.empty() && d[small.top()] > 0) { + d[small.top()]--; + small.pop(); + } + + while (!large.empty() && d[large.top()] > 0) { + d[large.top()]--; + large.pop(); + } + + res.push_back(k & 1 ? small.top() : (large.top() + 0LL + small.top()) / 2.0); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ + medianSlidingWindow(nums, k) { + const small = new MaxPriorityQueue({ compare: (a, b) => b - a }); + const large = new MinPriorityQueue({ compare: (a, b) => a - b }); + const d = new Map(); + + for (let i = 0; i < k; i++) { + small.enqueue(nums[i]); + } + for (let i = 0; i < Math.floor(k / 2); i++) { + large.enqueue(small.dequeue()); + } + + const res = [k % 2 === 1 ? small.front() : (large.front() + small.front()) / 2]; + for (let i = k; i < nums.length; i++) { + const toRemove = nums[i - k]; + d.set(toRemove, (d.get(toRemove) || 0) + 1); + let balance = small.size() > 0 && toRemove <= small.front() ? -1 : 1; + + if (nums[i] <= small.front()) { + small.enqueue(nums[i]); + balance++; + } else { + large.enqueue(nums[i]); + balance--; + } + + if (balance > 0) { + large.enqueue(small.dequeue()); + } + if (balance < 0) { + small.enqueue(large.dequeue()); + } + + while (small.size() > 0 && d.get(small.front()) > 0) { + d.set(small.front(), d.get(small.front()) - 1); + small.dequeue(); + } + while (large.size() > 0 && d.get(large.front()) > 0) { + d.set(large.front(), d.get(large.front()) - 1); + large.dequeue(); + } + + res.push(k % 2 === 1 ? small.front() : (large.front() + small.front()) / 2); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: + * $O(n)$ extra space. + * $O(n - k + 1)$ space for output array. + +> Where $n$ is the size of the array $nums$ and $k$ is the size of the sliding window. + +--- + +## 3. Two Multisets + +::tabs-start + +```python +class Solution: + def medianSlidingWindow(self, nums: List[int], k: int) -> List[float]: + small, large = SortedList(), SortedList() + res = [] + for i in range(len(nums)): + if len(small) == 0 or nums[i] <= small[-1]: + small.add(nums[i]) + else: + large.add(nums[i]) + if i >= k: + if nums[i - k] in small: + small.remove(nums[i - k]) + else: + large.remove(nums[i - k]) + if len(small) > len(large) + 1: + large.add(small.pop()) + if len(large) > len(small): + small.add(large.pop(0)) + if i >= k - 1: + res.append(small[-1] if k & 1 else (small[-1] + large[0]) / 2) + return res +``` + +```java +public class Solution { + public double[] medianSlidingWindow(int[] nums, int k) { + TreeSet small = new TreeSet<>((a, b) -> + nums[a] != nums[b] ? Integer.compare(nums[a], nums[b]) : Integer.compare(a, b) + ); + TreeSet large = new TreeSet<>((a, b) -> + nums[a] != nums[b] ? Integer.compare(nums[a], nums[b]) : Integer.compare(a, b) + ); + double[] res = new double[nums.length - k + 1]; + for (int i = 0; i < nums.length; i++) { + if (small.isEmpty() || nums[i] <= nums[small.last()]) small.add(i); + else large.add(i); + if (i >= k) { + if (small.contains(i - k)) small.remove(i - k); + else large.remove(i - k); + } + while (small.size() > large.size() + 1) large.add(small.pollLast()); + while (large.size() > small.size()) small.add(large.pollFirst()); + if (i >= k - 1) { + res[i - k + 1] = k % 2 == 1 ? nums[small.last()] : + (nums[small.last()] + 0L + nums[large.first()]) / 2.0; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector medianSlidingWindow(vector& nums, int k) { + multiset small, large; + vector res; + for (int i = 0; i < nums.size(); ++i) { + if (small.empty() || nums[i] <= *small.rbegin()) small.insert(nums[i]); + else large.insert(nums[i]); + if (i >= k) { + if (small.count(nums[i - k])) small.erase(small.find(nums[i - k])); + else large.erase(large.find(nums[i - k])); + } + if (small.size() > large.size() + 1) { + large.insert(*small.rbegin()); + small.erase(prev(small.end())); + } + if (large.size() > small.size()) { + small.insert(*large.begin()); + large.erase(large.begin()); + } + if (i >= k - 1) { + res.push_back( + k % 2 == 1 ? *small.rbegin() : + ((*small.rbegin() + 0LL + *large.begin()) / 2.0) + ); + } + } + return res; + } +}; +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log k)$ +* Space complexity: + * $O(k)$ extra space. + * $O(n - k + 1)$ space for output array. + +> Where $n$ is the size of the array $nums$ and $k$ is the size of the sliding window. + +--- + +## 4. Multiset + +::tabs-start + +```python +class Solution: + def medianSlidingWindow(self, nums: List[int], k: int) -> List[float]: + window = SortedList() + res = [] + for i in range(len(nums)): + window.add(nums[i]) + if i >= k: + window.remove(nums[i - k]) + if i >= k - 1: + if k % 2 == 1: + res.append(float(window[k // 2])) + else: + res.append((window[k // 2 - 1] + window[k // 2]) / 2) + return res +``` + +```cpp +class Solution { +public: + vector medianSlidingWindow(vector& nums, int k) { + multiset window(nums.begin(), nums.begin() + k); + auto mid = next(window.begin(), k / 2); + vector res; + + for (int i = k;; i++) { + if (k & 1) { + res.push_back(*mid); + } else { + res.push_back((*mid + 0LL + *prev(mid)) / 2.0); + } + + if (i == nums.size()) return res; + + window.insert(nums[i]); + if (nums[i] < *mid) mid--; + if (nums[i - k] <= *mid) mid++; + window.erase(window.lower_bound(nums[i - k])); + } + } +}; +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log k)$ +* Space complexity: + * $O(k)$ extra space. + * $O(n - k + 1)$ space for output array. + +> Where $n$ is the size of the array $nums$ and $k$ is the size of the sliding window. \ No newline at end of file diff --git a/articles/smallest-string-starting-from-leaf.md b/articles/smallest-string-starting-from-leaf.md new file mode 100644 index 000000000..d3d241adb --- /dev/null +++ b/articles/smallest-string-starting-from-leaf.md @@ -0,0 +1,474 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def smallestFromLeaf(self, root: Optional[TreeNode]) -> str: + def dfs(root, cur): + if not root: + return + + cur = chr(ord('a') + root.val) + cur + if root.left and root.right: + return min( + dfs(root.left, cur), + dfs(root.right, cur) + ) + + if root.right: + return dfs(root.right, cur) + if root.left: + return dfs(root.left, cur) + return cur + + return dfs(root, "") +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public String smallestFromLeaf(TreeNode root) { + return dfs(root, ""); + } + + private String dfs(TreeNode root, String cur) { + if (root == null) { + return null; + } + + cur = (char) ('a' + root.val) + cur; + if (root.left != null && root.right != null) { + return min(dfs(root.left, cur), dfs(root.right, cur)); + } + + if (root.right != null) { + return dfs(root.right, cur); + } + if (root.left != null) { + return dfs(root.left, cur); + } + return cur; + } + + private String min(String a, String b) { + if (a == null) return b; + if (b == null) return a; + return a.compareTo(b) < 0 ? a : b; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + string smallestFromLeaf(TreeNode* root) { + return dfs(root, ""); + } + +private: + string dfs(TreeNode* root, string cur) { + if (!root) return ""; + + cur = char('a' + root->val) + cur; + if (root->left && root->right) { + return min(dfs(root->left, cur), dfs(root->right, cur)); + } + + if (root->right) return dfs(root->right, cur); + if (root->left) return dfs(root->left, cur); + return cur; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {string} + */ + smallestFromLeaf(root) { + const min = (a, b) => { + if (!a) return b; + if (!b) return a; + return a < b ? a : b; + }; + + const dfs = (node, cur) => { + if (!node) return; + + cur = String.fromCharCode(97 + node.val) + cur; + + if (node.left && node.right) { + return min(dfs(node.left, cur), dfs(node.right, cur)); + } + if (node.left) return dfs(node.left, cur); + if (node.right) return dfs(node.right, cur); + return cur; + }; + + return dfs(root, ""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def smallestFromLeaf(self, root: Optional[TreeNode]) -> str: + q = deque([(root, "")]) + res = None + + while q: + node, cur = q.popleft() + cur = chr(ord('a') + node.val) + cur + + if not node.left and not node.right: + res = min(res, cur) if res else cur + + if node.left: + q.append((node.left, cur)) + if node.right: + q.append((node.right, cur)) + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public String smallestFromLeaf(TreeNode root) { + Queue> q = new LinkedList<>(); + q.offer(new Pair<>(root, "")); + String res = null; + + while (!q.isEmpty()) { + Pair pair = q.poll(); + TreeNode node = pair.getKey(); + String cur = (char) ('a' + node.val) + pair.getValue(); + + if (node.left == null && node.right == null) { + if (res == null || cur.compareTo(res) < 0) { + res = cur; + } + } + + if (node.left != null) q.offer(new Pair<>(node.left, cur)); + if (node.right != null) q.offer(new Pair<>(node.right, cur)); + } + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + string smallestFromLeaf(TreeNode* root) { + queue> q; + q.push({root, ""}); + string res; + + while (!q.empty()) { + auto [node, cur] = q.front(); + q.pop(); + cur = char('a' + node->val) + cur; + + if (!node->left && !node->right) { + if (res.empty() || cur < res) { + res = cur; + } + } + + if (node->left) q.push({node->left, cur}); + if (node->right) q.push({node->right, cur}); + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {string} + */ + smallestFromLeaf(root) { + const q = new Queue(); + q.push([root, ""]); + let res = null; + + while (!q.isEmpty()) { + const [node, cur] = q.pop(); + const newCur = String.fromCharCode(97 + node.val) + cur; + + if (!node.left && !node.right) { + res = res === null || newCur < res ? newCur : res; + } + + if (node.left) q.push([node.left, newCur]); + if (node.right) q.push([node.right, newCur]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def smallestFromLeaf(self, root: Optional[TreeNode]) -> str: + stack = [(root, "")] + res = None + + while stack: + node, cur = stack.pop() + cur = chr(ord('a') + node.val) + cur + + if not node.left and not node.right: + res = min(res, cur) if res else cur + + if node.right: + stack.append((node.right, cur)) + if node.left: + stack.append((node.left, cur)) + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public String smallestFromLeaf(TreeNode root) { + Stack> stack = new Stack<>(); + stack.push(new Pair<>(root, "")); + String res = null; + + while (!stack.isEmpty()) { + Pair pair = stack.pop(); + TreeNode node = pair.getKey(); + String cur = (char) ('a' + node.val) + pair.getValue(); + + if (node.left == null && node.right == null) { + if (res == null || cur.compareTo(res) < 0) { + res = cur; + } + } + + if (node.right != null) stack.push(new Pair<>(node.right, cur)); + if (node.left != null) stack.push(new Pair<>(node.left, cur)); + } + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + string smallestFromLeaf(TreeNode* root) { + stack> stk; + stk.push({root, ""}); + string res; + + while (!stk.empty()) { + auto [node, cur] = stk.top();stk.pop(); + cur = char('a' + node->val) + cur; + + if (!node->left && !node->right) { + if (res.empty() || cur < res) { + res = cur; + } + } + + if (node->right) stk.push({node->right, cur}); + if (node->left) stk.push({node->left, cur}); + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {string} + */ + smallestFromLeaf(root) { + const stack = [[root, ""]]; + let res = null; + + while (stack.length) { + const [node, cur] = stack.pop(); + const newCur = String.fromCharCode(97 + node.val) + cur; + + if (!node.left && !node.right) { + res = res === null || newCur < res ? newCur : res; + } + + if (node.right) stack.push([node.right, newCur]); + if (node.left) stack.push([node.left, newCur]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/snakes-and-ladders.md b/articles/snakes-and-ladders.md new file mode 100644 index 000000000..afc71ef47 --- /dev/null +++ b/articles/snakes-and-ladders.md @@ -0,0 +1,572 @@ +## 1. Breadth First Search - I + +::tabs-start + +```python +class Solution: + def snakesAndLadders(self, board: List[List[int]]) -> int: + n = len(board) + + def intToPos(square): + r = (square - 1) // n + c = (square - 1) % n + if r % 2 == 1: + c = n - 1 - c + r = n - 1 - r + return r, c + + q = deque([(1, 0)]) + visit = set() + + while q: + square, moves = q.popleft() + + for i in range(1, 7): + nextSquare = square + i + r, c = intToPos(nextSquare) + if board[r][c] != -1: + nextSquare = board[r][c] + + if nextSquare == n * n: + return moves + 1 + + if nextSquare not in visit: + visit.add(nextSquare) + q.append((nextSquare, moves + 1)) + + return -1 +``` + +```java +public class Solution { + public int snakesAndLadders(int[][] board) { + int n = board.length; + Queue q = new LinkedList<>(); + q.offer(new int[]{1, 0}); + Set visit = new HashSet<>(); + + while (!q.isEmpty()) { + int[] cur = q.poll(); + int square = cur[0], moves = cur[1]; + + for (int i = 1; i <= 6; i++) { + int nextSquare = square + i; + int[] pos = intToPos(nextSquare, n); + int r = pos[0], c = pos[1]; + if (board[r][c] != -1) { + nextSquare = board[r][c]; + } + if (nextSquare == n * n) return moves + 1; + if (!visit.contains(nextSquare)) { + visit.add(nextSquare); + q.offer(new int[]{nextSquare, moves + 1}); + } + } + } + return -1; + } + + private int[] intToPos(int square, int n) { + int r = (square - 1) / n; + int c = (square - 1) % n; + if (r % 2 == 1) c = n - 1 - c; + r = n - 1 - r; + return new int[]{r, c}; + } +} +``` + +```cpp +class Solution { +public: + int snakesAndLadders(vector>& board) { + int n = board.size(); + queue> q; + q.push({1, 0}); + unordered_set visit; + + while (!q.empty()) { + auto [square, moves] = q.front(); q.pop(); + + for (int i = 1; i <= 6; i++) { + int nextSquare = square + i; + auto [r, c] = intToPos(nextSquare, n); + if (board[r][c] != -1) { + nextSquare = board[r][c]; + } + if (nextSquare == n * n) return moves + 1; + if (!visit.count(nextSquare)) { + visit.insert(nextSquare); + q.push({nextSquare, moves + 1}); + } + } + } + return -1; + } + +private: + pair intToPos(int square, int n) { + int r = (square - 1) / n; + int c = (square - 1) % n; + if (r % 2 == 1) c = n - 1 - c; + r = n - 1 - r; + return {r, c}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} board + * @return {number} + */ + snakesAndLadders(board) { + const n = board.length; + const queue = new Queue([[1, 0]]); + const visit = new Set(); + + const intToPos = (square) => { + let r = Math.floor((square - 1) / n); + let c = (square - 1) % n; + if (r % 2 === 1) c = n - 1 - c; + r = n - 1 - r; + return [r, c]; + }; + + while (!queue.isEmpty()) { + const [square, moves] = queue.pop(); + + for (let i = 1; i <= 6; i++) { + let nextSquare = square + i; + const [r, c] = intToPos(nextSquare); + if (board[r][c] !== -1) { + nextSquare = board[r][c]; + } + if (nextSquare === n * n) return moves + 1; + if (!visit.has(nextSquare)) { + visit.add(nextSquare); + queue.push([nextSquare, moves + 1]); + } + } + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Breadth First Search - II + +::tabs-start + +```python +class Solution: + def snakesAndLadders(self, board: List[List[int]]) -> int: + n = len(board) + + def intToPos(square): + r = (square - 1) // n + c = (square - 1) % n + if r % 2 == 1: + c = n - 1 - c + r = n - 1 - r + return r, c + + dist = [-1] * (n * n + 1) + q = deque([1]) + dist[1] = 0 + + while q: + square = q.popleft() + + for i in range(1, 7): + nextSquare = square + i + if nextSquare > n * n: + break + + r, c = intToPos(nextSquare) + if board[r][c] != -1: + nextSquare = board[r][c] + + if dist[nextSquare] == -1: + dist[nextSquare] = dist[square] + 1 + if nextSquare == n * n: + return dist[nextSquare] + q.append(nextSquare) + + return -1 +``` + +```java +public class Solution { + public int snakesAndLadders(int[][] board) { + int n = board.length; + int[] dist = new int[n * n + 1]; + Arrays.fill(dist, -1); + Queue q = new LinkedList<>(); + q.add(1); + dist[1] = 0; + + while (!q.isEmpty()) { + int square = q.poll(); + + for (int i = 1; i <= 6; i++) { + int nextSquare = square + i; + if (nextSquare > n * n) { + break; + } + + int[] pos = intToPos(nextSquare, n); + int r = pos[0], c = pos[1]; + + if (board[r][c] != -1) { + nextSquare = board[r][c]; + } + + if (dist[nextSquare] == -1) { + dist[nextSquare] = dist[square] + 1; + if (nextSquare == n * n) { + return dist[nextSquare]; + } + q.add(nextSquare); + } + } + } + + return -1; + } + + private int[] intToPos(int square, int n) { + int r = (square - 1) / n; + int c = (square - 1) % n; + if (r % 2 == 1) { + c = n - 1 - c; + } + r = n - 1 - r; + return new int[]{r, c}; + } +} +``` + +```cpp +class Solution { +public: + int snakesAndLadders(vector>& board) { + int n = board.size(); + vector dist(n * n + 1, -1); + queue q; + q.push(1); + dist[1] = 0; + + while (!q.empty()) { + int square = q.front(); + q.pop(); + + for (int i = 1; i <= 6; i++) { + int nextSquare = square + i; + if (nextSquare > n * n) { + break; + } + + auto [r, c] = intToPos(nextSquare, n); + if (board[r][c] != -1) { + nextSquare = board[r][c]; + } + + if (dist[nextSquare] == -1) { + dist[nextSquare] = dist[square] + 1; + if (nextSquare == n * n) { + return dist[nextSquare]; + } + q.push(nextSquare); + } + } + } + + return -1; + } + +private: + pair intToPos(int square, int n) { + int r = (square - 1) / n; + int c = (square - 1) % n; + if (r % 2 == 1) { + c = n - 1 - c; + } + r = n - 1 - r; + return {r, c}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} board + * @return {number} + */ + snakesAndLadders(board) { + const n = board.length; + const dist = new Array(n * n + 1).fill(-1); + const queue = new Queue([1]); + dist[1] = 0; + + const intToPos = (square) => { + let r = Math.floor((square - 1) / n); + let c = (square - 1) % n; + if (r % 2 === 1) c = n - 1 - c; + r = n - 1 - r; + return [r, c]; + }; + + while (!queue.isEmpty()) { + const square = queue.pop(); + + for (let i = 1; i <= 6; i++) { + let nextSquare = square + i; + if (nextSquare > n * n) break; + + const [r, c] = intToPos(nextSquare, n); + if (board[r][c] !== -1) { + nextSquare = board[r][c]; + } + + if (dist[nextSquare] === -1) { + dist[nextSquare] = dist[square] + 1; + if (nextSquare === n * n) { + return dist[nextSquare]; + } + queue.push(nextSquare); + } + } + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Breadth First Search - III + +::tabs-start + +```python +class Solution: + def snakesAndLadders(self, board: List[List[int]]) -> int: + n = len(board) + + def intToPos(square): + r = (square - 1) // n + c = (square - 1) % n + if r % 2 == 1: + c = n - 1 - c + r = n - 1 - r + return r, c + + q = deque([1]) + board[n - 1][0] = 0 + moves = 0 + + while q: + for _ in range(len(q)): + square = q.popleft() + + for i in range(1, 7): + nextSquare = square + i + if nextSquare > n * n: + break + + r, c = intToPos(nextSquare) + if board[r][c] != -1: + nextSquare = board[r][c] + + if board[r][c] != 0: + if nextSquare == n * n: + return moves + 1 + q.append(nextSquare) + board[r][c] = 0 + moves += 1 + + return -1 +``` + +```java +public class Solution { + public int snakesAndLadders(int[][] board) { + int n = board.length; + Queue q = new LinkedList<>(); + q.add(1); + board[n - 1][0] = 0; + int moves = 0; + + while (!q.isEmpty()) { + for (int it = q.size(); it > 0; it--) { + int square = q.poll(); + for (int i = 1; i <= 6; i++) { + int nextSquare = square + i; + if (nextSquare > n * n) { + break; + } + + int[] pos = intToPos(nextSquare, n); + int r = pos[0], c = pos[1]; + + if (board[r][c] != -1) { + nextSquare = board[r][c]; + } + + if (board[r][c] != 0) { + if (nextSquare == n * n) { + return moves + 1; + } + + board[r][c] = 0; + q.add(nextSquare); + } + } + } + moves++; + } + + return -1; + } + + private int[] intToPos(int square, int n) { + int r = (square - 1) / n; + int c = (square - 1) % n; + if (r % 2 == 1) { + c = n - 1 - c; + } + r = n - 1 - r; + return new int[]{r, c}; + } +} +``` + +```cpp +class Solution { +public: + int snakesAndLadders(vector>& board) { + int n = board.size(); + queue q; + q.push(1); + board[n - 1][0] = 0; + int moves = 0; + + while (!q.empty()) { + for (int it = q.size(); it > 0; it--) { + int square = q.front(); q.pop(); + for (int i = 1; i <= 6; i++) { + int nextSquare = square + i; + if (nextSquare > n * n) { + break; + } + + auto [r, c] = intToPos(nextSquare, n); + if (board[r][c] != -1) { + nextSquare = board[r][c]; + } + + if (board[r][c] != 0) { + if (nextSquare == n * n) { + return moves + 1; + } + + board[r][c] = 0; + q.push(nextSquare); + } + } + } + moves++; + } + + return -1; + } + +private: + pair intToPos(int square, int n) { + int r = (square - 1) / n; + int c = (square - 1) % n; + if (r % 2 == 1) { + c = n - 1 - c; + } + r = n - 1 - r; + return {r, c}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} board + * @return {number} + */ + snakesAndLadders(board) { + const n = board.length; + const queue = new Queue([1]); + board[n - 1][0] = 0; + + const intToPos = (square) => { + let r = Math.floor((square - 1) / n); + let c = (square - 1) % n; + if (r % 2 === 1) c = n - 1 - c; + r = n - 1 - r; + return [r, c]; + }; + + let moves = 0; + while (!queue.isEmpty()) { + for (let it = queue.size(); it > 0; it--) { + const square = queue.pop(); + for (let i = 1; i <= 6; i++) { + let nextSquare = square + i; + if (nextSquare > n * n) break; + + const [r, c] = intToPos(nextSquare, n); + if (board[r][c] !== -1) { + nextSquare = board[r][c]; + } + + if (board[r][c] !== 0) { + if (nextSquare === n * n) { + return moves + 1; + } + + board[r][c] = 0; + queue.push(nextSquare); + } + } + } + moves++; + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/solving-questions-with-brainpower.md b/articles/solving-questions-with-brainpower.md new file mode 100644 index 000000000..248ec63b3 --- /dev/null +++ b/articles/solving-questions-with-brainpower.md @@ -0,0 +1,233 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def mostPoints(self, questions: List[List[int]]) -> int: + def dfs(i): + if i >= len(questions): + return 0 + return max(dfs(i + 1), questions[i][0] + dfs(i + 1 + questions[i][1])) + return dfs(0) +``` + +```java +public class Solution { + public long mostPoints(int[][] questions) { + return dfs(0, questions); + } + + private long dfs(int i, int[][] questions) { + if (i >= questions.length) return 0; + return Math.max(dfs(i + 1, questions), questions[i][0] + dfs(i + 1 + questions[i][1], questions)); + } +} +``` + +```cpp +class Solution { +public: + long long mostPoints(vector>& questions) { + return dfs(0, questions); + } + +private: + long long dfs(int i, vector>& questions) { + if (i >= questions.size()) return 0; + return max(dfs(i + 1, questions), questions[i][0] + dfs(i + 1 + questions[i][1], questions)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} questions + * @return {number} + */ + mostPoints(questions) { + const dfs = (i) => { + if (i >= questions.length) return 0; + return Math.max(dfs(i + 1), questions[i][0] + dfs(i + 1 + questions[i][1])); + }; + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def mostPoints(self, questions: List[List[int]]) -> int: + dp = {} + def dfs(i): + if i >= len(questions): + return 0 + if i in dp: + return dp[i] + dp[i] = max(dfs(i + 1), questions[i][0] + dfs(i + 1 + questions[i][1])) + return dp[i] + return dfs(0) +``` + +```java +public class Solution { + private long[] dp; + + public long mostPoints(int[][] questions) { + dp = new long[questions.length]; + return dfs(0, questions); + } + + private long dfs(int i, int[][] questions) { + if (i >= questions.length) return 0; + if (dp[i] != 0) return dp[i]; + dp[i] = Math.max(dfs(i + 1, questions), questions[i][0] + dfs(i + 1 + questions[i][1], questions)); + return dp[i]; + } +} + +``` + +```cpp +class Solution { + vector dp; + +public: + long long mostPoints(vector>& questions) { + dp.assign(questions.size(), 0); + return dfs(0, questions); + } + +private: + long long dfs(int i, vector>& questions) { + if (i >= questions.size()) return 0; + if (dp[i] != 0) return dp[i]; + dp[i] = max(dfs(i + 1, questions), questions[i][0] + dfs(i + 1 + questions[i][1], questions)); + return dp[i]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} questions + * @return {number} + */ + mostPoints(questions) { + const dp = new Array(questions.length).fill(-1); + + const dfs = (i) => { + if (i >= questions.length) return 0; + if (dp[i] !== -1) return dp[i]; + dp[i] = Math.max(dfs(i + 1), questions[i][0] + dfs(i + 1 + questions[i][1])); + return dp[i]; + }; + + return dfs(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def mostPoints(self, questions: List[List[int]]) -> int: + dp = {} + + for i in range(len(questions) - 1, -1, -1): + dp[i] = max( + questions[i][0] + dp.get(i + 1 + questions[i][1], 0), + dp.get(i + 1, 0) + ) + return dp.get(0) +``` + +```java +public class Solution { + public long mostPoints(int[][] questions) { + int n = questions.length; + long[] dp = new long[n + 1]; + + for (int i = n - 1; i >= 0; i--) { + dp[i] = Math.max( + questions[i][0] + (i + 1 + questions[i][1] < n ? dp[i + 1 + questions[i][1]] : 0), + dp[i + 1] + ); + } + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + long long mostPoints(vector>& questions) { + int n = questions.size(); + vector dp(n + 1, 0); + + for (int i = n - 1; i >= 0; i--) { + dp[i] = max( + (long long)questions[i][0] + (i + 1 + questions[i][1] < n ? dp[i + 1 + questions[i][1]] : 0), + dp[i + 1] + ); + } + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} questions + * @return {number} + */ + mostPoints(questions) { + const n = questions.length; + const dp = new Array(n + 1).fill(0); + + for (let i = n - 1; i >= 0; i--) { + dp[i] = Math.max( + questions[i][0] + (i + 1 + questions[i][1] < n ? dp[i + 1 + questions[i][1]] : 0), + dp[i + 1] + ); + } + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/sort-an-array.md b/articles/sort-an-array.md new file mode 100644 index 000000000..6b34f49d2 --- /dev/null +++ b/articles/sort-an-array.md @@ -0,0 +1,1372 @@ +## 1. Quick Sort + +::tabs-start + +```python +class Solution: + def partition(self, nums: List[int], left: int, right: int) -> int: + mid = (left + right) >> 1 + nums[mid], nums[left + 1] = nums[left + 1], nums[mid] + + if nums[left] > nums[right]: + nums[left], nums[right] = nums[right], nums[left] + if nums[left + 1] > nums[right]: + nums[left + 1], nums[right] = nums[right], nums[left + 1] + if nums[left] > nums[left + 1]: + nums[left], nums[left + 1] = nums[left + 1], nums[left] + + pivot = nums[left + 1] + i = left + 1 + j = right + + while True: + while True: + i += 1 + if not nums[i] < pivot: + break + while True: + j -= 1 + if not nums[j] > pivot: + break + if i > j: + break + nums[i], nums[j] = nums[j], nums[i] + + nums[left + 1], nums[j] = nums[j], nums[left + 1] + return j + + def quickSort(self, nums: List[int], left: int, right: int) -> None: + if right <= left + 1: + if right == left + 1 and nums[right] < nums[left]: + nums[left], nums[right] = nums[right], nums[left] + return + + j = self.partition(nums, left, right) + self.quickSort(nums, left, j - 1) + self.quickSort(nums, j + 1, right) + + def sortArray(self, nums: List[int]) -> List[int]: + self.quickSort(nums, 0, len(nums) - 1) + return nums +``` + +```java +public class Solution { + private int partition(int[] nums, int left, int right) { + int mid = (left + right) >> 1; + swap(nums, mid, left + 1); + + if (nums[left] > nums[right]) + swap(nums, left, right); + if (nums[left + 1] > nums[right]) + swap(nums, left + 1, right); + if (nums[left] > nums[left + 1]) + swap(nums, left, left + 1); + + int pivot = nums[left + 1]; + int i = left + 1; + int j = right; + + while (true) { + while (nums[++i] < pivot); + while (nums[--j] > pivot); + if (i > j) break; + swap(nums, i, j); + } + + nums[left + 1] = nums[j]; + nums[j] = pivot; + return j; + } + + private void swap(int[] nums, int i, int j) { + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } + + private void quickSort(int[] nums, int left, int right) { + if (right <= left + 1) { + if (right == left + 1 && nums[right] < nums[left]) + swap(nums, left, right); + return; + } + + int j = partition(nums, left, right); + quickSort(nums, left, j - 1); + quickSort(nums, j + 1, right); + } + + public int[] sortArray(int[] nums) { + quickSort(nums, 0, nums.length - 1); + return nums; + } +} +``` + +```cpp +class Solution { +public: +int partition(vector& nums, int left, int right) { + int mid = (left + right) >> 1; + swap(nums[mid], nums[left + 1]); + + if (nums[left] > nums[right]) + swap(nums[left], nums[right]); + if (nums[left + 1] > nums[right]) + swap(nums[left + 1], nums[right]); + if (nums[left] > nums[left + 1]) + swap(nums[left], nums[left + 1]); + + int pivot = nums[left + 1]; + int i = left + 1; + int j = right; + + while (true) { + while (nums[++i] < pivot); + while (nums[--j] > pivot); + if (i > j) break; + swap(nums[i], nums[j]); + } + + nums[left + 1] = nums[j]; + nums[j] = pivot; + return j; + } + + void quickSort(vector& nums, int left, int right) { + if (right <= left + 1) { + if (right == left + 1 && nums[right] < nums[left]) + swap(nums[left], nums[right]); + return; + } + + int j = partition(nums, left, right); + quickSort(nums, left, j - 1); + quickSort(nums, j + 1, right); + } + + vector sortArray(vector& nums) { + quickSort(nums, 0, nums.size() - 1); + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortArray(nums) { + function partition(left, right) { + const mid = (left + right) >> 1; + [nums[mid], nums[left + 1]] = [nums[left + 1], nums[mid]]; + + if (nums[left] > nums[right]) + [nums[left], nums[right]] = [nums[right], nums[left]]; + if (nums[left + 1] > nums[right]) + [nums[left + 1], nums[right]] = [nums[right], nums[left + 1]]; + if (nums[left] > nums[left + 1]) + [nums[left], nums[left + 1]] = [nums[left + 1], nums[left]]; + + const pivot = nums[left + 1]; + let i = left + 1; + let j = right; + + while (true) { + while (nums[++i] < pivot); + while (nums[--j] > pivot); + if (i > j) break; + [nums[i], nums[j]] = [nums[j], nums[i]]; + } + + nums[left + 1] = nums[j]; + nums[j] = pivot; + return j; + } + + function quickSort(left, right) { + if (right <= left + 1) { + if (right == left + 1 && nums[right] < nums[left]) + [nums[left], nums[right]] = [nums[right], nums[left]]; + return; + } + + const j = partition(left, right); + quickSort(left, j - 1); + quickSort(j + 1, right); + } + + quickSort(0, nums.length - 1); + return nums; + } +} +``` + +```csharp +public class Solution { + public int[] SortArray(int[] nums) { + QuickSort(nums, 0, nums.Length - 1); + return nums; + } + + private void QuickSort(int[] nums, int left, int right) { + if (right <= left + 1) { + if (right == left + 1 && nums[right] < nums[left]) { + Swap(nums, left, right); + } + return; + } + + int j = Partition(nums, left, right); + QuickSort(nums, left, j - 1); + QuickSort(nums, j + 1, right); + } + + private int Partition(int[] nums, int left, int right) { + int mid = (left + right) >> 1; + Swap(nums, mid, left + 1); + + if (nums[left] > nums[right]) Swap(nums, left, right); + if (nums[left + 1] > nums[right]) Swap(nums, left + 1, right); + if (nums[left] > nums[left + 1]) Swap(nums, left, left + 1); + + int pivot = nums[left + 1]; + int i = left + 1, j = right; + + while (true) { + while (++i <= right && nums[i] < pivot) ; + while (--j >= left && nums[j] > pivot) ; + + if (i > j) break; + + Swap(nums, i, j); + } + + Swap(nums, left + 1, j); + return j; + } + + private void Swap(int[] nums, int i, int j) { + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ in average case, $O(n ^ 2)$ in worst case. +* Space complexity: $O(\log n)$ for recursive stack. + +--- + +## 2. Merge Sort + +::tabs-start + +```python +class Solution: + def sortArray(self, nums: List[int]) -> List[int]: + def merge(arr, L, M, R): + left, right = arr[L : M + 1], arr[M + 1 : R + 1] + i, j, k = L, 0, 0 + + while j < len(left) and k < len(right): + if left[j] <= right[k]: + arr[i] = left[j] + j += 1 + else: + arr[i] = right[k] + k += 1 + i += 1 + while j < len(left): + nums[i] = left[j] + j += 1 + i += 1 + while k < len(right): + nums[i] = right[k] + k += 1 + i += 1 + + def mergeSort(arr, l, r): + if l == r: + return + + m = (l + r) // 2 + mergeSort(arr, l, m) + mergeSort(arr, m + 1, r) + merge(arr, l, m, r) + return + + mergeSort(nums, 0, len(nums)) + return nums +``` + +```java +public class Solution { + public int[] sortArray(int[] nums) { + mergeSort(nums, 0, nums.length - 1); + return nums; + } + + private void mergeSort(int[] arr, int l, int r) { + if (l >= r) return; + int m = (l + r) / 2; + mergeSort(arr, l, m); + mergeSort(arr, m + 1, r); + merge(arr, l, m, r); + } + + private void merge(int[] arr, int l, int m, int r) { + ArrayList temp = new ArrayList<>(); + int i = l; + int j = m + 1; + + while (i <= m && j <= r) { + if (arr[i] <= arr[j]) { + temp.add(arr[i]); + i++; + } else { + temp.add(arr[j]); + j++; + } + } + + + while (i <= m) { + temp.add(arr[i]); + i++; + } + + while (j <= r) { + temp.add(arr[j]); + j++; + } + + for (i = l; i <= r; i++) { + arr[i] = temp.get(i - l); + } + } +} +``` + +```cpp +class Solution { +public: + vector sortArray(vector& nums) { + mergeSort(nums, 0, nums.size() - 1); + return nums; + } + +private: + void mergeSort(vector& arr, int l, int r) { + if (l >= r) return; + int m = (l + r) / 2; + mergeSort(arr, l, m); + mergeSort(arr, m + 1, r); + merge(arr, l, m, r); + } + + void merge(vector& arr, int l, int m, int r) { + vector temp; + int i = l, j = m + 1; + + while (i <= m && j <= r) { + if (arr[i] <= arr[j]) { + temp.push_back(arr[i++]); + } else { + temp.push_back(arr[j++]); + } + } + + while (i <= m) temp.push_back(arr[i++]); + while (j <= r) temp.push_back(arr[j++]); + + for (int i = l; i <= r; i++) { + arr[i] = temp[i - l]; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortArray(nums) { + this.mergeSort(nums, 0, nums.length - 1); + return nums; + } + + /** + * @param {number[]} arr + * @param {number} l + * @param {number} r + * @return {void} + */ + mergeSort(arr, l, r) { + if (l >= r) return; + let m = Math.floor((l + r) / 2); + this.mergeSort(arr, l, m); + this.mergeSort(arr, m + 1, r); + this.merge(arr, l, m, r); + } + + /** + * @param {number[]} arr + * @param {number} l + * @param {number} m + * @param {number} r + * @return {void} + */ + merge(arr, l, m, r) { + let temp = []; + let i = l, j = m + 1; + + while (i <= m && j <= r) { + if (arr[i] <= arr[j]) { + temp.push(arr[i++]); + } else { + temp.push(arr[j++]); + } + } + + while (i <= m) temp.push(arr[i++]); + while (j <= r) temp.push(arr[j++]); + + for (let i = l; i <= r; i++) { + arr[i] = temp[i - l]; + } + } +} +``` + +```csharp +public class Solution { + public int[] SortArray(int[] nums) { + MergeSort(nums, 0, nums.Length - 1); + return nums; + } + + private void MergeSort(int[] arr, int l, int r) { + if (l == r) return; + + int m = (l + r) / 2; + MergeSort(arr, l, m); + MergeSort(arr, m + 1, r); + Merge(arr, l, m, r); + } + + private void Merge(int[] arr, int L, int M, int R) { + int[] left = arr[L..(M + 1)]; + int[] right = arr[(M + 1)..(R + 1)]; + + int i = L, j = 0, k = 0; + + while (j < left.Length && k < right.Length) { + if (left[j] <= right[k]) { + arr[i++] = left[j++]; + } else { + arr[i++] = right[k++]; + } + } + + while (j < left.Length) { + arr[i++] = left[j++]; + } + + while (k < right.Length) { + arr[i++] = right[k++]; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Heap Sort + +::tabs-start + +```python +class Solution: + def sortArray(self, nums: List[int]) -> List[int]: + self.heapSort(nums) + return nums + + def heapify(self, arr, n, i): + l = (i << 1) + 1 + r = (i << 1) + 2 + largestNode = i + + if l < n and arr[l] > arr[largestNode]: + largestNode = l + + if r < n and arr[r] > arr[largestNode]: + largestNode = r + + if largestNode != i: + arr[i], arr[largestNode] = arr[largestNode], arr[i] + self.heapify(arr, n, largestNode) + + def heapSort(self, arr): + n = len(arr) + for i in range(n // 2 - 1, -1, -1): + self.heapify(arr, n, i) + + for i in range(n - 1, 0, -1): + arr[0], arr[i] = arr[i], arr[0] + self.heapify(arr, i, 0) +``` + +```java +public class Solution { + public int[] sortArray(int[] nums) { + heapSort(nums); + return nums; + } + + private void heapify(int[] arr, int n, int i) { + int l = (i << 1) + 1; + int r = (i << 1) + 2; + int largestNode = i; + + if (l < n && arr[l] > arr[largestNode]) { + largestNode = l; + } + + if (r < n && arr[r] > arr[largestNode]) { + largestNode = r; + } + + if (largestNode != i) { + int temp = arr[i]; + arr[i] = arr[largestNode]; + arr[largestNode] = temp; + heapify(arr, n, largestNode); + } + } + + private void heapSort(int[] arr) { + int n = arr.length; + for (int i = n / 2 - 1; i >= 0; i--) { + heapify(arr, n, i); + } + + for (int i = n - 1; i > 0; i--) { + int temp = arr[0]; + arr[0] = arr[i]; + arr[i] = temp; + heapify(arr, i, 0); + } + } +} +``` + +```cpp +class Solution { +public: + vector sortArray(vector& nums) { + heapSort(nums); + return nums; + } +private: + void heapify(vector& arr, int n, int i) { + int l = (i << 1) + 1; + int r = (i << 1) + 2; + int largestNode = i; + + if (l < n && arr[l] > arr[largestNode]) { + largestNode = l; + } + + if (r < n && arr[r] > arr[largestNode]) { + largestNode = r; + } + + if (largestNode != i) { + swap(arr[i], arr[largestNode]); + heapify(arr, n, largestNode); + } + } + + void heapSort(vector& arr) { + int n = arr.size(); + for (int i = n / 2 - 1; i >= 0; i--) { + heapify(arr, n, i); + } + + for (int i = n - 1; i > 0; i--) { + swap(arr[0], arr[i]); + heapify(arr, i, 0); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortArray(nums) { + this.heapSort(nums); + return nums; + } + + /** + * @param {number[]} arr + * @param {number} n + * @param {number} i + * @return {void} + */ + heapify(arr, n, i) { + let l = (i << 1) + 1; + let r = (i << 1) + 2; + let largestNode = i; + + if (l < n && arr[l] > arr[largestNode]) { + largestNode = l; + } + + if (r < n && arr[r] > arr[largestNode]) { + largestNode = r; + } + + if (largestNode !== i) { + [arr[i], arr[largestNode]] = [arr[largestNode], arr[i]]; + this.heapify(arr, n, largestNode); + } + } + + /** + * @param {number[]} arr + * @return {void} + */ + heapSort(arr) { + let n = arr.length; + for (let i = Math.floor(n / 2) - 1; i >= 0; i--) { + this.heapify(arr, n, i); + } + + for (let i = n - 1; i > 0; i--) { + [arr[0], arr[i]] = [arr[i], arr[0]]; + this.heapify(arr, i, 0); + } + } +} +``` + +```csharp +public class Solution { + public int[] SortArray(int[] nums) { + HeapSort(nums); + return nums; + } + + private void Heapify(int[] arr, int n, int i) { + int l = (i << 1) + 1; + int r = (i << 1) + 2; + int largestNode = i; + + if (l < n && arr[l] > arr[largestNode]) { + largestNode = l; + } + + if (r < n && arr[r] > arr[largestNode]) { + largestNode = r; + } + + if (largestNode != i) { + Swap(arr, i, largestNode); + Heapify(arr, n, largestNode); + } + } + + private void HeapSort(int[] arr) { + int n = arr.Length; + + for (int i = n / 2 - 1; i >= 0; i--) { + Heapify(arr, n, i); + } + + for (int i = n - 1; i > 0; i--) { + Swap(arr, 0, i); + Heapify(arr, i, 0); + } + } + + private void Swap(int[] arr, int i, int j) { + int temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(\log n)$ for recursive stack. + +--- + +## 4. Counting Sort + +::tabs-start + +```python +class Solution: + def sortArray(self, nums: List[int]) -> List[int]: + def counting_sort(): + count = defaultdict(int) + minVal, maxVal = min(nums), max(nums) + for val in nums: + count[val] += 1 + + index = 0 + for val in range(minVal, maxVal + 1): + while count[val] > 0: + nums[index] = val + index += 1 + count[val] -= 1 + + counting_sort() + return nums +``` + +```java +public class Solution { + private void countingSort(int[] arr) { + HashMap count = new HashMap<>(); + int minVal = arr[0], maxVal = arr[0]; + + for (int i = 0; i < arr.length; i++) { + minVal = Math.min(minVal, arr[i]); + maxVal = Math.max(maxVal, arr[i]); + count.put(arr[i], count.getOrDefault(arr[i], 0) + 1); + } + + int index = 0; + for (int val = minVal; val <= maxVal; ++val) { + while (count.getOrDefault(val, 0) > 0) { + arr[index] = val; + index += 1; + count.put(val, count.get(val) - 1); + } + } + } + + public int[] sortArray(int[] nums) { + countingSort(nums); + return nums; + } +} +``` + +```cpp +class Solution { +private: + void countingSort(vector &arr) { + unordered_map count; + int minVal = *min_element(arr.begin(), arr.end()); + int maxVal = *max_element(arr.begin(), arr.end()); + + for (auto& val : arr) { + count[val]++; + } + + int index = 0; + for (int val = minVal; val <= maxVal; ++val) { + while (count[val] > 0) { + arr[index] = val; + index += 1; + count[val] -= 1; + } + } + } + +public: + vector sortArray(vector& nums) { + countingSort(nums); + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortArray(nums) { + this.countingSort(nums); + return nums; + } + + /** + * @param {number[]} arr + * @return {void} + */ + countingSort(arr) { + let count = new Map(); + let minVal = Math.min(...nums); + let maxVal = Math.max(...nums); + + nums.forEach(val => { + if (!count.has(val)) { + count.set(val, 0); + } + count.set(val, count.get(val) + 1); + }); + + let index = 0; + for (let val = minVal; val <= maxVal; val += 1) { + while (count.get(val) > 0) { + nums[index] = val; + index += 1; + count.set(val, count.get(val) - 1); + } + } + } +} +``` + +```csharp +public class Solution { + public int[] SortArray(int[] nums) { + CountingSort(nums); + return nums; + } + + private void CountingSort(int[] nums) { + Dictionary count = new Dictionary(); + int minVal = int.MaxValue, maxVal = int.MinValue; + + foreach (int val in nums) { + if (!count.ContainsKey(val)) { + count[val] = 0; + } + count[val]++; + minVal = Math.Min(minVal, val); + maxVal = Math.Max(maxVal, val); + } + + int index = 0; + for (int val = minVal; val <= maxVal; val++) { + if (!count.ContainsKey(val)) continue; + while (count[val]-- > 0) { + nums[index++] = val; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + k)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums$ and $k$ is the range between the minimum and maximum values in the array. + +--- + +## 5. Radix Sort + +::tabs-start + +```python +class Solution: + def sortArray(self, nums: List[int]) -> List[int]: + def countSort(arr, n, d): + count = [0] * 10 + for num in arr: + count[(num // d) % 10] += 1 + for i in range(1, 10): + count[i] += count[i - 1] + + res = [0] * n + for i in range(n - 1, -1, -1): + idx = (arr[i] // d) % 10 + res[count[idx] - 1] = arr[i] + count[idx] -= 1 + + for i in range(n): + arr[i] = res[i] + + def radixSort(arr): + n = len(arr) + max_element = max(arr) + d = 1 + while max_element // d > 0: + countSort(arr, n, d) + d *= 10 + + negatives = [-num for num in nums if num < 0] + positives = [num for num in nums if num >= 0] + + if negatives: + radixSort(negatives) + negatives = [-num for num in reversed(negatives)] + + if positives: + radixSort(positives) + + return negatives + positives +``` + +```java +public class Solution { + public int[] sortArray(int[] nums) { + ArrayList negatives = new ArrayList<>(); + ArrayList positives = new ArrayList<>(); + + for (int num : nums) { + if (num < 0) { + negatives.add(-num); + } else { + positives.add(num); + } + } + + if (!negatives.isEmpty()) { + radixSort(negatives); + Collections.reverse(negatives); + for (int i = 0; i < negatives.size(); i++) { + negatives.set(i, -negatives.get(i)); + } + } + + if (!positives.isEmpty()) { + radixSort(positives); + } + + int index = 0; + for (int num : negatives) { + nums[index++] = num; + } + for (int num : positives) { + nums[index++] = num; + } + return nums; + } + + private void countSort(ArrayList arr, int n, int d) { + int[] count = new int[10]; + for (int num : arr) { + count[(num / d) % 10]++; + } + for (int i = 1; i < 10; i++) { + count[i] += count[i - 1]; + } + + ArrayList res = new ArrayList<>(Collections.nCopies(n, 0)); + for (int i = n - 1; i >= 0; i--) { + int idx = (arr.get(i) / d) % 10; + res.set(count[idx] - 1, arr.get(i)); + count[idx]--; + } + + for (int i = 0; i < n; i++) { + arr.set(i, res.get(i)); + } + } + + private void radixSort(ArrayList arr) { + int n = arr.size(); + int maxElement = Collections.max(arr); + int d = 1; + + while (maxElement / d > 0) { + countSort(arr, n, d); + d *= 10; + } + } +} +``` + +```cpp +class Solution { +public: + vector sortArray(vector& nums) { + vector negatives, positives; + + for (int num : nums) { + if (num < 0) { + negatives.push_back(-num); + } else { + positives.push_back(num); + } + } + + if (!negatives.empty()) { + radixSort(negatives); + reverse(negatives.begin(), negatives.end()); + for (int& num : negatives) { + num = -num; + } + } + + if (!positives.empty()) { + radixSort(positives); + } + + int index = 0; + for (int& num : negatives) { + nums[index++] = num; + } + for (int& num : positives) { + nums[index++] = num; + } + return nums; + } + +private: + void countSort(vector& arr, int n, int d) { + vector count(10, 0); + for (int num : arr) { + count[(num / d) % 10]++; + } + for (int i = 1; i < 10; i++) { + count[i] += count[i - 1]; + } + + vector res(n); + for (int i = n - 1; i >= 0; i--) { + int idx = (arr[i] / d) % 10; + res[count[idx] - 1] = arr[i]; + count[idx]--; + } + + for (int i = 0; i < n; i++) { + arr[i] = res[i]; + } + } + + void radixSort(vector& arr) { + int n = arr.size(); + int maxElement = *max_element(arr.begin(), arr.end()); + int d = 1; + + while (maxElement / d > 0) { + countSort(arr, n, d); + d *= 10; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortArray(nums) { + const negatives = nums.filter(num => num < 0).map(num => -num); + const positives = nums.filter(num => num >= 0); + + if (negatives.length > 0) { + this.radixSort(negatives); + negatives.reverse(); + for (let i = 0; i < negatives.length; i++) { + negatives[i] = -negatives[i]; + } + } + + if (positives.length > 0) { + this.radixSort(positives); + } + + return [...negatives, ...positives]; + } + + /** + * @param {number[]} arr + * @return {void} + */ + radixSort(arr) { + const maxElement = Math.max(...arr); + let d = 1; + + while (Math.floor(maxElement / d) > 0) { + this.countSort(arr, d); + d *= 10; + } + } + + /** + * @param {number[]} arr + * @param {number} d + * @return {void} + */ + countSort(arr, d) { + const count = Array(10).fill(0); + for (const num of arr) { + count[Math.floor(num / d) % 10]++; + } + for (let i = 1; i < 10; i++) { + count[i] += count[i - 1]; + } + + const res = Array(arr.length); + for (let i = arr.length - 1; i >= 0; i--) { + const idx = Math.floor(arr[i] / d) % 10; + res[count[idx] - 1] = arr[i]; + count[idx]--; + } + + for (let i = 0; i < arr.length; i++) { + arr[i] = res[i]; + } + } +} +``` + +```csharp +public class Solution { + public int[] SortArray(int[] nums) { + List negatives = new List(); + List positives = new List(); + + foreach (int num in nums) { + if (num < 0) negatives.Add(-num); + else positives.Add(num); + } + + if (negatives.Count > 0) { + RadixSort(negatives); + negatives.Reverse(); + for (int i = 0; i < negatives.Count; i++) { + negatives[i] = -negatives[i]; + } + } + + if (positives.Count > 0) { + RadixSort(positives); + } + + List result = new List(); + result.AddRange(negatives); + result.AddRange(positives); + + return result.ToArray(); + } + + private void RadixSort(List arr) { + int n = arr.Count; + int maxElement = 0; + foreach (int num in arr) { + if (num > maxElement) maxElement = num; + } + + int d = 1; + while (maxElement / d > 0) { + CountSort(arr, n, d); + d *= 10; + } + } + + private void CountSort(List arr, int n, int d) { + int[] count = new int[10]; + for (int i = 0; i < n; i++) { + int digit = (arr[i] / d) % 10; + count[digit]++; + } + + for (int i = 1; i < 10; i++) { + count[i] += count[i - 1]; + } + + int[] res = new int[n]; + for (int i = n - 1; i >= 0; i--) { + int digit = (arr[i] / d) % 10; + res[--count[digit]] = arr[i]; + } + + for (int i = 0; i < n; i++) { + arr[i] = res[i]; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(d * n)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums$ and $d$ is the number of digits in the maximum element of the array. + +--- + +## 6. Shell Sort + +::tabs-start + +```python +class Solution: + def sortArray(self, nums: List[int]) -> List[int]: + def shell_sort(nums, n): + gap = n // 2 + while gap >= 1: + for i in range(gap, n): + tmp = nums[i] + j = i - gap + while j >= 0 and nums[j] > tmp: + nums[j + gap] = nums[j] + j -= gap + nums[j + gap] = tmp + gap //= 2 + + n = len(nums) + if n == 1: + return nums + shell_sort(nums, n) + return nums +``` + +```java +public class Solution { + private void shellSort(int[] nums, int n) { + int gap = n / 2; + while (gap >= 1) { + for (int i = gap; i < n; i++) { + int tmp = nums[i]; + int j = i - gap; + while (j >= 0 && nums[j] > tmp) { + nums[j + gap] = nums[j]; + j -= gap; + } + nums[j + gap] = tmp; + } + gap /= 2; + } + } + + public int[] sortArray(int[] nums) { + int n = nums.length; + if (n == 1) return nums; + + shellSort(nums, n); + return nums; + } +} +``` + +```cpp +class Solution { +private: + void shellSort(vector& nums, int n) { + for (int gap = n / 2; gap >= 1; gap /= 2) { + for (int i = gap; i < n; i++) { + int tmp = nums[i]; + int j = i - gap; + while (j >= 0 && nums[j] > tmp) { + nums[j + gap] = nums[j]; + j -= gap; + } + nums[j + gap] = tmp; + } + } + } + +public: + vector sortArray(vector& nums) { + if (nums.size() == 1) return nums; + shellSort(nums, nums.size()); + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortArray(nums) { + const n = nums.length; + if (n === 1) return nums; + + const shellSort = () => { + let gap = Math.floor(n / 2); + while (gap >= 1) { + for (let i = gap; i < n; i++) { + let key = nums[i]; + let j = i - gap; + while (j >= 0 && nums[j] > key) { + nums[j + gap] = nums[j]; + j -= gap; + } + nums[j + gap] = key; + } + gap = Math.floor(gap / 2); + } + } + + shellSort(); + return nums; + } +} +``` + +```csharp +public class Solution { + public int[] SortArray(int[] nums) { + int n = nums.Length; + if (n == 1) return nums; + + ShellSort(nums, n); + return nums; + } + + private void ShellSort(int[] nums, int n) { + int gap = n / 2; + while (gap >= 1) { + for (int i = gap; i < n; i++) { + int tmp = nums[i]; + int j = i - gap; + while (j >= 0 && nums[j] > tmp) { + nums[j + gap] = nums[j]; + j -= gap; + } + nums[j + gap] = tmp; + } + gap /= 2; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ in average case, $O(n ^ 2)$ in worst case. +* Space complexity: $O(1)$ diff --git a/articles/sort-array-by-increasing-frequency.md b/articles/sort-array-by-increasing-frequency.md new file mode 100644 index 000000000..04a4e1c80 --- /dev/null +++ b/articles/sort-array-by-increasing-frequency.md @@ -0,0 +1,79 @@ +## 1. Custom Sort + +::tabs-start + +```python +class Solution: + def frequencySort(self, nums: List[int]) -> List[int]: + count = Counter(nums) + nums.sort(key=lambda n: (count[n], -n)) + return nums +``` + +```java +public class Solution { + public int[] frequencySort(int[] nums) { + Map count = new HashMap<>(); + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + Integer[] arr = Arrays.stream(nums).boxed().toArray(Integer[]::new); + Arrays.sort(arr, (a, b) -> { + int freqA = count.get(a), freqB = count.get(b); + if (freqA != freqB) return Integer.compare(freqA, freqB); + return Integer.compare(b, a); + }); + + return Arrays.stream(arr).mapToInt(i -> i).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector frequencySort(vector& nums) { + unordered_map count; + for (int num : nums) { + count[num]++; + } + + sort(nums.begin(), nums.end(), [&](int a, int b) { + if (count[a] != count[b]) return count[a] < count[b]; + return a > b; + }); + + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + frequencySort(nums) { + const count = {}; + for (let num of nums) { + count[num] = (count[num] || 0) + 1; + } + + nums.sort((a, b) => { + if (count[a] !== count[b]) return count[a] - count[b]; + return b - a; + }); + + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/sort-array-by-parity.md b/articles/sort-array-by-parity.md new file mode 100644 index 000000000..b282d0cea --- /dev/null +++ b/articles/sort-array-by-parity.md @@ -0,0 +1,325 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def sortArrayByParity(self, nums: List[int]) -> List[int]: + nums.sort(key = lambda x: x & 1) + return nums +``` + +```java +public class Solution { + public int[] sortArrayByParity(int[] nums) { + Integer[] A = Arrays.stream(nums).boxed().toArray(Integer[]::new); + Arrays.sort(A, (a, b) -> (a & 1) - (b & 1)); + return Arrays.stream(A).mapToInt(Integer::intValue).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector sortArrayByParity(vector& nums) { + sort(nums.begin(), nums.end(), [&](int& a, int& b) { + return (a & 1) < (b & 1); + }); + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortArrayByParity(nums) { + return nums.sort((a, b) => (a & 1) - (b & 1)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Array + +::tabs-start + +```python +class Solution: + def sortArrayByParity(self, nums: List[int]) -> List[int]: + even, odd = [], [] + for num in nums: + if num & 1: + odd.append(num) + else: + even.append(num) + + idx = 0 + for e in even: + nums[idx] = e + idx += 1 + for o in odd: + nums[idx] = o + idx += 1 + return nums +``` + +```java +public class Solution { + public int[] sortArrayByParity(int[] nums) { + List even = new ArrayList<>(); + List odd = new ArrayList<>(); + + for (int num : nums) { + if ((num & 1) == 1) { + odd.add(num); + } else { + even.add(num); + } + } + + int idx = 0; + for (int e : even) { + nums[idx++] = e; + } + for (int o : odd) { + nums[idx++] = o; + } + + return nums; + } +} +``` + +```cpp +class Solution { +public: + vector sortArrayByParity(vector& nums) { + vector even, odd; + + for (int& num : nums) { + if (num & 1) { + odd.push_back(num); + } else { + even.push_back(num); + } + } + + int idx = 0; + for (int& e : even) { + nums[idx++] = e; + } + for (int& o : odd) { + nums[idx++] = o; + } + + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortArrayByParity(nums) { + const even = []; + const odd = []; + + for (let num of nums) { + if (num % 2) { + odd.push(num); + } else { + even.push(num); + } + } + + let idx = 0; + for (let e of even) { + nums[idx++] = e; + } + for (let o of odd) { + nums[idx++] = o; + } + + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Two Pointers - I + +::tabs-start + +```python +class Solution: + def sortArrayByParity(self, nums: List[int]) -> List[int]: + i, j = 0, len(nums) - 1 + while i < j: + if nums[i] & 1: + nums[i], nums[j] = nums[j], nums[i] + j -= 1 + else: + i += 1 + return nums +``` + +```java +public class Solution { + public int[] sortArrayByParity(int[] nums) { + int i = 0, j = nums.length - 1; + while (i < j) { + if ((nums[i] & 1) == 1) { + int temp = nums[i]; + nums[i] = nums[j]; + nums[j--] = temp; + } else { + i++; + } + } + return nums; + } +} +``` + +```cpp +class Solution { +public: + vector sortArrayByParity(vector& nums) { + int i = 0, j = nums.size() - 1; + while (i < j) { + if ((nums[i] & 1) == 1) { + swap(nums[i], nums[j]); + j--; + } else { + i++; + } + } + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortArrayByParity(nums) { + let i = 0, j = nums.length - 1; + while (i < j) { + if ((nums[i] & 1) == 1) { + [nums[i], nums[j]] = [nums[j], nums[i]]; + j--; + } else { + i++; + } + } + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Two Pointers - II + +::tabs-start + +```python +class Solution: + def sortArrayByParity(self, nums: List[int]) -> List[int]: + l = 0 + for r in range(len(nums)): + if nums[r] % 2 == 0: + nums[l], nums[r] = nums[r], nums[l] + l += 1 + return nums +``` + +```java +public class Solution { + public int[] sortArrayByParity(int[] nums) { + for (int l = 0, r = 0; r < nums.length; r++) { + if (nums[r] % 2 == 0) { + int temp = nums[l]; + nums[l] = nums[r]; + nums[r] = temp; + l++; + } + } + return nums; + } +} +``` + +```cpp +class Solution { +public: + vector sortArrayByParity(vector& nums) { + for (int l = 0, r = 0; r < nums.size(); r++) { + if (nums[r] % 2 == 0) { + swap(nums[l], nums[r]); + l++; + } + } + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortArrayByParity(nums) { + for (let l = 0, r = 0; r < nums.length; r++) { + if (nums[r] % 2 == 0) { + [nums[l], nums[r]] = [nums[r], nums[l]]; + l++; + } + } + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/sort-characters-by-frequency.md b/articles/sort-characters-by-frequency.md new file mode 100644 index 000000000..779312faf --- /dev/null +++ b/articles/sort-characters-by-frequency.md @@ -0,0 +1,340 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def frequencySort(self, s: str) -> str: + count = Counter(s) + sorted_chars = sorted(s, key=lambda x: (-count[x], x)) + return ''.join(sorted_chars) +``` + +```java +public class Solution { + public String frequencySort(String s) { + int[] count = new int[123]; + for (char c : s.toCharArray()) { + count[c]++; + } + + Character[] chars = new Character[s.length()]; + for (int i = 0; i < s.length(); i++) { + chars[i] = s.charAt(i); + } + + Arrays.sort(chars, (a, b) -> { + if (count[b] == count[a]) { + return a - b; + } + return count[b] - count[a]; + }); + + StringBuilder result = new StringBuilder(); + for (char c : chars) { + result.append(c); + } + + return result.toString(); + } +} +``` + +```cpp +class Solution { +public: + string frequencySort(string s) { + vector count(123); + for (char c : s) { + count[c]++; + } + + vector chars(s.begin(), s.end()); + sort(chars.begin(), chars.end(), [&](char a, char b) { + if (count[b] == count[a]) { + return a < b; + } + return count[b] < count[a]; + }); + + return string(chars.begin(), chars.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + frequencySort(s) { + const count = {}; + for (const char of s) { + count[char] = (count[char] || 0) + 1; + } + + const sortedChars = [...s].sort((a, b) => { + if (count[b] === count[a]) { + return a.localeCompare(b); + } + return count[b] - count[a]; + }); + + return sortedChars.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Frequency Sort + +::tabs-start + +```python +class Solution: + def frequencySort(self, s: str) -> str: + count = [0] * 123 + for c in s: + count[ord(c)] += 1 + + freq = [(chr(i), count[i]) for i in range(123) if count[i] > 0] + freq.sort(key=lambda x: (-x[1], x[0])) + + return ''.join(char * freq for char, freq in freq) +``` + +```java +public class Solution { + public String frequencySort(String s) { + int[] count = new int[123]; + for (char c : s.toCharArray()) { + count[c]++; + } + + List freq = new ArrayList<>(); + for (int i = 0; i < 123; i++) { + if (count[i] > 0) { + freq.add(new int[]{i, count[i]}); + } + } + + freq.sort((a, b) -> { + if (b[1] == a[1]) { + return a[0] - b[0]; + } + return b[1] - a[1]; + }); + + StringBuilder res = new StringBuilder(); + for (int[] entry : freq) { + for (int i = 0; i < entry[1]; i++) { + res.append((char) entry[0]); + } + } + + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string frequencySort(string s) { + vector count(123, 0); + for (char c : s) { + count[c]++; + } + + vector> freq; + for (int i = 0; i < 123; i++) { + if (count[i] > 0) { + freq.emplace_back((char)i, count[i]); + } + } + + sort(freq.begin(), freq.end(), [](auto& a, auto& b) { + if (a.second == b.second) { + return a.first < b.first; + } + return a.second > b.second; + }); + + string res; + for (const auto& entry : freq) { + res += string(entry.second, entry.first); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + frequencySort(s) { + const count = new Array(123).fill(0); + for (const char of s) { + count[char.charCodeAt(0)]++; + } + + const freq = []; + for (let i = 0; i < 123; i++) { + if (count[i] > 0) { + freq.push([String.fromCharCode(i), count[i]]); + } + } + + freq.sort((a, b) => { + if (b[1] === a[1]) { + return a[0].localeCompare(b[0]); + } + return b[1] - a[1]; + }); + + let res = ''; + for (const [char, freqCount] of freq) { + res += char.repeat(freqCount); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for the output string. + +--- + +## 3. Bucket Sort + +::tabs-start + +```python +class Solution: + def frequencySort(self, s: str) -> str: + count = Counter(s) # char -> freq + buckets = defaultdict(list) # freq -> [char] + + for char, freq in count.items(): + buckets[freq].append(char) + + res = [] + for i in range(len(s), 0, -1): + if i in buckets: + for c in buckets[i]: + res.append(c * i) + + return "".join(res) +``` + +```java +public class Solution { + public String frequencySort(String s) { + Map count = new HashMap<>(); + for (char c : s.toCharArray()) { + count.put(c, count.getOrDefault(c, 0) + 1); + } + + List> buckets = new ArrayList<>(s.length() + 1); + for (int i = 0; i <= s.length(); i++) { + buckets.add(new ArrayList<>()); + } + + for (Map.Entry entry : count.entrySet()) { + buckets.get(entry.getValue()).add(entry.getKey()); + } + + StringBuilder res = new StringBuilder(); + for (int i = s.length(); i > 0; i--) { + for (char c : buckets.get(i)) { + for (int j = 0; j < i; j++) { + res.append(c); + } + } + } + + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string frequencySort(string s) { + unordered_map count; + for (char c : s) { + count[c]++; + } + + vector> buckets(s.size() + 1); + for (auto& entry : count) { + buckets[entry.second].push_back(entry.first); + } + + string res; + for (int i = s.size(); i > 0; i--) { + for (char c : buckets[i]) { + res += string(i, c); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {string} + */ + frequencySort(s) { + const count = {}; + for (const char of s) { + count[char] = (count[char] || 0) + 1; + } + + const buckets = Array.from({ length: s.length + 1 }, () => []); + for (const [char, freq] of Object.entries(count)) { + buckets[freq].push(char); + } + + let res = ''; + for (let i = s.length; i > 0; i--) { + for (const char of buckets[i]) { + res += char.repeat(i); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/sort-colors.md b/articles/sort-colors.md new file mode 100644 index 000000000..46bc4a86a --- /dev/null +++ b/articles/sort-colors.md @@ -0,0 +1,530 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def sortColors(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + nums.sort() +``` + +```java +public class Solution { + public void sortColors(int[] nums) { + Arrays.sort(nums); + } +} +``` + +```cpp +class Solution { +public: + void sortColors(vector& nums) { + sort(nums.begin(), nums.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ + sortColors(nums) { + nums.sort((a, b) => a - b); + } +} +``` + +```csharp +public class Solution { + public void SortColors(int[] nums) { + Array.Sort(nums); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Counting Sort + +::tabs-start + +```python +class Solution: + def sortColors(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + count = [0] * 3 + for num in nums: + count[num] += 1 + + index = 0 + for i in range(3): + while count[i]: + count[i] -= 1 + nums[index] = i + index += 1 +``` + +```java +public class Solution { + public void sortColors(int[] nums) { + int[] count = new int[3]; + for (int num : nums) { + count[num]++; + } + + int index = 0; + for (int i = 0; i < 3; i++) { + while (count[i]-- > 0) { + nums[index++] = i; + } + } + } +} +``` + +```cpp +class Solution { +public: + void sortColors(vector& nums) { + vector count(3); + for (int& num : nums) { + count[num]++; + } + + int index = 0; + for (int i = 0; i < 3; i++) { + while (count[i]-- > 0) { + nums[index++] = i; + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ + sortColors(nums) { + const count = new Int32Array(3); + for (let num of nums) { + count[num]++; + } + + let index = 0; + for (let i = 0; i < 3; i++) { + while (count[i]-- > 0) { + nums[index++] = i; + } + } + } +} +``` + +```csharp +public class Solution { + public void SortColors(int[] nums) { + int[] count = new int[3]; + foreach (int num in nums) { + count[num]++; + } + + int index = 0; + for (int i = 0; i < 3; i++) { + while (count[i]-- > 0) { + nums[index++] = i; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Three Pointers - I + +::tabs-start + +```python +class Solution: + def sortColors(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + l, r = 0, len(nums) - 1 + i = 0 + + def swap(i, j): + temp = nums[i] + nums[i] = nums[j] + nums[j] = temp + + while i <= r: + if nums[i] == 0: + swap(l, i) + l += 1 + elif nums[i] == 2: + swap(i, r) + r -= 1 + i -= 1 + i += 1 +``` + +```java +public class Solution { + public void sortColors(int[] nums) { + int i = 0, l = 0, r = nums.length - 1; + while (i <= r) { + if (nums[i] == 0) { + swap(nums, l, i); + l++; + } else if (nums[i] == 2) { + swap(nums, i, r); + r--; + i--; + } + i++; + } + } + + private void swap(int[] nums, int i, int j) { + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } +} +``` + +```cpp +class Solution { +public: + void sortColors(vector& nums) { + int i = 0, l = 0, r = nums.size() - 1; + while (i <= r) { + if (nums[i] == 0) { + swap(nums[l], nums[i]); + l++; + } else if (nums[i] == 2) { + swap(nums[i], nums[r]); + r--; + i--; + } + i++; + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ + sortColors(nums) { + let i = 0, l = 0, r = nums.length - 1; + while (i <= r) { + if (nums[i] == 0) { + [nums[l], nums[i]] = [nums[i], nums[l]]; + l++; + } else if (nums[i] == 2) { + [nums[i], nums[r]] = [nums[r], nums[i]]; + r--; + i--; + } + i++; + } + } +} +``` + +```csharp +public class Solution { + public void SortColors(int[] nums) { + int i = 0, l = 0, r = nums.Length - 1; + + while (i <= r) { + if (nums[i] == 0) { + Swap(nums, l, i); + l++; + } else if (nums[i] == 2) { + Swap(nums, i, r); + r--; + i--; + } + i++; + } + } + + private void Swap(int[] nums, int i, int j) { + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Three Pointers - II + +::tabs-start + +```python +class Solution: + def sortColors(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + zero = one = two = 0 + for i in range(len(nums)): + if nums[i] == 0: + nums[two] = 2 + nums[one] = 1 + nums[zero] = 0 + two += 1 + one += 1 + zero += 1 + elif nums[i] == 1: + nums[two] = 2 + nums[one] = 1 + two += 1 + one += 1 + else: + nums[two] = 2 + two += 1 +``` + +```java +public class Solution { + public void sortColors(int[] nums) { + int zero = 0, one = 0, two = 0; + for (int i = 0; i < nums.length; i++) { + if (nums[i] == 0) { + nums[two++] = 2; + nums[one++] = 1; + nums[zero++] = 0; + } else if (nums[i] == 1) { + nums[two++] = 2; + nums[one++] = 1; + } else { + nums[two++] = 2; + } + } + } +} +``` + +```cpp +class Solution { +public: + void sortColors(vector& nums) { + int zero = 0, one = 0, two = 0; + for (int i = 0; i < nums.size(); i++) { + if (nums[i] == 0) { + nums[two++] = 2; + nums[one++] = 1; + nums[zero++] = 0; + } else if (nums[i] == 1) { + nums[two++] = 2; + nums[one++] = 1; + } else { + nums[two++] = 2; + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ + sortColors(nums) { + let zero = 0, one = 0, two = 0; + for (let i = 0; i < nums.length; i++) { + if (nums[i] == 0) { + nums[two++] = 2; + nums[one++] = 1; + nums[zero++] = 0; + } else if (nums[i] == 1) { + nums[two++] = 2; + nums[one++] = 1; + } else { + nums[two++] = 2; + } + } + } +} +``` + +```csharp +public class Solution { + public void SortColors(int[] nums) { + int zero = 0, one = 0, two = 0; + + for (int i = 0; i < nums.Length; i++) { + if (nums[i] == 0) { + nums[two++] = 2; + nums[one++] = 1; + nums[zero++] = 0; + } else if (nums[i] == 1) { + nums[two++] = 2; + nums[one++] = 1; + } else { + nums[two++] = 2; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Three Pointers - III + +::tabs-start + +```python +class Solution: + def sortColors(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + zero = one = 0 + for two in range(len(nums)): + tmp = nums[two] + nums[two] = 2 + if tmp < 2: + nums[one] = 1 + one += 1 + if tmp < 1: + nums[zero] = 0 + zero += 1 +``` + +```java +public class Solution { + public void sortColors(int[] nums) { + int zero = 0, one = 0; + for (int two = 0; two < nums.length; two++) { + int tmp = nums[two]; + nums[two] = 2; + if (tmp < 2) { + nums[one++] = 1; + } + if (tmp < 1) { + nums[zero++] = 0; + } + } + } +} +``` + +```cpp +class Solution { +public: + void sortColors(vector& nums) { + int zero = 0, one = 0; + for (int two = 0; two < nums.size(); two++) { + int tmp = nums[two]; + nums[two] = 2; + if (tmp < 2) { + nums[one++] = 1; + } + if (tmp < 1) { + nums[zero++] = 0; + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ + sortColors(nums) { + let zero = 0, one = 0; + for (let two = 0; two < nums.length; two++) { + let tmp = nums[two]; + nums[two] = 2; + if (tmp < 2) { + nums[one++] = 1; + } + if (tmp < 1) { + nums[zero++] = 0; + } + } + } +} +``` + +```csharp +public class Solution { + public void SortColors(int[] nums) { + int zero = 0, one = 0; + + for (int two = 0; two < nums.Length; two++) { + int tmp = nums[two]; + nums[two] = 2; + + if (tmp < 2) { + nums[one++] = 1; + } + if (tmp < 1) { + nums[zero++] = 0; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/sort-items-by-groups-respecting-dependencies.md b/articles/sort-items-by-groups-respecting-dependencies.md new file mode 100644 index 000000000..69ce62c62 --- /dev/null +++ b/articles/sort-items-by-groups-respecting-dependencies.md @@ -0,0 +1,562 @@ +## 1. Topological Sort (DFS) + +::tabs-start + +```python +class Solution: + def sortItems(self, n, m, group, beforeItems): + for i in range(n): + if group[i] == -1: + group[i] = m + m += 1 + + item_adj = defaultdict(list) + group_adj = defaultdict(list) + for i in range(n): + for par in beforeItems[i]: + item_adj[par].append(i) + if group[i] != group[par]: + group_adj[group[par]].append(group[i]) + + itm = self.topo_sort(item_adj, n) + if not itm: return [] + grp = self.topo_sort(group_adj, m) + if not grp: return [] + + grouping = defaultdict(list) + for i in itm: + grouping[group[i]].append(i) + + res = [] + for g in grp: + res.extend(grouping[g]) + + return res + + def topo_sort(self, adj, N): + visited = [0] * N + topo = [] + + def dfs(node): + if visited[node] == 1: + return True + if visited[node] == 2: + return False + visited[node] = 1 + for neighbor in adj[node]: + if dfs(neighbor): + return True + topo.append(node) + visited[node] = 2 + return False + + for i in range(N): + if visited[i] == 0: + if dfs(i): + return [] + + return topo[::-1] +``` + +```java +public class Solution { + public int[] sortItems(int n, int m, int[] group, List> beforeItems) { + for (int i = 0; i < n; i++) { + if (group[i] == -1) { + group[i] = m++; + } + } + + List[] itemAdj = new ArrayList[n]; + List[] groupAdj = new ArrayList[m]; + for (int i = 0; i < n; i++) { + itemAdj[i] = new ArrayList<>(); + } + for (int i = 0; i < m; i++) { + groupAdj[i] = new ArrayList<>(); + } + + for (int i = 0; i < n; i++) { + for (int parent : beforeItems.get(i)) { + itemAdj[parent].add(i); + if (group[i] != group[parent]) { + groupAdj[group[parent]].add(group[i]); + } + } + } + + List itm = topoSort(itemAdj, n); + if (itm.isEmpty()) return new int[]{}; + List grp = topoSort(groupAdj, m); + if (grp.isEmpty()) return new int[]{}; + + List[] grouping = new ArrayList[m]; + for (int i = 0; i < m; i++) { + grouping[i] = new ArrayList<>(); + } + for (int i : itm) { + grouping[group[i]].add(i); + } + + List res = new ArrayList<>(); + for (int g : grp) { + res.addAll(grouping[g]); + } + + return res.stream().mapToInt(Integer::intValue).toArray(); + } + + private List topoSort(List[] adj, int N) { + int[] visited = new int[N]; + List topo = new ArrayList<>(); + + for (int i = 0; i < N; i++) { + if (visited[i] == 0) { + if (dfs(i, adj, visited, topo)) { + return new ArrayList<>(); + } + } + } + + Collections.reverse(topo); + return topo; + } + + private boolean dfs(int node, List[] adj, int[] visited, List topo) { + if (visited[node] == 1) return true; + if (visited[node] == 2) return false; + visited[node] = 1; + for (int neighbor : adj[node]) { + if (dfs(neighbor, adj, visited, topo)) { + return true; + } + } + topo.add(node); + visited[node] = 2; + return false; + } +} +``` + +```cpp +class Solution { +public: + vector sortItems(int n, int m, vector& group, vector>& beforeItems) { + for (int i = 0; i < n; ++i) { + if (group[i] == -1) { + group[i] = m++; + } + } + + vector> itemAdj(n), groupAdj(m); + for (int i = 0; i < n; ++i) { + for (int parent : beforeItems[i]) { + itemAdj[parent].push_back(i); + if (group[i] != group[parent]) { + groupAdj[group[parent]].push_back(group[i]); + } + } + } + + vector itm = topoSort(itemAdj, n); + if (itm.empty()) return {}; + vector grp = topoSort(groupAdj, m); + if (grp.empty()) return {}; + + unordered_map> grouping; + for (int i : itm) { + grouping[group[i]].push_back(i); + } + + vector res; + for (int& g : grp) { + res.insert(res.end(), grouping[g].begin(), grouping[g].end()); + } + + return res; + } + +private: + vector topoSort(const vector>& adj, int N) { + vector visited(N, 0), topo; + function dfs = [&](int node) { + if (visited[node] == 1) return true; + if (visited[node] == 2) return false; + visited[node] = 1; + for (int neighbor : adj[node]) { + if (dfs(neighbor)) return true; + } + topo.push_back(node); + visited[node] = 2; + return false; + }; + + for (int i = 0; i < N; ++i) { + if (visited[i] == 0 && dfs(i)) { + return {}; + } + } + + reverse(topo.begin(), topo.end()); + return topo; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} m + * @param {number[]} group + * @param {number[][]} beforeItems + * @return {number[]} + */ + sortItems(n, m, group, beforeItems) { + for (let i = 0; i < n; i++) { + if (group[i] === -1) { + group[i] = m++; + } + } + + const itemAdj = Array.from({ length: n }, () => []); + const groupAdj = Array.from({ length: m }, () => []); + for (let i = 0; i < n; i++) { + for (const parent of beforeItems[i]) { + itemAdj[parent].push(i); + if (group[i] !== group[parent]) { + groupAdj[group[parent]].push(group[i]); + } + } + } + + const itm = this.topoSort(itemAdj, n); + if (!itm.length) return []; + const grp = this.topoSort(groupAdj, m); + if (!grp.length) return []; + + const grouping = {}; + for (const i of itm) { + if (!grouping[group[i]]) grouping[group[i]] = []; + grouping[group[i]].push(i); + } + + const res = []; + for (const g of grp) { + if (grouping[g]) res.push(...grouping[g]); + } + + return res; + } + + /** + * @param {number[][]} adj + * @param {number} N + * @return {number[]} + */ + topoSort(adj, N) { + const visited = new Array(N).fill(0); + const topo = []; + + const dfs = (node) => { + if (visited[node] === 1) return true; + if (visited[node] === 2) return false; + visited[node] = 1; + for (const neighbor of adj[node]) { + if (dfs(neighbor)) return true; + } + topo.push(node); + visited[node] = 2; + return false; + }; + + for (let i = 0; i < N; i++) { + if (visited[i] === 0 && dfs(i)) { + return []; + } + } + + return topo.reverse(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of items and $E$ is the total number of $beforeItems$ dependencies. + +--- + +## 2. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def sortItems(self, n, m, group, beforeItems): + for i in range(n): + if group[i] == -1: + group[i] = m + m += 1 + + item_adj = defaultdict(list) + group_adj = defaultdict(list) + item_indegree = [0] * n + group_indegree = [0] * m + + for i in range(n): + for par in beforeItems[i]: + item_adj[par].append(i) + item_indegree[i] += 1 + if group[i] != group[par]: + group_adj[group[par]].append(group[i]) + group_indegree[group[i]] += 1 + + itm = self.topo_sort(item_adj, item_indegree, n) + if not itm: return [] + grp = self.topo_sort(group_adj, group_indegree, m) + if not grp: return [] + + grouping = defaultdict(list) + for i in itm: + grouping[group[i]].append(i) + + res = [] + for g in grp: + res.extend(grouping[g]) + + return res + + def topo_sort(self, adj, indegree, N): + topo = [] + q = deque([i for i in range(N) if indegree[i] == 0]) + + while q: + node = q.popleft() + topo.append(node) + for neighbor in adj[node]: + indegree[neighbor] -= 1 + if indegree[neighbor] == 0: + q.append(neighbor) + + return topo if len(topo) == N else [] +``` + +```java +public class Solution { + public int[] sortItems(int n, int m, int[] group, List> beforeItems) { + for (int i = 0; i < n; i++) { + if (group[i] == -1) { + group[i] = m++; + } + } + + List> itemAdj = new ArrayList<>(); + List> groupAdj = new ArrayList<>(); + for (int i = 0; i < n; i++) itemAdj.add(new ArrayList<>()); + for (int i = 0; i < m; i++) groupAdj.add(new ArrayList<>()); + + int[] itemIndegree = new int[n]; + int[] groupIndegree = new int[m]; + + for (int i = 0; i < n; i++) { + for (int par : beforeItems.get(i)) { + itemAdj.get(par).add(i); + itemIndegree[i]++; + if (group[i] != group[par]) { + groupAdj.get(group[par]).add(group[i]); + groupIndegree[group[i]]++; + } + } + } + + List itm = topoSort(itemAdj, itemIndegree, n); + if (itm.isEmpty()) return new int[0]; + List grp = topoSort(groupAdj, groupIndegree, m); + if (grp.isEmpty()) return new int[0]; + + Map> grouping = new HashMap<>(); + for (int i : itm) { + grouping.computeIfAbsent(group[i], x -> new ArrayList<>()).add(i); + } + + List res = new ArrayList<>(); + for (int g : grp) { + res.addAll(grouping.getOrDefault(g, new ArrayList<>())); + } + + return res.stream().mapToInt(Integer::intValue).toArray(); + } + + private List topoSort(List> adj, int[] indegree, int N) { + Queue q = new LinkedList<>(); + List topo = new ArrayList<>(); + for (int i = 0; i < N; i++) { + if (indegree[i] == 0) q.add(i); + } + + while (!q.isEmpty()) { + int node = q.poll(); + topo.add(node); + for (int neighbor : adj.get(node)) { + indegree[neighbor]--; + if (indegree[neighbor] == 0) q.add(neighbor); + } + } + + return topo.size() == N ? topo : new ArrayList<>(); + } +} +``` + +```cpp +class Solution { +public: + vector sortItems(int n, int m, vector& group, vector>& beforeItems) { + for (int i = 0; i < n; i++) { + if (group[i] == -1) group[i] = m++; + } + + vector> itemAdj(n), groupAdj(m); + vector itemIndegree(n, 0), groupIndegree(m, 0); + + for (int i = 0; i < n; i++) { + for (int& par : beforeItems[i]) { + itemAdj[par].push_back(i); + itemIndegree[i]++; + if (group[i] != group[par]) { + groupAdj[group[par]].push_back(group[i]); + groupIndegree[group[i]]++; + } + } + } + + vector itm = topoSort(itemAdj, itemIndegree, n); + if (itm.empty()) return {}; + vector grp = topoSort(groupAdj, groupIndegree, m); + if (grp.empty()) return {}; + + unordered_map> grouping; + for (int& i : itm) { + grouping[group[i]].push_back(i); + } + + vector res; + for (int& g : grp) { + res.insert(res.end(), grouping[g].begin(), grouping[g].end()); + } + + return res; + } + +private: + vector topoSort(vector>& adj, vector& indegree, int N) { + queue q; + vector topo; + for (int i = 0; i < N; i++) { + if (indegree[i] == 0) q.push(i); + } + + while (!q.empty()) { + int node = q.front(); q.pop(); + topo.push_back(node); + for (int& neighbor : adj[node]) { + indegree[neighbor]--; + if (indegree[neighbor] == 0) q.push(neighbor); + } + } + + return topo.size() == N ? topo : vector(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} m + * @param {number[]} group + * @param {number[][]} beforeItems + * @return {number[]} + */ + sortItems(n, m, group, beforeItems) { + for (let i = 0; i < n; i++) { + if (group[i] === -1) group[i] = m++; + } + + const itemAdj = Array.from({ length: n }, () => []); + const groupAdj = Array.from({ length: m }, () => []); + const itemIndegree = Array(n).fill(0); + const groupIndegree = Array(m).fill(0); + + for (let i = 0; i < n; i++) { + for (const par of beforeItems[i]) { + itemAdj[par].push(i); + itemIndegree[i]++; + if (group[i] !== group[par]) { + groupAdj[group[par]].push(group[i]); + groupIndegree[group[i]]++; + } + } + } + + const itm = this.topoSort(itemAdj, itemIndegree, n); + if (itm.length === 0) return []; + const grp = this.topoSort(groupAdj, groupIndegree, m); + if (grp.length === 0) return []; + + const grouping = new Map(); + for (const i of itm) { + if (!grouping.has(group[i])) grouping.set(group[i], []); + grouping.get(group[i]).push(i); + } + + const res = []; + for (const g of grp) { + if (grouping.has(g)) res.push(...grouping.get(g)); + } + + return res; + } + + /** + * @param {number[][]} adj + * @param {number[]} indegree + * @param {number} N + * @return {number[]} + */ + topoSort(adj, indegree, N) { + const q = new Queue(); + const topo = []; + for (let i = 0; i < N; i++) { + if (indegree[i] === 0) q.push(i); + } + + while (!q.isEmpty()) { + const node = q.pop(); + topo.push(node); + for (const neighbor of adj[node]) { + indegree[neighbor]--; + if (indegree[neighbor] === 0) q.push(neighbor); + } + } + + return topo.length === N ? topo : []; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number of items and $E$ is the total number of $beforeItems$ dependencies. \ No newline at end of file diff --git a/articles/sort-list.md b/articles/sort-list.md new file mode 100644 index 000000000..768ec1c4c --- /dev/null +++ b/articles/sort-list.md @@ -0,0 +1,730 @@ +## 1. Convert To Array + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]: + arr = [] + cur = head + + while cur: + arr.append(cur.val) + cur = cur.next + + arr.sort() + cur = head + for val in arr: + cur.val = val + cur = cur.next + + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode sortList(ListNode head) { + if (head == null) return null; + + List arr = new ArrayList<>(); + ListNode cur = head; + + while (cur != null) { + arr.add(cur.val); + cur = cur.next; + } + + Collections.sort(arr); + cur = head; + for (int val : arr) { + cur.val = val; + cur = cur.next; + } + + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* sortList(ListNode* head) { + if (!head) return nullptr; + + vector arr; + ListNode* cur = head; + + while (cur) { + arr.push_back(cur->val); + cur = cur->next; + } + + sort(arr.begin(), arr.end()); + cur = head; + for (int val : arr) { + cur->val = val; + cur = cur->next; + } + + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + sortList(head) { + if (!head) return null; + + let arr = []; + let cur = head; + + while (cur) { + arr.push(cur.val); + cur = cur.next; + } + + arr.sort((a, b) => a - b); + cur = head; + for (let val of arr) { + cur.val = val; + cur = cur.next; + } + + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Recursive Merge Sort + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]: + if not head or not head.next: + return head + + left = head + right = self.getMid(head) + tmp = right.next + right.next = None + right = tmp + + left = self.sortList(left) + right = self.sortList(right) + return self.merge(left, right) + + def getMid(self, head): + slow, fast = head, head.next + while fast and fast.next: + slow = slow.next + fast = fast.next.next + return slow + + def merge(self, list1, list2): + tail = dummy = ListNode() + while list1 and list2: + if list1.val < list2.val: + tail.next = list1 + list1 = list1.next + else: + tail.next = list2 + list2 = list2.next + tail = tail.next + + if list1: + tail.next = list1 + if list2: + tail.next = list2 + + return dummy.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode sortList(ListNode head) { + if (head == null || head.next == null) { + return head; + } + + ListNode left = head; + ListNode right = getMid(head); + ListNode temp = right.next; + right.next = null; + right = temp; + + left = sortList(left); + right = sortList(right); + return merge(left, right); + } + + private ListNode getMid(ListNode head) { + ListNode slow = head, fast = head.next; + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + } + return slow; + } + + private ListNode merge(ListNode list1, ListNode list2) { + ListNode dummy = new ListNode(0); + ListNode tail = dummy; + + while (list1 != null && list2 != null) { + if (list1.val < list2.val) { + tail.next = list1; + list1 = list1.next; + } else { + tail.next = list2; + list2 = list2.next; + } + tail = tail.next; + } + + if (list1 != null) { + tail.next = list1; + } + if (list2 != null) { + tail.next = list2; + } + + return dummy.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* sortList(ListNode* head) { + if (!head || !head->next) { + return head; + } + + ListNode* left = head; + ListNode* right = getMid(head); + ListNode* temp = right->next; + right->next = nullptr; + right = temp; + + left = sortList(left); + right = sortList(right); + return merge(left, right); + } + +private: + ListNode* getMid(ListNode* head) { + ListNode* slow = head; + ListNode* fast = head->next; + while (fast && fast->next) { + slow = slow->next; + fast = fast->next->next; + } + return slow; + } + + ListNode* merge(ListNode* list1, ListNode* list2) { + ListNode dummy(0); + ListNode* tail = &dummy; + + while (list1 && list2) { + if (list1->val < list2->val) { + tail->next = list1; + list1 = list1->next; + } else { + tail->next = list2; + list2 = list2->next; + } + tail = tail->next; + } + + if (list1) { + tail->next = list1; + } + if (list2) { + tail->next = list2; + } + + return dummy.next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + sortList(head) { + if (!head || !head.next) { + return head; + } + + let left = head; + let right = this.getMid(head); + let temp = right.next; + right.next = null; + right = temp; + + left = this.sortList(left); + right = this.sortList(right); + return this.merge(left, right); + } + + /** + * @param {ListNode} head + * @return {ListNode} + */ + getMid(head) { + let slow = head, fast = head.next; + while (fast && fast.next) { + slow = slow.next; + fast = fast.next.next; + } + return slow; + } + + /** + * @param {ListNode} list1 + * @param {ListNode} list2 + * @return {ListNode} + */ + merge(list1, list2) { + let dummy = new ListNode(0); + let tail = dummy; + + while (list1 && list2) { + if (list1.val < list2.val) { + tail.next = list1; + list1 = list1.next; + } else { + tail.next = list2; + list2 = list2.next; + } + tail = tail.next; + } + + if (list1) { + tail.next = list1; + } + if (list2) { + tail.next = list2; + } + + return dummy.next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(\log n)$ for recursion stack. + +--- + +## 3. Iterative Merge Sort + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]: + if not head or not head.next: + return head + + length = 0 + cur = head + while cur: + length += 1 + cur = cur.next + + dummy = ListNode(0) + dummy.next = head + step = 1 + + while step < length: + prev, curr = dummy, dummy.next + while curr: + left = curr + right = self.split(left, step) + curr = self.split(right, step) + merged = self.merge(left, right) + prev.next = merged + while prev.next: + prev = prev.next + step *= 2 + + return dummy.next + + def split(self, head, step): + if not head: + return None + for _ in range(step - 1): + if not head.next: + break + head = head.next + next_part = head.next + head.next = None + return next_part + + def merge(self, list1, list2): + tail = dummy = ListNode(0) + while list1 and list2: + if list1.val < list2.val: + tail.next = list1 + list1 = list1.next + else: + tail.next = list2 + list2 = list2.next + tail = tail.next + + tail.next = list1 or list2 + return dummy.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode sortList(ListNode head) { + if (head == null || head.next == null) { + return head; + } + + int length = 0; + ListNode cur = head; + while (cur != null) { + length++; + cur = cur.next; + } + + ListNode dummy = new ListNode(0); + dummy.next = head; + int step = 1; + + while (step < length) { + ListNode prev = dummy, curr = dummy.next; + while (curr != null) { + ListNode left = curr; + ListNode right = split(left, step); + curr = split(right, step); + ListNode merged = merge(left, right); + prev.next = merged; + while (prev.next != null) { + prev = prev.next; + } + } + step *= 2; + } + + return dummy.next; + } + + private ListNode split(ListNode head, int step) { + if (head == null) return null; + for (int i = 0; i < step - 1 && head.next != null; i++) { + head = head.next; + } + ListNode nextPart = head.next; + head.next = null; + return nextPart; + } + + private ListNode merge(ListNode list1, ListNode list2) { + ListNode dummy = new ListNode(0); + ListNode tail = dummy; + + while (list1 != null && list2 != null) { + if (list1.val < list2.val) { + tail.next = list1; + list1 = list1.next; + } else { + tail.next = list2; + list2 = list2.next; + } + tail = tail.next; + } + + tail.next = (list1 != null) ? list1 : list2; + return dummy.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +class Solution { +public: + ListNode* sortList(ListNode* head) { + if (!head || !head->next) { + return head; + } + + int length = 0; + ListNode* cur = head; + while (cur) { + length++; + cur = cur->next; + } + + ListNode dummy(0); + dummy.next = head; + int step = 1; + + while (step < length) { + ListNode* prev = &dummy, *curr = dummy.next; + while (curr) { + ListNode* left = curr; + ListNode* right = split(left, step); + curr = split(right, step); + ListNode* merged = merge(left, right); + prev->next = merged; + while (prev->next) { + prev = prev->next; + } + } + step *= 2; + } + + return dummy.next; + } + +private: + ListNode* split(ListNode* head, int step) { + if (!head) return nullptr; + for (int i = 0; i < step - 1 && head->next; i++) { + head = head->next; + } + ListNode* nextPart = head->next; + head->next = nullptr; + return nextPart; + } + + ListNode* merge(ListNode* list1, ListNode* list2) { + ListNode dummy(0); + ListNode* tail = &dummy; + + while (list1 && list2) { + if (list1->val < list2->val) { + tail->next = list1; + list1 = list1->next; + } else { + tail->next = list2; + list2 = list2->next; + } + tail = tail->next; + } + + tail->next = list1 ? list1 : list2; + return dummy.next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + sortList(head) { + if (!head || !head.next) { + return head; + } + + let length = 0; + let cur = head; + while (cur) { + length++; + cur = cur.next; + } + + let dummy = new ListNode(0); + dummy.next = head; + let step = 1; + + while (step < length) { + let prev = dummy, curr = dummy.next; + while (curr) { + let left = curr; + let right = this.split(left, step); + curr = this.split(right, step); + let merged = this.merge(left, right); + prev.next = merged; + while (prev.next) { + prev = prev.next; + } + } + step *= 2; + } + + return dummy.next; + } + + /** + * @param {ListNode} head + * @param {number} step + * @return {ListNode} + */ + split(head, step) { + if (!head) return null; + for (let i = 0; i < step - 1 && head.next; i++) { + head = head.next; + } + let nextPart = head.next; + head.next = null; + return nextPart; + } + + /** + * @param {ListNode} list1 + * @param {ListNode} list2 + * @return {ListNode} + */ + merge(list1, list2) { + let dummy = new ListNode(0); + let tail = dummy; + + while (list1 && list2) { + if (list1.val < list2.val) { + tail.next = list1; + list1 = list1.next; + } else { + tail.next = list2; + list2 = list2.next; + } + tail = tail.next; + } + + tail.next = list1 || list2; + return dummy.next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/sort-the-jumbled-numbers.md b/articles/sort-the-jumbled-numbers.md new file mode 100644 index 000000000..b8c0cbf47 --- /dev/null +++ b/articles/sort-the-jumbled-numbers.md @@ -0,0 +1,244 @@ +## 1. Convert To Strings + Sorting + +::tabs-start + +```python +class Solution: + def sortJumbled(self, mapping: List[int], nums: List[int]) -> List[int]: + pairs = [] + + for i, n in enumerate(nums): + n = str(n) + mapped_n = 0 + for c in n: + mapped_n *= 10 + mapped_n += mapping[int(c)] + pairs.append((mapped_n, i)) + + pairs.sort() + return [nums[p[1]] for p in pairs] +``` + +```java +public class Solution { + public int[] sortJumbled(int[] mapping, int[] nums) { + int n = nums.length; + int[][] pairs = new int[n][2]; + + for (int i = 0; i < n; i++) { + String numStr = String.valueOf(nums[i]); + int mapped_n = 0; + for (char c : numStr.toCharArray()) { + mapped_n = mapped_n * 10 + mapping[c - '0']; + } + pairs[i][0] = mapped_n; + pairs[i][1] = i; + } + + Arrays.sort(pairs, (a, b) -> a[0] - b[0]); + + int[] res = new int[n]; + for (int i = 0; i < n; i++) { + res[i] = nums[pairs[i][1]]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector sortJumbled(vector& mapping, vector& nums) { + vector> pairs; + + for (int i = 0; i < nums.size(); ++i) { + string numStr = to_string(nums[i]); + int mapped_n = 0; + for (char c : numStr) { + mapped_n = mapped_n * 10 + mapping[c - '0']; + } + pairs.push_back({mapped_n, i}); + } + + sort(pairs.begin(), pairs.end()); + + vector res; + for (auto& p : pairs) { + res.push_back(nums[p.second]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} mapping + * @param {number[]} nums + * @return {number[]} + */ + sortJumbled(mapping, nums) { + let pairs = []; + + for (let i = 0; i < nums.length; i++) { + let numStr = nums[i].toString(); + let mapped_n = 0; + for (let c of numStr) { + mapped_n = mapped_n * 10 + mapping[parseInt(c)]; + } + pairs.push([mapped_n, i]); + } + + pairs.sort((a, b) => a[0] - b[0]); + + return pairs.map(p => nums[p[1]]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Iterate On Numbers + Sorting + +::tabs-start + +```python +class Solution: + def sortJumbled(self, mapping: List[int], nums: List[int]) -> List[int]: + pairs = [] + + for i, n in enumerate(nums): + mapped_n = 0 + base = 1 + + if n == 0: + mapped_n = mapping[0] + else: + while n > 0: + digit = n % 10 + n //= 10 + mapped_n += base * mapping[digit] + base *= 10 + + pairs.append((mapped_n, i)) + + pairs.sort() + return [nums[p[1]] for p in pairs] +``` + +```java +public class Solution { + public int[] sortJumbled(int[] mapping, int[] nums) { + int n = nums.length; + int[][] pairs = new int[n][2]; + + for (int i = 0; i < n; i++) { + int mapped_n = 0, base = 1; + int num = nums[i]; + + if (num == 0) { + mapped_n = mapping[0]; + } else { + while (num > 0) { + int digit = num % 10; + num /= 10; + mapped_n += base * mapping[digit]; + base *= 10; + } + } + + pairs[i][0] = mapped_n; + pairs[i][1] = i; + } + + Arrays.sort(pairs, (a, b) -> Integer.compare(a[0], b[0])); + + int[] res = new int[n]; + for (int i = 0; i < n; i++) { + res[i] = nums[pairs[i][1]]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector sortJumbled(vector& mapping, vector& nums) { + vector> pairs; + + for (int i = 0; i < nums.size(); i++) { + int mapped_n = 0, base = 1; + int num = nums[i]; + + if (num == 0) { + mapped_n = mapping[0]; + } else { + while (num > 0) { + int digit = num % 10; + num /= 10; + mapped_n += base * mapping[digit]; + base *= 10; + } + } + + pairs.push_back({mapped_n, i}); + } + + sort(pairs.begin(), pairs.end()); + + vector res; + for (auto& p : pairs) { + res.push_back(nums[p.second]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} mapping + * @param {number[]} nums + * @return {number[]} + */ + sortJumbled(mapping, nums) { + let pairs = []; + + for (let i = 0; i < nums.length; i++) { + let numStr = nums[i].toString(); + let mapped_n = 0; + for (let c of numStr) { + mapped_n = mapped_n * 10 + mapping[parseInt(c)]; + } + pairs.push([mapped_n, i]); + } + + pairs.sort((a, b) => a[0] - b[0]); + + return pairs.map(p => nums[p[1]]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/sort-the-people.md b/articles/sort-the-people.md new file mode 100644 index 000000000..c5d8ca911 --- /dev/null +++ b/articles/sort-the-people.md @@ -0,0 +1,256 @@ +## 1. Hash Map + +::tabs-start + +```python +class Solution: + def sortPeople(self, names: List[str], heights: List[int]) -> List[str]: + height_to_name = {} + for h, n in zip(heights, names): + height_to_name[h] = n + + res = [] + for h in reversed(sorted(heights)): + res.append(height_to_name[h]) + + return res +``` + +```java +public class Solution { + public String[] sortPeople(String[] names, int[] heights) { + Map map = new HashMap<>(); + for (int i = 0; i < heights.length; i++) { + map.put(heights[i], names[i]); + } + + Arrays.sort(heights); + String[] res = new String[heights.length]; + for (int i = 0; i < heights.length; i++) { + res[i] = map.get(heights[heights.length - 1 - i]); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector sortPeople(vector& names, vector& heights) { + unordered_map map; + for (int i = 0; i < heights.size(); i++) { + map[heights[i]] = names[i]; + } + + sort(heights.begin(), heights.end()); + vector res; + for (int i = heights.size() - 1; i >= 0; i--) { + res.push_back(map[heights[i]]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} names + * @param {number[]} heights + * @return {string[]} + */ + sortPeople(names, heights) { + const map = {}; + for (let i = 0; i < heights.length; i++) { + map[heights[i]] = names[i]; + } + + heights.sort((a, b) => a - b); + const res = []; + for (let i = heights.length - 1; i >= 0; i--) { + res.push(map[heights[i]]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Sorting the Pairs + +::tabs-start + +```python +class Solution: + def sortPeople(self, names: List[str], heights: List[int]) -> List[str]: + arr = list(zip(heights, names)) + arr.sort(reverse=True) + return [name for _, name in arr] +``` + +```java +public class Solution { + public String[] sortPeople(String[] names, int[] heights) { + int n = names.length; + Pair[] arr = new Pair[n]; + + for (int i = 0; i < n; i++) { + arr[i] = new Pair(heights[i], names[i]); + } + + Arrays.sort(arr, (a, b) -> Integer.compare(b.height, a.height)); + + String[] res = new String[n]; + for (int i = 0; i < n; i++) { + res[i] = arr[i].name; + } + + return res; + } + + static class Pair { + int height; + String name; + + Pair(int height, String name) { + this.height = height; + this.name = name; + } + } +} +``` + +```cpp +class Solution { +public: + vector sortPeople(vector& names, vector& heights) { + vector> arr; + for (int i = 0; i < names.size(); i++) { + arr.emplace_back(heights[i], names[i]); + } + + sort(arr.begin(), arr.end(), [](auto& a, auto& b) { + return a.first > b.first; + }); + + vector res; + for (auto& [_, name] : arr) { + res.push_back(name); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} names + * @param {number[]} heights + * @return {string[]} + */ + sortPeople(names, heights) { + const arr = names.map((name, i) => [heights[i], name]); + arr.sort((a, b) => b[0] - a[0]); + return arr.map(pair => pair[1]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sorting the Indices + +::tabs-start + +```python +class Solution: + def sortPeople(self, names: List[str], heights: List[int]) -> List[str]: + indices = list(range(len(names))) + indices.sort(key=lambda i: -heights[i]) + return [names[i] for i in indices] +``` + +```java +public class Solution { + public String[] sortPeople(String[] names, int[] heights) { + Integer[] indices = new Integer[names.length]; + for (int i = 0; i < names.length; i++) { + indices[i] = i; + } + + Arrays.sort(indices, (i, j) -> Integer.compare(heights[j], heights[i])); + + String[] res = new String[names.length]; + for (int i = 0; i < names.length; i++) { + res[i] = names[indices[i]]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector sortPeople(vector& names, vector& heights) { + int n = names.size(); + vector indices(n); + iota(indices.begin(), indices.end(), 0); + + sort(indices.begin(), indices.end(), [&](int a, int b) { + return heights[a] > heights[b]; + }); + + vector res; + for (int i : indices) { + res.push_back(names[i]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} names + * @param {number[]} heights + * @return {string[]} + */ + sortPeople(names, heights) { + const indices = names.map((_, i) => i); + indices.sort((a, b) => heights[b] - heights[a]); + return indices.map(i => names[i]); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/special-array-i.md b/articles/special-array-i.md new file mode 100644 index 000000000..40ccd36c6 --- /dev/null +++ b/articles/special-array-i.md @@ -0,0 +1,129 @@ +## 1. Modulo Comparision + +::tabs-start + +```python +class Solution: + def isArraySpecial(self, nums: List[int]) -> bool: + for i in range(1, len(nums)): + if nums[i - 1] % 2 == nums[i] % 2: + return False + return True +``` + +```java +public class Solution { + public boolean isArraySpecial(int[] nums) { + for (int i = 1; i < nums.length; i++) { + if (nums[i - 1] % 2 == nums[i] % 2) { + return false; + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isArraySpecial(vector& nums) { + for (int i = 1; i < nums.size(); i++) { + if (nums[i - 1] % 2 == nums[i] % 2) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + isArraySpecial(nums) { + for (let i = 1; i < nums.length; i++) { + if (nums[i - 1] % 2 === nums[i] % 2) { + return false; + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 2. Bitwise Comparision + +::tabs-start + +```python +class Solution: + def isArraySpecial(self, nums: List[int]) -> bool: + for i in range(1, len(nums)): + if nums[i - 1] & 1 == nums[i] & 1: + return False + return True +``` + +```java +public class Solution { + public boolean isArraySpecial(int[] nums) { + for (int i = 1; i < nums.length; i++) { + if ((nums[i - 1] & 1) == (nums[i] & 1)) { + return false; + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isArraySpecial(vector& nums) { + for (int i = 1; i < nums.size(); i++) { + if ((nums[i - 1] & 1) == (nums[i] & 1)) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {boolean} + */ + isArraySpecial(nums) { + for (let i = 1; i < nums.length; i++) { + if ((nums[i - 1] & 1) === (nums[i] & 1)) { + return false; + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/special-array-with-x-elements-greater-than-or-equal-x.md b/articles/special-array-with-x-elements-greater-than-or-equal-x.md new file mode 100644 index 000000000..3ba445d99 --- /dev/null +++ b/articles/special-array-with-x-elements-greater-than-or-equal-x.md @@ -0,0 +1,506 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def specialArray(self, nums: List[int]) -> int: + for i in range(1, len(nums) + 1): + cnt = 0 + for num in nums: + if num >= i: + cnt += 1 + + if cnt == i: + return i + + return -1 +``` + +```java +public class Solution { + public int specialArray(int[] nums) { + for (int i = 1; i <= nums.length; i++) { + int count = 0; + for (int num : nums) { + if (num >= i) { + count++; + } + } + if (count == i) { + return i; + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int specialArray(vector& nums) { + for (int i = 1; i <= nums.size(); i++) { + int count = 0; + for (int num : nums) { + if (num >= i) { + count++; + } + } + if (count == i) { + return i; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + specialArray(nums) { + for (let i = 1; i <= nums.length; i++) { + let count = 0; + for (let num of nums) { + if (num >= i) { + count++; + } + } + if (count === i) { + return i; + } + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Binary Search + +::tabs-start + +```python +class Solution: + def specialArray(self, nums: List[int]) -> int: + l, r = 1, len(nums) + while l <= r: + mid = (l + r) >> 1 + cnt = sum(1 for num in nums if num >= mid) + + if cnt == mid: + return mid + + if cnt < mid: + r = mid - 1 + else: + l = mid + 1 + + return -1 +``` + +```java +public class Solution { + public int specialArray(int[] nums) { + int l = 1, r = nums.length; + while (l <= r) { + int mid = (l + r) / 2; + int cnt = 0; + for (int num : nums) { + if (num >= mid) cnt++; + } + + if (cnt == mid) return mid; + + if (cnt < mid) { + r = mid - 1; + } else { + l = mid + 1; + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int specialArray(vector& nums) { + int l = 1, r = nums.size(); + while (l <= r) { + int mid = (l + r) / 2; + int cnt = 0; + for (int num : nums) { + if (num >= mid) cnt++; + } + + if (cnt == mid) return mid; + + if (cnt < mid) { + r = mid - 1; + } else { + l = mid + 1; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + specialArray(nums) { + let l = 1, r = nums.length; + while (l <= r) { + const mid = Math.floor((l + r) / 2); + const cnt = nums.filter(num => num >= mid).length; + + if (cnt === mid) return mid; + + if (cnt < mid) { + r = mid - 1; + } else { + l = mid + 1; + } + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ + +--- + +## 3. Sorting + +::tabs-start + +```python +class Solution: + def specialArray(self, nums: List[int]) -> int: + nums.sort() + i = 0 + prev = -1 + total_right = len(nums) + while i < len(nums): + if nums[i] == total_right or (prev < total_right < nums[i]): + return total_right + + while i + 1 < len(nums) and nums[i] == nums[i + 1]: + i += 1 + + prev = nums[i] + i += 1 + total_right = len(nums) - i + + return -1 +``` + +```java +public class Solution { + public int specialArray(int[] nums) { + Arrays.sort(nums); + int i = 0, prev = -1, totalRight = nums.length; + + while (i < nums.length) { + if (nums[i] == totalRight || + (prev < totalRight && totalRight < nums[i])) { + return totalRight; + } + + while (i + 1 < nums.length && nums[i] == nums[i + 1]) { + i++; + } + + prev = nums[i]; + i++; + totalRight = nums.length - i; + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int specialArray(vector& nums) { + sort(nums.begin(), nums.end()); + int i = 0, prev = -1, totalRight = nums.size(); + + while (i < nums.size()) { + if (nums[i] == totalRight || + (prev < totalRight && totalRight < nums[i])) { + return totalRight; + } + + while (i + 1 < nums.size() && nums[i] == nums[i + 1]) { + i++; + } + + prev = nums[i]; + i++; + totalRight = nums.size() - i; + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + specialArray(nums) { + nums.sort((a, b) => a - b); + let i = 0, prev = -1, totalRight = nums.length; + + while (i < nums.length) { + if (nums[i] === totalRight || + (prev < totalRight && totalRight < nums[i])) { + return totalRight; + } + + while (i + 1 < nums.length && nums[i] === nums[i + 1]) { + i++; + } + + prev = nums[i]; + i++; + totalRight = nums.length - i; + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 4. Sorting + Two Pointers + +::tabs-start + +```python +class Solution: + def specialArray(self, nums: List[int]) -> int: + nums.sort() + n = len(nums) + i, j = 0, 1 + + while i < n and j <= n: + while i < n and j > nums[i]: + i += 1 + + if j == n - i: + return j + j += 1 + + return -1 +``` + +```java +public class Solution { + public int specialArray(int[] nums) { + Arrays.sort(nums); + int n = nums.length; + int i = 0, j = 1; + + while (i < n && j <= n) { + while (i < n && j > nums[i]) i++; + + if (j == n - i) { + return j; + } + j++; + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + int specialArray(vector& nums) { + sort(nums.begin(), nums.end()); + int n = nums.size(); + int i = 0, j = 1; + + while (i < n && j <= n) { + while (i < n && j > nums[i]) i++; + + if (j == n - i) { + return j; + } + j++; + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + specialArray(nums) { + nums.sort((a, b) => a - b); + const n = nums.length; + let i = 0, j = 1; + + while (i < n && j <= n) { + while (i < n && j > nums[i]) i++; + + if (j == n - i) { + return j; + } + j++; + } + + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 5. Counting Sort + +::tabs-start + +```python +class Solution: + def specialArray(self, nums: List[int]) -> int: + count = [0] * (len(nums) + 1) + for num in nums: + index = min(num, len(nums)) + count[index] += 1 + + total_right = 0 + for i in range(len(nums), -1, -1): + total_right += count[i] + if i == total_right: + return total_right + return -1 +``` + +```java +public class Solution { + public int specialArray(int[] nums) { + int[] count = new int[nums.length + 1]; + for (int num : nums) { + int index = Math.min(num, nums.length); + count[index]++; + } + + int totalRight = 0; + for (int i = nums.length; i >= 0; i--) { + totalRight += count[i]; + if (i == totalRight) { + return totalRight; + } + } + return -1; + } +} +``` + +```cpp +class Solution { +public: + int specialArray(vector& nums) { + vector count(nums.size() + 1, 0); + for (int num : nums) { + int index = min(num, (int)nums.size()); + count[index]++; + } + + int totalRight = 0; + for (int i = nums.size(); i >= 0; --i) { + totalRight += count[i]; + if (i == totalRight) { + return totalRight; + } + } + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + specialArray(nums) { + const count = new Array(nums.length + 1).fill(0); + for (const num of nums) { + const index = Math.min(num, nums.length); + count[index]++; + } + + let totalRight = 0; + for (let i = nums.length; i >= 0; i--) { + totalRight += count[i]; + if (i === totalRight) { + return totalRight; + } + } + return -1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/spiral-matrix-ii.md b/articles/spiral-matrix-ii.md new file mode 100644 index 000000000..1df46f786 --- /dev/null +++ b/articles/spiral-matrix-ii.md @@ -0,0 +1,454 @@ +## 1. Iteration + +::tabs-start + +```python +class Solution: + def generateMatrix(self, n: int) -> List[List[int]]: + mat = [[0] * n for _ in range(n)] + left, right = 0, n - 1 + top, bottom = 0, n - 1 + val = 1 + + while left <= right: + # Fill every val in top row + for c in range(left, right + 1): + mat[top][c] = val + val += 1 + top += 1 + + # Fill every val in right col + for r in range(top, bottom + 1): + mat[r][right] = val + val += 1 + right -= 1 + + # Fill every val in bottom row (reverse order) + for c in range(right, left - 1, -1): + mat[bottom][c] = val + val += 1 + bottom -= 1 + + # Fill every val in the left col (reverse order) + for r in range(bottom, top - 1, -1): + mat[r][left] = val + val += 1 + left += 1 + + return mat +``` + +```java +public class Solution { + public int[][] generateMatrix(int n) { + int[][] mat = new int[n][n]; + int left = 0, right = n - 1, top = 0, bottom = n - 1, val = 1; + + while (left <= right) { + // Fill every val in top row + for (int c = left; c <= right; c++) { + mat[top][c] = val++; + } + top++; + + // Fill every val in right col + for (int r = top; r <= bottom; r++) { + mat[r][right] = val++; + } + right--; + + // Fill every val in bottom row (reverse order) + for (int c = right; c >= left; c--) { + mat[bottom][c] = val++; + } + bottom--; + + // Fill every val in the left col (reverse order) + for (int r = bottom; r >= top; r--) { + mat[r][left] = val++; + } + left++; + } + + return mat; + } +} +``` + +```cpp +class Solution { +public: + vector> generateMatrix(int n) { + vector> mat(n, vector(n, 0)); + int left = 0, right = n - 1, top = 0, bottom = n - 1, val = 1; + + while (left <= right) { + // Fill every val in top row + for (int c = left; c <= right; c++) { + mat[top][c] = val++; + } + top++; + + // Fill every val in right col + for (int r = top; r <= bottom; r++) { + mat[r][right] = val++; + } + right--; + + // Fill every val in bottom row (reverse order) + for (int c = right; c >= left; c--) { + mat[bottom][c] = val++; + } + bottom--; + + // Fill every val in the left col (reverse order) + for (int r = bottom; r >= top; r--) { + mat[r][left] = val++; + } + left++; + } + + return mat; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number[][]} + */ + generateMatrix(n) { + const mat = Array.from({ length: n }, () => Array(n).fill(0)); + let left = 0, right = n - 1, top = 0, bottom = n - 1, val = 1; + + while (left <= right) { + // Fill every val in top row + for (let c = left; c <= right; c++) { + mat[top][c] = val++; + } + top++; + + // Fill every val in right col + for (let r = top; r <= bottom; r++) { + mat[r][right] = val++; + } + right--; + + // Fill every val in bottom row (reverse order) + for (let c = right; c >= left; c--) { + mat[bottom][c] = val++; + } + bottom--; + + // Fill every val in the left col (reverse order) + for (let r = bottom; r >= top; r--) { + mat[r][left] = val++; + } + left++; + } + + return mat; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ for the output matrix. + +--- + +## 2. Recursion + +::tabs-start + +```python +class Solution: + def generateMatrix(self, n: int) -> List[List[int]]: + mat = [[0] * n for _ in range(n)] + + def fill(left, right, top, bottom, val): + if left > right or top > bottom: + return + + # Fill every val in top row + for c in range(left, right + 1): + mat[top][c] = val + val += 1 + top += 1 + + # Fill every val in right col + for r in range(top, bottom + 1): + mat[r][right] = val + val += 1 + right -= 1 + + # Fill every val in bottom row (reverse order) + for c in range(right, left - 1, -1): + mat[bottom][c] = val + val += 1 + bottom -= 1 + + # Fill every val in the left col (reverse order) + for r in range(bottom, top - 1, -1): + mat[r][left] = val + val += 1 + left += 1 + + # Recur for the inner layer + fill(left, right, top, bottom, val) + + fill(0, n - 1, 0, n - 1, 1) + return mat +``` + +```java +public class Solution { + public int[][] generateMatrix(int n) { + int[][] mat = new int[n][n]; + fill(mat, 0, n - 1, 0, n - 1, 1); + return mat; + } + + private void fill(int[][] mat, int left, int right, int top, int bottom, int val) { + if (left > right || top > bottom) return; + + // Fill every val in top row + for (int c = left; c <= right; c++) { + mat[top][c] = val++; + } + top++; + + // Fill every val in right col + for (int r = top; r <= bottom; r++) { + mat[r][right] = val++; + } + right--; + + // Fill every val in bottom row (reverse order) + for (int c = right; c >= left; c--) { + mat[bottom][c] = val++; + } + bottom--; + + // Fill every val in the left col (reverse order) + for (int r = bottom; r >= top; r--) { + mat[r][left] = val++; + } + left++; + + // Recur for the inner layer + fill(mat, left, right, top, bottom, val); + } +} +``` + +```cpp +class Solution { +public: + vector> generateMatrix(int n) { + vector> mat(n, vector(n, 0)); + fill(mat, 0, n - 1, 0, n - 1, 1); + return mat; + } + +private: + void fill(vector> &mat, int left, int right, int top, int bottom, int val) { + if (left > right || top > bottom) return; + + // Fill every val in top row + for (int c = left; c <= right; c++) { + mat[top][c] = val++; + } + top++; + + // Fill every val in right col + for (int r = top; r <= bottom; r++) { + mat[r][right] = val++; + } + right--; + + // Fill every val in bottom row (reverse order) + for (int c = right; c >= left; c--) { + mat[bottom][c] = val++; + } + bottom--; + + // Fill every val in the left col (reverse order) + for (int r = bottom; r >= top; r--) { + mat[r][left] = val++; + } + left++; + + // Recur for the inner layer + fill(mat, left, right, top, bottom, val); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number[][]} + */ + generateMatrix(n) { + const mat = Array.from({ length: n }, () => Array(n).fill(0)); + + const fill = (left, right, top, bottom, val) => { + if (left > right || top > bottom) return; + + // Fill every val in top row + for (let c = left; c <= right; c++) { + mat[top][c] = val++; + } + top++; + + // Fill every val in right col + for (let r = top; r <= bottom; r++) { + mat[r][right] = val++; + } + right--; + + // Fill every val in bottom row (reverse order) + for (let c = right; c >= left; c--) { + mat[bottom][c] = val++; + } + bottom--; + + // Fill every val in the left col (reverse order) + for (let r = bottom; r >= top; r--) { + mat[r][left] = val++; + } + left++; + + // Recur for the inner layer + fill(left, right, top, bottom, val); + }; + + fill(0, n - 1, 0, n - 1, 1); + return mat; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: + * $O(n)$ space for recursion stack. + * $O(n ^ 2)$ space for the output matrix. + +--- + +## 3. Iteration (Optimal) + +::tabs-start + +```python +class Solution: + def generateMatrix(self, n: int) -> List[List[int]]: + mat = [[0] * n for _ in range(n)] + r = c = 0 + dr, dc = 0, 1 + + for val in range(n * n): + mat[r][c] = val + 1 + if mat[(r + dr) % n][(c + dc) % n] != 0: + dr, dc = dc, -dr + r, c = r + dr, c + dc + + return mat +``` + +```java +public class Solution { + public int[][] generateMatrix(int n) { + int[][] mat = new int[n][n]; + int r = 0, c = 0, dr = 0, dc = 1; + + for (int val = 0; val < n * n; val++) { + mat[r][c] = val + 1; + int nextR = (r + dr) % n, nextC = (c + dc) % n; + if (nextR < 0) nextR += n; + if (nextC < 0) nextC += n; + if (mat[nextR][nextC] != 0) { + int temp = dr; + dr = dc; + dc = -temp; + } + r += dr; + c += dc; + } + + return mat; + } +} +``` + +```cpp +class Solution { +public: + vector> generateMatrix(int n) { + vector> mat(n, vector(n, 0)); + int r = 0, c = 0, dr = 0, dc = 1; + + for (int val = 0; val < n * n; val++) { + mat[r][c] = val + 1; + int nextR = (r + dr) % n, nextC = (c + dc) % n; + if (nextR < 0) nextR += n; + if (nextC < 0) nextC += n; + if (mat[nextR][nextC] != 0) { + int temp = dr; + dr = dc; + dc = -temp; + } + r += dr; + c += dc; + } + + return mat; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number[][]} + */ + generateMatrix(n) { + const mat = Array.from({ length: n }, () => Array(n).fill(0)); + let r = 0, c = 0, dr = 0, dc = 1; + + for (let val = 0; val < n * n; val++) { + mat[r][c] = val + 1; + let nextR = (r + dr) % n, nextC = (c + dc) % n; + if (nextR < 0) nextR += n; + if (nextC < 0) nextC += n; + if (mat[nextR][nextC] !== 0) { + [dr, dc] = [dc, -dr]; + } + r += dr; + c += dc; + } + + return mat; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ for the output matrix. \ No newline at end of file diff --git a/articles/spiral-matrix.md b/articles/spiral-matrix.md new file mode 100644 index 000000000..2e82fff76 --- /dev/null +++ b/articles/spiral-matrix.md @@ -0,0 +1,756 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def spiralOrder(self, matrix: List[List[int]]) -> List[int]: + m, n = len(matrix), len(matrix[0]) + res = [] + + # append all the elements in the given direction + def dfs(row, col, r, c, dr, dc): + if row == 0 or col == 0: + return + + for i in range(col): + r += dr + c += dc + res.append(matrix[r][c]) + + # sub-problem + dfs(col, row - 1, r, c, dc, -dr) + + # start by going to the right + dfs(m, n, 0, -1, 0, 1) + return res +``` + +```java +public class Solution { + public List spiralOrder(int[][] matrix) { + int m = matrix.length, n = matrix[0].length; + List res = new ArrayList<>(); + + // append all the elements in the given direction + dfs(m, n, 0, -1, 0, 1, matrix, res); + return res; + } + + private void dfs(int row, int col, int r, int c, + int dr, int dc, int[][] matrix, List res) { + if (row == 0 || col == 0) return; + + for (int i = 0; i < col; i++) { + r += dr; + c += dc; + res.add(matrix[r][c]); + } + + // sub-problem + dfs(col, row - 1, r, c, dc, -dr, matrix, res); + } +} +``` + +```cpp +class Solution { +public: + vector spiralOrder(vector>& matrix) { + int m = matrix.size(), n = matrix[0].size(); + vector res; + + // append all the elements in the given direction + dfs(m, n, 0, -1, 0, 1, matrix, res); + return res; + } + + void dfs(int row, int col, int r, int c, int dr, int dc, + vector>& matrix, vector& res) { + if (row == 0 || col == 0) return; + + for (int i = 0; i < col; i++) { + r += dr; + c += dc; + res.push_back(matrix[r][c]); + } + + // sub-problem + dfs(col, row - 1, r, c, dc, -dr, matrix, res); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number[]} + */ + spiralOrder(matrix) { + const m = matrix.length, n = matrix[0].length; + const res = []; + + // append all the elements in the given direction + const dfs = (row, col, r, c, dr, dc) => { + if (row === 0 || col === 0) return; + + for (let i = 0; i < col; i++) { + r += dr; + c += dc; + res.push(matrix[r][c]); + } + + // sub-problem + dfs(col, row - 1, r, c, dc, -dr); + }; + + // start by going to the right + dfs(m, n, 0, -1, 0, 1); + return res; + } +} +``` + +```csharp +public class Solution { + public List SpiralOrder(int[][] matrix) { + int m = matrix.Length, n = matrix[0].Length; + List res = new List(); + + // append all the elements in the given direction + Dfs(m, n, 0, -1, 0, 1, matrix, res); + return res; + } + + private void Dfs(int row, int col, int r, int c, int dr, + int dc, int[][] matrix, List res) { + if (row == 0 || col == 0) return; + + for (int i = 0; i < col; i++) { + r += dr; + c += dc; + res.Add(matrix[r][c]); + } + + // sub-problem + Dfs(col, row - 1, r, c, dc, -dr, matrix, res); + } +} +``` + +```go +func spiralOrder(matrix [][]int) []int { + m, n := len(matrix), len(matrix[0]) + res := []int{} + + // Helper function for DFS traversal + var dfs func(row, col, r, c, dr, dc int) + dfs = func(row, col, r, c, dr, dc int) { + if row == 0 || col == 0 { + return + } + + for i := 0; i < col; i++ { + r += dr + c += dc + res = append(res, matrix[r][c]) + } + + // Recursive call with updated direction and dimensions + dfs(col, row-1, r, c, dc, -dr) + } + + // Start the DFS by going to the right + dfs(m, n, 0, -1, 0, 1) + return res +} +``` + +```kotlin +class Solution { + fun spiralOrder(matrix: Array): List { + val m = matrix.size + val n = matrix[0].size + val res = mutableListOf() + + // Helper function for DFS traversal + fun dfs(row: Int, col: Int, r: Int, c: Int, dr: Int, dc: Int) { + if (row == 0 || col == 0) return + + var newRow = r + var newCol = c + + for (i in 0 until col) { + newRow += dr + newCol += dc + res.add(matrix[newRow][newCol]) + } + + // Recursive call with updated direction and dimensions + dfs(col, row - 1, newRow, newCol, dc, -dr) + } + + // Start the DFS by going to the right + dfs(m, n, 0, -1, 0, 1) + return res + } +} +``` + +```swift +class Solution { + func spiralOrder(_ matrix: [[Int]]) -> [Int] { + var m = matrix.count + var n = matrix[0].count + var res: [Int] = [] + + // append all the elements in the given direction + func dfs(_ row: Int, _ col: Int, _ r: inout Int, _ c: inout Int, _ dr: Int, _ dc: Int) { + if row == 0 || col == 0 { + return + } + + for _ in 0.. Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 2. Iteration + +::tabs-start + +```python +class Solution: + def spiralOrder(self, matrix: List[List[int]]) -> List[int]: + res = [] + left, right = 0, len(matrix[0]) + top, bottom = 0, len(matrix) + + while left < right and top < bottom: + for i in range(left, right): + res.append(matrix[top][i]) + top += 1 + for i in range(top, bottom): + res.append(matrix[i][right - 1]) + right -= 1 + if not (left < right and top < bottom): + break + for i in range(right - 1, left - 1, -1): + res.append(matrix[bottom - 1][i]) + bottom -= 1 + for i in range(bottom - 1, top - 1, -1): + res.append(matrix[i][left]) + left += 1 + + return res +``` + +```java +public class Solution { + public List spiralOrder(int[][] matrix) { + List res = new ArrayList<>(); + int left = 0, right = matrix[0].length; + int top = 0, bottom = matrix.length; + + while (left < right && top < bottom) { + for (int i = left; i < right; i++) { + res.add(matrix[top][i]); + } + top++; + for (int i = top; i < bottom; i++) { + res.add(matrix[i][right - 1]); + } + right--; + if (!(left < right && top < bottom)) { + break; + } + for (int i = right - 1; i >= left; i--) { + res.add(matrix[bottom - 1][i]); + } + bottom--; + for (int i = bottom - 1; i >= top; i--) { + res.add(matrix[i][left]); + } + left++; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector spiralOrder(vector>& matrix) { + vector res; + int left = 0, right = matrix[0].size(); + int top = 0, bottom = matrix.size(); + + while (left < right && top < bottom) { + for (int i = left; i < right; i++) { + res.push_back(matrix[top][i]); + } + top++; + for (int i = top; i < bottom; i++) { + res.push_back(matrix[i][right - 1]); + } + right--; + if (!(left < right && top < bottom)) { + break; + } + for (int i = right - 1; i >= left; i--) { + res.push_back(matrix[bottom - 1][i]); + } + bottom--; + for (int i = bottom - 1; i >= top; i--) { + res.push_back(matrix[i][left]); + } + left++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number[]} + */ + spiralOrder(matrix) { + const res = []; + let left = 0; + let right = matrix[0].length; + let top = 0; + let bottom = matrix.length; + + while (left < right && top < bottom) { + for (let i = left; i < right; i++) { + res.push(matrix[top][i]); + } + top++; + for (let i = top; i < bottom; i++) { + res.push(matrix[i][right - 1]); + } + right--; + if (!(left < right && top < bottom)) { + break; + } + for (let i = right - 1; i >= left; i--) { + res.push(matrix[bottom - 1][i]); + } + bottom--; + for (let i = bottom - 1; i >= top; i--) { + res.push(matrix[i][left]); + } + left++; + } + + return res; + } +} +``` + +```csharp +public class Solution { + public List SpiralOrder(int[][] matrix) { + List res = new List(); + int left = 0, right = matrix[0].Length; + int top = 0, bottom = matrix.Length; + + while (left < right && top < bottom) { + for (int i = left; i < right; i++) { + res.Add(matrix[top][i]); + } + top++; + for (int i = top; i < bottom; i++) { + res.Add(matrix[i][right - 1]); + } + right--; + if (!(left < right && top < bottom)) { + break; + } + for (int i = right - 1; i >= left; i--) { + res.Add(matrix[bottom - 1][i]); + } + bottom--; + for (int i = bottom - 1; i >= top; i--) { + res.Add(matrix[i][left]); + } + left++; + } + + return res; + } +} +``` + +```go +func spiralOrder(matrix [][]int) []int { + res := []int{} + if len(matrix) == 0 || len(matrix[0]) == 0 { + return res + } + + left, right := 0, len(matrix[0]) + top, bottom := 0, len(matrix) + + for left < right && top < bottom { + for i := left; i < right; i++ { + res = append(res, matrix[top][i]) + } + top++ + + for i := top; i < bottom; i++ { + res = append(res, matrix[i][right-1]) + } + right-- + + if !(left < right && top < bottom) { + break + } + + for i := right - 1; i >= left; i-- { + res = append(res, matrix[bottom-1][i]) + } + bottom-- + + for i := bottom - 1; i >= top; i-- { + res = append(res, matrix[i][left]) + } + left++ + } + + return res +} +``` + +```kotlin +class Solution { + fun spiralOrder(matrix: Array): List { + val res = mutableListOf() + if (matrix.isEmpty() || matrix[0].isEmpty()) return res + + var left = 0 + var right = matrix[0].size + var top = 0 + var bottom = matrix.size + + while (left < right && top < bottom) { + for (i in left until right) { + res.add(matrix[top][i]) + } + top++ + + for (i in top until bottom) { + res.add(matrix[i][right - 1]) + } + right-- + + if (!(left < right && top < bottom)) break + + for (i in right - 1 downTo left) { + res.add(matrix[bottom - 1][i]) + } + bottom-- + + for (i in bottom - 1 downTo top) { + res.add(matrix[i][left]) + } + left++ + } + + return res + } +} +``` + +```swift +class Solution { + func spiralOrder(_ matrix: [[Int]]) -> [Int] { + var res: [Int] = [] + var left = 0, right = matrix[0].count + var top = 0, bottom = matrix.count + + while left < right && top < bottom { + for i in left.. Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 3. Iteration (Optimal) + +::tabs-start + +```python +class Solution: + def spiralOrder(self, matrix: List[List[int]]) -> List[int]: + res = [] + directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] + steps = [len(matrix[0]), len(matrix) - 1] + + r, c, d = 0, -1, 0 + while steps[d & 1]: + for i in range(steps[d & 1]): + r += directions[d][0] + c += directions[d][1] + res.append(matrix[r][c]) + steps[d & 1] -= 1 + d += 1 + d %= 4 + return res +``` + +```java +public class Solution { + public List spiralOrder(int[][] matrix) { + List res = new ArrayList<>(); + int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; + int[] steps = {matrix[0].length, matrix.length - 1}; + + int r = 0, c = -1, d = 0; + while (steps[d % 2] > 0) { + for (int i = 0; i < steps[d % 2]; i++) { + r += directions[d][0]; + c += directions[d][1]; + res.add(matrix[r][c]); + } + steps[d % 2]--; + d = (d + 1) % 4; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector spiralOrder(vector>& matrix) { + vector res; + vector> directions = {{0, 1}, {1, 0}, + {0, -1}, {-1, 0}}; + vector steps = {matrix[0].size(), matrix.size() - 1}; + + int r = 0, c = -1, d = 0; + while (steps[d % 2]) { + for (int i = 0; i < steps[d % 2]; i++) { + r += directions[d].first; + c += directions[d].second; + res.push_back(matrix[r][c]); + } + steps[d % 2]--; + d = (d + 1) % 4; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number[]} + */ + spiralOrder(matrix) { + const res = []; + const directions = [[0, 1], [1, 0], [0, -1], [-1, 0]]; + const steps = [matrix[0].length, matrix.length - 1]; + + let r = 0, c = -1, d = 0; + while (steps[d % 2]) { + for (let i = 0; i < steps[d % 2]; i++) { + r += directions[d][0]; + c += directions[d][1]; + res.push(matrix[r][c]); + } + steps[d % 2]--; + d = (d + 1) % 4; + } + return res; + } +} +``` + +```csharp +public class Solution { + public List SpiralOrder(int[][] matrix) { + var res = new List(); + var directions = new (int, int)[] { (0, 1), (1, 0), + (0, -1), (-1, 0) }; + var steps = new int[] { matrix[0].Length, matrix.Length - 1 }; + + int r = 0, c = -1, d = 0; + while (steps[d % 2] > 0) { + for (int i = 0; i < steps[d % 2]; i++) { + r += directions[d].Item1; + c += directions[d].Item2; + res.Add(matrix[r][c]); + } + steps[d % 2]--; + d = (d + 1) % 4; + } + return res; + } +} +``` + +```go +func spiralOrder(matrix [][]int) []int { + res := []int{} + if len(matrix) == 0 || len(matrix[0]) == 0 { + return res + } + + directions := [4][2]int{{0, 1}, {1, 0}, {0, -1}, {-1, 0}} + steps := []int{len(matrix[0]), len(matrix) - 1} + + r, c, d := 0, -1, 0 + + for steps[d&1] > 0 { + for i := 0; i < steps[d&1]; i++ { + r += directions[d][0] + c += directions[d][1] + res = append(res, matrix[r][c]) + } + steps[d&1]-- + d = (d + 1) % 4 + } + + return res +} +``` + +```kotlin +class Solution { + fun spiralOrder(matrix: Array): List { + val res = mutableListOf() + if (matrix.isEmpty() || matrix[0].isEmpty()) return res + + val directions = arrayOf( + intArrayOf(0, 1), + intArrayOf(1, 0), + intArrayOf(0, -1), + intArrayOf(-1, 0) + ) + val steps = mutableListOf(matrix[0].size, matrix.size - 1) + + var r = 0 + var c = -1 + var d = 0 + + while (steps[d and 1] > 0) { + for (i in 0 until steps[d and 1]) { + r += directions[d][0] + c += directions[d][1] + res.add(matrix[r][c]) + } + steps[d and 1]-- + d = (d + 1) % 4 + } + + return res + } +} +``` + +```swift +class Solution { + func spiralOrder(_ matrix: [[Int]]) -> [Int] { + var res: [Int] = [] + let directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] + var steps = [matrix[0].count, matrix.count - 1] + + var r = 0, c = -1, d = 0 + while steps[d & 1] > 0 { + for _ in 0.. Where $m$ is the number of rows and $n$ is the number of columns. \ No newline at end of file diff --git a/articles/split-array-largest-sum.md b/articles/split-array-largest-sum.md new file mode 100644 index 000000000..617691f9b --- /dev/null +++ b/articles/split-array-largest-sum.md @@ -0,0 +1,1088 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def splitArray(self, nums: List[int], k: int) -> int: + n = len(nums) + + def dfs(i, m): + if i == n: + return 0 if m == 0 else float("inf") + if m == 0: + return float("inf") + + res = float("inf") + curSum = 0 + for j in range(i, n - m + 1): + curSum += nums[j] + res = min(res, max(curSum, dfs(j + 1, m - 1))) + + return res + + return dfs(0, k) +``` + +```java +public class Solution { + public int splitArray(int[] nums, int k) { + int n = nums.length; + + return dfs(nums, 0, k, n); + } + + private int dfs(int[] nums, int i, int m, int n) { + if (i == n) { + return m == 0 ? 0 : Integer.MAX_VALUE; + } + if (m == 0) { + return Integer.MAX_VALUE; + } + + int res = Integer.MAX_VALUE; + int curSum = 0; + for (int j = i; j <= n - m; j++) { + curSum += nums[j]; + res = Math.min(res, Math.max(curSum, dfs(nums, j + 1, m - 1, n))); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int splitArray(vector& nums, int k) { + int n = nums.size(); + return dfs(nums, 0, k, n); + } + +private: + int dfs(vector& nums, int i, int m, int n) { + if (i == n) { + return m == 0 ? 0 : INT_MAX; + } + if (m == 0) { + return INT_MAX; + } + + int res = INT_MAX, curSum = 0; + for (int j = i; j <= n - m; j++) { + curSum += nums[j]; + res = min(res, max(curSum, dfs(nums, j + 1, m - 1, n))); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + splitArray(nums, k) { + const n = nums.length; + + const dfs = (i, m) => { + if (i === n) { + return m === 0 ? 0 : Infinity; + } + if (m === 0) { + return Infinity; + } + + let res = Infinity; + let curSum = 0; + for (let j = i; j <= n - m; j++) { + curSum += nums[j]; + res = Math.min(res, Math.max(curSum, dfs(j + 1, m - 1))); + } + + return res; + }; + + return dfs(0, k); + } +} +``` + +```csharp +public class Solution { + public int SplitArray(int[] nums, int k) { + int n = nums.Length; + return Dfs(nums, 0, k, n); + } + + private int Dfs(int[] nums, int i, int m, int n) { + if (i == n) { + return m == 0 ? 0 : int.MaxValue; + } + if (m == 0) { + return int.MaxValue; + } + + int res = int.MaxValue; + int curSum = 0; + for (int j = i; j <= n - m; j++) { + curSum += nums[j]; + int next = Dfs(nums, j + 1, m - 1, n); + if (next != int.MaxValue) { + res = Math.Min(res, Math.Max(curSum, next)); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def splitArray(self, nums: List[int], k: int) -> int: + n = len(nums) + dp = [[-1] * (k + 1) for _ in range(n)] + + def dfs(i, m): + if i == n: + return 0 if m == 0 else float("inf") + if m == 0: + return float("inf") + if dp[i][m] != -1: + return dp[i][m] + + res = float("inf") + curSum = 0 + for j in range(i, n - m + 1): + curSum += nums[j] + res = min(res, max(curSum, dfs(j + 1, m - 1))) + + dp[i][m] = res + return res + + return dfs(0, k) +``` + +```java +public class Solution { + private int[][] dp; + + public int splitArray(int[] nums, int k) { + int n = nums.length; + dp = new int[n][k + 1]; + for (int[] it : dp) { + Arrays.fill(it, -1); + } + return dfs(nums, 0, k, n); + } + + private int dfs(int[] nums, int i, int m, int n) { + if (i == n) { + return m == 0 ? 0 : Integer.MAX_VALUE; + } + if (m == 0) { + return Integer.MAX_VALUE; + } + if (dp[i][m] != -1) { + return dp[i][m]; + } + + int res = Integer.MAX_VALUE; + int curSum = 0; + for (int j = i; j <= n - m; j++) { + curSum += nums[j]; + res = Math.min(res, Math.max(curSum, dfs(nums, j + 1, m - 1, n))); + } + + return dp[i][m] = res; + } +} +``` + +```cpp +class Solution { + vector> dp; + +public: + int splitArray(vector& nums, int k) { + int n = nums.size(); + dp.assign(n, vector(k + 1, -1)); + return dfs(nums, 0, k, n); + } + +private: + int dfs(vector& nums, int i, int m, int n) { + if (i == n) { + return m == 0 ? 0 : INT_MAX; + } + if (m == 0) { + return INT_MAX; + } + if (dp[i][m] != -1) { + return dp[i][m]; + } + + int res = INT_MAX, curSum = 0; + for (int j = i; j <= n - m; j++) { + curSum += nums[j]; + res = min(res, max(curSum, dfs(nums, j + 1, m - 1, n))); + } + + return dp[i][m] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + splitArray(nums, k) { + const n = nums.length; + const dp = Array.from({ length: n }, () => Array(k + 1).fill(-1)); + + const dfs = (i, m) => { + if (i === n) { + return m === 0 ? 0 : Infinity; + } + if (m === 0) { + return Infinity; + } + if (dp[i][m] !== -1) { + return dp[i][m]; + } + + let res = Infinity; + let curSum = 0; + for (let j = i; j <= n - m; j++) { + curSum += nums[j]; + res = Math.min(res, Math.max(curSum, dfs(j + 1, m - 1))); + } + + return dp[i][m] = res; + }; + + return dfs(0, k); + } +} +``` + +```csharp +public class Solution { + private int[,] dp; + + public int SplitArray(int[] nums, int k) { + int n = nums.Length; + dp = new int[n, k + 1]; + + for (int i = 0; i < n; i++) { + for (int j = 0; j <= k; j++) { + dp[i, j] = -1; + } + } + + return Dfs(nums, 0, k, n); + } + + private int Dfs(int[] nums, int i, int m, int n) { + if (i == n) { + return m == 0 ? 0 : int.MaxValue; + } + if (m == 0) { + return int.MaxValue; + } + if (dp[i, m] != -1) { + return dp[i, m]; + } + + int res = int.MaxValue; + int curSum = 0; + for (int j = i; j <= n - m; j++) { + curSum += nums[j]; + int next = Dfs(nums, j + 1, m - 1, n); + if (next != int.MaxValue) { + res = Math.Min(res, Math.Max(curSum, next)); + } + } + + dp[i, m] = res; + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * n ^ 2)$ +* Space complexity: $O(k * n)$ + +> Where $n$ is the size of the array $nums$ and $k$ is the number of sub-arrays to form. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def splitArray(self, nums: List[int], k: int) -> int: + n = len(nums) + dp = [[float("inf")] * (k + 1) for _ in range(n + 1)] + dp[n][0] = 0 + + for m in range(1, k + 1): + for i in range(n - 1, -1, -1): + curSum = 0 + for j in range(i, n - m + 1): + curSum += nums[j] + dp[i][m] = min(dp[i][m], max(curSum, dp[j + 1][m - 1])) + + return dp[0][k] +``` + +```java +public class Solution { + public int splitArray(int[] nums, int k) { + int n = nums.length; + int[][] dp = new int[n + 1][k + 1]; + for (int[] it : dp) { + Arrays.fill(it, Integer.MAX_VALUE); + } + dp[n][0] = 0; + + for (int m = 1; m <= k; m++) { + for (int i = n - 1; i >= 0; i--) { + int curSum = 0; + for (int j = i; j < n - m + 1; j++) { + curSum += nums[j]; + dp[i][m] = Math.min(dp[i][m], Math.max(curSum, dp[j + 1][m - 1])); + } + } + } + + return dp[0][k]; + } +} +``` + +```cpp +class Solution { +public: + int splitArray(vector& nums, int k) { + int n = nums.size(); + vector> dp(n + 1, vector(k + 1, INT_MAX)); + dp[n][0] = 0; + + for (int m = 1; m <= k; m++) { + for (int i = n - 1; i >= 0; i--) { + int curSum = 0; + for (int j = i; j < n - m + 1; j++) { + curSum += nums[j]; + dp[i][m] = min(dp[i][m], max(curSum, dp[j + 1][m - 1])); + } + } + } + + return dp[0][k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + splitArray(nums, k) { + const n = nums.length; + const dp = Array.from({ length: n + 1 }, () => + Array(k + 1).fill(Infinity) + ); + dp[n][0] = 0; + + for (let m = 1; m <= k; m++) { + for (let i = n - 1; i >= 0; i--) { + let curSum = 0; + for (let j = i; j < n - m + 1; j++) { + curSum += nums[j]; + dp[i][m] = Math.min(dp[i][m], Math.max(curSum, dp[j + 1][m - 1])); + } + } + } + + return dp[0][k]; + } +} +``` + +```csharp +public class Solution { + public int SplitArray(int[] nums, int k) { + int n = nums.Length; + int[,] dp = new int[n + 1, k + 1]; + + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= k; j++) { + dp[i, j] = int.MaxValue; + } + } + + dp[n, 0] = 0; + + for (int m = 1; m <= k; m++) { + for (int i = n - 1; i >= 0; i--) { + int curSum = 0; + for (int j = i; j < n - m + 1; j++) { + curSum += nums[j]; + if (dp[j + 1, m - 1] != int.MaxValue) { + dp[i, m] = Math.Min(dp[i, m], Math.Max(curSum, dp[j + 1, m - 1])); + } + } + } + } + + return dp[0, k]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * n ^ 2)$ +* Space complexity: $O(k * n)$ + +> Where $n$ is the size of the array $nums$ and $k$ is the number of sub-arrays to form. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def splitArray(self, nums: List[int], k: int) -> int: + n = len(nums) + dp = [float("inf")] * (n + 1) + dp[n] = 0 + + for m in range(1, k + 1): + nextDp = [float("inf")] * (n + 1) + for i in range(n - 1, -1, -1): + curSum = 0 + for j in range(i, n - m + 1): + curSum += nums[j] + nextDp[i] = min(nextDp[i], max(curSum, dp[j + 1])) + dp = nextDp + + return dp[0] +``` + +```java +public class Solution { + public int splitArray(int[] nums, int k) { + int n = nums.length; + int[] dp = new int[n + 1]; + int[] nextDp = new int[n + 1]; + Arrays.fill(dp, Integer.MAX_VALUE); + dp[n] = 0; + + for (int m = 1; m <= k; m++) { + Arrays.fill(nextDp, Integer.MAX_VALUE); + for (int i = n - 1; i >= 0; i--) { + int curSum = 0; + for (int j = i; j < n - m + 1; j++) { + curSum += nums[j]; + nextDp[i] = Math.min(nextDp[i], Math.max(curSum, dp[j + 1])); + } + } + int[] temp = dp; + dp = nextDp; + nextDp = temp; + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int splitArray(vector& nums, int k) { + int n = nums.size(); + vector dp(n + 1, INT_MAX), nextDp(n + 1, INT_MAX); + dp[n] = 0; + + for (int m = 1; m <= k; m++) { + fill(nextDp.begin(), nextDp.end(), INT_MAX); + for (int i = n - 1; i >= 0; i--) { + int curSum = 0; + for (int j = i; j < n - m + 1; j++) { + curSum += nums[j]; + nextDp[i] = min(nextDp[i], max(curSum, dp[j + 1])); + } + } + dp.swap(nextDp); + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + splitArray(nums, k) { + const n = nums.length; + let dp = new Array(n + 1).fill(Infinity); + let nextDp = new Array(n + 1).fill(Infinity); + dp[n] = 0; + + for (let m = 1; m <= k; m++) { + nextDp.fill(Infinity); + for (let i = n - 1; i >= 0; i--) { + let curSum = 0; + for (let j = i; j < n - m + 1; j++) { + curSum += nums[j]; + nextDp[i] = Math.min(nextDp[i], Math.max(curSum, dp[j + 1])); + } + } + [dp, nextDp] = [nextDp, dp]; + } + + return dp[0]; + } +} +``` + +```csharp +public class Solution { + public int SplitArray(int[] nums, int k) { + int n = nums.Length; + int[] dp = new int[n + 1]; + int[] nextDp = new int[n + 1]; + Array.Fill(dp, int.MaxValue); + dp[n] = 0; + + for (int m = 1; m <= k; m++) { + Array.Fill(nextDp, int.MaxValue); + for (int i = n - 1; i >= 0; i--) { + int curSum = 0; + for (int j = i; j < n - m + 1; j++) { + curSum += nums[j]; + if (dp[j + 1] != int.MaxValue) { + nextDp[i] = Math.Min(nextDp[i], Math.Max(curSum, dp[j + 1])); + } + } + } + var temp = dp; + dp = nextDp; + nextDp = temp; + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * n ^ 2)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums$ and $k$ is the number of sub-arrays to form. + +--- + +## 5. Binary Search + +::tabs-start + +```python +class Solution: + def splitArray(self, nums: List[int], k: int) -> int: + def canSplit(largest): + subarray = 1 + curSum = 0 + for num in nums: + curSum += num + if curSum > largest: + subarray += 1 + if subarray > k: + return False + curSum = num + return True + + l, r = max(nums), sum(nums) + res = r + while l <= r: + mid = l + (r - l) // 2 + if canSplit(mid): + res = mid + r = mid - 1 + else: + l = mid + 1 + return res +``` + +```java +public class Solution { + public int splitArray(int[] nums, int k) { + int l = 0, r = 0, res = 0; + for (int num : nums) { + l = Math.max(l, num); + r += num; + } + res = r; + + while (l <= r) { + int mid = l + (r - l) / 2; + if (canSplit(nums, k, mid)) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return res; + } + + private boolean canSplit(int[] nums, int k, int largest) { + int subarray = 1, curSum = 0; + for (int num : nums) { + curSum += num; + if (curSum > largest) { + subarray++; + if (subarray > k) return false; + curSum = num; + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + int splitArray(vector& nums, int k) { + int l = *max_element(nums.begin(), nums.end()); + int r = accumulate(nums.begin(), nums.end(), 0); + int res = r; + + while (l <= r) { + int mid = l + (r - l) / 2; + if (canSplit(nums, k, mid)) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return res; + } + +private: + bool canSplit(vector& nums, int k, int largest) { + int subarray = 1, curSum = 0; + for (int num : nums) { + curSum += num; + if (curSum > largest) { + subarray++; + if (subarray > k) return 0; + curSum = num; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + splitArray(nums, k) { + const canSplit = (largest) => { + let subarray = 1, curSum = 0; + for (const num of nums) { + curSum += num; + if (curSum > largest) { + subarray++; + if (subarray > k) return false; + curSum = num; + } + } + return true; + }; + + let l = Math.max(...nums); + let r = nums.reduce((a, b) => a + b, 0); + let res = r; + + while (l <= r) { + const mid = Math.floor(l + (r - l) / 2); + if (canSplit(mid)) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int SplitArray(int[] nums, int k) { + int l = 0, r = 0, res = 0; + foreach (int num in nums) { + l = Math.Max(l, num); + r += num; + } + res = r; + + while (l <= r) { + int mid = l + (r - l) / 2; + if (CanSplit(nums, k, mid)) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return res; + } + + private bool CanSplit(int[] nums, int k, int largest) { + int subarray = 1, curSum = 0; + foreach (int num in nums) { + curSum += num; + if (curSum > largest) { + subarray++; + if (subarray > k) return false; + curSum = num; + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log s)$ +* Space complexity: $O(1)$ + +> Where $n$ is the size of the array $nums$ and $s$ is the sum of all the elements in the array. + +--- + +## 6. Binary Search + Prefix Sum + +::tabs-start + +```python +class Solution: + def splitArray(self, nums: List[int], k: int) -> int: + n = len(nums) + prefix = [0] * (n + 1) + for i in range(n): + prefix[i + 1] = prefix[i] + nums[i] + + def canSplit(largest): + subarrays = 0 + i = 0 + while i < n: + l, r = i + 1, n + while l <= r: + mid = l + (r - l) // 2 + if prefix[mid] - prefix[i] <= largest: + l = mid + 1 + else: + r = mid - 1 + subarrays += 1 + i = r + if subarrays > k: + return False + return True + + l, r = max(nums), sum(nums) + res = r + while l <= r: + mid = l + (r - l) // 2 + if canSplit(mid): + res = mid + r = mid - 1 + else: + l = mid + 1 + + return res +``` + +```java +public class Solution { + private int[] prefix; + private int n; + + public int splitArray(int[] nums, int k) { + n = nums.length; + prefix = new int[n + 1]; + for (int i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + nums[i]; + } + + int l = Integer.MIN_VALUE, r = 0; + for (int num : nums) { + l = Math.max(l, num); + r += num; + } + + int res = r; + while (l <= r) { + int mid = l + (r - l) / 2; + if (canSplit(mid, k)) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return res; + } + + private boolean canSplit(int largest, int k) { + int subarrays = 0, i = 0; + while (i < n) { + int l = i + 1, r = n; + while (l <= r) { + int mid = l + (r - l) / 2; + if (prefix[mid] - prefix[i] <= largest) { + l = mid + 1; + } else { + r = mid - 1; + } + } + subarrays++; + i = r; + if (subarrays > k) { + return false; + } + } + return true; + } +} +``` + +```cpp +class Solution { +private: + vector prefix; + int n; + +public: + int splitArray(vector& nums, int k) { + n = nums.size(); + prefix.resize(n + 1, 0); + for (int i = 0; i < n; ++i) { + prefix[i + 1] = prefix[i] + nums[i]; + } + + int l = *max_element(nums.begin(), nums.end()); + int r = accumulate(nums.begin(), nums.end(), 0); + int res = r; + + while (l <= r) { + int mid = l + (r - l) / 2; + if (canSplit(mid, k)) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + + return res; + } + +private: + bool canSplit(int largest, int k) { + int subarrays = 0, i = 0; + while (i < n) { + int l = i + 1, r = n; + while (l <= r) { + int mid = l + (r - l) / 2; + if (prefix[mid] - prefix[i] <= largest) { + l = mid + 1; + } else { + r = mid - 1; + } + } + subarrays++; + i = r; + if (subarrays > k) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + splitArray(nums, k) { + const n = nums.length; + const prefix = new Array(n + 1).fill(0); + for (let i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + nums[i]; + } + + const canSplit = (largest) => { + let subarrays = 0, i = 0; + while (i < n) { + let l = i + 1, r = n; + while (l <= r) { + const mid = Math.floor(l + (r - l) / 2); + if (prefix[mid] - prefix[i] <= largest) { + l = mid + 1; + } else { + r = mid - 1; + } + } + subarrays++; + i = r; + if (subarrays > k) { + return false; + } + } + return true; + }; + + let l = Math.max(...nums); + let r = nums.reduce((a, b) => a + b, 0), res = r; + while (l <= r) { + const mid = Math.floor(l + (r - l) / 2); + if (canSplit(mid)) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return res; + } +} +``` + +```csharp +public class Solution { + private int[] prefix; + private int n; + + public int SplitArray(int[] nums, int k) { + n = nums.Length; + prefix = new int[n + 1]; + for (int i = 0; i < n; i++) { + prefix[i + 1] = prefix[i] + nums[i]; + } + + int l = int.MinValue, r = 0; + foreach (int num in nums) { + l = Math.Max(l, num); + r += num; + } + + int res = r; + while (l <= r) { + int mid = l + (r - l) / 2; + if (CanSplit(mid, k)) { + res = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return res; + } + + private bool CanSplit(int largest, int k) { + int subarrays = 0, i = 0; + while (i < n) { + int l = i + 1, r = n; + while (l <= r) { + int mid = l + (r - l) / 2; + if (prefix[mid] - prefix[i] <= largest) { + l = mid + 1; + } else { + r = mid - 1; + } + } + subarrays++; + i = r; + if (subarrays > k) { + return false; + } + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + (k * \log n * \log s))$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the array $nums$, $s$ is the sum of all the elements in the array, and $k$ is the number of sub-arrays to form. \ No newline at end of file diff --git a/articles/split-linked-list-in-parts.md b/articles/split-linked-list-in-parts.md new file mode 100644 index 000000000..5df42573a --- /dev/null +++ b/articles/split-linked-list-in-parts.md @@ -0,0 +1,343 @@ +## 1. Convert To Array + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def splitListToParts(self, head: Optional[ListNode], k: int) -> List[Optional[ListNode]]: + arr, cur = [], head + while cur: + arr.append(cur) + cur = cur.next + + N = len(arr) + base_len, remainder = N // k, N % k + + res = [None] * k + start = 0 + for i in range(k): + if start < N: + res[i] = arr[start] + tail = start + base_len - 1 + if remainder: + tail += 1 + remainder -= 1 + arr[tail].next = None + start = tail + 1 + + return res +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode[] splitListToParts(ListNode head, int k) { + List arr = new ArrayList<>(); + for (ListNode cur = head; cur != null; cur = cur.next) { + arr.add(cur); + } + + int N = arr.size(); + int base_len = N / k, remainder = N % k; + + ListNode[] res = new ListNode[k]; + int start = 0; + for (int i = 0; i < k; i++) { + if (start < N) { + res[i] = arr.get(start); + int tail = start + base_len - 1; + if (remainder > 0) { + tail++; + remainder--; + } + arr.get(tail).next = null; + start = tail + 1; + } + } + + return res; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + vector splitListToParts(ListNode* head, int k) { + vector arr; + for (ListNode* cur = head; cur != nullptr; cur = cur->next) { + arr.push_back(cur); + } + + int N = arr.size(); + int base_len = N / k, remainder = N % k; + + vector res(k, nullptr); + int start = 0; + for (int i = 0; i < k; i++) { + if (start < N) { + res[i] = arr[start]; + int tail = start + base_len - 1; + if (remainder > 0) { + tail++; + remainder--; + } + arr[tail]->next = nullptr; + start = tail + 1; + } + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} k + * @return {ListNode[]} + */ + splitListToParts(head, k) { + let arr = []; + for (let cur = head; cur !== null; cur = cur.next) { + arr.push(cur); + } + + let N = arr.length; + let base_len = Math.floor(N / k), remainder = N % k; + + let res = new Array(k).fill(null); + let start = 0; + for (let i = 0; i < k; i++) { + if (start < N) { + res[i] = arr[start]; + let tail = start + base_len - 1; + if (remainder > 0) { + tail++; + remainder--; + } + arr[tail].next = null; + start = tail + 1; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Iteration + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def splitListToParts(self, head: Optional[ListNode], k: int) -> List[Optional[ListNode]]: + length, curr = 0, head + while curr: + curr = curr.next + length += 1 + + base_len, remainder = length // k, length % k + curr, res = head, [] + + for i in range(k): + res.append(curr) + for j in range(base_len - 1 + (1 if remainder else 0)): + if not curr: + break + curr = curr.next + remainder -= 1 if remainder else 0 + if curr: + curr.next, curr = None, curr.next + + return res +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode[] splitListToParts(ListNode head, int k) { + int length = 0; + ListNode curr = head; + while (curr != null) { + curr = curr.next; + length++; + } + + int baseLen = length / k, remainder = length % k; + ListNode[] res = new ListNode[k]; + curr = head; + + for (int i = 0; i < k; i++) { + res[i] = curr; + for (int j = 0; j < baseLen - 1 + (remainder > 0 ? 1 : 0); j++) { + if (curr == null) break; + curr = curr.next; + } + if (curr != null) { + ListNode temp = curr.next; + curr.next = null; + curr = temp; + } + remainder--; + } + return res; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + vector splitListToParts(ListNode* head, int k) { + int length = 0; + ListNode* curr = head; + while (curr) { + curr = curr->next; + length++; + } + + int baseLen = length / k, remainder = length % k; + vector res(k, nullptr); + curr = head; + + for (int i = 0; i < k; i++) { + res[i] = curr; + for (int j = 0; j < baseLen - 1 + (remainder > 0 ? 1 : 0); j++) { + if (!curr) break; + curr = curr->next; + } + if (curr) { + ListNode* temp = curr->next; + curr->next = nullptr; + curr = temp; + } + remainder--; + } + return res; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} k + * @return {ListNode[]} + */ + splitListToParts(head, k) { + let length = 0, curr = head; + while (curr) { + curr = curr.next; + length++; + } + + let baseLen = Math.floor(length / k), remainder = length % k; + let res = new Array(k).fill(null); + curr = head; + + for (let i = 0; i < k; i++) { + res[i] = curr; + for (let j = 0; j < baseLen - 1 + (remainder > 0 ? 1 : 0); j++) { + if (!curr) break; + curr = curr.next; + } + if (curr) { + let temp = curr.next; + curr.next = null; + curr = temp; + } + remainder--; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n)$ space for the output array. \ No newline at end of file diff --git a/articles/splitting-a-string-into-descending-consecutive-values.md b/articles/splitting-a-string-into-descending-consecutive-values.md new file mode 100644 index 000000000..b736eda11 --- /dev/null +++ b/articles/splitting-a-string-into-descending-consecutive-values.md @@ -0,0 +1,570 @@ +## 1. Backtracking + +::tabs-start + +```python +class Solution: + def splitString(self, s: str) -> bool: + n = len(s) + + def isValid(splits): + for i in range(1, len(splits)): + if splits[i] != splits[i - 1] - 1: + return False + return len(splits) > 1 + + def dfs(i, splits): + if i == n: + return isValid(splits) + num = 0 + for j in range(i, n): + num = num * 10 + int(s[j]) + splits.append(num) + if dfs(j + 1, splits): + return True + splits.pop() + return False + + return dfs(0, []) +``` + +```java +public class Solution { + public boolean splitString(String s) { + return dfs(s, 0, new ArrayList<>()); + } + + private boolean isValid(List splits) { + for (int i = 1; i < splits.size(); i++) { + if (splits.get(i) != splits.get(i - 1) - 1) { + return false; + } + } + return splits.size() > 1; + } + + private boolean dfs(String s, int i, List splits) { + if (i == s.length()) { + return isValid(splits); + } + long num = 0; + for (int j = i; j < s.length(); j++) { + num = num * 10 + (s.charAt(j) - '0'); + splits.add(num); + if (dfs(s, j + 1, splits)) { + return true; + } + splits.remove(splits.size() - 1); + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool splitString(string s) { + vector splits; + return dfs(s, 0, splits); + } + +private: + bool isValid(vector& splits) { + for (int i = 1; i < splits.size(); i++) { + if (splits[i] != splits[i - 1] - 1) { + return false; + } + } + return splits.size() > 1; + } + + bool dfs(string& s, int i, vector& splits) { + if (i == s.size()) { + return isValid(splits); + } + unsigned long long num = 0; + for (int j = i; j < s.size(); j++) { + num = num * 10 + (s[j] - '0'); + splits.push_back(num); + if (dfs(s, j + 1, splits)) { + return true; + } + splits.pop_back(); + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + splitString(s) { + const n = s.length; + + const isValid = (splits) => { + for (let i = 1; i < splits.length; i++) { + if (splits[i] !== splits[i - 1] - 1) { + return false; + } + } + return splits.length > 1; + }; + + const dfs = (i, splits) => { + if (i === n) { + return isValid(splits); + } + let num = 0; + for (let j = i; j < n; j++) { + num = num * 10 + Number(s[j]); + splits.push(num); + if (dfs(j + 1, splits)) { + return true; + } + splits.pop(); + } + return false; + }; + + return dfs(0, []); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Recursion - I + +::tabs-start + +```python +class Solution: + def splitString(self, s: str) -> bool: + def dfs(index, prev): + if index == len(s): + return True + num = 0 + for j in range(index, len(s)): + num = num * 10 + int(s[j]) + if num + 1 == prev and dfs(j + 1, num): + return True + return False + + val = 0 + for i in range(len(s) - 1): + val = val * 10 + int(s[i]) + if dfs(i + 1, val): + return True + + return False +``` + +```java +public class Solution { + public boolean splitString(String s) { + int n = s.length(); + long val = 0; + for (int i = 0; i < n - 1; i++) { + val = val * 10 + (s.charAt(i) - '0'); + if (dfs(s, i + 1, val)) { + return true; + } + } + return false; + } + + private boolean dfs(String s, int index, long prev) { + if (index == s.length()) { + return true; + } + long num = 0; + for (int j = index; j < s.length(); j++) { + num = num * 10 + (s.charAt(j) - '0'); + if (num + 1 == prev && dfs(s, j + 1, num)) { + return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool splitString(string s) { + int n = s.size(); + unsigned long long val = 0; + for (int i = 0; i < n - 1; i++) { + val = val * 10 + (s[i] - '0'); + if (dfs(s, i + 1, val)) { + return true; + } + } + return false; + } + +private: + bool dfs(string& s, int index, long long prev) { + if (index == s.size()) { + return true; + } + unsigned long long num = 0; + for (int j = index; j < s.size(); j++) { + num = num * 10 + (s[j] - '0'); + if (num + 1 == prev && dfs(s, j + 1, num)) { + return true; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + splitString(s) { + const n = s.length; + + const dfs = (index, prev) => { + if (index === n) { + return true; + } + let num = 0; + for (let j = index; j < n; j++) { + num = num * 10 + Number(s[j]); + if (num + 1 === prev && dfs(j + 1, num)) { + return true; + } + } + return false; + }; + + let val = 0; + for (let i = 0; i < n - 1; i++) { + val = val * 10 + Number(s[i]); + if (dfs(i + 1, val)) { + return true; + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Recursion - II + +::tabs-start + +```python +class Solution: + def splitString(self, s: str) -> bool: + def dfs(index, prev): + if index == len(s): + return True + num = 0 + for j in range(index, len(s)): + num = num * 10 + int(s[j]) + if num + 1 == prev and dfs(j + 1, num): + return True + if num >= prev: + break + return False + + val = 0 + for i in range(len(s) - 1): + val = val * 10 + int(s[i]) + if dfs(i + 1, val): + return True + + return False +``` + +```java +public class Solution { + public boolean splitString(String s) { + int n = s.length(); + long val = 0; + for (int i = 0; i < n - 1; i++) { + val = val * 10 + (s.charAt(i) - '0'); + if (dfs(s, i + 1, val)) { + return true; + } + } + return false; + } + + private boolean dfs(String s, int index, long prev) { + if (index == s.length()) { + return true; + } + long num = 0; + for (int j = index; j < s.length(); j++) { + num = num * 10 + (s.charAt(j) - '0'); + if (num + 1 == prev && dfs(s, j + 1, num)) { + return true; + } + if (num >= prev) { + break; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool splitString(string s) { + int n = s.size(); + unsigned long long val = 0; + for (int i = 0; i < n - 1; i++) { + val = val * 10 + (s[i] - '0'); + if (dfs(s, i + 1, val)) { + return true; + } + } + return false; + } + +private: + bool dfs(string& s, int index, long long prev) { + if (index == s.size()) { + return true; + } + unsigned long long num = 0; + for (int j = index; j < s.size(); j++) { + num = num * 10 + (s[j] - '0'); + if (num + 1 == prev && dfs(s, j + 1, num)) { + return true; + } + if (num >= prev) { + break; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + splitString(s) { + const n = s.length; + + const dfs = (index, prev) => { + if (index === n) { + return true; + } + let num = 0; + for (let j = index; j < n; j++) { + num = num * 10 + Number(s[j]); + if (num + 1 === prev && dfs(j + 1, num)) { + return true; + } + if (num >= prev) { + break; + } + } + return false; + }; + + let val = 0; + for (let i = 0; i < n - 1; i++) { + val = val * 10 + Number(s[i]); + if (dfs(i + 1, val)) { + return true; + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 4. Stack + +::tabs-start + +```python +class Solution: + def splitString(self, s: str) -> bool: + n = len(s) + stack = [] + val = 0 + + for i in range(n - 1): + val = val * 10 + int(s[i]) + stack.append((i + 1, val)) + + while stack: + index, prev = stack.pop() + num = 0 + for j in range(index, n): + num = num * 10 + int(s[j]) + if num + 1 == prev: + if j + 1 == n: + return True + stack.append((j + 1, num)) + elif num >= prev: + break + + return False +``` + +```java +public class Solution { + public boolean splitString(String s) { + int n = s.length(); + Stack stack = new Stack<>(); + long val = 0; + + for (int i = 0; i < n - 1; i++) { + val = val * 10 + (s.charAt(i) - '0'); + stack.push(new long[]{i + 1, val}); + + while (!stack.isEmpty()) { + long[] top = stack.pop(); + int index = (int) top[0]; + long prev = top[1]; + long num = 0; + + for (int j = index; j < n; j++) { + num = num * 10 + (s.charAt(j) - '0'); + if (num + 1 == prev) { + if (j + 1 == n) { + return true; + } + stack.push(new long[]{j + 1, num}); + } else if (num >= prev) { + break; + } + } + } + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool splitString(string s) { + int n = s.size(); + stack> stack; + unsigned long long val = 0; + + for (int i = 0; i < n - 1; i++) { + val = val * 10 + (s[i] - '0'); + stack.push({i + 1, val}); + + while (!stack.empty()) { + auto [index, prev] = stack.top(); + stack.pop(); + unsigned long long num = 0; + + for (int j = index; j < n; j++) { + num = num * 10 + (s[j] - '0'); + if (num + 1 == prev) { + if (j + 1 == n) { + return true; + } + stack.push({j + 1, num}); + } else if (num >= prev) { + break; + } + } + } + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + splitString(s) { + const n = s.length; + let stack = []; + let val = 0; + + for (let i = 0; i < n - 1; i++) { + val = val * 10 + Number(s[i]); + stack.push([i + 1, val]); + + while (stack.length) { + let [index, prev] = stack.pop(); + let num = 0; + + for (let j = index; j < n; j++) { + num = num * 10 + Number(s[j]); + if (num + 1 === prev) { + if (j + 1 === n) { + return true; + } + stack.push([j + 1, num]); + } else if (num >= prev) { + break; + } + } + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/sqrtx.md b/articles/sqrtx.md new file mode 100644 index 000000000..bc2e10fa7 --- /dev/null +++ b/articles/sqrtx.md @@ -0,0 +1,454 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def mySqrt(self, x: int) -> int: + if x == 0: + return 0 + + res = 1 + for i in range(1, x + 1): + if i * i > x: + return res + res = i + + return res +``` + +```java +public class Solution { + public int mySqrt(int x) { + if (x == 0) { + return 0; + } + + int res = 1; + for (int i = 1; i <= x; i++) { + if ((long) i * i > x) { + return res; + } + res = i; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int mySqrt(int x) { + if (x == 0) { + return 0; + } + + int res = 1; + for (int i = 1; i <= x; i++) { + if ((long long) i * i > x) { + return res; + } + res = i; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @return {number} + */ + mySqrt(x) { + if (x === 0) { + return 0; + } + + let res = 1; + for (let i = 1; i <= x; i++) { + if (i * i > x) { + return res; + } + res = i; + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int MySqrt(int x) { + if (x == 0) return 0; + + int res = 1; + for (int i = 1; i <= x; i++) { + if ((long)i * i > x) { + return res; + } + res = i; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\sqrt {n})$ +* Space complexity: $O(1)$ + +--- + +## 2. In-Built Function + +::tabs-start + +```python +class Solution: + def mySqrt(self, x: int) -> int: + return int(sqrt(x)) +``` + +```java +public class Solution { + public int mySqrt(int x) { + return (int) Math.sqrt(x); + } +} +``` + +```cpp +class Solution { +public: + int mySqrt(int x) { + return (int) sqrt(x); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @return {number} + */ + mySqrt(x) { + return Math.floor(Math.sqrt(x)); + } +} +``` + +```csharp +public class Solution { + public int MySqrt(int x) { + return (int)Math.Sqrt(x); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 3. Binary Search + +::tabs-start + +```python +class Solution: + def mySqrt(self, x: int) -> int: + l, r = 0, x + res = 0 + + while l <= r: + m = l + (r - l) // 2 + if m * m > x: + r = m - 1 + elif m * m < x: + l = m + 1 + res = m + else: + return m + + return res +``` + +```java +public class Solution { + public int mySqrt(int x) { + int l = 0, r = x; + int res = 0; + + while (l <= r) { + int m = l + (r - l) / 2; + if ((long) m * m > x) { + r = m - 1; + } else if ((long) m * m < x) { + l = m + 1; + res = m; + } else { + return m; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int mySqrt(int x) { + int l = 0, r = x; + int res = 0; + + while (l <= r) { + int m = l + (r - l) / 2; + if ((long long) m * m > x) { + r = m - 1; + } else if ((long long) m * m < x) { + l = m + 1; + res = m; + } else { + return m; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @return {number} + */ + mySqrt(x) { + let l = 0, r = x; + let res = 0; + + while (l <= r) { + const m = Math.floor(l + (r - l) / 2); + if (m * m > x) { + r = m - 1; + } else if (m * m < x) { + l = m + 1; + res = m; + } else { + return m; + } + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int MySqrt(int x) { + int l = 0, r = x; + int res = 0; + + while (l <= r) { + int m = l + (r - l) / 2; + long sq = (long)m * m; + + if (sq > x) { + r = m - 1; + } else if (sq < x) { + l = m + 1; + res = m; + } else { + return m; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Recursion + +::tabs-start + +```python +class Solution: + def mySqrt(self, x: int) -> int: + if x < 2: + return x + + l = self.mySqrt(x >> 2) << 1 + r = l + 1 + return l if r ** 2 > x else r +``` + +```java +public class Solution { + public int mySqrt(int x) { + if (x < 2) { + return x; + } + + int l = mySqrt(x >> 2) << 1; + int r = l + 1; + return (long) r * r > x ? l : r; + } +} +``` + +```cpp +class Solution { +public: + int mySqrt(int x) { + if (x < 2) { + return x; + } + + int l = mySqrt(x >> 2) << 1; + int r = l + 1; + return (long long) r * r > x ? l : r; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @return {number} + */ + mySqrt(x) { + if (x < 2) { + return x; + } + + const l = this.mySqrt(x >> 2) << 1; + const r = l + 1; + return r * r > x ? l : r; + } +} +``` + +```csharp +public class Solution { + public int MySqrt(int x) { + if (x < 2) { + return x; + } + + int l = MySqrt(x >> 2) << 1; + int r = l + 1; + return (long)r * r > x ? l : r; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(\log n)$ for recursion stack. + +--- + +## 5. Newton's Method + +::tabs-start + +```python +class Solution: + def mySqrt(self, x: int) -> int: + r = x + while r * r > x: + r = (r + x // r) >> 1 + return r +``` + +```java +public class Solution { + public int mySqrt(int x) { + long r = x; + while (r * r > x) { + r = (r + x / r) >> 1; + } + return (int) r; + } +} +``` + +```cpp +class Solution { +public: + int mySqrt(int x) { + long long r = x; + while (r * r > x) { + r = (r + x / r) >> 1; + } + return r; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} x + * @return {number} + */ + mySqrt(x) { + let r = x; + while (r * r > x) { + r = (r + Math.floor(x / r)) >>> 1; + } + return r; + } +} +``` + +```csharp +public class Solution { + public int MySqrt(int x) { + long r = x; + while (r * r > x) { + r = (r + x / r) >> 1; + } + return (int)r; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/squares-of-a-sorted-array.md b/articles/squares-of-a-sorted-array.md new file mode 100644 index 000000000..c4d76f6b3 --- /dev/null +++ b/articles/squares-of-a-sorted-array.md @@ -0,0 +1,266 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def sortedSquares(self, nums: List[int]) -> List[int]: + for i in range(len(nums)): + nums[i] *= nums[i] + nums.sort() + return nums +``` + +```java +public class Solution { + public int[] sortedSquares(int[] nums) { + for (int i = 0; i < nums.length; i++) { + nums[i] *= nums[i]; + } + Arrays.sort(nums); + return nums; + } +} +``` + +```cpp +class Solution { +public: + vector sortedSquares(vector& nums) { + for (int i = 0; i < nums.size(); i++) { + nums[i] *= nums[i]; + } + sort(nums.begin(), nums.end()); + return nums; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortedSquares(nums) { + for (let i = 0; i < nums.length; i++) { + nums[i] *= nums[i]; + } + nums.sort((a, b) => a - b); + return nums; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 2. Two Pointers - I + +::tabs-start + +```python +class Solution: + def sortedSquares(self, nums: List[int]) -> List[int]: + l, r, res = 0, len(nums) - 1, [] + + while l <= r: + if (nums[l] * nums[l]) > (nums[r] * nums[r]): + res.append(nums[l] * nums[l]) + l += 1 + else: + res.append(nums[r] * nums[r]) + r -= 1 + + return res[::-1] +``` + +```java +public class Solution { + public int[] sortedSquares(int[] nums) { + int l = 0, r = nums.length - 1; + ArrayList res = new ArrayList<>(); + + while (l <= r) { + if (nums[l] * nums[l] > nums[r] * nums[r]) { + res.add(nums[l] * nums[l]); + l++; + } else { + res.add(nums[r] * nums[r]); + r--; + } + } + + Collections.reverse(res); + return res.stream().mapToInt(i -> i).toArray(); + } +} +``` + +```cpp +class Solution { +public: + vector sortedSquares(vector& nums) { + int l = 0, r = nums.size() - 1; + vector res; + + while (l <= r) { + if (nums[l] * nums[l] > nums[r] * nums[r]) { + res.push_back(nums[l] * nums[l]); + l++; + } else { + res.push_back(nums[r] * nums[r]); + r--; + } + } + + reverse(res.begin(), res.end()); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortedSquares(nums) { + let l = 0, r = nums.length - 1; + const res = []; + + while (l <= r) { + if (nums[l] * nums[l] > nums[r] * nums[r]) { + res.push(nums[l] * nums[l]); + l++; + } else { + res.push(nums[r] * nums[r]); + r--; + } + } + + return res.reverse(); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for the output array. + +--- + +## 3. Two Pointers - II + +::tabs-start + +```python +class Solution: + def sortedSquares(self, nums: List[int]) -> List[int]: + n = len(nums) + res = [0] * n + l, r = 0, n - 1 + res_index = n - 1 + + while l <= r: + if abs(nums[l]) > abs(nums[r]): + res[res_index] = nums[l] * nums[l] + l += 1 + else: + res[res_index] = nums[r] * nums[r] + r -= 1 + res_index -= 1 + + return res +``` + +```java +public class Solution { + public int[] sortedSquares(int[] nums) { + int n = nums.length; + int[] res = new int[n]; + int l = 0, r = n - 1, resIndex = n - 1; + + while (l <= r) { + if (Math.abs(nums[l]) > Math.abs(nums[r])) { + res[resIndex] = nums[l] * nums[l]; + l++; + } else { + res[resIndex] = nums[r] * nums[r]; + r--; + } + resIndex--; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector sortedSquares(vector& nums) { + int n = nums.size(); + vector res(n); + int l = 0, r = n - 1, resIndex = n - 1; + + while (l <= r) { + if (abs(nums[l]) > abs(nums[r])) { + res[resIndex] = nums[l] * nums[l]; + l++; + } else { + res[resIndex] = nums[r] * nums[r]; + r--; + } + resIndex--; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + sortedSquares(nums) { + const n = nums.length; + const res = new Array(n); + let l = 0, r = n - 1, resIndex = n - 1; + + while (l <= r) { + if (Math.abs(nums[l]) > Math.abs(nums[r])) { + res[resIndex] = nums[l] * nums[l]; + l++; + } else { + res[resIndex] = nums[r] * nums[r]; + r--; + } + resIndex--; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for the output array. \ No newline at end of file diff --git a/articles/stickers-to-spell-word.md b/articles/stickers-to-spell-word.md new file mode 100644 index 000000000..2abbe91a7 --- /dev/null +++ b/articles/stickers-to-spell-word.md @@ -0,0 +1,580 @@ +## 1. Dynamic Programming (Top-Down) - I + +::tabs-start + +```python +class Solution: + def minStickers(self, stickers: List[str], target: str) -> int: + stickCount = [] + for s in stickers: + stickCount.append({}) + for c in s: + stickCount[-1][c] = 1 + stickCount[-1].get(c, 0) + + dp = {} + + def dfs(t, stick): + if t in dp: + return dp[t] + res = 1 if stick else 0 + remainT = "" + for c in t: + if c in stick and stick[c] > 0: + stick[c] -= 1 + else: + remainT += c + if remainT: + used = float("inf") + for s in stickCount: + if remainT[0] not in s: + continue + used = min(used, dfs(remainT, s.copy())) + dp[remainT] = used + res += used + return res + + res = dfs(target, {}) + return res if res != float("inf") else -1 +``` + +```java +public class Solution { + private List> stickCount; + private Map dp; + + public int minStickers(String[] stickers, String target) { + stickCount = new ArrayList<>(); + dp = new HashMap<>(); + + for (String s : stickers) { + Map countMap = new HashMap<>(); + for (char c : s.toCharArray()) { + countMap.put(c, countMap.getOrDefault(c, 0) + 1); + } + stickCount.add(countMap); + } + + int res = dfs(target, new HashMap<>()); + return res == Integer.MAX_VALUE ? -1 : res; + } + + private int dfs(String t, Map stick) { + if (t.isEmpty()) return 0; + if (dp.containsKey(t)) return dp.get(t); + + int res = stick.isEmpty() ? 0 : 1; + StringBuilder remainT = new StringBuilder(); + + for (char c : t.toCharArray()) { + if (stick.containsKey(c) && stick.get(c) > 0) { + stick.put(c, stick.get(c) - 1); + } else { + remainT.append(c); + } + } + + if (remainT.length() > 0) { + int used = Integer.MAX_VALUE; + for (Map s : stickCount) { + if (!s.containsKey(remainT.charAt(0))) continue; + int curr = dfs(remainT.toString(), new HashMap<>(s)); + if (curr != Integer.MAX_VALUE) { + used = Math.min(used, curr); + } + } + dp.put(remainT.toString(), used); + if (used != Integer.MAX_VALUE && res != Integer.MAX_VALUE) { + res += used; + } else { + res = Integer.MAX_VALUE; + } + } + + return res; + } +} +``` + +```cpp +class Solution { + vector> stickCount; + unordered_map dp; + +public: + int minStickers(vector& stickers, string target) { + stickCount.clear(); + dp.clear(); + + for (const string& s : stickers) { + unordered_map countMap; + for (char c : s) { + countMap[c]++; + } + stickCount.push_back(countMap); + } + + int res = dfs(target, unordered_map()); + return res == INT_MAX ? -1 : res; + } + +private: + int dfs(const string& t, unordered_map stick) { + if (t.empty()) return 0; + if (dp.count(t)) return dp[t]; + + int res = stick.empty() ? 0 : 1; + string remainT; + + for (char c : t) { + if (stick.count(c) && stick[c] > 0) { + stick[c]--; + } else { + remainT += c; + } + } + + if (!remainT.empty()) { + int used = INT_MAX; + for (const auto& s : stickCount) { + if (!s.count(remainT[0])) continue; + int curr = dfs(remainT, unordered_map(s)); + if (curr != INT_MAX) { + used = min(used, curr); + } + } + dp[remainT] = used; + if (used != INT_MAX && res != INT_MAX) { + res += used; + } else { + res = INT_MAX; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} stickers + * @param {string} target + * @return {number} + */ + minStickers(stickers, target) { + const stickCount = []; + const dp = new Map(); + + for (const s of stickers) { + const countMap = new Map(); + for (const c of s) { + countMap.set(c, (countMap.get(c) || 0) + 1); + } + stickCount.push(countMap); + } + + const dfs = (t, stick) => { + if (t === '') return 0; + if (dp.has(t)) return dp.get(t); + + let res = stick.size === 0 ? 0 : 1; + let remainT = ''; + + for (const c of t) { + if (stick.has(c) && stick.get(c) > 0) { + stick.set(c, stick.get(c) - 1); + } else { + remainT += c; + } + } + + if (remainT.length > 0) { + let used = Infinity; + for (const s of stickCount) { + if (!s.has(remainT[0])) continue; + const newStick = new Map(s); + const curr = dfs(remainT, newStick); + used = Math.min(used, curr); + } + dp.set(remainT, used); + if (used !== Infinity && res !== Infinity) { + res += used; + } else { + res = Infinity; + } + } + + return res; + }; + + const res = dfs(target, new Map()); + return res === Infinity ? -1 : res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * k *2 ^ n)$ +* Space complexity: $O(m * k + 2 ^ n)$ + +> Where $n$ is the length of the target string, $m$ is the number of stickers and $k$ is the average length of each sticker. + +--- + +## 2. Dynamic Programming (Top-Down) - II + +::tabs-start + +```python +class Solution: + def minStickers(self, stickers: List[str], target: str) -> int: + tmp = [c for c in target] + target = ''.join(sorted(tmp)) + stickCount = [] + for s in stickers: + stickCount.append(Counter(s)) + + dp = {} + dp[""] = 0 + + def dfs(t): + if t in dp: + return dp[t] + + tarMp = Counter(t) + res = float("inf") + for s in stickCount: + if t[0] not in s: + continue + remainT = [] + for c in tarMp: + if tarMp[c] > s[c]: + remainT.extend([c] * (tarMp[c] - s[c])) + + remainT = ''.join(sorted(remainT)) + res = min(res, 1 + dfs(remainT)) + + dp[t] = res + return res + + ans = dfs(target) + return -1 if ans == float("inf") else ans +``` + +```java +public class Solution { + private Map dp = new HashMap<>(); + private List> stickCount = new ArrayList<>(); + + public int minStickers(String[] stickers, String target) { + dp.put("", 0); + for (String s : stickers) { + Map counter = new HashMap<>(); + for (char c : s.toCharArray()) { + counter.put(c, counter.getOrDefault(c, 0) + 1); + } + stickCount.add(counter); + } + char[] targetArray = target.toCharArray(); + Arrays.sort(targetArray); + target = new String(targetArray); + + int ans = dfs(target); + return ans == Integer.MAX_VALUE ? -1 : ans; + } + + private int dfs(String t) { + if (dp.containsKey(t)) { + return dp.get(t); + } + + Map tarMp = new HashMap<>(); + for (char c : t.toCharArray()) { + tarMp.put(c, tarMp.getOrDefault(c, 0) + 1); + } + + int res = Integer.MAX_VALUE; + for (Map s : stickCount) { + if (!s.containsKey(t.charAt(0))) { + continue; + } + + StringBuilder remainT = new StringBuilder(); + for (Map.Entry entry : tarMp.entrySet()) { + char c = entry.getKey(); + int need = entry.getValue() - s.getOrDefault(c, 0); + for (int i = 0; i < Math.max(0, need); i++) { + remainT.append(c); + } + } + char[] remainArray = remainT.toString().toCharArray(); + Arrays.sort(remainArray); + int cur = dfs(new String(remainArray)); + if (cur == Integer.MAX_VALUE) cur--; + res = Math.min(res, 1 + cur); + } + + dp.put(t, res); + return res; + } +} +``` + +```cpp +class Solution { +private: + unordered_map dp; + vector> stickCount; + +public: + int minStickers(vector& stickers, string target) { + dp[""] = 0; + for (const string& s : stickers) { + unordered_map counter; + for (char c : s) { + counter[c]++; + } + stickCount.push_back(counter); + } + + sort(target.begin(), target.end()); + int ans = dfs(target); + return ans == INT_MAX ? -1 : ans; + } + + int dfs(const string& t) { + if (dp.find(t) != dp.end()) { + return dp[t]; + } + + unordered_map tarMp; + for (char c : t) { + tarMp[c]++; + } + + int res = INT_MAX; + for (auto& s : stickCount) { + if (s.find(t[0]) == s.end()) { + continue; + } + + string remainT; + for (const auto& [c, count] : tarMp) { + int need = count - (s.count(c) ? s.at(c) : 0); + remainT.append(max(0, need), c); + } + + sort(remainT.begin(), remainT.end()); + int cur = dfs(remainT); + if (cur == INT_MAX) cur--; + res = min(res, 1 + cur); + } + + dp[t] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} stickers + * @param {string} target + * @return {number} + */ + minStickers(stickers, target) { + const dp = { "": 0 }; + const stickCount = stickers.map((s) => { + const counter = {}; + for (const c of s) { + counter[c] = (counter[c] || 0) + 1; + } + return counter; + }); + + const dfs = (t) => { + if (dp[t] !== undefined) return dp[t]; + + const tarMp = {}; + for (const c of t) { + tarMp[c] = (tarMp[c] || 0) + 1; + } + + let res = Infinity; + for (const s of stickCount) { + if (!s[t[0]]) continue; + + let remainT = []; + for (const [c, count] of Object.entries(tarMp)) { + let need = count - (s[c] || 0); + while (need > 0) { + remainT.push(c); + need--; + } + } + + remainT = remainT.sort().join(""); + res = Math.min(res, 1 + dfs(remainT)); + } + + dp[t] = res; + return res; + }; + + const ans = dfs(target.split("").sort().join("")); + return ans === Infinity ? -1 : ans; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * k *2 ^ n)$ +* Space complexity: $O(m * k + 2 ^ n)$ + +> Where $n$ is the length of the target string, $m$ is the number of stickers and $k$ is the average length of each sticker. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minStickers(self, stickers: List[str], target: str) -> int: + n = len(target) + N = 1 << n + dp = [-1] * N + dp[0] = 0 + + for t in range(N): + if dp[t] == -1: + continue + for s in stickers: + nextT = t + for c in s: + for i in range(n): + if target[i] == c and not ((nextT >> i) & 1): + nextT |= 1 << i + break + if dp[nextT] == -1 or dp[nextT] > dp[t] + 1: + dp[nextT] = dp[t] + 1 + return dp[N - 1] +``` + +```java +public class Solution { + public int minStickers(String[] stickers, String target) { + int n = target.length(); + int N = 1 << n; + int[] dp = new int[N]; + Arrays.fill(dp, -1); + dp[0] = 0; + + for (int t = 0; t < N; t++) { + if (dp[t] == -1) continue; + for (String s : stickers) { + int nextT = t; + for (char c : s.toCharArray()) { + for (int i = 0; i < n; i++) { + if (target.charAt(i) == c && ((nextT >> i) & 1) == 0) { + nextT |= 1 << i; + break; + } + } + } + if (dp[nextT] == -1 || dp[nextT] > dp[t] + 1) { + dp[nextT] = dp[t] + 1; + } + } + } + + return dp[N - 1]; + } +} +``` + +```cpp +class Solution { +public: + int minStickers(vector& stickers, string target) { + int n = target.length(); + int N = 1 << n; + vector dp(N, -1); + dp[0] = 0; + + for (int t = 0; t < N; t++) { + if (dp[t] == -1) continue; + for (string& s : stickers) { + int nextT = t; + for (char c : s) { + for (int i = 0; i < n; i++) { + if (target[i] == c && ((nextT >> i) & 1) == 0) { + nextT |= 1 << i; + break; + } + } + } + if (dp[nextT] == -1 || dp[nextT] > dp[t] + 1) { + dp[nextT] = dp[t] + 1; + } + } + } + + return dp[N - 1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} stickers + * @param {string} target + * @return {number} + */ + minStickers(stickers, target) { + const n = target.length; + const N = 1 << n; + const dp = Array(N).fill(-1); + dp[0] = 0; + + for (let t = 0; t < N; t++) { + if (dp[t] === -1) continue; + for (let s of stickers) { + let nextT = t; + for (let c of s) { + for (let i = 0; i < n; i++) { + if (target[i] === c && ((nextT >> i) & 1) === 0) { + nextT |= 1 << i; + break; + } + } + } + if (dp[nextT] === -1 || dp[nextT] > dp[t] + 1) { + dp[nextT] = dp[t] + 1; + } + } + } + + return dp[N - 1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m * k *2 ^ n)$ +* Space complexity: $O(2 ^ n)$ + +> Where $n$ is the length of the target string, $m$ is the number of stickers and $k$ is the average length of each sticker. \ No newline at end of file diff --git a/articles/stone-game-ii.md b/articles/stone-game-ii.md new file mode 100644 index 000000000..b9468738a --- /dev/null +++ b/articles/stone-game-ii.md @@ -0,0 +1,540 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def stoneGameII(self, piles: List[int]) -> int: + dp = {} + + def dfs(alice, i, M): + if i == len(piles): + return 0 + if (alice, i, M) in dp: + return dp[(alice, i, M)] + + res = 0 if alice else float("inf") + total = 0 + for X in range(1, 2 * M + 1): + if i + X > len(piles): + break + total += piles[i + X - 1] + if alice: + res = max(res, total + dfs(not alice, i + X, max(M, X))) + else: + res = min(res, dfs(not alice, i + X, max(M, X))) + + dp[(alice, i, M)] = res + return res + + return dfs(True, 0, 1) +``` + +```java +public class Solution { + private int[][][] dp; + + public int stoneGameII(int[] piles) { + int n = piles.length; + dp = new int[2][n][n + 1]; + for (int[][] layer : dp) { + for (int[] row : layer) { + Arrays.fill(row, -1); + } + } + + return dfs(1, 0, 1, piles); + } + + private int dfs(int alice, int i, int M, int[] piles) { + if (i == piles.length) return 0; + if (dp[alice][i][M] != -1) return dp[alice][i][M]; + + int res = alice == 1 ? 0 : Integer.MAX_VALUE; + int total = 0; + + for (int X = 1; X <= 2 * M; X++) { + if (i + X > piles.length) break; + total += piles[i + X - 1]; + if (alice == 1) { + res = Math.max(res, total + dfs(0, i + X, Math.max(M, X), piles)); + } else { + res = Math.min(res, dfs(1, i + X, Math.max(M, X), piles)); + } + } + + dp[alice][i][M] = res; + return res; + } +} +``` + +```cpp +class Solution { +private: + vector>> dp; + +public: + int stoneGameII(vector& piles) { + int n = piles.size(); + dp.resize(2, vector>(n, vector(n + 1, -1))); + return dfs(1, 0, 1, piles); + } + +private: + int dfs(int alice, int i, int M, vector& piles) { + if (i == piles.size()) return 0; + if (dp[alice][i][M] != -1) return dp[alice][i][M]; + + int res = alice == 1 ? 0 : INT_MAX; + int total = 0; + + for (int X = 1; X <= 2 * M; X++) { + if (i + X > piles.size()) break; + total += piles[i + X - 1]; + if (alice == 1) { + res = max(res, total + dfs(0, i + X, max(M, X), piles)); + } else { + res = min(res, dfs(1, i + X, max(M, X), piles)); + } + } + + dp[alice][i][M] = res; + return dp[alice][i][M]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} piles + * @return {number} + */ + stoneGameII(piles) { + const n = piles.length; + this.dp = Array.from({ length: 2 }, () => + Array.from({ length: n }, () => Array(n + 1).fill(-1)) + ); + + const dfs = (alice, i, M) => { + if (i === n) return 0; + if (this.dp[alice][i][M] !== -1) return this.dp[alice][i][M]; + + let res = alice === 1 ? 0 : Infinity; + let total = 0; + + for (let X = 1; X <= 2 * M; X++) { + if (i + X > n) break; + total += piles[i + X - 1]; + if (alice === 1) { + res = Math.max(res, total + dfs(0, i + X, Math.max(M, X))); + } else { + res = Math.min(res, dfs(1, i + X, Math.max(M, X))); + } + } + + this.dp[alice][i][M] = res; + return res; + }; + + return dfs(1, 0, 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Dynamic Programming (Top-Down) + Suffix Sum + +::tabs-start + +```python +class Solution: + def stoneGameII(self, piles: List[int]) -> int: + n = len(piles) + dp = [[None] * (n + 1) for _ in range(n)] + suffix_sum = [0] * n + suffix_sum[-1] = piles[-1] + for i in range(n - 2, -1, -1): + suffix_sum[i] = piles[i] + suffix_sum[i + 1] + + def dfs(i, M): + if i == n: + return 0 + if dp[i][M] is not None: + return dp[i][M] + + res = 0 + for X in range(1, 2 * M + 1): + if i + X > n: + break + res = max(res, suffix_sum[i] - dfs(i + X, max(M, X))) + dp[i][M] = res + return res + + return dfs(0, 1) +``` + +```java +public class Solution { + private int[][] dp; + private int[] suffixSum; + + public int stoneGameII(int[] piles) { + int n = piles.length; + dp = new int[n][n + 1]; + for (int i = 0; i < n; i++) { + Arrays.fill(dp[i], -1); + } + + suffixSum = new int[n]; + suffixSum[n - 1] = piles[n - 1]; + for (int i = n - 2; i >= 0; i--) { + suffixSum[i] = piles[i] + suffixSum[i + 1]; + } + + return dfs(0, 1); + } + + private int dfs(int i, int M) { + if (i == suffixSum.length) return 0; + if (dp[i][M] != -1) return dp[i][M]; + + int res = 0; + for (int X = 1; X <= 2 * M; X++) { + if (i + X > suffixSum.length) break; + res = Math.max(res, suffixSum[i] - dfs(i + X, Math.max(M, X))); + } + + return dp[i][M] = res; + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + vector suffixSum; + +public: + int stoneGameII(vector& piles) { + int n = piles.size(); + dp.resize(n, vector(n + 1, -1)); + + suffixSum.resize(n); + suffixSum[n - 1] = piles[n - 1]; + for (int i = n - 2; i >= 0; i--) { + suffixSum[i] = piles[i] + suffixSum[i + 1]; + } + + return dfs(0, 1, piles); + } + +private: + int dfs(int i, int M, vector& piles) { + if (i == suffixSum.size()) return 0; + if (dp[i][M] != -1) return dp[i][M]; + + int res = 0; + for (int X = 1; X <= 2 * M; X++) { + if (i + X > suffixSum.size()) break; + res = max(res, suffixSum[i] - dfs(i + X, max(M, X), piles)); + } + + return dp[i][M] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} piles + * @return {number} + */ + stoneGameII(piles) { + const n = piles.length; + const dp = Array.from({ length: n }, () => Array(n + 1).fill(-1)); + + const suffixSum = Array(n).fill(0); + suffixSum[n - 1] = piles[n - 1]; + for (let i = n - 2; i >= 0; i--) { + suffixSum[i] = piles[i] + suffixSum[i + 1]; + } + + const dfs = (i, M) => { + if (i === n) return 0; + if (dp[i][M] !== -1) return dp[i][M]; + + let res = 0; + for (let X = 1; X <= 2 * M; X++) { + if (i + X > n) break; + res = Math.max(res, suffixSum[i] - dfs(i + X, Math.max(M, X))); + } + + return dp[i][M] = res; + }; + + return dfs(0, 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def stoneGameII(self, piles: List[int]) -> int: + n = len(piles) + dp = [[[0] * (n + 1) for _ in range(n + 1)] for _ in range(2)] + + for i in range(n - 1, -1, -1): + for M in range(1, n + 1): + total = 0 + dp[1][i][M] = 0 + dp[0][i][M] = float("inf") + + for X in range(1, 2 * M + 1): + if i + X > n: + break + total += piles[i + X - 1] + + dp[1][i][M] = max(dp[1][i][M], total + dp[0][i + X][max(M, X)]) + dp[0][i][M] = min(dp[0][i][M], dp[1][i + X][max(M, X)]) + + return dp[1][0][1] +``` + +```java +public class Solution { + public int stoneGameII(int[] piles) { + int n = piles.length; + int[][][] dp = new int[2][n + 1][n + 1]; + + for (int i = n - 1; i >= 0; i--) { + for (int M = 1; M <= n; M++) { + int total = 0; + dp[1][i][M] = 0; + dp[0][i][M] = Integer.MAX_VALUE; + + for (int X = 1; X <= 2 * M; X++) { + if (i + X > n) break; + total += piles[i + X - 1]; + dp[1][i][M] = Math.max(dp[1][i][M], total + dp[0][i + X][Math.max(M, X)]); + dp[0][i][M] = Math.min(dp[0][i][M], dp[1][i + X][Math.max(M, X)]); + } + } + } + + return dp[1][0][1]; + } +} +``` + +```cpp +class Solution { +public: + int stoneGameII(vector& piles) { + int n = piles.size(); + vector>> dp(2, vector>(n + 1, vector(n + 1, 0))); + + for (int i = n - 1; i >= 0; i--) { + for (int M = 1; M <= n; M++) { + int total = 0; + dp[1][i][M] = 0; + dp[0][i][M] = INT_MAX; + + for (int X = 1; X <= 2 * M; X++) { + if (i + X > n) break; + total += piles[i + X - 1]; + + dp[1][i][M] = max(dp[1][i][M], total + dp[0][i + X][max(M, X)]); + dp[0][i][M] = min(dp[0][i][M], dp[1][i + X][max(M, X)]); + } + } + } + + return dp[1][0][1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} piles + * @return {number} + */ + stoneGameII(piles) { + const n = piles.length; + const dp = Array.from({ length: 2 }, () => + Array.from({ length: n + 1 }, () => Array(n + 1).fill(0)) + ); + + for (let i = n - 1; i >= 0; i--) { + for (let M = 1; M <= n; M++) { + let total = 0; + dp[1][i][M] = 0; + dp[0][i][M] = Infinity; + + for (let X = 1; X <= 2 * M; X++) { + if (i + X > n) break; + total += piles[i + X - 1]; + + dp[1][i][M] = Math.max(dp[1][i][M], total + dp[0][i + X][Math.max(M, X)]); + dp[0][i][M] = Math.min(dp[0][i][M], dp[1][i + X][Math.max(M, X)]); + } + } + } + + return dp[1][0][1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Dynamic Programming (Bottom-Up) + Suffix Sum + +::tabs-start + +```python +class Solution: + def stoneGameII(self, piles: List[int]) -> int: + n = len(piles) + suffix_sum = [0] * n + suffix_sum[-1] = piles[-1] + for i in range(n - 2, -1, -1): + suffix_sum[i] = piles[i] + suffix_sum[i + 1] + + dp = [[0] * (n + 1) for _ in range(n + 1)] + + for i in range(n - 1, -1, -1): + for M in range(1, n + 1): + for X in range(1, 2 * M + 1): + if i + X > n: + break + dp[i][M] = max(dp[i][M], suffix_sum[i] - dp[i + X][max(M, X)]) + + return dp[0][1] +``` + +```java +public class Solution { + public int stoneGameII(int[] piles) { + int n = piles.length; + + int[] suffixSum = new int[n]; + suffixSum[n - 1] = piles[n - 1]; + for (int i = n - 2; i >= 0; i--) { + suffixSum[i] = piles[i] + suffixSum[i + 1]; + } + + int[][] dp = new int[n + 1][n + 1]; + + for (int i = n - 1; i >= 0; i--) { + for (int M = 1; M <= n; M++) { + for (int X = 1; X <= 2 * M; X++) { + if (i + X > n) break; + dp[i][M] = Math.max(dp[i][M], suffixSum[i] - dp[i + X][Math.max(M, X)]); + } + } + } + + return dp[0][1]; + } +} +``` + +```cpp +class Solution { +public: + int stoneGameII(vector& piles) { + int n = piles.size(); + + vector suffixSum(n, 0); + suffixSum[n - 1] = piles[n - 1]; + for (int i = n - 2; i >= 0; i--) { + suffixSum[i] = piles[i] + suffixSum[i + 1]; + } + + vector> dp(n + 1, vector(n + 1, 0)); + + for (int i = n - 1; i >= 0; i--) { + for (int M = 1; M <= n; M++) { + for (int X = 1; X <= 2 * M; X++) { + if (i + X > n) break; + dp[i][M] = max(dp[i][M], suffixSum[i] - dp[i + X][max(M, X)]); + } + } + } + + return dp[0][1]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} piles + * @return {number} + */ + stoneGameII(piles) { + const n = piles.length; + + const suffixSum = Array(n).fill(0); + suffixSum[n - 1] = piles[n - 1]; + for (let i = n - 2; i >= 0; i--) { + suffixSum[i] = piles[i] + suffixSum[i + 1]; + } + + const dp = Array.from({ length: n + 1 }, () => Array(n + 1).fill(0)); + + for (let i = n - 1; i >= 0; i--) { + for (let M = 1; M <= n; M++) { + for (let X = 1; X <= 2 * M; X++) { + if (i + X > n) break; + dp[i][M] = Math.max(dp[i][M], suffixSum[i] - dp[i + X][Math.max(M, X)]); + } + } + } + + return dp[0][1]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/stone-game-iii.md b/articles/stone-game-iii.md new file mode 100644 index 000000000..781024036 --- /dev/null +++ b/articles/stone-game-iii.md @@ -0,0 +1,597 @@ +## 1. Dynamic Programming (Top-Down) - I + +::tabs-start + +```python +class Solution: + def stoneGameIII(self, stoneValue: List[int]) -> str: + n = len(stoneValue) + dp = [[None] * 2 for _ in range(n)] + + def dfs(i, alice): + if i >= n: + return 0 + if dp[i][alice] is not None: + return dp[i][alice] + + res = float("-inf") if alice == 1 else float("inf") + score = 0 + for j in range(i, min(i + 3, n)): + if alice == 1: + score += stoneValue[j] + res = max(res, score + dfs(j + 1, 0)) + else: + score -= stoneValue[j] + res = min(res, score + dfs(j + 1, 1)) + + dp[i][alice] = res + return res + + result = dfs(0, 1) + if result == 0: + return "Tie" + return "Alice" if result > 0 else "Bob" +``` + +```java +public class Solution { + private Integer[][] dp; + private int n; + + public String stoneGameIII(int[] stoneValue) { + n = stoneValue.length; + dp = new Integer[n][2]; + + int result = dfs(0, 1, stoneValue); + if (result == 0) return "Tie"; + return result > 0 ? "Alice" : "Bob"; + } + + private int dfs(int i, int alice, int[] stoneValue) { + if (i >= n) return 0; + if (dp[i][alice] != null) return dp[i][alice]; + + int res = alice == 1 ? Integer.MIN_VALUE : Integer.MAX_VALUE; + int score = 0; + for (int j = i; j < Math.min(i + 3, n); j++) { + if (alice == 1) { + score += stoneValue[j]; + res = Math.max(res, score + dfs(j + 1, 0, stoneValue)); + } else { + score -= stoneValue[j]; + res = Math.min(res, score + dfs(j + 1, 1, stoneValue)); + } + } + + dp[i][alice] = res; + return res; + } +} +``` + +```cpp +class Solution { + vector> dp; + int n; + +public: + string stoneGameIII(vector& stoneValue) { + n = stoneValue.size(); + dp.assign(n, vector(2, INT_MIN)); + + int result = dfs(0, 1, stoneValue); + if (result == 0) return "Tie"; + return result > 0 ? "Alice" : "Bob"; + } + +private: + int dfs(int i, int alice, vector& stoneValue) { + if (i >= n) return 0; + if (dp[i][alice] != INT_MIN) return dp[i][alice]; + + int res = alice == 1 ? INT_MIN : INT_MAX; + int score = 0; + for (int j = i; j < min(i + 3, n); j++) { + if (alice == 1) { + score += stoneValue[j]; + res = max(res, score + dfs(j + 1, 0, stoneValue)); + } else { + score -= stoneValue[j]; + res = min(res, score + dfs(j + 1, 1, stoneValue)); + } + } + + dp[i][alice] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} stoneValue + * @return {string} + */ + stoneGameIII(stoneValue) { + const n = stoneValue.length; + const dp = Array.from({ length: n }, () => [null, null]); + + const dfs = (i, alice) => { + if (i >= n) return 0; + if (dp[i][alice] !== null) return dp[i][alice]; + + let res = alice === 1 ? -Infinity : Infinity; + let score = 0; + for (let j = i; j < Math.min(i + 3, n); j++) { + if (alice === 1) { + score += stoneValue[j]; + res = Math.max(res, score + dfs(j + 1, 0)); + } else { + score -= stoneValue[j]; + res = Math.min(res, score + dfs(j + 1, 1)); + } + } + + dp[i][alice] = res; + return res; + }; + + const result = dfs(0, 1); + if (result === 0) return "Tie"; + return result > 0 ? "Alice" : "Bob"; + } +} +``` + +```csharp +public class Solution { + public string StoneGameIII(int[] stoneValue) { + int n = stoneValue.Length; + int?[,] dp = new int?[n, 2]; + + int Dfs(int i, int alice) { + if (i >= n) return 0; + if (dp[i, alice].HasValue) return dp[i, alice].Value; + + int res = alice == 1 ? int.MinValue : int.MaxValue; + int score = 0; + + for (int j = i; j < Math.Min(i + 3, n); j++) { + if (alice == 1) { + score += stoneValue[j]; + res = Math.Max(res, score + Dfs(j + 1, 0)); + } else { + score -= stoneValue[j]; + res = Math.Min(res, score + Dfs(j + 1, 1)); + } + } + + dp[i, alice] = res; + return res; + } + + int result = Dfs(0, 1); + if (result == 0) return "Tie"; + return result > 0 ? "Alice" : "Bob"; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) - II + +::tabs-start + +```python +class Solution: + def stoneGameIII(self, stoneValue: List[int]) -> str: + n = len(stoneValue) + dp = {} + + def dfs(i): + if i >= n: + return 0 + if i in dp: + return dp[i] + + res, total = float("-inf"), 0 + for j in range(i, min(i + 3, n)): + total += stoneValue[j] + res = max(res, total - dfs(j + 1)) + + dp[i] = res + return res + + result = dfs(0) + if result == 0: + return "Tie" + return "Alice" if result > 0 else "Bob" +``` + +```java +public class Solution { + private int[] dp; + private int n; + + public String stoneGameIII(int[] stoneValue) { + this.n = stoneValue.length; + this.dp = new int[n]; + Arrays.fill(dp, Integer.MIN_VALUE); + + int result = dfs(0, stoneValue); + if (result == 0) return "Tie"; + return result > 0 ? "Alice" : "Bob"; + } + + private int dfs(int i, int[] stoneValue) { + if (i >= n) return 0; + if (dp[i] != Integer.MIN_VALUE) return dp[i]; + + int res = Integer.MIN_VALUE, total = 0; + for (int j = i; j < Math.min(i + 3, n); j++) { + total += stoneValue[j]; + res = Math.max(res, total - dfs(j + 1, stoneValue)); + } + + dp[i] = res; + return res; + } +} +``` + +```cpp +class Solution { +public: + string stoneGameIII(vector& stoneValue) { + n = stoneValue.size(); + dp.assign(n, INT_MIN); + + int result = dfs(0, stoneValue); + if (result == 0) return "Tie"; + return result > 0 ? "Alice" : "Bob"; + } + +private: + vector dp; + int n; + + int dfs(int i, vector& stoneValue) { + if (i >= n) return 0; + if (dp[i] != INT_MIN) return dp[i]; + + int res = INT_MIN, total = 0; + for (int j = i; j < min(i + 3, n); j++) { + total += stoneValue[j]; + res = max(res, total - dfs(j + 1, stoneValue)); + } + + dp[i] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} stoneValue + * @return {string} + */ + stoneGameIII(stoneValue) { + const n = stoneValue.length; + const dp = new Array(n); + + const dfs = (i) => { + if (i >= n) return 0; + if (dp[i] !== undefined) return dp[i]; + + let res = -Infinity, total = 0; + for (let j = i; j < Math.min(i + 3, n); j++) { + total += stoneValue[j]; + res = Math.max(res, total - dfs(j + 1)); + } + + dp[i] = res; + return res; + }; + + const result = dfs(0); + if (result === 0) return "Tie"; + return result > 0 ? "Alice" : "Bob"; + } +} +``` + +```csharp +public class Solution { + public string StoneGameIII(int[] stoneValue) { + int n = stoneValue.Length; + Dictionary dp = new Dictionary(); + + int Dfs(int i) { + if (i >= n) return 0; + if (dp.ContainsKey(i)) return dp[i]; + + int res = int.MinValue, total = 0; + for (int j = i; j < Math.Min(i + 3, n); j++) { + total += stoneValue[j]; + res = Math.Max(res, total - Dfs(j + 1)); + } + + dp[i] = res; + return res; + } + + int result = Dfs(0); + if (result == 0) return "Tie"; + return result > 0 ? "Alice" : "Bob"; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def stoneGameIII(self, stoneValue: List[int]) -> str: + n = len(stoneValue) + dp = [float("-inf")] * (n + 1) + dp[n] = 0 + + for i in range(n - 1, -1, -1): + total = 0 + for j in range(i, min(i + 3, n)): + total += stoneValue[j] + dp[i] = max(dp[i], total - dp[j + 1]) + + result = dp[0] + if result == 0: + return "Tie" + return "Alice" if result > 0 else "Bob" +``` + +```java +public class Solution { + public String stoneGameIII(int[] stoneValue) { + int n = stoneValue.length; + int[] dp = new int[n + 1]; + Arrays.fill(dp, Integer.MIN_VALUE); + dp[n] = 0; + + for (int i = n - 1; i >= 0; i--) { + int total = 0; + dp[i] = Integer.MIN_VALUE; + for (int j = i; j < Math.min(i + 3, n); j++) { + total += stoneValue[j]; + dp[i] = Math.max(dp[i], total - dp[j + 1]); + } + } + + int result = dp[0]; + if (result == 0) return "Tie"; + return result > 0 ? "Alice" : "Bob"; + } +} +``` + +```cpp +class Solution { +public: + string stoneGameIII(vector& stoneValue) { + int n = stoneValue.size(); + vector dp(n + 1, INT_MIN); + dp[n] = 0; + + for (int i = n - 1; i >= 0; i--) { + int total = 0; + dp[i] = INT_MIN; + for (int j = i; j < min(i + 3, n); j++) { + total += stoneValue[j]; + dp[i] = max(dp[i], total - dp[j + 1]); + } + } + + int result = dp[0]; + if (result == 0) return "Tie"; + return result > 0 ? "Alice" : "Bob"; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} stoneValue + * @return {string} + */ + stoneGameIII(stoneValue) { + const n = stoneValue.length; + const dp = new Array(n + 1).fill(-Infinity); + dp[n] = 0; + + for (let i = n - 1; i >= 0; i--) { + let total = 0; + dp[i] = -Infinity; + for (let j = i; j < Math.min(i + 3, n); j++) { + total += stoneValue[j]; + dp[i] = Math.max(dp[i], total - dp[j + 1]); + } + } + + const result = dp[0]; + if (result === 0) return "Tie"; + return result > 0 ? "Alice" : "Bob"; + } +} +``` + +```csharp +public class Solution { + public string StoneGameIII(int[] stoneValue) { + int n = stoneValue.Length; + int[] dp = new int[n + 1]; + for (int i = 0; i <= n; i++) dp[i] = int.MinValue; + dp[n] = 0; + + for (int i = n - 1; i >= 0; i--) { + int total = 0; + for (int j = i; j < Math.Min(i + 3, n); j++) { + total += stoneValue[j]; + dp[i] = Math.Max(dp[i], total - dp[j + 1]); + } + } + + int result = dp[0]; + if (result == 0) return "Tie"; + return result > 0 ? "Alice" : "Bob"; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def stoneGameIII(self, stoneValue: List[int]) -> str: + n = len(stoneValue) + dp = [0] * 4 + + for i in range(n - 1, -1, -1): + total = 0 + dp[i % 4] = float("-inf") + for j in range(i, min(i + 3, n)): + total += stoneValue[j] + dp[i % 4] = max(dp[i % 4], total - dp[(j + 1) % 4]) + + if dp[0] == 0: + return "Tie" + return "Alice" if dp[0] > 0 else "Bob" +``` + +```java +public class Solution { + public String stoneGameIII(int[] stoneValue) { + int n = stoneValue.length; + int[] dp = new int[4]; + + for (int i = n - 1; i >= 0; i--) { + int total = 0; + dp[i % 4] = Integer.MIN_VALUE; + for (int j = i; j < Math.min(i + 3, n); j++) { + total += stoneValue[j]; + dp[i % 4] = Math.max(dp[i % 4], total - dp[(j + 1) % 4]); + } + } + + if (dp[0] == 0) return "Tie"; + return dp[0] > 0 ? "Alice" : "Bob"; + } +} +``` + +```cpp +class Solution { +public: + string stoneGameIII(vector& stoneValue) { + int n = stoneValue.size(); + vector dp(4, 0); + + for (int i = n - 1; i >= 0; --i) { + int total = 0; + dp[i % 4] = INT_MIN; + for (int j = i; j < min(i + 3, n); ++j) { + total += stoneValue[j]; + dp[i % 4] = max(dp[i % 4], total - dp[(j + 1) % 4]); + } + } + + if (dp[0] == 0) return "Tie"; + return dp[0] > 0 ? "Alice" : "Bob"; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} stoneValue + * @return {string} + */ + stoneGameIII(stoneValue) { + const n = stoneValue.length; + const dp = new Array(4).fill(0); + + for (let i = n - 1; i >= 0; i--) { + let total = 0; + dp[i % 4] = -Infinity; + for (let j = i; j < Math.min(i + 3, n); j++) { + total += stoneValue[j]; + dp[i % 4] = Math.max(dp[i % 4], total - dp[(j + 1) % 4]); + } + } + + if (dp[0] === 0) return "Tie"; + return dp[0] > 0 ? "Alice" : "Bob"; + } +} +``` + +```csharp +public class Solution { + public string StoneGameIII(int[] stoneValue) { + int n = stoneValue.Length; + int[] dp = new int[4]; + + for (int i = n - 1; i >= 0; i--) { + int total = 0; + dp[i % 4] = int.MinValue; + + for (int j = i; j < Math.Min(i + 3, n); j++) { + total += stoneValue[j]; + dp[i % 4] = Math.Max(dp[i % 4], total - dp[(j + 1) % 4]); + } + } + + if (dp[0] == 0) return "Tie"; + return dp[0] > 0 ? "Alice" : "Bob"; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/stone-game.md b/articles/stone-game.md new file mode 100644 index 000000000..54e5043c1 --- /dev/null +++ b/articles/stone-game.md @@ -0,0 +1,525 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def stoneGame(self, piles: List[int]) -> bool: + def dfs(l, r): + if l > r: + return 0 + even = (r - l) % 2 == 0 + left = piles[l] if even else 0 + right = piles[r] if even else 0 + return max(dfs(l + 1, r) + left, dfs(l, r - 1) + right) + + total = sum(piles) + alice_score = dfs(0, len(piles) - 1) + return alice_score > total - alice_score +``` + +```java +public class Solution { + public boolean stoneGame(int[] piles) { + int total = 0; + for (int pile : piles) { + total += pile; + } + + int aliceScore = dfs(0, piles.length - 1, piles); + return aliceScore > total - aliceScore; + } + + private int dfs(int l, int r, int[] piles) { + if (l > r) { + return 0; + } + boolean even = (r - l) % 2 == 0; + int left = even ? piles[l] : 0; + int right = even ? piles[r] : 0; + return Math.max(dfs(l + 1, r, piles) + left, dfs(l, r - 1, piles) + right); + } +} +``` + +```cpp +class Solution { +public: + bool stoneGame(vector& piles) { + int total = accumulate(piles.begin(), piles.end(), 0); + int aliceScore = dfs(0, piles.size() - 1, piles); + return aliceScore > total - aliceScore; + } + +private: + int dfs(int l, int r, const vector& piles) { + if (l > r) { + return 0; + } + bool even = (r - l) % 2 == 0; + int left = even ? piles[l] : 0; + int right = even ? piles[r] : 0; + return max(dfs(l + 1, r, piles) + left, dfs(l, r - 1, piles) + right); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} piles + * @return {boolean} + */ + stoneGame(piles) { + const total = piles.reduce((a, b) => a + b, 0); + + const dfs = (l, r) => { + if (l > r) { + return 0; + } + const even = (r - l) % 2 === 0; + const left = even ? piles[l] : 0; + const right = even ? piles[r] : 0; + return Math.max(dfs(l + 1, r) + left, dfs(l, r - 1) + right); + }; + + const aliceScore = dfs(0, piles.length - 1); + return aliceScore > total - aliceScore; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def stoneGame(self, piles: List[int]) -> bool: + dp = {} + + def dfs(l, r): + if l > r: + return 0 + if (l, r) in dp: + return dp[(l, r)] + even = (r - l) % 2 == 0 + left = piles[l] if even else 0 + right = piles[r] if even else 0 + dp[(l, r)] = max(dfs(l + 1, r) + left, dfs(l, r - 1) + right) + return dp[(l, r)] + + total = sum(piles) + alice_score = dfs(0, len(piles) - 1) + return alice_score > total - alice_score +``` + +```java +public class Solution { + private int[][] dp; + + public boolean stoneGame(int[] piles) { + int n = piles.length; + dp = new int[n][n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + dp[i][j] = -1; + } + } + + int total = 0; + for (int pile : piles) { + total += pile; + } + + int aliceScore = dfs(0, n - 1, piles); + return aliceScore > total - aliceScore; + } + + private int dfs(int l, int r, int[] piles) { + if (l > r) { + return 0; + } + if (dp[l][r] != -1) { + return dp[l][r]; + } + boolean even = (r - l) % 2 == 0; + int left = even ? piles[l] : 0; + int right = even ? piles[r] : 0; + dp[l][r] = Math.max(dfs(l + 1, r, piles) + left, dfs(l, r - 1, piles) + right); + return dp[l][r]; + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + +public: + bool stoneGame(vector& piles) { + int n = piles.size(); + dp = vector>(n, vector(n, -1)); + int total = accumulate(piles.begin(), piles.end(), 0); + int aliceScore = dfs(0, n - 1, piles); + return aliceScore > total - aliceScore; + } + +private: + int dfs(int l, int r, const vector& piles) { + if (l > r) { + return 0; + } + if (dp[l][r] != -1) { + return dp[l][r]; + } + bool even = (r - l) % 2 == 0; + int left = even ? piles[l] : 0; + int right = even ? piles[r] : 0; + dp[l][r] = max(dfs(l + 1, r, piles) + left, dfs(l, r - 1, piles) + right); + return dp[l][r]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} piles + * @return {boolean} + */ + stoneGame(piles) { + const n = piles.length; + const dp = Array.from({ length: n }, () => Array(n).fill(-1)); + + const dfs = (l, r) => { + if (l > r) { + return 0; + } + if (dp[l][r] !== -1) { + return dp[l][r]; + } + const even = (r - l) % 2 === 0; + const left = even ? piles[l] : 0; + const right = even ? piles[r] : 0; + dp[l][r] = Math.max(dfs(l + 1, r) + left, dfs(l, r - 1) + right); + return dp[l][r]; + }; + + const total = piles.reduce((a, b) => a + b, 0); + const aliceScore = dfs(0, n - 1); + return aliceScore > total - aliceScore; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def stoneGame(self, piles: List[int]) -> bool: + n = len(piles) + dp = [[0] * n for _ in range(n)] + + for l in range(n - 1, -1, -1): + for r in range(l, n): + even = (r - l) % 2 == 0 + left = piles[l] if even else 0 + right = piles[r] if even else 0 + if l == r: + dp[l][r] = left + else: + dp[l][r] = max(dp[l + 1][r] + left, dp[l][r - 1] + right) + + total = sum(piles) + alice_score = dp[0][n - 1] + return alice_score > total - alice_score +``` + +```java +public class Solution { + public boolean stoneGame(int[] piles) { + int n = piles.length; + int[][] dp = new int[n][n]; + + for (int l = n - 1; l >= 0; l--) { + for (int r = l; r < n; r++) { + boolean even = (r - l) % 2 == 0; + int left = even ? piles[l] : 0; + int right = even ? piles[r] : 0; + if (l == r) { + dp[l][r] = left; + } else { + dp[l][r] = Math.max(dp[l + 1][r] + left, dp[l][r - 1] + right); + } + } + } + + int total = 0; + for (int pile : piles) { + total += pile; + } + + int aliceScore = dp[0][n - 1]; + return aliceScore > total - aliceScore; + } +} +``` + +```cpp +class Solution { +public: + bool stoneGame(vector& piles) { + int n = piles.size(); + vector> dp(n, vector(n, 0)); + + for (int l = n - 1; l >= 0; l--) { + for (int r = l; r < n; r++) { + bool even = (r - l) % 2 == 0; + int left = even ? piles[l] : 0; + int right = even ? piles[r] : 0; + if (l == r) { + dp[l][r] = left; + } else { + dp[l][r] = max(dp[l + 1][r] + left, dp[l][r - 1] + right); + } + } + } + + int total = accumulate(piles.begin(), piles.end(), 0); + int aliceScore = dp[0][n - 1]; + return aliceScore > total - aliceScore; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} piles + * @return {boolean} + */ + stoneGame(piles) { + const n = piles.length; + const dp = Array.from({ length: n }, () => Array(n).fill(0)); + + for (let l = n - 1; l >= 0; l--) { + for (let r = l; r < n; r++) { + const even = (r - l) % 2 === 0; + const left = even ? piles[l] : 0; + const right = even ? piles[r] : 0; + if (l === r) { + dp[l][r] = left; + } else { + dp[l][r] = Math.max(dp[l + 1][r] + left, dp[l][r - 1] + right); + } + } + } + + const total = piles.reduce((a, b) => a + b, 0); + const aliceScore = dp[0][n - 1]; + return aliceScore > total - aliceScore; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def stoneGame(self, piles: List[int]) -> bool: + n = len(piles) + dp = [0] * n + + for l in reversed(range(n)): + for r in range(l, n): + even = ((r - l) % 2 == 0) + left = piles[l] if even else 0 + right = piles[r] if even else 0 + + if l == r: + dp[r] = left + else: + dp[r] = max(dp[r] + left, dp[r - 1] + right) + + total = sum(piles) + alice_score = dp[n - 1] + return alice_score > (total - alice_score) +``` + +```java +public class Solution { + public boolean stoneGame(int[] piles) { + int n = piles.length; + int[] dp = new int[n]; + + for (int l = n - 1; l >= 0; l--) { + for (int r = l; r < n; r++) { + boolean even = (r - l) % 2 == 0; + int left = even ? piles[l] : 0; + int right = even ? piles[r] : 0; + + if (l == r) { + dp[r] = left; + } else { + dp[r] = Math.max(dp[r] + left, dp[r - 1] + right); + } + } + } + + int total = 0; + for (int pile : piles) { + total += pile; + } + + int aliceScore = dp[n - 1]; + return aliceScore > (total - aliceScore); + } +} +``` + +```cpp +class Solution { +public: + bool stoneGame(vector& piles) { + int n = piles.size(); + vector dp(n, 0); + + for (int l = n - 1; l >= 0; l--) { + for (int r = l; r < n; r++) { + bool even = (r - l) % 2 == 0; + int left = even ? piles[l] : 0; + int right = even ? piles[r] : 0; + + if (l == r) { + dp[r] = left; + } else { + dp[r] = max(dp[r] + left, dp[r - 1] + right); + } + } + } + + int total = accumulate(piles.begin(), piles.end(), 0); + int aliceScore = dp[n - 1]; + return aliceScore > (total - aliceScore); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} piles + * @return {boolean} + */ + stoneGame(piles) { + const n = piles.length; + const dp = new Array(n).fill(0); + + for (let l = n - 1; l >= 0; l--) { + for (let r = l; r < n; r++) { + const even = (r - l) % 2 === 0; + const left = even ? piles[l] : 0; + const right = even ? piles[r] : 0; + + if (l === r) { + dp[r] = left; + } else { + dp[r] = Math.max(dp[r] + left, dp[r - 1] + right); + } + } + } + + const total = piles.reduce((a, b) => a + b, 0); + const aliceScore = dp[n - 1]; + return aliceScore > (total - aliceScore); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 5. Return TRUE + +::tabs-start + +```python +class Solution: + def stoneGame(self, piles: List[int]) -> bool: + return True +``` + +```java +public class Solution { + public boolean stoneGame(int[] piles) { + return true; + } +} +``` + +```cpp +class Solution { +public: + bool stoneGame(vector& piles) { + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} piles + * @return {boolean} + */ + stoneGame(piles) { + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/string-compression-ii.md b/articles/string-compression-ii.md new file mode 100644 index 000000000..d1021b9ea --- /dev/null +++ b/articles/string-compression-ii.md @@ -0,0 +1,480 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def getLengthOfOptimalCompression(self, s: str, k: int) -> int: + cache = {} + + def count(i, k, prev, prev_cnt): + if (i, k, prev, prev_cnt) in cache: + return cache[(i, k, prev, prev_cnt)] + if k < 0: + return float("inf") + if i == len(s): + return 0 + + if s[i] == prev: + incr = 1 if prev_cnt in [1, 9, 99] else 0 + res = incr + count(i + 1, k, prev, prev_cnt + 1) + else: + res = min( + count(i + 1, k - 1, prev, prev_cnt), # delete s[i] + 1 + count(i + 1, k, s[i], 1) # don't delete + ) + + cache[(i, k, prev, prev_cnt)] = res + return res + + return count(0, k, "", 0) +``` + +```java +public class Solution { + private final int INF = Integer.MAX_VALUE / 2; + private String s; + private int[][][][] dp; + + public int getLengthOfOptimalCompression(String s, int k) { + this.s = s; + int n = s.length(); + dp = new int[n + 1][k + 1][27][n + 1]; + for (int[][][] arr1 : dp) { + for (int[][] arr2 : arr1) { + for (int[] arr3 : arr2) { + Arrays.fill(arr3, -1); + } + } + } + return count(0, k, 26, 0); + } + + private int count(int i, int k, int prev, int prevCnt) { + if (k < 0) return INF; + if (i == s.length()) return 0; + if (dp[i][k][prev][prevCnt] != -1) return dp[i][k][prev][prevCnt]; + + int res; + if (prev == (s.charAt(i) - 'a')) { + int incr = (prevCnt == 1 || prevCnt == 9 || prevCnt == 99) ? 1 : 0; + res = incr + count(i + 1, k, prev, prevCnt + 1); + } else { + res = Math.min( + count(i + 1, k - 1, prev, prevCnt), // delete s[i] + 1 + count(i + 1, k, s.charAt(i) - 'a', 1) // don't delete + ); + } + + return dp[i][k][prev][prevCnt] = res; + } +} +``` + +```cpp +class Solution { + static const int INF = INT_MAX / 2; + vector>>> dp; + + int count(int i, int k, int prev, int prevCnt, string& s) { + if (k < 0) return INF; + if (i == s.size()) return 0; + if (dp[i][k][prev][prevCnt] != -1) return dp[i][k][prev][prevCnt]; + + int res; + if (prev == s[i] - 'a') { + int incr = (prevCnt == 1 || prevCnt == 9 || prevCnt == 99) ? 1 : 0; + res = incr + count(i + 1, k, prev, prevCnt + 1, s); + } else { + res = 1 + count(i + 1, k, s[i] - 'a', 1, s); // don't delete + if (k > 0) { + res = min(res, count(i + 1, k - 1, prev, prevCnt, s)); // delete s[i] + } + } + + return dp[i][k][prev][prevCnt] = res; + } + +public: + int getLengthOfOptimalCompression(string s, int k) { + int n = s.size(); + dp = vector>>>( + n + 1, vector>>(k + 1, vector>(27, vector(101, -1))) + ); + return count(0, k, 26, 0, s); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + getLengthOfOptimalCompression(s, k) { + const INF = 1e9; + const n = s.length; + const dp = {}; + + const count = (i, k, prev, prevCnt) => { + if (k < 0) return INF; + if (i === n) return 0; + const key = `${i},${k},${prev},${prevCnt}`; + if (key in dp) return dp[key]; + + let res; + if (prev === s.charCodeAt(i) - 97) { + const incr = prevCnt === 1 || prevCnt === 9 || prevCnt === 99 ? 1 : 0; + res = incr + count(i + 1, k, prev, prevCnt + 1); + } else { + res = 1 + count(i + 1, k, s.charCodeAt(i) - 97, 1); // don't delete + if (k > 0) { + res = Math.min(res, count(i + 1, k - 1, prev, prevCnt)); // delete s[i] + } + } + + dp[key] = res; + return res; + }; + + return count(0, k, 26, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(k * n ^ 2)$ +* Space complexity: $O(k * n ^ 2)$ + +> Where $n$ is the length of the string $s$ and $k$ is the maximum number of characters that can be deleted from the string. + +--- + +## 2. Dynamic Programming (Top-Down Optimized) + +::tabs-start + +```python +class Solution: + def getLengthOfOptimalCompression(self, s: str, k: int) -> int: + n = len(s) + dp = {} + + def dfs(i, k): + if n - i <= k: + return 0 + if (i, k) in dp: + return dp[(i, k)] + + res = 150 + if k > 0: + res = dfs(i + 1, k - 1) + + freq = delCnt = 0 + comp_len = 1 + for j in range(i, n): + if s[i] == s[j]: + if freq in [1, 9, 99]: + comp_len += 1 + freq += 1 + else: + delCnt += 1 + if delCnt > k: + break + res = min(res, comp_len + dfs(j + 1, k - delCnt)) + dp[(i, k)] = res + return res + + return dfs(0, k) +``` + +```java +public class Solution { + private int n; + private int[][] dp; + + public int getLengthOfOptimalCompression(String s, int k) { + n = s.length(); + dp = new int[n + 1][k + 1]; + for (int[] row : dp) Arrays.fill(row, -1); + return dfs(0, k, s); + } + + private int dfs(int i, int k, String s) { + if (n - i <= k) return 0; + if (dp[i][k] != -1) return dp[i][k]; + + int res = 150; + if (k > 0) res = dfs(i + 1, k - 1, s); + + int freq = 0, delCnt = 0, comp_len = 1; + for (int j = i; j < n; j++) { + if (s.charAt(i) == s.charAt(j)) { + if (freq == 1 || freq == 9 || freq == 99) comp_len++; + freq++; + } else { + delCnt++; + if (delCnt > k) break; + } + res = Math.min(res, comp_len + dfs(j + 1, k - delCnt, s)); + } + dp[i][k] = res; + return res; + } +} +``` + +```cpp +class Solution { +private: + int n; + vector> dp; + + int dfs(int i, int k, const string& s) { + if (n - i <= k) return 0; + if (dp[i][k] != -1) return dp[i][k]; + + int res = 150; + if (k > 0) res = dfs(i + 1, k - 1, s); + + int freq = 0, delCnt = 0, comp_len = 1; + for (int j = i; j < n; j++) { + if (s[i] == s[j]) { + if (freq == 1 || freq == 9 || freq == 99) comp_len++; + freq++; + } else { + delCnt++; + if (delCnt > k) break; + } + res = min(res, comp_len + dfs(j + 1, k - delCnt, s)); + } + dp[i][k] = res; + return res; + } + +public: + int getLengthOfOptimalCompression(string s, int k) { + n = s.size(); + dp = vector>(n + 1, vector(k + 1, -1)); + return dfs(0, k, s); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + getLengthOfOptimalCompression(s, k) { + const n = s.length; + const dp = Array.from({ length: n + 1 }, () => Array(k + 1).fill(-1)); + + const dfs = (i, k) => { + if (n - i <= k) return 0; + if (dp[i][k] !== -1) return dp[i][k]; + + let res = 150; + if (k > 0) res = dfs(i + 1, k - 1); + + let freq = 0, delCnt = 0, comp_len = 1; + for (let j = i; j < n; j++) { + if (s[i] === s[j]) { + if (freq === 1 || freq === 9 || freq === 99) comp_len++; + freq++; + } else { + delCnt++; + if (delCnt > k) break; + } + res = Math.min(res, comp_len + dfs(j + 1, k - delCnt)); + } + dp[i][k] = res; + return res; + }; + + return dfs(0, k); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ is the length of the string $s$ and $k$ is the maximum number of characters that can be deleted from the string. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def getLengthOfOptimalCompression(self, s: str, k: int) -> int: + n = len(s) + dp = [[150] * (k + 1) for _ in range(n)] + dp.append([0] * (k + 1)) + + for i in range(n - 1, -1, -1): + for rem_k in range(k + 1): + if rem_k > 0: + dp[i][rem_k] = dp[i + 1][rem_k - 1] + + freq = delCnt = 0 + comp_len = 1 + for j in range(i, n): + if s[i] == s[j]: + if freq in [1, 9, 99]: + comp_len += 1 + freq += 1 + else: + delCnt += 1 + if delCnt > rem_k: + break + dp[i][rem_k] = min(dp[i][rem_k], comp_len + dp[j + 1][rem_k - delCnt]) + + return dp[0][k] +``` + +```java +public class Solution { + public int getLengthOfOptimalCompression(String s, int k) { + int n = s.length(); + int[][] dp = new int[n + 1][k + 1]; + + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= k; j++) { + dp[i][j] = 150; + } + } + + for (int remK = 0; remK <= k; remK++) { + dp[n][remK] = 0; + } + + for (int i = n - 1; i >= 0; i--) { + for (int remK = 0; remK <= k; remK++) { + if (remK > 0) { + dp[i][remK] = dp[i + 1][remK - 1]; + } + + int freq = 0, delCnt = 0, compLen = 1; + for (int j = i; j < n; j++) { + if (s.charAt(i) == s.charAt(j)) { + if (freq == 1 || freq == 9 || freq == 99) { + compLen++; + } + freq++; + } else { + delCnt++; + if (delCnt > remK) break; + } + dp[i][remK] = Math.min(dp[i][remK], compLen + dp[j + 1][remK - delCnt]); + } + } + } + + return dp[0][k]; + } +} +``` + +```cpp +class Solution { +public: + int getLengthOfOptimalCompression(string s, int k) { + int n = s.size(); + vector> dp(n + 1, vector(k + 1, 150)); + + for (int remK = 0; remK <= k; remK++) { + dp[n][remK] = 0; + } + + for (int i = n - 1; i >= 0; i--) { + for (int remK = 0; remK <= k; remK++) { + if (remK > 0) { + dp[i][remK] = dp[i + 1][remK - 1]; + } + + int freq = 0, delCnt = 0, compLen = 1; + for (int j = i; j < n; j++) { + if (s[i] == s[j]) { + if (freq == 1 || freq == 9 || freq == 99) { + compLen++; + } + freq++; + } else { + delCnt++; + if (delCnt > remK) break; + } + dp[i][remK] = min(dp[i][remK], compLen + dp[j + 1][remK - delCnt]); + } + } + } + + return dp[0][k]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} k + * @return {number} + */ + getLengthOfOptimalCompression(s, k) { + const n = s.length; + const dp = Array.from({ length: n + 1 }, () => Array(k + 1).fill(150)); + + for (let remK = 0; remK <= k; remK++) { + dp[n][remK] = 0; + } + + for (let i = n - 1; i >= 0; i--) { + for (let remK = 0; remK <= k; remK++) { + if (remK > 0) { + dp[i][remK] = dp[i + 1][remK - 1]; + } + + let freq = 0, delCnt = 0, compLen = 1; + for (let j = i; j < n; j++) { + if (s[i] === s[j]) { + if (freq === 1 || freq === 9 || freq === 99) { + compLen++; + } + freq++; + } else { + delCnt++; + if (delCnt > remK) break; + } + dp[i][remK] = Math.min(dp[i][remK], compLen + dp[j + 1][remK - delCnt]); + } + } + } + + return dp[0][k]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * k)$ +* Space complexity: $O(n * k)$ + +> Where $n$ is the length of the string $s$ and $k$ is the maximum number of characters that can be deleted from the string. \ No newline at end of file diff --git a/articles/string-encode-and-decode.md b/articles/string-encode-and-decode.md new file mode 100644 index 000000000..ca00a45b8 --- /dev/null +++ b/articles/string-encode-and-decode.md @@ -0,0 +1,601 @@ +## 1. Encoding & Decoding + +::tabs-start + +```python +class Solution: + def encode(self, strs: List[str]) -> str: + if not strs: + return "" + sizes, res = [], "" + for s in strs: + sizes.append(len(s)) + for sz in sizes: + res += str(sz) + res += ',' + res += '#' + for s in strs: + res += s + return res + + def decode(self, s: str) -> List[str]: + if not s: + return [] + sizes, res, i = [], [], 0 + while s[i] != '#': + cur = "" + while s[i] != ',': + cur += s[i] + i += 1 + sizes.append(int(cur)) + i += 1 + i += 1 + for sz in sizes: + res.append(s[i:i + sz]) + i += sz + return res +``` + +```java +public class Solution { + + public String encode(List strs) { + if (strs.isEmpty()) return ""; + StringBuilder res = new StringBuilder(); + List sizes = new ArrayList<>(); + for (String str : strs) { + sizes.add(str.length()); + } + for (int size : sizes) { + res.append(size).append(','); + } + res.append('#'); + for (String str : strs) { + res.append(str); + } + return res.toString(); + } + + public List decode(String str) { + if (str.length() == 0) { + return new ArrayList<>(); + } + List res = new ArrayList<>(); + List sizes = new ArrayList<>(); + int i = 0; + while (str.charAt(i) != '#') { + StringBuilder cur = new StringBuilder(); + while (str.charAt(i) != ',') { + cur.append(str.charAt(i)); + i++; + } + sizes.add(Integer.parseInt(cur.toString())); + i++; + } + i++; + for (int sz : sizes) { + res.add(str.substring(i, i + sz)); + i += sz; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + string encode(vector& strs) { + if (strs.empty()) return ""; + vector sizes; + string res = ""; + for (string& s : strs) { + sizes.push_back(s.size()); + } + for (int sz : sizes) { + res += to_string(sz) + ','; + } + res += '#'; + for (string& s : strs) { + res += s; + } + return res; + } + + vector decode(string s) { + if (s.empty()) return {}; + vector sizes; + vector res; + int i = 0; + while (s[i] != '#') { + string cur = ""; + while (s[i] != ',') { + cur += s[i]; + i++; + } + sizes.push_back(stoi(cur)); + i++; + } + i++; + for (int sz : sizes) { + res.push_back(s.substr(i, sz)); + i += sz; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} strs + * @returns {string} + */ + encode(strs) { + if (strs.length === 0) return ""; + let sizes = [], res = ""; + for (let s of strs) { + sizes.push(s.length); + } + for (let sz of sizes) { + res += sz + ','; + } + res += '#'; + for (let s of strs) { + res += s; + } + return res; + } + + /** + * @param {string} str + * @returns {string[]} + */ + decode(str) { + if (str.length === 0) return []; + let sizes = [], res = [], i = 0; + while (str[i] !== '#') { + let cur = ""; + while (str[i] !== ',') { + cur += str[i]; + i++; + } + sizes.push(parseInt(cur)); + i++; + } + i++; + for (let sz of sizes) { + res.push(str.substr(i, sz)); + i += sz; + } + return res; + } +} +``` + +```csharp +public class Solution { + + public string Encode(IList strs) { + if (strs.Count == 0) return ""; + List sizes = new List(); + string res = ""; + foreach (string s in strs) { + sizes.Add(s.Length); + } + foreach (int sz in sizes) { + res += sz.ToString() + ','; + } + res += '#'; + foreach (string s in strs) { + res += s; + } + return res; + } + + public List Decode(string s) { + if (s.Length == 0) { + return new List(); + } + List sizes = new List(); + List res = new List(); + int i = 0; + while (s[i] != '#') { + string cur = ""; + while (s[i] != ',') { + cur += s[i]; + i++; + } + sizes.Add(int.Parse(cur)); + i++; + } + i++; + foreach (int sz in sizes) { + res.Add(s.Substring(i, sz)); + i += sz; + } + return res; + } +} +``` + +```go +type Solution struct{} + +func (s *Solution) Encode(strs []string) string { + if len(strs) == 0 { + return "" + } + var sizes []string + for _, str := range strs { + sizes = append(sizes, strconv.Itoa(len(str))) + } + return strings.Join(sizes, ",") + "#" + strings.Join(strs, "") +} + +func (s *Solution) Decode(encoded string) []string { + if encoded == "" { + return []string{} + } + parts := strings.SplitN(encoded, "#", 2) + sizes := strings.Split(parts[0], ",") + var res []string + i := 0 + for _, sz := range sizes { + if sz == "" { + continue + } + length, _ := strconv.Atoi(sz) + res = append(res, parts[1][i:i+length]) + i += length + } + return res +} +``` + +```kotlin +class Solution { + fun encode(strs: List): String { + if (strs.isEmpty()) return "" + val sizes = mutableListOf() + for (str in strs) { + sizes.add(str.length.toString()) + } + return sizes.joinToString(",") + "#" + strs.joinToString("") + } + + fun decode(encoded: String): List { + if (encoded.isEmpty()) return emptyList() + val parts = encoded.split("#", limit = 2) + val sizes = parts[0].split(",") + val res = mutableListOf() + var i = 0 + for (sz in sizes) { + if (sz.isEmpty()) continue + val length = sz.toInt() + res.add(parts[1].substring(i, i + length)) + i += length + } + return res + } +} +``` + +```swift +class Solution { + func encode(_ strs: [String]) -> String { + if strs.isEmpty { return "" } + + var sizes: [Int] = [] + var res = "" + for s in strs { + sizes.append(s.count) + } + for sz in sizes { + res += String(sz) + res += "," + } + + res += "#" + for s in strs { + res += s + } + return res + } + + func decode(_ s: String) -> [String] { + if s.isEmpty { return [] } + let sArr = Array(s) + var sizes: [Int] = [] + var res: [String] = [] + var i = 0 + + while sArr[i] != "#" { + var cur = "" + while sArr[i] != "," { + cur.append(sArr[i]) + i += 1 + } + sizes.append(Int(cur)!) + i += 1 + } + + i += 1 + for sz in sizes { + let substring = String(sArr[i.. Where $m$ is the sum of lengths of all the strings and $n$ is the number of strings. + +--- + +## 2. Encoding & Decoding (Optimal) + +::tabs-start + +```python +class Solution: + + def encode(self, strs: List[str]) -> str: + res = "" + for s in strs: + res += str(len(s)) + "#" + s + return res + + def decode(self, s: str) -> List[str]: + res = [] + i = 0 + + while i < len(s): + j = i + while s[j] != '#': + j += 1 + length = int(s[i:j]) + i = j + 1 + j = i + length + res.append(s[i:j]) + i = j + + return res +``` + +```java +public class Solution { + + public String encode(List strs) { + StringBuilder res = new StringBuilder(); + for (String s : strs) { + res.append(s.length()).append('#').append(s); + } + return res.toString(); + } + + public List decode(String str) { + List res = new ArrayList<>(); + int i = 0; + while (i < str.length()) { + int j = i; + while (str.charAt(j) != '#') { + j++; + } + int length = Integer.parseInt(str.substring(i, j)); + i = j + 1; + j = i + length; + res.add(str.substring(i, j)); + i = j; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + string encode(vector& strs) { + string res; + for (const string& s : strs) { + res += to_string(s.size()) + "#" + s; + } + return res; + } + + vector decode(string s) { + vector res; + int i = 0; + while (i < s.size()) { + int j = i; + while (s[j] != '#') { + j++; + } + int length = stoi(s.substr(i, j - i)); + i = j + 1; + j = i + length; + res.push_back(s.substr(i, length)); + i = j; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} strs + * @returns {string} + */ + encode(strs) { + let res = ""; + for (let s of strs) { + res += s.length + "#" + s; + } + return res; + } + + /** + * @param {string} str + * @returns {string[]} + */ + decode(str) { + let res = []; + let i = 0; + while (i < str.length) { + let j = i; + while (str[j] !== '#') { + j++; + } + let length = parseInt(str.substring(i, j)); + i = j + 1; + j = i + length; + res.push(str.substring(i, j)); + i = j; + } + return res; + } +} +``` + +```csharp +public class Solution { + public string Encode(IList strs) { + string res = ""; + foreach (string s in strs) { + res += s.Length + "#" + s; + } + return res; + } + + public List Decode(string s) { + List res = new List(); + int i = 0; + while (i < s.Length) { + int j = i; + while (s[j] != '#') { + j++; + } + int length = int.Parse(s.Substring(i, j - i)); + i = j + 1; + j = i + length; + res.Add(s.Substring(i, length)); + i = j; + } + return res; + } +} +``` + +```go +type Solution struct{} + +func (s *Solution) Encode(strs []string) string { + res := "" + for _, str := range strs { + res += strconv.Itoa(len(str)) + "#" + str + } + return res +} + +func (s *Solution) Decode(encoded string) []string { + res := []string{} + i := 0 + for i < len(encoded) { + j := i + for encoded[j] != '#' { + j++ + } + length, _ := strconv.Atoi(encoded[i:j]) + i = j + 1 + res = append(res, encoded[i:i+length]) + i += length + } + return res +} +``` + +```kotlin +class Solution { + + fun encode(strs: List): String { + val res = StringBuilder() + for (str in strs) { + res.append(str.length).append('#').append(str) + } + return res.toString() + } + + fun decode(encoded: String): List { + val res = mutableListOf() + var i = 0 + while (i < encoded.length) { + var j = i + while (encoded[j] != '#') { + j++ + } + val length = encoded.substring(i, j).toInt() + i = j + 1 + res.add(encoded.substring(i, i + length)) + i += length + } + return res + } +} +``` + +```swift +class Solution { + func encode(_ strs: [String]) -> String { + var res = "" + for s in strs { + res += "\(s.count)#\(s)" + } + return res + } + + func decode(_ s: String) -> [String] { + var res = [String]() + let sArr = Array(s) + var i = 0 + + while i < sArr.count { + var j = i + while sArr[j] != "#" { + j += 1 + } + let lengthStr = String(sArr[i.. Where $m$ is the sum of lengths of all the strings and $n$ is the number of strings. \ No newline at end of file diff --git a/articles/string-matching-in-an-array.md b/articles/string-matching-in-an-array.md new file mode 100644 index 000000000..f369be41c --- /dev/null +++ b/articles/string-matching-in-an-array.md @@ -0,0 +1,1157 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def stringMatching(self, words: List[str]) -> List[str]: + res = [] + + for i in range(len(words)): + for j in range(len(words)): + if i == j: + continue + + if words[i] in words[j]: + res.append(words[i]) + break + + return res +``` + +```java +public class Solution { + public List stringMatching(String[] words) { + List res = new ArrayList<>(); + + for (int i = 0; i < words.length; i++) { + for (int j = 0; j < words.length; j++) { + if (i == j) { + continue; + } + + if (words[j].contains(words[i])) { + res.add(words[i]); + break; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector stringMatching(vector& words) { + vector res; + + for (int i = 0; i < words.size(); i++) { + for (int j = 0; j < words.size(); j++) { + if (i == j) { + continue; + } + + if (words[j].find(words[i]) != string::npos) { + res.push_back(words[i]); + break; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + stringMatching(words) { + let res = []; + + for (let i = 0; i < words.length; i++) { + for (let j = 0; j < words.length; j++) { + if (i === j) { + continue; + } + + if (words[j].includes(words[i])) { + res.push(words[i]); + break; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * m ^ 2)$ +* Space complexity: + * $O(1)$ extra space. + * $O(n * m)$ space for the output list. + +> Where $n$ is the number of words, and $m$ is the length of the longest word. + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def stringMatching(self, words: List[str]) -> List[str]: + res = [] + words.sort(key=len) + + for i in range(len(words)): + for j in range(i + 1, len(words)): + if words[i] in words[j]: + res.append(words[i]) + break + + return res +``` + +```java +public class Solution { + public List stringMatching(String[] words) { + List res = new ArrayList<>(); + Arrays.sort(words, Comparator.comparingInt(String::length)); + + for (int i = 0; i < words.length; i++) { + for (int j = i + 1; j < words.length; j++) { + if (words[j].contains(words[i])) { + res.add(words[i]); + break; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector stringMatching(vector& words) { + vector res; + sort(words.begin(), words.end(), [](const string& a, const string& b) { + return a.length() < b.length(); + }); + + for (int i = 0; i < words.size(); i++) { + for (int j = i + 1; j < words.size(); j++) { + if (words[j].find(words[i]) != string::npos) { + res.push_back(words[i]); + break; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + stringMatching(words) { + let res = []; + words.sort((a, b) => a.length - b.length); + + for (let i = 0; i < words.length; i++) { + for (let j = i + 1; j < words.length; j++) { + if (words[j].includes(words[i])) { + res.push(words[i]); + break; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * m ^ 2)$ +* Space complexity: + * $O(1)$ or $O(n)$ depending on the sorting algorithm. + * $O(n * m)$ space for the output list. + +> Where $n$ is the number of words, and $m$ is the length of the longest word. + +--- + +## 3. Knuth-Morris-Pratt (KMP) Algorithm + +::tabs-start + +```python +class Solution: + def stringMatching(self, words: List[str]) -> List[str]: + def kmp(word1: str, word2: str) -> int: + lps = [0] * len(word2) + prevLPS, i = 0, 1 + + while i < len(word2): + if word2[i] == word2[prevLPS]: + lps[i] = prevLPS + 1 + prevLPS += 1 + i += 1 + elif prevLPS == 0: + lps[i] = 0 + i += 1 + else: + prevLPS = lps[prevLPS - 1] + + i = j = 0 + while i < len(word1): + if word1[i] == word2[j]: + i += 1 + j += 1 + else: + if j == 0: + i += 1 + else: + j = lps[j - 1] + + if j == len(word2): + return i - len(word2) + + return -1 + + res = [] + words.sort(key=len) + + for i in range(len(words)): + for j in range(i + 1, len(words)): + if kmp(words[j], words[i]) != -1: + res.append(words[i]) + break + + return res +``` + +```java +public class Solution { + public List stringMatching(String[] words) { + List res = new ArrayList<>(); + Arrays.sort(words, Comparator.comparingInt(String::length)); + + for (int i = 0; i < words.length; i++) { + for (int j = i + 1; j < words.length; j++) { + if (kmp(words[j], words[i]) != -1) { + res.add(words[i]); + break; + } + } + } + + return res; + } + + private int kmp(String word1, String word2) { + int[] lps = new int[word2.length()]; + int prevLPS = 0, i = 1; + + while (i < word2.length()) { + if (word2.charAt(i) == word2.charAt(prevLPS)) { + lps[i] = prevLPS + 1; + prevLPS++; + i++; + } else if (prevLPS == 0) { + lps[i] = 0; + i++; + } else { + prevLPS = lps[prevLPS - 1]; + } + } + + i = 0; + int j = 0; + while (i < word1.length()) { + if (word1.charAt(i) == word2.charAt(j)) { + i++; + j++; + } else { + if (j == 0) { + i++; + } else { + j = lps[j - 1]; + } + } + + if (j == word2.length()) { + return i - word2.length(); + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + vector stringMatching(vector& words) { + vector res; + sort(words.begin(), words.end(), [](const string& a, const string& b) { + return a.length() < b.length(); + }); + + for (int i = 0; i < words.size(); i++) { + for (int j = i + 1; j < words.size(); j++) { + if (kmp(words[j], words[i]) != -1) { + res.push_back(words[i]); + break; + } + } + } + + return res; + } + +private: + int kmp(const string& word1, const string& word2) { + vector lps(word2.size(), 0); + int prevLPS = 0, i = 1; + + while (i < word2.size()) { + if (word2[i] == word2[prevLPS]) { + lps[i++] = ++prevLPS; + } else if (prevLPS == 0) { + lps[i++] = 0; + } else { + prevLPS = lps[prevLPS - 1]; + } + } + + i = 0; + int j = 0; + while (i < word1.size()) { + if (word1[i] == word2[j]) { + i++; + j++; + } else { + if (j == 0) { + i++; + } else { + j = lps[j - 1]; + } + } + + if (j == word2.size()) { + return i - word2.size(); + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + stringMatching(words) { + const kmp = (word1, word2) => { + const lps = Array(word2.length).fill(0); + let prevLPS = 0, i = 1; + + while (i < word2.length) { + if (word2[i] === word2[prevLPS]) { + lps[i++] = ++prevLPS; + } else if (prevLPS === 0) { + lps[i++] = 0; + } else { + prevLPS = lps[prevLPS - 1]; + } + } + + i = 0; + let j = 0; + while (i < word1.length) { + if (word1[i] === word2[j]) { + i++; + j++; + } else { + if (j === 0) { + i++; + } else { + j = lps[j - 1]; + } + } + + if (j === word2.length) { + return i - word2.length; + } + } + + return -1; + }; + + let res = []; + words.sort((a, b) => a.length - b.length); + + for (let i = 0; i < words.length; i++) { + for (let j = i + 1; j < words.length; j++) { + if (kmp(words[j], words[i]) !== -1) { + res.push(words[i]); + break; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * m)$ +* Space complexity: + * $O(m)$ extra space. + * $O(1)$ or $O(n)$ space depending on the sorting algorithm. + * $O(n * m)$ space for the output list. + +> Where $n$ is the number of words, and $m$ is the length of the longest word. + +--- + +## 4. Rabin-Karp Algorithm (Rolling Hash) + +::tabs-start + +```python +class Solution: + def stringMatching(self, words: List[str]) -> List[str]: + def rabinKarp(word1: str, word2: str) -> int: + base1, mod1 = 31, 768258391 + base2, mod2 = 37, 685683731 + n, m = len(word1), len(word2) + + power1, power2 = 1, 1 + for _ in range(m): + power1 = (power1 * base1) % mod1 + power2 = (power2 * base2) % mod2 + + word1_hash1 = word1_hash2 = 0 + word2_hash1 = word2_hash2 = 0 + + for i in range(m): + word1_hash1 = (word1_hash1 * base1 + ord(word2[i])) % mod1 + word1_hash2 = (word1_hash2 * base2 + ord(word2[i])) % mod2 + word2_hash1 = (word2_hash1 * base1 + ord(word1[i])) % mod1 + word2_hash2 = (word2_hash2 * base2 + ord(word1[i])) % mod2 + + for i in range(n - m + 1): + if word2_hash1 == word1_hash1 and word2_hash2 == word1_hash2: + return i + + if i + m < n: + word2_hash1 = (word2_hash1 * base1 - ord(word1[i]) * power1 + ord(word1[i + m])) % mod1 + word2_hash2 = (word2_hash2 * base2 - ord(word1[i]) * power2 + ord(word1[i + m])) % mod2 + + word2_hash1 = (word2_hash1 + mod1) % mod1 + word2_hash2 = (word2_hash2 + mod2) % mod2 + + return -1 + + res = [] + words.sort(key=len) + + for i in range(len(words)): + for j in range(i + 1, len(words)): + if rabinKarp(words[j], words[i]) != -1: + res.append(words[i]) + break + + return res +``` + +```java +public class Solution { + public List stringMatching(String[] words) { + List res = new ArrayList<>(); + Arrays.sort(words, Comparator.comparingInt(String::length)); + + for (int i = 0; i < words.length; i++) { + for (int j = i + 1; j < words.length; j++) { + if (rabinKarp(words[j], words[i]) != -1) { + res.add(words[i]); + break; + } + } + } + + return res; + } + + private int rabinKarp(String word1, String word2) { + int base1 = 31, mod1 = 768258391; + int base2 = 37, mod2 = 685683731; + int n = word1.length(), m = word2.length(); + + long power1 = 1, power2 = 1; + for (int k = 0; k < m; k++) { + power1 = (power1 * base1) % mod1; + power2 = (power2 * base2) % mod2; + } + + long word1Hash1 = 0, word1Hash2 = 0; + long word2Hash1 = 0, word2Hash2 = 0; + + for (int i = 0; i < m; i++) { + word1Hash1 = (word1Hash1 * base1 + word2.charAt(i)) % mod1; + word1Hash2 = (word1Hash2 * base2 + word2.charAt(i)) % mod2; + word2Hash1 = (word2Hash1 * base1 + word1.charAt(i)) % mod1; + word2Hash2 = (word2Hash2 * base2 + word1.charAt(i)) % mod2; + } + + for (int i = 0; i <= n - m; i++) { + if (word2Hash1 == word1Hash1 && word2Hash2 == word1Hash2) { + return i; + } + + if (i + m < n) { + word2Hash1 = (word2Hash1 * base1 - word1.charAt(i) * power1 + word1.charAt(i + m)) % mod1; + word2Hash2 = (word2Hash2 * base2 - word1.charAt(i) * power2 + word1.charAt(i + m)) % mod2; + + if (word2Hash1 < 0) word2Hash1 += mod1; + if (word2Hash2 < 0) word2Hash2 += mod2; + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + vector stringMatching(vector& words) { + vector res; + sort(words.begin(), words.end(), [](const string& a, const string& b) { + return a.length() < b.length(); + }); + + for (int i = 0; i < words.size(); i++) { + for (int j = i + 1; j < words.size(); j++) { + if (rabinKarp(words[j], words[i]) != -1) { + res.push_back(words[i]); + break; + } + } + } + + return res; + } + +private: + int rabinKarp(const string& word1, const string& word2) { + int base1 = 31, mod1 = 768258391; + int base2 = 37, mod2 = 685683731; + int n = word1.size(), m = word2.size(); + + long long power1 = 1, power2 = 1; + for (int i = 0; i < m; i++) { + power1 = (power1 * base1) % mod1; + power2 = (power2 * base2) % mod2; + } + + long long word1Hash1 = 0, word1Hash2 = 0; + long long word2Hash1 = 0, word2Hash2 = 0; + + for (int i = 0; i < m; i++) { + word1Hash1 = (word1Hash1 * base1 + word2[i]) % mod1; + word1Hash2 = (word1Hash2 * base2 + word2[i]) % mod2; + word2Hash1 = (word2Hash1 * base1 + word1[i]) % mod1; + word2Hash2 = (word2Hash2 * base2 + word1[i]) % mod2; + } + + for (int i = 0; i <= n - m; i++) { + if (word2Hash1 == word1Hash1 && word2Hash2 == word1Hash2) { + return i; + } + + if (i + m < n) { + word2Hash1 = (word2Hash1 * base1 - word1[i] * power1 + word1[i + m]) % mod1; + word2Hash2 = (word2Hash2 * base2 - word1[i] * power2 + word1[i + m]) % mod2; + + if (word2Hash1 < 0) word2Hash1 += mod1; + if (word2Hash2 < 0) word2Hash2 += mod2; + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + stringMatching(words) { + const rabinKarp = (word1, word2) => { + const base1 = 31, mod1 = 768258391; + const base2 = 37, mod2 = 685683731; + const n = word1.length, m = word2.length; + + let power1 = 1, power2 = 1; + for (let k = 0; k < m; k++) { + power1 = (power1 * base1) % mod1; + power2 = (power2 * base2) % mod2; + } + + let hash1 = 0, hash2 = 0; + let cur1 = 0, cur2 = 0; + + for (let i = 0; i < m; i++) { + hash1 = (hash1 * base1 + word2.charCodeAt(i)) % mod1; + hash2 = (hash2 * base2 + word2.charCodeAt(i)) % mod2; + cur1 = (cur1 * base1 + word1.charCodeAt(i)) % mod1; + cur2 = (cur2 * base2 + word1.charCodeAt(i)) % mod2; + } + + for (let i = 0; i <= n - m; i++) { + if (cur1 === hash1 && cur2 === hash2) { + return i; + } + + if (i + m < n) { + cur1 = (cur1 * base1 - word1.charCodeAt(i) * power1 + word1.charCodeAt(i + m)) % mod1; + cur2 = (cur2 * base2 - word1.charCodeAt(i) * power2 + word1.charCodeAt(i + m)) % mod2; + + cur1 = (cur1 + mod1) % mod1; + cur2 = (cur2 + mod2) % mod2; + } + } + + return -1; + }; + + words.sort((a, b) => a.length - b.length); + let res = []; + + for (let i = 0; i < words.length; i++) { + for (let j = i + 1; j < words.length; j++) { + if (rabinKarp(words[j], words[i]) !== -1) { + res.push(words[i]); + break; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * m)$ +* Space complexity: + * $O(1)$ or $O(n)$ space depending on the sorting algorithm. + * $O(n * m)$ space for the output list. + +> Where $n$ is the number of words, and $m$ is the length of the longest word. + +--- + +## 5. Z-Algorithm + +::tabs-start + +```python +class Solution: + def stringMatching(self, words: List[str]) -> List[str]: + def zAlgorithm(word1: str, word2: str) -> int: + s = word2 + "$" + word1 + n = len(s) + z = [0] * n + l, r = 0, 0 + + for i in range(1, n): + if i <= r: + z[i] = min(r - i + 1, z[i - l]) + while i + z[i] < n and s[z[i]] == s[i + z[i]]: + z[i] += 1 + if i + z[i] - 1 > r: + l, r = i, i + z[i] - 1 + + for i in range(len(word2) + 1, n): + if z[i] == len(word2): + return i - len(word2) - 1 + + return -1 + + res = [] + words.sort(key=len) + + for i in range(len(words)): + for j in range(i + 1, len(words)): + if zAlgorithm(words[j], words[i]) != -1: + res.append(words[i]) + break + + return res +``` + +```java +public class Solution { + public List stringMatching(String[] words) { + List res = new ArrayList<>(); + Arrays.sort(words, Comparator.comparingInt(String::length)); + + for (int i = 0; i < words.length; i++) { + for (int j = i + 1; j < words.length; j++) { + if (zAlgorithm(words[j], words[i]) != -1) { + res.add(words[i]); + break; + } + } + } + + return res; + } + + private int zAlgorithm(String word1, String word2) { + String s = word2 + "$" + word1; + int n = s.length(); + int[] z = new int[n]; + int l = 0, r = 0; + + for (int i = 1; i < n; i++) { + if (i <= r) { + z[i] = Math.min(r - i + 1, z[i - l]); + } + while (i + z[i] < n && s.charAt(z[i]) == s.charAt(i + z[i])) { + z[i]++; + } + if (i + z[i] - 1 > r) { + l = i; + r = i + z[i] - 1; + } + } + + for (int i = word2.length() + 1; i < n; i++) { + if (z[i] == word2.length()) { + return i - word2.length() - 1; + } + } + + return -1; + } +} +``` + +```cpp +class Solution { +public: + vector stringMatching(vector& words) { + vector res; + sort(words.begin(), words.end(), [](const string& a, const string& b) { + return a.length() < b.length(); + }); + + for (int i = 0; i < words.size(); i++) { + for (int j = i + 1; j < words.size(); j++) { + if (zAlgorithm(words[j], words[i]) != -1) { + res.push_back(words[i]); + break; + } + } + } + + return res; + } + +private: + int zAlgorithm(const string& word1, const string& word2) { + string s = word2 + "$" + word1; + int n = s.size(); + vector z(n, 0); + int l = 0, r = 0; + + for (int i = 1; i < n; i++) { + if (i <= r) { + z[i] = min(r - i + 1, z[i - l]); + } + while (i + z[i] < n && s[z[i]] == s[i + z[i]]) { + z[i]++; + } + if (i + z[i] - 1 > r) { + l = i; + r = i + z[i] - 1; + } + } + + for (int i = word2.size() + 1; i < n; i++) { + if (z[i] == word2.size()) { + return i - word2.size() - 1; + } + } + + return -1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + stringMatching(words) { + const zAlgorithm = (word1, word2) => { + const s = word2 + "$" + word1; + const n = s.length; + const z = Array(n).fill(0); + let l = 0, r = 0; + + for (let i = 1; i < n; i++) { + if (i <= r) { + z[i] = Math.min(r - i + 1, z[i - l]); + } + while (i + z[i] < n && s[z[i]] === s[i + z[i]]) { + z[i]++; + } + if (i + z[i] - 1 > r) { + l = i; + r = i + z[i] - 1; + } + } + + for (let i = word2.length + 1; i < n; i++) { + if (z[i] === word2.length) { + return i - word2.length - 1; + } + } + + return -1; + }; + + words.sort((a, b) => a.length - b.length); + let res = []; + + for (let i = 0; i < words.length; i++) { + for (let j = i + 1; j < words.length; j++) { + if (zAlgorithm(words[j], words[i]) !== -1) { + res.push(words[i]); + break; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 * m)$ +* Space complexity: + * $O(m)$ extra space. + * $O(1)$ or $O(n)$ space depending on the sorting algorithm. + * $O(n * m)$ space for the output list. + +> Where $n$ is the number of words, and $m$ is the length of the longest word. + +--- + +## 6. Trie + +::tabs-start + +```python +class TrieNode: + def __init__(self): + self.children = [None] * 26 + self.cnt = 0 + +class Trie: + def __init__(self): + self.root = TrieNode() + + def insert_suffixes(self, word: str) -> None: + for i in range(len(word)): + node = self.root + for j in range(i, len(word)): + idx = ord(word[j]) - ord('a') + if not node.children[idx]: + node.children[idx] = TrieNode() + + node = node.children[idx] + node.cnt += 1 + + def search(self, word: str) -> bool: + node = self.root + for c in word: + idx = ord(c) - ord('a') + node = node.children[idx] + return node.cnt > 1 + +class Solution: + def stringMatching(self, words: List[str]) -> List[str]: + res = [] + trie = Trie() + + for word in words: + trie.insert_suffixes(word) + + for word in words: + if trie.search(word): + res.append(word) + + return res +``` + +```java +class TrieNode { + TrieNode[] children; + int cnt; + + TrieNode() { + children = new TrieNode[26]; + cnt = 0; + } +} + +class Trie { + TrieNode root; + + Trie() { + root = new TrieNode(); + } + + void insertSuffixes(String word) { + for (int i = 0; i < word.length(); i++) { + TrieNode node = root; + for (int j = i; j < word.length(); j++) { + int idx = word.charAt(j) - 'a'; + if (node.children[idx] == null) { + node.children[idx] = new TrieNode(); + } + + node = node.children[idx]; + node.cnt++; + } + } + } + + boolean search(String word) { + TrieNode node = root; + for (int i = 0; i < word.length(); i++) { + int idx = word.charAt(i) - 'a'; + node = node.children[idx]; + } + return node.cnt > 1; + } +} + +class Solution { + public List stringMatching(String[] words) { + List res = new ArrayList<>(); + Trie trie = new Trie(); + + for (String word : words) { + trie.insertSuffixes(word); + } + + for (String word : words) { + if (trie.search(word)) { + res.add(word); + } + } + + return res; + } +} +``` + +```cpp +class TrieNode { +public: + TrieNode* children[26]; + int cnt; + + TrieNode() { + for (int i = 0; i < 26; i++) children[i] = nullptr; + cnt = 0; + } +}; + +class Trie { +public: + TrieNode* root; + + Trie() { + root = new TrieNode(); + } + + void insertSuffixes(const string& word) { + for (int i = 0; i < word.size(); i++) { + TrieNode* node = root; + for (int j = i; j < word.size(); j++) { + int idx = word[j] - 'a'; + if (!node->children[idx]) { + node->children[idx] = new TrieNode(); + } + + node = node->children[idx]; + node->cnt++; + } + } + } + + bool search(const string& word) { + TrieNode* node = root; + for (char c : word) { + int idx = c - 'a'; + node = node->children[idx]; + } + return node->cnt > 1; + } +}; + +class Solution { +public: + vector stringMatching(vector& words) { + vector res; + Trie trie; + + for (const string& word : words) { + trie.insertSuffixes(word); + } + + for (const string& word : words) { + if (trie.search(word)) { + res.push_back(word); + } + } + + return res; + } +}; +``` + +```javascript +class TrieNode { + constructor() { + this.children = new Array(26).fill(null); + this.cnt = 0; + } +} + +class Trie { + constructor() { + this.root = new TrieNode(); + } + + /** + * @param {string} word + * @return {void} + */ + insertSuffixes(word) { + for (let i = 0; i < word.length; i++) { + let node = this.root; + for (let j = i; j < word.length; j++) { + let idx = word.charCodeAt(j) - 97; + if (!node.children[idx]) { + node.children[idx] = new TrieNode(); + } + + node = node.children[idx]; + node.cnt++; + } + } + } + + /** + * @param {string} word + * @return {boolean} + */ + search(word) { + let node = this.root; + for (let i = 0; i < word.length; i++) { + let idx = word.charCodeAt(i) - 97; + node = node.children[idx]; + } + return node.cnt > 1; + } +} + +class Solution { + /** + * @param {string[]} words + * @return {string[]} + */ + stringMatching(words) { + const res = []; + const trie = new Trie(); + + for (let word of words) { + trie.insertSuffixes(word); + } + + for (let word of words) { + if (trie.search(word)) { + res.push(word); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m ^ 2)$ +* Space complexity: + * $O(n * m ^ 2)$ extra space. + * $O(n * m)$ space for the output list. + +> Where $n$ is the number of words, and $m$ is the length of the longest word. \ No newline at end of file diff --git a/articles/student-attendance-record-ii.md b/articles/student-attendance-record-ii.md new file mode 100644 index 000000000..b941f3076 --- /dev/null +++ b/articles/student-attendance-record-ii.md @@ -0,0 +1,854 @@ +## 1. Dynamic Programming (Top-Down) - I + +::tabs-start + +```python +class Solution: + def checkRecord(self, n: int) -> int: + MOD = 1000000007 + cache = [[[-1 for _ in range(3)] for _ in range(2)] for _ in range(n + 1)] + + def dfs(i, cntA, cntL): + if i == 0: + return 1 + if cache[i][cntA][cntL] != -1: + return cache[i][cntA][cntL] + + res = dfs(i - 1, cntA, 0) % MOD + + if cntA == 0: + res = (res + dfs(i - 1, 1, 0)) % MOD + + if cntL < 2: + res = (res + dfs(i - 1, cntA, cntL + 1)) % MOD + + cache[i][cntA][cntL] = res + return res + + return dfs(n, 0, 0) +``` + +```java +public class Solution { + private static final int MOD = 1000000007; + private int[][][] cache; + + public int checkRecord(int n) { + this.cache = new int[n + 1][2][3]; + for (int[][] matrix : cache) { + for (int[] row : matrix) { + Arrays.fill(row, -1); + } + } + return dfs(n, 0, 0); + } + + private int dfs(int i, int cntA, int cntL) { + if (i == 0) { + return 1; + } + if (cache[i][cntA][cntL] != -1) { + return cache[i][cntA][cntL]; + } + + int res = dfs(i - 1, cntA, 0) % MOD; + + if (cntA == 0) { + res = (res + dfs(i - 1, 1, 0)) % MOD; + } + + if (cntL < 2) { + res = (res + dfs(i - 1, cntA, cntL + 1)) % MOD; + } + + return cache[i][cntA][cntL] = res; + } +} +``` + +```cpp +class Solution { + const int MOD = 1000000007; + vector>> cache; + +public: + int checkRecord(int n) { + cache.assign(n + 1, vector>(2, vector(3, -1))); + return dfs(n, 0, 0); + } + +private: + int dfs(int i, int cntA, int cntL) { + if (i == 0) { + return 1; + } + if (cache[i][cntA][cntL] != -1) { + return cache[i][cntA][cntL]; + } + + int res = dfs(i - 1, cntA, 0) % MOD; + + if (cntA == 0) { + res = (res + dfs(i - 1, 1, 0)) % MOD; + } + + if (cntL < 2) { + res = (res + dfs(i - 1, cntA, cntL + 1)) % MOD; + } + + return cache[i][cntA][cntL] = res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + checkRecord(n) { + const MOD = 1000000007; + let cache = Array.from({ length: n + 1 }, () => + Array.from({ length: 2 }, () => new Array(3).fill(-1)) + ); + + const dfs = (i, cntA, cntL) => { + if (i === 0) return 1; + if (cache[i][cntA][cntL] !== -1) return cache[i][cntA][cntL]; + + let res = dfs(i - 1, cntA, 0) % MOD; + + if (cntA === 0) { + res = (res + dfs(i - 1, 1, 0)) % MOD; + } + + if (cntL < 2) { + res = (res + dfs(i - 1, cntA, cntL + 1)) % MOD; + } + + return (cache[i][cntA][cntL] = res); + }; + + return dfs(n, 0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) - II + +::tabs-start + +```python +class Solution: + def checkRecord(self, n: int) -> int: + MOD = 10**9 + 7 + cache = {} + + def count(n): + if n == 1: + # (A, L) + return { + (0, 0): 1, (0, 1): 1, (0, 2): 0, + (1, 0): 1, (1, 1): 0, (1, 2): 0 + } + + if n in cache: + return cache[n] + + tmp = count(n - 1) + res = defaultdict(int) + + # Choose P + res[(0, 0)] = ((tmp[(0, 0)] + tmp[(0, 1)]) % MOD + tmp[(0, 2)]) % MOD + res[(1, 0)] = ((tmp[(1, 0)] + tmp[(1, 1)]) % MOD + tmp[(1, 2)]) % MOD + + # Choose L + res[(0, 1)] = tmp[(0, 0)] + res[(0, 2)] = tmp[(0, 1)] + res[(1, 1)] = tmp[(1, 0)] + res[(1, 2)] = tmp[(1, 1)] + + # Choose A + res[(1, 0)] += ((tmp[(0, 0)] + tmp[(0, 1)]) % MOD + tmp[(0, 2)]) % MOD + + cache[n] = res + return res + + return sum(count(n).values()) % MOD +``` + +```java +public class Solution { + private static final int MOD = 1000000007; + private int[][][] cache; + private int[][] baseCase; + + public int checkRecord(int n) { + cache = new int[n + 1][2][3]; + baseCase = new int[][]{{1, 1, 0}, {1, 0, 0}}; + for (int[][] matrix : cache) { + for (int[] row : matrix) { + Arrays.fill(row, -1); + } + } + int[][] result = count(n); + int total = 0; + for (int[] row : result) { + for (int val : row) { + total = (total + val) % MOD; + } + } + return total; + } + + private int[][] count(int n) { + if (n == 1) { + // (A, L) + return baseCase; + } + + if (cache[n][0][0] != -1) { + return cache[n]; + } + + int[][] prev = count(n - 1); + int[][] res = cache[n]; + + // Choose P + res[0][0] = ((prev[0][0] + prev[0][1]) % MOD + prev[0][2]) % MOD; + res[1][0] = ((prev[1][0] + prev[1][1]) % MOD + prev[1][2]) % MOD; + + // Choose L + res[0][1] = prev[0][0]; + res[0][2] = prev[0][1]; + res[1][1] = prev[1][0]; + res[1][2] = prev[1][1]; + + // Choose A + res[1][0] = (res[1][0] + ((prev[0][0] + prev[0][1]) % MOD + prev[0][2]) % MOD) % MOD; + + return cache[n]; + } +} +``` + +```cpp +class Solution { +private: + static constexpr int MOD = 1000000007; + vector> baseCase = {{1, 1, 0}, {1, 0, 0}}; + vector>> cache; + +public: + int checkRecord(int n) { + cache.assign(n + 1, vector>(2, vector(3, -1))); + const vector>& result = count(n); + int total = 0; + for (const auto& row : result) { + for (int val : row) { + total = (total + val) % MOD; + } + } + return total; + } + +private: + const vector>& count(int n) { + if (n == 1) { + return baseCase; + } + + if (cache[n][0][0] != -1) { + return cache[n]; + } + + const vector>& prev = count(n - 1); + auto& res = cache[n]; + + // Choose P + res[0][0] = ((prev[0][0] + prev[0][1]) % MOD + prev[0][2]) % MOD; + res[1][0] = ((prev[1][0] + prev[1][1]) % MOD + prev[1][2]) % MOD; + + // Choose L + res[0][1] = prev[0][0]; + res[0][2] = prev[0][1]; + res[1][1] = prev[1][0]; + res[1][2] = prev[1][1]; + + // Choose A + res[1][0] = (res[1][0] + ((prev[0][0] + prev[0][1]) % MOD + prev[0][2]) % MOD) % MOD; + + return cache[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + checkRecord(n) { + const MOD = 1000000007; + const baseCase = [ + [1, 1, 0], // (A = 0, L = 0, 1, 2) + [1, 0, 0] // (A = 1, L = 0, 1, 2) + ]; + let cache = Array.from({ length: n + 1 }, () => + Array.from({ length: 2 }, () => new Array(3).fill(-1)) + ); + + const count = (n) => { + if (n === 1) return baseCase; + if (cache[n][0][0] !== -1) return cache[n]; + + const prev = count(n - 1); + const res = cache[n]; + + // Choose P + res[0][0] = ((prev[0][0] + prev[0][1]) % MOD + prev[0][2]) % MOD; + res[1][0] = ((prev[1][0] + prev[1][1]) % MOD + prev[1][2]) % MOD; + + // Choose L + res[0][1] = prev[0][0]; + res[0][2] = prev[0][1]; + res[1][1] = prev[1][0]; + res[1][2] = prev[1][1]; + + // Choose A + res[1][0] = (res[1][0] + ((prev[0][0] + prev[0][1]) % MOD + prev[0][2]) % MOD) % MOD; + + return res; + }; + + const result = count(n); + let total = 0; + for (const row of result) { + for (const val of row) { + total = (total + val) % MOD; + } + } + return total; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def checkRecord(self, n: int) -> int: + MOD = 1000000007 + dp = [[[0 for _ in range(3)] for _ in range(2)] for _ in range(n + 1)] + + dp[0][0][0] = 1 # Base case + + for i in range(1, n + 1): + for cntA in range(2): + for cntL in range(3): + # Choose P + dp[i][cntA][0] = (dp[i][cntA][0] + dp[i - 1][cntA][cntL]) % MOD + + # Choose A + if cntA > 0: + dp[i][cntA][0] = (dp[i][cntA][0] + dp[i - 1][cntA - 1][cntL]) % MOD + + # Choose L + if cntL > 0: + dp[i][cntA][cntL] = (dp[i][cntA][cntL] + dp[i - 1][cntA][cntL - 1]) % MOD + + return sum(dp[n][cntA][cntL] for cntA in range(2) for cntL in range(3)) % MOD +``` + +```java +public class Solution { + public int checkRecord(int n) { + final int MOD = 1000000007; + int[][][] dp = new int[n + 1][2][3]; + + dp[0][0][0] = 1; + + for (int i = 1; i <= n; i++) { + for (int cntA = 0; cntA < 2; cntA++) { + for (int cntL = 0; cntL < 3; cntL++) { + // Choose P + dp[i][cntA][0] = (dp[i][cntA][0] + dp[i - 1][cntA][cntL]) % MOD; + + // Choose A + if (cntA > 0) { + dp[i][cntA][0] = (dp[i][cntA][0] + dp[i - 1][cntA - 1][cntL]) % MOD; + } + + // Choose L + if (cntL > 0) { + dp[i][cntA][cntL] = (dp[i][cntA][cntL] + dp[i - 1][cntA][cntL - 1]) % MOD; + } + } + } + } + + int result = 0; + for (int cntA = 0; cntA < 2; cntA++) { + for (int cntL = 0; cntL < 3; cntL++) { + result = (result + dp[n][cntA][cntL]) % MOD; + } + } + + return result; + } +} +``` + +```cpp +class Solution { +public: + int checkRecord(int n) { + const int MOD = 1000000007; + vector>> dp(n + 1, vector>(2, vector(3, 0))); + + dp[0][0][0] = 1; + + for (int i = 1; i <= n; i++) { + for (int cntA = 0; cntA < 2; cntA++) { + for (int cntL = 0; cntL < 3; cntL++) { + // Choose P + dp[i][cntA][0] = (dp[i][cntA][0] + dp[i - 1][cntA][cntL]) % MOD; + + // Choose A + if (cntA > 0) { + dp[i][cntA][0] = (dp[i][cntA][0] + dp[i - 1][cntA - 1][cntL]) % MOD; + } + + // Choose L + if (cntL > 0) { + dp[i][cntA][cntL] = (dp[i][cntA][cntL] + dp[i - 1][cntA][cntL - 1]) % MOD; + } + } + } + } + + int result = 0; + for (int cntA = 0; cntA < 2; cntA++) { + for (int cntL = 0; cntL < 3; cntL++) { + result = (result + dp[n][cntA][cntL]) % MOD; + } + } + + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + checkRecord(n) { + const MOD = 1000000007; + const dp = Array.from({ length: n + 1 }, () => + Array.from({ length: 2 }, () => new Array(3).fill(0)) + ); + + dp[0][0][0] = 1; + + for (let i = 1; i <= n; i++) { + for (let cntA = 0; cntA < 2; cntA++) { + for (let cntL = 0; cntL < 3; cntL++) { + // Choose P + dp[i][cntA][0] = (dp[i][cntA][0] + dp[i - 1][cntA][cntL]) % MOD; + + // Choose A + if (cntA > 0) { + dp[i][cntA][0] = (dp[i][cntA][0] + dp[i - 1][cntA - 1][cntL]) % MOD; + } + + // Choose L + if (cntL > 0) { + dp[i][cntA][cntL] = (dp[i][cntA][cntL] + dp[i - 1][cntA][cntL - 1]) % MOD; + } + } + } + } + + let result = 0; + for (let cntA = 0; cntA < 2; cntA++) { + for (let cntL = 0; cntL < 3; cntL++) { + result = (result + dp[n][cntA][cntL]) % MOD; + } + } + + return result; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) - I + +::tabs-start + +```python +class Solution: + def checkRecord(self, n: int) -> int: + if n == 1: + return 3 + + MOD = 10**9 + 7 + dp = { + (0, 0): 1, (0, 1): 1, (0, 2): 0, + (1, 0): 1, (1, 1): 0, (1, 2): 0 + } + + for i in range(n - 1): + ndp = defaultdict(int) + + # Choose P + ndp[(0, 0)] = ((dp[(0, 0)] + dp[(0, 1)]) % MOD + dp[(0, 2)]) % MOD + ndp[(1, 0)] = ((dp[(1, 0)] + dp[(1, 1)]) % MOD + dp[(1, 2)]) % MOD + + # Choose L + ndp[(0, 1)] = dp[(0, 0)] + ndp[(1, 1)] = dp[(1, 0)] + ndp[(0, 2)] = dp[(0, 1)] + ndp[(1, 2)] = dp[(1, 1)] + + # Choose A + ndp[(1, 0)] = (ndp[(1, 0)] + (((dp[(0, 0)] + dp[(0, 1)]) % MOD + dp[(0, 2)]) % MOD)) % MOD + + dp = ndp + + return sum(dp.values()) % MOD +``` + +```java +public class Solution { + public int checkRecord(int n) { + if (n == 1) return 3; + + final int MOD = 1000000007; + int[][] dp = {{1, 1, 0}, {1, 0, 0}}; + + for (int i = 0; i < n - 1; i++) { + int[][] ndp = new int[2][3]; + + // Choose P + ndp[0][0] = ((dp[0][0] + dp[0][1]) % MOD + dp[0][2]) % MOD; + ndp[1][0] = ((dp[1][0] + dp[1][1]) % MOD + dp[1][2]) % MOD; + + // Choose L + ndp[0][1] = dp[0][0]; + ndp[1][1] = dp[1][0]; + ndp[0][2] = dp[0][1]; + ndp[1][2] = dp[1][1]; + + // Choose A + ndp[1][0] = (ndp[1][0] + ((dp[0][0] + dp[0][1]) % MOD + dp[0][2]) % MOD) % MOD; + + dp = ndp; + } + + int total = 0; + for (int[] row : dp) { + for (int val : row) { + total = (total + val) % MOD; + } + } + return total; + } +} +``` + +```cpp +class Solution { +public: + int checkRecord(int n) { + if (n == 1) return 3; + + const int MOD = 1000000007; + vector> dp = {{1, 1, 0}, {1, 0, 0}}; + + for (int i = 0; i < n - 1; i++) { + vector> ndp(2, vector(3, 0)); + + // Choose P + ndp[0][0] = ((dp[0][0] + dp[0][1]) % MOD + dp[0][2]) % MOD; + ndp[1][0] = ((dp[1][0] + dp[1][1]) % MOD + dp[1][2]) % MOD; + + // Choose L + ndp[0][1] = dp[0][0]; + ndp[1][1] = dp[1][0]; + ndp[0][2] = dp[0][1]; + ndp[1][2] = dp[1][1]; + + // Choose A + ndp[1][0] = (ndp[1][0] + ((dp[0][0] + dp[0][1]) % MOD + dp[0][2]) % MOD) % MOD; + + swap(dp, ndp); + } + + int total = 0; + for (auto& row : dp) { + for (int val : row) { + total = (total + val) % MOD; + } + } + return total; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + checkRecord(n) { + if (n === 1) return 3; + + const MOD = 1000000007; + let dp = [[1, 1, 0], [1, 0, 0]]; + + for (let i = 0; i < n - 1; i++) { + let ndp = Array.from({ length: 2 }, () => new Array(3).fill(0)); + + // Choose P + ndp[0][0] = ((dp[0][0] + dp[0][1]) % MOD + dp[0][2]) % MOD; + ndp[1][0] = ((dp[1][0] + dp[1][1]) % MOD + dp[1][2]) % MOD; + + // Choose L + ndp[0][1] = dp[0][0]; + ndp[1][1] = dp[1][0]; + ndp[0][2] = dp[0][1]; + ndp[1][2] = dp[1][1]; + + // Choose A + ndp[1][0] = (ndp[1][0] + ((dp[0][0] + dp[0][1]) % MOD + dp[0][2]) % MOD) % MOD; + + [dp, ndp] = [ndp, dp]; + } + + let total = 0; + for (let row of dp) { + for (let val of row) { + total = (total + val) % MOD; + } + } + return total; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Dynamic Programming (Space Optimized) - II + +::tabs-start + +```python +class Solution: + def checkRecord(self, n: int) -> int: + MOD = 1000000007 + dp = [[0] * 3 for _ in range(2)] + + dp[0][0] = 1 # Base case + + for i in range(1, n + 1): + next_dp = [[0] * 3 for _ in range(2)] + + for cntA in range(2): + for cntL in range(3): + # Choose P + next_dp[cntA][0] = (next_dp[cntA][0] + dp[cntA][cntL]) % MOD + + # Choose A + if cntA > 0: + next_dp[cntA][0] = (next_dp[cntA][0] + dp[cntA - 1][cntL]) % MOD + + # Choose L + if cntL > 0: + next_dp[cntA][cntL] = (next_dp[cntA][cntL] + dp[cntA][cntL - 1]) % MOD + + dp = next_dp + + return sum(dp[cntA][cntL] for cntA in range(2) for cntL in range(3)) % MOD +``` + +```java +public class Solution { + public int checkRecord(int n) { + final int MOD = 1000000007; + int[][] dp = new int[2][3]; + + dp[0][0] = 1; + + for (int i = 1; i <= n; i++) { + int[][] nextDp = new int[2][3]; + + for (int cntA = 0; cntA < 2; cntA++) { + for (int cntL = 0; cntL < 3; cntL++) { + // Choose P + nextDp[cntA][0] = (nextDp[cntA][0] + dp[cntA][cntL]) % MOD; + + // Choose A + if (cntA > 0) { + nextDp[cntA][0] = (nextDp[cntA][0] + dp[cntA - 1][cntL]) % MOD; + } + + // Choose L + if (cntL > 0) { + nextDp[cntA][cntL] = (nextDp[cntA][cntL] + dp[cntA][cntL - 1]) % MOD; + } + } + } + + dp = nextDp; + } + + int result = 0; + for (int cntA = 0; cntA < 2; cntA++) { + for (int cntL = 0; cntL < 3; cntL++) { + result = (result + dp[cntA][cntL]) % MOD; + } + } + return result; + } +} +``` + +```cpp +class Solution { +public: + int checkRecord(int n) { + const int MOD = 1000000007; + vector> dp(2, vector(3, 0)); + + dp[0][0] = 1; + + for (int i = 1; i <= n; i++) { + vector> nextDp(2, vector(3, 0)); + + for (int cntA = 0; cntA < 2; cntA++) { + for (int cntL = 0; cntL < 3; cntL++) { + // Choose P + nextDp[cntA][0] = (nextDp[cntA][0] + dp[cntA][cntL]) % MOD; + + // Choose A + if (cntA > 0) { + nextDp[cntA][0] = (nextDp[cntA][0] + dp[cntA - 1][cntL]) % MOD; + } + + // Choose L + if (cntL > 0) { + nextDp[cntA][cntL] = (nextDp[cntA][cntL] + dp[cntA][cntL - 1]) % MOD; + } + } + } + + dp = nextDp; + } + + int result = 0; + for (int cntA = 0; cntA < 2; cntA++) { + for (int cntL = 0; cntL < 3; cntL++) { + result = (result + dp[cntA][cntL]) % MOD; + } + } + + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + checkRecord(n) { + const MOD = 1000000007; + let dp = Array.from({ length: 2 }, () => new Array(3).fill(0)); + + dp[0][0] = 1; + + for (let i = 1; i <= n; i++) { + let nextDp = Array.from({ length: 2 }, () => new Array(3).fill(0)); + + for (let cntA = 0; cntA < 2; cntA++) { + for (let cntL = 0; cntL < 3; cntL++) { + // Choose P + nextDp[cntA][0] = (nextDp[cntA][0] + dp[cntA][cntL]) % MOD; + + // Choose A + if (cntA > 0) { + nextDp[cntA][0] = (nextDp[cntA][0] + dp[cntA - 1][cntL]) % MOD; + } + + // Choose L + if (cntL > 0) { + nextDp[cntA][cntL] = (nextDp[cntA][cntL] + dp[cntA][cntL - 1]) % MOD; + } + } + } + + dp = nextDp; + } + + let result = 0; + for (let cntA = 0; cntA < 2; cntA++) { + for (let cntL = 0; cntL < 3; cntL++) { + result = (result + dp[cntA][cntL]) % MOD; + } + } + + return result; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/subarray-product-less-than-k.md b/articles/subarray-product-less-than-k.md new file mode 100644 index 000000000..dd7807c14 --- /dev/null +++ b/articles/subarray-product-less-than-k.md @@ -0,0 +1,313 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int: + n, res = len(nums), 0 + + for i in range(n): + curProd = 1 + for j in range(i, n): + curProd *= nums[j] + if curProd >= k: + break + res += 1 + + return res +``` + +```java +public class Solution { + public int numSubarrayProductLessThanK(int[] nums, int k) { + int n = nums.length, res = 0; + + for (int i = 0; i < n; i++) { + int curProd = 1; + for (int j = i; j < n; j++) { + curProd *= nums[j]; + if (curProd >= k) break; + res++; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubarrayProductLessThanK(vector& nums, int k) { + int n = nums.size(), res = 0; + + for (int i = 0; i < n; i++) { + int curProd = 1; + for (int j = i; j < n; j++) { + curProd *= nums[j]; + if (curProd >= k) break; + res++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + numSubarrayProductLessThanK(nums, k) { + let n = nums.length, res = 0; + + for (let i = 0; i < n; i++) { + let curProd = 1; + for (let j = i; j < n; j++) { + curProd *= nums[j]; + if (curProd >= k) break; + res++; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Binary Search + +::tabs-start + +```python +class Solution: + def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int: + if k <= 1: + return 0 + + n = len(nums) + res = 0 + logs = [0] * (n + 1) + logK = log(k) + for i in range(n): + logs[i + 1] = logs[i] + log(nums[i]) + + for i in range(n): + l, r = i + 1, n + 1 + while l < r: + mid = (l + r) >> 1 + if logs[mid] < logs[i] + logK: + l = mid + 1 + else: + r = mid + + res += l - (i + 1) + + return res +``` + +```java +public class Solution { + public int numSubarrayProductLessThanK(int[] nums, int k) { + if (k <= 1) return 0; + + int n = nums.length, res = 0; + double[] logs = new double[n + 1]; + double logK = Math.log(k); + + for (int i = 0; i < n; i++) { + logs[i + 1] = logs[i] + Math.log(nums[i]); + } + + for (int i = 0; i < n; i++) { + int l = i + 1, r = n + 1; + while (l < r) { + int mid = (l + r) / 2; + if (logs[mid] < logs[i] + logK) { + l = mid + 1; + } else { + r = mid; + } + } + res += l - (i + 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubarrayProductLessThanK(vector& nums, int k) { + if (k <= 1) return 0; + + int n = nums.size(), res = 0; + vector logs(n + 1, 0); + double logK = log(k); + + for (int i = 0; i < n; i++) { + logs[i + 1] = logs[i] + log(nums[i]); + } + + for (int i = 0; i < n; i++) { + int l = i + 1, r = n + 1; + while (l < r) { + int mid = (l + r) / 2; + if (logs[mid] < logs[i] + logK) { + l = mid + 1; + } else { + r = mid; + } + } + res += l - (i + 1); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + numSubarrayProductLessThanK(nums, k) { + if (k <= 1) return 0; + + const n = nums.length; + let res = 0; + const logs = new Array(n + 1).fill(0); + const logK = Math.log(k); + + for (let i = 0; i < n; i++) { + logs[i + 1] = logs[i] + Math.log(nums[i]); + } + + for (let i = 0; i < n; i++) { + let l = i + 1, r = n + 1; + while (l < r) { + const mid = Math.floor((l + r) / 2); + if (logs[mid] < logs[i] + logK) { + l = mid + 1; + } else { + r = mid; + } + } + res += l - (i + 1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sliding Window + +::tabs-start + +```python +class Solution: + def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int: + res = 0 + l = 0 + product = 1 + for r in range(len(nums)): + product *= nums[r] + while l <= r and product >= k: + product //= nums[l] + l += 1 + res += (r - l + 1) + return res +``` + +```java +public class Solution { + public int numSubarrayProductLessThanK(int[] nums, int k) { + int res = 0, l = 0; + long product = 1; + for (int r = 0; r < nums.length; r++) { + product *= nums[r]; + while (l <= r && product >= k) { + product /= nums[l++]; + } + res += (r - l + 1); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numSubarrayProductLessThanK(vector& nums, int k) { + int res = 0, l = 0; + long long product = 1; + for (int r = 0; r < nums.size(); r++) { + product *= nums[r]; + while (l <= r && product >= k) { + product /= nums[l++]; + } + res += (r - l + 1); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + numSubarrayProductLessThanK(nums, k) { + let res = 0, l = 0, product = 1; + for (let r = 0; r < nums.length; r++) { + product *= nums[r]; + while (l <= r && product >= k) { + product = Math.floor(product / nums[l++]); + } + res += (r - l + 1); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/subarray-sum-equals-k.md b/articles/subarray-sum-equals-k.md new file mode 100644 index 000000000..fa979f529 --- /dev/null +++ b/articles/subarray-sum-equals-k.md @@ -0,0 +1,213 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def subarraySum(self, nums: List[int], k: int) -> int: + res = 0 + for i in range(len(nums)): + sum = 0 + for j in range(i, len(nums)): + sum += nums[j] + if sum == k: + res += 1 + return res +``` + +```java +public class Solution { + public int subarraySum(int[] nums, int k) { + int res = 0; + for (int i = 0; i < nums.length; i++) { + int sum = 0; + for (int j = i; j < nums.length; j++) { + sum += nums[j]; + if (sum == k) res++; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int subarraySum(vector& nums, int k) { + int res = 0; + for (int i = 0; i < nums.size(); i++) { + int sum = 0; + for (int j = i; j < nums.size(); j++) { + sum += nums[j]; + if (sum == k) res++; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + subarraySum(nums, k) { + let res = 0; + for (let i = 0; i < nums.length; i++) { + let sum = 0; + for (let j = i; j < nums.length; j++) { + sum += nums[j]; + if (sum == k) res++; + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int SubarraySum(int[] nums, int k) { + int res = 0; + for (int i = 0; i < nums.Length; i++) { + int sum = 0; + for (int j = i; j < nums.Length; j++) { + sum += nums[j]; + if (sum == k) { + res++; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def subarraySum(self, nums: List[int], k: int) -> int: + res = curSum = 0 + prefixSums = { 0 : 1 } + + for num in nums: + curSum += num + diff = curSum - k + + res += prefixSums.get(diff, 0) + prefixSums[curSum] = 1 + prefixSums.get(curSum, 0) + + return res +``` + +```java +public class Solution { + public int subarraySum(int[] nums, int k) { + int res = 0, curSum = 0; + Map prefixSums = new HashMap<>(); + prefixSums.put(0, 1); + + for (int num : nums) { + curSum += num; + int diff = curSum - k; + res += prefixSums.getOrDefault(diff, 0); + prefixSums.put(curSum, prefixSums.getOrDefault(curSum, 0) + 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int subarraySum(vector& nums, int k) { + int res = 0, curSum = 0; + unordered_map prefixSums; + prefixSums[0] = 1; + + for (int num : nums) { + curSum += num; + int diff = curSum - k; + res += prefixSums[diff]; + prefixSums[curSum]++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + subarraySum(nums, k) { + let res = 0, curSum = 0; + const prefixSums = new Map(); + prefixSums.set(0, 1); + + for (let num of nums) { + curSum += num; + let diff = curSum - k; + res += prefixSums.get(diff) || 0; + prefixSums.set(curSum, (prefixSums.get(curSum) || 0) + 1); + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int SubarraySum(int[] nums, int k) { + int res = 0, curSum = 0; + Dictionary prefixSums = new Dictionary(); + prefixSums[0] = 1; + + foreach (int num in nums) { + curSum += num; + int diff = curSum - k; + + if (prefixSums.ContainsKey(diff)) { + res += prefixSums[diff]; + } + + if (!prefixSums.ContainsKey(curSum)) { + prefixSums[curSum] = 0; + } + prefixSums[curSum]++; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/subarray-sums-divisible-by-k.md b/articles/subarray-sums-divisible-by-k.md new file mode 100644 index 000000000..f1faeaf61 --- /dev/null +++ b/articles/subarray-sums-divisible-by-k.md @@ -0,0 +1,280 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def subarraysDivByK(self, nums: List[int], k: int) -> int: + n, res = len(nums), 0 + + for i in range(n): + curSum = 0 + for j in range(i, n): + curSum += nums[j] + if curSum % k == 0: + res += 1 + + return res +``` + +```java +public class Solution { + public int subarraysDivByK(int[] nums, int k) { + int n = nums.length, res = 0; + + for (int i = 0; i < n; i++) { + int curSum = 0; + for (int j = i; j < n; j++) { + curSum += nums[j]; + if (curSum % k == 0) { + res++; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int subarraysDivByK(vector& nums, int k) { + int n = nums.size(), res = 0; + + for (int i = 0; i < n; i++) { + int curSum = 0; + for (int j = i; j < n; j++) { + curSum += nums[j]; + if (curSum % k == 0) { + res++; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + subarraysDivByK(nums, k) { + const n = nums.length; + let res = 0; + + for (let i = 0; i < n; i++) { + let curSum = 0; + for (let j = i; j < n; j++) { + curSum += nums[j]; + if (curSum % k === 0) { + res++; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Prefix Sum + Hash Map + +::tabs-start + +```python +class Solution: + def subarraysDivByK(self, nums: List[int], k: int) -> int: + prefix_sum = 0 + res = 0 + prefix_cnt = defaultdict(int) + prefix_cnt[0] = 1 + + for n in nums: + prefix_sum += n + remain = prefix_sum % k + + res += prefix_cnt[remain] + prefix_cnt[remain] += 1 + + return res +``` + +```java +public class Solution { + public int subarraysDivByK(int[] nums, int k) { + int prefixSum = 0, res = 0; + Map prefixCnt = new HashMap<>(); + prefixCnt.put(0, 1); + + for (int n : nums) { + prefixSum += n; + int remain = prefixSum % k; + if (remain < 0) remain += k; + + res += prefixCnt.getOrDefault(remain, 0); + prefixCnt.put(remain, prefixCnt.getOrDefault(remain, 0) + 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int subarraysDivByK(vector& nums, int k) { + int prefixSum = 0, res = 0; + unordered_map prefixCnt; + prefixCnt[0] = 1; + + for (int n : nums) { + prefixSum += n; + int remain = prefixSum % k; + if (remain < 0) remain += k; + + res += prefixCnt[remain]; + prefixCnt[remain]++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + subarraysDivByK(nums, k) { + let prefixSum = 0, res = 0; + const prefixCnt = new Map(); + prefixCnt.set(0, 1); + + for (let n of nums) { + prefixSum += n; + let remain = prefixSum % k; + if (remain < 0) remain += k; + + res += prefixCnt.get(remain) || 0; + prefixCnt.set(remain, (prefixCnt.get(remain) || 0) + 1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(k)$ + +--- + +## 3. Prefix Sum + Array + +::tabs-start + +```python +class Solution: + def subarraysDivByK(self, nums: List[int], k: int) -> int: + count = [0] * k + count[0] = 1 + prefix = res = 0 + + for num in nums: + prefix = (prefix + num + k) % k + res += count[prefix] + count[prefix] += 1 + + return res +``` + +```java +public class Solution { + public int subarraysDivByK(int[] nums, int k) { + int[] count = new int[k]; + count[0] = 1; + int prefix = 0, res = 0; + + for (int num : nums) { + prefix = (prefix + num % k + k) % k; + res += count[prefix]; + count[prefix]++; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int subarraysDivByK(vector& nums, int k) { + vector count(k, 0); + count[0] = 1; + int prefix = 0, res = 0; + + for (int num : nums) { + prefix = (prefix + num % k + k) % k; + res += count[prefix]; + count[prefix]++; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + subarraysDivByK(nums, k) { + const count = Array(k).fill(0); + count[0] = 1; + let prefix = 0, res = 0; + + for (let num of nums) { + prefix = (prefix + num % k + k) % k; + res += count[prefix]; + count[prefix]++; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + k)$ +* Space complexity: $O(k)$ \ No newline at end of file diff --git a/articles/subarrays-with-k-different-integers.md b/articles/subarrays-with-k-different-integers.md new file mode 100644 index 000000000..a2e24a470 --- /dev/null +++ b/articles/subarrays-with-k-different-integers.md @@ -0,0 +1,546 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def subarraysWithKDistinct(self, nums: List[int], k: int) -> int: + n, res = len(nums), 0 + + for i in range(n): + seen = set() + for j in range(i, n): + seen.add(nums[j]) + if len(seen) > k: + break + + if len(seen) == k: + res += 1 + + return res +``` + +```java +public class Solution { + public int subarraysWithKDistinct(int[] nums, int k) { + int n = nums.length, res = 0; + + for (int i = 0; i < n; i++) { + Set seen = new HashSet<>(); + for (int j = i; j < n; j++) { + seen.add(nums[j]); + if (seen.size() > k) { + break; + } + if (seen.size() == k) { + res++; + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int subarraysWithKDistinct(vector& nums, int k) { + int n = nums.size(), res = 0; + + for (int i = 0; i < n; i++) { + unordered_set seen; + for (int j = i; j < n; j++) { + seen.insert(nums[j]); + if (seen.size() > k) { + break; + } + if (seen.size() == k) { + res++; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + subarraysWithKDistinct(nums, k) { + let n = nums.length, res = 0; + + for (let i = 0; i < n; i++) { + let seen = new Set(); + for (let j = i; j < n; j++) { + seen.add(nums[j]); + if (seen.size > k) { + break; + } + if (seen.size === k) { + res++; + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Sliding Window + +::tabs-start + +```python +class Solution: + def subarraysWithKDistinct(self, nums: List[int], k: int) -> int: + + def atMostK(k): + count = defaultdict(int) + res = l = 0 + + for r in range(len(nums)): + count[nums[r]] += 1 + if count[nums[r]] == 1: + k -= 1 + + while k < 0: + count[nums[l]] -= 1 + if count[nums[l]] == 0: + k += 1 + l += 1 + + res += (r - l + 1) + + return res + + return atMostK(k) - atMostK(k - 1) +``` + +```java +public class Solution { + public int subarraysWithKDistinct(int[] nums, int k) { + return atMostK(nums, k) - atMostK(nums, k - 1); + } + + private int atMostK(int[] nums, int k) { + HashMap count = new HashMap<>(); + int res = 0, l = 0; + + for (int r = 0; r < nums.length; r++) { + count.put(nums[r], count.getOrDefault(nums[r], 0) + 1); + if (count.get(nums[r]) == 1) { + k--; + } + + while (k < 0) { + count.put(nums[l], count.get(nums[l]) - 1); + if (count.get(nums[l]) == 0) { + k++; + } + l++; + } + + res += (r - l + 1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int subarraysWithKDistinct(vector& nums, int k) { + return atMostK(nums, k) - atMostK(nums, k - 1); + } + +private: + int atMostK(vector& nums, int k) { + unordered_map count; + int res = 0, l = 0; + + for (int r = 0; r < nums.size(); r++) { + count[nums[r]]++; + if (count[nums[r]] == 1) { + k--; + } + + while (k < 0) { + count[nums[l]]--; + if (count[nums[l]] == 0) { + k++; + } + l++; + } + + res += (r - l + 1); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + subarraysWithKDistinct(nums, k) { + const atMostK = (k) => { + const count = new Map(); + let res = 0, l = 0; + + for (let r = 0; r < nums.length; r++) { + count.set(nums[r], (count.get(nums[r]) || 0) + 1); + if (count.get(nums[r]) === 1) { + k--; + } + + while (k < 0) { + count.set(nums[l], count.get(nums[l]) - 1); + if (count.get(nums[l]) === 0) { + k++; + } + l++; + } + + res += (r - l + 1); + } + + return res; + }; + + return atMostK(k) - atMostK(k - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Sliding Window (One Pass) - I + +::tabs-start + +```python +class Solution: + def subarraysWithKDistinct(self, nums: List[int], k: int) -> int: + count = defaultdict(int) + res = 0 + l_far = 0 + l_near = 0 + + for r in range(len(nums)): + count[nums[r]] += 1 + + while len(count) > k: + count[nums[l_near]] -= 1 + if count[nums[l_near]] == 0: + count.pop(nums[l_near]) + l_near += 1 + l_far = l_near + + while count[nums[l_near]] > 1: + count[nums[l_near]] -= 1 + l_near += 1 + + if len(count) == k: + res += l_near - l_far + 1 + + return res +``` + +```java +public class Solution { + public int subarraysWithKDistinct(int[] nums, int k) { + HashMap count = new HashMap<>(); + int res = 0, l_far = 0, l_near = 0; + + for (int r = 0; r < nums.length; r++) { + count.put(nums[r], count.getOrDefault(nums[r], 0) + 1); + + while (count.size() > k) { + count.put(nums[l_near], count.get(nums[l_near]) - 1); + if (count.get(nums[l_near]) == 0) { + count.remove(nums[l_near]); + } + l_near++; + l_far = l_near; + } + + while (count.get(nums[l_near]) > 1) { + count.put(nums[l_near], count.get(nums[l_near]) - 1); + l_near++; + } + + if (count.size() == k) { + res += l_near - l_far + 1; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int subarraysWithKDistinct(vector& nums, int k) { + unordered_map count; + int res = 0, l_far = 0, l_near = 0; + + for (int r = 0; r < nums.size(); r++) { + count[nums[r]]++; + + while (count.size() > k) { + count[nums[l_near]]--; + if (count[nums[l_near]] == 0) { + count.erase(nums[l_near]); + } + l_near++; + l_far = l_near; + } + + while (count[nums[l_near]] > 1) { + count[nums[l_near]]--; + l_near++; + } + + if (count.size() == k) { + res += l_near - l_far + 1; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + subarraysWithKDistinct(nums, k) { + const count = new Map(); + let res = 0, l_far = 0, l_near = 0; + + for (let r = 0; r < nums.length; r++) { + count.set(nums[r], (count.get(nums[r]) || 0) + 1); + + while (count.size > k) { + count.set(nums[l_near], count.get(nums[l_near]) - 1); + if (count.get(nums[l_near]) === 0) { + count.delete(nums[l_near]); + } + l_near++; + } + + while (count.get(nums[l_near]) > 1) { + count.set(nums[l_near], count.get(nums[l_near]) - 1); + l_near++; + } + + if (count.size === k) { + res += l_near - l_far + 1; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Sliding Window (One Pass) - II + +::tabs-start + +```python +class Solution: + def subarraysWithKDistinct(self, nums: List[int], k: int) -> int: + n = len(nums) + count = [0] * (n + 1) + res = l = cnt = 0 + + for r in range(n): + count[nums[r]] += 1 + if count[nums[r]] == 1: + k -= 1 + + if k < 0: + count[nums[l]] -= 1 + l += 1 + k += 1 + cnt = 0 + + if k == 0: + while count[nums[l]] > 1: + count[nums[l]] -= 1 + l += 1 + cnt += 1 + + res += (cnt + 1) + + return res +``` + +```java +public class Solution { + public int subarraysWithKDistinct(int[] nums, int k) { + int n = nums.length; + int[] count = new int[n + 1]; + int res = 0, l = 0, cnt = 0; + + for (int r = 0; r < n; r++) { + count[nums[r]]++; + if (count[nums[r]] == 1) { + k--; + } + + if (k < 0) { + count[nums[l]]--; + l++; + k++; + cnt = 0; + } + + if (k == 0) { + while (count[nums[l]] > 1) { + count[nums[l]]--; + l++; + cnt++; + } + + res += (cnt + 1); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int subarraysWithKDistinct(vector& nums, int k) { + int n = nums.size(); + vector count(n + 1, 0); + int res = 0, l = 0, cnt = 0; + + for (int r = 0; r < n; r++) { + count[nums[r]]++; + if (count[nums[r]] == 1) { + k--; + } + + if (k < 0) { + count[nums[l]]--; + l++; + k++; + cnt = 0; + } + + if (k == 0) { + while (count[nums[l]] > 1) { + count[nums[l]]--; + l++; + cnt++; + } + + res += (cnt + 1); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + subarraysWithKDistinct(nums, k) { + const n = nums.length; + const count = new Array(n + 1).fill(0); + let res = 0, l = 0, cnt = 0; + + for (let r = 0; r < n; r++) { + count[nums[r]]++; + if (count[nums[r]] === 1) { + k--; + } + + if (k < 0) { + count[nums[l]]--; + l++; + k++; + cnt = 0; + } + + if (k === 0) { + while (count[nums[l]] > 1) { + count[nums[l]]--; + l++; + cnt++; + } + + res += (cnt + 1); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/subsets-ii.md b/articles/subsets-ii.md new file mode 100644 index 000000000..fef683226 --- /dev/null +++ b/articles/subsets-ii.md @@ -0,0 +1,890 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def subsetsWithDup(self, nums: List[int]) -> List[List[int]]: + res = set() + + def backtrack(i, subset): + if i == len(nums): + res.add(tuple(subset)) + return + + subset.append(nums[i]) + backtrack(i + 1, subset) + subset.pop() + backtrack(i + 1, subset) + + nums.sort() + backtrack(0, []) + return [list(s) for s in res] +``` + +```java +public class Solution { + Set> res = new HashSet<>(); + + public List> subsetsWithDup(int[] nums) { + Arrays.sort(nums); + backtrack(nums, 0, new ArrayList<>()); + return new ArrayList<>(res); + } + + private void backtrack(int[] nums, int i, List subset) { + if (i == nums.length) { + res.add(new ArrayList<>(subset)); + return; + } + + subset.add(nums[i]); + backtrack(nums, i + 1, subset); + subset.remove(subset.size() - 1); + backtrack(nums, i + 1, subset); + } +} +``` + +```cpp +class Solution { + set> res; +public: + vector> subsetsWithDup(vector& nums) { + sort(nums.begin(), nums.end()); + backtrack(nums, 0, {}); + return vector>(res.begin(), res.end()); + } + + void backtrack(vector& nums, int i, vector subset) { + if (i == nums.size()) { + res.insert(subset); + return; + } + + subset.push_back(nums[i]); + backtrack(nums, i + 1, subset); + subset.pop_back(); + backtrack(nums, i + 1, subset); + } +}; +``` + +```javascript +class Solution { + constructor() { + this.res = new Set(); + } + + /** + * @param {number[]} nums + * @return {number[][]} + */ + subsetsWithDup(nums) { + nums.sort((a, b) => a - b); + this.backtrack(nums, 0, []); + return Array.from(this.res).map(subset => JSON.parse(subset)); + } + + /** + * @param {number[]} nums + * @param {number[]} subset + * @return {void} + */ + backtrack(nums, i, subset) { + if (i === nums.length) { + this.res.add(JSON.stringify(subset)); + return; + } + + subset.push(nums[i]); + this.backtrack(nums, i + 1, subset); + subset.pop(); + this.backtrack(nums, i + 1, subset); + } +} +``` + +```csharp +public class Solution { + HashSet res = new HashSet(); + + public List> SubsetsWithDup(int[] nums) { + Array.Sort(nums); + Backtrack(nums, 0, new List()); + List> result = new List>(); + result.Add(new List()); + res.Remove(""); + foreach (string str in res) { + List subset = new List(); + string[] arr = str.Split(','); + foreach (string num in arr) { + subset.Add(int.Parse(num)); + } + result.Add(subset); + } + return result; + } + + private void Backtrack(int[] nums, int i, List subset) { + if (i == nums.Length) { + res.Add(string.Join(",", subset)); + return; + } + + subset.Add(nums[i]); + Backtrack(nums, i + 1, subset); + subset.RemoveAt(subset.Count - 1); + Backtrack(nums, i + 1, subset); + } +} +``` + +```go +func subsetsWithDup(nums []int) [][]int { + sort.Ints(nums) + res := make(map[string][]int) + + var backtrack func(int, []int) + backtrack = func(i int, subset []int) { + if i == len(nums) { + key := fmt.Sprint(subset) + res[key] = append([]int{}, subset...) + return + } + + subset = append(subset, nums[i]) + backtrack(i+1, subset) + subset = subset[:len(subset)-1] + backtrack(i+1, subset) + } + + backtrack(0, []int{}) + + var result [][]int + for _, v := range res { + result = append(result, v) + } + return result +} +``` + +```kotlin +class Solution { + fun subsetsWithDup(nums: IntArray): List> { + nums.sort() + val res = HashSet>() + + fun backtrack(i: Int, subset: MutableList) { + if (i == nums.size) { + res.add(ArrayList(subset)) + return + } + + subset.add(nums[i]) + backtrack(i + 1, subset) + subset.removeAt(subset.size - 1) + backtrack(i + 1, subset) + } + + backtrack(0, mutableListOf()) + return res.toList() + } +} +``` + +```swift +class Solution { + func subsetsWithDup(_ nums: [Int]) -> [[Int]] { + var res = Set<[Int]>() + var subset = [Int]() + let nums = nums.sorted() + + func backtrack(_ i: Int) { + if i == nums.count { + res.insert(subset) + return + } + + subset.append(nums[i]) + backtrack(i + 1) + subset.removeLast() + backtrack(i + 1) + } + + backtrack(0) + return Array(res) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^n)$ +* Space complexity: $O(2 ^ n)$ + +--- + +## 2. Backtracking - I + +::tabs-start + +```python +class Solution: + def subsetsWithDup(self, nums: List[int]) -> List[List[int]]: + res = [] + nums.sort() + + def backtrack(i, subset): + if i == len(nums): + res.append(subset[::]) + return + + subset.append(nums[i]) + backtrack(i + 1, subset) + subset.pop() + + while i + 1 < len(nums) and nums[i] == nums[i + 1]: + i += 1 + backtrack(i + 1, subset) + + backtrack(0, []) + return res +``` + +```java +public class Solution { + List> res = new ArrayList<>(); + + public List> subsetsWithDup(int[] nums) { + Arrays.sort(nums); + backtrack(0, new ArrayList<>(), nums); + return res; + } + + private void backtrack(int i, List subset, int[] nums) { + if (i == nums.length) { + res.add(new ArrayList<>(subset)); + return; + } + + subset.add(nums[i]); + backtrack(i + 1, subset, nums); + subset.remove(subset.size() - 1); + + while (i + 1 < nums.length && nums[i] == nums[i + 1]) { + i++; + } + backtrack(i + 1, subset, nums); + } +} +``` + +```cpp +class Solution { + vector> res; +public: + vector> subsetsWithDup(vector& nums) { + sort(nums.begin(), nums.end()); + backtrack(0, {}, nums); + return res; + } + + void backtrack(int i, vector subset, vector& nums) { + if (i == nums.size()) { + res.push_back(subset); + return; + } + + subset.push_back(nums[i]); + backtrack(i + 1, subset, nums); + subset.pop_back(); + + while (i + 1 < nums.size() && nums[i] == nums[i + 1]) { + i++; + } + backtrack(i + 1, subset, nums); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + subsetsWithDup(nums) { + const res = []; + nums.sort((a, b) => a - b); + this.backtrack(0, [], nums, res); + return res; + } + + /** + * @param {number} start + * @param {number[]} subset + * @param {number[]} nums + * @param {number[][]} res + * @return {void} + */ + backtrack(start, subset, nums, res) { + res.push([...subset]); + for (let i = start; i < nums.length; i++) { + if (i > start && nums[i] === nums[i - 1]) { + continue; + } + subset.push(nums[i]); + this.backtrack(i + 1, subset, nums, res); + subset.pop(); + } + } +} +``` + +```csharp +public class Solution { + List> res = new List>(); + + public List> SubsetsWithDup(int[] nums) { + Array.Sort(nums); + Backtrack(0, new List(), nums); + return res; + } + + private void Backtrack(int i, List subset, int[] nums) { + if (i == nums.Length) { + res.Add(new List(subset)); + return; + } + + subset.Add(nums[i]); + Backtrack(i + 1, subset, nums); + subset.RemoveAt(subset.Count - 1); + + while (i + 1 < nums.Length && nums[i] == nums[i + 1]) { + i++; + } + Backtrack(i + 1, subset, nums); + } +} +``` + +```go +func subsetsWithDup(nums []int) [][]int { + var res [][]int + sort.Ints(nums) + + var backtrack func(int, []int) + backtrack = func(i int, subset []int) { + if i == len(nums) { + res = append(res, append([]int{}, subset...)) + return + } + + subset = append(subset, nums[i]) + backtrack(i+1, subset) + subset = subset[:len(subset)-1] + + for i+1 < len(nums) && nums[i] == nums[i+1] { + i++ + } + + backtrack(i+1, subset) + } + + backtrack(0, []int{}) + return res +} +``` + +```kotlin +class Solution { + fun subsetsWithDup(nums: IntArray): List> { + val res = mutableListOf>() + nums.sort() + + fun backtrack(i: Int, subset: MutableList) { + if (i == nums.size) { + res.add(ArrayList(subset)) + return + } + + subset.add(nums[i]) + backtrack(i + 1, subset) + subset.removeAt(subset.size - 1) + + var j = i + while (j + 1 < nums.size && nums[j] == nums[j + 1]) { + j++ + } + + backtrack(j + 1, subset) + } + + backtrack(0, mutableListOf()) + return res + } +} +``` + +```swift +class Solution { + func subsetsWithDup(_ nums: [Int]) -> [[Int]] { + var res = [[Int]]() + var subset = [Int]() + let nums = nums.sorted() + + func backtrack(_ i: Int) { + if i == nums.count { + res.append(subset) + return + } + + subset.append(nums[i]) + backtrack(i + 1) + subset.removeLast() + + var j = i + while j + 1 < nums.count && nums[j] == nums[j + 1] { + j += 1 + } + backtrack(j + 1) + } + + backtrack(0) + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: + * $O(n)$ extra space. + * $O(2 ^ n)$ space for the output list. + +--- + +## 3. Backtracking - II + +::tabs-start + +```python +class Solution: + def subsetsWithDup(self, nums: List[int]) -> List[List[int]]: + nums.sort() + res = [] + def backtrack(i, subset): + res.append(subset[::]) + + for j in range(i, len(nums)): + if j > i and nums[j] == nums[j - 1]: + continue + subset.append(nums[j]) + backtrack(j + 1, subset) + subset.pop() + + backtrack(0, []) + return res +``` + +```java +public class Solution { + List> res = new ArrayList<>(); + + public List> subsetsWithDup(int[] nums) { + Arrays.sort(nums); + backtrack(0, new ArrayList<>(), nums); + return res; + } + + private void backtrack(int i, List subset, int[] nums) { + res.add(new ArrayList<>(subset)); + for (int j = i; j < nums.length; j++) { + if (j > i && nums[j] == nums[j - 1]) { + continue; + } + subset.add(nums[j]); + backtrack(j + 1, subset, nums); + subset.remove(subset.size() - 1); + } + } +} +``` + +```cpp +class Solution { +public: + vector> res; + + vector> subsetsWithDup(vector& nums) { + sort(nums.begin(), nums.end()); + backtrack(0, {}, nums); + return res; + } + + void backtrack(int i, vector subset, vector& nums) { + res.push_back(subset); + for (int j = i; j < nums.size(); j++) { + if (j > i && nums[j] == nums[j - 1]) { + continue; + } + subset.push_back(nums[j]); + backtrack(j + 1, subset, nums); + subset.pop_back(); + } + } +}; +``` + +```javascript +class Solution { + constructor() { + this.res = []; + } + + /** + * @param {number[]} nums + * @return {number[][]} + */ + subsetsWithDup(nums) { + nums.sort((a, b) => a - b); + this.backtrack(0, [], nums); + return this.res; + } + + /** + * @param {number} i + * @param {number[]} subset + * @param {number[]} nums + * @return {void} + */ + backtrack(i, subset, nums) { + this.res.push([...subset]); + for (let j = i; j < nums.length; j++) { + if (j > i && nums[j] === nums[j - 1]) { + continue; + } + subset.push(nums[j]); + this.backtrack(j + 1, subset, nums); + subset.pop(); + } + } +} +``` + +```csharp +public class Solution { + private List> res = new List>(); + + public List> SubsetsWithDup(int[] nums) { + Array.Sort(nums); + Backtrack(0, new List(), nums); + return res; + } + + private void Backtrack(int i, List subset, int[] nums) { + res.Add(new List(subset)); + for (int j = i; j < nums.Length; j++) { + if (j > i && nums[j] == nums[j - 1]) { + continue; + } + subset.Add(nums[j]); + Backtrack(j + 1, subset, nums); + subset.RemoveAt(subset.Count - 1); + } + } +} +``` + +```go +func subsetsWithDup(nums []int) [][]int { + var res [][]int + sort.Ints(nums) + + var backtrack func(int, []int) + backtrack = func(i int, subset []int) { + res = append(res, append([]int{}, subset...)) + + for j := i; j < len(nums); j++ { + if j > i && nums[j] == nums[j-1] { + continue + } + subset = append(subset, nums[j]) + backtrack(j+1, subset) + subset = subset[:len(subset)-1] + } + } + + backtrack(0, []int{}) + return res +} +``` + +```kotlin +class Solution { + fun subsetsWithDup(nums: IntArray): List> { + val res = mutableListOf>() + nums.sort() + + fun backtrack(i: Int, subset: MutableList) { + res.add(ArrayList(subset)) + + for (j in i until nums.size) { + if (j > i && nums[j] == nums[j - 1]) { + continue + } + subset.add(nums[j]) + backtrack(j + 1, subset) + subset.removeAt(subset.size - 1) + } + } + + backtrack(0, mutableListOf()) + return res + } +} +``` + +```swift +class Solution { + func subsetsWithDup(_ nums: [Int]) -> [[Int]] { + var res = [[Int]]() + var subset = [Int]() + let nums = nums.sorted() + + func backtrack(_ i: Int) { + res.append(subset) + + for j in i.. i && nums[j] == nums[j - 1] { + continue + } + subset.append(nums[j]) + backtrack(j + 1) + subset.removeLast() + } + } + + backtrack(0) + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: + * $O(n)$ extra space. + * $O(2 ^ n)$ space for the output list. + +--- + +## 4. Iteration + +::tabs-start + +```python +class Solution: + def subsetsWithDup(self, nums: List[int]) -> List[List[int]]: + nums.sort() + res = [[]] + prev_Idx = idx = 0 + + for i in range(len(nums)): + idx = prev_idx if i >= 1 and nums[i] == nums[i - 1] else 0 + prev_idx = len(res) + for j in range(idx, prev_idx): + tmp = res[j].copy() + tmp.append(nums[i]) + res.append(tmp) + + return res +``` + +```java +public class Solution { + public List> subsetsWithDup(int[] nums) { + Arrays.sort(nums); + List> res = new ArrayList<>(); + res.add(new ArrayList<>()); + int prevIdx = 0; + int idx = 0; + + for (int i = 0; i < nums.length; i++) { + idx = (i >= 1 && nums[i] == nums[i - 1]) ? prevIdx : 0; + prevIdx = res.size(); + for (int j = idx; j < prevIdx; j++) { + List tmp = new ArrayList<>(res.get(j)); + tmp.add(nums[i]); + res.add(tmp); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> subsetsWithDup(vector& nums) { + sort(nums.begin(), nums.end()); + vector> res = {{}}; + int prevIdx = 0; + int idx = 0; + + for (int i = 0; i < nums.size(); i++) { + idx = (i >= 1 && nums[i] == nums[i - 1]) ? prevIdx : 0; + prevIdx = res.size(); + for (int j = idx; j < prevIdx; j++) { + std::vector tmp = res[j]; + tmp.push_back(nums[i]); + res.push_back(tmp); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + subsetsWithDup(nums) { + nums.sort((a, b) => a - b); + const res = [[]]; + let prevIdx = 0; + let idx = 0; + + for (let i = 0; i < nums.length; i++) { + idx = (i >= 1 && nums[i] === nums[i - 1]) ? prevIdx : 0; + prevIdx = res.length; + for (let j = idx; j < prevIdx; j++) { + const tmp = [...res[j]]; + tmp.push(nums[i]); + res.push(tmp); + } + } + + return res; + } +} +``` + +```csharp +public class Solution { + public List> SubsetsWithDup(int[] nums) { + Array.Sort(nums); + var res = new List> { new List() }; + int prevIdx = 0; + int idx = 0; + + for (int i = 0; i < nums.Length; i++) { + idx = (i >= 1 && nums[i] == nums[i - 1]) ? prevIdx : 0; + prevIdx = res.Count; + for (int j = idx; j < prevIdx; j++) { + var tmp = new List(res[j]); + tmp.Add(nums[i]); + res.Add(tmp); + } + } + + return res; + } +} +``` + +```go +func subsetsWithDup(nums []int) [][]int { + sort.Ints(nums) + res := [][]int{{}} + prevIdx, idx := 0, 0 + + for i := 0; i < len(nums); i++ { + if i > 0 && nums[i] == nums[i-1] { + idx = prevIdx + } else { + idx = 0 + } + prevIdx = len(res) + for j := idx; j < prevIdx; j++ { + tmp := append([]int{}, res[j]...) + tmp = append(tmp, nums[i]) + res = append(res, tmp) + } + } + + return res +} +``` + +```kotlin +class Solution { + fun subsetsWithDup(nums: IntArray): List> { + nums.sort() + val res = mutableListOf(listOf()) + var prevIdx = 0 + var idx = 0 + + for (i in nums.indices) { + idx = if (i > 0 && nums[i] == nums[i - 1]) prevIdx else 0 + prevIdx = res.size + for (j in idx until prevIdx) { + val tmp = ArrayList(res[j]) + tmp.add(nums[i]) + res.add(tmp) + } + } + + return res + } +} +``` + +```swift +class Solution { + func subsetsWithDup(_ nums: [Int]) -> [[Int]] { + let nums = nums.sorted() + var res: [[Int]] = [[]] + var prevIdx = 0 + var idx = 0 + + for i in 0..= 1 && nums[i] == nums[i - 1]) ? prevIdx : 0 + prevIdx = res.count + + for j in idx.. List[List[int]]: + res = [] + subset = [] + + def dfs(i): + if i >= len(nums): + res.append(subset.copy()) + return + subset.append(nums[i]) + dfs(i + 1) + subset.pop() + dfs(i + 1) + + dfs(0) + return res +``` + +```java +public class Solution { + + public List> subsets(int[] nums) { + List> res = new ArrayList<>(); + List subset = new ArrayList<>(); + dfs(nums, 0, subset, res); + return res; + } + + private void dfs(int[] nums, int i, List subset, List> res) { + if (i >= nums.length) { + res.add(new ArrayList<>(subset)); + return; + } + subset.add(nums[i]); + dfs(nums, i + 1, subset, res); + subset.remove(subset.size() - 1); + dfs(nums, i + 1, subset, res); + } +} +``` + +```cpp +class Solution { +public: + vector> subsets(vector& nums) { + vector> res; + vector subset; + dfs(nums, 0, subset, res); + return res; + } + +private: + void dfs(const vector& nums, int i, vector& subset, vector>& res) { + if (i >= nums.size()) { + res.push_back(subset); + return; + } + subset.push_back(nums[i]); + dfs(nums, i + 1, subset, res); + subset.pop_back(); + dfs(nums, i + 1, subset, res); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + subsets(nums) { + const res = []; + const subset = []; + this.dfs(nums, 0, subset, res); + return res; + } + + /** + * @param {number[]} nums + * @param {number} i + * @param {number[]} subset + * @param {number[][]} res + * @return {void} + */ + dfs(nums, i, subset, res) { + if (i >= nums.length) { + res.push([...subset]); + return; + } + subset.push(nums[i]); + this.dfs(nums, i + 1, subset, res); + subset.pop(); + this.dfs(nums, i + 1, subset, res); + } +} +``` + +```csharp +public class Solution { + + public List> Subsets(int[] nums) { + var res = new List>(); + var subset = new List(); + Dfs(nums, 0, subset, res); + return res; + } + + private void Dfs(int[] nums, int i, List subset, List> res) { + if (i >= nums.Length) { + res.Add(new List(subset)); + return; + } + subset.Add(nums[i]); + Dfs(nums, i + 1, subset, res); + subset.RemoveAt(subset.Count - 1); + Dfs(nums, i + 1, subset, res); + } +} +``` + +```go +func subsets(nums []int) [][]int { + res := [][]int{} + subset := []int{} + + var dfs func(int) + dfs = func(i int) { + if i >= len(nums) { + temp := make([]int, len(subset)) + copy(temp, subset) + res = append(res, temp) + return + } + subset = append(subset, nums[i]) + dfs(i + 1) + subset = subset[:len(subset)-1] + dfs(i + 1) + } + + dfs(0) + return res +} +``` + +```kotlin +class Solution { + fun subsets(nums: IntArray): List> { + val res = mutableListOf>() + val subset = mutableListOf() + + fun dfs(i: Int) { + if (i >= nums.size) { + res.add(subset.toList()) + return + } + subset.add(nums[i]) + dfs(i + 1) + subset.removeAt(subset.size - 1) + dfs(i + 1) + } + + dfs(0) + return res + } +} +``` + +```swift +class Solution { + func subsets(_ nums: [Int]) -> [[Int]] { + var res = [[Int]]() + var subset = [Int]() + + func dfs(_ i: Int) { + if i >= nums.count { + res.append(subset) + return + } + subset.append(nums[i]) + dfs(i + 1) + subset.removeLast() + dfs(i + 1) + } + + dfs(0) + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: + * $O(n)$ extra space. + * $O(2 ^ n)$ for the output list. + +--- + +## 2. Iteration + +::tabs-start + +```python +class Solution: + def subsets(self, nums: List[int]) -> List[List[int]]: + res = [[]] + + for num in nums: + res += [subset + [num] for subset in res] + + return res +``` + +```java +public class Solution { + public List> subsets(int[] nums) { + List> res = new ArrayList<>(); + res.add(new ArrayList<>()); + + for (int num : nums) { + int size = res.size(); + for (int i = 0; i < size; i++) { + List subset = new ArrayList<>(res.get(i)); + subset.add(num); + res.add(subset); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> subsets(vector& nums) { + vector> res = {{}}; + + for (int num : nums) { + int size = res.size(); + for (int i = 0; i < size; i++) { + vector subset = res[i]; + subset.push_back(num); + res.push_back(subset); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + subsets(nums) { + let res = [[]]; + + for (let num of nums) { + let size = res.length; + for (let i = 0; i < size; i++) { + let subset = res[i].slice(); + subset.push(num); + res.push(subset); + } + } + + return res; + } +} +``` + +```csharp +public class Solution { + public List> Subsets(int[] nums) { + List> res = new List>(); + res.Add(new List()); + + foreach (int num in nums) { + int size = res.Count; + for (int i = 0; i < size; i++) { + List subset = new List(res[i]); + subset.Add(num); + res.Add(subset); + } + } + + return res; + } +} +``` + +```go +func subsets(nums []int) [][]int { + res := [][]int{{}} + + for _, num := range nums { + n := len(res) + for i := 0; i < n; i++ { + newSubset := make([]int, len(res[i])) + copy(newSubset, res[i]) + newSubset = append(newSubset, num) + res = append(res, newSubset) + } + } + + return res +} +``` + +```kotlin +class Solution { + fun subsets(nums: IntArray): List> { + val res = mutableListOf>(listOf()) + + for (num in nums) { + val n = res.size + for (i in 0 until n) { + val newSubset = res[i].toMutableList() + newSubset.add(num) + res.add(newSubset) + } + } + + return res + } +} +``` + +```swift +class Solution { + func subsets(_ nums: [Int]) -> [[Int]] { + var res: [[Int]] = [[]] + + for num in nums { + res += res.map { $0 + [num] } + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: + * $O(n)$ extra space. + * $O(2 ^ n)$ for the output list. + +--- + +## 3. Bit Manipulation + +::tabs-start + +```python +class Solution: + def subsets(self, nums: List[int]) -> List[List[int]]: + n = len(nums) + res = [] + for i in range(1 << n): + subset = [nums[j] for j in range(n) if (i & (1 << j))] + res.append(subset) + return res +``` + +```java +public class Solution { + public List> subsets(int[] nums) { + int n = nums.length; + List> res = new ArrayList<>(); + for (int i = 0; i < (1 << n); i++) { + List subset = new ArrayList<>(); + for (int j = 0; j < n; j++) { + if ((i & (1 << j)) != 0) { + subset.add(nums[j]); + } + } + res.add(subset); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> subsets(vector& nums) { + int n = nums.size(); + vector> res; + for (int i = 0; i < (1 << n); i++) { + vector subset; + for (int j = 0; j < n; j++) { + if (i & (1 << j)) { + subset.push_back(nums[j]); + } + } + res.push_back(subset); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + subsets(nums) { + let res = []; + let n = nums.length; + for (let i = 0; i < (1 << n); i++) { + let subset = []; + for (let j = 0; j < n; j++) { + if (i & (1 << j)) { + subset.push(nums[j]); + } + } + res.push(subset); + } + return res; + } +} +``` + +```csharp +public class Solution { + public List> Subsets(int[] nums) { + int n = nums.Length; + List> res = new List>(); + for (int i = 0; i < (1 << n); i++) { + List subset = new List(); + for (int j = 0; j < n; j++) { + if ((i & (1 << j)) != 0) { + subset.Add(nums[j]); + } + } + res.Add(subset); + } + return res; + } +} +``` + +```go +func subsets(nums []int) [][]int { + n := len(nums) + res := [][]int{} + + for i := 0; i < (1 << n); i++ { + subset := []int{} + for j := 0; j < n; j++ { + if (i & (1 << j)) != 0 { + subset = append(subset, nums[j]) + } + } + res = append(res, subset) + } + + return res +} +``` + +```kotlin +class Solution { + fun subsets(nums: IntArray): List> { + val n = nums.size + val res = mutableListOf>() + + for (i in 0 until (1 shl n)) { + val subset = mutableListOf() + for (j in 0 until n) { + if (i and (1 shl j) != 0) { + subset.add(nums[j]) + } + } + res.add(subset) + } + + return res + } +} +``` + +```swift +class Solution { + func subsets(_ nums: [Int]) -> [[Int]] { + let n = nums.count + var res: [[Int]] = [] + + for i in 0..<(1 << n) { + var subset: [Int] = [] + for j in 0.. bool: + if not subRoot: + return True + if not root: + return False + + if self.sameTree(root, subRoot): + return True + return (self.isSubtree(root.left, subRoot) or + self.isSubtree(root.right, subRoot)) + + def sameTree(self, root: Optional[TreeNode], subRoot: Optional[TreeNode]) -> bool: + if not root and not subRoot: + return True + if root and subRoot and root.val == subRoot.val: + return (self.sameTree(root.left, subRoot.left) and + self.sameTree(root.right, subRoot.right)) + return False +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + + public boolean isSubtree(TreeNode root, TreeNode subRoot) { + if (subRoot == null) { + return true; + } + if (root == null) { + return false; + } + + if (sameTree(root, subRoot)) { + return true; + } + return isSubtree(root.left, subRoot) || + isSubtree(root.right, subRoot); + } + + public boolean sameTree(TreeNode root, TreeNode subRoot) { + if (root == null && subRoot == null) { + return true; + } + if (root != null && subRoot != null && root.val == subRoot.val) { + return sameTree(root.left, subRoot.left) && + sameTree(root.right, subRoot.right); + } + return false; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + bool isSubtree(TreeNode* root, TreeNode* subRoot) { + if (!subRoot) { + return true; + } + if (!root) { + return false; + } + + if (sameTree(root, subRoot)) { + return true; + } + return isSubtree(root->left, subRoot) || + isSubtree(root->right, subRoot); + } + + bool sameTree(TreeNode* root, TreeNode* subRoot) { + if (!root && !subRoot) { + return true; + } + if (root && subRoot && root->val == subRoot->val) { + return sameTree(root->left, subRoot->left) && + sameTree(root->right, subRoot->right); + } + return false; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @param {TreeNode} subRoot + * @return {boolean} + */ + isSubtree(root, subRoot) { + if (!subRoot) { + return true; + } + if (!root) { + return false; + } + + if (this.sameTree(root, subRoot)) { + return true; + } + return ( + this.isSubtree(root.left, subRoot) || + this.isSubtree(root.right, subRoot) + ); + } + + /** + * @param {TreeNode} root + * @param {TreeNode} subRoot + * @return {boolean} + */ + sameTree(root, subRoot) { + if (!root && !subRoot) { + return true; + } + if (root && subRoot && root.val === subRoot.val) { + return ( + this.sameTree(root.left, subRoot.left) && + this.sameTree(root.right, subRoot.right) + ); + } + return false; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + + public bool IsSubtree(TreeNode root, TreeNode subRoot) { + if (subRoot == null) { + return true; + } + if (root == null) { + return false; + } + + if (SameTree(root, subRoot)) { + return true; + } + return IsSubtree(root.left, subRoot) || + IsSubtree(root.right, subRoot); + } + + public bool SameTree(TreeNode root, TreeNode subRoot) { + if (root == null && subRoot == null) { + return true; + } + if (root != null && subRoot != null && root.val == subRoot.val) { + return SameTree(root.left, subRoot.left) && + SameTree(root.right, subRoot.right); + } + return false; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func isSubtree(root *TreeNode, subRoot *TreeNode) bool { + if subRoot == nil { + return true + } + if root == nil { + return false + } + + if sameTree(root, subRoot) { + return true + } + return isSubtree(root.Left, subRoot) || isSubtree(root.Right, subRoot) +} + +func sameTree(root *TreeNode, subRoot *TreeNode) bool { + if root == nil && subRoot == nil { + return true + } + if root != nil && subRoot != nil && root.Val == subRoot.Val { + return sameTree(root.Left, subRoot.Left) && sameTree(root.Right, subRoot.Right) + } + return false +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun isSubtree(root: TreeNode?, subRoot: TreeNode?): Boolean { + if (subRoot == null) { + return true + } + if (root == null) { + return false + } + + if (sameTree(root, subRoot)) { + return true + } + return isSubtree(root.left, subRoot) || isSubtree(root.right, subRoot) + } + + fun sameTree(root: TreeNode?, subRoot: TreeNode?): Boolean { + if (root == null && subRoot == null) { + return true + } + if (root != null && subRoot != null && root.`val` == subRoot.`val`) { + return sameTree(root.left, subRoot.left) && sameTree(root.right, subRoot.right) + } + return false + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func isSubtree(_ root: TreeNode?, _ subRoot: TreeNode?) -> Bool { + if subRoot == nil { + return true + } + if root == nil { + return false + } + if sameTree(root, subRoot) { + return true + } + return isSubtree(root?.left, subRoot) || isSubtree(root?.right, subRoot) + } + + func sameTree(_ root: TreeNode?, _ subRoot: TreeNode?) -> Bool { + if root == nil && subRoot == nil { + return true + } + if let root = root, let subRoot = subRoot, root.val == subRoot.val { + return sameTree(root.left, subRoot.left) && sameTree(root.right, subRoot.right) + } + return false + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m + n)$ + +> Where $m$ is the number of nodes in $subRoot$ and $n$ is the number of nodes in $root$. + +--- + +## 2. Serialization And Pattern Matching + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def serialize(self, root: Optional[TreeNode]) -> str: + if root == None: + return "$#" + + return ("$" + str(root.val) + self.serialize(root.left) + self.serialize(root.right)) + + def z_function(self, s: str) -> list: + z = [0] * len(s) + l, r, n = 0, 0, len(s) + for i in range(1, n): + if i <= r: + z[i] = min(r - i + 1, z[i - l]) + while i + z[i] < n and s[z[i]] == s[i + z[i]]: + z[i] += 1 + if i + z[i] - 1 > r: + l, r = i, i + z[i] - 1 + return z + + def isSubtree(self, root: Optional[TreeNode], subRoot: Optional[TreeNode]) -> bool: + serialized_root = self.serialize(root) + serialized_subRoot = self.serialize(subRoot) + combined = serialized_subRoot + "|" + serialized_root + + z_values = self.z_function(combined) + sub_len = len(serialized_subRoot) + + for i in range(sub_len + 1, len(combined)): + if z_values[i] == sub_len: + return True + return False +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public String serialize(TreeNode root) { + if (root == null) { + return "$#"; + } + return "$" + root.val + serialize(root.left) + serialize(root.right); + } + + public int[] z_function(String s) { + int[] z = new int[s.length()]; + int l = 0, r = 0, n = s.length(); + for (int i = 1; i < n; i++) { + if (i <= r) { + z[i] = Math.min(r - i + 1, z[i - l]); + } + while (i + z[i] < n && s.charAt(z[i]) == s.charAt(i + z[i])) { + z[i]++; + } + if (i + z[i] - 1 > r) { + l = i; + r = i + z[i] - 1; + } + } + return z; + } + + public boolean isSubtree(TreeNode root, TreeNode subRoot) { + String serialized_root = serialize(root); + String serialized_subRoot = serialize(subRoot); + String combined = serialized_subRoot + "|" + serialized_root; + + int[] z_values = z_function(combined); + int sub_len = serialized_subRoot.length(); + + for (int i = sub_len + 1; i < combined.length(); i++) { + if (z_values[i] == sub_len) { + return true; + } + } + return false; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + string serialize(TreeNode* root) { + if (root == nullptr) { + return "$#"; + } + return "$" + to_string(root->val) + + serialize(root->left) + serialize(root->right); + } + + vector z_function(string s) { + vector z(s.length()); + int l = 0, r = 0, n = s.length(); + for (int i = 1; i < n; i++) { + if (i <= r) { + z[i] = min(r - i + 1, z[i - l]); + } + while (i + z[i] < n && s[z[i]] == s[i + z[i]]) { + z[i]++; + } + if (i + z[i] - 1 > r) { + l = i; + r = i + z[i] - 1; + } + } + return z; + } + + bool isSubtree(TreeNode* root, TreeNode* subRoot) { + string serialized_root = serialize(root); + string serialized_subRoot = serialize(subRoot); + string combined = serialized_subRoot + "|" + serialized_root; + + vector z_values = z_function(combined); + int sub_len = serialized_subRoot.length(); + + for (int i = sub_len + 1; i < combined.length(); i++) { + if (z_values[i] == sub_len) { + return true; + } + } + return false; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @return {string} + */ + serialize(root) { + if (root === null) { + return "$#"; + } + return "$" + root.val + this.serialize(root.left) + this.serialize(root.right); + } + + /** + * @param {string} s + * @return {number[]} + */ + z_function(s) { + const z = new Array(s.length).fill(0); + let l = 0, r = 0, n = s.length; + for (let i = 1; i < n; i++) { + if (i <= r) { + z[i] = Math.min(r - i + 1, z[i - l]); + } + while (i + z[i] < n && s[z[i]] === s[i + z[i]]) { + z[i]++; + } + if (i + z[i] - 1 > r) { + l = i; + r = i + z[i] - 1; + } + } + return z; + } + + /** + * @param {TreeNode} root + * @param {TreeNode} subRoot + * @return {boolean} + */ + isSubtree(root, subRoot) { + const serialized_root = this.serialize(root); + const serialized_subRoot = this.serialize(subRoot); + const combined = serialized_subRoot + "|" + serialized_root; + + const z_values = this.z_function(combined); + const sub_len = serialized_subRoot.length; + + for (let i = sub_len + 1; i < combined.length; i++) { + if (z_values[i] === sub_len) { + return true; + } + } + return false; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public string Serialize(TreeNode root) { + if (root == null) { + return "$#"; + } + return "$" + root.val + + Serialize(root.left) + Serialize(root.right); + } + + public int[] ZFunction(string s) { + int[] z = new int[s.Length]; + int l = 0, r = 0, n = s.Length; + for (int i = 1; i < n; i++) { + if (i <= r) { + z[i] = Math.Min(r - i + 1, z[i - l]); + } + while (i + z[i] < n && s[z[i]] == s[i + z[i]]) { + z[i]++; + } + if (i + z[i] - 1 > r) { + l = i; + r = i + z[i] - 1; + } + } + return z; + } + + public bool IsSubtree(TreeNode root, TreeNode subRoot) { + string serialized_root = Serialize(root); + string serialized_subRoot = Serialize(subRoot); + string combined = serialized_subRoot + "|" + serialized_root; + + int[] z_values = ZFunction(combined); + int sub_len = serialized_subRoot.Length; + + for (int i = sub_len + 1; i < combined.Length; i++) { + if (z_values[i] == sub_len) { + return true; + } + } + return false; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func serialize(root *TreeNode) string { + if root == nil { + return "$#" + } + return "$" + strconv.Itoa(root.Val) + serialize(root.Left) + serialize(root.Right) +} + +func zFunction(s string) []int { + n := len(s) + z := make([]int, n) + l, r := 0, 0 + + for i := 1; i < n; i++ { + if i <= r { + z[i] = min(r-i+1, z[i-l]) + } + for i+z[i] < n && s[z[i]] == s[i+z[i]] { + z[i]++ + } + if i+z[i]-1 > r { + l = i + r = i + z[i] - 1 + } + } + return z +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +func isSubtree(root *TreeNode, subRoot *TreeNode) bool { + serializedRoot := serialize(root) + serializedSubRoot := serialize(subRoot) + combined := serializedSubRoot + "|" + serializedRoot + + zValues := zFunction(combined) + subLen := len(serializedSubRoot) + + for i := subLen + 1; i < len(combined); i++ { + if zValues[i] == subLen { + return true + } + } + return false +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + private fun serialize(root: TreeNode?): String { + return when (root) { + null -> "$#" + else -> "$${root.`val`}${serialize(root.left)}${serialize(root.right)}" + } + } + + private fun zFunction(s: String): IntArray { + val n = s.length + val z = IntArray(n) + var l = 0 + var r = 0 + + for (i in 1 until n) { + if (i <= r) { + z[i] = minOf(r - i + 1, z[i - l]) + } + while (i + z[i] < n && s[z[i]] == s[i + z[i]]) { + z[i]++ + } + if (i + z[i] - 1 > r) { + l = i + r = i + z[i] - 1 + } + } + return z + } + + fun isSubtree(root: TreeNode?, subRoot: TreeNode?): Boolean { + val serializedRoot = serialize(root) + val serializedSubRoot = serialize(subRoot) + val combined = serializedSubRoot + "|" + serializedRoot + + val zValues = zFunction(combined) + val subLen = serializedSubRoot.length + + return (subLen + 1 until combined.length).any { i -> zValues[i] == subLen } + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func serialize(_ root: TreeNode?) -> String { + guard let root = root else { + return "$#" + } + return "$\(root.val)" + serialize(root.left) + serialize(root.right) + } + + func zFunction(_ s: String) -> [Int] { + let n = s.count + var z = [Int](repeating: 0, count: n) + var l = 0, r = 0 + let chars = Array(s) + + for i in 1.. r { + l = i + r = i + z[i] - 1 + } + } + return z + } + + func isSubtree(_ root: TreeNode?, _ subRoot: TreeNode?) -> Bool { + let serializedRoot = serialize(root) + let serializedSubRoot = serialize(subRoot) + let combined = serializedSubRoot + "|" + serializedRoot + + let zValues = zFunction(combined) + let subLen = serializedSubRoot.count + + for i in (subLen + 1).. Where $m$ is the number of nodes in $subRoot$ and $n$ is the number of nodes in $root$. \ No newline at end of file diff --git a/articles/successful-pairs-of-spells-and-potions.md b/articles/successful-pairs-of-spells-and-potions.md new file mode 100644 index 000000000..87faf3177 --- /dev/null +++ b/articles/successful-pairs-of-spells-and-potions.md @@ -0,0 +1,461 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def successfulPairs(self, spells: List[int], potions: List[int], success: int) -> List[int]: + res = [] + + for s in spells: + cnt = 0 + for p in potions: + if s * p >= success: + cnt += 1 + res.append(cnt) + + return res +``` + +```java +public class Solution { + public int[] successfulPairs(int[] spells, int[] potions, long success) { + int[] res = new int[spells.length]; + + for (int i = 0; i < spells.length; i++) { + int cnt = 0; + for (int p : potions) { + if ((long) spells[i] * p >= success) { + cnt++; + } + } + res[i] = cnt; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector successfulPairs(vector& spells, vector& potions, long long success) { + vector res(spells.size()); + + for (int i = 0; i < spells.size(); i++) { + int cnt = 0; + for (int p : potions) { + if ((long long) spells[i] * p >= success) { + cnt++; + } + } + res[i] = cnt; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} spells + * @param {number[]} potions + * @param {number} success + * @return {number[]} + */ + successfulPairs(spells, potions, success) { + let res = []; + + for (let s of spells) { + let cnt = 0; + for (let p of potions) { + if (s * p >= success) { + cnt++; + } + } + res.push(cnt); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(1)$ + +> Where $n$ and $m$ are the sizes of the arrays $spells$ and $potions$ respectively. + +--- + +## 2. Sorting + Binary Search + +::tabs-start + +```python +class Solution: + def successfulPairs(self, spells: List[int], potions: List[int], success: int) -> List[int]: + potions.sort() + res = [] + + for s in spells: + l, r = 0, len(potions) - 1 + idx = len(potions) + + while l <= r: + m = (l + r) // 2 + if s * potions[m] >= success: + r = m - 1 + idx = m + else: + l = m + 1 + + res.append(len(potions) - idx) + + return res +``` + +```java +public class Solution { + public int[] successfulPairs(int[] spells, int[] potions, long success) { + Arrays.sort(potions); + int[] res = new int[spells.length]; + + for (int i = 0; i < spells.length; i++) { + int l = 0, r = potions.length - 1, idx = potions.length; + + while (l <= r) { + int m = (l + r) / 2; + if ((long) spells[i] * potions[m] >= success) { + r = m - 1; + idx = m; + } else { + l = m + 1; + } + } + + res[i] = potions.length - idx; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector successfulPairs(vector& spells, vector& potions, long long success) { + sort(potions.begin(), potions.end()); + vector res(spells.size()); + + for (int i = 0; i < spells.size(); i++) { + int l = 0, r = potions.size() - 1, idx = potions.size(); + + while (l <= r) { + int m = (l + r) / 2; + if ((long long) spells[i] * potions[m] >= success) { + r = m - 1; + idx = m; + } else { + l = m + 1; + } + } + + res[i] = potions.size() - idx; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} spells + * @param {number[]} potions + * @param {number} success + * @return {number[]} + */ + successfulPairs(spells, potions, success) { + potions.sort((a, b) => a - b); + let res = []; + + for (let s of spells) { + let l = 0, r = potions.length - 1, idx = potions.length; + + while (l <= r) { + let m = Math.floor((l + r) / 2); + if (s * potions[m] >= success) { + r = m - 1; + idx = m; + } else { + l = m + 1; + } + } + + res.push(potions.length - idx); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O((m + n) * \log m)$ +* Space complexity: + * $O(1)$ or $O(m)$ extra space depending on the sorting algorithm. + * $O(n)$ space for the output array. + +> Where $n$ and $m$ are the sizes of the arrays $spells$ and $potions$ respectively. + +--- + +## 3. Sorting + Two Pointers + +::tabs-start + +```python +class Solution: + def successfulPairs(self, spells: List[int], potions: List[int], success: int) -> List[int]: + n, m = len(spells), len(potions) + S = spells[:] + count = defaultdict(int) + spells.sort() + potions.sort() + + j = m - 1 + for i in range(n): + while j >= 0 and spells[i] * potions[j] >= success: + j -= 1 + count[spells[i]] = m - j - 1 + + res = [0] * n + for i in range(n): + res[i] = count[S[i]] + + return res +``` + +```java +public class Solution { + public int[] successfulPairs(int[] spells, int[] potions, long success) { + int n = spells.length, m = potions.length; + int[] S = Arrays.copyOf(spells, n); + Map count = new HashMap<>(); + Arrays.sort(spells); + Arrays.sort(potions); + + int j = m - 1; + for (int i = 0; i < n; i++) { + while (j >= 0 && (long) spells[i] * potions[j] >= success) { + j--; + } + count.put(spells[i], m - j - 1); + } + + int[] res = new int[n]; + for (int i = 0; i < n; i++) { + res[i] = count.get(S[i]); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector successfulPairs(vector& spells, vector& potions, long long success) { + int n = spells.size(), m = potions.size(); + vector S = spells; + unordered_map count; + sort(spells.begin(), spells.end()); + sort(potions.begin(), potions.end()); + + int j = m - 1; + for (int i = 0; i < n; i++) { + while (j >= 0 && (long long) spells[i] * potions[j] >= success) { + j--; + } + count[spells[i]] = m - j - 1; + } + + vector res(n); + for (int i = 0; i < n; i++) { + res[i] = count[S[i]]; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} spells + * @param {number[]} potions + * @param {number} success + * @return {number[]} + */ + successfulPairs(spells, potions, success) { + const n = spells.length, m = potions.length; + const S = [...spells]; + const count = new Map(); + spells.sort((a, b) => a - b); + potions.sort((a, b) => a - b); + + let j = m - 1; + for (let i = 0; i < n; i++) { + while (j >= 0 && spells[i] * potions[j] >= success) { + j--; + } + count.set(spells[i], m - j - 1); + } + + return S.map(s => count.get(s)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + m\log m)$ +* Space complexity: + * $O(1)$ or $O(m + n)$ extra space depending on the sorting algorithm. + * $O(n)$ space for the output array. + +> Where $n$ and $m$ are the sizes of the arrays $spells$ and $potions$ respectively. + +--- + +## 4. Sorting + Two Pointers (Optimal) + +::tabs-start + +```python +class Solution: + def successfulPairs(self, spells: List[int], potions: List[int], success: int) -> List[int]: + n, m = len(spells), len(potions) + sIdx = list(range(n)) + sIdx.sort(key=lambda x: spells[x]) + potions.sort() + + j = m - 1 + res = [0] * n + for i in range(n): + while j >= 0 and spells[sIdx[i]] * potions[j] >= success: + j -= 1 + res[sIdx[i]] = m - j - 1 + + return res +``` + +```java +public class Solution { + public int[] successfulPairs(int[] spells, int[] potions, long success) { + int n = spells.length, m = potions.length; + Integer[] sIdx = new Integer[n]; + for (int i = 0; i < n; i++) sIdx[i] = i; + + Arrays.sort(sIdx, Comparator.comparingInt(i -> spells[i])); + Arrays.sort(potions); + + int j = m - 1; + int[] res = new int[n]; + for (int i = 0; i < n; i++) { + while (j >= 0 && (long) spells[sIdx[i]] * potions[j] >= success) { + j--; + } + res[sIdx[i]] = m - j - 1; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector successfulPairs(vector& spells, vector& potions, long long success) { + int n = spells.size(), m = potions.size(); + vector sIdx(n); + for (int i = 0; i < n; i++) sIdx[i] = i; + + sort(sIdx.begin(), sIdx.end(), [&](int a, int b) { + return spells[a] < spells[b]; + }); + + sort(potions.begin(), potions.end()); + + int j = m - 1; + vector res(n); + for (int i = 0; i < n; i++) { + while (j >= 0 && (long long) spells[sIdx[i]] * potions[j] >= success) { + j--; + } + res[sIdx[i]] = m - j - 1; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} spells + * @param {number[]} potions + * @param {number} success + * @return {number[]} + */ + successfulPairs(spells, potions, success) { + const n = spells.length, m = potions.length; + const sIdx = Array.from({ length: n }, (_, i) => i); + + sIdx.sort((a, b) => spells[a] - spells[b]); + potions.sort((a, b) => a - b); + + let j = m - 1; + const res = new Array(n).fill(0); + + for (let i = 0; i < n; i++) { + while (j >= 0 && spells[sIdx[i]] * potions[j] >= success) { + j--; + } + res[sIdx[i]] = m - j - 1; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n + m\log m)$ +* Space complexity: + * $O(1)$ or $O(m + n)$ extra space depending on the sorting algorithm. + * $O(n)$ space for the output array. + +> Where $n$ and $m$ are the sizes of the arrays $spells$ and $potions$ respectively. \ No newline at end of file diff --git a/articles/sum-of-absolute-differences-in-a-sorted-array.md b/articles/sum-of-absolute-differences-in-a-sorted-array.md new file mode 100644 index 000000000..f705f47d7 --- /dev/null +++ b/articles/sum-of-absolute-differences-in-a-sorted-array.md @@ -0,0 +1,430 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def getSumAbsoluteDifferences(self, nums: List[int]) -> List[int]: + res = [] + + for i in nums: + sum = 0 + for j in nums: + sum += abs(i - j) + res.append(sum) + + return res +``` + +```java +public class Solution { + public int[] getSumAbsoluteDifferences(int[] nums) { + int n = nums.length; + int[] res = new int[n]; + + for (int i = 0; i < n; i++) { + int sum = 0; + for (int j = 0; j < n; j++) { + sum += Math.abs(nums[i] - nums[j]); + } + res[i] = sum; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector getSumAbsoluteDifferences(vector& nums) { + int n = nums.size(); + vector res; + + for (int i = 0; i < n; i++) { + int sum = 0; + for (int j = 0; j < n; j++) { + sum += abs(nums[i] - nums[j]); + } + res.push_back(sum); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + getSumAbsoluteDifferences(nums) { + const n = nums.length; + const res = []; + + for (let i = 0; i < n; i++) { + let sum = 0; + for (let j = 0; j < n; j++) { + sum += Math.abs(nums[i] - nums[j]); + } + res.push(sum); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ for the output array. + +--- + +## 2. Prefix & Suffix Sums (Extra Space) + +::tabs-start + +```python +class Solution: + def getSumAbsoluteDifferences(self, nums: List[int]) -> List[int]: + n = len(nums) + prefix_sum = [0] * n + suffix_sum = [0] * n + res = [0] * n + + prefix_sum[0] = nums[0] + for i in range(1, n): + prefix_sum[i] = prefix_sum[i - 1] + nums[i] + + suffix_sum[n - 1] = nums[n - 1] + for i in range(n - 2, -1, -1): + suffix_sum[i] = suffix_sum[i + 1] + nums[i] + + for i in range(n): + left_sum = (i * nums[i]) - (prefix_sum[i - 1] if i > 0 else 0) + right_sum = (suffix_sum[i + 1] if i < n - 1 else 0) - ((n - i - 1) * nums[i]) + res[i] = left_sum + right_sum + + return res +``` + +```java +public class Solution { + public int[] getSumAbsoluteDifferences(int[] nums) { + int n = nums.length; + int[] prefixSum = new int[n]; + int[] suffixSum = new int[n]; + int[] res = new int[n]; + + prefixSum[0] = nums[0]; + for (int i = 1; i < n; i++) { + prefixSum[i] = prefixSum[i - 1] + nums[i]; + } + + suffixSum[n - 1] = nums[n - 1]; + for (int i = n - 2; i >= 0; i--) { + suffixSum[i] = suffixSum[i + 1] + nums[i]; + } + + for (int i = 0; i < n; i++) { + int leftSum = i > 0 ? (i * nums[i] - prefixSum[i - 1]) : 0; + int rightSum = i < n - 1 ? (suffixSum[i + 1] - (n - i - 1) * nums[i]) : 0; + res[i] = leftSum + rightSum; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector getSumAbsoluteDifferences(vector& nums) { + int n = nums.size(); + vector prefixSum(n, 0), suffixSum(n, 0), res(n, 0); + + prefixSum[0] = nums[0]; + for (int i = 1; i < n; i++) { + prefixSum[i] = prefixSum[i - 1] + nums[i]; + } + + suffixSum[n - 1] = nums[n - 1]; + for (int i = n - 2; i >= 0; i--) { + suffixSum[i] = suffixSum[i + 1] + nums[i]; + } + + for (int i = 0; i < n; i++) { + int leftSum = i > 0 ? (i * nums[i] - prefixSum[i - 1]) : 0; + int rightSum = i < n - 1 ? (suffixSum[i + 1] - (n - i - 1) * nums[i]) : 0; + res[i] = leftSum + rightSum; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + getSumAbsoluteDifferences(nums) { + const n = nums.length; + const prefixSum = Array(n).fill(0); + const suffixSum = Array(n).fill(0); + const res = Array(n).fill(0); + + prefixSum[0] = nums[0]; + for (let i = 1; i < n; i++) { + prefixSum[i] = prefixSum[i - 1] + nums[i]; + } + + suffixSum[n - 1] = nums[n - 1]; + for (let i = n - 2; i >= 0; i--) { + suffixSum[i] = suffixSum[i + 1] + nums[i]; + } + + for (let i = 0; i < n; i++) { + const leftSum = i > 0 ? (i * nums[i] - prefixSum[i - 1]) : 0; + const rightSum = i < n - 1 ? (suffixSum[i + 1] - (n - i - 1) * nums[i]) : 0; + res[i] = leftSum + rightSum; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Prefix & Suffix Sums + +::tabs-start + +```python +class Solution: + def getSumAbsoluteDifferences(self, nums: List[int]) -> List[int]: + n = len(nums) + res = [0] * n + + res[n - 1] = nums[n - 1] + for i in range(n - 2, -1, -1): + res[i] = res[i + 1] + nums[i] + + prefix_sum = 0 + for i in range(n): + left_sum = (i * nums[i]) - prefix_sum + right_sum = (res[i + 1] if i < n - 1 else 0) - ((n - i - 1) * nums[i]) + res[i] = left_sum + right_sum + prefix_sum += nums[i] + + return res +``` + +```java +public class Solution { + public int[] getSumAbsoluteDifferences(int[] nums) { + int n = nums.length; + int[] res = new int[n]; + + res[n - 1] = nums[n - 1]; + for (int i = n - 2; i >= 0; i--) { + res[i] = res[i + 1] + nums[i]; + } + + int prefixSum = 0; + for (int i = 0; i < n; i++) { + int leftSum = i * nums[i] - prefixSum; + int rightSum = i < n - 1 ? (res[i + 1] - (n - i - 1) * nums[i]) : 0; + res[i] = leftSum + rightSum; + prefixSum += nums[i]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector getSumAbsoluteDifferences(vector& nums) { + int n = nums.size(); + vector res(n, 0); + + res[n - 1] = nums[n - 1]; + for (int i = n - 2; i >= 0; i--) { + res[i] = res[i + 1] + nums[i]; + } + + int prefixSum = 0; + for (int i = 0; i < n; i++) { + int leftSum = i * nums[i] - prefixSum; + int rightSum = i < n - 1 ? (res[i + 1] - (n - i - 1) * nums[i]) : 0; + res[i] = leftSum + rightSum; + prefixSum += nums[i]; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + getSumAbsoluteDifferences(nums) { + const n = nums.length; + const res = Array(n).fill(0); + + res[n - 1] = nums[n - 1]; + for (let i = n - 2; i >= 0; i--) { + res[i] = res[i + 1] + nums[i]; + } + + let prefixSum = 0; + for (let i = 0; i < n; i++) { + const leftSum = i * nums[i] - prefixSum; + const rightSum = i < n - 1 ? (res[i + 1] - (n - i - 1) * nums[i]) : 0; + res[i] = leftSum + rightSum; + prefixSum += nums[i]; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for the output array. + +--- + +## 4. Prefix & Suffix Sums (Optimal) + +::tabs-start + +```python +class Solution: + def getSumAbsoluteDifferences(self, nums: List[int]) -> List[int]: + n = len(nums) + res = [0] * n + + total_sum = sum(nums) + prefix_sum = 0 + + for i, num in enumerate(nums): + total_sum -= nums[i] + left_sum = i * nums[i] - prefix_sum + right_sum = total_sum - (n - i - 1) * nums[i] + res[i] = left_sum + right_sum + prefix_sum += nums[i] + + return res +``` + +```java +public class Solution { + public int[] getSumAbsoluteDifferences(int[] nums) { + int n = nums.length; + int[] res = new int[n]; + + int totalSum = 0, prefixSum = 0; + for (int num : nums) { + totalSum += num; + } + + for (int i = 0; i < n; i++) { + totalSum -= nums[i]; + int leftSum = i * nums[i] - prefixSum; + int rightSum = totalSum - (n - i - 1) * nums[i]; + res[i] = leftSum + rightSum; + prefixSum += nums[i]; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector getSumAbsoluteDifferences(vector& nums) { + int n = nums.size(); + vector res(n, 0); + + int totalSum = 0, prefixSum = 0; + for (int& num : nums) { + totalSum += num; + } + + for (int i = 0; i < n; i++) { + totalSum -= nums[i]; + int leftSum = i * nums[i] - prefixSum; + int rightSum = totalSum - (n - i - 1) * nums[i]; + res[i] = leftSum + rightSum; + prefixSum += nums[i]; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[]} + */ + getSumAbsoluteDifferences(nums) { + const n = nums.length; + const res = Array(n).fill(0); + + let totalSum = nums.reduce((sum, num) => sum + num, 0); + let prefixSum = 0; + + for (let i = 0; i < n; i++) { + totalSum -= nums[i]; + const leftSum = i * nums[i] - prefixSum; + const rightSum = totalSum - (n - i - 1) * nums[i]; + res[i] = leftSum + rightSum; + prefixSum += nums[i]; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for the output array. \ No newline at end of file diff --git a/articles/sum-of-all-subset-xor-totals.md b/articles/sum-of-all-subset-xor-totals.md new file mode 100644 index 000000000..b35c24e37 --- /dev/null +++ b/articles/sum-of-all-subset-xor-totals.md @@ -0,0 +1,414 @@ +## 1. Backtracking + +::tabs-start + +```python +class Solution: + def subsetXORSum(self, nums: List[int]) -> int: + res = 0 + + def backtrack(i, subset): + nonlocal res + xorr = 0 + for num in subset: + xorr ^= num + res += xorr + + for j in range(i, len(nums)): + subset.append(nums[j]) + backtrack(j + 1, subset) + subset.pop() + + backtrack(0, []) + return res +``` + +```java +public class Solution { + int res = 0; + + public int subsetXORSum(int[] nums) { + backtrack(0, nums, new ArrayList<>()); + return res; + } + + private void backtrack(int i, int[] nums, List subset) { + int xorr = 0; + for (int num : subset) xorr ^= num; + res += xorr; + + for (int j = i; j < nums.length; j++) { + subset.add(nums[j]); + backtrack(j + 1, nums, subset); + subset.remove(subset.size() - 1); + } + } +} +``` + +```cpp +class Solution { +public: + int subsetXORSum(vector& nums) { + int res = 0; + vector subset; + + function backtrack = [&](int i) { + int xorr = 0; + for (int num : subset) xorr ^= num; + res += xorr; + + for (int j = i; j < nums.size(); ++j) { + subset.push_back(nums[j]); + backtrack(j + 1); + subset.pop_back(); + } + }; + + backtrack(0); + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + subsetXORSum(nums) { + let res = 0; + + const backtrack = (i, subset) => { + let xorr = 0; + for (let num of subset) xorr ^= num; + res += xorr; + + for (let j = i; j < nums.length; j++) { + subset.push(nums[j]); + backtrack(j + 1, subset); + subset.pop(); + } + }; + + backtrack(0, []); + return res; + } +} +``` + +```csharp +public class Solution { + private int res = 0; + + public int SubsetXORSum(int[] nums) { + Backtrack(0, new List(), nums); + return res; + } + + private void Backtrack(int i, List subset, int[] nums) { + int xorr = 0; + foreach (int num in subset) { + xorr ^= num; + } + res += xorr; + + for (int j = i; j < nums.Length; j++) { + subset.Add(nums[j]); + Backtrack(j + 1, subset, nums); + subset.RemoveAt(subset.Count - 1); + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Recursion + +::tabs-start + +```python +class Solution: + def subsetXORSum(self, nums: List[int]) -> int: + def dfs(i, total): + if i == len(nums): + return total + return dfs(i + 1, total ^ nums[i]) + dfs(i + 1, total) + + return dfs(0, 0) +``` + +```java +public class Solution { + public int subsetXORSum(int[] nums) { + return dfs(nums, 0, 0); + } + + private int dfs(int[] nums, int i, int total) { + if (i == nums.length) { + return total; + } + return dfs(nums, i + 1, total ^ nums[i]) + dfs(nums, i + 1, total); + } +} +``` + +```cpp +class Solution { +public: + int subsetXORSum(vector& nums) { + return dfs(nums, 0, 0); + } + +private: + int dfs(vector& nums, int i, int total) { + if (i == nums.size()) { + return total; + } + return dfs(nums, i + 1, total ^ nums[i]) + dfs(nums, i + 1, total); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + subsetXORSum(nums) { + const dfs = (i, total) => { + if (i === nums.length) { + return total; + } + return dfs(i + 1, total ^ nums[i]) + dfs(i + 1, total); + }; + + return dfs(0, 0); + } +} +``` + +```csharp +public class Solution { + public int SubsetXORSum(int[] nums) { + return Dfs(0, 0, nums); + } + + private int Dfs(int i, int total, int[] nums) { + if (i == nums.Length) { + return total; + } + return Dfs(i + 1, total ^ nums[i], nums) + Dfs(i + 1, total, nums); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Bit Manipulation + +::tabs-start + +```python +class Solution: + def subsetXORSum(self, nums: List[int]) -> int: + n = len(nums) + res = 0 + + for mask in range(1 << n): + xorr = 0 + for i in range(n): + if mask & (1 << i): + xorr ^= nums[i] + res += xorr + + return res +``` + +```java +public class Solution { + public int subsetXORSum(int[] nums) { + int n = nums.length; + int res = 0; + + for (int mask = 0; mask < (1 << n); mask++) { + int xorr = 0; + for (int i = 0; i < n; i++) { + if ((mask & ( 1 << i)) != 0) { + xorr ^= nums[i]; + } + } + res += xorr; + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int subsetXORSum(vector& nums) { + int n = nums.size(); + int res = 0; + + for (int mask = 0; mask < (1 << n); mask++) { + int xorr = 0; + for (int i = 0; i < n; i++) { + if ((mask & ( 1 << i)) != 0) { + xorr ^= nums[i]; + } + } + res += xorr; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + subsetXORSum(nums) { + const n = nums.length; + let res = 0; + + for (let mask = 0; mask < (1 << n); mask++) { + let xorr = 0; + for (let i = 0; i < n; i++) { + if ((mask & ( 1 << i)) !== 0) { + xorr ^= nums[i]; + } + } + res += xorr; + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int SubsetXORSum(int[] nums) { + int n = nums.Length; + int res = 0; + + for (int mask = 0; mask < (1 << n); mask++) { + int xorr = 0; + for (int i = 0; i < n; i++) { + if ((mask & (1 << i)) != 0) { + xorr ^= nums[i]; + } + } + res += xorr; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * 2 ^ n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 4. Bit Manipulation (Optimal) + +::tabs-start + +```python +class Solution: + def subsetXORSum(self, nums: List[int]) -> int: + res = 0 + for num in nums: + res |= num + return res << (len(nums) - 1) +``` + +```java +public class Solution { + public int subsetXORSum(int[] nums) { + int res = 0; + for (int num : nums) { + res |= num; + } + return res << (nums.length - 1); + } +} +``` + +```cpp +class Solution { +public: + int subsetXORSum(vector& nums) { + int res = 0; + for (int& num : nums) { + res |= num; + } + return res << (nums.size() - 1); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number} + */ + subsetXORSum(nums) { + let res = 0; + for (let num of nums) { + res |= num; + } + return res << (nums.length - 1); + } +} +``` + +```csharp +public class Solution { + public int SubsetXORSum(int[] nums) { + int res = 0; + foreach (int num in nums) { + res |= num; + } + return res << (nums.Length - 1); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/sum-of-subarray-minimums.md b/articles/sum-of-subarray-minimums.md new file mode 100644 index 000000000..f1103db9b --- /dev/null +++ b/articles/sum-of-subarray-minimums.md @@ -0,0 +1,596 @@ +## 1. Brute FOrce + +::tabs-start + +```python +class Solution: + def sumSubarrayMins(self, arr: List[int]) -> int: + n, res = len(arr), 0 + MOD = 1000000007 + + for i in range(n): + minVal = arr[i] + for j in range(i, n): + minVal = min(minVal, arr[j]) + res = (res + minVal) % MOD + + return res +``` + +```java +public class Solution { + public int sumSubarrayMins(int[] arr) { + int n = arr.length, res = 0; + int MOD = 1000000007; + + for (int i = 0; i < n; i++) { + int minVal = arr[i]; + for (int j = i; j < n; j++) { + minVal = Math.min(minVal, arr[j]); + res = (res + minVal) % MOD; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int sumSubarrayMins(vector& arr) { + int n = arr.size(), res = 0; + const int MOD = 1000000007; + + for (int i = 0; i < n; i++) { + int minVal = arr[i]; + for (int j = i; j < n; j++) { + minVal = min(minVal, arr[j]); + res = (res + minVal) % MOD; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + sumSubarrayMins(arr) { + const n = arr.length; + let res = 0; + const MOD = 1000000007; + + for (let i = 0; i < n; i++) { + let minVal = arr[i]; + for (let j = i; j < n; j++) { + minVal = Math.min(minVal, arr[j]); + res = (res + minVal) % MOD; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ + +--- + +## 2. Monotonically Increasing Stack (Two Pass) + +::tabs-start + +```python +class Solution: + def sumSubarrayMins(self, arr: List[int]) -> int: + MOD = 10**9 + 7 + n = len(arr) + + # Compute previous smaller + prev_smaller = [-1] * n + stack = [] + for i in range(n): + while stack and arr[stack[-1]] > arr[i]: + stack.pop() + prev_smaller[i] = stack[-1] if stack else -1 + stack.append(i) + + # Compute next smaller + next_smaller = [n] * n + stack = [] + for i in range(n - 1, -1, -1): + while stack and arr[stack[-1]] >= arr[i]: + stack.pop() + next_smaller[i] = stack[-1] if stack else n + stack.append(i) + + res = 0 + for i in range(n): + left = i - prev_smaller[i] + right = next_smaller[i] - i + res = (res + arr[i] * left * right) % MOD + + return res +``` + +```java +public class Solution { + public int sumSubarrayMins(int[] arr) { + int MOD = 1000000007; + int n = arr.length; + + // Compute previous smaller + int[] prevSmaller = new int[n]; + Stack stack = new Stack<>(); + for (int i = 0; i < n; i++) { + while (!stack.isEmpty() && arr[stack.peek()] > arr[i]) { + stack.pop(); + } + prevSmaller[i] = stack.isEmpty() ? -1 : stack.peek(); + stack.push(i); + } + + // Compute next smaller + int[] nextSmaller = new int[n]; + stack = new Stack<>(); + for (int i = n - 1; i >= 0; i--) { + while (!stack.isEmpty() && arr[stack.peek()] >= arr[i]) { + stack.pop(); + } + nextSmaller[i] = stack.isEmpty() ? n : stack.peek(); + stack.push(i); + } + + // Calculate result + long res = 0; + for (int i = 0; i < n; i++) { + long left = i - prevSmaller[i]; + long right = nextSmaller[i] - i; + res = (res + arr[i] * left * right) % MOD; + } + + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + int sumSubarrayMins(vector& arr) { + const int MOD = 1e9 + 7; + int n = arr.size(); + + // Compute previous smaller + vector prevSmaller(n, -1); + stack stack; + for (int i = 0; i < n; i++) { + while (!stack.empty() && arr[stack.top()] > arr[i]) { + stack.pop(); + } + prevSmaller[i] = stack.empty() ? -1 : stack.top(); + stack.push(i); + } + + // Compute next smaller + vector nextSmaller(n, n); + stack = {}; + for (int i = n - 1; i >= 0; i--) { + while (!stack.empty() && arr[stack.top()] >= arr[i]) { + stack.pop(); + } + nextSmaller[i] = stack.empty() ? n : stack.top(); + stack.push(i); + } + + // Calculate result + long long res = 0; + for (int i = 0; i < n; i++) { + long long left = i - prevSmaller[i]; + long long right = nextSmaller[i] - i; + res = (res + arr[i] * left * right) % MOD; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + sumSubarrayMins(arr) { + const MOD = 1e9 + 7; + const n = arr.length; + + // Compute previous smaller + const prevSmaller = new Array(n).fill(-1); + const stack = []; + for (let i = 0; i < n; i++) { + while (stack.length > 0 && arr[stack[stack.length - 1]] > arr[i]) { + stack.pop(); + } + prevSmaller[i] = stack.length > 0 ? stack[stack.length - 1] : -1; + stack.push(i); + } + + // Compute next smaller + const nextSmaller = new Array(n).fill(n); + stack.length = 0; + for (let i = n - 1; i >= 0; i--) { + while (stack.length > 0 && arr[stack[stack.length - 1]] >= arr[i]) { + stack.pop(); + } + nextSmaller[i] = stack.length > 0 ? stack[stack.length - 1] : n; + stack.push(i); + } + + // Calculate result + let res = 0; + for (let i = 0; i < n; i++) { + const left = i - prevSmaller[i]; + const right = nextSmaller[i] - i; + res = (res + arr[i] * left * right) % MOD; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Monotonically Increasing Stack (One Pass) + +::tabs-start + +```python +class Solution: + def sumSubarrayMins(self, arr: List[int]) -> int: + MOD = 10 ** 9 + 7 + res = 0 + arr = [float("-inf")] + arr + [float("-inf")] + stack = [] # (index, num) + + for i, n in enumerate(arr): + while stack and n < stack[-1][1]: + j, m = stack.pop() + left = j - stack[-1][0] if stack else j + 1 + right = i - j + res = (res + m * left * right) % MOD + stack.append((i, n)) + + return res +``` + +```java +public class Solution { + public int sumSubarrayMins(int[] arr) { + int MOD = 1000000007; + int res = 0; + int[] newArr = new int[arr.length + 2]; + newArr[0] = Integer.MIN_VALUE; + newArr[newArr.length - 1] = Integer.MIN_VALUE; + System.arraycopy(arr, 0, newArr, 1, arr.length); + + Stack stack = new Stack<>(); + + for (int i = 0; i < newArr.length; i++) { + while (!stack.isEmpty() && newArr[i] < stack.peek()[1]) { + int[] top = stack.pop(); + int j = top[0], m = top[1]; + int left = stack.isEmpty() ? j + 1 : j - stack.peek()[0]; + int right = i - j; + res = (int) ((res + (long) m * left * right) % MOD); + } + stack.push(new int[]{i, newArr[i]}); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int sumSubarrayMins(vector& arr) { + const int MOD = 1e9 + 7; + int res = 0; + vector newArr(arr.size() + 2, INT_MIN); + copy(arr.begin(), arr.end(), newArr.begin() + 1); + + stack> stack; + + for (int i = 0; i < newArr.size(); i++) { + while (!stack.empty() && newArr[i] < stack.top().second) { + auto [j, m] = stack.top(); + stack.pop(); + int left = stack.empty() ? j + 1 : j - stack.top().first; + int right = i - j; + res = (res + (long long) m * left * right % MOD) % MOD; + } + stack.emplace(i, newArr[i]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + sumSubarrayMins(arr) { + const MOD = 1e9 + 7; + let res = 0; + arr = [-Infinity, ...arr, -Infinity]; + let stack = []; + + for (let i = 0; i < arr.length; i++) { + while (stack.length > 0 && arr[i] < stack[stack.length - 1][1]) { + let [j, m] = stack.pop(); + let left = stack.length > 0 ? j - stack[stack.length - 1][0] : j + 1; + let right = i - j; + res = (res + m * left * right) % MOD; + } + stack.push([i, arr[i]]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Monotonically Increasing Stack (Optimal) + +::tabs-start + +```python +class Solution: + def sumSubarrayMins(self, arr: List[int]) -> int: + MOD = 10**9 + 7 + stack = [] + res, n = 0, len(arr) + + for i in range(n + 1): + while stack and (i == n or arr[i] < arr[stack[-1]]): + j = stack.pop() + left = j - (stack[-1] if stack else -1) + right = i - j + res = (res + arr[j] * left * right) % MOD + stack.append(i) + + return res +``` + +```java +public class Solution { + public int sumSubarrayMins(int[] arr) { + int MOD = 1000000007; + int n = arr.length; + Stack stack = new Stack<>(); + long res = 0; + + for (int i = 0; i <= n; i++) { + while (!stack.isEmpty() && (i == n || arr[i] < arr[stack.peek()])) { + int j = stack.pop(); + int left = j - (stack.isEmpty() ? -1 : stack.peek()); + int right = i - j; + res = (res + (long) arr[j] * left * right) % MOD; + } + stack.push(i); + } + + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + int sumSubarrayMins(vector& arr) { + const int MOD = 1e9 + 7; + int n = arr.size(); + stack stack; + long long res = 0; + + for (int i = 0; i <= n; i++) { + while (!stack.empty() && (i == n || arr[i] < arr[stack.top()])) { + int j = stack.top(); + stack.pop(); + int left = j - (stack.empty() ? -1 : stack.top()); + int right = i - j; + res = (res + (long long) arr[j] * left * right) % MOD; + } + stack.push(i); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + sumSubarrayMins(arr) { + const MOD = 1e9 + 7; + const n = arr.length; + const stack = []; + let res = 0; + + for (let i = 0; i <= n; i++) { + while (stack.length > 0 && (i === n || arr[i] < arr[stack[stack.length - 1]])) { + const j = stack.pop(); + const left = j - (stack.length > 0 ? stack[stack.length - 1] : -1); + const right = i - j; + res = (res + arr[j] * left * right) % MOD; + } + stack.push(i); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 5. Dynamic Programming + Stack + +::tabs-start + +```python +class Solution: + def sumSubarrayMins(self, arr: List[int]) -> int: + MOD = 10**9 + 7 + n = len(arr) + dp = [0] * n + stack, res = [], 0 + + for i in range(n): + while stack and arr[stack[-1]] > arr[i]: + stack.pop() + + j = stack[-1] if stack else -1 + dp[i] = (dp[j] if j != -1 else 0) + arr[i] * (i - j) + dp[i] %= MOD + res = (res + dp[i]) % MOD + stack.append(i) + + return res +``` + +```java +public class Solution { + public int sumSubarrayMins(int[] arr) { + int MOD = 1000000007; + int n = arr.length; + int[] dp = new int[n]; + Stack stack = new Stack<>(); + long res = 0; + + for (int i = 0; i < n; i++) { + while (!stack.isEmpty() && arr[stack.peek()] > arr[i]) { + stack.pop(); + } + + int j = stack.isEmpty() ? -1 : stack.peek(); + dp[i] = ((j != -1 ? dp[j] : 0) + arr[i] * (i - j)) % MOD; + res = (res + dp[i]) % MOD; + stack.push(i); + } + + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + int sumSubarrayMins(vector& arr) { + const int MOD = 1e9 + 7; + int n = arr.size(); + vector dp(n, 0); + stack stack; + long long res = 0; + + for (int i = 0; i < n; i++) { + while (!stack.empty() && arr[stack.top()] > arr[i]) { + stack.pop(); + } + + int j = stack.empty() ? -1 : stack.top(); + dp[i] = ((j != -1 ? dp[j] : 0) + arr[i] * (i - j)) % MOD; + res = (res + dp[i]) % MOD; + stack.push(i); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} arr + * @return {number} + */ + sumSubarrayMins(arr) { + const MOD = 1e9 + 7; + const n = arr.length; + const dp = new Array(n).fill(0); + const stack = []; + let res = 0; + + for (let i = 0; i < n; i++) { + while (stack.length > 0 && arr[stack[stack.length - 1]] > arr[i]) { + stack.pop(); + } + + const j = stack.length > 0 ? stack[stack.length - 1] : -1; + dp[i] = ((j !== -1 ? dp[j] : 0) + arr[i] * (i - j)) % MOD; + res = (res + dp[i]) % MOD; + stack.push(i); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/sum-of-two-integers.md b/articles/sum-of-two-integers.md new file mode 100644 index 000000000..6d43c0a14 --- /dev/null +++ b/articles/sum-of-two-integers.md @@ -0,0 +1,440 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def getSum(self, a: int, b: int) -> int: + return a + b +``` + +```java +public class Solution { + public int getSum(int a, int b) { + return a + b; + } +} +``` + +```cpp +class Solution { +public: + int getSum(int a, int b) { + return a + b; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} a + * @param {number} b + * @return {number} + */ + getSum(a, b) { + return a + b; + } +} +``` + +```csharp +public class Solution { + public int GetSum(int a, int b) { + return a + b; + } +} +``` + +```go +func getSum(a int, b int) int { + return a + b +} +``` + +```kotlin +class Solution { + fun getSum(a: Int, b: Int): Int { + return a + b + } +} +``` + +```swift +class Solution { + func getSum(_ a: Int, _ b: Int) -> Int { + return a + b + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 2. Bit Manipulation + +::tabs-start + +```python +class Solution: + def getSum(self, a: int, b: int) -> int: + carry = 0 + res = 0 + mask = 0xFFFFFFFF + + for i in range(32): + a_bit = (a >> i) & 1 + b_bit = (b >> i) & 1 + cur_bit = a_bit ^ b_bit ^ carry + carry = (a_bit + b_bit + carry) >= 2 + if cur_bit: + res |= (1 << i) + + if res > 0x7FFFFFFF: + res = ~(res ^ mask) + + return res +``` + +```java +public class Solution { + public int getSum(int a, int b) { + int carry = 0, res = 0, mask = 0xFFFFFFFF; + + for (int i = 0; i < 32; i++) { + int a_bit = (a >> i) & 1; + int b_bit = (b >> i) & 1; + int cur_bit = a_bit ^ b_bit ^ carry; + carry = (a_bit + b_bit + carry) >= 2 ? 1 : 0; + if (cur_bit != 0) { + res |= (1 << i); + } + } + + if (res > 0x7FFFFFFF) { + res = ~(res ^ mask); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int getSum(int a, int b) { + int carry = 0, res = 0, mask = 0xFFFFFFFF; + + for (int i = 0; i < 32; i++) { + int a_bit = (a >> i) & 1; + int b_bit = (b >> i) & 1; + int cur_bit = a_bit ^ b_bit ^ carry; + carry = (a_bit + b_bit + carry) >= 2 ? 1 : 0; + if (cur_bit) { + res |= (1 << i); + } + } + + if (res > 0x7FFFFFFF) { + res = ~(res ^ mask); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} a + * @param {number} b + * @return {number} + */ + getSum(a, b) { + let carry = 0, res = 0, mask = 0xFFFFFFFF; + + for (let i = 0; i < 32; i++) { + let a_bit = (a >> i) & 1; + let b_bit = (b >> i) & 1; + let cur_bit = a_bit ^ b_bit ^ carry; + carry = (a_bit + b_bit + carry) >= 2 ? 1 : 0; + if (cur_bit) { + res |= (1 << i); + } + } + + if (res > 0x7FFFFFFF) { + res = ~(res ^ mask); + } + + return res; + } +} +``` + +```csharp +public class Solution { + public int GetSum(int a, int b) { + int carry = 0, res = 0, mask = unchecked((int)0xFFFFFFFF); + + for (int i = 0; i < 32; i++) { + int a_bit = (a >> i) & 1; + int b_bit = (b >> i) & 1; + int cur_bit = a_bit ^ b_bit ^ carry; + carry = (a_bit + b_bit + carry) >= 2 ? 1 : 0; + if (cur_bit != 0) { + res |= (1 << i); + } + } + + if (res > Int32.MaxValue) { + res = ~(res ^ mask); + } + + return res; + } +} +``` + +```go +func getSum(a int, b int) int { + carry := 0 + res := 0 + mask := 0xFFFFFFFF + + for i := 0; i < 32; i++ { + aBit := (a >> i) & 1 + bBit := (b >> i) & 1 + curBit := aBit ^ bBit ^ carry + if (aBit + bBit + carry) >= 2 { + carry = 1 + } else { + carry = 0 + } + if curBit == 1 { + res |= (1 << i) + } + } + + if res > 0x7FFFFFFF { + res = ^(res ^ mask) + } + + return res +} +``` + +```kotlin +class Solution { + fun getSum(a: Int, b: Int): Int { + var carry = 0 + var res = 0 + val mask = 0xFFFFFFFF.toInt() + + for (i in 0 until 32) { + val aBit = (a shr i) and 1 + val bBit = (b shr i) and 1 + val curBit = aBit xor bBit xor carry + carry = if (aBit + bBit + carry >= 2) 1 else 0 + if (curBit == 1) { + res = res or (1 shl i) + } + } + + if (res > 0x7FFFFFFF) { + res = res.inv() xor mask + } + + return res + } +} +``` + +```swift +class Solution { + func getSum(_ a: Int, _ b: Int) -> Int { + var carry = 0 + var res = 0 + let mask = 0xFFFFFFFF + + for i in 0..<32 { + let aBit = (a >> i) & 1 + let bBit = (b >> i) & 1 + let curBit = aBit ^ bBit ^ carry + carry = (aBit + bBit + carry) >= 2 ? 1 : 0 + if curBit == 1 { + res |= (1 << i) + } + } + + if res > 0x7FFFFFFF { + res = ~(res ^ mask) + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 3. Bit Manipulation (Optimal) + +::tabs-start + +```python +class Solution: + def getSum(self, a: int, b: int) -> int: + mask = 0xFFFFFFFF + max_int = 0x7FFFFFFF + + while b != 0: + carry = (a & b) << 1 + a = (a ^ b) & mask + b = carry & mask + + return a if a <= max_int else ~(a ^ mask) +``` + +```java +public class Solution { + public int getSum(int a, int b) { + while (b != 0) { + int carry = (a & b) << 1; + a ^= b; + b = carry; + } + return a; + } +} +``` + +```cpp +class Solution { +public: + int getSum(int a, int b) { + while (b != 0) { + int carry = (a & b) << 1; + a ^= b; + b = carry; + } + return a; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} a + * @param {number} b + * @return {number} + */ + getSum(a, b) { + while (b !== 0) { + let carry = (a & b) << 1; + a ^= b; + b = carry; + } + return a; + } +} +``` + +```csharp +public class Solution { + public int GetSum(int a, int b) { + while (b != 0) { + int carry = (a & b) << 1; + a ^= b; + b = carry; + } + return a; + } +} +``` + +```go +func getSum(a int, b int) int { + mask := 0xFFFFFFFF + maxInt := 0x7FFFFFFF + + for b != 0 { + carry := (a & b) << 1 + a = (a ^ b) & mask + b = carry & mask + } + + if a <= maxInt { + return a + } + return ^(a ^ mask) +} +``` + +```kotlin +class Solution { + fun getSum(a: Int, b: Int): Int { + var carry = 0 + var res = 0 + val mask = 0xFFFFFFFF.toInt() + + for (i in 0 until 32) { + val aBit = (a shr i) and 1 + val bBit = (b shr i) and 1 + val curBit = aBit xor bBit xor carry + carry = if (aBit + bBit + carry >= 2) 1 else 0 + if (curBit == 1) { + res = res or (1 shl i) + } + } + + if (res > 0x7FFFFFFF) { + res = res.inv() xor mask + } + + return res + } +} +``` + +```swift +class Solution { + func getSum(_ a: Int, _ b: Int) -> Int { + let mask = 0xFFFFFFFF + let maxInt = 0x7FFFFFFF + var a = a + var b = b + + while b != 0 { + let carry = (a & b) << 1 + a = (a ^ b) & mask + b = carry & mask + } + + return a <= maxInt ? a : ~(a ^ mask) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/sum-root-to-leaf-numbers.md b/articles/sum-root-to-leaf-numbers.md new file mode 100644 index 000000000..02bfd196e --- /dev/null +++ b/articles/sum-root-to-leaf-numbers.md @@ -0,0 +1,652 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def sumNumbers(self, root: TreeNode) -> int: + def dfs(cur, num): + if not cur: + return 0 + + num = num * 10 + cur.val + if not cur.left and not cur.right: + return num + return dfs(cur.left, num) + dfs(cur.right, num) + + return dfs(root, 0) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int sumNumbers(TreeNode root) { + return dfs(root, 0); + } + + private int dfs(TreeNode cur, int num) { + if (cur == null) return 0; + + num = num * 10 + cur.val; + if (cur.left == null && cur.right == null) return num; + + return dfs(cur.left, num) + dfs(cur.right, num); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int sumNumbers(TreeNode* root) { + return dfs(root, 0); + } + +private: + int dfs(TreeNode* cur, int num) { + if (!cur) return 0; + + num = num * 10 + cur->val; + if (!cur->left && !cur->right) return num; + + return dfs(cur->left, num) + dfs(cur->right, num); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + sumNumbers(root) { + const dfs = (cur, num) => { + if (!cur) return 0; + + num = num * 10 + cur.val; + if (!cur.left && !cur.right) return num; + + return dfs(cur.left, num) + dfs(cur.right, num); + }; + + return dfs(root, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(h)$ for recursion stack. + +> Where $n$ is the number of nodes and $h$ is the height of the given tree. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def sumNumbers(self, root: TreeNode) -> int: + res = 0 + q = deque([(root, 0)]) + while q: + cur, num = q.popleft() + num = num * 10 + cur.val + if not cur.left and not cur.right: + res += num + continue + + if cur.left: + q.append((cur.left, num)) + if cur.right: + q.append((cur.right, num)) + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int sumNumbers(TreeNode root) { + int res = 0; + Queue> q = new LinkedList<>(); + q.offer(new Pair<>(root, 0)); + while (!q.isEmpty()) { + Pair p = q.poll(); + TreeNode cur = p.getKey(); + int num = p.getValue() * 10 + cur.val; + if (cur.left == null && cur.right == null) { + res += num; + continue; + } + + if (cur.left != null) q.offer(new Pair<>(cur.left, num)); + if (cur.right != null) q.offer(new Pair<>(cur.right, num)); + } + + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int sumNumbers(TreeNode* root) { + int res = 0; + queue> q; + q.push({root, 0}); + while (!q.empty()) { + auto [cur, num] = q.front();q.pop(); + num = num * 10 + cur->val; + if (!cur->left && !cur->right) { + res += num; + continue; + } + + if (cur->left) q.push({cur->left, num}); + if (cur->right) q.push({cur->right, num}); + } + + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + sumNumbers(root) { + let res = 0; + const q = new Queue([[root, 0]]); + while (!q.isEmpty()) { + const [cur, num] = q.pop(); + const newNum = num * 10 + cur.val; + if (!cur.left && !cur.right) { + res += newNum; + continue; + } + + if (cur.left) q.push([cur.left, newNum]); + if (cur.right) q.push([cur.right, newNum]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def sumNumbers(self, root: Optional[TreeNode]) -> int: + res = 0 + stack = [] + cur, num = root, 0 + + while cur or stack: + if cur: + num = num * 10 + cur.val + if not cur.left and not cur.right: + res += num + + stack.append((cur.right, num)) + cur = cur.left + else: + cur, num = stack.pop() + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int sumNumbers(TreeNode root) { + int res = 0, num = 0; + Stack> stack = new Stack<>(); + TreeNode cur = root; + + while (cur != null || !stack.isEmpty()) { + if (cur != null) { + num = num * 10 + cur.val; + if (cur.left == null && cur.right == null) + res += num; + + stack.push(new Pair<>(cur.right, num)); + cur = cur.left; + } else { + Pair p = stack.pop(); + cur = p.getKey(); + num = p.getValue(); + } + } + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int sumNumbers(TreeNode* root) { + int res = 0; + stack> st; + TreeNode* cur = root; + int num = 0; + + while (cur || !st.empty()) { + if (cur) { + num = num * 10 + cur->val; + if (!cur->left && !cur->right) + res += num; + + st.push({cur->right, num}); + cur = cur->left; + } else { + cur = st.top().first; + num = st.top().second; + st.pop(); + } + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + sumNumbers(root) { + let res = 0, num = 0; + let stack = []; + let cur = root; + + while (cur || stack.length) { + if (cur) { + num = num * 10 + cur.val; + if (!cur.left && !cur.right) + res += num; + + stack.push([cur.right, num]); + cur = cur.left; + } else { + [cur, num] = stack.pop(); + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(h)$ + +> Where $n$ is the number of nodes and $h$ is the height of the given tree. + +--- + +## 4. Morris Travrsal + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def sumNumbers(self, root: Optional[TreeNode]) -> int: + res = 0 + cur = root + num = 0 + power = [1] * 10 + for i in range(1, 10): + power[i] *= power[i - 1] * 10 + + while cur: + if not cur.left: + num = num * 10 + cur.val + if not cur.right: + res += num + cur = cur.right + else: + prev = cur.left + steps = 1 + while prev.right and prev.right != cur: + prev = prev.right + steps += 1 + + if not prev.right: + prev.right = cur + num = num * 10 + cur.val + cur = cur.left + else: + prev.right = None + if not prev.left: + res += num + num //= power[steps] + cur = cur.right + + return res +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int sumNumbers(TreeNode root) { + int res = 0, num = 0; + int[] power = new int[10]; + power[0] = 1; + for (int i = 1; i < 10; i++) { + power[i] = power[i - 1] * 10; + } + + TreeNode cur = root; + while (cur != null) { + if (cur.left == null) { + num = num * 10 + cur.val; + if (cur.right == null) res += num; + cur = cur.right; + } else { + TreeNode prev = cur.left; + int steps = 1; + while (prev.right != null && prev.right != cur) { + prev = prev.right; + steps++; + } + + if (prev.right == null) { + prev.right = cur; + num = num * 10 + cur.val; + cur = cur.left; + } else { + prev.right = null; + if (prev.left == null) res += num; + num /= power[steps]; + cur = cur.right; + } + } + } + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int sumNumbers(TreeNode* root) { + int res = 0, num = 0; + int power[10] = {1}; + for (int i = 1; i < 10; i++) { + power[i] = power[i - 1] * 10; + } + + TreeNode* cur = root; + while (cur) { + if (!cur->left) { + num = num * 10 + cur->val; + if (!cur->right) res += num; + cur = cur->right; + } else { + TreeNode* prev = cur->left; + int steps = 1; + while (prev->right && prev->right != cur) { + prev = prev->right; + steps++; + } + + if (!prev->right) { + prev->right = cur; + num = num * 10 + cur->val; + cur = cur->left; + } else { + prev->right = nullptr; + if (!prev->left) res += num; + num /= power[steps]; + cur = cur->right; + } + } + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {number} + */ + sumNumbers(root) { + let res = 0, num = 0; + let power = Array(10).fill(1); + for (let i = 1; i < 10; i++) { + power[i] = power[i - 1] * 10; + } + + let cur = root; + while (cur) { + if (!cur.left) { + num = num * 10 + cur.val; + if (!cur.right) res += num; + cur = cur.right; + } else { + let prev = cur.left, steps = 1; + while (prev.right && prev.right !== cur) { + prev = prev.right; + steps++; + } + + if (!prev.right) { + prev.right = cur; + num = num * 10 + cur.val; + cur = cur.left; + } else { + prev.right = null; + if (!prev.left) res += num; + num = Math.floor(num / power[steps]); + cur = cur.right; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/surrounded-regions.md b/articles/surrounded-regions.md new file mode 100644 index 000000000..1c0c5445c --- /dev/null +++ b/articles/surrounded-regions.md @@ -0,0 +1,1386 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def solve(self, board: List[List[str]]) -> None: + ROWS, COLS = len(board), len(board[0]) + + def capture(r, c): + if (r < 0 or c < 0 or r == ROWS or + c == COLS or board[r][c] != "O" + ): + return + board[r][c] = "T" + capture(r + 1, c) + capture(r - 1, c) + capture(r, c + 1) + capture(r, c - 1) + + for r in range(ROWS): + if board[r][0] == "O": + capture(r, 0) + if board[r][COLS - 1] == "O": + capture(r, COLS - 1) + + for c in range(COLS): + if board[0][c] == "O": + capture(0, c) + if board[ROWS - 1][c] == "O": + capture(ROWS - 1, c) + + for r in range(ROWS): + for c in range(COLS): + if board[r][c] == "O": + board[r][c] = "X" + elif board[r][c] == "T": + board[r][c] = "O" +``` + +```java +public class Solution { + private int ROWS, COLS; + + public void solve(char[][] board) { + ROWS = board.length; + COLS = board[0].length; + + for (int r = 0; r < ROWS; r++) { + if (board[r][0] == 'O') { + capture(board, r, 0); + } + if (board[r][COLS - 1] == 'O') { + capture(board, r, COLS - 1); + } + } + + for (int c = 0; c < COLS; c++) { + if (board[0][c] == 'O') { + capture(board, 0, c); + } + if (board[ROWS - 1][c] == 'O') { + capture(board, ROWS - 1, c); + } + } + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (board[r][c] == 'O') { + board[r][c] = 'X'; + } else if (board[r][c] == 'T') { + board[r][c] = 'O'; + } + } + } + } + + private void capture(char[][] board, int r, int c) { + if (r < 0 || c < 0 || r >= ROWS || + c >= COLS || board[r][c] != 'O') { + return; + } + board[r][c] = 'T'; + capture(board, r + 1, c); + capture(board, r - 1, c); + capture(board, r, c + 1); + capture(board, r, c - 1); + } +} +``` + +```cpp +class Solution { + int ROWS, COLS; + +public: + void solve(vector>& board) { + ROWS = board.size(); + COLS = board[0].size(); + + for (int r = 0; r < ROWS; r++) { + if (board[r][0] == 'O') { + capture(board, r, 0); + } + if (board[r][COLS - 1] == 'O') { + capture(board, r, COLS - 1); + } + } + + for (int c = 0; c < COLS; c++) { + if (board[0][c] == 'O') { + capture(board, 0, c); + } + if (board[ROWS - 1][c] == 'O') { + capture(board, ROWS - 1, c); + } + } + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (board[r][c] == 'O') { + board[r][c] = 'X'; + } else if (board[r][c] == 'T') { + board[r][c] = 'O'; + } + } + } + } + +private: + void capture(vector>& board, int r, int c) { + if (r < 0 || c < 0 || r >= ROWS || + c >= COLS || board[r][c] != 'O') { + return; + } + board[r][c] = 'T'; + capture(board, r + 1, c); + capture(board, r - 1, c); + capture(board, r, c + 1); + capture(board, r, c - 1); + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[][]} board + * @return {void} Do not return anything, modify board in-place instead. + */ + solve(board) { + let ROWS = board.length, COLS = board[0].length; + + const capture = (r, c) => { + if (r < 0 || c < 0 || r == ROWS || + c == COLS || board[r][c] !== 'O') { + return; + } + board[r][c] = 'T'; + capture(r + 1, c); + capture(r - 1, c); + capture(r, c + 1); + capture(r, c - 1); + } + + for (let r = 0; r < ROWS; r++) { + if (board[r][0] === 'O') capture(r, 0); + if (board[r][COLS - 1] === 'O') capture(r, COLS - 1); + } + + for (let c = 0; c < COLS; c++) { + if (board[0][c] === 'O') capture(0, c); + if (board[ROWS - 1][c] === 'O') capture(ROWS - 1, c); + } + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (board[r][c] === 'O') board[r][c] = 'X'; + else if (board[r][c] === 'T') board[r][c] = 'O'; + } + } + } +} +``` + +```csharp +public class Solution { + private int ROWS, COLS; + + public void Solve(char[][] board) { + ROWS = board.Length; + COLS = board[0].Length; + + for (int r = 0; r < ROWS; r++) { + if (board[r][0] == 'O') { + Capture(board, r, 0); + } + if (board[r][COLS - 1] == 'O') { + Capture(board, r, COLS - 1); + } + } + + for (int c = 0; c < COLS; c++) { + if (board[0][c] == 'O') { + Capture(board, 0, c); + } + if (board[ROWS - 1][c] == 'O') { + Capture(board, ROWS - 1, c); + } + } + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (board[r][c] == 'O') { + board[r][c] = 'X'; + } else if (board[r][c] == 'T') { + board[r][c] = 'O'; + } + } + } + } + + private void Capture(char[][] board, int r, int c) { + if (r < 0 || c < 0 || r == ROWS || + c == COLS || board[r][c] != 'O') { + return; + } + board[r][c] = 'T'; + Capture(board, r + 1, c); + Capture(board, r - 1, c); + Capture(board, r, c + 1); + Capture(board, r, c - 1); + } +} +``` + +```go +func solve(board [][]byte) { + rows, cols := len(board), len(board[0]) + + var capture func(r, c int) + capture = func(r, c int) { + if r < 0 || c < 0 || r == rows || + c == cols || board[r][c] != 'O' { + return + } + board[r][c] = 'T' + capture(r+1, c) + capture(r-1, c) + capture(r, c+1) + capture(r, c-1) + } + + for r := 0; r < rows; r++ { + if board[r][0] == 'O' { + capture(r, 0) + } + if board[r][cols-1] == 'O' { + capture(r, cols-1) + } + } + + for c := 0; c < cols; c++ { + if board[0][c] == 'O' { + capture(0, c) + } + if board[rows-1][c] == 'O' { + capture(rows-1, c) + } + } + + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + if board[r][c] == 'O' { + board[r][c] = 'X' + } else if board[r][c] == 'T' { + board[r][c] = 'O' + } + } + } +} +``` + +```kotlin +class Solution { + fun solve(board: Array) { + val rows = board.size + val cols = board[0].size + + fun capture(r: Int, c: Int) { + if (r < 0 || c < 0 || r == rows || + c == cols || board[r][c] != 'O') { + return + } + board[r][c] = 'T' + capture(r + 1, c) + capture(r - 1, c) + capture(r, c + 1) + capture(r, c - 1) + } + + for (r in 0 until rows) { + if (board[r][0] == 'O') { + capture(r, 0) + } + if (board[r][cols - 1] == 'O') { + capture(r, cols - 1) + } + } + + for (c in 0 until cols) { + if (board[0][c] == 'O') { + capture(0, c) + } + if (board[rows - 1][c] == 'O') { + capture(rows - 1, c) + } + } + + for (r in 0 until rows) { + for (c in 0 until cols) { + if (board[r][c] == 'O') { + board[r][c] = 'X' + } else if (board[r][c] == 'T') { + board[r][c] = 'O' + } + } + } + } +} +``` + +```swift +class Solution { + func solve(_ board: inout [[Character]]) { + let ROWS = board.count + let COLS = board[0].count + + func capture(_ r: Int, _ c: Int) { + if r < 0 || c < 0 || r == ROWS || c == COLS || board[r][c] != "O" { + return + } + board[r][c] = "T" + capture(r + 1, c) + capture(r - 1, c) + capture(r, c + 1) + capture(r, c - 1) + } + + for r in 0.. Where $m$ is the number of rows and $n$ is the number of columns of the $board$. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def solve(self, board: List[List[str]]) -> None: + ROWS, COLS = len(board), len(board[0]) + directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + + def capture(): + q = deque() + for r in range(ROWS): + for c in range(COLS): + if (r == 0 or r == ROWS - 1 or + c == 0 or c == COLS - 1 and + board[r][c] == "O" + ): + q.append((r, c)) + while q: + r, c = q.popleft() + if board[r][c] == "O": + board[r][c] = "T" + for dr, dc in directions: + nr, nc = r + dr, c + dc + if 0 <= nr < ROWS and 0 <= nc < COLS: + q.append((nr, nc)) + + capture() + for r in range(ROWS): + for c in range(COLS): + if board[r][c] == "O": + board[r][c] = "X" + elif board[r][c] == "T": + board[r][c] = "O" +``` + +```java +public class Solution { + private int ROWS, COLS; + private int[][] directions = new int[][]{ + {1, 0}, {-1, 0}, {0, 1}, {0, -1} + }; + + public void solve(char[][] board) { + ROWS = board.length; + COLS = board[0].length; + + capture(board); + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (board[r][c] == 'O') { + board[r][c] = 'X'; + } else if (board[r][c] == 'T') { + board[r][c] = 'O'; + } + } + } + } + + private void capture(char[][] board) { + Queue q = new LinkedList<>(); + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (r == 0 || r == ROWS - 1 || + c == 0 || c == COLS - 1 && + board[r][c] == 'O') { + q.offer(new int[]{r, c}); + } + } + } + while (!q.isEmpty()) { + int[] cell = q.poll(); + int r = cell[0], c = cell[1]; + if (board[r][c] == 'O') { + board[r][c] = 'T'; + for (int[] direction : directions) { + int nr = r + direction[0], nc = c + direction[1]; + if (nr >= 0 && nr < ROWS && nc >= 0 && nc < COLS) { + q.offer(new int[]{nr, nc}); + } + } + } + } + } +} +``` + +```cpp +class Solution { + int ROWS, COLS; + vector> directions = {{1, 0}, {-1, 0}, + {0, 1}, {0, -1}}; + +public: + void solve(vector>& board) { + ROWS = board.size(); + COLS = board[0].size(); + + capture(board); + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (board[r][c] == 'O') { + board[r][c] = 'X'; + } else if (board[r][c] == 'T') { + board[r][c] = 'O'; + } + } + } + } + +private: + void capture(vector>& board) { + queue> q; + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (r == 0 || r == ROWS - 1 || + c == 0 || c == COLS - 1 && + board[r][c] == 'O') { + q.push({r, c}); + } + } + } + while (!q.empty()) { + auto [r, c] = q.front(); + q.pop(); + if (board[r][c] == 'O') { + board[r][c] = 'T'; + for (auto& direction : directions) { + int nr = r + direction.first; + int nc = c + direction.second; + if (nr >= 0 && nr < ROWS && + nc >= 0 && nc < COLS) { + q.push({nr, nc}); + } + } + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[][]} board + * @return {void} Do not return anything, modify board in-place instead. + */ + solve(board) { + let ROWS = board.length, COLS = board[0].length; + let directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]; + + const capture = () => { + let q = new Queue(); + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (r === 0 || r === ROWS - 1 || + c === 0 || c === COLS - 1 && + board[r][c] === 'O') { + q.push([r, c]); + } + } + } + while (!q.isEmpty()) { + let [r, c] = q.pop(); + if (board[r][c] === 'O') { + board[r][c] = 'T'; + for (let [dr, dc] of directions) { + let nr = r + dr, nc = c + dc; + if (nr >= 0 && nr < ROWS && + nc >= 0 && nc < COLS) { + q.push([nr, nc]); + } + } + } + } + } + + capture(); + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (board[r][c] === 'O') board[r][c] = 'X'; + else if (board[r][c] === 'T') board[r][c] = 'O'; + } + } + } +} +``` + +```csharp +public class Solution { + private int ROWS, COLS; + private int[][] directions = new int[][] { + new int[] { 1, 0 }, new int[] { -1, 0 }, + new int[] { 0, 1 }, new int[] { 0, -1 } + }; + + public void Solve(char[][] board) { + ROWS = board.Length; + COLS = board[0].Length; + + Capture(board); + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (board[r][c] == 'O') { + board[r][c] = 'X'; + } else if (board[r][c] == 'T') { + board[r][c] = 'O'; + } + } + } + } + + private void Capture(char[][] board) { + Queue q = new Queue(); + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (r == 0 || r == ROWS - 1 || + c == 0 || c == COLS - 1 && + board[r][c] == 'O') { + q.Enqueue(new int[] { r, c }); + } + } + } + while (q.Count > 0) { + int[] cell = q.Dequeue(); + int r = cell[0], c = cell[1]; + if (board[r][c] == 'O') { + board[r][c] = 'T'; + foreach (var direction in directions) { + int nr = r + direction[0]; + int nc = c + direction[1]; + if (nr >= 0 && nr < ROWS && + nc >= 0 && nc < COLS) { + q.Enqueue(new int[] { nr, nc }); + } + } + } + } + } +} +``` + +```go +func solve(board [][]byte) { + rows, cols := len(board), len(board[0]) + directions := [][]int{{1, 0}, {-1, 0}, {0, 1}, {0, -1}} + + capture := func() { + q := [][]int{} + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + if r == 0 || r == rows-1 || c == 0 || c == cols-1 { + if board[r][c] == 'O' { + q = append(q, []int{r, c}) + } + } + } + } + for len(q) > 0 { + // Dequeue + r, c := q[0][0], q[0][1] + q = q[1:] + if board[r][c] == 'O' { + board[r][c] = 'T' + for _, dir := range directions { + nr, nc := r+dir[0], c+dir[1] + if nr >= 0 && nr < rows && nc >= 0 && nc < cols { + q = append(q, []int{nr, nc}) + } + } + } + } + } + + capture() + + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + if board[r][c] == 'O' { + board[r][c] = 'X' + } else if board[r][c] == 'T' { + board[r][c] = 'O' + } + } + } +} +``` + +```kotlin +class Solution { + fun solve(board: Array) { + val rows = board.size + val cols = board[0].size + val directions = arrayOf(intArrayOf(1, 0), + intArrayOf(-1, 0), + intArrayOf(0, 1), + intArrayOf(0, -1)) + + fun capture() { + val q: LinkedList = LinkedList() + for (r in 0 until rows) { + for (c in 0 until cols) { + if (r == 0 || r == rows - 1 || c == 0 || c == cols - 1) { + if (board[r][c] == 'O') { + q.add(intArrayOf(r, c)) + } + } + } + } + while (q.isNotEmpty()) { + val (r, c) = q.poll() + if (board[r][c] == 'O') { + board[r][c] = 'T' + for (dir in directions) { + val nr = r + dir[0] + val nc = c + dir[1] + if (nr in 0 until rows && nc in 0 until cols) { + q.add(intArrayOf(nr, nc)) + } + } + } + } + } + + capture() + + for (r in 0 until rows) { + for (c in 0 until cols) { + if (board[r][c] == 'O') { + board[r][c] = 'X' + } else if (board[r][c] == 'T') { + board[r][c] = 'O' + } + } + } + } +} +``` + +```swift +class Solution { + func solve(_ board: inout [[Character]]) { + let ROWS = board.count + let COLS = board[0].count + let directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + + func capture() { + var queue = Deque<(Int, Int)>() + for r in 0..= 0, nr < ROWS, nc >= 0, nc < COLS { + queue.append((nr, nc)) + } + } + } + } + } + + capture() + + for r in 0.. Where $m$ is the number of rows and $n$ is the number of columns of the $board$. + +--- + +## 3. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu = self.find(u) + pv = self.find(v) + if pu == pv: + return False + if self.Size[pu] >= self.Size[pv]: + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + else: + self.Size[pv] += self.Size[pu] + self.Parent[pu] = pv + return True + + def connected(self, u, v): + return self.find(u) == self.find(v) + +class Solution: + def solve(self, board: List[List[str]]) -> None: + ROWS, COLS = len(board), len(board[0]) + directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + dsu = DSU(ROWS * COLS + 1) + + for r in range(ROWS): + for c in range(COLS): + if board[r][c] != "O": + continue + if (r == 0 or c == 0 or + r == (ROWS - 1) or c == (COLS - 1) + ): + dsu.union(ROWS * COLS, r * COLS + c) + else: + for dx, dy in directions: + nr, nc = r + dx, c + dy + if board[nr][nc] == "O": + dsu.union(r * COLS + c, nr * COLS + nc) + + for r in range(ROWS): + for c in range(COLS): + if not dsu.connected(ROWS * COLS, r * COLS + c): + board[r][c] = "X" +``` + +```java +class DSU { + int[] Parent, Size; + + public DSU(int n) { + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + public int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (Size[pu] >= Size[pv]) { + Size[pu] += Size[pv]; + Parent[pv] = pu; + } else { + Size[pv] += Size[pu]; + Parent[pu] = pv; + } + return true; + } + + public boolean connected(int u, int v) { + return find(u) == find(v); + } +} + +public class Solution { + public void solve(char[][] board) { + int ROWS = board.length, COLS = board[0].length; + DSU dsu = new DSU(ROWS * COLS + 1); + int[][] directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (board[r][c] != 'O') continue; + if (r == 0 || c == 0 || + r == ROWS - 1 || c == COLS - 1) { + dsu.union(ROWS * COLS, r * COLS + c); + } else { + for (int[] dir : directions) { + int nr = r + dir[0], nc = c + dir[1]; + if (board[nr][nc] == 'O') { + dsu.union(r * COLS + c, nr * COLS + nc); + } + } + } + } + } + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (!dsu.connected(ROWS * COLS, r * COLS + c)) { + board[r][c] = 'X'; + } + } + } + } +} +``` + +```cpp +class DSU { + vector Parent, Size; + +public: + DSU(int n) { + Parent.resize(n + 1); + Size.resize(n + 1); + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + bool unionNodes(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (Size[pu] >= Size[pv]) { + Size[pu] += Size[pv]; + Parent[pv] = pu; + } else { + Size[pv] += Size[pu]; + Parent[pu] = pv; + } + return true; + } + + bool connected(int u, int v) { + return find(u) == find(v); + } +}; + +class Solution { +public: + void solve(vector>& board) { + int ROWS = board.size(), COLS = board[0].size(); + DSU dsu(ROWS * COLS + 1); + vector> directions = {{1, 0}, {-1, 0}, + {0, 1}, {0, -1}}; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (board[r][c] != 'O') continue; + if (r == 0 || c == 0 || + r == ROWS - 1 || c == COLS - 1) { + dsu.unionNodes(ROWS * COLS, r * COLS + c); + } else { + for (auto& dir : directions) { + int nr = r + dir[0], nc = c + dir[1]; + if (board[nr][nc] == 'O') { + dsu.unionNodes(r * COLS + c, nr * COLS + nc); + } + } + } + } + } + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (!dsu.connected(ROWS * COLS, r * COLS + c)) { + board[r][c] = 'X'; + } + } + } + } +}; +``` + +```javascript +class DSU { + constructor(n) { + this.Parent = Array.from({ length: n + 1 }, (_, i) => i); + this.Size = Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.Parent[node] !== node) { + this.Parent[node] = this.find(this.Parent[node]); + } + return this.Parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u); + let pv = this.find(v); + if (pu === pv) return false; + if (this.Size[pu] >= this.Size[pv]) { + this.Size[pu] += this.Size[pv]; + this.Parent[pv] = pu; + } else { + this.Size[pv] += this.Size[pu]; + this.Parent[pu] = pv; + } + return true; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + connected(u, v) { + return this.find(u) === this.find(v); + } +} + +class Solution { + /** + * @param {character[][]} board + * @return {void} Do not return anything, modify board in-place instead. + */ + solve(board) { + const ROWS = board.length, COLS = board[0].length; + const dsu = new DSU(ROWS * COLS + 1); + const directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]; + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (board[r][c] !== 'O') continue; + if (r === 0 || c === 0 || + r === ROWS - 1 || c === COLS - 1) { + dsu.union(ROWS * COLS, r * COLS + c); + } else { + for (let [dx, dy] of directions) { + const nr = r + dx, nc = c + dy; + if (board[nr][nc] === 'O') { + dsu.union(r * COLS + c, nr * COLS + nc); + } + } + } + } + } + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + if (!dsu.connected(ROWS * COLS, r * COLS + c)) { + board[r][c] = 'X'; + } + } + } + } +} +``` + +```csharp +public class DSU { + private int[] Parent, Size; + + public DSU(int n) { + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + public int Find(int node) { + if (Parent[node] != node) { + Parent[node] = Find(Parent[node]); + } + return Parent[node]; + } + + public bool Union(int u, int v) { + int pu = Find(u), pv = Find(v); + if (pu == pv) return false; + if (Size[pu] >= Size[pv]) { + Size[pu] += Size[pv]; + Parent[pv] = pu; + } else { + Size[pv] += Size[pu]; + Parent[pu] = pv; + } + return true; + } + + public bool Connected(int u, int v) { + return Find(u) == Find(v); + } +} + +public class Solution { + public void Solve(char[][] board) { + int ROWS = board.Length, COLS = board[0].Length; + DSU dsu = new DSU(ROWS * COLS + 1); + int[][] directions = new int[][] { + new int[] { 1, 0 }, new int[] { -1, 0 }, + new int[] { 0, 1 }, new int[] { 0, -1 } + }; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (board[r][c] != 'O') continue; + if (r == 0 || c == 0 || + r == ROWS - 1 || c == COLS - 1) { + dsu.Union(ROWS * COLS, r * COLS + c); + } else { + foreach (var dir in directions) { + int nr = r + dir[0], nc = c + dir[1]; + if (board[nr][nc] == 'O') { + dsu.Union(r * COLS + c, nr * COLS + nc); + } + } + } + } + } + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + if (!dsu.Connected(ROWS * COLS, r * COLS + c)) { + board[r][c] = 'X'; + } + } + } + } +} +``` + +```go +type DSU struct { + Parent []int + Size []int +} + +func NewDSU(n int) *DSU { + dsu := &DSU{ + Parent: make([]int, n+1), + Size: make([]int, n+1), + } + for i := 0; i <= n; i++ { + dsu.Parent[i] = i + dsu.Size[i] = 1 + } + return dsu +} + +func (dsu *DSU) Find(node int) int { + if dsu.Parent[node] != node { + dsu.Parent[node] = dsu.Find(dsu.Parent[node]) + } + return dsu.Parent[node] +} + +func (dsu *DSU) Union(u, v int) bool { + pu := dsu.Find(u) + pv := dsu.Find(v) + if pu == pv { + return false + } + if dsu.Size[pu] >= dsu.Size[pv] { + dsu.Size[pu] += dsu.Size[pv] + dsu.Parent[pv] = pu + } else { + dsu.Size[pv] += dsu.Size[pu] + dsu.Parent[pu] = pv + } + return true +} + +func (dsu *DSU) Connected(u, v int) bool { + return dsu.Find(u) == dsu.Find(v) +} + +func solve(board [][]byte) { + rows, cols := len(board), len(board[0]) + directions := [][]int{{1, 0}, {-1, 0}, {0, 1}, {0, -1}} + dsu := NewDSU(rows * cols) + + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + if board[r][c] != 'O' { + continue + } + if r == 0 || c == 0 || r == rows-1 || c == cols-1 { + dsu.Union(rows*cols, r*cols+c) + } else { + for _, dir := range directions { + nr, nc := r+dir[0], c+dir[1] + if nr >= 0 && nr < rows && nc >= 0 && + nc < cols && board[nr][nc] == 'O' { + dsu.Union(r*cols+c, nr*cols+nc) + } + } + } + } + } + + for r := 0; r < rows; r++ { + for c := 0; c < cols; c++ { + if !dsu.Connected(rows*cols, r*cols+c) { + board[r][c] = 'X' + } + } + } +} +``` + +```kotlin +class DSU(n: Int) { + val parent: IntArray = IntArray(n + 1) { it } + val size: IntArray = IntArray(n + 1) { 1 } + + fun find(node: Int): Int { + if (parent[node] != node) { + parent[node] = find(parent[node]) + } + return parent[node] + } + + fun union(u: Int, v: Int): Boolean { + val pu = find(u) + val pv = find(v) + if (pu == pv) return false + if (size[pu] >= size[pv]) { + size[pu] += size[pv] + parent[pv] = pu + } else { + size[pv] += size[pu] + parent[pu] = pv + } + return true + } + + fun connected(u: Int, v: Int): Boolean { + return find(u) == find(v) + } +} + +class Solution { + fun solve(board: Array) { + val rows = board.size + val cols = board[0].size + val directions = arrayOf(intArrayOf(1, 0), + intArrayOf(-1, 0), + intArrayOf(0, 1), + intArrayOf(0, -1)) + val dsu = DSU(rows * cols) + + for (r in 0 until rows) { + for (c in 0 until cols) { + if (board[r][c] != 'O') continue + if (r == 0 || c == 0 || r == rows - 1 || c == cols - 1) { + dsu.union(rows * cols, r * cols + c) + } else { + for (dir in directions) { + val nr = r + dir[0] + val nc = c + dir[1] + if (nr in 0 until rows && + nc in 0 until cols && board[nr][nc] == 'O') { + dsu.union(r * cols + c, nr * cols + nc) + } + } + } + } + } + + for (r in 0 until rows) { + for (c in 0 until cols) { + if (!dsu.connected(rows * cols, r * cols + c)) { + board[r][c] = 'X' + } + } + } + } +} +``` + +```swift +class DSU { + private var parent: [Int] + private var size: [Int] + + init(_ n: Int) { + parent = Array(0...n) + size = Array(repeating: 1, count: n + 1) + } + + func find(_ node: Int) -> Int { + if parent[node] != node { + parent[node] = find(parent[node]) + } + return parent[node] + } + + func union(_ u: Int, _ v: Int) { + let pu = find(u) + let pv = find(v) + if pu == pv { return } + if size[pu] >= size[pv] { + size[pu] += size[pv] + parent[pv] = pu + } else { + size[pv] += size[pu] + parent[pu] = pv + } + } + + func connected(_ u: Int, _ v: Int) -> Bool { + return find(u) == find(v) + } +} + +class Solution { + func solve(_ board: inout [[Character]]) { + let ROWS = board.count + let COLS = board[0].count + let directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] + let dsu = DSU(ROWS * COLS + 1) + let N = ROWS * COLS + + for r in 0..= 0, nr < ROWS, nc >= 0, nc < COLS, board[nr][nc] == "O" { + dsu.union(r * COLS + c, nr * COLS + nc) + } + } + } + } + } + + for r in 0.. Where $m$ is the number of rows and $n$ is the number of columns of the $board$. \ No newline at end of file diff --git a/articles/swap-nodes-in-pairs.md b/articles/swap-nodes-in-pairs.md new file mode 100644 index 000000000..89edd0eee --- /dev/null +++ b/articles/swap-nodes-in-pairs.md @@ -0,0 +1,419 @@ +## 1. Convert TO Array + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]: + if not head: + return None + arr = [] + cur = head + + while cur: + arr.append(cur) + cur = cur.next + + for i in range(0, len(arr) - 1, 2): + arr[i], arr[i + 1] = arr[i + 1], arr[i] + + for i in range(len(arr) - 1): + arr[i].next = arr[i + 1] + + arr[-1].next = None + return arr[0] +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode swapPairs(ListNode head) { + if (head == null) return null; + + List arr = new ArrayList<>(); + ListNode cur = head; + + while (cur != null) { + arr.add(cur); + cur = cur.next; + } + + for (int i = 0; i < arr.size() - 1; i += 2) { + ListNode temp = arr.get(i); + arr.set(i, arr.get(i + 1)); + arr.set(i + 1, temp); + } + + for (int i = 0; i < arr.size() - 1; i++) { + arr.get(i).next = arr.get(i + 1); + } + + arr.get(arr.size() - 1).next = null; + return arr.get(0); + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* swapPairs(ListNode* head) { + if (!head) return nullptr; + + vector arr; + ListNode* cur = head; + + while (cur) { + arr.push_back(cur); + cur = cur->next; + } + + for (size_t i = 0; i + 1 < arr.size(); i += 2) { + swap(arr[i], arr[i + 1]); + } + + for (size_t i = 0; i + 1 < arr.size(); i++) { + arr[i]->next = arr[i + 1]; + } + + arr.back()->next = nullptr; + return arr[0]; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + swapPairs(head) { + if (!head) return null; + + let arr = []; + let cur = head; + + while (cur) { + arr.push(cur); + cur = cur.next; + } + + for (let i = 0; i + 1 < arr.length; i += 2) { + [arr[i], arr[i + 1]] = [arr[i + 1], arr[i]]; + } + + for (let i = 0; i + 1 < arr.length; i++) { + arr[i].next = arr[i + 1]; + } + + arr[arr.length - 1].next = null; + return arr[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Recursion + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]: + if not head or not head.next: + return head + + cur = head + nxt = head.next + cur.next = self.swapPairs(nxt.next) + nxt.next = cur + return nxt +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode swapPairs(ListNode head) { + if (head == null || head.next == null) { + return head; + } + + ListNode cur = head; + ListNode nxt = head.next; + cur.next = swapPairs(nxt.next); + nxt.next = cur; + + return nxt; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* swapPairs(ListNode* head) { + if (!head || !head->next) { + return head; + } + + ListNode* cur = head; + ListNode* nxt = head->next; + cur->next = swapPairs(nxt->next); + nxt->next = cur; + + return nxt; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + swapPairs(head) { + if (!head || !head.next) { + return head; + } + + let cur = head; + let nxt = head.next; + cur.next = this.swapPairs(nxt.next); + nxt.next = cur; + + return nxt; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Iteration + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]: + dummy = ListNode(0, head) + prev, curr = dummy, head + + while curr and curr.next: + nxtPair = curr.next.next + second = curr.next + + # Reverse this pair + second.next = curr + curr.next = nxtPair + prev.next = second + + # Update pointers + prev = curr + curr = nxtPair + + return dummy.next +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode swapPairs(ListNode head) { + ListNode dummy = new ListNode(0, head); + ListNode prev = dummy, curr = head; + + while (curr != null && curr.next != null) { + ListNode nxtPair = curr.next.next; + ListNode second = curr.next; + + // Reverse this pair + second.next = curr; + curr.next = nxtPair; + prev.next = second; + + // Update pointers + prev = curr; + curr = nxtPair; + } + + return dummy.next; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* swapPairs(ListNode* head) { + ListNode dummy(0, head); + ListNode* prev = &dummy, *curr = head; + + while (curr && curr->next) { + ListNode* nxtPair = curr->next->next; + ListNode* second = curr->next; + + // Reverse this pair + second->next = curr; + curr->next = nxtPair; + prev->next = second; + + // Update pointers + prev = curr; + curr = nxtPair; + } + + return dummy.next; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @return {ListNode} + */ + swapPairs(head) { + let dummy = new ListNode(0, head); + let prev = dummy, curr = head; + + while (curr && curr.next) { + let nxtPair = curr.next.next; + let second = curr.next; + + // Reverse this pair + second.next = curr; + curr.next = nxtPair; + prev.next = second; + + // Update pointers + prev = curr; + curr = nxtPair; + } + + return dummy.next; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/swapping-nodes-in-a-linked-list.md b/articles/swapping-nodes-in-a-linked-list.md new file mode 100644 index 000000000..8a0267213 --- /dev/null +++ b/articles/swapping-nodes-in-a-linked-list.md @@ -0,0 +1,785 @@ +## 1. Convert To Array + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def swapNodes(self, head: Optional[ListNode], k: int) -> Optional[ListNode]: + arr = [] + cur = head + while cur: + arr.append(cur.val) + cur = cur.next + + n = len(arr) + arr[k - 1], arr[n - k] = arr[n - k], arr[k - 1] + + cur, i = head, 0 + while cur: + cur.val = arr[i] + cur = cur.next + i += 1 + + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode swapNodes(ListNode head, int k) { + List arr = new ArrayList<>(); + ListNode cur = head; + + while (cur != null) { + arr.add(cur.val); + cur = cur.next; + } + + int n = arr.size(); + int temp = arr.get(k - 1); + arr.set(k - 1, arr.get(n - k)); + arr.set(n - k, temp); + + cur = head; + int i = 0; + while (cur != null) { + cur.val = arr.get(i); + cur = cur.next; + i++; + } + + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* swapNodes(ListNode* head, int k) { + vector arr; + ListNode* cur = head; + + while (cur) { + arr.push_back(cur->val); + cur = cur->next; + } + + int n = arr.size(); + swap(arr[k - 1], arr[n - k]); + + cur = head; + int i = 0; + while (cur) { + cur->val = arr[i]; + cur = cur->next; + i++; + } + + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} k + * @return {ListNode} + */ + swapNodes(head, k) { + let arr = []; + let cur = head; + + while (cur) { + arr.push(cur.val); + cur = cur.next; + } + + let n = arr.length; + [arr[k - 1], arr[n - k]] = [arr[n - k], arr[k - 1]]; + + cur = head; + let i = 0; + while (cur) { + cur.val = arr[i]; + cur = cur.next; + i++; + } + + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Recursion + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def swapNodes(self, head: Optional[ListNode], k: int) -> Optional[ListNode]: + left, right, startIdx = None, None, 0 + + def dfs(node): + nonlocal left, right, startIdx + if not node: + return 0 + + startIdx += 1 + if startIdx == k: + left = node + + endIdx = dfs(node.next) + 1 + if endIdx == k: + right = node + + return endIdx + + dfs(head) + if left and right: + left.val, right.val = right.val, left.val + + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode swapNodes(ListNode head, int k) { + ListNode[] left = new ListNode[1]; + ListNode[] right = new ListNode[1]; + int[] startIdx = {0}; + + dfs(head, k, startIdx, left, right); + + if (left[0] != null && right[0] != null) { + int temp = left[0].val; + left[0].val = right[0].val; + right[0].val = temp; + } + + return head; + } + + private int dfs(ListNode node, int k, int[] startIdx, ListNode[] left, ListNode[] right) { + if (node == null) { + return 0; + } + + startIdx[0]++; + if (startIdx[0] == k) { + left[0] = node; + } + + int endIdx = dfs(node.next, k, startIdx, left, right) + 1; + if (endIdx == k) { + right[0] = node; + } + + return endIdx; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* swapNodes(ListNode* head, int k) { + ListNode* left = nullptr; + ListNode* right = nullptr; + int startIdx = 0; + + dfs(head, k, startIdx, left, right); + + if (left && right) { + swap(left->val, right->val); + } + + return head; + } + +private: + int dfs(ListNode* node, int k, int& startIdx, ListNode*& left, ListNode*& right) { + if (!node) { + return 0; + } + + startIdx++; + if (startIdx == k) { + left = node; + } + + int endIdx = dfs(node->next, k, startIdx, left, right) + 1; + if (endIdx == k) { + right = node; + } + + return endIdx; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} k + * @return {ListNode} + */ + swapNodes(head, k) { + let left = null, right = null, startIdx = 0; + + const dfs = (node) => { + if (!node) return 0; + + startIdx++; + if (startIdx === k) left = node; + + let endIdx = dfs(node.next) + 1; + if (endIdx === k) right = node; + + return endIdx; + }; + + dfs(head); + if (left && right) { + [left.val, right.val] = [right.val, left.val]; + } + + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 3. Iteration (Two Pass) + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def swapNodes(self, head: Optional[ListNode], k: int) -> Optional[ListNode]: + n = 0 + cur = head + while cur: + n += 1 + cur = cur.next + + left, right = None, None + cur = head + for i in range(1, n + 1): + if i == k: + left = cur + if i == (n - k + 1): + right = cur + cur = cur.next + + left.val, right.val = right.val, left.val + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode swapNodes(ListNode head, int k) { + int n = 0; + ListNode cur = head; + while (cur != null) { + n++; + cur = cur.next; + } + + ListNode left = null, right = null; + cur = head; + for (int i = 1; i <= n; i++) { + if (i == k) { + left = cur; + } + if (i == (n - k + 1)) { + right = cur; + } + cur = cur.next; + } + + int temp = left.val; + left.val = right.val; + right.val = temp; + + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* swapNodes(ListNode* head, int k) { + int n = 0; + ListNode* cur = head; + while (cur) { + n++; + cur = cur->next; + } + + ListNode* left = nullptr; + ListNode* right = nullptr; + cur = head; + for (int i = 1; i <= n; i++) { + if (i == k) { + left = cur; + } + if (i == (n - k + 1)) { + right = cur; + } + cur = cur->next; + } + + swap(left->val, right->val); + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} k + * @return {ListNode} + */ + swapNodes(head, k) { + let n = 0; + let cur = head; + while (cur) { + n++; + cur = cur.next; + } + + let left = null, right = null; + cur = head; + for (let i = 1; i <= n; i++) { + if (i === k) { + left = cur; + } + if (i === (n - k + 1)) { + right = cur; + } + cur = cur.next; + } + + [left.val, right.val] = [right.val, left.val]; + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Iteration (One Pass) - I + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def swapNodes(self, head: Optional[ListNode], k: int) -> Optional[ListNode]: + cur = head + for _ in range(k - 1): + cur = cur.next + + left = cur + right = head + + while cur.next: + cur = cur.next + right = right.next + + left.val, right.val = right.val, left.val + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode swapNodes(ListNode head, int k) { + ListNode cur = head; + for (int i = 0; i < k - 1; i++) { + cur = cur.next; + } + + ListNode left = cur; + ListNode right = head; + + while (cur.next != null) { + cur = cur.next; + right = right.next; + } + + int temp = left.val; + left.val = right.val; + right.val = temp; + + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* swapNodes(ListNode* head, int k) { + ListNode* cur = head; + for (int i = 0; i < k - 1; i++) { + cur = cur->next; + } + + ListNode* left = cur; + ListNode* right = head; + + while (cur->next) { + cur = cur->next; + right = right->next; + } + + swap(left->val, right->val); + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} k + * @return {ListNode} + */ + swapNodes(head, k) { + let cur = head; + for (let i = 0; i < k - 1; i++) { + cur = cur.next; + } + + let left = cur; + let right = head; + + while (cur.next) { + cur = cur.next; + right = right.next; + } + + [left.val, right.val] = [right.val, left.val]; + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 5. Iteration (One Pass) - II + +::tabs-start + +```python +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def swapNodes(self, head: Optional[ListNode], k: int) -> Optional[ListNode]: + left, right = None, None + cur = head + + while cur: + if right: + right = right.next + if k == 1: + left = cur + right = head + k -= 1 + cur = cur.next + + left.val, right.val = right.val, left.val + return head +``` + +```java +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +public class Solution { + public ListNode swapNodes(ListNode head, int k) { + ListNode left = null, right = null, cur = head; + + while (cur != null) { + if (right != null) { + right = right.next; + } + if (k == 1) { + left = cur; + right = head; + } + k--; + cur = cur.next; + } + + int temp = left.val; + left.val = right.val; + right.val = temp; + + return head; + } +} +``` + +```cpp +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* swapNodes(ListNode* head, int k) { + ListNode* left = nullptr; + ListNode* right = nullptr; + ListNode* cur = head; + + while (cur) { + if (right) { + right = right->next; + } + if (k == 1) { + left = cur; + right = head; + } + k--; + cur = cur->next; + } + + swap(left->val, right->val); + return head; + } +}; +``` + +```javascript +/** + * Definition for singly-linked list. + * class ListNode { + * constructor(val = 0, next = null) { + * this.val = val; + * this.next = next; + * } + * } + */ +class Solution { + /** + * @param {ListNode} head + * @param {number} k + * @return {ListNode} + */ + swapNodes(head, k) { + let left = null, right = null, cur = head; + + while (cur) { + if (right) { + right = right.next; + } + if (k === 1) { + left = cur; + right = head; + } + k--; + cur = cur.next; + } + + [left.val, right.val] = [right.val, left.val]; + return head; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/swim-in-rising-water.md b/articles/swim-in-rising-water.md new file mode 100644 index 000000000..b87262dcb --- /dev/null +++ b/articles/swim-in-rising-water.md @@ -0,0 +1,1870 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def swimInWater(self, grid: List[List[int]]) -> int: + n = len(grid) + visit = [[False] * n for _ in range(n)] + + def dfs(node, t): + r, c = node + if min(r, c) < 0 or max(r, c) >= n or visit[r][c]: + return 1000000 + if r == (n - 1) and c == (n - 1): + return max(t, grid[r][c]) + visit[r][c] = True + t = max(t, grid[r][c]) + res = min(dfs((r + 1, c), t), + dfs((r - 1, c), t), + dfs((r, c + 1), t), + dfs((r, c - 1), t)) + visit[r][c] = False + return res + + return dfs((0, 0), 0) +``` + +```java +public class Solution { + public int swimInWater(int[][] grid) { + int n = grid.length; + boolean[][] visit = new boolean[n][n]; + + return dfs(grid, visit, 0, 0, 0); + } + + private int dfs(int[][] grid, boolean[][] visit, + int r, int c, int t) { + int n = grid.length; + if (r < 0 || c < 0 || r >= n || c >= n || visit[r][c]) { + return 1000000; + } + if (r == n - 1 && c == n - 1) { + return Math.max(t, grid[r][c]); + } + visit[r][c] = true; + t = Math.max(t, grid[r][c]); + int res = Math.min(Math.min(dfs(grid, visit, r + 1, c, t), + dfs(grid, visit, r - 1, c, t)), + Math.min(dfs(grid, visit, r, c + 1, t), + dfs(grid, visit, r, c - 1, t))); + visit[r][c] = false; + return res; + } +} +``` + +```cpp +class Solution { +public: + int swimInWater(vector>& grid) { + int n = grid.size(); + vector> visit(n, vector(n, false)); + return dfs(grid, visit, 0, 0, 0); + } + +private: + int dfs(vector>& grid, vector>& visit, + int r, int c, int t) { + int n = grid.size(); + if (r < 0 || c < 0 || r >= n || c >= n || visit[r][c]) { + return 1000000; + } + if (r == n - 1 && c == n - 1) { + return max(t, grid[r][c]); + } + visit[r][c] = true; + t = max(t, grid[r][c]); + int res = min(min(dfs(grid, visit, r + 1, c, t), + dfs(grid, visit, r - 1, c, t)), + min(dfs(grid, visit, r, c + 1, t), + dfs(grid, visit, r, c - 1, t))); + visit[r][c] = false; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + swimInWater(grid) { + const n = grid.length; + const visit = Array.from({ length: n }, () => + Array(n).fill(false)); + + const dfs = (r, c, t) => { + if (r < 0 || c < 0 || r >= n || + c >= n || visit[r][c]) { + return 1000000; + } + if (r === n - 1 && c === n - 1) { + return Math.max(t, grid[r][c]); + } + visit[r][c] = true; + t = Math.max(t, grid[r][c]); + const res = Math.min( + Math.min(dfs(r + 1, c, t), + dfs(r - 1, c, t)), + Math.min(dfs(r, c + 1, t), + dfs(r, c - 1, t)) + ); + visit[r][c] = false; + return res; + } + + return dfs(0, 0, 0); + } +} +``` + +```csharp +public class Solution { + public int SwimInWater(int[][] grid) { + int n = grid.Length; + bool[][] visit = new bool[n][]; + for (int i = 0; i < n; i++) { + visit[i] = new bool[n]; + } + return Dfs(grid, visit, 0, 0, 0); + } + + private int Dfs(int[][] grid, bool[][] visit, + int r, int c, int t) { + int n = grid.Length; + if (r < 0 || c < 0 || r >= n || + c >= n || visit[r][c]) { + return 1000000; + } + if (r == n - 1 && c == n - 1) { + return Math.Max(t, grid[r][c]); + } + visit[r][c] = true; + t = Math.Max(t, grid[r][c]); + int res = Math.Min(Math.Min(Dfs(grid, visit, r + 1, c, t), + Dfs(grid, visit, r - 1, c, t)), + Math.Min(Dfs(grid, visit, r, c + 1, t), + Dfs(grid, visit, r, c - 1, t))); + visit[r][c] = false; + return res; + } +} +``` + +```go +func swimInWater(grid [][]int) int { + n := len(grid) + visit := make([][]bool, n) + for i := range visit { + visit[i] = make([]bool, n) + } + + var dfs func(r, c, t int) int + dfs = func(r, c, t int) int { + if r < 0 || c < 0 || r >= n || c >= n || visit[r][c] { + return 1000000 + } + if r == n-1 && c == n-1 { + return max(t, grid[r][c]) + } + visit[r][c] = true + t = max(t, grid[r][c]) + + res := min( + min(dfs(r+1, c, t), dfs(r-1, c, t)), + min(dfs(r, c+1, t), dfs(r, c-1, t)), + ) + + visit[r][c] = false + return res + } + + return dfs(0, 0, 0) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun swimInWater(grid: Array): Int { + val n = grid.size + val visit = Array(n) { BooleanArray(n) } + + fun dfs(r: Int, c: Int, t: Int): Int { + if (r < 0 || c < 0 || r >= n || c >= n || visit[r][c]) { + return 1000000 + } + if (r == n - 1 && c == n - 1) return maxOf(t, grid[r][c]) + visit[r][c] = true + val time = maxOf(t, grid[r][c]) + + val res = minOf( + dfs(r + 1, c, time), + dfs(r - 1, c, time), + dfs(r, c + 1, time), + dfs(r, c - 1, time) + ) + + visit[r][c] = false + return res + } + + return dfs(0, 0, 0) + } +} +``` + +```swift +class Solution { + func swimInWater(_ grid: [[Int]]) -> Int { + let n = grid.count + var visit = Array(repeating: Array(repeating: false, count: n), count: n) + + func dfs(_ node: (Int, Int), _ t: Int) -> Int { + let (r, c) = node + if r < 0 || c < 0 || r >= n || c >= n || visit[r][c] { + return 1000000 + } + if r == n - 1 && c == n - 1 { + return max(t, grid[r][c]) + } + visit[r][c] = true + let t = max(t, grid[r][c]) + let res = min(dfs((r + 1, c), t), + dfs((r - 1, c), t), + dfs((r, c + 1), t), + dfs((r, c - 1), t)) + visit[r][c] = false + return res + } + + return dfs((0, 0), 0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(4 ^ {n ^ 2})$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Depth First Search + +::tabs-start + +```python +class Solution: + def swimInWater(self, grid: List[List[int]]) -> int: + n = len(grid) + visit = [[False] * n for _ in range(n)] + minH = maxH = grid[0][0] + for row in range(n): + maxH = max(maxH, max(grid[row])) + minH = min(minH, min(grid[row])) + + def dfs(node, t): + r, c = node + if (min(r, c) < 0 or max(r, c) >= n or + visit[r][c] or grid[r][c] > t): + return False + if r == (n - 1) and c == (n - 1): + return True + visit[r][c] = True + return (dfs((r + 1, c), t) or + dfs((r - 1, c), t) or + dfs((r, c + 1), t) or + dfs((r, c - 1), t)) + + for t in range(minH, maxH): + if dfs((0, 0), t): + return t + for r in range(n): + for c in range(n): + visit[r][c] = False + + return maxH +``` + +```java +public class Solution { + public int swimInWater(int[][] grid) { + int n = grid.length; + boolean[][] visit = new boolean[n][n]; + int minH = grid[0][0], maxH = grid[0][0]; + for (int row = 0; row < n; row++) { + for (int col = 0; col < n; col++) { + maxH = Math.max(maxH, grid[row][col]); + minH = Math.min(minH, grid[row][col]); + } + } + + for (int t = minH; t < maxH; t++) { + if (dfs(grid, visit, 0, 0, t)) { + return t; + } + for (int r = 0; r < n; r++) { + Arrays.fill(visit[r], false); + } + } + return maxH; + } + + private boolean dfs(int[][] grid, boolean[][] visit, int r, int c, int t) { + if (r < 0 || c < 0 || r >= grid.length || + c >= grid.length || visit[r][c] || grid[r][c] > t) { + return false; + } + if (r == grid.length - 1 && c == grid.length - 1) { + return true; + } + visit[r][c] = true; + return dfs(grid, visit, r + 1, c, t) || + dfs(grid, visit, r - 1, c, t) || + dfs(grid, visit, r, c + 1, t) || + dfs(grid, visit, r, c - 1, t); + } +} +``` + +```cpp +class Solution { +public: + int swimInWater(vector>& grid) { + int n = grid.size(); + vector> visit(n, vector(n, false)); + int minH = grid[0][0], maxH = grid[0][0]; + for (int row = 0; row < n; row++) { + for (int col = 0; col < n; col++) { + maxH = max(maxH, grid[row][col]); + minH = min(minH, grid[row][col]); + } + } + + for (int t = minH; t < maxH; t++) { + if (dfs(grid, visit, 0, 0, t)) { + return t; + } + for (int r = 0; r < n; r++) { + fill(visit[r].begin(), visit[r].end(), false); + } + } + return maxH; + } + +private: + bool dfs(vector>& grid, vector>& visit, + int r, int c, int t) { + if (r < 0 || c < 0 || r >= grid.size() || + c >= grid.size() || visit[r][c] || grid[r][c] > t) { + return false; + } + if (r == grid.size() - 1 && c == grid.size() - 1) { + return true; + } + visit[r][c] = true; + return dfs(grid, visit, r + 1, c, t) || + dfs(grid, visit, r - 1, c, t) || + dfs(grid, visit, r, c + 1, t) || + dfs(grid, visit, r, c - 1, t); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + swimInWater(grid) { + const n = grid.length; + const visit = Array.from({ length: n }, () => + Array(n).fill(false)); + let minH = grid[0][0], maxH = grid[0][0]; + for (let row = 0; row < n; row++) { + for (let col = 0; col < n; col++) { + maxH = Math.max(maxH, grid[row][col]); + minH = Math.min(minH, grid[row][col]); + } + } + + const dfs = (node, t) => { + const [r, c] = node; + if (Math.min(r, c) < 0 || Math.max(r, c) >= n || + visit[r][c] || grid[r][c] > t) { + return false; + } + if (r === n - 1 && c === n - 1) { + return true; + } + visit[r][c] = true; + return dfs([r + 1, c], t) || + dfs([r - 1, c], t) || + dfs([r, c + 1], t) || + dfs([r, c - 1], t); + }; + + for (let t = minH; t < maxH; t++) { + if (dfs([0, 0], t)) { + return t; + } + for (let r = 0; r < n; r++) { + for (let c = 0; c < n; c++) { + visit[r][c] = false; + } + } + } + return maxH; + } +} +``` + +```csharp +public class Solution { + public int SwimInWater(int[][] grid) { + int n = grid.Length; + bool[][] visit = new bool[n][]; + for (int i = 0; i < n; i++) { + visit[i] = new bool[n]; + } + int minH = grid[0][0], maxH = grid[0][0]; + for (int row = 0; row < n; row++) { + for (int col = 0; col < n; col++) { + maxH = Math.Max(maxH, grid[row][col]); + minH = Math.Min(minH, grid[row][col]); + } + } + + for (int t = minH; t < maxH; t++) { + if (dfs(grid, visit, 0, 0, t)) { + return t; + } + for (int r = 0; r < n; r++) { + Array.Fill(visit[r], false); + } + } + return maxH; + } + + private bool dfs(int[][] grid, bool[][] visit, int r, int c, int t) { + if (r < 0 || c < 0 || r >= grid.Length || + c >= grid.Length || visit[r][c] || grid[r][c] > t) { + return false; + } + if (r == grid.Length - 1 && c == grid.Length - 1) { + return true; + } + visit[r][c] = true; + return dfs(grid, visit, r + 1, c, t) || + dfs(grid, visit, r - 1, c, t) || + dfs(grid, visit, r, c + 1, t) || + dfs(grid, visit, r, c - 1, t); + } +} +``` + +```go +func swimInWater(grid [][]int) int { + n := len(grid) + minH, maxH := grid[0][0], grid[0][0] + for r := 0; r < n; r++ { + for c := 0; c < n; c++ { + if grid[r][c] < minH { + minH = grid[r][c] + } + if grid[r][c] > maxH { + maxH = grid[r][c] + } + } + } + + visit := make([][]bool, n) + for i := range visit { + visit[i] = make([]bool, n) + } + + var dfs func(r, c, t int) bool + dfs = func(r, c, t int) bool { + if r < 0 || c < 0 || r >= n || c >= n || + visit[r][c] || grid[r][c] > t { + return false + } + if r == n-1 && c == n-1 { + return true + } + visit[r][c] = true + found := dfs(r+1, c, t) || dfs(r-1, c, t) || + dfs(r, c+1, t) || dfs(r, c-1, t) + return found + } + + for t := minH; t <= maxH; t++ { + if dfs(0, 0, t) { + return t + } + for i := range visit { + for j := range visit[i] { + visit[i][j] = false + } + } + } + + return maxH +} +``` + +```kotlin +class Solution { + fun swimInWater(grid: Array): Int { + val n = grid.size + var minH = grid[0][0] + var maxH = grid[0][0] + for (row in grid) { + minH = minOf(minH, row.minOrNull() ?: minH) + maxH = maxOf(maxH, row.maxOrNull() ?: maxH) + } + + val visit = Array(n) { BooleanArray(n) } + + fun dfs(r: Int, c: Int, t: Int): Boolean { + if (r < 0 || c < 0 || r >= n || c >= n || + visit[r][c] || grid[r][c] > t) { + return false + } + if (r == n - 1 && c == n - 1) { + return true + } + visit[r][c] = true + return dfs(r + 1, c, t) || dfs(r - 1, c, t) || + dfs(r, c + 1, t) || dfs(r, c - 1, t) + } + + for (t in minH..maxH) { + if (dfs(0, 0, t)) return t + for (r in 0 until n) { + visit[r].fill(false) + } + } + + return maxH + } +} +``` + +```swift +class Solution { + func swimInWater(_ grid: [[Int]]) -> Int { + let n = grid.count + var visit = Array(repeating: Array(repeating: false, count: n), count: n) + var minH = grid[0][0], maxH = grid[0][0] + + for row in 0.. Bool { + let (r, c) = node + if r < 0 || c < 0 || r >= n || c >= n || visit[r][c] || grid[r][c] > t { + return false + } + if r == n - 1 && c == n - 1 { + return true + } + visit[r][c] = true + return dfs((r + 1, c), t) || + dfs((r - 1, c), t) || + dfs((r, c + 1), t) || + dfs((r, c - 1), t) + } + + for t in minH...maxH { + if dfs((0, 0), t) { + return t + } + for r in 0.. int: + n = len(grid) + visit = [[False] * n for _ in range(n)] + minH = maxH = grid[0][0] + for row in range(n): + maxH = max(maxH, max(grid[row])) + minH = min(minH, min(grid[row])) + + def dfs(node, t): + r, c = node + if (min(r, c) < 0 or max(r, c) >= n or + visit[r][c] or grid[r][c] > t): + return False + if r == (n - 1) and c == (n - 1): + return True + visit[r][c] = True + return (dfs((r + 1, c), t) or + dfs((r - 1, c), t) or + dfs((r, c + 1), t) or + dfs((r, c - 1), t)) + + l, r = minH, maxH + while l < r: + m = (l + r) >> 1 + if dfs((0, 0), m): + r = m + else: + l = m + 1 + for row in range(n): + for col in range(n): + visit[row][col] = False + + return r +``` + +```java +public class Solution { + public int swimInWater(int[][] grid) { + int n = grid.length; + boolean[][] visit = new boolean[n][n]; + int minH = grid[0][0], maxH = grid[0][0]; + for (int row = 0; row < n; row++) { + for (int col = 0; col < n; col++) { + maxH = Math.max(maxH, grid[row][col]); + minH = Math.min(minH, grid[row][col]); + } + } + + int l = minH, r = maxH; + while (l < r) { + int m = (l + r) >> 1; + if (dfs(grid, visit, 0, 0, m)) { + r = m; + } else { + l = m + 1; + } + for (int row = 0; row < n; row++) { + Arrays.fill(visit[row], false); + } + } + return r; + } + + private boolean dfs(int[][] grid, boolean[][] visit, int r, int c, int t) { + if (r < 0 || c < 0 || r >= grid.length || + c >= grid.length || visit[r][c] || grid[r][c] > t) { + return false; + } + if (r == grid.length - 1 && c == grid.length - 1) { + return true; + } + visit[r][c] = true; + return dfs(grid, visit, r + 1, c, t) || + dfs(grid, visit, r - 1, c, t) || + dfs(grid, visit, r, c + 1, t) || + dfs(grid, visit, r, c - 1, t); + } +} +``` + +```cpp +class Solution { +public: + int swimInWater(vector>& grid) { + int n = grid.size(); + vector> visit(n, vector(n, false)); + int minH = grid[0][0], maxH = grid[0][0]; + for (int row = 0; row < n; row++) { + for (int col = 0; col < n; col++) { + maxH = max(maxH, grid[row][col]); + minH = min(minH, grid[row][col]); + } + } + + int l = minH, r = maxH; + while (l < r) { + int m = (l + r) >> 1; + if (dfs(grid, visit, 0, 0, m)) { + r = m; + } else { + l = m + 1; + } + for (int row = 0; row < n; row++) { + fill(visit[row].begin(), visit[row].end(), false); + } + } + return r; + } + +private: + bool dfs(vector>& grid, vector>& visit, + int r, int c, int t) { + if (r < 0 || c < 0 || r >= grid.size() || + c >= grid.size() || visit[r][c] || grid[r][c] > t) { + return false; + } + if (r == grid.size() - 1 && c == grid.size() - 1) { + return true; + } + visit[r][c] = true; + return dfs(grid, visit, r + 1, c, t) || + dfs(grid, visit, r - 1, c, t) || + dfs(grid, visit, r, c + 1, t) || + dfs(grid, visit, r, c - 1, t); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + swimInWater(grid) { + const n = grid.length; + const visit = Array.from({ length: n }, () => + Array(n).fill(false)); + let minH = grid[0][0], maxH = grid[0][0]; + for (let row = 0; row < n; row++) { + for (let col = 0; col < n; col++) { + maxH = Math.max(maxH, grid[row][col]); + minH = Math.min(minH, grid[row][col]); + } + } + + const dfs = (node, t) => { + const [r, c] = node; + if (Math.min(r, c) < 0 || Math.max(r, c) >= n || + visit[r][c] || grid[r][c] > t) { + return false; + } + if (r === n - 1 && c === n - 1) { + return true; + } + visit[r][c] = true; + return dfs([r + 1, c], t) || + dfs([r - 1, c], t) || + dfs([r, c + 1], t) || + dfs([r, c - 1], t); + }; + + let l = minH, r = maxH; + while (l < r) { + let m = (l + r) >> 1; + if (dfs([0, 0], m)) { + r = m; + } else { + l = m + 1; + } + for (let row = 0; row < n; row++) { + for (let col = 0; col < n; col++) { + visit[row][col] = false; + } + } + } + return r; + } +} +``` + +```csharp +public class Solution { + public int SwimInWater(int[][] grid) { + int n = grid.Length; + bool[][] visit = new bool[n][]; + for (int i = 0; i < n; i++) { + visit[i] = new bool[n]; + } + int minH = grid[0][0], maxH = grid[0][0]; + for (int row = 0; row < n; row++) { + for (int col = 0; col < n; col++) { + maxH = Math.Max(maxH, grid[row][col]); + minH = Math.Min(minH, grid[row][col]); + } + } + + int l = minH, r = maxH; + while (l < r) { + int m = (l + r) >> 1; + if (dfs(grid, visit, 0, 0, m)) { + r = m; + } else { + l = m + 1; + } + for (int row = 0; row < n; row++) { + Array.Fill(visit[row], false); + } + } + return r; + } + + private bool dfs(int[][] grid, bool[][] visit, int r, int c, int t) { + if (r < 0 || c < 0 || r >= grid.Length || + c >= grid.Length || visit[r][c] || grid[r][c] > t) { + return false; + } + if (r == grid.Length - 1 && c == grid.Length - 1) { + return true; + } + visit[r][c] = true; + return dfs(grid, visit, r + 1, c, t) || + dfs(grid, visit, r - 1, c, t) || + dfs(grid, visit, r, c + 1, t) || + dfs(grid, visit, r, c - 1, t); + } +} +``` + +```go +func swimInWater(grid [][]int) int { + n := len(grid) + minH, maxH := grid[0][0], grid[0][0] + for r := 0; r < n; r++ { + for c := 0; c < n; c++ { + if grid[r][c] < minH { + minH = grid[r][c] + } + if grid[r][c] > maxH { + maxH = grid[r][c] + } + } + } + + visit := make([][]bool, n) + for i := range visit { + visit[i] = make([]bool, n) + } + + var dfs func(r, c, t int) bool + dfs = func(r, c, t int) bool { + if r < 0 || c < 0 || r >= n || c >= n || + visit[r][c] || grid[r][c] > t { + return false + } + if r == n-1 && c == n-1 { + return true + } + visit[r][c] = true + found := dfs(r+1, c, t) || dfs(r-1, c, t) || + dfs(r, c+1, t) || dfs(r, c-1, t) + return found + } + + l, r := minH, maxH + for l < r { + m := (l + r) / 2 + if dfs(0, 0, m) { + r = m + } else { + l = m + 1 + } + for i := range visit { + for j := range visit[i] { + visit[i][j] = false + } + } + } + + return r +} +``` + +```kotlin +class Solution { + fun swimInWater(grid: Array): Int { + val n = grid.size + var minH = grid[0][0] + var maxH = grid[0][0] + for (row in grid) { + minH = minOf(minH, row.minOrNull() ?: minH) + maxH = maxOf(maxH, row.maxOrNull() ?: maxH) + } + + val visit = Array(n) { BooleanArray(n) } + + fun dfs(r: Int, c: Int, t: Int): Boolean { + if (r < 0 || c < 0 || r >= n || c >= n || + visit[r][c] || grid[r][c] > t) { + return false + } + if (r == n - 1 && c == n - 1) { + return true + } + visit[r][c] = true + return dfs(r + 1, c, t) || dfs(r - 1, c, t) || + dfs(r, c + 1, t) || dfs(r, c - 1, t) + } + + var l = minH + var r = maxH + while (l < r) { + val m = (l + r) / 2 + if (dfs(0, 0, m)) { + r = m + } else { + l = m + 1 + } + for (row in visit) { + row.fill(false) + } + } + + return r + } +} +``` + +```swift +class Solution { + func swimInWater(_ grid: [[Int]]) -> Int { + let n = grid.count + var visit = Array(repeating: Array(repeating: false, count: n), count: n) + var minH = grid[0][0], maxH = grid[0][0] + + for row in 0.. Bool { + let (r, c) = node + if r < 0 || c < 0 || r >= n || c >= n || visit[r][c] || grid[r][c] > t { + return false + } + if r == n - 1 && c == n - 1 { + return true + } + visit[r][c] = true + return dfs((r + 1, c), t) || + dfs((r - 1, c), t) || + dfs((r, c + 1), t) || + dfs((r, c - 1), t) + } + + var l = minH, r = maxH + while l < r { + let m = (l + r) >> 1 + if dfs((0, 0), m) { + r = m + } else { + l = m + 1 + } + for row in 0.. int: + N = len(grid) + visit = set() + minH = [[grid[0][0], 0, 0]] # (time/max-height, r, c) + directions = [[0, 1], [0, -1], [1, 0], [-1, 0]] + + visit.add((0, 0)) + while minH: + t, r, c = heapq.heappop(minH) + if r == N - 1 and c == N - 1: + return t + for dr, dc in directions: + neiR, neiC = r + dr, c + dc + if (neiR < 0 or neiC < 0 or + neiR == N or neiC == N or + (neiR, neiC) in visit + ): + continue + visit.add((neiR, neiC)) + heapq.heappush(minH, [max(t, grid[neiR][neiC]), neiR, neiC]) +``` + +```java +public class Solution { + public int swimInWater(int[][] grid) { + int N = grid.length; + boolean[][] visit = new boolean[N][N]; + PriorityQueue minHeap = new PriorityQueue<>( + Comparator.comparingInt(a -> a[0]) + ); + int[][] directions = { + {0, 1}, {0, -1}, {1, 0}, {-1, 0} + }; + + minHeap.offer(new int[]{grid[0][0], 0, 0}); + visit[0][0] = true; + + while (!minHeap.isEmpty()) { + int[] curr = minHeap.poll(); + int t = curr[0], r = curr[1], c = curr[2]; + if (r == N - 1 && c == N - 1) { + return t; + } + for (int[] dir : directions) { + int neiR = r + dir[0], neiC = c + dir[1]; + if (neiR >= 0 && neiC >= 0 && neiR < N && + neiC < N && !visit[neiR][neiC]) { + visit[neiR][neiC] = true; + minHeap.offer(new int[]{ + Math.max(t, grid[neiR][neiC]), + neiR, neiC + }); + } + } + } + return N * N; + } +} +``` + +```cpp +class Solution { +public: + int swimInWater(vector>& grid) { + int N = grid.size(); + set> visit; + priority_queue, + vector>, greater<>> minHeap; + vector> directions = { + {0, 1}, {0, -1}, {1, 0}, {-1, 0} + }; + + minHeap.push({grid[0][0], 0, 0}); + visit.insert({0, 0}); + + while (!minHeap.empty()) { + auto curr = minHeap.top(); + minHeap.pop(); + int t = curr[0], r = curr[1], c = curr[2]; + if (r == N - 1 && c == N - 1) { + return t; + } + for (const auto& dir : directions) { + int neiR = r + dir[0], neiC = c + dir[1]; + if (neiR < 0 || neiC < 0 || neiR == N || + neiC == N || visit.count({neiR, neiC})) { + continue; + } + visit.insert({neiR, neiC}); + minHeap.push({ + max(t, grid[neiR][neiC]), neiR, neiC + }); + } + } + + return N * N; + } +}; +``` + +```javascript +/** + * const { MinPriorityQueue } = require('@datastructures-js/priority-queue'); + */ + +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + swimInWater(grid) { + const N = grid.length; + const visit = new Set(); + const minPQ = new MinPriorityQueue(entry => entry[0]); + const directions = [ + [0, 1], + [0, -1], + [1, 0], + [-1, 0], + ]; + + minPQ.push([grid[0][0], 0, 0]); + visit.add('0,0'); + + while (!minPQ.isEmpty()) { + const [t, r, c] = minPQ.pop(); + if (r === N - 1 && c === N - 1) { + return t; + } + for (const [dr, dc] of directions) { + const neiR = r + dr; + const neiC = c + dc; + if ( + neiR < 0 || + neiC < 0 || + neiR >= N || + neiC >= N || + visit.has(`${neiR},${neiC}`) + ) { + continue; + } + visit.add(`${neiR},${neiC}`); + minPQ.push([ + Math.max(t, grid[neiR][neiC]), neiR, neiC + ]); + } + } + } +} +``` + +```csharp +public class Solution { + public int SwimInWater(int[][] grid) { + int N = grid.Length; + var visit = new HashSet<(int, int)>(); + var minHeap = new PriorityQueue<(int t, int r, int c), int>(); + int[][] directions = { + new int[]{0, 1}, new int[]{0, -1}, + new int[]{1, 0}, new int[]{-1, 0} + }; + + minHeap.Enqueue((grid[0][0], 0, 0), grid[0][0]); + visit.Add((0, 0)); + + while (minHeap.Count > 0) { + var curr = minHeap.Dequeue(); + int t = curr.t, r = curr.r, c = curr.c; + if (r == N - 1 && c == N - 1) { + return t; + } + foreach (var dir in directions) { + int neiR = r + dir[0], neiC = c + dir[1]; + if (neiR < 0 || neiC < 0 || neiR >= N || + neiC >= N || visit.Contains((neiR, neiC))) { + continue; + } + visit.Add((neiR, neiC)); + minHeap.Enqueue( + (Math.Max(t, grid[neiR][neiC]), neiR, neiC), + Math.Max(t, grid[neiR][neiC])); + } + } + + return N * N; + } +} +``` + +```go +type Node struct { + time, r, c int +} + +func swimInWater(grid [][]int) int { + N := len(grid) + directions := [][2]int{{0, 1}, {0, -1}, {1, 0}, {-1, 0}} + + pq := priorityqueue.NewWith(func(a, b interface{}) int { + return utils.IntComparator(a.(Node).time, b.(Node).time) + }) + + pq.Enqueue(Node{grid[0][0], 0, 0}) + visited := make(map[[2]int]bool) + visited[[2]int{0, 0}] = true + + for !pq.Empty() { + item, _ := pq.Dequeue() + node := item.(Node) + t, r, c := node.time, node.r, node.c + + if r == N-1 && c == N-1 { + return t + } + + for _, dir := range directions { + neiR, neiC := r+dir[0], c+dir[1] + if neiR < 0 || neiC < 0 || neiR >= N || neiC >= N || + visited[[2]int{neiR, neiC}] { + continue + } + + visited[[2]int{neiR, neiC}] = true + pq.Enqueue(Node{max(t, grid[neiR][neiC]), neiR, neiC}) + } + } + + return -1 +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun swimInWater(grid: Array): Int { + val N = grid.size + val directions = listOf(Pair(0, 1), Pair(0, -1), Pair(1, 0), Pair(-1, 0)) + + val minHeap = PriorityQueue(compareBy>> { it.first }) + minHeap.offer(Pair(grid[0][0], Pair(0, 0))) + + val visited = HashSet>() + visited.add(Pair(0, 0)) + + while (minHeap.isNotEmpty()) { + val (t, pos) = minHeap.poll() + val (r, c) = pos + + if (r == N - 1 && c == N - 1) return t + + for ((dr, dc) in directions) { + val neiR = r + dr + val neiC = c + dc + if (neiR !in 0 until N || neiC !in 0 until N || Pair(neiR, neiC) in visited) { + continue + } + + visited.add(Pair(neiR, neiC)) + minHeap.offer(Pair(maxOf(t, grid[neiR][neiC]), Pair(neiR, neiC))) + } + } + + return -1 + } +} +``` + +```swift +struct Item: Comparable { + let time: Int + let row: Int + let col: Int + + static func < (lhs: Item, rhs: Item) -> Bool { + return lhs.time < rhs.time + } +} + +class Solution { + func swimInWater(_ grid: [[Int]]) -> Int { + let N = grid.count + var visit = Set<[Int]>() + var minHeap = Heap() + + let directions = [(0, 1), (0, -1), (1, 0), (-1, 0)] + + minHeap.insert(Item(time: grid[0][0], row: 0, col: 0)) + visit.insert([0, 0]) + + while !minHeap.isEmpty { + let item = minHeap.removeMin() + let t = item.time, r = item.row, c = item.col + + if r == N - 1 && c == N - 1 { + return t + } + + for (dr, dc) in directions { + let neiR = r + dr, neiC = c + dc + if neiR < 0 || neiC < 0 || neiR == N || neiC == N || visit.contains([neiR, neiC]) { + continue + } + visit.insert([neiR, neiC]) + minHeap.insert(Item(time: max(t, grid[neiR][neiC]), row: neiR, col: neiC)) + } + } + + return -1 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 \log n)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 5. Kruskal's Algorithm + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu = self.find(u) + pv = self.find(v) + if pu == pv: + return False + if self.Size[pu] < self.Size[pv]: + pu, pv = pv, pu + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + return True + + def connected(self, u, v): + return self.find(u) == self.find(v) + +class Solution: + def swimInWater(self, grid: List[List[int]]) -> int: + N = len(grid) + dsu = DSU(N * N) + positions = sorted((grid[r][c], r, c) for r in range(N) for c in range(N)) + directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] + + for t, r, c in positions: + for dr, dc in directions: + nr, nc = r + dr, c + dc + if 0 <= nr < N and 0 <= nc < N and grid[nr][nc] <= t: + dsu.union(r * N + c, nr * N + nc) + if dsu.connected(0, N * N - 1): + return t +``` + +```java +class DSU { + private int[] Parent; + private int[] Size; + + public DSU(int n) { + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) Parent[i] = i; + Arrays.fill(Size, 1); + } + + public int find(int node) { + if (Parent[node] != node) + Parent[node] = find(Parent[node]); + return Parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (Size[pu] < Size[pv]) { + int temp = pu; + pu = pv; + pv = temp; + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } + + public boolean connected(int u, int v) { + return find(u) == find(v); + } +} + +public class Solution { + public int swimInWater(int[][] grid) { + int N = grid.length; + DSU dsu = new DSU(N * N); + List positions = new ArrayList<>(); + for (int r = 0; r < N; r++) + for (int c = 0; c < N; c++) + positions.add(new int[]{grid[r][c], r, c}); + positions.sort(Comparator.comparingInt(a -> a[0])); + int[][] directions = { + {0, 1}, {1, 0}, {0, -1}, {-1, 0} + }; + + for (int[] pos : positions) { + int t = pos[0], r = pos[1], c = pos[2]; + for (int[] dir : directions) { + int nr = r + dir[0], nc = c + dir[1]; + if (nr >= 0 && nr < N && nc >= 0 && + nc < N && grid[nr][nc] <= t) { + dsu.union(r * N + c, nr * N + nc); + } + } + if (dsu.connected(0, N * N - 1)) return t; + } + return N * N; + } +} +``` + +```cpp +class DSU { + vector Parent, Size; +public: + DSU(int n) : Parent(n + 1), Size(n + 1, 1) { + for (int i = 0; i <= n; i++) Parent[i] = i; + } + + int find(int node) { + if (Parent[node] != node) + Parent[node] = find(Parent[node]); + return Parent[node]; + } + + bool unionSets(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (Size[pu] < Size[pv]) swap(pu, pv); + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } + + bool connected(int u, int v) { + return find(u) == find(v); + } +}; + +class Solution { +public: + int swimInWater(vector>& grid) { + int N = grid.size(); + DSU dsu(N * N); + vector> positions; + for (int r = 0; r < N; r++) + for (int c = 0; c < N; c++) + positions.emplace_back(grid[r][c], r, c); + + sort(positions.begin(), positions.end()); + vector> directions = { + {0, 1}, {1, 0}, {0, -1}, {-1, 0} + }; + + for (auto& [t, r, c] : positions) { + for (auto& [dr, dc] : directions) { + int nr = r + dr, nc = c + dc; + if (nr >= 0 && nr < N && nc >= 0 && + nc < N && grid[nr][nc] <= t) { + dsu.unionSets(r * N + c, nr * N + nc); + } + } + if (dsu.connected(0, N * N - 1)) return t; + } + return N * N; + } +}; +``` + +```javascript +class DSU { + constructor(n) { + this.Parent = Array.from({ length: n + 1 }, (_, i) => i); + this.Size = Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.Parent[node] !== node) { + this.Parent[node] = this.find(this.Parent[node]); + } + return this.Parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u), pv = this.find(v); + if (pu === pv) return false; + if (this.Size[pu] < this.Size[pv]) [pu, pv] = [pv, pu]; + this.Size[pu] += this.Size[pv]; + this.Parent[pv] = pu; + return true; + } + + /** + * @param {number} n + * @param {number} n + * @return {boolean} + */ + connected(u, v) { + return this.find(u) == this.find(v); + } +} + +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + swimInWater(grid) { + const N = grid.length; + const dsu = new DSU(N * N); + const positions = []; + for (let r = 0; r < N; r++) { + for (let c = 0; c < N; c++) { + positions.push([grid[r][c], r, c]); + } + } + positions.sort((a, b) => a[0] - b[0]); + const directions = [ + [0, 1], [1, 0], [0, -1], [-1, 0] + ]; + + for (const [t, r, c] of positions) { + for (const [dr, dc] of directions) { + const nr = r + dr, nc = c + dc; + if (nr >= 0 && nr < N && nc >= 0 && + nc < N && grid[nr][nc] <= t) { + dsu.union(r * N + c, nr * N + nc); + } + } + if (dsu.connected(0, N * N - 1)) return t; + } + return N * N; + } +} +``` + +```csharp +public class DSU { + private int[] Parent, Size; + + public DSU(int n) { + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) Parent[i] = i; + Array.Fill(Size, 1); + } + + public int Find(int node) { + if (Parent[node] != node) + Parent[node] = Find(Parent[node]); + return Parent[node]; + } + + public bool Union(int u, int v) { + int pu = Find(u), pv = Find(v); + if (pu == pv) return false; + if (Size[pu] < Size[pv]) { + int temp = pu; + pu = pv; + pv = temp; + } + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } + + public bool Connected(int u, int v) { + return Find(u) == Find(v); + } +} + +public class Solution { + public int SwimInWater(int[][] grid) { + int N = grid.Length; + DSU dsu = new DSU(N * N); + List positions = new List(); + for (int r = 0; r < N; r++) + for (int c = 0; c < N; c++) + positions.Add(new int[] {grid[r][c], r, c}); + positions.Sort((a, b) => a[0] - b[0]); + int[][] directions = new int[][] { + new int[] {0, 1}, new int[] {1, 0}, + new int[] {0, -1}, new int[] {-1, 0} + }; + + foreach (var pos in positions) { + int t = pos[0], r = pos[1], c = pos[2]; + foreach (var dir in directions) { + int nr = r + dir[0], nc = c + dir[1]; + if (nr >= 0 && nr < N && nc >= 0 && + nc < N && grid[nr][nc] <= t) { + dsu.Union(r * N + c, nr * N + nc); + } + } + if (dsu.Connected(0, N * N - 1)) return t; + } + return N * N; + } +} +``` + +```go +type DSU struct { + Parent, Size []int +} + +func NewDSU(n int) *DSU { + dsu := &DSU{ + Parent: make([]int, n+1), + Size: make([]int, n+1), + } + for i := 0; i <= n; i++ { + dsu.Parent[i] = i + dsu.Size[i] = 1 + } + return dsu +} + +func (dsu *DSU) Find(node int) int { + if dsu.Parent[node] != node { + dsu.Parent[node] = dsu.Find(dsu.Parent[node]) + } + return dsu.Parent[node] +} + +func (dsu *DSU) Union(u, v int) bool { + pu, pv := dsu.Find(u), dsu.Find(v) + if pu == pv { + return false + } + if dsu.Size[pu] < dsu.Size[pv] { + pu, pv = pv, pu + } + dsu.Size[pu] += dsu.Size[pv] + dsu.Parent[pv] = pu + return true +} + +func (dsu *DSU) Connected(u, v int) bool { + return dsu.Find(u) == dsu.Find(v) +} + +func swimInWater(grid [][]int) int { + N := len(grid) + dsu := NewDSU(N * N) + positions := make([][3]int, 0, N*N) + for r := 0; r < N; r++ { + for c := 0; c < N; c++ { + positions = append(positions, [3]int{grid[r][c], r, c}) + } + } + sort.Slice(positions, func(i, j int) bool { + return positions[i][0] < positions[j][0] + }) + directions := [][2]int{{0, 1}, {1, 0}, {0, -1}, {-1, 0}} + + for _, pos := range positions { + t, r, c := pos[0], pos[1], pos[2] + for _, d := range directions { + nr, nc := r+d[0], c+d[1] + if nr >= 0 && nc >= 0 && nr < N && nc < N && grid[nr][nc] <= t { + dsu.Union(r*N+c, nr*N+nc) + } + } + if dsu.Connected(0, N*N-1) { + return t + } + } + return -1 +} +``` + +```kotlin +class DSU(n: Int) { + private val parent = IntArray(n + 1) { it } + private val size = IntArray(n + 1) { 1 } + + fun find(node: Int): Int { + if (parent[node] != node) { + parent[node] = find(parent[node]) + } + return parent[node] + } + + fun union(u: Int, v: Int): Boolean { + var pu = find(u) + var pv = find(v) + if (pu == pv) return false + if (size[pu] < size[pv]) { + val temp = pu + pu = pv + pv = temp + } + size[pu] += size[pv] + parent[pv] = pu + return true + } + + fun connected(u: Int, v: Int): Boolean { + return find(u) == find(v) + } +} + +class Solution { + fun swimInWater(grid: Array): Int { + val N = grid.size + val dsu = DSU(N * N) + val positions = mutableListOf>() + for (r in grid.indices) { + for (c in grid[r].indices) { + positions.add(Triple(grid[r][c], r, c)) + } + } + positions.sortBy { it.first } + val directions = listOf(Pair(0, 1), Pair(1, 0), Pair(0, -1), Pair(-1, 0)) + + for ((t, r, c) in positions) { + for ((dr, dc) in directions) { + val nr = r + dr + val nc = c + dc + if (nr in 0 until N && nc in 0 until N && grid[nr][nc] <= t) { + dsu.union(r * N + c, nr * N + nc) + } + } + if (dsu.connected(0, N * N - 1)) { + return t + } + } + return -1 + } +} +``` + +```swift +class DSU { + private var parent: [Int] + private var size: [Int] + + init(_ n: Int) { + parent = Array(0...(n - 1)) + size = Array(repeating: 1, count: n) + } + + func find(_ node: Int) -> Int { + if parent[node] != node { + parent[node] = find(parent[node]) + } + return parent[node] + } + + func union(_ u: Int, _ v: Int) -> Bool { + let pu = find(u) + let pv = find(v) + if pu == pv { + return false + } + if size[pu] < size[pv] { + parent[pu] = pv + size[pv] += size[pu] + } else { + parent[pv] = pu + size[pu] += size[pv] + } + return true + } + + func connected(_ u: Int, _ v: Int) -> Bool { + return find(u) == find(v) + } +} + +class Solution { + func swimInWater(_ grid: [[Int]]) -> Int { + let N = grid.count + let dsu = DSU(N * N) + var positions = [(Int, Int, Int)]() + let directions = [(0, 1), (1, 0), (0, -1), (-1, 0)] + + for r in 0..= 0, nc >= 0, nr < N, nc < N, grid[nr][nc] <= t { + dsu.union(r * N + c, nr * N + nc) + } + } + if dsu.connected(0, N * N - 1) { + return t + } + } + return -1 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2 \log n)$ +* Space complexity: $O(n ^ 2)$ \ No newline at end of file diff --git a/articles/symmetric-tree.md b/articles/symmetric-tree.md new file mode 100644 index 000000000..615d4dd34 --- /dev/null +++ b/articles/symmetric-tree.md @@ -0,0 +1,447 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def isSymmetric(self, root: Optional[TreeNode]) -> bool: + def dfs(left, right): + if not left and not right: + return True + if not left or not right: + return False + return ( + left.val == right.val and + dfs(left.left, right.right) and + dfs(left.right, right.left) + ) + return dfs(root.left, root.right) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean isSymmetric(TreeNode root) { + return dfs(root.left, root.right); + } + + private boolean dfs(TreeNode left, TreeNode right) { + if (left == null && right == null) { + return true; + } + if (left == null || right == null) { + return false; + } + return left.val == right.val && + dfs(left.left, right.right) && + dfs(left.right, right.left); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool isSymmetric(TreeNode* root) { + return dfs(root->left, root->right); + } + +private: + bool dfs(TreeNode* left, TreeNode* right) { + if (!left && !right) { + return true; + } + if (!left || !right) { + return false; + } + return (left->val == right->val) && + dfs(left->left, right->right) && + dfs(left->right, right->left); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isSymmetric(root) { + const dfs = (left, right) => { + if (!left && !right) { + return true; + } + if (!left || !right) { + return false; + } + return ( + left.val === right.val && + dfs(left.left, right.right) && + dfs(left.right, right.left) + ); + }; + return dfs(root.left, root.right); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def isSymmetric(self, root: Optional[TreeNode]) -> bool: + if not root: + return True + + stack = [(root.left, root.right)] + while stack: + left, right = stack.pop() + if not left and not right: + continue + if not left or not right or left.val != right.val: + return False + stack.append((left.left, right.right)) + stack.append((left.right, right.left)) + return True +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean isSymmetric(TreeNode root) { + if (root == null) return true; + + Stack stack = new Stack<>(); + stack.push(new TreeNode[]{root.left, root.right}); + + while (!stack.isEmpty()) { + TreeNode[] nodes = stack.pop(); + TreeNode left = nodes[0], right = nodes[1]; + + if (left == null && right == null) continue; + if (left == null || right == null || left.val != right.val) { + return false; + } + + stack.push(new TreeNode[]{left.left, right.right}); + stack.push(new TreeNode[]{left.right, right.left}); + } + + return true; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool isSymmetric(TreeNode* root) { + if (!root) return true; + + std::stack> stack; + stack.push({root->left, root->right}); + + while (!stack.empty()) { + auto [left, right] = stack.top(); + stack.pop(); + + if (!left && !right) continue; + if (!left || !right || left->val != right->val) { + return false; + } + stack.push({left->left, right->right}); + stack.push({left->right, right->left}); + } + + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isSymmetric(root) { + if (!root) return true; + + const stack = [[root.left, root.right]]; + + while (stack.length > 0) { + const [left, right] = stack.pop(); + + if (!left && !right) continue; + if (!left || !right || left.val !== right.val) { + return false; + } + + stack.push([left.left, right.right]); + stack.push([left.right, right.left]); + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def isSymmetric(self, root: Optional[TreeNode]) -> bool: + if not root: + return True + + queue = deque([(root.left, root.right)]) + while queue: + for _ in range(len(queue)): + left, right = queue.popleft() + if not left and not right: + continue + if not left or not right or left.val != right.val: + return False + queue.append((left.left, right.right)) + queue.append((left.right, right.left)) + + return True +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public boolean isSymmetric(TreeNode root) { + if (root == null) return true; + + Queue queue = new LinkedList<>(); + queue.add(new TreeNode[]{root.left, root.right}); + + while (!queue.isEmpty()) { + for (int i = queue.size(); i > 0; i--) { + TreeNode[] nodes = queue.poll(); + TreeNode left = nodes[0], right = nodes[1]; + + if (left == null && right == null) continue; + if (left == null || right == null || left.val != right.val) { + return false; + } + queue.add(new TreeNode[]{left.left, right.right}); + queue.add(new TreeNode[]{left.right, right.left}); + } + } + + return true; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool isSymmetric(TreeNode* root) { + if (!root) return true; + + queue> queue; + queue.push({root->left, root->right}); + + while (!queue.empty()) { + for (int i = queue.size(); i > 0; i--) { + auto [left, right] = queue.front(); + queue.pop(); + + if (!left && !right) continue; + if (!left || !right || left->val != right->val) { + return false; + } + queue.push({left->left, right->right}); + queue.push({left->right, right->left}); + } + } + + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isSymmetric(root) { + if (!root) return true; + + const queue = new Queue([[root.left, root.right]]); + + while (!queue.isEmpty()) { + for (let i = queue.size(); i > 0; i--) { + const [left, right] = queue.pop(); + + if (!left && !right) continue; + if (!left || !right || left.val !== right.val) { + return false; + } + queue.push([left.left, right.right]); + queue.push([left.right, right.left]); + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/target-sum.md b/articles/target-sum.md new file mode 100644 index 000000000..71c10c0c2 --- /dev/null +++ b/articles/target-sum.md @@ -0,0 +1,754 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def findTargetSumWays(self, nums: List[int], target: int) -> int: + + def backtrack(i, total): + if i ==len(nums): + return total == target + + return (backtrack(i + 1, total + nums[i]) + + backtrack(i + 1, total - nums[i])) + + return backtrack(0, 0) +``` + +```java +public class Solution { + public int findTargetSumWays(int[] nums, int target) { + return backtrack(0, 0, nums, target); + } + + private int backtrack(int i, int total, int[] nums, int target) { + if (i == nums.length) { + return total == target ? 1 : 0; + } + return backtrack(i + 1, total + nums[i], nums, target) + + backtrack(i + 1, total - nums[i], nums, target); + } +} +``` + +```cpp +class Solution { +public: + int findTargetSumWays(vector& nums, int target) { + return backtrack(0, 0, nums, target); + } + + int backtrack(int i, int total, vector& nums, int target) { + if (i == nums.size()) { + return total == target; + } + return backtrack(i + 1, total + nums[i], nums, target) + + backtrack(i + 1, total - nums[i], nums, target); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + findTargetSumWays(nums, target) { + + const backtrack = (i, total) => { + if (i === nums.length) { + return total === target ? 1 : 0; + } + return backtrack(i + 1, total + nums[i]) + + backtrack(i + 1, total - nums[i]); + } + + return backtrack(0, 0); + } +} +``` + +```csharp +public class Solution { + public int FindTargetSumWays(int[] nums, int target) { + return Backtrack(0, 0, nums, target); + } + + private int Backtrack(int i, int total, int[] nums, int target) { + if (i == nums.Length) { + return total == target ? 1 : 0; + } + return Backtrack(i + 1, total + nums[i], nums, target) + + Backtrack(i + 1, total - nums[i], nums, target); + } +} +``` + +```go +func findTargetSumWays(nums []int, target int) int { + var backtrack func(i int, total int) int + backtrack = func(i int, total int) int { + if i == len(nums) { + if total == target { + return 1 + } + return 0 + } + return backtrack(i+1, total+nums[i]) + backtrack(i+1, total-nums[i]) + } + + return backtrack(0, 0) +} +``` + +```kotlin +class Solution { + fun findTargetSumWays(nums: IntArray, target: Int): Int { + fun backtrack(i: Int, total: Int): Int { + if (i == nums.size) { + return if (total == target) 1 else 0 + } + return backtrack(i + 1, total + nums[i]) + + backtrack(i + 1, total - nums[i]) + } + + return backtrack(0, 0) + } +} +``` + +```swift +class Solution { + func findTargetSumWays(_ nums: [Int], _ target: Int) -> Int { + func backtrack(_ i: Int, _ total: Int) -> Int { + if i == nums.count { + return total == target ? 1 : 0 + } + return backtrack(i + 1, total + nums[i]) + backtrack(i + 1, total - nums[i]) + } + return backtrack(0, 0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def findTargetSumWays(self, nums: List[int], target: int) -> int: + dp = {} # (index, total) -> # of ways + + def backtrack(i, total): + if i == len(nums): + return 1 if total == target else 0 + if (i, total) in dp: + return dp[(i, total)] + + dp[(i, total)] = (backtrack(i + 1, total + nums[i]) + + backtrack(i + 1, total - nums[i])) + return dp[(i, total)] + + return backtrack(0, 0) +``` + +```java +public class Solution { + private int[][] dp; + private int totalSum; + + public int findTargetSumWays(int[] nums, int target) { + totalSum = 0; + for (int num : nums) totalSum += num; + dp = new int[nums.length][2 * totalSum + 1]; + for (int i = 0; i < nums.length; i++) { + for (int j = 0; j < 2 * totalSum + 1; j++) { + dp[i][j] = Integer.MIN_VALUE; + } + } + return backtrack(0, 0, nums, target); + } + + private int backtrack(int i, int total, int[] nums, int target) { + if (i == nums.length) { + return total == target ? 1 : 0; + } + if (dp[i][total + totalSum] != Integer.MIN_VALUE) { + return dp[i][total + totalSum]; + } + dp[i][total + totalSum] = backtrack(i + 1, total + nums[i], nums, target) + + backtrack(i + 1, total - nums[i], nums, target); + return dp[i][total + totalSum]; + } +} +``` + +```cpp +class Solution { + vector> dp; + int totalSum; + +public: + int findTargetSumWays(vector& nums, int target) { + totalSum = accumulate(nums.begin(), nums.end(), 0); + dp = vector>(nums.size(), vector(2 * totalSum + 1, INT_MIN)); + return backtrack(0, 0, nums, target); + } + + int backtrack(int i, int total, vector& nums, int target) { + if (i == nums.size()) { + return total == target; + } + if (dp[i][total + totalSum] != INT_MIN) { + return dp[i][total + totalSum]; + } + dp[i][total + totalSum] = backtrack(i + 1, total + nums[i], nums, target) + + backtrack(i + 1, total - nums[i], nums, target); + return dp[i][total + totalSum]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + findTargetSumWays(nums, target) { + const NEG_INF = Number.MIN_SAFE_INTEGER; + const totalSum = nums.reduce((a, b) => a + b, 0); + const dp = Array.from({ length: nums.length }, () => + Array(2 * totalSum + 1).fill(NEG_INF)); + + const backtrack = (i, total) => { + if (i === nums.length) { + return total === target ? 1 : 0; + } + if (dp[i][total + totalSum] !== NEG_INF) { + return dp[i][total + totalSum]; + } + dp[i][total + totalSum] = backtrack(i + 1, total + nums[i]) + + backtrack(i + 1, total - nums[i]); + return dp[i][total + totalSum]; + } + + return backtrack(0, 0); + } +} +``` + +```csharp +public class Solution { + private int[,] dp; + private int totalSum; + + public int FindTargetSumWays(int[] nums, int target) { + totalSum = 0; + foreach (var num in nums) totalSum += num; + dp = new int[nums.Length, 2 * totalSum + 1]; + for (int i = 0; i < nums.Length; i++) { + for (int j = 0; j < 2 * totalSum + 1; j++) { + dp[i, j] = int.MinValue; + } + } + return Backtrack(0, 0, nums, target); + } + + private int Backtrack(int i, int total, int[] nums, int target) { + if (i == nums.Length) { + return total == target ? 1 : 0; + } + + if (dp[i, total + totalSum] != int.MinValue) { + return dp[i, total + totalSum]; + } + + dp[i, total + totalSum] = Backtrack(i + 1, total + nums[i], nums, target) + + Backtrack(i + 1, total - nums[i], nums, target); + return dp[i, total + totalSum]; + } +} +``` + +```go +func findTargetSumWays(nums []int, target int) int { + totalSum := 0 + for _, num := range nums { + totalSum += num + } + + dp := make([][]int, len(nums)) + for i := range dp { + dp[i] = make([]int, 2*totalSum+1) + for j := range dp[i] { + dp[i][j] = math.MinInt32 + } + } + + var backtrack func(i, total int) int + backtrack = func(i, total int) int { + if i == len(nums) { + if total == target { + return 1 + } + return 0 + } + + if dp[i][total+totalSum] != math.MinInt32 { + return dp[i][total+totalSum] + } + + dp[i][total+totalSum] = (backtrack(i+1, total+nums[i]) + + backtrack(i+1, total-nums[i])) + return dp[i][total+totalSum] + } + + return backtrack(0, 0) +} +``` + +```kotlin +class Solution { + fun findTargetSumWays(nums: IntArray, target: Int): Int { + var totalSum = nums.sum() + var dp = Array(nums.size) { IntArray(2 * totalSum + 1) { Int.MIN_VALUE } } + + fun backtrack(i: Int, total: Int): Int { + if (i == nums.size) { + return if (total == target) 1 else 0 + } + if (dp[i][total + totalSum] != Int.MIN_VALUE) { + return dp[i][total + totalSum] + } + dp[i][total + totalSum] = backtrack(i + 1, total + nums[i]) + + backtrack(i + 1, total - nums[i]) + return dp[i][total + totalSum] + } + + return backtrack(0, 0) + } +} +``` + +```swift +class Solution { + func findTargetSumWays(_ nums: [Int], _ target: Int) -> Int { + let totalSum = nums.reduce(0, +) + var dp = Array(repeating: Array(repeating: Int.min, count: 2 * totalSum + 1), count: nums.count) + + func backtrack(_ i: Int, _ total: Int) -> Int { + if i == nums.count { + return total == target ? 1 : 0 + } + if dp[i][total + totalSum] != Int.min { + return dp[i][total + totalSum] + } + dp[i][total + totalSum] = backtrack(i + 1, total + nums[i]) + + backtrack(i + 1, total - nums[i]) + return dp[i][total + totalSum] + } + + return backtrack(0, 0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the length of the array $nums$ and $m$ is the sum of all the elements in the array. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def findTargetSumWays(self, nums: List[int], target: int) -> int: + n = len(nums) + dp = [defaultdict(int) for _ in range(n + 1)] + dp[0][0] = 1 + + for i in range(n): + for total, count in dp[i].items(): + dp[i + 1][total + nums[i]] += count + dp[i + 1][total - nums[i]] += count + + return dp[n][target] +``` + +```java +public class Solution { + public int findTargetSumWays(int[] nums, int target) { + int n = nums.length; + Map[] dp = new HashMap[n + 1]; + for (int i = 0; i <= n; i++) { + dp[i] = new HashMap<>(); + } + dp[0].put(0, 1); + + for (int i = 0; i < n; i++) { + for (Map.Entry entry : dp[i].entrySet()) { + int total = entry.getKey(); + int count = entry.getValue(); + dp[i + 1].put(total + nums[i], + dp[i + 1].getOrDefault(total + nums[i], 0) + count); + dp[i + 1].put(total - nums[i], + dp[i + 1].getOrDefault(total - nums[i], 0) + count); + } + } + return dp[n].getOrDefault(target, 0); + } +} +``` + +```cpp +class Solution { +public: + int findTargetSumWays(vector& nums, int target) { + int n = nums.size(); + vector> dp(n + 1); + dp[0][0] = 1; + + for (int i = 0; i < n; i++) { + for (auto &p : dp[i]) { + dp[i + 1][p.first + nums[i]] += p.second; + dp[i + 1][p.first - nums[i]] += p.second; + } + } + return dp[n][target]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + findTargetSumWays(nums, target) { + const n = nums.length; + let dp = Array.from({ length: n + 1 }, () => ({})); + dp[0][0] = 1; + + for (let i = 0; i < n; i++) { + for (let total in dp[i]) { + total = Number(total); + let count = dp[i][total]; + dp[i + 1][total + nums[i]] = (dp[i + 1][total + nums[i]] || 0) + count; + dp[i + 1][total - nums[i]] = (dp[i + 1][total - nums[i]] || 0) + count; + } + } + return dp[n][target] || 0; + } +} +``` + +```csharp +public class Solution { + public int FindTargetSumWays(int[] nums, int S) { + int n = nums.Length; + Dictionary[] dp = new Dictionary[n + 1]; + for (int i = 0; i <= n; i++) { + dp[i] = new Dictionary(); + } + dp[0][0] = 1; + + for (int i = 0; i < n; i++) { + foreach (var entry in dp[i]) { + int total = entry.Key; + int count = entry.Value; + if (!dp[i + 1].ContainsKey(total + nums[i])) { + dp[i + 1][total + nums[i]] = 0; + } + dp[i + 1][total + nums[i]] += count; + + if (!dp[i + 1].ContainsKey(total - nums[i])) { + dp[i + 1][total - nums[i]] = 0; + } + dp[i + 1][total - nums[i]] += count; + } + } + return dp[n].ContainsKey(S) ? dp[n][S] : 0; + } +} +``` + +```go +func findTargetSumWays(nums []int, target int) int { + n := len(nums) + dp := make([]map[int]int, n+1) + + for i := 0; i <= n; i++ { + dp[i] = make(map[int]int) + } + + dp[0][0] = 1 + + for i := 0; i < n; i++ { + for total, count := range dp[i] { + dp[i+1][total+nums[i]] += count + dp[i+1][total-nums[i]] += count + } + } + + return dp[n][target] +} +``` + +```kotlin +class Solution { + fun findTargetSumWays(nums: IntArray, target: Int): Int { + val n = nums.size + val dp = Array(n + 1) { mutableMapOf() } + + dp[0][0] = 1 + + for (i in 0 until n) { + for ((total, count) in dp[i]) { + dp[i + 1][total + nums[i]] = dp[i + 1].getOrDefault(total + nums[i], 0) + count + dp[i + 1][total - nums[i]] = dp[i + 1].getOrDefault(total - nums[i], 0) + count + } + } + + return dp[n][target] ?: 0 + } +} +``` + +```swift +class Solution { + func findTargetSumWays(_ nums: [Int], _ target: Int) -> Int { + let n = nums.count + var dp = Array(repeating: [Int: Int](), count: n + 1) + dp[0][0] = 1 + + for i in 0.. Where $n$ is the length of the array $nums$ and $m$ is the sum of all the elements in the array. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def findTargetSumWays(self, nums: List[int], target: int) -> int: + dp = defaultdict(int) + dp[0] = 1 + + for num in nums: + next_dp = defaultdict(int) + for total, count in dp.items(): + next_dp[total + num] += count + next_dp[total - num] += count + dp = next_dp + + return dp[target] +``` + +```java +public class Solution { + public int findTargetSumWays(int[] nums, int target) { + Map dp = new HashMap<>(); + dp.put(0, 1); + + for (int num : nums) { + Map nextDp = new HashMap<>(); + for (Map.Entry entry : dp.entrySet()) { + int total = entry.getKey(); + int count = entry.getValue(); + nextDp.put(total + num, + nextDp.getOrDefault(total + num, 0) + count); + nextDp.put(total - num, + nextDp.getOrDefault(total - num, 0) + count); + } + dp = nextDp; + } + return dp.getOrDefault(target, 0); + } +} +``` + +```cpp +class Solution { +public: + int findTargetSumWays(vector& nums, int target) { + unordered_map dp; + dp[0] = 1; + + for (int num : nums) { + unordered_map nextDp; + for (auto& entry : dp) { + int total = entry.first; + int count = entry.second; + nextDp[total + num] += count; + nextDp[total - num] += count; + } + dp = nextDp; + } + return dp[target]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + findTargetSumWays(nums, target) { + let dp = new Map(); + dp.set(0, 1); + + for (let num of nums) { + let nextDp = new Map(); + for (let [total, count] of dp) { + nextDp.set((total + num), + (nextDp.get((total + num)) || 0) + count); + nextDp.set((total - num), + (nextDp.get((total - num)) || 0) + count); + } + dp = nextDp; + } + return dp.get(target) || 0; + } +} +``` + +```csharp +public class Solution { + public int FindTargetSumWays(int[] nums, int target) { + Dictionary dp = new Dictionary(); + dp[0] = 1; + + foreach (int num in nums) { + Dictionary nextDp = new Dictionary(); + foreach (var entry in dp) { + int total = entry.Key; + int count = entry.Value; + + if (!nextDp.ContainsKey(total + num)) { + nextDp[total + num] = 0; + } + nextDp[total + num] += count; + + if (!nextDp.ContainsKey(total - num)) { + nextDp[total - num] = 0; + } + nextDp[total - num] += count; + } + dp = nextDp; + } + return dp.ContainsKey(target) ? dp[target] : 0; + } +} +``` + +```go +func findTargetSumWays(nums []int, target int) int { + dp := make(map[int]int) + dp[0] = 1 + + for _, num := range nums { + nextDp := make(map[int]int) + for total, count := range dp { + nextDp[total+num] += count + nextDp[total-num] += count + } + dp = nextDp + } + + return dp[target] +} +``` + +```kotlin +class Solution { + fun findTargetSumWays(nums: IntArray, target: Int): Int { + val dp = mutableMapOf(0 to 1) + + for (num in nums) { + val nextDp = mutableMapOf() + for ((total, count) in dp) { + nextDp[total + num] = nextDp.getOrDefault(total + num, 0) + count + nextDp[total - num] = nextDp.getOrDefault(total - num, 0) + count + } + dp.clear() + dp.putAll(nextDp) + } + + return dp[target] ?: 0 + } +} +``` + +```swift +class Solution { + func findTargetSumWays(_ nums: [Int], _ target: Int) -> Int { + var dp = [0: 1] + + for num in nums { + var nextDp = [Int: Int]() + for (total, count) in dp { + nextDp[total + num, default: 0] += count + nextDp[total - num, default: 0] += count + } + dp = nextDp + } + return dp[target, default: 0] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the array $nums$ and $m$ is the sum of all the elements in the array. \ No newline at end of file diff --git a/articles/task-scheduling.md b/articles/task-scheduling.md new file mode 100644 index 000000000..03e752212 --- /dev/null +++ b/articles/task-scheduling.md @@ -0,0 +1,1081 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def leastInterval(self, tasks: List[str], n: int) -> int: + count = [0] * 26 + for task in tasks: + count[ord(task) - ord('A')] += 1 + + arr = [] + for i in range(26): + if count[i] > 0: + arr.append([count[i], i]) + + time = 0 + processed = [] + while arr: + maxi = -1 + for i in range(len(arr)): + if all(processed[j] != arr[i][1] for j in range(max(0, time - n), time)): + if maxi == -1 or arr[maxi][0] < arr[i][0]: + maxi = i + + time += 1 + cur = -1 + if maxi != -1: + cur = arr[maxi][1] + arr[maxi][0] -= 1 + if arr[maxi][0] == 0: + arr.pop(maxi) + processed.append(cur) + return time +``` + +```java +public class Solution { + public int leastInterval(char[] tasks, int n) { + int[] count = new int[26]; + for (char task : tasks) { + count[task - 'A']++; + } + + List arr = new ArrayList<>(); + for (int i = 0; i < 26; i++) { + if (count[i] > 0) { + arr.add(new int[]{count[i], i}); + } + } + + int time = 0; + List processed = new ArrayList<>(); + while (!arr.isEmpty()) { + int maxi = -1; + for (int i = 0; i < arr.size(); i++) { + boolean ok = true; + for (int j = Math.max(0, time - n); j < time; j++) { + if (j < processed.size() && processed.get(j) == arr.get(i)[1]) { + ok = false; + break; + } + } + if (!ok) continue; + if (maxi == -1 || arr.get(maxi)[0] < arr.get(i)[0]) { + maxi = i; + } + } + + time++; + int cur = -1; + if (maxi != -1) { + cur = arr.get(maxi)[1]; + arr.get(maxi)[0]--; + if (arr.get(maxi)[0] == 0) { + arr.remove(maxi); + } + } + processed.add(cur); + } + return time; + } +} +``` + +```cpp +class Solution { +public: + int leastInterval(vector& tasks, int n) { + vector count(26, 0); + for (char task : tasks) { + count[task - 'A']++; + } + + vector> arr; + for (int i = 0; i < 26; i++) { + if (count[i] > 0) { + arr.emplace_back(count[i], i); + } + } + + int time = 0; + vector processed; + while (!arr.empty()) { + int maxi = -1; + for (int i = 0; i < arr.size(); i++) { + bool ok = true; + for (int j = max(0, time - n); j < time; j++) { + if (j < processed.size() && processed[j] == arr[i].second) { + ok = false; + break; + } + } + if (!ok) continue; + if (maxi == -1 || arr[maxi].first < arr[i].first) { + maxi = i; + } + } + + time++; + int cur = -1; + if (maxi != -1) { + cur = arr[maxi].second; + arr[maxi].first--; + if (arr[maxi].first == 0) { + arr.erase(arr.begin() + maxi); + } + } + processed.push_back(cur); + } + return time; + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[]} tasks + * @param {number} n + * @return {number} + */ + leastInterval(tasks, n) { + const count = new Array(26).fill(0); + for (const task of tasks) { + count[task.charCodeAt(0) - 'A'.charCodeAt(0)]++; + } + + const arr = []; + for (let i = 0; i < 26; i++) { + if (count[i] > 0) { + arr.push([count[i], i]); + } + } + + let time = 0; + const processed = []; + while (arr.length > 0) { + let maxi = -1; + for (let i = 0; i < arr.length; i++) { + let ok = true; + for (let j = Math.max(0, time - n); j < time; j++) { + if (j < processed.length && processed[j] === arr[i][1]) { + ok = false; + break; + } + } + if (!ok) continue; + if (maxi === -1 || arr[maxi][0] < arr[i][0]) { + maxi = i; + } + } + + time++; + let cur = -1; + if (maxi !== -1) { + cur = arr[maxi][1]; + arr[maxi][0]--; + if (arr[maxi][0] === 0) { + arr.splice(maxi, 1); + } + } + processed.push(cur); + } + return time; + } +} +``` + +```csharp +public class Solution { + public int LeastInterval(char[] tasks, int n) { + int[] count = new int[26]; + foreach (char task in tasks) { + count[task - 'A']++; + } + + List arr = new List(); + for (int i = 0; i < 26; i++) { + if (count[i] > 0) { + arr.Add(new int[] { count[i], i }); + } + } + + int time = 0; + List processed = new List(); + while (arr.Count > 0) { + int maxi = -1; + for (int i = 0; i < arr.Count; i++) { + bool ok = true; + for (int j = Math.Max(0, time - n); j < time; j++) { + if (j < processed.Count && processed[j] == arr[i][1]) { + ok = false; + break; + } + } + if (!ok) continue; + if (maxi == -1 || arr[maxi][0] < arr[i][0]) { + maxi = i; + } + } + + time++; + int cur = -1; + if (maxi != -1) { + cur = arr[maxi][1]; + arr[maxi][0]--; + if (arr[maxi][0] == 0) { + arr.RemoveAt(maxi); + } + } + processed.Add(cur); + } + return time; + } +} +``` + +```go +func leastInterval(tasks []byte, n int) int { + count := make([]int, 26) + for _, task := range tasks { + count[task-'A']++ + } + + arr := [][]int{} + for i := 0; i < 26; i++ { + if count[i] > 0 { + arr = append(arr, []int{count[i], i}) + } + } + + time := 0 + processed := []int{} + for len(arr) > 0 { + maxi := -1 + for i := 0; i < len(arr); i++ { + canProcess := true + for j := max(0, time-n); j < time && canProcess; j++ { + if processed[j] == arr[i][1] { + canProcess = false + } + } + if canProcess && (maxi == -1 || arr[maxi][0] < arr[i][0]) { + maxi = i + } + } + + time++ + cur := -1 + if maxi != -1 { + cur = arr[maxi][1] + arr[maxi][0]-- + if arr[maxi][0] == 0 { + arr = append(arr[:maxi], arr[maxi+1:]...) + } + } + processed = append(processed, cur) + } + return time +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun leastInterval(tasks: CharArray, n: Int): Int { + val count = IntArray(26) + for (task in tasks) { + count[task - 'A']++ + } + + val arr = mutableListOf>() + for (i in 0..25) { + if (count[i] > 0) { + arr.add(Pair(count[i], i)) + } + } + + var time = 0 + val processed = mutableListOf() + while (arr.isNotEmpty()) { + var maxi = -1 + for (i in arr.indices) { + val canProcess = (maxOf(0, time - n) until time).all { processed[it] != arr[i].second } + if (canProcess && (maxi == -1 || arr[maxi].first < arr[i].first)) { + maxi = i + } + } + + time++ + var cur = -1 + if (maxi != -1) { + cur = arr[maxi].second + arr[maxi] = Pair(arr[maxi].first - 1, arr[maxi].second) + if (arr[maxi].first == 0) { + arr.removeAt(maxi) + } + } + processed.add(cur) + } + return time + } +} +``` + +```swift +class Solution { + func leastInterval(_ tasks: [Character], _ n: Int) -> Int { + var count = [Int](repeating: 0, count: 26) + for task in tasks { + count[Int(task.asciiValue! - Character("A").asciiValue!)] += 1 + } + + var arr = [(Int, Int)]() + for i in 0..<26 { + if count[i] > 0 { + arr.append((count[i], i)) + } + } + + var time = 0 + var processed = [Int]() + + while !arr.isEmpty { + var maxi = -1 + for i in 0.. Where $t$ is the time to process given tasks and $n$ is the cooldown time. + +--- + +## 2. Max-Heap + +::tabs-start + +```python +class Solution: + def leastInterval(self, tasks: List[str], n: int) -> int: + count = Counter(tasks) + maxHeap = [-cnt for cnt in count.values()] + heapq.heapify(maxHeap) + + time = 0 + q = deque() # pairs of [-cnt, idleTime] + while maxHeap or q: + time += 1 + + if not maxHeap: + time = q[0][1] + else: + cnt = 1 + heapq.heappop(maxHeap) + if cnt: + q.append([cnt, time + n]) + if q and q[0][1] == time: + heapq.heappush(maxHeap, q.popleft()[0]) + return time +``` + +```java +public class Solution { + public int leastInterval(char[] tasks, int n) { + int[] count = new int[26]; + for (char task : tasks) { + count[task - 'A']++; + } + + PriorityQueue maxHeap = new PriorityQueue<>(Collections.reverseOrder()); + for (int cnt : count) { + if (cnt > 0) { + maxHeap.add(cnt); + } + } + + int time = 0; + Queue q = new LinkedList<>(); + while (!maxHeap.isEmpty() || !q.isEmpty()) { + time++; + + if (maxHeap.isEmpty()) { + time = q.peek()[1]; + } else { + int cnt = maxHeap.poll() - 1; + if (cnt > 0) { + q.add(new int[]{cnt, time + n}); + } + } + + if (!q.isEmpty() && q.peek()[1] == time) { + maxHeap.add(q.poll()[0]); + } + } + + return time; + } +} +``` + +```cpp +class Solution { +public: + int leastInterval(vector& tasks, int n) { + vector count(26, 0); + for (char task : tasks) { + count[task - 'A']++; + } + + priority_queue maxHeap; + for (int cnt : count) { + if (cnt > 0) { + maxHeap.push(cnt); + } + } + + int time = 0; + queue> q; + while (!maxHeap.empty() || !q.empty()) { + time++; + + if (maxHeap.empty()) { + time = q.front().second; + } else { + int cnt = maxHeap.top() - 1; + maxHeap.pop(); + if (cnt > 0) { + q.push({cnt, time + n}); + } + } + + if (!q.empty() && q.front().second == time) { + maxHeap.push(q.front().first); + q.pop(); + } + } + + return time; + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[]} tasks + * @param {number} n + * @return {number} + */ + leastInterval(tasks, n) { + let count = new Array(26).fill(0); + for (let task of tasks) { + count[task.charCodeAt(0) - 'A'.charCodeAt(0)]++; + } + + let maxHeap = new MaxPriorityQueue(); + for (let i = 0; i < 26; i++) { + if (count[i] > 0) maxHeap.push(count[i]); + } + + let time = 0; + let q = new Queue(); + + while (maxHeap.size() > 0 || q.size() > 0) { + time++; + + if (maxHeap.size() > 0) { + let cnt = maxHeap.pop() - 1; + if (cnt !== 0) { + q.push([cnt, time + n]); + } + } + + if (q.size() > 0 && q.front()[1] === time) { + maxHeap.push(q.pop()[0]); + } + } + + return time; + } +} +``` + +```csharp +public class Solution { + public int LeastInterval(char[] tasks, int n) { + int[] count = new int[26]; + foreach (var task in tasks) { + count[task - 'A']++; + } + + var maxHeap = new PriorityQueue(); + for (int i = 0; i < 26; i++) { + if (count[i] > 0) { + maxHeap.Enqueue(count[i], -count[i]); + } + } + + int time = 0; + Queue queue = new Queue(); + while (maxHeap.Count > 0 || queue.Count > 0) { + if (queue.Count > 0 && time >= queue.Peek()[1]) { + int[] temp = queue.Dequeue(); + maxHeap.Enqueue(temp[0], -temp[0]); + } + if (maxHeap.Count > 0) { + int cnt = maxHeap.Dequeue() - 1; + if (cnt > 0) { + queue.Enqueue(new int[] { cnt, time + n + 1 }); + } + } + time++; + } + return time; + } +} +``` + +```go +func leastInterval(tasks []byte, n int) int { + count := make(map[byte]int) + for _, task := range tasks { + count[task]++ + } + + maxHeap := priorityqueue.NewWith(func(a, b interface{}) int { + return b.(int) - a.(int) + }) + for _, cnt := range count { + maxHeap.Enqueue(cnt) + } + + time := 0 + q := make([][2]int, 0) + + for maxHeap.Size() > 0 || len(q) > 0 { + time++ + + if maxHeap.Size() == 0 { + time = q[0][1] + } else { + cnt, _ := maxHeap.Dequeue() + cnt = cnt.(int) - 1 + if cnt.(int) > 0 { + q = append(q, [2]int{cnt.(int), time + n}) + } + } + + if len(q) > 0 && q[0][1] == time { + maxHeap.Enqueue(q[0][0]) + q = q[1:] + } + } + + return time +} +``` + +```kotlin +class Solution { + fun leastInterval(tasks: CharArray, n: Int): Int { + val count = IntArray(26) + for (task in tasks) { + count[task - 'A']++ + } + + val maxHeap = PriorityQueue(compareBy { it * -1 }) + for (cnt in count) { + if (cnt > 0) { + maxHeap.offer(cnt) + } + } + + var time = 0 + val q = ArrayDeque>() + + while (maxHeap.isNotEmpty() || q.isNotEmpty()) { + time++ + + if (maxHeap.isEmpty()) { + time = q.first().second + } else { + val cnt = 1 + maxHeap.poll() * -1 + if (cnt != 0) { + q.addLast(Pair(cnt * -1, time + n)) + } + } + + if (q.isNotEmpty() && q.first().second == time) { + maxHeap.offer(q.removeFirst().first) + } + } + return time + } +} +``` + +```swift +class Solution { + func leastInterval(_ tasks: [Character], _ n: Int) -> Int { + var count = [Character: Int]() + for task in tasks { + count[task, default: 0] += 1 + } + + var maxHeap = Heap(Array(count.values)) + var time = 0 + var queue = Deque<(Int, Int)>() + + while !maxHeap.isEmpty || !queue.isEmpty { + time += 1 + if maxHeap.isEmpty { + time = queue.first!.1 + } else { + let cnt = maxHeap.popMax()! - 1 + if cnt > 0 { + queue.append((cnt, time + n)) + } + } + if let front = queue.first, front.1 == time { + maxHeap.insert(front.0) + queue.removeFirst() + } + } + + return time + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +> Where $m$ is the number of tasks. + +--- + +## 3. Greedy + +::tabs-start + +```python +class Solution: + def leastInterval(self, tasks: List[str], n: int) -> int: + count = [0] * 26 + for task in tasks: + count[ord(task) - ord('A')] += 1 + + count.sort() + maxf = count[25] + idle = (maxf - 1) * n + + for i in range(24, -1, -1): + idle -= min(maxf - 1, count[i]) + return max(0, idle) + len(tasks) +``` + +```java +public class Solution { + public int leastInterval(char[] tasks, int n) { + int[] count = new int[26]; + for (char task : tasks) { + count[task - 'A']++; + } + + Arrays.sort(count); + int maxf = count[25]; + int idle = (maxf - 1) * n; + + for (int i = 24; i >= 0; i--) { + idle -= Math.min(maxf - 1, count[i]); + } + return Math.max(0, idle) + tasks.length; + } +} +``` + +```cpp +class Solution { +public: + int leastInterval(vector& tasks, int n) { + vector count(26, 0); + for (char task : tasks) { + count[task - 'A']++; + } + + sort(count.begin(), count.end()); + int maxf = count[25]; + int idle = (maxf - 1) * n; + + for (int i = 24; i >= 0; i--) { + idle -= min(maxf - 1, count[i]); + } + return max(0, idle) + tasks.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[]} tasks + * @param {number} n + * @return {number} + */ + leastInterval(tasks, n) { + const count = new Array(26).fill(0); + for (const task of tasks) { + count[task.charCodeAt(0) - 'A'.charCodeAt(0)]++; + } + + count.sort((a, b) => a - b); + const maxf = count[25]; + let idle = (maxf - 1) * n; + + for (let i = 24; i >= 0; i--) { + idle -= Math.min(maxf - 1, count[i]); + } + return Math.max(0, idle) + tasks.length; + } +} +``` + +```csharp +public class Solution { + public int LeastInterval(char[] tasks, int n) { + int[] count = new int[26]; + foreach (char task in tasks) { + count[task - 'A']++; + } + + Array.Sort(count); + int maxf = count[25]; + int idle = (maxf - 1) * n; + + for (int i = 24; i >= 0; i--) { + idle -= Math.Min(maxf - 1, count[i]); + } + return Math.Max(0, idle) + tasks.Length; + } +} +``` + +```go +func leastInterval(tasks []byte, n int) int { + count := make([]int, 26) + for _, task := range tasks { + count[task-'A']++ + } + + sort.Ints(count) + maxf := count[25] + idle := (maxf - 1) * n + + for i := 24; i >= 0; i-- { + idle -= min(maxf-1, count[i]) + } + + return max(0, idle) + len(tasks) +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun leastInterval(tasks: CharArray, n: Int): Int { + val count = IntArray(26) + for (task in tasks) { + count[task - 'A']++ + } + + count.sort() + val maxf = count[25] + var idle = (maxf - 1) * n + + for (i in 24 downTo 0) { + idle -= min(maxf - 1, count[i]) + } + + return max(0, idle) + tasks.size + } +} +``` + +```swift +class Solution { + func leastInterval(_ tasks: [Character], _ n: Int) -> Int { + var count = [Int](repeating: 0, count: 26) + for task in tasks { + count[Int(task.asciiValue! - Character("A").asciiValue!)] += 1 + } + + count.sort() + let maxf = count[25] + var idle = (maxf - 1) * n + + for i in stride(from: 24, through: 0, by: -1) { + idle -= min(maxf - 1, count[i]) + } + + return max(0, idle) + tasks.count + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +> Where $m$ is the number of tasks. + +--- + +## 4. Math + +::tabs-start + +```python +class Solution: + def leastInterval(self, tasks: List[str], n: int) -> int: + count = [0] * 26 + for task in tasks: + count[ord(task) - ord('A')] += 1 + + maxf = max(count) + maxCount = 0 + for i in count: + maxCount += 1 if i == maxf else 0 + + time = (maxf - 1) * (n + 1) + maxCount + return max(len(tasks), time) +``` + +```java +public class Solution { + public int leastInterval(char[] tasks, int n) { + int[] count = new int[26]; + for (char task : tasks) { + count[task - 'A']++; + } + + int maxf = Arrays.stream(count).max().getAsInt(); + int maxCount = 0; + for (int i : count) { + if (i == maxf) { + maxCount++; + } + } + + int time = (maxf - 1) * (n + 1) + maxCount; + return Math.max(tasks.length, time); + } +} +``` + +```cpp +class Solution { +public: + int leastInterval(vector& tasks, int n) { + vector count(26, 0); + for (char task : tasks) { + count[task - 'A']++; + } + + int maxf = *max_element(count.begin(), count.end()); + int maxCount = 0; + for (int i : count) { + if (i == maxf) { + maxCount++; + } + } + + int time = (maxf - 1) * (n + 1) + maxCount; + return max((int)tasks.size(), time); + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[]} tasks + * @param {number} n + * @return {number} + */ + leastInterval(tasks, n) { + const count = new Array(26).fill(0); + for (const task of tasks) { + count[task.charCodeAt(0) - 'A'.charCodeAt(0)]++; + } + + const maxf = Math.max(...count); + let maxCount = 0; + for (const i of count) { + if (i === maxf) { + maxCount++; + } + } + + const time = (maxf - 1) * (n + 1) + maxCount; + return Math.max(tasks.length, time); + } +} +``` + +```csharp +public class Solution { + public int LeastInterval(char[] tasks, int n) { + int[] count = new int[26]; + foreach (char task in tasks) { + count[task - 'A']++; + } + + int maxf = count.Max(); + int maxCount = 0; + foreach (int i in count) { + if (i == maxf) { + maxCount++; + } + } + + int time = (maxf - 1) * (n + 1) + maxCount; + return Math.Max(tasks.Length, time); + } +} +``` + +```go +func leastInterval(tasks []byte, n int) int { + count := make([]int, 26) + for _, task := range tasks { + count[task-'A']++ + } + + maxf := 0 + for _, cnt := range count { + if cnt > maxf { + maxf = cnt + } + } + + maxCount := 0 + for _, cnt := range count { + if cnt == maxf { + maxCount++ + } + } + + time := (maxf - 1) * (n + 1) + maxCount + if len(tasks) > time { + return len(tasks) + } + return time +} +``` + +```kotlin +class Solution { + fun leastInterval(tasks: CharArray, n: Int): Int { + val count = IntArray(26) + for (task in tasks) { + count[task - 'A']++ + } + + val maxf = count.maxOrNull() ?: 0 + var maxCount = 0 + for (cnt in count) { + if (cnt == maxf) { + maxCount++ + } + } + + val time = (maxf - 1) * (n + 1) + maxCount + return max(tasks.size, time) + } +} +``` + +```swift +class Solution { + func leastInterval(_ tasks: [Character], _ n: Int) -> Int { + var count = [Int](repeating: 0, count: 26) + for task in tasks { + count[Int(task.asciiValue! - Character("A").asciiValue!)] += 1 + } + + let maxf = count.max()! + var maxCount = 0 + for i in count { + if i == maxf { + maxCount += 1 + } + } + + let time = (maxf - 1) * (n + 1) + maxCount + return max(tasks.count, time) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +> Where $m$ is the number of tasks. \ No newline at end of file diff --git a/articles/text-justification.md b/articles/text-justification.md new file mode 100644 index 000000000..fb48dcabe --- /dev/null +++ b/articles/text-justification.md @@ -0,0 +1,181 @@ +## 1. Iteration + +::tabs-start + +```python +class Solution: + def fullJustify(self, words: List[str], maxWidth: int) -> List[str]: + res = [] + line, length = [], 0 + i = 0 + + while i < len(words): + if length + len(words[i]) + len(line) <= maxWidth: + line.append(words[i]) + length += len(words[i]) + i += 1 + else: + # Line complete + extra_space = maxWidth - length + remainder = extra_space % max(1, (len(line) - 1)) + space = extra_space // max(1, (len(line) - 1)) + for j in range(max(1, len(line) - 1)): + line[j] += " " * space + if remainder: + line[j] += " " + remainder -= 1 + res.append("".join(line)) + line, length = [], 0 + + # Handling last line + last_line = " ".join(line) + trail_space = maxWidth - len(last_line) + res.append(last_line + " " * trail_space) + return res +``` + +```java +public class Solution { + public List fullJustify(String[] words, int maxWidth) { + List res = new ArrayList<>(); + List line = new ArrayList<>(); + int length = 0, i = 0; + + while (i < words.length) { + // If the current word can fit in the line + if (length + words[i].length() + line.size() <= maxWidth) { + line.add(words[i]); + length += words[i].length(); + i++; + } else { + // Line complete + int extra_space = maxWidth - length; + int remainder = extra_space % Math.max(1, (line.size() - 1)); + int space = extra_space / Math.max(1, (line.size() - 1)); + + for (int j = 0; j < Math.max(1, line.size() - 1); j++) { + line.set(j, line.get(j) + " ".repeat(space)); + if (remainder > 0) { + line.set(j, line.get(j) + " "); + remainder--; + } + } + + res.add(String.join("", line)); + line.clear(); + length = 0; + } + } + + // Handling last line + String last_line = String.join(" ", line); + int trail_space = maxWidth - last_line.length(); + res.add(last_line + " ".repeat(trail_space)); + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector fullJustify(vector& words, int maxWidth) { + vector res; + vector line; + int length = 0, i = 0; + + while (i < words.size()) { + if (length + words[i].size() + line.size() <= maxWidth) { + line.push_back(words[i]); + length += words[i].size(); + i++; + } else { + // Line complete + int extra_space = maxWidth - length; + int remainder = extra_space % max(1, (int)(line.size() - 1)); + int space = extra_space / max(1, (int)(line.size() - 1)); + + for (int j = 0; j < max(1, (int)line.size() - 1); j++) { + line[j] += string(space, ' '); + if (remainder > 0) { + line[j] += " "; + remainder--; + } + } + + string justified_line = accumulate(line.begin(), line.end(), string()); + res.push_back(justified_line); + line.clear(); + length = 0; + } + } + + // Handling last line + string last_line = accumulate(line.begin(), line.end(), string(), + [](string a, string b) { + return a.empty() ? b : a + " " + b; + }); + int trail_space = maxWidth - last_line.size(); + last_line += string(trail_space, ' '); + res.push_back(last_line); + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {number} maxWidth + * @return {string[]} + */ + fullJustify(words, maxWidth) { + let res = []; + let line = [], length = 0, i = 0; + + while (i < words.length) { + if (length + words[i].length + line.length <= maxWidth) { + line.push(words[i]); + length += words[i].length; + i++; + } else { + // Line complete + let extra_space = maxWidth - length; + let remainder = extra_space % Math.max(1, line.length - 1); + let space = Math.floor(extra_space / Math.max(1, line.length - 1)); + + for (let j = 0; j < Math.max(1, line.length - 1); j++) { + line[j] += " ".repeat(space); + if (remainder > 0) { + line[j] += " "; + remainder--; + } + } + + res.push(line.join("")); + line = []; + length = 0; + } + } + + // Handling last line + let last_line = line.join(" "); + let trail_space = maxWidth - last_line.length; + res.push(last_line + " ".repeat(trail_space)); + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of words and $m$ is the average length of the words. \ No newline at end of file diff --git a/articles/the-number-of-beautiful-subsets.md b/articles/the-number-of-beautiful-subsets.md new file mode 100644 index 000000000..1ed55b435 --- /dev/null +++ b/articles/the-number-of-beautiful-subsets.md @@ -0,0 +1,739 @@ +## 1. Backtracking + +::tabs-start + +```python +class Solution: + def beautifulSubsets(self, nums: List[int], k: int) -> int: + def helper(i, count): + if i == len(nums): + return 1 + + res = helper(i + 1, count) # Skip nums[i] + if not count[nums[i] + k] and not count[nums[i] - k]: + count[nums[i]] += 1 + res += helper(i + 1, count) + count[nums[i]] -= 1 + + return res + + return helper(0, defaultdict(int)) - 1 +``` + +```java +public class Solution { + public int beautifulSubsets(int[] nums, int k) { + return helper(0, new HashMap<>(), nums, k) - 1; + } + + private int helper(int i, Map count, int[] nums, int k) { + if (i == nums.length) { + return 1; + } + + int res = helper(i + 1, count, nums, k); // Skip nums[i] + + if (!count.containsKey(nums[i] + k) && !count.containsKey(nums[i] - k)) { + count.put(nums[i], count.getOrDefault(nums[i], 0) + 1); + res += helper(i + 1, count, nums, k); + count.put(nums[i], count.get(nums[i]) - 1); + if (count.get(nums[i]) == 0) { + count.remove(nums[i]); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int beautifulSubsets(vector& nums, int k) { + unordered_map count; + return helper(0, count, nums, k) - 1; + } + +private: + int helper(int i, unordered_map& count, vector& nums, int k) { + if (i == nums.size()) { + return 1; + } + + int res = helper(i + 1, count, nums, k); // Skip nums[i] + if (!count[nums[i] + k] && !count[nums[i] - k]) { + count[nums[i]]++; + res += helper(i + 1, count, nums, k); + count[nums[i]]--; + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + beautifulSubsets(nums, k) { + const helper = (i, count) => { + if (i === nums.length) { + return 1; + } + + let res = helper(i + 1, count); // Skip nums[i] + + if (!count.has(nums[i] + k) && !count.has(nums[i] - k)) { + count.set(nums[i], (count.get(nums[i]) || 0) + 1); + res += helper(i + 1, count); + count.set(nums[i], count.get(nums[i]) - 1); + if (count.get(nums[i]) === 0) { + count.delete(nums[i]); + } + } + + return res; + }; + + return helper(0, new Map()) - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def beautifulSubsets(self, nums: List[int], k: int) -> int: + cnt = Counter(nums) + groups = [] # List of dicts + cache = {} + + def helper(n, g): + if n not in g: + return 1 + if n in cache: + return cache[n] + + skip = helper(n + k, g) + include = (2**g[n] - 1) * helper(n + 2 * k, g) + cache[n] = skip + include + return skip + include + + visit = set() + for n in cnt.keys(): + if n in visit: + continue + g = {} + while n - k in cnt: + n -= k + while n in cnt: + g[n] = cnt[n] + visit.add(n) + n += k + groups.append(g) + + res = 1 + for g in groups: + n = min(g.keys()) + res *= helper(n, g) + + return res - 1 +``` + +```java +public class Solution { + private Map cache; + private Map cnt; + private Set visit; + + public int beautifulSubsets(int[] nums, int k) { + List> groups = new ArrayList<>(); + this.cache = new HashMap<>(); + this.cnt = new HashMap<>(); + this.visit = new HashSet<>(); + + for (int num : nums) { + cnt.put(num, cnt.getOrDefault(num, 0) + 1); + } + + for (int n : cnt.keySet()) { + if (visit.contains(n)) { + continue; + } + Map g = new HashMap<>(); + while (cnt.containsKey(n - k)) { + n -= k; + } + while (cnt.containsKey(n)) { + g.put(n, cnt.get(n)); + visit.add(n); + n += k; + } + groups.add(g); + } + + int res = 1; + for (Map g : groups) { + int n = Collections.min(g.keySet()); + res *= helper(n, g, k); + } + + return res - 1; + } + + private int helper(int n, Map g, int k) { + if (!g.containsKey(n)) { + return 1; + } + if (cache.containsKey(n)) { + return cache.get(n); + } + + int skip = helper(n + k, g, k); + int include = (int) ((Math.pow(2, g.get(n)) - 1) * helper(n + 2 * k, g, k)); + int result = skip + include; + cache.put(n, result); + return result; + } +} +``` + +```cpp +class Solution { +public: + int beautifulSubsets(vector& nums, int k) { + vector> groups; + cache.clear(); + cnt.clear(); + visit.clear(); + + for (int& num : nums) { + cnt[num]++; + } + + for (auto it = cnt.begin(); it != cnt.end(); ++it) { + int n = it->first; + if (visit.count(n)) { + continue; + } + unordered_map g; + while (cnt.count(n - k)) { + n -= k; + } + while (cnt.count(n)) { + g[n] = cnt[n]; + visit.insert(n); + n += k; + } + groups.push_back(g); + } + + int res = 1; + for (auto& g : groups) { + int n = min_element(g.begin(), g.end())->first; + res *= helper(n, g, k); + } + return res - 1; + } + +private: + unordered_map cache; + unordered_map cnt; + unordered_set visit; + + int helper(int n, unordered_map& g, int k) { + if (!g.count(n)) { + return 1; + } + if (cache.count(n)) { + return cache[n]; + } + + int skip = helper(n + k, g, k); + int include = (pow(2, g[n]) - 1) * helper(n + 2 * k, g, k); + return cache[n] = skip + include; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + beautifulSubsets(nums, k) { + let cnt = new Map(); + for (const num of nums) { + cnt.set(num, (cnt.get(num) || 0) + 1); + } + + let groups = []; + let cache = new Map(); + let visit = new Set(); + + for (let n of cnt.keys()) { + if (visit.has(n)) { + continue; + } + let g = new Map(); + while (cnt.has(n - k)) { + n -= k; + } + while (cnt.has(n)) { + g.set(n, cnt.get(n)); + visit.add(n); + n += k; + } + groups.push(g); + } + + const helper = (n, g) => { + if (!g.has(n)) { + return 1; + } + if (cache.has(n)) { + return cache.get(n); + } + + let skip = helper(n + k, g); + let include = (2 ** g.get(n) - 1) * helper(n + 2 * k, g); + let result = skip + include; + cache.set(n, result); + return result; + }; + + let res = 1; + for (const g of groups) { + let n = Math.min(...g.keys()); + res *= helper(n, g); + } + return res - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def beautifulSubsets(self, nums: List[int], k: int) -> int: + cnt = Counter(nums) + groups = [] # List of dicts + + visit = set() + for n in cnt.keys(): + if n in visit: + continue + g = {} + while n - k in cnt: + n -= k + while n in cnt: + g[n] = cnt[n] + visit.add(n) + n += k + groups.append(g) + + res = 1 + for g in groups: + dp = {} + prev = None + + for num in sorted(g): + count = g[num] + if prev is None or prev + k != num: + dp[num] = (dp.get(prev, 1) * (1 + (2 ** count - 1))) + else: + dp[num] = dp[prev] + (2 ** count - 1) * dp.get(prev - k, 1) + prev = num + + res *= dp[prev] + + return res - 1 +``` + +```java +class Solution { + public int beautifulSubsets(int[] nums, int k) { + Map cnt = new HashMap<>(); + for (int num : nums) { + cnt.put(num, cnt.getOrDefault(num, 0) + 1); + } + + List> groups = new ArrayList<>(); + Set visit = new HashSet<>(); + + for (int n : cnt.keySet()) { + if (visit.contains(n)) { + continue; + } + Map g = new HashMap<>(); + while (cnt.containsKey(n - k)) { + n -= k; + } + while (cnt.containsKey(n)) { + g.put(n, cnt.get(n)); + visit.add(n); + n += k; + } + groups.add(g); + } + + int res = 1; + for (Map g : groups) { + Map dp = new HashMap<>(); + Integer prev = null; + + List arr = new ArrayList<>(g.keySet()); + Collections.sort(arr); + for (int num : arr) { + int count = g.get(num); + if (prev == null || prev + k != num) { + dp.put(num, dp.getOrDefault(prev, 1) * (1 + (int) Math.pow(2, count) - 1)); + } else { + dp.put(num, dp.get(prev) + + ((int) Math.pow(2, count) - 1) * dp.getOrDefault(prev - k, 1)); + } + prev = num; + } + + res *= dp.get(prev); + } + + return res - 1; + } +} +``` + +```cpp +class Solution { +public: + int beautifulSubsets(vector& nums, int k) { + unordered_map cnt; + for (int num : nums) { + cnt[num]++; + } + + vector> groups; + unordered_set visit; + + for (auto it = cnt.begin(); it != cnt.end(); ++it) { + int n = it->first; + if (visit.count(n)) { + continue; + } + unordered_map g; + while (cnt.count(n - k)) { + n -= k; + } + while (cnt.count(n)) { + g[n] = cnt[n]; + visit.insert(n); + n += k; + } + groups.push_back(g); + } + + int res = 1; + for (auto& g : groups) { + unordered_map dp; + int prev = -1; + + vector keys; + for (auto& [num, _] : g) { + keys.push_back(num); + } + sort(keys.begin(), keys.end()); + + for (int num : keys) { + int count = g[num]; + if (prev == -1 || prev + k != num) { + dp[num] = dp.count(prev) ? dp[prev] * (1 + (1 << count) - 1) : + (1 + (1 << count) - 1); + } else { + dp[num] = dp[prev] + ((1 << count) - 1) * + (dp.count(prev - k) ? dp[prev - k] : 1); + } + prev = num; + } + + res *= dp[prev]; + } + + return res - 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + beautifulSubsets(nums, k) { + let cnt = new Map(); + for (const num of nums) { + cnt.set(num, (cnt.get(num) || 0) + 1); + } + + let groups = []; + let visit = new Set(); + + for (const n of cnt.keys()) { + if (visit.has(n)) { + continue; + } + let g = new Map(); + let num = n; + while (cnt.has(num - k)) { + num -= k; + } + while (cnt.has(num)) { + g.set(num, cnt.get(num)); + visit.add(num); + num += k; + } + groups.push(g); + } + + let res = 1; + for (const g of groups) { + let dp = new Map(); + let prev = null; + + for (const num of g.keys()) { + let count = g.get(num); + if (prev === null || prev + k !== num) { + dp.set(num, (dp.get(prev) || 1) * (1 + (2 ** count - 1))); + } else { + dp.set(num, dp.get(prev) + (2 ** count - 1) * (dp.get(prev - k) || 1)); + } + prev = num; + } + + res *= dp.get(prev); + } + + return res - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def beautifulSubsets(self, nums: List[int], k: int) -> int: + cnt = Counter(nums) + groups = defaultdict(dict) + + # Group numbers based on their remainder with k + for num in nums: + groups[num % k][num] = cnt[num] + + res = 1 + for g in groups.values(): + prev = 0 + dp, ndp = 0, 1 + + for num in sorted(g.keys()): + count = g[num] + have = (1 << count) - 1 + tmp = ndp + ndp += dp + + if prev == 0 or prev + k != num: + dp = have * (tmp + dp) + else: + dp = tmp * have + + prev = num + + res *= (dp + ndp) + + return res - 1 +``` + +```java +public class Solution { + public int beautifulSubsets(int[] nums, int k) { + Map> groups = new HashMap<>(); + Map cnt = new HashMap<>(); + for (int num : nums) { + cnt.put(num, cnt.getOrDefault(num, 0) + 1); + } + + // Group numbers based on remainder with k + for (int num : nums) { + groups.putIfAbsent(num % k, new HashMap<>()); + groups.get(num % k).put(num, cnt.get(num)); + } + + int res = 1; + for (Map g : groups.values()) { + int prev = 0, dp = 0, ndp = 1; + List sortedKeys = new ArrayList<>(g.keySet()); + Collections.sort(sortedKeys); + + for (int num : sortedKeys) { + int count = g.get(num); + int have = (1 << count) - 1; + int tmp = ndp; + ndp += dp; + + if (prev == 0 || prev + k != num) { + dp = have * (tmp + dp); + } else { + dp = tmp * have; + } + + prev = num; + } + + res *= (dp + ndp); + } + + return res - 1; + } +} +``` + +```cpp +class Solution { +public: + int beautifulSubsets(vector& nums, int k) { + unordered_map> groups; + unordered_map cnt; + for (int& num : nums) { + cnt[num]++; + } + + // Group numbers based on remainder with k + for (int num : nums) { + groups[num % k][num] = cnt[num]; + } + + int res = 1; + for (auto& [rem, g] : groups) { + int prev = 0, dp = 0, ndp = 1; + + for (auto& [num, count] : g) { + int have = (1 << count) - 1; + int tmp = ndp; + ndp += dp; + + if (prev == 0 || prev + k != num) { + dp = have * (tmp + dp); + } else { + dp = tmp * have; + } + + prev = num; + } + + res *= (dp + ndp); + } + + return res - 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + beautifulSubsets(nums, k) { + let groups = new Map(); + let cnt = new Map(); + for (const num of nums) { + cnt.set(num, (cnt.get(num) || 0) + 1); + } + + // Group numbers based on remainder with k + for (const num of nums) { + if (!groups.has(num % k)) { + groups.set(num % k, new Map()); + } + groups.get(num % k).set(num, cnt.get(num)); + } + + let res = 1; + for (const g of groups.values()) { + let prev = 0, dp = 0, ndp = 1; + let sortedKeys = Array.from(g.keys()).sort((a, b) => a - b); + + for (const num of sortedKeys) { + let count = g.get(num); + let have = (1 << count) - 1; + let tmp = ndp; + ndp += dp; + + if (prev === 0 || prev + k !== num) { + dp = have * (tmp + dp); + } else { + dp = tmp * have; + } + + prev = num; + } + + res *= (dp + ndp); + } + + return res - 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/three-integer-sum.md b/articles/three-integer-sum.md new file mode 100644 index 000000000..18b4b76b7 --- /dev/null +++ b/articles/three-integer-sum.md @@ -0,0 +1,752 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def threeSum(self, nums: List[int]) -> List[List[int]]: + res = set() + nums.sort() + for i in range(len(nums)): + for j in range(i + 1, len(nums)): + for k in range(j + 1, len(nums)): + if nums[i] + nums[j] + nums[k] == 0: + tmp = [nums[i], nums[j], nums[k]] + res.add(tuple(tmp)) + return [list(i) for i in res] +``` + +```java +public class Solution { + public List> threeSum(int[] nums) { + Set> res = new HashSet<>(); + Arrays.sort(nums); + for (int i = 0; i < nums.length; i++) { + for (int j = i + 1; j < nums.length; j++) { + for (int k = j + 1; k < nums.length; k++) { + if (nums[i] + nums[j] + nums[k] == 0) { + List tmp = Arrays.asList(nums[i], nums[j], nums[k]); + res.add(tmp); + } + } + } + } + return new ArrayList<>(res); + } +} +``` + +```cpp +class Solution { +public: + vector> threeSum(vector& nums) { + set> res; + sort(nums.begin(), nums.end()); + for (int i = 0; i < nums.size(); i++) { + for (int j = i + 1; j < nums.size(); j++) { + for (int k = j + 1; k < nums.size(); k++) { + if (nums[i] + nums[j] + nums[k] == 0) { + res.insert({nums[i], nums[j], nums[k]}); + } + } + } + } + return vector>(res.begin(), res.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + threeSum(nums) { + const res = new Set(); + nums.sort((a, b) => a - b); + for (let i = 0; i < nums.length; i++) { + for (let j = i + 1; j < nums.length; j++) { + for (let k = j + 1; k < nums.length; k++) { + if (nums[i] + nums[j] + nums[k] === 0) { + res.add(JSON.stringify([nums[i], nums[j], nums[k]])); + } + } + } + } + return Array.from(res).map(item => JSON.parse(item)); + } +} +``` + +```csharp +public class Solution { + public List> ThreeSum(int[] nums) { + HashSet> uniqueTriplets = new HashSet>(); + List> res = new List>(); + Array.Sort(nums); + + for (int i = 0; i < nums.Length; i++) { + for (int j = i + 1; j < nums.Length; j++) { + for (int k = j + 1; k < nums.Length; k++) { + if (nums[i] + nums[j] + nums[k] == 0) { + var triplet = Tuple.Create(nums[i], nums[j], nums[k]); + uniqueTriplets.Add(triplet); + } + } + } + } + + foreach (var triplet in uniqueTriplets) { + res.Add(new List { triplet.Item1, triplet.Item2, triplet.Item3 }); + } + return res; + } +} +``` + +```go +func threeSum(nums []int) [][]int { + res := map[[3]int]struct{}{} + sort.Ints(nums) + for i := 0; i < len(nums); i++ { + for j := i + 1; j < len(nums); j++ { + for k := j + 1; k < len(nums); k++ { + if nums[i]+nums[j]+nums[k] == 0 { + res[[3]int{nums[i], nums[j], nums[k]}] = struct{}{} + } + } + } + } + var result [][]int + for triplet := range res { + result = append(result, []int{triplet[0], triplet[1], triplet[2]}) + } + return result +} +``` + +```kotlin +class Solution { + fun threeSum(nums: IntArray): List> { + val res = HashSet>() + nums.sort() + for (i in nums.indices) { + for (j in i + 1 until nums.size) { + for (k in j + 1 until nums.size) { + if (nums[i] + nums[j] + nums[k] == 0) { + res.add(listOf(nums[i], nums[j], nums[k])) + } + } + } + } + return res.map { it.toList() } + } +} +``` + +```swift +class Solution { + func threeSum(_ nums: [Int]) -> [[Int]] { + var res = Set<[Int]>() + let nums = nums.sorted() + + for i in 0.. Where $m$ is the number of triplets and $n$ is the length of the given array. + +--- + +## 2. Hash Map + +::tabs-start + +```python +class Solution: + def threeSum(self, nums: List[int]) -> List[List[int]]: + nums.sort() + count = defaultdict(int) + for num in nums: + count[num] += 1 + + res = [] + for i in range(len(nums)): + count[nums[i]] -= 1 + if i and nums[i] == nums[i - 1]: + continue + + for j in range(i + 1, len(nums)): + count[nums[j]] -= 1 + if j - 1 > i and nums[j] == nums[j - 1]: + continue + target = -(nums[i] + nums[j]) + if count[target] > 0: + res.append([nums[i], nums[j], target]) + + for j in range(i + 1, len(nums)): + count[nums[j]] += 1 + return res +``` + +```java +public class Solution { + public List> threeSum(int[] nums) { + Arrays.sort(nums); + Map count = new HashMap<>(); + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + List> res = new ArrayList<>(); + for (int i = 0; i < nums.length; i++) { + count.put(nums[i], count.get(nums[i]) - 1); + if (i > 0 && nums[i] == nums[i - 1]) continue; + + for (int j = i + 1; j < nums.length; j++) { + count.put(nums[j], count.get(nums[j]) - 1); + if (j > i + 1 && nums[j] == nums[j - 1]) continue; + + int target = -(nums[i] + nums[j]); + if (count.getOrDefault(target, 0) > 0) { + res.add(Arrays.asList(nums[i], nums[j], target)); + } + } + + for (int j = i + 1; j < nums.length; j++) { + count.put(nums[j], count.get(nums[j]) + 1); + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> threeSum(vector& nums) { + sort(nums.begin(), nums.end()); + unordered_map count; + for (int num : nums) { + count[num]++; + } + + vector> res; + for (int i = 0; i < nums.size(); i++) { + count[nums[i]]--; + if (i > 0 && nums[i] == nums[i - 1]) continue; + + for (int j = i + 1; j < nums.size(); j++) { + count[nums[j]]--; + if (j > i + 1 && nums[j] == nums[j - 1]) continue; + + int target = -(nums[i] + nums[j]); + if (count[target] > 0) { + res.push_back({nums[i], nums[j], target}); + } + } + + for (int j = i + 1; j < nums.size(); j++) { + count[nums[j]]++; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + threeSum(nums) { + nums.sort((a, b) => a - b); + const count = new Map(); + for (let num of nums) { + count.set(num, (count.get(num) || 0) + 1); + } + + const res = []; + for (let i = 0; i < nums.length; i++) { + count.set(nums[i], count.get(nums[i]) - 1); + if (i > 0 && nums[i] === nums[i - 1]) continue; + + for (let j = i + 1; j < nums.length; j++) { + count.set(nums[j], count.get(nums[j]) - 1); + if (j > i + 1 && nums[j] === nums[j - 1]) continue; + + const target = -(nums[i] + nums[j]); + if (count.get(target) > 0) { + res.push([nums[i], nums[j], target]); + } + } + + for (let j = i + 1; j < nums.length; j++) { + count.set(nums[j], count.get(nums[j]) + 1); + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public List> ThreeSum(int[] nums) { + Array.Sort(nums); + Dictionary count = new Dictionary(); + foreach (int num in nums) { + if (!count.ContainsKey(num)) { + count[num] = 0; + } + count[num]++; + } + + List> res = new List>(); + for (int i = 0; i < nums.Length; i++) { + count[nums[i]]--; + if (i > 0 && nums[i] == nums[i - 1]) continue; + + for (int j = i + 1; j < nums.Length; j++) { + count[nums[j]]--; + if (j > i + 1 && nums[j] == nums[j - 1]) continue; + + int target = -(nums[i] + nums[j]); + if (count.ContainsKey(target) && count[target] > 0) { + res.Add(new List { nums[i], nums[j], target }); + } + } + + for (int j = i + 1; j < nums.Length; j++) { + count[nums[j]]++; + } + } + + return res; + } +} +``` + +```go +func threeSum(nums []int) [][]int { + sort.Ints(nums) + count := make(map[int]int) + for _, num := range nums { + count[num]++ + } + + var res [][]int + for i := 0; i < len(nums); i++ { + count[nums[i]]-- + if i > 0 && nums[i] == nums[i-1] { + continue + } + + for j := i + 1; j < len(nums); j++ { + count[nums[j]]-- + if j > i+1 && nums[j] == nums[j-1] { + continue + } + target := -(nums[i] + nums[j]) + if count[target] > 0 { + res = append(res, []int{nums[i], nums[j], target}) + } + } + + for j := i + 1; j < len(nums); j++ { + count[nums[j]]++ + } + } + + return res +} +``` + +```kotlin +class Solution { + fun threeSum(nums: IntArray): List> { + nums.sort() + val count = HashMap() + for (num in nums) { + count[num] = count.getOrDefault(num, 0) + 1 + } + + val res = mutableListOf>() + for (i in nums.indices) { + count[nums[i]] = count[nums[i]]!! - 1 + if (i > 0 && nums[i] == nums[i - 1]) continue + + for (j in i + 1 until nums.size) { + count[nums[j]] = count[nums[j]]!! - 1 + if (j > i + 1 && nums[j] == nums[j - 1]) continue + + val target = -(nums[i] + nums[j]) + if (count.getOrDefault(target, 0) > 0) { + res.add(listOf(nums[i], nums[j], target)) + } + } + + for (j in i + 1 until nums.size) { + count[nums[j]] = count[nums[j]]!! + 1 + } + } + + return res + } +} +``` + +```swift +class Solution { + func threeSum(_ nums: [Int]) -> [[Int]] { + var nums = nums.sorted() + var count = [Int: Int]() + for num in nums { + count[num, default: 0] += 1 + } + + var res = [[Int]]() + for i in 0.. 0 && nums[i] == nums[i - 1] { + continue + } + + for j in (i + 1).. i + 1 && nums[j] == nums[j - 1] { + continue + } + let target = -(nums[i] + nums[j]) + if let cnt = count[target], cnt > 0 { + res.append([nums[i], nums[j], target]) + } + } + + for j in (i + 1).. List[List[int]]: + res = [] + nums.sort() + + for i, a in enumerate(nums): + if a > 0: + break + + if i > 0 and a == nums[i - 1]: + continue + + l, r = i + 1, len(nums) - 1 + while l < r: + threeSum = a + nums[l] + nums[r] + if threeSum > 0: + r -= 1 + elif threeSum < 0: + l += 1 + else: + res.append([a, nums[l], nums[r]]) + l += 1 + r -= 1 + while nums[l] == nums[l - 1] and l < r: + l += 1 + + return res +``` + +```java +public class Solution { + public List> threeSum(int[] nums) { + Arrays.sort(nums); + List> res = new ArrayList<>(); + + for (int i = 0; i < nums.length; i++) { + if (nums[i] > 0) break; + if (i > 0 && nums[i] == nums[i - 1]) continue; + + int l = i + 1, r = nums.length - 1; + while (l < r) { + int sum = nums[i] + nums[l] + nums[r]; + if (sum > 0) { + r--; + } else if (sum < 0) { + l++; + } else { + res.add(Arrays.asList(nums[i], nums[l], nums[r])); + l++; + r--; + while (l < r && nums[l] == nums[l - 1]) { + l++; + } + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> threeSum(vector& nums) { + sort(nums.begin(), nums.end()); + vector> res; + + for (int i = 0; i < nums.size(); i++) { + if (nums[i] > 0) break; + if (i > 0 && nums[i] == nums[i - 1]) continue; + + int l = i + 1, r = nums.size() - 1; + while (l < r) { + int sum = nums[i] + nums[l] + nums[r]; + if (sum > 0) { + r--; + } else if (sum < 0) { + l++; + } else { + res.push_back({nums[i], nums[l], nums[r]}); + l++; + r--; + while (l < r && nums[l] == nums[l - 1]) { + l++; + } + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {number[][]} + */ + threeSum(nums) { + nums.sort((a, b) => a - b); + const res = []; + + for (let i = 0; i < nums.length; i++) { + if (nums[i] > 0) break; + if (i > 0 && nums[i] === nums[i - 1]) continue; + + let l = i + 1; + let r = nums.length - 1; + while (l < r) { + const sum = nums[i] + nums[l] + nums[r]; + if (sum > 0) { + r--; + } else if (sum < 0) { + l++; + } else { + res.push([nums[i], nums[l], nums[r]]); + l++; + r--; + while (l < r && nums[l] === nums[l - 1]) { + l++; + } + } + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public List> ThreeSum(int[] nums) { + Array.Sort(nums); + List> res = new List>(); + + for (int i = 0; i < nums.Length; i++) { + if (nums[i] > 0) break; + if (i > 0 && nums[i] == nums[i - 1]) continue; + + int l = i + 1, r = nums.Length - 1; + while (l < r) { + int sum = nums[i] + nums[l] + nums[r]; + if (sum > 0) { + r--; + } else if (sum < 0) { + l++; + } else { + res.Add(new List {nums[i], nums[l], nums[r]}); + l++; + r--; + while (l < r && nums[l] == nums[l - 1]) { + l++; + } + } + } + } + return res; + } +} +``` + +```go +func threeSum(nums []int) [][]int { + res := [][]int{} + sort.Ints(nums) + + for i := 0; i < len(nums); i++ { + a := nums[i] + if a > 0 { + break + } + if i > 0 && a == nums[i-1] { + continue + } + + l, r := i+1, len(nums)-1 + for l < r { + threeSum := a + nums[l] + nums[r] + if threeSum > 0 { + r-- + } else if threeSum < 0 { + l++ + } else { + res = append(res, []int{a, nums[l], nums[r]}) + l++ + r-- + for l < r && nums[l] == nums[l-1] { + l++ + } + } + } + } + + return res +} +``` + +```kotlin +class Solution { + fun threeSum(nums: IntArray): List> { + val res = mutableListOf>() + nums.sort() + + for (i in nums.indices) { + val a = nums[i] + if (a > 0) break + if (i > 0 && a == nums[i - 1]) continue + + var l = i + 1 + var r = nums.size - 1 + while (l < r) { + val threeSum = a + nums[l] + nums[r] + when { + threeSum > 0 -> r-- + threeSum < 0 -> l++ + else -> { + res.add(listOf(a, nums[l], nums[r])) + l++ + r-- + while (l < r && nums[l] == nums[l - 1]) { + l++ + } + } + } + } + } + + return res + } +} +``` + +```swift +class Solution { + func threeSum(_ nums: [Int]) -> [[Int]] { + var res = [[Int]]() + var nums = nums.sorted() + + for i in 0.. 0 { + break + } + if i > 0 && a == nums[i - 1] { + continue + } + + var l = i + 1, r = nums.count - 1 + while l < r { + let threeSum = a + nums[l] + nums[r] + if threeSum > 0 { + r -= 1 + } else if threeSum < 0 { + l += 1 + } else { + res.append([a, nums[l], nums[r]]) + l += 1 + r -= 1 + while l < r && nums[l] == nums[l - 1] { + l += 1 + } + } + } + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: + * $O(1)$ or $O(n)$ extra space depending on the sorting algorithm. + * $O(m)$ space for the output list. + +> Where $m$ is the number of triplets and $n$ is the length of the given array. \ No newline at end of file diff --git a/articles/time-based-key-value-store.md b/articles/time-based-key-value-store.md new file mode 100644 index 000000000..8fc87e0e5 --- /dev/null +++ b/articles/time-based-key-value-store.md @@ -0,0 +1,859 @@ +## 1. Brute Force + +::tabs-start + +```python +class TimeMap: + + def __init__(self): + self.keyStore = {} + + def set(self, key: str, value: str, timestamp: int) -> None: + if key not in self.keyStore: + self.keyStore[key] = {} + if timestamp not in self.keyStore[key]: + self.keyStore[key][timestamp] = [] + self.keyStore[key][timestamp].append(value) + + def get(self, key: str, timestamp: int) -> str: + if key not in self.keyStore: + return "" + seen = 0 + + for time in self.keyStore[key]: + if time <= timestamp: + seen = max(seen, time) + return "" if seen == 0 else self.keyStore[key][seen][-1] +``` + +```java +public class TimeMap { + private Map>> keyStore; + + public TimeMap() { + keyStore = new HashMap<>(); + } + + public void set(String key, String value, int timestamp) { + if (!keyStore.containsKey(key)) { + keyStore.put(key, new HashMap<>()); + } + if (!keyStore.get(key).containsKey(timestamp)) { + keyStore.get(key).put(timestamp, new ArrayList<>()); + } + keyStore.get(key).get(timestamp).add(value); + } + + public String get(String key, int timestamp) { + if (!keyStore.containsKey(key)) { + return ""; + } + int seen = 0; + + for (int time : keyStore.get(key).keySet()) { + if (time <= timestamp) { + seen = Math.max(seen, time); + } + } + if (seen == 0) return ""; + int back = keyStore.get(key).get(seen).size() - 1; + return keyStore.get(key).get(seen).get(back); + } +} +``` + +```cpp +class TimeMap { +public: + unordered_map>> keyStore; + TimeMap() {} + + void set(string key, string value, int timestamp) { + keyStore[key][timestamp].push_back(value); + } + + string get(string key, int timestamp) { + if (keyStore.find(key) == keyStore.end()) { + return ""; + } + int seen = 0; + for (const auto& [time, _] : keyStore[key]) { + if (time <= timestamp) { + seen = max(seen, time); + } + } + return seen == 0 ? "" : keyStore[key][seen].back(); + } +}; +``` + +```javascript +class TimeMap { + constructor() { + this.keyStore = new Map(); + } + + /** + * @param {string} key + * @param {string} value + * @param {number} timestamp + * @return {void} + */ + set(key, value, timestamp) { + if (!this.keyStore.has(key)) { + this.keyStore.set(key, new Map()); + } + if (!this.keyStore.get(key).has(timestamp)) { + this.keyStore.get(key).set(timestamp, []); + } + this.keyStore.get(key).get(timestamp).push(value); + } + + /** + * @param {string} key + * @param {number} timestamp + * @return {string} + */ + get(key, timestamp) { + if (!this.keyStore.has(key)) { + return ""; + } + let seen = 0; + + for (let time of this.keyStore.get(key).keys()) { + if (time <= timestamp) { + seen = Math.max(seen, time); + } + } + return seen === 0 ? "" : this.keyStore.get(key).get(seen).at(-1); + } +} +``` + +```csharp +public class TimeMap { + private Dictionary>> keyStore; + + public TimeMap() { + keyStore = new Dictionary>>(); + } + + public void Set(string key, string value, int timestamp) { + if (!keyStore.ContainsKey(key)) { + keyStore[key] = new Dictionary>(); + } + if (!keyStore[key].ContainsKey(timestamp)) { + keyStore[key][timestamp] = new List(); + } + keyStore[key][timestamp].Add(value); + } + + public string Get(string key, int timestamp) { + if (!keyStore.ContainsKey(key)) { + return ""; + } + var timestamps = keyStore[key]; + int seen = 0; + + foreach (var time in timestamps.Keys) { + if (time <= timestamp) { + seen = time; + } + } + return seen == 0 ? "" : timestamps[seen][^1]; + } +} +``` + +```go +type TimeMap struct { + keyStore map[string]map[int][]string +} + +func Constructor() TimeMap { + return TimeMap{ + keyStore: make(map[string]map[int][]string), + } +} + +func (this *TimeMap) Set(key string, value string, timestamp int) { + if _, exists := this.keyStore[key]; !exists { + this.keyStore[key] = make(map[int][]string) + } + this.keyStore[key][timestamp] = append(this.keyStore[key][timestamp], value) +} + +func (this *TimeMap) Get(key string, timestamp int) string { + if _, exists := this.keyStore[key]; !exists { + return "" + } + + seen := 0 + for time := range this.keyStore[key] { + if time <= timestamp { + seen = max(seen, time) + } + } + + if seen == 0 { + return "" + } + values := this.keyStore[key][seen] + return values[len(values)-1] +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class TimeMap() { + private val keyStore = HashMap>>() + + fun set(key: String, value: String, timestamp: Int) { + if (!keyStore.containsKey(key)) { + keyStore[key] = HashMap() + } + if (!keyStore[key]!!.containsKey(timestamp)) { + keyStore[key]!![timestamp] = mutableListOf() + } + keyStore[key]!![timestamp]!!.add(value) + } + + fun get(key: String, timestamp: Int): String { + if (!keyStore.containsKey(key)) { + return "" + } + + var seen = 0 + for (time in keyStore[key]!!.keys) { + if (time <= timestamp) { + seen = maxOf(seen, time) + } + } + + if (seen == 0) { + return "" + } + return keyStore[key]!![seen]!!.last() + } +} +``` + +```swift +class TimeMap { + private var keyStore: [String: [Int: [String]]] + + init() { + self.keyStore = [:] + } + + func set(_ key: String, _ value: String, _ timestamp: Int) { + if keyStore[key] == nil { + keyStore[key] = [:] + } + if keyStore[key]![timestamp] == nil { + keyStore[key]![timestamp] = [] + } + keyStore[key]![timestamp]!.append(value) + } + + func get(_ key: String, _ timestamp: Int) -> String { + guard let timeMap = keyStore[key] else { + return "" + } + + var seen = 0 + for time in timeMap.keys { + if time <= timestamp { + seen = max(seen, time) + } + } + return seen == 0 ? "" : timeMap[seen]!.last! + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for $set()$ and $O(n)$ for $get()$. +* Space complexity: $O(m * n)$ + +> Where $n$ is the total number of unique timestamps associated with a key and $m$ is the total number of keys. + +--- + +## 2. Binary Search (Sorted Map) + +::tabs-start + +```python +from sortedcontainers import SortedDict + +class TimeMap: + def __init__(self): + self.m = defaultdict(SortedDict) + + def set(self, key: str, value: str, timestamp: int) -> None: + self.m[key][timestamp] = value + + def get(self, key: str, timestamp: int) -> str: + if key not in self.m: + return "" + + timestamps = self.m[key] + idx = timestamps.bisect_right(timestamp) - 1 + + if idx >= 0: + closest_time = timestamps.iloc[idx] + return timestamps[closest_time] + return "" +``` + +```java +public class TimeMap { + private Map> m; + + public TimeMap() { + m = new HashMap<>(); + } + + public void set(String key, String value, int timestamp) { + m.computeIfAbsent(key, k -> new TreeMap<>()).put(timestamp, value); + } + + public String get(String key, int timestamp) { + if (!m.containsKey(key)) return ""; + TreeMap timestamps = m.get(key); + Map.Entry entry = timestamps.floorEntry(timestamp); + return entry == null ? "" : entry.getValue(); + } +} +``` + +```cpp +class TimeMap { +public: + unordered_map> m; + + TimeMap() {} + + void set(string key, string value, int timestamp) { + m[key].insert({timestamp, value}); + } + + string get(string key, int timestamp) { + auto it = m[key].upper_bound(timestamp); + return it == m[key].begin() ? "" : prev(it)->second; + } +}; +``` + +```javascript +class TimeMap { + constructor() { + this.keyStore = new Map(); + } + + /** + * @param {string} key + * @param {string} value + * @param {number} timestamp + * @return {void} + */ + set(key, value, timestamp) { + if (!this.keyStore.has(key)) { + this.keyStore.set(key, []); + } + this.keyStore.get(key).push([timestamp, value]); + } + + /** + * @param {string} key + * @param {number} timestamp + * @return {string} + */ + get(key, timestamp) { + const values = this.keyStore.get(key) || []; + let left = 0; + let right = values.length - 1; + let result = ''; + + while (left <= right) { + const mid = Math.floor((left + right) / 2); + if (values[mid][0] <= timestamp) { + result = values[mid][1]; + left = mid + 1; + } else { + right = mid - 1; + } + } + + return result; + } +} +``` + +```csharp +public class TimeMap { + private Dictionary> m; + + public TimeMap() { + m = new Dictionary>(); + } + + public void Set(string key, string value, int timestamp) { + if (!m.ContainsKey(key)) { + m[key] = new SortedList(); + } + m[key][timestamp] = value; + } + + public string Get(string key, int timestamp) { + if (!m.ContainsKey(key)) return ""; + var timestamps = m[key]; + int left = 0; + int right = timestamps.Count - 1; + + while (left <= right) { + int mid = left + (right - left) / 2; + if (timestamps.Keys[mid] == timestamp) { + return timestamps.Values[mid]; + } else if (timestamps.Keys[mid] < timestamp) { + left = mid + 1; + } else { + right = mid - 1; + } + } + + if (right >= 0) { + return timestamps.Values[right]; + } + return ""; + } +} +``` + +```go +type TimeMap struct { + m map[string][]pair +} + +type pair struct { + timestamp int + value string +} + +func Constructor() TimeMap { + return TimeMap{ + m: make(map[string][]pair), + } +} + +func (this *TimeMap) Set(key string, value string, timestamp int) { + this.m[key] = append(this.m[key], pair{timestamp, value}) +} + +func (this *TimeMap) Get(key string, timestamp int) string { + if _, exists := this.m[key]; !exists { + return "" + } + + pairs := this.m[key] + idx := sort.Search(len(pairs), func(i int) bool { + return pairs[i].timestamp > timestamp + }) + + if idx == 0 { + return "" + } + return pairs[idx-1].value +} +``` + +```kotlin +class TimeMap() { + private val m = HashMap>() + + fun set(key: String, value: String, timestamp: Int) { + m.computeIfAbsent(key) { TreeMap() }[timestamp] = value + } + + fun get(key: String, timestamp: Int): String { + if (!m.containsKey(key)) return "" + return m[key]!!.floorEntry(timestamp)?.value ?: "" + } +} +``` + +```swift +class TimeMap { + private var m: [String: [(Int, String)]] + + init() { + self.m = [:] + } + + func set(_ key: String, _ value: String, _ timestamp: Int) { + if m[key] == nil { + m[key] = [] + } + m[key]!.append((timestamp, value)) + } + + func get(_ key: String, _ timestamp: Int) -> String { + guard let timestamps = m[key] else { + return "" + } + + var l = 0, r = timestamps.count - 1 + var res = "" + + while l <= r { + let mid = (l + r) / 2 + if timestamps[mid].0 <= timestamp { + res = timestamps[mid].1 + l = mid + 1 + } else { + r = mid - 1 + } + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for $set()$ and $O(\log n)$ for $get()$. +* Space complexity: $O(m * n)$ + +> Where $n$ is the total number of values associated with a key and $m$ is the total number of keys. + +--- + +## 3. Binary Search (Array) + +::tabs-start + +```python +class TimeMap: + + def __init__(self): + self.keyStore = {} # key : list of [val, timestamp] + + def set(self, key: str, value: str, timestamp: int) -> None: + if key not in self.keyStore: + self.keyStore[key] = [] + self.keyStore[key].append([value, timestamp]) + + def get(self, key: str, timestamp: int) -> str: + res, values = "", self.keyStore.get(key, []) + l, r = 0, len(values) - 1 + while l <= r: + m = (l + r) // 2 + if values[m][1] <= timestamp: + res = values[m][0] + l = m + 1 + else: + r = m - 1 + return res +``` + +```java +public class TimeMap { + + private Map>> keyStore; + + public TimeMap() { + keyStore = new HashMap<>(); + } + + public void set(String key, String value, int timestamp) { + keyStore.computeIfAbsent(key, k -> new ArrayList<>()).add(new Pair<>(timestamp, value)); + } + + public String get(String key, int timestamp) { + List> values = keyStore.getOrDefault(key, new ArrayList<>()); + int left = 0, right = values.size() - 1; + String result = ""; + + while (left <= right) { + int mid = left + (right - left) / 2; + if (values.get(mid).getKey() <= timestamp) { + result = values.get(mid).getValue(); + left = mid + 1; + } else { + right = mid - 1; + } + } + + return result; + } + + private static class Pair { + private final K key; + private final V value; + + public Pair(K key, V value) { + this.key = key; + this.value = value; + } + + public K getKey() { + return key; + } + + public V getValue() { + return value; + } + } +} +``` + +```cpp +class TimeMap { +private: + unordered_map>> keyStore; + +public: + TimeMap() {} + + void set(string key, string value, int timestamp) { + keyStore[key].emplace_back(timestamp, value); + } + + string get(string key, int timestamp) { + auto& values = keyStore[key]; + int left = 0, right = values.size() - 1; + string result = ""; + + while (left <= right) { + int mid = left + (right - left) / 2; + if (values[mid].first <= timestamp) { + result = values[mid].second; + left = mid + 1; + } else { + right = mid - 1; + } + } + + return result; + } +}; +``` + +```javascript +class TimeMap { + constructor() { + this.keyStore = new Map(); + } + + /** + * @param {string} key + * @param {string} value + * @param {number} timestamp + * @return {void} + */ + set(key, value, timestamp) { + if (!this.keyStore.has(key)) { + this.keyStore.set(key, []); + } + this.keyStore.get(key).push([timestamp, value]); + } + + /** + * @param {string} key + * @param {number} timestamp + * @return {string} + */ + get(key, timestamp) { + const values = this.keyStore.get(key) || []; + let left = 0; + let right = values.length - 1; + let result = ''; + + while (left <= right) { + const mid = Math.floor((left + right) / 2); + if (values[mid][0] <= timestamp) { + result = values[mid][1]; + left = mid + 1; + } else { + right = mid - 1; + } + } + + return result; + } +} +``` + +```csharp +public class TimeMap { + + private Dictionary>> keyStore; + + public TimeMap() { + keyStore = new Dictionary>>(); + } + + public void Set(string key, string value, int timestamp) { + if (!keyStore.ContainsKey(key)) { + keyStore[key] = new List>(); + } + keyStore[key].Add(Tuple.Create(timestamp, value)); + } + + public string Get(string key, int timestamp) { + if (!keyStore.ContainsKey(key)) { + return ""; + } + + var values = keyStore[key]; + int left = 0, right = values.Count - 1; + string result = ""; + + while (left <= right) { + int mid = left + (right - left) / 2; + if (values[mid].Item1 <= timestamp) { + result = values[mid].Item2; + left = mid + 1; + } else { + right = mid - 1; + } + } + + return result; + } +} +``` + +```go +type TimeMap struct { + m map[string][]pair +} + +type pair struct { + timestamp int + value string +} + +func Constructor() TimeMap { + return TimeMap{ + m: make(map[string][]pair), + } +} + +func (this *TimeMap) Set(key string, value string, timestamp int) { + this.m[key] = append(this.m[key], pair{timestamp, value}) +} + +func (this *TimeMap) Get(key string, timestamp int) string { + if _, exists := this.m[key]; !exists { + return "" + } + + pairs := this.m[key] + l, r := 0, len(pairs)-1 + + for l <= r { + mid := (l + r) / 2 + if pairs[mid].timestamp <= timestamp { + if mid == len(pairs)-1 || pairs[mid+1].timestamp > timestamp { + return pairs[mid].value + } + l = mid + 1 + } else { + r = mid - 1 + } + } + return "" +} +``` + +```kotlin +class TimeMap() { + private val keyStore = HashMap>>() + + fun set(key: String, value: String, timestamp: Int) { + if (!keyStore.containsKey(key)) { + keyStore[key] = mutableListOf() + } + keyStore[key]!!.add(Pair(value, timestamp)) + } + + fun get(key: String, timestamp: Int): String { + var res = "" + val values = keyStore[key] ?: return res + var l = 0 + var r = values.size - 1 + + while (l <= r) { + val m = (l + r) / 2 + if (values[m].second <= timestamp) { + res = values[m].first + l = m + 1 + } else { + r = m - 1 + } + } + return res + } +} +``` + +```swift +class TimeMap { + private var keyStore: [String: [(String, Int)]] + + init() { + self.keyStore = [:] + } + + func set(_ key: String, _ value: String, _ timestamp: Int) { + if keyStore[key] == nil { + keyStore[key] = [] + } + keyStore[key]!.append((value, timestamp)) + } + + func get(_ key: String, _ timestamp: Int) -> String { + guard let values = keyStore[key] else { + return "" + } + + var res = "" + var l = 0, r = values.count - 1 + + while l <= r { + let m = (l + r) / 2 + if values[m].1 <= timestamp { + res = values[m].0 + l = m + 1 + } else { + r = m - 1 + } + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ for $set()$ and $O(\log n)$ for $get()$. +* Space complexity: $O(m * n)$ + +> Where $n$ is the total number of values associated with a key and $m$ is the total number of keys. \ No newline at end of file diff --git a/articles/time-needed-to-buy-tickets.md b/articles/time-needed-to-buy-tickets.md new file mode 100644 index 000000000..b7422f54c --- /dev/null +++ b/articles/time-needed-to-buy-tickets.md @@ -0,0 +1,329 @@ +## 1. Queue + +::tabs-start + +```python +class Solution: + def timeRequiredToBuy(self, tickets: List[int], k: int) -> int: + n = len(tickets) + q = deque() + + for i in range(n): + q.append(i) + + time = 0 + while q: + time += 1 + cur = q.popleft() + tickets[cur] -= 1 + if tickets[cur] == 0: + if cur == k: + return time + else: + q.append(cur) + return time +``` + +```java +public class Solution { + public int timeRequiredToBuy(int[] tickets, int k) { + int n = tickets.length; + Queue queue = new LinkedList<>(); + + for (int i = 0; i < n; i++) { + queue.add(i); + } + + int time = 0; + while (!queue.isEmpty()) { + time++; + int cur = queue.poll(); + tickets[cur]--; + if (tickets[cur] == 0) { + if (cur == k) { + return time; + } + } else { + queue.add(cur); + } + } + return time; + } +} +``` + +```cpp +class Solution { +public: + int timeRequiredToBuy(vector& tickets, int k) { + int n = tickets.size(); + queue q; + + for (int i = 0; i < n; i++) { + q.push(i); + } + + int time = 0; + while (!q.empty()) { + time++; + int cur = q.front(); + q.pop(); + tickets[cur]--; + if (tickets[cur] == 0) { + if (cur == k) { + return time; + } + } else { + q.push(cur); + } + } + return time; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} tickets + * @param {number} k + * @return {number} + */ + timeRequiredToBuy(tickets, k) { + let n = tickets.length; + let queue = new Queue(); + + for (let i = 0; i < n; i++) { + queue.push(i); + } + + let time = 0; + while (queue.size() > 0) { + time++; + let cur = queue.pop(); + tickets[cur]--; + if (tickets[cur] === 0) { + if (cur === k) { + return time; + } + } else { + queue.push(cur); + } + } + return time; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the size of the input array and $m$ is the maximum value in the input array. + +--- + +## 2. Iteration + +::tabs-start + +```python +class Solution: + def timeRequiredToBuy(self, tickets: List[int], k: int) -> int: + n = len(tickets) + idx = 0 + + time = 0 + while True: + time += 1 + tickets[idx] -= 1 + if tickets[idx] == 0: + if idx == k: + return time + idx = (idx + 1) % n + while tickets[idx] == 0: + idx = (idx + 1) % n + + return time +``` + +```java +public class Solution { + public int timeRequiredToBuy(int[] tickets, int k) { + int n = tickets.length; + int idx = 0; + + int time = 0; + while (true) { + time++; + tickets[idx]--; + if (tickets[idx] == 0) { + if (idx == k) { + return time; + } + } + idx = (idx + 1) % n; + while (tickets[idx] == 0) { + idx = (idx + 1) % n; + } + } + } +} +``` + +```cpp +class Solution { +public: + int timeRequiredToBuy(vector& tickets, int k) { + int n = tickets.size(); + int idx = 0; + + int time = 0; + while (true) { + time++; + tickets[idx]--; + if (tickets[idx] == 0) { + if (idx == k) { + return time; + } + } + idx = (idx + 1) % n; + while (tickets[idx] == 0) { + idx = (idx + 1) % n; + } + } + + return time; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} tickets + * @param {number} k + * @return {number} + */ + timeRequiredToBuy(tickets, k) { + let n = tickets.length; + let idx = 0; + + let time = 0; + while (true) { + time++; + tickets[idx]--; + if (tickets[idx] === 0) { + if (idx === k) { + return time; + } + } + idx = (idx + 1) % n; + while (tickets[idx] === 0) { + idx = (idx + 1) % n; + } + } + + return time; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(1)$ + +> Where $n$ is the size of the input array and $m$ is the maximum value in the input array. + +--- + +## 3. Iteration (One Pass) + +::tabs-start + +```python +class Solution: + def timeRequiredToBuy(self, tickets: List[int], k: int) -> int: + res = 0 + + for i in range(len(tickets)): + if i <= k: + res += min(tickets[i], tickets[k]) + else: + res += min(tickets[i], tickets[k] - 1) + + return res +``` + +```java +public class Solution { + public int timeRequiredToBuy(int[] tickets, int k) { + int res = 0; + + for (int i = 0; i < tickets.length; i++) { + if (i <= k) { + res += Math.min(tickets[i], tickets[k]); + } else { + res += Math.min(tickets[i], tickets[k] - 1); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int timeRequiredToBuy(vector& tickets, int k) { + int res = 0; + + for (int i = 0; i < tickets.size(); i++) { + if (i <= k) { + res += min(tickets[i], tickets[k]); + } else { + res += min(tickets[i], tickets[k] - 1); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} tickets + * @param {number} k + * @return {number} + */ + timeRequiredToBuy(tickets, k) { + let res = 0; + + for (let i = 0; i < tickets.length; i++) { + if (i <= k) { + res += Math.min(tickets[i], tickets[k]); + } else { + res += Math.min(tickets[i], tickets[k] - 1); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/time-needed-to-inform-all-employees.md b/articles/time-needed-to-inform-all-employees.md new file mode 100644 index 000000000..7a80a7c02 --- /dev/null +++ b/articles/time-needed-to-inform-all-employees.md @@ -0,0 +1,479 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def numOfMinutes(self, n: int, headID: int, manager: List[int], informTime: List[int]) -> int: + adj = [[] for _ in range(n)] + for i in range(n): + if i != headID: + adj[manager[i]].append(i) + + def dfs(node): + res = 0 + for child in adj[node]: + res = max(res, informTime[node] + dfs(child)) + return res + + return dfs(headID) +``` + +```java +public class Solution { + public int numOfMinutes(int n, int headID, int[] manager, int[] informTime) { + List[] adj = new ArrayList[n]; + for (int i = 0; i < n; i++) { + adj[i] = new ArrayList<>(); + } + for (int i = 0; i < n; i++) { + if (i != headID) { + adj[manager[i]].add(i); + } + } + + return dfs(headID, adj, informTime); + } + + private int dfs(int node, List[] adj, int[] informTime) { + int res = 0; + for (int child : adj[node]) { + res = Math.max(res, informTime[node] + dfs(child, adj, informTime)); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numOfMinutes(int n, int headID, vector& manager, vector& informTime) { + vector> adj(n); + for (int i = 0; i < n; i++) { + if (i != headID) { + adj[manager[i]].push_back(i); + } + } + return dfs(headID, adj, informTime); + } + +private: + int dfs(int node, vector>& adj, vector& informTime) { + int res = 0; + for (int child : adj[node]) { + res = max(res, informTime[node] + dfs(child, adj, informTime)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} headID + * @param {number[]} manager + * @param {number[]} informTime + * @return {number} + */ + numOfMinutes(n, headID, manager, informTime) { + const adj = Array.from({ length: n }, () => []); + for (let i = 0; i < n; i++) { + if (i !== headID) { + adj[manager[i]].push(i); + } + } + + const dfs = (node) => { + let res = 0; + for (const child of adj[node]) { + res = Math.max(res, informTime[node] + dfs(child)); + } + return res; + }; + + return dfs(headID); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def numOfMinutes(self, n: int, headID: int, manager: List[int], informTime: List[int]) -> int: + adj = defaultdict(list) + for i in range(n): + adj[manager[i]].append(i) + + q = deque([(headID, 0)]) # (id, time) + res = 0 + + while q: + node, time = q.popleft() + res = max(res, time) + for emp in adj[node]: + q.append((emp, time + informTime[node])) + + return res +``` + +```java +public class Solution { + public int numOfMinutes(int n, int headID, int[] manager, int[] informTime) { + Map> adj = new HashMap<>(); + for (int i = 0; i < n; i++) { + adj.computeIfAbsent(manager[i], k -> new ArrayList<>()).add(i); + } + + Queue queue = new LinkedList<>(); + queue.add(new int[]{headID, 0}); + int res = 0; + + while (!queue.isEmpty()) { + int[] curr = queue.poll(); + int id = curr[0], time = curr[1]; + res = Math.max(res, time); + for (int emp : adj.getOrDefault(id, new ArrayList<>())) { + queue.add(new int[]{emp, time + informTime[id]}); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int numOfMinutes(int n, int headID, vector& manager, vector& informTime) { + unordered_map> adj; + for (int i = 0; i < n; ++i) { + adj[manager[i]].push_back(i); + } + + queue> q; // {id, time} + q.push({headID, 0}); + int res = 0; + + while (!q.empty()) { + auto [id, time] = q.front(); + q.pop(); + res = max(res, time); + for (int emp : adj[id]) { + q.push({emp, time + informTime[id]}); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} headID + * @param {number[]} manager + * @param {number[]} informTime + * @return {number} + */ + numOfMinutes(n, headID, manager, informTime) { + const adj = new Map(); + for (let i = 0; i < n; i++) { + if (!adj.has(manager[i])) adj.set(manager[i], []); + adj.get(manager[i]).push(i); + } + + const queue = new Queue([[headID, 0]]); // [id, time] + let res = 0; + + while (!queue.isEmpty()) { + const [id, time] = queue.pop(); + res = Math.max(res, time); + if (adj.has(id)) { + for (const emp of adj.get(id)) { + queue.push([emp, time + informTime[id]]); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Topological Sort (Kahn's Algorithm) + +::tabs-start + +```python +class Solution: + def numOfMinutes(self, n: int, headID: int, manager: List[int], informTime: List[int]) -> int: + indegree = [0] * n + time = [0] * n + + for i in range(n): + if manager[i] != -1: + indegree[manager[i]] += 1 + + queue = deque() + for i in range(n): + if indegree[i] == 0: + queue.append(i) + + while queue: + node = queue.popleft() + time[node] += informTime[node] + if manager[node] != -1: + time[manager[node]] = max(time[manager[node]], time[node]) + indegree[manager[node]] -= 1 + if indegree[manager[node]] == 0: + queue.append(manager[node]) + + return time[headID] +``` + +```java +public class Solution { + public int numOfMinutes(int n, int headID, int[] manager, int[] informTime) { + int[] indegree = new int[n]; + int[] time = new int[n]; + + for (int i = 0; i < n; i++) { + if (manager[i] != -1) { + indegree[manager[i]]++; + } + } + + Queue queue = new LinkedList<>(); + for (int i = 0; i < n; i++) { + if (indegree[i] == 0) { + queue.add(i); + } + } + + while (!queue.isEmpty()) { + int node = queue.poll(); + time[node] += informTime[node]; + if (manager[node] != -1) { + time[manager[node]] = Math.max(time[manager[node]], time[node]); + indegree[manager[node]]--; + if (indegree[manager[node]] == 0) { + queue.add(manager[node]); + } + } + } + + return time[headID]; + } +} +``` + +```cpp +class Solution { +public: + int numOfMinutes(int n, int headID, vector& manager, vector& informTime) { + vector indegree(n, 0); + vector time(n, 0); + + for (int i = 0; i < n; ++i) { + if (manager[i] != -1) { + indegree[manager[i]]++; + } + } + + queue queue; + for (int i = 0; i < n; ++i) { + if (indegree[i] == 0) { + queue.push(i); + } + } + + while (!queue.empty()) { + int node = queue.front(); + queue.pop(); + time[node] += informTime[node]; + if (manager[node] != -1) { + time[manager[node]] = max(time[manager[node]], time[node]); + indegree[manager[node]]--; + if (indegree[manager[node]] == 0) { + queue.push(manager[node]); + } + } + } + + return time[headID]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} headID + * @param {number[]} manager + * @param {number[]} informTime + * @return {number} + */ + numOfMinutes(n, headID, manager, informTime) { + const indegree = Array(n).fill(0); + const time = Array(n).fill(0); + + for (let i = 0; i < n; i++) { + if (manager[i] !== -1) { + indegree[manager[i]]++; + } + } + + const queue = new Queue(); + for (let i = 0; i < n; i++) { + if (indegree[i] === 0) { + queue.push(i); + } + } + + while (!queue.isEmpty()) { + const node = queue.pop(); + time[node] += informTime[node]; + if (manager[node] !== -1) { + time[manager[node]] = Math.max(time[manager[node]], time[node]); + indegree[manager[node]]--; + if (indegree[manager[node]] === 0) { + queue.push(manager[node]); + } + } + } + + return time[headID]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Depth First Search (Optimal) + +::tabs-start + +```python +class Solution: + def numOfMinutes(self, n: int, headID: int, manager: List[int], informTime: List[int]) -> int: + def dfs(node): + if manager[node] != -1: + informTime[node] += dfs(manager[node]) + manager[node] = -1 + return informTime[node] + + res = 0 + for node in range(n): + res = max(res, dfs(node)) + return res +``` + +```java +public class Solution { + public int numOfMinutes(int n, int headID, int[] manager, int[] informTime) { + int res = 0; + for (int node = 0; node < n; node++) { + res = Math.max(res, dfs(node, manager, informTime)); + } + return res; + } + + private int dfs(int node, int[] manager, int[] informTime) { + if (manager[node] != -1) { + informTime[node] += dfs(manager[node], manager, informTime); + manager[node] = -1; + } + return informTime[node]; + } +} +``` + +```cpp +class Solution { +public: + int numOfMinutes(int n, int headID, vector& manager, vector& informTime) { + function dfs = [&](int node) { + if (manager[node] != -1) { + informTime[node] += dfs(manager[node]); + manager[node] = -1; + } + return informTime[node]; + }; + + int res = 0; + for (int node = 0; node < n; ++node) { + res = max(res, dfs(node)); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number} headID + * @param {number[]} manager + * @param {number[]} informTime + * @return {number} + */ + numOfMinutes(n, headID, manager, informTime) { + const dfs = (node) => { + if (manager[node] !== -1) { + informTime[node] += dfs(manager[node]); + manager[node] = -1; + } + return informTime[node]; + }; + + let res = 0; + for (let node = 0; node < n; node++) { + res = Math.max(res, dfs(node)); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. \ No newline at end of file diff --git a/articles/top-k-elements-in-list.md b/articles/top-k-elements-in-list.md new file mode 100644 index 000000000..7bca2df74 --- /dev/null +++ b/articles/top-k-elements-in-list.md @@ -0,0 +1,660 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def topKFrequent(self, nums: List[int], k: int) -> List[int]: + count = {} + for num in nums: + count[num] = 1 + count.get(num, 0) + + arr = [] + for num, cnt in count.items(): + arr.append([cnt, num]) + arr.sort() + + res = [] + while len(res) < k: + res.append(arr.pop()[1]) + return res +``` + +```java +public class Solution { + public int[] topKFrequent(int[] nums, int k) { + Map count = new HashMap<>(); + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + List arr = new ArrayList<>(); + for (Map.Entry entry : count.entrySet()) { + arr.add(new int[] {entry.getValue(), entry.getKey()}); + } + arr.sort((a, b) -> b[0] - a[0]); + + int[] res = new int[k]; + for (int i = 0; i < k; i++) { + res[i] = arr.get(i)[1]; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector topKFrequent(vector& nums, int k) { + unordered_map count; + for (int num : nums) { + count[num]++; + } + + vector> arr; + for (const auto& p : count) { + arr.push_back({p.second, p.first}); + } + sort(arr.rbegin(), arr.rend()); + + vector res; + for (int i = 0; i < k; ++i) { + res.push_back(arr[i].second); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ + topKFrequent(nums, k) { + const count = {}; + for (const num of nums) { + count[num] = (count[num] || 0) + 1; + } + + const arr = Object.entries(count).map(([num, freq]) => [freq, parseInt(num)]); + arr.sort((a, b) => b[0] - a[0]); + + return arr.slice(0, k).map(pair => pair[1]); + } +} +``` + +```csharp +public class Solution { + public int[] TopKFrequent(int[] nums, int k) { + Dictionary count = new Dictionary(); + foreach (int num in nums) { + if (count.ContainsKey(num)) count[num]++; + else count[num] = 1; + } + + List arr = count.Select(entry => new int[] {entry.Value, entry.Key}).ToList(); + arr.Sort((a, b) => b[0].CompareTo(a[0])); + + int[] res = new int[k]; + for (int i = 0; i < k; i++) { + res[i] = arr[i][1]; + } + return res; + } +} +``` + +```go +func topKFrequent(nums []int, k int) []int { + count := make(map[int]int) + for _, num := range nums { + count[num]++ + } + + arr := make([][2]int, 0, len(count)) + for num, cnt := range count { + arr = append(arr, [2]int{cnt, num}) + } + + sort.Slice(arr, func(i, j int) bool { + return arr[i][0] > arr[j][0] + }) + + res := make([]int, k) + for i := 0; i < k; i++ { + res[i] = arr[i][1] + } + return res +} +``` + +```kotlin +class Solution { + fun topKFrequent(nums: IntArray, k: Int): IntArray { + val count = HashMap() + for (num in nums) { + count[num] = count.getOrDefault(num, 0) + 1 + } + + val arr = mutableListOf>() + for ((num, freq) in count) { + arr.add(Pair(freq, num)) + } + arr.sortByDescending { it.first } + + val res = IntArray(k) + for (i in 0 until k) { + res[i] = arr[i].second + } + return res + } +} +``` + +```swift +class Solution { + func topKFrequent(_ nums: [Int], _ k: Int) -> [Int] { + var count = [Int: Int]() + for num in nums { + count[num, default: 0] += 1 + } + + var arr = [(Int, Int)]() + for (num, cnt) in count { + arr.append((cnt, num)) + } + arr.sort { $0.0 < $1.0 } + + var res = [Int]() + while res.count < k { + res.append(arr.removeLast().1) + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Min-Heap + +::tabs-start + +```python +class Solution: + def topKFrequent(self, nums: List[int], k: int) -> List[int]: + count = {} + for num in nums: + count[num] = 1 + count.get(num, 0) + + heap = [] + for num in count.keys(): + heapq.heappush(heap, (count[num], num)) + if len(heap) > k: + heapq.heappop(heap) + + res = [] + for i in range(k): + res.append(heapq.heappop(heap)[1]) + return res +``` + +```java +public class Solution { + public int[] topKFrequent(int[] nums, int k) { + Map count = new HashMap<>(); + for (int num : nums) { + count.put(num, count.getOrDefault(num, 0) + 1); + } + + PriorityQueue heap = new PriorityQueue<>((a, b) -> a[0] - b[0]); + for (Map.Entry entry : count.entrySet()) { + heap.offer(new int[]{entry.getValue(), entry.getKey()}); + if (heap.size() > k) { + heap.poll(); + } + } + + int[] res = new int[k]; + for (int i = 0; i < k; i++) { + res[i] = heap.poll()[1]; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector topKFrequent(vector& nums, int k) { + unordered_map count; + for (int num : nums) { + count[num]++; + } + + priority_queue, vector>, greater>> heap; + for (auto& entry : count) { + heap.push({entry.second, entry.first}); + if (heap.size() > k) { + heap.pop(); + } + } + + vector res; + for (int i = 0; i < k; i++) { + res.push_back(heap.top().second); + heap.pop(); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ + topKFrequent(nums, k) { + const count = {}; + for (const num of nums) { + count[num] = (count[num] || 0) + 1; + } + + const heap = new MinPriorityQueue(x => x[1]); + for(const [num, cnt] of Object.entries(count)){ + heap.enqueue([num, cnt]); + if (heap.size() > k) heap.dequeue(); + } + + const res = []; + for(let i = 0; i < k; i++) { + const [num, cnt] = heap.dequeue(); + res.push(num) + } + return res; + } +} +``` + +```csharp +public class Solution { + public int[] TopKFrequent(int[] nums, int k) { + var count = new Dictionary(); + foreach (var num in nums) { + if (count.ContainsKey(num)) { + count[num]++; + } else { + count[num] = 1; + } + } + + var heap = new PriorityQueue(); + foreach (var entry in count) { + heap.Enqueue(entry.Key, entry.Value); + if (heap.Count > k) { + heap.Dequeue(); + } + } + + var res = new int[k]; + for (int i = 0; i < k; i++) { + res[i] = heap.Dequeue(); + } + return res; + } +} +``` + +```go +func topKFrequent(nums []int, k int) []int { + count := make(map[int]int) + for _, num := range nums { + count[num]++ + } + + heap := priorityqueue.NewWith(func(a, b interface{}) int { + freqA := a.([2]int)[0] + freqB := b.([2]int)[0] + return utils.IntComparator(freqA, freqB) + }) + + for num, freq := range count { + heap.Enqueue([2]int{freq, num}) + if heap.Size() > k { + heap.Dequeue() + } + } + + res := make([]int, k) + for i := k - 1; i >= 0; i-- { + value, _ := heap.Dequeue() + res[i] = value.([2]int)[1] + } + return res +} +``` + +```kotlin +class Solution { + fun topKFrequent(nums: IntArray, k: Int): IntArray { + val count = HashMap() + for (num in nums) { + count[num] = count.getOrDefault(num, 0) + 1 + } + + val heap = PriorityQueue>(compareBy { it.first }) + for ((num, freq) in count) { + heap.add(Pair(freq, num)) + if (heap.size > k) { + heap.poll() + } + } + + val res = IntArray(k) + for (i in k - 1 downTo 0) { + res[i] = heap.poll().second + } + return res + } +} +``` + +```swift +struct NumFreq: Comparable { + let num: Int + let freq: Int + + static func < (lhs: NumFreq, rhs: NumFreq) -> Bool { + return lhs.freq < rhs.freq + } +} + +class Solution { + func topKFrequent(_ nums: [Int], _ k: Int) -> [Int] { + var count = [Int: Int]() + for num in nums { + count[num, default: 0] += 1 + } + + var heap: Heap = [] + for (num, freq) in count { + heap.insert(NumFreq(num: num, freq: freq)) + if heap.count > k { + heap.removeMin() + } + } + + var res = [Int]() + while !heap.isEmpty { + res.append(heap.removeMin().num) + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log k)$ +* Space complexity: $O(n + k)$ + +> Where $n$ is the length of the array and $k$ is the number of top frequent elements. + +--- + +## 3. Bucket Sort + +::tabs-start + +```python +class Solution: + def topKFrequent(self, nums: List[int], k: int) -> List[int]: + count = {} + freq = [[] for i in range(len(nums) + 1)] + + for num in nums: + count[num] = 1 + count.get(num, 0) + for num, cnt in count.items(): + freq[cnt].append(num) + + res = [] + for i in range(len(freq) - 1, 0, -1): + for num in freq[i]: + res.append(num) + if len(res) == k: + return res +``` + +```java +public class Solution { + public int[] topKFrequent(int[] nums, int k) { + Map count = new HashMap<>(); + List[] freq = new List[nums.length + 1]; + + for (int i = 0; i < freq.length; i++) { + freq[i] = new ArrayList<>(); + } + + for (int n : nums) { + count.put(n, count.getOrDefault(n, 0) + 1); + } + for (Map.Entry entry : count.entrySet()) { + freq[entry.getValue()].add(entry.getKey()); + } + + int[] res = new int[k]; + int index = 0; + for (int i = freq.length - 1; i > 0 && index < k; i--) { + for (int n : freq[i]) { + res[index++] = n; + if (index == k) { + return res; + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + vector topKFrequent(vector& nums, int k) { + unordered_map count; + vector> freq(nums.size() + 1); + + for (int n : nums) { + count[n] = 1 + count[n]; + } + for (const auto& entry : count) { + freq[entry.second].push_back(entry.first); + } + + vector res; + for (int i = freq.size() - 1; i > 0; --i) { + for (int n : freq[i]) { + res.push_back(n); + if (res.size() == k) { + return res; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ + topKFrequent(nums, k) { + const count = {}; + const freq = Array.from({ length: nums.length + 1 }, () => []); + + for (const n of nums) { + count[n] = (count[n] || 0) + 1; + } + for (const n in count) { + freq[count[n]].push(parseInt(n)); + } + + const res = []; + for (let i = freq.length - 1; i > 0; i--) { + for (const n of freq[i]) { + res.push(n); + if (res.length === k) { + return res; + } + } + } + } +} +``` + +```csharp +public class Solution { + public int[] TopKFrequent(int[] nums, int k) { + Dictionary count = new Dictionary(); + List[] freq = new List[nums.Length + 1]; + for (int i = 0; i < freq.Length; i++) { + freq[i] = new List(); + } + + foreach (int n in nums) { + if (count.ContainsKey(n)) { + count[n]++; + } else { + count[n] = 1; + } + } + foreach (var entry in count){ + freq[entry.Value].Add(entry.Key); + } + + int[] res = new int[k]; + int index = 0; + for (int i = freq.Length - 1; i > 0 && index < k; i--) { + foreach (int n in freq[i]) { + res[index++] = n; + if (index == k) { + return res; + } + } + } + return res; + } +} +``` + +```go +func topKFrequent(nums []int, k int) []int { + count := make(map[int]int) + freq := make([][]int, len(nums)+1) + + for _, num := range nums { + count[num]++ + } + for num, cnt := range count { + freq[cnt] = append(freq[cnt], num) + } + + res := []int{} + for i := len(freq) - 1; i > 0; i-- { + for _, num := range freq[i] { + res = append(res, num) + if len(res) == k { + return res + } + } + } + return res +} +``` + +```kotlin +class Solution { + fun topKFrequent(nums: IntArray, k: Int): IntArray { + val count = HashMap() + val freq = List(nums.size + 1) { mutableListOf() } + + for (num in nums) { + count[num] = count.getOrDefault(num, 0) + 1 + } + for ((num, cnt) in count) { + freq[cnt].add(num) + } + + val res = mutableListOf() + for (i in freq.size - 1 downTo 1) { + for (num in freq[i]) { + res.add(num) + if (res.size == k) { + return res.toIntArray() + } + } + } + return res.toIntArray() + } +} +``` + +```swift +class Solution { + func topKFrequent(_ nums: [Int], _ k: Int) -> [Int] { + var count = [Int: Int]() + var freq = [[Int]](repeating: [], count: nums.count + 1) + + for num in nums { + count[num, default: 0] += 1 + } + + for (num, cnt) in count { + freq[cnt].append(num) + } + + var res = [Int]() + for i in stride(from: freq.count - 1, through: 1, by: -1) { + for num in freq[i] { + res.append(num) + if res.count == k { + return res + } + } + } + + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/transpose-matrix.md b/articles/transpose-matrix.md new file mode 100644 index 000000000..d92b422ab --- /dev/null +++ b/articles/transpose-matrix.md @@ -0,0 +1,208 @@ +## 1. Iteration - I + +::tabs-start + +```python +class Solution: + def transpose(self, matrix: List[List[int]]) -> List[List[int]]: + ROWS, COLS = len(matrix), len(matrix[0]) + res = [[0] * ROWS for _ in range(COLS)] + + for r in range(ROWS): + for c in range(COLS): + res[c][r] = matrix[r][c] + + return res +``` + +```java +public class Solution { + public int[][] transpose(int[][] matrix) { + int ROWS = matrix.length, COLS = matrix[0].length; + int[][] res = new int[COLS][ROWS]; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + res[c][r] = matrix[r][c]; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> transpose(vector>& matrix) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + vector> res(COLS, vector(ROWS)); + + for (int r = 0; r < ROWS; ++r) { + for (int c = 0; c < COLS; ++c) { + res[c][r] = matrix[r][c]; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number[][]} + */ + transpose(matrix) { + const ROWS = matrix.length, COLS = matrix[0].length; + const res = Array.from({ length: COLS }, () => Array(ROWS).fill(0)); + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + res[c][r] = matrix[r][c]; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ for the output array. + +> Where $n$ is the number of rows and $m$ is the number of columns in the matrix. + +--- + +## 2. Iteration - II + +::tabs-start + +```python +class Solution: + def transpose(self, matrix: List[List[int]]) -> List[List[int]]: + ROWS, COLS = len(matrix), len(matrix[0]) + + if ROWS == COLS: + for r in range(ROWS): + for c in range(r): + matrix[r][c], matrix[c][r] = matrix[c][r], matrix[r][c] + + return matrix + + res = [[0] * ROWS for _ in range(COLS)] + + for r in range(ROWS): + for c in range(COLS): + res[c][r] = matrix[r][c] + + return res +``` + +```java +public class Solution { + public int[][] transpose(int[][] matrix) { + int ROWS = matrix.length, COLS = matrix[0].length; + + if (ROWS == COLS) { + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < r; c++) { + int tmp = matrix[r][c]; + matrix[r][c] = matrix[c][r]; + matrix[c][r] = tmp; + } + } + + return matrix; + } + + int[][] res = new int[COLS][ROWS]; + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + res[c][r] = matrix[r][c]; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> transpose(vector>& matrix) { + int ROWS = matrix.size(), COLS = matrix[0].size(); + + if (ROWS == COLS) { + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < r; c++) { + swap(matrix[r][c], matrix[c][r]); + } + } + + return matrix; + } + + vector> res(COLS, vector(ROWS)); + + for (int r = 0; r < ROWS; ++r) { + for (int c = 0; c < COLS; ++c) { + res[c][r] = matrix[r][c]; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} matrix + * @return {number[][]} + */ + transpose(matrix) { + const ROWS = matrix.length, COLS = matrix[0].length; + + if (ROWS === COLS) { + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < r; c++) { + [matrix[r][c], matrix[c][r]] = [matrix[c][r], matrix[r][c]]; + } + } + + return matrix; + } + + const res = Array.from({ length: COLS }, () => Array(ROWS).fill(0)); + + for (let r = 0; r < ROWS; r++) { + for (let c = 0; c < COLS; c++) { + res[c][r] = matrix[r][c]; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of rows and $m$ is the number of columns in the matrix. \ No newline at end of file diff --git a/articles/trapping-rain-water.md b/articles/trapping-rain-water.md new file mode 100644 index 000000000..4e8f600b8 --- /dev/null +++ b/articles/trapping-rain-water.md @@ -0,0 +1,971 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def trap(self, height: List[int]) -> int: + if not height: + return 0 + n = len(height) + res = 0 + + for i in range(n): + leftMax = rightMax = height[i] + + for j in range(i): + leftMax = max(leftMax, height[j]) + for j in range(i + 1, n): + rightMax = max(rightMax, height[j]) + + res += min(leftMax, rightMax) - height[i] + return res +``` + +```java +public class Solution { + public int trap(int[] height) { + if (height == null || height.length == 0) { + return 0; + } + int n = height.length; + int res = 0; + + for (int i = 0; i < n; i++) { + int leftMax = height[i]; + int rightMax = height[i]; + + for (int j = 0; j < i; j++) { + leftMax = Math.max(leftMax, height[j]); + } + for (int j = i + 1; j < n; j++) { + rightMax = Math.max(rightMax, height[j]); + } + + res += Math.min(leftMax, rightMax) - height[i]; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int trap(vector& height) { + if (height.empty()) { + return 0; + } + int n = height.size(); + int res = 0; + + for (int i = 0; i < n; i++) { + int leftMax = height[i]; + int rightMax = height[i]; + + for (int j = 0; j < i; j++) { + leftMax = max(leftMax, height[j]); + } + for (int j = i + 1; j < n; j++) { + rightMax = max(rightMax, height[j]); + } + + res += min(leftMax, rightMax) - height[i]; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} height + * @return {number} + */ + trap(height) { + if (!height.length) { + return 0; + } + let n = height.length; + let res = 0; + + for (let i = 0; i < n; i++) { + let leftMax = height[i]; + let rightMax = height[i]; + + for (let j = 0; j < i; j++) { + leftMax = Math.max(leftMax, height[j]); + } + for (let j = i + 1; j < n; j++) { + rightMax = Math.max(rightMax, height[j]); + } + + res += Math.min(leftMax, rightMax) - height[i]; + } + return res; + } +} +``` + +```csharp +public class Solution { + public int Trap(int[] height) { + if (height == null || height.Length == 0) { + return 0; + } + int n = height.Length; + int res = 0; + + for (int i = 0; i < n; i++) { + int leftMax = height[i]; + int rightMax = height[i]; + + for (int j = 0; j < i; j++) { + leftMax = Math.Max(leftMax, height[j]); + } + for (int j = i + 1; j < n; j++) { + rightMax = Math.Max(rightMax, height[j]); + } + + res += Math.Min(leftMax, rightMax) - height[i]; + } + + return res; + } +} +``` + +```go +func trap(height []int) int { + if len(height) == 0 { + return 0 + } + n := len(height) + res := 0 + + for i := 0; i < n; i++ { + leftMax := height[i] + rightMax := height[i] + + for j := 0; j < i; j++ { + if height[j] > leftMax { + leftMax = height[j] + } + } + for j := i + 1; j < n; j++ { + if height[j] > rightMax { + rightMax = height[j] + } + } + + res += min(leftMax, rightMax) - height[i] + } + return res +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun trap(height: IntArray): Int { + if (height.isEmpty()) return 0 + val n = height.size + var res = 0 + + for (i in 0 until n) { + var leftMax = height[i] + var rightMax = height[i] + + for (j in 0 until i) { + leftMax = maxOf(leftMax, height[j]) + } + for (j in i + 1 until n) { + rightMax = maxOf(rightMax, height[j]) + } + + res += minOf(leftMax, rightMax) - height[i] + } + return res + } +} +``` + +```swift +class Solution { + func trap(_ height: [Int]) -> Int { + if height.isEmpty { + return 0 + } + let n = height.count + var res = 0 + + for i in 0.. int: + n = len(height) + if n == 0: + return 0 + + leftMax = [0] * n + rightMax = [0] * n + + leftMax[0] = height[0] + for i in range(1, n): + leftMax[i] = max(leftMax[i - 1], height[i]) + + rightMax[n - 1] = height[n - 1] + for i in range(n - 2, -1, -1): + rightMax[i] = max(rightMax[i + 1], height[i]) + + res = 0 + for i in range(n): + res += min(leftMax[i], rightMax[i]) - height[i] + return res +``` + +```java +public class Solution { + public int trap(int[] height) { + int n = height.length; + if (n == 0) { + return 0; + } + + int[] leftMax = new int[n]; + int[] rightMax = new int[n]; + + leftMax[0] = height[0]; + for (int i = 1; i < n; i++) { + leftMax[i] = Math.max(leftMax[i - 1], height[i]); + } + + rightMax[n - 1] = height[n - 1]; + for (int i = n - 2; i >= 0; i--) { + rightMax[i] = Math.max(rightMax[i + 1], height[i]); + } + + int res = 0; + for (int i = 0; i < n; i++) { + res += Math.min(leftMax[i], rightMax[i]) - height[i]; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int trap(vector& height) { + int n = height.size(); + if (n == 0) { + return 0; + } + + vector leftMax(n); + vector rightMax(n); + + leftMax[0] = height[0]; + for (int i = 1; i < n; i++) { + leftMax[i] = max(leftMax[i - 1], height[i]); + } + + rightMax[n - 1] = height[n - 1]; + for (int i = n - 2; i >= 0; i--) { + rightMax[i] = max(rightMax[i + 1], height[i]); + } + + int res = 0; + for (int i = 0; i < n; i++) { + res += min(leftMax[i], rightMax[i]) - height[i]; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} height + * @return {number} + */ + trap(height) { + let n = height.length; + if (n === 0) { + return 0; + } + + let leftMax = new Array(n).fill(0); + let rightMax = new Array(n).fill(0); + + leftMax[0] = height[0]; + for (let i = 1; i < n; i++) { + leftMax[i] = Math.max(leftMax[i - 1], height[i]); + } + + rightMax[n - 1] = height[n - 1]; + for (let i = n - 2; i >= 0; i--) { + rightMax[i] = Math.max(rightMax[i + 1], height[i]); + } + + let res = 0; + for (let i = 0; i < n; i++) { + res += Math.min(leftMax[i], rightMax[i]) - height[i]; + } + return res; + } +} +``` + +```csharp +public class Solution { + public int Trap(int[] height) { + int n = height.Length; + if (n == 0) { + return 0; + } + + int[] leftMax = new int[n]; + int[] rightMax = new int[n]; + + leftMax[0] = height[0]; + for (int i = 1; i < n; i++) { + leftMax[i] = Math.Max(leftMax[i - 1], height[i]); + } + + rightMax[n - 1] = height[n - 1]; + for (int i = n - 2; i >= 0; i--) { + rightMax[i] = Math.Max(rightMax[i + 1], height[i]); + } + + int res = 0; + for (int i = 0; i < n; i++) { + res += Math.Min(leftMax[i], rightMax[i]) - height[i]; + } + return res; + } +} +``` + +```go +func trap(height []int) int { + n := len(height) + if n == 0 { + return 0 + } + + leftMax := make([]int, n) + rightMax := make([]int, n) + + leftMax[0] = height[0] + for i := 1; i < n; i++ { + leftMax[i] = max(leftMax[i-1], height[i]) + } + + rightMax[n-1] = height[n-1] + for i := n - 2; i >= 0; i-- { + rightMax[i] = max(rightMax[i+1], height[i]) + } + + res := 0 + for i := 0; i < n; i++ { + res += min(leftMax[i], rightMax[i]) - height[i] + } + return res +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun trap(height: IntArray): Int { + val n = height.size + if (n == 0) return 0 + + val leftMax = IntArray(n) + val rightMax = IntArray(n) + + leftMax[0] = height[0] + for (i in 1 until n) { + leftMax[i] = maxOf(leftMax[i - 1], height[i]) + } + + rightMax[n - 1] = height[n - 1] + for (i in n - 2 downTo 0) { + rightMax[i] = maxOf(rightMax[i + 1], height[i]) + } + + var res = 0 + for (i in 0 until n) { + res += minOf(leftMax[i], rightMax[i]) - height[i] + } + return res + } +} +``` + +```swift +class Solution { + func trap(_ height: [Int]) -> Int { + let n = height.count + if n == 0 { + return 0 + } + + var leftMax = [Int](repeating: 0, count: n) + var rightMax = [Int](repeating: 0, count: n) + + leftMax[0] = height[0] + for i in 1.. int: + if not height: + return 0 + stack = [] + res = 0 + + for i in range(len(height)): + while stack and height[i] >= height[stack[-1]]: + mid = height[stack.pop()] + if stack: + right = height[i] + left = height[stack[-1]] + h = min(right, left) - mid + w = i - stack[-1] - 1 + res += h * w + stack.append(i) + return res +``` + +```java +public class Solution { + public int trap(int[] height) { + if (height.length == 0) { + return 0; + } + + Stack stack = new Stack<>(); + int res = 0; + + for (int i = 0; i < height.length; i++) { + while (!stack.isEmpty() && height[i] >= height[stack.peek()]) { + int mid = height[stack.pop()]; + if (!stack.isEmpty()) { + int right = height[i]; + int left = height[stack.peek()]; + int h = Math.min(right, left) - mid; + int w = i - stack.peek() - 1; + res += h * w; + } + } + stack.push(i); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int trap(vector& height) { + if (height.empty()) { + return 0; + } + + stack stk; + int res = 0; + + for (int i = 0; i < height.size(); i++) { + while (!stk.empty() && height[i] >= height[stk.top()]) { + int mid = height[stk.top()]; + stk.pop(); + if (!stk.empty()) { + int right = height[i]; + int left = height[stk.top()]; + int h = min(right, left) - mid; + int w = i - stk.top() - 1; + res += h * w; + } + } + stk.push(i); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} height + * @return {number} + */ + trap(height) { + if (height.length === 0) { + return 0; + } + + const stack = []; + let res = 0; + + for (let i = 0; i < height.length; i++) { + while (stack.length > 0 && height[i] >= height[stack[stack.length - 1]]) { + const mid = height[stack.pop()]; + if (stack.length > 0) { + const right = height[i]; + const left = height[stack[stack.length - 1]]; + const h = Math.min(right, left) - mid; + const w = i - stack[stack.length - 1] - 1; + res += h * w; + } + } + stack.push(i); + } + return res; + } +} +``` + +```csharp +public class Solution { + public int Trap(int[] height) { + if (height.Length == 0) { + return 0; + } + + Stack stack = new Stack(); + int res = 0; + + for (int i = 0; i < height.Length; i++) { + while (stack.Count > 0 && height[i] >= height[stack.Peek()]) { + int mid = height[stack.Pop()]; + if (stack.Count > 0) { + int right = height[i]; + int left = height[stack.Peek()]; + int h = Math.Min(right, left) - mid; + int w = i - stack.Peek() - 1; + res += h * w; + } + } + stack.Push(i); + } + return res; + } +} +``` + +```go +func trap(height []int) int { + if len(height) == 0 { + return 0 + } + + stack := linkedliststack.New() + res := 0 + + for i := 0; i < len(height); i++ { + for !stack.Empty() { + topIndex, _ := stack.Peek() + if height[i] >= height[topIndex.(int)] { + midIndex, _ := stack.Pop() + mid := height[midIndex.(int)] + if !stack.Empty() { + topIndex, _ := stack.Peek() + right := height[i] + left := height[topIndex.(int)] + h := min(right, left) - mid + w := i - topIndex.(int) - 1 + res += h * w + } + } else { + break + } + } + stack.Push(i) + } + return res +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun trap(height: IntArray): Int { + if (height.isEmpty()) return 0 + + val stack = ArrayDeque() + var res = 0 + + for (i in height.indices) { + while (stack.isNotEmpty() && height[i] >= height[stack.first()]) { + val mid = stack.removeFirst() + if (stack.isNotEmpty()) { + val left = stack.first() + val right = height[i] + val h = minOf(right, height[left]) - height[mid] + val w = i - left - 1 + res += h * w + } + } + stack.addFirst(i) + } + return res + } +} +``` + +```swift +class Solution { + func trap(_ height: [Int]) -> Int { + if height.isEmpty { + return 0 + } + var stack = [Int]() + var res = 0 + + for i in 0..= height[stack.last!] { + let mid = height[stack.removeLast()] + if !stack.isEmpty { + let right = height[i] + let left = height[stack.last!] + let h = min(right, left) - mid + let w = i - stack.last! - 1 + res += h * w + } + } + stack.append(i) + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Two Pointers + +::tabs-start + +```python +class Solution: + def trap(self, height: List[int]) -> int: + if not height: + return 0 + + l, r = 0, len(height) - 1 + leftMax, rightMax = height[l], height[r] + res = 0 + while l < r: + if leftMax < rightMax: + l += 1 + leftMax = max(leftMax, height[l]) + res += leftMax - height[l] + else: + r -= 1 + rightMax = max(rightMax, height[r]) + res += rightMax - height[r] + return res +``` + +```java +public class Solution { + public int trap(int[] height) { + if (height == null || height.length == 0) { + return 0; + } + + int l = 0, r = height.length - 1; + int leftMax = height[l], rightMax = height[r]; + int res = 0; + while (l < r) { + if (leftMax < rightMax) { + l++; + leftMax = Math.max(leftMax, height[l]); + res += leftMax - height[l]; + } else { + r--; + rightMax = Math.max(rightMax, height[r]); + res += rightMax - height[r]; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int trap(vector& height) { + if (height.empty()) { + return 0; + } + + int l = 0, r = height.size() - 1; + int leftMax = height[l], rightMax = height[r]; + int res = 0; + while (l < r) { + if (leftMax < rightMax) { + l++; + leftMax = max(leftMax, height[l]); + res += leftMax - height[l]; + } else { + r--; + rightMax = max(rightMax, height[r]); + res += rightMax - height[r]; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} height + * @return {number} + */ + trap(height) { + if (!height || height.length === 0) { + return 0; + } + + let l = 0; + let r = height.length - 1; + let leftMax = height[l]; + let rightMax = height[r]; + let res = 0; + while (l < r) { + if (leftMax < rightMax) { + l++; + leftMax = Math.max(leftMax, height[l]); + res += leftMax - height[l]; + } else { + r--; + rightMax = Math.max(rightMax, height[r]); + res += rightMax - height[r]; + } + } + return res; + } +} +``` + +```csharp +public class Solution { + public int Trap(int[] height) { + if (height == null || height.Length == 0) { + return 0; + } + + int l = 0, r = height.Length - 1; + int leftMax = height[l], rightMax = height[r]; + int res = 0; + while (l < r) { + if (leftMax < rightMax) { + l++; + leftMax = Math.Max(leftMax, height[l]); + res += leftMax - height[l]; + } else { + r--; + rightMax = Math.Max(rightMax, height[r]); + res += rightMax - height[r]; + } + } + return res; + } +} +``` + +```go +func trap(height []int) int { + if len(height) == 0 { + return 0 + } + + l, r := 0, len(height)-1 + leftMax, rightMax := height[l], height[r] + res := 0 + + for l < r { + if leftMax < rightMax { + l++ + leftMax = max(leftMax, height[l]) + res += leftMax - height[l] + } else { + r-- + rightMax = max(rightMax, height[r]) + res += rightMax - height[r] + } + } + return res +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +``` + +```kotlin +class Solution { + fun trap(height: IntArray): Int { + if (height.isEmpty()) return 0 + + var l = 0 + var r = height.size - 1 + var leftMax = height[l] + var rightMax = height[r] + var res = 0 + + while (l < r) { + if (leftMax < rightMax) { + l++ + leftMax = maxOf(leftMax, height[l]) + res += leftMax - height[l] + } else { + r-- + rightMax = maxOf(rightMax, height[r]) + res += rightMax - height[r] + } + } + return res + } +} +``` + +```swift +class Solution { + func trap(_ height: [Int]) -> Int { + if height.isEmpty { + return 0 + } + + var l = 0, r = height.count - 1 + var leftMax = height[l], rightMax = height[r] + var res = 0 + + while l < r { + if leftMax < rightMax { + l += 1 + leftMax = max(leftMax, height[l]) + res += leftMax - height[l] + } else { + r -= 1 + rightMax = max(rightMax, height[r]) + res += rightMax - height[r] + } + } + return res + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/triangle.md b/articles/triangle.md new file mode 100644 index 000000000..53b994a33 --- /dev/null +++ b/articles/triangle.md @@ -0,0 +1,535 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def minimumTotal(self, triangle: List[List[int]]) -> int: + def dfs(row, col): + if row >= len(triangle): + return 0 + return triangle[row][col] + min(dfs(row + 1, col), dfs(row + 1, col + 1)) + + return dfs(0, 0) +``` + +```java +public class Solution { + public int minimumTotal(List> triangle) { + return dfs(0, 0, triangle); + } + + private int dfs(int row, int col, List> triangle) { + if (row >= triangle.size()) { + return 0; + } + return triangle.get(row).get(col) + Math.min(dfs(row + 1, col, triangle), dfs(row + 1, col + 1, triangle)); + } +} +``` + +```cpp +class Solution { +public: + int minimumTotal(vector>& triangle) { + return dfs(0, 0, triangle); + } + +private: + int dfs(int row, int col, vector>& triangle) { + if (row >= triangle.size()) { + return 0; + } + return triangle[row][col] + min(dfs(row + 1, col, triangle), dfs(row + 1, col + 1, triangle)); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} triangle + * @return {number} + */ + minimumTotal(triangle) { + const dfs = (row, col) => { + if (row >= triangle.length) { + return 0; + } + return triangle[row][col] + Math.min(dfs(row + 1, col), dfs(row + 1, col + 1)); + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def minimumTotal(self, triangle: List[List[int]]) -> int: + memo = [[0] * len(triangle[r]) for r in range(len(triangle))] + INF = float("inf") + for r in range(len(triangle)): + for c in range(len(triangle[r])): + memo[r][c] = INF + + def dfs(row, col): + if row >= len(triangle): + return 0 + if memo[row][col] != INF: + return memo[row][col] + + memo[row][col] = triangle[row][col] + min(dfs(row + 1, col), dfs(row + 1, col + 1)) + return memo[row][col] + + return dfs(0, 0) +``` + +```java +public class Solution { + public int minimumTotal(List> triangle) { + int[][] memo = new int[triangle.size()][]; + int INF = Integer.MAX_VALUE; + for (int r = 0; r < triangle.size(); r++) { + memo[r] = new int[triangle.get(r).size()]; + Arrays.fill(memo[r], INF); + } + + return dfs(0, 0, triangle, memo); + } + + private int dfs(int row, int col, List> triangle, int[][] memo) { + if (row >= triangle.size()) { + return 0; + } + if (memo[row][col] != Integer.MAX_VALUE) { + return memo[row][col]; + } + + memo[row][col] = triangle.get(row).get(col) + Math.min(dfs(row + 1, col, triangle, memo), dfs(row + 1, col + 1, triangle, memo)); + return memo[row][col]; + } +} +``` + +```cpp +class Solution { +public: + int minimumTotal(vector>& triangle) { + vector> memo(triangle.size(), vector(0)); + int INF = INT_MAX; + for (int r = 0; r < triangle.size(); ++r) { + memo[r].resize(triangle[r].size(), INF); + } + + return dfs(0, 0, triangle, memo); + } + +private: + int dfs(int row, int col, vector>& triangle, vector>& memo) { + if (row >= triangle.size()) { + return 0; + } + if (memo[row][col] != INT_MAX) { + return memo[row][col]; + } + + memo[row][col] = triangle[row][col] + min(dfs(row + 1, col, triangle, memo), dfs(row + 1, col + 1, triangle, memo)); + return memo[row][col]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} triangle + * @return {number} + */ + minimumTotal(triangle) { + const memo = Array.from({ length: triangle.length }, (_, r) => Array(triangle[r].length).fill(Infinity)); + + const dfs = (row, col) => { + if (row >= triangle.length) { + return 0; + } + if (memo[row][col] !== Infinity) { + return memo[row][col]; + } + + memo[row][col] = triangle[row][col] + Math.min(dfs(row + 1, col), dfs(row + 1, col + 1)); + return memo[row][col]; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def minimumTotal(self, triangle: List[List[int]]) -> int: + n = len(triangle) + dp = [[0] * len(triangle[row]) for row in range(n)] + dp[-1] = triangle[-1][:] + + for row in range(n - 2, -1, -1): + for col in range(len(triangle[row])): + dp[row][col] = triangle[row][col] + min(dp[row + 1][col], dp[row + 1][col + 1]) + + return dp[0][0] +``` + +```java +public class Solution { + public int minimumTotal(List> triangle) { + int n = triangle.size(); + int[][] dp = new int[n][n]; + for (int col = 0; col < triangle.get(n - 1).size(); col++) { + dp[n - 1][col] = triangle.get(n - 1).get(col); + } + + for (int row = n - 2; row >= 0; row--) { + for (int col = 0; col < triangle.get(row).size(); col++) { + dp[row][col] = triangle.get(row).get(col) + Math.min(dp[row + 1][col], dp[row + 1][col + 1]); + } + } + + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + int minimumTotal(vector>& triangle) { + int n = triangle.size(); + vector> dp(n, vector(n, 0)); + for (int col = 0; col < triangle[n - 1].size(); ++col) { + dp[n - 1][col] = triangle[n - 1][col]; + } + + for (int row = n - 2; row >= 0; --row) { + for (int col = 0; col < triangle[row].size(); ++col) { + dp[row][col] = triangle[row][col] + min(dp[row + 1][col], dp[row + 1][col + 1]); + } + } + + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} triangle + * @return {number} + */ + minimumTotal(triangle) { + const n = triangle.length; + const dp = Array.from({ length: n }, (_, i) => Array(triangle[i].length).fill(0)); + for (let col = 0; col < triangle[n - 1].length; col++) { + dp[n - 1][col] = triangle[n - 1][col]; + } + + for (let row = n - 2; row >= 0; row--) { + for (let col = 0; col < triangle[row].length; col++) { + dp[row][col] = triangle[row][col] + Math.min(dp[row + 1][col], dp[row + 1][col + 1]); + } + } + + return dp[0][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Dynamic Programming (Space Optimized) - I + +::tabs-start + +```python +class Solution: + def minimumTotal(self, triangle: List[List[int]]) -> int: + n = len(triangle) + dp = triangle[0][:] + + for row in range(1, n): + nxtDp = [0] * len(triangle[row]) + nxtDp[0] = dp[0] + triangle[row][0] + for col in range(1, len(triangle[row]) - 1): + nxtDp[col] = triangle[row][col] + min(dp[col], dp[col - 1]) + nxtDp[-1] = dp[-1] + triangle[row][-1] + dp = nxtDp + + return min(dp) +``` + +```java +public class Solution { + public int minimumTotal(List> triangle) { + int n = triangle.size(); + int[] dp = new int[n]; + dp[0] = triangle.get(0).get(0); + + for (int row = 1; row < n; row++) { + int[] nxtDp = new int[row + 1]; + nxtDp[0] = dp[0] + triangle.get(row).get(0); + for (int col = 1; col < row; col++) { + nxtDp[col] = triangle.get(row).get(col) + Math.min(dp[col], dp[col - 1]); + } + nxtDp[row] = dp[row - 1] + triangle.get(row).get(row); + dp = nxtDp; + } + + int minPath = Integer.MAX_VALUE; + for (int value : dp) { + minPath = Math.min(minPath, value); + } + return minPath; + } +} +``` + +```cpp +class Solution { +public: + int minimumTotal(vector>& triangle) { + int n = triangle.size(); + vector dp = triangle[0]; + + for (int row = 1; row < n; row++) { + vector nxtDp(row + 1, 0); + nxtDp[0] = dp[0] + triangle[row][0]; + for (int col = 1; col < row; col++) { + nxtDp[col] = triangle[row][col] + min(dp[col], dp[col - 1]); + } + nxtDp[row] = dp[row - 1] + triangle[row][row]; + dp = nxtDp; + } + + return *min_element(dp.begin(), dp.end()); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} triangle + * @return {number} + */ + minimumTotal(triangle) { + let n = triangle.length; + let dp = [...triangle[0]]; + + for (let row = 1; row < n; row++) { + let nxtDp = new Array(row + 1).fill(0); + nxtDp[0] = dp[0] + triangle[row][0]; + for (let col = 1; col < row; col++) { + nxtDp[col] = triangle[row][col] + Math.min(dp[col], dp[col - 1]); + } + nxtDp[row] = dp[row - 1] + triangle[row][row]; + dp = nxtDp; + } + + return Math.min(...dp); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ extra space. + +--- + +## 5. Dynamic Programming (Space Optimized) - II + +::tabs-start + +```python +class Solution: + def minimumTotal(self, triangle: List[List[int]]) -> int: + n = len(triangle) + dp = triangle[-1][:] + + for row in range(n - 2, -1, -1): + for col in range(len(triangle[row])): + dp[col] = triangle[row][col] + min(dp[col], dp[col + 1]) + + return dp[0] +``` + +```java +public class Solution { + public int minimumTotal(List> triangle) { + int n = triangle.size(); + int[] dp = new int[n]; + for (int i = 0; i < n; i++) { + dp[i] = triangle.get(n - 1).get(i); + } + + for (int row = n - 2; row >= 0; row--) { + for (int col = 0; col < triangle.get(row).size(); col++) { + dp[col] = triangle.get(row).get(col) + Math.min(dp[col], dp[col + 1]); + } + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int minimumTotal(vector>& triangle) { + int n = triangle.size(); + vector dp(triangle.back()); + + for (int row = n - 2; row >= 0; --row) { + for (int col = 0; col < triangle[row].size(); ++col) { + dp[col] = triangle[row][col] + min(dp[col], dp[col + 1]); + } + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} triangle + * @return {number} + */ + minimumTotal(triangle) { + const n = triangle.length; + const dp = [...triangle[n - 1]]; + + for (let row = n - 2; row >= 0; row--) { + for (let col = 0; col < triangle[row].length; col++) { + dp[col] = triangle[row][col] + Math.min(dp[col], dp[col + 1]); + } + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ extra space. + +--- + +## 6. Dynamic Programming (In-Place) + +::tabs-start + +```python +class Solution: + def minimumTotal(self, triangle: List[List[int]]) -> int: + for row in range(len(triangle) - 2, -1, -1): + for col in range(len(triangle[row])): + triangle[row][col] += min(triangle[row + 1][col], triangle[row + 1][col + 1]) + + return triangle[0][0] +``` + +```java +public class Solution { + public int minimumTotal(List> triangle) { + for (int row = triangle.size() - 2; row >= 0; row--) { + for (int col = 0; col < triangle.get(row).size(); col++) { + triangle.get(row).set(col, triangle.get(row).get(col) + + Math.min(triangle.get(row + 1).get(col), triangle.get(row + 1).get(col + 1))); + } + } + return triangle.get(0).get(0); + } +} +``` + +```cpp +class Solution { +public: + int minimumTotal(vector>& triangle) { + for (int row = triangle.size() - 2; row >= 0; row--) { + for (int col = 0; col < triangle[row].size(); col++) { + triangle[row][col] += min(triangle[row + 1][col], triangle[row + 1][col + 1]); + } + } + return triangle[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} triangle + * @return {number} + */ + minimumTotal(triangle) { + for (let row = triangle.length - 2; row >= 0; row--) { + for (let col = 0; col < triangle[row].length; col++) { + triangle[row][col] += Math.min(triangle[row + 1][col], triangle[row + 1][col + 1]); + } + } + return triangle[0][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/trim-a-binary-search-tree.md b/articles/trim-a-binary-search-tree.md new file mode 100644 index 000000000..6a7eb3f21 --- /dev/null +++ b/articles/trim-a-binary-search-tree.md @@ -0,0 +1,488 @@ +## 1. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def trimBST(self, root: Optional[TreeNode], low: int, high: int) -> Optional[TreeNode]: + if not root: + return None + + if root.val > high: + return self.trimBST(root.left, low, high) + if root.val < low: + return self.trimBST(root.right, low, high) + + root.left = self.trimBST(root.left, low, high) + root.right = self.trimBST(root.right, low, high) + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode trimBST(TreeNode root, int low, int high) { + if (root == null) { + return null; + } + + if (root.val > high) { + return trimBST(root.left, low, high); + } + if (root.val < low) { + return trimBST(root.right, low, high); + } + + root.left = trimBST(root.left, low, high); + root.right = trimBST(root.right, low, high); + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* trimBST(TreeNode* root, int low, int high) { + if (!root) return nullptr; + + if (root->val > high) { + return trimBST(root->left, low, high); + } + if (root->val < low) { + return trimBST(root->right, low, high); + } + + root->left = trimBST(root->left, low, high); + root->right = trimBST(root->right, low, high); + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} low + * @param {number} high + * @return {TreeNode} + */ + trimBST(root, low, high) { + if (!root) return null; + + if (root.val > high) return this.trimBST(root.left, low, high); + if (root.val < low) return this.trimBST(root.right, low, high); + + root.left = this.trimBST(root.left, low, high); + root.right = this.trimBST(root.right, low, high); + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Iterative DFS + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def trimBST(self, root, low, high): + while root and (root.val < low or root.val > high): + if root.val < low: + root = root.right + else: + root = root.left + + stack = [root] + while stack: + node = stack.pop() + if not node: + continue + left_out = node.left and node.left.val < low + right_out = node.right and node.right.val > high + if left_out: + node.left = node.left.right + if right_out: + node.right = node.right.left + if left_out or right_out: + stack.append(node) + else: + if node.left: + stack.append(node.left) + if node.right: + stack.append(node.right) + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode trimBST(TreeNode root, int low, int high) { + while (root != null && (root.val < low || root.val > high)) { + root = (root.val < low) ? root.right : root.left; + } + + Stack stack = new Stack<>(); + stack.push(root); + + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + if (node == null) continue; + + boolean leftOut = (node.left != null && node.left.val < low); + boolean rightOut = (node.right != null && node.right.val > high); + + if (leftOut) node.left = node.left.right; + if (rightOut) node.right = node.right.left; + + if (leftOut || rightOut) { + stack.push(node); + } else { + if (node.left != null) stack.push(node.left); + if (node.right != null) stack.push(node.right); + } + } + + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* trimBST(TreeNode* root, int low, int high) { + while (root && (root->val < low || root->val > high)) { + root = (root->val < low) ? root->right : root->left; + } + + stack stack; + stack.push(root); + + while (!stack.empty()) { + TreeNode* node = stack.top(); + stack.pop(); + if (!node) continue; + + bool leftOut = (node->left && node->left->val < low); + bool rightOut = (node->right && node->right->val > high); + + if (leftOut) node->left = node->left->right; + if (rightOut) node->right = node->right->left; + + if (leftOut || rightOut) { + stack.push(node); + } else { + if (node->left) stack.push(node->left); + if (node->right) stack.push(node->right); + } + } + + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} low + * @param {number} high + * @return {TreeNode} + */ + trimBST(root, low, high) { + while (root && (root.val < low || root.val > high)) { + root = (root.val < low) ? root.right : root.left; + } + + const stack = [root]; + + while (stack.length > 0) { + let node = stack.pop(); + if (!node) continue; + + let leftOut = node.left && node.left.val < low; + let rightOut = node.right && node.right.val > high; + + if (leftOut) node.left = node.left.right; + if (rightOut) node.right = node.right.left; + + if (leftOut || rightOut) { + stack.push(node); + } else { + if (node.left) stack.push(node.left); + if (node.right) stack.push(node.right); + } + } + + return root; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Iterative DFS (Optimal) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def trimBST(self, root: Optional[TreeNode], low: int, high: int) -> Optional[TreeNode]: + while root and (root.val < low or root.val > high): + root = root.right if root.val < low else root.left + + tmpRoot = root + while root: + while root.left and root.left.val < low: + root.left = root.left.right + root = root.left + + root = tmpRoot + while root: + while root.right and root.right.val > high: + root.right = root.right.left + root = root.right + + return tmpRoot +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public TreeNode trimBST(TreeNode root, int low, int high) { + while (root != null && (root.val < low || root.val > high)) { + root = (root.val < low) ? root.right : root.left; + } + + TreeNode tmpRoot = root; + while (root != null) { + while (root.left != null && root.left.val < low) { + root.left = root.left.right; + } + root = root.left; + } + + root = tmpRoot; + while (root != null) { + while (root.right != null && root.right.val > high) { + root.right = root.right.left; + } + root = root.right; + } + + return tmpRoot; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* trimBST(TreeNode* root, int low, int high) { + while (root && (root->val < low || root->val > high)) { + root = (root->val < low) ? root->right : root->left; + } + + TreeNode* tmpRoot = root; + while (root) { + while (root->left && root->left->val < low) { + root->left = root->left->right; + } + root = root->left; + } + + root = tmpRoot; + while (root) { + while (root->right && root->right->val > high) { + root->right = root->right->left; + } + root = root->right; + } + + return tmpRoot; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {TreeNode} root + * @param {number} low + * @param {number} high + * @return {TreeNode} + */ + trimBST(root, low, high) { + while (root && (root.val < low || root.val > high)) { + root = (root.val < low) ? root.right : root.left; + } + + let tmpRoot = root; + while (root) { + while (root.left && root.left.val < low) { + root.left = root.left.right; + } + root = root.left; + } + + root = tmpRoot; + while (root) { + while (root.right && root.right.val > high) { + root.right = root.right.left; + } + root = root.right; + } + + return tmpRoot; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/two-city-scheduling.md b/articles/two-city-scheduling.md new file mode 100644 index 000000000..61f80767c --- /dev/null +++ b/articles/two-city-scheduling.md @@ -0,0 +1,683 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def twoCitySchedCost(self, costs: List[List[int]]) -> int: + n = len(costs) // 2 + + def dfs(i, aCount, bCount): + if i == len(costs): + return 0 + + res = float("inf") + if aCount > 0: + res = costs[i][0] + dfs(i + 1, aCount - 1, bCount) + + if bCount > 0: + res = min(res, costs[i][1] + dfs(i + 1, aCount, bCount - 1)) + return res + + return dfs(0, n, n) +``` + +```java +public class Solution { + public int twoCitySchedCost(int[][] costs) { + int n = costs.length / 2; + return dfs(costs, 0, n, n); + } + + private int dfs(int[][] costs, int i, int aCount, int bCount) { + if (i == costs.length) { + return 0; + } + + int res = Integer.MAX_VALUE; + if (aCount > 0) { + res = costs[i][0] + dfs(costs, i + 1, aCount - 1, bCount); + } + + if (bCount > 0) { + res = Math.min(res, costs[i][1] + dfs(costs, i + 1, aCount, bCount - 1)); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int twoCitySchedCost(vector>& costs) { + int n = costs.size() / 2; + return dfs(costs, 0, n, n); + } + +private: + int dfs(vector>& costs, int i, int aCount, int bCount) { + if (i == costs.size()) { + return 0; + } + + int res = INT_MAX; + if (aCount > 0) { + res = costs[i][0] + dfs(costs, i + 1, aCount - 1, bCount); + } + + if (bCount > 0) { + res = min(res, costs[i][1] + dfs(costs, i + 1, aCount, bCount - 1)); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + twoCitySchedCost(costs) { + let n = costs.length / 2; + + const dfs = (i, aCount, bCount) => { + if (i === costs.length) { + return 0; + } + + let res = Infinity; + if (aCount > 0) { + res = costs[i][0] + dfs(i + 1, aCount - 1, bCount); + } + + if (bCount > 0) { + res = Math.min(res, costs[i][1] + dfs(i + 1, aCount, bCount - 1)); + } + + return res; + }; + + return dfs(0, n, n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ N)$ +* Space complexity: $O(N)$ for recursion stack. + +> Where $N$ is the size of the array $costs$. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def twoCitySchedCost(self, costs: List[List[int]]) -> int: + n = len(costs) // 2 + dp = [[-1] * (n + 1) for _ in range(n + 1)] + + def dfs(i, aCount, bCount): + if i == len(costs): + return 0 + if dp[aCount][bCount] != -1: + return dp[aCount][bCount] + + res = float("inf") + if aCount > 0: + res = costs[i][0] + dfs(i + 1, aCount - 1, bCount) + if bCount > 0: + res = min(res, costs[i][1] + dfs(i + 1, aCount, bCount - 1)) + + dp[aCount][bCount] = res + return res + + return dfs(0, n, n) +``` + +```java +public class Solution { + private int[][] dp; + + public int twoCitySchedCost(int[][] costs) { + int n = costs.length / 2; + dp = new int[n + 1][n + 1]; + for (int[] row : dp) { + Arrays.fill(row, -1); + } + return dfs(costs, 0, n, n); + } + + private int dfs(int[][] costs, int i, int aCount, int bCount) { + if (i == costs.length) { + return 0; + } + if (dp[aCount][bCount] != -1) { + return dp[aCount][bCount]; + } + + int res = Integer.MAX_VALUE; + if (aCount > 0) { + res = costs[i][0] + dfs(costs, i + 1, aCount - 1, bCount); + } + if (bCount > 0) { + res = Math.min(res, costs[i][1] + dfs(costs, i + 1, aCount, bCount - 1)); + } + + dp[aCount][bCount] = res; + return res; + } +} +``` + +```cpp +class Solution { +public: + vector> dp; + + int twoCitySchedCost(vector>& costs) { + int n = costs.size() / 2; + dp = vector>(n + 1, vector(n + 1, -1)); + return dfs(costs, 0, n, n); + } + +private: + int dfs(vector>& costs, int i, int aCount, int bCount) { + if (i == costs.size()) { + return 0; + } + if (dp[aCount][bCount] != -1) { + return dp[aCount][bCount]; + } + + int res = INT_MAX; + if (aCount > 0) { + res = costs[i][0] + dfs(costs, i + 1, aCount - 1, bCount); + } + if (bCount > 0) { + res = min(res, costs[i][1] + dfs(costs, i + 1, aCount, bCount - 1)); + } + + dp[aCount][bCount] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + twoCitySchedCost(costs) { + let n = costs.length / 2; + let dp = Array.from({ length: n + 1 }, () => new Array(n + 1).fill(-1)); + + const dfs = (i, aCount, bCount) => { + if (i === costs.length) { + return 0; + } + if (dp[aCount][bCount] !== -1) { + return dp[aCount][bCount]; + } + + let res = Infinity; + if (aCount > 0) { + res = costs[i][0] + dfs(i + 1, aCount - 1, bCount); + } + if (bCount > 0) { + res = Math.min(res, costs[i][1] + dfs(i + 1, aCount, bCount - 1)); + } + + dp[aCount][bCount] = res; + return res; + }; + + return dfs(0, n, n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +> Where $n$ is the half of the size of the array $costs$. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def twoCitySchedCost(self, costs: List[List[int]]) -> int: + n = len(costs) // 2 + dp = [[0] * (n + 1) for _ in range(n + 1)] + + for aCount in range(n + 1): + for bCount in range(n + 1): + i = aCount + bCount + if i == 0: + continue + + dp[aCount][bCount] = float("inf") + if aCount > 0: + dp[aCount][bCount] = min(dp[aCount][bCount], dp[aCount - 1][bCount] + costs[i - 1][0]) + if bCount > 0: + dp[aCount][bCount] = min(dp[aCount][bCount], dp[aCount][bCount - 1] + costs[i - 1][1]) + + return dp[n][n] +``` + +```java +public class Solution { + public int twoCitySchedCost(int[][] costs) { + int n = costs.length / 2; + int[][] dp = new int[n + 1][n + 1]; + + for (int aCount = 0; aCount <= n; aCount++) { + for (int bCount = 0; bCount <= n; bCount++) { + int i = aCount + bCount; + if (i == 0) continue; + + dp[aCount][bCount] = Integer.MAX_VALUE; + if (aCount > 0) { + dp[aCount][bCount] = Math.min(dp[aCount][bCount], dp[aCount - 1][bCount] + costs[i - 1][0]); + } + if (bCount > 0) { + dp[aCount][bCount] = Math.min(dp[aCount][bCount], dp[aCount][bCount - 1] + costs[i - 1][1]); + } + } + } + + return dp[n][n]; + } +} +``` + +```cpp +class Solution { +public: + int twoCitySchedCost(vector>& costs) { + int n = costs.size() / 2; + vector> dp(n + 1, vector(n + 1)); + + for (int aCount = 0; aCount <= n; aCount++) { + for (int bCount = 0; bCount <= n; bCount++) { + int i = aCount + bCount; + if (i == 0) continue; + + dp[aCount][bCount] = INT_MAX; + if (aCount > 0) { + dp[aCount][bCount] = min(dp[aCount][bCount], dp[aCount - 1][bCount] + costs[i - 1][0]); + } + if (bCount > 0) { + dp[aCount][bCount] = min(dp[aCount][bCount], dp[aCount][bCount - 1] + costs[i - 1][1]); + } + } + } + + return dp[n][n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + twoCitySchedCost(costs) { + let n = costs.length / 2; + let dp = Array.from({ length: n + 1 }, () => new Array(n + 1).fill(0)); + + for (let aCount = 0; aCount <= n; aCount++) { + for (let bCount = 0; bCount <= n; bCount++) { + let i = aCount + bCount; + if (i === 0) continue; + + dp[aCount][bCount] = Infinity; + if (aCount > 0) { + dp[aCount][bCount] = Math.min(dp[aCount][bCount], dp[aCount - 1][bCount] + costs[i - 1][0]); + } + if (bCount > 0) { + dp[aCount][bCount] = Math.min(dp[aCount][bCount], dp[aCount][bCount - 1] + costs[i - 1][1]); + } + } + } + + return dp[n][n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +> Where $n$ is the half of the size of the array $costs$. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def twoCitySchedCost(self, costs: List[List[int]]) -> int: + n = len(costs) // 2 + dp = [0] * (n + 1) + + for aCount in range(n + 1): + for bCount in range(n + 1): + i = aCount + bCount + if i == 0: + continue + + tmp = dp[bCount] + dp[bCount] = float("inf") + if aCount > 0: + dp[bCount] = min(dp[bCount], tmp + costs[i - 1][0]) + if bCount > 0: + dp[bCount] = min(dp[bCount], dp[bCount - 1] + costs[i - 1][1]) + + return dp[n] +``` + +```java +public class Solution { + public int twoCitySchedCost(int[][] costs) { + int n = costs.length / 2; + int[] dp = new int[n + 1]; + + for (int aCount = 0; aCount <= n; aCount++) { + for (int bCount = 0; bCount <= n; bCount++) { + int i = aCount + bCount; + if (i == 0) continue; + + int tmp = dp[bCount]; + dp[bCount] = Integer.MAX_VALUE; + if (aCount > 0) { + dp[bCount] = Math.min(dp[bCount], tmp + costs[i - 1][0]); + } + if (bCount > 0) { + dp[bCount] = Math.min(dp[bCount], dp[bCount - 1] + costs[i - 1][1]); + } + } + } + + return dp[n]; + } +} +``` + +```cpp +class Solution { +public: + int twoCitySchedCost(vector>& costs) { + int n = costs.size() / 2; + vector dp(n + 1, 0); + + for (int aCount = 0; aCount <= n; aCount++) { + for (int bCount = 0; bCount <= n; bCount++) { + int i = aCount + bCount; + if (i == 0) continue; + + int tmp = dp[bCount]; + dp[bCount] = INT_MAX; + if (aCount > 0) { + dp[bCount] = min(dp[bCount], tmp + costs[i - 1][0]); + } + if (bCount > 0) { + dp[bCount] = min(dp[bCount], dp[bCount - 1] + costs[i - 1][1]); + } + } + } + + return dp[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + twoCitySchedCost(costs) { + let n = costs.length / 2; + let dp = new Array(n + 1).fill(0); + + for (let aCount = 0; aCount <= n; aCount++) { + for (let bCount = 0; bCount <= n; bCount++) { + let i = aCount + bCount; + if (i === 0) continue; + + let tmp = dp[bCount]; + dp[bCount] = Infinity; + if (aCount > 0) { + dp[bCount] = Math.min(dp[bCount], tmp + costs[i - 1][0]); + } + if (bCount > 0) { + dp[bCount] = Math.min(dp[bCount], dp[bCount - 1] + costs[i - 1][1]); + } + } + } + + return dp[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +> Where $n$ is the half of the size of the array $costs$. + +--- + +## 5. Greedy + +::tabs-start + +```python +class Solution: + def twoCitySchedCost(self, costs: List[List[int]]) -> int: + diffs = [] + for c1, c2 in costs: + diffs.append([c2 - c1, c1, c2]) + + diffs.sort() + res = 0 + for i in range(len(diffs)): + if i < len(diffs) // 2: + res += diffs[i][2] + else: + res += diffs[i][1] + + return res +``` + +```java +public class Solution { + public int twoCitySchedCost(int[][] costs) { + List diffs = new ArrayList<>(); + for (int[] cost : costs) { + diffs.add(new int[]{cost[1] - cost[0], cost[0], cost[1]}); + } + + diffs.sort(Comparator.comparingInt(a -> a[0])); + + int res = 0; + for (int i = 0; i < diffs.size(); i++) { + if (i < diffs.size() / 2) { + res += diffs.get(i)[2]; + } else { + res += diffs.get(i)[1]; + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int twoCitySchedCost(vector>& costs) { + vector> diffs; + for (auto& cost : costs) { + diffs.push_back({cost[1] - cost[0], cost[0], cost[1]}); + } + + sort(diffs.begin(), diffs.end()); + + int res = 0; + for (int i = 0; i < diffs.size(); i++) { + if (i < diffs.size() / 2) { + res += diffs[i][2]; + } else { + res += diffs[i][1]; + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + twoCitySchedCost(costs) { + let diffs = []; + for (let cost of costs) { + diffs.push([cost[1] - cost[0], cost[0], cost[1]]); + } + + diffs.sort((a, b) => a[0] - b[0]); + + let res = 0; + for (let i = 0; i < diffs.length; i++) { + if (i < diffs.length / 2) { + res += diffs[i][2]; + } else { + res += diffs[i][1]; + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 6. Greedy (Optimal) + +::tabs-start + +```python +class Solution: + def twoCitySchedCost(self, costs: List[List[int]]) -> int: + costs.sort(key=lambda x: x[1] - x[0]) + n, res = len(costs) // 2, 0 + + for i in range(n): + res += costs[i][1] + costs[i + n][0] + return res +``` + +```java +public class Solution { + public int twoCitySchedCost(int[][] costs) { + Arrays.sort(costs, (a, b) -> Integer.compare(a[1] - a[0], b[1] - b[0])); + int n = costs.length / 2, res = 0; + + for (int i = 0; i < n; i++) { + res += costs[i][1] + costs[i + n][0]; + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int twoCitySchedCost(vector>& costs) { + sort(costs.begin(), costs.end(), [](const auto& a, const auto& b) { + return (a[1] - a[0]) < (b[1] - b[0]); + }); + + int n = costs.size() / 2, res = 0; + for (int i = 0; i < n; i++) { + res += costs[i][1] + costs[i + n][0]; + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} costs + * @return {number} + */ + twoCitySchedCost(costs) { + costs.sort((a, b) => (a[1] - a[0]) - (b[1] - b[0])); + let n = costs.length / 2, res = 0; + + for (let i = 0; i < n; i++) { + res += costs[i][1] + costs[i + n][0]; + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file diff --git a/articles/two-integer-sum-ii.md b/articles/two-integer-sum-ii.md new file mode 100644 index 000000000..f63e71a60 --- /dev/null +++ b/articles/two-integer-sum-ii.md @@ -0,0 +1,638 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def twoSum(self, numbers: List[int], target: int) -> List[int]: + for i in range(len(numbers)): + for j in range(i + 1, len(numbers)): + if numbers[i] + numbers[j] == target: + return [i + 1, j + 1] + return [] +``` + +```java +public class Solution { + public int[] twoSum(int[] numbers, int target) { + for (int i = 0; i < numbers.length; i++) { + for (int j = i + 1; j < numbers.length; j++) { + if (numbers[i] + numbers[j] == target) { + return new int[] { i + 1, j + 1 }; + } + } + } + return new int[0]; + } +} +``` + +```cpp +class Solution { +public: + vector twoSum(vector& numbers, int target) { + for (int i = 0; i < numbers.size(); i++) { + for (int j = i + 1; j < numbers.size(); j++) { + if (numbers[i] + numbers[j] == target) { + return { i + 1, j + 1 }; + } + } + } + return {}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} numbers + * @param {number} target + * @return {number[]} + */ + twoSum(numbers, target) { + for (let i = 0; i < numbers.length; i++) { + for (let j = i + 1; j < numbers.length; j++) { + if (numbers[i] + numbers[j] === target) { + return [i + 1, j + 1]; + } + } + } + return []; + } +} +``` + +```csharp +public class Solution { + public int[] TwoSum(int[] numbers, int target) { + for (int i = 0; i < numbers.Length; i++) { + for (int j = i + 1; j < numbers.Length; j++) { + if (numbers[i] + numbers[j] == target) { + return new int[] { i + 1, j + 1 }; + } + } + } + return new int[0]; + } +} +``` + +```go +func twoSum(numbers []int, target int) []int { + for i := 0; i < len(numbers); i++ { + for j := i + 1; j < len(numbers); j++ { + if numbers[i]+numbers[j] == target { + return []int{i + 1, j + 1} + } + } + } + return []int{} +} +``` + +```kotlin +class Solution { + fun twoSum(numbers: IntArray, target: Int): IntArray { + for (i in numbers.indices) { + for (j in i + 1 until numbers.size) { + if (numbers[i] + numbers[j] == target) { + return intArrayOf(i + 1, j + 1) + } + } + } + return intArrayOf() + } +} +``` + +```swift +class Solution { + func twoSum(_ numbers: [Int], _ target: Int) -> [Int] { + for i in 0.. List[int]: + for i in range(len(numbers)): + l, r = i + 1, len(numbers) - 1 + tmp = target - numbers[i] + while l <= r: + mid = l + (r - l)//2 + if numbers[mid] == tmp: + return [i + 1, mid + 1] + elif numbers[mid] < tmp: + l = mid + 1 + else: + r = mid - 1 + return [] +``` + +```java +public class Solution { + public int[] twoSum(int[] numbers, int target) { + for (int i = 0; i < numbers.length; i++) { + int l = i + 1, r = numbers.length - 1; + int tmp = target - numbers[i]; + while (l <= r) { + int mid = l + (r - l) / 2; + if (numbers[mid] == tmp) { + return new int[] { i + 1, mid + 1 }; + } else if (numbers[mid] < tmp) { + l = mid + 1; + } else { + r = mid - 1; + } + } + } + return new int[0]; + } +} +``` + +```cpp +class Solution { +public: + vector twoSum(vector& numbers, int target) { + for (int i = 0; i < numbers.size(); i++) { + int l = i + 1, r = numbers.size() - 1; + int tmp = target - numbers[i]; + while (l <= r) { + int mid = l + (r - l) / 2; + if (numbers[mid] == tmp) { + return { i + 1, mid + 1 }; + } else if (numbers[mid] < tmp) { + l = mid + 1; + } else { + r = mid - 1; + } + } + } + return {}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} numbers + * @param {number} target + * @return {number[]} + */ + twoSum(numbers, target) { + for (let i = 0; i < numbers.length; i++) { + let l = i + 1, r = numbers.length - 1; + let tmp = target - numbers[i]; + while (l <= r) { + let mid = l + Math.floor((r - l) / 2); + if (numbers[mid] === tmp) { + return [i + 1, mid + 1]; + } else if (numbers[mid] < tmp) { + l = mid + 1; + } else { + r = mid - 1; + } + } + } + return []; + } +} +``` + +```csharp +public class Solution { + public int[] TwoSum(int[] numbers, int target) { + for (int i = 0; i < numbers.Length; i++) { + int l = i + 1, r = numbers.Length - 1; + int tmp = target - numbers[i]; + while (l <= r) { + int mid = l + (r - l) / 2; + if (numbers[mid] == tmp) { + return new int[] { i + 1, mid + 1 }; + } else if (numbers[mid] < tmp) { + l = mid + 1; + } else { + r = mid - 1; + } + } + } + return new int[0]; + } +} +``` + +```go +func twoSum(numbers []int, target int) []int { + for i := 0; i < len(numbers); i++ { + l, r := i+1, len(numbers)-1 + tmp := target - numbers[i] + for l <= r { + mid := l + (r-l)/2 + if numbers[mid] == tmp { + return []int{i + 1, mid + 1} + } else if numbers[mid] < tmp { + l = mid + 1 + } else { + r = mid - 1 + } + } + } + return []int{} +} +``` + +```kotlin +class Solution { + fun twoSum(numbers: IntArray, target: Int): IntArray { + for (i in numbers.indices) { + var l = i + 1 + var r = numbers.size - 1 + val tmp = target - numbers[i] + while (l <= r) { + val mid = l + (r - l) / 2 + when { + numbers[mid] == tmp -> return intArrayOf(i + 1, mid + 1) + numbers[mid] < tmp -> l = mid + 1 + else -> r = mid - 1 + } + } + } + return intArrayOf() + } +} +``` + +```swift +class Solution { + func twoSum(_ numbers: [Int], _ target: Int) -> [Int] { + for i in 0.. List[int]: + mp = defaultdict(int) + for i in range(len(numbers)): + tmp = target - numbers[i] + if mp[tmp]: + return [mp[tmp], i + 1] + mp[numbers[i]] = i + 1 + return [] +``` + +```java +public class Solution { + public int[] twoSum(int[] numbers, int target) { + Map mp = new HashMap<>(); + for (int i = 0; i < numbers.length; i++) { + int tmp = target - numbers[i]; + if (mp.containsKey(tmp)) { + return new int[] { mp.get(tmp), i + 1 }; + } + mp.put(numbers[i], i + 1); + } + return new int[0]; + } +} +``` + +```cpp +class Solution { +public: + vector twoSum(vector& numbers, int target) { + unordered_map mp; + for (int i = 0; i < numbers.size(); i++) { + int tmp = target - numbers[i]; + if (mp.count(tmp)) { + return { mp[tmp], i + 1 }; + } + mp[numbers[i]] = i + 1; + } + return {}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} numbers + * @param {number} target + * @return {number[]} + */ + twoSum(numbers, target) { + const mp = new Map(); + for (let i = 0; i < numbers.length; i++) { + const tmp = target - numbers[i]; + if (mp.has(tmp)) { + return [mp.get(tmp), i + 1]; + } + mp.set(numbers[i], i + 1); + } + return []; + } +} +``` + +```csharp +public class Solution { + public int[] TwoSum(int[] numbers, int target) { + Dictionary mp = new Dictionary(); + for (int i = 0; i < numbers.Length; i++) { + int tmp = target - numbers[i]; + if (mp.ContainsKey(tmp)) { + return new int[] { mp[tmp], i + 1 }; + } + mp[numbers[i]] = i + 1; + } + return new int[0]; + } +} +``` + +```go +func twoSum(numbers []int, target int) []int { + mp := make(map[int]int) + for i := 0; i < len(numbers); i++ { + tmp := target - numbers[i] + if val, exists := mp[tmp]; exists { + return []int{val, i + 1} + } + mp[numbers[i]] = i + 1 + } + return []int{} +} +``` + +```kotlin +class Solution { + fun twoSum(numbers: IntArray, target: Int): IntArray { + val mp = HashMap() + for (i in numbers.indices) { + val tmp = target - numbers[i] + if (mp.containsKey(tmp)) { + return intArrayOf(mp[tmp]!!, i + 1) + } + mp[numbers[i]] = i + 1 + } + return intArrayOf() + } +} +``` + +```swift +class Solution { + func twoSum(_ numbers: [Int], _ target: Int) -> [Int] { + var mp = [Int: Int]() + + for i in 0.. List[int]: + l, r = 0, len(numbers) - 1 + + while l < r: + curSum = numbers[l] + numbers[r] + + if curSum > target: + r -= 1 + elif curSum < target: + l += 1 + else: + return [l + 1, r + 1] + return [] +``` + +```java +public class Solution { + public int[] twoSum(int[] numbers, int target) { + int l = 0, r = numbers.length - 1; + + while (l < r) { + int curSum = numbers[l] + numbers[r]; + + if (curSum > target) { + r--; + } else if (curSum < target) { + l++; + } else { + return new int[] { l + 1, r + 1 }; + } + } + return new int[0]; + } +} +``` + +```cpp +class Solution { +public: + vector twoSum(vector& numbers, int target) { + int l = 0, r = numbers.size() - 1; + + while (l < r) { + int curSum = numbers[l] + numbers[r]; + + if (curSum > target) { + r--; + } else if (curSum < target) { + l++; + } else { + return { l + 1, r + 1 }; + } + } + return {}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} numbers + * @param {number} target + * @return {number[]} + */ + twoSum(numbers, target) { + let l = 0, r = numbers.length - 1; + + while (l < r) { + const curSum = numbers[l] + numbers[r]; + + if (curSum > target) { + r--; + } else if (curSum < target) { + l++; + } else { + return [l + 1, r + 1]; + } + } + return []; + } +} +``` + +```csharp +public class Solution { + public int[] TwoSum(int[] numbers, int target) { + int l = 0, r = numbers.Length - 1; + + while (l < r) { + int curSum = numbers[l] + numbers[r]; + + if (curSum > target) { + r--; + } else if (curSum < target) { + l++; + } else { + return new int[] { l + 1, r + 1 }; + } + } + return new int[0]; + } +} +``` + +```go +func twoSum(numbers []int, target int) []int { + l, r := 0, len(numbers) - 1 + + for l < r { + curSum := numbers[l] + numbers[r] + if curSum > target { + r-- + } else if curSum < target { + l++ + } else { + return []int{l + 1, r + 1} + } + } + return []int{} +} +``` + +```kotlin +class Solution { + fun twoSum(numbers: IntArray, target: Int): IntArray { + var l = 0 + var r = numbers.size - 1 + + while (l < r) { + val curSum = numbers[l] + numbers[r] + when { + curSum > target -> r-- + curSum < target -> l++ + else -> return intArrayOf(l + 1, r + 1) + } + } + return intArrayOf() + } +} +``` + +```swift +class Solution { + func twoSum(_ numbers: [Int], _ target: Int) -> [Int] { + var l = 0, r = numbers.count - 1 + + while l < r { + let curSum = numbers[l] + numbers[r] + + if curSum > target { + r -= 1 + } else if curSum < target { + l += 1 + } else { + return [l + 1, r + 1] + } + } + return [] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/two-integer-sum.md b/articles/two-integer-sum.md new file mode 100644 index 000000000..9fba0afdc --- /dev/null +++ b/articles/two-integer-sum.md @@ -0,0 +1,697 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def twoSum(self, nums: List[int], target: int) -> List[int]: + for i in range(len(nums)): + for j in range(i + 1, len(nums)): + if nums[i] + nums[j] == target: + return [i, j] + return [] +``` + +```java +public class Solution { + public int[] twoSum(int[] nums, int target) { + for (int i = 0; i < nums.length; i++) { + for (int j = i + 1; j < nums.length; j++) { + if (nums[i] + nums[j] == target) { + return new int[]{i, j}; + } + } + } + return new int[0]; + } +} +``` + +```cpp +class Solution { +public: + vector twoSum(vector& nums, int target) { + for (int i = 0; i < nums.size(); i++) { + for (int j = i + 1; j < nums.size(); j++) { + if (nums[i] + nums[j] == target) { + return {i, j}; + } + } + } + return {}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ + twoSum(nums, target) { + for (let i = 0; i < nums.length; i++) { + for (let j = i + 1; j < nums.length; j++) { + if (nums[i] + nums[j] === target) { + return [i, j]; + } + } + } + return []; + } +} +``` + +```csharp +public class Solution { + public int[] TwoSum(int[] nums, int target) { + for (int i = 0; i < nums.Length; i++) { + for (int j = i + 1; j < nums.Length; j++) { + if (nums[i] + nums[j] == target) { + return new int[]{i, j}; + } + } + } + return new int[0]; + } +} +``` + +```go +func twoSum(nums []int, target int) []int { + for i := 0; i < len(nums); i++ { + for j := i + 1; j < len(nums); j++ { + if nums[i] + nums[j] == target { + return []int{i, j} + } + } + } + return []int{} +} +``` + +```kotlin +class Solution { + fun twoSum(nums: IntArray, target: Int): IntArray { + for (i in nums.indices) { + for (j in i + 1 until nums.size) { + if (nums[i] + nums[j] == target) { + return intArrayOf(i, j) + } + } + } + return intArrayOf() + } +} +``` + +```swift +class Solution { + func twoSum(_ nums: [Int], _ target: Int) -> [Int] { + for i in 0.. List[int]: + A = [] + for i, num in enumerate(nums): + A.append([num, i]) + + A.sort() + i, j = 0, len(nums) - 1 + while i < j: + cur = A[i][0] + A[j][0] + if cur == target: + return [min(A[i][1], A[j][1]), + max(A[i][1], A[j][1])] + elif cur < target: + i += 1 + else: + j -= 1 + return [] +``` + +```java +public class Solution { + public int[] twoSum(int[] nums, int target) { + int[][] A = new int[nums.length][2]; + for (int i = 0; i < nums.length; i++) { + A[i][0] = nums[i]; + A[i][1] = i; + } + + Arrays.sort(A, Comparator.comparingInt(a -> a[0])); + + int i = 0, j = nums.length - 1; + while (i < j) { + int cur = A[i][0] + A[j][0]; + if (cur == target) { + return new int[]{Math.min(A[i][1], A[j][1]), + Math.max(A[i][1], A[j][1])}; + } else if (cur < target) { + i++; + } else { + j--; + } + } + return new int[0]; + } +} +``` + +```cpp +class Solution { +public: + vector twoSum(vector& nums, int target) { + vector> A; + for (int i = 0; i < nums.size(); i++) { + A.push_back({nums[i], i}); + } + + sort(A.begin(), A.end()); + + int i = 0, j = nums.size() - 1; + while (i < j) { + int cur = A[i].first + A[j].first; + if (cur == target) { + return {min(A[i].second, A[j].second), + max(A[i].second, A[j].second)}; + } else if (cur < target) { + i++; + } else { + j--; + } + } + return {}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ + twoSum(nums, target) { + let A = []; + for (let i = 0; i < nums.length; i++) { + A.push([nums[i], i]); + } + + A.sort((a, b) => a[0] - b[0]); + + let i = 0, j = nums.length - 1; + while (i < j) { + let cur = A[i][0] + A[j][0]; + if (cur === target) { + return [Math.min(A[i][1], A[j][1]), + Math.max(A[i][1], A[j][1])]; + } else if (cur < target) { + i++; + } else { + j--; + } + } + return []; + } +} +``` + +```csharp +public class Solution { + public int[] TwoSum(int[] nums, int target) { + List A = new List(); + for (int idx = 0; idx < nums.Length; idx++) { + A.Add(new int[]{nums[idx], idx}); + } + + A.Sort((a, b) => a[0].CompareTo(b[0])); + + int i = 0, j = nums.Length - 1; + while (i < j) { + int cur = A[i][0] + A[j][0]; + if (cur == target) { + return new int[]{ + Math.Min(A[i][1], A[j][1]), + Math.Max(A[i][1], A[j][1]) + }; + } else if (cur < target) { + i++; + } else { + j--; + } + } + return new int[0]; + } +} +``` + +```go +func twoSum(nums []int, target int) []int { + A := make([][2]int, len(nums)) + for i, num := range nums { + A[i] = [2]int{num, i} + } + + sort.Slice(A, func(i, j int) bool { + return A[i][0] < A[j][0] + }) + + i, j := 0, len(nums)-1 + for i < j { + cur := A[i][0] + A[j][0] + if cur == target { + if A[i][1] < A[j][1] { + return []int{A[i][1], A[j][1]} + } else { + return []int{A[j][1], A[i][1]} + } + } else if cur < target { + i++ + } else { + j-- + } + } + return []int{} +} +``` + +```kotlin +class Solution { + fun twoSum(nums: IntArray, target: Int): IntArray { + val A = nums.mapIndexed { index, num -> num to index }.toMutableList() + A.sortBy { it.first } + + var i = 0 + var j = nums.size - 1 + while (i < j) { + val cur = A[i].first + A[j].first + if (cur == target) { + return intArrayOf( + minOf(A[i].second, A[j].second), + maxOf(A[i].second, A[j].second) + ) + } else if (cur < target) { + i++ + } else { + j-- + } + } + return intArrayOf() + } +} +``` + +```swift +class Solution { + func twoSum(_ nums: [Int], _ target: Int) -> [Int] { + var A = [(Int, Int)]() + for (i, num) in nums.enumerated() { + A.append((num, i)) + } + + A.sort { $0.0 < $1.0 } + var i = 0 + var j = nums.count - 1 + + while i < j { + let cur = A[i].0 + A[j].0 + if cur == target { + return [min(A[i].1, A[j].1), + max(A[i].1, A[j].1)] + } else if cur < target { + i += 1 + } else { + j -= 1 + } + } + return [] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Hash Map (Two Pass) + +::tabs-start + +```python +class Solution: + def twoSum(self, nums: List[int], target: int) -> List[int]: + indices = {} # val -> index + + for i, n in enumerate(nums): + indices[n] = i + + for i, n in enumerate(nums): + diff = target - n + if diff in indices and indices[diff] != i: + return [i, indices[diff]] +``` + +```java +public class Solution { + public int[] twoSum(int[] nums, int target) { + Map indices = new HashMap<>(); // val -> index + + for (int i = 0; i < nums.length; i++) { + indices.put(nums[i], i); + } + + for (int i = 0; i < nums.length; i++) { + int diff = target - nums[i]; + if (indices.containsKey(diff) && indices.get(diff) != i) { + return new int[]{i, indices.get(diff)}; + } + } + + return new int[0]; + } +} +``` + +```cpp +class Solution { +public: + vector twoSum(vector& nums, int target) { + unordered_map indices; // val -> index + + for (int i = 0; i < nums.size(); i++) { + indices[nums[i]] = i; + } + + for (int i = 0; i < nums.size(); i++) { + int diff = target - nums[i]; + if (indices.count(diff) && indices[diff] != i) { + return {i, indices[diff]}; + } + } + + return {}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ + twoSum(nums, target) { + const indices = {}; // val -> index + + for (let i = 0; i < nums.length; i++) { + indices[nums[i]] = i; + } + + for (let i = 0; i < nums.length; i++) { + let diff = target - nums[i]; + if (indices[diff] !== undefined && indices[diff] !== i) { + return [i, indices[diff]]; + } + } + + return []; + } +} +``` + +```csharp +public class Solution { + public int[] TwoSum(int[] nums, int target) { + // val -> index + Dictionary indices = new Dictionary(); + + for (int i = 0; i < nums.Length; i++) { + indices[nums[i]] = i; + } + + for (int i = 0; i < nums.Length; i++) { + int diff = target - nums[i]; + if (indices.ContainsKey(diff) && indices[diff] != i) { + return new int[]{i, indices[diff]}; + } + } + + return new int[0]; + } +} +``` + +```go +func twoSum(nums []int, target int) []int { + indices := make(map[int]int) + + for i, n := range nums { + indices[n] = i + } + + for i, n := range nums { + diff := target - n + if j, found := indices[diff]; found && j != i { + return []int{i, j} + } + } + return []int{} +} +``` + +```kotlin +class Solution { + fun twoSum(nums: IntArray, target: Int): IntArray { + val indices = HashMap() + + for ((i, n) in nums.withIndex()) { + indices[n] = i + } + + for ((i, n) in nums.withIndex()) { + val diff = target - n + if (indices.containsKey(diff) && indices[diff] != i) { + return intArrayOf(i, indices[diff]!!) + } + } + return intArrayOf() + } +} +``` + +```swift +class Solution { + func twoSum(_ nums: [Int], _ target: Int) -> [Int] { + var indices = [Int: Int]() // val -> index + + for (i, n) in nums.enumerated() { + indices[n] = i + } + + for (i, n) in nums.enumerated() { + let diff = target - n + if let j = indices[diff], j != i { + return [i, j] + } + } + + return [] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Hash Map (One Pass) + +::tabs-start + +```python +class Solution: + def twoSum(self, nums: List[int], target: int) -> List[int]: + prevMap = {} # val -> index + + for i, n in enumerate(nums): + diff = target - n + if diff in prevMap: + return [prevMap[diff], i] + prevMap[n] = i +``` + +```java +public class Solution { + public int[] twoSum(int[] nums, int target) { + HashMap prevMap = new HashMap<>(); + + for (int i = 0; i < nums.length; i++) { + int num = nums[i]; + int diff = target - num; + + if (prevMap.containsKey(diff)) { + return new int[] { prevMap.get(diff), i }; + } + + prevMap.put(num, i); + } + + return new int[] {}; + } +} +``` + +```cpp +class Solution { +public: + vector twoSum(vector& nums, int target) { + int n = nums.size(); + unordered_map prevMap; + + for (int i = 0; i < n; i++) { + int diff = target - nums[i]; + if (prevMap.find(diff) != prevMap.end()) { + return {prevMap[diff], i}; + } + prevMap.insert({nums[i], i}); + } + return {}; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ + twoSum(nums, target) { + const prevMap = new Map(); + + for (let i = 0; i < nums.length; i++) { + const diff = target - nums[i]; + if (prevMap.has(diff)) { + return [prevMap.get(diff), i]; + } + + prevMap.set(nums[i], i); + } + + return []; + } +} +``` + +```csharp +public class Solution { + public int[] TwoSum(int[] nums, int target) { + Dictionary prevMap = new Dictionary(); + + for (int i = 0; i < nums.Length; i++) { + var diff = target - nums[i]; + if (prevMap.ContainsKey(diff)) { + return new int[] {prevMap[diff], i}; + } + prevMap[nums[i]] = i; + } + return null; + } +} +``` + +```go +func twoSum(nums []int, target int) []int { + prevMap := make(map[int]int) + + for i, n := range nums { + diff := target - n + if j, found := prevMap[diff]; found { + return []int{j, i} + } + prevMap[n] = i + } + return []int{} +} +``` + +```kotlin +class Solution { + fun twoSum(nums: IntArray, target: Int): IntArray { + val prevMap = HashMap() + + for ((i, n) in nums.withIndex()) { + val diff = target - n + if (prevMap.containsKey(diff)) { + return intArrayOf(prevMap[diff]!!, i) + } + prevMap[n] = i + } + return intArrayOf() + } +} +``` + +```swift +class Solution { + func twoSum(_ nums: [Int], _ target: Int) -> [Int] { + var prevMap = [Int: Int]() // val -> index + + for (i, n) in nums.enumerated() { + let diff = target - n + if let index = prevMap[diff] { + return [index, i] + } + prevMap[n] = i + } + + return [] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/ugly-number.md b/articles/ugly-number.md new file mode 100644 index 000000000..f2a68a2e5 --- /dev/null +++ b/articles/ugly-number.md @@ -0,0 +1,76 @@ +## 1. Math + +::tabs-start + +```python +class Solution: + def isUgly(self, n: int) -> bool: + if n <= 0: + return False + + for p in [2, 3, 5]: + while n % p == 0: + n //= p + + return n == 1 +``` + +```java +public class Solution { + public boolean isUgly(int n) { + if (n <= 0) return false; + + for (int p = 2; p <= 5 && n > 0; p++) { + while (n % p == 0) { + n /= p; + } + } + + return n == 1; + } +} +``` + +```cpp +class Solution { +public: + bool isUgly(int n) { + if (n <= 0) return false; + + for (int p = 2; p <= 5 && n > 0; p++) { + while (n % p == 0) { + n /= p; + } + } + + return n == 1; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {boolean} + */ + isUgly(n) { + if (n <= 0) return false; + + for (let p of [2, 3, 5]) { + while (n % p == 0) { + n /= p; + } + } + + return n === 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/uncommon-words-from-two-sentences.md b/articles/uncommon-words-from-two-sentences.md new file mode 100644 index 000000000..67be56a4b --- /dev/null +++ b/articles/uncommon-words-from-two-sentences.md @@ -0,0 +1,183 @@ +## 1. Hash Map - I + +::tabs-start + +```python +class Solution: + def uncommonFromSentences(self, s1: str, s2: str) -> List[str]: + count = defaultdict(int) + for w in s1.split(" ") + s2.split(" "): + count[w] += 1 + + res = [] + for w, cnt in count.items(): + if cnt == 1: + res.append(w) + return res +``` + +```java +public class Solution { + public String[] uncommonFromSentences(String s1, String s2) { + String[] words = (s1 + " " + s2).split(" "); + Map count = new HashMap<>(); + + for (String w : words) { + count.put(w, count.getOrDefault(w, 0) + 1); + } + + List res = new ArrayList<>(); + for (Map.Entry entry : count.entrySet()) { + if (entry.getValue() == 1) { + res.add(entry.getKey()); + } + } + + return res.toArray(new String[0]); + } +} +``` + +```cpp +class Solution { +public: + vector uncommonFromSentences(string s1, string s2) { + unordered_map count; + istringstream ss(s1 + " " + s2); + string w; + while (ss >> w) { + count[w]++; + } + + vector res; + for (auto& [word, freq] : count) { + if (freq == 1) { + res.push_back(word); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s1 + * @param {string} s2 + * @return {string[]} + */ + uncommonFromSentences(s1, s2) { + const words = (s1 + " " + s2).split(" "); + const count = new Map(); + + for (const w of words) { + count.set(w, (count.get(w) || 0) + 1); + } + + const res = []; + for (const [w, c] of count.entries()) { + if (c === 1) { + res.push(w); + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ and $m$ are the lengths of the strings $s1$ and $s2$, respectively. + +--- + +## 2. Hash Map - II + +::tabs-start + +```python +class Solution: + def uncommonFromSentences(self, s1: str, s2: str) -> List[str]: + return [w for w, cnt in Counter(s1.split(" ") + s2.split(" ")).items() if cnt == 1] +``` + +```java +public class Solution { + public String[] uncommonFromSentences(String s1, String s2) { + String[] words = (s1 + " " + s2).split(" "); + Map count = new HashMap<>(); + + for (String w : words) { + count.put(w, count.getOrDefault(w, 0) + 1); + } + + return count.entrySet() + .stream() + .filter(e -> e.getValue() == 1) + .map(Map.Entry::getKey) + .toArray(String[]::new); + } +} +``` + +```cpp +class Solution { +public: + vector uncommonFromSentences(string s1, string s2) { + unordered_map count; + istringstream ss(s1 + " " + s2); + string w; + while (ss >> w) { + count[w]++; + } + + vector res; + for (auto& [w, c] : count) { + if (c == 1) { + res.push_back(w); + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s1 + * @param {string} s2 + * @return {string[]} + */ + uncommonFromSentences(s1, s2) { + const words = (s1 + " " + s2).split(" "); + const count = new Map(); + + for (const w of words) { + count.set(w, (count.get(w) || 0) + 1); + } + + return [...count.entries()] + .filter(([_, c]) => c === 1) + .map(([w]) => w); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(n + m)$ + +> Where $n$ and $m$ are the lengths of the strings $s1$ and $s2$, respectively. \ No newline at end of file diff --git a/articles/uncrossed-lines.md b/articles/uncrossed-lines.md new file mode 100644 index 000000000..a08442f64 --- /dev/null +++ b/articles/uncrossed-lines.md @@ -0,0 +1,560 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def maxUncrossedLines(self, nums1: List[int], nums2: List[int]) -> int: + def dfs(i, j): + if i == len(nums1) or j == len(nums2): + return 0 + + if nums1[i] == nums2[j]: + return 1 + dfs(i + 1, j + 1) + return max(dfs(i, j + 1), dfs(i + 1, j)) + + return dfs(0, 0) +``` + +```java +public class Solution { + public int maxUncrossedLines(int[] nums1, int[] nums2) { + int n1 = nums1.length, n2 = nums2.length; + + return dfs(nums1, nums2, 0, 0); + } + + private int dfs(int[] nums1, int[] nums2, int i, int j) { + if (i == nums1.length || j == nums2.length) return 0; + + if (nums1[i] == nums2[j]) { + return 1 + dfs(nums1, nums2, i + 1, j + 1); + } + return Math.max(dfs(nums1, nums2, i, j + 1), dfs(nums1, nums2, i + 1, j)); + } +} +``` + +```cpp +class Solution { +public: + int dfs(vector& nums1, vector& nums2, int i, int j) { + if (i == nums1.size() || j == nums2.size()) return 0; + + if (nums1[i] == nums2[j]) { + return 1 + dfs(nums1, nums2, i + 1, j + 1); + } + return max(dfs(nums1, nums2, i, j + 1), dfs(nums1, nums2, i + 1, j)); + } + + int maxUncrossedLines(vector& nums1, vector& nums2) { + return dfs(nums1, nums2, 0, 0); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number} + */ + maxUncrossedLines(nums1, nums2) { + const dfs = (i, j) => { + if (i === nums1.length || j === nums2.length) { + return 0; + } + if (nums1[i] === nums2[j]) { + return 1 + dfs(i + 1, j + 1); + } + return Math.max(dfs(i, j + 1), dfs(i + 1, j)); + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ {n + m})$ +* Space complexity: $O(n + m)$ for recursion stack. + +> Where $n$ and $m$ are the sizes of the arrays $nums1$ and $nums2$ respectively. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def maxUncrossedLines(self, nums1: List[int], nums2: List[int]) -> int: + dp = {} + + def dfs(i, j): + if i == len(nums1) or j == len(nums2): + return 0 + + if (i, j) in dp: + return dp[(i, j)] + + if nums1[i] == nums2[j]: + dp[(i, j)] = 1 + dfs(i + 1, j + 1) + else: + dp[(i, j)] = max(dfs(i, j + 1), dfs(i + 1, j)) + + return dp[(i, j)] + + return dfs(0, 0) +``` + +```java +public class Solution { + public int maxUncrossedLines(int[] nums1, int[] nums2) { + int n = nums1.length, m = nums2.length; + int[][] dp = new int[n][m]; + + for (int[] row : dp) { + Arrays.fill(row, -1); + } + + return dfs(0, 0, nums1, nums2, dp); + } + + private int dfs(int i, int j, int[] nums1, int[] nums2, int[][] dp) { + if (i == nums1.length || j == nums2.length) { + return 0; + } + + if (dp[i][j] != -1) { + return dp[i][j]; + } + + if (nums1[i] == nums2[j]) { + dp[i][j] = 1 + dfs(i + 1, j + 1, nums1, nums2, dp); + } else { + dp[i][j] = Math.max(dfs(i, j + 1, nums1, nums2, dp), dfs(i + 1, j, nums1, nums2, dp)); + } + + return dp[i][j]; + } +} +``` + +```cpp +class Solution { +public: + int maxUncrossedLines(vector& nums1, vector& nums2) { + int n = nums1.size(), m = nums2.size(); + vector> dp(n, vector(m, -1)); + + return dfs(0, 0, nums1, nums2, dp); + } + +private: + int dfs(int i, int j, vector& nums1, vector& nums2, vector>& dp) { + if (i == nums1.size() || j == nums2.size()) { + return 0; + } + + if (dp[i][j] != -1) { + return dp[i][j]; + } + + if (nums1[i] == nums2[j]) { + dp[i][j] = 1 + dfs(i + 1, j + 1, nums1, nums2, dp); + } else { + dp[i][j] = max(dfs(i, j + 1, nums1, nums2, dp), dfs(i + 1, j, nums1, nums2, dp)); + } + + return dp[i][j]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number} + */ + maxUncrossedLines(nums1, nums2) { + const n = nums1.length, m = nums2.length; + const dp = Array.from({ length: n }, () => Array(m).fill(-1)); + + const dfs = (i, j) => { + if (i === n || j === m) { + return 0; + } + + if (dp[i][j] !== -1) { + return dp[i][j]; + } + + if (nums1[i] === nums2[j]) { + dp[i][j] = 1 + dfs(i + 1, j + 1); + } else { + dp[i][j] = Math.max(dfs(i, j + 1), dfs(i + 1, j)); + } + + return dp[i][j]; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ and $m$ are the sizes of the arrays $nums1$ and $nums2$ respectively. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def maxUncrossedLines(self, nums1: List[int], nums2: List[int]) -> int: + n, m = len(nums1), len(nums2) + dp = [[0] * (m + 1) for _ in range(n + 1)] + + for i in range(n): + for j in range(m): + if nums1[i] == nums2[j]: + dp[i + 1][j + 1] = 1 + dp[i][j] + else: + dp[i + 1][j + 1] = max(dp[i][j + 1], dp[i + 1][j]) + + return dp[n][m] +``` + +```java +public class Solution { + public int maxUncrossedLines(int[] nums1, int[] nums2) { + int n = nums1.length, m = nums2.length; + int[][] dp = new int[n + 1][m + 1]; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + if (nums1[i] == nums2[j]) { + dp[i + 1][j + 1] = 1 + dp[i][j]; + } else { + dp[i + 1][j + 1] = Math.max(dp[i][j + 1], dp[i + 1][j]); + } + } + } + + return dp[n][m]; + } +} +``` + +```cpp +class Solution { +public: + int maxUncrossedLines(vector& nums1, vector& nums2) { + int n = nums1.size(), m = nums2.size(); + vector> dp(n + 1, vector(m + 1, 0)); + + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + if (nums1[i] == nums2[j]) { + dp[i + 1][j + 1] = 1 + dp[i][j]; + } else { + dp[i + 1][j + 1] = max(dp[i][j + 1], dp[i + 1][j]); + } + } + } + + return dp[n][m]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number} + */ + maxUncrossedLines(nums1, nums2) { + const n = nums1.length, m = nums2.length; + const dp = Array.from({ length: n + 1 }, () => Array(m + 1).fill(0)); + + for (let i = 0; i < n; i++) { + for (let j = 0; j < m; j++) { + if (nums1[i] === nums2[j]) { + dp[i + 1][j + 1] = 1 + dp[i][j]; + } else { + dp[i + 1][j + 1] = Math.max(dp[i][j + 1], dp[i + 1][j]); + } + } + } + + return dp[n][m]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n * m)$ + +> Where $n$ and $m$ are the sizes of the arrays $nums1$ and $nums2$ respectively. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def maxUncrossedLines(self, nums1: List[int], nums2: List[int]) -> int: + prev = [0] * (len(nums2) + 1) + + for i in range(len(nums1)): + dp = [0] * (len(nums2) + 1) + for j in range(len(nums2)): + if nums1[i] == nums2[j]: + dp[j + 1] = 1 + prev[j] + else: + dp[j + 1] = max(dp[j], prev[j + 1]) + prev = dp + + return prev[len(nums2)] +``` + +```java +public class Solution { + public int maxUncrossedLines(int[] nums1, int[] nums2) { + int[] prev = new int[nums2.length + 1]; + + for (int i = 0; i < nums1.length; i++) { + int[] dp = new int[nums2.length + 1]; + for (int j = 0; j < nums2.length; j++) { + if (nums1[i] == nums2[j]) { + dp[j + 1] = 1 + prev[j]; + } else { + dp[j + 1] = Math.max(dp[j], prev[j + 1]); + } + } + prev = dp; + } + + return prev[nums2.length]; + } +} +``` + +```cpp +class Solution { +public: + int maxUncrossedLines(vector& nums1, vector& nums2) { + vector prev(nums2.size() + 1, 0); + + for (int i = 0; i < nums1.size(); i++) { + vector dp(nums2.size() + 1, 0); + for (int j = 0; j < nums2.size(); j++) { + if (nums1[i] == nums2[j]) { + dp[j + 1] = 1 + prev[j]; + } else { + dp[j + 1] = max(dp[j], prev[j + 1]); + } + } + prev = dp; + } + + return prev[nums2.size()]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number} + */ + maxUncrossedLines(nums1, nums2) { + let prev = new Array(nums2.length + 1).fill(0); + + for (let i = 0; i < nums1.length; i++) { + const dp = new Array(nums2.length + 1).fill(0); + for (let j = 0; j < nums2.length; j++) { + if (nums1[i] === nums2[j]) { + dp[j + 1] = 1 + prev[j]; + } else { + dp[j + 1] = Math.max(dp[j], prev[j + 1]); + } + } + prev = dp; + } + + return prev[nums2.length]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(m)$ + +> Where $n$ and $m$ are the sizes of the arrays $nums1$ and $nums2$ respectively. + +--- + +## 5. Dynamic Programming (Optimal) + +::tabs-start + +```python +class Solution: + def maxUncrossedLines(self, nums1: List[int], nums2: List[int]) -> int: + n, m = len(nums1), len(nums2) + if m > n: + n, m = m, n + nums1, nums2 = nums2, nums1 + + dp = [0] * (m + 1) + + for i in range(n): + prev = 0 + for j in range(m): + temp = dp[j + 1] + if nums1[i] == nums2[j]: + dp[j + 1] = 1 + prev + else: + dp[j + 1] = max(dp[j + 1], dp[j]) + prev = temp + + return dp[m] +``` + +```java +public class Solution { + public int maxUncrossedLines(int[] nums1, int[] nums2) { + int n = nums1.length, m = nums2.length; + if (m > n) { + int[] tempArr = nums1; + nums1 = nums2; + nums2 = tempArr; + int temp = n; + n = m; + m = temp; + } + + int[] dp = new int[m + 1]; + + for (int i = 0; i < n; i++) { + int prev = 0; + for (int j = 0; j < m; j++) { + int temp = dp[j + 1]; + if (nums1[i] == nums2[j]) { + dp[j + 1] = 1 + prev; + } else { + dp[j + 1] = Math.max(dp[j + 1], dp[j]); + } + prev = temp; + } + } + + return dp[m]; + } +} +``` + +```cpp +class Solution { +public: + int maxUncrossedLines(vector& nums1, vector& nums2) { + int n = nums1.size(), m = nums2.size(); + if (m > n) { + swap(nums1, nums2); + swap(n, m); + } + + vector dp(m + 1, 0); + + for (int i = 0; i < n; i++) { + int prev = 0; + for (int j = 0; j < m; j++) { + int temp = dp[j + 1]; + if (nums1[i] == nums2[j]) { + dp[j + 1] = 1 + prev; + } else { + dp[j + 1] = max(dp[j + 1], dp[j]); + } + prev = temp; + } + } + + return dp[m]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number} + */ + maxUncrossedLines(nums1, nums2) { + let n = nums1.length, m = nums2.length; + if (m > n) { + [nums1, nums2] = [nums2, nums1]; + [n, m] = [m, n]; + } + + const dp = Array(m + 1).fill(0); + + for (let i = 0; i < n; i++) { + let prev = 0; + for (let j = 0; j < m; j++) { + const temp = dp[j + 1]; + if (nums1[i] === nums2[j]) { + dp[j + 1] = 1 + prev; + } else { + dp[j + 1] = Math.max(dp[j + 1], dp[j]); + } + prev = temp; + } + } + + return dp[m]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(min(n, m))$ + +> Where $n$ and $m$ are the sizes of the arrays $nums1$ and $nums2$ respectively. \ No newline at end of file diff --git a/articles/unique-binary-search-trees-ii.md b/articles/unique-binary-search-trees-ii.md new file mode 100644 index 000000000..3c3a4ccee --- /dev/null +++ b/articles/unique-binary-search-trees-ii.md @@ -0,0 +1,670 @@ +## 1. Recursion + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def generateTrees(self, n: int) -> List[Optional[TreeNode]]: + def generate(left, right): + if left > right: + return [None] + + res = [] + for val in range(left, right + 1): + for leftTree in generate(left, val - 1): + for rightTree in generate(val + 1, right): + root = TreeNode(val, leftTree, rightTree) + res.append(root) + return res + + return generate(1, n) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List generateTrees(int n) { + return generate(1, n); + } + + private List generate(int left, int right) { + List res = new ArrayList<>(); + if (left > right) { + res.add(null); + return res; + } + + for (int val = left; val <= right; val++) { + List leftTrees = generate(left, val - 1); + List rightTrees = generate(val + 1, right); + + for (TreeNode leftTree : leftTrees) { + for (TreeNode rightTree : rightTrees) { + res.add(new TreeNode(val, leftTree, rightTree)); + } + } + } + return res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector generateTrees(int n) { + return generate(1, n); + } + +private: + vector generate(int left, int right) { + if (left > right) return {nullptr}; + + vector res; + for (int val = left; val <= right; val++) { + vector leftTrees = generate(left, val - 1); + vector rightTrees = generate(val + 1, right); + + for (auto& leftTree : leftTrees) { + for (auto& rightTree : rightTrees) { + res.push_back(new TreeNode(val, leftTree, rightTree)); + } + } + } + return res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number} n + * @return {TreeNode[]} + */ + generateTrees(n) { + const generate = (left, right) => { + if (left > right) return [null]; + + let res = []; + for (let val = left; val <= right; val++) { + let leftTrees = generate(left, val - 1); + let rightTrees = generate(val + 1, right); + + for (let leftTree of leftTrees) { + for (let rightTree of rightTrees) { + res.push(new TreeNode(val, leftTree, rightTree)); + } + } + } + return res; + }; + + return generate(1, n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\frac {4 ^ n}{\sqrt {n}})$ +* Space complexity: + * $O(n)$ space for recursion stack. + * $O(\frac {4 ^ n}{\sqrt {n}})$ space for the output. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def generateTrees(self, n: int) -> List[Optional[TreeNode]]: + dp = {} + + def generate(left, right): + if left > right: + return [None] + if (left, right) in dp: + return dp[(left, right)] + + res = [] + for val in range(left, right + 1): + for leftTree in generate(left, val - 1): + for rightTree in generate(val + 1, right): + root = TreeNode(val, leftTree, rightTree) + res.append(root) + + dp[(left, right)] = res + return res + + return generate(1, n) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private List[][] dp; + + public List generateTrees(int n) { + dp = new ArrayList[n + 1][n + 1]; + return generate(1, n); + } + + private List generate(int left, int right) { + if (left > right) return Collections.singletonList(null); + if (dp[left][right] != null) return dp[left][right]; + + List res = new ArrayList<>(); + for (int val = left; val <= right; val++) { + for (TreeNode leftTree : generate(left, val - 1)) { + for (TreeNode rightTree : generate(val + 1, right)) { + res.add(new TreeNode(val, leftTree, rightTree)); + } + } + } + return dp[left][right] = res; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector>> dp; + + vector generateTrees(int n) { + dp.resize(n + 1, vector>(n + 1)); + return generate(1, n); + } + +private: + vector generate(int left, int right) { + if (left > right) return {nullptr}; + if (!dp[left][right].empty()) return dp[left][right]; + + vector res; + for (int val = left; val <= right; val++) { + for (auto& leftTree : generate(left, val - 1)) { + for (auto& rightTree : generate(val + 1, right)) { + res.push_back(new TreeNode(val, leftTree, rightTree)); + } + } + } + return dp[left][right] = res; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number} n + * @return {TreeNode[]} + */ + generateTrees(n) { + const dp = Array.from({ length: n + 1 }, () => Array(n + 1).fill(null)); + + const generate = (left, right) => { + if (left > right) return [null]; + if (dp[left][right]) return dp[left][right]; + + let res = []; + for (let val = left; val <= right; val++) { + for (let leftTree of generate(left, val - 1)) { + for (let rightTree of generate(val + 1, right)) { + res.push(new TreeNode(val, leftTree, rightTree)); + } + } + } + return (dp[left][right] = res); + }; + + return generate(1, n); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\frac {4 ^ n}{\sqrt {n}})$ +* Space complexity: + * $O(n)$ space for recursion stack. + * $O(n ^ 2)$ extra space. + * $O(\frac {4 ^ n}{\sqrt {n}})$ space for the output. + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def generateTrees(self, n: int) -> List[Optional[TreeNode]]: + dp = [[[] for _ in range(n + 2)] for _ in range(n + 2)] + for i in range(1, n + 2): + dp[i][i - 1] = [None] + + for length in range(1, n + 1): + for left in range(1, n - length + 2): + right = left + length - 1 + dp[left][right] = [] + for val in range(left, right + 1): + for leftTree in dp[left][val - 1]: + for rightTree in dp[val + 1][right]: + root = TreeNode(val, leftTree, rightTree) + dp[left][right].append(root) + + return dp[1][n] +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List generateTrees(int n) { + List[][] dp = new ArrayList[n + 2][n + 2]; + for (int i = 1; i <= n + 1; i++) { + dp[i][i - 1] = new ArrayList<>(); + dp[i][i - 1].add(null); + } + + for (int length = 1; length <= n; length++) { + for (int left = 1; left + length - 1 <= n; left++) { + int right = left + length - 1; + dp[left][right] = new ArrayList<>(); + + for (int val = left; val <= right; val++) { + for (TreeNode leftTree : dp[left][val - 1]) { + for (TreeNode rightTree : dp[val + 1][right]) { + dp[left][right].add(new TreeNode(val, leftTree, rightTree)); + } + } + } + } + } + return dp[1][n]; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector generateTrees(int n) { + vector>> dp(n + 2, vector>(n + 2)); + for (int i = 1; i <= n + 1; i++) { + dp[i][i - 1].push_back(nullptr); + } + + for (int length = 1; length <= n; length++) { + for (int left = 1; left + length - 1 <= n; left++) { + int right = left + length - 1; + + for (int val = left; val <= right; val++) { + for (auto& leftTree : dp[left][val - 1]) { + for (auto& rightTree : dp[val + 1][right]) { + dp[left][right].push_back(new TreeNode(val, leftTree, rightTree)); + } + } + } + } + } + return dp[1][n]; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number} n + * @return {TreeNode[]} + */ + generateTrees(n) { + const dp = Array.from({ length: n + 2 }, () => Array(n + 2).fill(null)); + for (let i = 1; i <= n + 1; i++) { + dp[i][i - 1] = [null]; + } + + for (let length = 1; length <= n; length++) { + for (let left = 1; left + length - 1 <= n; left++) { + let right = left + length - 1; + dp[left][right] = []; + + for (let val = left; val <= right; val++) { + for (let leftTree of dp[left][val - 1]) { + for (let rightTree of dp[val + 1][right]) { + dp[left][right].push(new TreeNode(val, leftTree, rightTree)); + } + } + } + } + } + return dp[1][n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\frac {4 ^ n}{\sqrt {n}})$ +* Space complexity: + * $O(n ^ 2)$ extra space. + * $O(\frac {4 ^ n}{\sqrt {n}})$ space for the output. + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def generateTrees(self, n: int) -> list[Optional[TreeNode]]: + dp = [[] for _ in range(n + 1)] + dp[0] = [None] + + for length in range(1, n + 1): + dp[length] = [] + for val in range(1, length + 1): + for leftTree in dp[val - 1]: + for rightTree in dp[length - val]: + root = TreeNode(val) + root.left = leftTree + root.right = self.shift(rightTree, val) + dp[length].append(root) + + return dp[n] + + def shift(self, node: Optional[TreeNode], offset: int) -> Optional[TreeNode]: + if not node: + return None + root = TreeNode(node.val + offset) + root.left = self.shift(node.left, offset) + root.right = self.shift(node.right, offset) + return root +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public List generateTrees(int n) { + List[] dp = new ArrayList[n + 1]; + dp[0] = new ArrayList<>(); + dp[0].add(null); + + for (int length = 1; length <= n; length++) { + dp[length] = new ArrayList<>(); + for (int val = 1; val <= length; val++) { + for (TreeNode leftTree : dp[val - 1]) { + for (TreeNode rightTree : dp[length - val]) { + TreeNode root = new TreeNode(val); + root.left = leftTree; + root.right = shift(rightTree, val); + dp[length].add(root); + } + } + } + } + return dp[n]; + } + + private TreeNode shift(TreeNode node, int offset) { + if (node == null) return null; + TreeNode root = new TreeNode(node.val + offset); + root.left = shift(node.left, offset); + root.right = shift(node.right, offset); + return root; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector generateTrees(int n) { + vector> dp(n + 1); + dp[0].push_back(nullptr); + + for (int length = 1; length <= n; length++) { + for (int val = 1; val <= length; val++) { + for (auto& leftTree : dp[val - 1]) { + for (auto& rightTree : dp[length - val]) { + TreeNode* root = new TreeNode(val); + root->left = leftTree; + root->right = shift(rightTree, val); + dp[length].push_back(root); + } + } + } + } + return dp[n]; + } + +private: + TreeNode* shift(TreeNode* node, int offset) { + if (!node) return nullptr; + TreeNode* root = new TreeNode(node->val + offset); + root->left = shift(node->left, offset); + root->right = shift(node->right, offset); + return root; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + /** + * @param {number} n + * @return {TreeNode[]} + */ + generateTrees(n) { + const shift = (node, offset) => { + if (!node) return null; + let root = new TreeNode(node.val + offset); + root.left = shift(node.left, offset); + root.right = shift(node.right, offset); + return root; + }; + + let dp = Array.from({ length: n + 1 }, () => []); + dp[0].push(null); + + for (let length = 1; length <= n; length++) { + for (let val = 1; val <= length; val++) { + for (let leftTree of dp[val - 1]) { + for (let rightTree of dp[length - val]) { + let root = new TreeNode(val); + root.left = leftTree; + root.right = shift(rightTree, val); + dp[length].push(root); + } + } + } + } + return dp[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\frac {4 ^ n}{\sqrt {n}})$ +* Space complexity: + * $O(n)$ for the recursion stack. + * $O(n)$ extra space. + * $O(\frac {4 ^ n}{\sqrt {n}})$ space for the output. \ No newline at end of file diff --git a/articles/unique-binary-search-trees.md b/articles/unique-binary-search-trees.md new file mode 100644 index 000000000..499868203 --- /dev/null +++ b/articles/unique-binary-search-trees.md @@ -0,0 +1,411 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def numTrees(self, n: int) -> int: + if n <= 1: + return 1 + + res = 0 + for i in range(1, n + 1): + res += self.numTrees(i - 1) * self.numTrees(n - i) + return res +``` + +```java +public class Solution { + public int numTrees(int n) { + if (n <= 1) { + return 1; + } + + int res = 0; + for (int i = 1; i <= n; i++) { + res += numTrees(i - 1) * numTrees(n - i); + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int numTrees(int n) { + if (n <= 1) { + return 1; + } + + int res = 0; + for (int i = 1; i <= n; i++) { + res += numTrees(i - 1) * numTrees(n - i); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numTrees(n) { + if (n <= 1) { + return 1; + } + + let res = 0; + for (let i = 1; i <= n; i++) { + res += this.numTrees(i - 1) * this.numTrees(n - i); + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(3 ^ n)$ +* Space complexity: $O(n)$ for recursion stack. + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + + def __init__(self): + self.dp = {} + + def numTrees(self, n: int) -> int: + if n <= 1: + return 1 + if n in self.dp: + return self.dp[n] + + res = 0 + for i in range(1, n + 1): + res += self.numTrees(i - 1) * self.numTrees(n - i) + + self.dp[n] = res + return res +``` + +```java +public class Solution { + private Map dp = new HashMap<>(); + + public int numTrees(int n) { + if (n <= 1) { + return 1; + } + if (dp.containsKey(n)) { + return dp.get(n); + } + + int res = 0; + for (int i = 1; i <= n; i++) { + res += numTrees(i - 1) * numTrees(n - i); + } + + dp.put(n, res); + return res; + } +} +``` + +```cpp +class Solution { +private: + unordered_map dp; + +public: + int numTrees(int n) { + if (n <= 1) { + return 1; + } + if (dp.find(n) != dp.end()) { + return dp[n]; + } + + int res = 0; + for (int i = 1; i <= n; i++) { + res += numTrees(i - 1) * numTrees(n - i); + } + + dp[n] = res; + return res; + } +}; +``` + +```javascript +class Solution { + constructor() { + this.dp = new Map(); + } + + /** + * @param {number} n + * @return {number} + */ + numTrees(n) { + if (n <= 1) { + return 1; + } + if (this.dp.has(n)) { + return this.dp.get(n); + } + + let res = 0; + for (let i = 1; i <= n; i++) { + res += this.numTrees(i - 1) * this.numTrees(n - i); + } + + this.dp.set(n, res); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def numTrees(self, n: int) -> int: + numTree = [1] * (n + 1) + + for nodes in range(2, n + 1): + total = 0 + for root in range(1, nodes + 1): + left = root - 1 + right = nodes - root + total += numTree[left] * numTree[right] + numTree[nodes] = total + + return numTree[n] +``` + +```java +public class Solution { + public int numTrees(int n) { + int[] numTree = new int[n + 1]; + numTree[0] = 1; + numTree[1] = 1; + + for (int nodes = 2; nodes <= n; nodes++) { + int total = 0; + for (int root = 1; root <= nodes; root++) { + int left = root - 1; + int right = nodes - root; + total += numTree[left] * numTree[right]; + } + numTree[nodes] = total; + } + + return numTree[n]; + } +} +``` + +```cpp +class Solution { +public: + int numTrees(int n) { + vector numTree(n + 1, 1); + + for (int nodes = 2; nodes <= n; ++nodes) { + int total = 0; + for (int root = 1; root <= nodes; ++root) { + int left = root - 1; + int right = nodes - root; + total += numTree[left] * numTree[right]; + } + numTree[nodes] = total; + } + + return numTree[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numTrees(n) { + const numTree = Array(n + 1).fill(1); + + for (let nodes = 2; nodes <= n; nodes++) { + let total = 0; + for (let root = 1; root <= nodes; root++) { + let left = root - 1; + let right = nodes - root; + total += numTree[left] * numTree[right]; + } + numTree[nodes] = total; + } + + return numTree[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 4. Catalan Numbers - I + +::tabs-start + +```python +class Solution: + def numTrees(self, n: int) -> int: + res = 1 + for i in range(1, n): + res *= (n + i + 1) + res //= i + return res // n +``` + +```java +public class Solution { + public int numTrees(int n) { + long res = 1; + for (int i = 1; i < n; i++) { + res *= (n + i + 1); + res /= i; + } + return (int) (res / n); + } +} +``` + +```cpp +class Solution { +public: + int numTrees(int n) { + long long res = 1; + for (int i = 1; i < n; i++) { + res *= (n + i + 1); + res /= i; + } + return res / n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numTrees(n) { + let res = 1n; + for (let i = 1n; i < BigInt(n); i++) { + res *= BigInt(n) + i + 1n; + res /= i; + } + return Number(res / BigInt(n)); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. + +--- + +## 5. Catalan Numbers - II + +::tabs-start + +```python +class Solution: + def numTrees(self, n: int) -> int: + res = 1 + for i in range(n): + res *= (4 * i + 2) / (i + 2) + return int(res) +``` + +```java +public class Solution { + public int numTrees(int n) { + long res = 1; + for (int i = 0; i < n; i++) { + res *= (4 * i + 2) / (i + 2.0); + } + return (int) res; + } +} +``` + +```cpp +class Solution { +public: + int numTrees(int n) { + long long res = 1; + for (int i = 0; i < n; i++) { + res *= (4 * i + 2) / (i + 2.0); + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @return {number} + */ + numTrees(n) { + let res = 1; + for (let i = 0; i < n; i++) { + res *= (4 * i + 2) / (i + 2); + } + return Math.floor(res); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/unique-email-addresses.md b/articles/unique-email-addresses.md new file mode 100644 index 000000000..41aead4ae --- /dev/null +++ b/articles/unique-email-addresses.md @@ -0,0 +1,197 @@ +## 1. Built-In Functions + +::tabs-start + +```python +class Solution: + def numUniqueEmails(self, emails: List[str]) -> int: + unique = set() + + for e in emails: + local, domain = e.split('@') + local = local.split("+")[0] + local = local.replace(".", "") + unique.add((local, domain)) + return len(unique) +``` + +```java +public class Solution { + public int numUniqueEmails(String[] emails) { + Set unique = new HashSet<>(); + + for (String e : emails) { + String[] parts = e.split("@"); + String local = parts[0]; + String domain = parts[1]; + + local = local.split("\\+")[0]; + local = local.replace(".", ""); + unique.add(local + "@" + domain); + } + return unique.size(); + } +} +``` + +```cpp +class Solution { +public: + int numUniqueEmails(vector& emails) { + unordered_set unique; + + for (string e : emails) { + string local = e.substr(0, e.find('@')); + local = local.substr(0, local.find('+')); + erase(local, '.'); + unique.insert(local + e.substr(e.find('@'))); + } + return unique.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} emails + * @return {number} + */ + numUniqueEmails(emails) { + const unique = new Set(); + + for (let e of emails) { + let [local, domain] = e.split('@'); + local = local.split('+')[0]; + local = local.replace(/\./g, ''); + unique.add(`${local}@${domain}`); + } + return unique.size; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of strings in the array, and $m$ is the average length of these strings. + +--- + +## 2. Iteration + +::tabs-start + +```python +class Solution: + def numUniqueEmails(self, emails: List[str]) -> int: + unique = set() + + for e in emails: + i, local = 0, "" + while e[i] not in ["@", "+"]: + if e[i] != ".": + local += e[i] + i += 1 + + while e[i] != "@": + i += 1 + domain = e[i + 1:] + unique.add((local, domain)) + return len(unique) +``` + +```java +public class Solution { + public int numUniqueEmails(String[] emails) { + Set unique = new HashSet<>(); + + for (String e : emails) { + int i = 0; + StringBuilder local = new StringBuilder(); + while (i < e.length() && e.charAt(i) != '@' && e.charAt(i) != '+') { + if (e.charAt(i) != '.') { + local.append(e.charAt(i)); + } + i++; + } + + while (i < e.length() && e.charAt(i) != '@') { + i++; + } + String domain = e.substring(i + 1); + unique.add(local.toString() + "@" + domain); + } + return unique.size(); + } +} +``` + +```cpp +class Solution { +public: + int numUniqueEmails(vector& emails) { + unordered_set unique; + + for (string e : emails) { + int i = 0; + string local = ""; + while (i < e.length() && e[i] != '@' && e[i] != '+') { + if (e[i] != '.') { + local += e[i]; + } + i++; + } + + while (i < e.length() && e[i] != '@') { + i++; + } + string domain = e.substr(i + 1); + unique.insert(local + "@" + domain); + } + return unique.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} emails + * @return {number} + */ + numUniqueEmails(emails) { + const unique = new Set(); + + for (let e of emails) { + let i = 0, local = ""; + while (i < e.length && e[i] !== '@' && e[i] !== '+') { + if (e[i] !== '.') { + local += e[i]; + } + i++; + } + + while (i < e.length && e[i] !== '@') { + i++; + } + const domain = e.slice(i + 1); + unique.add(`${local}@${domain}`); + } + return unique.size; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(n)$ + +> Where $n$ is the number of strings in the array, and $m$ is the average length of these strings. \ No newline at end of file diff --git a/articles/unique-length-3-palindromic-subsequences.md b/articles/unique-length-3-palindromic-subsequences.md new file mode 100644 index 000000000..642af0a41 --- /dev/null +++ b/articles/unique-length-3-palindromic-subsequences.md @@ -0,0 +1,863 @@ +## 1. Brute Force (Recursion) + +::tabs-start + +```python +class Solution: + def countPalindromicSubsequence(self, s: str) -> int: + res = set() + + def rec(i, cur): + if len(cur) == 3: + if cur[0] == cur[2]: + res.add(cur) + return + if i == len(s): + return + rec(i + 1, cur) + rec(i + 1, cur + s[i]) + + rec(0, "") + return len(res) +``` + +```java +public class Solution { + public int countPalindromicSubsequence(String s) { + Set res = new HashSet<>(); + rec(s, 0, "", res); + return res.size(); + } + + private void rec(String s, int i, String cur, Set res) { + if (cur.length() == 3) { + if (cur.charAt(0) == cur.charAt(2)) { + res.add(cur); + } + return; + } + if (i == s.length()) { + return; + } + rec(s, i + 1, cur, res); + rec(s, i + 1, cur + s.charAt(i), res); + } +} +``` + +```cpp +class Solution { +public: + int countPalindromicSubsequence(string s) { + unordered_set res; + rec(s, 0, "", res); + return res.size(); + } + +private: + void rec(const string& s, int i, string cur, unordered_set& res) { + if (cur.length() == 3) { + if (cur[0] == cur[2]) { + res.insert(cur); + } + return; + } + if (i == s.length()) { + return; + } + rec(s, i + 1, cur, res); + rec(s, i + 1, cur + s[i], res); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + countPalindromicSubsequence(s) { + const res = new Set(); + + const rec = (i, cur) => { + if (cur.length === 3) { + if (cur[0] === cur[2]) { + res.add(cur); + } + return; + } + if (i === s.length) { + return; + } + rec(i + 1, cur); + rec(i + 1, cur + s[i]); + }; + + rec(0, ""); + return res.size; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(2 ^ n)$ +* Space complexity: $O(n + m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the number of unique three length pallindromic subsequences (26 * 26 = 676). + +--- + +## 2. Brute Force + +::tabs-start + +```python +class Solution: + def countPalindromicSubsequence(self, s: str) -> int: + res = set() + + for i in range(len(s) - 2): + for j in range(i + 1, len(s) - 1): + for k in range(j + 1, len(s)): + if s[i] != s[k]: + continue + res.add(s[i] + s[j] + s[k]) + return len(res) +``` + +```java +public class Solution { + public int countPalindromicSubsequence(String s) { + Set res = new HashSet<>(); + + for (int i = 0; i < s.length() - 2; i++) { + for (int j = i + 1; j < s.length() - 1; j++) { + for (int k = j + 1; k < s.length(); k++) { + if (s.charAt(i) != s.charAt(k)) { + continue; + } + res.add("" + s.charAt(i) + s.charAt(j) + s.charAt(k)); + } + } + } + return res.size(); + } +} +``` + +```cpp +class Solution { +public: + int countPalindromicSubsequence(string s) { + unordered_set res; + + for (int i = 0; i < s.length() - 2; i++) { + for (int j = i + 1; j < s.length() - 1; j++) { + for (int k = j + 1; k < s.length(); k++) { + if (s[i] != s[k]) { + continue; + } + res.insert(string() + s[i] + s[j] + s[k]); + } + } + } + return res.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + countPalindromicSubsequence(s) { + const res = new Set(); + + for (let i = 0; i < s.length - 2; i++) { + for (let j = i + 1; j < s.length - 1; j++) { + for (let k = j + 1; k < s.length; k++) { + if (s[i] !== s[k]) { + continue; + } + res.add(s[i] + s[j] + s[k]); + } + } + } + return res.size; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the number of unique three length pallindromic subsequences (26 * 26 = 676). + +--- + +## 3. Sequential Matching for Each Pallindrome + +::tabs-start + +```python +class Solution: + def countPalindromicSubsequence(self, s: str) -> int: + res = 0 + for ends in range(ord('a'), ord('z') + 1): + for mid in range(ord('a'), ord('z') + 1): + seq = chr(ends) + chr(mid) + chr(ends) + idx, found = 0, 0 + for c in s: + if seq[idx] == c: + idx += 1 + if idx == 3: + found = 1 + break + res += found + return res +``` + +```java +public class Solution { + public int countPalindromicSubsequence(String s) { + int res = 0; + for (char ends = 'a'; ends <= 'z'; ends++) { + for (char mid = 'a'; mid <= 'z'; mid++) { + String seq = "" + ends + mid + ends; + int idx = 0, found = 0; + for (char c : s.toCharArray()) { + if (seq.charAt(idx) == c) { + idx++; + if (idx == 3) { + found = 1; + break; + } + } + } + res += found; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int countPalindromicSubsequence(string s) { + int res = 0; + for (char ends = 'a'; ends <= 'z'; ends++) { + for (char mid = 'a'; mid <= 'z'; mid++) { + string seq = string() + ends + mid + ends; + int idx = 0, found = 0; + for (char& c : s) { + if (seq[idx] == c) { + idx++; + if (idx == 3) { + found = 1; + break; + } + } + } + res += found; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + countPalindromicSubsequence(s) { + let res = 0; + for (let ends = 'a'.charCodeAt(0); ends <= 'z'.charCodeAt(0); ends++) { + for (let mid = 'a'.charCodeAt(0); mid <= 'z'.charCodeAt(0); mid++) { + const seq = String.fromCharCode(ends) + + String.fromCharCode(mid) + + String.fromCharCode(ends); + let idx = 0, found = 0; + for (const c of s) { + if (seq[idx] === c) { + idx++; + if (idx === 3) { + found = 1; + break; + } + } + } + res += found; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(1)$ + +> Where $n$ is the length of the string $s$ and $m$ is the number of unique three length pallindromic subsequences (26 * 26 = 676). + +--- + +## 4. Iterate On Middle Characters + +::tabs-start + +```python +class Solution: + def countPalindromicSubsequence(self, s: str) -> int: + res = set() + left = set() + right = collections.Counter(s) + + for i in range(len(s)): + right[s[i]] -= 1 + if right[s[i]] == 0: + right.pop(s[i]) + + for j in range(26): + c = chr(ord('a') + j) + if c in left and c in right: + res.add((s[i], c)) + left.add(s[i]) + + return len(res) +``` + +```java +public class Solution { + public int countPalindromicSubsequence(String s) { + Set res = new HashSet<>(); + Set left = new HashSet<>(); + int[] right = new int[26]; + + for (char c : s.toCharArray()) { + right[c - 'a']++; + } + + for (int i = 0; i < s.length(); i++) { + right[s.charAt(i) - 'a']--; + if (right[s.charAt(i) - 'a'] == 0) { + right[s.charAt(i) - 'a'] = -1; + } + + for (int j = 0; j < 26; j++) { + char c = (char) (j + 'a'); + if (left.contains(c) && right[j] > 0) { + res.add("" + s.charAt(i) + c); + } + } + left.add(s.charAt(i)); + } + + return res.size(); + } +} +``` + +```cpp +class Solution { +public: + int countPalindromicSubsequence(string s) { + unordered_set res; + unordered_set left; + vector right(26, 0); + + for (char c : s) { + right[c - 'a']++; + } + + for (int i = 0; i < s.length(); i++) { + right[s[i] - 'a']--; + if (right[s[i] - 'a'] == 0) { + right[s[i] - 'a'] = -1; + } + + for (int j = 0; j < 26; j++) { + char c = 'a' + j; + if (left.count(c) && right[j] > 0) { + res.insert(string() + s[i] + c); + } + } + left.insert(s[i]); + } + + return res.size(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + countPalindromicSubsequence(s) { + const res = new Set(); + const left = new Set(); + const right = Array(26).fill(0); + + for (const c of s) { + right[c.charCodeAt(0) - 'a'.charCodeAt(0)]++; + } + + for (let i = 0; i < s.length; i++) { + right[s.charCodeAt(i) - 'a'.charCodeAt(0)]--; + if (right[s.charCodeAt(i) - 'a'.charCodeAt(0)] === 0) { + right[s.charCodeAt(i) - 'a'.charCodeAt(0)] = -1; + } + + for (let j = 0; j < 26; j++) { + const c = String.fromCharCode('a'.charCodeAt(0) + j); + if (left.has(c) && right[j] > 0) { + res.add(s[i] + c); + } + } + left.add(s[i]); + } + + return res.size; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(26 * n)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $s$ and $m$ is the number of unique three length pallindromic subsequences (26 * 26 = 676). + +--- + +## 5. Prefix Count + +::tabs-start + +```python +class Solution: + def countPalindromicSubsequence(self, s: str) -> int: + n = len(s) + prefix = [[0] * 26 for _ in range(n + 1)] + firstIndex = [-1] * 26 + lastIndex = [-1] * 26 + + for i in range(n): + j = ord(s[i]) - ord('a') + if firstIndex[j] == -1: + firstIndex[j] = i + lastIndex[j] = i + prefix[i + 1] = prefix[i][:] + prefix[i + 1][j] += 1 + + res = 0 + for ends in range(26): + if firstIndex[ends] == -1 or firstIndex[ends] == lastIndex[ends]: + continue + l, r = firstIndex[ends], lastIndex[ends] + for mid in range(26): + if prefix[r][mid] - prefix[l + 1][mid] > 0: + res += 1 + return res +``` + +```java +public class Solution { + public int countPalindromicSubsequence(String s) { + int n = s.length(); + int[][] prefix = new int[n + 1][26]; + int[] firstIndex = new int[26]; + int[] lastIndex = new int[26]; + Arrays.fill(firstIndex, -1); + Arrays.fill(lastIndex, -1); + + for (int i = 0; i < n; i++) { + int j = s.charAt(i) - 'a'; + if (firstIndex[j] == -1) { + firstIndex[j] = i; + } + lastIndex[j] = i; + for (int k = 0; k < 26; k++) { + prefix[i + 1][k] = prefix[i][k]; + } + prefix[i + 1][j]++; + } + + int res = 0; + for (int ends = 0; ends < 26; ends++) { + if (firstIndex[ends] == -1 || firstIndex[ends] == lastIndex[ends]) { + continue; + } + int l = firstIndex[ends], r = lastIndex[ends]; + for (int mid = 0; mid < 26; mid++) { + if (prefix[r][mid] - prefix[l + 1][mid] > 0) { + res++; + } + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int countPalindromicSubsequence(string s) { + int n = s.length(); + vector> prefix(n + 1, vector(26)); + vector firstIndex(26, -1); + vector lastIndex(26, -1); + + for (int i = 0; i < n; i++) { + int j = s[i] - 'a'; + if (firstIndex[j] == -1) { + firstIndex[j] = i; + } + lastIndex[j] = i; + prefix[i + 1] = prefix[i]; + prefix[i + 1][j]++; + } + + int res = 0; + for (int ends = 0; ends < 26; ends++) { + if (firstIndex[ends] == -1 || firstIndex[ends] == lastIndex[ends]) { + continue; + } + int l = firstIndex[ends], r = lastIndex[ends]; + for (int mid = 0; mid < 26; mid++) { + if (prefix[r][mid] - prefix[l + 1][mid] > 0) { + res++; + } + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + countPalindromicSubsequence(s) { + const n = s.length; + const prefix = Array.from({ length: n + 1 }, () => Array(26).fill(0)); + const firstIndex = Array(26).fill(-1); + const lastIndex = Array(26).fill(-1); + + for (let i = 0; i < n; i++) { + const j = s.charCodeAt(i) - 'a'.charCodeAt(0); + if (firstIndex[j] === -1) { + firstIndex[j] = i; + } + lastIndex[j] = i; + for (let k = 0; k < 26; k++) { + prefix[i + 1][k] = prefix[i][k]; + } + prefix[i + 1][j]++; + } + + let res = 0; + for (let ends = 0; ends < 26; ends++) { + if (firstIndex[ends] === -1 || firstIndex[ends] === lastIndex[ends]) { + continue; + } + const l = firstIndex[ends], r = lastIndex[ends]; + for (let mid = 0; mid < 26; mid++) { + if (prefix[r][mid] - prefix[l + 1][mid] > 0) { + res++; + } + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(26 * n)$ +* Space complexity: $O(26 * n)$ + +--- + +## 6. First And Last Index + +::tabs-start + +```python +class Solution: + def countPalindromicSubsequence(self, s: str) -> int: + res = 0 + + for i in range(26): + c = chr(ord('a') + i) + l, r = s.find(c), s.rfind(c) + if l == -1 or l == r: + continue + + mids = set() + for j in range(l + 1, r): + mids.add(s[j]) + res += len(mids) + + return res +``` + +```java +public class Solution { + public int countPalindromicSubsequence(String s) { + int res = 0; + + for (char c = 'a'; c <= 'z'; c++) { + int l = s.indexOf(c), r = s.lastIndexOf(c); + if (l == -1 || l == r) continue; + + Set mids = new HashSet<>(); + for (int j = l + 1; j < r; j++) { + mids.add(s.charAt(j)); + } + res += mids.size(); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int countPalindromicSubsequence(string s) { + int res = 0; + + for (char c = 'a'; c <= 'z'; c++) { + int l = s.find(c), r = s.rfind(c); + if (l == -1 || l == r) continue; + + unordered_set mids; + for (int j = l + 1; j < r; j++) { + mids.insert(s[j]); + } + res += mids.size(); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + countPalindromicSubsequence(s) { + let res = 0; + + for (let i = 0; i < 26; i++) { + const c = String.fromCharCode('a'.charCodeAt(0) + i); + const l = s.indexOf(c), r = s.lastIndexOf(c); + if (l === -1 || l === r) continue; + + const mids = new Set(); + for (let j = l + 1; j < r; j++) { + mids.add(s[j]); + } + res += mids.size; + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(26 * n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. + +--- + +## 7. First And Last Index (Optimal) + +::tabs-start + +```python +class Solution: + def countPalindromicSubsequence(self, s: str) -> int: + firstIndex = [-1] * 26 + lastIndex = [-1] * 26 + + for i in range(len(s)): + j = ord(s[i]) - ord('a') + if firstIndex[j] == -1: + firstIndex[j] = i + lastIndex[j] = i + + res = 0 + for ends in range(26): + if firstIndex[ends] == -1 or firstIndex[ends] == lastIndex[ends]: + continue + l, r = firstIndex[ends], lastIndex[ends] + mask = 0 + for i in range(l + 1, r): + c = ord(s[i]) - ord('a') + if mask & (1 << c): + continue + mask |= (1 << c) + res += 1 + + return res +``` + +```java +public class Solution { + public int countPalindromicSubsequence(String s) { + int[] firstIndex = new int[26]; + int[] lastIndex = new int[26]; + Arrays.fill(firstIndex, -1); + Arrays.fill(lastIndex, -1); + + for (int i = 0; i < s.length(); i++) { + int j = s.charAt(i) - 'a'; + if (firstIndex[j] == -1) { + firstIndex[j] = i; + } + lastIndex[j] = i; + } + + int res = 0; + for (int ends = 0; ends < 26; ends++) { + if (firstIndex[ends] == -1 || firstIndex[ends] == lastIndex[ends]) { + continue; + } + int l = firstIndex[ends], r = lastIndex[ends]; + int mask = 0; + for (int i = l + 1; i < r; i++) { + int c = s.charAt(i) - 'a'; + if ((mask & (1 << c)) != 0) { + continue; + } + mask |= (1 << c); + res++; + } + } + return res; + } +} +``` + +```cpp +class Solution { +public: + int countPalindromicSubsequence(string s) { + vector firstIndex(26, -1); + vector lastIndex(26, -1); + + for (int i = 0; i < s.size(); i++) { + int j = s[i] - 'a'; + if (firstIndex[j] == -1) { + firstIndex[j] = i; + } + lastIndex[j] = i; + } + + int res = 0; + for (int ends = 0; ends < 26; ends++) { + if (firstIndex[ends] == -1 || firstIndex[ends] == lastIndex[ends]) { + continue; + } + int l = firstIndex[ends], r = lastIndex[ends]; + int mask = 0; + for (int i = l + 1; i < r; i++) { + int c = s[i] - 'a'; + if (mask & (1 << c)) { + continue; + } + mask |= (1 << c); + res++; + } + } + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {number} + */ + countPalindromicSubsequence(s) { + const firstIndex = Array(26).fill(-1); + const lastIndex = Array(26).fill(-1); + + for (let i = 0; i < s.length; i++) { + const j = s.charCodeAt(i) - 'a'.charCodeAt(0); + if (firstIndex[j] === -1) { + firstIndex[j] = i; + } + lastIndex[j] = i; + } + + let res = 0; + for (let ends = 0; ends < 26; ends++) { + if (firstIndex[ends] === -1 || firstIndex[ends] === lastIndex[ends]) { + continue; + } + const l = firstIndex[ends], r = lastIndex[ends]; + let mask = 0; + for (let i = l + 1; i < r; i++) { + const c = s.charCodeAt(i) - 'a'.charCodeAt(0); + if (mask & (1 << c)) { + continue; + } + mask |= (1 << c); + res++; + } + } + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(26 * n)$ +* Space complexity: $O(1)$ since we have at most $26$ different characters. \ No newline at end of file diff --git a/articles/unique-paths-ii.md b/articles/unique-paths-ii.md new file mode 100644 index 000000000..35178ff44 --- /dev/null +++ b/articles/unique-paths-ii.md @@ -0,0 +1,481 @@ +## 1. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def uniquePathsWithObstacles(self, grid: List[List[int]]) -> int: + M, N = len(grid), len(grid[0]) + dp = {(M - 1, N - 1): 1} + + def dfs(r, c): + if r == M or c == N or grid[r][c]: + return 0 + if (r, c) in dp: + return dp[(r, c)] + dp[(r, c)] = dfs(r + 1, c) + dfs(r, c + 1) + return dp[(r, c)] + + return dfs(0, 0) +``` + +```java +public class Solution { + private int[][] dp; + + public int uniquePathsWithObstacles(int[][] grid) { + int M = grid.length, N = grid[0].length; + dp = new int[M][N]; + for (int i = 0; i < M; i++) { + for (int j = 0; j < N; j++) { + dp[i][j] = -1; + } + } + return dfs(0, 0, grid, M, N); + } + + private int dfs(int r, int c, int[][] grid, int M, int N) { + if (r == M || c == N || grid[r][c] == 1) { + return 0; + } + if (r == M - 1 && c == N - 1) { + return 1; + } + if (dp[r][c] != -1) { + return dp[r][c]; + } + dp[r][c] = dfs(r + 1, c, grid, M, N) + dfs(r, c + 1, grid, M, N); + return dp[r][c]; + } +} +``` + +```cpp +class Solution { +private: + vector> dp; + +public: + int uniquePathsWithObstacles(vector>& grid) { + int M = grid.size(), N = grid[0].size(); + dp.resize(M, vector(N, -1)); + return dfs(0, 0, grid, M, N); + } + +private: + int dfs(int r, int c, vector>& grid, int M, int N) { + if (r == M || c == N || grid[r][c] == 1) { + return 0; + } + if (r == M - 1 && c == N - 1) { + return 1; + } + if (dp[r][c] != -1) { + return dp[r][c]; + } + dp[r][c] = dfs(r + 1, c, grid, M, N) + dfs(r, c + 1, grid, M, N); + return dp[r][c]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + uniquePathsWithObstacles(grid) { + const M = grid.length, N = grid[0].length; + const dp = Array.from({ length: M }, () => Array(N).fill(-1)); + + const dfs = (r, c) => { + if (r === M || c === N || grid[r][c] === 1) { + return 0; + } + if (r === M - 1 && c === N - 1) { + return 1; + } + if (dp[r][c] !== -1) { + return dp[r][c]; + } + dp[r][c] = dfs(r + 1, c) + dfs(r, c + 1); + return dp[r][c]; + }; + + return dfs(0, 0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 2. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def uniquePathsWithObstacles(self, grid: List[List[int]]) -> int: + M, N = len(grid), len(grid[0]) + if grid[0][0] == 1 or grid[M - 1][N - 1] == 1: + return 0 + dp = [[0] * (N + 1) for _ in range(M + 1)] + + + dp[M - 1][N - 1] = 1 + + for r in range(M - 1, -1, -1): + for c in range(N - 1, -1, -1): + if grid[r][c] == 1: + dp[r][c] = 0 + else: + dp[r][c] += dp[r + 1][c] + dp[r][c] += dp[r][c + 1] + + return dp[0][0] +``` + +```java +public class Solution { + public int uniquePathsWithObstacles(int[][] grid) { + int M = grid.length, N = grid[0].length; + if (grid[0][0] == 1 || grid[M - 1][N - 1] == 1) { + return 0; + } + + int[][] dp = new int[M + 1][N + 1]; + dp[M - 1][N - 1] = 1; + + for (int r = M - 1; r >= 0; r--) { + for (int c = N - 1; c >= 0; c--) { + if (grid[r][c] == 1) { + dp[r][c] = 0; + } else { + dp[r][c] += dp[r + 1][c]; + dp[r][c] += dp[r][c + 1]; + } + } + } + + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + int uniquePathsWithObstacles(vector>& grid) { + int M = grid.size(), N = grid[0].size(); + if (grid[0][0] == 1 || grid[M - 1][N - 1] == 1) { + return 0; + } + + vector> dp(M + 1, vector(N + 1, 0)); + dp[M - 1][N - 1] = 1; + + for (int r = M - 1; r >= 0; r--) { + for (int c = N - 1; c >= 0; c--) { + if (grid[r][c] == 1) { + dp[r][c] = 0; + } else { + dp[r][c] += dp[r + 1][c]; + dp[r][c] += dp[r][c + 1]; + } + } + } + + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + uniquePathsWithObstacles(grid) { + const M = grid.length, N = grid[0].length; + if (grid[0][0] === 1 || grid[M - 1][N - 1] === 1) { + return 0; + } + + const dp = Array.from({ length: M + 1 }, () => Array(N + 1).fill(0)); + dp[M - 1][N - 1] = 1; + + for (let r = M - 1; r >= 0; r--) { + for (let c = N - 1; c >= 0; c--) { + if (grid[r][c] === 1) { + dp[r][c] = 0; + } else { + dp[r][c] += dp[r + 1][c]; + dp[r][c] += dp[r][c + 1]; + } + } + } + + return dp[0][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(m * n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 3. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def uniquePathsWithObstacles(self, grid: List[List[int]]) -> int: + M, N = len(grid), len(grid[0]) + dp = [0] * (N + 1) + dp[N - 1] = 1 + + for r in range(M - 1, -1, -1): + for c in range(N - 1, -1, -1): + if grid[r][c]: + dp[c] = 0 + else: + dp[c] += dp[c + 1] + + return dp[0] +``` + +```java +public class Solution { + public int uniquePathsWithObstacles(int[][] grid) { + int M = grid.length, N = grid[0].length; + int[] dp = new int[N + 1]; + dp[N - 1] = 1; + + for (int r = M - 1; r >= 0; r--) { + for (int c = N - 1; c >= 0; c--) { + if (grid[r][c] == 1) { + dp[c] = 0; + } else { + dp[c] += dp[c + 1]; + } + } + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + int uniquePathsWithObstacles(vector>& grid) { + int M = grid.size(), N = grid[0].size(); + vector dp(N + 1, 0); + dp[N - 1] = 1; + + for (int r = M - 1; r >= 0; r--) { + for (int c = N - 1; c >= 0; c--) { + if (grid[r][c] == 1) { + dp[c] = 0; + } else { + dp[c] += dp[c + 1]; + } + } + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + uniquePathsWithObstacles(grid) { + const M = grid.length, N = grid[0].length; + const dp = new Array(N + 1).fill(0); + dp[N - 1] = 1; + + for (let r = M - 1; r >= 0; r--) { + for (let c = N - 1; c >= 0; c--) { + if (grid[r][c] === 1) { + dp[c] = 0; + } else { + dp[c] += dp[c + 1]; + } + } + } + + return dp[0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(n)$ + +> Where $m$ is the number of rows and $n$ is the number of columns. + +--- + +## 4. Dynamic Programming (In-Place) + +::tabs-start + +```python +class Solution: + def uniquePathsWithObstacles(self, grid: List[List[int]]) -> int: + M, N = len(grid), len(grid[0]) + if grid[0][0] == 1 or grid[M - 1][N - 1] == 1: + return 0 + + grid[M - 1][N - 1] = 1 + + for r in range(M - 1, -1, -1): + for c in range(N - 1, -1, -1): + if r == M - 1 and c == N - 1: + continue + + if grid[r][c] == 1: + grid[r][c] = 0 + else: + down = grid[r + 1][c] if r + 1 < M else 0 + right = grid[r][c + 1] if c + 1 < N else 0 + grid[r][c] = down + right + + return grid[0][0] +``` + +```java +public class Solution { + public int uniquePathsWithObstacles(int[][] grid) { + int M = grid.length, N = grid[0].length; + if (grid[0][0] == 1 || grid[M - 1][N - 1] == 1) { + return 0; + } + + grid[M - 1][N - 1] = 1; + + for (int r = M - 1; r >= 0; r--) { + for (int c = N - 1; c >= 0; c--) { + if (r == M - 1 && c == N - 1) { + continue; + } + + if (grid[r][c] == 1) { + grid[r][c] = 0; + } else { + int down = (r + 1 < M) ? grid[r + 1][c] : 0; + int right = (c + 1 < N) ? grid[r][c + 1] : 0; + grid[r][c] = down + right; + } + } + } + + return grid[0][0]; + } +} +``` + +```cpp +class Solution { +public: + int uniquePathsWithObstacles(vector>& grid) { + int M = grid.size(), N = grid[0].size(); + if (grid[0][0] == 1 || grid[M - 1][N - 1] == 1) { + return 0; + } + + grid[M - 1][N - 1] = 1; + + for (int r = M - 1; r >= 0; r--) { + for (int c = N - 1; c >= 0; c--) { + if (r == M - 1 && c == N - 1) { + continue; + } + + if (grid[r][c] == 1) { + grid[r][c] = 0; + } else { + uint down = (r + 1 < M) ? grid[r + 1][c] : 0; + uint right = (c + 1 < N) ? grid[r][c + 1] : 0; + grid[r][c] = down + right; + } + } + } + + return grid[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} grid + * @return {number} + */ + uniquePathsWithObstacles(grid) { + const M = grid.length, N = grid[0].length; + if (grid[0][0] === 1 || grid[M - 1][N - 1] === 1) { + return 0; + } + + grid[M - 1][N - 1] = 1; + + for (let r = M - 1; r >= 0; r--) { + for (let c = N - 1; c >= 0; c--) { + if (r === M - 1 && c === N - 1) { + continue; + } + + if (grid[r][c] === 1) { + grid[r][c] = 0; + } else { + const down = (r + 1 < M) ? grid[r + 1][c] : 0; + const right = (c + 1 < N) ? grid[r][c + 1] : 0; + grid[r][c] = down + right; + } + } + } + + return grid[0][0]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m * n)$ +* Space complexity: $O(1)$ extra space. + +> Where $m$ is the number of rows and $n$ is the number of columns. \ No newline at end of file diff --git a/articles/valid-binary-search-tree.md b/articles/valid-binary-search-tree.md new file mode 100644 index 000000000..3b076e3c7 --- /dev/null +++ b/articles/valid-binary-search-tree.md @@ -0,0 +1,977 @@ +## 1. Brute Force + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + left_check = staticmethod(lambda val, limit: val < limit) + right_check = staticmethod(lambda val, limit: val > limit) + + def isValidBST(self, root: Optional[TreeNode]) -> bool: + if not root: + return True + + if (not self.isValid(root.left, root.val, self.left_check) or + not self.isValid(root.right, root.val, self.right_check)): + return False + + return self.isValidBST(root.left) and self.isValidBST(root.right) + + def isValid(self, root: Optional[TreeNode], limit: int, check) -> bool: + if not root: + return True + if not check(root.val, limit): + return False + return (self.isValid(root.left, limit, check) and + self.isValid(root.right, limit, check)) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + static boolean left_check(int val, int limit) { + return val < limit; + } + + static boolean right_check(int val, int limit) { + return val > limit; + } + + public boolean isValidBST(TreeNode root) { + if (root == null) { + return true; + } + + if (!isValid(root.left, root.val, Solution::left_check) || + !isValid(root.right, root.val, Solution::right_check)) { + return false; + } + + return isValidBST(root.left) && isValidBST(root.right); + } + + public boolean isValid(TreeNode root, int limit, CheckFunction check) { + if (root == null) { + return true; + } + if (!check.apply(root.val, limit)) { + return false; + } + return isValid(root.left, limit, check) && + isValid(root.right, limit, check); + } + + interface CheckFunction { + boolean apply(int val, int limit); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + static bool left_check(int val, int limit) { + return val < limit; + } + + static bool right_check(int val, int limit) { + return val > limit; + } + + bool isValidBST(TreeNode* root) { + if (!root) { + return true; + } + + if (!isValid(root->left, root->val, left_check) || + !isValid(root->right, root->val, right_check)) { + return false; + } + + return isValidBST(root->left) && isValidBST(root->right); + } + + bool isValid(TreeNode* root, int limit, bool (*check)(int, int)) { + if (!root) { + return true; + } + if (!check(root->val, limit)) { + return false; + } + return isValid(root->left, limit, check) && + isValid(root->right, limit, check); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {number} val + * @param {number} limit + * @returns {boolean} + */ + left_check(val, limit) { + return val < limit; + } + + /** + * @param {number} val + * @param {number} limit + * @returns {boolean} + */ + right_check(val, limit) { + return val > limit; + } + + /** + * @param {TreeNode} root + * @returns {boolean} + */ + isValidBST(root) { + if (!root) { + return true; + } + + if (!this.isValid(root.left, root.val, this.left_check) || + !this.isValid(root.right, root.val, this.right_check)) { + return false; + } + + return this.isValidBST(root.left) && this.isValidBST(root.right); + } + + /** + * @param {TreeNode} root + * @param {number} limit + * @param {function} check + * @returns {boolean} + */ + isValid(root, limit, check) { + if (!root) { + return true; + } + if (!check.call(this, root.val, limit)) { + return false; + } + return this.isValid(root.left, limit, check) && + this.isValid(root.right, limit, check); + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + static bool LeftCheck(int val, int limit) { + return val < limit; + } + + static bool RightCheck(int val, int limit) { + return val > limit; + } + + public bool IsValidBST(TreeNode root) { + if (root == null) { + return true; + } + + if (!IsValid(root.left, root.val, LeftCheck) || + !IsValid(root.right, root.val, RightCheck)) { + return false; + } + + return IsValidBST(root.left) && IsValidBST(root.right); + } + + public bool IsValid(TreeNode root, int limit, Func check) { + if (root == null) { + return true; + } + if (!check(root.val, limit)) { + return false; + } + return IsValid(root.left, limit, check) && + IsValid(root.right, limit, check); + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func isValidBST(root *TreeNode) bool { + if root == nil { + return true + } + return isValid(root.Left, root.Val, func(val, limit int) bool { return val < limit }) && + isValid(root.Right, root.Val, func(val, limit int) bool { return val > limit }) && + isValidBST(root.Left) && + isValidBST(root.Right) +} + +func isValid(root *TreeNode, limit int, check func(int, int) bool) bool { + if root == nil { + return true + } + if !check(root.Val, limit) { + return false + } + return isValid(root.Left, limit, check) && isValid(root.Right, limit, check) +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + private val leftCheck: (Int, Int) -> Boolean = { value, limit -> value < limit } + private val rightCheck: (Int, Int) -> Boolean = { value, limit -> value > limit } + + fun isValidBST(root: TreeNode?): Boolean { + if (root == null) return true + if (!isValid(root.left, root.`val`, leftCheck) || !isValid(root.right, root.`val`, rightCheck)) { + return false + } + return isValidBST(root.left) && isValidBST(root.right) + } + + private fun isValid(root: TreeNode?, limit: Int, check: (Int, Int) -> Boolean): Boolean { + if (root == null) return true + if (!check(root.`val`, limit)) return false + return isValid(root.left, limit, check) && isValid(root.right, limit, check) + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func isValidBST(_ root: TreeNode?) -> Bool { + guard let root = root else { return true } + if !isValid(root.left, root.val, { $0 < $1 }) || + !isValid(root.right, root.val, { $0 > $1 }) { + return false + } + + return isValidBST(root.left) && isValidBST(root.right) + } + + private func isValid(_ root: TreeNode?, _ limit: Int, _ check: (Int, Int) -> Bool) -> Bool { + guard let root = root else { return true } + if !check(root.val, limit) { + return false + } + + return isValid(root.left, limit, check) && isValid(root.right, limit, check) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Depth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def isValidBST(self, root: Optional[TreeNode]) -> bool: + def valid(node, left, right): + if not node: + return True + if not (left < node.val < right): + return False + + return valid(node.left, left, node.val) and valid( + node.right, node.val, right + ) + + return valid(root, float("-inf"), float("inf")) +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public boolean isValidBST(TreeNode root) { + return valid(root, Long.MIN_VALUE, Long.MAX_VALUE); + } + + public boolean valid(TreeNode node, long left, long right) { + if (node == null) { + return true; + } + if (!(left < node.val && node.val < right)) { + return false; + } + return valid(node.left, left, node.val) && + valid(node.right, node.val, right); + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + bool isValidBST(TreeNode* root) { + return valid(root, LONG_MIN, LONG_MAX); + } + + bool valid(TreeNode* node, long left, long right) { + if (!node) { + return true; + } + if (!(left < node->val && node->val < right)) { + return false; + } + return valid(node->left, left, node->val) && + valid(node->right, node->val, right); + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isValidBST(root) { + return this.valid(root, -Infinity, Infinity); + } + + /** + * @param {TreeNode} node + * @param {number} left + * @param {number} right + */ + valid(node, left, right) { + if (node === null) { + return true; + } + if (!(left < node.val && node.val < right)) { + return false; + } + return this.valid(node.left, left, node.val) && + this.valid(node.right, node.val, right); + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public bool IsValidBST(TreeNode root) { + return valid(root, long.MinValue, long.MaxValue); + } + + public bool valid(TreeNode node, long left, long right) { + if (node == null) { + return true; + } + if (!(left < node.val && node.val < right)) { + return false; + } + return valid(node.left, left, node.val) && + valid(node.right, node.val, right); + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func isValidBST(root *TreeNode) bool { + return valid(root, math.MinInt64, math.MaxInt64) +} + +func valid(node *TreeNode, left, right int64) bool { + if node == nil { + return true + } + + val := int64(node.Val) + if val <= left || val >= right { + return false + } + + return valid(node.Left, left, val) && valid(node.Right, val, right) +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun isValidBST(root: TreeNode?): Boolean { + return valid(root, Long.MIN_VALUE, Long.MAX_VALUE) + } + + private fun valid(node: TreeNode?, left: Long, right: Long): Boolean { + if (node == null) { + return true + } + + val value = node.`val`.toLong() + if (value <= left || value >= right) { + return false + } + + return valid(node.left, left, value) && + valid(node.right, value, right) + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func isValidBST(_ root: TreeNode?) -> Bool { + return valid(root, Int.min, Int.max) + } + + private func valid(_ node: TreeNode?, _ left: Int, _ right: Int) -> Bool { + guard let node = node else { return true } + if !(left < node.val && node.val < right) { + return false + } + return valid(node.left, left, node.val) && valid(node.right, node.val, right) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Breadth First Search + +::tabs-start + +```python +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right + +class Solution: + def isValidBST(self, root: Optional[TreeNode]) -> bool: + if not root: + return True + + q = deque([(root, float("-inf"), float("inf"))]) + + while q: + node, left, right = q.popleft() + if not (left < node.val < right): + return False + if node.left: + q.append((node.left, left, node.val)) + if node.right: + q.append((node.right, node.val, right)) + + return True +``` + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public boolean isValidBST(TreeNode root) { + if (root == null) { + return true; + } + + Queue queue = new LinkedList<>(); + queue.offer(new Object[]{root, Long.MIN_VALUE, Long.MAX_VALUE}); + + while (!queue.isEmpty()) { + Object[] current = queue.poll(); + TreeNode node = (TreeNode) current[0]; + long left = (long) current[1]; + long right = (long) current[2]; + + if (!(left < node.val && node.val < right)) { + return false; + } + + if (node.left != null) { + queue.offer(new Object[]{node.left, left, (long) node.val}); + } + if (node.right != null) { + queue.offer(new Object[]{node.right, (long) node.val, right}); + } + } + + return true; + } +} +``` + +```cpp +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + bool isValidBST(TreeNode* root) { + if (!root) { + return true; + } + + queue> queue; + queue.push(make_tuple(root, LONG_MIN, LONG_MAX)); + + while (!queue.empty()) { + auto [node, left, right] = queue.front(); + queue.pop(); + + if (!(left < node->val && node->val < right)) { + return false; + } + if (node->left) { + queue.push(make_tuple(node->left, left, node->val)); + } + if (node->right) { + queue.push(make_tuple(node->right, node->val, right)); + } + } + + return true; + } +}; +``` + +```javascript +/** + * Definition for a binary tree node. + * class TreeNode { + * constructor(val = 0, left = null, right = null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +class Solution { + /** + * @param {TreeNode} root + * @return {boolean} + */ + isValidBST(root) { + if (root === null) { + return true; + } + + const queue = new Queue([[root, -Infinity, Infinity]]); + + while (queue.size() > 0) { + const [node, left, right] = queue.pop(); + + if (!(left < node.val && node.val < right)) { + return false; + } + if (node.left) { + queue.push([node.left, left, node.val]); + } + if (node.right) { + queue.push([node.right, node.val, right]); + } + } + + return true; + } +} +``` + +```csharp +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution { + public bool IsValidBST(TreeNode root) { + if (root == null) { + return true; + } + + Queue<(TreeNode node, long left, long right)> queue = new Queue<(TreeNode, long, long)>(); + queue.Enqueue((root, long.MinValue, long.MaxValue)); + + while (queue.Count > 0) { + var (node, left, right) = queue.Dequeue(); + + if (!(left < node.val && node.val < right)) { + return false; + } + if (node.left != null) { + queue.Enqueue((node.left, left, node.val)); + } + if (node.right != null) { + queue.Enqueue((node.right, node.val, right)); + } + } + + return true; + } +} +``` + +```go +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +type QueueItem struct { + node *TreeNode + left int64 + right int64 +} + +func isValidBST(root *TreeNode) bool { + if root == nil { + return true + } + + queue := []QueueItem{{root, math.MinInt64, math.MaxInt64}} + + for len(queue) > 0 { + item := queue[0] + queue = queue[1:] + + val := int64(item.node.Val) + if val <= item.left || val >= item.right { + return false + } + + if item.node.Left != nil { + queue = append(queue, QueueItem{item.node.Left, item.left, val}) + } + if item.node.Right != nil { + queue = append(queue, QueueItem{item.node.Right, val, item.right}) + } + } + + return true +} +``` + +```kotlin +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + private data class QueueItem( + val node: TreeNode, + val left: Long, + val right: Long + ) + + fun isValidBST(root: TreeNode?): Boolean { + if (root == null) { + return true + } + + val queue = ArrayDeque() + queue.addLast(QueueItem(root, Long.MIN_VALUE, Long.MAX_VALUE)) + + while (queue.isNotEmpty()) { + val (node, left, right) = queue.removeFirst() + + val value = node.`val`.toLong() + if (value <= left || value >= right) { + return false + } + + node.left?.let { + queue.addLast(QueueItem(it, left, value)) + } + node.right?.let { + queue.addLast(QueueItem(it, value, right)) + } + } + + return true + } +} +``` + +```swift +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func isValidBST(_ root: TreeNode?) -> Bool { + guard let root = root else { return true } + + var q = Deque<(TreeNode, Int, Int)>() + q.append((root, Int.min, Int.max)) + + while !q.isEmpty { + let (node, left, right) = q.popFirst()! + + if !(left < node.val && node.val < right) { + return false + } + + if let leftNode = node.left { + q.append((leftNode, left, node.val)) + } + if let rightNode = node.right { + q.append((rightNode, node.val, right)) + } + } + return true + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/valid-palindrome-ii.md b/articles/valid-palindrome-ii.md new file mode 100644 index 000000000..6b1fbf303 --- /dev/null +++ b/articles/valid-palindrome-ii.md @@ -0,0 +1,476 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def validPalindrome(self, s: str) -> bool: + if s == s[::-1]: + return True + + for i in range(len(s)): + newS = s[:i] + s[i + 1:] + if newS == newS[::-1]: + return True + + return False +``` + +```java +public class Solution { + public boolean validPalindrome(String s) { + if (isPalindrome(s)) { + return true; + } + + for (int i = 0; i < s.length(); i++) { + String newS = s.substring(0, i) + s.substring(i + 1); + if (isPalindrome(newS)) { + return true; + } + } + + return false; + } + + private boolean isPalindrome(String s) { + int left = 0, right = s.length() - 1; + while (left < right) { + if (s.charAt(left) != s.charAt(right)) { + return false; + } + left++; + right--; + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool validPalindrome(string s) { + if (isPalindrome(s)) { + return true; + } + + for (int i = 0; i < s.size(); i++) { + string newS = s.substr(0, i) + s.substr(i + 1); + if (isPalindrome(newS)) { + return true; + } + } + + return false; + } + +private: + bool isPalindrome(const string& s) { + int left = 0, right = s.size() - 1; + while (left < right) { + if (s[left] != s[right]) { + return false; + } + left++; + right--; + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + validPalindrome(s) { + if (this.isPalindrome(s)) { + return true; + } + + for (let i = 0; i < s.length; i++) { + const newS = s.slice(0, i) + s.slice(i + 1); + if (this.isPalindrome(newS)) { + return true; + } + } + + return false; + } + + /** + * @param {string} s + * @return {boolean} + */ + isPalindrome(s) { + let left = 0, right = s.length - 1; + while (left < right) { + if (s[left] !== s[right]) { + return false; + } + left++; + right--; + } + return true; + } +} +``` + +```csharp +public class Solution { + public bool ValidPalindrome(string s) { + if (IsPalindrome(s)) return true; + + for (int i = 0; i < s.Length; i++) { + string newS = s.Substring(0, i) + s.Substring(i + 1); + if (IsPalindrome(newS)) return true; + } + + return false; + } + + private bool IsPalindrome(string str) { + int left = 0, right = str.Length - 1; + while (left < right) { + if (str[left] != str[right]) return false; + left++; + right--; + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Two Pointers + +::tabs-start + +```python +class Solution: + def validPalindrome(self, s: str) -> bool: + l, r = 0, len(s) - 1 + + while l < r: + if s[l] != s[r]: + skipL = s[l + 1 : r + 1] + skipR = s[l : r] + return skipL == skipL[::-1] or skipR == skipR[::-1] + l, r = l + 1, r - 1 + + return True +``` + +```java +public class Solution { + public boolean validPalindrome(String s) { + int l = 0, r = s.length() - 1; + + while (l < r) { + if (s.charAt(l) != s.charAt(r)) { + return isPalindrome(s.substring(0, l) + s.substring(l + 1)) || + isPalindrome(s.substring(0, r) + s.substring(r + 1)); + } + l++; + r--; + } + + return true; + } + + private boolean isPalindrome(String s) { + int l = 0, r = s.length() - 1; + while (l < r) { + if (s.charAt(l) != s.charAt(r)) { + return false; + } + l++; + r--; + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool validPalindrome(string s) { + int l = 0, r = s.size() - 1; + + while (l < r) { + if (s[l] != s[r]) { + return isPalindrome(s.substr(0, l) + s.substr(l + 1)) || + isPalindrome(s.substr(0, r) + s.substr(r + 1)); + } + l++; + r--; + } + + return true; + } + +private: + bool isPalindrome(string s) { + int l = 0, r = s.length() - 1; + while (l < r) { + if (s[l] != s[r]) { + return false; + } + l++; + r--; + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + validPalindrome(s) { + let l = 0, r = s.length - 1; + + while (l < r) { + if (s[l] !== s[r]) { + return this.isPalindrome(s.slice(0, l) + s.slice(l + 1)) || + this.isPalindrome(s.slice(0, r) + s.slice(r + 1)); + } + l++; + r--; + } + + return true; + } + + /** + * @param {string} s + * @return {boolean} + */ + isPalindrome(s) { + let left = 0, right = s.length - 1; + while (left < right) { + if (s[left] !== s[right]) { + return false; + } + left++; + right--; + } + return true; + } +} +``` + +```csharp +public class Solution { + public bool ValidPalindrome(string s) { + int l = 0, r = s.Length - 1; + + while (l < r) { + if (s[l] != s[r]) { + string skipL = s.Substring(l + 1, r - l); + string skipR = s.Substring(l, r - l); + return IsPalindrome(skipL) || IsPalindrome(skipR); + } + l++; + r--; + } + + return true; + } + + private bool IsPalindrome(string str) { + int left = 0, right = str.Length - 1; + while (left < right) { + if (str[left] != str[right]) return false; + left++; + right--; + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Two Pointers (Optimal) + +::tabs-start + +```python +class Solution: + def validPalindrome(self, s: str) -> bool: + def is_palindrome(l, r): + while l < r: + if s[l] != s[r]: + return False + l += 1 + r -= 1 + return True + + l, r = 0, len(s) - 1 + while l < r: + if s[l] != s[r]: + return (is_palindrome(l + 1, r) or + is_palindrome(l, r - 1)) + l += 1 + r -= 1 + + return True +``` + +```java +public class Solution { + public boolean validPalindrome(String s) { + int l = 0, r = s.length() - 1; + + while (l < r) { + if (s.charAt(l) != s.charAt(r)) { + return isPalindrome(s, l + 1, r) || + isPalindrome(s, l, r - 1); + } + l++; + r--; + } + + return true; + } + + private boolean isPalindrome(String s, int l, int r) { + while (l < r) { + if (s.charAt(l) != s.charAt(r)) { + return false; + } + l++; + r--; + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool validPalindrome(string s) { + int l = 0, r = s.size() - 1; + + while (l < r) { + if (s[l] != s[r]) { + return isPalindrome(s, l + 1, r) || + isPalindrome(s, l, r - 1); + } + l++; + r--; + } + + return true; + } + +private: + bool isPalindrome(const string& s, int l, int r) { + while (l < r) { + if (s[l] != s[r]) { + return false; + } + l++; + r--; + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + validPalindrome(s) { + let l = 0, r = s.length - 1; + + while (l < r) { + if (s[l] !== s[r]) { + return this.isPalindrome(s, l + 1, r) || + this.isPalindrome(s, l, r - 1); + } + l++; + r--; + } + + return true; + } + + /** + * @param {string} s + * @param {number} l + * @param {number} r + * @return {boolean} + */ + isPalindrome(s, l, r) { + while (l < r) { + if (s[l] !== s[r]) { + return false; + } + l++; + r--; + } + return true; + } +} +``` + +```csharp +public class Solution { + public bool ValidPalindrome(string s) { + bool IsPalindrome(int l, int r) { + while (l < r) { + if (s[l] != s[r]) return false; + l++; + r--; + } + return true; + } + + int left = 0, right = s.Length - 1; + while (left < right) { + if (s[left] != s[right]) { + return IsPalindrome(left + 1, right) || IsPalindrome(left, right - 1); + } + left++; + right--; + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/valid-parenthesis-string.md b/articles/valid-parenthesis-string.md new file mode 100644 index 000000000..c412ad605 --- /dev/null +++ b/articles/valid-parenthesis-string.md @@ -0,0 +1,1459 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def checkValidString(self, s: str) -> bool: + + def dfs(i, open): + if open < 0: + return False + if i == len(s): + return open == 0 + + if s[i] == '(': + return dfs(i + 1, open + 1) + elif s[i] == ')': + return dfs(i + 1, open - 1) + else: + return (dfs(i + 1, open) or + dfs(i + 1, open + 1) or + dfs(i + 1, open - 1)) + return dfs(0, 0) +``` + +```java +public class Solution { + public boolean checkValidString(String s) { + + return dfs(0, 0, s); + } + + private boolean dfs(int i, int open, String s) { + if (open < 0) return false; + if (i == s.length()) return open == 0; + + if (s.charAt(i) == '(') { + return dfs(i + 1, open + 1, s); + } else if (s.charAt(i) == ')') { + return dfs(i + 1, open - 1, s); + } else { + return dfs(i + 1, open, s) || + dfs(i + 1, open + 1, s) || + dfs(i + 1, open - 1, s); + } + } +} +``` + +```cpp +class Solution { +public: + bool checkValidString(string s) { + return dfs(0, 0, s); + } + +private: + bool dfs(int i, int open, const string& s) { + if (open < 0) return false; + if (i == s.size()) return open == 0; + + if (s[i] == '(') { + return dfs(i + 1, open + 1, s); + } else if (s[i] == ')') { + return dfs(i + 1, open - 1, s); + } else { + return dfs(i + 1, open, s) || + dfs(i + 1, open + 1, s) || + dfs(i + 1, open - 1, s); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + checkValidString(s) { + function dfs(i, open) { + if (open < 0) return false; + if (i === s.length) return open === 0; + + if (s[i] === '(') { + return dfs(i + 1, open + 1); + } else if (s[i] === ')') { + return dfs(i + 1, open - 1); + } else { + return dfs(i + 1, open) || + dfs(i + 1, open + 1) || + dfs(i + 1, open - 1); + } + } + + return dfs(0, 0); + } +} +``` + +```csharp +public class Solution { + public bool CheckValidString(string s) { + return Dfs(0, 0, s); + } + + private bool Dfs(int i, int open, string s) { + if (open < 0) return false; + if (i == s.Length) return open == 0; + + if (s[i] == '(') { + return Dfs(i + 1, open + 1, s); + } else if (s[i] == ')') { + return Dfs(i + 1, open - 1, s); + } else { + return Dfs(i + 1, open, s) || + Dfs(i + 1, open + 1, s) || + Dfs(i + 1, open - 1, s); + } + } +} +``` + +```go +func checkValidString(s string) bool { + var dfs func(i, open int) bool + dfs = func(i, open int) bool { + if open < 0 { + return false + } + if i == len(s) { + return open == 0 + } + + if s[i] == '(' { + return dfs(i+1, open+1) + } else if s[i] == ')' { + return dfs(i+1, open-1) + } else { + return (dfs(i+1, open) || + dfs(i+1, open+1) || + dfs(i+1, open-1)) + } + } + return dfs(0, 0) +} +``` + +```kotlin +class Solution { + fun checkValidString(s: String): Boolean { + + fun dfs(i: Int, open: Int): Boolean { + if (open < 0) { + return false + } + if (i == s.length) { + return open == 0 + } + + return when (s[i]) { + '(' -> dfs(i + 1, open + 1) + ')' -> dfs(i + 1, open - 1) + else -> dfs(i + 1, open) || dfs(i + 1, open + 1) || dfs(i + 1, open - 1) + } + } + + return dfs(0, 0) + } +} +``` + +```swift +class Solution { + func checkValidString(_ s: String) -> Bool { + let chars = Array(s) + + func dfs(_ i: Int, _ open: Int) -> Bool { + if open < 0 { return false } + if i == chars.count { return open == 0 } + + if chars[i] == "(" { + return dfs(i + 1, open + 1) + } else if chars[i] == ")" { + return dfs(i + 1, open - 1) + } else { + return dfs(i + 1, open) || + dfs(i + 1, open + 1) || + dfs(i + 1, open - 1) + } + } + + return dfs(0, 0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(3 ^ n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def checkValidString(self, s: str) -> bool: + n = len(s) + memo = [[None] * (n + 1) for _ in range(n + 1)] + + def dfs(i, open): + if open < 0: + return False + if i == n: + return open == 0 + if memo[i][open] is not None: + return memo[i][open] + + if s[i] == '(': + result = dfs(i + 1, open + 1) + elif s[i] == ')': + result = dfs(i + 1, open - 1) + else: + result = (dfs(i + 1, open) or + dfs(i + 1, open + 1) or + dfs(i + 1, open - 1)) + + memo[i][open] = result + return result + + return dfs(0, 0) +``` + +```java +public class Solution { + public boolean checkValidString(String s) { + int n = s.length(); + Boolean[][] memo = new Boolean[n + 1][n + 1]; + return dfs(0, 0, s, memo); + } + + private boolean dfs(int i, int open, String s, Boolean[][] memo) { + if (open < 0) return false; + if (i == s.length()) return open == 0; + + if (memo[i][open] != null) return memo[i][open]; + + boolean result; + if (s.charAt(i) == '(') { + result = dfs(i + 1, open + 1, s, memo); + } else if (s.charAt(i) == ')') { + result = dfs(i + 1, open - 1, s, memo); + } else { + result = (dfs(i + 1, open, s, memo) || + dfs(i + 1, open + 1, s, memo) || + dfs(i + 1, open - 1, s, memo)); + } + + memo[i][open] = result; + return result; + } +} +``` + +```cpp +class Solution { +public: + bool checkValidString(string s) { + int n = s.size(); + memo = vector>(n + 1, vector(n + 1, -1)); + return dfs(0, 0, s); + } + +private: + vector> memo; + + bool dfs(int i, int open, const string& s) { + if (open < 0) return false; + if (i == s.size()) return open == 0; + + if (memo[i][open] != -1) return memo[i][open] == 1; + + bool result; + if (s[i] == '(') { + result = dfs(i + 1, open + 1, s); + } else if (s[i] == ')') { + result = dfs(i + 1, open - 1, s); + } else { + result = (dfs(i + 1, open, s) || + dfs(i + 1, open + 1, s) || + dfs(i + 1, open - 1, s)); + } + + memo[i][open] = result ? 1 : 0; + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + checkValidString(s) { + const n = s.length; + const memo = Array.from({ length: n + 1 }, () => + Array(n + 1).fill(null)); + + function dfs(i, open) { + if (open < 0) return false; + if (i === n) return open === 0; + + if (memo[i][open] !== null) return memo[i][open]; + + let result; + if (s[i] === '(') { + result = dfs(i + 1, open + 1); + } else if (s[i] === ')') { + result = dfs(i + 1, open - 1); + } else { + result = dfs(i + 1, open) || + dfs(i + 1, open + 1) || + dfs(i + 1, open - 1); + } + + memo[i][open] = result; + return result; + } + + return dfs(0, 0); + } +} +``` + +```csharp +public class Solution { + public bool CheckValidString(string s) { + int n = s.Length; + bool?[,] memo = new bool?[n + 1, n + 1]; + return Dfs(0, 0, s, memo); + } + + private bool Dfs(int i, int open, string s, bool?[,] memo) { + if (open < 0) return false; + if (i == s.Length) return open == 0; + + if (memo[i, open].HasValue) return memo[i, open].Value; + + bool result; + if (s[i] == '(') { + result = Dfs(i + 1, open + 1, s, memo); + } else if (s[i] == ')') { + result = Dfs(i + 1, open - 1, s, memo); + } else { + result = Dfs(i + 1, open, s, memo) || + Dfs(i + 1, open + 1, s, memo) || + Dfs(i + 1, open - 1, s, memo); + } + + memo[i, open] = result; + return result; + } +} +``` + +```go +func checkValidString(s string) bool { + memo := make([][]int, len(s)+1) + for i := range memo { + memo[i] = make([]int, len(s)+1) + for j := range memo[i] { + memo[i][j] = -1 + } + } + + var dfs func(i, open int) bool + dfs = func(i, open int) bool { + if open < 0 { + return false + } + if i == len(s) { + return open == 0 + } + if memo[i][open] != -1 { + return memo[i][open] == 1 + } + + result := false + if s[i] == '(' { + result = dfs(i+1, open+1) + } else if s[i] == ')' { + result = dfs(i+1, open-1) + } else { + result = (dfs(i+1, open) || + dfs(i+1, open+1) || + dfs(i+1, open-1)) + } + + memo[i][open] = 1 + if !result { + memo[i][open] = 0 + } + return result + } + return dfs(0, 0) +} +``` + +```kotlin +class Solution { + fun checkValidString(s: String): Boolean { + val memo = Array(s.length + 1) { IntArray(s.length + 1) { -1 } } + + fun dfs(i: Int, open: Int): Boolean { + if (open < 0) return false + if (i == s.length) return open == 0 + if (memo[i][open] != -1) return memo[i][open] == 1 + + val result = when (s[i]) { + '(' -> dfs(i + 1, open + 1) + ')' -> dfs(i + 1, open - 1) + else -> (dfs(i + 1, open) || + dfs(i + 1, open + 1) || + dfs(i + 1, open - 1)) + } + + memo[i][open] = if (result) 1 else 0 + return result + } + + return dfs(0, 0) + } +} +``` + +```swift +class Solution { + func checkValidString(_ s: String) -> Bool { + let n = s.count + let sArr = Array(s) + var memo = Array( + repeating: Array(repeating: nil, count: n + 1), + count: n + 1 + ) + + func dfs(_ i: Int, _ open: Int) -> Bool { + if open < 0 { + return false + } + if i == n { + return open == 0 + } + if let memoized = memo[i][open] { + return memoized + } + + let result: Bool + if sArr[i] == "(" { + result = dfs(i + 1, open + 1) + } else if sArr[i] == ")" { + result = dfs(i + 1, open - 1) + } else { + result = dfs(i + 1, open) || + dfs(i + 1, open + 1) || + dfs(i + 1, open - 1) + } + + memo[i][open] = result + return result + } + + return dfs(0, 0) + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def checkValidString(self, s: str) -> bool: + n = len(s) + dp = [[False] * (n + 1) for _ in range(n + 1)] + dp[n][0] = True + + for i in range(n - 1, -1, -1): + for open in range(n): + res = False + if s[i] == '*': + res |= dp[i + 1][open + 1] + if open > 0: + res |= dp[i + 1][open - 1] + res |= dp[i + 1][open] + else: + if s[i] == '(': + res |= dp[i + 1][open + 1] + elif open > 0: + res |= dp[i + 1][open - 1] + dp[i][open] = res + + return dp[0][0] +``` + +```java +public class Solution { + public boolean checkValidString(String s) { + int n = s.length(); + boolean[][] dp = new boolean[n + 1][n + 1]; + dp[n][0] = true; + + for (int i = n - 1; i >= 0; i--) { + for (int open = 0; open < n; open++) { + boolean res = false; + if (s.charAt(i) == '*') { + res |= dp[i + 1][open + 1]; + if (open > 0) res |= dp[i + 1][open - 1]; + res |= dp[i + 1][open]; + } else { + if (s.charAt(i) == '(') { + res |= dp[i + 1][open + 1]; + } else if (open > 0) { + res |= dp[i + 1][open - 1]; + } + } + dp[i][open] = res; + } + } + return dp[0][0]; + } +} +``` + +```cpp +class Solution { +public: + bool checkValidString(string s) { + int n = s.size(); + vector> dp(n + 1, vector(n + 1, false)); + dp[n][0] = true; + + for (int i = n - 1; i >= 0; --i) { + for (int open = 0; open < n; ++open) { + bool res = false; + if (s[i] == '*') { + res |= dp[i + 1][open + 1]; + if (open > 0) res |= dp[i + 1][open - 1]; + res |= dp[i + 1][open]; + } else { + if (s[i] == '(') { + res |= dp[i + 1][open + 1]; + } else if (open > 0) { + res |= dp[i + 1][open - 1]; + } + } + dp[i][open] = res; + } + } + return dp[0][0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + checkValidString(s) { + const n = s.length; + const dp = Array.from({ length: n + 1 }, () => + Array(n + 1).fill(false)); + dp[n][0] = true; + + for (let i = n - 1; i >= 0; i--) { + for (let open = 0; open < n; open++) { + let res = false; + if (s[i] === '*') { + res ||= dp[i + 1][open + 1]; + if (open > 0) res ||= dp[i + 1][open - 1]; + res ||= dp[i + 1][open]; + } else { + if (s[i] === '(') { + res ||= dp[i + 1][open + 1]; + } else if (open > 0) { + res ||= dp[i + 1][open - 1]; + } + } + dp[i][open] = res; + } + } + return dp[0][0]; + } +} +``` + +```csharp +public class Solution { + public bool CheckValidString(string s) { + int n = s.Length; + bool[,] dp = new bool[n + 1, n + 1]; + dp[n, 0] = true; + + for (int i = n - 1; i >= 0; i--) { + for (int open = 0; open < n; open++) { + bool res = false; + if (s[i] == '*') { + res |= dp[i + 1, open + 1]; + if (open > 0) res |= dp[i + 1, open - 1]; + res |= dp[i + 1, open]; + } else { + if (s[i] == '(') { + res |= dp[i + 1, open + 1]; + } else if (open > 0) { + res |= dp[i + 1, open - 1]; + } + } + dp[i, open] = res; + } + } + return dp[0, 0]; + } +} +``` + +```go +func checkValidString(s string) bool { + n := len(s) + dp := make([][]bool, n+1) + for i := range dp { + dp[i] = make([]bool, n+1) + } + dp[n][0] = true + + for i := n - 1; i >= 0; i-- { + for open := 0; open < n; open++ { + res := false + if s[i] == '*' { + res = dp[i+1][open+1] + if open > 0 { + res = res || dp[i+1][open-1] + } + res = res || dp[i+1][open] + } else { + if s[i] == '(' { + res = dp[i+1][open+1] + } else if open > 0 { + res = dp[i+1][open-1] + } + } + dp[i][open] = res + } + } + return dp[0][0] +} +``` + +```kotlin +class Solution { + fun checkValidString(s: String): Boolean { + val n = s.length + val dp = Array(n + 1) { BooleanArray(n + 1) } + dp[n][0] = true + + for (i in n - 1 downTo 0) { + for (open in 0 until n) { + var res = false + if (s[i] == '*') { + res = dp[i + 1][open + 1] + if (open > 0) { + res = res || dp[i + 1][open - 1] + } + res = res || dp[i + 1][open] + } else { + if (s[i] == '(') { + res = dp[i + 1][open + 1] + } else if (open > 0) { + res = dp[i + 1][open - 1] + } + } + dp[i][open] = res + } + } + return dp[0][0] + } +} +``` + +```swift +class Solution { + func checkValidString(_ s: String) -> Bool { + let n = s.count + var dp = Array(repeating: Array(repeating: false, count: n + 1), count: n + 1) + dp[n][0] = true + + let chars = Array(s) + + for i in stride(from: n - 1, through: 0, by: -1) { + for open in 0.. 0 { + res = res || dp[i + 1][open - 1] + } + res = res || dp[i + 1][open] + } else { + if chars[i] == "(" { + res = res || dp[i + 1][open + 1] + } else if open > 0 { + res = res || dp[i + 1][open - 1] + } + } + dp[i][open] = res + } + } + + return dp[0][0] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 4. Dynamic Programming (Space Optimized) + +::tabs-start + +```python +class Solution: + def checkValidString(self, s: str) -> bool: + n = len(s) + dp = [False] * (n + 1) + dp[0] = True + + for i in range(n - 1, -1, -1): + new_dp = [False] * (n + 1) + for open in range(n): + if s[i] == '*': + new_dp[open] = (dp[open + 1] or + (open > 0 and dp[open - 1]) or + dp[open]) + elif s[i] == '(': + new_dp[open] = dp[open + 1] + elif open > 0: + new_dp[open] = dp[open - 1] + dp = new_dp + + return dp[0] +``` + +```java +public class Solution { + public boolean checkValidString(String s) { + int n = s.length(); + boolean[] dp = new boolean[n + 1]; + dp[0] = true; + + for (int i = n - 1; i >= 0; i--) { + boolean[] newDp = new boolean[n + 1]; + for (int open = 0; open < n; open++) { + if (s.charAt(i) == '*') { + newDp[open] = dp[open + 1] || + (open > 0 && dp[open - 1]) || dp[open]; + } else if (s.charAt(i) == '(') { + newDp[open] = dp[open + 1]; + } else if (open > 0) { + newDp[open] = dp[open - 1]; + } + } + dp = newDp; + } + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + bool checkValidString(string s) { + int n = s.size(); + vector dp(n + 1, false); + dp[0] = true; + + for (int i = n - 1; i >= 0; --i) { + vector newDp(n + 1, false); + for (int open = 0; open < n; ++open) { + if (s[i] == '*') { + newDp[open] = dp[open + 1] || + (open > 0 && dp[open - 1]) || dp[open]; + } else if (s[i] == '(') { + newDp[open] = dp[open + 1]; + } else if (open > 0) { + newDp[open] = dp[open - 1]; + } + } + dp = newDp; + } + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + checkValidString(s) { + const n = s.length; + let dp = Array(n + 1).fill(false); + dp[0] = true; + + for (let i = n - 1; i >= 0; i--) { + const newDp = Array(n + 1).fill(false); + for (let open = 0; open < n; open++) { + if (s[i] === '*') { + newDp[open] = dp[open + 1] || + (open > 0 && dp[open - 1]) || dp[open]; + } else if (s[i] === '(') { + newDp[open] = dp[open + 1]; + } else if (open > 0) { + newDp[open] = dp[open - 1]; + } + } + dp = newDp; + } + return dp[0]; + } +} +``` + +```csharp +public class Solution { + public bool CheckValidString(string s) { + int n = s.Length; + bool[] dp = new bool[n + 1]; + dp[0] = true; + + for (int i = n - 1; i >= 0; i--) { + bool[] newDp = new bool[n + 1]; + for (int open = 0; open < n; open++) { + if (s[i] == '*') { + newDp[open] = dp[open + 1] || + (open > 0 && dp[open - 1]) || dp[open]; + } else if (s[i] == '(') { + newDp[open] = dp[open + 1]; + } else if (open > 0) { + newDp[open] = dp[open - 1]; + } + } + dp = newDp; + } + return dp[0]; + } +} +``` + +```go +func checkValidString(s string) bool { + n := len(s) + dp := make([]bool, n+1) + dp[0] = true + + for i := n - 1; i >= 0; i-- { + newDp := make([]bool, n+1) + for open := 0; open < n; open++ { + if s[i] == '*' { + newDp[open] = (dp[open+1] || + (open > 0 && dp[open-1]) || + dp[open]) + } else if s[i] == '(' { + newDp[open] = dp[open+1] + } else if open > 0 { + newDp[open] = dp[open-1] + } + } + dp = newDp + } + return dp[0] +} +``` + +```kotlin +class Solution { + fun checkValidString(s: String): Boolean { + val n = s.length + var dp = BooleanArray(n + 1) + dp[0] = true + + for (i in n - 1 downTo 0) { + val newDp = BooleanArray(n + 1) + for (open in 0 until n) { + newDp[open] = when (s[i]) { + '*' -> (dp[open + 1] || + (open > 0 && dp[open - 1]) || + dp[open]) + '(' -> dp[open + 1] + else -> open > 0 && dp[open - 1] + } + } + dp = newDp + } + return dp[0] + } +} +``` + +```swift +class Solution { + func checkValidString(_ s: String) -> Bool { + let n = s.count + var dp = Array(repeating: false, count: n + 1) + dp[0] = true + + let chars = Array(s) + + for i in stride(from: n - 1, through: 0, by: -1) { + var new_dp = Array(repeating: false, count: n + 1) + for open in 0.. 0 && dp[open - 1]) || dp[open] + } else if chars[i] == "(" { + new_dp[open] = dp[open + 1] + } else if open > 0 { + new_dp[open] = dp[open - 1] + } + } + dp = new_dp + } + + return dp[0] + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 5. Stack + +::tabs-start + +```python +class Solution: + def checkValidString(self, s: str) -> bool: + left = [] + star = [] + for i, ch in enumerate(s): + if ch == '(': + left.append(i) + elif ch == '*': + star.append(i) + else: + if not left and not star: + return False + if left: + left.pop() + else: + star.pop() + + while left and star: + if left.pop() > star.pop(): + return False + return not left +``` + +```java +public class Solution { + public boolean checkValidString(String s) { + Stack left = new Stack<>(); + Stack star = new Stack<>(); + for (int i = 0; i < s.length(); i++) { + char ch = s.charAt(i); + if (ch == '(') { + left.push(i); + } else if (ch == '*') { + star.push(i); + } else { + if (left.isEmpty() && star.isEmpty()) return false; + if (!left.isEmpty()) { + left.pop(); + } else{ + star.pop(); + } + } + } + while (!left.isEmpty() && !star.isEmpty()) { + if (left.pop() > star.pop()) + return false; + } + return left.isEmpty(); + } +} +``` + +```cpp +class Solution { +public: + bool checkValidString(string s) { + stack left, star; + for (int i = 0; i < s.size(); ++i) { + if (s[i] == '(') { + left.push(i); + } else if (s[i] == '*') { + star.push(i); + } else { + if (left.empty() && star.empty()) return false; + if (!left.empty()) { + left.pop(); + } else { + star.pop(); + } + } + } + + while (!left.empty() && !star.empty()) { + if (left.top() > star.top()) return false; + left.pop(); + star.pop(); + } + return left.empty(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + checkValidString(s) { + const left = []; + const star = []; + for (let i = 0; i < s.length; i++) { + const ch = s[i]; + if (ch === '(') { + left.push(i); + } else if (ch === '*') { + star.push(i); + } else { + if (left.length === 0 && star.length === 0) { + return false; + } + if (left.length > 0) { + left.pop(); + } else { + star.pop(); + } + } + } + + while (left.length > 0 && star.length > 0) { + if (left.pop() > star.pop()) return false; + } + return left.length === 0; + } +} +``` + +```csharp +public class Solution { + public bool CheckValidString(string s) { + Stack left = new Stack(); + Stack star = new Stack(); + for (int i = 0; i < s.Length; i++) { + char ch = s[i]; + if (ch == '(') { + left.Push(i); + } else if (ch == '*') { + star.Push(i); + } else { + if (left.Count == 0 && star.Count == 0) return false; + if (left.Count > 0) { + left.Pop(); + } else { + star.Pop(); + } + } + } + + while (left.Count > 0 && star.Count > 0) { + if (left.Pop() > star.Pop()) return false; + } + return left.Count == 0; + } +} +``` + +```go +func checkValidString(s string) bool { + var left, star []int + for i, ch := range s { + if ch == '(' { + left = append(left, i) + } else if ch == '*' { + star = append(star, i) + } else { + if len(left) == 0 && len(star) == 0 { + return false + } + if len(left) > 0 { + left = left[:len(left)-1] + } else { + star = star[:len(star)-1] + } + } + } + + for len(left) > 0 && len(star) > 0 { + if left[len(left)-1] > star[len(star)-1] { + return false + } + left = left[:len(left)-1] + star = star[:len(star)-1] + } + return len(left) == 0 +} +``` + +```kotlin +class Solution { + fun checkValidString(s: String): Boolean { + val left = ArrayDeque() + val star = ArrayDeque() + + for ((i, ch) in s.withIndex()) { + when (ch) { + '(' -> left.addLast(i) + '*' -> star.addLast(i) + ')' -> { + if (left.isEmpty() && star.isEmpty()) return false + if (left.isNotEmpty()) left.removeLast() + else star.removeLast() + } + } + } + + while (left.isNotEmpty() && star.isNotEmpty()) { + if (left.last() > star.last()) return false + left.removeLast() + star.removeLast() + } + return left.isEmpty() + } +} +``` + +```swift +class Solution { + func checkValidString(_ s: String) -> Bool { + var left = [Int]() + var star = [Int]() + + let chars = Array(s) + + for (i, ch) in chars.enumerated() { + if ch == "(" { + left.append(i) + } else if ch == "*" { + star.append(i) + } else { + if left.isEmpty && star.isEmpty { + return false + } + if !left.isEmpty { + left.popLast() + } else { + star.popLast() + } + } + } + + while !left.isEmpty && !star.isEmpty { + if left.last! > star.last! { + return false + } + left.popLast() + star.popLast() + } + + return left.isEmpty + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 6. Greedy + +::tabs-start + +```python +class Solution: + def checkValidString(self, s: str) -> bool: + leftMin, leftMax = 0, 0 + + for c in s: + if c == "(": + leftMin, leftMax = leftMin + 1, leftMax + 1 + elif c == ")": + leftMin, leftMax = leftMin - 1, leftMax - 1 + else: + leftMin, leftMax = leftMin - 1, leftMax + 1 + if leftMax < 0: + return False + if leftMin < 0: + leftMin = 0 + return leftMin == 0 +``` + +```java +public class Solution { + public boolean checkValidString(String s) { + int leftMin = 0, leftMax = 0; + + for (char c : s.toCharArray()) { + if (c == '(') { + leftMin++; + leftMax++; + } else if (c == ')') { + leftMin--; + leftMax--; + } else { + leftMin--; + leftMax++; + } + if (leftMax < 0) { + return false; + } + if (leftMin < 0) { + leftMin = 0; + } + } + return leftMin == 0; + } +} +``` + +```cpp +class Solution { +public: + bool checkValidString(string s) { + int leftMin = 0, leftMax = 0; + + for (char c : s) { + if (c == '(') { + leftMin++; + leftMax++; + } else if (c == ')') { + leftMin--; + leftMax--; + } else { + leftMin--; + leftMax++; + } + if (leftMax < 0) { + return false; + } + if (leftMin < 0) { + leftMin = 0; + } + } + return leftMin == 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + checkValidString(s) { + let leftMin = 0; + let leftMax = 0; + + for (const c of s) { + if (c === '(') { + leftMin++; + leftMax++; + } else if (c === ')') { + leftMin--; + leftMax--; + } else { + leftMin--; + leftMax++; + } + if (leftMax < 0) { + return false; + } + if (leftMin < 0) { + leftMin = 0; + } + } + return leftMin === 0; + } +} +``` + +```csharp +public class Solution { + public bool CheckValidString(string s) { + int leftMin = 0, leftMax = 0; + + foreach (char c in s) { + if (c == '(') { + leftMin++; + leftMax++; + } else if (c == ')') { + leftMin--; + leftMax--; + } else { + leftMin--; + leftMax++; + } + if (leftMax < 0) { + return false; + } + if (leftMin < 0) { + leftMin = 0; + } + } + return leftMin == 0; + } +} +``` + +```go +func checkValidString(s string) bool { + leftMin, leftMax := 0, 0 + + for _, c := range s { + if c == '(' { + leftMin, leftMax = leftMin+1, leftMax+1 + } else if c == ')' { + leftMin, leftMax = leftMin-1, leftMax-1 + } else { + leftMin, leftMax = leftMin-1, leftMax+1 + } + if leftMax < 0 { + return false + } + if leftMin < 0 { + leftMin = 0 + } + } + return leftMin == 0 +} +``` + +```kotlin +class Solution { + fun checkValidString(s: String): Boolean { + var leftMin = 0 + var leftMax = 0 + + for (c in s) { + when (c) { + '(' -> { + leftMin++ + leftMax++ + } + ')' -> { + leftMin-- + leftMax-- + } + else -> { + leftMin-- + leftMax++ + } + } + if (leftMax < 0) return false + if (leftMin < 0) leftMin = 0 + } + return leftMin == 0 + } +} +``` + +```swift +class Solution { + func checkValidString(_ s: String) -> Bool { + var leftMin = 0 + var leftMax = 0 + + for c in s { + if c == "(" { + leftMin += 1 + leftMax += 1 + } else if c == ")" { + leftMin -= 1 + leftMax -= 1 + } else { + leftMin -= 1 + leftMax += 1 + } + if leftMax < 0 { + return false + } + if leftMin < 0 { + leftMin = 0 + } + } + return leftMin == 0 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/valid-perfect-square.md b/articles/valid-perfect-square.md new file mode 100644 index 000000000..c5682c5d4 --- /dev/null +++ b/articles/valid-perfect-square.md @@ -0,0 +1,447 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def isPerfectSquare(self, num: int) -> bool: + for i in range(1, num + 1): + sq = i * i + if sq > num: + return False + if sq == num: + return True +``` + +```java +public class Solution { + public boolean isPerfectSquare(int num) { + for (long i = 1; i <= num; i++) { + long sq = i * i; + if (sq > num) { + return false; + } + if (sq == num) { + return true; + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool isPerfectSquare(int num) { + for (long long i = 1; i <= num; i++) { + long long sq = i * i; + if (sq > num) { + return false; + } + if (sq == num) { + return true; + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} num + * @return {boolean} + */ + isPerfectSquare(num) { + for (let i = 1; i <= num; i++) { + let sq = i * i; + if (sq > num) { + return false; + } + if (sq === num) { + return true; + } + } + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\sqrt {n})$ +* Space complexity: $O(1)$ + +--- + +## 2. In-Built Function + +::tabs-start + +```python +class Solution: + def isPerfectSquare(self, num: int) -> bool: + sqRoot = int(sqrt(num)) + return sqRoot * sqRoot == num +``` + +```java +public class Solution { + public boolean isPerfectSquare(int num) { + int sqRoot = (int) Math.sqrt(num); + return sqRoot * sqRoot == num; + } +} +``` + +```cpp +class Solution { +public: + bool isPerfectSquare(int num) { + int sqRoot = (int) sqrt(num); + return sqRoot * sqRoot == num; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} num + * @return {boolean} + */ + isPerfectSquare(num) { + let sqRoot = Math.floor(Math.sqrt(num)); + return sqRoot * sqRoot === num; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ +* Space complexity: $O(1)$ + +--- + +## 3. Binary Search + +::tabs-start + +```python +class Solution: + def isPerfectSquare(self, num: int) -> bool: + l, r = 1, num + + while l <= r: + m = l + (r - l) // 2 + sq = m * m + if sq > num: + r = m - 1 + elif sq < num: + l = m + 1 + else: + return True + + return False +``` + +```java +public class Solution { + public boolean isPerfectSquare(int num) { + long l = 1, r = num; + + while (l <= r) { + long m = l + (r - l) / 2; + long sq = m * m; + if (sq > num) { + r = m - 1; + } else if (sq < num) { + l = m + 1; + } else { + return true; + } + } + + return false; + } +} +``` + +```cpp +class Solution { +public: + bool isPerfectSquare(int num) { + long long l = 1, r = num; + + while (l <= r) { + long long m = l + (r - l) / 2; + long long sq = m * m; + if (sq > num) { + r = m - 1; + } else if (sq < num) { + l = m + 1; + } else { + return true; + } + } + + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} num + * @return {boolean} + */ + isPerfectSquare(num) { + let l = 1, r = num; + + while (l <= r) { + let m = Math.floor(l + (r - l) / 2); + let sq = m * m; + if (sq > num) { + r = m - 1; + } else if (sq < num) { + l = m + 1; + } else { + return true; + } + } + + return false; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Math + +::tabs-start + +```python +class Solution: + def isPerfectSquare(self, num: int) -> bool: + i = 1 + while num > 0: + num -= i + i += 2 + return num == 0 +``` + +```java +public class Solution { + public boolean isPerfectSquare(int num) { + int i = 1; + while (num > 0) { + num -= i; + i += 2; + } + return num == 0; + } +} +``` + +```cpp +class Solution { +public: + bool isPerfectSquare(int num) { + int i = 1; + while (num > 0) { + num -= i; + i += 2; + } + return num == 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} num + * @return {boolean} + */ + isPerfectSquare(num) { + let i = 1; + while (num > 0) { + num -= i; + i += 2; + } + return num === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\sqrt {n})$ +* Space complexity: $O(1)$ + +--- + +## 5. Newton's Method + +::tabs-start + +```python +class Solution: + def isPerfectSquare(self, num: int) -> bool: + r = num + while r * r > num: + r = (r + (num // r)) // 2 + return r * r == num +``` + +```java +public class Solution { + public boolean isPerfectSquare(int num) { + long r = num; + while (r * r > num) { + r = (r + num / r) / 2; + } + return r * r == num; + } +} +``` + +```cpp +class Solution { +public: + bool isPerfectSquare(int num) { + long long r = num; + while (r * r > num) { + r = (r + num / r) / 2; + } + return r * r == num; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} num + * @return {boolean} + */ + isPerfectSquare(num) { + let r = num; + while (r * r > num) { + r = Math.floor((r + Math.floor(num / r)) / 2); + } + return r * r === num; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(\log n)$ +* Space complexity: $O(1)$ + +--- + +## 6. Bit Manipulation + +::tabs-start + +```python +class Solution: + def isPerfectSquare(self, num: int) -> bool: + r, mask = 0, 1 << 15 + + while mask > 0: + r |= mask + if r > (num // r): + r ^= mask + mask >>= 1 + + return r * r == num +``` + +```java +public class Solution { + public boolean isPerfectSquare(int num) { + int r = 0, mask = 1 << 15; + + while (mask > 0) { + r |= mask; + if (r > (num / r)) { + r ^= mask; + } + mask >>= 1; + } + + return r * r == num; + } +} +``` + +```cpp +class Solution { +public: + bool isPerfectSquare(int num) { + int r = 0, mask = 1 << 15; + + while (mask > 0) { + r |= mask; + if (r > (num / r)) { + r ^= mask; + } + mask >>= 1; + } + + return r * r == num; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} num + * @return {boolean} + */ + isPerfectSquare(num) { + let r = 0, mask = 1 << 15; + + while (mask > 0) { + r |= mask; + if (r > Math.floor(num / r)) { + r ^= mask; + } + mask >>= 1; + } + + return r * r === num; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(1)$ since we iterate at most $15$ times. +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/valid-sudoku.md b/articles/valid-sudoku.md new file mode 100644 index 000000000..74fb5deda --- /dev/null +++ b/articles/valid-sudoku.md @@ -0,0 +1,831 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def isValidSudoku(self, board: List[List[str]]) -> bool: + for row in range(9): + seen = set() + for i in range(9): + if board[row][i] == ".": + continue + if board[row][i] in seen: + return False + seen.add(board[row][i]) + + for col in range(9): + seen = set() + for i in range(9): + if board[i][col] == ".": + continue + if board[i][col] in seen: + return False + seen.add(board[i][col]) + + for square in range(9): + seen = set() + for i in range(3): + for j in range(3): + row = (square//3) * 3 + i + col = (square % 3) * 3 + j + if board[row][col] == ".": + continue + if board[row][col] in seen: + return False + seen.add(board[row][col]) + return True +``` + +```java +public class Solution { + public boolean isValidSudoku(char[][] board) { + for (int row = 0; row < 9; row++) { + Set seen = new HashSet<>(); + for (int i = 0; i < 9; i++) { + if (board[row][i] == '.') continue; + if (seen.contains(board[row][i])) return false; + seen.add(board[row][i]); + } + } + + for (int col = 0; col < 9; col++) { + Set seen = new HashSet<>(); + for (int i = 0; i < 9; i++) { + if (board[i][col] == '.') continue; + if (seen.contains(board[i][col])) return false; + seen.add(board[i][col]); + } + } + + for (int square = 0; square < 9; square++) { + Set seen = new HashSet<>(); + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + int row = (square / 3) * 3 + i; + int col = (square % 3) * 3 + j; + if (board[row][col] == '.') continue; + if (seen.contains(board[row][col])) return false; + seen.add(board[row][col]); + } + } + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isValidSudoku(vector>& board) { + for (int row = 0; row < 9; row++) { + unordered_set seen; + for (int i = 0; i < 9; i++) { + if (board[row][i] == '.') continue; + if (seen.count(board[row][i])) return false; + seen.insert(board[row][i]); + } + } + + for (int col = 0; col < 9; col++) { + unordered_set seen; + for (int i = 0; i < 9; i++) { + if (board[i][col] == '.') continue; + if (seen.count(board[i][col])) return false; + seen.insert(board[i][col]); + } + } + + for (int square = 0; square < 9; square++) { + unordered_set seen; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + int row = (square / 3) * 3 + i; + int col = (square % 3) * 3 + j; + if (board[row][col] == '.') continue; + if (seen.count(board[row][col])) return false; + seen.insert(board[row][col]); + } + } + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[][]} board + * @return {boolean} + */ + isValidSudoku(board) { + for (let row = 0; row < 9; row++) { + let seen = new Set(); + for (let i = 0; i < 9; i++) { + if (board[row][i] === '.') continue; + if (seen.has(board[row][i])) return false; + seen.add(board[row][i]); + } + } + + for (let col = 0; col < 9; col++) { + let seen = new Set(); + for (let i = 0; i < 9; i++) { + if (board[i][col] === '.') continue; + if (seen.has(board[i][col])) return false; + seen.add(board[i][col]); + } + } + + for (let square = 0; square < 9; square++) { + let seen = new Set(); + for (let i = 0; i < 3; i++) { + for (let j = 0; j < 3; j++) { + let row = Math.floor(square / 3) * 3 + i; + let col = (square % 3) * 3 + j; + if (board[row][col] === '.') continue; + if (seen.has(board[row][col])) return false; + seen.add(board[row][col]); + } + } + } + + return true; + } +} +``` + +```csharp +public class Solution { + public bool IsValidSudoku(char[][] board) { + for (int row = 0; row < 9; row++) { + HashSet seen = new HashSet(); + for (int i = 0; i < 9; i++) { + if (board[row][i] == '.') continue; + if (seen.Contains(board[row][i])) return false; + seen.Add(board[row][i]); + } + } + + for (int col = 0; col < 9; col++) { + HashSet seen = new HashSet(); + for (int i = 0; i < 9; i++) { + if (board[i][col] == '.') continue; + if (seen.Contains(board[i][col])) return false; + seen.Add(board[i][col]); + } + } + + for (int square = 0; square < 9; square++) { + HashSet seen = new HashSet(); + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + int row = (square / 3) * 3 + i; + int col = (square % 3) * 3 + j; + if (board[row][col] == '.') continue; + if (seen.Contains(board[row][col])) return false; + seen.Add(board[row][col]); + } + } + } + + return true; + } +} +``` + +```go +func isValidSudoku(board [][]byte) bool { + for row := 0; row < 9; row++ { + seen := make(map[byte]bool) + for i := 0; i < 9; i++ { + if board[row][i] == '.' { + continue + } + if seen[board[row][i]] { + return false + } + seen[board[row][i]] = true + } + } + + for col := 0; col < 9; col++ { + seen := make(map[byte]bool) + for i := 0; i < 9; i++ { + if board[i][col] == '.' { + continue + } + if seen[board[i][col]] { + return false + } + seen[board[i][col]] = true + } + } + + for square := 0; square < 9; square++ { + seen := make(map[byte]bool) + for i := 0; i < 3; i++ { + for j := 0; j < 3; j++ { + row := (square / 3) * 3 + i + col := (square % 3) * 3 + j + if board[row][col] == '.' { + continue + } + if seen[board[row][col]] { + return false + } + seen[board[row][col]] = true + } + } + } + return true +} +``` + +```kotlin +class Solution { + fun isValidSudoku(board: Array): Boolean { + for (row in 0 until 9) { + val seen = mutableSetOf() + for (i in 0 until 9) { + if (board[row][i] == '.') continue + if (board[row][i] in seen) return false + seen.add(board[row][i]) + } + } + + for (col in 0 until 9) { + val seen = mutableSetOf() + for (i in 0 until 9) { + if (board[i][col] == '.') continue + if (board[i][col] in seen) return false + seen.add(board[i][col]) + } + } + + for (square in 0 until 9) { + val seen = mutableSetOf() + for (i in 0 until 3) { + for (j in 0 until 3) { + val row = (square / 3) * 3 + i + val col = (square % 3) * 3 + j + if (board[row][col] == '.') continue + if (board[row][col] in seen) return false + seen.add(board[row][col]) + } + } + } + return true + } +} +``` + +```swift +class Solution { + func isValidSudoku(_ board: [[Character]]) -> Bool { + for row in 0..<9 { + var seen = Set() + for i in 0..<9 { + if board[row][i] == "." { continue } + if seen.contains(board[row][i]) { return false } + seen.insert(board[row][i]) + } + } + + for col in 0..<9 { + var seen = Set() + for i in 0..<9 { + if board[i][col] == "." { continue } + if seen.contains(board[i][col]) { return false } + seen.insert(board[i][col]) + } + } + + for square in 0..<9 { + var seen = Set() + for i in 0..<3 { + for j in 0..<3 { + let row = (square / 3) * 3 + i + let col = (square % 3) * 3 + j + if board[row][col] == "." { continue } + if seen.contains(board[row][col]) { return false } + seen.insert(board[row][col]) + } + } + } + + return true + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 2. Hash Set (One Pass) + +::tabs-start + +```python +class Solution: + def isValidSudoku(self, board: List[List[str]]) -> bool: + cols = defaultdict(set) + rows = defaultdict(set) + squares = defaultdict(set) + + for r in range(9): + for c in range(9): + if board[r][c] == ".": + continue + if ( board[r][c] in rows[r] + or board[r][c] in cols[c] + or board[r][c] in squares[(r // 3, c // 3)]): + return False + + cols[c].add(board[r][c]) + rows[r].add(board[r][c]) + squares[(r // 3, c // 3)].add(board[r][c]) + + return True +``` + +```java +public class Solution { + public boolean isValidSudoku(char[][] board) { + Map> cols = new HashMap<>(); + Map> rows = new HashMap<>(); + Map> squares = new HashMap<>(); + + for (int r = 0; r < 9; r++) { + for (int c = 0; c < 9; c++) { + if (board[r][c] == '.') continue; + + String squareKey = (r / 3) + "," + (c / 3); + + if (rows.computeIfAbsent(r, k -> new HashSet<>()).contains(board[r][c]) || + cols.computeIfAbsent(c, k -> new HashSet<>()).contains(board[r][c]) || + squares.computeIfAbsent(squareKey, k -> new HashSet<>()).contains(board[r][c])) { + return false; + } + + rows.get(r).add(board[r][c]); + cols.get(c).add(board[r][c]); + squares.get(squareKey).add(board[r][c]); + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isValidSudoku(vector>& board) { + unordered_map> rows, cols; + map, unordered_set> squares; + + for (int r = 0; r < 9; r++) { + for (int c = 0; c < 9; c++) { + if (board[r][c] == '.') continue; + + pair squareKey = {r / 3, c / 3}; + + if (rows[r].count(board[r][c]) || cols[c].count(board[r][c]) || squares[squareKey].count(board[r][c])) { + return false; + } + + rows[r].insert(board[r][c]); + cols[c].insert(board[r][c]); + squares[squareKey].insert(board[r][c]); + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {character[][]} board + * @return {boolean} + */ + isValidSudoku(board) { + const cols = new Map(); + const rows = new Map(); + const squares = new Map(); + + for (let r = 0; r < 9; r++) { + for (let c = 0; c < 9; c++) { + if (board[r][c] === '.') continue; + + const squareKey = `${Math.floor(r / 3)},${Math.floor(c / 3)}`; + + if ((rows.get(r) && rows.get(r).has(board[r][c])) || + (cols.get(c) && cols.get(c).has(board[r][c])) || + (squares.get(squareKey) && squares.get(squareKey).has(board[r][c]))) { + return false; + } + + if (!rows.has(r)) rows.set(r, new Set()); + if (!cols.has(c)) cols.set(c, new Set()); + if (!squares.has(squareKey)) squares.set(squareKey, new Set()); + + rows.get(r).add(board[r][c]); + cols.get(c).add(board[r][c]); + squares.get(squareKey).add(board[r][c]); + } + } + return true; + } +} +``` + +```csharp +public class Solution { + public bool IsValidSudoku(char[][] board) { + Dictionary> cols = new Dictionary>(); + Dictionary> rows = new Dictionary>(); + Dictionary> squares = new Dictionary>(); + + for (int r = 0; r < 9; r++) { + for (int c = 0; c < 9; c++) { + if (board[r][c] == '.') continue; + + string squareKey = (r / 3) + "," + (c / 3); + + if ((rows.ContainsKey(r) && rows[r].Contains(board[r][c])) || + (cols.ContainsKey(c) && cols[c].Contains(board[r][c])) || + (squares.ContainsKey(squareKey) && squares[squareKey].Contains(board[r][c]))) { + return false; + } + + if (!rows.ContainsKey(r)) rows[r] = new HashSet(); + if (!cols.ContainsKey(c)) cols[c] = new HashSet(); + if (!squares.ContainsKey(squareKey)) squares[squareKey] = new HashSet(); + + rows[r].Add(board[r][c]); + cols[c].Add(board[r][c]); + squares[squareKey].Add(board[r][c]); + } + } + return true; + } +} +``` + +```go +func isValidSudoku(board [][]byte) bool { + rows := make([]map[byte]bool, 9) + cols := make([]map[byte]bool, 9) + squares := make([]map[byte]bool, 9) + + for i := 0; i < 9; i++ { + rows[i] = make(map[byte]bool) + cols[i] = make(map[byte]bool) + squares[i] = make(map[byte]bool) + } + + for r := 0; r < 9; r++ { + for c := 0; c < 9; c++ { + if board[r][c] == '.' { + continue + } + val := board[r][c] + squareIdx := (r/3)*3 + c/3 + + if rows[r][val] || cols[c][val] || + squares[squareIdx][val] { + return false + } + + rows[r][val] = true + cols[c][val] = true + squares[squareIdx][val] = true + } + } + + return true +} +``` + +```kotlin +class Solution { + fun isValidSudoku(board: Array): Boolean { + val rows = Array(9) { HashSet() } + val cols = Array(9) { HashSet() } + val squares = Array(9) { HashSet() } + + for (r in 0 until 9) { + for (c in 0 until 9) { + val value = board[r][c] + if (value == '.') continue + + val squareIdx = (r / 3) * 3 + (c / 3) + if (value in rows[r] || value in cols[c] || + value in squares[squareIdx]) { + return false + } + + rows[r].add(value) + cols[c].add(value) + squares[squareIdx].add(value) + } + } + + return true + } +} +``` + +```swift +class Solution { + func isValidSudoku(_ board: [[Character]]) -> Bool { + var cols = [Int: Set]() + var rows = [Int: Set]() + var squares = [String: Set]() + + for r in 0..<9 { + for c in 0..<9 { + if board[r][c] == "." { continue } + + let squareKey = "\(r / 3),\(c / 3)" + + if rows[r]?.contains(board[r][c]) == true || + cols[c]?.contains(board[r][c]) == true || + squares[squareKey]?.contains(board[r][c]) == true { + return false + } + + rows[r, default: []].insert(board[r][c]) + cols[c, default: []].insert(board[r][c]) + squares[squareKey, default: []].insert(board[r][c]) + } + } + + return true + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n ^ 2)$ + +--- + +## 3. Bitmask + +::tabs-start + +```python +class Solution: + def isValidSudoku(self, board: List[List[str]]) -> bool: + rows = [0] * 9 + cols = [0] * 9 + squares = [0] * 9 + + for r in range(9): + for c in range(9): + if board[r][c] == ".": + continue + + val = int(board[r][c]) - 1 + if (1 << val) & rows[r]: + return False + if (1 << val) & cols[c]: + return False + if (1 << val) & squares[(r // 3) * 3 + (c // 3)]: + return False + + rows[r] |= (1 << val) + cols[c] |= (1 << val) + squares[(r // 3) * 3 + (c // 3)] |= (1 << val) + + return True +``` + +```java +public class Solution { + public boolean isValidSudoku(char[][] board) { + int[] rows = new int[9]; + int[] cols = new int[9]; + int[] squares = new int[9]; + + for (int r = 0; r < 9; r++) { + for (int c = 0; c < 9; c++) { + if (board[r][c] == '.') continue; + + int val = board[r][c] - '1'; + + if ((rows[r] & (1 << val)) > 0 || (cols[c] & (1 << val)) > 0 || + (squares[(r / 3) * 3 + (c / 3)] & (1 << val)) > 0) { + return false; + } + + rows[r] |= (1 << val); + cols[c] |= (1 << val); + squares[(r / 3) * 3 + (c / 3)] |= (1 << val); + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isValidSudoku(vector>& board) { + int rows[9] = {0}; + int cols[9] = {0}; + int squares[9] = {0}; + + for (int r = 0; r < 9; ++r) { + for (int c = 0; c < 9; ++c) { + if (board[r][c] == '.') continue; + + int val = board[r][c] - '1'; + + if ((rows[r] & (1 << val)) || (cols[c] & (1 << val)) || + (squares[(r / 3) * 3 + (c / 3)] & (1 << val))) { + return false; + } + + rows[r] |= (1 << val); + cols[c] |= (1 << val); + squares[(r / 3) * 3 + (c / 3)] |= (1 << val); + } + } + return true; + } +}; +``` + +```javascript +class Solution { + isValidSudoku(board) { + let rows = new Array(9).fill(0); + let cols = new Array(9).fill(0); + let squares = new Array(9).fill(0); + + for (let r = 0; r < 9; r++) { + for (let c = 0; c < 9; c++) { + if (board[r][c] === '.') continue; + + let val = board[r][c] - '1'; + + if ((rows[r] & (1 << val)) || (cols[c] & (1 << val)) || + (squares[Math.floor(r / 3) * 3 + Math.floor(c / 3)] & (1 << val))) { + return false; + } + + rows[r] |= (1 << val); + cols[c] |= (1 << val); + squares[Math.floor(r / 3) * 3 + Math.floor(c / 3)] |= (1 << val); + } + } + return true; + } +} +``` + +```csharp +public class Solution { + public bool IsValidSudoku(char[][] board) { + int[] rows = new int[9]; + int[] cols = new int[9]; + int[] squares = new int[9]; + + for (int r = 0; r < 9; r++) { + for (int c = 0; c < 9; c++) { + if (board[r][c] == '.') continue; + + int val = board[r][c] - '1'; + + if ((rows[r] & (1 << val)) > 0 || (cols[c] & (1 << val)) > 0 || + (squares[(r / 3) * 3 + (c / 3)] & (1 << val)) > 0) { + return false; + } + + rows[r] |= (1 << val); + cols[c] |= (1 << val); + squares[(r / 3) * 3 + (c / 3)] |= (1 << val); + } + } + return true; + } +} +``` + +```go +func isValidSudoku(board [][]byte) bool { + rows := make([]int, 9) + cols := make([]int, 9) + squares := make([]int, 9) + + for r := 0; r < 9; r++ { + for c := 0; c < 9; c++ { + if board[r][c] == '.' { + continue + } + + val := board[r][c] - '1' + bit := 1 << val + squareIdx := (r/3)*3 + c/3 + + if rows[r]&bit != 0 || cols[c]&bit != 0 || + squares[squareIdx]&bit != 0 { + return false + } + + rows[r] |= bit + cols[c] |= bit + squares[squareIdx] |= bit + } + } + + return true +} +``` + +```kotlin +class Solution { + fun isValidSudoku(board: Array): Boolean { + val rows = IntArray(9) + val cols = IntArray(9) + val squares = IntArray(9) + + for (r in 0 until 9) { + for (c in 0 until 9) { + if (board[r][c] == '.') continue + + val value = board[r][c] - '1' + val bit = 1 shl value + val squareIdx = (r / 3) * 3 + (c / 3) + + if ((rows[r] and bit) != 0 || (cols[c] and bit) != 0 || + (squares[squareIdx] and bit) != 0) { + return false + } + + rows[r] = rows[r] or bit + cols[c] = cols[c] or bit + squares[squareIdx] = squares[squareIdx] or bit + } + } + + return true + } +} +``` + +```swift +class Solution { + func isValidSudoku(_ board: [[Character]]) -> Bool { + var rows = [Int](repeating: 0, count: 9) + var cols = [Int](repeating: 0, count: 9) + var squares = [Int](repeating: 0, count: 9) + + for r in 0..<9 { + for c in 0..<9 { + if board[r][c] == "." { continue } + + let val = Int(board[r][c].asciiValue! - Character("0").asciiValue!) + let bitmask = 1 << (val - 1) + + if (rows[r] & bitmask) != 0 { return false } + if (cols[c] & bitmask) != 0 { return false } + if (squares[(r / 3) * 3 + (c / 3)] & bitmask) != 0 { return false } + + rows[r] |= bitmask + cols[c] |= bitmask + squares[(r / 3) * 3 + (c / 3)] |= bitmask + } + } + + return true + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/valid-tree.md b/articles/valid-tree.md new file mode 100644 index 000000000..7266d9836 --- /dev/null +++ b/articles/valid-tree.md @@ -0,0 +1,1078 @@ +## 1. Cycle Detection (DFS) + +::tabs-start + +```python +class Solution: + def validTree(self, n: int, edges: List[List[int]]) -> bool: + if len(edges) > (n - 1): + return False + + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + adj[v].append(u) + + visit = set() + def dfs(node, par): + if node in visit: + return False + + visit.add(node) + for nei in adj[node]: + if nei == par: + continue + if not dfs(nei, node): + return False + return True + + return dfs(0, -1) and len(visit) == n +``` + +```java +public class Solution { + public boolean validTree(int n, int[][] edges) { + if (edges.length > n - 1) { + return false; + } + + List> adj = new ArrayList<>(); + for (int i = 0; i < n; i++) { + adj.add(new ArrayList<>()); + } + + for (int[] edge : edges) { + adj.get(edge[0]).add(edge[1]); + adj.get(edge[1]).add(edge[0]); + } + + Set visit = new HashSet<>(); + if (!dfs(0, -1, visit, adj)) { + return false; + } + + return visit.size() == n; + } + + private boolean dfs(int node, int parent, Set visit, + List> adj) { + if (visit.contains(node)) { + return false; + } + + visit.add(node); + for (int nei : adj.get(node)) { + if (nei == parent) { + continue; + } + if (!dfs(nei, node, visit, adj)) { + return false; + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool validTree(int n, vector>& edges) { + if (edges.size() > n - 1) { + return false; + } + + vector> adj(n); + for (const auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + unordered_set visit; + if (!dfs(0, -1, visit, adj)) { + return false; + } + + return visit.size() == n; + } + +private: + bool dfs(int node, int parent, unordered_set& visit, + vector>& adj) { + if (visit.count(node)) { + return false; + } + + visit.insert(node); + for (int nei : adj[node]) { + if (nei == parent) { + continue; + } + if (!dfs(nei, node, visit, adj)) { + return false; + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @returns {boolean} + */ + validTree(n, edges) { + if (edges.length > n - 1) { + return false; + } + + const adj = Array.from({ length: n }, () => []); + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + } + + const visit = new Set(); + const dfs = (node, parent) => { + if (visit.has(node)) { + return false; + } + + visit.add(node); + for (const nei of adj[node]) { + if (nei === parent) { + continue; + } + if (!dfs(nei, node)) { + return false; + } + } + return true; + }; + + return dfs(0, -1) && visit.size === n; + } +} +``` + +```csharp +public class Solution { + public bool ValidTree(int n, int[][] edges) { + if (edges.Length > n - 1) { + return false; + } + + List> adj = new List>(); + for (int i = 0; i < n; i++) { + adj.Add(new List()); + } + + foreach (var edge in edges) { + adj[edge[0]].Add(edge[1]); + adj[edge[1]].Add(edge[0]); + } + + HashSet visit = new HashSet(); + if (!Dfs(0, -1, visit, adj)) { + return false; + } + + return visit.Count == n; + } + + private bool Dfs(int node, int parent, HashSet visit, + List> adj) { + if (visit.Contains(node)) { + return false; + } + + visit.Add(node); + foreach (var nei in adj[node]) { + if (nei == parent) { + continue; + } + if (!Dfs(nei, node, visit, adj)) { + return false; + } + } + return true; + } +} +``` + +```go +func validTree(n int, edges [][]int) bool { + if len(edges) > n-1 { + return false + } + + adj := make([][]int, n) + for _, edge := range edges { + u, v := edge[0], edge[1] + adj[u] = append(adj[u], v) + adj[v] = append(adj[v], u) + } + + visit := make(map[int]bool) + var dfs func(node, parent int) bool + dfs = func(node, parent int) bool { + if visit[node] { + return false + } + visit[node] = true + for _, nei := range adj[node] { + if nei == parent { + continue + } + if !dfs(nei, node) { + return false + } + } + return true + } + + return dfs(0, -1) && len(visit) == n +} +``` + +```kotlin +class Solution { + fun validTree(n: Int, edges: Array): Boolean { + if (edges.size > n - 1) return false + + val adj = Array(n) { mutableListOf() } + for ((u, v) in edges) { + adj[u].add(v) + adj[v].add(u) + } + + val visit = HashSet() + + fun dfs(node: Int, parent: Int): Boolean { + if (node in visit) return false + visit.add(node) + for (nei in adj[node]) { + if (nei == parent) continue + if (!dfs(nei, node)) return false + } + return true + } + + return dfs(0, -1) && visit.size == n + } +} +``` + +```swift +class Solution { + func validTree(_ n: Int, _ edges: [[Int]]) -> Bool { + if edges.count > (n - 1) { + return false + } + + var adj = Array(repeating: [Int](), count: n) + for edge in edges { + let u = edge[0] + let v = edge[1] + adj[u].append(v) + adj[v].append(u) + } + + var visited = Set() + + func dfs(_ node: Int, _ parent: Int) -> Bool { + if visited.contains(node) { + return false + } + visited.insert(node) + for nei in adj[node] { + if nei == parent { + continue + } + if !dfs(nei, node) { + return false + } + } + return true + } + + return dfs(0, -1) && visited.count == n + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number vertices and $E$ is the number of edges in the graph. + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def validTree(self, n: int, edges: List[List[int]]) -> bool: + if len(edges) > n - 1: + return False + + adj = [[] for _ in range(n)] + for u, v in edges: + adj[u].append(v) + adj[v].append(u) + + visit = set() + q = deque([(0, -1)]) # (current node, parent node) + visit.add(0) + + while q: + node, parent = q.popleft() + for nei in adj[node]: + if nei == parent: + continue + if nei in visit: + return False + visit.add(nei) + q.append((nei, node)) + + return len(visit) == n +``` + +```java +public class Solution { + public boolean validTree(int n, int[][] edges) { + if (edges.length > n - 1) { + return false; + } + + List> adj = new ArrayList<>(); + for (int i = 0; i < n; i++) { + adj.add(new ArrayList<>()); + } + + for (int[] edge : edges) { + adj.get(edge[0]).add(edge[1]); + adj.get(edge[1]).add(edge[0]); + } + + Set visit = new HashSet<>(); + Queue q = new LinkedList<>(); + q.offer(new int[]{0, -1}); // {current node, parent node} + visit.add(0); + + while (!q.isEmpty()) { + int[] pair = q.poll(); + int node = pair[0], parent = pair[1]; + for (int nei : adj.get(node)) { + if (nei == parent) { + continue; + } + if (visit.contains(nei)) { + return false; + } + visit.add(nei); + q.offer(new int[]{nei, node}); + } + } + + return visit.size() == n; + } +} +``` + +```cpp +class Solution { +public: + bool validTree(int n, vector>& edges) { + if (edges.size() > n - 1) { + return false; + } + + vector> adj(n); + for (const auto& edge : edges) { + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + unordered_set visit; + queue> q; + q.push({0, -1}); // {current node, parent node} + visit.insert(0); + + while (!q.empty()) { + auto [node, parent] = q.front(); + q.pop(); + for (int nei : adj[node]) { + if (nei == parent) { + continue; + } + if (visit.count(nei)) { + return false; + } + visit.insert(nei); + q.push({nei, node}); + } + } + + return visit.size() == n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @returns {boolean} + */ + validTree(n, edges) { + if (edges.length > n - 1) { + return false; + } + + const adj = Array.from({ length: n }, () => []); + for (const [u, v] of edges) { + adj[u].push(v); + adj[v].push(u); + } + + const visit = new Set(); + const q = new Queue([[0, -1]]); // [current node, parent node] + visit.add(0); + + while (!q.isEmpty()) { + const [node, parent] = q.pop(); + for (const nei of adj[node]) { + if (nei === parent) continue; + if (visit.has(nei)) return false; + visit.add(nei); + q.push([nei, node]); + } + } + + return visit.size === n; + } +} +``` + +```csharp +public class Solution { + public bool ValidTree(int n, int[][] edges) { + if (edges.Length > n - 1) { + return false; + } + + List> adj = new List>(); + for (int i = 0; i < n; i++) { + adj.Add(new List()); + } + + foreach (var edge in edges) { + adj[edge[0]].Add(edge[1]); + adj[edge[1]].Add(edge[0]); + } + + HashSet visit = new HashSet(); + Queue<(int, int)> q = new Queue<(int, int)>(); + q.Enqueue((0, -1)); // (current node, parent node) + visit.Add(0); + + while (q.Count > 0) { + var (node, parent) = q.Dequeue(); + foreach (var nei in adj[node]) { + if (nei == parent) { + continue; + } + if (visit.Contains(nei)) { + return false; + } + visit.Add(nei); + q.Enqueue((nei, node)); + } + } + + return visit.Count == n; + } +} +``` + +```go +func validTree(n int, edges [][]int) bool { + if len(edges) > n-1 { + return false + } + + adj := make([][]int, n) + for _, edge := range edges { + u, v := edge[0], edge[1] + adj[u] = append(adj[u], v) + adj[v] = append(adj[v], u) + } + + visit := make(map[int]bool) + q := [][2]int{{0, -1}} // (current node, parent node) + visit[0] = true + + for len(q) > 0 { + node, parent := q[0][0], q[0][1] + q = q[1:] + + for _, nei := range adj[node] { + if nei == parent { + continue + } + if visit[nei] { + return false + } + visit[nei] = true + q = append(q, [2]int{nei, node}) + } + } + + return len(visit) == n +} +``` + +```kotlin +class Solution { + fun validTree(n: Int, edges: Array): Boolean { + if (edges.size > n - 1) return false + + val adj = Array(n) { mutableListOf() } + for ((u, v) in edges) { + adj[u].add(v) + adj[v].add(u) + } + + val visit = mutableSetOf() + val q: Queue> = LinkedList() // Queue of (node, parent) + q.offer(0 to -1) + visit.add(0) + + while (q.isNotEmpty()) { + val (node, parent) = q.poll() + + for (nei in adj[node]) { + if (nei == parent) continue + if (nei in visit) return false + visit.add(nei) + q.offer(nei to node) + } + } + + return visit.size == n + } +} +``` + +```swift +class Solution { + func validTree(_ n: Int, _ edges: [[Int]]) -> Bool { + if edges.count > n - 1 { + return false + } + + var adj = [[Int]](repeating: [], count: n) + for edge in edges { + let u = edge[0] + let v = edge[1] + adj[u].append(v) + adj[v].append(u) + } + + var visit = Set() + var q = Deque<(Int, Int)>() // (current node, parent node) + q.append((0, -1)) + visit.insert(0) + + while !q.isEmpty { + let (node, parent) = q.removeFirst() + for nei in adj[node] { + if nei == parent { + continue + } + if visit.contains(nei) { + return false + } + visit.insert(nei) + q.append((nei, node)) + } + } + + return visit.count == n + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + E)$ +* Space complexity: $O(V + E)$ + +> Where $V$ is the number vertices and $E$ is the number of edges in the graph. + +--- + +## 3. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.comps = n + self.Parent = list(range(n + 1)) + self.Size = [1] * (n + 1) + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, u, v): + pu = self.find(u) + pv = self.find(v) + if pu == pv: + return False + + self.comps -= 1 + if self.Size[pu] < self.Size[pv]: + pu, pv = pv, pu + self.Size[pu] += self.Size[pv] + self.Parent[pv] = pu + return True + + def components(self): + return self.comps + +class Solution: + def validTree(self, n: int, edges: List[List[int]]) -> bool: + if len(edges) > n - 1: + return False + + dsu = DSU(n) + for u, v in edges: + if not dsu.union(u, v): + return False + return dsu.components() == 1 +``` + +```java +class DSU { + int[] Parent, Size; + int comps; + + public DSU(int n) { + comps = n; + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + public int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + public boolean union(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (Size[pu] < Size[pv]) { + int temp = pu; + pu = pv; + pv = temp; + } + comps--; + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } + + public int components() { + return comps; + } +} + +public class Solution { + public boolean validTree(int n, int[][] edges) { + if (edges.length > n - 1) { + return false; + } + + DSU dsu = new DSU(n); + for (int[] edge : edges) { + if (!dsu.union(edge[0], edge[1])) { + return false; + } + } + return dsu.components() == 1; + } +} +``` + +```cpp +class DSU { + vector Parent, Size; + int comps; +public: + DSU(int n) { + comps = n; + Parent.resize(n + 1); + Size.resize(n + 1); + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + bool unionNodes(int u, int v) { + int pu = find(u), pv = find(v); + if (pu == pv) return false; + if (Size[pu] < Size[pv]) { + swap(pu, pv); + } + comps--; + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } + + int components() { + return comps; + } +}; + +class Solution { +public: + bool validTree(int n, vector>& edges) { + if (edges.size() > n - 1) { + return false; + } + + DSU dsu(n); + for (auto& edge : edges) { + if (!dsu.unionNodes(edge[0], edge[1])) { + return false; + } + } + return dsu.components() == 1; + } +}; +``` + +```javascript +class DSU { + constructor(n) { + this.comps = n; + this.Parent = Array.from({ length: n + 1 }, (_, i) => i); + this.Size = Array(n + 1).fill(1); + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.Parent[node] !== node) { + this.Parent[node] = this.find(this.Parent[node]); + } + return this.Parent[node]; + } + + /** + * @param {number} u + * @param {number} v + * @return {boolean} + */ + union(u, v) { + let pu = this.find(u); + let pv = this.find(v); + if (pu === pv) return false; + if (this.Size[pu] < this.Size[pv]) { + [pu, pv] = [pv, pu]; + } + this.comps--; + this.Size[pu] += this.Size[pv]; + this.Parent[pv] = pu; + return true; + } + + /** + * @return {number} + */ + components() { + return this.comps; + } +} + +class Solution { + /** + * @param {number} n + * @param {number[][]} edges + * @returns {boolean} + */ + validTree(n, edges) { + if (edges.length > n - 1) { + return false; + } + + const dsu = new DSU(n); + for (const [u, v] of edges) { + if (!dsu.union(u, v)) { + return false; + } + } + return dsu.components() === 1; + } +} +``` + +```csharp +public class DSU { + private int[] Parent, Size; + private int comps; + + public DSU(int n) { + comps = n; + Parent = new int[n + 1]; + Size = new int[n + 1]; + for (int i = 0; i <= n; i++) { + Parent[i] = i; + Size[i] = 1; + } + } + + public int Find(int node) { + if (Parent[node] != node) { + Parent[node] = Find(Parent[node]); + } + return Parent[node]; + } + + public bool Union(int u, int v) { + int pu = Find(u), pv = Find(v); + if (pu == pv) return false; + if (Size[pu] < Size[pv]) { + int temp = pu; + pu = pv; + pv = temp; + } + comps--; + Size[pu] += Size[pv]; + Parent[pv] = pu; + return true; + } + + public int Components() { + return comps; + } +} + +public class Solution { + public bool ValidTree(int n, int[][] edges) { + if (edges.Length > n - 1) { + return false; + } + + DSU dsu = new DSU(n); + foreach (var edge in edges) { + if (!dsu.Union(edge[0], edge[1])) { + return false; + } + } + return dsu.Components() == 1; + } +} +``` + +```go +type DSU struct { + Parent []int + Size []int + Comps int +} + +func NewDSU(n int) *DSU { + parent := make([]int, n+1) + size := make([]int, n+1) + for i := 0; i <= n; i++ { + parent[i] = i + size[i] = 1 + } + return &DSU{Parent: parent, Size: size, Comps: n} +} + +func (dsu *DSU) Find(node int) int { + if dsu.Parent[node] != node { + dsu.Parent[node] = dsu.Find(dsu.Parent[node]) + } + return dsu.Parent[node] +} + +func (dsu *DSU) Union(u, v int) bool { + pu, pv := dsu.Find(u), dsu.Find(v) + if pu == pv { + return false + } + dsu.Comps-- + if dsu.Size[pu] < dsu.Size[pv] { + pu, pv = pv, pu + } + dsu.Size[pu] += dsu.Size[pv] + dsu.Parent[pv] = pu + return true +} + +func (dsu *DSU) Components() int { + return dsu.Comps +} + +func validTree(n int, edges [][]int) bool { + if len(edges) > n-1 { + return false + } + dsu := NewDSU(n) + for _, edge := range edges { + if !dsu.Union(edge[0], edge[1]) { + return false + } + } + return dsu.Components() == 1 +} +``` + +```kotlin +class DSU(n: Int) { + private val parent = IntArray(n + 1) { it } + private val size = IntArray(n + 1) { 1 } + var comps = n + private set + + fun find(node: Int): Int { + if (parent[node] != node) { + parent[node] = find(parent[node]) + } + return parent[node] + } + + fun union(u: Int, v: Int): Boolean { + val pu = find(u) + val pv = find(v) + if (pu == pv) return false + + comps-- + if (size[pu] < size[pv]) { + parent[pu] = pv + size[pv] += size[pu] + } else { + parent[pv] = pu + size[pu] += size[pv] + } + return true + } +} + +class Solution { + fun validTree(n: Int, edges: Array): Boolean { + if (edges.size > n - 1) return false + + val dsu = DSU(n) + for ((u, v) in edges) { + if (!dsu.union(u, v)) return false + } + return dsu.comps == 1 + } +} +``` + +```swift +class DSU { + var comps: Int + var parent: [Int] + var size: [Int] + + init(_ n: Int) { + comps = n + parent = Array(0.. Int { + if parent[node] != node { + parent[node] = find(parent[node]) + } + return parent[node] + } + + func union(_ u: Int, _ v: Int) -> Bool { + let pu = find(u) + let pv = find(v) + if pu == pv { + return false + } + comps -= 1 + if size[pu] < size[pv] { + parent[pu] = pv + size[pv] += size[pu] + } else { + parent[pv] = pu + size[pu] += size[pv] + } + return true + } + + func components() -> Int { + return comps + } +} + +class Solution { + func validTree(_ n: Int, _ edges: [[Int]]) -> Bool { + if edges.count > n - 1 { + return false + } + + let dsu = DSU(n) + for edge in edges { + let u = edge[0], v = edge[1] + if !dsu.union(u, v) { + return false + } + } + return dsu.components() == 1 + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(V + (E * α(V)))$ +* Space complexity: $O(V)$ + +> Where $V$ is the number of vertices and $E$ is the number of edges in the graph. $α()$ is used for amortized complexity. \ No newline at end of file diff --git a/articles/valid-word-abbreviation.md b/articles/valid-word-abbreviation.md new file mode 100644 index 000000000..79c44a2e7 --- /dev/null +++ b/articles/valid-word-abbreviation.md @@ -0,0 +1,163 @@ +## 1. Two Pointers + +::tabs-start + +```python +class Solution: + def validWordAbbreviation(self, word: str, abbr: str) -> bool: + n, m = len(word), len(abbr) + i = j = 0 + + while i < n and j < m: + if abbr[j] == '0': + return False + + if word[i] == abbr[j]: + i, j = i + 1, j + 1 + elif abbr[j].isalpha(): + return False + else: + subLen = 0 + while j < m and abbr[j].isdigit(): + subLen = subLen * 10 + int(abbr[j]) + j += 1 + i += subLen + + return i == n and j == m +``` + +```java +public class Solution { + public boolean validWordAbbreviation(String word, String abbr) { + int n = word.length(), m = abbr.length(); + int i = 0, j = 0; + + while (i < n && j < m) { + if (abbr.charAt(j) == '0') return false; + + if (Character.isLetter(abbr.charAt(j))) { + if (i < n && word.charAt(i) == abbr.charAt(j)) { + i++; + j++; + } else { + return false; + } + } else { + int subLen = 0; + while (j < m && Character.isDigit(abbr.charAt(j))) { + subLen = subLen * 10 + (abbr.charAt(j) - '0'); + j++; + } + i += subLen; + } + } + + return i == n && j == m; + } +} +``` + +```cpp +class Solution { +public: + bool validWordAbbreviation(string word, string abbr) { + int n = word.length(), m = abbr.length(); + int i = 0, j = 0; + + while (i < n && j < m) { + if (abbr[j] == '0') return false; + + if (isalpha(abbr[j])) { + if (word[i] == abbr[j]) { + i++; j++; + } else { + return false; + } + } else { + int subLen = 0; + while (j < m && isdigit(abbr[j])) { + subLen = subLen * 10 + (abbr[j] - '0'); + j++; + } + i += subLen; + } + } + + return i == n && j == m; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} word + * @param {string} abbr + * @return {boolean} + */ + validWordAbbreviation(word, abbr) { + let n = word.length, m = abbr.length; + let i = 0, j = 0; + + while (i < n && j < m) { + if (abbr[j] === '0') return false; + + if (isNaN(abbr[j])) { + if (word[i] === abbr[j]) { + i++; j++; + } else { + return false; + } + } else { + let subLen = 0; + while (j < m && !isNaN(abbr[j]) && abbr[j] !== ' ') { + subLen = subLen * 10 + parseInt(abbr[j]); + j++; + } + i += subLen; + } + } + + return i === n && j === m; + } +} +``` + +```csharp +public class Solution { + public bool ValidWordAbbreviation(string word, string abbr) { + int n = word.Length, m = abbr.Length; + int i = 0, j = 0; + + while (i < n && j < m) { + if (abbr[j] == '0') return false; + + if (char.IsLetter(abbr[j])) { + if (i < n && word[i] == abbr[j]) { + i++; j++; + } else { + return false; + } + } else { + int subLen = 0; + while (j < m && char.IsDigit(abbr[j])) { + subLen = subLen * 10 + (abbr[j] - '0'); + j++; + } + i += subLen; + } + } + + return i == n && j == m; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(1)$ + +> Where $n$ and $m$ are the lengths of the strings $word$ and $abbr$, respectively. \ No newline at end of file diff --git a/articles/validate-binary-tree-nodes.md b/articles/validate-binary-tree-nodes.md new file mode 100644 index 000000000..fe797fbd4 --- /dev/null +++ b/articles/validate-binary-tree-nodes.md @@ -0,0 +1,668 @@ +## 1. Depth First Search + +::tabs-start + +```python +class Solution: + def validateBinaryTreeNodes(self, n: int, leftChild: List[int], rightChild: List[int]) -> bool: + hasParent = set(leftChild + rightChild) + hasParent.discard(-1) + if len(hasParent) == n: + return False + + root = -1 + for i in range(n): + if i not in hasParent: + root = i + break + + visit = set() + def dfs(i): + if i == -1: + return True + if i in visit: + return False + visit.add(i) + return dfs(leftChild[i]) and dfs(rightChild[i]) + + return dfs(root) and len(visit) == n +``` + +```java +public class Solution { + private Set visit; + + public boolean validateBinaryTreeNodes(int n, int[] leftChild, int[] rightChild) { + Set hasParent = new HashSet<>(); + for (int c : leftChild) if (c != -1) hasParent.add(c); + for (int c : rightChild) if (c != -1) hasParent.add(c); + if (hasParent.size() == n) return false; + + int root = -1; + for (int i = 0; i < n; i++) { + if (!hasParent.contains(i)) { + root = i; + break; + } + } + visit = new HashSet<>(); + return dfs(root, leftChild, rightChild) && visit.size() == n; + } + + private boolean dfs(int i, int[] leftChild, int[] rightChild) { + if (i == -1) return true; + if (visit.contains(i)) return false; + visit.add(i); + return dfs(leftChild[i], leftChild, rightChild) && + dfs(rightChild[i], leftChild, rightChild); + } +} +``` + +```cpp +class Solution { +public: + unordered_set visit; + + bool validateBinaryTreeNodes(int n, vector& leftChild, vector& rightChild) { + unordered_set hasParent; + for (int c : leftChild) if (c != -1) hasParent.insert(c); + for (int c : rightChild) if (c != -1) hasParent.insert(c); + if (hasParent.size() == n) return false; + + int root = -1; + for (int i = 0; i < n; i++) { + if (!hasParent.count(i)) { + root = i; + break; + } + } + return dfs(root, leftChild, rightChild) && visit.size() == n; + } + +private: + bool dfs(int i, vector& leftChild, vector& rightChild) { + if (i == -1) return true; + if (visit.count(i)) return false; + visit.insert(i); + return dfs(leftChild[i], leftChild, rightChild) && + dfs(rightChild[i], leftChild, rightChild); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[]} leftChild + * @param {number[]} rightChild + * @return {boolean} + */ + validateBinaryTreeNodes(n, leftChild, rightChild) { + let hasParent = new Set([...leftChild, ...rightChild].filter(c => c !== -1)); + if (hasParent.size === n) return false; + + let root = 0; + for (let i = 0; i < n; i++) { + if (!hasParent.has(i)) { + root = i; + break; + } + } + + const visit = new Set(); + const dfs = (i) => { + if (i === -1) return true; + if (visit.has(i)) return false; + visit.add(i); + return dfs(leftChild[i]) && dfs(rightChild[i]); + }; + + return dfs(root) && visit.size === n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Breadth First Search + +::tabs-start + +```python +class Solution: + def validateBinaryTreeNodes(self, n: int, leftChild: list[int], rightChild: list[int]) -> bool: + indegree = [0] * n + for i in range(n): + if leftChild[i] != -1: + indegree[leftChild[i]] += 1 + if indegree[leftChild[i]] > 1: + return False + if rightChild[i] != -1: + indegree[rightChild[i]] += 1 + if indegree[rightChild[i]] > 1: + return False + + root = -1 + for i in range(n): + if indegree[i] == 0: + if root != -1: + return False + root = i + + if root == -1: + return False + + count = 0 + q = deque([root]) + while q: + i = q.popleft() + count += 1 + if leftChild[i] != -1: + q.append(leftChild[i]) + if rightChild[i] != -1: + q.append(rightChild[i]) + return count == n +``` + +```java +public class Solution { + public boolean validateBinaryTreeNodes(int n, int[] leftChild, int[] rightChild) { + int[] indegree = new int[n]; + for (int i = 0; i < n; i++) { + if (leftChild[i] != -1) { + if (++indegree[leftChild[i]] > 1) return false; + } + if (rightChild[i] != -1) { + if (++indegree[rightChild[i]] > 1) return false; + } + } + + int root = -1; + for (int i = 0; i < n; i++) { + if (indegree[i] == 0) { + if (root != -1) return false; + root = i; + } + } + + if (root == -1) return false; + + int count = 0; + Queue q = new LinkedList<>(); + q.offer(root); + + while (!q.isEmpty()) { + int i = q.poll(); + count++; + if (leftChild[i] != -1) q.offer(leftChild[i]); + if (rightChild[i] != -1) q.offer(rightChild[i]); + } + return count == n; + } +} +``` + +```cpp +class Solution { +public: + bool validateBinaryTreeNodes(int n, vector& leftChild, vector& rightChild) { + vector indegree(n, 0); + for (int i = 0; i < n; i++) { + if (leftChild[i] != -1) { + if (++indegree[leftChild[i]] > 1) return false; + } + if (rightChild[i] != -1) { + if (++indegree[rightChild[i]] > 1) return false; + } + } + + int root = -1; + for (int i = 0; i < n; i++) { + if (indegree[i] == 0) { + if (root != -1) return false; + root = i; + } + } + + if (root == -1) return false; + + int count = 0; + queue q; + q.push(root); + + while (!q.empty()) { + int i = q.front();q.pop(); + count++; + if (leftChild[i] != -1) q.push(leftChild[i]); + if (rightChild[i] != -1) q.push(rightChild[i]); + } + return count == n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[]} leftChild + * @param {number[]} rightChild + * @return {boolean} + */ + validateBinaryTreeNodes(n, leftChild, rightChild) { + let indegree = new Array(n).fill(0); + for (let i = 0; i < n; i++) { + if (leftChild[i] !== -1) { + if (++indegree[leftChild[i]] > 1) return false; + } + if (rightChild[i] !== -1) { + if (++indegree[rightChild[i]] > 1) return false; + } + } + + let root = -1; + for (let i = 0; i < n; i++) { + if (indegree[i] === 0) { + if (root !== -1) return false; + root = i; + } + } + + if (root === -1) return false; + + let count = 0; + let q = new Queue([root]); + + while (!q.isEmpty()) { + let i = q.pop(); + count++; + if (leftChild[i] !== -1) q.push(leftChild[i]); + if (rightChild[i] !== -1) q.push(rightChild[i]); + } + return count === n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 3. Iterative DFS + +::tabs-start + +```python +class Solution: + def validateBinaryTreeNodes(self, n: int, leftChild: list[int], rightChild: list[int]) -> bool: + indegree = [0] * n + for i in range(n): + if leftChild[i] != -1: + indegree[leftChild[i]] += 1 + if indegree[leftChild[i]] > 1: + return False + if rightChild[i] != -1: + indegree[rightChild[i]] += 1 + if indegree[rightChild[i]] > 1: + return False + + root = next((i for i in range(n) if indegree[i] == 0), -1) + if root == -1: + return False + + count, stack = 0, [root] + while stack: + node = stack.pop() + count += 1 + if leftChild[node] != -1: + stack.append(leftChild[node]) + if rightChild[node] != -1: + stack.append(rightChild[node]) + + return count == n +``` + +```java +public class Solution { + public boolean validateBinaryTreeNodes(int n, int[] leftChild, int[] rightChild) { + int[] indegree = new int[n]; + for (int i = 0; i < n; i++) { + if (leftChild[i] != -1) { + if (++indegree[leftChild[i]] > 1) return false; + } + if (rightChild[i] != -1) { + if (++indegree[rightChild[i]] > 1) return false; + } + } + + int root = -1; + for (int i = 0; i < n; i++) { + if (indegree[i] == 0) { + if (root != -1) return false; + root = i; + } + } + + if (root == -1) return false; + + int count = 0; + Stack stack = new Stack<>(); + stack.push(root); + + while (!stack.isEmpty()) { + int node = stack.pop(); + count++; + if (leftChild[node] != -1) stack.push(leftChild[node]); + if (rightChild[node] != -1) stack.push(rightChild[node]); + } + return count == n; + } +} +``` + +```cpp +class Solution { +public: + bool validateBinaryTreeNodes(int n, vector& leftChild, vector& rightChild) { + vector indegree(n, 0); + for (int i = 0; i < n; i++) { + if (leftChild[i] != -1 && ++indegree[leftChild[i]] > 1) return false; + if (rightChild[i] != -1 && ++indegree[rightChild[i]] > 1) return false; + } + + int root = -1; + for (int i = 0; i < n; i++) { + if (indegree[i] == 0) { + if (root != -1) return false; + root = i; + } + } + + if (root == -1) return false; + + int count = 0; + stack stk; + stk.push(root); + + while (!stk.empty()) { + int node = stk.top(); stk.pop(); + count++; + if (leftChild[node] != -1) stk.push(leftChild[node]); + if (rightChild[node] != -1) stk.push(rightChild[node]); + } + return count == n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number} n + * @param {number[]} leftChild + * @param {number[]} rightChild + * @return {boolean} + */ + validateBinaryTreeNodes(n, leftChild, rightChild) { + let indegree = new Array(n).fill(0); + for (let i = 0; i < n; i++) { + if (leftChild[i] !== -1) { + if (++indegree[leftChild[i]] > 1) return false; + } + if (rightChild[i] !== -1) { + if (++indegree[rightChild[i]] > 1) return false; + } + } + + let root = -1; + for (let i = 0; i < n; i++) { + if (indegree[i] === 0) { + if (root !== -1) return false; + root = i; + } + } + + if (root === -1) return false; + + let count = 0; + let stack = [root]; + + while (stack.length) { + let node = stack.pop(); + count++; + if (leftChild[node] !== -1) stack.push(leftChild[node]); + if (rightChild[node] !== -1) stack.push(rightChild[node]); + } + return count === n; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 4. Disjoint Set Union + +::tabs-start + +```python +class DSU: + def __init__(self, n): + self.Parent = list(range(n)) + self.Components = n + + def find(self, node): + if self.Parent[node] != node: + self.Parent[node] = self.find(self.Parent[node]) + return self.Parent[node] + + def union(self, parent, child): + parentRoot = self.find(parent) + childRoot = self.find(child) + if childRoot != child or parentRoot == childRoot: + return False + + self.Components -= 1 + self.Parent[childRoot] = parentRoot + return True + +class Solution: + def validateBinaryTreeNodes(self, n: int, leftChild: list[int], rightChild: list[int]) -> bool: + dsu = DSU(n) + + for parent in range(n): + for child in (leftChild[parent], rightChild[parent]): + if child == -1: + continue + if not dsu.union(parent, child): + return False + + return dsu.Components == 1 +``` + +```java +class DSU { + int[] Parent; + int Components; + + public DSU(int n) { + Parent = new int[n]; + for (int i = 0; i < n; i++) { + Parent[i] = i; + } + Components = n; + } + + public int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + public boolean union(int parent, int child) { + int parentRoot = find(parent); + int childRoot = find(child); + if (childRoot != child || parentRoot == childRoot) { + return false; + } + + Components--; + Parent[childRoot] = parentRoot; + return true; + } +} + +class Solution { + public boolean validateBinaryTreeNodes(int n, int[] leftChild, int[] rightChild) { + DSU dsu = new DSU(n); + + for (int parent = 0; parent < n; parent++) { + for (int child : new int[]{leftChild[parent], rightChild[parent]}) { + if (child == -1) continue; + if (!dsu.union(parent, child)) return false; + } + } + + return dsu.Components == 1; + } +} +``` + +```cpp +class DSU { +public: + vector Parent; + int Components; + + DSU(int n) { + Parent.resize(n); + iota(Parent.begin(), Parent.end(), 0); + Components = n; + } + + int find(int node) { + if (Parent[node] != node) { + Parent[node] = find(Parent[node]); + } + return Parent[node]; + } + + bool unionSets(int parent, int child) { + int parentRoot = find(parent); + int childRoot = find(child); + if (childRoot != child || parentRoot == childRoot) { + return false; + } + + Components--; + Parent[childRoot] = parentRoot; + return true; + } +}; + +class Solution { +public: + bool validateBinaryTreeNodes(int n, vector& leftChild, vector& rightChild) { + DSU dsu(n); + + for (int parent = 0; parent < n; parent++) { + for (int child : {leftChild[parent], rightChild[parent]}) { + if (child == -1) continue; + if (!dsu.unionSets(parent, child)) return false; + } + } + + return dsu.Components == 1; + } +}; +``` + +```javascript +class DSU { + /** + * @constructor + * @param {number} n + */ + constructor(n) { + this.Parent = Array.from({ length: n }, (_, i) => i); + this.Components = n; + } + + /** + * @param {number} node + * @return {number} + */ + find(node) { + if (this.Parent[node] !== node) { + this.Parent[node] = this.find(this.Parent[node]); + } + return this.Parent[node]; + } + + /** + * @param {number} parent + * @param {number} child + * @return {boolean} + */ + union(parent, child) { + let parentRoot = this.find(parent); + let childRoot = this.find(child); + if (childRoot !== child || parentRoot === childRoot) { + return false; + } + + this.Components--; + this.Parent[childRoot] = parentRoot; + return true; + } +} + +class Solution { + /** + * @param {number} n + * @param {number[]} leftChild + * @param {number[]} rightChild + * @return {boolean} + */ + validateBinaryTreeNodes(n, leftChild, rightChild) { + let dsu = new DSU(n); + + for (let parent = 0; parent < n; parent++) { + for (let child of [leftChild[parent], rightChild[parent]]) { + if (child === -1) continue; + if (!dsu.union(parent, child)) return false; + } + } + + return dsu.Components === 1; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/validate-parentheses.md b/articles/validate-parentheses.md new file mode 100644 index 000000000..a7e649a78 --- /dev/null +++ b/articles/validate-parentheses.md @@ -0,0 +1,336 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def isValid(self, s: str) -> bool: + while '()' in s or '{}' in s or '[]' in s: + s = s.replace('()', '') + s = s.replace('{}', '') + s = s.replace('[]', '') + return s == '' +``` + +```java +public class Solution { + public boolean isValid(String s) { + while (s.contains("()") || s.contains("{}") || s.contains("[]")) { + s = s.replace("()", ""); + s = s.replace("{}", ""); + s = s.replace("[]", ""); + } + return s.isEmpty(); + } +} +``` + +```cpp +class Solution { +public: + bool isValid(string s) { + while (true) { + size_t pos = string::npos; + if ((pos = s.find("()")) != string::npos) { + s.erase(pos, 2); + continue; + } + if ((pos = s.find("{}")) != string::npos) { + s.erase(pos, 2); + continue; + } + if ((pos = s.find("[]")) != string::npos) { + s.erase(pos, 2); + continue; + } + break; + } + return s.empty(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + isValid(s) { + while (s.includes("()") || s.includes("{}") || s.includes("[]")) { + s = s.replace("()", ""); + s = s.replace("{}", ""); + s = s.replace("[]", ""); + } + return s === ""; + } +} +``` + +```csharp +public class Solution { + public bool IsValid(string s) { + while (s.Contains("()") || s.Contains("{}") || s.Contains("[]")) { + s = s.Replace("()", ""); + s = s.Replace("{}", ""); + s = s.Replace("[]", ""); + } + return s == ""; + } +} +``` + +```go +func isValid(s string) bool { + for strings.Contains(s, "()") || strings.Contains(s, "{}") || strings.Contains(s, "[]") { + s = strings.ReplaceAll(s, "()", "") + s = strings.ReplaceAll(s, "{}", "") + s = strings.ReplaceAll(s, "[]", "") + } + return s == "" +} +``` + +```kotlin +class Solution { + fun isValid(s: String): Boolean { + var str = s + while (str.contains("()") || str.contains("{}") || str.contains("[]")) { + str = str.replace("()", "") + str = str.replace("{}", "") + str = str.replace("[]", "") + } + return str.isEmpty() + } +} +``` + +```swift +class Solution { + func isValid(_ s: String) -> Bool { + var str = s + while str.contains("()") || str.contains("{}") || str.contains("[]") { + str = str.replacingOccurrences(of: "()", with: "") + str = str.replacingOccurrences(of: "{}", with: "") + str = str.replacingOccurrences(of: "[]", with: "") + } + return str.isEmpty + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 2)$ +* Space complexity: $O(n)$ + +--- + +## 2. Stack + +::tabs-start + +```python +class Solution: + def isValid(self, s: str) -> bool: + stack = [] + closeToOpen = { ")" : "(", "]" : "[", "}" : "{" } + + for c in s: + if c in closeToOpen: + if stack and stack[-1] == closeToOpen[c]: + stack.pop() + else: + return False + else: + stack.append(c) + + return True if not stack else False +``` + +```java +public class Solution { + public boolean isValid(String s) { + Stack stack = new Stack<>(); + java.util.Map closeToOpen = new java.util.HashMap<>(); + closeToOpen.put(')', '('); + closeToOpen.put(']', '['); + closeToOpen.put('}', '{'); + + for (char c : s.toCharArray()) { + if (closeToOpen.containsKey(c)) { + if (!stack.isEmpty() && stack.peek() == closeToOpen.get(c)) { + stack.pop(); + } else { + return false; + } + } else { + stack.push(c); + } + } + return stack.isEmpty(); + } +} +``` + +```cpp +class Solution { +public: + bool isValid(string s) { + std::stack stack; + std::unordered_map closeToOpen = { + {')', '('}, + {']', '['}, + {'}', '{'} + }; + + for (char c : s) { + if (closeToOpen.count(c)) { + if (!stack.empty() && stack.top() == closeToOpen[c]) { + stack.pop(); + } else { + return false; + } + } else { + stack.push(c); + } + } + return stack.empty(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @return {boolean} + */ + isValid(s) { + const stack = []; + const closeToOpen = { + ')': '(', + ']': '[', + '}': '{' + }; + + for (let c of s) { + if (closeToOpen[c]) { + if (stack.length > 0 && stack[stack.length - 1] === closeToOpen[c]) { + stack.pop(); + } else { + return false; + } + } else { + stack.push(c); + } + } + return stack.length === 0; + } +} +``` + +```csharp +public class Solution { + public bool IsValid(string s) { + Stack stack = new Stack(); + Dictionary closeToOpen = new Dictionary { + { ')', '(' }, + { ']', '[' }, + { '}', '{' } + }; + + foreach (char c in s) { + if (closeToOpen.ContainsKey(c)) { + if (stack.Count > 0 && stack.Peek() == closeToOpen[c]) { + stack.Pop(); + } else { + return false; + } + } else { + stack.Push(c); + } + } + return stack.Count == 0; + } +} +``` + +```go +func isValid(s string) bool { + stack := linkedliststack.New() + closeToOpen := map[rune]rune{')': '(', ']': '[', '}': '{'} + + for _, c := range s { + if open, exists := closeToOpen[c]; exists { + if !stack.Empty() { + top, ok := stack.Pop() + if ok && top.(rune) != open { + return false + } + } else { + return false + } + } else { + stack.Push(c) + } + } + + return stack.Empty() +} +``` + +```kotlin +class Solution { + fun isValid(s: String): Boolean { + val stack = ArrayDeque() + val closeToOpen = hashMapOf(')' to '(', ']' to '[', '}' to '{') + + for (c in s) { + if (c in closeToOpen) { + if (stack.isNotEmpty() && stack.first() == closeToOpen[c]) { + stack.removeFirst() + } else { + return false + } + } else { + stack.addFirst(c) + } + } + + return stack.isEmpty() + } +} +``` + +```swift +class Solution { + func isValid(_ s: String) -> Bool { + var stack = [Character]() + let closeToOpen: [Character: Character] = [")": "(", "]": "[", "}": "{"] + + for c in s { + if let open = closeToOpen[c] { + if !stack.isEmpty && stack.last! == open { + stack.popLast() + } else { + return false + } + } else { + stack.append(c) + } + } + + return stack.isEmpty + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ \ No newline at end of file diff --git a/articles/validate-stack-sequences.md b/articles/validate-stack-sequences.md new file mode 100644 index 000000000..00867b541 --- /dev/null +++ b/articles/validate-stack-sequences.md @@ -0,0 +1,160 @@ +## 1. Stack + +::tabs-start + +```python +class Solution: + def validateStackSequences(self, pushed: List[int], popped: List[int]) -> bool: + i = 0 + stack = [] + for n in pushed: + stack.append(n) + while i < len(popped) and stack and popped[i] == stack[-1]: + stack.pop() + i += 1 + return not stack +``` + +```java +public class Solution { + public boolean validateStackSequences(int[] pushed, int[] popped) { + Stack stack = new Stack<>(); + int i = 0; + for (int n : pushed) { + stack.push(n); + while (i < popped.length && !stack.isEmpty() && popped[i] == stack.peek()) { + stack.pop(); + i++; + } + } + return stack.isEmpty(); + } +} +``` + +```cpp +class Solution { +public: + bool validateStackSequences(vector& pushed, vector& popped) { + stack stk; + int i = 0; + for (int n : pushed) { + stk.push(n); + while (i < popped.size() && !stk.empty() && popped[i] == stk.top()) { + stk.pop(); + i++; + } + } + return stk.empty(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} pushed + * @param {number[]} popped + * @return {boolean} + */ + validateStackSequences(pushed, popped) { + const stack = []; + let i = 0; + for (const n of pushed) { + stack.push(n); + while (i < popped.length && stack.length > 0 && popped[i] === stack[stack.length - 1]) { + stack.pop(); + i++; + } + } + return stack.length === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Two Pointers + +::tabs-start + +```python +class Solution: + def validateStackSequences(self, pushed: List[int], popped: List[int]) -> bool: + l = r = 0 + for num in pushed: + pushed[l] = num + l += 1 + while l > 0 and pushed[l - 1] == popped[r]: + r += 1 + l -= 1 + return l == 0 +``` + +```java +public class Solution { + public boolean validateStackSequences(int[] pushed, int[] popped) { + int l = 0, r = 0; + for (int num : pushed) { + pushed[l++] = num; + while (l > 0 && pushed[l - 1] == popped[r]) { + r++; + l--; + } + } + return l == 0; + } +} +``` + +```cpp +class Solution { +public: + bool validateStackSequences(vector& pushed, vector& popped) { + int l = 0, r = 0; + for (int& num : pushed) { + pushed[l++] = num; + while (l > 0 && pushed[l - 1] == popped[r]) { + r++; + l--; + } + } + return l == 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} pushed + * @param {number[]} popped + * @return {boolean} + */ + validateStackSequences(pushed, popped) { + let l = 0, r = 0; + for (const num of pushed) { + pushed[l++] = num; + while (l > 0 && pushed[l - 1] === popped[r]) { + r++; + l--; + } + } + return l === 0; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ extra space. \ No newline at end of file diff --git a/articles/verifying-an-alien-dictionary.md b/articles/verifying-an-alien-dictionary.md new file mode 100644 index 000000000..86694cf0a --- /dev/null +++ b/articles/verifying-an-alien-dictionary.md @@ -0,0 +1,271 @@ +## 1. Sorting + +::tabs-start + +```python +class Solution: + def isAlienSorted(self, words: List[str], order: str) -> bool: + order_index = {c: i for i, c in enumerate(order)} + + def compare(word): + return [order_index[c] for c in word] + + return words == sorted(words, key=compare) +``` + +```java +public class Solution { + public boolean isAlienSorted(String[] words, String order) { + int[] orderIndex = new int[26]; + for (int i = 0; i < order.length(); i++) + orderIndex[order.charAt(i) - 'a'] = i; + + Comparator compare = (w1, w2) -> { + for (int i = 0; i < Math.min(w1.length(), w2.length()); i++) { + if (w1.charAt(i) != w2.charAt(i)) + return orderIndex[w1.charAt(i) - 'a'] - orderIndex[w2.charAt(i) - 'a']; + } + return w1.length() - w2.length(); + }; + + String[] sortedWords = words.clone(); + Arrays.sort(sortedWords, compare); + return Arrays.equals(words, sortedWords); + } +} +``` + +```cpp +class Solution { +public: + bool isAlienSorted(vector& words, string order) { + int orderIndex[26]; + for (int i = 0; i < order.size(); ++i) + orderIndex[order[i] - 'a'] = i; + + auto compare = [&](const string &a, const string &b) { + for (int i = 0; i < min(a.size(), b.size()); ++i) { + if (a[i] != b[i]) + return orderIndex[a[i] - 'a'] < orderIndex[b[i] - 'a']; + } + return a.size() < b.size(); + }; + + return is_sorted(words.begin(), words.end(), compare); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string} order + * @return {boolean} + */ + isAlienSorted(words, order) { + let orderIndex = new Array(26).fill(0); + for (let i = 0; i < order.length; i++) { + orderIndex[order.charCodeAt(i) - 97] = i; + } + + const compare = (w1, w2) => { + for (let i = 0; i < Math.min(w1.length, w2.length); i++) { + if (w1[i] !== w2[i]) { + return orderIndex[w1.charCodeAt(i) - 97] - orderIndex[w2.charCodeAt(i) - 97]; + } + } + return w1.length - w2.length; + }; + + let sortedWords = [...words].sort(compare); + return words.join() === sortedWords.join(); + } +} +``` + +```csharp +public class Solution { + public bool IsAlienSorted(string[] words, string order) { + int[] orderIndex = new int[26]; + for (int i = 0; i < order.Length; i++) { + orderIndex[order[i] - 'a'] = i; + } + + string[] sortedWords = (string[])words.Clone(); + Array.Sort(sortedWords, (w1, w2) => { + for (int i = 0; i < Math.Min(w1.Length, w2.Length); i++) { + if (w1[i] != w2[i]) { + return orderIndex[w1[i] - 'a'] - orderIndex[w2[i] - 'a']; + } + } + return w1.Length - w2.Length; + }); + + for (int i = 0; i < words.Length; i++) { + if (!words[i].Equals(sortedWords[i])) { + return false; + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m\log n)$ +* Space complexity: $O(n * m)$ + +> Where $n$ is the number of words and $m$ is the average length of a word. + +--- + +## 2. Comparing adjacent words + +::tabs-start + +```python +class Solution: + def isAlienSorted(self, words: List[str], order: str) -> bool: + order_index = {c: i for i, c in enumerate(order)} + + for i in range(len(words) - 1): + w1, w2 = words[i], words[i + 1] + + for j in range(len(w1)): + if j == len(w2): + return False + + if w1[j] != w2[j]: + if order_index[w1[j]] > order_index[w2[j]]: + return False + break + return True +``` + +```java +public class Solution { + public boolean isAlienSorted(String[] words, String order) { + int[] orderIndex = new int[26]; + for (int i = 0; i < order.length(); i++) + orderIndex[order.charAt(i) - 'a'] = i; + + for (int i = 0; i < words.length - 1; i++) { + String w1 = words[i], w2 = words[i + 1]; + int j = 0; + + for (; j < w1.length(); j++) { + if (j == w2.length()) return false; + if (w1.charAt(j) != w2.charAt(j)) { + if (orderIndex[w1.charAt(j) - 'a'] > orderIndex[w2.charAt(j) - 'a']) { + return false; + } + break; + } + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool isAlienSorted(vector& words, string order) { + int orderIndex[26] = {0}; + for (int i = 0; i < order.size(); ++i) + orderIndex[order[i] - 'a'] = i; + + for (int i = 0; i < words.size() - 1; ++i) { + string w1 = words[i], w2 = words[i + 1]; + int j = 0; + + for (; j < w1.size(); ++j) { + if (j == w2.size()) return false; + if (w1[j] != w2[j]) { + if (orderIndex[w1[j] - 'a'] > orderIndex[w2[j] - 'a']) + return false; + break; + } + } + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words + * @param {string} order + * @return {boolean} + */ + isAlienSorted(words, order) { + let orderIndex = new Array(26).fill(0); + for (let i = 0; i < order.length; i++) { + orderIndex[order.charCodeAt(i) - 97] = i; + } + + for (let i = 0; i < words.length - 1; i++) { + let w1 = words[i], w2 = words[i + 1]; + + for (let j = 0; j < w1.length; j++) { + if (j === w2.length) return false; + + if (w1[j] !== w2[j]) { + if (orderIndex[w1.charCodeAt(j) - 97] > orderIndex[w2.charCodeAt(j) - 97]) + return false; + break; + } + } + } + return true; + } +} +``` + +```csharp +public class Solution { + public bool IsAlienSorted(string[] words, string order) { + Dictionary orderIndex = new Dictionary(); + for (int i = 0; i < order.Length; i++) { + orderIndex[order[i]] = i; + } + + for (int i = 0; i < words.Length - 1; i++) { + string w1 = words[i]; + string w2 = words[i + 1]; + + for (int j = 0; j < w1.Length; j++) { + if (j == w2.Length) { + return false; + } + + if (w1[j] != w2[j]) { + if (orderIndex[w1[j]] > orderIndex[w2[j]]) { + return false; + } + break; + } + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n * m)$ +* Space complexity: $O(1)$ since we have $26$ different characters. + +> Where $n$ is the number of words and $m$ is the average length of a word. \ No newline at end of file diff --git a/articles/widest-vertical-area-between-two-points-containing-no-points.md b/articles/widest-vertical-area-between-two-points-containing-no-points.md new file mode 100644 index 000000000..b41a574f7 --- /dev/null +++ b/articles/widest-vertical-area-between-two-points-containing-no-points.md @@ -0,0 +1,212 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def maxWidthOfVerticalArea(self, points: List[List[int]]) -> int: + n = len(points) + res = 0 + + for i in range(1, n): + x1 = points[i][0] + for j in range(i): + x2 = points[j][0] + hasPoints = False + for k in range(n): + if k == i or k == j: + continue + + x3 = points[k][0] + if x3 > min(x1, x2) and x3 < max(x1, x2): + hasPoints = True + break + + if not hasPoints: + res = max(res, abs(x1 - x2)) + + return res +``` + +```java +public class Solution { + public int maxWidthOfVerticalArea(int[][] points) { + int n = points.length, res = 0; + + for (int i = 1; i < n; i++) { + int x1 = points[i][0]; + for (int j = 0; j < i; j++) { + int x2 = points[j][0]; + boolean hasPoints = false; + + for (int k = 0; k < n; k++) { + if (k == i || k == j) continue; + + int x3 = points[k][0]; + if (x3 > Math.min(x1, x2) && x3 < Math.max(x1, x2)) { + hasPoints = true; + break; + } + } + + if (!hasPoints) { + res = Math.max(res, Math.abs(x1 - x2)); + } + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxWidthOfVerticalArea(vector>& points) { + int n = points.size(), res = 0; + + for (int i = 1; i < n; i++) { + int x1 = points[i][0]; + for (int j = 0; j < i; j++) { + int x2 = points[j][0]; + bool hasPoints = false; + + for (int k = 0; k < n; k++) { + if (k == i || k == j) continue; + + int x3 = points[k][0]; + if (x3 > min(x1, x2) && x3 < max(x1, x2)) { + hasPoints = true; + break; + } + } + + if (!hasPoints) { + res = max(res, abs(x1 - x2)); + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} points + * @return {number} + */ + maxWidthOfVerticalArea(points) { + let n = points.length, res = 0; + + for (let i = 1; i < n; i++) { + let x1 = points[i][0]; + for (let j = 0; j < i; j++) { + let x2 = points[j][0]; + let hasPoints = false; + + for (let k = 0; k < n; k++) { + if (k === i || k === j) continue; + + let x3 = points[k][0]; + if (x3 > Math.min(x1, x2) && x3 < Math.max(x1, x2)) { + hasPoints = true; + break; + } + } + + if (!hasPoints) { + res = Math.max(res, Math.abs(x1 - x2)); + } + } + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n ^ 3)$ +* Space complexity: $O(1)$ + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def maxWidthOfVerticalArea(self, points: List[List[int]]) -> int: + points.sort() + res = 0 + for i in range(len(points) - 1): + res = max(res, points[i + 1][0] - points[i][0]) + return res +``` + +```java +public class Solution { + public int maxWidthOfVerticalArea(int[][] points) { + Arrays.sort(points, Comparator.comparingInt(a -> a[0])); + int res = 0; + + for (int i = 0; i < points.length - 1; i++) { + res = Math.max(res, points[i + 1][0] - points[i][0]); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + int maxWidthOfVerticalArea(vector>& points) { + sort(points.begin(), points.end(), [](const auto& a, const auto& b) { + return a[0] < b[0]; + }); + + int res = 0; + for (int i = 0; i < points.size() - 1; i++) { + res = max(res, points[i + 1][0] - points[i][0]); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[][]} points + * @return {number} + */ + maxWidthOfVerticalArea(points) { + points.sort((a, b) => a[0] - b[0]); + let res = 0; + + for (let i = 0; i < points.length - 1; i++) { + res = Math.max(res, points[i + 1][0] - points[i][0]); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. \ No newline at end of file diff --git a/articles/wiggle-sort.md b/articles/wiggle-sort.md new file mode 100644 index 000000000..d18f99598 --- /dev/null +++ b/articles/wiggle-sort.md @@ -0,0 +1,293 @@ +## 1. Max-Heap + +::tabs-start + +```python +class Solution: + def wiggleSort(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + + maxHeap = [] + for num in nums: + heappush(maxHeap, -num) + + n = len(nums) + for i in range(1, n, 2): + nums[i] = -heappop(maxHeap) + for i in range(0, n, 2): + nums[i] = -heappop(maxHeap) +``` + +```java +public class Solution { + public void wiggleSort(int[] nums) { + PriorityQueue maxHeap = new PriorityQueue<>(Collections.reverseOrder()); + for (int num : nums) { + maxHeap.add(num); + } + + for (int i = 1; i < nums.length; i += 2) { + nums[i] = maxHeap.poll(); + } + for (int i = 0; i < nums.length; i += 2) { + nums[i] = maxHeap.poll(); + } + } +} +``` + +```cpp +class Solution { +public: + void wiggleSort(vector& nums) { + priority_queue maxHeap; + for (int& num : nums) { + maxHeap.push(num); + } + + for (int i = 1; i < nums.size(); i += 2) { + nums[i] = maxHeap.top(); + maxHeap.pop(); + } + for (int i = 0; i < nums.size(); i += 2) { + nums[i] = maxHeap.top(); + maxHeap.pop(); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ + wiggleSort(nums) { + const maxHeap = new PriorityQueue((a, b) => b - a); + nums.forEach(num => maxHeap.enqueue(num)); + + for (let i = 1; i < nums.length; i += 2) { + nums[i] = maxHeap.dequeue(); + } + for (let i = 0; i < nums.length; i += 2) { + nums[i] = maxHeap.dequeue(); + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(n)$ + +--- + +## 2. Sorting + +::tabs-start + +```python +class Solution: + def wiggleSort(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + + nums.sort() + for i in range(1, len(nums) - 1, 2): + nums[i], nums[i + 1] = nums[i + 1], nums[i] +``` + +```java +public class Solution { + public void wiggleSort(int[] nums) { + Arrays.sort(nums); + for (int i = 1; i < nums.length - 1; i += 2) { + int temp = nums[i]; + nums[i] = nums[i + 1]; + nums[i + 1] = temp; + } + } +} +``` + +```cpp +class Solution { +public: + void wiggleSort(vector& nums) { + sort(nums.begin(), nums.end()); + for (int i = 1; i < nums.size() - 1; i += 2) { + swap(nums[i], nums[i + 1]); + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ + wiggleSort(nums) { + nums.sort((a, b) => a - b); + for (let i = 1; i < nums.length - 1; i += 2) { + [nums[i], nums[i + 1]] = [nums[i + 1], nums[i]]; + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n \log n)$ +* Space complexity: $O(1)$ or $O(n)$ depending on the sorting algorithm. + +--- + +## 3. Greedy - I + +::tabs-start + +```python +class Solution: + def wiggleSort(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + + for i in range(1, len(nums)): + if ((i % 2 == 1 and nums[i] < nums[i - 1]) or + (i % 2 == 0 and nums[i] > nums[i - 1]) + ): + nums[i], nums[i - 1] = nums[i - 1], nums[i] +``` + +```java +public class Solution { + public void wiggleSort(int[] nums) { + for (int i = 1; i < nums.length; i++) { + if ((i % 2 == 1 && nums[i] < nums[i - 1]) || + (i % 2 == 0 && nums[i] > nums[i - 1])) { + int temp = nums[i]; + nums[i] = nums[i - 1]; + nums[i - 1] = temp; + } + } + } +} +``` + +```cpp +class Solution { +public: + void wiggleSort(vector& nums) { + for (int i = 1; i nums[i - 1])) { + swap(nums[i], nums[i - 1]); + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ + wiggleSort(nums) { + for (let i = 1; i < nums.length; i++) { + if ((i % 2 == 1 && nums[i] < nums[i - 1]) || + (i % 2 == 0 && nums[i] > nums[i - 1])) { + [nums[i], nums[i - 1]] = [nums[i - 1], nums[i]]; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ + +--- + +## 4. Greedy - II + +::tabs-start + +```python +class Solution: + def wiggleSort(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + + for i in range(1, len(nums)): + if (i % 2) ^ (nums[i] > nums[i - 1]): + nums[i], nums[i - 1] = nums[i - 1], nums[i] +``` + +```java +public class Solution { + public void wiggleSort(int[] nums) { + for (int i = 1; i < nums.length; i++) { + if (((i % 2) ^ (nums[i] > nums[i - 1] ? 1 : 0)) != 0) { + int temp = nums[i]; + nums[i] = nums[i - 1]; + nums[i - 1] = temp; + } + } + } +} +``` + +```cpp +class Solution { +public: + void wiggleSort(vector& nums) { + for (int i = 1; i nums[i - 1])) { + swap(nums[i], nums[i - 1]); + } + } + } +}; +``` + +```javascript +class Solution { + /** + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ + wiggleSort(nums) { + for(var i = 1; i < nums.length; i++) { + if ((i % 2) ^ (nums[i] > nums[i - 1])) { + [nums[i], nums[i - 1]] = [nums[i - 1], nums[i]]; + } + } + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(1)$ \ No newline at end of file diff --git a/articles/word-break-ii.md b/articles/word-break-ii.md new file mode 100644 index 000000000..45660e5f9 --- /dev/null +++ b/articles/word-break-ii.md @@ -0,0 +1,1188 @@ +## 1. Backtracking + +::tabs-start + +```python +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> List[str]: + wordDict = set(wordDict) + + def backtrack(i): + if i == len(s): + res.append(" ".join(cur)) + return + + for j in range(i, len(s)): + w = s[i:j + 1] + if w in wordDict: + cur.append(w) + backtrack(j + 1) + cur.pop() + + cur = [] + res = [] + backtrack(0) + return res +``` + +```java +public class Solution { + private Set wordSet; + private List res; + + public List wordBreak(String s, List wordDict) { + wordSet = new HashSet<>(wordDict); + res = new ArrayList<>(); + List cur = new ArrayList<>(); + backtrack(s, 0, cur); + return res; + } + + private void backtrack(String s, int i, List cur) { + if (i == s.length()) { + res.add(String.join(" ", cur)); + return; + } + + for (int j = i; j < s.length(); j++) { + String w = s.substring(i, j + 1); + if (wordSet.contains(w)) { + cur.add(w); + backtrack(s, j + 1, cur); + cur.remove(cur.size() - 1); + } + } + } +} +``` + +```cpp +class Solution { + unordered_set wordSet; + vector res; + +public: + vector wordBreak(string s, vector& wordDict) { + wordSet = unordered_set(wordDict.begin(), wordDict.end()); + vector cur; + backtrack(s, 0, cur); + return res; + } + +private: + void backtrack(const string& s, int i, vector& cur) { + if (i == s.size()) { + res.push_back(join(cur)); + return; + } + + for (int j = i; j < s.size(); ++j) { + string w = s.substr(i, j - i + 1); + if (wordSet.count(w)) { + cur.push_back(w); + backtrack(s, j + 1, cur); + cur.pop_back(); + } + } + } + + string join(const vector& words) { + ostringstream oss; + for (int i = 0; i < words.size(); ++i) { + if (i > 0) oss << " "; + oss << words[i]; + } + return oss.str(); + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string[]} wordDict + * @return {string[]} + */ + wordBreak(s, wordDict) { + const wordSet = new Set(wordDict); + const res = []; + const cur = []; + + const backtrack = (i) => { + if (i === s.length) { + res.push(cur.join(" ")); + return; + } + + for (let j = i; j < s.length; j++) { + const w = s.substring(i, j + 1); + if (wordSet.has(w)) { + cur.push(w); + backtrack(j + 1); + cur.pop(); + } + } + }; + + backtrack(0); + return res; + } +} +``` + +```csharp +public class Solution { + public List WordBreak(string s, List wordDict) { + HashSet wordSet = new HashSet(wordDict); + List res = new List(); + List cur = new List(); + + void Backtrack(int i) { + if (i == s.Length) { + res.Add(string.Join(" ", cur)); + return; + } + + for (int j = i; j < s.Length; j++) { + string word = s.Substring(i, j - i + 1); + if (wordSet.Contains(word)) { + cur.Add(word); + Backtrack(j + 1); + cur.RemoveAt(cur.Count - 1); + } + } + } + + Backtrack(0); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n * 2 ^ n)$ +* Space complexity: $O(m + 2 ^ n)$ + +> Where $n$ is the length of the string $s$ and $m$ is the sum of the lengths of the strings in the $wordDict$. + +--- + +## 2. Backtracking + Trie + +::tabs-start + +```python +class TrieNode: + def __init__(self): + self.children = {} + self.isWord = False + +class Trie: + def __init__(self): + self.root = TrieNode() + + def addWord(self, word): + curr = self.root + for c in word: + if c not in curr.children: + curr.children[c] = TrieNode() + curr = curr.children[c] + curr.isWord = True + +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> List[str]: + trie = Trie() + for word in wordDict: + trie.addWord(word) + + def backtrack(i, path): + if i == len(s): + res.append(" ".join(path)) + return + + node = trie.root + word = [] + for i in range(i, len(s)): + char = s[i] + if char not in node.children: + break + + word.append(char) + node = node.children[char] + + if node.isWord: + path.append("".join(word)) + backtrack(i + 1, path) + path.pop() + + res = [] + backtrack(0, []) + return res +``` + +```java +class TrieNode { + HashMap children = new HashMap<>(); + boolean isWord = false; +} + +class Trie { + TrieNode root; + + Trie() { + root = new TrieNode(); + } + + void addWord(String word) { + TrieNode curr = root; + for (char c : word.toCharArray()) { + curr.children.putIfAbsent(c, new TrieNode()); + curr = curr.children.get(c); + } + curr.isWord = true; + } +} + +public class Solution { + public List wordBreak(String s, List wordDict) { + Trie trie = new Trie(); + for (String word : wordDict) { + trie.addWord(word); + } + + List res = new ArrayList<>(); + backtrack(0, s, new ArrayList<>(), trie, res); + return res; + } + + private void backtrack(int index, String s, List path, Trie trie, List res) { + if (index == s.length()) { + res.add(String.join(" ", path)); + return; + } + + TrieNode node = trie.root; + StringBuilder word = new StringBuilder(); + for (int i = index; i < s.length(); i++) { + char c = s.charAt(i); + if (!node.children.containsKey(c)) { + break; + } + + word.append(c); + node = node.children.get(c); + + if (node.isWord) { + path.add(word.toString()); + backtrack(i + 1, s, path, trie, res); + path.remove(path.size() - 1); + } + } + } +} +``` + +```cpp +struct TrieNode { + unordered_map children; + bool isWord = false; +}; + +class Trie { +public: + TrieNode* root; + + Trie() { + root = new TrieNode(); + } + + void addWord(const string& word) { + TrieNode* curr = root; + for (char c : word) { + if (!curr->children.count(c)) { + curr->children[c] = new TrieNode(); + } + curr = curr->children[c]; + } + curr->isWord = true; + } +}; + +class Solution { +public: + vector wordBreak(string s, vector& wordDict) { + Trie trie; + for (const string& word : wordDict) { + trie.addWord(word); + } + + vector res; + vector path; + backtrack(0, s, path, trie, res); + return res; + } + +private: + void backtrack(int index, string& s, vector& path, Trie& trie, vector& res) { + if (index == s.size()) { + stringstream ss; + for (int i = 0; i < path.size(); ++i) { + if (i > 0) ss << " "; + ss << path[i]; + } + res.push_back(ss.str()); + return; + } + + TrieNode* node = trie.root; + string word; + for (int i = index; i < s.size(); ++i) { + char c = s[i]; + if (!node->children.count(c)) break; + + word.push_back(c); + node = node->children[c]; + + if (node->isWord) { + path.push_back(word); + backtrack(i + 1, s, path, trie, res); + path.pop_back(); + } + } + } +}; +``` + +```javascript +class TrieNode { + constructor() { + this.children = new Map(); + this.isWord = false; + } +} + +class Trie { + constructor() { + this.root = new TrieNode(); + } + + /** + * @param {string} word + * @return {void} + */ + addWord(word) { + let curr = this.root; + for (const c of word) { + if (!curr.children.has(c)) { + curr.children.set(c, new TrieNode()); + } + curr = curr.children.get(c); + } + curr.isWord = true; + } +} + +class Solution { + /** + * @param {string} s + * @param {string[]} wordDict + * @return {string[]} + */ + wordBreak(s, wordDict) { + const trie = new Trie(); + for (const word of wordDict) { + trie.addWord(word); + } + + const res = []; + const backtrack = (index, path) => { + if (index === s.length) { + res.push(path.join(" ")); + return; + } + + let node = trie.root; + let word = ""; + for (let i = index; i < s.length; i++) { + const char = s[i]; + if (!node.children.has(char)) { + break; + } + + word += char; + node = node.children.get(char); + + if (node.isWord) { + path.push(word); + backtrack(i + 1, path); + path.pop(); + } + } + }; + + backtrack(0, []); + return res; + } +} +``` + +```csharp +public class TrieNode { + public Dictionary children = new Dictionary(); + public bool isWord = false; +} + +public class Trie { + public TrieNode root = new TrieNode(); + + public void AddWord(string word) { + TrieNode curr = root; + foreach (char c in word) { + if (!curr.children.ContainsKey(c)) { + curr.children[c] = new TrieNode(); + } + curr = curr.children[c]; + } + curr.isWord = true; + } +} + +public class Solution { + public List WordBreak(string s, List wordDict) { + Trie trie = new Trie(); + foreach (string word in wordDict) { + trie.AddWord(word); + } + + List res = new List(); + + void Backtrack(int index, List path) { + if (index == s.Length) { + res.Add(string.Join(" ", path)); + return; + } + + TrieNode node = trie.root; + StringBuilder word = new StringBuilder(); + + for (int i = index; i < s.Length; i++) { + char c = s[i]; + if (!node.children.ContainsKey(c)) break; + + word.Append(c); + node = node.children[c]; + + if (node.isWord) { + path.Add(word.ToString()); + Backtrack(i + 1, path); + path.RemoveAt(path.Count - 1); + } + } + } + + Backtrack(0, new List()); + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n * 2 ^ n)$ +* Space complexity: $O(m + 2 ^ n)$ + +> Where $n$ is the length of the string $s$ and $m$ is the sum of the lengths of the strings in the $wordDict$. + +--- + +## 3. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> List[str]: + wordDict = set(wordDict) + cache = {} + + def backtrack(i): + if i == len(s): + return [""] + if i in cache: + return cache[i] + + res = [] + for j in range(i, len(s)): + w = s[i:j + 1] + if w not in wordDict: + continue + strings = backtrack(j + 1) + for substr in strings: + sentence = w + if substr: + sentence += " " + substr + res.append(sentence) + cache[i] = res + return res + + return backtrack(0) +``` + +```java +public class Solution { + private Set wordSet; + private Map> cache; + + public List wordBreak(String s, List wordDict) { + wordSet = new HashSet<>(wordDict); + cache = new HashMap<>(); + return backtrack(s, 0); + } + + private List backtrack(String s, int i) { + if (i == s.length()) + return Arrays.asList(""); + if (cache.containsKey(i)) + return cache.get(i); + + List res = new ArrayList<>(); + for (int j = i; j < s.length(); j++) { + String w = s.substring(i, j + 1); + if (!wordSet.contains(w)) + continue; + List strings = backtrack(s, j + 1); + for (String substr : strings) { + String sentence = w; + if (!substr.isEmpty()) + sentence += " " + substr; + res.add(sentence); + } + } + cache.put(i, res); + return res; + } +} +``` + +```cpp +class Solution { +public: + vector wordBreak(string s, vector& wordDict) { + wordSet = unordered_set(wordDict.begin(), wordDict.end()); + cache = unordered_map>(); + return backtrack(s, 0); + } + +private: + unordered_set wordSet; + unordered_map> cache; + + vector backtrack(const string& s, int i) { + if (i == s.size()) + return {""}; + if (cache.count(i)) + return cache[i]; + + vector res; + for (int j = i; j < s.size(); ++j) { + string w = s.substr(i, j - i + 1); + if (!wordSet.count(w)) + continue; + vector strings = backtrack(s, j + 1); + for (const string& substr : strings) { + string sentence = w; + if (!substr.empty()) + sentence += " " + substr; + res.push_back(sentence); + } + } + cache[i] = res; + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string[]} wordDict + * @return {string[]} + */ + wordBreak(s, wordDict) { + const wordSet = new Set(wordDict); + const cache = new Map(); + + const backtrack = (i) => { + if (i === s.length) { + return [""]; + } + if (cache.has(i)) { + return cache.get(i); + } + + const res = []; + for (let j = i; j < s.length; j++) { + const w = s.substring(i, j + 1); + if (!wordSet.has(w)) continue; + + const strings = backtrack(j + 1); + for (const substr of strings) { + let sentence = w; + if (substr) { + sentence += " " + substr; + } + res.push(sentence); + } + } + cache.set(i, res); + return res; + }; + + return backtrack(0); + } +} +``` + +```csharp +public class Solution { + public List WordBreak(string s, List wordDictList) { + HashSet wordDict = new HashSet(wordDictList); + Dictionary> cache = new Dictionary>(); + + List Backtrack(int i) { + if (i == s.Length) return new List { "" }; + if (cache.ContainsKey(i)) return cache[i]; + + List res = new List(); + for (int j = i; j < s.Length; j++) { + string w = s.Substring(i, j - i + 1); + if (!wordDict.Contains(w)) continue; + + List substrings = Backtrack(j + 1); + foreach (string substr in substrings) { + string sentence = w; + if (!string.IsNullOrEmpty(substr)) { + sentence += " " + substr; + } + res.Add(sentence); + } + } + + cache[i] = res; + return res; + } + + return Backtrack(0); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n * 2 ^ n)$ +* Space complexity: $O(m + n * 2 ^ n)$ + +> Where $n$ is the length of the string $s$ and $m$ is the sum of the lengths of the strings in the $wordDict$. + +--- + +## 4. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> List[str]: + wordSet = set(wordDict) + n = len(s) + dp = [[] for _ in range(n + 1)] + dp[0] = [""] + + for i in range(1, n + 1): + for j in range(i): + if s[j:i] in wordSet: + for sentence in dp[j]: + dp[i].append((sentence + " " + s[j:i]).strip()) + + return dp[n] +``` + +```java +public class Solution { + public List wordBreak(String s, List wordDict) { + Set wordSet = new HashSet<>(wordDict); + int n = s.length(); + List[] dp = new ArrayList[n + 1]; + for (int i = 0; i <= n; i++) { + dp[i] = new ArrayList<>(); + } + dp[0].add(""); + + for (int i = 1; i <= n; i++) { + for (int j = 0; j < i; j++) { + if (wordSet.contains(s.substring(j, i))) { + for (String sentence : dp[j]) { + dp[i].add((sentence + " " + s.substring(j, i)).trim()); + } + } + } + } + + return dp[n]; + } +} +``` + +```cpp +class Solution { +public: + vector wordBreak(string s, vector& wordDict) { + unordered_set wordSet(wordDict.begin(), wordDict.end()); + int n = s.size(); + vector> dp(n + 1); + dp[0] = {""}; + + for (int i = 1; i <= n; ++i) { + for (int j = 0; j < i; ++j) { + string word = s.substr(j, i - j); + if (wordSet.count(word)) { + for (const string& sentence : dp[j]) { + if (sentence.empty()) { + dp[i].push_back(word); + } else { + dp[i].push_back(sentence + " " + word); + } + } + } + } + } + + return dp[n]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string[]} wordDict + * @return {string[]} + */ + wordBreak(s, wordDict) { + const wordSet = new Set(wordDict); + const n = s.length; + const dp = Array.from({ length: n + 1 }, () => []); + dp[0].push(""); + + for (let i = 1; i <= n; i++) { + for (let j = 0; j < i; j++) { + if (wordSet.has(s.substring(j, i))) { + for (const sentence of dp[j]) { + dp[i].push((sentence + " " + s.substring(j, i)).trim()); + } + } + } + } + + return dp[n]; + } +} +``` + +```csharp +public class Solution { + public List WordBreak(string s, List wordDictList) { + HashSet wordSet = new HashSet(wordDictList); + int n = s.Length; + List[] dp = new List[n + 1]; + for (int i = 0; i <= n; i++) { + dp[i] = new List(); + } + dp[0].Add(""); + + for (int i = 1; i <= n; i++) { + for (int j = 0; j < i; j++) { + string word = s.Substring(j, i - j); + if (wordSet.Contains(word)) { + foreach (string sentence in dp[j]) { + string space = string.IsNullOrEmpty(sentence) ? "" : " "; + dp[i].Add(sentence + space + word); + } + } + } + } + + return dp[n]; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n * 2 ^ n)$ +* Space complexity: $O(m + n * 2 ^ n)$ + +> Where $n$ is the length of the string $s$ and $m$ is the sum of the lengths of the strings in the $wordDict$. + +--- + +## 5. Dynamic Programming (Top-Down) Using Trie + +::tabs-start + +```python +class TrieNode: + def __init__(self): + self.children = {} + self.isWord = False + +class Trie: + def __init__(self): + self.root = TrieNode() + + def addWord(self, word): + curr = self.root + for c in word: + if c not in curr.children: + curr.children[c] = TrieNode() + curr = curr.children[c] + curr.isWord = True + +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> List[str]: + trie = Trie() + for word in wordDict: + trie.addWord(word) + + cache = {} + + def backtrack(index): + if index == len(s): + return [""] + if index in cache: + return cache[index] + + res = [] + curr = trie.root + for i in range(index, len(s)): + char = s[i] + if char not in curr.children: + break + curr = curr.children[char] + if curr.isWord: + for suffix in backtrack(i + 1): + if suffix: + res.append(s[index:i + 1] + " " + suffix) + else: + res.append(s[index:i + 1]) + + cache[index] = res + return res + + return backtrack(0) +``` + +```java +class TrieNode { + Map children = new HashMap<>(); + boolean isWord = false; +} + +class Trie { + TrieNode root; + + Trie() { + root = new TrieNode(); + } + + void addWord(String word) { + TrieNode curr = root; + for (char c : word.toCharArray()) { + curr.children.putIfAbsent(c, new TrieNode()); + curr = curr.children.get(c); + } + curr.isWord = true; + } +} + +public class Solution { + public List wordBreak(String s, List wordDict) { + Trie trie = new Trie(); + for (String word : wordDict) { + trie.addWord(word); + } + + Map> cache = new HashMap<>(); + return backtrack(0, s, trie, cache); + } + + private List backtrack(int index, String s, Trie trie, Map> cache) { + if (index == s.length()) { + return Collections.singletonList(""); + } + + if (cache.containsKey(index)) { + return cache.get(index); + } + + List res = new ArrayList<>(); + TrieNode curr = trie.root; + + for (int i = index; i < s.length(); i++) { + char c = s.charAt(i); + if (!curr.children.containsKey(c)) { + break; + } + curr = curr.children.get(c); + if (curr.isWord) { + for (String suffix : backtrack(i + 1, s, trie, cache)) { + if (!suffix.isEmpty()) { + res.add(s.substring(index, i + 1) + " " + suffix); + } else { + res.add(s.substring(index, i + 1)); + } + } + } + } + + cache.put(index, res); + return res; + } +} +``` + +```cpp +struct TrieNode { + unordered_map children; + bool isWord = false; +}; + +class Trie { +public: + TrieNode* root; + + Trie() { + root = new TrieNode(); + } + + void addWord(const string& word) { + TrieNode* curr = root; + for (char c : word) { + if (!curr->children.count(c)) { + curr->children[c] = new TrieNode(); + } + curr = curr->children[c]; + } + curr->isWord = true; + } +}; + +class Solution { +public: + vector wordBreak(string s, vector& wordDict) { + Trie trie; + for (const string& word : wordDict) { + trie.addWord(word); + } + + unordered_map> cache; + return backtrack(0, s, trie, cache); + } + +private: + vector backtrack(int index, string& s, Trie& trie, unordered_map>& cache) { + if (index == s.size()) { + return {""}; + } + + if (cache.count(index)) { + return cache[index]; + } + + vector res; + TrieNode* curr = trie.root; + + for (int i = index; i < s.size(); ++i) { + char c = s[i]; + if (!curr->children.count(c)) { + break; + } + curr = curr->children[c]; + if (curr->isWord) { + for (const string& suffix : backtrack(i + 1, s, trie, cache)) { + if (!suffix.empty()) { + res.push_back(s.substr(index, i - index + 1) + " " + suffix); + } else { + res.push_back(s.substr(index, i - index + 1)); + } + } + } + } + + return cache[index] = res; + } +}; +``` + +```javascript +class TrieNode { + constructor() { + this.children = new Map(); + this.isWord = false; + } +} + +class Trie { + constructor() { + this.root = new TrieNode(); + } + + /** + * @param {string} word + * @return {void} + */ + addWord(word) { + let curr = this.root; + for (const c of word) { + if (!curr.children.has(c)) { + curr.children.set(c, new TrieNode()); + } + curr = curr.children.get(c); + } + curr.isWord = true; + } +} + +class Solution { + /** + * @param {string} s + * @param {string[]} wordDict + * @return {string[]} + */ + wordBreak(s, wordDict) { + const trie = new Trie(); + for (const word of wordDict) { + trie.addWord(word); + } + + const cache = new Map(); + + const backtrack = (index) => { + if (index === s.length) { + return [""]; + } + if (cache.has(index)) { + return cache.get(index); + } + + const res = []; + let curr = trie.root; + + for (let i = index; i < s.length; i++) { + const char = s[i]; + if (!curr.children.has(char)) { + break; + } + curr = curr.children.get(char); + if (curr.isWord) { + for (const suffix of backtrack(i + 1)) { + if (suffix) { + res.push(s.slice(index, i + 1) + " " + suffix); + } else { + res.push(s.slice(index, i + 1)); + } + } + } + } + + cache.set(index, res); + return res; + }; + + return backtrack(0); + } +} +``` + +```csharp +public class TrieNode { + public Dictionary Children = new Dictionary(); + public bool IsWord = false; +} + +public class Trie { + public TrieNode Root = new TrieNode(); + + public void AddWord(string word) { + TrieNode curr = Root; + foreach (char c in word) { + if (!curr.Children.ContainsKey(c)) { + curr.Children[c] = new TrieNode(); + } + curr = curr.Children[c]; + } + curr.IsWord = true; + } +} + +public class Solution { + private Dictionary> cache = new Dictionary>(); + + public List WordBreak(string s, List wordDict) { + Trie trie = new Trie(); + foreach (string word in wordDict) { + trie.AddWord(word); + } + return Backtrack(0, s, trie.Root, trie); + } + + private List Backtrack(int index, string s, TrieNode root, Trie trie) { + if (index == s.Length) return new List { "" }; + if (cache.ContainsKey(index)) return cache[index]; + + List res = new List(); + TrieNode curr = root; + + for (int i = index; i < s.Length; i++) { + char c = s[i]; + if (!curr.Children.ContainsKey(c)) break; + + curr = curr.Children[c]; + if (curr.IsWord) { + List suffixes = Backtrack(i + 1, s, root, trie); + foreach (string suffix in suffixes) { + if (suffix == "") { + res.Add(s.Substring(index, i - index + 1)); + } else { + res.Add(s.Substring(index, i - index + 1) + " " + suffix); + } + } + } + } + + cache[index] = res; + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(m + n * 2 ^ n)$ +* Space complexity: $O(m + n * 2 ^ n)$ + +> Where $n$ is the length of the string $s$ and $m$ is the sum of the lengths of the strings in the $wordDict$. \ No newline at end of file diff --git a/articles/word-break.md b/articles/word-break.md new file mode 100644 index 000000000..acc8366d3 --- /dev/null +++ b/articles/word-break.md @@ -0,0 +1,1712 @@ +## 1. Recursion + +::tabs-start + +```python +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> bool: + + def dfs(i): + if i == len(s): + return True + + for w in wordDict: + if ((i + len(w)) <= len(s) and + s[i : i + len(w)] == w + ): + if dfs(i + len(w)): + return True + return False + + return dfs(0) +``` + +```java +public class Solution { + public boolean wordBreak(String s, List wordDict) { + return dfs(s, wordDict, 0); + } + + private boolean dfs(String s, List wordDict, int i) { + if (i == s.length()) { + return true; + } + + for (String w : wordDict) { + if (i + w.length() <= s.length() && + s.substring(i, i + w.length()).equals(w)) { + if (dfs(s, wordDict, i + w.length())) { + return true; + } + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool wordBreak(string s, vector& wordDict) { + return dfs(s, wordDict, 0); + } + +private: + bool dfs(const string& s, const vector& wordDict, int i) { + if (i == s.length()) { + return true; + } + + for (const string& w : wordDict) { + if (i + w.length() <= s.length() && + s.substr(i, w.length()) == w) { + if (dfs(s, wordDict, i + w.length())) { + return true; + } + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string[]} wordDict + * @return {boolean} + */ + wordBreak(s, wordDict) { + return this.dfs(s, wordDict, 0); + } + + /** + * @param {string} s + * @param {string[]} wordDict + * @param {number} i + * @return {boolean} + */ + dfs(s, wordDict, i) { + if (i === s.length) { + return true; + } + + for (let w of wordDict) { + if (i + w.length <= s.length && + s.substring(i, i + w.length) === w) { + if (this.dfs(s, wordDict, i + w.length)) { + return true; + } + } + } + return false; + } +} +``` + +```csharp +public class Solution { + public bool WordBreak(string s, List wordDict) { + return Dfs(s, wordDict, 0); + } + + private bool Dfs(string s, List wordDict, int i) { + if (i == s.Length) { + return true; + } + + foreach (string w in wordDict) { + if (i + w.Length <= s.Length && + s.Substring(i, w.Length) == w) { + if (Dfs(s, wordDict, i + w.Length)) { + return true; + } + } + } + return false; + } +} +``` + +```go +func wordBreak(s string, wordDict []string) bool { + return dfs(s, wordDict, 0) +} + +func dfs(s string, wordDict []string, i int) bool { + if i == len(s) { + return true + } + + for _, w := range wordDict { + if len(s[i:]) >= len(w) && s[i:i+len(w)] == w { + if dfs(s, wordDict, i+len(w)) { + return true + } + } + } + + return false +} +``` + +```kotlin +class Solution { + fun wordBreak(s: String, wordDict: List): Boolean { + return dfs(s, wordDict, 0) + } + + private fun dfs(s: String, wordDict: List, i: Int): Boolean { + if (i == s.length) { + return true + } + + for (w in wordDict) { + if (s.length - i >= w.length && s.substring(i, i + w.length) == w) { + if (dfs(s, wordDict, i + w.length)) { + return true + } + } + } + + return false + } +} +``` + +```swift +class Solution { + func wordBreak(_ s: String, _ wordDict: [String]) -> Bool { + let chars = Array(s) + + func dfs(_ i: Int) -> Bool { + if i == chars.count { + return true + } + + for w in wordDict { + if i + w.count <= chars.count, + String(chars[i.. Where $n$ is the length of the string $s$, $m$ is the number of words in $wordDict$ and $t$ is the maximum length of any word in $wordDict$. + +--- + +## 2. Recursion (Hash Set) + +::tabs-start + +```python +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> bool: + wordSet = set(wordDict) + + def dfs(i): + if i == len(s): + return True + + for j in range(i, len(s)): + if s[i : j + 1] in wordSet: + if dfs(j + 1): + return True + return False + + return dfs(0) +``` + +```java +public class Solution { + public boolean wordBreak(String s, List wordDict) { + HashSet wordSet = new HashSet<>(wordDict); + + return dfs(s, wordSet, 0); + } + + private boolean dfs(String s, HashSet wordSet, int i) { + if (i == s.length()) { + return true; + } + + for (int j = i; j < s.length(); j++) { + if (wordSet.contains(s.substring(i, j + 1))) { + if (dfs(s, wordSet, j + 1)) { + return true; + } + } + } + return false; + } +} +``` + +```cpp +class Solution { +public: + bool wordBreak(string s, vector& wordDict) { + unordered_set wordSet(wordDict.begin(), wordDict.end()); + return dfs(s, wordSet, 0); + } + + bool dfs(const string& s, const unordered_set& wordSet, int i) { + if (i == s.size()) { + return true; + } + + for (int j = i; j < s.size(); j++) { + if (wordSet.find(s.substr(i, j - i + 1)) != wordSet.end()) { + if (dfs(s, wordSet, j + 1)) { + return true; + } + } + } + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string[]} wordDict + * @return {boolean} + */ + wordBreak(s, wordDict) { + const wordSet = new Set(wordDict); + const dfs = (i) => { + if (i === s.length) { + return true; + } + + for (let j = i; j < s.length; j++) { + if (wordSet.has(s.substring(i, j + 1))) { + if (dfs(j + 1)) { + return true; + } + } + } + return false; + } + + return dfs(0); + } +} +``` + +```csharp +public class Solution { + public bool WordBreak(string s, List wordDict) { + HashSet wordSet = new HashSet(wordDict); + return Dfs(s, wordSet, 0); + } + + private bool Dfs(string s, HashSet wordSet, int i) { + if (i == s.Length) { + return true; + } + + for (int j = i; j < s.Length; j++) { + if (wordSet.Contains(s.Substring(i, j - i + 1))) { + if (Dfs(s, wordSet, j + 1)) { + return true; + } + } + } + return false; + } +} +``` + +```go +func wordBreak(s string, wordDict []string) bool { + wordSet := make(map[string]bool) + for _, w := range wordDict { + wordSet[w] = true + } + return dfs(s, wordSet, 0) +} + +func dfs(s string, wordSet map[string]bool, i int) bool { + if i == len(s) { + return true + } + + for j := i; j < len(s); j++ { + if wordSet[s[i:j+1]] { + if dfs(s, wordSet, j+1) { + return true + } + } + } + + return false +} +``` + +```kotlin +class Solution { + fun wordBreak(s: String, wordDict: List): Boolean { + val wordSet = wordDict.toHashSet() + return dfs(s, wordSet, 0) + } + + private fun dfs(s: String, wordSet: HashSet, i: Int): Boolean { + if (i == s.length) { + return true + } + + for (j in i until s.length) { + if (wordSet.contains(s.substring(i, j + 1))) { + if (dfs(s, wordSet, j + 1)) { + return true + } + } + } + + return false + } +} +``` + +```swift +class Solution { + func wordBreak(_ s: String, _ wordDict: [String]) -> Bool { + let wordSet = Set(wordDict) + let chars = Array(s) + + func dfs(_ i: Int) -> Bool { + if i == chars.count { + return true + } + + for j in i..Where $n$ is the length of the string $s$ and $m$ is the number of words in $wordDict$. + +--- + +## 3. Dynamic Programming (Top-Down) + +::tabs-start + +```python +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> bool: + memo = {len(s) : True} + def dfs(i): + if i in memo: + return memo[i] + + for w in wordDict: + if ((i + len(w)) <= len(s) and + s[i : i + len(w)] == w + ): + if dfs(i + len(w)): + memo[i] = True + return True + memo[i] = False + return False + + return dfs(0) +``` + +```java +public class Solution { + private Map memo; + + public boolean wordBreak(String s, List wordDict) { + memo = new HashMap<>(); + memo.put(s.length(), true); + return dfs(s, wordDict, 0); + } + + private boolean dfs(String s, List wordDict, int i) { + if (memo.containsKey(i)) { + return memo.get(i); + } + + for (String w : wordDict) { + if (i + w.length() <= s.length() && + s.substring(i, i + w.length()).equals(w)) { + if (dfs(s, wordDict, i + w.length())) { + memo.put(i, true); + return true; + } + } + } + memo.put(i, false); + return false; + } +} +``` + +```cpp +class Solution { +public: + unordered_map memo; + + bool wordBreak(string s, vector& wordDict) { + memo[s.length()] = true; + return dfs(s, wordDict, 0); + } + + bool dfs(string& s, vector& wordDict, int i) { + if (memo.find(i) != memo.end()) { + return memo[i]; + } + + for (const string& w : wordDict) { + if (i + w.length() <= s.length() && + s.substr(i, w.length()) == w) { + if (dfs(s, wordDict, i + w.length())) { + memo[i] = true; + return true; + } + } + } + memo[i] = false; + return false; + } +}; +``` + +```javascript +class Solution { + constructor() { + this.memo = {}; + } + + /** + * @param {string} s + * @param {string[]} wordDict + * @return {boolean} + */ + wordBreak(s, wordDict) { + this.memo = { [s.length]: true }; + return this.dfs(s, wordDict, 0); + } + + /** + * @param {string} s + * @param {string[]} wordDict + * @param {number} i + * @return {boolean} + */ + dfs(s, wordDict, i) { + if (i in this.memo) { + return this.memo[i]; + } + + for (let w of wordDict) { + if (i + w.length <= s.length && + s.substring(i, i + w.length) === w) { + if (this.dfs(s, wordDict, i + w.length)) { + this.memo[i] = true; + return true; + } + } + } + this.memo[i] = false; + return false; + } +} +``` + +```csharp +public class Solution { + private Dictionary memo; + + public bool WordBreak(string s, List wordDict) { + memo = new Dictionary { { s.Length, true } }; + return Dfs(s, wordDict, 0); + } + + private bool Dfs(string s, List wordDict, int i) { + if (memo.ContainsKey(i)) { + return memo[i]; + } + + foreach (var w in wordDict) { + if (i + w.Length <= s.Length && + s.Substring(i, w.Length) == w) { + if (Dfs(s, wordDict, i + w.Length)) { + memo[i] = true; + return true; + } + } + } + memo[i] = false; + return false; + } +} +``` + +```go +func wordBreak(s string, wordDict []string) bool { + memo := make(map[int]bool) + memo[len(s)] = true + + var dfs func(int) bool + dfs = func(i int) bool { + if val, found := memo[i]; found { + return val + } + + for _, w := range wordDict { + if i+len(w) <= len(s) && s[i:i+len(w)] == w { + if dfs(i + len(w)) { + memo[i] = true + return true + } + } + } + + memo[i] = false + return false + } + + return dfs(0) +} +``` + +```kotlin +class Solution { + fun wordBreak(s: String, wordDict: List): Boolean { + val memo = hashMapOf(s.length to true) + + fun dfs(i: Int): Boolean { + memo[i]?.let { return it } + + for (w in wordDict) { + if (i + w.length <= s.length && + s.substring(i, i + w.length) == w) { + if (dfs(i + w.length)) { + memo[i] = true + return true + } + } + } + + memo[i] = false + return false + } + + return dfs(0) + } +} +``` + +```swift +class Solution { + func wordBreak(_ s: String, _ wordDict: [String]) -> Bool { + var memo = [Int: Bool]() + memo[s.count] = true + let chars = Array(s) + + func dfs(_ i: Int) -> Bool { + if let cached = memo[i] { + return cached + } + + for w in wordDict { + if i + w.count <= chars.count, + String(chars[i.. Where $n$ is the length of the string $s$, $m$ is the number of words in $wordDict$ and $t$ is the maximum length of any word in $wordDict$. + +--- + +## 4. Dynamic Programming (Hash Set) + +::tabs-start + +```python +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> bool: + wordSet = set(wordDict) + t = 0 + for w in wordDict: + t = max(t, len(w)) + + memo = {} + def dfs(i): + if i in memo: + return memo[i] + if i == len(s): + return True + for j in range(i, min(len(s), i + t)): + if s[i : j + 1] in wordSet: + if dfs(j + 1): + memo[i] = True + return True + memo[i] = False + return False + + return dfs(0) +``` + +```java +public class Solution { + private HashSet wordSet; + private Boolean[] memo; + private int t; + + public boolean wordBreak(String s, List wordDict) { + wordSet = new HashSet<>(wordDict); + memo = new Boolean[s.length()]; + t = 0; + for (int i = 0; i < wordDict.size(); i++) { + t = Math.max(t, wordDict.get(i).length()); + } + return dfs(s, 0); + } + + private boolean dfs(String s, int i) { + if (i == s.length()) { + return true; + } + if (memo[i] != null) { + return memo[i]; + } + + for (int j = i; j < Math.min(i + t, s.length()); j++) { + if (wordSet.contains(s.substring(i, j + 1))) { + if (dfs(s, j + 1)) { + memo[i] = true; + return true; + } + } + } + memo[i] = false; + return false; + } +} +``` + +```cpp +class Solution { +public: + unordered_set wordSet; + vector memo; + int t; + + bool wordBreak(string s, vector& wordDict) { + wordSet.insert(wordDict.begin(), wordDict.end()); + memo.resize(s.size(), -1); + t = 0; + for (string& w : wordDict) { + t = max(t, int(w.length())); + } + return dfs(s, 0); + } + + bool dfs(string& s, int i) { + if (i == s.size()) { + return true; + } + if (memo[i] != -1) { + return memo[i] == 1; + } + + for (int j = i; j < (i + t, s.size()); j++) { + if (wordSet.count(s.substr(i, j - i + 1))) { + if (dfs(s, j + 1)) { + memo[i] = 1; + return true; + } + } + } + memo[i] = 0; + return false; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string[]} wordDict + * @return {boolean} + */ + wordBreak(s, wordDict) { + this.wordSet = new Set(wordDict); + this.memo = new Array(s.length).fill(null); + this.t = 0; + for (const w of wordDict) { + this.t = Math.max(this.t, w.length); + } + return this.dfs(s, 0); + } + + /** + * @param {string} s + * @param {number} i + * @return {boolean} + */ + dfs(s, i) { + if (i === s.length) { + return true; + } + if (this.memo[i] !== null) { + return this.memo[i]; + } + + for (let j = i; j < Math.min(i + this.t, s.length); j++) { + if (this.wordSet.has(s.substring(i, j + 1))) { + if (this.dfs(s, j + 1)) { + this.memo[i] = true; + return true; + } + } + } + this.memo[i] = false; + return false; + } +} +``` + +```csharp +public class Solution { + private HashSet wordSet; + private Dictionary memo; + private int t; + + public bool WordBreak(string s, List wordDict) { + wordSet = new HashSet(wordDict); + memo = new Dictionary(); + t = 0; + foreach (var w in wordDict) { + t = Math.Max(t, w.Length); + } + return Dfs(s, 0); + } + + private bool Dfs(string s, int i) { + if (i == s.Length) { + return true; + } + if (memo.ContainsKey(i)) { + return memo[i]; + } + + for (int j = i; j < Math.Min(i + t, s.Length); j++) { + if (wordSet.Contains(s.Substring(i, j - i + 1))) { + if (Dfs(s, j + 1)) { + memo[i] = true; + return true; + } + } + } + memo[i] = false; + return false; + } +} +``` + +```go +func wordBreak(s string, wordDict []string) bool { + wordSet := make(map[string]bool) + maxLen := 0 + + for _, w := range wordDict { + wordSet[w] = true + if len(w) > maxLen { + maxLen = len(w) + } + } + + memo := make(map[int]bool) + + var dfs func(int) bool + dfs = func(i int) bool { + if val, found := memo[i]; found { + return val + } + if i == len(s) { + return true + } + for j := i; j < len(s) && j < i+maxLen; j++ { + if wordSet[s[i:j+1]] { + if dfs(j + 1) { + memo[i] = true + return true + } + } + } + memo[i] = false + return false + } + + return dfs(0) +} +``` + +```kotlin +class Solution { + fun wordBreak(s: String, wordDict: List): Boolean { + val wordSet = wordDict.toSet() + val maxLen = wordDict.maxOfOrNull { it.length } ?: 0 + val memo = HashMap() + + fun dfs(i: Int): Boolean { + memo[i]?.let { return it } + if (i == s.length) return true + + for (j in i until minOf(s.length, i + maxLen)) { + if (s.substring(i, j + 1) in wordSet) { + if (dfs(j + 1)) { + memo[i] = true + return true + } + } + } + + memo[i] = false + return false + } + + return dfs(0) + } +} +``` + +```swift +class Solution { + func wordBreak(_ s: String, _ wordDict: [String]) -> Bool { + let wordSet = Set(wordDict) + var maxLen = 0 + for w in wordDict { + maxLen = max(maxLen, w.count) + } + + var memo = [Int: Bool]() + let chars = Array(s) + + func dfs(_ i: Int) -> Bool { + if let cached = memo[i] { + return cached + } + if i == chars.count { + return true + } + for j in i.. Where $n$ is the length of the string $s$, $m$ is the number of words in $wordDict$ and $t$ is the maximum length of any word in $wordDict$. + +--- + +## 5. Dynamic Programming (Bottom-Up) + +::tabs-start + +```python +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> bool: + dp = [False] * (len(s) + 1) + dp[len(s)] = True + + for i in range(len(s) - 1, -1, -1): + for w in wordDict: + if (i + len(w)) <= len(s) and s[i : i + len(w)] == w: + dp[i] = dp[i + len(w)] + if dp[i]: + break + + return dp[0] +``` + +```java +public class Solution { + public boolean wordBreak(String s, List wordDict) { + boolean[] dp = new boolean[s.length() + 1]; + dp[s.length()] = true; + + for (int i = s.length() - 1; i >= 0; i--) { + for (String w : wordDict) { + if ((i + w.length()) <= s.length() && + s.substring(i, i + w.length()).equals(w)) { + dp[i] = dp[i + w.length()]; + } + if (dp[i]) { + break; + } + } + } + + return dp[0]; + } +} +``` + +```cpp +class Solution { +public: + bool wordBreak(string s, vector& wordDict) { + vector dp(s.size() + 1, false); + dp[s.size()] = true; + + for (int i = s.size() - 1; i >= 0; i--) { + for (const auto& w : wordDict) { + if ((i + w.size()) <= s.size() && + s.substr(i, w.size()) == w) { + dp[i] = dp[i + w.size()]; + } + if (dp[i]) { + break; + } + } + } + + return dp[0]; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {string[]} wordDict + * @return {boolean} + */ + wordBreak(s, wordDict) { + const dp = new Array(s.length + 1).fill(false); + dp[s.length] = true; + + for (let i = s.length - 1; i >= 0; i--) { + for (const w of wordDict) { + if ( i + w.length <= s.length && + s.slice(i, i + w.length) === w) { + dp[i] = dp[i + w.length]; + } + if (dp[i]) { + break; + } + } + } + + return dp[0]; + } +} +``` + +```csharp +public class Solution { + public bool WordBreak(string s, List wordDict) { + bool[] dp = new bool[s.Length + 1]; + dp[s.Length] = true; + + for (int i = s.Length - 1; i >= 0; i--) { + foreach (string w in wordDict) { + if ((i + w.Length) <= s.Length && + s.Substring(i, w.Length) == w) { + dp[i] = dp[i + w.Length]; + } + if (dp[i]) { + break; + } + } + } + + return dp[0]; + } +} +``` + +```go +func wordBreak(s string, wordDict []string) bool { + dp := make([]bool, len(s)+1) + dp[len(s)] = true + + for i := len(s) - 1; i >= 0; i-- { + for _, w := range wordDict { + if i+len(w) <= len(s) && s[i:i+len(w)] == w { + dp[i] = dp[i+len(w)] + } + if dp[i] { + break + } + } + } + + return dp[0] +} +``` + +```kotlin +class Solution { + fun wordBreak(s: String, wordDict: List): Boolean { + val dp = BooleanArray(s.length + 1) + dp[s.length] = true + + for (i in s.length - 1 downTo 0) { + for (w in wordDict) { + if (i + w.length <= s.length && + s.substring(i, i + w.length) == w) { + dp[i] = dp[i + w.length] + } + if (dp[i]) break + } + } + + return dp[0] + } +} +``` + +```swift +class Solution { + func wordBreak(_ s: String, _ wordDict: [String]) -> Bool { + var dp = Array(repeating: false, count: s.count + 1) + dp[s.count] = true + let chars = Array(s) + + for i in stride(from: s.count - 1, through: 0, by: -1) { + for w in wordDict { + if i + w.count <= chars.count, String(chars[i.. Where $n$ is the length of the string $s$, $m$ is the number of words in $wordDict$ and $t$ is the maximum length of any word in $wordDict$. + +--- + +## 6. Dynamic Programming (Trie) + +::tabs-start + +```python +class TrieNode: + def __init__(self): + self.children = {} + self.is_word = False + +class Trie: + def __init__(self): + self.root = TrieNode() + + def insert(self, word): + node = self.root + for char in word: + if char not in node.children: + node.children[char] = TrieNode() + node = node.children[char] + node.is_word = True + + def search(self, s, i, j): + node = self.root + for idx in range(i, j + 1): + if s[idx] not in node.children: + return False + node = node.children[s[idx]] + return node.is_word + +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> bool: + trie = Trie() + for word in wordDict: + trie.insert(word) + + dp = [False] * (len(s) + 1) + dp[len(s)] = True + + t = 0 + for w in wordDict: + t = max(t, len(w)) + + for i in range(len(s), -1, -1): + for j in range(i, min(len(s), i + t)): + if trie.search(s, i, j): + dp[i] = dp[j + 1] + if dp[i]: + break + + return dp[0] +``` + +```java +class TrieNode { + HashMap children; + boolean isWord; + + public TrieNode() { + children = new HashMap<>(); + isWord = false; + } +} + +class Trie { + TrieNode root; + + public Trie() { + root = new TrieNode(); + } + + public void insert(String word) { + TrieNode node = root; + for (char c : word.toCharArray()) { + node.children.putIfAbsent(c, new TrieNode()); + node = node.children.get(c); + } + node.isWord = true; + } + + public boolean search(String s, int i, int j) { + TrieNode node = root; + for (int idx = i; idx <= j; idx++) { + if (!node.children.containsKey(s.charAt(idx))) { + return false; + } + node = node.children.get(s.charAt(idx)); + } + return node.isWord; + } +} + +public class Solution { + public boolean wordBreak(String s, List wordDict) { + Trie trie = new Trie(); + for (String word : wordDict) { + trie.insert(word); + } + + int n = s.length(); + boolean[] dp = new boolean[n + 1]; + dp[n] = true; + + int maxLen = 0; + for (String word : wordDict) { + maxLen = Math.max(maxLen, word.length()); + } + + for (int i = n - 1; i >= 0; i--) { + for (int j = i; j < Math.min(n, i + maxLen); j++) { + if (trie.search(s, i, j)) { + dp[i] = dp[j + 1]; + if (dp[i]) break; + } + } + } + + return dp[0]; + } +} +``` + +```cpp +class TrieNode { +public: + unordered_map children; + bool is_word = false; +}; + +class Trie { +public: + TrieNode* root; + + Trie() { + root = new TrieNode(); + } + + void insert(string word) { + TrieNode* node = root; + for (char c : word) { + if (!node->children.count(c)) { + node->children[c] = new TrieNode(); + } + node = node->children[c]; + } + node->is_word = true; + } + + bool search(string& s, int i, int j) { + TrieNode* node = root; + for (int idx = i; idx <= j; ++idx) { + if (!node->children.count(s[idx])) { + return false; + } + node = node->children[s[idx]]; + } + return node->is_word; + } +}; + +class Solution { +public: + bool wordBreak(string s, vector& wordDict) { + Trie trie; + for (string word : wordDict) { + trie.insert(word); + } + + int n = s.length(); + vector dp(n + 1, false); + dp[n] = true; + + int maxLen = 0; + for (string w : wordDict) { + maxLen = max(maxLen, (int)w.size()); + } + + for (int i = n - 1; i >= 0; --i) { + for (int j = i; j < min(n, i + maxLen); ++j) { + if (trie.search(s, i, j)) { + dp[i] = dp[j + 1]; + if (dp[i]) break; + } + } + } + + return dp[0]; + } +}; +``` + +```javascript +class TrieNode { + constructor() { + this.children = {}; + this.isWord = false; + } +} + +class Trie { + constructor() { + this.root = new TrieNode(); + } + + /** + * @param {string} word + * @return {void} + */ + insert(word) { + let node = this.root; + for (let char of word) { + if (!node.children[char]) { + node.children[char] = new TrieNode(); + } + node = node.children[char]; + } + node.isWord = true; + } + + /** + * @param {string} s + * @param {number} i + * @param {number} j + * @return {boolean} + */ + search(s, i, j) { + let node = this.root; + for (let idx = i; idx <= j; idx++) { + if (!node.children[s[idx]]) { + return false; + } + node = node.children[s[idx]]; + } + return node.isWord; + } +} + +class Solution { + /** + * @param {string} s + * @param {string[]} wordDict + * @return {boolean} + */ + wordBreak(s, wordDict) { + const trie = new Trie(); + for (let word of wordDict) { + trie.insert(word); + } + + const dp = new Array(s.length + 1).fill(false); + dp[s.length] = true; + + let maxLen = 0; + for (let w of wordDict) { + maxLen = Math.max(maxLen, w.length); + } + + for (let i = s.length - 1; i >= 0; i--) { + for (let j = i; j < Math.min(s.length, i + maxLen); j++) { + if (trie.search(s, i, j)) { + dp[i] = dp[j + 1]; + if (dp[i]) break; + } + } + } + + return dp[0]; + } +} +``` + +```csharp +public class TrieNode { + public Dictionary Children; + public bool IsWord; + + public TrieNode() { + Children = new Dictionary(); + IsWord = false; + } +} + +public class Trie { + public TrieNode Root; + + public Trie() { + Root = new TrieNode(); + } + + public void Insert(string word) { + TrieNode node = Root; + foreach (char c in word) { + if (!node.Children.ContainsKey(c)) { + node.Children[c] = new TrieNode(); + } + node = node.Children[c]; + } + node.IsWord = true; + } + + public bool Search(string s, int i, int j) { + TrieNode node = Root; + for (int idx = i; idx <= j; idx++) { + if (!node.Children.ContainsKey(s[idx])) { + return false; + } + node = node.Children[s[idx]]; + } + return node.IsWord; + } +} + +public class Solution { + public bool WordBreak(string s, IList wordDict) { + Trie trie = new Trie(); + foreach (string word in wordDict) { + trie.Insert(word); + } + + int n = s.Length; + bool[] dp = new bool[n + 1]; + dp[n] = true; + + int maxLen = 0; + foreach (string word in wordDict) { + maxLen = Math.Max(maxLen, word.Length); + } + + for (int i = n - 1; i >= 0; i--) { + for (int j = i; j < Math.Min(n, i + maxLen); j++) { + if (trie.Search(s, i, j)) { + dp[i] = dp[j + 1]; + if (dp[i]) break; + } + } + } + + return dp[0]; + } +} +``` + +```go +type TrieNode struct { + children map[rune]*TrieNode + isWord bool +} + +func NewTrieNode() *TrieNode { + return &TrieNode{children: make(map[rune]*TrieNode)} +} + +type Trie struct { + root *TrieNode +} + +func NewTrie() *Trie { + return &Trie{root: NewTrieNode()} +} + +func (t *Trie) Insert(word string) { + node := t.root + for _, char := range word { + if _, found := node.children[char]; !found { + node.children[char] = NewTrieNode() + } + node = node.children[char] + } + node.isWord = true +} + +func (t *Trie) Search(s string, i, j int) bool { + node := t.root + for idx := i; idx <= j; idx++ { + char := rune(s[idx]) + if _, found := node.children[char]; !found { + return false + } + node = node.children[char] + } + return node.isWord +} + +func wordBreak(s string, wordDict []string) bool { + trie := NewTrie() + for _, word := range wordDict { + trie.Insert(word) + } + + dp := make([]bool, len(s)+1) + dp[len(s)] = true + + maxLength := 0 + for _, word := range wordDict { + if len(word) > maxLength { + maxLength = len(word) + } + } + + for i := len(s) - 1; i >= 0; i-- { + for j := i; j < len(s) && j < i+maxLength; j++ { + if trie.Search(s, i, j) { + dp[i] = dp[j+1] + if dp[i] { + break + } + } + } + } + + return dp[0] +} +``` + +```kotlin +class TrieNode { + val children = mutableMapOf() + var isWord = false +} + +class Trie { + private val root = TrieNode() + + fun insert(word: String) { + var node = root + for (char in word) { + node = node.children.computeIfAbsent(char) { TrieNode() } + } + node.isWord = true + } + + fun search(s: String, i: Int, j: Int): Boolean { + var node = root + for (idx in i..j) { + val char = s[idx] + node = node.children[char] ?: return false + } + return node.isWord + } +} + +class Solution { + fun wordBreak(s: String, wordDict: List): Boolean { + val trie = Trie() + wordDict.forEach { trie.insert(it) } + + val dp = BooleanArray(s.length + 1) + dp[s.length] = true + + val maxLength = wordDict.maxOfOrNull { it.length } ?: 0 + + for (i in s.length - 1 downTo 0) { + for (j in i until minOf(s.length, i + maxLength)) { + if (trie.search(s, i, j)) { + dp[i] = dp[j + 1] + if (dp[i]) break + } + } + } + + return dp[0] + } +} +``` + +```swift +class TrieNode { + var children = [Character: TrieNode]() + var isWord = false +} + +class Trie { + private let root = TrieNode() + + func insert(_ word: String) { + var node = root + for char in word { + if node.children[char] == nil { + node.children[char] = TrieNode() + } + node = node.children[char]! + } + node.isWord = true + } + + func search(_ s: [Character], _ i: Int, _ j: Int) -> Bool { + var node = root + for idx in i...j { + if node.children[s[idx]] == nil { + return false + } + node = node.children[s[idx]]! + } + return node.isWord + } +} + +class Solution { + func wordBreak(_ s: String, _ wordDict: [String]) -> Bool { + let trie = Trie() + for word in wordDict { + trie.insert(word) + } + + var dp = Array(repeating: false, count: s.count + 1) + dp[s.count] = true + let chars = Array(s) + + var maxLen = 0 + for word in wordDict { + maxLen = max(maxLen, word.count) + } + + for i in stride(from: s.count, through: 0, by: -1) { + for j in i.. Where $n$ is the length of the string $s$, $m$ is the number of words in $wordDict$ and $t$ is the maximum length of any word in $wordDict$. \ No newline at end of file diff --git a/articles/word-ladder.md b/articles/word-ladder.md new file mode 100644 index 000000000..ea29ccf35 --- /dev/null +++ b/articles/word-ladder.md @@ -0,0 +1,1723 @@ +## 1. Breadth First Search - I + +::tabs-start + +```python +class Solution: + def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int: + if (endWord not in wordList) or (beginWord == endWord): + return 0 + + n, m = len(wordList), len(wordList[0]) + adj = [[] for _ in range(n)] + mp = {} + for i in range(n): + mp[wordList[i]] = i + + for i in range(n): + for j in range(i + 1, n): + cnt = 0 + for k in range(m): + if wordList[i][k] != wordList[j][k]: + cnt += 1 + if cnt == 1: + adj[i].append(j) + adj[j].append(i) + + q, res = deque(), 1 + visit = set() + for i in range(m): + for c in range(97, 123): + if chr(c) == beginWord[i]: + continue + word = beginWord[:i] + chr(c) + beginWord[i + 1:] + if word in mp and mp[word] not in visit: + q.append(mp[word]) + visit.add(mp[word]) + + while q: + res += 1 + for i in range(len(q)): + node = q.popleft() + if wordList[node] == endWord: + return res + for nei in adj[node]: + if nei not in visit: + visit.add(nei) + q.append(nei) + + return 0 +``` + +```java +public class Solution { + public int ladderLength(String beginWord, String endWord, List wordList) { + if (!wordList.contains(endWord) || beginWord.equals(endWord)) { + return 0; + } + + int n = wordList.size(); + int m = wordList.get(0).length(); + List> adj = new ArrayList<>(n); + for (int i = 0; i < n; i++) { + adj.add(new ArrayList<>()); + } + + Map mp = new HashMap<>(); + for (int i = 0; i < n; i++) { + mp.put(wordList.get(i), i); + } + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + int cnt = 0; + for (int k = 0; k < m; k++) { + if (wordList.get(i).charAt(k) != wordList.get(j).charAt(k)) { + cnt++; + } + } + if (cnt == 1) { + adj.get(i).add(j); + adj.get(j).add(i); + } + } + } + + Queue q = new LinkedList<>(); + int res = 1; + Set visit = new HashSet<>(); + + for (int i = 0; i < m; i++) { + for (char c = 'a'; c <= 'z'; c++) { + if (c == beginWord.charAt(i)) { + continue; + } + String word = beginWord.substring(0, i) + c + beginWord.substring(i + 1); + if (mp.containsKey(word) && !visit.contains(mp.get(word))) { + q.add(mp.get(word)); + visit.add(mp.get(word)); + } + } + } + + while (!q.isEmpty()) { + res++; + int size = q.size(); + for (int i = 0; i < size; i++) { + int node = q.poll(); + if (wordList.get(node).equals(endWord)) { + return res; + } + for (int nei : adj.get(node)) { + if (!visit.contains(nei)) { + visit.add(nei); + q.add(nei); + } + } + } + } + + return 0; + } +} +``` + +```cpp +class Solution { +public: + int ladderLength(string beginWord, string endWord, vector& wordList) { + if (find(wordList.begin(), wordList.end(), endWord) == wordList.end() || + beginWord == endWord) { + return 0; + } + + int n = wordList.size(); + int m = wordList[0].size(); + vector> adj(n); + unordered_map mp; + for (int i = 0; i < n; i++) { + mp[wordList[i]] = i; + } + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + int cnt = 0; + for (int k = 0; k < m; k++) { + if (wordList[i][k] != wordList[j][k]) { + cnt++; + } + } + if (cnt == 1) { + adj[i].push_back(j); + adj[j].push_back(i); + } + } + } + + queue q; + int res = 1; + unordered_set visit; + + for (int i = 0; i < m; i++) { + for (char c = 'a'; c <= 'z'; c++) { + if (c == beginWord[i]) { + continue; + } + string word = beginWord.substr(0, i) + c + beginWord.substr(i + 1); + if (mp.find(word) != mp.end() && visit.find(mp[word]) == visit.end()) { + q.push(mp[word]); + visit.insert(mp[word]); + } + } + } + + while (!q.empty()) { + res++; + int size = q.size(); + for (int i = 0; i < size; i++) { + int node = q.front(); + q.pop(); + if (wordList[node] == endWord) { + return res; + } + for (int nei : adj[node]) { + if (visit.find(nei) == visit.end()) { + visit.insert(nei); + q.push(nei); + } + } + } + } + + return 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} beginWord + * @param {string} endWord + * @param {string[]} wordList + * @return {number} + */ + ladderLength(beginWord, endWord, wordList) { + if (!wordList.includes(endWord) || + beginWord === endWord) { + return 0; + } + + const n = wordList.length; + const m = wordList[0].length; + const adj = Array.from({ length: n }, () => []); + const mp = new Map(); + + for (let i = 0; i < n; i++) { + mp.set(wordList[i], i); + } + + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + let cnt = 0; + for (let k = 0; k < m; k++) { + if (wordList[i][k] !== wordList[j][k]) { + cnt++; + } + } + if (cnt === 1) { + adj[i].push(j); + adj[j].push(i); + } + } + } + + const q = new Queue(); + let res = 1; + const visit = new Set(); + + for (let i = 0; i < m; i++) { + for (let c = 97; c < 123; c++) { + if (String.fromCharCode(c) === beginWord[i]) { + continue; + } + const word = beginWord.slice(0, i) + + String.fromCharCode(c) + beginWord.slice(i + 1); + if (mp.has(word) && !visit.has(mp.get(word))) { + q.push(mp.get(word)); + visit.add(mp.get(word)); + } + } + } + + while (!q.isEmpty()) { + res++; + let size = q.size(); + for (let i = 0; i < size; i++) { + let node = q.pop(); + if (wordList[node] === endWord) { + return res; + } + for (let nei of adj[node]) { + if (!visit.has(nei)) { + visit.add(nei); + q.push(nei); + } + } + } + } + + return 0; + } +} +``` + +```csharp +public class Solution { + public int LadderLength(string beginWord, string endWord, IList wordList) { + if (!wordList.Contains(endWord) || beginWord == endWord) { + return 0; + } + + int n = wordList.Count; + int m = wordList[0].Length; + List> adj = new List>(n); + for (int i = 0; i < n; i++) { + adj.Add(new List()); + } + + Dictionary mp = new Dictionary(); + for (int i = 0; i < n; i++) { + mp[wordList[i]] = i; + } + + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + int cnt = 0; + for (int k = 0; k < m; k++) { + if (wordList[i][k] != wordList[j][k]) { + cnt++; + } + } + if (cnt == 1) { + adj[i].Add(j); + adj[j].Add(i); + } + } + } + + Queue q = new Queue(); + int res = 1; + HashSet visit = new HashSet(); + + for (int i = 0; i < m; i++) { + for (char c = 'a'; c <= 'z'; c++) { + if (c == beginWord[i]) { + continue; + } + string word = beginWord.Substring(0, i) + c + + beginWord.Substring(i + 1); + if (mp.ContainsKey(word) && !visit.Contains(mp[word])) { + q.Enqueue(mp[word]); + visit.Add(mp[word]); + } + } + } + + while (q.Count > 0) { + res++; + int size = q.Count; + for (int i = 0; i < size; i++) { + int node = q.Dequeue(); + if (wordList[node] == endWord) { + return res; + } + foreach (int nei in adj[node]) { + if (!visit.Contains(nei)) { + visit.Add(nei); + q.Enqueue(nei); + } + } + } + } + + return 0; + } +} +``` + +```go +func ladderLength(beginWord string, endWord string, wordList []string) int { + if !contains(wordList, endWord) || beginWord == endWord { + return 0 + } + + n, m := len(wordList), len(wordList[0]) + adj := make([][]int, n) + mp := make(map[string]int) + + for i := 0; i < n; i++ { + mp[wordList[i]] = i + } + + for i := 0; i < n; i++ { + for j := i + 1; j < n; j++ { + cnt := 0 + for k := 0; k < m; k++ { + if wordList[i][k] != wordList[j][k] { + cnt++ + } + } + if cnt == 1 { + adj[i] = append(adj[i], j) + adj[j] = append(adj[j], i) + } + } + } + + q := []int{} + res := 1 + visit := make(map[int]bool) + + for i := 0; i < m; i++ { + for c := 'a'; c <= 'z'; c++ { + if rune(beginWord[i]) == c { + continue + } + word := beginWord[:i] + string(c) + beginWord[i+1:] + if idx, exists := mp[word]; exists && !visit[idx] { + q = append(q, idx) + visit[idx] = true + } + } + } + + for len(q) > 0 { + res++ + size := len(q) + for i := 0; i < size; i++ { + node := q[0] + q = q[1:] + if wordList[node] == endWord { + return res + } + for _, nei := range adj[node] { + if !visit[nei] { + visit[nei] = true + q = append(q, nei) + } + } + } + } + return 0 +} + +func contains(wordList []string, word string) bool { + for _, w := range wordList { + if w == word { + return true + } + } + return false +} +``` + +```kotlin +class Solution { + fun ladderLength(beginWord: String, endWord: String, wordList: List): Int { + if (!wordList.contains(endWord) || beginWord == endWord) { + return 0 + } + + val n = wordList.size + val m = wordList[0].length + val adj = Array(n) { mutableListOf() } + val mp = HashMap() + + for (i in 0 until n) { + mp[wordList[i]] = i + } + + for (i in 0 until n) { + for (j in i + 1 until n) { + var cnt = 0 + for (k in 0 until m) { + if (wordList[i][k] != wordList[j][k]) { + cnt++ + } + } + if (cnt == 1) { + adj[i].add(j) + adj[j].add(i) + } + } + } + + val q = ArrayDeque() + var res = 1 + val visit = HashSet() + + for (i in 0 until m) { + for (c in 'a'..'z') { + if (beginWord[i] == c) { + continue + } + val word = beginWord.substring(0, i) + c + beginWord.substring(i + 1) + mp[word]?.let { idx -> + if (!visit.contains(idx)) { + q.add(idx) + visit.add(idx) + } + } + } + } + + while (q.isNotEmpty()) { + res++ + repeat(q.size) { + val node = q.removeFirst() + if (wordList[node] == endWord) { + return res + } + for (nei in adj[node]) { + if (!visit.contains(nei)) { + visit.add(nei) + q.add(nei) + } + } + } + } + return 0 + } +} +``` + +```swift +class Solution { + func ladderLength(_ beginWord: String, _ endWord: String, _ wordList: [String]) -> Int { + if !wordList.contains(endWord) || beginWord == endWord { + return 0 + } + + let n = wordList.count + let m = wordList[0].count + var adj = [[Int]](repeating: [], count: n) + var mp = [String: Int]() + + for i in 0..() + var res = 1 + var visit = Set() + let beginChars = Array(beginWord) + + for i in 0.. Where $n$ is the number of words and $m$ is the length of the word. + +--- + +## 2. Breadth First Search - II + +::tabs-start + +```python +class Solution: + def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int: + if (endWord not in wordList) or (beginWord == endWord): + return 0 + words, res = set(wordList), 0 + q = deque([beginWord]) + while q: + res += 1 + for _ in range(len(q)): + node = q.popleft() + if node == endWord: + return res + for i in range(len(node)): + for c in range(97, 123): + if chr(c) == node[i]: + continue + nei = node[:i] + chr(c) + node[i + 1:] + if nei in words: + q.append(nei) + words.remove(nei) + return 0 +``` + +```java +public class Solution { + public int ladderLength(String beginWord, String endWord, List wordList) { + if (!wordList.contains(endWord) || beginWord.equals(endWord)) return 0; + Set words = new HashSet<>(wordList); + int res = 0; + Queue q = new LinkedList<>(); + q.offer(beginWord); + + while (!q.isEmpty()) { + res++; + for (int i = q.size(); i > 0; i--) { + String node = q.poll(); + if (node.equals(endWord)) return res; + for (int j = 0; j < node.length(); j++) { + for (char c = 'a'; c <= 'z'; c++) { + if (c == node.charAt(j)) continue; + String nei = node.substring(0, j) + c + node.substring(j + 1); + if (words.contains(nei)) { + q.offer(nei); + words.remove(nei); + } + } + } + } + } + return 0; + } +} +``` + +```cpp +class Solution { +public: + int ladderLength(string beginWord, string endWord, vector& wordList) { + unordered_set words(wordList.begin(), wordList.end()); + if (words.find(endWord) == words.end() || beginWord == endWord) return 0; + int res = 0; + queue q; + q.push(beginWord); + + while (!q.empty()) { + res++; + int len = q.size(); + for (int i = 0; i < len; i++) { + string node = q.front(); + q.pop(); + if (node == endWord) return res; + for (int j = 0; j < node.length(); j++) { + char original = node[j]; + for (char c = 'a'; c <= 'z'; c++) { + if (c == original) continue; + node[j] = c; + if (words.find(node) != words.end()) { + q.push(node); + words.erase(node); + } + } + node[j] = original; + } + } + } + return 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} beginWord + * @param {string} endWord + * @param {string[]} wordList + * @return {number} + */ + ladderLength(beginWord, endWord, wordList) { + const words = new Set(wordList); + if (!words.has(endWord) || beginWord === endWord) { + return 0; + } + let res = 0; + const q = new Queue([beginWord]); + + while (!q.isEmpty()) { + res++; + let len = q.size(); + for (let i = 0; i < len; i++) { + const node = q.pop(); + if (node === endWord) return res; + for (let j = 0; j < node.length; j++) { + for (let c = 97; c < 123; c++) { + if (String.fromCharCode(c) === node[j]) { + continue; + } + const nei = node.slice(0, j) + + String.fromCharCode(c) + + node.slice(j + 1); + if (words.has(nei)) { + q.push(nei); + words.delete(nei); + } + } + } + } + } + return 0; + } +} +``` + +```csharp +public class Solution { + public int LadderLength(string beginWord, string endWord, IList wordList) { + var words = new HashSet(wordList); + if (!words.Contains(endWord) || beginWord == endWord) return 0; + int res = 0; + var q = new Queue(); + q.Enqueue(beginWord); + + while (q.Count > 0) { + res++; + int len = q.Count; + for (int i = 0; i < len; i++) { + string node = q.Dequeue(); + if (node == endWord) return res; + char[] arr = node.ToCharArray(); + for (int j = 0; j < arr.Length; j++) { + char original = arr[j]; + for (char c = 'a'; c <= 'z'; c++) { + if (c == original) continue; + arr[j] = c; + string nei = new string(arr); + if (words.Contains(nei)) { + q.Enqueue(nei); + words.Remove(nei); + } + } + arr[j] = original; + } + } + } + return 0; + } +} +``` + +```go +func ladderLength(beginWord string, endWord string, wordList []string) int { + if !contains(wordList, endWord) || beginWord == endWord { + return 0 + } + + words := make(map[string]bool) + for _, word := range wordList { + words[word] = true + } + + res := 0 + q := []string{beginWord} + + for len(q) > 0 { + res++ + size := len(q) + for i := 0; i < size; i++ { + node := q[0] + q = q[1:] + + if node == endWord { + return res + } + + for i := 0; i < len(node); i++ { + for c := 'a'; c <= 'z'; c++ { + if rune(node[i]) == c { + continue + } + nei := node[:i] + string(c) + node[i+1:] + if words[nei] { + q = append(q, nei) + delete(words, nei) + } + } + } + } + } + return 0 +} + +func contains(wordList []string, word string) bool { + for _, w := range wordList { + if w == word { + return true + } + } + return false +} +``` + +```kotlin +class Solution { + fun ladderLength(beginWord: String, endWord: String, wordList: List): Int { + if (!wordList.contains(endWord) || beginWord == endWord) { + return 0 + } + + val words = wordList.toMutableSet() + var res = 0 + val q = ArrayDeque().apply { add(beginWord) } + + while (q.isNotEmpty()) { + res++ + repeat(q.size) { + val node = q.removeFirst() + + if (node == endWord) { + return res + } + + for (i in node.indices) { + for (c in 'a'..'z') { + if (node[i] == c) { + continue + } + val nei = node.substring(0, i) + c + node.substring(i + 1) + if (words.remove(nei)) { + q.add(nei) + } + } + } + } + } + return 0 + } +} +``` + +```swift +class Solution { + func ladderLength(_ beginWord: String, _ endWord: String, _ wordList: [String]) -> Int { + if !wordList.contains(endWord) || beginWord == endWord { + return 0 + } + + var words = Set(wordList) + var queue = Deque() + queue.append(beginWord) + var res = 0 + + while !queue.isEmpty { + res += 1 + for _ in 0.. Where $n$ is the number of words and $m$ is the length of the word. + +--- + +## 3. Breadth First Search - III + +::tabs-start + +```python +class Solution: + def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int: + if endWord not in wordList: + return 0 + + nei = collections.defaultdict(list) + wordList.append(beginWord) + for word in wordList: + for j in range(len(word)): + pattern = word[:j] + "*" + word[j + 1 :] + nei[pattern].append(word) + + visit = set([beginWord]) + q = deque([beginWord]) + res = 1 + while q: + for i in range(len(q)): + word = q.popleft() + if word == endWord: + return res + for j in range(len(word)): + pattern = word[:j] + "*" + word[j + 1 :] + for neiWord in nei[pattern]: + if neiWord not in visit: + visit.add(neiWord) + q.append(neiWord) + res += 1 + return 0 +``` + +```java +public class Solution { + public int ladderLength(String beginWord, String endWord, List wordList) { + if (!wordList.contains(endWord)) { + return 0; + } + + Map> nei = new HashMap<>(); + wordList.add(beginWord); + for (String word : wordList) { + for (int j = 0; j < word.length(); j++) { + String pattern = word.substring(0, j) + "*" + word.substring(j + 1); + nei.computeIfAbsent(pattern, k -> new ArrayList<>()).add(word); + } + } + + Set visit = new HashSet<>(); + Queue q = new LinkedList<>(); + q.offer(beginWord); + int res = 1; + while (!q.isEmpty()) { + int size = q.size(); + for (int i = 0; i < size; i++) { + String word = q.poll(); + if (word.equals(endWord)) { + return res; + } + for (int j = 0; j < word.length(); j++) { + String pattern = word.substring(0, j) + "*" + word.substring(j + 1); + for (String neiWord : nei.getOrDefault(pattern, Collections.emptyList())) { + if (!visit.contains(neiWord)) { + visit.add(neiWord); + q.offer(neiWord); + } + } + } + } + res++; + } + return 0; + } +} +``` + +```cpp +class Solution { +public: + int ladderLength(string beginWord, string endWord, vector& wordList) { + if (endWord.empty() || find(wordList.begin(), wordList.end(), endWord) == wordList.end()) { + return 0; + } + + unordered_map> nei; + wordList.push_back(beginWord); + for (const string& word : wordList) { + for (int j = 0; j < word.size(); ++j) { + string pattern = word.substr(0, j) + "*" + word.substr(j + 1); + nei[pattern].push_back(word); + } + } + + unordered_set visit{beginWord}; + queue q; + q.push(beginWord); + int res = 1; + while (!q.empty()) { + int size = q.size(); + for (int i = 0; i < size; ++i) { + string word = q.front(); + q.pop(); + if (word == endWord) { + return res; + } + for (int j = 0; j < word.size(); ++j) { + string pattern = word.substr(0, j) + "*" + word.substr(j + 1); + for (const string& neiWord : nei[pattern]) { + if (visit.find(neiWord) == visit.end()) { + visit.insert(neiWord); + q.push(neiWord); + } + } + } + } + ++res; + } + return 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} beginWord + * @param {string} endWord + * @param {string[]} wordList + * @return {number} + */ + ladderLength(beginWord, endWord, wordList) { + if (!wordList.includes(endWord)) { + return 0; + } + + const nei = {}; + wordList.push(beginWord); + for (const word of wordList) { + for (let j = 0; j < word.length; ++j) { + const pattern = word.substring(0, j) + + '*' + word.substring(j + 1); + if (!nei[pattern]) { + nei[pattern] = []; + } + nei[pattern].push(word); + } + } + + const visit = new Set([beginWord]); + const q =new Queue([beginWord]); + let res = 1; + while (!q.isEmpty()) { + const size = q.size(); + for (let i = 0; i < size; ++i) { + const word = q.pop(); + if (word === endWord) { + return res; + } + for (let j = 0; j < word.length; ++j) { + const pattern = word.substring(0, j) + + '*' + word.substring(j + 1); + for (const neiWord of nei[pattern]) { + if (!visit.has(neiWord)) { + visit.add(neiWord); + q.push(neiWord); + } + } + } + } + ++res; + } + return 0; + } +} +``` + +```csharp +public class Solution { + public int LadderLength(string beginWord, string endWord, IList wordList) { + if (!wordList.Contains(endWord)) { + return 0; + } + + Dictionary> nei = new Dictionary>(); + wordList.Add(beginWord); + foreach (string word in wordList) { + for (int j = 0; j < word.Length; j++) { + string pattern = word.Substring(0, j) + + "*" + word.Substring(j + 1); + if (!nei.ContainsKey(pattern)) { + nei[pattern] = new List(); + } + nei[pattern].Add(word); + } + } + + HashSet visit = new HashSet(); + Queue q = new Queue(); + q.Enqueue(beginWord); + int res = 1; + while (q.Count > 0) { + int size = q.Count; + for (int i = 0; i < size; i++) { + string word = q.Dequeue(); + if (word == endWord) { + return res; + } + for (int j = 0; j < word.Length; j++) { + string pattern = word.Substring(0, j) + + "*" + word.Substring(j + 1); + if (nei.ContainsKey(pattern)) { + foreach (string neiWord in nei[pattern]) { + if (!visit.Contains(neiWord)) { + visit.Add(neiWord); + q.Enqueue(neiWord); + } + } + } + } + } + res++; + } + return 0; + } +} +``` + +```go +func ladderLength(beginWord string, endWord string, wordList []string) int { + if !contains(wordList, endWord) { + return 0 + } + + nei := make(map[string][]string) + wordList = append(wordList, beginWord) + + for _, word := range wordList { + for j := 0; j < len(word); j++ { + pattern := word[:j] + "*" + word[j+1:] + nei[pattern] = append(nei[pattern], word) + } + } + + visit := make(map[string]bool) + visit[beginWord] = true + q := []string{beginWord} + res := 1 + + for len(q) > 0 { + for i := len(q); i > 0; i-- { + word := q[0] + q = q[1:] + + if word == endWord { + return res + } + + for j := 0; j < len(word); j++ { + pattern := word[:j] + "*" + word[j+1:] + for _, neiWord := range nei[pattern] { + if !visit[neiWord] { + visit[neiWord] = true + q = append(q, neiWord) + } + } + } + } + res++ + } + return 0 +} + +func contains(wordList []string, word string) bool { + for _, w := range wordList { + if w == word { + return true + } + } + return false +} +``` + +```kotlin +class Solution { + fun ladderLength(beginWord: String, endWord: String, wordList: List): Int { + if (!wordList.contains(endWord)) { + return 0 + } + + val nei = HashMap>().withDefault { mutableListOf() } + val allWords = wordList.toMutableList().apply { add(beginWord) } + + for (word in allWords) { + for (j in word.indices) { + val pattern = word.substring(0, j) + "*" + word.substring(j + 1) + nei[pattern] = nei.getValue(pattern).apply { add(word) } + } + } + + val visit = hashSetOf(beginWord) + val q = ArrayDeque().apply { add(beginWord) } + var res = 1 + + while (q.isNotEmpty()) { + repeat(q.size) { + val word = q.removeFirst() + + if (word == endWord) { + return res + } + + for (j in word.indices) { + val pattern = word.substring(0, j) + "*" + word.substring(j + 1) + for (neiWord in nei.getValue(pattern)) { + if (visit.add(neiWord)) { + q.add(neiWord) + } + } + } + } + res++ + } + return 0 + } +} +``` + +```swift +class Solution { + func ladderLength(_ beginWord: String, _ endWord: String, _ wordList: [String]) -> Int { + if !wordList.contains(endWord) { + return 0 + } + + var nei = [String: [String]]() + var wordList = wordList + wordList.append(beginWord) + + for word in wordList { + let wordArray = Array(word) + for j in 0.. = [beginWord] + var queue = Deque() + queue.append(beginWord) + var res = 1 + + while !queue.isEmpty { + for _ in 0.. Where $n$ is the number of words and $m$ is the length of the word. + +--- + +## 4. Meet In The Middle (BFS) + +::tabs-start + +```python +class Solution: + def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int: + if endWord not in wordList or beginWord == endWord: + return 0 + m = len(wordList[0]) + wordSet = set(wordList) + qb, qe = deque([beginWord]), deque([endWord]) + fromBegin, fromEnd = {beginWord: 1}, {endWord: 1} + + while qb and qe: + if len(qb) > len(qe): + qb, qe = qe, qb + fromBegin, fromEnd = fromEnd, fromBegin + for _ in range(len(qb)): + word = qb.popleft() + steps = fromBegin[word] + for i in range(m): + for c in range(97, 123): + if chr(c) == word[i]: + continue + nei = word[:i] + chr(c) + word[i + 1:] + if nei not in wordSet: + continue + if nei in fromEnd: + return steps + fromEnd[nei] + if nei not in fromBegin: + fromBegin[nei] = steps + 1 + qb.append(nei) + return 0 +``` + +```java +public class Solution { + public int ladderLength(String beginWord, String endWord, List wordList) { + if (!wordList.contains(endWord) || beginWord.equals(endWord)) + return 0; + int m = wordList.get(0).length(); + Set wordSet = new HashSet<>(wordList); + Queue qb = new LinkedList<>(), qe = new LinkedList<>(); + Map fromBegin = new HashMap<>(); + Map fromEnd = new HashMap<>(); + qb.add(beginWord); + qe.add(endWord); + fromBegin.put(beginWord, 1); + fromEnd.put(endWord, 1); + + while (!qb.isEmpty() && !qe.isEmpty()) { + if (qb.size() > qe.size()) { + Queue tempQ = qb; + qb = qe; + qe = tempQ; + Map tempMap = fromBegin; + fromBegin = fromEnd; + fromEnd = tempMap; + } + int size = qb.size(); + for (int k = 0; k < size; k++) { + String word = qb.poll(); + int steps = fromBegin.get(word); + for (int i = 0; i < m; i++) { + for (char c = 'a'; c <= 'z'; c++) { + if (c == word.charAt(i)) + continue; + String nei = word.substring(0, i) + + c + word.substring(i + 1); + if (!wordSet.contains(nei)) + continue; + if (fromEnd.containsKey(nei)) + return steps + fromEnd.get(nei); + if (!fromBegin.containsKey(nei)) { + fromBegin.put(nei, steps + 1); + qb.add(nei); + } + } + } + } + } + return 0; + } +} +``` + +```cpp +class Solution { +public: + int ladderLength(string beginWord, string endWord, vector& wordList) { + if (find(wordList.begin(), wordList.end(), endWord) == wordList.end() || + beginWord == endWord) + return 0; + int m = wordList[0].size(); + unordered_set wordSet(wordList.begin(), wordList.end()); + queue qb, qe; + unordered_map fromBegin, fromEnd; + qb.push(beginWord); + qe.push(endWord); + fromBegin[beginWord] = 1; + fromEnd[endWord] = 1; + + while (!qb.empty() && !qe.empty()) { + if (qb.size() > qe.size()) { + swap(qb, qe); + swap(fromBegin, fromEnd); + } + int size = qb.size(); + for (int k = 0; k < size; k++) { + string word = qb.front(); + qb.pop(); + int steps = fromBegin[word]; + for (int i = 0; i < m; i++) { + for (char c = 'a'; c <= 'z'; c++) { + if (c == word[i]) + continue; + string nei = word.substr(0, i) + + c + word.substr(i + 1); + if (!wordSet.count(nei)) + continue; + if (fromEnd.count(nei)) + return steps + fromEnd[nei]; + if (!fromBegin.count(nei)) { + fromBegin[nei] = steps + 1; + qb.push(nei); + } + } + } + } + } + return 0; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} beginWord + * @param {string} endWord + * @param {string[]} wordList + * @return {number} + */ + ladderLength(beginWord, endWord, wordList) { + if (!wordList.includes(endWord) || + beginWord === endWord) { + return 0; + } + const m = wordList[0].length; + const wordSet = new Set(wordList); + let qb = new Queue([beginWord]); + let qe = new Queue([endWord]); + let fromBegin = { [beginWord]: 1 }; + let fromEnd = { [endWord]: 1 }; + + while (!qb.isEmpty() && !qe.isEmpty()) { + if (qb.size() > qe.size()) { + [qb, qe] = [qe, qb]; + [fromBegin, fromEnd] = [fromEnd, fromBegin]; + } + const size = qb.size(); + for (let k = 0; k < size; k++) { + const word = qb.pop(); + const steps = fromBegin[word]; + for (let i = 0; i < m; i++) { + for (let c = 97; c <= 122; c++) { + if (String.fromCharCode(c) === word[i]) + continue; + const nei = word.slice(0, i) + + String.fromCharCode(c) + + word.slice(i + 1); + if (!wordSet.has(nei)) + continue; + if (fromEnd[nei] !== undefined) + return steps + fromEnd[nei]; + if (fromBegin[nei] === undefined) { + fromBegin[nei] = steps + 1; + qb.push(nei); + } + } + } + } + } + return 0; + } +} +``` + +```csharp +public class Solution { + public int LadderLength(string beginWord, string endWord, IList wordList) { + if (!wordList.Contains(endWord) || beginWord == endWord) + return 0; + int m = wordList[0].Length; + HashSet wordSet = new HashSet(wordList); + Queue qb = new Queue(), qe = new Queue(); + Dictionary fromBegin = new Dictionary(), + fromEnd = new Dictionary(); + qb.Enqueue(beginWord); + qe.Enqueue(endWord); + fromBegin[beginWord] = 1; + fromEnd[endWord] = 1; + + while (qb.Count > 0 && qe.Count > 0) { + if (qb.Count > qe.Count) { + var tempQ = qb; + qb = qe; + qe = tempQ; + var tempMap = fromBegin; + fromBegin = fromEnd; + fromEnd = tempMap; + } + int size = qb.Count; + for (int k = 0; k < size; k++) { + string word = qb.Dequeue(); + int steps = fromBegin[word]; + for (int i = 0; i < m; i++) { + for (char c = 'a'; c <= 'z'; c++) { + if (c == word[i]) + continue; + string nei = word.Substring(0, i) + + c + word.Substring(i + 1); + if (!wordSet.Contains(nei)) + continue; + if (fromEnd.ContainsKey(nei)) + return steps + fromEnd[nei]; + if (!fromBegin.ContainsKey(nei)) { + fromBegin[nei] = steps + 1; + qb.Enqueue(nei); + } + } + } + } + } + return 0; + } +} +``` + +```go +func ladderLength(beginWord string, endWord string, wordList []string) int { + if len(wordList) == 0 || len(beginWord) != len(wordList[0]) { + return 0 + } + + wordSet := make(map[string]bool) + for _, word := range wordList { + wordSet[word] = true + } + + if !wordSet[endWord] || beginWord == endWord { + return 0 + } + + m := len(beginWord) + qb := []string{beginWord} + qe := []string{endWord} + fromBegin := map[string]int{beginWord: 1} + fromEnd := map[string]int{endWord: 1} + + for len(qb) > 0 && len(qe) > 0 { + if len(qb) > len(qe) { + qb, qe = qe, qb + fromBegin, fromEnd = fromEnd, fromBegin + } + + size := len(qb) + for i := 0; i < size; i++ { + word := qb[0] + qb = qb[1:] + steps := fromBegin[word] + + wordBytes := []byte(word) + for j := 0; j < m; j++ { + orig := wordBytes[j] + for c := byte('a'); c <= byte('z'); c++ { + if c == orig { + continue + } + wordBytes[j] = c + nei := string(wordBytes) + + if !wordSet[nei] { + continue + } + if val, exists := fromEnd[nei]; exists { + return steps + val + } + if _, exists := fromBegin[nei]; !exists { + fromBegin[nei] = steps + 1 + qb = append(qb, nei) + } + } + wordBytes[j] = orig + } + } + } + return 0 +} +``` + +```kotlin +class Solution { + fun ladderLength(beginWord: String, endWord: String, wordList: List): Int { + if (!wordList.contains(endWord) || beginWord == endWord) { + return 0 + } + + val m = wordList[0].length + val wordSet = wordList.toSet() + val qb = ArrayDeque().apply { add(beginWord) } + val qe = ArrayDeque().apply { add(endWord) } + val fromBegin = hashMapOf(beginWord to 1) + val fromEnd = hashMapOf(endWord to 1) + + while (qb.isNotEmpty() && qe.isNotEmpty()) { + if (qb.size > qe.size) { + qb.swap(qe) + fromBegin.swap(fromEnd) + } + + repeat(qb.size) { + val word = qb.removeFirst() + val steps = fromBegin[word]!! + + for (i in 0 until m) { + for (c in 'a'..'z') { + if (c == word[i]) continue + val nei = word.substring(0, i) + c + word.substring(i + 1) + + if (!wordSet.contains(nei)) continue + fromEnd[nei]?.let { return steps + it } + if (nei !in fromBegin) { + fromBegin[nei] = steps + 1 + qb.add(nei) + } + } + } + } + } + return 0 + } + + private fun ArrayDeque.swap(other: ArrayDeque) { + val temp = ArrayDeque(this) + this.clear() + this.addAll(other) + other.clear() + other.addAll(temp) + } + + private fun HashMap.swap(other: HashMap) { + val temp = HashMap() + temp.putAll(this) + this.clear() + this.putAll(other) + other.clear() + other.putAll(temp) + } +} +``` + +```swift +class Solution { + func ladderLength(_ beginWord: String, _ endWord: String, _ wordList: [String]) -> Int { + if !wordList.contains(endWord) || beginWord == endWord { + return 0 + } + + let m = wordList[0].count + var wordSet = Set(wordList) + var qb = Deque([beginWord]) + var qe = Deque([endWord]) + var fromBegin = [beginWord: 1] + var fromEnd = [endWord: 1] + + while !qb.isEmpty && !qe.isEmpty { + if qb.count > qe.count { + swap(&qb, &qe) + swap(&fromBegin, &fromEnd) + } + + for _ in 0.. Where $n$ is the number of words and $m$ is the length of the word. \ No newline at end of file diff --git a/articles/word-pattern.md b/articles/word-pattern.md new file mode 100644 index 000000000..c09b50aa1 --- /dev/null +++ b/articles/word-pattern.md @@ -0,0 +1,533 @@ +## 1. Two Hash Maps + +::tabs-start + +```python +class Solution: + def wordPattern(self, pattern: str, s: str) -> bool: + words = s.split(" ") + if len(pattern) != len(words): + return False + + charToWord = {} + wordToChar = {} + + for c, w in zip(pattern, words): + if c in charToWord and charToWord[c] != w: + return False + if w in wordToChar and wordToChar[w] != c: + return False + charToWord[c] = w + wordToChar[w] = c + return True +``` + +```java +public class Solution { + public boolean wordPattern(String pattern, String s) { + String[] words = s.split(" "); + if (pattern.length() != words.length) { + return false; + } + + Map charToWord = new HashMap<>(); + Map wordToChar = new HashMap<>(); + + for (int i = 0; i < pattern.length(); i++) { + char c = pattern.charAt(i); + String w = words[i]; + + if (charToWord.containsKey(c) && !charToWord.get(c).equals(w)) { + return false; + } + if (wordToChar.containsKey(w) && wordToChar.get(w) != c) { + return false; + } + + charToWord.put(c, w); + wordToChar.put(w, c); + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool wordPattern(string pattern, string s) { + vector words; + string word; + stringstream ss(s); + while (ss >> word) { + words.push_back(word); + } + + if (pattern.length() != words.size()) { + return false; + } + + unordered_map charToWord; + unordered_map wordToChar; + + for (int i = 0; i < pattern.length(); i++) { + char c = pattern[i]; + string& w = words[i]; + + if (charToWord.count(c) && charToWord[c] != w) { + return false; + } + if (wordToChar.count(w) && wordToChar[w] != c) { + return false; + } + + charToWord[c] = w; + wordToChar[w] = c; + } + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} pattern + * @param {string} s + * @return {boolean} + */ + wordPattern(pattern, s) { + const words = s.split(" "); + if (pattern.length !== words.length) { + return false; + } + + const charToWord = new Map(); + const wordToChar = new Map(); + + for (let i = 0; i < pattern.length; i++) { + const c = pattern[i]; + const w = words[i]; + + if (charToWord.has(c) && charToWord.get(c) !== w) { + return false; + } + if (wordToChar.has(w) && wordToChar.get(w) !== c) { + return false; + } + + charToWord.set(c, w); + wordToChar.set(w, c); + } + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $pattern$ and $m$ is the length of the string $s$. + +--- + +## 2. Two Hash Maps (Optimal) + +::tabs-start + +```python +class Solution: + def wordPattern(self, pattern: str, s: str) -> bool: + charToWord = {} + wordToChar = {} + words = s.split() + + if len(pattern) != len(words): + return False + + for i, (c, word) in enumerate(zip(pattern, words)): + if charToWord.get(c, 0) != wordToChar.get(word, 0): + return False + charToWord[c] = i + 1 + wordToChar[word] = i + 1 + + return True +``` + +```java +public class Solution { + public boolean wordPattern(String pattern, String s) { + Map charToWord = new HashMap<>(); + Map wordToChar = new HashMap<>(); + String[] words = s.split(" "); + + if (words.length != pattern.length()) return false; + + for (int i = 0; i < pattern.length(); i++) { + if (charToWord.containsKey(pattern.charAt(i)) && + !words[charToWord.get(pattern.charAt(i))].equals(words[i])) { + return false; + } + + if (wordToChar.containsKey(words[i]) && + pattern.charAt(wordToChar.get(words[i])) != pattern.charAt(i)) { + return false; + } + + charToWord.put(pattern.charAt(i), i); + wordToChar.put(words[i], i); + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool wordPattern(string pattern, string s) { + unordered_map charToWord; + unordered_map wordToChar; + istringstream in(s); + int i = 0, n = pattern.size(); + for (string word; in >> word; ++i) { + if (i == n || charToWord[pattern[i]] != wordToChar[word]) { + return false; + } + charToWord[pattern[i]] = wordToChar[word] = i + 1; + } + return i == n; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} pattern + * @param {string} s + * @return {boolean} + */ + wordPattern(pattern, s) { + const charToWord = new Map(); + const wordToChar = new Map(); + const words = s.split(" "); + + if (pattern.length !== words.length) { + return false; + } + + for (let i = 0; i < words.length; i++) { + const c = pattern[i]; + const word = words[i]; + + if ((charToWord.get(c) || 0) !== (wordToChar.get(word) || 0)) { + return false; + } + + charToWord.set(c, i + 1); + wordToChar.set(word, i + 1); + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $pattern$ and $m$ is the length of the string $s$. + +--- + +## 3. Hash Set + +::tabs-start + +```python +class Solution: + def wordPattern(self, pattern: str, s: str) -> bool: + words = s.split() + if len(pattern) != len(words): + return False + + charToWord = {} + store = set() + + for i, (c, w) in enumerate(zip(pattern, words)): + if c in charToWord: + if words[charToWord[c]] != w: + return False + else: + if w in store: + return False + charToWord[c] = i + store.add(w) + + return True +``` + +```java +public class Solution { + public boolean wordPattern(String pattern, String s) { + String[] words = s.split(" "); + if (pattern.length() != words.length) return false; + + Map charToWord = new HashMap<>(); + Set store = new HashSet<>(); + + for (int i = 0; i < pattern.length(); i++) { + char c = pattern.charAt(i); + + if (charToWord.containsKey(c)) { + if (!words[charToWord.get(c)].equals(words[i])) { + return false; + } + } else { + if (store.contains(words[i])) { + return false; + } + charToWord.put(c, i); + store.add(words[i]); + } + } + + return true; + } +} +``` + +```cpp +class Solution { +public: + bool wordPattern(string pattern, string s) { + stringstream ss(s); + string word; + vector words; + + while (ss >> word) { + words.push_back(word); + } + + if (pattern.length() != words.size()) return false; + + unordered_map charToWord; + set store; + + for (int i = 0; i < pattern.length(); i++) { + char c = pattern[i]; + + if (charToWord.count(c)) { + if (words[charToWord[c]] != words[i]) { + return false; + } + } else { + if (store.count(words[i])) { + return false; + } + charToWord[c] = i; + store.insert(words[i]); + } + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} pattern + * @param {string} s + * @return {boolean} + */ + wordPattern(pattern, s) { + const charToWord = new Map(); + const wordToChar = new Map(); + const words = s.split(" "); + + if (pattern.length !== words.length) { + return false; + } + + for (let i = 0; i < words.length; i++) { + const c = pattern[i]; + const word = words[i]; + + if ((charToWord.get(c) || 0) !== (wordToChar.get(word) || 0)) { + return false; + } + + charToWord.set(c, i + 1); + wordToChar.set(word, i + 1); + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $pattern$ and $m$ is the length of the string $s$. + +--- + +## 4. Single Hash Map + +::tabs-start + +```python +class Solution: + def wordPattern(self, pattern: str, s: str) -> bool: + words = s.split() + if len(pattern) != len(words): + return False + + charToWord = {} + + for i, (c, w) in enumerate(zip(pattern, words)): + if c in charToWord: + if words[charToWord[c]] != w: + return False + else: + # iterates atmost 26 times (a - z) + for k in charToWord: + if words[charToWord[k]] == w: + return False + charToWord[c] = i + + return True +``` + +```java +public class Solution { + public boolean wordPattern(String pattern, String s) { + String[] words = s.split(" "); + if (pattern.length() != words.length) { + return false; + } + + Map charToWord = new HashMap<>(); + for (int i = 0; i < pattern.length(); i++) { + char c = pattern.charAt(i); + String w = words[i]; + + if (charToWord.containsKey(c)) { + if (!words[charToWord.get(c)].equals(w)) { + return false; + } + } else { + for (Map.Entry entry : charToWord.entrySet()) { + if (words[entry.getValue()].equals(w)) { + return false; + } + } + charToWord.put(c, i); + } + } + return true; + } +} +``` + +```cpp +class Solution { +public: + bool wordPattern(string pattern, string s) { + vector words; + string word; + stringstream ss(s); + while (ss >> word) { + words.push_back(word); + } + + if (pattern.size() != words.size()) { + return false; + } + + unordered_map charToWord; + for (int i = 0; i < pattern.size(); ++i) { + char c = pattern[i]; + const string& w = words[i]; + + if (charToWord.count(c)) { + if (words[charToWord[c]] != w) { + return false; + } + } else { + for (const auto& [key, val] : charToWord) { + if (words[val] == w) { + return false; + } + } + charToWord[c] = i; + } + } + + return true; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} pattern + * @param {string} s + * @return {boolean} + */ + wordPattern(pattern, s) { + const words = s.split(" "); + if (pattern.length !== words.length) { + return false; + } + + const charToWord = new Map(); + for (let i = 0; i < pattern.length; i++) { + const c = pattern[i]; + const w = words[i]; + + if (charToWord.has(c)) { + if (words[charToWord.get(c)] !== w) { + return false; + } + } else { + for (const [key, index] of charToWord.entries()) { + if (words[index] === w) { + return false; + } + } + charToWord.set(c, i); + } + } + + return true; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n + m)$ +* Space complexity: $O(m)$ + +> Where $n$ is the length of the string $pattern$ and $m$ is the length of the string $s$. \ No newline at end of file diff --git a/articles/word-subsets.md b/articles/word-subsets.md new file mode 100644 index 000000000..d96eab686 --- /dev/null +++ b/articles/word-subsets.md @@ -0,0 +1,293 @@ +## 1. Brute Force + +::tabs-start + +```python +class Solution: + def wordSubsets(self, words1: List[str], words2: List[str]) -> List[str]: + res = [] + for w1 in words1: + count1 = Counter(w1) + is_subset = True + + for w2 in words2: + count2 = Counter(w2) + for c in count2: + if count2[c] > count1[c]: + is_subset = False + break + + if not is_subset: break + + if is_subset: + res.append(w1) + + return res +``` + +```java +public class Solution { + public List wordSubsets(String[] words1, String[] words2) { + List res = new ArrayList<>(); + + for (String w1 : words1) { + int[] count1 = new int[26]; + for (char c : w1.toCharArray()) count1[c - 'a']++; + + boolean isSubset = true; + for (String w2 : words2) { + int[] count2 = new int[26]; + for (char c : w2.toCharArray()) count2[c - 'a']++; + + for (int i = 0; i < 26; i++) { + if (count2[i] > count1[i]) { + isSubset = false; + break; + } + } + + if (!isSubset) break; + } + + if (isSubset) res.add(w1); + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector wordSubsets(vector& words1, vector& words2) { + vector res; + + for (const string& w1 : words1) { + vector count1(26, 0); + for (char c : w1) count1[c - 'a']++; + + bool isSubset = true; + for (const string& w2 : words2) { + vector count2(26, 0); + for (char c : w2) count2[c - 'a']++; + + for (int i = 0; i < 26; i++) { + if (count2[i] > count1[i]) { + isSubset = false; + break; + } + } + + if (!isSubset) break; + } + + if (isSubset) res.push_back(w1); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words1 + * @param {string[]} words2 + * @return {string[]} + */ + wordSubsets(words1, words2) { + const res = []; + + for (const w1 of words1) { + const count1 = Array(26).fill(0); + for (const c of w1) count1[c.charCodeAt(0) - 97]++; + + let isSubset = true; + for (const w2 of words2) { + const count2 = Array(26).fill(0); + for (const c of w2) count2[c.charCodeAt(0) - 97]++; + + for (let i = 0; i < 26; i++) { + if (count2[i] > count1[i]) { + isSubset = false; + break; + } + } + + if (!isSubset) break; + } + + if (isSubset) res.push(w1); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N * n + N * M * m)$ +* Space complexity: + * $O(1)$ extra space, since we have at most $26$ different characters. + * $O(N * n)$ space for the output list. + +> Where $N$ is the size of the array $words1$, $n$ is the length of the longest word in $words1$, $M$ is the size of the array $words2$, and $m$ is the length of the longest word in $words2$. + +--- + +## 2. Greedy + Hash Map + +::tabs-start + +```python +class Solution: + def wordSubsets(self, words1: List[str], words2: List[str]) -> List[str]: + count_2 = defaultdict(int) + for w in words2: + count_w = Counter(w) + for c, cnt in count_w.items(): + count_2[c] = max(count_2[c], cnt) + + res = [] + for w in words1: + count_w = Counter(w) + flag = True + for c, cnt in count_2.items(): + if count_w[c] < cnt: + flag = False + break + if flag: + res.append(w) + + return res +``` + +```java +public class Solution { + public List wordSubsets(String[] words1, String[] words2) { + int[] count2 = new int[26]; + for (String w : words2) { + int[] countW = new int[26]; + for (char c : w.toCharArray()) { + countW[c - 'a']++; + } + for (int i = 0; i < 26; i++) { + count2[i] = Math.max(count2[i], countW[i]); + } + } + + List res = new ArrayList<>(); + for (String w : words1) { + int[] countW = new int[26]; + for (char c : w.toCharArray()) { + countW[c - 'a']++; + } + + boolean flag = true; + for (int i = 0; i < 26; i++) { + if (countW[i] < count2[i]) { + flag = false; + break; + } + } + + if (flag) { + res.add(w); + } + } + + return res; + } +} +``` + +```cpp +class Solution { +public: + vector wordSubsets(vector& words1, vector& words2) { + vector count2(26, 0); + for (string& w : words2) { + vector countW(26, 0); + for (char c : w) countW[c - 'a']++; + for (int i = 0; i < 26; ++i) + count2[i] = max(count2[i], countW[i]); + } + + vector res; + for (string& w : words1) { + vector countW(26, 0); + for (char c : w) countW[c - 'a']++; + + bool flag = true; + for (int i = 0; i < 26; ++i) { + if (countW[i] < count2[i]) { + flag = false; + break; + } + } + + if (flag) res.push_back(w); + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string[]} words1 + * @param {string[]} words2 + * @return {string[]} + */ + wordSubsets(words1, words2) { + const count2 = new Array(26).fill(0); + for (let w of words2) { + const countW = new Array(26).fill(0); + for (let c of w) { + countW[c.charCodeAt(0) - 97]++; + } + for (let i = 0; i < 26; i++) { + count2[i] = Math.max(count2[i], countW[i]); + } + } + + const res = []; + for (let w of words1) { + const countW = new Array(26).fill(0); + for (let c of w) { + countW[c.charCodeAt(0) - 97]++; + } + + let flag = true; + for (let i = 0; i < 26; i++) { + if (countW[i] < count2[i]) { + flag = false; + break; + } + } + + if (flag) res.push(w); + } + + return res; + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(N * n + M * m)$ +* Space complexity: + * $O(1)$ extra space, since we have at most $26$ different characters. + * $O(N * n)$ space for the output list. + +> Where $N$ is the size of the array $words1$, $n$ is the length of the longest word in $words1$, $M$ is the size of the array $words2$, and $m$ is the length of the longest word in $words2$. \ No newline at end of file diff --git a/articles/zigzag-conversion.md b/articles/zigzag-conversion.md new file mode 100644 index 000000000..5df4dafff --- /dev/null +++ b/articles/zigzag-conversion.md @@ -0,0 +1,226 @@ +## 1. Iteration - I + +::tabs-start + +```python +class Solution: + def convert(self, s: str, numRows: int) -> str: + if numRows == 1: + return s + + res = [] + for r in range(numRows): + increment = 2 * (numRows - 1) + for i in range(r, len(s), increment): + res.append(s[i]) + if r > 0 and r < numRows - 1 and i + increment - 2 * r < len(s): + res.append(s[i + increment - 2 * r]) + + return ''.join(res) +``` + +```java +public class Solution { + public String convert(String s, int numRows) { + if (numRows == 1) { + return s; + } + + StringBuilder res = new StringBuilder(); + int len = s.length(); + + for (int r = 0; r < numRows; r++) { + int increment = 2 * (numRows - 1); + for (int i = r; i < len; i += increment) { + res.append(s.charAt(i)); + if (r > 0 && r < numRows - 1 && i + increment - 2 * r < len) { + res.append(s.charAt(i + increment - 2 * r)); + } + } + } + + return res.toString(); + } +} +``` + +```cpp +class Solution { +public: + string convert(string s, int numRows) { + if (numRows == 1) { + return s; + } + + string res; + int len = s.size(); + + for (int r = 0; r < numRows; r++) { + int increment = 2 * (numRows - 1); + for (int i = r; i < len; i += increment) { + res += s[i]; + if (r > 0 && r < numRows - 1 && i + increment - 2 * r < len) { + res += s[i + increment - 2 * r]; + } + } + } + + return res; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} numRows + * @return {string} + */ + convert(s, numRows) { + if (numRows === 1) { + return s; + } + + let res = []; + const len = s.length; + + for (let r = 0; r < numRows; r++) { + const increment = 2 * (numRows - 1); + for (let i = r; i < len; i += increment) { + res.push(s[i]); + if (r > 0 && r < numRows - 1 && i + increment - 2 * r < len) { + res.push(s[i + increment - 2 * r]); + } + } + } + + return res.join(''); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for the ouput string. + +--- + +## 2. Iteration - II + +::tabs-start + +```python +class Solution: + def convert(self, s: str, numRows: int) -> str: + if numRows == 1 or numRows >= len(s): + return s + + res = [[] for _ in range(numRows)] + row, dir = 0, 1 + for c in s: + res[row].append(c) + row += dir + if row == 0 or row == (numRows - 1): + dir *= -1 + + return ''.join([''.join(row) for row in res]) +``` + +```java +public class Solution { + public String convert(String s, int numRows) { + if (numRows == 1 || numRows >= s.length()) { + return s; + } + + List[] res = new ArrayList[numRows]; + for (int i = 0; i < numRows; i++) { + res[i] = new ArrayList<>(); + } + + int row = 0, dir = 1; + for (int i = 0; i < s.length(); i++) { + res[row].add(s.charAt(i)); + row += dir; + if (row == 0 || row == numRows - 1) { + dir *= -1; + } + } + + StringBuilder result = new StringBuilder(); + for (List rowList : res) { + for (char c : rowList) { + result.append(c); + } + } + return result.toString(); + } +} +``` + +```cpp +class Solution { +public: + string convert(string s, int numRows) { + if (numRows == 1 || numRows >= s.size()) { + return s; + } + + vector res(numRows); + int row = 0, dir = 1; + + for (char& c : s) { + res[row] += c; + row += dir; + if (row == 0 || row == numRows - 1) { + dir *= -1; + } + } + + string result; + for (string& rowString : res) { + result += rowString; + } + return result; + } +}; +``` + +```javascript +class Solution { + /** + * @param {string} s + * @param {number} numRows + * @return {string} + */ + convert(s, numRows) { + if (numRows === 1 || numRows >= s.length) { + return s; + } + + const res = Array.from({ length: numRows }, () => []); + let row = 0, dir = 1; + + for (const c of s) { + res[row].push(c); + row += dir; + if (row === 0 || row === numRows - 1) { + dir *= -1; + } + } + + return res.map(row => row.join("")).join(""); + } +} +``` + +::tabs-end + +### Time & Space Complexity + +* Time complexity: $O(n)$ +* Space complexity: $O(n)$ for the output string. \ No newline at end of file diff --git a/c/0001-two-sum.c b/c/0001-two-sum.c new file mode 100644 index 000000000..ccc15633e --- /dev/null +++ b/c/0001-two-sum.c @@ -0,0 +1,43 @@ +/* + Time: O(n) + Space: O(n) +*/ + +typedef struct { + int key; // key of hash_table + int val; + + UT_hash_handle hh; // Makes this structure hashable +} hash_table; + +hash_table *hash = NULL, *elem, *tmp; + +int* twoSum(int* nums, int numsSize, int target, int* returnSize){ + int* res = calloc((*returnSize = 2), sizeof(int)); + + for(int i = 0; i < numsSize; ++i){ + int k = target - nums[i]; + + HASH_FIND_INT(hash, &k, elem); // Look for the item in hash table + + if (elem) { + res[0] = elem->val; + res[1] = i; + break; + } + else { + elem = malloc(sizeof(hash_table)); + elem->key = nums[i]; // array element as key of hash table + elem->val = i; // index of an element as value of hash table + + HASH_ADD_INT(hash, key, elem); // Add item to hash table + } + } + + // Free up the hash table + HASH_ITER(hh, hash, elem, tmp) { + HASH_DEL(hash, elem); free(elem); + } + + return res; +} \ No newline at end of file diff --git a/c/0002-add-two-numbers.c b/c/0002-add-two-numbers.c new file mode 100644 index 000000000..b3e5a1194 --- /dev/null +++ b/c/0002-add-two-numbers.c @@ -0,0 +1,67 @@ +/* + Given two linked lists where the nodes represent the digits of two numbers, + add the numbers together and return the sum as a linked list. + Ex. l1 = [2,4,3], + l2 = [5,6,4] -> [7,0,8] + + Traverse the linked lists and add the values of the corresponding nodes, if + the sum is greater than 10, carry is present and will be added along with + the next pair of digits. The lengths of the lists may differ, so check if + the nodes are not null, before adding. + + Time: O(max(m, n)) where m, n are lengths of l1 and l2 respectively + Space: O(max(m, n)) +*/ + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * struct ListNode *next; + * }; + */ +struct ListNode* newNode(int val) { + struct ListNode* node = (struct ListNode*) malloc(sizeof(struct ListNode)); + node->val = val; + node->next = NULL; + + return node; +} + +struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2){ + struct ListNode* head = NULL; + struct ListNode* curr = NULL; + + int carry = 0; + + while (l1 || l2) { + int sum = carry; + + if (l1) + sum += l1->val; + if (l2) + sum += l2->val; + + carry = sum/10; + sum %= 10; + + if (head == NULL) { + head = newNode(sum); + curr = head; + } else { + curr->next = newNode(sum); + curr = curr->next; + } + + if (l1) + l1 = l1->next; + if (l2) + l2 = l2->next; + } + + if (carry) + curr->next = newNode(carry); + + + return head; +} diff --git a/c/0003-longest-substring-without-repeating-characters.c b/c/0003-longest-substring-without-repeating-characters.c new file mode 100644 index 000000000..0dd45d67f --- /dev/null +++ b/c/0003-longest-substring-without-repeating-characters.c @@ -0,0 +1,28 @@ +/* +Given a string s, find the length of the longest substring without repeating characters. +Time: O(n) +Space: O(1) +*/ + +int max(int a, int b) { + return a>b?a:b; +} + +int lengthOfLongestSubstring(char * s){ + int alpha[128] = {0}; + int i=0; + int j=0; + int ans=0; + while (s[j]!='\0') { + alpha[s[j]]++; + if (alpha[s[j]]>1) { + while (alpha[s[j]]>1) { + alpha[s[i]]--; + i++; + } + } + ans = max(ans, j-i+1); + j++; + } + return ans; +} diff --git a/c/0004-median-of-two-sorted-arrays.c b/c/0004-median-of-two-sorted-arrays.c new file mode 100644 index 000000000..2a5a87256 --- /dev/null +++ b/c/0004-median-of-two-sorted-arrays.c @@ -0,0 +1,62 @@ +/* + Time: log(min(n, m)) + Space: O(1) +*/ + +#define min(x, y) ((x < y) ? (x) : (y)) +#define max(x, y) ((x > y) ? (x) : (y)) + +double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size){ + int* A = nums1; + int* B = nums2; + int ASize = nums1Size; + int BSize = nums2Size; + + int total = nums1Size + nums2Size; + int half = total / 2; + + if(nums2Size < nums1Size) + { + A = nums2; + B = nums1; + ASize = nums2Size; + BSize = nums1Size; + } + + int l = 0; + int r = ASize - 1; + + while(true) + { + int i = l + ((r - l - 2 + 1) / 2); // A Mid, round down instead of rounding towards 0 + int j = half - i - 2; // B Mid + + + int Aleft = i >= 0 ? A[i] : INT_MIN; + int Aright = (i + 1) < ASize ? A[i + 1] : INT_MAX; + int Bleft = j >= 0 ? B[j] : INT_MIN; + int Bright = (j + 1) < BSize ? B[j + 1] : INT_MAX; + + // partition is correct + if(Aleft <= Bright && Bleft <= Aright) + { + // Odd + if(total % 2) + { + return min(Aright, Bright); + } + + // Even + return (max(Aleft, Bleft) + min(Aright, Bright)) / 2.0; + } + else if(Aleft > Bright) + { + r = i - 1; + } + else + { + l = i + 1; + } + } + +} diff --git a/c/0005-longest-palindromic-substring.c b/c/0005-longest-palindromic-substring.c new file mode 100644 index 000000000..884f9dc26 --- /dev/null +++ b/c/0005-longest-palindromic-substring.c @@ -0,0 +1,42 @@ + +/* +Given a string s, return the longest palindromic substring in s. +Time: O(n^2) +Space: O(1) +*/ + +char* longestPalindrome(char * s){ + int pos=0; + int lenmax=1; + for (int i=0; s[i]!='\0'; i++){ + int g=i, d=i; + while (g>=0 && s[d]!='\0' && s[g]==s[d]){ + g-=1; + d+=1; + } + d--; + g++; + if (d-g>=lenmax){ + lenmax=d-g+1; + pos=g; + } + g=i,d=i+1; + while (g>=0 && s[d]!='\0' && s[g]==s[d]){ + g-=1; + d+=1; + } + d--; + g++; + + if (d-g>=lenmax){ + lenmax=d-g+1; + pos=g; + } + } + char* new_s = (char*)malloc(sizeof(char)*(lenmax+1)); + new_s[lenmax]='\0'; + for (int i=0; i INT_MAX / 10) || + (res < INT_MIN / 10) || + (res == INT_MAX / 10 && digit > INT_MAX % 10) || + (res == INT_MIN && digit > INT_MIN % 10)) { + return 0; + } else { + res *= 10; + } + res += digit; + + x = x / 10; + } + + return res; +} \ No newline at end of file diff --git a/c/0009-palindrome-number.c b/c/0009-palindrome-number.c new file mode 100644 index 000000000..30400e8d8 --- /dev/null +++ b/c/0009-palindrome-number.c @@ -0,0 +1,23 @@ +bool isPalindrome(int x){ + if (x < 0) { + return false; + } + long div = 1; + while (x >= 10 * div) { + div *= 10; + } + + while (x) { + int right = x % 10; + int left = x / div; + + if (left != right) { + return false; + } + + x = (x % div) / 10; + div /= 100; + } + + return true; +} \ No newline at end of file diff --git a/c/0010-regular-expression-matching.c b/c/0010-regular-expression-matching.c new file mode 100644 index 000000000..82c0b4e99 --- /dev/null +++ b/c/0010-regular-expression-matching.c @@ -0,0 +1,26 @@ +bool isMatch(char *s, char *p) { + int m = strlen(s); + int n = strlen(p); + + bool dp[m + 1][n + 1]; + memset(dp, false, sizeof(dp)); + dp[0][0] = true; + + for (int j = 1; j <= n; j++) { + if (p[j - 1] == '*') { + dp[0][j] = dp[0][j - 2]; + } + } + + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + if (p[j - 1] == s[i - 1] || p[j - 1] == '.') { + dp[i][j] = dp[i - 1][j - 1]; + } else if (p[j - 1] == '*') { + dp[i][j] = dp[i][j - 2] || ((s[i - 1] == p[j - 2] || p[j - 2] == '.') && dp[i - 1][j]); + } + } + } + + return dp[m][n]; +} diff --git a/c/0011-container-with-most-water.c b/c/0011-container-with-most-water.c new file mode 100644 index 000000000..634d66a27 --- /dev/null +++ b/c/0011-container-with-most-water.c @@ -0,0 +1,25 @@ +int maxArea(int* height, int heightSize){ + int left = 0; + int right = heightSize - 1; + int res = 0; + + while (left < right) { + res = max(res, min(height[left], height[right]) * (right - left)); + if (height[left] < height[right]) { + left += 1; + } + else if (height[right] <= height[left]) { + right -= 1; + } + } + return res; +} + +// C does not have a predefined min and max function +int max(int a, int b) { + return (a > b) ? a : b; +} + +int min(int a, int b) { + return (a < b) ? a : b; +} \ No newline at end of file diff --git a/c/0013-roman-to-integer.c b/c/0013-roman-to-integer.c new file mode 100644 index 000000000..478d37765 --- /dev/null +++ b/c/0013-roman-to-integer.c @@ -0,0 +1,42 @@ +int value(char c){ + + switch(c) { + case 'I': return 1; + case 'V': return 5; + case 'X': return 10; + case 'L': return 50; + case 'C': return 100; + case 'D': return 500; + case 'M': return 1000; + default: return 0; + } +} + +int romanToInt(char * s){ + int len = 0; + int sum = 0; + int valueCurrent, valueNext; + + len = strlen(s); + + for(int i=0; i valueCurrent) { + sum = sum - valueCurrent; + } + else { + sum = sum + valueCurrent; + } + } + + return sum; +} \ No newline at end of file diff --git a/c/0014-Longest-Common-Prefix.c b/c/0014-Longest-Common-Prefix.c new file mode 100644 index 000000000..89f205a68 --- /dev/null +++ b/c/0014-Longest-Common-Prefix.c @@ -0,0 +1,21 @@ +char * longestCommonPrefix(char ** strs, int strsSize){ + int commonPrefixCount = 0; + int firstStrSize = strlen(strs[0]); + + for(int i = 0; i < firstStrSize; i++) + { + for(int s = 1; s < strsSize; s++) + { + if(i == strlen(strs[s]) || strs[0][i] != strs[s][i]) + { + // Add null terminator after the last common prefix char + strs[0][commonPrefixCount] = '\0'; + return strs[0]; + } + } + + commonPrefixCount++; + } + + return strs[0]; +} diff --git a/c/0014-longest-common-prefix.c b/c/0014-longest-common-prefix.c new file mode 100644 index 000000000..89f205a68 --- /dev/null +++ b/c/0014-longest-common-prefix.c @@ -0,0 +1,21 @@ +char * longestCommonPrefix(char ** strs, int strsSize){ + int commonPrefixCount = 0; + int firstStrSize = strlen(strs[0]); + + for(int i = 0; i < firstStrSize; i++) + { + for(int s = 1; s < strsSize; s++) + { + if(i == strlen(strs[s]) || strs[0][i] != strs[s][i]) + { + // Add null terminator after the last common prefix char + strs[0][commonPrefixCount] = '\0'; + return strs[0]; + } + } + + commonPrefixCount++; + } + + return strs[0]; +} diff --git a/c/0015-3sum.c b/c/0015-3sum.c new file mode 100644 index 000000000..ca0dbcded --- /dev/null +++ b/c/0015-3sum.c @@ -0,0 +1,72 @@ +int compareInt(const void *a, const void *b) { + return *(int*)a - *(int*)b; +} + +/** + * Return an array of arrays of size *returnSize. + * The sizes of the arrays are returned as *returnColumnSizes array. + * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free(). + */ +int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) { + int cap = 32; + int **result = (int**)malloc(sizeof(int*) * cap); + int *cols = (int*)malloc(sizeof(int) * cap); + int *triplet; + int sum, target, count; + int i, left, right; + + qsort(nums, numsSize, sizeof(int), compareInt); + + count = 0; + for (i = 0; i < numsSize - 2; i++) { + // The array is sorted, there is no possible tripet when this happen. + if (nums[i] > 0) { + break; + } + // skip element with same value to avoid duplicate triplets. + if (i > 0 && nums[i] == nums[i - 1]) { + continue; + } + target = 0 - nums[i]; + left = i + 1; + right = numsSize - 1; + while (left < right) { + sum = nums[left] + nums[right]; + if (sum > target) { + right--; + } else if (sum < target) { + left++; + } else { + triplet = (int*)malloc(sizeof(int) * 3); + triplet[0] = nums[i]; + triplet[1] = nums[left]; + triplet[2] = nums[right]; + + result[count] = triplet; + cols[count] = 3; + count++; + + if (count == cap) { + cap *= 2; + result = (int**)realloc(result, sizeof(int*) * cap); + cols = (int*)realloc(cols, sizeof(int) * cap); + } + + left++; + right--; + + // skip element with same value to avoid duplicate triplets. + while(left < right && nums[left] == nums[left - 1]) { + left++; + } + while(left < right && nums[right] == nums[right + 1]) { + right--; + } + } + } + } + *returnSize = count; + *returnColumnSizes = cols; + + return result; +} diff --git a/c/0019-remove-nth-node-from-end-of-list.c b/c/0019-remove-nth-node-from-end-of-list.c new file mode 100644 index 000000000..28538e4ff --- /dev/null +++ b/c/0019-remove-nth-node-from-end-of-list.c @@ -0,0 +1,39 @@ +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * struct ListNode *next; + * }; + */ + + +struct ListNode* removeNthFromEnd(struct ListNode* head, int n){ + if (head->next == NULL) { + return head->next; + } + + struct ListNode* slow = head; + struct ListNode* fast = head; + + while (n--) { + fast = fast->next; + } + + if (fast != NULL) { + while (fast->next != NULL) { + slow = slow->next; + fast = fast->next; + } + struct ListNode* tmp = slow->next; + slow->next = tmp->next; + free(tmp); + } + else { + slow->val = slow->next->val; + struct ListNode* tmp = slow->next; + slow->next = tmp->next; + free(tmp); + } + + return head; +} \ No newline at end of file diff --git a/c/0020-valid-parentheses.c b/c/0020-valid-parentheses.c new file mode 100644 index 000000000..8bece2ead --- /dev/null +++ b/c/0020-valid-parentheses.c @@ -0,0 +1,80 @@ + +struct Node { + char val; + struct Node* next; +}; + +struct Stack { + struct Node* head; + size_t len; +}; + +struct Node* Node(char val, struct Node* next) { + struct Node* root = (struct Node*)malloc(sizeof(struct Node)); + root -> next = next; + root -> val = val; + return root; +} + +struct Stack* Stack() { + struct Stack* stack = (struct Stack*)malloc(sizeof(struct Stack)); + stack -> len = 0; + stack -> head = NULL; + return stack; +} + +void append(struct Stack* stack, char val) { + struct Node* node = Node(val, stack -> head); + stack -> head = node; + stack -> len += 1; +} + +char pop(struct Stack* stack) { + if (stack -> head == NULL) { + return NULL; + } + char val = stack -> head -> val; + struct Node* deleteNode = stack -> head; + stack -> head = stack -> head -> next; + stack -> len -= 1; + free(deleteNode); + return val; +} + +void freeStack(struct Stack* stack) { + while (pop(stack) != NULL) { + pop(stack); + } + free(stack); +} + +char opposite_parenthesis(char closing) { + char opening = NULL; + if (closing == ')') { + opening = '('; + } else if (closing == '}') { + opening = '{'; + } else if (closing == ']') { + opening = '['; + } + return opening; +} + +bool isValid(char * s){ + + struct Stack* stack = Stack(); + char* chr; + + for (chr = s; *chr != '\0'; chr++) { + if (opposite_parenthesis(*chr) == NULL) { + append(stack, *chr); + } else if (stack -> len != 0 && opposite_parenthesis(*chr) == pop(stack)) { + continue; + } else { + return false; + } + } + bool result = stack -> len == 0; + freeStack(stack); + return result; +} \ No newline at end of file diff --git a/c/0021-merge-two-sorted-lists.c b/c/0021-merge-two-sorted-lists.c new file mode 100644 index 000000000..7ee608d9c --- /dev/null +++ b/c/0021-merge-two-sorted-lists.c @@ -0,0 +1,46 @@ + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * struct ListNode *next; + * }; + */ + + +struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2){ + + if (list1 == NULL && list2 == NULL) { + return NULL; + } else if (list1 != NULL && list2 == NULL) { + return list1; + } else if (list2 != NULL && list1 == NULL) { + return list2; + } + + struct ListNode* temp_node = NULL; + + if (list1 -> val < list2 -> val) { + temp_node = list1; + list1 = list1 -> next; + } else { + temp_node = list2; + list2 = list2 -> next; + } + + struct ListNode* root = temp_node; + + while((list1 != NULL) || (list2 != NULL)) { + + if ((list2 == NULL) || ((list1 != NULL) && (list1 -> val < list2 -> val))) { + temp_node -> next = list1; + list1 = list1 -> next; + } else { + temp_node -> next = list2; + list2 = list2 -> next; + } + temp_node = temp_node -> next; + } + + return root; +} \ No newline at end of file diff --git a/c/0022-generate-parentheses.c b/c/0022-generate-parentheses.c new file mode 100644 index 000000000..0ca68783a --- /dev/null +++ b/c/0022-generate-parentheses.c @@ -0,0 +1,141 @@ +/* + * Problem: Generate Parentheses (Medium) + * Link: https://leetcode.com/problems/generate-parentheses/ + * + * The solution uses a backtracking approach to find all well-formed parantheses + * pairs. + */ + +#include +#include +#include +#include +#include + +/*---------------------------------------------------*/ +// Things get easier to manage with this structure +// It will essentially act a minimal version of `std::string`. +// The length of the string is always fixed as we know what the length is going +// to be. +struct _Str +{ + char* buf; + int len; + int idx; +} DefStr = { .idx = 0 }; + +// Doesn't add anything to the code other than convenience of writing `Str ...` +// over `struct _Str ...` +typedef struct _Str Str; + +// Pushes a character to the back of the Str +void +push_back(Str* self, char chr) +{ + if (self->idx > self->len) { + // Length check + return; + } + self->buf[self->idx++] = chr; +} + +// Pops last character from the Str. Add Nullchar at the end just to signify the +// end of string. +void +pop_back(Str* self) +{ + if (self->idx == 0) { + return; + } + self->buf[self->idx--] = '\0'; +} + +/*---------------------------------------------------*/ + +/* + * This problem can be reduced to a subsets problem, where we explore every + * possible parantheses combination and keep the valid ones. + * + * To do so, we will -- + * 1. Generate a combination + * 2. Check if it is balanced + * 3. Backtrack + * 4. Repeat + */ +void +backtrack(char** results, + int n, + int open, + int closed, + Str* curr, + int* returnSize) +{ + if (closed > open) { + // This is the case when we prune the exploration of not well-formed + // parentheses + return; + } + if (open + closed == 2 * n) { + // ^ checking lengths + // Create a copy of the qualifying buffer and save it in Results + // wf = well-formed + results[*returnSize] = (char*)calloc(2 * n + 1, sizeof(char)); + memcpy(results[*returnSize], curr->buf, 2 * n); + *returnSize = *returnSize + 1; + return; + } + + if (open < n) { + // If we do not have enough opening brackets, add one till we have `n` + push_back(curr, '('); + backtrack(results, n, open + 1, closed, curr, returnSize); + + // Pop this so that we can explore the other combinations + pop_back(curr); + } + + if (closed < n) { + // If we do not have enough closing brackets, add one till we have `n` + push_back(curr, ')'); + backtrack(results, n, open, closed + 1, curr, returnSize); + + // Pop this so that we can explore the other combinations + pop_back(curr); + } +} + +/** + * Note: The returned array must be malloced, assume caller calls free(). + */ +char** +generateParenthesis(int n, int* returnSize) +{ + /* + * Allocate enough memory for maximum possible combinations + * The maximum would ideally be every possible combination (included invalid + * ones). This is 2^(len of combination) == 2^(2 * n) + * + * Ofc, we will need less memory than this, but its fine. + * + * In the worst case according to our constraints, n = 9. + * In this case, we will allocate 2^9 * 8 = 4096 bytes, which is 4KB. So we + * can be sure that this program won't hog **too** much memory. + */ + + char** results = (char**)malloc(pow(2, 2 * n) * sizeof(char*)); + + // Length is excluding null character, calloc includes null character + // Calloc because it zeroes out our memory, so we automatically get a null + // character. + Str current = { .len = 2 * n, .buf = (char*)calloc(2 * n + 1, sizeof(char)) }; + + // We backtrack. + *returnSize = 0; + backtrack(results, n, 0, 0, ¤t, returnSize); + + // Free the Str buffer as we won't need it now. + free(current.buf); + + // Return results + return results; +} diff --git a/c/0023-merge-k-sorted-lists.c b/c/0023-merge-k-sorted-lists.c new file mode 100644 index 000000000..5379955d7 --- /dev/null +++ b/c/0023-merge-k-sorted-lists.c @@ -0,0 +1,75 @@ +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * struct ListNode *next; + * }; + */ + +/** + * Time: O(n.log(k)) + * Space: O(1) + */ + +typedef struct ListNode ListNode; + +ListNode* mergeLists(ListNode* l1, ListNode* l2) +{ + ListNode dummy; + dummy.next = NULL; + ListNode* tail = &dummy; + + while(l1 && l2) + { + if(l1->val < l2->val) + { + tail->next = l1; + l1 = l1->next; + } + else + { + tail->next = l2; + l2 = l2->next; + } + + tail = tail->next; + } + + if(l1) + { + tail->next = l1; + } + else if(l2) + { + tail->next = l2; + } + + return dummy.next; +} + +struct ListNode* mergeKLists(struct ListNode** lists, int listsSize){ + + if(listsSize == 0) + { + return NULL; + } + + while(listsSize > 1) + { + ListNode** mergedLists; + size_t mergedListsIndex = 0; + + for(size_t i = 0; i < listsSize; i += 2) + { + ListNode* l1 = lists[i]; + ListNode* l2 = (i + 1) < listsSize ? lists[i + 1] : NULL; + + mergedLists[mergedListsIndex++] = mergeLists(l1, l2); + } + + lists = mergedLists; + listsSize = (listsSize + 1) / 2; + } + + return lists[0]; +} diff --git a/c/0024-swap-nodes-in-pairs.c b/c/0024-swap-nodes-in-pairs.c new file mode 100644 index 000000000..fc0609ef8 --- /dev/null +++ b/c/0024-swap-nodes-in-pairs.c @@ -0,0 +1,41 @@ +/* + Given a linked list, swap every two adjacent nodes and return its head. + You must solve the problem without modifying the values in the list's nodes (i.e., only nodes themselves may be changed.) + + Ex. Input: head = [1,2,3,4] + Output: [2,1,4,3] + + Time : O(N) + Space : O(1) +*/ + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * struct ListNode *next; + * }; + */ +struct ListNode* swapPairs(struct ListNode* head) { + if (head == NULL || head->next == NULL) + return head; + + struct ListNode *new_head = head->next; + struct ListNode *prev = NULL; + + while (head != NULL && head->next != NULL) { + struct ListNode *next_pair = head->next->next; + struct ListNode *second = head->next; + + if (prev != NULL) + prev->next = second; + + second->next = head; + head->next = next_pair; + + prev = head; + head = next_pair; + } + + return new_head; +} diff --git a/c/0025-reverse-nodes-in-k-group.c b/c/0025-reverse-nodes-in-k-group.c new file mode 100644 index 000000000..6cbfa3613 --- /dev/null +++ b/c/0025-reverse-nodes-in-k-group.c @@ -0,0 +1,60 @@ +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * struct ListNode *next; + * }; + */ + +typedef struct ListNode ListNode; + +ListNode* getKth(ListNode* curr, int k) +{ + while(curr && k > 0) + { + curr = curr->next; + k--; + } + + return curr; +} + +struct ListNode* reverseKGroup(struct ListNode* head, int k){ + ListNode dummy = { 0, head }; + ListNode* prevGroupTail = &dummy; + + while(true) + { + ListNode* kth = getKth(prevGroupTail, k); + + if(!kth) + { + break; + } + + ListNode* nextGroupHead = kth->next; + + + // Reverse group + ListNode* prev = nextGroupHead; + ListNode* curr = prevGroupTail->next; + while(curr != nextGroupHead) + { + ListNode* temp = curr->next; + curr->next = prev; + // Increment the pointers + prev = curr; + curr = temp; + } + + + ListNode* lastNodeInCurrentGroup = prevGroupTail->next; + // Connect the previous group to the current group + prevGroupTail->next = kth; // kth is now the first node in the group + // Update prevGroupTail for the next iteration of the loop + prevGroupTail = lastNodeInCurrentGroup; + + } + + return dummy.next; +} diff --git a/c/0026-remove-duplicates-from-sorted-array.c b/c/0026-remove-duplicates-from-sorted-array.c new file mode 100644 index 000000000..93d92ed96 --- /dev/null +++ b/c/0026-remove-duplicates-from-sorted-array.c @@ -0,0 +1,11 @@ +int removeDuplicates(int* nums, int numsSize){ + int indx = 1; + + for(int i = 1; i < numsSize; i++){ + if(nums[i] != nums[i-1]){ + nums[indx] = nums[i]; + indx++; + } + } + return indx; +} \ No newline at end of file diff --git a/c/0027-remove-element.c b/c/0027-remove-element.c new file mode 100644 index 000000000..490383b21 --- /dev/null +++ b/c/0027-remove-element.c @@ -0,0 +1,14 @@ +int removeElement(int* nums, int numsSize, int val){ + int k = 0; + + for(int i = 0; i < numsSize; i++) + { + if(nums[i] != val) + { + nums[k] = nums[i]; + k++; + } + } + + return k; +} diff --git a/c/0028-find-the-index-of-the-first-occurrence-in-a-string.c b/c/0028-find-the-index-of-the-first-occurrence-in-a-string.c new file mode 100644 index 000000000..7112e2ff8 --- /dev/null +++ b/c/0028-find-the-index-of-the-first-occurrence-in-a-string.c @@ -0,0 +1,19 @@ +int strStr(char * haystack, char * needle){ + int h_size = strlen(haystack); + int n_size = strlen(needle); + int i, j; + if (h_size < n_size) { + return -1; + } + for (i = 0; i < h_size - n_size + 1; i++) { + for (j = 0; j < n_size; j++) { + if (haystack[i + j] != needle[j]) { + break; + } + } + if (j == n_size) { + return i; + } + } + return -1; +} diff --git a/c/0033-search-in-rotated-sorted-array.c b/c/0033-search-in-rotated-sorted-array.c new file mode 100644 index 000000000..649184e2d --- /dev/null +++ b/c/0033-search-in-rotated-sorted-array.c @@ -0,0 +1,62 @@ +int findPivotIndex(int* nums, int numsSize) +{ + int s = 0; + int e = numsSize; + + while(s <= e){ + + int m = s + (e - s)/2; + + if(m < e && nums[m] > nums[m+1]){ + return m; + } + else if(s < m && nums[m] < nums[m-1]){ + return m - 1; + } + else if(nums[s] < nums[m]){ + s = m + 1; + } + else if(nums[s] >= nums[m]){ + e = m - 1; + } + } + + // If the array is not rotated then last position will be the pivot element. + + return numsSize; +} + +int binarySearch(int *nums, int s, int e, int target) +{ + while(s <= e){ + + int m = s + (e - s)/2; + + if(nums[m] == target){ + return m; + } + else if(nums[m] < target){ + s = m + 1; + } + else{ + e = m - 1; + } + } + + return -1; +} + +int search(int* nums, int numsSize, int target){ + + int n = numsSize - 1; + + int pivotIndex = findPivotIndex(nums, n); + + int firstTry = binarySearch(nums, 0, pivotIndex, target); + + if(firstTry != -1){ + return firstTry; + } + + return binarySearch(nums, pivotIndex + 1, n, target); +} \ No newline at end of file diff --git a/c/0035-search-insert-position.c b/c/0035-search-insert-position.c new file mode 100644 index 000000000..963671453 --- /dev/null +++ b/c/0035-search-insert-position.c @@ -0,0 +1,22 @@ +/* + +Space: O(1) +Time: O(log(n)) +*/ + +int searchInsert(int* nums, int numsSize, int target){ + int i=0; + int j=numsSize-1; + int m; + while (i<=j) { + m = (i+j)/2; + if (nums[m]==target) { + return m; + } else if (nums[m] array = (int*)malloc(array_size * sizeof(int)); + comb_array -> array_size = array_size; + return comb_array; +} + +// A generic stack node with void* pointer. +struct Stack { + void* value; + struct Stack* next; +}; + +// A function to create the stack node. +struct Stack* Node(void* value, struct Stack* next) { + struct Stack* node = (struct Stack*)malloc(sizeof(struct Stack)); + node -> value = value; + node -> next = next; + return node; +} + +// append to the stack. +void append(struct Stack* root, void* value) { + struct Stack* node = Node(value, root -> next); + root -> next = node; +} + +// pop from the stack. +void* pop(struct Stack* root) { + void* value = root -> next -> value; + struct Stack* delete_node = root -> next; + root -> next = root -> next -> next; + free(delete_node); + return value; +} + +// Recursive backtracking method. +int backtrack(struct Stack* results, struct Stack* candidates_stack, int stack_size, int running_sum, int* candidates, int index) { + + if (running_sum == 0) { + // create a combination array. + struct Combination_Array* combination = New(stack_size); + // copy the candidates from the candidates_stack to the combination array. + struct Stack* node = candidates_stack; + for (int i = 0; i < stack_size; i++) { + combination -> array[i] = *(int*)node -> next -> value; + node = node -> next; + } + // append the combination to the results stack. + append(results, combination); + return 1; + } + + int returnSize = 0; + if (running_sum > 0) { + for (int i = index; i >= 0; i--) { + append(candidates_stack, &candidates[i]); + returnSize += backtrack(results, candidates_stack, stack_size + 1, running_sum - candidates[i], candidates, i); + // backtrack by poping the candidate from the candidate_stack. + pop(candidates_stack); + } + } + + return returnSize; +} + + +int** combinationSum(int* candidates, int candidatesSize, int target, int* returnSize, int** returnColumnSizes){ + + // A candidates_stack to track all possible combinations of candidates during backtracking. + struct Stack* candidates_stack = Node(NULL, NULL); + // A results stack to store each combination solution on the stack during backtracking. + struct Stack* results = Node(NULL, NULL); + + // The backtracking method returns the total number of possible combinations (the final result size). + *returnSize = backtrack(results, candidates_stack, 0, target, candidates, candidatesSize - 1); + + // prepare the result array. + int** result_array = (int**)malloc(*returnSize * sizeof(int*)); + *returnColumnSizes = (int*)malloc(*returnSize * sizeof(int)); + + for (int i = 0; i < *returnSize; i++) { + // pop() each solution from the results stack to the results array. + struct Combination_Array* combination = (struct Combination_Array*)pop(results); + result_array[i] = combination -> array; + returnColumnSizes[0][i] = combination -> array_size; + free(combination); + } + // finally free the results stack and the candidates stack. + free(results); + free(candidates_stack); + return result_array; + +} \ No newline at end of file diff --git a/c/0040-combination-sum-ii.c b/c/0040-combination-sum-ii.c new file mode 100644 index 000000000..999269456 --- /dev/null +++ b/c/0040-combination-sum-ii.c @@ -0,0 +1,44 @@ +void backtrack(int *candidates, int candidatesSize, int target, int start, int *current, int currentSize, int **result, int *resultSize, int *returnColumnSizes) { + if (target == 0) { + result[*resultSize] = (int *)malloc(currentSize * sizeof(int)); + returnColumnSizes[*resultSize] = currentSize; + for (int i = 0; i < currentSize; i++) { + result[*resultSize][i] = current[i]; + } + (*resultSize)++; + return; + } + + for (int i = start; i < candidatesSize; i++) { + if (i > start && candidates[i] == candidates[i - 1]) { + continue; // Skip duplicates to avoid duplicate combinations + } + + if (candidates[i] > target) { + break; // Since the array is sorted, we can exit early + } + + current[currentSize] = candidates[i]; + backtrack(candidates, candidatesSize, target - candidates[i], i + 1, current, currentSize + 1, result, resultSize, returnColumnSizes); + } +} + +int compare(const void *a, const void *b) { + return (*(int *)a - *(int *)b); +} + +int** combinationSum2(int* candidates, int candidatesSize, int target, int* returnSize, int** returnColumnSizes) { + qsort(candidates, candidatesSize, sizeof(int), compare); + + int **result = (int **)malloc(1000 * sizeof(int *)); + int *current = (int *)malloc(candidatesSize * sizeof(int)); + *returnColumnSizes = (int *)malloc(1000 * sizeof(int)); + + int resultSize = 0; + backtrack(candidates, candidatesSize, target, 0, current, 0, result, &resultSize, *returnColumnSizes); + + free(current); + + *returnSize = resultSize; + return result; +} diff --git a/c/0041-first-missing-positive.c b/c/0041-first-missing-positive.c new file mode 100644 index 000000000..f95c05617 --- /dev/null +++ b/c/0041-first-missing-positive.c @@ -0,0 +1,26 @@ +int firstMissingPositive(int* nums, int numsSize){ + + int i = 0; + + while(i < numsSize){ + int val = nums[i]; + if(val > 0) val = val - 1; + + if(nums[i] > 0 && nums[i] < numsSize && nums[i] != nums[val]){ + int temp = nums[i]; + nums[i] = nums[val]; + nums[val] = temp; + } + else i++; + } + + int ans = numsSize + 1; + for(int i = 0 ; i < numsSize ; i++){ + if(nums[i] != i+1){ + ans = i+1; + break; + } + } + + return ans; +} \ No newline at end of file diff --git a/c/0042-trapping-rain-water.c b/c/0042-trapping-rain-water.c new file mode 100644 index 000000000..e86497142 --- /dev/null +++ b/c/0042-trapping-rain-water.c @@ -0,0 +1,44 @@ +/* + Given elevation map array, compute trapped water + Ex. height = [0,1,0,2,1,0,1,3,2,1,2,1] -> 6 + + Keep two arrays which store the largest bar uptil that point in both + forward (0 -> n) and reverse (n -> 0) directions. Maximum trappable water + depends on the smaller of the left and right maximums. + + Time: O(n) + Space: O(n) +*/ + +int trap(int* height, int heightSize) { + + // Initialize leftMax to store the largest height present to the left of every bar + int leftMax[heightSize]; + + leftMax[0] = height[0]; + for (int i = 1; i < heightSize; ++i) { + leftMax[i] = fmax(height[i], leftMax[i-1]); + } + + // Initialize rightMax similar to leftMax but for largest height to the right + int rightMax[heightSize]; + + rightMax[heightSize-1] = height[heightSize-1]; + for (int i = heightSize-2; i >= 0; --i) { + rightMax[i] = fmax(height[i], rightMax[i+1]); + } + + int waterTrapped = 0; + + for (int i = 0; i < heightSize; ++i) { + // The minimum of both side decides how much water can be trapped + int minHeight = fmin(leftMax[i], rightMax[i]); + + // If the minHeight is more the current height at a point, water is present there + if (minHeight > height[i]) { + waterTrapped += minHeight - height[i]; + } + } + + return waterTrapped; +} diff --git a/c/0043-multiply-strings.c b/c/0043-multiply-strings.c new file mode 100644 index 000000000..171be7a85 --- /dev/null +++ b/c/0043-multiply-strings.c @@ -0,0 +1,32 @@ +char* multiply(char* num1, char* num2) { + int len1 = strlen(num1); + int len2 = strlen(num2); + int len = len1 + len2; + int* result = (int*)calloc(len, sizeof(int)); + + for (int i = len1 - 1; i >= 0; i--) { + for (int j = len2 - 1; j >= 0; j--) { + int product = (num1[i] - '0') * (num2[j] - '0'); + int sum = product + result[i + j + 1]; + result[i + j + 1] = sum % 10; + result[i + j] += sum / 10; + } + } + + char* res = (char*)malloc((len + 1) * sizeof(char)); + int idx = 0; + for (int i = 0; i < len; i++) { + if (idx == 0 && result[i] == 0) { + continue; + } + res[idx++] = result[i] + '0'; + } + res[idx] = '\0'; + + if (idx == 0) { + return "0"; + } + + free(result); + return res; +} diff --git a/c/0045-jump-game-ii.c b/c/0045-jump-game-ii.c new file mode 100644 index 000000000..39ef3ff51 --- /dev/null +++ b/c/0045-jump-game-ii.c @@ -0,0 +1,21 @@ +int jump(int* nums, int numsSize){ + int left = 0; + int right = 0; + int res = 0; + + while (right < numsSize - 1) { + int maxJump = 0; + for (int i = left; i <= right; i++) { + maxJump = max(maxJump, i + nums[i]); + } + left = right + 1; + right = maxJump; + res += 1; + } + return res; +} + +// C doesn't have a built-in max function +int max(int a, int b) { + return (a > b) ? a : b; +} \ No newline at end of file diff --git a/c/0046-permutations.c b/c/0046-permutations.c new file mode 100644 index 000000000..fd790a6b4 --- /dev/null +++ b/c/0046-permutations.c @@ -0,0 +1,44 @@ +void swap(int* a, int* b) { + int temp = *a; + *a = *b; + *b = temp; +} + +void backtrack(int* nums, int numsSize, int** result, int* returnSize, int* current, int index) { + if (index == numsSize) { + result[*returnSize] = (int*)malloc(numsSize * sizeof(int)); + for (int i = 0; i < numsSize; i++) { + result[*returnSize][i] = current[i]; + } + (*returnSize)++; + return; + } + + for (int i = index; i < numsSize; i++) { + swap(&nums[i], &nums[index]); + current[index] = nums[index]; + backtrack(nums, numsSize, result, returnSize, current, index + 1); + swap(&nums[i], &nums[index]); + } +} + +int** permute(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) { + int totalPermutations = 1; + for (int i = 1; i <= numsSize; i++) { + totalPermutations *= i; + } + + int** result = (int**)malloc(totalPermutations * sizeof(int*)); + int* current = (int*)malloc(numsSize * sizeof(int)); + *returnSize = 0; + *returnColumnSizes = (int*)malloc(totalPermutations * sizeof(int)); + + backtrack(nums, numsSize, result, returnSize, current, 0); + + for (int i = 0; i < totalPermutations; i++) { + (*returnColumnSizes)[i] = numsSize; + } + + free(current); + return result; +} diff --git a/c/0048-rotate-image.c b/c/0048-rotate-image.c new file mode 100644 index 000000000..34067590c --- /dev/null +++ b/c/0048-rotate-image.c @@ -0,0 +1,28 @@ +void rotate(int** matrix, int matrixSize, int* matrixColSize){ + int left = 0; + int right = matrixSize - 1; + + while (left < right) { + for (int i = 0; i < right - left; i++) { + int top = left; + int bottom = right; + + // save the topLeft + int topLeft = matrix[top][left + i]; + + // move bottom left into top left + matrix[top][left + i] = matrix[bottom - i][left]; + + // move bottom right into bottom left + matrix[bottom - i][left] = matrix[bottom][right - i]; + + // move top right into bottom right + matrix[bottom][right - i] = matrix[top + i][right]; + + // move top left into top right + matrix[top + i][right] = topLeft; + } + right -= 1; + left += 1; + } +} \ No newline at end of file diff --git a/c/0049-group-anagrams.c b/c/0049-group-anagrams.c new file mode 100644 index 000000000..6357726d4 --- /dev/null +++ b/c/0049-group-anagrams.c @@ -0,0 +1,71 @@ +// Represent an elemet of the hash map. +typedef struct { + char *key; + char *strs[100]; + size_t size; + UT_hash_handle hh; +} StrsHash; + +char *StrToKey(char *str) { + int size = strlen(str); + char c[26] = {0}; + char *key = malloc(size + 1); + char *k = key; + size_t i, j; + + for (i = 0; i < size; i++) { + c[str[i] - 'a']++; + } + + for (i = 0; i < 26; i++) { + for (j = 0; j < c[i]; j++) { + *k++ = 'a' + i; + } + } + *k = '\0'; + return key; +} + +/** + * Return an array of arrays of size *returnSize. + * The sizes of the arrays are returned as *returnColumnSizes array. + * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free(). + */ +char *** groupAnagrams(char ** strs, int strsSize, int* returnSize, int** returnColumnSizes){ + StrsHash *map = NULL; + StrsHash *entry; + char *key = NULL; + char **resStrs = NULL; + char ***result = NULL; + int *colsSize = NULL; + size_t i; + + // Create a hash map. + for (i = 0; i < strsSize; i++) { + entry = NULL; + key = StrToKey(strs[i]); + HASH_FIND_STR(map, key, entry); + if (!entry) { + entry = malloc(sizeof(StrsHash)); + entry->key = key; + entry->size = 0; + HASH_ADD_KEYPTR(hh, map, key, strlen(key), entry); + } else { + free(key); + } + entry->strs[entry->size++] = strs[i]; + } + + // Prepare the answer from the hash map. + *returnSize = HASH_COUNT(map); + colsSize = (int*)malloc(*returnSize * sizeof(int)); + result = (char***)malloc(*returnSize * sizeof(char**)); + *returnColumnSizes = colsSize; + + for (entry = map, i = 0; entry != NULL; entry = entry->hh.next, i++) { + colsSize[i] = entry->size; + result[i] = entry->strs; + } + + return result; +} diff --git a/c/0050-powx-n.c b/c/0050-powx-n.c new file mode 100644 index 000000000..80218db3b --- /dev/null +++ b/c/0050-powx-n.c @@ -0,0 +1,25 @@ +double calc(double x, int n) { + long exponent = abs(n); + + if (x == 0.0) + return 0.0; + if (n == 0) + return 1.0; + + double res = calc(x, n/2); + res = res * res; + + if (n%2 == 0) + return res; + else + return x * res; +} + +double myPow(double x, int n){ + double res = calc(x, n); + + if (n >= 0) + return res; + else + return 1.0 / res; +} \ No newline at end of file diff --git a/c/0051-n-queens.c b/c/0051-n-queens.c new file mode 100644 index 000000000..7a8c35df8 --- /dev/null +++ b/c/0051-n-queens.c @@ -0,0 +1,126 @@ + +/** + * Return an array of arrays of size *returnSize. + * The sizes of the arrays are returned as *returnColumnSizes array. + * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free(). + */ + + +// An array to traverse all 4 diagonal directions on the chessboard. +int diagonals[][2] = {{1, -1}, {1, 1}, {-1, 1}, {-1, -1}}; + +// A result-stack to store all possible n-queen solutions at a time on a stack during backtracking. +struct result_stack { + char** chessboard; + struct result_stack* next; +}; + +// A Node() function to create a new stack node. +struct result_stack* Node() { + struct result_stack* node = (struct result_stack*)malloc(sizeof(struct result_stack)); + node -> next = NULL; + node -> chessboard = NULL; + return node; +} + +// toggle_queen() function to place and remove any queen on the chessboard by passing in the toggle parameter as 1 or -1. +// toggle == 1 to place a queen at (row, col) on the chessboard. Similarly toggle == -1 to remove a queen from the board. +void toggle_queen(char** chessboard, int n, int row, int col, char toggle) { + + for (int i = 0; i < n; i++) chessboard[row][i] += toggle; + for (int j = 0; j < n; j++) chessboard[j][col] += toggle; + + for (int x = 0; x < 4; x++) { + int i = row + diagonals[x][0]; + int j = col + diagonals[x][1]; + while (i >= 0 && i < n && j >= 0 && j < n) { + chessboard[i][j] += toggle; + i += diagonals[x][0]; + j += diagonals[x][1]; + } + } + chessboard[row][col] -= 3 * toggle; +} + +// copy_board() function to copy each possible solution from the chessboard during backtracking. +char** copy_board(char** chessboard, int n) { + + char** copy = (char**)malloc(n * sizeof(char*)); + for(int i = 0; i < n; i++) { + copy[i] = (char*)malloc((n + 1) * sizeof(char)); + for(int j = 0; j < n; j++) { + chessboard[i][j] == -1 ? (copy[i][j] = 'Q') : (copy[i][j] = '.'); + } + copy[i][n] = '\0'; + } + return copy; +} + +// Recursive backtracking method to go through all possible queen placements on the chessboard. +int backtrack(struct result_stack* stack, char** chessboard, int n, int row) { + + if (row == n) { + // Push the solution to the stack. + struct result_stack* node = Node(); // create a new stack node for a solution. + node -> chessboard = copy_board(chessboard, n); + node -> next = stack -> next; + stack -> next = node; + return 1; + } + + int result_size = 0; + for (int col = 0; col < n; col++) { + if (chessboard[row][col] == 0) { + // Place the queen with toggle = 1. + toggle_queen(chessboard, n, row, col, 1); + result_size += backtrack(stack, chessboard, n, row + 1); + // Backtrack by removing the queen with toggle = -1. + toggle_queen(chessboard, n, row, col, -1); + } + } + return result_size; +} + + +char *** solveNQueens(int n, int* returnSize, int** returnColumnSizes){ + + // Create a N x N chessboard for checking all possible queen placement scenarios. + char** chessboard = (char**)malloc(n * sizeof(char*)); + for(int i = 0; i < n; i++) { + chessboard[i] = (char*)malloc(n * sizeof(char)); + for(int j = 0; j < n; j++) { + chessboard[i][j] = 0; + } + } + + // Create an empty stack to collect all possible n-queen solutions during backtracking. + struct result_stack* stack = Node(); + + // The Backtrack() method will find all possible solutions and stores them on the stack. + // Then returns the total size of the result, which is the total number of possible n-queen solutions. + *returnSize = backtrack(stack, chessboard, n, 0); + + // prepare the result array using the *returnSize obtained from backtracking. + char*** result = (char***)malloc(*returnSize * sizeof(char**)); + *returnColumnSizes = (int*)malloc(*returnSize * sizeof(int)); + + // Pop every n-queen solution from the stack and assign them to the result array. + for (int i = 0; i < *returnSize; i++) { + returnColumnSizes[0][i] = n; + result[i] = stack -> next -> chessboard; + struct result_stack* deletenode = stack -> next; + stack -> next = stack -> next -> next; + // free up each stack node after every solution. + free(deletenode); + } + //free up the stack. + free(stack); + + // free up the chessboard. + for (int row = 0; row < n; row++) { + free(chessboard[row]); + } + free(chessboard); + + return result; +} diff --git a/c/0052-n-queens-ii.c b/c/0052-n-queens-ii.c new file mode 100644 index 000000000..c1d651bfa --- /dev/null +++ b/c/0052-n-queens-ii.c @@ -0,0 +1,53 @@ +/* +Given an integer n, return the number of distinct solutions to the n-queens puzzle. + +Space: O(n²) +Time: ? +*/ + +int min(int a, int b){ + return a=0; k--) + if (board[k][j]) + return false; + for (int k=j-1; k>=0; k--) + if (board[i][k]) + return false; + int m = min(i, j)+1; + for (int k=1; k b) ? a : b; +} \ No newline at end of file diff --git a/c/0054-spiral-matrix.c b/c/0054-spiral-matrix.c new file mode 100644 index 000000000..60ef62cce --- /dev/null +++ b/c/0054-spiral-matrix.c @@ -0,0 +1,57 @@ +/** + * Note: The returned array must be malloced, assume caller calls free(). + */ +int* spiralOrder(int** matrix, int matrixSize, int* matrixColSize, int* returnSize) { + // Allocate the result array and set its size + int *result = (int*)malloc(sizeof(int) * matrixSize * matrixColSize[0]); + *returnSize = 0; + + // Handle the empty matrix case + if (matrixSize == 0 || matrixColSize[0] == 0) { + return result; + } + + // Initialize the boundaries of the matrix + int top = 0, bottom = matrixSize - 1, left = 0, right = matrixColSize[0] - 1; + + // Traverse the matrix in a clockwise spiral order + while (top <= bottom && left <= right) { + // Traverse the top row + for (int col = left; col <= right; col++) { + *result = matrix[top][col]; + (*returnSize)++; + result++; + } + top++; + + // Traverse the right column + for (int row = top; row <= bottom; row++) { + *result = matrix[row][right]; + (*returnSize)++; + result++; + } + right--; + + // Traverse the bottom row + if (top <= bottom) { + for (int col = right; col >= left; col--) { + *result = matrix[bottom][col]; + (*returnSize)++; + result++; + } + bottom--; + } + + // Traverse the left column + if (left <= right) { + for (int row = bottom; row >= top; row--) { + *result = matrix[row][left]; + (*returnSize)++; + result++; + } + left++; + } + } + + return result - (*returnSize); // Adjust the pointer back before returning +} diff --git a/c/0055-jump-game.c b/c/0055-jump-game.c new file mode 100644 index 000000000..327b87967 --- /dev/null +++ b/c/0055-jump-game.c @@ -0,0 +1,10 @@ +bool canJump(int* nums, int numsSize){ + int goal = numsSize - 1; + + for (int i = numsSize - 2; i >= 0; i--) { + if (i + nums[i] >= goal) { + goal = i; + } + } + return goal == 0; +} \ No newline at end of file diff --git a/c/0056-merge-intervals.c b/c/0056-merge-intervals.c new file mode 100644 index 000000000..975958c2a --- /dev/null +++ b/c/0056-merge-intervals.c @@ -0,0 +1,61 @@ +// Interval structure +struct Interval { + int start; + int end; +}; + +// Function to compare intervals for sorting +int compareIntervals(const void* a, const void* b) { + return ((struct Interval*)a)->start - ((struct Interval*)b)->start; +} + +// Function to merge intervals +int** merge(int** intervals, int intervalsSize, int* intervalsColSize, int* returnSize, int** returnColumnSizes) { + if (intervalsSize <= 1) { + *returnSize = intervalsSize; + *returnColumnSizes = intervalsColSize; + return intervals; + } + + // Create an array of Interval structures + struct Interval* sortedIntervals = (struct Interval*)malloc(sizeof(struct Interval) * intervalsSize); + for (int i = 0; i < intervalsSize; i++) { + sortedIntervals[i].start = intervals[i][0]; + sortedIntervals[i].end = intervals[i][1]; + } + + // Sort intervals based on start times + qsort(sortedIntervals, intervalsSize, sizeof(struct Interval), compareIntervals); + + // Merge intervals + struct Interval* mergedIntervals = (struct Interval*)malloc(sizeof(struct Interval) * intervalsSize); + int mergedCount = 0; + + for (int i = 0; i < intervalsSize; i++) { + if (mergedCount == 0 || mergedIntervals[mergedCount - 1].end < sortedIntervals[i].start) { + mergedIntervals[mergedCount++] = sortedIntervals[i]; + } else { + mergedIntervals[mergedCount - 1].end = mergedIntervals[mergedCount - 1].end > sortedIntervals[i].end + ? mergedIntervals[mergedCount - 1].end + : sortedIntervals[i].end; + } + } + + // Allocate memory for return arrays + int** result = (int**)malloc(sizeof(int*) * mergedCount); + *returnColumnSizes = (int*)malloc(sizeof(int) * mergedCount); + + for (int i = 0; i < mergedCount; i++) { + result[i] = (int*)malloc(sizeof(int) * 2); + result[i][0] = mergedIntervals[i].start; + result[i][1] = mergedIntervals[i].end; + (*returnColumnSizes)[i] = 2; + } + + *returnSize = mergedCount; + + free(sortedIntervals); + free(mergedIntervals); + + return result; +} diff --git a/c/0057-insert-interval.c b/c/0057-insert-interval.c new file mode 100644 index 000000000..6feb84f31 --- /dev/null +++ b/c/0057-insert-interval.c @@ -0,0 +1,46 @@ +int** insert(int** intervals, int intervalsSize, int* intervalsColSize, int* newInterval, int newIntervalSize, int* returnSize, int** returnColumnSizes) { + // Create a result array to store the merged intervals + int** result = (int**)malloc(sizeof(int*) * (intervalsSize + 1)); + *returnColumnSizes = (int*)malloc(sizeof(int) * (intervalsSize + 1)); + + int i = 0, j = 0; + + // Add intervals that end before the new interval starts + while (i < intervalsSize && intervals[i][1] < newInterval[0]) { + result[j] = (int*)malloc(sizeof(int) * 2); + result[j][0] = intervals[i][0]; + result[j][1] = intervals[i][1]; + (*returnColumnSizes)[j] = 2; + j++; + i++; + } + + // Merge overlapping intervals + while (i < intervalsSize && intervals[i][0] <= newInterval[1]) { + newInterval[0] = (newInterval[0] < intervals[i][0]) ? newInterval[0] : intervals[i][0]; + newInterval[1] = (newInterval[1] > intervals[i][1]) ? newInterval[1] : intervals[i][1]; + i++; + } + + // Add the merged interval + result[j] = (int*)malloc(sizeof(int) * 2); + result[j][0] = newInterval[0]; + result[j][1] = newInterval[1]; + (*returnColumnSizes)[j] = 2; + j++; + + // Add remaining intervals + while (i < intervalsSize) { + result[j] = (int*)malloc(sizeof(int) * 2); + result[j][0] = intervals[i][0]; + result[j][1] = intervals[i][1]; + (*returnColumnSizes)[j] = 2; + j++; + i++; + } + + // Update the return size + *returnSize = j; + + return result; +} diff --git a/c/0058-length-of-last-word.c b/c/0058-length-of-last-word.c new file mode 100644 index 000000000..740d8a2a4 --- /dev/null +++ b/c/0058-length-of-last-word.c @@ -0,0 +1,13 @@ +int lengthOfLastWord(char * s){ + int len = 0; + + if(s[0] != ' ') len = 1; + + for(int i=1; i= m || posY >= n) return 0; + + if(dp[posX][posY] != -1) return dp[posX][posY]; + + int right = ways(posX + 1, posY, m, n); // moves to right corner. + + int bottom = ways(posX, posY + 1, m, n);// moves to bottom corner. + + dp[posX][posY] = right + bottom; + + return dp[posX][posY]; +} + +int uniquePaths(int m, int n) { + + // Initializing the dp array with -1 value. + for(int i = 0; i < 105; i++){ + for(int j = 0; j < 105; j++) dp[i][j] = -1; + } + + int ans = ways(0, 0, m, n); + + return ans; +} \ No newline at end of file diff --git a/c/0066-plus-one.c b/c/0066-plus-one.c new file mode 100644 index 000000000..2f32b2269 --- /dev/null +++ b/c/0066-plus-one.c @@ -0,0 +1,44 @@ +/* + Given an integer array where the elements represent the digits of a number, + return the resulting array when we add one to it. + Ex. digits = [1,2,3] + 1 -> [1,2,4] + + The length of the array after adding 1 will either remain the same or + increase by 1. So we keep the size of the result array as n+1 during the + start. We iterate from the last digit to the first digit backwards and + keep track of the carry generated by the previous digit. + + If there is a carry left at the end, the addition caused the length to + increase, so we return the result. Else if there is no carry, the length + remained constant, so we skip the first element of result and return. + + Time: O(n) + Space: O(n) +*/ + +/** + * Note: The returned array must be malloced, assume caller calls free(). + */ +int* plusOne(int* digits, int digitsSize, int* returnSize){ + + // Reserve the result with a digitsSize+1 size array + int* result = (int*) malloc(sizeof(int)*(digitsSize+1)); + *returnSize = digitsSize+1; + result[0] = 1; + + int carry = 1; + for (int i = digitsSize; i > 0; --i) { + int sum = digits[i-1] + carry; + carry = sum/10; + sum = sum%10; + + result[i] = sum; + } + + if (carry) + return result; + + // Skip the additional element which was reserved for carry + *returnSize = digitsSize; + return result+1; +} diff --git a/c/0067-add-binary.c b/c/0067-add-binary.c new file mode 100644 index 000000000..129e23e92 --- /dev/null +++ b/c/0067-add-binary.c @@ -0,0 +1,24 @@ +char *addBinary(const char *a, const char *b) { + int maxLen = strlen(a) > strlen(b) ? strlen(a) : strlen(b); + char *res = (char *)malloc((maxLen + 2) * sizeof(char)); + memset(res, 0, (maxLen + 2) * sizeof(char)); + unsigned int carry = 0; + + for(int i = 0; i < maxLen; i++) { + unsigned int bitA = i < strlen(a) ? a[strlen(a) - i - 1] - '0' : 0; + unsigned int bitB = i < strlen(b) ? b[strlen(b) - i - 1] - '0' : 0; + + unsigned int total = bitA + bitB + carry; + char sum = '0' + total % 2; + carry = total / 2; + + // Add to the beginning of the string + memmove(res + 1, res, strlen(res)); + res[0] = sum; + } + if(carry) { + memmove(res + 1, res, strlen(res)); + res[0] = '1'; + } + return res; +} diff --git a/c/0069-sqrtx.c b/c/0069-sqrtx.c new file mode 100644 index 000000000..f7ff4b534 --- /dev/null +++ b/c/0069-sqrtx.c @@ -0,0 +1,18 @@ +int mySqrt(int x){ + int l = 0; + int r = x; + + while(l <= r){ + long int m = (l + r) / 2; + if(m * m == x){ + return m; + } + else if(m * m > x){ + r = m - 1; + } + else{ + l = m + 1; + } + } + return r; +} diff --git a/c/0070-climbing-stairs.c b/c/0070-climbing-stairs.c new file mode 100644 index 000000000..dd37d2dad --- /dev/null +++ b/c/0070-climbing-stairs.c @@ -0,0 +1,15 @@ +int climbStairs(int n){ + if (n <= 3) { + return n; + } + + int n1 = 2; + int n2 = 3; + + for (int i = 4; i <= n; i++) { + int temp = n1 + n2; + n1 = n2; + n2 = temp; + } + return n2; +} \ No newline at end of file diff --git a/c/0072-edit-distance.c b/c/0072-edit-distance.c new file mode 100644 index 000000000..4f212e6f3 --- /dev/null +++ b/c/0072-edit-distance.c @@ -0,0 +1,24 @@ +int min(int a, int b, int c) { + return a < b ? (a < c ? a : c) : (b < c ? b : c); +} + +int minDistance(char *word1, char *word2) { + int m = strlen(word1); + int n = strlen(word2); + + int dp[m + 1][n + 1]; + for (int i = 0; i <= m; ++i) { + for (int j = 0; j <= n; ++j) { + if (i == 0) + dp[i][j] = j; + else if (j == 0) + dp[i][j] = i; + else if (word1[i - 1] == word2[j - 1]) + dp[i][j] = dp[i - 1][j - 1]; + else + dp[i][j] = 1 + min(dp[i - 1][j - 1], dp[i][j - 1], dp[i - 1][j]); + } + } + + return dp[m][n]; +} diff --git a/c/0073-set-matrix-zeroes.c b/c/0073-set-matrix-zeroes.c new file mode 100644 index 000000000..3167d2020 --- /dev/null +++ b/c/0073-set-matrix-zeroes.c @@ -0,0 +1,61 @@ +/* + Given a 2d matrix if an element is 0, set it's entire row and column to 0 as + well. + Ex. matrix = [[ 1, 1, 1], [[ 1, 0, 1], + [ 1, 0, 1], -> [ 0, 0, 0], + [ 1, 1, 1]] [ 1, 0, 1]] + + An identical result matrix can be initialised and then updated according to + the zeros values in the original matrix. However, this would take O(m*n) + space, which can be improved. + + By doing the updations in the same matrix, space complexity becomes O(1). We + reserve the top most row as marker, they will signify whether the + corresponding column has a zero or not. Similarly the first column, will do + the same for the rows. + + An additional variable is needed as the m[0][0] is common in both the first + row and first column. + + Time: O(m*n) where m, n are the matrix dimensions + Space: O(1) +*/ + +void setZeroes(int** matrix, int matrixSize, int* matrixColSize){ + int ROW = matrixSize; + int COL = *matrixColSize; + bool rowZero = false; + + for (int r = 0; r < ROW; ++r) { + for (int c = 0; c < COL; ++c) { + if (matrix[r][c] == 0) { + matrix[0][c] = 0; + if (r > 0) + matrix[r][0] = 0; + else + rowZero = true; + } + } + } + + for (int r = 1; r < ROW; ++r) { + for (int c = 1; c < COL; ++c) { + if (matrix[0][c] == 0 || matrix[r][0] == 0) + matrix[r][c] = 0; + } + } + + // Set first column as zeros if matrix[0][0] is set + if (matrix[0][0] == 0) { + for (int r = 0; r < ROW; ++r) { + matrix[r][0] = 0; + } + } + + // Set first row as zeros if rowZero is true + if (rowZero) { + for (int c = 0; c < COL; ++c) { + matrix[0][c] = 0; + } + } +} \ No newline at end of file diff --git a/c/0074-search-a-2d-matrix.c b/c/0074-search-a-2d-matrix.c new file mode 100644 index 000000000..b89473f51 --- /dev/null +++ b/c/0074-search-a-2d-matrix.c @@ -0,0 +1,47 @@ +/* + Given a 2d matrix, where, + 1. the rows are sorted from left to right, + 2. the last element of each row is less than first element of next row + + Find an efficient method to search for a given element in this array + Ex. matrix = [[ 1, 3, 5, 7], + [10, 11, 16, 20], + [23, 30, 34, 60]], + target = 3 -> true (3 exists) + + The 2D matrix can be thought of as a 1D array if the rows are arranged + sequentially. And given the conditions, the 1D array will also be sorted. + So, binary search can be used to solve this problem with indexes (row, col) + rather than one. + + Time: O(log(mn)) + Space: O(1) +*/ + +bool searchMatrix(int** matrix, int matrixSize, int* matrixColSize, int target){ + int m = matrixSize; + int n = *matrixColSize; + + int low = 0; + int high = m*n - 1; + + int mid = low + (high - low)/2; + + + while (low <= high) { + int row = mid / n; + int col = mid % n; + + if (matrix[row][col] > target) + high = mid - 1; + else if (matrix[row][col] < target) + low = mid + 1; + else + return true; + mid = low + (high - low)/2; + + } + + return false; + +} diff --git a/c/0075-sort-colors.c b/c/0075-sort-colors.c new file mode 100644 index 000000000..f4b0e57ca --- /dev/null +++ b/c/0075-sort-colors.c @@ -0,0 +1,36 @@ +/* +Given an array nums with n objects colored red, white, or blue, sort +them in-place so that objects of the same color are adjacent, with +the colors in the order red, white, and blue. + +Space: O(1) +Time: O(n) (one-pass algorithm) +*/ + + +void swap(int* nums, int a, int b) { + int tmp = nums[a]; + nums[a] = nums[b]; + nums[b] = tmp; +} + +void sortColors(int* nums, int numsSize){ + int pos1 = 0; + int right = numsSize-1; + int left = 0; + // Before index (left-pos1) -> only 0 + // Between indexes (left-pos1) and left -> only 1 + // After indexe left -> only 2 + while (left<=right) { + if (nums[left]==2) { + swap(nums, left, right); + right--; + } else if (nums[left]==0) { + swap(nums, left, left-pos1); + left++; + } else { + pos1++; + left++; + } + } +} diff --git a/c/0076-minimum-window-substring.c b/c/0076-minimum-window-substring.c new file mode 100644 index 000000000..133d96503 --- /dev/null +++ b/c/0076-minimum-window-substring.c @@ -0,0 +1,51 @@ +/* +Given two strings s and t of lengths m and n respectively, return the minimum window substring of s such that every +character in t (including duplicates) is included in the window. If there is no such substring, return the empty string "". +Space: O(1) +Time: O(n+m) where n is the length of s and m the length of t +*/ + +char * minWindow(char * s, char * t){ + int alpha[59] = {0}; + int need = 0; + for (int i=0; t[i]!='\0'; i++) { // Initialise the alphabet + if (alpha[t[i]-'A']==0) + need++; + alpha[t[i]-'A']--; + } + int maxLen = 100001; + int pos=0; + int i=0, j=0; + while (s[j]!='\0') { + alpha[s[j]-'A']++; + if (alpha[s[j]-'A']==0) { + need--; + if (need==0) { + while (alpha[s[i]-'A']>0) { + alpha[s[i]-'A']--; + i++; + } + if ((j-i+1)0 && board[i-1][j]==word[1]){ + if (dfs(board, n, m, i-1, j, word+1)){ + board[i][j] = c; + return true; + } + } + if (j>0 && board[i][j-1]==word[1]){ + if (dfs(board, n, m, i, j-1, word+1)){ + board[i][j] = c; + return true; + } + } + if (i<(n-1) && board[i+1][j]==word[1]){ + if (dfs(board, n, m, i+1, j, word+1)){ + board[i][j] = c; + return true; + } + } + if (j<(m-1) && board[i][j+1]==word[1]){ + if (dfs(board, n, m, i, j+1, word+1)){ + board[i][j] = c; + return true; + } + } + board[i][j] = c; + return false; +} + + +bool exist(char** board, int boardSize, int* boardColSize, char * word){ + int m = (*boardColSize), i, j; + for (i=0; inext && cur->next->val == cur->val) { + cur->next = cur->next->next; + } + cur = cur->next; + } + return head; +} \ No newline at end of file diff --git a/c/0084-largest-rectangle-in-histogram.c b/c/0084-largest-rectangle-in-histogram.c new file mode 100644 index 000000000..4465b15f9 --- /dev/null +++ b/c/0084-largest-rectangle-in-histogram.c @@ -0,0 +1,77 @@ +const int MAX_N = 1e5 + 5; +struct Stack +{ + int top; + int capacity; + int *array; +}; + +struct Stack *createStack() +{ + struct Stack *stack = (struct Stack *)malloc(sizeof(struct Stack)); + stack->capacity = MAX_N; + stack->top = -1; + stack->array = (int *)malloc(stack->capacity * sizeof(int)); + return stack; +} + +int isFull(struct Stack *stack) +{ + return stack->top == stack->capacity - 1; +} + +int isEmpty(struct Stack *stack) +{ + return stack->top == -1; +} + +void push(struct Stack *stack, int val) +{ + if (isFull(stack)) + return; + stack->array[++stack->top] = val; +} + +void pop(struct Stack *stack) +{ + if (isEmpty(stack)) + return; + --stack->top; +} + +int peek(struct Stack *stack) +{ + if (isEmpty(stack)) + return INT_MIN; + return stack->array[stack->top]; +} + +int max(int num1, int num2) +{ + return (num1 > num2) ? num1 : num2; +} + +int largestRectangleArea(int *heights, int heightsSize) +{ + int ans = 0; + struct Stack *st = createStack(); + for (int i = 0; i < heightsSize; i++) + { + while (!isEmpty(st) && heights[peek(st)] > heights[i]) + { + int tp = peek(st); + pop(st); + int dist = (isEmpty(st) ? i : i - peek(st) - 1); + ans = max(ans, dist * heights[tp]); + } + push(st, i); + } + while (!isEmpty(st)) + { + int tp = peek(st); + pop(st); + int dist = (isEmpty(st) ? heightsSize : heightsSize - peek(st) - 1); + ans = max(ans, dist * heights[tp]); + } + return ans; +} diff --git a/c/0088-merge-sorted-array.c b/c/0088-merge-sorted-array.c new file mode 100644 index 000000000..cec328b08 --- /dev/null +++ b/c/0088-merge-sorted-array.c @@ -0,0 +1,27 @@ +/* +Merge nums1 and nums2 into a single array sorted in non-decreasing order. + +Space: O(1) +Time: O(n+m) +*/ + +void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){ + int i=m-1; + int j=n-1; + while (i>=0 || j>=0) { + if (j<0){ // Only numbers from nums1 remain + return; + } else if (i<0) { // Only numbers from nums2 remain + nums1[j] = nums2[j]; + j--; + } else { + if (nums1[i]>nums2[j]) { + nums1[i+j+1] = nums1[i]; + i--; + } else { + nums1[i+j+1] = nums2[j]; + j--; + } + } + } +} diff --git a/c/0090-subsets-ii.c b/c/0090-subsets-ii.c new file mode 100644 index 000000000..78b37edce --- /dev/null +++ b/c/0090-subsets-ii.c @@ -0,0 +1,40 @@ +void backtrack(int* nums, int numsSize, int start, int* subset, int subsetSize, int*** result, int* resultSize, int** resultColSizes) { + // Add the current subset to the result set + *result = (int**)realloc(*result, (*resultSize + 1) * sizeof(int*)); + (*result)[*resultSize] = (int*)malloc(subsetSize * sizeof(int)); + for (int i = 0; i < subsetSize; i++) { + (*result)[*resultSize][i] = subset[i]; + } + (*resultColSizes) = (int*)realloc(*resultColSizes, (*resultSize + 1) * sizeof(int)); + (*resultColSizes)[*resultSize] = subsetSize; + (*resultSize)++; + + // Backtracking + for (int i = start; i < numsSize; i++) { + // Skip duplicates to avoid duplicates in subsets + if (i > start && nums[i] == nums[i - 1]) { + continue; + } + + subset[subsetSize] = nums[i]; + backtrack(nums, numsSize, i + 1, subset, subsetSize + 1, result, resultSize, resultColSizes); + } +} + +int compare(const void* a, const void* b) { + return *(int*)a - *(int*)b; +} + +int** subsetsWithDup(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) { + qsort(nums, numsSize, sizeof(int), compare); + int** result = NULL; + int* resultColSizes = NULL; + int* subset = (int*)malloc(numsSize * sizeof(int)); + *returnSize = 0; + + backtrack(nums, numsSize, 0, subset, 0, &result, returnSize, &resultColSizes); + + *returnColumnSizes = resultColSizes; + free(subset); + return result; +} diff --git a/c/0091-decode-ways.c b/c/0091-decode-ways.c new file mode 100644 index 000000000..c04c2cae2 --- /dev/null +++ b/c/0091-decode-ways.c @@ -0,0 +1,29 @@ +/* +Given a string s containing only digits, return the number +of ways to decode it. + +Space: O(n) +Time: O(n) +*/ + +int numDecodings(char * s){ + int n = strlen(s); + if (n==1) + return s[0]!='0'; // if s is '0' then it's invalid + int* dp = malloc(sizeof(int)*(n+1)); + dp[n] = 1; // Initialise the empty string + dp[n-1] = s[n-1]=='0'?0:1; // If it begins with a '0' it's invalid + for (int i=n-2; i>=0; i--) { + if (s[i]=='0') { // Begin with a zero + dp[i] = 0; + } else if (s[i]=='1' || s[i]=='2') { + if (s[i]=='2' && s[i+1]>='7') + dp[i] = dp[i+2]; // Only one way to decode + else + dp[i] = dp[i+1] + dp[i+2]; // Two way to decode + } else { + dp[i] = dp[i+1]; // One way to decode + } + } + return dp[0]; +} diff --git a/c/0094-binary-tree-inorder-traversal.c b/c/0094-binary-tree-inorder-traversal.c new file mode 100644 index 000000000..8aa0a36ae --- /dev/null +++ b/c/0094-binary-tree-inorder-traversal.c @@ -0,0 +1,23 @@ +/* +Given the root of a binary tree, return the inorder traversal of its nodes' values. + +Space: O(n) +Time: O(n) +*/ + +void fill_array(struct TreeNode* root, int* ans, int* pos) { + // Fill the array with the inorder traversal order + if (root==NULL) + return; + fill_array(root->left, ans, pos); + ans[*pos] = root->val; + *pos = *pos +1; + fill_array(root->right, ans, pos); +} + +int* inorderTraversal(struct TreeNode* root, int* returnSize){ + int* ans = malloc(sizeof(int)*101); // 101 as a length as we don't know the max and its inferior of 100 + *returnSize = 0; // Current position in the array + fill_array(root, ans, returnSize); + return ans; +} diff --git a/c/0096-unique-binary-search-trees.c b/c/0096-unique-binary-search-trees.c new file mode 100644 index 000000000..be86d3978 --- /dev/null +++ b/c/0096-unique-binary-search-trees.c @@ -0,0 +1,20 @@ +/* +Given an integer n, return the number of structurally unique BST's +(binary search trees) which has exactly n nodes of unique values from 1 to n. + +Space: O(n) +Time: O(n²) +*/ + +int numTrees(int n){ + int* dp = (int*)malloc(sizeof(int)*(n+1)); + dp[0]=dp[1]=1; + for (int i=2; i<(n+1); i++){ + dp[i] = 0; + for (int j=1; j<(i+1); j++){ + dp[i]+=dp[j-1]*dp[i-j]; + } + } + // We could free dp + return dp[n]; +} diff --git a/c/0097-interleaving-string.c b/c/0097-interleaving-string.c new file mode 100644 index 000000000..a4f157582 --- /dev/null +++ b/c/0097-interleaving-string.c @@ -0,0 +1,33 @@ +bool isInterleave(char *s1, char *s2, char *s3) { + int len1 = strlen(s1); + int len2 = strlen(s2); + int len3 = strlen(s3); + + // If the lengths of the input strings don't add up correctly, return false + if (len1 + len2 != len3) { + return false; + } + + // Create a 2D DP array to store the results of subproblems + bool dp[len1 + 1][len2 + 1]; + memset(dp, false, sizeof(dp)); + + // Base case: empty strings can always interleave to form an empty string + dp[0][0] = true; + + // Fill in the DP array + for (int i = 0; i <= len1; i++) { + for (int j = 0; j <= len2; j++) { + // If s1 matches the interleaved portion of s3 + if (i > 0 && s1[i - 1] == s3[i + j - 1]) { + dp[i][j] = dp[i][j] || dp[i - 1][j]; + } + // If s2 matches the interleaved portion of s3 + if (j > 0 && s2[j - 1] == s3[i + j - 1]) { + dp[i][j] = dp[i][j] || dp[i][j - 1]; + } + } + } + + return dp[len1][len2]; +} diff --git a/c/0098-validate-binary-search-tree.c b/c/0098-validate-binary-search-tree.c new file mode 100644 index 000000000..fd8bbc13e --- /dev/null +++ b/c/0098-validate-binary-search-tree.c @@ -0,0 +1,18 @@ +/* +Given the root of a binary tree, determine if it is a valid binary search tree (BST). +Time: O(n) +Space: O(1) +*/ + +#define MAX 9223372036854775807 +#define MIN -9223372036854775808 + +bool isSubTreeValid(struct TreeNode* root, long int lowerBound, long int upperBound) { + if (root==NULL) + return true; + return root->valval>lowerBound && isSubTreeValid(root->left, lowerBound, root->val) && isSubTreeValid(root->right, root->val, upperBound); +} + +bool isValidBST(struct TreeNode* root){ + return isSubTreeValid(root, MIN, MAX); +} diff --git a/c/0100-same-tree.c b/c/0100-same-tree.c new file mode 100644 index 000000000..a803c8921 --- /dev/null +++ b/c/0100-same-tree.c @@ -0,0 +1,20 @@ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * struct TreeNode *left; + * struct TreeNode *right; + * }; + */ + + +bool isSameTree(struct TreeNode* p, struct TreeNode* q){ + if (p == NULL && q == NULL) { + return true; + } + if ((p == NULL || q == NULL) || (p -> val != q -> val)) { + return false; + } + return (isSameTree(p -> left, q -> left) && isSameTree(p -> right, q -> right)); +} \ No newline at end of file diff --git a/c/0102-binary-tree-level-order-traversal.c b/c/0102-binary-tree-level-order-traversal.c new file mode 100644 index 000000000..edc2c2588 --- /dev/null +++ b/c/0102-binary-tree-level-order-traversal.c @@ -0,0 +1,166 @@ +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * struct TreeNode *left; + * struct TreeNode *right; + * }; + */ + +#define ARRAY_ALLOCATION_SIZE 500 +typedef struct TreeNode TreeNode; + +// Circular queue with dynamic size +typedef struct Queue +{ + TreeNode** treeNodeArray; + int treeNodeArraySize; // Allocated size of the treeNodeArray + int treeNodeArrayUsed; // Number of used space in the treeNodeArray + int front; + int back; +}Queue; + +void queueInit(Queue* queue) +{ + queue->treeNodeArray = (TreeNode**)malloc(ARRAY_ALLOCATION_SIZE * sizeof(TreeNode*)); + queue->treeNodeArraySize = ARRAY_ALLOCATION_SIZE; + queue->treeNodeArrayUsed = 0; + queue->front = -1; + queue->back = -1; +} + +int isQueueEmpty(Queue* queue) +{ + if(queue->treeNodeArrayUsed == 0) + { + return true; + } + + return false; +} + +void queuePush(Queue* queue, TreeNode* node) +{ + // Check if space reallocation is needed + if(queue->treeNodeArrayUsed == queue->treeNodeArraySize) + { + // Reallocate bigger space for the array + queue->treeNodeArraySize += ARRAY_ALLOCATION_SIZE; // Increase the array size + queue->treeNodeArray = (TreeNode**)realloc(queue->treeNodeArray, queue->treeNodeArraySize * sizeof(TreeNode*)); + + // If the front passed the back, we need to move any element statring of the front to the end of the array + if(queue->front >= queue->back) + { + int oldArraySize = (queue->treeNodeArraySize - ARRAY_ALLOCATION_SIZE); + // Calculate how many elements we need to move, starting from the from til the array end (with old array size before reallocation) + int elementsToMoveCount = oldArraySize - queue->front; + + for(int i = 1; i < elementsToMoveCount; i++) + { + printf("%d, %d\n", i, elementsToMoveCount); + queue->treeNodeArray[queue->treeNodeArraySize - i] = queue->treeNodeArray[--oldArraySize]; + } + + // Set the new front position + queue->front = queue->treeNodeArraySize - elementsToMoveCount; + } + } + + // If back is at the end of the array, we circle back to beginning of the array + if(++queue->back == queue->treeNodeArraySize) + { + queue->back = 0; + } + + // Add node at the back + queue->treeNodeArray[queue->back] = node; + + // Increment the treeNodeArrayUsed counter + queue->treeNodeArrayUsed++; +} + +TreeNode* queuePop(Queue* queue) +{ + // Make sure the queue is not empty + if(!isQueueEmpty(queue)) + { + // Decrement the treeNodeArrayUsed counter + queue->treeNodeArrayUsed--; + + // If front is at the end of the array, we circle back to beginning of the array + if(++queue->front == queue->treeNodeArraySize) + { + queue->front = 0; + } + + return queue->treeNodeArray[queue->front]; + } + + return NULL; +} + + +/** + * Return an array of arrays of size *returnSize. + * The sizes of the arrays are returned as *returnColumnSizes array. + * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free(). + */ +int** levelOrder(struct TreeNode* root, int* returnSize, int** returnColumnSizes){ + int resultAllocatedSize = ARRAY_ALLOCATION_SIZE; + int** result = (int**)malloc(resultAllocatedSize * sizeof(int*)); + int resultIndex = 0; + Queue queue; + + // Initialize to 0 + *returnSize = 0; + *returnColumnSizes = (int*)malloc(resultAllocatedSize * sizeof(int)); + + + if(!root) + { + return result; + } + + queueInit(&queue); + // Initialize the queue with the root node + queuePush(&queue, root); + + while(!isQueueEmpty(&queue)) + { + int levelLen = queue.treeNodeArrayUsed; + int* level = (int*)malloc(levelLen * sizeof(int)); + int levelIndex = 0; + + for(int i = 0; i < levelLen; i++) + { + TreeNode* poppedNode = queuePop(&queue); + + level[levelIndex++] = poppedNode->val; + if(poppedNode->left) + { + queuePush(&queue, poppedNode->left); + } + + if(poppedNode->right) + { + queuePush(&queue, poppedNode->right); + } + } + + // Check if result array has enough space + if(resultIndex + 1 == resultAllocatedSize) + { + resultAllocatedSize += ARRAY_ALLOCATION_SIZE; + + result = (int**)realloc(result, resultAllocatedSize * sizeof(int*)); + *returnColumnSizes = (int*)realloc(*returnColumnSizes, resultAllocatedSize * sizeof(int)); + } + + result[resultIndex] = level; + (*returnColumnSizes)[resultIndex] = levelLen; + resultIndex++; + (*returnSize)++; + } + + return result; +} diff --git a/c/0104-maximum-depth-of-binary-tree.c b/c/0104-maximum-depth-of-binary-tree.c new file mode 100644 index 000000000..ee46b64a9 --- /dev/null +++ b/c/0104-maximum-depth-of-binary-tree.c @@ -0,0 +1,28 @@ +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * struct TreeNode *left; + * struct TreeNode *right; + * }; + */ + +int max(int a, int b) { + if (a > b) { + return a; + } + return b; +} + + +int maxDepth(struct TreeNode* root) { + + if (root == NULL) { + return 0; + } + return max( + maxDepth(root -> left), + maxDepth(root -> right) + ) + 1; +} + diff --git a/c/0105-construct-binary-tree-from-preorder-and-inorder-traversal.c b/c/0105-construct-binary-tree-from-preorder-and-inorder-traversal.c new file mode 100644 index 000000000..cf3ed3fea --- /dev/null +++ b/c/0105-construct-binary-tree-from-preorder-and-inorder-traversal.c @@ -0,0 +1,43 @@ +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * struct TreeNode *left; + * struct TreeNode *right; + * }; + */ +typedef struct TreeNode TreeNode; + +TreeNode* build(int* preorder, int* inorder, int* rootIndex, int inorderLeft, int inorderRight) +{ + if(inorderLeft >= inorderRight) + { + return NULL; + } + + TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode)); + root->val = preorder[*rootIndex]; + int mid = 0; + + // Finding mid + for(size_t i = inorderLeft; i < inorderRight; i++) + { + if(inorder[i] == preorder[*rootIndex]) + { + mid = i; + break; + } + } + + (*rootIndex)++; + + root->left = build(preorder, inorder, rootIndex, inorderLeft, mid); + root->right = build(preorder, inorder, rootIndex, mid + 1, inorderRight); + + return root; +} + +struct TreeNode* buildTree(int* preorder, int preorderSize, int* inorder, int inorderSize){ + int rootIndex = 0; + return build(preorder, inorder, &rootIndex, 0, inorderSize); +} diff --git a/c/0108-convert-sorted-array-to-binary-search-tree.c b/c/0108-convert-sorted-array-to-binary-search-tree.c new file mode 100644 index 000000000..769e5ca9f --- /dev/null +++ b/c/0108-convert-sorted-array-to-binary-search-tree.c @@ -0,0 +1,22 @@ +/* +Given an integer array nums where the elements are sorted in +ascending order, convert it to a height-balanced binary search tree. + +Space: O(n) +Time: O(n) +*/ + +struct TreeNode* dichomoty_rec(int* nums, int i, int j) { + if (i>j) + return NULL; + struct TreeNode* new_t = malloc(sizeof(struct TreeNode)); + int m = (i+j)/2; + new_t->val = nums[m]; + new_t->left = dichomoty_rec(nums, i, m-1); + new_t->right = dichomoty_rec(nums, m+1, j); + return new_t; +} + +struct TreeNode* sortedArrayToBST(int* nums, int numsSize){ + return dichomoty_rec(nums, 0, numsSize-1); +} diff --git a/c/0110-balanced-binary-tree.c b/c/0110-balanced-binary-tree.c new file mode 100644 index 000000000..fe03a00fb --- /dev/null +++ b/c/0110-balanced-binary-tree.c @@ -0,0 +1,34 @@ +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * struct TreeNode *left; + * struct TreeNode *right; + * }; + */ +int max(int a, int b) { + return a > b ? a : b; +} + +int dfs(struct TreeNode* root) { + if (!root) { + return 0; + } + + int depth_left = dfs(root->left); + int depth_right = dfs(root->right); + + if (depth_left == -1 || depth_right == -1 || abs(depth_left - depth_right) > 1) { + return -1; + } + + return max(depth_left, depth_right) + 1; +} + +bool isBalanced(struct TreeNode* root) { + if (dfs(root) != -1) { + return true; + } else { + return false; + } +} \ No newline at end of file diff --git a/c/0112-path-sum.c b/c/0112-path-sum.c new file mode 100644 index 000000000..f94aa9f8d --- /dev/null +++ b/c/0112-path-sum.c @@ -0,0 +1,16 @@ +/* +Given the root of a binary tree and an integer targetSum, return true +if the tree has a root-to-leaf path such that adding up all the values +along the path equals targetSum. + +Space: O(log(n)) (due to recursive calls) +Time: O(n) +*/ + +bool hasPathSum(struct TreeNode* root, int targetSum){ + if (!root) + return false; + if (!root->left && !root->right) + return root->val == targetSum; + return hasPathSum(root->left, targetSum - root->val) || hasPathSum(root->right, targetSum - root->val); +} diff --git a/c/0115-distinct-subsequences.c b/c/0115-distinct-subsequences.c new file mode 100644 index 000000000..2cb5e86aa --- /dev/null +++ b/c/0115-distinct-subsequences.c @@ -0,0 +1,22 @@ +int numDistinct(char *s, char *t) { + int m = strlen(s); + int n = strlen(t); + + uint64_t dp[m + 1][n + 1]; + memset(dp, 0, sizeof(dp)); + + for (int i = 0; i <= m; ++i) + dp[i][0] = 1; + + for (int i = 1; i <= m; ++i) { + for (int j = 1; j <= n; ++j) { + if (s[i - 1] == t[j - 1]) { + dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]; + } else { + dp[i][j] = dp[i - 1][j]; + } + } + } + + return (int)dp[m][n]; +} diff --git a/c/0118-pascals-triangle.c b/c/0118-pascals-triangle.c new file mode 100644 index 000000000..4ab56a62d --- /dev/null +++ b/c/0118-pascals-triangle.c @@ -0,0 +1,24 @@ +/* +Given an integer numRows, return the first numRows of Pascal's triangle. + +Space: O(n²) (n=numRows) +Time: O(n²) +*/ + +int** generate(int numRows, int* returnSize, int** returnColumnSizes){ + *returnSize = numRows; + (*returnColumnSizes) = malloc(sizeof(int*)*numRows); + int** ans = malloc(sizeof(int*)*numRows); + for (int i=0; i= 0; i--) { + for (int j = 0; j <= i; j++) { + triangle[i][j] += min(triangle[i + 1][j], triangle[i + 1][j + 1]); + } + } + return triangle[0][0]; +} diff --git a/c/0121-best-time-to-buy-and-sell-stock.c b/c/0121-best-time-to-buy-and-sell-stock.c new file mode 100644 index 000000000..171bf740e --- /dev/null +++ b/c/0121-best-time-to-buy-and-sell-stock.c @@ -0,0 +1,30 @@ + + +int min(int a, int b) { + + if (a > b) { + return b; + } + return a; +} + +int max(int a, int b) { + + if (a > b) { + return a; + } + return b; +} + +int maxProfit(int* prices, int pricesSize){ + + int min_price = prices[0]; + int max_profits = 0; + + for (int i = 0; i < pricesSize; i++) { + min_price = min(min_price, prices[i]); + max_profits = max(prices[i] - min_price, max_profits); + } + + return max_profits; +} \ No newline at end of file diff --git a/c/0122-best-time-to-buy-and-sell-stock-ii.c b/c/0122-best-time-to-buy-and-sell-stock-ii.c new file mode 100644 index 000000000..2d57ae455 --- /dev/null +++ b/c/0122-best-time-to-buy-and-sell-stock-ii.c @@ -0,0 +1,15 @@ +/* +You are given an integer array prices where prices[i] is the price of a given stock on the ith day. +Find and return the maximum profit you can achieve. + +Time: O(n) +Space: O(1) +*/ + +int maxProfit(int* prices, int pricesSize){ + int profit = 0; // All profit made + for (int i=1; ib?a:b; +} + +int rec(struct TreeNode* t, int* m) { // Return the maximum path sum which uses t->Val + if (t==NULL) + return -1001; + int r = rec(t->right, m); + int l = rec(t->left, m); + if (t->val>0) + *m = max(*m, t->val+max(l+r, max(l, r))); + else + *m = max(*m, max(r, max(l, l+r+t->val))); + return max(0, max(l, r))+t->val; +} + +int maxPathSum(struct TreeNode* root){ + int m = root->val; + int c = rec(root, &m); + return max(c, m); +} diff --git a/c/0125-valid-palindrome.c b/c/0125-valid-palindrome.c new file mode 100644 index 000000000..e87015add --- /dev/null +++ b/c/0125-valid-palindrome.c @@ -0,0 +1,30 @@ +/* + Time: O(n) + Space: O(1) +*/ + +bool isPalindrome(char * s){ + int n = strlen(s); + if (n==1) return true; + + int left = 0; + int right = n-1; + + while (left < right) { + if (isalnum(s[left]) && isalnum(s[right])) { + char leftChar = tolower(s[left]); + char rightChar = tolower(s[right]); + + if (leftChar != rightChar) { + return false; + } + left++; + right--; + } + + if (!isalnum(s[left])) left++; + if (!isalnum(s[right])) right--; + } + + return true; +} \ No newline at end of file diff --git a/c/0128-longest-consecutive-sequence.c b/c/0128-longest-consecutive-sequence.c new file mode 100644 index 000000000..a750ba4b5 --- /dev/null +++ b/c/0128-longest-consecutive-sequence.c @@ -0,0 +1,158 @@ +#define INIT_HASH_SIZE 4096 + +// Represent an element in a hash table. +typedef struct Hash { + int key; + struct Hash *next; +} Hash; + +Hash **InitHash() { + Hash **h = (Hash**)calloc(INIT_HASH_SIZE, sizeof(Hash*)); + assert(h); + return h; +} + +Hash *NewHash(int key) { + Hash *h = (Hash*)malloc(sizeof(Hash)); + assert(h); + h->key = key; + h->next = NULL; + return h; +} + +int HashKey(int key) { + while(key < 0) key += INIT_HASH_SIZE; + return (key % INIT_HASH_SIZE); +} + +void AddHash(Hash **hash, int key) { + assert(hash); + int hash_key = HashKey(key); + Hash **head = hash + hash_key; + if (!*head) { + *head = NewHash(key); + } else { + Hash *h = *head; + while (h) { + if (h->key == key) { + return; + } else if (!h->next) { + h->next = NewHash(key); + return; + } + h = h->next; + } + } +} + +// If hash not round return 0 on purpose. +int GetHash(Hash **hash, int key) { + assert(hash); + Hash * h = hash[HashKey(key)]; + while(h) { + if (h->key == key) { + return 1; + } + h = h->next; + } + + return 0; +} + +int longestConsecutive(int* nums, int numsSize){ + Hash **hash = InitHash(); + int i, len, n, maxLen = 0; + + for (i = 0; i < numsSize; i++) { + AddHash(hash, nums[i]); + } + for (i = 0; i < numsSize; i++) { + n = nums[i]; + len = 1; + if (GetHash(hash, n - 1) == 0) { + while(GetHash(hash, ++n) == 1) { + len++; + } + } + maxLen = (len > maxLen) ? len : maxLen; + } + + return maxLen; +} + +// +// According to the official solution, using hash map will be considered as O(n). +// +// Alternative solution 1 - With uthash, the runtime is poorer. It is a little surprise. +// +// typedef struct Hash { +// int num; +// UT_hash_handle hh; +// } Hash; +// +// void AddHash(Hash **hash, int num) { +// Hash *entry; +// HASH_FIND_INT(*hash, &num, entry); +// if (!entry) { +// entry = malloc(sizeof(Hash)); +// entry->num = num; +// HASH_ADD_INT(*hash, num, entry); +// } +// } +// +// Hash * GetHash(Hash **hash, int num) { +// Hash *entry = NULL; +// HASH_FIND_INT(*hash, &num, entry); +// return entry; +// } +// +// int longestConsecutive(int* nums, int numsSize){ +// Hash *root = NULL; +// int i, len, n, maxLen = 0; +// +// for (i = 0; i < numsSize; i++) { +// AddHash(&root, nums[i]); +// } +// for (i = 0; i < numsSize; i++) { +// n = nums[i]; +// len = 1; +// if (!GetHash(&root, n - 1)) { +// while(GetHash(&root, ++n)) { +// len++; +// } +// } +// maxLen = (len > maxLen) ? len : maxLen; +// } +// +// return maxLen; +// } +// +// +// Alternative solution 2 - With sorting, O(nlogn), the runtime is the best. +// +// int compare(const void *a, const void *b) { +// return *(int*)a - *(int*)b; +// } +// +// int longestConsecutive(int* nums, int numsSize){ +// int i, len, max; +// if (numsSize <= 0) { +// return 0; +// } +// len = 1; +// max = 1; +// qsort(nums, numsSize, sizeof(int), compare); +// for (i = 0; i < numsSize - 1; i++) { +// if (nums[i] + 1 == nums[i + 1]) { +// len++; +// } else if (nums[i] == nums[i + 1]){ +// continue; +// } else { +// len = 1; +// } +// max = (max > len) ? max : len; +// } +// +// return max; +// } +// diff --git a/c/0129-sum-root-to-leaf-numbers.c b/c/0129-sum-root-to-leaf-numbers.c new file mode 100644 index 000000000..6230f0f79 --- /dev/null +++ b/c/0129-sum-root-to-leaf-numbers.c @@ -0,0 +1,22 @@ +/* +Return the total sum of all root-to-leaf numbers. + +Space: O(1) +Time: O(n) +*/ + +int dfs(struct TreeNode* r, int acc) { + if (r==NULL) + return acc; + if (r->left==NULL && r->right==NULL) + return acc*10 + r->val; + if (r->left==NULL) + return dfs(r->right, acc*10 + r->val); + if (r->right==NULL) + return dfs(r->left, acc*10 + r->val); + return dfs(r->right, acc*10 + r->val) + dfs(r->left, acc*10 + r->val); +} + +int sumNumbers(struct TreeNode* root){ + return dfs(root, 0); +} diff --git a/c/0130-surrounded-regions.c b/c/0130-surrounded-regions.c new file mode 100644 index 000000000..cac33df3a --- /dev/null +++ b/c/0130-surrounded-regions.c @@ -0,0 +1,48 @@ +/* +Given an m x n matrix board containing 'X' and 'O', capture all regions that are 4-directionally surrounded by 'X'. +Time: O(n^2) +Space: O(1) +*/ + + +void dfsModifyBorder(char** board, int n, int m, int i, int j) { + if (board[i][j]=='X') + return; + board[i][j]='T'; + if (i>0 && board[i-1][j]=='O') + dfsModifyBorder(board, n, m, i-1, j); + if (j>0 && board[i][j-1]=='O') + dfsModifyBorder(board, n, m, i, j-1); + if (i<(n-1) && board[i+1][j]=='O') + dfsModifyBorder(board, n, m, i+1, j); + if (j<(m-1) && board[i][j+1]=='O') + dfsModifyBorder(board, n, m, i, j+1); +} + +void reModifyBorder(char** board, int n, int m) { + int i, j; + for (i=0; i val = val; + node -> numNeighbors = numNeighbors; + if (numNeighbors > 0) { + node -> neighbors = (struct Node**)malloc(numNeighbors * sizeof(struct Node*)); + } else { + node -> neighbors = NULL; + } + return node; +} + +struct Node* clone_node(struct Node* s, struct Node** hashset) { + + if (s == NULL) return s; + if (hashset[(s -> val) - 1] != NULL) return hashset[(s -> val) - 1]; + + struct Node* new_node = create_node(s -> val, s -> numNeighbors); + hashset[(s -> val) - 1] = new_node; + for (int i = 0; i < s -> numNeighbors; i++) { + new_node -> neighbors[i] = clone_node(s -> neighbors[i], hashset); + } + return new_node; +} + +struct Node *cloneGraph(struct Node *s) { + + struct Node** hashset = (struct Node**)malloc(100 * sizeof(struct Node*)); + for (int i = 0; i < 100; i++) { + hashset[i] = NULL; + } + struct Node* s_clone = clone_node(s, hashset); + free(hashset); + return s_clone; +} \ No newline at end of file diff --git a/c/0134-gas-station.c b/c/0134-gas-station.c new file mode 100644 index 000000000..f62c30621 --- /dev/null +++ b/c/0134-gas-station.c @@ -0,0 +1,25 @@ +int canCompleteCircuit(int* gas, int gasSize, int* cost, int costSize){ + // Find the totalGas and totalCost + int totalGas = 0; + int totalCost = 0; + for (int i = 0; i < gasSize; i++) { + totalGas += gas[i]; + totalCost += cost[i]; + } + + // If totalCost is more than totalGas, it is not possible to complete circuit + if (totalGas < totalCost) { + return -1; + } + + int total = 0; + int result = 0; + for (int i = 0; i < gasSize; i++) { + total += gas[i] - cost[i]; + if (total < 0) { + total = 0; + result = i + 1; + } + } + return result; +} \ No newline at end of file diff --git a/c/0135-candy.c b/c/0135-candy.c new file mode 100644 index 000000000..3c4f2b11e --- /dev/null +++ b/c/0135-candy.c @@ -0,0 +1,32 @@ +int candy(int* ratings, int ratingsSize) { + int* candies = (int*)malloc(sizeof(int) * ratingsSize); + + // Initialize all children with 1 candy + for (int i = 0; i < ratingsSize; i++) { + candies[i] = 1; + } + + // First pass: from left to right + for (int i = 1; i < ratingsSize; i++) { + if (ratings[i] > ratings[i - 1]) { + candies[i] = candies[i - 1] + 1; + } + } + + // Second pass: from right to left + for (int i = ratingsSize - 2; i >= 0; i--) { + if (ratings[i] > ratings[i + 1] && candies[i] <= candies[i + 1]) { + candies[i] = candies[i + 1] + 1; + } + } + + // Calculate the total number of candies + int totalCandies = 0; + for (int i = 0; i < ratingsSize; i++) { + totalCandies += candies[i]; + } + + free(candies); + + return totalCandies; +} diff --git a/c/0136-single-number.c b/c/0136-single-number.c new file mode 100644 index 000000000..6e9dd99cb --- /dev/null +++ b/c/0136-single-number.c @@ -0,0 +1,7 @@ +int singleNumber(int* nums, int numsSize){ + int res = 0; + for (int i = 0; i < numsSize; i++) { + res = nums[i] ^ res; + } + return res; +} \ No newline at end of file diff --git a/c/0138-copy-list-with-random-pointer.c b/c/0138-copy-list-with-random-pointer.c new file mode 100644 index 000000000..d6229ae07 --- /dev/null +++ b/c/0138-copy-list-with-random-pointer.c @@ -0,0 +1,89 @@ +/** + * Definition for a Node. + * struct Node { + * int val; + * struct Node *next; + * struct Node *random; + * }; + */ + +/* + Time: O(n) + Space: O(1) +*/ + +typedef struct Node Node; +struct Node* copyRandomList(struct Node* head) { + + if(head == NULL) + { + return NULL; + } + + /** + * Insert each new node after each original node + */ + Node* curr = head; + while(curr) + { + // Create the new node + Node* newNode = (Node*)malloc(sizeof(Node)); + newNode->val = curr->val; + + // Insert new node after the original node + newNode->next = curr->next; + curr->next = newNode; + + // Move curr to the next original node + curr = curr->next->next; + } + + + /** + * Add the random node for each new node + */ + curr = head; + Node* newNode = NULL; + while(curr) + { + // The new node is the next node to the original node + newNode = curr->next; + + if(curr->random) + { + newNode->random = curr->random->next; + } + else + { + newNode->random = NULL; + } + + // Move curr to the next original node + curr = curr->next->next; + } + + /** + * Separate the original nodes list from the new nodes list + */ + Node* originalList = head; + Node* copiedList = head->next; + Node* copiedListHead = head->next; + while(originalList) + { + originalList->next = originalList->next->next; + if(copiedList->next) + { + copiedList->next = copiedList->next->next; + } + else + { + copiedList->next = NULL; + } + + originalList = originalList->next; + copiedList = copiedList->next; + } + + + return copiedListHead; +} diff --git a/c/0141-linked-list-cycle.c b/c/0141-linked-list-cycle.c new file mode 100644 index 000000000..1a1356571 --- /dev/null +++ b/c/0141-linked-list-cycle.c @@ -0,0 +1,35 @@ + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * struct ListNode *next; + * }; + */ + +bool Traverse(struct ListNode* slow, struct ListNode* fast) { + + if (slow == NULL || slow -> next == NULL) { + return false; + } + + if (fast == NULL || fast -> next == NULL) { + return false; + } + + if (slow == fast) { + return true; + } + return Traverse(slow -> next, fast -> next -> next); +} + + +bool hasCycle(struct ListNode *head) { + + if (head == NULL) { + return false; + } + + return Traverse(head, head -> next); + +} \ No newline at end of file diff --git a/c/0142-linked-list-cycle-ii.c b/c/0142-linked-list-cycle-ii.c new file mode 100644 index 000000000..5bcc4ab7a --- /dev/null +++ b/c/0142-linked-list-cycle-ii.c @@ -0,0 +1,59 @@ +/** + * Given the head of a linked list, return the node where the cycle begins. If + * there is no cycle, return null. + * + * There is a cycle in a linked list if there is some node in the list that can + * be reached again by continuously following the next pointer. Internally, pos + * is used to denote the index of the node that tail's next pointer is connected + * to (0-indexed). It is -1 if there is no cycle. Note that pos is not passed as + * a parameter. + * + * Do not modify the linked list. + * + * Constraints: + * + * The number of the nodes in the list is in the range [0, 104]. + * -105 <= Node.val <= 105 + * pos is -1 or a valid index in the linked-list. + * + * Definition for singly-linked list. + * struct ListNode { + * int val; + * struct ListNode *next; + * }; + * + * Space: O(1) + * Time: O(n) + */ + +struct ListNode *detectCycle(struct ListNode *head) { + struct ListNode *fast = head; + struct ListNode *slow = head; + + if (!head) + return NULL; + + while (fast->next && fast->next->next) { + fast = fast->next->next; + slow = slow->next; + + if (fast == slow) { + /** + * The index of the node cycle is located the same number of nodes + * away from the start of the linked list and the intersection of + * the slow and fast pointers. + */ + struct ListNode *head_node = head; + struct ListNode *intersection = slow; + + while (head_node != intersection) { + head_node = head_node->next; + intersection = intersection->next; + } + + return intersection; + } + } + + return NULL; +} diff --git a/c/0143-reorder-list.c b/c/0143-reorder-list.c new file mode 100644 index 000000000..219bca75e --- /dev/null +++ b/c/0143-reorder-list.c @@ -0,0 +1,61 @@ +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * struct ListNode *next; + * }; + */ + +struct ListNode* reverse(struct ListNode* head) { + struct ListNode* prev = NULL; + struct ListNode* curr = head; + struct ListNode* next = curr->next; + + while (curr != NULL) { + next = curr->next; + curr->next = prev; + prev = curr; + curr = next; + } + + return prev; +} + +void merge(struct ListNode* l1, struct ListNode* l2) { + while (l1 != NULL) { + struct ListNode* p1 = l1->next; + struct ListNode* p2 = l2->next; + + l1->next = l2; + if (p1 == NULL) { + break; + } + l2->next = p1; + + l1 = p1; + l2 = p2; + } +} + +void reorderList(struct ListNode* head){ + if (head->next == NULL) { + return; + } + + struct ListNode* prev = NULL; + struct ListNode* slow = head; + struct ListNode* fast = head; + + while (fast != NULL && fast->next != NULL) { + prev = slow; + slow = slow->next; + fast = fast->next->next; + } + + prev->next = NULL; + + struct ListNode* l1 = head; + struct ListNode* l2 = reverse(slow); + + merge(l1, l2); +} \ No newline at end of file diff --git a/c/0146-lru-cache.c b/c/0146-lru-cache.c new file mode 100644 index 000000000..192d0669b --- /dev/null +++ b/c/0146-lru-cache.c @@ -0,0 +1,147 @@ +typedef struct Node { + int key; + int value; + struct Node* prev; + struct Node* next; + UT_hash_handle hh; /* makes this structure hashable */ +} Node; + + +typedef struct { + int cap; + Node* leftMost; // LRU + Node* rightMost; // MRU +} LRUCache; + +Node* hashedNodes = NULL; + +void removeNode(Node* node) +{ + node->prev->next = node->next; + node->next->prev = node->prev; +} + +// Insert node at the right (MRU), just before the rightMost node +void insertNode(Node* node, LRUCache* cache) +{ + Node* prev = cache->rightMost->prev; + Node* next = cache->rightMost; + + prev->next = node; + next->prev = node; + + node->next = next; + node->prev = prev; +} + + +LRUCache* lRUCacheCreate(int capacity) { + // Allocate space for LRUCache + LRUCache* cache = (LRUCache*)malloc(sizeof(LRUCache)); + + // Set the capacity + cache->cap = capacity; + + // Allocate space for the leftMost and rightMost nodes + cache->leftMost = (Node*)malloc(sizeof(Node)); + cache->rightMost = (Node*)malloc(sizeof(Node)); + + // Initialize the nodes where we connect the leftMost and rightMost to form the list + cache->leftMost->prev = NULL; + cache->leftMost->next = cache->rightMost; + + cache->rightMost->prev = cache->leftMost; + cache->rightMost->next = NULL; + + return cache; +} + +int lRUCacheGet(LRUCache* obj, int key) { + Node* node; + HASH_FIND_INT(hashedNodes, &key, node); + + // Check if the key exists + if(node) + { + // Remove the node from its current position in the list + removeNode(node); + + // Insert the node at the right (MRU) + insertNode(node, obj); + + return node->value; + } + + return -1; +} + +void lRUCachePut(LRUCache* obj, int key, int value) { + Node* node; + HASH_FIND_INT(hashedNodes, &key, node); + + // Check if the key exists + if(node) + { + // Remove the node from its current position in the list + removeNode(node); + + // Update the node's current value + node->value = value; + } + else + { + // Allocate space for a new node + node = (Node*)malloc(sizeof(Node)); + // Initialize the node + node->key = key; + node->value = value; + + // Add the new node to the hash + HASH_ADD_INT(hashedNodes, key, node); + } + + // Insert the node at the right (MRU) + insertNode(node, obj); + + // Check if we have exceeded the capacity after adding the new node + if(HASH_COUNT(hashedNodes) > obj->cap) + { + Node* LRUNode = obj->leftMost->next; + + // Remove the left node (LRU) from the list + removeNode(LRUNode); + + // Remove the node from the hash + HASH_DEL(hashedNodes, LRUNode); + + // Deallocate the node + free(LRUNode); + } +} + +void lRUCacheFree(LRUCache* obj) { + // Deallocate the nodes first + free(obj->leftMost); + free(obj->rightMost); + + // Deallocate the LRUCache + free(obj); + + // Delete and deallocate all the nodes from the hash + Node *currentNode, *tmp; + + HASH_ITER(hh, hashedNodes, currentNode, tmp) { + HASH_DEL(hashedNodes, currentNode); /* delete; nodes advances to next */ + free(currentNode); + } +} + +/** + * Your LRUCache struct will be instantiated and called as such: + * LRUCache* obj = lRUCacheCreate(capacity); + * int param_1 = lRUCacheGet(obj, key); + + * lRUCachePut(obj, key, value); + + * lRUCacheFree(obj); +*/ diff --git a/c/0148-sort-list.c b/c/0148-sort-list.c new file mode 100644 index 000000000..95f3696ff --- /dev/null +++ b/c/0148-sort-list.c @@ -0,0 +1,70 @@ +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * struct ListNode *next; + * }; + */ + +struct ListNode* merge(struct ListNode *list1, struct ListNode *list2) { + struct ListNode *head, *tail; + + if (!list1 || !list2) { + if (!list1) { + return list2; + } + return list1; + } + + if (list1->val < list2->val) { + head = list1; + list1 = list1->next; + } else { + head = list2; + list2 = list2->next; + } + tail = head; + + while (list1 && list2) { + if (list1->val < list2->val) { + tail->next = list1; + list1 = list1->next; + } else { + tail->next = list2; + list2 = list2->next; + } + tail = tail->next; + } + + if (list1) { + tail->next = list1; + } + + if (list2) { + tail->next = list2; + } + + return head; +} + +// Split a linked list in two halfs: +struct ListNode* split(struct ListNode* head) { + struct ListNode *slow = head, *fast = head, *prev = NULL; + + while (fast && fast->next) { + prev = slow; + slow = slow->next; + fast = fast->next->next; + } + + prev->next = NULL; + return slow; +} + +struct ListNode* sortList(struct ListNode *head) { + if (!head || !head->next) { + return head; + } + + return merge(sortList(head), sortList(split(head))); +} diff --git a/c/0150-evaluate-reverse-polish-notation.c b/c/0150-evaluate-reverse-polish-notation.c new file mode 100644 index 000000000..8918ab206 --- /dev/null +++ b/c/0150-evaluate-reverse-polish-notation.c @@ -0,0 +1,51 @@ +int evalRPN(char ** tokens, int tokensSize){ + long int stk[tokensSize]; + + int stkIndex = -1; + + for(int i = 0; i < tokensSize; i++) + { + if(strcmp(tokens[i], "+") == 0) + { + int first = stk[stkIndex]; + stkIndex--; + int second = stk[stkIndex]; + + stk[stkIndex] = first + second; + } + else if(strcmp(tokens[i], "-") == 0) + { + int first = stk[stkIndex]; + stkIndex--; + int second = stk[stkIndex]; + + stk[stkIndex] = second - first; + } + else if(strcmp(tokens[i], "*") == 0) + { + long first = stk[stkIndex]; + stkIndex--; + int second = stk[stkIndex]; + + stk[stkIndex] = first * second; + + } + else if(strcmp(tokens[i], "/") == 0) + { + int first = stk[stkIndex]; + stkIndex--; + int second = stk[stkIndex]; + + stk[stkIndex] = second / first; + } + else + { + stkIndex++; + stk[stkIndex] = atoi(tokens[i]); + } + + } + + + return stk[stkIndex]; +} diff --git a/c/0152-maximum-product-subarray.c b/c/0152-maximum-product-subarray.c new file mode 100644 index 000000000..d294228ba --- /dev/null +++ b/c/0152-maximum-product-subarray.c @@ -0,0 +1,21 @@ +int maxProduct(int* nums, int numsSize){ + int res = nums[0], curMin = 1, curMax = 1; + + for (int i = 0; i < numsSize; i++) { + int temp = curMax * nums[i]; + curMax = max(max(nums[i] * curMax, nums[i] * curMin), nums[i]); + curMin = min(min(temp, nums[i] * curMin), nums[i]); + res = max(res, curMax); + } + return res; +} + +// C doesn't have a built-in max function +int max(int a, int b) { + return (a > b) ? a : b; +} + +// C doesn't have a built-in min function +int min(int a, int b) { + return (a < b) ? a : b; +} \ No newline at end of file diff --git a/c/0153-find-minimum-in-rotated-sorted-array.c b/c/0153-find-minimum-in-rotated-sorted-array.c new file mode 100644 index 000000000..e2d05adaf --- /dev/null +++ b/c/0153-find-minimum-in-rotated-sorted-array.c @@ -0,0 +1,19 @@ +int findMin(int* nums, int numsSize){ + // set the starting indicies to find minimum pivot + int left = 0; + int right = numsSize - 1; + + while (left < right) { + // calculate middle index + int middle = (left + right) / 2; + + // set the left or right index accordingly + if (nums[middle] > nums[right]) { + left = middle + 1; + } + else { + right = middle; + } + } + return nums[left]; +} \ No newline at end of file diff --git a/c/0155-min-stack.c b/c/0155-min-stack.c new file mode 100644 index 000000000..d9019bef2 --- /dev/null +++ b/c/0155-min-stack.c @@ -0,0 +1,142 @@ +#include +#include + +// A structure to represent an element of the stack. +typedef struct Node { + int value; + struct Node* next; +} Node; + +Node* nodeCreate(int val) { + Node* node = (Node*)malloc(sizeof(Node)); + node->value = val; + node->next = NULL; + return node; +} + +void nodePush(Node** obj, int val) { + assert(obj); + Node* node = (Node*)malloc(sizeof(Node)); + node->value = val; + node->next = *obj; + *obj = node; +} + +void nodePop(Node** obj) { + assert(obj); + if (*obj) { + Node* tmp = *obj; + *obj = tmp->next; + free(tmp); + } +} + +// A structure to represent a stack. +typedef struct { + int size; + Node* top; +} MyStack; + +MyStack* stackCreate() +{ + MyStack* stack = (MyStack*)malloc(sizeof(MyStack)); + stack->size = 0; + stack->top = NULL; + return stack; +} + +void stackPush(MyStack* stack, int item) +{ + if (!stack->top) { + stack->top = nodeCreate(item); + } else { + nodePush(&stack->top, item); + } + stack->size++; +} + +void stackPop(MyStack* stack) +{ + nodePop(&stack->top); + stack->size--; +} + +int stackTop(MyStack* stack) +{ + assert(stack); + assert(stack->top); + return stack->top->value; +} + +typedef struct { + MyStack* stk; + MyStack* minStk; +} MinStack; + +MinStack* minStackCreate() { + MinStack* min = (MinStack*)malloc(sizeof(MinStack)); + min->stk = NULL; + min->minStk = NULL; + return min; +} + +void minStackPush(MinStack* obj, int val) { + assert(obj); + if(!obj->stk) { + obj->stk = stackCreate(); + obj->minStk = stackCreate(); + } + stackPush(obj->stk, val); + if (obj->minStk->top) { + int minTop = stackTop(obj->minStk); + if ( val < minTop) { + stackPush(obj->minStk, val); + } else { + stackPush(obj->minStk, minTop); + } + } else { + stackPush(obj->minStk, val); + } +} + +void minStackPop(MinStack* obj) { + assert(obj); + stackPop(obj->stk); + stackPop(obj->minStk); +} + +int minStackTop(MinStack* obj) { + assert(obj); + return stackTop(obj->stk); +} + +int minStackGetMin(MinStack* obj) { + assert(obj); + return stackTop(obj->minStk); +} + +void minStackFree(MinStack* obj) { + assert(obj); + if (obj->stk) { + while(obj->stk->top) { + stackPop(obj->stk); + } + while(obj->minStk->top) { + stackPop(obj->minStk); + } + free(obj->stk); + free(obj->minStk); + } + free(obj); +} + +/** + * Your MinStack struct will be instantiated and called as such: + * MinStack* obj = minStackCreate(); + * minStackPush(obj, val); + * minStackPop(obj); + * int param_3 = minStackTop(obj); + * int param_4 = minStackGetMin(obj); + * minStackFree(obj); + */ + diff --git a/c/0160-intersection-of-two-linked-lists.c b/c/0160-intersection-of-two-linked-lists.c new file mode 100644 index 000000000..ba4b039d8 --- /dev/null +++ b/c/0160-intersection-of-two-linked-lists.c @@ -0,0 +1,27 @@ +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * struct ListNode *next; + * }; + */ +struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) { + struct ListNode *currA = headA; + struct ListNode *currB = headB; + + while (currA && currB){ + if (currA == currB){ + return currA; + } + currA = currA->next; + currB = currB->next; + + if (!currA && currB){ + currA = headB; + } + if (!currB && currA){ + currB = headA; + } + } + return NULL; +} diff --git a/c/0162-find-peak-element.c b/c/0162-find-peak-element.c new file mode 100644 index 000000000..c259a7f5b --- /dev/null +++ b/c/0162-find-peak-element.c @@ -0,0 +1,15 @@ +int findPeakElement(int* nums, int numsSize) { + int start = 0; + int end = numsSize - 1; + + while(start < end){ + int mid = start + (end - start)/2; + + if(nums[mid] > nums[mid+1]) + end = mid; + else if(nums[mid] < nums[mid+1]) + start = mid + 1; + } + + return start; +} \ No newline at end of file diff --git a/c/0167-two-sum-ii-input-array-is-sorted.c b/c/0167-two-sum-ii-input-array-is-sorted.c new file mode 100644 index 000000000..3d95cbecb --- /dev/null +++ b/c/0167-two-sum-ii-input-array-is-sorted.c @@ -0,0 +1,28 @@ + +/* + Given a 1-indexed sorted int array & target: + Return indices (added by 1) of 2 nums that add to target + 2 pointers, outside in, iterate i/j if sum is too low/high + Time: O(n) + Space: O(1) +*/ + +int* twoSum(int* numbers, int numbersSize, int target, int* returnSize){ + *returnSize = 2; + int* ans = malloc(sizeof(int)*2); + + int i = 0; + int j = numbersSize-1; + int k; + while (true) { + k = numbers[i]+numbers[j]-target; + if (k==0) { // numbers[i]+numbers[j] = target + ans[0] = i+1; + ans[1] = j+1; + return ans; + } else if (k>0) // numbers[i]+numbers[j] > target + j--; + else // numbers[i]+numbers[j] < target + i++; + } +} diff --git a/c/0169-majority-element.c b/c/0169-majority-element.c new file mode 100644 index 000000000..264a31e27 --- /dev/null +++ b/c/0169-majority-element.c @@ -0,0 +1,23 @@ +/* +Given an array nums of size n, return the majority element. + +Space: O(1) +Time: O(n) +*/ + +int majorityElement(int* nums, int numsSize){ + int candidate=nums[0]; + int count=1; + for (int i=1; i (((unsigned int)*(int*)a * j) + *(int*)b); +} + +char * largestNumber(int* nums, int numsSize){ + char *res = NULL; + int i, len, pos; + + if (numsSize < 0) { + return res; + } + + // Sort the array with specified comparaotor. + qsort(nums, numsSize, sizeof(int), compareInt); + + // Caculate the length of the return string. + len = 1; + for (i = 0; i < numsSize; i++) len += snprintf(NULL, 0, "%d", nums[i]); + res = calloc(len, sizeof(char)); + + // If the firs element of sorted array is 0, + // return a single digit of 0 no matter how long is the string. + if (nums[0] == 0) { + res[0] = '0'; + return res; + } + + // Print all nums to the return string. + pos = 0; + for (i = 0; i < numsSize; i++) { + pos += snprintf(res + pos, len, "%d", nums[i]); + } + + return res; +} diff --git a/c/0190-reverse-bits.c b/c/0190-reverse-bits.c new file mode 100644 index 000000000..ed5fd3d01 --- /dev/null +++ b/c/0190-reverse-bits.c @@ -0,0 +1,10 @@ +uint32_t reverseBits(uint32_t n) { + uint32_t res = 0; + + for (int i = 0; i < 32; i++) { + res <<= 1; + res |= n & 1; + n >>= 1; + } + return res; +} \ No newline at end of file diff --git a/c/0191-number-of-1-bits.c b/c/0191-number-of-1-bits.c new file mode 100644 index 000000000..b2b4a6f41 --- /dev/null +++ b/c/0191-number-of-1-bits.c @@ -0,0 +1,12 @@ +int hammingWeight(uint32_t n) { + int res = 0; + int bit = 0; + while (n != 0) { + bit = n & 1; + if (bit == 1) { + res++; + } + n = n >> 1; + } + return res; +} \ No newline at end of file diff --git a/c/0198-house-robber.c b/c/0198-house-robber.c new file mode 100644 index 000000000..b8e94ae0b --- /dev/null +++ b/c/0198-house-robber.c @@ -0,0 +1,35 @@ +int dp[105] = {}; + +int solve(int *nums, int n) +{ + if(n == 0) return nums[0]; + + if(n < 0) return 0; + + if(dp[n] != -1) return dp[n]; + + // robbing the present house and then move to the (n-2)th house + int way1 = nums[n] + solve(nums, n-2); + + // skipping the present house and rob the (n-1)th house + int way2 = 0 + solve(nums, n-1); + + if(way1 > way2) { + dp[n] = way1; + } + else { + dp[n] = way2; + } + + return dp[n]; +} + +int rob(int* nums, int numsSize){ + + // Initializing the dp array with initial value -1. + for(int i = 0; i < 105; i++){ + dp[i] = -1 ; + } + + return solve(nums, numsSize - 1); +} \ No newline at end of file diff --git a/c/0199-binary-tree-right-side-view.c b/c/0199-binary-tree-right-side-view.c new file mode 100644 index 000000000..338d3b0c3 --- /dev/null +++ b/c/0199-binary-tree-right-side-view.c @@ -0,0 +1,57 @@ +/* + Given a binary tree, return the right side view of it (the nodes which can + be seen in front when looking from the right). + Ex. 1 <- + / \ + 2 3 <- -> [1, 3, 4] + \ \ + 5 4 <- + + + The right side view is basically the last element at each level of the tree. + So BFS is one way this problem can be solved. + + The solution below uses recursion, where on reaching a new depth we add that + node as the right view for that depth. We recursively visit the right child + before the left child to get the right view. + + Time: O(n) + Space: O(n) +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * struct TreeNode *left; + * struct TreeNode *right; + * }; + */ + +void dfs(struct TreeNode* root, int* result, int* returnSize, int depth) { + if (root == NULL) + return; + + // If a new depth is reached, add the node as right view for that depth + if (*returnSize <= depth) { + *returnSize += 1; + result[depth] = root->val; + } + + // Visit the right child first then left child + dfs(root->right, result, returnSize, depth+1); + dfs(root->left, result, returnSize, depth+1); +} + +/** + * Note: The returned array must be malloced, assume caller calls free(). + */ +int* rightSideView(struct TreeNode* root, int* returnSize) { + // Size of result array depends on the depth of tree, which can be precomputed + int* result = (int*) malloc(sizeof(int)*100); + *returnSize = 0; + + dfs(root, result, returnSize, 0); + + return result; +} diff --git a/c/0200-number-of-islands.c b/c/0200-number-of-islands.c new file mode 100644 index 000000000..701e371bd --- /dev/null +++ b/c/0200-number-of-islands.c @@ -0,0 +1,39 @@ + + +int directions[][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + +void searchIsland(int row, int col, int rows, int cols, char** grid) { + if ( row >= 0 && col >= 0 && row < rows && col < cols && grid[row][col] == '1') { + for (int i = 0; i < 4; i++) { + searchIsland(row + directions[i][0], col + directions[i][1], rows, cols, grid); + grid[row][col] = '0'; + } + } +} + + +int numIslands(char** grid, int gridSize, int* gridColSize){ + + int rows = gridSize; + int cols = gridColSize[0]; + + int IslandCount = 0; + + for (int row = 0; row < rows; row++) { + for (int col = 0; col < cols; col++) { + if (grid[row][col] == '1') { + searchIsland(row, col, rows, cols, grid); + IslandCount++; + } + } + } + return IslandCount; +} + + + + +// int numIslands(char** grid, int gridSize, int* gridColSize){ +// printf("%c", grid[0][4]); +// return 0; +// } \ No newline at end of file diff --git a/c/0202-happy-number.c b/c/0202-happy-number.c new file mode 100644 index 000000000..7cb975c94 --- /dev/null +++ b/c/0202-happy-number.c @@ -0,0 +1,20 @@ +bool isHappy(int n) { + int slow = n; + int fast = sumSquareDigits(n); + + while (slow != fast) { + fast = sumSquareDigits(sumSquareDigits(fast)); + slow = sumSquareDigits(slow); + } + return fast == 1; +} + +int sumSquareDigits(n) { + int result = 0; + while (n != 0) { + int digit = n % 10; + result += digit * digit; + n /= 10; + } + return result; +} \ No newline at end of file diff --git a/c/0205-isomorphic-strings.c b/c/0205-isomorphic-strings.c new file mode 100644 index 000000000..4f2fc50bc --- /dev/null +++ b/c/0205-isomorphic-strings.c @@ -0,0 +1,35 @@ +/* +Given two strings s and t, determine if they are isomorphic. + +Space: O(1) +Time: O(n) +*/ + +bool isIsomorphic(char * s, char * t){ + int alphabet_s[256]; // Alphabet of t letters to s + int alphabet_t[256]; // Alphabet of s letters to t + for (int i=0; i<256; i++){ // Fill alphabets with empty values + alphabet_s[i] = -1; + alphabet_t[i] = -1; + } + int i; // To be able to use it outside the loop for + for (i=0; s[i]!='\0'; i++) { + if (alphabet_t[s[i]]==-1 && alphabet_s[t[i]]==-1) { + alphabet_t[s[i]] = t[i]; + alphabet_s[t[i]] = s[i]; + } else if (alphabet_t[s[i]]==-1) { + if (alphabet_s[t[i]] != s[i]) { + return false; + } + alphabet_t[s[i]] = t[i]; + } else if (alphabet_s[t[i]]==-1) { + if (alphabet_t[s[i]] != t[i]) { + return false; + } + alphabet_s[t[i]] = s[i]; + } else if (alphabet_t[s[i]] != t[i] || alphabet_s[t[i]] != s[i]) { + return false; + } + } + return t[i]=='\0'; +} diff --git a/c/0206-reverse-linked-list.c b/c/0206-reverse-linked-list.c new file mode 100644 index 000000000..db59aeb5e --- /dev/null +++ b/c/0206-reverse-linked-list.c @@ -0,0 +1,33 @@ +/** + * Given the head of a singly linked list, reverse the list, and return the + * reversed list. + * + * Constraints: + * + * The number of nodes in the list is the range [0, 5000]. + * -5000 <= Node.val <= 5000 + * + * Definition for singly-linked list. + * struct ListNode { + * int val; + * struct ListNode *next; + * }; + * + * Space: O(1) + * Time: O(n) + */ + +struct ListNode* reverseList(struct ListNode* head){ + struct ListNode *current = head; + struct ListNode *next = NULL; + struct ListNode *previous = NULL; + + while (current) { + next = current->next; + current->next = previous; + previous = current; + current = next; + } + + return previous; +} diff --git a/c/0207-course-schedule.c b/c/0207-course-schedule.c new file mode 100644 index 000000000..576d9e9f0 --- /dev/null +++ b/c/0207-course-schedule.c @@ -0,0 +1,49 @@ +#include +#include + +// The most prerequisites a course has in the test data. +#define PREREQ_MAX (13) + +bool hasLoops(int idx, int cmap[][PREREQ_MAX], int cpr[], int* visited) { + // The current course requires a course that requires the current course (looped requirements). + if (visited[idx] > 0) + return true; + + // Check the current course's prerequisites for loops. + if (visited[idx] == 0) { + visited[idx] = 1; + for (int i = 0; i < cpr[idx]; i++) { + if (hasLoops(cmap[idx][i], cmap, cpr, visited)) { + return true; + } + } + // Mark course as processed in a previous loop. Alternatively, we can also just clear the visited[] array. + visited[idx] = -1; + } + + return false; +} + +bool canFinish(int numCourses, int** prerequisites, int prerequisitesSize, int* prerequisitesColSize) { + int cmap[2001][PREREQ_MAX]; // Holds the prerequisites of each course. + int cpr[2001] = {0}; // Number of course prerequisites. + int visited[2001] = {0}; + + // Map out all prerequisites of all courses. + for (int i = 0; i < prerequisitesSize; i++) { + int course = prerequisites[i][0]; + int prereq = prerequisites[i][1]; + + cmap[course][cpr[course]++] = prereq; + } + + // Check for loops. + for (int i = 0; i < numCourses; i++) { + // If the courses loop, then we can't finish the courses. + if (hasLoops(i, cmap, cpr, visited)) { + return false; + } + } + + return true; +} diff --git a/c/0208-implement-trie-prefix-tree.c b/c/0208-implement-trie-prefix-tree.c new file mode 100644 index 000000000..1a6b16c03 --- /dev/null +++ b/c/0208-implement-trie-prefix-tree.c @@ -0,0 +1,108 @@ + +// struct trieNode { +// struct trieNode* nextNodes[26]; +// bool end; +// }; + +typedef struct { + struct Trie* nextNodes[26]; /* a Trie* pointer array of size 26 to store char nodes from a to z*/ + bool end; /* a boolean to mark the end of word */ +} Trie; + +// Create a trie node using malloc. +Trie* trieCreate() { + Trie* root = (Trie*)malloc(sizeof(Trie)); + for (int i = 0; i < 26; i++) { + root -> nextNodes[i] = NULL; + } + root -> end = false; + return root; +} + +// Get the next character node from the current trie node. +Trie* get_node(Trie* node, char character) { + int index = character - 97; + Trie* nextNode = node -> nextNodes[index]; + return nextNode; +} + +// Add a new character node to the current trie node. +void add_node(Trie* node, char character, Trie* newNode) { + int index = character - 97; + node -> nextNodes[index] = newNode; + return; +} + +// Insert implementation in recursion. +void insert_recur(Trie* node, int idx, char * word, int wordSize) { + if (idx == wordSize) { + node -> end = true; + return; + } + if (get_node(node, word[idx]) == NULL) { + add_node(node, word[idx], trieCreate()); + } + return insert_recur(get_node(node, word[idx]), idx + 1, word, wordSize); +} + +// Insert method. +void trieInsert(Trie* obj, char * word) { + insert_recur(obj, 0, word, strlen(word)); + return; +} + +// Search implementation in recursion. +bool trieSearch_recur(Trie* node, int idx, char * word, int wordSize) { + if (idx == wordSize) { + return node -> end; + } + if (get_node(node, word[idx]) == NULL) { + return false; + } + return trieSearch_recur(get_node(node, word[idx]), idx + 1, word, wordSize); +} + +// Search method. +bool trieSearch(Trie* obj, char * word) { + return trieSearch_recur(obj, 0, word, strlen(word)); +} + +// prefix search implementation in recursion. +bool trieStartsWith_recur(Trie* node, int idx, char * prefix, int prefixSize) { + if (idx == prefixSize) { + return true; + } + if (get_node(node, prefix[idx]) == NULL) { + return false; + } + return trieStartsWith_recur(get_node(node, prefix[idx]), idx + 1, prefix, prefixSize); +} + +// prefix search method. +bool trieStartsWith(Trie* obj, char * prefix) { + return trieStartsWith_recur(obj, 0, prefix, strlen(prefix)); +} + +// free method to free up memory of all the nodes. +void trieFree(Trie* obj) { + if (obj == NULL) { + return; + } + for (char i = 97; i < 123; i++) { + trieFree(get_node(obj, i)); + } + free(obj); + return; +} + +/** + * Your Trie struct will be instantiated and called as such: + * Trie* obj = trieCreate(); + * trieInsert(obj, word); + + * bool param_2 = trieSearch(obj, word); + + * bool param_3 = trieStartsWith(obj, prefix); + + * trieFree(obj); +*/ \ No newline at end of file diff --git a/c/0209-minimum-size-subarray-sum.c b/c/0209-minimum-size-subarray-sum.c new file mode 100644 index 000000000..d72919ef1 --- /dev/null +++ b/c/0209-minimum-size-subarray-sum.c @@ -0,0 +1,32 @@ +/* +Given an array of positive integers nums and a positive integer target, return +the minimal length of a contiguous subarray of which the sum is greater than or +equal to target + +Space: O(1) +Time: O(n) +*/ + +int min(int a, int b) { + return a=target) { + if (len==0) + len = j-i+1; + while (cpt-nums[i] >= target) { + cpt -= nums[i]; + len = min(len, j-i); + i++; + } + } + j++; + } + return len; +} diff --git a/c/0211-design-add-and-search-words-data-structure.c b/c/0211-design-add-and-search-words-data-structure.c new file mode 100644 index 000000000..b251ca20a --- /dev/null +++ b/c/0211-design-add-and-search-words-data-structure.c @@ -0,0 +1,86 @@ +typedef struct WordDictionary{ + struct WordDictionary *c[26]; + bool isWord; +} WordDictionary; + +WordDictionary *NewDictionary() { + // allocate and init the memory for a dictionary element. + WordDictionary *n = (WordDictionary*)calloc(1, sizeof(WordDictionary)); + return n; +} + +WordDictionary* wordDictionaryCreate() { + WordDictionary *root = NewDictionary(); + return root; +} + +void wordDictionaryAddWord(WordDictionary* obj, char * word) { + assert(obj); + int n = strlen(word); + WordDictionary* dict = obj; + int i, j; + for (i = 0; i < n; i++) { + j = word[i] - 'a'; + if (!dict->c[j]) { + dict->c[j] = NewDictionary(); + } + dict = dict->c[j]; + } + dict->isWord = true; +} + +bool wordDictionarySearchR(WordDictionary* dict, char * word, int index) { + assert(dict); + + char c = word[index]; + int i, j; + if (index == strlen(word)) { + return dict->isWord; + } + if (c == '.') { + for (i = 0; i < 26; i++) { + if (dict->c[i] && wordDictionarySearchR(dict->c[i], word, index + 1)) { + return true; + } + } + } else { + j = c - 'a'; + if (!dict->c[j]) { + return false; + } + return wordDictionarySearchR(dict->c[j], word, index + 1); + } + return false; +} + +bool wordDictionarySearch(WordDictionary* obj, char * word) { + assert(obj); + return wordDictionarySearchR(obj, word, 0); +} + +void wordDictionaryFreeR(WordDictionary* dict) { + assert(dict); + int i; + for (i = 0; i < 26; i++) { + if (dict->c[i]) { + wordDictionaryFreeR(dict->c[i]); + } + } + free(dict); +} + +void wordDictionaryFree(WordDictionary* obj) { + assert(obj); + wordDictionaryFreeR(obj); +} + +/** + * Your WordDictionary struct will be instantiated and called as such: + * WordDictionary* obj = wordDictionaryCreate(); + * wordDictionaryAddWord(obj, word); + + * bool param_2 = wordDictionarySearch(obj, word); + + * wordDictionaryFree(obj); +*/ + diff --git a/c/0212-word-search-ii.c b/c/0212-word-search-ii.c new file mode 100644 index 000000000..b29316139 --- /dev/null +++ b/c/0212-word-search-ii.c @@ -0,0 +1,87 @@ +typedef struct TrieNode trie_t; + +struct TrieNode { + bool valid; + char* word; + int count; + trie_t* parent; + trie_t* children[26]; +}; + +void insert(trie_t* root, char* word) { + char* curr = word; + while (*curr != '\0') { + int index = *curr - 'a'; + if (root->children[index] == NULL) { + root->children[index] = (trie_t*)calloc(1, sizeof(trie_t)); + root->children[index]->valid = true; + root->children[index]->parent = root; + } + root->count++; + root = root->children[index]; + curr++; + } + root->count++; + root->word = word; +} + +void traverse(char** result, int* resultSize, char** board, bool** visited, int boardSize, int boardColSize, int x, int y, trie_t* node) { + if (node == NULL || !node->valid) { + return; + } + if (node->word != NULL) { + result[(*resultSize)++] = node->word; + node->word = NULL; + trie_t* curNode = node; + while (curNode != NULL) { + curNode->count--; + if (curNode->count <= 0) { + curNode->valid = false; + } + curNode = curNode->parent; + } + } + visited[x][y] = true; + + int directions[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; + for (int i = 0; i < 4; i++) { + int newX = x + directions[i][0]; + int newY = y + directions[i][1]; + if (newX >= 0 && newX < boardSize && newY >= 0 && newY < boardColSize && !visited[newX][newY]) { + int c = board[newX][newY] - 'a'; + traverse(result, resultSize, board, visited, boardSize, boardColSize, newX, newY, node->children[c]); + } + } + + visited[x][y] = false; +} + +char ** findWords(char** board, int boardSize, int* boardColSize, char ** words, int wordsSize, int* returnSize) { + // Create the trie and insert words + trie_t* trie = (trie_t*) calloc(1, sizeof(trie_t)); + for (int i = 0; i < wordsSize; i++) { + insert(trie, words[i]); + } + + // Allocate memory for the result array + char** result = (char**) malloc(sizeof(char*) * wordsSize); + int resultSize = 0; + + // Create and initialize the visited array + bool** visited = (bool**) malloc(sizeof(bool*) * boardSize); + for (int i = 0; i < boardSize; i++) { + visited[i] = (bool*) calloc(*boardColSize, sizeof(bool)); + } + + // Traverse the board and find words + for (int i = 0; i < boardSize; i++) { + for (int j = 0; j < *boardColSize; j++) { + int c = board[i][j] - 'a'; + traverse(result, &resultSize, board, visited, boardSize, *boardColSize, i, j, trie->children[c]); + } + } + + // Clean up and set the return size + *returnSize = resultSize; + return result; +} diff --git a/c/0213-house-robber-ii.c b/c/0213-house-robber-ii.c new file mode 100644 index 000000000..c1656e705 --- /dev/null +++ b/c/0213-house-robber-ii.c @@ -0,0 +1,25 @@ +int rob(int* nums, int numsSize){ + int n = numsSize; + + if (n == 1) { + return nums[0]; + } + + int range1 = robber(nums, 0, n - 2); + int range2 = robber(nums, 1, n - 1); + + return (range1 > range2) ? range1 : range2; +} + +int robber(int* nums, int start, int end) { + int prev = 0; + int curr = 0; + int next = 0; + + for (int i = start; i <= end; i++) { + next = (curr > prev + nums[i]) ? curr : prev + nums[i]; + prev = curr; + curr = next; + } + return curr; +} \ No newline at end of file diff --git a/c/0215-kth-largest-element-in-an-array.c b/c/0215-kth-largest-element-in-an-array.c new file mode 100644 index 000000000..2b517ae24 --- /dev/null +++ b/c/0215-kth-largest-element-in-an-array.c @@ -0,0 +1,41 @@ +// Function to partition the array using Lomuto partition scheme +int partition(int* nums, int left, int right) { + int pivot = nums[right]; + int i = left - 1; + + for (int j = left; j < right; j++) { + if (nums[j] <= pivot) { + i++; + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } + } + + int temp = nums[i + 1]; + nums[i + 1] = nums[right]; + nums[right] = temp; + + return i + 1; +} + +// Function to find the kth largest element using Quickselect +int quickSelect(int* nums, int left, int right, int k) { + if (left <= right) { + int partitionIndex = partition(nums, left, right); + + if (partitionIndex == k) { + return nums[partitionIndex]; + } else if (partitionIndex > k) { + return quickSelect(nums, left, partitionIndex - 1, k); + } else { + return quickSelect(nums, partitionIndex + 1, right, k); + } + } + + return -1; // Invalid case +} + +int findKthLargest(int* nums, int numsSize, int k) { + return quickSelect(nums, 0, numsSize - 1, numsSize - k); +} diff --git a/c/0217-contains-duplicate.c b/c/0217-contains-duplicate.c new file mode 100644 index 000000000..d60fab49b --- /dev/null +++ b/c/0217-contains-duplicate.c @@ -0,0 +1,42 @@ +/* + Time: O(n) + Space: O(1) +*/ + +typedef struct { + int key; + UT_hash_handle hh; // Makes this structure hashable +} hash_table; + +hash_table *hash = NULL, *elem, *tmp; + +bool containsDuplicate(int* nums, int numsSize){ + if (numsSize == 1) { + return false; + } + + bool flag = false; + + for (int i=0; ikey = nums[i]; + HASH_ADD_INT(hash, key, elem); + + flag = false; + } + else { + flag = true; + break; + } + } + + // Free up the hash table + HASH_ITER(hh, hash, elem, tmp) { + HASH_DEL(hash, elem); free(elem); + } + + return flag; +} \ No newline at end of file diff --git a/c/0225-implement-stack-using-queues.c b/c/0225-implement-stack-using-queues.c new file mode 100644 index 000000000..dff71182e --- /dev/null +++ b/c/0225-implement-stack-using-queues.c @@ -0,0 +1,136 @@ +// A structure to represent an element of the queue. +typedef struct Node { + int value; + struct Node* next; +} Node; + +typedef struct MyQueue { + int size; + Node* front; + Node* rear; +} MyQueue; + +MyQueue* MyQueueCreate() { + MyQueue* queue = (MyQueue*)malloc(sizeof(MyQueue)); + queue->size = 0; + queue->front = NULL; + queue->rear = NULL; + return queue; +} + +void MyQueuePush(MyQueue** obj, int val) { + assert(obj); + assert(*obj); + MyQueue* queue = *obj; + Node* node = (Node*)malloc(sizeof(Node)); + node->value = val; + node->next = NULL; + if (queue->rear) { + queue->rear->next = node; + queue->rear = node; + } else { + queue->front = node; + queue->rear = node; + } + queue->size++; +} + +void MyQueuePop(MyQueue** obj) { + assert(obj); + assert(*obj); + MyQueue* queue = *obj; + Node* tmp = queue->front; + if (queue->size > 0 && tmp) { + queue->front = queue->front->next; + queue->size--; + free(tmp); + } + if (queue->rear == tmp) { + queue->rear = NULL; + } +} + +int MyQueueFront(MyQueue** obj) { + assert(obj); + MyQueue* queue = *obj; + assert(queue->front); + return queue->front->value; +} + +int MyQueueRear(MyQueue** obj) { + assert(obj); + MyQueue* queue = *obj; + assert(queue->rear); + return queue->rear->value; +} + +int MyQueueSize(MyQueue** obj) { + assert(obj); + assert(*obj); + return (*obj)->size; +} + +typedef struct { + MyQueue* queue; +} MyStack; + + +MyStack* myStackCreate() { + MyStack* stack = (MyStack*)malloc(sizeof(MyStack)); + stack->queue = NULL; + return stack; +} + +void myStackPush(MyStack* obj, int x) { + assert(obj); + int size; + if (!obj->queue) { + obj->queue = MyQueueCreate(); + } + MyQueuePush(&obj->queue, x); + size = obj->queue->size; + while(--size > 0) { + MyQueuePush(&obj->queue, MyQueueFront(&obj->queue)); + MyQueuePop(&obj->queue); + } +} + +int myStackPop(MyStack* obj) { + assert(obj); + int val = MyQueueFront(&obj->queue); + MyQueuePop(&obj->queue); + return val; +} + +int myStackTop(MyStack* obj) { + assert(obj); + assert(obj->queue); + return MyQueueFront(&obj->queue); +} + +bool myStackEmpty(MyStack* obj) { + assert(obj); + if (obj->queue) { + return obj->queue->size == 0; + } + return true; +} + +void myStackFree(MyStack* obj) { + if(obj) { + while(obj->queue && obj->queue->front) { + MyQueuePop(&obj->queue); + } + free(obj); + } +} + +/** + * Your MyStack struct will be instantiated and called as such: + * MyStack* obj = myStackCreate(); + * myStackPush(obj, x); + * int param_2 = myStackPop(obj); + * int param_3 = myStackTop(obj); + * bool param_4 = myStackEmpty(obj); + * myStackFree(obj); + */ diff --git a/c/0226-invert-binary-tree.c b/c/0226-invert-binary-tree.c new file mode 100644 index 000000000..5a3612cb0 --- /dev/null +++ b/c/0226-invert-binary-tree.c @@ -0,0 +1,20 @@ +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * struct TreeNode *left; + * struct TreeNode *right; + * }; + */ + +struct TreeNode *invertTree(struct TreeNode* root) { + + if (root == NULL) { + return root; + } + struct TreeNode* inverted_right = invertTree(root -> right); + struct TreeNode* inverted_left = invertTree(root -> left); + root -> right = inverted_left; + root -> left = inverted_right; + return root; +} diff --git a/c/0230-kth-smallest-element-in-a-bst.c b/c/0230-kth-smallest-element-in-a-bst.c new file mode 100644 index 000000000..15f587a87 --- /dev/null +++ b/c/0230-kth-smallest-element-in-a-bst.c @@ -0,0 +1,29 @@ +/* +Given the root of a binary search tree, and an integer k, return +the kth smallest value (1-indexed) of all the values of the nodes +in the tree. + +Space: O(log(n)) (due to recursive calls) +Time: O(n) +*/ + +void iterate_bst(struct TreeNode* t, int* pos, int* ans, int k) { + if (t==NULL) + return; + iterate_bst(t->left, pos, ans, k); + if ((*ans)!=-1) { // Already found + return; + } else if (*pos == k) { // t->val is the number wanted + *ans = t->val; + return; + } + *pos = *pos + 1; + iterate_bst(t->right, pos, ans, k); +} + +int kthSmallest(struct TreeNode* root, int k){ + int pos = 1; // Current pos in the tree + int ans = -1; + iterate_bst(root, &pos, &ans, k); + return ans; +} diff --git a/c/0234-palindrome-linked-list.c b/c/0234-palindrome-linked-list.c new file mode 100644 index 000000000..59eb86918 --- /dev/null +++ b/c/0234-palindrome-linked-list.c @@ -0,0 +1,36 @@ +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * struct ListNode *next; + * }; + */ +bool isPalindrome(struct ListNode* head){ + + // find middle of linked list + struct ListNode *slow = head, *fast = head; + while (fast && fast->next){ + slow = slow->next; + fast = fast->next->next; + } + // reverse second half + struct ListNode* prev = NULL; + while (slow){ + struct ListNode* nxt = slow->next; + slow->next = prev; + prev = slow; + slow = nxt; + } + + // compare left and right + struct ListNode *left=head, *right=prev; + while (right){ + if (left->val != right->val){ + return false; + } + left = left->next; + right = right->next; + } + return true; + +} diff --git a/c/0235-lowest-common-ancestor-of-a-binary-search-tree.c b/c/0235-lowest-common-ancestor-of-a-binary-search-tree.c new file mode 100644 index 000000000..5da9fb8ed --- /dev/null +++ b/c/0235-lowest-common-ancestor-of-a-binary-search-tree.c @@ -0,0 +1,23 @@ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * struct TreeNode *left; + * struct TreeNode *right; + * }; + */ + +struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q) { + + int rootVal = root -> val; + int pVal = p -> val; + int qVal = q -> val; + + if (pVal > rootVal && qVal > rootVal) { + return lowestCommonAncestor(root -> right, p, q); + } else if (pVal < rootVal && qVal < rootVal) { + return lowestCommonAncestor(root -> left, p, q); + } + return root; +} \ No newline at end of file diff --git a/c/0238-product-of-array-except-self.c b/c/0238-product-of-array-except-self.c new file mode 100644 index 000000000..ba642398e --- /dev/null +++ b/c/0238-product-of-array-except-self.c @@ -0,0 +1,27 @@ +/* + Time: O(n) + Space: O(n) + + * Note: The returned array must be malloced, assume caller calls free(). +*/ + +int* productExceptSelf(int* nums, int numsSize, int* returnSize) { + *returnSize = numsSize; + + int *result = (int *)malloc(sizeof(int)*numsSize); + memset(result, 1, numsSize*sizeof(result[0])); // Fill up result array with 1s + + int pre = 1; + for(int i=0; i=0; i--) { + result[i] *= post; + post *= nums[i]; + } + + return result; +} \ No newline at end of file diff --git a/c/0239-sliding-window-maximum.c b/c/0239-sliding-window-maximum.c new file mode 100644 index 000000000..fba51ec89 --- /dev/null +++ b/c/0239-sliding-window-maximum.c @@ -0,0 +1,84 @@ +#include +#include + +typedef struct { + int value; + int index; +} Pair; + +typedef struct { + Pair* data; + int front; + int rear; + int size; +} Deque; + +// Create a new deque +Deque* createDeque(int size) { + Deque* deque = (Deque*)malloc(sizeof(Deque)); + deque->data = (Pair*)malloc(size * sizeof(Pair)); + deque->front = 0; + deque->rear = -1; + deque->size = 0; + return deque; +} + +// Push a new element to the back of the deque +void pushBack(Deque* deque, int value, int index) { + while (deque->size > 0 && value >= deque->data[deque->rear].value) { + deque->rear--; + deque->size--; + } + deque->rear++; + deque->data[deque->rear].value = value; + deque->data[deque->rear].index = index; + deque->size++; +} + +// Pop elements from the front of the deque +void popFront(Deque* deque, int index) { + if (deque->size > 0 && deque->data[deque->front].index == index) { + deque->front++; + deque->size--; + } +} + +// Calculate the maximum sliding window +int* maxSlidingWindow(int* nums, int numsSize, int k, int* returnSize) { + // Check if the input array is empty + if (numsSize == 0) { + *returnSize = 0; + return NULL; + } + + // Calculate the size of the result array + *returnSize = numsSize - k + 1; + + // Allocate memory for the result array + int* result = (int*)malloc((*returnSize) * sizeof(int)); + + // Create a deque (double-ended queue) to efficiently track maximum elements + Deque* deque = createDeque(numsSize); + + // Iterate through the input array + for (int i = 0; i < numsSize; i++) { + // Push the current element to the deque, maintaining the maximum at the front + pushBack(deque, nums[i], i); + + // Check if the window has reached the required size + if (i >= k - 1) { + // Store the maximum element in the result array + result[i - k + 1] = deque->data[deque->front].value; + + // Pop elements from the front of the deque that are outside the current window + popFront(deque, i - k + 1); + } + } + + // Free memory allocated for the deque + free(deque->data); + free(deque); + + // Return the result array containing maximum elements in each sliding window + return result; +} diff --git a/c/0242-valid-anagram.c b/c/0242-valid-anagram.c new file mode 100644 index 000000000..6f87c3e06 --- /dev/null +++ b/c/0242-valid-anagram.c @@ -0,0 +1,12 @@ +bool isAnagram(char* s, char* t) { + int hash[26] = {0}; + int i = 0; + + while (s[i]) hash[s[i++] - 'a']++; + i = 0; + while (t[i]) hash[t[i++] - 'a']--; + + for (i = 0; i < 26; i++) + if (hash[i] != 0) return false; + return true; +} \ No newline at end of file diff --git a/c/0263-ugly-number.c b/c/0263-ugly-number.c new file mode 100644 index 000000000..61453593d --- /dev/null +++ b/c/0263-ugly-number.c @@ -0,0 +1,18 @@ +/* +An ugly number is a positive integer whose prime factors are limited to 2, 3, and 5. +Given an integer n, return true if n is an ugly number. +*/ + +bool isUgly(int n){ + if (n<=0) + return false; + if (n==1) + return true; + if (n%3==0) + return isUgly(n/3); + if (n%2==0) + return isUgly(n/2); + if (n%5==0) + return isUgly(n/5); + return false; +} diff --git a/c/0268-missing-number.c b/c/0268-missing-number.c new file mode 100644 index 000000000..6f3ac6fc3 --- /dev/null +++ b/c/0268-missing-number.c @@ -0,0 +1,8 @@ +int missingNumber(int* nums, int numsSize){ + int res = numsSize; + + for (int i = 0; i < numsSize; i++) { + res += i - nums[i]; + } + return res; +} \ No newline at end of file diff --git a/c/0279-perfect-squares.c b/c/0279-perfect-squares.c new file mode 100644 index 000000000..2a0d8d4ff --- /dev/null +++ b/c/0279-perfect-squares.c @@ -0,0 +1,20 @@ +int min(int a, int b) { + return a < b ? a : b; +} + +int numSquares(int n) { + int dp[n + 1]; + for (int i = 0; i <= n; i++) { + dp[i] = INT_MAX; // Initialize to a large value + } + + dp[0] = 0; + + for (int i = 1; i <= n; i++) { + for (int j = 1; j * j <= i; j++) { + dp[i] = min(dp[i], dp[i - j * j] + 1); + } + } + + return dp[n]; +} diff --git a/c/0283-move-zeroes.c b/c/0283-move-zeroes.c new file mode 100644 index 000000000..0e2039579 --- /dev/null +++ b/c/0283-move-zeroes.c @@ -0,0 +1,12 @@ +void moveZeroes(int* nums, int numsSize){ + int pos = 0; + + for(int i=0; i s) + if(word == NULL) + { + return false; + } + + HASH_FIND_INT(charToWordMap, &ch, charToWordEntry); + HASH_FIND_STR(wordToCharMap, word, wordToCharEntry); + + // If the char does exist in the map and the mapping is not the current word + if(charToWordEntry && strcmp(charToWordEntry->word, word) != 0) + { + return false; + } + + // If the word does exist in the map and the mapping is not the current char + if(wordToCharEntry && wordToCharEntry->ch != ch) + { + return false; + } + + /* Setup hash entries */ + charToWordEntry = (charToWord*)malloc(sizeof(charToWord)); + charToWordEntry->ch = ch; + charToWordEntry->word = word; + + wordToCharEntry = (wordToChar*)malloc(sizeof(wordToChar)); + wordToCharEntry->word = word; + wordToCharEntry->ch = ch; + + /* Add entries to the hashes */ + HASH_ADD_INT(charToWordMap, ch, charToWordEntry); + HASH_ADD_STR(wordToCharMap, word, wordToCharEntry); + + // Move to the next word + word = strtok(NULL, " "); + } + + // If there is any words left (s > pattern) + if(word != NULL) + { + return false; + } + + return true; +} diff --git a/c/0290-word-pattern.c b/c/0290-word-pattern.c new file mode 100644 index 000000000..d68203583 --- /dev/null +++ b/c/0290-word-pattern.c @@ -0,0 +1,72 @@ +typedef struct charToWord { + int ch; /* we'll use this field as the key */ + char* word; + UT_hash_handle hh; /* makes this structure hashable */ +}charToWord; + +typedef struct wordToChar { + char* word; /* we'll use this field as the key */ + int ch; + UT_hash_handle hh; /* makes this structure hashable */ +}wordToChar; + +bool wordPattern(char * pattern, char * s){ + charToWord* charToWordMap = NULL; + wordToChar* wordToCharMap = NULL; + char* word = NULL; + + // Get the first word + word = strtok(s, " "); + + for(size_t i = 0; i < strlen(pattern); i++) + { + charToWord* charToWordEntry = NULL; + wordToChar* wordToCharEntry = NULL; + int ch = pattern[i]; + + // If there is no words left (pattern > s) + if(word == NULL) + { + return false; + } + + HASH_FIND_INT(charToWordMap, &ch, charToWordEntry); + HASH_FIND_STR(wordToCharMap, word, wordToCharEntry); + + // If the char does exist in the map and the mapping is not the current word + if(charToWordEntry && strcmp(charToWordEntry->word, word) != 0) + { + return false; + } + + // If the word does exist in the map and the mapping is not the current char + if(wordToCharEntry && wordToCharEntry->ch != ch) + { + return false; + } + + /* Setup hash entries */ + charToWordEntry = (charToWord*)malloc(sizeof(charToWord)); + charToWordEntry->ch = ch; + charToWordEntry->word = word; + + wordToCharEntry = (wordToChar*)malloc(sizeof(wordToChar)); + wordToCharEntry->word = word; + wordToCharEntry->ch = ch; + + /* Add entries to the hashes */ + HASH_ADD_INT(charToWordMap, ch, charToWordEntry); + HASH_ADD_STR(wordToCharMap, word, wordToCharEntry); + + // Move to the next word + word = strtok(NULL, " "); + } + + // If there is any words left (s > pattern) + if(word != NULL) + { + return false; + } + + return true; +} diff --git a/c/0297-serialize-and-deserialize-binary-tree.c b/c/0297-serialize-and-deserialize-binary-tree.c new file mode 100644 index 000000000..60875f7cc --- /dev/null +++ b/c/0297-serialize-and-deserialize-binary-tree.c @@ -0,0 +1,88 @@ +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * struct TreeNode *left; + * struct TreeNode *right; + * }; + */ +#define DATA_ALLOCATION_SIZE 10000 + +typedef struct TreeNode TreeNode; + +void dfs_encode(TreeNode* node, char** data, int* dataIndex, int* dataSize) +{ + // Check if new space reallocation is needed + if((*dataIndex) + 10 >= *dataSize) // (+10) to add a little bit of margin for any data to be written in this function scope + { + // Reallocate data string for a bigger space + *dataSize += DATA_ALLOCATION_SIZE; + *data = (char*)realloc(*data, *dataSize * sizeof(char)); + } + + if(!node) + { + (*data)[(*dataIndex)++] = 'N'; + (*data)[(*dataIndex)++] = ','; + return; + } + + // Returns total number of characters written is returned excluding the null-character + int charsWritten = sprintf(*data + *dataIndex, "%d", node->val); + *dataIndex += charsWritten; + (*data)[(*dataIndex)++] = ','; // This is actually is written in-place of the null terminator + + dfs_encode(node->left, data, dataIndex, dataSize); + dfs_encode(node->right, data, dataIndex, dataSize); +} + +TreeNode* dfs_decode(char** data) +{ + // Reached end of the string + if((*data)[0] == '\0') + { + return NULL; + } + else if((*data)[0] == 'N') + { + // Skip two chars ('N' and ',') + *data += 2; + return NULL; + } + + TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode)); + char* endPtr = NULL; // Points to the first char after the numeric value + + // Set the node value + node->val = strtol(*data, &endPtr, 10); + *data = endPtr + 1; // (+1) to skip the ',' + + node->left = dfs_decode(data); + node->right = dfs_decode(data); + + return node; +} + +/** Encodes a tree to a single string. */ +char* serialize(struct TreeNode* root) { + // Allocate the maximum possible number of nodes + int dataSize = DATA_ALLOCATION_SIZE; + char* data = (char*)malloc(dataSize * sizeof(char)); + int dataIndex = 0; // points to the first empty space in the data string + + dfs_encode(root, &data, &dataIndex, &dataSize); + // Add the null terminator + data[dataIndex] = '\0'; + + return data; +} + +/** Decodes your encoded data to tree. */ +struct TreeNode* deserialize(char* data) { + + return dfs_decode(&data); +} + +// Your functions will be called as such: +// char* data = serialize(root); +// deserialize(data); diff --git a/c/0300-longest-increasing-subsequence.c b/c/0300-longest-increasing-subsequence.c new file mode 100644 index 000000000..185f969ef --- /dev/null +++ b/c/0300-longest-increasing-subsequence.c @@ -0,0 +1,26 @@ +/* +Given an integer array nums, return the length of the longest strictly increasing subsequence. +Time: O(nlog(n)) +Space: O(n) +*/ + +int lengthOfLIS(int* nums, int numsSize){ + int max=0, mid, i, j; + int* dp = calloc(numsSize, sizeof(int)); // dp[k] is the minimal last value of a subsequence of size k+1; + dp[0] = nums[0]; + for (int k=0; ksize = numsSize; + n->sums = (int*)malloc(sizeof(int) * numsSize); + + // Pre calculate the sum of each index from 0. + n->sums[0] = nums[0]; + for(int i = 1; i < numsSize; i++){ + n->sums[i] = n->sums[i-1] + nums[i]; + } + + return n; +} + +int numArraySumRange(NumArray* obj, int left, int right) { + assert(obj); + if (left == 0) { + return obj->sums[right]; + } + return obj->sums[right] - obj->sums[left - 1]; +} + +void numArrayFree(NumArray* obj) { + if (obj) { + free(obj->sums); + free(obj); + } +} + +/** + * Your NumArray struct will be instantiated and called as such: + * NumArray* obj = numArrayCreate(nums, numsSize); + * int param_1 = numArraySumRange(obj, left, right); + + * numArrayFree(obj); +*/ diff --git a/c/0309-best-time-to-buy-and-sell-stock-with-cooldown.c b/c/0309-best-time-to-buy-and-sell-stock-with-cooldown.c new file mode 100644 index 000000000..2e9eb7b2f --- /dev/null +++ b/c/0309-best-time-to-buy-and-sell-stock-with-cooldown.c @@ -0,0 +1,18 @@ +int maxProfit(int* prices, int pricesSize){ + int sold = 0; + int hold = INT_MIN; + int rest = 0; + + for (int i = 0; i < pricesSize; i++) { + int prevSold = sold; + sold = hold + prices[i]; + hold = max(hold, rest - prices[i]); + rest = max(rest, prevSold); + } + return max(sold, rest); +} + +// C doesn't have a built-in max function +int max(int a, int b) { + return (a > b) ? a : b; +} \ No newline at end of file diff --git a/c/0312-burst-balloons.c b/c/0312-burst-balloons.c new file mode 100644 index 000000000..b10b93b5c --- /dev/null +++ b/c/0312-burst-balloons.c @@ -0,0 +1,30 @@ +int max(int a, int b) { + return (a > b) ? a : b; +} + +int maxCoins(int* nums, int numsSize) { + // Add padding of 1 to both ends of the array + int n = numsSize + 2; + int paddedNums[n]; + paddedNums[0] = paddedNums[n - 1] = 1; + for (int i = 1; i < n - 1; i++) { + paddedNums[i] = nums[i - 1]; + } + + // Create a 2D DP array to store the results + int dp[n][n]; + memset(dp, 0, sizeof(dp)); + + // Start dynamic programming process + for (int len = 2; len < n; len++) { + for (int left = 0; left < n - len; left++) { + int right = left + len; + for (int k = left + 1; k < right; k++) { + dp[left][right] = max(dp[left][right], + paddedNums[left] * paddedNums[k] * paddedNums[right] + dp[left][k] + dp[k][right]); + } + } + } + + return dp[0][n - 1]; +} diff --git a/c/0322-coin-change.c b/c/0322-coin-change.c new file mode 100644 index 000000000..c3a1ef107 --- /dev/null +++ b/c/0322-coin-change.c @@ -0,0 +1,28 @@ +/* +Return the fewest number of coins that you need to make up that amount. +Time; O(nm) where n is the amount desired and m the number of coins +Space: O(n) +*/ + +int min(unsigned int a, int b) { + return a=coins[j] && cpt>dp[i-coins[j]]) + cpt = dp[i-coins[j]]; + } + if (cpt!=UINT_MAX) + dp[i] = cpt+1; + } + return dp[amount]==UINT_MAX?-1:dp[amount]; +} diff --git a/c/0329-longest-increasing-path-in-a-matrix.c b/c/0329-longest-increasing-path-in-a-matrix.c new file mode 100644 index 000000000..b6061179b --- /dev/null +++ b/c/0329-longest-increasing-path-in-a-matrix.c @@ -0,0 +1,40 @@ +/* +Given an m x n integers matrix, return the length of the longest increasing path in matrix. +Time; O(n*m) +Space; O(n*m) + +*/ + +int max(int a, int b) { + return a>b?a:b; +} + +int dfs(int** matrix, int** dp, int n, int m, int i, int j) { + if (dp[i][j]!=0) + return dp[i][j]; + int cpt=0; + if (i>0 && matrix[i-1][j]>matrix[i][j]) + cpt = max(cpt, dfs(matrix, dp, n, m, i-1, j)); + if (j>0 && matrix[i][j-1]>matrix[i][j]) + cpt = max(cpt, dfs(matrix, dp, n, m, i, j-1)); + if (i<(n-1) && matrix[i+1][j]>matrix[i][j]) + cpt = max(cpt, dfs(matrix, dp, n, m, i+1, j)); + if (j<(m-1) && matrix[i][j+1]>matrix[i][j]) + cpt = max(cpt, dfs(matrix, dp, n, m, i, j+1)); + dp[i][j] = cpt+1; + return cpt+1; +} + +int longestIncreasingPath(int** matrix, int matrixSize, int* matrixColSize){ + int** dp = malloc(sizeof(int*)*matrixSize); + int m = *matrixColSize, i, j; + for (i=0; i> 1] + (i & 1); + + return ret; +} diff --git a/c/0343-integer-break.c b/c/0343-integer-break.c new file mode 100644 index 000000000..167a75145 --- /dev/null +++ b/c/0343-integer-break.c @@ -0,0 +1,20 @@ +int integerBreak(int n) { + if (n <= 2) { + return 1; + } + + int maxProducts[n + 1]; + maxProducts[0] = 0; + maxProducts[1] = 0; + maxProducts[2] = 1; + + for (int i = 3; i <= n; i++) { + maxProducts[i] = 0; + for (int j = 1; j <= i / 2; j++) { + maxProducts[i] = fmax(maxProducts[i], j * (i - j)); + maxProducts[i] = fmax(maxProducts[i], j * maxProducts[i - j]); + } + } + + return maxProducts[n]; +} diff --git a/c/0344-reverse-string.c b/c/0344-reverse-string.c new file mode 100644 index 000000000..07fac3788 --- /dev/null +++ b/c/0344-reverse-string.c @@ -0,0 +1,15 @@ +/* +Write a function that reverses a string. The input string is given as an array of characters s. + +Space: O(1) +Time: O(n) +*/ + +void reverseString(char* s, int sSize){ + int h = sSize/2; + for (int i=0; icount - ((Heap*)x)->count; +} + +/** + * Note: The returned array must be malloced, assume caller calls free(). + */ +int* topKFrequent(int* nums, int numsSize, int k, int* returnSize){ + int hash[20001] = {0}; + Heap* heap; + int *res; + int i, j; + int c = 0; + + for (i = 0; i < numsSize; i++) { + hash[nums[i]+10000]++; + if (hash[nums[i]+10000]) { + c++; + } + } + + heap = (Heap*)malloc(sizeof(Heap) * c); + memset(heap, 0, sizeof(Heap) * c); + + c = 0; + for (i = 0; i < 20001; i++) { + if(hash[i] > 0) { + heap[c].num = i - 10000; + heap[c].count = hash[i]; + c++; + } + } + qsort(heap, c, sizeof(Heap), compareHeap); + + c = 0; + res = (int*)malloc(sizeof(int) * k); + for (i = 0; i < k; i++) { + res[c++] = heap[i].num; + } + + *returnSize = c; + return res; +} diff --git a/c/0367-valid-perfect-square.c b/c/0367-valid-perfect-square.c new file mode 100644 index 000000000..26283a772 --- /dev/null +++ b/c/0367-valid-perfect-square.c @@ -0,0 +1,17 @@ +bool isPerfectSquare(int num) { + int low = 0; + int high = num; + + while(low <= high){ + long long int mid = low + (high - low)/2; + + if(mid * mid == num) + return true; + + if(mid * mid < num) + low = mid + 1; + else + high = mid - 1; + } + return false; +} \ No newline at end of file diff --git a/c/0371-sum-of-two-integers.c b/c/0371-sum-of-two-integers.c new file mode 100644 index 000000000..99e683d5f --- /dev/null +++ b/c/0371-sum-of-two-integers.c @@ -0,0 +1,8 @@ +int getSum(int a, int b){ + while (b != 0) { + int temp = a ^ b; + b = (unsigned)(a & b) << 1; + a = temp; + } + return a; +} \ No newline at end of file diff --git a/c/0374-guess-number-higher-or-lower.c b/c/0374-guess-number-higher-or-lower.c new file mode 100644 index 000000000..7ac0b9c11 --- /dev/null +++ b/c/0374-guess-number-higher-or-lower.c @@ -0,0 +1,21 @@ +/* +I pick a number from 1 to n. You have to guess which number I picked. + +Space: O(1) +Time: O(log(n)) +*/ + +long guess_bis(long min, long max){ + long m = (max+min)/2; + int tmp = guess(m); + if (tmp==0) + return m; + else if (tmp<0) + return guess_bis(min,m-1); + else + return guess_bis(m+1,max); +} + +long guessNumber(long n){ + return guess_bis(0,n); +} diff --git a/c/0377-combination-sum-iv.c b/c/0377-combination-sum-iv.c new file mode 100644 index 000000000..9543eac6b --- /dev/null +++ b/c/0377-combination-sum-iv.c @@ -0,0 +1,18 @@ +int combinationSum4(int* nums, int numsSize, int target) { + uint64_t dp[target + 1]; // Using uint64_t to avoid overflow + for (int i = 0; i <= target; i++) { + dp[i] = 0; + } + + dp[0] = 1; + + for (int i = 1; i <= target; i++) { + for (int j = 0; j < numsSize; j++) { + if (i - nums[j] >= 0) { + dp[i] += dp[i - nums[j]]; + } + } + } + + return (int)dp[target]; +} diff --git a/c/0392-is-subsequence.c b/c/0392-is-subsequence.c new file mode 100644 index 000000000..b91f6bc93 --- /dev/null +++ b/c/0392-is-subsequence.c @@ -0,0 +1,44 @@ +/** + * Given two strings s and t, return true if s is a subsequence of t, or false + * otherwise. A subsequence of a string is a new string that is formed from the + * original string by deleting some (can be none) of the characters without + * disturbing the relative positions of the remaining characters. (i.e., "ace" + * is a subsequence of "abcde" while "aec" is not). + * + * Example 1: + + * Input: s = "abc", t = "ahbgdc" + * Output: true + * + * Example 2: + * + * Input: s = "axc", t = "ahbgdc" + * Output: false + * + * Constraints: + * + * 0 <= s.length <= 100 + * 0 <= t.length <= 104 + * s and t consist only of lowercase English letters. + * + * Space: O(1) + * Time: O(n) + */ + +bool isSubsequence(char * s, char * t){ + char *s_char = s; + char *t_char = t; + + while (*s_char != 0) { + if (*t_char == 0) { + return false; + } + else if (*s_char == *t_char) { + ++s_char; + } + + ++t_char; + } + + return true; +} diff --git a/c/0409-longest-palindrome.c b/c/0409-longest-palindrome.c new file mode 100644 index 000000000..57802057d --- /dev/null +++ b/c/0409-longest-palindrome.c @@ -0,0 +1,37 @@ +/** + * Given a string s which consists of lowercase or uppercase letters, return the + * length of the longest palindrome that can be built with those letters. + * + * Letters are case sensitive, for example, "Aa" is not considered a palindrome + * here. + * + * Constraints: + * + * 1 <= s.length <= 2000 + * s consists of lowercase and/or uppercase English letters only. + * + * Space: O(1) + * Time: O(n) + */ + +int longestPalindrome(char *s) { + int chars_seen[128] = { 0 }; + + for (char *s_char = s; *s_char; ++s_char) + ++chars_seen[*s_char]; + + int odd = 0; + int sum = 0; + for (int i = 'A'; i <= 'z'; ++i) { + if (chars_seen[i] % 2) { + odd = 1; + sum += chars_seen[i] - 1; + } else { + sum += chars_seen[i]; + } + } + + sum += odd; + + return sum; +} diff --git a/c/0416-partition-equal-subset-sum.c b/c/0416-partition-equal-subset-sum.c new file mode 100644 index 000000000..9b39d66e4 --- /dev/null +++ b/c/0416-partition-equal-subset-sum.c @@ -0,0 +1,28 @@ +bool canPartition(int* nums, int numsSize) { + int totalSum = 0; + for (int i = 0; i < numsSize; i++) { + totalSum += nums[i]; + } + + if (totalSum % 2 != 0) { + return false; // If the total sum is odd, we cannot partition equally + } + + int targetSum = totalSum / 2; + bool dp[targetSum + 1]; // dp[i] represents whether a subset with sum i is possible + + // Initialize dp array + for (int i = 0; i <= targetSum; i++) { + dp[i] = false; + } + dp[0] = true; // Empty subset can always achieve sum 0 + + // Dynamic programming approach + for (int i = 0; i < numsSize; i++) { + for (int j = targetSum; j >= nums[i]; j--) { + dp[j] = dp[j] || dp[j - nums[i]]; + } + } + + return dp[targetSum]; +} diff --git a/c/0417-pacific-atlantic-water-flow.c b/c/0417-pacific-atlantic-water-flow.c new file mode 100644 index 000000000..826266b6e --- /dev/null +++ b/c/0417-pacific-atlantic-water-flow.c @@ -0,0 +1,83 @@ + + +/** + * Return an array of arrays of size *returnSize. + * The sizes of the arrays are returned as *returnColumnSizes array. + * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free(). + */ + +int directions[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; +bool pacific_reach[200][200]; +bool atlantic_reach[200][200]; + +void resetSet(bool set[200][200]) { + for (int i = 0; i < 200; i++) { + for (int j = 0; j < 200; j++) { + set[i][j] = false; + } + } +} + +void dfs(int row, int col, bool set[200][200], int** heights, int rows, int cols) { + set[row][col] = true; + int a = 0; + int b = 0; + for (int i = 0; i < 4; i++) { + a = directions[i][0] + row; + b = directions[i][1] + col; + if ((a >= 0) && (a < rows) && (b >= 0) && (b < cols) && (!set[a][b])) { + if (heights[a][b] >= heights[row][col]) { + dfs(a, b, set, heights, rows, cols); + } + } + } +} + +int** pacificAtlantic(int** heights, int heightsSize, int* heightsColSize, int* returnSize, int** returnColumnSizes) { + + int rows = heightsSize; + int cols = heightsColSize[0]; + + resetSet(pacific_reach); + resetSet(atlantic_reach); + + for (int row = 0; row < rows; row++) { + dfs(row, 0, pacific_reach, heights, rows, cols); + dfs(row, cols - 1, atlantic_reach, heights, rows, cols); + } + for (int col = 0; col < cols; col++) { + dfs(0, col, pacific_reach, heights, rows, cols); + dfs(rows - 1, col, atlantic_reach, heights, rows, cols); + } + + int res = 0; + for (int row = 0; row < rows; row++) { + for (int col = 0; col < cols; col++) { + if ((pacific_reach[row][col] == true) && (atlantic_reach[row][col] == true)) { + res += 1; + } + } + } + + *returnSize = res; + returnColumnSizes[0] = (int*)malloc(res * sizeof(int)); + for (int i = 0; i < res; i++) { + returnColumnSizes[0][i] = 2; + } + + int** results = (int**)malloc(res * sizeof(int*)); + res = 0; + for (int row = 0; row < rows; row++) { + for (int col = 0; col < cols; col++) { + if ((pacific_reach[row][col] == true) && (atlantic_reach[row][col] == true)) { + int* buffer = (int*)malloc(2 * sizeof(int)); + buffer[0] = row; + buffer[1] = col; + results[res] = buffer; + res += 1; + } + } + } + + return results; +} \ No newline at end of file diff --git a/c/0424-longest-repeating-character-replacement.c b/c/0424-longest-repeating-character-replacement.c new file mode 100644 index 000000000..5b6ea100b --- /dev/null +++ b/c/0424-longest-repeating-character-replacement.c @@ -0,0 +1,20 @@ +int characterReplacement(char * s, int k){ + int count[26] = {0}; + int left = 0, right = 0, maxCount = 0; + + while (right < strlen(s)) { + count[s[right] - 'A']++; + maxCount = max(maxCount, count[s[right] - 'A']); + right++; + if (right - left - maxCount > k) { + count[s[left] - 'A']--; + left++; + } + } + return right - left; +} + +// C doesn't have a built-in max function +int max(int a, int b) { + return (a > b) ? a : b; +} diff --git a/c/0435-non-overlapping-intervals.c b/c/0435-non-overlapping-intervals.c new file mode 100644 index 000000000..08ee31e29 --- /dev/null +++ b/c/0435-non-overlapping-intervals.c @@ -0,0 +1,41 @@ +// Interval structure +struct Interval { + int start; + int end; +}; + +// Function to compare intervals for sorting +int compareIntervals(const void* a, const void* b) { + return ((struct Interval*)a)->end - ((struct Interval*)b)->end; +} + +int eraseOverlapIntervals(int** intervals, int intervalsSize, int* intervalsColSize) { + if (intervalsSize <= 1) { + return 0; + } + + // Create an array of Interval structures + struct Interval* sortedIntervals = (struct Interval*)malloc(sizeof(struct Interval) * intervalsSize); + for (int i = 0; i < intervalsSize; i++) { + sortedIntervals[i].start = intervals[i][0]; + sortedIntervals[i].end = intervals[i][1]; + } + + // Sort intervals based on end times + qsort(sortedIntervals, intervalsSize, sizeof(struct Interval), compareIntervals); + + int end = sortedIntervals[0].end; + int nonOverlapCount = 1; + + for (int i = 1; i < intervalsSize; i++) { + if (sortedIntervals[i].start >= end) { + end = sortedIntervals[i].end; + nonOverlapCount++; + } + } + + free(sortedIntervals); + + // Return the count of overlapping intervals to be removed + return intervalsSize - nonOverlapCount; +} diff --git a/c/0438-find-all-anagrams-in-a-string.c b/c/0438-find-all-anagrams-in-a-string.c new file mode 100644 index 000000000..26425a778 --- /dev/null +++ b/c/0438-find-all-anagrams-in-a-string.c @@ -0,0 +1,47 @@ +#define TO_INDEX(c) c - 'a' + +bool isEquals(int* a, int* b, int n) { + for (int i = 0; i < n; i++) { + if (a[i] != b[i]) { + return false; + } + } + return true; +} + +/** + * Note: The returned array must be malloced, assume caller calls free(). + */ +int* findAnagrams(char * s, char * p, int* returnSize) { + int sSize = strlen(s); + int pSize = strlen(p); + int pFreq[26] = {0}; + int sFreq[26] = {0}; + int i, count; + int* ans; + + if (sSize < pSize) { + *returnSize = 0; + return ans; + } + + for (i = 0; i < pSize; i++) { + pFreq[TO_INDEX(p[i])]++; + sFreq[TO_INDEX(s[i])]++; + } + + count = 0; + ans = (int*)calloc(sSize - pSize + 1, sizeof(int)); + for (i = 0; i <= sSize - pSize; i++) { + if (i > 0) { + sFreq[TO_INDEX(s[i - 1])]--; + sFreq[TO_INDEX(s[i + pSize - 1])]++; + } + if (isEquals(sFreq, pFreq, 26)) { + ans[count++] = i; + } + } + *returnSize = count; + + return ans; +} diff --git a/c/0448-Find-All-Numbers-Disappeared-in-an-Array.c b/c/0448-Find-All-Numbers-Disappeared-in-an-Array.c new file mode 100644 index 000000000..1bc9efe88 --- /dev/null +++ b/c/0448-Find-All-Numbers-Disappeared-in-an-Array.c @@ -0,0 +1,25 @@ +/** + * Note: The returned array must be malloced, assume caller calls free(). + */ +int* findDisappearedNumbers(int* nums, int numsSize, int* returnSize){ + int* disappearedNumbers = (int*)malloc(numsSize * sizeof(int)); // Allocate space for the worst case + *returnSize = 0; // Points to the first empty index in the array + + // Mark available numbers as negative + for(int i = 0; i < numsSize; i++) + { + int index = abs(nums[i]); + nums[index - 1] = -1 * abs(nums[index - 1]); + } + + // Find unmarked numbers (disappeared numbers) + for(int i = 0; i < numsSize; i++) + { + if(nums[i] > 0) + { + disappearedNumbers[(*returnSize)++] = i + 1; + } + } + + return disappearedNumbers; +} diff --git a/c/0448-find-all-numbers-disappeared-in-an-array.c b/c/0448-find-all-numbers-disappeared-in-an-array.c new file mode 100644 index 000000000..1bc9efe88 --- /dev/null +++ b/c/0448-find-all-numbers-disappeared-in-an-array.c @@ -0,0 +1,25 @@ +/** + * Note: The returned array must be malloced, assume caller calls free(). + */ +int* findDisappearedNumbers(int* nums, int numsSize, int* returnSize){ + int* disappearedNumbers = (int*)malloc(numsSize * sizeof(int)); // Allocate space for the worst case + *returnSize = 0; // Points to the first empty index in the array + + // Mark available numbers as negative + for(int i = 0; i < numsSize; i++) + { + int index = abs(nums[i]); + nums[index - 1] = -1 * abs(nums[index - 1]); + } + + // Find unmarked numbers (disappeared numbers) + for(int i = 0; i < numsSize; i++) + { + if(nums[i] > 0) + { + disappearedNumbers[(*returnSize)++] = i + 1; + } + } + + return disappearedNumbers; +} diff --git a/c/0463-island-perimeter.c b/c/0463-island-perimeter.c new file mode 100644 index 000000000..f2ac70de4 --- /dev/null +++ b/c/0463-island-perimeter.c @@ -0,0 +1,23 @@ +/* +Determine the perimeter of the island. + +Space: O(1) +Time: O(n²) +*/ + + +int islandPerimeter(int** grid, int gridSize, int* gridColSize){ + int stripes = 0; + for (int i=0; i0 && grid[i-1][j]==1) // Common stripe at the top + stripes -= 2; + if (j>0 && grid[i][j-1]==1) // Common stripe on the left + stripes -= 2; + } + } + } + return stripes; +} diff --git a/c/0494-target-sum.c b/c/0494-target-sum.c new file mode 100644 index 000000000..72897087c --- /dev/null +++ b/c/0494-target-sum.c @@ -0,0 +1,29 @@ +int findTargetSumWays(int* nums, int numsSize, int target) { + int sum = 0; + for (int i = 0; i < numsSize; i++) { + sum += nums[i]; + } + if (target > sum || target < -sum) { + return 0; + } + + int n = numsSize; + int dp[n + 1][2 * sum + 1]; + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= 2 * sum; j++) { + dp[i][j] = 0; + } + } + dp[0][sum] = 1; + + for (int i = 0; i < n; i++) { + for (int j = nums[i]; j <= 2 * sum - nums[i]; j++) { + if (dp[i][j]) { + dp[i + 1][j + nums[i]] += dp[i][j]; + dp[i + 1][j - nums[i]] += dp[i][j]; + } + } + } + + return dp[n][sum + target]; +} diff --git a/c/0496-next-greater-element-i.c b/c/0496-next-greater-element-i.c new file mode 100644 index 000000000..736905f9a --- /dev/null +++ b/c/0496-next-greater-element-i.c @@ -0,0 +1,92 @@ +#define INIT_HASH_SIZE 1024 + +// Represent an element in a hash table. +typedef struct Hash { + int key; + int value; + struct Hash *next; +} Hash; + +// Use -1 as non-existed key, +// as 0 <= nums1[i], nums2[i] <= 104 +Hash *InitHash() { + Hash *h = (Hash*)calloc(INIT_HASH_SIZE, sizeof(Hash)); + assert(h); + for (int i = 0; i < INIT_HASH_SIZE; i++) { + h[i].key = -1; + } + return h; +} + +Hash *NewHash(int key, int value) { + Hash *h = (Hash*)malloc(sizeof(Hash)); + assert(h); + h->key = key; + h->value = value; + h->next = NULL; + return h; +} + +int HashKey(int key) { + while(key < 0) key += INIT_HASH_SIZE; + return (key % INIT_HASH_SIZE); +} + +void SetHash(Hash *root, int key, int value) { + assert(root); + Hash *h = &root[HashKey(key)]; + if (h->key == -1) { + h->key = key; + h->value = value; + } else if (h->key == key) { + h->value = value; + } else { + while (h) { + if (h->key == key) { + h->value = value; + return; + } else if (!h->next) { + h->next = NewHash(key, value); + return; + } + h = h->next; + } + } +} + +// If hash not round return -1 on purpose. +int GetHash(Hash *root, int key) { + Hash *h = &root[HashKey(key)]; + while(h) { + if (h->key == key) { + return h->value; + } + h = h->next; + } + return -1; +} + +/** + * Note: The returned array must be malloced, assume caller calls free(). + */ +int* nextGreaterElement(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize){ + Hash *hash = InitHash(); + int *res = (int*)malloc(nums1Size*sizeof(int)); + int i, j; + + for (i = 0; i < nums2Size; i++) { + for (j = i + 1; j < nums2Size; j++) { + if (nums2[j] > nums2[i]) { + SetHash(hash, nums2[i], nums2[j]); + break; + } + } + } + + for (i = 0; i < nums1Size; i++) { + res[i] = GetHash(hash, nums1[i]); + } + + *returnSize = nums1Size; + return res; +} diff --git a/c/0518-coin-change-ii.c b/c/0518-coin-change-ii.c new file mode 100644 index 000000000..dbbff57e0 --- /dev/null +++ b/c/0518-coin-change-ii.c @@ -0,0 +1,24 @@ +/* +You are given an integer array coins representing coins of different +denominations and an integer amount representing a total amount of money. +Return the number of combinations that make up that amount. + +Space: O(n) where n is the amount +Time: O(np) where p is the number of coins +*/ + +int change(int amount, int* coins, int coinsSize){ + if (coinsSize==0) { // Take care of extreme cases + return amount==0; + } + int* dp = calloc((amount+1), sizeof(int)); + dp[0] = 1; // One way to give zero + for (int i=0; ikey = key; + entry->value = longUrl; + HASH_ADD_KEYPTR(hh, root, key, strlen(key), entry); + } else { + free(key); + } + return entry->key; +} + +/** Decodes a shortened URL to its original URL. */ +char* decode(char* shortUrl) { + Hash *entry = NULL; + HASH_FIND_STR(root, shortUrl, entry); + if (entry) { + return entry->value; + } + return NULL; +} + +// Your functions will be called as such: +// char* s = encode(s); +// decode(s); + diff --git a/c/0540-single-element-in-a-sorted-array.c b/c/0540-single-element-in-a-sorted-array.c new file mode 100644 index 000000000..1fb90cc38 --- /dev/null +++ b/c/0540-single-element-in-a-sorted-array.c @@ -0,0 +1,18 @@ +int singleNonDuplicate(int* nums, int numsSize) { + int start = 0; + int end = numsSize- 1; + + while(start < end){ + + int mid = start + (end - start)/2; + + if((mid % 2 == 0 && nums[mid] == nums[mid+1]) || (mid % 2 != 0 && nums[mid] == nums[mid-1])){ + start = mid + 1; + } + else{ + end = mid; + } + } + + return nums[start]; +} \ No newline at end of file diff --git a/c/0543-diameter-of-binary-tree.c b/c/0543-diameter-of-binary-tree.c new file mode 100644 index 000000000..da5acc352 --- /dev/null +++ b/c/0543-diameter-of-binary-tree.c @@ -0,0 +1,33 @@ +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * struct TreeNode *left; + * struct TreeNode *right; + * }; + */ + +int max(int a, int b) { + if (a > b) { + return a; + } + return b; +} + +int recur(struct TreeNode* root, int* maxDiameter) { + if (root == NULL) { + return 0; + } + int leftHeight = recur(root -> left, maxDiameter); + int rightHeight = recur(root -> right, maxDiameter); + *maxDiameter = max(*maxDiameter, leftHeight + rightHeight); + return max(leftHeight, rightHeight) + 1; +} + +int diameterOfBinaryTree(struct TreeNode* root) { + int maxDiameter = 0; + + recur(root, &maxDiameter); + + return maxDiameter; +} diff --git a/c/0554-Brick-Wall.c b/c/0554-Brick-Wall.c new file mode 100644 index 000000000..7cb1713c5 --- /dev/null +++ b/c/0554-Brick-Wall.c @@ -0,0 +1,45 @@ +#define max(x, y) ((x) > (y) ? (x) : (y)) + +typedef struct hash_entry { + int position; /* we'll use this field as the key */ + int gapCount; + UT_hash_handle hh; /* makes this structure hashable */ +} hash_entry; + +int leastBricks(int** wall, int wallSize, int* wallColSize){ + hash_entry* wallGapCountMap = NULL; + + for(int r = 0; r < wallSize; r++) + { + int position = 0; + for(int b = 0; b < *(wallColSize + r) - 1; b++) + { + position += wall[r][b]; + + hash_entry* retrievedMapEntry; + HASH_FIND_INT(wallGapCountMap, &position, retrievedMapEntry); + + // If the position already exists in the map then increment its gap count + if(retrievedMapEntry) + { + retrievedMapEntry->gapCount += 1; + } + else + { + // If the position doesn't exist in the map then create a new map entry for it and add it to the map + hash_entry* mapEntryToAdd = (hash_entry*)malloc(sizeof(hash_entry)); + mapEntryToAdd->position = position; + mapEntryToAdd->gapCount = 1; + HASH_ADD_INT(wallGapCountMap, position, mapEntryToAdd); + } + } + } + + int maxGap = 0; + for (hash_entry* retrievedMapEntry = wallGapCountMap; retrievedMapEntry != NULL; retrievedMapEntry = retrievedMapEntry->hh.next) + { + maxGap = max(maxGap, retrievedMapEntry->gapCount); + } + + return wallSize - maxGap; +} diff --git a/c/0554-brick-wall.c b/c/0554-brick-wall.c new file mode 100644 index 000000000..7cb1713c5 --- /dev/null +++ b/c/0554-brick-wall.c @@ -0,0 +1,45 @@ +#define max(x, y) ((x) > (y) ? (x) : (y)) + +typedef struct hash_entry { + int position; /* we'll use this field as the key */ + int gapCount; + UT_hash_handle hh; /* makes this structure hashable */ +} hash_entry; + +int leastBricks(int** wall, int wallSize, int* wallColSize){ + hash_entry* wallGapCountMap = NULL; + + for(int r = 0; r < wallSize; r++) + { + int position = 0; + for(int b = 0; b < *(wallColSize + r) - 1; b++) + { + position += wall[r][b]; + + hash_entry* retrievedMapEntry; + HASH_FIND_INT(wallGapCountMap, &position, retrievedMapEntry); + + // If the position already exists in the map then increment its gap count + if(retrievedMapEntry) + { + retrievedMapEntry->gapCount += 1; + } + else + { + // If the position doesn't exist in the map then create a new map entry for it and add it to the map + hash_entry* mapEntryToAdd = (hash_entry*)malloc(sizeof(hash_entry)); + mapEntryToAdd->position = position; + mapEntryToAdd->gapCount = 1; + HASH_ADD_INT(wallGapCountMap, position, mapEntryToAdd); + } + } + } + + int maxGap = 0; + for (hash_entry* retrievedMapEntry = wallGapCountMap; retrievedMapEntry != NULL; retrievedMapEntry = retrievedMapEntry->hh.next) + { + maxGap = max(maxGap, retrievedMapEntry->gapCount); + } + + return wallSize - maxGap; +} diff --git a/c/0560-subarray-sum-equals-k.c b/c/0560-subarray-sum-equals-k.c new file mode 100644 index 000000000..cc832a80d --- /dev/null +++ b/c/0560-subarray-sum-equals-k.c @@ -0,0 +1,78 @@ +#define INIT_HASH_SIZE 4096 + +// Represent an element in a hash table. +typedef struct Hash { + int key; + int count; + struct Hash *next; +} Hash; + +Hash *InitHash() { + Hash *h = (Hash*)calloc(INIT_HASH_SIZE, sizeof(Hash)); + assert(h); + return h; +} + +Hash *NewHash(int key) { + Hash *h = (Hash*)malloc(sizeof(Hash)); + assert(h); + h->key = key; + h->count = 1; + h->next = NULL; + return h; +} + +int HashKey(int key) { + while(key < 0) key += INIT_HASH_SIZE; + return (key % INIT_HASH_SIZE); +} + +void AddHash(Hash *root, int key) { + assert(root); + Hash *h = &root[HashKey(key)]; + if (h->key == 0 && h->count == 0) { + h->key = key; + h->count = 1; + } else if (h->key == key) { + h->count++; + } else { + while (h) { + if (h->key == key) { + h->count++; + return; + } else if (!h->next) { + h->next = NewHash(key); + return; + } + h = h->next; + } + } +} + +// If hash not round return 0 on purpose. +int GetHash(Hash *root, int key) { + Hash *h = &root[HashKey(key)]; + while(h) { + if (h->key == key) { + return h->count; + } + h = h->next; + } + return 0; +} + +int subarraySum(int* nums, int numsSize, int k){ + Hash* hash = InitHash(); + int sum = 0; + int res = 0; + int r = 0; + + AddHash(hash, 0); + for(int i = 0; i < numsSize; i++){ + sum += nums[i]; + res += GetHash(hash, sum - k); + AddHash(hash, sum); + } + + return res; +} diff --git a/c/0567-permutation-in-string.c b/c/0567-permutation-in-string.c new file mode 100644 index 000000000..62362caf5 --- /dev/null +++ b/c/0567-permutation-in-string.c @@ -0,0 +1,36 @@ +bool isPermutation(int *count) { + for (int i = 0; i < 26; i++) { + if (count[i] != 0) { + return false; + } + } + return true; +} + +bool checkInclusion(char * s1, char * s2){ + const int m = strlen(s1); + const int n = strlen(s2); + + if (m > n) { + return false; + } + + int count[26] = {0}; + for (int i = 0; i < m; i++) { + count[s1[i] - 'a']++; + count[s2[i] - 'a']--; + } + + if (isPermutation(count)) { + return true; + } + + for (int i = m; i < n; i++) { + count[s2[i] - 'a']--; + count[s2[i - m] - 'a']++; + if (isPermutation(count)) { + return true; + } + } + return false; +} \ No newline at end of file diff --git a/c/0572-subtree-of-another-tree.c b/c/0572-subtree-of-another-tree.c new file mode 100644 index 000000000..14767f1d1 --- /dev/null +++ b/c/0572-subtree-of-another-tree.c @@ -0,0 +1,34 @@ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * struct TreeNode *left; + * struct TreeNode *right; + * }; + */ + +bool isSame(struct TreeNode* p, struct TreeNode* q) { + + if (p == NULL && q == NULL) { + return true; + } + if ((p == NULL || q == NULL) || (p -> val != q -> val)) { + return false; + } + return (isSame(p -> left, q -> left) && isSame(p -> right, q -> right)); +} + + +bool isSubtree(struct TreeNode* root, struct TreeNode* subRoot){ + + if (root == NULL) { + return false; + } + + bool compareTree = false; + if (root -> val == subRoot -> val) { + compareTree = isSame(root, subRoot); + } + return (isSubtree(root -> left, subRoot) || isSubtree(root -> right, subRoot) || compareTree); +} diff --git a/c/0589-n-ary-tree-preorder-traversal.c b/c/0589-n-ary-tree-preorder-traversal.c new file mode 100644 index 000000000..0b951a22d --- /dev/null +++ b/c/0589-n-ary-tree-preorder-traversal.c @@ -0,0 +1,42 @@ +/** + * Given the root of an n-ary tree, return the preorder traversal of its nodes' + * values. + * + * Nary-Tree input serialization is represented in their level order traversal. + * Each group of children is separated by the null value (See examples) + * + * Constraints: + * + * The number of nodes in the tree is in the range [0, 104]. + * 0 <= Node.val <= 10^4 + * The height of the n-ary tree is less than or equal to 1000. + * + * Definition for a Node. + * struct Node { + * int val; + * int numChildren; + * struct Node** children; + * }; + * + * Note: The returned array must be malloced, assume caller calls free(). + * + * Space: O(n) + * Time: O(n) + */ + +void traverse(struct Node* root, int *ret, int* index) { + if (!root) + return; + + ret[(*index)++] = root->val; + + for (int i = 0; i < root->numChildren; ++i) + traverse(root->children[i], ret, index); +} + +int* preorder(struct Node* root, int* returnSize) { + int *ret = malloc(10000 * sizeof(int)); + *returnSize = 0; + traverse(root, ret, returnSize); + return ret; +} diff --git a/c/0605-can-place-flowers.c b/c/0605-can-place-flowers.c new file mode 100644 index 000000000..529f13fdf --- /dev/null +++ b/c/0605-can-place-flowers.c @@ -0,0 +1,31 @@ +/* +Given an integer array flowerbed containing 0's and 1's, where 0 means empty and 1 means not +empty, and an integer n, return if n new flowers can be planted in the flowerbed without +violating the no-adjacent-flowers rule. + +Space: O(1) +Time: O(n) +*/ + +bool canPlaceFlowers(int* flowerbed, int flowerbedSize, int n){ + int cpt = 0; // Count the number of flowers that can be added + int i=0; + while (i=n; +} diff --git a/c/0617-merge-two-binary-trees.c b/c/0617-merge-two-binary-trees.c new file mode 100644 index 000000000..fc3bedb3a --- /dev/null +++ b/c/0617-merge-two-binary-trees.c @@ -0,0 +1,18 @@ +/* +You are given two binary trees root1 and root2. +Return the merged tree + +Time: O(n) +Space: O(1) As we reuse the trees +*/ + +struct TreeNode* mergeTrees(struct TreeNode* root1, struct TreeNode* root2){ + if (root1==NULL) + return root2; + else if (root2==NULL) + return root1; + root1->val += root2->val; + root1->left = mergeTrees(root1->left, root2->left); + root1->right = mergeTrees(root1->right, root2->right); + return root1; +} diff --git a/c/0621-task-scheduler.c b/c/0621-task-scheduler.c new file mode 100644 index 000000000..9afa014ce --- /dev/null +++ b/c/0621-task-scheduler.c @@ -0,0 +1,23 @@ +int leastInterval(char *tasks, int tasksSize, int n) { + int taskCount[26] = {0}; + + for (int i = 0; i < tasksSize; i++) { + taskCount[tasks[i] - 'A']++; + } + + int maxTaskFreq = 0; + int numMaxTasks = 0; + + for (int i = 0; i < 26; i++) { + if (taskCount[i] > maxTaskFreq) { + maxTaskFreq = taskCount[i]; + numMaxTasks = 1; + } else if (taskCount[i] == maxTaskFreq) { + numMaxTasks++; + } + } + + int intervals = (maxTaskFreq - 1) * (n + 1) + numMaxTasks; + + return (intervals > tasksSize) ? intervals : tasksSize; +} diff --git a/c/0647-palindromic-substrings.c b/c/0647-palindromic-substrings.c new file mode 100644 index 000000000..502bc83a6 --- /dev/null +++ b/c/0647-palindromic-substrings.c @@ -0,0 +1,31 @@ +/* +Given a string s, return the number of palindromic substrings in it. +A string is a palindrome when it reads the same backward as forward. +A substring is a contiguous sequence of characters within the string. +Time: O(n^2) +Space: O(1) +*/ + +int countSubstrings(char * s){ + int cpt=0; + int x,y; + + for (int i=0; s[i]!='\0'; i++){ + cpt++; // s[i] is a palindrom + x=i-1; + y=i+1; + while (x>=0 && s[y]!='\0' && s[x]==s[y]){ // Odd length palindromic substring + cpt++; + x--; + y++; + } + x=i; + y=i+1; + while (x>=0 && s[y]!='\0' && s[x]==s[y]){ // Even length palindromic substring + cpt++; + x--; + y++; + } + } + return cpt; +} diff --git a/c/0665-non-decreasing-array.c b/c/0665-non-decreasing-array.c new file mode 100644 index 000000000..f35c9bb9c --- /dev/null +++ b/c/0665-non-decreasing-array.c @@ -0,0 +1,14 @@ +bool checkPossibility(int *nums, int numsSize) { + if (numsSize == 1) return true; + + char count = 0; + if (nums[0] > nums[1]) count++; + for (int i = 1; i < numsSize - 1; i++) + if (nums[i] > nums[i + 1]) { + if (++count > 1) return false; + if ((nums[i - 1] > nums[i + 1]) && (i < numsSize - 2) && + (nums[i + 2] < nums[i])) + return false; + } + return true; +} diff --git a/c/0673-number-of-longest-increasing-subsequence.c b/c/0673-number-of-longest-increasing-subsequence.c new file mode 100644 index 000000000..41a629c26 --- /dev/null +++ b/c/0673-number-of-longest-increasing-subsequence.c @@ -0,0 +1,34 @@ +int findNumberOfLIS(int* nums, int numsSize) { + if (numsSize == 0) { + return 0; + } + + int lengths[numsSize]; + int counts[numsSize]; + int max_length = 1; + int result = 0; + + for (int i = 0; i < numsSize; i++) { + lengths[i] = 1; + counts[i] = 1; + for (int j = 0; j < i; j++) { + if (nums[i] > nums[j]) { + if (lengths[j] + 1 > lengths[i]) { + lengths[i] = lengths[j] + 1; + counts[i] = counts[j]; + } else if (lengths[j] + 1 == lengths[i]) { + counts[i] += counts[j]; + } + } + } + max_length = fmax(max_length, lengths[i]); + } + + for (int i = 0; i < numsSize; i++) { + if (lengths[i] == max_length) { + result += counts[i]; + } + } + + return result; +} diff --git a/c/0678-valid-parenthesis-string.c b/c/0678-valid-parenthesis-string.c new file mode 100644 index 000000000..e10516751 --- /dev/null +++ b/c/0678-valid-parenthesis-string.c @@ -0,0 +1,35 @@ +/* + Time: O(n) + Space: O(1) +*/ + +bool checkValidString(char * s) { + int n = strlen(s); + + int balanced = 0; + for (int i=0; i=0; i--) { + if (s[i] == ')' || s[i] == '*') + balanced++; + else + balanced--; + + if (balanced < 0) + return false; + } + + return true; +} \ No newline at end of file diff --git a/c/0680-valid-palindrome-ii.c b/c/0680-valid-palindrome-ii.c new file mode 100644 index 000000000..283643782 --- /dev/null +++ b/c/0680-valid-palindrome-ii.c @@ -0,0 +1,26 @@ +bool validPalindromeLR(char * s, int left, int right){ + while (left < right) { + if (s[left] != s[right]) { + return false; + } + left++; + right--; + } + + return true; +} + +bool validPalindrome(char * s){ + int left = 0; + int right = strlen(s) - 1; + + while (left < right) { + if (s[left] != s[right]) { + return validPalindromeLR(s, left + 1, right) || validPalindromeLR(s, left, right - 1); + } + left++; + right--; + } + + return true; +} diff --git a/c/0682-baseball-game.c b/c/0682-baseball-game.c new file mode 100644 index 000000000..567bea7c4 --- /dev/null +++ b/c/0682-baseball-game.c @@ -0,0 +1,71 @@ +/* +Return the sum of all the scores on the record after applying all the operations. + +Space: O(n) +Time: O(n) +Where n is the size of operations +*/ + +//-------Implementation of a stack-------- + +typedef struct stack { + int val; + struct stack* next; +} stack; + +int sum(stack* s) { + if (s==NULL) + return 0; + return s->val + sum(s->next); +} + +stack* add(stack* s, int value) { + stack* new_s = malloc(sizeof(stack)); + new_s->val = value; + new_s->next = s; + return new_s; +} + +stack* rmv(stack* s) { + if (s==NULL) { + printf("Impossible de supprimer un élément d'une pile vide"); + return NULL; + } + stack* ans = s->next; + free(s); + return ans; +} + +//------------Main algorithm------------- + +int calPoints(char ** operations, int operationsSize){ + stack* s = NULL; + for (int i=0; ival + (s->next)->val; + s = add(s, to_add); + } else if (operations[i][0]=='D') { + s = add(s, 2*(s->val)); + } else if (operations[i][0]=='C') { + s = rmv(s); + } else { // It's a number + if (operations[i][0]=='-') { // It's a negative number + int n = 0; + for (int j=0; operations[i][j+1]!='\0'; j++) { + n *= 10; + n -= operations[i][j+1] - '0'; + } + s = add(s, n); + } else { // It's a positive number + int n = 0; + for (int j=0; operations[i][j]!='\0'; j++) { + n *= 10; + n += operations[i][j] - '0'; + } + s = add(s, n); + } + } + } + + return sum(s); +} diff --git a/c/0684-redundant-connection.c b/c/0684-redundant-connection.c new file mode 100644 index 000000000..4ee3fb558 --- /dev/null +++ b/c/0684-redundant-connection.c @@ -0,0 +1,35 @@ +/* +Return an edge that can be removed so that the resulting graph is a tree of n nodes. +Time: O(n) +Space: O(n) +*/ +int max(int a, int b) { + return a>b?a:b; +} + +int find(int* parent, int k) { + if (parent[k]==k) + return k; + return find(parent, parent[k]); +} + + + +int* findRedundantConnection(int** edges, int edgesSize, int* edgesColSize, int* returnSize){ + int* ans = malloc(sizeof(int)*2); + int* parent = malloc(sizeof(int)*(edgesSize+1)); + for (int i=0; i<=edgesSize; i++) + parent[i] = i; + for (int i=0; idata != NULL) { + free(current->data); + } + free(current); + } + } + + // Clean up the memory used for sticker signatures + for (int i = 0; i < signatureSize; i++) { + if (stickerSignatures[i] != NULL) { + free(stickerSignatures[i]); + } + } +} + +// Hash function to map a character to an index +int hashFunction(char c) { + return c - 'a'; +} + +// Initialize the signature for a word (count of each letter) +void initializeSignature(char *word, int *signature) { + for (int i = 0; i < 26; i++) { + signature[i] = 0; + } + + for (char *c = word; *c != '\0'; c++) { + signature[hashFunction(*c)]++; + } +} + +// Preprocess stickers and filter out dominated ones +int preprocessStickers(char **stickers, int stickersSize, + int **stickerSignatures, char **workingStickers, const int const *targetSignature) { + for (int i = 0; i < stickersSize; i++) { + workingStickers[i] = stickers[i]; + stickerSignatures[i] = (int *)malloc(sizeof(int) * 26); + + for (int j = 0; j < 26; j++) { + stickerSignatures[i][j] = 0; + } + + for (char *c = stickers[i]; *c != '\0'; c++) { + int index = hashFunction(*c); + + if (targetSignature[index] > 0) { + stickerSignatures[i][index]++; + } + } + } + + int remaining = stickersSize; + + for (int i = 0; i < remaining; i++) { + for (int j = 0; j < remaining; j++) { + if (i == j || workingStickers[j] == NULL) { + continue; + } + + int dominated = 1; + + for (int k = 0; k < 26; k++) { + if (stickerSignatures[i][k] > stickerSignatures[j][k]) { + dominated = 0; + break; + } + } + + if (dominated) { + free(stickerSignatures[i]); + stickerSignatures[i] = NULL; + workingStickers[i] = NULL; + remaining--; + + if (i < remaining) { + stickerSignatures[i] = stickerSignatures[remaining]; + workingStickers[i] = workingStickers[remaining]; + stickerSignatures[remaining] = NULL; + workingStickers[remaining] = NULL; + i--; + } + + break; + } + } + } + + return remaining; +} + +// Create a hash table to map characters to stickers +int makeHashTable(char **stickers, int stickersSize, char *target, InstanceList **mapping) { + for (int i = 0; i < 26; i++) { + mapping[i] = NULL; + } + + for (char *c = target; *c != '\0'; c++) { + int index = hashFunction(*c); + + if (mapping[index] == NULL) { + mapping[index] = (InstanceList *)malloc(sizeof(InstanceList)); + mapping[index]->data = NULL; + } + } + + for (int i = 0; i < stickersSize; i++) { + for (char *c = stickers[i]; *c != '\0'; c++) { + int index = hashFunction(*c); + + if (mapping[index] != NULL) { + InstanceList *spot = mapping[index]; + + if (spot->data != NULL && spot->size > 0 + && spot->data[spot->size - 1].index == i) { + spot->data[spot->size - 1].count++; + continue; + } + + if (spot->data == NULL) { + spot->data = (Instance *)malloc(sizeof(Instance) * MIN); + spot->size = 0; + spot->maxSize = MIN; + } else if (spot->size == spot->maxSize) { + spot->maxSize *= 2; + spot->data = realloc(spot->data, sizeof(Instance) * spot->maxSize); + } + + spot->data[spot->size].index = i; + spot->data[spot->size].count = 1; + spot->size++; + } + } + } + + return 0; +} + +// Find the index with the minimum size in mapping +int minIndex(InstanceList **mapping, int *targetSignature) { + int index = -1; + + for (int i = 0; i < 26; i++) { + if (targetSignature[i] > 0 && (index == -1 || mapping[i]->size < mapping[index]->size)) { + index = i; + } + } + + return index; +} + +// Recursive search function to find the minimum stickers required +void search(InstanceList **mapping, int **stickerSignatures, int *targetSignature, int depth, int *maxDepth) { + if (depth >= *maxDepth && *maxDepth > 0) { + return; + } + + int minLetter = minIndex(mapping, targetSignature); + + if (minLetter < 0) { + if (*maxDepth == 0 || depth < *maxDepth) { + *maxDepth = depth; + } + + return; + } + + for (int option = 0; option < mapping[minLetter]->size; option++) { + int index = mapping[minLetter]->data[option].index; + + for (int i = 0; i < 26; i++) { + targetSignature[i] -= stickerSignatures[index][i]; + } + + search(mapping, stickerSignatures, targetSignature, depth + 1, maxDepth); + + for (int i = 0; i < 26; i++) { + targetSignature[i] += stickerSignatures[index][i]; + } + } + + return; +} + +// Main function to calculate the minimum stickers required +int minStickers(char **stickers, int stickersSize, char *target) { + int targetSignature[26]; + initializeSignature(target, targetSignature); + int *stickerSignatures[stickersSize]; + char *workingStickers[stickersSize]; + int domainSize = preprocessStickers(stickers, stickersSize, stickerSignatures, workingStickers, targetSignature); + InstanceList *mapping[26]; + makeHashTable(workingStickers, domainSize, target, mapping); + + for (int i = 0; i < 26; i++) { + if (mapping[i] != NULL && mapping[i]->data == NULL) { + cleanup(mapping, stickerSignatures, domainSize); + return -1; + } + } + + int maxDepth = 0; + search(mapping, stickerSignatures, targetSignature, 0, &maxDepth); + cleanup(mapping, stickerSignatures, domainSize); + return maxDepth; +} + +// Alternative Solution + +// int solve(int mask, char **stickers, char *target, int n, int m, int *dp) { +// if (mask == (1 << m) - 1) { +// return 0; +// } +// if (dp[mask] != -1) { +// return dp[mask]; +// } +// int ans = 1e9; +// for (int i = 0; i < n; i++) { +// int freq[26] = {0}; +// for (int j = 0; stickers[i][j] != '\0'; j++) { +// freq[stickers[i][j] - 'a']++; +// } +// int new_mask = 0; +// for (int j = 0; j < m; j++) { +// if ((1 << j) & mask) continue; +// if (freq[target[j] - 'a']) { +// freq[target[j] - 'a']--; +// new_mask |= (1 << j); +// } +// } +// if (new_mask != 0) { +// int temp = 1 + solve(new_mask | mask, stickers, target, n, m, dp); +// ans = (temp < ans) ? temp : ans; +// } +// } +// return dp[mask] = ans; +// } + +// int minStickers(char **stickers, int stickersSize, char *target) { +// int mask = 0; +// int n = stickersSize, m = strlen(target); +// int *dp = (int *)malloc((1 << m) * sizeof(int)); +// for (int i = 0; i < (1 << m); i++) { +// dp[i] = -1; +// } +// int ans = solve(mask, stickers, target, n, m, dp); +// free(dp); +// return (ans == 1e9) ? -1 : ans; +// } diff --git a/c/0695-max-area-of-island.c b/c/0695-max-area-of-island.c new file mode 100644 index 000000000..0e66e100f --- /dev/null +++ b/c/0695-max-area-of-island.c @@ -0,0 +1,35 @@ +/* +Return the maximum area of an island in grid. If there is no island, return 0. +Time: O(n^2) +Space: O(1) + +*/ + +int max(int a, int b) { + return a>b?a:b; +} + +int dfs(int** grid, int n, int m, int i, int j) { + int cpt=1; + grid[i][j]=0; + if (i>0 && grid[i-1][j]==1) + cpt += dfs(grid, n, m, i-1, j); + if (j>0 && grid[i][j-1]==1) + cpt += dfs(grid, n, m, i, j-1); + if (i<(n-1) && grid[i+1][j]==1) + cpt += dfs(grid, n, m, i+1, j); + if (j<(m-1) && grid[i][j+1]==1) + cpt += dfs(grid, n, m, i, j+1); + return cpt; +} + +int maxAreaOfIsland(int** grid, int gridSize, int* gridColSize){ + int m = 0, h=*gridColSize, i, j; + for (i=0; i array = array; + heap -> max_len = max_len; + heap -> len = 0; + heap -> append = heap_insert; + heap -> pop = heap_pop; + return heap; +} + +int heap_parent(int index) { + return (index - 1) / 2; +} + +int heap_left(int index) { + return 2 * index + 1; +} + +int heap_right(int index) { + return 2 * index + 2; +} + +void heap_swap(struct heap* heap, int parent, int index) { + int temp = heap -> array[index]; + heap -> array[index] = heap -> array[parent]; + heap -> array[parent] = temp; +} + +void heap_heapify(struct heap* heap, int index) { + if (index != 0) { + int parent = heap_parent(index); + if (heap -> array[parent] > heap -> array[index]) { + heap_swap(heap, parent, index); + heap_heapify(heap, parent); + } + } +} + +bool heap_insert(struct heap* heap, int val) { + + if (heap -> len == heap -> max_len) { + return false; + } + heap -> array[heap -> len] = val; + heap_heapify(heap, heap -> len); + heap -> len += 1; + return true; +} + +bool heap_has_left(struct heap* heap,int index) { + return heap_left(index) < heap -> len; +} + +bool heap_has_right(struct heap* heap, int index) { + return heap_right(index) < heap -> len; +} + +void heap_heapify_reverse(struct heap* heap, int index) { + + int left = heap_left(index); + int right = heap_right(index); + int minimum = index; + if (heap_has_left(heap, index) && (heap -> array[left] < heap -> array[minimum])) { + minimum = left; + } + if (heap_has_right(heap, index) && (heap -> array[right] < heap -> array[minimum])) { + minimum = right; + } + if (minimum != index) { + heap_swap(heap, index, minimum); + heap_heapify_reverse(heap, minimum); + } +} + +int heap_pop(struct heap* heap) { + if (heap -> len == 0) { + return NULL; + } + int val = heap -> array[0]; + heap -> len -= 1; + heap -> array[0] = heap -> array[heap -> len]; + if (heap -> len > 1) { + heap_heapify_reverse(heap, 0); + } + return val; +} + +void heap_free(struct heap* heap) { + free(heap -> array); + free(heap); +} + +typedef struct { + struct heap* heap; + int k; +} KthLargest; + + +KthLargest* kthLargestCreate(int k, int* nums, int numsSize) { + + KthLargest* self = malloc(sizeof(KthLargest)); + self -> heap = Heap(numsSize + 2); + self -> k = k; + for (int i = 0; i < numsSize; i++) { + self -> heap -> append(self -> heap, nums[i]); + } + while (self -> heap -> len > k) { + self -> heap -> pop(self -> heap); + } + return self; +} + +int kthLargestAdd(KthLargest* obj, int val) { + obj -> heap -> append(obj -> heap, val); + if (obj -> heap -> len > obj -> k) { + obj -> heap -> pop(obj -> heap); + } + return obj -> heap -> array[0]; +} + +void kthLargestFree(KthLargest* obj) { + heap_free(obj -> heap); + free(obj); +} + +/** + * Your KthLargest struct will be instantiated and called as such: + * KthLargest* obj = kthLargestCreate(k, nums, numsSize); + * int param_1 = kthLargestAdd(obj, val); + + * kthLargestFree(obj); +*/ \ No newline at end of file diff --git a/c/0704-binary-search.c b/c/0704-binary-search.c new file mode 100644 index 000000000..e09c86992 --- /dev/null +++ b/c/0704-binary-search.c @@ -0,0 +1,30 @@ +/* + Given an array of integers, search for a target value. + Examples: + nums = [-1,0,3,5,9,12], target = 9, return 4 (index of target 9) + nums = [-1,0,3,5,9,12], target = 2, return -1 (not found) + Array is sorted, so perform binary search + + Time: O(log n) + Space: O(1) +*/ + +int search(int* nums, int numsSize, int target) { + int left = 0; + int right = numsSize-1; + + while (left <= right) { + int mid = left + (right - left) / 2; + + if (nums[mid] == target) { + return mid; + } + else if (nums[mid] < target) { + left = mid + 1; + } + else { + right = mid - 1; + } + } + return -1; +} \ No newline at end of file diff --git a/c/0705-design-hashset.c b/c/0705-design-hashset.c new file mode 100644 index 000000000..dc27f5c4f --- /dev/null +++ b/c/0705-design-hashset.c @@ -0,0 +1,147 @@ +// -------------------- Complex solution -------------------- +// --------- LinkedList --------- +typedef struct Node { + int value; + struct Node *next; +} Node; + +typedef struct LinkedListHead { + Node *next; +} LinkedListHead; + +Node *newNode(int key) { + Node *node = (Node *)malloc(sizeof(Node)); + node->value = key; + node->next = NULL; + return node; +} + +LinkedListHead *newLinkedList() { + LinkedListHead *head = (LinkedListHead *)malloc(sizeof(LinkedListHead)); + head->next = NULL; + return head; +} + +bool linkedListContains(LinkedListHead *head, int key) { + Node *currentNode = head->next; + while (currentNode) { + if (currentNode->value == key) return true; + currentNode = currentNode->next; + } + return false; +} + +void linkedListPush(LinkedListHead *head, int key) { + Node *node = newNode(key); + node->next = head->next; + head->next = node; +} + +void linkedListRemove(LinkedListHead *head, int key) { + Node *previousNode = head->next; + if (previousNode == NULL) return; + if (previousNode->value == key) { + head->next = previousNode->next; + free(previousNode); + return; + } + Node *currentNode = previousNode->next; + while (currentNode) { + if (currentNode->value == key) { + previousNode->next = currentNode->next; + free(currentNode); + return; + } + currentNode = currentNode->next; + } +} + +void freeLinkedList(LinkedListHead *head) { + Node *previousNode = head->next; + if (previousNode) { + Node *currentNode = previousNode->next; + while (currentNode) { + free(previousNode); + previousNode = currentNode; + currentNode = currentNode->next; + } + free(previousNode); + } + free(head); +} +// --------- LinkedList --------- + +// ----------- HashSet ---------- +typedef struct { + LinkedListHead *head; +} MyHashSet; + +#define HASH_SET_DEFAULT_SIZE 10000 + +MyHashSet *myHashSetCreate() { + MyHashSet *obj = + (MyHashSet *)malloc(sizeof(MyHashSet) * HASH_SET_DEFAULT_SIZE); + for (int i = 0; i < HASH_SET_DEFAULT_SIZE; i++) { + obj[i].head = newLinkedList(); + } + return obj; +} + +int getHashPosition(int key) { return key % HASH_SET_DEFAULT_SIZE; } + +void myHashSetAdd(MyHashSet *obj, int key) { + int pos = getHashPosition(key); + if (obj[pos].head == NULL) obj[pos].head = newLinkedList(); + if (!linkedListContains(obj[pos].head, key)) + linkedListPush(obj[pos].head, key); +} + +void myHashSetRemove(MyHashSet *obj, int key) { + linkedListRemove(obj[getHashPosition(key)].head, key); +} + +bool myHashSetContains(MyHashSet *obj, int key) { + LinkedListHead *head = obj[getHashPosition(key)].head; + if (head == NULL || head->next == NULL) return false; + return linkedListContains(head, key); +} + +void myHashSetFree(MyHashSet *obj) { + for (int i = 0; i < HASH_SET_DEFAULT_SIZE; i++) + if (obj[i].head != NULL) freeLinkedList(obj[i].head); + free(obj); +} +// ----------- HashSet ---------- +// -------------------- Complex solution -------------------- + +// -------------------- Simpler and faster solution -------------------- +typedef struct { + bool *values; +} MyHashSet; + +MyHashSet *myHashSetCreate() { + MyHashSet *obj = (MyHashSet *)malloc(sizeof(MyHashSet)); + obj->values = (bool *)calloc(1000001, sizeof(bool)); + return obj; +} + +void myHashSetAdd(MyHashSet *obj, int key) { obj->values[key] = true; } + +void myHashSetRemove(MyHashSet *obj, int key) { obj->values[key] = false; } + +bool myHashSetContains(MyHashSet *obj, int key) { return obj->values[key]; } + +void myHashSetFree(MyHashSet *obj) { free(obj); } +// -------------------- Simpler and faster solution -------------------- + +/** + * Your MyHashSet struct will be instantiated and called as such: + * MyHashSet* obj = myHashSetCreate(); + * myHashSetAdd(obj, key); + + * myHashSetRemove(obj, key); + + * bool param_3 = myHashSetContains(obj, key); + + * myHashSetFree(obj); +*/ diff --git a/c/0706-design-hashmap.c b/c/0706-design-hashmap.c new file mode 100644 index 000000000..74b9d6c10 --- /dev/null +++ b/c/0706-design-hashmap.c @@ -0,0 +1,117 @@ +#define HASH_SIZE 1024 + +typedef struct HashNode { + int key; + int value; + struct HashNode *next; +} HashNode; + +typedef struct { + HashNode *hash[HASH_SIZE]; +} MyHashMap; + +inline HashNode *NewHashNode(int key, int value) { + HashNode *h = (HashNode*)malloc(sizeof(HashNode)); + h->key = key; + h->value = value; + h->next = NULL; + return h; +} + +inline int KeyToIndex(int key) { + while (key < 0) key += HASH_SIZE; + return key % HASH_SIZE; +} + +MyHashMap* myHashMapCreate() { + MyHashMap* h = (MyHashMap*)calloc(1, sizeof(MyHashMap)); + return h; +} + +void myHashMapPut(MyHashMap* obj, int key, int value) { + assert(obj); + HashNode **hash = &obj->hash[KeyToIndex(key)]; + HashNode *h = *hash; + if (!*hash) { + *hash = NewHashNode(key, value); + } else if (h->key == key) { + h->value = value; + return; + } else { + while (h->next) { + h = h->next; + if (h->key == key) { + h->value = value; + return; + } + } + h->next = NewHashNode(key, value); + } +} + +int myHashMapGet(MyHashMap* obj, int key) { + assert(obj); + HashNode* hash = obj->hash[KeyToIndex(key)]; + while (hash) { + if (hash->key == key) { + return hash->value; + } + hash = hash->next; + } + return -1; +} + +void myHashMapRemove(MyHashMap* obj, int key) { + assert(obj); + HashNode **head = &obj->hash[KeyToIndex(key)]; + if (*head) { + HashNode *h = *head; + if (h->key == key) { + HashNode *tmp = *head; + *head = h->next; + free(tmp); + return; + } else { + HashNode *h_parent = h; + while(h) { + if (h->key == key) { + h_parent->next = h->next; + free(h); + return; + } + h_parent = h; + h = h->next; + } + } + } + return; +} + +void myHashMapFree(MyHashMap* obj) { + if(obj) { + HashNode* h; + HashNode *tmp; + for (int i = 0; i < HASH_SIZE; i++) { + h = obj->hash[i]; + if(h) { + while (h != NULL) { + tmp = h->next; + free(h); + h = tmp; + } + } + } + } +} + +/** + * Your MyHashMap struct will be instantiated and called as such: + * MyHashMap* obj = myHashMapCreate(); + * myHashMapPut(obj, key, value); + + * int param_2 = myHashMapGet(obj, key); + + * myHashMapRemove(obj, key); + + * myHashMapFree(obj); +*/ diff --git a/c/0707-design-linked-list.c b/c/0707-design-linked-list.c new file mode 100644 index 000000000..6457788ba --- /dev/null +++ b/c/0707-design-linked-list.c @@ -0,0 +1,130 @@ +typedef struct Node { + struct Node* next; + struct Node* prev; + int val; +} Node; + +typedef struct { + Node* head; + Node* tail; + int length; +} MyLinkedList; + +Node* createNode(int val) { + Node* new_node = (Node*)malloc(sizeof(Node) ); + new_node->next = NULL; + new_node->prev = NULL; + new_node->val = val; + return new_node; +} + +MyLinkedList* myLinkedListCreate() { + MyLinkedList* list = (MyLinkedList*)malloc(sizeof(MyLinkedList) ); + list->head = createNode(-1); + list->tail = list->head; + list->length = 0; + return list; +} + +Node* traverseList(MyLinkedList* obj, int index) { + if (index < 0 || index >= obj->length) + return NULL; + Node* trev = obj->head; + while (index >= 0) { + trev = trev->next; + index -= 1; + } + return trev; +} + +int myLinkedListGet(MyLinkedList* obj, int index) { + Node *node = traverseList(obj, index); + if (node != NULL) + return node->val; + return -1; +} + +void myLinkedListAddAtHead(MyLinkedList* obj, int val) { + Node* new_node = createNode(val); + new_node->next = obj->head->next; + new_node->prev = obj->head; + obj->head->next = new_node; + if (new_node->next != NULL) + new_node->next->prev = new_node; + else + obj->tail = new_node; + obj->length += 1; + return; +} + +void myLinkedListAddAtTail(MyLinkedList* obj, int val) { + Node* new_node = createNode(val); + obj->tail->next = new_node; + new_node->prev = obj->tail; + obj->tail = obj->tail->next; + obj->length += 1; + return; +} + +void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) { + if (index == 0) { + myLinkedListAddAtHead(obj, val); + return; + } + else if (index == obj->length) { + myLinkedListAddAtTail(obj, val); + return; + } + Node* prev_node = traverseList(obj, index - 1); + if (prev_node == NULL) + return; + Node* new_node = createNode(val); + new_node->next = prev_node->next; + new_node->prev = prev_node; + prev_node->next = new_node; + if (new_node->next != NULL) + new_node->next->prev = new_node; + obj->length += 1; + return; +} + +void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) { + Node *node = traverseList(obj, index); + if (node == NULL) + return; + Node *temp = node; + node->prev->next = node->next; + if (node->next != NULL) + node->next->prev = node->prev; + else + obj->tail = node->prev; + obj->length -= 1; + free(temp); + return; +} + +void myLinkedListFree(MyLinkedList* obj) { + Node *prev = NULL, *cur = obj->head; + while (cur != NULL) { + prev = cur; + cur = cur->next; + free(prev); + } + free(obj); +} + +/** + * Your MyLinkedList struct will be instantiated and called as such: + * MyLinkedList* obj = myLinkedListCreate(); + * int param_1 = myLinkedListGet(obj, index); + + * myLinkedListAddAtHead(obj, val); + + * myLinkedListAddAtTail(obj, val); + + * myLinkedListAddAtIndex(obj, index, val); + + * myLinkedListDeleteAtIndex(obj, index); + + * myLinkedListFree(obj); +*/ diff --git a/c/0724-find-pivot-index.c b/c/0724-find-pivot-index.c new file mode 100644 index 000000000..0a82e5f08 --- /dev/null +++ b/c/0724-find-pivot-index.c @@ -0,0 +1,22 @@ +/* +Given an array of integers nums, calculate the pivot index of this array. + +Space: O(1) +Time: O(n) +*/ + +int pivotIndex(int* nums, int numsSize){ + int right_sum = 0; + int left_sum = 0; + for (int i=0; i< numsSize; i++) // Initialise the right sum + right_sum += nums[i]; + + for (int i=0; i [1,1,4,2,1,1,0,0] + + Time: O(N) + Space: O(1) +*/ + +/** + * Note: The returned array must be malloced, assume caller calls free(). + */ +int* dailyTemperatures(int* temperatures, int temperaturesSize, int* returnSize){ + *returnSize = temperaturesSize; + + int* result = (int*) malloc(sizeof(int)*temperaturesSize); + + // Initialize result array to zero + for (int i = 0; i < temperaturesSize; ++i) result[i] = 0; + + + for (int i = temperaturesSize-1; i >= 0; --i) { + int j = i + 1; + + while (j < temperaturesSize && temperatures[j] <= temperatures[i]) { + if (result[j] <= 0) + break; + j += result[j]; + } + + // If a day with higher temperature found, update result for that index + if (j < temperaturesSize && temperatures[j] > temperatures[i]) { + result[i] = j - i; + } + } + + return result; +} diff --git a/c/0740-delete-and-earn.c b/c/0740-delete-and-earn.c new file mode 100644 index 000000000..4b98b7769 --- /dev/null +++ b/c/0740-delete-and-earn.c @@ -0,0 +1,31 @@ +int max(int a, int b) { + return a > b ? a : b; +} + +int deleteAndEarn(int* nums, int numsSize) { + if (numsSize == 0) return 0; + + int max_num = nums[0]; + for (int i = 0; i < numsSize; i++) { + max_num = max(max_num, nums[i]); + } + + int freq[max_num + 1]; + for (int i = 0; i <= max_num; i++) { + freq[i] = 0; + } + + for (int i = 0; i < numsSize; i++) { + freq[nums[i]] += nums[i]; + } + + int dp[max_num + 1]; + dp[0] = 0; + dp[1] = freq[1]; + + for (int i = 2; i <= max_num; i++) { + dp[i] = max(dp[i - 1], dp[i - 2] + freq[i]); + } + + return dp[max_num]; +} diff --git a/c/0746-min-cost-climbing-stairs.c b/c/0746-min-cost-climbing-stairs.c new file mode 100644 index 000000000..be72b9dd1 --- /dev/null +++ b/c/0746-min-cost-climbing-stairs.c @@ -0,0 +1,9 @@ +int min(int a, int b) { return a > b ? b : a; } + +int minCostClimbingStairs(int *cost, int costSize) { + + for (int i = 2; i < costSize; i++) + cost[i] += min(cost[i - 1], cost[i - 2]); + + return min(cost[costSize - 1], cost[costSize - 2]); +} \ No newline at end of file diff --git a/c/0763-partition-labels.c b/c/0763-partition-labels.c new file mode 100644 index 000000000..8484ba017 --- /dev/null +++ b/c/0763-partition-labels.c @@ -0,0 +1,26 @@ +int* partitionLabels(char* s, int* returnSize) { + int* result = NULL; + int* lastOccurrence = (int*)calloc(26, sizeof(int)); // Store the last occurrence index of each character + + // Find the last occurrence index of each character + for (int i = 0; s[i] != '\0'; i++) { + lastOccurrence[s[i] - 'a'] = i; + } + + int start = 0, end = 0; // Pointers to track the current partition + *returnSize = 0; + + for (int i = 0; s[i] != '\0'; i++) { + end = (end > lastOccurrence[s[i] - 'a']) ? end : lastOccurrence[s[i] - 'a']; + + if (i == end) { + (*returnSize)++; + result = (int*)realloc(result, sizeof(int) * (*returnSize)); + result[(*returnSize) - 1] = end - start + 1; + start = end + 1; + } + } + + free(lastOccurrence); + return result; +} diff --git a/c/0837-new-21-game.c b/c/0837-new-21-game.c new file mode 100644 index 000000000..3a26222c3 --- /dev/null +++ b/c/0837-new-21-game.c @@ -0,0 +1,27 @@ +double new21Game(int n, int k, int maxPts) { + if (k == 0 || n >= k + maxPts) { + return 1.0; + } + + double windowSum = 1.0; + double probability = 0.0; + + double dp[n + 1]; + dp[0] = 1.0; + + for (int i = 1; i <= n; i++) { + dp[i] = windowSum / maxPts; + + if (i < k) { + windowSum += dp[i]; + } else { + probability += dp[i]; + } + + if (i >= maxPts) { + windowSum -= dp[i - maxPts]; + } + } + + return probability; +} diff --git a/c/0846-hand-of-straights.c b/c/0846-hand-of-straights.c new file mode 100644 index 000000000..88a73faa4 --- /dev/null +++ b/c/0846-hand-of-straights.c @@ -0,0 +1,21 @@ +int cmpfunc(const void* a, const void* b) { + return (*(int*)a - *(int*)b); +} + +bool isNStraightHand(int* hand, int handSize, int groupSize) { + if (handSize % groupSize != 0) return false; + + qsort(hand, handSize, sizeof(int), cmpfunc); + + for (int i = 0; i < handSize; i++) { + if (hand[i] == -1) continue; + int k = i; + for (int j = 1; j < groupSize; j++) { + while (k < handSize && hand[i] + j != hand[k]) k++; + if (k == handSize) return false; + hand[k] = -1; + } + } + + return true; +} diff --git a/c/0853-car-fleet.c b/c/0853-car-fleet.c new file mode 100644 index 000000000..3ed09cf4f --- /dev/null +++ b/c/0853-car-fleet.c @@ -0,0 +1,36 @@ +typedef struct { + int position; + int speed; +} Car; + +// Function to compare cars for sorting based on their positions +int compareCars(const void* a, const void* b) { + return ((Car*)a)->position - ((Car*)b)->position; +} + +int carFleet(int target, int* position, int positionSize, int* speed, int speedSize) { + if (positionSize == 0) return 0; + + Car cars[positionSize]; + for (int i = 0; i < positionSize; i++) { + cars[i].position = position[i]; + cars[i].speed = speed[i]; + } + + // Sort cars based on their positions in descending order + qsort(cars, positionSize, sizeof(Car), compareCars); + + int fleets = 0; + double prevTime = -1.0; + + for (int i = positionSize - 1; i >= 0; i--) { + double time = (double)(target - cars[i].position) / cars[i].speed; + + if (time > prevTime) { + fleets++; + prevTime = time; + } + } + + return fleets; +} diff --git a/c/0875-koko-eating-bananas.c b/c/0875-koko-eating-bananas.c new file mode 100644 index 000000000..6f33e8329 --- /dev/null +++ b/c/0875-koko-eating-bananas.c @@ -0,0 +1,66 @@ +/* + Given an array of bananas piles containing differing amounts of bananas and + 'h' hours to eat all of them. + Determine the minimum speed (i.e. number of bananas per-hour) possible. + + Ex. piles = [3,6,7,11], h = 8 -> 4 + + If all the bananas can be eaten with speed 'x' than the same holds true for + any speed more than 'x'. Similarly if bananas cannot be eated at speed 'y', + the same will be speeds less than 'y'. + + Binary search can be performed on the possible values of speed till the + minimum speed is found with, + left bound = ceil(total/h) + right bound = maximum pile size + + Time: O(NlogM) where N is number of piles and M is maximum pile size + Space: O(1) +*/ + +int hoursRequired(int* piles, int pilesSize, int h, int speed) { + int hours = 0; + for (int i = 0; i < pilesSize; ++i) { + hours += (piles[i]+speed-1)/speed; + } + + return hours; +} + +long sumOfArray(int* piles, int pilesSize) { + long total = 0l; + + for (int i = 0; i < pilesSize; ++i) { + total += piles[i]; + } + + return total; +} + +int maxElement(int* piles, int pilesSize) { + int maxElem = piles[0]; + + for (int i = 0; i < pilesSize; ++i) { + maxElem = fmax(maxElem, piles[i]); + } + return maxElem; +} + +int minEatingSpeed(int* piles, int pilesSize, int h){ + long total = sumOfArray(piles, pilesSize); + + int l = (total+h-1)/h; + int r = maxElement(piles, pilesSize); + + while (l < r) { + int mid = l + (r-l)/2; + + int hours = hoursRequired(piles, pilesSize, h, mid); + if (hours <= h) + r = mid; + else if (hours > h) + l = mid + 1; + } + + return r; +} diff --git a/c/0876-middle-of-the-linked-list.c b/c/0876-middle-of-the-linked-list.c new file mode 100644 index 000000000..518a17ce0 --- /dev/null +++ b/c/0876-middle-of-the-linked-list.c @@ -0,0 +1,32 @@ +/** + * Given the head of a singly linked list, return the middle node of the linked + * list. + * + * If there are two middle nodes, return the second middle node. + * + * Constraints: + * + * The number of nodes in the list is in the range [1, 100]. + * 1 <= Node.val <= 100 + * + * Definition for singly-linked list. + * struct ListNode { + * int val; + * struct ListNode *next; + * }; + * + * Space = O(1) + * Time = O(n) + */ + +struct ListNode* middleNode(struct ListNode* head){ + struct ListNode* slow = head; + struct ListNode* fast = head; + + while (fast && fast->next) { + slow = slow->next; + fast = fast->next->next; + } + + return slow; +} diff --git a/c/0881-boats-to-save-people.c b/c/0881-boats-to-save-people.c new file mode 100644 index 000000000..509845fc0 --- /dev/null +++ b/c/0881-boats-to-save-people.c @@ -0,0 +1,32 @@ +/* + +Space: O(n) (because of quicksort) +Time: O(nlog(n)) (because of quicksort) +*/ + +int cmp(const void* a, const void* b) { // Function of comparison for quicksort + return *(int*)a - *(int*)b; +} + +int numRescueBoats(int* people, int peopleSize, int limit){ + int cpt=0; + qsort(people, peopleSize, sizeof(int), cmp); + int i=0; + int j=peopleSize-1; + while (people[i]+people[j] > limit && j>0) + j--; + cpt += peopleSize-j-1; + while (ikey = key; + HASH_ADD_KEYPTR(hh, root, key, strlen(key), entry); + } else { + free(key); + } + } + + return HASH_COUNT(root); +} diff --git a/c/0953-verifying-an-alien-dictionary.c b/c/0953-verifying-an-alien-dictionary.c new file mode 100644 index 000000000..dfa59f725 --- /dev/null +++ b/c/0953-verifying-an-alien-dictionary.c @@ -0,0 +1,34 @@ +/* +Given a sequence of words written in the alien language, and the +order of the alphabet, return true if and only if the given words +are sorted lexicographically in this alien language. + +Space: O(1) +Time: O(n*m) +(Where n is the number of words and m the average length of the elements in words) +*/ + + +bool isAlienSorted(char ** words, int wordsSize, char * order){ + int alphabet[26] = {0}; + for (int i=0; i<26; i++) + alphabet[order[i] - 'a'] = i; + for (int i=1; iv_i){ + return false; + } + break; + } + j++; + } + if (words[i-1][j]!='\0' && words[i][j] == '\0'){ // If words[i] is shorter than words[i-1] + return false; + } + } + return true; +} diff --git a/c/0973-k-closest-points-to-origin.c b/c/0973-k-closest-points-to-origin.c new file mode 100644 index 000000000..dff361e47 --- /dev/null +++ b/c/0973-k-closest-points-to-origin.c @@ -0,0 +1,48 @@ +typedef struct { + int x; + int y; +} Point; + +// Function to calculate the Euclidean distance from origin +double distance(Point p) { + return sqrt(p.x * p.x + p.y * p.y); +} + +// Function to compare two points based on distance +int compare(const void* a, const void* b) { + Point* pointA = (Point*)a; + Point* pointB = (Point*)b; + + double distanceA = distance(*pointA); + double distanceB = distance(*pointB); + + if (distanceA < distanceB) return -1; + if (distanceA > distanceB) return 1; + return 0; +} + +// Function to find k closest points to origin +int** kClosest(int** points, int pointsSize, int* pointsColSize, int k, int* returnSize, int** returnColumnSizes) { + Point* pointArr = (Point*)malloc(pointsSize * sizeof(Point)); + for (int i = 0; i < pointsSize; i++) { + pointArr[i].x = points[i][0]; + pointArr[i].y = points[i][1]; + } + + qsort(pointArr, pointsSize, sizeof(Point), compare); + + int** result = (int**)malloc(k * sizeof(int*)); + *returnSize = k; + *returnColumnSizes = (int*)malloc(k * sizeof(int)); + + for (int i = 0; i < k; i++) { + result[i] = (int*)malloc(2 * sizeof(int)); + result[i][0] = pointArr[i].x; + result[i][1] = pointArr[i].y; + (*returnColumnSizes)[i] = 2; + } + + free(pointArr); + + return result; +} diff --git a/c/0980-unique-paths-iii.c b/c/0980-unique-paths-iii.c new file mode 100644 index 000000000..c9fa18244 --- /dev/null +++ b/c/0980-unique-paths-iii.c @@ -0,0 +1,49 @@ +const int OBSTACLE = -1, EMPTY = 0, START = 1, END = 2; +int uniquePathsIII(int** grid, int gridSize, int* gridColSize){ + int startRow, startCol, endRow, endCol, + toVisit = 0, visited = 0, R = gridSize, C = gridColSize[0]; + for(int row = 0; row < R; row++) + for(int col = 0; col < C; col++) { + //dereference the value of the current cell + int cell = grid[row][col]; + if(cell != OBSTACLE) { + //all non obstacle cells must be visited + toVisit++; + //remember start and end cells + if(cell == START) { + startRow = row; + startCol = col; + } else if(cell == END) { + endRow = row; + endCol = col; + } + } else + //obstacles can be considered visited cells + visited |= 1 << (row*C + col); + } + + //The starting state is an empty path which will visit the start cell next + return solve(R, C, endRow, endCol, visited, toVisit, startRow, startCol); +} + +int solve(int R, int C, int endRow, int endCol, int visited, int toVisit, int row, int col){ + toVisit--; + //base case, we are at the end cell. + if(row == endRow && col == endCol) + return !toVisit; + + //add current cell to visited set + visited |= 1 << (row*C + col); + + //count paths which go through all other valid neighbor cells. + int ret = 0; + if(row > 0 && !(visited & (1 << ((row - 1)*C + col)))) + ret += solve(R, C, endRow, endCol, visited, toVisit, row - 1, col); + if(row + 1 < R && !(visited & (1 << ((row + 1)*C + col)))) + ret += solve(R, C, endRow, endCol, visited, toVisit, row + 1, col); + if(col > 0 && !(visited & (1 << (row*C + col - 1)))) + ret += solve(R, C, endRow, endCol, visited, toVisit, row, col - 1); + if(col + 1 < C && !(visited & (1 << (row*C + col + 1)))) + ret += solve(R, C, endRow, endCol, visited, toVisit, row, col + 1); + return ret; +} diff --git a/c/0981-time-based-key-value-store.c b/c/0981-time-based-key-value-store.c new file mode 100644 index 000000000..6bf68e2fa --- /dev/null +++ b/c/0981-time-based-key-value-store.c @@ -0,0 +1,119 @@ +#include +#include +#include + +// Define a struct to hold key-value pairs and timestamps +typedef struct { + char* key; + char* value; + int timestamp; +} Entry; + +// Define the TimeMap structure +typedef struct { + Entry** entries; // Array of arrays to store key-value pairs and timestamps + int* sizes; // Array to store the current size of each entry + int* capacities; // Array to store the current capacity of each entry + int size; // Number of distinct keys +} TimeMap; + +// Function to create a new TimeMap +TimeMap* timeMapCreate() { + TimeMap* obj = (TimeMap*)malloc(sizeof(TimeMap)); + obj->entries = NULL; + obj->sizes = NULL; + obj->capacities = NULL; + obj->size = 0; + return obj; +} + +// Function to set a key-value pair with timestamp +void timeMapSet(TimeMap* obj, char* key, char* value, int timestamp) { + if (obj == NULL || key == NULL || value == NULL) return; + + int index = -1; + for (int i = 0; i < obj->size; i++) { + if (strcmp(obj->entries[i][0].key, key) == 0) { + index = i; + break; + } + } + + // If key is not found, create a new entry + if (index == -1) { + obj->size++; + obj->entries = (Entry**)realloc(obj->entries, sizeof(Entry*) * obj->size); + obj->sizes = (int*)realloc(obj->sizes, sizeof(int) * obj->size); + obj->capacities = (int*)realloc(obj->capacities, sizeof(int) * obj->size); + + index = obj->size - 1; + obj->entries[index] = (Entry*)malloc(sizeof(Entry)); + obj->sizes[index] = 0; + obj->capacities[index] = 1; + } + // If the entry is full, double its capacity + else if (obj->sizes[index] == obj->capacities[index]) { + obj->capacities[index] *= 2; + obj->entries[index] = (Entry*)realloc(obj->entries[index], sizeof(Entry) * obj->capacities[index]); + } + + // Add the new entry to the TimeMap + Entry* entry = &obj->entries[index][obj->sizes[index]]; + entry->key = strdup(key); + entry->value = strdup(value); + entry->timestamp = timestamp; + + obj->sizes[index]++; +} + +// Function to get the value for a given key and timestamp +char* timeMapGet(TimeMap* obj, char* key, int timestamp) { + if (obj == NULL || key == NULL || obj->size == 0) return ""; + + // Iterate through entries to find the matching key + for (int i = 0; i < obj->size; i++) { + if (strcmp(obj->entries[i][0].key, key) == 0) { + Entry* entries = obj->entries[i]; + int left = 0; + int right = obj->sizes[i] - 1; + int index = -1; + + // Binary search to find the appropriate timestamp index + while (left <= right) { + int mid = left + (right - left) / 2; + + if (entries[mid].timestamp <= timestamp) { + index = mid; + left = mid + 1; + } else { + right = mid - 1; + } + } + + // If a matching timestamp is found, return the corresponding value + if (index != -1) { + return entries[index].value; + } + } + } + + return ""; // Return an empty string if key or timestamp is not found +} + +// Function to free memory allocated by the TimeMap +void timeMapFree(TimeMap* obj) { + if (obj == NULL) return; + + for (int i = 0; i < obj->size; i++) { + for (int j = 0; j < obj->sizes[i]; j++) { + free(obj->entries[i][j].key); + free(obj->entries[i][j].value); + } + free(obj->entries[i]); + } + + free(obj->entries); + free(obj->sizes); + free(obj->capacities); + free(obj); +} diff --git a/c/0983-minimum-cost-for-tickets.c b/c/0983-minimum-cost-for-tickets.c new file mode 100644 index 000000000..bab4cfa43 --- /dev/null +++ b/c/0983-minimum-cost-for-tickets.c @@ -0,0 +1,30 @@ +int min(int a, int b) { + return a < b ? a : b; +} + +int max(int a, int b) { + return a > b ? a : b; +} + +int mincostTickets(int* days, int daysSize, int* costs, int costsSize) { + int k = 0; + int dp[366]; + + for (int i = 0; i <= 365; i++) { + dp[i] = 0; + } + + for (int i = 1; i <= 365; i++) { + dp[i] = dp[i - 1]; + if (i == days[k]) { + dp[i] = min(dp[i - 1] + costs[0], + min(dp[max(0, i - 7)] + costs[1], + dp[max(0, i - 30)] + costs[2])); + if (k < daysSize - 1) { + k++; + } + } + } + + return dp[365]; +} diff --git a/c/0989-add-to-array-form-of-integer.c b/c/0989-add-to-array-form-of-integer.c new file mode 100644 index 000000000..e23705060 --- /dev/null +++ b/c/0989-add-to-array-form-of-integer.c @@ -0,0 +1,43 @@ +int* addToArrayForm(int* A, int ASize, int K, int* returnSize) { + // Calculate the maximum size of the result array + int maxSize = (ASize > 6) ? ASize + 2 : 8; // Maximum 6 digits in K, plus 2 for potential carry. + + // Create the result array + int* result = (int*)malloc(maxSize * sizeof(int)); + + // Initialize variables + int carry = 0; + int i = ASize - 1; + int j = 0; + + // Perform the addition + while (i >= 0 || K > 0 || carry > 0) { + int sum = carry; + if (i >= 0) { + sum += A[i]; + i--; + } + if (K > 0) { + sum += K % 10; + K /= 10; + } + + result[j] = sum % 10; + carry = sum / 10; + j++; + } + + // Reverse the result array + int left = 0; + int right = j - 1; + while (left < right) { + int temp = result[left]; + result[left] = result[right]; + result[right] = temp; + left++; + right--; + } + + *returnSize = j; + return result; +} diff --git a/c/0994-rotting-oranges.c b/c/0994-rotting-oranges.c new file mode 100644 index 000000000..7286b5040 --- /dev/null +++ b/c/0994-rotting-oranges.c @@ -0,0 +1,43 @@ + +int directions[4][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; + +bool rotting_process(int** grid, int rows, int cols, int timestamp) { + bool continue_process = false; + for (int row = 0; row < rows; row++) { + for (int col = 0; col < cols; col++) { + if (grid[row][col] == timestamp) { + for (int i = 0; i < 4; i++) { + int r = row + directions[i][0]; + int c = col + directions[i][1]; + if (rows > r && r >= 0 && cols > c && c >= 0) { + if (grid[r][c] == 1) { + grid[r][c] = timestamp + 1; + continue_process = true; + } + } + } + } + } + } + return continue_process; +} + +int orangesRotting(int** grid, int gridSize, int* gridColSize){ + + int rows = gridSize; + int cols = gridColSize[0]; + + int timestamp = 2; + while (rotting_process(grid, rows, cols, timestamp)) { + timestamp += 1; + } + + for (int row = 0; row < rows; row++) { + for (int col = 0; col < cols; col++) { + if (grid[row][col] == 1) { + return -1; + } + } + } + return timestamp - 2; +} \ No newline at end of file diff --git a/c/1035-uncrossed-lines.c b/c/1035-uncrossed-lines.c new file mode 100644 index 000000000..133d61ccb --- /dev/null +++ b/c/1035-uncrossed-lines.c @@ -0,0 +1,16 @@ +int maxUncrossedLines(int* nums1, int nums1Size, int* nums2, int nums2Size) { + int dp[nums1Size + 1][nums2Size + 1]; + + for (int i = 0; i <= nums1Size; i++) { + for (int j = 0; j <= nums2Size; j++) { + if (i == 0 || j == 0) + dp[i][j] = 0; + else if (nums1[i - 1] == nums2[j - 1]) + dp[i][j] = dp[i - 1][j - 1] + 1; + else + dp[i][j] = fmax(dp[i - 1][j], dp[i][j - 1]); + } + } + + return dp[nums1Size][nums2Size]; +} diff --git a/c/1046-last-stone-weight.c b/c/1046-last-stone-weight.c new file mode 100644 index 000000000..41aea7fff --- /dev/null +++ b/c/1046-last-stone-weight.c @@ -0,0 +1,84 @@ +#include +#include + +int lastStoneWeight(int *stones, int stonesSize) { + while (stonesSize > 1) { + // Find indices of the two largest stones + int max1 = 0, max2 = 1; + if (stones[max2] > stones[max1]) { + int temp = max1; + max1 = max2; + max2 = temp; + } + + for (int i = 2; i < stonesSize; i++) { + if (stones[i] > stones[max1]) { + max2 = max1; + max1 = i; + } else if (stones[i] > stones[max2]) { + max2 = i; + } + } + + // Smash the stones and update the array + if (stones[max1] != stones[max2]) { + stones[max1] -= stones[max2]; + stones[max2] = 0; + } else { + stones[max1] = 0; + stones[max2] = 0; + } + + // Remove the smashed stones + int newSize = 0; + for (int i = 0; i < stonesSize; i++) { + if (stones[i] != 0) { + stones[newSize++] = stones[i]; + } + } + stonesSize = newSize; + } + + return (stonesSize == 0) ? 0 : stones[0]; +} + +/* +int extractMax(int* stones, int stonesSize) { + int max = stones[0]; + for (int i = 1; i < stonesSize; i++) { + if (stones[i] > max) { + max = stones[i]; + } + } + for (int i = 0; i < stonesSize; i++) { + if (stones[i] == max) { + stones[i] = 0; + break; + } + } + return max; +} + +void insert(int* stones, int stonesSize, int value) { + for (int i = 0; i < stonesSize; i++) { + if (stones[i] == 0) { + stones[i] = value; + return; + } + } +} + +int lastStoneWeight(int* stones, int stonesSize) { + while (1) { + int y = extractMax(stones, stonesSize); + int x = extractMax(stones, stonesSize); + if (x == 0) { + return y; + } + if (x != y) { + insert(stones, stonesSize, y - x); + } + } + return 0; +} +*/ \ No newline at end of file diff --git a/c/1137-n-th-tribonacci-number.c b/c/1137-n-th-tribonacci-number.c new file mode 100644 index 000000000..0db422386 --- /dev/null +++ b/c/1137-n-th-tribonacci-number.c @@ -0,0 +1,17 @@ +int tribonacci(int n) { + if (n == 0) + return 0; + else if (n == 1 || n == 2) + return 1; + + int trib[n + 1]; + trib[0] = 0; + trib[1] = 1; + trib[2] = 1; + + for (int i = 3; i <= n; i++) { + trib[i] = trib[i - 1] + trib[i - 2] + trib[i - 3]; + } + + return trib[n]; +} diff --git a/c/1143-longest-common-subsequence.c b/c/1143-longest-common-subsequence.c new file mode 100644 index 000000000..f592d49be --- /dev/null +++ b/c/1143-longest-common-subsequence.c @@ -0,0 +1,27 @@ +int longestCommonSubsequence(char * text1, char * text2) { + int m = strlen(text1); + int n = strlen(text2); + + int dp[m + 1][n + 1]; // dp[i][j] represents the length of the LCS of text1[0...i-1] and text2[0...j-1] + + // Initialize the first row and column to 0 + for (int i = 0; i <= m; i++) { + dp[i][0] = 0; + } + for (int j = 0; j <= n; j++) { + dp[0][j] = 0; + } + + // Dynamic programming approach to fill the dp array + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + if (text1[i - 1] == text2[j - 1]) { + dp[i][j] = dp[i - 1][j - 1] + 1; + } else { + dp[i][j] = (dp[i - 1][j] > dp[i][j - 1]) ? dp[i - 1][j] : dp[i][j - 1]; + } + } + } + + return dp[m][n]; +} diff --git a/c/1189-Maximum-Number-of-Balloons.c b/c/1189-Maximum-Number-of-Balloons.c new file mode 100644 index 000000000..14770d81c --- /dev/null +++ b/c/1189-Maximum-Number-of-Balloons.c @@ -0,0 +1,36 @@ +/* +Given a string text, you want to use the characters of text to form as many instances of the word "balloon" as possible. + +Space: O(1) +Time: O(n) +*/ + + +int min(int a, int b) { + return ab[1] ) + qsort(intervals, intervalsSize, sizeof(int*), cmp_fun); + + // Treatment of intervals sorted + int end = 0; + int number_remaining = intervalsSize; + for (int i=0; ib[1] ) + qsort(intervals, intervalsSize, sizeof(int*), cmp_fun); + + // Treatment of intervals sorted + int end = 0; + int number_remaining = intervalsSize; + for (int i=0; ib?a:b; +} + +int* replaceElements(int* arr, int arrSize, int* returnSize){ + int greatest = -1; + *returnSize = arrSize; + for (int i=arrSize-1; i>=0; i--) { + int m = greatest; + greatest = max(greatest, arr[i]); + arr[i] = m; + } + return arr; +} diff --git a/c/1299-replace-elements-with-greatest-element-on-right-side.c b/c/1299-replace-elements-with-greatest-element-on-right-side.c new file mode 100644 index 000000000..3ed12545f --- /dev/null +++ b/c/1299-replace-elements-with-greatest-element-on-right-side.c @@ -0,0 +1,21 @@ +/* +Given an array arr, replace every element in that array with the greatest element among the elements to its right, and replace the last element with -1. + +Space: O(1) +Time: O(n) +*/ + +int max(int a, int b) { + return a>b?a:b; +} + +int* replaceElements(int* arr, int arrSize, int* returnSize){ + int greatest = -1; + *returnSize = arrSize; + for (int i=arrSize-1; i>=0; i--) { + int m = greatest; + greatest = max(greatest, arr[i]); + arr[i] = m; + } + return arr; +} diff --git a/c/1406-stone-game-iii.c b/c/1406-stone-game-iii.c new file mode 100644 index 000000000..afcaee300 --- /dev/null +++ b/c/1406-stone-game-iii.c @@ -0,0 +1,25 @@ +char *stoneGameIII(int* stoneValue, int stoneValueSize) { + int n = stoneValueSize; + int dp[n]; + for (int i = 0; i < n; ++i) { + dp[i] = -1e9; + } + + for (int i = n - 1; i >= 0; --i) { + int take = 0; + for (int k = 0; k < 3 && i + k < n; ++k) { + take += stoneValue[i + k]; + dp[i] = (dp[i] > take - ((i + k + 1 < n) ? dp[i + k + 1] : 0)) ? dp[i] : take - ((i + k + 1 < n) ? dp[i + k + 1] : 0); + } + } + + char *result = (char *)malloc(6 * sizeof(char)); // "Alice" or "Bob" or "Tie" plus null terminator + if (dp[0] > 0) { + strcpy(result, "Alice"); + } else if (dp[0] < 0) { + strcpy(result, "Bob"); + } else { + strcpy(result, "Tie"); + } + return result; +} diff --git a/c/1448-Count-Good-Nodes-in-Binary-Tree.c b/c/1448-Count-Good-Nodes-in-Binary-Tree.c new file mode 100644 index 000000000..8d6cd8a03 --- /dev/null +++ b/c/1448-Count-Good-Nodes-in-Binary-Tree.c @@ -0,0 +1,18 @@ +/* +Given a binary tree root, a node X in the tree is named good if in the path from root to X there are no nodes with a value greater than X. +Return the number of good nodes in the binary tree. +Time: O(n) +Space: O(log(h)) Where h is the height of the tree +*/ + +int nbGood(struct TreeNode* root, int m) { + if (root==NULL) + return 0; + if (root->val >= m) + return 1+nbGood(root->left, root->val)+nbGood(root->right, root->val); + return nbGood(root->left, m)+nbGood(root->right, m); +} + +int goodNodes(struct TreeNode* root){ + return nbGood(root, INT_MIN); +} diff --git a/c/1448-count-good-nodes-in-binary-tree.c b/c/1448-count-good-nodes-in-binary-tree.c new file mode 100644 index 000000000..8d6cd8a03 --- /dev/null +++ b/c/1448-count-good-nodes-in-binary-tree.c @@ -0,0 +1,18 @@ +/* +Given a binary tree root, a node X in the tree is named good if in the path from root to X there are no nodes with a value greater than X. +Return the number of good nodes in the binary tree. +Time: O(n) +Space: O(log(h)) Where h is the height of the tree +*/ + +int nbGood(struct TreeNode* root, int m) { + if (root==NULL) + return 0; + if (root->val >= m) + return 1+nbGood(root->left, root->val)+nbGood(root->right, root->val); + return nbGood(root->left, m)+nbGood(root->right, m); +} + +int goodNodes(struct TreeNode* root){ + return nbGood(root, INT_MIN); +} diff --git a/c/1456-maximum-number-of-vowels-in-a-substring-of-given-length.c b/c/1456-maximum-number-of-vowels-in-a-substring-of-given-length.c new file mode 100644 index 000000000..47bd87a82 --- /dev/null +++ b/c/1456-maximum-number-of-vowels-in-a-substring-of-given-length.c @@ -0,0 +1,25 @@ +int isVowel(char c) { + return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u'; +} + +int maxVowels(char* s, int k) { + int windowSum = 0; + int len = strlen(s); + int maxVowelCount; + + // calculate for the first window + for (int i = 0; i < k; i++) { + windowSum += isVowel(s[i]); + } + maxVowelCount = windowSum; + + + for (int i = k; i < len; i++) { + // remove the first ekem of the previous window and add the next element + windowSum = windowSum - isVowel(s[i - k]) + isVowel(s[i]); + if (windowSum > maxVowelCount) { + maxVowelCount = windowSum; + } + } + return maxVowelCount; +} \ No newline at end of file diff --git a/c/1470-shuffle-the-array.c b/c/1470-shuffle-the-array.c new file mode 100644 index 000000000..ed4574650 --- /dev/null +++ b/c/1470-shuffle-the-array.c @@ -0,0 +1,15 @@ +int* shuffle(int* nums, int numsSize, int n, int* returnSize) { + *returnSize = numsSize; + int* result = (int*)malloc(numsSize * sizeof(int)); + + int i, j, k; + i = j = k = 0; + + while (i < n) { + result[k++] = nums[i]; + result[k++] = nums[i + n]; + i++; + } + + return result; +} diff --git a/c/1480-running-sum-of-1d-array.c b/c/1480-running-sum-of-1d-array.c new file mode 100644 index 000000000..4dd0142a6 --- /dev/null +++ b/c/1480-running-sum-of-1d-array.c @@ -0,0 +1,20 @@ +/** + * Given an array nums. We define a running sum of an array as + * runningSum[i] = sum(nums[0]…nums[i]). + * + * Return the running sum of nums. + * + * Time: O(n) + * Space: O(n) + */ + +int* runningSum(int* nums, int numsSize, int* returnSize){ + int *ret = malloc(numsSize * sizeof(int)); + *returnSize = numsSize; + + for (int i = 0; i < numsSize; i++) { + ret[i] = i ? nums[i] + ret[i-1] : nums[i]; + } + + return ret; +} diff --git a/c/1626-best-team-with-no-conflicts.c b/c/1626-best-team-with-no-conflicts.c new file mode 100644 index 000000000..42860ae52 --- /dev/null +++ b/c/1626-best-team-with-no-conflicts.c @@ -0,0 +1,37 @@ +struct Player { + int age; + int score; +}; + +int comparePlayers(const void *a, const void *b) { + struct Player *playerA = (struct Player *)a; + struct Player *playerB = (struct Player *)b; + if (playerA->age != playerB->age) { + return playerA->age - playerB->age; + } else { + return playerA->score - playerB->score; + } +} + +int bestTeamScore(int* scores, int scoresSize, int* ages, int agesSize) { + struct Player players[scoresSize]; + for (int i = 0; i < scoresSize; i++) { + players[i].age = ages[i]; + players[i].score = scores[i]; + } + + qsort(players, scoresSize, sizeof(struct Player), comparePlayers); + + int dp[scoresSize]; + int ans = 0; + for (int i = 0; i < scoresSize; i++) { + dp[i] = players[i].score; + for (int j = 0; j < i; j++) { + if (players[j].score <= players[i].score) { + dp[i] = (dp[i] > dp[j] + players[i].score) ? dp[i] : dp[j] + players[i].score; + } + } + ans = (ans > dp[i]) ? ans : dp[i]; + } + return ans; +} diff --git a/c/1799-maximize-score-after-n-operations.c b/c/1799-maximize-score-after-n-operations.c new file mode 100644 index 000000000..517261356 --- /dev/null +++ b/c/1799-maximize-score-after-n-operations.c @@ -0,0 +1,63 @@ +#include +#include +#include + +int greatestCommonDivisor(int a, int b) { + while (b != 0) { + int temp = b; + b = a % b; + a = temp; + } + return a; +} + +int max(int a, int b) { + return (a > b) ? a : b; +} + +int func(int* nums, int op, int mask, int* dp, int** gcdValues, int m) { + int n = m / 2; + if (op > n) return 0; + if (dp[mask] != -1) return dp[mask]; + + for (int i = 0; i < m; i++) { + if ((mask & (1 << i))) continue; + for (int j = i + 1; j < m; j++) { + if ((mask & (1 << j))) continue; + + int newMask = (1 << i) | (1 << j) | mask; + int score = op * gcdValues[i][j] + func(nums, op + 1, newMask, dp, gcdValues, m); + dp[mask] = max(dp[mask], score); + } + } + return dp[mask]; +} + +int maxScore(int* nums, int numsSize) { + int m = numsSize, n = numsSize / 2; + int** gcdValues = (int**)malloc(m * sizeof(int*)); + for (int i = 0; i < m; i++) { + gcdValues[i] = (int*)malloc(m * sizeof(int)); + } + + for (int i = 0; i < m; i++) { + for (int j = 0; j < m; j++) { + gcdValues[i][j] = greatestCommonDivisor(nums[i], nums[j]); + } + } + + int* dp = (int*)malloc((1 << 14) * sizeof(int)); + for (int i = 0; i < (1 << 14); i++) { + dp[i] = -1; + } + + int result = func(nums, 1, 0, dp, gcdValues, m); + + for (int i = 0; i < m; i++) { + free(gcdValues[i]); + } + free(gcdValues); + free(dp); + + return result; +} diff --git a/c/1822-sign-of-the-product-of-an-array.c b/c/1822-sign-of-the-product-of-an-array.c new file mode 100644 index 000000000..274fdebb8 --- /dev/null +++ b/c/1822-sign-of-the-product-of-an-array.c @@ -0,0 +1,6 @@ +int arraySign(int *nums, int numsSize) { + char prodSign = 1; + for (int i = 0; i < numsSize; i++) + prodSign *= ((unsigned)-nums[i] >> 31) - ((unsigned)nums[i] >> 31); + return prodSign; +} diff --git a/c/1838-frequency-of-the-most-frequent-element.c b/c/1838-frequency-of-the-most-frequent-element.c new file mode 100644 index 000000000..360bb29a8 --- /dev/null +++ b/c/1838-frequency-of-the-most-frequent-element.c @@ -0,0 +1,34 @@ +int greater(const void *a, const void *b) { + return *(int*)a - *(int*)b; +} + +int maxFrequency(int* nums, int numsSize, int k){ + int left, right; + long sum = 0; + qsort(nums, numsSize, sizeof(int), greater); + + // + // Exapmle; + // nums = [2, 3, 1, 4], k = 3 + // + // After the array is sorted + // ------ + // X X X O + // X X O O + // X O O O + // O O O O + // ------ + // 'O' represents the value of the array. + // 'X' represents the value added by each operation. + // Just need to find the longer interval where the number of 'X' + // is less than or equal to k. + // + + for (right = 0, left = 0; right < numsSize; right++) { + sum += nums[right]; + if ((long)(right - left + 1) * nums[right] - sum > k) { + sum -= nums[left++]; + } + } + return right - left; +} diff --git a/c/1856-maximum-subarray-min-product.c b/c/1856-maximum-subarray-min-product.c new file mode 100644 index 000000000..4a1962b41 --- /dev/null +++ b/c/1856-maximum-subarray-min-product.c @@ -0,0 +1,28 @@ +#define MOD 1000000007 + +long long max(long long a, long long b) { + return a > b ? a : b; +} + +int maxSumMinProduct(int* nums, int numsSize) { + long long res = 0; + long long dp[numsSize + 1]; + long long st[numsSize + 1]; + + dp[0] = 0; + for (int i = 0; i < numsSize; ++i) { + dp[i + 1] = dp[i] + nums[i]; + } + + int st_top = -1; + + for (int i = 0; i <= numsSize; ++i) { + while (st_top >= 0 && (i == numsSize || nums[st[st_top]] > nums[i])) { + int j = st[st_top--]; + res = max(res, (long long)nums[j] * (dp[i] - dp[st_top < 0 ? 0 : st[st_top] + 1])); + } + st[++st_top] = i; + } + + return (int)(res % MOD); +} diff --git a/c/1899-merge-triplets-to-form-target-triplet.c b/c/1899-merge-triplets-to-form-target-triplet.c new file mode 100644 index 000000000..7652d7568 --- /dev/null +++ b/c/1899-merge-triplets-to-form-target-triplet.c @@ -0,0 +1,13 @@ +bool mergeTriplets(int** triplets, int tripletsSize, int* tripletsColSize, int* target, int targetSize) { + int x = 0, y = 0, z = 0; + + for (int i = 0; i < tripletsSize; i++) { + if (triplets[i][0] <= target[0] && triplets[i][1] <= target[1] && triplets[i][2] <= target[2]) { + x = (x > triplets[i][0]) ? x : triplets[i][0]; + y = (y > triplets[i][1]) ? y : triplets[i][1]; + z = (z > triplets[i][2]) ? z : triplets[i][2]; + } + } + + return (x == target[0] && y == target[1] && z == target[2]); +} diff --git a/c/1905-Count-Sub-Islands.c b/c/1905-Count-Sub-Islands.c new file mode 100644 index 000000000..c0da237c0 --- /dev/null +++ b/c/1905-Count-Sub-Islands.c @@ -0,0 +1,35 @@ +/* + +Space: O(n²) (due to recursives calls) +Time: O(n²) +*/ + +bool dfs_test(int** grid1, int** grid2, int i, int j, int n, int m) { + // Test if the island in grid2 is entirely in grid1 and delete the island in grid2 + bool ans = (grid1[i][j] == 1); + grid2[i][j] = 0; + if (i>0 && grid2[i-1][j]==1) + ans = dfs_test(grid1, grid2, i-1, j, n ,m) && ans; + if (j>0 && grid2[i][j-1]==1) + ans = dfs_test(grid1, grid2, i, j-1, n ,m) && ans; + if (i<(n-1) && grid2[i+1][j]==1) + ans = dfs_test(grid1, grid2, i+1, j, n ,m) && ans; + if (j<(m-1) && grid2[i][j+1]==1) + ans = dfs_test(grid1, grid2, i, j+1, n ,m) && ans; + return ans; +} + +int countSubIslands(int** grid1, int grid1Size, int* grid1ColSize, int** grid2, int grid2Size, int* grid2ColSize){ + int cpt=0; + for (int i=0; i0 && grid2[i-1][j]==1) + ans = dfs_test(grid1, grid2, i-1, j, n ,m) && ans; + if (j>0 && grid2[i][j-1]==1) + ans = dfs_test(grid1, grid2, i, j-1, n ,m) && ans; + if (i<(n-1) && grid2[i+1][j]==1) + ans = dfs_test(grid1, grid2, i+1, j, n ,m) && ans; + if (j<(m-1) && grid2[i][j+1]==1) + ans = dfs_test(grid1, grid2, i, j+1, n ,m) && ans; + return ans; +} + +int countSubIslands(int** grid1, int grid1Size, int* grid1ColSize, int** grid2, int grid2Size, int* grid2ColSize){ + int cpt=0; + for (int i=0; i (y) ? (x) : (y)) + +int minSwaps(char * s){ + int extraClosingBrackets = 0; + int maxExtraClosingBrackets = 0; + size_t sLen = strlen(s); + + for(size_t i = 0; i < sLen; i++) + { + if(s[i] == ']') + { + extraClosingBrackets += 1; + } + else + { + extraClosingBrackets -= 1; + } + + maxExtraClosingBrackets = max(maxExtraClosingBrackets, extraClosingBrackets); + } + + return (maxExtraClosingBrackets + 1) / 2; +} diff --git a/c/1964-find-the-longest-valid-obstacle-course-at-each-position.c b/c/1964-find-the-longest-valid-obstacle-course-at-each-position.c new file mode 100644 index 000000000..2cb215300 --- /dev/null +++ b/c/1964-find-the-longest-valid-obstacle-course-at-each-position.c @@ -0,0 +1,36 @@ +int upperBound(int *arr, int size, int target) { + int left = 0, right = size - 1; + while (left <= right) { + int mid = left + (right - left) / 2; + if (arr[mid] <= target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return left; +} + +int* longestObstacleCourseAtEachPosition(int* obstacles, int obstaclesSize, int* returnSize) { + int* lis = (int*)malloc(obstaclesSize * sizeof(int)); + int* result = (int*)malloc(obstaclesSize * sizeof(int)); + *returnSize = obstaclesSize; + + int lisSize = 0; + + for (int i = 0; i < obstaclesSize; ++i) { + int x = obstacles[i]; + if (lisSize == 0 || lis[lisSize - 1] <= x) { + lis[lisSize] = x; + result[i] = lisSize + 1; + lisSize++; + } else { + int idx = upperBound(lis, lisSize, x); + lis[idx] = x; + result[i] = idx + 1; + } + } + + free(lis); + return result; +} diff --git a/c/1968-array-with-elements-not-equal-to-average-of-neighbors.c b/c/1968-array-with-elements-not-equal-to-average-of-neighbors.c new file mode 100644 index 000000000..2b36edcc5 --- /dev/null +++ b/c/1968-array-with-elements-not-equal-to-average-of-neighbors.c @@ -0,0 +1,26 @@ +/* +You want to rearrange the elements in the array such that every element +in the rearranged array is not equal to the average of its neighbors + +Space: O(n) +Time: O(nlog(n)) (quicksort) +*/ + +int cmp(const void* a, const void* b) { + return *(int*)a - *(int*)b; +} +int* rearrangeArray(int* nums, int numsSize, int* returnSize){ + int* ans = malloc(sizeof(int)*numsSize); + *returnSize = numsSize; + qsort(nums, numsSize, sizeof(int), cmp); + int i, j=0; + for (i=1; i b) return b; + return a; +} + +int minimumDifference(int* nums, int numsSize, int k) { + if (k == 1) return 0; + + qsort(nums, numsSize, sizeof(int), cmp); + int res = 1e5; + int l = 0; + int r = k - 1; + + while (r < numsSize) + { + res = min(res, nums[r] - nums[l]); + l += 1; + r += 1; + } + + return res; +} \ No newline at end of file diff --git a/c/2001-number-of-pairs-of-interchangeable-rectangles.c b/c/2001-number-of-pairs-of-interchangeable-rectangles.c new file mode 100644 index 000000000..403012213 --- /dev/null +++ b/c/2001-number-of-pairs-of-interchangeable-rectangles.c @@ -0,0 +1,43 @@ +typedef struct hash_entry { + double* ratio; /* we'll use this field as the key */ + long long count; + UT_hash_handle hh; /* makes this structure hashable */ +} hash_entry; + +long long interchangeableRectangles(int** rectangles, int rectanglesSize, int* rectanglesColSize){ + hash_entry* ratioCountMap = NULL; + long long res = 0; + + for(size_t i = 0; i < rectanglesSize; i++) + { + // Calculate the ratio (W / H) + double* ratio = (double*)malloc(sizeof(double)); + *ratio = (double)rectangles[i][0] / rectangles[i][1]; + + hash_entry* retrievedMapEntry; + HASH_FIND_PTR(ratioCountMap, ratio, retrievedMapEntry); + + // If the ratio already exists in the map then increment its count + if(retrievedMapEntry) + { + // Free the allocated memory for the ratio + free(ratio); + retrievedMapEntry->count += 1; + } + else + { + // If the ratio doesn't exist in the map then create a new map entry for it and add it to the map + hash_entry* mapEntryToAdd = (hash_entry*)malloc(sizeof(hash_entry)); + mapEntryToAdd->ratio = ratio; + mapEntryToAdd->count = 1; + HASH_ADD_KEYPTR(hh, ratioCountMap, mapEntryToAdd->ratio, sizeof(double), mapEntryToAdd); + } + } + + for (hash_entry* retrievedMapEntry = ratioCountMap; retrievedMapEntry != NULL; retrievedMapEntry = retrievedMapEntry->hh.next) + { + res += (retrievedMapEntry->count * (retrievedMapEntry->count - 1)) / 2; + } + + return res; +} diff --git a/c/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.c b/c/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.c new file mode 100644 index 000000000..df53e5025 --- /dev/null +++ b/c/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.c @@ -0,0 +1,39 @@ +int palSize(char *s, int size, int mask) { + int left = 0, right = size, res = 0; + while (left <= right) { + if ((mask & (1 << left)) == 0) { + left++; + } else if ((mask & (1 << right)) == 0) { + right--; + } else if (s[left] != s[right]) { + return 0; + } else { + res += 1 + (left++ != right--); + } + } + return res; +} + +int max(int a, int b) { + if (a > b) return a; + return b; +} + +int maxProduct(char * s) { + int n = strlen(s); + int dp[4096] = {}, res = 0, mask = (1 << n) - 1; + int i, j; + + for (i = 1; i <= mask; i++) { + dp[i] = palSize(s, n, i); + } + + for (i = mask; i > 0; i--) { + if (dp[i] == 0) continue; + for(j = mask ^ i; j > 0; j = (j - 1) & (mask ^ i)) { + if (dp[j] == 0) continue; + res = max(res, dp[i] * dp[j]); + } + } + return res; +} diff --git a/c/2140-solving-questions-with-brainpower.c b/c/2140-solving-questions-with-brainpower.c new file mode 100644 index 000000000..0e3c4f7d0 --- /dev/null +++ b/c/2140-solving-questions-with-brainpower.c @@ -0,0 +1,18 @@ +long long mostPoints(int** questions, int questionsSize, int* questionsColSize) { + int n = questionsSize; + long long* dp = (long long*)malloc((n + 1) * sizeof(long long)); + + for (int i = 0; i <= n; i++) { + dp[i] = 0; + } + + for (int i = n - 1; i >= 0; i--) { + int points = questions[i][0]; + int jump = questions[i][1]; + dp[i] = (points + dp[(jump + i + 1) < n ? (jump + i + 1) : n]) > dp[i + 1] ? (points + dp[(jump + i + 1) < n ? (jump + i + 1) : n]) : dp[i + 1]; + } + + long long result = dp[0]; + free(dp); + return result; +} diff --git a/c/2215-find-the-difference-of-two-arrays.c b/c/2215-find-the-difference-of-two-arrays.c new file mode 100644 index 000000000..386e90881 --- /dev/null +++ b/c/2215-find-the-difference-of-two-arrays.c @@ -0,0 +1,179 @@ +/** + * Return an array of arrays of size *returnSize. + * The sizes of the arrays are returned as *returnColumnSizes array. + * Note: Both returned array and *columnSizes array must be malloced, assume + * caller calls free(). + */ + +// Enumeration for more descriptive indexing into arrays +enum QuickSortPositions { LEFT_MOST, RIGHT_MOST }; +enum AnswerArrayIndex { ARRAY_ONE, ARRAY_TWO }; + +// Prototypes +void quicksort(int *nums, int numsSize); +void _quicksort(int *array, int array_length, int left, int right); +int *partition3(int *array, int left, int right); +void swap(int *first_value, int *second_value); + +int **createAnswerArray(int nums1Size, int nums2Size); +void skipRepeatedValues(int *nums, int *numsCurrentIndex, int numsSize); +int hasNotReachedTheEndOfOneOfTheArrays(int nums1CurrentIndex, int nums1Size, + int nums2CurrentIndex, int nums2Size); +void processArrays(int *nums1, int nums1Size, int *nums2, int nums2Size, + int *answerArrayOne, int *answerArrayTwo, + int *answerArrayOneSize, int *answerArrayTwoSize); + +int **findDifference(int *nums1, int nums1Size, int *nums2, int nums2Size, + int *returnSize, int **returnColumnSizes) { + // Preparing return values + *returnSize = 2; + *returnColumnSizes = (int *)calloc(*returnSize, sizeof(int)); + int *answerArrayOneSize = &(*returnColumnSizes)[ARRAY_ONE]; + int *answerArrayTwoSize = &(*returnColumnSizes)[ARRAY_TWO]; + + int **answer = createAnswerArray(nums1Size, nums2Size); + int *answerArrayOne = answer[ARRAY_ONE]; + int *answerArrayTwo = answer[ARRAY_TWO]; + + // This approach needs sorted arrays + quicksort(nums1, nums1Size); + quicksort(nums2, nums2Size); + + // Populate the answer arrays + processArrays(nums1, nums1Size, nums2, nums2Size, answerArrayOne, + answerArrayTwo, answerArrayOneSize, answerArrayTwoSize); + + return answer; +} + +void processArrays(int *nums1, int nums1Size, int *nums2, int nums2Size, + int *answerArrayOne, int *answerArrayTwo, + int *answerArrayOneSize, int *answerArrayTwoSize) { + int nums1CurrentIndex = 0; + int nums2CurrentIndex = 0; + + while (hasNotReachedTheEndOfOneOfTheArrays(nums1CurrentIndex, nums1Size, + nums2CurrentIndex, nums2Size)) { + skipRepeatedValues(nums1, &nums1CurrentIndex, nums1Size); + skipRepeatedValues(nums2, &nums2CurrentIndex, nums2Size); + + if (nums1[nums1CurrentIndex] == nums2[nums2CurrentIndex]) { + nums1CurrentIndex++; + nums2CurrentIndex++; + } else if (nums1[nums1CurrentIndex] < nums2[nums2CurrentIndex]) + answerArrayOne[(*answerArrayOneSize)++] = nums1[nums1CurrentIndex++]; + else + answerArrayTwo[(*answerArrayTwoSize)++] = nums2[nums2CurrentIndex++]; + } + + // Populate remaining unique elements + while (nums1CurrentIndex < nums1Size) { + skipRepeatedValues(nums1, &nums1CurrentIndex, nums1Size); + answerArrayOne[(*answerArrayOneSize)++] = nums1[nums1CurrentIndex++]; + } + while (nums2CurrentIndex < nums2Size) { + skipRepeatedValues(nums2, &nums2CurrentIndex, nums2Size); + answerArrayTwo[(*answerArrayTwoSize)++] = nums2[nums2CurrentIndex++]; + } +} + +// Helper Functions +void skipRepeatedValues(int *nums, int *numsCurrentIndex, int numsSize) { + while (*numsCurrentIndex < numsSize - 1 && + nums[*numsCurrentIndex] == nums[*numsCurrentIndex + 1]) { + (*numsCurrentIndex)++; + } +} + +int hasNotReachedTheEndOfOneOfTheArrays(int nums1CurrentIndex, int nums1Size, + int nums2CurrentIndex, int nums2Size) { + return nums1CurrentIndex < nums1Size && nums2CurrentIndex < nums2Size; +} + +int **createAnswerArray(int nums1Size, int nums2Size) { + int **answer = (int **)malloc(2 * sizeof(int *)); + answer[ARRAY_ONE] = (int *)malloc(nums1Size * sizeof(int)); + answer[ARRAY_TWO] = (int *)malloc(nums2Size * sizeof(int)); + return answer; +} + +// QuickSort Implementation +void quicksort(int *nums, int numsSize) { + _quicksort(nums, numsSize, 0, numsSize - 1); +} + +void _quicksort(int *array, int array_length, int left, int right) { + while (left < right) { + int *middle = partition3(array, left, right); + if (middle[LEFT_MOST] - left < right - middle[RIGHT_MOST]) { + _quicksort(array, array_length, left, middle[LEFT_MOST] - 1); + left = middle[RIGHT_MOST] + 1; + } else { + _quicksort(array, array_length, middle[RIGHT_MOST] + 1, right); + right = middle[LEFT_MOST] - 1; + } + free(middle); + } +} + +int *partition3(int *array, int left, int right) { + int pivot_position = left + (right - left) / 2; + int pivot = array[pivot_position]; + swap(&array[left], &array[pivot_position]); + + int *middle = (int *)malloc(2 * sizeof(int)); + + middle[LEFT_MOST] = left; + middle[RIGHT_MOST] = left; + + for (int i = left + 1; i <= right; ++i) { + if (array[i] == pivot) { + middle[RIGHT_MOST]++; + swap(&array[middle[RIGHT_MOST]], &array[i]); + } + if (array[i] < pivot) { + middle[LEFT_MOST]++; + middle[RIGHT_MOST]++; + swap(&array[middle[RIGHT_MOST]], &array[i]); + swap(&array[middle[LEFT_MOST]], &array[middle[RIGHT_MOST]]); + } + } + swap(&array[left], &array[middle[LEFT_MOST]]); + return middle; +} + +void swap(int *first_value, int *second_value) { + int tmp_value = *first_value; + *first_value = *second_value; + *second_value = tmp_value; +} + +// Simpler answer with Sets: + +int **findDifference(int *nums1, int nums1Size, int *nums2, int nums2Size, + int *returnSize, int **returnColumnSizes) { + int **answer = (int **)malloc(2 * sizeof(int *)); + answer[0] = (int *)malloc(nums1Size * sizeof(int)); + answer[1] = (int *)malloc(nums2Size * sizeof(int)); + + *returnSize = 2; + *returnColumnSizes = (int *)calloc(*returnSize, sizeof(int)); + int *answerArrayOneSize = &(*returnColumnSizes)[0]; + int *answerArrayTwoSize = &(*returnColumnSizes)[1]; + + int *table = (int *)calloc(2001, sizeof(int)); + + for (int i = 0; i < nums1Size; i++) { + table[nums1[i] + 1000] = 1; + } + for (int i = 0; i < nums2Size; i++) { + if (table[nums2[i] + 1000] == 0) + answer[1][(*answerArrayTwoSize)++] = nums2[i]; + table[nums2[i] + 1000] = -1; + } + + for (int i = 0; i < 2001; i++) { + if (table[i] == 1) answer[0][(*answerArrayOneSize)++] = i - 1000; + } + return answer; +} diff --git a/c/2466-count-ways-to-build-good-strings.c b/c/2466-count-ways-to-build-good-strings.c new file mode 100644 index 000000000..c695ea5b7 --- /dev/null +++ b/c/2466-count-ways-to-build-good-strings.c @@ -0,0 +1,26 @@ +int countGoodStrings(int low, int high, int zero, int one) { + int mod = 1000000007; + int* dp = (int*)malloc((high + 1) * sizeof(int)); + + dp[0] = 1; + int res = 0; + + for (int i = 1; i <= high; ++i) { + dp[i] = 0; + + if (i >= zero) { + dp[i] = (dp[i] + dp[i - zero]) % mod; + } + + if (i >= one) { + dp[i] = (dp[i] + dp[i - one]) % mod; + } + + if (i >= low) { + res = (res + dp[i]) % mod; + } + } + + free(dp); + return res; +} diff --git a/cpp/0001-two-sum.cpp b/cpp/0001-two-sum.cpp new file mode 100644 index 000000000..5ce94c170 --- /dev/null +++ b/cpp/0001-two-sum.cpp @@ -0,0 +1,26 @@ +/* + Given int array & target, return indices of 2 nums that add to target + Ex. nums = [2,7,11,15] & target = 9 -> [0,1], 2 + 7 = 9 + + At each num, calculate complement, if exists in hash map then return + + Time: O(n) + Space: O(n) +*/ + +class Solution { +public: + vector twoSum(vector& nums, int target) { + int n = nums.size(); + unordered_map mp; // val -> index + + for (int i = 0; i < n; i++) { + int complement = target - nums[i]; + if (mp.find(complement) != mp.end()) { + return {mp[complement], i}; + } + mp.insert({nums[i], i}); + } + return {}; + } +}; diff --git a/cpp/0002-add-two-numbers.cpp b/cpp/0002-add-two-numbers.cpp new file mode 100644 index 000000000..4d8e96d34 --- /dev/null +++ b/cpp/0002-add-two-numbers.cpp @@ -0,0 +1,53 @@ +/* + Given 2 linked lists, digits stored in reverse order, add them + Ex. l1 = [2,4,3] l2 = [5,6,4] -> [7,0,8] (342 + 465 = 807) + + Sum digit-by-digit + carry, handle if one list becomes null + + Time: O(max(m, n)) + Space: O(max(m, n)) +*/ + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { + ListNode* dummy = new ListNode(); + + ListNode* curr = dummy; + int carry = 0; + + while (l1 != NULL || l2 != NULL) { + int val1 = (l1 != NULL) ? l1->val : 0; + int val2 = (l2 != NULL) ? l2->val : 0; + + int sum = val1 + val2 + carry; + carry = sum / 10; + + curr->next = new ListNode(sum % 10); + curr = curr->next; + + if (l1 != NULL) { + l1 = l1->next; + } + if (l2 != NULL) { + l2 = l2->next; + } + } + + if (carry == 1) { + curr->next = new ListNode(1); + } + + return dummy->next; + } +}; diff --git a/cpp/0003-longest-substring-without-repeating-characters.cpp b/cpp/0003-longest-substring-without-repeating-characters.cpp new file mode 100644 index 000000000..050098abe --- /dev/null +++ b/cpp/0003-longest-substring-without-repeating-characters.cpp @@ -0,0 +1,56 @@ +/* + Given string, find longest substring w/o repeating chars + Ex. s = "abcabcbb" -> 3 "abc", s = "bbbbb" -> 1 "b" + + Sliding window, expand if unique, contract if duplicate + + Time: O(n) + Space: O(n) +*/ +/* +class Solution { +public: + int lengthOfLongestSubstring(string s) { + unordered_set letters; + + int i = 0; + int j = 0; + + int result = 0; + + while (j < s.size()) { + if (letters.find(s[j]) == letters.end()) { + letters.insert(s[j]); + result = max(result, j - i + 1); + j++; + } else { + letters.erase(s[i]); + i++; + } + } + + return result; + } +}; +*/ +// Same solution as above with the same amount of total iterations. +// Above solution: no inner loop, but the "j" variable is not increased at each iteration +// Below: inner loop increasing "i", outer loop increasing "j". +class Solution { +public: + int lengthOfLongestSubstring(string& s) { + unordered_set chars; + int maxSize = 0; + int i = 0, j = 0; + while (j < s.size()){ + while (chars.find(s[j]) != chars.end()){ + chars.erase(s[i]); + ++i; + } + maxSize = max(maxSize, j - i + 1); + chars.insert(s[j]); + ++j; + } + return maxSize; + } +}; diff --git a/cpp/0004-median-of-two-sorted-arrays.cpp b/cpp/0004-median-of-two-sorted-arrays.cpp new file mode 100644 index 000000000..d6f3f5d58 --- /dev/null +++ b/cpp/0004-median-of-two-sorted-arrays.cpp @@ -0,0 +1,62 @@ +/* + Given 2 sorted arrays of size m & n, return the median of these arrays + Ex. nums1 = [1,3] nums2 = [2] -> 2, nums1 = [1,2] nums2 = [3,4] -> 2.5 + + Binary search, partition each array until partitions are correct, get median + [1,2,3,4,5] + | a|b | + [1,2,3,4,5,6,7,8] --> a <= d ? yes, c <= b ? no, so need to fix + | c|d | + + Time: O(log min(m, n)) + Space: O(1) +*/ + +class Solution { +public: + double findMedianSortedArrays(vector& nums1, vector& nums2) { + int m = nums1.size(); + int n = nums2.size(); + + if (m > n) { + return findMedianSortedArrays(nums2, nums1); + } + + int total = m + n; + + int low = 0; + int high = m; + + double result = 0.0; + + while (low <= high) { + // nums1 + int i = low + (high - low) / 2; + // nums2 + int j = (total + 1) / 2 - i; + + int left1 = (i > 0) ? nums1[i - 1] : INT_MIN; + int right1 = (i < m) ? nums1[i] : INT_MAX; + int left2 = (j > 0) ? nums2[j - 1] : INT_MIN; + int right2 = (j < n) ? nums2[j] : INT_MAX; + + // partition is correct + if (left1 <= right2 && left2 <= right1) { + // even + if (total % 2 == 0) { + result = (max(left1, left2) + min(right1, right2)) / 2.0; + // odd + } else { + result = max(left1, left2); + } + break; + } else if (left1 > right2) { + high = i - 1; + } else { + low = i + 1; + } + } + + return result; + } +}; diff --git a/cpp/0005-longest-palindromic-substring.cpp b/cpp/0005-longest-palindromic-substring.cpp new file mode 100644 index 000000000..38c54c462 --- /dev/null +++ b/cpp/0005-longest-palindromic-substring.cpp @@ -0,0 +1,35 @@ +/* + Given a string s, return the longest palindromic substring in s + Ex. s = "babad" -> "bab", s = "cbbd" -> "bb" + + Expand around center, extend as far as possible, store max length + + Time: O(n^2) + Space: O(1) +*/ + +class Solution { +public: + string longestPalindrome(string s) { + int maxStart = 0; + int maxLength = 1; + + for (int i = 0; i < s.size() - 1; i++) { + middleOut(s, i, i, maxStart, maxLength); + middleOut(s, i, i + 1, maxStart, maxLength); + } + + return s.substr(maxStart, maxLength); + } +private: + void middleOut(string s, int i, int j, int& maxStart, int& maxLength) { + while (i >= 0 && j <= s.size() - 1 && s[i] == s[j]) { + i--; + j++; + } + if (j - i - 1 > maxLength) { + maxStart = i + 1; + maxLength = j - i - 1; + } + } +}; diff --git a/cpp/0006-zigzag-conversion.cpp b/cpp/0006-zigzag-conversion.cpp new file mode 100644 index 000000000..b5614b2f7 --- /dev/null +++ b/cpp/0006-zigzag-conversion.cpp @@ -0,0 +1,30 @@ +/* + For each row the next chracter is at index 2 * (n -1) and + For middle rows there will be extra characters + Time: O(n) + Space: O(1) +*/ +class Solution { +public: + string convert(string s, int n) { + // Edge case + if(n == 1) return s; + // Other cases + // Take string to store answer + string ans = ""; + // We are going to traverse each row + for(int row = 0; row < n ; row++){ + // for each row the next chracter is at index 2 * (n -1) + int increment = 2 * (n -1); + // For first and last rows + for(int i = row; i < s.length(); i+= increment){ + ans += s[i]; + // For middle rows there will be extra characters + if(row > 0 && row < n-1 && i+increment - 2 * row < s.length()){ + ans += s[i+increment - 2 * row]; + } + } + } + return ans; + } +}; diff --git a/cpp/0007-reverse-integer.cpp b/cpp/0007-reverse-integer.cpp new file mode 100644 index 000000000..8486ac0a3 --- /dev/null +++ b/cpp/0007-reverse-integer.cpp @@ -0,0 +1,28 @@ +/* + Given a signed 32-bit integer, return it with its digits reversed + Ex. x = 123 -> 321, x = -123 -> -321, x = 120 -> 21 + + Reverse bit-by-bit starting from right, shift right off every time + + Time: O(log x) + Space: O(1) +*/ + +class Solution { +public: + int reverse(int x) { + int rev = 0; + while (x != 0) { + int temp = x % 10; + x /= 10; + if (rev > INT_MAX / 10 || (rev == INT_MAX / 10 && temp > 7)) { + return 0; + } + if (rev < INT_MIN / 10 || (rev == INT_MIN / 10 && temp < -8)) { + return 0; + } + rev = rev * 10 + temp; + } + return rev; + } +}; diff --git a/cpp/0009-palindrome-number.cpp b/cpp/0009-palindrome-number.cpp new file mode 100644 index 000000000..0d2be0412 --- /dev/null +++ b/cpp/0009-palindrome-number.cpp @@ -0,0 +1,21 @@ +class Solution { +public: + bool isPalindrome(int x) { + if(x < 0) return false; + + long div = 1; + while(x >= 10 * div) + div *= 10; + + while(x) { + int right = x % 10; + int left = x / div; + + if(left != right) return false; + + x = (x % div) / 10; + div = div / 100; + } + return true; + } +}; diff --git a/cpp/0010-regular-expression-matching.cpp b/cpp/0010-regular-expression-matching.cpp new file mode 100644 index 000000000..e8088e799 --- /dev/null +++ b/cpp/0010-regular-expression-matching.cpp @@ -0,0 +1,49 @@ +/* + Given string & pattern, implement RegEx matching + '.' -> matches any single character + '*' -> matches zero or more of the preceding element + Matching should cover the entire input string (not partial) + Ex. s = "aa", p = "a" -> false, "a" doesn't match entire string "aa" + + DFS + memo, 2 choices at a *: either use it, or don't use it + + Time: O(m x n) + Space: O(m x n) +*/ + +class Solution { +public: + bool isMatch(string s, string p) { + return dfs(s, p, 0, 0); + } +private: + map, bool> dp; + + bool dfs(string& s, string& p, int i, int j) { + if (dp.find({i, j}) != dp.end()) { + return dp[{i, j}]; + } + + if (i >= s.size() && j >= p.size()) { + return true; + } + if (j >= p.size()) { + return false; + } + + bool match = i < s.size() && (s[i] == p[j] || p[j] == '.'); + if (j + 1 < p.size() && p[j + 1] == '*') { + // choices: either (1) don't use *, or (2) use * + dp[{i, j}] = dfs(s, p, i, j + 2) || (match && dfs(s, p, i + 1, j)); + return dp[{i, j}]; + } + + if (match) { + dp[{i, j}] = dfs(s, p, i + 1, j + 1); + return dp[{i, j}]; + } + + dp[{i, j}] = false; + return dp[{i, j}]; + } +}; diff --git a/cpp/0011-container-with-most-water.cpp b/cpp/0011-container-with-most-water.cpp new file mode 100644 index 000000000..5d5ff435d --- /dev/null +++ b/cpp/0011-container-with-most-water.cpp @@ -0,0 +1,33 @@ +/* + Given array of heights, find max water container can store + Ex. height = [1,8,6,2,5,4,8,3,7] -> 49, (8 - 1) x min(8, 7) + + 2 pointers outside in, greedily iterate pointer w/ lower height + + Time: O(n) + Space: O(1) +*/ + +class Solution { +public: + int maxArea(vector& height) { + int i = 0; + int j = height.size() - 1; + + int curr = 0; + int result = 0; + + while (i < j) { + curr = (j - i) * min(height[i], height[j]); + result = max(result, curr); + + if (height[i] <= height[j]) { + i++; + } else { + j--; + } + } + + return result; + } +}; diff --git a/cpp/0012-integer-to-roman.cpp b/cpp/0012-integer-to-roman.cpp new file mode 100644 index 000000000..7615e5726 --- /dev/null +++ b/cpp/0012-integer-to-roman.cpp @@ -0,0 +1,95 @@ +/* Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M. + +Symbol Value +I 1 +V 5 +X 10 +L 50 +C 100 +D 500 +M 1000 +For example, 2 is written as II in Roman numeral, just two one's added together. 12 is written as XII, which is simply X + II. The number 27 is written as XXVII, which is XX + V + II. + +Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same principle applies to the number nine, which is written as IX. There are six instances where subtraction is used: + +I can be placed before V (5) and X (10) to make 4 and 9. +X can be placed before L (50) and C (100) to make 40 and 90. +C can be placed before D (500) and M (1000) to make 400 and 900. +Given an integer, convert it to a roman numeral. +Example +Input: num = 1994 +Output: "MCMXCIV" +Explanation: M = 1000, CM = 900, XC = 90 and IV = 4. */ + +class Solution { +public: + string intToRoman(int num) { + string s=""; + while(num>=1000) + { + s+="M"; + num=num-1000; + } + if(num>=900) + { + s+="CM"; + num=num-900; + } + while(num>=500) + { + s+="D"; + num=num-500; + } + if(num>=400) + { + s+="CD"; + num=num-400; + } + while(num>=100) + { + s+="C"; + num=num-100; + } + if(num>=90) + { + s+="XC"; + num=num-90; + } + while(num>=50) + { + s+="L"; + num=num-50; + } + if(num>=40) + { + s+="XL"; + num=num-40; + } + while(num>=10) + { + s+="X"; + num=num-10; + } + if(num>=9) + { + s+="IX"; + num=num-9; + } + while(num>=5) + { + s+="V"; + num=num-5; + } + if(num>=4) + { + s+="IV"; + num=num-4; + } + while(num>=1) + { + s+="I"; + num=num-1; + } + return s; + } +}; \ No newline at end of file diff --git a/cpp/0013-roman-to-integer.cpp b/cpp/0013-roman-to-integer.cpp new file mode 100644 index 000000000..20773c4bf --- /dev/null +++ b/cpp/0013-roman-to-integer.cpp @@ -0,0 +1,87 @@ +/*Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M. + +Symbol Value +I 1 +V 5 +X 10 +L 50 +C 100 +D 500 +M 1000 +For example, 2 is written as II in Roman numeral, just two ones added together. 12 is written as XII, which is simply X + II. The number 27 is written as XXVII, which is XX + V + II. + +Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same principle applies to the number nine, which is written as IX. There are six instances where subtraction is used: + +I can be placed before V (5) and X (10) to make 4 and 9. +X can be placed before L (50) and C (100) to make 40 and 90. +C can be placed before D (500) and M (1000) to make 400 and 900. +Given a roman numeral, convert it to an integer. +Example 3: + +Input: s = "MCMXCIV" +Output: 1994 +Explanation: M = 1000, CM = 900, XC = 90 and IV = 4. */ + +int prec(char x){ + switch(x){ + case 'I': + return 1; + case 'V': + return 2; + case 'X': + return 3; + case 'L': + return 4; + case 'C': + return 5; + case 'D': + return 6; + case 'M': + return 7; + default: + return -1; + } +} + +int val(char x){ + switch(x){ + case 'I': + return 1; + case 'V': + return 5; + case 'X': + return 10; + case 'L': + return 50; + case 'C': + return 100; + case 'D': + return 500; + case 'M': + return 1000; + default: + return -1; + } +} + +class Solution { +public: + + int romanToInt(string s) + { + int ans = 0; + + for(int i=0; i= prec(s[i+1])){ + ans += val(s[i]); + } + else if(prec(s[i]) < prec(s[i+1])){ + ans = ans - val(s[i]) + val(s[i+1]); + i++; + } + } + + return ans; + } + +}; diff --git a/cpp/0014-longest-common-prefix.cpp b/cpp/0014-longest-common-prefix.cpp new file mode 100644 index 000000000..f4931a6f0 --- /dev/null +++ b/cpp/0014-longest-common-prefix.cpp @@ -0,0 +1,28 @@ +class Solution { +public: + string longestCommonPrefix(vector& strs) { + string result = strs[0]; + int charIndex = 0; + + //finding minimum string length - that could be max common prefix + long maxCharIndex = strs[0].length(); + for (int i = 1; i < strs.size(); ++i) { + if (strs[i].length() < maxCharIndex) { + maxCharIndex = strs[i].length(); + } + } + + while (charIndex < maxCharIndex) { + char prevChar = strs[0][charIndex]; + for (int i = 1; i < strs.size(); ++i) { + if (prevChar == strs[i][charIndex]) { + continue; + } + return result.substr(0, charIndex); + } + ++charIndex; + result += prevChar; + } + return result.substr(0, charIndex); + } +}; diff --git a/cpp/0015-3sum.cpp b/cpp/0015-3sum.cpp new file mode 100644 index 000000000..654a8b07c --- /dev/null +++ b/cpp/0015-3sum.cpp @@ -0,0 +1,59 @@ +/* + Given int array, return all unique triplets that sum to 0 + Ex. nums = [-1,0,1,2,-1,-4] -> [[-1,-1,2],[-1,0,1]] + + Sort, for each i, have j & k go outside in, check for 0 sums + + Time: O(n^2) + Space: O(n) +*/ + +class Solution { +public: + vector> threeSum(vector& nums) { + vector> result; + + int n = nums.size(); + if (n < 3) { + return result; + } + + sort(nums.begin(), nums.end()); + + for (int i = 0; i < n - 2; i++) { + if (nums[i] > 0) { + break; + } + if (i > 0 && nums[i - 1] == nums[i]) { + continue; + } + + int j = i + 1; + int k = n - 1; + + while (j < k) { + int sum = nums[i] + nums[j] + nums[k]; + + if (sum < 0) { + j++; + } else if (sum > 0) { + k--; + } else { + result.push_back({nums[i], nums[j], nums[k]}); + + while (j < k && nums[j] == nums[j + 1]) { + j++; + } + j++; + + while (j < k && nums[k - 1] == nums[k]) { + k--; + } + k--; + } + } + } + + return result; + } +}; diff --git a/cpp/0017-letter-combinations-of-a-phone-number.cpp b/cpp/0017-letter-combinations-of-a-phone-number.cpp new file mode 100644 index 000000000..e2e87dcf5 --- /dev/null +++ b/cpp/0017-letter-combinations-of-a-phone-number.cpp @@ -0,0 +1,47 @@ +/* + Given cell phone pad, return all possible letter combos that the number could represent + Ex. digits = "23" -> ["ad","ae","af","bd","be","bf","cd","ce","cf"] + + Hash map all digits to letters, add 1 letter at a time for each digit, then backtrack undo + + Time: O(n x 4^n) + Space: O(n x 4^n) +*/ + +class Solution { +public: + vector letterCombinations(string digits) { + if (digits.empty()) { + return {}; + } + + unordered_map m = { + {'2', "abc"}, + {'3', "def"}, + {'4', "ghi"}, + {'5', "jkl"}, + {'6', "mno"}, + {'7', "pqrs"}, + {'8', "tuv"}, + {'9', "wxyz"} + }; + string curr = ""; + vector result; + + dfs(digits, 0, m, curr, result); + return result; + } +private: + void dfs(string digits, int index, unordered_map& m, string& curr, vector& result) { + if (index == digits.size()) { + result.push_back(curr); + return; + } + string str = m[digits[index]]; + for (int i = 0; i < str.size(); i++) { + curr.push_back(str[i]); + dfs(digits, index + 1, m, curr, result); + curr.pop_back(); + } + } +}; diff --git a/cpp/0018-4sum.cpp b/cpp/0018-4sum.cpp new file mode 100644 index 000000000..430cd864f --- /dev/null +++ b/cpp/0018-4sum.cpp @@ -0,0 +1,36 @@ +// Time Complexity = O(n^3) +// Space Complexity = O(n) + +class Solution { +public: + vector> fourSum(vector& nums, int target) { + sort(nums.begin(), nums.end()); + + vector > res; + int n = nums.size(); + + for (int i = 0; i < n; i++) { + if (i > 0 && nums[i] == nums[i - 1]) + continue; + for (int j = i + 1; j < n; j++) { + if (j > (i + 1) && nums[j] == nums[j - 1]) + continue; + int l = j + 1, r = n - 1; + while (l < r) { + long sm = (long)nums[i] + (long)nums[j] + (long)nums[l] + (long)nums[r]; + if (sm == target) { + res.push_back(vector{nums[i], nums[j], nums[l], nums[r]}); + l += 1; + while (l < r && nums[l] == nums[l - 1]) + l += 1; + } + else if (sm > target) + r -= 1; + else + l += 1; + } + } + } + return res; + } +}; diff --git a/cpp/0019-remove-nth-node-from-end-of-list.cpp b/cpp/0019-remove-nth-node-from-end-of-list.cpp new file mode 100644 index 000000000..af139e8e0 --- /dev/null +++ b/cpp/0019-remove-nth-node-from-end-of-list.cpp @@ -0,0 +1,48 @@ +/* + Given head of a linked list, remove nth node from end of list + Ex. head = [1,2,3,4,5], n = 2 -> [1,2,3,5] + + Create 2 pointers "n" apart, iterate until end, will be at nth + + Time: O(n) + Space: O(1) +*/ + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* removeNthFromEnd(ListNode* head, int n) { + if (head->next == NULL) { + return NULL; + } + + ListNode* slow = head; + ListNode* fast = head; + + while (n > 0) { + fast = fast->next; + n--; + } + + if (fast == NULL) { + return head->next; + } + + while (fast->next != NULL) { + slow = slow->next; + fast = fast->next; + } + + slow->next = slow->next->next; + return head; + } +}; diff --git a/cpp/0020-valid-parentheses.cpp b/cpp/0020-valid-parentheses.cpp new file mode 100644 index 000000000..58c5fbf56 --- /dev/null +++ b/cpp/0020-valid-parentheses.cpp @@ -0,0 +1,40 @@ +/* + Given s w/ '(, ), {, }, [, ]', determine if valid + Ex. s = "()[]{}" -> true, s = "(]" -> false + + Stack of opens, check for matching closes & validity + + Time: O(n) + Space: O(n) +*/ + +class Solution { +public: + bool isValid(string s) { + stack open; + unordered_map parens = { + {')', '('}, + {']', '['}, + {'}', '{'}, + }; + + for (const auto& c : s) { + if (parens.find(c) != parens.end()) { + // if input starts with a closing bracket. + if (open.empty()) { + return false; + } + + if (open.top() != parens[c]) { + return false; + } + + open.pop(); + } else { + open.push(c); + } + } + + return open.empty(); + } +}; diff --git a/cpp/0021-merge-two-sorted-lists.cpp b/cpp/0021-merge-two-sorted-lists.cpp new file mode 100644 index 000000000..cae89422e --- /dev/null +++ b/cpp/0021-merge-two-sorted-lists.cpp @@ -0,0 +1,55 @@ +/* + Given heads of 2 sorted linked lists, merge into 1 sorted list + Ex. list1 = [1,2,4], list2 = [1,3,4] -> [1,1,2,3,4,4] + + Create curr pointer, iterate thru, choose next to be lower one + + Time: O(m + n) + Space: O(1) +*/ + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) { + if (list1 == NULL && list2 == NULL) { + return NULL; + } + if (list1 == NULL) { + return list2; + } + if (list2 == NULL) { + return list1; + } + + ListNode* dummy = new ListNode(); + ListNode *curr = dummy; + while (list1 != NULL && list2 != NULL) { + if (list1->val <= list2->val) { + curr->next = list1; + list1 = list1->next; + } else { + curr->next = list2; + list2 = list2->next; + } + curr = curr->next; + } + + if (list1 == NULL) { + curr->next = list2; + } else { + curr->next = list1; + } + + return dummy->next; + } +}; diff --git a/cpp/0022-generate-parentheses.cpp b/cpp/0022-generate-parentheses.cpp new file mode 100644 index 000000000..0a42f1ca0 --- /dev/null +++ b/cpp/0022-generate-parentheses.cpp @@ -0,0 +1,66 @@ +/* + Given n pairs of parentheses, generate all combos of well-formed parentheses + Ex. n = 3 -> ["((()))","(()())","(())()","()(())","()()()"], n = 1 -> ["()"] + + Backtracking, keep valid, favor trying opens, then try closes if still valid + + Time: O(2^n) + Space: O(n) +*/ + +class Solution { +public: + vector generateParenthesis(int n) { + vector result; + generate(n, 0, 0, "", result); + return result; + } +private: + void generate(int n, int open, int close, string str, vector& result) { + if (open == n && close == n) { + result.push_back(str); + return; + } + if (open < n) { + generate(n, open + 1, close, str + '(', result); + } + if (open > close) { + generate(n, open, close + 1, str + ')', result); + } + } +}; + +/* + Using a single stack without recursion + + Time: O(2^n) + Space: O(n) +*/ +class Solution { +public: + vector generateParenthesis(int n) { + stack> stk; + vector result; + stk.push({"(", "1", "0"}); // {string, left_count, right_count} + + while (!stk.empty()) { + for (int i = 0; i < stk.size(); i++) { + vector item = stk.top(); + stk.pop(); + int left = stoi(item[1]), right = stoi(item[2]); + if (left == n && right == n) { + result.push_back(item[0]); + continue; + } + if (left < n) { + stk.push({item[0] + "(", to_string(++left), to_string(right)}); + left--; // reverse left count + } + if (left > right) { + stk.push({item[0] + ")", to_string(left), to_string(++right)}); + } + } + } + return result; + } +}; diff --git a/cpp/0023-merge-k-sorted-lists.cpp b/cpp/0023-merge-k-sorted-lists.cpp new file mode 100644 index 000000000..26fb63d89 --- /dev/null +++ b/cpp/0023-merge-k-sorted-lists.cpp @@ -0,0 +1,106 @@ +/* + Given array of k sorted linked-lists, merge all into 1 sorted list + Ex. lists = [[1,4,5],[1,3,4],[2,6]] -> [1,1,2,3,4,4,5,6] + + Min heap -> optimize space w/ divide-and-conquer, merge 2 each time + + Time: O(n log k) + Space: O(n) -> O(1) +*/ + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +// class Solution { +// public: +// ListNode* mergeKLists(vector& lists) { +// priority_queue, greater> pq; +// for (int i = 0; i < lists.size(); i++) { +// ListNode* node = lists[i]; +// while (node != NULL) { +// pq.push(node->val); +// node = node->next; +// } +// } +// if (pq.empty()) { +// return NULL; +// } +// ListNode* node = new ListNode(pq.top()); +// pq.pop(); +// ListNode* head = node; +// while (!pq.empty()) { +// node->next = new ListNode(pq.top()); +// pq.pop(); +// node = node->next; +// } +// return head; +// } +// }; + +class Solution { +public: + ListNode* mergeKLists(vector& lists) { + int n = lists.size(); + if (n == 0) { + return NULL; + } + + while (n > 1) { + for (int i = 0; i < n / 2; i++) { + lists[i] = mergeTwoLists(lists[i], lists[n - i - 1]); + } + n = (n + 1) / 2; + } + + return lists.front(); + } +private: + ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) { + if (list1 == NULL && list2 == NULL) { + return NULL; + } + if (list1 == NULL) { + return list2; + } + if (list2 == NULL) { + return list1; + } + + ListNode* head = NULL; + if (list1->val <= list2->val) { + head = list1; + list1 = list1->next; + } else { + head = list2; + list2 = list2->next; + } + ListNode* curr = head; + + while (list1 != NULL && list2 != NULL) { + if (list1->val <= list2->val) { + curr->next = list1; + list1 = list1->next; + } else { + curr->next = list2; + list2 = list2->next; + } + curr = curr->next; + } + + if (list1 == NULL) { + curr->next = list2; + } else { + curr->next = list1; + } + + return head; + } +}; diff --git a/cpp/0024-swap-nodes-in-pairs.cpp b/cpp/0024-swap-nodes-in-pairs.cpp new file mode 100644 index 000000000..7cdae3d2f --- /dev/null +++ b/cpp/0024-swap-nodes-in-pairs.cpp @@ -0,0 +1,46 @@ +/* + Given a linked list, swap every two adjacent nodes and return its head. + You must solve the problem without modifying the values in the list's nodes (i.e., only nodes themselves may be changed.) + + Ex. Input: head = [1,2,3,4] + Output: [2,1,4,3] + + Time : O(N); + Space : O(1); +*/ + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* swapPairs(ListNode* head) { + if (!head || !head->next) + return head; + + ListNode *new_head = head->next; + ListNode *prev = NULL; + + while (head && head->next) { + ListNode *next_pair = head->next->next; + ListNode *second = head->next; + + if (prev) + prev->next = second; + + second->next = head; + head->next = next_pair; + + prev = head; + head = next_pair; + } + return new_head; + } +}; diff --git a/cpp/0025-reverse-nodes-in-k-group.cpp b/cpp/0025-reverse-nodes-in-k-group.cpp new file mode 100644 index 000000000..6dce22a54 --- /dev/null +++ b/cpp/0025-reverse-nodes-in-k-group.cpp @@ -0,0 +1,58 @@ +/* + Given head of linked list, reverse nodes of list k at a time + Ex. head = [1,2,3,4,5], k = 2 -> [2,1,4,3,5] + + Maintain prev, curr, & temp pointers to reverse, count k times + + Time: O(n) + Space: O(1) +*/ + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* reverseKGroup(ListNode* head, int k) { + ListNode* dummy = new ListNode(); + dummy->next = head; + + ListNode* prev = dummy; + ListNode* curr = dummy->next; + ListNode* temp = NULL; + + int count = k; + + while (curr != NULL) { + if (count > 1) { + temp = prev->next; + prev->next = curr->next; + curr->next = curr->next->next; + prev->next->next = temp; + + count--; + } else { + prev = curr; + curr = curr->next; + count = k; + + ListNode* end = curr; + for (int i = 0; i < k; i++) { + if (end == NULL) { + return dummy->next; + } + end = end->next; + } + } + } + + return dummy->next; + } +}; diff --git a/cpp/0026-remove-duplicates-from-sorted-array.cpp b/cpp/0026-remove-duplicates-from-sorted-array.cpp new file mode 100644 index 000000000..f618ca230 --- /dev/null +++ b/cpp/0026-remove-duplicates-from-sorted-array.cpp @@ -0,0 +1,12 @@ +int removeDuplicates(int* nums, int numsSize){ + int indx = 1; + + for(int i = 1; i < numsSize; i++){ + if(nums[i] != nums[i-1]){ + nums[indx] = nums[i]; + indx++; + } + } + return indx; +} + diff --git a/cpp/0027-remove-element.cpp b/cpp/0027-remove-element.cpp new file mode 100644 index 000000000..3b7845b59 --- /dev/null +++ b/cpp/0027-remove-element.cpp @@ -0,0 +1,17 @@ +class Solution { +public: + int removeElement(vector& nums, int val) { + int n=nums.size(); + int count=0; + for(int i=0;i 4 (value 0 is at index 4) + + Modified binary search, if low <= mid left sorted, else right sorted + + Time: O(log n) + Space: O(1) +*/ + +class Solution { +public: + int search(vector& nums, int target) { + int low = 0; + int high = nums.size() - 1; + + while (low <= high) { + int mid = low + (high - low) / 2; + if (nums[mid] == target) { + return mid; + } + if (nums[low] <= nums[mid]) { + if (nums[low] <= target && target <= nums[mid]) { + high = mid - 1; + } else { + low = mid + 1; + } + } else { + if (nums[mid] <= target && target <= nums[high]) { + low = mid + 1; + } else { + high = mid - 1; + } + } + } + + return -1; + } +}; diff --git a/cpp/0034-find-first-and-last-position-of-element-in-sorted-array.cpp b/cpp/0034-find-first-and-last-position-of-element-in-sorted-array.cpp new file mode 100644 index 000000000..fdb4f8322 --- /dev/null +++ b/cpp/0034-find-first-and-last-position-of-element-in-sorted-array.cpp @@ -0,0 +1,32 @@ +/* + Given an array of integers nums sorted in non-decreasing order, find the starting and ending position of a given target value. + Ex. nums = [5,7,7,8,8,10], target = 8 -> [3, 4] (start position is 3, end position is 4) + + Use binary search, firstly binary search left endpoint, then binary search right endpoint. + + Time: O(log n) + Space: O(1) +*/ + +class Solution { +public: + vector searchRange(vector& nums, int target) { + if (nums.empty()) return {-1, -1}; + int l = 0, r = nums.size() - 1; + while (l < r) { + int mid = l + r >> 1; + if (nums[mid] >= target) r = mid; + else l = mid + 1; + } + if (nums[l] != target) return {-1, -1}; + int left = l; + + l = 0, r = nums.size() - 1; + while (l < r) { + int mid = l + r + 1ll >> 1; + if (nums[mid] <= target) l = mid; + else r = mid - 1; + } + return {left, r}; + } +}; \ No newline at end of file diff --git a/cpp/0035-search-insert-position.cpp b/cpp/0035-search-insert-position.cpp new file mode 100644 index 000000000..cb90e9eba --- /dev/null +++ b/cpp/0035-search-insert-position.cpp @@ -0,0 +1,34 @@ +/* + Given a sorted array of distinct integers and a target value, return the index if the target is found. + If not, return the index where it would be if it were inserted in order. + + Ex. + Input: nums = [1,3,5,6], target = 5 + Output: 2 + + 1.- Find the number in the middle of the vector. + 2.- Takes a part (first or second), depending on whether or not the target is greater than the middel. + 3.- Change the current left or right part. + 3.- Do this process until the left exceeds the right. + + Time: O(log n) + Space: O(1) +*/ + +class Solution { +public: + int searchInsert(vector& nums, int target) { + int left = 0; + int right = nums.size() - 1; + + while (left <= right) { + int mid = left + (right - left) / 2; + + if (nums[mid] < target) + left = mid + 1; + else + right = mid - 1; + } + return left; + } +}; \ No newline at end of file diff --git a/cpp/0036-valid-sudoku.cpp b/cpp/0036-valid-sudoku.cpp new file mode 100644 index 000000000..ec1c25da0 --- /dev/null +++ b/cpp/0036-valid-sudoku.cpp @@ -0,0 +1,38 @@ +/* + Determine if a 9x9 Sudoku board is valid (no repeats) + + Boolean matrices to store seen values. Check rows, cols, 3x3 sub-boxes + + Time: O(cnt^2) + Space: O(cnt^2) +*/ + +class Solution { +public: + bool isValidSudoku(vector>& board) { + const int cnt = 9; + bool row[cnt][cnt] = {false}; + bool col[cnt][cnt] = {false}; + bool sub[cnt][cnt] = {false}; + + for(int r = 0; r < cnt; ++r){ + for(int c = 0; c < cnt; ++c){ + if(board[r][c] == '.') + continue; // if not number pass + + int idx = board[r][c] - '0' - 1; //char to num idx + int area = (r/3) * 3 + (c/3); + + //if number already exists + if(row[r][idx] || col[c][idx] || sub[area][idx]){ + return false; + } + + row[r][idx] = true; + col[c][idx] = true; + sub[area][idx] = true; + } + } + return true; + } +}; diff --git a/cpp/0039-combination-sum.cpp b/cpp/0039-combination-sum.cpp new file mode 100644 index 000000000..66797833c --- /dev/null +++ b/cpp/0039-combination-sum.cpp @@ -0,0 +1,37 @@ +/* + Given distinct int array & a target, return list of all unique combos that sum to target + Ex. candidates = [2,3,6,7] target = 7 -> [[2,2,3],[7]] + + Backtracking, generate all combo sums, push/pop + index checking to explore new combos + + Time: O(2^target) + Space: O(target) +*/ + +class Solution { +public: + vector> combinationSum(vector& candidates, int target) { + std::vector curr; + std::vector> res; + helper(candidates, target, 0, curr, res); + return res; + } +private: + void helper(std::vector& cands, int target, int i, std::vector& curr, std::vector>& res) { + if (i >= cands.size() || target < 0) + return; + + if (target == 0) { + res.push_back(curr); + return; + } + + curr.push_back(cands[i]); + + helper(cands, target - cands[i], i, curr, res); + + curr.pop_back(); + + helper(cands, target, i + 1, curr, res); + } +}; diff --git a/cpp/0040-combination-sum-ii.cpp b/cpp/0040-combination-sum-ii.cpp new file mode 100644 index 000000000..67ddf460b --- /dev/null +++ b/cpp/0040-combination-sum-ii.cpp @@ -0,0 +1,40 @@ +/* + Given array & a target, find all unique combos that sum to target, nums can only be used once + Ex. candidates = [10,1,2,7,6,1,5], target = 8 -> [[1,1,6],[1,2,5],[1,7],[2,6]] + + Backtracking, generate all combo sums, push/pop + index checking to explore new combos + + Time: O(2^n) + Space: O(n) +*/ + +class Solution { +public: + vector> combinationSum2(vector& candidates, int target) { + sort(candidates.begin(), candidates.end()); + + vector curr; + vector> result; + + dfs(candidates, target, 0, 0, curr, result); + return result; + } +private: + void dfs(vector& candidates, int target, int sum, int start, vector& curr, vector>& result) { + if (sum > target) { + return; + } + if (sum == target) { + result.push_back(curr); + return; + } + for (int i = start; i < candidates.size(); i++) { + if (i > start && candidates[i] == candidates[i - 1]) { + continue; + } + curr.push_back(candidates[i]); + dfs(candidates, target, sum + candidates[i], i + 1, curr, result); + curr.pop_back(); + } + } +}; diff --git a/cpp/0041-first-missing-positive.cpp b/cpp/0041-first-missing-positive.cpp new file mode 100644 index 000000000..8722adbca --- /dev/null +++ b/cpp/0041-first-missing-positive.cpp @@ -0,0 +1,16 @@ +class Solution { +public: + int firstMissingPositive(vector& nums) { + for(int i=0; i=1 && x<=nums.size() && x!=nums[x-1]){ + swap(x, nums[x-1]); + } + } + for(int i=0; i 6 + + 2 pointers, outside in, track max left/right + For lower max, curr only dependent on that one + Compute height of these, iterate lower one + + Time: O(n) + Space: O(1) +*/ + +class Solution { +public: + int trap(vector& height) { + int i = 0; + int j = height.size() - 1; + + int maxLeft = height[i]; + int maxRight = height[j]; + + int result = 0; + + while (i < j) { + if (maxLeft <= maxRight) { + i++; + maxLeft = max(maxLeft, height[i]); + result += maxLeft - height[i]; + } else { + j--; + maxRight = max(maxRight, height[j]); + result += maxRight - height[j]; + } + } + + return result; + } +}; diff --git a/cpp/0043-multiply-strings.cpp b/cpp/0043-multiply-strings.cpp new file mode 100644 index 000000000..9f228b94f --- /dev/null +++ b/cpp/0043-multiply-strings.cpp @@ -0,0 +1,34 @@ +/* + Given 2 ints represented as strings, return product, also represented as a string + Ex. num1 = "2" num2 = "3" -> "6", num1 = "123" num2 = "456" -> "56088" + + Standard multiplication, right to left per digit, compute sums & carries at each pos + + Time: O(m x n) + Space: O(m + n) +*/ + +class Solution { +public: + string multiply(string num1, string num2) { + int m = num1.size(); + int n = num2.size(); + + string result(m + n, '0'); + + for (int i = m - 1; i >= 0; i--) { + for (int j = n - 1; j >= 0; j--) { + int sum = (num1[i] - '0') * (num2[j] - '0') + (result[i + j + 1] - '0'); + result[i + j + 1] = sum % 10 + '0'; + result[i + j] += sum / 10; + } + } + + for (int i = 0; i < m + n; i++) { + if (result[i] != '0') { + return result.substr(i); + } + } + return "0"; + } +}; diff --git a/cpp/0045-jump-game-ii.cpp b/cpp/0045-jump-game-ii.cpp new file mode 100644 index 000000000..c8486d1d4 --- /dev/null +++ b/cpp/0045-jump-game-ii.cpp @@ -0,0 +1,37 @@ +/* + Given int array, determine min jumps to reach last index + Ex. nums = [2,3,1,1,4] -> 2, index 0 to 1 to last + + Greedy: At each point, determine furthest reachable, jump to it + + Time: O(n) + Space: O(1) +*/ + +class Solution { +public: + int jump(vector& nums) { + int n = nums.size(); + int result = 0; + + int i = 0; + while (i < n - 1) { + if (i + nums[i] >= n - 1) { + result++; + break; + } + int maxIndex = i + 1; + int maxValue = 0; + for (int j = i + 1; j < i + 1 + nums[i]; j++) { + if (j + nums[j] > maxValue) { + maxIndex = j; + maxValue = j + nums[j]; + } + } + i = maxIndex; + result++; + } + + return result; + } +}; diff --git a/cpp/0046-permutations.cpp b/cpp/0046-permutations.cpp new file mode 100644 index 000000000..6b91369a2 --- /dev/null +++ b/cpp/0046-permutations.cpp @@ -0,0 +1,30 @@ +/* + Given array of distinct integers, return all the possible permutations + Ex. nums = [1,2,3] -> [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] + + Permute by swapping i/start, DFS from this point, backtrack to undo swap + + Time: O(n x n!) + Space: O(n!) +*/ + +class Solution { +public: + vector> permute(vector& nums) { + vector> result; + dfs(nums, 0, result); + return result; + } +private: + void dfs(vector& nums, int start, vector>& result) { + if (start == nums.size()) { + result.push_back(nums); + return; + } + for (int i = start; i < nums.size(); i++) { + swap(nums[i], nums[start]); + dfs(nums, start + 1, result); + swap(nums[i], nums[start]); + } + } +}; diff --git a/cpp/0048-rotate-image.cpp b/cpp/0048-rotate-image.cpp new file mode 100644 index 000000000..6193ff469 --- /dev/null +++ b/cpp/0048-rotate-image.cpp @@ -0,0 +1,21 @@ +/* + Given a 2D image matrix, rotate image 90 deg CW + + Transpose + reflect (rev on diag then rev left to right) + + Time: O(n^2) + Space: O(1) +*/ + +class Solution { +public: + void rotate(vector>& matrix) { + int n = matrix.size(); + for (int i = 0; i < n; i++) { + for (int j = i; j < n; j++) { + swap(matrix[i][j], matrix[j][i]); + } + reverse(matrix[i].begin(), matrix[i].end()); + } + } +}; diff --git a/cpp/0049-group-anagrams.cpp b/cpp/0049-group-anagrams.cpp new file mode 100644 index 000000000..ad7222b51 --- /dev/null +++ b/cpp/0049-group-anagrams.cpp @@ -0,0 +1,39 @@ +/* + Given array of strings, group anagrams together (same letters diff order) + Ex. strs = ["eat","tea","tan","ate","nat","bat"] -> [["bat"],["nat","tan"],["ate","eat","tea"]] + + Count chars, for each string use total char counts (naturally sorted) as key + + Time: O(n x l) -> n = length of strs, l = max length of a string in strs + Space: O(n x l) +*/ + +class Solution { +public: + vector> groupAnagrams(vector& strs) { + unordered_map> m; + for (int i = 0; i < strs.size(); i++) { + string key = getKey(strs[i]); + m[key].push_back(strs[i]); + } + + vector> result; + for (auto it = m.begin(); it != m.end(); it++) { + result.push_back(it->second); + } + return result; + } +private: + string getKey(string str) { + vector count(26); + for (int j = 0; j < str.size(); j++) { + count[str[j] - 'a']++; + } + + string key = ""; + for (int i = 0; i < count.size(); i++) { + key.append(to_string(count[i]) + '#'); + } + return key; + } +}; diff --git a/cpp/0050-powx-n.cpp b/cpp/0050-powx-n.cpp new file mode 100644 index 000000000..e99dee414 --- /dev/null +++ b/cpp/0050-powx-n.cpp @@ -0,0 +1,56 @@ +/* + Implement pow(x, n), which calculates x raised to the power n + Ex. x = 2 n = 10 -> 1024, x = 2.1 n = 3 -> 9.261, x = 2 n = -2 -> 0.25 + + Divide-and-conquer, even x^n = A * A, odd x^n = A * A * x + + Time: O(log n) + Space: O(1) -> optimized from recursive O(log n) to do iteratively +*/ + +// class Solution { +// public: +// double myPow(double x, int n) { +// long exponent = abs(n); +// double result = helper(x, exponent); +// if (n >= 0) { +// return result; +// } +// return 1.0 / result; +// } +// private: +// double helper(double x, long n) { +// if (x == 0.0) { +// return 0; +// } +// if (n == 0) { +// return 1.0; +// } +// double result = helper(x * x, n / 2); +// if (n % 2 == 0) { +// return result; +// } +// return result * x; +// } +// }; + +class Solution { +public: + double myPow(double x, int n) { + long exponent = abs(n); + double curr = x; + double result = 1.0; + + for (long i = exponent; i > 0; i /= 2) { + if (i % 2 == 1) { + result *= curr; + } + curr *= curr; + } + + if (n < 0) { + return 1.0 / result; + } + return result; + } +}; diff --git a/cpp/0051-n-queens.cpp b/cpp/0051-n-queens.cpp new file mode 100644 index 000000000..c7ec56368 --- /dev/null +++ b/cpp/0051-n-queens.cpp @@ -0,0 +1,50 @@ +/* + N-Queens: place n queens such that no 2 queens atk each other, return all soln's + + Place queens per row, try all possibilities & validate for further rows, backtrack + + Time: O(n!) + Space: O(n^2) +*/ + +class Solution { +private: + unordered_set cols; //for Columns + unordered_set negDiag; //for negative diagnals R-C + unordered_set posDiag; //for positive diagnals R+C + + void backtrack(int n, int row, vector>& res, vector& board){ + if(row==n){ + res.push_back(board); + return ; + } + + for(int col = 0; col < n; col++){ //Shifting through each col + if( cols.find(col) != cols.end() or //if queen alread placed in this col + negDiag.find(row - col) != negDiag.end() or //if queen in negDiag + posDiag.find(row + col) != posDiag.end() //if queen in posDiag + ) + continue; + + cols.insert(col); + negDiag.insert(row - col); + posDiag.insert(row + col); + board[row][col] = 'Q'; + + backtrack(n, row +1, res, board); + + cols.erase(col); + negDiag.erase(row - col); + posDiag.erase(row + col); + board[row][col] = '.'; + } + } + +public: + vector> solveNQueens(int n) { + vector> res; + vector board(n, string(n,'.')); + backtrack(n, 0, res, board); + return res; + } +}; diff --git a/cpp/0052-n-queens-ii.cpp b/cpp/0052-n-queens-ii.cpp new file mode 100644 index 000000000..1a746aa21 --- /dev/null +++ b/cpp/0052-n-queens-ii.cpp @@ -0,0 +1,38 @@ +class Solution { +public: + int queen[9]; + bool check(int &r,int &c,int n){ + for(int i=0;i 6, [4,-1,2,1] + + At each point, determine if it's better to add to curr sum or start over + + Time: O(n) + Space: O(1) +*/ + +class Solution { +public: + int maxSubArray(vector& nums) { + int curr = nums[0]; + int result = nums[0]; + + for (int i = 1; i < nums.size(); i++) { + curr = max(curr + nums[i], nums[i]); + result = max(result, curr); + } + + return result; + } +}; diff --git a/cpp/0054-spiral-matrix.cpp b/cpp/0054-spiral-matrix.cpp new file mode 100644 index 000000000..f49a7eeff --- /dev/null +++ b/cpp/0054-spiral-matrix.cpp @@ -0,0 +1,48 @@ +/* + Given a matrix, return all elements in spiral order + + Set up boundaries, go outside in CW: top->right->bottom->left + + Time: O(m x n) + Space: O(m x n) +*/ + +class Solution { +public: + vector spiralOrder(vector>& matrix) { + int left = 0; + int top = 0; + int right = matrix[0].size() - 1; + int bottom = matrix.size() - 1; + + vector result; + + while (top <= bottom && left <= right) { + for (int j = left; j <= right; j++) { + result.push_back(matrix[top][j]); + } + top++; + + for (int i = top; i <= bottom; i++) { + result.push_back(matrix[i][right]); + } + right--; + + if (top <= bottom) { + for (int j = right; j >= left; j--) { + result.push_back(matrix[bottom][j]); + } + } + bottom--; + + if (left <= right) { + for (int i = bottom; i >= top; i--) { + result.push_back(matrix[i][left]); + } + } + left++; + } + + return result; + } +}; diff --git a/cpp/0055-jump-game.cpp b/cpp/0055-jump-game.cpp new file mode 100644 index 000000000..ad967efda --- /dev/null +++ b/cpp/0055-jump-game.cpp @@ -0,0 +1,29 @@ +/* + Given int array, return true if can reach last index + Ex. nums = [2,3,1,1,4] -> true, index 0 to 1 to last + + Greedy: At each point, determine furthest reachable index + + Time: O(n) + Space: O(1) +*/ + +class Solution { +public: + bool canJump(vector& nums) { + int n = nums.size(); + int reachable = 0; + + for (int i = 0; i < n; i++) { + if (i > reachable) { + return false; + } + reachable = max(reachable, i + nums[i]); + if (reachable >= n - 1) { + break; + } + } + + return true; + } +}; diff --git a/cpp/0056-merge-intervals.cpp b/cpp/0056-merge-intervals.cpp new file mode 100644 index 000000000..6bf3939f7 --- /dev/null +++ b/cpp/0056-merge-intervals.cpp @@ -0,0 +1,39 @@ +/* + Given an array of intervals, merge all overlapping intervals + Ex. intervals = [[1,3],[2,6],[8,10],[15,18]] -> [[1,6],[8,10],[15,18]] + + Sort by earliest start time, merge overlapping intervals (take longer end time) + + Time: O(n log n) + Space: O(n) +*/ + +class Solution { +public: + vector> merge(vector>& intervals) { + int n = intervals.size(); + if (n == 1) { + return intervals; + } + + sort(intervals.begin(), intervals.end(), [](const auto& a, const auto& b) { + return a[0] < b[0]; + }); + + vector> result; + + int i = 0; + while (i < n - 1) { + if (intervals[i][1] >= intervals[i+1][0]) { + intervals[i+1][0] = intervals[i][0]; + intervals[i+1][1] = max(intervals[i][1], intervals[i+1][1]); + } else { + result.push_back(intervals[i]); + } + i++; + } + result.push_back(intervals[i]); + + return result; + } +}; diff --git a/cpp/0057-insert-interval.cpp b/cpp/0057-insert-interval.cpp new file mode 100644 index 000000000..dbd87a161 --- /dev/null +++ b/cpp/0057-insert-interval.cpp @@ -0,0 +1,39 @@ +/* + Given array of non-overlapping intervals & a new interval, insert & merge if necessary + Ex. intervals = [[1,3],[6,9]], newInterval = [2,5] -> [[1,5],[6,9]] + + To merge: while intervals are still overlapping the new one, take the larger bounds + + Time: O(n) + Space: O(n) +*/ + +class Solution { +public: + vector> insert(vector>& intervals, vector& newInterval) { + vector> ans; + int newStart = newInterval[0]; + int newEnd = newInterval[1]; + int n = intervals.size(); + for (int i = 0; i < n; i++) { + // Case 1: Non overlapping interval + // If new interval is before the current interval + if (intervals[i][0] > newEnd) { + ans.push_back(newInterval); + copy(intervals.begin() + i, intervals.end(), back_inserter(ans)); + return ans; + } + // If new interval is after the current interval + else if (intervals[i][1] < newStart) { + ans.push_back(intervals[i]); + } + // Case 2: Overlapping interval + else { + newInterval[0] = min(newInterval[0], intervals[i][0]); + newInterval[1] = max(newInterval[1], intervals[i][1]); + } + } + ans.push_back(newInterval); + return ans; + } +}; diff --git a/cpp/0058-length-of-last-word.cpp b/cpp/0058-length-of-last-word.cpp new file mode 100644 index 000000000..58bc1ba4e --- /dev/null +++ b/cpp/0058-length-of-last-word.cpp @@ -0,0 +1,21 @@ +class Solution { +public: + /* + Approach: + Traverse from end to the first whitespace character and count the number of letters. + Return the count as our pointer hits the whitespace character. + + Time complexity: O(n) + Space complexity: O(1) + */ + int lengthOfLastWord(string s) { + int n = s.length(); + + int ptr = n-1; + while(ptr >= 0 && s[ptr] == ' ') ptr--; /* Skip the trailing whitespaces */ + + int len = 0; + while(ptr >= 0 && s[ptr--] != ' ') len++; /* Counting the letters in the last word */ + return len; + } +}; \ No newline at end of file diff --git a/cpp/0059-spiral-matrix-ii.cpp b/cpp/0059-spiral-matrix-ii.cpp new file mode 100644 index 000000000..7b6f85d8b --- /dev/null +++ b/cpp/0059-spiral-matrix-ii.cpp @@ -0,0 +1,43 @@ +/* + Given a positive integer n, generate an n x n matrix filled with elements from 1 to n2 in spiral order. + Ex: Input -> n = 3 + Output -> [[1,2,3],[8,9,4],[7,6,5]] + + Fill matrix layer by layer in four directions. + + Time - O(n^2) + Space - O(1) +*/ + +class Solution { +public: + vector> generateMatrix(int n) { + int left = 0, right = n - 1, top = 0, bottom = n - 1; + int val = 1; + vector> matrix(n, vector (n)); + + while (left <= right && top <= bottom) { + for (int j = left; j <= right; j++) { + matrix[top][j] = val++; + } + top++; + + for (int i = top; i <= bottom; i++) { + matrix[i][right] = val++; + } + right--; + + for (int j = right; j >= left; j--) { + matrix[bottom][j] = val++; + } + bottom--; + + for (int i = bottom; i >= top; i--) { + matrix[i][left] = val++; + } + left++; + } + + return matrix; + } +}; diff --git a/cpp/0061-rotate-list.cpp b/cpp/0061-rotate-list.cpp new file mode 100644 index 000000000..c2c02b8d4 --- /dev/null +++ b/cpp/0061-rotate-list.cpp @@ -0,0 +1,45 @@ +/* + Given the head of a linked list, rotate the list to the right by k places. + Example: list = [1,2,3,4,5] and k = 2 + Output: [4,5,1,2,3] + + Time complexity: O(n) + Space complexity: O(1) +*/ + +class Solution { +private: + int findLen(ListNode* head){ + int len = 0; + while(head!=NULL){ + len++; + head = head->next; + } + return len; + } + ListNode* findNewHead(ListNode* head,int k){ + int i=0; + while(i+1next; + } + ListNode* ret = head->next; + head->next = NULL; + return ret; + } + ListNode* findLast(ListNode* head){ + while(head->next!=NULL) head = head->next; + return head; + } +public: + ListNode* rotateRight(ListNode* head, int k) { + int len = findLen(head); // Finds the length of the Linked List + if(len==0) return head; + k = k%len; + if(k==0) return head; + ListNode* newHead = findNewHead(head,len-k); // Finds the node that is the new head + ListNode* last = findLast(newHead); // Finds the last node from the new head and connects it to the previous head + last->next = head; + return newHead; + } +}; \ No newline at end of file diff --git a/cpp/0062-unique-paths.cpp b/cpp/0062-unique-paths.cpp new file mode 100644 index 000000000..df95164c6 --- /dev/null +++ b/cpp/0062-unique-paths.cpp @@ -0,0 +1,31 @@ +/* + Given grid, return # of unique paths from top-left to bottom-right + Ex. m = 3, n = 2 -> 3 unique paths (R->D->D, D->D->R, D->R->D) + + DP: edges have 1 unique path, inner cells consider where it comes from + Recurrence relation: grid[i][j] = grid[i-1][j] + grid[i][j-1] + + Time: O(m x n) + Space: O(m x n) +*/ + +class Solution { +public: + int uniquePaths(int m, int n) { + vector> grid(m, vector(n, 0)); + + for (int i = 0; i < m; i++) { + grid[i][0] = 1; + } + for (int j = 0; j < n; j++) { + grid[0][j] = 1; + } + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + grid[i][j] = grid[i - 1][j] + grid[i][j - 1]; + } + } + + return grid[m - 1][n - 1]; + } +}; diff --git a/cpp/0063-unique-paths-ii.cpp b/cpp/0063-unique-paths-ii.cpp new file mode 100644 index 000000000..064a0be6b --- /dev/null +++ b/cpp/0063-unique-paths-ii.cpp @@ -0,0 +1,40 @@ +/* + +Given an m x n integer array grid. There is a robot initially located at the top-left corner (i.e., grid[0][0]). The robot tries to move to the bottom-right corner (i.e., grid[m - 1][n - 1]). +The robot can only move either down or right at any point in time. An obstacle and space are marked as 1 or 0 respectively in grid. A path that the robot takes cannot include any square that is an obstacle. + +Return the number of possible unique paths that the robot can take to reach the bottom-right corner. + +Example. obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]] + There is one obstacle in the middle of the 3x3 grid above. + There are two ways to reach the bottom-right corner: + 1. Right -> Right -> Down -> Down + 2. Down -> Down -> Right -> Right + So, the number of unique paths the robot can take is 2. Hence we return 2 as our answer. + + +Time: O(m * n) +Space: O(n) + +*/ + + +class Solution { +public: + int uniquePathsWithObstacles(vector>& grid) { + + int m = grid.size(), n = grid[0].size(); + if(grid[m-1][n-1] == 1 || grid[0][0] == 1) return 0; + vector dp(n); + dp[n-1] = 1; + for(int i=m-1; i>=0; i--) { + for(int j=n-1; j>=0; j--) { + if(grid[i][j] == 1) dp[j] = 0; + else if(j == n-1) continue; + else dp[j] = dp[j] + dp[j+1]; + } + } + return dp[0]; + + } +}; diff --git a/cpp/0064-minimum-path-sum.cpp b/cpp/0064-minimum-path-sum.cpp new file mode 100644 index 000000000..a860a19d6 --- /dev/null +++ b/cpp/0064-minimum-path-sum.cpp @@ -0,0 +1,29 @@ +class Solution{ + public: + void Helper(vector> & grid, vector> & dp, int i, int k){ + int X = grid.size(); + int Y = grid[0].size(); + for(int i = 0; i < X; i++){ + for(k = 0; k < Y; k++){ + if((i - 1 >= 0) && (k - 1 >= 0)){ + dp[i][k] = grid[i][k] + min(dp[i - 1][k], dp[i][k - 1]); + } + else{ + if(i - 1 >= 0){ + dp[i][k] = grid[i][k] + dp[i - 1][k]; + } + if(k - 1 >= 0){ + dp[i][k] = grid[i][k] + dp[i][k - 1]; + } + } + } + } + + } + int minPathSum(vector>& grid){ + vector> dp(grid.size(), vector(grid[0].size())); + dp[0][0] = grid[0][0]; + Helper(grid, dp, grid.size() - 1, grid[0].size() - 1); + return dp[grid.size() - 1][grid[0].size() - 1]; + } +}; diff --git a/cpp/0066-plus-one.cpp b/cpp/0066-plus-one.cpp new file mode 100644 index 000000000..480f0c31e --- /dev/null +++ b/cpp/0066-plus-one.cpp @@ -0,0 +1,26 @@ +/* + Given large int as an array, add 1 (consider carry) + Ex. digits = [1,2,3] -> [1,2,4] + + From right to left, keep carrying until digit < 9, add 1 + + Time: O(n) + Space: O(1) +*/ + +class Solution { +public: + vector plusOne(vector& digits) { + for (int i = digits.size() - 1; i >= 0; i--) { + if (digits[i] < 9) { + digits[i]++; + return digits; + } + digits[i] = 0; + } + + digits[0] = 1; + digits.push_back(0); + return digits; + } +}; diff --git a/cpp/0067-Add-Binary.cpp b/cpp/0067-Add-Binary.cpp new file mode 100644 index 000000000..5f2db9ee8 --- /dev/null +++ b/cpp/0067-Add-Binary.cpp @@ -0,0 +1,28 @@ +class Solution { +public: + string addBinary(string a, string b) { + string res; + int maxLen = a.size() > b.size() ? a.size() : b.size(); + unsigned int carry = 0; + + for(int i = 0; i < maxLen; i++) + { + unsigned int bitA = i < a.size() ? a[a.size() - i - 1] - '0' : 0; + unsigned int bitB = i < b.size() ? b[b.size() - i - 1] - '0' : 0; + + unsigned int total = bitA + bitB + carry; + char sum = '0' + total % 2; + carry = total / 2; + + // Add to the beginning of the string + res.insert(0, 1, sum); + } + + if(carry) + { + res.insert(0, 1, '1'); + } + + return res; + } +}; diff --git a/cpp/0067-add-binary.cpp b/cpp/0067-add-binary.cpp new file mode 100644 index 000000000..5f2db9ee8 --- /dev/null +++ b/cpp/0067-add-binary.cpp @@ -0,0 +1,28 @@ +class Solution { +public: + string addBinary(string a, string b) { + string res; + int maxLen = a.size() > b.size() ? a.size() : b.size(); + unsigned int carry = 0; + + for(int i = 0; i < maxLen; i++) + { + unsigned int bitA = i < a.size() ? a[a.size() - i - 1] - '0' : 0; + unsigned int bitB = i < b.size() ? b[b.size() - i - 1] - '0' : 0; + + unsigned int total = bitA + bitB + carry; + char sum = '0' + total % 2; + carry = total / 2; + + // Add to the beginning of the string + res.insert(0, 1, sum); + } + + if(carry) + { + res.insert(0, 1, '1'); + } + + return res; + } +}; diff --git a/cpp/0069-sqrtx.cpp b/cpp/0069-sqrtx.cpp new file mode 100644 index 000000000..c3d142c07 --- /dev/null +++ b/cpp/0069-sqrtx.cpp @@ -0,0 +1,37 @@ +/* + Given a non-negative integer x, return the square root of x rounded down to the nearest integer. The returned integer should be non-negative as well. + You must not use any built-in exponent function or operator. + + For example, do not use pow(x, 0.5) in c++ or x ** 0.5 in python. + + Ex. Input: x = 4 + Output: 2 + Explanation: The square root of 4 is 2, so we return 2. + + Time : O(log N) + Space : O(1) +*/ + +class Solution { +public: + int mySqrt(int x) { + if(x == 0 || x == 1) + return x; + + long long beg = 0, mid = 0, end = x/2; + while(beg <= end) { + mid = (beg + end)/2; + if(mid * mid < x) { + if((mid + 1) * (mid + 1) > x) + return mid; + beg = mid+1; + } else if(mid * mid > x) { + if( (mid - 1) * (mid - 1) < x) + return mid-1; + end = mid - 1; + } else + return mid; + } + return mid; + } +}; diff --git a/cpp/0070-climbing-stairs.cpp b/cpp/0070-climbing-stairs.cpp new file mode 100644 index 000000000..049f2a931 --- /dev/null +++ b/cpp/0070-climbing-stairs.cpp @@ -0,0 +1,36 @@ +/* + Climbing stairs, either 1 or 2 steps, distinct ways to reach top + Ex. n = 2 -> 2 (1 + 1, 2), n = 3 -> 3 (1 + 1 + 1, 1 + 2, 2 + 1) + + Recursion w/ memoization -> DP, why DP? Optimal substructure + Recurrence relation: dp[i] = dp[i - 1] + dp[i - 2] + Reach ith step in 2 ways: 1) 1 step from i-1, 2) 2 steps from i-2 + + Time: O(n) + Space: O(1) +*/ + +class Solution { +public: + int climbStairs(int n) { + if (n == 1) { + return 1; + } + if (n == 2) { + return 2; + } + + int first = 1; + int second = 2; + + int result = 0; + + for (int i = 2; i < n; i++) { + result = first + second; + first = second; + second = result; + } + + return result; + } +}; diff --git a/cpp/0071-simplify-path.cpp b/cpp/0071-simplify-path.cpp new file mode 100644 index 000000000..f33b39c8e --- /dev/null +++ b/cpp/0071-simplify-path.cpp @@ -0,0 +1,49 @@ +/* + Given an absolute path to a file or directory in a Unix-style file system, + convert it to the simplified canonical path. + + In a Unix-style file system, a period '.' refers to the current directory. + A double period '..' refers to the directory up a level. + Any multiple consecutive slashes (i.e. '//') are treated as a single slash '/'. + For this problem, any other format of periods such as '...' are treated as file/directory names. + + The canonical path should have the following format: + - The path starts with a single slash '/'. + - Any two directories are separated by a single slash '/'. + - The path does not end with a trailing '/'. + - The path only contains the directories on the path from the root directory to the target file or directory (i.e., no period '.' or double period '..') + + Return the simplified canonical path. + + Time: O(n) + Space: O(n) +*/ + +class Solution { +public: + string simplifyPath(string path) { + stringstream ss(path); + string dir; + stack stk; + + while (getline(ss, dir, '/')) { + if (dir.empty() || dir == ".") { + continue; + } + else if (dir == "..") { + if (!stk.empty()) + stk.pop(); + } + else { + stk.push(dir); + } + } + + string res = ""; + while (!stk.empty()) { + res = "/" + stk.top() + res; + stk.pop(); + } + return res.empty()? "/" : res; + } +}; diff --git a/cpp/0072-edit-distance.cpp b/cpp/0072-edit-distance.cpp new file mode 100644 index 000000000..46e9290b5 --- /dev/null +++ b/cpp/0072-edit-distance.cpp @@ -0,0 +1,77 @@ +/* + Given 2 strings, return minimum number of operations to convert word1 to word2 + + Naive: check all possible edit sequences & choose shortest one + Optimal: DP, if chars at i & j same, no operations needed, else 3 cases: + (1) replace (i - 1, j - 1), (2) delete (i - 1, j), (3) insert (i, j - 1) + + Time: O(m x n) + Space: O(m x n) +*/ + +class Solution { +public: + int minDistance(string word1, string word2) { + if (word1.empty() && word2.empty()) { + return 0; + } + if (word1.empty() || word2.empty()) { + return 1; + } + + int m = word1.size(); + int n = word2.size(); + + vector> dp(m + 1, vector(n + 1)); + + // base cases (convert to empty string w/ deletions), dist is just length + for (int i = 1; i <= m; i++) { + dp[i][0] = i; + } + for (int j = 1; j <= n; j++) { + dp[0][j] = j; + } + + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + if (word1[i - 1] == word2[j - 1]) { + // no operation needed, same char + dp[i][j] = dp[i - 1][j - 1]; + } else { + // min(replace, delete, insert) + 1 <-- since an op was needed + dp[i][j] = min(dp[i - 1][j - 1], min(dp[i - 1][j], dp[i][j - 1])) + 1; + } + } + } + + return dp[m][n]; + } +}; + +// Since we only need at most dp[i - 1][j - 1], can space optimize to O(n) +// class Solution { +// public: +// int minDistance(string word1, string word2) { +// int m = word1.size(); +// int n = word2.size(); +// int prev = 0; +// vector curr(n + 1); +// for (int j = 1; j <= n; j++) { +// curr[j] = j; +// } +// for (int i = 1; i <= m; i++) { +// prev = curr[0]; +// curr[0] = i; +// for (int j = 1; j <= n; j++) { +// int temp = curr[j]; +// if (word1[i - 1] == word2[j - 1]) { +// curr[j] = prev; +// } else { +// curr[j] = min(prev, min(curr[j - 1], curr[j])) + 1; +// } +// prev = temp; +// } +// } +// return curr[n]; +// } +// }; diff --git a/cpp/0073-set-matrix-zeroes.cpp b/cpp/0073-set-matrix-zeroes.cpp new file mode 100644 index 000000000..b8f3d4e44 --- /dev/null +++ b/cpp/0073-set-matrix-zeroes.cpp @@ -0,0 +1,62 @@ +/* + Given matrix, if element 0, set entire row/col to 0 + + Use 1st row/col as flag to determine if entire row/col 0 + + Time: O(mn) + Space: O(1) +*/ + +class Solution { +public: + void setZeroes(vector>& matrix) { + int m = matrix.size(); + int n = matrix[0].size(); + + bool isFirstRowZero = false; + bool isFirstColZero = false; + + for (int i = 0; i < m; i++) { + if (matrix[i][0] == 0) { + isFirstColZero = true; + break; + } + } + + for (int j = 0; j < n; j++) { + if (matrix[0][j] == 0) { + isFirstRowZero = true; + break; + } + } + + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + if (matrix[i][j] == 0) { + matrix[i][0] = 0; + matrix[0][j] = 0; + } + } + } + + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + if (matrix[i][0] == 0 || matrix[0][j] == 0) { + matrix[i][j] = 0; + } + } + } + + if (isFirstColZero) { + for (int i = 0; i < m; i++) { + matrix[i][0] = 0; + } + } + + if (isFirstRowZero) { + for (int j = 0; j < n; j++) { + matrix[0][j] = 0; + } + } + } +}; diff --git a/cpp/0074-search-a-2d-matrix.cpp b/cpp/0074-search-a-2d-matrix.cpp new file mode 100644 index 000000000..5a1ddd529 --- /dev/null +++ b/cpp/0074-search-a-2d-matrix.cpp @@ -0,0 +1,49 @@ +/* + Search for target value in matrix where every row & col is sorted + + Perform 2 binary searches: 1 to find row, then another to find col + + Time: O(log m + log n) + Space: O(1) +*/ + +class Solution { +public: + bool searchMatrix(vector>& matrix, int target) { + int lowRow = 0; + int highRow = matrix.size() - 1; + + while (lowRow < highRow) { + int mid = lowRow + (highRow - lowRow) / 2; + if (matrix[mid][0] == target) { + return true; + } + if (matrix[mid][0] < target && target < matrix[mid + 1][0]) { + lowRow = mid; + break; + } + if (matrix[mid][0] < target) { + lowRow = mid + 1; + } else { + highRow = mid - 1; + } + } + + int lowCol = 0; + int highCol = matrix[0].size() - 1; + + while (lowCol <= highCol) { + int mid = lowCol + (highCol - lowCol) / 2; + if (matrix[lowRow][mid] == target) { + return true; + } + if (matrix[lowRow][mid] < target) { + lowCol = mid + 1; + } else { + highCol = mid - 1; + } + } + + return false; + } +}; diff --git a/cpp/0075-Sort-colors.cpp b/cpp/0075-Sort-colors.cpp new file mode 100644 index 000000000..45383cf9c --- /dev/null +++ b/cpp/0075-Sort-colors.cpp @@ -0,0 +1,22 @@ +class Solution { +public: + void sortColors(vector& nums) { + int p1=0,p2=nums.size()-1; + for(int i=p1;i<=p2;i++) + { + if(nums[i]==0) + { + swap(nums[i],nums[p1]); + p1++; + } + if(nums[i]==2) + { + swap(nums[i],nums[p2]); + p2--; + i--; + } + } + + + } +}; diff --git a/cpp/0075-sort-colors.cpp b/cpp/0075-sort-colors.cpp new file mode 100644 index 000000000..45383cf9c --- /dev/null +++ b/cpp/0075-sort-colors.cpp @@ -0,0 +1,22 @@ +class Solution { +public: + void sortColors(vector& nums) { + int p1=0,p2=nums.size()-1; + for(int i=p1;i<=p2;i++) + { + if(nums[i]==0) + { + swap(nums[i],nums[p1]); + p1++; + } + if(nums[i]==2) + { + swap(nums[i],nums[p2]); + p2--; + i--; + } + } + + + } +}; diff --git a/cpp/0076-minimum-window-substring.cpp b/cpp/0076-minimum-window-substring.cpp new file mode 100644 index 000000000..14eb516f4 --- /dev/null +++ b/cpp/0076-minimum-window-substring.cpp @@ -0,0 +1,62 @@ +/* + Given 2 strings s & t, return min window substring + of s such that all chars in t are included in window + Ex. s = "ADOBECODEBANC" t = "ABC" -> "BANC" + + Sliding window + hash map {char -> count} + Move j until valid, move i to find smaller + + Time: O(m + n) + Space: O(m + n) +*/ + +class Solution { +public: + string minWindow(string s, string t) { + // count of char in t + unordered_map m; + for (int i = 0; i < t.size(); i++) { + m[t[i]]++; + } + + int i = 0; + int j = 0; + + // # of chars in t that must be in s + int counter = t.size(); + + int minStart = 0; + int minLength = INT_MAX; + + while (j < s.size()) { + // if char in s exists in t, decrease + if (m[s[j]] > 0) { + counter--; + } + // if char doesn't exist in t, will be -'ve + m[s[j]]--; + // move j to find valid window + j++; + + // when window found, move i to find smaller + while (counter == 0) { + if (j - i < minLength) { + minStart = i; + minLength = j - i; + } + + m[s[i]]++; + // when char exists in t, increase + if (m[s[i]] > 0) { + counter++; + } + i++; + } + } + + if (minLength != INT_MAX) { + return s.substr(minStart, minLength); + } + return ""; + } +}; diff --git a/cpp/0077-combinations.cpp b/cpp/0077-combinations.cpp new file mode 100644 index 000000000..408fe57e8 --- /dev/null +++ b/cpp/0077-combinations.cpp @@ -0,0 +1,27 @@ +class Solution { +private: + void backtrack(int start, int n, int k, vector &combination, vector> &res){ + //base case, when size of combination is k, we wanna stop + if(combination.size() == k){ + res.push_back(combination); + return; + } + + for(int i = start; i<=n; i++){ + combination.push_back(i); + backtrack(i+1, n, k, combination, res); + combination.pop_back(); + } + } +public: + vector> combine(int n, int k) { + vector> res; + + //initial empty list to pass to the backtrack function + vector emptyCombination; + + backtrack(1, n, k, emptyCombination, res); + + return res; + } +}; \ No newline at end of file diff --git a/cpp/0078-subsets.cpp b/cpp/0078-subsets.cpp new file mode 100644 index 000000000..dee1eab47 --- /dev/null +++ b/cpp/0078-subsets.cpp @@ -0,0 +1,28 @@ +/* + Given an integer array of unique elements, return all possible subsets (the power set) + Ex. nums = [1,2,3] -> [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]] + + Backtracking, generate all combinations, push/pop + index checking to explore new combos + + Time: O(n x 2^n) + Space: O(n) +*/ + +class Solution { +public: + vector> subsets(vector& nums) { + vector curr; + vector> result; + dfs(nums, 0, curr, result); + return result; + } +private: + void dfs(vector& nums, int start, vector& curr, vector>& result) { + result.push_back(curr); + for (int i = start; i < nums.size(); i++) { + curr.push_back(nums[i]); + dfs(nums, i + 1, curr, result); + curr.pop_back(); + } + } +}; diff --git a/cpp/0079-word-search.cpp b/cpp/0079-word-search.cpp new file mode 100644 index 000000000..09a5fd102 --- /dev/null +++ b/cpp/0079-word-search.cpp @@ -0,0 +1,51 @@ +/* + Given a char board & a word, return true if word exists in the grid + + DFS traversal, set visited cells to '#', search in 4 directions, backtrack + + Time: O(n x 3^l) -> n = # of cells, l = length of word + Space: O(l) +*/ + +class Solution { +public: + bool exist(vector>& board, string word) { + int m = board.size(); + int n = board[0].size(); + + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (board[i][j] == word[0]) { + if (dfs(board, word, 0, i, j, m, n)) { + return true; + } + } + } + } + + return false; + } +private: + bool dfs(vector>& board, string word, + int index, int i, int j, int m, int n) { + + if (i < 0 || i >= m || j < 0 || j >= n || board[i][j] != word[index]) { + return false; + } + if (index == word.size() - 1) { + return true; + } + + board[i][j] = '#'; + + if (dfs(board, word, index + 1, i - 1, j, m, n) + || dfs(board, word, index + 1, i + 1, j, m, n) + || dfs(board, word, index + 1, i, j - 1, m, n) + || dfs(board, word, index + 1, i, j + 1, m, n)) { + return true; + } + + board[i][j] = word[index]; + return false; + } +}; diff --git a/cpp/0080-remove-duplicates-from-sorted-array-ii.cpp b/cpp/0080-remove-duplicates-from-sorted-array-ii.cpp new file mode 100644 index 000000000..56e8dc1a3 --- /dev/null +++ b/cpp/0080-remove-duplicates-from-sorted-array-ii.cpp @@ -0,0 +1,35 @@ +/* + Given a sorted array nums, remove some duplicates in-place such that each unique element appears at most twice. + Ex:- nums = [1,1,1,2,2,3] -> [1,1,2,2,3,_] + + Input -> nums = [1,1,1,2,2,3] + Output -> 5 + + Use two pointers to find at most 2 duplicates. + + Time - O(n) + Space - O(1) +*/ + +class Solution { +public: + int removeDuplicates(vector& nums) { + int k = 2; // define at most k times of duplicate numbers + + int l = 1, count = 1; + + for(int r = 1; r < nums.size(); r++){ + if(nums[r] == nums[r-1]){ + if(count < k) + nums[l++] = nums[r]; + count++; + } + else { + count = 1; + nums[l++] = nums[r]; + } + } + + return l; + } +}; \ No newline at end of file diff --git a/cpp/0083-remove-duplicates-from-sorted-list.cpp b/cpp/0083-remove-duplicates-from-sorted-list.cpp new file mode 100644 index 000000000..5d589ac49 --- /dev/null +++ b/cpp/0083-remove-duplicates-from-sorted-list.cpp @@ -0,0 +1,22 @@ +// Time Complexity is O(N). +// Space Complexity is O(1). + +class Solution { +public: + ListNode* deleteDuplicates(ListNode* head) { + ListNode * fast = head; + ListNode * slow = head; + + while(slow != NULL) + { + while(fast != NULL && slow->val == fast->val) + fast = fast -> next; + + slow->next = fast; + slow = slow -> next; + } + + return head; + + } +}; diff --git a/cpp/0084-largest-rectangle-in-histogram.cpp b/cpp/0084-largest-rectangle-in-histogram.cpp new file mode 100644 index 000000000..642ed6eac --- /dev/null +++ b/cpp/0084-largest-rectangle-in-histogram.cpp @@ -0,0 +1,44 @@ +/* +Given array of heights, return area of largest rectangle +Ex. heights = [2,1,5,6,2,3] -> 10 (5 x 2 at index 2 and 3) + +Monotonic incr stack, if curr height lower extend back, find max area + +Time: O(n) +Space: O(n) +*/ + +class Solution { +public: + int largestRectangleArea(vector& heights) { + // pair: [index, height] + stack> stk; + int result = 0; + + for (int i = 0; i < heights.size(); i++) { + int start = i; + + while (!stk.empty() && stk.top().second > heights[i]) { + int index = stk.top().first; + int width = i - index; + int height = stk.top().second; + stk.pop(); + + result = max(result, height * width); + start = index; + } + + stk.push({start, heights[i]}); + } + + while (!stk.empty()) { + int width = heights.size() - stk.top().first; + int height = stk.top().second; + stk.pop(); + + result = max(result, height * width); + } + + return result; + } +}; diff --git a/cpp/0088-Merge-sorted-array.cpp b/cpp/0088-Merge-sorted-array.cpp new file mode 100644 index 000000000..4e847e172 --- /dev/null +++ b/cpp/0088-Merge-sorted-array.cpp @@ -0,0 +1,31 @@ +class Solution { +public: + void merge(vector& nums1, int m, vector& nums2, int n) { + int j=0; + int i=0; + if(n==0) return; + if(m==0) + { + for(int i = 0; i < n; i++){ + nums1[i] = nums2[i]; + } return; + } + while(inums2[j]) + { + swap(nums1[i],nums2[j]); + sort(nums2.begin(),nums2.end()); + } + i++; + } + j=0; + while(i& nums1, int m, vector& nums2, int n) { + int j=0; + int i=0; + if(n==0) return; + if(m==0) + { + for(int i = 0; i < n; i++){ + nums1[i] = nums2[i]; + } return; + } + while(inums2[j]) + { + swap(nums1[i],nums2[j]); + sort(nums2.begin(),nums2.end()); + } + i++; + } + j=0; + while(i [[],[1],[1,2],[1,2,2],[2],[2,2]] + + Backtracking, generate all combos, push/pop + to explore new combos, skip duplicates + + Time: O(n x 2^n) + Space: O(n) +*/ + +class Solution { +public: + vector> subsetsWithDup(vector& nums) { + sort(nums.begin(), nums.end()); + + vector curr; + vector> result; + + dfs(nums, 0, curr, result); + return result; + } +private: + void dfs(vector& nums, int start, vector& curr, vector>& result) { + result.push_back(curr); + for (int i = start; i < nums.size(); i++) { + if (i > start && nums[i] == nums[i - 1]) { + continue; + } + curr.push_back(nums[i]); + dfs(nums, i + 1, curr, result); + curr.pop_back(); + } + } +}; diff --git a/cpp/0091-decode-ways.cpp b/cpp/0091-decode-ways.cpp new file mode 100644 index 000000000..385bf81ae --- /dev/null +++ b/cpp/0091-decode-ways.cpp @@ -0,0 +1,38 @@ +/* + Given a string w/ only digits, return # ways to decode it (letter -> digit) + Ex. s = "12" -> 2 (AB 1 2 or L 12), s = "226" -> 3 (2 26 or 22 6 or 2 2 6) + + DP: At each digit, check validity of ones & tens, if valid add to # ways + Recurrence relation: dp[i] += dp[i-1] (if valid) + dp[i-2] (if valid) + + Time: O(n) + Space: O(n) +*/ + +class Solution { +public: + int numDecodings(string s) { + if (s[0] == '0') { + return 0; + } + + int n = s.size(); + + vector dp(n + 1); + dp[0] = 1; + dp[1] = 1; + + for (int i = 2; i <= n; i++) { + int ones = stoi(s.substr(i - 1, 1)); + if (ones >= 1 && ones <= 9) { + dp[i] += dp[i - 1]; + } + int tens = stoi(s.substr(i - 2, 2)); + if (tens >= 10 && tens <= 26) { + dp[i] += dp[i - 2]; + } + } + + return dp[n]; + } +}; diff --git a/cpp/0092-reverse-linked-list-ii.cpp b/cpp/0092-reverse-linked-list-ii.cpp new file mode 100644 index 000000000..9021edd6b --- /dev/null +++ b/cpp/0092-reverse-linked-list-ii.cpp @@ -0,0 +1,34 @@ +/* + Given the head of a singly linked list and two integers left and right where left <= right, reverse the nodes of the list from position left to position right, and return the reversed list. + + Time complexity: O(n) + Space complexity: O(1) +*/ +class Solution { +public: + ListNode* reverseBetween(ListNode* head, int left, int right) { + ListNode* dummy = new ListNode(); + dummy->next = head; + + int i=0; + ListNode* leftConnector = dummy,*temp = head; + while(inext; + i++; + } + ListNode* prev = NULL; + i=0; + while(inext; + temp->next = prev; + prev = temp; + temp = store; + i++; + } + leftConnector->next->next = temp; + leftConnector->next = prev; + + return dummy->next; + } +}; \ No newline at end of file diff --git a/cpp/0094-binary-tree-inorder-traversal.cpp b/cpp/0094-binary-tree-inorder-traversal.cpp new file mode 100644 index 000000000..45406e5f6 --- /dev/null +++ b/cpp/0094-binary-tree-inorder-traversal.cpp @@ -0,0 +1,46 @@ +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +#include + +class Solution { +public: + /* Recursive + vector helper(TreeNode* node, vector& path) { + if (node == NULL) + return path; + + path = helper(node->left, path); + path.push_back(node->val); + path = helper(node->right, path); + return path; + } + */ + + // Iterative + vector inorderTraversal(TreeNode* root) { + stack stk; + vector path; + TreeNode* current = root; + + while (!stk.empty() || current != NULL) { + while (current != NULL) { + stk.push(current); + current = current->left; + } + TreeNode* node = stk.top(); + path.push_back(node->val); + stk.pop(); + current = node->right; + } + return path; + } +}; diff --git a/cpp/0097-interleaving-string.cpp b/cpp/0097-interleaving-string.cpp new file mode 100644 index 000000000..26f936c64 --- /dev/null +++ b/cpp/0097-interleaving-string.cpp @@ -0,0 +1,41 @@ +/* + Given 3 strings, find if s3 is formed by interleaving of s1 & s2 + Ex. s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac" -> true + + DFS + memo, cache on s1 & s2 indices i & j + 2 choices: either take s1 & iterate i, or take s2 & iterate j + + Time: O(m x n) + Space: O(m x n) +*/ + +class Solution { +public: + bool isInterleave(string s1, string s2, string s3) { + if (s3.size() != s1.size() + s2.size()) { + return false; + } + return dfs(s1, s2, s3, 0, 0); + } +private: + map, bool> dp; + + bool dfs(string s1, string s2, string s3, int i, int j) { + if (i == s1.size() && j == s2.size()) { + return true; + } + if (dp.find({i, j}) != dp.end()) { + return dp[{i, j}]; + } + + if (i < s1.size() && s1[i] == s3[i + j] && dfs(s1, s2, s3, i + 1, j)) { + return true; + } + if (j < s2.size() && s2[j] == s3[i + j] && dfs(s1, s2, s3, i, j + 1)) { + return true; + } + + dp[{i, j}] = false; + return dp[{i, j}]; + } +}; diff --git a/cpp/0098-validate-binary-search-tree.cpp b/cpp/0098-validate-binary-search-tree.cpp new file mode 100644 index 000000000..d3c1cee47 --- /dev/null +++ b/cpp/0098-validate-binary-search-tree.cpp @@ -0,0 +1,102 @@ +/* + Given root of binary tree, determine if it's valid (left all < curr, right all > curr) + + Inorder traversal & check if prev >= curr, recursive/iterative solutions + + Time: O(n) + Space: O(n) +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool isValidBST(TreeNode* root) { + return helper(root, LONG_MIN, LONG_MAX); + } +private: + bool helper(TreeNode* root, long left, long right){ + if (!root) + return true; + if (root->val < right && root->val > left){ + return helper(root->left, left, root->val) && helper(root->right, root->val, right); + } + return false; + } +}; +/* +class Solution { +public: + bool isValidBST(TreeNode* root) { + TreeNode* prev = NULL; + return inorder(root, prev); + } +private: + bool inorder(TreeNode* root, TreeNode*& prev) { + if (root == NULL) { + return true; + } + + if (!inorder(root->left, prev)) { + return false; + } + + if (prev != NULL && prev->val >= root->val) { + return false; + } + prev = root; + + if (!inorder(root->right, prev)) { + return false; + } + + return true; + } +}; +*/ + +// class Solution { +// public: +// bool isValidBST(TreeNode* root) { +// stack stk; +// TreeNode* prev = NULL; + +// while (!stk.empty() || root != NULL) { +// while (root != NULL) { +// stk.push(root); +// root = root->left; +// } +// root = stk.top(); +// stk.pop(); + +// if (prev != NULL && prev->val >= root->val) { +// return false; +// } + +// prev = root; +// root = root->right; +// } + +// return true; +// } +// }; diff --git a/cpp/0100-same-tree.cpp b/cpp/0100-same-tree.cpp new file mode 100644 index 000000000..b5843c9e7 --- /dev/null +++ b/cpp/0100-same-tree.cpp @@ -0,0 +1,36 @@ +/* + Given roots of 2 binary trees, check if they're the same or not (same structure & values) + Ex. p = [1,2,3] q = [1,2,3] -> true, p = [1,2] q = [1,null,2] -> false + + Check: (1) matching nulls, (2) non-matching nulls, (3) non-matching values + + Time: O(n) + Space: O(n) +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool isSameTree(TreeNode* p, TreeNode* q) { + if (p == NULL && q == NULL) { + return true; + } + if (p == NULL || q == NULL) { + return false; + } + if (p->val != q->val) { + return false; + } + return isSameTree(p->left, q->left) && isSameTree(p->right, q->right); + } +}; diff --git a/cpp/0101-symmetric-tree.cpp b/cpp/0101-symmetric-tree.cpp new file mode 100644 index 000000000..88828dbd4 --- /dev/null +++ b/cpp/0101-symmetric-tree.cpp @@ -0,0 +1,46 @@ +/* + Given the root of a binary tree, this function checks whether the tree is symmetric, + i.e., whether it is a mirror of itself around its center. + + Time Complexity: O(n), where n is the number of nodes in the binary tree. + Space Complexity: O(n), due to the recursive stack space. + + Approach: + - If the root is NULL, return true (base case). + - Recursively check if the left subtree of the root is mirrored with the right subtree. + - To check if two subtrees are mirrored, compare their left and right children recursively. + + Example: + Input: root = [1,2,2,3,4,4,3] + Output: true + + Input: root = [1,2,2,null,3,null,3] + Output: false +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool isSymmetric(TreeNode* root) { + return dfs(root, root); + } + + bool dfs(TreeNode* left, TreeNode* right) { + if (!left && !right) + return true; + if (!left || !right) + return false; + + return left->val == right->val && dfs(left->left, right->right) && dfs(left->right, right->left); + } +}; \ No newline at end of file diff --git a/cpp/0102-binary-tree-level-order-traversal.cpp b/cpp/0102-binary-tree-level-order-traversal.cpp new file mode 100644 index 000000000..cacee33ca --- /dev/null +++ b/cpp/0102-binary-tree-level-order-traversal.cpp @@ -0,0 +1,57 @@ +/* + Given root of binary tree, return level order traversal of its nodes (left to right) + Ex. root = [3,9,20,null,null,15,7] -> [[3],[9,20],[15,7]] + + Standard BFS traversal, at each level, push left & right nodes if they exist to queue + + Time: O(n) + Space: O(n) +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector> levelOrder(TreeNode* root) { + vector> result; + + if (root == NULL) { + return result; + } + + queue q; + q.push(root); + + while (!q.empty()) { + int count = q.size(); + vector curr; + + for (int i = 0; i < count; i++) { + TreeNode* node = q.front(); + q.pop(); + + curr.push_back(node->val); + + if (node->left != NULL) { + q.push(node->left); + } + if (node->right != NULL) { + q.push(node->right); + } + } + + result.push_back(curr); + } + + return result; + } +}; diff --git a/cpp/0103-binary-tree-zigzag-level-order-traversal.cpp b/cpp/0103-binary-tree-zigzag-level-order-traversal.cpp new file mode 100644 index 000000000..6a3f4dcf5 --- /dev/null +++ b/cpp/0103-binary-tree-zigzag-level-order-traversal.cpp @@ -0,0 +1,56 @@ +/* + Given the root of a binary tree, return the zigzag level order traversal of its nodes' values. + (i.e., from left to right, then right to left for the next level and alternate between). + + Ex. Input: root = [3,9,20,null,null,15,7] + Output: [[3],[20,9],[15,7]] + + Time : O(N) + Space : O(N) +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector> zigzagLevelOrder(TreeNode* root) { + vector > v; + if(root == NULL) + return v; + + queue q; + q.push(root); + bool changeDirection = true; + + while(!q.empty()) { + vector v1; + int siz = q.size(); + + for(int i = 0 ; i < siz ; i++) { + if(changeDirection) + v1.push_back(q.front() -> val); + else + v1.insert(v1.begin(), q.front() -> val); + + if(q.front() -> left != NULL) + q.push(q.front() -> left); + if(q.front() -> right != NULL) + q.push(q.front() -> right); + + q.pop(); + } + changeDirection = !changeDirection; + v.push_back(v1); + } + return v; + } +}; diff --git a/cpp/0104-maximum-depth-of-binary-tree.cpp b/cpp/0104-maximum-depth-of-binary-tree.cpp new file mode 100644 index 000000000..a61f428b2 --- /dev/null +++ b/cpp/0104-maximum-depth-of-binary-tree.cpp @@ -0,0 +1,107 @@ +/* + Given root of binary tree, return max depth (# nodes along longest path from root to leaf) + + At every node, max depth is the max depth between its left & right children + 1 + + Time: O(n) + Space: O(n) +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + int maxDepth(TreeNode* root) { + // return maxDepthRecur(root); // recursive DFS - preorder - easiest + // return maxDepthStkInorder(root); // iterative DFS - inorder + return maxDepthStkPreorder(root); // iterative DFS - preorder - easier + // return maxDepthQueueLevelorder(root); // iterative BFS - levelorder - easy + } + + int maxDepthQueueLevelorder(TreeNode* root) { + if (root == NULL) return 0; + + queue q; + int ans = 0, depth = 0; + q.push(root); + + while (!q.empty()) + { + int s = q.size(); + for(int i = 0; i < s; i++) + { + root = q.front(); + q.pop(); + + if (root->left) q.push(root->left); + if (root->right) q.push(root->right); + } + depth += 1; + ans = max(ans, depth); + } + return ans; + } + + + int maxDepthStkPreorder(TreeNode* root) { + if (root == NULL) return 0; + + stack> s; + int ans = 1, depth = 1; + s.push({root, depth}); + while (!s.empty()) + { + root = s.top().first; + depth = s.top().second; + + ans = max(ans, depth); + s.pop(); + if (root->left) s.push({root->left, depth + 1}); + if (root->right) s.push({root->right, depth + 1}); + } + return ans; + } + + int maxDepthStkInorder(TreeNode* root) { + stack> s; + int ans = 0, depth = 0; + + while (root || !s.empty()) + { + while (root != NULL) + { + s.push(make_pair(root, ++depth)); + root = root->left; + } + + root = s.top().first; + ans = max(ans, depth); + + depth = s.top().second; + s.pop(); + + root = root->right; + } + return ans; + } + + int maxDepthRecur(TreeNode* root) { + // recursive DFS + if (root == NULL) return 0; + + // we should inc. the depth by 1 here + // and check for max in left and right subtrees + + return 1 + max(maxDepthRecur(root->left), maxDepthRecur(root->right)); + } +} diff --git a/cpp/0105-construct-binary-tree-from-preorder-and-inorder-traversal.cpp b/cpp/0105-construct-binary-tree-from-preorder-and-inorder-traversal.cpp new file mode 100644 index 000000000..b577dc928 --- /dev/null +++ b/cpp/0105-construct-binary-tree-from-preorder-and-inorder-traversal.cpp @@ -0,0 +1,50 @@ +/* + Given 2 integer arrays preorder & inorder, construct & return the binary tree + Ex. preorder = [3,9,20,15,7], inorder = [9,3,15,20,7] -> [3,9,20,null,null,15,7] + + Preorder dictates nodes, inorder dictates subtrees (preorder values, inorder positions) + + Time: O(n) + Space: O(n) +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* buildTree(vector& preorder, vector& inorder) { + int index = 0; + return build(preorder, inorder, index, 0, inorder.size() - 1); + } +private: + TreeNode* build(vector& preorder, vector& inorder, int& index, int i, int j) { + if (i > j) { + return NULL; + } + + TreeNode* root = new TreeNode(preorder[index]); + + int split = 0; + for (int i = 0; i < inorder.size(); i++) { + if (preorder[index] == inorder[i]) { + split = i; + break; + } + } + index++; + + root->left = build(preorder, inorder, index, i, split - 1); + root->right = build(preorder, inorder, index, split + 1, j); + + return root; + } +}; diff --git a/cpp/0110-balanced-binary-tree.cpp b/cpp/0110-balanced-binary-tree.cpp new file mode 100644 index 000000000..751c3e21f --- /dev/null +++ b/cpp/0110-balanced-binary-tree.cpp @@ -0,0 +1,47 @@ +/* + Given binary tree, determine if height-balanced (all left & right subtrees height diff <= 1) + + Check if subtrees are balanced, if so, use their heights to determine further balance + + Time: O(n) + Space: O(n) +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool isBalanced(TreeNode* root) { + int height = 0; + return dfs(root, height); + } +private: + bool dfs(TreeNode* root, int& height) { + if (root == NULL) { + height = -1; + return true; + } + + int left = 0; + int right = 0; + + if (!dfs(root->left, left) || !dfs(root->right, right)) { + return false; + } + if (abs(left - right) > 1) { + return false; + } + + height = 1 + max(left, right); + return true; + } +}; diff --git a/cpp/0111-minimum-depth-of-binary-tree.cpp b/cpp/0111-minimum-depth-of-binary-tree.cpp new file mode 100644 index 000000000..6677814d4 --- /dev/null +++ b/cpp/0111-minimum-depth-of-binary-tree.cpp @@ -0,0 +1,51 @@ +/* + Given a binary tree, find its minimum depth. + + The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node. + + Note: A leaf is a node with no children. + + Ex. Input: root = [3,9,20,null,null,15,7] + Output: 2 + + Time : O(N) + Space : O(N) +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int minDepth(TreeNode* root) { + if(!root) + return 0; + int count = 1; + queue q; + q.push(root); + while(!q.empty()) { + int size = q.size(); + for(int i = 0 ; i < size ; i++) { + TreeNode* front = q.front(); + if(!front -> left && !front -> right) + return count; + if(front -> left) + q.push(front -> left); + if(front -> right) + q.push(front -> right); + + q.pop(); + } + count++; + } + return count; + } +}; diff --git a/cpp/0112-path-sum.cpp b/cpp/0112-path-sum.cpp new file mode 100644 index 000000000..19788ca1b --- /dev/null +++ b/cpp/0112-path-sum.cpp @@ -0,0 +1,39 @@ +/* +0112-path-sum.cpp +Given the root of a binary tree and an integer targetSum, return true if the tree has a root-to-leaf path such that adding up all the values along the path equals targetSum. + +A leaf is a node with no children. + +Algorithm Used : DFS + +Time Complexity : O(n) +n = number of nodes in binary tree + +Space Complexity : O(h) +h = height of binary tree + +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool hasPathSum(TreeNode* root, int targetSum) { + if (!root) return false; + + targetSum -= root->val; + + if (!root->left && !root->right && (targetSum == 0)) return true; + + return hasPathSum(root->left, targetSum) || hasPathSum(root->right, targetSum); + } +}; diff --git a/cpp/0115-distinct-subsequences.cpp b/cpp/0115-distinct-subsequences.cpp new file mode 100644 index 000000000..bb6d371cb --- /dev/null +++ b/cpp/0115-distinct-subsequences.cpp @@ -0,0 +1,41 @@ +/* + Given 2 strings s & t: + Return # of distinct subsequences of s which equals t + Ex. s = "rabbbit", t = "rabbit" -> 3, RABBbIT, RAbBBIT, RABbBIT + + DFS + memo, cache on i & j indices to the # of distinct subseq + 2 choices: if chars equal, look at remainder of both s & t + if chars not equal, only look at remainder of s + + Time: O(m x n) + Space: O(m x n) +*/ + +class Solution { +public: + int numDistinct(string s, string t) { + return dfs(s, t, 0, 0); + } +private: + // {(i, j) -> # of distinct subsequences} + map, int> dp; + + int dfs(string& s, string& t, int i, int j) { + if (j == t.size()) { + return 1; + } + if (i == s.size()) { + return 0; + } + if (dp.find({i, j}) != dp.end()) { + return dp[{i, j}]; + } + + if (s[i] == t[j]) { + dp[{i, j}] = dfs(s, t, i + 1, j + 1) + dfs(s, t, i + 1, j); + } else { + dp[{i, j}] = dfs(s, t, i + 1, j); + } + return dp[{i, j}]; + } +}; diff --git a/cpp/0116-populating-next-right-pointers-in-each-node.cpp b/cpp/0116-populating-next-right-pointers-in-each-node.cpp new file mode 100644 index 000000000..24a47e615 --- /dev/null +++ b/cpp/0116-populating-next-right-pointers-in-each-node.cpp @@ -0,0 +1,69 @@ +/* + You are given a perfect binary tree where all leaves are on the same level, and every parent has two children. The binary tree has the following definition: + + struct Node { + int val; + Node *left; + Node *right; + Node *next; + } + + Populate each next pointer to point to its next right node. If there is no next right node, the next pointer should be set to NULL. + Initially, all next pointers are set to NULL. + + Ex. Input: root = [1,2,3,4,5,6,7] + Output: [1,#,2,3,#,4,5,6,7,#] + Explanation: Given the above perfect binary tree (Figure A), your function should populate each next pointer to point to its next right node, + just like in Figure B. The serialized output is in level order as connected by the next pointers, with '#' signifying the end of each level. + + Time : O(N) + Space : O(N) +*/ + +/* +// Definition for a Node. +class Node { +public: + int val; + Node* left; + Node* right; + Node* next; + + Node() : val(0), left(NULL), right(NULL), next(NULL) {} + + Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {} + + Node(int _val, Node* _left, Node* _right, Node* _next) + : val(_val), left(_left), right(_right), next(_next) {} +}; +*/ + +class Solution { +public: + Node* connect(Node* root) { + if(root == NULL || (root -> right == NULL && root -> left == NULL)) + return root; + queue q; + q.push(root); + + while(!q.empty()) { + int size = q.size(); + Node* temp = NULL; + + for(int i = 0 ; i < size ; i++) { + Node* front = q.front(); + + if(temp != NULL) + temp -> next = front; + temp = front; + + if(front -> left) { + q.push(front -> left); + q.push(front -> right); + } + q.pop(); + } + } + return root; + } +}; diff --git a/cpp/0117-populating-next-right-pointers-in-each-node-ii.cpp b/cpp/0117-populating-next-right-pointers-in-each-node-ii.cpp new file mode 100644 index 000000000..1973145f2 --- /dev/null +++ b/cpp/0117-populating-next-right-pointers-in-each-node-ii.cpp @@ -0,0 +1,68 @@ +/* + Given a binary tree + + struct Node { + int val; + Node *left; + Node *right; + Node *next; + } + + Populate each next pointer to point to its next right node. If there is no next right node, the next pointer should be set to NULL. + + Initially, all next pointers are set to NULL. + + Ex. Input: root = [1,2,3,4,5,null,7] + Output: [1,#,2,3,#,4,5,7,#] + Explanation: Given the above binary tree (Figure A), your function should populate each next pointer to point to its next right node, + just like in Figure B. The serialized output is in level order as connected by the next pointers, with '#' signifying the end of each level. + + Time : O(N) + Space : O(1) +*/ + +/* +// Definition for a Node. +class Node { +public: + int val; + Node* left; + Node* right; + Node* next; + + Node() : val(0), left(NULL), right(NULL), next(NULL) {} + + Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {} + + Node(int _val, Node* _left, Node* _right, Node* _next) + : val(_val), left(_left), right(_right), next(_next) {} +}; +*/ + +class Solution { +public: + Node* connect(Node* root) { + if(root == NULL) + return NULL; + Node* parent = root; + + while(parent) { + Node* newNode = new Node; + Node* temp = newNode; + + while(parent) { + if(parent -> left) { + temp -> next = parent -> left; + temp = temp -> next; + } + if(parent -> right) { + temp -> next = parent -> right; + temp = temp -> next; + } + parent = parent -> next; + } + parent = newNode -> next; + } + return root; + } +}; diff --git a/cpp/0118-pascals-triangle.cpp b/cpp/0118-pascals-triangle.cpp new file mode 100644 index 000000000..348bc756b --- /dev/null +++ b/cpp/0118-pascals-triangle.cpp @@ -0,0 +1,17 @@ +class Solution { +public: + vector> generate(int numRows) + { + vector> ret; + + for(int i = 0; i < numRows ; i++){ + vector row(i+1, 1); + for(int j = 1; j < i ; j++){ + row[j] = ret[i-1][j] + ret[i-1][j-1]; + } + ret.push_back(row); + } + + return ret; + } +}; \ No newline at end of file diff --git a/cpp/0120-triangle.cpp b/cpp/0120-triangle.cpp new file mode 100644 index 000000000..d35c0e184 --- /dev/null +++ b/cpp/0120-triangle.cpp @@ -0,0 +1,21 @@ +class Solution{ + public: + int minimumTotal(vector>& triangle){ + for(int i = 0; i < triangle.size() - 1; i++){ + for(int k = 0; k < triangle[i + 1].size(); k++){ + if(k == 0){ + triangle[i + 1][0] = triangle[i + 1][0] + triangle[i][0]; + } + else{ + if(k == triangle[i + 1].size() - 1){ + triangle[i + 1][k] = triangle[i + 1][k] + triangle[i][k - 1]; + } + else{ + triangle[i + 1][k] = triangle[i + 1][k] + min(triangle[i][k - 1], triangle[i][k]); + } + } + } + } + return *min_element(triangle[triangle.size() - 1].begin(), triangle[triangle.size() - 1].end()); + } +}; diff --git a/cpp/0121-best-time-to-buy-and-sell-stock.cpp b/cpp/0121-best-time-to-buy-and-sell-stock.cpp new file mode 100644 index 000000000..ef59077b3 --- /dev/null +++ b/cpp/0121-best-time-to-buy-and-sell-stock.cpp @@ -0,0 +1,24 @@ +/* + Given array prices, return max profit w/ 1 buy & 1 sell + Ex. prices = [7,1,5,3,6,4] -> 5 (buy at $1, sell at $6) + + For each, get diff b/w that & min value before, store max + + Time: O(n) + Space: O(1) +*/ + +class Solution { +public: + int maxProfit(vector& prices) { + int maxP = 0, l = 0, r = 0; + while (r < prices.size()){ + if (prices[r] > prices[l]) + maxP = max(maxP, prices[r] - prices[l]); + else + l = r; + ++r; + } + return maxP; + } +}; diff --git a/cpp/0122-best-time-to-buy-and-sell-stock-ii.cpp b/cpp/0122-best-time-to-buy-and-sell-stock-ii.cpp new file mode 100644 index 000000000..5d71bd465 --- /dev/null +++ b/cpp/0122-best-time-to-buy-and-sell-stock-ii.cpp @@ -0,0 +1,22 @@ + /* + Approach: + If stock price is going up tomorrow just buy it today and sell it tomorrow. + + Time complexity : O(n) + Space complexity: O(1) + + n is number of days. +*/ + +class Solution { +public: + int maxProfit(vector& prices) { + int ans =0; + + // look into the next day and if you are making profit just buy it today. + for(int i =1;iprices[i-1]) ans += (prices[i]-prices[i-1]); + } + return ans; + } +}; \ No newline at end of file diff --git a/cpp/0124-binary-tree-maximum-path-sum.cpp b/cpp/0124-binary-tree-maximum-path-sum.cpp new file mode 100644 index 000000000..751da2414 --- /dev/null +++ b/cpp/0124-binary-tree-maximum-path-sum.cpp @@ -0,0 +1,42 @@ +/* + Given root of binary tree, return max path sum (seq of adj node values added together) + + Path can only have <= 1 split point, assume curPath has it, so return can't split again + + Time: O(n) + Space: O(n) +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int maxPathSum(TreeNode* root) { + int maxPath = INT_MIN; + dfs(root, maxPath); + return maxPath; + } +private: + int dfs(TreeNode* root, int& maxPath) { + if (root == NULL) { + return 0; + } + + int left = max(dfs(root->left, maxPath), 0); + int right = max(dfs(root->right, maxPath), 0); + + int curPath = root->val + left + right; + maxPath = max(maxPath, curPath); + + return root->val + max(left, right); + } +}; diff --git a/cpp/0125-valid-palindrome.cpp b/cpp/0125-valid-palindrome.cpp new file mode 100644 index 000000000..3f71d0254 --- /dev/null +++ b/cpp/0125-valid-palindrome.cpp @@ -0,0 +1,33 @@ +/* + Given a string s, return true if it's a palindrome + Ex. s = "A man, a plan, a canal: Panama" -> true + + 2 pointers, outside in, skip non-letters & compare + + Time: O(n) + Space: O(1) +*/ + +class Solution { +public: + bool isPalindrome(string s) { + int i = 0; + int j = s.size() - 1; + + while (i < j) { + while (!isalnum(s[i]) && i < j) { + i++; + } + while (!isalnum(s[j]) && i < j) { + j--; + } + if (tolower(s[i]) != tolower(s[j])) { + return false; + } + i++; + j--; + } + + return true; + } +}; diff --git a/cpp/0127-word-ladder.cpp b/cpp/0127-word-ladder.cpp new file mode 100644 index 000000000..6d698aed2 --- /dev/null +++ b/cpp/0127-word-ladder.cpp @@ -0,0 +1,55 @@ +/* + Given 2 words & a dictionary, return min # of words to transform b/w them + Ex. begin = "hit", end = "cog", dict = ["hot","dot","dog","lot","log","cog"] -> 5 + "hit" -> "hot" -> "dot" -> "dog" -> "cog" + + BFS, change 1 letter at a time (neighbors), if in dict add to queue, else skip + + Time: O(m^2 x n) -> m = length of each word, n = # of words in input word list + Space: O(m^2 x n) +*/ + +class Solution { +public: + int ladderLength(string beginWord, string endWord, vector& wordList) { + unordered_set dict; + for (int i = 0; i < wordList.size(); i++) { + dict.insert(wordList[i]); + } + + queue q; + q.push(beginWord); + + int result = 1; + + while (!q.empty()) { + int count = q.size(); + + for (int i = 0; i < count; i++) { + string word = q.front(); + q.pop(); + + if (word == endWord) { + return result; + } + dict.erase(word); + + for (int j = 0; j < word.size(); j++) { + char c = word[j]; + for (int k = 0; k < 26; k++) { + word[j] = k + 'a'; + if (dict.find(word) != dict.end()) { + q.push(word); + dict.erase(word); + } + word[j] = c; + } + } + } + + result++; + } + + return 0; + } +}; diff --git a/cpp/0128-longest-consecutive-sequence.cpp b/cpp/0128-longest-consecutive-sequence.cpp new file mode 100644 index 000000000..bba4acb52 --- /dev/null +++ b/cpp/0128-longest-consecutive-sequence.cpp @@ -0,0 +1,29 @@ +/* + Given unsorted array, return length of longest consecutive sequence + Ex. nums = [100,4,200,1,3,2] -> 4, longest is [1,2,3,4] + + Store in hash set, only check for longer seq if it's the beginning + + Time: O(n) + Space: O(n) +*/ + + +class Solution { +public: + int longestConsecutive(vector& nums) { + unordered_sets(nums.begin(), nums.end()); + int longest = 0; + for(auto &n: s){ + //if this is the start of the sequence + if(!s.count(n - 1)){ + int length = 1; + while(s.count(n + length)) + ++length; + longest = max(longest, length); + } + + } + return longest; + } +}; diff --git a/cpp/0129-sum-root-to-leaf-numbers.cpp b/cpp/0129-sum-root-to-leaf-numbers.cpp new file mode 100644 index 000000000..7014e36d9 --- /dev/null +++ b/cpp/0129-sum-root-to-leaf-numbers.cpp @@ -0,0 +1,38 @@ +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +class Solution { +public: + int sumNumbers(TreeNode* root) { + if (!root) + return 0; + stack > stk; + stk.push(make_pair(root, root->val)); + + int sum = 0; + while (!stk.empty()) { + pair elm = stk.top(); + stk.pop(); + TreeNode* node = elm.first; + int num = elm.second; + if (!node->left && !node->right) { + sum += num; + continue; + } + if (node->left) + stk.push(make_pair(node->left, num * 10 + node->left->val)); + if (node->right) + stk.push(make_pair(node->right, num * 10 + node->right->val)); + } + return sum; + } +}; diff --git a/cpp/0130-surrounded-regions.cpp b/cpp/0130-surrounded-regions.cpp new file mode 100644 index 000000000..a6dc107fe --- /dev/null +++ b/cpp/0130-surrounded-regions.cpp @@ -0,0 +1,110 @@ +/* + Given a matrix, capture ('X') all regions that are surrounded ('O') + + Distinguish captured vs escaped, 'X' vs 'O' vs 'E' + + Time: O(m x n) + Space: O(m x n) +*/ + +class Solution { +public: + void solve(vector>& board) { + int m = board.size(); + int n = board[0].size(); + + // marking escaped cells along the border + for (int i = 0; i < m; i++) { + dfs(board,i,0,m,n); + dfs(board,i,n-1,m,n); + } + + for (int j = 0; j < n; j++) { + dfs(board,0,j,m,n); + dfs(board,m-1,j,m,n); + } + + // flip cells to correct final states + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (board[i][j] == 'O') { + board[i][j] = 'X'; + } + if (board[i][j] == 'E') { + board[i][j] = 'O'; + } + } + } + } +private: + void dfs(vector>& board, int i, int j, int m, int n) { + if (i < 0 || i >= m || j < 0 || j >= n || board[i][j] != 'O') { + return; + } + + board[i][j] = 'E'; + + dfs(board, i - 1, j, m, n); + dfs(board, i + 1, j, m, n); + dfs(board, i, j - 1, m, n); + dfs(board, i, j + 1, m, n); + } +}; + +/* + BFS Solution +*/ + +class Solution { +private: + int rows, cols; + + void bfs(int row, int col, vector>& board) { + board[row][col] = 'E'; + queue> q; + q.push({row, col}); + + vector> directions = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}}; + while (!q.empty()) { + auto [r, c] = q.front(); + q.pop(); + for (const auto &direction : directions) { + int newRow = r + direction.first; + int newCol = c + direction.second; + if (newRow < rows && newRow >= 0 && newCol < cols && newCol >= 0 && board[newRow][newCol] == 'O') { + board[newRow][newCol] = 'E'; + q.push({newRow, newCol}); + } + } + } + + } + +public: + void solve(vector>& board) { + rows = board.size(); + cols = board[0].size(); + + for (int row = 0; row < rows; ++row) { + if (board[row][0] == 'O') bfs(row, 0, board); + if (board[row][cols - 1] == 'O') bfs(row, cols - 1, board); + } + + for (int col = 0; col < cols; ++col) { + if (board[0][col] == 'O') bfs(0, col, board); + if (board[rows - 1][col] == 'O') bfs(rows - 1, col, board); + } + + for (int row = 0; row < rows; ++row) { + for (int col = 0; col < cols; ++col) { + if (board[row][col] == 'O') { + board[row][col] = 'X'; + } + else if (board[row][col] == 'E') { + board[row][col] = 'O'; + } + } + } + + } +}; diff --git a/cpp/0131-palindrome-partitioning.cpp b/cpp/0131-palindrome-partitioning.cpp new file mode 100644 index 000000000..6d4866e7a --- /dev/null +++ b/cpp/0131-palindrome-partitioning.cpp @@ -0,0 +1,44 @@ +/* + Given a string, partition such that every substring is a palindrome, return all possible ones + Ex. s = "aab" -> [["a","a","b"],["aa","b"]], s = "a" -> [["a"]] + + Generate all possible substrings at idx, if palindrome potential candidate, backtrack after + + Time: O(n x 2^n) + Space: O(n) +*/ + +class Solution { +public: + vector> partition(string s) { + vector curr; + vector> result; + dfs(s, 0, curr, result); + return result; + } +private: + void dfs(string s, int start, vector& curr, vector>& result) { + if (start == s.size()) { + result.push_back(curr); + return; + } + for (int i = start; i < s.size(); i++) { + if (isPalindrome(s, start, i)) { + string str = s.substr(start, i - start + 1); + curr.push_back(str); + dfs(s, i + 1, curr, result); + curr.pop_back(); + } + } + } + bool isPalindrome(string s, int left, int right) { + while (left < right) { + if (s[left] != s[right]) { + return false; + } + left++; + right--; + } + return true; + } +}; diff --git a/cpp/0133-clone-graph.cpp b/cpp/0133-clone-graph.cpp new file mode 100644 index 000000000..12e35f0ea --- /dev/null +++ b/cpp/0133-clone-graph.cpp @@ -0,0 +1,83 @@ +/* + Given ref of a node in connected undirected graph, return deep copy + + Both BFS & DFS work, map original node to its copy + + Time: O(m + n) + Space: O(n) +*/ + +/* +// Definition for a Node. +class Node { +public: + int val; + vector neighbors; + Node() { + val = 0; + neighbors = vector(); + } + Node(int _val) { + val = _val; + neighbors = vector(); + } + Node(int _val, vector _neighbors) { + val = _val; + neighbors = _neighbors; + } +}; +*/ + +// class Solution { +// public: +// Node* cloneGraph(Node* node) { +// if (node == NULL) { +// return NULL; +// } +// if (m.find(node) == m.end()) { +// m[node] = new Node(node->val); +// for (int i = 0; i < node->neighbors.size(); i++) { +// Node* neighbor = node->neighbors[i]; +// m[node]->neighbors.push_back(cloneGraph(neighbor)); +// } +// } +// return m[node]; +// } +// private: +// unordered_map m; +// }; + +class Solution { +public: + Node* cloneGraph(Node* node) { + if (node == NULL) { + return NULL; + } + + Node* copy = new Node(node->val); + m[node] = copy; + + queue q; + q.push(node); + + while (!q.empty()) { + Node* curr = q.front(); + q.pop(); + + for (int i = 0; i < curr->neighbors.size(); i++) { + Node* neighbor = curr->neighbors[i]; + + if (m.find(neighbor) == m.end()) { + m[neighbor] = new Node(neighbor->val); + q.push(neighbor); + } + + m[curr]->neighbors.push_back(m[neighbor]); + } + } + + return copy; + } +private: + unordered_map m; +}; diff --git a/cpp/0134-gas-station.cpp b/cpp/0134-gas-station.cpp new file mode 100644 index 000000000..ff4c7468c --- /dev/null +++ b/cpp/0134-gas-station.cpp @@ -0,0 +1,39 @@ +/* + Gas stations along circular route, return where to start to complete 1 trip + Ex. gas = [1,2,3,4,5] cost = [3,4,5,1,2] -> index 3 (station 4), tank = 4,8,7,6,5 + + At a start station, if total ever becomes negative won't work, try next station + + Time: O(n) + Space: O(1) +*/ + +class Solution { +public: + int canCompleteCircuit(vector& gas, vector& cost) { + int n = gas.size(); + + int totalGas = 0; + int totalCost = 0; + for (int i = 0; i < n; i++) { + totalGas += gas[i]; + totalCost += cost[i]; + } + if (totalGas < totalCost) { + return -1; + } + + int total = 0; + int result = 0; + + for (int i = 0; i < n; i++) { + total += gas[i] - cost[i]; + if (total < 0) { + total = 0; + result = i + 1; + } + } + + return result; + } +}; diff --git a/cpp/0136-single-number.cpp b/cpp/0136-single-number.cpp new file mode 100644 index 000000000..e20ac227f --- /dev/null +++ b/cpp/0136-single-number.cpp @@ -0,0 +1,22 @@ +/* + Given int array, every element appears twice except 1, find it + Ex. nums = [2,2,1] -> 1, nums = [4,1,2,1,2] -> 4 + + a XOR a returns 0, so returns 0 for all except the unique one + + Time: O(n) + Space: O(1) +*/ + +class Solution { +public: + int singleNumber(vector& nums) { + int result = 0; + + for (int i = 0; i < nums.size(); i++) { + result = result ^ nums[i]; + } + + return result; + } +}; diff --git a/cpp/0138-copy-list-with-random-pointer.cpp b/cpp/0138-copy-list-with-random-pointer.cpp new file mode 100644 index 000000000..3e462a64f --- /dev/null +++ b/cpp/0138-copy-list-with-random-pointer.cpp @@ -0,0 +1,144 @@ +/* + Given linked list w/ also a random pointer, construct deep copy + + Hash map {old -> new}, O(n) space + Optimize interweave old and new nodes, O(1) space + A -> A' -> B -> B' -> C -> C', A'.random = A.random.next + + Time: O(n) + Space: O(n) -> can optimize to O(1) +*/ + +/* +// Definition for a Node. +class Node { +public: + int val; + Node* next; + Node* random; + + Node(int _val) { + val = _val; + next = NULL; + random = NULL; + } +}; +*/ + +// class Solution { +// public: +// Node* copyRandomList(Node* head) { +// if (head == NULL) { +// return NULL; +// } +// Node* oldNode = head; +// Node* newNode = new Node(oldNode->val); +// visited[oldNode] = newNode; +// while (oldNode != NULL) { +// newNode->next = getClonedNode(oldNode->next); +// newNode->random = getClonedNode(oldNode->random); +// oldNode = oldNode->next; +// newNode = newNode->next; +// } +// return visited[head]; +// } +// private: +// unordered_map visited; +// Node* getClonedNode(Node* node) { +// if (node == NULL) { +// return NULL; +// } +// if (visited.find(node) != visited.end()) { +// return visited[node]; +// } +// visited[node] = new Node(node->val); +// return visited[node]; +// } +// }; +/* +class Solution { +public: + Node* copyRandomList(Node* head) { + if (head == NULL) { + return NULL; + } + + Node* ptr = head; + while (ptr != NULL) { + Node* newNode = new Node(ptr->val); + newNode->next = ptr->next; + ptr->next = newNode; + ptr = newNode->next; + } + ptr = head; + + while (ptr != NULL) { + if (ptr->random == NULL) { + ptr->next->random == NULL; + } else { + ptr->next->random = ptr->random->next; + } + ptr = ptr->next->next; + } + + Node* oldPtr = head; + Node* newPtr = head->next; + Node* oldHead = head->next; + + while (oldPtr != NULL) { + oldPtr->next = oldPtr->next->next; + if (newPtr->next == NULL) { + newPtr->next = NULL; + } else { + newPtr->next = newPtr->next->next; + } + oldPtr = oldPtr->next; + newPtr = newPtr->next; + } + + return oldHead; + } +}; +*/ + +// class Solution { +// public: +// Node* copyRandomList(Node* head) { +// unordered_map nodes; +// Node* h = head; + +// while (h){ +// nodes[h] = new Node(h->val); +// h = h->next; +// } +// h = head; +// while (h){ +// Node* newNode = nodes[h]; +// newNode->next = nodes[h->next]; +// newNode->random = nodes[h->random]; +// h = h->next; +// } +// return nodes[head]; +// } +// }; + +class Solution { +public: + Node* copyRandomList(Node* head) { + unordered_map nodes; + Node* curr = head; + + while (curr != NULL) { + nodes[curr] = new Node(curr->val); + curr = curr->next; + } + + curr = head; + while (curr != NULL) { + nodes[curr]->next = nodes[curr->next]; + nodes[curr]->random = nodes[curr->random]; + curr = curr->next; + } + return nodes[head]; + } +}; diff --git a/cpp/0139-word-break.cpp b/cpp/0139-word-break.cpp new file mode 100644 index 000000000..c0613d33e --- /dev/null +++ b/cpp/0139-word-break.cpp @@ -0,0 +1,37 @@ +/* + Given a string & dictionary, return true if: + Can segment string into 1 or more dictionary words + + DP, at each loop, substring, check if in dict, & store + + Time: O(n^3) + Space: O(n) +*/ + +class Solution { +public: + bool wordBreak(string s, vector& wordDict) { + unordered_set words; + for (int i = 0; i < wordDict.size(); i++) { + words.insert(wordDict[i]); + } + + int n = s.size(); + vector dp(n + 1); + dp[0] = true; + + for (int i = 1; i <= n; i++) { + for (int j = i - 1; j >= 0; j--) { + if (dp[j]) { + string word = s.substr(j, i - j); + if (words.find(word) != words.end()) { + dp[i] = true; + break; + } + } + } + } + + return dp[n]; + } +}; diff --git a/cpp/0141-linked-list-cycle.cpp b/cpp/0141-linked-list-cycle.cpp new file mode 100644 index 000000000..1745aacdc --- /dev/null +++ b/cpp/0141-linked-list-cycle.cpp @@ -0,0 +1,38 @@ +/* + Given head of a linked list, determine if it has a cycle in it + + Slow/fast pointers, if they ever intersect then there's a cycle + + Time: O(n) + Space: O(1) +*/ + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ +class Solution { +public: + bool hasCycle(ListNode *head) { + if (head == NULL) { + return false; + } + + ListNode* slow = head; + ListNode* fast = head; + + while (fast->next != NULL && fast->next->next != NULL) { + slow = slow->next; + fast = fast->next->next; + if (slow == fast) { + return true; + } + } + + return false; + } +}; diff --git a/cpp/0143-reorder-list.cpp b/cpp/0143-reorder-list.cpp new file mode 100644 index 000000000..bbd971f32 --- /dev/null +++ b/cpp/0143-reorder-list.cpp @@ -0,0 +1,75 @@ +/* + Given head of linked-list, reorder list alternating outside in + Ex. head = [1,2,3,4] -> [1,4,2,3], head = [1,2,3,4,5] -> [1,5,2,4,3] + + Find middle node, split in half, reverse 2nd half of list, merge + + Time: O(n) + Space: O(1) +*/ + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + void reorderList(ListNode* head) { + if (head->next == NULL) { + return; + } + + ListNode* prev = NULL; + ListNode* slow = head; + ListNode* fast = head; + + while (fast != NULL && fast->next != NULL) { + prev = slow; + slow = slow->next; + fast = fast->next->next; + } + + prev->next = NULL; + + ListNode* l1 = head; + ListNode* l2 = reverse(slow); + + merge(l1, l2); + } +private: + ListNode* reverse(ListNode* head) { + ListNode* prev = NULL; + ListNode* curr = head; + ListNode* next = curr->next; + + while (curr != NULL) { + next = curr->next; + curr->next = prev; + prev = curr; + curr = next; + } + + return prev; + } + void merge(ListNode* l1, ListNode* l2) { + while (l1 != NULL) { + ListNode* p1 = l1->next; + ListNode* p2 = l2->next; + + l1->next = l2; + if (p1 == NULL) { + break; + } + l2->next = p1; + + l1 = p1; + l2 = p2; + } + } +}; diff --git a/cpp/0144-binary-tree-preorder-traversal.cpp b/cpp/0144-binary-tree-preorder-traversal.cpp new file mode 100644 index 000000000..25922450f --- /dev/null +++ b/cpp/0144-binary-tree-preorder-traversal.cpp @@ -0,0 +1,34 @@ +/* + Given the root of a binary tree, return the preorder traversal of its nodes' values. + + Ex. Input: root = [1,null,2,3] + Output: [1,2,3] + + Time : O(N) + Space : O(H) -> H = Height of the binary tree +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector res; + + vector preorderTraversal(TreeNode* root) { + if(root != NULL) { + res.push_back(root -> val); + preorderTraversal(root -> left); + preorderTraversal(root -> right); + } + return res; + } +}; diff --git a/cpp/0145-binary-tree-postorder-traversal.cpp b/cpp/0145-binary-tree-postorder-traversal.cpp new file mode 100644 index 000000000..84429c711 --- /dev/null +++ b/cpp/0145-binary-tree-postorder-traversal.cpp @@ -0,0 +1,25 @@ +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector postorderTraversal(TreeNode* root) { + if(!root) return {}; + + postorderTraversal(root->left); + postorderTraversal(root->right); + result.push_back(root->val); + + return result; + } +private: + vector result; +}; \ No newline at end of file diff --git a/cpp/0146-lru-cache.cpp b/cpp/0146-lru-cache.cpp new file mode 100644 index 000000000..32011eff9 --- /dev/null +++ b/cpp/0146-lru-cache.cpp @@ -0,0 +1,100 @@ +/* + Design data structure that follows constraints of an LRU cache + + Hash map + doubly linked list, left = LRU, right = MRU + get: update to MRU, put: update to MRU, remove LRU if full + + Time: O(1) + Space: O(capacity) +*/ + +class Node { +public: + int k; + int val; + Node* prev; + Node* next; + + Node(int key, int value) { + k = key; + val = value; + prev = NULL; + next = NULL; + } +}; + +class LRUCache { +public: + LRUCache(int capacity) { + cap = capacity; + + left = new Node(0, 0); + right = new Node(0, 0); + + left->next = right; + right->prev = left; + } + + int get(int key) { + if (cache.find(key) != cache.end()) { + remove(cache[key]); + insert(cache[key]); + return cache[key]->val; + } + return -1; + } + + void put(int key, int value) { + if (cache.find(key) != cache.end()) { + remove(cache[key]); + + // Free allocated memory for the removed node + delete cache[key]; + } + cache[key] = new Node(key, value); + insert(cache[key]); + + if (cache.size() > cap) { + // remove from list & delete LRU from map + Node* lru = left->next; + remove(lru); + cache.erase(lru->k); + + // Free allocated memory for the removed node + delete lru; + } + } +private: + int cap; + unordered_map cache; // {key -> node} + Node* left; + Node* right; + + // remove node from list + void remove(Node* node) { + Node* prev = node->prev; + Node* next = node->next; + + prev->next = next; + next->prev = prev; + } + + // insert node at right + void insert(Node* node) { + Node* prev = right->prev; + Node* next = right; + + prev->next = node; + next->prev = node; + + node->prev = prev; + node->next = next; + } +}; + +/** + * Your LRUCache object will be instantiated and called as such: + * LRUCache* obj = new LRUCache(capacity); + * int param_1 = obj->get(key); + * obj->put(key,value); + */ diff --git a/cpp/0147-insertion-sort-list.cpp b/cpp/0147-insertion-sort-list.cpp new file mode 100644 index 000000000..9fd7d1fec --- /dev/null +++ b/cpp/0147-insertion-sort-list.cpp @@ -0,0 +1,69 @@ +/* + Given the head of a singly linked list, sort the list using insertion sort, and return the sorted list's head. + + The steps of the insertion sort algorithm: + + 1. Insertion sort iterates, consuming one input element each repetition and growing a sorted output list. + 2. At each iteration, insertion sort removes one element from the input data, finds the location it belongs within the sorted list and inserts it there. + 3. It repeats until no input elements remain. + + Ex. Input: head = [4,2,1,3] + Output: [1,2,3,4] + + Time : O(N^2) + Space: O(1) +*/ + + + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* insertionSortList(ListNode* head) { + if(head == NULL || head -> next == NULL) { + return head; + } + + ListNode *ptr1 = head -> next, *sortedPtr = head; + while(ptr1 != NULL) { + if(ptr1 -> val < sortedPtr -> val) { + ListNode *ptr2 = head, *lagPtr = head; + while(true) { + if(ptr2 -> val > ptr1 -> val) { + if(ptr2 == head) { + sortedPtr -> next = ptr1 -> next; + ptr1 -> next = head; + head = ptr1; + ptr1 = sortedPtr -> next; + break; + } + else { + sortedPtr -> next = ptr1 -> next; + ptr1 -> next = ptr2; + lagPtr -> next = ptr1; + ptr1 = sortedPtr -> next; + break; + } + } + lagPtr = ptr2; + ptr2 = ptr2 -> next; + } + + } else { + sortedPtr = sortedPtr -> next; + ptr1 = ptr1 -> next; + } + } + + return head; + } +}; diff --git a/cpp/0148-sort-list.cpp b/cpp/0148-sort-list.cpp new file mode 100644 index 000000000..070d922f1 --- /dev/null +++ b/cpp/0148-sort-list.cpp @@ -0,0 +1,76 @@ +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +private: + // find middle of LL using slow and fast pointers + ListNode* findLLMid(ListNode* head) { + ListNode* slow = head; + ListNode* fast = head->next; + while (fast != NULL && fast->next != NULL) { + slow = slow->next; + fast = fast->next->next; + } + return slow; + } + + // merges two sorted LLs + ListNode* merge(ListNode* head1, ListNode* head2) { + ListNode* dummy = new ListNode(); + ListNode* p = dummy; + ListNode* p1 = head1; + ListNode* p2 = head2; + + while (p1 != NULL && p2 != NULL) { + if (p1->val < p2->val) { + ListNode* temp = p1->next; + p->next = p1; + p1->next = NULL; + p1 = temp; + } else { + ListNode* temp = p2->next; + p->next = p2; + p2->next = NULL; + p2 = temp; + } + p = p->next; + } + + if (p1 == NULL) { + p->next = p2; + } else if (p2 == NULL) { + p->next = p1; + } + + return dummy->next; + } +public: + // merge sort implementation + ListNode* sortList(ListNode* head) { + // base cases + if (head == NULL) + return NULL; + if (head->next == NULL) + return head; + + // split LL into two halves + ListNode* middleOfLL = findLLMid(head); + ListNode* leftHalf = head; + ListNode* rightHalf = middleOfLL->next; + middleOfLL->next = NULL; // this cuts off the right half from the left half + + // sort the two halves seperately and then merge them into one + leftHalf = sortList(leftHalf); + rightHalf = sortList(rightHalf); + head = merge(leftHalf, rightHalf); + + return head; + } +}; \ No newline at end of file diff --git a/cpp/0149-max-points-on-a-line.cpp b/cpp/0149-max-points-on-a-line.cpp new file mode 100644 index 000000000..413f6a396 --- /dev/null +++ b/cpp/0149-max-points-on-a-line.cpp @@ -0,0 +1,21 @@ +class Solution { +public: + int maxPoints(vector>& points) { + int res = 1; + for (int i=0; i count; + for (int j=i+1; j& p1, vector& p2) { + if ((p2[0] - p1[0]) == 0) + return INT_MAX; // aka edge case to handle a slope of infinity + return (float) (p2[1] - p1[1]) / (float) (p2[0] - p1[0]); + } +}; \ No newline at end of file diff --git a/cpp/0150-evaluate-reverse-polish-notation.cpp b/cpp/0150-evaluate-reverse-polish-notation.cpp new file mode 100644 index 000000000..5f86685df --- /dev/null +++ b/cpp/0150-evaluate-reverse-polish-notation.cpp @@ -0,0 +1,43 @@ +/* + Evaluate RPN, valid operators: +, -, *, / + + Stack, if num push, if operator apply to top 2 nums + + Time: O(n) + Space: O(n) +*/ + +class Solution { +public: + int evalRPN(vector& tokens) { + stack stk; + + for (int i = 0; i < tokens.size(); i++) { + string token = tokens[i]; + + if (token.size() > 1 || isdigit(token[0])) { + stk.push(stoi(token)); + continue; + } + + int num2 = stk.top(); + stk.pop(); + int num1 = stk.top(); + stk.pop(); + + int result = 0; + if (token == "+") { + result = num1 + num2; + } else if (token == "-") { + result = num1 - num2; + } else if (token == "*") { + result = num1 * num2; + } else { + result = num1 / num2; + } + stk.push(result); + } + + return stk.top(); + } +}; diff --git a/cpp/0152-maximum-product-subarray.cpp b/cpp/0152-maximum-product-subarray.cpp new file mode 100644 index 000000000..b6bf43bc3 --- /dev/null +++ b/cpp/0152-maximum-product-subarray.cpp @@ -0,0 +1,19 @@ +class Solution { +public: + int maxProduct(vector& nums) { + int res = nums[0]; + int curMin = 1, curMax = 1; + + for(int i = 0; i < nums.size(); i++) + { + int n = nums[i]; + + int tmp = curMax * n; + curMax = max(max(n * curMax, n * curMin), n); + curMin = min(min(tmp, n * curMin), n); + res = max(res, curMax); + } + + return res; + } +}; diff --git a/cpp/0153-find-minimum-in-rotated-sorted-array.cpp b/cpp/0153-find-minimum-in-rotated-sorted-array.cpp new file mode 100644 index 000000000..08bef6139 --- /dev/null +++ b/cpp/0153-find-minimum-in-rotated-sorted-array.cpp @@ -0,0 +1,26 @@ +/* + * @lc app=leetcode id=153 lang=cpp + * + * [153] Find Minimum in Rotated Sorted Array + */ + +// @lc code=start +class Solution { +public: + int findMin(vector &nums) { + int l = 0; + int r = nums.size() - 1; + int res = nums[0]; + while (l <= r) { + int mid = (r - l) / 2 + l; + if (nums[mid] >= res) { + l = mid + 1; + } else { + r = mid - 1; + } + res = std::min(nums[mid], res); + } + return res; + } +}; +// @lc code=end diff --git a/cpp/0155-min-stack.cpp b/cpp/0155-min-stack.cpp new file mode 100644 index 000000000..dfe073550 --- /dev/null +++ b/cpp/0155-min-stack.cpp @@ -0,0 +1,55 @@ +/* + Design stack that supports push, pop, top, & retriving min element + + 2 stacks, 1 normal & 1 monotonic decr, only push if lower than top + + Time: O(1) + Space: O(n) +*/ + +class MinStack { +public: + MinStack() { + + } + + void push(int val) { + stk.push(val); + + if (minStk.empty() || val < minStk.top().first) { + minStk.push({val, 1}); + } else if (val == minStk.top().first) { + minStk.top().second++; + } + } + + void pop() { + if (stk.top() == minStk.top().first) { + minStk.top().second--; + if (minStk.top().second == 0) { + minStk.pop(); + } + } + stk.pop(); + } + + int top() { + return stk.top(); + } + + int getMin() { + return minStk.top().first; + } +private: + stack stk; + stack> minStk; +}; + +/** + * Your MinStack object will be instantiated and called as such: + * MinStack* obj = new MinStack(); + * obj->push(val); + * obj->pop(); + * int param_3 = obj->top(); + * int param_4 = obj->getMin(); + */ diff --git a/cpp/0160-intersection-of-two-linked-lists.cpp b/cpp/0160-intersection-of-two-linked-lists.cpp new file mode 100644 index 000000000..1c274c49f --- /dev/null +++ b/cpp/0160-intersection-of-two-linked-lists.cpp @@ -0,0 +1,21 @@ +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ + +class Solution { +public: + ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) { + ListNode *trevA = headA, *trevB = headB; + + while (trevA != trevB) { + trevA = (trevA != NULL) ? trevA->next : headB; + trevB = (trevB != NULL) ? trevB->next : headA; + } + return trevA; + } +}; diff --git a/cpp/0162-find-peak-element.cpp b/cpp/0162-find-peak-element.cpp new file mode 100644 index 000000000..7ae9f57c8 --- /dev/null +++ b/cpp/0162-find-peak-element.cpp @@ -0,0 +1,27 @@ +// Time: O(logN) +// Space: O(1) + +class Solution { +public: + int findPeakElement(vector& nums) { + int n = nums.size(); + + if(n == 1) return 0; + + int left = 0, right = n - 1; + while(left <= right) { + int mid = left + (right-left)/2; + + if(mid > 0 && nums[mid] < nums[mid-1]) { + right = mid - 1; + } + else if(mid < n-1 && nums[mid] < nums[mid+1]) { + left = mid + 1; + } + else { + return mid; + } + } + return -1; + } +}; diff --git a/cpp/0167-two-sum-ii-input-array-is-sorted.cpp b/cpp/0167-two-sum-ii-input-array-is-sorted.cpp new file mode 100644 index 000000000..10992b30d --- /dev/null +++ b/cpp/0167-two-sum-ii-input-array-is-sorted.cpp @@ -0,0 +1,34 @@ +/* + Given a 1-indexed sorted int array & target: + Return indices (added by 1) of 2 nums that add to target + + 2 pointers, outside in, iterate i/j if sum is too low/high + + Time: O(n) + Space: O(1) +*/ + +class Solution { +public: + vector twoSum(vector& numbers, int target) { + int i = 0; + int j = numbers.size() - 1; + + vector result; + + while (i < j) { + int sum = numbers[i] + numbers[j]; + if (sum < target) { + i++; + } else if (sum > target) { + j--; + } else { + result.push_back(i + 1); + result.push_back(j + 1); + break; + } + } + + return result; + } +}; diff --git a/cpp/0169-majority-element.cpp b/cpp/0169-majority-element.cpp new file mode 100644 index 000000000..b8445788e --- /dev/null +++ b/cpp/0169-majority-element.cpp @@ -0,0 +1,22 @@ +/* +Given an array nums of size n, return the majority element. + +The majority element is the element that appears more than ⌊n / 2⌋ times. You may assume that the majority element always exists in the array. +*/ + +class Solution { +public: + int majorityElement(vector& nums) { + int count = 0; + int res = 0; + + for (const int& num: nums) { + if (count == 0) { + res = num; + } + count += (num == res) ? 1 : -1; + } + + return res; + } +}; diff --git a/cpp/0179-largest-number.cpp b/cpp/0179-largest-number.cpp new file mode 100644 index 000000000..12921e449 --- /dev/null +++ b/cpp/0179-largest-number.cpp @@ -0,0 +1,29 @@ +class Solution { +public: + + // to check which should come first + // see that by adding in which way gives bigger number + static bool mysort(string a, string b){ + return a+b > b+a; + } + string largestNumber(vector& nums) { + string s = ""; + vector all_numbers; + + // convert every number to string + for(int it: nums){ + all_numbers.push_back(to_string(it)); + } + + // sort accoring to custom sort function + sort(all_numbers.begin(),all_numbers.end(),mysort); + if(all_numbers[0]=="0"){ + return "0"; + } + + for(string a: all_numbers){ + s += a; + } + return s; + } +}; \ No newline at end of file diff --git a/cpp/0187-repeated-dna-sequences.cpp b/cpp/0187-repeated-dna-sequences.cpp new file mode 100644 index 000000000..9120156f8 --- /dev/null +++ b/cpp/0187-repeated-dna-sequences.cpp @@ -0,0 +1,21 @@ +class Solution { +public: + vector findRepeatedDnaSequences(string s) { + + int n = s.size(); + if(n <=10){ return {};} + vector answer; + + unordered_map hash; + + for(int i = 0;i<=s.size()-10;i++){ + string ss = s.substr(i,10); + hash[ss]++; + if(hash[ss]==2){ + answer.push_back(ss); + } + } + + return answer; + } +}; \ No newline at end of file diff --git a/cpp/0189-rotate-array.cpp b/cpp/0189-rotate-array.cpp new file mode 100644 index 000000000..5e411d955 --- /dev/null +++ b/cpp/0189-rotate-array.cpp @@ -0,0 +1,23 @@ +/* + Given an array, rotate the array to the right by k steps, where k is non-negative. + Ex. + Input: nums = [1,2,3,4,5,6,7], k = 3 + Output: [5,6,7,1,2,3,4] + + 1.- To avoid problems with the size of the vector we use the remainder of a division. + 2.- Reverse the entire vector. + 3.- Reverse the parts you want to obtain the result. + + Time: O(1) + Space: O(1) +*/ + +class Solution { +public: + void rotate(vector& nums, int k) { + k %= nums.size(); + reverse(nums.begin(), nums.end()); + reverse(nums.begin(), nums.begin() + k); + reverse(nums.begin() + k, nums.end()); + } +}; diff --git a/cpp/0190-reverse-bits.cpp b/cpp/0190-reverse-bits.cpp new file mode 100644 index 000000000..3eb0e05d7 --- /dev/null +++ b/cpp/0190-reverse-bits.cpp @@ -0,0 +1,24 @@ +/* + Reverse bits of a given integer + Ex. n = 10011100 -> 00111001 = 57 + + Shift into result & shift out of n + + Time: O(1) + Space: O(1) +*/ + +class Solution { +public: + uint32_t reverseBits(uint32_t n) { + uint32_t result = 0; + + for (int i = 0; i < 32; i++) { + result <<= 1; + result |= n & 1; + n >>= 1; + } + + return result; + } +}; diff --git a/cpp/0191-number-of-1-bits.cpp b/cpp/0191-number-of-1-bits.cpp new file mode 100644 index 000000000..146c3d990 --- /dev/null +++ b/cpp/0191-number-of-1-bits.cpp @@ -0,0 +1,44 @@ +/* + Return number of '1' bits in an int + Ex. n = 00001011 -> 3 + + Simply count bit-by-bit & shift it off + + Time: O(1) + Space: O(1) +*/ + +class Solution { +public: + int hammingWeight(uint32_t n) { + int bit = 0; + int result = 0; + + while (n != 0) { + bit = n & 1; + if (bit == 1) { + result++; + } + n = n >> 1; + } + + return result; + } +}; + + +/* use kernighan's algorithm to only iterate num(set bits) times */ +class Solution { +public: + int hammingWeight(uint32_t n) { + unsigned int count = 0; + + while(n) { + ++count; + // unset rightmost set bit + n = (n & (n - 1)); + } + + return count; + } +}; \ No newline at end of file diff --git a/cpp/0198-house-robber.cpp b/cpp/0198-house-robber.cpp new file mode 100644 index 000000000..e7c3c9030 --- /dev/null +++ b/cpp/0198-house-robber.cpp @@ -0,0 +1,27 @@ +/* + Given int array, return max amount can rob (can't rob adjacent houses) + Ex. nums = [1,2,3,1] -> 4, rob house 1 then house 3: 1 + 3 = 4 + + Recursion w/ memoization -> DP, rob either 2 away + here, or 1 away + Recurrence relation: robFrom[i] = max(robFrom[i-2] + nums[i], robFrom[i-1]) + + Time: O(n) + Space: O(1) +*/ + +class Solution { +public: + int rob(vector& nums) { + int prev = 0; + int curr = 0; + int next = 0; + + for (int i = 0; i < nums.size(); i++) { + next = max(prev + nums[i], curr); + prev = curr; + curr = next; + } + + return curr; + } +}; diff --git a/cpp/0199-binary-tree-right-side-view.cpp b/cpp/0199-binary-tree-right-side-view.cpp new file mode 100644 index 000000000..2b68815ab --- /dev/null +++ b/cpp/0199-binary-tree-right-side-view.cpp @@ -0,0 +1,55 @@ +/* + Given root of binary tree, return values that can only be seen from the right side + + BFS traversal, push right first before left, store only first value + + Time: O(n) + Space: O(n) +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector rightSideView(TreeNode* root) { + if (root == NULL) { + return {}; + } + + queue q; + q.push(root); + + vector result; + + while (!q.empty()) { + int count = q.size(); + + for (int i = count; i > 0; i--) { + TreeNode* node = q.front(); + q.pop(); + + if (i == count) { + result.push_back(node->val); + } + + if (node->right != NULL) { + q.push(node->right); + } + if (node->left != NULL) { + q.push(node->left); + } + } + } + + return result; + } +}; diff --git a/cpp/0200-number-of-islands.cpp b/cpp/0200-number-of-islands.cpp new file mode 100644 index 000000000..6a1df18ac --- /dev/null +++ b/cpp/0200-number-of-islands.cpp @@ -0,0 +1,41 @@ +/* + Given grid where '1' is land & '0' is water, return # of islands + + DFS, set visited land to '0' to not visit it again, count islands + + Time: O(m x n) + Space: O(m x n) +*/ + +class Solution { +public: + int numIslands(vector>& grid) { + int m = grid.size(); + int n = grid[0].size(); + + int result = 0; + + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == '1') { + dfs(grid, i, j, m, n); + result++; + } + } + } + + return result; + } +private: + void dfs(vector>& grid, int i, int j, int m, int n) { + if (i < 0 || i >= m || j < 0 || j >= n || grid[i][j] == '0') { + return; + } + grid[i][j] = '0'; + + dfs(grid, i - 1, j, m, n); + dfs(grid, i + 1, j, m, n); + dfs(grid, i, j - 1, m, n); + dfs(grid, i, j + 1, m, n); + } +}; diff --git a/cpp/0202-happy-number.cpp b/cpp/0202-happy-number.cpp new file mode 100644 index 000000000..a0c78b1dc --- /dev/null +++ b/cpp/0202-happy-number.cpp @@ -0,0 +1,39 @@ +/* + Given num, replace by sum of squares of its digits + Repeat until 1 or endless loop, determine if ends in 1 + Ex. n = 19 -> true, 1^2 + 9^2 = 82, 8^2 + 2^2 = 68 ... 1 + + Detect cycle w/ slow/fast pointer technique + If happy will eventually be 1, else pointers will meet + + Time: O(log n) + Space: O(1) +*/ + +class Solution { +public: + bool isHappy(int n) { + int slow = n; + int fast = getNext(n); + + while (slow != fast && fast != 1) { + slow = getNext(slow); + fast = getNext(getNext(fast)); + } + + if (fast == 1) { + return true; + } + return false; + } +private: + int getNext(int n) { + int sum = 0; + while (n > 0) { + int digit = n % 10; + n /= 10; + sum += pow(digit, 2); + } + return sum; + } +}; diff --git a/cpp/0203-remove-linked-list-elements.cpp b/cpp/0203-remove-linked-list-elements.cpp new file mode 100644 index 000000000..d68dea846 --- /dev/null +++ b/cpp/0203-remove-linked-list-elements.cpp @@ -0,0 +1,19 @@ +class Solution { +public: + ListNode* removeElements(ListNode* head, int val) { + ListNode *dummy = new ListNode(0, head); + ListNode *prev = dummy, *curr = head; + + while(curr) { + ListNode *nxt = curr->next; + + if(curr->val == val) + prev->next = nxt; + else + prev = curr; + + curr = nxt; + } + return dummy->next; + } +}; diff --git a/cpp/0205-Isomorphic-Strings.cpp b/cpp/0205-Isomorphic-Strings.cpp new file mode 100644 index 000000000..1059107b3 --- /dev/null +++ b/cpp/0205-Isomorphic-Strings.cpp @@ -0,0 +1,23 @@ +/*Given two strings s and t, determine if they are isomorphic. + +Two strings s and t are isomorphic if the characters in s can be replaced to get t. + +All occurrences of a character must be replaced with another character while preserving the order of characters. No two characters may map to the same character, but a character may map to itself. + + */ +class Solution { +public: + bool isIsomorphic(string s, string t) { + unordered_map>m1; + unordered_map>m2; + for(int i=0;i>m1; + unordered_map>m2; + for(int i=0;i [5,4,3,2,1], head = [1,2] -> [2,1] + + Maintain prev, curr pointers, iterate thru & reverse + + Time: O(n) + Space: O(1) +*/ + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution +{ +public: + ListNode *reverseList(ListNode *head) + { + if (head == NULL || head->next == NULL) + return head; + + ListNode *prev = NULL; + ListNode *curr = head; + + while (curr != NULL) + { + ListNode *temp = curr->next; + curr->next = prev; + prev = curr; + curr = temp; + } + return prev; + } +}; diff --git a/cpp/0207-course-schedule.cpp b/cpp/0207-course-schedule.cpp new file mode 100644 index 000000000..3df974445 --- /dev/null +++ b/cpp/0207-course-schedule.cpp @@ -0,0 +1,48 @@ +/* + Courses & prerequisites, return true if can finish all courses + Ex. numCourses = 2, prerequisites = [[1,0]] -> true + + All courses can be completed if there's no cycle (visit already visited) + + Time: O(V + E) + Space: O(V + E) +*/ + +class Solution { +public: + bool canFinish(int numCourses, vector>& prerequisites) { + // map each course to prereq list + unordered_map> m; + for (int i = 0; i < prerequisites.size(); i++) { + m[prerequisites[i][0]].push_back(prerequisites[i][1]); + } + // all courses along current DFS path + unordered_set visited; + + for (int course = 0; course < numCourses; course++) { + if (!dfs(course, m, visited)) { + return false; + } + } + return true; + } +private: + bool dfs(int course, unordered_map>& m, unordered_set& visited) { + if (visited.find(course) != visited.end()) { + return false; + } + if (m[course].empty()) { + return true; + } + visited.insert(course); + for (int i = 0; i < m[course].size(); i++) { + int nextCourse = m[course][i]; + if (!dfs(nextCourse, m, visited)) { + return false; + } + } + m[course].clear(); + visited.erase(course); + return true; + } +}; diff --git a/cpp/0208-implement-trie-prefix-tree.cpp b/cpp/0208-implement-trie-prefix-tree.cpp new file mode 100644 index 000000000..cc8cbefa9 --- /dev/null +++ b/cpp/0208-implement-trie-prefix-tree.cpp @@ -0,0 +1,83 @@ +/* + Implement trie (store/retrieve keys in dataset of strings) + + Each node contains pointer to next letter & is word flag + + Time: O(n) insert, O(n) search, O(n) startsWith + Space: O(n) insert, O(1) search, O(1) startsWith +*/ + +class TrieNode { +public: + TrieNode* children[26]; + bool isWord; + + TrieNode() { + for (int i = 0; i < 26; i++) { + children[i] = NULL; + } + isWord = false; + } +}; + +class Trie { +public: + Trie() { + root = new TrieNode(); + } + + void insert(string word) { + TrieNode* node = root; + int curr = 0; + + for (int i = 0; i < word.size(); i++) { + curr = word[i] - 'a'; + if (node->children[curr] == NULL) { + node->children[curr] = new TrieNode(); + } + node = node->children[curr]; + } + + node->isWord = true; + } + + bool search(string word) { + TrieNode* node = root; + int curr = 0; + + for (int i = 0; i < word.size(); i++) { + curr = word[i] - 'a'; + if (node->children[curr] == NULL) { + return false; + } + node = node->children[curr]; + } + + return node->isWord; + } + + bool startsWith(string prefix) { + TrieNode* node = root; + int curr = 0; + + for (int i = 0; i < prefix.size(); i++) { + curr = prefix[i] - 'a'; + if (node->children[curr] == NULL) { + return false; + } + node = node->children[curr]; + } + + return true; + } +private: + TrieNode* root; +}; + +/** + * Your Trie object will be instantiated and called as such: + * Trie* obj = new Trie(); + * obj->insert(word); + * bool param_2 = obj->search(word); + * bool param_3 = obj->startsWith(prefix); + */ diff --git a/cpp/0209-minimum-size-subarray-sum.cpp b/cpp/0209-minimum-size-subarray-sum.cpp new file mode 100644 index 000000000..994074132 --- /dev/null +++ b/cpp/0209-minimum-size-subarray-sum.cpp @@ -0,0 +1,37 @@ +/* Given an array of positive integers nums and a positive integer target, +return the minimal length of a contiguous subarray [numsl, numsl+1, ..., numsr-1, numsr] +of which the sum is greater than or equal to target. +If there is no such subarray, return 0 instead. +Ex.: target = 7, nums = [2,3,1,2,4,3] -> 2 + target = 4, nums = [1,4,4] -> 1 +Sliding window (with two pointer). Keep adding elements to the SL. When Sum => target or SP points to end of the vector resize the SW. */ + +class Solution{ + public: + int minSubArrayLen(int target, vector& nums){ + int min; + int fp, sp; + int sum; + fp = 0; + sp = 1; + sum = nums[0]; + min = nums.size() + 1; + while(fp != sp){ + if(Sum >= target){ + min = min(sp - fp, min); + sum = Sìsum - nums[fp]; + fp++; + } + else{ + if(sp < nums.size()){ + sum = sum + nums[sp]; + sp++; + } + else{ + fp++; + } + } + } + return min; + } +}; diff --git a/cpp/0210-course-schedule-ii.cpp b/cpp/0210-course-schedule-ii.cpp new file mode 100644 index 000000000..763d68a08 --- /dev/null +++ b/cpp/0210-course-schedule-ii.cpp @@ -0,0 +1,56 @@ +/* + Courses & prerequisites, return ordering of courses to take to finish all courses + Ex. numCourses = 2, prerequisites = [[1,0]] -> [0,1], take course 0 then 1 + + All courses can be completed if there's no cycle, check for cycles + + Time: O(V + E) + Space: O(V + E) +*/ + +class Solution { +public: + vector findOrder(int numCourses, vector>& prerequisites) { + unordered_map> m; + // build adjacency list of prereqs + for (int i = 0; i < prerequisites.size(); i++) { + m[prerequisites[i][0]].push_back(prerequisites[i][1]); + } + unordered_set visit; + unordered_set cycle; + + vector result; + for (int course = 0; course < numCourses; course++) { + if (!dfs(course, m, visit, cycle, result)) { + return {}; + } + } + return result; + } +private: + // a course has 3 possible states: + // visited -> course added to result + // visiting -> course not added to result, but added to cycle + // unvisited -> course not added to result or cycle + bool dfs(int course, unordered_map>& m, unordered_set& visit, + unordered_set& cycle, vector& result) { + + if (cycle.find(course) != cycle.end()) { + return false; + } + if (visit.find(course) != visit.end()) { + return true; + } + cycle.insert(course); + for (int i = 0; i < m[course].size(); i++) { + int nextCourse = m[course][i]; + if (!dfs(nextCourse, m, visit, cycle, result)) { + return false; + } + } + cycle.erase(course); + visit.insert(course); + result.push_back(course); + return true; + } +}; diff --git a/cpp/0211-design-add-and-search-words-data-structure.cpp b/cpp/0211-design-add-and-search-words-data-structure.cpp new file mode 100644 index 000000000..71a1d4def --- /dev/null +++ b/cpp/0211-design-add-and-search-words-data-structure.cpp @@ -0,0 +1,75 @@ +/* + Design add & search words data structure + + Implement trie, handle wildcards: traverse all children & search substrings + + Time: O(m x 26^n) -> m = # of words, n = length of words + Space: O(n) +*/ + +class TrieNode { +public: + TrieNode* children[26]; + bool isWord; + + TrieNode() { + for (int i = 0; i < 26; i++) { + children[i] = NULL; + } + isWord = false; + } +}; + +class WordDictionary { +public: + WordDictionary() { + root = new TrieNode(); + } + + void addWord(string word) { + TrieNode* node = root; + int curr = 0; + + for (int i = 0; i < word.size(); i++) { + curr = word[i] - 'a'; + if (node->children[curr] == NULL) { + node->children[curr] = new TrieNode(); + } + node = node->children[curr]; + } + + node->isWord = true; + } + + bool search(string word) { + TrieNode* node = root; + return searchInNode(word, 0, node); + } +private: + TrieNode* root; + + bool searchInNode(string& word, int i, TrieNode* node) { + if (node == NULL) { + return false; + } + if (i == word.size()) { + return node->isWord; + } + if (word[i] != '.') { + return searchInNode(word, i + 1, node->children[word[i] - 'a']); + } + for (int j = 0; j < 26; j++) { + if (searchInNode(word, i + 1, node->children[j])) { + return true; + } + } + return false; + } +}; + +/** + * Your WordDictionary object will be instantiated and called as such: + * WordDictionary* obj = new WordDictionary(); + * obj->addWord(word); + * bool param_2 = obj->search(word); + */ diff --git a/cpp/0212-word-search-ii.cpp b/cpp/0212-word-search-ii.cpp new file mode 100644 index 000000000..1c23850c5 --- /dev/null +++ b/cpp/0212-word-search-ii.cpp @@ -0,0 +1,89 @@ +/* + Given a board of characters & a list of words, return all words on the board + + Implement trie, for search: iterate thru children until isWord, add to result + + Time: O(m x (4 x 3^(l - 1))) -> m = # of cells, l = max length of words + Space: O(n) -> n = total number of letters in dictionary (no overlap in Trie) +*/ + +class TrieNode { +public: + TrieNode* children[26]; + bool isWord; + + TrieNode() { + for (int i = 0; i < 26; i++) { + children[i] = NULL; + } + isWord = false; + } +}; + +class Solution { +public: + vector findWords(vector>& board, vector& words) { + for (int i = 0; i < words.size(); i++) { + insert(words[i]); + } + + int m = board.size(); + int n = board[0].size(); + + TrieNode* node = root; + vector result; + + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + search(board, i, j, m, n, node, "", result); + } + } + + return result; + } +private: + TrieNode* root = new TrieNode(); + + void insert(string word) { + TrieNode* node = root; + int curr = 0; + + for (int i = 0; i < word.size(); i++) { + curr = word[i] - 'a'; + if (node->children[curr] == NULL) { + node->children[curr] = new TrieNode(); + } + node = node->children[curr]; + } + + node->isWord = true; + } + + void search(vector>& board, int i, int j, int m, int n, TrieNode* node, string word, vector& result) { + if (i < 0 || i >= m || j < 0 || j >= n || board[i][j] == '#') { + return; + } + + char c = board[i][j]; + + node = node->children[c - 'a']; + if (node == NULL) { + return; + } + + word += board[i][j]; + if (node->isWord) { + result.push_back(word); + node->isWord = false; + } + + board[i][j] = '#'; + + search(board, i - 1, j, m, n, node, word, result); + search(board, i + 1, j, m, n, node, word, result); + search(board, i, j - 1, m, n, node, word, result); + search(board, i, j + 1, m, n, node, word, result); + + board[i][j] = c; + } +}; diff --git a/cpp/0213-house-robber-ii.cpp b/cpp/0213-house-robber-ii.cpp new file mode 100644 index 000000000..33998c157 --- /dev/null +++ b/cpp/0213-house-robber-ii.cpp @@ -0,0 +1,40 @@ +/* + Given int array in a circle, return max amount can rob (can't rob adj houses) + Ex. nums = [2,3,2] -> 3, can't rob house 1 & 3 b/c circular adj, so rob 2 + + Recursion w/ memo -> DP, rob either 2 away + here, or 1 away, try both ranges + Recurrence relation: robFrom[i] = max(robFrom[i-2] + nums[i], robFrom[i-1]) + + Time: O(n) + Space: O(1) +*/ + +class Solution { +public: + int rob(vector& nums) { + int n = nums.size(); + + if (n == 1) { + return nums[0]; + } + + int range1 = robber(nums, 0, n - 2); + int range2 = robber(nums, 1, n - 1); + + return max(range1, range2); + } +private: + int robber(vector& nums, int start, int end) { + int prev = 0; + int curr = 0; + int next = 0; + + for (int i = start; i <= end; i++) { + next = max(prev + nums[i], curr); + prev = curr; + curr = next; + } + + return curr; + } +}; diff --git a/cpp/0215-kth-largest-element-in-an-array.cpp b/cpp/0215-kth-largest-element-in-an-array.cpp new file mode 100644 index 000000000..089811ea4 --- /dev/null +++ b/cpp/0215-kth-largest-element-in-an-array.cpp @@ -0,0 +1,112 @@ +/* + Given array and int k, return kth largest element in array + Ex. nums = [3,2,1,5,6,4], k = 2 -> 5 + + Quickselect, partition until pivot = k, left side all > k + + Time: O(n) -> optimized from O(n log k) min heap solution + Space: O(1) +*/ + +// class Solution { +// public: +// int findKthLargest(vector& nums, int k) { +// priority_queue, greater> pq; +// for (int i = 0; i < nums.size(); i++) { +// pq.push(nums[i]); +// if (pq.size() > k) { +// pq.pop(); +// } +// } +// return pq.top(); +// } +// }; + +/* +class Solution { +public: + int findKthLargest(vector& nums, int k) { + int low = 0; + int high = nums.size() - 1; + int pivotIndex = nums.size(); + + while (pivotIndex != k - 1) { + pivotIndex = partition(nums, low, high); + if (pivotIndex < k - 1) { + low = pivotIndex + 1; + } else { + high = pivotIndex - 1; + } + } + + return nums[k - 1]; + } +private: + int partition(vector& nums, int low, int high) { + int pivot = nums[low]; + + int i = low + 1; + int j = high; + + while (i <= j) { + if (nums[i] < pivot && pivot < nums[j]) { + swap(nums[i], nums[j]); + i++; + j--; + } + if (nums[i] >= pivot) { + i++; + } + if (pivot >= nums[j]) { + j--; + } + } + + swap(nums[low], nums[j]); + return j; + } +}; +*/ + +// Video's QuickSelect implementation +// class Solution { +// public: +// int findKthLargest(vector& nums, int k) { +// int index = nums.size() - k; +// return quickSelect(nums, index, 0, nums.size() - 1); +// } +// private: +// int quickSelect(vector& nums, int k, int l, int r){ +// int pivot = nums[r]; +// int p_pos = l; +// for (int i = l; i < r; ++i){ +// if (nums[i] <= pivot){ +// swap(nums[i], nums[p_pos]); +// ++p_pos; +// } +// } +// swap(nums[p_pos], nums[r]); +// if (k < p_pos) +// return quickSelect(nums, k, l, p_pos - 1); +// if (k > p_pos) +// return quickSelect(nums, k, p_pos + 1, r); +// return nums[p_pos]; +// } +// }; + +// Solution that can pass current tests +class Solution { +public: + int findKthLargest(vector& nums, int k) { + int pivot = nums[rand() % nums.size()]; + vector left, mid, right; + for (auto num: nums) { + if (num > pivot) left.push_back(num); + else if (num == pivot) mid.push_back(num); + else right.push_back(num); + } + if (k <= left.size()) return findKthLargest(left, k); + else if (k <= left.size() + mid.size()) return mid[0]; + else return findKthLargest(right, k - left.size() - mid.size()); + } +}; diff --git a/cpp/0217-contains-duplicate.cpp b/cpp/0217-contains-duplicate.cpp new file mode 100644 index 000000000..94ed51831 --- /dev/null +++ b/cpp/0217-contains-duplicate.cpp @@ -0,0 +1,25 @@ +/* + Given int array, return true if any value appears at least twice + Ex. nums = [1,2,3,1] -> true, nums = [1,2,3,4] -> false + + If the number has been seen previously, then it has a duplicate. Otherwise, insert it into the hash set. + + Time: O(n) + Space: O(n) +*/ + +class Solution { +public: + bool containsDuplicate(vector& nums) { + unordered_set s; + + for (int i = 0; i < nums.size(); i++) { + if (s.find(nums[i]) != s.end()) { + return true; + } + s.insert(nums[i]); + } + + return false; + } +}; diff --git a/cpp/0219-contains-duplicate-ii.cpp b/cpp/0219-contains-duplicate-ii.cpp new file mode 100644 index 000000000..51dd1e417 --- /dev/null +++ b/cpp/0219-contains-duplicate-ii.cpp @@ -0,0 +1,15 @@ +class Solution { +public: + bool containsNearbyDuplicate(vector& nums, int k) { + unordered_map number_map; + for (int i = 0; i < nums.size(); ++i) { + int num = nums[i]; + if (number_map.find(num) != number_map.end() && i - number_map[num] <= k) { + return true; + }else { + number_map[num] = i; + } + } + return false; + } +}; \ No newline at end of file diff --git a/cpp/0221-maximal-square.cpp b/cpp/0221-maximal-square.cpp new file mode 100644 index 000000000..6f78b2913 --- /dev/null +++ b/cpp/0221-maximal-square.cpp @@ -0,0 +1,33 @@ +/* + Given an m x n binary matrix filled with 0's and 1's, find the largest square containing only 1's and return its area. + + Ex. Input: matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]] + Output: 4 + + Time : O(m*n) + Space : O(m*n) +*/ + +class Solution { +public: + int maximalSquare(vector>& matrix) { + int rows = matrix.size(), cols = matrix[0].size(); + + vector> dp (rows+1, vector(cols+1, 0)); + int maxi = 0; + for(int i = rows-1 ; i >= 0; --i) { + for(int j = cols-1 ; j >=0 ; --j) { + if(matrix[i][j] == '1') { + int right = dp[i][j+1], dia = dp[i+1][j+1], bottom = dp[i+1][j]; + + dp[i][j] = 1 + min(right, min(dia, bottom)); + maxi = max(maxi, dp[i][j]); + } + else { + dp[i][j] = 0; + } + } + } + return maxi*maxi; + } +}; diff --git a/cpp/0225-implement-stack-using-queues.cpp b/cpp/0225-implement-stack-using-queues.cpp new file mode 100644 index 000000000..b195b6f39 --- /dev/null +++ b/cpp/0225-implement-stack-using-queues.cpp @@ -0,0 +1,64 @@ +/* +Implement a last-in-first-out (LIFO) stack using only two queues. The implemented stack should support all the functions of a normal stack (push, top, pop, and empty). + +Implement the MyStack class: + +void push(int x) Pushes element x to the top of the stack. +int pop() Removes the element on the top of the stack and returns it. +int top() Returns the element on the top of the stack. +boolean empty() Returns true if the stack is empty, false otherwise. +Notes: + +You must use only standard operations of a queue, which means that only push to back, peek/pop from front, size and is empty operations are valid. +Depending on your language, the queue may not be supported natively. You may simulate a queue using a list or deque (double-ended queue) as long as you use only a queue's standard operations. + +*/ +/** + * Your MyStack object will be instantiated and called as such: + * MyStack* obj = new MyStack(); + * obj->push(x); + * int param_2 = obj->pop(); + * int param_3 = obj->top(); + * bool param_4 = obj->empty(); + */ +class MyStack { + queueq1; + queueq2; +public: + MyStack() { + + } + + void push(int x) { + q1.push(x); + } + + int pop() { + while(q1.size()!=1){ + q2.push(q1.front()); + q1.pop(); + } + int x=q1.front(); + q1.pop(); + swap(q1,q2); + return x; + + } + + int top() { + while(q1.size()!=1){ + q2.push(q1.front()); + q1.pop(); + } + int x=q1.front(); + q1.pop(); + swap(q1,q2); + q1.push(x); + return x; + + } + bool empty() { + return(q1.empty() && q2.empty()); + + } +}; diff --git a/cpp/0226-invert-binary-tree.cpp b/cpp/0226-invert-binary-tree.cpp new file mode 100644 index 000000000..266b934e8 --- /dev/null +++ b/cpp/0226-invert-binary-tree.cpp @@ -0,0 +1,33 @@ +/* + Given the root of a binary tree, invert the tree, and return its root + Ex. root = [4,2,7,1,3,6,9] -> [4,7,2,9,6,3,1], [2,1,3] -> [2,3,1] + + Preorder traversal, at each node, swap it's left and right children + + Time: O(n) + Space: O(n) +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* invertTree(TreeNode* root) { + if (root == NULL) { + return NULL; + } + swap(root->left, root->right); + invertTree(root->left); + invertTree(root->right); + return root; + } +}; diff --git a/cpp/0230-kth-smallest-element-in-a-bst.cpp b/cpp/0230-kth-smallest-element-in-a-bst.cpp new file mode 100644 index 000000000..1a8f97f3d --- /dev/null +++ b/cpp/0230-kth-smallest-element-in-a-bst.cpp @@ -0,0 +1,42 @@ +/* + Given root of BST & int k, return kth smallest value (1-indexed) of all values in tree + Ex. root = [3,1,4,null,2] k = 1 -> 1, root = [5,3,6,2,4,null,null,1] k = 3 -> 3 + + Inorder traversal, each visit decrement k, when k = 0 return, works because inorder + + Time: O(n) + Space: O(n) +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int kthSmallest(TreeNode* root, int k) { + int result = 0; + inorder(root, k, result); + return result; + } +private: + void inorder(TreeNode* root, int& k, int& result) { + if (root == NULL) { + return; + } + inorder(root->left, k, result); + k--; + if (k == 0) { + result = root->val; + return; + } + inorder(root->right, k, result); + } +}; diff --git a/cpp/0231-power-of-two.cpp b/cpp/0231-power-of-two.cpp new file mode 100644 index 000000000..d26ba43fd --- /dev/null +++ b/cpp/0231-power-of-two.cpp @@ -0,0 +1,6 @@ +class Solution { +public: + bool isPowerOfTwo(int n) { + return n > 0 && !(n & (n - 1)); + } +}; \ No newline at end of file diff --git a/cpp/0234-palindrome-linked-list.cpp b/cpp/0234-palindrome-linked-list.cpp new file mode 100644 index 000000000..27a2a2e5b --- /dev/null +++ b/cpp/0234-palindrome-linked-list.cpp @@ -0,0 +1,16 @@ +class Solution{ + public: + bool isPalindrome(ListNode* head){ + vector v; + while(head != nullptr){ + v.push_back(head -> val); + head = head -> next; + } + for(int i = 0; i < v.size() / 2; i++){ + if(v[i] != v[v.size() - i - 1]){ + return false; + } + } + return true; + } +}; diff --git a/cpp/0235-lowest-common-ancestor-of-a-binary-search-tree.cpp b/cpp/0235-lowest-common-ancestor-of-a-binary-search-tree.cpp new file mode 100644 index 000000000..15883fd1e --- /dev/null +++ b/cpp/0235-lowest-common-ancestor-of-a-binary-search-tree.cpp @@ -0,0 +1,47 @@ +/* + Given a binary search tree (BST), find the LCA of 2 given nodes in the BST + + Use BST property: if curr > left & right go left, else if < go right, else done + + Time: O(n) + Space: O(n) +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode(int x) : val(x), left(NULL), right(NULL) {} + * }; + */ + +class Solution { +public: + TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { + if (p->val < root->val && q->val < root->val) { + return lowestCommonAncestor(root->left, p, q); + } else if (p->val > root->val && q->val > root->val) { + return lowestCommonAncestor(root->right, p, q); + } else { + return root; + } + } +}; + +// class Solution { +// public: +// TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { +// while (root != NULL) { +// if (p->val < root->val && q->val < root->val) { +// root = root->left; +// } else if (p->val > root->val && q->val > root->val) { +// root = root->right; +// } else { +// return root; +// } +// } +// return NULL; +// } +// }; diff --git a/cpp/0236-power-of-three.cpp b/cpp/0236-power-of-three.cpp new file mode 100644 index 000000000..f07e6f83a --- /dev/null +++ b/cpp/0236-power-of-three.cpp @@ -0,0 +1,15 @@ +class Solution{ + public: + bool isPowerOfThree(int n){ + if(n <= 0){ + return false; + } + while(n > 1){ + if(n % 3 != 0){ + return false; + } + n = n / 3; + } + return true; + } +}; diff --git a/cpp/0238-product-of-array-except-self.cpp b/cpp/0238-product-of-array-except-self.cpp new file mode 100644 index 000000000..4eac22fd1 --- /dev/null +++ b/cpp/0238-product-of-array-except-self.cpp @@ -0,0 +1,32 @@ +/* + Given an integer array nums, return an array such that: + answer[i] is equal to the product of all elements of nums except nums[i] + Ex. nums = [1,2,3,4] -> [24,12,8,6], nums = [-1,1,0,-3,3] -> [0,0,9,0,0] + + Calculate prefix products forward, then postfix backwards in a 2nd pass + + Time: O(n) + Space: O(1) +*/ + +class Solution { +public: + vector productExceptSelf(vector& nums) { + int n = nums.size(); + vector result(n, 1); + + int prefix = 1; + for (int i = 0; i < n; i++) { + result[i] = prefix; + prefix = prefix * nums[i]; + } + + int postfix = 1; + for (int i = n - 1; i >= 0; i--) { + result[i] = result[i] * postfix; + postfix = postfix * nums[i]; + } + + return result; + } +}; diff --git a/cpp/0239-sliding-window-maximum.cpp b/cpp/0239-sliding-window-maximum.cpp new file mode 100644 index 000000000..2201f51e7 --- /dev/null +++ b/cpp/0239-sliding-window-maximum.cpp @@ -0,0 +1,39 @@ +/* + Given int array & sliding window size k, return max sliding window + Ex. nums = [1,3,-1,-3,5,3,6,7] k = 3 -> [3,3,5,5,6,7] + + Sliding window deque, ensure monotonic decr, leftmost largest + + Time: O(n) + Space: O(n) +*/ + +class Solution { +public: + vector maxSlidingWindow(vector& nums, int k) { + deque dq; + vector result; + + int i = 0; + int j = 0; + + while (j < nums.size()) { + while (!dq.empty() && nums[dq.back()] < nums[j]) { + dq.pop_back(); + } + dq.push_back(j); + + if (i > dq.front()) { + dq.pop_front(); + } + + if (j + 1 >= k) { + result.push_back(nums[dq.front()]); + i++; + } + j++; + } + + return result; + } +}; diff --git a/cpp/0242-valid-anagram.cpp b/cpp/0242-valid-anagram.cpp new file mode 100644 index 000000000..d4c817c5a --- /dev/null +++ b/cpp/0242-valid-anagram.cpp @@ -0,0 +1,23 @@ +// hashmap solution, similar to neetcode python implementation + +class Solution { +public: + bool isAnagram(string s, string t) { + if(s.size() != t.size()) return false; + + unordered_map smap; + unordered_map tmap; + + for(int i = 0; i < s.size(); i++){ + smap[s[i]]++; + tmap[t[i]]++; + } + + for (auto const& [key, value] : smap) { + if (value != tmap[key]) { + return false; + } + } + return true; + } +}; \ No newline at end of file diff --git a/cpp/0252-meeting-rooms.cpp b/cpp/0252-meeting-rooms.cpp new file mode 100644 index 000000000..5689ecda6 --- /dev/null +++ b/cpp/0252-meeting-rooms.cpp @@ -0,0 +1,26 @@ +/* + Given array of time intervals, determine if can attend all meetings + Ex. intervals = [[0,30],[5,10],[15,20]] -> false + + Sort by start time, check adj meetings, if overlap return false + + Time: O(n log n) + Space: O(1) +*/ + +class Solution { +public: + bool canAttendMeetings(vector>& intervals) { + if (intervals.empty()) { + return true; + } + + sort(intervals.begin(), intervals.end()); + for (int i = 0; i < intervals.size() - 1; i++) { + if (intervals[i][1] > intervals[i + 1][0]) { + return false; + } + } + return true; + } +}; diff --git a/cpp/0253-meeting-rooms-ii.cpp b/cpp/0253-meeting-rooms-ii.cpp new file mode 100644 index 000000000..2c073f94a --- /dev/null +++ b/cpp/0253-meeting-rooms-ii.cpp @@ -0,0 +1,32 @@ +/* + Given array of time intervals, determine min # of meeting rooms required + Ex. intervals = [[0,30],[5,10],[15,20]] -> 2 + + Min heap for earliest end times, most overlap will be heap size + + Time: O(n log n) + Space: O(n) +*/ + +class Solution { +public: + int minMeetingRooms(vector>& intervals) { + // sort intervals by start time + sort(intervals.begin(), intervals.end()); + + // min heap to track min end time of merged intervals + priority_queue, greater> pq; + pq.push(intervals[0][1]); + + for (int i = 1; i < intervals.size(); i++) { + // compare curr start w/ earliest end time, if no overlap then pop + if (intervals[i][0] >= pq.top()) { + pq.pop(); + } + // add new room (will replace/be same size if above was true) + pq.push(intervals[i][1]); + } + + return pq.size(); + } +}; diff --git a/cpp/0261-graph-valid-tree.cpp b/cpp/0261-graph-valid-tree.cpp new file mode 100644 index 000000000..e5beeb304 --- /dev/null +++ b/cpp/0261-graph-valid-tree.cpp @@ -0,0 +1,49 @@ +/* + Graph of nodes, list of edges, determine if edges make valid tree + Ex. n = 5, edges = [[0,1],[0,2],[0,3],[1,4]] -> true + + (1) For graph to be a valid tree, must have exactly n - 1 edges + (2) If graph fully connected & has n - 1 edges, can't contain cycle + + Time: O(n) + Space: O(n) +*/ + +class Solution { +public: + bool validTree(int n, vector>& edges) { + vector> adj(n); + for (int i = 0; i < edges.size(); i++) { + vector edge = edges[i]; + adj[edge[0]].push_back(edge[1]); + adj[edge[1]].push_back(edge[0]); + } + + vector visited(n); + if (hasCycle(adj, visited, -1, 0)) { + return false; + } + + for (int i = 0; i < visited.size(); i++) { + if (!visited[i]) { + return false; + } + } + return true; + } +private: + bool hasCycle(vector>& adj, vector& visited, int parent, int child) { + if (visited[child]) { + return true; + } + visited[child] = true; + // checking for cycles and connectedness + for (int i = 0; i < adj[child].size(); i++) { + int curr = adj[child][i]; + if (curr != parent && hasCycle(adj, visited, child, curr)) { + return true; + } + } + return false; + } +}; diff --git a/cpp/0263-ugly-number.cpp b/cpp/0263-ugly-number.cpp new file mode 100644 index 000000000..99952f064 --- /dev/null +++ b/cpp/0263-ugly-number.cpp @@ -0,0 +1,12 @@ +class Solution { +public: + bool isUgly(int n) { + if(n <= 0) + return false; + + for(int p: {2, 3, 5}) + while(n % p == 0) + n = n / p; + return n == 1; + } +}; diff --git a/cpp/0268-missing-number.cpp b/cpp/0268-missing-number.cpp new file mode 100644 index 000000000..27233453b --- /dev/null +++ b/cpp/0268-missing-number.cpp @@ -0,0 +1,25 @@ +/* + Given array in range [0, n], return missing + Ex. nums = [3,0,1] -> 2, nums = [0,1] -> 2 + + Use the fact that XOR is its own inverse + Ex. [0,1,3,4] + Missing = 4^(0^0)^(1^1)^(2^3)^(3^4) + = (4^4)^(0^0)^(1^1)^(3^3)^2 + = 0^0^0^0^2 = 2 + + Time: O(n) + Space: O(1) +*/ + +class Solution { +public: + int missingNumber(vector& nums) { + int n = nums.size(); + int result = n; + for (int i = 0; i < n; i++) { + result ^= i ^ nums[i]; + } + return result; + } +}; diff --git a/cpp/0269-alien-dictionary.cpp b/cpp/0269-alien-dictionary.cpp new file mode 100644 index 000000000..cc016214b --- /dev/null +++ b/cpp/0269-alien-dictionary.cpp @@ -0,0 +1,58 @@ +#include +#include +#include +#include +using namespace std; + +class Solution { +public: + string alienOrder(vector& words) { + map degree; + map> graph; + int n = words.size(); + + for (auto& word : words) { + for (auto& ch : word) { + degree[ch] = 0; + } + + for (int i = 0; i < n - 1; i++) { + int l = min((int)words[i].size(), (int)words[i + 1].size()); + for (int j = 0; j < l; j++) { + char x = words[i][j]; + char y = words[i + 1][j]; + if (x != y) { + graph[x].push_back(y); + degree[y]++; + break; + } + } + } + + string ret = ""; + queue q; + map::iterator it = degree.begin(); + while (it != degree.end()) { + if (it->second == 0) { + q.push(it->first); + } + it++; + } + + while (!q.empty()) { + char x = q.front(); + q.pop(); + ret += x; + vector::iterator sit = graph[x].begin(); + while (sit != graph[x].end()) { + degree[*sit]--; + if (degree[*sit] == 0) { + q.push(*sit); + } + sit++; + } + } + return ret.size() == degree.size() ? ret : ""; + } + } +}; diff --git a/cpp/0271-encode-and-decode-strings.cpp b/cpp/0271-encode-and-decode-strings.cpp new file mode 100644 index 000000000..a6dc11601 --- /dev/null +++ b/cpp/0271-encode-and-decode-strings.cpp @@ -0,0 +1,48 @@ +/* + Design algorithm to encode/decode: list of strings <-> string + + Encode/decode w/ non-ASCII delimiter: {len of str, "#", str} + + Time: O(n) + Space: O(1) +*/ + +class Codec { +public: + + // Encodes a list of strings to a single string. + string encode(vector& strs) { + string result = ""; + + for (int i = 0; i < strs.size(); i++) { + string str = strs[i]; + result += to_string(str.size()) + "#" + str; + } + + return result; + } + + // Decodes a single string to a list of strings. + vector decode(string s) { + vector result; + + int i = 0; + while (i < s.size()) { + int j = i; + while (s[j] != '#') { + j++; + } + int length = stoi(s.substr(i, j - i)); + string str = s.substr(j + 1, length); + result.push_back(str); + i = j + 1 + length; + } + + return result; + } +private: +}; + +// Your Codec object will be instantiated and called as such: +// Codec codec; +// codec.decode(codec.encode(strs)); diff --git a/cpp/0278-first-bad-version.cpp b/cpp/0278-first-bad-version.cpp new file mode 100644 index 000000000..2b157af0f --- /dev/null +++ b/cpp/0278-first-bad-version.cpp @@ -0,0 +1,39 @@ +/* + Suppose you have versions and you want to find out the first bad one, which causes all the following ones to be bad.n[1, 2, ..., n] + You are given an API which returns whether is bad. Implement a function to find the first bad version. + + Ex. + Input: n = 5, bad = 4 + Output: 4 + + Explanation: + call isBadVersion(3) -> false + call isBadVersion(5) -> true + call isBadVersion(4) -> true + + 1.- Find the number in the middle of the vector. + 2.- Takes a part (first or second), depending on whether or not the target is greater than the middel. + 3.- Change the current left or right part. + 3.- Do this process until the left reaches the right. + + Time: O(log n) + Space: O(1) +*/ + +class Solution { +public: + int firstBadVersion(int n) { + int left = 1; + int right = n; + + while (right > left) { + int mid = left + (right - left) / 2; + + if (isBadVersion(mid)) + right = mid; + else + left = mid + 1; + } + return left; + } +}; diff --git a/cpp/0279-perfect-squares.cpp b/cpp/0279-perfect-squares.cpp new file mode 100644 index 000000000..d73953e42 --- /dev/null +++ b/cpp/0279-perfect-squares.cpp @@ -0,0 +1,46 @@ +#include +#include +using namespace std; + +/* + problem link: https://leetcode.com/problems/perfect-squares/description/ + Given an integer n, return the least number of perfect square numbers that sum to n. + + A perfect square is an integer that is the square of an integer; in other words, it is the + product of some integer with itself. For example, 1, 4, 9, and 16 are perfect squares while 3 + and 11 are not. + + Example 1: + + Input: n = 12 + Output: 3 + Explanation: 12 = 4 + 4 + 4. + + Example 2: + + Input: n = 13 + Output: 2 + Explanation: 13 = 4 + 9. +*/ + +class Solution { +public: + // Similar to Coin Change problem + int numSquares(int n) { + // Create the dp array to eliminate the cache + vector dp(n + 1); + // Initialize the firest element of dp as 0 + dp[0] = 0; + for (int i = 1; i <= n; i++) + { + // Biggest is when all square is 1^2 that is when dp[i] = i + dp[i] = i; + for (int j = 1; j * j <= i; j++) { + // Update the value of dp[i] + dp[i] = min(dp[i], dp[i - j * j] + 1); + } + } + // Return the value of dp[n] + return dp[n]; + } +}; diff --git a/cpp/0280-wiggle-sort.cpp b/cpp/0280-wiggle-sort.cpp new file mode 100644 index 000000000..0410ffe99 --- /dev/null +++ b/cpp/0280-wiggle-sort.cpp @@ -0,0 +1,14 @@ +/* + Time: O(n) + Space: O(1) +*/ + +class Solution { +public: + void wiggleSort(vector &nums) { + for(int i=1; i nums[i-1]))) + swap(nums[i], nums[i-1]); + } + } +}; diff --git a/cpp/0283-move-zeroes.cpp b/cpp/0283-move-zeroes.cpp new file mode 100644 index 000000000..e444eccf6 --- /dev/null +++ b/cpp/0283-move-zeroes.cpp @@ -0,0 +1,16 @@ +class Solution { +public: + void moveZeroes(vector& nums) { + int left = 0; + + for(int i = 0; i < nums.size(); i++){ + if(nums[i] != 0){ + nums[left++] = nums[i]; + } + } + + for(left; left < nums.size(); left ++){ + nums[left] = 0; + } + } +}; diff --git a/cpp/0286-walls-and-gates.cpp b/cpp/0286-walls-and-gates.cpp new file mode 100644 index 000000000..e539eeefb --- /dev/null +++ b/cpp/0286-walls-and-gates.cpp @@ -0,0 +1,46 @@ +/* + Given grid: -1 wall, 0 gate, INF empty, fill each empty w/ dist to nearest gate + + BFS traversal, shortest path from each gate to all empty rooms + Each gate only looks at within 1 space, then next gate, guarantees shortest + + Time: O(m x n) + Space: O(m x n) +*/ + +class Solution { +public: + void wallsAndGates(vector>& rooms) { + int m = rooms.size(); + int n = rooms[0].size(); + + queue> q; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (rooms[i][j] == 0) { + q.push({i, j}); + } + } + } + + while (!q.empty()) { + int row = q.front().first; + int col = q.front().second; + q.pop(); + + for (int i = 0; i < 4; i++) { + int x = row + dirs[i][0]; + int y = col + dirs[i][1]; + + if (x < 0 || x >= m || y < 0 || y >= n || rooms[x][y] != INT_MAX) { + continue; + } + + rooms[x][y] = rooms[row][col] + 1; + q.push({x, y}); + } + } + } +private: + vector> dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; +}; diff --git a/cpp/0287-find-the-duplicate-number.cpp b/cpp/0287-find-the-duplicate-number.cpp new file mode 100644 index 000000000..a852e1090 --- /dev/null +++ b/cpp/0287-find-the-duplicate-number.cpp @@ -0,0 +1,30 @@ +/* + Given int array, return the one repeated number + Ex. nums = [1,3,4,2,2] -> 2, nums = [3,1,3,4,2] -> 3 + + If there's duplicate, must be a cycle, find meeting point + Take 1 back to start, they'll intersect at the duplicate + + Time: O(n) + Space: O(1) +*/ + +class Solution { +public: + int findDuplicate(vector& nums) { + int slow = nums[0]; + int fast = nums[nums[0]]; + + while (slow != fast) { + slow = nums[slow]; + fast = nums[nums[fast]]; + } + + slow = 0; + while (slow != fast) { + slow = nums[slow]; + fast = nums[fast]; + } + return slow; + } +}; diff --git a/cpp/0289-game-of-life.cpp b/cpp/0289-game-of-life.cpp new file mode 100644 index 000000000..5f87d257d --- /dev/null +++ b/cpp/0289-game-of-life.cpp @@ -0,0 +1,52 @@ +class Solution { +public: + int cntNei(vector> & g, int r, int c, int rows, int cols){ + int cnt = 0; + + for (int i = r - 1; i <= r + 1; i++) { + for (int j = c - 1; j <= c + 1; j++) { + if ((i == r && j == c) || i < 0 || j < 0 || i == rows || j == cols) { + continue; + } + if (g[i][j] == 1 || g[i][j] == 3) { + cnt++; + } + } + } + + return cnt; + } + + void gameOfLife(vector>& g) { + int rows = g.size(); + int cols = g[0].size(); + + for (int r = 0; r < rows; r++){ + for (int c = 0; c < cols; c++){ + int nei = cntNei(g, r, c, rows, cols); + + if (g[r][c] == 1) { + // when cell is alive + if (nei == 2 || nei == 3) { + g[r][c] = 3; + } + } else { + // when cell is not alive right now + if (nei == 3) { + g[r][c] = 2; + } + } + } + } + + for (int r = 0; r < rows ; r++){ + for (int c = 0; c < cols; c++){ + if (g[r][c] == 1) { + g[r][c] = 0; + } else if (g[r][c] == 2 || g[r][c] == 3) { + g[r][c] = 1; + } + } + } + } +}; diff --git a/cpp/0290-word-pattern.cpp b/cpp/0290-word-pattern.cpp new file mode 100644 index 000000000..b86c1756a --- /dev/null +++ b/cpp/0290-word-pattern.cpp @@ -0,0 +1,19 @@ +class Solution { +public: + bool wordPattern(string pattern, string str) { + vector pat_map (26, 0); + unordered_map str_map; + int i=0, n = pattern.size(); + istringstream ss (str); + string token; + + for(string token; ss >> token; ++i) { + if(i == n || pat_map[pattern[i]-'a'] != str_map[token]) return false; + + // 1-based indexing since map assigns 0 as a default value for keys not found. + pat_map[pattern[i]-'a'] = str_map[token] = i+1; + } + + return i == n; + } +}; diff --git a/cpp/0295-find-median-from-data-stream.cpp b/cpp/0295-find-median-from-data-stream.cpp new file mode 100644 index 000000000..c070cc6aa --- /dev/null +++ b/cpp/0295-find-median-from-data-stream.cpp @@ -0,0 +1,66 @@ +/* + Implement data structure that gets the median from a data stream + + Max heap of lower values & min heap of higher values, access to mids + + Time: O(log n) + O(1) + Space: O(n) +*/ + +class MedianFinder { +public: + MedianFinder() { + + } + + void addNum(int num) { + if (lower.empty()) { + lower.push(num); + return; + } + + if (lower.size() > higher.size()) { + if (lower.top() > num) { + higher.push(lower.top()); + lower.pop(); + lower.push(num); + } else { + higher.push(num); + } + } else { + if (num > higher.top()) { + lower.push(higher.top()); + higher.pop(); + higher.push(num); + } else { + lower.push(num); + } + } + } + + double findMedian() { + double result = 0.0; + + if (lower.size() == higher.size()) { + result = lower.top() + (higher.top() - lower.top()) / 2.0; + } else { + if (lower.size() > higher.size()) { + result = lower.top(); + } else { + result = higher.top(); + } + } + + return result; + } +private: + priority_queue lower; + priority_queue, greater> higher; +}; + +/** + * Your MedianFinder object will be instantiated and called as such: + * MedianFinder* obj = new MedianFinder(); + * obj->addNum(num); + * double param_2 = obj->findMedian(); + */ diff --git a/cpp/0297-serialize-and-deserialize-binary-tree.cpp b/cpp/0297-serialize-and-deserialize-binary-tree.cpp new file mode 100644 index 000000000..d747ea8e8 --- /dev/null +++ b/cpp/0297-serialize-and-deserialize-binary-tree.cpp @@ -0,0 +1,69 @@ +/* + Design an algorithm to serialize & deserialize a binary tree + + Use stringstream to concisely handle negatives, nulls, etc. + + Time: O(n) serialize, O(n) deserialize + Space: O(n) serialize, O(n) deserialize +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode(int x) : val(x), left(NULL), right(NULL) {} + * }; + */ +class Codec { +public: + + // Encodes a tree to a single string. + string serialize(TreeNode* root) { + ostringstream out; + encode(root, out); + return out.str(); + } + + // Decodes your encoded data to tree. + TreeNode* deserialize(string data) { + istringstream in(data); + return decode(in); + } + +private: + + void encode(TreeNode* root, ostringstream& out) { + if (root == NULL) { + out << "N "; + return; + } + + out << root->val << " "; + + encode(root->left, out); + encode(root->right, out); + } + + TreeNode* decode(istringstream& in) { + string value = ""; + in >> value; + + if (value == "N") { + return NULL; + } + + TreeNode* root = new TreeNode(stoi(value)); + + root->left = decode(in); + root->right = decode(in); + + return root; + } + +}; + +// Your Codec object will be instantiated and called as such: +// Codec ser, deser; +// TreeNode* ans = deser.deserialize(ser.serialize(root)); diff --git a/cpp/0300-longest-increasing-subsequence.cpp b/cpp/0300-longest-increasing-subsequence.cpp new file mode 100644 index 000000000..c1a3ccd1b --- /dev/null +++ b/cpp/0300-longest-increasing-subsequence.cpp @@ -0,0 +1,40 @@ +/* + Given int array, return length of longest increasing subsequence + Ex. nums = [10,9,2,5,3,7,101,18] -> 4, [2,3,7,101] + + Why DP? 1) Max/min of smth, 2) make decisions based on prev decisions + "Decision": is it worth it to consider this number? + If use may contribute to better LIS, but may also eliminate an even better LIS + + Framework to solve DP: + 1) Need some function or array that represents ans to the problem (dp array) + 2) Way to transition b/w states (recurrence relation), depends on question + 3) Need a base case (initial solution for every subproblem) + + Recurrence relation: dp[i] = max(dp[j] + 1) + Base case: dp[i] = 1, since every element on its own has an LIS of 1 + + Time: O(n^2) + Space: O(n) +*/ + +class Solution { +public: + int lengthOfLIS(vector& nums) { + int n = nums.size(); + vector dp(n, 1); + + int result = 1; + + for (int i = 1; i < n; i++) { + for (int j = 0; j < i; j++) { + if (nums[j] < nums[i]) { + dp[i] = max(dp[i], dp[j] + 1); + } + } + result = max(result, dp[i]); + } + + return result; + } +}; diff --git a/cpp/0303-range-sum-query-immutable.cpp b/cpp/0303-range-sum-query-immutable.cpp new file mode 100644 index 000000000..cb3070620 --- /dev/null +++ b/cpp/0303-range-sum-query-immutable.cpp @@ -0,0 +1,40 @@ +/* +Given an integer array nums, handle multiple queries of the following type: + +Calculate the sum of the elements of nums between indices left and right inclusive where left <= right. + +Implement the NumArray class: + + - NumArray(int[] nums) Initializes the object with the integer array nums. + - int sumRange(int left, int right) Returns the sum of the elements of nums between indices left and right + inclusive (i.e. nums[left] + nums[left + 1] + ... + nums[right]). + +*/ + + +class NumArray { +public: + vector prefixSum; + NumArray(vector& nums) { + int count=0; + for(int i=0;i=0){ + answer-=prefixSum[left-1]; + } + return answer; + } +}; + +/** + * Your NumArray object will be instantiated and called as such: + * NumArray* obj = new NumArray(nums); + * int param_1 = obj->sumRange(left,right); + */ \ No newline at end of file diff --git a/cpp/0304-range-sum-query-2d-immutable.cpp b/cpp/0304-range-sum-query-2d-immutable.cpp new file mode 100644 index 000000000..af9cf4489 --- /dev/null +++ b/cpp/0304-range-sum-query-2d-immutable.cpp @@ -0,0 +1,37 @@ +class NumMatrix { +public: + + vector> dp; + + NumMatrix(vector>& matrix) { + int r = matrix.size(),c = matrix[0].size(); + dp = matrix; + + for(int i =0;i0) dp[i][j] += dp[i-1][j]; // add prev row + if(j>0) dp[i][j] += dp[i][j-1]; // add prev col + + // remove diagonal as it is added twice above + if(i>0&&j>0) dp[i][j] -= dp[i-1][j-1]; + } + } + } + + int sumRegion(int row1, int col1, int row2, int col2) { + int answer = dp[row2][col2]; + + if(row1>0) answer -= dp[row1-1][col2]; // remove prev row on col1 + if(col1>0) answer -= dp[row2][col1-1]; // remo prev col on row2 + + // add prev diagonal as pre row and prev col both contains that value + if(row1>0&&col1>0) answer += dp[row1-1][col1-1]; + return answer; + } +}; + +/** + * Your NumMatrix object will be instantiated and called as such: + * NumMatrix* obj = new NumMatrix(matrix); + * int param_1 = obj->sumRegion(row1,col1,row2,col2); + */ \ No newline at end of file diff --git a/cpp/0309-best-time-to-buy-and-sell-stock-with-cooldown.cpp b/cpp/0309-best-time-to-buy-and-sell-stock-with-cooldown.cpp new file mode 100644 index 000000000..eb1f4d513 --- /dev/null +++ b/cpp/0309-best-time-to-buy-and-sell-stock-with-cooldown.cpp @@ -0,0 +1,48 @@ +/* + Array of stock prices, find max profit + After a sell cooldown of 1 day, can't engage in multiple transactions + Ex. prices = [1,2,3,0,2] -> 3, transactions = [buy,sell,cd,buy,sell] + + DP + state machine: held ---> sold ---> reset ---> held + sell rest buy + + Time: O(n) + Space: O(1) -> optimized from O(n) since only need i - 1 prev state +*/ + +// class Solution { +// public: +// int maxProfit(vector& prices) { +// int n = prices.size(); +// vector s0(n, 0); +// vector s1(n, 0); +// vector s2(n, 0); +// s0[0] = 0; +// s1[0] = -prices[0]; +// s2[0] = INT_MIN; +// for (int i = 1; i < n; i++) { +// s0[i] = max(s0[i - 1], s2[i - 1]); +// s1[i] = max(s1[i - 1], s0[i - 1] - prices[i]); +// s2[i] = s1[i - 1] + prices[i]; +// } +// return max(s0[n - 1], s2[n - 1]); +// } +// }; + +class Solution { +public: + int maxProfit(vector& prices) { + int sold = 0; + int hold = INT_MIN; + int rest = 0; + + for (int i = 0; i < prices.size(); i++) { + int prevSold = sold; + sold = hold + prices[i]; + hold = max(hold, rest - prices[i]); + rest = max(rest, prevSold); + } + + return max(sold, rest); + } +}; diff --git a/cpp/0312-burst-balloons.cpp b/cpp/0312-burst-balloons.cpp new file mode 100644 index 000000000..fece390c4 --- /dev/null +++ b/cpp/0312-burst-balloons.cpp @@ -0,0 +1,52 @@ +/* + Given array of balloons w/ coins, if burst ith, get (i-1) + i + (i+1) coins + Return max coins can collect by bursting the balloons wisely + + DP to return max coins obtainable in each interval [left, right] + Divide & conquer left & right depends on previous bursts, so think backwards + Instead of which one to burst first, need to think which one to burst last + + Time: O(n^3) -> O(n^2) states, for each states, determining max coins is O(n) + Space: O(n^2) -> O(n^2) to store all states +*/ + +class Solution { +public: + int maxCoins(vector& nums) { + // add 1 before & after nums + nums.insert(nums.begin(), 1); + nums.insert(nums.end(), 1); + int n = nums.size(); + + // cache results of dp + vector> memo(n, vector(n, 0)); + + // 1 & n - 2 since we can't burst our fake balloons + return dp(nums, memo, 1, n - 2); + } +private: + int dp(vector& nums, vector>& memo, int left, int right) { + // base case interval is empty, yields 0 coins + if (right - left < 0) { + return 0; + } + + // we've already seen this, return from cache + if (memo[left][right] > 0) { + return memo[left][right]; + } + + // find the last burst in nums[left]...nums[right] + int result = 0; + for (int i = left; i <= right; i++) { + // nums[i] is the last burst + int curr = nums[left - 1] * nums[i] * nums[right + 1]; + // nums[i] is fixed, recursively call left & right sides + int remaining = dp(nums, memo, left, i - 1) + dp(nums, memo, i + 1, right); + result = max(result, curr + remaining); + } + // add to cache + memo[left][right] = result; + return result; + } +}; diff --git a/cpp/0322-coin-change.cpp b/cpp/0322-coin-change.cpp new file mode 100644 index 000000000..7ba78ae6e --- /dev/null +++ b/cpp/0322-coin-change.cpp @@ -0,0 +1,30 @@ +/* + Given array of coins & an amount, return fewest coins to make that amount + Ex. coins = [1,2,5], amount = 11 -> 3, $11 = $5 + $5 + $1 + + Compute all min counts for amounts up to i, "simulate" use of a coin + + Time: O(m x n) -> m = # of coins, n = amount + Space: O(n) +*/ + +class Solution { +public: + int coinChange(vector& coins, int amount) { + vector dp(amount + 1, amount + 1); + dp[0] = 0; + + for (int i = 1; i < amount + 1; i++) { + for (int j = 0; j < coins.size(); j++) { + if (i - coins[j] >= 0) { + dp[i] = min(dp[i], 1 + dp[i - coins[j]]); + } + } + } + + if (dp[amount] == amount + 1) { + return -1; + } + return dp[amount]; + } +}; diff --git a/cpp/0323-number-of-connected-components-in-an-undirected-graph.cpp b/cpp/0323-number-of-connected-components-in-an-undirected-graph.cpp new file mode 100644 index 000000000..6253c33d2 --- /dev/null +++ b/cpp/0323-number-of-connected-components-in-an-undirected-graph.cpp @@ -0,0 +1,57 @@ +/* + Graph of n nodes, given edges array, return # of connected components + Ex. n = 5, edges = [[0,1],[1,2],[3,4]] -> 2 + + Union find, for each edge combine, if already in same set keep traversing + If not in same set, decrement count by 1, count will store # of components + + Time: O(n) + Space: O(n) +*/ + +class Solution { +public: + int countComponents(int n, vector>& edges) { + vector parents; + vector ranks; + for (int i = 0; i < n; i++) { + parents.push_back(i); + ranks.push_back(1); + } + + int result = n; + for (int i = 0; i < edges.size(); i++) { + int n1 = edges[i][0]; + int n2 = edges[i][1]; + result -= doUnion(parents, ranks, n1, n2); + } + return result; + } +private: + int doFind(vector& parents, int n) { + int p = parents[n]; + while (p != parents[p]) { + parents[p] = parents[parents[p]]; + p = parents[p]; + } + return p; + } + + int doUnion(vector& parents, vector& ranks, int n1, int n2) { + int p1 = doFind(parents, n1); + int p2 = doFind(parents, n2); + if (p1 == p2) { + return 0; + } + + if (ranks[p1] > ranks[p2]) { + parents[p2] = p1; + ranks[p1] += ranks[p2]; + } else { + parents[p1] = p2; + ranks[p2] += ranks[p1]; + } + + return 1; + } +}; diff --git a/cpp/0329-longest-increasing-path-in-a-matrix.cpp b/cpp/0329-longest-increasing-path-in-a-matrix.cpp new file mode 100644 index 000000000..5beb7f174 --- /dev/null +++ b/cpp/0329-longest-increasing-path-in-a-matrix.cpp @@ -0,0 +1,48 @@ +/* + Given matrix, return length of longest increasing path + Ex. matrix = [[9,9,4],[6,6,8],[2,1,1]] -> 4, [1,2,6,9] + + DFS + memo, cache on indices, compare to prev for increasing check + + Time: O(m x n) + Space: O(m x n) +*/ + +class Solution { +public: + int longestIncreasingPath(vector>& matrix) { + int m = matrix.size(); + int n = matrix[0].size(); + + int result = 0; + + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + result = max(result, dfs(matrix, -1, i, j, m, n)); + } + } + + return result; + } +private: + // {(i, j) -> longest increasing path at (i, j)} + map, int> dp; + + int dfs(vector>& matrix, int prev, int i, int j, int m, int n) { + if (i < 0 || i >= m || j < 0 || j >= n || matrix[i][j] <= prev) { + return 0; + } + if (dp.find({i, j}) != dp.end()) { + return dp[{i, j}]; + } + + int result = 1; + result = max(result, 1 + dfs(matrix, matrix[i][j], i - 1, j, m, n)); + result = max(result, 1 + dfs(matrix, matrix[i][j], i + 1, j, m, n)); + result = max(result, 1 + dfs(matrix, matrix[i][j], i, j - 1, m, n)); + result = max(result, 1 + dfs(matrix, matrix[i][j], i, j + 1, m, n)); + dp[{i, j}] = result; + + return dp[{i, j}]; + } +}; diff --git a/cpp/0332-reconstruct-itinerary.cpp b/cpp/0332-reconstruct-itinerary.cpp new file mode 100644 index 000000000..2b74b8f69 --- /dev/null +++ b/cpp/0332-reconstruct-itinerary.cpp @@ -0,0 +1,37 @@ +/* + Given airline tickets, find valid itinerary (use all tickets once) + Ex. tickets = [["MUC","LHR"],["JFK","MUC"],["SFO","SJC"],["LHR","SFO"]] + output = ["JFK","MUC","LHR","SFO","SJC"] + + Greedy DFS, build route backwards when retreating, merge cycles into main path + + Time: O(E log (E / V)) -> E = # of flights, V = # of airports, sorting + Space: O(V + E) -> store # of airports & # of flights in hash map +*/ + +class Solution { +public: + vector findItinerary(vector>& tickets) { + unordered_map> m; + for (int i = 0; i < tickets.size(); i++) { + m[tickets[i][0]].insert(tickets[i][1]); + } + + vector result; + dfs(m, "JFK", result); + reverse(result.begin(), result.end()); + return result; + } +private: + void dfs(unordered_map>& m, + string airport, vector& result) { + + while (!m[airport].empty()) { + string next = *m[airport].begin(); + m[airport].erase(m[airport].begin()); + dfs(m, next, result); + } + + result.push_back(airport); + } +}; diff --git a/cpp/0338-counting-bits.cpp b/cpp/0338-counting-bits.cpp new file mode 100644 index 000000000..cc413ed38 --- /dev/null +++ b/cpp/0338-counting-bits.cpp @@ -0,0 +1,25 @@ +/* + Given int, return array: for each i, ans[i] is # of 1's + Ex. n = 2 -> [0,1,1], 0 = 0 has 0, 1 = 1 has 1, 2 = 10 has 1 + + x = 1001011101 = 605 + x'= 0100101110 = 302 + Differ by 1 bit, by removing LSB: f(x) = f(x / 2) + (x mod 2) + + Time: O(n) + Space: O(1), the output array does not count towards space +*/ + +class Solution { +public: + vector countBits(int n) { + vector result(n + 1, 0); + + for (int i = 1; i <= n; i++) { + // i / 2 i % 2 + result[i] = result[i >> 1] + (i & 1); + } + + return result; + } +}; diff --git a/cpp/0341-flatten-nested-list-iterator.cpp b/cpp/0341-flatten-nested-list-iterator.cpp new file mode 100644 index 000000000..c19ad3a12 --- /dev/null +++ b/cpp/0341-flatten-nested-list-iterator.cpp @@ -0,0 +1,58 @@ +/** + * // This is the interface that allows for creating nested lists. + * // You should not implement it, or speculate about its implementation + * class NestedInteger { + * public: + * // Return true if this NestedInteger holds a single integer, rather than a nested list. + * bool isInteger() const; + * + * // Return the single integer that this NestedInteger holds, if it holds a single integer + * // The result is undefined if this NestedInteger holds a nested list + * int getInteger() const; + * + * // Return the nested list that this NestedInteger holds, if it holds a nested list + * // The result is undefined if this NestedInteger holds a single integer + * const vector &getList() const; + * }; + */ + +class NestedIterator { +private: + vector nestedList; + vector stack; + + void dfs(vector nestedList) { + for (const auto &item : nestedList) { + if (item.isInteger()) { + stack.push_back(item.getInteger()); + } else { + dfs(item.getList()); + } + } + } + +public: + NestedIterator(vector &nestedList) : nestedList(nestedList) { + dfs(nestedList); + reverse(stack.begin(), stack.end()); + } + + int next() { + if (!hasNext()) { + return -1; + } + int retval = stack.back(); + stack.pop_back(); + return retval; + } + + bool hasNext() { + return stack.size(); + } +}; + +/** + * Your NestedIterator object will be instantiated and called as such: + * NestedIterator i(nestedList); + * while (i.hasNext()) cout << i.next(); + */ diff --git a/cpp/0342-power-of-four.cpp b/cpp/0342-power-of-four.cpp new file mode 100644 index 000000000..b002a813f --- /dev/null +++ b/cpp/0342-power-of-four.cpp @@ -0,0 +1,13 @@ +class Solution{ + public: + bool isPowerOfFour(int n){ + if(n <= 0){ + return false; + } + bool pow = true; + while((n > 1) && (pow == true)){ + n % 4 == 0 ? n = n / 4 : pow = false; + } + return pow; + } +}; diff --git a/cpp/0343-integer-break.cpp b/cpp/0343-integer-break.cpp new file mode 100644 index 000000000..466c61cdd --- /dev/null +++ b/cpp/0343-integer-break.cpp @@ -0,0 +1,28 @@ +/* +Given an integer n, break it into the sum of k positive integers, where k >= 2, and maximize the product of those integers. +Return the maximum product that we can get. + +Example. For n = 10, we can break it as 10 = 3 + 3 + 4. Product of these integers is 3 x 3 x 4 = 36, which is the maximum product that + we can get in this case. So we return 36 as the answer. + + +Time: O(n^2) +Space: O(n) + +*/ + +class Solution { +public: + int integerBreak(int n) { + vector dp(n+1, INT_MIN); + dp[0] = 1, dp[1] = 1; + for(int ind=2; ind<=n; ind++) { + for(int i=ind-1; i>=1; i--) { + dp[ind] = max(dp[ind], i * dp[ind - i]); + } + if(ind < n) dp[ind] = max(dp[ind], ind); + } + return dp[n]; + } +}; + diff --git a/cpp/0344-Reverse-String.cpp b/cpp/0344-Reverse-String.cpp new file mode 100644 index 000000000..c57f2a9d0 --- /dev/null +++ b/cpp/0344-Reverse-String.cpp @@ -0,0 +1,13 @@ +class Solution { +public: + void reverseString(vector& s) { + int left = 0, right = s.size() - 1; + + while (left < right){ + swap(s[left], s[right]); + + left++; + right--; + } + } +}; diff --git a/cpp/0344-reverse-string.cpp b/cpp/0344-reverse-string.cpp new file mode 100644 index 000000000..c57f2a9d0 --- /dev/null +++ b/cpp/0344-reverse-string.cpp @@ -0,0 +1,13 @@ +class Solution { +public: + void reverseString(vector& s) { + int left = 0, right = s.size() - 1; + + while (left < right){ + swap(s[left], s[right]); + + left++; + right--; + } + } +}; diff --git a/cpp/0347-top-k-frequent-elements.cpp b/cpp/0347-top-k-frequent-elements.cpp new file mode 100644 index 000000000..646a9ba38 --- /dev/null +++ b/cpp/0347-top-k-frequent-elements.cpp @@ -0,0 +1,65 @@ +/* + Given an integer array nums & an integer k, return the k most frequent elements + Ex. nums = [1,1,1,2,2,3] k = 2 -> [1,2], nums = [1] k = 1 -> [1] + + Heap -> optimize w/ freq map & bucket sort (no freq can be > n), get results from end +*/ + +// Time: O(n log k) +// Space: O(n + k) + +// class Solution { +// public: +// vector topKFrequent(vector& nums, int k) { +// unordered_map m; +// for (int i = 0; i < nums.size(); i++) { +// m[nums[i]]++; +// } +// priority_queue, vector>, greater>> pq; +// for (auto it = m.begin(); it != m.end(); it++) { +// pq.push({it->second, it->first}); +// if (pq.size() > k) { +// pq.pop(); +// } +// } +// vector result; +// while (!pq.empty()) { +// result.push_back(pq.top().second); +// pq.pop(); +// } +// return result; +// } +// }; + +// Time: O(n) +// Space: O(n) + +class Solution { +public: + vector topKFrequent(vector& nums, int k) { + int n = nums.size(); + + unordered_map m; + for (int i = 0; i < n; i++) { + m[nums[i]]++; + } + + vector> buckets(n + 1); + for (auto it = m.begin(); it != m.end(); it++) { + buckets[it->second].push_back(it->first); + } + + vector result; + + for (int i = n; i >= 0; i--) { + if (result.size() >= k) { + break; + } + if (!buckets[i].empty()) { + result.insert(result.end(), buckets[i].begin(), buckets[i].end()); + } + } + + return result; + } +}; diff --git a/cpp/0355-design-twitter.cpp b/cpp/0355-design-twitter.cpp new file mode 100644 index 000000000..f5b88455b --- /dev/null +++ b/cpp/0355-design-twitter.cpp @@ -0,0 +1,65 @@ +/* + Design Twitter: post tweets, follow/unfollow, see recent tweets + + Maintain user -> tweet pairs & hash map {user -> ppl they follow} + + Time: O(n) + Space: O(n) +*/ + +class Twitter { +public: + Twitter() { + + } + + void postTweet(int userId, int tweetId) { + posts.push_back({userId, tweetId}); + } + + vector getNewsFeed(int userId) { + // 10 tweets + int count = 10; + vector result; + + // since postTweet pushes to the back, looping from back gets most recent + for (int i = posts.size() - 1; i >= 0; i--) { + if (count == 0) { + break; + } + + int followingId = posts[i].first; + int tweetId = posts[i].second; + unordered_set following = followMap[userId]; + // add to result if they're following them or it's a tweet from themself + if (following.find(followingId) != following.end() || followingId == userId) { + result.push_back(tweetId); + count--; + } + } + + return result; + } + + void follow(int followerId, int followeeId) { + followMap[followerId].insert(followeeId); + } + + void unfollow(int followerId, int followeeId) { + followMap[followerId].erase(followeeId); + } +private: + // pairs: [user, tweet] + vector> posts; + // hash map: {user -> people they follow} + unordered_map> followMap; +}; + +/** + * Your Twitter object will be instantiated and called as such: + * Twitter* obj = new Twitter(); + * obj->postTweet(userId,tweetId); + * vector param_2 = obj->getNewsFeed(userId); + * obj->follow(followerId,followeeId); + * obj->unfollow(followerId,followeeId); + */ diff --git a/cpp/0367-valid-perfect-square.cpp b/cpp/0367-valid-perfect-square.cpp new file mode 100644 index 000000000..a7fe155f8 --- /dev/null +++ b/cpp/0367-valid-perfect-square.cpp @@ -0,0 +1,28 @@ +/* +Approach: Binary search the number such that divind num by that number gives the number itself + +Time Complexity: log(n) +Space Complexity: O(1) +*/ +class Solution { +public: + bool isPerfectSquare(int num) { + if(num ==1) {return true;} + int l = 0,r = num; + while(l 3, a = 2 b = 3 -> 5 + + XOR for addition, AND for carry bit + + Time: O(n) + Space: O(1) +*/ + +class Solution { +public: + int getSum(int a, int b) { + while (b != 0) { + int carry = a & b; + a = a ^ b; + b = (unsigned)carry << 1; + } + return a; + } +}; diff --git a/cpp/0374-guess-number-higher-or-lower.cpp b/cpp/0374-guess-number-higher-or-lower.cpp new file mode 100644 index 000000000..13edfe903 --- /dev/null +++ b/cpp/0374-guess-number-higher-or-lower.cpp @@ -0,0 +1,18 @@ +class Solution { +public: + int guessNumber(int n) { + int low = 1; + int high = n; + + while(true) { + int mid = low + (high - low)/2; + int myGuess = guess(mid); + if(myGuess == 1) + low = mid + 1; + else if(myGuess == -1) + high = mid - 1; + else + return mid; + } + } +}; diff --git a/cpp/0377-combination-sum-iv.cpp b/cpp/0377-combination-sum-iv.cpp new file mode 100644 index 000000000..07d534476 --- /dev/null +++ b/cpp/0377-combination-sum-iv.cpp @@ -0,0 +1,28 @@ +/* + Given an array of distinct integers nums and a target integer 'target', + return the number of possible combinations that add up to target. + + Ex. nums = [1,2,3] target = 4 + Possible Combinations: (1,1,1,1) (1,1,2) (1,2,1) (1,3) (2,1,1,) + (2,2) (3,1). + So, total number of combinations possible is 7. + + Time: O(n * m) + Space: O(m) +*/ + +class Solution { +public: + int combinationSum4(vector& nums, int target) { + sort(nums.begin(), nums.end()); + vector dp(target+1, 0); + dp[0] = 1; + for(int total=1; total<=target; total++) { + for(int i=0; i indices; + vector values; + RandomizedSet() { + } + + bool insert(int val) { + + if(indices.find(val)==indices.end()){ + values.push_back(val); + // store the index of that value + indices[val] = values.size() -1; + return true; + } + return false; + } + + bool remove(int val) { + + if(indices.find(val)==indices.end()){ + return false; + } + + // find index of the value + int idx = indices[val]; + // get the last value in the vector + // and change its index to curr val's index + indices[values[values.size()-1]] = idx; + + // replace curr vals index with last + // last value + values[idx] = values[values.size()-1]; + + // so now the curr val is removed from the + // vector replaced by last value and so + // we could just remove last value + values.pop_back(); + + // also erase it from the hash map + indices.erase(val); + return true; + } + + int getRandom() { + return values[rand()%values.size()]; + } +}; + +/** + * Your RandomizedSet object will be instantiated and called as such: + * RandomizedSet* obj = new RandomizedSet(); + * bool param_1 = obj->insert(val); + * bool param_2 = obj->remove(val); + * int param_3 = obj->getRandom(); + */ \ No newline at end of file diff --git a/cpp/0387-first-unique-character-in-a-string.cpp b/cpp/0387-first-unique-character-in-a-string.cpp new file mode 100644 index 000000000..013108202 --- /dev/null +++ b/cpp/0387-first-unique-character-in-a-string.cpp @@ -0,0 +1,24 @@ +class Solution{ + public: + unordered_map Map = {}; + int firstUniqChar(string s){ + char c; + int Min; + for(int i = 0; i < s.length(); i++){ + if(Map.find(s[i]) != Map.end()){ + Map[s[i]]++; + } + else{ + Map.insert(make_pair(s[i], 1)); + } + } + Min = s.length(); + for(auto & m : Map){ + if((m.second == 1) && (s.find(m.first) < Min)){ + c = m.first; + Min = s.find(m.first); + } + } + return Min == s.length() ? -1 : Min; + } +}; diff --git a/cpp/0392-is-subsequence.cpp b/cpp/0392-is-subsequence.cpp new file mode 100644 index 000000000..7bf6aa9df --- /dev/null +++ b/cpp/0392-is-subsequence.cpp @@ -0,0 +1,19 @@ +// Time Complexity is O(N) where n is the size of the target string. +// Space Complexity is O(1) + +class Solution { +public: + bool isSubsequence(string s, string t) { + int i = 0 , j = 0; + while(j < s.size() && i < t.size()) + { + if(s[j] == t[i]) + j++; + + i++; + } + + if(j >= s.size()) return true; + return false; + } +}; diff --git a/cpp/0394-decode-string.cpp b/cpp/0394-decode-string.cpp new file mode 100644 index 000000000..73c31c301 --- /dev/null +++ b/cpp/0394-decode-string.cpp @@ -0,0 +1,40 @@ +class Solution { +public: + string decodeString(string s) { + stack stack; + string result; + + for (int i = 0; i < s.length(); i++) { + if (s[i] != ']') { + stack.push(string(1, s[i])); + } else { + string substr; + while (!stack.empty() && stack.top() != "[") { + substr = stack.top() + substr; + stack.pop(); + } + stack.pop(); + + string k; + while (!stack.empty() && isdigit(stack.top()[0])) { + k = stack.top() + k; + stack.pop(); + } + int kInt = stoi(k); + + string temp; + for (int j = 0; j < kInt; j++) { + temp += substr; + } + stack.push(temp); + } + } + + while (!stack.empty()) { + result = stack.top() + result; + stack.pop(); + } + + return result; + } +}; diff --git a/cpp/0399-evaluate-division.cpp b/cpp/0399-evaluate-division.cpp new file mode 100644 index 000000000..89b8078f0 --- /dev/null +++ b/cpp/0399-evaluate-division.cpp @@ -0,0 +1,57 @@ +class Solution { + unordered_map>> graph; + unordered_map visited; + double queryAns; + +public: + bool dfs(string startNode, string endNode, double runningProduct){ + if(graph.find(startNode) == graph.end() || graph.find(endNode) == graph.end()) { + return false; + } + + if(startNode == endNode && graph.find(startNode)!=graph.end()) { + queryAns = runningProduct; + return true; + + } + + bool tempAns = false; + visited[startNode] = true; + + for(int i = 0; i < graph[startNode].size(); i++){ + if(!visited[graph[startNode][i].first]){ + tempAns = dfs(graph[startNode][i].first, endNode, runningProduct*graph[startNode][i].second); + if(tempAns){ + break; + } + } + } + visited[startNode] = false; + + return tempAns; + } + + vector calcEquation(vector>& equations, vector& values, vector>& queries) { + int n = equations.size(), m = queries.size(); + vector ans(m); + + for(int i = 0; i < n ; i++){ + + graph[equations[i][0]].push_back({equations[i][1], values[i]}); + graph[equations[i][1]].push_back({equations[i][0], 1/values[i]}); + visited[equations[i][0]] = false; + visited[equations[i][1]] = false; + + } + + for(int i = 0; i < m ; i++){ + + queryAns = 1; + bool pathFound = dfs(queries[i][0], queries[i][1], 1); + if(pathFound) ans[i] = queryAns; + else ans[i] = -1; + + } + return ans; + } +}; diff --git a/cpp/0402-remove-k-digits.cpp b/cpp/0402-remove-k-digits.cpp new file mode 100644 index 000000000..9a74b5806 --- /dev/null +++ b/cpp/0402-remove-k-digits.cpp @@ -0,0 +1,37 @@ +// Time Complexity is O(N) where N is the size of the input string. +// Space complexity is O(N) as well +class Solution { +public: + string removeKdigits(string num, int k) { + int n = num.size(); + + stacks; + int count = k; + + for(int i = 0 ; i < n; i++) + { + while(!s.empty() && count > 0 && s.top() > num[i]) + { + s.pop(); + count--; + } + s.push(num[i]); + } + + // In case the num was already in a non increasing order (e.x: 123456) + while(s.size() != n - k) s.pop(); + + string res = ""; + while(!s.empty()) + { + res += s.top(); + s.pop(); + } + reverse(res.begin() , res.end()); + // Remove the zeros from the left if they exist. + while (res[0] == '0') res.erase(0 , 1); + + + return (res == "") ? "0": res; + } +}; diff --git a/cpp/0416-partition-equal-subset-sum.cpp b/cpp/0416-partition-equal-subset-sum.cpp new file mode 100644 index 000000000..e91cdccb0 --- /dev/null +++ b/cpp/0416-partition-equal-subset-sum.cpp @@ -0,0 +1,42 @@ +/* + Given non-empty, non-negative integer array nums, find if: + Can be partitionined into 2 subsets such that sums are equal + Ex. nums = [1,5,11,5] -> true, [1,5,5] & [11], both add to 11 + + Maintain DP set, for each num, check if num in set + curr = target + If not, add curr to every num in set we checked & iterate + + Time: O(n x sum(nums)) + Space: O(sum(nums)) +*/ + +class Solution { +public: + bool canPartition(vector& nums) { + int target = 0; + for (int i = 0; i < nums.size(); i++) { + target += nums[i]; + } + if (target % 2 != 0) { + return false; + } + target /= 2; + + unordered_set dp; + dp.insert(0); + + for (int i = 0; i < nums.size(); i++) { + unordered_set dpNext; + for (auto it = dp.begin(); it != dp.end(); it++) { + if (*it + nums[i] == target) { + return true; + } + dpNext.insert(*it + nums[i]); + dpNext.insert(*it); + } + dp = dpNext; + } + + return false; + } +}; diff --git a/cpp/0417-pacific-atlantic-water-flow.cpp b/cpp/0417-pacific-atlantic-water-flow.cpp new file mode 100644 index 000000000..0db9a0cd7 --- /dev/null +++ b/cpp/0417-pacific-atlantic-water-flow.cpp @@ -0,0 +1,128 @@ +/* + Top & left pacific, bottom & right atlantic, determine spots that flow to both + + Instead go outside in, from oceans to spots where rain could flow from + Faster bc avoids repeated work: cells along a path can also reach that ocean + + Time: O(m x n) + Space: O(m x n) +*/ + +class Solution { +public: + vector> pacificAtlantic(vector>& heights) { + int m = heights.size(); + int n = heights[0].size(); + + vector> pacific(m, vector(n)); + vector> atlantic(m, vector(n)); + + for (int i = 0; i < m; i++) { + dfs(heights, pacific, i, 0, m, n); + dfs(heights, atlantic, i, n - 1, m, n); + } + + for (int j = 0; j < n; j++) { + dfs(heights, pacific, 0, j, m, n); + dfs(heights, atlantic, m - 1, j, m, n); + } + + vector> result; + + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (pacific[i][j] && atlantic[i][j]) { + result.push_back({i, j}); + } + } + } + + return result; + } +private: + void dfs(vector>& heights, vector>& visited, + int i, int j, int m, int n) { + + visited[i][j] = true; + + if (i > 0 && !visited[i - 1][j] && heights[i - 1][j] >= heights[i][j]) { + dfs(heights, visited, i - 1, j, m, n); + } + if (i < m - 1 && !visited[i + 1][j] && heights[i + 1][j] >= heights[i][j]) { + dfs(heights, visited, i + 1, j, m, n); + } + if (j > 0 && !visited[i][j - 1] && heights[i][j - 1] >= heights[i][j]) { + dfs(heights, visited, i, j - 1, m, n); + } + if (j < n - 1 && !visited[i][j + 1] && heights[i][j + 1] >= heights[i][j]) { + dfs(heights, visited, i, j + 1, m, n); + } + } +}; + + +/* + BFS solution +*/ + +class Solution { +private: + int rows, cols; + + void bfs(queue>& q, vector>& visited, vector>& heights) { + + vector> directions = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}}; + while (!q.empty()) { + auto [row, col] = q.front(); + q.pop(); + for (const auto &direction : directions) { + int newRow = row + direction.first; + int newCol = col + direction.second; + if (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols && !visited[newRow][newCol] + && heights[newRow][newCol] >= heights[row][col]) { + q.push({newRow, newCol}); + visited[newRow][newCol] = true; + } + } + } + } + +public: + vector> pacificAtlantic(vector>& heights) { + rows = heights.size(); + cols = heights[0].size(); + vector> results; + queue> pacificQueue; + queue> atlanticQueue; + vector> pacific(rows, vector(cols, false)); + vector> atlantic(rows, vector(cols, false)); + + for (int row = 0; row < rows; ++row) { + pacific[row][0] = true; + atlantic[row][cols - 1] = true; + pacificQueue.push({row, 0}); + atlanticQueue.push({row, cols-1}); + } + + for (int col = 0; col < cols; ++col) { + pacific[0][col] = true; + atlantic[rows - 1][col] = true; + pacificQueue.push({0, col}); + atlanticQueue.push({rows - 1, col}); + } + + bfs(pacificQueue, pacific, heights); + bfs(atlanticQueue, atlantic, heights); + + for (int row = 0; row < rows; ++row) { + for (int col = 0; col < cols; ++col) { + if (pacific[row][col] && atlantic[row][col]) { + results.push_back({row, col}); + } + } + } + + return results; + + } +}; diff --git a/cpp/0424-longest-repeating-character-replacement.cpp b/cpp/0424-longest-repeating-character-replacement.cpp new file mode 100644 index 000000000..4b83280ce --- /dev/null +++ b/cpp/0424-longest-repeating-character-replacement.cpp @@ -0,0 +1,36 @@ +/* + Given a string s & an int k, can change any char k times: + Return length of longest substring containing same letter + Ex. s = "ABAB" k = 2 -> 4 "AAAA", s = "AABABBA" k = 1 -> 4 + + Sliding window, expand if can change char, contract if > k + + Time: O(n) + Space: O(26) +*/ + +class Solution { +public: + int characterReplacement(string s, int k) { + vector count(26); + int maxCount = 0; + + int i = 0; + int j = 0; + + int result = 0; + + while (j < s.size()) { + count[s[j] - 'A']++; + maxCount = max(maxCount, count[s[j] - 'A']); + if (j - i + 1 - maxCount > k) { + count[s[i] - 'A']--; + i++; + } + result = max(result, j - i + 1); + j++; + } + + return result; + } +}; diff --git a/cpp/0435-non-overlapping-intervals.cpp b/cpp/0435-non-overlapping-intervals.cpp new file mode 100644 index 000000000..ce2d229fe --- /dev/null +++ b/cpp/0435-non-overlapping-intervals.cpp @@ -0,0 +1,27 @@ +/* + Given array of intervals, return min # of intervals to remove for all non-overlapping + Ex. intervals = [[1,2],[1,3],[2,3],[3,4]] -> 1, remove [1,3] for all non-overlapping + + Remove interval w/ longer end point, since will always overlap more or = vs shorter one + + Time: O(n log n) + Space: O(1) +*/ + +class Solution { +public: + int eraseOverlapIntervals(vector>& intervals) { + sort(intervals.begin(), intervals.end()); + int prevEnd = intervals[0][1]; + int count = 0; + for (int i = 1; i < intervals.size(); i++) { + if (prevEnd > intervals[i][0]) { + count++; + prevEnd = min(prevEnd, intervals[i][1]); + } else { + prevEnd = intervals[i][1]; + } + } + return count; + } +}; diff --git a/cpp/0438-find-all-anagrams-in-a-string.cpp b/cpp/0438-find-all-anagrams-in-a-string.cpp new file mode 100644 index 000000000..07a4f7744 --- /dev/null +++ b/cpp/0438-find-all-anagrams-in-a-string.cpp @@ -0,0 +1,44 @@ +class Solution{ + public: + unordered_map Create(string p){ + unordered_map Mapp; + for(char & i : p){ + if(Mapp.find(i) == Mapp.end()){ + Mapp.insert(make_pair(i, 1)); + } + else{ + Mapp[i]++; + } + } + return Mapp; + } + vector findAnagrams(string s, string p){ + unordered_map Maps, Mapp = {}; + vector nums = {}; + int Fp, Sp; + int lens, lenp; + Fp = 0; + Sp = p.length(); + lens = s.length(); + lenp = p.length(); + Mapp = Create(p); + Maps = Create(s.substr(0, p.length())); + for(Fp = 0; Fp < lens - lenp + 1; Fp++){ + if(Maps == Mapp){ + nums.push_back(Fp); + } + if(Maps.find(s[Sp]) != Maps.end()){ + Maps[s[Sp]]++; + } + else{ + Maps.insert(make_pair(s[Sp], 1)); + } + Sp++; + Maps[s[Fp]]--; + if(Maps[s[Fp]] == 0){ + Maps.erase(s[Fp]); + } + } + return nums; + } +}; diff --git a/cpp/0441-arranging-coins.cpp b/cpp/0441-arranging-coins.cpp new file mode 100644 index 000000000..127ebb866 --- /dev/null +++ b/cpp/0441-arranging-coins.cpp @@ -0,0 +1,27 @@ +class Solution { +public: + int arrangeCoins(int n) { + int l=1,r = n,answer=1; + long long sum,m; + + // binary search the values + while(l<=r){ + m = l + (r-l)/2; + + sum = m * (m+1)/2; + + if(sum==n){ + return (int)m; + } + else if(n findDisappearedNumbers(vector& nums) { + vector ans; + + for(int x : nums){ /* Mark values as negative */ + int currentVal = abs(x); + nums[currentVal-1] = 0 - abs(nums[currentVal-1]); + } + + for(int i = 1; i <= nums.size(); i++) + if(nums[i-1] > 0) ans.push_back(i); /* Find unmarked values */ + + return ans; + } +}; diff --git a/cpp/0450-delete-node-in-a-bst.cpp b/cpp/0450-delete-node-in-a-bst.cpp new file mode 100644 index 000000000..642a686cd --- /dev/null +++ b/cpp/0450-delete-node-in-a-bst.cpp @@ -0,0 +1,46 @@ +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ + +// Time: O(h) +// Space: O(1) +class Solution { +public: + TreeNode* deleteNode(TreeNode* root, int key) { + if(root == nullptr) return root; + + if(root->val > key) { + root->left = deleteNode(root->left, key); + } + else if(root->val < key) { + root->right = deleteNode(root->right, key); + } + + // perform deletion if root->val == key + else { + if(!root->left) return root->right; + if(!root->right) return root->left; + + // both left and right subtrees are not null + // traverse the right subtree to find the minimum/leftmost value + // (or) travel the left subtree to find the maximum/rightmost value + TreeNode* temp = root->right; + while(temp->left) { + temp = temp->left; + } + root->val = temp->val; + + root->right = deleteNode(root->right, temp->val); + } + + return root; + } +}; diff --git a/cpp/0451-sort-characters-by-frequency.cpp b/cpp/0451-sort-characters-by-frequency.cpp new file mode 100644 index 000000000..e0adb2ac9 --- /dev/null +++ b/cpp/0451-sort-characters-by-frequency.cpp @@ -0,0 +1,28 @@ +class Solution { +public: + string frequencySort(string s) { + //count the frequency / the big A and small A are different characters + //the string cannot not be empty according to the question + unordered_map freq; + for(char c: s){ + freq[c]++; + } + + // Move it to the vector so we can sort it easily + vector> freq_v(freq.begin(), freq.end()); + + // Sort the vector by the frequency + sort(freq_v.begin(), freq_v.end(), [](const pair& a, const pair& b) { + return a.second > b.second; + }); + + // Create the answer string -> add the chars with their frequency + string ans = ""; + for(auto pair: freq_v){ + for(int i = 0; i < pair.second; i++){ + ans += pair.first; + } + } + return ans; + } +}; diff --git a/cpp/0456-132-pattern.cpp b/cpp/0456-132-pattern.cpp new file mode 100644 index 000000000..78d98a486 --- /dev/null +++ b/cpp/0456-132-pattern.cpp @@ -0,0 +1,35 @@ +/* + Given nums, return true if there is a 132 pattern where + num[i] < nums[k] < nums[j] for i < j < k + + Use a monotonically decreasing stack to store a pair + {candidate nums[j], candidate nums[i]} where + candidate nums[i] is the minimum number in nums before j + + Traverse nums. + Pop the stack while n >= candidate nums[j] at the top of the stack (n is a better candidate than those) + Then, if stack not empty, check if 132 patter if found --> return True + Else, add the current num as a candidate nums[j] with the current min as the candidate nums[i] then update current min +*/ +class Solution { +public: + bool find132pattern(vector& nums) { + stack> s; + int curMin = nums[0]; + + for(auto & n : nums){ + // pop all values in stack that have nums[j] candidate <= n + while(!s.empty() && s.top().first <= n){ + s.pop(); + } + // if stack not empty, check if we found 132 pattern + if(!s.empty() && s.top().second < n) return true; + else{ + // add n to stack and update curMin + s.push({n, curMin}); + curMin = min(curMin, n); + } + } + return false; + } +}; diff --git a/cpp/0463-island-perimeter.cpp b/cpp/0463-island-perimeter.cpp new file mode 100644 index 000000000..da9ebb066 --- /dev/null +++ b/cpp/0463-island-perimeter.cpp @@ -0,0 +1,28 @@ +// Time complexity is O(M*N) +// Space complexity is O(1) + +class Solution { +public: + int islandPerimeter(vector>& grid) { + int m = grid.size(); + int n = grid[0].size(); + + int prem = 0; + + for(int i = 0 ; i < m ; i++) + { + for(int j = 0 ; j < n ; j++) + { + if(grid[i][j] == 1) { + prem += 4; + + if(j > 0 && grid[i][j-1] == 1) prem -=2; + if(i >0 && grid[i-1][j] == 1) prem -=2; + + } + } + } + + return prem; + } +}; diff --git a/cpp/0473-matchsticks-to-square.cpp b/cpp/0473-matchsticks-to-square.cpp new file mode 100644 index 000000000..f2e0faf01 --- /dev/null +++ b/cpp/0473-matchsticks-to-square.cpp @@ -0,0 +1,45 @@ +class Solution { + int a,b,c,d; + bool fun(vector& matchsticks,int i){ + if(i==matchsticks.size()){ + if(a==0 && b==0 && c==0 && d==0) return true; + else return false; + } + + if(matchsticks[i]<=a){ + a-=matchsticks[i]; + if(fun(matchsticks,i+1)) return true; + a+=matchsticks[i]; + } + + if(matchsticks[i]<=b){ + b-=matchsticks[i]; + if(fun(matchsticks,i+1)) return true; + b+=matchsticks[i]; + } + + if(matchsticks[i]<=c){ + c-=matchsticks[i]; + if(fun(matchsticks,i+1)) return true; + c+=matchsticks[i]; + } + + if(matchsticks[i]<=d){ + d-=matchsticks[i]; + if(fun(matchsticks,i+1)) return true; + d+=matchsticks[i]; + } + + return false; + } +public: + bool makesquare(vector& matchsticks) { + if(matchsticks.size()<4) return false; + int sum = accumulate(matchsticks.begin(), matchsticks.end(),0); + if(sum % 4 != 0) return false; + int sizeSum=sum/4; + a=sizeSum,b=sizeSum,c=sizeSum,d=sizeSum; + sort(matchsticks.rbegin(), matchsticks.rend()); + return fun(matchsticks,0); + } +}; diff --git a/cpp/0474-ones-and-zeroes.cpp b/cpp/0474-ones-and-zeroes.cpp new file mode 100644 index 000000000..d7237ca17 --- /dev/null +++ b/cpp/0474-ones-and-zeroes.cpp @@ -0,0 +1,38 @@ +class Solution { +public: + int rec(vector>& oz, int i, int m, int n, vector>>& dp) + { + if (i >= oz.size()) + return 0; + + if (oz[i].first > m || oz[i].second > n) + return rec(oz, i + 1, m, n, dp); + + if (dp[i][m][n] != -1) + return dp[i][m][n]; + + int take = 1 + rec(oz, i + 1, m - oz[i].first, n - oz[i].second, dp); + int notTake = rec(oz, i + 1, m, n, dp); + + return dp[i][m][n] = max(take, notTake); + } + int findMaxForm(vector& strs, int m, int n) { + vector> oz(strs.size()); + vector>> dp (strs.size() + 1, vector>(m + 1, vector (n + 1, -1))); + + for (int i = 0; i < strs.size(); i++) + { + int one = 0, zero = 0; + for (int j = 0; j < strs[i].size(); j++) + { + if (strs[i][j] == '1') + one++; + else + zero++; + } + oz[i] = {zero, one}; + } + + return rec(oz, 0, m, n, dp); + } +}; diff --git a/cpp/0494-target-sum.cpp b/cpp/0494-target-sum.cpp new file mode 100644 index 000000000..83b05eb40 --- /dev/null +++ b/cpp/0494-target-sum.cpp @@ -0,0 +1,34 @@ +/* + Given int array & a target, want to build expressions w/ '+' & '-' + Return number of different expressions that evaluates to target + + Recursion w/ memoization, cache on (index, total), which stores # ways + If total ever reaches the target, return 1 (this is a way), else 0 + + Time: O(n x target) + Space: O(n x target) +*/ + +class Solution { +public: + int findTargetSumWays(vector& nums, int target) { + return backtrack(nums, target, 0, 0); + } +private: + // {(index, total) -> # of ways} + map, int> dp; + + int backtrack(vector& nums, int target, int i, int total) { + if (i == nums.size()) { + return total == target ? 1 : 0; + } + if (dp.find({i, total}) != dp.end()) { + return dp[{i, total}]; + } + + dp[{i, total}] = backtrack(nums, target, i + 1, total + nums[i]) + + backtrack(nums, target, i + 1, total - nums[i]); + + return dp[{i, total}]; + } +}; diff --git a/cpp/0496-next-greater-element-i.cpp b/cpp/0496-next-greater-element-i.cpp new file mode 100644 index 000000000..0d3ac70b6 --- /dev/null +++ b/cpp/0496-next-greater-element-i.cpp @@ -0,0 +1,32 @@ +class Solution { +public: + vector nextGreaterElement(vector& nums1, vector& nums2) { + // O (n + m) + map nums1Idx; { + int idx = 0; + for(int n: nums1) + nums1Idx[n] = idx++; + } + vector res; + for(int i = 0; i < nums1.size(); i++) + res.push_back(-1); + + stack stack; + for(int i = 0; i < nums2.size(); i++) { + int cur = nums2[i]; + + // while stack has elements and current is greater than the top of the stack + while(stack.size() && cur > stack.top()) { + int val = stack.top(); // take top val + stack.pop(); + int idx = nums1Idx[val]; + res[idx] = cur; + } + + if(nums1Idx.count(cur)) + stack.push(cur); + } + + return res; + } +}; diff --git a/cpp/0509-fibonacci-number.cpp b/cpp/0509-fibonacci-number.cpp new file mode 100644 index 000000000..a93c903f8 --- /dev/null +++ b/cpp/0509-fibonacci-number.cpp @@ -0,0 +1,41 @@ +/* + The Fibonacci numbers, commonly denoted F(n) form a sequence, called the Fibonacci sequence, such that each number is the sum of the two preceding ones, starting from 0 and 1. That is, + F(0) = 0, F(1) = 1 + F(n) = F(n - 1) + F(n - 2), for n > 1. + Given n, calculate F(n). + + Ex. Input: n = 2 + Output: 1 + Explanation: F(2) = F(1) + F(0) = 1 + 0 = 1. + + Time : O(N) + Space : O(N) +*/ + +/* + Recursive solution : + Time : O(2^N), Space : O(n) + class Solution { + public: + int fib(int n) { + if(n < 2) + return n; + return fib(n-1) + fib(n-2); + } + }; + +*/ + +class Solution { +public: + int fib(int n) { + if(n < 2) + return n; + vector v(n+1, 0); + v[1] = 1; + for(int i = 2 ; i <= n ; i++) { + v[i] = v[i-1] + v[i-2]; + } + return v[n]; + } +}; diff --git a/cpp/0515-find-the-largest-value-in-each-tree-row.cpp b/cpp/0515-find-the-largest-value-in-each-tree-row.cpp new file mode 100644 index 000000000..350356c6b --- /dev/null +++ b/cpp/0515-find-the-largest-value-in-each-tree-row.cpp @@ -0,0 +1,46 @@ +/* + Given the root of a binary tree, return an array of the largest value in each row of the tree (0-indexed). + + Ex. Input: root = [1,3,2,5,3,null,9] + Output: [1,3,9] + + Time : O(N) + Space : O(N) +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector largestValues(TreeNode* root) { + vector v; + if (!root) + return v; + queue q; + q.push(root); + while (!q.empty()) { + int size = q.size(); + int maxi = INT_MIN; + for (int i = 0; i < size; ++i) { + TreeNode* node = q.front(); + q.pop(); + maxi = max(maxi, node->val); + if (node->left) + q.push(node->left); + if (node->right) + q.push(node->right); + } + v.push_back(maxi); + } + return v; + } +}; diff --git a/cpp/0518-coin-change-ii.cpp b/cpp/0518-coin-change-ii.cpp new file mode 100644 index 000000000..5e5bd0dba --- /dev/null +++ b/cpp/0518-coin-change-ii.cpp @@ -0,0 +1,39 @@ +/* + Given array of coins & an amount, return # of combos that make up this amount + Ex. amount = 5, coins = [1,2,5] -> 4 (5, 2+2+1, 2+1+1+1, 1+1+1+1+1) + + DFS + memo, 2 choices: either try coin & stay at idx, or don't try & proceed + + Time: O(m x n) + Space: O(m x n) +*/ + +class Solution { +public: + int change(int amount, vector& coins) { + return dfs(amount, coins, 0, 0); + } +private: + // {(index, sum) -> # of combos that make up this amount} + map, int> dp; + + int dfs(int amount, vector& coins, int i, int sum) { + if (sum == amount) { + return 1; + } + if (sum > amount) { + return 0; + } + if (i == coins.size()) { + return 0; + } + if (dp.find({i, sum}) != dp.end()) { + return dp[{i, sum}]; + } + + dp[{i, sum}] = dfs(amount, coins, i, sum + coins[i]) + + dfs(amount, coins, i + 1, sum); + + return dp[{i, sum}]; + } +}; diff --git a/cpp/0523-continuous-subarray-sum.cpp b/cpp/0523-continuous-subarray-sum.cpp new file mode 100644 index 000000000..9e5e2453f --- /dev/null +++ b/cpp/0523-continuous-subarray-sum.cpp @@ -0,0 +1,24 @@ +class Solution { +public: + bool checkSubarraySum(vector& nums, int k) { + unordered_mapm; + m[0] = -1; + int sum = 0; + + for(int i = 0;i < nums.size();i++){ + sum += nums[i]; + if(k != 0){ + sum %= k; + } + + if(m.count(sum) > 0){ + if(i - m[sum] > 1) return true; + } + else{ + m[sum] = i; + } + + } + return false; + } +}; diff --git a/cpp/0535-encode-and-decode-tinyurl.cpp b/cpp/0535-encode-and-decode-tinyurl.cpp new file mode 100644 index 000000000..6fe1ca43f --- /dev/null +++ b/cpp/0535-encode-and-decode-tinyurl.cpp @@ -0,0 +1,21 @@ +class Solution { +private: + map encodeMap; + map decodeMap; + string base = "http://tinyurl.com/"; +public: + // Encodes a URL to a shortened URL. + string encode(string longUrl) { + if(!encodeMap.count(longUrl)) { + string shortUrl = base + to_string(encodeMap.size() + 1); + encodeMap[longUrl] = shortUrl; + decodeMap[shortUrl] = longUrl; + } + return encodeMap[longUrl]; + } + + // Decodes a shortened URL to its original URL. + string decode(string shortUrl) { + return decodeMap[shortUrl]; + } +}; diff --git a/cpp/0538-convert-bst-to-greater-tree.cpp b/cpp/0538-convert-bst-to-greater-tree.cpp new file mode 100644 index 000000000..206765964 --- /dev/null +++ b/cpp/0538-convert-bst-to-greater-tree.cpp @@ -0,0 +1,36 @@ +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* convertBST(TreeNode* root) { + int currSum=0; + reversed(root,currSum); + return root; + } + +private: + + // just do a inorder treversal starting from right + // maintain a current sum + // change the root->val to the curr sum + + void reversed(TreeNode* root,int &currSum){ + if(root==NULL){ + return; + } + + reversed(root->right,currSum); + currSum+=root->val; + root->val=currSum; + reversed(root->left,currSum); + } +}; diff --git a/cpp/0540-single-element-in-a-sorted-array.cpp b/cpp/0540-single-element-in-a-sorted-array.cpp new file mode 100644 index 000000000..ed141bc25 --- /dev/null +++ b/cpp/0540-single-element-in-a-sorted-array.cpp @@ -0,0 +1,27 @@ +// Time complexity: O(log n) +// Space complexity: O(1) + +class Solution +{ +public: + int singleNonDuplicate(vector &nums) + { + int left = 0, right = nums.size() - 2; + + while (left <= right) + { + int mid1 = (left + right) >> 1; + int mid2 = mid1 ^ 1; + if (nums[mid1] == nums[mid2]) + { + left = mid1 + 1; + } + else + { + right = mid1 - 1; + } + } + + return nums[left]; + } +}; \ No newline at end of file diff --git a/cpp/0543-diameter-of-binary-tree.cpp b/cpp/0543-diameter-of-binary-tree.cpp new file mode 100644 index 000000000..3addfe793 --- /dev/null +++ b/cpp/0543-diameter-of-binary-tree.cpp @@ -0,0 +1,40 @@ +/* + Given root of binary tree, return length of diameter of tree (longest path b/w any 2 nodes) + + Max path b/w 2 leaf nodes, "1 +" to add path + + Time: O(n) + Space: O(n) +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int diameterOfBinaryTree(TreeNode* root) { + int result = 0; + dfs(root, result); + return result; + } +private: + int dfs(TreeNode* root, int& result) { + if (root == NULL) { + return 0; + } + + int left = dfs(root->left, result); + int right = dfs(root->right, result); + + result = max(result, left + right); + return 1 + max(left, right); + } +}; diff --git a/cpp/0554-brick-wall.cpp b/cpp/0554-brick-wall.cpp new file mode 100644 index 000000000..06ca2028f --- /dev/null +++ b/cpp/0554-brick-wall.cpp @@ -0,0 +1,42 @@ + /* + Approach: + Store the count of the end of the brick for each row in a hash and keep the track + of max number of brick that ends at same position, return rows - max. + + Time complexity : O(n x m) + Space complexity: O(n x m) + + n is number of rows, m is max brick in a row. +*/ + +class Solution { +public: + int leastBricks(vector>& wall) { + + map end_count; + int end_of_brick, max_end_count=0; + + int rows = wall.size(),cols; + + for(int i =0;i& nums, int target) { + int i=0,j=0,count=0,n=size(nums),sum=0; + unordered_mapmp; + while(j true, s2 contains "ba" + + Sliding window, expand + count down char, contract + count up char + + Time: O(n) + Space: O(1) +*/ + +class Solution { +public: + bool checkInclusion(string s1, string s2) { + int m = s1.size(); + int n = s2.size(); + if (m > n) { + return false; + } + + vector count(26); + for (int i = 0; i < m; i++) { + count[s1[i] - 'a']++; + count[s2[i] - 'a']--; + } + if (isPermutation(count)) { + return true; + } + + for (int i = m; i < n; i++) { + count[s2[i] - 'a']--; + count[s2[i - m] - 'a']++; + if (isPermutation(count)) { + return true; + } + } + + return false; + } +private: + bool isPermutation(vector& count) { + for (int i = 0; i < 26; i++) { + if (count[i] != 0) { + return false; + } + } + return true; + } +}; diff --git a/cpp/0572-subtree-of-another-tree.cpp b/cpp/0572-subtree-of-another-tree.cpp new file mode 100644 index 000000000..89b79b427 --- /dev/null +++ b/cpp/0572-subtree-of-another-tree.cpp @@ -0,0 +1,45 @@ +/* + Given the roots of 2 binary trees, return true if a tree has a subtree of the other tree + + Check at each node of the root tree if it's the same as the subRoot tree (structure + values) + + Time: O(m x n) + Space: O(m) +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool isSubtree(TreeNode* root, TreeNode* subRoot) { + if (root == NULL) { + return false; + } + if (isSame(root, subRoot)) { + return true; + } + return isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot); + } +private: + bool isSame(TreeNode* root, TreeNode* subRoot) { + if (root == NULL && subRoot == NULL) { + return true; + } + if (root == NULL || subRoot == NULL) { + return false; + } + if (root->val != subRoot->val) { + return false; + } + return isSame(root->left, subRoot->left) && isSame(root->right, subRoot->right); + } +}; diff --git a/cpp/0605-can-place-flowers.cpp b/cpp/0605-can-place-flowers.cpp new file mode 100644 index 000000000..e3c3e594e --- /dev/null +++ b/cpp/0605-can-place-flowers.cpp @@ -0,0 +1,32 @@ +class Solution { +public: + bool canPlaceFlowers(vector& flowerbed, int n) { + // pre-check, if n is 0 ... return true + if(n == 0){ + return true; + } + + // add a zero to the front and end of FB + flowerbed.insert (flowerbed.begin(),0); + flowerbed.push_back(0); + + // iterate through vector (1, vector -1) (for) + // if prev, curr, and next are 0 + // plant flower (1) + // decrement n + for(int i = 1; i < flowerbed.size() - 1; i++){ + if (flowerbed[i - 1] == 0 && flowerbed[i] == 0 && flowerbed[i+1] == 0){ + flowerbed[i] = 1; + n--; + } + if (n == 0){ + return true; + } + + } + + // return false as else + return false; + + } +}; diff --git a/cpp/0606-construct-string-from-binary-tree.cpp b/cpp/0606-construct-string-from-binary-tree.cpp new file mode 100644 index 000000000..6040122b7 --- /dev/null +++ b/cpp/0606-construct-string-from-binary-tree.cpp @@ -0,0 +1,37 @@ +/* +0606-construct-string-from-binary-tree.cpp + +Algorithm Used: Preorder + +Time Complexity: O(n) +n is number of nodes + +Space Complexity: O(h) +h is height of binary tree + +*/ +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + string tree2str(TreeNode* root) { + if (!root) return ""; + string ans = to_string(root->val); + if (root->right) { + ans = ans + "(" + tree2str(root->left) + ")"; + ans = ans + "(" + tree2str(root->right) + ")"; + } else if (root->left) { + ans = ans + "(" + tree2str(root->left) + ")"; + } + return ans; + } +}; diff --git a/cpp/0617-merge-two-binary-trees.cpp b/cpp/0617-merge-two-binary-trees.cpp new file mode 100644 index 000000000..cb1445f32 --- /dev/null +++ b/cpp/0617-merge-two-binary-trees.cpp @@ -0,0 +1,28 @@ +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) { + if (root1 == NULL && root2 == NULL) + return NULL; + if (root1 != NULL && root2 == NULL) + return root1; + if (root1 == NULL && root2 != NULL) + return root2; + if (root1 != NULL && root2 != NULL) + root1->val += root2->val; + + root1->left = mergeTrees(root1->left, root2->left); + root1->right = mergeTrees(root1->right, root2->right); + return root1; + } +}; \ No newline at end of file diff --git a/cpp/0621-task-scheduler.cpp b/cpp/0621-task-scheduler.cpp new file mode 100644 index 000000000..a6cdc817b --- /dev/null +++ b/cpp/0621-task-scheduler.cpp @@ -0,0 +1,73 @@ +/* + Given array of tasks & cooldown b/w same tasks, return least # of units of time + Ex. tasks = ["A","A","A","B","B","B"] n = 2 -> 8 (A->B->idle->A->B->idle->A->B) + + Key is to determine # of idles, greedy: always arrange task w/ most freq first + 3A, 2B, 1C -> A??A??A -> AB?AB?A -> ABCAB#A, since A most freq, needs most idles + + Time: O(n) + Space: O(1) +*/ +/* +class Solution { +public: + int leastInterval(vector& tasks, int n) { + vector counter(26); + + int maxCount = 0; + int maxCountFrequency = 0; + + for (int i = 0; i < tasks.size(); i++) { + counter[tasks[i] - 'A']++; + int currCount = counter[tasks[i] - 'A']; + + if (maxCount == currCount) { + maxCountFrequency++; + } else if (maxCount < currCount) { + maxCount = currCount; + maxCountFrequency = 1; + } + } + + int partCount = maxCount - 1; + int partLength = n - (maxCountFrequency - 1); + int emptySlots = partCount * partLength; + int availableTasks = tasks.size() - maxCount * maxCountFrequency; + int idles = max(0, emptySlots - availableTasks); + + return tasks.size() + idles; + } +}; +*/ + +// Time O(n * cooldown) +class Solution { +public: + int leastInterval(vector& tasks, int n) { + priority_queue pq; + queue> q; + vector counter(26); + + for (int i = 0; i < tasks.size(); ++i) + ++counter[tasks[i] - 'A']; + for (int i = 0; i < 26; ++i){ + if (counter[i]) + pq.push(counter[i]); + } + + int time = 0; + while (!q.empty() || !pq.empty()){ + ++time; + if (!pq.empty()){ + if (pq.top() - 1) + q.push({pq.top() - 1, time + n}); + pq.pop(); + } + if (!q.empty() && q.front()[1] == time){ + pq.push(q.front()[0]); + q.pop(); + } + } + return time; + } +}; diff --git a/cpp/0637-average-of-levels-in-binary-tree.cpp b/cpp/0637-average-of-levels-in-binary-tree.cpp new file mode 100644 index 000000000..85da4d9aa --- /dev/null +++ b/cpp/0637-average-of-levels-in-binary-tree.cpp @@ -0,0 +1,50 @@ +/* + Given the root of a binary tree, return the average value of the nodes on each level in the form of an array. + Answers within 10-5 of the actual answer will be accepted. + + Ex. Input: root = [3,9,20,null,null,15,7] + Output: [3.00000,14.50000,11.00000] + Explanation: The average value of nodes on level 0 is 3, on level 1 is 14.5, and on level 2 is 11. + Hence return [3, 14.5, 11]. + + Time : O(N) + Space : O(N) +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + vector averageOfLevels(TreeNode* root) { + vector v; + if(root == NULL) + return v; + queue q; + q.push(root); + + while(!q.empty()) { + double sum = 0, count = 0; + int siz = q.size(); + for(int i = 0 ; i < siz ; i++) { + sum += q.front() -> val; + if(q.front() -> left) + q.push(q.front() -> left); + if(q.front() -> right) + q.push(q.front() -> right); + ++count; + q.pop(); + } + v.push_back(sum / count); + } + return v; + } +}; diff --git a/cpp/0647-palindromic-substrings.cpp b/cpp/0647-palindromic-substrings.cpp new file mode 100644 index 000000000..a22bfd513 --- /dev/null +++ b/cpp/0647-palindromic-substrings.cpp @@ -0,0 +1,31 @@ +/* + Given a string, return # of palindromic substrings in it + Ex. s = "babad" -> "bab", s = "cbbd" -> "bb" + + 2 pointers, middle out, check both odd & even sized strings + + Time: O(n^2) + Space: O(1) +*/ + +class Solution { +public: + int countSubstrings(string s) { + int result = 0; + + for (int i = 0; i < s.size(); i++) { + middleOut(s, i, i, result); + middleOut(s, i, i + 1, result); + } + + return result; + } +private: + void middleOut(string s, int i, int j, int& result) { + while (i >= 0 && j < s.size() && s[i] == s[j]) { + result++; + i--; + j++; + } + } +}; diff --git a/cpp/0665-non-decreasing-array.cpp b/cpp/0665-non-decreasing-array.cpp new file mode 100644 index 000000000..181d8d44f --- /dev/null +++ b/cpp/0665-non-decreasing-array.cpp @@ -0,0 +1,25 @@ +class Solution { +public: + bool checkPossibility(vector& nums) { + bool changed = false; + + for(int i = 0; i < nums.size() - 1; i++){ + if(nums[i] <= nums[i+1]){ + continue; + } + if(changed){ + return false; + } + + if(i == 0 || nums[i+1] >= nums[i-1]){ + nums[i] = nums[i+1]; + } + else{ + nums[i+1] = nums[i]; + } + changed = true; + } + + return true; + } +}; diff --git a/cpp/0678-valid-parenthesis-string.cpp b/cpp/0678-valid-parenthesis-string.cpp new file mode 100644 index 000000000..b231c9975 --- /dev/null +++ b/cpp/0678-valid-parenthesis-string.cpp @@ -0,0 +1,33 @@ +class Solution { +public: + bool checkValidString(string s) { + int n = s.size(); + + int balanced = 0; + for(int i=0; i=0; i--) { + if(s[i] == ')' || s[i] == '*') + balanced++; + else + balanced--; + + if(balanced < 0) + return false; + } + + return true; + } +}; \ No newline at end of file diff --git a/cpp/0680-valid-palindrome-ii.cpp b/cpp/0680-valid-palindrome-ii.cpp new file mode 100644 index 000000000..b9d54689b --- /dev/null +++ b/cpp/0680-valid-palindrome-ii.cpp @@ -0,0 +1,24 @@ +class Solution { +private: + bool validPalindromeUtil(string s, int i, int j) { + while(i < j) + if(s[i] == s[j]) { + i += 1; + j -= 1; + } else + return false; + return true; + } +public: + bool validPalindrome(string s) { + int i = 0, j = s.length() - 1; + + while(i < j) + if(s[i] == s[j]) { + i += 1; + j -= 1; + } else + return validPalindromeUtil(s, i + 1, j) || validPalindromeUtil(s, i, j - 1); + return true; + } +}; diff --git a/cpp/0682-baseball-game.cpp b/cpp/0682-baseball-game.cpp new file mode 100644 index 000000000..722589413 --- /dev/null +++ b/cpp/0682-baseball-game.cpp @@ -0,0 +1,41 @@ +class Solution { +public: + int calPoints(vector& ops) { + stack stack; + int sum = 0; + + for (int i = 0; i < ops.size(); i++){ + if (ops[i] == "+"){ + int first = stack.top(); + stack.pop(); + + int second = stack.top(); + + stack.push(first); + + stack.push(first + second); + + sum += first + second; + } + + else if (ops[i] == "D"){ + sum += 2 * stack.top(); + stack.push(2 * stack.top()); + } + + else if (ops[i] == "C"){ + sum -= stack.top(); + stack.pop(); + } + + else{ + sum += stoi(ops[i]); + stack.push(stoi(ops[i])); + } + } + + return sum; + + + } +}; diff --git a/cpp/0684-redundant-connection.cpp b/cpp/0684-redundant-connection.cpp new file mode 100644 index 000000000..14a20d716 --- /dev/null +++ b/cpp/0684-redundant-connection.cpp @@ -0,0 +1,64 @@ +/* + Given undirected graph, return an edge that can be removed to make a tree + Ex. edges = [[1,2],[1,3],[2,3]] -> [2,3] + + If n nodes & n edges, guaranteed a cycle + How to know creating cycle? When connecting a node already connected + Union Find: can find this redundant edge, track parents & ranks + + Time: O(n) + Space: O(n) +*/ + +class Solution { +public: + vector findRedundantConnection(vector>& edges) { + int n = edges.size(); + + vector parents; + vector ranks; + for (int i = 0; i < n + 1; i++) { + parents.push_back(i); + ranks.push_back(1); + + } + + vector result; + for (int i = 0; i < n; i++) { + int n1 = edges[i][0]; + int n2 = edges[i][1]; + if (!doUnion(parents, ranks, n1, n2)) { + result = {n1, n2}; + break; + } + } + return result; + } +private: + int doFind(vector& parents, int n) { + int p = parents[n]; + while (p != parents[p]) { + parents[p] = parents[parents[p]]; + p = parents[p]; + } + return p; + } + + bool doUnion(vector& parents, vector& ranks, int n1, int n2) { + int p1 = doFind(parents, n1); + int p2 = doFind(parents, n2); + if (p1 == p2) { + return false; + } + + if (ranks[p1] > ranks[p2]) { + parents[p2] = p1; + ranks[p1] += ranks[p2]; + } else { + parents[p1] = p2; + ranks[p2] += ranks[p1]; + } + + return true; + } +}; diff --git a/cpp/0695-max-area-of-island.cpp b/cpp/0695-max-area-of-island.cpp new file mode 100644 index 000000000..9be51dc70 --- /dev/null +++ b/cpp/0695-max-area-of-island.cpp @@ -0,0 +1,38 @@ +/* + Given grid where '1' is land & '0' is water, return largest island + + DFS, set visited land to '0' to not visit it again, store biggest + + Time: O(m x n) + Space: O(m x n) +*/ + +class Solution { +public: + int maxAreaOfIsland(vector>& grid) { + int m = grid.size(); + int n = grid[0].size(); + + int result = 0; + + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == 1) { + result = max(result, dfs(grid, i, j, m, n)); + } + } + } + + return result; + } +private: + int dfs(vector>& grid, int i, int j, int m, int n) { + if (i < 0 || i >= m || j < 0 || j >= n || grid[i][j] == 0) { + return 0; + } + grid[i][j] = 0; + + return 1 + dfs(grid, i - 1, j, m, n) + dfs(grid, i + 1, j, m, n) + + dfs(grid, i, j - 1, m, n) + dfs(grid, i, j + 1, m, n); + } +}; diff --git a/cpp/0701-insert-into-a-binary-search-tree.cpp b/cpp/0701-insert-into-a-binary-search-tree.cpp new file mode 100644 index 000000000..4ffe223b1 --- /dev/null +++ b/cpp/0701-insert-into-a-binary-search-tree.cpp @@ -0,0 +1,45 @@ +/* + You are given the root node of a binary search tree (BST) and a value to insert into the tree. + Return the root node of the BST after the insertion. It is guaranteed that the new value does not exist in the original BST. + + Notice that there may exist multiple valid ways for the insertion, as long as the tree remains a BST after insertion. + You can return any of them. + + Ex. Input: root = [4,2,7,1,3], val = 5 + Output: [4,2,7,1,3,5] + + Time : O(N) + Space : O(N) +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + TreeNode * create(int val) { + TreeNode *n = new TreeNode; + n -> val = val; + n -> left = n -> right = NULL; + return n; + } + + TreeNode* insertIntoBST(TreeNode* root, int val) { + if(root == NULL) + return create(val); + + else if(val > root -> val) + root -> right = insertIntoBST(root -> right, val); + else if(val < root -> val) + root -> left = insertIntoBST(root -> left, val); + return root; + } +}; diff --git a/cpp/0703-kth-largest-element-in-a-stream.cpp b/cpp/0703-kth-largest-element-in-a-stream.cpp new file mode 100644 index 000000000..aef3e8f2e --- /dev/null +++ b/cpp/0703-kth-largest-element-in-a-stream.cpp @@ -0,0 +1,39 @@ +/* + Design a class to find the kth largest element in a stream + + Min heap & maintain only k elements, top will always be kth largest + Ex. nums = [6,2,3,1,7], k = 3 -> [1,2,3,6,7] -> [3,6,7] + + Time: O(n log n + m log k) -> n = length of nums, m = add calls + Space: O(n) +*/ + +class KthLargest { +public: + KthLargest(int k, vector& nums) { + this->k = k; + for (int i = 0; i < nums.size(); i++) { + pq.push(nums[i]); + } + while (pq.size() > this->k) { + pq.pop(); + } + } + + int add(int val) { + pq.push(val); + if (pq.size() > k) { + pq.pop(); + } + return pq.top(); + } +private: + int k; + priority_queue, greater> pq; +}; + +/** + * Your KthLargest object will be instantiated and called as such: + * KthLargest* obj = new KthLargest(k, nums); + * int param_1 = obj->add(val); + */ diff --git a/cpp/0704-binary-search.cpp b/cpp/0704-binary-search.cpp new file mode 100644 index 000000000..eef59c8d8 --- /dev/null +++ b/cpp/0704-binary-search.cpp @@ -0,0 +1,30 @@ +/* + Given sorted int array, search for a target value + Ex. nums = [-1,0,3,5,9,12], target = 9 -> 4 (index) + + Since array is sorted, perform binary search + + Time: O(log n) + Space: O(1) +*/ + +class Solution { +public: + int search(vector& nums, int target) { + int low = 0; + int high = nums.size() - 1; + + while (low <= high) { + int mid = low + (high - low) / 2; + if (nums[mid] < target) { + low = mid + 1; + } else if (nums[mid] > target) { + high = mid - 1; + } else { + return mid; + } + } + + return -1; + } +}; diff --git a/cpp/0705-design-hashset.cpp b/cpp/0705-design-hashset.cpp new file mode 100644 index 000000000..ff1780528 --- /dev/null +++ b/cpp/0705-design-hashset.cpp @@ -0,0 +1,25 @@ +// Time: O(n) +// Space: O(n) + +class MyHashSet { +public: + void add(int key) { + if (!contains(key)) { + hashSet.push_back(key); + } + } + + void remove(int key) { + auto k = find(hashSet.begin(), hashSet.end(), key); + if (k != hashSet.end()) { + hashSet.erase(k); + } + } + + bool contains(int key) { + return (find(hashSet.begin(), hashSet.end(), key) != hashSet.end()); + } + +private: + vector hashSet; +}; diff --git a/cpp/0706-design-hashmap.cpp b/cpp/0706-design-hashmap.cpp new file mode 100644 index 000000000..c4b9c5276 --- /dev/null +++ b/cpp/0706-design-hashmap.cpp @@ -0,0 +1,59 @@ +/* + Design a HashMap without using any built-in hash table libraries. + + Designing a hash table using the chaining method, where a vector holds a list of pairs representing the key-value mappings. + + Time: O(n) + Space: O(n) +*/ + +class MyHashMap { +public: + const int N = 10010; + vector>> h; + + /** Initialize your data structure here. */ + MyHashMap() { + h = vector>>(N); + } + + list>::iterator find(int key) { + int t = key % N; + for (auto it = h[t].begin(); it != h[t].end(); it ++ ) { + if (it->first == key) + return it; + } + return h[t].end(); + } + + /** value will always be non-negative. */ + void put(int key, int value) { + auto t = key % N; + auto it = find(key); + if (it == h[t].end()) h[t].push_back({key, value}); + else it->second = value; + } + + /** Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key */ + int get(int key) { + auto t = key % N; + auto it = find(key); + if (it != h[t].end()) return it->second; + return -1; + } + + /** Removes the mapping of the specified value key if this map contains a mapping for the key */ + void remove(int key) { + auto t = key % N; + auto it = find(key); + if (it != h[t].end()) h[t].erase(it); + } +}; + +/** + * Your MyHashMap object will be instantiated and called as such: + * MyHashMap* obj = new MyHashMap(); + * obj->put(key,value); + * int param_2 = obj->get(key); + * obj->remove(key); + */ \ No newline at end of file diff --git a/cpp/0724-find-pivot-index.cpp b/cpp/0724-find-pivot-index.cpp new file mode 100644 index 000000000..e4db0f954 --- /dev/null +++ b/cpp/0724-find-pivot-index.cpp @@ -0,0 +1,24 @@ +class Solution { +public: + int pivotIndex(vector& nums) { + int total; + for(int x: nums){ + total += x; + } + + int leftSum = 0; + int rightSum; + + for(int i = 0; i < nums.size(); i++){ + rightSum = total - nums[i] - leftSum; + + if(leftSum == rightSum){ + return i; + } + + leftSum += nums[i]; + } + + return -1; + } +}; diff --git a/cpp/0735-asteroid-collision.cpp b/cpp/0735-asteroid-collision.cpp new file mode 100644 index 000000000..b6199099d --- /dev/null +++ b/cpp/0735-asteroid-collision.cpp @@ -0,0 +1,30 @@ +class Solution { +public: + vector asteroidCollision(vector& asteroids) { + vector stk; + + for (auto ast: asteroids) + { + while (!stk.empty() && stk.back() > 0 && ast < 0) + { + int diff = ast + stk.back(); + if (diff > 0) + { + ast = 0; + } + else if (diff < 0) + { + stk.pop_back(); + } + else + { + ast = 0; + stk.pop_back(); + } + } + if (ast != 0) + stk.push_back(ast); + } + return stk; + } +}; diff --git a/cpp/0739-daily-temperatures.cpp b/cpp/0739-daily-temperatures.cpp new file mode 100644 index 000000000..e3d273b30 --- /dev/null +++ b/cpp/0739-daily-temperatures.cpp @@ -0,0 +1,37 @@ +/* + Given array of temps, return an array w/ # of days until warmer + Ex. temperature = [73,74,75,71,69,72,76,73] -> [1,1,4,2,1,1,0,0] + + Monotonic decr stack, at each day, compare incr from prev days + + Time: O(n) + Space: O(n) +*/ + +class Solution { +public: + vector dailyTemperatures(vector& temperatures) { + int n = temperatures.size(); + + // pair: [index, temp] + stack> stk; + vector result(n); + + for (int i = 0; i < n; i++) { + int currDay = i; + int currTemp = temperatures[i]; + + while (!stk.empty() && stk.top().second < currTemp) { + int prevDay = stk.top().first; + int prevTemp = stk.top().second; + stk.pop(); + + result[prevDay] = currDay - prevDay; + } + + stk.push({currDay, currTemp}); + } + + return result; + } +}; diff --git a/cpp/0740-delete-and-earn.cpp b/cpp/0740-delete-and-earn.cpp new file mode 100644 index 000000000..d33ae64fc --- /dev/null +++ b/cpp/0740-delete-and-earn.cpp @@ -0,0 +1,18 @@ +class Solution { +public: + int deleteAndEarn(vector& nums) { + int maxnum=0; + int trans[10001]={0}; + for(int i:nums){ + trans[i]+=i; + maxnum=max(maxnum,i); + } + int dp[maxnum+1]; + dp[0]=trans[0]; + dp[1]=max(trans[1],trans[0]); + for(int i=2;i<=maxnum;i++){ + dp[i] = max(dp[i - 1], dp[i - 2] +trans[i]); + } + return dp[maxnum]; + } +}; diff --git a/cpp/0743-network-delay-time.cpp b/cpp/0743-network-delay-time.cpp new file mode 100644 index 000000000..0e80ee662 --- /dev/null +++ b/cpp/0743-network-delay-time.cpp @@ -0,0 +1,63 @@ +/* + Signal sent from node k to network of n nodes, return time for all nodes to receive it + Ex. times = [[2,1,1],[2,3,1],[3,4,1]], n = 4, k = 2 -> 2 + u,v,w -> u = source node, v = target node, w = signal travel time + + Shortest path from node k to every other node, Dijkstra's to find fastest path + + Time: O(V + E log V) + Space: O(V + E) +*/ + +class Solution { +public: + int networkDelayTime(vector>& times, int n, int k) { + vector> adj[n + 1]; + for (int i = 0; i < times.size(); i++) { + int source = times[i][0]; + int dest = times[i][1]; + int time = times[i][2]; + adj[source].push_back({time, dest}); + } + + vector signalReceiveTime(n + 1, INT_MAX); + priority_queue, vector>, greater>> pq; + pq.push({0, k}); + + // time for start node is 0 + signalReceiveTime[k] = 0; + + while (!pq.empty()) { + int currNodeTime = pq.top().first; + int currNode = pq.top().second; + pq.pop(); + + if (currNodeTime > signalReceiveTime[currNode]) { + continue; + } + + // send signal to adjacent nodes + for (int i = 0; i < adj[currNode].size(); i++) { + pair edge = adj[currNode][i]; + int time = edge.first; + int neighborNode = edge.second; + + // fastest signal time for neighborNode so far + if (signalReceiveTime[neighborNode] > currNodeTime + time) { + signalReceiveTime[neighborNode] = currNodeTime + time; + pq.push({signalReceiveTime[neighborNode], neighborNode}); + } + } + } + + int result = INT_MIN; + for (int i = 1; i <= n; i++) { + result = max(result, signalReceiveTime[i]); + } + + if (result == INT_MAX) { + return -1; + } + return result; + } +}; diff --git a/cpp/0746-min-cost-climbing-stairs.cpp b/cpp/0746-min-cost-climbing-stairs.cpp new file mode 100644 index 000000000..f2adde947 --- /dev/null +++ b/cpp/0746-min-cost-climbing-stairs.cpp @@ -0,0 +1,27 @@ +/* + Given cost array, ith step is cost[i], can climb 1 or 2 steps + Return min cost to reach top floor, can start at index 0 or 1 + Ex. cost = [10,15,20] -> 15, start at idx 1, pay 15, climb 2 + + Recursion w/ memoization -> DP, min cost to reach 1/2 steps below curr step + Recurrence relation: minCost[i] = min(minCost[i-1] + cost[i-1], minCost[i-2] + cost[i-2]) + + Time: O(n) + Space: O(1) +*/ + +class Solution { +public: + int minCostClimbingStairs(vector& cost) { + int downOne = 0; + int downTwo = 0; + + for (int i = 2; i <= cost.size(); i++) { + int temp = downOne; + downOne = min(downOne + cost[i - 1], downTwo + cost[i - 2]); + downTwo = temp; + } + + return downOne; + } +}; diff --git a/cpp/0763-partition-labels.cpp b/cpp/0763-partition-labels.cpp new file mode 100644 index 000000000..ed728297a --- /dev/null +++ b/cpp/0763-partition-labels.cpp @@ -0,0 +1,38 @@ +/* + Partition string so each letter appears in at most 1 part, return sizes + Ex. s = "ababcbacadefegdehijhklij" -> [9,7,8] + + Greedy: determine last occurrence of each char, then loop thru & get sizes + + Time: O(n) + Space: O(1) +*/ + +class Solution { +public: + vector partitionLabels(string s) { + int n = s.size(); + // {char -> last index in s} + vector lastIndex(26); + for (int i = 0; i < n; i++) { + lastIndex[s[i] - 'a'] = i; + } + + int size = 0; + int end = 0; + + vector result; + + for (int i = 0; i < n; i++) { + size++; + // constantly checking for further indices if possible + end = max(end, lastIndex[s[i] - 'a']); + if (i == end) { + result.push_back(size); + size = 0; + } + } + + return result; + } +}; diff --git a/cpp/0767-reorganize-string.cpp b/cpp/0767-reorganize-string.cpp new file mode 100644 index 000000000..1526a168b --- /dev/null +++ b/cpp/0767-reorganize-string.cpp @@ -0,0 +1,44 @@ +// Time: O(NlogN) +// Space: O(N) + +class Solution { +public: + string reorganizeString(string s) { + string res=""; + + unordered_map mp; + priority_queue> maxh; + + for(auto ch : s) + mp[ch] += 1; + + for(auto m : mp) + maxh.push(make_pair(m.second, m.first)); + + while(maxh.size() > 1){ + auto top1= maxh.top(); + maxh.pop(); + auto top2 = maxh.top(); + maxh.pop(); + + res += top1.second; + res += top2.second; + + if(--top1.first > 0) + maxh.push(top1); + + if(--top2.first > 0) + maxh.push(top2); + } + + if(!maxh.empty()){ + if(maxh.top().first > 1) + return ""; + + else + res += maxh.top().second; + } + + return res; + } +}; diff --git a/cpp/0778-swim-in-rising-water.cpp b/cpp/0778-swim-in-rising-water.cpp new file mode 100644 index 000000000..7ad10d9a0 --- /dev/null +++ b/cpp/0778-swim-in-rising-water.cpp @@ -0,0 +1,54 @@ +/* + Given an integer elevation matrix, rain falls, at time t, depth everywhere is t + Can swim iff elevation at most t, return least time get from top left to bottom right + + Shortest path w/ min heap: at every step, find lowest water level to move forward + + Time: O(n^2 log n) + Space: O(n^2) +*/ + +class Solution { +public: + int swimInWater(vector>& grid) { + int n = grid.size(); + if (n == 1) { + return 0; + } + + vector> visited(n, vector(n)); + visited[0][0] = true; + + int result = max(grid[0][0], grid[n - 1][n - 1]); + + priority_queue, vector>, greater>> pq; + pq.push({result, 0, 0}); + + while (!pq.empty()) { + vector curr = pq.top(); + pq.pop(); + + result = max(result, curr[0]); + + for (int i = 0; i < 4; i++) { + int x = curr[1] + dirs[i][0]; + int y = curr[2] + dirs[i][1]; + + if (x < 0 || x >= n || y < 0 || y >= n || visited[x][y]) { + continue; + } + + if (x == n - 1 && y == n - 1) { + return result; + } + + pq.push({grid[x][y], x, y}); + visited[x][y] = true; + } + } + + return -1; + } +private: + vector> dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; +}; diff --git a/cpp/0787-cheapest-flights-within-k-stops.cpp b/cpp/0787-cheapest-flights-within-k-stops.cpp new file mode 100644 index 000000000..b8645e6db --- /dev/null +++ b/cpp/0787-cheapest-flights-within-k-stops.cpp @@ -0,0 +1,115 @@ +/** + This function uses the Bellman-Ford algorithm to find the cheapest price from source (src) to destination (dst) + with at most k stops allowed. It iteratively relaxes the edges for k+1 iterations, updating the minimum + cost to reach each vertex. The final result is the minimum cost to reach the destination, or -1 if the + destination is not reachable within the given constraints. + + Space Complexity: O(n) - space used for the prices array. + Time Complexity: O(k * |flights|) - k iterations, processing all flights in each iteration. + */ +class Solution { +public: + int findCheapestPrice(int n, vector>& flights, int src, int dst, int k) { + vector prices(n, INT_MAX); + prices[src] = 0; + + // Perform k+1 iterations of Bellman-Ford algorithm. + for (int i = 0; i < k + 1; i++) { + vector tmpPrices(begin(prices), end(prices)); + + for (auto it : flights) { + int s = it[0]; + int d = it[1]; + int p = it[2]; + + if (prices[s] == INT_MAX) continue; + + if (prices[s] + p < tmpPrices[d]) { + tmpPrices[d] = prices[s] + p; + } + } + prices = tmpPrices; + } + return prices[dst] == INT_MAX ? -1 : prices[dst]; + } +}; + + +/* + Given cities connected by flights [from,to,price], also given src, dst, & k: + Return cheapest price from src to dst with at most k stops + + Dijkstra's but modified, normal won't work b/c will discard heap nodes w/o finishing + Modify: need to re-consider a node if dist from source is shorter than what we recorded + But, if we encounter node already processed but # of stops from source is lesser, + Need to add it back to the heap to be considered again + + Time: O(V^2 log V) -> V = number of cities + Space: O(V^2) +*/ + +class Solution { +public: + int findCheapestPrice(int n, vector>& flights, int src, int dst, int k) { + // build adjacency matrix + vector> adj(n, vector(n)); + for (int i = 0; i < flights.size(); i++) { + vector flight = flights[i]; + adj[flight[0]][flight[1]] = flight[2]; + } + + // shortest distances + vector distances(n, INT_MAX); + distances[src] = 0; + // shortest steps + vector currStops(n, INT_MAX); + currStops[src] = 0; + + // priority queue -> (cost, node, stops) + priority_queue, vector>, greater>> pq; + pq.push({0, src, 0}); + + while (!pq.empty()) { + int cost = pq.top()[0]; + int node = pq.top()[1]; + int stops = pq.top()[2]; + pq.pop(); + + // if destination is reached, return cost to get here + if (node == dst) { + return cost; + } + + // if no more steps left, continue + if (stops == k + 1) { + continue; + } + + // check & relax all neighboring edges + for (int neighbor = 0; neighbor < n; neighbor++) { + if (adj[node][neighbor] > 0) { + int currCost = cost; + int neighborDist = distances[neighbor]; + int neighborWeight = adj[node][neighbor]; + + // check if better cost + int currDist = currCost + neighborWeight; + if (currDist < neighborDist || stops + 1 < currStops[neighbor]) { + pq.push({currDist, neighbor, stops + 1}); + distances[neighbor] = currDist; + currStops[neighbor] = stops; + } else if (stops < currStops[neighbor]) { + // check if better steps + pq.push({currDist, neighbor, stops + 1}); + } + currStops[neighbor] = stops; + } + } + } + + if (distances[dst] == INT_MAX) { + return -1; + } + return distances[dst]; + } +}; \ No newline at end of file diff --git a/cpp/0838-push-dominoes.cpp b/cpp/0838-push-dominoes.cpp new file mode 100644 index 000000000..fa0ddb478 --- /dev/null +++ b/cpp/0838-push-dominoes.cpp @@ -0,0 +1,39 @@ +/* + Time: O(n) + Space: O(n) +*/ + +class Solution { +public: + string pushDominoes(string dominoes) { + + string res = ""; + char prev; + int n = dominoes.size(), count = 1; + + vector left(n, 0), right(n, 0); + for (int i = 0; i < n; i++) { + if (dominoes[i] == 'R') { count = 1; prev = 'R'; } + else if (dominoes[i] != '.') prev = dominoes[i]; + if (prev == 'R' && dominoes[i] == '.') right[i] = count++; + } + + prev = '.'; + for (int i = n-1; i >= 0; i--) { + if (dominoes[i] == 'L') { count = 1; prev = 'L'; } + else if (dominoes[i] != '.') prev = dominoes[i]; + if (prev == 'L' && dominoes[i] == '.') left[i] = count++; + } + + for (int i = 0; i < n; i++) { + if (!left[i] && !right[i]) res += dominoes[i]; + else if (!left[i]) res += 'R'; + else if (!right[i]) res += 'L'; + else if (left[i] == right[i]) res += '.'; + else if (left[i] < right[i]) res += 'L'; + else res += 'R'; + } + + return res; + } +}; diff --git a/cpp/0846-hand-of-straights.cpp b/cpp/0846-hand-of-straights.cpp new file mode 100644 index 000000000..f237e9b05 --- /dev/null +++ b/cpp/0846-hand-of-straights.cpp @@ -0,0 +1,41 @@ +/* + Given int array, return true if can rearrange cards into groupSize consecutive + Ex. hand = [1,2,3,6,2,3,4,7,8], groupSize = 3 -> true, [1,2,3],[2,3,4],[6,7,8] + + Loop thru ordered map, for a value, check groupSize consecutive & remove + + Time: O(n log n) + Space: O(n) +*/ + +class Solution { +public: + bool isNStraightHand(vector& hand, int groupSize) { + int n = hand.size(); + + if (n % groupSize != 0) { + return false; + } + + // map {card value -> count} + map m; + for (int i = 0; i < n; i++) { + m[hand[i]]++; + } + + while (!m.empty()) { + int curr = m.begin()->first; + for (int i = 0; i < groupSize; i++) { + if (m[curr + i] == 0) { + return false; + } + m[curr + i]--; + if (m[curr + i] < 1) { + m.erase(curr + i); + } + } + } + + return true; + } +}; diff --git a/cpp/0853-car-fleet.cpp b/cpp/0853-car-fleet.cpp new file mode 100644 index 000000000..679806513 --- /dev/null +++ b/cpp/0853-car-fleet.cpp @@ -0,0 +1,37 @@ +/* + n cars 1 road, diff pos/speeds, faster cars slow down -> car fleet, return # fleets + Ex. target = 12, pos = [10,8,0,5,3], speeds = [2,4,1,1,3] -> 3 (10 & 8, 0, 5 & 3) + + Sort by start pos, calculate time for each car to finish, loop backwards + If car behind finishes faster then catches up to fleet, else creates new fleet + + Time: O(n log n) + Speed: O(n) +*/ + +class Solution { +public: + int carFleet(int target, vector& position, vector& speed) { + int n = position.size(); + + vector> cars; + for (int i = 0; i < n; i++) { + double time = (double) (target - position[i]) / speed[i]; + cars.push_back({position[i], time}); + } + sort(cars.begin(), cars.end()); + + double maxTime = 0.0; + int result = 0; + + for (int i = n - 1; i >= 0; i--) { + double time = cars[i].second; + if (time > maxTime) { + maxTime = time; + result++; + } + } + + return result; + } +}; diff --git a/cpp/0875-koko-eating-bananas.cpp b/cpp/0875-koko-eating-bananas.cpp new file mode 100644 index 000000000..b7116cbbf --- /dev/null +++ b/cpp/0875-koko-eating-bananas.cpp @@ -0,0 +1,41 @@ +/* + Given array of banana piles, guards are gone for h hours + Return min int k such that can eat all banans within h + Ex. piles = [3,6,7,11] h = 8 -> 4 (1@3, 2@6, 2@7, 3@11) + + Binary search, for each k count hours needed, store min + + Time: O(n x log m) -> n = # of piles, m = max # in a pile + Space: O(1) +*/ + +class Solution { +public: + int minEatingSpeed(vector& piles, int h) { + int n = piles.size(); + + int low = 1; + int high = 0; + for (int i = 0; i < n; i++) { + high = max(high, piles[i]); + } + + int result = high; + + while (low <= high) { + int k = low + (high - low) / 2; + long int hours = 0; + for (int i = 0; i < n; i++) { + hours += ceil((double) piles[i] / k); + } + if (hours <= h) { + result = min(result, k); + high = k - 1; + } else { + low = k + 1; + } + } + + return result; + } +}; diff --git a/cpp/0876-middle-of-the-linked-list.cpp b/cpp/0876-middle-of-the-linked-list.cpp new file mode 100644 index 000000000..6b00c50f4 --- /dev/null +++ b/cpp/0876-middle-of-the-linked-list.cpp @@ -0,0 +1,29 @@ +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +class Solution { +public: + ListNode* middleNode(ListNode* head) { + /* + The slow and fast pointer algorithm is used to find the middle + of a linked list by iterating through the list with a slow + pointer that moves one step at a time and a fast pointer that + moves two steps at a time. When the fast pointer reaches the + end of the list, the slow pointer will be at the midpoint of the list. + */ + ListNode *slow_pointer = head, *fast_pointer = head; + while (fast_pointer != NULL && fast_pointer->next != NULL) { + slow_pointer = slow_pointer->next; + fast_pointer = fast_pointer->next->next; + } + return slow_pointer; + } +}; diff --git a/cpp/0877-stone-game.cpp b/cpp/0877-stone-game.cpp new file mode 100644 index 000000000..89dede079 --- /dev/null +++ b/cpp/0877-stone-game.cpp @@ -0,0 +1,40 @@ +/* + Alice and Bob play a game with piles of stones. There are an even number of piles arranged + in a row, and each pile has a positive integer number of stones piles[i]. + + The objective of the game is to end with the most stones. The total number of stones across + all the piles is odd, so there are no ties. + + Alice and Bob take turns, with Alice starting first. Each turn, a player takes the entire pile + of stones either from the beginning or from the end of the row. This continues until there are + no more piles left, at which point the person with the most stones wins. + + Assuming Alice and Bob play optimally, return true if Alice wins the game, or false if Bob wins. + + Ex: Input: piles = [5,3,4,5] + Output: true + Explanation: + Alice starts first, and can only take the first 5 or the last 5. + Say she takes the first 5, so that the row becomes [3, 4, 5]. + If Bob takes 3, then the board is [4, 5], and Alice takes 5 to win with 10 points. + If Bob takes the last 5, then the board is [3, 4], and Alice takes 4 to win with 9 points. + This demonstrated that taking the first 5 was a winning move for Alice, so we return true. + + Space: O(n/2) + Time : O(1) +*/ + + +class Solution { +public: + bool stoneGame(vector& piles) { + int alice = 0, bob = 0, size = piles.size(); + for(int i = 0 ; i < size/2; i++) { + alice = alice + max( piles[i], piles[size - 1- i] ); + bob = bob + min( piles[i], piles[size - 1- i] ); + } + if(alice > bob) + return true; + return false; + } +}; diff --git a/cpp/0881-boats-to-save-people.cpp b/cpp/0881-boats-to-save-people.cpp new file mode 100644 index 000000000..511967cfe --- /dev/null +++ b/cpp/0881-boats-to-save-people.cpp @@ -0,0 +1,38 @@ +/* + Given an array of people's weight, people[i] is the weight of the ith person, + and there is an infinite number of boats where each boat can carry a maximum weight of limit. + Each boat carries at most two people at the same time, provided the sum of the weight of those people is at most limit. + Return minimum number of boats to carry every given person. + + sort, while there is people to carry + carry the heaviest and lightest person provided their weight doesn't exceed limit. + otherwise, carry the heaviest person only + + Time: O(n) + Space: O(1) +*/ + +class Solution { +public: + int numRescueBoats(vector& people, int limit) { + sort(people.begin(), people.end()); + + int boatRequired = 0; + int lightestPerson = 0; + int heaviestPerson = people.size()-1; + + //WHILE THERE IS SOMEONE TO CARRY + while (lightestPerson <= heaviestPerson){ + if(people[lightestPerson] + people[heaviestPerson] <= limit){ + --heaviestPerson; + ++lightestPerson; + } + else{ + --heaviestPerson; + } + ++boatRequired; + } + + return boatRequired; + } +}; diff --git a/cpp/0895-maximum-frequency-stack.cpp b/cpp/0895-maximum-frequency-stack.cpp new file mode 100644 index 000000000..f454ae3da --- /dev/null +++ b/cpp/0895-maximum-frequency-stack.cpp @@ -0,0 +1,23 @@ +class FreqStack { + unordered_map cnt; + vector> stacks; + +public: + void push(int val) { + int k = ++cnt[val]; + if (k > stacks.size()) + stacks.push_back({val}); + else + stacks[k - 1].push_back(val); + } + + int pop() { + int val = stacks.back().back(); + stacks.back().pop_back(); + if (stacks.back().empty()) + stacks.pop_back(); + --cnt[val]; + return val; + } +}; + diff --git a/cpp/0901-online-stock-span.cpp b/cpp/0901-online-stock-span.cpp new file mode 100644 index 000000000..fa7cf6bbf --- /dev/null +++ b/cpp/0901-online-stock-span.cpp @@ -0,0 +1,30 @@ +class StockSpanner { +public: + + stack> st; + pair pr; + StockSpanner() { + pr = {0, 0}; + } + + int next(int price) { + + int ret = 1; + while (!st.empty() && price >= pr.first) + { + ret += pr.second; + st.pop(); + if (!st.empty()) + pr = st.top(); + } + st.push(make_pair(price, ret)); + pr = st.top(); + return (ret); + } +}; + +/** + * Your StockSpanner object will be instantiated and called as such: + * StockSpanner* obj = new StockSpanner(); + * int param_1 = obj->next(price); + */ diff --git a/cpp/0904-fruit-into-baskets.cpp b/cpp/0904-fruit-into-baskets.cpp new file mode 100644 index 000000000..0f10ed9f3 --- /dev/null +++ b/cpp/0904-fruit-into-baskets.cpp @@ -0,0 +1,20 @@ +class Solution { +public: + int totalFruit(vector& fruits) { + unordered_map count; + int l = 0, res = 0; + + for (int r = 0; r < fruits.size(); r++) { + count[fruits[r]]++; + + while (count.size() > 2) { + count[fruits[l]]--; + if (count[fruits[l]] == 0) + count.erase(fruits[l]); + l++; + } + res = max(res, r - l + 1); + } + return res; + } +}; diff --git a/cpp/0912-sort-an-array.cpp b/cpp/0912-sort-an-array.cpp new file mode 100644 index 000000000..53f16fdde --- /dev/null +++ b/cpp/0912-sort-an-array.cpp @@ -0,0 +1,52 @@ +/* + Given an array of integers nums, sort the array in ascending order and return it. + Ex. nums = [5,2,3,1] -> [1,2,3,5] + + Use Merge sort to sort the array. + + Time - O(nlogn) + Space - O(n) +*/ + +class Solution { + private: + void merge(vector &nums, int low, int mid, int high) { + if(low >= high) + return; + + int l = low, r = mid + 1, k = 0, size = high - low + 1; + vector sorted(size, 0); + + while (l <= mid and r <= high){ + if(nums[l] < nums[r]) + sorted[k++] = nums[l++]; + else + sorted[k++] = nums[r++]; + } + + while(l <= mid) + sorted[k++] = nums[l++]; + while(r <= high) + sorted[k++] = nums[r++]; + + for(k = 0; k < size; k++) + nums[k + low] = sorted[k]; + } + + void mergeSort(vector& nums, int low, int high){ + if(low >= high) + return; + + int mid = low + (high - low) / 2; + + mergeSort(nums, low, mid); + mergeSort(nums, mid + 1, high); + merge(nums, low, mid, high); + } + +public: + vector sortArray(vector& nums) { + mergeSort(nums, 0, nums.size()-1); + return nums; + } +}; diff --git a/cpp/0929-unique-email-addresses.cpp b/cpp/0929-unique-email-addresses.cpp new file mode 100644 index 000000000..16a8ad345 --- /dev/null +++ b/cpp/0929-unique-email-addresses.cpp @@ -0,0 +1,15 @@ +class Solution { +public: + int numUniqueEmails(vector& emails) { + set unique_emails; + for(string email: emails) { + string local_name = email.substr(0, email.find('@')); + local_name = local_name.substr(0, email.find('+')); + local_name = regex_replace(local_name, regex("\\."), ""); + string domain_name = email.substr(email.find('@') + 1, email.length()); + email = local_name + '@' + domain_name; + unique_emails.insert(email); + } + return unique_emails.size(); + } +}; diff --git a/cpp/0946-validate-stack-sequences.cpp b/cpp/0946-validate-stack-sequences.cpp new file mode 100644 index 000000000..b328aa3e4 --- /dev/null +++ b/cpp/0946-validate-stack-sequences.cpp @@ -0,0 +1,25 @@ +/* + Given two integer arrays pushed and popped each with distinct values, + return true if this could have been the result of a sequence of push + and pop operations on an initially empty stack, or false otherwise. + + Time: O(n) + Space: O(n) +*/ + +class Solution { +public: + bool validateStackSequences(vector& pushed, vector& popped) { + stack stk; + int i = 0; + for (int num : pushed) { + stk.push(num); + while (!stk.empty() && stk.top() == popped[i]) { + stk.pop(); + ++i; + } + } + return stk.empty(); + } +}; + diff --git a/cpp/0953-verifying-an-alien-dictionary.cpp b/cpp/0953-verifying-an-alien-dictionary.cpp new file mode 100644 index 000000000..827dc2dd9 --- /dev/null +++ b/cpp/0953-verifying-an-alien-dictionary.cpp @@ -0,0 +1,99 @@ +/* + Given list of words in another language, return string such that: + Letters are sorted in lexicographical incr order wrt this language + Ex. words = ["wrt","wrf","er","ett","rftt"] + + Build graph + record edges, BFS + topological sort, check cyclic + + Time: O(n) + Space: O(n) +*/ + +class Solution { +public: + string alienOrder(vector &words) { + + unordered_map> graph; + unordered_map indegree; + + // indegree make all char 0 + for(auto word : words){ + for(auto c : word){ + indegree[c]=0; + } + } + + for(int i=0; i set; + + if(graph.find(ch1) != graph.end()){ + set = graph[ch1]; + + if(set.find(ch2) == set.end()){ + set.insert(ch2); + indegree[ch2]++; + graph[ch1] = set; + } + } + else{ + set.insert(ch2); + indegree[ch2]++; + graph[ch1] = set; + } + + flag = true; + break; + } + + } + + if(flag == false and (curr.length() > next.length())) return ""; + } + + priority_queue, greater> q; + + for(auto it : indegree){ + if(it.second == 0){ + //cout<0){ + auto rem = q.top(); + q.pop(); + + ans += rem; + count++; + + if(graph.find(rem) != graph.end()){ + unordered_set nbrs = graph[rem]; + + for(auto nbr : nbrs){ + indegree[nbr]--; + if(indegree[nbr] == 0){ + q.push(nbr); + } + } + } + } + + if(count == indegree.size()){ + return ans; + } + return ""; + } +}; diff --git a/cpp/0958-check-completeness-of-a-binary-tree.cpp b/cpp/0958-check-completeness-of-a-binary-tree.cpp new file mode 100644 index 000000000..260ad6714 --- /dev/null +++ b/cpp/0958-check-completeness-of-a-binary-tree.cpp @@ -0,0 +1,49 @@ +/* + Given the root of a binary tree, determine if it is a complete binary tree. + + In a complete binary tree, every level, except possibly the last, is completely filled, + and all nodes in the last level are as far left as possible. It can have between 1 and 2h + nodes inclusive at the last level h. + + Ex. Input: root = [1,2,3,4,5,6] + Output: true + Explanation: Every level before the last is full (ie. levels with node-values {1} and {2, 3}), + and all nodes in the last level ({4, 5, 6}) are as far left as possible. + + Time : O(N); + Space : O(N); +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + bool isCompleteTree(TreeNode* root) { + queue q; + q.push(root); + bool isNull = false; + + while(!q.empty()) { + TreeNode * front = q.front(); + if(!front) + isNull = true; + else { + if(isNull) + return false; + q.push(front -> left); + q.push(front -> right); + } + q.pop(); + } + return true; + } +}; diff --git a/cpp/0973-k-closest-points-to-origin.cpp b/cpp/0973-k-closest-points-to-origin.cpp new file mode 100644 index 000000000..398e50e58 --- /dev/null +++ b/cpp/0973-k-closest-points-to-origin.cpp @@ -0,0 +1,111 @@ +/* + Given array of points & an int k, return k closest points to (0, 0) + Ex. points = [[1,3],[-2,2]], k = 1 -> [[-2,2]] + + Quickselect, partition until pivot = k, left side all < k + + Time: O(k log n) + Space: O(n) +*/ + +// class Solution { +// public: +// vector> kClosest(vector>& points, int k) { +// priority_queue>> pq; +// for (int i = 0; i < points.size(); i++) { +// double distance = sqrt(pow(points[i][0], 2) + pow(points[i][1], 2)); +// pq.push({distance, points[i]}); +// if (pq.size() > k) { +// pq.pop(); +// } +// } + +// vector> result; +// while(!pq.empty()) { +// result.push_back(pq.top().second); +// pq.pop(); +// } + +// return result; +// } +// }; +/* +class Solution { +public: + vector> kClosest(vector>& points, int k) { + int low = 0; + int high = points.size() - 1; + int pivotIndex = points.size(); + + while (pivotIndex != k) { + pivotIndex = partition(points, low, high); + if (pivotIndex < k) { + low = pivotIndex; + } else { + high = pivotIndex - 1; + } + } + + return vector>(points.begin(), points.begin() + k); + } +private: + int partition(vector>& points, int low, int high) { + vector pivot = points[low + (high - low) / 2]; + int pivotDistance = getDistance(pivot); + + while (low < high) { + if (getDistance(points[low]) >= pivotDistance) { + swap(points[low], points[high]); + high--; + } else { + low++; + } + } + + if (getDistance(points[low]) < pivotDistance) { + low++; + } + return low; + } + + int getDistance(vector& point) { + return pow(point[0], 2) + pow(point[1], 2); + } +}; +*/ + +/* +// O(n logn) solution using sorting +class Solution { +public: + vector> kClosest(vector>& points, int k) { + vector> res(k); + sort(points.begin(), points.end(), [](vector& p1, vector& p2){ + int dist_p1 = pow(p1[0],2) + pow(p1[1],2); + int dist_p2 = pow(p2[0],2) + pow(p2[1],2); + return dist_p1 < dist_p2; + }); + copy(points.begin(), points.begin() + k, res.begin()); + return res; + } +}; +*/ + +// O(k logn) solution +class Solution { +public: + vector> kClosest(vector>& points, int k) { + vector> triples; + for (auto& p : points) + triples.push_back({p[0] * p[0] + p[1] * p[1], p[0], p[1]}); + // Min heap of vectors (triples). This constructor takes O(n) time (n = len(v)) + priority_queue, vector>, greater>> pq(triples.begin(), triples.end()); + vector> res; + while (k--){ + vector el = pq.top(); + pq.pop(); + res.push_back({el[1], el[2]}); + } + return res; + } +}; diff --git a/cpp/0977-squares-of-a-sorted-array.cpp b/cpp/0977-squares-of-a-sorted-array.cpp new file mode 100644 index 000000000..f41a67987 --- /dev/null +++ b/cpp/0977-squares-of-a-sorted-array.cpp @@ -0,0 +1,24 @@ +/* + Given an integer array nums sorted in non-decreasing order, return + an array of the squares of each number sorted in non-decreasing order. + + Ex. + Input: nums = [-4,-1,0,3,10] + Output: [0,1,9,16,100] + + 1.- Multiply each number of the nums by themselves. + 2.- Use the sort function to sort the vector. + + Time: O(NlogN) + Space: O(N) +*/ + +class Solution { +public: + vector sortedSquares(vector& nums) { + for (int& i : nums) + i *= i; + sort(nums.begin(), nums.end()); + return nums; + } +}; diff --git a/cpp/0981-time-based-key-value-store.cpp b/cpp/0981-time-based-key-value-store.cpp new file mode 100644 index 000000000..b82391423 --- /dev/null +++ b/cpp/0981-time-based-key-value-store.cpp @@ -0,0 +1,53 @@ +/* + Design time-based key-value structure, multiple vals at diff times + + Hash map, since timestamps are naturally in order, binary search + + Time: O(log n) + Space: O(n) +*/ + +class TimeMap { +public: + TimeMap() { + + } + + void set(string key, string value, int timestamp) { + m[key].push_back({timestamp, value}); + } + + string get(string key, int timestamp) { + if (m.find(key) == m.end()) { + return ""; + } + + int low = 0; + int high = m[key].size() - 1; + + while (low <= high) { + int mid = low + (high - low) / 2; + if (m[key][mid].first < timestamp) { + low = mid + 1; + } else if (m[key][mid].first > timestamp) { + high = mid - 1; + } else { + return m[key][mid].second; + } + } + + if (high >= 0) { + return m[key][high].second; + } + return ""; + } +private: + unordered_map>> m; +}; + +/** + * Your TimeMap object will be instantiated and called as such: + * TimeMap* obj = new TimeMap(); + * obj->set(key,value,timestamp); + * string param_2 = obj->get(key,timestamp); + */ diff --git a/cpp/0983-minimum-cost-for-tickets.cpp b/cpp/0983-minimum-cost-for-tickets.cpp new file mode 100644 index 000000000..b256e6f21 --- /dev/null +++ b/cpp/0983-minimum-cost-for-tickets.cpp @@ -0,0 +1,51 @@ +/* +The days of the year in which you will travel are given as an integer array days. Each day is an integer from 1 to 365. + +Train tickets are sold in three different ways: + +a 1-day pass is sold for costs[0] dollars, +a 7-day pass is sold for costs[1] dollars, and +a 30-day pass is sold for costs[2] dollars. +The passes allow that many days of consecutive travel. + +Return the minimum number of dollars you need to travel every day in the given list of days. + + +Example. For days = [1,4,6,7,8,20] and costs = [2,7,15] we can buy a 1-day pass + for costs[0] = $2, which covers day 1. On day 3 we can buy a 7-day pass + for costs[1] = $7, which covers days 3,4....9. On day 20 we can again buy a + 1-day pass for costs[0] = $2 that will cover the 20th day. So in total we spent + 2 + 7 + 2 = $11 which is the minimum dollars needed for travelling in this case. + + +Time: O(n) +Space: O(n) + +*/ + + +class Solution { +public: + int mincostTickets(vector& days, vector& costs) { + + vector dp(days.size() + 1, 1e9); + dp[days.size()] = 0; + for(int i=days.size()-1; i>=0; i--) { + for(int j=0; j<3; j++) { + if(j == 0) { + auto it = lower_bound(days.begin()+i, days.end(), days[i]+1); + dp[i] = min(dp[i], costs[j] + dp[it-days.begin()]); + } + else if(j == 1) { + auto it = lower_bound(days.begin()+i, days.end(), days[i]+7); + dp[i] = min(dp[i], costs[j] + dp[it-days.begin()]); + } else { + auto it = lower_bound(days.begin()+i, days.end(), days[i]+30); + dp[i] = min(dp[i], costs[j] + dp[it-days.begin()]); + } + } + } + return dp[0]; + + } +}; \ No newline at end of file diff --git a/cpp/0994-rotting-oranges.cpp b/cpp/0994-rotting-oranges.cpp new file mode 100644 index 000000000..b6ea6e00f --- /dev/null +++ b/cpp/0994-rotting-oranges.cpp @@ -0,0 +1,74 @@ +/* + Given grid: 0 empty cell, 1 fresh orange, 2 rotten orange + Return min # of minutes until no cell has a fresh orange + + BFS: rotten will contaminate neighbors first, then propagate out + + Time: O(m x n) + Space: O(m x n) +*/ + +class Solution { +public: + int orangesRotting(vector>& grid) { + int m = grid.size(); + int n = grid[0].size(); + + // build initial set of rotten oranges + queue> q; + int fresh = 0; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == 2) { + q.push({i, j}); + } else if (grid[i][j] == 1) { + fresh++; + } + } + } + // mark the start of a minute + q.push({-1, -1}); + + int result = -1; + + // start rotting process via BFS + while (!q.empty()) { + int row = q.front().first; + int col = q.front().second; + q.pop(); + + if (row == -1) { + // finish 1 minute of processing, mark next minute + result++; + if (!q.empty()) { + q.push({-1, -1}); + } + } else { + // rotten orange, contaminate its neighbors + for (int i = 0; i < dirs.size(); i++) { + int x = row + dirs[i][0]; + int y = col + dirs[i][1]; + + if (x < 0 || x >= m || y < 0 || y >= n) { + continue; + } + + if (grid[x][y] == 1) { + // contaminate + grid[x][y] = 2; + fresh--; + // this orange will now contaminate others + q.push({x, y}); + } + } + } + } + + if (fresh == 0) { + return result; + } + return -1; + } +private: + vector> dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; +}; diff --git a/cpp/1011-capacity-to-ship-packages-within-d-days.cpp b/cpp/1011-capacity-to-ship-packages-within-d-days.cpp new file mode 100644 index 000000000..b728a7e45 --- /dev/null +++ b/cpp/1011-capacity-to-ship-packages-within-d-days.cpp @@ -0,0 +1,53 @@ +/* + Shipping capacity at the least needs to be + as much as the highest weight on the conveyor + belt and at the maximum can be total of all + the weights on the conveyor belt. + + + Time Complexity -> O(nlogn) + Space Complexity -> O(1) +*/ +class Solution { +public: + int shipWithinDays(vector& weights, int days) { + + int high = 0; + int low = 0; + + for (int i = 0; i < weights.size(); i++) { + high += weights[i]; + low = max(low, weights[i]); + } + + int answer = high; + + while (low <= high) { + int mid = low + (high - low) / 2; + + if (canShipWithinDays(weights, days, mid)) { + high = mid - 1; + answer = min(answer, mid); + } else low = mid + 1; + + } + + return answer; + } + +private: + bool canShipWithinDays(vector&weights, int days, int max) { + int sum = 0; + + for (int i = 0; i < weights.size() - 1; i++) { + sum += weights[i]; + + if (sum + weights[i + 1] > max) { + sum = 0; + days--; + } + } + + return days > 0; + } +}; diff --git a/cpp/1024-video-stitching.cpp b/cpp/1024-video-stitching.cpp new file mode 100644 index 000000000..f8f1ff00a --- /dev/null +++ b/cpp/1024-video-stitching.cpp @@ -0,0 +1,42 @@ +/* + You are given a series of video clips from a sporting event that lasted time seconds. These video clips can be overlapping with each other and have varying lengths. + Each video clip is described by an array clips where clips[i] = [starti, endi] indicates that the ith clip started at starti and ended at endi. + We can cut these clips into segments freely. + For example, a clip [0, 7] can be cut into segments [0, 1] + [1, 3] + [3, 7]. + Return the minimum number of clips needed so that we can cut the clips into segments that cover the entire sporting event [0, time]. If the task is impossible, return -1. + + Ex. Input: clips = [[0,2],[4,6],[8,10],[1,9],[1,5],[5,9]], time = 10 + Output: 3 + Explanation: We take the clips [0,2], [8,10], [1,9]; a total of 3 clips. + Then, we can reconstruct the sporting event as follows: + We cut [1,9] into segments [1,2] + [2,8] + [8,9]. + Now we have segments [0,2] + [2,8] + [8,10] which cover the sporting event [0, 10]. + + Time : O(N log N) + Space : O(1) +*/ + +class Solution { +public: + int videoStitching(vector>& clips, int time) { + int maxi = 0; + sort(clips.begin() , clips.end()); + for(int i = 0 ; i < clips.size() ; i++) + maxi = max(maxi, clips[i][1]); + + if(maxi < time) + return -1; + + int count = 0, endTime = 0, covered = INT_MIN; + for(int i = 0; endTime < time ; ) { + ++count; + while(i < clips.size() && clips[i][0] <= endTime) + covered = max(covered, clips[i++][1]); + + if(endTime == covered) + return -1; + endTime = covered; + } + return count; + } +}; diff --git a/cpp/1046-Last-Stone-Weight.cpp b/cpp/1046-Last-Stone-Weight.cpp new file mode 100644 index 000000000..12c70ccdb --- /dev/null +++ b/cpp/1046-Last-Stone-Weight.cpp @@ -0,0 +1,35 @@ +/* + Given array of stones to smash, return smallest possible weight of last stone + If x == y both stones destroyed, if x != y stone x destroyed, stone y = y - x + Ex. stones = [2,7,4,1,8,1] -> 1, [2,4,1,1,1], [2,1,1,1], [1,1,1], [1] + + Max heap, pop 2 biggest, push back difference until no more 2 elements left + + Time: O(n log n) + Space: O(n) +*/ + +class Solution { +public: + int lastStoneWeight(vector& stones) { + priority_queue pq; + for (int i = 0; i < stones.size(); i++) { + pq.push(stones[i]); + } + + while (pq.size() > 1) { + int y = pq.top(); + pq.pop(); + int x = pq.top(); + pq.pop(); + if (y > x) { + pq.push(y - x); + } + } + + if (pq.empty()) { + return 0; + } + return pq.top(); + } +}; diff --git a/cpp/1046-last-stone-weight.cpp b/cpp/1046-last-stone-weight.cpp new file mode 100644 index 000000000..12c70ccdb --- /dev/null +++ b/cpp/1046-last-stone-weight.cpp @@ -0,0 +1,35 @@ +/* + Given array of stones to smash, return smallest possible weight of last stone + If x == y both stones destroyed, if x != y stone x destroyed, stone y = y - x + Ex. stones = [2,7,4,1,8,1] -> 1, [2,4,1,1,1], [2,1,1,1], [1,1,1], [1] + + Max heap, pop 2 biggest, push back difference until no more 2 elements left + + Time: O(n log n) + Space: O(n) +*/ + +class Solution { +public: + int lastStoneWeight(vector& stones) { + priority_queue pq; + for (int i = 0; i < stones.size(); i++) { + pq.push(stones[i]); + } + + while (pq.size() > 1) { + int y = pq.top(); + pq.pop(); + int x = pq.top(); + pq.pop(); + if (y > x) { + pq.push(y - x); + } + } + + if (pq.empty()) { + return 0; + } + return pq.top(); + } +}; diff --git a/cpp/1071-greatest-common-divisor-of-strings.cpp b/cpp/1071-greatest-common-divisor-of-strings.cpp new file mode 100644 index 000000000..3da171c6d --- /dev/null +++ b/cpp/1071-greatest-common-divisor-of-strings.cpp @@ -0,0 +1,47 @@ +/** + * Time Complexity: O(n^2) + * Space Complexity: O(1) + */ + +class Solution { +public: + string gcdOfStrings(string str1, string str2) { + string shortest, longest; + + if(str1.length() < str2.length()){ + shortest = str1; + longest = str2; + } + else{ + shortest = str2; + longest = str1; + } + + string solution = ""; + ushort shortest_length = shortest.length(); + ushort longest_length = longest.length(); + + for(ushort i = shortest_length; i > 0; --i) + { + if (longest_length % i != 0 || shortest_length % i != 0) continue; + + for(ushort j = 0; j < longest_length; ++j) + { + ushort first_pointer = j % i; + ushort second_pointer = j % shortest_length; + + if(shortest[first_pointer] != longest[j] || shortest[second_pointer] != longest[j]) + { + solution = ""; + break; + } + + if(first_pointer == j) solution += longest[j]; + } + + if(solution != "") return solution; + } + + return ""; + } +}; \ No newline at end of file diff --git a/cpp/1091-shortest-path-in-binary-matrix.cpp b/cpp/1091-shortest-path-in-binary-matrix.cpp new file mode 100644 index 000000000..e53b7db7d --- /dev/null +++ b/cpp/1091-shortest-path-in-binary-matrix.cpp @@ -0,0 +1,39 @@ +class Solution { +public: + int shortestPathBinaryMatrix(vector>& grid) { + int n = grid.size(); + if(grid[0][0]==1 || grid[n-1][n-1]==1) return -1; + + // {{r,c},length} + queue,int>> q; + set> visited; + + q.push({{0,0},0}); + visited.insert(make_pair(0,0)); + + int drow[] = {0,1,0,-1,1,-1,1,-1}; + int dcol[] = {1,0,-1,0,1,-1,-1,1}; + + while(!q.empty()){ + int row = q.front().first.first; + int col = q.front().first.second; + int len = q.front().second; + + if(row == n-1 && col == n-1){ + return len+1; + } + + for(int i=0; i<8; i++){ + int nrow = row + drow[i]; + int ncol = col + dcol[i]; + + if(nrow>=0 && nrow=0 && ncol>& trips, int capacity) { + priority_queue, vector>, greater>> minHeap; + for (int i=0; i capacity) + return false; + } + + return true; + } +}; \ No newline at end of file diff --git a/cpp/1095-find-in-mountain-array.cpp b/cpp/1095-find-in-mountain-array.cpp new file mode 100644 index 000000000..2d5946541 --- /dev/null +++ b/cpp/1095-find-in-mountain-array.cpp @@ -0,0 +1,112 @@ +/** + * + * Algorithm: + * - The algorithm's goal is to find the minimum index at which the value "target", + * exist within a "MountainArray". + * - It employs a binary search approach, dividing the array into ascending and + * descending halves. Additionally, it identifies the peak index to determine the + * transition from ascending to descending. + * + * Time Complexity: O(log n) + * Space Complexity: O(1) + * + */ + +/** + * // This is the MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * class MountainArray { + * public: + * int get(int index); + * int length(); + * }; + */ + +class Solution { +public: + int binarySearch(int& begin, int& end, const int& target, MountainArray &mountainArr) + { + while (begin <= end) + { + int mid = begin + (end-begin)/2; + int mid_number = mountainArr.get(mid); + + if(mid_number == target) + { + return mid; + } + else if (mid_number > target) + { + end = --mid; + } + else + { + begin = ++mid; + } + } + return -1; + } + + int reverseBinarySearch(int& begin, int& end, const int& target, MountainArray &mountainArr) + { + while (begin <= end) + { + int mid = begin + (end-begin)/2; + int mid_number = mountainArr.get(mid); + + if(mid_number == target) + { + return mid; + } + else if (mid_number > target) + { + begin = ++mid; + } + else + { + end = --mid; + } + } + return -1; + } + + int findPeakElement(int& begin, int& end, MountainArray &mountainArr) + { + while (begin < end) + { + int mid = begin + (end-begin)/2; + if(mountainArr.get(mid) < mountainArr.get(mid+1)) + { + begin = ++mid; + } + else + { + end = --mid; + } + } + return begin; + } + + int findInMountainArray(int target, MountainArray &mountainArr) { + ios_base::sync_with_stdio(false); + cin.tie(NULL); + + int begin = 0; + int end = mountainArr.length()-1; + int minimum_index = 0; + + int peak_index = findPeakElement(begin, end, mountainArr); + + begin = 0; + end = peak_index; + minimum_index = binarySearch(begin, end, target, mountainArr); + + if(minimum_index != -1) return minimum_index; + + begin = peak_index; + end = mountainArr.length()-1; + minimum_index = reverseBinarySearch(begin, end, target, mountainArr); + + return minimum_index; + } +}; \ No newline at end of file diff --git a/cpp/1137-n-th-tribonacci-number.cpp b/cpp/1137-n-th-tribonacci-number.cpp new file mode 100644 index 000000000..63792e69f --- /dev/null +++ b/cpp/1137-n-th-tribonacci-number.cpp @@ -0,0 +1,19 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int tribonacci(int n) { + int t[] = {0, 1, 1}; + if (n < 3) { + return t[n]; + } + for (int i = 3; i <= n; i++) { + int sum_t = t[0] + t[1] + t[2]; + t[0] = t[1]; + t[1] = t[2]; + t[2] = sum_t; + } + return t[2]; + } +}; diff --git a/cpp/1143-Longest-Common-Subsequence.cpp b/cpp/1143-Longest-Common-Subsequence.cpp new file mode 100644 index 000000000..43cbe1947 --- /dev/null +++ b/cpp/1143-Longest-Common-Subsequence.cpp @@ -0,0 +1,37 @@ +/* + Given 2 strings, return length of longest common subsequence + Ex. text1 = "abcde", text2 = "ace" -> 3, "ace" is LCS + + j + a c e + a 3 + b 2 --> visualization of below, build DP bottom-up + i c 2 + d 1 + e 1 + + Time: O(m x n) + Space: O(m x n) +*/ + +class Solution { +public: + int longestCommonSubsequence(string text1, string text2) { + int m = text1.size(); + int n = text2.size(); + + vector> dp(m + 1, vector(n + 1)); + + for (int i = m - 1; i >= 0; i--) { + for (int j = n - 1; j >= 0; j--) { + if (text1[i] == text2[j]) { + dp[i][j] = 1 + dp[i + 1][j + 1]; + } else { + dp[i][j] = max(dp[i + 1][j], dp[i][j + 1]); + } + } + } + + return dp[0][0]; + } +}; diff --git a/cpp/1143-longest-common-subsequence.cpp b/cpp/1143-longest-common-subsequence.cpp new file mode 100644 index 000000000..43cbe1947 --- /dev/null +++ b/cpp/1143-longest-common-subsequence.cpp @@ -0,0 +1,37 @@ +/* + Given 2 strings, return length of longest common subsequence + Ex. text1 = "abcde", text2 = "ace" -> 3, "ace" is LCS + + j + a c e + a 3 + b 2 --> visualization of below, build DP bottom-up + i c 2 + d 1 + e 1 + + Time: O(m x n) + Space: O(m x n) +*/ + +class Solution { +public: + int longestCommonSubsequence(string text1, string text2) { + int m = text1.size(); + int n = text2.size(); + + vector> dp(m + 1, vector(n + 1)); + + for (int i = m - 1; i >= 0; i--) { + for (int j = n - 1; j >= 0; j--) { + if (text1[i] == text2[j]) { + dp[i][j] = 1 + dp[i + 1][j + 1]; + } else { + dp[i][j] = max(dp[i + 1][j], dp[i][j + 1]); + } + } + } + + return dp[0][0]; + } +}; diff --git a/cpp/1161-maximum-level-sum-of-a-binary-tree.cpp b/cpp/1161-maximum-level-sum-of-a-binary-tree.cpp new file mode 100644 index 000000000..11454fe7b --- /dev/null +++ b/cpp/1161-maximum-level-sum-of-a-binary-tree.cpp @@ -0,0 +1,59 @@ +/* + Given the root of a binary tree, the level of its root is 1, the level of its children is 2, and so on. + Return the smallest level x such that the sum of all the values of nodes at level x is maximal. + + Ex. Input: root = [1,7,0,7,-8,null,null] + Output: 2 + Explanation: + Level 1 sum = 1. + Level 2 sum = 7 + 0 = 7. + Level 3 sum = 7 + -8 = -1. + So we return the level with the maximum sum which is level 2. + + Time : O(N) + Space : O(N) +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int maxLevelSum(TreeNode* root) { + if(!root) + return -1; + + int lvl = 0, maxi = INT_MIN, res; + queue q; + q.push(root); + + while(!q.empty()) { + ++lvl; + int sum = 0; + int size = q.size(); + + while(size--) { + TreeNode * temp = q.front(); + q.pop(); + sum += temp -> val; + if(temp -> left) + q.push(temp -> left); + if(temp -> right) + q.push(temp -> right); + } + if(maxi < sum) { + res = lvl; + maxi = sum; + } + } + return res; + } +}; diff --git a/cpp/1189-maximum-number-of-balloons.cpp b/cpp/1189-maximum-number-of-balloons.cpp new file mode 100644 index 000000000..a8fa405b8 --- /dev/null +++ b/cpp/1189-maximum-number-of-balloons.cpp @@ -0,0 +1,16 @@ +class Solution { +public: + int maxNumberOfBalloons(string text) { + map countText; + map balloon; + for(char c: text) + countText[c]++; + for(char c: std::string("balloon")) + balloon[c]++; + + int res = text.length(); + for(const auto &[key, value]: balloon) + res = min(res, countText[key] / value); + return res; + } +}; diff --git a/cpp/1209-Remove-All-Adjacent-Duplicates-in-String-II.cpp b/cpp/1209-Remove-All-Adjacent-Duplicates-in-String-II.cpp new file mode 100644 index 000000000..26afe7e18 --- /dev/null +++ b/cpp/1209-Remove-All-Adjacent-Duplicates-in-String-II.cpp @@ -0,0 +1,39 @@ +// Time and space complexity is O(n) where n is the size of the input string. +class Solution { +public: + string removeDuplicates(string s, int k) { + stack> st; + + for(int i = 0 ; i < s.size(); i++) + { + int count = 1; + if(!st.empty() && st.top().first == s[i]) + { + count += st.top().second; + st.pop(); + } + + st.push({s[i] , count}); + + if(count == k) st.pop(); + + } + + string ans = ""; + while(!st.empty()) + { + int freq = st.top().second; + int c = st.top().first; + while(freq > 0) + { + ans += c; + freq--; + } + + st.pop(); + } + + reverse(ans.begin() , ans.end()); + return ans; + } +}; diff --git a/cpp/1209-remove-all-adjacent-duplicates-in-string-ii.cpp b/cpp/1209-remove-all-adjacent-duplicates-in-string-ii.cpp new file mode 100644 index 000000000..26afe7e18 --- /dev/null +++ b/cpp/1209-remove-all-adjacent-duplicates-in-string-ii.cpp @@ -0,0 +1,39 @@ +// Time and space complexity is O(n) where n is the size of the input string. +class Solution { +public: + string removeDuplicates(string s, int k) { + stack> st; + + for(int i = 0 ; i < s.size(); i++) + { + int count = 1; + if(!st.empty() && st.top().first == s[i]) + { + count += st.top().second; + st.pop(); + } + + st.push({s[i] , count}); + + if(count == k) st.pop(); + + } + + string ans = ""; + while(!st.empty()) + { + int freq = st.top().second; + int c = st.top().first; + while(freq > 0) + { + ans += c; + freq--; + } + + st.pop(); + } + + reverse(ans.begin() , ans.end()); + return ans; + } +}; diff --git a/cpp/1213-maximum-product-difference-between-two-pairs.cpp b/cpp/1213-maximum-product-difference-between-two-pairs.cpp new file mode 100644 index 000000000..fa74d7a3a --- /dev/null +++ b/cpp/1213-maximum-product-difference-between-two-pairs.cpp @@ -0,0 +1,7 @@ +class Solution{ + public: + int maxProductDifference(vector& nums){ + sort(nums.begin(), nums.end()); + return nums.rbegin()[1] * nums.back() - nums[0] * nums[1]; + } +}; diff --git a/cpp/1220-count-vowels-permutation.cpp b/cpp/1220-count-vowels-permutation.cpp new file mode 100644 index 000000000..45d35ed76 --- /dev/null +++ b/cpp/1220-count-vowels-permutation.cpp @@ -0,0 +1,42 @@ +/* +Given an integer n, our task is to count how many strings of length n can be formed under the following rules: + +Each character is a lower case vowel ('a', 'e', 'i', 'o', 'u') +Each vowel 'a' may only be followed by an 'e'. +Each vowel 'e' may only be followed by an 'a' or an 'i'. +Each vowel 'i' may not be followed by another 'i'. +Each vowel 'o' may only be followed by an 'i' or a 'u'. +Each vowel 'u' may only be followed by an 'a'. +Since the answer may be too large, we have to return it modulo 10^9 + 7. + +Example. For n = 2, Output = 10 + + Explanation: All possible strings of length 2 that can be formed as per the given rules are: "ae", "ea", "ei", "ia", "ie", "io", "iu", + "oi", "ou" and "ua". + So we return 10 as our answer. + + +Time: O(n) +Space: O(1) + +*/ + + +class Solution { +const unsigned int mod = 1e9+7; +public: + int countVowelPermutation(int n) { + vector prev(5,1), curr(5, 0); + for(int i=1; i> st; + + for (int i = 0; i < s.size(); i++) { + if (s[i] == '(') { + st.push(make_pair(s[i], i)); + } else if (s[i] == ')') { + if (!st.empty() and st.top().first == '(') { + st.pop(); + } else { + st.push(make_pair(')', i)); + } + } + } + + while (!st.empty()) { + s.erase(s.begin() + st.top().second); + st.pop(); + } + + return s; + } +}; diff --git a/cpp/1260-shift-2d-grid.cpp b/cpp/1260-shift-2d-grid.cpp new file mode 100644 index 000000000..23f713527 --- /dev/null +++ b/cpp/1260-shift-2d-grid.cpp @@ -0,0 +1,26 @@ +class Solution { +public: + vector> shiftGrid(vector>& grid, int k) { + const int M = grid.size(), N = grid[0].size(); + + auto posToVal = [&] (int r, int c) -> int { + return r * N + c;}; + auto valToPos = [&] (int v) -> int* { + return new int[] {v / N, v % N};}; + + vector> res; + for(int r = 0; r < M; r++) { + vector row; + for(int c = 0; c < N; c++) + row.push_back(0); + res.push_back(row); + } + for(int r = 0; r < M; r++) + for(int c = 0; c < N; c++) { + int newVal = (posToVal(r, c) + k) % (M * N); + int *newRC = valToPos(newVal); + res[newRC[0]][newRC[1]] = grid[r][c]; + } + return res; + } +}; diff --git a/cpp/1299-Replace-Elements-with-Greatest-Element-on-Right-Side.cpp b/cpp/1299-Replace-Elements-with-Greatest-Element-on-Right-Side.cpp new file mode 100644 index 000000000..0116ad06a --- /dev/null +++ b/cpp/1299-Replace-Elements-with-Greatest-Element-on-Right-Side.cpp @@ -0,0 +1,17 @@ +class Solution { +public: + vector replaceElements(vector& arr) { + // O(N) Time Complexity , O(1) Space complexity + int n = arr.size(); + int maxSoFar = arr[n-1]; + arr[n-1] = -1; + + for(int i=n-2;i>=0;i--) + { + int temp = maxSoFar; + if(maxSoFar < arr[i]) maxSoFar = arr[i]; + arr[i] = temp; + } + return arr; + } +}; diff --git a/cpp/1299-replace-elements-with-greatest-element-on-right-side.cpp b/cpp/1299-replace-elements-with-greatest-element-on-right-side.cpp new file mode 100644 index 000000000..0116ad06a --- /dev/null +++ b/cpp/1299-replace-elements-with-greatest-element-on-right-side.cpp @@ -0,0 +1,17 @@ +class Solution { +public: + vector replaceElements(vector& arr) { + // O(N) Time Complexity , O(1) Space complexity + int n = arr.size(); + int maxSoFar = arr[n-1]; + arr[n-1] = -1; + + for(int i=n-2;i>=0;i--) + { + int temp = maxSoFar; + if(maxSoFar < arr[i]) maxSoFar = arr[i]; + arr[i] = temp; + } + return arr; + } +}; diff --git a/cpp/1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.cpp b/cpp/1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.cpp new file mode 100644 index 000000000..476c6637d --- /dev/null +++ b/cpp/1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.cpp @@ -0,0 +1,30 @@ +/* +Using Sliding window. +First we calculate sum of first k elements in vector then we check if the average is greater than or equal to the threshold. +If the condition is met then we append the res variable. +Then we maintain the k size window and traverse over the vector and check for the condition. + +Since we are traversing only once +T.C -> O(N) +S.c -> O(1) +*/ +class Solution { +public: + int numOfSubarrays(vector& arr, int k, int threshold) { + int sum = 0; + int n = arr.size(); + for(int i=0;i= threshold) res++; + while(right < n){ + sum -= arr[left++]; + sum += arr[right++]; + if(sum/k >= threshold) res++; + } + return res; + } +}; \ No newline at end of file diff --git a/cpp/1448-Count-Good-Nodes-In-Binary-Tree.cpp b/cpp/1448-Count-Good-Nodes-In-Binary-Tree.cpp new file mode 100644 index 000000000..e59e372ad --- /dev/null +++ b/cpp/1448-Count-Good-Nodes-In-Binary-Tree.cpp @@ -0,0 +1,41 @@ +/* + Given binary tree, node is "good" if path from root has no nodes > X, return # of "good" + + Maintain greatest value seen so far on a path, if further node >= this max, "good" node + + Time: O(n) + Space: O(n) +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int goodNodes(TreeNode* root) { + int result = 0; + dfs(root, root->val, result); + return result; + } +private: + void dfs(TreeNode* root, int maxSoFar, int& result) { + if (root == NULL) { + return; + } + + if (root->val >= maxSoFar) { + result++; + } + + dfs(root->left, max(maxSoFar, root->val), result); + dfs(root->right, max(maxSoFar, root->val), result); + } +}; diff --git a/cpp/1448-count-good-nodes-in-binary-tree.cpp b/cpp/1448-count-good-nodes-in-binary-tree.cpp new file mode 100644 index 000000000..e59e372ad --- /dev/null +++ b/cpp/1448-count-good-nodes-in-binary-tree.cpp @@ -0,0 +1,41 @@ +/* + Given binary tree, node is "good" if path from root has no nodes > X, return # of "good" + + Maintain greatest value seen so far on a path, if further node >= this max, "good" node + + Time: O(n) + Space: O(n) +*/ + +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode() : val(0), left(nullptr), right(nullptr) {} + * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} + * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} + * }; + */ +class Solution { +public: + int goodNodes(TreeNode* root) { + int result = 0; + dfs(root, root->val, result); + return result; + } +private: + void dfs(TreeNode* root, int maxSoFar, int& result) { + if (root == NULL) { + return; + } + + if (root->val >= maxSoFar) { + result++; + } + + dfs(root->left, max(maxSoFar, root->val), result); + dfs(root->right, max(maxSoFar, root->val), result); + } +}; diff --git a/cpp/1456-maximum-number-of-vowels-in-a-substring-of-given-length.cpp b/cpp/1456-maximum-number-of-vowels-in-a-substring-of-given-length.cpp new file mode 100644 index 000000000..4e23e460a --- /dev/null +++ b/cpp/1456-maximum-number-of-vowels-in-a-substring-of-given-length.cpp @@ -0,0 +1,45 @@ +/* + Given a string s and an integer k, return the maximum number of vowel letters in any substring of s with length k. + Vowel letters in English are 'a', 'e', 'i', 'o', and 'u'. + + Ex. + Input: s = "abciiidef", k = 3 + Output: 3 + Explanation: The substring "iii" contains 3 vowel letters. + + Approach : Sliding Window + + Time : O(N) + Space : O(1) +*/ + + +class Solution { +public: + bool isVowel(char ch) { + if(ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u') + return true; + return false; + } + int maxVowels(string s, int k) { + int ptr1 = 0, ptr2 = 0, count = 0, maxi = INT_MIN; + while(ptr2 < s.size()) { + if(ptr2 - ptr1 == k) { + maxi = max(count, maxi); + if(maxi == k) + return count; + if(isVowel(s[ptr1++])) + count--; + if(isVowel(s[ptr2++])) + count++; + + } else { + if(isVowel(s[ptr2])) + count++; + ptr2++; + } + } + maxi = max(count, maxi); + return maxi; + } +}; diff --git a/cpp/1461-check-if-a-string-contains-all-binary-codes-of-size-k.cpp b/cpp/1461-check-if-a-string-contains-all-binary-codes-of-size-k.cpp new file mode 100644 index 000000000..8dfca3289 --- /dev/null +++ b/cpp/1461-check-if-a-string-contains-all-binary-codes-of-size-k.cpp @@ -0,0 +1,19 @@ +class Solution { +public: + bool hasAllCodes(string s, int k) { + + set all_substrings; + int total = 1 < +#include + +using namespace std; + +class Solution { +public: + int cherryPickup(vector>& grid) { + int rows = grid.size(); + int cols = grid[0].size(); + + vector>> dp(rows, vector>(cols, vector(cols, 0))); + + for (int i = rows - 1; i >= 0; --i) { + for (int j = 0; j < cols; ++j) { + for (int k = 0; k < cols; ++k) { + int cherries = grid[i][j] + (j != k ? grid[i][k] : 0); + if (i == rows - 1) { + dp[i][j][k] = cherries; + } else { + int maxCherries = 0; + for (int dj = -1; dj <= 1; ++dj) { + for (int dk = -1; dk <= 1; ++dk) { + int nj = j + dj; + int nk = k + dk; + if (nj >= 0 && nj < cols && nk >= 0 && nk < cols) { + maxCherries = max(maxCherries, dp[i + 1][nj][nk]); + } + } + } + dp[i][j][k] = cherries + maxCherries; + } + } + } + } + + return dp[0][0][cols - 1]; + } +}; diff --git a/cpp/1466-reorder-routes-to-make-all-paths-lead-to-the-city-zero.cpp b/cpp/1466-reorder-routes-to-make-all-paths-lead-to-the-city-zero.cpp new file mode 100644 index 000000000..fb8bff22f --- /dev/null +++ b/cpp/1466-reorder-routes-to-make-all-paths-lead-to-the-city-zero.cpp @@ -0,0 +1,36 @@ +class Solution { +public: + int minReorder(int n, vector>& connections) { + unordered_map>> graph; + unordered_set visited; + + for (int i = 0; i < n; i++) + graph[i] = vector>{}; + + for (vector connection : connections) { + graph[connection[0]].push_back(make_pair(connection[1], true)); + graph[connection[1]].push_back(make_pair(connection[0], false)); + } + + int cnt = 0; + stack stk; + stk.push(0); + visited.insert(0); + + while (!stk.empty()) { + int u = stk.top(); + stk.pop(); + for (pair v : graph[u]) { + if (visited.count(v.first)) + continue; + stk.push(v.first); + visited.insert(v.first); + if (v.second) + cnt += 1; + } + } + + return cnt; + } +}; + diff --git a/cpp/1470-shuffle-the-array.cpp b/cpp/1470-shuffle-the-array.cpp new file mode 100644 index 000000000..3bc0a9e6e --- /dev/null +++ b/cpp/1470-shuffle-the-array.cpp @@ -0,0 +1,15 @@ +class Solution { +public: + vector shuffle(vector& nums, int n) { + int lp = 0; + int rp = n; + int p = 0; + vector shuffled(2*n); + + while (p < 2 * n) { + shuffled[p++] = nums[lp++]; + shuffled[p++] = nums[rp++]; + } + return shuffled; + } +}; diff --git a/cpp/1498-number-of-subsequences-that-satisfy-the-given-sum-condition.cpp b/cpp/1498-number-of-subsequences-that-satisfy-the-given-sum-condition.cpp new file mode 100644 index 000000000..78f75f15c --- /dev/null +++ b/cpp/1498-number-of-subsequences-that-satisfy-the-given-sum-condition.cpp @@ -0,0 +1,36 @@ +// Time Complexity - O(nlogn) +// Space Complexity - O(n) + +class Solution { +public: + int numSubseq(vector& nums, int target) { + sort(nums.begin(), nums.end()); + int n = nums.size(); + + int left = 0, right = n - 1; + int res = 0, mod = 1e9 + 7; + while (left <= right) { + if (nums[left] + nums[right] > target) { + right--; + } + else { + res = (res + fastPower(2, right - left, mod)) % mod; + left++; + } + } + return res; + } + + int fastPower(int a, int b, int mod) { + long long ans = 1; + long long base = a; + while (b != 0) { + if (b % 2 == 1) { + ans = (ans * base) % mod; + } + base = (base * base) % mod; + b /= 2; + } + return ans; + } +}; diff --git a/cpp/1512-number-of-good-pairs.cpp b/cpp/1512-number-of-good-pairs.cpp new file mode 100644 index 000000000..f3b408c8c --- /dev/null +++ b/cpp/1512-number-of-good-pairs.cpp @@ -0,0 +1,20 @@ +class Solution { + unordered_map Memo = {}; + int numIdenticalPairs(vector & nums){ + unordered_map Memo; + int i, k; + int NGood; + NGood = 0; + Memo = {}; + for(int & i : nums){ + if(Memo.find(i) == Memo.end()){ + Memo.insert(make_pair(i, 1)); + } + else{ + NGood = NGood + Memo[i]; + Memo[i]++; + } + } + return NGood; + } +}; diff --git a/cpp/1514-path-with-maximum-probability.cpp b/cpp/1514-path-with-maximum-probability.cpp new file mode 100644 index 000000000..7b32b20f2 --- /dev/null +++ b/cpp/1514-path-with-maximum-probability.cpp @@ -0,0 +1,38 @@ +class Solution { +public: + double maxProbability(int n, vector>& edges, vector& succProb, int start_node, int end_node) { + // adj list mapping {node -> [(probability, neighborNode), ...]} + unordered_map>> adj; + for (int i=0; i edge = edges[i]; + adj[edge[0]].push_back({succProb[i], edge[1]}); + adj[edge[1]].push_back({succProb[i], edge[0]}); + } + + // maxHeap storing pairs of (probability, node) + priority_queue> pq; + pq.push({1.0, start_node}); + + unordered_set visited; + + // applying Dijkstra's algorithm + while (!pq.empty()) { + double currProb = pq.top().first; + int currNode = pq.top().second; + pq.pop(); + + if (currNode == end_node) + return currProb; + + visited.insert(currNode); + + for (auto& [nextProb, nextNode] : adj[currNode]) { + if (visited.find(nextNode) == visited.end()) { + pq.push({currProb*nextProb, nextNode}); + } + } + } + + return 0.0; + } +}; \ No newline at end of file diff --git a/cpp/1578-minimum-time-to-make-rope-colorful.cpp b/cpp/1578-minimum-time-to-make-rope-colorful.cpp new file mode 100644 index 000000000..d10a706c9 --- /dev/null +++ b/cpp/1578-minimum-time-to-make-rope-colorful.cpp @@ -0,0 +1,21 @@ +class Solution { +public: + int minCost(string colors, vector& neededTime) { + int left = 0, time = 0; + for (int right = 1; right < colors.size(); ++right) { + if (colors[left] == colors[right]) { + if (neededTime[left] < neededTime[right]) { + time += neededTime[left]; + left = right; + } + else { + time += neededTime[right]; + } + } + else { + left = right; + } + } + return time; + } +}; diff --git a/cpp/1584-Min-Cost-To-Connect-All-Points.cpp b/cpp/1584-Min-Cost-To-Connect-All-Points.cpp new file mode 100644 index 000000000..7beb444fb --- /dev/null +++ b/cpp/1584-Min-Cost-To-Connect-All-Points.cpp @@ -0,0 +1,54 @@ +/* + Given array of points, return min cost to connect all points + All points have 1 path b/w them, cost is Manhattan distance + + MST problem, Prim's, greedily pick node not in MST & has smallest edge cost + Add to MST, & for all its neighbors, try to update min dist values, repeat + + Time: O(n^2) + Space: O(n) +*/ + +class Solution { +public: + int minCostConnectPoints(vector>& points) { + int n = points.size(); + + int edgesUsed = 0; + // track visited nodes + vector inMST(n); + vector minDist(n, INT_MAX); + minDist[0] = 0; + + int result = 0; + + while (edgesUsed < n) { + int currMinEdge = INT_MAX; + int currNode = -1; + + // greedily pick lowest cost node not in MST + for (int i = 0; i < n; i++) { + if (!inMST[i] && currMinEdge > minDist[i]) { + currMinEdge = minDist[i]; + currNode = i; + } + } + + result += currMinEdge; + edgesUsed++; + inMST[currNode] = true; + + // update adj nodes of curr node + for (int i = 0; i < n; i++) { + int cost = abs(points[currNode][0] - points[i][0]) + + abs(points[currNode][1] - points[i][1]); + + if (!inMST[i] && minDist[i] > cost) { + minDist[i] = cost; + } + } + } + + return result; + } +}; diff --git a/cpp/1584-min-cost-to-connect-all-points.cpp b/cpp/1584-min-cost-to-connect-all-points.cpp new file mode 100644 index 000000000..7beb444fb --- /dev/null +++ b/cpp/1584-min-cost-to-connect-all-points.cpp @@ -0,0 +1,54 @@ +/* + Given array of points, return min cost to connect all points + All points have 1 path b/w them, cost is Manhattan distance + + MST problem, Prim's, greedily pick node not in MST & has smallest edge cost + Add to MST, & for all its neighbors, try to update min dist values, repeat + + Time: O(n^2) + Space: O(n) +*/ + +class Solution { +public: + int minCostConnectPoints(vector>& points) { + int n = points.size(); + + int edgesUsed = 0; + // track visited nodes + vector inMST(n); + vector minDist(n, INT_MAX); + minDist[0] = 0; + + int result = 0; + + while (edgesUsed < n) { + int currMinEdge = INT_MAX; + int currNode = -1; + + // greedily pick lowest cost node not in MST + for (int i = 0; i < n; i++) { + if (!inMST[i] && currMinEdge > minDist[i]) { + currMinEdge = minDist[i]; + currNode = i; + } + } + + result += currMinEdge; + edgesUsed++; + inMST[currNode] = true; + + // update adj nodes of curr node + for (int i = 0; i < n; i++) { + int cost = abs(points[currNode][0] - points[i][0]) + + abs(points[currNode][1] - points[i][1]); + + if (!inMST[i] && minDist[i] > cost) { + minDist[i] = cost; + } + } + } + + return result; + } +}; diff --git a/cpp/1603-design-parking-system.cpp b/cpp/1603-design-parking-system.cpp new file mode 100644 index 000000000..f77c01432 --- /dev/null +++ b/cpp/1603-design-parking-system.cpp @@ -0,0 +1,16 @@ +class ParkingSystem { +public: + ParkingSystem(int big, int medium, int small) : mCarSpotsLeft{big, medium, small} {} + + bool addCar(int carType) { + if (mCarSpotsLeft[carType - 1] > 0) { + mCarSpotsLeft[carType - 1]--; + return true; + } else { + return false; + } + } + +private: + array mCarSpotsLeft; +}; \ No newline at end of file diff --git a/cpp/1641-count-sorted-vowel-strings.cpp b/cpp/1641-count-sorted-vowel-strings.cpp new file mode 100644 index 000000000..db528956a --- /dev/null +++ b/cpp/1641-count-sorted-vowel-strings.cpp @@ -0,0 +1,23 @@ +class Solution{ + public: + int countVowelStrings(int n){ + int An, En, In, On, Un; + int An1, En1, On1, In1, Un1; + int i; + An1 = En1 = In1 = On1 = Un1 = 1; + i = 1; + while(i < n){ + An = An1 + En1 + On1 + In1 + Un1; + En = En1 + On1 + In1 + Un1; + In = In1 + On1 + Un1; + On = On1 + Un1; + Un = Un1; + An1 = An; + En1 = En; + On1 = On; + In1 = In; + i++; + } + return An1 + En1 + In1 + On1 + Un1; + } +}; diff --git a/cpp/1675-minimize-deviation-in-array.cpp b/cpp/1675-minimize-deviation-in-array.cpp new file mode 100644 index 000000000..f245d863e --- /dev/null +++ b/cpp/1675-minimize-deviation-in-array.cpp @@ -0,0 +1,43 @@ +/* + You are given an array nums of n positive integers. + + You can perform two types of operations on any element of the array any number of times: + + If the element is even, divide it by 2. + For example, if the array is [1,2,3,4], then you can do this operation on the last element, and the array will be [1,2,3,2]. + If the element is odd, multiply it by 2. + For example, if the array is [1,2,3,4], then you can do this operation on the first element, and the array will be [2,2,3,4]. + + The deviation of the array is the maximum difference between any two elements in the array. + +Return the minimum deviation the array can have after performing some number of operations. + Ex. Input: nums = [1,2,3,4] + Output: 1 + Explanation: You can transform the array to [1,2,3,2], then to [2,2,3,2], then the deviation will be 3 - 2 = 1. + + Time : O(N); + Space : O(N); +*/ + +class Solution { +public: + int minimumDeviation(vector& nums) { + priority_queue pq; + int minimum = INT_MAX; + for(auto i : nums) { + if(i & 1) + i *= 2; + minimum = min(minimum, i); + pq.push(i); + } + int res = INT_MAX; + while(pq.top() % 2 == 0) { + int val = pq.top(); + res = min(res, val - minimum); + minimum = min(val/2, minimum); + pq.pop(); + pq.push(val/2); + } + return min(res, pq.top() - minimum); + } +}; diff --git a/cpp/1700-number-of-students-unable-to-eat-lunch.cpp b/cpp/1700-number-of-students-unable-to-eat-lunch.cpp new file mode 100644 index 000000000..0f2097b38 --- /dev/null +++ b/cpp/1700-number-of-students-unable-to-eat-lunch.cpp @@ -0,0 +1,24 @@ +class Solution { +public: + int countStudents(vector& students, vector& sandwiches) { + queue q1; + for(int i = 0; i < students.size(); i++) { + q1.push(students[i]); + } + + int sandwichPos = 0; + int curr = 0; + while(!q1.empty() && curr <= q1.size()) { + if(q1.front() == sandwiches[sandwichPos]) { + q1.pop(); + sandwichPos++; + curr = 0; + } else { + q1.push(q1.front()); + q1.pop(); + } + curr++; + } + return q1.size(); + } +}; diff --git a/cpp/1721-swapping-nodes-in-a-linked-list.cpp b/cpp/1721-swapping-nodes-in-a-linked-list.cpp new file mode 100644 index 000000000..ee9e4044d --- /dev/null +++ b/cpp/1721-swapping-nodes-in-a-linked-list.cpp @@ -0,0 +1,43 @@ +/* + You are given the head of a linked list, and an integer k. + Return the head of the linked list after swapping the values of the kth node from the beginning and the kth node from the end (the list is 1-indexed). + + Ex. Input: head = [1,2,3,4,5], k = 2 + Output: [1,4,3,2,5] + + Time : O(N) + Space : O(1) +*/ + +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +class Solution { +public: + ListNode* swapNodes(ListNode* head, int k) { + if(head == NULL || head -> next == NULL) + return head; + + ListNode *ptr = head, *beg = head, *end = head; + int a = 0; + while(ptr != NULL) { + a++; + if(a == k) + beg = ptr; + if(a >= k + 1) + end = end -> next; + ptr = ptr -> next; + } + int temp = beg -> val; + beg -> val = end -> val; + end -> val = temp; + return head; + } +}; diff --git a/cpp/1768-merge-strings-alternately.cpp b/cpp/1768-merge-strings-alternately.cpp new file mode 100644 index 000000000..63bcc88c6 --- /dev/null +++ b/cpp/1768-merge-strings-alternately.cpp @@ -0,0 +1,18 @@ +class Solution { +public: + string mergeAlternately(string word1, string word2) + { + int i=0; + string final=""; + + while(i < word1.size() && i < word2.size()) + final = final + word1[i] + word2[i++]; + + while(i < word1.size()) + final += word1[i++]; + while(i < word2.size()) + final += word2[i++]; + + return final; + } +}; \ No newline at end of file diff --git a/cpp/1822-sign-of-the-product-of-an-array.cpp b/cpp/1822-sign-of-the-product-of-an-array.cpp new file mode 100644 index 000000000..debab8ccd --- /dev/null +++ b/cpp/1822-sign-of-the-product-of-an-array.cpp @@ -0,0 +1,17 @@ +// Time: O(n) +// Space: O(1) + +class Solution { +public: + int arraySign(vector& nums) { + int neg = 0; + for (int i : nums) { + if (i == 0) { + return 0; + } else { + neg += (i < 0) ? 1 : 0; + } + } + return (neg % 2 == 0) ? 1 : -1; + } +}; diff --git a/cpp/1838-frequency-of-the-most-frequent-element.cpp b/cpp/1838-frequency-of-the-most-frequent-element.cpp new file mode 100644 index 000000000..d20e7dd07 --- /dev/null +++ b/cpp/1838-frequency-of-the-most-frequent-element.cpp @@ -0,0 +1,29 @@ + +/* + Time: O(nlogn) + Space: O(1) +*/ +class Solution { +public: + int maxFrequency(vector& nums, long long k) { + sort(nums.begin(),nums.end()); + int l=0; + int r=0; + int res=0; + long long total=0; + int n=nums.size(); + while(r total+k){ + total-=nums[l]; + l++; + } + + res=max(res,r-l+1); + r++; + } + return res; + } +}; \ No newline at end of file diff --git a/cpp/1845-seat-reservation-manager.cpp b/cpp/1845-seat-reservation-manager.cpp new file mode 100644 index 000000000..8743ecbf0 --- /dev/null +++ b/cpp/1845-seat-reservation-manager.cpp @@ -0,0 +1,26 @@ +class SeatManager { +private: + priority_queue, greater> minHeap; +public: + SeatManager(int n) { + for (int i=1; i<=n; ++i) + minHeap.push(i); + } + + int reserve() { + int smallestSeat = minHeap.top(); + minHeap.pop(); + return smallestSeat; + } + + void unreserve(int seatNumber) { + minHeap.push(seatNumber); + } +}; + +/** + * Your SeatManager object will be instantiated and called as such: + * SeatManager* obj = new SeatManager(n); + * int param_1 = obj->reserve(); + * obj->unreserve(seatNumber); + */ \ No newline at end of file diff --git a/cpp/1849-splitting-a-string-into-descending-consecutive-values.cpp b/cpp/1849-splitting-a-string-into-descending-consecutive-values.cpp new file mode 100644 index 000000000..baf28bc7f --- /dev/null +++ b/cpp/1849-splitting-a-string-into-descending-consecutive-values.cpp @@ -0,0 +1,25 @@ +typedef unsigned long long ll; +class Solution { +public: + bool solve(string & s , ll last , int index,int cnt){ + + if(index >= s.size()) return cnt > 1; + + ll num = 0; + bool ret = false; + + for(int i = index ; i < s.size() ; i++){ + num = num * 10; + num += (s[i] - '0'); + + if(last == -1 || last == num + 1){ + ret |= solve(s , num , i + 1 , cnt + 1); + }else if(last != -1 && num >= last)break; + } + + return ret; + } + bool splitString(string s) { + return solve(s,-1,0,0); + } +}; diff --git a/cpp/1851-Minimum-Interval-To-Include-Each-Query.cpp b/cpp/1851-Minimum-Interval-To-Include-Each-Query.cpp new file mode 100644 index 000000000..1ef415eda --- /dev/null +++ b/cpp/1851-Minimum-Interval-To-Include-Each-Query.cpp @@ -0,0 +1,54 @@ +/* + Given intervals array & queries array, ans to a query is min interval containing it + Ex. intervals = [[1,4],[2,4],[3,6],[4,4]], queries = [2,3,4,5] -> [3,3,1,4] + + Min heap & sort by size of intervals, top will be min size, + + Time: O(n log n + q log q) -> n = number of intervals, q = number of queries + Space: O(n + q) +*/ + +class Solution { +public: + vector minInterval(vector>& intervals, vector& queries) { + vector sortedQueries = queries; + + // [size of interval, end of interval] + priority_queue, vector>, greater>> pq; + // {query -> size of interval} + unordered_map m; + + // also need only valid intervals so sort by start time & sort queries + sort(intervals.begin(), intervals.end()); + sort(sortedQueries.begin(), sortedQueries.end()); + + vector result; + + int i = 0; + for (int j = 0; j < sortedQueries.size(); j++) { + int query = sortedQueries[j]; + + while (i < intervals.size() && intervals[i][0] <= query) { + int left = intervals[i][0]; + int right = intervals[i][1]; + pq.push({right - left + 1, right}); + i++; + } + + while (!pq.empty() && pq.top().second < query) { + pq.pop(); + } + + if (!pq.empty()) { + m[query] = pq.top().first; + } else { + m[query] = -1; + } + } + + for (int j = 0; j < queries.size(); j++) { + result.push_back(m[queries[j]]); + } + return result; + } +}; diff --git a/cpp/1851-minimum-interval-to-include-each-query.cpp b/cpp/1851-minimum-interval-to-include-each-query.cpp new file mode 100644 index 000000000..1ef415eda --- /dev/null +++ b/cpp/1851-minimum-interval-to-include-each-query.cpp @@ -0,0 +1,54 @@ +/* + Given intervals array & queries array, ans to a query is min interval containing it + Ex. intervals = [[1,4],[2,4],[3,6],[4,4]], queries = [2,3,4,5] -> [3,3,1,4] + + Min heap & sort by size of intervals, top will be min size, + + Time: O(n log n + q log q) -> n = number of intervals, q = number of queries + Space: O(n + q) +*/ + +class Solution { +public: + vector minInterval(vector>& intervals, vector& queries) { + vector sortedQueries = queries; + + // [size of interval, end of interval] + priority_queue, vector>, greater>> pq; + // {query -> size of interval} + unordered_map m; + + // also need only valid intervals so sort by start time & sort queries + sort(intervals.begin(), intervals.end()); + sort(sortedQueries.begin(), sortedQueries.end()); + + vector result; + + int i = 0; + for (int j = 0; j < sortedQueries.size(); j++) { + int query = sortedQueries[j]; + + while (i < intervals.size() && intervals[i][0] <= query) { + int left = intervals[i][0]; + int right = intervals[i][1]; + pq.push({right - left + 1, right}); + i++; + } + + while (!pq.empty() && pq.top().second < query) { + pq.pop(); + } + + if (!pq.empty()) { + m[query] = pq.top().first; + } else { + m[query] = -1; + } + } + + for (int j = 0; j < queries.size(); j++) { + result.push_back(m[queries[j]]); + } + return result; + } +}; diff --git a/cpp/1888-minimum-number-of-flips-to-make-the-binary-string-alternating.cpp b/cpp/1888-minimum-number-of-flips-to-make-the-binary-string-alternating.cpp new file mode 100644 index 000000000..a10a2aec7 --- /dev/null +++ b/cpp/1888-minimum-number-of-flips-to-make-the-binary-string-alternating.cpp @@ -0,0 +1,49 @@ +// O(n) time and O(n) space complexity +// Use a sliding window to update the number of differences between current string and two target strings +// Instead of doing the type-1 operation N times, we can append a copy of the string to the end of the string, and iterate with a sliding window. +// As you iterate through the window, update the count of differences +// If the sliding window is the full size, update the result with the minimum of the two counts of differences +class Solution { +public: + int minFlips(string s) { + int n = s.size(); + s = s.append(s); + string t1 = ""; + string t2 = ""; + for (int i = 0; i < s.size(); i++){ + if (i % 2 == 0) { + t1.append("0"); + t2.append("1"); + } else { + t1.append("1"); + t2.append("0"); + } + } + int res = INT_MAX; + int diff1 = 0; + int diff2 = 0; + int l = 0; + for (int r = 0; r < s.size(); r++){ + if (s[r] != t1[r]) { + diff1++; + } + if (s[r] != t2[r]) { + diff2++; + } + if ((r - l + 1) > n) { + if (s[l] != t1[l]) { + diff1--; + } + if (s[l] != t2[l]) { + diff2--; + } + l++; + } + if (r - l + 1 == n) { + int temp = min(res, diff1); + res = min(temp, diff2); + } + } + return res; + } +}; diff --git a/cpp/1899-merge-triplets-to-form-target-triplet.cpp b/cpp/1899-merge-triplets-to-form-target-triplet.cpp new file mode 100644 index 000000000..326404d17 --- /dev/null +++ b/cpp/1899-merge-triplets-to-form-target-triplet.cpp @@ -0,0 +1,30 @@ +/* + Update: [max(ai,aj), max(bi,bj), max(ci,cj)], return if possible to obtain target + Ex. triplets = [[2,5,3],[1,8,4],[1,7,5]] target = [2,7,5] -> true, update 1st/3rd + + Skip all "bad" triplets (can never become target), if match add to "good" set + + Time: O(n) + Space: O(1) +*/ + +class Solution { +public: + bool mergeTriplets(vector>& triplets, vector& target) { + unordered_set s; + + for (int i = 0; i < triplets.size(); i++) { + if (triplets[i][0] > target[0] || triplets[i][1] > target[1] || triplets[i][2] > target[2]) { + continue; + } + + for (int j = 0; j < 3; j++) { + if (triplets[i][j] == target[j]) { + s.insert(j); + } + } + } + + return s.size() == 3; + } +}; diff --git a/cpp/1905-count-sub-islands.cpp b/cpp/1905-count-sub-islands.cpp new file mode 100644 index 000000000..07b4dfc86 --- /dev/null +++ b/cpp/1905-count-sub-islands.cpp @@ -0,0 +1,37 @@ +class Solution { +public: + int countSubIslands(vector>& grid1, vector>& grid2) { + const int ROWS = grid1.size(), COLS = grid1[0].size(); + set visit; + + function dfs = [&] (int r, int c) -> bool { + if ( + r < 0 + || c < 0 + || r == ROWS + || c == COLS + || grid2[r][c] == 0 + || visit.count(r*COLS + c) + ) + return true; + + visit.insert(r*COLS + c); + bool res = true; + if(grid1[r][c] == 0) + res = false; + + res = dfs(r - 1, c) && res; + res = dfs(r + 1, c) && res; + res = dfs(r, c - 1) && res; + res = dfs(r, c + 1) && res; + return res; + }; + + int count = 0; + for(int r = 0; r < ROWS; r++) + for(int c = 0; c < COLS; c++) + if(grid2[r][c] && !visit.count(r*COLS + c) && dfs(r, c)) + count += 1; + return count; + } +}; diff --git a/cpp/1911-maximum-alternating-subsequence-sum.cpp b/cpp/1911-maximum-alternating-subsequence-sum.cpp new file mode 100644 index 000000000..7ecb59437 --- /dev/null +++ b/cpp/1911-maximum-alternating-subsequence-sum.cpp @@ -0,0 +1,30 @@ +/* +Given an array nums, return the maximum alternating sum of any subsequence of nums (after reindexing the elements of the subsequence). +The alternating sum of a 0-indexed array is defined as the sum of the elements at even indices minus the sum of the elements at odd indices. +For example, the alternating sum of [4,2,5,3] is (4 + 5) - (2 + 3) = 4. + +Example. Let nums = [6,2,1,2,4,5]. + The subsequence {6,1,5} can be choosen which gives maximum alternating sum of (6 + 5) - 1 = 10, which is the optimal solution in this case. + So we return 10 as our answer. + + + +Time: O(n) +Space: O(1) + +*/ + + +class Solution { +public: + long long maxAlternatingSum(vector& nums) { + long long even = 0, odd = 0, tmpEven, tmpOdd; + for(int i=nums.size()-1; i>=0; i--) { + tmpEven = max(odd + nums[i], even); + tmpOdd = max(even - nums[i], odd); + even = tmpEven; + odd = tmpOdd; + } + return even; + } +}; \ No newline at end of file diff --git a/cpp/1920-build-array-from-permutation.cpp b/cpp/1920-build-array-from-permutation.cpp new file mode 100644 index 000000000..a64538c9d --- /dev/null +++ b/cpp/1920-build-array-from-permutation.cpp @@ -0,0 +1,10 @@ +class Solution { + public: + vector buildArray(vector& nums){ + vector ans; + for(int & n : nums){ + ans.push_back(nums[n]); + } + return ans; + } +}; diff --git a/cpp/1921-eliminate-maximum-number-of-monsters.cpp b/cpp/1921-eliminate-maximum-number-of-monsters.cpp new file mode 100644 index 000000000..1832189a1 --- /dev/null +++ b/cpp/1921-eliminate-maximum-number-of-monsters.cpp @@ -0,0 +1,45 @@ +/* + You are playing a video game where you are defending your city from a group of n monsters. + You are given a 0-indexed integer array dist of size n, where dist[i] is the initial distance in kilometers of the ith monster from the city. + The monsters walk toward the city at a constant speed. The speed of each monster is given to you in an integer array speed of size n, + where speed[i] is the speed of the ith monster in kilometers per minute. + You have a weapon that, once fully charged, can eliminate a single monster. + However, the weapon takes one minute to charge.The weapon is fully charged at the very start. + You lose when any monster reaches your city. If a monster reaches the city at the exact moment the weapon is fully charged, + it counts as a loss, and the game ends before you can use your weapon. + + Return the maximum number of monsters that you can eliminate before you lose, or n if you can eliminate all the monsters before they reach the city. + + Ex. Input: dist = [1,3,4], speed = [1,1,1] + Output: 3 + Explanation: + In the beginning, the distances of the monsters are [1,3,4]. You eliminate the first monster. + After a minute, the distances of the monsters are [X,2,3]. You eliminate the second monster. + After a minute, the distances of the monsters are [X,X,2]. You eliminate the thrid monster. + All 3 monsters can be eliminated. + + Time : O(N) + Space : O(N) +*/ + +class Solution { +public: + int eliminateMaximum(vector& dist, vector& speed) { + vector v; + + for(int i = 0 ; i < dist.size() ; i++) + v.push_back((float) dist[i] / speed[i]); + + sort(v.begin(), v.end()); + int count = 0; + int time = 0, i = 0; + while(i < v.size()) { + if(time >= v[i]) + return count; + ++count; + time += 1; + ++i; + } + return count; + } +}; diff --git a/cpp/1929-concatenation-of-array.cpp b/cpp/1929-concatenation-of-array.cpp new file mode 100644 index 000000000..4ae21e9d4 --- /dev/null +++ b/cpp/1929-concatenation-of-array.cpp @@ -0,0 +1,12 @@ +class Solution{ + public: + vector getConcatenation(vector& nums){ + vector ans; + int len; + len = nums.size(); + for(int i = 0; i < 2 * len; i++){ + ans.push_back(nums[i % len]); + } + return ans; + } +}; diff --git a/cpp/1930-unique-length-3-palindromic-subsequences.cpp b/cpp/1930-unique-length-3-palindromic-subsequences.cpp new file mode 100644 index 000000000..8061343bd --- /dev/null +++ b/cpp/1930-unique-length-3-palindromic-subsequences.cpp @@ -0,0 +1,21 @@ +class Solution { +public: + int countPalindromicSubsequence(string s) { + vector> v(26, {-1, -1}); + for (int i = 0; i < s.size(); i++) { + if (v[s[i] - 'a'].first == -1) v[s[i] - 'a'].first = i; + else v[s[i] - 'a'].second = i; + } + + int res = 0; + for (int i = 0; i < 26; i++) { + if (v[i].second != -1) { + unordered_set tmp; + for (int j = v[i].first + 1; j < v[i].second; j++) tmp.insert(s[j]); + res += tmp.size(); + } + } + + return res; + } +}; diff --git a/cpp/1958-check-if-move-is-legal.cpp b/cpp/1958-check-if-move-is-legal.cpp new file mode 100644 index 000000000..ccea7f645 --- /dev/null +++ b/cpp/1958-check-if-move-is-legal.cpp @@ -0,0 +1,30 @@ +class Solution { +public: + bool checkMove(vector>& board, int rMove, int cMove, char color) { + const int ROWS = board.size(), COLS = board[0].size(); + int direction[8][4] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}, + {1, 1}, {-1, -1}, {1, -1}, {-1, 1}}; + board[rMove][cMove] = color; + + function legal = [&] (int row, int col, char color, int direc[]) -> bool { + int dr = direc[0], dc = direc[1]; + row = row + dr; + col = col + dc; + int length = 1; + + while(0 <= row && row < ROWS && 0 <= col && col < COLS) { + length += 1; + if(board[row][col] == '.') return false; + if(board[row][col] == color) + return length >= 3; + row = row + dr; + col = col + dc; + } + return false; + }; + + for(auto& d: direction) + if(legal(rMove, cMove, color, d)) return true; + return false; + } +}; diff --git a/cpp/1963-minimum-number-of-swaps-to-make-the-string-balanced.cpp b/cpp/1963-minimum-number-of-swaps-to-make-the-string-balanced.cpp new file mode 100644 index 000000000..f5ecab0bc --- /dev/null +++ b/cpp/1963-minimum-number-of-swaps-to-make-the-string-balanced.cpp @@ -0,0 +1,46 @@ +/* + Approach: + Just check which '[' brackes are wrongly placed correct that bracket. + use stack to keep track of bracket + + Time complexity : O(n) + Space complexity: O(n) + + n is length of the string. +*/ + +class Solution { +public: + int minSwaps(string s) { + + int answer=0; + + stack stc; + stc.push(']'); + + int n = s.size(); + + for(int i=0;i rearrangeArray(vector& nums) { + vector ans; + for(int i=1;ib && b>c || a& inputStrings, string& currentString, int stringLength, set& stringSet) { + if (!result.empty()) return; + if (currentString.size() == stringLength && stringSet.find(currentString) == stringSet.end()) { + result = currentString; + return; + } + if (currentString.size() > stringLength) return; + + for (char ch = '0'; ch <= '1'; ++ch) { + currentString.push_back(ch); + backtrack(inputStrings, currentString, stringLength, stringSet); + currentString.pop_back(); + } + } + + string findDifferentBinaryString(vector& inputStrings) { + int stringLength = inputStrings[0].size(); + string currentString = ""; + set stringSet(inputStrings.begin(), inputStrings.end()); + + backtrack(inputStrings, currentString, stringLength, stringSet); + return result; + } +}; diff --git a/cpp/1984-minimum-difference-between-highest-and-lowest-of-k-scores.cpp b/cpp/1984-minimum-difference-between-highest-and-lowest-of-k-scores.cpp new file mode 100644 index 000000000..9117152b3 --- /dev/null +++ b/cpp/1984-minimum-difference-between-highest-and-lowest-of-k-scores.cpp @@ -0,0 +1,15 @@ +class Solution { +public: + int minimumDifference(vector& nums, int k) { + sort(nums.begin(), nums.end()); + int l = 0, r = k - 1; + int res = INT_MAX; + + while(r < nums.size()) { + res = min(res, nums[r] - nums[l]); + l = l + 1; + r = r + 1; + } + return res; + } +}; diff --git a/cpp/1985-Find-The-Kth-Largest-Integer-In-The-Array.cpp b/cpp/1985-Find-The-Kth-Largest-Integer-In-The-Array.cpp new file mode 100644 index 000000000..6ea070610 --- /dev/null +++ b/cpp/1985-Find-The-Kth-Largest-Integer-In-The-Array.cpp @@ -0,0 +1,15 @@ +class Solution { +private: + static bool st(string &a,string &b){ + if(a.size()==b.size()) return a& nums, int k) { + sort(nums.begin(),nums.end(),st); + return nums[nums.size()-k]; + + } +}; diff --git a/cpp/1985-find-the-kth-largest-integer-in-the-array.cpp b/cpp/1985-find-the-kth-largest-integer-in-the-array.cpp new file mode 100644 index 000000000..6ea070610 --- /dev/null +++ b/cpp/1985-find-the-kth-largest-integer-in-the-array.cpp @@ -0,0 +1,15 @@ +class Solution { +private: + static bool st(string &a,string &b){ + if(a.size()==b.size()) return a& nums, int k) { + sort(nums.begin(),nums.end(),st); + return nums[nums.size()-k]; + + } +}; diff --git a/cpp/2001-number-of-pairs-of-interchangeable-rectangles.cpp b/cpp/2001-number-of-pairs-of-interchangeable-rectangles.cpp new file mode 100644 index 000000000..7bc66d5c9 --- /dev/null +++ b/cpp/2001-number-of-pairs-of-interchangeable-rectangles.cpp @@ -0,0 +1,38 @@ +/* + Approach: + Keep the track of the ratios in a hash map + + Time complexity : O(n) + Space complexity: O(n) + + n is number of rectangles +*/ + +class Solution { +public: + long long interchangeableRectangles(vector>& rectangles) { + + map hash; + long double ratio; + + long long answer=0; + + for(int i=0;i= s.length()) + { + if(isPalindrome(s1)&&isPalindrome(s2)){ + int l = s1.length()*s2.length(); + answer = max(answer,l); + } + return; + } + + char c = s[idx]; + + /* + we have three options + 1. Add the char to the first string + 2. Add the char to the second string + 3. Add the char to none of the string + */ + + // add the character in the first string + s1.push_back(c); + generateAll(idx+1,s1,s2,s); + s1.pop_back(); + + // add the character in the second string + s2.push_back(c); + generateAll(idx+1,s1,s2,s); + s2.pop_back(); + + // add character in no string + generateAll(idx+1,s1,s2,s); + } + + int maxProduct(string s) { + + string s1 = ""; + string s2 = ""; + int idx = 0; + + generateAll(idx,s1,s2,s); + + return answer; + } + + +}; \ No newline at end of file diff --git a/cpp/2013-Detect-Squares.cpp b/cpp/2013-Detect-Squares.cpp new file mode 100644 index 000000000..641a9d745 --- /dev/null +++ b/cpp/2013-Detect-Squares.cpp @@ -0,0 +1,53 @@ +/* + Given stream of points, add new points, return count of squares + + Find diagonals, if exists then forms a square, loop thru all points + + Time: O(1) add O(n^2) count -> n = number of points + Space: O(n) +*/ + +class DetectSquares { +public: + DetectSquares() { + + } + + void add(vector point) { + points[point[0]][point[1]]++; + } + + int count(vector point) { + int x1 = point[0]; + int y1 = point[1]; + + int result = 0; + + for (auto x = points.begin(); x != points.end(); x++) { + unordered_map yPoints = x->second; + for (auto y = yPoints.begin(); y != yPoints.end(); y++) { + int x3 = x->first; + int y3 = y->first; + + // skip points on same x-axis or y-axis + if (abs(x3 - x1) == 0 || abs(x3 - x1) != abs(y3 - y1)) { + continue; + } + + result += points[x3][y3] * points[x1][y3] * points[x3][y1]; + } + } + + return result; + } +private: + // {x -> {y -> count}} + unordered_map> points; +}; + +/** + * Your DetectSquares object will be instantiated and called as such: + * DetectSquares* obj = new DetectSquares(); + * obj->add(point); + * int param_2 = obj->count(point); + */ diff --git a/cpp/2013-detect-squares.cpp b/cpp/2013-detect-squares.cpp new file mode 100644 index 000000000..641a9d745 --- /dev/null +++ b/cpp/2013-detect-squares.cpp @@ -0,0 +1,53 @@ +/* + Given stream of points, add new points, return count of squares + + Find diagonals, if exists then forms a square, loop thru all points + + Time: O(1) add O(n^2) count -> n = number of points + Space: O(n) +*/ + +class DetectSquares { +public: + DetectSquares() { + + } + + void add(vector point) { + points[point[0]][point[1]]++; + } + + int count(vector point) { + int x1 = point[0]; + int y1 = point[1]; + + int result = 0; + + for (auto x = points.begin(); x != points.end(); x++) { + unordered_map yPoints = x->second; + for (auto y = yPoints.begin(); y != yPoints.end(); y++) { + int x3 = x->first; + int y3 = y->first; + + // skip points on same x-axis or y-axis + if (abs(x3 - x1) == 0 || abs(x3 - x1) != abs(y3 - y1)) { + continue; + } + + result += points[x3][y3] * points[x1][y3] * points[x3][y1]; + } + } + + return result; + } +private: + // {x -> {y -> count}} + unordered_map> points; +}; + +/** + * Your DetectSquares object will be instantiated and called as such: + * DetectSquares* obj = new DetectSquares(); + * obj->add(point); + * int param_2 = obj->count(point); + */ diff --git a/cpp/2017-grid-game.cpp b/cpp/2017-grid-game.cpp new file mode 100644 index 000000000..c414451b3 --- /dev/null +++ b/cpp/2017-grid-game.cpp @@ -0,0 +1,33 @@ +/* +Approch: + 2nd robot can collect either all the bottom points + before the break point (when 1st robot goes to bottom) + or collect all the top points after that break point. +Time Complexity: O(N) +Space Complexity: O(1) +*/ + +class Solution { +public: + long long gridGame(vector>& grid) { + + // prefix sum + long long top = grid[0][0],bottom = 0, answer = LONG_MAX; + for(int i =1;inext) { + slow = slow->next; + fast = fast->next->next; + } + slow = reverseList(slow); + + int mx = 0; + while(head && slow) { + mx = max(mx, head->val + slow->val); + head = head->next; + slow = slow->next; + } + + return mx; + } +private: + ListNode* reverseList(ListNode* head) { + ListNode* prev = NULL; + ListNode* curr = head; + ListNode* NEXT = NULL; + + while(curr) { + NEXT = curr->next; + curr->next = prev; + prev = curr; + curr = NEXT; + } + + return prev; + } +}; diff --git a/cpp/2140-solving-questions-with-brainpower.cpp b/cpp/2140-solving-questions-with-brainpower.cpp new file mode 100644 index 000000000..8c3052b46 --- /dev/null +++ b/cpp/2140-solving-questions-with-brainpower.cpp @@ -0,0 +1,28 @@ +// Time Complexity: O(n) +// Space Complexity: O(n) + +class Solution +{ +public: + long long mostPoints(vector> &questions) + { + int n = questions.size(); + vector dp(n, 0); + dp[n - 1] = questions[n - 1][0]; + long long ans = dp[n - 1]; + for (int i = n - 2; i >= 0; i--) + { + int k = i + questions[i][1] + 1; + if (k < n) + { + dp[i] = (dp[k] + questions[i][0]) > dp[i + 1] ? (dp[k] + questions[i][0]) : dp[i + 1]; + } + else + { + dp[i] = questions[i][0] > dp[i + 1] ? questions[i][0] : dp[i + 1]; + } + ans = ans > dp[i] ? ans : dp[i]; + } + return ans; + } +}; \ No newline at end of file diff --git a/cpp/2215-find-the-difference-of-two-arrays.cpp b/cpp/2215-find-the-difference-of-two-arrays.cpp new file mode 100644 index 000000000..0b9be1479 --- /dev/null +++ b/cpp/2215-find-the-difference-of-two-arrays.cpp @@ -0,0 +1,33 @@ +// Time Complexity: O(m + n), we check each element of nums1Set and nums2Set +// Space Complexity: O(m + n), where m and n are length sets in worst case. + +class Solution +{ +public: + vector> findDifference(vector &nums1, vector &nums2) + { + unordered_set nums1Set(nums1.begin(), nums1.end()); + unordered_set nums2Set(nums2.begin(), nums2.end()); + + vector lst1; + vector lst2; + + for (const auto &num : nums1Set) + { + if (nums2Set.find(num) == nums2Set.end()) + { + lst1.push_back(num); + } + } + + for (const auto &num : nums2Set) + { + if (nums1Set.find(num) == nums1Set.end()) + { + lst2.push_back(num); + } + } + + return {lst1, lst2}; + } +}; \ No newline at end of file diff --git a/cpp/2235-add-two-integers.cpp b/cpp/2235-add-two-integers.cpp new file mode 100644 index 000000000..fea0ba5d6 --- /dev/null +++ b/cpp/2235-add-two-integers.cpp @@ -0,0 +1,6 @@ +class Solution{ + public: + int sum(int num1, int num2){ + return num1 + num2; + } +}; diff --git a/cpp/2300-successful-pairs-of-spells-and-potions.cpp b/cpp/2300-successful-pairs-of-spells-and-potions.cpp new file mode 100644 index 000000000..e4ce2a2bd --- /dev/null +++ b/cpp/2300-successful-pairs-of-spells-and-potions.cpp @@ -0,0 +1,34 @@ +// Time: O(N * logN) +// Space: O(N) + +class Solution { +public: + vector successfulPairs(vector& spells, vector& potions, long long success) { + sort(potions.begin(), potions.end()); + + int n = spells.size(); + int m = potions.size(); + vector pairs(n); + + for(int i = 0; i < n; i++) { + int spell = spells[i]; + + int start = 0, end = m - 1; + int curr; + + while(start <= end) { + curr = start + (end-start)/2; + long long strength = (long long)potions[curr] * (long long)spell; + if(strength < success) { + start = curr + 1; + } + else { + end = curr - 1; + } + } + pairs[i] = m - start; + } + + return pairs; + } +}; diff --git a/cpp/2306-naming-a-company.cpp b/cpp/2306-naming-a-company.cpp new file mode 100644 index 000000000..74203914a --- /dev/null +++ b/cpp/2306-naming-a-company.cpp @@ -0,0 +1,46 @@ +//time O(n) +//space O(n) + +class Solution { +public: + long long distinctNames(vector& ideas) { + + unordered_map> dict; + + for(const string& idea : ideas){ + dict[idea[0]].insert(idea.substr(1)); + } + + if(dict.size() < 2){ + return 0; + } + + long long count = 0; + + for(char a = 'a'; a <= 'z' ; a++){ + + if(dict.find(a) == dict.end()) + continue; + + for(char b = a+1; b <= 'z'; b++){ + + if(dict.find(b) == dict.end()) + continue; + + int aKeys = dict[a].size(); + int bKeys = dict[b].size(); + + for(const string& suffix : dict[a]){ + if(dict[b].find(suffix) != dict[b].end()){ + aKeys--; + bKeys--; + } + } + + count += 2 * (aKeys*bKeys); + } + } + + return count; + } +}; diff --git a/cpp/2315-count-asterisks.cpp b/cpp/2315-count-asterisks.cpp new file mode 100644 index 000000000..577e4f184 --- /dev/null +++ b/cpp/2315-count-asterisks.cpp @@ -0,0 +1,18 @@ +class Solution{ + public: + int countAsterisks(string s){ + int NAst; + bool Coppia; + Coppia = false; + NAst = 0; + for(char & c : s){ + if((c == '*') && (Coppia == false)){ + NAst++; + } + if(c == '|'){ + Coppia = !Coppia; + } + } + return NAst; + } +}; diff --git a/cpp/2348-number-of-zero-filled-subarrays.cpp b/cpp/2348-number-of-zero-filled-subarrays.cpp new file mode 100644 index 000000000..12f52f981 --- /dev/null +++ b/cpp/2348-number-of-zero-filled-subarrays.cpp @@ -0,0 +1,30 @@ +/* + Given an integer array nums, return the number of subarrays filled with 0. + A subarray is a contiguous non-empty sequence of elements within an array. + + Ex. Input: nums = [1,3,0,0,2,0,0,4] + Output: 6 + Explanation: + There are 4 occurrences of [0] as a subarray. + There are 2 occurrences of [0,0] as a subarray. + There is no occurrence of a subarray with a size more than 2 filled with 0. Therefore, we return 6. + + Time : O(N) + Space : O(1) +*/ + +class Solution { +public: + long long zeroFilledSubarray(vector& nums) { + long long res = 0, count = 0; + for (int i = 0; i < nums.size(); i++) { + if (nums[i]) { + res += (count * (count + 1)) / 2; + count = 0; + } else + ++count; + } + res += (count * (count + 1)) / 2; + return res; + } +}; diff --git a/cpp/2389-longest-subsequence-with-limited-sum.cpp b/cpp/2389-longest-subsequence-with-limited-sum.cpp new file mode 100644 index 000000000..d6075cd56 --- /dev/null +++ b/cpp/2389-longest-subsequence-with-limited-sum.cpp @@ -0,0 +1,45 @@ +/* 2389. Longest Subsequence With Limited Sum +You are given an integer array nums of length n, and an integer array queries of length m. + +Return an array answer of length m where answer[i] is the maximum size of a subsequence that you can take from nums such that the sum of its elements is less than or equal to queries[i]. + +A subsequence is an array that can be derived from another array by deleting some or no elements without changing the order of the remaining elements. +*/ + + +class Solution { +public: + vector answerQueries(vector& nums, vector& queries) { + vector ans; + sort(nums.begin(),nums.end()); + int sum=0,cnt=0; + for(int i=0;i stk; + for(int i=0;i lastSeen(26, -1); + int count = 1, substringStart = 0; + + for (int i = 0; i < s.length(); i++) { + if (lastSeen[s[i] - 'a'] >= substringStart) { + count++; + substringStart = i; + } + lastSeen[s[i] - 'a'] = i; + } + + return count; + } +}; + + +class Solution { + public: + int minPartitions(std::string s) { + // Set to keep track of characters in the current substring + std::unordered_set currentChars; + // Variable to count the number of partitions + int partitionCount = 0; + + // Iterate over each character in the string + for (char c : s) { + // If the character is already in the set, it means we've encountered a duplicate + if (currentChars.find(c) != currentChars.end()) { + // Increment the partition count and start a new substring + partitionCount++; + currentChars.clear(); + } + // Add the current character to the set + currentChars.insert(c); + } + + // There will be at least one partition at the end if currentChars is not empty + if (!currentChars.empty()) { + partitionCount++; + } + + return partitionCount; + } +}; diff --git a/cpp/2483-minimum-penalty-for-a-shop.cpp b/cpp/2483-minimum-penalty-for-a-shop.cpp new file mode 100644 index 000000000..245c136c0 --- /dev/null +++ b/cpp/2483-minimum-penalty-for-a-shop.cpp @@ -0,0 +1,43 @@ +/* + You are given the customer visit log of a shop represented by a 0-indexed string customers consisting only of characters 'N' and 'Y': + if the ith character is 'Y', it means that customers come at the ith hour + whereas 'N' indicates that no customers come at the ith hour. + + If the shop closes at the jth hour (0 <= j <= n), the penalty is calculated as follows: + For every hour when the shop is open and no customers come, the penalty increases by 1. + For every hour when the shop is closed and customers come, the penalty increases by 1. + + Return the earliest hour at which the shop must be closed to incur a minimum penalty. + Note that if a shop closes at the jth hour, it means the shop is closed at the hour j. + + Ex. Input: customers = "YYNY" + Output: 2 + Explanation: + - Closing the shop at the 0th hour incurs in 1+1+0+1 = 3 penalty. + - Closing the shop at the 1st hour incurs in 0+1+0+1 = 2 penalty. + - Closing the shop at the 2nd hour incurs in 0+0+0+1 = 1 penalty. + - Closing the shop at the 3rd hour incurs in 0+0+1+1 = 2 penalty. + - Closing the shop at the 4th hour incurs in 0+0+1+0 = 1 penalty. + Closing the shop at 2nd or 4th hour gives a minimum penalty. Since 2 is earlier, the optimal closing time is 2. + + Time : O(N) + Space : O(1) +*/ + +class Solution { +public: + int bestClosingTime(string customers) { + int res = -1, maxi = 0, pen = 0; + for(int i = 0 ; i < customers.size() ; ++i) { + if(customers[i] == 'Y') + ++pen; + else + --pen; + if(pen > maxi) { + maxi = pen; + res = i; + } + } + return ++res; + } +}; diff --git a/cpp/2542-maximum-subsequence-score.cpp b/cpp/2542-maximum-subsequence-score.cpp new file mode 100644 index 000000000..1848c443e --- /dev/null +++ b/cpp/2542-maximum-subsequence-score.cpp @@ -0,0 +1,38 @@ +// Time: O(NlogN) +// Space: O(N) + +class Solution { +public: + long long maxScore(vector& nums1, vector& nums2, int k) { + int size = nums1.size(); + vector> pairs(size); + + // populating the array + for(int i = 0; i < size; i++) { + pairs.push_back(make_pair(nums1[i], nums2[i])); + } + + // sorting the array using comparator lambda function + sort(pairs.begin(), pairs.end(), [](pair a, pair b) { + return (a.second > b.second); + }); + + priority_queue, greater> minh; + long long currSum = 0; + long long maxSum = INT_MIN; + + for(int i = 0; i < size; i++) { + currSum += pairs[i].first; + minh.push(pairs[i].first); + + if(minh.size() > k) { + currSum -= minh.top(); + minh.pop(); + } + if(minh.size() == k) { + maxSum = max(maxSum, (currSum * pairs[i].second)); + } + } + return maxSum; + } +}; diff --git a/cpp/2657-find-the-prefix-common-array-of-two-arrays.cpp b/cpp/2657-find-the-prefix-common-array-of-two-arrays.cpp new file mode 100644 index 000000000..b3b7a6c48 --- /dev/null +++ b/cpp/2657-find-the-prefix-common-array-of-two-arrays.cpp @@ -0,0 +1,38 @@ +/* + You are given two 0-indexed integer permutations A and B of length n. + A prefix common array of A and B is an array C such that C[i] is equal to the count of numbers that are present at or before the index i in both A and B. + Return the prefix common array of A and B. + A sequence of n integers is called a permutation if it contains all integers from 1 to n exactly once. + + Ex. Input: A = [1,3,2,4], B = [3,1,2,4] + Output: [0,2,3,4] + Explanation: At i = 0: no number is common, so C[0] = 0. + At i = 1: 1 and 3 are common in A and B, so C[1] = 2. + At i = 2: 1, 2, and 3 are common in A and B, so C[2] = 3. + At i = 3: 1, 2, 3, and 4 are common in A and B, so C[3] = 4. + + Time : O(n) + Space : O(n) +*/ + +class Solution { +public: + vector findThePrefixCommonArray(vector& A, vector& B) { + vector C; + vector v(A.size()+1,0); + int count = 0; + + for(int i = 0; i < A.size(); ++i){ + if(v[A[i]] < 0) + count++; + ++v[A[i]]; + + if(v[B[i]]>0) + count++; + --v[B[i]]; + + C.push_back(count); + } + return C; + } +}; diff --git a/cpp/2707-extra-characters-in-a-string.cpp b/cpp/2707-extra-characters-in-a-string.cpp new file mode 100644 index 000000000..cf5e543be --- /dev/null +++ b/cpp/2707-extra-characters-in-a-string.cpp @@ -0,0 +1,36 @@ +/* + You are given a 0-indexed string s and a dictionary of words dictionary. + You have to break s into one or more non-overlapping substrings such that each substring is present in dictionary. + There may be some extra characters in s which are not present in any of the substrings. + + Return the minimum number of extra characters left over if you break up s optimally. + + Ex. Input: s = "leetscode", dictionary = ["leet","code","leetcode"] + Output: 1 + Explanation: We can break s in two substrings: "leet" from index 0 to 3 and "code" from index 5 to 8. There is only 1 unused character (at index 4), so we return 1. + + Time : O(N^M) M = dictionary.size(), N = s.size() + Space : O(N) +*/ + +class Solution { +public: + int minExtraChar(string s, vector& dictionary) { + int n = s.size(); + vector dp(n + 1, n); + + dp[0] = 0; + + for (int i = 1; i <= n; ++i) { + for(int j = 0 ; j < dictionary.size() ; ++j) { + int len = dictionary[j].size(); + if (i >= len && s.substr(i - len, len) == dictionary[j]) { + dp[i] = min(dp[i], dp[i - len]); + } + } + dp[i] = min(dp[i], dp[i - 1] + 1); + } + + return dp[n]; + } +}; diff --git a/cpp/a.txt b/cpp/a.txt deleted file mode 100644 index 5d308e1d0..000000000 --- a/cpp/a.txt +++ /dev/null @@ -1 +0,0 @@ -aaaa diff --git a/csharp/0001-two-sum.cs b/csharp/0001-two-sum.cs new file mode 100644 index 000000000..62db7f941 --- /dev/null +++ b/csharp/0001-two-sum.cs @@ -0,0 +1,15 @@ +public class Solution { + public int[] TwoSum(int[] nums, int target) { + Dictionary indices = new Dictionary(); + + for (int i = 0; i < nums.Length; i++) { + var diff = target - nums[i]; + if (indices.ContainsKey(diff)) { + return new int[] {indices[diff], i}; + } + indices[nums[i]] = i; + } + return null; + } +} + diff --git a/csharp/0002-add-two-numbers.cs b/csharp/0002-add-two-numbers.cs new file mode 100644 index 000000000..c4d5f4081 --- /dev/null +++ b/csharp/0002-add-two-numbers.cs @@ -0,0 +1,35 @@ +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +public class Solution { + public ListNode AddTwoNumbers(ListNode l1, ListNode l2) { + var sumList = new ListNode(); + var current = sumList; + int carry = 0, sum = 0; + + while (l1 != null || l2 != null || carry == 1) + { + var v1 = l1 == null ? 0 : l1.val; + var v2 = l2 == null ? 0 : l2.val; + sum = v1 + v2 + carry; + carry = sum > 9 ? 1 : 0; + sum = sum % 10; + current.next = new ListNode(sum); + + current = current.next; + l1 = l1 == null ? null : l1.next; + l2 = l2 == null ? null : l2.next; + } + + return sumList.next; + } +} diff --git a/csharp/0003-longest-substring-without-repeating-characters.cs b/csharp/0003-longest-substring-without-repeating-characters.cs new file mode 100644 index 000000000..15f410758 --- /dev/null +++ b/csharp/0003-longest-substring-without-repeating-characters.cs @@ -0,0 +1,61 @@ +// hashset +public class Solution +{ + public int LengthOfLongestSubstring(string s) + { + int leftPointer = 0, rightPointer = 0, maxLength = 0; + HashSet chars = new HashSet(); + + while (rightPointer < s.Length) + { + char currChar = s[rightPointer]; + if (chars.Contains(currChar)) + { + // Move left pointer until all duplicate chars removed + chars.Remove(s[leftPointer]); + leftPointer++; + } + else + { + chars.Add(currChar); + maxLength = Math.Max(maxLength, rightPointer - leftPointer + 1); + rightPointer++; + } + } + return maxLength; + } +} + +//bitmask +public class Solution +{ + private Int128 ONE = 1; + public int LengthOfLongestSubstring(string s) + { + int Convert(char ch) => ch - ' '; + Int128 mask = 0; + int l = 0, r = 0, output = 0; + while (r < s.Length) + { + Int128 temp = mask ^ (ONE << Convert(s[r])); + if (temp < mask) + { + while (s[l] != s[r]) + { + mask ^= ONE << Convert(s[l]); + l++; + } + mask ^= ONE << Convert(s[l]); + l++; + } + else + { + mask = temp; + output = Math.Max(output, r - l + 1); + r++; + } + } + + return output; + } +} \ No newline at end of file diff --git a/csharp/0004-median-of-two-sorted-arrays.cs b/csharp/0004-median-of-two-sorted-arrays.cs new file mode 100644 index 000000000..9a4656852 --- /dev/null +++ b/csharp/0004-median-of-two-sorted-arrays.cs @@ -0,0 +1,56 @@ +public class Solution { + public double FindMedianSortedArrays(int[] nums1, int[] nums2) + { + if (nums1.Length <= 0 && nums2.Length == 1) + { + return nums2[0]; + } + if (nums2.Length <= 0 && nums1.Length == 1) + { + return nums1[0]; + } + + var m = nums1.Length; + var n = nums2.Length; + if (m > n) + { + return FindMedianSortedArrays(nums2, nums1); + } + var total = m + n; + var half = (total + 1) / 2; + var left = 0; + var right = m; + var result = 0.0; + while (left <= right) + { + var i = left + (right - left) / 2; + var j = half - i; + var left1 = (i > 0) ? nums1[i - 1] : int.MinValue; + var right1 = (i < m) ? nums1[i] : int.MaxValue; + var left2 = (j > 0) ? nums2[j - 1] : int.MinValue; + var right2 = (j < n) ? nums2[j] : int.MaxValue; + + if (left1 <= right2 && left2 <= right1) + { + if (total % 2 == 0) + { + result = (Math.Max(left1, left2) + Math.Min(right1, right2)) / 2.0; + } + else + { + result = Math.Max(left1, left2); + } + break; + } + else if (left1 > right2) + { + right = i - 1; + } + else + { + left = i + 1; + } + } + return result; + } +} diff --git a/csharp/0005-longest-palindromic-substring.cs b/csharp/0005-longest-palindromic-substring.cs new file mode 100644 index 000000000..28e1bb71a --- /dev/null +++ b/csharp/0005-longest-palindromic-substring.cs @@ -0,0 +1,28 @@ +public class Solution { + public string LongestPalindrome(string s) { + var l = 0; + var h = 0; + var len1 = 0; + for (var i = 0; i < s.Length; i++) { + len1 = Math.Max(lengthOfPalindrome(s, i, i), + lengthOfPalindrome(s, i, i + 1)); + + if(len1 > h-l) { + l = i - (len1 - 1) / 2; + h = i + len1 / 2; + } + } + return s.Substring(l, h - l + 1); + + } + + public int lengthOfPalindrome(String s, int L, int H) { + + while(L >= 0 && H < s.Length && s[L] == s[H]) { + L--; + H++; + } + return H - L - 1; + + } +} \ No newline at end of file diff --git a/csharp/0007-reverse-integer.cs b/csharp/0007-reverse-integer.cs new file mode 100644 index 000000000..7da570c17 --- /dev/null +++ b/csharp/0007-reverse-integer.cs @@ -0,0 +1,24 @@ +public class Solution { + public int Reverse(int x) { + int rev = 0; + + while(x != 0) { + var digit = x % 10; + x = x / 10; + if(rev > Int32.MaxValue / 10 || + // since max value is 2,147,483,647, + // last digit greater than 7 will overflow + (rev == Int32.MaxValue / 10 && digit > 7)) + return 0; + + if (rev < Int32.MinValue/10 || + // since min value is -2,147,483,648 + (rev == Int32.MinValue / 10 && digit < -8)) + return 0; + + rev = rev * 10 + digit; + } + + return rev; + } +} \ No newline at end of file diff --git a/csharp/0010-regular-expression-matching.cs b/csharp/0010-regular-expression-matching.cs new file mode 100644 index 000000000..98fce71dc --- /dev/null +++ b/csharp/0010-regular-expression-matching.cs @@ -0,0 +1,43 @@ +public class Solution +{ + // T: O(M*N) | S: O(M*N) + public bool IsMatch(string s, string p) + { + // Top down + var cache = new Dictionary<(int, int), bool>(); + + bool dfs(int i, int j) + { + if (cache.ContainsKey((i, j))) + return cache[(i, j)]; + if (i >= s.Length && j >= p.Length) + return true; + if (j >= p.Length) + return false; + + var match = i < s.Length && (s[i] == p[j] || p[j] == '.'); + if (j + 1 < p.Length && p[j + 1] == '*') + { + cache.TryAdd((i, j), false); + cache[(i, j)] = (match && dfs(i + 1, j)) || //use * + dfs(i, j + 2); //dont use * + return cache[(i, j)]; + } + + if (match) + { + cache.TryAdd((i, j), false); + cache[(i, j)] = dfs(i + 1, j + 1); + return cache[(i, j)]; + + } + + cache.TryAdd((i, j), false); + cache[(i, j)] = false; + return cache[(i, j)]; + + } + + return dfs(0, 0); + } +} \ No newline at end of file diff --git a/csharp/0011-container-with-most-water.cs b/csharp/0011-container-with-most-water.cs new file mode 100644 index 000000000..c4e068e7f --- /dev/null +++ b/csharp/0011-container-with-most-water.cs @@ -0,0 +1,19 @@ +public class Solution { + public int MaxArea(int[] height) { + int res = 0, area = 0, left = 0, right = height.Length-1; + + while (left < right){ + + area = (Math.Min(height[left], height[right])) * (right - left); + res = Math.Max(area, res); + + if(height[left] < height[right]){ + left++; + }else{ + right--; + } + + } + return res; + } +} diff --git a/csharp/0014-longest-common-prefix.cs b/csharp/0014-longest-common-prefix.cs new file mode 100644 index 000000000..113f9dc19 --- /dev/null +++ b/csharp/0014-longest-common-prefix.cs @@ -0,0 +1,25 @@ +public class Solution { + public string LongestCommonPrefix(string[] strs) { + string result = strs[0]; + int charIndex = 0; + + //finding minimum string length - that could be max common prefix + int maxCharIndex = strs[0].Length; + for (int i = 1; i < strs.Length; ++i) { + maxCharIndex = Math.Min(maxCharIndex, strs[i].Length); + } + + while (charIndex < maxCharIndex) { + char prevChar = strs[0][charIndex]; + for (int i = 1; i < strs.Length; ++i) { + if (prevChar == strs[i][charIndex]) { + continue; + } + return result.Substring(0, charIndex); + } + ++charIndex; + result += prevChar; + } + return result.Substring(0, charIndex); + } +} diff --git a/csharp/0015-3sum.cs b/csharp/0015-3sum.cs new file mode 100644 index 000000000..2d44c4f35 --- /dev/null +++ b/csharp/0015-3sum.cs @@ -0,0 +1,31 @@ +public class Solution { + public IList> ThreeSum(int[] nums) { + List> res = new List>(); + int left, right; + Array.Sort(nums); + + for(int i = 0; i < nums.Length; i++){ + if(i > 0 && nums[i] == nums[i-1]){ + continue; + } + + left = i+1; + right = nums.Length-1; + + while(left < right){ + if(nums[i] + nums[left] + nums[right] > 0){ + right--; + }else if(nums[i] + nums[left] + nums[right] < 0){ + left++; + }else { + res.Add(new List {nums[i], nums[left], nums[right]}); + left++; + while(nums[left] == nums[left-1] && left < right){ + left++; + } + } + } + } + return res; + } +} diff --git a/csharp/0017-letter-combinations-of-a-phone-number.cs b/csharp/0017-letter-combinations-of-a-phone-number.cs new file mode 100644 index 000000000..50827f31e --- /dev/null +++ b/csharp/0017-letter-combinations-of-a-phone-number.cs @@ -0,0 +1,34 @@ +public class Solution { + public IList LetterCombinations(string digits) + { + var lettersMap = new Dictionary + { + {'2', "abc"}, + {'3', "def"}, + {'4', "ghi"}, + {'5', "jkl"}, + {'6', "mno"}, + {'7', "pqrs"}, + {'8', "tuv"}, + {'9', "wxyz"} + }; + + var result = new List(); + + if (!string.IsNullOrEmpty(digits)) + Backtrack(result, digits, lettersMap, "", 0); + + return result; + } + + void Backtrack(List result, string digits, Dictionary lettersMap, string curString, int start) + { + if (curString.Length == digits.Length) + { result.Add(curString); return; } + + foreach (var c in lettersMap[digits[start]]) + { + Backtrack(result, digits, lettersMap, curString + c, start + 1); + } + } +} diff --git a/csharp/0019-remove-nth-node-from-end-of-list.cs b/csharp/0019-remove-nth-node-from-end-of-list.cs new file mode 100644 index 000000000..4b9d4321b --- /dev/null +++ b/csharp/0019-remove-nth-node-from-end-of-list.cs @@ -0,0 +1,32 @@ +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ +public class Solution { + public ListNode RemoveNthFromEnd(ListNode head, int n) { + var dummy = new ListNode(0, head); + var left = dummy; + var right = head; + + while(n > 0) { + right = right.next; + n--; + } + + while(right != null) { + left = left.next; + right = right.next; + } + + // delete + left.next = left.next.next; + return dummy.next; + } +} \ No newline at end of file diff --git a/csharp/0020-valid-parentheses.cs b/csharp/0020-valid-parentheses.cs new file mode 100644 index 000000000..2f4daea92 --- /dev/null +++ b/csharp/0020-valid-parentheses.cs @@ -0,0 +1,20 @@ +public class Solution { + public bool IsValid(string s) { + var stack = new Stack(); + var pairs = new Dictionary() { + [')'] = '(', + [']'] = '[', + ['}'] = '{' + }; + + foreach (char c in s) { + if (!pairs.ContainsKey(c)) { + stack.Push(c); + } else if (stack.Count == 0 || stack.Pop() != pairs[c]) { + return false; + } + } + + return stack.Count == 0; + } +} \ No newline at end of file diff --git a/csharp/0021-merge-two-sorted-lists.cs b/csharp/0021-merge-two-sorted-lists.cs new file mode 100644 index 000000000..493937d9d --- /dev/null +++ b/csharp/0021-merge-two-sorted-lists.cs @@ -0,0 +1,36 @@ +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ +public class Solution { + public ListNode MergeTwoLists(ListNode list1, ListNode list2) { + var dummy = new ListNode(); + var tail = dummy; + + while(list1 is not null && list2 is not null) { + if(list1.val < list2.val) { + tail.next = list1; + list1 = list1.next; + } + else { + tail.next = list2; + list2 = list2.next; + } + tail = tail.next; + } + + if(list1 is not null) + tail.next = list1; + else if(list2 is not null) + tail.next = list2; + + return dummy.next; + } +} \ No newline at end of file diff --git a/csharp/0022-generate-parentheses.cs b/csharp/0022-generate-parentheses.cs new file mode 100644 index 000000000..25e5ffb6f --- /dev/null +++ b/csharp/0022-generate-parentheses.cs @@ -0,0 +1,29 @@ +public class Solution { + public IList GenerateParenthesis(int n) { + var result = new List(); + var seq = new StringBuilder(); + + void backtrack(int open, int close) { + if(seq.Length == n * 2) { + result.Add(seq.ToString()); + return; + } + + if(open < n) { + seq.Append("("); + backtrack(open + 1, close); + seq.Remove(seq.Length - 1, 1); + } + if(close < open) { + seq.Append(")"); + backtrack(open, close + 1); + seq.Remove(seq.Length - 1, 1); + } + + } + + backtrack(0, 0); + + return result; + } +} \ No newline at end of file diff --git a/csharp/0023-merge-k-sorted-lists.cs b/csharp/0023-merge-k-sorted-lists.cs new file mode 100644 index 000000000..eb178bd9e --- /dev/null +++ b/csharp/0023-merge-k-sorted-lists.cs @@ -0,0 +1,65 @@ +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ + +public class Solution { + public ListNode MergeKLists(ListNode[] lists) { + if (lists.Length == 0) + { + return null; + } + + while (lists.Length > 1) + { + var mergedLists = new ListNode[(lists.Length + 1) / 2]; + for (int i = 0; i < lists.Length; i += 2) + { + var l1 = lists[i]; + var l2 = (i + 1 < lists.Length) ? lists[i + 1] : null; + mergedLists[i/2] = (MergeLists(l1, l2)); + } + lists = mergedLists; + } + + return lists[0]; + } + + public ListNode MergeLists(ListNode l1, ListNode l2) + { + var sorted = new ListNode(); + var current = sorted; + + while (l1 != null && l2 != null) + { + if (l1.val <= l2.val) + { + current.next = l1; + l1 = l1.next; + } + else + { + current.next = l2; + l2 = l2.next; + } + current = current.next; + } + + if (l1 != null) + { + current.next = l1; + } else + { + current.next = l2; + } + + return sorted.next; + } +} diff --git a/csharp/0025-reverse-nodes-in-k-group.cs b/csharp/0025-reverse-nodes-in-k-group.cs new file mode 100644 index 000000000..b964f96a4 --- /dev/null +++ b/csharp/0025-reverse-nodes-in-k-group.cs @@ -0,0 +1,58 @@ +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ +public class Solution +{ + public ListNode ReverseKGroup(ListNode head, int k) + { + var dummy = new ListNode(0, head); + var groupPrev = dummy; + var groupNext = dummy; + + while (true) + { + var kth = getKth(groupPrev, k); + if (kth == null) + break; + + groupNext = kth.next; + + // reverse group + var prev = kth.next; + var curr = groupPrev.next; + + while (curr != groupNext) + { + var temp = curr.next; + curr.next = prev; + prev = curr; + curr = temp; + } + + var tmp = groupPrev.next; + groupPrev.next = kth; + groupPrev = tmp; + } + + return dummy.next; + } + + private ListNode getKth(ListNode curr, int k) + { + while (curr != null && k > 0) + { + curr = curr.next; + k -= 1; + } + + return curr; + } +} \ No newline at end of file diff --git a/csharp/0026-remove-duplicates-from-sorted-array.cs b/csharp/0026-remove-duplicates-from-sorted-array.cs new file mode 100644 index 000000000..e1d04a5cf --- /dev/null +++ b/csharp/0026-remove-duplicates-from-sorted-array.cs @@ -0,0 +1,13 @@ +public class Solution { + public int RemoveDuplicates(int[] nums) { + int l = 1, r = 1; + while (r < nums.Length) { + if (nums[r] != nums[r - 1]) { + nums[l] = nums[r]; + ++l; + } + ++r; + } + return l; + } +} diff --git a/csharp/0027-remove-element.cs b/csharp/0027-remove-element.cs new file mode 100644 index 000000000..ea70dc16d --- /dev/null +++ b/csharp/0027-remove-element.cs @@ -0,0 +1,19 @@ +public class Solution { + public int RemoveElement(int[] nums, int val) { + if (nums == null || nums.Length == 0) + return 0; + + int i = 0; + + for (int j = 0; j < nums.Length; j++) + { + while (j < nums.Length && nums[j] == val) + j++; + + if (j < nums.Length) + nums[i++] = nums[j]; + } + + return i; + } +} \ No newline at end of file diff --git a/csharp/0028-find-the-index-of-the-first-occurrence-in-a-string.cs b/csharp/0028-find-the-index-of-the-first-occurrence-in-a-string.cs new file mode 100644 index 000000000..c7e927325 --- /dev/null +++ b/csharp/0028-find-the-index-of-the-first-occurrence-in-a-string.cs @@ -0,0 +1,33 @@ +public class Solution +{ + public int StrStr(string haystack, string needle) + { + if (needle.Length > haystack.Length) return -1; + + int l = 0; + int r = needle.Length - 1; + + while (r < haystack.Length) + { + bool areEqual = true; + for (int i = l, j = 0; i <= r; i++, j++) + { + if (haystack[i] != needle[j]) + { + areEqual = false; + break; + } + } + + if (areEqual) + { + return l; + } + + l++; + r++; + } + + return -1; + } +} \ No newline at end of file diff --git a/csharp/0033-search-in-rotated-sorted-array.cs b/csharp/0033-search-in-rotated-sorted-array.cs new file mode 100644 index 000000000..3887374a3 --- /dev/null +++ b/csharp/0033-search-in-rotated-sorted-array.cs @@ -0,0 +1,25 @@ +public class Solution { + public int Search(int[] nums, int target) { + int low = 0; + int high = nums.Length - 1; + + while(low <= high) { + var mid = (low + high) / 2; + + if(nums[mid] == target) { + return mid; + } else if(nums[low] <= nums[mid]) { + if(target > nums[mid] || target < nums[low]) + low = mid + 1; + else high = mid - 1; + } else { + if(target < nums[mid] || target > nums[high]) + high = mid - 1; + else low = mid + 1; + } + } + + + return -1; + } +} \ No newline at end of file diff --git a/csharp/0034-find-first-and-last-position-of-element-in-sorted-array.cs b/csharp/0034-find-first-and-last-position-of-element-in-sorted-array.cs new file mode 100644 index 000000000..b3136ad39 --- /dev/null +++ b/csharp/0034-find-first-and-last-position-of-element-in-sorted-array.cs @@ -0,0 +1,51 @@ +public class Solution { +public int[] SearchRange(int[] nums, int target) + { + int first = -1, last = -1; + + first = searchEle(nums, target, true); + if (first == -1) + return new int[] { first, last }; + + last = searchEle(nums, target, false); + + + return new int[] { first, last }; + } + + public int searchEle(int[] nums, int target, bool isStart) + { + int n = nums.Length; + int start = 0, end = nums.Length - 1; + + while (start <= end) + { + int mid = (start + end )/ 2; + if (nums[mid] == target) + { + if (isStart) + { + if (mid == start || nums[mid - 1] != target) + { + return mid; + } + end = mid - 1; + } + else + { + if (mid == end || nums[mid + 1] != target) + { + return mid; + } + start = mid + 1; + } + } + else if (nums[mid] > target) + end = mid - 1; + else + start = mid + 1; + + } + return -1; + } +} diff --git a/csharp/0035-search-insert-position.cs b/csharp/0035-search-insert-position.cs new file mode 100644 index 000000000..efffedb94 --- /dev/null +++ b/csharp/0035-search-insert-position.cs @@ -0,0 +1,25 @@ +public class Solution +{ + public int SearchInsert(int[] nums, int target) + { + int leftPointer = 0; + int rightPointer = nums.Length - 1; + while (leftPointer <= rightPointer) + { + int mid = leftPointer + (rightPointer - leftPointer) / 2; + if (nums[mid] == target) + { + return mid; + } + else if (nums[mid] < target) + { + leftPointer = mid + 1; + } + else + { + rightPointer = mid - 1; + } + } + return leftPointer; + } +} \ No newline at end of file diff --git a/csharp/0036-valid-sudoku.cs b/csharp/0036-valid-sudoku.cs new file mode 100644 index 000000000..391468d61 --- /dev/null +++ b/csharp/0036-valid-sudoku.cs @@ -0,0 +1,30 @@ +public class Solution { + public bool IsValidSudoku(char[][] board) { + var rows = new Dictionary>(); + var cols = new Dictionary>(); + var squares = new Dictionary<(int, int), HashSet>(); + + for(var r = 0; r < 9; r++) { + rows[r] = new HashSet(); + for(var c = 0; c < 9; c++) { + if(!cols.ContainsKey(c)) cols[c] = new HashSet(); + if(!squares.ContainsKey((r/3, c/3))) + squares[(r/3, c/3)] = new HashSet(); + + if(board[r][c] == '.') continue; + + + if(rows[r].Contains(board[r][c]) || + cols[c].Contains(board[r][c]) || + squares[(r / 3, c / 3)].Contains(board[r][c])) + return false; + + rows[r].Add(board[r][c]); + cols[c].Add(board[r][c]); + squares[(r / 3, c / 3)].Add(board[r][c]); + } + } + + return true; + } +} \ No newline at end of file diff --git a/csharp/0039-combination-sum.cs b/csharp/0039-combination-sum.cs new file mode 100644 index 000000000..0a482fa19 --- /dev/null +++ b/csharp/0039-combination-sum.cs @@ -0,0 +1,31 @@ +public class Solution { + IList> result = new List>(); + public void backtrack(int index, List path, int total, int[] candidates, int target) { + if(total == target) { + result.Add(path.ToList()); + return; + } + + if(total > target || index >= candidates.Length) return; + + path.Add(candidates[index]); + backtrack(index, + path, + total + candidates[index], + candidates, + target); + + path.Remove(path.Last()); + + backtrack(index + 1, + path, + total, + candidates, + target); + + } + public IList> CombinationSum(int[] candidates, int target) { + backtrack(0, new List(), 0, candidates, target); + return result; + } +} \ No newline at end of file diff --git a/csharp/0040-combination-sum-ii.cs b/csharp/0040-combination-sum-ii.cs new file mode 100644 index 000000000..75a7b991f --- /dev/null +++ b/csharp/0040-combination-sum-ii.cs @@ -0,0 +1,39 @@ +public class Solution +{ + //T: O(2^T), where T is target + public IList> CombinationSum2(int[] candidates, int target) + { + + var result = new List>(); + Array.Sort(candidates); + + void dfs(int pos, Stack current, int target) + { + if (target == 0) + { + result.Add(current.ToList()); + } + if (target <= 0) + { + return; + } + + var prev = -1; + + for (var i = pos; i < candidates.Length; i++) + { + if (candidates[i] == prev) + continue; + + current.Push(candidates[i]); + dfs(i + 1, current, target - candidates[i]); + current.Pop(); + prev = candidates[i]; + } + + } + + dfs(0, new Stack(), target); + return result; + } +} \ No newline at end of file diff --git a/csharp/0042-trapping-rain-water.cs b/csharp/0042-trapping-rain-water.cs new file mode 100644 index 000000000..be79f14b2 --- /dev/null +++ b/csharp/0042-trapping-rain-water.cs @@ -0,0 +1,31 @@ +public class Solution +{ + + public int Trap(int[] height) + { + + if (height is null || height.Length == 0) return 0; + + int left = 0, right = height.Length - 1; + int leftMax = height[left], rightMax = height[right]; + var result = 0; + + while (left < right) + { + if (leftMax < rightMax) + { + left++; + leftMax = Math.Max(leftMax, height[left]); + result += leftMax - height[left]; + } + else + { + right--; + rightMax = Math.Max(rightMax, height[right]); + result += rightMax - height[right]; + } + } + + return result; + } +} \ No newline at end of file diff --git a/csharp/0043-multiply-strings.cs b/csharp/0043-multiply-strings.cs new file mode 100644 index 000000000..332e93c94 --- /dev/null +++ b/csharp/0043-multiply-strings.cs @@ -0,0 +1,51 @@ +public class Solution +{ + //T: O(m*n) | S: O(m+n) + public string Multiply(string num1, string num2) + { + if (string.Equals(num1, "0") || string.Equals(num2, "0")) + return "0"; + var m = num1.Length; + var n = num2.Length; + + var result = new int[m + n]; + + num1 = Reverse(num1); + num2 = Reverse(num2); + + for (var i1 = 0; i1 < num1.Length; i1++) + { + for (var i2 = 0; i2 < num2.Length; i2++) + { + + var digit = (num1[i1] - '0') * (num2[i2] - '0'); + result[i1 + i2] += digit; + result[i1 + i2 + 1] += (result[i1 + i2]) / 10; + result[i1 + i2] = (result[i1 + i2]) % 10; + } + } + + Array.Reverse(result); + var i = 0; + while (i < result.Length && result[i] == 0) + { + i++; + } + + var str = new StringBuilder(); + for (; i < result.Length; i++) + { + //Console.WriteLine(result[i]); + str.Append(result[i]); + } + return str.ToString(); + + } + + private string Reverse(string str) + { + var array = str.ToCharArray(); + Array.Reverse(array); + return new string(array); + } +} \ No newline at end of file diff --git a/csharp/0045-jump-game-ii.cs b/csharp/0045-jump-game-ii.cs new file mode 100644 index 000000000..5b317b3f1 --- /dev/null +++ b/csharp/0045-jump-game-ii.cs @@ -0,0 +1,16 @@ +public class Solution { + public int Jump(int[] nums) { + int left = 0, right = 0, res = 0; + + while (right < nums.Length - 1) { + int maxJump = 0; + for (int i = left; i <= right; i++) { + maxJump = Math.Max(maxJump, i + nums[i]); + } + left = right + 1; + right = maxJump; + res++; + } + return res; + } +} \ No newline at end of file diff --git a/csharp/0046-permutations.cs b/csharp/0046-permutations.cs new file mode 100644 index 000000000..a885f91c2 --- /dev/null +++ b/csharp/0046-permutations.cs @@ -0,0 +1,26 @@ +public class Solution +{ + public IList> Permute(int[] nums) + { + var result = new List>(); + PermuteRecurse(result, nums, 0); + return result; + } + + private void PermuteRecurse(List> res, int[] arr, int start) + { + if (start == arr.Length) + { + var list = arr.Select(t => (t)).ToList(); + res.Add(list); + return; + } + + for (var i = start; i < arr.Length; i++) + { + (arr[start], arr[i]) = (arr[i], arr[start]); + PermuteRecurse(res, arr, start + 1); + (arr[start], arr[i]) = (arr[i], arr[start]); + } + } +} \ No newline at end of file diff --git a/csharp/0047-permutations-ii.cs b/csharp/0047-permutations-ii.cs new file mode 100644 index 000000000..1de2041a8 --- /dev/null +++ b/csharp/0047-permutations-ii.cs @@ -0,0 +1,34 @@ +public class Solution +{ + public IList> PermuteUnique(int[] nums) + { + Array.Sort(nums); + var output = new List>(); + Backtrack(output, nums.ToList(), []); + return output; + } + + private void Backtrack(List> output, List nums, List current) + { + if (nums.Count == 0) + { + output.Add(new List(current)); + } + else + { + for (int i = 0; i < nums.Count; i++) + { + if (i > 0 && nums[i] == nums[i - 1]) + { + continue; + } + int num = nums[i]; + current.Add(num); + nums.Remove(num); + Backtrack(output, nums, current); + nums.Insert(i, num); + current.RemoveAt(current.Count - 1); + } + } + } +} \ No newline at end of file diff --git a/csharp/0048-rotate-image.cs b/csharp/0048-rotate-image.cs new file mode 100644 index 000000000..16ed870d2 --- /dev/null +++ b/csharp/0048-rotate-image.cs @@ -0,0 +1,34 @@ +public class Solution { + public void Rotate(int[][] matrix) { + (int left, int right) = (0, matrix.Length -1); + + while(left < right) { + var limit = right - left; + for(var i = 0; i < limit; i++) { + (int top, int bottom) = (left, right); + + // save the top left position + var topLeft = matrix[top][left + i]; + + // put the bottom left value to the top left position + matrix[top][left + i] = matrix[bottom - i][left]; + + // put the bottom right value to the bottom left position + matrix[bottom - i][left] = matrix[bottom][right - i]; + + // put the top right value to the bottom right position + matrix[bottom][right - i] = matrix[top + i][right]; + + // put the saved top left value to the top right position + matrix[top + i][right] = topLeft; + + + } + + left++; + right--; + } + + return; + } +} \ No newline at end of file diff --git a/csharp/0049-group-anagrams.cs b/csharp/0049-group-anagrams.cs new file mode 100644 index 000000000..6862c2cd0 --- /dev/null +++ b/csharp/0049-group-anagrams.cs @@ -0,0 +1,19 @@ +public class Solution { + public IList> GroupAnagrams(string[] strs) { + var groups = new Dictionary>(); + + foreach (string s in strs) { + char[] hash = new char[26]; + foreach (char c in s) { + hash[c - 'a']++; + } + + string key = new string(hash); + if (!groups.ContainsKey(key)) { + groups[key] = new List(); + } + groups[key].Add(s); + } + return groups.Values.ToList(); + } +} \ No newline at end of file diff --git a/csharp/0050-powx-n.cs b/csharp/0050-powx-n.cs new file mode 100644 index 000000000..8ccc8d897 --- /dev/null +++ b/csharp/0050-powx-n.cs @@ -0,0 +1,18 @@ +public class Solution +{ + public double MyPow(double x, long n) + { + var result = helper(x, Math.Abs(n)); + return n > 0 ? result : 1 / result; + } + + private double helper(double x, long n) + { + if (x == 0) return 0; + if (n == 0) return 1; + + var result = helper(x, n / 2); + result = result * result; + return n % 2 == 1 ? x * result : result; + } +} \ No newline at end of file diff --git a/csharp/0051-n-queens.cs b/csharp/0051-n-queens.cs new file mode 100644 index 000000000..8ed870c69 --- /dev/null +++ b/csharp/0051-n-queens.cs @@ -0,0 +1,51 @@ +public class Solution { + public IList> SolveNQueens(int n) + { + var result = new List>(); + IList board = new List(); + for (int i = 0; i < n; i++) + { + board.Add(new StringBuilder(n)); + board[i].Append('.', n); + + } + backtrackingNQueens(n, 0, board, result, new HashSet<(int i, int j)>(), new HashSet<(int i, int j)>(), new HashSet<(int i, int j)>()); + return result; + } + + private void backtrackingNQueens(int n, int row, IList board, List> result, HashSet<(int i, int j)> col, HashSet<(int i, int j)> positiveDiag, HashSet<(int i, int j)> negativeDiag) + { + if (n == 0) + { + result.Add(board.Select(s => s.ToString()).ToList()); + return; + } + if (row == board.Count) return; + + for (int c = 0; c < board.Count; c++) + { + (int i, int j) column = (0, c); + int m = Math.Min(row, c); + (int i, int j) diag1 = (row - m, c - m); + m = Math.Min(row, board.Count - 1 - c); + (int i, int j) diag2 = (row - m, c + m); + + if (col.Contains(column) || positiveDiag.Contains(diag1) || + negativeDiag.Contains(diag2)) continue; + + col.Add(column); + positiveDiag.Add(diag1); + negativeDiag.Add(diag2); + + + board[row][c] = 'Q'; + backtrackingNQueens(n - 1, row + 1, board, result, col, positiveDiag, negativeDiag); + + board[row][c] = '.'; + col.Remove(column); + positiveDiag.Remove(diag1); + negativeDiag.Remove(diag2); + } + } + +} diff --git a/csharp/0052-n-queens-ii.cs b/csharp/0052-n-queens-ii.cs new file mode 100644 index 000000000..778564378 --- /dev/null +++ b/csharp/0052-n-queens-ii.cs @@ -0,0 +1,48 @@ +public class Solution +{ + public int TotalNQueens(int n) + { + List col = new List(n); + List diag1 = new List(n); + List diag2 = new List(n); + int output = 0; + Backtrack(ref output, col, diag1, diag2, n, 0, n); + return output; + } + + private void Backtrack(ref int output, List col, List diag1, List diag2, int grid_size, int row, int queens_left) + { + if(queens_left == 0) + { + output++; + } + else + { + for(int c = 0; c < grid_size; c++) + { + if(CanPlace(col, diag1, diag2, row, c)) + { + Place(col, diag1, diag2, row, c); + Backtrack(ref output, col, diag1, diag2, grid_size, row + 1, queens_left - 1); + Remove(col, diag1, diag2, row, c); + } + } + } + } + private bool CanPlace(List col, List diag1, List diag2, int r, int c) + { + return !col.Contains(c) && !diag1.Contains(r + c) && !diag2.Contains(r - c); + } + private void Place(List col, List diag1, List diag2, int r, int c) + { + col.Add(c); + diag1.Add(r + c); + diag2.Add(r - c); + } + private void Remove(List col, List diag1, List diag2, int r, int c) + { + col.Remove(c); + diag1.Remove(r + c); + diag2.Remove(r - c); + } +} \ No newline at end of file diff --git a/csharp/0053-maximum-subarray.cs b/csharp/0053-maximum-subarray.cs new file mode 100644 index 000000000..b5f4d2595 --- /dev/null +++ b/csharp/0053-maximum-subarray.cs @@ -0,0 +1,15 @@ +public class Solution { + public int MaxSubArray(int[] nums) { + int maxSub = nums[0]; + int curSum = 0; + + for(int i = 0; i < nums.Length; i++){ + if(curSum < 0){ + curSum = 0; + } + curSum += nums[i]; + maxSub = Math.Max(maxSub, curSum); + } + return maxSub; + } +} diff --git a/csharp/0054-spiral-matrix.cs b/csharp/0054-spiral-matrix.cs new file mode 100644 index 000000000..f5b5bb2eb --- /dev/null +++ b/csharp/0054-spiral-matrix.cs @@ -0,0 +1,45 @@ +public class Solution { + public IList SpiralOrder(int[][] matrix) { + List result = new List(); + int top = 0; + int left = 0; + int right = matrix[0].Length - 1; + int bottom = matrix.Length - 1; + + while (true) + { + //Left to Right + for (int i = left; i <= right; i++) + { + result.Add(matrix[top][i]); + } + top++; + if (left > right || top > bottom) break; + + //Top to Bottom + for (int i = top; i <= bottom; i++) + { + result.Add(matrix[i][right]); + } + right--; + if (left > right || top > bottom) break; + + //Right to Left + for (int i = right; i >= left; i--) + { + result.Add(matrix[bottom][i]); + } + bottom--; + if (left > right || top > bottom) break; + + //Bottom to Top + for (int i = bottom; i >= top; i--) + { + result.Add(matrix[i][left]); + } + left++; + if (left > right || top > bottom) break; + }//Repeat + return result; + } +} diff --git a/csharp/0055-jump-game.cs b/csharp/0055-jump-game.cs new file mode 100644 index 000000000..476aa0107 --- /dev/null +++ b/csharp/0055-jump-game.cs @@ -0,0 +1,14 @@ +public class Solution { + public bool CanJump(int[] nums) { + int goal = nums.Length - 1; + for (int i = nums.Length-1; i>=0; i--) + { + if (nums[i] + i >= goal) + { + goal = i; + } + } + + return goal == 0 ; + } +} diff --git a/csharp/0056-merge-intervals.cs b/csharp/0056-merge-intervals.cs new file mode 100644 index 000000000..4e7b6c263 --- /dev/null +++ b/csharp/0056-merge-intervals.cs @@ -0,0 +1,32 @@ +public class Solution +{ + public int[][] Merge(int[][] intervals) + { + + var sortedInterval = intervals.Clone() as int[][]; + Array.Sort(sortedInterval, (a, b) => a[0] - b[0]); + + var mergedInterval = new List(); + var lastInterval = sortedInterval[0]; + mergedInterval.Add(lastInterval); + + for (var i = 1; i < sortedInterval.Length; i++) + { + var current = sortedInterval[i]; + var lastIntervalEnd = lastInterval[1]; + var nextIntervalEnd = current[1]; + var nextIntervalStart = current[0]; + + if (lastIntervalEnd >= nextIntervalStart) + lastInterval[1] = Math.Max(nextIntervalEnd, lastIntervalEnd); + else + { + lastInterval = current; + mergedInterval.Add(lastInterval); + //lastInterval = current; + } + } + + return mergedInterval.ToArray(); + } +} \ No newline at end of file diff --git a/csharp/0057-insert-interval.cs b/csharp/0057-insert-interval.cs new file mode 100644 index 000000000..a25d3a8e5 --- /dev/null +++ b/csharp/0057-insert-interval.cs @@ -0,0 +1,25 @@ +public class Solution { + public int[][] Insert(int[][] intervals, int[] newInterval) { + var result = new List(); + + for(var i = 0; i < intervals.Length; i++) { + if(newInterval[1] < intervals[i][0]) { + result.Add(newInterval); + result.AddRange( + intervals.AsEnumerable().Skip(i).ToArray()); + + return result.ToArray(); + } + else if(newInterval[0] > intervals[i][1]) { + result.Add(intervals[i]); + } else { + newInterval[0] = Math.Min(intervals[i][0], newInterval[0]); + newInterval[1] = Math.Max(intervals[i][1], newInterval[1]); + } + } + + result.Add(newInterval); + + return result.ToArray(); + } +} \ No newline at end of file diff --git a/csharp/0058-length-of-last-word.cs b/csharp/0058-length-of-last-word.cs new file mode 100644 index 000000000..2b41c6661 --- /dev/null +++ b/csharp/0058-length-of-last-word.cs @@ -0,0 +1,22 @@ +public class Solution { + public int LengthOfLastWord(string s) { + s = s.TrimEnd(); + string[] arr = s.Split(' '); + return arr[arr.Length - 1].Length; + } +} + +public class NeetCodeSolution { + public int LengthOfLastWord(string s) { + var result = 0; + var i = s.Length - 1; + while (s[i] == ' ') --i; + for (; i >= 0; --i) { + if (s[i] == ' ') { + return result; + } + ++result; + } + return result; + } +} diff --git a/csharp/0062-unique-paths.cs b/csharp/0062-unique-paths.cs new file mode 100644 index 000000000..2283bc2b6 --- /dev/null +++ b/csharp/0062-unique-paths.cs @@ -0,0 +1,17 @@ +public class Solution { + public int UniquePaths(int m, int n) { + var row = new int[n]; + Array.Fill(row, 1); + foreach (var i in Enumerable.Range(0, m - 1)) + { + var newRow = new int[n]; + Array.Fill(newRow, 1); + for (int j = n - 2; j >=0; j--) + { + newRow[j] = newRow[j + 1] + row[j]; + } + row = newRow; + } + return row[0]; + } +} diff --git a/csharp/0063-unique-paths-ii.cs b/csharp/0063-unique-paths-ii.cs new file mode 100644 index 000000000..f3ad446c0 --- /dev/null +++ b/csharp/0063-unique-paths-ii.cs @@ -0,0 +1,22 @@ +public class Solution { + public int UniquePathsWithObstacles(int[][] obstacleGrid) { + var rowLength = obstacleGrid.Length; + var colLength = obstacleGrid[0].Length; + + int[] row = new int[colLength]; + Array.Fill(row, 0); + row[colLength - 1] = 1; + + for(int i = rowLength - 1; i >= 0; i--) + { + for(int j = colLength - 1; j >= 0; j--) + { + if(obstacleGrid[i][j] == 1) + row[j] = 0; + else if (j + 1 < colLength) + row[j] = row[j] + row[j + 1]; + } + } + return row[0]; + } +} \ No newline at end of file diff --git a/csharp/0066-plus-one.cs b/csharp/0066-plus-one.cs new file mode 100644 index 000000000..35bf048a9 --- /dev/null +++ b/csharp/0066-plus-one.cs @@ -0,0 +1,12 @@ +public class Solution { + public int[] PlusOne(int[] digits) { + string n = string.Join("", digits); + n = (BigInteger.Parse(n) + 1).ToString(); + int[] result = new int[n.Length]; + for (int i = 0; i < n.Length; i++) + { + result[i] = n[i] - '0'; + } + return result; + } +} \ No newline at end of file diff --git a/csharp/0069-sqrtx.cs b/csharp/0069-sqrtx.cs new file mode 100644 index 000000000..a994a0f06 --- /dev/null +++ b/csharp/0069-sqrtx.cs @@ -0,0 +1,21 @@ +public class Solution +{ + public int MySqrt(int x) + { + int left = 0, right = x; + while (left <= right) + { + int mid = (left + right) >> 1; + long pow = (long)mid * mid; + if (pow <= x) + { + left = mid + 1; + } + else + { + right = mid - 1; + } + } + return left - 1; + } +} diff --git a/csharp/0070-climbing-stairs.cs b/csharp/0070-climbing-stairs.cs new file mode 100644 index 000000000..886c56cb2 --- /dev/null +++ b/csharp/0070-climbing-stairs.cs @@ -0,0 +1,16 @@ +public class Solution { + public int ClimbStairs(int n) { + + int one = 1; + int two = 1; + + for(int i = 0; i < n - 1; i++) + { + int temp = one; + one = one + two; + two = temp; + } + + return one; + } +} diff --git a/csharp/0072-edit-distance.cs b/csharp/0072-edit-distance.cs new file mode 100644 index 000000000..7e2b6d9a9 --- /dev/null +++ b/csharp/0072-edit-distance.cs @@ -0,0 +1,34 @@ +public class Solution +{ + //T: O(N^2), S: O(N^2) + public int MinDistance(string word1, string word2) + { + //Bottom up + var m = word1.Length; + var n = word2.Length; + var dp = new int[m + 1, n + 1]; + + for (var i = 0; i < m + 1; i++) + { + dp[i, n] = m - i; + } + for (var j = 0; j < n + 1; j++) + { + dp[m, j] = n - j; + } + + for (var i = m - 1; i >= 0; i--) + { + for (var j = n - 1; j >= 0; j--) + { + if (word1[i] == word2[j]) + dp[i, j] = dp[i + 1, j + 1]; + else + dp[i, j] = 1 + Math.Min(Math.Min(dp[i + 1, j + 1], dp[i + 1, j]), dp[i, j + 1]); //Replace, Delete, Insert + } + } + + return dp[0, 0]; + } + +} \ No newline at end of file diff --git a/csharp/0073-set-matrix-zeroes.cs b/csharp/0073-set-matrix-zeroes.cs new file mode 100644 index 000000000..a737681c9 --- /dev/null +++ b/csharp/0073-set-matrix-zeroes.cs @@ -0,0 +1,23 @@ +public class Solution { + public void SetZeroes(int[][] matrix) { + int m = matrix.Length, n = matrix[0].Length; + bool firstRowHasZeros = matrix[0].Contains(0); + + for (int i = 1; i < m; i++) + for (int j = 0; j < n; j++) + if (matrix[i][j] == 0) + matrix[i][0] = matrix[0][j] = 0; + + for (int i = 1; i < m; i++) + if (matrix[i][0] == 0) + Array.Fill(matrix[i], 0); + + for (int j = 0; j < n; j++) + if (matrix[0][j] == 0) + for (int i = 0; i < m; i++) + matrix[i][j] = 0; + + if (firstRowHasZeros) + Array.Fill(matrix[0], 0); + } +} diff --git a/csharp/0074-search-a-2d-matrix.cs b/csharp/0074-search-a-2d-matrix.cs new file mode 100644 index 000000000..16bc52716 --- /dev/null +++ b/csharp/0074-search-a-2d-matrix.cs @@ -0,0 +1,28 @@ +public class Solution { + public bool SearchMatrix(int[][] matrix, int target) { + int ROWS = matrix.Length; + int COLS = matrix[0].Length; + int top = 0, bot = ROWS - 1; + int row = 0; + + while(top <= bot) { + row = (top + bot) / 2; + if(target > matrix[row][COLS-1]) top = row + 1; + else if (target < matrix[row][0]) bot = row - 1; + else break; + } + + if (top > bot) return false; + + int l = 0, r = COLS - 1; + + while (l <= r) { + int m = (l + r) / 2; + if (target > matrix[row][m]) l = m + 1; + else if (target < matrix[row][m]) r = m - 1; + else return true; + } + + return false; + } +} diff --git a/csharp/0075-sort-colors.cs b/csharp/0075-sort-colors.cs new file mode 100644 index 000000000..1f0c769b3 --- /dev/null +++ b/csharp/0075-sort-colors.cs @@ -0,0 +1,28 @@ +public class Solution { + public void SortColors(int[] nums) { + int low = 0; + int high = nums.Length - 1; + int mid = 0; + + while (mid <= high) { + switch (nums[mid]) { + case 0: + int temp0 = nums[low]; + nums[low] = nums[mid]; + nums[mid] = temp0; + low++; + mid++; + break; + case 1: + mid++; + break; + case 2: + int temp2 = nums[mid]; + nums[mid] = nums[high]; + nums[high] = temp2; + high--; + break; + } + } + } +} \ No newline at end of file diff --git a/csharp/0076-minimum-window-substring.cs b/csharp/0076-minimum-window-substring.cs new file mode 100644 index 000000000..e60dd7a44 --- /dev/null +++ b/csharp/0076-minimum-window-substring.cs @@ -0,0 +1,60 @@ +public class Solution +{ + + public string MinWindow(string s, string t) + + { + if (string.IsNullOrEmpty(t)) return string.Empty; + + var countT = new Dictionary(); + var window = new Dictionary(); + + foreach (var c in t) + { + AddCharToDictionary(c, countT); + } + + var have = 0; + var need = countT.Count; + var left = 0; + var res = new[] { -1, -1 }; + var resultLength = int.MaxValue; + for (var right = 0; right < s.Length; right++) + { + var c = s[right]; + AddCharToDictionary(c, window); + + if (countT.ContainsKey(c) && window[c] == countT[c]) have++; + + while (have == need) + { + // update our result + var windowSize = right - left + 1; + if (windowSize < resultLength) + { + res = new[] { left, right }; + resultLength = windowSize; + } + + // pop from the left of our window + window[s[left]]--; + if (countT.ContainsKey(s[left]) && window[s[left]] < countT[s[left]]) + { + have--; + } + + left++; + } + } + + return resultLength == int.MaxValue + ? string.Empty + : s.Substring(res[0], res[1] - res[0] + 1); + } + + private void AddCharToDictionary(char c, IDictionary dict) + { + if (dict.ContainsKey(c)) dict[c]++; + else dict.Add(c, 1); + } +} diff --git a/csharp/0077-combinations.cs b/csharp/0077-combinations.cs new file mode 100644 index 000000000..1f0fdccd4 --- /dev/null +++ b/csharp/0077-combinations.cs @@ -0,0 +1,26 @@ +public class Solution +{ + public IList> Combine(int n, int k) + { + var output = new List>(); + Backtrack(output, [], n, k, 1); + return output; + } + + private void Backtrack(List> output, List current, int n, int k, int index) + { + if (current.Count == k) + { + output.Add(new List(current)); + } + else + { + for (int i = index; i <= n; i++) + { + current.Add(i); + Backtrack(output, current, n, k, i + 1); + current.RemoveAt(current.Count - 1); + } + } + } +} \ No newline at end of file diff --git a/csharp/0078-subsets.cs b/csharp/0078-subsets.cs new file mode 100644 index 000000000..2cf6b2739 --- /dev/null +++ b/csharp/0078-subsets.cs @@ -0,0 +1,19 @@ +public class Solution { + private List subset = new List(); + private List> result = new List>(); + private void backtrack(int i, int[] nums) { + if(i >= nums.Length) { + result.Add(new List(subset)); + return; + } + subset.Add(nums[i]); + backtrack(i + 1, nums); + subset.Remove(nums[i]); + backtrack(i + 1, nums); + + } + public IList> Subsets(int[] nums) { + backtrack(0, nums); + return result; + } +} \ No newline at end of file diff --git a/csharp/0079-word-search.cs b/csharp/0079-word-search.cs new file mode 100644 index 000000000..b88c25c5c --- /dev/null +++ b/csharp/0079-word-search.cs @@ -0,0 +1,44 @@ +public class Solution +{ + //T:O(N*M*4^LenOfWord), S: O(m+n+ L) + //S: O(m+n) is for the visited array + public bool Exist(char[][] board, string word) + { + var rows = board.Length; + var cols = board[0].Length; + var visited = new bool[rows, cols]; + + for (var i = 0; i < rows; i++) + { + for (var j = 0; j < cols; j++) + { + if (board[i][j] == word[0] && search(i, j, 0, word, board, visited)) + return true; + } + } + + return false; + } + + public bool search(int r, int c, int index, string word, char[][] board, bool[,] visited) + { + var rows = board.Length; + var cols = board[0].Length; + + if (index == word.Length) + return true; + if (r < 0 || c < 0 || r >= rows || c >= cols || word[index] != board[r][c] || visited[r, c]) + return false; + + visited[r, c] = true; + var result = search(r + 1, c, index + 1, word, board, visited) || + search(r - 1, c, index + 1, word, board, visited) || + search(r, c + 1, index + 1, word, board, visited) || + search(r, c - 1, index + 1, word, board, visited); + + visited[r, c] = false; + return result; + + } + +} \ No newline at end of file diff --git a/csharp/0080-remove-duplicates-from-sorted-array-ii.cs b/csharp/0080-remove-duplicates-from-sorted-array-ii.cs new file mode 100644 index 000000000..ff59ab1d1 --- /dev/null +++ b/csharp/0080-remove-duplicates-from-sorted-array-ii.cs @@ -0,0 +1,29 @@ +public class Solution +{ + public int RemoveDuplicates(int[] nums) + { + int l = 0; + int r = 0; + + while ( r < nums.Length) + { + int count = 1; + + while (r + 1 < nums.Length && nums[r] == nums[r + 1]) + { + count++; + r++; + } + + for (int i = 0; i < Math.Min(2, count); i++) + { + nums[l] = nums[r]; + l++; + } + + r++; + } + + return l; + } +} \ No newline at end of file diff --git a/csharp/0084-largest-rectangle-in-histogram.cs b/csharp/0084-largest-rectangle-in-histogram.cs new file mode 100644 index 000000000..d0a968b03 --- /dev/null +++ b/csharp/0084-largest-rectangle-in-histogram.cs @@ -0,0 +1,19 @@ +public class Solution { + public int LargestRectangleArea(int[] heights) { + int n = heights.Length, max = 0; + var stack = new Stack(); + for(int i = 0; i <= n; i++) + { + var height = i < n ? heights[i] : 0; + while(stack.Count != 0 && heights[stack.Peek()] > height) + { + var currHeight = heights[stack.Pop()]; + var prevIndex = stack.Count == 0 ? -1 : stack.Peek(); + max = Math.Max(max, currHeight * (i - 1 - prevIndex)); + } + stack.Push(i); + } + + return max; + } +} diff --git a/csharp/0088-merge-sorted-array.cs b/csharp/0088-merge-sorted-array.cs new file mode 100644 index 000000000..3c7b51ee3 --- /dev/null +++ b/csharp/0088-merge-sorted-array.cs @@ -0,0 +1,25 @@ +public class Solution +{ + public void Merge(int[] nums1, int m, int[] nums2, int n) + { + var index1 = m - 1; + var index2 = n - 1; + var targetIndex = m + n - 1; + + for (; targetIndex >= 0; targetIndex--) + { + var useNums1 = (index2 < 0) || (index1 >= 0 && nums1[index1] >= nums2[index2]); + + if (useNums1) + { + nums1[targetIndex] = nums1[index1]; + index1 -= 1; + } + else + { + nums1[targetIndex] = nums2[index2]; + index2 -= 1; + } + } + } +} \ No newline at end of file diff --git a/csharp/0090-subsets-ii.cs b/csharp/0090-subsets-ii.cs new file mode 100644 index 000000000..72339bcf6 --- /dev/null +++ b/csharp/0090-subsets-ii.cs @@ -0,0 +1,25 @@ +public class Solution +{ + + //T: O(N*2^N) + + public IList> Subsets(int[] nums) + { + var list = new List>(); + Array.Sort(nums); + backTrack(list, new List(), nums, 0); + return list; + } + + private void backTrack(List> list, List curr, int[] nums, int start) + { + list.Add(new List(curr)); + for (var i = start; i < nums.Length; i++) + { + if (i > start && nums[i] == nums[i - 1]) continue; + curr.Add(nums[i]); + backTrack(list, curr, nums, i + 1); + curr.RemoveAt(curr.Count - 1); + } + } +} \ No newline at end of file diff --git a/csharp/0091-decode-ways.cs b/csharp/0091-decode-ways.cs new file mode 100644 index 000000000..6c5bff602 --- /dev/null +++ b/csharp/0091-decode-ways.cs @@ -0,0 +1,20 @@ +public class Solution { + public int NumDecodings(string s) { + List dp = Enumerable.Repeat(1, s.Length + 1).ToList(); + + for(var i = s.Length - 1; i >= 0; i--) { + if (s[i] == '0') + dp[i] = 0; + else + dp[i] = dp[i + 1]; + + if (i + 1 < s.Length && ( + s[i] == '1' || + (s[i] == '2' && "0123456".Contains(s[i + 1])) + )) + dp[i] += dp[i + 2]; + } + + return dp[0]; + } +} \ No newline at end of file diff --git a/csharp/0093-restore-ip-addresses.cs b/csharp/0093-restore-ip-addresses.cs new file mode 100644 index 000000000..f15c37ce7 --- /dev/null +++ b/csharp/0093-restore-ip-addresses.cs @@ -0,0 +1,42 @@ +public class Solution +{ + public IList RestoreIpAddresses(string s) + { + if (s.Length < 4) return []; + + var output = new List(); + Backtrack(output, new StringBuilder(), s, null, 0, 0); + return output; + } + private void Backtrack(List output, StringBuilder sb, ReadOnlySpan input, int? octet, int octet_count, int index) + { + if (octet_count > 4) return; + + if (sb.Length - 3 == input.Length) + { + output.Add(sb.ToString()); + } + else + { + int max_index = Math.Min(index + 3, input.Length); + for (int i = index; i < max_index; i++) + { + if (octet.HasValue) + { + if (octet.Value == 0 || octet > 25 || octet == 25 && input[i] > '5') break; + + octet *= 10; + octet += input[i] - '0'; + } + else + { + octet = input[i] - '0'; + } + string octet_string = sb.Length == 0 ? octet.Value.ToString() : $".{octet.Value}"; + sb.Append(octet_string); + Backtrack(output, sb, input, null, octet_count + 1, i + 1); + sb.Remove(sb.Length - octet_string.Length, octet_string.Length); + } + } + } +} \ No newline at end of file diff --git a/csharp/0094-binary-tree-inorder-traversal.cs b/csharp/0094-binary-tree-inorder-traversal.cs new file mode 100644 index 000000000..2e38af000 --- /dev/null +++ b/csharp/0094-binary-tree-inorder-traversal.cs @@ -0,0 +1,48 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public IList InorderTraversal(TreeNode root) { + // return InorderTraversalRecursive(root).ToList(); + return InorderTraversalIterative(root).ToList(); + } + + // Time: O(n) + // Space: O(n) + private IEnumerable InorderTraversalRecursive(TreeNode node) { + if (node == null) { + return Enumerable.Empty(); + } + + return InorderTraversalRecursive(node.left) + .Append(node.val) + .Concat(InorderTraversalRecursive(node.right)); + } + + // Time: O(n) + // Space: O(n) + private IEnumerable InorderTraversalIterative(TreeNode node) { + List result = new(); + Stack stack = new(); + while (node != null || stack.Any()) { + while (node != null) { + stack.Push(node); + node = node.left; + } + node = stack.Pop(); + result.Add(node.val); + node = node.right; + } + return result; + } +} diff --git a/csharp/0097-interleaving-string.cs b/csharp/0097-interleaving-string.cs new file mode 100644 index 000000000..828acbd1e --- /dev/null +++ b/csharp/0097-interleaving-string.cs @@ -0,0 +1,31 @@ +public class Solution +{ + //T:O(M*N) + public bool IsInterleave(string s1, string s2, string s3) + { + if (s1.Length + s2.Length != s3.Length) + return false; + + var dp = new bool[s1.Length + 1, s2.Length + 1]; + dp[s1.Length, s2.Length] = true; + + for (var i = s1.Length; i >= 0; i--) + { + for (var j = s2.Length; j >= 0; j--) + { + if (i < s1.Length && s1[i] == s3[i + j] && dp[i + 1, j]) + { + dp[i, j] = true; + } + if (j < s2.Length && s2[j] == s3[i + j] && dp[i, j + 1]) + { + dp[i, j] = true; + } + } + + } + + return dp[0, 0]; + } + +} \ No newline at end of file diff --git a/csharp/0098-validate-binary-search-tree.cs b/csharp/0098-validate-binary-search-tree.cs new file mode 100644 index 000000000..985aa3f76 --- /dev/null +++ b/csharp/0098-validate-binary-search-tree.cs @@ -0,0 +1,25 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public bool IsValidBST(TreeNode root, int? left = null, int? right = null) { + if(root == null) return true; + + if((left != null && root.val <= left) || + (right != null && root.val >= right)) + return false; + + return IsValidBST(root.left, left, root.val) && + IsValidBST(root.right, root.val, right); + } +} \ No newline at end of file diff --git a/csharp/0100-same-tree.cs b/csharp/0100-same-tree.cs new file mode 100644 index 000000000..c03868acf --- /dev/null +++ b/csharp/0100-same-tree.cs @@ -0,0 +1,24 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public bool IsSameTree(TreeNode p, TreeNode q) { + if (p == null || q == null) + return p == q; + + return + p.val == q.val && + IsSameTree(p.left, q.left) && + IsSameTree(p.right, q.right); + } +} \ No newline at end of file diff --git a/csharp/0102-binary-tree-level-order-traversal.cs b/csharp/0102-binary-tree-level-order-traversal.cs new file mode 100644 index 000000000..2e18acbe9 --- /dev/null +++ b/csharp/0102-binary-tree-level-order-traversal.cs @@ -0,0 +1,42 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public IList> LevelOrder(TreeNode root) { + var result = new List>(); + if(root == null) return result; + + var q = new Queue(); + q.Enqueue(root); + + while(true) { + var curLevelCount = q.Count; + if(curLevelCount == 0) break; + var curNodes = new List(); + while(curLevelCount > 0) { + var cur = q.Dequeue(); + curNodes.Add(cur.val); + + if(cur.left != null) + q.Enqueue(cur.left); + + if(cur.right != null) + q.Enqueue(cur.right); + curLevelCount--; + } + result.Add(curNodes); + } + + return result; + } +} \ No newline at end of file diff --git a/csharp/0104-maximum-depth-of-binary-tree.cs b/csharp/0104-maximum-depth-of-binary-tree.cs new file mode 100644 index 000000000..e3b4a13a1 --- /dev/null +++ b/csharp/0104-maximum-depth-of-binary-tree.cs @@ -0,0 +1,22 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution +{ + public int MaxDepth(TreeNode root) + { + if (root == null) return 0; + + return 1 + Math.Max(MaxDepth(root.left), MaxDepth(root.right)); + } +} \ No newline at end of file diff --git a/csharp/0105-construct-binary-tree-from-preorder-and-inorder-traversal.cs b/csharp/0105-construct-binary-tree-from-preorder-and-inorder-traversal.cs new file mode 100644 index 000000000..b247957b6 --- /dev/null +++ b/csharp/0105-construct-binary-tree-from-preorder-and-inorder-traversal.cs @@ -0,0 +1,38 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ + +public class Solution +{ + public TreeNode BuildTree(int[] preorder, int[] inorder) + { + return BuildTreeHelper(0, 0, inorder.Length - 1, preorder, inorder); + } + + private TreeNode BuildTreeHelper(int preStart, int inStart, int inEnd, int[] preorder, int[] inorder) + { + if (preorder.Length == 0 && inorder.Length == 0) + return null; + + if (preStart > preorder.Length - 1 || inStart > inEnd) + return null; + + var rootNode = new TreeNode(preorder[preStart]); + var mid = Array.IndexOf(inorder, preorder[preStart]); + + rootNode.left = BuildTreeHelper(preStart + 1, inStart, mid - 1, preorder, inorder); + rootNode.right = BuildTreeHelper(preStart + mid - inStart + 1, mid + 1, inEnd, preorder, inorder); + + return rootNode; + } +} \ No newline at end of file diff --git a/csharp/0110-balanced-binary-tree.cs b/csharp/0110-balanced-binary-tree.cs new file mode 100644 index 000000000..2240044a3 --- /dev/null +++ b/csharp/0110-balanced-binary-tree.cs @@ -0,0 +1,34 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private bool _result = true; + + public bool IsBalanced(TreeNode root) { + Dfs(root); + return _result; + } + + private int Dfs(TreeNode root) { + if(root == null) { + return -1; + } + + var leftDepth = Dfs(root.left); + var rightDepth = Dfs(root.right); + + _result = _result && (Math.Abs(rightDepth - leftDepth) <= 1); + + return Math.Max(leftDepth, rightDepth) + 1; + } +} diff --git a/csharp/0112-path-sum.cs b/csharp/0112-path-sum.cs new file mode 100644 index 000000000..9f47fba48 --- /dev/null +++ b/csharp/0112-path-sum.cs @@ -0,0 +1,24 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public bool HasPathSum(TreeNode root, int targetSum) { + if(root == null) return false; + if(root.left == null && root.right == null) + return targetSum == root.val; + + return + HasPathSum(root.left, targetSum - root.val) || + HasPathSum(root.right, targetSum - root.val); + } +} \ No newline at end of file diff --git a/csharp/0115-distinct-subsequences.cs b/csharp/0115-distinct-subsequences.cs new file mode 100644 index 000000000..17bb0111b --- /dev/null +++ b/csharp/0115-distinct-subsequences.cs @@ -0,0 +1,28 @@ +public class Solution { + public int NumDistinct(string s, string t) { + var nS = s.Length; + var nT = t.Length; + + if (nS < nT) return 0; + + var dp = new int[nS + 1, nT + 1]; + + for (int i = 0; i <= nS; i++) { + dp[i, 0] = 1; + } + + for (int i = 1; i <= nS; i++) { + var sIndex = i - 1; + for (int j = 1; j <= nT; j++) { + var tIndex = j - 1; + if (s[sIndex] == t[tIndex]) { + dp[i, j] = dp[i - 1, j - 1] + dp[i - 1, j]; + } else { + dp[i, j] = dp[i - 1, j]; + } + } + } + + return dp[nS, nT]; + } +} diff --git a/csharp/0118-pascals-triangle.cs b/csharp/0118-pascals-triangle.cs new file mode 100644 index 000000000..7b1080f89 --- /dev/null +++ b/csharp/0118-pascals-triangle.cs @@ -0,0 +1,20 @@ +public class Solution { + public IList> Generate(int numRows) { + int itr = 1; + IList> triangle = new List>(); + while(itr <= numRows) { + List row = new List(); + for (int i = 0; i < itr; ++i) { + if (i == 0 || i == itr - 1) { + row.Add(1); + } + else { + row.Add(triangle[itr - 2][i - 1] + triangle[itr - 2][i]); + } + } + triangle.Add(row); + ++itr; + } + return triangle; + } +} diff --git a/csharp/0121-best-time-to-buy-and-sell-stock.cs b/csharp/0121-best-time-to-buy-and-sell-stock.cs new file mode 100644 index 000000000..d98cc9594 --- /dev/null +++ b/csharp/0121-best-time-to-buy-and-sell-stock.cs @@ -0,0 +1,13 @@ +public class Solution { + public int MaxProfit(int[] prices) { + int maxProfit = 0; + int minPrice = prices[0]; + + for (int i = 1; i < prices.Length; i++) { + int currPrice = prices[i]; + minPrice = Math.Min(minPrice, currPrice); + maxProfit = Math.Max(maxProfit, currPrice - minPrice); + } + return maxProfit; + } +} \ No newline at end of file diff --git a/csharp/0124-binary-tree-maximum-path-sum.cs b/csharp/0124-binary-tree-maximum-path-sum.cs new file mode 100644 index 000000000..1c869a6b9 --- /dev/null +++ b/csharp/0124-binary-tree-maximum-path-sum.cs @@ -0,0 +1,25 @@ +public class Solution { + +int maxPathSum = Int32.MinValue; + + public int MaxPathSum(TreeNode root) + { + DfsMaxPathSum(root); + return maxPathSum; + } + + private int DfsMaxPathSum(TreeNode root) + { + if (root == null) + return 0; + + int leftMax = DfsMaxPathSum(root.left), + rightMax = DfsMaxPathSum(root.right), + currentMax = 0; + + currentMax = Math.Max(currentMax, Math.Max(leftMax + root.val, rightMax + root.val)); + maxPathSum = Math.Max(maxPathSum, leftMax + root.val + rightMax); + + return currentMax; + } +} diff --git a/csharp/0125-valid-palindrome.cs b/csharp/0125-valid-palindrome.cs new file mode 100644 index 000000000..b843295bb --- /dev/null +++ b/csharp/0125-valid-palindrome.cs @@ -0,0 +1,21 @@ +public class Solution { + public bool IsPalindrome(string s) { + int left = 0; + int right = s.Length - 1; + + while (left < right) { + if (!char.IsLetterOrDigit(s[left])) { + left++; + } else if (!char.IsLetterOrDigit(s[right])) { + right--; + } else { + if (char.ToLower(s[left]) != char.ToLower(s[right])) { + return false; + } + left++; + right--; + } + } + return true; + } +} \ No newline at end of file diff --git a/csharp/0127-word-ladder.cs b/csharp/0127-word-ladder.cs new file mode 100644 index 000000000..53be35ff3 --- /dev/null +++ b/csharp/0127-word-ladder.cs @@ -0,0 +1,73 @@ +public class Solution +{ + //T: O(N^2*M) where N is the length of the word, M is total no. of words + //S: O(N^2*M) where N is the length of the word, M is total no. of words + public int LadderLength(string beginWord, string endWord, IList wordList) + { + if (!wordList.Contains(endWord)) + { + return 0; + } + + var nei = new Dictionary>(); + if (!wordList.Contains(beginWord)) + wordList.Add(beginWord); + + foreach (var word in wordList) + { + for (var j = 0; j < word.Length; j++) + { + var pattern = word.Substring(0, j) + "*" + word.Substring(j + 1); + nei.TryAdd(pattern, new HashSet()); + nei[pattern].Add(word); + } + } + + foreach (var neiWord in nei.Keys) + { + foreach (var val in nei[neiWord]) + { + Console.WriteLine("neiWord : " + neiWord + " val : " + val); + } + } + + var visited = new HashSet(); + visited.Add(beginWord); + + var queue = new Queue(); + queue.Enqueue(beginWord); + + var result = 1; + + while (queue.Count > 0) + { + var count = queue.Count; + for (var i = 0; i < count; i++) + { + var item = queue.Dequeue(); + if (string.Equals(item, endWord)) + return result; + + for (var j = 0; j < item.Length; j++) + { + var pattern = item.Substring(0, j) + "*" + item.Substring(j + 1); + foreach (var neiWord in nei[pattern]) + { + if (!visited.Contains(neiWord)) + { + queue.Enqueue(neiWord); + visited.Add(neiWord); + + } + } + + } + + } + result += 1; + } + + return 0; + } +} \ No newline at end of file diff --git a/csharp/0128-longest-consecutive-sequence.cs b/csharp/0128-longest-consecutive-sequence.cs new file mode 100644 index 000000000..b19878df3 --- /dev/null +++ b/csharp/0128-longest-consecutive-sequence.cs @@ -0,0 +1,22 @@ +public class Solution { + public int LongestConsecutive(int[] nums) { + if (nums.Length < 2) return nums.Length; + + var set = new HashSet(nums); + var longest = 0; + foreach (var n in set) + { + if (!set.Contains(n-1)) + { + var length = 0; + while (set.Contains(n+length)) + { + length++; + longest = Math.Max(longest, length); + } + } + } + + return longest; + } +} diff --git a/csharp/0130-surrounded-regions.cs b/csharp/0130-surrounded-regions.cs new file mode 100644 index 000000000..5ff1ba463 --- /dev/null +++ b/csharp/0130-surrounded-regions.cs @@ -0,0 +1,50 @@ +public class Solution { + public void Solve(char[][] board) { + var n = board.Length; + + if (n == 0) return; + var m = board[0].Length; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + if ((i == 0 || j == 0 || i == n - 1 || j == m - 1) && board[i][j] == 'O') { + CaptureDfs(board, i, j); + } + } + } + + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + if (board[i][j] == 'O') { + board[i][j] = 'X'; + } + } + } + + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + if (board[i][j] == 'T') { + board[i][j] = 'O'; + } + } + } + } + + private void CaptureDfs(char[][] board, int x, int y) { + var n = board.Length; + var m = board[0].Length; + + if (x >= n || x < 0 || y >= m || y < 0) { + return; + } + + if (board[x][y] == 'T' || board[x][y] == 'X') return; + + board[x][y] = 'T'; + CaptureDfs(board, x+1, y); + CaptureDfs(board, x-1, y); + CaptureDfs(board, x, y+1); + CaptureDfs(board, x, y-1); + + } +} diff --git a/csharp/0131-palindrome-partitioning.cs b/csharp/0131-palindrome-partitioning.cs new file mode 100644 index 000000000..9f23ac76b --- /dev/null +++ b/csharp/0131-palindrome-partitioning.cs @@ -0,0 +1,46 @@ +public class Solution +{ + //O(N.2^N) + public IList> Partition(string s) + { + var result = new List>(); + var stack = new List(); + + void dfs(int i) + { + if (i >= s.Length) + { + result.Add(stack.ToList()); + return; + } + + for (var j = i; j < s.Length; j++) + { + if (IsPalindrome(s, i, j)) + { + stack.Add(s.Substring(i, j - i + 1)); + dfs(j + 1); + stack.RemoveAt(stack.Count - 1); + } + } + + } + + dfs(0); + + return result; + } + + public bool IsPalindrome(string s, int l, int r) + { + while (l < r) + { + if (s[l] != s[r]) + return false; + l++; + r--; + } + + return true; + } +} \ No newline at end of file diff --git a/csharp/0133-clone-graph.cs b/csharp/0133-clone-graph.cs new file mode 100644 index 000000000..c3fde4b0e --- /dev/null +++ b/csharp/0133-clone-graph.cs @@ -0,0 +1,41 @@ +/* +// Definition for a Node. +public class Node { + public int val; + public IList neighbors; + + public Node() { + val = 0; + neighbors = new List(); + } + + public Node(int _val) { + val = _val; + neighbors = new List(); + } + + public Node(int _val, List _neighbors) { + val = _val; + neighbors = _neighbors; + } +} +*/ + +public class Solution { + + Dictionary map = new Dictionary(); + + public Node CloneGraph(Node node) { + if (node == null) return null; + if (!map.ContainsKey(node)) + { + map.Add(node, new Node(node.val)); + foreach (var n in node.neighbors) + { + map[node].neighbors.Add(CloneGraph(n)); + } + } + + return map[node]; + } +} diff --git a/csharp/0134-gas-station.cs b/csharp/0134-gas-station.cs new file mode 100644 index 000000000..3e582e2b6 --- /dev/null +++ b/csharp/0134-gas-station.cs @@ -0,0 +1,18 @@ +public class Solution { + public int CanCompleteCircuit(int[] gas, int[] cost) { + if (gas.Sum() < cost.Sum()) { + return -1; + } + + int res = 0, total = 0; + + for (int i = 0; i < gas.Length; i++) { + total += gas[i] - cost[i]; + if (total < 0) { + total = 0; + res = i + 1; + } + } + return res; + } +} \ No newline at end of file diff --git a/csharp/0136-single-number.cs b/csharp/0136-single-number.cs new file mode 100644 index 000000000..a85091fdf --- /dev/null +++ b/csharp/0136-single-number.cs @@ -0,0 +1,10 @@ +public class Solution { + public int SingleNumber(int[] nums) { + var x = 0; + for (int i=0;i < nums.Length; i++) + { + x ^= nums[i]; + } + return x; + } +} \ No newline at end of file diff --git a/csharp/0138-copy-list-with-random-pointer.cs b/csharp/0138-copy-list-with-random-pointer.cs new file mode 100644 index 000000000..63f8282a4 --- /dev/null +++ b/csharp/0138-copy-list-with-random-pointer.cs @@ -0,0 +1,54 @@ +/* +// Definition for a Node. +public class Node { + public int val; + public Node next; + public Node random; + + public Node(int _val) { + val = _val; + next = null; + random = null; + } +} +*/ + +public class Solution { + public Node CopyRandomList(Node head) { + var map = new Dictionary(); + if (head == null) return null; + var copy = new Node(head.val); + + + map[head] = copy; + var cur1 = head.next; + var cur2 = copy; + + // first pass to create the nodes + while(cur1 != null) { + var next2 = new Node(cur1.val); + cur2.next = next2; + //map the nodes + map[cur1] = next2; + cur1 = cur1.next; + cur2 = cur2.next; + } + + cur1 = head; + cur2 = copy; + + // second pass to update the random pointers + while(cur2 != null) { + var random = cur1.random != null + ? map[cur1.random] + : null; + + cur2.random = random; + cur1 = cur1.next; + cur2 = cur2.next; + + + } + return copy; + } +} \ No newline at end of file diff --git a/csharp/0139-word-break.cs b/csharp/0139-word-break.cs new file mode 100644 index 000000000..abc4ad281 --- /dev/null +++ b/csharp/0139-word-break.cs @@ -0,0 +1,22 @@ +public class Solution +{ + public bool WordBreak(string s, IList wordDict) + { + bool[] dp = new bool[s.Length + 1]; + Array.Fill(dp, false); + dp[s.Length] = true; + + for (int i = s.Length - 1; i >= 0; i--) + { + foreach (var w in wordDict) + { + if (i + w.Length <= s.Length && s.Substring(i, w.Length) == w) + dp[i] = dp[i + w.Length]; + + if (dp[i] == true) + break; + } + } + return dp[0]; + } +} diff --git a/csharp/0141-linked-list-cycle.cs b/csharp/0141-linked-list-cycle.cs new file mode 100644 index 000000000..af4d51fdc --- /dev/null +++ b/csharp/0141-linked-list-cycle.cs @@ -0,0 +1,25 @@ +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ + +public class Solution { + public bool HasCycle(ListNode head) { + ListNode slow = head, fast = head; + + while (fast != null && fast.next != null) + { + fast = fast.next.next; + slow = slow.next; + if (slow.Equals(fast)) return true; + } + return false; + } +} diff --git a/csharp/0143-reorder-list.cs b/csharp/0143-reorder-list.cs new file mode 100644 index 000000000..3d0448965 --- /dev/null +++ b/csharp/0143-reorder-list.cs @@ -0,0 +1,62 @@ +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ +public class Solution { + private ListNode revList(ListNode head) { + ListNode prev = null; + + var cur = head; + + while(cur != null) { + var temp = cur.next; + cur.next = prev; + prev = cur; + cur = temp; + } + return prev; + + } + public void ReorderList(ListNode head) { + if(head == null || head.next == null) return; + + // find middle + var slow = head; + var fast = head.next; + var nodeCount = 1; + while(true) { + if(fast == null || fast.next == null) break; + slow = slow.next; + nodeCount++; + fast = fast.next.next; + } + + var middle = slow.next; + slow.next = null; + + + var secondHead = revList(middle); + + // merge two lists + var first = head; + var second = secondHead; + while(second != null) { + var temp1 = first.next; + var temp2 = second.next; + first.next = second; + second.next = temp1; + first = temp1; + second = temp2; + + } + + return; + } +} \ No newline at end of file diff --git a/csharp/0144-binary-tree-preorder-traversal.cs b/csharp/0144-binary-tree-preorder-traversal.cs new file mode 100644 index 000000000..467f0eed9 --- /dev/null +++ b/csharp/0144-binary-tree-preorder-traversal.cs @@ -0,0 +1,20 @@ +public class Solution { + public IList PreorderTraversal(TreeNode root) + { + var output = new List(); + Traverse(output, root); + return output; + } + + private void Traverse(List output, TreeNode root) + { + if (root is null) + { + return; + } + + output.Add(root.val); + Traverse(output, root.left); + Traverse(output, root.right); + } +} \ No newline at end of file diff --git a/csharp/0145-binary-tree-postorder-traversal.cs b/csharp/0145-binary-tree-postorder-traversal.cs new file mode 100644 index 000000000..fded66c94 --- /dev/null +++ b/csharp/0145-binary-tree-postorder-traversal.cs @@ -0,0 +1,20 @@ +public class Solution { + public IList PostorderTraversal(TreeNode root) + { + var output = new List(); + Traverse(output, root); + return output; + } + + private void Traverse(List output, TreeNode root) + { + if (root is null) + { + return; + } + + Traverse(output, root.left); + Traverse(output, root.right); + output.Add(root.val); + } +} \ No newline at end of file diff --git a/csharp/0146-lru-cache.cs b/csharp/0146-lru-cache.cs new file mode 100644 index 000000000..668206cbe --- /dev/null +++ b/csharp/0146-lru-cache.cs @@ -0,0 +1,45 @@ +public class LRUCache { + private Dictionary> _dict = new(); + private LinkedList<(int key, int value)> _values = new(); + + private int _capacity; + + public LRUCache(int capacity) { + _capacity = capacity; + } + + public int Get(int key) { + if (!_dict.ContainsKey(key)) { + return -1; + } + + var node = _dict[key]; + _values.Remove(node); + _values.AddFirst(node); + + return node.Value.value; + } + + public void Put(int key, int value) { + if (!_dict.ContainsKey(key) && _dict.Count >= _capacity) { + var node = _values.Last; + _dict.Remove(node.Value.key); + _values.Remove(node); + } + + var existingNode = _dict.GetValueOrDefault(key); + if (existingNode != null) { + _values.Remove(existingNode); + } + + _values.AddFirst((key, value)); + _dict[key] = _values.First; + } +} + +/** + * Your LRUCache object will be instantiated and called as such: + * LRUCache obj = new LRUCache(capacity); + * int param_1 = obj.Get(key); + * obj.Put(key,value); + */ diff --git a/csharp/0148-sort-list.cs b/csharp/0148-sort-list.cs new file mode 100644 index 000000000..52db9e9fa --- /dev/null +++ b/csharp/0148-sort-list.cs @@ -0,0 +1,49 @@ +public class Solution +{ + public ListNode SortList(ListNode head) + { + if (head is null || head.next is null) + return head; + + ListNode middle = GetMiddle(head); + ListNode left = head; + ListNode right = middle.next; + middle.next = null; + + return Merge(SortList(left), SortList(right)); + } + private ListNode GetMiddle(ListNode head) + { + ListNode slow = head; + ListNode fast = head.next; + while (fast != null && fast.next != null) + { + slow = slow.next; + fast = fast.next.next; + } + return slow; + } + private ListNode Merge(ListNode left, ListNode right) + { + var head = new ListNode(); + var tail = head; + while (left is not null && right is not null) + { + if (left.val < right.val) + { + tail.next = left; + left = left.next; + } + else + { + tail.next = right; + right = right.next; + } + tail = tail.next; + } + + tail.next = left is not null ? left : right!; + + return head.next; + } +} \ No newline at end of file diff --git a/csharp/0150-evaluate-reverse-polish-notation.cs b/csharp/0150-evaluate-reverse-polish-notation.cs new file mode 100644 index 000000000..dbf80b5bc --- /dev/null +++ b/csharp/0150-evaluate-reverse-polish-notation.cs @@ -0,0 +1,27 @@ +public class Solution { + private static int evaluate(int b, int a, string op) => op switch{ + "+" => a + b, + "-" => a - b, + "*" => a * b, + "/" => a / b, + _ => throw new Exception() + }; + public int EvalRPN(string[] tokens) { + var stack = new Stack(); + var result = 0; + + foreach(var token in tokens) { + int number = 0; + var isNumber = int.TryParse(token, out number); + if(isNumber) + stack.Push(number); + else { + result = evaluate(stack.Pop(), stack.Pop(), token); + stack.Push(result); + } + + } + + return stack.Pop(); + } +} \ No newline at end of file diff --git a/csharp/0152-maximum-product-subarray.cs b/csharp/0152-maximum-product-subarray.cs new file mode 100644 index 000000000..42d3a2887 --- /dev/null +++ b/csharp/0152-maximum-product-subarray.cs @@ -0,0 +1,17 @@ +public class Solution { + public int MaxProduct(int[] nums) { + var maxProduct = nums.Max(); + (int curMin, int curMax) = (1, 1); + + foreach(var n in nums) { + var tmp = curMax * n; + + curMax = new int[] {n, n * curMin, n * curMax}.Max(); + curMin = new int[] {n, n * curMin, tmp}.Min(); + + maxProduct = Math.Max(maxProduct, curMax); + } + + return maxProduct; + } +} \ No newline at end of file diff --git a/csharp/0153-find-minimum-in-rotated-sorted-array.cs b/csharp/0153-find-minimum-in-rotated-sorted-array.cs new file mode 100644 index 000000000..3afc55e86 --- /dev/null +++ b/csharp/0153-find-minimum-in-rotated-sorted-array.cs @@ -0,0 +1,18 @@ +public class Solution { + public int FindMin(int[] nums) { + int left = 0, right = nums.Length - 1; + while (left <= right) { + if (nums[left] <= nums[right]) { + return nums[left]; + } + int mid = (left + right) / 2; + if (nums[mid] >= nums[left]) { + left = mid + 1; + } + else { + right = mid; + } + } + return 0; + } +} \ No newline at end of file diff --git a/csharp/0155-min-stack.cs b/csharp/0155-min-stack.cs new file mode 100644 index 000000000..00f60291f --- /dev/null +++ b/csharp/0155-min-stack.cs @@ -0,0 +1,28 @@ +public class MinStack { + private Stack stack; + private Stack minStack; + + public MinStack() { + stack = new Stack(); + minStack = new Stack(); + } + + public void Push(int val) { + stack.Push(val); + int min = Math.Min(val, minStack.Count != 0 ? minStack.Peek() : val); + minStack.Push(min); + } + + public void Pop() { + stack.Pop(); + minStack.Pop(); + } + + public int Top() { + return stack.Peek(); + } + + public int GetMin() { + return minStack.Peek(); + } +} \ No newline at end of file diff --git a/csharp/0167-two-sum-ii-input-array-is-sorted.cs b/csharp/0167-two-sum-ii-input-array-is-sorted.cs new file mode 100644 index 000000000..1d6fe4f2d --- /dev/null +++ b/csharp/0167-two-sum-ii-input-array-is-sorted.cs @@ -0,0 +1,20 @@ +public class Solution { + public int[] TwoSum(int[] numbers, int target) { + // Using 2 pointers. Since sorted, if l+r > target, decrease r. + // Else if l+r < target, increase l. Else, result is found. + int left = 0, right = numbers.Length - 1; + + while (left < right) { + int sum = numbers[left] + numbers[right]; + if (sum > target) { + right--; + } else if (sum < target) { + left++; + } else { + return new int[] {left + 1, right + 1}; + } + } + + return new int[0]; + } +} \ No newline at end of file diff --git a/csharp/0168-excel-sheet-column-title.cs b/csharp/0168-excel-sheet-column-title.cs new file mode 100644 index 000000000..03f0c1c19 --- /dev/null +++ b/csharp/0168-excel-sheet-column-title.cs @@ -0,0 +1,22 @@ +public class Solution { + + private const int _ASCII_BASE = 64; + + public string ConvertToTitle(int columnNumber) + { + StringBuilder sb = new StringBuilder(); + + while (columnNumber > 0) + { + int rest = columnNumber % 26; + rest = rest == 0 ? 26 : rest; + + sb.Insert(0, (char)(rest + _ASCII_BASE)); + + columnNumber -= rest; + columnNumber /= 26; + } + + return sb.ToString(); + } +} \ No newline at end of file diff --git a/csharp/0169-majority-element.cs b/csharp/0169-majority-element.cs new file mode 100644 index 000000000..7e47e7623 --- /dev/null +++ b/csharp/0169-majority-element.cs @@ -0,0 +1,14 @@ +public class Solution { + public int MajorityElement(int[] nums) { + int res = 0; + int count = 0; + + for(var i = 0; i < nums.Length; i ++){ + if (count == 0){ + res = nums[i]; + } + count += (nums[i] == res) ? 1 : -1; + } + return res; + } +} \ No newline at end of file diff --git a/csharp/0171-excel-sheet-column-number.cs b/csharp/0171-excel-sheet-column-number.cs new file mode 100644 index 000000000..3e6b6b8ce --- /dev/null +++ b/csharp/0171-excel-sheet-column-number.cs @@ -0,0 +1,16 @@ +public class Solution { + public int TitleToNumber(string columnTitle) { + int result = 0; + char[] chars = columnTitle.ToCharArray().Reverse().ToArray(); + + for (int index = 0; index < chars.Length; index++) { + char currentChar = chars[index]; + int delta = currentChar - 'A' + 1; + int sum = delta * (int)Math.Pow(26, index); + + result += sum; + } + + return result; + } +} \ No newline at end of file diff --git a/csharp/0179-largest-number.cs b/csharp/0179-largest-number.cs new file mode 100644 index 000000000..031e71538 --- /dev/null +++ b/csharp/0179-largest-number.cs @@ -0,0 +1,12 @@ +public class Solution { + public string LargestNumber(int[] nums) + { + if(nums.All(_ => _ == 0)) return "0"; + + var s = nums.Select(_ => _.ToString()).ToList(); + + s.Sort((a, b) => (b+a).CompareTo(a+b)); + + return string.Concat(s); + } +} \ No newline at end of file diff --git a/csharp/0187-repeated-dna-sequences.cs b/csharp/0187-repeated-dna-sequences.cs new file mode 100644 index 000000000..79dad0251 --- /dev/null +++ b/csharp/0187-repeated-dna-sequences.cs @@ -0,0 +1,18 @@ +public class Solution +{ + public IList FindRepeatedDnaSequences(string s) + { + var seen = new HashSet(); + var result = new HashSet(); + + for (var i = 0; i < s.Length - 9; i++) + { + var cur = s.Substring(i, 10); + if (seen.Contains(cur)) + result.Add(cur); + seen.Add(cur); + } + + return result.ToList(); + } +} \ No newline at end of file diff --git a/csharp/0190-reverse-bits.cs b/csharp/0190-reverse-bits.cs new file mode 100644 index 000000000..973dd2d11 --- /dev/null +++ b/csharp/0190-reverse-bits.cs @@ -0,0 +1,14 @@ +public class Solution +{ + public uint reverseBits(uint n) + { + uint result = 0; + for (int i = 0; i < 32; i++) + { + uint temp = (n & 1); + result = (result << 1) + temp; + n >>= 1; + } + return result; + } +} diff --git a/csharp/0191-number-of-1-bits.cs b/csharp/0191-number-of-1-bits.cs new file mode 100644 index 000000000..5a613da87 --- /dev/null +++ b/csharp/0191-number-of-1-bits.cs @@ -0,0 +1,11 @@ +public class Solution { + public int HammingWeight(uint n) { + var binary = Convert.ToString(n, 2); + var hammingWeight = 0; + for (int i = 0; i < binary.Length; i++) + { + hammingWeight += binary[i] - '0'; + } + return hammingWeight; + } +} \ No newline at end of file diff --git a/csharp/0198-house-robber.cs b/csharp/0198-house-robber.cs new file mode 100644 index 000000000..d9595ac9b --- /dev/null +++ b/csharp/0198-house-robber.cs @@ -0,0 +1,13 @@ +public class Solution { + public int Rob(int[] nums) { + int rob1 = 0, rob2 = 0; + + foreach(var num in nums) { + var temp = Math.Max(num + rob1, rob2); + rob1 = rob2; + rob2 = temp; + } + + return rob2; + } +} \ No newline at end of file diff --git a/csharp/0199-binary-tree-right-side-view.cs b/csharp/0199-binary-tree-right-side-view.cs new file mode 100644 index 000000000..1b2a60b0e --- /dev/null +++ b/csharp/0199-binary-tree-right-side-view.cs @@ -0,0 +1,30 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private List _result = new(); + + public IList RightSideView(TreeNode root) { + Dfs(root, 0); + return _result; + } + + private void Dfs(TreeNode root, int level) { + if (root == null) return; + if (level >= _result.Count) _result.Add(root.val); + + // At first visit right node + Dfs(root.right, level + 1); + Dfs(root.left, level + 1); + } +} diff --git a/csharp/0200-number-of-islands.cs b/csharp/0200-number-of-islands.cs new file mode 100644 index 000000000..3dd59106c --- /dev/null +++ b/csharp/0200-number-of-islands.cs @@ -0,0 +1,34 @@ +public class Solution { + public int NumIslands(char[][] grid) { + var m = grid.Length; + var n = grid[0].Length; + var visited = new bool[m, n]; + var numIslands = 0; + + + void dfs(int i, int j, char[][] grid) { + if(i < 0 || i >=m) return; + if(j < 0 || j >=n) return; + if(visited[i, j] == true || grid[i][j]== '0') return; + + visited[i, j] = true; + + dfs(i + 1, j, grid); + dfs(i - 1, j, grid); + dfs(i, j - 1, grid); + dfs(i, j + 1, grid); + } + + for(int i = 0; i < m; i++) { + for(int j = 0; j < n; j++) { + if(visited[i,j] == false + && grid[i][j] == '1') { + numIslands++; + dfs(i, j, grid); + } + } + } + return numIslands; + + } +} \ No newline at end of file diff --git a/csharp/0202-happy-number.cs b/csharp/0202-happy-number.cs new file mode 100644 index 000000000..21192da8c --- /dev/null +++ b/csharp/0202-happy-number.cs @@ -0,0 +1,27 @@ +public class Solution { + public bool IsHappy(int n) { + + var set = new HashSet(); + + while (!set.Contains(n)) + { + set.Add(n); + n = SumOfSquare(n); + if (n == 1) return true; + } + + return false; + } + + int SumOfSquare (int x) + { + int sum = 0; + + for (int i = x; i > 0; i /= 10){ + int y = i % 10; + sum += y * y; + } + + return sum; + } +} \ No newline at end of file diff --git a/csharp/0203-remove-linked-list-elements.cs b/csharp/0203-remove-linked-list-elements.cs new file mode 100644 index 000000000..a07d5b6f1 --- /dev/null +++ b/csharp/0203-remove-linked-list-elements.cs @@ -0,0 +1,21 @@ +public class Solution +{ + public ListNode RemoveElements(ListNode head, int val) + { + ListNode dummy = new ListNode(0, head); + ListNode current = dummy; + + while (current.next is not null) + { + if (current.next.val == val) + { + current.next = current.next.next; + } + else + { + current = current.next; + } + } + return dummy.next; + } +} \ No newline at end of file diff --git a/csharp/0205-isomorphic-strings.cs b/csharp/0205-isomorphic-strings.cs new file mode 100644 index 000000000..04097c6a8 --- /dev/null +++ b/csharp/0205-isomorphic-strings.cs @@ -0,0 +1,29 @@ +public class Solution { + public bool IsIsomorphic(string s, string t) { + Dictionary mapST = new Dictionary(); + Dictionary mapTS = new Dictionary(); + + for (int i = 0; i < s.Length; i++) { + string sChar = s[i].ToString(); + string tChar = t[i].ToString(); + + if (mapST.ContainsKey(sChar)) { + if (mapST[sChar] != tChar) { + return false; + } + } else { + mapST.Add(sChar, tChar); + } + + if (mapTS.ContainsKey(tChar)) { + if (mapTS[tChar] != sChar) { + return false; + } + } else { + mapTS.Add(tChar, sChar); + } + } + + return true; + } +} \ No newline at end of file diff --git a/csharp/0206-reverse-linked-list.cs b/csharp/0206-reverse-linked-list.cs new file mode 100644 index 000000000..614105b24 --- /dev/null +++ b/csharp/0206-reverse-linked-list.cs @@ -0,0 +1,27 @@ +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ +public class Solution +{ + public ListNode ReverseList(ListNode head) + { + ListNode prev = null, curr = head; + + while (curr != null) + { + var temp = curr.next; + curr.next = prev; + prev = curr; + curr = temp; + } + return prev; + } +} diff --git a/csharp/0207-course-schedule.cs b/csharp/0207-course-schedule.cs new file mode 100644 index 000000000..4422f9e07 --- /dev/null +++ b/csharp/0207-course-schedule.cs @@ -0,0 +1,50 @@ +public class Solution { + public bool CanFinish(int numCourses, int[][] prerequisites) + { + + IDictionary> preMap = new Dictionary>(); + HashSet visited = new HashSet(); + for (int i = 0; i < numCourses; i++) + { + preMap.Add(i, new List()); + } + + foreach (int[] course in prerequisites) + { + int courseToTake = course[0]; + int courseDependOn = course[1]; + preMap[courseToTake].Add(courseDependOn); + } + + foreach (int c in Enumerable.Range(0, numCourses)) + { + if (!DfsGraph(preMap, visited, c)) + { + return false; + } + } + return true; + } + public bool DfsGraph(IDictionary> preMap, HashSet visited, int crs) + { + if (visited.Contains(crs)) + { + return false; + } + if (preMap[crs] == new List()) + { + return true; + } + visited.Add(crs); + foreach (var pre in preMap[crs]) + { + if (!DfsGraph(preMap, visited, pre)) + { + return false; + } + } + visited.Remove(crs); + preMap[crs] = new List(); + return true; + } +} diff --git a/csharp/0208-implement-trie-prefix-tree.cs b/csharp/0208-implement-trie-prefix-tree.cs new file mode 100644 index 000000000..490adc5fb --- /dev/null +++ b/csharp/0208-implement-trie-prefix-tree.cs @@ -0,0 +1,57 @@ +public class TrieNode { + public TrieNode() { + childrenMap = new Dictionary(); + } + public Dictionary childrenMap { get; set; } + public bool isWord{ get; set; } +} +public class Trie { + + private TrieNode root; + public Trie() { + root = new TrieNode(); + } + + public void Insert(string word) { + var cur = root; + foreach(var c in word) { + if(!cur.childrenMap.ContainsKey(c)) { + cur.childrenMap[c] = new TrieNode(); + } + cur = cur.childrenMap[c]; + } + cur.isWord = true; + } + + public bool Search(string word) { + var node = traverse(word); + return node != null && node.isWord; + } + + public bool StartsWith(string prefix) { + var node = traverse(prefix); + return node!= null; + } + + private TrieNode traverse(string path) { + var cur = root; + + foreach(var c in path) { + if(cur.childrenMap.ContainsKey(c)){ + cur = cur.childrenMap[c]; + } + else { + return null; + } + } + return cur; + } +} + +/** + * Your Trie object will be instantiated and called as such: + * Trie obj = new Trie(); + * obj.Insert(word); + * bool param_2 = obj.Search(word); + * bool param_3 = obj.StartsWith(prefix); + */ \ No newline at end of file diff --git a/csharp/0210-course-schedule-ii.cs b/csharp/0210-course-schedule-ii.cs new file mode 100644 index 000000000..733ff94eb --- /dev/null +++ b/csharp/0210-course-schedule-ii.cs @@ -0,0 +1,53 @@ +public class Solution { + List output = null; + public int[] FindOrder(int numCourses, int[][] prerequisites) + { + IDictionary> preMap = new Dictionary>(); + HashSet visited = new HashSet(); + HashSet cycle = new HashSet(); + output = new List(); + for (int i = 0; i < numCourses; i++) + { + preMap.Add(i, new List()); + } + + foreach (int[] course in prerequisites) + { + int courseToTake = course[0]; + int courseDependOn = course[1]; + preMap[courseToTake].Add(courseDependOn); + } + + foreach (int c in Enumerable.Range(0, numCourses)) + { + if (DfsGraphTopologicalSort(preMap, visited, cycle, c) == false) + { + return Array.Empty(); + } + } + return output.ToArray(); + } + + public bool DfsGraphTopologicalSort(IDictionary> preMap, HashSet visited, HashSet cycle, int crs) + { + if (cycle.Contains(crs)) return false; + if (visited.Contains(crs)) return true; + + if (preMap[crs] == new List()) + { + return true; + } + cycle.Add(crs); + foreach (var pre in preMap[crs]) + { + if (DfsGraphTopologicalSort(preMap, visited,cycle, pre)==false) + { + return false; + } + } + cycle.Remove(crs); + visited.Add(crs); + output.Add(crs); + return true; + } +} diff --git a/csharp/0211-design-add-and-search-words-data-structure.cs b/csharp/0211-design-add-and-search-words-data-structure.cs new file mode 100644 index 000000000..7bb388b30 --- /dev/null +++ b/csharp/0211-design-add-and-search-words-data-structure.cs @@ -0,0 +1,80 @@ +public class TrieNode +{ + public Dictionary Children; + public bool EndOfWord; + + public TrieNode() + { + Children = new Dictionary(); + EndOfWord = false; + } +} + +public class WordDictionary +{ + + public TrieNode root; + + public WordDictionary() + { + root = new TrieNode(); + } + + public void AddWord(string word) + { + var current = root; + for (var i = 0; i < word.Length; i++) + { + if (!current.Children.ContainsKey(word[i])) + { + var newNode = new TrieNode(); + current.Children.Add(word[i], newNode); + } + current = current.Children[word[i]]; + } + current.EndOfWord = true; + } + + public bool Search(string word) + { + return dfs(0, root, word); + } + + private bool dfs(int index, TrieNode root, string word) + { + var currentNode = root; + + for (var i = index; i < word.Length; i++) + { + var letter = word[i]; + if (letter == '.') + { + foreach (var (key, value) in currentNode.Children) + { + if (dfs(i + 1, value, word)) + { + return true; + } + } + + return false; + } + else + { + if (!currentNode.Children.ContainsKey(letter)) + { + return false; + } + currentNode = currentNode.Children[letter]; + } + } + return currentNode.EndOfWord; + } +} + +/** + * Your WordDictionary object will be instantiated and called as such: + * WordDictionary obj = new WordDictionary(); + * obj.AddWord(word); + * bool param_2 = obj.Search(word); + */ \ No newline at end of file diff --git a/csharp/0212-word-search-ii.cs b/csharp/0212-word-search-ii.cs new file mode 100644 index 000000000..53831960a --- /dev/null +++ b/csharp/0212-word-search-ii.cs @@ -0,0 +1,85 @@ +public class Solution +{ + //T: O(m*n*4^Length of the word) S: O(m+n+ L) + //S: O(m+n) is for the visited array + public IList FindWords(char[][] board, string[] words) + { + foreach (var word in words) + AddWord(word); + + var rows = board.Length; + var cols = board[0].Length; + + var result = new HashSet(); + var visited = new HashSet<(int, int)>(); + + void dfs(int row, int col, TrieNode node, string word) + { + + if (row < 0 || col < 0 || row >= rows || col >= cols || + visited.Contains((row, col)) || + !node.children.ContainsKey(board[row][col])) + return; + + visited.Add((row, col)); + node = node.children[board[row][col]]; + word += board[row][col]; + + if (node.EndOfWord) + result.Add(word); + + dfs(row - 1, col, node, word); + dfs(row + 1, col, node, word); + dfs(row, col - 1, node, word); + dfs(row, col + 1, node, word); + + visited.Remove((row, col)); + } + + for (var row = 0; row < rows; row++) + { + for (var col = 0; col < cols; col++) + { + + dfs(row, col, root, ""); + } + } + + return result.ToList(); + } + + public TrieNode root; + public Solution() + { + root = new TrieNode(); + } + + public void AddWord(string word) + { + var current = root; + for (var i = 0; i < word.Length; i++) + { + if (!current.children.ContainsKey(word[i])) + { + var child = new TrieNode(); + current.children.Add(word[i], child); + } + current = current.children[word[i]]; + } + current.EndOfWord = true; + + } + +} + +public class TrieNode +{ + public Dictionary children; + public bool EndOfWord; + public TrieNode() + { + children = new Dictionary(); + EndOfWord = false; + } + +} \ No newline at end of file diff --git a/csharp/0213-house-robber-ii.cs b/csharp/0213-house-robber-ii.cs new file mode 100644 index 000000000..a0f2a71b6 --- /dev/null +++ b/csharp/0213-house-robber-ii.cs @@ -0,0 +1,21 @@ +public class Solution { + public int Rob(int[] nums) { + + return Math.Max(nums[0],Math.Max( + getMaxRobAmount(nums, 0, nums.Length - 1), + getMaxRobAmount(nums, 1, nums.Length)) + ); + } + + public int getMaxRobAmount(int[] nums, int start, int end) { + int rob1 = 0, rob2 = 0; + + for(int i = start; i < end; i++) { + int temp = Math.Max(nums[i] + rob1, rob2); + rob1 = rob2; + rob2 = temp; + } + + return rob2; + } +} diff --git a/csharp/0215-kth-largest-element-in-an-array.cs b/csharp/0215-kth-largest-element-in-an-array.cs new file mode 100644 index 000000000..57a599a19 --- /dev/null +++ b/csharp/0215-kth-largest-element-in-an-array.cs @@ -0,0 +1,17 @@ +public class Solution { + public int FindKthLargest(int[] nums, int k) { + PriorityQueue queue = new PriorityQueue(); + + for(var i = 0; i < nums.Length; i++) { + if (queue.Count < k) + queue.Enqueue(nums[i], nums[i]); + else { + if (nums[i] <= queue.Peek()) continue; + + queue.Dequeue(); + queue.Enqueue(nums[i], nums[i]); + } + } + return queue.Dequeue(); + } +} diff --git a/csharp/0217-contains-duplicate.cs b/csharp/0217-contains-duplicate.cs new file mode 100644 index 000000000..4dc27e279 --- /dev/null +++ b/csharp/0217-contains-duplicate.cs @@ -0,0 +1,11 @@ +public class Solution { + public bool ContainsDuplicate(int[] nums) { + HashSet set = new HashSet(); + + foreach (int x in nums){ + if (set.Contains(x)) return true; + set.Add(x); + } + return false; + } +} \ No newline at end of file diff --git a/csharp/0219-contains-duplicate-ii.cs b/csharp/0219-contains-duplicate-ii.cs new file mode 100644 index 000000000..4eb87aba4 --- /dev/null +++ b/csharp/0219-contains-duplicate-ii.cs @@ -0,0 +1,20 @@ +public class Solution +{ + public bool ContainsNearbyDuplicate(int[] nums, int k) + { + // key is value + // value is index + var dct = new Dictionary(); + + for (int i = 0; i < nums.Length; i++) + { + if (dct.TryGetValue(nums[i], out int last_index)) + { + if (i - last_index <= k) return true; + } + dct[nums[i]] = i; + } + + return false; + } +} \ No newline at end of file diff --git a/csharp/0225-implement-stack-using-queues.cs b/csharp/0225-implement-stack-using-queues.cs new file mode 100644 index 000000000..474843d5d --- /dev/null +++ b/csharp/0225-implement-stack-using-queues.cs @@ -0,0 +1,33 @@ +public class MyStack +{ + Queue queue; + + public MyStack() + { + queue = new Queue(); + } + + public void Push(int x) + { + queue.Enqueue(x); + for (int i = 0; i < queue.Count - 1; i++) + { + queue.Enqueue(queue.Dequeue()); + } + } + + public int Pop() + { + return queue.Dequeue(); + } + + public int Top() + { + return queue.Peek(); + } + + public bool Empty() + { + return queue.Count == 0; + } +} \ No newline at end of file diff --git a/csharp/0226-invert-binary-tree.cs b/csharp/0226-invert-binary-tree.cs new file mode 100644 index 000000000..ec517ad24 --- /dev/null +++ b/csharp/0226-invert-binary-tree.cs @@ -0,0 +1,29 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution +{ + public TreeNode InvertTree(TreeNode root) + { + if (root == null) return null; + + var tmp = root.left; + root.left = root.right; + root.right = tmp; + + InvertTree(root.left); + InvertTree(root.right); + + return root; + } +} \ No newline at end of file diff --git a/csharp/0230-kth-smallest-element-in-a-bst.cs b/csharp/0230-kth-smallest-element-in-a-bst.cs new file mode 100644 index 000000000..623e57f4b --- /dev/null +++ b/csharp/0230-kth-smallest-element-in-a-bst.cs @@ -0,0 +1,37 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public int KthSmallest(TreeNode root, int k) { + var result = -1; + var inorderStack = new Stack(); + + var cur = root; + + while(cur != null || inorderStack.Count > 0) { + while(cur != null) { + inorderStack.Push(cur); + cur = cur.left; + } + cur = inorderStack.Pop(); + + k--; + if(k == 0) { + result = cur.val; + break; + } + cur = cur.right; + } + return result; + } +} \ No newline at end of file diff --git a/csharp/0232-implement-queue-using-stacks.cs b/csharp/0232-implement-queue-using-stacks.cs new file mode 100644 index 000000000..aba38c73a --- /dev/null +++ b/csharp/0232-implement-queue-using-stacks.cs @@ -0,0 +1,47 @@ +public class MyQueue +{ + Stack stack1; + Stack stack2; + + public MyQueue() + { + stack1 = new Stack(); + stack2 = new Stack(); + } + + public void Push(int x) + { + stack1.Push(x); + } + + public int Pop() + { + if (stack2.Count == 0) + { + while (stack1.Count > 0) + { + stack2.Push(stack1.Pop()); + } + } + + return stack2.Pop(); + } + + public int Peek() + { + if (stack2.Count == 0) + { + while (stack1.Count > 0) + { + stack2.Push(stack1.Pop()); + } + } + + return stack2.Peek(); + } + + public bool Empty() + { + return stack1.Count == 0 && stack2.Count == 0; + } +} \ No newline at end of file diff --git a/csharp/0235-lowest-common-ancestor-of-a-binary-search-tree.cs b/csharp/0235-lowest-common-ancestor-of-a-binary-search-tree.cs new file mode 100644 index 000000000..d2451993e --- /dev/null +++ b/csharp/0235-lowest-common-ancestor-of-a-binary-search-tree.cs @@ -0,0 +1,25 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int x) { val = x; } + * } + */ + +public class Solution { + public TreeNode LowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + // Traverse Right child + if (p.val > root.val && q.val > root.val) { + return LowestCommonAncestor(root.right, p, q); + } + + // Traverse Left Child + if (p.val < root.val && q.val < root.val) { + return LowestCommonAncestor(root.left, p, q); + } + + return root; + } +} \ No newline at end of file diff --git a/csharp/0238-product-of-array-except-self.cs b/csharp/0238-product-of-array-except-self.cs new file mode 100644 index 000000000..2a57fdd1b --- /dev/null +++ b/csharp/0238-product-of-array-except-self.cs @@ -0,0 +1,17 @@ +public class Solution { + public int[] ProductExceptSelf(int[] nums) { + int prefix = 1, postfix = 1; + int[] res = new int[nums.Length]; + + for(int i = 0; i < nums.Length; i++){ + res[i] = prefix; + prefix *= nums[i]; + } + + for(int i = nums.Length-1; i>=0; i--){ + res[i] *= postfix; + postfix *= nums[i]; + } + return res; + } +} diff --git a/csharp/0239-sliding-window-maximum.cs b/csharp/0239-sliding-window-maximum.cs new file mode 100644 index 000000000..5b9dd98f1 --- /dev/null +++ b/csharp/0239-sliding-window-maximum.cs @@ -0,0 +1,32 @@ +public class Solution +{ + public int[] MaxSlidingWindow(int[] nums, int k) + { + + var output = new List(); + LinkedList queue = new(); + int left = 0, right = 0; + + while (right < nums.Length) + { + // pop smaller values from queue + while (queue.Count > 0 && nums[queue.Last.Value] < nums[right]) + queue.RemoveLast(); + queue.AddLast(right); + + // remove left val from the window + if (left > queue.First.Value) + queue.RemoveFirst(); + + if (right + 1 >= k) + { + output.Add(nums[queue.First.Value]); + left++; + } + + right++; + } + + return output.ToArray(); + } +} \ No newline at end of file diff --git a/csharp/0242-valid-anagram.cs b/csharp/0242-valid-anagram.cs new file mode 100644 index 000000000..521b6018e --- /dev/null +++ b/csharp/0242-valid-anagram.cs @@ -0,0 +1,20 @@ +public class Solution { + public bool IsAnagram(string s, string t) { + if (s.Length != t.Length) return false; + if (s == t) return true; + + Dictionary sCounts = new Dictionary(); + Dictionary tCounts = new Dictionary(); + + for (int i = 0; i < s.Length; i++) { + sCounts[s[i]] = 1 + (sCounts.ContainsKey(s[i]) ? sCounts[s[i]] : 0); + tCounts[t[i]] = 1 + (tCounts.ContainsKey(t[i]) ? tCounts[t[i]] : 0); + } + + foreach (char c in sCounts.Keys) { + int tCount = tCounts.ContainsKey(c) ? tCounts[c] : 0; + if (sCounts[c] != tCount) return false; + } + return true; + } +} \ No newline at end of file diff --git a/csharp/0252-meeting-rooms.cs b/csharp/0252-meeting-rooms.cs new file mode 100644 index 000000000..3ac8aead2 --- /dev/null +++ b/csharp/0252-meeting-rooms.cs @@ -0,0 +1,14 @@ +public class Solution { + public bool CanAttendMeetings(int[][] intervals) { + + Array.Sort(intervals, (a, b) => a[0] < b[0] ? -1 : 1); + for (int i = 1; i < intervals.Length; i++) + { + if (intervals[i][0] < intervals[i - 1][1]) + { + return false; + } + } + return true; + } +} diff --git a/csharp/0253-meeting-rooms-ii.cs b/csharp/0253-meeting-rooms-ii.cs new file mode 100644 index 000000000..6f4ceb9e5 --- /dev/null +++ b/csharp/0253-meeting-rooms-ii.cs @@ -0,0 +1,26 @@ +public class Solution { + public int MinMeetingRooms(int[][] intervals) { + if (intervals == null || intervals.Length == 0) + return 0; + + int result = 0; + int[] start = new int[intervals.Length], end = new int[intervals.Length]; + + for (int i = 0; i < intervals.Length; i++) + { + start[i] = intervals[i][0]; + end[i] = intervals[i][1]; + } + + Array.Sort(start); + Array.Sort(end); + + for (int s = 0, e = 0; s < start.Length; s++) + if (start[s] < end[e]) + result++; + else + e++; + + return result; + } +} diff --git a/csharp/0256-paint-house.cs b/csharp/0256-paint-house.cs new file mode 100644 index 000000000..2660b01fa --- /dev/null +++ b/csharp/0256-paint-house.cs @@ -0,0 +1,17 @@ +class Solution +{ + public int MinCost(int[][] costs) + { + int[] previousRow = new int[3]; + for (int i = 0; i < costs.Length; i++) + { + int currentRowHouse0 = costs[i][0] + Math.Min(previousRow[1], previousRow[2]); + int currentRowHouse1 = costs[i][1] + Math.Min(previousRow[0], previousRow[2]); + int currentRowHouse2 = costs[i][2] + Math.Min(previousRow[0], previousRow[1]); + previousRow[0] = currentRowHouse0; + previousRow[1] = currentRowHouse1; + previousRow[2] = currentRowHouse2; + } + return Math.Min(Math.Min(previousRow[0], previousRow[1]), previousRow[2]); + } +} \ No newline at end of file diff --git a/csharp/0261-graph-valid-tree.cs b/csharp/0261-graph-valid-tree.cs new file mode 100644 index 000000000..aa8ba9b41 --- /dev/null +++ b/csharp/0261-graph-valid-tree.cs @@ -0,0 +1,41 @@ +public class Solution { + public bool ValidTree(int n, int[][] edges) + { + if (n == 0) return true; + + var adj = new HashSet[n]; + + for (int i = 0; i < n; i++) + { + adj[i] = new HashSet(); + } + foreach (var edge in edges) + { + var e1 = edge[0]; + var e2 = edge[1]; + adj[e1].Add(e2); adj[e2].Add(e1); + } + var visited = new bool[n]; + + var res = DfsValidTree(adj, 0, visited); + + if (visited.Any(c => !c)) return false; + return res; + } + + private bool DfsValidTree(HashSet[] adj, int current, bool[] visited) + { + if (visited[current]) return false; + visited[current] = true; + + var nextLevel = adj[current]; + foreach (var level in nextLevel) + { + adj[level].Remove(current); + if (!DfsValidTree(adj, level, visited)) + { + return false; + } + } + return true; + }} diff --git a/csharp/0268-missing-number.cs b/csharp/0268-missing-number.cs new file mode 100644 index 000000000..5b6dbda7f --- /dev/null +++ b/csharp/0268-missing-number.cs @@ -0,0 +1,10 @@ +public class Solution { + public int MissingNumber(int[] nums) { + int sum = 0; + int total = nums.Length * (nums.Length + 1) / 2; + for (int i = 0; i < nums.Length; i++) { + sum += nums[i]; + } + return total - sum; + } +} \ No newline at end of file diff --git a/csharp/0269-alien-dictionary.cs b/csharp/0269-alien-dictionary.cs new file mode 100644 index 000000000..d41f7e44d --- /dev/null +++ b/csharp/0269-alien-dictionary.cs @@ -0,0 +1,78 @@ +public class Solution +{ + public string AlienOrder(string[] words) + { + + var adj = new Dictionary>(); + + //Add all the available characters to the adjacency list resolves all the edges + foreach (var word in words) + { + foreach (var c in word) + { + if (adj.ContainsKey(c)) continue; + adj[c] = new HashSet(); + //alphabet.Add(c); + } + } + + for (var i = 0; i < words.Length - 1; i++) + { + var w1 = words[i]; + var w2 = words[i + 1]; + + var minLen = Math.Min(w1.Length, w2.Length); + if (w1.Length > w2.Length && w1.Substring(0, minLen) == w2.Substring(0, minLen)) + { + return ""; + } + + for (var j = 0; j < minLen; j++) + { + if (w1[j] != w2[j]) + { + //adj.TryAdd(w1[j], new HashSet()); + adj[w1[j]].Add(w2[j]); + break; + } + } + + } + + var visited = new Dictionary(); //false = visited, true = in the current path + var res = new List(); + + bool dfs(char c) + { + if (visited.ContainsKey(c)) + return visited[c]; //true: there is a cycle - we are visiting this twice + + + visited.TryAdd(c, false); + visited[c] = true; + + foreach (var neigh in adj[c]) + { + + if (dfs(neigh)) + return true; + } + + + visited[c] = false; + res.Add(c); + + return visited[c]; + } + + foreach (var c in adj.Keys) + { + if (dfs(c)) + return ""; + } + + res.Reverse(); + return string.Join("", res); + + } +} \ No newline at end of file diff --git a/csharp/0271-encode-and-decode-strings.cs b/csharp/0271-encode-and-decode-strings.cs new file mode 100644 index 000000000..ba4a065d2 --- /dev/null +++ b/csharp/0271-encode-and-decode-strings.cs @@ -0,0 +1,25 @@ +public class Codec { + + public string encode(IList strs) { + return string.Concat(strs.SelectMany(s=> $"{s.Length}#{s}")); + } + + public IList decode(string s) { + var res = new List(); + + var i = 0; + while (i < s.Length) { + var j = i; + while (s[j] != '#') { + ++j; + } + + int.TryParse(s.Substring(i, j-i), out var len); + j++; + res.Add(s.Substring(j, len)); + i = j + len; + } + + return res; + } +} \ No newline at end of file diff --git a/csharp/0283-move-zeroes.cs b/csharp/0283-move-zeroes.cs new file mode 100644 index 000000000..64f2b18ef --- /dev/null +++ b/csharp/0283-move-zeroes.cs @@ -0,0 +1,40 @@ +public class Solution +{ + public void MoveZeroes(int[] nums) + { + int readIndex = 0; + int writeIndex = 0; + + while (readIndex < nums.Length) + { + if (nums[readIndex] == 0) + { + readIndex++; + continue; + } + if (readIndex != writeIndex) + { + nums[writeIndex] = nums[readIndex]; + nums[readIndex] = 0; + } + writeIndex++; + readIndex++; + } + } +} + +public class NeetCodeWaySolution { //NeetCodeWay + public void MoveZeroes(int[] nums) { + if (nums.Length <= 1) return; + int l = 0, r = 0; + while (r < nums.Length) { + if (nums[r] != 0) { + var t = nums[l]; + nums[l++] = nums[r]; + nums[r++] = t; + } else { + ++r; + } + } + } +} diff --git a/csharp/0286-walls-and-gates.cs b/csharp/0286-walls-and-gates.cs new file mode 100644 index 000000000..89ee6ac69 --- /dev/null +++ b/csharp/0286-walls-and-gates.cs @@ -0,0 +1,56 @@ +public class Solution +{ + private Queue<(int, int)> queue = new Queue<(int, int)>(); + private int rows; + private int cols; + + + public void WallsAndGates(int[][] rooms) + { + rows = rooms.Length; + cols = rooms[0].Length; + var visited = new int[rows, cols]; + + + void addRoom(int row, int col) + { + if (row < 0 || col < 0 || row == rows || col == cols || rooms[row][col] == -1 || visited[row, col] == 1) + return; + + visited[row, col] = 1; + queue.Enqueue((row, col)); + } + + for (var i = 0; i < rows; i++) + { + for (var j = 0; j < cols; j++) + { + if (rooms[i][j] == 0) + { + queue.Enqueue((i, j)); + visited[i, j] = 1; + } + } + } + + var currentDistance = 0; + while (queue.Count > 0) + { + var count = queue.Count; + for (var i = 0; i < count; i++) + { + var (row, col) = queue.Dequeue(); + rooms[row][col] = currentDistance; + addRoom(row + 1, col); + addRoom(row - 1, col); + addRoom(row, col + 1); + addRoom(row, col - 1); + + } + + currentDistance++; + } + + } + +} \ No newline at end of file diff --git a/csharp/0287-find-the-duplicate-number.cs b/csharp/0287-find-the-duplicate-number.cs new file mode 100644 index 000000000..7059ef818 --- /dev/null +++ b/csharp/0287-find-the-duplicate-number.cs @@ -0,0 +1,38 @@ +public class Solution { + public int FindDuplicate(int[] nums) { + return floydAlgorithm(nums); + // return arrayAsHashMapIterative(nums); + } + + private int floydAlgorithm(int[] nums) { + int slow = 0, fast = 0; + + while(true) { + slow = nums[slow]; + fast = nums[nums[fast]]; + if (slow == fast) + break; + } + + var slow2 = 0; + + while(true) { + slow = nums[slow]; + slow2 = nums[slow2]; + if (slow == slow2) + return slow; + } + + return 0; + } + + private int arrayAsHashMapIterative(int[] nums) { + while(nums[0] != nums[nums[0]]) { + int nxt = nums[nums[0]]; + nums[nums[0]] = nums[0]; + nums[0] = nxt; + } + + return nums[0]; + } +} \ No newline at end of file diff --git a/csharp/0290-word-pattern.cs b/csharp/0290-word-pattern.cs new file mode 100644 index 000000000..61b1441a9 --- /dev/null +++ b/csharp/0290-word-pattern.cs @@ -0,0 +1,23 @@ +public class Solution { + public bool WordPattern(string pattern, string s) { + var words = s.Split(' '); + if (words.Length != pattern.Length) return false; + + Dictionary charToWord = new Dictionary(); + Dictionary wordToChar = new Dictionary(); + + for (int i = 0; i < pattern.Length; i++) { + char c = pattern[i]; + string w = words[i]; + if (charToWord.ContainsKey(c) && (charToWord[c] != w)) { + return false; + } + if(wordToChar.ContainsKey(w) && (wordToChar[w] != c)) { + return false; + } + charToWord[c] = w; + wordToChar[w] = c; + } + return true; + } +} \ No newline at end of file diff --git a/csharp/0295-find-median-from-data-stream.cs b/csharp/0295-find-median-from-data-stream.cs new file mode 100644 index 000000000..1e11a2796 --- /dev/null +++ b/csharp/0295-find-median-from-data-stream.cs @@ -0,0 +1,46 @@ +public class MedianFinder { + private PriorityQueue leftHeap = new(Comparer.Create((a, b) => b - a)); + private PriorityQueue rightHeap = new(); + + public MedianFinder() { + + } + + // T: log(n) + public void AddNum(int num) { + if (leftHeap.Count == 0 || num > leftHeap.Peek()) + rightHeap.Enqueue(num, num); + else + leftHeap.Enqueue(num, num); + + Balance(); + } + + private void Balance() { + var (big, small) = leftHeap.Count > rightHeap.Count + ? (leftHeap, rightHeap) + : (rightHeap, leftHeap); + + while (big.Count - small.Count > 1) { + var value = big.Dequeue(); + small.Enqueue(value, value); + } + } + + // T: O(1) + public double FindMedian() { + if (leftHeap.Count == rightHeap.Count) + return (leftHeap.Peek() + rightHeap.Peek()) / 2.0; + + return leftHeap.Count > rightHeap.Count + ? leftHeap.Peek() + : rightHeap.Peek(); + } +} + +/** + * Your MedianFinder object will be instantiated and called as such: + * MedianFinder obj = new MedianFinder(); + * obj.AddNum(num); + * double param_2 = obj.FindMedian(); + */ diff --git a/csharp/0297-serialize-and-deserialize-binary-tree.cs b/csharp/0297-serialize-and-deserialize-binary-tree.cs new file mode 100644 index 000000000..2cc97fffa --- /dev/null +++ b/csharp/0297-serialize-and-deserialize-binary-tree.cs @@ -0,0 +1,66 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int x) { val = x; } + * } + */ + +public class Codec +{ + + private List encodedList { get; set; } + + // Encodes a tree to a single string. + public string serialize(TreeNode root) + { + encodedList = new List(); + void dfs(TreeNode root) + { + if (root == null) + { + encodedList.Add("N"); + return; + } + + encodedList.Add(root.val + ""); + dfs(root.left); + dfs(root.right); + } + + dfs(root); + Console.WriteLine(string.Join(",", encodedList)); + return string.Join(",", encodedList); + } + + // Decodes your encoded data to tree. + public TreeNode deserialize(string data) + { + var nodesArray = data.Split(","); + var index = 0; + + TreeNode dfs() + { + if (nodesArray[index] == "N") + { + index++; + return null; + } + + var newNode = new TreeNode(int.Parse(nodesArray[index])); + index++; + newNode.left = dfs(); + newNode.right = dfs(); + return newNode; + } + + return dfs(); + } +} + +// Your Codec object will be instantiated and called as such: +// Codec ser = new Codec(); +// Codec deser = new Codec(); +// TreeNode ans = deser.deserialize(ser.serialize(root)); \ No newline at end of file diff --git a/csharp/0300-longest-increasing-subsequence.cs b/csharp/0300-longest-increasing-subsequence.cs new file mode 100644 index 000000000..d4722f0db --- /dev/null +++ b/csharp/0300-longest-increasing-subsequence.cs @@ -0,0 +1,18 @@ +public class Solution { + public int LengthOfLIS(int[] nums) { + int[] dp = new int[nums.Length]; + Array.Fill(dp, 1); + + for (int i = nums.Length - 1; i >= 0; i--) + { + for (int j = i + 1; j < nums.Length; j++) + { + if (nums[i] < nums[j]) + { + dp[i] = Math.Max(dp[i], 1 + dp[j]); + } + } + } + return dp.Max(); + } +} diff --git a/csharp/0303-range-sum-query-immutable.cs b/csharp/0303-range-sum-query-immutable.cs new file mode 100644 index 000000000..792bd7711 --- /dev/null +++ b/csharp/0303-range-sum-query-immutable.cs @@ -0,0 +1,15 @@ +public class NumArray { + private int[] nums; + public NumArray(int[] nums) { + this.nums = nums; + } + + public int SumRange(int left, int right) { + int sum = 0; + for (int i = left; i <= right; i++) + { + sum += this.nums[i]; + } + return sum; + } +} diff --git a/csharp/0309-best-time-to-buy-and-sell-stock-with-cooldown.cs b/csharp/0309-best-time-to-buy-and-sell-stock-with-cooldown.cs new file mode 100644 index 000000000..02973c1f8 --- /dev/null +++ b/csharp/0309-best-time-to-buy-and-sell-stock-with-cooldown.cs @@ -0,0 +1,13 @@ +public class Solution { + public int MaxProfit(int[] prices) { + int sold = 0, rest = 0, hold = Int32.MinValue; + + for (int i = 0; i < prices.Length; i++) { + int prevSold = sold; + sold = hold + prices[i]; + hold = Math.Max(hold, rest - prices[i]); + rest = Math.Max(rest, prevSold); + } + return Math.Max(sold, rest); + } +} \ No newline at end of file diff --git a/csharp/0312-burst-balloons.cs b/csharp/0312-burst-balloons.cs new file mode 100644 index 000000000..374655f48 --- /dev/null +++ b/csharp/0312-burst-balloons.cs @@ -0,0 +1,36 @@ +public class Solution +{ + // T: O(N^3) | S: O(N^2) + public int MaxCoins(int[] nums) + { + var n = nums.Length + 2; + var memo = new int[n, n]; + //Array.Fill(memo, -1); + var arr = new int[n]; + arr[0] = 1; + arr[^1] = 1; + for (int i = 1; i < n - 1; i++) arr[i] = nums[i - 1]; + + return MaxCoins(arr, memo, 1, n - 2); + } + + private int MaxCoins(int[] arr, int[,] memo, int start, int end) + { + if (start > end) + return 0; + + if (memo[start, end] != 0) + return memo[start, end]; + + memo[start, end] = 0; + for (var i = start; i < end + 1; i++) + { + var coins = arr[start - 1] * arr[i] * arr[end + 1]; + coins += MaxCoins(arr, memo, start, i - 1) + MaxCoins(arr, memo, i + 1, end); + memo[start, end] = Math.Max(memo[start, end], coins); + } + + return memo[start, end]; + } + +} \ No newline at end of file diff --git a/csharp/0315-count-of-smaller-numbers-after-self.cs b/csharp/0315-count-of-smaller-numbers-after-self.cs new file mode 100644 index 000000000..6014729a3 --- /dev/null +++ b/csharp/0315-count-of-smaller-numbers-after-self.cs @@ -0,0 +1,25 @@ +public class Solution { + public IList CountSmaller(int[] nums) { + + var sortedNums = new List(); + var result = new int[nums.Length]; + + for (int i = nums.Length - 1; i >= 0; i--) + { + int left = 0; + int right = sortedNums.Count; + + while (left < right) + { + var mid = left + (right - left) / 2; + if (sortedNums[mid] >= nums[i]) right = mid; + else left = mid + 1; + } + + result[i] = left; + sortedNums.Insert(left, nums[i]); + } + + return result; + } +} diff --git a/csharp/0322-coin-change.cs b/csharp/0322-coin-change.cs new file mode 100644 index 000000000..39a58047e --- /dev/null +++ b/csharp/0322-coin-change.cs @@ -0,0 +1,19 @@ +public class Solution { + public int CoinChange(int[] coins, int amount) { + var dp = Enumerable.Repeat(amount + 1, amount + 1).ToArray(); + + dp[0] = 0; + + for(var a = 1; a <= amount; a++) { + foreach(var c in coins) { + if(a - c >= 0) { + dp[a] = Math.Min(dp[a], 1 + dp[a-c]); + } + } + } + + return dp[amount] == amount + 1 + ? -1 + : dp[amount]; + } +} \ No newline at end of file diff --git a/csharp/0323-number-of-connected-components-in-an-undirected-graph.cs b/csharp/0323-number-of-connected-components-in-an-undirected-graph.cs new file mode 100644 index 000000000..fcf0d8348 --- /dev/null +++ b/csharp/0323-number-of-connected-components-in-an-undirected-graph.cs @@ -0,0 +1,40 @@ +public class Solution { + private int noOfConnectedComponents = 0; + private int[] rank; + + public int CountComponents(int n, int[][] edges) + { + if (n == 0) + return noOfConnectedComponents; + + noOfConnectedComponents = n; + rank = new int[n]; + + for (int i = 0; i < n; i++) + rank[i] = i; + + foreach (int[] edge in edges) + Union(edge[0], edge[1]); + + return noOfConnectedComponents; + } + + private void Union(int x, int y) + { + int p1 = Find(x), p2 = Find(y); + + if (p1 != p2) + { + rank[p1] = p2; + noOfConnectedComponents--; + } + } + + private int Find(int n) + { + if (rank[n] != n) + rank[n] = Find(rank[n]); + + return rank[n]; + } +} diff --git a/csharp/0329-longest-increasing-path-in-a-matrix.cs b/csharp/0329-longest-increasing-path-in-a-matrix.cs new file mode 100644 index 000000000..b04d7d44d --- /dev/null +++ b/csharp/0329-longest-increasing-path-in-a-matrix.cs @@ -0,0 +1,44 @@ +public class Solution +{ + //T: O(N*M) | S: O(N*M) || has complex topological sort order and hence memoization. + public int LongestIncreasingPath(int[][] matrix) + { + + var rows = matrix.Length; + var cols = matrix[0].Length; + var maxValue = int.MinValue; + + var dictionary = new Dictionary<(int, int), int>(); + + int dfs(int i, int j, int previous) + { + if (i >= matrix.Length || j >= matrix[0].Length || i < 0 || j < 0 || matrix[i][j] <= previous) + return 0; + + if (dictionary.ContainsKey((i, j))) + return dictionary[(i, j)]; + + + var value = 1 + Math.Max( + Math.Max( + Math.Max(dfs(i + 1, j, matrix[i][j]), dfs(i - 1, j, matrix[i][j])), + dfs(i, j - 1, matrix[i][j])), + dfs(i, j + 1, matrix[i][j])); + + dictionary.TryAdd((i, j), 0); + dictionary[(i, j)] = value; + + maxValue = Math.Max(maxValue, value); + return value; + } + + for (var i = 0; i < rows; i++) + { + for (var j = 0; j < cols; j++) + { + dfs(i, j, -1); + } + } + return maxValue; + } +} \ No newline at end of file diff --git a/csharp/0332-reconstruct-itinerary.cs b/csharp/0332-reconstruct-itinerary.cs new file mode 100644 index 000000000..d17cb4bb5 --- /dev/null +++ b/csharp/0332-reconstruct-itinerary.cs @@ -0,0 +1,38 @@ +public class Solution { +public IList FindItinerary(IList> tickets) + { + var map = new Dictionary>(); + foreach (var ticket in tickets) + { + if (!map.TryGetValue(ticket[0], out var adj)) + { + adj = new List(); + map.Add(ticket[0], adj); + } + adj.Add(ticket[1]); + } + + foreach (var adj in map.Values) + { + adj.Sort(Comparer.Create((a, b) => string.Compare(b, a))); + } + + var res = new Stack(); + DfsVisit(map, "JFK", res); + return res.ToList(); + } + + private void DfsVisit(Dictionary> map, string src, Stack ans) + { + if (map.TryGetValue(src, out var adj)) + { + while (adj.Count > 0) + { + var next = adj.Last(); + adj.RemoveAt(adj.Count - 1); + DfsVisit(map, next, ans); + } + } + ans.Push(src); + } +} diff --git a/csharp/0338-counting-bits.cs b/csharp/0338-counting-bits.cs new file mode 100644 index 000000000..5d2b40397 --- /dev/null +++ b/csharp/0338-counting-bits.cs @@ -0,0 +1,35 @@ +public class Solution { + public int[] CountBits(int n) { + var hammingWeights = new int[n+1]; + for (int i = 0; i <= n; i++) + { + var binary = Convert.ToString(i, 2); + var hammingWeight = 0; + for (int j = 0; j < binary.Length; j++) + { + hammingWeight += binary[j] - '0'; + } + hammingWeights[i] = hammingWeight; + } + return hammingWeights; + } + + // With dp + public int[] CountBits(int n) + { + var dp = new int[n + 1]; + var offset = 1; + + for (int i = 1; i < n + 1; i++) + { + if (offset * 2 == i) + { + offset = i; + } + + dp[i] = 1 + dp[i - offset]; + } + + return dp; + } +} \ No newline at end of file diff --git a/csharp/0344-reverse-string.cs b/csharp/0344-reverse-string.cs new file mode 100644 index 000000000..672ad3807 --- /dev/null +++ b/csharp/0344-reverse-string.cs @@ -0,0 +1,30 @@ +// While Loop Solution +class Solution +{ + public void ReverseString(char[] s) + { + int leftPointer = 0; + int rightPointer = s.Length - 1; + while (leftPointer < rightPointer) + { + char temp = s[leftPointer]; + s[leftPointer++] = s[rightPointer]; + s[rightPointer--] = temp; + } + } +} + +// For Loop Solution +public class Solution +{ + public void ReverseString(char[] s) + { + var h = s.Length / 2; + for (int i = 0; i < h; i++) + { + var temp = s[i]; + s[i] = s[s.Length - i - 1]; + s[s.Length - i - 1] = temp; + } + } +} \ No newline at end of file diff --git a/csharp/0347-top-k-frequent-elements.cs b/csharp/0347-top-k-frequent-elements.cs new file mode 100644 index 000000000..e7175c351 --- /dev/null +++ b/csharp/0347-top-k-frequent-elements.cs @@ -0,0 +1,29 @@ +public class Solution { + public int[] TopKFrequent(int[] nums, int k) { + int[] arr = new int[k]; + var dict = new Dictionary(); + for (int i = 0; i < nums.Length; i++) + { + if (dict.ContainsKey(nums[i])) + { + dict[nums[i]]++; + } + else + { + dict.Add(nums[i], 1); + } + } + + var pq = new PriorityQueue(); + foreach (var key in dict.Keys) + { + pq.Enqueue(key, dict[key]); + if (pq.Count > k) pq.Dequeue(); + } + int i2 = k; + while (pq.Count > 0) { + arr[--i2] = pq.Dequeue(); + } + return arr; + } +} \ No newline at end of file diff --git a/csharp/0355-design-twitter.cs b/csharp/0355-design-twitter.cs new file mode 100644 index 000000000..70645356e --- /dev/null +++ b/csharp/0355-design-twitter.cs @@ -0,0 +1,124 @@ +public class Twitter +{ + + private PriorityQueue pq; + private int time; + private Dictionary> followers; + private Dictionary> tweets; + + public Twitter() + { + time = 0; + pq = new PriorityQueue(new MaxHeap()); + followers = new Dictionary>(); + tweets = new Dictionary>(); + } + + //T: O(1) + public void PostTweet(int userId, int tweetId) + { + time++; + if (!tweets.ContainsKey(userId)) + tweets.Add(userId, new List()); + tweets[userId].Add(new Tweet(time, tweetId)); + } + + public IList GetNewsFeed(int userId) + { + var result = new List(); + var followersOfUserId = new HashSet(); + if (followers.ContainsKey(userId)) + followersOfUserId = followers[userId]; + followersOfUserId.Add(userId); + + //We are just adding the last indexed tweet of the all the followers tweet + foreach (var followeeId in followersOfUserId) + { + if (tweets.ContainsKey(followeeId)) + { + var lastTweetIndex = tweets[followeeId].Count - 1; + var tweet = tweets[followeeId][lastTweetIndex]; + var tweetInfo = new TweetInfo(tweet.Time, tweet.TweetId, followeeId, lastTweetIndex - 1); + pq.Enqueue(tweetInfo, tweet.Time); + } + } + + while (pq.Count > 0 && result.Count < 10) + { + var tweetInfo = pq.Dequeue(); + result.Add(tweetInfo.TweetId); + + if (tweetInfo.Index >= 0) + { + var tweet = tweets[tweetInfo.FolloweeId][tweetInfo.Index]; + var tweetInfo2 = new TweetInfo(tweet.Time, tweet.TweetId, tweetInfo.FolloweeId, tweetInfo.Index - 1); + pq.Enqueue(tweetInfo2, tweet.Time); + } + } + + return result; + } + + //T: O(1) + public void Follow(int followerId, int followeeId) + { + if (!followers.ContainsKey(followerId)) + followers.Add(followerId, new HashSet()); + followers[followerId].Add(followeeId); + } + + // T: O(1) using HashSet for O(1) deletions + public void Unfollow(int followerId, int followeeId) + { + if (!followers.ContainsKey(followerId)) + return; + var followersList = followers[followerId]; + if (followersList.Contains(followeeId)) + followers[followerId].Remove(followeeId); + } + + public class Tweet + { + public int Time; + public int TweetId; + + public Tweet(int time, int tweetId) + { + Time = time; + TweetId = tweetId; + } + } + + public class TweetInfo : Tweet + { + public int Index; + public int FolloweeId; + + public TweetInfo(int time, int tweetId, int followeeId, int index) : base(time, tweetId) + { + Index = index; + FolloweeId = followeeId; + Time = time; + TweetId = tweetId; + } + } + + public class MaxHeap : IComparer + { + public int Compare(int x, int y) + { + if (x < y) return 1; + if (x > y) return -1; + else return 0; + } + } +} + +/** + * Your Twitter object will be instantiated and called as such: + * Twitter obj = new Twitter(); + * obj.PostTweet(userId,tweetId); + * IList param_2 = obj.GetNewsFeed(userId); + * obj.Follow(followerId,followeeId); + * obj.Unfollow(followerId,followeeId); + */ \ No newline at end of file diff --git a/csharp/0371-sum-of-two-integers.cs b/csharp/0371-sum-of-two-integers.cs new file mode 100644 index 000000000..6779682e1 --- /dev/null +++ b/csharp/0371-sum-of-two-integers.cs @@ -0,0 +1,11 @@ +public class Solution { + public int GetSum(int a, int b) { + while (b != 0) + { + var carry = a & b; + a = a ^ b; + b = carry << 1; + } + return a; + } +} \ No newline at end of file diff --git a/csharp/0374-guess-number-higher-or-lower.cs b/csharp/0374-guess-number-higher-or-lower.cs new file mode 100644 index 000000000..dbb8f3534 --- /dev/null +++ b/csharp/0374-guess-number-higher-or-lower.cs @@ -0,0 +1,27 @@ +public class Solution : GuessGame +{ + public int GuessNumber(int n) + { + int left = 0, right = n - 1; + + while (left <= right) + { + int mid = left + ((right - left) >> 1); + int result_guess = guess(mid); + if (result_guess == 1) + { + left = mid + 1; + } + else if (result_guess == -1) + { + right = mid - 1; + } + else + { + return mid; + } + } + + return left; + } +} diff --git a/csharp/0392-is-subsequence.cs b/csharp/0392-is-subsequence.cs new file mode 100644 index 000000000..29b2d9792 --- /dev/null +++ b/csharp/0392-is-subsequence.cs @@ -0,0 +1,22 @@ +public class Solution +{ + public bool IsSubsequence(string s, string t) + { + int i = 0; + int j = 0; + while ((i < s.Length) && (j < t.Length)) + { + if (s[i] == t[j]) + { + i += 1; + } + j += 1; + + } + if (i == s.Length) + { + return true; + } + else return false; + } +} \ No newline at end of file diff --git a/csharp/0416-partition-equal-subset-sum.cs b/csharp/0416-partition-equal-subset-sum.cs new file mode 100644 index 000000000..2be4b8e68 --- /dev/null +++ b/csharp/0416-partition-equal-subset-sum.cs @@ -0,0 +1,51 @@ +public class Solution +{ + + public bool CanPartition(int[] nums) + { + + + var sum = nums.Sum(); + if (sum % 2 != 0) + { + return false; + } + + return subSetSum(nums, sum / 2); + } + + private bool subSetSum(int[] nums, int target) + { + var dp = new bool[nums.Length + 1, target + 1]; + + for (var i = 0; i < nums.Length + 1; i++) + { + for (var j = 0; j < target + 1; j++) + { + if (i == 0) + { + dp[i, j] = false; + } + if (j == 0) + { + dp[i, j] = true; + } + } + } + + for (var i = 1; i < nums.Length + 1; i++) + { + for (var j = 1; j < target + 1; j++) + { + if (nums[i - 1] <= j) + { + dp[i, j] = dp[i - 1, j] || dp[i - 1, j - nums[i - 1]]; + } + else + dp[i, j] = dp[i - 1, j]; + } + } + + return dp[nums.Length, target]; + } +} \ No newline at end of file diff --git a/csharp/0417-pacific-atlantic-water-flow.cs b/csharp/0417-pacific-atlantic-water-flow.cs new file mode 100644 index 000000000..894291766 --- /dev/null +++ b/csharp/0417-pacific-atlantic-water-flow.cs @@ -0,0 +1,45 @@ +public class Solution { + + public IList> PacificAtlantic(int[][] heights) + { + List> res = new(); + int m = heights.Length, n = heights[0].Length; + bool[,] isPacific = new bool[m, n]; + bool[,] isAtlantic = new bool[m, n]; + for (int row = 0; row < m; row++) + { + DFSPacificAtlantic(row, 0, heights, isPacific, heights[row][0]); + DFSPacificAtlantic(row, n - 1, heights, isAtlantic, heights[row][n - 1]); + } + + for (int col = 0; col < n; col++) + { + DFSPacificAtlantic( 0, col, heights, isPacific, heights[0][col]); + DFSPacificAtlantic(m-1,col, heights, isAtlantic, heights[m-1][col]); + } + for (int i = 0; i < m; i++) + { + for (int j = 0; j < n; j++) + { + if (isAtlantic[i, j] && isPacific[i, j]) + { + res.Add(new List { i, j }); + } + } + } + + return res; + } + + private void DFSPacificAtlantic(int row, int col, int[][] heights, bool[,] visits, int prev) + { + int m = heights.Length, n = heights[0].Length; + if (row < 0 || row >= m || col < 0 || col >= n || visits[row, col] || heights[row][col] < prev) + return; + visits[row, col] = true; + DFSPacificAtlantic(row, col + 1, heights, visits, heights[row][col]); + DFSPacificAtlantic(row, col - 1, heights, visits, heights[row][col]); + DFSPacificAtlantic(row + 1, col, heights, visits, heights[row][col]); + DFSPacificAtlantic(row - 1, col, heights, visits, heights[row][col]); + } +} diff --git a/csharp/0424-longest-repeating-character-replacement.cs b/csharp/0424-longest-repeating-character-replacement.cs new file mode 100644 index 000000000..edf49982f --- /dev/null +++ b/csharp/0424-longest-repeating-character-replacement.cs @@ -0,0 +1,21 @@ +public class Solution { + public int CharacterReplacement(string s, int k) { + int left = 0, maxLength = 0; + int mostFrequentLetterCount = 0; // Count of most frequent letter in the window + int[] charCounts = new int[26]; // Counts per letter + + for (int right = 0; right < s.Length; right++) { + charCounts[s[right] - 'A']++; + mostFrequentLetterCount = Math.Max(mostFrequentLetterCount, charCounts[s[right] - 'A']); + + int lettersToChange = (right - left + 1) - mostFrequentLetterCount; + if (lettersToChange > k) { // Window is invalid, decrease char count and move left pointer + charCounts[s[left] - 'A']--; + left++; + } + + maxLength = Math.Max(maxLength, (right - left + 1)); + } + return maxLength; + } +} \ No newline at end of file diff --git a/csharp/0435-non-overlapping-intervals.cs b/csharp/0435-non-overlapping-intervals.cs new file mode 100644 index 000000000..bdc01c819 --- /dev/null +++ b/csharp/0435-non-overlapping-intervals.cs @@ -0,0 +1,28 @@ +public class Solution +{ + public int EraseOverlapIntervals(int[][] intervals) + { + var sortedIntervals = intervals.Clone() as int[][]; + Array.Sort(sortedIntervals, (a, b) => a[0] - b[0]); + + var result = 0; + var prevEnd = sortedIntervals[0][1]; + for (var i = 1; i < sortedIntervals.Length; i++) + { + + var curr = sortedIntervals[i]; + + if (prevEnd > curr[0]) + { + result++; + prevEnd = Math.Min(prevEnd, curr[1]); + } + else + { + prevEnd = curr[1]; + } + } + + return result; + } +} \ No newline at end of file diff --git a/csharp/0438-find-all-anagrams-in-a-string.cs b/csharp/0438-find-all-anagrams-in-a-string.cs new file mode 100644 index 000000000..e2d23352f --- /dev/null +++ b/csharp/0438-find-all-anagrams-in-a-string.cs @@ -0,0 +1,78 @@ +public class Solution +{ + + public IList FindAnagrams(string s, string p) + { + List res = new List(); + + int pSize = p.Length; + int sSize = s.Length; + + if (pSize > sSize) + { + return res; + } + + Dictionary pHash = new Dictionary(); + Dictionary sHash = new Dictionary(); + + foreach (char c in p) + { + int tmp; + pHash.TryGetValue(c, out tmp); + pHash[c] = tmp + 1; + } + + for (int x = 0; x < pSize; x++) + { + char c = s[x]; + int tmp; + sHash.TryGetValue(c, out tmp); + sHash[c] = tmp + 1; + } + + if (compareDict(pHash, sHash)) + { + res.Add(0); + } + + int i = 1; + int j = i + pSize - 1; + + while (j < sSize) + { + sHash[s[i - 1]]--; + + int tmp; + sHash.TryGetValue(s[j], out tmp); + sHash[s[j]] = tmp + 1; + + if (compareDict(pHash, sHash)) + { + res.Add(i); + } + + i++; + j++; + } + + return res; + } + + public bool compareDict(Dictionary d1, Dictionary d2) + { + foreach (var i in d1) + { + if (d2.ContainsKey(i.Key) && i.Value == d2[i.Key]) + { + continue; + } + else + { + return false; + } + } + + return true; + } +} diff --git a/csharp/0448-find-all-numbers-disappeared-in-an-array.cs b/csharp/0448-find-all-numbers-disappeared-in-an-array.cs new file mode 100644 index 000000000..58b5c139e --- /dev/null +++ b/csharp/0448-find-all-numbers-disappeared-in-an-array.cs @@ -0,0 +1,15 @@ +public class Solution { + public IList FindDisappearedNumbers(int[] nums) { + for(int i = 0; i < nums.Length; i++) { + int index = Math.Abs(nums[i]) - 1; + nums[index] = -Math.Abs(nums[index]); + } + List result = new List(); + for(int i = 0; i < nums.Length; i++) { + if(nums[i] > 0) { + result.Add(i + 1); + } + } + return result; + } +} \ No newline at end of file diff --git a/csharp/0463-island-perimeter.cs b/csharp/0463-island-perimeter.cs new file mode 100644 index 000000000..8caa3e769 --- /dev/null +++ b/csharp/0463-island-perimeter.cs @@ -0,0 +1,42 @@ +public class Solution { +int perimeter = 0; + public int IslandPerimeter(int[][] grid) + { + if (grid?.Length == 0) + return 0; + + bool[,] visits = new bool[grid.Length, grid[0].Length]; + + for (int i = 0; i < grid.Length; i++) + { + for (int j = 0; j < grid[0].Length; j++) + { + if (grid[i][j] == 1) + { + return DfsIslandPerimeter(grid, visits, i, j); + } + } + + } + + return perimeter; + } + + public int DfsIslandPerimeter(int[][] grid, bool[,] visits, int i, int j) + { + if (i < 0 || i >= grid.Length || j < 0 || j >= grid[0].Length || grid[i][j] == 0) + { + return 1; + } + if (visits[i, j]) + { + return 0; + } + visits[i, j] = true; //to mark it as visited in iteration. + perimeter = DfsIslandPerimeter(grid, visits, i, j + 1); + perimeter += DfsIslandPerimeter(grid, visits, i + 1, j); + perimeter += DfsIslandPerimeter(grid, visits, i, j - 1); + perimeter += DfsIslandPerimeter(grid, visits, i - 1, j); + return perimeter; + } +} diff --git a/csharp/0494-target-sum.cs b/csharp/0494-target-sum.cs new file mode 100644 index 000000000..f3683f63a --- /dev/null +++ b/csharp/0494-target-sum.cs @@ -0,0 +1,18 @@ +public class Solution { + public int FindTargetSumWays(int[] nums, int target) { + var mem = new Dictionary<(int, int), int>(); + + int dfs(int index, int total){ + if(index == nums.Length) + return total == target ? 1: 0; + + + if(mem.ContainsKey((index, total))){ + return mem[(index, total)]; + } + + mem[(index, total)] = dfs(index+1, total+nums[index]) + dfs(index+1, total-nums[index]); + return mem[(index, total)]; + } + + return dfs(0,0); \ No newline at end of file diff --git a/csharp/0496-next-greater-element-i.cs b/csharp/0496-next-greater-element-i.cs new file mode 100644 index 000000000..a35a45c3e --- /dev/null +++ b/csharp/0496-next-greater-element-i.cs @@ -0,0 +1,20 @@ +public class Solution { + public int[] NextGreaterElement(int[] nums1, int[] nums2) { + + Dictionary dic = new Dictionary(); + Stack stack = new Stack(); + foreach(var num in nums2) + { + while(stack.Count > 0 && num > stack.Peek()) + dic.Add(stack.Pop(),num); + + stack.Push(num); + } + + int[] res = new int[nums1.Length]; + for(int i = 0; i < nums1.Length; i++) + res[i] = dic.ContainsKey(nums1[i])? dic[nums1[i]] : -1; + + return res; + } +} \ No newline at end of file diff --git a/csharp/0498-diagonal-traverse.cs b/csharp/0498-diagonal-traverse.cs new file mode 100644 index 000000000..75b0366fd --- /dev/null +++ b/csharp/0498-diagonal-traverse.cs @@ -0,0 +1,89 @@ +public class Solution +{ + public enum Direction + { + Left, + Right + } + + public struct Pos + { + public int i; + public int j; + + public Pos(int i, int j) + { + this.i = i; + this.j = j; + } + } + + public static bool OutOfRange(int[][] mat, int i, int j) + { + if (mat.Length == 0 || mat[0].Length == 0) return true; + + if (i >= mat.Length || j >= mat[0].Length) return true; + + if (i < 0 || j < 0) return true; + + return false; + } + + public static bool MoveForward(ref Pos pos, ref Direction direction, int[][] mat) + { + var i = pos.i; + var j = pos.j; + var maxI = mat.Length - 1; + var maxJ = mat[0].Length - 1; + + if (direction == Direction.Left) + { + i += 1; + j -= 1; + } + else + { + i -= 1; + j += 1; + } + + if (OutOfRange(mat, i, j)) + { + direction = direction == Direction.Right ? Direction.Left : Direction.Right; + + i = pos.i; + j = pos.j; + + if (direction == Direction.Left) + { + if (j < maxJ) j += 1; + else i += 1; + } + else + { + if (i < maxI) i += 1; + else j += 1; + } + } + + pos = new Pos(i, j); + + return !OutOfRange(mat, i, j); + } + + public int[] FindDiagonalOrder(int[][] mat) + { + int[] result = new int[mat.Length * mat[0].Length]; + var pos = new Pos(0, 0); + var direction = Direction.Right; + var i = 0; + + do + { + result[i++] = mat[pos.i][pos.j]; + } + while (MoveForward(ref pos, ref direction, mat)); + + return result; + } +} \ No newline at end of file diff --git a/csharp/0518-coin-change-ii.cs b/csharp/0518-coin-change-ii.cs new file mode 100644 index 000000000..8f4df9f05 --- /dev/null +++ b/csharp/0518-coin-change-ii.cs @@ -0,0 +1,26 @@ +public class Solution +{ + // memoization reduces T from O(M^N) to O(M.N) where M- is no. of coins, N is amount + public int Change(int amount, int[] coins) + { + var mem = new Dictionary<(int, int), int>(); + + int dfs(int index, int a) + { + if (a == amount) + return 1; + if (a > amount) + return 0; + if (index == coins.Length) + return 0; + if (mem.ContainsKey((index, a))) + return mem[(index, a)]; + + mem[(index, a)] = dfs(index, a + coins[index]) + dfs(index + 1, a); + return mem[(index, a)]; + + } + + return dfs(0, 0); + } +} \ No newline at end of file diff --git a/csharp/0523-continuous-subarray-sum.cs b/csharp/0523-continuous-subarray-sum.cs new file mode 100644 index 000000000..1f7453063 --- /dev/null +++ b/csharp/0523-continuous-subarray-sum.cs @@ -0,0 +1,19 @@ +public class Solution +{ + public bool CheckSubarraySum(int[] nums, int k) + { + var remainder = new Dictionary(); + remainder.Add(0, -1); + var total = 0; + for (var i = 0; i < nums.Length; i++) + { + total += nums[i]; + var r = total % k; + if (!remainder.ContainsKey(r)) + remainder.Add(r, i); + else if (i - remainder[r] > 1) + return true; + } + return false; + } +} \ No newline at end of file diff --git a/csharp/0535-encode-and-decode-tinyurl.cs b/csharp/0535-encode-and-decode-tinyurl.cs new file mode 100644 index 000000000..fbd898b63 --- /dev/null +++ b/csharp/0535-encode-and-decode-tinyurl.cs @@ -0,0 +1,29 @@ +public class Codec +{ + public Dictionary EncodeMap = new Dictionary(); + public Dictionary DecodeMap = new Dictionary(); + public string Base = "http://tinyurl.com/"; + + // Encodes a URL to a shortened URL + public string encode(string longUrl) + { + if (!EncodeMap.ContainsKey(longUrl)) + { + var shortUrl = Base + EncodeMap.Count().ToString(); + EncodeMap.Add(longUrl, shortUrl); + DecodeMap.Add(shortUrl, longUrl); + } + + return EncodeMap[longUrl]; + } + + // Decodes a shortened URL to its original URL. + public string decode(string shortUrl) + { + return DecodeMap[shortUrl]; + } +} + +// Your Codec object will be instantiated and called as such: +// Codec codec = new Codec(); +// codec.decode(codec.encode(url)); \ No newline at end of file diff --git a/csharp/0543-diameter-of-binary-tree.cs b/csharp/0543-diameter-of-binary-tree.cs new file mode 100644 index 000000000..040f1eef0 --- /dev/null +++ b/csharp/0543-diameter-of-binary-tree.cs @@ -0,0 +1,31 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + int result = -1; + + public int DiameterOfBinaryTree(TreeNode root) { + dfs(root); + return result; + } + + private int dfs(TreeNode current) { + if (current == null) { + return -1; + } + int left = 1 + dfs(current.left); + int right = 1 + dfs(current.right); + result = Math.Max(result, (left + right)); + return Math.Max(left, right); + } +} \ No newline at end of file diff --git a/csharp/0554-brick-wall.cs b/csharp/0554-brick-wall.cs new file mode 100644 index 000000000..1f9e3614a --- /dev/null +++ b/csharp/0554-brick-wall.cs @@ -0,0 +1,20 @@ +public class Solution { + public int LeastBricks(IList> wall) { + Dictionary countGap = new Dictionary(); + countGap[0] = 0; + + foreach (var row in wall) { + int total = 0; + for (int i = 0; i < row.Count - 1; i++) { + int brick = row[i]; + total += brick; + if (!countGap.ContainsKey(total)) { + countGap[total] = 0; + } + countGap[total]++; + } + } + + return wall.Count - countGap.Values.Max(); + } +} diff --git a/csharp/0560-subarray-sum-equals-k.cs b/csharp/0560-subarray-sum-equals-k.cs new file mode 100644 index 000000000..da672b5d8 --- /dev/null +++ b/csharp/0560-subarray-sum-equals-k.cs @@ -0,0 +1,31 @@ +public class Solution +{ + public int SubarraySum(int[] nums, int k) + { + Dictionary prefixToCounts = new Dictionary(); + prefixToCounts.Add(0, 1); + + int sum = 0; + int res = 0; + + for (int i = 0; i < nums.Length; i++) + { + sum += nums[i]; + int diff = sum - k; + + prefixToCounts.TryGetValue(diff, out int count); + + res += count; + + if (prefixToCounts.ContainsKey(sum)) + { + prefixToCounts[sum] += 1; + continue; + } + + prefixToCounts.Add(sum, 1); + } + + return res; + } +} \ No newline at end of file diff --git a/csharp/0567-permutation-in-string.cs b/csharp/0567-permutation-in-string.cs new file mode 100644 index 000000000..cf7272e03 --- /dev/null +++ b/csharp/0567-permutation-in-string.cs @@ -0,0 +1,56 @@ +public class Solution +{ + public bool CheckInclusion(string s1, string s2) + { + { + if (s1.Length > s2.Length) return false; + + var s1Count = Enumerable.Repeat(0, 26).ToArray(); + var s2Count = Enumerable.Repeat(0, 26).ToArray(); + + for (var i = 0; i < s1.Length; i++) + { + s1Count[s1[i] - 'a']++; + s2Count[s2[i] - 'a']++; + } + + var matches = 0; + for (var i = 0; i < 26; i++) + { + if (s1Count[i] == s2Count[i]) matches++; + } + + var left = 0; + for (var right = s1.Length; right < s2.Length; right++) + { + if (matches == 26) return true; + + var index = s2[right] - 'a'; + s2Count[index]++; + if (s1Count[index] == s2Count[index]) + { + matches++; + } + else if (s1Count[index] + 1 == s2Count[index]) + { + matches--; + } + + index = s2[left] - 'a'; + s2Count[index]--; + if (s1Count[index] == s2Count[index]) + { + matches++; + } + else if (s1Count[index] - 1 == s2Count[index]) + { + matches--; + } + + left++; + } + + return matches == 26; + } + } +} \ No newline at end of file diff --git a/csharp/0572-subtree-of-another-tree.cs b/csharp/0572-subtree-of-another-tree.cs new file mode 100644 index 000000000..e4a42ad35 --- /dev/null +++ b/csharp/0572-subtree-of-another-tree.cs @@ -0,0 +1,29 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + public bool IsSubtree(TreeNode root, TreeNode subRoot) { + if (root == null) return root == subRoot; + if (root.val == subRoot?.val && IsSameTree(root, subRoot)) return true; + + return IsSubtree(root.left, subRoot) || IsSubtree(root.right, subRoot); + } + + public bool IsSameTree(TreeNode p, TreeNode q) { + if (p == null || q == null) return p == q; + + return p.val == q.val + && IsSameTree(p.left, q.left) + && IsSameTree(p.right, q.right); + } +} diff --git a/csharp/0605-can-place-flowers.cs b/csharp/0605-can-place-flowers.cs new file mode 100644 index 000000000..776a1ae3c --- /dev/null +++ b/csharp/0605-can-place-flowers.cs @@ -0,0 +1,13 @@ +public class Solution { + public bool CanPlaceFlowers(int[] flowerbed, int n) { + + for(int i = 0; i < flowerbed.Length; i++){ + if(n == 0) return true; + if((i == 0 || flowerbed[i - 1] == 0) && (flowerbed[i] == 0) && (i == flowerbed.Length - 1 || flowerbed[i + 1] == 0)){ + flowerbed[i] = 1; + n--; + } + } + return n == 0; + } +} \ No newline at end of file diff --git a/csharp/0617-merge-two-binary-trees.cs b/csharp/0617-merge-two-binary-trees.cs new file mode 100644 index 000000000..4d0a4e486 --- /dev/null +++ b/csharp/0617-merge-two-binary-trees.cs @@ -0,0 +1,12 @@ +public class Solution { + public TreeNode MergeTrees(TreeNode root1, TreeNode root2) { + if (root1 == null) return root2; + + if (root2 == null) return root1; + + return new TreeNode(root1.val + root2.val, + MergeTrees(root1.left, root2.left), + MergeTrees(root1.right, root2.right) + ); + } +} \ No newline at end of file diff --git a/csharp/0621-task-scheduler.cs b/csharp/0621-task-scheduler.cs new file mode 100644 index 000000000..a0e38ed2b --- /dev/null +++ b/csharp/0621-task-scheduler.cs @@ -0,0 +1,89 @@ +public class Solution +{ + private PriorityQueue pq; + private Dictionary dictionary; + public int LeastInterval(char[] tasks, int n) + { + // Count tasks in the array + if (n == 0) + return tasks.Length; + dictionary = new Dictionary(); + foreach (var task in tasks) + { + if (dictionary.ContainsKey(task)) + { + dictionary[task]++; + } + else + dictionary.Add(task, 1); + } + + + pq = new PriorityQueue(new MaxHeap()); + + var time = 0; + + AddItemsToPQ(); + + + while (pq.Count > 0) + { + var list = new List(); + var cnt = 0; + for (var i = 0; i < n + 1; i++) + { + if (pq.Count > 0) + { + var item = pq.Dequeue(); + cnt++; + //Console.WriteLine($"Dequeued {item.Task} with frequency : {item.Frequency}"); + item.Frequency--; + if (item.Frequency > 0) + list.Add(item); + } + } + + for (var i = 0; i < list.Count; i++) + { + var item = list[i]; + //Console.WriteLine($"Enqueued {item.Task} with frequency : {item.Frequency}"); + pq.Enqueue(item, item.Frequency); + } + + time += pq.Count == 0 ? cnt : n + 1; + //Console.WriteLine($"Done with iteration, current time: {time}"); + } + + return time; + } + + private void AddItemsToPQ() + { + foreach (var keyValuePair in dictionary) + { + pq.Enqueue(new FreqClass(keyValuePair.Value, 0, keyValuePair.Key), keyValuePair.Value); + } + } + + public class MaxHeap : IComparer + { + public int Compare(int x, int y) + { + return y - x; + } + } + + public class FreqClass + { + public int Frequency; + public int IdleTime; + public char Task; + + public FreqClass(int frequency, int idleTime, char task) + { + Frequency = frequency; + IdleTime = idleTime; + Task = task; + } + } +} \ No newline at end of file diff --git a/csharp/0647-palindromic-substrings.cs b/csharp/0647-palindromic-substrings.cs new file mode 100644 index 000000000..816106413 --- /dev/null +++ b/csharp/0647-palindromic-substrings.cs @@ -0,0 +1,23 @@ +public class Solution { + public int CountSubstrings(string s) { + var count = 0; + + for(var i = 0; i < s.Length; i++) { + count += getPalindromeCount(s, i, i); + count += getPalindromeCount(s, i, i + 1); + } + + return count; + } + + public int getPalindromeCount(string s, int l, int r) { + var count = 0; + + while(l >=0 && r < s.Length && s[l] == s[r]) { + count++; + l--; + r++; + } + return count; + } +} \ No newline at end of file diff --git a/csharp/0665-non-decreasing-array.cs b/csharp/0665-non-decreasing-array.cs new file mode 100644 index 000000000..a6cbd7eab --- /dev/null +++ b/csharp/0665-non-decreasing-array.cs @@ -0,0 +1,27 @@ +public class Solution +{ + public bool CheckPossibility(int[] nums) + { + bool isChanged = false; + + for (int i = 0; i < nums.Length - 1; i++) + { + if (nums[i] <= nums[i + 1]) continue; + + if (isChanged) return false; + + if (i == 0 || nums[i + 1] >= nums[i - 1]) + { + nums[i] = nums[i + 1]; + } + else + { + nums[i + 1] = nums[i]; + } + + isChanged = true; + } + + return true; + } +} \ No newline at end of file diff --git a/csharp/0678-valid-parenthesis-string.cs b/csharp/0678-valid-parenthesis-string.cs new file mode 100644 index 000000000..a358e2610 --- /dev/null +++ b/csharp/0678-valid-parenthesis-string.cs @@ -0,0 +1,37 @@ +public class Solution +{ + //T: O(N) , S: O(1) + public bool CheckValidString(string s) + { + var leftMax = 0; + var leftMin = 0; + + foreach (var ch in s) + { + if (ch == '(') + { + leftMin++; + leftMax++; + } + else if (ch == ')') + { + leftMax--; + leftMin--; + } + else + { + leftMin--; + leftMax++; + } + + if (leftMax < 0) + return false; + + if (leftMin < 0) + leftMin = 0; + + } + + return leftMin == 0; + } +} \ No newline at end of file diff --git a/csharp/0680-valid-palindrome-ii.cs b/csharp/0680-valid-palindrome-ii.cs new file mode 100644 index 000000000..e95a9af27 --- /dev/null +++ b/csharp/0680-valid-palindrome-ii.cs @@ -0,0 +1,31 @@ +public class Solution +{ + public bool ValidPalindrome(string s) + { + int leftPointer = 0; + int rightPointer = s.Length - 1; + while (leftPointer < rightPointer) + { + if (s[leftPointer] != s[rightPointer]) + { + return IsPalindrome(s, leftPointer + 1, rightPointer) || IsPalindrome(s, leftPointer, rightPointer - 1); + } + leftPointer++; + rightPointer--; + } + return true; + } + public bool IsPalindrome(String s, int leftPointer, int rightPointer) + { + while (leftPointer < rightPointer) + { + if (s[leftPointer] != s[rightPointer]) + { + return false; + } + leftPointer++; + rightPointer--; + } + return true; + } +} \ No newline at end of file diff --git a/csharp/0682-baseball-game.cs b/csharp/0682-baseball-game.cs new file mode 100644 index 000000000..e84e122be --- /dev/null +++ b/csharp/0682-baseball-game.cs @@ -0,0 +1,26 @@ +public class Solution { + public int CalPoints(string[] operations) { + List stack = new(); + + foreach(var operation in operations) + { + switch(operation) + { + case "D": + stack.Add(stack[stack.Count()-1]*2); + break; + case "+": + stack.Add(stack[stack.Count()-1] + stack[stack.Count()-2]); + break; + case "C": + stack.RemoveAt(stack.Count()-1); + break; + default: + stack.Add(int.Parse(operation)); + break; + } + } + + return stack.Sum(); + } +} \ No newline at end of file diff --git a/csharp/0684-redundant-connection.cs b/csharp/0684-redundant-connection.cs new file mode 100644 index 000000000..3b9f76173 --- /dev/null +++ b/csharp/0684-redundant-connection.cs @@ -0,0 +1,45 @@ +public class Solution { + public int[] FindRedundantConnection(int[][] edges) { + var nodes = edges.Length; + int[] parent = new int[nodes + 1]; + int[] rank = new int[nodes + 1]; + + for(int i = 0; i < nodes; i++) { + parent[i] = i; + rank[i] = 1; + } + + int findParent (int n) { + var p = parent[n]; + + while(p != parent[p]) { + parent[p] = parent[parent[p]]; + p = parent[p]; + } + + return p; + } + bool union(int n1, int n2) { + (int p1, int p2) = (findParent(n1), findParent(n2)); + + if(p1 == p2) return false; + + if(rank[p1] > rank[p2]) { + parent[p2] = p1; + rank[p1] += rank[p2]; + } else { + parent[p1] = p2; + rank[p2] += rank[p1]; + } + + return true; + } + + foreach(var edge in edges) { + if(union(edge[0], edge[1]) is false) + return edge; + } + + return new int[2]; + } +} \ No newline at end of file diff --git a/csharp/0695-max-area-of-island.cs b/csharp/0695-max-area-of-island.cs new file mode 100644 index 000000000..a2a959c49 --- /dev/null +++ b/csharp/0695-max-area-of-island.cs @@ -0,0 +1,28 @@ +public class Solution { + public int MaxAreaOfIsland(int[][] grid) + { + int r = grid.Length, c = grid[0].Length, area = 0; + + bool[,] visits = new bool[r, c]; + for (int i = 0; i < r; i++) + { + for (int j = 0; j < c; j++) + { + area = Math.Max(area, DFSMaxAreaOfIsland(i, j, grid, visits)); + } + } + + return area; + } + + private int DFSMaxAreaOfIsland(int row, int col, int[][] grid, bool[,] visits) + { + int m = grid.Length, n = grid[0].Length; + if (row < 0 || row >= m || col < 0 || col >= n || visits[row, col] || grid[row][col] == 0) + return 0; + visits[row, col] = true; + return (1 + DFSMaxAreaOfIsland(row, col + 1, grid, visits) + + DFSMaxAreaOfIsland(row, col - 1, grid, visits) + + DFSMaxAreaOfIsland(row + 1, col, grid, visits) + + DFSMaxAreaOfIsland(row - 1, col, grid, visits)); + }} diff --git a/csharp/0701-insert-into-a-binary-search-tree.cs b/csharp/0701-insert-into-a-binary-search-tree.cs new file mode 100644 index 000000000..d6294702e --- /dev/null +++ b/csharp/0701-insert-into-a-binary-search-tree.cs @@ -0,0 +1,51 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution +{ + public TreeNode InsertIntoBST(TreeNode root, int val) + { + if (root is null) return new TreeNode(val); + + TreeNode cur = root; + + while (cur is not null) + { + if (cur.val < val) + { + if (cur.right is null) + { + cur.right = new TreeNode(val); + break; + } + + cur = cur.right; + continue; + } + + if (cur.val > val) + { + if (cur.left is null) + { + cur.left = new TreeNode(val); + break; + } + + cur = cur.left; + continue; + } + } + + return root; + } +} \ No newline at end of file diff --git a/csharp/0703-kth-largest-element-in-a-stream.cs b/csharp/0703-kth-largest-element-in-a-stream.cs new file mode 100644 index 000000000..09cf7299d --- /dev/null +++ b/csharp/0703-kth-largest-element-in-a-stream.cs @@ -0,0 +1,19 @@ +public class KthLargest { + PriorityQueue data = new PriorityQueue(); + int k; + + public KthLargest(int k, int[] nums) { + this.k = k; + foreach(var num in nums) + Add(num); + } + + public int Add(int val) { + data.Enqueue(val, val); + + if(data.Count > k) + data.Dequeue(); + + return data.Peek(); + } +} \ No newline at end of file diff --git a/csharp/0704-binary-search.cs b/csharp/0704-binary-search.cs new file mode 100644 index 000000000..32772916b --- /dev/null +++ b/csharp/0704-binary-search.cs @@ -0,0 +1,17 @@ +public class Solution { + public int Search(int[] nums, int target) { + int left = 0, right = nums.Length - 1; + + while (left <= right) { + int mid = left + ((right - left) / 2); // (left + right) / 2 can lead to overflow + if (nums[mid] > target) { + right = mid - 1; + } else if (nums[mid] < target) { + left = mid + 1; + } else { // Found the value + return mid; + } + } + return -1; + } +} \ No newline at end of file diff --git a/csharp/0705-design-hashset.cs b/csharp/0705-design-hashset.cs new file mode 100644 index 000000000..d99291c64 --- /dev/null +++ b/csharp/0705-design-hashset.cs @@ -0,0 +1,19 @@ +public class MyHashSet { + private bool[] hashSet; + + public MyHashSet() { + hashSet = new bool[1000001]; + } + + public void Add(int key) { + hashSet[key] = true; + } + + public void Remove(int key) { + hashSet[key] = false; + } + + public bool Contains(int key) { + return hashSet[key]; + } +} diff --git a/csharp/0706-design-hashmap.cs b/csharp/0706-design-hashmap.cs new file mode 100644 index 000000000..908d3cae9 --- /dev/null +++ b/csharp/0706-design-hashmap.cs @@ -0,0 +1,97 @@ +public class MyHashMap +{ + private class LinkedListNode + { + public LinkedListNode(int key, int val, LinkedListNode next) + { + this.Key = key; + this.Val = val; + this.Next = next; + } + + public int Key { get; set; } + public int Val { get; set; } + public LinkedListNode Next { get; set; } + } + + public MyHashMap() + { + Map = new LinkedListNode[10000]; + } + + public int Hash(int key) + { + return key % Map.Length; + } + + public void Put(int key, int value) + { + int index = Hash(key); + if (Map[index] == null) + Map[index] = new LinkedListNode(key, value, null); + else + { + LinkedListNode node = Map[index]; + while (true) + { + if (node.Key == key) + { + node.Val = value; + return; + } + if (node.Next == null) + break; + else + node = node.Next; + } + node.Next = new LinkedListNode(key, value, null); + } + } + + public int Get(int key) + { + int index = Hash(key); + LinkedListNode node = Map[index]; + if (node != null) + { + while (true) + { + if (node.Key == key) + return node.Val; + + if (node.Next == null) + break; + else + node = node.Next; + } + } + + return -1; + } + + public void Remove(int key) + { + int index = Hash(key); + LinkedListNode node = Map[index]; + if (node == null) + return; + if (node.Key == key) + { + Map[index] = node.Next; + node = null; + return; + } + while (node.Next != null) + { + if (node.Next.Key == key) + { + node.Next = node.Next.Next; + return; + } + + node = node.Next; + } + } + + private LinkedListNode[] Map { get; set; } +} \ No newline at end of file diff --git a/csharp/0724-find-pivot-index.cs b/csharp/0724-find-pivot-index.cs new file mode 100644 index 000000000..8365c8228 --- /dev/null +++ b/csharp/0724-find-pivot-index.cs @@ -0,0 +1,18 @@ +public class Solution { + public int PivotIndex(int[] nums) { + int total = nums.Sum(); + + int leftSum = 0; + + for (int i = 0; i < nums.Length; i++) + { + if (leftSum == total - leftSum - nums[i]) + { + return i; + } + + leftSum += nums[i]; + } + return -1; + } +} \ No newline at end of file diff --git a/csharp/0739-daily-temperatures.cs b/csharp/0739-daily-temperatures.cs new file mode 100644 index 000000000..5fb7b1f1e --- /dev/null +++ b/csharp/0739-daily-temperatures.cs @@ -0,0 +1,22 @@ +public class Solution +{ + public int[] DailyTemperatures(int[] temperatures) + { + var result = new int[temperatures.Length]; + Array.Fill(result, 0); + var stack = new Stack(); + + for (var i = 0; i < temperatures.Length; i++) + { + var t = temperatures[i]; + while (stack.Any() && temperatures[stack.Peek()] < t) + { + var prev = stack.Pop(); + result[prev] = i - prev; + } + stack.Push(i); + } + + return result; + } +} \ No newline at end of file diff --git a/csharp/0743-network-delay-time.cs b/csharp/0743-network-delay-time.cs new file mode 100644 index 000000000..00d6ccd18 --- /dev/null +++ b/csharp/0743-network-delay-time.cs @@ -0,0 +1,36 @@ +public class Solution { + public int NetworkDelayTime(int[][] times, int n, int k) { + List<(int node, int weight)>[] adjList = new List<(int node, int weight)>[n + 1]; + + for (var i = 0; i <= n; i++) + { + adjList[i] = new List<(int node, int wht)>(); + } + + foreach (var time in times) + { + adjList[time[0]].Add((time[1], time[2])); + } + + int[] visited = new int[n + 1]; + Array.Fill(visited, 0); + PriorityQueue queue = new(); + + queue.Enqueue(k, 0); + int res = 0; + while (queue.TryDequeue(out int node, out int weight)) + { + if (visited[node] == 1) continue; + visited[node] = 1; + res = Math.Max(res, weight); + foreach (var adj in adjList[node]) + { + var totalWT = weight + adj.weight; + queue.Enqueue(adj.node, totalWT); + } + } + + int? visitedCount = visited.Where(e => e == 1)?.Count(); + return visitedCount == n ? res : -1; + } +} diff --git a/csharp/0746-min-cost-climbing-stairs.cs b/csharp/0746-min-cost-climbing-stairs.cs new file mode 100644 index 000000000..b17bcf46d --- /dev/null +++ b/csharp/0746-min-cost-climbing-stairs.cs @@ -0,0 +1,25 @@ +public class Solution { + public int MinCostClimbingStairs(int[] cost) { + // return topDown(cost); + return bottomUp(cost); + } + + private int topDown(int[] cost) { + var l = cost.Length; + + for(var i = 2; i < l; i ++) { + cost[i] += Math.Min(cost[i - 1], cost[i - 2]); + } + + return Math.Min(cost[l - 1], cost[l - 2]); + } + + private int bottomUp(int[] cost) { + + for(var i = cost.Length - 3; i >= 0; i--) { + cost[i] += Math.Min(cost[i + 1], cost[i + 2]); + } + + return Math.Min(cost[0], cost[1]); + } +} \ No newline at end of file diff --git a/csharp/0752-open-the-lock.cs b/csharp/0752-open-the-lock.cs new file mode 100644 index 000000000..617d08fe1 --- /dev/null +++ b/csharp/0752-open-the-lock.cs @@ -0,0 +1,48 @@ +public class Solution { + public int OpenLock(string[] deadends, string target) + { + const string start = "0000"; + + var deadEnds = deadends.ToHashSet(); + var visited = new HashSet(); + if (deadEnds.Contains(start) || deadends.Contains(target)) return -1; + + Queue q = new Queue(new[] { start }); + visited.Add(start); + int res = 0; + while (q.Count > 0) + { + int queueCnt=q.Count; + for (int i = 0; i < queueCnt; i++) + { + var curr = q.Dequeue(); + if (curr == target) return res; + foreach (var nei in GetNeighbors(curr)) + { + if (!visited.Contains(nei) && !deadends.Contains(nei)) + { + q.Enqueue(nei); + visited.Add(nei); + } + } + } + res++; + } + return -1; + } + + private List GetNeighbors(string s) + { + var result = new List(); + for (int i = 0; i < s.Length; i++) + { + var charAr1 = s.ToCharArray(); + charAr1[i] = charAr1[i] == '9' ? '0' : (char)((int)charAr1[i] + 1); + result.Add(new string(charAr1)); + var charAr2 = s.ToCharArray(); + charAr2[i] = charAr2[i] == '0' ? '9' : (char)((int)charAr2[i] - 1); + result.Add(new string(charAr2)); + } + return result; + } +} diff --git a/csharp/0763-partition-labels.cs b/csharp/0763-partition-labels.cs new file mode 100644 index 000000000..13f480d42 --- /dev/null +++ b/csharp/0763-partition-labels.cs @@ -0,0 +1,29 @@ +public class Solution +{ + public IList PartitionLabels(string s) + { + + var chars = new int[26]; + for (var ch = 0; ch < s.Length; ch++) + { + chars[s[ch] - 'a'] = ch; + } + + var result = new List(); + var end = 0; + var size = 0; + for (var i = 0; i < s.Length; i++) + { + end = Math.Max(chars[s[i] - 'a'], end); + size++; + if (i == end) + { + result.Add(size); + size = 0; + } + + } + + return result; + } +} \ No newline at end of file diff --git a/csharp/0778-swim-in-rising-water.cs b/csharp/0778-swim-in-rising-water.cs new file mode 100644 index 000000000..ccc68dbc2 --- /dev/null +++ b/csharp/0778-swim-in-rising-water.cs @@ -0,0 +1,33 @@ +public class Solution { + PriorityQueue<(int, int), int> pq; + private HashSet<(int, int)> visited; + public int SwimInWater(int[][] grid) + { + int n = grid.Length; + pq = new PriorityQueue<(int, int), int>(); + visited = new HashSet<(int, int)>(); + pq.Enqueue((0, 0), grid[0][0]); + while (pq.TryDequeue(out var nei, out var priority)) + { + int r = nei.Item1; + int c = nei.Item2; + visited.Add((r, c)); + + if (r == n - 1 && c == n - 1) return priority; + EnQueue(r + 1, c, grid, priority); + EnQueue(r - 1, c, grid, priority); + EnQueue(r, c + 1, grid, priority); + EnQueue(r, c - 1, grid, priority); + } + return -1; + } + + private void EnQueue(int r, int c, int[][] grid, int preCost) + { + if (r >= grid.Length || r < 0) return; + if (c >= grid.Length || c < 0) return; + if (visited.Contains((r, c))) return; + + pq.Enqueue((r, c), Math.Max(preCost, grid[r][c])); + } +} diff --git a/csharp/0787-cheapest-flights-within-k-stops.cs b/csharp/0787-cheapest-flights-within-k-stops.cs new file mode 100644 index 000000000..9645ce23f --- /dev/null +++ b/csharp/0787-cheapest-flights-within-k-stops.cs @@ -0,0 +1,33 @@ +public class Solution +{ + //Bellman ford algo - T: O(K. E) & S: O(V) + public int FindCheapestPrice(int n, int[][] flights, int src, int dst, int k) + { + var prices = new int[n]; + Array.Fill(prices, int.MaxValue); + prices[src] = 0; + + for (var time = 0; time < k + 1; time++) + { + var tempPrices = prices.Clone() as int[]; + + foreach (var item in flights) + { + var s = item[0]; + var d = item[1]; + var p = item[2]; + if (prices[s] == int.MaxValue) + { + continue; + } + if (prices[s] + p < tempPrices[d]) + { + tempPrices[d] = prices[s] + p; + } + } + prices = tempPrices; + } + + return (prices[dst] == int.MaxValue) ? -1 : prices[dst]; + } +} \ No newline at end of file diff --git a/csharp/0846-hand-of-straights.cs b/csharp/0846-hand-of-straights.cs new file mode 100644 index 000000000..2593c86ea --- /dev/null +++ b/csharp/0846-hand-of-straights.cs @@ -0,0 +1,45 @@ +public class Solution +{ + //Same problem as #1296. Divide Array in Sets of K Consecutive Numbers + //T: O(logN) for min heap, N is for all the items-> so overall O(NlogN) + public bool IsNStraightHand(int[] hand, int groupSize) + { + if (hand.Length % groupSize != 0) + return false; + + + var dictionary = new Dictionary(); + var minHeap = new PriorityQueue(); + foreach (var item in hand) + { + dictionary.TryAdd(item, 0); + dictionary[item]++; + } + + // heapify is linear algorithm + foreach (var key in dictionary.Keys) + minHeap.Enqueue(key, key); + + while (minHeap.Count > 0) + { + var first = minHeap.Peek(); + + for (var i = first; i < first + groupSize; i++) + { + if (!dictionary.ContainsKey(i)) + return false; + + dictionary[i]--; + if (dictionary[i] == 0) + if (i != minHeap.Peek()) + return false; + else + minHeap.Dequeue(); + } + + } + + return true; + + } +} \ No newline at end of file diff --git a/csharp/0853-car-fleet.cs b/csharp/0853-car-fleet.cs new file mode 100644 index 000000000..a819b5beb --- /dev/null +++ b/csharp/0853-car-fleet.cs @@ -0,0 +1,23 @@ +public class Solution +{ + public int CarFleet(int target, int[] position, int[] speed) + { + var pair = new (int, int)[position.Length]; + for (var i = 0; i < position.Length; i++) + { + pair[i] = (position[i], speed[i]); + } + + var stack = new Stack(); + foreach (var (p, s) in pair.OrderByDescending(i => i.Item1)) + { + stack.Push((target - p) / (double)s); + if (stack.Count >= 2 && stack.Peek() <= stack.Skip(1).First()) + { + stack.Pop(); + } + } + + return stack.Count; + } +} \ No newline at end of file diff --git a/csharp/0875-koko-eating-bananas.cs b/csharp/0875-koko-eating-bananas.cs new file mode 100644 index 000000000..e983aae71 --- /dev/null +++ b/csharp/0875-koko-eating-bananas.cs @@ -0,0 +1,30 @@ +public class Solution +{ + public int MinEatingSpeed(int[] piles, int h) + { + int left = 1, right = piles.Max(); + var result = right; + + while (left <= right) + { + var mid = left + (right - left) / 2; + long hours = 0; + foreach (var pile in piles) + { + hours += (int)Math.Ceiling(pile / (double)mid); + } + + if (hours <= h) + { + result = Math.Min(result, mid); + right = mid - 1; + } + else + { + left = mid + 1; + } + } + + return result; + } +} \ No newline at end of file diff --git a/csharp/0876-middle-of-the-linked-list.cs b/csharp/0876-middle-of-the-linked-list.cs new file mode 100644 index 000000000..dfb059e01 --- /dev/null +++ b/csharp/0876-middle-of-the-linked-list.cs @@ -0,0 +1,24 @@ +/** + * Definition for singly-linked list. + * public class ListNode { + * public int val; + * public ListNode next; + * public ListNode(int val=0, ListNode next=null) { + * this.val = val; + * this.next = next; + * } + * } + */ +public class Solution { + public ListNode MiddleNode(ListNode head) { + ListNode slow = head; + ListNode fast = head; + while(fast != null && fast.next != null){ + fast = fast.next.next; + slow = slow.next; + + } + return slow; + + } +} diff --git a/csharp/0909-snakes-and-ladders.cs b/csharp/0909-snakes-and-ladders.cs new file mode 100644 index 000000000..ad990e577 --- /dev/null +++ b/csharp/0909-snakes-and-ladders.cs @@ -0,0 +1,51 @@ +public class Solution { + + public int SnakesAndLadders(int[][] board) + { + int n = board.Length; + bool[,] visits = new bool[n, n]; + Queue q = new Queue(); + q.Enqueue(1); + int res = 0; + + while (q.Count > 0) + { + int cnt = q.Count; + + for (int i = 0; i < cnt; i++) + { + int cur = q.Dequeue(); + if (cur == n * n) + return res; + + for (int j = 1; j <= 6; j++) + { + int newVal = cur + j; + + if (newVal > n * n) + break; + var pos = GetPositionForInt(newVal, n); + if (visits[pos.r, pos.c] == true) + continue; + if (board[pos.r][pos.c] == -1) + q.Enqueue(newVal); + else + q.Enqueue(board[pos.r][pos.c]); + + visits[pos.r, pos.c] = true; + } + } + res++; + } + + return -1; + } + private (int r, int c) GetPositionForInt(int x, int n) + { + int r = n - (x - 1) / n - 1; + int c = (x - 1) % n; + if (r % 2 == n % 2) + return (r, n - c - 1); + return (r, c); + } +} diff --git a/csharp/0912-sort-an-array.cs b/csharp/0912-sort-an-array.cs new file mode 100644 index 000000000..a988cdcf1 --- /dev/null +++ b/csharp/0912-sort-an-array.cs @@ -0,0 +1,63 @@ +public class Solution { + + private int[] aux; // Auxiliary array for merging + + /// + /// Sorts the input array using the classical merge sort algorithm. Uses + /// aux array to save space instead of keeping copies of all array segments + /// + /// The input array of integers to be sorted + /// The sorted array of integers + public int[] SortArray(int[] nums) + { + // Optimization: early return for empty arrays or arrays with one element + if (nums == null || nums.Length <= 1) return nums; + + aux = new int[nums.Length]; + MergeSort(nums, 0, nums.Length - 1); + + return nums; + } + + /// + /// Recursively divides and merges the array segments + /// + /// The input array of integers to be sorted + /// The start index of the segment to be sorted + /// The end index of the segment to be sorted + private void MergeSort(int[] nums, int left, int right) + { + // Base case: return when left has crossed right + if (left >= right) return; + + // Get new mid to halve the array segment + int mid = (left + right) / 2; + + // Sort the first and the second halves + MergeSort(nums, left, mid); + MergeSort(nums, mid + 1, right); + + // Copy to aux[] + for (int k = left; k <= right; k++) + { + aux[k] = nums[k]; + } + + // Merge back to nums[] + // two pointers to help merge + int i = left, j = mid + 1; + for (int k = left; k <= right; k++) + { + // If current left has crossed mid, nums Kth value should be current jth of aux; j++ + // Implies all the left elements have been merged; now we just add the remaining right elements, one by one, in order. + if (i > mid) nums[k] = aux[j++]; + // Else if current j has crossed right, nums kth value should be ith of aux; i++ + // Implies all the right elements have been merged; now we just add the remaining left elements, one by one, in order. + else if (j > right) nums[k] = aux[i++]; + // Else if ith value is greater than jth value in aux, nums kth value should be jth of aux; j++ + else if (aux[i] > aux[j]) nums[k] = aux[j++]; + // Else nums[k] is the left most unsorted value; i++ + else nums[k] = aux[i++]; + } + } +} \ No newline at end of file diff --git a/csharp/0929-unique-email-addresses.cs b/csharp/0929-unique-email-addresses.cs new file mode 100644 index 000000000..270b3fc2f --- /dev/null +++ b/csharp/0929-unique-email-addresses.cs @@ -0,0 +1,9 @@ +public class Solution { + public int NumUniqueEmails(string[] emails) + => emails.Select(x => x.Split('@')). + Select(split => split[0]. + Split('+')[0]. + Replace(".", "") + "@" + split[1]). + Distinct(). + Count(); +} \ No newline at end of file diff --git a/csharp/0958-check-completeness-of-a-binary-tree.cs b/csharp/0958-check-completeness-of-a-binary-tree.cs new file mode 100644 index 000000000..8f449f7e6 --- /dev/null +++ b/csharp/0958-check-completeness-of-a-binary-tree.cs @@ -0,0 +1,44 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution +{ + public bool IsCompleteTree(TreeNode root) + { + Queue queue = new Queue(); + queue.Enqueue(root); + + bool nullFound = false; + + while (queue.Any()) + { + TreeNode node = queue.Dequeue(); + + if (node is null) + { + nullFound = true; + continue; + } + + if (nullFound) + { + return false; + } + + queue.Enqueue(node.left); + queue.Enqueue(node.right); + } + + return true; + } +} \ No newline at end of file diff --git a/csharp/0973-k-closest-points-to-origin.cs b/csharp/0973-k-closest-points-to-origin.cs new file mode 100644 index 000000000..37d659fbb --- /dev/null +++ b/csharp/0973-k-closest-points-to-origin.cs @@ -0,0 +1,21 @@ +public class Solution { + public int[][] KClosest(int[][] points, int k) { + var items = points.Select(point => { + long x = point[0]; + long y = point[1]; + + return (point, x * x + y * y); + }); + + int[][] result = new int[k][]; + // T: O(n) + PriorityQueue queue = new(items); + + // T: O(k log(n)) + for (int i = 0; i < k; i++) { + result[i] = queue.Dequeue(); + } + + return result; + } +} diff --git a/csharp/0981-time-based-key-value-store.cs b/csharp/0981-time-based-key-value-store.cs new file mode 100644 index 000000000..b78183783 --- /dev/null +++ b/csharp/0981-time-based-key-value-store.cs @@ -0,0 +1,52 @@ +public class TimeMap { + + private Dictionary> _dict; + public TimeMap() { + _dict = new Dictionary>(); + } + + public void Set(string key, string value, int timestamp) { + var value1 = new List<(int, string)>(); + if(!_dict.ContainsKey(key)){ + _dict.Add(key, value1); + } + _dict[key].Add((timestamp, value)); + + } + + public string Get(string key, int timestamp) { + if(!_dict.ContainsKey(key)){ + return ""; + } + var value = _dict[key]; + + var left = 0; + var right = value.Count; + var result = ""; + + while(left < right){ + var mid = (left + right)/2; + if(value[mid].timestamp == timestamp){ + result = value[mid].value1; + return result; + } + else if(value[mid].timestamp < timestamp){ + left = mid + 1; + result = value[mid].value1; + } + else{ + right = mid; + } + + } + + return result; + } +} + +/** + * Your TimeMap object will be instantiated and called as such: + * TimeMap obj = new TimeMap(); + * obj.Set(key,value,timestamp); + * string param_2 = obj.Get(key,timestamp); + */ \ No newline at end of file diff --git a/csharp/0994-rotting-oranges.cs b/csharp/0994-rotting-oranges.cs new file mode 100644 index 000000000..73d3d93c0 --- /dev/null +++ b/csharp/0994-rotting-oranges.cs @@ -0,0 +1,51 @@ +public class Solution { + public int OrangesRotting(int[][] grid) + { + if (grid == null || grid[0].Length == 0) + return 0; + + int r = grid.Length, c = grid[0].Length, fresh = 0, time = 0; + + Queue<(int, int)> q = new Queue<(int, int)>(); + + for (int i = 0; i < r; i++) + { + for (int j = 0; j < c; j++) + { + if (grid[i][j] == 1) + fresh++; + else if (grid[i][j] == 2) + { + q.Enqueue((i, j)); + }; + } + } + + if (fresh == 0) + return 0; + + int[,] dir = new int[,] { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } }; + while (q.Any()) + { + time++; + int size = q.Count; + for (int i = 0; i < size; i++) + { + var curr = q.Dequeue(); + for (int j = 0; j < 4; j++) + { + int row = curr.Item1 + dir[j, 0]; + int col = curr.Item2 + dir[j, 1]; + + if (row >= 0 && row < r && col >= 0 && col < c && grid[row][col] == 1) + { + grid[row][col] = 2; + q.Enqueue((row, col)); + fresh--; + } + } + } + } + + return fresh == 0 ? time - 1 : -1; + }} diff --git a/csharp/1046-Last-Stone-Weight.cs b/csharp/1046-Last-Stone-Weight.cs new file mode 100644 index 000000000..9922e3179 --- /dev/null +++ b/csharp/1046-Last-Stone-Weight.cs @@ -0,0 +1,49 @@ +public class Solution +{ + private PriorityQueue pq; + + // T: O(NLogN) + public int LastStoneWeight(int[] stones) + { + pq = new PriorityQueue(new MaxHeapComparer()); + + AddStones(stones); + ComputeLastStoneWeight(); + + return pq.Count == 0 ? 0 : pq.Dequeue(); + } + + + private void AddStones(int[] stones) + { + foreach (var stone in stones) + { + // T: Heapify is O(N) for every enqueued item + pq.Enqueue(stone, stone); + } + } + + // T: O(NLogN), to get max value its O(LogN) and we perform this for N items => O(NLogN) + private void ComputeLastStoneWeight() + { + while (pq.Count > 1) + { + var y = pq.Dequeue(); + var x = pq.Dequeue(); + + if (x != y) + { + var diff = y - x; + pq.Enqueue(diff, diff); + } + } + } + + public class MaxHeapComparer : IComparer + { + public int Compare(int x, int y) + { + return y - x; + } + } +} \ No newline at end of file diff --git a/csharp/1046-last-stone-weight.cs b/csharp/1046-last-stone-weight.cs new file mode 100644 index 000000000..9922e3179 --- /dev/null +++ b/csharp/1046-last-stone-weight.cs @@ -0,0 +1,49 @@ +public class Solution +{ + private PriorityQueue pq; + + // T: O(NLogN) + public int LastStoneWeight(int[] stones) + { + pq = new PriorityQueue(new MaxHeapComparer()); + + AddStones(stones); + ComputeLastStoneWeight(); + + return pq.Count == 0 ? 0 : pq.Dequeue(); + } + + + private void AddStones(int[] stones) + { + foreach (var stone in stones) + { + // T: Heapify is O(N) for every enqueued item + pq.Enqueue(stone, stone); + } + } + + // T: O(NLogN), to get max value its O(LogN) and we perform this for N items => O(NLogN) + private void ComputeLastStoneWeight() + { + while (pq.Count > 1) + { + var y = pq.Dequeue(); + var x = pq.Dequeue(); + + if (x != y) + { + var diff = y - x; + pq.Enqueue(diff, diff); + } + } + } + + public class MaxHeapComparer : IComparer + { + public int Compare(int x, int y) + { + return y - x; + } + } +} \ No newline at end of file diff --git a/csharp/1094-Car-Pooling.cs b/csharp/1094-Car-Pooling.cs new file mode 100644 index 000000000..761921659 --- /dev/null +++ b/csharp/1094-Car-Pooling.cs @@ -0,0 +1,19 @@ +public class Solution { + public bool CarPooling(int[][] trips, int capacity) { + var dictionary = new SortedDictionary(); + + foreach (var trip in trips) + { + if (!dictionary.ContainsKey(trip[1])) dictionary[trip[1]] = 0; + if (!dictionary.ContainsKey(trip[2])) dictionary[trip[2]] = 0; + dictionary[trip[1]] += trip[0]; dictionary[trip[2]] -= trip[0]; + } + int currentPassennger = 0; + foreach (var keyval in dictionary) + { + currentPassennger += keyval.Value; + if (currentPassennger > capacity) return false; + } + return true; + } +} diff --git a/csharp/1094-car-pooling.cs b/csharp/1094-car-pooling.cs new file mode 100644 index 000000000..761921659 --- /dev/null +++ b/csharp/1094-car-pooling.cs @@ -0,0 +1,19 @@ +public class Solution { + public bool CarPooling(int[][] trips, int capacity) { + var dictionary = new SortedDictionary(); + + foreach (var trip in trips) + { + if (!dictionary.ContainsKey(trip[1])) dictionary[trip[1]] = 0; + if (!dictionary.ContainsKey(trip[2])) dictionary[trip[2]] = 0; + dictionary[trip[1]] += trip[0]; dictionary[trip[2]] -= trip[0]; + } + int currentPassennger = 0; + foreach (var keyval in dictionary) + { + currentPassennger += keyval.Value; + if (currentPassennger > capacity) return false; + } + return true; + } +} diff --git a/csharp/1137-n-th-tribonacci-number.cs b/csharp/1137-n-th-tribonacci-number.cs new file mode 100644 index 000000000..79c207e42 --- /dev/null +++ b/csharp/1137-n-th-tribonacci-number.cs @@ -0,0 +1,78 @@ +// iterative with no array +public class Solution +{ + public int Tribonacci(int n) + { + int seq1 = 0; + int seq2 = 0; + int seq3 = 1; + + for (int i = 0; i < n; i++) + { + int seq2_temp = seq2; + seq2 += seq3; + seq3 = seq2_temp; + + int seq1_temp = seq1; + seq1 += seq2; + seq2 = seq1_temp; + } + + return seq1; + } +} + +// bottom up solution +public class Solution +{ + public int Tribonacci(int n) + { + if (n <= 0) + { + return 0; + } + else if (n <= 2) + { + return 1; + } + var dp = new int[n]; + dp[0] = 1; + dp[1] = 1; + dp[2] = 2; + + for (int i = 3; i < n; i++) + { + dp[i] = dp[i - 3] + dp[i - 2] + dp[i - 1]; + } + + return dp[n - 1]; + } +} + +// recursive + memo solution +public class Solution +{ + public int Tribonacci(int n) + { + int[] mem = new int[n]; + return Recursive(n, mem); + } + + private int Recursive(int n, int[] mem) + { + if (n <= 0) + { + return 0; + } + else if (n == 1) + { + return 1; + } + else if (mem[n - 1] == 0) + { + mem[n - 1] = Recursive(n - 1, mem) + Recursive(n - 2, mem) + Recursive(n - 3, mem); + } + + return mem[n - 1]; + } +} \ No newline at end of file diff --git a/csharp/1143-Longest-Common-Subsequence.cs b/csharp/1143-Longest-Common-Subsequence.cs new file mode 100644 index 000000000..cc2057fcb --- /dev/null +++ b/csharp/1143-Longest-Common-Subsequence.cs @@ -0,0 +1,23 @@ +public class Solution { + public int LongestCommonSubsequence(string text1, string text2) { + int[,] dp = new int[text1.Length + 1, text2.Length + 1]; + for (int i = 0; i < text1.Length + 1; i++) + { + for (int j = 0; j < text2.Length + 1; j++) + { + dp[i, j] = 0; + } + } + for (int i = text1.Length - 1; i >= 0; i--) + { + for (int j = text2.Length - 1; j >= 0; j--) + { + if (text1[i] == text2[j]) + dp[i, j] = 1 + dp[i + 1, j + 1]; + else + dp[i, j] = Math.Max(dp[i, j + 1], dp[i + 1, j]); + } + } + return dp[0, 0]; + } +} diff --git a/csharp/1143-longest-common-subsequence.cs b/csharp/1143-longest-common-subsequence.cs new file mode 100644 index 000000000..cc2057fcb --- /dev/null +++ b/csharp/1143-longest-common-subsequence.cs @@ -0,0 +1,23 @@ +public class Solution { + public int LongestCommonSubsequence(string text1, string text2) { + int[,] dp = new int[text1.Length + 1, text2.Length + 1]; + for (int i = 0; i < text1.Length + 1; i++) + { + for (int j = 0; j < text2.Length + 1; j++) + { + dp[i, j] = 0; + } + } + for (int i = text1.Length - 1; i >= 0; i--) + { + for (int j = text2.Length - 1; j >= 0; j--) + { + if (text1[i] == text2[j]) + dp[i, j] = 1 + dp[i + 1, j + 1]; + else + dp[i, j] = Math.Max(dp[i, j + 1], dp[i + 1, j]); + } + } + return dp[0, 0]; + } +} diff --git a/csharp/1189-maximum-number-of-balloons.cs b/csharp/1189-maximum-number-of-balloons.cs new file mode 100644 index 000000000..41fbba7e4 --- /dev/null +++ b/csharp/1189-maximum-number-of-balloons.cs @@ -0,0 +1,19 @@ +public class Solution { + public int MaxNumberOfBalloons(string text) { + var charCounts = text.GroupBy(c => c) + .ToDictionary(g => g.Key, g => g.Count()); + var balloonCounts = "balloon".GroupBy(c => c) + .ToDictionary(g => g.Key, g => g.Count()); + + int result = text.Length; + foreach (var balloonCount in balloonCounts) { + if (charCounts.ContainsKey(balloonCount.Key)) { + result = Math.Min(result, charCounts[balloonCount.Key] / balloonCount.Value); + } else { + result = 0; + break; + } + } + return result; + } +} \ No newline at end of file diff --git a/csharp/1239-maximum-length-of-a-concatenated-string-with-unique-characters.cs b/csharp/1239-maximum-length-of-a-concatenated-string-with-unique-characters.cs new file mode 100644 index 000000000..0453b125b --- /dev/null +++ b/csharp/1239-maximum-length-of-a-concatenated-string-with-unique-characters.cs @@ -0,0 +1,48 @@ +public class Solution +{ + public int MaxLength(IList arr) + { + List masks = GetMasks(arr); + int max_len = 0; + Backtrack(ref max_len, 0, masks, 0); + return max_len; + } + + private void Backtrack(ref int max_len, int current_mask, List masks, int index) + { + for (int i = index; i < masks.Count; i++) + { + if ((current_mask & masks[i]) == 0) + { + int xor = current_mask ^ masks[i]; + max_len = Math.Max(max_len, int.PopCount(xor)); + Backtrack(ref max_len, xor, masks, i + 1); + } + else + { + Backtrack(ref max_len, current_mask, masks, i + 1); + } + } + } + + private List GetMasks(IList arr) + { + List masks = new List(); + for (int i = 0; i < arr.Count; i++) + { + int mask = 0; bool is_unique = true; + for (int j = 0; j < arr[i].Length; j++) + { + int temp = mask ^ (1 << arr[i][j] - 'a'); + if (temp < mask) + { + is_unique = false; + break; + } + mask = temp; + } + if (is_unique) masks.Add(mask); + } + return masks; + } +} \ No newline at end of file diff --git a/csharp/1299-Replace-Elements-With-Greatest-Element-On-Right-Side.cs b/csharp/1299-Replace-Elements-With-Greatest-Element-On-Right-Side.cs new file mode 100644 index 000000000..6908948d6 --- /dev/null +++ b/csharp/1299-Replace-Elements-With-Greatest-Element-On-Right-Side.cs @@ -0,0 +1,11 @@ +public class Solution { + public int[] ReplaceElements(int[] arr) { + int max = -1; + for(int i = arr.Length-1; i >= 0; i--) { + int newMax = Math.Max(arr[i], max); + arr[i] = max; + max = newMax; + } + return arr; + } +} \ No newline at end of file diff --git a/csharp/1299-replace-elements-with-greatest-element-on-right-side.cs b/csharp/1299-replace-elements-with-greatest-element-on-right-side.cs new file mode 100644 index 000000000..6908948d6 --- /dev/null +++ b/csharp/1299-replace-elements-with-greatest-element-on-right-side.cs @@ -0,0 +1,11 @@ +public class Solution { + public int[] ReplaceElements(int[] arr) { + int max = -1; + for(int i = arr.Length-1; i >= 0; i--) { + int newMax = Math.Max(arr[i], max); + arr[i] = max; + max = newMax; + } + return arr; + } +} \ No newline at end of file diff --git a/csharp/1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.cs b/csharp/1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.cs new file mode 100644 index 000000000..91060ef8f --- /dev/null +++ b/csharp/1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.cs @@ -0,0 +1,44 @@ +/* + +Approach: +1. We will initialize i, j, c, res, and sum to 0. +2. We will calculate the sum of the first k elements and check if the average is greater than or equal to the threshold. +3. We will increment the result if the average is greater than or equal to the threshold. +4. We will iterate through the array and calculate the sum of the next k elements. +5. We will check if the average is greater than or equal to the threshold and increment the result accordingly. +6. We will return the result. + +Time Complexity: O(n) +Space Complexity: O(1) + +*/ +public class Solution { + public int NumOfSubarrays(int[] arr, int k, int threshold) { + int i, j, c, res, sum; // Initialize i, j, c, res, and sum to 0. + i = c = sum = res = 0; // Initialize i, c, sum, and res to 0. + j = i + k - 1; // Initialize j to i + k - 1. + + while (c <= j) { // Calculate the sum of the first k elements. + sum = sum + arr[c]; // Add the element at index c to the sum. + c++; // Increment c. + } + + res = (sum / k) >= threshold ? 1 : 0; // Check if the average is greater than or equal to the threshold and increment the result accordingly. + + while (j < arr.Length) { // Iterate through the array and calculate the sum of the next k elements. + sum = sum - arr[i++]; // Subtract the element at index i from the sum and increment i. + j++; // Increment j. + if (j < arr.Length) { // Check if j is less than the length of the array. + sum = sum + arr[j]; // Add the element at index j to the sum. + } + else { + break; // Break the loop if j is equal to or greater than the length of the array. + } + if ((sum / k) >= threshold) { // Check if the average is greater than or equal to the threshold. + res++; // Increment the result. + } + } + + return res; // Return the result. + } +} diff --git a/csharp/1370-increasing-decreasing-string.cs b/csharp/1370-increasing-decreasing-string.cs new file mode 100644 index 000000000..d5151a5ba --- /dev/null +++ b/csharp/1370-increasing-decreasing-string.cs @@ -0,0 +1,31 @@ +public class Solution { + public string SortString(string s) { + var charCounts = new Dictionary(); + + foreach (var ch in s) { + if (charCounts.ContainsKey(ch)) { + charCounts[ch]++; + } else { + charCounts[ch] = 1; + } + } + + var sortedKeys = charCounts.Keys.ToList(); + sortedKeys.Sort(); + + var pattern = string.Concat(sortedKeys) + string.Concat(sortedKeys.AsEnumerable().Reverse()); + var maxItem = charCounts.Values.Max(); + var sample = string.Concat(Enumerable.Repeat(pattern, (maxItem / 2) + 1)); + + var result = ""; + + foreach (var ch in sample) { + if (charCounts.ContainsKey(ch) && charCounts[ch] > 0) { + result += ch; + charCounts[ch]--; + } + } + + return result; + } +} \ No newline at end of file diff --git a/csharp/1383-Maximum-Performance-Of-A-Team.cs b/csharp/1383-Maximum-Performance-Of-A-Team.cs new file mode 100644 index 000000000..9295535d5 --- /dev/null +++ b/csharp/1383-Maximum-Performance-Of-A-Team.cs @@ -0,0 +1,36 @@ +public class Solution { +public class Engineer + { + public int speed; + public int efficiency; + public Engineer(int speed, int efficiency) + { + this.speed = speed; + this.efficiency = efficiency; + } + + } + + public int MaxPerformance(int n, int[] speed, int[] efficiency, int k) + { + List engineers = new(); + for (int i = 0; i < n; i++) + { + engineers.Add(new Engineer(speed[i], efficiency[i])); + } + + engineers = engineers.OrderByDescending(x => x.efficiency).ToList(); + var queue = new PriorityQueue(); + long speedTotal = 0, result = 0; + foreach (var engineer in engineers) + { + if (queue.Count > k - 1) + speedTotal -= queue.Dequeue(); + queue.Enqueue(engineer.speed, engineer.speed); + speedTotal += engineer.speed; + result = Math.Max(result, speedTotal * engineer.efficiency); + } + + return (int)(result % 1000000007); + } +} diff --git a/csharp/1383-maximum-performance-of-a-team.cs b/csharp/1383-maximum-performance-of-a-team.cs new file mode 100644 index 000000000..9295535d5 --- /dev/null +++ b/csharp/1383-maximum-performance-of-a-team.cs @@ -0,0 +1,36 @@ +public class Solution { +public class Engineer + { + public int speed; + public int efficiency; + public Engineer(int speed, int efficiency) + { + this.speed = speed; + this.efficiency = efficiency; + } + + } + + public int MaxPerformance(int n, int[] speed, int[] efficiency, int k) + { + List engineers = new(); + for (int i = 0; i < n; i++) + { + engineers.Add(new Engineer(speed[i], efficiency[i])); + } + + engineers = engineers.OrderByDescending(x => x.efficiency).ToList(); + var queue = new PriorityQueue(); + long speedTotal = 0, result = 0; + foreach (var engineer in engineers) + { + if (queue.Count > k - 1) + speedTotal -= queue.Dequeue(); + queue.Enqueue(engineer.speed, engineer.speed); + speedTotal += engineer.speed; + result = Math.Max(result, speedTotal * engineer.efficiency); + } + + return (int)(result % 1000000007); + } +} diff --git a/csharp/1396-design-underground-system.cs b/csharp/1396-design-underground-system.cs new file mode 100644 index 000000000..d69635922 --- /dev/null +++ b/csharp/1396-design-underground-system.cs @@ -0,0 +1,31 @@ +public class UndergroundSystem +{ + // key: id + // value: station, time + private Dictionary checked_in = new(); + // key: start and end station + // value: total time, count + private Dictionary<(string, string), (int, int)> backlog = new(); + public UndergroundSystem() { } + + public void CheckIn(int id, string stationName, int t) + { + checked_in[id] = (stationName, t); + } + + public void CheckOut(int id, string stationName, int t) + { + (string station, int time) = checked_in[id]; + if (!backlog.TryAdd((station, stationName), (t - time, 1))) + { + (int total_time, int count) = backlog[(station, stationName)]; + backlog[(station, stationName)] = (total_time + (t - time), count + 1); + } + } + + public double GetAverageTime(string startStation, string endStation) + { + (int total_time, int count) = backlog[(startStation, endStation)]; + return (double)total_time / count; + } +} \ No newline at end of file diff --git a/csharp/1423-Maximum-Points-You-Can-Obtain-from-Cards.cs b/csharp/1423-Maximum-Points-You-Can-Obtain-from-Cards.cs new file mode 100644 index 000000000..41ee26354 --- /dev/null +++ b/csharp/1423-Maximum-Points-You-Can-Obtain-from-Cards.cs @@ -0,0 +1,21 @@ +public class Solution { + public int MaxScore(int[] cardPoints, int k) { + if (k > cardPoints.Length) + { + return cardPoints.Sum(); + } + int res = 0, total = 0; + int l = 0, r = cardPoints.Length - k; + for (int i = r; i < cardPoints.Length; i++) + total += cardPoints[i]; + res = total; + while (r < cardPoints.Length) + { + total += cardPoints[l] - cardPoints[r]; + res = Math.Max(res, total); + l++; + r++; + } + return res; + } +} diff --git a/csharp/1423-maximum-points-you-can-obtain-from-cards.cs b/csharp/1423-maximum-points-you-can-obtain-from-cards.cs new file mode 100644 index 000000000..41ee26354 --- /dev/null +++ b/csharp/1423-maximum-points-you-can-obtain-from-cards.cs @@ -0,0 +1,21 @@ +public class Solution { + public int MaxScore(int[] cardPoints, int k) { + if (k > cardPoints.Length) + { + return cardPoints.Sum(); + } + int res = 0, total = 0; + int l = 0, r = cardPoints.Length - k; + for (int i = r; i < cardPoints.Length; i++) + total += cardPoints[i]; + res = total; + while (r < cardPoints.Length) + { + total += cardPoints[l] - cardPoints[r]; + res = Math.Max(res, total); + l++; + r++; + } + return res; + } +} diff --git a/csharp/1448-Count-Good-Nodes-in-Binary-Tree.cs b/csharp/1448-Count-Good-Nodes-in-Binary-Tree.cs new file mode 100644 index 000000000..68cf2e8f0 --- /dev/null +++ b/csharp/1448-Count-Good-Nodes-in-Binary-Tree.cs @@ -0,0 +1,30 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private int goodNodeCount = 0; + public void dfs(TreeNode cur, int pathMax) { + if(cur == null) return; + if(cur.val >= pathMax) { + pathMax = cur.val; + goodNodeCount++; + } + dfs(cur.left, pathMax); + dfs(cur.right, pathMax); + + } + public int GoodNodes(TreeNode root) { + dfs(root, int.MinValue); + return goodNodeCount; + } +} \ No newline at end of file diff --git a/csharp/1448-count-good-nodes-in-binary-tree.cs b/csharp/1448-count-good-nodes-in-binary-tree.cs new file mode 100644 index 000000000..68cf2e8f0 --- /dev/null +++ b/csharp/1448-count-good-nodes-in-binary-tree.cs @@ -0,0 +1,30 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * public int val; + * public TreeNode left; + * public TreeNode right; + * public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +public class Solution { + private int goodNodeCount = 0; + public void dfs(TreeNode cur, int pathMax) { + if(cur == null) return; + if(cur.val >= pathMax) { + pathMax = cur.val; + goodNodeCount++; + } + dfs(cur.left, pathMax); + dfs(cur.right, pathMax); + + } + public int GoodNodes(TreeNode root) { + dfs(root, int.MinValue); + return goodNodeCount; + } +} \ No newline at end of file diff --git a/csharp/1466-Reorder-Routes-to-Make-All-Paths-Lead-To-The-City-Zero.cs b/csharp/1466-Reorder-Routes-to-Make-All-Paths-Lead-To-The-City-Zero.cs new file mode 100644 index 000000000..83ffe75da --- /dev/null +++ b/csharp/1466-Reorder-Routes-to-Make-All-Paths-Lead-To-The-City-Zero.cs @@ -0,0 +1,47 @@ +public class Solution { + public int MinReorder(int n, int[][] connections) + { + if (connections == null || connections?.Length < 2) return 0; + + Dictionary> paths = new Dictionary>(); + List[] graph = new List[n]; + foreach (var connection in connections) + { + if (!paths.ContainsKey(connection[0])) + paths.Add(connection[0], new HashSet()); + + paths[connection[0]].Add(connection[1]); + + if (graph[connection[0]] == null) + graph[connection[0]] = new List(); + graph[connection[0]].Add(connection[1]); + + if (graph[connection[1]] == null) + graph[connection[1]] = new List(); + graph[connection[1]].Add(connection[0]); + } + int cnt = 0; + HashSet visited = new HashSet(); + DFSMinReorder(graph, 0, paths, visited, ref cnt); + return cnt; + } + + private void DFSMinReorder(List[] graph, int u, Dictionary> paths, HashSet visited, ref int cnt) + { + visited.Add(u); + + if (graph[u] != null) + { + foreach (var v in graph[u]) + { + if (!visited.Contains(v)) + { + if (paths.ContainsKey(u) && paths[u].Contains(v)) + cnt++; + + DFSMinReorder(graph, v, paths, visited, ref cnt); + } + } + } + } +} diff --git a/csharp/1466-reorder-routes-to-make-all-paths-lead-to-the-city-zero.cs b/csharp/1466-reorder-routes-to-make-all-paths-lead-to-the-city-zero.cs new file mode 100644 index 000000000..83ffe75da --- /dev/null +++ b/csharp/1466-reorder-routes-to-make-all-paths-lead-to-the-city-zero.cs @@ -0,0 +1,47 @@ +public class Solution { + public int MinReorder(int n, int[][] connections) + { + if (connections == null || connections?.Length < 2) return 0; + + Dictionary> paths = new Dictionary>(); + List[] graph = new List[n]; + foreach (var connection in connections) + { + if (!paths.ContainsKey(connection[0])) + paths.Add(connection[0], new HashSet()); + + paths[connection[0]].Add(connection[1]); + + if (graph[connection[0]] == null) + graph[connection[0]] = new List(); + graph[connection[0]].Add(connection[1]); + + if (graph[connection[1]] == null) + graph[connection[1]] = new List(); + graph[connection[1]].Add(connection[0]); + } + int cnt = 0; + HashSet visited = new HashSet(); + DFSMinReorder(graph, 0, paths, visited, ref cnt); + return cnt; + } + + private void DFSMinReorder(List[] graph, int u, Dictionary> paths, HashSet visited, ref int cnt) + { + visited.Add(u); + + if (graph[u] != null) + { + foreach (var v in graph[u]) + { + if (!visited.Contains(v)) + { + if (paths.ContainsKey(u) && paths[u].Contains(v)) + cnt++; + + DFSMinReorder(graph, v, paths, visited, ref cnt); + } + } + } + } +} diff --git a/csharp/1480-running-sum-of-1d-array.cs b/csharp/1480-running-sum-of-1d-array.cs new file mode 100644 index 000000000..c5aa02bf8 --- /dev/null +++ b/csharp/1480-running-sum-of-1d-array.cs @@ -0,0 +1,11 @@ +public class Solution +{ + public int[] RunningSum(int[] nums) + { + for (int i = 1; i < nums.Length; i++) + { + nums[i] = nums[i - 1] + nums[i]; + } + return nums; + } +} diff --git a/csharp/1584-Min-Cost-to-Connect-All-Points.cs b/csharp/1584-Min-Cost-to-Connect-All-Points.cs new file mode 100644 index 000000000..e3480dc8b --- /dev/null +++ b/csharp/1584-Min-Cost-to-Connect-All-Points.cs @@ -0,0 +1,60 @@ +public class Solution +{ + //T: O(N^2 LogN) + public int MinCostConnectPoints(int[][] points) + { + //creating the ajacency list. + var N = points.Length; + var dictionary = new Dictionary>>(); //Cost and Node + + if (N == 1) + return 0; + + for (var i = 0; i < N; i++) + { + dictionary.Add(i, new List>()); + } + + for (var i = 0; i < N; i++) + { + var x1 = points[i][0]; + var y1 = points[i][1]; + + for (var j = i + 1; j < N; j++) + { + var x2 = points[j][0]; + var y2 = points[j][1]; + + var dist = Math.Abs(x2 - x1) + Math.Abs(y1 - y2); + + dictionary[j].Add(new Tuple(dist, i)); + dictionary[i].Add(new Tuple(dist, j)); + + } + } + + var res = 0; + //Prim's + var visited = new HashSet(); + var minHeap = new PriorityQueue<(int, int), int>(); // Cost and Node + minHeap.Enqueue((0, 0), 0); + + while (minHeap.Count > 0) + { + var (cost, point) = minHeap.Dequeue(); + if (visited.Contains(point)) + continue; + res += cost; + visited.Add(point); + var adj = dictionary[point]; + + for (var i = 0; i < adj.Count; i++) + { + if (!visited.Contains(adj[i].Item2)) + minHeap.Enqueue((adj[i].Item1, adj[i].Item2), adj[i].Item1); + } + } + + return res; + } +} \ No newline at end of file diff --git a/csharp/1584-min-cost-to-connect-all-points.cs b/csharp/1584-min-cost-to-connect-all-points.cs new file mode 100644 index 000000000..e3480dc8b --- /dev/null +++ b/csharp/1584-min-cost-to-connect-all-points.cs @@ -0,0 +1,60 @@ +public class Solution +{ + //T: O(N^2 LogN) + public int MinCostConnectPoints(int[][] points) + { + //creating the ajacency list. + var N = points.Length; + var dictionary = new Dictionary>>(); //Cost and Node + + if (N == 1) + return 0; + + for (var i = 0; i < N; i++) + { + dictionary.Add(i, new List>()); + } + + for (var i = 0; i < N; i++) + { + var x1 = points[i][0]; + var y1 = points[i][1]; + + for (var j = i + 1; j < N; j++) + { + var x2 = points[j][0]; + var y2 = points[j][1]; + + var dist = Math.Abs(x2 - x1) + Math.Abs(y1 - y2); + + dictionary[j].Add(new Tuple(dist, i)); + dictionary[i].Add(new Tuple(dist, j)); + + } + } + + var res = 0; + //Prim's + var visited = new HashSet(); + var minHeap = new PriorityQueue<(int, int), int>(); // Cost and Node + minHeap.Enqueue((0, 0), 0); + + while (minHeap.Count > 0) + { + var (cost, point) = minHeap.Dequeue(); + if (visited.Contains(point)) + continue; + res += cost; + visited.Add(point); + var adj = dictionary[point]; + + for (var i = 0; i < adj.Count; i++) + { + if (!visited.Contains(adj[i].Item2)) + minHeap.Enqueue((adj[i].Item1, adj[i].Item2), adj[i].Item1); + } + } + + return res; + } +} \ No newline at end of file diff --git a/csharp/1603-design-parking-system.cs b/csharp/1603-design-parking-system.cs new file mode 100644 index 000000000..4af549c2f --- /dev/null +++ b/csharp/1603-design-parking-system.cs @@ -0,0 +1,16 @@ +public class ParkingSystem +{ + private int[] spaces; + + public ParkingSystem(int big, int medium, int small) + { + spaces = [big, medium, small]; + } + + public bool AddCar(int carType) + { + if (spaces[carType - 1] <= 0) return false; + spaces[carType - 1] -= 1; + return true; + } +} \ No newline at end of file diff --git a/csharp/1768-merge-strings-alternately.cs b/csharp/1768-merge-strings-alternately.cs new file mode 100644 index 000000000..75cc8ff89 --- /dev/null +++ b/csharp/1768-merge-strings-alternately.cs @@ -0,0 +1,18 @@ +public class Solution +{ + public string MergeAlternately(string word1, string word2) + { + var result = string.Empty; + var firstPointer = 0; + var secondPointer = 0; + while (firstPointer < word1.Length || secondPointer < word2.Length) + { + if (firstPointer < word1.Length) + result += word1[firstPointer++]; + + if (secondPointer < word2.Length) + result += word2[secondPointer++]; + } + return result; + } +} \ No newline at end of file diff --git a/csharp/1838-Frequency-Of-The-Most-Frequent-Element.cs b/csharp/1838-Frequency-Of-The-Most-Frequent-Element.cs new file mode 100644 index 000000000..1693b19a5 --- /dev/null +++ b/csharp/1838-Frequency-Of-The-Most-Frequent-Element.cs @@ -0,0 +1,22 @@ +public class Solution { + public int MaxFrequency(int[] nums, int k) { + Array.Sort(nums); + + int left = 0, right = 0; + var max = 0; + long totalSum = 0; + + for(right = 0; right < nums.Length; right++) { + totalSum += nums[right]; + + while(nums[right] * (right - left + 1) > totalSum + k) { + totalSum -= nums[left]; + left++; + } + + max = Math.Max((right - left + 1), max); + } + + return max; + } +} \ No newline at end of file diff --git a/csharp/1838-frequency-of-the-most-frequent-element.cs b/csharp/1838-frequency-of-the-most-frequent-element.cs new file mode 100644 index 000000000..1693b19a5 --- /dev/null +++ b/csharp/1838-frequency-of-the-most-frequent-element.cs @@ -0,0 +1,22 @@ +public class Solution { + public int MaxFrequency(int[] nums, int k) { + Array.Sort(nums); + + int left = 0, right = 0; + var max = 0; + long totalSum = 0; + + for(right = 0; right < nums.Length; right++) { + totalSum += nums[right]; + + while(nums[right] * (right - left + 1) > totalSum + k) { + totalSum -= nums[left]; + left++; + } + + max = Math.Max((right - left + 1), max); + } + + return max; + } +} \ No newline at end of file diff --git a/csharp/1851-Minimum-Interval-to-Include-Each-Query.cs b/csharp/1851-Minimum-Interval-to-Include-Each-Query.cs new file mode 100644 index 000000000..429c40574 --- /dev/null +++ b/csharp/1851-Minimum-Interval-to-Include-Each-Query.cs @@ -0,0 +1,44 @@ +public class Solution +{ + public int[] MinInterval(int[][] intervals, int[] queries) + { + + var q = queries.Length; + var indexDict = new int[q][]; + var index = 0; + foreach (var query in queries) + { + indexDict[index] = new int[2] { query, index }; + index++; + } + Array.Sort(indexDict, (a, b) => a[0] - b[0]); + Array.Sort(intervals, (a, b) => a[0] - b[0]); + + var pq = new PriorityQueue(); + var result = new int[queries.Length]; + + index = 0; + foreach (var query in indexDict) + { + var resultIndex = query; + var calResult = -1; + + while (index < intervals.Length && intervals[index][0] <= resultIndex[0]) + { + var curr = intervals[index]; + pq.Enqueue(new int[2] { curr[1] - curr[0] + 1, curr[1] }, curr[1] - curr[0] + 1); + index++; + } + + while (pq.Count > 0 && pq.Peek()[1] < resultIndex[0]) + { + pq.Dequeue(); + } + calResult = pq.Count > 0 ? pq.Peek()[0] : -1; + result[resultIndex[1]] = calResult; + } + + return result; + + } +} \ No newline at end of file diff --git a/csharp/1851-minimum-interval-to-include-each-query.cs b/csharp/1851-minimum-interval-to-include-each-query.cs new file mode 100644 index 000000000..429c40574 --- /dev/null +++ b/csharp/1851-minimum-interval-to-include-each-query.cs @@ -0,0 +1,44 @@ +public class Solution +{ + public int[] MinInterval(int[][] intervals, int[] queries) + { + + var q = queries.Length; + var indexDict = new int[q][]; + var index = 0; + foreach (var query in queries) + { + indexDict[index] = new int[2] { query, index }; + index++; + } + Array.Sort(indexDict, (a, b) => a[0] - b[0]); + Array.Sort(intervals, (a, b) => a[0] - b[0]); + + var pq = new PriorityQueue(); + var result = new int[queries.Length]; + + index = 0; + foreach (var query in indexDict) + { + var resultIndex = query; + var calResult = -1; + + while (index < intervals.Length && intervals[index][0] <= resultIndex[0]) + { + var curr = intervals[index]; + pq.Enqueue(new int[2] { curr[1] - curr[0] + 1, curr[1] }, curr[1] - curr[0] + 1); + index++; + } + + while (pq.Count > 0 && pq.Peek()[1] < resultIndex[0]) + { + pq.Dequeue(); + } + calResult = pq.Count > 0 ? pq.Peek()[0] : -1; + result[resultIndex[1]] = calResult; + } + + return result; + + } +} \ No newline at end of file diff --git a/csharp/1899-Merge-Triplets-to-Form-Target-Triplet.cs b/csharp/1899-Merge-Triplets-to-Form-Target-Triplet.cs new file mode 100644 index 000000000..aeb898826 --- /dev/null +++ b/csharp/1899-Merge-Triplets-to-Form-Target-Triplet.cs @@ -0,0 +1,23 @@ +public class Solution +{ + //T: O(N) + public bool MergeTriplets(int[][] triplets, int[] target) + { + var hashSet = new HashSet<(int, int)>(); + + foreach (var t in triplets) + { + if (t[0] > target[0] || t[1] > target[1] || t[2] > target[2]) + continue; + + for (var i = 0; i < t.Length; i++) + { + if (t[i] == target[i]) + hashSet.Add((i, t[i])); + } + } + + return hashSet.Count == 3; + } + +} \ No newline at end of file diff --git a/csharp/1899-merge-triplets-to-form-target-triplet.cs b/csharp/1899-merge-triplets-to-form-target-triplet.cs new file mode 100644 index 000000000..aeb898826 --- /dev/null +++ b/csharp/1899-merge-triplets-to-form-target-triplet.cs @@ -0,0 +1,23 @@ +public class Solution +{ + //T: O(N) + public bool MergeTriplets(int[][] triplets, int[] target) + { + var hashSet = new HashSet<(int, int)>(); + + foreach (var t in triplets) + { + if (t[0] > target[0] || t[1] > target[1] || t[2] > target[2]) + continue; + + for (var i = 0; i < t.Length; i++) + { + if (t[i] == target[i]) + hashSet.Add((i, t[i])); + } + } + + return hashSet.Count == 3; + } + +} \ No newline at end of file diff --git a/csharp/1905-Count-Sub-Islands.cs b/csharp/1905-Count-Sub-Islands.cs new file mode 100644 index 000000000..3e195199c --- /dev/null +++ b/csharp/1905-Count-Sub-Islands.cs @@ -0,0 +1,30 @@ +public class Solution { + public int CountSubIslands(int[][] grid1, int[][] grid2) + { + int r = grid1.Length, c = grid1[0].Length, subIslands = 0; + for (int i = 0; i < r; i++) + { + for (int j = 0; j < c; j++) + { + if (grid2[i][j] == 1 && DfsSubIsLand(grid1, grid2, i, j)) + { + subIslands++; + } + } + } + return subIslands; + } + + private bool DfsSubIsLand(int[][] grid1, int[][] grid2, int r, int c) + { + if (r < 0 || c < 0 || r >= grid2.Length || c >= grid2[r].Length || grid2[r][c] == 0) { return true; } + + grid2[r][c] = 0; + bool stillASubIsland = true; + stillASubIsland &= DfsSubIsLand(grid1, grid2, r-1, c); + stillASubIsland &= DfsSubIsLand(grid1, grid2, r, c+1); + stillASubIsland &= DfsSubIsLand(grid1, grid2, r+1, c); + stillASubIsland &= DfsSubIsLand(grid1, grid2, r, c-1); + return stillASubIsland && grid1[r][c] == 1; + } +} diff --git a/csharp/1905-count-sub-islands.cs b/csharp/1905-count-sub-islands.cs new file mode 100644 index 000000000..3e195199c --- /dev/null +++ b/csharp/1905-count-sub-islands.cs @@ -0,0 +1,30 @@ +public class Solution { + public int CountSubIslands(int[][] grid1, int[][] grid2) + { + int r = grid1.Length, c = grid1[0].Length, subIslands = 0; + for (int i = 0; i < r; i++) + { + for (int j = 0; j < c; j++) + { + if (grid2[i][j] == 1 && DfsSubIsLand(grid1, grid2, i, j)) + { + subIslands++; + } + } + } + return subIslands; + } + + private bool DfsSubIsLand(int[][] grid1, int[][] grid2, int r, int c) + { + if (r < 0 || c < 0 || r >= grid2.Length || c >= grid2[r].Length || grid2[r][c] == 0) { return true; } + + grid2[r][c] = 0; + bool stillASubIsland = true; + stillASubIsland &= DfsSubIsLand(grid1, grid2, r-1, c); + stillASubIsland &= DfsSubIsLand(grid1, grid2, r, c+1); + stillASubIsland &= DfsSubIsLand(grid1, grid2, r+1, c); + stillASubIsland &= DfsSubIsLand(grid1, grid2, r, c-1); + return stillASubIsland && grid1[r][c] == 1; + } +} diff --git a/csharp/1929-concatenation-of-array.cs b/csharp/1929-concatenation-of-array.cs new file mode 100644 index 000000000..6908b9ef4 --- /dev/null +++ b/csharp/1929-concatenation-of-array.cs @@ -0,0 +1,13 @@ +public class Solution { + public int[] GetConcatenation(int[] nums) { + int n = nums.Length; + int[] ans = new int[2 * n]; + + for (int i = 0; i < n; i++) { + ans[i] = nums[i]; + ans[i + n] = nums[i]; + } + + return ans; + } +} \ No newline at end of file diff --git a/csharp/1963-minimum-number-of-swaps-to-make-the-string-balanced.cs b/csharp/1963-minimum-number-of-swaps-to-make-the-string-balanced.cs new file mode 100644 index 000000000..ac4dc3c92 --- /dev/null +++ b/csharp/1963-minimum-number-of-swaps-to-make-the-string-balanced.cs @@ -0,0 +1,28 @@ +public class Solution +{ + public int MinSwaps(string s) + { + int open_braces = 0, swaps = 0; + foreach (var ch in s) + { + if (ch == '[') + { + open_braces++; + } + else + { + if (open_braces <= 0) + { + open_braces++; + swaps++; + } + else + { + open_braces--; + } + } + } + + return swaps; + } +} \ No newline at end of file diff --git a/csharp/1984-minimum-difference-between-highest-and-lowest-of-k-scores.cs b/csharp/1984-minimum-difference-between-highest-and-lowest-of-k-scores.cs new file mode 100644 index 000000000..b7446068d --- /dev/null +++ b/csharp/1984-minimum-difference-between-highest-and-lowest-of-k-scores.cs @@ -0,0 +1,13 @@ +public class Solution { + public int MinimumDifference(int[] nums, int k) { + int min = Int32.MaxValue; + int l = 0, r = k - 1; + Array.Sort(nums); + while (r < nums.Length) { + min = Math.Min(min, nums[r] - nums[l]); + l++; + r++; + } + return min; + } +} diff --git a/csharp/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.cs b/csharp/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.cs new file mode 100644 index 000000000..12dea07e2 --- /dev/null +++ b/csharp/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.cs @@ -0,0 +1,50 @@ +public class Solution { + public int MaxProduct(string s) { + if(s == null || s.Length < 2) + return 0; + if(s.Length == 2) + return 1; + + int n = s.Length; + int total = 1 << n; + + List<(int, int)> possible = new List<(int, int)>(); + + for(int i = 0; i < total; i++) { + StringBuilder sb = new StringBuilder(); + + for(int j = 0; j < n; j++) { + if((i & (1 << j)) != 0) { + sb.Append(s[j]); + } + } + + if(IsPalindrome(sb.ToString())) { + possible.Add((i, sb.Length)); + } + } + + int ans = 0; + for(int i = 0; i < possible.Count; i++) { + int bitmask = possible[i].Item1; + int count = possible[i].Item2; + for(int j = i + 1; j < possible.Count; j++) { + int bitmask2 = possible[j].Item1; + int count2 = possible[j].Item2; + if((bitmask & bitmask2) == 0) + ans = Math.Max(ans, count * count2); + } + } + return ans; + } + + private bool IsPalindrome(string s){ + int i = 0; + int j = s.Length - 1; + while(i < j) { + if(s[i++] != s[j--]) + return false; + } + return true; + } +} \ No newline at end of file diff --git a/csharp/2013-Detect-Squares.cs b/csharp/2013-Detect-Squares.cs new file mode 100644 index 000000000..a66b48ff5 --- /dev/null +++ b/csharp/2013-Detect-Squares.cs @@ -0,0 +1,35 @@ +public class DetectSquares { + + private Dictionary<(int x, int y), int> _pointsCounter = new(); + private List<(int x, int y)> _points = new(); + + public DetectSquares() { } + + public void Add(int[] point) { + var p = (point[0], point[1]); + _points.Add(p); + _pointsCounter[p] = 1 + _pointsCounter.GetValueOrDefault(p, 0); + } + + public int Count(int[] point) { + int px = point[0], py = point[1]; + int result = 0; + + foreach (var (x, y) in _points) { + + if (Math.Abs(px - x) != Math.Abs(py - y) + || x == px || y == py) { + continue; + } + result += _pointsCounter.GetValueOrDefault((px, y), 0) * _pointsCounter.GetValueOrDefault((x, py), 0); + } + return result; + } +} + +/** + * Your DetectSquares object will be instantiated and called as such: + * DetectSquares obj = new DetectSquares(); + * obj.Add(point); + * int param_2 = obj.Count(point); + */ \ No newline at end of file diff --git a/csharp/2013-detect-squares.cs b/csharp/2013-detect-squares.cs new file mode 100644 index 000000000..a66b48ff5 --- /dev/null +++ b/csharp/2013-detect-squares.cs @@ -0,0 +1,35 @@ +public class DetectSquares { + + private Dictionary<(int x, int y), int> _pointsCounter = new(); + private List<(int x, int y)> _points = new(); + + public DetectSquares() { } + + public void Add(int[] point) { + var p = (point[0], point[1]); + _points.Add(p); + _pointsCounter[p] = 1 + _pointsCounter.GetValueOrDefault(p, 0); + } + + public int Count(int[] point) { + int px = point[0], py = point[1]; + int result = 0; + + foreach (var (x, y) in _points) { + + if (Math.Abs(px - x) != Math.Abs(py - y) + || x == px || y == py) { + continue; + } + result += _pointsCounter.GetValueOrDefault((px, y), 0) * _pointsCounter.GetValueOrDefault((x, py), 0); + } + return result; + } +} + +/** + * Your DetectSquares object will be instantiated and called as such: + * DetectSquares obj = new DetectSquares(); + * obj.Add(point); + * int param_2 = obj.Count(point); + */ \ No newline at end of file diff --git a/csharp/2215-find-the-difference-of-two-arrays.cs b/csharp/2215-find-the-difference-of-two-arrays.cs new file mode 100644 index 000000000..6db6978fe --- /dev/null +++ b/csharp/2215-find-the-difference-of-two-arrays.cs @@ -0,0 +1,26 @@ +public class Solution +{ + public IList> FindDifference(int[] nums1, int[] nums2) + { + HashSet set1 = new HashSet(nums1); + HashSet set2 = new HashSet(nums2); + + IList> res = new List>() { new List(), new List() }; + + foreach (var el in set1) + { + if (set2.Contains(el)) continue; + + res[0].Add(el); + } + + foreach (var el in set2) + { + if (set1.Contains(el)) continue; + + res[1].Add(el); + } + + return res; + } +} \ No newline at end of file diff --git a/csharp/2390-removing-stars-from-a-string.cs b/csharp/2390-removing-stars-from-a-string.cs new file mode 100644 index 000000000..f3204ca16 --- /dev/null +++ b/csharp/2390-removing-stars-from-a-string.cs @@ -0,0 +1,21 @@ +public class Solution +{ + public string RemoveStars(string s) + { + var output = new StringBuilder(s.Length); + + foreach (var ch in s) + { + if (ch == '*') + { + output.Length--; + } + else + { + output.Append(ch); + } + } + + return output.ToString(); + } +} \ No newline at end of file diff --git a/dart/0001-two-sum.dart b/dart/0001-two-sum.dart new file mode 100644 index 000000000..084c9c0b1 --- /dev/null +++ b/dart/0001-two-sum.dart @@ -0,0 +1,14 @@ +// Time Complexity: O(n) +// Space Complexity: O(n) + +class Solution { + List twoSum(List nums, int target) { + var map = Map(); + for (int i = 0; i < nums.length; i++) { + var x = target - nums[i]; + if (map.containsKey(x)) return [map[x]!, i]; + map[nums[i]] = i; + } + throw ""; + } +} diff --git a/dart/0002-add-two-numbers.dart b/dart/0002-add-two-numbers.dart new file mode 100644 index 000000000..53a0d80fd --- /dev/null +++ b/dart/0002-add-two-numbers.dart @@ -0,0 +1,41 @@ +/** + * Definition for singly-linked list. + * class ListNode { + * int val; + * ListNode? next; + * ListNode([this.val = 0, this.next]); + * } + */ +class Solution { + ListNode? addTwoNumbers(ListNode? l1, ListNode? l2) { + ListNode ?listnode; + int carry = 0; + while (l1 != null || l2 != null) { + int val = (l1?.val ?? 0) + (l2?.val ?? 0) + carry; + l1 = l1?.next; + l2 = l2?.next; + if (val > 9){ + val = val - 10; + carry = 1; + } else { + carry = 0; + } + + if (listnode != null) + listnode = ListNode(val, listnode); + + else + listnode = ListNode(val); + } + if (carry != 0){ + listnode = ListNode(carry, listnode); + } + var list; + while (listnode != null) { + list = ListNode(listnode.val, list);; + listnode = listnode?.next; + } + return list; + } + +} diff --git a/dart/0003-longest-substring-without-repeating-characters.dart b/dart/0003-longest-substring-without-repeating-characters.dart new file mode 100644 index 000000000..3e3957b10 --- /dev/null +++ b/dart/0003-longest-substring-without-repeating-characters.dart @@ -0,0 +1,27 @@ +class Solution { + int lengthOfLongestSubstring(String s) { + int max = 0; + Map map = {}; + + List list = s.split(''); + List result = []; + + for (int i = 0; i < list.length; i++) { + if (!result.contains(list[i])) { + result.add(list[i]); + } else { + map.putIfAbsent(result.join(), () => result.length); + while (result.contains(list[i])) { + result.removeAt(0); + } + result.add(list[i]); + } + } + + map.putIfAbsent(result.join(), () => result.length); + map.forEach((key, value) { + max = max > value ? max : value; + }); + return max; + } +} \ No newline at end of file diff --git a/dart/0004-median-of-two-sorted-arrays.dart b/dart/0004-median-of-two-sorted-arrays.dart new file mode 100644 index 000000000..16acbe71a --- /dev/null +++ b/dart/0004-median-of-two-sorted-arrays.dart @@ -0,0 +1,13 @@ +class Solution { + double findMedianSortedArrays(List nums1, List nums2) { + Listlist = nums1 + nums2; + + list.sort(); + int index = list.length ~/ 2; + bool isodd = (list.length & 0x01) != 0; + if (!isodd) { + return (list[index] + list[index - 1]) / 2; + } + return double.parse(list[index].toString()); + } +} diff --git a/dart/0011-container-with-most-water.dart b/dart/0011-container-with-most-water.dart new file mode 100644 index 000000000..8cca6296b --- /dev/null +++ b/dart/0011-container-with-most-water.dart @@ -0,0 +1,24 @@ +// Time Complexity: O(n) +// Space Complexity: O(1) + +import 'dart:math'; + +class Solution { + int maxArea(List height) { + var maxWater = 0, l = 0, r = height.length - 1; + while (l < r) { + var dist = r - l; + var minHeight = min(height[l], height[r]); + + if ((minHeight * dist) > maxWater) { + maxWater = minHeight * dist; + } + + if (height[l] < height[r]) + l++; + else + r--; + } + return maxWater; + } +} diff --git a/dart/0014-longest-common-prefix.dart b/dart/0014-longest-common-prefix.dart new file mode 100644 index 000000000..cb67a5e19 --- /dev/null +++ b/dart/0014-longest-common-prefix.dart @@ -0,0 +1,14 @@ +class Solution { + String longestCommonPrefix(List strs) { + String res = ""; + for (int i=0; i= s.length || strs[0][i] != s[i]){ + return res; + } + } + res += strs[0][i]; + } + return res; + } +} \ No newline at end of file diff --git a/dart/0015-3sum.dart b/dart/0015-3sum.dart new file mode 100644 index 000000000..ac3196c38 --- /dev/null +++ b/dart/0015-3sum.dart @@ -0,0 +1,31 @@ +class Solution { + List> threeSum(List nums) { + List> result = []; + nums.sort(); + + for (var i = 0; i < nums.length; i++) { + var a = nums[i]; + if (i > 0 && a == nums[i - 1]) continue; + + var l = i + 1; + var r = nums.length - 1; + while (l < r) { + var sum = a + nums[l] + nums[r]; + if (sum < 0) { + l++; + } else if (sum > 0) { + r--; + } else { + result.add([a, nums[l], nums[r]]); + l++; + r--; + while (l < r && nums[l] == nums[l - 1]) { + l++; + } + } + } + } + + return result; + } +} diff --git a/dart/0020-valid-parentheses.dart b/dart/0020-valid-parentheses.dart new file mode 100644 index 000000000..1463c262b --- /dev/null +++ b/dart/0020-valid-parentheses.dart @@ -0,0 +1,36 @@ +class Solution { + bool isValid(String s) { + var res = []; + for (int i=0; i nums, int val) { + int i = 0; + int end = nums.length - 1; + while (i <= end) { + if (nums[i] == val) { + int tmp = nums[end]; + nums[end] = nums[i]; + nums[i] = tmp; + end -= 1; + } else { + i += 1; + } + } + return i; + } +} \ No newline at end of file diff --git a/dart/0028-find-the-index-of-the-first-occurrence-in-a-string.dart b/dart/0028-find-the-index-of-the-first-occurrence-in-a-string.dart new file mode 100644 index 000000000..e304f9923 --- /dev/null +++ b/dart/0028-find-the-index-of-the-first-occurrence-in-a-string.dart @@ -0,0 +1,6 @@ +class Solution { + int strStr(String haystack, String needle) { + var result = haystack.replaceAll(needle, '0').indexOf('0'); + return result; + } +} \ No newline at end of file diff --git a/dart/0036-valid-sudoku.dart b/dart/0036-valid-sudoku.dart new file mode 100644 index 000000000..891f63da0 --- /dev/null +++ b/dart/0036-valid-sudoku.dart @@ -0,0 +1,31 @@ +// As the board size is fixed +// it will result in the following +// Time Complexity: O(1) +// Space Complexity: O(1) + +class Solution { + bool isValidSudoku(List> board) { + var rows = List.filled(9, 0); + var cols = List.filled(9, 0); + var grids = List.filled(9, 0); + + for (int r = 0; r < 9; r++) { + for (int c = 0; c < 9; c++) { + if (board[r][c] == ".") continue; + + var idx = int.parse(board[r][c]) - 1; + + if (rows[r] & 1 << idx != 0) return false; + rows[r] |= 1 << idx; + + if (cols[c] & 1 << idx != 0) return false; + cols[c] |= 1 << idx; + + if (grids[r ~/ 3 * 3 + c ~/ 3] & 1 << idx != 0) return false; + grids[r ~/ 3 * 3 + c ~/ 3] |= 1 << idx; + } + } + + return true; + } +} diff --git a/dart/0042-trapping-rain-water.dart b/dart/0042-trapping-rain-water.dart new file mode 100644 index 000000000..efc5c2143 --- /dev/null +++ b/dart/0042-trapping-rain-water.dart @@ -0,0 +1,24 @@ +class Solution { + int trap(List height) { + if (height.isEmpty) return 0; + + int l = 0; + int r = height.length - 1; + int leftMax = height[l]; + int rightMax = height[r]; + int res = 0; + + while (l < r) { + if (leftMax < rightMax) { + l++; + leftMax = leftMax > height[l] ? leftMax : height[l]; + res += leftMax - height[l]; + } else { + r--; + rightMax = rightMax > height[r] ? rightMax : height[r]; + res += rightMax - height[r]; + } + } + return res; + } +} diff --git a/dart/0049-group-anagrams.dart b/dart/0049-group-anagrams.dart new file mode 100644 index 000000000..90773c3d6 --- /dev/null +++ b/dart/0049-group-anagrams.dart @@ -0,0 +1,23 @@ +// Time Complexity: O(n * m) +// Space Complexity: O(n * m) + +class Solution { + List> groupAnagrams(List strs) { + var map = Map>(); + + for (var str in strs) { + var count = List.filled(26, 0); + for (int i = 0; i < str.length; i++) { + count[str[i].codeUnitAt(0) - 'a'.codeUnitAt(0)]++; + } + var key = count.join("#"); + if (map.containsKey(key)) { + map[key]!.add(str); + continue; + } + map[key] = [str]; + } + + return List.from(map.values); + } +} diff --git a/dart/0058-length-of-last-word.dart b/dart/0058-length-of-last-word.dart new file mode 100644 index 000000000..59d6b53b1 --- /dev/null +++ b/dart/0058-length-of-last-word.dart @@ -0,0 +1,6 @@ +class Solution { + int lengthOfLastWord(String s) { + final words = s.trim().split(" "); + return words[words.length - 1].length; + } +} \ No newline at end of file diff --git a/dart/0070-climbing-stairs.dart b/dart/0070-climbing-stairs.dart new file mode 100644 index 000000000..b3299dde7 --- /dev/null +++ b/dart/0070-climbing-stairs.dart @@ -0,0 +1,13 @@ +class Solution { + int climbStairs(int n) { + int first=0; + int second=1; + int res=1; + for(int i=0;i> generate(int numRows) { + if (numRows == 1) { + return [[1]]; + } + + List> res = [[1], [1, 1]]; + for (int i=2; i tmp = []; + for (int j=0; j prices) { + var maxProfit = 0; + var buyDay = 0; + + for (var sellDay = 1; sellDay < prices.length; sellDay++) { + if (prices[buyDay] > prices[sellDay]) { + buyDay = sellDay; + } + var profit = prices[sellDay] - prices[buyDay]; + maxProfit = max(maxProfit, profit); + } + + return maxProfit; + } +} diff --git a/dart/0125-valid-palindrome.dart b/dart/0125-valid-palindrome.dart new file mode 100644 index 000000000..90f50f0d8 --- /dev/null +++ b/dart/0125-valid-palindrome.dart @@ -0,0 +1,32 @@ +// Time Complexity: O(n) +// Space Complexity: O(1) + +class Solution { + static bool isAlphaNum(String s) { + if ((s.codeUnits[0] >= 'a'.codeUnitAt(0) && + s.codeUnits[0] <= 'z'.codeUnitAt(0)) || + (s.codeUnits[0] >= 'A'.codeUnitAt(0) && + s.codeUnits[0] <= 'Z'.codeUnitAt(0)) || + (s.codeUnits[0] >= '0'.codeUnitAt(0) && + s.codeUnits[0] <= '9'.codeUnitAt(0))) { + return true; + } + return false; + } + + bool isPalindrome(String s) { + var left = 0, right = s.length - 1; + + while (left < right) { + while (!Solution.isAlphaNum(s[left]) && left < right) left++; + while (!Solution.isAlphaNum(s[right]) && left < right) right--; + + if (s[left].toLowerCase() != s[right].toLowerCase()) return false; + + left++; + right--; + } + + return true; + } +} diff --git a/dart/0130-surrounded-regions.dart b/dart/0130-surrounded-regions.dart new file mode 100644 index 000000000..f16f439a9 --- /dev/null +++ b/dart/0130-surrounded-regions.dart @@ -0,0 +1,51 @@ +class Solution { + late List> board; + late int rows; + late int columns; + + bool isInBound(int row, int column) { + return 0 <= row && row < rows && 0 <= column && column < columns; + } + + void dfs(int row, int column) { + if (!isInBound(row, column) || board[row][column] != 'O') { + return; + } + + board[row][column] = 'T'; + + dfs(row + 1, column); + dfs(row, column + 1); + dfs(row - 1, column); + dfs(row, column - 1); + } + + void solve(List> board) { + this.board = board; + rows = board.length; + columns = board[0].length; + + // Traverse the first and last columns + for (int column = 0; column < columns; column++) { + dfs(0, column); + dfs(rows - 1, column); + } + + // Traverse the first and last rows + for (int row = 0; row < rows; row++) { + dfs(row, 0); + dfs(row, columns - 1); + } + + // Replace all 'O' with 'X' and 'T' back to 'O' + for (int row = 0; row < rows; row++) { + for (int column = 0; column < columns; column++) { + if (board[row][column] == 'O') { + board[row][column] = 'X'; + } else if (board[row][column] == 'T') { + board[row][column] = 'O'; + } + } + } + } +} \ No newline at end of file diff --git a/dart/0136-single-number.dart b/dart/0136-single-number.dart new file mode 100644 index 000000000..6030ca15c --- /dev/null +++ b/dart/0136-single-number.dart @@ -0,0 +1,19 @@ +class Solution { + int singleNumber(List nums) { + int? val = null; + nums.sort(); + int i = 0; + while (i < nums.length - 2) { + if (!(nums[i] != nums[i + 2] && nums[i] == nums[i + 1])) { + val = nums[i]; + break; + } else { + i = i + 2; + } + } + if (val == null) { + val = nums[nums.length - 1]; + } + return val; + } +} diff --git a/dart/0150-evaluate-reverse-polish-notation.dart b/dart/0150-evaluate-reverse-polish-notation.dart new file mode 100644 index 000000000..7bf5f2a74 --- /dev/null +++ b/dart/0150-evaluate-reverse-polish-notation.dart @@ -0,0 +1,28 @@ +class Solution { + int evalRPN(List tokens) { + var stack = []; + for (var i = 0; i < tokens.length; i++) { + var char = tokens[i]; + if (char == '+') { + var a = stack.removeLast(); + var b = stack.removeLast(); + stack.add(a + b); + } else if (char == '-') { + var a = stack.removeLast(); + var b = stack.removeLast(); + stack.add(b - a); + } else if (char == '*') { + var a = stack.removeLast(); + var b = stack.removeLast(); + stack.add(a * b); + } else if (char == '/') { + var a = stack.removeLast(); + var b = stack.removeLast(); + stack.add(b ~/ a); + } else { + stack.add(int.parse(char)); + } + } + return stack.first; + } +} diff --git a/dart/0155-min-stack.dart b/dart/0155-min-stack.dart new file mode 100644 index 000000000..c60d58056 --- /dev/null +++ b/dart/0155-min-stack.dart @@ -0,0 +1,28 @@ +class MinStack { + final List stack = []; + final List minStack = []; + + MinStack(); + + void push(int val) { + stack.add(val); + if (minStack.isEmpty) { + minStack.add(val); + } else { + minStack.add(min(val, minStack.last)); + } + } + + void pop() { + stack.removeLast(); + minStack.removeLast(); + } + + int top() { + return stack.last; + } + + int getMin() { + return minStack.last; + } +} diff --git a/dart/0169-majority-element.dart b/dart/0169-majority-element.dart new file mode 100644 index 000000000..6372fb13b --- /dev/null +++ b/dart/0169-majority-element.dart @@ -0,0 +1,20 @@ +class Solution { + int majorityElement(List nums) { + int res = 0; + int count = 0; + + for (int n in nums) { + if (count == 0) { + res = n; + } + + if (res == n) { + count++; + } else { + count--; + } + } + + return res; + } +} diff --git a/dart/0206-reverse-linked-list.dart b/dart/0206-reverse-linked-list.dart new file mode 100644 index 000000000..b2ec56f3b --- /dev/null +++ b/dart/0206-reverse-linked-list.dart @@ -0,0 +1,25 @@ +// Time Complexity: O(n) +// Space Complexity: O(1) + +class Solution { + ListNode? reverseList(ListNode? head) { + if (head == null) return null; + + ListNode? newHead; + + while (head != null) { + if (newHead == null) { + newHead = head; + head = head.next; + newHead.next = null; + } else { + var temp = newHead; + newHead = head; + head = head.next; + newHead.next = temp; + } + } + + return newHead; + } +} diff --git a/dart/0217-contains-duplicate.dart b/dart/0217-contains-duplicate.dart new file mode 100644 index 000000000..c05e64bbb --- /dev/null +++ b/dart/0217-contains-duplicate.dart @@ -0,0 +1,15 @@ +// Time Complexity: O(n) +// Space Complexity: O(n) + +class Solution { + bool containsDuplicate(List nums) { + Set hashSet = Set(); + for (int n in nums) { + if (hashSet.contains(n)) { + return true; + } + hashSet.add(n); + } + return false; + } +} diff --git a/dart/0226-invert-binary-tree.dart b/dart/0226-invert-binary-tree.dart new file mode 100644 index 000000000..b57274cf1 --- /dev/null +++ b/dart/0226-invert-binary-tree.dart @@ -0,0 +1,16 @@ +class Solution { + TreeNode? invertTree(TreeNode? root) { + if (root == null) { + return null; + } + + var tmp = root.left; + root.left = root.right; + root.right = tmp; + + invertTree(root.left); + invertTree(root.right); + + return root; + } +} diff --git a/dart/0242-valid-anagram.dart b/dart/0242-valid-anagram.dart new file mode 100644 index 000000000..8640c03d5 --- /dev/null +++ b/dart/0242-valid-anagram.dart @@ -0,0 +1,24 @@ +// Time Complexity: O(s + t) +// Space Complexity: O(1) + +class Solution { + bool isAnagram(String s, String t) { + if (s.length != t.length) return false; + + final counter = List.filled(26, 0); + + for (int i = 0; i < s.length; i++) { + counter[s[i].codeUnitAt(0) - 'a'.codeUnitAt(0)]++; + } + + for (int i = 0; i < t.length; i++) { + counter[t[i].codeUnitAt(0) - 'a'.codeUnitAt(0)]--; + } + + for (int val in counter) { + if (val != 0) return false; + } + + return true; + } +} \ No newline at end of file diff --git a/dart/0344-reverse-string.dart b/dart/0344-reverse-string.dart new file mode 100644 index 000000000..fba91ab3d --- /dev/null +++ b/dart/0344-reverse-string.dart @@ -0,0 +1,15 @@ +// Time Complexity: O(n) +// Space Complexity: O(1) + +class Solution { + void reverseString(List s) { + var l = 0, r = s.length - 1; + while (l <= r) { + var tmp = s[l]; + s[l] = s[r]; + s[r] = tmp; + l += 1; + r -= 1; + } + } +} diff --git a/dart/0347-top-k-frequent-elements.dart b/dart/0347-top-k-frequent-elements.dart new file mode 100644 index 000000000..fd3025fff --- /dev/null +++ b/dart/0347-top-k-frequent-elements.dart @@ -0,0 +1,39 @@ +class Solution { + List topKFrequent(List nums, int k) { + Map frequencyMap = {}; + for (int num in nums) { + frequencyMap[num] = (frequencyMap[num] ?? 0) + 1; + } + + List> frequencyList = + List.filled(nums.length + 1, List.empty(growable: true)); + + frequencyMap.forEach((num, count) { + frequencyList[count] = [...frequencyList[count], num]; + }); + + List res = []; + + for (var i = frequencyList.length - 1; i >= 0; i--) { + res.addAll(frequencyList[i]); + + if (res.length == k) { + break; + } + } + + return res; + } +} + +class Solution { + List topKFrequent(List nums, int k) { + Map map = {}; + for (int n in nums) { + map[n] = (map[n] ?? 0) + 1; + } + List uniqueNums = map.keys.toList(); + uniqueNums.sort((a, b) => map[b]!.compareTo(map[a]!)); + return uniqueNums.sublist(0, k); + } +} diff --git a/dart/0392-is-subsequence.dart b/dart/0392-is-subsequence.dart new file mode 100644 index 000000000..dab8ade12 --- /dev/null +++ b/dart/0392-is-subsequence.dart @@ -0,0 +1,12 @@ +class Solution { + bool isSubsequence(String s, String t) { + int i = 0, j = 0; + while (i < t.length && j != s.length) { + if (t[i] == s[j]) { + j += 1; + } + i += 1; + } + return j == s.length; + } +} \ No newline at end of file diff --git a/dart/0617-merge-two-binary-trees.dart b/dart/0617-merge-two-binary-trees.dart new file mode 100644 index 000000000..98e45a687 --- /dev/null +++ b/dart/0617-merge-two-binary-trees.dart @@ -0,0 +1,24 @@ +/** + * Definition for a binary tree node. + * class TreeNode { + * int val; + * TreeNode? left; + * TreeNode? right; + * TreeNode([this.val = 0, this.left, this.right]); + * } + */ +class Solution { + TreeNode? mergeTrees(TreeNode? root1, TreeNode? root2) { + if (root1 == null && root2 == null) { + return null; + } + + int val1 = (root1 == null) ? 0 : root1.val; + int val2 = (root2 == null) ? 0 : root2.val; + TreeNode res = TreeNode(); + res.val = val1 + val2; + res.left = mergeTrees((root1 == null) ? null : root1.left, (root2 == null) ? null : root2.left); + res.right = mergeTrees((root1 == null) ? null : root1.right, (root2 == null) ? null : root2.right); + return res; + } +} \ No newline at end of file diff --git a/dart/0704-binary-search.dart b/dart/0704-binary-search.dart new file mode 100644 index 000000000..8f024676d --- /dev/null +++ b/dart/0704-binary-search.dart @@ -0,0 +1,18 @@ +class Solution { + int search(List nums, int target) { + int l = 0, r = nums.length - 1; + while (l <= r) { + int m = (l + r) ~/ 2; + if (nums[m] == target) { + return m; + } else { + if (nums[m] > target) { + r = m - 1; + } else { + l = m + 1; + } + } + } + return -1; + } +} \ No newline at end of file diff --git a/dart/0746-min-cost-climbing-stairs.dart b/dart/0746-min-cost-climbing-stairs.dart new file mode 100644 index 000000000..0a4258dd7 --- /dev/null +++ b/dart/0746-min-cost-climbing-stairs.dart @@ -0,0 +1,9 @@ +class Solution { + int minCostClimbingStairs(List cost) { + for (int i = cost.length - 3; i >= 0; i--) { + cost[i] += min(cost[i + 1], cost[i + 2]); + } + + return min(cost[0], cost[1]); + } +} diff --git a/dart/0929-unique-email-addresses.dart b/dart/0929-unique-email-addresses.dart new file mode 100644 index 000000000..82bdca704 --- /dev/null +++ b/dart/0929-unique-email-addresses.dart @@ -0,0 +1,20 @@ +// N is the number of emails +// M is the maximum length of an email address +// Time complexity: O(NM) +// Space complexity: O(NM) + +class Solution { + int numUniqueEmails(List emails) { + Set uniqueEmails = Set(); + for (String email in emails) { + String local = email.split('@')[0]; + String domain = email.split('@')[1]; + local = local.replaceAll('.', ''); + if (local.contains('+')) { + local = local.split('+')[0]; + } + uniqueEmails.add('$local@$domain'); + } + return uniqueEmails.length; + } +} diff --git a/dart/1143-longest-common-subsequence.dart b/dart/1143-longest-common-subsequence.dart new file mode 100644 index 000000000..37e156761 --- /dev/null +++ b/dart/1143-longest-common-subsequence.dart @@ -0,0 +1,20 @@ +import 'dart:math'; + +class Solution { + int longestCommonSubsequence(String text1, String text2) { + int m = text1.length; + int n = text2.length; + List> dp = List.generate(m + 1, (_) => List.filled(n + 1, 0)); + + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + if (text1[i - 1] == text2[j - 1]) { + dp[i][j] = dp[i - 1][j - 1] + 1; + } else { + dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]); + } + } + } + return dp[m][n]; + } +} diff --git a/dart/1299-replace-elements-with-greatest-element-on-right-side.dart b/dart/1299-replace-elements-with-greatest-element-on-right-side.dart new file mode 100644 index 000000000..f9b1faa28 --- /dev/null +++ b/dart/1299-replace-elements-with-greatest-element-on-right-side.dart @@ -0,0 +1,11 @@ +class Solution { + List replaceElements(List arr) { + var curMax = -1; + for (int i = arr.length - 1; i > -1; i--) { + int newMax = max(curMax, arr[i]); + arr[i] = curMax; + curMax = newMax; + } + return arr; + } +} \ No newline at end of file diff --git a/dart/1929-concatenation-of-array.dart b/dart/1929-concatenation-of-array.dart new file mode 100644 index 000000000..7faa6bfc0 --- /dev/null +++ b/dart/1929-concatenation-of-array.dart @@ -0,0 +1,19 @@ +// Time Complexity: O(n) +// Space Complexity: O(n) + +class Solution { + List getConcatenation(List nums) { + List ans = List.filled(2 * nums.length, 0); + for (int i = 0; i < nums.length; i++) { + ans[i] = nums[i]; + ans[i + nums.length] = nums[i]; + } + return ans; +} + + +class Solution { + List getConcatenation(List nums) { + return nums + nums; + } +} diff --git a/go/0001-two-sum.go b/go/0001-two-sum.go new file mode 100644 index 000000000..e7d06e8e9 --- /dev/null +++ b/go/0001-two-sum.go @@ -0,0 +1,12 @@ +func twoSum(nums []int, target int) []int { + m := make(map[int]int) + for idx, num := range nums { + + if val, found := m[target-num]; found { + return []int{val, idx} + } + + m[num] = idx + } + return nil +} diff --git a/go/0002-add-two-numbers.go b/go/0002-add-two-numbers.go new file mode 100644 index 000000000..3a1514f32 --- /dev/null +++ b/go/0002-add-two-numbers.go @@ -0,0 +1,34 @@ +func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode { + var dummy = new(ListNode) + var l3 **ListNode = &dummy + var carry int + list1 := l1 + list2 := l2 + + for (list1 != nil) || (list2 != nil) { + l3 = &((*l3).Next) + *l3 = new(ListNode) + var sum int + + if list1 != nil { + sum += list1.Val + list1 = list1.Next + } + + if list2 != nil { + sum += list2.Val + list2 = list2.Next + } + + (*l3).Val = (sum + carry) % 10 + carry = (sum + carry) / 10 + } + + if carry > 0 { + l3 = &((*l3).Next) + *l3 = new(ListNode) + (*l3).Val = carry + } + + return dummy.Next +} \ No newline at end of file diff --git a/go/0003-longest-substring-without-repeating-characters.go b/go/0003-longest-substring-without-repeating-characters.go new file mode 100644 index 000000000..5c90b7d54 --- /dev/null +++ b/go/0003-longest-substring-without-repeating-characters.go @@ -0,0 +1,22 @@ +func lengthOfLongestSubstring(s string) int { + charSet := make(map[byte]bool) + l := 0 + res := 0 + + for r, _ := range s { + for charSet[s[r]] { + delete(charSet,s[l]) + l++ + } + charSet[s[r]] = true + res = max(res, r-l+1) + } + return res +} + +func max(a,b int) int { + if a > b{ + return a + } + return b +} diff --git a/go/0004-median-of-two-sorted-arrays.go b/go/0004-median-of-two-sorted-arrays.go new file mode 100644 index 000000000..a10097345 --- /dev/null +++ b/go/0004-median-of-two-sorted-arrays.go @@ -0,0 +1,70 @@ +func findMedianSortedArrays(nums1 []int, nums2 []int) float64 { + A, B := nums1, nums2 + total := len(nums1) + len(nums2) + half := (total + 1) / 2 + + var Aleft, Aright float64 + var Bleft, Bright float64 + + if len(B) < len(A) { + A, B = B, A + } + + l, r := 0, len(A)-1 + for { + i := (l + r) >> 1 // A + j := half - i - 2 // B + + if i >= 0 { + Aleft = float64(A[i]) + } else { + Aleft = math.Inf(-1) + } + + if (i + 1) < len(A) { + Aright = float64(A[i+1]) + } else { + Aright = math.Inf(1) + } + + if j >= 0 { + Bleft = float64(B[j]) + } else { + Bleft = math.Inf(-1) + } + + if (j + 1) < len(B) { + Bright = float64(B[j+1]) + } else { + Bright = math.Inf(1) + } + + // partition is correct + if Aleft <= Bright && Bleft <= Aright { + // odd + if total%2 == 1 { + return max(Aleft, Bleft) + } + // even + return (max(Aleft, Bleft) + min(Aright, Bright)) / 2 + } else if Aleft > Bright { + r = i - 1 + } else { + l = i + 1 + } + } +} + +func max(a, b float64) float64 { + if a > b { + return a + } + return b +} + +func min(a, b float64) float64 { + if a < b { + return a + } + return b +} diff --git a/go/0005-Longest-Palindromic-Substring.go b/go/0005-Longest-Palindromic-Substring.go new file mode 100644 index 000000000..d22b8de4d --- /dev/null +++ b/go/0005-Longest-Palindromic-Substring.go @@ -0,0 +1,25 @@ +func longestPalindrome(s string) string { + res, resLen := "", 0 + + for i := 0; i < len(s); i++ { + l, r := i, i + for l >= 0 && r < len(s) && s[l] == s[r] { + if (r - l + 1) > resLen { + res = s[l : r+1] + resLen = r - l + 1 + } + l-- + r++ + } + l, r = i, i+1 + for l >= 0 && r < len(s) && s[l] == s[r] { + if (r - l + 1) > resLen { + res = s[l : r+1] + resLen = r - l + 1 + } + l-- + r++ + } + } + return res +} diff --git a/go/0005-longest-palindromic-substring.go b/go/0005-longest-palindromic-substring.go new file mode 100644 index 000000000..d22b8de4d --- /dev/null +++ b/go/0005-longest-palindromic-substring.go @@ -0,0 +1,25 @@ +func longestPalindrome(s string) string { + res, resLen := "", 0 + + for i := 0; i < len(s); i++ { + l, r := i, i + for l >= 0 && r < len(s) && s[l] == s[r] { + if (r - l + 1) > resLen { + res = s[l : r+1] + resLen = r - l + 1 + } + l-- + r++ + } + l, r = i, i+1 + for l >= 0 && r < len(s) && s[l] == s[r] { + if (r - l + 1) > resLen { + res = s[l : r+1] + resLen = r - l + 1 + } + l-- + r++ + } + } + return res +} diff --git a/go/0006-zigzag-conversion.go b/go/0006-zigzag-conversion.go new file mode 100644 index 000000000..c7dc66598 --- /dev/null +++ b/go/0006-zigzag-conversion.go @@ -0,0 +1,25 @@ +package zigzagconversion + +import ( + "strings" +) + +func convert(s string, numRows int) string { + if numRows == 1 { + return s + } + var result strings.Builder + listLength := len(s) + step := (numRows - 1) * 2 + for i := 0; i < numRows; i++ { + // reset steps at last row + for k := i; k < listLength; k += step { + result.WriteByte(s[k]) + if i < numRows-1 && i > 0 && k+step-i*2 < listLength { + result.WriteByte(s[step+k-i*2]) + } + + } + } + return result.String() +} diff --git a/go/0007-reverse-integer.go b/go/0007-reverse-integer.go new file mode 100644 index 000000000..42dcc5c6e --- /dev/null +++ b/go/0007-reverse-integer.go @@ -0,0 +1,23 @@ +func reverse(x int) int { + negative := x < 0 + num := 0 + + if negative { + x = -x + } + + for x > 0 { + if math.MaxInt32/10 < num { + return 0 + } + + num = 10*num + x%10 + x /= 10 + } + + if negative { + return -num + } + + return num +} \ No newline at end of file diff --git a/go/0008-string-to-integer.go b/go/0008-string-to-integer.go new file mode 100644 index 000000000..c0435d3db --- /dev/null +++ b/go/0008-string-to-integer.go @@ -0,0 +1,57 @@ +package stringtointeger + +import ( + "math" + "strconv" +) + +func myAtoi(s string) int { + sign := 1 + sum := 0 + plusFound := false + NumIntParsed := 0 + for _, v := range s { + if s == "" { + return 0 + } + if v >= '0' && v <= '9' { + NumIntParsed++ + n, err := strconv.ParseInt(string(v), 10, 32) + if err == nil { + sum = sum*10 + int(n) + } + // Max and Min int values + if sum > math.MaxInt32 { + if sign < 0 { + return math.MinInt32 + } else { + return math.MaxInt32 + } + } + // check if there is at last one int is parsed + } else if sum >= 0 && NumIntParsed > 0 { + return sign * sum + } else { + //-------------- checking for edge case --------------- + // check for ++ and +- case + if plusFound || sign == -1 { + return 0 + } + if v == '-' { + sign = -1 + } else if v == '+' { + plusFound = true + + } else if v == ' ' { + // to check for + serounded with spaces + if plusFound { + return 0 + } + continue + } else { + break + } + } + } + return sign * sum +} diff --git a/go/0009-palindrome-number.go b/go/0009-palindrome-number.go new file mode 100644 index 000000000..5ea72d781 --- /dev/null +++ b/go/0009-palindrome-number.go @@ -0,0 +1,10 @@ +func isPalindrome(x int) bool { + s := strconv.Itoa(x) + r := []rune(s) + for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 { + if r[i] != r[j] { + return false + } + } + return true +} \ No newline at end of file diff --git a/go/0010-regular-expression-matching.go b/go/0010-regular-expression-matching.go new file mode 100644 index 000000000..a0298590f --- /dev/null +++ b/go/0010-regular-expression-matching.go @@ -0,0 +1,41 @@ +func isMatch(s string, p string) bool { + // dp[i][j] is true if p[:i] matches s[:j] + dp := make([][]bool, len(p) + 1) + for i := 0; i < len(p) + 1; i++ { + dp[i] = make([]bool, len(s) + 1) + for j := 0; j < len(s) + 1; j++ { + dp[i][j] = false + } + } + + dp[0][0] = true + + // Base case for i = 0 is already set up, as empty pattern can only match empty string + // But a nonempty pattern can match an empty string, so we do base cases for j = 0 + for i := 1; i < len(p); i++ { + if p[i] == '*' { + dp[i + 1][0] = dp[i - 1][0] + } + } + + // Now for the general case + for i := 0; i < len(p); i++ { + for j := 0; j < len(s); j++ { + if p[i] == '.' || p[i] == s[j] { + // Single character matches + dp[i + 1][j + 1] = dp[i][j] + } else if p[i] == '*' { + // Wildcard - check that the character matches + // or if we can have 0 repetitions of the previous char + dp[i + 1][j + 1] = dp[i - 1][j + 1] || dp[i][j + 1] + if p[i - 1] == '.' || p[i - 1] == s[j] { + if dp[i + 1][j] { + dp[i + 1][j + 1] = dp[i + 1][j] + } + } + } + } + } + + return dp[len(p)][len(s)] +} diff --git a/go/0011-container-with-most-water.go b/go/0011-container-with-most-water.go new file mode 100644 index 000000000..a2b4bf806 --- /dev/null +++ b/go/0011-container-with-most-water.go @@ -0,0 +1,28 @@ +func maxArea(height []int) int { + left := 0 + right := len(height) - 1 + res := 0 + + for left < right { + area := min(height[left], height[right]) * (right - left) + + if area > res { + res = area + } + + if height[left] > height[right] { + right-- + } else { + left++ + } + } + + return res +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} \ No newline at end of file diff --git a/go/0012-integer-to-roman.go b/go/0012-integer-to-roman.go new file mode 100644 index 000000000..69305a6fa --- /dev/null +++ b/go/0012-integer-to-roman.go @@ -0,0 +1,19 @@ +import "strings" + +func intToRoman(num int) string { + integer := [13]int{1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1} + roman := [13]string{"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"} + + var sb strings.Builder + for i := 0; i < 13; i++ { + if num/integer[i] > 0 { + count := num / integer[i] + for count > 0 { + sb.WriteString(roman[i]) + count-- + } + num = num % integer[i] + } + } + return sb.String() +} diff --git a/go/0013-roman-to-integer.go b/go/0013-roman-to-integer.go new file mode 100644 index 000000000..70cfbf7f1 --- /dev/null +++ b/go/0013-roman-to-integer.go @@ -0,0 +1,15 @@ +package main + +func romanToInt(s string) int { + letters := map[byte]int{'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000} + length := len(s) + var ans int + for i := 0; i < length; i++ { + if i+1 < length && letters[s[i]] < letters[s[i+1]] { + ans -= letters[s[i]] + } else { + ans += letters[s[i]] + } + } + return ans +} diff --git a/go/0014-longest-common-prefix.go b/go/0014-longest-common-prefix.go new file mode 100644 index 000000000..fd7f7eb41 --- /dev/null +++ b/go/0014-longest-common-prefix.go @@ -0,0 +1,12 @@ +func longestCommonPrefix(strs []string) string { + var res strings.Builder + for i := 0; i < len(strs[0]); i++ { + for _, s := range(strs) { + if i == len(s) || s[i] != strs[0][i] { + return res.String() + } + } + res.WriteByte(strs[0][i]) + } + return res.String() +} diff --git a/go/0015-3sum.go b/go/0015-3sum.go new file mode 100644 index 000000000..345046858 --- /dev/null +++ b/go/0015-3sum.go @@ -0,0 +1,43 @@ +func threeSum(nums []int) [][]int { + n := len(nums) + + // Sort the given array + sort.Ints(nums) + + var result [][]int + for num1Idx := 0; num1Idx < n-2; num1Idx++ { + // Skip all duplicates from left + // num1Idx>0 ensures this check is made only from 2nd element onwards + if num1Idx > 0 && nums[num1Idx] == nums[num1Idx-1] { + continue + } + + num2Idx := num1Idx + 1 + num3Idx := n - 1 + for num2Idx < num3Idx { + sum := nums[num2Idx] + nums[num3Idx] + nums[num1Idx] + if sum == 0 { + // Add triplet to result + result = append(result, []int{nums[num1Idx], nums[num2Idx], nums[num3Idx]}) + num2Idx++ + num3Idx-- + + // Skip all duplicates from right + for num2Idx < num3Idx && nums[num3Idx] == nums[num3Idx+1] { + num3Idx-- + } + // Skip all duplicates from left + for num2Idx < num3Idx && nums[num2Idx] == nums[num2Idx-1] { + num2Idx++ + } + } else if sum > 0 { + // Decrement num3Idx to reduce sum value + num3Idx-- + } else { + // Increment num2Idx to increase sum value + num2Idx++ + } + } + } + return result +} diff --git a/go/0016-3sum-closest.go b/go/0016-3sum-closest.go new file mode 100644 index 000000000..e704bd02e --- /dev/null +++ b/go/0016-3sum-closest.go @@ -0,0 +1,36 @@ +package threeSumClosest + +import ( + "math" + "sort" +) + +func threeSumClosest(nums []int, target int) int { + Length := len(nums) + // Sort given array of numbers + sort.Ints(nums) + var left, right, sum, diff, result int + min := math.MaxInt + for i := 0; i < Length-2; i++ { + left = i + 1 + right = Length - 1 + for left < right { + sum = nums[left] + nums[right] + nums[i] + // Calculate the distance between the target and sum + diff = int(math.Abs(float64(target - sum))) + if sum < target { + left++ + } else if sum > target { + right-- + } else { + return sum + } + // Check for smallest distance from the target + if diff < min { + min = diff + result = sum + } + } + } + return result +} diff --git a/go/0017-letter-combinations-of-a-phone-number.go b/go/0017-letter-combinations-of-a-phone-number.go new file mode 100644 index 000000000..e86507eb8 --- /dev/null +++ b/go/0017-letter-combinations-of-a-phone-number.go @@ -0,0 +1,34 @@ +package main + +func letterCombinations(digits string) []string { + ans := make([]string, 0) + if len(digits) == 0 { + return ans + } + m := make(map[byte]string) + m['2'] = "abc" + m['3'] = "def" + m['4'] = "ghi" + m['5'] = "jkl" + m['6'] = "mno" + m['7'] = "pqrs" + m['8'] = "tuv" + m['9'] = "wxyz" + curr := "" + var backtrack func(idx int) + backtrack = func(idx int) { + if len(curr) == len(digits) { + ans = append(ans, curr) + return + } + dig := digits[idx] + str := m[dig] + for i := 0; i < len(str); i++ { + curr += string(str[i]) + backtrack(idx + 1) + curr = curr[:len(curr)-1] + } + } + backtrack(0) + return ans +} diff --git a/go/0018-4Sum.go b/go/0018-4Sum.go new file mode 100644 index 000000000..c505513f5 --- /dev/null +++ b/go/0018-4Sum.go @@ -0,0 +1,45 @@ +func fourSum(nums []int, target int) [][]int { + if len(nums) < 4 { + return [][]int{} + } + + sort.Ints(nums) + + result := make([][]int, 0) + + for i, ival := range nums { + if i != 0 && nums[i] == nums[i - 1] { + continue + } + + for j := i + 1; j < len(nums) - 1; j++ { + if j != i+1 && nums[j] == nums[j-1] { + continue + } + + jval := nums[j] + + for l, r := j+1, len(nums) - 1; l < r; { + lval, rval := nums[l], nums[r] + + sum := ival + jval + lval + rval + switch { + case sum < target: + l++ + case sum > target: + r-- + default: + result = append(result, []int{ival, jval, lval, rval}) + for ; l < r && nums[l] == nums[l+1]; l++ { + } + for ; l < r && nums[r] == nums[r-1]; r-- { + } + l++ + r-- + } + } + } + } + + return result +} \ No newline at end of file diff --git a/go/0018-4sum.go b/go/0018-4sum.go new file mode 100644 index 000000000..c505513f5 --- /dev/null +++ b/go/0018-4sum.go @@ -0,0 +1,45 @@ +func fourSum(nums []int, target int) [][]int { + if len(nums) < 4 { + return [][]int{} + } + + sort.Ints(nums) + + result := make([][]int, 0) + + for i, ival := range nums { + if i != 0 && nums[i] == nums[i - 1] { + continue + } + + for j := i + 1; j < len(nums) - 1; j++ { + if j != i+1 && nums[j] == nums[j-1] { + continue + } + + jval := nums[j] + + for l, r := j+1, len(nums) - 1; l < r; { + lval, rval := nums[l], nums[r] + + sum := ival + jval + lval + rval + switch { + case sum < target: + l++ + case sum > target: + r-- + default: + result = append(result, []int{ival, jval, lval, rval}) + for ; l < r && nums[l] == nums[l+1]; l++ { + } + for ; l < r && nums[r] == nums[r-1]; r-- { + } + l++ + r-- + } + } + } + } + + return result +} \ No newline at end of file diff --git a/go/0019-remove-nth-node-from-end-of-list.go b/go/0019-remove-nth-node-from-end-of-list.go new file mode 100644 index 000000000..eb065831f --- /dev/null +++ b/go/0019-remove-nth-node-from-end-of-list.go @@ -0,0 +1,30 @@ +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func removeNthFromEnd(head *ListNode, n int) *ListNode { + if head == nil { + return head + } + + slow, fast := head, head + for n > 0 { + fast = fast.Next + n-- + } + + if fast == nil { + return slow.Next + } + + for fast.Next != nil { + slow = slow.Next + fast = fast.Next + } + + slow.Next = slow.Next.Next + return head +} diff --git a/go/0020-valid-parentheses.go b/go/0020-valid-parentheses.go new file mode 100644 index 000000000..becc3468d --- /dev/null +++ b/go/0020-valid-parentheses.go @@ -0,0 +1,29 @@ +func isValid(s string) bool { + pairs := map[byte]byte{ + '}': '{', + ']': '[', + ')': '(', + } + + stack := make([]byte, 0) + + for _, char := range []byte(s) { + pair, ok := pairs[char] + if !ok { + stack = append(stack, char) + continue + } + + if len(stack) == 0 { + return false + } + + if stack[len(stack) - 1] != pair { + return false + } + + stack = stack[:len(stack) - 1] + } + + return len(stack) == 0 +} diff --git a/go/0021-merge-two-sorted-lists.go b/go/0021-merge-two-sorted-lists.go new file mode 100644 index 000000000..ff5d36607 --- /dev/null +++ b/go/0021-merge-two-sorted-lists.go @@ -0,0 +1,49 @@ +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func mergeTwoLists(l1 *ListNode, l2 *ListNode) *ListNode { + if l1 == nil { + return l2 + } + + if l2 == nil { + return l1 + } + + ptr1, ptr2 := l1, l2 + + result := new(ListNode) + + temp := result + + for ptr1 != nil && ptr2 != nil { + if ptr1.Val < ptr2.Val { + temp.Next = ptr1 + temp = temp.Next + ptr1 = ptr1.Next + } else { + temp.Next = ptr2 + temp = temp.Next + ptr2 = ptr2.Next + } + } + + for ptr1 != nil{ + temp.Next = ptr1 + temp = temp.Next + ptr1 = ptr1.Next + } + + for ptr2 != nil{ + temp.Next = ptr2 + temp = temp.Next + ptr2 = ptr2.Next + } + + result = result.Next + return result +} \ No newline at end of file diff --git a/go/0022-generate-parentheses.go b/go/0022-generate-parentheses.go new file mode 100644 index 000000000..24b30f8be --- /dev/null +++ b/go/0022-generate-parentheses.go @@ -0,0 +1,35 @@ +package main + +import "strings" + +func generateParenthesis(n int) []string { + var stack []string + var res []string + + var backtrack func(int, int) + backtrack = func(openN int, closedN int) { + if openN == n && closedN == n && openN == closedN { + res = append(res, strings.Join(stack, "")) + return + } + if openN < n { + stack = append(stack, "(") + backtrack(openN+1, closedN) + pop(&stack) + } + if closedN < openN { + stack = append(stack, ")") + backtrack(openN, closedN+1) + pop(&stack) + } + } + backtrack(0, 0) + return res + +} + +func pop(list *[]string) { + //since pop() function in go doesn't exist + length := len(*list) + *list = (*list)[:length-1] +} diff --git a/go/0023-merge-k-sorted-lists.go b/go/0023-merge-k-sorted-lists.go new file mode 100644 index 000000000..eeb877fd9 --- /dev/null +++ b/go/0023-merge-k-sorted-lists.go @@ -0,0 +1,42 @@ +func mergeKLists(lists []*ListNode) *ListNode { + if lists == nil || len(lists) == 0 { + return nil + } + + for len(lists) > 1 { + var mergedLists []*ListNode + for i := 0; i < len(lists); i += 2 { + l1 := lists[i] + var l2 *ListNode + if (i + 1) < len(lists) { + l2 = lists[i + 1] + } + mergedLists = append(mergedLists, mergeList(l1, l2)) + } + lists = mergedLists + } + return lists[0] +} + +func mergeList(l1, l2 *ListNode) *ListNode { + dummy := &ListNode{} + tail := dummy + + for l1 != nil && l2 != nil { + if l1.Val < l2.Val { + tail.Next = l1 + l1 = l1.Next + } else { + tail.Next = l2 + l2 = l2.Next + } + tail = tail.Next + } + if l1 != nil { + tail.Next = l1 + } + if l2 != nil { + tail.Next = l2 + } + return dummy.Next +} diff --git a/go/0024-swap-nodes-in-pairs.go b/go/0024-swap-nodes-in-pairs.go new file mode 100644 index 000000000..268dcbb37 --- /dev/null +++ b/go/0024-swap-nodes-in-pairs.go @@ -0,0 +1,19 @@ +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ + func swapPairs(head *ListNode) *ListNode { + if head == nil || head.Next == nil { + return head + } + + next := head.Next + swapped := swapPairs(next.Next) + + next.Next, head.Next = head, swapped + + return next +} \ No newline at end of file diff --git a/go/0025-reverse-nodes-in-k-group.go b/go/0025-reverse-nodes-in-k-group.go new file mode 100644 index 000000000..4e74d7ea1 --- /dev/null +++ b/go/0025-reverse-nodes-in-k-group.go @@ -0,0 +1,34 @@ +func reverseKGroup(head *ListNode, k int) *ListNode { + dummy := &ListNode{Val: 0, Next: head} + groupPrev := dummy + + for true { + kth := getKth(groupPrev, k) + if kth == nil { + break; + } + groupNext := kth.Next + + // reverse group + prev, curr := kth.Next, groupPrev.Next + for curr != groupNext { + tmp := curr.Next + curr.Next = prev + prev = curr + curr = tmp + } + + tmp := groupPrev.Next + groupPrev.Next = kth + groupPrev = tmp + } + return dummy.Next +} + +func getKth(curr *ListNode, k int) *ListNode { + for curr != nil && k > 0 { + curr = curr.Next + k -= 1 + } + return curr +} diff --git a/go/0026-remove-duplicates-from-sorted-array.go b/go/0026-remove-duplicates-from-sorted-array.go new file mode 100644 index 000000000..58c2fcfba --- /dev/null +++ b/go/0026-remove-duplicates-from-sorted-array.go @@ -0,0 +1,15 @@ +func removeDuplicates(nums []int) int { + length := len(nums) + if length < 2 { + return length + } + k, i := 1, 1 + for i < length { + if nums[i] != nums[i-1] { + nums[k] = nums[i] + k++ + } + i++ + } + return k +} diff --git a/go/0027-remove-element.go b/go/0027-remove-element.go new file mode 100644 index 000000000..fc89c77f9 --- /dev/null +++ b/go/0027-remove-element.go @@ -0,0 +1,14 @@ +func removeElement(nums []int, val int) int { + left, right := 0, len(nums)-1 + + for left <= right { + if nums[left] == val { + nums[left], nums[right] = nums[right], nums[left] + right-- + } else { + left++ + } + } + + return left +} \ No newline at end of file diff --git a/go/0028-find-the-index-of-the-first-occurrence-in-a-string.go b/go/0028-find-the-index-of-the-first-occurrence-in-a-string.go new file mode 100644 index 000000000..17aae789b --- /dev/null +++ b/go/0028-find-the-index-of-the-first-occurrence-in-a-string.go @@ -0,0 +1,23 @@ +func strStr(haystack string, needle string) int { + if haystack == "" && needle == "" { + return 0 + } + lh := len(haystack) + ln := len(needle) + for i := 0; i < lh; i++ { + ct := 0 + for j := 0; j < ln; j++ { + if i+j >= lh { + return -1 + } + if needle[j] != haystack[i+j] { + break + } + ct++ + } + if ct == ln { + return i + } + } + return -1 +} diff --git a/go/0033-search-in-rotated-sorted-array.go b/go/0033-search-in-rotated-sorted-array.go new file mode 100644 index 000000000..dace26e5c --- /dev/null +++ b/go/0033-search-in-rotated-sorted-array.go @@ -0,0 +1,27 @@ +func search(nums []int, target int) int { + left, right := 0, len(nums) - 1 + + for left <= right { + mid := (left + right) / 2 + if target == nums[mid] { + return mid + } + + // left sorted portion + if nums[left] <= nums[mid] { + if target > nums[mid] || target < nums[left] { + left = mid + 1 + } else { + right = mid - 1 + } + // Right sorted portion + } else { + if target < nums[mid] || target > nums[right] { + right = mid - 1 + } else { + left = mid + 1 + } + } + } + return -1 +} \ No newline at end of file diff --git a/go/0034-find-first-and-last-position-of-element-in-sorted-array.go b/go/0034-find-first-and-last-position-of-element-in-sorted-array.go new file mode 100644 index 000000000..c5277836b --- /dev/null +++ b/go/0034-find-first-and-last-position-of-element-in-sorted-array.go @@ -0,0 +1,26 @@ +func searchRange(nums []int, target int) []int { + right := binSearch(nums, target, false) + left := binSearch(nums, target, true) + return []int{left, right} +} + +func binSearch(nums []int, target int, leftBias bool) int { + l, r := 0, len(nums)-1 + i := -1 + for l <= r { + m := (l + r) / 2 + if nums[m] < target { + l = m + 1 + } else if nums[m] > target { + r = m - 1 + } else { + i = m + if leftBias { + r = m - 1 + } else { + l = m + 1 + } + } + } + return i +} \ No newline at end of file diff --git a/go/0035-search-insert-position.go b/go/0035-search-insert-position.go new file mode 100644 index 000000000..c74168867 --- /dev/null +++ b/go/0035-search-insert-position.go @@ -0,0 +1,12 @@ +func searchInsert(nums []int, target int) int { + low, high := 0, len(nums) + for low < high { + mid := low + (high - low)/2 + if target > nums[mid] { + low = mid + 1 + } else { + high = mid + } + } + return low +} diff --git a/go/0036-valid-sudoku.go b/go/0036-valid-sudoku.go new file mode 100644 index 000000000..0c28415ff --- /dev/null +++ b/go/0036-valid-sudoku.go @@ -0,0 +1,25 @@ +func isValidSudoku(board [][]byte) bool { + rows := [9][9]bool{} + cols := [9][9]bool{} + squares := [9][9]bool{} + + for r := range 9 { + for c := range 9 { + if board[r][c] == '.' { + continue + } + + value := board[r][c] - '1' + s := r/3*3 + c/3 + if rows[r][value] || cols[c][value] || squares[s][value] { + return false + } + + rows[r][value] = true + cols[c][value] = true + squares[s][value] = true + } + } + + return true +} diff --git a/go/0039-combination-sum.go b/go/0039-combination-sum.go new file mode 100644 index 000000000..26e47c64a --- /dev/null +++ b/go/0039-combination-sum.go @@ -0,0 +1,24 @@ +package main + +func combinationSum(candidates []int, target int) [][]int { + ans := make([][]int, 0) + curr := make([]int, 0) + var backtrack func(idx int, currSum int, curr []int) + backtrack = func(idx int, currSum int, curr []int) { + if currSum == target { + ans = append(ans, append([]int{}, curr...)) + return + } + if currSum > target { + return + } + for i := idx; i < len(candidates); i++ { + curr = append(curr, candidates[i]) + backtrack(i, currSum+candidates[i], curr) + curr = curr[:len(curr)-1] + } + + } + backtrack(0, 0, curr) + return ans +} diff --git a/go/0040-combination-sum-ii.go b/go/0040-combination-sum-ii.go new file mode 100644 index 000000000..ee3ed2f34 --- /dev/null +++ b/go/0040-combination-sum-ii.go @@ -0,0 +1,30 @@ +package main + +import "sort" + +func combinationSum2(candidates []int, target int) [][]int { + ans := make([][]int, 0) + curr := make([]int, 0) + sort.Ints(candidates) + var backtrack func(idx int, currSum int, curr []int) + backtrack = func(idx int, currSum int, curr []int) { + if currSum == target { + ans = append(ans, append([]int{}, curr...)) + return + } + if currSum > target { + return + } + for i := idx; i < len(candidates); i++ { + if i > idx && candidates[i] == candidates[i-1] { + continue + } + curr = append(curr, candidates[i]) + backtrack(i+1, currSum+candidates[i], curr) + curr = curr[:len(curr)-1] + } + + } + backtrack(0, 0, curr) + return ans +} diff --git a/go/0041-first-missing-positive.go b/go/0041-first-missing-positive.go new file mode 100644 index 000000000..77b23e9bd --- /dev/null +++ b/go/0041-first-missing-positive.go @@ -0,0 +1,16 @@ +func firstMissingPositive(nums []int) int { + for i := range nums { + for nums[i] != i+1 && (nums[i] > 0 && nums[i] <= len(nums)) && nums[i] != nums[nums[i]-1] { + // Swap nums[i] with the number at its correct position (nums[nums[i]-1]) + nums[i], nums[nums[i]-1] = nums[nums[i]-1], nums[i] + } + } + // Find the first missing positive number + for i, n := range nums { + if n != i+1 { + return i + 1 + } + } + // If no missing positive number was found, return the length of the array + 1 + return len(nums) + 1 +} diff --git a/go/0042-trapping-rain-water.go b/go/0042-trapping-rain-water.go new file mode 100644 index 000000000..8b22b112d --- /dev/null +++ b/go/0042-trapping-rain-water.go @@ -0,0 +1,30 @@ +func trap(height []int) int { + if height == nil { + return 0 + } + + left, right := 0, len(height) - 1 + leftMax, rightMax := height[left], height[right] + res := 0 + + for left < right { + if leftMax < rightMax { + left += 1 + leftMax = max(leftMax, height[left]) + res += leftMax - height[left] + } else { + right -= 1 + rightMax = max(rightMax, height[right]) + res += rightMax - height[right] + } + } + return res +} + +// Golang does not have a built-in max for integers +func max(a int, b int) int { + if a > b { + return a; + } + return b; +} \ No newline at end of file diff --git a/go/0045-jump-game-ii.go b/go/0045-jump-game-ii.go new file mode 100644 index 000000000..3aa05e7ca --- /dev/null +++ b/go/0045-jump-game-ii.go @@ -0,0 +1,16 @@ +func jump(nums []int) int { + step, end, max := 0, 0, 0 + + for i := 0; i < len(nums)-1; i++ { + if i + nums[i] > max { + max = i+nums[i] + } + + if i == end { + step++; + end = max + } + } + + return step +} \ No newline at end of file diff --git a/go/0046-permutations.go b/go/0046-permutations.go new file mode 100644 index 000000000..f033de893 --- /dev/null +++ b/go/0046-permutations.go @@ -0,0 +1,25 @@ +package main + +func permute(nums []int) [][]int { + n := len(nums) + ans := make([][]int, 0) + curr := make([]int, 0, n) + vis := make(map[int]int) + var backtrack func(idx int) + backtrack = func(idx int) { + if len(curr) == n { + ans = append(ans, append([]int{}, curr...)) + } + for i := 0; i < n; i++ { + if vis[i] == 0 { + vis[i]++ + curr = append(curr, nums[i]) + backtrack(i + 1) + curr = curr[:len(curr)-1] + vis[i]-- + } + } + } + backtrack(0) + return ans +} diff --git a/go/0047-permutations-ii.go b/go/0047-permutations-ii.go new file mode 100644 index 000000000..b46c04db9 --- /dev/null +++ b/go/0047-permutations-ii.go @@ -0,0 +1,33 @@ +func permuteUnique(nums []int) [][]int { + numLen := len(nums) + res := [][]int{} + counter := make(map[int]int) + + for _, n := range nums { + counter[n]++ + } + + var backtrack func([]int, map[int]int) + + backtrack = func(perm []int, counter map[int]int) { + if len(perm) == numLen { + res = append(res, append([]int{}, perm...)) + return + } + + for n, count := range counter { + if count == 0 { + continue + } + perm = append(perm, n) + counter[n]-- + backtrack(perm, counter) + perm = perm[:len(perm)-1] + counter[n]++ + } + } + + backtrack([]int{}, counter) + + return res +} diff --git a/go/0048-rotate-image.go b/go/0048-rotate-image.go new file mode 100644 index 000000000..0662b0f4a --- /dev/null +++ b/go/0048-rotate-image.go @@ -0,0 +1,25 @@ +func rotate(matrix [][]int) { + left, right := 0, len(matrix) - 1 + for left < right { + for i := 0; i < right - left; i++ { + top, bottom := left, right + + // save the topleft + topLeft := matrix[top][left + i] + + // move bottom left into top left + matrix[top][left + i] = matrix[bottom - i][left] + + // move bottom right into bottom left + matrix[bottom - i][left] = matrix[bottom][right - i] + + // move top right into bottom right + matrix[bottom][right - i] = matrix[top + i][right] + + // move top left into top right + matrix[top + i][right] = topLeft + } + right -= 1 + left += 1 + } +} \ No newline at end of file diff --git a/go/0049-group-anagrams.go b/go/0049-group-anagrams.go new file mode 100644 index 000000000..e1a5bcb0c --- /dev/null +++ b/go/0049-group-anagrams.go @@ -0,0 +1,21 @@ +package main + +func groupAnagrams(strs []string) [][]string { + anagramMap := make(map[[26]int][]string) + for _, s := range strs { + var count [26]int + for _, c := range s { + count[c - 'a']++ + } + anagramMap[count] = append(anagramMap[count], s) + } + result := make([][]string, len(anagramMap)) + idx := 0 + for _, v := range anagramMap { + result[idx] = v + idx++ + } + return result +} + + diff --git a/go/0051-n-queens.go b/go/0051-n-queens.go new file mode 100644 index 000000000..c1e44ea17 --- /dev/null +++ b/go/0051-n-queens.go @@ -0,0 +1,32 @@ +package main + +func solveNQueens(n int) [][]string { + ans, curr := make([][]string, 0), make([]string, 0) + column, diag1, diag2 := make(map[int]int), make(map[int]int), make(map[int]int) + var backtrack func(y int) + backtrack = func(y int) { + if y == n { + ans = append(ans, append([]string{}, curr...)) + } + for x := 0; x < n; x++ { + if column[x] > 0 || diag1[x+y] > 0 || diag2[n+x-y-1] > 0 { + continue + } + column[x], diag1[x+y], diag2[n+x-y-1] = 1, 1, 1 + s := "" + for i := 0; i < n; i++ { + if i == x { + s += "Q" + } else { + s += "." + } + } + curr = append(curr, s) + backtrack(y + 1) + column[x], diag1[x+y], diag2[n+x-y-1] = 0, 0, 0 + curr = curr[:len(curr)-1] + } + } + backtrack(0) + return ans +} diff --git a/go/0053-maximum-subarray.go b/go/0053-maximum-subarray.go new file mode 100644 index 000000000..2a2669bc4 --- /dev/null +++ b/go/0053-maximum-subarray.go @@ -0,0 +1,20 @@ +func maxSubArray(nums []int) int { + result, total := nums[0], 0 + + for i := 0; i < len(nums); i++ { + total += nums[i] + result = max(result, total) + if total < 0 { + total = 0 + } + } + return result +} + +// Golang does not have a built-in max for integers +func max(a int, b int) int { + if a > b { + return a + } + return b +} \ No newline at end of file diff --git a/go/0054-spiral-matrix.go b/go/0054-spiral-matrix.go new file mode 100644 index 000000000..e1e8b5620 --- /dev/null +++ b/go/0054-spiral-matrix.go @@ -0,0 +1,16 @@ +var visited = 200 // -100 <= matrix[i][j] <= 100 + +func spiralOrder(matrix [][]int) []int { + n, m := len(matrix[0]), len(matrix) + x, y, dx, dy := 0, 0, 1, 0 + ans := make([]int, 0) + for i := 0; i < m*n; i++ { + if !(0 <= x+dx && x+dx < n) || !(0 <= y+dy && y+dy < m) || matrix[y+dy][x+dx] == visited { + dx, dy = -dy, dx + } + ans = append(ans, matrix[y][x]) + matrix[y][x] = visited + x, y = x+dx, y+dy + } + return ans +} diff --git a/go/0055-jump-game.go b/go/0055-jump-game.go new file mode 100644 index 000000000..47a7bfb5f --- /dev/null +++ b/go/0055-jump-game.go @@ -0,0 +1,10 @@ +func canJump(nums []int) bool { + goal := len(nums) - 1 + + for i := len(nums) - 2; i >= 0; i-- { + if i + nums[i] >= goal { + goal = i + } + } + return goal == 0 +} \ No newline at end of file diff --git a/go/0056-merge-intervals.go b/go/0056-merge-intervals.go new file mode 100644 index 000000000..6c41bffe2 --- /dev/null +++ b/go/0056-merge-intervals.go @@ -0,0 +1,27 @@ +import "sort" + +func merge(intervals [][]int) [][]int { + const ( + start = 0 + end = 1 + ) + + sort.SliceStable(intervals, func(i, j int) bool { + return intervals[i][start] < intervals[j][start] + }) + + res := [][]int{intervals[0]} + for i := 1; i < len(intervals); i++ { + currResLastIndex := len(res) - 1 + currEnd := res[currResLastIndex] + curr := intervals[i] + + if currEnd[end] < curr[start] { + res = append(res, curr) + } else if currEnd[end] < curr[end] { + res[currResLastIndex][end] = curr[end] + } + } + + return res +} \ No newline at end of file diff --git a/go/0057-insert-interval.go b/go/0057-insert-interval.go new file mode 100644 index 000000000..dac85fbac --- /dev/null +++ b/go/0057-insert-interval.go @@ -0,0 +1,43 @@ +func insert(intervals [][]int, newInterval []int) [][]int { + + res := [][]int{} + + for i, currInt := range intervals { + currStart, currEnd := currInt[0], currInt[1] + newIntStart, newIntEnd := newInterval[0], newInterval[1] + + if newIntEnd < currStart { + res = append(res, newInterval) + + res = append(res, intervals[i:]...) + + return res + } else if newIntStart > currEnd { + + res = append(res, currInt) + + } else { + newInterval[0] = min(newIntStart, currStart) + newInterval[1] = max(newIntEnd, currEnd) + + } + } + + res = append(res, newInterval) + + return res + +} +func min(a, b int) int { + if a > b { + return b + } + return a +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} \ No newline at end of file diff --git a/go/0058-Length-of-Last-Word.go b/go/0058-Length-of-Last-Word.go new file mode 100644 index 000000000..70814305e --- /dev/null +++ b/go/0058-Length-of-Last-Word.go @@ -0,0 +1,15 @@ +func lengthOfLastWord(s string) int { + l := 0 + + for i := len(s) - 1; i >= 0; i-- { + if s[i] == ' ' { + if l >= 1 { + return l + } + } else { + l++ + } + } + + return l +}å \ No newline at end of file diff --git a/go/0058-length-of-last-word.go b/go/0058-length-of-last-word.go new file mode 100644 index 000000000..70814305e --- /dev/null +++ b/go/0058-length-of-last-word.go @@ -0,0 +1,15 @@ +func lengthOfLastWord(s string) int { + l := 0 + + for i := len(s) - 1; i >= 0; i-- { + if s[i] == ' ' { + if l >= 1 { + return l + } + } else { + l++ + } + } + + return l +}å \ No newline at end of file diff --git a/go/0062-unique-paths.go b/go/0062-unique-paths.go new file mode 100644 index 000000000..d6cfe5c5b --- /dev/null +++ b/go/0062-unique-paths.go @@ -0,0 +1,12 @@ +func uniquePaths(m int, n int) int { + dp := make([]int, n) + dp[0] = 1 + + for i := 0; i < m; i++ { + for j := 1; j < n; j++ { + dp[j] += dp[j-1] + } + } + + return dp[n-1] +} \ No newline at end of file diff --git a/go/0063-unique-paths-ii.go b/go/0063-unique-paths-ii.go new file mode 100644 index 000000000..4ea4d5a08 --- /dev/null +++ b/go/0063-unique-paths-ii.go @@ -0,0 +1,27 @@ +func uniquePathsWithObstacles(obstacleGrid [][]int) int { + m, n := len(obstacleGrid), len(obstacleGrid[0]) + + if m == 0 || n == 0 || obstacleGrid[0][0] == 1 { + return 0 + } + + dp := make([][]int, m+1) + + for i := 0; i <= m; i++ { + dp[i] = make([]int, n+1) + } + + dp[0][1] = 1 + + for i := 1; i <= m; i++ { + for j := 1; j <= n; j++ { + if obstacleGrid[i-1][j-1] == 1 { + dp[i][j] = 0 + } else { + dp[i][j] = dp[i-1][j] + dp[i][j-1] + } + } + } + + return dp[m][n]; +} \ No newline at end of file diff --git a/go/0066-plus-one.go b/go/0066-plus-one.go new file mode 100644 index 000000000..4e2849149 --- /dev/null +++ b/go/0066-plus-one.go @@ -0,0 +1,12 @@ +func plusOne(digits []int) []int { + for i := len(digits) - 1; i >= 0; i-- { + if digits[i] < 9 { + digits[i] += 1 + return digits + } + digits[i] = 0 + } + digits[0] = 1 + digits = append(digits, 0) + return digits +} \ No newline at end of file diff --git a/go/0070-climbing-stairs.go b/go/0070-climbing-stairs.go new file mode 100644 index 000000000..5b0cebc48 --- /dev/null +++ b/go/0070-climbing-stairs.go @@ -0,0 +1,10 @@ +func climbStairs(n int) int { + one, two := 1, 1 + + for i := 0; i < n-1; i++ { + sum := one + two + one, two = two, sum + } + + return two +} \ No newline at end of file diff --git a/go/0073-set-matrix-zeroes.go b/go/0073-set-matrix-zeroes.go new file mode 100644 index 000000000..e8c8e9aa6 --- /dev/null +++ b/go/0073-set-matrix-zeroes.go @@ -0,0 +1,37 @@ +func setZeroes(matrix [][]int) { + m, n := len(matrix), len(matrix[0]) + isCol := false + + for i := 0; i < m; i++ { + if matrix[i][0] == 0 { + isCol = true + } + + for j := 1; j < n; j++ { + if matrix[i][j] == 0 { + matrix[i][0] = 0 + matrix[0][j] = 0 + } + } + } + + for i := 1; i < m; i++ { + for j := 1; j < n; j++ { + if matrix[i][0] == 0 || matrix[0][j] == 0 { + matrix[i][j] = 0 + } + } + } + + if matrix[0][0] == 0 { + for j := 0; j < n; j++ { + matrix[0][j] = 0 + } + } + + if isCol { + for i := 0; i < m; i++ { + matrix[i][0] = 0 + } + } +} \ No newline at end of file diff --git a/go/0074-search-a-2d-matrix.go b/go/0074-search-a-2d-matrix.go new file mode 100644 index 000000000..697a3c196 --- /dev/null +++ b/go/0074-search-a-2d-matrix.go @@ -0,0 +1,40 @@ +package main + +func searchMatrix(matrix [][]int, target int) bool { + ROWS := len(matrix) + COLUMNS := len(matrix[0]) + + top := 0 + bot := ROWS - 1 + + for top <= bot { + row := (top + bot) / 2 + if target > matrix[row][len(matrix[row])-1] { + top = row + 1 + } else if target < matrix[row][0] { + bot = row - 1 + } else { + break + } + } + + if !(top <= bot) { + return false + } + + row := (top + bot) / 2 + left := 0 + right := COLUMNS - 1 + for left <= right { + middle := (left + right) / 2 + if target > matrix[row][middle] { + left = middle + 1 + } else if target < matrix[row][middle] { + right = middle - 1 + } else { + return true + } + } + return false + +} diff --git a/go/0075-sort-colors.go b/go/0075-sort-colors.go new file mode 100644 index 000000000..a04143fe4 --- /dev/null +++ b/go/0075-sort-colors.go @@ -0,0 +1,16 @@ +func sortColors(nums []int) { + low, mid, hi := 0, 0, len(nums)-1 + + for low <= hi { + if nums[low] == 2 { + nums[low], nums[hi] = nums[hi], nums[low] + hi-- + } else if nums[low] == 0 { + nums[low], nums[mid] = nums[mid], nums[low] + mid++ + low++ + } else { + low++ + } + } +} \ No newline at end of file diff --git a/go/0076-minimum-window-substring.go b/go/0076-minimum-window-substring.go new file mode 100644 index 000000000..dd014f04d --- /dev/null +++ b/go/0076-minimum-window-substring.go @@ -0,0 +1,39 @@ +func minWindow(s string, t string) string { + start, end := 0, 0 + targetCharacterFrequency := make(map[uint8]int) + currentCharacterFrequency := make(map[uint8]int) + distinctCharacterCount := 0 + minSubstring := "" + + for index := range t { + targetCharacterFrequency[t[index]]++ + } + + for end < len(s) { + currentCharacterFrequency[s[end]]++ + if targetCharacterFrequency[s[end]] != 0 && + targetCharacterFrequency[s[end]] == currentCharacterFrequency[s[end]] { + distinctCharacterCount++ + } + + for distinctCharacterCount == len(targetCharacterFrequency) { + if minSubstring == "" { + minSubstring = s[start:end+1] + } + if end - start + 1 < len(minSubstring) { + minSubstring = s[start:end+1] + } + + currentCharacterFrequency[s[start]]-- + if currentCharacterFrequency[s[start]] < targetCharacterFrequency[s[start]] { + distinctCharacterCount-- + } + + start++ + } + end++ + } + + return minSubstring +} + diff --git a/go/0077-combinations.go b/go/0077-combinations.go new file mode 100644 index 000000000..87850c099 --- /dev/null +++ b/go/0077-combinations.go @@ -0,0 +1,19 @@ +func backtrack(n, k, start int, arr []int, ans *[][]int) { + if len(arr) == k { + comb := make([]int, k) + copy(comb, arr) + *ans = append(*ans, comb) + return + } + for i := start; i <= n-k+(len(arr)+1); i++ { + arr = append(arr, i) + backtrack(n, k, i+1, arr, ans) + arr = arr[:len(arr)-1] + } +} + +func combine(n int, k int) [][]int { + ans := [][]int{} + backtrack(n, k, 1, []int{}, &ans) + return ans +} diff --git a/go/0078-subsets.go b/go/0078-subsets.go new file mode 100644 index 000000000..189e331e4 --- /dev/null +++ b/go/0078-subsets.go @@ -0,0 +1,20 @@ +package main + +func subsets(nums []int) [][]int { + ans := make([][]int, 0) + curr := make([]int, 0) + var backtrack func(idx int) + backtrack = func(idx int) { + ans = append(ans, append([]int{}, curr...)) + if idx == len(nums) { + return + } + for i := idx; i < len(nums); i++ { + curr = append(curr, nums[i]) + backtrack(i + 1) + curr = curr[:len(curr)-1] + } + } + backtrack(0) + return ans +} diff --git a/go/0079-word-search.go b/go/0079-word-search.go new file mode 100644 index 000000000..36ddcecf4 --- /dev/null +++ b/go/0079-word-search.go @@ -0,0 +1,38 @@ +package main + +func exist(board [][]byte, word string) bool { + n := len(board) + m := len(board[0]) + + var dfs func(i int, j int, curr int) bool + dfs = func(i int, j int, curr int) bool { + if i < 0 || j < 0 || i >= n || j >= m || curr == len(word) { + return false + } + if board[i][j] != word[curr] || board[i][j] == '*' { + return false + } + if curr == len(word)-1 { + return true + } + + tmp := board[i][j] + board[i][j] = '*' + + res := dfs(i+1, j, curr+1) || dfs(i-1, j, curr+1) || dfs(i, j-1, curr+1) || dfs(i, j+1, curr+1) + + board[i][j] = tmp + + return res + } + + for i := 0; i < n; i++ { + for j := 0; j < m; j++ { + if dfs(i, j, 0) { + return true + } + } + } + + return false +} diff --git a/go/0083-remove-duplicates-from-sorted-list.go b/go/0083-remove-duplicates-from-sorted-list.go new file mode 100644 index 000000000..25e6b168b --- /dev/null +++ b/go/0083-remove-duplicates-from-sorted-list.go @@ -0,0 +1,27 @@ +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func deleteDuplicates(head *ListNode) *ListNode { + if head == nil || head.Next == nil { + return head + } + + prev, curr := head, head.Next + + for curr != nil { + for curr != nil && curr.Val == prev.Val { + curr = curr.Next + prev.Next = curr + } + + if curr != nil { + prev, curr = curr, curr.Next + } + } + + return head +} \ No newline at end of file diff --git a/go/0084-largest-rectangle-in-histogram.go b/go/0084-largest-rectangle-in-histogram.go new file mode 100644 index 000000000..239a2d623 --- /dev/null +++ b/go/0084-largest-rectangle-in-histogram.go @@ -0,0 +1,33 @@ +type StackValue struct { + index int + height int +} + +func largestRectangleArea(heights []int) int { + stack := []StackValue{} // pair: {index, height} + maxArea := 0 + var start int + + for i, h := range heights { + start = i + for len(stack) != 0 && stack[len(stack)-1].height > h { + index, height := stack[len(stack)-1].index, stack[len(stack)-1].height + stack = stack[0 : len(stack)-1] //pop top from stack + maxArea = max(maxArea, height*(i-index)) + start = index + } + stack = append(stack, StackValue{start, h}) + } + + for _, h := range stack { + maxArea = max(maxArea, h.height*(len(heights)-h.index)) + } + return maxArea +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} diff --git a/go/0088-merge-sorted-array.go b/go/0088-merge-sorted-array.go new file mode 100644 index 000000000..da2cbcec4 --- /dev/null +++ b/go/0088-merge-sorted-array.go @@ -0,0 +1,25 @@ +// Time Complexity: O(m + n) +// Space Complexity: O(1) + +func merge(nums1 []int, m int, nums2 []int, n int) { + last := m + n - 1 + m -= 1 + n -= 1 + + for m >= 0 && n >= 0 { + if nums1[m] > nums2[n] { + nums1[last] = nums1[m] + m -= 1 + } else { + nums1[last] = nums2[n] + n -= 1 + } + last -= 1 + } + + for n >= 0 { + nums1[last] = nums2[n] + last -= 1 + n -= 1 + } +} diff --git a/go/0090-subsets-ii.go b/go/0090-subsets-ii.go new file mode 100644 index 000000000..9080c8da4 --- /dev/null +++ b/go/0090-subsets-ii.go @@ -0,0 +1,25 @@ +package main + +import "sort" + +func subsetsWithDup(nums []int) [][]int { + n := len(nums) + ans := make([][]int, 0, 1< idx && nums[i] == nums[i-1] { + continue + } + curr = append(curr, nums[i]) + //not backtrack(idx + 1)!! + backtrack(i + 1) + curr = curr[:len(curr)-1] + } + } + backtrack(0) + return ans +} diff --git a/go/0091-decode-ways.go b/go/0091-decode-ways.go new file mode 100644 index 000000000..1a330a82b --- /dev/null +++ b/go/0091-decode-ways.go @@ -0,0 +1,41 @@ +func numDecodingsMemoization(s string) int { + dp := make([]int, len(s) + 1) + dp[len(s)] = 1 + + var dfs func(i int) int + dfs = func(i int) int { + if dp[i] != 0 { + return dp[i] + } else if s[i] == '0' { + return 0 + } + + res := dfs(i + 1) + if i + 1 < len(s) && ( + s[i] == '1' || s[i] == '2' && (s[i + 1] >= '0' && s[i + 1] <= '6')) { + res += dfs(i + 2) + } + dp[i] = res + return res + } + + return dfs(0) +} + +func numDecodingsTabulation(s string) int { + dp := make([]int, len(s) + 1) + dp[len(s)] = 1 + for i := len(s) - 1; i >= 0; i-- { + if s[i] == '0' { + dp[i] = 0 + } else { + dp[i] = dp[i + 1] + } + + if i + 1 < len(s) && ( + s[i] == '1' || s[i] == '2' && (s[i + 1] >= '0' && s[i + 1] <= '6')) { + dp[i] += dp[i + 2] + } + } + return dp[0] +} diff --git a/go/0093-restore-ip-addresses.go b/go/0093-restore-ip-addresses.go new file mode 100644 index 000000000..557131c01 --- /dev/null +++ b/go/0093-restore-ip-addresses.go @@ -0,0 +1,44 @@ +package main + +import "strconv" + +func main() { + +} + +func restoreIpAddresses(s string) []string { + res := []string{} + + if len(s) > 12 { + return res + } + + var backtrack func(i, dots int, currentIP string) + + backtrack = func(i, dots int, currentIP string) { + if dots == 4 && i == len(s) { + res = append(res, currentIP[:len(currentIP)-1]) + return + } else if dots > 4 { + return + } + + + for j := i; j < min(i+3, len(s)); j++ { + val, _ := strconv.Atoi(s[i:j+1]) + if val < 256 && (i == j || s[i] != '0') { + backtrack(j+1 , dots + 1, currentIP + s[i:j+1] + ".") + } + } + } + + backtrack(0,0,"") + return res +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} \ No newline at end of file diff --git a/go/0094-binary-tree-inorder-traversal.go b/go/0094-binary-tree-inorder-traversal.go new file mode 100644 index 000000000..7afc21c61 --- /dev/null +++ b/go/0094-binary-tree-inorder-traversal.go @@ -0,0 +1,25 @@ +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func inorderTraversal(root *TreeNode) []int { + res := []int{} + stack := []*TreeNode{} + cur := root + + for cur != nil || len(stack) > 0 { + if cur != nil { + stack = append(stack, cur) + cur = cur.Left + } else { + cur, stack = stack[len(stack)-1], stack[:len(stack)-1] + res = append(res, cur.Val) + cur = cur.Right + } + } + return res +} diff --git a/go/0097-interleaving-string.go b/go/0097-interleaving-string.go new file mode 100644 index 000000000..dbf2eade7 --- /dev/null +++ b/go/0097-interleaving-string.go @@ -0,0 +1,37 @@ +var cache map[[2]int]bool +var st1 string +var st2 string +var st3 string + +func isInterleave(s1 string, s2 string, s3 string) bool { + if len(s1)+len(s2) != len(s3) { + return false + } + cache = make(map[[2]int]bool) + st1 = s1 + st2 = s2 + st3 = s3 + return dfs(0, 0) +} + +func dfs(i, j int) bool { + if i >= len(st1) && j >= len(st2) { + return true + } + + val, ok := cache[[2]int{i, j}] + + if ok { + return val + } + + if i < len(st1) && st1[i] == st3[i+j] && dfs(i+1, j) { + return true + } + if j < len(st2) && st2[j] == st3[i+j] && dfs(i, j+1) { + return true + } + + cache[[2]int{i, j}] = false + return false +} \ No newline at end of file diff --git a/go/0098-validate-binary-search-tree.go b/go/0098-validate-binary-search-tree.go new file mode 100644 index 000000000..429665116 --- /dev/null +++ b/go/0098-validate-binary-search-tree.go @@ -0,0 +1,19 @@ +func isValidBST(root *TreeNode) bool { + return isValid(root, nil, nil) +} + +func isValid(root, min, max *TreeNode) bool { + if root == nil { + return true + } + + if min != nil && root.Val <= min.Val { + return false + } + + if max != nil && root.Val >= max.Val { + return false + } + + return isValid(root.Left, min, root) && isValid(root.Right, root, max) +} \ No newline at end of file diff --git a/go/0100-same-tree.go b/go/0100-same-tree.go new file mode 100644 index 000000000..09889bf3c --- /dev/null +++ b/go/0100-same-tree.go @@ -0,0 +1,11 @@ +func isSameTree(p *TreeNode, q *TreeNode) bool { + if p == nil && q == nil { + return true + } + + if p == nil || q == nil || p.Val != q.Val { + return false + } + + return isSameTree(p.Left, q.Left) && isSameTree(p.Right, q.Right) +} \ No newline at end of file diff --git a/go/0102-binary-tree-level-order-traversal.go b/go/0102-binary-tree-level-order-traversal.go new file mode 100644 index 000000000..741d7572c --- /dev/null +++ b/go/0102-binary-tree-level-order-traversal.go @@ -0,0 +1,42 @@ +func levelOrder(root *TreeNode) [][]int { + // nil check + if root == nil { + return nil + } + + var result [][]int + level := 0 + q := make([]*TreeNode,1) + q[0] = root + for len(q) > 0 { + curLen := len(q) + //add elements to the queue per level + for i := 0; i < curLen; i++ { + //queue implementation : delete element from front(1st element of slice) + node := q[0] + q = q[1:] + + if len(result) <= level { + //create new slice with first value of the level and append to result matrix + result = append(result,[]int{node.Val}) + }else { + //add values to the slice created per level + result[level] = append(result[level],node.Val) + } + + //queue implementation : add element at Back( after last element of slice by append) + //add left node at queue + if node.Left != nil { + q = append(q,node.Left) + } + //add Right node at queue + if node.Right != nil{ + q = append(q,node.Right) + } + } + //increment the level after adding current elements to result + level++ + } + return result + + } diff --git a/go/0104-maximum-depth-of-binary-tree.go b/go/0104-maximum-depth-of-binary-tree.go new file mode 100644 index 000000000..e2c292b92 --- /dev/null +++ b/go/0104-maximum-depth-of-binary-tree.go @@ -0,0 +1,14 @@ +func maxDepth(root *TreeNode) int { + if root == nil { + return 0 + } + + left := maxDepth(root.Left) + right := maxDepth(root.Right) + + if left > right { + return 1 + left + } + + return right + 1 +} \ No newline at end of file diff --git a/go/0105-construct-binary-tree-from-preorder-and-inorder-traversal.go b/go/0105-construct-binary-tree-from-preorder-and-inorder-traversal.go new file mode 100644 index 000000000..e613e8748 --- /dev/null +++ b/go/0105-construct-binary-tree-from-preorder-and-inorder-traversal.go @@ -0,0 +1,20 @@ +func buildTree(preorder []int, inorder []int) *TreeNode { + if len(preorder) == 0 || len(inorder) == 0 { + return nil + } + + root := &TreeNode{Val: preorder[0]} + mid := index(inorder, preorder[0]) + root.Left = buildTree(preorder[1: mid + 1], inorder[:mid]) + root.Right = buildTree(preorder[mid + 1:], inorder[mid + 1:]) + return root +} + +func index(arr []int, val int) int { + for i, v := range arr { + if v == val { + return i + } + } + return -1 +} diff --git a/go/0108-convert-sorted-array-to-binary-search-tree.go b/go/0108-convert-sorted-array-to-binary-search-tree.go new file mode 100644 index 000000000..90802cf8c --- /dev/null +++ b/go/0108-convert-sorted-array-to-binary-search-tree.go @@ -0,0 +1,19 @@ +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func sortedArrayToBST(nums []int) *TreeNode { + if nums == nil || len(nums) == 0 { + return nil + } + mid := len(nums) / 2 + return &TreeNode{ + Val: nums[mid], + Left: sortedArrayToBST(nums[:mid]), + Right: sortedArrayToBST(nums[mid+1:]), + } +} diff --git a/go/0110-balanced-binary-tree.go b/go/0110-balanced-binary-tree.go new file mode 100644 index 000000000..1254efcfe --- /dev/null +++ b/go/0110-balanced-binary-tree.go @@ -0,0 +1,34 @@ +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +type BalanceTree struct { + Balance bool + Height int +} + +func isBalanced(root *TreeNode) bool { + return dfs(root).Balance +} + +func dfs(root *TreeNode) BalanceTree { + if root == nil { + return BalanceTree{true, 0} + } + + left, right := dfs(root.Left), dfs(root.Right) + balanced := (left.Balance && right.Balance && int(math.Abs(float64(left.Height)-float64(right.Height))) <= 1) + + return BalanceTree{balanced, 1 + max(left.Height, right.Height)} +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} \ No newline at end of file diff --git a/go/0112-path-sum.go b/go/0112-path-sum.go new file mode 100644 index 000000000..f6286e933 --- /dev/null +++ b/go/0112-path-sum.go @@ -0,0 +1,25 @@ +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ + +func hasPathSum(root *TreeNode, targetSum int) bool { + if root == nil { + return false + } + if isChild(root) && targetSum == root.Val { + return true + } + if hasPathSum(root.Left, targetSum-root.Val) || hasPathSum(root.Right, targetSum-root.Val) { + return true + } + return false +} + +func isChild(node *TreeNode) bool { + return node.Left == nil && node.Right == nil +} \ No newline at end of file diff --git a/go/0116-populating-next-right-pointers-in-each-node.go b/go/0116-populating-next-right-pointers-in-each-node.go new file mode 100644 index 000000000..7ba80d37e --- /dev/null +++ b/go/0116-populating-next-right-pointers-in-each-node.go @@ -0,0 +1,30 @@ +/** + * Definition for a Node. + * type Node struct { + * Val int + * Left *Node + * Right *Node + * Next *Node + * } + */ + +func connect(root *Node) *Node { + populate(root) + return root +} + +func populate(node *Node) { + if node == nil { + return + } + if node.Left == nil { + return + } + + node.Left.Next = node.Right + if node.Next != nil { + node.Right.Next = node.Next.Left + } + populate(node.Left) + populate(node.Right) +} diff --git a/go/0118-pascals-triangle.go b/go/0118-pascals-triangle.go new file mode 100644 index 000000000..360f7616b --- /dev/null +++ b/go/0118-pascals-triangle.go @@ -0,0 +1,23 @@ +func generate(rowIndex int) [][]int { + if rowIndex == 0 { + return [][]int{{1}} + } else { + return getAllRow(rowIndex - 1) + } +} + +func getAllRow(rowIndex int) [][]int { + if rowIndex == 0 { + return [][]int{{1}} + } + ListPrec := getAllRow(rowIndex - 1) + Len := len(ListPrec[len(ListPrec) - 1]) + ListPrec = append(ListPrec, []int{1}) + for i := 0; i < Len - 1; i++ { + ListPrec[len(ListPrec) - 1] = append( + ListPrec[len(ListPrec) - 1], + ListPrec[len(ListPrec) - 2][i] + ListPrec[len(ListPrec) - 2][i + 1]) + } + ListPrec[len(ListPrec) - 1] = append(ListPrec[len(ListPrec) - 1], 1) + return ListPrec +} diff --git a/go/0121-best-time-to-buy-and-sell-stock.go b/go/0121-best-time-to-buy-and-sell-stock.go new file mode 100644 index 000000000..45c110191 --- /dev/null +++ b/go/0121-best-time-to-buy-and-sell-stock.go @@ -0,0 +1,16 @@ +func maxProfit(prices []int) int { + min := math.MaxUint32 + res := 0 + + for _, price := range prices { + if price > min { + if price-min > res { + res = price - min + } + } else { + min = price + } + } + + return res +} \ No newline at end of file diff --git a/go/0122-best-time-to-buy-and-sell-stock-ii.go b/go/0122-best-time-to-buy-and-sell-stock-ii.go new file mode 100644 index 000000000..951e8fbcc --- /dev/null +++ b/go/0122-best-time-to-buy-and-sell-stock-ii.go @@ -0,0 +1,11 @@ +func maxProfit(prices []int) int { + var maxProfit int + + for i := 0; i < len(prices) - 1; i++ { + if prices[i] < prices[i+1] { + maxProfit += prices[i+1] - prices[i] + } + } + + return maxProfit +} diff --git a/go/0124-binary-tree-maximum-path-sum.go b/go/0124-binary-tree-maximum-path-sum.go new file mode 100644 index 000000000..68dd48059 --- /dev/null +++ b/go/0124-binary-tree-maximum-path-sum.go @@ -0,0 +1,33 @@ +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func maxPathSum(root *TreeNode) int { + arr := []int{math.MinInt32} + maxPathSumUtil(root, arr) + return arr[0] +} + +func maxPathSumUtil(root *TreeNode, arr []int) int { + if root == nil { + return 0 + } + + left := max(0, maxPathSumUtil(root.Left, arr)) + right := max(0, maxPathSumUtil(root.Right, arr)) + arr[0] = max(arr[0], root.Val+left+right) + + return root.Val + max(left, right) +} + +func max(a, b int) int { + if a > b { + return a + } + + return b +} \ No newline at end of file diff --git a/go/0125-valid-palindrome.go b/go/0125-valid-palindrome.go new file mode 100644 index 000000000..1b48665df --- /dev/null +++ b/go/0125-valid-palindrome.go @@ -0,0 +1,33 @@ +func isPalindrome(s string) bool { + i := 0 + j := len(s) - 1 + arr := []rune(s) + + for i < j { + left := unicode.ToLower(arr[i]) + right := unicode.ToLower(arr[j]) + + if !isLetterOrDigit(left) { + i++ + continue + } + + if !isLetterOrDigit(right) { + j-- + continue + } + + if left != right { + return false + } + + i++ + j-- + } + + return true +} + +func isLetterOrDigit(s rune) bool { + return unicode.IsLetter(s) || unicode.IsDigit(s) +} \ No newline at end of file diff --git a/go/0127-word-ladder.go b/go/0127-word-ladder.go new file mode 100644 index 000000000..4b39e9739 --- /dev/null +++ b/go/0127-word-ladder.go @@ -0,0 +1,47 @@ +func ladderLength(beginWord string, endWord string, wordList []string) int { + if !contains(wordList, endWord) { + return 0 + } + + nei := make(map[string][]string) + wordList = append(wordList, beginWord) + for _, word := range wordList { + for j := 0; j < len(word); j++ { + pattern := word[:j] + "*" + word[j + 1:] + nei[pattern] = append(nei[pattern], word) + } + } + + visit := map[string]bool{beginWord: true} + q := []string{beginWord} + res := 1 + for len(q) != 0 { + for tmp := len(q); tmp > 0; tmp-- { + word := q[0] + q = q[1:] + if word == endWord { + return res + } + for j := 0; j < len(word); j++ { + pattern := word[:j] + "*" + word[j + 1:] + for _, neiWord := range nei[pattern] { + if !visit[neiWord] { + visit[neiWord] = true + q = append(q, neiWord) + } + } + } + } + res += 1 + } + return 0 +} + +func contains(s []string, word string) bool { + for _, element := range s { + if element == word { + return true + } + } + return false +} diff --git a/go/0128-longest-consecutive-sequence.go b/go/0128-longest-consecutive-sequence.go new file mode 100644 index 000000000..1f5d3a988 --- /dev/null +++ b/go/0128-longest-consecutive-sequence.go @@ -0,0 +1,20 @@ +func longestConsecutive(nums []int) int { + set := make(map[int]bool) + longest := 0 + + for _, n := range nums { + set[n] = true + } + + for _, n := range nums { + if !set[n - 1] { + length := 1 + for set[n + length] { + length++ + } + longest = max(length, longest) + } + } + + return longest +} diff --git a/go/0129-sum-root-to-leaf-numbers.go b/go/0129-sum-root-to-leaf-numbers.go new file mode 100644 index 000000000..79ad7f057 --- /dev/null +++ b/go/0129-sum-root-to-leaf-numbers.go @@ -0,0 +1,38 @@ +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func sumNumbers(root *TreeNode) int { + if root == nil { + return 0 + } + var res []int + + dfs(root, 0, &res) + return sum(res...) +} + +func dfs(node *TreeNode, curSum int, res *[]int) { + if node == nil { + return + } + + curSum = (curSum * 10) + node.Val + if node.Left == nil && node.Right == nil { + *res = append(*res, curSum) + } + dfs(node.Left, curSum, res) + dfs(node.Right, curSum, res) +} + +func sum(nums ...int) int { + res := 0 + for _, n := range nums { + res += n + } + return res +} diff --git a/go/0130-surrounded-regions.go b/go/0130-surrounded-regions.go new file mode 100644 index 000000000..7f85fca2e --- /dev/null +++ b/go/0130-surrounded-regions.go @@ -0,0 +1,38 @@ +func solve(board [][]byte) { + m := len(board) + n := len(board[0]) + + for i := 0; i < n; i++ { + dfs(board, 0, i) + dfs(board, m-1, i) + } + + for i := 0; i < m; i++ { + dfs(board, i, 0) + dfs(board, i, n-1) + } + + for i := 0; i < m; i++ { + for j := 0; j < n; j++ { + if board[i][j] == 'O' { + board[i][j] = 'X' + } + + if board[i][j] == '*' { + board[i][j] = 'O' + } + } + } +} + +func dfs(board [][]byte, m int, n int) { + if m < 0 || m >= len(board) || n < 0 || n >= len(board[0]) || board[m][n] == 'X' || board[m][n] == '*' { + return + } + + board[m][n] = '*' + dfs(board, m+1, n) + dfs(board, m-1, n) + dfs(board, m, n+1) + dfs(board, m, n-1) +} \ No newline at end of file diff --git a/go/0131-palindrome-partitioning.go b/go/0131-palindrome-partitioning.go new file mode 100644 index 000000000..c37c4a519 --- /dev/null +++ b/go/0131-palindrome-partitioning.go @@ -0,0 +1,34 @@ +package main + +func partition(s string) [][]string { + ans := make([][]string, 0) + curr := make([]string, 0) + var backtrack func(idx int) + backtrack = func(idx int) { + if idx == len(s) { + ans = append(ans, append([]string{}, curr...)) + } + for i := idx; i < len(s); i++ { + if isPalindrome(s[idx : i+1]) { + curr = append(curr, s[idx:i+1]) + backtrack(i + 1) + curr = curr[:len(curr)-1] + } + } + } + backtrack(0) + return ans +} + +func isPalindrome(s string) bool { + l := 0 + r := len(s) - 1 + for l < r { + if s[l] != s[r] { + return false + } + l++ + r-- + } + return true +} diff --git a/go/0133-clone-graph.go b/go/0133-clone-graph.go new file mode 100644 index 000000000..d0a2cc7c4 --- /dev/null +++ b/go/0133-clone-graph.go @@ -0,0 +1,29 @@ +/** + * Definition for a Node. + * type Node struct { + * Val int + * Neighbors []*Node + * } + */ + +func cloneGraph(node *Node) *Node { + if node == nil { + return node + } + visited := map[*Node]*Node{} + return clone(node, visited) +} + +func clone(node *Node, visited map[*Node]*Node) *Node { + if _, ok := visited[node]; ok { + return visited[node] + } + + newNode := &Node{Val: node.Val} + visited[node] = newNode + + for _, n := range node.Neighbors { + newNode.Neighbors = append(newNode.Neighbors, clone(n, visited)) + } + return newNode +} diff --git a/go/0134-gas-station.go b/go/0134-gas-station.go new file mode 100644 index 000000000..599bb8852 --- /dev/null +++ b/go/0134-gas-station.go @@ -0,0 +1,27 @@ +func canCompleteCircuit(gas []int, cost []int) int { + sumGas := 0 + sumCost := 0 + + for idx, _ := range gas { + sumGas += gas[idx] + sumCost += cost[idx] + } + + if sumGas < sumCost { + return -1 + } + + res := 0 + total := 0 + + for idx, _ := range gas { + total += gas[idx] - cost[idx] + + if total < 0 { + total = 0 + res = idx + 1 + } + } + + return res +} \ No newline at end of file diff --git a/go/0136-single-number.go b/go/0136-single-number.go new file mode 100644 index 000000000..31d9ea384 --- /dev/null +++ b/go/0136-single-number.go @@ -0,0 +1,9 @@ +func singleNumber(nums []int) int { + res := 0 + + for _, num := range nums { + res ^= num + } + + return res +} \ No newline at end of file diff --git a/go/0138-copy-list-with-random-pointer.go b/go/0138-copy-list-with-random-pointer.go new file mode 100644 index 000000000..fbc1e06b7 --- /dev/null +++ b/go/0138-copy-list-with-random-pointer.go @@ -0,0 +1,18 @@ +func copyRandomList(head *Node) *Node { + oldToCopy := make(map[*Node]*Node) + + cur := head + for cur != nil { + clone := &Node{Val: cur.Val} + oldToCopy[cur] = clone + cur = cur.Next + } + cur = head + for cur != nil { + clone := oldToCopy[cur] + clone.Next = oldToCopy[cur.Next] + clone.Random = oldToCopy[cur.Random] + cur = cur.Next + } + return oldToCopy[head] +} diff --git a/go/0139-word-break.go b/go/0139-word-break.go new file mode 100644 index 000000000..db0429676 --- /dev/null +++ b/go/0139-word-break.go @@ -0,0 +1,16 @@ +func wordBreak(s string, wordDict []string) bool { + dp := make([]bool, len(s) + 1) + dp[len(s)] = true + + for i := len(s) - 1; i >= 0; i-- { + for _, w := range wordDict { + if (i + len(w)) <= len(s) && s[i : i + len(w)] == w { + dp[i] = dp[i + len(w)] + } + if dp[i] { + break + } + } + } + return dp[0] +} diff --git a/go/0141-linked-list-cycle.go b/go/0141-linked-list-cycle.go new file mode 100644 index 000000000..2683117ef --- /dev/null +++ b/go/0141-linked-list-cycle.go @@ -0,0 +1,15 @@ +func hasCycle(head *ListNode) bool { + if head == nil || head.Next == nil { + return false + } + slow := head + fast := head + for fast != nil && fast.Next != nil { + slow = slow.Next + fast = fast.Next.Next + if slow == fast { + return true + } + } + return false +} diff --git a/go/0143-reorder-list.go b/go/0143-reorder-list.go new file mode 100644 index 000000000..8d4aa634f --- /dev/null +++ b/go/0143-reorder-list.go @@ -0,0 +1,43 @@ +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ + func reorderList(head *ListNode) { + slow := head + fast := head.Next + + for fast != nil && fast.Next != nil { + fast = fast.Next.Next + slow = slow.Next + } + + reversed := reverse(slow.Next) + slow.Next = nil + + curr := head + + for curr != nil && reversed != nil { + next := curr.Next + revNext := reversed.Next + curr.Next = reversed + reversed.Next = next + curr = next + reversed = revNext + } +} + +func reverse(node *ListNode) *ListNode { + var prev, curr *ListNode = nil, node + + for curr != nil { + next := curr.Next + curr.Next = prev + prev = curr + curr = next + } + + return prev +} \ No newline at end of file diff --git a/go/0146-lru-cache.go b/go/0146-lru-cache.go new file mode 100644 index 000000000..120cb88f5 --- /dev/null +++ b/go/0146-lru-cache.go @@ -0,0 +1,62 @@ +type Node struct { + key int + val int + prev *Node + next *Node +} + +type LRUCache struct { + capacity int + cache map[int]*Node + left *Node + right *Node +} + + +func Constructor(capacity int) LRUCache { + ret := LRUCache { + capacity: capacity, + cache: make(map[int]*Node), + + left: &Node{key: 0, val: 0}, + right: &Node{key: 0, val: 0}} + ret.left.next, ret.right.prev = ret.right, ret.left + return ret +} + +func (this *LRUCache) Remove(node *Node) { + prev, nxt := node.prev, node.next + prev.next, nxt.prev = nxt, prev +} + +func (this *LRUCache) Insert(node *Node) { + prev, nxt := this.right.prev, this.right + nxt.prev = node + prev.next = nxt.prev + node.next, node.prev = nxt, prev +} + +func (this *LRUCache) Get(key int) int { + if _, ok := this.cache[key]; ok { + this.Remove(this.cache[key]) + this.Insert(this.cache[key]) + return this.cache[key].val + } + return -1 +} + + +func (this *LRUCache) Put(key int, value int) { + if _, ok := this.cache[key]; ok { + this.Remove(this.cache[key]) + } + this.cache[key] = &Node{key: key, val: value} + this.Insert(this.cache[key]) + + if len(this.cache) > this.capacity { + // remove from the list and delete the LRU from hashmap + lru := this.left.next + this.Remove(lru) + delete(this.cache, lru.key) + } +} diff --git a/go/0147-insertion-sort-list.go b/go/0147-insertion-sort-list.go new file mode 100644 index 000000000..043597f10 --- /dev/null +++ b/go/0147-insertion-sort-list.go @@ -0,0 +1,17 @@ +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func insertionSortList(head *ListNode) *ListNode { + dummy := new(ListNode) + for head != nil { + cur := dummy + for ; cur.Next != nil && cur.Next.Val < head.Val; cur = cur.Next { + } + cur.Next, head.Next, head = head, cur.Next, head.Next + } + return dummy.Next +} diff --git a/go/0150-evaluate-reverse-polish-notation.go b/go/0150-evaluate-reverse-polish-notation.go new file mode 100644 index 000000000..28812145c --- /dev/null +++ b/go/0150-evaluate-reverse-polish-notation.go @@ -0,0 +1,32 @@ +func evalRPN(tokens []string) int { + var stack []int + var a, b int + for _, c := range tokens { + switch c { + case "+": + a, b, stack = getAndPopLastOperand(stack) + stack = append(stack, (a + b)) + case "-": + a, b, stack = getAndPopLastOperand(stack) + stack = append(stack, (a - b)) + case "*": + a, b, stack = getAndPopLastOperand(stack) + stack = append(stack, (a * b)) + case "/": + a, b, stack = getAndPopLastOperand(stack) + stack = append(stack, (a / b)) + default: + i, _ := strconv.Atoi(c) + stack = append(stack, i) + } + } + return stack[0] +} + +func getAndPopLastOperand(stack []int) (int, int, []int) { + a := stack[len(stack)-2] + b := stack[len(stack)-1] + stack = stack[:len(stack)-2] + + return a, b, stack +} \ No newline at end of file diff --git a/go/0152-maximum-product-subarray.go b/go/0152-maximum-product-subarray.go new file mode 100644 index 000000000..b8a6a77fa --- /dev/null +++ b/go/0152-maximum-product-subarray.go @@ -0,0 +1,27 @@ +func maxProduct(nums []int) int { + res, curMin, curMax := nums[0], 1, 1 + + for i := 0; i < len(nums); i++ { + temp := curMax * nums[i] + curMax = max(max(nums[i] * curMax, nums[i] * curMin), nums[i]) + curMin = min(min(temp, nums[i] * curMin), nums[i]) + res = max(res, curMax) + } + return res +} + +// Golang does not have a built-in max for integers +func max(a int, b int) int { + if (a > b) { + return a + } + return b +} + +// Golang does not have a built-in min for integers +func min(a int, b int) int { + if (a < b) { + return a + } + return b +} \ No newline at end of file diff --git a/go/0153-find-minimum-in-rotated-sorted-array.go b/go/0153-find-minimum-in-rotated-sorted-array.go new file mode 100644 index 000000000..216837da4 --- /dev/null +++ b/go/0153-find-minimum-in-rotated-sorted-array.go @@ -0,0 +1,18 @@ +func findMin(nums []int) int { + res := nums[0] + l, r := 0, len(nums)-1 + + for l <= r { + p := (l+r) / 2 + if nums[p] >= nums[0] { + l = p+1 + } else { + if nums[p] < res { + res = nums[p] + } + r = p-1 + } + } + + return res +} diff --git a/go/0155-min-stack.go b/go/0155-min-stack.go new file mode 100644 index 000000000..d6878cc2f --- /dev/null +++ b/go/0155-min-stack.go @@ -0,0 +1,60 @@ +type MinStack struct { + top *StackNode + min int +} + +type StackNode struct { + data int + next *StackNode + lastmin int +} + +var mystack MinStack = MinStack{top: nil} +var newtop *StackNode + +func Constructor() MinStack { + return mystack +} + +func (this *MinStack) Push(val int) { + if this.top == nil { + newtop = &StackNode{data: val, next: this.top} + this.min = val + } else { + newtop = &StackNode{data: val, next: this.top, lastmin: this.min} + } + this.top = newtop + if this.top.data < this.min { + this.min = this.top.data + } +} + + +func (this *MinStack) Pop() { + if this.top.next == nil { + this.top = nil + return + } + this.min = this.top.lastmin + *this.top = *this.top.next +} + + +func (this *MinStack) Top() int { + return this.top.data +} + + +func (this *MinStack) GetMin() int { + return this.min; +} + + +/** + * Your MinStack object will be instantiated and called as such: + * obj := Constructor(); + * obj.Push(val); + * obj.Pop(); + * param_3 := obj.Top(); + * param_4 := obj.GetMin(); + */ diff --git a/go/0160-intersection-of-two-linked-lists.go b/go/0160-intersection-of-two-linked-lists.go new file mode 100644 index 000000000..2cf236064 --- /dev/null +++ b/go/0160-intersection-of-two-linked-lists.go @@ -0,0 +1,23 @@ +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func getIntersectionNode(headA, headB *ListNode) *ListNode { + a, b := headA, headB + for a != b { + if a == nil { + a = headB + } else { + a = a.Next + } + if b == nil { + b = headA + } else { + b = b.Next + } + } + return a +} \ No newline at end of file diff --git a/go/0167-two-sum-ii-input-array-is-sorted.go b/go/0167-two-sum-ii-input-array-is-sorted.go new file mode 100644 index 000000000..31c26a5ad --- /dev/null +++ b/go/0167-two-sum-ii-input-array-is-sorted.go @@ -0,0 +1,19 @@ +func twoSum(numbers []int, target int) []int { + p1 := 0 + p2 := len(numbers) - 1 + + for p1 < p2 { + n := numbers[p1] + numbers[p2] + if n == target { + return []int{p1 + 1, p2 + 1} + } + + if n > target { + p2-- + }else{ + p1++ + } + } + + return []int{0,0} +} diff --git a/go/0169-majority-element.go b/go/0169-majority-element.go new file mode 100644 index 000000000..d86f6b142 --- /dev/null +++ b/go/0169-majority-element.go @@ -0,0 +1,11 @@ +func majorityElement(nums []int) int { + counter := map[int]int{} + treshold := len(nums) / 2 + for _, n := range nums { + counter[n]++ + if counter[n] > treshold { + return n + } + } + return -1 +} diff --git a/go/0179-largest-number.go b/go/0179-largest-number.go new file mode 100644 index 000000000..4e9a97d88 --- /dev/null +++ b/go/0179-largest-number.go @@ -0,0 +1,8 @@ +func largestNumber(nums []int) string { + ans, s := "", make([]string, len(nums)) + for i, num := range nums { s[i] = strconv.Itoa(num) } + sort.Slice(s, func(a, b int) bool { return s[a] + s[b] > s[b] + s[a] }) + if s[0] == "0" { return "0" } + for _, v := range s { ans += v } + return ans +} \ No newline at end of file diff --git a/go/0187-repeated-dna-sequences.go b/go/0187-repeated-dna-sequences.go new file mode 100644 index 000000000..9d48b1058 --- /dev/null +++ b/go/0187-repeated-dna-sequences.go @@ -0,0 +1,12 @@ +func findRepeatedDnaSequences(s string) []string { + seen, res := map[string]bool{}, map[string]bool{} + + for l := range len(s) - 9 { + cur := s[l : l+10] + if seen[cur] { + res[cur] = true + } + seen[cur] = true + } + return slices.Collect(maps.Keys(res)) +} diff --git a/go/0189-rotate-array.go b/go/0189-rotate-array.go new file mode 100644 index 000000000..99d66dd42 --- /dev/null +++ b/go/0189-rotate-array.go @@ -0,0 +1,14 @@ +func rotate(nums []int, k int) { + k = k % len(nums) + reverse(nums, 0, len(nums) - 1) + reverse(nums, 0, k - 1) + reverse(nums, k, len(nums) - 1) +} + +func reverse(nums []int, start, end int) { + for start <= end { + nums[start], nums[end] = nums[end], nums[start] + start++ + end-- + } +} \ No newline at end of file diff --git a/go/0190-reverse-bits.go b/go/0190-reverse-bits.go new file mode 100644 index 000000000..8a6d2500a --- /dev/null +++ b/go/0190-reverse-bits.go @@ -0,0 +1,8 @@ +func reverseBits(num uint32) uint32 { + var res uint32 = 0 + for i := 0; i < 32; i++ { + bit := (num >> i) & 1 + res = res | (bit << (31 - i)) + } + return res +} \ No newline at end of file diff --git a/go/0191-number-of-1-bits.go b/go/0191-number-of-1-bits.go new file mode 100644 index 000000000..1e5f036eb --- /dev/null +++ b/go/0191-number-of-1-bits.go @@ -0,0 +1,8 @@ +func hammingWeight(num uint32) int { + res := 0 + for num > 0 { + num &= num - 1 + res += 1 + } + return res +} \ No newline at end of file diff --git a/go/0198-house-robber.go b/go/0198-house-robber.go new file mode 100644 index 000000000..114a30fd0 --- /dev/null +++ b/go/0198-house-robber.go @@ -0,0 +1,13 @@ +func rob(nums []int) int { + dp := make([] int, len(nums)+2) + + for i := 2; i < len(dp); i++ { + if dp[i-1] > dp[i-2] + nums[i-2] { + dp[i] = dp[i-1] + } else { + dp[i] = dp[i-2] + nums[i-2] + } + } + + return dp[len(dp)-1] +} \ No newline at end of file diff --git a/go/0199-binary-tree-right-side-view.go b/go/0199-binary-tree-right-side-view.go new file mode 100644 index 000000000..887133012 --- /dev/null +++ b/go/0199-binary-tree-right-side-view.go @@ -0,0 +1,23 @@ +func rightSideView(root *TreeNode) []int { + res := make([]int, 0) + q := []*TreeNode{root} + + for len(q) != 0 { + var rightSide *TreeNode + qLen := len(q) + + for i := 0; i < qLen; i++ { + node := q[0] + q = q[1:] + if node != nil { + rightSide = node + q = append(q, node.Left) + q = append(q, node.Right) + } + } + if rightSide != nil { + res = append(res, rightSide.Val) + } + } + return res +} diff --git a/go/0200-number-of-islands.go b/go/0200-number-of-islands.go new file mode 100644 index 000000000..73b0d26d5 --- /dev/null +++ b/go/0200-number-of-islands.go @@ -0,0 +1,31 @@ +func numIslands(grid [][]byte) int { + m, n := len(grid), len(grid[0]) + num_islands := 0 + + var dfs func (i int, j int) + dfs = func (i int, j int) { + + if i < 0 || i >= m { return } + if j < 0 || j >= n { return } + if grid[i][j] == '0' { return } + + // Mark as visited so dfs doesn't loop + grid[i][j] = '0' + + dfs(i - 1, j) + dfs(i + 1, j) + dfs(i, j - 1) + dfs(i, j + 1) + } + + for i, row := range grid { + for j, point := range row { + if point == '1' { + num_islands++ + dfs(i, j) + } + } + } + return num_islands +} + diff --git a/go/0202-happy-number.go b/go/0202-happy-number.go new file mode 100644 index 000000000..db214cd33 --- /dev/null +++ b/go/0202-happy-number.go @@ -0,0 +1,35 @@ +func isHappy(n int) bool { + + total := 0 + alreadySeen := make(map[int]bool) + for { + + // if we have seen this number + if seen := alreadySeen[n]; seen { + break + } + + alreadySeen[n] = true + + // split n into its individual digits. + strn := fmt.Sprint(n) + + // get the length + length := len(strn) + + // compute sum of digits + for i := 0; i < length; i++ { + digit, _ := strconv.Atoi(string(strn[i])) + total += (digit * digit) + } + if total == 1 { + return true + } + + // reassign n to total + n = total + total = 0 + } + +return false +} \ No newline at end of file diff --git a/go/0203-remove-linked-list-elements.go b/go/0203-remove-linked-list-elements.go new file mode 100644 index 000000000..0080f8db3 --- /dev/null +++ b/go/0203-remove-linked-list-elements.go @@ -0,0 +1,21 @@ +func removeElements(head *ListNode, val int) *ListNode { + if head == nil { + return nil + } + + curr := head + + for curr.Next != nil { + if curr.Next.Val == val { + curr.Next = curr.Next.Next + } else { + curr = curr.Next + } + } + + if head.Val == val { + return head.Next + } + + return head +} \ No newline at end of file diff --git a/go/0205-isomorphic-strings.go b/go/0205-isomorphic-strings.go new file mode 100644 index 000000000..a16a3e328 --- /dev/null +++ b/go/0205-isomorphic-strings.go @@ -0,0 +1,18 @@ +func isIsomorphic(s string, t string) bool { + if len(s) != len(t) { + return false + } + var charMapS, charMapT [128]byte + + for i := range s { + charS, charT := s[i], t[i] + if (charMapS[charS] != 0 && charMapS[charS] != charT) || + (charMapT[charT] != 0 && charMapT[charT] != charS) { + return false + } else { + charMapS[charS], charMapT[charT] = charT, charS + } + } + + return true +} diff --git a/go/0206-reverse-linked-list.go b/go/0206-reverse-linked-list.go new file mode 100644 index 000000000..fd7982649 --- /dev/null +++ b/go/0206-reverse-linked-list.go @@ -0,0 +1,32 @@ +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func reverseList(head *ListNode) *ListNode { + if head == nil || head.Next == nil { + return head + } + + reversedListHead := reverseList(head.Next) + head.Next.Next = head + head.Next = nil + return reversedListHead +} + +// Iterative version +// func reverseList(head *ListNode) *ListNode { +// var prev *ListNode +// curr := head +// +// for curr != nil { +// tmp := curr.Next +// curr.Next = prev +// prev = curr +// curr = tmp +// } +// +// return prev +// } \ No newline at end of file diff --git a/go/0207-course-schedule.go b/go/0207-course-schedule.go new file mode 100644 index 000000000..006f07841 --- /dev/null +++ b/go/0207-course-schedule.go @@ -0,0 +1,42 @@ +func canFinish(numCourses int, prerequisites [][]int) bool { + graph := make(map[int][]int) + + for _, prer := range prerequisites { + graph[prer[1]] = append(graph[prer[1]], prer[0]) + } + + visitSet := make(map[int]struct{}) + + var dfs func(course int) bool + + dfs = func(course int) bool { + if _, ok := visitSet[course]; ok { + return false + } + + if len(graph[course]) == 0 { + return true + } + + visitSet[course] = struct{}{} + + for _, pre := range graph[course] { + if !dfs(pre) { + return false + } + } + delete(visitSet, course) + + graph[course] = []int{} + + return true + } + + for i := 0; i < numCourses; i++ { + if !dfs(i) { + return false + } + } + + return true +} diff --git a/go/0208-implement-trie-prefix-tree.go b/go/0208-implement-trie-prefix-tree.go new file mode 100644 index 000000000..b842e29cc --- /dev/null +++ b/go/0208-implement-trie-prefix-tree.go @@ -0,0 +1,54 @@ +type Trie struct { + children [26]*Trie + endOfWord bool + root *Trie +} + +func Constructor() Trie { + return Trie{ + children: [26]*Trie{}, + endOfWord: false, + root: &Trie{}} +} + +func (this *Trie) Insert(word string) { + cur := this.root + + for i := 0; i < len(word); i++ { + index := word[i] - 'a' + if cur.children[index] == nil { + cur.children[index] = &Trie{} + } + cur = cur.children[index] + } + + cur.endOfWord = true +} + +func (this *Trie) Search(word string) bool { + cur := this.root + + for i := 0; i < len(word); i++ { + index := word[i] - 'a' + if cur.children[index] == nil { + return false + } + cur = cur.children[index] + } + + return cur.endOfWord +} + +func (this *Trie) StartsWith(prefix string) bool { + cur := this.root + + for i := 0; i < len(prefix); i++ { + index := prefix[i] - 'a' + if cur.children[index] == nil { + return false + } + cur = cur.children[index] + } + + return true +} diff --git a/go/0209-minimum-size-subarray-sum.go b/go/0209-minimum-size-subarray-sum.go new file mode 100644 index 000000000..6253e06b7 --- /dev/null +++ b/go/0209-minimum-size-subarray-sum.go @@ -0,0 +1,25 @@ +/* +Time: O(n) +Space: O(1) +*/ + +func minSubArrayLen(target int, nums []int) int { + minSize := len(nums) + 1 + L := 0 + curSum := 0 + + for R := range nums { + curSum += nums[R] + for curSum >= target { + if R+1-L < minSize { + minSize = R + 1 - L + } + curSum -= nums[L] + L += 1 + } + } + if minSize == len(nums)+1 { + return 0 + } + return minSize +} diff --git a/go/0210-course-schedule-ii.go b/go/0210-course-schedule-ii.go new file mode 100644 index 000000000..e4eeea6bd --- /dev/null +++ b/go/0210-course-schedule-ii.go @@ -0,0 +1,41 @@ +const CRS = 0 +const PRE = 1 +func findOrder(numCourses int, prerequisites [][]int) []int { + prereq := make([][]int, 0) + for i := 0; i < numCourses; i++ { + prereq = append(prereq, make([]int, 0)) + } + for _, edge := range prerequisites { + prereq[edge[CRS]] = append(prereq[edge[CRS]], edge[PRE]) + } + + output := make([]int, 0) + visit, cycle := make([]bool, numCourses), make([]bool, numCourses) + + var dfs func(int) bool + dfs = func(crs int) bool { + if cycle[crs] { + return false + } else if visit[crs] { + return true + } + + cycle[crs] = true + for _, pre := range prereq[crs] { + if !dfs(pre) { + return false + } + } + cycle[crs] = false + visit[crs] = true + output = append(output, crs) + return true + } + + for c := 0; c < numCourses; c++ { + if dfs(c) == false { + return []int{} + } + } + return output +} diff --git a/go/0211-design-add-and-search-words-data-structure.go b/go/0211-design-add-and-search-words-data-structure.go new file mode 100644 index 000000000..1540fc5ef --- /dev/null +++ b/go/0211-design-add-and-search-words-data-structure.go @@ -0,0 +1,53 @@ +type TrieNode struct { + children map[byte]*TrieNode + word bool +} + +type WordDictionary struct { + root *TrieNode +} + + +func Constructor() WordDictionary { + return WordDictionary{root: &TrieNode{children: make(map[byte]*TrieNode)}} +} + + +func (this *WordDictionary) AddWord(word string) { + cur := this.root + for c := 0; c < len(word); c++ { + if _, ok := cur.children[word[c]]; !ok { + cur.children[word[c]] = &TrieNode{children: make(map[byte]*TrieNode)} + } + cur = cur.children[word[c]] + } + cur.word = true +} + + +func (this *WordDictionary) Search(word string) bool { + var dfs func(int, *TrieNode) bool + dfs = func(j int, root *TrieNode) bool { + cur := root + + for i := j; i < len(word); i++ { + c := word[i] + if c == '.' { + for _, child := range cur.children { + if dfs(i + 1, child) { + return true + } + } + return false + } else { + if _, ok := cur.children[c]; !ok { + return false + } + cur = cur.children[c] + } + } + return cur.word + } + + return dfs(0, this.root) +} diff --git a/go/0212-word-search-ii.go b/go/0212-word-search-ii.go new file mode 100644 index 000000000..b158f541d --- /dev/null +++ b/go/0212-word-search-ii.go @@ -0,0 +1,82 @@ +type TrieNode struct { + children map[byte]*TrieNode + isWord bool + refs int +} + +func (this *TrieNode) addWord(word string) { + cur := this + cur.refs += 1 + for c := 0; c < len(word); c++ { + if _, ok := cur.children[word[c]]; !ok { + cur.children[word[c]] = &TrieNode{children: make(map[byte]*TrieNode)} + } + cur = cur.children[word[c]] + cur.refs += 1 + } + cur.isWord = true +} + +func (this *TrieNode) removeWord(word string) { + cur := this + cur.refs += 1 + for c := 0; c < len(word); c++ { + if _, ok := cur.children[word[c]]; ok { + cur = cur.children[word[c]]; + cur.refs -= 1 + } + } +} + +func findWords(board [][]byte, words []string) []string { + root := &TrieNode{children: make(map[byte]*TrieNode)} + for _, w := range words { + root.addWord(w); + } + + ROWS, COLS := len(board), len(board[0]) + res := make(map[string]bool) + visit := make(map[int]bool) + + var dfs func(int, int, *TrieNode, string) + dfs = func(r, c int, node *TrieNode, word string) { + if + r < 0 || r >= ROWS || + c < 0 || c >= COLS || + node.children[board[r][c]] == nil || + node.children[board[r][c]].refs < 1 || + visit[r*COLS + c] { + return + } + + visit[r*COLS + c] = true + node = node.children[board[r][c]] + word += string(board[r][c]) + if node.isWord { + node.isWord = false + res[word] = true + root.removeWord(word) + } + + dfs(r + 1, c, node, word) + dfs(r - 1, c, node, word) + dfs(r, c + 1, node, word) + dfs(r, c - 1, node, word) + visit[r*COLS + c] = false + } + + for r := 0; r < ROWS; r++ { + for c := 0; c < COLS; c++ { + dfs(r, c, root, "") + } + } + return list(res) +} + +func list(mapping map[string]bool) []string { + res := make([]string, 0, len(mapping)) + for key, _ := range mapping { + res = append(res, key) + } + return res +} diff --git a/go/0213-house-robber-ii.go b/go/0213-house-robber-ii.go new file mode 100644 index 000000000..d87dae4dc --- /dev/null +++ b/go/0213-house-robber-ii.go @@ -0,0 +1,33 @@ +func rob(nums []int) int { + n := len(nums) + + if n == 1 { + return nums[0] + } + + if n == 2 { + return max(nums[0], nums[1]) + } + + return max(robUtil(nums, 0, n-1), robUtil(nums, 1, n)) +} + +func robUtil(nums []int, st, end int) int { + dp := make([]int, len(nums)) + dp[st] = nums[st] + dp[st+1] = max(nums[st], nums[st+1]) + + for i := st+2; i < end; i++ { + dp[i] = max(nums[i]+dp[i-2], dp[i-1]) + } + + return dp[end-1] +} + +func max(a, b int) int { + if a > b { + return a + } + + return b +} \ No newline at end of file diff --git a/go/0215-kth-largest-element-in-an-array.go b/go/0215-kth-largest-element-in-an-array.go new file mode 100644 index 000000000..6f2607ec5 --- /dev/null +++ b/go/0215-kth-largest-element-in-an-array.go @@ -0,0 +1,4 @@ +func findKthLargest(nums []int, k int) int { + sort.Ints(nums) + return nums[len(nums)-k] +} \ No newline at end of file diff --git a/go/0217-contains-duplicate.go b/go/0217-contains-duplicate.go new file mode 100644 index 000000000..81b409b61 --- /dev/null +++ b/go/0217-contains-duplicate.go @@ -0,0 +1,17 @@ +func containsDuplicate(nums []int) bool { + if len(nums) <= 1 { + return false + } + + xm := make(map[int]struct{}) + + for _, v := range nums { + if _, ok := xm[v]; ok { + return true + } + + xm[v] = struct{}{} + } + + return false +} diff --git a/go/0219-contains-duplicate-ii.go b/go/0219-contains-duplicate-ii.go new file mode 100644 index 000000000..9532b53f4 --- /dev/null +++ b/go/0219-contains-duplicate-ii.go @@ -0,0 +1,11 @@ +func containsNearbyDuplicate(nums []int, k int) bool { + seen := make(map[int]int) + + for i, n := range nums { + if j, ok := seen[n]; ok && i - j <= k { + return true + } + seen[n] = i + } + return false +} diff --git a/go/0225-implement-stack-using-queues.go b/go/0225-implement-stack-using-queues.go new file mode 100644 index 000000000..fdece80c3 --- /dev/null +++ b/go/0225-implement-stack-using-queues.go @@ -0,0 +1,41 @@ +type MyStack struct { + queue []int +} + + +func Constructor() MyStack { + return MyStack{} +} + + +func (this *MyStack) Push(x int) { + this.queue = append(this.queue, x) +} + + +func (this *MyStack) Pop() int { + if this.Empty() { + return 0 + } + + elem := this.queue[len(this.queue) - 1] + + this.queue = this.queue[:len(this.queue) - 1] + + return elem +} + + +func (this *MyStack) Top() int { + if this.Empty() { + return 0 + } + + return this.queue[len(this.queue) - 1] +} + + +func (this *MyStack) Empty() bool { + return len(this.queue) == 0 +} + diff --git a/go/0226-invert-binary-tree.go b/go/0226-invert-binary-tree.go new file mode 100644 index 000000000..40d0e0e7d --- /dev/null +++ b/go/0226-invert-binary-tree.go @@ -0,0 +1,19 @@ +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func invertTree(root *TreeNode) *TreeNode { + if root == nil { + return root + } + + left := invertTree(root.Left) + root.Left = invertTree(root.Right) + root.Right = left + + return root +} \ No newline at end of file diff --git a/go/0230-kth-smallest-element-in-a-bst.go b/go/0230-kth-smallest-element-in-a-bst.go new file mode 100644 index 000000000..e46393efa --- /dev/null +++ b/go/0230-kth-smallest-element-in-a-bst.go @@ -0,0 +1,22 @@ +func kthSmallest(root *TreeNode, k int) int { + stack := make([]*TreeNode, 0, k) + + for { + for root != nil { + stack = append(stack, root) + root = root.Left + } + + root = stack[len(stack) - 1] + stack = stack[:len(stack) - 1] + + k-- + if k == 0 { + return root.Val + } + + root = root.Right + } + + return -1 +} diff --git a/go/0234-palindrome-linked-list.go b/go/0234-palindrome-linked-list.go new file mode 100644 index 000000000..fa8421737 --- /dev/null +++ b/go/0234-palindrome-linked-list.go @@ -0,0 +1,48 @@ +/** +Move slow and fast pointer along the list. +Slow pointer nodes are added to reversed list. +When no more fast move is possible, iterate +along slow and back along reversed list while +checking for value equality + +Time: O(N) +Space: O(1) +**/ + +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func isPalindrome(head *ListNode) bool { + slow, fast := head, head + var rev *ListNode + + for fast != nil && fast.Next != nil { + fast = fast.Next.Next + next_slow := slow.Next + slow.Next = rev + rev = slow + slow = next_slow + } + + // if fast is not null, slow is middle of + // odd length list which is skipped + // if fast is null, slow is first element of + // the 2nd half of even length list + if fast != nil { + slow = slow.Next + } + + for slow != nil { + if slow.Val != rev.Val { + return false + } + slow = slow.Next + rev = rev.Next + } + + return true +} diff --git a/go/0235-lowest-common-ancestor-of-a-binary-search-tree.go b/go/0235-lowest-common-ancestor-of-a-binary-search-tree.go new file mode 100644 index 000000000..1d4f02182 --- /dev/null +++ b/go/0235-lowest-common-ancestor-of-a-binary-search-tree.go @@ -0,0 +1,20 @@ +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ + +func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode { + if p.Val > root.Val && q.Val > root.Val { + return lowestCommonAncestor(root.Right, p, q) + } + + if p.Val < root.Val && q.Val < root.Val { + return lowestCommonAncestor(root.Left, p, q) + } + + return root +} diff --git a/go/0236-lowest-common-ancestor-of-binary-tree.go b/go/0236-lowest-common-ancestor-of-binary-tree.go new file mode 100644 index 000000000..ee582da0d --- /dev/null +++ b/go/0236-lowest-common-ancestor-of-binary-tree.go @@ -0,0 +1,26 @@ +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode { + if root == nil || p == root || q == root { + return root + } + + left := lowestCommonAncestor(root.Left, p, q) + right := lowestCommonAncestor(root.Right, p, q) + + if left == nil { + return right + } + + if right == nil { + return left + } + + return root +} \ No newline at end of file diff --git a/go/0238-product-of-array-except-self.go b/go/0238-product-of-array-except-self.go new file mode 100644 index 000000000..9de010089 --- /dev/null +++ b/go/0238-product-of-array-except-self.go @@ -0,0 +1,16 @@ +func productExceptSelf(nums []int) []int { + res := make([]int, len(nums)) + + prefix := 1 + for i, num := range nums { + res[i] = prefix + prefix *= num + } + + postfix := 1 + for i := len(nums) - 1; i >= 0; i-- { + res[i] *= postfix + postfix *= nums[i] + } + return res +} diff --git a/go/0239-sliding-window-maximum.go b/go/0239-sliding-window-maximum.go new file mode 100644 index 000000000..f57f12629 --- /dev/null +++ b/go/0239-sliding-window-maximum.go @@ -0,0 +1,28 @@ +func maxSlidingWindow(nums []int, k int) []int { + // get top/last element of queue: q[len(q)-1] + // pop/remove from the top/last element of queue: q[:len(q)-1] + // remove left value from queue: q[1:] + output := []int{} + q := make([]int, 0) + l, r := 0, 0 + + for r < len(nums) { + // pop smaller values from q + for len(q) != 0 && nums[q[len(q)-1]] < nums[r] { + q = q[:len(q)-1] + } + q = append(q, r) + + // remove left val from window + if l > q[0] { + q = q[1:] + } + + if (r + 1) >= k { + output = append(output, nums[q[0]]) + l++ + } + r++ + } + return output +} \ No newline at end of file diff --git a/go/0242-valid-anagram.go b/go/0242-valid-anagram.go new file mode 100644 index 000000000..d80708202 --- /dev/null +++ b/go/0242-valid-anagram.go @@ -0,0 +1,20 @@ +func isAnagram(s string, t string) bool { + if len(s) != len(t) { + return false + } + + var freq [26]int + + for idx := 0; idx < len(s); idx++ { + freq[s[idx] - 'a']++ + freq[t[idx] - 'a']-- + } + + for idx := 0; idx < len(freq); idx++ { + if freq[idx] != 0 { + return false + } + } + + return true +} \ No newline at end of file diff --git a/go/0252-meeting-rooms.go b/go/0252-meeting-rooms.go new file mode 100644 index 000000000..8f0e84645 --- /dev/null +++ b/go/0252-meeting-rooms.go @@ -0,0 +1,27 @@ +/** + * Definition of Interval: + * type Interval struct { + * Start, End int + * } + */ + +/** + * @param intervals: an array of meeting time intervals + * @return: if a person could attend all meetings + */ + +import "sort" + +func CanAttendMeetings(intervals []*Interval) bool { + // Write your code here + sort.Slice(intervals, func(i, j int) bool { + return intervals[i].Start < intervals[j].Start + }) + + for i := 1; i < len(intervals); i++ { + if intervals[i].Start < intervals[i - 1].End { + return false + } + } + return true +} diff --git a/go/0263-ugly-number.go b/go/0263-ugly-number.go new file mode 100644 index 000000000..024cee09c --- /dev/null +++ b/go/0263-ugly-number.go @@ -0,0 +1,13 @@ +func isUgly(n int) bool { + if n <= 0 { + return false + } + + primes := []int{2, 3, 5} + for _, p := range primes { + for n%p == 0 { + n /= p + } + } + return n == 1 +} \ No newline at end of file diff --git a/go/0268-missing-number.go b/go/0268-missing-number.go new file mode 100644 index 000000000..c77b7e46b --- /dev/null +++ b/go/0268-missing-number.go @@ -0,0 +1,8 @@ +func missingNumber(nums []int) int { + res := len(nums) + + for i := 0; i < len(nums); i++ { + res += i - nums[i] + } + return res +} \ No newline at end of file diff --git a/go/0271-encode-and-decode-strings.go b/go/0271-encode-and-decode-strings.go new file mode 100644 index 000000000..0a758973c --- /dev/null +++ b/go/0271-encode-and-decode-strings.go @@ -0,0 +1,56 @@ +import ( + "strings" + "strconv" +) + +type Codec struct { + b strings.Builder +} + +// Encodes a list of strings to a single string. +func (codec *Codec) Encode(strs []string) string { + defer codec.b.Reset() + + for _, word := range strs { + codec.b.WriteString(strconv.Itoa(len(word))) + codec.b.WriteRune('|') + codec.b.WriteString(word) + } + + return codec.b.String() +} + +// Decodes a single string to a list of strings. +func (codec *Codec) Decode(strs string) []string { + var words []string + + for i := 0; i < len(strs); { + lenStart := i + lenEnd := i + + for strs[lenEnd] != '|' { + lenEnd++ + } + + var l int + dec := 1 + + for j := lenEnd - 1; j >= lenStart; j-- { + l += (int(strs[j]) - 48) * dec + dec *= 10 + } + + start := lenEnd + 1 + end := start + l + + words = append(words, string(strs[start:end])) + + i = end + } + + return words +} + +// Your Codec object will be instantiated and called as such: +// var codec Codec +// codec.Decode(codec.Encode(strs)); \ No newline at end of file diff --git a/go/0279-perfect-squares.go b/go/0279-perfect-squares.go new file mode 100644 index 000000000..98872f097 --- /dev/null +++ b/go/0279-perfect-squares.go @@ -0,0 +1,27 @@ +var MX = 10005 + +func numSquares(n int) int { + dp := make([]int, n+1) + for i := range dp { + dp[i] = MX + } + + dp[0] = 0 + for i := 1; i <= n; i++ { + mn := MX + j := 1 + for i-j*j >= 0 { + mn = min(mn, dp[i-j*j]+1) + j++ + } + dp[i] = mn + } + return dp[n] +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} diff --git a/go/0283-move-zeroes.go b/go/0283-move-zeroes.go new file mode 100644 index 000000000..feffb0633 --- /dev/null +++ b/go/0283-move-zeroes.go @@ -0,0 +1,8 @@ +func moveZeroes(nums []int) { + for l, r := 0, 0; r < len(nums); r++ { + if nums[r] != 0 { + nums[l], nums[r] = nums[r], nums[l] + l++ + } + } +} \ No newline at end of file diff --git a/go/0287-find-the-duplicate-number.go b/go/0287-find-the-duplicate-number.go new file mode 100644 index 000000000..759104305 --- /dev/null +++ b/go/0287-find-the-duplicate-number.go @@ -0,0 +1,15 @@ +func findDuplicate(nums []int) int { + slow, fast := nums[0], nums[nums[0]] + + for slow != fast { + slow = nums[slow] + fast = nums[nums[fast]] + } + + slow = 0 + for slow != fast { + slow = nums[slow] + fast = nums[fast] + } + return slow +} \ No newline at end of file diff --git a/go/0290-word-pattern.go b/go/0290-word-pattern.go new file mode 100644 index 000000000..05f257360 --- /dev/null +++ b/go/0290-word-pattern.go @@ -0,0 +1,20 @@ +func wordPattern(pattern string, s string) bool { + words := strings.Split(s, " ") + if len(pattern) != len(words) { + return false + } + + seenChr, seenWord := make(map[byte]int, len(pattern)), make(map[string]int, len(pattern)) + for i := 0; i < len(pattern); i++ { + chrPos, chrOk := seenChr[pattern[i]] + wordPos, wordOk := seenWord[words[i]] + if chrOk != wordOk || chrPos != wordPos { + return false + } + + seenChr[pattern[i]] = i + seenWord[words[i]] = i + } + + return true +} \ No newline at end of file diff --git a/go/0295-find-median-from-data-stream.go b/go/0295-find-median-from-data-stream.go new file mode 100644 index 000000000..99a8d1bd4 --- /dev/null +++ b/go/0295-find-median-from-data-stream.go @@ -0,0 +1,50 @@ +type IntHeap []int +func (h IntHeap) Len() int { return len(h) } +func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] } +func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } +func (h *IntHeap) Push(x interface{}) {*h = append(*h, x.(int))} +func (h *IntHeap) Pop() interface{} { + old := *h + n := len(old) + x := old[n-1] + *h = old[0 : n-1] + return x +} + +type MedianFinder struct { + small IntHeap + large IntHeap +} + + +func Constructor() MedianFinder { + return MedianFinder{small: IntHeap{}, large: IntHeap{}} +} + + +func (this *MedianFinder) AddNum(num int) { + if len(this.large) > 0 && num > this.large[0] { + heap.Push(&this.large, num) + } else { + heap.Push(&this.small, -1 * num) + } + + if len(this.small) > len(this.large) + 1 { + val := -1 * heap.Pop(&this.small).(int) + heap.Push(&this.large, val) + } + if len(this.large) > len(this.small) + 1 { + val := heap.Pop(&this.large).(int) + heap.Push(&this.small, -1 * val) + } +} + + +func (this *MedianFinder) FindMedian() float64 { + if len(this.small) > len(this.large) { + return float64(-1 * this.small[0]) + } else if len(this.large) > len(this.small) { + return float64(this.large[0]) + } + return float64(-1 * this.small[0] + this.large[0]) / 2 +} diff --git a/go/0297-serialize-and-deserialize-binary-tree.go b/go/0297-serialize-and-deserialize-binary-tree.go new file mode 100644 index 000000000..0c16638f7 --- /dev/null +++ b/go/0297-serialize-and-deserialize-binary-tree.go @@ -0,0 +1,45 @@ +type Codec struct { +} + +func Constructor() Codec { + return Codec{} +} + +func (this *Codec) serialize(root *TreeNode) string { + res := make([]string, 0) + + var dfs func(node* TreeNode) + dfs = func(node *TreeNode) { + if node == nil{ + res = append(res, "N") + return + } + res = append(res, strconv.Itoa(node.Val)) + dfs(node.Left) + dfs(node.Right) + } + + dfs(root) + return strings.Join(res, ",") +} + +func (this *Codec) deserialize(data string) *TreeNode { + vals := strings.Split(data, ",") + i := 0 + + var dfs func() *TreeNode + dfs = func() *TreeNode { + if vals[i] == "N" { + i += 1 + return nil + } + val, _ := strconv.Atoi(vals[i]) + node := &TreeNode{Val: val} + i += 1 + node.Left = dfs() + node.Right = dfs() + return node + } + + return dfs() +} diff --git a/go/0300-longest-increasing-subsequence.go b/go/0300-longest-increasing-subsequence.go new file mode 100644 index 000000000..e06222247 --- /dev/null +++ b/go/0300-longest-increasing-subsequence.go @@ -0,0 +1,19 @@ +func lengthOfLIS(nums []int) int { + cache := make([]int, len(nums)) + LIS := 0 + for i := range nums { + curMax := 0 + for j := 0; j < i+1; j++ { + if nums[j] < nums[i] { + if curMax < cache[j] { + curMax = cache[j] + } + } + } + cache[i] = curMax + 1 + if cache[i] > LIS { + LIS = cache[i] + } + } + return LIS +} diff --git a/go/0303-range-sum-query.go b/go/0303-range-sum-query.go new file mode 100644 index 000000000..341e5b0cd --- /dev/null +++ b/go/0303-range-sum-query.go @@ -0,0 +1,27 @@ +type NumArray struct { + nums []int +} + +func Constructor(nums []int) NumArray { + for i := 1; i < len(nums); i++ { + nums[i] += nums[i-1] + } + return NumArray{nums} +} + +func (this *NumArray) SumRange(left int, right int) int { + var a int + if left-1 < 0 { + a = 0 + } else { + a = this.nums[left-1] + } + b := this.nums[right] + return b - a +} + +/** + * Your NumArray object will be instantiated and called as such: + * obj := Constructor(nums); + * param_1 := obj.SumRange(left,right); + */ \ No newline at end of file diff --git a/go/0309-best-time-to-buy-and-sell-stock-with-cooldown.go b/go/0309-best-time-to-buy-and-sell-stock-with-cooldown.go new file mode 100644 index 000000000..975bbe3e9 --- /dev/null +++ b/go/0309-best-time-to-buy-and-sell-stock-with-cooldown.go @@ -0,0 +1,19 @@ +func maxProfit(prices []int) int { + sold, hold, rest := 0, math.MinInt32, 0 + + for i := 0; i < len(prices); i++ { + prevSold := sold + sold = hold + prices[i] + hold = max(hold, rest - prices[i]) + rest = max(rest, prevSold) + } + return max(sold, rest) +} + +// Golang does not have a built-in max for integers +func max(a int, b int) int { + if (a > b) { + return a + } + return b +} \ No newline at end of file diff --git a/go/0322-coin-change.go b/go/0322-coin-change.go new file mode 100644 index 000000000..48467c1ee --- /dev/null +++ b/go/0322-coin-change.go @@ -0,0 +1,26 @@ +func coinChange(coins []int, amount int) int { + changes := make([]int, amount+1) + for i := range changes { + changes[i] = amount + 1 + } + changes[0] = 0 + + for i := 1; i < amount+1; i++ { + for _, c := range coins { + if c <= i { + changes[i] = min(changes[i], 1+changes[i-c]) + } + } + } + if changes[amount] != amount+1 { + return changes[amount] + } + return -1 +} + +func min(x, y int) int { + if x < y { + return x + } + return y +} diff --git a/go/0323-number-of-connected-components-in-an-undirected-graph.go b/go/0323-number-of-connected-components-in-an-undirected-graph.go new file mode 100644 index 000000000..d1c3cc8c9 --- /dev/null +++ b/go/0323-number-of-connected-components-in-an-undirected-graph.go @@ -0,0 +1,37 @@ +func countComponents(n int, edges [][]int) int { + if n == 1 { + return 1 + } + + var components int + + visited := make([]bool, n) + + graph := make(map[int][]int) + + for _, edge := range edges { + graph[edge[0]] = append(graph[edge[0]], edge[1]) + graph[edge[1]] = append(graph[edge[1]], edge[0]) + } + + var dfs func(i int) + + dfs = func(i int) { + visited[i] = true + + for _, j := range graph[i] { + if !visited[j] { + dfs(j) + } + } + } + + for i := 0; i < n; i++ { + if !visited[i] { + components++ + dfs(i) + } + } + + return components +} diff --git a/go/0332-reconstruct-itinerary.go b/go/0332-reconstruct-itinerary.go new file mode 100644 index 000000000..16a526156 --- /dev/null +++ b/go/0332-reconstruct-itinerary.go @@ -0,0 +1,48 @@ +func findItinerary(tickets [][]string) []string { + + adjMap := make(map[string][]string) + + for i := 0; i < len(tickets); i++ { + adjMap[tickets[i][0]] = append(adjMap[tickets[i][0]], tickets[i][1]) + } + + for i := 0; i < len(tickets); i++ { + sort.Strings(adjMap[tickets[i][0]]) + } + + res := []string{"JFK"} + + var dfs func(source string) bool + + dfs = func(src string) bool { + + if len(res) == len(tickets)+1 { + return true + } + + if _, ok := adjMap[src]; !ok { + return false + } + + destinationList := adjMap[src] + + for i, destination := range destinationList { + + adjMap[src] = append(adjMap[src][:i], adjMap[src][i+1:]...) + + res = append(res, destination) + if dfs(destination) == true { + return true + } + adjMap[src] = append(adjMap[src][:i+1], adjMap[src][i:]...) + adjMap[src][i] = destination + + res = res[:len(res)-1] + } + + return false + } + + dfs("JFK") + return res +} diff --git a/go/0338-counting-bits.go b/go/0338-counting-bits.go new file mode 100644 index 000000000..19d014359 --- /dev/null +++ b/go/0338-counting-bits.go @@ -0,0 +1,12 @@ +func countBits(n int) []int { + dp := make([]int, n + 1) + offset := 1 + + for i := 1; i <= n; i++ { + if offset * 2 == i { + offset = i + } + dp[i] = 1 + dp[i - offset] + } + return dp +} \ No newline at end of file diff --git a/go/0344-reverse-string.go b/go/0344-reverse-string.go new file mode 100644 index 000000000..9cc97fd90 --- /dev/null +++ b/go/0344-reverse-string.go @@ -0,0 +1,9 @@ +func reverseString(s []byte) { + L, R := 0, len(s)-1 + + for L < len(s)/2 { + s[L], s[R] = s[R], s[L] + L++ + R-- + } +} diff --git a/go/0347-top-k-frequent-elements.go b/go/0347-top-k-frequent-elements.go new file mode 100644 index 000000000..e270050e9 --- /dev/null +++ b/go/0347-top-k-frequent-elements.go @@ -0,0 +1,20 @@ +func topKFrequent(nums []int, k int) []int { + var res []int + countMap := make(map[int]int) + countSliceLength := len(nums) + 1 + countSlice := make([][]int, countSliceLength) + + for _, num := range nums { + countMap[num]++ + } + + for num, count := range countMap { + countSlice[count] = append(countSlice[count], num) + } + + for i := countSliceLength - 1; len(res) != k; i-- { + res = append(res, countSlice[i]...) + } + + return res +} diff --git a/go/0352-data-stream-as-disjoint-intervals.go b/go/0352-data-stream-as-disjoint-intervals.go new file mode 100644 index 000000000..c57bc225d --- /dev/null +++ b/go/0352-data-stream-as-disjoint-intervals.go @@ -0,0 +1,44 @@ +package main + +import "sort" + +type SummaryRanges struct { + numSet map[int]bool +} + +func Constructor() SummaryRanges { + return SummaryRanges{numSet: map[int]bool{}} +} + +func (this *SummaryRanges) AddNum(value int) { + this.numSet[value] = true +} + +func (this *SummaryRanges) GetIntervals() [][]int { + nums := []int{} + for num, _ := range this.numSet { + nums = append(nums, num) + } + sort.Ints(nums) + + res := [][]int{} + + i := 0 + + for i < len(nums) { + start := nums[i] + + for i +1 < len(nums) && nums[i] +1 == nums[i+1] { + i++ + } + + res = append(res, []int{start, nums[i]}) + i++ + } + + return res +} + +func main() { + +} \ No newline at end of file diff --git a/go/0355-design-twitter.go b/go/0355-design-twitter.go new file mode 100644 index 000000000..5ead16216 --- /dev/null +++ b/go/0355-design-twitter.go @@ -0,0 +1,87 @@ +type Tweet struct { + count int + tweetId int + followeeId int + index int +} + +type TweetHeap []*Tweet +func (h TweetHeap) Len() int { return len(h) } +func (h TweetHeap) Less(i, j int) bool { return h[i].count < h[j].count } +func (h TweetHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } +func (h *TweetHeap) Push(x interface{}) {*h = append(*h, x.(*Tweet))} +func (h *TweetHeap) Pop() interface{} { + old := *h + n := len(old) + x := old[n-1] + *h = old[0 : n-1] + return x +} + +type Twitter struct { + count int + tweetMap map[int][]*Tweet + followMap map[int]map[int]bool +} + +func Constructor() Twitter { + return Twitter{tweetMap: make(map[int][]*Tweet), followMap: make(map[int]map[int]bool)} +} + +func (this *Twitter) PostTweet(userId int, tweetId int) { + if _, ok := this.tweetMap[userId]; !ok { + this.tweetMap[userId] = make([]*Tweet, 0) + } + this.tweetMap[userId] = append(this.tweetMap[userId], &Tweet{count: this.count, tweetId: tweetId}) + this.count -= 1 +} + + +func (this *Twitter) GetNewsFeed(userId int) []int { + res := make([]int, 0) + minHeap := TweetHeap{} + + if _, ok := this.followMap[userId]; !ok { + this.followMap[userId] = make(map[int]bool) + } + this.followMap[userId][userId] = true; + for followeeId, _ := range this.followMap[userId] { + if _, ok := this.tweetMap[followeeId]; ok { + index := len(this.tweetMap[followeeId]) - 1 + tweet := this.tweetMap[followeeId][index] + heap.Push(&minHeap, &Tweet{ + count: tweet.count, + tweetId: tweet.tweetId, + followeeId: followeeId, + index: index - 1}) + } + } + + for len(minHeap) > 0 && len(res) < 10 { + tweet := heap.Pop(&minHeap).(*Tweet) + res = append(res, tweet.tweetId) + if tweet.index >= 0 { + nextTweet := this.tweetMap[tweet.followeeId][tweet.index] + heap.Push(&minHeap, &Tweet{count: nextTweet.count, + tweetId: nextTweet.tweetId, + followeeId: tweet.followeeId, + index: tweet.index - 1}) + } + } + return res +} + + +func (this *Twitter) Follow(followerId int, followeeId int) { + if _, ok := this.followMap[followerId]; !ok { + this.followMap[followerId] = make(map[int]bool) + } + this.followMap[followerId][followeeId] = true +} + + +func (this *Twitter) Unfollow(followerId int, followeeId int) { + if _, ok := this.followMap[followerId]; ok { + delete(this.followMap[followerId], followeeId) + } +} diff --git a/go/0367-valid-perfect-square.go b/go/0367-valid-perfect-square.go new file mode 100644 index 000000000..7acd14ee3 --- /dev/null +++ b/go/0367-valid-perfect-square.go @@ -0,0 +1,15 @@ +func isPerfectSquare(num int) bool { + l, r := 0, num + for l <= r { + m := (l + r) / 2 + product := m * m + if product == num { + return true + } else if product > num { + r = m - 1 + } else { + l = m + 1 + } + } + return false +} \ No newline at end of file diff --git a/go/0371-sum-of-two-integers.go b/go/0371-sum-of-two-integers.go new file mode 100644 index 000000000..3c14b508b --- /dev/null +++ b/go/0371-sum-of-two-integers.go @@ -0,0 +1,8 @@ +func getSum(a int, b int) int { + for b != 0 { + temp := (a & b) << 1 + a = (a ^ b) + b = temp + } + return a +} \ No newline at end of file diff --git a/go/0374-guess-number-higher-or-lower.go b/go/0374-guess-number-higher-or-lower.go new file mode 100644 index 000000000..7088c901c --- /dev/null +++ b/go/0374-guess-number-higher-or-lower.go @@ -0,0 +1,17 @@ +func guessNumber(n int) int { + low := 1 + high := n + + for true { + mid := low + (high - low)/2 + myGuess := guess(mid) + if myGuess == 1 { + low = mid + 1 + } else if myGuess == -1 { + high = mid - 1 + } else { + return mid + } + } + return -1 +} diff --git a/go/0380-insert-delete-getrandom-o1.go b/go/0380-insert-delete-getrandom-o1.go new file mode 100644 index 000000000..7d28c52bc --- /dev/null +++ b/go/0380-insert-delete-getrandom-o1.go @@ -0,0 +1,43 @@ +import "math/rand" + +type RandomizedSet struct { + hash map[int]int + array []int + length int +} + +func Constructor() RandomizedSet { + return RandomizedSet{ + hash: make(map[int]int), + array: []int{}, + length: 0, + } +} + +func (this *RandomizedSet) Insert(val int) bool { + if _, ok := this.hash[val]; ok { + return false + } + this.array = append(this.array, val) + this.hash[val] = len(this.array) - 1 + this.length++ + return true +} + +func (this *RandomizedSet) Remove(val int) bool { + idx, ok := this.hash[val] + if !ok { + return false + } + last := this.array[this.length-1] + this.array[idx] = last + this.hash[last] = idx + this.array = this.array[:len(this.array)-1] + delete(this.hash, val) + this.length-- + return true +} + +func (this *RandomizedSet) GetRandom() int { + return this.array[rand.Intn(this.length)] +} diff --git a/go/0392-is-subsequence.go b/go/0392-is-subsequence.go new file mode 100644 index 000000000..291c98240 --- /dev/null +++ b/go/0392-is-subsequence.go @@ -0,0 +1,20 @@ +func isSubsequence(s string, t string) bool { + if len(s) == 0 { + return true + } + + p := 0 + + + for i := 0; i < len(t); i++ { + if s[p] == t[i] { + p++ + } + + if p == len(s) { + return true + } + } + + return false +} \ No newline at end of file diff --git a/go/0416-partition-equal-subset-sum.go b/go/0416-partition-equal-subset-sum.go new file mode 100644 index 000000000..7d5002763 --- /dev/null +++ b/go/0416-partition-equal-subset-sum.go @@ -0,0 +1,102 @@ +func canPartitionTabulation(nums []int) bool { + sum := sum(nums) + if sum % 2 != 0 { + return false + } + + dp := make(map[int]bool) + dp[0] = true + target := sum / 2 + + for i := len(nums) - 1; i >= 0; i-- { + nextDP := make(map[int]bool) + for t, _ := range dp { + if (t + nums[i]) == target { + return true + } + nextDP[t + nums[i]] = true + nextDP[t] = true + } + dp = nextDP + } + return false +} + +func sum(nums []int) int { + res := 0 + for _, num := range nums { + res += num + } + return res +} + + + +const NUM = 0 +const FREQ = 1 +type byNum [][]int +func (s byNum) Len() int {return len(s)} +func (s byNum) Swap(i, j int) {s[i], s[j] = s[j], s[i]} +func (s byNum) Less(i, j int) bool {return s[i][NUM] > s[j][NUM]} + +func canPartitionMemoization(nums[] int) bool { + count := make(map[int]int) + sum := 0 + for _, n := range nums { + count[n] += 1 + sum += n + } + + if sum % 2 != 0 { + return false + } + + numsF := make([][]int, len(count)) + idx := 0 + for num, freq := range count { + numsF[idx] = []int{num, freq} + idx++ + } + sort.Sort(byNum(numsF)) + visited := make([]bool, sum/2 + 1) + visited[0] = true + return solveCanPartition(visited, numsF, sum/2) +} + +func solveCanPartition(visited []bool, nums [][]int, target int) bool { + if visited[target] { + return target == 0 + } + visited[target] = true + + for index := predecessor(nums, target); index < len(nums); index++ { + nums[index][FREQ]-- + if nums[index][FREQ] >= 0 && solveCanPartition(visited, nums, target - nums[index][NUM]) { + return true + } + nums[index][FREQ]++ + } + + return false +} + +func predecessor(nums [][]int, target int) int { + l := 0 + h := len(nums) - 1 + for h - l > 1 { + m := (h + l)/2 + if nums[m][NUM] > target { + l = m + 1 + } else { + h = m + } + } + + if nums[l][0] <= target { + return l + } else if nums[h][0] <= target { + return h + } else { + return math.MaxInt32 + } +} diff --git a/go/0417-pacific-atlantic-water-flow.go b/go/0417-pacific-atlantic-water-flow.go new file mode 100644 index 000000000..4b968893c --- /dev/null +++ b/go/0417-pacific-atlantic-water-flow.go @@ -0,0 +1,42 @@ +func pacificAtlantic(heights [][]int) [][]int { + ROWS, COLS := len(heights), len(heights[0]) + pac, atl := make(map[int]bool), make(map[int] bool) + + var dfs func(int, int, map[int]bool, int) + dfs = func(r, c int, visit map[int]bool, prevHeight int) { + if ( + visit[r*COLS + c] || + r < 0 || + c < 0 || + r == ROWS || + c == COLS || + heights[r][c] < prevHeight) { + return; + } + visit[r*COLS + c] = true + dfs(r + 1, c, visit, heights[r][c]) + dfs(r - 1, c, visit, heights[r][c]) + dfs(r, c + 1, visit, heights[r][c]) + dfs(r, c - 1, visit, heights[r][c]) + } + + for c := 0; c < COLS; c++ { + dfs(0, c, pac, heights[0][c]) + dfs(ROWS - 1, c, atl, heights[ROWS - 1][c]) + } + + for r := 0; r < ROWS; r++ { + dfs(r, 0, pac, heights[r][0]) + dfs(r, COLS - 1, atl, heights[r][COLS - 1]) + } + + res := make([][]int, 0) + for r := 0; r < ROWS; r++ { + for c := 0; c < COLS; c++ { + if pac[r*COLS + c] && atl[r*COLS + c] { + res = append(res, []int{r, c}) + } + } + } + return res +} diff --git a/go/0424-longest-repeating-character-replacement.go b/go/0424-longest-repeating-character-replacement.go new file mode 100644 index 000000000..8c29468d5 --- /dev/null +++ b/go/0424-longest-repeating-character-replacement.go @@ -0,0 +1,25 @@ +func characterReplacement(s string, k int) int { + count := make(map[byte]int) + res := 0 + + l := 0 + maxf := 0 + for r, _ := range s { + count[s[r]] = 1 + count[s[r]] + maxf = max(maxf, count[s[r]]) + + if (r - l + 1) - maxf > k{ + count[s[l]] -= 1 + l++ + } + res = max(res, r - l + 1) + } + return res +} + +func max(a,b int) int { + if a > b { + return a + } + return b +} diff --git a/go/0435-non-overlapping-intervals.go b/go/0435-non-overlapping-intervals.go new file mode 100644 index 000000000..18070d3fc --- /dev/null +++ b/go/0435-non-overlapping-intervals.go @@ -0,0 +1,31 @@ +func eraseOverlapIntervals(intervals [][]int) int { + const ( + start = 0 + end = 1 + ) + sort.Slice(intervals, func(i, j int) bool { return intervals[i][start] < intervals[j][start] }) + + numRemovals := 0 + currEnd := intervals[0][end] + + for i := 1; i < len(intervals); i++ { + currInterval := intervals[i] + + if currInterval[start] < currEnd { + numRemovals += 1 + currEnd = min(currInterval[end], currEnd) + } else { + currEnd = currInterval[end] + } + } + + return numRemovals +} + +func min(i, j int) int { + if i < j { + return i + } else { + return j + } +} \ No newline at end of file diff --git a/go/0438-find-all-anagrams-in-a-string.go b/go/0438-find-all-anagrams-in-a-string.go new file mode 100644 index 000000000..cd2a387fe --- /dev/null +++ b/go/0438-find-all-anagrams-in-a-string.go @@ -0,0 +1,31 @@ +func findAnagrams(s string, p string) []int { + len_s, len_p := len(s), len(p) + if len_p > len_s { + return []int{} + } + + ans := make([]int, 0, len_s) + count_p, count_s := count(p), count(s[:len_p]) + + if count_p == count_s { + ans = append(ans, 0) + } + + for i := len_p; i < len_s; i++ { + count_s[int(s[i-len_p]-'a')]-- + count_s[int(s[i]-'a')]++ + if count_p == count_s { + ans = append(ans, i-len_p+1) + } + } + + return ans +} + +func count(s string) [26]int { + arr := [26]int{} + for _, r := range []rune(s) { + arr[int(r-'a')]++ + } + return arr +} \ No newline at end of file diff --git a/go/0448-find-all-numbers-disappeared-in-an-array.go b/go/0448-find-all-numbers-disappeared-in-an-array.go new file mode 100644 index 000000000..906f31657 --- /dev/null +++ b/go/0448-find-all-numbers-disappeared-in-an-array.go @@ -0,0 +1,21 @@ +func findDisappearedNumbers(nums []int) []int { + for _, n := range nums { + i := abs(n) - 1 + nums[i] = -1 * abs(nums[i]) + } + + var res []int + for i, n := range nums { + if n > 0 { + res = append(res, i + 1) + } + } + return res +} + +func abs(n int) int { + if n < 0 { + return n * -1 + } + return n +} diff --git a/go/0451-sort-characters-by-frequency.go b/go/0451-sort-characters-by-frequency.go new file mode 100644 index 000000000..1c4e0ecf4 --- /dev/null +++ b/go/0451-sort-characters-by-frequency.go @@ -0,0 +1,29 @@ +func frequencySort(s string) string { + type charFreq struct { + c rune + f int + } + + mp := map[rune]int{} + cs := []charFreq{} + + for _, c := range s { + mp[c]++ + } + + for c, f := range mp { + cs = append(cs, charFreq{c,f}) + } + + sort.Slice(cs, func(i,j int) bool{ + return cs[i].f > cs[j].f + }) + + ans := "" + + for _, cf := range cs { + ans += strings.Repeat(string(cf.c), cf.f) + } + + return ans +} diff --git a/go/0463-island-perimeter.go b/go/0463-island-perimeter.go new file mode 100644 index 000000000..465c911bb --- /dev/null +++ b/go/0463-island-perimeter.go @@ -0,0 +1,29 @@ +func islandPerimeter(grid [][]int) int { + ROWS, COLS := len(grid), len(grid[0]) + visit := make(map[int]bool) + + var dfs func(int, int) int + dfs = func(i, j int) int { + if i >= ROWS || j >= COLS || i < 0 || j < 0 || grid[i][j] == 0 { + return 1 + } else if visit[i*COLS + j] { + return 0 + } + + visit[i*COLS + j] = true + perim := dfs(i, j + 1) + perim += dfs(i + 1, j) + perim += dfs(i, j - 1) + perim += dfs(i - 1, j) + return perim + } + + for i := 0; i < ROWS; i++ { + for j := 0; j < COLS; j++ { + if grid[i][j] != 0 { + return dfs(i, j) + } + } + } + return -1 +} diff --git a/go/0472-concatenated-words.go b/go/0472-concatenated-words.go new file mode 100644 index 000000000..8aafcea39 --- /dev/null +++ b/go/0472-concatenated-words.go @@ -0,0 +1,35 @@ +func findAllConcatenatedWordsInADict(words []string) []string { + wordSet := initWordSet(words) + res := []string{} + + var dfs func(word string) bool + + dfs = func(word string) bool { + for i := 1; i < len(word); i++ { + prefix, suffix := word[:i], word[i:] + if (wordSet[prefix] && wordSet[suffix]) || wordSet[prefix] && dfs(suffix) { + return true + } + } + + return false + } + + for _, w := range words { + if dfs(w) { + res = append(res, w) + } + } + + return res +} + +func initWordSet(words []string) map[string]bool { + wordSet := make(map[string]bool, len(words)) + + for _, word := range words { + wordSet[word] = true + } + + return wordSet +} \ No newline at end of file diff --git a/go/0494-target-sum.go b/go/0494-target-sum.go new file mode 100644 index 000000000..6d509db40 --- /dev/null +++ b/go/0494-target-sum.go @@ -0,0 +1,25 @@ +func findTargetSumWays(nums []int, target int) int { + return findSum(nums, 0, target, make(map[string]int)) +} + +func findSum(nums []int, idx int, target int, memo map[string]int) int { + key := fmt.Sprint(idx)+"*"+fmt.Sprint(target) + + if val, ok := memo[key]; ok { + return val + } + + if idx == len(nums) { + if target == 0 { + return 1 + } + + return 0 + } + + res := findSum(nums, idx+1, target+nums[idx], memo) + findSum(nums, idx+1, target-nums[idx], memo) + + memo[key] = res + + return res +} \ No newline at end of file diff --git a/go/0496-next-greater-element-i.go b/go/0496-next-greater-element-i.go new file mode 100644 index 000000000..94f6d44af --- /dev/null +++ b/go/0496-next-greater-element-i.go @@ -0,0 +1,24 @@ +func nextGreaterElement(nums1 []int, nums2 []int) []int { + nums1idx := make(map[int]int) + ans := []int{} + for i, v := range nums1 { + nums1idx[v] = i + ans = append(ans, -1) + } + stack := []int{} + + for _, v := range nums2 { + for len(stack) > 0 && stack[len(stack)-1] < v { + val := stack[len(stack)-1] + stack = stack[:len(stack)-1] + idx := nums1idx[val] + ans[idx] = v + } + + if _, e := nums1idx[v]; !e { + continue + } + stack = append(stack, v) + } + return ans +} diff --git a/go/0518-coin-change-ii.go b/go/0518-coin-change-ii.go new file mode 100644 index 000000000..e8bcde4ba --- /dev/null +++ b/go/0518-coin-change-ii.go @@ -0,0 +1,23 @@ +func change(amount int, coins []int) int { + + row := make([]int, amount+1) + row[0] = 1 + + for i := len(coins) - 1; i >= 0; i-- { + + nextRow := make([]int, amount+1) + nextRow[0] = 1 + + for a := 1; a < amount+1; a++ { + nextRow[a] = row[a] + if a-coins[i] >= 0 { + nextRow[a] += nextRow[a-coins[i]] + } + } + + row = nextRow + } + + return row[amount] +} + \ No newline at end of file diff --git a/go/0535-encode-and-decode-tinyurl.go b/go/0535-encode-and-decode-tinyurl.go new file mode 100644 index 000000000..354e24ade --- /dev/null +++ b/go/0535-encode-and-decode-tinyurl.go @@ -0,0 +1,25 @@ +type Codec struct { + encodeMap map[string]string + decodeMap map[string]string +} +const base string = "http://tinyurl.com/" + + +func Constructor() Codec { + return Codec{make(map[string]string), make(map[string]string)} +} + +// Encodes a URL to a shortened URL. +func (this *Codec) encode(longUrl string) string { + if _, ok := this.encodeMap[longUrl]; !ok { + shortUrl := base + strconv.Itoa(len(this.encodeMap) + 1) + this.encodeMap[longUrl] = shortUrl + this.decodeMap[shortUrl] = longUrl + } + return this.encodeMap[longUrl] +} + +// Decodes a shortened URL to its original URL. +func (this *Codec) decode(shortUrl string) string { + return this.decodeMap[shortUrl] +} diff --git a/go/0543-diameter-of-binary-tree.go b/go/0543-diameter-of-binary-tree.go new file mode 100644 index 000000000..73051e637 --- /dev/null +++ b/go/0543-diameter-of-binary-tree.go @@ -0,0 +1,32 @@ +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func diameterOfBinaryTree(root *TreeNode) int { + maxLength := 0 + dfs(root, &maxLength) + return maxLength +} + +func dfs(t *TreeNode, maxLength *int) int { + if t == nil { + return 0 + } + + left := dfs(t.Left, maxLength) + right := dfs(t.Right, maxLength) + *maxLength = max(*maxLength, left+right) + + return max(left, right) + 1 +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} \ No newline at end of file diff --git a/go/0554-brick-wall.go b/go/0554-brick-wall.go new file mode 100644 index 000000000..a77696410 --- /dev/null +++ b/go/0554-brick-wall.go @@ -0,0 +1,22 @@ +func leastBricks(wall [][]int) int { + + countGap := make(map[int]int) + rowSum := 0 + + for _, row := range wall { + rowSum = 0 + for _, brick := range row[0 : len(row)-1] { + rowSum += brick + countGap[rowSum]++ + } + } + + mostFrequentGap := 0 + for _, gapFrequency := range countGap { + if gapFrequency > mostFrequentGap { + mostFrequentGap = gapFrequency + } + } + + return len(wall) - mostFrequentGap +} \ No newline at end of file diff --git a/go/0560-subarray-sum-equals-k.go b/go/0560-subarray-sum-equals-k.go new file mode 100644 index 000000000..f9f973474 --- /dev/null +++ b/go/0560-subarray-sum-equals-k.go @@ -0,0 +1,13 @@ +func subarraySum(nums []int, k int) int { + prefix := map[int]int{0: 1} + curSum := 0 + count := 0 + for _, n := range nums { + curSum += n + if _, ok := prefix[curSum-k]; ok { + count += prefix[curSum-k] + } + prefix[curSum]++ + } + return count +} diff --git a/go/0567-permutation-in-string.go b/go/0567-permutation-in-string.go new file mode 100644 index 000000000..7e9b97439 --- /dev/null +++ b/go/0567-permutation-in-string.go @@ -0,0 +1,44 @@ +func checkInclusion(s1 string, s2 string) bool { + if len(s1) > len(s2){ + return false + } + + s1Count, s2Count := [26]int{}, [26]int{} + for i, _ := range s1 { + s1Count[s1[i] - 'a']++ + s2Count[s2[i] - 'a']++ + } + matches := 0 + for i:=0;i<26;i++ { + if s1Count[i] == s2Count[i] { + matches += 1 + } else { + matches += 0 + } + } + + l := 0 + for r:=len(s1);r 0 { + return this.tail.prev.val + } + return -1 +} + +func (this *MyCircularQueue) Rear() int { + if this.length > 0 { + return this.head.next.val + } + return -1 +} + +func (this *MyCircularQueue) IsEmpty() bool { + return this.length == 0 +} + +func (this *MyCircularQueue) IsFull() bool { + return this.length == this.capacity +} + +/** + * Your MyCircularQueue object will be instantiated and called as such: + * obj := Constructor(k); + * param_1 := obj.EnQueue(value); + * param_2 := obj.DeQueue(); + * param_3 := obj.Front(); + * param_4 := obj.Rear(); + * param_5 := obj.IsEmpty(); + * param_6 := obj.IsFull(); + */ diff --git a/go/0647-palindromic-substrings.go b/go/0647-palindromic-substrings.go new file mode 100644 index 000000000..3695458d9 --- /dev/null +++ b/go/0647-palindromic-substrings.go @@ -0,0 +1,19 @@ +func countSubstrings(s string) int { + n := len(s) + pal := func(l, r int) int { + count := 0 + for l >= 0 && r < n && s[l] == s[r] { + count++ + l-- + r++ + } + return count + } + + count := 0 + for i := range s { + count += pal(i, i) + count += pal(i, i+1) + } + return count +} \ No newline at end of file diff --git a/go/0658-find-k-closest-elements.go b/go/0658-find-k-closest-elements.go new file mode 100644 index 000000000..f14f00d1b --- /dev/null +++ b/go/0658-find-k-closest-elements.go @@ -0,0 +1,13 @@ +func findClosestElements(arr []int, k int, x int) []int { + l, r := 0, len(arr)-k + + for l < r { + m := (l + r) / 2 + if x-arr[m] > arr[m+k]-x { + l = m + 1 + } else { + r = m + } + } + return arr[l : l+k] +} diff --git a/go/0665-non-decreasing-array.go b/go/0665-non-decreasing-array.go new file mode 100644 index 000000000..c5dfa3d14 --- /dev/null +++ b/go/0665-non-decreasing-array.go @@ -0,0 +1,21 @@ +func checkPossibility(nums []int) bool { + changed := false + + for i := 0; i < len(nums)-1; i++ { + if nums[i] <= nums[i+1] { + continue + } + if changed { + return false + } + if i == 0 || nums[i+1] >= nums[i-1] { + nums[i] = nums[i+1] + } else { + nums[i+1] = nums[i] + } + + changed = true + } + + return true +} \ No newline at end of file diff --git a/go/0669-trim-a-binary-search-tree.go b/go/0669-trim-a-binary-search-tree.go new file mode 100644 index 000000000..dfe3f6a92 --- /dev/null +++ b/go/0669-trim-a-binary-search-tree.go @@ -0,0 +1,23 @@ +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ +func trimBST(root *TreeNode, low int, high int) *TreeNode { + if root == nil { + return nil + } + if root.Val > high { + return trimBST(root.Left, low, high) + } + if root.Val < low { + return trimBST(root.Right, low, high) + } + root.Left = trimBST(root.Left, low, high) + root.Right = trimBST(root.Right, low, high) + + return root +} \ No newline at end of file diff --git a/go/0678-valid-parenthesis-string.go b/go/0678-valid-parenthesis-string.go new file mode 100644 index 000000000..ac93ec96b --- /dev/null +++ b/go/0678-valid-parenthesis-string.go @@ -0,0 +1,23 @@ +func checkValidString(s string) bool { + minLeft, maxLeft := 0, 0 + + for i := range s { + if s[i] == '(' { + minLeft++ + maxLeft++ + } else if s[i] == ')' { + minLeft-- + maxLeft-- + } else { + minLeft-- + maxLeft++ + } + if maxLeft < 0 { + return false + } + if minLeft < 0 { + minLeft = 0 + } + } + return minLeft == 0 +} diff --git a/go/0680-valid-palindrome-ii.go b/go/0680-valid-palindrome-ii.go new file mode 100644 index 000000000..c8245b9e3 --- /dev/null +++ b/go/0680-valid-palindrome-ii.go @@ -0,0 +1,26 @@ +func validPalindrome(s string) bool { + i := 0 + j := len(s) - 1 + + for i < j { + if s[i] == s[j] { + i += 1 + j -= 1 + } else { + return validPalindromeUtil(s, i + 1, j) || validPalindromeUtil(s, i, j - 1) + } + } + return true +} + +func validPalindromeUtil(s string, i, j int) bool { + for i < j { + if s[i] == s[j] { + i += 1 + j -= 1 + } else { + return false + } + } + return true +} diff --git a/go/0682-baseball-game.go b/go/0682-baseball-game.go new file mode 100644 index 000000000..dcefa5711 --- /dev/null +++ b/go/0682-baseball-game.go @@ -0,0 +1,34 @@ +/* +Maintain a stack of previous score, +summed after applying all operations +Time: O(n) +Space: O(n) +*/ + +import "strconv" + +func calPoints(operations []string) int { + record := []int{} + + for _, op := range operations { + if len(record) > 0 && op == "C" { + record = record[:len(record)-1] + } else if len(record) > 0 && op == "D" { + record = append(record, record[len(record)-1]*2) + } else if len(record) > 0 && op == "+" { + record = append(record, sum(record[len(record)-2:])) + } else { + iop, _ := strconv.Atoi(op) + record = append(record, iop) + } + } + return sum(record) +} + +func sum(nums []int) int { + res := 0 + for _, n := range nums { + res += n + } + return res +} diff --git a/go/0684-redundant-connection.go b/go/0684-redundant-connection.go new file mode 100644 index 000000000..ecce48fd7 --- /dev/null +++ b/go/0684-redundant-connection.go @@ -0,0 +1,30 @@ +func findRedundantConnection(edges [][]int) []int { + parent := make([]int, len(edges)+1) + + for i := 0; i < len(parent); i++ { + parent[i] = i + } + + for _, edge := range edges { + if find(parent, edge[0]) == find(parent, edge[1]) { + return edge + } + + unify(parent, edge[0], edge[1]) + } + + return []int{} +} + +func find(parent []int, num int) int { + if parent[num] == num { + return num + } + + return find(parent, parent[num]) +} + +func unify(parent []int, x, y int) { + parent[find(parent, y)] = find(parent, x) +} + diff --git a/go/0695-max-area-of-island.go b/go/0695-max-area-of-island.go new file mode 100644 index 000000000..8d320f77f --- /dev/null +++ b/go/0695-max-area-of-island.go @@ -0,0 +1,31 @@ +func maxAreaOfIsland(grid [][]int) int { + row, col := len(grid), len(grid[0]) + res := 0 + + for i := 0; i < row; i++ { + for j := 0; j < col; j++ { + if grid[i][j] == 1 { + curr := dfs(grid, i, j) + if curr > res { + res = curr + } + } + } + } + + return res +} + +func dfs(grid [][]int, row, col int) int { + if row < 0 || row >= len(grid) || col < 0 || col >= len(grid[0]) { + return 0 + } + + if grid[row][col] == 0 { + return 0 + } + + grid[row][col] = 0 + + return dfs(grid, row+1, col) + dfs(grid, row-1, col) + dfs(grid, row, col+1) + dfs(grid, row, col-1) + 1 +} \ No newline at end of file diff --git a/go/0703-kth-largest-element-in-a-stream.go b/go/0703-kth-largest-element-in-a-stream.go new file mode 100644 index 000000000..057ebda24 --- /dev/null +++ b/go/0703-kth-largest-element-in-a-stream.go @@ -0,0 +1,38 @@ +type IntHeap []int + +func (h IntHeap) Len() int { return len(h) } +func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] } +func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } +func (h *IntHeap) Push(x interface{}) {*h = append(*h, x.(int))} +func (h *IntHeap) Pop() interface{} { + old := *h + n := len(old) + x := old[n-1] + *h = old[0 : n-1] + return x +} + +type KthLargest struct { + minHeap *IntHeap + k int +} + + +func Constructor(k int, nums []int) KthLargest { + tmp := IntHeap(nums) + this := KthLargest{minHeap: &tmp, k: k} + heap.Init(this.minHeap) + for len(*this.minHeap) > k { + heap.Pop(this.minHeap) + } + return this +} + + +func (this *KthLargest) Add(val int) int { + heap.Push(this.minHeap, val) + if len(*this.minHeap) > this.k { + heap.Pop(this.minHeap) + } + return (*this.minHeap)[0] +} diff --git a/go/0704-binary-search.go b/go/0704-binary-search.go new file mode 100644 index 000000000..91e89b210 --- /dev/null +++ b/go/0704-binary-search.go @@ -0,0 +1,20 @@ +package main + +func search(nums []int, target int) int { + left := 0 + right := len(nums) - 1 + + for left <= right { + middle := (left + right) / 2 + + if nums[middle] == target { + return middle + } else if nums[middle] < target { + left = middle + 1 + } else { + right = middle - 1 + } + } + return -1 + +} diff --git a/go/0706-design-hashmap.go b/go/0706-design-hashmap.go new file mode 100644 index 000000000..4ad926e0c --- /dev/null +++ b/go/0706-design-hashmap.go @@ -0,0 +1,71 @@ +type Node struct { + Key int + Val int + Next *Node +} + +func NewNode(key, val int) *Node { + return &Node{ + Key: key, + Val: val, + } +} + +type MyHashMap struct { + Array []*Node +} + +func Constructor() MyHashMap { + var arr []*Node + for i := 0; i < 512; i++ { + arr = append(arr, NewNode(-1, -1)) + } + return MyHashMap{ + Array: arr, + } +} + +func (this *MyHashMap) hash(key int) int { + return key % len(this.Array) +} + +func (this *MyHashMap) Put(key int, value int) { + cur := this.Array[this.hash(key)] + for cur.Next != nil { + if cur.Next.Key == key { + cur.Next.Val = value + return + } + cur = cur.Next + } + cur.Next = NewNode(key, value) +} + +func (this *MyHashMap) Get(key int) int { + cur := this.Array[this.hash(key)] + for cur.Next != nil { + if cur.Next.Key == key { + return cur.Next.Val + } + cur = cur.Next + } + return -1 +} + +func (this *MyHashMap) Remove(key int) { + cur := this.Array[this.hash(key)] + for cur.Next != nil && cur.Next.Key != key { + cur = cur.Next + } + if cur != nil && cur.Next != nil { + cur.Next = cur.Next.Next + } +} + +/** + * Your MyHashMap object will be instantiated and called as such: + * obj := Constructor(); + * obj.Put(key,value); + * param_2 := obj.Get(key); + * obj.Remove(key); + */ diff --git a/go/0707-design-linked-list.go b/go/0707-design-linked-list.go new file mode 100644 index 000000000..a9ca47932 --- /dev/null +++ b/go/0707-design-linked-list.go @@ -0,0 +1,95 @@ +type Node struct { + val int + prev *Node + next *Node +} + +func NewNode(val int, prev, next *Node) *Node { + return &Node{ + val: val, + prev: prev, + next: next, + } +} + +type MyLinkedList struct { + head *Node + tail *Node +} + +func Constructor() MyLinkedList { + head := NewNode(-1, nil, nil) + tail := NewNode(-1, head, nil) + head.next = tail + + return MyLinkedList{ + head: head, + tail: tail, + } +} + +func (this *MyLinkedList) Get(index int) int { + cur := this.head.next + for cur != nil && index > 0 { + cur = cur.next + index-- + } + + if cur != nil && cur != this.tail && index == 0 { + return cur.val + } + + return -1 +} + +func (this *MyLinkedList) AddAtHead(val int) { + node := NewNode(val, this.head, this.head.next) + this.head.next.prev = node + this.head.next = node +} + +func (this *MyLinkedList) AddAtTail(val int) { + node := NewNode(val, this.tail.prev, this.tail) + this.tail.prev.next = node + this.tail.prev = node +} + +func (this *MyLinkedList) AddAtIndex(index int, val int) { + cur := this.head.next + for cur.next != nil && index > 0 { + cur = cur.next + index-- + } + + if index == 0 { + node := NewNode(val, cur.prev, cur) + cur.prev.next = node + cur.prev = node + } + return +} + +func (this *MyLinkedList) DeleteAtIndex(index int) { + cur := this.head.next + + for cur.next != nil && index > 0 { + cur = cur.next + index-- + } + + if index == 0 && cur != nil && cur != this.tail { + prev, next := cur.prev, cur.next + prev.next, next.prev = next, prev + } + return +} + +/** + * Your MyLinkedList object will be instantiated and called as such: + * obj := Constructor(); + * param_1 := obj.Get(index); + * obj.AddAtHead(val); + * obj.AddAtTail(val); + * obj.AddAtIndex(index,val); + * obj.DeleteAtIndex(index); + */ diff --git a/go/0724-find-pivot-index.go b/go/0724-find-pivot-index.go new file mode 100644 index 000000000..5c0465a05 --- /dev/null +++ b/go/0724-find-pivot-index.go @@ -0,0 +1,16 @@ +func pivotIndex(nums []int) int { + total := 0 + for _, n := range(nums) { + total += n + } + + leftSum := 0 + for i := 0; i < len(nums); i++ { + rightSum := total - nums[i] - leftSum + if leftSum == rightSum { + return i + } + leftSum += nums[i] + } + return -1 +} diff --git a/go/0735-asteroid-collision.go b/go/0735-asteroid-collision.go new file mode 100644 index 000000000..b5fd18c4f --- /dev/null +++ b/go/0735-asteroid-collision.go @@ -0,0 +1,27 @@ +func asteroidCollision(asteroids []int) []int { + + stack := []int{} + diff := 0 + + for _, asteroid := range asteroids { + for len(stack) > 0 && stack[len(stack)-1] > 0 && asteroid < 0 { + diff = stack[len(stack)-1] + asteroid + if diff < 0 { + // asteroid larger than top of stack, destroys it + stack = stack[0 : len(stack)-1] + } else if diff == 0 { + // asteroid and top of stack equal, both destroyed + stack = stack[0 : len(stack)-1] + asteroid = 0 + } else { + // asteroid smaller than top of stack, gets destroyed + asteroid = 0 + } + } + if asteroid != 0 { + stack = append(stack, asteroid) + } + } + + return stack +} \ No newline at end of file diff --git a/go/0739-daily-temperatures.go b/go/0739-daily-temperatures.go new file mode 100644 index 000000000..abb8da8f5 --- /dev/null +++ b/go/0739-daily-temperatures.go @@ -0,0 +1,20 @@ +func dailyTemperatures(temperatures []int) []int { + result := make([]int, len(temperatures)) + + for i := len(temperatures) - 1; i >= 0; i-- { + j := i + 1 + + for j < len(temperatures) && temperatures[j] <= temperatures[i] { + if result[j] <= 0 { + break + } + j += result[j] + } + + if j < len(temperatures) && temperatures[j] > temperatures[i] { + result[i] = j - i + } + + } + return result +} \ No newline at end of file diff --git a/go/0740-delete-and-earn.go b/go/0740-delete-and-earn.go new file mode 100644 index 000000000..5f7e63fca --- /dev/null +++ b/go/0740-delete-and-earn.go @@ -0,0 +1,41 @@ +func deleteAndEarn(nums []int) int { + count := make(map[int]int) + + unique := make([]int, 0) + + for _, num := range nums { + if _, ok := count[num]; !ok { + unique = append(unique, num) + } + + count[num]++ + } + + sort.Ints(unique) + + earn1, earn2 := 0, 0 + + for i := 0; i < len(unique); i++ { + currEarn := unique[i] * count[unique[i]] + + if i > 0 && unique[i] == unique[i - 1] + 1 { + temp := earn2 + earn2 = max(earn2, currEarn + earn1) + earn1 = temp + } else { + temp := earn2 + earn2 = currEarn + earn2 + earn1 = temp + } + } + + return earn2 +} + +func max(a, b int) int { + if a > b { + return a + } + + return b +} \ No newline at end of file diff --git a/go/0743-network-delay-time.go b/go/0743-network-delay-time.go new file mode 100644 index 000000000..1d52bce39 --- /dev/null +++ b/go/0743-network-delay-time.go @@ -0,0 +1,71 @@ +type neighbour struct { + destination int + weight int +} + +type heapNode struct { + distance int + nodeIndex int +} + +func networkDelayTime(times [][]int, n int, k int) int { + edgeMap := make(map[int][]neighbour) + for _, log := range times { + edgeMap[log[0]] = append(edgeMap[log[0]], neighbour{destination: log[1], weight: log[2]}) + } + + h := &minHeap{heapNode{distance: 0, nodeIndex: k}} + heap.Init(h) + visited := make(map[int]bool) + t := 0 + + for !h.isEmpty() { + hNode := heap.Pop(h).(heapNode) + + if vis := visited[hNode.nodeIndex]; vis { + continue + } + + t = max(t, hNode.distance) + + visited[hNode.nodeIndex] = true + + neighbours := edgeMap[hNode.nodeIndex] + for _, neigh := range neighbours { + if vis := visited[neigh.destination]; !vis { + heap.Push(h, heapNode{ + distance: neigh.weight + hNode.distance, + nodeIndex: neigh.destination}) + } + } + } + + if n == len(visited) { + return t + } + return -1 +} + +func max(a, b int) int { + if a < b { + return b + } + return a +} + +type minHeap []heapNode + +func (h minHeap) Len() int { return len(h) } +func (h *minHeap) isEmpty() bool { return len(*h) == 0 } +func (h minHeap) Less(i, j int) bool { return h[i].distance < h[j].distance } +func (h minHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } + +func (h *minHeap) Push(x interface{}) { + *h = append(*h, x.(heapNode)) +} + +func (h *minHeap) Pop() interface{} { + l := (*h)[len(*h)-1] + *h = (*h)[:len(*h)-1] + return l +} \ No newline at end of file diff --git a/go/0746-min-cost-climbing-stairs.go b/go/0746-min-cost-climbing-stairs.go new file mode 100644 index 000000000..a88add029 --- /dev/null +++ b/go/0746-min-cost-climbing-stairs.go @@ -0,0 +1,25 @@ +func minCostClimbingStairs(cost []int) int { + n := len(cost) + dp := make([]int, n+1) + + if n == 2 { + return min(cost[0], cost[1]) + } + + for i := 2; i <= n; i++ { + a := dp[i-1] + cost[i-1] + b := dp[i-2] + cost[i-2] + + dp[i] = min(a, b) + } + + return dp[n] +} + +func min(x, y int) int { + if x > y { + return y + } + + return x +} \ No newline at end of file diff --git a/go/0763-partition-labels.go b/go/0763-partition-labels.go new file mode 100644 index 000000000..1c32c61e7 --- /dev/null +++ b/go/0763-partition-labels.go @@ -0,0 +1,26 @@ +func partitionLabels(s string) []int { + mapCharToIndex := map[byte]int{} + + for idx := 0; idx < len(s); idx++ { + mapCharToIndex[s[idx]] = idx + } + + st, lastIdx := 0, 0 + res := []int{} + + for end := 0; end < len(s); end++ { + curr := s[end] + idx := mapCharToIndex[curr] + + if idx > lastIdx { + lastIdx = idx + } + + if end == lastIdx { + res = append(res, lastIdx-st+1) + st = lastIdx + 1 + } + } + + return res +} \ No newline at end of file diff --git a/go/0778-swim-in-rising-water.go b/go/0778-swim-in-rising-water.go new file mode 100644 index 000000000..3b6ed31ec --- /dev/null +++ b/go/0778-swim-in-rising-water.go @@ -0,0 +1,52 @@ +var dirs = []int{0, 1, 0, -1, 0} + +func swimInWater(grid [][]int) int { + gridSize := len(grid) + minTime, maxTime := grid[0][0], gridSize*gridSize-1 + + for minTime < maxTime { + middleTime := minTime + (maxTime-minTime)/2 + visited := initVisitedArray(gridSize) + if canReach(grid, visited, 0, 0, gridSize, middleTime) { + maxTime = middleTime + } else { + minTime = middleTime + 1 + } + } + return minTime +} + +func initVisitedArray(gridSize int) [][]bool { + visited := make([][]bool, gridSize) + for i := range visited { + visited[i] = make([]bool, gridSize) + } + return visited +} + +func canReach(grid [][]int, visited [][]bool, x, y, gridSize, level int) bool { + visited[x][y] = true + for i := 0; i < 4; i++ { + newX, newY := x+dirs[i], y+dirs[i+1] + if !isValidMove(newX, newY, gridSize) { + continue + } + if visited[newX][newY] || grid[newX][newY] > level { + continue + } + if newX == gridSize-1 && newY == gridSize-1 { + return true + } + if canReach(grid, visited, newX, newY, gridSize, level) { + return true + } + } + return false +} + +func isValidMove(x, y, gridSize int) bool { + if y < 0 || x < 0 || y >= gridSize || x >= gridSize { + return false + } + return true +} diff --git a/go/0787-cheapest-flights-within-k-stops.go b/go/0787-cheapest-flights-within-k-stops.go new file mode 100644 index 000000000..663fd51e0 --- /dev/null +++ b/go/0787-cheapest-flights-within-k-stops.go @@ -0,0 +1,38 @@ +func findCheapestPrice(n int, flights [][]int, src int, dst int, k int) int { + cost := make([]int, n) + + for i := 0; i < n; i++ { + cost[i] = math.MaxUint32 + } + + cost[src] = 0 + + for i := 0; i <= k; i++ { + temp := make([]int, n) + copy(temp, cost) + + for j := 0; j < len(flights); j++ { + from, to, price := flights[j][0], flights[j][1], flights[j][2] + + if cost[from] != math.MaxUint32 { + temp[to] = min(temp[to], cost[from]+price) + } + } + + cost = temp + } + + if cost[dst] == math.MaxUint32 { + return -1 + } + + return cost[dst] +} + +func min(x, y int) int { + if x > y { + return y + } + + return x +} \ No newline at end of file diff --git a/go/0789-minimum-distance-between-two-bst-nodes.go b/go/0789-minimum-distance-between-two-bst-nodes.go new file mode 100644 index 000000000..d915735e2 --- /dev/null +++ b/go/0789-minimum-distance-between-two-bst-nodes.go @@ -0,0 +1,43 @@ +package main + +import "math" + +func main() { + +} + +type TreeNode struct { + Val int + Left *TreeNode + Right *TreeNode +} + +func minDiffInBST(root *TreeNode) int { + var prev *TreeNode + res := math.MaxInt + + var dfs func(*TreeNode) + + dfs = func(node *TreeNode) { + if node != nil { + dfs(node.Left) + + if prev != nil { + res = min(res, node.Val-prev.Val) + } + prev = node + + dfs(node.Right) + } + } + + dfs(root) + return res +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} diff --git a/go/0846-hand-of-straights.go b/go/0846-hand-of-straights.go new file mode 100644 index 000000000..ef826dae1 --- /dev/null +++ b/go/0846-hand-of-straights.go @@ -0,0 +1,45 @@ +func isNStraightHand(hand []int, groupSize int) bool { + + if len(hand)%groupSize != 0 { + return false + } + + countMap := make(map[int]int) + + for _, num := range hand { + countMap[num]++ + } + + var minHeap []int + + for uniqueNum, _ := range countMap { + minHeap = append(minHeap, uniqueNum) + } + + sort.Ints(minHeap) + + for len(minHeap) != 0 { + first := minHeap[0] + + for i := first; i < groupSize+first; i++ { + if _, ok := countMap[i]; !ok { + return false + } + + countMap[i]-- + val := countMap[i] + + if val == 0 { + if i != minHeap[0] { + return false + } + + minHeap = minHeap[1:] + } + + } + + } + return true + +} \ No newline at end of file diff --git a/go/0852-peak-index-in-a-mountain-array.go b/go/0852-peak-index-in-a-mountain-array.go new file mode 100644 index 000000000..322ecce2e --- /dev/null +++ b/go/0852-peak-index-in-a-mountain-array.go @@ -0,0 +1,16 @@ +func peakIndexInMountainArray(arr []int) int { + left, right := 0, len(arr)-1 + res := -1 + + for left <= right { + mid := left + (right-left+1)/2 + + if arr[mid-1] <= arr[mid] { + left, res = mid+1, mid + } else { + right = mid - 1 + } + } + + return res +} \ No newline at end of file diff --git a/go/0853-car-fleet.go b/go/0853-car-fleet.go new file mode 100644 index 000000000..7b5a731e7 --- /dev/null +++ b/go/0853-car-fleet.go @@ -0,0 +1,24 @@ +func carFleet(target int, position []int, speed []int) int { + pair := []carInfo{} + stack := []float32{} + for i, _ := range position { + pair = append(pair, carInfo{position[i], speed[i]}) + } + + sort.Slice(pair, func(i, j int) bool { + return pair[i].pos < pair[j].pos + }) + + for i := len(pair) - 1; i >= 0; i-- { + stack = append(stack, float32(target-pair[i].pos)/float32(pair[i].spd)) + if len(stack) >= 2 && stack[len(stack)-1] <= stack[len(stack)-2] { + stack = stack[:len(stack)-1] + } + } + return len(stack) +} + +type carInfo struct { + pos int + spd int +} \ No newline at end of file diff --git a/go/0875-koko-eating-bananas.go b/go/0875-koko-eating-bananas.go new file mode 100644 index 000000000..27c9ba54c --- /dev/null +++ b/go/0875-koko-eating-bananas.go @@ -0,0 +1,30 @@ +// canEat returns true if all the bananas can be eaten within the time limit at speed +func canEat(piles []int, timeLimit, speed int) bool { + timeNeed := 0 + for _, banana := range piles { + timeNeed += (banana + speed - 1) / speed + if timeNeed > timeLimit { + return false + } + } + + return true +} + +func minEatingSpeed(piles []int, h int) int { + // more bananas can be eaten with more speed + // check if all the bananas can be eaten with a particular speed + // if possible, store the ans & try smaller speed, greater speed otherwise + lo, hi, ans := 1, 1000000000, 1 + for lo <= hi { + mid := (lo + hi) / 2 + if canEat(piles, h, mid) { + ans = mid + hi = mid - 1 + } else { + lo = mid + 1 + } + } + + return ans +} diff --git a/go/0876-middle-of-the-linked-list.go b/go/0876-middle-of-the-linked-list.go new file mode 100644 index 000000000..c02c1be4e --- /dev/null +++ b/go/0876-middle-of-the-linked-list.go @@ -0,0 +1,20 @@ +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func middleNode(head *ListNode) *ListNode { + if head == nil || head.Next == nil { + return head + } + + slow, fast := head, head + + for fast != nil && fast.Next != nil { + slow, fast = slow.Next, fast.Next.Next + } + + return slow +} diff --git a/go/0881-boats-to-save-people.go b/go/0881-boats-to-save-people.go new file mode 100644 index 000000000..8c070ca87 --- /dev/null +++ b/go/0881-boats-to-save-people.go @@ -0,0 +1,15 @@ +func numRescueBoats(people []int, limit int) int { + sort.Ints(people) + + res := 0 // boats + l, r := 0, len(people)-1 + for l <= r { + remain := limit - people[r] + r -= 1 + res += 1 + if l <= r && remain >= people[l] { + l += 1 + } + } + return res +} diff --git a/go/0904-fruit-into-baskets.go b/go/0904-fruit-into-baskets.go new file mode 100644 index 000000000..3b86627a6 --- /dev/null +++ b/go/0904-fruit-into-baskets.go @@ -0,0 +1,34 @@ +package main + +func main() { + +} + +func totalFruit(fruits []int) int { + count := map[int]int{} + left, total, res := 0, 0, 0 + + for _, fruit := range fruits { + count[fruit]++ + total++ + + for len(count) > 2 { + f := fruits[left] + count[f]-- + total-- + left++ + if count[f] == 0 { + delete(count, f) + } + } + res = max(res, total) + } + return res +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} \ No newline at end of file diff --git a/go/0912-sort-an-array.go b/go/0912-sort-an-array.go new file mode 100644 index 000000000..74d42e6e7 --- /dev/null +++ b/go/0912-sort-an-array.go @@ -0,0 +1,4 @@ +func sortArray(nums []int) []int { + sort.Ints(nums) + return nums +} diff --git a/go/0918-maximum-sum-circular-subarray.go b/go/0918-maximum-sum-circular-subarray.go new file mode 100644 index 000000000..97385bd03 --- /dev/null +++ b/go/0918-maximum-sum-circular-subarray.go @@ -0,0 +1,40 @@ +package main + +func main() { + +} + +func maxSubarraySumCircular(nums []int) int { + globalMax, globalMin := nums[0], nums[0] + currentMax, currentMin := 0, 0 + total := 0 + + for _, num := range nums { + currentMax = max(num, currentMax+num) + currentMin = min(num, currentMin+num) + total += num + globalMax = max(globalMax, currentMax) + globalMin = min(globalMin, currentMin) + } + + if globalMax > 0 { + return max(globalMax, total-globalMin) + } else { + return globalMax + } + +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} diff --git a/go/0926-flip-string-to-monotone-increasing.go b/go/0926-flip-string-to-monotone-increasing.go new file mode 100644 index 000000000..637957ab1 --- /dev/null +++ b/go/0926-flip-string-to-monotone-increasing.go @@ -0,0 +1,20 @@ +func minFlipsMonoIncr(s string) int { + res, countOne := 0, 0 + + for _, ch := range s { + if ch == '1' { + countOne++ + } else { + res = min(res+1, countOne) + } + } + + return res +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} \ No newline at end of file diff --git a/go/0929-unique-email-addresses.go b/go/0929-unique-email-addresses.go new file mode 100644 index 000000000..235d63e79 --- /dev/null +++ b/go/0929-unique-email-addresses.go @@ -0,0 +1,27 @@ +import "strings" + +func numUniqueEmails(emails []string) int { + ans, uniqueAddressMap := 0, make(map[string]struct{}) + for _, email := range emails { + address := uniqueAddress(email) + if _, ok := uniqueAddressMap[address]; !ok { + ans++ + uniqueAddressMap[address] = struct{}{} + } + } + return ans +} + +func uniqueAddress(email string) string { + parts := strings.Split(email, "@") + local, domain := parts[0], parts[1] + + local = strings.ReplaceAll(local, ".", "") + + plusIndex := strings.Index(local, "+") + if plusIndex >= 0 { + local = local[:plusIndex] + } + + return local + "@" + domain +} diff --git a/go/0948-bag-of-tokens.go b/go/0948-bag-of-tokens.go new file mode 100644 index 000000000..5b23e729d --- /dev/null +++ b/go/0948-bag-of-tokens.go @@ -0,0 +1,21 @@ +func bagOfTokensScore(tokens []int, power int) int { + res, score := 0, 0 + sort.Ints(tokens) + + l, r := 0, len(tokens)-1 + for l <= r { + if power >= tokens[l] { + power -= tokens[l] + l += 1 + score += 1 + res = max(res, score) + } else if score > 0 { + power += tokens[r] + r -= 1 + score -= 1 + } else { + break + } + } + return res +} diff --git a/go/0953-verifying-an-alien-dictionary.go b/go/0953-verifying-an-alien-dictionary.go new file mode 100644 index 000000000..10cfd3832 --- /dev/null +++ b/go/0953-verifying-an-alien-dictionary.go @@ -0,0 +1,26 @@ +func isAlienSorted(words []string, order string) bool { + // first differing char + // if word A is prefix of word B, word B must be AFTER word A + orderInd := make(map[byte]int) + for i := 0; i < len(order); i++ { + orderInd[order[i]] = i + } + + for i := 0; i < len(words) - 1; i++ { + w1, w2 := words[i], words[i + 1] + + for j := 0; j < len(w1); j++ { + if j == len(w2) { + return false + } + + if w1[j] != w2[j] { + if orderInd[w2[j]] < orderInd[w1[j]] { + return false + } + break + } + } + } + return true +} diff --git a/go/0958-check-completeness-of-a-binary-tree.go b/go/0958-check-completeness-of-a-binary-tree.go new file mode 100644 index 000000000..e1ca84882 --- /dev/null +++ b/go/0958-check-completeness-of-a-binary-tree.go @@ -0,0 +1,24 @@ +func isCompleteTree(root *TreeNode) bool { + if root == nil { + return true + } + + nilNodeFound := false + q := []*TreeNode{root} + + for len(q) > 0 { + node := q[0] + q = q[1:] + + if node == nil { + nilNodeFound = true + } else { + if nilNodeFound { + return false + } + q = append(q, node.Left) + q = append(q, node.Right) + } + } + return true +} \ No newline at end of file diff --git a/go/0973-k-closest-points-to-origin.go b/go/0973-k-closest-points-to-origin.go new file mode 100644 index 000000000..93186b7ec --- /dev/null +++ b/go/0973-k-closest-points-to-origin.go @@ -0,0 +1,55 @@ +type PointEntry struct { + dist int + x int + y int +} + +type PointHeap []*PointEntry + +func (h PointHeap) Len() int { return len(h) } +func (h PointHeap) Less(i, j int) bool { return h[i].dist < h[j].dist } +func (h PointHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } +func (h *PointHeap) Push(x interface{}) {*h = append(*h, x.(*PointEntry))} +func (h *PointHeap) Pop() interface{} { + old := *h + n := len(old) + x := old[n-1] + *h = old[0 : n-1] + return x +} + +const X = 0 +const Y = 1 +func kClosest(points [][]int, k int) [][]int { + _pts := make([]*PointEntry, 0, len(points)) + for _, point := range points { + dist := (pow(abs(point[X] - 0), 2) + pow(abs(point[Y] - 0), 2)) + _pts = append(_pts, &PointEntry{dist: dist, x: point[X], y: point[Y]}) + } + pts := PointHeap(_pts) + + res := make([][]int, 0) + heap.Init(&pts) + for i := 0; i < k; i++ { + pointEntry := heap.Pop(&pts).(*PointEntry) + res = append(res, []int{pointEntry.x, pointEntry.y}) + } + return res +} + +func abs(a int) int { + if a < 0 { + return -a + } + return a +} + +func pow(x, n int) int { + if n == 0 { + return 1 + } else if n == 1 { + return x + } else { + return pow(x, n/2) * pow(x, (n + 1)/2) + } +} diff --git a/go/0977-squares-of-a-sorted-array.go b/go/0977-squares-of-a-sorted-array.go new file mode 100644 index 000000000..6000ed266 --- /dev/null +++ b/go/0977-squares-of-a-sorted-array.go @@ -0,0 +1,22 @@ +func sortedSquares(nums []int) []int { + sq := make([]int, len(nums)) + i := len(nums) - 1 + for l, r := 0, len(nums)-1; l <= r; { + if abs(nums[l]) > abs(nums[r]) { + sq[i] = nums[l] * nums[l] + l++ + } else { + sq[i] = nums[r] * nums[r] + r-- + } + i-- + } + return sq +} + +func abs(x int) int { + if x < 0 { + x = x * -1 + } + return x +} \ No newline at end of file diff --git a/go/0981-time-based-key-value-store.go b/go/0981-time-based-key-value-store.go new file mode 100644 index 000000000..a2c100e1b --- /dev/null +++ b/go/0981-time-based-key-value-store.go @@ -0,0 +1,45 @@ +type TimeMap struct { + store map[string][]ValStamp // key : list of [val, timestamp] +} + +type ValStamp struct { + Val string + Time int +} + +func Constructor() TimeMap { + return TimeMap{store: make(map[string][]ValStamp)} +} + +func (this *TimeMap) Set(key string, value string, timestamp int) { + if _, ok := this.store[key]; !ok { + this.store[key] = make([]ValStamp, 0) + } + this.store[key] = append(this.store[key], ValStamp{value, timestamp}) +} + +func (this *TimeMap) Get(key string, timestamp int) string { + var res string + var values []ValStamp + if _, ok := this.store[key]; ok { + values = this.store[key] + } + l, r := 0, len(values)-1 + for l <= r { + mid := l + (r-l)/2 + if values[mid].Time <= timestamp { + res = values[mid].Val + l = mid + 1 + } else { + r = mid - 1 + } + } + return res +} + +/** + * Your TimeMap object will be instantiated and called as such: + * obj := Constructor(); + * obj.Set(key,value,timestamp); + * param_2 := obj.Get(key,timestamp); + */ \ No newline at end of file diff --git a/go/0989-add-to-array-form-of-integer.go b/go/0989-add-to-array-form-of-integer.go new file mode 100644 index 000000000..328f58eed --- /dev/null +++ b/go/0989-add-to-array-form-of-integer.go @@ -0,0 +1,37 @@ +package main + +func main() { + +} + +func addToArrayForm(num []int, k int) []int { + // Reverse + for i, j := 0, len(num)-1; i < j; i, j = i+1, j-1 { + num[i], num[j] = num[j], num[i] + } + i := 0 + + for k > 0 { + digit := k % 10 + + if i < len(num) { + num[i] += digit + } else { + num = append(num, digit) + } + + carry := num[i] / 10 + num[i] %= 10 + + k /= 10 + k += carry + i++ + } + + // Reverse + for i2, j := 0, len(num)-1; i2 < j; i2, j = i2+1, j-1 { + num[i2], num[j] = num[j], num[i2] + } + + return num +} \ No newline at end of file diff --git a/go/0994-rotting-oranges.go b/go/0994-rotting-oranges.go new file mode 100644 index 000000000..c03015bec --- /dev/null +++ b/go/0994-rotting-oranges.go @@ -0,0 +1,48 @@ +const ROW = 0 +const COL = 1 +func orangesRotting(grid [][]int) int { + q := make([][]int, 0) + fresh := 0 + time := 0 + + for r := 0; r < len(grid); r++ { + for c := 0; c < len(grid[0]); c++ { + if grid[r][c] == 1 { + fresh += 1 + } + if grid[r][c] == 2 { + q = append(q, []int{r, c}) + } + } + } + + directions := [4][2]int{{0, 1}, {0, -1}, {1, 0}, {-1, 0}} + for fresh > 0 && len(q) != 0 { + length := len(q) + for i := 0; i < length; i++ { + cell := q[0] + q = q[1:] + + for _, d := range directions { + row, col := cell[ROW] + d[ROW], cell[COL] + d[COL] + + // if in bounds and notrotten, make rotten + // and add to q + if( + row >= 0 && row < len(grid) && + col >= 0 && col < len(grid[0]) && + grid[row][col] == 1) { + grid[row][col] = 2 + q = append(q, []int{row, col}) + fresh -= 1 + } + } + } + time += 1 + } + + if fresh == 0 { + return time; + } + return -1 +} diff --git a/go/1029-two-city-scheduling.go b/go/1029-two-city-scheduling.go new file mode 100644 index 000000000..386b2a548 --- /dev/null +++ b/go/1029-two-city-scheduling.go @@ -0,0 +1,17 @@ +package main + +import "sort" + +func main() { + +} + +func twoCitySchedCost(costs [][]int) int { + sort.Slice(costs, func(a, b int) bool { return costs[a][1] - costs[a][0] < costs[b][1] - costs[b][0]}); + + n ,totalCost := len(costs) / 2, 0 + for i := 0; i < n ; i++ { + totalCost += costs[i][1] + costs[i +n][0] + } + return totalCost +} \ No newline at end of file diff --git a/go/1046-last-stone-weight.go b/go/1046-last-stone-weight.go new file mode 100644 index 000000000..c1f8f30f7 --- /dev/null +++ b/go/1046-last-stone-weight.go @@ -0,0 +1,43 @@ +type IntHeap []int + +func (h IntHeap) Len() int { return len(h) } +func (h IntHeap) Less(i, j int) bool { return h[i] < h[j] } +func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } +func (h *IntHeap) Push(x interface{}) {*h = append(*h, x.(int))} +func (h *IntHeap) Pop() interface{} { + old := *h + n := len(old) + x := old[n-1] + *h = old[0 : n-1] + return x +} + +type KthLargest struct { + minHeap *IntHeap + k int +} + +func lastStoneWeight(_stones []int) int { + for s := 0; s < len(_stones); s++ { + _stones[s] *= -1 + } + stones := IntHeap(_stones) + heap.Init(&stones) + + for len(stones) > 1 { + first, _ := heap.Pop(&stones).(int) + second, _ := heap.Pop(&stones).(int) + if second > first { + heap.Push(&stones, first - second) + } + } + stones = append(stones, 0) + return abs(stones[0]) +} + +func abs(x int) int { + if x < 0 { + return -x + } + return x +} diff --git a/go/1071-greatest-common-divisor-of-strings.go b/go/1071-greatest-common-divisor-of-strings.go new file mode 100644 index 000000000..9d7c47390 --- /dev/null +++ b/go/1071-greatest-common-divisor-of-strings.go @@ -0,0 +1,37 @@ +package main + +import "strings" + +func main() { + +} + +func gcdOfStrings(str1 string, str2 string) string { + len1, len2 := len(str1), len(str2) + var isDivisor func(l int) bool + + isDivisor = func(l int) bool { + if len1%l != 0 || len2%l != 0 { + return false + } + + f1, f2 := len1/l, len2/l + + return strings.Repeat(str1[:l], f1) == str1 && strings.Repeat(str1[:l], f2) == str2 + } + + for l := min(len1,len2); l > 0; l-- { + if isDivisor(l) { + return str1[:l] + } + } + + return "" +} + +func min( a,b int) int { + if a < b { + return a + } + return b +} \ No newline at end of file diff --git a/go/1091-shortest-path-in-binary-matrix.go b/go/1091-shortest-path-in-binary-matrix.go new file mode 100644 index 000000000..536783e17 --- /dev/null +++ b/go/1091-shortest-path-in-binary-matrix.go @@ -0,0 +1,35 @@ +func shortestPathBinaryMatrix(grid [][]int) int { + rows, cols := len(grid), len(grid[0]) + // if top-left or bottom-right == 1 + // there is no clear path + if grid[0][0] == 1 || grid[rows-1][cols-1] == 1 { + return -1 + } + + seen := make(map[[2]int]bool) + seen[[2]int{0, 0}] = true + queue := make([][3]int, 0) + queue = append(queue, [3]int{0, 0, 1}) + + for len(queue) > 0 { + cell := queue[0] + queue = queue[1:] + r, c, d := cell[0], cell[1], cell[2] + // if we reached bottom-right + // returns the current distance + if r == rows-1 && c == cols-1 { + return d + } + // 8-directionally connected cells + adjCells := [][2]int{{0, -1}, {0, 1}, {-1, 0}, {1, 0}, {-1, -1}, {1, 1}, {-1, 1}, {1, -1}} + for _, adjc := range adjCells { + rr, cc := r+adjc[0], c+adjc[1] + if rr < 0 || rr >= rows || cc < 0 || cc >= cols || grid[cc][rr] == 1 || seen[[2]int{rr, cc}] { + continue + } + queue = append(queue, [3]int{rr, cc, d + 1}) + seen[[2]int{rr, cc}] = true + } + } + return -1 +} diff --git a/go/1137-n-th-tribonacci-number.go b/go/1137-n-th-tribonacci-number.go new file mode 100644 index 000000000..067739691 --- /dev/null +++ b/go/1137-n-th-tribonacci-number.go @@ -0,0 +1,29 @@ +package main + +func main() { + +} + +func tribonacci(n int) int { + t := [3]int{0, 1, 1} + + if n < 3 { + return t[n] + } + + for i := 3; i < n+1; i++ { + t[0], t[1], t[2] = t[1], t[2], sum(t) + } + + return t[2] +} + +func sum(arr [3]int) int { + total := 0 + + for _, val := range arr { + total += val + } + + return total +} \ No newline at end of file diff --git a/go/1143-Longest-Common-Subsequence.go b/go/1143-Longest-Common-Subsequence.go new file mode 100644 index 000000000..286ea9935 --- /dev/null +++ b/go/1143-Longest-Common-Subsequence.go @@ -0,0 +1,26 @@ +func longestCommonSubsequence(text1 string, text2 string) int { + dp := make([][]int, len(text1) + 1) + + for i := 0; i < len(dp); i++ { + dp[i] = make([]int, len(text2) + 1) + } + + for i := len(text1) - 1; i >= 0; i-- { + for j := len(text2) - 1; j >= 0; j-- { + if text1[i] == text2[j] { + dp[i][j] = 1 + dp[i + 1][j + 1] + } else { + dp[i][j] = max(dp[i][j + 1], dp[i + 1][j]) + } + } + } + return dp[0][0] +} + +func max(a, b int) int { + if a > b { + return a + } + + return b +} diff --git a/go/1143-longest-common-subsequence.go b/go/1143-longest-common-subsequence.go new file mode 100644 index 000000000..286ea9935 --- /dev/null +++ b/go/1143-longest-common-subsequence.go @@ -0,0 +1,26 @@ +func longestCommonSubsequence(text1 string, text2 string) int { + dp := make([][]int, len(text1) + 1) + + for i := 0; i < len(dp); i++ { + dp[i] = make([]int, len(text2) + 1) + } + + for i := len(text1) - 1; i >= 0; i-- { + for j := len(text2) - 1; j >= 0; j-- { + if text1[i] == text2[j] { + dp[i][j] = 1 + dp[i + 1][j + 1] + } else { + dp[i][j] = max(dp[i][j + 1], dp[i + 1][j]) + } + } + } + return dp[0][0] +} + +func max(a, b int) int { + if a > b { + return a + } + + return b +} diff --git a/go/1189-maximum-number-of-balloons.go b/go/1189-maximum-number-of-balloons.go new file mode 100644 index 000000000..1cdd19926 --- /dev/null +++ b/go/1189-maximum-number-of-balloons.go @@ -0,0 +1,25 @@ +func maxNumberOfBalloons(text string) int { + countText := counter(text) + balloon := counter("balloon") + + res := len(text) + for c, _ := range balloon { + res = min(res, countText[c] / balloon[c]) + } + return res +} + +func counter(text string) map[rune]int { + count := make(map[rune]int) + for _, c := range text { + count[c] = count[c] + 1 + } + return count +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} diff --git a/go/1220-count-vowels-permutation.go b/go/1220-count-vowels-permutation.go new file mode 100644 index 000000000..28b4ab35f --- /dev/null +++ b/go/1220-count-vowels-permutation.go @@ -0,0 +1,9 @@ +const mod = 1_000_000_007 + +func countVowelPermutation(n int) int { + a, e, i, o, u := 1, 1, 1, 1, 1 + for k := 0; k < n-1; k++ { + a, e, i, o, u = e%mod, (a+i)%mod, (a+e+o+u)%mod, (i+u)%mod, a%mod + } + return (a + e + i + o + u) % mod +} \ No newline at end of file diff --git a/go/1299-Replace-Elements-With-Greatest-Element-On-Right-Side.go b/go/1299-Replace-Elements-With-Greatest-Element-On-Right-Side.go new file mode 100644 index 000000000..3f3afa24f --- /dev/null +++ b/go/1299-Replace-Elements-With-Greatest-Element-On-Right-Side.go @@ -0,0 +1,16 @@ +func findMaxElement(rightMax int, lastElement int) int { + if lastElement > rightMax { + rightMax = lastElement + } + return rightMax +} + +func replaceElements(arr []int) []int { + var rightMax int = -1 + for i := len(arr) - 1; i >= 0; i-- { + var newMax int = findMaxElement(rightMax, arr[i]) + arr[i] = rightMax + rightMax = newMax + } + return arr +} diff --git a/go/1299-replace-elements-with-greatest-element-on-right-side.go b/go/1299-replace-elements-with-greatest-element-on-right-side.go new file mode 100644 index 000000000..3f3afa24f --- /dev/null +++ b/go/1299-replace-elements-with-greatest-element-on-right-side.go @@ -0,0 +1,16 @@ +func findMaxElement(rightMax int, lastElement int) int { + if lastElement > rightMax { + rightMax = lastElement + } + return rightMax +} + +func replaceElements(arr []int) []int { + var rightMax int = -1 + for i := len(arr) - 1; i >= 0; i-- { + var newMax int = findMaxElement(rightMax, arr[i]) + arr[i] = rightMax + rightMax = newMax + } + return arr +} diff --git a/go/1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.go b/go/1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.go new file mode 100644 index 000000000..dc1ed0326 --- /dev/null +++ b/go/1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.go @@ -0,0 +1,24 @@ +func numOfSubarrays(arr []int, k int, threshold int) int { + res := 0 + + var sum func([]int) int + sum = func(nums []int) int { + r := 0 + for _, n := range nums { + r += n + } + return r + } + + curSum := sum(arr[:k-1]) + + for i := k - 1; i < len(arr); i++ { + curSum += arr[i] + if (curSum / k) >= threshold { + res += 1 + } + curSum -= arr[i-k+1] + } + + return res +} diff --git a/go/1448-count-good-nodes-in-binary-tree.go b/go/1448-count-good-nodes-in-binary-tree.go new file mode 100644 index 000000000..878854e42 --- /dev/null +++ b/go/1448-count-good-nodes-in-binary-tree.go @@ -0,0 +1,30 @@ +/** + * Definition for a binary tree node. + * type TreeNode struct { + * Val int + * Left *TreeNode + * Right *TreeNode + * } + */ + func goodNodes(root *TreeNode) int { + return goodNodesUtil(root, root.Val) +} + +func goodNodesUtil(root *TreeNode, parent int) int { + if root == nil { + return 0 + } + + res := 1 + max := root.Val + + if parent > root.Val { + res = 0 + max = parent + } + + res += goodNodesUtil(root.Left, max) + res += goodNodesUtil(root.Right, max) + + return res +} \ No newline at end of file diff --git a/go/1456-maximum-number-of-vowels-in-a-substring-of-given-length.go b/go/1456-maximum-number-of-vowels-in-a-substring-of-given-length.go new file mode 100644 index 000000000..f2283b329 --- /dev/null +++ b/go/1456-maximum-number-of-vowels-in-a-substring-of-given-length.go @@ -0,0 +1,25 @@ +func maxVowels(s string, k int) int { + vowel := map[byte]bool{ + 'a': true, + 'e': true, + 'i': true, + 'o': true, + 'u': true, + } + + l, cnt, res := 0, 0, 0 + for r := range len(s) { + if vowel[s[r]] { + cnt += 1 + } + + if r-l+1 > k { + if vowel[s[l]] { + cnt -= 1 + } + l += 1 + } + res = max(res, cnt) + } + return res +} diff --git a/go/1470-shuffle-the-array.go b/go/1470-shuffle-the-array.go new file mode 100644 index 000000000..cd3c91ac4 --- /dev/null +++ b/go/1470-shuffle-the-array.go @@ -0,0 +1,25 @@ +package main + +import "math" + +func main() { + +} + +func shuffle(nums []int, n int) []int { + for i := 0; i < n; i++ { + nums[i] = nums[i] << 10 + nums[i] = nums[i] | nums[i+n] + } + + j := 2*n - 1 + + for i := n - 1; i > -1; i-- { + y := nums[i] & (int(math.Pow(2.,10.)) - 1) + x := nums[i] >> 10 + nums[j] = y + nums[j-1] = x + j -= 2 + } + return nums +} \ No newline at end of file diff --git a/go/1472-design-browser-history.go b/go/1472-design-browser-history.go new file mode 100644 index 000000000..d1a603592 --- /dev/null +++ b/go/1472-design-browser-history.go @@ -0,0 +1,36 @@ +package main + +import "math" + +func main() { + +} + +type BrowserHistory struct { + history []string + current int +} + +func Constructor(homepage string) BrowserHistory { + return BrowserHistory{ + history: []string{homepage}, + current: 0, + } +} + +func (this *BrowserHistory) Visit(url string) { + this.history = this.history[0: this.current + 1] + this.history = append(this.history, url) + this.current++ +} + +func (this *BrowserHistory) Back(steps int) string { + this.current = int(math.Max(float64(this.current) - float64(steps), 0)) + return this.history[this.current] +} + +func (this *BrowserHistory) Forward(steps int) string { + this.current = int(math.Min(float64(this.current) +float64(steps), float64(len(this.history) - 1))) + return this.history[this.current] +} + diff --git a/go/1512-number-of-good-pairs.go b/go/1512-number-of-good-pairs.go new file mode 100644 index 000000000..0e22e25e9 --- /dev/null +++ b/go/1512-number-of-good-pairs.go @@ -0,0 +1,14 @@ +func numIdenticalPairs(nums []int) int { + pairs := 0 + + for i := 0; i < len(nums); i++ { + for j := i+1; j < len(nums); j++ { + if nums[i] == nums[j] { + pairs++ + } + } + } + + return pairs +} + diff --git a/go/1584-min-cost-to-connect-all-points.go b/go/1584-min-cost-to-connect-all-points.go new file mode 100644 index 000000000..32e8962f6 --- /dev/null +++ b/go/1584-min-cost-to-connect-all-points.go @@ -0,0 +1,64 @@ +func minCostConnectPoints(points [][]int) int { + + n := len(points) + adj := make([][][]int, n) + for i := 0; i < n; i++ { + x1, y1 := points[i][0], points[i][1] + for j := i + 1; j < n; j++ { + x2, y2 := points[j][0], points[j][1] + dist := abs(x1-x2) + abs(y1-y2) + adj[i] = append(adj[i], []int{dist, j}) + adj[j] = append(adj[j], []int{dist, i}) + } + } + // prims + res := 0 + visited := make(map[int]bool) + + h := &IntHeap{[2]int{0, 0}} + + for len(visited) < n { + pntObj := heap.Pop(h).([2]int) + cost, pnt := pntObj[0], pntObj[1] + + if visited[pnt] { + continue + } + res += cost + visited[pnt] = true + + for _, neighbour := range adj[pnt] { + nCost, nPoint := neighbour[0], neighbour[1] + + if !visited[nPoint] { + heap.Push(h, [2]int{nCost, nPoint}) + } + } + } + return res +} + +func abs(n int) int { + if n > 0 { + return n + } + return -n +} + +type IntHeap [][2]int + +func (h IntHeap) Len() int { return len(h) } +func (h IntHeap) Less(i, j int) bool { return h[i][0] < h[j][0] } +func (h IntHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } + +func (h *IntHeap) Push(x interface{}) { + *h = append(*h, x.([2]int)) +} + +func (h *IntHeap) Pop() interface{} { + old := *h + n := len(old) + x := old[n-1] + *h = old[:n-1] + return x +} \ No newline at end of file diff --git a/go/1658-minimum-operations-to-reduce-x-to-zero.go b/go/1658-minimum-operations-to-reduce-x-to-zero.go new file mode 100644 index 000000000..f80fdc39f --- /dev/null +++ b/go/1658-minimum-operations-to-reduce-x-to-zero.go @@ -0,0 +1,28 @@ +func minOperations(nums []int, x int) int { + sum := 0 + for _, num := range nums { + sum += num + } + + target := sum - x + currSum := 0 + maxWindow := -1 + l := 0 + for r := range len(nums) { + currSum += nums[r] + + for l <= r && currSum > target { + currSum -= nums[l] + l += 1 + } + + if currSum == target { + maxWindow = max(maxWindow, r-l+1) + } + } + + if maxWindow == -1 { + return maxWindow + } + return len(nums) - maxWindow +} diff --git a/go/1669-merge-in-between-linked-lists.go b/go/1669-merge-in-between-linked-lists.go new file mode 100644 index 000000000..33a52e952 --- /dev/null +++ b/go/1669-merge-in-between-linked-lists.go @@ -0,0 +1,30 @@ +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func mergeInBetween(list1 *ListNode, a int, b int, list2 *ListNode) *ListNode { + curr := list1 + i := 0 + for i < a-1 { + curr = curr.Next + i++ + } + + head := curr + for i <= b { + curr = curr.Next + i++ + } + head.Next = list2 + + for list2.Next != nil { + list2 = list2.Next + } + + list2.Next = curr + + return list1 +} diff --git a/go/1721-swapping-nodes-in-a-linked-list.go b/go/1721-swapping-nodes-in-a-linked-list.go new file mode 100644 index 000000000..03fe7fe41 --- /dev/null +++ b/go/1721-swapping-nodes-in-a-linked-list.go @@ -0,0 +1,27 @@ +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func swapNodes(head *ListNode, k int) *ListNode { + curr := head + for i := 0; i < k-1; i++ { + curr = curr.Next + } + + left := curr + right := head + + for curr.Next != nil { + curr = curr.Next + right = right.Next + } + + left.Val, right.Val = right.Val, left.Val + + return head +} +//Time: O(n) +//Space: O(1) diff --git a/go/1822-sign-of-the-product-of-an-array.go b/go/1822-sign-of-the-product-of-an-array.go new file mode 100644 index 000000000..ad74ee50d --- /dev/null +++ b/go/1822-sign-of-the-product-of-an-array.go @@ -0,0 +1,20 @@ +/* +Time: O(n) +Space: O(1) +*/ + +func arraySign(nums []int) int { + neg := 0 + for _, n := range nums { + if n == 0 { + return 0 + } else if n < 0 { + neg++ + } + } + if neg%2 == 0 { + return 1 + } else { + return -1 + } +} diff --git a/go/1838-frequency-of-the-most-frequent-element.go b/go/1838-frequency-of-the-most-frequent-element.go new file mode 100644 index 000000000..25cb19ebf --- /dev/null +++ b/go/1838-frequency-of-the-most-frequent-element.go @@ -0,0 +1,24 @@ +import ( + "math" + "sort" +) + +func maxFrequency(nums []int, k int) int { + sort.Ints(nums) + + left, right, res, total := 0,0,0,0 + + for right < len(nums) { + total += nums[right] + + for nums[right] * (right - left +1) > total + k { + total -= nums[left] + left++ + } + + res = int(math.Max(float64(res), float64(right - left + 1))) + right++ + } + + return res +} \ No newline at end of file diff --git a/go/1898-maximum-number-of-removable-characters.go b/go/1898-maximum-number-of-removable-characters.go new file mode 100644 index 000000000..aac684f37 --- /dev/null +++ b/go/1898-maximum-number-of-removable-characters.go @@ -0,0 +1,33 @@ +func maximumRemovals(s string, p string, removable []int) int { + l, r := 0, len(removable)-1 + for l <= r { + m := (l + r) / 2 + remove := make(map[int]bool) + for i := 0; i <= m; i++ { + remove[removable[i]] = true + } + if isSubsequence(s, p, remove) { + l = m + 1 + } else { + r = m - 1 + } + } + return r + 1 +} + +func isSubsequence(s, p string, remove map[int]bool) bool { + i, j := 0, 0 + for i < len(s) && j < len(p) { + if remove[i] == true || s[i] != p[j] { + i++ + continue + } + i++ + j++ + } + return j == len(p) +} + + + + \ No newline at end of file diff --git a/go/1905-count-sub-islands.go b/go/1905-count-sub-islands.go new file mode 100644 index 000000000..bb5ef1fcb --- /dev/null +++ b/go/1905-count-sub-islands.go @@ -0,0 +1,39 @@ +func countSubIslands(grid1 [][]int, grid2 [][]int) int { + ROWS, COLS := len(grid1), len(grid1[0]) + visit := make(map[int]bool) + + var dfs func(int, int) bool + dfs = func(r, c int) bool { + if ( + r < 0 || + c < 0 || + r == ROWS || + c == COLS || + grid2[r][c] == 0 || + visit[r*COLS + c]) { + return true + } + + visit[r*COLS + c] = true + res := true + if grid1[r][c] == 0 { + res = false + } + + res = dfs(r - 1, c) && res + res = dfs(r + 1, c) && res + res = dfs(r, c - 1) && res + res = dfs(r, c + 1) && res + return res + } + + count := 0 + for r := 0; r < ROWS; r++ { + for c := 0; c < COLS; c++ { + if grid2[r][c] != 0 && !visit[r*COLS + c] && dfs(r, c) { + count += 1 + } + } + } + return count +} diff --git a/go/1929-concatenation-of-array.go b/go/1929-concatenation-of-array.go new file mode 100644 index 000000000..05d29edeb --- /dev/null +++ b/go/1929-concatenation-of-array.go @@ -0,0 +1,11 @@ +func getConcatenation(nums []int) []int { + n := len(nums) + ans := make([]int, 2*n) //lets make an array of int type and 2*n size + i := 0 + for i < n { //implementing for loop for the given condition + ans[i] = nums[i] + ans[i+n] = nums[i] + i++ + } + return ans +} diff --git a/go/1958-check-if-move-is-legal.go b/go/1958-check-if-move-is-legal.go new file mode 100644 index 000000000..d13070c17 --- /dev/null +++ b/go/1958-check-if-move-is-legal.go @@ -0,0 +1,32 @@ +const ROW = 0 +const COL = 1 +func checkMove(board [][]byte, rMove int, cMove int, color byte) bool { + ROWS, COLS := len(board), len(board[0]) + direction := [][]int{{1, 0}, {-1, 0}, {0, 1}, {0, -1}, + {1, 1}, {-1, -1}, {1, -1}, {-1, 1}} + board[rMove][cMove] = color + + legal := func(row, col int, color byte, direc []int) bool { + dr, dc := direc[ROW], direc[COL] + row, col = row + dr, col + dc + length := 1 + + for 0 <= row && row < ROWS && 0 <= col && col < COLS { + length += 1 + if board[row][col] == '.' { + return false + } else if(board[row][col] == color) { + return length >= 3 + } + row, col = row + dr, col + dc + } + return false + } + + for _, d := range direction { + if legal(rMove, cMove, color, d) { + return true + } + } + return false +} diff --git a/go/1963-minimum-number-of-swaps-to-make-the-string-balanced.go b/go/1963-minimum-number-of-swaps-to-make-the-string-balanced.go new file mode 100644 index 000000000..39c8b37c2 --- /dev/null +++ b/go/1963-minimum-number-of-swaps-to-make-the-string-balanced.go @@ -0,0 +1,15 @@ +func minSwaps(s string) int { + close, maxClose := 0, 0 + for _, c := range s { + if c == '[' { + close += -1 + } else { + close += +1 + } + + if close > maxClose { + maxClose = close + } + } + return (maxClose + 1) / 2 +} \ No newline at end of file diff --git a/go/1968-array-with-elements-not-equal-to-average-of-neighbors.go b/go/1968-array-with-elements-not-equal-to-average-of-neighbors.go new file mode 100644 index 000000000..019929db3 --- /dev/null +++ b/go/1968-array-with-elements-not-equal-to-average-of-neighbors.go @@ -0,0 +1,16 @@ +func rearrangeArray(nums []int) []int { + sort.Ints(nums) + res := []int{} + + l, r := 0, len(nums)-1 + for len(res) != len(nums) { + res = append(res, nums[l]) + l += 1 + + if l <= r { + res = append(res, nums[r]) + r -= 1 + } + } + return res +} diff --git a/go/1984-minimum-difference-between-highest-and-lowest-of-k-scores.go b/go/1984-minimum-difference-between-highest-and-lowest-of-k-scores.go new file mode 100644 index 000000000..9324cf84f --- /dev/null +++ b/go/1984-minimum-difference-between-highest-and-lowest-of-k-scores.go @@ -0,0 +1,20 @@ +func minimumDifference(nums []int, k int) int { + sort.Ints(nums) + l := 0 + r := k - 1 + res := math.MaxInt32 + + for r < len(nums) { + res = min(res, nums[r] - nums[l]) + l = l + 1 + r = r + 1 + } + return res +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} diff --git a/go/1985-find-the-kth-largest-integer-in-the-array.go b/go/1985-find-the-kth-largest-integer-in-the-array.go new file mode 100644 index 000000000..7691453ae --- /dev/null +++ b/go/1985-find-the-kth-largest-integer-in-the-array.go @@ -0,0 +1,16 @@ +import "sort" + +func kthLargestNumber(nums []string, k int) string { + sort.Slice(nums, func(i, j int) bool { + if len(nums[i]) != len(nums[j]) { + return len(nums[i]) > len(nums[j]) + } + for idx := range nums[i] { + if nums[i][idx] != nums[j][idx] { + return nums[i][idx] > nums[j][idx] + } + } + return true + }) + return nums[k-1] +} diff --git a/go/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.go b/go/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.go new file mode 100644 index 000000000..4086e0a22 --- /dev/null +++ b/go/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.go @@ -0,0 +1,46 @@ +func maxProduct(s string) (res int) { + dp := make([]int, 1< b { + return a + } + return b + } + + for m := 1; m <= mask; m++ { + dp[m] = palindromeSize(s, m) + } + + for m1 := mask; m1 > 0; m1-- { + if dp[m1]*(len(s)-dp[m1]) <= res { + continue + } + for m2 := mask ^ m1; m2 > 0; m2 = (m2 - 1) & (mask ^ m1) { + res = max(res, dp[m1]*dp[m2]) + } + } + return +} \ No newline at end of file diff --git a/go/2130-maximum-twin-sum-of-a-linked-list.go b/go/2130-maximum-twin-sum-of-a-linked-list.go new file mode 100644 index 000000000..c038d7512 --- /dev/null +++ b/go/2130-maximum-twin-sum-of-a-linked-list.go @@ -0,0 +1,51 @@ +/** + * Definition for singly-linked list. + * type ListNode struct { + * Val int + * Next *ListNode + * } + */ +func pairSum(head *ListNode) int { + // find mid of a list + slow := head + fast := head.Next + for fast != nil && fast.Next != nil { + slow = slow.Next + fast = fast.Next.Next + } + + // reverse links in 2nd half + second := reverse(slow.Next) + // split into 2 lists + slow.Next = nil + + // traverse two lists with pointers and compare twin sum + first := head + var maxSum int + for first != nil && second != nil { + maxSum = max(maxSum, first.Val+second.Val) + first = first.Next + second = second.Next + } + return maxSum +} + +func reverse(node *ListNode) *ListNode { + var curr, prev *ListNode = node, nil + for curr != nil { + tmp := curr.Next + curr.Next = prev + prev = curr + curr = tmp + } + return prev +} + +func max(a, b int) int { + if a > b { + return a + } + return b +} +//Time: O(n) +//Space: O(1) diff --git a/go/2421-number-of-good-paths.go b/go/2421-number-of-good-paths.go new file mode 100644 index 000000000..d5387854d --- /dev/null +++ b/go/2421-number-of-good-paths.go @@ -0,0 +1,108 @@ +package main + +import "sort" + +func main() { + +} + +type UnionFind struct { + parent []int + rank []int +} + +func Constructor(n int) UnionFind { + parent := make([]int, n) + rank := make([]int, n) + + for i := 0; i < n; i++ { + parent[i] = i + rank[i] = 1 + } + return UnionFind{ + parent, rank, + } +} + +func (u *UnionFind) find(i int) int { + for i != u.parent[i] { + u.parent[i] = u.parent[u.parent[i]] + i = u.parent[i] + } + + return i +} + +func (u *UnionFind) union(a, b int) bool { + aRoot, bRoot := u.find(a), u.find(b) + + if aRoot == bRoot { + return false + } + + if u.rank[aRoot] < u.rank[bRoot] { + u.parent[aRoot] = bRoot + u.rank[bRoot] += u.rank[aRoot] + } else { + u.parent[bRoot] = aRoot + u.rank[aRoot] += u.rank[bRoot] + } + + return true +} + +func getAdjList(edges [][]int) map[int][]int { + adj := make(map[int][]int) + + for _, edge := range edges { + a, b := edge[0], edge[1] + + adj[a] = append(adj[a], b) + adj[b] = append(adj[b], a) + } + + return adj +} + +func getValToIndex(vals []int) map[int][]int { + valToIndex := make(map[int][]int) + + for i, val := range vals { + valToIndex[val] = append(valToIndex[val], i) + } + + return valToIndex +} + +func numberOfGoodPaths(vals []int, edges [][]int) int { + adj := getAdjList(edges) + valToIndex := getValToIndex(vals) + + res := 0 + uf := Constructor(len(vals)) + + keys := make([]int, 0, len(valToIndex)) + for k := range valToIndex { + keys = append(keys, k) + } + sort.Ints(keys) + + for _, val := range keys { + for _, i := range valToIndex[val] { + for _, nei := range adj[i] { + if vals[nei] <= vals[i] { + uf.union(nei, i) + } + } + } + + count := make(map[int]int) + + for _, i := range valToIndex[val] { + count[uf.find(i)] += 1 + res += count[uf.find(i)] + } + } + + return res +} \ No newline at end of file diff --git a/hints/add-two-numbers.md b/hints/add-two-numbers.md new file mode 100644 index 000000000..2cdafc4fa --- /dev/null +++ b/hints/add-two-numbers.md @@ -0,0 +1,23 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m + n) time and O(1) space, where m is the length of list l1 and n is the length of list l2. +

+
+ +
+
+ Hint 1 +

+ Try to visualize the addition of two numbers. We know that the addition of two numbers is done by starting at the one's digit. We add the numbers by going through digit by digit. We track the extra value as a carry because the addition of two digits can result in a number with two digits. The carry is then added to the next digits, and so on. How do you implement this in case of linked lists? +

+
+ +
+
+ Hint 2 +

+ We track the extra value, carry, here as well. We iterate through the lists l1 and l2 until both lists reach null. We add the values of both nodes as well as the carry. If either of the nodes is null, we add 0 in its place and continue the process while tracking the carry simultaneously. Once we complete the process, if we are left with any carry, we add an extra node with that carry value and return the head of the result list. +

+
\ No newline at end of file diff --git a/hints/anagram-groups.md b/hints/anagram-groups.md new file mode 100644 index 000000000..b77b383f8 --- /dev/null +++ b/hints/anagram-groups.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m * n) time and O(m) space, where m is the number of strings and n is the length of the longest string. +

+
+ +
+
+ Hint 1 +

+ A naive solution would be to sort each string and group them using a hash map. This would be an O(m * nlogn) solution. Though this solution is acceptable, can you think of a better way without sorting the strings? +

+
+ +
+
+ Hint 2 +

+ By the definition of an anagram, we only care about the frequency of each character in a string. How is this helpful in solving the problem? +

+
+ +
+
+ Hint 3 +

+ We can simply use an array of size O(26), since the character set is a through z (26 continuous characters), to count the frequency of each character in a string. Then, we can use this array as the key in the hash map to group the strings. +

+
\ No newline at end of file diff --git a/hints/balanced-binary-tree.md b/hints/balanced-binary-tree.md new file mode 100644 index 000000000..51473ca01 --- /dev/null +++ b/hints/balanced-binary-tree.md @@ -0,0 +1,23 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(n) space, where n is the number of nodes in the tree. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would involve traversing every node and checking whether the tree rooted at each node is balanced by computing the heights of its left and right subtrees. This approach would result in an O(n^2) solution. Can you think of a more efficient way? Perhaps you could avoid repeatedly computing the heights for every node by determining balance and height in a single traversal. +

+
+ +
+
+ Hint 2 +

+ We can use the Depth First Search (DFS) algorithm to compute the heights at each node. While calculating the heights of the left and right subtrees, we also check if the tree rooted at the current node is balanced. If leftHeight - rightHeight > 1, we update a global variable, such as isBalanced = False. After traversing all the nodes, the value of isBalanced indicates whether the entire tree is balanced or not. +

+
\ No newline at end of file diff --git a/hints/binary-search.md b/hints/binary-search.md new file mode 100644 index 000000000..a1a01c8e2 --- /dev/null +++ b/hints/binary-search.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(logn) time and O(1) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ Can you find an algorithm that is useful when the array is sorted? Maybe other than linear seacrh. +

+
+ +
+
+ Hint 2 +

+ The problem name is the name of the algorithm that we can use. We need to find a target value and if it does not exist in the array return -1. We have l and r as the boundaries of the segment of the array in which we are searching. Try building conditions to eliminate half of the search segment at each step. Maybe sorted nature of the array can be helpful. +

+
+ +
+
+ Hint 3 +

+ We compare the target value with the mid of the segment. For example, consider the array [1, 2, 3, 4, 5] and target = 4. The mid value is 3, thus, on the next iteration we search to the right of mid. The remaining segment is [4,5]. Why? +

+
+ +
+
+ Hint 4 +

+ Because the array is sorted, all elements to the left of mid (including 3) are guaranteed to be smaller than the target. Therefore, we can safely eliminate that half of the array from consideration, narrowing the search to the right half and repeat this search until we find the target. +

+
\ No newline at end of file diff --git a/hints/binary-tree-diameter.md b/hints/binary-tree-diameter.md new file mode 100644 index 000000000..d8c6b6ec3 --- /dev/null +++ b/hints/binary-tree-diameter.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(n) space, where n is the number of nodes in the tree. +

+
+ +
+
+ Hint 1 +

+ The diameter of a binary tree is the maximum among the sums of the left height and right height of the nodes in the tree. Why? +

+
+ +
+
+ Hint 2 +

+ Because the diameter of a binary tree is defined as the longest path between any two nodes in the tree. The path may or may not pass through the root. For any given node, the longest path that passes through it is the sum of the height of its left subtree and the height of its right subtree. +

+
+ +
+
+ Hint 3 +

+ A brute force solution would be to go through every node in the tree and compute its left height and right height, returning the maximum diameter found. This would be an O(n^2) solution. Can you think of a better way? Maybe we can compute the diameter as we calculate the height of the tree? Think about what information you need from each subtree during a single traversal. +

+
+ +
+
+ Hint 4 +

+ We can use the Depth First Search (DFS) algorithm to calculate the height of the tree. At each node, the subtrees return their respective heights (leftHeight and rightHeight). Then we calculate the diameter at that node as d = leftHeight + rightHeight. We use a global variable to update the maximum diameter as needed during the traversal. +

+
\ No newline at end of file diff --git a/hints/binary-tree-from-preorder-and-inorder-traversal.md b/hints/binary-tree-from-preorder-and-inorder-traversal.md new file mode 100644 index 000000000..d46b6141c --- /dev/null +++ b/hints/binary-tree-from-preorder-and-inorder-traversal.md @@ -0,0 +1,47 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(n) space, where n is the number of nodes. +

+
+ +
+
+ Hint 1 +

+ You can observe that the in-order traversal helps divide the array into two halves based on the root node: the left part corresponds to the left subtree, and the right part corresponds to the right subtree. Can you think of how we can get the index of the root node in the in-order array? Maybe you should look into the pre-order traversal array. +

+
+ +
+
+ Hint 2 +

+ From the pre-order traversal, we know that the first node is the root node. Using this information, can you now construct the binary tree? +

+
+ +
+
+ Hint 3 +

+ After getting the root node from pre-order traversal, we then look for the index of that node in the in-order array. We can linearly search for the index but this would be an O(n^2) solution. Can you think of a better way? Maybe we can use a data structure to get the index of a node in O(1)? +

+
+ +
+
+ Hint 4 +

+ We can use a hash map to get the index of any node in the in-order array in O(1) time. How can we implement this? +

+
+ +
+
+ Hint 5 +

+ We use Depth First Search (DFS) to construct the tree. A global variable tracks the current index in the pre-order array. Indices l and r represent the segment in the in-order array for the current subtree. For each node in the pre-order array, we create a node, find its index in the in-order array using the hash map, and recursively build the left and right subtrees by splitting the range [l, r] into two parts for the left and right subtrees. +

+
\ No newline at end of file diff --git a/hints/binary-tree-maximum-path-sum.md b/hints/binary-tree-maximum-path-sum.md new file mode 100644 index 000000000..318e1a388 --- /dev/null +++ b/hints/binary-tree-maximum-path-sum.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(n) space, where n is the number of nodes in the given tree. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would involve checking the path sum between every pair of nodes in the tree, leading to an O(n^2) time complexity. Can you think of a more efficient approach? Consider what information you would need at each node to calculate the path sum if it passes through the current node. +

+
+ +
+
+ Hint 2 +

+ At a node, there are three scenarios to compute the maximum path sum that includes the current node. One includes both the left and right subtrees, with the current node as the connecting node. Another path sum includes only one of the subtrees (either left or right), but not both. Another considers the path sum extending from the current node to the parent. However, the parent’s contribution is computed during the traversal at the parent node. Can you implement this? +

+
+ +
+
+ Hint 3 +

+ We can use the Depth First Search (DFS) algorithm to traverse the tree. We maintain a global variable to track the maximum path sum. At each node, we first calculate the maximum path sum from the left and right subtrees by traversing them. After that, we compute the maximum path sum at the current node. This approach follows a post-order traversal, where we visit the subtrees before processing the current node. +

+
+ +
+
+ Hint 4 +

+ We return the maximum path sum from the current node to its parent, considering only one of the subtrees (either left or right) to extend the path. While calculating the left and right subtree path sums, we also ensure that we take the maximum with 0 to avoid negative sums, indicating that we should not include the subtree path in the calculation of the maximum path at the current node. +

+
\ No newline at end of file diff --git a/hints/binary-tree-right-side-view.md b/hints/binary-tree-right-side-view.md new file mode 100644 index 000000000..a02d2c585 --- /dev/null +++ b/hints/binary-tree-right-side-view.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(n) space, where n is the number of nodes in the given tree. +

+
+ +
+
+ Hint 1 +

+ In the right-side view of a tree, can you identify the nodes that are visible? Maybe you could traverse the tree level by level and determine which nodes are visible from the right side. +

+
+ +
+
+ Hint 2 +

+ The nodes visible in the right-side view are the last nodes at each level of the tree. Can you think of an algorithm to identify these nodes? Maybe an algorithm that can traverse the tree level by level. +

+
+ +
+
+ Hint 3 +

+ We can use the Breadth First Search (BFS) algorithm to traverse the tree level by level. Once we completely visit a level, we take the last node of that level and add it to the result array. After processing all levels, we return the result. +

+
\ No newline at end of file diff --git a/hints/burst-balloons.md b/hints/burst-balloons.md new file mode 100644 index 000000000..c553a4f8e --- /dev/null +++ b/hints/burst-balloons.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n ^ 3) time and O(n ^ 2) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ Try to simulate the process recursively by passing the array to the recursive function. At each step, iterate through the array, pop an element, and recursively apply the same process to the two subarrays on both sides of the popped element, returning the maximum result from all recursive paths. This approach is exponential. Can you think of a way to optimize it? Maybe you should consider observing the subproblems instead of modifying the array. +

+
+ +
+
+ Hint 2 +

+ Instead of passing the array, we can pass the range of indices l and r that need to be processed. We pad the input array with 1s on both sides for easier computation, but l and r represent the first and last indices of the original input array. Can you think of a reverse engineering approach for popping elements? +

+
+ +
+
+ Hint 3 +

+ We determine the result by considering each element as the last one to be popped in the current range. For each element, we calculate its value by multiplying it with the elements at l - 1 and r + 1, then recursively solve the subproblems for the ranges (l, i - 1) and (i + 1, r), where i is the current element in the given range. Can you think of a way to optimize and avoid redundant calculations? +

+
+ +
+
+ Hint 4 +

+ We can use memoization to cache the results of recursive calls and avoid redundant calculations. A hash map or a 2D array can be used to store results since the recursive function parameters l and r are within the range of the input array size. +

+
\ No newline at end of file diff --git a/hints/buy-and-sell-crypto-with-cooldown.md b/hints/buy-and-sell-crypto-with-cooldown.md new file mode 100644 index 000000000..84988bd54 --- /dev/null +++ b/hints/buy-and-sell-crypto-with-cooldown.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ Try to think in terms of recursion and visualize it as a decision tree. Can you determine the possible decisions at each recursion step? Also, can you identify the base cases and the essential information that needs to be tracked during recursion? +

+
+ +
+
+ Hint 2 +

+ At each recursion step, we can buy only if we haven't already bought a coin, or we can sell if we own one. When buying, we subtract the coin value, and when selling, we add it. We explore all possible buying and selling options recursively, iterating through the coins from left to right using index i. For the cooldown condition, if we buy a coin, we increment the index i by two. +

+
+ +
+
+ Hint 3 +

+ We can use a boolean variable canBuy to indicate whether buying is allowed at the current recursive step. If we go out of bounds, we return 0. This approach is exponential. Can you think of a way to optimize it? +

+
+ +
+
+ Hint 4 +

+ We can use memoization to cache the results of recursive calls and avoid recalculations. A hash map or a 2D array can be used to store these results. +

+
\ No newline at end of file diff --git a/hints/buy-and-sell-crypto.md b/hints/buy-and-sell-crypto.md new file mode 100644 index 000000000..0597ae13a --- /dev/null +++ b/hints/buy-and-sell-crypto.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to iterate through the array with index i, considering it as the day to buy, and trying all possible options for selling it on the days to the right of index i. This would be an O(n^2) solution. Can you think of a better way? +

+
+ +
+
+ Hint 2 +

+ You should buy at a price and always sell at a higher price. Can you iterate through the array with index i, considering it as either the buying price or the selling price? +

+
+ +
+
+ Hint 3 +

+ We can iterate through the array with index i, considering it as the selling value. But what value will it be optimal to consider as buying point on the left of index i? +

+
+ +
+
+ Hint 4 +

+ We are trying to maximize profit = sell - buy. If the current i is the sell value, we want to choose the minimum buy value to the left of i to maximize the profit. The result will be the maximum profit among all. However, if all profits are negative, we can return 0 since we are allowed to skip doing transaction. +

+
\ No newline at end of file diff --git a/hints/car-fleet.md b/hints/car-fleet.md new file mode 100644 index 000000000..23c63e134 --- /dev/null +++ b/hints/car-fleet.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(nlogn) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ First draw a picture of all the points which represents the positions and respective speeds of the cars. It is appropriate to represent the position and speed of each car as an array, where each cell corresponds to a car. It is also logical to sort this array based on the positions in descending order. Why? +

+
+ +
+
+ Hint 2 +

+ Because a car can only form a fleet with another car that is ahead of it, sorting the array in descending order ensures clarity about the final speed of each car. Sorting in ascending order would create ambiguity, as the next car might form a fleet with another car while reaching the target, making it difficult to determine its final speed. +

+
+ +
+
+ Hint 3 +

+ Calculating the time for a car to reach the target is straightforward and can be done using the formula: time = (target - position) / speed. Now, it becomes easy to identify that two cars will form a fleet if and only if the car ahead has a time that is greater than or equal to the time of the car behind it. How can we maintain the total number of fleets happened while going through the array? Maybe a data structure is helpful. +

+
+ +
+
+ Hint 4 +

+ We can use a stack to maintain the times of the fleets. As we iterate through the array (sorted in descending order of positions), we compute the time for each car to reach the target and check if it can form a fleet with the car ahead. If the current car's time is less than or equal to the top of the stack, it joins the same fleet. Otherwise, it forms a new fleet, and we push its time onto the stack. The length of the stack at the end represents the total number of fleets formed. +

+
\ No newline at end of file diff --git a/hints/cheapest-flight-path.md b/hints/cheapest-flight-path.md new file mode 100644 index 000000000..b42275442 --- /dev/null +++ b/hints/cheapest-flight-path.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n + (m * k)) time and O(n) space, where n is the number of cities, m is the number of flights, and k is the number of stops. +

+
+ +
+
+ Hint 1 +

+ Consider this as a graph problem where the cities are nodes and the flights are edges connecting two cities, with the ticket cost as the edge weight. Can you think of a shortest path algorithm to solve the problem? Perhaps a better algorithm than Dijkstra's that can intuitively handle the k stops condition. +

+
+ +
+
+ Hint 2 +

+ We can use the Bellman-Ford algorithm. Initialize a prices array of size n with Infinity, setting prices[source] = 0. These values describe the cost to reach a city from the source city. Iterate (k + 1) times (stops are 0-indexed), updating the cost to each city by extending paths from cities with valid costs. We only update the cost for a city if it is less than the previous cost. How would you implement this? +

+
+ +
+
+ Hint 3 +

+ At each level of iteration, we go through the given flights and use them to update the price array with the minimum costs compared to the previous level. We use a temporary prices array at each level to store the updated costs. After completing all levels, we return the result stored in prices[dst]. If that value is Infinity, we return -1 instead. +

+
\ No newline at end of file diff --git a/hints/climbing-stairs.md b/hints/climbing-stairs.md new file mode 100644 index 000000000..78e5ce36b --- /dev/null +++ b/hints/climbing-stairs.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n) time and O(n) space, where n is the number of steps. +

+
+ +
+
+ Hint 1 +

+ At each step, we have two choices: climb one step or climb two steps. We can solve this by considering both options and picking the minimum using recursion. However, this results in O(2^n) time complexity. Can you think of a better approach? Perhaps, try to avoid the repeated work of calling recursion more than once with same parameters. +

+
+ +
+
+ Hint 2 +

+ This is a Dynamic Programming problem. We can use Memoization to avoid repeated work. Create an n-sized array cache to store the results of recursive calls. When the recursion is called with specific parameters, return the stored value if it has already been computed. How would you implement this? +

+
+ +
+
+ Hint 3 +

+ We start the initial recursion with i = 0, indicating that we are at position i. We first check if the current recursion with the given i is already cached. If it is, we immediately return the stored value. Otherwise, we perform the recursion, store the result in the cache, and then return it. Can you think of the base condition to stop the recursion? +

+
+ +
+
+ Hint 4 +

+ At each recursion, we perform two recursive calls: one for climbing one step and another for climbing two steps. The minimum return value between the two is the result for the current recursion. The base condition is to return 0 if i == n. This is a one-dimensional dynamic programming problem, which can be further optimized using more advanced techniques. +

+
\ No newline at end of file diff --git a/hints/clone-graph.md b/hints/clone-graph.md new file mode 100644 index 000000000..1e332bf4a --- /dev/null +++ b/hints/clone-graph.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(V + E) time and O(E) space, where V is the number of vertices and E is the number of edges in the given graph. +

+
+ +
+
+ Hint 1 +

+ We are given only the reference to the node in the graph. Cloning the entire graph means we need to clone all the nodes as well as their child nodes. We can't just clone the node and its neighbor and return the node. We also need to clone the entire graph. Can you think of a recursive way to do this, as we are cloning nodes in a nested manner? Also, can you think of a data structure that can store the nodes with their cloned references? +

+
+ +
+
+ Hint 2 +

+ We can use the Depth First Search (DFS) algorithm. We use a hash map to map the nodes to their cloned nodes. We start from the given node. At each step of the DFS, we create a node with the current node's value. We then recursively go to the current node's neighbors and try to clone them first. After that, we add their cloned node references to the current node's neighbors list. Can you think of a base condition to stop this recursive path? +

+
+ +
+
+ Hint 3 +

+ We stop this recursive path when we encounter a node that has already been cloned or visited. This DFS approach creates an exact clone of the given graph, and we return the clone of the given node. +

+
\ No newline at end of file diff --git a/hints/coin-change-ii.md b/hints/coin-change-ii.md new file mode 100644 index 000000000..3f18a6318 --- /dev/null +++ b/hints/coin-change-ii.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n * a) time and O(n * a) space, where n is the number of coins and a is the given amount. +

+
+ +
+
+ Hint 1 +

+ As we need to find the total number of combinations, think in terms of recursion and visualize it as a decision tree where multiple coin choices are available at each recursion step. Can you determine a way to allow picking the same coin multiple times? Maybe you should consider the decisions made at each recursion step. +

+
+ +
+
+ Hint 2 +

+ The given coins are unique. We recursively iterate through the coins array using index i, tracking the collected amount along the current path. At each step, we can either skip the current coin or pick it, ensuring the total does not exceed the target. To allow picking the same coin multiple times, we recurse with the same index but an updated amount, generating different combinations. +

+
+ +
+
+ Hint 3 +

+ If we reach the target amount, we return 1. The recursion stops if the index goes out of bounds. We count all possible ways and return the total. This approach is exponential. Can you think of a way to optimize it? Maybe you should consider an approach to avoid redundant computations. +

+
+ +
+
+ Hint 4 +

+ We can use memoization to cache the results of recursive calls and avoid redundant computations. A hash map or a 2D array can be used to store these results. +

+
\ No newline at end of file diff --git a/hints/coin-change.md b/hints/coin-change.md new file mode 100644 index 000000000..c8522e7cb --- /dev/null +++ b/hints/coin-change.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n * t) time and O(t) space, where n is the number of coins and t is the given amount. +

+
+ +
+
+ Hint 1 +

+ Think of this problem in terms of recursion and try to visualize the decision tree, as there are multiple choices at each step. We start with the given amount. At each step of recursion, we have n coins and branch into paths using coins that are less than or equal to the current amount. Can you express this in terms of a recurrence relation? Also, try to determine the base condition to stop the recursion. +

+
+ +
+
+ Hint 2 +

+ If the amount is 0, we return 0 coins. The recurrence relation can be expressed as min(1 + dfs(amount - coins[i])), where we return the minimum coins among all paths. This results in an O(n ^ t) solution, where n is the number of coins and t is the total amount. Can you think of a better approach? Perhaps consider the repeated work and find a way to avoid it. +

+
+ +
+
+ Hint 3 +

+ We can use memoization to avoid the repeated work of calculating the result for each recursive call. A hash map or an array of size t can be used to cache the computed values for a specific amount. At each recursion step, we iterate over every coin and extend only the valid paths. If a result has already been computed, we return it from the cache instead of recalculating it. +

+
\ No newline at end of file diff --git a/hints/combination-target-sum-ii.md b/hints/combination-target-sum-ii.md new file mode 100644 index 000000000..ef8aabd56 --- /dev/null +++ b/hints/combination-target-sum-ii.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n * (2^n)) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute-force solution would be to create a hash set, which is used to detect duplicates, to get combinations without duplicates. Can you think of a better way without using a hash set? Maybe you should sort the input array and observe the recursive calls that are responsible for duplicate combinations. +

+
+ +
+
+ Hint 2 +

+ We can use backtracking to generate all combinations whose sum equals the given target. When the input array contains duplicate elements, it may result in duplicate combinations. To avoid this, we can sort the array. Why does sorting help? Because as we traverse the array from left to right, we form combinations with the current element. By skipping duplicate elements, we ensure that the same combinations are not repeated for identical elements. How can you implement this? +

+
+ +
+
+ Hint 3 +

+ We recursively traverse the given array starting at index i, with a variable sum representing the sum of the picked elements. We explore elements from i to the end of the array and extend the recursive path if adding the current element results in sum <= target. When we processed an element, we backtrack and proceed to the next distinct element, skipping any similar elements in the middle to avoid duplicate combinations. +

+
\ No newline at end of file diff --git a/hints/combination-target-sum.md b/hints/combination-target-sum.md new file mode 100644 index 000000000..1eca71f3f --- /dev/null +++ b/hints/combination-target-sum.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(2^(t/m)) time and O(t/m) space, where t is the given target and m is the minimum value in the given array. +

+
+ +
+
+ Hint 1 +

+ Can you think of this problem in terms of a decision tree, where at each step, we have n decisions, where n is the size of the array? In this decision tree, we can observe that different combinations of paths are formed. Can you think of a base condition to stop extending a path? Maybe you should consider the target value. +

+
+ +
+
+ Hint 2 +

+ We can use backtracking to recursively traverse these paths and make decisions to choose an element at each step. We maintain a variable sum, which represents the sum of all the elements chosen in the current path. We stop this recursive path if sum == target, and add a copy of the chosen elements to the result. How do you implement it? +

+
+ +
+
+ Hint 3 +

+ We recursively traverse the array starting from index i. At each step, we select an element from i to the end of the array. We extend the recursive path with elements where sum <= target after including that element. This creates multiple recursive paths, and we append the current list to the result whenever the base condition is met. +

+
\ No newline at end of file diff --git a/hints/combinations-of-a-phone-number.md b/hints/combinations-of-a-phone-number.md new file mode 100644 index 000000000..7154a859d --- /dev/null +++ b/hints/combinations-of-a-phone-number.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n * (4^n)) time and O(n) space, where n is the length of the input string. +

+
+ +
+
+ Hint 1 +

+ We can use a hash map to pair all the digits with their corresponding letters. Think of this as a decision tree, where at each step, we have a digit, and we select one of multiple characters to proceed to the next digit in the given string digits. Can you think of an algorithm to generate all combinations of strings? +

+
+ +
+
+ Hint 2 +

+ We can use backtracking where we select a character and process it, then backtrack to process another character. We recursively iterate on the given string with index i. At each step, we consider the letters from the hash map that correspond to the digit at the i-th index. Can you think of the base condition to stop this recursive path? +

+
+ +
+
+ Hint 3 +

+ We initialize an empty string that represents the choices of the characters throughout the current recursive path. When the index i reaches the end of the string, we add the current string to the result list and return. +

+
\ No newline at end of file diff --git a/hints/copy-linked-list-with-random-pointer.md b/hints/copy-linked-list-with-random-pointer.md new file mode 100644 index 000000000..73a1417ae --- /dev/null +++ b/hints/copy-linked-list-with-random-pointer.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n) time and O(n) space, where n is the length of the given list. +

+
+ +
+
+ Hint 1 +

+ There is an extra random pointer for each node, and unlike the next pointer, which points to the next node, the random pointer can point to any random node in the list. A deep copy is meant to create completely separate nodes occupying different memory. Why can't we build a new list while iterating through the original list? +

+
+ +
+
+ Hint 2 +

+ Because, while iterating through the list, when we encounter a node and create a copy of it, we can't immediately assign the random pointer's address. This is because the random pointer might point to a node that has not yet been created. To solve this, we can first create copies of all the nodes in one iteration. However, we still can't directly assign the random pointers since we don't have the addresses of the copies of those random pointers. Can you think of a data structure to store this information? Maybe a hash data structure could help. +

+
+ +
+
+ Hint 3 +

+ We can use a hash data structure, such as a hash map, which takes O(1) time to retrieve data. This can help by mapping the original nodes to their corresponding copies. This way, we can easily retrieve the copy of any node and assign the random pointers in a subsequent pass after creating copies of all nodes in the first pass. +

+
\ No newline at end of file diff --git a/hints/count-connected-components.md b/hints/count-connected-components.md new file mode 100644 index 000000000..4998f22ad --- /dev/null +++ b/hints/count-connected-components.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(V + E) time and O(V + E) space, where V is the number vertices and E is the number of edges in the graph. +

+
+ +
+
+ Hint 1 +

+ Assume there are no edges initially, so we have n components, as there are that many nodes given. Now, we should add the given edges between the nodes. Can you think of an algorithm to add the edges between the nodes? Also, after adding an edge, how do you determine the number of components left? +

+
+ +
+
+ Hint 2 +

+ We can use the Union-Find (DSU) algorithm to add the given edges. For simplicity, we use Union-Find by size, where we merge the smaller component into the larger one. The Union-Find algorithm inserts the edge only between two nodes from different components. It does not add the edge if the nodes are from the same component. How do you find the number of components after adding the edges? For example, consider that nodes 0 and 1 are not connected, so there are initially two components. After adding an edge between these nodes, they become part of the same component, leaving us with one component. +

+
+ +
+
+ Hint 3 +

+ We create an object of the DSU and initialize the result variable res = n, which indicates that there are n components initially. We then iterate through the given edges. For each edge, we attempt to connect the nodes using the union function of the DSU. If the union is successful, we decrement res; otherwise, we continue. Finally, we return res as the number of components. +

+
\ No newline at end of file diff --git a/hints/count-good-nodes-in-binary-tree.md b/hints/count-good-nodes-in-binary-tree.md new file mode 100644 index 000000000..39e8783d5 --- /dev/null +++ b/hints/count-good-nodes-in-binary-tree.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time andO(n) space, where n is the number of nodes in the given tree. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would involve considering every node and checking if the path from the root to that node is valid, resulting in an O(n^2) time complexity. Can you think of a better approach? +

+
+ +
+
+ Hint 2 +

+ We can use the Depth First Search (DFS) algorithm to traverse the tree. But can you think of a way to determine if the current node is a good node in a single traversal? Maybe we need to track a value while traversing the tree. +

+
+ +
+
+ Hint 3 +

+ While traversing the tree, we should track the maximum value along the current path. This allows us to determine whether the nodes we encounter are good. We can use a global variable to count the number of good nodes. +

+
\ No newline at end of file diff --git a/hints/count-number-of-islands.md b/hints/count-number-of-islands.md new file mode 100644 index 000000000..a8cb92e6f --- /dev/null +++ b/hints/count-number-of-islands.md @@ -0,0 +1,23 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m * n) time and O(m * n) space, where m is the number of rows and n is the number of columns in the grid. +

+
+ +
+
+ Hint 1 +

+ An island is a group of 1's in which every 1 is reachable from any other 1 in that group. Can you think of an algorithm that can find the number of groups by visiting a group only once? Maybe there is a recursive way of doing it. +

+
+ +
+
+ Hint 2 +

+ We can use the Depth First Search (DFS) algorithm to traverse each group independently. We iterate through each cell of the grid. When we encounter a 1, we perform a DFS starting at that cell and recursively visit every other 1 that is reachable. During this process, we mark the visited 1's as 0 to ensure we don't revisit them, as they belong to the same group. The number of groups corresponds to the number of islands. +

+
\ No newline at end of file diff --git a/hints/count-paths.md b/hints/count-paths.md new file mode 100644 index 000000000..299793547 --- /dev/null +++ b/hints/count-paths.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(m * n) time and O(m * n) space, where m is the number of rows and n is the number of columns in the grid. +

+
+ +
+
+ Hint 1 +

+ Try to think in terms of recursion and visualize it as a decision tree, where we have two choices at each step. Can you determine the base condition and recurrence relation? +

+
+ +
+
+ Hint 2 +

+ We recursively traverse the grid using row i and column j. At each step, we explore both possibilities: moving down or moving right, ensuring we do not go out of bounds. If we reach the bottom-right cell, we return 1. +

+
+ +
+
+ Hint 3 +

+ This approach has exponential complexity. Can you think of a way to optimize the recursion? Maybe you should consider using a dynamic programming approach. +

+
+ +
+
+ Hint 4 +

+ We can use memoization to cache the results of recursive calls and avoid recalculations. A hash map or a 2D array can be used to store these results. +

+
\ No newline at end of file diff --git a/hints/count-squares.md b/hints/count-squares.md new file mode 100644 index 000000000..69b289bdb --- /dev/null +++ b/hints/count-squares.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(1) time for each add() call, O(n) time for each count() call, and O(n) space, where n is the total number of points. +

+
+ +
+
+ Hint 1 +

+ Initially, we can store the points in a global list for the add() call. For the count() call, a brute force approach would use three nested loops to check other points except for the query point, resulting in an O(n^3) time solution. Can you think of a better way? Maybe you should consider the observation that can be drawn from the diagonal of a square. +

+
+ +
+
+ Hint 2 +

+ In a square's diagonal, the absolute difference between the x-coordinates is equal to the absolute difference between the y-coordinates of the two endpoints, and neither difference can be zero. Using these two points, we can determine the other diagonal endpoints. +

+
+ +
+
+ Hint 3 +

+ We store points in a hash map instead of a list for O(1) lookups, treating duplicate points as one while tracking their frequencies. For the count() function, we iterate through points that, along with the query point, can form a diagonal. Let the query point be (qx, qy) and the other point be (x, y), ensuring they form a diagonal. What could be the other two points? Maybe you should consider the points forming a right-to-left diagonal, treating (qx, qy) as the top-right corner. +

+
+ +
+
+ Hint 4 +

+ The other two points are point1 (x, qy) and point2 (qx, y). For counting, we simply add count of point1 * count of point2 to the result res. +

+
\ No newline at end of file diff --git a/hints/count-subsequences.md b/hints/count-subsequences.md new file mode 100644 index 000000000..53a37dcee --- /dev/null +++ b/hints/count-subsequences.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(m * n) time and O(m * n) space, where m is the length of the string s and n is the length of the string t. +

+
+ +
+
+ Hint 1 +

+ Try to think in terms of recursion and visualize it as a decision tree, as we need to explore all subsequences of s. Can you determine the possible decisions at each recursion step? +

+
+ +
+
+ Hint 2 +

+ We recursively iterate through the strings using indices i and j for s and t, respectively. At each recursion step, we can either skip the current character of s or include it in the current path if it matches the current character of t. Can you determine the base conditions for this recursive function? +

+
+ +
+
+ Hint 3 +

+ If index j goes out of bounds, we return 1 as a valid subsequence is found. If index i goes out of bounds first, we return 0. At each recursion step, we return the sum of both paths. This approach is exponential. Can you think of a way to optimize it? +

+
+ +
+
+ Hint 4 +

+ We can use memoization to cache the results of recursive calls and avoid redundant computations. A hash map or a 2D array can be used to store these results. +

+
\ No newline at end of file diff --git a/hints/counting-bits.md b/hints/counting-bits.md new file mode 100644 index 000000000..45188a165 --- /dev/null +++ b/hints/counting-bits.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(n) space, where n is the given integer. +

+
+ +
+
+ Hint 1 +

+ A straightforward approach would be to iterate from 0 to n using i, and for each i, iterate through its bits to count the number of set bits. This would result in an O(n \log n) approach. Can you think of a better way? Maybe you should try identifying a pattern by observing the bitwise representation of consecutive numbers. +

+
+ +
+
+ Hint 2 +

+ For example, to compute set bits for 7, add 1 to the count of set bits in 3, which was previously computed by adding 1 to the count of set bits in 1. Observing the pattern, for numbers less than 4, add 1 to the count from two positions before. For numbers less than 8, add 1 to the count from four positions before. Can you derive a dynamic programming relation from this? +

+
+ +
+
+ Hint 3 +

+ We find an offset for the current number based on the number before offset positions. The dynamic programming relation is dp[i] = 1 + dp[i - offset], where dp[i] represents the number of set bits in i. The offset starts at 1 and updates when encountering a power of 2. To simplify the power of 2 check, if offset * 2 equals the current number, we update offset to the current number. +

+
\ No newline at end of file diff --git a/hints/course-schedule-ii.md b/hints/course-schedule-ii.md new file mode 100644 index 000000000..e187980f9 --- /dev/null +++ b/hints/course-schedule-ii.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(V + E) time and O(V + E) space, where V is the number of courses (nodes) and E is the number of prerequisites (edges). +

+
+ +
+
+ Hint 1 +

+ Consider the problem as a graph where courses represent the nodes, and prerequisite[i] = [a, b] represents a directed edge from a to b. We need to determine whether the graph contains a cycle. Why? Because if there is a cycle, it is impossible to complete the courses involved in the cycle. Can you think of an algorithm to detect a cycle in a graph and also find the valid ordering if a cycle doesn't exist? +

+
+ +
+
+ Hint 2 +

+ We can use DFS to detect a cycle in a graph. However, we also need to find the valid ordering of the courses, which can also be achieved using DFS. Alternatively, we can use the Topological Sort algorithm to find the valid ordering in this directed graph, where the graph must be acyclic to complete all the courses, and the prerequisite of a course acts as the parent node of that course. How would you implement this? +

+
+ +
+
+ Hint 3 +

+ We compute the indegrees of all the nodes. Then, we perform a BFS starting from the nodes that have no parents (indegree[node] == 0). At each level, we traverse these nodes, decrement the indegree of their child nodes, and append those child nodes to the queue if their indegree becomes 0. We only append nodes whose indegree is 0 or becomes 0 during the BFS to our result array. If the length of the result array is not equal to the number of courses, we return an empty array. +

+
\ No newline at end of file diff --git a/hints/course-schedule.md b/hints/course-schedule.md new file mode 100644 index 000000000..1392ac134 --- /dev/null +++ b/hints/course-schedule.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(V + E) time and O(V + E) space, where V is the number of courses (nodes) and E is the number of prerequisites (edges). +

+
+ +
+
+ Hint 1 +

+ Consider the problem as a graph where courses represent the nodes, and prerequisite[i] = [a, b] represents a directed edge from a to b. We need to determine whether the graph contains a cycle. Why? Because if there is a cycle, it is impossible to complete the courses involved in the cycle. Can you think of an algorithm to detect cycle in a graph? +

+
+ +
+
+ Hint 2 +

+ We can use the Depth First Search (DFS) algorithm to detect a cycle in a graph. We iterate over each course, run a DFS from that course, and first try to finish its prerequisite courses by recursively traversing through them. To detect a cycle, we initialize a hash set called path, which contains the nodes visited in the current DFS call. If we encounter a course that is already in the path, we can conclude that a cycle is detected. How would you implement it? +

+
+ +
+
+ Hint 3 +

+ We run a DFS starting from each course by initializing a hash set, path, to track the nodes in the current DFS call. At each step of the DFS, we return false if the current node is already in the path, indicating a cycle. We recursively traverse the neighbors of the current node, and if any of the neighbor DFS calls detect a cycle, we immediately return false. Additionally, we clear the neighbors list of a node when no cycle is found from that node to avoid revisiting those paths again. +

+
\ No newline at end of file diff --git a/hints/daily-temperatures.md b/hints/daily-temperatures.md new file mode 100644 index 000000000..64c1a171e --- /dev/null +++ b/hints/daily-temperatures.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would involve iterating through the array with index i and checking how far is the next greater element to the right of i. This would be an O(n^2) solution. Can you think of a better way? +

+
+ +
+
+ Hint 2 +

+ Can you consider a reverse approach? For example, in [2, 1, 1, 3], the next greater element for the numbers [2, 1, 1] is 3. Instead of checking for each element individually, can you think of a way where, by standing at the element 3, you compute the result for the elements [2, 1, 1]? Maybe there's a data structure that is useful here. +

+
+ +
+
+ Hint 3 +

+ We can use a stack to maintain indices in a monotonically decreasing order, popping indices where the values are smaller than the current element. This helps us find the result by using the difference between indices while considering the values at those indices. Can you see how the stack is useful? +

+
+ +
+
+ Hint 4 +

+ In the array [2, 1, 1, 3], we don't perform any pop operations while processing [2, 1, 1] because these elements are already in decreasing order. However, when we reach 3, we pop elements from the stack until the top element of the stack is no longer less than the current element. For each popped element, we compute the difference between the indices and store it in the position corresponding to the popped element. +

+
\ No newline at end of file diff --git a/hints/decode-ways.md b/hints/decode-ways.md new file mode 100644 index 000000000..56c40d46a --- /dev/null +++ b/hints/decode-ways.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n) time and O(n) space, where n is the length of the given string. +

+
+ +
+
+ Hint 1 +

+ The characters A through Z are mapped to the numbers from 1 to 26. A mapped number can have at most 2 digits. In the given string of digits, we can explore all possible decodings by combining one or two consecutive digits. Think of this in terms of a decision tree and explore all paths. Can you derive a recurrence relation for this? +

+
+ +
+
+ Hint 2 +

+ Iterate over the string with index i. At each index, we have two choices: decode the current digit as a character with its mapped value, or combine the current digit with the next digit to form a two-digit value. The recurrence relation can be expressed as dfs(i + 1) + dfs(i + 2) where dfs is the recursive function. Also, consider edge cases, as not every two-digit number or a number with a leading zero is valid. How would you implement this? +

+
+ +
+
+ Hint 3 +

+ A brute-force recursive solution would result in O(2^n) time complexity. Can you think of a better way? Perhaps you should consider the repeated work of calling the recursion multiple times with the same parameter values and find a way to avoid this. Also, can you think about the base condition of this recursive function? +

+
+ +
+
+ Hint 4 +

+ The base condition is to return 1 if i goes out of bounds. If the current digit is '0', return 0, as no character maps to '0', making the string invalid. Use memoization to avoid repeated work by caching recursion results in an array or hash map and returning the stored value when the result is already calculated. +

+
\ No newline at end of file diff --git a/hints/depth-of-binary-tree.md b/hints/depth-of-binary-tree.md new file mode 100644 index 000000000..51538e4c8 --- /dev/null +++ b/hints/depth-of-binary-tree.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(n) space, where n is the number of nodes in the tree. +

+
+ +
+
+ Hint 1 +

+ From the definition of binary tree's maximum depth, Can you think of a way to achieve this recursively? Maybe you should consider the max depth of the subtrees first before computing the maxdepth at the root. +

+
+ +
+
+ Hint 2 +

+ We use the Depth First Search (DFS) algorithm to find the maximum depth of a binary tree, starting from the root. For the subtrees rooted at the left and right children of the root node, we calculate their maximum depths recursively going through left and right subtrees. We return 1 + max(leftDepth, rightDepth). Why? +

+
+ +
+
+ Hint 3 +

+ The +1 accounts for the current node, as it contributes to the current depth in the recursion call. We pass the maximum depth from the current node's left and right subtrees to its parent because the current maximum depth determines the longest path from the parent to a leaf node through this subtree. +

+
\ No newline at end of file diff --git a/hints/design-twitter-feed.md b/hints/design-twitter-feed.md new file mode 100644 index 000000000..8fc7ab4f8 --- /dev/null +++ b/hints/design-twitter-feed.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(nlogn) time for each getNewsFeed() function call, O(1) time for the remaining methods, and O((N * m) + (N * M) + n) space, where n is the number of followeeIds associated with the userId, m is the maximum number of tweets by any user, N is the total number of userIds, and M is the maximum number of followees for any user. +

+
+ +
+
+ Hint 1 +

+ Can you think of a data structure to store all the information, such as userIds and corresponding followeeIds, or userIds and their tweets? Maybe you should think of a hash data structure in terms of key-value pairs. Also, can you think of a way to determine that a tweet was posted before another tweet? +

+
+ +
+
+ Hint 2 +

+ We use a hash map followMap to store userIds and their unique followeeIds as a hash set. Another hash map, tweetMap, stores userIds and their tweets as a list of (count, tweetId) pairs. A counter count, incremented with each tweet, tracks the order of tweets. The variable count is helpful in distinguishing the time of tweets from two users. This way of storing data makes the functions follow(), unfollow(), and postTweet() run in O(1). Can you think of a way to implement getNewsFeed()? Maybe consider a brute force approach and try to optimize it. +

+
+ +
+
+ Hint 3 +

+ A naive solution would involve taking the tweets of the userId and its followeeIds into a list, sorting them in descending order based on their count values, and returning the top 10 tweets as the most recent ones. Can you think of a more efficient approach that avoids collecting all tweets and sorting? Perhaps consider a data structure and leverage the fact that each user's individual tweets list is already sorted. +

+
+ +
+
+ Hint 4 +

+ We can use a Max-Heap to efficiently retrieve the top 10 most recent tweets. For each followee and the userId, we insert their most recent tweet from the tweetMap into the heap, along with the tweet's count and its index in the tweet list. This index is necessary because after processing a tweet, we can insert the next most recent tweet from the same user's list. By always pushing and popping tweets from the heap, we ensure that the 10 most recent tweets are retrieved without sorting all tweets. +

+
\ No newline at end of file diff --git a/hints/design-word-search-data-structure.md b/hints/design-word-search-data-structure.md new file mode 100644 index 000000000..db99718c2 --- /dev/null +++ b/hints/design-word-search-data-structure.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time for each function call and O(t + n) space, where n is the length of the string and t is the total number of nodes created in the Trie. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to store each added word in a list and search linearly through the list for a word every time. This would be an O(m * n) solution, where m is the size of the list and n is the length of the string. Can you think of a better way? Maybe there is a tree-like data structure. +

+
+ +
+
+ Hint 2 +

+ We can use a Trie to implement adding and searching for words efficiently. Adding a word follows the standard Trie insertion process. However, when searching for a word containing '.', which can match any character, we need a different approach. Instead of directly matching, we consider all possible characters at the position of '.' and recursively check the rest of the word for each possibility. How would you implement it? +

+
+ +
+
+ Hint 3 +

+ We traverse the word with index i, starting at the root of the Trie. For normal characters, we search as usual. When encountering a dot ('.'), we try all possible characters by recursively extending the search in each direction. If any path leads to a valid word, we return true; otherwise, we return false. Although we try all paths for a dot, the time complexity is still O(n) because there are at most two dots ('.') in the word, making the complexity O((26^2) * n). +

+
\ No newline at end of file diff --git a/hints/duplicate-integer.md b/hints/duplicate-integer.md new file mode 100644 index 000000000..7639cfb85 --- /dev/null +++ b/hints/duplicate-integer.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to check every element against every other element in the array. This would be an O(n^2) solution. Can you think of a better way? +

+
+ +
+
+ Hint 2 +

+ Is there a way to check if an element is a duplicate without comparing it to every other element? Maybe there's a data structure that is useful here. +

+
+ +
+
+ Hint 3 +

+ We can use a hash data structure like a hash set or hash map to store elements we've already seen. This will allow us to check if an element is a duplicate in constant time. +

+
diff --git a/hints/eating-bananas.md b/hints/eating-bananas.md new file mode 100644 index 000000000..41bb76a40 --- /dev/null +++ b/hints/eating-bananas.md @@ -0,0 +1,47 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(nlogm) time and O(1) space, where n is the size of the input array, and m is the maximum value in the array. +

+
+ +
+
+ Hint 1 +

+ Given h is always greater than or equal to the length of piles, can you determine the upper bound for the answer? How much time does it take Koko to eat a pile with x bananas? +

+
+ +
+
+ Hint 2 +

+ It takes ceil(x / k) time to finish the x pile when Koko eats at a rate of k bananas per hour. Our task is to determine the minimum possible value of k. However, we must also ensure that at this rate, k, Koko can finish eating all the piles within the given h hours. Can you now think of the upper bound for k? +

+
+ +
+
+ Hint 3 +

+ The upper bound for k is the maximum size of all the piles. Why? Because if Koko eats the largest pile in one hour, then it is straightforward that she can eat any other pile in an hour only. +

+
+ +
+
+ Hint 4 +

+ Consider m to be the largest pile and n to be the number of piles. A brute force solution would be to linearly check all values from 1 to m and find the minimum possible value at which Koko can complete the task. This approach would take O(n * m) time. Can you think of a more efficient method? Perhaps an efficient searching algorithm could help. +

+
+ +
+
+ Hint 5 +

+ Rather than linearly scanning, we can use binary search. The upper bound of k is max(piles) and since we are only dealing with positive values, the lower bound is 1. The search space of our binary search is 1 through max(piles). This allows us to find the smallest possible k using binary search. +

+
\ No newline at end of file diff --git a/hints/edit-distance.md b/hints/edit-distance.md new file mode 100644 index 000000000..277fa3cbe --- /dev/null +++ b/hints/edit-distance.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(m * n) time and O(m * n) space, where m and n are the lengths of the strings word1 and word2, respectively. +

+
+ +
+
+ Hint 1 +

+ Try to think in terms of recursion and visualize it as a decision tree, as we have choices at each recursion step. Can you determine the recurrence relation and base cases? +

+
+ +
+
+ Hint 2 +

+ We recursively iterate through the strings using indices i and j for word1 and word2, respectively. If the characters at the current indices match, we increment both indices without counting an operation. Otherwise, we have three choices: insert the character at the current index of word1 (increment j), delete the current character of word1 (increment i), or replace the character at index i in word1 (increment both i and j). +

+
+ +
+
+ Hint 3 +

+ If index i goes out of bounds, we return the number of remaining characters in word2 (using insert operations). If index j goes out of bounds, we return the number of remaining characters in word1 (using delete operations). At each step, we return the minimum operation path. This approach is exponential. Can you think of a way to optimize it? +

+
+ +
+
+ Hint 4 +

+ We can use memoization to cache the results and avoid redundant calculations. A hash map or a 2D array can be used to store these results. +

+
\ No newline at end of file diff --git a/hints/evaluate-reverse-polish-notation.md b/hints/evaluate-reverse-polish-notation.md new file mode 100644 index 000000000..8e63e0dfd --- /dev/null +++ b/hints/evaluate-reverse-polish-notation.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would involve repeatedly finding an operator + - * / in the array and modifying the array by computing the result for that operator and two operands to its left. This would be an O(n^2) solution. Can you think of a better way? Maybe we can use a data structure to handle operations efficiently. +

+
+ +
+
+ Hint 2 +

+ We can use a stack. We iterate through the array, and if we encounter a number, we push it onto the stack. If we encounter an operator, we pop two elements from the stack, treat them as operands, and solve the equation using the current operator. Then, we push the result back onto the stack. Why does this work? +

+
+ +
+
+ Hint 3 +

+ As the array has postfix expression, stack helps us to maintain the correct order of operations by ensuring that we always use the most recent operands (those closest to the operator) when performing the operation. After the iteration, the final result is left in the stack. +

+
\ No newline at end of file diff --git a/hints/find-duplicate-integer.md b/hints/find-duplicate-integer.md new file mode 100644 index 000000000..e399acf89 --- /dev/null +++ b/hints/find-duplicate-integer.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A naive approach would be to use a hash set, which takes O(1) time to detect duplicates. Although this solution is acceptable, it requires O(n) extra space. Can you think of a better solution that avoids using extra space? Consider that the elements in the given array nums are within the range 1 to len(nums). +

+
+ +
+
+ Hint 2 +

+ We can use the given input array itself as a hash set without creating a new one. This can be achieved by marking the positions (0-indexed) corresponding to the elements that have already been encountered. Can you implement this? +

+
+ +
+
+ Hint 3 +

+ We iterate through the array using index i. For each element, we use its absolute value to find the corresponding index and mark that position as negative: nums[abs(nums[i]) - 1] *= -1. Taking absolute value ensures we work with the original value even if it’s already negated. How can you detect duplicates? +

+
+ +
+
+ Hint 4 +

+ For example, in the array [2, 1, 2, 3], where 2 is repeated, we mark the index corresponding to each element as negative. If we encounter a number whose corresponding position is already negative, it means the number is a duplicate, and we return it. +

+
\ No newline at end of file diff --git a/hints/find-median-in-a-data-stream.md b/hints/find-median-in-a-data-stream.md new file mode 100644 index 000000000..ea07e7519 --- /dev/null +++ b/hints/find-median-in-a-data-stream.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(logn) time for addNum(), O(1) time for findMedian(), and O(n) space, where n is the current number of elements. +

+
+ +
+
+ Hint 1 +

+ A naive solution would be to store the data stream in an array and sort it each time to find the median, resulting in O(nlogn) time for each findMedian() call. Can you think of a better way? Perhaps using a data structure that allows efficient insertion and retrieval of the median can make the solution more efficient. +

+
+ +
+
+ Hint 2 +

+ If we divide the array into two parts, we can find the median in O(1) if the left half can efficiently return the maximum and the right half can efficiently return the minimum. These values determine the median. However, the process changes slightly if the total number of elements is odd — in that case, the median is the element from the half with the larger size. Can you think of a data structure which is suitable to implement this? +

+
+ +
+
+ Hint 3 +

+ We can use a Heap (Max-Heap for the left half and Min-Heap for the right half). Instead of dividing the array, we store the elements in these heaps as they arrive in the data stream. But how can you maintain equal halves of elements in these two heaps? How do you implement this? +

+
+ +
+
+ Hint 4 +

+ We initialize a Max-Heap and a Min-Heap. When adding an element, if the element is greater than the minimum element of the Min-Heap, we push it into the Min-Heap; otherwise, we push it into the Max-Heap. If the size difference between the two heaps becomes greater than one, we rebalance them by popping an element from the larger heap and pushing it into the smaller heap. This process ensures that the elements are evenly distributed between the two heaps, allowing us to retrieve the middle element or elements in O(1) time. +

+
\ No newline at end of file diff --git a/hints/find-minimum-in-rotated-sorted-array.md b/hints/find-minimum-in-rotated-sorted-array.md new file mode 100644 index 000000000..568e4ef1f --- /dev/null +++ b/hints/find-minimum-in-rotated-sorted-array.md @@ -0,0 +1,40 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(logn) time and O(1) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to do a linear search on the array to find the minimum element. This would be an O(n) solution. Can you think of a better way? Maybe an efficient searching algorithm is helpful. +

+
+ +
+
+ Hint 2 +

+ Given that the array is rotated after sorting, elements from the right end are moved to the left end one by one. This creates two parts of a sorted array, separated by a deflection point caused by the rotation. For example, consider the array [3, 4, 1, 2]. Here, the array is rotated twice, resulting in two sorted segments: [3, 4] and [1, 2]. And the minimum element will be the first element of the right segment. Can you do a binary search to find this cut? +

+
+ +
+
+ Hint 3 +

+ We perform a binary search on the array with pointers l and r, which belong to two different sorted segments. For example, in [3, 4, 5, 6, 1, 2, 3], l = 0, r = 6, and mid = 3. At least two of l, mid, and r will always be in the same sorted segment. Can you find conditions to eliminate one half and continue the binary search? Perhaps analyzing all possible conditions for l, mid, and r would help. +

+
+ +
+
+ Hint 4 +

+ There will be two conditions where l and mid will be in left sorted segment or mid and r will be in right sorted segement. + If l and mid in sorted segement, then nums[l] < nums[mid] and the minimum element will be in the right part. If mid and r in sorted segment, then nums[m] < nums[r] and the minimum element will be in the left part. After the binary search we end up finding the minimum element. +

+
\ No newline at end of file diff --git a/hints/find-target-in-rotated-sorted-array.md b/hints/find-target-in-rotated-sorted-array.md new file mode 100644 index 000000000..7133b3821 --- /dev/null +++ b/hints/find-target-in-rotated-sorted-array.md @@ -0,0 +1,40 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(logn) time and O(1) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to do a linear search on the array to find the target element. This would be an O(n) solution. Can you think of a better way? Maybe an efficient searching algorithm is helpful. +

+
+ +
+
+ Hint 2 +

+ Given that the array is rotated after sorting, elements from the right end are moved to the left end one by one, creating two sorted segments separated by a deflection point due to the rotation. For example, consider the array [3, 4, 1, 2], which is rotated twice, resulting in two sorted segments: [3, 4] and [1, 2]. In a fully sorted array, it's easy to find the target. So, if you can identify the deflection point (cut), you can perform a binary search on both segments to find the target element. Can you use binary search to find this cut? +

+
+ +
+
+ Hint 3 +

+ We perform a binary search on the array with pointers l and r, which belong to two different sorted segments. For example, in [3, 4, 5, 6, 1, 2, 3], l = 0, r = 6, and mid = 3. At least two of l, mid, and r will always be in the same sorted segment. Can you find conditions to eliminate one half and continue the binary search? Perhaps analyzing all possible conditions for l, mid, and r may help. +

+
+ +
+
+ Hint 4 +

+ There are two cases: l and mid belong to the left sorted segment, or mid and r belong to the right sorted segment. + If l and mid are in the same segment, nums[l] < nums[mid], so the pivot index must lie in the right part. If mid and r are in the same segment, nums[mid] < nums[r], so the pivot index must lie in the left part. After the binary search, we eventually find the pivot index. Once the pivot is found, it's straightforward to select the segment where the target lies and perform a binary search on that segement to find its position. If we don't find the target, we return -1. +

+
\ No newline at end of file diff --git a/hints/foreign-dictionary.md b/hints/foreign-dictionary.md new file mode 100644 index 000000000..9837818a0 --- /dev/null +++ b/hints/foreign-dictionary.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(N + V + E) time and O(V + E) space, where N is the sum of the lengths of all the strings, V is the number of unique characters (vertices), and E is the number of edges. +

+
+ +
+
+ Hint 1 +

+ Can you think of this as a graph problem? Characters from a through z are nodes. What could the edges represent here? How can you create edges from the given words? Perhaps you should try comparing two adjacent words. +

+
+ +
+
+ Hint 2 +

+ The relative ordering of the characters can be treated as edges. For example, consider the words ordered as ["ape", "apple"]. "ape" comes before "apple", which indicates that 'e' is a predecessor of 'p'. Therefore, there is a directed edge e -> p, and this dependency should be valid across all the words. In this way, we can build an adjacency list by comparing adjacent words. Can you think of an algorithm that is suitable to find a valid ordering? +

+
+ +
+
+ Hint 3 +

+ We can use Topological Sort to ensure every node appears after its predecessor. Using DFS, we traverse the graph built from the adjacency list. A visited map tracks nodes in the current DFS path: False means not in the path, and True means in the path. If any DFS call returns True, it indicates a cycle and we return immediately. How do we extract the ordering from this DFS? +

+
+ +
+
+ Hint 4 +

+ When we visit a node and its children and don't find a cycle, we mark the node as False in the map and append it to the result, treating this as a post-order traversal. If we find a cycle, we return an empty string; otherwise, we return the result list. +

+
\ No newline at end of file diff --git a/hints/gas-station.md b/hints/gas-station.md new file mode 100644 index 000000000..52464072d --- /dev/null +++ b/hints/gas-station.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute force approach would be to start at each gas station, simulate the process, and return the index where completing the circuit is possible. This would be an O(n^2) time solution. Can you think of a better way? Maybe a greedy approach works. +

+
+ +
+
+ Hint 2 +

+ We can immediately return -1 if sum(gas) < sum(cost), as completing the circuit is impossible due to insufficient gas. Otherwise, a solution always exists because the total gas is sufficient to cover the total cost, meaning there must be a valid starting point that allows completing the circuit. +

+
+ +
+
+ Hint 3 +

+ We start with a variable total to track the gas balance and initialize the result index to 0. As we iterate through the array with index i, we accumulate the difference (gas[i] - cost[i]). If total becomes negative at any index, we reset it to 0 and update the result index to (i + 1). +

+
\ No newline at end of file diff --git a/hints/generate-parentheses.md b/hints/generate-parentheses.md new file mode 100644 index 000000000..454a10e81 --- /dev/null +++ b/hints/generate-parentheses.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(4^n / sqrt(n)) time and O(n) space, where n is the number of parenthesis pairs in the string. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to generate all possible strings of size 2n and add only the valid strings. This would be an O(n * 2 ^ (2n)) solution. Can you think of a better way? Maybe you can use pruning to avoid generating invalid strings. +

+
+ +
+
+ Hint 2 +

+ We can use backtracking with pruning. But what makes a string invalid? Can you think of a condition for this? +

+
+ +
+
+ Hint 3 +

+ When the count of closing brackets exceeds the count of opening brackets, the string becomes invalid. Therefore, we can maintain two variables, open and close, to track the number of opening and closing brackets. We avoid exploring paths where close > open. Once the string length reaches 2n, we add it to the result. +

+
\ No newline at end of file diff --git a/hints/hand-of-straights.md b/hints/hand-of-straights.md new file mode 100644 index 000000000..1a35e0287 --- /dev/null +++ b/hints/hand-of-straights.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(nlogn) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ It is observed that to form a group, the minimum value should be the starting value of the group. Additionally, the minimum value in the array serves as the starting value for one or more groups based on its frequency. Can you think of an efficient way to determine the frequencies of array elements? Maybe a specific data structure can be useful here. +

+
+ +
+
+ Hint 2 +

+ We can use a hash map to store the elements along with their frequencies. Additionally, we sort the given array. Then, we iterate through the sorted array and try to form groups by decrementing the frequency count. If we fail to form a group at any step, we immediately return false. Can you think why this works? +

+
+ +
+
+ Hint 3 +

+ Sorting ensures we start with the smallest available value, while the hash map helps track element availability using frequency counts. At each step, we pick the smallest available value x and attempt to form a group from x to x + groupSize - 1. If all elements are present based on their frequency counts, we decrement their counts as we iterate. If we successfully form all groups, we return true; otherwise, we return false. +

+
\ No newline at end of file diff --git a/hints/house-robber-ii.md b/hints/house-robber-ii.md new file mode 100644 index 000000000..1cc52b34d --- /dev/null +++ b/hints/house-robber-ii.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n) time and O(n) space, where n is the number of houses. +

+
+ +
+
+ Hint 1 +

+ First, consider solving the problem to get the maximum money after robbing without the condition that 'the first and last houses are adjacent'. Can you express this using a recurrence relation? Perhaps you could draw a decision tree, as at each step, you can either rob the current house and skip the next one or skip the current house and move to the next. +

+
+ +
+
+ Hint 2 +

+ The recurrence relation can be expressed as max(nums[i] + dfs(i + 2), dfs(i + 1)), where i is the current house and dfs is the recursive function. The base condition for this recursion would be to return 0 when i goes out of bounds. This solution results in O(2^n) time complexity because, at each recursive step, we branch into two paths. Can you think of a way to avoid recalculating the result for the same recursive call multiple times? +

+
+ +
+
+ Hint 3 +

+ We can use memoization to store the result of a recursive function in a hash map or an array and immediately return this value when the function is called again with the same parameter values. How would you implement this? How would you solve the problem if the first and last houses are adjacent to each other? Perhaps you should consider skipping any one house between the two. +

+
+ +
+
+ Hint 4 +

+ We can create two arrays from the given array. The first will include houses from the first house to the second-to-last house, and the second will include houses from the second house to the last house. We can run the recursive function on both arrays independently and return the maximum result between the two. Advanced techniques such as bottom-up dynamic programming can further optimize the solution. +

+
\ No newline at end of file diff --git a/hints/house-robber.md b/hints/house-robber.md new file mode 100644 index 000000000..2a1e5d7c8 --- /dev/null +++ b/hints/house-robber.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n) time and O(n) space, where n is the number of houses. +

+
+ +
+
+ Hint 1 +

+ Can you think of this problem in terms of recursion? Consider drawing a decision tree where, at each step, we can choose to rob the house or skip it. If we rob the current house, we cannot rob the next or the previous house. Can you derive a recurrence relation to solve the problem? +

+
+ +
+
+ Hint 2 +

+ We can recursively start from the first house and branch paths accordingly. If we rob the current house, we skip the next house; otherwise, we move to the next house. The recurrence relation can be expressed as max(nums[i] + dfs(i + 2), dfs(i + 1)), where i is the current house and dfs is the recursive function. Can you determine the base condition to stop the recursion? +

+
+ +
+
+ Hint 3 +

+ The base condition would be to return 0 when i goes out of bounds. This recursion can leads to O(2^n) time solution. Can you think of a better way? Maybe you should try to avoid recalculating the result for a recursive call. +

+
+ +
+
+ Hint 4 +

+ We can use Memoization to avoid recalculating the result multiple times for a recursive call. By storing the result of each recursive call in a hash map or an array using i as the parameter, we can immediately return the stored result if the recursion is called with the same i value again. Further optimization can be achieved using advanced techniques like Bottom-Up dynamic programming. +

+
\ No newline at end of file diff --git a/hints/implement-prefix-tree.md b/hints/implement-prefix-tree.md new file mode 100644 index 000000000..f2908be7f --- /dev/null +++ b/hints/implement-prefix-tree.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time for each function call and O(t) space, where n is the length of the given string and t is the total number of nodes created in the Trie. +

+
+ +
+
+ Hint 1 +

+ A Trie is structured as a tree-like data structure where each node contains a hash map (or an array for fixed character sets) to store references to its child nodes, which represent characters. Each node also includes a boolean flag to indicate whether the current node marks the end of a valid word. The Trie starts with a root node that does not hold any character and serves as the entry point for all operations. The child nodes of the root and subsequent nodes represent unique characters from the words stored in the Trie, forming a hierarchical structure based on the prefixes of the words. +

+
+ +
+
+ Hint 2 +

+ To insert a word, we iterate through the characters of the word with index i, starting at the root of the Trie as the current node. If the current node already contains word[i], we continue to the next character and move to the node that word[i] points to. If word[i] is not present, we create a new node for word[i] and continue the process until we reach the end of the word. We mark the boolean variable as true as it is the end of the inserted word. +

+
+ +
+
+ Hint 3 +

+ Searching for a word is similar to inserting, but instead of creating new nodes, we return false if we don't find a character in the path while iterating or if the end-of-word marker is not set to true when we reach the end of the word. +

+
\ No newline at end of file diff --git a/hints/insert-new-interval.md b/hints/insert-new-interval.md new file mode 100644 index 000000000..a807b4c61 --- /dev/null +++ b/hints/insert-new-interval.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) extra space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ The given intervals are non-overlapping and sorted in ascending order based on the start value. Try to visualize them as line segments and consider how a new interval can be inserted. Maybe you should analyze different cases of inserting a new interval. +

+
+ +
+
+ Hint 2 +

+ First, we append all intervals to the output list that have an end value smaller than the start value of the new interval. Then, we encounter one of three cases: we have appended all intervals, we reach an interval whose start value is greater than the new interval’s end, or we find an overlapping interval. Can you think of a way to handle these cases efficiently? +

+
+ +
+
+ Hint 3 +

+ We iterate through the remaining intervals, updating the new interval if its end value is greater than or equal to the current interval's start value. We adjust the start and end of the new interval to the minimum and maximum values, respectively. After this, any remaining intervals are appended to the output list, and we return the result. +

+
\ No newline at end of file diff --git a/hints/interleaving-string.md b/hints/interleaving-string.md new file mode 100644 index 000000000..16a8c100a --- /dev/null +++ b/hints/interleaving-string.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(m * n) time and O(m * n) space, where m is the length of the string s1 and n is the length of the string s2. +

+
+ +
+
+ Hint 1 +

+ If the sum of the characters in s1 and s2 does not equal s3, we return false. Think in terms of recursion and visualize it as a decision tree, where we explore different combinations of portions from both strings. Can you determine the possible decisions at each recursion step? +

+
+ +
+
+ Hint 2 +

+ We recursively iterate through the strings using indices i, j, and k for s1, s2, and s3, respectively. At each step, we extend the current path in two directions based on whether the k-th character of s3 matches the current character of s1 or s2. If any path returns true, we immediately return true. If k goes out of bounds, it means we have successfully formed the interleaved string, so we return true. +

+
+ +
+
+ Hint 3 +

+ This approach is exponential. Can you think of a way to optimize it? Since k depends on i and j, it can be treated as a constant, as we can derive k using i + j. +

+
+ +
+
+ Hint 4 +

+ We can use memoization to cache the results of recursive calls and avoid redundant computations. Treating i and j as states, we can use a hash map or a 2D array to store the results. +

+
\ No newline at end of file diff --git a/hints/invert-a-binary-tree.md b/hints/invert-a-binary-tree.md new file mode 100644 index 000000000..eacb6582d --- /dev/null +++ b/hints/invert-a-binary-tree.md @@ -0,0 +1,23 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(n) space, where n is the number of nodes in the tree. +

+
+ +
+
+ Hint 1 +

+ From the diagram, you can see that the left and right children of every node in the tree are swapped. Can you think of a way to achieve this recursively? Maybe an algorithm that is helpful to traverse the tree. +

+
+ +
+
+ Hint 2 +

+ We can use the Depth First Search (DFS) algorithm. At each node, we swap its left and right children by swapping their pointers. This inverts the current node, but every node in the tree also needs to be inverted. To achieve this, we recursively visit the left and right children and perform the same operation. If the current node is null, we simply return. +

+
\ No newline at end of file diff --git a/hints/is-anagram.md b/hints/is-anagram.md new file mode 100644 index 000000000..8454011d2 --- /dev/null +++ b/hints/is-anagram.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n + m) time and O(1) space, where n is the length of the string s and m is the length of the string t. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to sort the given strings and check for their equality. This would be an O(nlogn + mlogm) solution. Though this solution is acceptable, can you think of a better way without sorting the given strings? +

+
+ +
+
+ Hint 2 +

+ By the definition of the anagram, we can rearrange the characters. Does the order of characters matter in both the strings? Then what matters? +

+
+ +
+
+ Hint 3 +

+ We can just consider maintaining the frequency of each character. We can do this by having two separate hash tables for the two strings. Then, we can check whether the frequency of each character in string s is equal to that in string t and vice versa. +

+
\ No newline at end of file diff --git a/hints/is-palindrome.md b/hints/is-palindrome.md new file mode 100644 index 000000000..697108060 --- /dev/null +++ b/hints/is-palindrome.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the length of the input string. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to create a copy of the string, reverse it, and then check for equality. This would be an O(n) solution with extra space. Can you think of a way to do this without O(n) space? +

+
+ +
+
+ Hint 2 +

+ Can you find the logic by observing the definition of pallindrome or from the brute force solution? +

+
+ +
+
+ Hint 3 +

+ A palindrome string is a string that is read the same from the start as well as from the end. This means the character at the start should match the character at the end at the same index. We can use the two pointer algorithm to do this efficiently. +

+
\ No newline at end of file diff --git a/hints/islands-and-treasure.md b/hints/islands-and-treasure.md new file mode 100644 index 000000000..b7ef502de --- /dev/null +++ b/hints/islands-and-treasure.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m * n) time and O(m * n) space, where m is the number of rows and n is the number of columns in the given grid. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to iterate on each land cell and run a BFS from that cells to find the nearest treasure chest. This would be an O((m * n)^2) solution. Can you think of a better way? Sometimes it is not optimal to go from source to destination. +

+
+ +
+
+ Hint 2 +

+ We can see that instead of going from every cell to find the nearest treasure chest, we can do it in reverse. We can just do a BFS from all the treasure chests in grid and just explore all possible paths from those chests. Why? Because in this approach, the treasure chests self mark the cells level by level and the level number will be the distance from that cell to a treasure chest. We don't revisit a cell. This approach is called Multi-Source BFS. How would you implement it? +

+
+ +
+
+ Hint 3 +

+ We insert all the cells (row, col) that represent the treasure chests into the queue. Then, we process the cells level by level, handling all the current cells in the queue at once. For each cell, we mark it as visited and store the current level value as the distance at that cell. We then try to add the neighboring cells (adjacent cells) to the queue, but only if they have not been visited and are land cells. +

+
\ No newline at end of file diff --git a/hints/jump-game-ii.md b/hints/jump-game-ii.md new file mode 100644 index 000000000..4c7c41e30 --- /dev/null +++ b/hints/jump-game-ii.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute force approach would be to recursively explore all paths from index 0 to its reachable indices, then process those indices similarly and return the minimum steps to reach the last index. This would be an exponential approach. Can you think of a better way? Maybe a greedy approach works. +

+
+ +
+
+ Hint 2 +

+ We maintain two pointers, l and r, initially set to 0, representing the range of reachable indices. At each step, we iterate through the indices in the range l to r and determine the farthest index that can be reached from the current range. +

+
+ +
+
+ Hint 3 +

+ We then update the pointers l and r to the next range, setting l to r + 1 and r to the farthest index reachable from the current range. We continue this process until the pointers go out of bounds. +

+
+ +
+
+ Hint 4 +

+ The number of steps taken represents the minimum steps required to reach the last index, as it is guaranteed that we can reach it. +

+
\ No newline at end of file diff --git a/hints/jump-game.md b/hints/jump-game.md new file mode 100644 index 000000000..91e7092c9 --- /dev/null +++ b/hints/jump-game.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the size of input array. +

+
+ +
+
+ Hint 1 +

+ A brute force approach would be to recursively explore all paths from index 0 to its reachable indices, then process those indices similarly, returning true if we reach the last index. This would be an exponential approach. Can you think of a better way? Maybe a greedy approach works. +

+
+ +
+
+ Hint 2 +

+ Instead of processing the array from index 0, start from the last index. Let the target index be goal = n - 1. Iterate in reverse from index n - 2. +

+
+ +
+
+ Hint 3 +

+ At each iteration, we check whether the current index can reach goal. If it can, we update goal to the current index, as reaching the current index means we can also reach the goal. +

+
+ +
+
+ Hint 4 +

+ To determine if we can reach the last index, the goal should be 0 after the iteration. Otherwise, reaching the last index is not possible. +

+
\ No newline at end of file diff --git a/hints/k-closest-points-to-origin.md b/hints/k-closest-points-to-origin.md new file mode 100644 index 000000000..277e19160 --- /dev/null +++ b/hints/k-closest-points-to-origin.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(nlogk) time and O(k) space, where n is the size of the input array, and k is the number of points to be returned. +

+
+ +
+
+ Hint 1 +

+ A naive solution would be to sort the array in ascending order based on the distances of the points from the origin (0, 0) and return the first k points. This would take O(nlogn) time. Can you think of a better way? Perhaps you could use a data structure that maintains only k points and allows efficient insertion and removal. +

+
+ +
+
+ Hint 2 +

+ We can use a Max-Heap that keeps the maximum element at its top and allows retrieval in O(1) time. This data structure is ideal because we need to return the k closest points to the origin. By maintaining only k points in the heap, we can efficiently remove the farthest point when the size exceeds k. How would you implement this? +

+
+ +
+
+ Hint 3 +

+ We initialize a Max-Heap that orders points based on their distances from the origin. Starting with an empty heap, we iterate through the array of points, inserting each point into the heap. If the size of the heap exceeds k, we remove the farthest point (the maximum element in the heap). After completing the iteration, the heap will contain the k closest points to the origin. Finally, we convert the heap into an array and return it. +

+
\ No newline at end of file diff --git a/hints/kth-largest-element-in-an-array.md b/hints/kth-largest-element-in-an-array.md new file mode 100644 index 000000000..8d68feba3 --- /dev/null +++ b/hints/kth-largest-element-in-an-array.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(nlogk) time and O(k) space, where n is the size of the input array, and k represents the rank of the largest number to be returned (i.e., the k-th largest element). +

+
+ +
+
+ Hint 1 +

+ A naive solution would be to sort the array in descending order and return the k-th largest element. This would be an O(nlogn) solution. Can you think of a better way? Maybe you should think of a data structure which can maintain only the top k largest elements. +

+
+ +
+
+ Hint 2 +

+ We can use a Min-Heap, which stores elements and keeps the smallest element at its top. When we add an element to the Min-Heap, it takes O(logk) time since we are storing k elements in it. Retrieving the top element (the smallest in the heap) takes O(1) time. How can this be useful for finding the k-th largest element? +

+
+ +
+
+ Hint 3 +

+ The k-th largest element is the smallest element among the top k largest elements. This means we only need to maintain k elements in our Min-Heap to efficiently determine the k-th largest element. Whenever the size of the Min-Heap exceeds k, we remove the smallest element by popping from the heap. How do you implement this? +

+
+ +
+
+ Hint 4 +

+ We initialize an empty Min-Heap. We iterate through the array and add elements to the heap. When the size of the heap exceeds k, we pop from the heap and continue. After the iteration, the top element of the heap is the k-th largest element. +

+
\ No newline at end of file diff --git a/hints/kth-largest-integer-in-a-stream.md b/hints/kth-largest-integer-in-a-stream.md new file mode 100644 index 000000000..2d7d952e8 --- /dev/null +++ b/hints/kth-largest-integer-in-a-stream.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(mlogk) time and O(k) space, where m is the number of times add() is called, and k represents the rank of the largest number to be tracked (i.e., the k-th largest element). +

+
+ +
+
+ Hint 1 +

+ A brute force solution would involve sorting the array in every time a number is added using add(), and then returning the k-th largest element. This would take O(m * nlogn) time, where m is the number of calls to add() and n is the total number of elements added. However, do we really need to track all the elements added, given that we only need the k-th largest element? Maybe you should think of a data structure which can maintain only the top k largest elements. +

+
+ +
+
+ Hint 2 +

+ We can use a Min-Heap, which stores elements and keeps the smallest element at its top. When we add an element to the Min-Heap, it takes O(logk) time since we are storing k elements in it. Retrieving the top element (the smallest in the heap) takes O(1) time. How can this be useful for finding the k-th largest element? +

+
+ +
+
+ Hint 3 +

+ The k-th largest element is the smallest element among the top k largest elements. This means we only need to maintain k elements in our Min-Heap to efficiently determine the k-th largest element. Whenever the size of the Min-Heap exceeds k, we remove the smallest element by popping from the heap. How do you implement this? +

+
+ +
+
+ Hint 4 +

+ We initialize a Min-Heap with the elements of the input array. When the add() function is called, we insert the new element into the heap. If the heap size exceeds k, we remove the smallest element (the root of the heap). Finally, the top element of the heap represents the k-th largest element and is returned. +

+
\ No newline at end of file diff --git a/hints/kth-smallest-integer-in-bst.md b/hints/kth-smallest-integer-in-bst.md new file mode 100644 index 000000000..2af2e63de --- /dev/null +++ b/hints/kth-smallest-integer-in-bst.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n) time and O(n) space, where n is the number of nodes in the given tree. +

+
+ +
+
+ Hint 1 +

+ A naive solution would be to store the node values in an array, sort it, and then return the k-th value from the sorted array. This would be an O(nlogn) solution due to sorting. Can you think of a better way? Maybe you should try one of the traversal technique. +

+
+ +
+
+ Hint 2 +

+ We can use the Depth First Search (DFS) algorithm to traverse the tree. Since the tree is a Binary Search Tree (BST), we can leverage its structure and perform an in-order traversal, where we first visit the left subtree, then the current node, and finally the right subtree. Why? Because we need the k-th smallest integer, and by visiting the left subtree first, we ensure that we encounter smaller nodes before the current node. How can you implement this? +

+
+ +
+
+ Hint 3 +

+ We keep a counter variable cnt to track the position of the current node in the ascending order of values. When cnt == k, we store the current node's value in a global variable and return. This allows us to identify and return the k-th smallest element during the in-order traversal. +

+
\ No newline at end of file diff --git a/hints/largest-rectangle-in-histogram.md b/hints/largest-rectangle-in-histogram.md new file mode 100644 index 000000000..35976b385 --- /dev/null +++ b/hints/largest-rectangle-in-histogram.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A rectangle has a height and a width. Can you visualize how rectangles are formed in the given input? Considering one bar at a time might help. We can try to form rectangles by going through every bar and current bar's height will be the height of the rectangle. How can you determine the width of the rectangle for the current bar being the height of the rectangle? Extending the current bar to the left and right might help determine the rectangle's width. +

+
+ +
+
+ Hint 2 +

+ For a bar with height h, try extending it to the left and right. We can see that we can't extend further when we encounter a bar with a smaller height than h. The width will be the number of bars within this extended range. A brute force solution would be to go through every bar and find the area of the rectangle it can form by extending towards the left and right. This would be an O(n^2) solution. Can you think of a better way? Maybe precomputing the left and right boundaries might be helpful. +

+
+ +
+
+ Hint 3 +

+ The left and right boundaries are the positions up to which we can extend the bar at index i. The area of the rectangle will be height[i] * (right - left + 1), which is the general formula for height * width. These boundaries are determined by the first smaller bars encountered to the left and right of the current bar. How can we find the left and right boundaries now? Maybe a data structure is helpful. +

+
+ +
+
+ Hint 4 +

+ We can use a stack with a monotonically strictly increasing nature, but instead of storing values, we store indices in the stack and perform operations based on the values at those indices. The top of the stack will represent the smaller bar that we encounter while extending the current bar. To find the left and right boundaries, we perform this algorithm from left to right and vice versa, storing the boundaries. Then, we iterate through the array to find the area for each bar and return the maximum area we get. +

+
\ No newline at end of file diff --git a/hints/last-stone-weight.md b/hints/last-stone-weight.md new file mode 100644 index 000000000..5404da55d --- /dev/null +++ b/hints/last-stone-weight.md @@ -0,0 +1,23 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(nlogn) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A naive solution would involve simulating the process by sorting the array at each step and processing the top 2 heaviest stones, resulting in an O(n * nlogn) time complexity. Can you think of a better way? Consider using a data structure that efficiently supports insertion and removal of elements and maintain the sorted order. +

+
+ +
+
+ Hint 2 +

+ We can use a Max-Heap, which allows us to retrieve the maximum element in O(1) time. We initially insert all the weights into the Max-Heap, which takes O(logn) time per insertion. We then simulate the process until only one or no element remains in the Max-Heap. At each step, we pop two elements from the Max-Heap which takes O(logn) time. If they are equal, we do not insert anything back into the heap and continue. Otherwise, we insert the difference of the two elements back into the heap. +

+
\ No newline at end of file diff --git a/hints/level-order-traversal-of-binary-tree.md b/hints/level-order-traversal-of-binary-tree.md new file mode 100644 index 000000000..e59c454b5 --- /dev/null +++ b/hints/level-order-traversal-of-binary-tree.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(n) space, where n is the number of nodes in the given tree. +

+
+ +
+
+ Hint 1 +

+ The level of a tree refers to the nodes that are at equal distance from the root node. Can you think of an algorithm that traverses the tree level by level, rather than going deeper into the tree? +

+
+ +
+
+ Hint 2 +

+ We can use the Breadth First Search (BFS) algorithm to traverse the tree level by level. BFS uses a queue data structure to achieve this. At each step of BFS, we only iterate over the queue up to its size at that step. Then, we take the left and right child pointers and add them to the queue. This allows us to explore all paths simultaneously. +

+
+ +
+
+ Hint 3 +

+ The number of times we iterate the queue corresponds to the number of levels in the tree. At each step, we pop all nodes from the queue for the current level and add them collectively to the resultant array. This ensures that we capture all nodes at each level of the tree. +

+
\ No newline at end of file diff --git a/hints/linked-list-cycle-detection.md b/hints/linked-list-cycle-detection.md new file mode 100644 index 000000000..04c8f4623 --- /dev/null +++ b/hints/linked-list-cycle-detection.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the length of the given list. +

+
+ +
+
+ Hint 1 +

+ A naive approach would be to use a hash set, which takes O(1) time to detect duplicates. Although this solution is acceptable, it requires O(n) extra space. Can you think of a better solution that avoids using extra space? Maybe there is an efficient algorithm which uses two pointers. +

+
+ +
+
+ Hint 2 +

+ We can use the fast and slow pointers technique, which is primarily used to detect cycles in a linked list. We iterate through the list using two pointers. The slow pointer moves one step at a time, while the fast pointer moves two steps at a time. If the list has a cycle, these two pointers will eventually meet. Why does this work? +

+
+ +
+
+ Hint 3 +

+ When there is no cycle in the list, the loop ends when the fast pointer becomes null. If a cycle exists, the fast pointer moves faster and continuously loops through the cycle. With each step, it reduces the gap between itself and the slow pointer by one node. For example, if the gap is 10, the slow pointer moves by 1, increasing the gap to 11, while the fast pointer moves by 2, reducing the gap to 9. This process continues until the fast pointer catches up to the slow pointer, confirming a cycle. +

+
\ No newline at end of file diff --git a/hints/longest-common-subsequence.md b/hints/longest-common-subsequence.md new file mode 100644 index 000000000..d25df25e5 --- /dev/null +++ b/hints/longest-common-subsequence.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(m * n) time and O(m * n) space, where m is the length of the string text1 and n is the length of the string text2. +

+
+ +
+
+ Hint 1 +

+ Try to think in terms of recursion and visualize it as a decision tree. Can you determine the possible decisions at each step? Maybe you should consider iterating through both strings recursively at the same time. +

+
+ +
+
+ Hint 2 +

+ At each recursion step, we have two choices: if the characters at the current indices of both strings match, we move both indices forward and extend the subsequence. Otherwise, we explore two paths by advancing one index at a time and recursively finding the longest subsequence. We return the maximum length between these two choices. This approach is exponential. Can you think of a way to optimize it? +

+
+ +
+
+ Hint 3 +

+ We return 0 if either index goes out of bounds. To optimize, we can use memoization to cache recursive call results and avoid redundant calculations. A hash map or a 2D array can be used to store these results. +

+
\ No newline at end of file diff --git a/hints/longest-consecutive-sequence.md b/hints/longest-consecutive-sequence.md new file mode 100644 index 000000000..7065b8463 --- /dev/null +++ b/hints/longest-consecutive-sequence.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to consider every element from the array as the start of the sequence and count the length of the sequence formed with that starting element. This would be an O(n^2) solution. Can you think of a better way? +

+
+ +
+
+ Hint 2 +

+ Is there any way to identify the start of a sequence? For example, in [1, 2, 3, 10, 11, 12], only 1 and 10 are the beginning of a sequence. Instead of trying to form a sequence for every number, we should only consider numbers like 1 and 10. +

+
+ +
+
+ Hint 3 +

+ We can consider a number num as the start of a sequence if and only if num - 1 does not exist in the given array. We iterate through the array and only start building the sequence if it is the start of a sequence. This avoids repeated work. We can use a hash set for O(1) lookups by converting the array to a hash set. +

+
\ No newline at end of file diff --git a/hints/longest-increasing-path-in-matrix.md b/hints/longest-increasing-path-in-matrix.md new file mode 100644 index 000000000..8008fe73f --- /dev/null +++ b/hints/longest-increasing-path-in-matrix.md @@ -0,0 +1,23 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m * n) time and O(m * n) space, where m is the number of rows and n is the number of columns in the given matrix. +

+
+ +
+
+ Hint 1 +

+ If we move from one cell to an adjacent cell with a greater value, we won't revisit a cell, as the path is strictly increasing. A brute force approach would be to run a dfs from every cell and return the longest path found. This approach is exponential. Can you think of a way to optimize it to avoid redundant calculations? +

+
+ +
+
+ Hint 2 +

+ We can use memoization to cache the results of recursive calls and avoid redundant computations. A hash map or a 2D array can be used to store these results. +

+
\ No newline at end of file diff --git a/hints/longest-increasing-subsequence.md b/hints/longest-increasing-subsequence.md new file mode 100644 index 000000000..36a4e3e78 --- /dev/null +++ b/hints/longest-increasing-subsequence.md @@ -0,0 +1,47 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n ^ 2) time and O(n ^ 2) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A subsequence is formed by selecting elements while maintaining their order. Using recursion, we can generate all subsequences. The recursive function returns the length of the longest increasing subsequence up to index i, processing from left to right. At each step, we decide whether to include or exclude the current element. +

+
+ +
+
+ Hint 2 +

+ Since the sequence must be increasing, we represent choices by adding 1 when including an element and 0 when excluding it. In recursion, how can we ensure the current element is greater than the previous one? Perhaps additional information is needed to process it. +

+
+ +
+
+ Hint 3 +

+ We can store the index of the previously chosen element as j, making it easier to process the current element at index i. If and only if j == -1 or nums[i] > nums[j], we include the current element and extend the recursive path. Can you determine the recurrence relation? At most, two recursive calls are made at each recursion step. +

+
+ +
+
+ Hint 4 +

+ We stop the recursion when index i goes out of bounds and return 0 since no more elements can be added. The initial recursion call starts with j = -1. At each step, we include the current element if it is greater than the previous one and continue the recursion, or we exclude it and explore the next possibility. We return the maximum value obtained from both paths. +

+
+ +
+
+ Hint 5 +

+ The time complexity of this approach is exponential. We can use memoization to store results of recursive calls and avoid recalculations. A hash map or a 2D array can be used to cache these results. +

+
\ No newline at end of file diff --git a/hints/longest-palindromic-substring.md b/hints/longest-palindromic-substring.md new file mode 100644 index 000000000..9d4dc9eb9 --- /dev/null +++ b/hints/longest-palindromic-substring.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n^2) time and O(1) space, where n is the length of the given string. +

+
+ +
+
+ Hint 1 +

+ A brute-force solution would be to check if every substring is a palindrome and return the maximum length among all the palindromic substring lengths. This would be an O(n^3) time solution. Can you think of a better way? Perhaps you should consider thinking in terms of the center of a palindrome. +

+
+ +
+
+ Hint 2 +

+ Iterate over the string with index i and treat the current character as the center. For this character, try to extend outward to the left and right simultaneously, but only if both characters are equal. Update the result variable accordingly. How would you implement this? Can you consider both cases: even-length and odd-length palindromes? +

+
+ +
+
+ Hint 3 +

+ Maintain two variables, resLen and res, which denote the length of the longest palindrome and the start index of that palindrome, respectively. At each index, you can create an odd-length palindrome starting at that index extending outward from both its left and right indices, i.e., i - 1 and i + 1. How can you find the even-length palindrome for this index? +

+
+ +
+
+ Hint 4 +

+ For an even-length palindrome, consider expanding from indices i and i + 1. This two-pointer approach, extending from the center of the palindrome, will help find all palindromic substrings in the given string. Update the two result variables and return the substring starting at res with a length of resLen. +

+
\ No newline at end of file diff --git a/hints/longest-repeating-substring-with-replacement.md b/hints/longest-repeating-substring-with-replacement.md new file mode 100644 index 000000000..808ff742f --- /dev/null +++ b/hints/longest-repeating-substring-with-replacement.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(m) space, where n is the length of the given string and m is the number of unique characters in the string. +

+
+ +
+
+ Hint 1 +

+ Which characters would you replace in a string to make all its characters unique? Can you think with respect to the frequency of the characters? +

+
+ +
+
+ Hint 2 +

+ It is always optimal to replace characters with the most frequent character in the string. Why? Because using the most frequent character minimizes the number of replacements required to make all characters in the string identical. How can you find the number of replacements now? +

+
+ +
+
+ Hint 3 +

+ The number of replacements is equal to the difference between the length of the string and the frequency of the most frequent character in the string. A brute force solution would be to consider all substrings, use a hash map for frequency counting, and return the maximum length of the substring that has at most k replacements. This would be an O(n^2) solution. Can you think of a better way? +

+
+ +
+
+ Hint 4 +

+ We can use the sliding window approach. The window size will be dynamic, and we will shrink the window when the number of replacements exceeds k. The result will be the maximum window size observed at each iteration. +

+
\ No newline at end of file diff --git a/hints/longest-substring-without-duplicates.md b/hints/longest-substring-without-duplicates.md new file mode 100644 index 000000000..42c7ca28a --- /dev/null +++ b/hints/longest-substring-without-duplicates.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(m) space, where n is the length of the string and m is the number of unique characters in the string. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to try the substring starting at index i and try to find the maximum length we can form without duplicates by starting at that index. we can use a hash set to detect duplicates in O(1) time. Can you think of a better way? +

+
+ +
+
+ Hint 2 +

+ We can use the sliding window algorithm. Since we only care about substrings without duplicate characters, the sliding window can help us maintain valid substring with its dynamic nature. +

+
+ +
+
+ Hint 3 +

+ We can iterate through the given string with index r as the right boundary and l as the left boundary of the window. We use a hash set to check if the character is present in the window or not. When we encounter a character at index r that is already present in the window, we shrink the window by incrementing the l pointer until the window no longer contains any duplicates. Also, we remove characters from the hash set that are excluded from the window as the l pointer moves. At each iteration, we update the result with the length of the current window, r - l + 1, if this length is greater than the current result. +

+
\ No newline at end of file diff --git a/hints/lowest-common-ancestor-in-binary-search-tree.md b/hints/lowest-common-ancestor-in-binary-search-tree.md new file mode 100644 index 000000000..b7357e2b3 --- /dev/null +++ b/hints/lowest-common-ancestor-in-binary-search-tree.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(h) time and O(h) space, where h is the height of the given tree. +

+
+ +
+
+ Hint 1 +

+ A Binary Search Tree (BST) is a tree in which the values of all nodes in the left subtree of a node are less than the node's value, and the values of all nodes in the right subtree are greater than the node's value. Additionally, every subtree of a BST must also satisfy this property, meaning the "less than" or "greater than" condition is valid for all nodes in the tree, not just the root. How can you use this idea to find the LCA of the given nodes in the tree? +

+
+ +
+
+ Hint 2 +

+ We can use recursion to traverse the tree. Can you figure out the conditions we encounter when choosing a path between the left and right subtrees during traversal using the values of the two given nodes? Perhaps you can determine the LCA by traversing based on these conditions. +

+
+ +
+
+ Hint 3 +

+ If nodes p and q are in different subtrees, a split occurs, making the current node the LCA. If both are in the left or right subtree, the LCA lies in that subtree and we further choose that subtree to traverse using recursion. You should also handle other multiple scenarios to get the LCA. +

+
+ +
+
+ Hint 4 +

+ The LCA can also be one of the nodes, p or q, if the current node is equal to either of them. This is because if we encounter either p or q during the traversal, that node is the LCA. +

+
\ No newline at end of file diff --git a/hints/lru-cache.md b/hints/lru-cache.md new file mode 100644 index 000000000..7db2c01ef --- /dev/null +++ b/hints/lru-cache.md @@ -0,0 +1,47 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(1) time for each put() and get() function call and an overall space of O(n), where n is the capacity of the LRU cache. +

+
+ +
+
+ Hint 1 +

+ Can you think of a data structure for storing data in key-value pairs? Maybe a hash-based data structure with unique keys. +

+
+ +
+
+ Hint 2 +

+ We can use a hash map which takes O(1) time to get and put the values. But, how can you deal with the least recently used to be removed criteria as a key is updated by the put() or recently used by the get() functions? Can you think of a data structure to store the order of values? +

+
+ +
+
+ Hint 3 +

+ A brute-force solution would involve maintaining the order of key-value pairs in an array list, performing operations by iterating through the list to erase and insert these key-value pairs. However, this would result in an O(n) time complexity. Can you think of a data structure that allows removing and reinserting elements in O(1) time? +

+
+ +
+
+ Hint 4 +

+ We can use a doubly-linked list, which allows us to remove a node from the list when we have the address of that node. Can you think of a way to store these addresses so that we can efficiently remove or update a key when needed? +

+
+ +
+
+ Hint 5 +

+ We can use a doubly linked list where key-value pairs are stored as nodes, with the least recently used (LRU) node at the head and the most recently used (MRU) node at the tail. Whenever a key is accessed using get() or put(), we remove the corresponding node and reinsert it at the tail. When the cache reaches its capacity, we remove the LRU node from the head of the list. Additionally, we use a hash map to store each key and the corresponding address of its node, enabling efficient operations in O(1) time. +

+
\ No newline at end of file diff --git a/hints/max-area-of-island.md b/hints/max-area-of-island.md new file mode 100644 index 000000000..a8502e52a --- /dev/null +++ b/hints/max-area-of-island.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m * n) time and O(m * n) space, where m is the number of rows and n is the number of columns in the grid. +

+
+ +
+
+ Hint 1 +

+ An island is a group of 1's in which every 1 is reachable from any other 1 in that group. Can you think of an algorithm that can find the number of groups by visiting a group only once? Maybe there is a recursive way of doing it. +

+
+ +
+
+ Hint 2 +

+ We can use the Depth First Search (DFS) algorithm to traverse each group by starting at a cell with 1 and recursively visiting all the cells that are reachable from that cell and are also 1. Can you think about how to find the area of that island? How would you implement this? +

+
+ +
+
+ Hint 3 +

+ We traverse the grid, and when we encounter a 1, we initialize a variable area. We then start a DFS from that cell to visit all connected 1's recursively, marking them as 0 to indicate they are visited. At each recursion step, we increment area. After completing the DFS, we update maxArea, which tracks the maximum area of an island in the grid, if maxArea < area. Finally, after traversing the grid, we return maxArea. +

+
\ No newline at end of file diff --git a/hints/max-water-container.md b/hints/max-water-container.md new file mode 100644 index 000000000..02fb8859b --- /dev/null +++ b/hints/max-water-container.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to try all pairs of bars in the array, compute the water for each pair, and return the maximum water among all pairs. This would be an O(n^2) solution. Can you think of a better way? +

+
+ +
+
+ Hint 2 +

+ Can you think of an algorithm that runs in linear time and is commonly used in problems that deal with pairs of numbers? Find a formula to calculate the amount of water when we fix two heights. +

+
+ +
+
+ Hint 3 +

+ We can use the two pointer algorithm. One pointer is at the start and the other at the end. At each step, we calculate the amount of water using the formula (j - i) * min(heights[i], heights[j]). Then, we move the pointer that has the smaller height value. Can you think why we only move the pointer at smaller height? +

+
+ +
+
+ Hint 4 +

+ In the formula, the amount of water depends only on the minimum height. Therefore, it is appropriate to replace the smaller height value. +

+
\ No newline at end of file diff --git a/hints/maximum-product-subarray.md b/hints/maximum-product-subarray.md new file mode 100644 index 000000000..d46997d32 --- /dev/null +++ b/hints/maximum-product-subarray.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to find the product for every subarray and then return the maximum among all the products. This would be an O(n ^ 2) approach. Can you think of a better way? Maybe you should think of a dynamic programming approach. +

+
+ +
+
+ Hint 2 +

+ Try to identify a pattern by finding the maximum product for an array with two elements and determining what values are needed when increasing the array size to three. Perhaps you only need two values when introducing a new element. +

+
+ +
+
+ Hint 3 +

+ We maintain both the minimum and maximum product values and update them when introducing a new element by considering three cases: starting a new subarray, multiplying with the previous max product, or multiplying with the previous min product. The max product is updated to the maximum of these three, while the min product is updated to the minimum. We also track a global max product for the result. This approach is known as Kadane's algorithm. +

+
\ No newline at end of file diff --git a/hints/maximum-subarray.md b/hints/maximum-subarray.md new file mode 100644 index 000000000..f8071b628 --- /dev/null +++ b/hints/maximum-subarray.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute force approach would be to compute the sum of every subarray and return the maximum among them. This would be an O(n^2) approach. Can you think of a better way? Maybe you should consider a dynamic programming-based approach. +

+
+ +
+
+ Hint 2 +

+ Instead of calculating the sum for every subarray, try maintaining a running sum. Maybe you should consider whether extending the previous sum or starting fresh with the current element gives a better result. Can you think of a way to track this efficiently? +

+
+ +
+
+ Hint 3 +

+ We use a variable curSum to track the sum of the elements. At each index, we have two choices: either add the current element to curSum or start a new subarray by resetting curSum to the current element. Maybe you should track the maximum sum at each step and update the global maximum accordingly. +

+
+ +
+
+ Hint 4 +

+ This algorithm is known as Kadane's algorithm. +

+
\ No newline at end of file diff --git a/hints/median-of-two-sorted-arrays.md b/hints/median-of-two-sorted-arrays.md new file mode 100644 index 000000000..bfbe9c032 --- /dev/null +++ b/hints/median-of-two-sorted-arrays.md @@ -0,0 +1,47 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(log(min(n, m))) time and O(1) space, where n is the size of nums1 and m is the size of nums2. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to create a new array by merging elements from both arrays, then sorting it and returning the median. This would be an O(n + m) solution. Can you think of a better way? Maybe you can use the criteria of both the arrays being sorted in ascending order. +

+
+ +
+
+ Hint 2 +

+ Suppose we merged both arrays. Then, we would have half = (m + n) / 2 elements to the left of the median. So, without merging, is there any way to use this information to find the median? You can leverage the fact that the arrays are sorted. Consider the smaller array between the two and use binary search to find the correct partition between the two arrays, which will allow you to directly find the median without fully merging the arrays. How will you implement this? +

+
+ +
+
+ Hint 3 +

+ We will always try to keep array A smaller and interchange it with array B if len(A) > len(B). Now, we perform binary search on the number of elements we will choose from array A. It is straightforward that when we choose x elements from array A, we have to choose half - x elements from array B. But we should also ensure that this partition is valid. How can we do this? +

+
+ +
+
+ Hint 4 +

+ When we do a partition for both arrays, we should ensure that the maximum elements from the left partitions of both arrays are smaller than or equal to the minimum elements of the right partitions of both the arrays. This will ensure that the partition is valid, and we can then find the median. We can find the min or max of these partitions in O(1) as these partitions are sorted in ascending order. Why does this work? +

+
+ +
+
+ Hint 5 +

+ For example, consider the arrays A = [1, 2, 3, 4, 5] and B = [1, 2, 3, 4, 5, 6, 7, 8]. When we select x = 2, we take 4 elements from array B. However, this partition is not valid because value 4 from the left partition of array B is greater than the value 3 from the right partition of array A. So, we should try to take more elements from array A to make the partition valid. Binary search will eventually help us find a valid partition. +

+
\ No newline at end of file diff --git a/hints/meeting-schedule-ii.md b/hints/meeting-schedule-ii.md new file mode 100644 index 000000000..1342e260f --- /dev/null +++ b/hints/meeting-schedule-ii.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(nlogn) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ Try to visualize the meetings as line segments on a number line representing start and end times. The number of rooms required is the maximum number of overlapping meetings at any point on the number line. Can you think of a way to determine this efficiently? +

+
+ +
+
+ Hint 2 +

+ We create two arrays, start and end, containing the start and end times of all meetings, respectively. After sorting both arrays, we use a two-pointer based approach. How do you implement this? +

+
+ +
+
+ Hint 3 +

+ We use two pointers, s and e, for the start and end arrays, respectively. We also maintain a variable count to track the current number of active meetings. At each iteration, we increment s while the start time is less than the current end time and increase count, as these meetings must begin before the earliest ongoing meeting ends. +

+
+ +
+
+ Hint 4 +

+ Then, we increment e and decrement count as a meeting has ended. At each step, we update the result with the maximum value of active meetings stored in count. +

+
\ No newline at end of file diff --git a/hints/meeting-schedule.md b/hints/meeting-schedule.md new file mode 100644 index 000000000..0216b7b66 --- /dev/null +++ b/hints/meeting-schedule.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(nlogn) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ If two intervals are sorted in ascending order by their start values, they overlap if the start value of the second interval is less than the end value of the first interval. And these are called overlapping intervals. +

+
+ +
+
+ Hint 2 +

+ A brute force approach would be to check every pair of intervals and return false if any overlap is found. This would be an O(n^2) solution. Can you think of a better way? Maybe you should visualize the given intervals as line segments. +

+
+ +
+
+ Hint 3 +

+ We should sort the given intervals based on their start values, as this makes it easier to check for overlaps by comparing adjacent intervals. We then iterate through the intervals from left to right and return false if any adjacent intervals overlap. +

+
\ No newline at end of file diff --git a/hints/merge-intervals.md b/hints/merge-intervals.md new file mode 100644 index 000000000..89af36fe5 --- /dev/null +++ b/hints/merge-intervals.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(nlogn) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ Sorting the given intervals in ascending order based on their start values is beneficial, as it helps in identifying overlapping intervals efficiently. How can you determine if two intervals overlap? +

+
+ +
+
+ Hint 2 +

+ If two intervals are sorted in ascending order by their start values, they overlap if the start value of the second interval is less than or equal to the end value of the first interval. +

+
+ +
+
+ Hint 3 +

+ We iterate through the sorted intervals from left to right, starting with the first interval in the output list. From the second interval onward, we compare each interval with the last appended interval. Can you determine the possible cases for this comparison? +

+
+ +
+
+ Hint 4 +

+ The two cases are: if the current interval overlaps with the last appended interval, we update its end value to the maximum of both intervals' end values and continue. Otherwise, we append the current interval and proceed. +

+
\ No newline at end of file diff --git a/hints/merge-k-sorted-linked-lists.md b/hints/merge-k-sorted-linked-lists.md new file mode 100644 index 000000000..aec8ab73a --- /dev/null +++ b/hints/merge-k-sorted-linked-lists.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n * k) time and O(1) space, where k is the total number of lists and n is the total number of nodes across all lists. +

+
+ +
+
+ Hint 1 +

+ A brute-force solution would involve storing all n nodes in an array, sorting them, and converting the array back into a linked list, resulting in an O(nlogn) time complexity. Can you think of a better way? Perhaps consider leveraging the idea behind merging two sorted linked lists. +

+
+ +
+
+ Hint 2 +

+ We can merge two sorted linked lists without using any extra space. To handle k sorted linked lists, we can iteratively merge each linked list with a resultant merged list. How can you implement this? +

+
+ +
+
+ Hint 3 +

+ We iterate through the list array with index i, starting at i = 1. We merge the linked lists using mergeTwoLists(lists[i], lists[i - 1]), which returns the head of the merged list. This head is stored in lists[i], and the process continues. Finally, the merged list is obtained at the last index, and we return its head. +

+
\ No newline at end of file diff --git a/hints/merge-triplets-to-form-target.md b/hints/merge-triplets-to-form-target.md new file mode 100644 index 000000000..fd475aa48 --- /dev/null +++ b/hints/merge-triplets-to-form-target.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ An important observation is that we can ignore triplets with values greater than the target triplet. +

+
+ +
+
+ Hint 2 +

+ Specifically, if a triplet t has any element greater than the corresponding value in target (i.e., t[0] > target[0], t[1] > target[1], or t[2] > target[2]), we can discard it. This is because using such a triplet in operations would exceed the target values, making it invalid. +

+
+ +
+
+ Hint 3 +

+ Now, from the remaining valid triplets, we only need to check whether the target triplet values exist. Since all values in the valid triplets are less than or equal to the corresponding values in the target triplet, finding the target triplet among them guarantees that we can achieve it. +

+
\ No newline at end of file diff --git a/hints/merge-two-sorted-linked-lists.md b/hints/merge-two-sorted-linked-lists.md new file mode 100644 index 000000000..5fbfafd3c --- /dev/null +++ b/hints/merge-two-sorted-linked-lists.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n + m) time and O(1) space, where n is the length of list1 and m is the length of list2. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would involve storing the values of both linked lists in an array, sorting the array, and then converting it back into a linked list. This approach would use O(n) extra space and is trivial. Can you think of a better way? Perhaps the sorted nature of the lists can be leveraged. +

+
+ +
+
+ Hint 2 +

+ We create a dummy node to keep track of the head of the resulting linked list while iterating through the lists. Using l1 and l2 as iterators for list1 and list2, respectively, we traverse both lists node by node to build a final linked list that is also sorted. How do you implement this? +

+ +
+
+ Hint 3 +

+ For example, consider list1 = [1, 2, 3] and list2 = [2, 3, 4]. While iterating through the lists, we move the pointers by comparing the node values from both lists. We link the next pointer of the iterator to the node with the smaller value. For instance, when l1 = 1 and l2 = 2, since l1 < l2, we point the iterator's next pointer to l1 and proceed. +

+

+
\ No newline at end of file diff --git a/hints/min-cost-climbing-stairs.md b/hints/min-cost-climbing-stairs.md new file mode 100644 index 000000000..258121826 --- /dev/null +++ b/hints/min-cost-climbing-stairs.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n) time and O(n) space, where n is the number of steps on the staircase. +

+
+ +
+
+ Hint 1 +

+ Can you find the recurrence relation to solve the problem, given that at each step we have two options: going one step or two steps? Consider drawing a decision tree where we branch into two paths at each step. By exploring every path, we can get the minimum cost. However, this results in an O(2^n) time solution. Can you think of a better approach? Is there any repeated work in the decision tree that we can optimize? +

+
+ +
+
+ Hint 2 +

+ The recurrence relation can be expressed as cost[i] + min(dfs(i + 1), dfs(i + 2)), where i is the current position and dfs is the recursive function. To avoid recalculating the result of a recursive call multiple times, we can use Memoization. Initialize a cache array of size n, where n is the number of steps on the staircase. How would you implement this? +

+
+ +
+
+ Hint 3 +

+ We start the recursion from positions 0 and 1. At each recursive step, before computing the result, we check if the result for the current position has already been calculated. If it has, we return the stored value. Otherwise, we calculate the result for the current position, store it in the cache, and then return the result. What can be the base condition for this recursion to stop? +

+
+ +
+
+ Hint 4 +

+ The base condition would be to return 0 if we are at the top of the staircase i >= n. This is a one-dimensional dynamic programming problem. We can further optimize the memoization solution by using advanced techniques such as Bottom-Up dynamic programming based on the recurrance relation. +

+
\ No newline at end of file diff --git a/hints/min-cost-to-connect-points.md b/hints/min-cost-to-connect-points.md new file mode 100644 index 000000000..cc585977d --- /dev/null +++ b/hints/min-cost-to-connect-points.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O((n^2)logn) time and O(n^2) space, where n is the number of points. +

+
+ +
+
+ Hint 1 +

+ Think of this problem as a graph, where the given points represent nodes. We need to connect these nodes into a single component by creating edges. Can you think of an advanced graph algorithm that can be used to connect all points into one component? +

+
+ +
+
+ Hint 2 +

+ We use Kruskal's algorithm along with Union-Find (DSU) to connect nodes into components. The final component forms the minimum spanning tree (MST), where the edges between nodes are weighted by the Manhattan distance, and the total weight of the tree is minimized. How would you implement this? +

+
+ +
+
+ Hint 3 +

+ We create the possible edges by iterating through every pair of points and calculating the weights as the Manhattan distance between them. Next, we sort the edges in ascending order based on their weights, as we aim to minimize the cost. Then, we traverse through these edges, connecting the nodes and adding the weight of the edge to the total cost if the edge is successfully added. The final result will be the minimum cost. +

+
\ No newline at end of file diff --git a/hints/minimum-interval-including-query.md b/hints/minimum-interval-including-query.md new file mode 100644 index 000000000..9b56faba2 --- /dev/null +++ b/hints/minimum-interval-including-query.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(nlogn + mlogm) time and O(n + m) space, where m is the size of the array queries and n is the size of the array intervals. +

+
+ +
+
+ Hint 1 +

+ A brute force approach would be to iterate through each query and, for each query, check all intervals to find the result. This would be an O(m * n) solution. Can you think of a better way? Maybe processing the queries in sorted order could help. +

+
+ +
+
+ Hint 2 +

+ We sort the intervals by start value and process the queries in ascending order. Using a pointer i, we add intervals to a min-heap while their start values are less than or equal to the query, storing their end values and sizes. +

+
+ +
+
+ Hint 3 +

+ The min-heap is ordered by interval size. We remove elements from the heap while the top element’s end value is less than the current query. The result for the query is the top element’s size if the heap is non-empty; otherwise, it is -1. +

+
\ No newline at end of file diff --git a/hints/minimum-stack.md b/hints/minimum-stack.md new file mode 100644 index 000000000..411e300dd --- /dev/null +++ b/hints/minimum-stack.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(1) time for each function call and O(n) space, where n is the maximum number of elements present in the stack. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to always check for the minimum element in the stack for the getMin() function call. This would be an O(n) appraoch. Can you think of a better way? Maybe O(n) extra space to store some information. +

+
+ +
+
+ Hint 2 +

+ We can use a stack to maintain the elements. But how can we find the minimum element at any given time? Perhaps we should consider a prefix approach. +

+
+ +
+
+ Hint 3 +

+ We use an additional stack to maintain the prefix minimum element. When popping elements from the main stack, we should also pop from this extra stack. However, when pushing onto the extra stack, we should push the minimum of the top element of the extra stack and the current element onto this extra stack. +

+
\ No newline at end of file diff --git a/hints/minimum-window-with-characters.md b/hints/minimum-window-with-characters.md new file mode 100644 index 000000000..3df37fff9 --- /dev/null +++ b/hints/minimum-window-with-characters.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(m) space, where n is the length of the string s and m is the number of unique characters in s and t. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would involve checking every substring of s against t and returning the minimum length valid substring. This would be an O(n^2) solution. Can you think of a better way? Maybe you should think in terms of frequency of characters. +

+
+ +
+
+ Hint 2 +

+ We need to find substrings in s that should have atleast the characters of t. We can use hash maps to maintain the frequencies of characters. It will be O(1) for lookups. Can you think of an algorithm now? +

+
+ +
+
+ Hint 3 +

+ We can use a dynamically sized sliding window approach on s. We iterate through s while maintaining a window. If the current window contains at least the frequency of characters from t, we update the result and shrink the window until it is valid. +

+
+ +
+
+ Hint 4 +

+ We should ensure that we maintain the result substring and only update it if we find a shorter valid substring. Additionally, we need to keep track of the result substring's length so that we can return an empty string if no valid substring is found. +

+
\ No newline at end of file diff --git a/hints/missing-number.md b/hints/missing-number.md new file mode 100644 index 000000000..2d5f6908b --- /dev/null +++ b/hints/missing-number.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute force approach would iterate through the range of numbers from 0 to n, checking if each number is present in the given array. If a number is missing, it is returned. This results in an O(n^2) solution. Can you think of a better way? Maybe a data structure could help optimize this process. +

+
+ +
+
+ Hint 2 +

+ We can use a hash set by inserting the given array elements into it. Then, we iterate through the range of numbers from 0 to n and use the hash set for O(1) lookups to find the missing number. Can you think of a way to further optimize this? Maybe a bitwise operator could be useful. +

+
+ +
+
+ Hint 3 +

+ We can use bitwise XOR. When two identical numbers are XORed, the result is 0. Using this property, we can efficiently find the missing number. +

+
+ +
+
+ Hint 4 +

+ We first compute the bitwise XOR of numbers from 0 to n. Then, we iterate through the array and XOR its elements as well. The missing number remains in the final XOR result since all other numbers appear twice—once in the range and once in the array—while the missing number is XORed only once. +

+
\ No newline at end of file diff --git a/hints/multiply-strings.md b/hints/multiply-strings.md new file mode 100644 index 000000000..193cc4c27 --- /dev/null +++ b/hints/multiply-strings.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m*n) time and O(m+n) space, where m is the length of the string num1 and n is the length of the string num2. +

+
+ +
+
+ Hint 1 +

+ Implement a helper function that takes two number strings and returns their sum. Ensure that the length of num1 is greater than num2, swapping them if necessary. Can you think of a way to multiply the strings? Maybe you should first consider basic multiplication, where num1 is multiplied by each digit of num2. +

+
+ +
+
+ Hint 2 +

+ When multiplying num1 with each digit of num2, we iterate through num2 in reverse order. Based on the digit's position, we pad zeros to the multiplication result accordingly—no padding for the last digit, one zero for the second last, and so on. What should be the next step after each multiplication? Maybe you should implement a helper function to handle this. +

+
+ +
+
+ Hint 3 +

+ We implement a helper function that takes num1, a digit, and a variable zeroes, returning the multiplication result with zeroes padded at the end. A global string res stores the final result. +

+
+ +
+
+ Hint 4 +

+ In the main function, we iterate through num2 in reverse order, calling the helper function to multiply num1 with the current digit and append the appropriate number of padding zeros. We then call another helper function that takes this multiplication result and the global result string res, adds them, and updates res. +

+
\ No newline at end of file diff --git a/hints/n-queens.md b/hints/n-queens.md new file mode 100644 index 000000000..60c5f6fef --- /dev/null +++ b/hints/n-queens.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n!) time and O(n^2) space, where n is the size of the given square board. +

+
+ +
+
+ Hint 1 +

+ A queen can move in 8 directions, and no two queens can be in the same row or column. This means we can place one queen per row or column. We iterate column-wise and try to place a queen in each column while ensuring no other queen exists in the same row, left diagonal, or left bottom diagonal. Can you think of a recursive algorithm to find all possible combinations? +

+
+ +
+
+ Hint 2 +

+ We can use backtracking to traverse through the columns with index c while maintaining a board that represents the current state in the recursive path. We reach the base condition when c == n and we add a copy of the board to the result. How would you implement this? +

+
+ +
+
+ Hint 3 +

+ We initialize an empty board and recursively go through each column. For each column, we check each cell to see if we can place a queen there. We use a function to check if the cell is suitable by iterating along the left directions and verifying if the same row, left diagonal, or left bottom diagonal are free. If it is possible, we place the queen on the board, move along the recursive path, and then backtrack by removing the queen to continue to the next cell in the column. +

+
\ No newline at end of file diff --git a/hints/network-delay-time.md b/hints/network-delay-time.md new file mode 100644 index 000000000..901cefb98 --- /dev/null +++ b/hints/network-delay-time.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(ElogV) time and O(V + E) space, where E is the number of edges and V is the number of vertices (nodes). +

+
+ +
+
+ Hint 1 +

+ As we are given the source node and we need to find the minimum time to reach all nodes, this represents the shortest path from the source node to all nodes. Can you think of a standard algorithm to find the shortest path from a source to a destination? Maybe a heap-based algorithm is helpful. +

+
+ +
+
+ Hint 2 +

+ We can use Dijkstra's algorithm to find the shortest path from a source to destination. We end up finding the shortest paths from the source to the nodes that we encounter in finding the destination. So, to find shortest path for all nodes from the source, we need to perform Dijkstra's algorithm until the heap in this algorithm becomes empty. How would you implement this? +

+
+ +
+
+ Hint 3 +

+ We use a Min-Heap as we need to find the minimum time. We create an adjacency list for the given times (weighted edges). We also initialize an array dist[] of size n (number of nodes) which represents the distance from the source to all nodes, initialized with infinity. We put dist[source] = 0. Then we continue the algorithm. After the heap becomes empty, if we don't visit any node, we return -1; otherwise, we return the time. +

+
\ No newline at end of file diff --git a/hints/non-cyclical-number.md b/hints/non-cyclical-number.md new file mode 100644 index 000000000..6672425b9 --- /dev/null +++ b/hints/non-cyclical-number.md @@ -0,0 +1,23 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(logn) time and O(logn) space, where n is the given integer. +

+
+ +
+
+ Hint 1 +

+ Create a helper function that returns the sum of the squares of a number's digits. Then, simulate the given process. If we reach 1, return true. However, we may get stuck in a cycle if a number is processed more than once. What data structure can be used to detect if a number has already been processed? +

+
+ +
+
+ Hint 2 +

+ We can use a hash set to detect if a number has already been processed. At each step, we update n with the return value of the helper function. If the result is 1, we return true. If n is already in the set, we return false. Otherwise, we add n to the hash set and continue. +

+
\ No newline at end of file diff --git a/hints/non-overlapping-intervals.md b/hints/non-overlapping-intervals.md new file mode 100644 index 000000000..1e07a9c0b --- /dev/null +++ b/hints/non-overlapping-intervals.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(nlogn) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ If two intervals are sorted in ascending order by their start values, they overlap if the start value of the second interval is less than the end value of the first interval. And these are called overlapping intervals. +

+
+ +
+
+ Hint 2 +

+ A brute force approach would be to sort the given intervals in ascending order based on their start values and recursively explore all possibilities. This would be an exponential approach. Can you think of a better way? Maybe a greedy approach works here. +

+
+ +
+
+ Hint 3 +

+ We first sort the given intervals based on their start values to efficiently check for overlaps by comparing adjacent intervals. We then iterate through the sorted intervals from left to right, keeping track of the previous interval’s end value as prevEnd, initially set to the end value of the first interval. +

+
+ +
+
+ Hint 4 +

+ We then iterate from the second interval. If the current interval doesn't overlap, we update prevEnd to the current interval's end and continue. Otherwise, we set prevEnd to the minimum of prevEnd and the current interval’s end, greedily removing the interval that ends last to retain as many intervals as possible. +

+
\ No newline at end of file diff --git a/hints/number-of-one-bits.md b/hints/number-of-one-bits.md new file mode 100644 index 000000000..788499e2b --- /dev/null +++ b/hints/number-of-one-bits.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(1) time and O(1) space. +

+
+ +
+
+ Hint 1 +

+ The given integer is a 32-bit integer. Can you think of using bitwise operators to iterate through its bits? Maybe you should consider iterating 32 times. +

+
+ +
+
+ Hint 2 +

+ We iterate 32 times (0 to 31) using index i. The expression (1 << i) creates a bitmask with a set bit at the i-th position. How can you check whether the i-th bit is set in the given number? Maybe you should consider using the bitwise-AND ("&"). +

+
+ +
+
+ Hint 3 +

+ Since the mask has a set bit at the i-th position and all 0s elsewhere, we can perform a bitwise-AND with n. If n has a set bit at the i-th position, the result is positive; otherwise, it is 0. We increment the global count if the result is positive and return it after the iteration. +

+
\ No newline at end of file diff --git a/hints/pacific-atlantic-water-flow.md b/hints/pacific-atlantic-water-flow.md new file mode 100644 index 000000000..6ed584779 --- /dev/null +++ b/hints/pacific-atlantic-water-flow.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m * n) time and O(m * n) space, where m is the number of rows and n is the number of columns in the grid. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to traverse each cell in the grid and run a BFS from each cell to check if it can reach both oceans. This would result in an O((m * n)^2) solution. Can you think of a better way? Maybe you should consider a reverse way of traversing. +

+
+ +
+
+ Hint 2 +

+ We can use the Depth First Search (DFS) algorithm starting from the border cells of the grid. However, we reverse the condition such that the next visiting cell should have a height greater than or equal to the current cell. For the top and left borders connected to the Pacific Ocean, we use a hash set called pacific and run a DFS from each of these cells, visiting nodes recursively. Similarly, for the bottom and right borders connected to the Atlantic Ocean, we use a hash set called atlantic and run a DFS. The required coordinates are the cells that exist in both the pacific and atlantic sets. How do you implement this? +

+
+ +
+
+ Hint 3 +

+ We perform DFS from the border cells, using their respective hash sets. During the DFS, we recursively visit the neighbouring cells that are unvisited and have height greater than or equal to the current cell's height and add the current cell's coordinates to the corresponding hash set. Once the DFS completes, we traverse the grid and check if a cell exists in both the hash sets. If so, we add that cell to the result list. +

+
\ No newline at end of file diff --git a/hints/palindrome-partitioning.md b/hints/palindrome-partitioning.md new file mode 100644 index 000000000..a2d81783f --- /dev/null +++ b/hints/palindrome-partitioning.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n * (2^n)) time and O(n) space, where n is the length of the input string. +

+
+ +
+
+ Hint 1 +

+ For a given string there are 2^n possible partitions because at each index we have two decisions: we can either partition and start a new string, or continue without partitioning. Can you think of an algorithm to recursively traverse all combinations? +

+
+ +
+
+ Hint 2 +

+ We can use backtracking to recursively traverse the string with indices j (start of the current substring) and i (current iterating index). At each step, we either skip partitioning at the current index or, if the substring from j to i is a palindrome, make a partition, update j = i + 1, and start a new substring. The base condition to stop the recursion is when j reaches the end of the string. How do you implement this? +

+
+ +
+
+ Hint 3 +

+ We start with j = 0, i = 0 and a temporary list which stores the substrings from the partitions. Then we recursively iterate the string with the index i. At each step we apply the 2 decisions accordingly. At the base condition of the recursive path, we make a copy of the current partition list and add it to the result. +

+
\ No newline at end of file diff --git a/hints/palindromic-substrings.md b/hints/palindromic-substrings.md new file mode 100644 index 000000000..5e994313c --- /dev/null +++ b/hints/palindromic-substrings.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n^2) time and O(1) space, where n is the length of the given string. +

+
+ +
+
+ Hint 1 +

+ A brute-force solution would be to check if every substring is a palindrome and return the total number of palindromic substrings. This would be an O(n^3) time solution. Can you think of a better way? Perhaps you should consider thinking in terms of the center of a palindrome. +

+
+ +
+
+ Hint 2 +

+ Iterate over the string with index i and treat the current character as the center. For this character, try to extend outward to the left and right simultaneously, but only if both characters are equal. At each iteration, we increment the count of palindromes. How would you implement this? Can you consider both cases: even-length and odd-length palindromes? +

+
+ +
+
+ Hint 3 +

+ Initialize a variable res to track the count of palindromes. At each index, create an odd-length palindrome starting at that index extending outward from both its left and right indices, i.e., i - 1 and i + 1. How can you find the even-length palindrome for this index? +

+
+ +
+
+ Hint 4 +

+ For an even-length palindrome, consider expanding from indices i and i + 1. This two-pointer approach, extending from the center of the palindrome, will help find all palindromic substrings in the given string and return its count. +

+
\ No newline at end of file diff --git a/hints/partition-equal-subset-sum.md b/hints/partition-equal-subset-sum.md new file mode 100644 index 000000000..6dfc98881 --- /dev/null +++ b/hints/partition-equal-subset-sum.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n * t) time and O(n * t) space, where n is the size of the input array and t is half the sum of the array elements. +

+
+ +
+
+ Hint 1 +

+ If the sum of the array elements is not even, we can immediately return false. Think in terms of recursion, where we try to build a subset with a sum equal to half of the total sum. If we find such a subset, the remaining elements will automatically form another subset with the same sum. The entire array can also be considered as one subset, with the other being empty. Can you visualize this as a decision tree to process the array recursively? +

+
+ +
+
+ Hint 2 +

+ We recursively iterate through the array with index i. At each step, we decide whether to include the current element in the subset or not. Instead of forming the subset explicitly, can you think of a better approach? Maybe you only need to track the subset sum rather than generating the subset itself. +

+
+ +
+
+ Hint 3 +

+ We can track the subset sum using a variable curSum. At each step, we make two recursive calls. If adding the current element does not exceed the target, we include it. If either path leads to a solution, we immediately return true. Can you determine the base case for this recursion? All elements in the array are positive. +

+
+ +
+
+ Hint 4 +

+ If curSum equals half the sum of the array elements, we return true. If index i goes out of bounds, we return false. This solution is exponential, but we can use memoization to cache recursive call results and avoid redundant computations. We can use a hash map or a 2D array with dimensions n * t, where n is the size of the input array and t is half the sum of the input array elements. +

+
\ No newline at end of file diff --git a/hints/partition-labels.md b/hints/partition-labels.md new file mode 100644 index 000000000..c5e3551bb --- /dev/null +++ b/hints/partition-labels.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(m) space, where n is the length of the string s and m is the number of unique characters in the string s. +

+
+ +
+
+ Hint 1 +

+ A character has a first and last index in the given string. Can you think of a greedy approach to solve this problem? Maybe you should try iterating over one of these two indices. +

+
+ +
+
+ Hint 2 +

+ We store the last index of each character in a hash map or an array. As we iterate through the string, treating each index as a potential start of a partition, we track the end of the partition using the maximum last index of the characters seen so far in the current partition. Additionally, we maintain the size of the current partition using a variable, say size. +

+
+ +
+
+ Hint 3 +

+ We update the end of the current partition based on the maximum last index of the characters, extending the partition as needed. When the current index reaches the partition’s end, we finalize the partition, append its size to the output list, reset the size to 0, and continue the same process for the remaining string. +

+
\ No newline at end of file diff --git a/hints/permutation-string.md b/hints/permutation-string.md new file mode 100644 index 000000000..5abcaddf0 --- /dev/null +++ b/hints/permutation-string.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the maximum of the lengths of the two strings. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to check every substring of s2 with s1 by sorting s1 as well as the substring of s2. This would be an O(n^2) solution. Can you think of a better way? Maybe we can use the freqency of the characters of both the strings as we did in checking anagrams. +

+
+ +
+
+ Hint 2 +

+ We return false if the length of s1 is greater than the length of s2. To count the frequency of each character in a string, we can simply use an array of size O(26), since the character set consists of a through z (26 continuous characters). Which algorithm can we use now? +

+
+ +
+
+ Hint 3 +

+ We use a sliding window approach on s2 with a fixed window size equal to the length of s1. To track the current window, we maintain a running frequency count of characters in s2. This frequency count represents the characters in the current window. At each step, if the frequency count matches that of s1, we return true. +

+
\ No newline at end of file diff --git a/hints/permutations.md b/hints/permutations.md new file mode 100644 index 000000000..3f3d59477 --- /dev/null +++ b/hints/permutations.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n * n!) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A permutation is the same as the array but with the numbers arranged in a different order. The given array itself is also considered a permutation. This means we should make a decision at each step to take any element from the array that has not been chosen previously. By doing this recursively, we can generate all permutations. How do you implement it? +

+
+ +
+
+ Hint 2 +

+ We can use backtracking to explore all possible permutation paths. We initialize a temporary list to append the chosen elements and a boolean array of size n (the same size as the input array) to track which elements have been picked so far (true means the element is chosen; otherwise, false). At each step of recursion, we iterate through the entire array, picking elements that have not been chosen previously, and proceed further along that path. Can you think of the base condition to terminate the current recursive path? +

+
+ +
+
+ Hint 3 +

+ We observe that every permutation has the same size as the input array. Therefore, we can append a copy of the list of chosen elements in the current path to the result list if the size of the list equals the size of the input array terminating the current recursive path. +

+
\ No newline at end of file diff --git a/hints/plus-one.md b/hints/plus-one.md new file mode 100644 index 000000000..ec62b78c9 --- /dev/null +++ b/hints/plus-one.md @@ -0,0 +1,23 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ There are two cases when adding 1 to a number. If the rightmost digit is less than 9, we simply increment it. Otherwise, we set it to 0 and apply the same process to the preceding digit. +

+
+ +
+
+ Hint 2 +

+ We iterate through the given digits from right to left using index i. If the current digit is less than 9, we increment it and return the array. Otherwise, we set the digit to 0 and continue. If the loop completes without returning, we insert 1 at the beginning of the array and return it. +

+
\ No newline at end of file diff --git a/hints/pow-x-n.md b/hints/pow-x-n.md new file mode 100644 index 000000000..de42350f0 --- /dev/null +++ b/hints/pow-x-n.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(logn) time and O(logn) space, where n is the given integer. +

+
+ +
+
+ Hint 1 +

+ A brute force approach would be to iterate linearly up to n, multiplying by x each time to find (x^n). If n is negative, return (1 / (x^n)); otherwise, return (x^n). Can you think of a better way? Maybe a recursive approach would be more efficient. +

+
+ +
+
+ Hint 2 +

+ For example, to calculate 2^6, instead of multiplying 2 six times, we compute 2^3 and square the result. The same logic applies recursively to find 2^3 and further break down the multiplication. What should be the base case for this recursion? Maybe you should consider the term that cannot be further broken down. +

+
+ +
+
+ Hint 3 +

+ In (x^n), if x is 0, we return 0. If n is 0, we return 1, as any number raised to the power of 0 is 1. Otherwise, we compute (x^(n/2)) recursively and square the result. If n is odd, we multiply the final result by x. What should be the logic if n is negative? +

+
+ +
+
+ Hint 4 +

+ We start the recursion with the absolute value of n. After computing the result as res, we return res if n is non-negative; otherwise, we return (1 / res). +

+
\ No newline at end of file diff --git a/hints/products-of-array-discluding-self.md b/hints/products-of-array-discluding-self.md new file mode 100644 index 000000000..bab9964c9 --- /dev/null +++ b/hints/products-of-array-discluding-self.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute-force solution would be to iterate through the array with index i and compute the product of the array except for that index element. This would be an O(n^2) solution. Can you think of a better way? +

+
+ +
+
+ Hint 2 +

+ Is there a way to avoid the repeated work? Maybe we can store the results of the repeated work in an array. +

+
+ +
+
+ Hint 3 +

+ We can use the prefix and suffix technique. First, we iterate from left to right and store the prefix products for each index in a prefix array, excluding the current index's number. Then, we iterate from right to left and store the suffix products for each index in a suffix array, also excluding the current index's number. Can you figure out the solution from here? +

+
+ +
+
+ Hint 4 +

+ We can use the stored prefix and suffix products to compute the result array by iterating through the array and simply multiplying the prefix and suffix products at each index. +

+
\ No newline at end of file diff --git a/hints/reconstruct-flight-path.md b/hints/reconstruct-flight-path.md new file mode 100644 index 000000000..43f336c17 --- /dev/null +++ b/hints/reconstruct-flight-path.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(ElogE) time and O(E) space, where E is the number of tickets (edges). +

+
+ +
+
+ Hint 1 +

+ Consider this problem as a graph where airports are the nodes and tickets are the edges. Since we need to utilize all the tickets exactly once, can you think of an algorithm that visits every edge exactly once? Additionally, how do you ensure the smallest lexical order is maintained? +

+
+ +
+
+ Hint 2 +

+ We build an adjacency list from the given tickets, which represent directed edges. We perform a DFS to construct the result, but we first sort the neighbors' list of each node to ensure the smallest lexical order. Why? Sorting guarantees that during DFS, we visit the node with the smallest lexical order first. +

+
+ +
+
+ Hint 3 +

+ DFS would be a naive solution, as it takes O(E * V) time, where E is the number of tickets (edges) and V is the number of airports (nodes). In this approach, we traverse from the given source airport JFK, perform DFS by removing the neighbor, traversing it, and then reinserting it back. Can you think of a better way? Perhaps an advanced algorithm that incorporates DFS might be helpful? +

+
+ +
+
+ Hint 4 +

+ We can use Hierholzer's algorithm, a modified DFS approach. Instead of appending the node to the result list immediately, we first visit all its neighbors. This results in a post-order traversal. After completing all the DFS calls, we reverse the path to obtain the final path, which is also called Euler's path. +

+
\ No newline at end of file diff --git a/hints/redundant-connection.md b/hints/redundant-connection.md new file mode 100644 index 000000000..cdd3217fe --- /dev/null +++ b/hints/redundant-connection.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(V + E) time and O(V + E) space, where V is the number vertices and E is the number of edges in the graph. +

+
+ +
+
+ Hint 1 +

+ There will be only one edge that creates the cycle in the given problem. Why? Because the graph is initially acyclic, and a cycle is formed only after adding one extra edge that was not present in the graph initially. Can you think of an algorithm that helps determine whether the current connecting edge forms a cycle? Perhaps a component-oriented algorithm? +

+
+ +
+
+ Hint 2 +

+ We can use the Union-Find (DSU) algorithm to create the graph from the given edges. While connecting the edges, if we fail to connect any edge, it means this is the redundant edge, and we return it. How would you implement this? +

+
+ +
+
+ Hint 3 +

+ We create an instance of the DSU object and traverse through the given edges. For each edge, we attempt to connect the nodes using the union function. If the union function returns false, indicating that the current edge forms a cycle, we immediately return that edge. +

+
\ No newline at end of file diff --git a/hints/regular-expression-matching.md b/hints/regular-expression-matching.md new file mode 100644 index 000000000..e17c78964 --- /dev/null +++ b/hints/regular-expression-matching.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(m * n) time and O(m * n) space, where m is the length of the string s and n is the length of the string p. +

+
+ +
+
+ Hint 1 +

+ Try to think in terms of recursion and visualize it as a decision tree, where we explore different combinations to match the strings when encountering *. Multiple decisions are made at each step to find a valid matching path. Can you determine the possible decisions at each recursion step? +

+
+ +
+
+ Hint 2 +

+ We recursively iterate through the strings using indices i and j for s and p, respectively. If the characters match or p[j] is '.', we increment both i and j to process the remaining strings. When the next character of string p is '*', we have two choices: skip it (treating it as zero occurrences) or match one or more characters (if s[i] matches p[j]), incrementing i accordingly. +

+
+ +
+
+ Hint 3 +

+ If both indices go out of bounds, we return true; otherwise, we return false. If any recursive path returns true, we immediately return true. This approach is exponential. Can you think of a way to optimize it? +

+
+ +
+
+ Hint 4 +

+ We can use memoization to cache the results of recursive calls and avoid redundant calculations. A hash map or a 2D array can be used to store these results. +

+
\ No newline at end of file diff --git a/hints/remove-node-from-end-of-linked-list.md b/hints/remove-node-from-end-of-linked-list.md new file mode 100644 index 000000000..d73855f6f --- /dev/null +++ b/hints/remove-node-from-end-of-linked-list.md @@ -0,0 +1,47 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(N) time and O(1) space, where N is the length of the given list. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would involve storing the nodes of the list into an array, removing the nth node from the array, and then converting the array back into a linked list to return the new head. However, this requires O(N) extra space. Can you think of a better approach to avoid using extra space? Maybe you should first solve with a two pass approach. +

+
+ +
+
+ Hint 2 +

+ We can use a two-pass approach by first finding the length of the list, N. Since removing the nth node from the end is equivalent to removing the (N - n)th node from the front, as they both mean the same. How can you remove the node in a linked list? +

+
+ +
+
+ Hint 3 +

+ For example, consider a three-node list [1, 2, 3]. If we want to remove 2, we update the next pointer of 1 (initially pointing to 2) to point to the node after 2, which is 3. After this operation, the list becomes [1, 3], and we return the head. But, can we think of a more better approach? Maybe a greedy calculation can help. +

+
+ +
+
+ Hint 4 +

+ We can solve this in one pass using a greedy approach. Move the first pointer n steps ahead. Then, start another pointer second at the head and iterate both pointers simultaneously until first reaches null. At this point, the second pointer is just before the node to be removed. We then remove the node that is next to the second pointer. Why does this work? +

+
+ +
+
+ Hint 5 +

+ This greedy approach works because the second pointer is n nodes behind the first pointer. When the first pointer reaches the end, the second pointer is exactly n nodes from the end. This positioning allows us to remove the nth node from the end efficiently. +

+
\ No newline at end of file diff --git a/hints/reorder-linked-list.md b/hints/reorder-linked-list.md new file mode 100644 index 000000000..299b6c081 --- /dev/null +++ b/hints/reorder-linked-list.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the length of the given list. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to store the node values of the list in an array, reorder the values, and create a new list. Can you think of a better way? Perhaps you can try reordering the nodes directly in place, avoiding the use of extra space. +

+
+ +
+
+ Hint 2 +

+ For example, consider the list [1, 2, 3, 4, 5]. To reorder the list, we connect the first and last nodes, then continue with the second and second-to-last nodes, and so on. Essentially, the list is split into two halves: the first half remains as is, and the second half is reversed and merged with the first half. For instance, [1, 2] will merge with the reversed [5, 4, 3]. Can you figure out a way to implement this reordering process? Maybe dividing the list into two halves could help. +

+
+ +
+
+ Hint 3 +

+ We can divide the list into two halves using the fast and slow pointer approach, which helps identify the midpoint of the list. This allows us to split the list into two halves, with the heads labeled as l1 and l2. Next, we reverse the second half (l2). After these steps, we proceed to reorder the two lists by iterating through them node by node, updating the next pointers accordingly. +

+
diff --git a/hints/reverse-a-linked-list.md b/hints/reverse-a-linked-list.md new file mode 100644 index 000000000..9d8db81d4 --- /dev/null +++ b/hints/reverse-a-linked-list.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the length of the given list. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to store the node values of the linked list into an array, then reverse the array, convert it back into a linked list, and return the new linked list's head. This would be an O(n) time solution but uses extra space. Can you think of a better way? Maybe there is an approach to reverse a linked list in place. +

+
+ +
+
+ Hint 2 +

+ As you can see, the head of the linked list becomes the tail after we reverse it. Can you think of an approach to change the references of the node pointers? Perhaps reversing a simple two-node list might help clarify the process. +

+
+ +
+
+ Hint 3 +

+ For example, consider a list [2, 3], where 2 is the head of the list and 3 is the tail. When we reverse it, 3 becomes the new head, and its next pointer will point to 2. Then, 2's next pointer will point to null. Can you figure out how to apply this approach to reverse a linked list of length n by iterating through it? +

+
+ +
+
+ Hint 4 +

+ We can reverse the linked list in place by reversing the pointers between two nodes while keeping track of the next node's address. Before changing the next pointer of the current node, we must store the next node to ensure we don't lose the rest of the list during the reversal. This way, we can safely update the links between the previous and current nodes. +

+
\ No newline at end of file diff --git a/hints/reverse-bits.md b/hints/reverse-bits.md new file mode 100644 index 000000000..0eb364ce9 --- /dev/null +++ b/hints/reverse-bits.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(1) time and O(1) space. +

+
+ +
+
+ Hint 1 +

+ Given a 32-bit integer, what is the position of bit i after reversing the bits? Maybe you should observe the bit positions before and after reversal to find a pattern. +

+
+ +
+
+ Hint 2 +

+ After reversing the bits, the bit at position i moves to position 31 - i. Can you use this observation to construct the reversed number efficiently? +

+
+ +
+
+ Hint 3 +

+ We initialize res to 0 and iterate through the bits of the given integer n. We extract the bit at the i-th position using ((n >> i) & 1). If it is 1, we set the corresponding bit in res at position (31 - i) using (res |= (1 << (31 - i))). +

+
\ No newline at end of file diff --git a/hints/reverse-integer.md b/hints/reverse-integer.md new file mode 100644 index 000000000..23b4d9c75 --- /dev/null +++ b/hints/reverse-integer.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(1) time and O(1) space. +

+
+ +
+
+ Hint 1 +

+ A straightforward approach would be to convert the given integer to a string, reverse it, convert it back to an integer using a long type, and return 0 if the result exceeds the integer range. Can you think of a better way? +

+
+ +
+
+ Hint 2 +

+ We initially declare the result res as an int with a value of 0. We iterate through the given integer, extracting digits one by one. Before appending a digit to res, we consider multiple cases. Can you determine them? Maybe you should think about overflow. +

+
+ +
+
+ Hint 3 +

+ Let MAX be the maximum positive integer and MIN be the minimum negative integer. We iterate through each digit and check for overflow before updating res. If res > MAX / 10 or res < MIN / 10, return 0. If res == MAX / 10 and the current digit is greater than MAX % 10, return 0. If res == MIN / 10 and the current digit is less than MIN % 10, return 0. Otherwise, append the digit to res and continue. +

+
\ No newline at end of file diff --git a/hints/reverse-nodes-in-k-group.md b/hints/reverse-nodes-in-k-group.md new file mode 100644 index 000000000..b686ce645 --- /dev/null +++ b/hints/reverse-nodes-in-k-group.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the length of the given list. +

+
+ +
+
+ Hint 1 +

+ A brute-force solution would involve storing the linked list node values in an array, reversing the k groups one by one, and then converting the array back into a linked list, requiring extra space of O(n). Can you think of a better way? Perhaps you could apply the idea of reversing a linked list in-place without using extra space. +

+
+ +
+
+ Hint 2 +

+ We can avoid extra space by reversing each group in-place while keeping track of the head of the next group. For example, consider the list [1, 2, 3, 4, 5] with k = 2. First, we reverse the group [1, 2] to [2, 1]. Then, we reverse [3, 4], resulting in [2, 1, 4, 3, 5]. While reversing [3, 4], we need to link 1 to 4 and also link 3 to 5. How can we efficiently manage these pointers? +

+
+ +
+
+ Hint 3 +

+ We create a dummy node to handle modifications to the head of the linked list, pointing its next pointer to the current head. We then iterate k nodes, storing the address of the next group's head and tracking the tail of the previous group. After reversing the current group, we reconnect it by linking the previous group's tail to the new head and the current group's tail to the next group's head. This process is repeated for all groups, and we return the new head of the linked list. +

+
\ No newline at end of file diff --git a/hints/rotate-matrix.md b/hints/rotate-matrix.md new file mode 100644 index 000000000..3f5528d0f --- /dev/null +++ b/hints/rotate-matrix.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n^2) time and O(1) space, where n is the length of the side of the given square matrix. +

+
+ +
+
+ Hint 1 +

+ A brute force approach would use O(n^2) extra space to solve the problem. Can you think of a way to avoid using extra space? Maybe you should consider observing the positions of the elements before rotating and after rotating of the matrix. +

+
+ +
+
+ Hint 2 +

+ We can rotate the matrix in two steps. First, we reverse the matrix vertically, meaning the first row becomes the last, the second row becomes the second last, and so on. Next, we transpose the reversed matrix, meaning rows become columns and columns become rows. How would you transpose the matrix? +

+
+ +
+
+ Hint 3 +

+ Since the given matrix is a square matrix, we only need to iterate over the upper triangular part, meaning the right upper portion of the main diagonal. In this way, we can transpose a matrix. +

+
\ No newline at end of file diff --git a/hints/rotting-fruit.md b/hints/rotting-fruit.md new file mode 100644 index 000000000..76ce34b5b --- /dev/null +++ b/hints/rotting-fruit.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m * n) time and O(m * n) space, where m is the number of rows and n is the number of columns in the given grid. +

+
+ +
+
+ Hint 1 +

+ The DFS algorithm is not suitable for this problem because it explores nodes deeply rather than level by level. In this scenario, we need to determine which oranges rot at each second, which naturally fits a level-by-level traversal. Can you think of an algorithm designed for such a traversal? +

+
+ +
+
+ Hint 2 +

+ We can use the Breadth First Search (BFS) algorithm. At each second, we rot the oranges that are adjacent to the rotten ones. So, we store the rotten oranges in a queue and process them in one go. The time at which a fresh orange gets rotten is the level at which it is visited. How would you implement it? +

+
+ +
+
+ Hint 3 +

+ We traverse the grid and store the rotten oranges in a queue. We then run a BFS, processing the current level of rotten oranges and visiting the adjacent cells of each rotten orange. We only insert the adjacent cell into the queue if it contains a fresh orange. This process continues until the queue is empty. The level at which the BFS stops is the answer. However, we also need to check whether all oranges have rotted by traversing the grid. If any fresh orange is found, we return -1; otherwise, we return the level. +

+
\ No newline at end of file diff --git a/hints/same-binary-tree.md b/hints/same-binary-tree.md new file mode 100644 index 000000000..3a79a39f2 --- /dev/null +++ b/hints/same-binary-tree.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(n) space, where n is the number of nodes in the tree. +

+
+ +
+
+ Hint 1 +

+ Can you think of an algorithm that is used to traverse the tree? Maybe in terms of recursion. +

+
+ +
+
+ Hint 2 +

+ We can use the Depth First Search (DFS) algorithm to traverse the tree. Can you think of a way to simultaneously traverse both the trees? +

+
+ +
+
+ Hint 3 +

+ We traverse both trees starting from their root nodes. At each step in the recursion, we check if the current nodes in both trees are either null or have the same value. If one node is null while the other is not, or if their values differ, we return false. If the values match, we recursively check their left and right subtrees. If any recursive call returns false, the result for the current recursive call is false. +

+
\ No newline at end of file diff --git a/hints/search-2d-matrix.md b/hints/search-2d-matrix.md new file mode 100644 index 000000000..53153f798 --- /dev/null +++ b/hints/search-2d-matrix.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(log(m * n)) time and O(1) space, where m is the number of rows and n is the number of columns in the matrix. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to do a linear search on the matrix. This would be an O(m * n) solution. Can you think of a better way? Maybe an efficient searching algorithm, as the given matrix is sorted. +

+
+ +
+
+ Hint 2 +

+ We can use binary search, which is particularly effective when we visualize a row as a range of numbers, [x, y] where x is the first cell and y is the last cell of a row. Using this representation, it becomes straightforward to check if the target value falls within the range. How can you use binary search to solve the problem? +

+
+ +
+
+ Hint 3 +

+ We perform a binary search on the rows to identify the row in which the target value might fall. This operation takes O(logm) time, where m is the number of rows. Now, when we find the potential row, can you find the best way to search the target in that row? The sorted nature of each row is the hint. +

+
+ +
+
+ Hint 4 +

+ Once we identify the potential row where the target might exist, we can perform a binary search on that row which acts as a one dimensional array. It takes O(logn) time, where n is the number of columns in the row. +

+
\ No newline at end of file diff --git a/hints/search-for-word-ii.md b/hints/search-for-word-ii.md new file mode 100644 index 000000000..205964c7e --- /dev/null +++ b/hints/search-for-word-ii.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m * n * 4 * (3^(t-1)) + s) time and O(s) space, where m is the number of rows, n is the number of columns, t is the maximum length of any word and s is the sum of the lengths of all the words. +

+
+ +
+
+ Hint 1 +

+ To search for a word in the grid, we can use backtracking by starting at each cell, simultaneously iterating through the word and matching the characters with the cells, recursively visiting the neighboring cells. However, if we are given a list of words and need to search for each one, it becomes inefficient to iterate through each word and run backtracking for each. Can you think of a better way? Perhaps a data structure could help with more efficient word search and insertion. +

+
+ +
+
+ Hint 2 +

+ We can use a Trie to efficiently search for multiple words. After inserting all words into the Trie, we traverse the grid once. For each character in the grid, we check if it exists in the current Trie node. If not, we prune the search. If we encounter an "end of word" flag in the Trie node, we know a valid word has been found. But how can we add that word to the result list? Maybe you should think about what additional information you can store in the Trie node. +

+
+ +
+
+ Hint 3 +

+ When we insert a word into the Trie, we can store the word's index. Why? Because when we want to add the word to the result list after finding a valid word, we can easily add it using the index. After adding that word, we put index = -1 as we shouldn't add the word multiple times to the result list. How can you implement this? +

+
+ +
+
+ Hint 4 +

+ We insert all the words into the Trie with their indices marked. Then, we iterate through each cell in the grid. At each cell, we start at the root of the Trie and explore all possible paths. As we traverse, we match characters in the cell with those in the Trie nodes. If we encounter the end of a word, we take the index at that node and add the corresponding word to the result list. Afterward, we unmark that index and continue exploring further paths. +

+
\ No newline at end of file diff --git a/hints/search-for-word.md b/hints/search-for-word.md new file mode 100644 index 000000000..9dcdf4102 --- /dev/null +++ b/hints/search-for-word.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m * (4^n)) time and O(n) space, where m is the number of cells in the given board and n is the size of the given word. +

+
+ +
+
+ Hint 1 +

+ As we can start at any cell on the board, we can explore all paths from that cell. Can you think of an algorithm to do so? Also, you should consider a way to avoid visiting a cell that has already been visited in the current path. +

+
+ +
+
+ Hint 2 +

+ We can use a hash set to avoid revisiting a cell in the current path by inserting the (row, col) of the visiting cell into the hash set and exploring all paths (four directions, as we can move to four neighboring cells) from that cell. Can you think of the base condition for this recursive path? Maybe you should consider the board boundaries, and also, we can extend a path if the character at the cell matches the character in the word. +

+
+ +
+
+ Hint 3 +

+ We can use backtracking, starting from each cell on the board with coordinates (row, col) and index i for the given word. We return false if (row, col) is out of bounds or if board[row][col] != word[i]. When i reaches the end of the word, we return true, indicating a valid path. At each step, we add (row, col) to a hash set to avoid revisiting cells. After exploring the four possible directions, we backtrack and remove (row, col) from the hash set. +

+
\ No newline at end of file diff --git a/hints/serialize-and-deserialize-binary-tree.md b/hints/serialize-and-deserialize-binary-tree.md new file mode 100644 index 000000000..a93feef8e --- /dev/null +++ b/hints/serialize-and-deserialize-binary-tree.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(n) space, where n is the number of nodes in the given tree. +

+
+ +
+
+ Hint 1 +

+ A straightforward way to serialize a tree is by traversing it and adding nodes to a string separated by a delimiter (example: ","), but this does not handle null nodes effectively. During deserialization, it becomes unclear where to stop or how to handle missing children. Can you think of a way to indicate null nodes explicitly? +

+
+ +
+
+ Hint 2 +

+ Including a placeholder for null nodes (example: "N") during serialization ensures that the exact structure of the tree is preserved. This placeholder allows us to identify missing children and reconstruct the tree accurately during deserialization. +

+
+ +
+
+ Hint 3 +

+ We can use the Depth First Search (DFS) algorithm for both serialization and deserialization. During serialization, we traverse the tree and add node values to the result string separated by a delimiter, inserting N whenever we encounter a null node. During deserialization, we process the serialized string using an index i, create nodes for valid values, and return from the current path whenever we encounter N, reconstructing the tree accurately. +

+
\ No newline at end of file diff --git a/hints/set-zeroes-in-matrix.md b/hints/set-zeroes-in-matrix.md new file mode 100644 index 000000000..663ff919e --- /dev/null +++ b/hints/set-zeroes-in-matrix.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m*n) time and O(1) space, where m is the number of rows and n is the number of columns in the given matrix. +

+
+ +
+
+ Hint 1 +

+ A brute force approach would iterate through the given matrix and update the corresponding row and column in a new matrix on the fly. This would result in an O((m*n)*(m+n)) time and O(m*n) space solution. Can you think of a better way? Maybe you should consider using a single variable for a row and a single variable for a column instead of updating entire row and column. +

+
+ +
+
+ Hint 2 +

+ A better approach is to use O(m+n) boolean arrays. We iterate through the matrix, and when encountering a zero, we mark the respective row and column as true. In the second iteration, we set a cell to 0 if its corresponding row or column is marked true. Can you think of a way to optimize the space further? +

+
+ +
+
+ Hint 3 +

+ We can use the topmost row and leftmost column of the matrix as boolean arrays by marking 0 instead of true. However, since they overlap at one cell, we use a single variable to track the top row separately. We then iterate through the matrix and mark zeros accordingly. +

+
+ +
+
+ Hint 4 +

+ In the second iteration, we update all cells that are not part of the top row or left column accordingly. After making the necessary changes, we check the top-leftmost cell and update the corresponding column. Finally, we check the extra variable and update the top row accordingly. +

+
\ No newline at end of file diff --git a/hints/single-number.md b/hints/single-number.md new file mode 100644 index 000000000..6aab0485f --- /dev/null +++ b/hints/single-number.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute force approach would iterate through the array, checking each element using a nested loop. If a duplicate is found, we continue to the next element; otherwise, the current element is the required number. This results in an O(n^2) solution. Can you think of a better way? Maybe a data structure can help detect duplicates efficiently. +

+
+ +
+
+ Hint 2 +

+ We use a hash set, iterating through the array and adding elements that are not in the set while removing those that are already present. After the iteration, the required number remains in the hash set. This results in an O(n) space solution. Can you further optimize it? Maybe a bitwise operator could be useful here. +

+
+ +
+
+ Hint 3 +

+ Think about the bitwise XOR ("^"). What is the result when two identical numbers are XORed? +

+
+ +
+
+ Hint 4 +

+ When two identical numbers are XORed, they cancel out, resulting in zero. Since every number appears twice except for one, the XOR of the entire array gives the number that appears only once. +

+
\ No newline at end of file diff --git a/hints/sliding-window-maximum.md b/hints/sliding-window-maximum.md new file mode 100644 index 000000000..3506407c3 --- /dev/null +++ b/hints/sliding-window-maximum.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(nlogn) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would involve iterating through each window of size k and finding the maximum element within the window by iterating through it. This would be an O(n * k) solution. Can you think of a better way? Maybe think of a data structure that tells the current maximum element of the window in O(1) time. +

+
+ +
+
+ Hint 2 +

+ A heap is the best data structure to use when dealing with maximum or minimum values and it takes O(1) time to get the max or min value. Here, we use a max-heap. But what should we do if the current maximum element is no longer part of the window? Can you think of a different way of adding values to the max-heap? +

+
+ +
+
+ Hint 3 +

+ We process each window by adding elements to the heap along with their indices to track whether the maximum value is still within the current window. As we move from one window to the next, an element may go out of the window but still remain in the max-heap. Is there a way to handle this situation efficiently? +

+
+ +
+
+ Hint 4 +

+ We can ignore those elements that are no longer part of the current window, except when the maximum value is outside the window. In that case, we remove elements from the max-heap until the maximum value belongs to the current window. Why? Because those elements will be eventually removed when the maximum element goes out of the window. +

+
\ No newline at end of file diff --git a/hints/spiral-matrix.md b/hints/spiral-matrix.md new file mode 100644 index 000000000..2b8eced0a --- /dev/null +++ b/hints/spiral-matrix.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m*n) time and O(1) extra space, where m is the number of rows and n is the number of columns in the given matrix. +

+
+ +
+
+ Hint 1 +

+ Try to simulate the process as described in the problem. Think in terms of matrix layers, starting from the outermost boundaries and moving inward. Can you determine an efficient way to implement this? +

+
+ +
+
+ Hint 2 +

+ Each boundary consists of four parts: the top row, right column, bottom row, and left column, which follow the spiral order and act as four pointers. For each layer, the top pointer increments by one, the right pointer decrements by one, the left pointer increments by one, and the bottom pointer decrements by one. +

+
+ +
+
+ Hint 3 +

+ At each layer, four loops traverse the matrix: one moves left to right along the top row, another moves top to bottom along the right column, the next moves right to left along the bottom row, and the last moves bottom to top along the left column. This process generates the spiral order. +

+
\ No newline at end of file diff --git a/hints/string-encode-and-decode.md b/hints/string-encode-and-decode.md new file mode 100644 index 000000000..82bc8fbcb --- /dev/null +++ b/hints/string-encode-and-decode.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m) time for each encode() and decode() call and O(m+n) space, where m is the sum of lengths of all the strings and n is the number of strings. +

+
+ +
+
+ Hint 1 +

+ A naive solution would be to use a non-ascii character as a delimiter. Can you think of a better way? +

+
+ +
+
+ Hint 2 +

+ Try to encode and decode the strings using a smart approach based on the lengths of each string. How can you differentiate between the lengths and any numbers that might be present in the strings? +

+
+ +
+
+ Hint 3 +

+ We can use an encoding approach where we start with a number representing the length of the string, followed by a separator character (let's use # for simplicity), and then the string itself. To decode, we read the number until we reach a #, then use that number to read the specified number of characters as the string. +

+
\ No newline at end of file diff --git a/hints/subsets-ii.md b/hints/subsets-ii.md new file mode 100644 index 000000000..4001ea052 --- /dev/null +++ b/hints/subsets-ii.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n * (2^n)) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute-force solution would involve creating a hash set and inserting every subset into it. Then, converting the hash set to a list and returning it. However, this approach would require extra space of O(2^n). Can you think of a better way? Maybe you should sort the input array and observe which recusive calls are resposible to make duplicate subsets. +

+
+ +
+
+ Hint 2 +

+ We can use backtracking to generate subsets of an array. If the input contains duplicates, duplicate subsets may be created. To prevent this, we sort the array beforehand. For example, in [1, 1, 2], sorting allows us to create subsets using the first 1 and skip the second 1, ensuring unique subsets. How can you implement this? +

+
+ +
+
+ Hint 3 +

+ We start by sorting the input array. Then, we recursively iterate through the array from left to right, extending recursive paths by including or excluding each element. To avoid duplicate subsets, we skip an element if it is the same as the previous one. Finally, we return the generated subsets as a list. +

+
\ No newline at end of file diff --git a/hints/subsets.md b/hints/subsets.md new file mode 100644 index 000000000..63a694ba2 --- /dev/null +++ b/hints/subsets.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n * (2^n)) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ It is straightforward that if the array is empty we return an empty array. When we have an array [1] which is of size 1, we have two subsets, [[], [1]] as the output. Can you think why the output is so? +

+
+ +
+
+ Hint 2 +

+ We can see that one subset includes a number, and another does not. From this, we can conclude that we need to find the subsets that include a number and those that do not. This results in 2^n subsets for an array of size n because there are many combinations for including and excluding the array of numbers. Since the elements are unique, duplicate subsets will not be formed if we ensure that we don't pick the element more than once in the current subset. Which algorithm is helpful to generate all subsets, and how would you implement it? +

+
+ +
+
+ Hint 3 +

+ We can use backtracking to generate all possible subsets. We iterate through the given array with an index i and an initially empty temporary list representing the current subset. We recursively process each index, adding the corresponding element to the current subset and continuing, which results in a subset that includes that element. Alternatively, we skip the element by not adding it to the subset and proceed to the next index, forming a subset without including that element. What can be the base condition to end this recursion? +

+
+ +
+
+ Hint 4 +

+ When the index i reaches the end of the array, we append a copy of the subset formed in that particular recursive path to the result list and return. All subsets of the given array are generated from these different recursive paths, which represent various combinations of "include" and "not include" steps for the elements of the array. As we are only iterating from left to right in the array, we don't pick an element more than once. +

+
\ No newline at end of file diff --git a/hints/subtree-of-a-binary-tree.md b/hints/subtree-of-a-binary-tree.md new file mode 100644 index 000000000..79aaf1a57 --- /dev/null +++ b/hints/subtree-of-a-binary-tree.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(m * n) time and O(m + n) space, where n and m are the number of nodes in root and subRoot, respectively. +

+
+ +
+
+ Hint 1 +

+ A subtree of a tree is a tree rooted at a specific node. We need to check whether the given subRoot is identical to any of the subtrees of root. Can you think of a recursive way to check this? Maybe you can leverage the idea of solving a problem where two trees are given, and you need to check whether they are identical in structure and values. +

+
+ +
+
+ Hint 2 +

+ When two trees are identical, it means that every node in both trees has the same value and structure. We can use the Depth First Search (DFS) algorithm to solve the problem. How do you implement this? +

+
+ +
+
+ Hint 3 +

+ We traverse the given root, and at each node, we check if the subtree rooted at that node is identical to the given subRoot. We use a helper function, sameTree(root1, root2), to determine whether the two trees passed to it are identical in both structure and values. +

+
\ No newline at end of file diff --git a/hints/sum-of-two-integers.md b/hints/sum-of-two-integers.md new file mode 100644 index 000000000..340f197d0 --- /dev/null +++ b/hints/sum-of-two-integers.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(1) time and O(1) space. +

+
+ +
+
+ Hint 1 +

+ A brute force approach would use the addition operator. Can you think of a way to perform addition without using it? Maybe you should consider solving this using bit manipulation. +

+
+ +
+
+ Hint 2 +

+ We can use the bitwise XOR operator to perform addition. If both a and b have 1 at the same bit position, the sum at that position is 0, and a carry of 1 is generated. If the bits are different, the sum at that position is 1. Additionally, we account for the carry from the previous step in the next iteration. +

+
+ +
+
+ Hint 3 +

+ We iterate bit by bit from 0 to 31 since the given integers are 32-bit. We track the carry, initially set to 0, and initialize the result as res. During iteration, the XOR of the bits at the i-th position of both integers and the carry determines the current bit of res. How can you handle negative numbers? +

+
+ +
+
+ Hint 4 +

+ To handle negative numbers, if the final result exceeds the maximum positive 32-bit integer, it means the number should be negative. We adjust it using bitwise operations: flipping the bits with res ^ ((2 ^ 32) - 1) and applying ~ to restore the correct two’s complement representation. This ensures the result correctly represents signed 32-bit integers. +

+
\ No newline at end of file diff --git a/hints/surrounded-regions.md b/hints/surrounded-regions.md new file mode 100644 index 000000000..d61a1db78 --- /dev/null +++ b/hints/surrounded-regions.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m * n) time and O(m * n) space, where m is the number of rows and n is the number of columns in the matrix. +

+
+ +
+
+ Hint 1 +

+ We observe that we need to capture the regions that are not connected to the O's on the border of the matrix. This means there should be no path connecting the O's on the border to any O's in the region. Can you think of a way to check the region connected to these border O's? +

+
+ +
+
+ Hint 2 +

+ We can use the Depth First Search (DFS) algorithm. Instead of checking the region connected to the border O's, we can reverse the approach and mark the regions that are reachable from the border O's. How would you implement this? +

+
+ +
+
+ Hint 3 +

+ We run the DFS from every 'O' on the border of the matrix, visiting the neighboring cells that are equal to 'O' recursively and marking them as '#' to avoid revisiting. After completing all the DFS calls, we traverse the matrix again and capture the cells where matrix[i][j] == 'O', and unmark the cells back to 'O' where matrix[i][j] == '#'. +

+
\ No newline at end of file diff --git a/hints/swim-in-rising-water.md b/hints/swim-in-rising-water.md new file mode 100644 index 000000000..5e098b6f0 --- /dev/null +++ b/hints/swim-in-rising-water.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O((n^2)logn) time and O(n^2) space, where n is the number of rows or columns of the square matrix. +

+
+ +
+
+ Hint 1 +

+ Think of this problem as a graph where each cell represents a node. You can move from one cell to its adjacent cell if the time is greater than or equal to the adjacent cell's elevation. Note that swimming does not cost time, but you may need to wait at a cell for the time to reach the required elevation. What do you notice about the path from (0, 0) to (n - 1, n - 1)? Perhaps a greedy approach would be useful here. +

+
+ +
+
+ Hint 2 +

+ We can observe that the maximum elevation value along the path determines the time taken for that path. Therefore, we need to find the path where the maximum elevation is minimized. Can you think of an algorithm to find such a path from the source (0, 0) to the destination (n - 1, n - 1)? Perhaps a shortest path algorithm could be useful here. +

+
+ +
+
+ Hint 3 +

+ We can use Dijkstra's algorithm. We initialize a Min-heap and a matrix with infinity. We run the algorithm starting from the source (0, 0), and we track the maximum elevation encountered along the paths. This maximum elevation is used as the key for comparison in Dijkstra's algorithm. If we encounter the destination (n - 1, n - 1), we return the maximum elevation of the path that reached the destination. +

+
\ No newline at end of file diff --git a/hints/target-sum.md b/hints/target-sum.md new file mode 100644 index 000000000..06e9e9a3e --- /dev/null +++ b/hints/target-sum.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n * m) time and O(n * m) space, where n is the size of the input array and m is the sum of all the elements in the array. +

+
+ +
+
+ Hint 1 +

+ Try to think in terms of recursion and visualize it as a decision tree, where we have two choices at each recursion step: assigning a positive or negative sign. +

+
+ +
+
+ Hint 2 +

+ We recursively iterate through the array using index i, tracking the current sum along the recursive path. Each step branches into two paths, and we sum the number of ways to reach the target. If the index i goes out of bounds, we return 1 if the current sum equals the target; otherwise, we return 0. +

+
+ +
+
+ Hint 3 +

+ This approach is exponential. We can use memoization to cache recursive call results and avoid redundant calculations. A hash map or a 2D array with modifications can be used for caching. If using a 2D array, the dimensions can be (n * (2m + 1)), where n is the array size and m represents the sum of the array elements. +

+
\ No newline at end of file diff --git a/hints/task-scheduling.md b/hints/task-scheduling.md new file mode 100644 index 000000000..1b351db49 --- /dev/null +++ b/hints/task-scheduling.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(m) time and O(1) space, where m is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ There are at most 26 different tasks, represented by A through Z. It is more efficient to count the frequency of each task and store it in a hash map or an array of size 26. Can you think of a way to determine which task should be processed first? +

+
+ +
+
+ Hint 2 +

+ We should always process the most frequent task first. After selecting the most frequent task, we must ensure that it is not processed again until after n seconds, due to the cooldown condition. Can you think of an efficient way to select the most frequent task and enforce the cooldown? Perhaps you could use a data structure that allows for O(1) time to retrieve the maximum element and another data structure to cooldown the processed tasks. +

+
+ +
+
+ Hint 3 +

+ We can use a Max-Heap to efficiently retrieve the most frequent task at any given instance. However, to enforce the cooldown period, we must temporarily hold off from reinserting the processed task into the heap. This is where a queue data structure comes in handy. It helps maintain the order of processed tasks. Can you implement this? +

+
+ +
+
+ Hint 4 +

+ We start by calculating the frequency of each task and initialize a variable time to track the total processing time. The task frequencies are inserted into a Max-Heap. We also use a queue to store tasks along with the time they become available after the cooldown. At each step, if the Max-Heap is empty, we update time to match the next available task in the queue, covering idle time. Otherwise, we process the most frequent task from the heap, decrement its frequency, and if it's still valid, add it back to the queue with its next available time. If the task at the front of the queue becomes available, we pop it and reinsert it into the heap. +

+
\ No newline at end of file diff --git a/hints/three-integer-sum.md b/hints/three-integer-sum.md new file mode 100644 index 000000000..444e13872 --- /dev/null +++ b/hints/three-integer-sum.md @@ -0,0 +1,47 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n^2) time and O(1) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to check for every triplet in the array. This would be an O(n^3) solution. Can you think of a better way? +

+
+ +
+
+ Hint 2 +

+ Can you think of an algorithm after sorting the input array? What can we observe by rearranging the given equation in the problem? +

+
+ +
+
+ Hint 3 +

+ We can iterate through nums with index i and get nums[i] = -(nums[j] + nums[k]) after rearranging the equation, making -nums[i] = nums[j] + nums[k]. For each index i, we should efficiently calculate the j and k pairs without duplicates. Which algorithm is suitable to find j and k pairs? +

+
+ +
+
+ Hint 4 +

+ To efficiently find the j and k pairs, we run the two pointer approach on the elements to the right of index i as the array is sorted. When we run two pointer algorithm, consider j and k as pointers (j is at left, k is at right) and target = -nums[i], if the current sum num[j] + nums[k] < target then we need to increase the value of current sum by incrementing j pointer. Else if the current sum num[j] + nums[k] > target then we should decrease the value of current sum by decrementing k pointer. How do you deal with duplicates? +

+
+ +
+
+ Hint 5 +

+ When the current sum nums[j] + nums[k] == target add this pair to the result. We can move j or k pointer until j < k and the pairs are repeated. This ensures that no duplicate pairs are added to the result. +

+
diff --git a/hints/time-based-key-value-store.md b/hints/time-based-key-value-store.md new file mode 100644 index 000000000..5b4e80ea6 --- /dev/null +++ b/hints/time-based-key-value-store.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(1) time for set(), O(logn) time for get(), and O(m * n) space, where n is the total number of values associated with a key, and m is the total number of keys. +

+
+ +
+
+ Hint 1 +

+ Can you think of a data structure that is useful for storing key-value pairs? Perhaps a hash-based data structure where we not only store unique elements but also associate additional information with each element? +

+
+ +
+
+ Hint 2 +

+ We store key-value pairs in a hash map. In this case, we store the keys as usual, but instead of a single value, we store a list of values, each paired with its corresponding timestamp. This allows us to implement the set() method in O(1). How can you leverage this hash map to implement the get() method? +

+
+ +
+
+ Hint 3 +

+ A brute force solution would involve linearly going through every value associated with the key and returning the most recent value with a timestamp less than or equal to the given timestamp. This approach has a time complexity of O(n) for each get() call. Can you think of a better way? Since the timestamps in the value list are sorted in ascending order by default, maybe an efficient searching algorithm could help. +

+
+ +
+
+ Hint 4 +

+ We can use binary search because the timestamps in the values list are sorted in ascending order. This makes it straightforward to find the value with the most recent timestamp that is less than or equal to the given timestamp. +

+
\ No newline at end of file diff --git a/hints/top-k-elements-in-list.md b/hints/top-k-elements-in-list.md new file mode 100644 index 000000000..566ec05de --- /dev/null +++ b/hints/top-k-elements-in-list.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A naive solution would be to count the frequency of each number and then sort the array based on each element’s frequency. After that, we would select the top k frequent elements. This would be an O(nlogn) solution. Though this solution is acceptable, can you think of a better way? +

+
+ +
+
+ Hint 2 +

+ Can you think of an algorithm which involves grouping numbers based on their frequency? +

+
+ +
+
+ Hint 3 +

+ Use the bucket sort algorithm to create n buckets, grouping numbers based on their frequencies from 1 to n. Then, pick the top k numbers from the buckets, starting from n down to 1. +

+
\ No newline at end of file diff --git a/hints/trapping-rain-water.md b/hints/trapping-rain-water.md new file mode 100644 index 000000000..2ae54b063 --- /dev/null +++ b/hints/trapping-rain-water.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ How can we determine the amount of water that can be trapped at a specific position in the array? Perhaps looking at the image might help clarify. +

+
+ +
+
+ Hint 2 +

+ From the image, we can see that to calculate the amount of water trapped at a position, the greater element to the left l and the greater element to the right r of the current position are crucial. The formula for the trapped water at index i is given by: min(height[l], height[r]) - height[i]. +

+
+ +
+
+ Hint 3 +

+ A brute force solution would involve iterating through the array with index i, finding the greater elements to the left (l) and right (r) for each index, and then calculating the trapped water for that position. The total amount of trapped water would be the sum of the water trapped at each index. Finding l and r for each index involves repeated work, resulting in an O(n^2) solution. Can you think of a more efficient approach? Maybe there is something that we can precompute and store in arrays. +

+
+ +
+
+ Hint 4 +

+ We can store the prefix maximum in an array by iterating from left to right and the suffix maximum in another array by iterating from right to left. For example, in [1, 5, 2, 3, 4], for the element 3, the prefix maximum is 5, and the suffix maximum is 4. Once these arrays are built, we can iterate through the array with index i and calculate the total water trapped at each position using the formula: min(prefix[i], suffix[i]) - height[i]. +

+
\ No newline at end of file diff --git a/hints/two-integer-sum-ii.md b/hints/two-integer-sum-ii.md new file mode 100644 index 000000000..a07baf4ac --- /dev/null +++ b/hints/two-integer-sum-ii.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(1) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to check every pair of numbers in the array. This would be an O(n^2) solution. Can you think of a better way? +

+
+ +
+
+ Hint 2 +

+ Can you think of an algorithm by taking the advantage of array being sorted? +

+
+ +
+
+ Hint 3 +

+ We can use the two-pointer algorithm. If nums[0] + nums[n-1] > target, then we know nums[n - 1] can not possibly be included in any pairs. Why? Because nums[n - 1] is the largest element in the array. Even by adding it with nums[0], which is the smallest element, we still exceed the target. You can think of the case when nums[0] + nums[n - 1] < target. +

+
+ +
+
+ Hint 4 +

+ We keep two pointers, one at the start and the other at the end of the array. If the sum of the numbers at the two pointers is greater than the target, decrement the right pointer, else increment the left pointer. Repeat this process until you find a valid pair. +

+
\ No newline at end of file diff --git a/hints/two-integer-sum.md b/hints/two-integer-sum.md new file mode 100644 index 000000000..948aae221 --- /dev/null +++ b/hints/two-integer-sum.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(n) space, where n is the size of the input array. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to check every pair of numbers in the array. This would be an O(n^2) solution. Can you think of a better way? Maybe in terms of mathematical equation? +

+
+ +
+
+ Hint 2 +

+ Given, We need to find indices i and j such that i != j and nums[i] + nums[j] == target. Can you rearrange the equation and try to fix any index to iterate on? +

+
+ +
+
+ Hint 3 +

+ we can iterate through nums with index i. Let difference = target - nums[i] and check if difference exists in the hash map as we iterate through the array, else store the current element in the hashmap with its index and continue. We use a hashmap for O(1) lookups. +

+
\ No newline at end of file diff --git a/hints/valid-binary-search-tree.md b/hints/valid-binary-search-tree.md new file mode 100644 index 000000000..9a691ece8 --- /dev/null +++ b/hints/valid-binary-search-tree.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(n) space, where n is the number of nodes in the given tree. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would involve traversing the tree and, for every node, checking its entire left subtree to ensure all nodes are less than the current node, and its entire right subtree to ensure all nodes are greater. This results in an O(n^2) solution. Can you think of a better way? Maybe tracking values during the traversal would help. +

+
+ +
+
+ Hint 2 +

+ We can use the Depth First Search (DFS) algorithm to traverse the tree. At each node, we need to ensure that the tree rooted at that node is a valid Binary Search Tree (BST). One way to do this is by tracking an interval that defines the lower and upper limits for the node's value in that subtree. This interval will be updated as we move down the tree, ensuring each node adheres to the BST property. +

+
+ +
+
+ Hint 3 +

+ We start with the interval [-infinity, infinity] for the root node. As we traverse the tree, when checking the left subtree, we update the maximum value limit because all values in the left subtree must be less than the current node's value. Conversely, when checking the right subtree, we update the minimum value limit because all values in the right subtree must be greater than the current node's value. +

+
\ No newline at end of file diff --git a/hints/valid-parenthesis-string.md b/hints/valid-parenthesis-string.md new file mode 100644 index 000000000..29baed7d5 --- /dev/null +++ b/hints/valid-parenthesis-string.md @@ -0,0 +1,39 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n) time and O(n) space, where n is the length of the input string. +

+
+ +
+
+ Hint 1 +

+ A brute force approach would try all possibilities when encountering a '*' and recursively solve the problem, leading to an exponential approach. Can you think of a better way? Maybe a data structure commonly used in parenthesis problems could be useful. +

+
+ +
+
+ Hint 2 +

+ We can solve the problem using a stack-based approach. We maintain two stacks: one for tracking the indices of left parentheses and another for star indices. As we iterate through the string from left to right, we push indices onto their respective stacks when encountering a left parenthesis '(' or a star '*'. Can you determine the logic for the right parentesis case? +

+
+ +
+
+ Hint 3 +

+ If the left parenthesis stack is not empty, we pop from it. Otherwise, we pop from the star stack, treating the star as a left parenthesis to keep the string valid. After iterating the string, the stacks might be non-empty? Can you determine the logic for this case? +

+
+ +
+
+ Hint 4 +

+ Now, we try to match the remaining left parentheses with stars, ensuring the stars appear after the left parentheses in the string. We simultaneously pop from both stacks, and if the index of a left parenthesis is greater than that of a star, the string is invalid as there is no matching right parenthesis. In this case, we return false. +

+
\ No newline at end of file diff --git a/hints/valid-sudoku.md b/hints/valid-sudoku.md new file mode 100644 index 000000000..51655ed8b --- /dev/null +++ b/hints/valid-sudoku.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n^2) time and O(n^2) space, where n is the number of rows in the square grid. +

+
+ +
+
+ Hint 1 +

+ Which data structure would you prefer to use for checking duplicates? +

+
+ +
+
+ Hint 2 +

+ You can use a hash set for every row and column to check duplicates. But how can you efficiently check for the squares? +

+
+ +
+
+ Hint 3 +

+ We can find the index of each square by the equation (row / 3) * 3 + (col / 3). Then we use hash set for O(1) lookups while inserting the number into its row, column and square it belongs to. We use separate hash maps for rows, columns, and squares. +

+
\ No newline at end of file diff --git a/hints/valid-tree.md b/hints/valid-tree.md new file mode 100644 index 000000000..7e7d37358 --- /dev/null +++ b/hints/valid-tree.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(V + E) time and O(V + E) space, where V is the number vertices and E is the number of edges in the graph. +

+
+ +
+
+ Hint 1 +

+ According to the definition of a tree, a tree is an undirected graph with no cycles, all the nodes are connected as one component, and any two nodes have exactly one path. Can you think of a recursive algorithm to detect a cycle in the given graph and ensure it is a tree? +

+
+ +
+
+ Hint 2 +

+ We can use the Depth First Search (DFS) algorithm to detect a cycle in the graph. Since a tree has only one component, we can start the DFS from any node, say node 0. During the traversal, we recursively visit its neighbors (children). If we encounter any already visited node that is not the parent of the current node, we return false as it indicates a cycle. How would you implement this? +

+
+ +
+
+ Hint 3 +

+ We start DFS from node 0, assuming -1 as its parent. We initialize a hash set visit to track the visited nodes in the graph. During the DFS, we first check if the current node is already in visit. If it is, we return false, detecting a cycle. Otherwise, we mark the node as visited and perform DFS on its neighbors, skipping the parent node to avoid revisiting it. After all DFS calls, if we have visited all nodes, we return true, as the graph is connected. Otherwise, we return false because a tree must contain all nodes. +

+
\ No newline at end of file diff --git a/hints/validate-parentheses.md b/hints/validate-parentheses.md new file mode 100644 index 000000000..ec5216bb4 --- /dev/null +++ b/hints/validate-parentheses.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O(n) time and O(n) space, where n is the length of the given string. +

+
+ +
+
+ Hint 1 +

+ A brute force solution would be to continuously remove valid brackets until no more can be removed. If the remaining string is empty, return true; otherwise, return false. This would result in an O(n^2) solution. Can we think of a better approach? Perhaps a data structure could help. +

+
+ +
+
+ Hint 2 +

+ We can use a stack to store characters. Iterate through the string by index. For an opening bracket, push it onto the stack. If the bracket is a closing type, check for the corresponding opening bracket at the top of the stack. If we don't find the corresponding opening bracket, immediately return false. Why does this work? +

+
+ +
+
+ Hint 3 +

+ In a valid parenthesis expression, every opening bracket must have a corresponding closing bracket. The stack is used to process the valid string, and it should be empty after the entire process. This ensures that there is a valid substring between each opening and closing bracket. +

+
\ No newline at end of file diff --git a/hints/word-break.md b/hints/word-break.md new file mode 100644 index 000000000..cb850a49c --- /dev/null +++ b/hints/word-break.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution as good or better than O(n * m * t) time and O(n) space, where n is the length of the string s, m is the number of words in wordDict, and t is the maximum length of any word in wordDict. +

+
+ +
+
+ Hint 1 +

+ Try to think of this problem in terms of recursion, where we explore all possibilities. We iterate through the given string s, attempting to pick a word from wordDict that matches a portion of s, and then recursively continue processing the remaining string. Can you determine the recurrence relation and base condition? +

+
+ +
+
+ Hint 2 +

+ The base condition is to return true if we reach the end of the string s. At each recursive call with index i iterating through s, we check all words in wordDict and recursively process the remaining string by incrementing i by the length of the matched word. If any recursive path returns true, we immediately return true. However, this solution is exponential. Can you think of an optimization? Maybe you should consider an approach that avoids repeated work. +

+
+ +
+
+ Hint 3 +

+ We can avoid recalculating results for recursive calls by using memoization. Since we iterate with index i, we can use a hash map or an array of the same length as s to cache the results of recursive calls and prevent redundant computations. +

+
\ No newline at end of file diff --git a/hints/word-ladder.md b/hints/word-ladder.md new file mode 100644 index 000000000..efdd32a2c --- /dev/null +++ b/hints/word-ladder.md @@ -0,0 +1,31 @@ +
+
+ Recommended Time & Space Complexity +

+ You should aim for a solution with O((m ^ 2) * n) time and O((m ^ 2) * n) space, where n is the number of words and m is the length of the word. +

+
+ +
+
+ Hint 1 +

+ Consider the given problem in terms of a graph, treating strings as nodes. Think of a way to build edges where two strings have an edge if they differ by a single character. A naive approach would be to consider each pair of strings and check whether an edge can be formed. Can you think of an efficient way? For example, consider a string hot and think about the strings that can be formed from it by changing a single letter. +

+
+ +
+
+ Hint 2 +

+ To efficiently build edges, consider transforming each word into intermediate states by replacing one character with a wildcard, like *. For example, the word hot can be transformed into *ot, h*t, and ho*. These intermediate states act as "parents" that connect words differing by one character. For instance, *ot can connect to words like cot. For each word in the list, generate all possible patterns by replacing each character with * and store the word as a child of these patterns. We can run a BFS starting from the beginWord, visiting other words while avoiding revisiting by using a hash set. +

+
+ +
+
+ Hint 3 +

+ When visiting a node during BFS, if the word matches the endWord, we immediately return true. Otherwise, we generate the pattern words that can be formed from the current word and attempt to visit the words connected to these pattern words. We add only unvisited words to the queue. If we exhaust all possibilities without finding the endWord, we return false. +

+
\ No newline at end of file diff --git a/java/0001-two-sum.java b/java/0001-two-sum.java new file mode 100644 index 000000000..d6845efa9 --- /dev/null +++ b/java/0001-two-sum.java @@ -0,0 +1,19 @@ +class Solution { + + public int[] twoSum(int[] nums, int target) { + HashMap prevMap = new HashMap<>(); + + for (int i = 0; i < nums.length; i++) { + int num = nums[i]; + int diff = target - num; + + if (prevMap.containsKey(diff)) { + return new int[] { prevMap.get(diff), i }; + } + + prevMap.put(num, i); + } + + return new int[] {}; + } +} diff --git a/java/0002-add-two-numbers.java b/java/0002-add-two-numbers.java new file mode 100644 index 000000000..2432b3f4e --- /dev/null +++ b/java/0002-add-two-numbers.java @@ -0,0 +1,46 @@ +/* + Name - Add Two Numbers + Link - https://leetcode.com/problems/add-two-numbers/ + Time Complexity - O(m + n) + Space Complexity - o(1) + Note - make use of modulo (get remainder) and division (get quotient) +*/ + +class Solution { + + public ListNode addTwoNumbers(ListNode first, ListNode second) { + int q = 0; + int r = 0; + int sum = 0; + ListNode head = null; + ListNode temp = null; + while (first != null || second != null || q>0) { + sum = + q + + ( + ((first != null) ? first.val : 0) + + ((second != null) ? second.val : 0) + ); + r = sum % 10; + q = sum / 10; + ListNode newNode = new ListNode(r); + if (head == null) { + head = newNode; + } else { + temp = head; + while (temp.next != null) { + temp = temp.next; + } + temp.next = newNode; + newNode.next = null; + } + if (first != null) { + first = first.next; + } + if (second != null) { + second = second.next; + } + } + return head; + } +} diff --git a/java/0003-longest-substring-without-repeating-characters.java b/java/0003-longest-substring-without-repeating-characters.java new file mode 100644 index 000000000..a6d9f3f90 --- /dev/null +++ b/java/0003-longest-substring-without-repeating-characters.java @@ -0,0 +1,19 @@ +class Solution { + + public int lengthOfLongestSubstring(String s) { + List substringL = new ArrayList<>(); + int largestlength = 0; + for(int right = 0; right < s.length(); right++) { + if(substringL.contains(s.charAt(right))) { + // get the index of the char + int index = substringL.indexOf(s.charAt(right)); + substringL.remove(index); + if(index > 0) + substringL.subList(0, index).clear(); + } + substringL.add(s.charAt(right)); + largestlength = Math.max(largestlength, substringL.size()); + } + return largestlength; + } +} diff --git a/java/0004-median-of-two-sorted-arrays.java b/java/0004-median-of-two-sorted-arrays.java new file mode 100644 index 000000000..6ee680c01 --- /dev/null +++ b/java/0004-median-of-two-sorted-arrays.java @@ -0,0 +1,81 @@ +/*Brute-force solution (Linear)*/ +/* +// Runtime: O(m+n) +// Extra Space: O(m+n) +// +class Solution { + public double findMedianSortedArrays(int[] nums1, int[] nums2) { + int m = nums1.length; + int n = nums2.length; + int[] nums = new int[m+n]; + int i = 0, j = 0; + int k = 0; + while (i n) { + return findMedianSortedArrays(nums2, nums1); + } + + int total = m + n; + int half = (total + 1) / 2; + + int left = 0; + int right = m; + + var result = 0.0; + + while (left <= right) { + int i = left + (right - left) / 2; + int j = half - i; + + // get the four points around possible median + int left1 = (i > 0) ? nums1[i - 1] : Integer.MIN_VALUE; + int right1 = (i < m) ? nums1[i] : Integer.MAX_VALUE; + int left2 = (j > 0) ? nums2[j - 1] : Integer.MIN_VALUE; + int right2 = (j < n) ? nums2[j] : Integer.MAX_VALUE; + + // partition is correct + if (left1 <= right2 && left2 <= right1) { + // even + if (total % 2 == 0) { + result = + (Math.max(left1, left2) + Math.min(right1, right2)) / + 2.0; + // odd + } else { + result = Math.max(left1, left2); + } + break; + } + // partition is wrong (update left/right pointers) + else if (left1 > right2) { + right = i - 1; + } else { + left = i + 1; + } + } + + return result; + } +} diff --git a/java/0005-longest-palindromic-substring.java b/java/0005-longest-palindromic-substring.java new file mode 100644 index 000000000..df69c43f3 --- /dev/null +++ b/java/0005-longest-palindromic-substring.java @@ -0,0 +1,128 @@ +// Solution: Expanding Around Center + +// Time Complexity: O(n^2) +// Extra Space Complexity: O(1) + +class Solution1 { + + public String longestPalindrome(String s) { + int strLength = s.length(); + + if (strLength < 2) { + return s; + } + + int resultLength = 0; + String result = ""; + + for (int i = 0; i < s.length(); i++) { + //Odd length + int left = i, right = i; + while ( + left >= 0 && + right < s.length() && + s.charAt(left) == s.charAt(right) + ) { + if ((right - left + 1) > resultLength) { + result = s.substring(left, right + 1); + resultLength = right - left + 1; + } + left -= 1; + right += 1; + } + + // even length + left = i; + right = i + 1; + while ( + left >= 0 && + right < s.length() && + s.charAt(left) == s.charAt(right) + ) { + if ((right - left + 1) > resultLength) { + result = s.substring(left, right + 1); + resultLength = right - left + 1; + } + left -= 1; + right += 1; + } + } + + return result; + } +} + +// Solution: A more Optimized Expand Around Center + +// TIme Complexity: O(n^2) +// Extra Space Complexity: O(1) + +class Solution2 { + + public String longestPalindrome(String s) { + int best = 0; + int start = 0, end = 0; + + for (int i = 0; i < s.length(); i++) { + int left = i - 1; + while (i < s.length() - 1 && s.charAt(i) == s.charAt(i + 1)) { + ++i; + } + + int right = i + 1; + while ( + left >= 0 && + right < s.length() && + s.charAt(left) == s.charAt(right) + ) { + --left; + ++right; + } + + if (right - left > best) { + best = right - left; + start = left + 1; + end = right; + } + } + + return s.substring(start, end); + } +} + +// Solution: Dynamic Programming + +// Time Complexity: O(n^2) +// Extra Spce Complexity: O(n^2) + +class Solution3 { + + public String longestPalindrome(String s) { + int len = s.length(); + int left = 0, right = 1, max = 0; + + var isPalindrome = new boolean[len][len]; + + for (int i = len - 1; i >= 0; i--) { + for (int j = i; j < len; j++) { + if (i == j) { + isPalindrome[i][j] = true; + } else if (s.charAt(i) == s.charAt(j)) { + if (j - i == 1) { + isPalindrome[i][j] = true; + } else { + isPalindrome[i][j] = isPalindrome[i + 1][j - 1]; + } + } + + if (isPalindrome[i][j] && j - i + 1 > max) { + max = j - i + 1; + left = i; + right = j + 1; + } + } + } + + return s.substring(left, right); + } +} diff --git a/java/0006-zigzag-conversion.java b/java/0006-zigzag-conversion.java new file mode 100644 index 000000000..57e7d39e5 --- /dev/null +++ b/java/0006-zigzag-conversion.java @@ -0,0 +1,52 @@ +class Solution { + public String convert(String s, int numRows) { + if (numRows == 1) + return s; + + StringBuilder res = new StringBuilder(); + int increment = 2 * (numRows - 1); + + for (int r = 0; r < numRows; r++) { + for (int i = r; i < s.length(); i += increment) { + res.append(s.charAt(i)); + if (r > 0 && r < numRows - 1 && i + increment - 2 * r < s.length()) { + res.append(s.charAt(i + increment - 2 * r)); + } + } + } + return res.toString(); + } +} +-------------------------------------------------------------------------------------------------------------------------- +//We check whether we are at the diagonal or not using a boolean and increment the pointer accordingly. + +class Solution { + + public String convert(String s, int row) { + if (row == 1) return s; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < row; i++) { + int j = i; + if (i == 0 || i == row - 1) { + while (j < s.length()) { + sb.append(s.charAt(j)); + j += 2 * (row - 1); + } + } else { + boolean diagonal = false; + while (j < s.length()) { + if (!diagonal) { + sb.append(s.charAt(j)); + j += 2 * (row - i - 1); + diagonal = true; + } else { + sb.append(s.charAt(j)); + j += 2 * i; + diagonal = false; + } + } + } + } + return sb.toString(); + } +} diff --git a/java/0007-reverse-integer.java b/java/0007-reverse-integer.java new file mode 100644 index 000000000..f467e63ed --- /dev/null +++ b/java/0007-reverse-integer.java @@ -0,0 +1,19 @@ +class Solution { + + public int reverse(int x) { + boolean isNegative = x < 0; + + x = Math.abs(x); + + int num = 0; + + while (x > 0) { + if (Integer.MAX_VALUE / 10 < num) return 0; + + num = 10 * num + x % 10; + x /= 10; + } + + return isNegative ? -num : num; + } +} diff --git a/java/0009-palindrome-number.java b/java/0009-palindrome-number.java new file mode 100644 index 000000000..c4cd2fca6 --- /dev/null +++ b/java/0009-palindrome-number.java @@ -0,0 +1,16 @@ +class Solution { + public boolean isPalindrome(int x) { + if(x < 0) return false; + + long div = 1; + while(x >= 10 * div) + div *= 10; + + while(x > 0) { + if(x / div != x % 10) return false; + x = (int)((x % div) / 10); + div = div / 100; + } + return true; + } +} diff --git a/java/0010-regular-expression-matching.java b/java/0010-regular-expression-matching.java new file mode 100644 index 000000000..949a1cf31 --- /dev/null +++ b/java/0010-regular-expression-matching.java @@ -0,0 +1,30 @@ +class Solution { + + public boolean isMatch(String s, String p) { + boolean[][] cache = new boolean[s.length() + 1][p.length() + 1]; + + return dfs(cache, s, p, 0, 0); + } + + private boolean dfs(boolean[][] cache, String s, String p, int i, int j) { + if (cache[i][j] != false) return cache[i][j]; + + if (i >= s.length() && j >= p.length()) return true; + + if (j >= p.length()) return false; + + boolean match = + i < s.length() && + (s.charAt(i) == p.charAt(j) || p.charAt(j) == '.'); + + if (j + 1 < p.length() && p.charAt(j + 1) == '*') { + cache[i][j] = + dfs(cache, s, p, i, j + 2) || + (match && dfs(cache, s, p, i + 1, j)); + } else { + cache[i][j] = match && dfs(cache, s, p, i + 1, j + 1); + } + + return cache[i][j]; + } +} diff --git a/java/0011-container-with-most-water.java b/java/0011-container-with-most-water.java new file mode 100644 index 000000000..4d3ba5d75 --- /dev/null +++ b/java/0011-container-with-most-water.java @@ -0,0 +1,19 @@ +class Solution { + + public int maxArea(int[] height) { + int left = 0; + int right = height.length - 1; + int res = 0; + while (left < right) { + int containerLength = right - left; + int area = containerLength * Math.min(height[left], height[right]); + res = Math.max(res, area); + if (height[left] < height[right]) { + left++; + } else { + right--; + } + } + return res; + } +} diff --git a/java/0012-integer-to-roman.java b/java/0012-integer-to-roman.java new file mode 100644 index 000000000..9dd8168d4 --- /dev/null +++ b/java/0012-integer-to-roman.java @@ -0,0 +1,15 @@ +class Solution { + public String intToRoman(int num) { + int[] vals = new int[] {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}; + String[] symbols = new String[] {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}; + + StringBuilder res = new StringBuilder(); + for (int i = 0; i < vals.length && num >= 0; i++) { + while (num >= vals[i]) { + num -= vals[i]; + res.append(symbols[i]); + } + } + return res.toString(); + } +} \ No newline at end of file diff --git a/java/0013-roman-to-integer.java b/java/0013-roman-to-integer.java new file mode 100644 index 000000000..00d6b7a16 --- /dev/null +++ b/java/0013-roman-to-integer.java @@ -0,0 +1,22 @@ +class Solution { + public int romanToInt(String s) { + HashMap map = new HashMap(); + map.put('I', 1); + map.put('V' ,5); + map.put('X' ,10); + map.put('L' ,50); + map.put('C' ,100); + map.put('D' ,500); + map.put('M' ,1000); + + int result = 0; + for(int i=0; i < s.length(); i++){ + if (i > 0 && map.get(s.charAt(i)) > map.get(s.charAt(i-1))) { + result += map.get(s.charAt(i)) - 2*map.get(s.charAt(i-1)); + } else { + result += map.get(s.charAt(i)); + } + } + return result; +} +} diff --git a/java/0014-longest-common-prefix.java b/java/0014-longest-common-prefix.java new file mode 100644 index 000000000..57cb01b00 --- /dev/null +++ b/java/0014-longest-common-prefix.java @@ -0,0 +1,12 @@ +class Solution { + public String longestCommonPrefix(String[] strs) { + StringBuilder res = new StringBuilder(); + for(int i = 0; i < strs[0].length(); i++) { + for(String s: strs) + if(i == s.length() || s.charAt(i) != strs[0].charAt(i)) + return res.toString(); + res.append(strs[0].charAt(i)); + } + return res.toString(); + } +} diff --git a/java/0015-3sum.java b/java/0015-3sum.java new file mode 100644 index 000000000..725ba48cc --- /dev/null +++ b/java/0015-3sum.java @@ -0,0 +1,37 @@ +class Solution { + + //2 pointers + public List> threeSum(int[] nums) { + Arrays.sort(nums); + LinkedList> sol = new LinkedList>(); + + for (int i = 0; i < nums.length - 2; i++) { + //Only consider non-duplicate elements for i + if (i == 0 || (i > 0 && nums[i] != nums[i - 1])) { + int target = 0 - nums[i]; + int left = i + 1; + int right = nums.length - 1; + + while (left < right) { + if (nums[left] + nums[right] == target) { + sol.add(List.of(nums[i], nums[left], nums[right])); + while (left < right && nums[left] == nums[left + 1]) { + left++; + } + while (left < right && nums[right] == nums[right - 1]) { + right--; + } + left++; + right--; + } else if (nums[left] + nums[right] > target) { + right--; + } else { + left++; + } + } + } + } + + return sol; + } +} diff --git a/java/0017-letter-combinations-of-a-phone-number.java b/java/0017-letter-combinations-of-a-phone-number.java new file mode 100644 index 000000000..07cc797e6 --- /dev/null +++ b/java/0017-letter-combinations-of-a-phone-number.java @@ -0,0 +1,55 @@ +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +class Solution { + + private Map digitToChar = Map.of( + '2', + "abc", + '3', + "def", + '4', + "ghi", + '5', + "jkl", + '6', + "mno", + '7', + "pqrs", + '8', + "tuv", + '9', + "wxyz" + ); + + public List letterCombinations(String digits) { + if (digits.length() == 0) { + return new ArrayList(); + } + + List ans = new ArrayList(); + String cur = ""; + backtrack(digits, ans, cur, 0); + return ans; + } + + public void backtrack( + String digits, + List ans, + String cur, + int index + ) { + if (cur.length() == digits.length()) { + ans.add(cur); + return; + } else if (index >= digits.length()) { + return; + } else { + String digit = digitToChar.get(digits.charAt(index)); + for (char c : digit.toCharArray()) { + backtrack(digits, ans, cur + c, index + 1); + } + } + } +} diff --git a/java/0018-4sum.java b/java/0018-4sum.java new file mode 100644 index 000000000..c5e7b6a15 --- /dev/null +++ b/java/0018-4sum.java @@ -0,0 +1,33 @@ +class Solution { + public static List> fourSum(int[] nums, int target) { + List> res = new ArrayList<>(); + Arrays.sort(nums); + + for (int a = 0; a < nums.length-3; a++) { + if (a > 0 && nums[a] == nums[a - 1]) { + continue; + } + for (int i = a + 1; i < nums.length - 2; i++) { + if (i > 1 && nums[i] == nums[i - 1] && i-1 != a) { + continue; + } + int j = i + 1; + int k = nums.length - 1; + while (k > j) { + long sum = (long) nums[i] + nums[j] + nums[k] + nums[a]; + if (sum == target) { + res.add(new ArrayList<>(Arrays.asList(nums[a],nums[i], nums[j], nums[k]))); + j++; + while (nums[j] == nums[j - 1] && j < k) { + j++; + } + } else if (sum > target) { + k--; + } else + j++; + } + } + } + return res; + } +} diff --git a/java/0019-remove-nth-node-from-end-of-list.java b/java/0019-remove-nth-node-from-end-of-list.java new file mode 100644 index 000000000..79ff235d0 --- /dev/null +++ b/java/0019-remove-nth-node-from-end-of-list.java @@ -0,0 +1,40 @@ +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +// Using Two Pointer Approach: +// Take a pointer second and put it at (n+1)th position from the beginning +// Take pointer first and move it forward till second reaches Last Node and second.next points to null +// At that point we would have reached the (n-1)th node from the end using the pointer first +// Unlink or Skip that node + +class Solution { + + public ListNode removeNthFromEnd(ListNode head, int n) { + if (head == null || head.next == null) return null; + + ListNode temp = new ListNode(0); + temp.next = head; + ListNode first = temp, second = temp; + + while (n > 0) { + second = second.next; + n--; + } + + while (second.next != null) { + second = second.next; + first = first.next; + } + + first.next = first.next.next; + return temp.next; + } +} diff --git a/java/0020-valid-parentheses.java b/java/0020-valid-parentheses.java new file mode 100644 index 000000000..106c0006d --- /dev/null +++ b/java/0020-valid-parentheses.java @@ -0,0 +1,50 @@ +class Solution { + + public boolean isValid(String s) { + if (s.length() % 2 != 0) return false; + Stack stack = new Stack<>(); + for (int i = 0; i < s.length(); i++) { + if ( + stack.isEmpty() && + (s.charAt(i) == ')' || s.charAt(i) == '}' || s.charAt(i) == ']') + ) return false; else { + if ( + s.charAt(i) == ')' && stack.peek() == '(' + ) stack.pop(); else if ( + s.charAt(i) == '}' && stack.peek() == '{' + ) stack.pop(); else if ( + s.charAt(i) == ']' && stack.peek() == '[' + ) stack.pop(); else stack.add(s.charAt(i)); + } + } + return stack.isEmpty(); + } +} + +//Solution with HashMap Lookup table as described in the video + +class Solution { + public boolean isValid(String s) { + Stack brackets = new Stack<>(); + Map bracketLookup = new HashMap<>(3); + + bracketLookup.put(')', '('); + bracketLookup.put('}', '{'); + bracketLookup.put(']', '['); + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (bracketLookup.containsKey(c)) { + if (!brackets.isEmpty() && bracketLookup.get(c).equals(brackets.peek())) { + brackets.pop(); + } else { + return false; + } + } else { + brackets.push(c); + } + } + + return brackets.isEmpty(); + } +} diff --git a/java/0021-merge-two-sorted-lists.java b/java/0021-merge-two-sorted-lists.java new file mode 100644 index 000000000..6d9d17c82 --- /dev/null +++ b/java/0021-merge-two-sorted-lists.java @@ -0,0 +1,58 @@ +package java; + +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +class Solution { + + public ListNode mergeTwoLists(ListNode list1, ListNode list2) { + final ListNode root = new ListNode(); + ListNode prev = root; + while (list1 != null && list2 != null) { + if (list1.val < list2.val) { + prev.next = list1; + list1 = list1.next; + } else { + prev.next = list2; + list2 = list2.next; + } + prev = prev.next; + } + prev.next = list1 != null ? list1 : list2; + return root.next; + } +} + +// Solution using Recursion +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +class Solution { + + public ListNode mergeTwoLists(ListNode list1, ListNode list2) { + if (list1 == null) return list2; + if (list2 == null) return list1; + + if (list1.val < list2.val) { + list1.next = mergeTwoLists(list1.next, list2); + return list1; + } else { + list2.next = mergeTwoLists(list2.next, list1); + return list2; + } + } +} diff --git a/java/0022-generate-parentheses.java b/java/0022-generate-parentheses.java new file mode 100644 index 000000000..57f10bc04 --- /dev/null +++ b/java/0022-generate-parentheses.java @@ -0,0 +1,42 @@ +package arrays; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Stack; + +public class Solution { + + public static void main(String[] args) { + Solution sol = new Solution(); + sol.generateParenthesis(3); + } + + Stack stack = new Stack<>(); + List res = new ArrayList<>(); + + public List generateParenthesis(int n) { + backtrack(0, 0, n); + return res; + } + + private void backtrack(int openN, int closedN, int n) { + if (openN == closedN && closedN == n) { + StringBuilder sb = new StringBuilder(); + for (Character c: stack) { + sb.append(c); + } + res.add(sb.toString()); + } + if (openN < n) { + stack.push('('); + backtrack(openN + 1, closedN, n); + stack.pop(); + } + if (closedN < openN) { + stack.push(')'); + backtrack(openN, closedN + 1, n); + stack.pop(); + } + } +} diff --git a/java/0023-merge-k-sorted-lists.java b/java/0023-merge-k-sorted-lists.java new file mode 100644 index 000000000..699ad0a1e --- /dev/null +++ b/java/0023-merge-k-sorted-lists.java @@ -0,0 +1,81 @@ +// Solution using Min Heap +// Time Complexity: O(n*log(k)) +// Extra Space Complexity: O(k) + +class Solution1 { + + public ListNode mergeKLists(ListNode[] lists) { + if (lists == null || lists.length == 0) { + return null; + } + + PriorityQueue queue = new PriorityQueue<>((a, b) -> a.val - b.val); + for (ListNode node : lists) { + if (node != null) { + queue.offer(node); + } + } + + ListNode dummy = new ListNode(0); + ListNode current = dummy; + + while (!queue.isEmpty()) { + ListNode node = queue.poll(); + current.next = node; + current = current.next; + + if (node.next != null) { + queue.offer(node.next); + } + } + + return dummy.next; + } +} + +// Solution using Iterative Merge Sort +// Time Complexity: O(n*log(k)) +// Extra Space Complexity: O(1) + +class Solution2 { + + public ListNode mergeKLists(ListNode[] lists) { + int size = lists.length; + int interval = 1; + + while (interval < size) { + for (int i = 0; i < size - interval; i += 2 * interval) { + lists[i] = merge(lists[i], lists[i + interval]); + } + + interval *= 2; + } + + return size > 0 ? lists[0] : null; + } + + private ListNode merge(ListNode l1, ListNode l2) { + ListNode dummy = new ListNode(0); + ListNode curr = dummy; + + while (l1 != null && l2 != null) { + if (l1.val <= l2.val) { + curr.next = l1; + l1 = l1.next; + } else { + curr.next = l2; + l2 = l2.next; + } + + curr = curr.next; + } + + if (l1 != null) { + curr.next = l1; + } else { + curr.next = l2; + } + + return dummy.next; + } +} diff --git a/java/0024-swap-nodes-in-pairs.java b/java/0024-swap-nodes-in-pairs.java new file mode 100644 index 000000000..f3188ee9d --- /dev/null +++ b/java/0024-swap-nodes-in-pairs.java @@ -0,0 +1,31 @@ +//Iterative version +class Solution { + + public ListNode swapPairs(ListNode head) { + if (head == null || head.next == null) return head; + ListNode dummy = new ListNode(0); + dummy.next = head; + ListNode temp = dummy; + while (temp.next != null && temp.next.next != null) { + ListNode first = temp.next; + ListNode second = temp.next.next; + temp.next = second; + first.next = second.next; + second.next = first; + temp = first; + } + return dummy.next; + } +} + +//Recursive version +class Solution { + + public ListNode swapPairs(ListNode head) { + if (head == null || head.next == null) return head; + ListNode p = head.next; + head.next = swapPairs(head.next.next); + p.next = head; + return p; + } +} diff --git a/java/0025-reverse-nodes-in-k-group.java b/java/0025-reverse-nodes-in-k-group.java new file mode 100644 index 000000000..1be3b6f83 --- /dev/null +++ b/java/0025-reverse-nodes-in-k-group.java @@ -0,0 +1,81 @@ +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ + +//This recursive approach is super intuitive taken from leetcode discuss. + +// Time Complexity: O(n) +// Extra Space Complexity: O(n) + +class Solution1 { + + public ListNode reverseKGroup(ListNode head, int k) { + ListNode cur = head; + int count = 0; + while (cur != null && count != k) { + cur = cur.next; + count++; + } + if (count == k) { + cur = reverseKGroup(cur, k); + while (count-- > 0) { + ListNode temp = head.next; + head.next = cur; + cur = head; + head = temp; + } + head = cur; + } + return head; + } +} + +// An iterative approach + +// Time Complexity: O(n) +// Extra Space Complexity: O(1) + +class Solution2 { + + public ListNode reverseKGroup(ListNode head, int k) { + ListNode dummy = new ListNode(0, head); + ListNode curr = head; + ListNode prev = dummy; + ListNode temp = null; + + int count = k; + + while (curr != null) { + if (count > 1) { + temp = prev.next; + prev.next = curr.next; + curr.next = curr.next.next; + prev.next.next = temp; + + count--; + } else { + prev = curr; + curr = curr.next; + + ListNode end = curr; + + for (int i = 0; i < k; ++i) { + if (end == null) { + return dummy.next; + } + end = end.next; + } + count = k; + } + } + + return dummy.next; + } +} diff --git a/java/0026-remove-duplicates-from-sorted-array.java b/java/0026-remove-duplicates-from-sorted-array.java new file mode 100644 index 000000000..48aaa3891 --- /dev/null +++ b/java/0026-remove-duplicates-from-sorted-array.java @@ -0,0 +1,14 @@ +class Solution { + public int removeDuplicates(int[] nums) { + int swap = 1; + + for(int i=1; i 0) return -1; + if (haystack.length() != 0) { + int occurence = haystack.indexOf(needle); + if (occurence == -1) + return occurence; + result = occurence; + } + return result; + } +} diff --git a/java/0033-search-in-rotated-sorted-array.java b/java/0033-search-in-rotated-sorted-array.java new file mode 100644 index 000000000..cb22e21ea --- /dev/null +++ b/java/0033-search-in-rotated-sorted-array.java @@ -0,0 +1,33 @@ +class Solution { + public int search(int[] nums, int target) { + + int l = 0; + int r = nums.length - 1; + + while(l<=r){ + + int mid = (l+r)/2; + + if(nums[mid] == target){ + return mid; + } + //left sorted + if(nums[l]<=nums[mid]){ + if(target > nums[mid] || target < nums[l]){ + l = mid + 1; + }else{ + r = mid - 1; + } + }else{//right sorted + if(target < nums[mid] || target > nums [r]){ + r = mid - 1; + }else{ + l = mid + 1; + } + } + + } + + return -1; + } +} diff --git a/java/0034-find-first-and-last-position-of-element-in-sorted-array.java b/java/0034-find-first-and-last-position-of-element-in-sorted-array.java new file mode 100644 index 000000000..b990db9f6 --- /dev/null +++ b/java/0034-find-first-and-last-position-of-element-in-sorted-array.java @@ -0,0 +1,44 @@ +class Solution { + public int[] searchRange(int[] nums, int target) { + int left = 0; + int right = nums.length - 1; + int[] result = {-1, -1}; + + while (left <= right) { + int mid = left + ((right - left) / 2); + + if (nums[mid] == target) { + result = findEdges(mid, nums, target); + break; + } + + else if (target < nums[mid]) { + right = mid - 1; + } + + else if (target > nums[mid]) { + left = mid + 1; + } + } + + return result; + } + + public int[] findEdges(int mid, int[] nums, int target) { + int left = mid; + int right = mid; + int[] result = {left, right}; + + while (left >= 0 && nums[left] == target) { + result[0] = left; + left --; + } + + while (right <= nums.length - 1 && nums[right] == target) { + result[1] = right; + right ++; + } + + return result; + } +} \ No newline at end of file diff --git a/java/0035-search-insert-position.java b/java/0035-search-insert-position.java new file mode 100644 index 000000000..94e41fcf1 --- /dev/null +++ b/java/0035-search-insert-position.java @@ -0,0 +1,14 @@ +class Solution { + public int searchInsert(int[] nums, int target) { + //o(log n) and o(1) + int low = 0, high = nums.length; + while(low < high) { + int mid = low + (high - low)/2; + if(target > nums[mid]) + low = mid + 1; + else + high = mid; + } + return low; + } +} diff --git a/java/0036-valid-sudoku.java b/java/0036-valid-sudoku.java new file mode 100644 index 000000000..dbb4f6e9b --- /dev/null +++ b/java/0036-valid-sudoku.java @@ -0,0 +1,73 @@ +class Solution { + + public boolean isValidSudoku(char[][] board) { + //neetcode solution, slightly modified + + //a set of the characters that we have already come across (excluding '.' which denotes an empty space) + Set rowSet = null; + Set colSet = null; + + + for (int i = 0; i < 9; i++) { + //reinitialize the sets so we don't carry over found characters from the previous run + rowSet = new HashSet<>(); + colSet = new HashSet<>(); + for (int j = 0; j < 9; j++) { + char r = board[i][j]; + char c = board[j][i]; + if (r != '.'){ + if (rowSet.contains(r)){ + return false; + } else { + rowSet.add(r); + } + } + if (c != '.'){ + if (colSet.contains(c)){ + return false; + } else { + colSet.add(c); + } + } + } + } + + //block + //loop controls advance by 3 each time to jump through the boxes + for (int i = 0; i < 9; i = i + 3) { + for (int j = 0; j < 9; j = j + 3) { + //checkBlock will return true if valid + if (!checkBlock(i, j, board)) { + return false; + } + } + } + //passed all tests, therefore valid board + return true; + } + + public boolean checkBlock(int idxI, int idxJ, char[][] board) { + Set blockSet = new HashSet<>(); + //if idxI = 3 and indJ = 0 + //rows = 6 and cols = 3 + int rows = idxI + 3; + int cols = idxJ + 3; + //and because i initializes to idxI but only goes to rows, we loop 3 times (once for each row) + for (int i = idxI; i < rows; i++) { + //same for columns + for (int j = idxJ; j < cols; j++) { + if (board[i][j] == '.') { + continue; + } + + if (blockSet.contains(board[i][j])) { + return false; + } + + blockSet.add(board[i][j]); + } + } + + return true; + } +} diff --git a/java/0037-sudoku-solver.java b/java/0037-sudoku-solver.java new file mode 100644 index 000000000..e566b56b8 --- /dev/null +++ b/java/0037-sudoku-solver.java @@ -0,0 +1,56 @@ +//this problem is easier than it looks +//mix of search a 2d matrix and valid sudoku + +class Solution { + + public void solveSudoku(char[][] board) { + solve(board); + } + + public boolean solve(char[][] board) { + //traverse the complete sudoku and look for empty value + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 9; j++) { + //look for '.' (empty block) + if (board[i][j] == '.') { + //try filling all values + for (char c = '1'; c <= '9'; c++) { + //check if we can put that value at that place + //we will have a function for this which will check all the + //three conditions for a valid sudoku + if (isValid(board, i, j, c)) { + board[i][j] = c; + //check whether the new board is solvable and just return true if it is + //so, we can just have only one sudoku combination. + //else backtrack, i.e., make it '.' again. + if (solve(board)) return true; else board[i][j] = + '.'; + } + } + //if the board[i][j] is empty and we checked all values and + //nothing from '0' to '9' can be added then return false + //as this is unsolvable + return false; + } + } + } + return true; + } + + public boolean isValid(char[][] board, int row, int col, char c) { + for (int i = 0; i < 9; i++) { + //row check + if (board[row][i] == c) return false; + //col check + if (board[i][col] == c) return false; + //boxes check + //for this draw a sudoku and see by marking boxes and choosing a random postion + //this is hard to explain but try this one + //you'll get to the formula by yourself + if ( + board[3 * (row / 3) + i / 3][3 * (col / 3) + i % 3] == c + ) return false; + } + return true; + } +} diff --git a/java/0039-combination-sum.java b/java/0039-combination-sum.java new file mode 100644 index 000000000..e33fc50e8 --- /dev/null +++ b/java/0039-combination-sum.java @@ -0,0 +1,29 @@ +class Solution { + + public List> combinationSum(int[] candidates, int target) { + List> ans = new ArrayList>(); + List cur = new ArrayList(); + backtrack(candidates, target, ans, cur, 0); + return ans; + } + + public void backtrack( + int[] candidates, + int target, + List> ans, + List cur, + int index + ) { + if (target == 0) { + ans.add(new ArrayList(cur)); + } else if (target < 0 || index >= candidates.length) { + return; + } else { + cur.add(candidates[index]); + backtrack(candidates, target - candidates[index], ans, cur, index); + + cur.remove(cur.get(cur.size() - 1)); + backtrack(candidates, target, ans, cur, index + 1); + } + } +} diff --git a/java/0040-combination-sum-ii.java b/java/0040-combination-sum-ii.java new file mode 100644 index 000000000..c773f67ab --- /dev/null +++ b/java/0040-combination-sum-ii.java @@ -0,0 +1,29 @@ +class Solution { + + public List> combinationSum2(int[] candidates, int target) { + Arrays.sort(candidates); + List> ans = new ArrayList>(); + List ls = new ArrayList(); + comb(candidates, target, ans, ls, 0); + return ans; + } + + public void comb( + int[] candidates, + int target, + List> ans, + List ls, + int index + ) { + if (target == 0) { + ans.add(new ArrayList(ls)); + } else if (target < 0) return; else { + for (int i = index; i < candidates.length; i++) { + if (i > index && candidates[i] == candidates[i - 1]) continue; + ls.add(candidates[i]); + comb(candidates, target - candidates[i], ans, ls, i + 1); + ls.remove(ls.get(ls.size() - 1)); + } + } + } +} diff --git a/java/0041-first-missing-positive.java b/java/0041-first-missing-positive.java new file mode 100644 index 000000000..5a8d59580 --- /dev/null +++ b/java/0041-first-missing-positive.java @@ -0,0 +1,31 @@ +class Solution { + public int firstMissingPositive(int[] nums) { + int n = nums.length, size = 0; + + while(n > 0){ + n = n>>1; + size++; + } + n = nums.length; + int pivot = 0; + + + for(int i = 0; i < n; i++){ + if(nums[i] <= 0 || nums[i] > n){ + int temp = nums[i]; + nums[i] = nums[pivot]; + nums[pivot] = temp; + pivot++; + } + } + for(int i= 0; i < pivot; i++) + nums[i] = 0; + for(int i= pivot; i < n; i++){ + nums[(nums[i] - 1)&((1<= 0; i--) { + right[i] = Math.max(heights[i], max); + max = right[i]; + } + + for (int i = 0; i < heights.length; i++) { + c = c + Math.min(left[i], right[i]) - heights[i]; + } + return c; + } +} + +//O(1) space +class Solution { + + public int trap(int[] heights) { + if (heights.length == 0) return 0; + + int l = 0, r = heights.length - 1; + int leftMax = heights[l], rightMax = heights[r]; + int res = 0; + + while (l < r) { + if (leftMax < rightMax) { + l++; + leftMax = Math.max(leftMax, heights[l]); + res += leftMax - heights[l]; + } else { + r--; + rightMax = Math.max(rightMax, heights[r]); + res += rightMax - heights[r]; + } + } + + return res; + } +} diff --git a/java/0043-multiply-strings.java b/java/0043-multiply-strings.java new file mode 100644 index 000000000..9ea407384 --- /dev/null +++ b/java/0043-multiply-strings.java @@ -0,0 +1,56 @@ +class Solution { + + public String multiply(String num1, String num2) { + if ("0".equals(num1) || "0".equals(num2)) { + return "0"; + } + + int[] res = new int[num1.length() + num2.length()]; + + num1 = reverseString(num1); + num2 = reverseString(num2); + + for (int i = 0; i < num1.length(); i++) { + for (int j = 0; j < num2.length(); j++) { + int digit = + Integer.valueOf(String.valueOf(num1.charAt(i))) * + Integer.valueOf(String.valueOf(num2.charAt(j))); + res[i + j] += digit; + res[i + j + 1] += res[i + j] / 10; + res[i + j] = res[i + j] % 10; + } + } + + reverseArrayInPlace(res); + + // Get the proper starting point to avoid leading zeros + int startIndex = 0; + while (startIndex < res.length) { + if (res[startIndex] != 0) { + break; + } + startIndex++; + } + + StringBuilder buildResponse = new StringBuilder(); + for (int i = startIndex; i < res.length; i++) { + buildResponse.append(res[i]); + } + + return buildResponse.toString(); + } + + private String reverseString(String str) { + StringBuilder reversedStr = new StringBuilder(str); + reversedStr.reverse(); + return reversedStr.toString(); + } + + private void reverseArrayInPlace(int[] arr) { + for (int i = 0; i < arr.length / 2; i++) { + int temp = arr[i]; + arr[i] = arr[arr.length - i - 1]; + arr[arr.length - i - 1] = temp; + } + } +} diff --git a/java/0045-jump-game-ii.java b/java/0045-jump-game-ii.java new file mode 100644 index 000000000..664dfbe7d --- /dev/null +++ b/java/0045-jump-game-ii.java @@ -0,0 +1,15 @@ +class Solution { + + public int jump(int[] nums) { + int res = 0, r = 0, l = 0, fur = 0; + + while (r < nums.length - 1) { + fur = 0; + for (int i = l; i <= r; i++) fur = Math.max(fur, i + nums[i]); + l = r + 1; + r = fur; + res++; + } + return res; + } +} diff --git a/java/0046-permutations.java b/java/0046-permutations.java new file mode 100644 index 000000000..0e5d5e79f --- /dev/null +++ b/java/0046-permutations.java @@ -0,0 +1,32 @@ +import java.util.ArrayList; +import java.util.Arrays; + +class Solution { + + public List> permute(int[] nums) { + List> ans = new ArrayList<>(); + function(ans, nums, 0); + return ans; + } + + public void function(List> ans, int[] arr, int start) { + if (start == arr.length) { + List list = new ArrayList(); + for (int i = 0; i < arr.length; i++) list.add(arr[i]); + ans.add(list); + return; + } + + for (int i = start; i < arr.length; i++) { + swap(arr, start, i); + function(ans, arr, start + 1); + swap(arr, start, i); + } + } + + public void swap(int[] arr, int a, int b) { + int temp = arr[a]; + arr[a] = arr[b]; + arr[b] = temp; + } +} diff --git a/java/0047-permutations-ii.java b/java/0047-permutations-ii.java new file mode 100644 index 000000000..1c1e84571 --- /dev/null +++ b/java/0047-permutations-ii.java @@ -0,0 +1,33 @@ +class Solution { + public List> permuteUnique(int[] nums) { + Arrays.sort(nums); + List> res = new ArrayList<>(); + + backtrack(res, nums, new ArrayList<>(), new boolean[nums.length]); + return res; + } + + public void backtrack(List> res, + int[] nums, + List path, + boolean[] visited) { + if (path.size() == nums.length) { + res.add(new ArrayList<>(path)); + return; + } + + for (int i = 0; i < nums.length; i++) { + if (visited[i] || + (i > 0 && nums[i - 1] == nums[i] && visited[i - 1])) + continue; + + visited[i] = true; + path.add(nums[i]); + + backtrack(res, nums, path, visited); + + visited[i] = false; + path.remove(path.size() - 1); + } + } +} diff --git a/java/0048-rotate-image.java b/java/0048-rotate-image.java new file mode 100644 index 000000000..a5efc4c67 --- /dev/null +++ b/java/0048-rotate-image.java @@ -0,0 +1,33 @@ +class Solution { + public void rotate(int[][] matrix) { + int l = 0; + int r = matrix.length - 1; + + while ( l < r ) + { + for(int i = 0; i < r - l; i++) + { + int top = l; + int bottom = r; + //save the topleft + int topLeft = matrix[top][l + i]; + + //move bottom left into top left + matrix[top][l + i] = matrix[bottom - i][l]; + + // move bottom right into bottom left + matrix[bottom - i][l] = matrix[bottom][r - i]; + + // move top right into bottom right + matrix[bottom][r - i] = matrix[top + i][r]; + + // move top left into top right + matrix[top + i][r] = topLeft; + + } + + r -= 1; + l += 1; + } + } +} diff --git a/java/0049-group-anagrams.java b/java/0049-group-anagrams.java new file mode 100644 index 000000000..60f1904c1 --- /dev/null +++ b/java/0049-group-anagrams.java @@ -0,0 +1,47 @@ +class Solution { + public List> groupAnagrams(String[] strs) { + Map> res = new HashMap<>(); + + for (String s : strs) { + int[] count = new int[26]; + + for (char c : s.toCharArray()) { + count[c - 'a']++; + } + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 26; i++) { + sb.append('#'); + sb.append(count[i]); + } + String key = sb.toString(); + + if (!res.containsKey(key)) { + res.put(key, new ArrayList<>()); + } + res.get(key).add(s); + } + + return new ArrayList<>(res.values()); + } +} +------------------------------------------------------------------------- +class Solution { + public List> groupAnagrams(String[] strs) { + Map> map = new HashMap<>(); + + for (String word : strs) { + char[] chars = word.toCharArray(); + Arrays.sort(chars); + String sortedWord = new String(chars); + + if (!map.containsKey(sortedWord)) { + map.put(sortedWord, new ArrayList<>()); + } + + map.get(sortedWord).add(word); + } + + return new ArrayList<>(map.values()); + } +} diff --git a/java/0050-powx-n.java b/java/0050-powx-n.java new file mode 100644 index 000000000..987dfaf60 --- /dev/null +++ b/java/0050-powx-n.java @@ -0,0 +1,31 @@ +//Instead of using the classic recursive approach i.e. x*pow(x, n-1) just have (x*x), i.e., pow(x*x, n/2). +//This will make the TC logarithmic instead of linear. +//Just take care of the edge cases like Integer.MIN_VALUE, negative power, odd cases. +//Asked in Amazon, Meta, Google, Linkedin, Bloomberg +class Solution { + + public double myPow(double x, int n) { + if (n == 0) { + return 1; + } + //Make the negative values positive + else if (n < 0) { + //whenever even just divide it by 2 + //this will also include Integer.MIN_VALUE + //We're doing this because if I do -N and N=Integer.MIN_VALUE it'll become a value which is greater than the max value of Integer.MAX_VALUE + if (n % 2 == 0) { + n = n / 2; + n = -n; + x = (1 / x) * (1 / x); + } else { //Odds don't need to be divided as their negative is in the positive limit + n = -n; + x = 1 / x; + } + } + if (n % 2 == 0) { //even + return myPow(x * x, n / 2); + } else { //odd + return x * myPow(x * x, n / 2); + } + } +} diff --git a/java/0051-n-queens.java b/java/0051-n-queens.java new file mode 100644 index 000000000..41729643f --- /dev/null +++ b/java/0051-n-queens.java @@ -0,0 +1,126 @@ +// Solution 1: +class Solution { + + public List> solveNQueens(int n) { + List> ans = new ArrayList>(); + boolean[][] board = new boolean[n][n]; + queens(board, 0, ans); + return ans; + } + + public void queens(boolean[][] board, int row, List> ans2) { + //base case + if (row == board.length) { + ArrayList ans = new ArrayList(); + createAnswer(board, ans); + ans2.add(ans); + return; + } + for (int col = 0; col < board.length; col++) { + if (isSafe(board, row, col)) { + board[row][col] = true; + queens(board, row + 1, ans2); + board[row][col] = false; + } + } + } + + public void createAnswer(boolean[][] board, ArrayList ans) { + for (int i = 0; i < board.length; i++) { + StringBuilder str = new StringBuilder(); + for (int j = 0; j < board[0].length; j++) { + if (board[i][j]) { + str.append("Q"); + } else str.append("."); + } + ans.add(str.toString()); + } + } + + public boolean isSafe(boolean[][] board, int row, int col) { + for (int i = 0; i < row; i++) { + if (board[i][col]) { + return false; + } + } + int maxLeft = Math.min(row, col); + for (int i = 1; i <= maxLeft; i++) { + if (board[row - i][col - i]) { + return false; + } + } + int maxRight = Math.min(row, board.length - 1 - col); + for (int i = 1; i <= maxRight; i++) { + if (board[row - i][col + i]) return false; + } + return true; + } +} + + + +// Solution 2: +/* + * This solution uses 3 hashsets to check whether the current queen has conflicts with previous + * columns and two diagonals, avoiding the use of 2D boolean array and the isSafe checking function. + * + */ +class Solution { + public List> solveNQueens(int n) { + List> result = new ArrayList<>(); + List cur = new ArrayList<>(); + if (n <= 0) { + return result; + } + Set leftSet = new HashSet<>(); // diag \ row - col + Set rightSet = new HashSet<>(); // diag / row + col + Set colSet = new HashSet<>(); // column | col + dfs(n, result, cur, leftSet, rightSet, colSet); + return result; + } + + private void dfs(int n, List> result, List cur, Set leftSet, + Set rightSet, Set colSet) { + if (cur.size() == n) { + result.add(new ArrayList(cur)); + return; + } + int row = cur.size(); + // i is column index + for (int i = 0; i < n; i++) { + if (leftSet.contains(row - i) || rightSet.contains(row + i) || colSet.contains(i)) { + continue; + } + // current col index is added to the solution list cur + cur.add(convert(n, i)); + leftSet.add(row - i); + rightSet.add(row + i); + colSet.add(i); + // go to dfs next level + dfs(n, result, cur, leftSet, rightSet, colSet); + // backtracking + cur.remove(cur.size() - 1); + leftSet.remove(row - i); + rightSet.remove(row + i); + colSet.remove(i); + + } + } + + private String convert(int n, int col) { + StringBuilder res = new StringBuilder(); + for (int i = 0; i < n; i++) { + if (i == col) { + res.append("Q"); + } else { + res.append("."); + } + } + return res.toString(); + } +} + + + + + diff --git a/java/0052-n-queens-ii.java b/java/0052-n-queens-ii.java new file mode 100644 index 000000000..35bf70f50 --- /dev/null +++ b/java/0052-n-queens-ii.java @@ -0,0 +1,38 @@ +class Solution { + int count = 0; + + public int totalNQueens(int n) { + Set colSet = new HashSet<>(); + Set posDiagSet = new HashSet<>(); // (r + c) + Set negDiagSet = new HashSet<>(); // (r - c) + backtrack(0, n, colSet, posDiagSet, negDiagSet); + return count; + } + + private void backtrack( + int row, + int n, + Set colSet, + Set posDiagSet, + Set negDiagSet) { + if (row == n) { + count += 1; + return; + } + + for (int col = 0; col < n; col++) { + if (colSet.contains(col) + || posDiagSet.contains(row + col) + || negDiagSet.contains(row - col)) { + continue; + } + colSet.add(col); + posDiagSet.add(row + col); + negDiagSet.add(row - col); + backtrack(row + 1, n, colSet, posDiagSet, negDiagSet); + colSet.remove(col); + posDiagSet.remove(row + col); + negDiagSet.remove(row - col); + } + } +} diff --git a/java/0053-maximum-subarray.java b/java/0053-maximum-subarray.java new file mode 100644 index 000000000..e9d27b8cc --- /dev/null +++ b/java/0053-maximum-subarray.java @@ -0,0 +1,19 @@ +class Solution { + + public int maxSubArray(int[] nums) { + if (nums.length == 1) return nums[0]; + + int sum = 0; + int max = Integer.MIN_VALUE; + + for (int n : nums) { + sum += n; + max = Math.max(max, sum); + + if (sum < 0) { + sum = 0; + } + } + return max; + } +} diff --git a/java/0054-spiral-matrix.java b/java/0054-spiral-matrix.java new file mode 100644 index 000000000..6497eb804 --- /dev/null +++ b/java/0054-spiral-matrix.java @@ -0,0 +1,38 @@ +class Solution { + + public List spiralOrder(int[][] matrix) { + List list = new ArrayList<>(); + int rb = 0; + int re = matrix.length - 1; + int cb = 0; + int ce = matrix[0].length - 1; + + while (rb <= re && cb <= ce) { + for (int j = cb; j <= ce; j++) { + list.add(matrix[rb][j]); + } + rb++; + + for (int i = rb; i <= re; i++) { + list.add(matrix[i][ce]); + } + ce--; + + if (rb <= re) { + for (int j = ce; j >= cb; j--) { + list.add(matrix[re][j]); + } + } + re--; + + if (cb <= ce) { + for (int i = re; i >= rb; i--) { + list.add(matrix[i][cb]); + } + } + cb++; + } + + return list; + } +} diff --git a/java/0055-jump-game.java b/java/0055-jump-game.java new file mode 100644 index 000000000..f19449bf4 --- /dev/null +++ b/java/0055-jump-game.java @@ -0,0 +1,12 @@ +class Solution { + + public boolean canJump(int[] nums) { + int goal = nums.length - 1; + for (int i = nums.length - 2; i >= 0; i--) { + if (nums[i] + i >= goal) { + goal = i; + } + } + return goal == 0; + } +} diff --git a/java/0056-merge-intervals.java b/java/0056-merge-intervals.java new file mode 100644 index 000000000..3821e119a --- /dev/null +++ b/java/0056-merge-intervals.java @@ -0,0 +1,22 @@ +class Solution { + + public int[][] merge(int[][] intervals) { + ArrayList ans = new ArrayList<>(); + Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0])); + ans.add(intervals[0]); + for (int i = 1; i < intervals.length; i++) { + //comparing the values of prevEnd and curStart + int curStart = intervals[i][0]; + if (curStart <= ans.get(ans.size() - 1)[1]) { + //do the merging + ans.get(ans.size() - 1)[1] = + Math.max(ans.get(ans.size() - 1)[1], intervals[i][1]); + } else { + ans.add(intervals[i]); + } + } + int[][] res = new int[ans.size()][2]; + ans.toArray(res); + return res; + } +} diff --git a/java/0057-insert-interval.java b/java/0057-insert-interval.java new file mode 100644 index 000000000..1920b3e2e --- /dev/null +++ b/java/0057-insert-interval.java @@ -0,0 +1,20 @@ +class Solution { + + public int[][] insert(int[][] intervals, int[] newInterval) { + List res = new ArrayList<>(); + for (int[] interval : intervals) { + if (newInterval == null || interval[1] < newInterval[0]) res.add( + interval + ); else if (interval[0] > newInterval[1]) { + res.add(newInterval); + res.add(interval); + newInterval = null; + } else { + newInterval[0] = Math.min(interval[0], newInterval[0]); + newInterval[1] = Math.max(interval[1], newInterval[1]); + } + } + if (newInterval != null) res.add(newInterval); + return res.toArray(new int[res.size()][]); + } +} diff --git a/java/0058-length-of-last-word.java b/java/0058-length-of-last-word.java new file mode 100644 index 000000000..eba882519 --- /dev/null +++ b/java/0058-length-of-last-word.java @@ -0,0 +1,13 @@ +class Solution { + public int lengthOfLastWord(String s) { + int i = s.length() - 1, length = 0; + while (s.charAt(i) == ' ') { + i -= 1; + } + while (i >= 0 && s.charAt(i) != ' ') { + length += 1; + i -= 1; + } + return length; + } +} diff --git a/java/0059-spiral-matrix-ii.java b/java/0059-spiral-matrix-ii.java new file mode 100644 index 000000000..3e0da1691 --- /dev/null +++ b/java/0059-spiral-matrix-ii.java @@ -0,0 +1,30 @@ +class Solution { + public int[][] generateMatrix(int n) { + int[][] ans = new int[n][n]; + + int r1=0, r2=n-1; + int c1=0, c2=n-1; + int elem = 1; + while(r2>=r1 && c2>=c1){ + for(int i=c1; i<=c2; i++){ + ans[r1][i] = elem++; + } + for(int j=r1+1; j<=r2-1; j++){ + ans[j][c2] = elem++; + } + if(r2>r1 && c2>c1){ + for (int i = c2; i >= c1; i--){ + ans[r2][i] = elem++; + } + for (int j = r2-1; j>=r1+1; j--){ + ans[j][c1] = elem++; + } + } + r1++; + r2--; + c1++; + c2--; + } + return ans; + } +} \ No newline at end of file diff --git a/java/0060-permutation-sequence.java b/java/0060-permutation-sequence.java new file mode 100644 index 000000000..dbb7f9ad2 --- /dev/null +++ b/java/0060-permutation-sequence.java @@ -0,0 +1,79 @@ +class Solution { + + // Time complexity O(N^2) becuase of remove method + public String getPermutation(int n, int k) { + StringBuilder kthPerm = new StringBuilder(); + int fact = 1; + //this list will contain all the values from 1 to n for reference + ArrayList list = new ArrayList<>(); + for (int i = 1; i < n; i++) { + fact = fact * i; + list.add(i); + } + list.add(n); + k--; + while (true) { + kthPerm.append(list.get(k / fact)); + list.remove(k / fact); + if (list.size() == 0) break; + k = k % fact; + fact = fact / (list.size()); + } + return kthPerm.toString(); + } + + { + //Bruteforce solution (gives TLE) similar to Next Permutation problem no.31 + + // public String getPermutation(int n, int k) { + // int[] num = new int[n]; + // for (int i = 1; i<=n; i++) { + // num[i-1] = i; + // } + // for (int i = 1; i0 && nums[pivot]0 && nums[j]l + k = l - k; + + while (k > 0) { + temp = temp.next; + k--; + } + head = temp.next; + temp.next = null; + + return head; + } +} \ No newline at end of file diff --git a/java/0062-unique-paths.java b/java/0062-unique-paths.java new file mode 100644 index 000000000..3a30bb460 --- /dev/null +++ b/java/0062-unique-paths.java @@ -0,0 +1,56 @@ +class Solution { + + // Dynamic programming: TC = O(m*n), SC = O(m*n) + public int uniquePaths(int m, int n) { + int[][] dp = new int[m][n]; + + // Fill out last row + for (int j = 0; j < n; j++) { + dp[m - 1][j] = 1; + } + + // Fill out last column + for (int i = 0; i < m; i++) { + dp[i][n - 1] = 1; + } + + for (int i = m - 2; i >= 0; i--) { + for (int j = n - 2; j >= 0; j--) { + dp[i][j] = dp[i][j + 1] + dp[i + 1][j]; + } + } + return dp[0][0]; + } + + // Dynamic programming: TC = O(m*n), SC = O(min(m,n)) + public int uniquePaths2(int m, int n) { + if (m <= 0 || n <= 0) return 0; + + int[] dp = new int[n]; + for (int i = 0; i < n; i++) + dp[i] = 1; + + for (int i = 1; i < m; i++) + for (int j = 1; j < n; j++) + dp[j] += dp[j - 1]; + + return dp[n - 1]; + } + + // Combinatorics: TC = O(min(m,n)), SC = O(1) + // result = C(m + n, n) = (m + n)! / (m! * n!) + public int uniquePaths3(int m, int n) { + if (m <= 0 || n <= 0) return 0; + + if (m < n) return uniquePaths3(n, m); + + m--; + n--; + long res = 1; + for (int i = 1; i <= n; i++) { + res *= (m + i); + res /= i; + } + return (int)res; + } +} diff --git a/java/0063-unique-paths-ii.java b/java/0063-unique-paths-ii.java new file mode 100644 index 000000000..907fd7220 --- /dev/null +++ b/java/0063-unique-paths-ii.java @@ -0,0 +1,30 @@ +//Same as unique paths 1 just one extra condition to return 0 if grid[i][j] == 1 + +class Solution { + + public int uniquePathsWithObstacles(int[][] grid) { + return dfs( + grid, + 0, + 0, + grid.length, + grid[0].length, + new int[grid.length][grid[0].length] + ); + } + + public int dfs(int[][] grid, int i, int j, int m, int n, int[][] dp) { + if (i < 0 || j < 0 || i >= m || j >= n || grid[i][j] == 1) { + return 0; + } + if (i == m - 1 && j == n - 1) { + dp[i][j] = 1; + return dp[i][j]; + } + if (dp[i][j] != 0) return dp[i][j]; + int right = dfs(grid, i, j + 1, m, n, dp); + int left = dfs(grid, i + 1, j, m, n, dp); + dp[i][j] = right + left; + return dp[i][j]; + } +} diff --git a/java/0064-minimum-path-sum.java b/java/0064-minimum-path-sum.java new file mode 100644 index 000000000..f98a6b92c --- /dev/null +++ b/java/0064-minimum-path-sum.java @@ -0,0 +1,29 @@ +class Solution { + + public int minPathSum(int[][] grid) { + int m = grid.length - 1; + int n = grid[0].length - 1; + int[][] dp = new int[m + 1][n + 1]; + for (int[] arr : dp) { + Arrays.fill(arr, -1); + } + return helper(grid, m, n, dp); + } + + public int helper(int[][] grid, int m, int n, int[][] dp) { + if (m == 0 && n == 0) return grid[0][0]; + if (m == 0) { + dp[m][n] = grid[m][n] + helper(grid, m, n - 1, dp); + return dp[m][n]; + } + if (n == 0) { + dp[m][n] = grid[m][n] + helper(grid, m - 1, n, dp); + return dp[m][n]; + } + if (dp[m][n] != -1) return dp[m][n]; + dp[m][n] = + grid[m][n] + + Math.min(helper(grid, m, n - 1, dp), helper(grid, m - 1, n, dp)); + return dp[m][n]; + } +} diff --git a/java/0066-plus-one.java b/java/0066-plus-one.java new file mode 100644 index 000000000..7170b6f2c --- /dev/null +++ b/java/0066-plus-one.java @@ -0,0 +1,29 @@ +class Solution { + + public int[] plusOne(int[] digits) { + final int len = digits.length; + int[] newDigits = new int[len + 1]; + int carry = 1; + int currSum = 0; + for (int i = len - 1; i >= 0; i--) { + currSum = digits[i] + carry; + if (currSum > 9) { + digits[i] = currSum % 10; + newDigits[i + 1] = digits[i]; + carry = 1; + } else { + digits[i] = currSum; + newDigits[i + 1] = digits[i]; + carry = 0; + break; + } + } + + if (carry == 1) { + newDigits[0] = 1; + return newDigits; + } + + return digits; + } +} diff --git a/java/0067-Add-Binary.java b/java/0067-Add-Binary.java new file mode 100644 index 000000000..19b68c181 --- /dev/null +++ b/java/0067-Add-Binary.java @@ -0,0 +1,41 @@ +class Solution { + public String addBinary(String a, String b) { + // Go from right to left + int len1 = a.length(); + int len2 = b.length(); + + int minLen = Math.min(len1, len2); + int maxLen = Math.max(len1, len2); + + int index1 = len1 - 1; + int index2 = len2 - 1; + + StringBuilder bld = new StringBuilder(); + boolean carry = false; + + while((index1 >= 0) || (index2 >= 0) || carry) { + char c1 = (index1 >= 0) ? a.charAt(index1) : '0'; + char c2 = (index2 >= 0) ? b.charAt(index2) : '0'; + + char sum; + if((c1 == '0') && (c2 == '0')) { // Both zeros + sum = carry ? '1' : '0'; + carry = false; + } + else if((c1 == '1') && (c2 == '1')) { // Both ones + sum = carry ? '1' : '0'; + carry = true; + } + else { // Any one with a one + sum = carry ? '0' : '1'; + // carry will be forwarded + } + bld.append(sum); + + index1--; + index2--; + } + + return bld.reverse().toString(); + } +} \ No newline at end of file diff --git a/java/0067-add-binary.java b/java/0067-add-binary.java new file mode 100644 index 000000000..19b68c181 --- /dev/null +++ b/java/0067-add-binary.java @@ -0,0 +1,41 @@ +class Solution { + public String addBinary(String a, String b) { + // Go from right to left + int len1 = a.length(); + int len2 = b.length(); + + int minLen = Math.min(len1, len2); + int maxLen = Math.max(len1, len2); + + int index1 = len1 - 1; + int index2 = len2 - 1; + + StringBuilder bld = new StringBuilder(); + boolean carry = false; + + while((index1 >= 0) || (index2 >= 0) || carry) { + char c1 = (index1 >= 0) ? a.charAt(index1) : '0'; + char c2 = (index2 >= 0) ? b.charAt(index2) : '0'; + + char sum; + if((c1 == '0') && (c2 == '0')) { // Both zeros + sum = carry ? '1' : '0'; + carry = false; + } + else if((c1 == '1') && (c2 == '1')) { // Both ones + sum = carry ? '1' : '0'; + carry = true; + } + else { // Any one with a one + sum = carry ? '0' : '1'; + // carry will be forwarded + } + bld.append(sum); + + index1--; + index2--; + } + + return bld.reverse().toString(); + } +} \ No newline at end of file diff --git a/java/0068-text-justification.java b/java/0068-text-justification.java new file mode 100644 index 000000000..4eb0811a2 --- /dev/null +++ b/java/0068-text-justification.java @@ -0,0 +1,43 @@ +class Solution { + public List fullJustify(String[] words, int maxWidth) { + int count = 0; + List currLine = new ArrayList<>(); + List ans = new ArrayList<>(); + for (String w : words) { + if (count == 0) { + count += w.length(); + currLine.add(w); + } else if (count + 1 + w.length() > maxWidth) { + ans.add(getLine(currLine, count, maxWidth)); + currLine = new ArrayList<>(List.of(w)); + count = w.length(); + } else { + count += 1 + w.length(); + currLine.add(w); + } + } + ans.add(getLastLine(currLine, maxWidth)); + return ans; + } + + private String getLine(List words, int count, int maxWidth) { + if (words.size() == 0) return ""; + if (words.size() == 1) return words.get(0) + " ".repeat(maxWidth - words.get(0).length()); + int extraSpaces = (maxWidth - count) / (words.size() - 1); + int remSpaces = (maxWidth - count) % (words.size() - 1); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < words.size() - 1; i++) { + sb.append(words.get(i)).append(" "); + sb.append(" ".repeat(extraSpaces)); + if (remSpaces-- > 0) + sb.append(" "); + } + sb.append(words.getLast()); + return sb.toString(); + } + + private String getLastLine(List words, int maxWidth) { + String wordsStr = String.join(" ", words); + return wordsStr.toString() + " ".repeat(maxWidth - wordsStr.length()); + } +} diff --git a/java/0069-sqrtx.java b/java/0069-sqrtx.java new file mode 100644 index 000000000..da1666bd1 --- /dev/null +++ b/java/0069-sqrtx.java @@ -0,0 +1,29 @@ +class Solution { + public int mySqrt(int x) { + + // Linear search way -> loop through for all nums till x + // then see if their square <= x + + // Binary Search way -> we can optimise our approach by observing + // that nums till x are sorted + + int left = 0; + int right = x; + int mid = 0; + int probableAns = 0; + + while(left <= right){ + mid = left + (right-left)/2; + if((long)mid*mid <= (long)x){ + probableAns = mid; + // let's see if we can find a bigger num + left = mid+1; + } + else if((long)mid*mid > (long)x){ + right = mid-1; + } + } + + return probableAns; + } +} diff --git a/java/0070-climbing-stairs.java b/java/0070-climbing-stairs.java new file mode 100644 index 000000000..4df6c202d --- /dev/null +++ b/java/0070-climbing-stairs.java @@ -0,0 +1,54 @@ +//optimal +class Solution { + + public int climbStairs(int n) { + int a = 1; + int b = 1; + int c; + + for (int i = 0; i < n - 1; i++) { + c = a + b; + a = b; + b = c; + } + return b; + } +} + +//bottom up +class Solution { + + public int climbStairs(int n) { + int[] dp = new int[n + 1]; + dp[0] = 1; + dp[1] = 1; + + for (int i = 2; i < n + 1; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + return dp[n]; + } +} + +//top down with memo[] +class Solution { + + public int climbStairs(int n) { + int[] memo = new int[n + 1]; + Arrays.fill(memo, -1); + + return climbStairs(n - 1, memo) + climbStairs(n - 2, memo); + } + + private int climbStairs(int n, int[] memo) { + if (n < 0) return 0; + if (n == 0 || n == 1) { + memo[n] = 1; + return memo[n]; + } + if (memo[n] != -1) return memo[n]; + + memo[n] = climbStairs(n - 1, memo) + climbStairs(n - 2, memo); + return memo[n]; + } +} diff --git a/java/0071-simplify-path.java b/java/0071-simplify-path.java new file mode 100644 index 000000000..d1aad8659 --- /dev/null +++ b/java/0071-simplify-path.java @@ -0,0 +1,38 @@ +class Solution { + public String simplifyPath(String path) { + Stack stack = new Stack<>(); + StringBuilder curr = new StringBuilder(); + + String newPath = path + "/"; + + for(int i=0;i= 0) { + if (matrix[i][j] == target) { + return true; + } else if (matrix[i][j] > target) { + j--; + } else { + i++; + } + } + return false; + } + + // Time: O(log(mn)) | Space: O(1) + public boolean searchMatrix2(int[][] matrix, int target) { + if(matrix.length == 0) return false; + + int rows = matrix.length; + int columns = matrix[0].length; + + int low = 0; + int high = rows * columns; + + while(low < high) { + int mid = (low+high)/2; + + if(matrix[mid/columns][mid%columns] == target) { + return true; + } else if (matrix[mid/columns][mid%columns] < target) { + low = mid+1; + } else { + high = mid; + } + } + return false; + } +} diff --git a/java/0075-sort-colors.java b/java/0075-sort-colors.java new file mode 100644 index 000000000..aa59a42bb --- /dev/null +++ b/java/0075-sort-colors.java @@ -0,0 +1,20 @@ +class Solution { + + public void sortColors(int[] nums) { + int left = 0, mid = 0, right = nums.length - 1; + while (mid <= right) { + System.out.println(nums[mid]); + if (nums[mid] == 1) mid++; else if (nums[mid] == 0) { + swap(nums, mid++, left++); + } else if (nums[mid] == 2) { + swap(nums, mid, right--); + } + } + } + + public void swap(int[] nums, int i, int j) { + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } +} diff --git a/java/0076-minimum-window-substring.java b/java/0076-minimum-window-substring.java new file mode 100644 index 000000000..6f951f81d --- /dev/null +++ b/java/0076-minimum-window-substring.java @@ -0,0 +1,36 @@ +class Solution { + + //sliding window + public String minWindow(String s, String t) { + HashMap map = new HashMap<>(); + + for (char x : t.toCharArray()) { + map.put(x, map.getOrDefault(x, 0) + 1); + } + + int matched = 0; + int start = 0; + int minLen = s.length() + 1; + int subStr = 0; + for (int endWindow = 0; endWindow < s.length(); endWindow++) { + char right = s.charAt(endWindow); + if (map.containsKey(right)) { + map.put(right, map.get(right) - 1); + if (map.get(right) == 0) matched++; + } + + while (matched == map.size()) { + if (minLen > endWindow - start + 1) { + minLen = endWindow - start + 1; + subStr = start; + } + char deleted = s.charAt(start++); + if (map.containsKey(deleted)) { + if (map.get(deleted) == 0) matched--; + map.put(deleted, map.get(deleted) + 1); + } + } + } + return minLen > s.length() ? "" : s.substring(subStr, subStr + minLen); + } +} diff --git a/java/0077-combinations.java b/java/0077-combinations.java new file mode 100644 index 000000000..56343077e --- /dev/null +++ b/java/0077-combinations.java @@ -0,0 +1,21 @@ +class Solution { + List> res; + public List> combine(int n, int k) { + res = new ArrayList<>(); + backtrack(1, new ArrayList(), n, k); + return res; + } + + private void backtrack(int start, ArrayList comb, int n, int k) { + if (comb.size() == k){ + res.add(new ArrayList<>(comb)); + return; + } + + for (int i = start; i <= n; i++) { + comb.add(i); + backtrack(i+1, comb, n, k); + comb.remove((Integer) i); + } + } +} diff --git a/java/0078-subsets.java b/java/0078-subsets.java new file mode 100644 index 000000000..8508aa9b9 --- /dev/null +++ b/java/0078-subsets.java @@ -0,0 +1,30 @@ +// The idea is to have two conditions: +// One in which we will take the element into consideration, +// Second in which we won't take the element into consideration. +class Solution { + + public List> subsets(int[] nums) { + List> ans = new ArrayList<>(); + List list = new ArrayList<>(); + helper(ans, 0, nums, list); + return ans; + } + + public void helper( + List> ans, + int start, + int[] nums, + List list + ) { + if (start >= nums.length) { + ans.add(new ArrayList<>(list)); + } else { + // add the element and start the recursive call + list.add(nums[start]); + helper(ans, start + 1, nums, list); + // remove the element and do the backtracking call. + list.remove(list.size() - 1); + helper(ans, start + 1, nums, list); + } + } +} diff --git a/java/0079-word-search.java b/java/0079-word-search.java new file mode 100644 index 000000000..ce391f2a9 --- /dev/null +++ b/java/0079-word-search.java @@ -0,0 +1,49 @@ +//This is a typical backtracking problem similar to Number of Islands. +//In number of Isalnds, we sinked the islands. Here, we will do the same by adding changing the value of the char. +//I added 100 because it will exceed the ascii limit for characters and will change it to some ascii value which is not an alphabet. + +class Solution { + + public boolean exist(char[][] board, String word) { + int m = board.length; + int n = board[0].length; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (check(board, word, i, j, m, n, 0)) { + return true; + } + } + } + return false; + } + + public boolean check( + char[][] board, + String word, + int i, + int j, + int m, + int n, + int cur + ) { + if (cur >= word.length()) return true; + if ( + i < 0 || + j < 0 || + i >= m || + j >= n || + board[i][j] != word.charAt(cur) + ) return false; + boolean exist = false; + if (board[i][j] == word.charAt(cur)) { + board[i][j] += 100; + exist = + check(board, word, i + 1, j, m, n, cur + 1) || + check(board, word, i, j + 1, m, n, cur + 1) || + check(board, word, i - 1, j, m, n, cur + 1) || + check(board, word, i, j - 1, m, n, cur + 1); + board[i][j] -= 100; + } + return exist; + } +} diff --git a/java/0080-remove-duplicates-from-sorted-array-ii.java b/java/0080-remove-duplicates-from-sorted-array-ii.java new file mode 100644 index 000000000..37953fce1 --- /dev/null +++ b/java/0080-remove-duplicates-from-sorted-array-ii.java @@ -0,0 +1,60 @@ +class Solution { + public int removeDuplicates(int[] nums) { + int l = 0, r = 0, n = nums.length; + + while (r < n) { + int count = 1; + while (r + 1 < n && nums[r] == nums[r + 1]) { + r++; + count++; + } + + for (int i = 0; i < Math.min(2, count); i++) { + nums[l] = nums[r]; + l++; + } + r++; + } + return l; + } +} + +/* + * Time Complexity: O(n); + * Space Complexity: O(1); + */ + +class Solution { + public int removeDuplicates(int[] nums) { + int count = 1; + int index1 = 0; + for (int i = 1; i < nums.length; i++) { + if (nums[i] != nums[index1]) { + count = 1; + index1++; + nums[index1] = nums[i]; + } else { + count++; + if (count <= 2) { + index1++; + nums[index1] = nums[i]; + } + }; + }; + return index1 + 1; + } +} + +class Solution { + public int removeDuplicates(int[] nums) { + int k = 0; + for (int num: nums) { + if (k < 2 || nums[k-2] != num) { + nums[k++] = num; + } + } + return k; + } +} + + diff --git a/java/0081-search-in-rotated-sorted-array-ii.java b/java/0081-search-in-rotated-sorted-array-ii.java new file mode 100644 index 000000000..3f1f2555c --- /dev/null +++ b/java/0081-search-in-rotated-sorted-array-ii.java @@ -0,0 +1,26 @@ +class Solution { + + public boolean search(int[] nums, int target) { + int l = 0, r = nums.length - 1; + while (l <= r) { + // to avoid duplicates + while (l < r && nums[l] == nums[l + 1]) ++l; + while (l < r && nums[r] == nums[r - 1]) --r; + + int m = l + (r - l) / 2; + + int num = (nums[m] < nums[0]) == (target < nums[0]) + ? nums[m] + : target < nums[0] ? Integer.MIN_VALUE : Integer.MAX_VALUE; + + if (num < target) { + l = m + 1; + } else if (num > target) { + r = m - 1; + } else { + return true; + } + } + return false; + } +} diff --git a/java/0083-remove-duplicates-from-sorted-list.java b/java/0083-remove-duplicates-from-sorted-list.java new file mode 100644 index 000000000..d84bb3ac9 --- /dev/null +++ b/java/0083-remove-duplicates-from-sorted-list.java @@ -0,0 +1,21 @@ +//Three pointer approach that we use in in-place reversal of linked list. + +class Solution { + + public ListNode deleteDuplicates(ListNode head) { + ListNode p = null; + ListNode q = null; + ListNode r = head; + while (r != null) { + if (q != null && q.val == r.val) { + r = r.next; + q.next = r; + } else { + p = q; + q = r; + r = r.next; + } + } + return head; + } +} diff --git a/java/0084-largest-rectangle-in-histogram.java b/java/0084-largest-rectangle-in-histogram.java new file mode 100644 index 000000000..6ba5e0303 --- /dev/null +++ b/java/0084-largest-rectangle-in-histogram.java @@ -0,0 +1,28 @@ +class Solution { + public int largestRectangleArea(int[] heights) { + int area = 0, n = heights.length; + int start; + Stack> stack = new Stack<>(); + for(int i=0;i curHt){ + Pair pair = stack.pop(); + int index = pair.getKey(); + int h = pair.getValue(); + area = Math.max(area, h * (i - index)); + start = index; + } + stack.push(new Pair(start,curHt)); + } + + while(!stack.isEmpty()){ + Pair pair = stack.pop(); + int index = pair.getKey(); + int h = pair.getValue(); + area = Math.max(area, h * (n - index)); + } + return area; + } + +} diff --git a/java/0086-partition-list.java b/java/0086-partition-list.java new file mode 100644 index 000000000..ed7cf1ce1 --- /dev/null +++ b/java/0086-partition-list.java @@ -0,0 +1,26 @@ +class Solution { + public ListNode partition(ListNode head, int x) { + ListNode head1 = new ListNode(); // will store node.value < x + ListNode head2 = new ListNode(); // will store node.value >= x + + ListNode tail1 = head1, tail2 = head2, curr = head; + + while (curr != null) { + if (curr.val < x) { + tail1.next = curr; + tail1 = tail1.next; + } else { + tail2.next = curr; + tail2 = tail2.next; + } + curr = curr.next; + } + // at this point the two lists are not connected the way we want them to + // we'll need to connect the list with values= x + // also the last node (tail2) is currently pointing to some random node but it + // should point to null + tail1.next = head2.next; + tail2.next = null; + return head1.next; + } +} \ No newline at end of file diff --git a/java/0088-merge-sorted-array.java b/java/0088-merge-sorted-array.java new file mode 100644 index 000000000..6f6a858b7 --- /dev/null +++ b/java/0088-merge-sorted-array.java @@ -0,0 +1,19 @@ +class Solution { + + // Time: O(m+n) | Space: O(1) + public void merge(int[] nums1, int m, int[] nums2, int n) { + // Three pointer technique + int r1 = m-1; + int r2 = n-1; + + for(int w = m+n-1; w >= 0; w--) { + if(r1 >= 0 && r2 >= 0) { + nums1[w] = nums1[r1] > nums2[r2] ? nums1[r1--] : nums2[r2--]; + } else if (r1 >= 0) { + nums1[w] = nums1[r1--]; + } else { + nums1[w] = nums2[r2--]; + } + } + } +} \ No newline at end of file diff --git a/java/0090-subsets-ii.java b/java/0090-subsets-ii.java new file mode 100644 index 000000000..7273b6820 --- /dev/null +++ b/java/0090-subsets-ii.java @@ -0,0 +1,28 @@ +// Similar to subsets 1. Here, we'll just take care of the duplicates. +class Solution { + + public List> subsetsWithDup(int[] nums) { + Arrays.sort(nums); + List> ans = new ArrayList<>(); + List list = new ArrayList<>(); + subSet(nums, 0, ans, list); + return ans; + } + + public void subSet( + int[] nums, + int idx, + List> ans, + List list + ) { + ans.add(new ArrayList<>(list)); + + for (int i = idx; i < nums.length; i++) { + //skip the duplicate elements + if (i > idx && nums[i] == nums[i - 1]) continue; + list.add(nums[i]); + subSet(nums, i + 1, ans, list); + list.remove(list.size() - 1); + } + } +} diff --git a/java/0091-decode-ways.java b/java/0091-decode-ways.java new file mode 100644 index 000000000..82d13f1f1 --- /dev/null +++ b/java/0091-decode-ways.java @@ -0,0 +1,70 @@ +// Optimal +class Solution { + + public int numDecodings(String s) { + int twoBack = 1; // empty string + int oneBack = s.charAt(0) == '0' ? 0 : 1; + int current = oneBack; + for (int i = 2; i < s.length() + 1; i++) { + current = 0; + if (s.charAt(i - 1) != '0') { + current += oneBack; + } + if ( + s.charAt(i - 2) == '1' || + (s.charAt(i - 2) == '2' && s.charAt(i - 1) < '7') + ) { + current += twoBack; + } + twoBack = oneBack; + oneBack = current; + } + return current; + } +} + +//bottom up +class Solution { + + public int numDecodings(String s) { + int[] dp = new int[s.length() + 1]; + dp[0] = 1; // empty string + dp[1] = s.charAt(0) == '0' ? 0 : 1; + for (int i = 2; i < s.length() + 1; i++) { + if (s.charAt(i - 1) != '0') { + dp[i] += dp[i - 1]; + } + if ( + s.charAt(i - 2) == '1' || + (s.charAt(i - 2) == '2' && s.charAt(i - 1) < '7') + ) { + dp[i] += dp[i - 2]; + } + } + return dp[s.length()]; + } +} + +//top down with memoization +class Solution { + + public int numDecodings(String s) { + return numDecodings(s, 0, new Integer[s.length()]); + } + + private int numDecodings(String s, int i, Integer[] dp) { + if (i == s.length()) return 1; + if (s.charAt(i) == '0') return 0; + if (dp[i] != null) return dp[i]; + int count = 0; + count += numDecodings(s, i + 1, dp); + if ( + i < s.length() - 1 && + (s.charAt(i) == '1' || s.charAt(i) == '2' && s.charAt(i + 1) < '7') + ) { + count += numDecodings(s, i + 2, dp); + } + dp[i] = count; + return dp[i]; + } +} diff --git a/java/0092-reverse-linked-list-ii.java b/java/0092-reverse-linked-list-ii.java new file mode 100644 index 000000000..816f87321 --- /dev/null +++ b/java/0092-reverse-linked-list-ii.java @@ -0,0 +1,61 @@ +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +class Solution { + public ListNode reverseBetween(ListNode head, int left, int right) { + + if (head == null || head.next == null) { + return head; + } + + if (left > right || left == right) { + return head; + } + + ListNode l = head; + ListNode prevL = null; + ListNode r = head; + ListNode nextR = null; + int posL = 1; + int posR = 1; + + while (posL != left) { + prevL = l; + l = l.next; + posL++; + } + + while (posR != right) { + r = r.next; + posR++; + } + + nextR = r.next; + r.next = null; + ListNode node = reverseList(l); + node.next = nextR; + if (prevL == null) { + head = r; + } else { + prevL.next = r; + } + return head; + } + + public ListNode reverseList(ListNode l) { + if (l.next == null) { + return l; + } + ListNode newNode = reverseList(l.next); + newNode.next = l; + l.next = null; + return l; + } +} diff --git a/java/0094-binary-tree-inorder-traversal.java b/java/0094-binary-tree-inorder-traversal.java new file mode 100644 index 000000000..91317f389 --- /dev/null +++ b/java/0094-binary-tree-inorder-traversal.java @@ -0,0 +1,37 @@ +class Solution { + public List inorderTraversal(TreeNode root) { + //iterative + List res = new ArrayList<>(); + if(root == null) return res; + + Stack stack = new Stack<>(); + TreeNode node = root; + while(!stack.isEmpty() || node != null) { + if(node != null) { + stack.push(node); + node = node.left; + } else { + node = stack.pop(); + res.add(node.val); + node = node.right; + } + } + + return res; + + //recursive + // List res = new ArrayList<>(); + // dfs(root, res); + // return res; + } + + // public void dfs(TreeNode n, List res) { + // if(n == null) { + // return; + // } + + // dfs(n.left, res); + // res.add(n.val); + // dfs(n.right, res); + // } +} \ No newline at end of file diff --git a/java/0095-unique-binary-search-trees-ii.java b/java/0095-unique-binary-search-trees-ii.java new file mode 100644 index 000000000..a97daa621 --- /dev/null +++ b/java/0095-unique-binary-search-trees-ii.java @@ -0,0 +1,27 @@ +class Solution { + public List generateTrees(int n) { + return generate(1, n); + } + + private List generate(int left, int right) { + if (left > right) { + List r = new ArrayList<>(); + r.add(null); + return r; + } + + List res = new ArrayList<>(); + for (int val = left; val <= right; val++) { + for (TreeNode leftSubtree : generate(left, val - 1)) { + for (TreeNode rightSubtree: generate(val + 1, right)) { + TreeNode root = new TreeNode(val); + root.left = leftSubtree; + root.right = rightSubtree; + res.add(root); + } + } + } + + return res; + } +} diff --git a/java/0096-unique-binary-search-trees.java b/java/0096-unique-binary-search-trees.java new file mode 100644 index 000000000..9f804a46d --- /dev/null +++ b/java/0096-unique-binary-search-trees.java @@ -0,0 +1,17 @@ +class Solution { + public int numTrees(int n) { + int[] numTree = new int[n+1]; + numTree[0] = numTree[1] = 1; + + for(int nodes = 2; nodes < n+1; nodes++){ + int total = 0; + for(int root = 1; root < nodes+1; root++){ + int left = root - 1; + int right = nodes - root; + total += numTree[left] * numTree[right]; + } + numTree[nodes] = total; + } + return numTree[n]; + } +} diff --git a/java/0097-interleaving-string.java b/java/0097-interleaving-string.java new file mode 100644 index 000000000..1538cbaac --- /dev/null +++ b/java/0097-interleaving-string.java @@ -0,0 +1,34 @@ +class Solution { + + public boolean isInterleave(String s1, String s2, String s3) { + if (s1.length() + s2.length() != s3.length()) { + return false; + } + + boolean[][] dp = new boolean[s1.length() + 1][s2.length() + 1]; + dp[s1.length()][s2.length()] = true; + + for (int i = dp.length - 1; i >= 0; i--) for ( + int j = dp[0].length - 1; + j >= 0; + j-- + ) { + if ( + i < s1.length() && + s1.charAt(i) == s3.charAt(i + j) && + dp[i + 1][j] + ) { + dp[i][j] = true; + } + if ( + j < s2.length() && + s2.charAt(j) == s3.charAt(i + j) && + dp[i][j + 1] + ) { + dp[i][j] = true; + } + } + + return dp[0][0]; + } +} diff --git a/java/0098-validate-binary-search-tree.java b/java/0098-validate-binary-search-tree.java new file mode 100644 index 000000000..e178d564b --- /dev/null +++ b/java/0098-validate-binary-search-tree.java @@ -0,0 +1,34 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + + public boolean isValidBST(TreeNode root) { + if (root == null) return true; + return dfs(root, null, null); + } + + private boolean dfs(TreeNode root, Integer min, Integer max) { + if (root == null) return true; + + if ( + (min != null && root.val <= min) || max != null && root.val >= max + ) { + return false; + } + + return dfs(root.left, min, root.val) && dfs(root.right, root.val, max); + } +} diff --git a/java/0100-same-tree.java b/java/0100-same-tree.java new file mode 100644 index 000000000..0c34dc024 --- /dev/null +++ b/java/0100-same-tree.java @@ -0,0 +1,40 @@ +package java; + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + + public boolean isSameTree(TreeNode p, TreeNode q) { + return dfs(p, q); + } + + private boolean dfs(TreeNode p, TreeNode q) { + if (p == null && q == null) { + return true; + } + + if (p == null || q == null) { + return false; + } + + if (p.val != q.val) return false; + + boolean left = dfs(p.left, q.left); + boolean right = dfs(p.right, q.right); + + return left && right; + } +} diff --git a/java/0101-symmetric-tree.java b/java/0101-symmetric-tree.java new file mode 100644 index 000000000..74e4c8eb7 --- /dev/null +++ b/java/0101-symmetric-tree.java @@ -0,0 +1,17 @@ +class Solution { + public boolean isSymmetric(TreeNode root) { + return dfs(root.left,root.right); + } + private boolean dfs(TreeNode a, TreeNode b){ + if(a == null && b == null ){ + return true; + } + if(a == null || b == null ){ + return false; + }else if(a.val != b.val){ + return false; + }else{ // normal path + return dfs(a.left,b.right) && dfs(a.right,b.left) ; + } + } +} \ No newline at end of file diff --git a/java/0102-binary-tree-level-order-traversal.java b/java/0102-binary-tree-level-order-traversal.java new file mode 100644 index 000000000..595655701 --- /dev/null +++ b/java/0102-binary-tree-level-order-traversal.java @@ -0,0 +1,42 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + + public List> levelOrder(TreeNode root) { + List> res = new ArrayList<>(); + Queue queue = new LinkedList<>(); + + if (root == null) return res; + + queue.add(root); + while (!queue.isEmpty()) { + int len = queue.size(); + List level = new ArrayList<>(); + for (int i = 0; i < len; i++) { + TreeNode curr = queue.poll(); + level.add(curr.val); + if (curr.left != null) { + queue.add(curr.left); + } + if (curr.right != null) { + queue.add(curr.right); + } + } + res.add(level); + } + return res; + } +} diff --git a/java/0103-binary-tree-zigzag-level-order-traversal.java b/java/0103-binary-tree-zigzag-level-order-traversal.java new file mode 100644 index 000000000..ef7deddd5 --- /dev/null +++ b/java/0103-binary-tree-zigzag-level-order-traversal.java @@ -0,0 +1,30 @@ +class Solution { + boolean reverse = false; // flag to detrmine the direction left or right + List> sol = new ArrayList>(); + public List> zigzagLevelOrder(TreeNode root) { + if(root == null){return sol;} + Queue queue = new LinkedList<>(); + queue.add(root); + while( !queue.isEmpty()){ + List temp = new ArrayList(); + int size =queue.size(); + for(int i=0; i < size; i++){ + TreeNode node = queue.poll(); + temp.add(node.val); + if(node.left != null){ + queue.add(node.left); + } + if(node.right != null){ + queue.add(node.right); + } + } + if(reverse){ + Collections.reverse(temp); + } + reverse = !reverse; + sol.add(temp); + } + return sol; + } + +} \ No newline at end of file diff --git a/java/0104-maximum-depth-of-binary-tree.java b/java/0104-maximum-depth-of-binary-tree.java new file mode 100644 index 000000000..325b5e298 --- /dev/null +++ b/java/0104-maximum-depth-of-binary-tree.java @@ -0,0 +1,7 @@ +class Solution { + + public int maxDepth(TreeNode root) { + if (root == null) return 0; + return 1 + Math.max(maxDepth(root.left), maxDepth(root.right)); + } +} diff --git a/java/0105-construct-binary-tree-from-preorder-and-inorder-traversal.java b/java/0105-construct-binary-tree-from-preorder-and-inorder-traversal.java new file mode 100644 index 000000000..32bcfe8ec --- /dev/null +++ b/java/0105-construct-binary-tree-from-preorder-and-inorder-traversal.java @@ -0,0 +1,82 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + + public TreeNode buildTree(int[] preorder, int[] inorder) { + if (preorder.length == 0 || inorder.length == 0) return null; + + TreeNode root = new TreeNode(preorder[0]); + int mid = 0; + for (int i = 0; i < inorder.length; i++) { + if (preorder[0] == inorder[i]) mid = i; + } + + root.left = + buildTree( + Arrays.copyOfRange(preorder, 1, mid + 1), + Arrays.copyOfRange(inorder, 0, mid) + ); + root.right = + buildTree( + Arrays.copyOfRange(preorder, mid + 1, preorder.length), + Arrays.copyOfRange(inorder, mid + 1, inorder.length) + ); + + return root; + } +} + +// Solution without using Array copies +class Solution { + + Map inorderPositions = new HashMap<>(); + + public TreeNode buildTree(int[] preorder, int[] inorder) { + if (preorder.length < 1 || inorder.length < 1) return null; + + for (int i = 0; i < inorder.length; i++) { + inorderPositions.put(inorder[i], i); + } + + return builder(preorder, 0, 0, inorder.length - 1); + } + + public TreeNode builder( + int[] preorder, + int preorderIndex, + int inorderLow, + int inorderHigh + ) { + if ( + preorderIndex > preorder.length - 1 || inorderLow > inorderHigh + ) return null; + + int currentVal = preorder[preorderIndex]; + TreeNode n = new TreeNode(currentVal); + int mid = inorderPositions.get(currentVal); + + n.left = builder(preorder, preorderIndex + 1, inorderLow, mid - 1); + n.right = + builder( + preorder, + preorderIndex + (mid - inorderLow) + 1, + mid + 1, + inorderHigh + ); + + return n; + } +} diff --git a/java/0106-construct-binary-tree-from-inorder-and-postorder-traversal.java b/java/0106-construct-binary-tree-from-inorder-and-postorder-traversal.java new file mode 100644 index 000000000..4112747f7 --- /dev/null +++ b/java/0106-construct-binary-tree-from-inorder-and-postorder-traversal.java @@ -0,0 +1,56 @@ +class Solution { + + public TreeNode buildTree(int[] inorder, int[] postorder) { + HashMap inMap = new HashMap<>(); + for (int i = 0; i < inorder.length; i++) { + inMap.put(inorder[i], i); + } + return helper( + inorder, + 0, + inorder.length - 1, + postorder, + 0, + postorder.length - 1, + inMap + ); + } + + public TreeNode helper( + int[] inorder, + int iStart, + int iEnd, + int[] postorder, + int pStart, + int pEnd, + HashMap inMap + ) { + if (pStart > pEnd || iStart > iEnd) { + return null; + } + TreeNode root = new TreeNode(postorder[pEnd]); + int index = inMap.get(postorder[pEnd]); + int numsLeft = index - iStart; + root.left = + helper( + inorder, + iStart, + index - 1, + postorder, + pStart, + pStart + numsLeft - 1, + inMap + ); + root.right = + helper( + inorder, + index + 1, + iEnd, + postorder, + pStart + numsLeft, + pEnd - 1, + inMap + ); + return root; + } +} diff --git a/java/0107-binary-tree-level-order-traversal-ii.java b/java/0107-binary-tree-level-order-traversal-ii.java new file mode 100644 index 000000000..aa3e9f850 --- /dev/null +++ b/java/0107-binary-tree-level-order-traversal-ii.java @@ -0,0 +1,27 @@ +//Same just reverse in the end. +//Reversing in the end is better than using add(0, E) in case of arraylist as it's an O(1) operation. + +class Solution { + + public List> levelOrderBottom(TreeNode root) { + List> ans = new ArrayList<>(); + if (root == null) return ans; + Queue q = new LinkedList<>(); + q.offer(root); + while (!q.isEmpty()) { + List level = new ArrayList<>(); + int size = q.size(); + for (int i = 0; i < size; i++) { + TreeNode cur = q.poll(); + level.add(cur.val); + if (cur.left != null) q.offer(cur.left); + if (cur.right != null) q.offer(cur.right); + } + ans.add(level); + } + int i = 0, j = ans.size() - 1; + //reverse the list + Collections.reverse(ans); + return ans; + } +} diff --git a/java/0108-convert-sorted-array-to-binary-search-tree.java b/java/0108-convert-sorted-array-to-binary-search-tree.java new file mode 100644 index 000000000..0d4b617de --- /dev/null +++ b/java/0108-convert-sorted-array-to-binary-search-tree.java @@ -0,0 +1,19 @@ +class Solution { + public TreeNode sortedArrayToBST(int[] nums) { + return generateTree(nums, 0, nums.length - 1); + } + + public TreeNode generateTree(int[] nums, int low, int high) { + if (low > high) { + return null; + } + + int mid = low + ((high - low) / 2); + TreeNode node = new TreeNode(nums[mid]); + + node.left = generateTree(nums, low, mid - 1); + node.right = generateTree(nums, mid + 1, high); + + return node; + } +} \ No newline at end of file diff --git a/java/0110-balanced-binary-tree.java b/java/0110-balanced-binary-tree.java new file mode 100644 index 000000000..5d4d7ddeb --- /dev/null +++ b/java/0110-balanced-binary-tree.java @@ -0,0 +1,56 @@ +package com.coding.patterns.tree; + +class BalancedBinaryTree { + + private static Pair dfs(TreeNode root) { + if (root == null) { + return new Pair(true, 0); + } + + var left = dfs(root.left); + var right = dfs(root.right); + + var balanced = + left.getKey() && + right.getKey() && + (Math.abs(left.getValue() - right.getValue()) <= 1); + + return new Pair( + balanced, + 1 + Math.max(left.getValue(), right.getValue()) + ); + } + + public static boolean isBalanced(TreeNode root) { + return dfs(root).getKey(); + } +} + +// Solution using the bottom up approach +// TC and SC is On + +class Solution { + + public int height(TreeNode root){ + if(root == null){ + return 0; + } + + int lh = height(root.left); + int rh = height(root.right); + + return 1 + Math.max(lh,rh); + } + + public boolean isBalanced(TreeNode root) { + + if(root == null){ + return true; + } + + int lh = height(root.left); + int rh = height(root.right); + + return Math.abs(lh - rh) <= 1 && isBalanced(root.left) && isBalanced(root.right); + } +} \ No newline at end of file diff --git a/java/0112-path-sum.java b/java/0112-path-sum.java new file mode 100644 index 000000000..917d45748 --- /dev/null +++ b/java/0112-path-sum.java @@ -0,0 +1,30 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + //This would be easily solved by DFS and then comparing the values + public boolean dfs(TreeNode root, int targetSum, int currSum){ + if(root == null) return false; + + currSum += root.val; + if(root.left == null && root.right == null){ + return (currSum == targetSum); + } + return dfs(root.left, targetSum, currSum) || dfs(root.right, targetSum, currSum); + } + public boolean hasPathSum(TreeNode root, int targetSum) { + return dfs(root, targetSum, 0); + } +} diff --git a/java/0115-distinct-subsequences.java b/java/0115-distinct-subsequences.java new file mode 100644 index 000000000..5fe3f427b --- /dev/null +++ b/java/0115-distinct-subsequences.java @@ -0,0 +1,40 @@ +// Dynammic Programming - Memoization +// Time Complexity O(s * t) | Space Complexity O(s * t) +class Solution { + + public int numDistinct(String s, String t) { + int n = s.length() + 1; + int m = t.length() + 1; + int[][] memo = new int[n][m]; + + for (int[] row : memo) { + Arrays.fill(row, -1); + } + + return recursion(s, t, 0, 0, memo); + } + + public int recursion(String s, String t, int sIdx, int tIdx, int[][] memo) { + if (memo[sIdx][tIdx] != -1) { + return memo[sIdx][tIdx]; + } + + if (tIdx >= t.length()) { + return 1; + } + + if (sIdx >= s.length()) { + return 0; + } + + if (t.charAt(tIdx) == s.charAt(sIdx)) { + memo[sIdx][tIdx] = + recursion(s, t, sIdx + 1, tIdx + 1, memo) + + recursion(s, t, sIdx + 1, tIdx, memo); + return memo[sIdx][tIdx]; + } + + memo[sIdx][tIdx] = recursion(s, t, sIdx + 1, tIdx, memo); + return memo[sIdx][tIdx]; + } +} diff --git a/java/0116-populating-next-right-pointers-in-each-node.java b/java/0116-populating-next-right-pointers-in-each-node.java new file mode 100644 index 000000000..d08f29edc --- /dev/null +++ b/java/0116-populating-next-right-pointers-in-each-node.java @@ -0,0 +1,23 @@ +class Solution { + public Node connect(Node root) { + Node current = root; + Node next = root == null ? null : root.left; + + while (current != null && next != null) { + current.left.next = current.right; + + if (current.next != null) { + current.right.next = current.next.left; + } + + current = current.next; + + if (current == null) { + current = next; + next = current.left; + } + } + + return root; + } +} diff --git a/java/0118-pascals-triangle.java b/java/0118-pascals-triangle.java new file mode 100644 index 000000000..c4892841b --- /dev/null +++ b/java/0118-pascals-triangle.java @@ -0,0 +1,22 @@ +class Solution { + public List> generate(int numRows) { + + List> res = new ArrayList<>(); + + for(int i=0; i list = new ArrayList<>(); + + for(int j=0; j<=i; j++){ + if(j>0 && i>0 && res.get(i-1).size()-1>=j){ + list.add(res.get(i-1).get(j-1)+res.get(i-1).get(j)); + } else{ + list.add(1); + } + } + res.add(list); + } + + return res; + + } +} \ No newline at end of file diff --git a/java/0119-pascals-triangle-ii.java b/java/0119-pascals-triangle-ii.java new file mode 100644 index 000000000..384f9576b --- /dev/null +++ b/java/0119-pascals-triangle-ii.java @@ -0,0 +1,40 @@ +//Approach 1 using recursion + +class Solution { + + public List getRow(int rowIndex) { + List ans = new ArrayList<>(); + int[][] dp = new int[rowIndex + 1][rowIndex + 1]; + for (int i = 0; i <= rowIndex; i++) { + ans.add(value(rowIndex, i, dp)); + } + return ans; + } + + public int value(int row, int col, int[][] dp) { + if (row == 0 || col == 0 || col == row) return 1; + if (dp[row][col] != 0) return dp[row][col]; + dp[row][col] = value(row - 1, col - 1, dp) + value(row - 1, col, dp); + return dp[row][col]; + } + + /** Iterative approach to solving the problem - follows Neetcode's solution in Python + * O(n) time complexity + * */ + public List getRow(int rowIndex) { + List res = new ArrayList<>(rowIndex + 1); + for (int i = 0; i < rowIndex + 1; i++) { + res.add(1); + } + + for (int i = 2; i < rowIndex + 1; i++) { + for (int j = i - 1; j > 0; j--) { + res.set(j, res.get(j) + res.get(j - 1)); + } + } + return res; + } + + +} +//todo: add bottom up approach diff --git a/java/0120-triangle.java b/java/0120-triangle.java new file mode 100644 index 000000000..0e825b16a --- /dev/null +++ b/java/0120-triangle.java @@ -0,0 +1,14 @@ +class Solution { + public int minimumTotal(List> triangle) { + int l = triangle.size(); + int[] dp = new int[l + 1]; + + for (int row = l - 1; row > -1; --row) { + for (int col = 0; col < row + 1; ++col) { + dp[col] = triangle.get(row).get(col) + Math.min(dp[col], dp[col + 1]); + } + } + + return dp[0]; + } +} diff --git a/java/0121-best-time-to-buy-and-sell-stock.java b/java/0121-best-time-to-buy-and-sell-stock.java new file mode 100644 index 000000000..2bdd93355 --- /dev/null +++ b/java/0121-best-time-to-buy-and-sell-stock.java @@ -0,0 +1,17 @@ +class Solution { + + public int maxProfit(int[] prices) { + int left = 0; + int right = 1; + int maxProfit = 0; + while (right < prices.length) { + if (prices[left] < prices[right]) { + maxProfit = Math.max(maxProfit, prices[right] - prices[left]); + } else { + left = right; + } + right++; + } + return maxProfit; + } +} diff --git a/java/0122-best-time-to-buy-and-sell-stock-II.java b/java/0122-best-time-to-buy-and-sell-stock-II.java new file mode 100644 index 000000000..126b1ecce --- /dev/null +++ b/java/0122-best-time-to-buy-and-sell-stock-II.java @@ -0,0 +1,15 @@ +class Solution { + public int maxProfit(int[] prices) { + int oldStockPrice = prices[0]; + int profit = 0; + for(int i = 1; i wordList + ) { + Map> adjlist = new HashMap<>(); + wordList.add(beginWord); + for (String word : wordList) { + StringBuilder string = null; + for (int i = 0; i < word.length(); i++) { + string = new StringBuilder(word); + string.setCharAt(i, '*'); + List wordlist = adjlist.getOrDefault( + string.toString(), + new ArrayList() + ); + wordlist.add(word); + adjlist.put(string.toString(), wordlist); + } + } + + Queue queue = new LinkedList<>(); + queue.offer(beginWord); + Set visited = new HashSet<>(); + visited.add(beginWord); + int step = 1; + StringBuilder string = null; + while (!queue.isEmpty()) { + step++; + int size = queue.size(); + for (int j = 0; j < size; j++) { + String str = queue.poll(); + + for (int i = 0; i < str.length(); i++) { + string = new StringBuilder(str); + string.setCharAt(i, '*'); + for (String pat : adjlist.get(string.toString())) { + if (pat.equals(endWord)) { + return step; + } + if (visited.contains(pat)) { + continue; + } + queue.offer(pat); + visited.add(pat); + } + } + } + // step++; + } + return 0; + } +} diff --git a/java/0128-longest-consecutive-sequence.java b/java/0128-longest-consecutive-sequence.java new file mode 100644 index 000000000..0923cf9d7 --- /dev/null +++ b/java/0128-longest-consecutive-sequence.java @@ -0,0 +1,23 @@ +class Solution { + public int longestConsecutive(int[] nums) { + if (nums.length == 0) return 0; + HashSet hs = new HashSet<>(); + for(int num:nums) hs.add(num); + int longest =1; + for(int num: nums ){ + //check if the num is the start of a sequence by checking if left exists + if(!hs.contains(num-1)){ // start of a sequence + int count =1; + while(hs.contains(num + 1)){ // check if hs contains next no. + num++; + count++; + } + longest = Math.max(longest, count); + + } + if(longest > nums.length/2) break; + + } + return longest; + } +} diff --git a/java/0129-sum-root-to-leaf-numbers.java b/java/0129-sum-root-to-leaf-numbers.java new file mode 100644 index 000000000..ebd005aca --- /dev/null +++ b/java/0129-sum-root-to-leaf-numbers.java @@ -0,0 +1,109 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +// solution from the video +class Solution { + /* + Time complexity: O(V) where V is the number of vertices + Space complexity: O(h) where h is the height of the tree + */ + + public int sumNumbers(TreeNode root) { + // track the sum that we want to add + int solution = 0; + + // execute a dfs to find the leaf nodes + solution = findLeafNodes(root, solution); + + // return the solution + return solution; + } + + // dfs method + public int findLeafNodes(TreeNode node, int currentPath){ + // base case, if no node then return 0 + if(node==null){ + return 0; + } + + // add the current node value to the currentPath (move decimal to right by 1 and add) + currentPath = (currentPath * 10) + node.val; + + // if we are at a non-null node, check if it is a leaf + if(node.left==null && node.right==null){ + // return the solution + return currentPath; + } + + // check find the leaf nodes on the left and right + return findLeafNodes(node.left, currentPath) + findLeafNodes(node.right, currentPath); + } +} + +// solution using strings +class Solution { + /* + Time complexity: O(V) where V is the number of vertices + Space complexity: O(V) where V is the number of vertices + */ + + // keep track of the different root to node values as a string + List rootToLeafs = new ArrayList(); + + public int sumNumbers(TreeNode root) { + // track the sum that we want to add + int solution = 0; + + String currentPath = ""; + + // execute a dfs to find the leaf nodes + findLeafNodes(root, currentPath); + + // loop through all the paths, convert to int, add to solution + for(String curr:rootToLeafs){ + // save the current string as an integer + int currentVal = Integer.parseInt(curr); + + // add the current value to the solution + solution+=currentVal; + } + + // return the solution + return solution; + } + + // dfs method + public void findLeafNodes(TreeNode node, String currentPath){ + // base case, if no node then return + if(node==null){ + return; + } + + // add the current node value to the currentPath string + currentPath+=Integer.toString(node.val); + + // check the left most value + findLeafNodes(node.left, currentPath); + + // check the right most value + findLeafNodes(node.right, currentPath); + + // if we are at a non-null node, check if it is a leaf + if(node.left==null && node.right==null){ + // add the currentPath to the arraylist + rootToLeafs.add(currentPath); + } + } +} \ No newline at end of file diff --git a/java/0130-surrounded-regions.java b/java/0130-surrounded-regions.java new file mode 100644 index 000000000..aadd2c75c --- /dev/null +++ b/java/0130-surrounded-regions.java @@ -0,0 +1,40 @@ +class Solution { + + public void solve(char[][] board) { + int nRows = board.length; + int nCols = board[0].length; + + // 1a) Capture unsurrounded regions - top and bottom row (O -> T) + for (int i = 0; i < nCols; i++) { + if (board[0][i] == 'O') dfs(board, 0, i); + if (board[nRows - 1][i] == 'O') dfs(board, nRows - 1, i); + } + + // 1b) Capture unsurrounded regions - Left and right columns (O -> T) + for (int i = 0; i < nRows; i++) { + if (board[i][0] == 'O') dfs(board, i, 0); + if (board[i][nCols - 1] == 'O') dfs(board, i, nCols - 1); + } + + for (int r = 0; r < nRows; r++) { + for (int c = 0; c < nCols; c++) { + if (board[r][c] == 'O') board[r][c] = 'X'; // 2) Capture surrounded regions (O -> X) + if (board[r][c] == 'T') board[r][c] = 'O'; // 3) Uncapture unsurrounded regions (T- O) + } + } + } + + private void dfs(char[][] board, int r, int c) { + int nRows = board.length; + int nCols = board[0].length; + if ( + r < 0 || c < 0 || r >= nRows || c >= nCols || board[r][c] != 'O' + ) return; + + board[r][c] = 'T'; + dfs(board, r + 1, c); + dfs(board, r - 1, c); + dfs(board, r, c + 1); + dfs(board, r, c - 1); + } +} diff --git a/java/0131-palindrome-partitioning.java b/java/0131-palindrome-partitioning.java new file mode 100644 index 000000000..e81a155d5 --- /dev/null +++ b/java/0131-palindrome-partitioning.java @@ -0,0 +1,26 @@ +public class Solution { + + public List> partition(String s) { + List> res = new ArrayList>(); + if (s.equals("")) { + res.add(new ArrayList()); + return res; + } + for (int i = 0; i < s.length(); i++) { + if (isPalindrome(s, i + 1)) { + for (List list : partition(s.substring(i + 1))) { + list.add(0, s.substring(0, i + 1)); + res.add(list); + } + } + } + return res; + } + + public boolean isPalindrome(String s, int n) { + for (int i = 0; i < n / 2; i++) { + if (s.charAt(i) != s.charAt(n - i - 1)) return false; + } + return true; + } +} diff --git a/java/0133-clone-graph.java b/java/0133-clone-graph.java new file mode 100644 index 000000000..2329afb1e --- /dev/null +++ b/java/0133-clone-graph.java @@ -0,0 +1,15 @@ +class Solution { + + public HashMap map = new HashMap<>(); + + public Node cloneGraph(Node node) { + if (node == null) return null; + if (map.containsKey(node.val)) return map.get(node.val); + Node newNode = new Node(node.val, new ArrayList()); + map.put(node.val, newNode); + for (Node neighbor : node.neighbors) { + newNode.neighbors.add(cloneGraph(neighbor)); + } + return newNode; + } +} diff --git a/java/0134-gas-station.java b/java/0134-gas-station.java new file mode 100644 index 000000000..d41e541be --- /dev/null +++ b/java/0134-gas-station.java @@ -0,0 +1,19 @@ +class Solution { + + public int canCompleteCircuit(int[] gas, int[] cost) { + int sGas = 0, sCost = 0, res = 0, total = 0; + for (int i = 0; i < gas.length; i++) { + sGas += gas[i]; + sCost += cost[i]; + } + if (sGas < sCost) return -1; + for (int i = 0; i < gas.length; i++) { + total += gas[i] - cost[i]; + if (total < 0) { + total = 0; + res = i + 1; + } + } + return res; + } +} diff --git a/java/0135-candy.java b/java/0135-candy.java new file mode 100644 index 000000000..58a2cfa90 --- /dev/null +++ b/java/0135-candy.java @@ -0,0 +1,20 @@ +class Solution { + public int candy(int[] ratings) { + int[] candies = new int[ratings.length]; + Arrays.fill(candies, 1); + + for(int i = 1; i < ratings.length; i++){ + if(ratings[i-1] < ratings[i]) + candies[i] = candies[i-1] + 1; + } + for(int i = ratings.length-2; i >= 0; i--){ + if(ratings[i+1] < ratings[i]) + candies[i] = Math.max(candies[i], candies[i+1] + 1); + } + int res = 0; + for(int n : candies) + res += n; + + return res; + } +} diff --git a/java/0136-single-number.java b/java/0136-single-number.java new file mode 100644 index 000000000..0a44ded80 --- /dev/null +++ b/java/0136-single-number.java @@ -0,0 +1,9 @@ +//We can use xor operation as it cancel out itself (i.e. only when values are different in binary representation then give output). See how xor operation works if confused. +class Solution { + + public int singleNumber(int[] nums) { + int ans = nums[0]; + for (int i = 1; i < nums.length; i++) ans ^= nums[i]; + return ans; + } +} diff --git a/java/0138-copy-list-with-random-pointer.java b/java/0138-copy-list-with-random-pointer.java new file mode 100644 index 000000000..dd75285d4 --- /dev/null +++ b/java/0138-copy-list-with-random-pointer.java @@ -0,0 +1,18 @@ +class Solution { + + public Node copyRandomList(Node head) { + Node cur = head; + HashMap map = new HashMap<>(); + while (cur != null) { + map.put(cur, new Node(cur.val)); + cur = cur.next; + } + cur = head; + while (cur != null) { + map.get(cur).next = map.get(cur.next); + map.get(cur).random = map.get(cur.random); + cur = cur.next; + } + return map.get(head); + } +} diff --git a/java/0139-word-break.java b/java/0139-word-break.java new file mode 100644 index 000000000..4d47c0af0 --- /dev/null +++ b/java/0139-word-break.java @@ -0,0 +1,18 @@ +class Solution { + public boolean wordBreak(String s, List wordDict) { + boolean[] dp = new boolean[s.length() + 1]; + dp[s.length()] = true; + + for(int i = s.length()-1; i >= 0; i--){ + for(String w: wordDict){ + if((i + w.length()) <= s.length() && s.startsWith(w, i)){ + dp[i] = dp[i + w.length()]; + } + if(dp[i]){ + break; + } + } + } + return dp[0]; + } +} diff --git a/java/0141-linked-list-cycle.java b/java/0141-linked-list-cycle.java new file mode 100644 index 000000000..6388d2dfe --- /dev/null +++ b/java/0141-linked-list-cycle.java @@ -0,0 +1,15 @@ +//Fast and slow pointer + +public class Solution { + + public boolean hasCycle(ListNode head) { + ListNode fast = head; + ListNode slow = head; + while (fast != null && fast.next != null) { + fast = fast.next.next; + slow = slow.next; + if (fast == slow) return true; + } + return false; + } +} diff --git a/java/0142-linked-list-cycle-ii.java b/java/0142-linked-list-cycle-ii.java new file mode 100644 index 000000000..12dedf413 --- /dev/null +++ b/java/0142-linked-list-cycle-ii.java @@ -0,0 +1,24 @@ +public class Solution { + public ListNode detectCycle(ListNode head) { + ListNode slow = head; + ListNode fast = head; + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + if (slow == fast) { + break; + } + } + + if (fast == null || fast.next == null) { + return null; + } + + ListNode slow2 = head; + while (slow != slow2) { + slow = slow.next; + slow2 = slow2.next; + } + return slow; + } +} diff --git a/java/0143-reorder-list.java b/java/0143-reorder-list.java new file mode 100644 index 000000000..b93a19fd9 --- /dev/null +++ b/java/0143-reorder-list.java @@ -0,0 +1,45 @@ +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +class Solution { + + public void reorderList(ListNode head) { + + //Find middle of list using a slow and fast pointer approach + ListNode slow = head; + ListNode fast = head.next; + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + } + + //Reverse the second half of the list using a tmp variable + ListNode second = slow.next; + ListNode prev = slow.next = null; + while (second != null) { + ListNode tmp = second.next; + second.next = prev; + prev = second; + second = tmp; + } + + //Re-assign the pointers to match the pattern + ListNode first = head; + second = prev; + while (second != null) { + ListNode tmp1 = first.next; + ListNode tmp2 = second.next; + first.next = second; + second.next = tmp1; + first = tmp1; + second = tmp2; + } + } +} diff --git a/java/0144-binary-tree-preorder-traversal.java b/java/0144-binary-tree-preorder-traversal.java new file mode 100644 index 000000000..8662967b3 --- /dev/null +++ b/java/0144-binary-tree-preorder-traversal.java @@ -0,0 +1,39 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + + static void preOrder(TreeNode root, List res){ + + + if(root == null){ + return; + } + res.add(root.val); + preOrder(root.left,res); + preOrder(root.right,res); + + } + + public List preorderTraversal(TreeNode root) { + + List res = new ArrayList<>(); + + preOrder(root,res); + + return res; + + } +} \ No newline at end of file diff --git a/java/0145-binary-tree-postorder-traversal.java b/java/0145-binary-tree-postorder-traversal.java new file mode 100644 index 000000000..e87a4d314 --- /dev/null +++ b/java/0145-binary-tree-postorder-traversal.java @@ -0,0 +1,46 @@ +class Solution { + // Iterative + public List postorderTraversal(TreeNode root) { + Stack stack = new Stack<>(); + stack.add(root); + Stack visit = new Stack<>(); + visit.add(false); + + List res = new ArrayList<>(); + + while(!stack.isEmpty()){ + TreeNode curr=stack.pop(); + boolean v = visit.pop(); + if(curr != null){ + if(v != false){ + res.add(curr.val); + }else{ + stack.add(curr); + visit.add(true); + stack.add(curr.right); + visit.add(false); + stack.add(curr.left); + visit.add(false); + } + } + } + return res; + } +} + +class Solution { + // Recursive + public void postorder(TreeNode root, List res){ + if(root == null) return; + + postorder(root.left, res); + postorder(root.right, res); + res.add(root.val); + } + public List postorderTraversal(TreeNode root) { + List res = new ArrayList<>(); + + postorder(root, res); + return res; + } +} diff --git a/java/0146-lru-cache.java b/java/0146-lru-cache.java new file mode 100644 index 000000000..376e48aec --- /dev/null +++ b/java/0146-lru-cache.java @@ -0,0 +1,79 @@ +class LRUCache { + + private Map cache; + private int capacity; + + private Node left; + private Node right; + + public LRUCache(int capacity) { + this.capacity = capacity; + cache = new HashMap<>(); + + //left = LRU , right = most recent + this.left = new Node(0, 0); + this.right = new Node(0, 0); + this.left.next = this.right; + this.right.prev = this.left; + } + + public int get(int key) { + if (cache.containsKey(key)) { + remove(cache.get(key)); + insert(cache.get(key)); + return cache.get(key).val; + } else { + return -1; + } + } + + public void put(int key, int value) { + if (cache.containsKey(key)) { + remove(cache.get(key)); + } + cache.put(key, new Node(key, value)); + insert(cache.get(key)); + + if (cache.size() > capacity) { + // remove from the list and delte the LRU from the hashmap + Node lru = this.left.next; + remove(lru); + cache.remove(lru.key); + } + } + + // remove node from list + public void remove(Node node) { + Node prev = node.prev; + Node next = node.next; + + prev.next = next; + next.prev = prev; + } + + // insert node at right + public void insert(Node node) { + Node prev = this.right.prev; + Node next = this.right; + + prev.next = node; + next.prev = node; + + node.next = next; + node.prev = prev; + } + + private class Node { + + private int key; + private int val; + + Node next; + Node prev; + + public Node(int key, int val) { + this.key = key; + this.val = val; + } + } +} diff --git a/java/0147-insertion-sort-list.java b/java/0147-insertion-sort-list.java new file mode 100644 index 000000000..57566d040 --- /dev/null +++ b/java/0147-insertion-sort-list.java @@ -0,0 +1,27 @@ +class Solution { + public ListNode insertionSortList(ListNode head) { + ListNode dummy = new ListNode(0, head); + + ListNode prev = head; + ListNode cur = head.next; + while (cur != null) { + if (prev.val <= cur.val) { + prev = cur; + cur = cur.next; + } else { + ListNode elem = dummy; + while (elem.next.val < cur.val) { + elem = elem.next; + } + + prev.next = cur.next; + cur.next = elem.next; + elem.next = cur; + + cur = prev.next; + } + } + + return dummy.next; + } +} diff --git a/java/0148-sort-list.java b/java/0148-sort-list.java new file mode 100644 index 000000000..205480d3d --- /dev/null +++ b/java/0148-sort-list.java @@ -0,0 +1,81 @@ +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode() {} + * ListNode(int val) { this.val = val; } + * ListNode(int val, ListNode next) { this.val = val; this.next = next; } + * } + */ +class Solution { + // Merge Sort Implementation + public ListNode getMid(ListNode head){ + ListNode slow=head, fast=head.next; + while(fast != null && fast.next != null){ + fast = fast.next.next; + slow = slow.next; + } + return slow; + } + public ListNode merge(ListNode left, ListNode right){ + ListNode dummy = new ListNode(); + ListNode tail = dummy; + + while(left != null && right != null){ + if(left.val < right.val){ + tail.next = left; + left = left.next; + }else{ + tail.next = right; + right = right.next; + } + tail = tail.next; + } + if(left != null){ + tail.next = left; + } + if(right != null){ + tail.next = right; + } + return dummy.next; + } + public ListNode sortList(ListNode head) { + if(head == null || head.next == null){ + return head; + } + + // Split the list in 2 halfs + ListNode left = head; + ListNode right = getMid(head); + ListNode tmp = right.next; + right.next = null; + right = tmp; + + left = sortList(left); + right = sortList(right); + return merge(left, right); + } +} + +// Using a Heap to sort the list +class Solution { + public ListNode sortList(ListNode head) { + if(head == null || head.next == null){ + return head; + } + PriorityQueue queue = new PriorityQueue<>(); + ListNode temp = head; + while(temp.next!=null){ + queue.add(temp.val); + temp = temp.next; + } + queue.add(temp.val); + temp = head; + while(!queue.isEmpty()){ + temp.val = queue.poll(); + temp = temp.next; + } + return head; + } +} diff --git a/java/0149-max-points-on-a-line.java b/java/0149-max-points-on-a-line.java new file mode 100644 index 000000000..660794067 --- /dev/null +++ b/java/0149-max-points-on-a-line.java @@ -0,0 +1,23 @@ +class Solution { + public int maxPoints(int[][] points) { + int res = 1; + for (int i = 0; i < points.length; i++) { + int[] p1 = points[i]; + Map counter = new HashMap<>(); + for (int j = i + 1; j < points.length; j++) { + int[] p2 = points[j]; + double slope; + if (p1[0] == p2[0]) { + slope = Double.MAX_VALUE; + } else if (p1[1] == p2[1]) { + slope = 0; + } else { + slope = (double) (p2[1] - p1[1]) / (double) (p2[0] - p1[0]); + } + counter.put(slope, counter.getOrDefault(slope, 0) + 1); + res = Math.max(res, counter.get(slope) + 1); + } + } + return res; + } +} diff --git a/java/0150-evaluate-reverse-polish-notation.java b/java/0150-evaluate-reverse-polish-notation.java new file mode 100644 index 000000000..67e3ed5bf --- /dev/null +++ b/java/0150-evaluate-reverse-polish-notation.java @@ -0,0 +1,24 @@ +class Solution { + + public int evalRPN(String[] tokens) { + Stack stack = new Stack<>(); + for (String token : tokens) { + if (token.equals("+")) { + stack.add(stack.pop() + stack.pop()); + } else if (token.equals("-")) { + int a = stack.pop(); + int b = stack.pop(); + stack.add(b - a); + } else if (token.equals("*")) { + stack.add(stack.pop() * stack.pop()); + } else if (token.equals("/")) { + int a = stack.pop(); + int b = stack.pop(); + stack.add(b / a); + } else { + stack.add(Integer.parseInt(token)); + } + } + return stack.pop(); + } +} diff --git a/java/0152-maximum-product-subarray.java b/java/0152-maximum-product-subarray.java new file mode 100644 index 000000000..77810d399 --- /dev/null +++ b/java/0152-maximum-product-subarray.java @@ -0,0 +1,18 @@ +class Solution { + + public int maxProduct(int[] nums) { + if (nums.length == 1) return nums[0]; + + int res = nums[0]; + int max = 1; + int min = 1; + + for (int n : nums) { + int tmp = max * n; + max = Math.max(n, Math.max(tmp, min * n)); + min = Math.min(n, Math.min(tmp, min * n)); + res = Math.max(res, max); + } + return res; + } +} diff --git a/java/0153-find-minimum-in-rotated-sorted-array.java b/java/0153-find-minimum-in-rotated-sorted-array.java new file mode 100644 index 000000000..e48dde2be --- /dev/null +++ b/java/0153-find-minimum-in-rotated-sorted-array.java @@ -0,0 +1,21 @@ +class Solution { + + public int findMin(int[] nums) { + int l = 0; + int r = nums.length - 1; + + while (l <= r) { + if (nums[l] <= nums[r]) { + return nums[l]; + } + + int mid = (l + r) / 2; + if (nums[mid] >= nums[l]) { + l = mid + 1; + } else { + r = mid; + } + } + return 0; + } +} diff --git a/java/0155-min-stack.java b/java/0155-min-stack.java new file mode 100644 index 000000000..d2d33c71d --- /dev/null +++ b/java/0155-min-stack.java @@ -0,0 +1,39 @@ +class MinStack { + + private Stack stack; + private Stack minStack; + + public MinStack() { + stack = new Stack<>(); + minStack = new Stack<>(); + } + + public void push(int val) { + stack.push(val); + + // The min stack may be empty, so we need to check it + val = Math.min(val, minStack.isEmpty() ? val : minStack.peek()); + minStack.push(val); + } + + public void pop() { + stack.pop(); + minStack.pop(); + } + + public int top() { + return stack.peek(); + } + + public int getMin() { + return minStack.peek(); + } +} +/** + * Your MinStack object will be instantiated and called as such: + * MinStack obj = new MinStack(); + * obj.push(val); + * obj.pop(); + * int param_3 = obj.top(); + * int param_4 = obj.getMin(); + */ diff --git a/java/0160-intersection-of-two-linked-lists.java b/java/0160-intersection-of-two-linked-lists.java new file mode 100644 index 000000000..c5b1e7f98 --- /dev/null +++ b/java/0160-intersection-of-two-linked-lists.java @@ -0,0 +1,21 @@ +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ +public class Solution { + public ListNode getIntersectionNode(ListNode headA, ListNode headB) { + ListNode a = headA, b = headB; + while (a != b) { + a = (a != null) ? a.next : headB; + b = (b != null) ? b.next : headA; + } + return a; + } +} \ No newline at end of file diff --git a/java/0162-find-peak-element.java b/java/0162-find-peak-element.java new file mode 100644 index 000000000..8e61c7001 --- /dev/null +++ b/java/0162-find-peak-element.java @@ -0,0 +1,14 @@ +class Solution { + public int findPeakElement(int[] nums) { + int l = 0, r = nums.length-1; + while(l<=r){ + int m = l + (r-l)/2; + int left = (m-1==-1)?Integer.MIN_VALUE:nums[m-1]; + int right = (m+1==nums.length)?Integer.MIN_VALUE:nums[m+1]; + if(nums[m]>left && nums[m]>right) return m; + else if(nums[m] v_2) { + return 1; + } + } + return 0; + } +} diff --git a/java/0167-two-sum-ii-input-array-is-sorted.java b/java/0167-two-sum-ii-input-array-is-sorted.java new file mode 100644 index 000000000..fcdcccc81 --- /dev/null +++ b/java/0167-two-sum-ii-input-array-is-sorted.java @@ -0,0 +1,24 @@ +class Solution { + + public int[] twoSum(int[] numbers, int target) { + int a_pointer = 0; + int b_pointer = numbers.length - 1; + int num_a, num_b; + + while (a_pointer < b_pointer) { + num_a = numbers[a_pointer]; + num_b = numbers[b_pointer]; + + if (num_a + num_b == target) break; + + if (num_a + num_b < target) { + a_pointer++; + continue; + } + + b_pointer--; + } + + return new int[] { a_pointer + 1, b_pointer + 1 }; + } +} diff --git a/java/0168-excel-sheet-column-title.java b/java/0168-excel-sheet-column-title.java new file mode 100644 index 000000000..7826a2beb --- /dev/null +++ b/java/0168-excel-sheet-column-title.java @@ -0,0 +1,21 @@ +class Solution { + public String convertToTitle(int columnNumber) { + Map map = new HashMap<>(); + char c = 'A'; + + for (int i = 1; i <= 26; i++) { + map.put(i, c); + c++; + } + + StringBuilder res = new StringBuilder(); + + while (columnNumber > 0) { + int r = (columnNumber - 1) % 26; + res.insert(0, map.get(r + 1)); + columnNumber = (columnNumber - 1) / 26; + } + + return res.toString(); + } +} \ No newline at end of file diff --git a/java/0169-majority-element.java b/java/0169-majority-element.java new file mode 100644 index 000000000..124f472ce --- /dev/null +++ b/java/0169-majority-element.java @@ -0,0 +1,13 @@ +class Solution { + public int majorityElement(int[] nums) { + int res = 0, count = 0; + + for(int n: nums) { + if(count == 0) + res = n; + count += (n == res? 1: -1); + } + + return res; + } +} diff --git a/java/0173-binary-search-tree-iterator.java b/java/0173-binary-search-tree-iterator.java new file mode 100644 index 000000000..56f0e6c98 --- /dev/null +++ b/java/0173-binary-search-tree-iterator.java @@ -0,0 +1,32 @@ +class BSTIterator { + + TreeNode iterator; + Queue traversal; + + public BSTIterator(TreeNode root) { + iterator = root; + traversal = new ArrayDeque<>(); + fillStack(iterator); + } + + public void fillStack(TreeNode iterator){ + if (iterator.left != null) { + fillStack(iterator.left); + } + traversal.add(iterator.val); + if (iterator.right != null) { + fillStack(iterator.right); + } + } + + public int next() { + while (!traversal.isEmpty()) { + return traversal.poll(); + } + return -1; + } + + public boolean hasNext() { + return !traversal.isEmpty(); + } +} \ No newline at end of file diff --git a/java/0179-largest-number.java b/java/0179-largest-number.java new file mode 100644 index 000000000..6a1d8c80d --- /dev/null +++ b/java/0179-largest-number.java @@ -0,0 +1,26 @@ +class Solution { + public String largestNumber(int[] nums) { + String[] arr = new String[nums.length]; + + for(int i=0; i() { + @Override + public int compare(String s1, String s2) { + String n1 = s1 + s2, n2 = s2 + s1; + return n2.compareTo(n1); + } + }); + + if(arr[0].equals("0")) return "0"; + + StringBuilder sb = new StringBuilder(); + for(String s : arr) { + sb.append(s); + } + + return sb.toString(); + } +} \ No newline at end of file diff --git a/java/0187-repeated-dna-sequences.java b/java/0187-repeated-dna-sequences.java new file mode 100644 index 000000000..594bbbd17 --- /dev/null +++ b/java/0187-repeated-dna-sequences.java @@ -0,0 +1,17 @@ +class Solution { + + public List findRepeatedDnaSequences(String s) { + HashSet set = new HashSet<>(); + int start = 0; + HashSet ans = new HashSet<>(); + for (int end = 10; end <= s.length(); end++) { + if (set.contains(s.substring(start, end))) ans.add( + s.substring(start, end) + ); + set.add(s.substring(start, end)); + start++; + } + List list = new ArrayList<>(ans); + return list; + } +} diff --git a/java/0189-rotate-array.java b/java/0189-rotate-array.java new file mode 100644 index 000000000..6fd9ac12e --- /dev/null +++ b/java/0189-rotate-array.java @@ -0,0 +1,32 @@ +class Solution { + public void rotate(int[] nums, int k) { + //Do not return anything, modify nums in-place instead + k = k % nums.length; + int l = 0, r = nums.length - 1; + while(l < r) { + int tmp = nums[l]; + nums[l] = nums[r]; + nums[r] = tmp; + l += 1; + r -= 1; + } + l = 0; + r = k - 1; + while(l < r) { + int tmp = nums[l]; + nums[l] = nums[r]; + nums[r] = tmp; + l += 1; + r -= 1; + } + l = k; + r = nums.length - 1; + while(l < r) { + int tmp = nums[l]; + nums[l] = nums[r]; + nums[r] = tmp; + l += 1; + r -= 1; + } + } +} diff --git a/java/0190-reverse-bits.java b/java/0190-reverse-bits.java new file mode 100644 index 000000000..e904673e6 --- /dev/null +++ b/java/0190-reverse-bits.java @@ -0,0 +1,14 @@ +public class Solution { + + // you need treat n as an unsigned value + public int reverseBits(int n) { + int ans = 0; + + for (int i = 0; i < 32; i++) { + ans <<= 1; + ans |= (n & 1); + n >>= 1; + } + return ans; + } +} diff --git a/java/0191-number-of-1-bits.java b/java/0191-number-of-1-bits.java new file mode 100644 index 000000000..55897bf62 --- /dev/null +++ b/java/0191-number-of-1-bits.java @@ -0,0 +1,30 @@ +//standard way +public class Solution { + + // you need to treat n as an unsigned value + public int hammingWeight(int n) { + int count = 0; + int mask = 1; + for (int i = 0; i < 32; i++) { + if ((n & mask) != 0) { + count++; + } + n >>= 1; + } + return count; + } +} + +//smart way +public class Solution { + + // you need to treat n as an unsigned value + public int hammingWeight(int n) { + int count = 0; + while (n != 0) { + n = n & (n - 1); + count++; + } + return count; + } +} diff --git a/java/0198-house-robber.java b/java/0198-house-robber.java new file mode 100644 index 000000000..7418aa016 --- /dev/null +++ b/java/0198-house-robber.java @@ -0,0 +1,49 @@ +class Solution { + + public int rob(int[] nums) { + if (nums == null || nums.length == 0) return 0; + if (nums.length == 1) return nums[0]; + if (nums.length == 2) return Math.max(nums[0], nums[1]); + + int robWithOutIncludingLastHouse = 0, robWithIncludingLastHouse = 0; + + for (int n : nums) { + int temp = Math.max( + robWithOutIncludingLastHouse + n, + robWithIncludingLastHouse + ); + robWithOutIncludingLastHouse = robWithIncludingLastHouse; + robWithIncludingLastHouse = temp; + } + return robWithIncludingLastHouse; + } + + public int robDP(int[] nums) { + if (nums == null || nums.length == 0) return 0; + if (nums.length == 1) return nums[0]; + if (nums.length == 2) return Math.max(nums[0], nums[1]); + + int[] dp = new int[nums.length]; + dp[0] = nums[0]; + dp[1] = Math.max(nums[0], nums[1]); + + for (int i = 2; i < nums.length; i++) { + dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]); + } + return dp[nums.length - 1]; + } + + // DP with O(1) space + public int robDP2(int[] nums) { + if (nums == null || nums.length == 0) return 0; + + int dp0 = 0, dp1 = 0, curr; + + for (int i = 0; i < nums.length; i++) { + curr = Math.max(dp0 + nums[i], dp1); + dp0 = dp1; + dp1 = curr; + } + return Math.max(dp0, dp1); + } +} diff --git a/java/0199-binary-tree-right-side-view.java b/java/0199-binary-tree-right-side-view.java new file mode 100644 index 000000000..470c98c9f --- /dev/null +++ b/java/0199-binary-tree-right-side-view.java @@ -0,0 +1,23 @@ +class Solution { + + public List rightSideView(TreeNode root) { + List list = new ArrayList(); + if (root == null) return list; + bfs(list, root); + return list; + } + + public void bfs(List list, TreeNode root) { + Queue q = new LinkedList<>(); + q.offer(root); + while (!q.isEmpty()) { + int levelSize = q.size(); + for (int i = 0; i < levelSize; i++) { + TreeNode cur = q.poll(); + if (i == 0) list.add(cur.val); + if (cur.right != null) q.offer(cur.right); + if (cur.left != null) q.offer(cur.left); + } + } + } +} diff --git a/java/0200-number-of-islands.java b/java/0200-number-of-islands.java new file mode 100644 index 000000000..1c48f4e8f --- /dev/null +++ b/java/0200-number-of-islands.java @@ -0,0 +1,32 @@ +class Solution { + + public int numIslands(char[][] grid) { + int count = 0; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (grid[i][j] == '1') { + dfs(grid, i, j); + count++; + } + } + } + return count; + } + + public void dfs(char[][] grid, int i, int j) { + if ( + i < 0 || + j < 0 || + i >= grid.length || + j >= grid[0].length || + grid[i][j] == '0' + ) { + return; + } + grid[i][j] = '0'; + dfs(grid, i + 1, j); + dfs(grid, i, j + 1); + dfs(grid, i - 1, j); + dfs(grid, i, j - 1); + } +} diff --git a/java/0201-bitwise-and-of-numbers-range.java b/java/0201-bitwise-and-of-numbers-range.java new file mode 100644 index 000000000..a01954cfe --- /dev/null +++ b/java/0201-bitwise-and-of-numbers-range.java @@ -0,0 +1,11 @@ +class Solution { + public int rangeBitwiseAnd(int left, int right) { + int i = 0; + while(left != right){ + left = left >> 1; + right = right >> 1; + i += 1; + } + return left << i; + } +} diff --git a/java/0202-happy-number.java b/java/0202-happy-number.java new file mode 100644 index 000000000..8942da63a --- /dev/null +++ b/java/0202-happy-number.java @@ -0,0 +1,34 @@ +class Solution { + + public boolean isHappy(int n) { + if (n == 1 || n == -1) { + return true; + } + + Set visit = new HashSet(); + + // compute square until getting duplicate value + while (!visit.contains(n)) { + visit.add(n); + // using helper function to compute the sum of squares + n = sumOfSquare(n); + + if (n == 1) return true; + } + + return false; + } + + public int sumOfSquare(int n) { + int output = 0; + + while (n != 0) { + int digit = n % 10; + digit = digit * digit; + output += digit; + n = n / 10; + } + + return output; + } +} diff --git a/java/0203-remove-linked-list-elements.java b/java/0203-remove-linked-list-elements.java new file mode 100644 index 000000000..f742a3012 --- /dev/null +++ b/java/0203-remove-linked-list-elements.java @@ -0,0 +1,17 @@ +class Solution { + public ListNode removeElements(ListNode head, int val) { + ListNode dummy = new ListNode(0, head); + ListNode prev = dummy, curr = head; + while(curr != null) { + ListNode nxt = curr.next; + + if(curr.val == val) + prev.next = nxt; + else + prev = curr; + + curr = nxt; + } + return dummy.next; + } +} diff --git a/java/0205-isomorphic-strings.java b/java/0205-isomorphic-strings.java new file mode 100644 index 000000000..98a60008f --- /dev/null +++ b/java/0205-isomorphic-strings.java @@ -0,0 +1,20 @@ +class Solution { + public boolean isIsomorphic(String s, String t) { + HashMap mapS = new HashMap<>(); + HashMap mapT = new HashMap<>(); + + int s1=0, t1=0; + + while(s1 < s.length() && t1 < t.length()){ + if((mapS.containsKey(s.charAt(s1)) && mapS.get(s.charAt(s1)) != t.charAt(t1)) || + (mapT.containsKey(t.charAt(t1)) && mapT.get(t.charAt(t1)) != s.charAt(s1))){ + return false; + } + mapS.put(s.charAt(s1), t.charAt(t1)); + mapT.put(t.charAt(t1), s.charAt(s1)); + s1 += 1; + t1 += 1; + } + return true; + } +} diff --git a/java/0205_isomorphic_strings b/java/0205_isomorphic_strings new file mode 100644 index 000000000..22e2b9ffd --- /dev/null +++ b/java/0205_isomorphic_strings @@ -0,0 +1,19 @@ +class Solution { + public boolean isIsomorphic(String s, String t) { + HashMap mapST = new HashMap(); + HashMap mapTS = new HashMap(); + + for(int i = 0; i < s.length(); i++) { + char c1 = s.charAt(i); + char c2 = t.charAt(i); + + if(mapST.containsKey(c1) && !mapST.get(c1).equals(c2) || mapTS.containsKey(c2) && !mapTS.get(c2).equals(c1)) { + return false; + } + + mapST.put(c1,c2); + mapTS.put(c2,c1); + } + return true; + } +} diff --git a/java/0206-reverse-linked-list.java b/java/0206-reverse-linked-list.java new file mode 100644 index 000000000..18e332dae --- /dev/null +++ b/java/0206-reverse-linked-list.java @@ -0,0 +1,34 @@ +//Use three pointers and so you can change the next of the mid to the first one without losing the track of the original left. +//Iterative version +class Solution { + + public ListNode reverseList(ListNode head) { + ListNode current = head; + ListNode previous = null; + ListNode nextCurrent = null; + + while (current != null) { + nextCurrent = current.next; + current.next = previous; + previous = current; + current = nextCurrent; + } + + return previous; + } +} + +//Recursive version +class Solution { + + public ListNode reverseList(ListNode head) { + return rev(head, null); + } + + public ListNode rev(ListNode node, ListNode pre) { + if (node == null) return pre; + ListNode temp = node.next; + node.next = pre; + return rev(temp, node); + } +} diff --git a/java/0207-course-schedule.java b/java/0207-course-schedule.java new file mode 100644 index 000000000..e993e5a39 --- /dev/null +++ b/java/0207-course-schedule.java @@ -0,0 +1,40 @@ +class Solution { + + public boolean canFinish(int numCourses, int[][] prerequisites) { + List> adj = new ArrayList<>(); + for (int i = 0; i < numCourses; i++) { + adj.add(new ArrayList<>()); + } + + for (int i = 0; i < prerequisites.length; i++) { + adj.get(prerequisites[i][0]).add(prerequisites[i][1]); + } + + int[] visited = new int[numCourses]; + for (int i = 0; i < numCourses; i++) { + if (visited[i] == 0) { + if (isCyclic(adj, visited, i)) { + return false; + } + } + } + return true; + } + + private boolean isCyclic(List> adj, int[] visited, int curr) { + if (visited[curr] == 2) { + return true; + } + + visited[curr] = 2; + for (int i = 0; i < adj.get(curr).size(); i++) { + if (visited[adj.get(curr).get(i)] != 1) { + if (isCyclic(adj, visited, adj.get(curr).get(i))) { + return true; + } + } + } + visited[curr] = 1; + return false; + } +} diff --git a/java/0208-implement-trie-prefix-tree.java b/java/0208-implement-trie-prefix-tree.java new file mode 100644 index 000000000..7114aeeaa --- /dev/null +++ b/java/0208-implement-trie-prefix-tree.java @@ -0,0 +1,56 @@ +class Trie { + + Node root; + + public Trie() { + root = new Node('\0'); //dummy node + } + + public void insert(String word) { + Node curr = root; + for (char x : word.toCharArray()) { + if (curr.children[x - 'a'] == null) { + curr.children[x - 'a'] = new Node(x); + } + curr = curr.children[x - 'a']; + } + curr.isWord = true; + } + + public boolean search(String word) { + Node res = getLast(word); + return (res != null && res.isWord); + } + + //helper method + public Node getLast(String word) { + Node curr = root; + for (char x : word.toCharArray()) { + if (curr.children[x - 'a'] == null) { + return null; + } + + curr = curr.children[x - 'a']; + } + return curr; + } + + public boolean startsWith(String prefix) { + Node res = getLast(prefix); + if (res == null) return false; + return true; + } + + class Node { + + private char value; + private boolean isWord; + private Node[] children; + + public Node(char val) { + this.value = val; + this.isWord = false; + this.children = new Node[26]; + } + } +} diff --git a/java/0209-minimum-size-subarray-sum.java b/java/0209-minimum-size-subarray-sum.java new file mode 100644 index 000000000..e9614e0d9 --- /dev/null +++ b/java/0209-minimum-size-subarray-sum.java @@ -0,0 +1,17 @@ +class Solution { + + public int minSubArrayLen(int target, int[] nums) { + int l = 0, total = 0; + int res = Integer.MAX_VALUE; + + for (int r = 0; r < nums.length; r++) { + total += nums[r]; + while (total >= target) { + res = Math.min(r - l + 1, res); + total -= nums[l++]; + } + } + + return res == Integer.MAX_VALUE ? 0 : res; + } +} diff --git a/java/0210-course-schedule-ii.java b/java/0210-course-schedule-ii.java new file mode 100644 index 000000000..775a14e8d --- /dev/null +++ b/java/0210-course-schedule-ii.java @@ -0,0 +1,51 @@ +class Solution { + + public int[] findOrder(int numCourses, int[][] prerequisites) { + Map> adjList = new HashMap>(); + int[] indegree = new int[numCourses]; + int[] topologicalOrder = new int[numCourses]; + + //creating the adjlist + for (int i = 0; i < prerequisites.length; i++) { + int post = prerequisites[i][0]; + int pre = prerequisites[i][1]; + List lst = adjList.getOrDefault( + pre, + new ArrayList() + ); + lst.add(post); + adjList.put(pre, lst); + + indegree[post] += 1; + } + + Queue q = new LinkedList(); + for (int i = 0; i < numCourses; i++) { + if (indegree[i] == 0) { + q.add(i); + } + } + + int i = 0; + while (!q.isEmpty()) { + int node = q.remove(); + topologicalOrder[i++] = node; + + if (adjList.containsKey(node)) { + for (Integer neighbor : adjList.get(node)) { + indegree[neighbor]--; + + if (indegree[neighbor] == 0) { + q.add(neighbor); + } + } + } + } + + if (i == numCourses) { + return topologicalOrder; + } + + return new int[0]; + } +} diff --git a/java/0211-design-add-and-search-words-data-structure.java b/java/0211-design-add-and-search-words-data-structure.java new file mode 100644 index 000000000..2b3610447 --- /dev/null +++ b/java/0211-design-add-and-search-words-data-structure.java @@ -0,0 +1,73 @@ +/** + * Your WordDictionary object will be instantiated and called as such: + * WordDictionary obj = new WordDictionary(); + * obj.addWord(word); + * boolean param_2 = obj.search(word); + */ + +class WordDictionary { + + Node root; + + private class Node { + + char value; + boolean isWord; + Node[] children; + + public Node(char value) { + this.value = value; + isWord = false; + children = new Node[26]; + } + } + + public WordDictionary() { + root = new Node('\0'); + } + + public void addWord(String word) { + Node curr = root; + + for (int i = 0; i < word.length(); i++) { + char ch = word.charAt(i); + + if (curr.children[ch - 'a'] == null) { + curr.children[ch - 'a'] = new Node(ch); + } + + curr = curr.children[ch - 'a']; + } + + curr.isWord = true; + } + + // TC O(m^2) + public boolean search(String word) { + return searchHelper(word, root, 0); + } + + private boolean searchHelper(String word, Node curr, int index) { + for (int i = index; i < word.length(); i++) { + char ch = word.charAt(i); + + if (ch == '.') { + for (Node temp : curr.children) { + if (temp != null && searchHelper(word, temp, i + 1)) { + return true; + } + } + + return false; + } + + if (curr.children[ch - 'a'] == null) { + return false; + } + + curr = curr.children[ch - 'a']; + } + + return curr.isWord; + } +} diff --git a/java/0212-word-search-ii.java b/java/0212-word-search-ii.java new file mode 100644 index 000000000..b39c327e0 --- /dev/null +++ b/java/0212-word-search-ii.java @@ -0,0 +1,100 @@ +class Solution { + + private static int COLS; + private static int ROWS; + private Trie currentTrie; + + public List findWords(char[][] board, String[] words) { + Trie root = new Trie(); + for (String word : words) { + root.addWord(word); + } + + ROWS = board.length; + COLS = board[0].length; + HashSet res = new HashSet<>(); + HashSet visit = new HashSet<>(); + + for (int r = 0; r < ROWS; r++) { + for (int c = 0; c < COLS; c++) { + dfs(r, c, root, "", res, visit, board, root); + } + } + return new ArrayList<>(res); + } + + public void dfs( + int r, + int c, + Trie node, + String word, + HashSet res, + HashSet visit, + char[][] board, + Trie root + ) { + if ( + r < 0 || + c < 0 || + r == ROWS || + c == COLS || + !node.children.containsKey(board[r][c]) || + node.children.get(board[r][c]).refs < 1 || + visit.contains(r + "-" + c) + ) { + return; + } + + visit.add(r + "-" + c); + node = node.children.get(board[r][c]); + word += board[r][c]; + if (node.isWord) { + node.isWord = false; + res.add(word); + root.removeWord(word); + } + + dfs(r + 1, c, node, word, res, visit, board, root); + dfs(r - 1, c, node, word, res, visit, board, root); + dfs(r, c + 1, node, word, res, visit, board, root); + dfs(r, c - 1, node, word, res, visit, board, root); + visit.remove(r + "-" + c); + } + + class Trie { + + public HashMap children; + public boolean isWord; + public int refs = 0; + + public Trie() { + children = new HashMap<>(); + } + + public void addWord(String word) { + currentTrie = this; + currentTrie.refs += 1; + for (int i = 0; i < word.length(); i++) { + char currentCharacter = word.charAt(i); + if (!currentTrie.children.containsKey(currentCharacter)) { + currentTrie.children.put(currentCharacter, new Trie()); + } + currentTrie = currentTrie.children.get(currentCharacter); + currentTrie.refs += 1; + } + currentTrie.isWord = true; + } + + public void removeWord(String word) { + currentTrie = this; + currentTrie.refs -= 1; + for (int i = 0; i < word.length(); i++) { + char currentCharacter = word.charAt(i); + if (currentTrie.children.containsKey(currentCharacter)) { + currentTrie = currentTrie.children.get(currentCharacter); + currentTrie.refs -= 1; + } + } + } + } +} diff --git a/java/0213-house-robber-ii.java b/java/0213-house-robber-ii.java new file mode 100644 index 000000000..cfcb6b42d --- /dev/null +++ b/java/0213-house-robber-ii.java @@ -0,0 +1,23 @@ +class Solution { + + public int rob(int[] nums) { + if (nums.length == 0) return 0; + if (nums.length == 1) return nums[0]; + + return Math.max( + robHelper(nums, 0, nums.length - 2), + robHelper(nums, 1, nums.length - 1) + ); + } + + public int robHelper(int[] nums, int start, int end) { + int rob1 = 0; + int rob2 = 0; + for (int i = start; i <= end; i++) { + int temp = Math.max(rob1 + nums[i], rob2); + rob1 = rob2; + rob2 = temp; + } + return rob2; + } +} diff --git a/java/0215-kth-largest-element-in-an-array.java b/java/0215-kth-largest-element-in-an-array.java new file mode 100644 index 000000000..474db2879 --- /dev/null +++ b/java/0215-kth-largest-element-in-an-array.java @@ -0,0 +1,20 @@ +class Solution { + + public int findKthLargest(int[] nums, int k) { + //create a min heap + PriorityQueue heap = new PriorityQueue(); + + //iterate over the array + for (int n : nums) { + //first add the integer to heap + heap.add(n); + //if size of the heap is greater than k + if (heap.size() > k) { + //remove the root element (lowest of all) + heap.poll(); + } + } + //finally heap has k largest elements left with root as the kth largest element + return heap.peek(); + } +} diff --git a/java/0217-contains-duplicate.java b/java/0217-contains-duplicate.java new file mode 100644 index 000000000..6ad8b8bcb --- /dev/null +++ b/java/0217-contains-duplicate.java @@ -0,0 +1,13 @@ +class Solution { + + public boolean containsDuplicate(int[] nums) { + Set uniques = new HashSet<>(); + for (int i = 0; i < nums.length; i++) { + if (uniques.contains(nums[i])) { + return true; + } + uniques.add(nums[i]); + } + return false; + } +} diff --git a/java/0219-contains-duplicate-ii.java b/java/0219-contains-duplicate-ii.java new file mode 100644 index 000000000..28702d240 --- /dev/null +++ b/java/0219-contains-duplicate-ii.java @@ -0,0 +1,22 @@ +class Solution { + public boolean containsNearbyDuplicate(int[] nums, int k) { + + Set window = new HashSet<>(); + int left = 0; + for (int right = 0; right < nums.length; right++) { + + if (window.size() > k) { + window.remove(nums[left]); + left++; + } + + if (window.contains(nums[right])) { + return true; + } + + window.add(nums[right]); + } + + return false; + } +} diff --git a/java/0221-maximal-square.java b/java/0221-maximal-square.java new file mode 100644 index 000000000..9056610c5 --- /dev/null +++ b/java/0221-maximal-square.java @@ -0,0 +1,17 @@ +class Solution { + // dp bottom up + public int maximalSquare(char[][] matrix) { + int[][] dp = new int[matrix.length + 1][matrix[0].length + 1]; + int maxLen = 0; + for (int row = matrix.length - 1; row >= 0; row--) { + for (int col = matrix[0].length - 1; col >= 0; col--) { + if (matrix[row][col] == '1') { + dp[row][col] = 1 + Math.min(Math.min(dp[row + 1][col], dp[row][col + 1]), dp[row + 1][col + 1]); + maxLen = Math.max(maxLen, dp[row][col]); + } + } + } + + return maxLen * maxLen; + } +} \ No newline at end of file diff --git a/java/0225-implement-stack-using-queues.java b/java/0225-implement-stack-using-queues.java new file mode 100644 index 000000000..dad1cc792 --- /dev/null +++ b/java/0225-implement-stack-using-queues.java @@ -0,0 +1,31 @@ +class MyStack { + Deque q; + public MyStack() { + this.q = new ArrayDeque<>(); + } + + public void push(int x) { + this.q.addLast(x); + } + + public int pop() { + int size = this.q.size(); + for(int i = 0; i < size - 1; i++) + this.push(this.q.pollFirst()); + return this.q.pollFirst(); + } + + public int top() { + int size = q.size(); + for(int i = 0; i < size - 1; i++) + this.push(this.q.pollFirst()); + + int res = this.q.peekFirst(); + this.push(this.q.pollFirst()); + return res; + } + + public boolean empty() { + return this.q.size() == 0; + } +} diff --git a/java/0226-invert-binary-tree.java b/java/0226-invert-binary-tree.java new file mode 100644 index 000000000..1ae4a18ad --- /dev/null +++ b/java/0226-invert-binary-tree.java @@ -0,0 +1,10 @@ +class Solution { + + public TreeNode invertTree(TreeNode root) { + if (root == null) return null; + TreeNode node = new TreeNode(root.val); + node.right = invertTree(root.left); + node.left = invertTree(root.right); + return node; + } +} diff --git a/java/0229-majority-element-ii.java b/java/0229-majority-element-ii.java new file mode 100644 index 000000000..389223c9f --- /dev/null +++ b/java/0229-majority-element-ii.java @@ -0,0 +1,65 @@ +class Solution { + /** + * First solution utilizes a hashmap and then does the due diligience of adding the + * appropriate values that appear more than n/3 times + * Runtime O(n) : Space O(n) + */ + public List majorityElement(int[] nums) { + List res = new ArrayList<>(); + Map map = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + if (map.containsKey(nums[i])) + map.put(nums[i], map.get(nums[i]) + 1); + else + map.put(nums[i], 1); + } + + for (Map.Entry entry: map.entrySet()) { + int potentialCandidate = entry.getValue(); + if (potentialCandidate > nums.length / 3) + res.add(entry.getKey()); + } + + return res; + } + + + /** + * This is called Boyer-Moore Vote algorithm and the idea here is having candidates + * with diff values and two counters. + * For each number in the array we see if it equals the candidate and increment the count. + * The two numbers left after this process are the majority candidates. + * Loop through the array again then make sure that each candidate does indeed have more than n/3 occurrences + * + * Runtime O(n) : Space O(1) + */ + public List majorityElement_2(int[] nums) { + int candidate1 = 0, candidate2 = 0, count1 = 0, count2 = 0; + + for (int num : nums) { + if (num == candidate1) count1++; + else if (num == candidate2) count2++; + else if (count1 == 0) { + candidate1 = num; + count1++; + } else if (count2 == 0) { + candidate2 = num; + count2++; + } else { + count1--; + count2--; + } + } + + count1 = count2 = 0; + for (int num : nums) { + if (num == candidate1) count1++; + else if (num == candidate2) count2++; + } + + List res = new ArrayList<>(); + if (count1 > nums.length / 3) res.add(candidate1); + if (count2 > nums.length / 3) res.add(candidate2); + return res; + } +} \ No newline at end of file diff --git a/java/0230-kth-smallest-element-in-a-bst.java b/java/0230-kth-smallest-element-in-a-bst.java new file mode 100644 index 000000000..bcb845313 --- /dev/null +++ b/java/0230-kth-smallest-element-in-a-bst.java @@ -0,0 +1,31 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode() {} + * TreeNode(int val) { this.val = val; } + * TreeNode(int val, TreeNode left, TreeNode right) { + * this.val = val; + * this.left = left; + * this.right = right; + * } + * } + */ +class Solution { + + public int kthSmallest(TreeNode root, int k) { + List list = new ArrayList<>(); + inorder(root, list); + return list.get(k - 1); + } + + private void inorder(TreeNode root, List list) { + if (root == null) return; + + inorder(root.left, list); + list.add(root.val); + inorder(root.right, list); + } +} diff --git a/java/0234-palindrome-linked-list.java b/java/0234-palindrome-linked-list.java new file mode 100644 index 000000000..6bddfe778 --- /dev/null +++ b/java/0234-palindrome-linked-list.java @@ -0,0 +1,35 @@ +//reverse the later half +//after reversing just start comparing if at any time the value doesn't match it's not a palindrome, i.e. return false, else it's a palindrome and return true. +//It'll automatically take care of the edge cases of odd and even + +class Solution { + + public boolean isPalindrome(ListNode head) { + ListNode fast = head; + ListNode slow = head; + while (fast != null && fast.next != null) { + fast = fast.next.next; + slow = slow.next; + } + ListNode temp = reverse(slow); + while (temp != null && head != null) { + if (temp.val != head.val) return false; + temp = temp.next; + head = head.next; + } + return true; + } + + public ListNode reverse(ListNode head) { + ListNode p = null; + ListNode q = null; + ListNode r = head; + while (r != null) { + p = q; + q = r; + r = r.next; + q.next = p; + } + return q; + } +} diff --git a/java/0235-lowest-common-ancestor-of-a-binary-search-tree.java b/java/0235-lowest-common-ancestor-of-a-binary-search-tree.java new file mode 100644 index 000000000..9f5f47b83 --- /dev/null +++ b/java/0235-lowest-common-ancestor-of-a-binary-search-tree.java @@ -0,0 +1,32 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ + +class Solution { + + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + if (root == null) { + return null; + } + + if (p.val == root.val || q.val == root.val) { + return root; + } + + if (p.val < root.val && q.val < root.val) { + return lowestCommonAncestor(root.left, p, q); + } + + if (p.val > root.val && q.val > root.val) { + return lowestCommonAncestor(root.right, p, q); + } + + return root; + } +} diff --git a/java/0236-lowest-common-ancestor-of-a-binary-tree.java b/java/0236-lowest-common-ancestor-of-a-binary-tree.java new file mode 100644 index 000000000..57abd0fd1 --- /dev/null +++ b/java/0236-lowest-common-ancestor-of-a-binary-tree.java @@ -0,0 +1,17 @@ +class Solution { + + public TreeNode lowestCommonAncestor( + TreeNode root, + TreeNode p, + TreeNode q + ) { + if (root == null) return null; + if ((root.val == p.val || root.val == q.val)) return root; + TreeNode left = lowestCommonAncestor(root.left, p, q); + TreeNode right = lowestCommonAncestor(root.right, p, q); + + if (left != null && right != null) { + return root; + } else if (left != null) return left; else return right; + } +} diff --git a/java/0238-product-of-array-except-self.java b/java/0238-product-of-array-except-self.java new file mode 100644 index 000000000..9c1558559 --- /dev/null +++ b/java/0238-product-of-array-except-self.java @@ -0,0 +1,34 @@ +//Just store the left and right product (Try doing this with extra space first) +//This one is constant space because we are returning the array we created +//In first pass calculate the left product except self and in second calculate the right + +class Solution { + + public int[] productExceptSelf(int[] nums) { + int[] arr = new int[nums.length]; + int right = 1, left = 1; + for (int i = 0; i < nums.length; i++) { + arr[i] = left; + left *= nums[i]; + } + for (int i = nums.length - 1; i >= 0; i--) { + arr[i] *= right; + right *= nums[i]; + } + return arr; + } + + public int[] productExceptSelfNumsAsPrefix(int[] nums) { + int[] output = new int[nums.length]; + output[0] = 1; + + for (int i = 0; i < nums.length - 1; i++) output[i + 1] = + output[i] * nums[i]; + + for (int i = nums.length - 2; i >= 0; i--) { + output[i] = nums[i + 1] * output[i]; + nums[i] = nums[i] * nums[i + 1]; + } + return output; + } +} diff --git a/java/0239-sliding-window-maximum.java b/java/0239-sliding-window-maximum.java new file mode 100644 index 000000000..f4acef438 --- /dev/null +++ b/java/0239-sliding-window-maximum.java @@ -0,0 +1,15 @@ +class Solution { + + public int[] maxSlidingWindow(int[] nums, int k) { + int[] ans = new int[nums.length - k + 1]; + int j = 0; + Deque q = new LinkedList<>(); + for (int i = 0; i < nums.length; i++) { + if (!q.isEmpty() && q.peekFirst() < i - k + 1) q.pollFirst(); + while (!q.isEmpty() && nums[i] > nums[q.peekLast()]) q.pollLast(); + q.offer(i); + if (i >= k - 1) ans[j++] = nums[q.peekFirst()]; + } + return ans; + } +} diff --git a/java/0242-valid-anagram.java b/java/0242-valid-anagram.java new file mode 100644 index 000000000..381e5c255 --- /dev/null +++ b/java/0242-valid-anagram.java @@ -0,0 +1,17 @@ +class Solution { + + public boolean isAnagram(String s, String t) { + if (s.length() != t.length()) return false; + + int[] store = new int[26]; + + for (int i = 0; i < s.length(); i++) { + store[s.charAt(i) - 'a']++; + store[t.charAt(i) - 'a']--; + } + + for (int n : store) if (n != 0) return false; + + return true; + } +} diff --git a/java/0252-meeting-rooms.java b/java/0252-meeting-rooms.java new file mode 100644 index 000000000..0a9ffc639 --- /dev/null +++ b/java/0252-meeting-rooms.java @@ -0,0 +1,55 @@ +/** + * Definition of Interval: + * public class Interval { + * int start, end; + * Interval(int start, int end) { + * this.start = start; + * this.end = end; + * } + * } + */ + +public class Solution { + + /** + * @param intervals: an array of meeting time intervals + * @return: if a person could attend all meetings + */ + public boolean canAttendMeetings(List intervals) { + int length = intervals.size(); + if (intervals.size() == 0 || length == 1) return true; + // Write your code here + + int[] start = new int[length]; + int[] end = new int[length]; + for (int i = 0; i < length; i++) { + start[i] = intervals.get(i).start; + end[i] = intervals.get(i).end; + } + + Arrays.sort(start); + Arrays.sort(end); + + int j = 0; + while (j + 1 < length) { + if (end[j] > start[j + 1]) return false; + j++; + } + + return true; + } +} + +public class Solution { + + public boolean canAttendMeetings(List intervals) { + Collections.sort(intervals, (a, b) -> a.start - b.start); + + for (int i = 0; i + 1 < intervals.size(); i++) { + if (intervals.get(i).end > intervals.get(i + 1).start) { + return false; + } + } + return true; + } +} diff --git a/java/0253-meeting-rooms-ii.java b/java/0253-meeting-rooms-ii.java new file mode 100644 index 000000000..a4a819e42 --- /dev/null +++ b/java/0253-meeting-rooms-ii.java @@ -0,0 +1,81 @@ +/** + * Definition of Interval: + * public class Interval { + * int start, end; + * Interval(int start, int end) { + * this.start = start; + * this.end = end; + * } + * } + */ + +public class Solution { + + /** + * @param intervals: an array of meeting time intervals + * @return: the minimum number of conference rooms required + */ + public int minMeetingRooms(List intervals) { + if (intervals.isEmpty()) return 0; + + Collections.sort( + intervals, + (a, b) -> Integer.compare(a.start, b.start) + ); + + Queue queue = new PriorityQueue<>((a, b) -> + Integer.compare(a.end, b.end) + ); + + int count = 0; + for (Interval interval : intervals) { + while ( + !queue.isEmpty() && interval.start >= queue.peek().end + ) queue.poll(); + + queue.offer(interval); + count = Math.max(count, queue.size()); + } + return count; + } +} + +// Two pointer approach +public class Solution { + + public int minMeetingRooms(List intervals) { + if (intervals.size() == 0) { + return 0; + } + + int len = intervals.size(); + int[] startTime = new int[len]; + int[] endTime = new int[len]; + + for (int i = 0; i < len; i++) { + startTime[i] = intervals.get(i).start; + endTime[i] = intervals.get(i).end; + } + + Arrays.sort(startTime); + Arrays.sort(endTime); + + int res = 0; + int count = 0; + int s = 0; + int e = 0; + + while (s < len) { + if (startTime[s] < endTime[e]) { + s++; + count++; + } else { + e++; + count--; + } + res = Math.max(res, count); + } + + return res; + } +} diff --git a/java/0256-paint-house.java b/java/0256-paint-house.java new file mode 100644 index 000000000..bc265e5ac --- /dev/null +++ b/java/0256-paint-house.java @@ -0,0 +1,12 @@ +class Solution { + public int minCost(int[][] costs) { + int[] dp = new int[3]; + for (int i = 0; i < costs.length; i++) { + int dp0 = costs[i][0] + Math.min(dp[1], dp[2]); + int dp1 = costs[i][1] + Math.min(dp[0], dp[2]); + int dp2 = costs[i][2] + Math.min(dp[0], dp[1]); + dp = new int[] { dp0, dp1, dp2 }; + } + return Arrays.stream(dp).min().getAsInt(); + } +} diff --git a/java/0261-graph-valid-tree.java b/java/0261-graph-valid-tree.java new file mode 100644 index 000000000..6fde5473d --- /dev/null +++ b/java/0261-graph-valid-tree.java @@ -0,0 +1,43 @@ +public class Solution { + + private Map> adjacencyList = new HashMap<>(); + + public boolean validTree(int n, int[][] edges) { + if (n == 0 || n == 1) return true; + + if (edges.length == 0) return false; + + for (var edge : edges) { + var node1 = edge[0]; + var node2 = edge[1]; + adjacencyList.putIfAbsent(node1, new ArrayList<>()); + adjacencyList.putIfAbsent(node2, new ArrayList<>()); + adjacencyList.get(node1).add(node2); + adjacencyList.get(node2).add(node1); + } + + Set visited = new HashSet<>(); + + return ( + depthFirstSearch(edges[0][0], -1, visited) && visited.size() == n + ); + } + + private boolean depthFirstSearch( + int node, + int previous, + Set visited + ) { + if (visited.contains(node)) return false; + + visited.add(node); + + for (var neighbor : adjacencyList.get(node)) { + if (neighbor == previous) continue; + + if (!depthFirstSearch(neighbor, node, visited)) return false; + } + + return true; + } +} diff --git a/java/0263-ugly-number.java b/java/0263-ugly-number.java new file mode 100644 index 000000000..ec8c5101c --- /dev/null +++ b/java/0263-ugly-number.java @@ -0,0 +1,11 @@ +class Solution { + public boolean isUgly(int n) { + if(n <= 0) + return false; + + for(int p: new int[] {2, 3, 5}) + while(n % p == 0) + n = n / p; + return n == 1; + } +} diff --git a/java/0268-missing-number.java b/java/0268-missing-number.java new file mode 100644 index 000000000..f9e43df93 --- /dev/null +++ b/java/0268-missing-number.java @@ -0,0 +1,11 @@ +class Solution { + + public int missingNumber(int[] nums) { + int sum = 0; + int total = nums.length * (nums.length + 1) / 2; + for (int i = 0; i < nums.length; i++) { + sum += nums[i]; + } + return total - sum; + } +} diff --git a/java/0269-alien-dictionary.java b/java/0269-alien-dictionary.java new file mode 100644 index 000000000..19119a7a6 --- /dev/null +++ b/java/0269-alien-dictionary.java @@ -0,0 +1,134 @@ +class Solution { + + Map> adjList; + Map> revList; + Map degrees; + + // Return false if s.len < t.len condition is not met + private boolean createEdges(String left, String right) { + int minLen = Math.min(left.length(), right.length()); + + for (int i = 0; i < minLen; i++) { + char first = left.charAt(i), second = right.charAt(i); + + if (first != second) { + Set edges = this.adjList.get(first); + edges.add(second); + this.adjList.put(first, edges); + + Set revEdges = this.revList.get(second); + revEdges.add(first); + this.revList.put(second, revEdges); + + return true; + } + } + + // No difference found yet, there is a problem + return (left.length() <= right.length()); + } + + private void initialize(String[] words) { + // Initialize + int n = words.length; + this.adjList = new HashMap<>(); + this.revList = new HashMap<>(); + this.degrees = new HashMap<>(); + + for (int i = 0; i < n; i++) { + for (char c : words[i].toCharArray()) { + this.adjList.put(c, new HashSet<>()); + this.revList.put(c, new HashSet<>()); + this.degrees.put(c, 0); + } + } + } + + // Return false if creating any edge leads to a problem + private boolean createEdges(String[] words) { + int n = words.length; + for (int i = 0; i < n - 1; i++) { + for (int j = i + 1; j < n; j++) { + if (createEdges(words[i], words[j]) == false) return false; + } + } + return true; + } + + private void createDegrees() { + for (char c : this.adjList.keySet()) { + this.degrees.put(c, this.adjList.get(c).size()); + } + } + + private List topologicalSort() { + // take zero degrees first into a queue + Queue queue = new LinkedList<>(); + + Set taken = new HashSet<>(); + List ordering = new ArrayList<>(); + + for (char c : this.degrees.keySet()) { + if (this.degrees.get(c) == 0) { + queue.add(c); + } + } + + // Case 1: Doesn't exist + if (queue.isEmpty()) { + return null; + } + + while (!queue.isEmpty()) { + int qSize = queue.size(); + + // Level order traversal + for (int i = 0; i < qSize; i++) { + char removed = queue.remove(); + + // Put into the ordering + ordering.add(removed); + taken.add(removed); + + // In the reverse list, need to decrement for all neighbors, and if any is zero, need to put into the queue + for (char neighbor : this.revList.get(removed)) { + int degree = this.degrees.get(neighbor); + degree--; + + if (degree == 0) queue.add(neighbor); // add to queue + + this.degrees.put(neighbor, degree); + } + } + } + + // Case 2: Not all vertices were ordered + if (ordering.size() < this.revList.size()) { + return null; + } + + return ordering; + } + + private String getString(List ordering) { + StringBuilder bld = new StringBuilder(); + for (char c : ordering) bld.append(c); + return bld.reverse().toString(); + } + + // API + public String alienOrder(String[] words) { + initialize(words); + + // Edge case, s.len < t.len is not met + if (createEdges(words) == false) return ""; + + createDegrees(); + + List ordering = topologicalSort(); + + if (ordering == null) return ""; + + return getString(ordering); + } +} diff --git a/java/0271-encode-and-decode-strings.java b/java/0271-encode-and-decode-strings.java new file mode 100644 index 000000000..1e9a39a55 --- /dev/null +++ b/java/0271-encode-and-decode-strings.java @@ -0,0 +1,24 @@ +public class Solution { + + public String encode(List strs) { + StringBuilder encodedString = new StringBuilder(); + for (String str : strs) { + encodedString.append(str.length()).append("#").append(str); + } + return encodedString.toString(); + } + + public List decode(String str) { + List list = new ArrayList<>(); + int i = 0; + while (i < str.length()) { + int j = i; + while (str.charAt(j) != '#') j++; + + int length = Integer.valueOf(str.substring(i, j)); + i = j + 1 + length; + list.add(str.substring(j + 1, i)); + } + return list; + } +} diff --git a/java/0279-perfect-squares.java b/java/0279-perfect-squares.java new file mode 100644 index 000000000..f6d5f2a97 --- /dev/null +++ b/java/0279-perfect-squares.java @@ -0,0 +1,48 @@ +class Solution { + public int numSquares(int n) { + int[] dp = new int[n+1]; + Arrays.fill(dp, n); + dp[0] = 0; + + for(int target = 0; target < n + 1; target++){ + for(int s = 0; s < target; s++){ + int square = s*s; + if(target - square < 0) + break; + dp[target] = Math.min(dp[target], 1 + dp[target - square]); + } + } + return dp[n]; + } +} + +/*---------------------------------------------------------------------------------------------------------------------------------------------------------*/ + +//This is a BFS based approach. +//We can also do this problem similar to coin change but the Time and space complexity will remain same (Just an extra queue in this one stil linear space). + +class Solution { + + public int numSquares(int n) { + Queue q = new LinkedList<>(); + //add visited array so we don't go to values which we have traversed already (similar to dp) otherwise this will give tle + HashSet visited = new HashSet<>(); + int ans = 0; + q.offer(n); + while (!q.isEmpty()) { + int size = q.size(); + for (int i = 0; i < size; i++) { + int cur = q.poll(); + if (cur == 0) return ans; + for (int j = 1; j <= cur / j; j++) { + if (!visited.contains(cur - j * j)) { + q.offer(cur - j * j); + visited.add(cur - j * j); + } + } + } + ans++; + } + return -1; + } +} diff --git a/java/0280-wiggle-sort.java b/java/0280-wiggle-sort.java new file mode 100644 index 000000000..d543e366e --- /dev/null +++ b/java/0280-wiggle-sort.java @@ -0,0 +1,14 @@ +class Solution { + public void wiggleSort(int[] nums) { + for (int i = 1; i < nums.length; i++) { + if ((i % 2 == 0 && nums[i] > nums[i - 1]) + || (i % 2 == 1 && nums[i] < nums[i - 1])) + swap(nums, i, i - 1); + } + } + private void swap(int[] nums, int i, int j) { + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } +} diff --git a/java/0283-move-zeroes.java b/java/0283-move-zeroes.java new file mode 100644 index 000000000..2761e16da --- /dev/null +++ b/java/0283-move-zeroes.java @@ -0,0 +1,13 @@ +class Solution { + public void moveZeroes(int[] nums) { + //Do not return anything, modify nums in-place instead. + int l = 0; + for(int r = 0; r < nums.length; r++) + if(nums[r] != 0) { + int tmp = nums[l]; + nums[l] = nums[r]; + nums[r] = tmp; + l += 1; + } + } +} diff --git a/java/0286-walls-and-gates.java b/java/0286-walls-and-gates.java new file mode 100644 index 000000000..f28c26c8f --- /dev/null +++ b/java/0286-walls-and-gates.java @@ -0,0 +1,39 @@ +//Same as rotting oranges +//Time complexity will be O(N^2) since we are basically traversing every value in the grid. + +class Solution { + + public void wallsAndGates(int[][] rooms) { + Queue q = new LinkedList<>(); + int m = rooms.length; + int n = rooms[0].length; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (rooms[i][j] == 0) q.add(new int[] { i, j }); + } + } + if (q.size() == 0) return; + int[][] dirs = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } }; + while (!q.isEmpty()) { + int[] cur = q.poll(); + int row = cur[0]; + int col = cur[1]; + for (int[] dir : dirs) { + int x = row + dir[0]; + int y = col + dir[1]; + if ( + x >= m || + y >= n || + x < 0 || + y < 0 || + rooms[x][y] != Integer.MAX_VALUE + ) continue; + q.add(new int[] { x, y }); + //since cur is basically the index of door (which is equal to 0) + //So, we can just grab that value (rooms[row][col]) and add 1 to it and change it every time + rooms[x][y] = rooms[row][col] + 1; + //So, one level further from door (value 0) is equal to 1. Now, we do bfs from that position so value will be 2 and so on. + } + } + } +} diff --git a/java/0287-find-the-duplicate-number.java b/java/0287-find-the-duplicate-number.java new file mode 100644 index 000000000..2ea3296af --- /dev/null +++ b/java/0287-find-the-duplicate-number.java @@ -0,0 +1,28 @@ +//Fast and slow pointer approach +// Time Complexity: O(n) +// Space Complexity: O(1) + +class Solution { + public int findDuplicate(int[] nums) { + int slow = 0; + int fast = 0; + + do { + slow = nums[slow]; + fast = nums[nums[fast]]; + } + + while (slow != fast); + + int slow2 = 0; + + do { + slow = nums[slow]; + slow2 = nums[slow2]; + } + + while (slow != slow2); + + return slow2; + } +} diff --git a/java/0290-word-pattern.java b/java/0290-word-pattern.java new file mode 100644 index 000000000..0edd966b6 --- /dev/null +++ b/java/0290-word-pattern.java @@ -0,0 +1,26 @@ +class Solution { + public boolean wordPattern(String pattern, String s) { + String[] sArray = s.split("\s"); + if(sArray.length != pattern.length()) { + return false; + } + + HashMap charToWord = new HashMap<>(); + HashMap wordToChar = new HashMap<>(); + + for (int i = 0; i < pattern.length(); i++) { + + if(charToWord.containsKey(pattern.charAt(i)) && !charToWord.get(pattern.charAt(i)).equals(sArray[i])) { + return false; + } + + if(wordToChar.containsKey(sArray[i]) && !wordToChar.get(sArray[i]).equals(pattern.charAt(i))) { + return false; + } + + charToWord.put(pattern.charAt(i),sArray[i]); + wordToChar.put(sArray[i],pattern.charAt(i)); + } + return true; + } +} diff --git a/java/0295-find-median-from-data-stream.java b/java/0295-find-median-from-data-stream.java new file mode 100644 index 000000000..359fc1269 --- /dev/null +++ b/java/0295-find-median-from-data-stream.java @@ -0,0 +1,40 @@ +class MedianFinder { + + private Queue smallHeap; //small elements - maxHeap + private Queue largeHeap; //large elements - minHeap + + public MedianFinder() { + smallHeap = new PriorityQueue<>((a, b) -> b - a); + largeHeap = new PriorityQueue<>((a, b) -> a - b); + } + + public void addNum(int num) { + smallHeap.add(num); + if ( + smallHeap.size() - largeHeap.size() > 1 || + !largeHeap.isEmpty() && + smallHeap.peek() > largeHeap.peek() + ) { + largeHeap.add(smallHeap.poll()); + } + if (largeHeap.size() - smallHeap.size() > 1) { + smallHeap.add(largeHeap.poll()); + } + } + + public double findMedian() { + if (smallHeap.size() == largeHeap.size()) { + return (double) (largeHeap.peek() + smallHeap.peek()) / 2; + } else if (smallHeap.size() > largeHeap.size()) { + return (double) smallHeap.peek(); + } else { + return (double) largeHeap.peek(); + } + } +} +/** + * Your MedianFinder object will be instantiated and called as such: + * MedianFinder obj = new MedianFinder(); + * obj.addNum(num); + * double param_2 = obj.findMedian(); + */ diff --git a/java/0297-serialize-and-deserialize-binary-tree.java b/java/0297-serialize-and-deserialize-binary-tree.java new file mode 100644 index 000000000..ed5af6eba --- /dev/null +++ b/java/0297-serialize-and-deserialize-binary-tree.java @@ -0,0 +1,54 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +public class Codec { + + private int i; + + // Encodes a tree to a single string. + public String serialize(TreeNode root) { + List list = new ArrayList<>(); + serializeDFS(root, list); + + return String.join(",", list); + } + + private void serializeDFS(TreeNode root, List list) { + if (root == null) { + list.add("N"); + return; + } + list.add(String.valueOf(root.val)); + serializeDFS(root.left, list); + serializeDFS(root.right, list); + } + + // Decodes your encoded data to tree. + public TreeNode deserialize(String data) { + String[] tokens = data.split(","); + return deserializeDFS(tokens); + } + + private TreeNode deserializeDFS(String[] tokens) { + String token = tokens[this.i]; + if (token.equals("N")) { + this.i++; + return null; + } + var node = new TreeNode(Integer.parseInt(token)); + this.i++; + node.left = deserializeDFS(tokens); + node.right = deserializeDFS(tokens); + return node; + } +} +// Your Codec object will be instantiated and called as such: +// Codec ser = new Codec(); +// Codec deser = new Codec(); +// TreeNode ans = deser.deserialize(ser.serialize(root)); diff --git a/java/0300-longest-increasing-subsequence.java b/java/0300-longest-increasing-subsequence.java new file mode 100644 index 000000000..63a8e3dbe --- /dev/null +++ b/java/0300-longest-increasing-subsequence.java @@ -0,0 +1,37 @@ +class Solution { + + // Dynamic programming, O(n^2) + public int lengthOfLIS(int[] nums) { + if (nums.length == 1) return 1; + + int[] LIS = new int[nums.length]; + Arrays.fill(LIS, 1); + int maximumSoFar = 1; + + for (int i = nums.length - 1; i >= 0; i--) { + for (int j = i + 1; j < nums.length; j++) { + if (nums[i] < nums[j]) { + LIS[i] = Math.max(1 + LIS[j], LIS[i]); + } + } + maximumSoFar = Math.max(maximumSoFar, LIS[i]); + } + return maximumSoFar; + } + + // Binary search, O(nlogn) + public int lengthOfLIS(int[] nums) { + List lis = new ArrayList<>(nums.length); + + for (int n : nums) { + int i = Collections.binarySearch(lis, n); + if (i < 0) i = -i - 1; + + if (i == lis.size()) + lis.add(n); + else + lis.set(i, n); + } + return lis.size(); + } +} diff --git a/java/0303-range-sum-query-immutable.java b/java/0303-range-sum-query-immutable.java new file mode 100644 index 000000000..d2022a432 --- /dev/null +++ b/java/0303-range-sum-query-immutable.java @@ -0,0 +1,23 @@ +class NumArray { + + public int[] arr; + public NumArray(int[] nums) { + arr=nums; + for(int i=1;i 0; i--) { + this.tree[i] = this.tree[2 * i] + this.tree[2 * i + 1]; // parent value is sum of children values + } + } + + // O(logn) + public void update(int index, int val) { + index += this.n; + this.tree[index] = val; + while (index > 0) { + int left = index; + int right = index; + if ((index % 2 == 0)) { + // we are on the left node of parent + right = index + 1; + } else { + // we are on the right node of the parent + left = index -1; + } + this.tree[index / 2] = this.tree[right] + this.tree[left]; // parent is updated + index /= 2; + } + } + + // O(logn) + public int sumRange(int left, int right) { + int l = left + this.n; + int r = right + this.n; + int sum = 0; + + while (l <= r) { + if ((l % 2) == 1) { + // l is the right child of his parent, + // parent includes the sum of l and the right node which is not + // in the query sum, so we just include this.tree[l] in the sum + sum += this.tree[l]; + l++; // shrink interval + } + if ((r % 2) == 0) { + // r is the left child of his parent, + // parent includes the sum of r and the left node which is not + // in the query sum, so we just include this.tree[r] in the sum + sum += this.tree[r]; + r--; // shrink interval + } + l /= 2; + r /= 2; + } + return sum; + } +} \ No newline at end of file diff --git a/java/0309-best-time-to-buy-and-sell-stock-with-cooldown.java b/java/0309-best-time-to-buy-and-sell-stock-with-cooldown.java new file mode 100644 index 000000000..efa842b32 --- /dev/null +++ b/java/0309-best-time-to-buy-and-sell-stock-with-cooldown.java @@ -0,0 +1,35 @@ +class Solution { + + public int maxProfit(int[] prices) { + Map cache = new HashMap<>(); + return dfs(prices, cache, 0, true); + } + + public int dfs( + int[] prices, + Map cache, + int index, + boolean buying + ) { + if (index >= prices.length) { + return 0; + } + String key = index + "-" + buying; + + if (cache.containsKey(key)) { + return cache.get(key); + } + + int cooldown = dfs(prices, cache, index + 1, buying); + int buyOsell = Integer.MIN_VALUE; + + if (buying) { + buyOsell = dfs(prices, cache, index + 1, !buying) - prices[index]; + } else { + buyOsell = dfs(prices, cache, index + 2, !buying) + prices[index]; + } + + cache.put(key, Math.max(buyOsell, cooldown)); + return cache.get(key); + } +} diff --git a/java/0312-burst-balloons.java b/java/0312-burst-balloons.java new file mode 100644 index 000000000..6013e62cb --- /dev/null +++ b/java/0312-burst-balloons.java @@ -0,0 +1,38 @@ +// Solution: Dynamic Programming with Memoization + +// Time Complexity: O(n^3) +// Extra Space Complexity: O(n^2) +class Solution { + + public int maxCoins(int[] nums) { + var dp = new int[nums.length][nums.length]; + + return burst(nums, 0, nums.length - 1, dp); + } + + private int burst(int[] nums, int left, int right, int[][] dp) { + if (left > right) { + return 0; + } + if (dp[left][right] != 0) { + return dp[left][right]; + } + + for (int i = left; i <= right; i++) { + int coins = nums[i]; + + if (left - 1 >= 0) { + coins *= nums[left - 1]; + } + if (right + 1 < nums.length) { + coins *= nums[right + 1]; + } + + coins += + burst(nums, left, i - 1, dp) + burst(nums, i + 1, right, dp); + dp[left][right] = Math.max(dp[left][right], coins); + } + + return dp[left][right]; + } +} diff --git a/java/0316-remove-duplicate-letters.java b/java/0316-remove-duplicate-letters.java new file mode 100644 index 000000000..78373410f --- /dev/null +++ b/java/0316-remove-duplicate-letters.java @@ -0,0 +1,24 @@ +class Solution { + public String removeDuplicateLetters(String s) { + Set seen = new HashSet<>(); + HashMap last = new HashMap<>(); + for(char c : s.toCharArray()){ + if(!last.containsKey(c)) + last.put(c, s.lastIndexOf(c)); + } + Stack stack = new Stack(); + for(int i = 0; i < s.length(); i++){ + char c = s.charAt(i); + if(seen.contains(c)) + continue; + while(!stack.isEmpty() && stack.peek() > c && i < last.get(stack.peek())) + seen.remove(stack.pop()); + stack.push(c); + seen.add(c); + } + StringBuilder sb = new StringBuilder(); + for(char c: stack) + sb.append(c); + return sb.toString(); + } +} diff --git a/java/0322-coin-change.java b/java/0322-coin-change.java new file mode 100644 index 000000000..8d3a7f786 --- /dev/null +++ b/java/0322-coin-change.java @@ -0,0 +1,23 @@ +import java.util.Arrays; + +class Solution { + + public int coinChange(int[] coins, int amount) { + if (amount < 0 || coins.length == 0 || coins == null) { + return 0; + } + int[] dp = new int[amount + 1]; + Arrays.fill(dp, amount + 1); + dp[0] = 0; + + for (int i = 1; i <= amount; i++) { + for (int coin : coins) { + if (i - coin >= 0) { + dp[i] = Math.min(dp[i], 1 + dp[i - coin]); + } + } + } + + return dp[amount] != amount + 1 ? dp[amount] : -1; + } +} diff --git a/java/0323-number-of-connected-components-in-an-undirected-graph.java b/java/0323-number-of-connected-components-in-an-undirected-graph.java new file mode 100644 index 000000000..ac0e8aeac --- /dev/null +++ b/java/0323-number-of-connected-components-in-an-undirected-graph.java @@ -0,0 +1,54 @@ +class Solution { + + private int[] parent; + private int[] rank; + + public int countComponents(int n, int[][] edges) { + parent = new int[n]; + rank = new int[n]; + + for (int i = 0; i < n; i++) { + parent[i] = i; + rank[i] = 1; + } + + int result = n; + for (int i = 0; i < edges.length; i++) { + if (union(edges[i][0], edges[i][1]) == 1) { + result--; + } + } + + return result; + } + + private int find(int node) { + int result = node; + + while (parent[result] != result) { + parent[result] = parent[parent[result]]; + result = parent[result]; + } + + return result; + } + + private int union(int n1, int n2) { + int p1 = this.find(n1); + int p2 = this.find(n2); + + if (p1 == p2) { + return 0; + } + + if (rank[p2] > rank[p1]) { + parent[p1] = p2; + rank[p2] += rank[p1]; + } else { + parent[p2] = p1; + rank[p1] += rank[p2]; + } + + return 1; + } +} diff --git a/java/0329-longest-increasing-path-in-a-matrix.java b/java/0329-longest-increasing-path-in-a-matrix.java new file mode 100644 index 000000000..bab0de730 --- /dev/null +++ b/java/0329-longest-increasing-path-in-a-matrix.java @@ -0,0 +1,43 @@ +//Another similar problem: https://leetcode.com/contest/weekly-contest-300/problems/number-of-increasing-paths-in-a-grid/ + +class Solution { + + public int longestIncreasingPath(int[][] matrix) { + int m = matrix.length; + int n = matrix[0].length; + int[][] dp = new int[m][n]; + for (int d[] : dp) Arrays.fill(d, -1); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (dp[i][j] == -1) dfs(matrix, dp, m, n, i, j, -1); + } + } + int max = Integer.MIN_VALUE; + for (int[] d : dp) { + for (int i : d) max = Math.max(i, max); + } + return max; + } + + public int dfs( + int[][] matrix, + int[][] dp, + int m, + int n, + int i, + int j, + int parent + ) { + if ( + i >= m || j >= n || i < 0 || j < 0 || matrix[i][j] <= parent + ) return 0; + parent = matrix[i][j]; + if (dp[i][j] != -1) return dp[i][j]; + int left = dfs(matrix, dp, m, n, i, j - 1, parent); + int right = dfs(matrix, dp, m, n, i, j + 1, parent); + int bottom = dfs(matrix, dp, m, n, i + 1, j, parent); + int top = dfs(matrix, dp, m, n, i - 1, j, parent); + dp[i][j] = 1 + Math.max(Math.max(left, right), Math.max(top, bottom)); + return dp[i][j]; + } +} diff --git a/java/0332-reconstruct-itinerary.java b/java/0332-reconstruct-itinerary.java new file mode 100644 index 000000000..cfa902881 --- /dev/null +++ b/java/0332-reconstruct-itinerary.java @@ -0,0 +1,30 @@ +class Solution { + + public List findItinerary(List> tickets) { + LinkedList itinerary = new LinkedList<>(); + Map> graph = new HashMap<>(); + Stack stack = new Stack<>(); + + for (List ticket : tickets) { + graph + .computeIfAbsent(ticket.get(0), k -> new PriorityQueue<>()) + .add(ticket.get(1)); + } + + stack.push("JFK"); + while (!stack.isEmpty()) { + String nextDestination = stack.peek(); + + if ( + !graph + .getOrDefault(nextDestination, new PriorityQueue<>()) + .isEmpty() + ) { + stack.push(graph.get(nextDestination).poll()); + } else { + itinerary.addFirst(stack.pop()); + } + } + return itinerary; + } +} diff --git a/java/0337-house-robber-iii.java b/java/0337-house-robber-iii.java new file mode 100644 index 000000000..59f8b27f5 --- /dev/null +++ b/java/0337-house-robber-iii.java @@ -0,0 +1,19 @@ +class Solution { + public int[] dfs(TreeNode root){ + if(root == null) return new int[2]; + + int []left = dfs(root.left); + int []right = dfs(root.right); + + int []res = new int[2]; + + res[0] = left[1] + right[1] + root.val; //with Root + res[1] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]); //without Root + + return res; + } + public int rob(TreeNode root) { + int []ans = dfs(root); + return Math.max(ans[0], ans[1]); + } +} diff --git a/java/0338-counting-bits.java b/java/0338-counting-bits.java new file mode 100644 index 000000000..e8f850b5c --- /dev/null +++ b/java/0338-counting-bits.java @@ -0,0 +1,8 @@ +class Solution { + public int[] countBits(int n) { + int res[] = new int[n + 1]; + for (int i = 1; i <= n; i++) + res[i] = 1 + res[i & (i - 1)]; + return res; + } +} diff --git a/java/0341-flatten-nested-list-iterator.java b/java/0341-flatten-nested-list-iterator.java new file mode 100644 index 000000000..2c720fce7 --- /dev/null +++ b/java/0341-flatten-nested-list-iterator.java @@ -0,0 +1,27 @@ +public class NestedIterator implements Iterator { + Queue q; // here we use Queue in place of revesed stack + + public NestedIterator(List nestedList) { + q = new LinkedList<>(); + dfs(nestedList); + } + + @Override + public Integer next() { + return q.poll().getInteger(); + } + + @Override + public boolean hasNext() { + return !q.isEmpty(); + } + + private void dfs(List nestedList){ + for(NestedInteger n : nestedList){ + if(n.isInteger()) + q.add(n); + else + dfs(n.getList()); + } + } +} diff --git a/java/0342-power-of-four.java b/java/0342-power-of-four.java new file mode 100644 index 000000000..2496d9a5e --- /dev/null +++ b/java/0342-power-of-four.java @@ -0,0 +1,7 @@ +class Solution { + + public boolean isPowerOfFour(int n) { + double x = Math.log(n) / Math.log(4); + return x == (int) x; + } +} diff --git a/java/0343-integer-break.java b/java/0343-integer-break.java new file mode 100644 index 000000000..45d124787 --- /dev/null +++ b/java/0343-integer-break.java @@ -0,0 +1,15 @@ +class Solution { + public int integerBreak(int n) { + if (n < 4) return n - 1; + + int res = 1; + + while (n > 4) { + n -= 3; + res *= 3; + } + + res *= n; + return res; + } +} diff --git a/java/0344-reverse-string.java b/java/0344-reverse-string.java new file mode 100644 index 000000000..8de54bf24 --- /dev/null +++ b/java/0344-reverse-string.java @@ -0,0 +1,12 @@ +class Solution { + public void reverseString(char[] s) { + //Do not return anything, modify s in-place instead. + int l = 0; + int r = s.length - 1; + while(l <= r) { + char tmp = s[l]; + s[l++] = s[r]; + s[r--] = tmp; + } + } +} diff --git a/java/0347-top-k-frequent-elements.java b/java/0347-top-k-frequent-elements.java new file mode 100644 index 000000000..eefaf420a --- /dev/null +++ b/java/0347-top-k-frequent-elements.java @@ -0,0 +1,122 @@ +class Solution1 { + + /** + * Time Complexity: O(nlog(k)) + * Space Complexity: O(n) + */ + public int[] topKFrequent(int[] nums, int k) { + int[] arr = new int[k]; + HashMap map = new HashMap<>(); + for (int num : nums) map.put(num, map.getOrDefault(num, 0) + 1); + PriorityQueue> pq = new PriorityQueue<>( + (a, b) -> + a.getValue() - b.getValue() + ); + for (Map.Entry it : map.entrySet()) { + pq.add(it); + if (pq.size() > k) pq.poll(); + } + int i = k; + while (!pq.isEmpty()) { + arr[--i] = pq.poll().getKey(); + } + return arr; + } +} + +class Solution2 { + + /** + * Time Complexity: O(n) + * Space Complexity: O(n) + */ + public int[] topKFrequent(int[] numbers, int k) { + HashMap map = new HashMap<>(); + for (int number : numbers) map.put( + number, + map.getOrDefault(number, 0) + 1 + ); + + int size = map.size(); + int[] keys = new int[size]; + int i = 0; + for (int key : map.keySet()) keys[i++] = key; + + select(keys, map, 0, size - 1, size - k); + return Arrays.copyOfRange(keys, size - k, size); + } + + // Modified implementation of Hoare's selection algorithm: + + private void select( + int[] keys, + Map map, + int left, + int right, + int kSmallest + ) { + while (left != right) { + int pivot = partition(keys, map, left, right, (left + right) / 2); + + if (kSmallest == pivot) return; + + if (kSmallest < pivot) right = pivot - 1; else left = pivot + 1; + } + } + + private int partition( + int[] keys, + Map map, + int left, + int right, + int pivot + ) { + int pivotValue = map.get(keys[pivot]); + swap(keys, pivot, right); + int index = left; + + for (int i = left; i <= right; i++) if (map.get(keys[i]) < pivotValue) { + swap(keys, i, index); + index++; + } + swap(keys, right, index); + return index; + } + + private void swap(int[] array, int i1, int i2) { + int temp = array[i1]; + array[i1] = array[i2]; + array[i2] = temp; + } +} +class Solution3 { + /** + * Time Complexity: O(n) + * Space Complexity: O(n) + */ + public int[] topKFrequent(int[] nums, int k) { + Map count = new HashMap<>(); + List bucket[] = new ArrayList[nums.length + 1]; + + for (int num : nums) + count.merge(num, 1, Integer::sum); + + for (int key : count.keySet()){ + int freq = count.get(key); + if (bucket[freq] == null) + bucket[freq] = new ArrayList<>(); + bucket[freq].add(key); + } + + int index = 0; + int[] res = new int[k]; + for (int i = nums.length; i >= 0; i--) + if (bucket[i] != null) + for (int val : bucket[i]){ + res[index++] = val; + if(index == k) + return res; + } + return res; + } + diff --git a/java/0349-intersection-of-two-arrays.java b/java/0349-intersection-of-two-arrays.java new file mode 100644 index 000000000..4c09dec6c --- /dev/null +++ b/java/0349-intersection-of-two-arrays.java @@ -0,0 +1,16 @@ +class Solution { + public int[] intersection(int[] nums1, int[] nums2) { + var seen = new HashSet(); + for (int n : nums1) + seen.add(n); + + var res = new HashSet(); + for (int n : nums2) { + if (seen.contains(n)) { + res.add(n); + } + } + + return res.stream().mapToInt(Integer::intValue).toArray(); + } +} diff --git a/java/0355-design-twitter.java b/java/0355-design-twitter.java new file mode 100644 index 000000000..85541c131 --- /dev/null +++ b/java/0355-design-twitter.java @@ -0,0 +1,88 @@ +class Twitter { + int count; + HashMap> tweetMap; + HashMap> followerMap; + + public Twitter() { + count = 0; + tweetMap = new HashMap<>(); + followerMap = new HashMap<>(); + } + + public void postTweet(int userId, int tweetId) { + tweetMap.computeIfAbsent(userId, + k -> new ArrayList<>() + ); + + tweetMap.computeIfPresent(userId, (k, v) -> { + v.add(new int[]{count, tweetId}); + return v; + }); + + ++count; + } + + public List getNewsFeed(int userId) { + List res = new ArrayList<>(); + + PriorityQueue pq = new PriorityQueue<>((a, b) -> + Integer.compare(b[0], a[0]) + ); + + followerMap.computeIfAbsent(userId, + k -> new HashSet<>() + ); + + followerMap.get(userId).add(userId); + + followerMap.get(userId).forEach((followeeId) -> { + if (tweetMap.containsKey(followeeId)) { + int i = tweetMap.get(followeeId).size() - 1; + int[] tweet = tweetMap.get(followeeId).get(i); + pq.offer(new int[]{tweet[0], tweet[1], + followeeId, --i}); + } + }); + + while (!pq.isEmpty() && res.size() < 10) { + int[] data = pq.poll(); + res.add(data[1]); + + if (data[3] >= 0) { + int[] tweet = tweetMap.get(data[2]).get(data[3]); + pq.offer(new int[]{tweet[0], tweet[1], + data[2], --data[3]}); + } + } + + return res; + } + + public void follow(int followerId, int followeeId) { + followerMap.computeIfAbsent(followerId, + k -> new HashSet<>()); + + followerMap.computeIfPresent(followerId, (k, v) -> { + v.add(followeeId); + return v; + }); + } + + public void unfollow(int followerId, int followeeId) { + HashSet set = followerMap.computeIfAbsent(followerId, + k -> new HashSet<>()); + + if (set.contains(followeeId)) { + set.remove(followeeId); + } + } +} + +/** + * Your Twitter object will be instantiated and called as such: + * Twitter obj = new Twitter(); + * obj.postTweet(userId,tweetId); + * List param_2 = obj.getNewsFeed(userId); + * obj.follow(followerId,followeeId); + * obj.unfollow(followerId,followeeId); + */ diff --git a/java/0367-valid-perfect-square.java b/java/0367-valid-perfect-square.java new file mode 100644 index 000000000..9e460faa9 --- /dev/null +++ b/java/0367-valid-perfect-square.java @@ -0,0 +1,21 @@ +class Solution { + public boolean isPerfectSquare(int num) { + + long left = 1; + long right = num; + while (left <= right) { + + long mid = (left + right) / 2; + if (mid * mid == num) { + return true; + } else if (mid * mid > num) { + right = mid - 1; + } else { + left = mid + 1; + } + + } + + return false; + } +} \ No newline at end of file diff --git a/java/0371-sum-of-two-integers.java b/java/0371-sum-of-two-integers.java new file mode 100644 index 000000000..2961fca14 --- /dev/null +++ b/java/0371-sum-of-two-integers.java @@ -0,0 +1,11 @@ +class Solution { + + public int getSum(int a, int b) { + while (b != 0) { + int tmp = (a & b) << 1; + a = (a ^ b); + b = tmp; + } + return a; + } +} diff --git a/java/0374-guess-number-higher-or-lower.java b/java/0374-guess-number-higher-or-lower.java new file mode 100644 index 000000000..b3e786415 --- /dev/null +++ b/java/0374-guess-number-higher-or-lower.java @@ -0,0 +1,18 @@ +public class Solution extends GuessGame { + public int guessNumber(int n) { + //return a num between 1,...,n + int low = 1; + int high = n; + + while(true) { + int mid = low + (high - low)/2; + int myGuess = guess(mid); + if(myGuess == 1) + low = mid + 1; + else if(myGuess == -1) + high = mid - 1; + else + return mid; + } + } +} diff --git a/java/0377-combination-sum-iv.java b/java/0377-combination-sum-iv.java new file mode 100644 index 000000000..a491b3045 --- /dev/null +++ b/java/0377-combination-sum-iv.java @@ -0,0 +1,72 @@ +class Solution { + /* Tabulation Method + -------------------------- + T = Target, N = nums.length + Time complexity: O(T⋅N) + Space complexity: O(T) + */ + + public int combinationSum4(int[] nums, int target) { + int[] dp = new int[target+1]; + dp[0] = 1; + + for(int currSum = 1; currSum < dp.length; currSum++){ + for(int no : nums){ + if(currSum - no >= 0){ + dp[currSum] += dp[currSum - no]; + } + } + } + return dp[target]; + } + +/* Memoization Method + -------------------------- + T = Target, N = nums.length + Time complexity: O(T⋅N) + Space complexity: O(T) + Recursive Stack +*/ + + public int combinationSum4(int[] nums, int target) { + HashMap memo = new HashMap<>(); + return helper(nums, target, memo); + } + + private int helper(int[] nums, int t, HashMap memo){ + if(t == 0) + return 1; + if(t < 0) + return 0; + if(memo.containsKey(t)) + return memo.get(t); + + int count = 0; + for(int no : nums){ + count += helper(nums, t - no); + } + memo.put(t, count); + return count; + } + + /** + Simple brute force, count num of combinations + using Map ds + Time complexity: O(n) + Space complexity: O(n) + */ + public int combinationSum4(int[] nums, int target) { + Map cache = new HashMap<>(); + + cache.put(0, 1); + + for (int i = 1; i < target + 1; i++) { + cache.put(i, 0); + for (int n : nums) { + int temp = cache.containsKey(i - n) ? cache.get(i - n) : 0; + cache.put(i, cache.get(i) + temp); + } + } + + return cache.get(target); + } +} diff --git a/java/0380-insert-delete-getrandom-o1.java b/java/0380-insert-delete-getrandom-o1.java new file mode 100644 index 000000000..6052650ef --- /dev/null +++ b/java/0380-insert-delete-getrandom-o1.java @@ -0,0 +1,57 @@ +class RandomizedSet { + private Map indexing; + private List numbers; + + public RandomizedSet() { + this.indexing = new HashMap<>(); + this.numbers = new ArrayList<>(); + } + + // Append to the end, maintain indexing + // Delete by swapping with the end, maintain indexing + // Get random by getting a random index wrt list's size + + public boolean insert(int val) { + if(this.indexing.containsKey(val)) { + return false; + } + int indexInsert = this.numbers.size(); + this.numbers.add(val); + this.indexing.put(val, indexInsert); + return true; + } + + public boolean remove(int val) { + if(!this.indexing.containsKey(val)) { + return false; + } + + int lastIndex = this.numbers.size() - 1; + int lastElement = this.numbers.get(lastIndex); + int indexElement = this.indexing.get(val); + + // Swap with last element + this.numbers.set(indexElement, lastElement); + + // Update indices [Add & Delete] + this.indexing.put(lastElement, indexElement); + this.indexing.remove(val); + + // Remove from list + this.numbers.remove(lastIndex); + return true; + } + + public int getRandom() { + int randomIndex = (int) (Math.random() * this.numbers.size()); + return this.numbers.get(randomIndex); + } +} + +/** + * Your RandomizedSet object will be instantiated and called as such: + * RandomizedSet obj = new RandomizedSet(); + * boolean param_1 = obj.insert(val); + * boolean param_2 = obj.remove(val); + * int param_3 = obj.getRandom(); + */ \ No newline at end of file diff --git a/java/0387-first-unique-character-in-a-string.java b/java/0387-first-unique-character-in-a-string.java new file mode 100644 index 000000000..10a157431 --- /dev/null +++ b/java/0387-first-unique-character-in-a-string.java @@ -0,0 +1,14 @@ +class Solution { + public int firstUniqChar(String s) { + Map count = new HashMap<>(); + for (Character ch : s.toCharArray()) { + count.put(ch, count.getOrDefault(ch, 0) + 1); + } + for (int i = 0; i < s.length(); i++) { + if (count.get(s.charAt(i)) == 1) { + return i; + } + } + return -1; + } +} diff --git a/java/0392-is-subsequence.java b/java/0392-is-subsequence.java new file mode 100644 index 000000000..d4b769891 --- /dev/null +++ b/java/0392-is-subsequence.java @@ -0,0 +1,16 @@ +class Solution { + public boolean isSubsequence(String s, String t) { + int i = 0, j = 0; + while (i < s.length() && j < t.length()) { + if (s.charAt(i) == t.charAt(j)) { + i += 1; + } + j += 1; + } + if (i == s.length()) { + return true; + } else { + return false; + } + } +} diff --git a/java/0394-decode-string.java b/java/0394-decode-string.java new file mode 100644 index 000000000..a28bda52e --- /dev/null +++ b/java/0394-decode-string.java @@ -0,0 +1,33 @@ +class Solution { + + int index = 0; + + public String decodeString(String s) { + + StringBuilder decoded = new StringBuilder(); + while (index < s.length() && s.charAt(index) != ']') { + + // character is a letter of encoded + if (!Character.isDigit(s.charAt(index))) decoded.append(s.charAt(index++)); + + // character is number or [ ] + else { + int k = 0; + + // case: number + while (index < s.length() && Character.isDigit(s.charAt(index))) k = k * 10 + s.charAt(index++) - '0'; + + // case: [ + index++; + String answer = decodeString(s); + + // case: ] + index++; + + // add k*encoded to decoded + while (k-- > 0) decoded.append(answer); + } + } + return new String(decoded); + } +} diff --git a/java/0399-evaluate-division.java b/java/0399-evaluate-division.java new file mode 100644 index 000000000..98c6fe698 --- /dev/null +++ b/java/0399-evaluate-division.java @@ -0,0 +1,47 @@ +class Solution { + public double[] calcEquation(List> equations, double[] values, List> queries) { + Map>> adj = new HashMap<>(); + for (int i = 0; i < equations.size(); i++) { + List equation = equations.get(i); + adj.computeIfAbsent( + equation.get(0), k -> new ArrayList<>()).add( + new Pair<>(equation.get(1), values[i])); + adj.computeIfAbsent( + equation.get(1), k -> new ArrayList<>()).add( + new Pair<>(equation.get(0), 1 / values[i])); + } + double[] res = new double[queries.size()]; + for (int i = 0; i < queries.size(); i++) { + List query = queries.get(i); + res[i] = bfs(adj, query.get(0), query.get(1)); + } + return res; + } + + private double bfs(Map>> adj, String src, String target) { + if (!adj.containsKey(src) || !adj.containsKey(target)) { + return -1.0; + } + ArrayDeque> queue = new ArrayDeque<>(); + Set visited = new HashSet<>(); + queue.addLast(new Pair<>(src, 1.0)); + visited.add(src); + while (!queue.isEmpty()) { + Pair item = queue.pollFirst(); + String node = item.getKey(); + Double curWeight = item.getValue(); + if (node.equals(target)) { + return curWeight; + } + for (Pair neighbor : adj.get(node)) { + String nextNode = neighbor.getKey(); + Double weight = neighbor.getValue(); + if (!visited.contains(nextNode)) { + queue.addLast(new Pair<>(nextNode, curWeight * weight)); + visited.add(nextNode); + } + } + } + return -1.0; + } +} diff --git a/java/0402-remove-k-digits.java b/java/0402-remove-k-digits.java new file mode 100644 index 000000000..45b4a82be --- /dev/null +++ b/java/0402-remove-k-digits.java @@ -0,0 +1,31 @@ +class Solution { + public String removeKdigits(String num, int k) { + if(k >= num.length()) + return "0"; + + Stack stack = new Stack<>(); + for(char d: num.toCharArray()){ + while(!stack.empty() && d < stack.peek() && k > 0){ + stack.pop(); + k--; + } + stack.push(d); + } + while(!stack.empty() && k > 0){ + stack.pop(); + k--; + } + + StringBuilder res = new StringBuilder(); + while(!stack.empty()){ + res.insert(0, stack.pop()); + } + + for(int i = 0; i < res.length(); i++){ + if(res.charAt(i) != '0'){ + return res.toString().substring(i, res.length()); + } + } + return "0"; + } +} diff --git a/java/0408-valid-word-abbreviation.java b/java/0408-valid-word-abbreviation.java new file mode 100644 index 000000000..f47eec5c7 --- /dev/null +++ b/java/0408-valid-word-abbreviation.java @@ -0,0 +1,21 @@ +class Solution { + public boolean validWordAbbreviation(String word, String abbr) { + int i = 0, j = 0; + while (i < word.length() && j < abbr.length()) { + if (word.charAt(i) == abbr.charAt(j)) { + i++; + j++; + continue; + } + if (abbr.charAt(j) <= '0' || abbr.charAt(j) > '9') + return false; + int num = 0; + while (j < abbr.length() && Character.isDigit(abbr.charAt(j))) { + num = num * 10 + abbr.charAt(j) - '0'; + j++; + } + i += num; + } + return i == word.length() && j == abbr.length(); + } +} \ No newline at end of file diff --git a/java/0410-split-array-largest-sum.java b/java/0410-split-array-largest-sum.java new file mode 100644 index 000000000..192423a00 --- /dev/null +++ b/java/0410-split-array-largest-sum.java @@ -0,0 +1,35 @@ +class Solution { + public int splitArray(int[] nums, int k) { + int start = 0; + int end = 0; + + for (int i = 0; i < nums.length; i++) { + start = Math.max(start, nums[i]); + end += nums[i]; + } + + while (start < end) { + int mid = start + (end - start) / 2; + + // calculate how many pieces you can divide this in with this max sum + int sum = 0; + int pieces = 1; + for(int num : nums) { + if (sum + num > mid) { + sum = num; + pieces++; + } else { + sum += num; + } + } + + if (pieces > k) { + start = mid + 1; + } else { + end = mid; + } + + } + return end; // here start == end + } +} \ No newline at end of file diff --git a/java/0416-partition-equal-subset-sum.java b/java/0416-partition-equal-subset-sum.java new file mode 100644 index 000000000..09200ce94 --- /dev/null +++ b/java/0416-partition-equal-subset-sum.java @@ -0,0 +1,47 @@ +class Solution { + + // TC = O(n*sum), SC = O(n*sum) + public boolean canPartition(int[] nums) { + int sum = 0; + for (int num : nums) { + sum += num; + } + if (sum % 2 != 0) return false; + int target = sum / 2; + boolean[][] dp = new boolean[nums.length + 1][target + 1]; + int n = nums.length; + for (int i = 0; i <= n; i++) { + for (int j = 0; j <= target; j++) { + if (i == 0 || j == 0) { + if (i == 0) dp[i][j] = false; else if (j == 0) dp[i][j] = + true; + } else if (j >= nums[i - 1]) { + dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i - 1]]; + } else { + dp[i][j] = dp[i - 1][j]; + } + } + } + return dp[n][target]; + } + + // TC = O(n*sum), SC = O(sum) + public boolean canPartition(int[] nums) { + int sum = Arrays.stream(nums).sum(); + if (sum % 2 != 0) return false; + + int target = sum / 2; + boolean[] dp = new boolean[target]; + dp[0] = true; + + for (int no : nums) { + for (int i = target; i >= no; i--) { + if (dp[i - no] == true) { + if (i == target) return true; + dp[i] = true; + } + } + } + return false; + } +} diff --git a/java/0417-pacific-atlantic-water-flow.java b/java/0417-pacific-atlantic-water-flow.java new file mode 100644 index 000000000..8f6e5ec0c --- /dev/null +++ b/java/0417-pacific-atlantic-water-flow.java @@ -0,0 +1,47 @@ +class Solution { + + int[][] dir = { { 0, 1 }, { 0, -1 }, { 1, 0 }, { -1, 0 } }; + + public List> pacificAtlantic(int[][] heights) { + List> res = new ArrayList<>(); + + int rows = heights.length, cols = heights[0].length; + boolean[][] pacific = new boolean[rows][cols]; + boolean[][] atlantic = new boolean[rows][cols]; + + for (int i = 0; i < cols; i++) { + dfs(heights, 0, i, Integer.MIN_VALUE, pacific); + dfs(heights, rows - 1, i, Integer.MIN_VALUE, atlantic); + } + + for (int i = 0; i < rows; i++) { + dfs(heights, i, 0, Integer.MIN_VALUE, pacific); + dfs(heights, i, cols - 1, Integer.MIN_VALUE, atlantic); + } + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + if (pacific[i][j] && atlantic[i][j]) { + res.add(List.of(i, j)); + } + } + } + return res; + } + + private void dfs( + int[][] heights, + int i, + int j, + int prev, + boolean[][] ocean + ) { + if (i < 0 || i >= ocean.length || j < 0 || j >= ocean[0].length) return; + if (heights[i][j] < prev || ocean[i][j]) return; + + ocean[i][j] = true; + for (int[] d : dir) { + dfs(heights, i + d[0], j + d[1], heights[i][j], ocean); + } + } +} diff --git a/java/0424-longest-repeating-character-replacement.java b/java/0424-longest-repeating-character-replacement.java new file mode 100644 index 000000000..1ce9988b4 --- /dev/null +++ b/java/0424-longest-repeating-character-replacement.java @@ -0,0 +1,19 @@ +class Solution { + + public int characterReplacement(String s, int k) { + int[] arr = new int[26]; + int ans = 0; + int max = 0; + int i = 0; + for (int j = 0; j < s.length(); j++) { + arr[s.charAt(j) - 'A']++; + max = Math.max(max, arr[s.charAt(j) - 'A']); + if (j - i + 1 - max > k) { + arr[s.charAt(i) - 'A']--; + i++; + } + ans = Math.max(ans, j - i + 1); + } + return ans; + } +} diff --git a/java/0427-construct-quad-tree.java b/java/0427-construct-quad-tree.java new file mode 100644 index 000000000..ef29bbc55 --- /dev/null +++ b/java/0427-construct-quad-tree.java @@ -0,0 +1,42 @@ +class Solution { + + private int[][] grid; + + public Node construct(int[][] grid) { + this.grid = grid; + return dfs(0, 0, grid.length); + } + + private Node dfs(int row, int column, int n) { + Node node = new Node(); + + if (areAllEqual(row, column, n)) { + node.isLeaf = true; + node.val = grid[row][column] == 1; + } else { + n /= 2; + node.isLeaf = false; + node.val = false; + + node.topLeft = dfs(row, column, n); + node.bottomLeft = dfs(row + n, column, n); + node.topRight = dfs(row, column + n, n); + node.bottomRight = dfs(row + n, column + n, n); + } + + return node; + } + + private boolean areAllEqual(int row, int column, int n) { + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (grid[row][column] != grid[row + i][column + j]) { + return false; + } + } + } + + return true; + } + +} diff --git a/java/0435-non-overlapping-intervals.java b/java/0435-non-overlapping-intervals.java new file mode 100644 index 000000000..a189ba0a0 --- /dev/null +++ b/java/0435-non-overlapping-intervals.java @@ -0,0 +1,35 @@ +public class Solution { + + public int eraseOverlapIntervals(int[][] intervals) { + int intervalsRemoved = 0; + + Arrays.sort( + intervals, + (arr1, arr2) -> Integer.compare(arr1[0], arr2[0]) + ); + + int[] intervalFirst = intervals[0]; + + for (int i = 1; i < intervals.length; i++) { + if (firstIntervalwithinSecond(intervalFirst, intervals[i])) { + //mark first interval to be removed + intervalsRemoved++; + // determine which interval to remove + //remove the interval that ends last + if (intervalFirst[1] > intervals[i][1]) { + intervalFirst = intervals[i]; + } + } else { + intervalFirst = intervals[i]; + } + } + return intervalsRemoved; + } + + public boolean firstIntervalwithinSecond( + int[] intervalFirst, + int[] intervalSecond + ) { + return intervalSecond[0] < intervalFirst[1]; + } +} diff --git a/java/0438-find-all-anagrams-in-a-string.java b/java/0438-find-all-anagrams-in-a-string.java new file mode 100644 index 000000000..77e2e9edc --- /dev/null +++ b/java/0438-find-all-anagrams-in-a-string.java @@ -0,0 +1,29 @@ +class Solution { + public List findAnagrams(String s, String p) { + int startIndex = 0; + Map pMap = new HashMap<>(), sMap = new HashMap<>(); + List res = new ArrayList<>(); + + for(char c: p.toCharArray()) + pMap.put(c, 1 + pMap.getOrDefault(c, 0)); + + for(int i = 0; i < s.length(); i++) { + sMap.put(s.charAt(i), 1 + sMap.getOrDefault(s.charAt(i), 0)); + + if(i >= p.length() - 1) { + if(sMap.equals(pMap)) + res.add(startIndex); + + //if current character is in sMap, remove it and re-update the map. + if(sMap.containsKey(s.charAt(startIndex))) { + sMap.put(s.charAt(startIndex), sMap.get(s.charAt(startIndex)) - 1); + if(sMap.get(s.charAt(startIndex)) == 0) + sMap.remove(s.charAt(startIndex)); + } + startIndex += 1; + } + } + + return res; + } +} diff --git a/java/0441-arranging-coins.java b/java/0441-arranging-coins.java new file mode 100644 index 000000000..be30d3c00 --- /dev/null +++ b/java/0441-arranging-coins.java @@ -0,0 +1,22 @@ +class Solution { + public int arrangeCoins(int n) { + + int completedRows = 0; + long left = 1; + long right = n; + while (left <= right) { + + int mid = (int) ((left + right) / 2); + long coins = (long) ((mid / 2.0) * (mid + 1)); + if (coins > n) { + right = mid - 1; + } else { + completedRows = Math.max(completedRows, mid); + left = mid + 1; + } + + } + + return completedRows; + } +} \ No newline at end of file diff --git a/java/0442-find-all-duplicates-in-an-array.java b/java/0442-find-all-duplicates-in-an-array.java new file mode 100644 index 000000000..5c47594aa --- /dev/null +++ b/java/0442-find-all-duplicates-in-an-array.java @@ -0,0 +1,14 @@ +class Solution { + public List findDuplicates(int[] nums) { + List res = new ArrayList<>(); + + for (int n : nums) { + n = Math.abs(n); + if (nums[n - 1] < 0) + res.add(n); + nums[n - 1] = -nums[n - 1]; + } + + return res; + } +} diff --git a/java/0446-arithmetic-slices-ii-subsequence.java b/java/0446-arithmetic-slices-ii-subsequence.java new file mode 100644 index 000000000..17eb40d67 --- /dev/null +++ b/java/0446-arithmetic-slices-ii-subsequence.java @@ -0,0 +1,21 @@ +public class Solution { + public int numberOfArithmeticSlices(int[] nums) { + int res = 0; + int n = nums.length; + + Map[] dp = new HashMap[n]; + for (int i = 0; i < n; i++) { + dp[i] = new HashMap<>(); + } + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + long diff = (long) nums[i] - (long) nums[j]; + dp[i].put(diff, dp[i].getOrDefault(diff, 0) + 1 + dp[j].getOrDefault(diff, 0)); + res += dp[j].getOrDefault(diff, 0); + } + } + + return res; + } +} diff --git a/java/0448-find-all-numbers-disappeared-in-an-array.java b/java/0448-find-all-numbers-disappeared-in-an-array.java new file mode 100644 index 000000000..6274ce07e --- /dev/null +++ b/java/0448-find-all-numbers-disappeared-in-an-array.java @@ -0,0 +1,20 @@ +// Since the array has values form 0 to n we can use in-place sorting that's O(N) time and constant space. +class Solution { + + public List findDisappearedNumbers(int[] nums) { + List list = new ArrayList<>(); + for (int i = 0; i < nums.length; i++) { + int correct = nums[i] - 1; + if (nums[i] != nums[correct]) { + int temp = nums[i]; + nums[i] = nums[correct]; + nums[correct] = temp; + i--; + } + } + for (int i = 0; i < nums.length; i++) { + if (nums[i] - 1 != i) list.add(i + 1); + } + return list; + } +} diff --git a/java/0450-delete-node-in-a-bst.java b/java/0450-delete-node-in-a-bst.java new file mode 100644 index 000000000..a7dbc8e63 --- /dev/null +++ b/java/0450-delete-node-in-a-bst.java @@ -0,0 +1,34 @@ +/** + * TC : log (n) + * + * */ +class Solution { + public TreeNode minimumVal(TreeNode root) { + TreeNode curr = root; + while (curr != null && curr.left != null) { + curr = curr.left; + } + return curr; + } + + public TreeNode deleteNode(TreeNode root, int key) { + if (root == null) return null; + + if (key > root.val) { + root.right = deleteNode(root.right, key); + } else if (key < root.val) { + root.left = deleteNode(root.left, key); + } else { + if (root.left == null) { + return root.right; + } else if (root.right == null) { + return root.left; + } else { + TreeNode minVal = minimumVal(root.right); + root.val = minVal.val; + root.right = deleteNode(root.right, minVal.val); + } + } + return root; + } +} diff --git a/java/0451-sort-characters-by-frequency.java b/java/0451-sort-characters-by-frequency.java new file mode 100644 index 000000000..68cbe67bf --- /dev/null +++ b/java/0451-sort-characters-by-frequency.java @@ -0,0 +1,31 @@ +class Solution { + public String frequencySort(String s) { + Map map = new HashMap<>(); + for(char c: s.toCharArray()) + map.put(c, map.getOrDefault(c, 0) + 1); + + PriorityQueue q = new PriorityQueue<>((a, b) -> b.f - a.f); + for(char c: map.keySet()) + q.add(new pair(c, map.get(c))); + + StringBuilder res = new StringBuilder(); + while(!q.isEmpty()){ + pair r = q.poll(); + int f = r.f; + while(f > 0){ + res.append(r.c); + f--; + } + } + return res.toString(); + } +} +class pair{ + char c; + int f; + + public pair(char c, int f){ + this.c = c; + this.f = f; + } +} diff --git a/java/0452-minimum-number-of-arrows-to-burst-balloons.java b/java/0452-minimum-number-of-arrows-to-burst-balloons.java new file mode 100644 index 000000000..49e3e336a --- /dev/null +++ b/java/0452-minimum-number-of-arrows-to-burst-balloons.java @@ -0,0 +1,21 @@ +class Solution { + public int findMinArrowShots(int[][] points) { + Arrays.sort(points, Comparator.comparingInt((int[] a) -> a[0]) + .thenComparingInt((int[] a) -> a[1])); + + var res = points.length; + var prev = points[0]; + for (int i = 1; i < points.length; i++) { + int[] curr = points[i]; + if (curr[0] <= prev[1]) { + res--; + prev[0] = curr[0]; + prev[1] = Math.min(curr[1], prev[1]); + } else { + prev = curr; + } + } + + return res; + } +} diff --git a/java/0455-assign-cookies.java b/java/0455-assign-cookies.java new file mode 100644 index 000000000..98de8dad7 --- /dev/null +++ b/java/0455-assign-cookies.java @@ -0,0 +1,17 @@ +class Solution { + public int findContentChildren(int[] g, int[] s) { + Arrays.sort(g); + Arrays.sort(s); + + int i = 0, j = 0; + while(i < g.length){ + while(j < s.length && g[i] > s[j]) + j += 1; + if(j == s.length) + break; + i += 1; + j += 1; + } + return i; + } +} diff --git a/java/0456-132-pattern.java b/java/0456-132-pattern.java new file mode 100644 index 000000000..d95fcbb82 --- /dev/null +++ b/java/0456-132-pattern.java @@ -0,0 +1,23 @@ +class Solution { + /* + * Mono Stack + * TC: O(n) + * SC: O(n) + */ + public boolean find132pattern(int[] nums) { + int n = nums.length; + Stack st = new Stack<>(); + int secondMax = Integer.MIN_VALUE; + + for (int x = n - 1; x >= 0; x--) { + if (nums[x] < secondMax) + return true; + + while (!st.isEmpty() && st.peek() < nums[x]) + secondMax = Math.max(st.pop(), secondMax); + + st.push(nums[x]); + } + return false; + } +} diff --git a/java/0460-lfu-cache.java b/java/0460-lfu-cache.java new file mode 100644 index 000000000..cc76af55a --- /dev/null +++ b/java/0460-lfu-cache.java @@ -0,0 +1,71 @@ +class LFUCache { + private static class Entry { + int key; + int value; + int freq; + + public Entry(int key, int value, int freq) { + this.key = key; + this.value = value; + this.freq = freq; + } + } + + private final int capacity; + private int minFreq = 0; + private final Map entries = new HashMap<>(); + private final Map> freqMap = new HashMap<>(); + + public LFUCache(int capacity) { + this.capacity = capacity; + } + + public int get(int key) { + Entry e = entries.get(key); + if (e == null) return -1; + updateFrequency(e); + return e.value; + } + + public void put(int key, int value) { + if (capacity == 0) return; + if (entries.containsKey(key)) { + Entry e = entries.get(key); + e.value = value; + updateFrequency(e); + } else { + if (entries.size() == capacity) { + evictLeastFrequent(); + } + Entry e = new Entry(key, value, 1); + entries.put(key, e); + freqMap.computeIfAbsent(1, k -> new LinkedHashSet<>()).add(e); + minFreq = 1; // Reset min frequency to 1 for new entry + } + } + + private void updateFrequency(Entry e) { + int oldFreq = e.freq; + int newFreq = oldFreq + 1; + + freqMap.get(oldFreq).remove(e); + if (freqMap.get(oldFreq).isEmpty()) { + freqMap.remove(oldFreq); + if (minFreq == oldFreq) minFreq++; + } + + e.freq = newFreq; + freqMap.computeIfAbsent(newFreq, k -> new LinkedHashSet<>()).add(e); + } + + private void evictLeastFrequent() { + LinkedHashSet minFreqEntries = freqMap.get(minFreq); + Entry toRemove = minFreqEntries.iterator().next(); + minFreqEntries.remove(toRemove); + if (minFreqEntries.isEmpty()) { + freqMap.remove(minFreq); + } + + entries.remove(toRemove.key); + } +} diff --git a/java/0463-island-perimeter.java b/java/0463-island-perimeter.java new file mode 100644 index 000000000..061c8d272 --- /dev/null +++ b/java/0463-island-perimeter.java @@ -0,0 +1,33 @@ +class Solution { + class RecursiveBiFunction { + BiFunction func; + } + + public int islandPerimeter(int[][] _grid) { + final int[][] grid = _grid; + final Set visit = new HashSet<>(); + + final RecursiveBiFunction dfs = new RecursiveBiFunction(); + dfs.func = (i, j) -> { + if(i >= grid.length || j >= grid[0].length || i < 0 || j < 0 || grid[i][j] == 0) + return 1; + //convert 2D-Coordinate to 1D-Coordinate + int flatCoord = i*grid[0].length + j; + if(visit.contains(flatCoord)) + return 0; + + visit.add(flatCoord); + int perim = dfs.func.apply(i, j + 1); + perim += dfs.func.apply(i + 1, j); + perim += dfs.func.apply(i, j - 1); + perim += dfs.func.apply(i - 1, j); + return perim; + }; + + for(int i = 0; i < grid.length; i++) + for(int j = 0; j < grid[0].length; j++) + if(grid[i][j] != 0) + return dfs.func.apply(i, j); + return -1; + } +} diff --git a/java/0473-matchsticks-to-square.java b/java/0473-matchsticks-to-square.java new file mode 100644 index 000000000..7e449b8be --- /dev/null +++ b/java/0473-matchsticks-to-square.java @@ -0,0 +1,40 @@ +class Solution { + boolean[] used; + public boolean makesquare(int[] matchsticks) { + used = new boolean[matchsticks.length]; + int total = 0; + for (int n : matchsticks) { + total += n; + } + //Check if total of all the sides is divisible by 4 or not + if (total % 4 != 0) return false; + int side = total / 4; + + return helper(matchsticks, side, 0, 0, 4); + } + + boolean helper(int[] matchsticks, int targetSide, int currentSum, int index, int sides) { + //if all the sides are matching the target side length then we found a solution + if (sides == 0) + return true; + //Check if current side is equal to targetSide , that means we found another side + if (currentSum == targetSide) { + return helper(matchsticks, targetSide, 0, 0, sides - 1); + } + + for (int i = index; i < matchsticks.length; i++) { + //Only use matchsticks which are not used and which doesn't increase the current side more than target side + if (!used[i] && currentSum + matchsticks[i] <= targetSide) { + used[i] = true; + boolean found = helper(matchsticks, targetSide, currentSum + matchsticks[i], i + 1, sides); + if (found) { + return true; + } + used[i] = false; + } + } + return false; + } + + +} \ No newline at end of file diff --git a/java/0474-ones-and-zeroes.java b/java/0474-ones-and-zeroes.java new file mode 100644 index 000000000..2e4aa965f --- /dev/null +++ b/java/0474-ones-and-zeroes.java @@ -0,0 +1,15 @@ +class Solution { + public int findMaxForm(String[] strs, int m, int n) { + int[][] dp = new int[m + 1][n + 1]; + for (String str : strs) { + int zeros = (int) str.chars().filter(ch -> ch == '0').count(); + int ones = (int) str.chars().filter(ch -> ch == '1').count(); + for (int i = m; i >= zeros; i--) { + for (int j = n; j >= ones; j--) { + dp[i][j] = Math.max(dp[i][j], dp[i - zeros][j - ones] + 1); + } + } + } + return dp[m][n]; + } +} diff --git a/java/0494-target-sum.java b/java/0494-target-sum.java new file mode 100644 index 000000000..b44594bed --- /dev/null +++ b/java/0494-target-sum.java @@ -0,0 +1,81 @@ +// Solution Shown In Video + +public class Solution { + HashMap dp; + + public int findTargetSumWays(int[] nums, int S) { + dp = new HashMap<>(); + return calculate(nums, 0, 0, S); + } + + public int calculate(int[] nums, int i, int sum, int S) { + String s = i + "," + sum; + + if (i == nums.length) { + return (sum == S)? 1 : 0; + } + if(dp.containsKey(s)){ + return dp.get(s); + } + + int res = calculate(nums, i + 1, sum + nums[i], S) + calculate(nums, i + 1, sum - nums[i], S); + dp.put(s, res); + return res; + } +} + +/* Alternative Better Complexity Solution +------------------------------------------------------------------------------------------------------- +//Brute-force solution (accepted) +// Subset Sum DP solution (Recursive DP solution for java exceeds time limit) + + * Calculate for the sum of all the potential positive numbers (targetSum) + * + * Formula: targetSum = (∑nums + target) / 2 + * (must be even otherwise there's no valid answer so return 0) + * + * Return all the possible ways to get the targetSum + * since the remaining numbers would be negative we just need to account + * for the sum of the positive numbers (targetSum) + * + * The formula for the targetSum was derived as follows: + * P = potential positive numbers + * N = potential negative numbers + * ∑P - ∑N = target + * ∑P - ∑N + ∑nums = target + ∑nums + * ∑P - ∑N + (∑P + ∑N) = target + ∑nums + * ∑P + ∑P = target + ∑nums + * 2 * ∑P = target + ∑nums + * (2 * ∑P) / 2 = (target + ∑nums) / 2 + * ∑P = (target + ∑nums) / 2 + ------------------------------------------------------------------------------- +class Solution { + public int subsetSum(int[] nums, int targetSum) { + int[] dp = new int[targetSum + 1]; + dp[0] = 1; + + for (int n : nums) { + for (int i = targetSum; i >= n; i--) { + dp[i] += dp[i - n]; + } + } + + return dp[targetSum]; + } + + public int findTargetSumWays(int[] nums, int target) { + int targetSum = 0; + + for (int n : nums) { + targetSum += n; + } + + return (targetSum < target + || targetSum + target < 0 + || (targetSum + target) % 2 != 0) + ? 0 + : subsetSum(nums, (targetSum + target) / 2); + } +} +---------------------------------------------------------------------------------- +*/ diff --git a/java/0496-next-greater-element-i.java b/java/0496-next-greater-element-i.java new file mode 100644 index 000000000..e8d92f07b --- /dev/null +++ b/java/0496-next-greater-element-i.java @@ -0,0 +1,26 @@ +class Solution { + public int[] nextGreaterElement(int[] nums1, int[] nums2) { + + int[] res = new int[nums1.length]; + int counter=0; + + for(int i: nums1){ + res[counter++]=ans(i, nums2); + } + + return res; + + } + + private int ans(int i, int[] nums){ + for(int n=0; ni) + return nums[j]; + } + } + } + return -1; + } +} \ No newline at end of file diff --git a/java/0501-find-mode-in-binary-search-tree.java b/java/0501-find-mode-in-binary-search-tree.java new file mode 100644 index 000000000..bb2619b7d --- /dev/null +++ b/java/0501-find-mode-in-binary-search-tree.java @@ -0,0 +1,46 @@ +class Solution { + /** + * BFS Solution with Runtime Complexity O(n) -> O(n log n) + */ + public int[] findMode(TreeNode root) { + if (root == null) return new int[0]; + List curr = new ArrayList<>(); + Map freqMap = new HashMap<>(); + Queue q = new ArrayDeque<>(); + q.offer(root); + + while (!q.isEmpty()) { + int n = q.size(); + for (int i = 0; i < n; i++) { + TreeNode node = q.poll(); + if (freqMap.containsKey(node.val)) + freqMap.put(node.val, freqMap.get(node.val) + 1); + else + freqMap.put(node.val, 1); + + if (node.left != null) q.offer(node.left); + if (node.right != null) q.offer(node.right); + } + } + + int currMax = 0; + for (Map.Entry entry : freqMap.entrySet()) { + int key = entry.getKey(); + int value = entry.getValue(); + + if (value > currMax) { + curr.clear(); + curr.add(key); + currMax = Math.max(currMax, value); + } else if (value == currMax){ + curr.add(key); + } + } + + int[] res = new int[curr.size()]; + for (int j = 0; j < curr.size(); j++) { + res[j] = curr.get(j); + } + return res; + } +} \ No newline at end of file diff --git a/java/0502-ipo.java b/java/0502-ipo.java new file mode 100644 index 000000000..593dea5e0 --- /dev/null +++ b/java/0502-ipo.java @@ -0,0 +1,31 @@ +class Solution { + public int findMaximizedCapital(int k, int w, int[] profits, int[] capital) { + // Max-heap for profits of affordable projects + Queue maxProfit = new PriorityQueue<>(Comparator.reverseOrder()); + + // Min-heap for (capital, profit) pairs + Queue minCapital = new PriorityQueue<>(Comparator.comparingInt(a -> a[0])); + + for (int i = 0; i < capital.length; i++) { + minCapital.add(new int[] { capital[i], profits[i] }); + } + + for (int i = 0; i < k; i++) { + // Add all affordable projects to the maxProfit heap + while (!minCapital.isEmpty() && minCapital.peek()[0] <= w) { + int[] project = minCapital.poll(); + maxProfit.add(project[1]); + } + + // If there are no affordable projects, break + if (maxProfit.isEmpty()) { + break; + } + + // Select the project with the maximum profit + w += maxProfit.poll(); + } + + return w; + } +} diff --git a/java/0513-find-bottom-left-tree-value.java b/java/0513-find-bottom-left-tree-value.java new file mode 100644 index 000000000..e485a9e7d --- /dev/null +++ b/java/0513-find-bottom-left-tree-value.java @@ -0,0 +1,26 @@ +class Solution { + + static class pair { + TreeNode node; + int level; + pair(TreeNode node, int level) { + this.node = node; + this.level = level; + } + } + + public static int findBottomLeftValue(TreeNode root) { + Queue q = new ArrayDeque<>(); + q.add(new pair(root, 0)); + pair ans = new pair(root, 0); + while (!q.isEmpty()) { + pair current = q.poll(); + TreeNode currentNode = current.node; + int currentLevel = current.level; + if (currentNode.left != null) q.add(new pair(currentNode.left, currentLevel + 1)); + if (currentNode.right != null) q.add(new pair(currentNode.right, currentLevel + 1)); + if (ans.level < currentLevel) ans = current; + } + return ans.node.val; + } +} \ No newline at end of file diff --git a/java/0514-freedom-trail.java b/java/0514-freedom-trail.java new file mode 100644 index 000000000..eb56152c6 --- /dev/null +++ b/java/0514-freedom-trail.java @@ -0,0 +1,27 @@ +class Solution { + Map cache = new HashMap<>(); + + public int findRotateSteps(String ring, String key) { + return helper(ring, key, 0, 0); + } + + private int helper(String ring, String key, int r, int k) { + if (k == key.length()) + return 0; + String keyCache = r + "," + k; + if (cache.containsKey(keyCache)) + return cache.get(keyCache); + + int res = Integer.MAX_VALUE; + for (int i = 0; i < ring.length(); i++) { + if (ring.charAt(i) == key.charAt(k)) { + int minDist = Math.min(Math.abs(r - i), ring.length() - Math.abs(r - i)); + int steps = minDist + 1 + helper(ring, key, i, k + 1); + res = Math.min(res, steps); + } + } + + cache.put(keyCache, res); + return res; + } +} diff --git a/java/0518-coin-change-ii.java b/java/0518-coin-change-ii.java new file mode 100644 index 000000000..8e7a27dfc --- /dev/null +++ b/java/0518-coin-change-ii.java @@ -0,0 +1,21 @@ +// Dynammic Programming - Tabulation +// Time Complexity (n * amount) | Space Complexity (amount) where n is the length of coins +class Solution { + + public int change(int amount, int[] coins) { + int[] dp = new int[amount + 1]; + + // if amount is 0, there is only 1 way of making change (no money) + dp[0] = 1; + + for (int coin : coins) { + for (int i = 1; i <= amount; i++) { + if (coin <= i) { + dp[i] += dp[i - coin]; + } + } + } + + return dp[amount]; + } +} diff --git a/java/0523-continuous-subarray-sum.java b/java/0523-continuous-subarray-sum.java new file mode 100644 index 000000000..4f54f0635 --- /dev/null +++ b/java/0523-continuous-subarray-sum.java @@ -0,0 +1,20 @@ +//We are basically storing sum%k and storing it in the hashmap and checking it. +//Math logic is that the overall sum will get cancelled out because of modulo + +class Solution { + public boolean checkSubarraySum(int[] nums, int k) { + HashMap map = new HashMap<>(); + map.put(0, -1); + int sum = 0; + for (int i = 0; i=2) + return true; + if (!map.containsKey(rem)) + map.put(rem, i); + } + return false; + } +} diff --git a/java/0525-contiguous-array.java b/java/0525-contiguous-array.java new file mode 100644 index 000000000..243237caa --- /dev/null +++ b/java/0525-contiguous-array.java @@ -0,0 +1,28 @@ +class Solution { + public int findMaxLength(int[] nums) { + int zero = 0; + int one = 0; + int res = 0; + + HashMap diffIndex = new HashMap<>(); + + for (int i = 0; i < nums.length; i++) { + int n = nums[i]; + if (n == 0) + zero++; + else + one++; + if (diffIndex.get(one - zero) == null) + diffIndex.put(one - zero, i); + + if (one == zero) { + res = one + zero; + } else { + int idx = diffIndex.getOrDefault(one - zero, 0); + res = Math.max(res, i - idx); + } + } + + return res; + } +} diff --git a/java/0535-encode-and-decode-tinyurl.java b/java/0535-encode-and-decode-tinyurl.java new file mode 100644 index 000000000..56db59a72 --- /dev/null +++ b/java/0535-encode-and-decode-tinyurl.java @@ -0,0 +1,16 @@ +public class Codec { + Map map = new HashMap<>(); + + // Encodes a URL to a shortened URL. + public String encode(String longUrl) { + String key = "gjhgjhg7666"; + map.put(key, longUrl); + return key; + } + + // Decodes a shortened URL to its original URL. + public String decode(String shortUrl) { + return map.get(shortUrl); + + } +} diff --git a/java/0538-convert-bst-to-greater-tree.java b/java/0538-convert-bst-to-greater-tree.java new file mode 100644 index 000000000..61bc54071 --- /dev/null +++ b/java/0538-convert-bst-to-greater-tree.java @@ -0,0 +1,43 @@ +// Recursive solution +class Solution { + private int curSum = 0; + + public TreeNode convertBST(TreeNode root) { + convertBSTRecursive(root); + return root; + } + + private void convertBSTRecursive(TreeNode node) { + if (node == null) { + return; + } + + convertBSTRecursive(node.right); + int temp = node.val; + node.val += curSum; + curSum += temp; + convertBSTRecursive(node.left); + } +} + +// Iterative solution +class Solution { + public TreeNode convertBST(TreeNode root) { + Stack stack = new Stack<>(); + TreeNode cur = root; + + int curSum = 0; + while (cur != null || !stack.isEmpty()) { + while (cur != null) { + stack.push(cur); + cur = cur.right; + } + cur = stack.pop(); + cur.val += curSum; + curSum = cur.val; + cur = cur.left; + } + + return root; + } +} diff --git a/java/0540-single-element-in-a-sorted-array.java b/java/0540-single-element-in-a-sorted-array.java new file mode 100644 index 000000000..210894099 --- /dev/null +++ b/java/0540-single-element-in-a-sorted-array.java @@ -0,0 +1,26 @@ +/* +---------------------------------- + Time Complexity: O(log(n)) + Space Complexity: O(1) +---------------------------------*/ + +class Solution { + public int singleNonDuplicate(int[] nums) { + int l = 0; + int r = nums.length-1; + + while(l <= r){ + int m = l + (r-l)/2 ; + if((m - 1 < 0 || nums[m-1] != nums[m]) && (m + 1 == nums.length || nums[m] != nums[m+1])) + return nums[m]; + + int leftSize = 0; + leftSize = (nums[m-1] == nums[m])? m-1 : m; + if(leftSize % 2 == 1) + r = m - 1; + else + l = m + 1; + } + return -1; + } +} diff --git a/java/0543-diameter-of-binary-tree.java b/java/0543-diameter-of-binary-tree.java new file mode 100644 index 000000000..007071e61 --- /dev/null +++ b/java/0543-diameter-of-binary-tree.java @@ -0,0 +1,19 @@ +class Solution { + + int result = -1; + + public int diameterOfBinaryTree(TreeNode root) { + dfs(root); + return result; + } + + private int dfs(TreeNode current) { + if (current == null) { + return -1; + } + int left = 1 + dfs(current.left); + int right = 1 + dfs(current.right); + result = Math.max(result, (left + right)); + return Math.max(left, right); + } +} diff --git a/java/0554-brick-wall.java b/java/0554-brick-wall.java new file mode 100644 index 000000000..09ee2346a --- /dev/null +++ b/java/0554-brick-wall.java @@ -0,0 +1,17 @@ +class Solution { + public int leastBricks(List> wall) { + Map map = new HashMap<>(); + int maxGaps = 0; + + for(List row : wall) { + int pos = 0; + for(int i=0; i map = new HashMap(); + map.put(0, 1); + for (int i = 0; i < nums.length; i++) { + sum += nums[i]; + int diff = sum - k; + if (map.containsKey(diff)) { + res += map.get(diff); + } + map.put(sum, map.getOrDefault(sum, 0) + 1); + } + return res; + } +} diff --git a/java/0567-permutation-in-string.java b/java/0567-permutation-in-string.java new file mode 100644 index 000000000..60c8e302a --- /dev/null +++ b/java/0567-permutation-in-string.java @@ -0,0 +1,20 @@ +public class Solution { + public boolean checkInclusion(String s1, String s2) { + int n = s1.length(); + int[] freq = new int[26]; + int m = s2.length(); + for (int i = 0; i < n; i++) { + freq[s1.charAt(i) - 'a']++; + } + int[] freq2 = new int[26]; + for (int i = 0; i < m; i++) { + freq2[s2.charAt(i) - 'a']++; + if (i >= n) { + freq2[s2.charAt(i - n) - 'a']--; + } + if (Arrays.equals(freq, freq2)) + return true; + } + return false; + } +} \ No newline at end of file diff --git a/java/0572-subtree-of-another-tree.java b/java/0572-subtree-of-another-tree.java new file mode 100644 index 000000000..30bcf2abe --- /dev/null +++ b/java/0572-subtree-of-another-tree.java @@ -0,0 +1,24 @@ +// Solution: Recursive Approach + +// Time Complexity: O(n) +// Extra Space Complexity: O(n) +class Solution { + public boolean isSubtree(TreeNode root, TreeNode subRoot) { + if (subRoot == null || isSameTree(root, subRoot)) return true; + if (root == null) return false; + + return isSubtree(root.left, subRoot) || isSubtree(root.right, subRoot); + } + + private boolean isSameTree(TreeNode p, TreeNode q) { + if (p == null && q == null) { + return true; + } + + if (p == null || q == null || p.val != q.val) { + return false; + } + + return isSameTree(p.left, q.left) && isSameTree(p.right, q.right); + } +} diff --git a/java/0576-out-of-boundary-paths.java b/java/0576-out-of-boundary-paths.java new file mode 100644 index 000000000..274f59a77 --- /dev/null +++ b/java/0576-out-of-boundary-paths.java @@ -0,0 +1,23 @@ +class Solution { + int Rows, Cols; + int MOD = (int) 1e9 + 7; + public int findPaths(int m, int n, int maxMove, int startRow, int startColumn) { + Rows = m; + Cols = n; + Map cache = new HashMap<>(); + return dfs(startRow, startColumn, maxMove, cache); + } + private int dfs(int r, int c, int moves, Map cache){ + if(r < 0 || r >= Rows || c < 0 || c >= Cols) + return 1; + if(moves == 0) + return 0; + String pos = r + "," + c + "," + moves; + if(cache.containsKey(pos)) + return cache.get(pos); + + int res = ((dfs(r-1, c, moves-1, cache) + dfs(r+1, c, moves-1, cache))%MOD + (dfs(r, c-1, moves-1, cache) + dfs(r, c+1, moves-1, cache))%MOD)%MOD; + cache.put(pos, res); + return res; + } +} diff --git a/java/0605-can-place-flowers.java b/java/0605-can-place-flowers.java new file mode 100644 index 000000000..c5e665207 --- /dev/null +++ b/java/0605-can-place-flowers.java @@ -0,0 +1,14 @@ +class Solution { + public boolean canPlaceFlowers(int[] flowerbed, int n) { + int size = flowerbed.length; + if(n==0) return true; + for(int i=0; i pq = new PriorityQueue<>((a, b) -> b - a); + Queue> q = new LinkedList<>(); + int[] arr = new int[26]; + for (char c : tasks) arr[c - 'A']++; + for (int val : arr) if (val > 0) pq.add(val); + int time = 0; + + while ((!pq.isEmpty() || !q.isEmpty())) { + time++; + if (!pq.isEmpty()) { + int val = pq.poll(); + val--; + if (val > 0) q.add(new Pair(val, time + n)); + } + + if (!q.isEmpty() && q.peek().getValue() == time) pq.add( + q.poll().getKey() + ); + } + return time; + } +} diff --git a/java/0622-design-circular-queue.java b/java/0622-design-circular-queue.java new file mode 100644 index 000000000..44650d58c --- /dev/null +++ b/java/0622-design-circular-queue.java @@ -0,0 +1,97 @@ +class MyCircularQueue { + Node head=null; + Node tail=null; + + int currentLength=0; + int totalLength=0; + public MyCircularQueue(int k) { + head=new Node(-1); + tail=new Node(-1); + + head.next=tail; + head.prev=tail; + + tail.next=head; + tail.prev =head; + + totalLength=k; + } + + public boolean enQueue(int value) { + if(isFull()){ + return false; + } + + Node left = head.prev; + Node right= head; + + Node temp = new Node(value); + + temp.next= right; + + right.prev = temp; + + left.next=temp; + + temp.prev=left; + + currentLength++; + + + + return true; + } + + public boolean deQueue() { + if(isEmpty()){ + return false; + } + + Node left = tail; + Node right= tail.next.next; + + left.next = right; + + right.prev = left; + + + currentLength--; + + return true; + } + + public int Front() { + if(currentLength<=0){ + return -1; + } + + return tail.next.val; + } + + public int Rear() { + if(currentLength<=0){ + return -1; + } + + return head.prev.val; + } + + public boolean isEmpty() { + return currentLength==0 ? true : false; + } + + public boolean isFull() { + return currentLength==totalLength ? true : false; + } + +} + +class Node{ + int val; + Node next=null; + Node prev=null; + + Node(int val){ + this.val=val; + } +} diff --git a/java/0637-average-of-levels-in-binary-tree.java b/java/0637-average-of-levels-in-binary-tree.java new file mode 100644 index 000000000..99da71eee --- /dev/null +++ b/java/0637-average-of-levels-in-binary-tree.java @@ -0,0 +1,22 @@ +//Apply Breadth first search. +//Asked in Amazon and Meta +class Solution { + + public List averageOfLevels(TreeNode root) { + List ans = new ArrayList<>(); + Queue q = new LinkedList(); + q.offer(root); + while (!q.isEmpty()) { + int queue_size = q.size(); + double avg = 0; + for (int i = 0; i < queue_size; i++) { + TreeNode cur = q.poll(); + avg += cur.val; + if (cur.left != null) q.offer(cur.left); + if (cur.right != null) q.offer(cur.right); + } + ans.add(avg / queue_size); + } + return ans; + } +} diff --git a/java/0647-palindromic-substrings.java b/java/0647-palindromic-substrings.java new file mode 100644 index 000000000..eea84eaf3 --- /dev/null +++ b/java/0647-palindromic-substrings.java @@ -0,0 +1,26 @@ +class Solution { + public int countSubstrings(String s) { + int res = 0; + + for (int i = 0; i < s.length(); i++) { + res += countSubstrings(s, i, i); + res += countSubstrings(s, i, i + 1); + } + + return res; + } + + public int countSubstrings(String s, + int start, int end) { + int res = 0; + + while (start >= 0 && end < s.length() + && s.charAt(start) == s.charAt(end)) { + ++res; + --start; + ++end; + } + + return res; + } +} diff --git a/java/0658-find-k-closest-elements.java b/java/0658-find-k-closest-elements.java new file mode 100644 index 000000000..0144e46b6 --- /dev/null +++ b/java/0658-find-k-closest-elements.java @@ -0,0 +1,19 @@ +class Solution { + public List findClosestElements(int[] arr, int k, int x) { + int l =0; + int r = arr.length -k; + List result = new ArrayList<>(); + while( l < r){ + int m = (l+r) /2; + if(x - arr[m] > arr[m+k] -x ){ //right is closer + l = m+1; + }else{ //left is closer + r =m; + } + } + for(int i =l; i < l+k; i++){ //build the solution list + result.add(arr[i]); + } + return result; + } +} \ No newline at end of file diff --git a/java/0661-image-smoother.java b/java/0661-image-smoother.java new file mode 100644 index 000000000..25a8d56a0 --- /dev/null +++ b/java/0661-image-smoother.java @@ -0,0 +1,52 @@ +class Solution { + public int[][] imageSmoother(int[][] img) { + int ROWS = img.length, COLS = img[0].length; + int[][] res = new int[ROWS][COLS]; + + for(int r = 0; r < ROWS; r++){ + for(int c = 0; c < COLS; c++){ + int total = 0, cnt = 0; + for(int i = r - 1; i < r + 2; i++){ + for(int j = c - 1; j < c + 2; j++){ + if(i < 0 || i == ROWS || j < 0 || j == COLS) + continue; + total += img[i][j]; + cnt += 1; + } + } + res[r][c] = total / cnt; + } + } + return res; + } +} + +// Optimal Solution + +class Solution { + public int[][] imageSmoother(int[][] img) { + int ROWS = img.length, COLS = img[0].length; + + for(int r = 0; r < ROWS; r++){ + for(int c = 0; c < COLS; c++){ + int total = 0, cnt = 0; + + for(int i = r - 1; i < r + 2; i++){ + for(int j = c - 1; j < c + 2; j++){ + if(i < 0 || i == ROWS || j < 0 || j == COLS) + continue; + total += img[i][j] % 256; + cnt += 1; + } + } + img[r][c] = img[r][c] ^ (total / cnt) << 8; + } + } + for(int r = 0; r < ROWS; r++){ + for(int c = 0; c < COLS; c++){ + img[r][c] = img[r][c] >> 8; + } + } + return img; + } +} diff --git a/java/0662-maximum-width-of-binary-tree.java b/java/0662-maximum-width-of-binary-tree.java new file mode 100644 index 000000000..641914fda --- /dev/null +++ b/java/0662-maximum-width-of-binary-tree.java @@ -0,0 +1,36 @@ +class Solution { + public int widthOfBinaryTree(TreeNode root) { + int res = 0; + Queue q = new LinkedList<>(); + q.add(new tuple(root, 1, 0)); + int prevLevel = 0, prevNum = 1; + + while(!q.isEmpty()){ + tuple curr = q.poll(); + TreeNode node = curr.node; + int num = curr.num, level = curr.level; + + if(level > prevLevel){ + prevLevel = level; + prevNum = num; + } + res = Math.max(res, num - prevNum + 1); + if(node.left != null) + q.add(new tuple(node.left, num*2, level + 1)); + if(node.right != null) + q.add(new tuple(node.right, num*2 + 1, level + 1)); + } + return res; + } +} +class tuple{ + TreeNode node; + int num; + int level; + + public tuple(TreeNode node, int num, int level){ + this.node = node; + this.num = num; + this.level = level; + } +} diff --git a/java/0665-non-decreasing-array.java b/java/0665-non-decreasing-array.java new file mode 100644 index 000000000..28970bc8c --- /dev/null +++ b/java/0665-non-decreasing-array.java @@ -0,0 +1,21 @@ +class Solution { + public boolean checkPossibility(int[] nums) { + boolean changed = false; + + for (int i = 0; i < nums.length - 1; i++) { + if (nums[i] <= nums[i+1]) + continue; + if (changed) + return false; + + if (i == 0 || nums[i+1] >= nums[i-1]) + nums[i] = nums[i+1]; + else + nums[i+1] = nums[i]; + + changed = true; + } + + return true; + } +} \ No newline at end of file diff --git a/java/0669-trim-a-binary-search-tree.java b/java/0669-trim-a-binary-search-tree.java new file mode 100644 index 000000000..601458c74 --- /dev/null +++ b/java/0669-trim-a-binary-search-tree.java @@ -0,0 +1,16 @@ +class Solution { + public TreeNode trimBST(TreeNode root, int low, int high) { + if(root == null){ + return root; + } + if(root.val > high){ + return trimBST(root.left, low, high); + } + if(root.val < low){ + return trimBST(root.right, low, high); + } + root.left = trimBST(root.left, low, high); + root.right = trimBST(root.right, low, high); + return root; + } +} \ No newline at end of file diff --git a/java/0670-maximum-swap.java b/java/0670-maximum-swap.java new file mode 100644 index 000000000..2f14fa8e5 --- /dev/null +++ b/java/0670-maximum-swap.java @@ -0,0 +1,33 @@ +/*--------------------------------- + Time Complexity: O(n) + Space Complexity: O(n) +----------------------------------*/ + +class Solution { + public int maximumSwap(int num) { + StringBuilder str = new StringBuilder(Integer.toString(num)); + if(str.length() == 1){ + return num; + } + + int[] maxRight = new int[str.length()]; + int max = str.length()-1; + for(int i = str.length()-1; i >= 0; i--){ + maxRight[i] = max; + if(str.charAt(i) > str.charAt(max)){ + max = i; + } + } + + for(int i = 0; i < str.length(); i++){ + if(str.charAt(maxRight[i]) > str.charAt(i)){ + char t = str.charAt(maxRight[i]); + str.setCharAt(maxRight[i], str.charAt(i)); + str.setCharAt(i, t); + break; + } + } + + return Integer.parseInt(str.toString()); + } +} diff --git a/java/0673-number-of-longest-increasing-subsequence.java b/java/0673-number-of-longest-increasing-subsequence.java new file mode 100644 index 000000000..fdc4fa7b2 --- /dev/null +++ b/java/0673-number-of-longest-increasing-subsequence.java @@ -0,0 +1,31 @@ +class Solution { + public int findNumberOfLIS(int[] nums) { + int[][] dp = new int[nums.length][2]; + int LISLength = 0; + int LISCount = 0; + for (int i = nums.length - 1; i >= 0; i--) { + int maxLength = 1; + int maxCount = 1; + for (int j = i + 1; j < nums.length; j++) { + if (nums[j] > nums[i]) { + int length = dp[j][0]; + int count = dp[j][1]; + if (length + 1 > maxLength) { + maxLength = length + 1; + maxCount = count; + } else if (length + 1 == maxLength) { + maxCount += count; + } + } + } + if (maxLength > LISLength) { + LISLength = maxLength; + LISCount = maxCount; + } else if (maxLength == LISLength) { + LISCount += maxCount; + } + dp[i] = new int[] { maxLength, maxCount }; + } + return LISCount; + } +} diff --git a/java/0678-valid-parenthesis-string.java b/java/0678-valid-parenthesis-string.java new file mode 100644 index 000000000..74d30abfc --- /dev/null +++ b/java/0678-valid-parenthesis-string.java @@ -0,0 +1,54 @@ +class Solution { + + public boolean checkValidString(String s) { + int count = 0; + Stack op = new Stack<>(); + Stack st = new Stack<>(); + + for (int i = 0; i < s.length(); ++i) { + if (s.charAt(i) == '(') op.push(i); // index of opening + else if (s.charAt(i) == ')') { + if (op.size() > 0) op.pop(); // if we have brackets + else if (st.size() > 0) st.pop(); // if not brackets do we have stars + else return false; // a closing bracket without opening and star + } else st.push(i); // index of star + } + // if we left with some opening bracket that over stars con cover up + while (op.size() > 0 && st.size() > 0) { + if (op.peek() > st.peek()) return false; + op.pop(); + st.pop(); + } + + return op.size() == 0; + } +} + +// Time complexity: O(n) +// Space complexity: O(1) +class Solution2 { + + public boolean checkValidString(String s) { + int low = 0, high = 0; + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == '(') { + low++; + high++; + } else if (s.charAt(i) == ')') { + if (low > 0) { + low--; + } + high--; + } else { + if (low > 0) { + low--; + } + high++; + } + if (high < 0) { + return false; + } + } + return low == 0; + } +} diff --git a/java/0680-valid-palindrome-ii.java b/java/0680-valid-palindrome-ii.java new file mode 100644 index 000000000..faef2cfac --- /dev/null +++ b/java/0680-valid-palindrome-ii.java @@ -0,0 +1,24 @@ +class Solution { + public boolean validPalindrome(String s) { + int i = 0, j = s.length() - 1; + while(i < j) + if(s.charAt(i) == s.charAt(j)) { + i += 1; + j -= 1; + }else + return validPalindromeUtil(s, i + 1, j) || validPalindromeUtil(s, i, j - 1); + return true; + } + + boolean validPalindromeUtil(String s, int i, int j) { + while(i < j) + if(s.charAt(i) == s.charAt(j)) { + i += 1; + j -= 1; + } else { + return false; + } + + return true; + } +} diff --git a/java/0682-baseball-game.java b/java/0682-baseball-game.java new file mode 100644 index 000000000..1f98613c0 --- /dev/null +++ b/java/0682-baseball-game.java @@ -0,0 +1,29 @@ +class Solution { + public int calPoints(String[] operations) { + Stack st = new Stack<>(); + + for(String op : operations) { + if(op.equals("+") && st.size() >= 2) { + int score1 = st.pop(); + int score2 = st.peek(); + int score3 = score1 + score2; + st.push(score1); + st.push(score3); + } else if(op.equals("D") && !st.isEmpty()) { + int score = st.peek(); + st.push(score*2); + } else if(op.equals("C") && !st.isEmpty()) { + st.pop(); + } else { + st.push(Integer.parseInt(op)); + } + } + + int sum = 0; + while(!st.isEmpty()) { + sum += st.pop(); + } + + return sum; + } +} diff --git a/java/0684-redundant-connection.java b/java/0684-redundant-connection.java new file mode 100644 index 000000000..efa685eb8 --- /dev/null +++ b/java/0684-redundant-connection.java @@ -0,0 +1,27 @@ +class Solution { + + int[] parent; + + public int[] findRedundantConnection(int[][] edges) { + parent = new int[edges.length]; + for (int i = 0; i < edges.length; i++) parent[i] = i + 1; + + for (int[] edge : edges) { + if (find(edge[0]) == find(edge[1])) return edge; else union( + edge[0], + edge[1] + ); + } + + return new int[2]; + } + + public int find(int x) { + if (x == parent[x - 1]) return x; + return find(parent[x - 1]); + } + + public void union(int x, int y) { + parent[find(y) - 1] = find(x); + } +} diff --git a/java/0695-max-area-of-island.java b/java/0695-max-area-of-island.java new file mode 100644 index 000000000..684a58311 --- /dev/null +++ b/java/0695-max-area-of-island.java @@ -0,0 +1,30 @@ +class Solution { + int maxArea = 0; + + public int maxAreaOfIsland(int[][] grid) { + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + maxArea = Math.max(maxArea, + maxAreaOfIsland(grid, i, j)); + } + } + + return maxArea; + } + + public int maxAreaOfIsland(int[][] grid, int r, int c) { + if (r < 0 || c < 0 || + r == grid.length || + c == grid[0].length || + grid[r][c] == 0) { + return 0; + } + + grid[r][c] = 0; + + return (1 + maxAreaOfIsland(grid, r + 1, c) + + maxAreaOfIsland(grid, r - 1, c) + + maxAreaOfIsland(grid, r, c + 1) + + maxAreaOfIsland(grid, r, c - 1)); + } +} diff --git a/java/0698-partition-to-k-equal-sum-subsets.java b/java/0698-partition-to-k-equal-sum-subsets.java new file mode 100644 index 000000000..99bb87a54 --- /dev/null +++ b/java/0698-partition-to-k-equal-sum-subsets.java @@ -0,0 +1,37 @@ +class Solution { + int target; + + public boolean canPartitionKSubsets(int[] nums, int k) { + int sum = 0; + for(int n : nums){ + sum += n; + } + if(sum%k != 0) + return false; + + target = sum / k; + boolean[] used = new boolean[nums.length]; + return backtrack(nums, 0, k, 0, used); + } + + private boolean backtrack(int[] nums, int i, int k, int subsetSum, boolean[] used){ + if(k == 0) + return true; + if(subsetSum == target) + return backtrack(nums, 0, k-1, 0, used); + + for(int j = i; j < nums.length; j++){ + if(j > 0 && !used[j-1] && nums[j] == nums[j-1]) + continue; + if(used[j] || subsetSum + nums[j] > target) + continue; + + used[j] = true; + if(backtrack(nums, j+1, k, subsetSum + nums[j], used)) + return true; + + used[j] = false; + } + return false; + } +} diff --git a/java/0700-search-in-a-binary-search-tree.java b/java/0700-search-in-a-binary-search-tree.java new file mode 100644 index 000000000..c3cc2e033 --- /dev/null +++ b/java/0700-search-in-a-binary-search-tree.java @@ -0,0 +1,10 @@ +public TreeNode searchBST(TreeNode root, int val) { + if (root == null) { + return root; + } else if (root.val < val) { + return searchBST(root.right, val); + } else if (root.val > val) { + return searchBST(root.left, val); + } + return root; +} \ No newline at end of file diff --git a/java/0701-insert-into-a-binary-search-tree.java b/java/0701-insert-into-a-binary-search-tree.java new file mode 100644 index 000000000..05f4de3c7 --- /dev/null +++ b/java/0701-insert-into-a-binary-search-tree.java @@ -0,0 +1,37 @@ +class Solution { + public TreeNode insertIntoBST(TreeNode root, int val) { + if(root == null) return new TreeNode(val); + TreeNode curr = root; + while(true){ + if(curr.val <= val){ + if(curr.right != null){ + curr = curr.right; + }else{ + curr.right = new TreeNode(val); + break; + } + }else{ + if(curr.left != null) curr = curr.left; + else{ + curr.left = new TreeNode(val); + break; + } + } + } + return root; + } + + /* Using Recursive Solution + ------------------------------------------------------------------- + public TreeNode insertIntoBST(TreeNode root, int val) { + if(root == null) return new TreeNode(val); + if(root.val <= val){ + root.right = insertIntoBST(root.right, val); + }else{ + root.left = insertIntoBST(root.left, val); + } + return root; + } + ------------------------------------------------------------------- + */ +} diff --git a/java/0703-kth-largest-element-in-a-stream.java b/java/0703-kth-largest-element-in-a-stream.java new file mode 100644 index 000000000..e8b5655b5 --- /dev/null +++ b/java/0703-kth-largest-element-in-a-stream.java @@ -0,0 +1,19 @@ +class KthLargest { + + final PriorityQueue heap = new PriorityQueue<>(); + final int k; + + public KthLargest(int k, int[] nums) { + this.k = k; + for (int n : nums) add(n); + } + + public int add(int val) { + if (heap.size() < k) heap.offer(val); //for adding the values of the array + else if (val > heap.peek()) { + heap.poll(); //remove the top element + heap.add(val); //add the new element + } + return heap.peek(); + } +} diff --git a/java/0704-binary-search.java b/java/0704-binary-search.java new file mode 100644 index 000000000..7cd21ad3e --- /dev/null +++ b/java/0704-binary-search.java @@ -0,0 +1,19 @@ +class Solution { + + public int search(int[] nums, int target) { + int i = 0; + int j = nums.length - 1; + + while (i <= j) { + // mid is calculated this way to prevent integer overflow. + // See: https://blog.research.google/2006/06/extra-extra-read-all-about-it-nearly.html + int mid = i + (j - i) / 2; + + if (nums[mid] == target) return mid; else if ( + nums[mid] < target + ) i = mid + 1; else j = mid - 1; + } + + return -1; + } +} diff --git a/java/0705-design-hashset.java b/java/0705-design-hashset.java new file mode 100644 index 000000000..70341bbbf --- /dev/null +++ b/java/0705-design-hashset.java @@ -0,0 +1,18 @@ +class MyHashSet { + boolean [] setArray; + public MyHashSet() { + setArray=new boolean[(int)1e6+1]; + } + + public void add(int key) { + setArray[key]=true; + } + + public void remove(int key) { + setArray[key]=false; + } + + public boolean contains(int key) { + return setArray[key]; + } +} \ No newline at end of file diff --git a/java/0706-design-hashmap.java b/java/0706-design-hashmap.java new file mode 100644 index 000000000..f068c28c2 --- /dev/null +++ b/java/0706-design-hashmap.java @@ -0,0 +1,44 @@ +class ListNode { + int key, val; + ListNode next; + public ListNode(int key, int val, ListNode next) { + this.key = key; + this.val = val; + this.next = next; + } +} +class MyHashMap { + static final int size = 19997; + static final int mult = 12582917; + ListNode[] data; + public MyHashMap() { + this.data = new ListNode[size]; + } + private int hash(int key) { + return (int)((long)key * mult % size); + } + public void put(int key, int val) { + remove(key); + int h = hash(key); + ListNode node = new ListNode(key, val, data[h]); + data[h] = node; + } + public int get(int key) { + int h = hash(key); + ListNode node = data[h]; + for (; node != null; node = node.next) + if (node.key == key) return node.val; + return -1; + } + public void remove(int key) { + int h = hash(key); + ListNode node = data[h]; + if (node == null) return; + if (node.key == key) data[h] = node.next; + else for (; node.next != null; node = node.next) + if (node.next.key == key) { + node.next = node.next.next; + return; + } + } +} \ No newline at end of file diff --git a/java/0707-design-linked-list.java b/java/0707-design-linked-list.java new file mode 100644 index 000000000..82a279014 --- /dev/null +++ b/java/0707-design-linked-list.java @@ -0,0 +1,86 @@ +class ListNode { + int val; + ListNode prev; + ListNode next; + public ListNode(int val) { + this.val = val; + this.prev = null; + this.next = null; + } +} + +class MyLinkedList { + + ListNode left; + ListNode right; + + public MyLinkedList() { + left = new ListNode(0); + right = new ListNode(0); + left.next = right; + right.prev = left; + } + + public int get(int index) { + ListNode cur = left.next; + while(cur != null && index > 0) { + cur = cur.next; + index -= 1; + } + if(cur != null && cur != right && index == 0) { + return cur.val; + } + return -1; + } + + public void addAtHead(int val) { + ListNode node = new ListNode(val); + ListNode next = left.next; + ListNode prev = left; + prev.next = node; + next.prev = node; + node.next = next; + node.prev = prev; + } + + public void addAtTail(int val) { + ListNode node = new ListNode(val); + ListNode next = right; + ListNode prev = right.prev; + prev.next = node; + next.prev = node; + node.next = next; + node.prev = prev; + } + + public void addAtIndex(int index, int val) { + ListNode cur = left.next; + while(cur != null && index > 0) { + cur = cur.next; + index -= 1; + } + if(cur != null && index == 0) { + ListNode node = new ListNode(val); + ListNode next = cur; + ListNode prev = cur.prev; + prev.next = node; + next.prev = node; + node.next = next; + node.prev = prev; + } + } + + public void deleteAtIndex(int index) { + ListNode cur = left.next; + while(cur != null && index > 0) { + cur = cur.next; + index -= 1; + } + if(cur != null && cur != right && index == 0) { + ListNode next = cur.next; + ListNode prev = cur.prev; + next.prev = prev; + prev.next = next; + } + } +} diff --git a/java/0708-insert-into-a-sorted-circular-linked-list.java b/java/0708-insert-into-a-sorted-circular-linked-list.java new file mode 100644 index 000000000..830816e13 --- /dev/null +++ b/java/0708-insert-into-a-sorted-circular-linked-list.java @@ -0,0 +1,21 @@ +class Solution { + public Node insert(Node head, int insertVal) { + Node node = new Node(insertVal); + if (head == null) { + node.next = node; + return node; + } + Node prev = head, curr = head.next; + while (curr != head) { + if ((prev.val <= insertVal && insertVal <= curr.val) + || (prev.val > curr.val && (insertVal >= prev.val || insertVal <= curr.val))){ + break; + } + prev = curr; + curr = curr.next; + } + prev.next = node; + node.next = curr; + return head; + } +} diff --git a/java/0713-subarray-product-less-than-k.java b/java/0713-subarray-product-less-than-k.java new file mode 100644 index 000000000..4b2fa9066 --- /dev/null +++ b/java/0713-subarray-product-less-than-k.java @@ -0,0 +1,23 @@ +//Solution taken from Leetcode's solution section +//The answer is simple the only tricky thing is the ans+=right-left+1; because of which I got stuck +//Here's an amazing explanation for that | taken form https://leetcode.com/problems/subarray-product-less-than-k/solution/348324 +//Say, if we have an array nums = [10,5,2,6] and k = 100 +//At first, we have window with 10 so ans = 0+1 and product will be 10 +//After that we will move the window since product= k) product /= nums[left++]; + ans += right - left + 1; + } + return ans; + } +} diff --git a/java/0721-accounts-merge.java b/java/0721-accounts-merge.java new file mode 100644 index 000000000..4f3bcb433 --- /dev/null +++ b/java/0721-accounts-merge.java @@ -0,0 +1,75 @@ +class Solution { + public List> accountsMerge(List> accounts) { + int n = accounts.size(); + DSU dsu = new DSU(n); + + Map map = new HashMap<>(); // email -> index of acc + + for(int i = 0; i < n; i++){ + for(int j = 1; j < accounts.get(i).size(); j++){ + String email = accounts.get(i).get(j); + String name = accounts.get(i).get(0); + + if(!map.containsKey(email)) + map.put(email, i); + else + dsu.union(i, map.get(email)); + } + } + + Map> merged = new HashMap<>(); // index of acc -> list of emails + for(String email : map.keySet()){ + int group = map.get(email); + int lead = dsu.find(group); + + if(!merged.containsKey(lead)) + merged.put(lead, new ArrayList()); + + merged.get(lead).add(email); + } + + List> res = new ArrayList<>(); + for(int ac : merged.keySet()){ + List grp = merged.get(ac); + Collections.sort(grp); + grp.add(0, accounts.get(ac).get(0)); + res.add(grp); + } + return res; + } +} + +class DSU { + int[] parent; + int[] rank; + + public DSU(int size) { + rank = new int[size]; + parent = new int[size]; + for (int i = 0; i < size; i++) + parent[i] = i; + } + + public int find(int x) { + if (parent[x] != x) + parent[x] = find(parent[x]); + return parent[x]; + } + + // Union By Rank + + public boolean union(int x, int y) { + int xr = find(x), yr = find(y); + if (xr == yr) { + return false; + } else if (rank[xr] < rank[yr]) { + parent[xr] = yr; + } else if (rank[xr] > rank[yr]) { + parent[yr] = xr; + } else { + parent[yr] = xr; + rank[xr]++; + } + return true; + } +} diff --git a/java/0724-find-pivot-index.java b/java/0724-find-pivot-index.java new file mode 100644 index 000000000..d4ea35b86 --- /dev/null +++ b/java/0724-find-pivot-index.java @@ -0,0 +1,17 @@ +class Solution { + public int pivotIndex(int[] nums) { + int totalSum = 0; + for (int i = 0; i < nums.length; i++) { + totalSum += nums[i]; + } + int leftSum = 0; + for (int i = 0; i < nums.length; i++) { + int rightSum = totalSum - leftSum - nums[i]; + if (leftSum == rightSum) { + return i; + } + leftSum += nums[i]; + } + return -1; + } +} diff --git a/java/0725-split-linked-list-in-parts.java b/java/0725-split-linked-list-in-parts.java new file mode 100644 index 000000000..527e6c0a3 --- /dev/null +++ b/java/0725-split-linked-list-in-parts.java @@ -0,0 +1,29 @@ +class Solution { + public ListNode[] splitListToParts(ListNode head, int k) { + int numNodes = countNodes(head); + ListNode[] ans = new ListNode[k]; + + int remainder = numNodes % k; + int base_len = numNodes / k; + ListNode curr = head; + + for (int i = 0; i < k; i++) { + ListNode dummy = new ListNode(0), currHead = dummy; + for (int j = 0; j < base_len + (i < remainder ? 1 : 0); j++) { + currHead = currHead.next = new ListNode(curr.val); + if (curr != null) curr = curr.next; + } + ans[i] = dummy.next; + } + return ans; + } + + private int countNodes(ListNode root) { + int cnt = 0; + while (root != null) { + cnt++; + root = root.next; + } + return cnt; + } +} \ No newline at end of file diff --git a/java/0729-my-calendar-i.java b/java/0729-my-calendar-i.java new file mode 100644 index 000000000..0fde1dd7f --- /dev/null +++ b/java/0729-my-calendar-i.java @@ -0,0 +1,47 @@ +// Segment Tree solution + +class MyCalendar { + + public CalendarNode calendar; + + public MyCalendar() { + this.calendar = new CalendarNode(-1, -1); // dummy node + } + + public boolean book(int start, int end) { + return bookHelper(this.calendar, start, end-1); // "end-1" because "end" bound is exclusive (see example 1) + } + + private boolean bookHelper(CalendarNode cur, int targetStart, int targetEnd) { + if (targetStart > cur.end) { + // go to the right + if (cur.right == null) { + // we can insert event + cur.right = new CalendarNode(targetStart, targetEnd); + return true; + } + return bookHelper(cur.right, targetStart, targetEnd); + } else if (targetEnd < cur.start) { + // go to the left + if (cur.left == null) { + // we can insert event + cur.left = new CalendarNode(targetStart, targetEnd); + return true; + } + return bookHelper(cur.left, targetStart, targetEnd); + } + return false; + } +} + +class CalendarNode { + public int start; + public int end; + public CalendarNode left; + public CalendarNode right; + + public CalendarNode(int start, int end) { + this.start = start; + this.end = end; + } +} \ No newline at end of file diff --git a/java/0731-my-calendar-ii.java b/java/0731-my-calendar-ii.java new file mode 100644 index 000000000..4c6db292b --- /dev/null +++ b/java/0731-my-calendar-ii.java @@ -0,0 +1,28 @@ +class MyCalendarTwo { + + List non_overlapping; + List overlapping; + + public MyCalendarTwo() { + non_overlapping = new ArrayList<>(); + overlapping = new ArrayList<>(); + } + + public boolean book(int start, int end) { + for(int[] arr: overlapping){ + int s = arr[0], e = arr[1]; + if(start < e && end > s){ + return false; + } + } + + for(int[] arr: non_overlapping){ + int s = arr[0], e = arr[1]; + if(start < e && end > s){ + overlapping.add(new int[]{Math.max(start, s), Math.min(end, e)}); + } + } + non_overlapping.add(new int[]{start, end}); + return true; + } +} diff --git a/java/0735-asteroid-collision.java b/java/0735-asteroid-collision.java new file mode 100644 index 000000000..003969e24 --- /dev/null +++ b/java/0735-asteroid-collision.java @@ -0,0 +1,34 @@ +class Solution { + public int[] asteroidCollision(int[] asteroids) { + Stack stack = new Stack<>(); + int index = 0; + + while(index < asteroids.length) { + int a = asteroids[index]; + if(stack.isEmpty()) { + stack.push(a); + index++; + } else { + if(stack.peek() > 0 && a < 0) { + if(Math.abs(stack.peek()) > Math.abs(a)) { + index++; + } else if(Math.abs(stack.peek()) < Math.abs(a)) { + stack.pop(); + } else { + index++; + stack.pop(); + } + } else { + stack.push(a); + index++; + } + } + } + + int[] res = new int[stack.size()]; + for(int i=res.length-1; i>=0 ;i--) { + res[i] = stack.pop(); + } + return res; + } +} diff --git a/java/0739-daily-temperatures.java b/java/0739-daily-temperatures.java new file mode 100644 index 000000000..d03cbd7aa --- /dev/null +++ b/java/0739-daily-temperatures.java @@ -0,0 +1,18 @@ +class Solution { + + public int[] dailyTemperatures(int[] temperatures) { + int[] ans = new int[temperatures.length]; + Stack stack = new Stack<>(); + for (int currDay = 0; currDay < temperatures.length; currDay++) { + while ( + !stack.isEmpty() && + temperatures[currDay] > temperatures[stack.peek()] + ) { + int prevDay = stack.pop(); + ans[prevDay] = currDay - prevDay; + } + stack.add(currDay); + } + return ans; + } +} diff --git a/java/0740-delete-and-earn.java b/java/0740-delete-and-earn.java new file mode 100644 index 000000000..b6c1e26a4 --- /dev/null +++ b/java/0740-delete-and-earn.java @@ -0,0 +1,25 @@ +class Solution { + public int deleteAndEarn(int[] nums) { + Map counter = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + counter.put(nums[i], counter.getOrDefault(nums[i], 0) + 1); + } + List numsList = new ArrayList<>(counter.keySet()); + Collections.sort(numsList); + + int earnOne = 0; + int earnTwo = 0; + for (int i = 0; i < numsList.size(); i++) { + int curEarn = numsList.get(i) * counter.get(numsList.get(i)); + if (i > 0 && numsList.get(i) == numsList.get(i - 1) + 1) { + int temp = earnTwo; + earnTwo = Math.max(earnOne + curEarn, earnTwo); + earnOne = temp; + } else { + earnOne = earnTwo; + earnTwo += curEarn; + } + } + return earnTwo; + } +} diff --git a/java/0743-network-delay-time.java b/java/0743-network-delay-time.java new file mode 100644 index 000000000..2d30450a1 --- /dev/null +++ b/java/0743-network-delay-time.java @@ -0,0 +1,48 @@ +// Bellman Ford ALgorithm +// Time Complexty (n * t) | Space Complexity O(n) where t is the length of times +class Solution { + + public int networkDelayTime(int[][] times, int n, int k) { + // initialize an array with max value of size n + int[] paths = new int[n]; + Arrays.fill(paths, Integer.MAX_VALUE); + + paths[k - 1] = 0; + + for (int i = 0; i < n; i++) { + // make a copy of paths + int[] temp = new int[n]; + temp = Arrays.copyOf(paths, paths.length); + + // loop through times + for (int j = 0; j < times.length; j++) { + int src = times[j][0]; // source + int tgt = times[j][1]; // target + int time = times[j][2]; // time + + if ( + temp[src - 1] != Integer.MAX_VALUE && + temp[src - 1] + time < temp[tgt - 1] + ) { + temp[tgt - 1] = temp[src - 1] + time; + } + } + + // set paths to temp + paths = temp; + } + + int result = Integer.MIN_VALUE; + + // calculate max value + for (int i = 0; i < n; i++) { + if (paths[i] == Integer.MAX_VALUE) { + return -1; + } + result = Math.max(result, paths[i]); + } + + // return result + return result; + } +} diff --git a/java/0746-min-cost-climbing-stairs.java b/java/0746-min-cost-climbing-stairs.java new file mode 100644 index 000000000..bce10b0ec --- /dev/null +++ b/java/0746-min-cost-climbing-stairs.java @@ -0,0 +1,14 @@ +class Solution { + public int minCostClimbingStairs(int[] cost) { + int one = 0; + int two = 0; + + for (int i = cost.length - 1; i >= 0; i--) { + cost[i] += Math.min(one, two); + two = one; + one = cost[i]; + } + + return Math.min(cost[0], cost[1]); + } +} diff --git a/java/0752-open-the-lock.java b/java/0752-open-the-lock.java new file mode 100644 index 000000000..32643fd90 --- /dev/null +++ b/java/0752-open-the-lock.java @@ -0,0 +1,47 @@ +class Solution { + public int openLock(String[] deadends, String target) { + Set visited = new HashSet<>(); + for (String deadend : deadends) { + if (deadend.equals("0000")) { + return -1; + } + visited.add(deadend); + } + + Queue queue = new LinkedList<>(); + queue.offer("0000"); + visited.add("0000"); + + int turns = 0; + while (!queue.isEmpty()) { + int size = queue.size(); + for (int i = 0; i < size; i++) { + String lock = queue.poll(); + if (lock.equals(target)) { + return turns; + } + List children = generateChildren(lock); + for (String child : children) { + if (!visited.contains(child)) { + visited.add(child); + queue.offer(child); + } + } + } + turns++; + } + return -1; + } + + private List generateChildren(String lock) { + List children = new ArrayList<>(); + for (int i = 0; i < 4; i++) { + char[] digits = lock.toCharArray(); + digits[i] = (char)(((digits[i] - '0' + 1) % 10) + '0'); + children.add(new String(digits)); + digits[i] = (char)(((digits[i] - '0' - 2 + 10) % 10) + '0'); + children.add(new String(digits)); + } + return children; + } +} diff --git a/java/0763-partition-labels.java b/java/0763-partition-labels.java new file mode 100644 index 000000000..7df1a5eeb --- /dev/null +++ b/java/0763-partition-labels.java @@ -0,0 +1,23 @@ +class Solution { + + public List partitionLabels(String s) { + List list = new ArrayList<>(); + HashMap map = new HashMap<>(); + for (int i = 0; i < s.length(); i++) { + map.put(s.charAt(i), i); + } + int j = 0; + int i = 0; + while (i < s.length()) { + int count = 0; + j = Math.max(j, map.get(s.charAt(i))); + while (i <= j) { + j = Math.max(j, map.get(s.charAt(i))); + i++; + count++; + } + list.add(count); + } + return list; + } +} diff --git a/java/0767-reorganize-string.java b/java/0767-reorganize-string.java new file mode 100644 index 000000000..5bb07b31a --- /dev/null +++ b/java/0767-reorganize-string.java @@ -0,0 +1,43 @@ +//See this comment for explanation https://leetcode.com/problems/reorganize-string/discuss/113440/Java-solution-PriorityQueue/211009 + +class Solution { + + public String reorganizeString(String s) { + HashMap map = new HashMap<>(); + for (int i = 0; i < s.length(); i++) { + map.put(s.charAt(i), map.getOrDefault(s.charAt(i), 0) + 1); + } + PriorityQueue> pq = new PriorityQueue<>( + (a, b) -> + b.getValue() - a.getValue() + ); + pq.addAll(map.entrySet()); + + StringBuilder sb = new StringBuilder(); + + while (!pq.isEmpty()) { + Map.Entry temp1 = pq.poll(); + //if the character at sb's end is different from the max frequency character or the string is empty + if ( + sb.length() == 0 || sb.charAt(sb.length() - 1) != temp1.getKey() + ) { + sb.append(temp1.getKey()); + //update the value + temp1.setValue(temp1.getValue() - 1); + } else { //the character is same + //hold the current character and look for the 2nd most frequent character + Map.Entry temp2 = pq.poll(); + //if there is no temp2 i.e. the temp1 was the only character in the heap then there is no way to avoid adjacent duplicate values + if (temp2 == null) return ""; + //else do the same thing as above + sb.append(temp2.getKey()); + //update the value + temp2.setValue(temp2.getValue() - 1); + //if still has some value left add again to the heap + if (temp2.getValue() != 0) pq.offer(temp2); + } + if (temp1.getValue() != 0) pq.offer(temp1); + } + return sb.toString(); + } +} diff --git a/java/0778-swim-in-rising-water.java b/java/0778-swim-in-rising-water.java new file mode 100644 index 000000000..d201d5f9c --- /dev/null +++ b/java/0778-swim-in-rising-water.java @@ -0,0 +1,46 @@ +// Solution: Greedy Approach with Min Heap +// Time Complexity: O((n^2)*log(n)) +class Solution { + + private int[][] dirs = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } }; + + public int swimInWater(int[][] grid) { + int len = grid.length; + + if (len == 1) { + return 0; + } + + var seen = new boolean[len][len]; + seen[0][0] = true; + + var minHeap = new PriorityQueue((a, b) -> a[0] - b[0]); + minHeap.add(new Integer[] { grid[0][0], 0, 0 }); + + int result = 0; + + while (!minHeap.isEmpty()) { + var curr = minHeap.poll(); + + result = Math.max(result, curr[0]); + + if (curr[1] == len - 1 && curr[2] == len - 1) { + break; + } + + for (int i = 0; i < 4; i++) { + int x = curr[1] + dirs[i][0]; + int y = curr[2] + dirs[i][1]; + + if (x < 0 || x >= len || y < 0 || y >= len || seen[x][y]) { + continue; + } + + minHeap.add(new Integer[] { grid[x][y], x, y }); + seen[x][y] = true; + } + } + + return result; + } +} diff --git a/java/0779-k-th-symbol-in-grammar.java b/java/0779-k-th-symbol-in-grammar.java new file mode 100644 index 000000000..ff89dddf0 --- /dev/null +++ b/java/0779-k-th-symbol-in-grammar.java @@ -0,0 +1,18 @@ +class Solution { + public int kthGrammar(int n, int k) { + int left = 1; + int right = (int)Math.pow(2,n-1); + int cur = 0; + + for(int i = 0; i < n-1; i++){ + int mid = (left + right)/2; + if(k <= mid) + right = mid; + else{ + left = mid + 1; + cur = (cur == 1)? 0: 1; + } + } + return cur; + } +} diff --git a/java/0783-minimum-distance-between-bst-nodes.java b/java/0783-minimum-distance-between-bst-nodes.java new file mode 100644 index 000000000..9321943b4 --- /dev/null +++ b/java/0783-minimum-distance-between-bst-nodes.java @@ -0,0 +1,21 @@ +class Solution { + TreeNode prev = null; + int res = Integer.MAX_VALUE; + + public int minDiffInBST(TreeNode root) { + dfs(root); + return res; + } + + private void dfs(TreeNode node) { + if (node == null) { + return; + } + dfs(node.left); + if (prev != null) { + res = Math.min(res, node.val - prev.val); + } + prev = node; + dfs(node.right); + } +} diff --git a/java/0785-is-graph-bipartite.java b/java/0785-is-graph-bipartite.java new file mode 100644 index 000000000..3df634ca7 --- /dev/null +++ b/java/0785-is-graph-bipartite.java @@ -0,0 +1,29 @@ +class Solution { + public boolean isBipartite(int[][] graph) { + int[] color = new int[graph.length]; // 1 for one color , -1 for second color and 0 for not visited. + + for(int i = 0; i < graph.length; i++){ + if(color[i] != 0){ + continue; + } + + Queue q = new LinkedList<>(); + q.add(i); + color[i] = 1; + + while(!q.isEmpty()){ + int curr = q.poll(); + + for(int n : graph[curr]){ + if(color[n] == 0){ + color[n] = -1 * color[curr]; + q.add(n); + } + if(color[n] == color[curr]) + return false; + } + } + } + return true; + } +} diff --git a/java/0787-cheapest-flights-within-k-stops.java b/java/0787-cheapest-flights-within-k-stops.java new file mode 100644 index 000000000..f735be952 --- /dev/null +++ b/java/0787-cheapest-flights-within-k-stops.java @@ -0,0 +1,48 @@ +// Time Complexity O(k * n) | Space Complexity O(n) +class Solution { + + public int findCheapestPrice( + int n, + int[][] flights, + int src, + int dst, + int k + ) { + // initialize an array with max value of size n + int[] prices = new int[n]; + Arrays.fill(prices, Integer.MAX_VALUE); + + // price from source to source is always 0 + prices[src] = 0; + + for (int i = 0; i <= k; i++) { + // make a copy of prices + int[] temp = new int[n]; + temp = Arrays.copyOf(prices, prices.length); + + // loop through flights + for (int j = 0; j < flights.length; j++) { + int s = flights[j][0]; // from + int d = flights[j][1]; // to + int p = flights[j][2]; // price + + if (prices[s] == Integer.MAX_VALUE) { + continue; + } + + if (prices[s] + p < temp[d]) { + temp[d] = prices[s] + p; + } + } + + // set prices to temp + prices = temp; + } + + if (prices[dst] != Integer.MAX_VALUE) { + return prices[dst]; + } + + return -1; + } +} diff --git a/java/0791-custom-sort-string.java b/java/0791-custom-sort-string.java new file mode 100644 index 000000000..1e4f1be7c --- /dev/null +++ b/java/0791-custom-sort-string.java @@ -0,0 +1,22 @@ +class Solution { + public String customSortString(String order, String s) { + Map map = new HashMap<>(); + for(char c: s.toCharArray()) + map.put(c, map.getOrDefault(c, 0) + 1); + + StringBuilder res = new StringBuilder(); + for(char c: order.toCharArray()){ + while(map.containsKey(c) && map.get(c) > 0){ + res.append(c); + map.put(c, map.get(c)-1); + } + } + for(char c: map.keySet()){ + while(map.get(c) > 0){ + res.append(c); + map.put(c, map.get(c)-1); + } + } + return res.toString(); + } +} diff --git a/java/0797-all-paths-from-source-to-target.java b/java/0797-all-paths-from-source-to-target.java new file mode 100644 index 000000000..bdc55f33d --- /dev/null +++ b/java/0797-all-paths-from-source-to-target.java @@ -0,0 +1,27 @@ +class Solution { + + public List> allPathsSourceTarget(int[][] graph) { + List> ans = new ArrayList<>(); + List list = new ArrayList<>(); + list.add(0); + dfs(ans, graph, list, 0); + return ans; + } + + public void dfs( + List> ans, + int[][] graph, + List list, + int i + ) { + if (i == graph.length - 1) { + ans.add(new ArrayList(list)); + return; + } + for (int val : graph[i]) { + list.add(val); + dfs(ans, graph, list, val); + list.remove(list.size() - 1); + } + } +} diff --git a/java/0799-champagne-tower.java b/java/0799-champagne-tower.java new file mode 100644 index 000000000..4a0d917f3 --- /dev/null +++ b/java/0799-champagne-tower.java @@ -0,0 +1,18 @@ +class Solution { + public double champagneTower(int poured, int query_row, int query_glass) { + double[] prev_row = {poured}; + + for(int row = 1; row < query_row+1; row++){ + double[] cur_row = new double[row+1]; + for(int i = 0; i < row; i++){ + double extra = prev_row[i] - 1; + if(extra > 0){ + cur_row[i] += 0.5 * extra; + cur_row[i+1] += 0.5 * extra; + } + } + prev_row = cur_row; + } + return Math.min(1, prev_row[query_glass]); + } +} diff --git a/java/0802-find-eventual-safe-states.java b/java/0802-find-eventual-safe-states.java new file mode 100644 index 000000000..c8d0dc247 --- /dev/null +++ b/java/0802-find-eventual-safe-states.java @@ -0,0 +1,25 @@ +class Solution { + public List eventualSafeNodes(int[][] graph) { + List res = new ArrayList<>(); + HashMap safe = new HashMap<>(); + for(int i = 0; i < graph.length; i++){ + if(dfs(graph, i, safe)) + res.add(i); + } + return res; + } + + private boolean dfs(int[][] graph, int src, HashMap safe) { + if (safe.containsKey(src)) + return safe.get(src); + + safe.put(src, false); + + for (int neighbour : graph[src]) { + if (!dfs(graph, neighbour, safe)) + return false; + } + safe.put(src, true); + return true; + } +} diff --git a/java/0837-new-21-game.java b/java/0837-new-21-game.java new file mode 100644 index 000000000..00b4fb116 --- /dev/null +++ b/java/0837-new-21-game.java @@ -0,0 +1,24 @@ +class Solution { + public double new21Game(int n, int k, int maxPts) { + if (k == 0) { + return 1.0; + } + + double windowSum = 0.0; + for (int i = k; i < k + maxPts; i++) { + windowSum += (i <= n) ? 1.0 : 0.0; + } + + Map dp = new HashMap<>(); + for (int i = k - 1; i >= 0; i--) { + dp.put(i, windowSum / maxPts); + double remove = 0.0; + if (i + maxPts <= n) { + remove = dp.getOrDefault(i + maxPts, 1.0); + } + windowSum += (dp.get(i) - remove); + } + + return dp.get(0); + } +} diff --git a/java/0838-push-dominoes.java b/java/0838-push-dominoes.java new file mode 100644 index 000000000..f66a16a14 --- /dev/null +++ b/java/0838-push-dominoes.java @@ -0,0 +1,28 @@ +class Solution { + public String pushDominoes(String dominoes) { + int len = dominoes.length(); + Queue q = new LinkedList<>(); + char[] dom = dominoes.toCharArray(); + for (int i = 0; i < len; i++) + if (dominoes.charAt(i) != '.') q.offer(i); + + while (!q.isEmpty()) { + int i = q.poll(); + char ch = dom[i]; + if (dom[i] == 'R') { + if (i + 1 < len && dom[i + 1] == '.') { + if (i + 2 < len && dom[i + 2] == 'L') { + q.poll(); + } else { + dom[i + 1] = 'R'; + q.offer(i + 1); + } + } + } else if (i > 0 && dom[i - 1] == '.') { + dom[i - 1] = 'L'; + q.offer(i - 1); + } + } + return String.valueOf(dom); + } +} \ No newline at end of file diff --git a/java/0846-hand-of-straights.java b/java/0846-hand-of-straights.java new file mode 100644 index 000000000..a6d178bc0 --- /dev/null +++ b/java/0846-hand-of-straights.java @@ -0,0 +1,21 @@ +class Solution { + + public boolean isNStraightHand(int[] hand, int groupSize) { + if (hand.length % groupSize != 0) return false; + + HashMap hm = new HashMap<>(); + for (int card : hand) hm.put(card, hm.getOrDefault(card, 0) + 1); + + Arrays.sort(hand); + for (int card : hand) { + if (hm.get(card) <= 0) continue; + for (int i = 1; i < groupSize; i++) { + int count = hm.getOrDefault(card + i, 0); + if (count > 0) hm.put(card + i, count - 1); else return false; + } + hm.put(card, hm.get(card) - 1); + } + + return true; + } +} diff --git a/java/0852-peak-index-in-a-mountain-array.java b/java/0852-peak-index-in-a-mountain-array.java new file mode 100644 index 000000000..c1db1643e --- /dev/null +++ b/java/0852-peak-index-in-a-mountain-array.java @@ -0,0 +1,20 @@ +class Solution { + + public int peakIndexInMountainArray(int[] arr) { + int left = 0, right = arr.length - 1; + int res = -1; + + while (left <= right) { + int mid = left + (right - left + 1) / 2; + + if (arr[mid - 1] <= arr[mid]) { + left = mid + 1; + res = mid; + } else { + right = mid - 1; + } + } + + return res; + } +} diff --git a/java/0853-car-fleet.java b/java/0853-car-fleet.java new file mode 100644 index 000000000..f6e636eb7 --- /dev/null +++ b/java/0853-car-fleet.java @@ -0,0 +1,24 @@ +class Solution { + + public int carFleet(int target, int[] position, int[] speed) { + if (position.length == 1) return 1; + Stack stack = new Stack<>(); + int[][] combine = new int[position.length][2]; + for (int i = 0; i < position.length; i++) { + combine[i][0] = position[i]; + combine[i][1] = speed[i]; + } + + Arrays.sort(combine, java.util.Comparator.comparingInt(o -> o[0])); + for (int i = combine.length - 1; i >= 0; i--) { + double currentTime = (double) (target - combine[i][0]) / + combine[i][1]; + if (!stack.isEmpty() && currentTime <= stack.peek()) { + continue; + } else { + stack.push(currentTime); + } + } + return stack.size(); + } +} diff --git a/java/0863-all-nodes-distance-k-in-binary-tree.java b/java/0863-all-nodes-distance-k-in-binary-tree.java new file mode 100644 index 000000000..1e3070165 --- /dev/null +++ b/java/0863-all-nodes-distance-k-in-binary-tree.java @@ -0,0 +1,49 @@ +//Just store the value for parent values of the nodes in a map and just do bfs as we do in graph. + +class Solution { + + public List distanceK(TreeNode root, TreeNode target, int k) { + HashMap map = new HashMap<>(); + //First bfs to make a mapping to the parent nodes + Queue q1 = new LinkedList<>(); + q1.offer(root); + while (!q1.isEmpty()) { + int size = q1.size(); + for (int i = 0; i < size; i++) { + TreeNode cur = q1.poll(); + if (cur.left != null) { + q1.offer(cur.left); + map.put(cur.left, cur); + } + if (cur.right != null) { + q1.offer(cur.right); + map.put(cur.right, cur); + } + } + } + //Now do the bfs + //Same as we do in graphs with a visited set + Queue q2 = new LinkedList<>(); + HashSet vis = new HashSet<>(); + List ans = new ArrayList<>(); + int dis = 0; + q2.offer(target); + while (!q2.isEmpty()) { + int size = q2.size(); + for (int i = 0; i < size; i++) { + TreeNode cur = q2.poll(); + if (!vis.contains(cur)) { + if (cur.left != null) q2.offer(cur.left); + if (cur.right != null) q2.offer(cur.right); + if (map.get(cur) != null) q2.offer(map.get(cur)); + if (dis == k) { + ans.add(cur.val); + } + } + vis.add(cur); + } + dis++; + } + return ans; + } +} diff --git a/java/0872-leaf-similar-trees.java b/java/0872-leaf-similar-trees.java new file mode 100644 index 000000000..0224f9b46 --- /dev/null +++ b/java/0872-leaf-similar-trees.java @@ -0,0 +1,17 @@ +class Solution { + public boolean leafSimilar(TreeNode root1, TreeNode root2) { + ArrayList ls1 = new ArrayList<>(); + ArrayList ls2 = new ArrayList<>(); + tree(root1, ls1); + tree(root2, ls2); + return ls1.equals(ls2); + } + private void tree(TreeNode root, List ls){ + if(root == null) + return; + if(root.left == null && root.right == null) + ls.add(root.val); + tree(root.left, ls); + tree(root.right, ls); + } +} diff --git a/java/0875-koko-eating-bananas.java b/java/0875-koko-eating-bananas.java new file mode 100644 index 000000000..2d54eaf8c --- /dev/null +++ b/java/0875-koko-eating-bananas.java @@ -0,0 +1,34 @@ +class Solution { + + public int minEatingSpeed(int[] piles, int h) { + // Initalize the left and right boundaries + int left = 1, right = 1; + for (int pile : piles) { + right = Math.max(right, pile); + } + + while (left < right) { + // Get the middle index between left and right boundary indexes. + // hourSpent stands for the total hour Koko spends. + int middle = (left + right) / 2; + int hourSpent = 0; + + // Iterate over the piles and calculate hourSpent. + // We increase the hourSpent by ceil(pile / middle) + for (int pile : piles) { + hourSpent += Math.ceil((double) pile / middle); + } + + // Check if middle is a workable speed, and cut the search space by half. + if (hourSpent <= h) { + right = middle; + } else { + left = middle + 1; + } + } + + // Once the left and right boundaries coincide, we find the target value, + // that is, the minimum workable eating speed. + return right; + } +} diff --git a/java/0876-middle-of-the-linked-list.java b/java/0876-middle-of-the-linked-list.java new file mode 100644 index 000000000..ec3da64a9 --- /dev/null +++ b/java/0876-middle-of-the-linked-list.java @@ -0,0 +1,13 @@ +//fast and slow pointer +class Solution { + + public ListNode middleNode(ListNode head) { + ListNode slow = head; + ListNode fast = head; + while (fast != null && fast.next != null) { + fast = fast.next.next; + slow = slow.next; + } + return slow; + } +} diff --git a/java/0880-decoded-string-at-index.java b/java/0880-decoded-string-at-index.java new file mode 100644 index 000000000..da827cbc6 --- /dev/null +++ b/java/0880-decoded-string-at-index.java @@ -0,0 +1,34 @@ +/* +----------------------- + Time Complexity : O(n) + Space Complexity = O(1) +----------------------- +*/ + +class Solution { + public String decodeAtIndex(String s, int k) { + long wordLength = 0; + for (char c : s.toCharArray()) { + if (Character.isLetter(c)) { + wordLength += 1; + } else { + wordLength *= Character.getNumericValue(c); + } + } + + for (int i = s.length() - 1; i >= 0; i--) { + char c = s.charAt(i); + k %= wordLength; + if (Character.isLetter(c)) { + if (k == 0) { + return String.valueOf(c); + } else { + wordLength -= 1; + } + } else { + wordLength /= Character.getNumericValue(c); + } + } + return ""; + } +} diff --git a/java/0881-boats-to-save-people.java b/java/0881-boats-to-save-people.java new file mode 100644 index 000000000..e080cb396 --- /dev/null +++ b/java/0881-boats-to-save-people.java @@ -0,0 +1,15 @@ +class Solution { + public int numRescueBoats(int[] people, int limit) { + Arrays.sort(people); + int boatsNeeded = 0; + int lightIdx = 0; + int heavyIdx = people.length-1; + while (lightIdx <= heavyIdx) { + if (people[lightIdx] + people[heavyIdx] <= limit) + lightIdx++; + heavyIdx--; + boatsNeeded++; + } + return boatsNeeded; + } +} \ No newline at end of file diff --git a/java/0894-all-possible-full-binary-trees.java b/java/0894-all-possible-full-binary-trees.java new file mode 100644 index 000000000..ef6f1b29d --- /dev/null +++ b/java/0894-all-possible-full-binary-trees.java @@ -0,0 +1,26 @@ +class Solution { + HashMap> memo = new HashMap<>(); + public List allPossibleFBT(int n) { + if(n == 0) + return new ArrayList(); + if(n == 1) + return new ArrayList(Arrays.asList(new TreeNode(0))); + if(memo.containsKey(n)) + return memo.get(n); + + List res = new ArrayList<>(); + for(int l = 0; l < n; l++){ + int r = n-1-l; + List leftTree = allPossibleFBT(l); + List rightTree = allPossibleFBT(r); + + for(TreeNode t1 : leftTree){ + for(TreeNode t2 : rightTree){ + res.add(new TreeNode(0, t1, t2)); + } + } + } + memo.put(n, res); + return res; + } +} diff --git a/java/0895-maximum-frequency-stack.java b/java/0895-maximum-frequency-stack.java new file mode 100644 index 000000000..daf5b4803 --- /dev/null +++ b/java/0895-maximum-frequency-stack.java @@ -0,0 +1,20 @@ +class FreqStack { + HashMap freq = new HashMap<>(); + HashMap> m = new HashMap<>(); + int maxfreq = 0; + + public void push(int x) { + int f = freq.getOrDefault(x, 0) + 1; + freq.put(x, f); + maxfreq = Math.max(maxfreq, f); + if (!m.containsKey(f)) m.put(f, new Stack()); + m.get(f).add(x); + } + + public int pop() { + int x = m.get(maxfreq).pop(); + freq.put(x, maxfreq - 1); + if (m.get(maxfreq).size() == 0) maxfreq--; + return x; + } +} \ No newline at end of file diff --git a/java/0896-monotonic-array.java b/java/0896-monotonic-array.java new file mode 100644 index 000000000..a5afcb08c --- /dev/null +++ b/java/0896-monotonic-array.java @@ -0,0 +1,14 @@ +class Solution { + public boolean isMonotonic(int[] nums) { + boolean inc = true, dec = true; + + for (int i = 0; i < nums.length - 1; i++) { + if (nums[i] > nums[i + 1]) + inc = false; + if (nums[i] < nums[i + 1]) + dec = false; + } + + return inc || dec; + } +} \ No newline at end of file diff --git a/java/0901-online-stock-span.java b/java/0901-online-stock-span.java new file mode 100644 index 000000000..c4557fd8a --- /dev/null +++ b/java/0901-online-stock-span.java @@ -0,0 +1,15 @@ +class StockSpanner { + Stack s = new Stack<>(); + + public StockSpanner() { + + } + + public int next(int price) { + int span=1; + while(!s.isEmpty() && s.peek()[0]<=price) + span+=s.pop()[1]; + s.push(new int[]{price,span}); + return span; + } +} \ No newline at end of file diff --git a/java/0904-fruit-into-baskets.java b/java/0904-fruit-into-baskets.java new file mode 100644 index 000000000..4d8e84e76 --- /dev/null +++ b/java/0904-fruit-into-baskets.java @@ -0,0 +1,25 @@ +class Solution { + public int totalFruit(int[] fruits) { + Map fruitTypeToItsCount = new HashMap<>(); + int maxTotalFruits = 0; + int l = 0; + int r = 0; + + while (r < fruits.length) { + fruitTypeToItsCount.put(fruits[r], fruitTypeToItsCount.getOrDefault(fruits[r], 0) + 1); + + while (fruitTypeToItsCount.size() > 2) { + fruitTypeToItsCount.put(fruits[l], fruitTypeToItsCount.get(fruits[l]) - 1); + if (fruitTypeToItsCount.get(fruits[l]) == 0) { + fruitTypeToItsCount.remove(fruits[l]); + } + l++; + } + + maxTotalFruits = Math.max(maxTotalFruits, r - l + 1); + r++; + } + + return maxTotalFruits; + } +} diff --git a/java/0905-sort-array-by-parity.java b/java/0905-sort-array-by-parity.java new file mode 100644 index 000000000..bd7984407 --- /dev/null +++ b/java/0905-sort-array-by-parity.java @@ -0,0 +1,19 @@ +class Solution { + public int[] sortArrayByParity(int[] nums) { + int[] arr = new int[nums.length]; + int i = 0; + int j = nums.length-1; + for(int n : nums){ + if(n%2 == 0){ + arr[i] = n; + i++; + } + else{ + arr[j] = n; + j--; + } + + } + return arr; + } +} diff --git a/java/0907-sum-of-subarray-minimums.java b/java/0907-sum-of-subarray-minimums.java new file mode 100644 index 000000000..298d2da10 --- /dev/null +++ b/java/0907-sum-of-subarray-minimums.java @@ -0,0 +1,33 @@ +/*------------------------------- + Time Complexity: O(n) + Space Complexity: O(n) +-------------------------------*/ +class Solution { + public int sumSubarrayMins(int[] arr) { + int MOD = 1000000007; + int res = 0; + int[] extendedArr = new int[arr.length + 2]; + + for (int i = 0; i < arr.length; i++) { + extendedArr[i + 1] = arr[i]; + } + extendedArr[0] = Integer.MIN_VALUE; + extendedArr[extendedArr.length - 1] = Integer.MIN_VALUE; + + Stack stack = new Stack<>(); // (index, num) + + for (int i = 0; i < extendedArr.length; i++) { + while (!stack.isEmpty() && extendedArr[i] < stack.peek()[1]) { + int[] popped = stack.pop(); + int j = popped[0]; + int m = popped[1]; + int left = j - (stack.isEmpty() ? -1 : stack.peek()[0]); + int right = i - j; + res = (int) ((res + (long) m * left * right) % MOD); + } + stack.push(new int[] { i, extendedArr[i] }); + } + + return res; + } +} diff --git a/java/0909-snakes-and-ladders.java b/java/0909-snakes-and-ladders.java new file mode 100644 index 000000000..988502a4a --- /dev/null +++ b/java/0909-snakes-and-ladders.java @@ -0,0 +1,52 @@ +class Solution { + public int snakesAndLadders(int[][] board) { + int n = board.length; + + reverseBoard(board); + + boolean[] visited = new boolean[n*n+1]; + Queue q = new LinkedList<>(); + q.offer(new int[]{1, 0}); + visited[1] = true; + + while(!q.isEmpty()) { + int[] curr = q.poll(); + for(int j=1; j<=6; j++) { + int next = curr[0] + j; + int[] coor = squareToCoor(next, n); + if(board[coor[0]][coor[1]] != -1) { + next = board[coor[0]][coor[1]]; + } + if(next == n*n) { + return curr[1] + 1; + } + if(!visited[next]) { + visited[next] = true; + q.offer(new int[]{next, curr[1] + 1}); + } + } + } + + return -1; + } + + public int[] squareToCoor(int square, int n) { + int row = (square - 1) / n; + int col = (square - 1) % n; + if(row % 2 != 0) { + col = n - 1 - col; + } + return new int[]{row, col}; + } + + public void reverseBoard(int[][] board) { + int l = 0, r = board.length-1; + while(l < r) { + int[] temp = board[l]; + board[l] = board[r]; + board[r] = temp; + l++; + r--; + } + } +} \ No newline at end of file diff --git a/java/0912-sort-an-array.java b/java/0912-sort-an-array.java new file mode 100644 index 000000000..4dbe04b12 --- /dev/null +++ b/java/0912-sort-an-array.java @@ -0,0 +1,41 @@ +class Solution { + //Using Merge Sort + public int[] sortArray(int[] nums) { + mergeSort(nums, 0, nums.length-1); + return nums; + } + public void mergeSort(int []arr, int low, int high){ + if(low >= high) return; + + int mid = (low+high)/2; + mergeSort(arr, low, mid); + mergeSort(arr, mid+1, high); + merge(arr, low, high, mid); + } + public void merge(int []arr, int low, int high, int mid){ + ArrayList temp = new ArrayList<>(); + int left = low; + int right = mid+1; + + while(left <= mid && right <= high){ + if(arr[left] <= arr[right]){ + temp.add(arr[left]); + left++; + }else{ + temp.add(arr[right]); + right++; + } + } + while(left <= mid){ + temp.add(arr[left]); + left++; + } + while(right <= high){ + temp.add(arr[right]); + right++; + } + for(int i=low; i<= high; i++){ + arr[i] = temp.get(i-low); + } + } +} diff --git a/java/0918-maximum-sum-circular-subarray.java b/java/0918-maximum-sum-circular-subarray.java new file mode 100644 index 000000000..a733d6b1f --- /dev/null +++ b/java/0918-maximum-sum-circular-subarray.java @@ -0,0 +1,15 @@ +class Solution { + public int maxSubarraySumCircular(int[] nums) { + int curMax = 0, curMin = 0; + int globMax = nums[0], globMin = nums[0]; + int total = 0; + for (int n : nums) { + curMax = Math.max(curMax + n, n); + curMin = Math.min(curMin + n, n); + total += n; + globMax = Math.max(curMax, globMax); + globMin = Math.min(curMin, globMin); + } + return globMax > 0 ? Math.max(globMax, total - globMin) : globMax; + } +} diff --git a/java/0920-number-of-music-playlists.java b/java/0920-number-of-music-playlists.java new file mode 100644 index 000000000..8c5a78942 --- /dev/null +++ b/java/0920-number-of-music-playlists.java @@ -0,0 +1,58 @@ +class Solution { + public int numMusicPlaylists(int n, int goal, int k) { + map = new HashMap<>(); + return (int)findPlaylists(0, goal, n, k); + } + + HashMap map; + int mod = 1000000007; + + public long findPlaylists(int old_songs, int goal, int n, int k) { + if(goal == 0 && old_songs == n) { + return 1; + } + + if(goal == 0 || old_songs > n) { + return 0; + } + + if(map.containsKey(new Pair(old_songs, goal))) { + return map.get(new Pair(old_songs, goal)); + } + + // chossing an new_song + long res = ((n - old_songs) * (findPlaylists(old_songs + 1, goal - 1, n, k) % mod)) % mod; + + // choose an old_songs + if(old_songs > k) { + res = (res + ((old_songs - k) * (findPlaylists(old_songs, goal - 1, n, k)) % mod) % mod) % mod; + } + + map.put(new Pair(old_songs, goal), (int)res); + + return res; + } + + class Pair { + int a; + int b; + + public Pair(int a, int b) { + this.a = a; + this.b = b; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + Pair pair = (Pair) obj; + return a == pair.a && b == pair.b; + } + + @Override + public int hashCode() { + return 31 * a + b; + } + } +} diff --git a/java/0929-unique-email-addresses.java b/java/0929-unique-email-addresses.java new file mode 100644 index 000000000..08dda03a4 --- /dev/null +++ b/java/0929-unique-email-addresses.java @@ -0,0 +1,25 @@ +class Solution { + private String getFormattedEmail(String email) { + String[] arr = email.split("@"); + String localName = arr[0]; + String domainName = arr[1]; + + // Only keep the first part of "+" sign + String[] arrLocalWithPlus = localName.split("\\+"); + localName = arrLocalWithPlus[0]; + + // Replace "."/dots + localName = localName.replace(".", ""); + + return localName + "@" + domainName; + } + + public int numUniqueEmails(String[] emails) { + Set uniqueEmails = new HashSet<>(); + for(String email: emails) { + String formattedEmail = getFormattedEmail(email); + uniqueEmails.add(formattedEmail); + } + return uniqueEmails.size(); + } +} \ No newline at end of file diff --git a/java/0930-binary-subarrays-with-sum.java b/java/0930-binary-subarrays-with-sum.java new file mode 100644 index 000000000..b795098dd --- /dev/null +++ b/java/0930-binary-subarrays-with-sum.java @@ -0,0 +1,22 @@ +class Solution { + public int numSubarraysWithSum(int[] nums, int goal) { + return helper(nums, goal) - helper(nums, goal - 1); + } + private int helper(int[] nums, int goal){ + if(goal < 0) + return 0; + + int res = 0, sum = 0; + int l = 0; + + for(int r = 0; r < nums.length; r++){ + sum += nums[r]; + while(sum > goal){ + sum -= nums[l]; + l += 1; + } + res += (r - l + 1); + } + return res; + } +} diff --git a/java/0931-minimum-falling-path-sum.java b/java/0931-minimum-falling-path-sum.java new file mode 100644 index 000000000..9d396b243 --- /dev/null +++ b/java/0931-minimum-falling-path-sum.java @@ -0,0 +1,59 @@ +/*---------------------------------- + Time Complexity: O(n^2) + Space Complexity: (1) +----------------------------------*/ + +class Solution { + public int minFallingPathSum(int[][] matrix) { + int N = matrix.length; + + for(int r = 1; r < N; r++){ + for(int c = 0; c < N; c++){ + int mid = matrix[r-1][c]; + int left = (c > 0)? matrix[r-1][c-1]: Integer.MAX_VALUE; + int right = (c < N-1)? matrix[r-1][c+1]: Integer.MAX_VALUE; + matrix[r][c] = matrix[r][c] + Math.min(left, Math.min(mid, right)); + } + } + int res = Integer.MAX_VALUE; + for(int i = 0; i < N; i++){ + res = Math.min(res, matrix[N-1][i]); + } + return res; + } +} + +/*---------------------------------- + Time Complexity: O(n^2) + Space Complexity: (n^2) +----------------------------------*/ + +class Solution { + public int minFallingPathSum(int[][] matrix) { + int n = matrix.length; + int[][] dp = new int[n+1][n+1]; + + for(int i = n-1; i >= 0; i--){ + for(int j = 0; j < n; j++){ + if(j == 0) + dp[i][j] = matrix[i][j] + Math.min(dp[i+1][j], dp[i+1][j+1]); + else if (j == n-1) { + dp[i][j] = matrix[i][j] + Math.min(dp[i+1][j-1], dp[i+1][j]); + } else{ + int min = dp[i+1][j-1]; + if(dp[i+1][j] < min) + min = dp[i+1][j]; + if(dp[i+1][j+1] < min) + min = dp[i+1][j+1]; + + dp[i][j] = matrix[i][j] + min; + } + } + } + int res = Integer.MAX_VALUE; + for(int i=0; i q = new LinkedList<>(); + boolean[][] vis = new boolean[grid.length][grid[0].length]; + + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (grid[i][j] == 1) { + mark(grid, vis, q, i, j); + i = grid.length; + j = grid[0].length; + } + } + } + int count = 0; + int[][] dir = {{0,1}, {1,0}, {-1,0}, {0,-1}}; + + while (!q.isEmpty()) { + int n = q.size(); + + for (int k = 0; k < n; k++) { + int[] curr = q.poll(); + + for (int j = 0; j < 4; j++) { + int r = curr[0] + dir[j][0]; + int c = curr[1] + dir[j][1]; + + if (r >= 0 && c >= 0 && r < grid.length && c < grid[0].length && !vis[r][c]) { + if (grid[r][c] == 1) return count; + vis[r][c] = true; + q.offer(new int[]{r,c}); + } + } + } + count++; + } + return -1; + } + + void mark(int[][] grid, boolean[][] vis , Queue q, int i, int j) { + if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] == 0 || vis[i][j]) return; + vis[i][j] = true; + q.add(new int[]{i,j}); + mark(grid, vis, q, i + 1, j); + mark(grid, vis, q, i - 1, j); + mark(grid, vis, q, i, j + 1); + mark(grid, vis, q, i, j - 1); + + } +} diff --git a/java/0935-knight-dialer.java b/java/0935-knight-dialer.java new file mode 100644 index 000000000..1fda4a5f1 --- /dev/null +++ b/java/0935-knight-dialer.java @@ -0,0 +1,38 @@ +/*------------------------------ + Time Complexity: O(n) + Space Complexity: O(1) +-------------------------------*/ +class Solution { + public int knightDialer(int n) { + if(n == 1) + return 10; + + int MOD = (int)1e9 + 7; + int[][] jumps = { + {4, 6}, + {6, 8}, + {7, 9}, + {4, 8}, + {3, 9, 0}, + {}, + {1, 7, 0}, + {2, 6}, + {1, 3}, + {2, 4} + }; + int[] dp = new int[10]; + Arrays.fill(dp, 1); + for(int i = 0; i < n-1; i++){ + int[] next_dp = new int[10]; + for(int d = 0; d < 10; d++){ + for(int j: jumps[d]) + next_dp[j] = (next_dp[j] + dp[d]) % MOD; + } + dp = next_dp; + } + int res = 0; + for(int no: dp) + res = (res + no) % MOD; + return res; + } +} diff --git a/java/0938-range-sum-of-bst.java b/java/0938-range-sum-of-bst.java new file mode 100644 index 000000000..b18b54015 --- /dev/null +++ b/java/0938-range-sum-of-bst.java @@ -0,0 +1,15 @@ +/*-------------------------- + Time Complexity: O(n) + Space Complexity: O(n) +---------------------------*/ +class Solution { + public int rangeSumBST(TreeNode root, int low, int high) { + if(root == null) + return 0; + if(root.val > high) + return rangeSumBST(root.left, low, high); + if(root.val < low) + return rangeSumBST(root.right, low, high); + return root.val + rangeSumBST(root.left, low, high) + rangeSumBST(root.right, low, high); + } +} diff --git a/java/0946-validate-stack-sequences.java b/java/0946-validate-stack-sequences.java new file mode 100644 index 000000000..f13d00696 --- /dev/null +++ b/java/0946-validate-stack-sequences.java @@ -0,0 +1,17 @@ +class Solution { + public boolean validateStackSequences(int[] pushed, int[] popped) { + Stack stack = new Stack<>(); + + int i = 0; + for (int value : pushed) { + stack.push(value); + + while (i < popped.length && !stack.isEmpty() && stack.peek() == popped[i]) { + stack.pop(); + i++; + } + } + + return stack.isEmpty(); + } +} diff --git a/java/0948-bag-of-tokens.java b/java/0948-bag-of-tokens.java new file mode 100644 index 000000000..ae74dcf2a --- /dev/null +++ b/java/0948-bag-of-tokens.java @@ -0,0 +1,48 @@ +class Solution { + public int bagOfTokensScore(int[] tokens, int power) { + Arrays.sort(tokens); + int res = 0; + int score = 0; + + int l = 0; + int r = tokens.length - 1; + while (l <= r) { + if (power >= tokens[l]) { + power -= tokens[l++]; + score++; + res = Math.max(res, score); + } else if (score > 0) { + power += tokens[r--]; + score--; + } else { + break; + } + } + + return res; + } +} + +/*------------------------------------------------------*/ + +class Solution { + public int bagOfTokensScore(int[] tokens, int power) { + Arrays.sort(tokens); + int score = 0; + int i = 0, j = tokens.length - 1; + while (i < j) { + if (power >= tokens[i]) { + power -= tokens[i++]; + score += 1; + } + else if (score > 0 && i != j) { + score -= 1; + power += tokens[j--]; + } + else + break; + } + return score; + } +} + diff --git a/java/0950-reveal-cards-in-increasing-order.java b/java/0950-reveal-cards-in-increasing-order.java new file mode 100644 index 000000000..9269fc8e5 --- /dev/null +++ b/java/0950-reveal-cards-in-increasing-order.java @@ -0,0 +1,18 @@ +class Solution { + public int[] deckRevealedIncreasing(int[] deck) { + Arrays.sort(deck); + int[] res = new int[deck.length]; + ArrayDeque q = new ArrayDeque<>(); + for (int i = 0; i < deck.length; i++) { + q.addLast(i); + } + for (int n : deck) { + int i = q.removeFirst(); + res[i] = n; + if (!q.isEmpty()) { + q.addLast(q.removeFirst()); + } + } + return res; + } +} diff --git a/java/0951-flip-equivalent-binary-trees.java b/java/0951-flip-equivalent-binary-trees.java new file mode 100644 index 000000000..a32bbc095 --- /dev/null +++ b/java/0951-flip-equivalent-binary-trees.java @@ -0,0 +1,16 @@ +class Solution { + public boolean flipEquiv(TreeNode root1, TreeNode root2) { + if(root1 == null && root2 == null) return true; + if(root1 == null || root2 == null) return false; + if(root1.val != root2.val) return false; + + if(flipEquiv(root1.left, root2.left) && flipEquiv(root1.right, root2.right)){ //if they are euqal + return true; + } + if(flipEquiv(root1.right, root2.left) && flipEquiv(root1.left, root2.right)){ // if flipped once + return true; + }else{ + return false; + } + } +} diff --git a/java/0953-verifying-an-alien-dictionary.java b/java/0953-verifying-an-alien-dictionary.java new file mode 100644 index 000000000..38f852af8 --- /dev/null +++ b/java/0953-verifying-an-alien-dictionary.java @@ -0,0 +1,24 @@ +class Solution { + public boolean isAlienSorted(String[] words, String order) { + Map orderInd = new HashMap<>(); { + int ind = 0; + for(char c: order.toCharArray()) + orderInd.put(c, ind++); + } + + for(int i = 0; i < words.length - 1; i++) { + String w1 = words[i], w2 = words[i + 1]; + + for(int j = 0; j < w1.length(); j++) + if(j == w2.length()) + return false; + else if(w1.charAt(j) != w2.charAt(j)) + if(orderInd.get(w2.charAt(j)) < orderInd.get(w1.charAt(j))) + return false; + else + break; + } + + return true; + } +} diff --git a/java/0973-k-closest-points-to-origin.java b/java/0973-k-closest-points-to-origin.java new file mode 100644 index 000000000..566ea8d60 --- /dev/null +++ b/java/0973-k-closest-points-to-origin.java @@ -0,0 +1,57 @@ +//First solution Time Complexity is O(NlogN) +//Just take a min heap and add the values using the formula and return the top k values +//We can completely ignore the square root as we are just comparing the values (if a*a>b*b => a>b) + +class Solution { + + public int[][] kClosest(int[][] points, int k) { + PriorityQueue q = new PriorityQueue<>((a, b) -> + Integer.compare( + (a[0] * a[0] + a[1] * a[1]), + (b[0] * b[0] + b[1] * b[1]) + ) + ); + for (int[] point : points) { + q.add(point); + } + int[][] ans = new int[k][2]; + for (int i = 0; i < k; i++) { + int[] cur = q.poll(); + ans[i][0] = cur[0]; + ans[i][1] = cur[1]; + } + return ans; + } +} + +//This approach is a sightly optimized approach here we can use a max heap and maintain its size as k. +//So when we do the removal the time complexity will reduce from logn to logk +//Max heap because we will remove the top elements (the one which are greater) +//Overall Time complexity O(NlogK) + +class Solution { + + public int[][] kClosest(int[][] points, int k) { + PriorityQueue q = new PriorityQueue<>((a, b) -> + Integer.compare( + (b[0] * b[0] + b[1] * b[1]), + (a[0] * a[0] + a[1] * a[1]) + ) + ); //only this is changed (swapped) + for (int[] point : points) { + q.add(point); + //remove when size increase k + if (q.size() > k) { + q.remove(); + } + } + int[][] ans = new int[k][2]; + for (int i = 0; i < k; i++) { + int[] cur = q.poll(); + ans[i][0] = cur[0]; + ans[i][1] = cur[1]; + } + return ans; + } +} +//There are also some O(N) solutions using quick select and binary search https://leetcode.com/problems/k-closest-points-to-origin/solution/ diff --git a/java/0974-subarray-sums-divisible-by-k.java b/java/0974-subarray-sums-divisible-by-k.java new file mode 100644 index 000000000..067b370fb --- /dev/null +++ b/java/0974-subarray-sums-divisible-by-k.java @@ -0,0 +1,17 @@ +class Solution { + public int subarraysDivByK(int[] nums, int k) { + int prefix_sum = 0; + int res = 0; + Map map = new HashMap<>(); // remainder -> count + map.put(0, 1); + + for(int n: nums){ + prefix_sum += n; + int remainder = ((prefix_sum % k) + k) % k; // twice modulo to avoid negative remainders + + res += map.getOrDefault(remainder, 0); + map.put(remainder, map.getOrDefault(remainder, 0) + 1); + } + return res; + } +} diff --git a/java/0977-squares-of-a-sorted-array.java b/java/0977-squares-of-a-sorted-array.java new file mode 100644 index 000000000..016318b3e --- /dev/null +++ b/java/0977-squares-of-a-sorted-array.java @@ -0,0 +1,24 @@ +class Solution { + + public int[] sortedSquares(int[] nums) { + + int[] result = new int[nums.length]; + + int right = nums.length - 1; + int left = 0; + int resultIndex = result.length - 1; + while (left <= right) { + + if (nums[left] * nums[left] >= nums[right] * nums[right]) { + result[resultIndex] = nums[left] * nums[left]; + left++; + } else { + result[resultIndex] = nums[right] * nums[right]; + right--; + } + resultIndex--; + } + + return result; + } +} \ No newline at end of file diff --git a/java/0981-time-based-key-value-store.java b/java/0981-time-based-key-value-store.java new file mode 100644 index 000000000..e8c059052 --- /dev/null +++ b/java/0981-time-based-key-value-store.java @@ -0,0 +1,32 @@ +class TimeMap { + + HashMap>> map; + + public TimeMap() { + map = new HashMap<>(); + } + + public void set(String key, String value, int timestamp) { + if (!map.containsKey(key)) map.put(key, new ArrayList<>()); + map.get(key).add(new Pair(value, timestamp)); + } + + public String get(String key, int timestamp) { + if (!map.containsKey(key)) return ""; + List> list = map.get(key); + return search(list, timestamp); + } + + public String search(List> list, int timestamp) { + int start = 0; + int end = list.size() - 1; + while (start < end) { + int mid = start + (end - start + 1) / 2; + if (list.get(mid).getValue() <= timestamp) start = mid; else end = + mid - 1; + } + return list.get(start).getValue() <= timestamp + ? list.get(start).getKey() + : ""; + } +} diff --git a/java/0986-interval-list-intersections.java b/java/0986-interval-list-intersections.java new file mode 100644 index 000000000..010ab563a --- /dev/null +++ b/java/0986-interval-list-intersections.java @@ -0,0 +1,24 @@ +//Taken from solution section +//Just two pointer approach + +class Solution { + + public int[][] intervalIntersection(int[][] A, int[][] B) { + List ans = new ArrayList(); + int i = 0, j = 0; + + while (i < A.length && j < B.length) { + // Let's check if A[i] intersects B[j]. + // lo - the startpoint of the intersection + // hi - the endpoint of the intersection + int lo = Math.max(A[i][0], B[j][0]); + int hi = Math.min(A[i][1], B[j][1]); + if (lo <= hi) ans.add(new int[] { lo, hi }); + + // Remove the interval with the smallest endpoint + if (A[i][1] < B[j][1]) i++; else j++; + } + + return ans.toArray(new int[ans.size()][]); + } +} diff --git a/java/0994-rotting-oranges.java b/java/0994-rotting-oranges.java new file mode 100644 index 000000000..940316996 --- /dev/null +++ b/java/0994-rotting-oranges.java @@ -0,0 +1,36 @@ +class Solution { + + public int orangesRotting(int[][] grid) { + int m = grid.length, n = grid[0].length; + Queue queue = new LinkedList<>(); + int fresh = 0; + + for (int i = 0; i < m; i += 1) { + for (int j = 0; j < n; j += 1) { + if (grid[i][j] == 2) queue.offer(new int[] { i, j }); else if ( + grid[i][j] == 1 + ) fresh += 1; + } + } + + int count = 0; + int[][] dirs = { { 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 } }; + while (!queue.isEmpty() && fresh != 0) { + count += 1; + int sz = queue.size(); + for (int i = 0; i < sz; i += 1) { + int[] rotten = queue.poll(); + int r = rotten[0], c = rotten[1]; + for (int[] dir : dirs) { + int x = r + dir[0], y = c + dir[1]; + if (0 <= x && x < m && 0 <= y && y < n && grid[x][y] == 1) { + grid[x][y] = 2; + queue.offer(new int[] { x, y }); + fresh -= 1; + } + } + } + } + return fresh == 0 ? count : -1; + } +} diff --git a/java/1011-capacity-to-ship-packages-within-d-days.java b/java/1011-capacity-to-ship-packages-within-d-days.java new file mode 100644 index 000000000..0ad424bc0 --- /dev/null +++ b/java/1011-capacity-to-ship-packages-within-d-days.java @@ -0,0 +1,36 @@ +class Solution { + public boolean canShip(int cap, int days, int[] weights){ + int ships=1, currCap=cap; + for(int w: weights){ + if(currCap - w < 0){ + ships += 1; + currCap = cap; + } + currCap -= w; + } + return ships <= days; + } + public int shipWithinDays(int[] weights, int days) { + int l=0, r=0; + + for(int w: weights){ + l = Math.max(l, w); + r += w; + } + //We can improve lower bound by taking max of the average capacity and max weight + l = Math.max(l,r/days); + + int res = r; + + while(l <= r){ + int cap = (l+r)/2; + if(canShip(cap, days, weights)){ + res = Math.min(res, cap); + r = cap-1; + }else{ + l = cap+1; + } + } + return res; + } +} diff --git a/java/1020-number-of-enclaves.java b/java/1020-number-of-enclaves.java new file mode 100644 index 000000000..a763e0a13 --- /dev/null +++ b/java/1020-number-of-enclaves.java @@ -0,0 +1,46 @@ +/* +The basic idea is to iterate through the boundary +If we encounter any island i.e. 1 then we will run DFS +And update all those islands as 2 + +Then check for the number of 1s remaining in the board +Since those are the ones that have not been visited and return +*/ + +class Solution { + public int count(int [][]board){ + int c = 0; + for(int i=0; iboard.length-1 || c>board[0].length-1 || board[r][c] != 1) return; + + board[r][c] = 2; + + dfs(r+1, c, board); + dfs(r-1, c, board); + dfs(r, c+1, board); + dfs(r, c-1, board); + } + public int numEnclaves(int[][] board) { + int n=board.length, m=board[0].length; + + for(int i=0; i (a[1] - a[0]) - (b[1] - b[0]) + ); + + int n = costs.length / 2; + int total = 0; + for (int i = 0; i < n; i++) { + total += costs[i][1] + costs[i + n][0]; + } + + return total; + } +} \ No newline at end of file diff --git a/java/1041-robot-bounded-in-circle.java b/java/1041-robot-bounded-in-circle.java new file mode 100644 index 000000000..892635e40 --- /dev/null +++ b/java/1041-robot-bounded-in-circle.java @@ -0,0 +1,23 @@ +class Solution { + public boolean isRobotBounded(String instructions) { + int x=0, y=0; + int dirX = 0, dirY=1; + + for(char ch : instructions.toCharArray()){ + if(ch == 'G'){ + x += dirX; + y += dirY; + } else if(ch == 'L'){ + int temp = dirX; + dirX = -1*dirY; + dirY = temp; + } else{ + int temp = dirX; + dirX = dirY; + dirY = -1*temp; + } + } + + return (x==0 && y==0) || (dirX!=0 || dirY!=1); + } +} \ No newline at end of file diff --git a/java/1043-partition-array-for-maximum-sum.java b/java/1043-partition-array-for-maximum-sum.java new file mode 100644 index 000000000..2dbf23402 --- /dev/null +++ b/java/1043-partition-array-for-maximum-sum.java @@ -0,0 +1,24 @@ +class Solution { + public int maxSumAfterPartitioning(int[] arr, int k) { + Integer[] cache = new Integer[arr.length]; + return dfs(arr, k, 0, cache); + } + + private int dfs(int[] arr, int k, int i, Integer[] cache) { + if (i == arr.length) { + return 0; + } + if (cache[i] != null) { + return cache[i]; + } + int curMax = 0; + int res = 0; + for (int j = i; j < Math.min(arr.length, i + k); j++) { + curMax = Math.max(curMax, arr[j]); + int windowSize = j - i + 1; + res = Math.max(res, dfs(arr, k, j + 1, cache) + curMax * windowSize); + } + cache[i] = res; + return res; + } +} diff --git a/java/1046-Last-Stone-Weight.java b/java/1046-Last-Stone-Weight.java new file mode 100644 index 000000000..aa14de0f1 --- /dev/null +++ b/java/1046-Last-Stone-Weight.java @@ -0,0 +1,13 @@ +class Solution { + + public int lastStoneWeight(int[] stones) { + PriorityQueue maxHeap = new PriorityQueue(); + for (int stone : stones) maxHeap.add(-stone); + while (maxHeap.size() > 1) { + int stone1 = maxHeap.remove(); + int stone2 = maxHeap.remove(); + if (stone1 != stone2) maxHeap.add(stone1 - stone2); + } + return maxHeap.size() != 0 ? (-maxHeap.remove()) : 0; + } +} diff --git a/java/1046-last-stone-weight.java b/java/1046-last-stone-weight.java new file mode 100644 index 000000000..aa14de0f1 --- /dev/null +++ b/java/1046-last-stone-weight.java @@ -0,0 +1,13 @@ +class Solution { + + public int lastStoneWeight(int[] stones) { + PriorityQueue maxHeap = new PriorityQueue(); + for (int stone : stones) maxHeap.add(-stone); + while (maxHeap.size() > 1) { + int stone1 = maxHeap.remove(); + int stone2 = maxHeap.remove(); + if (stone1 != stone2) maxHeap.add(stone1 - stone2); + } + return maxHeap.size() != 0 ? (-maxHeap.remove()) : 0; + } +} diff --git a/java/1071-greatest-common-divisor-of-strings.java b/java/1071-greatest-common-divisor-of-strings.java new file mode 100644 index 000000000..e65a976e2 --- /dev/null +++ b/java/1071-greatest-common-divisor-of-strings.java @@ -0,0 +1,27 @@ +class Solution { + + // Helper function to calculate gcd using Euclidean algorithm + public int gcd(int a, int b){ + if(b == 0){ + return a; + } + return gcd(b, a%b); + } + + /* + Approach: If the string have a GCD, then str1 will be of the form m*GCD and str2 will be of the form n*GCD. + So, str1 + str2 = m*GCD + n*GCD = (m+n)*GCD, hence str1 + str2 should be equal to str2 + str1. + If the above condition is satisfied, then we can find the GCD of the lengths of the strings and return the substring of str1 of length GCD. + Else, return empty string. + */ + + public String gcdOfStrings(String str1, String str2) { + if((str1 + str2).equals(str2 + str1)){ + int len1 = str1.length(); + int len2 = str2.length(); + int gcd = gcd(len1, len2); + return str1.substring(0, gcd); + } + return ""; + } +} \ No newline at end of file diff --git a/java/1081-smallest-subsequence-of-distinct-characters.java b/java/1081-smallest-subsequence-of-distinct-characters.java new file mode 100644 index 000000000..621228947 --- /dev/null +++ b/java/1081-smallest-subsequence-of-distinct-characters.java @@ -0,0 +1,24 @@ +class Solution { + public String smallestSubsequence(String s) { + Set seen = new HashSet<>(); + HashMap last = new HashMap<>(); + for(char c : s.toCharArray()){ + if(!last.containsKey(c)) + last.put(c, s.lastIndexOf(c)); + } + Stack stack = new Stack(); + for(int i = 0; i < s.length(); i++){ + char c = s.charAt(i); + if(seen.contains(c)) + continue; + while(!stack.isEmpty() && stack.peek() > c && i < last.get(stack.peek())) + seen.remove(stack.pop()); + stack.push(c); + seen.add(c); + } + StringBuilder sb = new StringBuilder(); + for(char c: stack) + sb.append(c); + return sb.toString(); + } +} diff --git a/java/1091-shortest-path-in-binary-matrix.java b/java/1091-shortest-path-in-binary-matrix.java new file mode 100644 index 000000000..951865f03 --- /dev/null +++ b/java/1091-shortest-path-in-binary-matrix.java @@ -0,0 +1,35 @@ +class Solution { + public int shortestPathBinaryMatrix(int[][] grid) { + int n = grid.length; + Queue q = new LinkedList<>(); + q.add(new Integer[]{0, 0, 1}); // row, col, length + + boolean[][] visited = new boolean[n][n]; + visited[0][0] = true; + + int[][] direct = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}, {1, 1}, {-1, -1}, {1, -1}, {-1, 1}}; + + while (!q.isEmpty()) { + Integer[] curr = q.poll(); + int r = curr[0]; + int c = curr[1]; + int length = curr[2]; + + // out of bounds or no way further + if (Math.min(r, c) < 0 || Math.max(r, c) == n || grid[r][c] == 1) continue; + + if (r == n - 1 && c == n - 1) return length; + + for (int[] d : direct) { + int newRow = r + d[0]; + int newCol = c + d[1]; + if (Math.min(newRow, newCol) >= 0 && Math.max(newRow, newCol) < n && !visited[newRow][newCol]) { + q.add(new Integer[]{newRow, newCol, 1 + length}); + visited[newRow][newCol] = true; + } + } + } + return -1; + } + +} diff --git a/java/1094-car-pooling.java b/java/1094-car-pooling.java new file mode 100644 index 000000000..5dd460deb --- /dev/null +++ b/java/1094-car-pooling.java @@ -0,0 +1,17 @@ +class Solution { + public boolean carPooling(int[][] trips, int capacity) { + int[] passChange = new int[1001]; + for (int[] t : trips) { + passChange[t[1]] += t[0]; + passChange[t[2]] -= t[0]; + } + int curPass = 0; + for (int i = 0; i < 1001; i++) { + curPass += passChange[i]; + if (curPass > capacity) { + return false; + } + } + return true; + } +} diff --git a/java/1095-find-in-mountain-array.java b/java/1095-find-in-mountain-array.java new file mode 100644 index 000000000..c742ef310 --- /dev/null +++ b/java/1095-find-in-mountain-array.java @@ -0,0 +1,69 @@ +/** + * // This is MountainArray's API interface. + * // You should not implement it, or speculate about its implementation + * interface MountainArray { + * public int get(int index) {} + * public int length() {} + * } + */ + +class Solution { + public int findInMountainArray(int target, MountainArray mountainArr) { + int maxIndex = findMax(mountainArr); + int leftResult = binarySearchLeft(mountainArr, target, maxIndex); + int rightResult = binarySearchRight(mountainArr, target, maxIndex); + + return (leftResult == -1 && rightResult == -1) ? -1 : leftResult == -1 ? rightResult : leftResult; + } + + public int findMax(MountainArray array) { + int left = 0; + int right = array.length() - 1; + + while (left<=right) { + int mid = left + (right - left)/2; + if (array.get(mid) < array.get(mid + 1)) { + left = mid + 1; + } else { + right = mid - 1; + } + } + + return left; + } + + public int binarySearchLeft(MountainArray array, int target, int right) { + int left = 0; + + while (left<=right) { + int mid = left + (right - left)/2; + int midValue = array.get(mid); + if (midValue < target) { + left = mid + 1; + } else if (midValue > target) { + right = mid - 1; + } else { + return mid; + } + } + return -1; + } + + public int binarySearchRight(MountainArray array, int target, int left) { + int right = array.length() - 1; + + while (left <= right) { + int mid = left + (right - left)/2; + int midValue = array.get(mid); + + if (midValue < target) { + right = mid - 1; + } else if (midValue > target) { + left = mid + 1; + } else { + return mid; + } + } + return -1; + } +} \ No newline at end of file diff --git a/java/1105-filling-bookcase-shelves.java b/java/1105-filling-bookcase-shelves.java new file mode 100644 index 000000000..6aee87247 --- /dev/null +++ b/java/1105-filling-bookcase-shelves.java @@ -0,0 +1,33 @@ +class Solution { + public int minHeightShelves(int[][] books, int shelfWidth) { + Map cache = new HashMap<>(); + + return helper(books, shelfWidth, 0, cache); + } + private int helper(int[][] books, int shelfWidth, int i, Map cache) { + if (i == books.length) { + return 0; + } + if (cache.containsKey(i)) { + return cache.get(i); + } + + int curWidth = shelfWidth; + int maxHeight = 0; + int result = Integer.MAX_VALUE; + + for (int j = i; j < books.length; j++) { + int width = books[j][0]; + int height = books[j][1]; + if (curWidth < width) { + break; + } + curWidth -= width; + maxHeight = Math.max(maxHeight, height); + result = Math.min(result, helper(books, shelfWidth, j + 1, cache) + maxHeight); + } + + cache.put(i, result); + return result; + } +} diff --git a/java/1129-shortest-path-with-alternating-colors.java b/java/1129-shortest-path-with-alternating-colors.java new file mode 100644 index 000000000..5ef80bf6d --- /dev/null +++ b/java/1129-shortest-path-with-alternating-colors.java @@ -0,0 +1,57 @@ +class Solution { + public int[] shortestAlternatingPaths(int n, int[][] redEdges, int[][] blueEdges) { + Map> red = new HashMap<>(); + Map> blue = new HashMap<>(); + for (int i = 0; i < n; i++) { + red.put(i, new ArrayList<>()); + blue.put(i, new ArrayList<>()); + } + for (int[] redEdge : redEdges) { + red.get(redEdge[0]).add(redEdge[1]); + } + for (int[] blueEdge : blueEdges) { + blue.get(blueEdge[0]).add(blueEdge[1]); + } + int[] answer = new int[n]; + Arrays.fill(answer, -1); + + ArrayDeque deque = new ArrayDeque<>(); + boolean[][] visit = new boolean[n][2]; + deque.addLast(new ColorStep(0, 0, -1)); + while (!deque.isEmpty()) { + ColorStep step = deque.pollFirst(); + if (answer[step.node] == -1) { + answer[step.node] = step.length; + } + if (step.prevEdgeColor != 0) { + for (int nei : red.get(step.node)) { + if (!visit[nei][0]) { + visit[nei][0] = true; + deque.addLast(new ColorStep(nei, step.length + 1, 0)); + } + } + } + if (step.prevEdgeColor != 1) { + for (int nei : blue.get(step.node)) { + if (!visit[nei][1]) { + visit[nei][1] = true; + deque.addLast(new ColorStep(nei, step.length + 1, 1)); + } + } + } + } + return answer; + } + + public class ColorStep { + int node; + int length; + int prevEdgeColor; // 0 is for Red, 1 is for Blue + + public ColorStep(int node, int length, int prevEdgeColor) { + this.node = node; + this.length = length; + this.prevEdgeColor = prevEdgeColor; + } + } +} diff --git a/java/1140-stone-game-ii.java b/java/1140-stone-game-ii.java new file mode 100644 index 000000000..990a70219 --- /dev/null +++ b/java/1140-stone-game-ii.java @@ -0,0 +1,31 @@ +class Solution { + public int stoneGameII(int[] piles) { + Integer[][][] dp = new Integer[2][piles.length + 1][piles.length + 1]; + return dfs(piles, 0, 0, 1, dp); + } + + private int dfs(int[] piles, int alice, int i, int M, Integer[][][] dp) { + if (i == piles.length) { + return 0; + } + if (dp[alice][i][M] != null) { + return dp[alice][i][M]; + } + int res = (alice == 0) ? 0 : Integer.MAX_VALUE; + int total = 0; + for (int X = 1; X < 2 * M + 1; X++) { + if (i + X > piles.length) { + break; + } + total += piles[i + X - 1]; + int recurRes = dfs(piles, 1 - alice, i + X, Math.max(X, M), dp); + if (alice == 0) { + res = Math.max(res, total + recurRes); + } else { + res = Math.min(res, recurRes); + } + } + dp[alice][i][M] = res; + return res; + } +} diff --git a/java/1143-longest-common-subsequence.java b/java/1143-longest-common-subsequence.java new file mode 100644 index 000000000..d079fb422 --- /dev/null +++ b/java/1143-longest-common-subsequence.java @@ -0,0 +1,41 @@ +//memoized version + +class Solution { + + public int longestCommonSubsequence(String text1, String text2) { + int[][] dp = new int[text1.length()][text2.length()]; + return LCS(text1, text2, 0, 0, dp); + } + + public int LCS(String s1, String s2, int i, int j, int[][] dp) { + if (i >= s1.length() || j >= s2.length()) return 0; else if ( + dp[i][j] != 0 + ) return dp[i][j]; else if (s1.charAt(i) == s2.charAt(j)) return ( + 1 + LCS(s1, s2, i + 1, j + 1, dp) + ); else { + dp[i][j] = + Math.max(LCS(s1, s2, i + 1, j, dp), LCS(s1, s2, i, j + 1, dp)); + return dp[i][j]; + } + } +} + +// Iterative version +class Solution { + + public int longestCommonSubsequence(String text1, String text2) { + int[][] dp = new int[text1.length() + 1][text2.length() + 1]; + + for (int i = text1.length() - 1; i >= 0; i--) { + for (int j = text2.length() - 1; j >= 0; j--) { + if (text1.charAt(i) == text2.charAt(j)) { + dp[i][j] = 1 + dp[i + 1][j + 1]; + } else { + dp[i][j] = Math.max(dp[i][j + 1], dp[i + 1][j]); + } + } + } + + return dp[0][0]; + } +} \ No newline at end of file diff --git a/java/1155-number-of-dice-rolls-with-target-sum.java b/java/1155-number-of-dice-rolls-with-target-sum.java new file mode 100644 index 000000000..7c46a8184 --- /dev/null +++ b/java/1155-number-of-dice-rolls-with-target-sum.java @@ -0,0 +1,47 @@ +/* Top Down Method +-----------------------------------*/ +class Solution { + int MOD = (int)1e9 + 7; + Map memo; + + public int numRollsToTarget(int n, int k, int target) { + memo = new HashMap<>(); + return count(n, k, target); + } + + private int count(int n, int k, int target){ + String currState = n + "," + target; + if(n == 0) + return (target == 0)? 1: 0; + if(memo.containsKey(currState)) + return memo.get(currState); + + int res = 0; + for(int val = 1; val < k + 1; val++) + res = (res + count(n - 1, k, target - val)) % MOD; + memo.put(currState, res); + return res; + } +} + +/* Bottom Up Method +-----------------------------------------*/ +class Solution { + public int numRollsToTarget(int n, int k, int target) { + int[] dp = new int[target + 1]; + dp[0] = 1; + int MOD = (int) 1e9 + 7; + + for(int dice = 0; dice < n; dice++){ + int[] next_dp = new int[target + 1]; + + for(int val = 1; val < k + 1; val++){ + for(int total = val; total < target + 1; total++){ + next_dp[total] = (next_dp[total] + dp[total - val]) % MOD; + } + } + dp = next_dp; + } + return dp[target]; + } +} diff --git a/java/1160-find-words-that-can-be-formed-by-characters.java b/java/1160-find-words-that-can-be-formed-by-characters.java new file mode 100644 index 000000000..580511cff --- /dev/null +++ b/java/1160-find-words-that-can-be-formed-by-characters.java @@ -0,0 +1,24 @@ +//Intuitive approach + +class Solution { + + public int countCharacters(String[] words, String chars) { + int[] arrChars = new int[26]; + for (int i = 0; i < chars.length(); i++) { + arrChars[chars.charAt(i) - 'a']++; + } + int ans = 0; + for (String s : words) { + boolean flag = true; + int[] arrS = new int[26]; + for (int i = 0; i < s.length(); i++) { + arrS[s.charAt(i) - 'a']++; + } + for (int i = 0; i < 26; i++) { + if (arrS[i] > arrChars[i]) flag = false; + } + if (flag) ans += s.length(); + } + return ans; + } +} diff --git a/java/1189-maximum-number-of-balloons.java b/java/1189-maximum-number-of-balloons.java new file mode 100644 index 000000000..6848a6cdd --- /dev/null +++ b/java/1189-maximum-number-of-balloons.java @@ -0,0 +1,33 @@ +class Solution { + public int maxNumberOfBalloons(String text) { + HashMap balloon = new HashMap<>(); + HashMap countText = new HashMap<>(); + + char[] balloonArray = "balloon".toCharArray(); + + for (char c : balloonArray) { + if (balloon.containsKey(c)) { + balloon.put(c,balloon.get(c)+1); + } else { + balloon.put(c,1); + } + } + + char[] countTextArray = text.toCharArray(); + + for (char c : countTextArray) { + if (countText.containsKey(c)) { + countText.put(c,countText.get(c)+1); + } else { + countText.put(c,1); + } + } + + int res = text.length(); + for (Character c : balloon.keySet()) { + res = Math.min(res,countText.getOrDefault(c,0) / balloon.get(c)); + } + + return res; + } +} diff --git a/java/1207-unique-number-of-occurrences.java b/java/1207-unique-number-of-occurrences.java new file mode 100644 index 000000000..44b4ee6f3 --- /dev/null +++ b/java/1207-unique-number-of-occurrences.java @@ -0,0 +1,15 @@ +class Solution { + public boolean uniqueOccurrences(int[] arr) { + Map map = new HashMap<>(); + for(int n: arr) + map.put(n, map.getOrDefault(n, 0) + 1); + + Set set = new HashSet<>(); + for(int n: map.keySet()){ + if(set.contains(map.get(n))) + return false; + set.add(map.get(n)); + } + return true; + } +} diff --git a/java/1209-remove-all-adjacent-duplicates-in-string-ii.java b/java/1209-remove-all-adjacent-duplicates-in-string-ii.java new file mode 100644 index 000000000..19fbfd34b --- /dev/null +++ b/java/1209-remove-all-adjacent-duplicates-in-string-ii.java @@ -0,0 +1,30 @@ +class Solution { + public String removeDuplicates(String s, int k) { + Stack> stack = new Stack<>(); + char[] ss = s.toCharArray(); + + for (char current : ss) { + if (!stack.isEmpty() && stack.peek().getKey() == current) { + int topCharCount = stack.pop().getValue(); + stack.push(new Pair<>(current, topCharCount + 1)); + } else { + stack.push(new Pair<>(current, 1)); + } + + if (stack.peek().getValue() == k) { + stack.pop(); + } + } + + StringBuilder result = new StringBuilder(); + + while (!stack.isEmpty()) { + Pair poppedPair = stack.pop(); + for (int i = 0; i < poppedPair.getValue(); i++) { + result.append(poppedPair.getKey()); + } + } + + return result.reverse().toString(); + } +} diff --git a/java/1220-count-vowels-permutation.java b/java/1220-count-vowels-permutation.java new file mode 100644 index 000000000..a82106165 --- /dev/null +++ b/java/1220-count-vowels-permutation.java @@ -0,0 +1,141 @@ +class Solution { + int MOD = (int) 1e9+7; + + public int countVowelPermutation(int n) { + if (n == 1) { + return 5; + } + + long[][] dp = new long[n + 1][5]; + for (int j = 0; j < 5; j++) { + dp[1][j] = 1; + } + + for (int i = 2; i <= n; i++) { + dp[i][0] = dp[i - 1][1]; + dp[i][1] = (dp[i - 1][0] + dp[i - 1][2]) % MOD; + dp[i][2] = (dp[i - 1][0] + dp[i - 1][1] + dp[i - 1][3] + dp[i - 1][4]) % MOD; + dp[i][3] = (dp[i - 1][2] + dp[i - 1][4]) % MOD; + dp[i][4] = dp[i - 1][0]; + } + + long result = 0; + for (int j = 0; j < 5; j++) { + result = (result + dp[n][j]) % MOD; + } + + return (int)result; + } +} + +/* Top Down Approach +----------------------------------------- + Time Complexity : O(n) + Space Complexity : O(n) +----------------------------------------*/ + +class Solution { + HashMap memo = new HashMap<>(); + int MOD = (int) 1e9+7; + + public int countVowelPermutation(int n) { + long[] counts = getBaseCounts(); + if(n == 1) { + return getSum(counts); + } + + Map> mapNextCounting; + mapNextCounting = getNextCountMapping(); + + for(int i=1; i memo = new HashMap<>(); + int MOD = (int) 1e9 + 7; + + public int countVowelPermutation(int n) { + long ans = 0; + ans = (ans + dfs('a', n, 1)) % MOD; + ans = (ans + dfs('e', n, 1)) % MOD; + ans = (ans + dfs('i', n, 1)) % MOD; + ans = (ans + dfs('o', n, 1)) % MOD; + ans = (ans + dfs('u', n, 1)) % MOD; + return (int)ans; + } + + private int dfs(char c, int n, int l){ + if(l == n) + return 1; + + String key = c + "_" + l; + if (memo.containsKey(key)) return memo.get(key); + + long res = 0; + if (c == 'a') { + res = dfs('e', n, l + 1); + } else if (c == 'e') { + res = (res + dfs('a', n, l + 1)) % MOD; + res = (res + dfs('i', n, l + 1)) % MOD; + } else if (c == 'i') { + res = (res + dfs('a', n, l + 1)) % MOD; + res = (res + dfs('e', n, l + 1)) % MOD; + res = (res + dfs('o', n, l + 1)) % MOD; + res = (res + dfs('u', n, l + 1)) % MOD; + } else if (c == 'o') { + res = (res + dfs('i', n, l + 1)) % MOD; + res = (res + dfs('u', n, l + 1)) % MOD; + } else { + res = dfs('a', n, l + 1); + } + + memo.put(key, (int)(res % MOD)); + return (int)(res % MOD); + } +} diff --git a/java/1225-maximum-score-words-formed-by-letters.java b/java/1225-maximum-score-words-formed-by-letters.java new file mode 100644 index 000000000..59413fc12 --- /dev/null +++ b/java/1225-maximum-score-words-formed-by-letters.java @@ -0,0 +1,48 @@ +class Solution { + + Map freq; + int[] scoree; + + public int maxScoreWords(String[] words, char[] letters, int[] score) { + scoree = score; + freq = new HashMap<>(); + for(char c: letters){ + freq.put(c, freq.getOrDefault(c, 0) + 1); + } + + return backtrack(words, 0); + } + private int backtrack(String[] words, int idx){ + if(idx == words.length) + return 0; + + int res = backtrack(words, idx + 1); + if(can_form_word(words[idx])){ + for(char c: words[idx].toCharArray()) + freq.put(c, freq.get(c) - 1); + + res = Math.max(res, get_score(words[idx]) + backtrack(words, idx + 1)); + for(char c: words[idx].toCharArray()) + freq.put(c, freq.get(c) + 1); + } + return res; + } + private boolean can_form_word(String s){ + Map map = new HashMap<>(); + for(char c: s.toCharArray()) + map.put(c, map.getOrDefault(c, 0) + 1); + + for(char c: map.keySet()){ + if(!freq.containsKey(c) || map.get(c) > freq.get(c)) + return false; + } + return true; + } + private int get_score(String s){ + int score = 0; + for(char c: s.toCharArray()){ + score += scoree[c - 'a']; + } + return score; + } +} diff --git a/java/1235-maximum-profit-in-job-scheduling.java b/java/1235-maximum-profit-in-job-scheduling.java new file mode 100644 index 000000000..42980e73b --- /dev/null +++ b/java/1235-maximum-profit-in-job-scheduling.java @@ -0,0 +1,54 @@ +class Solution { + int[][] intervals; + Integer[] cache; + + public int jobScheduling(int[] startTime, int[] endTime, int[] profit) { + int n = startTime.length; + intervals = new int[n][3]; + cache = new Integer[n]; + + for (int i = 0; i < n; i++) { + intervals[i] = new int[]{startTime[i], endTime[i], profit[i]}; + } + + Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0])); + + return dfs(0); + } + + private int dfs(int i) { + if (i == intervals.length) { + return 0; + } + + if (cache[i] != null) { + return cache[i]; + } + + // don't include + int res = dfs(i + 1); + + // include + int j = binarySearch(intervals, i, intervals[i][1]); + cache[i] = res = Math.max(res, intervals[i][2] + dfs(j)); + + return res; + } + + private int binarySearch(int[][] intervals, int start, int target) { + int left = start + 1; + int right = intervals.length - 1; + + while (left <= right) { + int mid = left + (right - left) / 2; + + if (intervals[mid][0] < target) { + left = mid + 1; + } else { + right = mid - 1; + } + } + + return left; + } +} diff --git a/java/1248-count-number-of-nice-subarrays.java b/java/1248-count-number-of-nice-subarrays.java new file mode 100644 index 000000000..b0cd8b4cb --- /dev/null +++ b/java/1248-count-number-of-nice-subarrays.java @@ -0,0 +1,26 @@ +class Solution { + public int numberOfSubarrays(int[] nums, int k) { + int res = 0, odds = 0; + int l = 0, m = 0; + + for(int r = 0; r < nums.length; r++){ + if(nums[r] % 2 == 1) + odds += 1; + + while(odds > k){ + if(nums[l] % 2 == 1) + odds -= 1; + l += 1; + m = l; + } + + if(odds == k){ + while(nums[m] % 2 != 1){ + m += 1; + } + res += m - l + 1; + } + } + return res; + } +} diff --git a/java/1255-maximum-score-words-formed-by-letters.java b/java/1255-maximum-score-words-formed-by-letters.java new file mode 100644 index 000000000..0ac3a5913 --- /dev/null +++ b/java/1255-maximum-score-words-formed-by-letters.java @@ -0,0 +1,48 @@ +class Solution { + public int maxScoreWords(String[] words, char[] letters, int[] score) { + int[] letterCount = new int[26]; + for (char letter : letters) { + letterCount[letter - 'a'] += 1; + } + return backtrack(0, words, score, letterCount); + } + + private int backtrack(int i, String[] words, int[] score, int[] letterCount) { + if (i == words.length) { + return 0; + } + int res = backtrack(i + 1, words, score, letterCount); + String word = words[i]; + if (canFormWord(word, letterCount)) { + for (char ch : word.toCharArray()) { + letterCount[ch - 'a'] -= 1; + } + res = Math.max(res, getScore(word, score) + backtrack(i + 1, words, score, letterCount)); + for (char ch : word.toCharArray()) { + letterCount[ch - 'a'] += 1; + } + } + return res; + } + + private boolean canFormWord(String word, int[] letterCount) { + int[] wordCount = new int[26]; + for (char ch : word.toCharArray()) { + wordCount[ch - 'a'] += 1; + } + for (int i = 0; i < 26; i++) { + if (wordCount[i] > letterCount[i]) { + return false; + } + } + return true; + } + + private int getScore(String word, int[] score) { + int total = 0; + for (char ch : word.toCharArray()) { + total += score[ch - 'a']; + } + return total; + } +} diff --git a/java/1260-shift-2d-grid.java b/java/1260-shift-2d-grid.java new file mode 100644 index 000000000..d25901f88 --- /dev/null +++ b/java/1260-shift-2d-grid.java @@ -0,0 +1,26 @@ +class Solution { + static final int ROW = 0, COL = 1; + public List> shiftGrid(int[][] grid, int k) { + final int M = grid.length, N = grid[0].length; + + BiFunction posToVal = (r, c) -> + r * N + c; + Function valToPos = (v) -> + new int[] {v / N, v % N}; + + List> res = new ArrayList<>(); + for(int i = 0; i < M; i++) { + Integer[] tmp = new Integer[N]; + for(int j = 0; j < N; j++) + tmp[j] = 0; + res.add(Arrays.asList(tmp)); + } + for(int r = 0; r < M; r++) + for(int c = 0; c < N; c++) { + int newVal = (posToVal.apply(r, c) + k) % (M * N); + int[] newRC = valToPos.apply(newVal); + res.get(newRC[ROW]).set(newRC[COL], grid[r][c]); + } + return res; + } +} diff --git a/java/1268-search-suggestions-system.java b/java/1268-search-suggestions-system.java new file mode 100644 index 000000000..7cbf398e4 --- /dev/null +++ b/java/1268-search-suggestions-system.java @@ -0,0 +1,22 @@ +class Solution { + public List> suggestedProducts(String[] products, String searchWord) { + List> res = new ArrayList<>(); + Arrays.sort(products); + + int l = 0; + int r = products.length - 1; + for (int i = 0; i < searchWord.length(); i++) { + while (l <= r && (products[l].length() <= i || products[l].charAt(i) != searchWord.charAt(i))) + l++; + while (l <= r && (products[r].length() <= i || products[r].charAt(i) != searchWord.charAt(i))) + r--; + + List list = new ArrayList<>(); + for (int j = l; j < l + 3 && j <= r; j++) { + list.add(products[j]); + } + res.add(list); + } + return res; + } +} \ No newline at end of file diff --git a/java/1269-number-of-ways-to-stay-in-the-same-place-after-some-steps.java b/java/1269-number-of-ways-to-stay-in-the-same-place-after-some-steps.java new file mode 100644 index 000000000..9bccd54e0 --- /dev/null +++ b/java/1269-number-of-ways-to-stay-in-the-same-place-after-some-steps.java @@ -0,0 +1,59 @@ +/* Top-Down Approach +------------------------------------- + Time Complexity: O(n*min(n,m)) + Space Complexity: O(n*min(n,m)) +------------------------------------*/ +class Solution { + Map memo; + int MOD = (int)1e9+7; + public int numWays(int steps, int arrLen){ + memo = new HashMap<>(); + return dfs(0, steps, arrLen); + } + private int dfs(int i, int steps, int n){ + String pos = i + "," + steps; + if(memo.containsKey(pos)) + return memo.get(pos); + if(steps == 0 && i == 0) + return 1; + if(i < 0 || i > n-1 || steps == 0) + return 0; + + long left = dfs(i-1, steps-1, n); + long stay = dfs(i, steps-1, n); + long right = dfs(i+1, steps-1, n); + int res = (int)((left + stay + right)%MOD); + memo.put(pos, res); + return res; + } +} + +/* Bottom-Up Approach +------------------------------------ + Time Complexity: O(n*min(n,m)) + Space Complexity: O(n*min(n,m)) +-----------------------------------*/ +class Solution { + public int numWays(int steps, int arrLen) { + int MOD = (int)1e9+7; + arrLen = Math.min(arrLen, steps); + int[][] dp = new int[steps + 1][arrLen]; + + dp[0][0] = 1; + + for (int step = 1; step <= steps; step++) { + for (int pos = 0; pos < arrLen; pos++) { + dp[step][pos] = dp[step - 1][pos]; + + if (pos - 1 >= 0) { + dp[step][pos] = (dp[step][pos] + dp[step - 1][pos - 1]) % MOD; + } + if (pos + 1 < arrLen) { + dp[step][pos] = (dp[step][pos] + dp[step - 1][pos + 1]) % MOD; + } + } + } + + return dp[steps][0]; + } +} diff --git a/java/1275-find-winner-on-a-tic-tac-toe-game.java b/java/1275-find-winner-on-a-tic-tac-toe-game.java new file mode 100644 index 000000000..e2d684369 --- /dev/null +++ b/java/1275-find-winner-on-a-tic-tac-toe-game.java @@ -0,0 +1,31 @@ +class Solution { + + public String tictactoe(int[][] moves) { + int[][][] result = new int[2][2][3]; + int[][] diag1 = new int[2][1]; + int[][] diag2 = new int[2][1]; + + int p = 0; + for (int i = 0; i < moves.length; i++) { + int r = moves[i][0]; + int c = moves[i][1]; + + if (++result[p][0][r] == 3) return getWinner(p); + if (++result[p][1][c] == 3) return getWinner(p); + + if (r == c) if (++diag1[p][0] == 3) return getWinner(p); + + if ( + (r == 1 && c == 1) || (r == 0 && c == 2) || (r == 2 && c == 0) + ) if (++diag2[p][0] == 3) return getWinner(p); + + if (p == 0) p = 1; else p = 0; + } + + return moves.length == 9 ? "Draw" : "Pending"; + } + + private String getWinner(int p) { + return p == 0 ? "A" : "B"; + } +} diff --git a/java/1291-sequential-digits.java b/java/1291-sequential-digits.java new file mode 100644 index 000000000..f26bbe74e --- /dev/null +++ b/java/1291-sequential-digits.java @@ -0,0 +1,20 @@ +class Solution { + public List sequentialDigits(int low, int high) { + List res = new ArrayList<>(); + Queue q = new LinkedList<>(); + for(int i = 1; i < 10; i++) + q.add(i); + + while(!q.isEmpty()){ + int n = q.poll(); + if(n > high) + continue; + if(n >= low && n <= high) + res.add(n); + int ones = n % 10; + if(ones < 9) + q.add(n * 10 + (ones + 1)); + } + return res; + } +} diff --git a/java/1299-Replace-Elements-With-Greatest-Element-On-Right-Side.java b/java/1299-Replace-Elements-With-Greatest-Element-On-Right-Side.java new file mode 100644 index 000000000..e8b324338 --- /dev/null +++ b/java/1299-Replace-Elements-With-Greatest-Element-On-Right-Side.java @@ -0,0 +1,11 @@ +class Solution { + public int[] replaceElements(int[] arr) { + int rightMax = -1; + for (int i = arr.length - 1; i >= 0; i--) { + int newMax = Math.max(rightMax, arr[i]); + arr[i] = rightMax; + rightMax = newMax; + } + return arr; + } +} diff --git a/java/1299-replace-elements-with-greatest-element-on-right-side.java b/java/1299-replace-elements-with-greatest-element-on-right-side.java new file mode 100644 index 000000000..e8b324338 --- /dev/null +++ b/java/1299-replace-elements-with-greatest-element-on-right-side.java @@ -0,0 +1,11 @@ +class Solution { + public int[] replaceElements(int[] arr) { + int rightMax = -1; + for (int i = arr.length - 1; i >= 0; i--) { + int newMax = Math.max(rightMax, arr[i]); + arr[i] = rightMax; + rightMax = newMax; + } + return arr; + } +} diff --git a/java/1335-minimum-difficulty-of-a-job-schedule.java b/java/1335-minimum-difficulty-of-a-job-schedule.java new file mode 100644 index 000000000..3026df72e --- /dev/null +++ b/java/1335-minimum-difficulty-of-a-job-schedule.java @@ -0,0 +1,28 @@ +class Solution { + Map cache; + + public int minDifficulty(int[] jobDifficulty, int d) { + if(d > jobDifficulty.length) + return -1; + cache = new HashMap<>(); + return dfs(0, d, -1, jobDifficulty); + } + + private int dfs(int i, int d, int cur_max, int[] jobDiff){ + if(i == jobDiff.length) + return (d == 0)? 0: (int)1e9; + if(d == 0) + return (int)1e9; + String cur_state = i + "," + d + "," + cur_max; + if(cache.containsKey(cur_state)) + return cache.get(cur_state); + + cur_max = Math.max(cur_max, jobDiff[i]); + int res = Math.min( + dfs(i + 1, d, cur_max, jobDiff), + cur_max + dfs(i + 1, d - 1, -1, jobDiff) + ); + cache.put(cur_state, res); + return res; + } +} diff --git a/java/1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.java b/java/1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.java new file mode 100644 index 000000000..f2d9abbf8 --- /dev/null +++ b/java/1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.java @@ -0,0 +1,18 @@ +class Solution { + public int numOfSubarrays(int[] arr, int k, int threshold) { + int res = 0, currSum = 0; + + for (int i = 0; i < k - 1; i++) { + currSum += arr[i]; + } + + for (int l = 0; l < arr.length - k + 1; l++) { + currSum += arr[l + k - 1]; + if ((currSum / k) >= threshold) { + res++; + } + currSum -= arr[l]; + } + return res; + } +} diff --git a/java/1347-minimum-number-of-steps-to-make-two-strings-anagram.java b/java/1347-minimum-number-of-steps-to-make-two-strings-anagram.java new file mode 100644 index 000000000..78563cd19 --- /dev/null +++ b/java/1347-minimum-number-of-steps-to-make-two-strings-anagram.java @@ -0,0 +1,22 @@ +/*------------------------------- + Time Complexity: O(n) + Space Complexity: O(n) +--------------------------------*/ +class Solution { + public int minSteps(String s, String t) { + Map map1 = new HashMap<>(); + for(char c: s.toCharArray()) + map1.put(c, map1.getOrDefault(c, 0) + 1); + + int res = 0; + for(char c: t.toCharArray()){ + if(map1.containsKey(c) && map1.get(c) > 0){ + map1.put(c, map1.get(c)-1); + } + else{ + res++; + } + } + return res; + } +} diff --git a/java/1359-count-all-valid-pickup-and-delivery-options.java b/java/1359-count-all-valid-pickup-and-delivery-options.java new file mode 100644 index 000000000..e6922ff64 --- /dev/null +++ b/java/1359-count-all-valid-pickup-and-delivery-options.java @@ -0,0 +1,14 @@ +class Solution { + public int countOrders(int n) { + int MOD = (int) 1e9 + 7; + int slots = 2*n; + long res = 1; + + while(slots > 0){ + int valid_choices = slots*(slots - 1)/2; + res = (res * valid_choices) % MOD; + slots -= 2; + } + return (int)res; + } +} diff --git a/java/1361-validate-binary-tree-nodes.java b/java/1361-validate-binary-tree-nodes.java new file mode 100644 index 000000000..d80c4c1e2 --- /dev/null +++ b/java/1361-validate-binary-tree-nodes.java @@ -0,0 +1,47 @@ +/* BFS +-------------------------------- + Time Complexity: O(n) + Space Complexity: O(n) +------------------------------*/ + +class Solution { + public boolean validateBinaryTreeNodes(int n, int[] leftChild, int[] rightChild) { + int root = findRoot(n, leftChild, rightChild); + if(root == -1) + return false; + + Set visited = new HashSet<>(); + Queue q = new LinkedList<>(); + + q.add(root); + visited.add(root); + + while(!q.isEmpty()){ + int node = q.poll(); + int[] childerns = {leftChild[node], rightChild[node]}; + + for(int c: childerns){ + if(c == -1) + continue; + if(visited.contains(c)) + return false; + q.add(c); + visited.add(c); + } + } + return visited.size() == n; + } + private int findRoot(int n, int[] leftChild, int[] rightChild){ + Set set = new HashSet<>(); + for(int lc: leftChild) + set.add(lc); + for(int rc: rightChild) + set.add(rc); + + for(int i = 0; i < n; i++){ + if(!set.contains(i)) + return i; + } + return -1; + } +} diff --git a/java/1371-find-the-longest-substring-containing-vowels-in-even-counts.java b/java/1371-find-the-longest-substring-containing-vowels-in-even-counts.java new file mode 100644 index 000000000..334e52f93 --- /dev/null +++ b/java/1371-find-the-longest-substring-containing-vowels-in-even-counts.java @@ -0,0 +1,18 @@ +class Solution { + public int findTheLongestSubstring(String s) { + char[] char_mask = {1,0,0,0,2,0,0,0,4,0,0,0,0,0,8,0,0,0,0,0,16,0,0,0,0,0}; + int crr_mask = 0, res = 0; + int[] mask_map = new int[32]; + Arrays.fill(mask_map, -1); + + for(int i = 0; i < s.length(); i++){ + crr_mask ^= char_mask[s.charAt(i) - 'a']; + if(crr_mask != 0 && mask_map[crr_mask] == -1){ + mask_map[crr_mask] = i; + } + res = Math.max(res, i - mask_map[crr_mask]); + } + + return res; + } +} diff --git a/java/1376-time-needed-to-inform-all-employees.java b/java/1376-time-needed-to-inform-all-employees.java new file mode 100644 index 000000000..680e520e5 --- /dev/null +++ b/java/1376-time-needed-to-inform-all-employees.java @@ -0,0 +1,40 @@ +class Solution { + + public int numOfMinutes(int n, int headID, int[] manager, int[] informTime) { + Map> adjacentMap = prepareAdjacentMap(headID, manager); + + int time = 0; + + Deque> deque = new ArrayDeque<>(); + deque.addFirst(new Pair<>(headID, informTime[headID])); + + while (!deque.isEmpty()) { + Pair curManager = deque.pollLast(); + time = Math.max(time, curManager.getValue()); + + if (adjacentMap.containsKey(curManager.getKey())) { + for (Integer employee : adjacentMap.get(curManager.getKey())) { + deque.addFirst(new Pair<>(employee, curManager.getValue() + informTime[employee])); + } + } + } + + return time; + } + + private Map> prepareAdjacentMap(int headId, int[] manager) { + Map> adjacentMap = new HashMap<>(); + + for (int i = 0; i < manager.length; i++) { + if (i == headId) { + continue; + } + if (!adjacentMap.containsKey(manager[i])) { + adjacentMap.put(manager[i], new ArrayList<>()); + } + adjacentMap.get(manager[i]).add(i); + } + + return adjacentMap; + } +} diff --git a/java/1396-design-underground-system.java b/java/1396-design-underground-system.java new file mode 100644 index 000000000..67abf0b46 --- /dev/null +++ b/java/1396-design-underground-system.java @@ -0,0 +1,41 @@ +class UndergroundSystem { + // id, src, time + Map> entries; + // src, dest, int[total time, number of travels] + Map> times; + + public UndergroundSystem() { + this.entries = new HashMap<>(); + this.times = new HashMap<>(); + } + + public void checkIn(int id, String stationName, int t) { + entries.put(id, new Pair<>(stationName, t)); + } + + public void checkOut(int id, String dest, int t) { + Pair entry = entries.get(id); + String src = entry.getKey(); + int duration = t - entry.getValue(); + + Map city = times.getOrDefault(src, new HashMap<>()); + int[] total = city.getOrDefault(dest, new int[2]); + total[0] += duration; + total[1]++; + city.put(dest, total); + times.put(src, city); + } + + public double getAverageTime(String startStation, String endStation) { + int[] total = times.get(startStation).get(endStation); + return 1.0 * total[0] / total[1]; + } +} + +/** + * Your UndergroundSystem object will be instantiated and called as such: + * UndergroundSystem obj = new UndergroundSystem(); + * obj.checkIn(id,stationName,t); + * obj.checkOut(id,stationName,t); + * double param_3 = obj.getAverageTime(startStation,endStation); + */ \ No newline at end of file diff --git a/java/1405-longest-happy-string.java b/java/1405-longest-happy-string.java new file mode 100644 index 000000000..c06b91c74 --- /dev/null +++ b/java/1405-longest-happy-string.java @@ -0,0 +1,44 @@ +class Solution { + public String longestDiverseString(int a, int b, int c) { + String res = ""; + PriorityQueue> maxHeap = new PriorityQueue<>( + (i1, i2) -> i2.getKey() - i1.getKey()); + if (a > 0) { + maxHeap.add(new Pair<>(a, 'a')); + } + if (b > 0) { + maxHeap.add(new Pair<>(b, 'b')); + } + if (c > 0) { + maxHeap.add(new Pair<>(c, 'c')); + } + + while (!maxHeap.isEmpty()) { + Pair item = maxHeap.poll(); + Integer count = item.getKey(); + Character ch = item.getValue(); + if (res.length() > 1 + && ch.equals(res.charAt(res.length() - 1)) + && ch.equals(res.charAt(res.length() - 2))) { + if (maxHeap.isEmpty()) { + break; + } + Pair item2 = maxHeap.poll(); + Integer count2 = item2.getKey(); + Character ch2 = item2.getValue(); + res += ch2; + count2 -= 1; + if (count2 > 0) { + maxHeap.add(new Pair<>(count2, ch2)); + } + } else { + res += ch; + count -= 1; + } + if (count > 0) { + maxHeap.add(new Pair<>(count, ch)); + } + } + return res; + } +} diff --git a/java/1422-maximum-score-after-splitting-a-string.java b/java/1422-maximum-score-after-splitting-a-string.java new file mode 100644 index 000000000..08d97d187 --- /dev/null +++ b/java/1422-maximum-score-after-splitting-a-string.java @@ -0,0 +1,20 @@ +class Solution { + public int maxScore(String s) { + int zero = 0, one = 0; + int res = 0; + + for(char c: s.toCharArray()){ + if(c == '1') + one += 1; + } + for(int i = 0; i < s.length()-1; i++){ + char c = s.charAt(i); + if(c == '0') + zero += 1; + else + one -= 1; + res = Math.max(res, zero + one); + } + return res; + } +} diff --git a/java/1424-diagonal-traverse-ii.java b/java/1424-diagonal-traverse-ii.java new file mode 100644 index 000000000..cdf4f070a --- /dev/null +++ b/java/1424-diagonal-traverse-ii.java @@ -0,0 +1,26 @@ +class Solution { + public int[] findDiagonalOrder(List> nums) { + Map> map = new HashMap<>(); + int n = 0; + for(int i = nums.size()-1; i >= 0; i--){ + for(int j = 0; j < nums.get(i).size(); j++){ + n++; + if(!map.containsKey(i+j)){ + map.put(i+j, new ArrayList<>()); + } + map.get(i+j).add(nums.get(i).get(j)); + } + } + int[] res = new int[n]; + int idx = 0; + int curr = 0; + while(map.containsKey(curr)){ + for(int num: map.get(curr)){ + res[idx] = num; + idx++; + } + curr++; + } + return res; + } +} diff --git a/java/1436-destination-city.java b/java/1436-destination-city.java new file mode 100644 index 000000000..2fc9539b6 --- /dev/null +++ b/java/1436-destination-city.java @@ -0,0 +1,16 @@ +class Solution { + public String destCity(List> paths) { + Set set = new HashSet<>(); + for(List path: paths){ + String city = path.get(0); + set.add(city); + } + + for(List path: paths){ + String city = path.get(1); + if(!set.contains(city)) + return city; + } + return ""; + } +} diff --git a/java/1441-build-an-array-with-stack-operations.java b/java/1441-build-an-array-with-stack-operations.java new file mode 100644 index 000000000..412732eb5 --- /dev/null +++ b/java/1441-build-an-array-with-stack-operations.java @@ -0,0 +1,14 @@ +class Solution { + public List buildArray(int[] target, int n) { + List res = new ArrayList<>(); + int idx = 0; + for(int i = 1; i <= n && idx < target.length; i++){ + res.add("Push"); + if(target[idx++] != i){ + res.add("Pop"); + idx--; + } + } + return res; + } +} diff --git a/java/1442-count-triplets-that-can-form-two-arrays-of-equal-xor.java b/java/1442-count-triplets-that-can-form-two-arrays-of-equal-xor.java new file mode 100644 index 000000000..662b445d9 --- /dev/null +++ b/java/1442-count-triplets-that-can-form-two-arrays-of-equal-xor.java @@ -0,0 +1,43 @@ +/*------------------------------- + Time Complexity : O(n^3) + Space Complexity : O(1) +-------------------------------*/ +class Solution { + public int countTriplets(int[] arr) { + int res = 0; + + for(int i = 0; i < arr.length - 1; i++){ + int a = 0; + for(int j = i + 1; j < arr.length; j++){ + a ^= arr[j - 1]; + int b = 0; + for(int k = j; k < arr.length; k++){ + b ^= arr[k]; + if(a == b) + res += 1; + } + } + } + return res; + } +} + +/*------------------------------- + Time Complexity : O(n^2) + Space Complexity : O(1) +-------------------------------*/ +class Solution { + public int countTriplets(int[] arr) { + int res = 0; + + for(int i = 0; i < arr.length - 1; i++){ + int cur_xor = arr[i]; + for(int k = i + 1; k < arr.length; k++){ + cur_xor ^= arr[k]; + if(cur_xor == 0) + res += k - i; + } + } + return res; + } +} diff --git a/java/1448-Count-Good-Nodes-in-Binary-Tree.java b/java/1448-Count-Good-Nodes-in-Binary-Tree.java new file mode 100644 index 000000000..52644a4ac --- /dev/null +++ b/java/1448-Count-Good-Nodes-in-Binary-Tree.java @@ -0,0 +1,17 @@ +class Solution { + + public int goodNodes(TreeNode root) { + return helper(root, -99999); + } + + public int helper(TreeNode root, int max) { + if (root == null) return 0; + + int res = root.val >= max ? 1 : 0; + + res += helper(root.left, Math.max(root.val, max)); + res += helper(root.right, Math.max(root.val, max)); + + return res; + } +} diff --git a/java/1448-count-good-nodes-in-binary-tree.java b/java/1448-count-good-nodes-in-binary-tree.java new file mode 100644 index 000000000..52644a4ac --- /dev/null +++ b/java/1448-count-good-nodes-in-binary-tree.java @@ -0,0 +1,17 @@ +class Solution { + + public int goodNodes(TreeNode root) { + return helper(root, -99999); + } + + public int helper(TreeNode root, int max) { + if (root == null) return 0; + + int res = root.val >= max ? 1 : 0; + + res += helper(root.left, Math.max(root.val, max)); + res += helper(root.right, Math.max(root.val, max)); + + return res; + } +} diff --git a/java/1456-maximum-number-of-vowels-in-a-substring-of-given-length.java b/java/1456-maximum-number-of-vowels-in-a-substring-of-given-length.java new file mode 100644 index 000000000..c085590c4 --- /dev/null +++ b/java/1456-maximum-number-of-vowels-in-a-substring-of-given-length.java @@ -0,0 +1,18 @@ +class Solution { + public int maxVowels(String s, int k) { + Set set = new HashSet<>(); + Collections.addAll(set, 'a', 'e', 'i', 'o', 'u'); + + int l=0, cnt=0, res=0; + + for(int r=0; r k){ + cnt -= set.contains(s.charAt(l))?1:0; + l++; + } + res = Math.max(res, cnt); + } + return res; + } +} diff --git a/java/1457-pseudo-palindromic-paths-in-a-binary-tree.java b/java/1457-pseudo-palindromic-paths-in-a-binary-tree.java new file mode 100644 index 000000000..b00c16a14 --- /dev/null +++ b/java/1457-pseudo-palindromic-paths-in-a-binary-tree.java @@ -0,0 +1,29 @@ +class Solution { + Map count = new HashMap<>(); + int odd = 0; + + public int pseudoPalindromicPaths(TreeNode root) { + return dfs(root); + } + + private int dfs(TreeNode curr) { + if (curr == null) { + return 0; + } + + count.put(curr.val, count.getOrDefault(curr.val, 0) + 1); + int oddChange = count.get(curr.val) % 2 == 1 ? 1 : -1; + odd += oddChange; + + int res = 0; + if (curr.left == null && curr.right == null) { + res = (odd <= 1) ? 1 : 0; + } else { + res = dfs(curr.left) + dfs(curr.right); + } + + odd -= oddChange; + count.put(curr.val, count.get(curr.val) - 1); + return res; + } +} diff --git a/java/1461-check-if-a-string-contains-all-binary-codes-of-size-k.java b/java/1461-check-if-a-string-contains-all-binary-codes-of-size-k.java new file mode 100644 index 000000000..5d6aabcae --- /dev/null +++ b/java/1461-check-if-a-string-contains-all-binary-codes-of-size-k.java @@ -0,0 +1,9 @@ +class Solution { + public boolean hasAllCodes(String s, int k) { + Set set = new HashSet<>(); + for (int i = 0; i <= s.length() - k; i++) { + set.add(s.substring(i, i + k)); + } + return set.size() == Math.pow(2, k); + } +} \ No newline at end of file diff --git a/java/1462-course-schedule-iv.java b/java/1462-course-schedule-iv.java new file mode 100644 index 000000000..35999eb2a --- /dev/null +++ b/java/1462-course-schedule-iv.java @@ -0,0 +1,28 @@ +class Solution { + public List checkIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) { + HashMap> hm = new HashMap<>(); + List res = new ArrayList<>(); + boolean[] visited = new boolean[numCourses]; + for (int i = 0; i < prerequisites.length; i++) { + hm.putIfAbsent(prerequisites[i][1], new ArrayList<>()); + hm.get(prerequisites[i][1]).add(prerequisites[i][0]); + } + for (int i = 0; i < queries.length; i++) { + visited = new boolean[numCourses]; + res.add(dfs(hm, queries[i][1], queries[i][0], visited)); + } + return res; + } + + boolean dfs(HashMap> hm, int s, int target, boolean[] visited) { + if (!hm.containsKey(s)) return false; + if (hm.get(s).contains(target)) return true; + + for (int i: hm.get(s)) { + if (visited[i]) continue; + visited[i] = true; + if (dfs(hm, i, target, visited)) return true; + } + return false; + } +} diff --git a/java/1464-maximum-product-of-two-elements-in-an-array.java b/java/1464-maximum-product-of-two-elements-in-an-array.java new file mode 100644 index 000000000..825b5e80c --- /dev/null +++ b/java/1464-maximum-product-of-two-elements-in-an-array.java @@ -0,0 +1,7 @@ +class Solution { + public int maxProduct(int[] nums) { + Arrays.sort(nums); + + return (nums[nums.length-1]-1)*(nums[nums.length-2] -1); + } +} diff --git a/java/1466-reorder-routes-to-make-all-paths-lead-to-the-city-zero.java b/java/1466-reorder-routes-to-make-all-paths-lead-to-the-city-zero.java new file mode 100644 index 000000000..df6250995 --- /dev/null +++ b/java/1466-reorder-routes-to-make-all-paths-lead-to-the-city-zero.java @@ -0,0 +1,30 @@ +class Solution { + int res = 0; + HashMap> graph = new HashMap<>(); + HashSet oldEdges = new HashSet<>(); + + public int minReorder(int n, int[][] connections) { + HashSet visited = new HashSet<>(); + for (int[] edges : connections){ + int a = edges[0], b = edges[1]; + String s = a + "->" + b; + oldEdges.add(s); + graph.computeIfAbsent(a, val -> new ArrayList<>()).add(b); + graph.computeIfAbsent(b, val -> new ArrayList<>()).add(a); + } + dfs(0, -1, visited); + return res-1; + } + + private void dfs(int curr, int parent, HashSet visited) { + if (visited.contains(curr)) + return; + visited.add(curr); + String s = curr + "->" + parent; + if (!oldEdges.contains(s)) + res++; + for (int child : graph.get(curr)){ + dfs(child, curr, visited); + } + } +} diff --git a/java/1472-design-browser-history.java b/java/1472-design-browser-history.java new file mode 100644 index 000000000..ec69589d4 --- /dev/null +++ b/java/1472-design-browser-history.java @@ -0,0 +1,42 @@ +class ListNode{ + ListNode back; + String url; + ListNode forward; + public ListNode(String url){ + this.url = url; + back = forward = null; + } +} + +class BrowserHistory { + + ListNode head; + ListNode current; + + public BrowserHistory(String homepage) { + head = new ListNode(homepage); + current = head; + } + + public void visit(String url) { + current.forward = new ListNode(url); + current.forward.back = current; + current = current.forward; + } + + public String back(int steps) { + while(steps>0 && current.back!=null){ + current = current.back; + steps--; + } + return current.url; + } + + public String forward(int steps) { + while(steps>0 && current.forward!=null){ + current = current.forward; + steps--; + } + return current.url; + } +} \ No newline at end of file diff --git a/java/1481-least-number-of-unique-integers-after-k-removals.java b/java/1481-least-number-of-unique-integers-after-k-removals.java new file mode 100644 index 000000000..3cc46bc5a --- /dev/null +++ b/java/1481-least-number-of-unique-integers-after-k-removals.java @@ -0,0 +1,26 @@ +class Solution { + public int findLeastNumOfUniqueInts(int[] arr, int k) { + Map freq = new HashMap<>(); + for (int num : arr) { + freq.put(num, freq.getOrDefault(num, 0) + 1); + } + int[] freqList = new int[arr.length + 1]; + for (int f : freq.values()) { + freqList[f] += 1; + } + + int res = freq.size(); + for (int f = 1; f < freqList.length; f++) { + int remove = freqList[f]; + if (k >= f * remove) { + k -= f * remove; + res -= remove; + } else { + remove = k / f; + res -= remove; + break; + } + } + return res; + } +} diff --git a/java/1496-path-crossing.java b/java/1496-path-crossing.java new file mode 100644 index 000000000..23c3a7acd --- /dev/null +++ b/java/1496-path-crossing.java @@ -0,0 +1,23 @@ +class Solution { + public boolean isPathCrossing(String path) { + Set set = new HashSet<>(); + set.add("[0, 0]"); + int[] pos = {0, 0}; + + for(char c: path.toCharArray()){ + if(c == 'N'){ + pos[1] += 1; + } + else if(c == 'S') + pos[1] -= 1; + else if(c == 'E') + pos[0] += 1; + else + pos[0] -= 1; + if(set.contains(Arrays.toString(pos))) + return true; + set.add(Arrays.toString(pos)); + } + return false; + } +} diff --git a/java/1498-number-of-subsequences-that-satisfy-the-given-sum-condition.java b/java/1498-number-of-subsequences-that-satisfy-the-given-sum-condition.java new file mode 100644 index 000000000..a0aff7933 --- /dev/null +++ b/java/1498-number-of-subsequences-that-satisfy-the-given-sum-condition.java @@ -0,0 +1,27 @@ +class Solution { + public int numSubseq(int[] nums, int target) { + int mod = 1000000007; + int n = nums.length; + Arrays.sort(nums); + + int[] pow2 = new int[n]; + pow2[0] = 1; + for (int i = 1; i < n; i++) { + pow2[i] = (pow2[i - 1] * 2) % mod; + } + + int left = 0, right = n - 1; + int count = 0; + + while (left <= right) { + if (nums[left] + nums[right] <= target) { + count = (count + pow2[right - left]) % mod; + left++; + } else { + right--; + } + } + + return count; + } +} diff --git a/java/1503-last-moment-before-all-ants-fall-out-of-a-plank.java b/java/1503-last-moment-before-all-ants-fall-out-of-a-plank.java new file mode 100644 index 000000000..ddf2b48d4 --- /dev/null +++ b/java/1503-last-moment-before-all-ants-fall-out-of-a-plank.java @@ -0,0 +1,12 @@ +class Solution { + public int getLastMoment(int n, int[] left, int[] right) { + Arrays.sort(left); + Arrays.sort(right); + if(left.length == 0 && right.length != 0) + return n-right[0]; + if(left.length != 0 && right.length == 0) + return left[left.length-1]; + + return (int)Math.max(n-right[0], left[left.length-1]); + } +} diff --git a/java/1509-minimum-difference-between-largest-and-smallest-value-in-three-moves.java b/java/1509-minimum-difference-between-largest-and-smallest-value-in-three-moves.java new file mode 100644 index 000000000..f4475d8c4 --- /dev/null +++ b/java/1509-minimum-difference-between-largest-and-smallest-value-in-three-moves.java @@ -0,0 +1,14 @@ +class Solution { + public int minDifference(int[] nums) { + if(nums.length <= 4) + return 0; + + Arrays.sort(nums); + int res = Integer.MAX_VALUE; + for(int l = 0; l < 4; l++){ + int r = nums.length - 4 + l; + res = Math.min(res, nums[r] - nums[l]); + } + return res; + } +} diff --git a/java/1512-number-of-good-pairs.java b/java/1512-number-of-good-pairs.java new file mode 100644 index 000000000..6ee185cb6 --- /dev/null +++ b/java/1512-number-of-good-pairs.java @@ -0,0 +1,56 @@ +/* Brute Force (Method 1) +----------------------------- + Time Complexity: O(n^2) + Space Complexity: O(1) +----------------------------*/ +class Solution { + public int numIdenticalPairs(int[] nums) { + int i=0,j=0,c=0; + for(i=0;i freq = new HashMap<>(); + for(int n: nums) + freq.put(n, freq.getOrDefault(n, 0) + 1); + int res = 0; + for(int c : freq.values()){ + res += c*(c-1)/2; // Combination formula nC2 = n*n(n-1)/2 + } + return res; + } +} + +/* Method 3 +---------------------------- + Time Complexity: O(n) + Space Complexity: O(n) +---------------------------*/ +class Solution { + public int numIdenticalPairs(int[] nums) { + int res = 0; + Map freq = new HashMap<>(); + for(int n : nums){ + if(freq.containsKey(n)){ + res += freq.get(n); + freq.put(n, freq.get(n)+1); + } + else + freq.put(n, 1); + } + return res; + } +} diff --git a/java/1514-path-with-maximum-probability.java b/java/1514-path-with-maximum-probability.java new file mode 100644 index 000000000..a3bdfc427 --- /dev/null +++ b/java/1514-path-with-maximum-probability.java @@ -0,0 +1,74 @@ +class Solution { + public double maxProbability(int n, int[][] edges, double[] succProb, int start, int end) { + // create the graph + List[] graph = new LinkedList[n]; + for (int i = 0; i < n; i++) { + graph[i] = new LinkedList<>(); + } + + for (int i = 0; i < edges.length; i++) { + double from = edges[i][0]; + double to = edges[i][1]; + double weight = succProb[i]; + double[] m = new double[2]; + m[0] = to; + m[1] = weight; + graph[edges[i][0]].add(m); + double[] k = new double[2]; + k[0] = from; + k[1] = weight; + graph[edges[i][1]].add(k); + } + + // call dijkstra and return + return dijkstra(start, end, graph); + } + + class State{ + int id; + double proToStart; + + public State(int id, double proToStart){ + this.id = id; + this.proToStart = proToStart; + } + } + + private double dijkstra(int start, int end, List[] graph){ + double[] proTo = new double[graph.length]; + // 初始化为一个去不到的值 + Arrays.fill(proTo, -1); + proTo[start] = 1; + + PriorityQueue pq = new PriorityQueue((a, b) -> { + return Double.compare(b.proToStart, a.proToStart); + }); + pq.offer(new State(start, 1)); + + while (!pq.isEmpty()){ + State cur = pq.poll(); + int curid = cur.id; + double curproToStart = cur.proToStart; + + if (curid == end) { + return curproToStart; + } + + if (proTo[curid] > curproToStart) { + continue; + } + + List nexts = graph[curid]; + for (double[] next: nexts) { + double proToNext = proTo[curid] * next[1]; + int idx = (int) next[0]; + if (proToNext > proTo[idx]) { + proTo[idx] = proToNext; + pq.offer(new State(idx, proToNext)); + } + } + } + + return 0; + } +} diff --git a/java/1531-string-compression-ii.java b/java/1531-string-compression-ii.java new file mode 100644 index 000000000..834033060 --- /dev/null +++ b/java/1531-string-compression-ii.java @@ -0,0 +1,30 @@ +class Solution { + Map cache; + + public int getLengthOfOptimalCompression(String s, int k) { + cache = new HashMap<>(); + return count(0, k, '\0', 0, s); + } + private int count(int i, int k, char prev, int prev_count, String s){ + String curr_state = i + "," + k + "," + prev + "," + prev_count; + + if(cache.containsKey(curr_state)) + return cache.get(curr_state); + if(k < 0) + return Integer.MAX_VALUE; + if(i == s.length()) + return 0; + + int res = -1; + if(s.charAt(i) == prev){ + int incr = (prev_count == 1 || prev_count == 9 || prev_count == 99)? 1: 0; + res = incr + count(i + 1, k, prev, prev_count + 1, s); + } + else{ + res = Math.min(count(i + 1, k - 1, prev, prev_count, s), + 1 + count(i + 1, k, s.charAt(i), 1, s)); + } + cache.put(curr_state, res); + return res; + } +} diff --git a/java/1545-find-kth-bit-in-nth-binary-string.java b/java/1545-find-kth-bit-in-nth-binary-string.java new file mode 100644 index 000000000..666cf062e --- /dev/null +++ b/java/1545-find-kth-bit-in-nth-binary-string.java @@ -0,0 +1,30 @@ +/*---------------------------- + Time Complexity: O(n) + Space Complexity: O(n) +-----------------------------*/ + +class Solution { + public char findKthBit(int n, int k) { + int length = (int)Math.pow(2, n) - 1; + + return helper(length, k); + } + + private char helper(int length, int k){ + if(length == 1) + return '0'; + + int half = length/2; + + if(k <= half){ + return helper(half, k); + } + else if(k > half + 1){ + char res = helper(half, 1 + length - k); + return (res == '0')? '1': '0'; + } + else{ + return '1'; + } + } +} diff --git a/java/1553-minimum-number-of-days-to-eat-n-oranges.java b/java/1553-minimum-number-of-days-to-eat-n-oranges.java new file mode 100644 index 000000000..9c279a296 --- /dev/null +++ b/java/1553-minimum-number-of-days-to-eat-n-oranges.java @@ -0,0 +1,18 @@ +class Solution { + public int minDays(int n) { + Map dp = new HashMap<>(); + dp.put(0, 0); + dp.put(1, 1); + return dfs(n, dp); + } + + private int dfs(int n, Map dp) { + if (dp.containsKey(n)) { + return dp.get(n); + } + int optionOne = 1 + n % 2 + dfs(n / 2, dp); + int optionTwo = 1 + n % 3 + dfs(n / 3, dp); + dp.put(n, Math.min(optionOne, optionTwo)); + return dp.get(n); + } +} diff --git a/java/1557-minimum-number-of-vertices-to-reach-all-nodes.java b/java/1557-minimum-number-of-vertices-to-reach-all-nodes.java new file mode 100644 index 000000000..a1bd175ae --- /dev/null +++ b/java/1557-minimum-number-of-vertices-to-reach-all-nodes.java @@ -0,0 +1,15 @@ +class Solution { + public List findSmallestSetOfVertices(int n, List> edges) { + int[] incoming = new int[n]; + for(List ls: edges){ + int in = ls.get(1); + incoming[in]++; + } + List res = new ArrayList<>(); + for(int i=0; i visited){ + if(r < 0 || c < 0 || r >= grid.length || c >= grid[r].length || grid[r][c] == 0) + return; + + String pos = r + "," + c; + if(visited.contains(pos)) + return; + + visited.add(pos); + + dfs(grid, r+1, c, visited); + dfs(grid, r, c+1, visited); + dfs(grid, r-1, c, visited); + dfs(grid, r, c-1, visited); + } + + private int count_lands(int[][] grid){ + Set visited = new HashSet<>(); + int count = 0; + + for(int r = 0; r < grid.length; r++){ + for(int c = 0; c < grid[r].length; c++){ + if(grid[r][c] == 1 && !visited.contains(r + "," + c)){ + dfs(grid, r, c, visited); + count += 1; + } + } + } + + return count; + } +} diff --git a/java/1572-matrix-diagonal-sum.java b/java/1572-matrix-diagonal-sum.java new file mode 100644 index 000000000..4157d083c --- /dev/null +++ b/java/1572-matrix-diagonal-sum.java @@ -0,0 +1,13 @@ +class Solution { + public int diagonalSum(int[][] mat) { + int res = 0; + int n = mat.length; + + for (int i = 0; i < n; i++) { + res += mat[i][i]; + res += mat[i][n - 1 - i]; + } + + return res - (n % 2 == 1 ? mat[n / 2][n / 2] : 0); + } +} diff --git a/java/1578-minimum-time-to-make-rope-colorful.java b/java/1578-minimum-time-to-make-rope-colorful.java new file mode 100644 index 000000000..3a54416d7 --- /dev/null +++ b/java/1578-minimum-time-to-make-rope-colorful.java @@ -0,0 +1,23 @@ +/*------------------------------- + Time Complexity: O(n) + Space Complexity: O(1) +--------------------------------*/ +class Solution { + public int minCost(String colors, int[] neededTime) { + int res = 0, l = 0; + + for(int r = 1; r < colors.length(); r++){ + if(colors.charAt(l) == colors.charAt(r)){ + if(neededTime[l] < neededTime[r]){ + res += neededTime[l]; + l = r; + } + else + res += neededTime[r]; + } + else + l = r; + } + return res; + } +} diff --git a/java/1582-special-positions-in-a-binary-matrix.java b/java/1582-special-positions-in-a-binary-matrix.java new file mode 100644 index 000000000..b07eaf003 --- /dev/null +++ b/java/1582-special-positions-in-a-binary-matrix.java @@ -0,0 +1,27 @@ +class Solution { + public int numSpecial(int[][] mat) { + int r = mat.length; + int c = mat[0].length; + int[] rowOnes = new int[r]; + int[] colOnes = new int[c]; + + for(int i = 0; i < r; i++){ + for(int j = 0; j < c; j++){ + if(mat[i][j] == 1){ + rowOnes[i]++; + colOnes[j]++; + } + } + } + + int res = 0; + for(int i = 0; i < r; i++){ + for(int j = 0; j < c; j++){ + if(mat[i][j] == 1 && rowOnes[i] == 1 && colOnes[j] == 1){ + res++; + } + } + } + return res; + } +} diff --git a/java/1584-Min-Cost-to-Connect-All-Points.java b/java/1584-Min-Cost-to-Connect-All-Points.java new file mode 100644 index 000000000..f97cc6e20 --- /dev/null +++ b/java/1584-Min-Cost-to-Connect-All-Points.java @@ -0,0 +1,36 @@ +class Solution { + + // Time Complexity: O(N^2 log(N)) where N is the length of points. N^2 comes from the fact we need to find the distance between a currNode and every other node to pick the shortest distance. log(N) comes from Priority Queue + // Space Complexity: O(N^2) + public int minCostConnectPoints(int[][] points) { + PriorityQueue pq = new PriorityQueue<>((a, b) -> a[0] - b[0]); // edge weight, the index of next node + pq.offer(new int[] { 0, 0 }); + int len = points.length; + Set visited = new HashSet<>(); + int cost = 0; + + // When visited.size() == points.len meaning that all the nodes has been connected. + while (visited.size() < len) { + int[] arr = pq.poll(); + + int weight = arr[0]; + int currNode = arr[1]; + + if (visited.contains(currNode)) continue; + + visited.add(currNode); + cost += weight; + + for (int nextNode = 0; nextNode < len; nextNode++) { + if (!visited.contains(nextNode)) { + int nextWeight = + Math.abs(points[nextNode][0] - points[currNode][0]) + + Math.abs(points[nextNode][1] - points[currNode][1]); + pq.offer(new int[] { nextWeight, nextNode }); + } + } + } + + return cost; + } +} diff --git a/java/1584-min-cost-to-connect-all-points.java b/java/1584-min-cost-to-connect-all-points.java new file mode 100644 index 000000000..f97cc6e20 --- /dev/null +++ b/java/1584-min-cost-to-connect-all-points.java @@ -0,0 +1,36 @@ +class Solution { + + // Time Complexity: O(N^2 log(N)) where N is the length of points. N^2 comes from the fact we need to find the distance between a currNode and every other node to pick the shortest distance. log(N) comes from Priority Queue + // Space Complexity: O(N^2) + public int minCostConnectPoints(int[][] points) { + PriorityQueue pq = new PriorityQueue<>((a, b) -> a[0] - b[0]); // edge weight, the index of next node + pq.offer(new int[] { 0, 0 }); + int len = points.length; + Set visited = new HashSet<>(); + int cost = 0; + + // When visited.size() == points.len meaning that all the nodes has been connected. + while (visited.size() < len) { + int[] arr = pq.poll(); + + int weight = arr[0]; + int currNode = arr[1]; + + if (visited.contains(currNode)) continue; + + visited.add(currNode); + cost += weight; + + for (int nextNode = 0; nextNode < len; nextNode++) { + if (!visited.contains(nextNode)) { + int nextWeight = + Math.abs(points[nextNode][0] - points[currNode][0]) + + Math.abs(points[nextNode][1] - points[currNode][1]); + pq.offer(new int[] { nextWeight, nextNode }); + } + } + } + + return cost; + } +} diff --git a/java/1603-design-parking-system.java b/java/1603-design-parking-system.java new file mode 100644 index 000000000..85e1c3b49 --- /dev/null +++ b/java/1603-design-parking-system.java @@ -0,0 +1,16 @@ +class ParkingSystem { + + int[] space; + + public ParkingSystem(int big, int medium, int small) { + this.space = new int[] { big, medium, small }; + } + + public boolean addCar(int carType) { + if (this.space[carType - 1] == 0) { + return false; + } + this.space[carType - 1]--; + return true; + } +} \ No newline at end of file diff --git a/java/1608-special-array-with-x-elements-greater-than-or-equal-x.java b/java/1608-special-array-with-x-elements-greater-than-or-equal-x.java new file mode 100644 index 000000000..dedaa91db --- /dev/null +++ b/java/1608-special-array-with-x-elements-greater-than-or-equal-x.java @@ -0,0 +1,19 @@ +class Solution { + public int specialArray(int[] nums) { + int[] count = new int[nums.length + 1]; + for (int n : nums) { + int index = n < nums.length ? n : nums.length; + count[index] += 1; + } + + int totalRight = 0; + for (int i = nums.length; i >= 0; i--) { + totalRight += count[i]; + if (i == totalRight) { + return totalRight; + } + } + + return -1; + } +} diff --git a/java/1609-even-odd-tree.java b/java/1609-even-odd-tree.java new file mode 100644 index 000000000..c7a334f6c --- /dev/null +++ b/java/1609-even-odd-tree.java @@ -0,0 +1,36 @@ +/*_____________________________ + Time Complexity: O(n) + Space Complexity: O(n) +_______________________________*/ + +class Solution { + public boolean isEvenOddTree(TreeNode root) { + Queue q = new LinkedList<>(); + q.add(root); + boolean evenLevel = true; + + while(!q.isEmpty()){ + int prev = (evenLevel)? Integer.MIN_VALUE: Integer.MAX_VALUE; + int sz = q.size(); + + for(int i = 0; i < sz; i++){ + TreeNode curr = q.poll(); + int nodeVal = curr.val; + + if((evenLevel && nodeVal % 2 == 0) || (!evenLevel && nodeVal % 2 != 0)) + return false; + if((evenLevel && nodeVal <= prev) || (!evenLevel && nodeVal >= prev)) + return false; + + prev = nodeVal; + + if(curr.left != null) + q.add(curr.left); + if(curr.right != null) + q.add(curr.right); + } + evenLevel = !evenLevel; + } + return true; + } +} diff --git a/java/1611-minimum-one-bit-operations-to-make-integers-zero.java b/java/1611-minimum-one-bit-operations-to-make-integers-zero.java new file mode 100644 index 000000000..991aee4af --- /dev/null +++ b/java/1611-minimum-one-bit-operations-to-make-integers-zero.java @@ -0,0 +1,12 @@ +class Solution { + public int minimumOneBitOperations(int n) { + if(n == 0) + return 0; + + int k = 0; + while((int)Math.pow(2,k) <= n) + k += 1; + k -= 1; + return (int)Math.pow(2,k+1) - 1 - minimumOneBitOperations((int)Math.pow(2,k) ^ n); + } +} diff --git a/java/1624-largest-substring-between-two-equal-characters.java b/java/1624-largest-substring-between-two-equal-characters.java new file mode 100644 index 000000000..f695455ea --- /dev/null +++ b/java/1624-largest-substring-between-two-equal-characters.java @@ -0,0 +1,19 @@ +/*--------------------------- + Time Complexity: O(n) + Space Complexity: O(1) +-----------------------------*/ +class Solution { + public int maxLengthBetweenEqualCharacters(String s) { + Map char_index = new HashMap<>(); + int res = -1; + + for(int i = 0; i < s.length(); i++){ + if(char_index.containsKey(s.charAt(i))){ + res = Math.max(res, i - char_index.get(s.charAt(i)) - 1); + } + else + char_index.put(s.charAt(i), i); + } + return res; + } +} diff --git a/java/1626-best-team-with-no-conflicts.java b/java/1626-best-team-with-no-conflicts.java new file mode 100644 index 000000000..5ca35d115 --- /dev/null +++ b/java/1626-best-team-with-no-conflicts.java @@ -0,0 +1,27 @@ +class Solution { + public int bestTeamScore(int[] scores, int[] ages) { + int[][] pairs = new int[scores.length][scores.length]; + for (int i = 0; i < scores.length; i++) { + pairs[i] = new int[] { scores[i], ages[i] }; + } + Arrays.sort(pairs, (a, b) -> a[0] == b[0] ? a[1] - b[1] : a[0] - b[0]); + int[] dp = new int[pairs.length]; + for (int i = 0; i < pairs.length; i++) { + dp[i] = pairs[i][0]; + } + + for (int i = 0; i < pairs.length; i++) { + int maxScore = pairs[i][0]; + int maxAge = pairs[i][1]; + for (int j = 0; j < i; j++) { + int score = pairs[j][0]; + int age = pairs[j][1]; + if (maxAge >= age) { + dp[i] = Math.max(dp[i], maxScore + dp[j]); + } + } + } + + return Arrays.stream(dp).max().getAsInt(); + } +} diff --git a/java/1630-arithmetic-subarrays.java b/java/1630-arithmetic-subarrays.java new file mode 100644 index 000000000..262b1951d --- /dev/null +++ b/java/1630-arithmetic-subarrays.java @@ -0,0 +1,58 @@ +class Solution { + public List checkArithmeticSubarrays(int[] nums, int[] l, int[] r) { + List res = new ArrayList<>(); + + for(int i = 0; i < l.length; i++){ + int[] subArray = Arrays.copyOfRange(nums, l[i], r[i]+1); + Arrays.sort(subArray); + int diff = subArray[1] - subArray[0]; + boolean isArithmatic = true; + for(int j = 1; j < subArray.length; j++){ + if(subArray[j] - subArray[j-1] != diff){ + isArithmatic = false; + break; + } + } + res.add(isArithmatic); + } + return res; + } + + /* + ****************************************************** + ****************** SECOND SOLUTION ******************* + ****************************************************** + */ + + /** + * Runtime Complexity: O(n * m) + * Space Complexity: O(n) + */ + public List checkArithmeticSubarrays(int[] nums, int[] l, int[] r) { + List res = new ArrayList<>(); + + for (int i = 0; i < l.length; i++) { + res.add(check(nums, l[i], r[i])); + } + + return res; + } + + private boolean check(int[] nums, int l, int r) { + List subArray = new ArrayList<>(); + for (int i = l; i < r + 1; i++) { + subArray.add(nums[i]); + } + Collections.sort(subArray); + + Set diffTrack = new HashSet<>(); + diffTrack.add(Math.abs(subArray.get(0) - subArray.get(1))); + + for (int i = 2; i < subArray.size(); i++) { + int diff = Math.abs(subArray.get(i) - subArray.get(i - 1)); + if (!diffTrack.contains(diff)) + return false; + } + return true; + } +} diff --git a/java/1637-widest-vertical-area-between-two-points-containing-no-points.java b/java/1637-widest-vertical-area-between-two-points-containing-no-points.java new file mode 100644 index 000000000..041f546a2 --- /dev/null +++ b/java/1637-widest-vertical-area-between-two-points-containing-no-points.java @@ -0,0 +1,15 @@ +/*-------------------------------- + Time Complexity: O(nlog(n)) + Space Complexity: O(1) +---------------------------------*/ +class Solution { + public int maxWidthOfVerticalArea(int[][] points) { + Arrays.sort(points, (p1, p2) -> p1[0] - p2[0]); + + int res = 0; + for(int i = 1; i < points.length; i++){ + res = Math.max(res, points[i][0] - points[i-1][0]); + } + return res; + } +} diff --git a/java/1642-furthest-building-you-can-reach.java b/java/1642-furthest-building-you-can-reach.java new file mode 100644 index 000000000..b1ee60f3b --- /dev/null +++ b/java/1642-furthest-building-you-can-reach.java @@ -0,0 +1,43 @@ +/* Max Heap Method + ----------------------------------------*/ +class Solution { + public int furthestBuilding(int[] heights, int bricks, int ladders) { + PriorityQueue pq = new PriorityQueue<>((a, b) -> b - a); + for (int i = 0; i < heights.length - 1; i++) { + int diff = heights[i + 1] - heights[i]; + + if (diff <= 0) + continue; + + bricks -= diff; + pq.add(diff); + + if(bricks < 0){ + if(ladders == 0) + return i; + ladders -= 1; + bricks += pq.poll(); + } + } + return heights.length - 1; + } +} + +/* Min Heap Method + -----------------------------------------------*/ +class Solution { + public int furthestBuilding(int[] heights, int bricks, int ladders) { + PriorityQueue pq = new PriorityQueue<>(); + for(int i = 0; i < heights.length-1; i++){ + int diff = heights[i + 1] - heights[i]; + if(diff < 0) + continue; + pq.add(diff); + if(pq.size() > ladders) + bricks -= pq.poll(); + if(bricks < 0) + return i; + } + return heights.length - 1; + } +} diff --git a/java/1647-minimum-deletions-to-make-character-frequencies-unique.java b/java/1647-minimum-deletions-to-make-character-frequencies-unique.java new file mode 100644 index 000000000..037f30968 --- /dev/null +++ b/java/1647-minimum-deletions-to-make-character-frequencies-unique.java @@ -0,0 +1,20 @@ +class Solution { + public int minDeletions(String s) { + // This Map stores the frequency of characters in String s. + HashMap map = new HashMap<>(); + for(char c: s.toCharArray()){ + map.put(c, map.getOrDefault(c, 0) + 1); + } + // This set stores the frequency of Characters that we encounter + HashSet set = new HashSet<>(); + int res = 0; + for (char c: map.keySet()){ + while (map.get(c) > 0 && set.contains(map.get(c))){ + map.put(c, map.get(c)-1); + res++; + } + set.add(map.get(c)); + } + return res; + } +} diff --git a/java/1657-determine-if-two-strings-are-close.java b/java/1657-determine-if-two-strings-are-close.java new file mode 100644 index 000000000..295d261ee --- /dev/null +++ b/java/1657-determine-if-two-strings-are-close.java @@ -0,0 +1,35 @@ +/*------------------------------- + Time Complexity: O(n) + Space Complexity: O(n) +-------------------------------*/ + +class Solution { + public boolean closeStrings(String word1, String word2) { + if (word1.length() != word2.length()) { + return false; + } + + Map freqMap1 = new HashMap<>(); + Map freqMap2 = new HashMap<>(); + for (char ch : word1.toCharArray()) { + freqMap1.put(ch, freqMap1.getOrDefault(ch, 0) + 1); + } + for (char ch : word2.toCharArray()) { + freqMap2.put(ch, freqMap2.getOrDefault(ch, 0) + 1); + } + if (!freqMap1.keySet().equals(freqMap2.keySet())) { + return false; + } + + Map freqCount1 = new HashMap<>(); + Map freqCount2 = new HashMap<>(); + for (int freq : freqMap1.values()) { + freqCount1.put(freq, freqCount1.getOrDefault(freq, 0) + 1); + } + for (int freq : freqMap2.values()) { + freqCount2.put(freq, freqCount2.getOrDefault(freq, 0) + 1); + } + + return freqCount1.equals(freqCount2); + } +} diff --git a/java/1658-minimum-operations-to-reduce-x-to-zero.java b/java/1658-minimum-operations-to-reduce-x-to-zero.java new file mode 100644 index 000000000..347465d84 --- /dev/null +++ b/java/1658-minimum-operations-to-reduce-x-to-zero.java @@ -0,0 +1,20 @@ +class Solution { + public int minOperations(int[] nums, int x) { + int arrSum = IntStream.of(nums).sum(); + int target = arrSum - x, cur_sum = 0, max_win = -1, l = 0; + + for (int r = 0; r < nums.length; r++) { + cur_sum += nums[r]; + + while (l <= r && cur_sum > target) { + cur_sum -= nums[l]; + l++; + } + + if (cur_sum == target) + max_win = Math.max(max_win, r - l + 1); + } + + return max_win == -1 ? -1 : nums.length - max_win; + } +} \ No newline at end of file diff --git a/java/1669-merge-in-between-linked-lists.java b/java/1669-merge-in-between-linked-lists.java new file mode 100644 index 000000000..995f654b1 --- /dev/null +++ b/java/1669-merge-in-between-linked-lists.java @@ -0,0 +1,22 @@ +class Solution { + public ListNode mergeInBetween(ListNode list1, int a, int b, ListNode list2) { + ListNode curr = list1; + int i = 0; + while (i < a - 1) { + curr = curr.next; + i++; + } + + ListNode head = curr; + while (i <= b) { + curr = curr.next; + i++; + } + head.next = list2; + + while (list2.next != null) + list2 = list2.next; + list2.next = curr; + return list1; + } +} diff --git a/java/1688-count-of-matches-in-tournament.java b/java/1688-count-of-matches-in-tournament.java new file mode 100644 index 000000000..a274b279e --- /dev/null +++ b/java/1688-count-of-matches-in-tournament.java @@ -0,0 +1,31 @@ +/*------------------------------ + Time Complexity: O(log(n)) + Space Complexity: O(1) +-------------------------------*/ +class Solution { + public int numberOfMatches(int n) { + int res = 0; + + while(n > 1){ + if(n % 2 == 0){ + res += n/2; + n /= 2; + } + else{ + res += (n - 1)/2; + n = (n - 1)/2 + 1; + } + } + return res; + } +} + +/*------------------------------ + Time Complexity: O(1) + Space Complexity: O(1) +-------------------------------*/ +class Solution { + public int numberOfMatches(int n) { + return n - 1; + } +} diff --git a/java/1716-calculate-money-in-leetcode-bank.java b/java/1716-calculate-money-in-leetcode-bank.java new file mode 100644 index 000000000..4e4f65efe --- /dev/null +++ b/java/1716-calculate-money-in-leetcode-bank.java @@ -0,0 +1,41 @@ +/*------------------------------- + Time complexity: O(1) + Space Complexity: O(1) +-------------------------------*/ +class Solution { + public int totalMoney(int n) { + int weeks = n / 7;; + int low = 28; + int high = 28 + 7 * (weeks - 1); + int res = (weeks * (low + high) / 2); + + int monday = weeks + 1; + for(int i = 0; i < n % 7; i++) + res += i + monday; + + return res; + } +} + +/*------------------------------- + Time complexity: O(n) + Space Complexity: O(1) +-------------------------------*/ +class Solution { + public int totalMoney(int n) { + int day = 0; + int deposit = 1; + int res = 0; + + while(day < n){ + res += deposit; + deposit += 1; + day += 1; + + if(day % 7 == 0) + deposit = 1 + day/7; + } + return res; + } +} + diff --git a/java/1721-swapping-nodes-in-a-linked-list.java b/java/1721-swapping-nodes-in-a-linked-list.java new file mode 100644 index 000000000..b88cf1025 --- /dev/null +++ b/java/1721-swapping-nodes-in-a-linked-list.java @@ -0,0 +1,24 @@ +/*---------------------------- + Time Complexity : O(n) + Space Complexity : O(1) +----------------------------*/ +class Solution { + public ListNode swapNodes(ListNode head, int k) { + ListNode cur = head; + for(int i = 0; i < k-1; i++) + cur = cur.next; + + ListNode left = cur; + ListNode right = head; + while(cur.next != null) { + cur = cur.next; + right = right.next; + } + + int temp = left.val; + left.val = right.val; + right.val = temp; + + return head; + } +} diff --git a/java/1743-restore-the-array-from-adjacent-pairs.java b/java/1743-restore-the-array-from-adjacent-pairs.java new file mode 100644 index 000000000..dda4e1c34 --- /dev/null +++ b/java/1743-restore-the-array-from-adjacent-pairs.java @@ -0,0 +1,30 @@ +class Solution { + public static int[] restoreArray(int[][] adjacentPairs) { + Map> g = new HashMap<>(); + for(int[] p: adjacentPairs){ + int a = p[0], b = p[1]; + g.computeIfAbsent(a,val -> new ArrayList<>()).add(b); + g.computeIfAbsent(b,val -> new ArrayList<>()).add(a); + } + int source = 0; + for(int node: g.keySet()){ + if(g.get(node).size() == 1){ + source = node; + break; + } + } + int[] res = new int[adjacentPairs.length+1]; + Set visited = new HashSet<>(); + dfs(g, source, res, visited, 0); + return res; + } + private static void dfs(Map> g, int node, int[] res, Set visited, int i){ + res[i++] = node; + visited.add(node); + + for(int ne: g.get(node)){ + if(!visited.contains(ne)) + dfs(g, ne, res, visited, i); + } + } +} diff --git a/java/1750-minimum-length-of-string-after-deleting-similar-ends.java b/java/1750-minimum-length-of-string-after-deleting-similar-ends.java new file mode 100644 index 000000000..d8247f1e4 --- /dev/null +++ b/java/1750-minimum-length-of-string-after-deleting-similar-ends.java @@ -0,0 +1,16 @@ +class Solution { + public int minimumLength(String s) { + int l = 0; + int r = s.length() - 1; + + while (l < r && s.charAt(l) == s.charAt(r)) { + char temp = s.charAt(l); + while (l <= r && s.charAt(l) == temp) + l++; + while (l <= r && s.charAt(r) == temp) + r--; + } + + return (r - l + 1); + } +} diff --git a/java/1758-minimum-changes-to-make-alternating-binary-string.java b/java/1758-minimum-changes-to-make-alternating-binary-string.java new file mode 100644 index 000000000..0e6d30cca --- /dev/null +++ b/java/1758-minimum-changes-to-make-alternating-binary-string.java @@ -0,0 +1,13 @@ +class Solution { + public int minOperations(String s) { + int count = 0; + + for(int i = 0; i < s.length(); i++){ + if(i % 2 == 1) + count += (s.charAt(i) == '0')? 1: 0; + else + count += (s.charAt(i) == '1')? 1: 0; + } + return Math.min(count, s.length() - count); + } +} diff --git a/java/1768-merge-strings-alternately.java b/java/1768-merge-strings-alternately.java new file mode 100644 index 000000000..b80609586 --- /dev/null +++ b/java/1768-merge-strings-alternately.java @@ -0,0 +1,23 @@ +class Solution { + public String mergeAlternately(String word1, String word2) { + int i = 0; + StringBuilder res = new StringBuilder(); + + while (i < word1.length() || i < word2.length()) { + if (i < word1.length()) { + res.append(word1.charAt(i)); + } + if (i < word2.length()) { + res.append(word2.charAt(i)); + } + i++; + } + + return res.toString(); + } +} + +/** + * Time Complexity : O(n+m) + * Space Complexity : O(n+m) + */ \ No newline at end of file diff --git a/java/1822-sign-of-the-product-of-an-array.java b/java/1822-sign-of-the-product-of-an-array.java new file mode 100644 index 000000000..dff3b2060 --- /dev/null +++ b/java/1822-sign-of-the-product-of-an-array.java @@ -0,0 +1,10 @@ +class Solution { + public int arraySign(int[] nums) { + int sign = 1; + for (int i : nums) { + if (i == 0) return 0; + if (i < 0) sign *= -1; + } + return sign; + } +} \ No newline at end of file diff --git a/java/1834-single-threaded-cpu.java b/java/1834-single-threaded-cpu.java new file mode 100644 index 000000000..c0a0be6f3 --- /dev/null +++ b/java/1834-single-threaded-cpu.java @@ -0,0 +1,45 @@ +class Solution { + public int[] getOrder(int[][] tasks) { + + // Sort based on min task processing time or min task index. + PriorityQueue nextTask = new PriorityQueue((a, b) -> (a[1] != b[1] ? (a[1] - b[1]) : (a[2] - b[2]))); + + // Store task enqueue time, processing time, index. + int sortedTasks[][] = new int[tasks.length][3]; + for (int i = 0; i < tasks.length; ++i) { + sortedTasks[i][0] = tasks[i][0]; + sortedTasks[i][1] = tasks[i][1]; + sortedTasks[i][2] = i; + } + + // Sort the tasks based on enqueueTime + Arrays.sort(sortedTasks, (a, b) -> Integer.compare(a[0], b[0])); + int tasksProcessingOrder[] = new int[tasks.length]; + + long currTime = 0; + int taskIndex = 0; + int ansIndex = 0; + + // Stop when no tasks are left in array and heap. + while (taskIndex < tasks.length || !nextTask.isEmpty()) { + if (nextTask.isEmpty() && currTime < sortedTasks[taskIndex][0]) { + // When the heap is empty, try updating currTime to next task's enqueue time. + currTime = sortedTasks[taskIndex][0]; + } + + // Push all the tasks whose enqueueTime <= currtTime into the heap. + while (taskIndex < tasks.length && currTime >= sortedTasks[taskIndex][0]) { + nextTask.add(sortedTasks[taskIndex]); + ++taskIndex; + } + + int[] temp = nextTask.poll(); + + // Complete this task and increment currTime. + currTime += temp[1]; + tasksProcessingOrder[ansIndex++] = temp[2]; + } + + return tasksProcessingOrder; + } +} diff --git a/java/1838-frequency-of-the-most-frequent-element.java b/java/1838-frequency-of-the-most-frequent-element.java new file mode 100644 index 000000000..7c8fb4386 --- /dev/null +++ b/java/1838-frequency-of-the-most-frequent-element.java @@ -0,0 +1,18 @@ +class Solution { + public int maxFrequency(int[] nums, int k) { + Arrays.sort(nums); + int left = 0, currSum = 0, res = 0; + + for(int right = 0; right < nums.length; right++){ + int target = nums[right]; + currSum += nums[right]; + + while((right - left + 1)*target - currSum > k){ + currSum -= nums[left]; + left++; + } + res = Math.max(res, right - left + 1); + } + return res; + } +} diff --git a/java/1846-maximum-element-after-decreasing-and-rearranging.java b/java/1846-maximum-element-after-decreasing-and-rearranging.java new file mode 100644 index 000000000..28c9895fc --- /dev/null +++ b/java/1846-maximum-element-after-decreasing-and-rearranging.java @@ -0,0 +1,9 @@ +class Solution { + public int maximumElementAfterDecrementingAndRearranging(int[] arr) { + Arrays.sort(arr); + int prev = 0; + for(int n: arr) + prev = Math.min(prev + 1, n); + return prev; + } +} diff --git a/java/1849-splitting-a-string-into-descending-consecutive-values.java b/java/1849-splitting-a-string-into-descending-consecutive-values.java new file mode 100644 index 000000000..739726cd1 --- /dev/null +++ b/java/1849-splitting-a-string-into-descending-consecutive-values.java @@ -0,0 +1,32 @@ +import java.math.BigInteger; + +class Solution { + public boolean splitString(String s) { + for (int i = 0; i < s.length() - 1; i++) { + BigInteger curValue = new BigInteger(s.substring(0, i + 1)); + if (backtrack(i + 1, s, curValue)) { + return true; + } + } + + return false; + } + + private boolean backtrack(int startIndex, + String s, + BigInteger lastValue) { + if (startIndex >= s.length()) { + return true; + } + + for (int i = startIndex; i < s.length(); i++) { + BigInteger curValue = new BigInteger(s.substring(startIndex, i + 1)); + if (lastValue.subtract(curValue).compareTo(BigInteger.ONE) == 0 && + backtrack(i + 1, s, curValue)) { + return true; + } + } + + return false; + } +} diff --git a/java/1851-Minimum-Interval-to-Include-Each-Query.java b/java/1851-Minimum-Interval-to-Include-Each-Query.java new file mode 100644 index 000000000..e1a99ce15 --- /dev/null +++ b/java/1851-Minimum-Interval-to-Include-Each-Query.java @@ -0,0 +1,92 @@ +class Query { + + int index; + int queryTimeStamp; + int result; + + public Query(int index, int queryTimeStamp) { + this.index = index; + this.queryTimeStamp = queryTimeStamp; + this.result = -1; // initially store as -1 + } + + @Override + public String toString() { + return "[" + index + "," + queryTimeStamp + "," + result + "]"; + } + + public void setResult(int result) { + this.result = result; + } +} + +class IntervalComparator implements Comparator { + + public static int getSize(int[] interval) { + return (interval[1] - interval[0] + 1); + } + + @Override + public int compare(int[] o1, int[] o2) { + int o1Size = getSize(o1), o2Size = getSize(o2); + if (o1Size != o2Size) { + return (o1Size - o2Size); + } + return (o1[1] - o2[1]); + } +} + +class Solution { + + public int[] minInterval(int[][] intervals, int[] queries) { + // book-keeping & sorting + int numIntervals = intervals.length; + int numQueries = queries.length; + + // Sort by start times + Arrays.sort(intervals, (o1, o2) -> (o1[0] - o2[0])); + + Query[] sortedQueries = new Query[numQueries]; + for (int i = 0; i < numQueries; i++) sortedQueries[i] = + new Query(i, queries[i]); + + Arrays.sort( + sortedQueries, + (q1, q2) -> (q1.queryTimeStamp - q2.queryTimeStamp) + ); + + // algorithm + + Comparator comparator = new IntervalComparator(); + PriorityQueue pq = new PriorityQueue<>(comparator); + int idx = 0; + + for (Query query : sortedQueries) { + // 1. Keep taking all those queries which have lower starting time than the query time and add them to priority queue + while ( + (idx < numIntervals) && + (query.queryTimeStamp >= intervals[idx][0]) + ) { + pq.add(intervals[idx]); + idx++; + } + + // 2. Keep removing the inconsistent intervals and get the min size interval from priority queue + while (!pq.isEmpty() && (pq.peek()[1] < query.queryTimeStamp)) { + pq.remove(); + } + + // Now, priority queue must have the consistent & smallest interval + int ans = pq.isEmpty() ? -1 : IntervalComparator.getSize(pq.peek()); + query.setResult(ans); + } + + // reconversion + int[] results = new int[numQueries]; + for (Query query : sortedQueries) { + results[query.index] = query.result; + } + + return results; + } +} diff --git a/java/1851-minimum-interval-to-include-each-query.java b/java/1851-minimum-interval-to-include-each-query.java new file mode 100644 index 000000000..e1a99ce15 --- /dev/null +++ b/java/1851-minimum-interval-to-include-each-query.java @@ -0,0 +1,92 @@ +class Query { + + int index; + int queryTimeStamp; + int result; + + public Query(int index, int queryTimeStamp) { + this.index = index; + this.queryTimeStamp = queryTimeStamp; + this.result = -1; // initially store as -1 + } + + @Override + public String toString() { + return "[" + index + "," + queryTimeStamp + "," + result + "]"; + } + + public void setResult(int result) { + this.result = result; + } +} + +class IntervalComparator implements Comparator { + + public static int getSize(int[] interval) { + return (interval[1] - interval[0] + 1); + } + + @Override + public int compare(int[] o1, int[] o2) { + int o1Size = getSize(o1), o2Size = getSize(o2); + if (o1Size != o2Size) { + return (o1Size - o2Size); + } + return (o1[1] - o2[1]); + } +} + +class Solution { + + public int[] minInterval(int[][] intervals, int[] queries) { + // book-keeping & sorting + int numIntervals = intervals.length; + int numQueries = queries.length; + + // Sort by start times + Arrays.sort(intervals, (o1, o2) -> (o1[0] - o2[0])); + + Query[] sortedQueries = new Query[numQueries]; + for (int i = 0; i < numQueries; i++) sortedQueries[i] = + new Query(i, queries[i]); + + Arrays.sort( + sortedQueries, + (q1, q2) -> (q1.queryTimeStamp - q2.queryTimeStamp) + ); + + // algorithm + + Comparator comparator = new IntervalComparator(); + PriorityQueue pq = new PriorityQueue<>(comparator); + int idx = 0; + + for (Query query : sortedQueries) { + // 1. Keep taking all those queries which have lower starting time than the query time and add them to priority queue + while ( + (idx < numIntervals) && + (query.queryTimeStamp >= intervals[idx][0]) + ) { + pq.add(intervals[idx]); + idx++; + } + + // 2. Keep removing the inconsistent intervals and get the min size interval from priority queue + while (!pq.isEmpty() && (pq.peek()[1] < query.queryTimeStamp)) { + pq.remove(); + } + + // Now, priority queue must have the consistent & smallest interval + int ans = pq.isEmpty() ? -1 : IntervalComparator.getSize(pq.peek()); + query.setResult(ans); + } + + // reconversion + int[] results = new int[numQueries]; + for (Query query : sortedQueries) { + results[query.index] = query.result; + } + + return results; + } +} diff --git a/java/1856-maximum-subarray-min-product.java b/java/1856-maximum-subarray-min-product.java new file mode 100644 index 000000000..3365d820d --- /dev/null +++ b/java/1856-maximum-subarray-min-product.java @@ -0,0 +1,31 @@ +class Solution { + public int maxSumMinProduct(int[] nums) { + Stack stack = new Stack<>(); // index, value + long[] pre = new long[nums.length]; + pre[0] = nums[0]; + long res = 0, mod = (int) 1e9 + 7; + for (int i = 1; i < nums.length; i++) { + pre[i] = pre[i - 1] + nums[i]; + } + for (int i = 0; i < nums.length; i++) { + int idx = i; + while (stack.size() != 0 && nums[i] < stack.peek()[1]) { + long[] t = stack.pop(); + int start = (int) t[0]; + long value = t[1]; + long sum = pre[i - 1] - ((start - 1) < 0 ? 0 : pre[start - 1]); + res = Math.max(res, (value * sum)); + idx = start; + } + stack.push(new long[] { idx, nums[i] }); + } + while (stack.size() != 0) { + long[] t = stack.pop(); + int start = (int) t[0]; + long value = t[1]; + long sum = pre[nums.length - 1] - ((start - 1) < 0 ? 0 : pre[start - 1]); + res = Math.max(res, (value * sum)); + } + return (int) ((res + mod) % mod); + } +} \ No newline at end of file diff --git a/java/1877-minimize-maximum-pair-sum-in-array.java b/java/1877-minimize-maximum-pair-sum-in-array.java new file mode 100644 index 000000000..65fa28b40 --- /dev/null +++ b/java/1877-minimize-maximum-pair-sum-in-array.java @@ -0,0 +1,15 @@ +/*-------------------------------- + Time Complexity: O(n) + Space Complexity: O(1) +---------------------------------*/ +class Solution { + public int minPairSum(int[] nums) { + Arrays.sort(nums); + int res = Integer.MIN_VALUE; + + for(int i = 0, j = nums.length-1; i < j; i++,j--){ + res = Math.max(res, nums[i]+nums[j]); + } + return res; + } +} diff --git a/java/1882-process-tasks-using-servers.java b/java/1882-process-tasks-using-servers.java new file mode 100644 index 000000000..ddf4181a4 --- /dev/null +++ b/java/1882-process-tasks-using-servers.java @@ -0,0 +1,36 @@ +class Solution { + public int[] assignTasks(int[] servers, int[] tasks) { + int[] res = new int[tasks.length]; + + // [weight, index] + Queue available = new PriorityQueue<>((a, b) -> a[0] == b[0] ? a[1] - b[1] : a[0] - b[0]); + for (int i = 0; i < servers.length; i++) + available.add(new int[] { servers[i], i }); + + int time = 0; + int nextTask = 0; + // [weight, index, done time] + Queue unavail = new PriorityQueue<>((a, b) -> a[2] - b[2]); + while (nextTask < tasks.length) { + // release available servers + while (!unavail.isEmpty() && unavail.peek()[2] <= time) { + int[] curr = unavail.poll(); + available.add(new int[] { curr[0], curr[1] }); + } + + // assign task(s) + while (!available.isEmpty() && nextTask < time && nextTask != tasks.length) { + int[] curr = available.poll(); + unavail.add(new int[] { curr[0], curr[1], time + tasks[nextTask] }); + res[nextTask++] = curr[1]; + } + + // advance time + if (available.isEmpty()) + time = unavail.peek()[2]; + else + time++; + } + return res; + } +} \ No newline at end of file diff --git a/java/1887-reduction-operations-to-make-the-array-elements-equal.java b/java/1887-reduction-operations-to-make-the-array-elements-equal.java new file mode 100644 index 000000000..3c74badb9 --- /dev/null +++ b/java/1887-reduction-operations-to-make-the-array-elements-equal.java @@ -0,0 +1,17 @@ +/*---------------------------------- + Time Complexity: O(n*log(n)) + Space Complexity: O(1) +-----------------------------------*/ + +class Solution { + public int reductionOperations(int[] nums) { + Arrays.sort(nums); + int res = 0; + + for(int i = 1; i < nums.length; i++){ + if(nums[i] != nums[i-1]) + res += nums.length - i; + } + return res; + } +} diff --git a/java/1897-redistribute-characters-to-make-all-strings-equal.java b/java/1897-redistribute-characters-to-make-all-strings-equal.java new file mode 100644 index 000000000..3b7e608e6 --- /dev/null +++ b/java/1897-redistribute-characters-to-make-all-strings-equal.java @@ -0,0 +1,17 @@ +class Solution { + public boolean makeEqual(String[] words) { + Map map = new HashMap<>(); + + for(String s: words){ + for(char c: s.toCharArray()){ + map.put(c, map.getOrDefault(c, 0) + 1); + } + } + int n = words.length; + for(char c: map.keySet()){ + if(map.get(c) % n != 0) + return false; + } + return true; + } +} diff --git a/java/1898-maximum-number-of-removable-characters.java b/java/1898-maximum-number-of-removable-characters.java new file mode 100644 index 000000000..4d5f7ff16 --- /dev/null +++ b/java/1898-maximum-number-of-removable-characters.java @@ -0,0 +1,36 @@ +class Solution { + + public int maximumRemovals(String s, String p, int[] removable) { + int left =0; + int right = removable.length -1; + int result =0; + while(left <= right){ + HashSet removed = new HashSet<>(); + int mid = (left+right)/2; + for(int i =0; i <= mid; i++){ // populate the hashset + removed.add(removable[i]); + } + if(isSubseq(s,p,removed)){ + result = Math.max(result,mid+1); + left = mid+1; // greedy try to find even a higher value + }else{ + right = mid -1; + } + } + return result; + } + + private boolean isSubseq(String s , String subseq,HashSet removed){ + int i1 =0; + int i2 =0; + while(i1 < s.length() && i2 < subseq.length()){ + if( s.charAt(i1) != subseq.charAt(i2) || removed.contains(i1) ){ + i1++; + }else{ + i1++; + i2++; + } + } + return i2 == subseq.length(); + } +} \ No newline at end of file diff --git a/java/1899-merge-triplets-to-form-target-triplet.java b/java/1899-merge-triplets-to-form-target-triplet.java new file mode 100644 index 000000000..fdb0c2a4b --- /dev/null +++ b/java/1899-merge-triplets-to-form-target-triplet.java @@ -0,0 +1,16 @@ +class Solution { + + public boolean mergeTriplets(int[][] triplets, int[] target) { + boolean[] greedy = new boolean[3]; + loop:for (int[] triplet : triplets) { + for (int i = 0; i < 3; i++) if ( + triplet[i] > target[i] + ) continue loop; + + for (int i = 0; i < 3; i++) if (triplet[i] == target[i]) greedy[i] = + true; + } + + return greedy[0] && greedy[1] && greedy[2]; + } +} diff --git a/java/1903-largest-odd-number-in-string.java b/java/1903-largest-odd-number-in-string.java new file mode 100644 index 000000000..f937452b6 --- /dev/null +++ b/java/1903-largest-odd-number-in-string.java @@ -0,0 +1,12 @@ +class Solution { + public String largestOddNumber(String num) { + String res = ""; + for(int i = num.length()-1; i >= 0; i--){ + if(num.charAt(i) % 2 != 0){ + res = num.substring(0, i+1); + break; + } + } + return res; + } +} diff --git a/java/1905-count-sub-islands.java b/java/1905-count-sub-islands.java new file mode 100644 index 000000000..b8c4cf8e1 --- /dev/null +++ b/java/1905-count-sub-islands.java @@ -0,0 +1,42 @@ +class Solution { + class RecursiveBiFunction { + BiFunction func; + } + + public int countSubIslands(final int[][] grid1, final int[][] grid2) { + final int ROWS = grid1.length, COLS = grid1[0].length; + final Set visit = new HashSet<>(); + + final RecursiveBiFunction dfs = new RecursiveBiFunction<>(); + dfs.func = (r, c) -> { + int flatCoord = r*COLS + c; + if( + r < 0 + || c < 0 + || r == ROWS + || c == COLS + || grid2[r][c] == 0 + || visit.contains(flatCoord) + ) + return true; + + visit.add(flatCoord); + boolean res = true; + if(grid1[r][c] == 0) + res = false; + + res = dfs.func.apply(r - 1, c) && res; + res = dfs.func.apply(r + 1, c) && res; + res = dfs.func.apply(r, c - 1) && res; + res = dfs.func.apply(r, c + 1) && res; + return res; + }; + + int count = 0; + for(int r = 0; r < ROWS; r++) + for(int c = 0; c < COLS; c++) + if(grid2[r][c] != 0 && !visit.contains(r*COLS + c) && dfs.func.apply(r, c)) + count += 1; + return count; + } +} diff --git a/java/1913-maximum-product-difference-between-two-pairs.java b/java/1913-maximum-product-difference-between-two-pairs.java new file mode 100644 index 000000000..d706ca6da --- /dev/null +++ b/java/1913-maximum-product-difference-between-two-pairs.java @@ -0,0 +1,42 @@ +/*------------------------------- + Time Complexity: O(n) + Space Complexity: O(1) +-------------------------------*/ +class Solution { + public int maxProductDifference(int[] nums) { + int max1 = 0, max2 = 0; + int min1 = Integer.MAX_VALUE, min2 = Integer.MAX_VALUE; + + for(int n : nums){ + if(n > max2){ + if(n > max1){ + max2 = max1; + max1 = n; + } + else + max2 = n; + } + if(n < min2){ + if(n < min1){ + min2 = min1; + min1 = n; + } + else + min2 = n; + } + } + return (max1 * max2) - (min1 * min2); + } +} + +/*------------------------------------ + Time Complexity: O(nlog(n)) + Space Complexity: O(1) +-------------------------------------*/ +class Solution { + public int maxProductDifference(int[] nums) { + Arrays.sort(nums); + int res = (nums[nums.length-1]*nums[nums.length-2]) - (nums[0]*nums[1]); + return res; + } +} diff --git a/java/1921-eliminate-maximum-number-of-monsters.java b/java/1921-eliminate-maximum-number-of-monsters.java new file mode 100644 index 000000000..a331fcdfc --- /dev/null +++ b/java/1921-eliminate-maximum-number-of-monsters.java @@ -0,0 +1,18 @@ +class Solution { + public int eliminateMaximum(int[] dist, int[] speed) { + int n = dist.length; + double[] arrivalTime = new double[n]; + for(int i = 0; i < n; i++){ + arrivalTime[i] = (double) dist[i] / speed[i]; + } + Arrays.sort(arrivalTime); + int res = 0; + + for(int i = 0; i < n; i++){ + if(arrivalTime[i] <= i) + break; + res++; + } + return res; + } +} diff --git a/java/1929-concatenation-of-array.java b/java/1929-concatenation-of-array.java new file mode 100644 index 000000000..d72d95db7 --- /dev/null +++ b/java/1929-concatenation-of-array.java @@ -0,0 +1,10 @@ +class Solution { + public int[] getConcatenation(int[] nums) { + int[] ans = new int[nums.length*2]; + for(int i=0;i left = new HashSet<>(); + Map right = new HashMap<>(); + for(char c : s.toCharArray()) { + right.put(c, right.getOrDefault(c, 0) + 1); + } + Set res = new HashSet<>(); + + for(int mid = 0; mid < s.length(); mid++) { + char c = s.charAt(mid); + + right.put(c, right.get(c)-1); + if(right.get(c) == 0) { + right.remove(c); + } + + for(int i=0; i<26; i++) { + if(left.contains(letters[i]) && right.containsKey(letters[i])) { + res.add("" + letters[i] + c + letters[i]); + } + } + + left.add(c); + } + + return res.size(); + } +} +/* Alternative solution +------------------------------------------------------------------------*/ +class Solution { + public int countPalindromicSubsequence(String s) { + Map map = new HashMap<>(); + for(char c: s.toCharArray()) + map.put(c, map.getOrDefault(c, 0) + 1); + + int res = 0; + for(char c : map.keySet()){ + if (map.get(c) >= 2){ + int fo = s.indexOf(c), lo = s.lastIndexOf(c); + Set between = new HashSet<>(); + for(int i = fo+1; i < lo; i++) + between.add(s.charAt(i)); + res += between.size(); + } + } + return res; + } +} diff --git a/java/1958-check-if-move-is-legal.java b/java/1958-check-if-move-is-legal.java new file mode 100644 index 000000000..da828ebc1 --- /dev/null +++ b/java/1958-check-if-move-is-legal.java @@ -0,0 +1,29 @@ +class Solution { + public boolean checkMove(char[][] board, int rMove, int cMove, char color) { + int[][] direction = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}, + {1, 1}, {-1, -1}, {1, -1}, {-1, 1}}; + board[rMove][cMove] = color; + + for(int[] d: direction) + if(legal(board, rMove, cMove, color, d)) return true; + return false; + } + + boolean legal(char[][] board, int row, int col, char color, int[] direc) { + int ROWS = board.length, COLS = board[0].length; + int dr = direc[0], dc = direc[1]; + row = row + dr; + col = col + dc; + int length = 1; + + while(0 <= row && row < ROWS && 0 <= col && col < COLS) { + length += 1; + if(board[row][col] == '.') return false; + if(board[row][col] == color) + return length >= 3; + row = row + dr; + col = col + dc; + } + return false; + } +} diff --git a/java/1963-minimum-number-of-swaps-to-make-the-string-balanced.java b/java/1963-minimum-number-of-swaps-to-make-the-string-balanced.java new file mode 100644 index 000000000..9f73cdbd8 --- /dev/null +++ b/java/1963-minimum-number-of-swaps-to-make-the-string-balanced.java @@ -0,0 +1,15 @@ +class Solution { + public int minSwaps(String s) { + int extraClosingbrackets = 0, max = 0; + for(int i = 0; i < s.length(); i++) { + if(s.charAt(i)== ']') { + extraClosingbrackets++; + max = Math.max(extraClosingbrackets, max); + } + else + extraClosingbrackets--; + } + + return (max + 1) / 2; + } +} diff --git a/java/1968-array-with-elements-not-equal-to-average-of-neighbors.java b/java/1968-array-with-elements-not-equal-to-average-of-neighbors.java new file mode 100644 index 000000000..049ea894a --- /dev/null +++ b/java/1968-array-with-elements-not-equal-to-average-of-neighbors.java @@ -0,0 +1,42 @@ +class Solution { + + // Two pass Solution + public int[] rearrangeArray(int[] nums) { + + Arrays.sort(nums); + int n = nums.length; + int[] result = new int[n]; + + int median = 0; + if (n % 2 == 0) { + median = (nums[n / 2 - 1] + nums[n / 2]) / 2; + } else { + median = nums[n / 2]; + } + + int j = 0; + for (int i = 0; i < n; i += 2) { + result[i] = nums[j++]; + } + + for (int i = 1; i < n; i += 2) { + result[i] = nums[j++]; + } + + return result; + } +} + +// One Pass Solution + public int[] rearrangeArray(int[] nums) { + for(int i=1; i <= nums.length-2; i++){ + if( ( ( nums[i-1] < nums[i] && nums[i] < nums[i+1] ) || ( nums[i-1] > nums[i] && nums[i] > nums[i+1] ) )){ //swap + int temp = nums[i+1]; + nums[i+1] = nums[i]; + nums[i] = temp; + } + } + return nums; + } +} + diff --git a/java/1980-find-unique-binary-string.java b/java/1980-find-unique-binary-string.java new file mode 100644 index 000000000..d360ac603 --- /dev/null +++ b/java/1980-find-unique-binary-string.java @@ -0,0 +1,39 @@ +class Solution { + /** + * If k is the length of the String and there are n Strings + * Time Complexity = O(2^k) + * Space Complexity = n*k (for set) + k (for the current sequence) + */ + public String findDifferentBinaryString(String[] nums) { + Set uniqueNums = Set.of(nums); + return helper(uniqueNums, uniqueNums.size(), new StringBuffer()); + } + + String helper(Set uniqueStr, int size, StringBuffer currentSeq) { + //Check if current sequence has reached to required length + if (currentSeq.length() == size) { + //Check if current sequence exists in the provided list , we can keep track of it as global flag too + if (!uniqueStr.contains(currentSeq.toString())) { + return currentSeq.toString(); + } + //current sequence is not unique + return null; + } + + //Only if current sequence length is smaller than required length + currentSeq.append("0"); + String result = helper(uniqueStr, size, currentSeq); + currentSeq.deleteCharAt(currentSeq.length() - 1); + + //Check if we can find an ans with "0" + if (result != null) { + return result; + } + //If appending "0" didn't work then try with "1" + currentSeq.append("1"); + result = helper(uniqueStr, size, currentSeq); + currentSeq.deleteCharAt(currentSeq.length() - 1); + + return result; + } +} \ No newline at end of file diff --git a/java/1984-minimum-difference-between-highest-and-lowest-of-k-scores.java b/java/1984-minimum-difference-between-highest-and-lowest-of-k-scores.java new file mode 100644 index 000000000..e8aa531ee --- /dev/null +++ b/java/1984-minimum-difference-between-highest-and-lowest-of-k-scores.java @@ -0,0 +1,15 @@ +class Solution { + public int minimumDifference(int[] nums, int k) { + if(k == 1) return 0; + + Arrays.sort(nums); + + int min = Integer.MAX_VALUE; + for(int i=0; i { + @Override + public int compare(String s1, String s2) { + if(s1.length() != s2.length()) { + return (s1.length() - s2.length()); + } + int len = s1.length(); + for(int i=0; i pq = new PriorityQueue<>(new StringNumberComparartor()); + + for(String numStr: nums) { + pq.add(numStr); + if(pq.size() > k) { + pq.remove(); + } + } + + return pq.peek(); + } +} \ No newline at end of file diff --git a/java/1985-find-the-kth-largest-integer-in-the-array.java b/java/1985-find-the-kth-largest-integer-in-the-array.java new file mode 100644 index 000000000..a32c8f80e --- /dev/null +++ b/java/1985-find-the-kth-largest-integer-in-the-array.java @@ -0,0 +1,32 @@ +// Min Heap comparator for reverse priority queue +class StringNumberComparartor implements Comparator { + @Override + public int compare(String s1, String s2) { + if(s1.length() != s2.length()) { + return (s1.length() - s2.length()); + } + int len = s1.length(); + for(int i=0; i pq = new PriorityQueue<>(new StringNumberComparartor()); + + for(String numStr: nums) { + pq.add(numStr); + if(pq.size() > k) { + pq.remove(); + } + } + + return pq.peek(); + } +} \ No newline at end of file diff --git a/java/1993-operations-on-tree.java b/java/1993-operations-on-tree.java new file mode 100644 index 000000000..43921ab3b --- /dev/null +++ b/java/1993-operations-on-tree.java @@ -0,0 +1,79 @@ +class Solution { + private final int[] parent; + private final int[] locked; + private final Map> child; + + public LockingTree(int[] parent) { + this.parent = parent; + locked = new int[parent.length]; + child = new HashMap<>(); + + for (int i = 0; i < parent.length; i++) { + child.putIfAbsent(parent[i], new ArrayList<>()); + child.putIfAbsent(i, new ArrayList<>()); + child.get(parent[i]).add(i); + } + } + + public boolean lock(int num, int user) { + if (locked[num] <= 0) { + locked[num] = user; + return true; + } + + return false; + } + + public boolean unlock(int num, int user) { + if (locked[num] == user) { + locked[num] = 0; + return true; + } + + return false; + } + + public boolean upgrade(int num, int user) { + if (!noneAncestorsLocked(num)) { + return false; + } + + int lockedCount = checkDescendantsAndLockIfNeeded(num); + + if (lockedCount > 0) { + locked[num] = user; + } + + return lockedCount > 0; + } + + private boolean noneAncestorsLocked(int num) { + while (num != -1) { + if (locked[num] != 0) { + return false; + } + num = parent[num]; + } + + return true; + } + + private int checkDescendantsAndLockIfNeeded(int num) { + int lockedCount = 0; + Deque deque = new ArrayDeque<>(); + + deque.addFirst(num); + while (!deque.isEmpty()) { + int n = deque.pollLast(); + if (locked[n] > 0) { + lockedCount++; + locked[n] = 0; + } + for (Integer i : child.get(n)) { + deque.addFirst(i); + } + } + + return lockedCount; + } +} diff --git a/java/2001-number-of-pairs-of-interchangeable-rectangles.java b/java/2001-number-of-pairs-of-interchangeable-rectangles.java new file mode 100644 index 000000000..c87414528 --- /dev/null +++ b/java/2001-number-of-pairs-of-interchangeable-rectangles.java @@ -0,0 +1,15 @@ +class Solution { + public long interchangeableRectangles(int[][] rectangles) { + Map count = new HashMap<>(); + for (int[] rec : rectangles) { + double key = (double) rec[0] / rec[1]; + count.put(key, count.getOrDefault(key, (long) 0) + 1); + } + + long res = 0; + for (long c : count.values()) { + res += c * (c - 1) / 2; + } + return res; + } +} \ No newline at end of file diff --git a/java/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.java b/java/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.java new file mode 100644 index 000000000..3c27b2387 --- /dev/null +++ b/java/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.java @@ -0,0 +1,27 @@ +class Solution { + public int maxProduct(String s) { + int[] dp = new int[4096]; + int res = 0, mask = (1 << s.length()) - 1; + for (int m = 1; m <= mask; ++m) + dp[m] = palSize(s, m); + for (int m1 = mask; m1 > 0; --m1) + if (dp[m1] * (s.length() - dp[m1]) > res) + for(int m2 = mask ^ m1; m2 > 0; m2 = (m2 - 1) & (mask ^ m1)) + res = Math.max(res, dp[m1] * dp[m2]); + return res; +} +private int palSize(String s, int mask) { + int p1 = 0, p2 = s.length(), res = 0; + while (p1 <= p2) { + if ((mask & (1 << p1)) == 0) + ++p1; + else if ((mask & (1 << p2)) == 0) + --p2; + else if (s.charAt(p1) != s.charAt(p2)) + return 0; + else + res += 1 + (p1++ != p2-- ? 1 : 0); + } + return res; +} +} \ No newline at end of file diff --git a/java/2009-minimum-number-of-operations-to-make-array-continuous.java b/java/2009-minimum-number-of-operations-to-make-array-continuous.java new file mode 100644 index 000000000..c612c4d62 --- /dev/null +++ b/java/2009-minimum-number-of-operations-to-make-array-continuous.java @@ -0,0 +1,26 @@ +class Solution { + public int minOperations(int[] nums) { + int length = nums.length; + Set set = new HashSet<>(); + + for(int num : nums) + set.add(num); + + int[] newNums = new int[set.size()]; + int idx = 0; + for(int num : set) + newNums[idx++] = num; + + Arrays.sort(newNums); + int res = length; + int r = 0; + + for(int l = 0; l < newNums.length; l++){ + while(r < newNums.length && newNums[r] < newNums[l] + length) + r += 1; + int window = r - l; + res = Math.min(res, length - window); + } + return res; + } +} diff --git a/java/2013-Detect-Squares.java b/java/2013-Detect-Squares.java new file mode 100644 index 000000000..d6af2d55d --- /dev/null +++ b/java/2013-Detect-Squares.java @@ -0,0 +1,110 @@ +// https://leetcode.com/submissions/detail/761120641/ +class DetectSquares { + + private Integer[][] matrix; + + public DetectSquares() { + matrix = new Integer[1001][1001]; + } + + public void add(int[] point) { + if (matrix[point[0]][point[1]] == null) { + matrix[point[0]][point[1]] = 1; + } else { + matrix[point[0]][point[1]] = matrix[point[0]][point[1]] + 1; + } + } + + public int count(int[] point) { + int currentSquareCount = 0; + int currentPointCount = 1; + int startRow = point[0]; + int startCol = point[1]; + int curRow = point[0]; + int curCol = point[1]; + + while (curRow != 0 && curCol != 0) { + curRow--; + curCol--; + if ( + matrix[curRow][curCol] != null && + matrix[startRow][curCol] != null && + matrix[curRow][startCol] != null + ) { + currentSquareCount = + currentSquareCount + + ( + currentPointCount * + matrix[curRow][curCol] * + matrix[startRow][curCol] * + matrix[curRow][startCol] + ); + } + } + + curRow = point[0]; + curCol = point[1]; + while (curRow != 1000 && curCol != 1000) { + curRow++; + curCol++; + if ( + matrix[curRow][curCol] != null && + matrix[startRow][curCol] != null && + matrix[curRow][startCol] != null + ) { + currentSquareCount = + currentSquareCount + + ( + currentPointCount * + matrix[curRow][curCol] * + matrix[startRow][curCol] * + matrix[curRow][startCol] + ); + } + } + + curRow = point[0]; + curCol = point[1]; + while (curRow != 0 && curCol != 1000) { + curRow--; + curCol++; + if ( + matrix[curRow][curCol] != null && + matrix[startRow][curCol] != null && + matrix[curRow][startCol] != null + ) { + currentSquareCount = + currentSquareCount + + ( + currentPointCount * + matrix[curRow][curCol] * + matrix[startRow][curCol] * + matrix[curRow][startCol] + ); + } + } + + curRow = point[0]; + curCol = point[1]; + while (curRow != 1000 && curCol != 0) { + curRow++; + curCol--; + if ( + matrix[curRow][curCol] != null && + matrix[startRow][curCol] != null && + matrix[curRow][startCol] != null + ) { + currentSquareCount = + currentSquareCount + + ( + currentPointCount * + matrix[curRow][curCol] * + matrix[startRow][curCol] * + matrix[curRow][startCol] + ); + } + } + + return currentSquareCount; + } +} diff --git a/java/2013-detect-squares.java b/java/2013-detect-squares.java new file mode 100644 index 000000000..d6af2d55d --- /dev/null +++ b/java/2013-detect-squares.java @@ -0,0 +1,110 @@ +// https://leetcode.com/submissions/detail/761120641/ +class DetectSquares { + + private Integer[][] matrix; + + public DetectSquares() { + matrix = new Integer[1001][1001]; + } + + public void add(int[] point) { + if (matrix[point[0]][point[1]] == null) { + matrix[point[0]][point[1]] = 1; + } else { + matrix[point[0]][point[1]] = matrix[point[0]][point[1]] + 1; + } + } + + public int count(int[] point) { + int currentSquareCount = 0; + int currentPointCount = 1; + int startRow = point[0]; + int startCol = point[1]; + int curRow = point[0]; + int curCol = point[1]; + + while (curRow != 0 && curCol != 0) { + curRow--; + curCol--; + if ( + matrix[curRow][curCol] != null && + matrix[startRow][curCol] != null && + matrix[curRow][startCol] != null + ) { + currentSquareCount = + currentSquareCount + + ( + currentPointCount * + matrix[curRow][curCol] * + matrix[startRow][curCol] * + matrix[curRow][startCol] + ); + } + } + + curRow = point[0]; + curCol = point[1]; + while (curRow != 1000 && curCol != 1000) { + curRow++; + curCol++; + if ( + matrix[curRow][curCol] != null && + matrix[startRow][curCol] != null && + matrix[curRow][startCol] != null + ) { + currentSquareCount = + currentSquareCount + + ( + currentPointCount * + matrix[curRow][curCol] * + matrix[startRow][curCol] * + matrix[curRow][startCol] + ); + } + } + + curRow = point[0]; + curCol = point[1]; + while (curRow != 0 && curCol != 1000) { + curRow--; + curCol++; + if ( + matrix[curRow][curCol] != null && + matrix[startRow][curCol] != null && + matrix[curRow][startCol] != null + ) { + currentSquareCount = + currentSquareCount + + ( + currentPointCount * + matrix[curRow][curCol] * + matrix[startRow][curCol] * + matrix[curRow][startCol] + ); + } + } + + curRow = point[0]; + curCol = point[1]; + while (curRow != 1000 && curCol != 0) { + curRow++; + curCol--; + if ( + matrix[curRow][curCol] != null && + matrix[startRow][curCol] != null && + matrix[curRow][startCol] != null + ) { + currentSquareCount = + currentSquareCount + + ( + currentPointCount * + matrix[curRow][curCol] * + matrix[startRow][curCol] * + matrix[curRow][startCol] + ); + } + } + + return currentSquareCount; + } +} diff --git a/java/2017-grid-game.java b/java/2017-grid-game.java new file mode 100644 index 000000000..f3b5a6175 --- /dev/null +++ b/java/2017-grid-game.java @@ -0,0 +1,30 @@ +class Solution { + public long gridGame(int[][] grid) { + long[] prefix_top = new long[grid[0].length]; + long[] prefix_bottom = new long[grid[0].length]; + for(int i = 0; i < grid[0].length; i++) + { + prefix_top[i] = grid[0][i]; + prefix_bottom[i] = grid[1][i]; + } + for(int i = 1; i < grid[0].length; i++) + { + prefix_top[i] += prefix_top[i - 1]; + prefix_bottom[i] += prefix_bottom[i - 1]; + } + long res = Long.MAX_VALUE; + long maxi = Long.MIN_VALUE; + for(int i = 0; i < grid[0].length; i++) + { + long top = prefix_top[grid[0].length - 1] - prefix_top[i]; + long bottom = 0; + if(i > 0) + { + bottom = prefix_bottom[i - 1]; + } + maxi = Math.max(top, bottom); + res = Math.min(res, maxi); + } + return res; + } +} diff --git a/java/2038-remove-colored-pieces-if-both-neighbors-are-the-same-color.java b/java/2038-remove-colored-pieces-if-both-neighbors-are-the-same-color.java new file mode 100644 index 000000000..517e43eef --- /dev/null +++ b/java/2038-remove-colored-pieces-if-both-neighbors-are-the-same-color.java @@ -0,0 +1,19 @@ +/*-------------------------- + Time Complexity : O(n) + Space Complexity : O(1) +----------------------------*/ + +class Solution { + public boolean winnerOfGame(String colors) { + int alice = 0, bob = 0; + for(int i = 1; i < colors.length()-1; i++){ + if(colors.charAt(i) == 'A' && colors.charAt(i-1) == 'A' && colors.charAt(i+1) == 'A'){ + alice++; + } + else if(colors.charAt(i) == 'B' && colors.charAt(i-1) == 'B' && colors.charAt(i+1) == 'B'){ + bob++; + } + } + return (alice > bob); + } +} diff --git a/java/2050-parallel-courses-iii.java b/java/2050-parallel-courses-iii.java new file mode 100644 index 000000000..0b614e386 --- /dev/null +++ b/java/2050-parallel-courses-iii.java @@ -0,0 +1,32 @@ +public class Solution { + public int minimumTime(int n, int[][] relations, int[] time) { + Map> adj = new HashMap<>(); + for (int[] relation : relations) { + int src = relation[0]; + int dst = relation[1]; + adj.putIfAbsent(src, new ArrayList<>()); + adj.get(src).add(dst); + } + + Map maxTime = new HashMap<>(); + + for (int i = 1; i <= n; i++) { + dfs(i, time, adj, maxTime); + } + + return maxTime.values().stream().max(Integer::compareTo).get(); + } + + private int dfs(int src, int[] time, Map> adj, Map maxTime) { + if (maxTime.containsKey(src)) { + return maxTime.get(src); + } + + int res = time[src - 1]; + for (int nei : adj.getOrDefault(src, new ArrayList<>())) { + res = Math.max(res, time[src - 1] + dfs(nei, time, adj, maxTime)); + } + maxTime.put(src, res); + return res; + } +} diff --git a/java/2092-find-all-people-with-secret.java b/java/2092-find-all-people-with-secret.java new file mode 100644 index 000000000..936813c35 --- /dev/null +++ b/java/2092-find-all-people-with-secret.java @@ -0,0 +1,46 @@ +public class Solution { + public List findAllPeople(int n, int[][] meetings, int firstPerson) { + Set secrets = new HashSet<>(Arrays.asList(0, firstPerson)); + Map>> timeMap = new HashMap<>(); + + for (int[] meeting : meetings) { + int src = meeting[0], dst = meeting[1], time = meeting[2]; + timeMap.putIfAbsent(time, new HashMap<>()); + timeMap.get(time).putIfAbsent(src, new ArrayList<>()); + timeMap.get(time).putIfAbsent(dst, new ArrayList<>()); + timeMap.get(time).get(src).add(dst); + timeMap.get(time).get(dst).add(src); + } + + for (Integer time : new TreeSet<>(timeMap.keySet())) { + Set visit = new HashSet<>(); + for (Integer src : timeMap.get(time).keySet()) { + if (secrets.contains(src)) { + dfs(src, timeMap.get(time), visit, secrets); + } + } + } + + return new ArrayList<>(secrets); + } + + private void dfs(int src, Map> adj, Set visit, Set secrets) { + if (visit.contains(src)) { + return; + } + visit.add(src); + secrets.add(src); + for (Integer nei : adj.get(src)) { + dfs(nei, adj, visit, secrets); + } + } + + public static void main(String[] args) { + Solution solution = new Solution(); + int n = 5; + int[][] meetings = { { 0, 1, 2 }, { 1, 2, 3 }, { 2, 3, 4 }, { 4, 5, 5 } }; + int firstPerson = 1; + List result = solution.findAllPeople(n, meetings, firstPerson); + System.out.println(result); + } +} diff --git a/java/2108-find-first-palindromic-string-in-the-array.java b/java/2108-find-first-palindromic-string-in-the-array.java new file mode 100644 index 000000000..6fa9bab72 --- /dev/null +++ b/java/2108-find-first-palindromic-string-in-the-array.java @@ -0,0 +1,19 @@ +public class Solution { + + public String firstPalindrome(String[] words) { + for (int i = 0; i < words.length; i++) { + String word = words[i]; + int x = 0, y = word.length() - 1; + while (word.charAt(x) == word.charAt(y)) { + if (x >= y) + return word; + else { + x++; + y--; + } + } + } + return ""; + } + +} diff --git a/java/2125-number-of-laser-beams-in-a-bank.java b/java/2125-number-of-laser-beams-in-a-bank.java new file mode 100644 index 000000000..976a01f70 --- /dev/null +++ b/java/2125-number-of-laser-beams-in-a-bank.java @@ -0,0 +1,21 @@ +class Solution { + public int numberOfBeams(String[] bank) { + int prev = countOnes(bank[0]); + int res = 0; + + for(int i = 1; i < bank.length; i++){ + int curr = countOnes(bank[i]); + if(curr > 0){ + res += curr*prev; + prev = curr; + } + } + return res; + } + private int countOnes(String str){ + int res = 0; + for(char c: str.toCharArray()) + res += (c == '1')? 1: 0; + return res; + } +} diff --git a/java/2130-maximum-twin-sum-of-a-linked-list.java b/java/2130-maximum-twin-sum-of-a-linked-list.java new file mode 100644 index 000000000..a016be51d --- /dev/null +++ b/java/2130-maximum-twin-sum-of-a-linked-list.java @@ -0,0 +1,39 @@ +//The basic idea is to use the slow and fast pointers to find the mid +// Once found we will reverse the second half of the Linkedlist and compare the values + +class Solution { + public ListNode reverse(ListNode head){ + if(head == null) return head; + + ListNode curr = head; + ListNode prev = null; + + while(curr != null){ + ListNode nxt = curr.next; + curr.next = prev; + prev = curr; + curr = nxt; + } + return prev; + } + public int pairSum(ListNode head) { + int mx = Integer.MIN_VALUE; + + ListNode slow=head, fast=head; + + while(fast != null && fast.next != null){ + fast = fast.next.next; + slow = slow.next; + } + + ListNode prev = reverse(slow); + + ListNode head1 = head, head2 = prev; + while(head2 != null){ + mx = Math.max(mx, head1.val+head2.val); + head1 = head1.next; + head2 = head2.next; + } + return mx; + } +} diff --git a/java/2147-number-of-ways-to-divide-a-long-corridor.java b/java/2147-number-of-ways-to-divide-a-long-corridor.java new file mode 100644 index 000000000..99a034880 --- /dev/null +++ b/java/2147-number-of-ways-to-divide-a-long-corridor.java @@ -0,0 +1,19 @@ +class Solution { + public int numberOfWays(String corridor) { + int MOD = (int)1e9 + 7; + List seatIdx = new ArrayList<>(); + + for(int i = 0; i < corridor.length(); i++){ + if(corridor.charAt(i) == 'S') + seatIdx.add(i); + } + if(seatIdx.size() == 0 || seatIdx.size()%2 == 1) + return 0; + + long res = 1; + for(int i = 2; i < seatIdx.size(); i += 2){ + res = res*(seatIdx.get(i) - seatIdx.get(i-1)) % MOD; + } + return (int)res; + } +} diff --git a/java/2149-rearrange-array-elements-by-signs.java b/java/2149-rearrange-array-elements-by-signs.java new file mode 100644 index 000000000..147e4fcf3 --- /dev/null +++ b/java/2149-rearrange-array-elements-by-signs.java @@ -0,0 +1,16 @@ +public class Solution { + public int[] rearrangeArray(int[] nums) { + int i = 0, j = 1; + int[] res = new int[nums.length]; + for (int k = 0; k < nums.length; k++) { + if (nums[k] > 0) { + res[i] = nums[k]; + i += 2; + } else { + res[j] = nums[k]; + j += 2; + } + } + return res; + } +} \ No newline at end of file diff --git a/java/2215-find-the-difference-of-two-arrays.java b/java/2215-find-the-difference-of-two-arrays.java new file mode 100644 index 000000000..064a57357 --- /dev/null +++ b/java/2215-find-the-difference-of-two-arrays.java @@ -0,0 +1,32 @@ +class Solution { + public List> findDifference(int[] nums1, int[] nums2) { + List> res = new ArrayList<>(); + + List num1 = new ArrayList<>(); + for(int n: nums1) num1.add(n); + + List num2 = new ArrayList<>(); + for(int n: nums2) num2.add(n); + + Set set1 = new HashSet<>(); + Set set2 = new HashSet<>(); + + //Because we need to return result in 2 list of the result list, therefore we are creating these placeholders + res.add(new ArrayList<>()); + res.add(new ArrayList<>()); + + for(int i=0; i> findWinners(int[][] matches) { + Map map = new TreeMap<>(); // Store player as key and loses as value + for(int[] arr: matches){ + int winner = arr[0], loser = arr[1]; + map.put(winner, map.getOrDefault(winner, 0)); + map.put(loser, map.getOrDefault(loser, 0) + 1); + } + + List> res = new ArrayList<>(); + res.add(new ArrayList<>()); + res.add(new ArrayList<>()); + for(int player: map.keySet()){ + int loses = map.get(player); + if(loses == 0) + res.get(0).add(player); + else if(loses == 1) + res.get(1).add(player); + } + return res; + } +} diff --git a/java/2251-number-of-flowers-in-full-bloom.java b/java/2251-number-of-flowers-in-full-bloom.java new file mode 100644 index 000000000..7cf6d9403 --- /dev/null +++ b/java/2251-number-of-flowers-in-full-bloom.java @@ -0,0 +1,37 @@ +class Solution { + public int[] fullBloomFlowers(int[][] flowers, int[] people) { + List starts = new ArrayList<>(); + List ends = new ArrayList<>(); + + for(int[] flwr: flowers){ + starts.add(flwr[0]); + ends.add(flwr[1] + 1); + } + + Collections.sort(starts); + Collections.sort(ends); + int[] res = new int[people.length]; + + for(int i = 0; i < people.length; i++){ + int person = people[i]; + int startBlooming = BinarySearch(starts, person); + int stopBlooming = BinarySearch(ends, person); + res[i] = startBlooming - stopBlooming; + } + return res; + } + + private int BinarySearch(List ls, int target){ + int l = 0; + int r = ls.size(); + + while(l < r){ + int m = l + (r-l)/2; + if(target < ls.get(m)) + r = m; + else + l = m + 1; + } + return l; + } +} diff --git a/java/2264-largest-3-same-digit-number-in-string.java b/java/2264-largest-3-same-digit-number-in-string.java new file mode 100644 index 000000000..8b2f67952 --- /dev/null +++ b/java/2264-largest-3-same-digit-number-in-string.java @@ -0,0 +1,12 @@ +class Solution { + public String largestGoodInteger(String num) { + String res = ""; + + for(int i = 0; i <= num.length()-3; i++){ + String curr = num.substring(i, i+3); + if(curr.charAt(0) == curr.charAt(1) && curr.charAt(0) == curr.charAt(2)) + res = (curr.compareTo(res) > 0) ? curr: res; + } + return res; + } +} diff --git a/java/2300-successful-pairs-of-spells-and-potions.java b/java/2300-successful-pairs-of-spells-and-potions.java new file mode 100644 index 000000000..6d8cf543c --- /dev/null +++ b/java/2300-successful-pairs-of-spells-and-potions.java @@ -0,0 +1,18 @@ +class Solution { + public int[] successfulPairs( + int[] spells, int[] potions, long success) { + int [] res = new int[spells.length]; + Arrays.sort(potions); + for(int i=0;i=success) r=m-1; + else l=m+1; + } + res[i] = (l> map = new HashMap<>(); + for (String s : ideas) { + char key = s.charAt(0); + String val = s.substring(1, s.length()); + Set set = map.getOrDefault(key, new HashSet<>()); + set.add(val); + map.put(key, set); + } + + long res = 0; + for (char key1 : map.keySet()) { + Set set1 = map.get(key1); + for (char key2 : map.keySet()) { + if (key1 == key2) continue; + Set set2 = map.get(key2); + int overlap = 0; + for (String s : set1) { + if (set2.contains(s)) overlap++; + } + res += (set1.size() - overlap) * (set2.size() - overlap); + } + } + return res; + } +} diff --git a/java/2309-greatest-english-letter-in-upper-and-lower-case.java b/java/2309-greatest-english-letter-in-upper-and-lower-case.java new file mode 100644 index 000000000..1a51362e0 --- /dev/null +++ b/java/2309-greatest-english-letter-in-upper-and-lower-case.java @@ -0,0 +1,16 @@ +//Contest 298 brute-force feel free to share any improvements +class Solution { + + public String greatestLetter(String s) { + HashSet set = new HashSet<>(); + int ans = -1; + for (int i = 0; i < s.length(); i++) { + if ( + set.contains((s.charAt(i) - 32)) || + set.contains((s.charAt(i) + 32)) + ) ans = Math.max(ans, (int) Character.toUpperCase(s.charAt(i))); + set.add(0 + s.charAt(i)); + } + return ans == -1 ? "" : (char) ans + ""; + } +} diff --git a/java/2310-sum-of-numbers-with-units-digit-k.java b/java/2310-sum-of-numbers-with-units-digit-k.java new file mode 100644 index 000000000..80400f80e --- /dev/null +++ b/java/2310-sum-of-numbers-with-units-digit-k.java @@ -0,0 +1,20 @@ +//Contest 298 problem brute-force. Feel free to update this. +class Solution { + + public int minimumNumbers(int num, int k) { + if (num == 0) return 0; + if (num % 10 == k) return 1; + if (k == 0 && num % 10 == 0) return 1; else if ( + k == 0 && num % 10 != 0 + ) return -1; + int temp = num; + num -= k; + int len = 1; + while (num > 0) { + len++; + if (num % 10 == k) return len; + num -= k; + } + return num < 0 ? -1 : len; + } +} diff --git a/java/2328-number-of-increasing-paths-in-a-grid.java b/java/2328-number-of-increasing-paths-in-a-grid.java new file mode 100644 index 000000000..0ab6219a7 --- /dev/null +++ b/java/2328-number-of-increasing-paths-in-a-grid.java @@ -0,0 +1,47 @@ +//Exactly similar to : 329. Longest Increasing Path in a Matrix + +class Solution { + + long mod = (long) Math.pow(10, 9) + 7; + + public int countPaths(int[][] grid) { + int m = grid.length, n = grid[0].length; + long[][] dp = new long[m][n]; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (dp[i][j] == 0) { + dfs(grid, dp, i, j, m, n, -1); + } + } + } + long sum = 0; + for (long[] d : dp) { + for (long i : d) { + sum = (sum + i) % mod; + } + } + return (int) sum; + } + + public long dfs( + int[][] grid, + long[][] dp, + int i, + int j, + int m, + int n, + int prevValue + ) { + if ( + i < 0 || j < 0 || i >= m || j >= n || prevValue >= grid[i][j] + ) return 0; + if (dp[i][j] != 0) return dp[i][j]; + prevValue = grid[i][j]; + long left = dfs(grid, dp, i, j - 1, m, n, prevValue) % mod; + long bottom = dfs(grid, dp, i + 1, j, m, n, prevValue) % mod; + long right = dfs(grid, dp, i, j + 1, m, n, prevValue) % mod; + long top = dfs(grid, dp, i - 1, j, m, n, prevValue) % mod; + dp[i][j] = (1 + left + top + bottom + right) % mod; + return dp[i][j]; + } +} diff --git a/java/2348-number-of-zero-filled-subarrays.java b/java/2348-number-of-zero-filled-subarrays.java new file mode 100644 index 000000000..763edf481 --- /dev/null +++ b/java/2348-number-of-zero-filled-subarrays.java @@ -0,0 +1,12 @@ +class Solution { + public long zeroFilledSubarray(int[] nums) { + long res = 0; + long count = 0; + for (int i : nums) { + if (i != 0) count = 0; + else count++; + res += count; + } + return res; + } +} \ No newline at end of file diff --git a/java/2385-amount-of-time-for-binary-tree-to-be-infected.java b/java/2385-amount-of-time-for-binary-tree-to-be-infected.java new file mode 100644 index 000000000..e5bd94b05 --- /dev/null +++ b/java/2385-amount-of-time-for-binary-tree-to-be-infected.java @@ -0,0 +1,56 @@ +/*-------------------------- + Time Complexity: O(n) + Space Complexity: O(n) +---------------------------*/ +class Solution { + public int amountOfTime(TreeNode root, int start) { + Map> g = treeTograph(root); + int time = 0; + Queue q = new LinkedList<>(); + Set visited = new HashSet<>(); + q.add(start); + visited.add(start); + + while(!q.isEmpty()){ + int size = q.size(); + for (int i = 0; i < size; i++) { + int curr = q.poll(); + for (int neighbour : g.get(curr)) { + if (!visited.contains(neighbour)) { + q.add(neighbour); + visited.add(neighbour); + } + } + } + time++; + } + return time-1; + } + public HashMap> treeTograph(TreeNode root) { + HashMap> graph = new HashMap<>(); + buildGraph(root, graph); + return graph; + } + + private void buildGraph(TreeNode node, HashMap> graph) { + if (node == null) { + return; + } + + graph.putIfAbsent(node.val, new ArrayList<>()); + + if (node.left != null) { + graph.get(node.val).add(node.left.val); + graph.putIfAbsent(node.left.val, new ArrayList<>()); + graph.get(node.left.val).add(node.val); + buildGraph(node.left, graph); + } + + if (node.right != null) { + graph.get(node.val).add(node.right.val); + graph.putIfAbsent(node.right.val, new ArrayList<>()); + graph.get(node.right.val).add(node.val); + buildGraph(node.right, graph); + } + } +} diff --git a/java/2390-removing-stars-from-a-string.java b/java/2390-removing-stars-from-a-string.java new file mode 100644 index 000000000..e05ac2d8a --- /dev/null +++ b/java/2390-removing-stars-from-a-string.java @@ -0,0 +1,29 @@ +class Solution { + public String removeStars(String s) { + + // Create a new stack to keep track of characters encountered so far + Stack stk = new Stack<>(); + + // Iterate over each character in the input string + for (char c : s.toCharArray()) { + // If the current character is a star, + // remove the topmost character from the stack + if (c == '*') { + stk.pop(); + } + // If the current character is not a star, add it to the stack + else { + stk.push(c); + } + } + + // StringBuilder to store the characters in the stack + StringBuilder sb = new StringBuilder(); + for (char c : stk) { + sb.append(c); + } + + // Convert the StringBuilder to a string and return it as the output + return sb.toString(); + } +} diff --git a/java/2391-minimum-amount-of-time-to-collect-garbage.java b/java/2391-minimum-amount-of-time-to-collect-garbage.java new file mode 100644 index 000000000..a40eee377 --- /dev/null +++ b/java/2391-minimum-amount-of-time-to-collect-garbage.java @@ -0,0 +1,26 @@ +/*---------------------------------- + Time Complexity: O(n) + Space Complexity: O(1) +-----------------------------------*/ + +class Solution { + public int garbageCollection(String[] garbage, int[] travel) { + int[] idx = new int[3]; + int res = 0; + for(int i = 0; i < garbage.length; i++){ + res += garbage[i].length(); + if(garbage[i].contains("G")) + idx[0] = i; + if(garbage[i].contains("P")) + idx[1] = i; + if(garbage[i].contains("M")) + idx[2] = i; + } + for(int i: idx){ + for(int j = 0; j < i; j++){ + res += travel[j]; + } + } + return res; + } +} diff --git a/java/2405-optimal-partition-of-string.java b/java/2405-optimal-partition-of-string.java new file mode 100644 index 000000000..3fbe051a2 --- /dev/null +++ b/java/2405-optimal-partition-of-string.java @@ -0,0 +1,14 @@ +class Solution { + public int partitionString(String s) { + Set set = new HashSet<>(); + int res = 1; + for (char ch : s.toCharArray()) { + if (set.contains(ch)) { + set = new HashSet<>(); + res++; + } + set.add(ch); + } + return res; + } +} \ No newline at end of file diff --git a/java/2433-find-the-original-array-of-prefix-xor.java b/java/2433-find-the-original-array-of-prefix-xor.java new file mode 100644 index 000000000..a731300e2 --- /dev/null +++ b/java/2433-find-the-original-array-of-prefix-xor.java @@ -0,0 +1,12 @@ +class Solution { + public int[] findArray(int[] pref) { + int n = pref.length; + int[] arr = new int[n]; + arr[0] = pref[0]; + + for(int i = 1; i < n; i++) + arr[i] = pref[i]^pref[i-1]; + + return arr; + } +} diff --git a/java/2477-minimum-fuel-cost-to-report-to-the-capital.java b/java/2477-minimum-fuel-cost-to-report-to-the-capital.java new file mode 100644 index 000000000..fda61365d --- /dev/null +++ b/java/2477-minimum-fuel-cost-to-report-to-the-capital.java @@ -0,0 +1,29 @@ +class Solution { + long res = 0; + HashMap> g = new HashMap<>(); + public long minimumFuelCost(int[][] roads, int seats) { + for(int[] e : roads){ + int a = e[0], b = e[1]; + g.computeIfAbsent(a, val -> new ArrayList<>()).add(b); + g.computeIfAbsent(b, val -> new ArrayList<>()).add(a); + } + if (g.size() == 0) + return 0; + + dfs(0, -1, seats); + return res; + } + + private int dfs(int node, int parent, int seats){ + int passengers = 0; + + for(int child : g.get(node)){ + if(child != parent){ + int p = dfs(child, node, seats); + passengers += p; + res += (int) Math.ceil((double)p/seats); + } + } + return passengers + 1; + } +} diff --git a/java/2482-difference-between-ones-and-zeros-in-row-and-column.java b/java/2482-difference-between-ones-and-zeros-in-row-and-column.java new file mode 100644 index 000000000..456fff22e --- /dev/null +++ b/java/2482-difference-between-ones-and-zeros-in-row-and-column.java @@ -0,0 +1,23 @@ +class Solution { + public int[][] onesMinusZeros(int[][] grid) { + int r = grid.length, c = grid[0].length; + int[] onesRow = new int[r]; + int[] onesCol = new int[c]; + + for(int i = 0; i < r; i++){ + for(int j = 0; j < c; j++){ + onesRow[i] += grid[i][j]; + onesCol[j] += grid[i][j]; + } + } + + int[][] res = new int[r][c]; + for(int i = 0; i < r; i++){ + for(int j = 0; j < c; j++){ + int no = 2 * onesRow[i] + 2 * onesCol[j] - r - c; + res[i][j] = no; + } + } + return res; + } +} diff --git a/java/2483-minimum-penalty-for-a-shop.java b/java/2483-minimum-penalty-for-a-shop.java new file mode 100644 index 000000000..4e88d1ffc --- /dev/null +++ b/java/2483-minimum-penalty-for-a-shop.java @@ -0,0 +1,29 @@ +class Solution { + public int bestClosingTime(String cust) { + int n = cust.length(); + int[] pre_n = new int[n+1]; + int[] post_y = new int[n+1]; + + for(int i = 1; i <= n; i++){ + pre_n[i] = pre_n[i-1]; + if(cust.charAt(i-1) == 'N') + pre_n[i]++; + } + for(int i = n-1; i >= 0; i--){ + post_y[i] = post_y[i+1]; + if(cust.charAt(i) == 'Y') + post_y[i]++; + } + + int min_penalty = Integer.MAX_VALUE, idx = 0; + + for(int i = 0; i <= n; i++){ + int penalty = pre_n[i] + post_y[i]; + if(penalty < min_penalty){ + min_penalty = penalty; + idx = i; + } + } + return idx; + } +} diff --git a/java/2486-append-characters-to-string-to-make-subsequence.java b/java/2486-append-characters-to-string-to-make-subsequence.java new file mode 100644 index 000000000..ebcf80cfc --- /dev/null +++ b/java/2486-append-characters-to-string-to-make-subsequence.java @@ -0,0 +1,12 @@ +class Solution { + public int appendCharacters(String s, String t) { + int i = 0, j = 0; + while(i < s.length() && j < t.length()){ + if(s.charAt(i) == t.charAt(j)){ + j++; + } + i++; + } + return t.length() - j; + } +} diff --git a/java/2544-alternating-digit-sum b/java/2544-alternating-digit-sum new file mode 100644 index 000000000..d8763e66d --- /dev/null +++ b/java/2544-alternating-digit-sum @@ -0,0 +1,15 @@ +class Solution { + public int alternateDigitSum(int n) { + + int result =0; + int flag = 1; + + String num = String.valueOf(n); + for(int i=0; i> findMatrix(int[] nums) { + Map count = new HashMap<>(); + List> res = new ArrayList<>(); + + for (int n : nums) { + int row = count.getOrDefault(n, 0); + if (res.size() == row) { + res.add(new ArrayList<>()); + } + res.get(row).add(n); + count.put(n, count.getOrDefault(n, 0) + 1); + } + + return res; + } +} diff --git a/java/2616-minimize-the-maximum-difference-of-pairs.java b/java/2616-minimize-the-maximum-difference-of-pairs.java new file mode 100644 index 000000000..cd96e9c77 --- /dev/null +++ b/java/2616-minimize-the-maximum-difference-of-pairs.java @@ -0,0 +1,38 @@ +class Solution { + public int minimizeMax(int[] nums, int p) { + Arrays.sort(nums); + + int s = 0; + int e = nums[nums.length -1]; + int res = Integer.MAX_VALUE; + + while(s <= e) { + int mid = s + (e-s)/2; + + if(isPossible(nums, mid, p)) { + res = mid; + e = mid - 1; + } else { + s = mid + 1; + } + } + + return res; + } + + public boolean isPossible(int[] arr, int maxDiff, int pairs) { + int count = 0; + int i = 0; + + while(i < arr.length - 1) { + if(Math.abs(arr[i] - arr[i+1]) <= maxDiff) { + count++; + i += 2; + } else { + i++; + } + } + + return count >= pairs; + } +} diff --git a/java/2706-buy-two-chocolates.java b/java/2706-buy-two-chocolates.java new file mode 100644 index 000000000..092c666cf --- /dev/null +++ b/java/2706-buy-two-chocolates.java @@ -0,0 +1,34 @@ +/*------------------------------ + Time Complexity: O(n) + Space Complexity: O(1) +-------------------------------*/ +class Solution { + public int buyChoco(int[] prices, int money) { + int min1 = Integer.MAX_VALUE, min2 = Integer.MAX_VALUE; + + for(int p: prices){ + if(p < min2){ + if(p < min1){ + min2 = min1; + min1 = p; + } + else + min2 = p; + } + } + int leftover = money - (min1 + min2); + return (leftover < 0)? money: leftover; + } +} + +/*------------------------------ + Time Complexity: O(nlog(n)) + Space Complexity: O(1) +-------------------------------*/ +class Solution { + public int buyChoco(int[] prices, int money) { + Arrays.sort(prices); + int leftover = money - (prices[0] + prices[1]); + return (leftover < 0)? money : leftover; + } +} diff --git a/java/2707-extra-characters-in-a-string.java b/java/2707-extra-characters-in-a-string.java new file mode 100644 index 000000000..38f05caf6 --- /dev/null +++ b/java/2707-extra-characters-in-a-string.java @@ -0,0 +1,26 @@ +/* + Let N = length of string s, M = length of dictionary + Time: O(N * M) + Space: O(N) +*/ +class Solution { + public int minExtraChar(String s, String[] dictionary) { + int n = s.length(); + int[] dp = new int[n+1]; + + Arrays.fill(dp, n); + dp[0] = 0; + + for (int i = 1; i <= n; ++i) { + for (int j = 0; j < dictionary.length; ++j) { + int len = dictionary[j].length(); + if (i >= len && s.substring(i - len, i).equals(dictionary[j])) { + dp[i] = Math.min(dp[i], dp[i - len]); + } + } + dp[i] = Math.min(dp[i], dp[i - 1] + 1); + } + + return dp[n]; + } +} \ No newline at end of file diff --git a/java/2742-painting-the-walls.java b/java/2742-painting-the-walls.java new file mode 100644 index 000000000..0a50ee99d --- /dev/null +++ b/java/2742-painting-the-walls.java @@ -0,0 +1,22 @@ +class Solution { + Map dp; + public int paintWalls(int[] cost, int[] time) { + int n = cost.length; + dp = new HashMap<>(); + return dfs(0, cost.length, cost, time); + } + private int dfs(int i, int remain, int[] cost, int[] time){ + if(remain <= 0) + return 0; + if(i == cost.length) + return (int) 1e9; + String s = i + "," + remain; + if(dp.containsKey(s)) + return dp.get(s); + + int paint = cost[i] + dfs(i+1, remain-1-time[i], cost, time); + int skip = dfs(i+1, remain, cost, time); + dp.put(s, Math.min(paint, skip)); + return dp.get(s); + } +} diff --git a/java/2925-maximum-score-after-applying-operations-on-a-tree.java b/java/2925-maximum-score-after-applying-operations-on-a-tree.java new file mode 100644 index 000000000..4414ae5de --- /dev/null +++ b/java/2925-maximum-score-after-applying-operations-on-a-tree.java @@ -0,0 +1,34 @@ +class Solution { + List> g; + public long maximumScoreAfterOperations(int[][] edges, int[] values) { + g = new ArrayList<>(); + for (int i = 0; i < values.length; i++) + g.add(new ArrayList<>()); + for (int[] edge : edges) { + int u = edge[0], v = edge[1]; + g.get(u).add(v); + g.get(v).add(u); + } + long totalSum = 0; + for (int v : values) + totalSum += v; + + long minScore = dfs(0, -1, values); + + return totalSum - minScore; + } + + private long dfs(int node, int parent, int[] values) { + long childSum = 0; + + for (int neighbour : g.get(node)) { + if (neighbour == parent) + continue; + childSum += dfs(neighbour, node, values); + } + if(childSum == 0) + return values[node]; + long result = Math.min(values[node], childSum); + return result; + } +} diff --git a/java/2943-maximize-area-of-square-hole-in-grid.java b/java/2943-maximize-area-of-square-hole-in-grid.java new file mode 100644 index 000000000..b6aaf4647 --- /dev/null +++ b/java/2943-maximize-area-of-square-hole-in-grid.java @@ -0,0 +1,27 @@ +class Solution { + public int maximizeSquareHoleArea(int n, int m, int[] hBars, int[] vBars) { + Arrays.sort(hBars); + Arrays.sort(vBars); + + int maxH = getMaxConsecutiveLength(hBars); + int maxV = getMaxConsecutiveLength(vBars); + int sqrlen = Math.min(maxH + 1, maxV + 1); + + return sqrlen*sqrlen; + } + private int getMaxConsecutiveLength(int[] arr) { + int maxLen = 0; + int currentLen = 1; + + for (int i = 1; i < arr.length; i++) { + if (arr[i] == arr[i - 1] + 1) { + currentLen++; + } else { + maxLen = Math.max(maxLen, currentLen); + currentLen = 1; + } + } + maxLen = Math.max(maxLen, currentLen); + return maxLen; + } +} diff --git a/java/2971-find-polygon-with-the-largest-perimeter.java b/java/2971-find-polygon-with-the-largest-perimeter.java new file mode 100644 index 000000000..3c30a84b7 --- /dev/null +++ b/java/2971-find-polygon-with-the-largest-perimeter.java @@ -0,0 +1,15 @@ +public class Solution { + + public long largestPerimeter(int[] nums) { + Arrays.sort(nums); + long res = -1, amt = 0; + + for (int i : nums) { + if (amt > i) + res = amt + i; + amt += i; + } + + return res; + } +} \ No newline at end of file diff --git a/java/371-Sum-of-Two-Integers.java b/java/371-Sum-of-Two-Integers.java deleted file mode 100644 index ff641469f..000000000 --- a/java/371-Sum-of-Two-Integers.java +++ /dev/null @@ -1,10 +0,0 @@ -class Solution { - public int getSum(int a, int b) { - while (b != 0) { - int tmp = (a & b) << 1; - a = (a ^ b); - b = tmp; - } - return a; - } -} diff --git a/javascript/0001-two-sum.js b/javascript/0001-two-sum.js new file mode 100644 index 000000000..68c0fafce --- /dev/null +++ b/javascript/0001-two-sum.js @@ -0,0 +1,79 @@ +/** + * Brute Force - Linear Search + * Time O(N^2) | Space O(1) + * https://leetcode.com/problems/two-sum/ + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +var twoSum = (nums, target) => { + for (let curr = 0; curr < nums.length; curr++) {/* Time O(N) */ + const complement = target - nums[curr]; + + for (let next = (curr + 1); next < nums.length; next++) {/* Time O(N) */ + const num = nums[next]; + + const isTarget = num === complement + if (isTarget) return [ curr, next ]; + } + } + + return [ -1, -1 ]; +} + +/** + * Hash Map - 2 Pass + * Time O(N) | Space O(N) + * https://leetcode.com/problems/two-sum/ + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +var twoSum = (nums, target) => { + const map = getMap(nums); /* Time O(N) | Space O(N) */ + + return getSum(nums, target, map)/* Time O(N) */ +} + +const getMap = (nums, map = new Map()) => { + for (let index = 0; index < nums.length; index++) {/* Time O(N) */ + map.set(nums[index], index); /* Space O(N) */ + } + + return map +} + +const getSum = (nums, target, map) => { + for (let index = 0; index < nums.length; index++) {/* Time O(N) */ + const complement = target - nums[index]; + const sumIndex = map.get(complement); + + const isTarget = map.has(complement) && (map.get(complement) !== index) + if (isTarget) return [ index, sumIndex ] + } + + return [ -1, -1 ]; +} + +/** + * Hash Map - 1 Pass + * Time O(N) | Space O(N) + * https://leetcode.com/problems/two-sum/ + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +var twoSum = (nums, target, map = new Map()) => { + for (let index = 0; index < nums.length; index++) {/* Time O(N) */ + const num = nums[index]; + const complement = (target - num); + const sumIndex = map.get(complement); + + const isTarget = map.has(complement) + if (isTarget) return [ index, sumIndex ]; + + map.set(num, index); /* Space O(N) */ + } + + return [ -1, -1 ]; +} \ No newline at end of file diff --git a/javascript/0002-add-two-numbers.js b/javascript/0002-add-two-numbers.js new file mode 100644 index 000000000..129cf39c8 --- /dev/null +++ b/javascript/0002-add-two-numbers.js @@ -0,0 +1,60 @@ +/** + * https://leetcode.com/problems/add-two-numbers/ + * Time O(MAX(N, M)) | Space O(MAX(N, M)) + * @param {ListNode} l1 + * @param {ListNode} l2 + * @return {ListNode} + */ +var addTwoNumbers = function(l1, l2) { + let sentinel = tail = new ListNode(); + + return add(l1, l2, tail, sentinel); /* Time O(MAX(N, M)) | Space O(MAX(N, M)) */ +} + +const add = (l1, l2, tail, sentinel, carry = 0) => { + const isBaseCase = !(l1 || l2 || carry); + if (isBaseCase) return sentinel.next; + + return dfs(l1, l2, tail, sentinel, carry);/* Time O(MAX(N, M)) | Space O(MAX(N, M)) */ +} + +const dfs = (l1, l2, tail, sentinel, carry) => { + const sum = (l1?.val || 0) + (l2?.val || 0) + carry; + const val = sum % 10; + carry = Math.floor(sum / 10); + + tail.next = new ListNode(val); + tail = tail.next; + + l1 = l1?.next || null; + l2 = l2?.next || null; + + add(l1, l2, tail, sentinel, carry); /* Time O(MAX(N, M)) | Space O(MAX(N, M)) */ + + return sentinel.next; +} + +/** + * https://leetcode.com/problems/add-two-numbers/ + * Time O(MAX(N, M)) | Space O(MAX(N, M)) + * @param {ListNode} l1 + * @param {ListNode} l2 + * @return {ListNode} + */ +var addTwoNumbers = function(l1, l2, carry = 0) { + let sentinel = tail = new ListNode(); + + while (l1 || l2 || carry) {/* Time O(MAX(N, M)) */ + const sum = (l1?.val || 0) + (l2?.val || 0) + carry; + const val = sum % 10; + carry = Math.floor(sum / 10); + + tail.next = new ListNode(val); + tail = tail.next; + + l1 = l1?.next || null; + l2 = l2?.next || null; + } + + return sentinel.next; +}; diff --git a/javascript/0003-longest-substring-without-repeating-characters.js b/javascript/0003-longest-substring-without-repeating-characters.js new file mode 100644 index 000000000..0208f73fe --- /dev/null +++ b/javascript/0003-longest-substring-without-repeating-characters.js @@ -0,0 +1,22 @@ +/** + * https://leetcode.com/problems/longest-substring-without-repeating-characters/ + * Time O(N) | Space O(N) + * @param {string} s + * @return {number} + */ + +var lengthOfLongestSubstring = function (s) { + const set = new Set(); + let l = 0; + let max = 0; + + for (let r = 0; r < s.length; r++) { + while (set.has(s[r])) { + set.delete(s[l]); + l++; + } + set.add(s[r]); + max = Math.max(max, set.size); + } + return max; +}; diff --git a/javascript/0004-median-of-two-sorted-arrays.js b/javascript/0004-median-of-two-sorted-arrays.js new file mode 100644 index 000000000..7aa178d9d --- /dev/null +++ b/javascript/0004-median-of-two-sorted-arrays.js @@ -0,0 +1,51 @@ +/** + * @param {number[]} nums1 + * @param {number[]} nums2 + * Time O(log(N * M)) | Space O(N) + * @return {number} + */ +var findMedianSortedArrays = function (nums1, nums2) { + const canSwap = nums2.length < nums1.length; + if (canSwap) [nums1, nums2] = [nums2, nums1]; + + let [left, right] = [0, nums1.length - 1]; + const totalLength = nums1.length + nums2.length; + const mid = totalLength >> 1; + const isEven = totalLength % 2 === 0; + + while (true) { + const mid1 = left + right; + const mid2 = mid - mid1 - 2; + const { aLeft, aRight, bLeft, bRight } = getPointers( + nums1, + mid1, + nums2, + mid2 + ); + + const isTarget = aLeft <= bRight && bLeft <= aRight; + if (isTarget) + return isEven + ? (Math.max(aLeft, bLeft) + Math.min(aRight, bRight)) / 2 + : Math.min(aRight, bRight); + + const isTargetGreater = aLeft <= bRight; + if (isTargetGreater) left = mid1 + 1; + + const isTargetLess = bRight < aLeft; + if (isTargetLess) right = mid1 - 1; + } +}; + +const getPointers = (nums1, mid1, nums2, mid2) => { + const getLeft = (nums, index) => (0 <= index ? nums[index] : -Infinity); + + const [aLeft, bLeft] = [getLeft(nums1, mid1), getLeft(nums2, mid2)]; + + const getRight = (nums, index) => + index + 1 < nums.length ? nums[index + 1] : Infinity; + + const [aRight, bRight] = [getRight(nums1, mid1), getRight(nums2, mid2)]; + + return { aLeft, aRight, bLeft, bRight }; +}; diff --git a/javascript/0005-longest-palindromic-substring.js b/javascript/0005-longest-palindromic-substring.js new file mode 100644 index 000000000..c52a3dbec --- /dev/null +++ b/javascript/0005-longest-palindromic-substring.js @@ -0,0 +1,43 @@ +/** + * Expand Around Center + * Time O(N^2) | Space O(1) + * https://leetcode.com/problems/longest-palindromic-substring/ + * @param {string} s + * @return {string} + */ +var longestPalindrome = (s) => { + const isEmpty = s.length === 0; + if (isEmpty) return ''; + + const [ left, right ] = search(s);/* Time O(N * N) */ + + return s.slice(left, (right + 1));/* Time O(N * N) | Ignore Auxillary Space (N) */ +} + +const search = (s, left = 0, right = 0) => { + for (let index = 0; index < s.length; index++) {/* Time O(N) */ + const len1 = getLength(s, index, index); /* Time O(N) */ + const len2 = getLength(s, index, (index + 1)); /* Time O(N) */ + const [ length, window ] = [ (Math.max(len1, len2)), (right - left) ]; + + const canSkip = (length <= window); + if (canSkip) continue; + + left = (index - ((length - 1) >> 1)); + right = (index + (length >> 1)); + } + + return [ left, right ]; +} + +const getLength = (s, left, right) => { + const canExpand = () => ((0 <= left) && (right < s.length)); + const isSame = () => (s[left] === s[right]); + + const isPalindrome = () => (canExpand() && isSame()); + while (isPalindrome()) { left--; right++; }/* Time O(N) */ + + const window = ((right - left) - 1); + + return window; +} diff --git a/javascript/0007-reverse-integer.js b/javascript/0007-reverse-integer.js new file mode 100644 index 000000000..551e87ad7 --- /dev/null +++ b/javascript/0007-reverse-integer.js @@ -0,0 +1,30 @@ +/** + * https://leetcode.com/problems/reverse-integer/ + * Time O(log(x)) | Space O(1) + * @param {number} x + * @return {number} + */ +var reverse = function(x, result = 0) { + while (x !== 0) { + const digit = (x % 10) + + if (isOutOfBounds(digit, result)) return 0; + + x = Math.trunc(x / 10); + result = (result * 10) + digit; + } + + return result; +}; + +const isOutOfBounds = (digit, result) => { + const [ max, min ] = [ ((2 ** 31) - 1), (-(2 ** 31)) ]; + const [ maxProduct, maxRemainder ] = [ (max / 10), (max % 10) ]; + const [ minProduct, minRemainder ] = [ (min / 10), (min % 10) ]; + const isTarget = result === maxProduct; + + const isMaxOut = ((maxProduct < result) || (isTarget && (maxRemainder <= digit))); + const isMinOut = ((result < minProduct) || (isTarget && (digit <= minRemainder))); + + return isMaxOut || isMinOut; +} diff --git a/javascript/0009-palindrome-number.js b/javascript/0009-palindrome-number.js new file mode 100644 index 000000000..9312c1033 --- /dev/null +++ b/javascript/0009-palindrome-number.js @@ -0,0 +1,44 @@ +/** + * @param {number} x + * @return {boolean} + */ +var isPalindrome = function (x) { + // Creates array from int characters + // 121 -> [1,2,1] + let arr = Array.from(String(x), Number); + + // Uses two pointer + for (let i = 0; i < arr.length; i++) { + if (arr[i] !== arr[arr.length - 1 - i]) { + return false; + } + } + + return true; +}; + +// Runtime: 302 ms, faster than 40.50% of JavaScript online submissions for Palindrome Number. +// Memory Usage: 51.8 MB, less than 8.36% of JavaScript online submissions for Palindrome Number. + +/** + * Reverse Integer Using Modulo + * Time O(N) | Space O(1) + * https://leetcode.com/problems/palindrome-number/ + * @param {number} x + * @return {boolean} + */ + var isPalindrome = function(x) { + if (x < 0) return false; + + const inputX = x; + let revX = 0; + + while (x > 0) { + revX += x % 10; + x = Math.floor(x / 10); + + if (x > 0) revX *= 10 + } + + return revX === inputX; +}; diff --git a/javascript/0010-regular-expression-matching.js b/javascript/0010-regular-expression-matching.js new file mode 100644 index 000000000..81a21f2c9 --- /dev/null +++ b/javascript/0010-regular-expression-matching.js @@ -0,0 +1,98 @@ +/** + * Brute Force - DFS + * Time O((N + M) * 2^(N + (M / 2))) | Space O(N^2 + M^2) + * https://leetcode.com/problems/regular-expression-matching/ + * @param {string} s + * @param {string} p + * @return {boolean} + */ +var isMatch = (text, pattern) => { + const isBaseCase = (pattern.length === 0); + if (isBaseCase) return (text.length === 0); + + const isTextAndPatternEqual = (pattern[0] === text[0]), + isPatternPeriod = (pattern[0] === '.'), + isFirstMatch = (text && (isTextAndPatternEqual || isPatternPeriod)), + isNextPatternWildCard = (pattern.length >= 2 && pattern[1] === '*'); + + return isNextPatternWildCard/* Time O((N + M) * 2^(N + (M / 2))) | Space O(N^2 + M^2) */ + ? (isMatch(text, pattern.slice(2)) || (isFirstMatch && isMatch(text.slice(1), pattern))) + : (isFirstMatch && isMatch(text.slice(1), pattern.slice(1))); +}; + +/** + * DP - Top Down + * Matrix - Memoization + * Time O(N * M) | Space O(N * M) + * https://leetcode.com/problems/regular-expression-matching/ + * @param {string} s + * @param {string} p + * @return {boolean} + */ +var isMatch = (text, pattern, row = 0, col = 0, memo = initMemo(text, pattern)) => { + const hasSeen = (memo[row][col]); + if (hasSeen) return memo[row][col]; + + const isEqual = (col === pattern.length); + const ans = isEqual + ? row === text.length + : check(text, pattern, row, col, memo);/* Time O(N * M) | Space O(N * M) */ + + memo[row][col] = ans; + return ans; +} + +var initMemo = (text, pattern) => new Array((text.length + 1)).fill()/* Time O(N) | Space O(N) */ + .map(() => new Array((pattern.length + 1)).fill(false)) /* Time O(M) | Space O(M) */ + +var check = (text, pattern, row, col, memo) => { + const isTextDefined = (row < text.length), + isTextAndPatternEqual = (pattern[col] === text[row]), + isPatternPeriod = (pattern[col] === '.'), + isFirstMatch = (isTextDefined && (isTextAndPatternEqual || isPatternPeriod)), + isNextPatternWildCard = (((col + 1) < pattern.length) && pattern[col + 1] === '*'); + + return isNextPatternWildCard/* Time O(N * M) | Space O(N * M) */ + ? (isMatch(text, pattern, row, (col + 2), memo) || isFirstMatch && isMatch(text, pattern, (row + 1), col, memo)) + : (isFirstMatch && isMatch(text, pattern, (row + 1), (col + 1), memo)); +} + +/** + * Time O(N * M) | Space O(N * M) + * @param {string} s + * @param {string} p + * @return {boolean} + */ +var isMatch = (text, pattern) => { + const tabu = initTabu(text, pattern);/* Time O(N * M) | Space O(N * M) */ + + search(text, pattern, tabu); /* Time O(N * M) | Space O(N * M) */ + + return tabu[0][0]; + +} + +var initTabu = (text, pattern) => { + const tabu = new Array((text.length + 1)).fill() /* Time O(N) | Space O(N) */ + .map(() => new Array((pattern.length + 1)).fill(false));/* Time O(M) | Space O(M) */ + + tabu[text.length][pattern.length] = true; /* | Space O(N * M) */ + + return tabu +} + +var search = (text, pattern, tabu) => { + for (let row = text.length; 0 <= row; row--){ /* Time O(N) */ + for (let col = (pattern.length - 1); (0 <= col); col--){/* Time O(M) */ + const isTextDefined = row < text.length, + isTextAndPatternEqual = pattern[col] === text[row], + isPatternPeriod = pattern[col] === '.', + isFirstMatch = isTextDefined && (isTextAndPatternEqual || isPatternPeriod), + isNextPatternWildCard = col + 1 < pattern.length && pattern[col + 1] === '*'; + + tabu[row][col] = isNextPatternWildCard /* Space O(N * M) */ + ? tabu[row][col + 2] || (isFirstMatch && tabu[row + 1][col]) + : isFirstMatch && tabu[row + 1][col + 1]; + } + } +} \ No newline at end of file diff --git a/javascript/0011-container-with-most-water.js b/javascript/0011-container-with-most-water.js new file mode 100644 index 000000000..16272ddb4 --- /dev/null +++ b/javascript/0011-container-with-most-water.js @@ -0,0 +1,31 @@ +/** + * https://leetcode.com/problems/container-with-most-water/ + * Two pointers, Time O(N) | Space(1) + * @param {number[]} height + * @return {number} + */ + +var maxArea = function (height) { + let [left, right, max] = [0, height.length - 1, 0]; + + while (left < right) { + let containerHeight, area; + let containerWidth = right - left; + + if (height[left] < height[right]) { + containerHeight = height[left]; + left++; + } else { + containerHeight = height[right]; + right--; + } + + area = containerWidth * containerHeight; + + if (area > max) { + max = area; + } + } + + return max; +}; diff --git a/javascript/0012-integer-to-roman.js b/javascript/0012-integer-to-roman.js new file mode 100644 index 000000000..8f79ee3d2 --- /dev/null +++ b/javascript/0012-integer-to-roman.js @@ -0,0 +1,84 @@ +/** + * https://leetcode.com/problems/integer-to-roman/ + * @param {number} num + * @return {string} + */ + +var intToRoman = function(num) { + let ans = ""; + + while(num !== 0){ + + //M == 1000 + if(num >= 1000){ + num -= 1000; + ans += "M"; + } + //CM == 900 + else if(num >= 900){ + num -= 900; + ans += "CM"; + } + //D == 500 + else if(num >= 500){ + num -= 500; + ans += "D"; + } + //CD == 400 + else if(num >= 400){ + num -= 400; + ans += "CD" + } + //C == 100 + else if(num >= 100){ + num -= 100; + ans += "C"; + } + //XC == 90 + else if(num >= 90){ + num -= 90; + ans += "XC"; + } + //L == 50; + else if(num >= 50){ + num -= 50; + ans += "L"; + } + //XL == 40 + else if(num >= 40){ + num -= 40; + ans += "XL"; + } + //X == 10 + else if(num >= 10){ + num -= 10; + ans += "X"; + } + //IX == 9 + else if(num >= 9){ + num -= 9; + ans += "IX"; + } + //V == 5 + else if(num >= 5){ + num -= 5; + ans += "V"; + } + //IV == 4 + else if(num >= 4){ + num -= 4; + ans += "IV"; + } + //II == 2 + else if(num >= 2){ + num -= 2; + ans += "II"; + } + //I == 1 + else{ + num -= 1; + ans += "I"; + } + } + return ans; +}; diff --git a/javascript/0013-roman-to-integer.js b/javascript/0013-roman-to-integer.js new file mode 100644 index 000000000..f6bd00410 --- /dev/null +++ b/javascript/0013-roman-to-integer.js @@ -0,0 +1,93 @@ +/** + * @param {string} s + * @return {number} + */ +var romanToInt = function (s) { + let romans = { + I: 1, + V: 5, + X: 10, + L: 50, + C: 100, + D: 500, + M: 1000, + }; + + let arr = s.split(''); + + let sum = 0; + + for (let i = arr.length - 1; i >= 0; i--) { + // IV : 4 + if (romans[arr[i]] === romans['V']) { + if (romans[arr[i - 1]] === romans['I']) { + sum -= 1 * 2; + } + } + // IX : 4 + if (romans[arr[i]] === romans['X']) { + if (romans[arr[i - 1]] === romans['I']) { + sum -= 1 * 2; + } + } + // XL : 40 + if (romans[arr[i]] === romans['L']) { + if (romans[arr[i - 1]] === romans['X']) { + sum -= 10 * 2; + } + } + // XC : 90 + if (romans[arr[i]] === romans['C']) { + if (romans[arr[i - 1]] === romans['X']) { + sum -= 10 * 2; + } + } + // CD : 400 + if (romans[arr[i]] === romans['D']) { + if (romans[arr[i - 1]] === romans['C']) { + sum -= 100 * 2; + } + } + // CM : 900 + if (romans[arr[i]] === romans['M']) { + if (romans[arr[i - 1]] === romans['C']) { + sum -= 100 * 2; + } + } + + sum += romans[arr[i]]; + } + + return sum; +}; + +// Runtime: 148 ms, faster than 80.16% of JavaScript online submissions for Roman to Integer. +// Memory Usage: 47.5 MB, less than 18.15% of JavaScript online submissions for Roman to Integer. + +function romanToInt(s) { + let sum = 0 + let next = null + const romanArr = { + "I": 1, + "V": 5, + "X": 10, + "L": 50, + "C": 100, + "D": 500, + "M": 1000 + } + for (let i = 0; i < s.length; i++ ) { + next = s[i + 1] || null + const curr = s[i] + if (romanArr[next] > romanArr[curr]) { + sum -= romanArr[curr] + continue + } + sum += romanArr[curr] + } + return sum +}; + +// Runtime 97 ms +// Memory usage: 47.8 MB +// https://leetcode.com/problems/roman-to-integer/submissions/1020204566/ \ No newline at end of file diff --git a/javascript/0014-longest-common-prefix.js b/javascript/0014-longest-common-prefix.js new file mode 100644 index 000000000..783c06261 --- /dev/null +++ b/javascript/0014-longest-common-prefix.js @@ -0,0 +1,21 @@ +/** + * Time O(N^2) | Space O(N) + * @param {string[]} strs + * @return {string} + */ +var longestCommonPrefix = function(strs) { + + let pre = strs[0]; + + for(let word of strs) { + + for(let i = pre.length - 1; i >= 0; i--) { + + if(pre[i] !== word[i]) { + pre = pre.slice(0, i); + } + } + } + + return pre; +}; diff --git a/javascript/0015-3sum.js b/javascript/0015-3sum.js new file mode 100644 index 000000000..1f099451a --- /dev/null +++ b/javascript/0015-3sum.js @@ -0,0 +1,34 @@ +/** + * @param {number[]} nums + * @return {number[][]} + */ +var threeSum = function(nums) { + const res = []; + nums.sort((a,b) => a-b) + + for (let i = 0; i < nums.length; i++) { + const a = nums[i]; + if (a > 0) break; + if (i > 0 && a === nums[i - 1]) continue; + + let l = i + 1; + let r = nums.length - 1; + while (l < r) { + const threeSum = a + nums[l] + nums[r]; + if (threeSum > 0) { + r--; + } else if (threeSum < 0) { + l++; + } else { + res.push([a, nums[l], nums[r]]); + l++; + r--; + while (nums[l] === nums[l - 1] && l < r) { + l++; + } + } + } + } + return res; +} + diff --git a/javascript/0017-letter-combinations-of-a-phone-number.js b/javascript/0017-letter-combinations-of-a-phone-number.js new file mode 100644 index 000000000..3aaa12557 --- /dev/null +++ b/javascript/0017-letter-combinations-of-a-phone-number.js @@ -0,0 +1,39 @@ +/** + * https://leetcode.com/problems/letter-combinations-of-a-phone-number/ + * Time O(N * 4^N) | Space O(N) + * @param {string} digits + * @return {string[]} + */ + var letterCombinations = function(digits, combination = [], combinations = []) { + const isBaseCase = !digits + if (isBaseCase) { + if (combination.length) combinations.push(combination.join('')) + + return combinations; + } + + const letters = phoneButtons[ digits[0] ]; + + for (const char of letters) { + backTrack(digits, char, combination, combinations); + } + + return combinations; +}; + +const backTrack = (digits, char, combination, combinations) => { + combination.push(char) + letterCombinations(digits.slice(1), combination, combinations) + combination.pop() +} + +const phoneButtons = ({ + 2: ['a', 'b', 'c'], + 3: ['d', 'e', 'f'], + 4: ['g', 'h', 'i'], + 5: ['j', 'k', 'l'], + 6: ['m', 'n', 'o'], + 7: ['p', 'q', 'r', 's'], + 8: ['t', 'u', 'v'], + 9: ['w', 'x', 'y', 'z'], +}) diff --git a/javascript/0018-4sum.js b/javascript/0018-4sum.js new file mode 100644 index 000000000..fe342ed84 --- /dev/null +++ b/javascript/0018-4sum.js @@ -0,0 +1,48 @@ +/** + * @param {number[]} nums + * @param {number} target + * @return {number[][]} + */ +var fourSum = function (nums, target) { + const sortedNums = nums.sort((a, b) => a - b); + const res = []; + const quad = []; + + const kSum = (k, start, target) => { + if (k > 2) { + for (let i = start; i < sortedNums.length; i++) { + if (i !== start && sortedNums[i] === sortedNums[i - 1]) { + continue; + } + quad.push(sortedNums[i]); + kSum(k - 1, i + 1, target - sortedNums[i]); + quad.pop(); + } + } else { + let left = start; + let right = sortedNums.length - 1; + + while (left < right) { + const sum = sortedNums[left] + sortedNums[right]; + if (sum < target) { + left++; + } else if (sum > target) { + right--; + } else { + res.push( + quad.concat([sortedNums[left], sortedNums[right]]) + ); + left++; + while ( + left < right && + sortedNums[left] === sortedNums[left - 1] + ) { + left++; + } + } + } + } + }; + kSum(4, 0, target); + return res; +}; diff --git a/javascript/0019-remove-nth-node-from-end-of-list.js b/javascript/0019-remove-nth-node-from-end-of-list.js new file mode 100644 index 000000000..082779863 --- /dev/null +++ b/javascript/0019-remove-nth-node-from-end-of-list.js @@ -0,0 +1,74 @@ +/** + * https://leetcode.com/problems/remove-nth-node-from-end-of-list/ + * Time O(N) | Space O(N) + * @param {ListNode} head + * @param {number} n + * @return {ListNode} + */ + var removeNthFromEnd = function(head, n) { + const sentinel = new ListNode(); + + sentinel.next = head; + + const fast = moveFast(sentinel, n); /* Time O(N) */ + const slow = moveSlow(sentinel, fast);/* Time O(N) */ + + slow.next = slow.next.next || null; + + return sentinel.next; +}; + +const moveFast = (fast, n) => { + for (let i = 1; i <= (n + 1); i++) {/* Time O(N) */ + fast = fast.next; + } + + return fast; +} + +const moveSlow = (slow, fast) => { + while (fast) { /* Time O(N) */ + slow = slow.next; + fast = fast.next; + } + + return slow; +} + +/** + * https://leetcode.com/problems/remove-nth-node-from-end-of-list/ + * Time O(N) | Space O(1) + * @param {ListNode} head + * @param {number} n + * @return {ListNode} + */ + var removeNthFromEnd = function(head, n) { + const length = getNthFromEnd(head, n);/* Time O(N) */ + + const isHead = length < 0; + if (isHead) return head.next; + + const curr = moveNode(head, length); /* Time O(N) */ + + curr.next = curr.next.next; + + return head +}; + +const getNthFromEnd = (curr, n, length = 0) => { + while (curr) { /* Time O(N) */ + curr = curr.next; + length++; + } + + return (length - n) - 1; +} + +const moveNode = (curr, length) => { + while (length) { /* Time O(N) */ + curr = curr.next; + length--; + } + + return curr; +} diff --git a/javascript/0020-valid-parentheses.js b/javascript/0020-valid-parentheses.js new file mode 100644 index 000000000..c262edd16 --- /dev/null +++ b/javascript/0020-valid-parentheses.js @@ -0,0 +1,54 @@ +/** + * Time O(N) | Space O(N) + * https://leetcode.com/problems/valid-parentheses/ + * @param {string} s + * @return {boolean} + */ + var isValid = (s, stack = []) => { + for (const bracket of s.split('')) {/* Time O(N) */ + const isParenthesis = bracket === '('; + if (isParenthesis) stack.push(')'); /* Space O(N) */ + + const isCurlyBrace = bracket === '{'; + if (isCurlyBrace) stack.push('}'); /* Space O(N) */ + + const isSquareBracket = bracket === '['; + if (isSquareBracket) stack.push(']');/* Space O(N) */ + + const isOpenPair = isParenthesis || isCurlyBrace || isSquareBracket; + if (isOpenPair) continue; + + const isEmpty = !stack.length; + const isWrongPair = stack.pop() !== bracket; + const isInvalid = isEmpty || isWrongPair; + if (isInvalid) return false; + } + + return (stack.length === 0); +}; + +/** + * Time O(N) | Space O(N) + * https://leetcode.com/problems/valid-parentheses/ + * @param {string} s + * @return {boolean} + */ +var isValid = (s, stack = []) => { + const map = { + '}': '{', + ']': '[', + ')': '(', + }; + + for (const char of s) {/* Time O(N) */ + const isBracket = (char in map) + if (!isBracket) { stack.push(char); continue; }/* Space O(N) */ + + const isEqual = (stack[stack.length - 1] === map[char]) + if (isEqual) { stack.pop(); continue; } + + return false; + } + + return (stack.length === 0); +}; diff --git a/javascript/0021-merge-two-sorted-lists.js b/javascript/0021-merge-two-sorted-lists.js new file mode 100644 index 000000000..ede603e51 --- /dev/null +++ b/javascript/0021-merge-two-sorted-lists.js @@ -0,0 +1,57 @@ +/** + * https://leetcode.com/problems/merge-two-sorted-lists/ + * Time O(N + M) | Space O(N + M) + * @param {ListNode} list1 + * @param {ListNode} list2 + * @return {ListNode} + */ + var mergeTwoLists = function(list1, list2) { + const isBaseCase1 = list1 === null; + if (isBaseCase1) return list2; + + const isBaseCase2 = list2 === null; + if (isBaseCase2) return list1; + + const isL2Greater = list1.val <= list2.val; + if (isL2Greater) { + list1.next = mergeTwoLists(list1.next, list2);/* Time O(N + M) | Space O(N + M) */ + + return list1; + } + + const isL2Less = list2.val <= list1.val; + if (isL2Less) { + list2.next = mergeTwoLists(list1, list2.next);/* Time O(N + M) | Space O(N + M) */ + + return list2; + } +} + +/** + * https://leetcode.com/problems/merge-two-sorted-lists/ + * Time O(N + M) | Space O(N + M) + * @param {ListNode} list1 + * @param {ListNode} list2 + * @return {ListNode} + */ +var mergeTwoLists = function(list1, list2) { + let sentinel = tail = new ListNode(); + + while (list1 && list2) {/* Time O(N + M) */ + const isL2Greater = list1.val <= list2.val; + + if (isL2Greater) { + tail.next = list1; + list1 = list1.next; + } else { + tail.next = list2; + list2 = list2.next; + } + + tail = tail.next; + } + + tail.next = list1 || list2; + + return sentinel.next; +}; \ No newline at end of file diff --git a/javascript/0022-generate-parentheses.js b/javascript/0022-generate-parentheses.js new file mode 100644 index 000000000..436d607b7 --- /dev/null +++ b/javascript/0022-generate-parentheses.js @@ -0,0 +1,98 @@ +/** + * DFS + * Time O(((4^N) / (N * SQRT(N)))) | Space O(((4^N) / (N * SQRT(N)))) + * Time O(2^N) | Space O(2^N) + * https://leetcode.com/problems/generate-parentheses + * @param {number} n + * @return {string[]} + */ +var generateParenthesis = (n) => dfs(n); + +const dfs = (n, combos = [], open = 0, close = 0, path = []) => { + const isBaseCase = (path.length === (n * 2)); + if (isBaseCase) { + combos.push(path.join(''));/* Space O(N + N) */ + + return combos; + } + + const isOpen = open < n; + if (isOpen) backTrackOpen(n, combos, open, close, path); /* Time O(2^N) | Space O(2^N) */ + + const isClose = close < open; + if (isClose) backTrackClose(n, combos, open, close, path);/* Time O(2^N) | Space O(2^N) */ + + return combos; +} + +const backTrackOpen = (n, combos, open, close, path) => { + path.push('(');/* Space O(N) */ + dfs(n, combos, (open + 1), close, path);/* Time O(2^N) | Space O(2^N) */ + path.pop(); +} + +const backTrackClose = (n, combos, open, close, path) => { + path.push(')');/* Space O(N) */ + dfs(n, combos, open, (close + 1), path);/* Time O(2^N) | Space O(2^N) */ + path.pop(); +} + +/** + * BFS + * Time O(((4^N) / (N * SQRT(N)))) | Space O(((4^N) / (N * SQRT(N)))) + * Time O(2^N) | Space O(2^N) + * https://leetcode.com/problems/generate-parentheses + * @param {number} n + * @return {string[]} + */ +var generateParenthesis = (n) => bfs(n); + +const bfs = (n, combos = []) => { + const queue = new Queue([ ['', 0, 0] ]); + + while (!queue.isEmpty()) {/* Time O(2^N) */ + const [ str, open, close ] = queue.dequeue(); + + const isBaseCase = ((open === n) && (close === n)); + if (isBaseCase) { + combos.push(str); /* Space O(N) */ + + continue; + } + + const isOpen = open < n; + if (isOpen) queue.enqueue([ (`${str}(`), (open + 1), close ]); /* Space O(2^N) */ + + const isClose = close < open; + if (isClose) queue.enqueue([ (`${str})`), open, (close + 1) ]);/* Space O(2^N) */ + } + + return combos; +} + +/** + * DFS + * Time O(((4^N) / (N * SQRT(N)))) | Space O(((4^N) / (N * SQRT(N)))) + * Time O(2^N) | Space O(2^N) + * https://leetcode.com/problems/generate-parentheses + * @param {number} n + * @return {string[]} + */ +var generateParenthesis = (n, combos = []) => { + const isBaseCase = (n === 0); + if (isBaseCase) { + combos.push(''); + + return combos + } + + for (let c = 0; (c < n); c++) { + for (const left of generateParenthesis(c)) { + for (const right of generateParenthesis(((n - 1) - c))) { + combos.push(`(${left})${right}`); + } + } + } + + return combos +} diff --git a/javascript/0023-merge-k-sorted-lists.js b/javascript/0023-merge-k-sorted-lists.js new file mode 100644 index 000000000..b84454b1e --- /dev/null +++ b/javascript/0023-merge-k-sorted-lists.js @@ -0,0 +1,78 @@ +/** + * https://leetcode.com/problems/merge-k-sorted-lists/ + * Time O(N) | Space O(N) + * @param {ListNode[]} lists + * @return {ListNode} + */ + var mergeKLists = function(lists) { + let previous = null; + + for (let i = 0; i < lists.length; i++) { + previous = mergeTwoLists(previous, lists[i]); + } + + return previous; +}; + +var mergeTwoLists = function(list1, list2) { + let sentinel = tail = new ListNode(0); + + while (list1 && list2) { + const canAddL1 = list1.val <= list2.val; + if (canAddL1) { + tail.next = list1; + list1 = list1.next; + } else { + tail.next = list2; + list2 = list2.next; + } + + tail = tail.next; + } + + tail.next = list1 || list2; + + return sentinel.next; +}; + +/** + * https://leetcode.com/problems/merge-k-sorted-lists/ + * Time O(N * log(K)) | Space O(N + K) + * @param {ListNode[]} lists + * @return {ListNode} + */ + var mergeKLists = function(lists) { + const minHeap = getMinHeap(lists); + + return mergeLists(minHeap) +}; + +const getMinHeap = (lists) => { + const heap = new MinPriorityQueue({ priority: ({ val }) => val }); + + for (const node of lists) { + if (!node) continue; + + heap.enqueue(node); + } + + return heap; +} + + +const mergeLists = (minHeap) => { + let sentinel = tail = new ListNode(); + + while (!minHeap.isEmpty()) { + const node = minHeap.dequeue().element; + + tail.next = node; + tail = tail.next; + + if (!node.next) continue; + + minHeap.enqueue(node.next); + } + + return sentinel.next; +} diff --git a/javascript/0025-reverse-nodes-in-k-group.js b/javascript/0025-reverse-nodes-in-k-group.js new file mode 100644 index 000000000..26504326c --- /dev/null +++ b/javascript/0025-reverse-nodes-in-k-group.js @@ -0,0 +1,44 @@ +/** + * https://leetcode.com/problems/reverse-nodes-in-k-group/ + * Time O(N) | Space O(N) + * @param {ListNode} head + * @param {number} k + * @return {ListNode} + */ + var reverseKGroup = function(head, k) { + const sentinel = tail = new ListNode(0, head); + + while (true) { + let [ start, last ]= moveNode(tail, k); + if (!last) break; + + reverse([ start, tail.next, start ]) + + const next = tail.next; + tail.next = last; + tail = next; + } + + return sentinel.next; +}; + +const moveNode = (curr, k) => { + const canMove = () => k && curr; + while (canMove()) { + curr = curr.next; + k--; + } + + return [ (curr?.next || null), curr ]; +} + +const reverse = ([ prev, curr, start ]) => { + const isSame = () => curr === start; + while (!isSame()) { + const next = curr.next; + curr.next = prev; + + prev = curr; + curr = next; + } +} diff --git a/javascript/0026-remove-duplicates-from-sorted-array.js b/javascript/0026-remove-duplicates-from-sorted-array.js new file mode 100644 index 000000000..cc4ae0995 --- /dev/null +++ b/javascript/0026-remove-duplicates-from-sorted-array.js @@ -0,0 +1,18 @@ +/** + * Linear + * Time O(N) | Space O(1) + * https://leetcode.com/problems/remove-duplicates-from-sorted-array/ + * @param {number[]} nums + * @return {number} + */ +var removeDuplicates = function (nums) { + let swap = 1; + + for (let i = 1; i < nums.length; i++) { + if (nums[i] != nums[i - 1]) { + nums[swap] = nums[i]; + swap++; + } + } + return swap; +}; diff --git a/javascript/0027-remove-element.js b/javascript/0027-remove-element.js new file mode 100644 index 000000000..236c11b7a --- /dev/null +++ b/javascript/0027-remove-element.js @@ -0,0 +1,55 @@ +/** + * Time O(N) | Space O(1) + * @param {number[]} nums + * @param {number} val + * @return {number} + */ +var removeElement = function(nums, val) { + let ptr1 = nums.length - 2; + let ptr2 = nums.length - 1; + + if(!nums) return 0; + if(nums.length === 1) { + if(nums[0] === val) return 0; + return 1; + } + + while(ptr1 > -1) { + if(nums[ptr2] === val) { + ptr2--; + + while(nums[ptr1] === val) { + ptr2--; + ptr1--; + } + } + else if(nums[ptr1] === val) { + let temp = nums[ptr1]; + nums[ptr1] = nums[ptr2]; + nums[ptr2] = temp; + ptr2--; + + } + + ptr1--; + } + + return ptr2 + 1; +}; + + +/** + * @param {number[]} nums + * @param {number} val + * @return {number} + */ +var removeElement = function(nums, val) { + for(let i = 0; i < nums.length; i++) { + if(nums[i] === val) { + nums.splice(i, 1); + i--; + } + } + + return nums.length; +}; diff --git a/javascript/0028-find-the-index-of-the-first-occurrence-in-a-string.js b/javascript/0028-find-the-index-of-the-first-occurrence-in-a-string.js new file mode 100644 index 000000000..b1bd50934 --- /dev/null +++ b/javascript/0028-find-the-index-of-the-first-occurrence-in-a-string.js @@ -0,0 +1,23 @@ +/** + * Submission Details: + * https://leetcode.com/problems/find-the-index-of-the-first-occurrence-in-a-string/ + * Time O(n * m), Space O(1) + * Runtime: 48ms (beats 91.92%) || 41.6mb (beats 78.25%) + */ + +/** + * @param {string} haystack + * @param {string} needle + * @return {number} + */ +var strStr = function(haystack, needle) { + if (needle.length == 0) return 0; + for (let i = 0; i < haystack.length; i++) { + let k = i, j = 0; + while (haystack[k] == needle[j] && j < needle.length) { + k++, j++; + } + if (j == needle.length) return i; + } + return -1; +} \ No newline at end of file diff --git a/javascript/0033-search-in-rotated-sorted-array.js b/javascript/0033-search-in-rotated-sorted-array.js new file mode 100644 index 000000000..95294f2e3 --- /dev/null +++ b/javascript/0033-search-in-rotated-sorted-array.js @@ -0,0 +1,44 @@ +/** + * @param {number[]} nums + * @param {number} target + * Time O(log(N)) | Space O(1) + * @return {number} + */ +var search = (nums, target) => { + let [left, right] = [0, nums.length - 1]; + + while (left <= right) { + const mid = (left + right) >> 1; + const guess = nums[mid]; + const [leftNum, rightNum] = [nums[left], nums[right]]; + + const isTarget = guess === target; + if (isTarget) return mid; + + const isAscending = leftNum <= guess; + if (isAscending) { + const isInRange = leftNum <= target; + const isLess = target < guess; + + const isTargetGreater = !(isInRange && isLess); + if (isTargetGreater) left = mid + 1; + + const isTargetLess = isInRange && isLess; + if (isTargetLess) right = mid - 1; + } + + const isDescending = guess < leftNum; + if (isDescending) { + const isGreater = guess < target; + const isInRange = target <= rightNum; + + const isTargetGreater = isGreater && isInRange; + if (isTargetGreater) left = mid + 1; + + const isTargetLess = !(isGreater && isInRange); + if (isTargetLess) right = mid - 1; + } + } + + return -1; +}; diff --git a/javascript/0034-find-first-and-last-position-of-element-in-sorted-array.js b/javascript/0034-find-first-and-last-position-of-element-in-sorted-array.js new file mode 100644 index 000000000..c11490e11 --- /dev/null +++ b/javascript/0034-find-first-and-last-position-of-element-in-sorted-array.js @@ -0,0 +1,47 @@ +/** + * Binary Search + * https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array/ + * Time O(log(n)) | Space O(1) + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +var searchRange = function(nums, target) { + + const result = []; + + result.push(binarySearch(true, nums, target)); + result.push(binarySearch(false, nums, target)); + + return result; +}; + +var binarySearch = (isLeftBias, nums, target) => { + let left = 0; + let right = nums.length - 1; + let index = -1; + + while(left <= right) { + + const mid = (left + right) >> 1; + + if(target > nums[mid]) { + left = mid+1; + } + if(target < nums[mid]) { + right = mid-1; + } + + const isTarget = target === nums[mid]; + if(isTarget) { + if(isLeftBias) { + index = mid; + right = mid - 1; + } else { + index = mid; + left = mid + 1; + } + } + } + return index; +} diff --git a/javascript/0035-search-insert-position.js b/javascript/0035-search-insert-position.js new file mode 100644 index 000000000..a3ce8654d --- /dev/null +++ b/javascript/0035-search-insert-position.js @@ -0,0 +1,23 @@ +/** + * @param {number[]} nums + * @param {number} target + * @return {number} + */ +var searchInsert = function (nums, target) { + let left = 0; + let right = nums.length - 1; + while (left <= right) { + let midIdx = Math.floor((left + right) / 2); + if (target === nums[midIdx]) { + return midIdx; + } + + if (target > nums[midIdx]) { + left = midIdx + 1; + } else { + right = midIdx - 1; + } + } + + return left; +}; diff --git a/javascript/0036-valid-sudoku.js b/javascript/0036-valid-sudoku.js new file mode 100644 index 000000000..dd1256860 --- /dev/null +++ b/javascript/0036-valid-sudoku.js @@ -0,0 +1,134 @@ +/** + * Hash Map - Matrix + * Time O(ROWS * COLS) | Space O(ROWS * COLS) + * https://leetcode.com/problems/valid-sudoku/ + * @param {character[][]} board + * @return {boolean} + */ + + var isValidSudoku = function (board) { + let row = []; + let col = []; + let squares = new Map(); + // Creating new col, row and sqaures Sets + for (let i = 0; i < 9; i++) { + let newRowSet = new Set(); + let newColSet = new Set(); + row.push(newRowSet); + col.push(newColSet); + for (let j = 0; j < 9; j++) { + squares.set(`${Math.floor(i / 3)}:${Math.floor(j / 3)}`, new Set()); + } + } + + for (let i = 0; i < 9; i++) { + for (let j = 0; j < 9; j++) { + if (board[i][j] === '.') { + continue; + } + if ( + row[i].has(board[i][j]) || + col[j].has(board[i][j]) || + squares + .get(`${Math.floor(i / 3)}:${Math.floor(j / 3)}`) + .has(board[i][j]) + ) { + return false; + } + row[i].add(board[i][j]); + col[j].add(board[i][j]); + squares + .get(`${Math.floor(i / 3)}:${Math.floor(j / 3)}`) + .add(board[i][j]); + } + } + return true; +}; + +/** + * Hash Map - Matrix + * Time O(ROWS * COLS) | Space O(ROWS * COLS) + * https://leetcode.com/problems/valid-sudoku/ + * @param {character[][]} board + * @return {boolean} + */ + var isValidSudoku = (board) => { + const boards = 3; + const [ boxes, cols, rows ] = getBoards(boards);/* Time O(ROWS * COLS) | Space O(ROWS * COLS) */ + + return searchGrid(board, boxes, cols, rows); /* Time O(ROWS * COLS) | Space O(ROWS * COLS) */ +}; + +var initBoard = (rows, cols) => new Array(rows).fill() + .map(() => new Array(cols).fill(false)); + +var getBoards = (boards) => { + const [ rows, cols ] = [ 9, 9 ]; + + return new Array(boards).fill() + .map(() => initBoard(rows, cols)) +} + +var searchGrid = (board, boxes, cols, rows) => { + const [ _rows, _cols ] = [ 9, 9 ]; + + for (let row = 0; row < _rows; row++) {/* Time O(ROWS)*/ + for (let col = 0; col < _cols; col++) {/* Time O(COLS)*/ + const char = board[row][col]; + const index = (Math.floor(row / 3) * 3) + Math.floor(col / 3); + + const isEmpty = char === '.'; + if (isEmpty) continue; + + const hasMoved = boxes[index][(char - 1)] || cols[col][(char - 1)] || rows[row][(char - 1)]; + if (hasMoved) return false; + + rows[row][(char - 1)] = true; /* Space O(ROWS * COLS)*/ + cols[col][(char - 1)] = true; /* Space O(ROWS * COLS)*/ + boxes[index][(char - 1)] = true; /* Space O(ROWS * COLS)*/ + } + } + + return true; +} + +/** + * Array - Fixed Size + * Time O(ROWS * COLS) | Space O(CELLS) + * https://leetcode.com/problems/valid-sudoku/ + * @param {character[][]} board + * @return {boolean} + */ + var isValidSudoku = (board) => { + const [ boards, cells ] = [ 3, 9]; + const [ boxes, rows, cols ] = getBoards(boards, cells);/* Time O(ROWS * COLS) | Space O(CELLS) */ + + return searchGrid(board, boxes, rows, cols); /* Time O(ROWS * COLS) | Space O(CELLS) */ +} + +var getBoards = (boards, cells) => new Array(boards).fill() + .map(() => new Array(cells).fill(0)); + +var searchGrid = (board, boxes, rows, cols) => { + const [ _rows, _cols ] = [ 9, 9 ]; + + for (let row = 0; row < _rows; row++) {/* Time O(ROWS)*/ + for (let col = 0; col < _cols; col++) {/* Time O(COLS)*/ + const char = board[row][col]; + const position = 1 << (char - 1); + const index = (Math.floor(row / 3) * 3) + Math.floor(col / 3); + + const isEmpty = char === '.'; + if (isEmpty) continue; + + const hasMoved = (boxes[index] & position) || (cols[col] & position) || (rows[row] & position); + if (hasMoved) return false; + + rows[row] |= position; /* Space O(CELLS)*/ + cols[col] |= position; /* Space O(CELLS)*/ + boxes[index] |= position; /* Space O(CELLS)*/ + } + } + + return true; +} diff --git a/javascript/0039-combination-sum.js b/javascript/0039-combination-sum.js new file mode 100644 index 000000000..4850e5c9c --- /dev/null +++ b/javascript/0039-combination-sum.js @@ -0,0 +1,26 @@ +/** + * https://leetcode.com/problems/combination-sum/ + * Time O(N * ((Target/MIN) + 1)) | Space O(N * (Target/Min)) + * @param {number[]} candidates + * @param {number} target + * @return {number[][]} + */ + var combinationSum = function (candidates, target, index = 0, combination = [], combinations = []) { + const isBaseCase = target < 0; + if (isBaseCase) return combinations; + + const isTarget = target === 0; + if (isTarget) return combinations.push(combination.slice()); + + for (let i = index; i < candidates.length; i++) { + backTrack(candidates, target, i, combination, combinations); + } + + return combinations; +} + +const backTrack = (candidates, target, i, combination, combinations) => { + combination.push(candidates[i]); + combinationSum(candidates, (target - candidates[i]), i, combination, combinations); + combination.pop(); +} \ No newline at end of file diff --git a/javascript/0040-combination-sum-ii.js b/javascript/0040-combination-sum-ii.js new file mode 100644 index 000000000..8002c31db --- /dev/null +++ b/javascript/0040-combination-sum-ii.js @@ -0,0 +1,39 @@ +/** + * https://leetcode.com/problems/combination-sum-ii/ + * Time O(2^N) | Space O(N) + * @param {number[]} candidates + * @param {number} target + * @return {number[][]} + */ + var combinationSum2 = function(candidates, target) { + candidates.sort((a, b) => a - b) + + return dfs(candidates, target) +}; + +const dfs = (candidates, target, index = 0, combination = [], combinations = []) => { + const isBaseCase = target < 0; + if (isBaseCase) return combinations; + + const isTarget = target === 0; + if (isTarget) { + if (combination.length) combinations.push(combination.slice()); + + return combinations + } + + for (let i = index; i < candidates.length; i++) { + const isDuplicate = (index < i) && (candidates[i - 1] === candidates[i]); + if (isDuplicate) continue; + + backTrack(candidates, target, i, combination, combinations); + } + + return combinations; +} + +const backTrack = (candidates, target, i, combination, combinations) => { + combination.push(candidates[i]) + dfs(candidates, (target - candidates[i]), (i + 1), combination, combinations) + combination.pop() +} diff --git a/javascript/0041-first-missing-positive.js b/javascript/0041-first-missing-positive.js new file mode 100644 index 000000000..a22dc1cc8 --- /dev/null +++ b/javascript/0041-first-missing-positive.js @@ -0,0 +1,56 @@ +/** + * Cyclic Sort + * Time O(N) | Space O(1) + * https://leetcode.com/problems/first-missing-positive + * @param {number[]} nums + * @return {number} + */ +var firstMissingPositive = (nums) => { + cyclicSort(nums); + + return search(nums); +}; + +const cyclicSort = (nums, index = 0) => { + while (index < nums.length) { + const num = nums[index]; + const indexKey = (num - 1); + const indexNum = nums[indexKey]; + + if (canSwap(nums, num, indexNum)) { + swap(nums, index, indexKey); + continue; + } + + index += 1; + } +} + +const search = (nums, index = 0) => { + while (index < nums.length) { + const num = nums[index]; + const indexKey = (index + 1); + + if (!isEqual(num, indexKey)) return indexKey; + + index += 1; + } + + return (nums.length + 1); +} + +const canSwap = (nums, num, indexNum) => + isPositive(num) && + isInBound(num, nums) && + !isEqual(num, indexNum); + +const swap = (nums, index, indexKey) => + [nums[index], nums[indexKey]] = [nums[indexKey], nums[index]]; + +const isPositive = (num) => (0 < num); + +const isInBound = (num, nums) => (num <= nums.length); + +const isEqual = (num, indexNum) => (num === indexNum); + + diff --git a/javascript/0042-trapping-rain-water.js b/javascript/0042-trapping-rain-water.js new file mode 100644 index 000000000..299612d4c --- /dev/null +++ b/javascript/0042-trapping-rain-water.js @@ -0,0 +1,96 @@ +/** + * Linear + * Time O(n) | Space O(n) + * https://leetcode.com/problems/trapping-rain-water + * @param {number[]} height + * @return {number} + * + */ +var trap = function(height) { + + const maxLeft = []; + const maxRight = []; + const minLeftRight = []; + + let current = 0; + for(let i = 0; i < height.length; i++) { + maxLeft.push(current); + current = Math.max(current, height[i]); + } + current = 0; + for(let i = height.length - 1; i > -1; i--) { + maxRight.push(current); + current = Math.max(current, height[i]); + } + // because the elements were added reverse. + maxRight.reverse(); + + for(let i = 0; i < height.length; i++) { + const minofLeftRight = Math.min(maxLeft[i],maxRight[i]); + minLeftRight.push(minofLeftRight); + } + + let water = 0; + for(let i = 0; i < height.length; i++) { + if(minLeftRight[i] - height[i] > 0) { + water += minLeftRight[i] - height[i]; + } + } + + return water; +}; + + +/** + * https://leetcode.com/problems/trapping-rain-water/ + * Time O(N) | Space O(1) + * @param {number[]} height + * @return {number} + */ +var trap = function (height) { + let [left, right] = [0, height.length - 1]; + let [maxLeft, maxRight, area] = [0, 0, 0]; + + while (left < right) { + const [leftHeight, rightHeight] = getHeights(height, left, right); + const [leftWindow, rightWindow] = getWindows( + height, + left, + maxLeft, + right, + maxRight + ); + + const isRightGreater = leftHeight <= rightHeight; + if (isRightGreater) { + if (hasNewMax(maxLeft, leftHeight)) maxLeft = leftHeight; + else area += leftWindow; + + left++; + } + + const isRightLess = rightHeight < leftHeight; + if (isRightLess) { + if (hasNewMax(maxRight, rightHeight)) maxRight = rightHeight; + else area += rightWindow; + + right--; + } + } + + return area; +}; + +const hasNewMax = (max, height) => max < height; + +const getHeights = (height, left, right) => [height[left], height[right]]; + +const getWindows = (height, left, maxLeft, right, maxRight) => { + const [leftHeight, rightHeight] = getHeights(height, left, right); + const [leftWindow, rightWindow] = [ + maxLeft - leftHeight, + maxRight - rightHeight, + ]; + + return [leftWindow, rightWindow]; +}; diff --git a/javascript/0043-multiply-strings.js b/javascript/0043-multiply-strings.js new file mode 100644 index 000000000..8a1e007ec --- /dev/null +++ b/javascript/0043-multiply-strings.js @@ -0,0 +1,116 @@ +/** + * Matrix + * Time O(N * M) | Space O(N + M) + * https://leetcode.com/problems/multiply-strings/ + * @param {string} num1 + * @param {string} num2 + * @return {string} + */ +var multiply = (num1, num2) => { + const isZero = ((num1 === '0') || (num2 === '0')); + if (isZero) return '0'; + + const buffer = initBuffer(num1, num2);/* | Space (N + M) */ + + multiplication(num1, num2, buffer) /* Time O(N * M) */ + removeLeadingZero(buffer); /* Time O(N + M) | Time O(N + M)*/ + + return buffer.join(''); /* Time O(N + M) | Space O(N + M) */ +}; + +var initBuffer = (num1, num2) => { + const size = (num1.length + num2.length); + + return new Array(size).fill(0);/* Space (N + M) */ +} + +var multiplication = (num1, num2, buffer) => { + for (let i = (num1.length - 1); (0 <= i); i--) {/* Time O(N) */ + for (let j = (num2.length - 1); (0 <= j); j--) {/* Time O(M) */ + update(num1, i, num2, j, buffer); /* Space O(N + M) */ + } + } +} + +var removeLeadingZero = (buffer) => { + const isLeadZero = (buffer[0] === 0); + if (!isLeadZero) return; + + buffer.shift();/* Time O(N + M) | Time O(N + M) */ +} + +var update = (num1, i, num2, j, buffer) => { + const curPos = (i + j); + const prevPos = curPos + 1; + + const carry = buffer[prevPos]; + const product = getProduct(num1, i, num2, j); + const sum = (carry + product); + + const remainder = (sum % 10); + const value = ((sum - remainder) / 10); + + buffer[prevPos] = remainder;/* Space O(N + M) */ + buffer[curPos] += value; /* Space O(N + M) */ +} + +var getProduct = (num1, i, num2, j) => { + const [ iNum, jNum ] = [ Number(num1[i]), Number(num2[j]) ]; + + return (iNum * jNum); +} + +/** + * Matrix + * Time O(N * M) | Space O(N + M) + * https://leetcode.com/problems/multiply-strings/ + * @param {string} num1 + * @param {string} num2 + * @return {string} + */ +var multiply = (num1, num2) => { + const isZero = ((num1 === '0') || (num2 === '0')); + if (isZero) return '0'; + + const buffer = initBuffer(num1, num2);/* | Space O(N + M) */ + + multiplication(num1, num2, buffer); /* Time O(N * M) | Space O(N + M) */ + removeLeadingZero(buffer); /* Time O(N + M) | Space O(N + M) */ + + return buffer.join(''); /* Time O(N + M) | Space O(N + M) */ +}; + +var initBuffer = (num1, num2) => new Array(num1.length + num2.length).fill(0);/* Space O(N + M) */ + +var multiplication = (num1, num2, buffer) => { + [ num1, num2 ] = /* Time O(N + M) */ + [ reverse(num1), reverse(num2) ]; + + for (var i1 in num1) {/* Time O(N) */ + for (var i2 in num2) {/* Time O(M) */ + update(num1, i1, num2, i2, buffer);/* Space O(N + M) */ + } + } + + buffer.reverse();/* Time O(N + M) */ +} + +const reverse = (s) => s + .split('') /* Time O(K) | Space O (K) */ + .reverse();/* Time O(K) */ + +var update = (num1, i1, num2, i2, buffer) => { + const product = num1[i1] * num2[i2]; + const index = Number(i1) + Number(i2); + + buffer[index] += product; /* Space O(N + M) */ + buffer[(index + 1)] += Math.floor(buffer[index] / 10);/* Space O(N + M) */ + buffer[index] = (buffer[index] % 10); /* Space O(N + M) */ +} + +var removeLeadingZero = (buffer) => { + const isZero = (buffer[0] === 0); + if (!isZero) return; + + buffer.shift();/* Time O(N + M) | Space O(N + M) */ +} \ No newline at end of file diff --git a/javascript/0045-jump-game-ii.js b/javascript/0045-jump-game-ii.js new file mode 100644 index 000000000..1fe87cf54 --- /dev/null +++ b/javascript/0045-jump-game-ii.js @@ -0,0 +1,47 @@ +/** + * https://leetcode.com/problems/jump-game-ii/ + * Time O(N) | Space O(1) + * @param {number[]} nums + * @return {number} + */ +var jump = function (nums) { + let [ left, right, jumps ] = [ 0, 0, 0 ]; + + while (right < nums.length - 1) { + const maxReach = getMaxReach(nums, left, right); + + left = right + 1; + right = maxReach; + jumps += 1; + } + + return jumps; +}; + +const getMaxReach = (nums, left, right, maxReach = 0) => { + for (let i = left; i < right + 1; i++) { + const reach = nums[i] + i; + maxReach = Math.max(maxReach, reach); + } + + return maxReach; +} + +/** + * https://leetcode.com/problems/jump-game-ii/ + * Time O(N) | Space O(1) + * @param {number[]} nums + * @return {number} + */ + var jump = function(nums) { + let [ jumps, currentJumpEnd, farthest ] = [ 0, 0, 0]; + + for (let i = 0; i < nums.length - 1; i++) { + farthest = Math.max(farthest, (i + nums[i])); + + const canJump = i === currentJumpEnd + if (canJump) { jumps++; currentJumpEnd = farthest; } + } + + return jumps; +} \ No newline at end of file diff --git a/javascript/0046-permutations.js b/javascript/0046-permutations.js new file mode 100644 index 000000000..aec6eda35 --- /dev/null +++ b/javascript/0046-permutations.js @@ -0,0 +1,66 @@ +/** + * https://leetcode.com/problems/permutations/solution/ + * Time O(N!) | Space(N!) + * @param {number[]} nums + * @return {number[][]} + */ + var permute = function(nums) { + return dfs(nums) +} + +var dfs = function(nums, permutation = [], permutations = []) { + const isBaseCase = nums.length === permutation.length + if (isBaseCase) return permutations.push(permutation.slice()) + + for (let i = 0; i < nums.length; i++) { + if (permutation.includes(nums[i])) continue; + + backTrack(nums, i, permutation, permutations); + } + + return permutations; +} + +const backTrack = (nums, i, permutation, permutations) => { + permutation.push(nums[i]) + dfs(nums, permutation, permutations) + permutation.pop() +} + +/** + * https://leetcode.com/problems/permutations/solution/ + * Time O(N!) | Space(N!) + * @param {number[]} nums + * @return {number[][]} + */ +var permute = function(nums) { + return bfs(nums) +} + +const bfs = (nums, levels = [[]], permutations = []) => { + for (const num of nums) { + for (let i = (levels.length - 1); 0 <= i; i--) { + const previousLevel = levels.shift() + + for (let index = 0; index < (previousLevel.length + 1); index++) { + const level = reArrangeSet(previousLevel, num, index) + + const isBaseCase = level.length === nums.length; + if (isBaseCase) { + permutations.push(level); + continue + } + + levels.push(level) + } + } + } + + return permutations +} + +const reArrangeSet = (previousLevel, num, index) => { + const [ before, after ] = [ previousLevel.slice(0, index), previousLevel.slice(index) ] + + return [...before, num, ...after] +} diff --git a/javascript/0048-rotate-image.js b/javascript/0048-rotate-image.js new file mode 100644 index 000000000..5dde51336 --- /dev/null +++ b/javascript/0048-rotate-image.js @@ -0,0 +1,61 @@ +/** + * Time O(ROWS * COLS) | Space O(1) + * https://leetcode.com/problems/rotate-image/ + * @param {number[][]} matrix + * @return {void} Do not return anything, modify matrix in-place instead. + */ + var rotate = (matrix) => { + transpose(matrix);/* Time O(ROWS * COLS) */ + reflect(matrix); /* Time O(ROWS * COLS) */ +}; + +var transpose = (matrix) => { + const rows = matrix.length; + + for (let row = 0; (row < rows); row++) {/* Time O(ROWS) */ + for (let col = (row + 1); (col < rows); col++) {/* Time O(COLS) */ + swap1(matrix, row, col); + } + } +}; + +var swap1 = (matrix, row, col) => [matrix[row][col], matrix[col][row]] = [matrix[col][row], matrix[row][col]]; + +var reflect = (matrix) => { + const rows = matrix.length; + + for (let row = 0; (row < rows); row++) {/* Time O(ROWS) */ + for (let col = 0; (col < (rows / 2)); col++) {/* Time O(COLS) */ + const reflection = ((rows - col) - 1); + + swap2(matrix, row, col, reflection); + } + } +} + +var swap2 = (matrix, row, col, reflection) => [matrix[row][col], matrix[row][reflection]] = [matrix[row][reflection], matrix[row][col]]; + +/** + * Time O(ROWS * COLS) | Space O(1) + * https://leetcode.com/problems/rotate-image/ + * @param {number[][]} matrix + * @return {void} Do not return anything, modify matrix in-place instead. + */ + var rotate = (matrix) => { + reverse(matrix); /* Time O(ROWS) */ + transpose(matrix);/* Time O(ROWS * COLS) */ +}; + +var reverse = (matrix) => matrix.reverse(); + +var transpose = (matrix) => { + const rows = matrix.length; + + for (let row = 0; (row < rows); row++) {/* Time O(ROWS) */ + for (let col = 0; (col < row); col++) {/* Time O(COLS) */ + swap(matrix, row, col); + } + } +} + +var swap = (matrix, row, col) => [matrix[row][col], matrix[col][row]] = [matrix[col][row], matrix[row][col]]; \ No newline at end of file diff --git a/javascript/0049-group-anagrams.js b/javascript/0049-group-anagrams.js new file mode 100644 index 000000000..409d31a36 --- /dev/null +++ b/javascript/0049-group-anagrams.js @@ -0,0 +1,73 @@ +/** + * Sort - HeapSort Space O(1) | QuickSort Space O(log(K)) + * Hash Map - Adjacency List + * Time O(N * (K * log(K))) | Space O(N * K) + * https://leetcode.com/problems/group-anagrams/ + * @param {string[]} strs + * @return {string[][]} + */ +var groupAnagrams = (words, map = new Map()) => { + if (!words.length) return []; + + groupWords(words, map); /* Time O(N * (K * log(K)) | Space O(N * K) */ + + return [ ...map.values() ];/* Time O(N) | Space O(N * K) */ +}; + +var groupWords = (words, map) => { + for (const original of words) {/* Time O(N) */ + const sorted = reorder(original);/* Time O(K * log(K)) | Space O(K) */ + const values = map.get(sorted) || []; + + values.push(original); /* | Space O(N) */ + map.set(sorted, values); /* | Space O(N * K) */ + } +} + +const reorder = (str) => str + .split('') /* Time O(K) | Space O(K) */ + .sort((a, b) => a.localeCompare(b))/* Time O(K * log(K)) | Space O(1 || log(K)) */ + .join(''); /* Time O(K) | Space O(K) */ + +/** + * Hash Map + * Time O(N * K) | Space O(N * K) + * https://leetcode.com/problems/group-anagrams/ + * @param {string[]} words + * @return {string[][]} + */ +var groupAnagrams = (words, map = new Map()) => { + if (!words.length) return []; + + groupWords(words, map); /* Time O(N * K) | Space O(N * K) */ + + return [ ...map.values() ];/* Time O(N) | Space O(N * K) */ +} + +var groupWords = (words, map) => { + for (const original of words) {/* Time O(N) */ + const hash = getHash(original); /* Time O(K) | Space O(1) */ + const values = map.get(hash) || []; + + values.push(original); /* | Space O(N) */ + map.set(hash, values); /* | Space O(N * K) */ + } +} + +const getHash = (word) => { + const frequency = new Array(26).fill(0); + + for (const char of word) {/* Time O(K) */ + const charCode = getCode(char);/* Time O(1) | Space (1) */ + + frequency[charCode]++; /* | Space O(1) */ + } + + return buildHash(frequency) +} + +const getCode = (char) => char.charCodeAt(0) - 'a'.charCodeAt(0); + +const buildHash = (frequency) => frequency.toString(); + + diff --git a/javascript/0050-powx-n.js b/javascript/0050-powx-n.js new file mode 100644 index 000000000..8e8c03b7a --- /dev/null +++ b/javascript/0050-powx-n.js @@ -0,0 +1,142 @@ +/** + * Brute Force - Multiply + * Time O(N) | Space O(1) + * https://leetcode.com/problems/powx-n/ + * @param {number} x + * @param {number} n + * @return {number} + */ + var myPow = (x, n) => { + if (n < 0) { + x = (1 / x); + n = (-n); + } + + return getPow(x, n);/* Time O(N) */ +} + +var getPow = (x, n, pow = 1) => { + for (let i = 0; i < n; i++) {/* Time O(N) */ + pow = pow * x; + } + + return pow; +} + +/** + * DFS + * Time (log(N)) | Space O(log(N)) + * https://leetcode.com/problems/powx-n/ + * @param {number} x + * @param {number} n + * @return {number} + */ +var myPow = (x, n) => { + const isBaseCase1 = ((x === 1.0) || (n === 0)); + if (isBaseCase1) return 1; + + const isBaseCase2 = (n === 1); + if (isBaseCase2) return x; + + const isEven = ((n % 2) === 0); + if (isEven) return myPow((x * x), (n / 2));/* Time O(log(N)) | Space O(log(N)) */ + + const isOdd = ((n % 2) === 1); + if (isOdd) return (x * myPow(x, (n - 1)));/* Time O(log(N)) | Space O(log(N)) */ + + return (1 / myPow(x, -n)); +}; + +/** + * DFS + * Time (log(N)) | Space O(log(N)) + * https://leetcode.com/problems/powx-n/ + * @param {number} x + * @param {number} n + * @return {number} + */ +var myPow = (x, n) => { + const isBaseCase = (n === 0); + if (isBaseCase) return 1; + + const abs = Math.abs(n); + const isEven = ((abs % 2) === 0); + + const power = isEven + ? myPow((x * x), (abs / 2)) /* Time O(log(N)) | Space O(log(N)) */ + : (myPow((x * x), ((abs - 1) / 2)) * x);/* Time O(log(N)) | Space O(log(N)) */ + + const isNegative = (n < 0); + + return isNegative + ? (1 / power) + : power; +}; + +/** + * Fast Power - Recursive + * Time O(log(N)) | Space O(log(N)) + * https://leetcode.com/problems/powx-n/ + * @param {number} x + * @param {number} n + * @return {number} + */ + var myPow = (x, n) => { + if (n < 0) { + x = 1 / x; + n = -n; + } + + return fastPow(x, n);/* Time O(log(N)) | Space O(log(N)) */ +} + +var fastPow = (x, n) => { + const isBaseCase = n === 0; + if (isBaseCase) return 1.0; + + const half = fastPow(x, n / 2);/* Time O(log(N)) | Space O(log(N)) */ + + const isEven = ((n % 2) === 0); + if (isEven) return (half * half); + + const isOdd = ((n % 2) === 1); + if (isOdd) return ((half * half) * x); +} + +/** + * Fast Power - Iterative + * Time O(log(N)) | Space O(1) + * https://leetcode.com/problems/powx-n/ + * @param {number} x + * @param {number} n + * @return {number} + */ + var myPow = (x, n) => { + if (n < 0) { + x = (1 / x); + n = (-n); + } + + let [ pow, product ] = [ 1, x ]; + + for (let i = n; (0 < i); i = (i >> 1)) {/* Time O(log(N)) */ + const isOdd = ((i % 2) === 1); + if (isOdd) pow = (pow * product); + + product = (product * product); + } + + return pow; +} + + /** + * Number - Math + * Time O(1) | Space O(1) + * https://leetcode.com/problems/powx-n/ + * @param {number} x + * @param {number} n + * @return {number} + */ + var myPow = (x, n) => { + return Math.pow(x,n).toFixed(5); +} diff --git a/javascript/0051-n-queens.js b/javascript/0051-n-queens.js new file mode 100644 index 000000000..ad9d7a37b --- /dev/null +++ b/javascript/0051-n-queens.js @@ -0,0 +1,45 @@ +/** + * https://leetcode.com/problems/n-queens/ + * Time O(N!) | Space O(N^2) + * @param {number} n + * @return {string[][]} + */ +function solveNQueens(n, colSet = new Set(), posDiagSet = new Set(), negDiagSet = new Set()) { + const board = new Array(n).fill().map(() => new Array(n).fill('.')); + + return dfs(board, n, colSet, posDiagSet, negDiagSet); +} + +const dfs = (board, n, colSet, posDiagSet, negDiagSet, row = 0, moves = []) => { + const isBaseCase = row === n; + if (isBaseCase) { + const rows = board.map((_row) => _row.join('')) + + moves.push(rows); + + return moves; + } + + for (let col = 0; col < n; col++) { + const hasQueen = colSet.has(col) || posDiagSet.has(row + col) || negDiagSet.has(row - col) + if (hasQueen) continue; + + backTrack(board, n, row, col, colSet, posDiagSet, negDiagSet, moves); + } + + return moves +} + +const backTrack = (board, n, row, col, colSet, posDiagSet, negDiagSet, moves) => { + colSet.add(col); + posDiagSet.add(row + col); + negDiagSet.add(row - col); + board[row][col] = "Q"; + + dfs(board, n, colSet, posDiagSet, negDiagSet, (row + 1), moves); + + colSet.delete(col); + posDiagSet.delete(row + col); + negDiagSet.delete(row - col); + board[row][col] = "."; +} diff --git a/javascript/0052-n-queens-ii.js b/javascript/0052-n-queens-ii.js new file mode 100644 index 000000000..c29cd1773 --- /dev/null +++ b/javascript/0052-n-queens-ii.js @@ -0,0 +1,33 @@ +var totalNQueens = function (n) { + let col = new Set(); + let posDiag = new Set(); // (r + c) + let negDiag = new Set(); // (r - c) + + let board = new Array(n).fill().map(() => new Array(n).fill('.')); + let res = 0; + + function backtrack(r) { + if (r === n) { + res += 1; + return; + } + + for (let c = 0; c < n; c++) { + if (col.has(c) || posDiag.has(r + c) || negDiag.has(r - c)) { + continue; + } + + col.add(c); + posDiag.add(r + c); + negDiag.add(r - c); + + backtrack(r + 1); + + col.delete(c); + posDiag.delete(r + c); + negDiag.delete(r - c); + } + } + backtrack(0); + return res; +}; diff --git a/javascript/0053-maximum-subarray.js b/javascript/0053-maximum-subarray.js new file mode 100644 index 000000000..3280515a9 --- /dev/null +++ b/javascript/0053-maximum-subarray.js @@ -0,0 +1,76 @@ +/** + * https://leetcode.com/problems/maximum-subarray/ + * Time O(N^2) | Space O(1) + * @param {number[]} nums + * @return {number} + */ + var maxSubArray = function(nums, maxSum = -Infinity) { + for (let i = 0, sum = 0; i < nums.length; i++) { + for (let j = i; j < nums.length; j++) { + sum += nums[j]; + maxSum = Math.max(maxSum, sum); + } + } + + return maxSum; +} + +/** + * https://leetcode.com/problems/maximum-subarray/ + * Time O(N * log(N)) | Space O(log(N)) + * @param {number[]} nums + * @return {number} + */ +var maxSubArray = function(nums, left = 0, right = nums.length - 1) { + const isBaseCase = (right < left) + if (isBaseCase) return -Infinity; + + const mid = (left + right) >> 1; + const guess = nums[mid]; + const leftSum = getLeftSumFromMid(nums, mid, left) + const rightSum = getRightSumFromMid(nums, mid, right) + const sum = guess + leftSum + rightSum; + + const leftHalf = maxSubArray(nums, left, (mid - 1)); + const rightHalf = maxSubArray(nums, (mid + 1), right); + + return Math.max(sum, leftHalf, rightHalf); +} + +const getLeftSumFromMid = (nums, mid, left, sum = 0, maxSum = 0) => { + for (let i = (mid - 1); left <= i; i--) { + sum += nums[i]; + maxSum = Math.max(maxSum, sum); + } + + return maxSum; +} + +const getRightSumFromMid = (nums, mid, right, sum = 0, maxSum = 0) => { + for (let i = (mid + 1); i <= right; i++) { + sum += nums[i]; + maxSum = Math.max(maxSum, sum); + } + + return maxSum; +} + +/** + * https://leetcode.com/problems/maximum-subarray/ + * Time O(N) | Space O(1) + * @param {number[]} nums + * @return {number} + */ +var maxSubArray = function(nums) { + let [ runningSum, maxSum ] = [ nums[0], nums[0] ] + + for (let i = 1; i < nums.length; i++) { + const num = nums[i] + const sum = runningSum + num + + runningSum = Math.max(num, sum) + maxSum = Math.max(maxSum, runningSum) + } + + return maxSum +}; \ No newline at end of file diff --git a/javascript/0054-spiral-matrix.js b/javascript/0054-spiral-matrix.js new file mode 100644 index 000000000..9eb94dcb4 --- /dev/null +++ b/javascript/0054-spiral-matrix.js @@ -0,0 +1,219 @@ +/** + * Matrix - Spiral Traversal Pre Update + * Array - Ignore Auxilary Space O(ROWS * COLS) + * Time O(ROWS * COLS) | Space O(1) + * https://leetcode.com/problems/spiral-matrix/ + * @param {number[][]} matrix + * @return {number[]} + */ + var spiralOrder = function(matrix, order = []) { + const [ rows, cols ] = [ (matrix.length - 1), (matrix[0].length - 1) ]; + let [ top, bot, left, right ] = [ 0, rows, 0, cols ]; + + const isInBounds = () => ((left <= right) && (top <= bot)); + while (isInBounds()) {/* Time O(ROWS * COLS) */ + addTop( + matrix, top, bot, left, right, order + ); /* Time O(COLS) | Ignore Auxilary Spsace O(ROWS * COLS) */ + top++; + + addRight( + matrix, top, bot, left, right, order + ); /* Time O(ROWS) | Ignore Auxilary Spsace O(ROWS * COLS) */ + right--; + + const hasRow = (top <= bot); + if (hasRow) { + addBot( + matrix, top, bot, left, right, order + ); /* Time O(COLS) | Ignore Auxilary Spsace O(ROWS * COLS) */ + bot--; + } + + const hasCol = (left <= right); + if (hasCol) { + addLeft( + matrix, top, bot, left, right, order + ); /* Time O(ROWS) | Ignore Auxilary Spsace O(ROWS * COLS) */ + left++; + } + } + + return order; +}; + +var addTop = (matrix, top, bot, left, right, order) => { + for (let col = left; col <= right; col++) {/* Time O(COLS) */ + order.push(matrix[top][col]); /* Ignore Auxilary Spsace O(ROWS * COLS) */ + } +} + +var addRight = (matrix, top, bot, left, right, order) => { + for (let row = top; row <= bot; row++) {/* Time O(ROWS) */ + order.push(matrix[row][right]); /* Ignore Auxilary Spsace O(ROWS * COLS) */ + } +} + +var addBot = (matrix, top, bot, left, right, order) => { + for (let col = right; left <= col; col--) {/* Time O(COLS) */ + order.push(matrix[bot][col]); /* Ignore Auxilary Spsace O(ROWS * COLS) */ + } +} + +var addLeft = (matrix, top, bot, left, right, order) => { + for (let row = bot; top <= row; row--) {/* Time O(ROWS) */ + order.push(matrix[row][left]); /* Ignore Auxilary Spsace O(ROWS * COLS) */ + } +} + +/** + * Matrix - Spiral Traversal Post Update + * Array - Ignore Auxilary Space O(ROWS * COLS) + * Time O(ROWS * COLS) | Space O(1) + * https://leetcode.com/problems/spiral-matrix/ + * @param {number[][]} matrix + * @return {number[]} + */ +var spiralOrder = (matrix, order = []) => { + const [ rows, cols ] = [ matrix.length, matrix[0].length ]; + const cells = (rows * cols); + let [ top, bot, left, right ] = [ 0, (rows - 1), 0, (cols - 1) ]; + + while (order.length < cells) {/* Time O(ROWS * COLS) */ + traverse( + matrix, top, bot, left, right, order + ); /* Time O(ROWS * COLS) | Ignore Auxilary Spsace O(ROWS * COLS) */ + + top++; bot--; + left++; right--; + } + + return order; +} + +var traverse = (matrix, top, bot, left, right, order) => { + addTop(matrix, top, bot, left, right, order); /* Time O(COLS) | Ignore Auxilary Spsace O(ROWS * COLS) */ + addRight(matrix, top, bot, left, right, order);/* Time O(ROWS) | Ignore Auxilary Spsace O(ROWS * COLS)*/ + addBot(matrix, top, bot, left, right, order); /* Time O(COLS) | Ignore Auxilary Spsace O(ROWS * COLS)*/ + addLeft(matrix, top, bot, left, right, order); /* Time O(ROWS) | Ignore Auxilary Spsace O(ROWS * COLS. */ +} + +var addTop = (matrix, top, bot, left, right, order) => { + for (let col = left; (col <= right); col++) {/* Time O(COLS) */ + order.push(matrix[top][col]); /* Ignore Auxilary Spsace O(ROWS * COLS) */ + } +} + +var addRight = (matrix, top, bot, left, right, order) => { + for (let row = (top + 1); (row <= bot); row++) {/* Time O(ROWS) */ + order.push(matrix[row][right]); /* Ignore Auxilary Spsace O(ROWS * COLS) */ + } +} + +var addBot = (matrix, top, bot, left, right, order) => { + for (let col = (right - 1); (left <= col); col--) {/* Time O(COLS) */ + const isOutOfBounds = top === bot; + if (isOutOfBounds) return; + + order.push(matrix[bot][col]); /* Ignore Auxilary Spsace O(ROWS * COLS) */ + } +} + +var addLeft = (matrix, top, bot, left, right, order) => { + for (let row = bot - 1; row >= top + 1; row--) {/* Time O(ROWS) */ + const isOutOfBounds = left === right; + if (isOutOfBounds) return; + + order.push(matrix[row][left]); /* Ignore Auxilary Spsace O(ROWS * COLS) */ + } +} + +/** + * Matrix - Mark Visited In Place + * Array - Ignore Auxilary Space O(ROWS * COLS) + * Time O(ROWS * COLS) | Space O(1) + * https://leetcode.com/problems/spiral-matrix/ + * @param {number[][]} matrix + * @return {number[]} + */ + var spiralOrder = (matrix) => { + const order = initOrder(matrix);/* | Ignore Auxilary Spsace O(ROWS * COLS) */ + + spiral(matrix, order); /* Time O(ROWS * COLS) | Ignore Auxilary Spsace O(ROWS * COLS) */ + + return order; +} + +const initOrder = (matrix, VISITED = 101) => { + const order = [ matrix[0][0] ];/* Ignore Auxilary Spsace O(ROWS * COLS) */ + + matrix[0][0] = VISITED; /* Ignore Auxilary Spsace O(ROWS * COLS) */ + + return order; +} + +var spiral = (matrix, order) => { + let [ row, col, direction, changeDirection ] = [ 0, 0, 0, 0 ]; + + while (changeDirection < 2) { /* Time O(ROWS * COLS) | Ignore Auxilary Spsace O(ROWS * COLS) */ + [ row, col, direction, changeDirection ] =/* Time O(ROWS * COLS) | Ignore Auxilary Spsace O(ROWS * COLS) */ + getPointers(matrix, row, col, direction, changeDirection, order); + } +} + +const getPointers = (matrix, row, col, direction, changeDirection, order) => { + [ row, col, direction, changeDirection ] =/* Time O(ROWS * COLS) | Ignore Auxilary Spsace O(ROWS * COLS) */ + move(matrix, row, col, direction, changeDirection, order); + + direction = ((direction + 1) % 4); + changeDirection += 1; + + return [ row, col, direction, changeDirection ]; +} + +const move = (matrix, row, col, direction, changeDirection, order, VISITED = 101) => { + const [ rows, cols ] = [ matrix.length, matrix[0].length ]; + + while (canMove(matrix, row, rows, col, cols, direction)) {/* Time O(ROWS * COLS) */ + [ row, col ] = getCell(row, col, direction); + + order.push(matrix[row][col]); /* | Ignore Auxilary Spsace O(ROWS * COLS) */ + matrix[row][col] = VISITED; + + changeDirection = 0; + } + + return [ row, col, direction, changeDirection ]; +} + +const canMove = (matrix, row, rows, col, cols, direction) => { + if (!isInBounds(row, rows, col, cols, direction)) return false; + + return !hasSeen(matrix, row, col, direction) +} + +const isInBounds = (row, rows, col, cols, direction) => { + const [ _row, _col ] = getCell(row, col, direction); + const isRowInBounds = ((0 <= _row) && (_row < rows)); + const isColInBounds = ((0 <= _col) && (_col < cols)); + + return (isRowInBounds && isColInBounds); +} + +const hasSeen = (matrix, row, col, direction, VISITED = 101) => { + const [ _row, _col ] = getCell(row, col, direction); + + return (matrix[_row][_col] === VISITED); +} + +const getDirection = (direction) => { + const directions = [ [0, 1], [1, 0], [0, -1], [-1, 0] ]; + /* RIGHT BOT LEFT TOP */ + return directions[direction]; +} + +const getCell = (row, col, direction) => { + const [ _row, _col ] = getDirection(direction); + + return [ (row + _row), (col + _col) ]; +} diff --git a/javascript/0055-jump-game.js b/javascript/0055-jump-game.js new file mode 100644 index 000000000..7441763d8 --- /dev/null +++ b/javascript/0055-jump-game.js @@ -0,0 +1,98 @@ +/** + * Time O(2^N) | Space O(N) + * @param {number[]} nums + * @return {boolean} + */ + var canJump = (nums, index = 0) => { + const isBaseCase = index === nums.length - 1; + if (isBaseCase) return true; + + const furthestJump = Math.min(index + nums[index], (nums.length - 1)); + for (let nextIndex = (index + 1); nextIndex <= furthestJump; nextIndex++) { + if (canJump(nums, nextIndex)) return true; + } + + return false; +} + +/** + * Time O(N^2) | Space O(N) + * @param {number[]} nums + * @return {boolean} + */ +var canJump = (nums) => { + const memo = new Array(nums.length).fill(0); + memo[memo.length - 1] = 1; + + return canJumpFromIndex(nums, memo); +} + +const canJumpFromIndex = (nums, memo, index = 0) => { + if (memo[index] !== 0) return memo[index] === 1; + + const furthestJump = Math.min(index + nums[index], nums.length - 1); + for (let nextIndex = (index + 1); nextIndex <= furthestJump; nextIndex++) { + if (!canJumpFromIndex(nums, memo, nextIndex)) continue + + memo[index] = 1; + return true; + } + + memo[index] = -1; + return false; +} + +/** + * Time O(N^2) | Space O(N) + * @param {number[]} nums + * @return {boolean} + */ +var canJump = (nums) => { + const memo = new Array(nums.length).fill(0) + memo[memo.length - 1] = 1; + + for (let i = (nums.length - 2); 0 <= i; i--) { + const furthestJump = Math.min(i + nums[i], nums.length - 1); + for (let j = (i + 1); j <= furthestJump; j++) { + const isGood = memo[j] === 1 + if (isGood) { memo[i] = 1; break; } + } + } + + return memo[0] === 1; +} + +/** + * Time O(N) | Space O(1) + * @param {number[]} nums + * @return {boolean} + */ +var canJump = (nums, max = 0, index = 0) => { + while (index < nums.length) { + const num = nums[index] + const jumps = num + index + + const canNotReachEnd = max < index + if (canNotReachEnd) return false + + max = Math.max(max, jumps) + index++ + } + + return true +} + +/** + * Time O(N) | Space O(1) + * @param {number[]} nums + * @return {boolean} + */ +var canJump = (nums, right = nums.length - 1) => { + for (let i = right; 0 <= i; i--) { + const isJumpable = right <= (i + nums[i]) + if (isJumpable) right = i; + } + + return right === 0; +} + diff --git a/javascript/0056-merge-intervals.js b/javascript/0056-merge-intervals.js new file mode 100644 index 000000000..9ef5779d0 --- /dev/null +++ b/javascript/0056-merge-intervals.js @@ -0,0 +1,33 @@ +/** + * https://leetcode.com/problems/merge-intervals/ + * Time O(N * logN) | Space O(N) + * @param {number[][]} intervals + * @return {number[][]} + */ +var merge = function (intervals) { + intervals.sort(([aStart, aEnd], [bStart, bEnd]) => + aStart !== bStart ? aStart - bStart : aEnd - bEnd + ); + + return mergerInterval(intervals); +}; + +const mergerInterval = (intervals, merged = []) => { + let prev = intervals.shift(); + + for (const curr of intervals) { + const [prevStart, prevEnd] = prev; + const [currStart, currEnd] = curr; + + const hasOverlap = currStart <= prevEnd; + if (hasOverlap) { + prev[1] = Math.max(prev[1], curr[1]); + continue; + } + + merged.push(prev); + prev = curr; + } + + return [...merged, prev]; +}; diff --git a/javascript/0057-insert-interval.js b/javascript/0057-insert-interval.js new file mode 100644 index 000000000..0b03a8a9b --- /dev/null +++ b/javascript/0057-insert-interval.js @@ -0,0 +1,46 @@ +/** + * https://leetcode.com/problems/insert-interval/ + * Time O(N) | Space O(N) + * @param {number[][]} intervals + * @param {number[]} newInterval + * @return {number[][]} + */ +var insert = function (intervals, newInterval) { + const { beforeIndex, before } = getBefore(intervals, newInterval); + const afterIndex = mergeIntervals(intervals, newInterval, beforeIndex); + const after = intervals.slice(afterIndex); + + return [...before, newInterval, ...after]; +}; + +const getBefore = (intervals, newInterval, index = 0, before = []) => { + const hasGap = ([prevStart, prevEnd], [currStart, currEnd]) => + prevEnd < currStart; + + while (index < intervals.length && hasGap(intervals[index], newInterval)) { + const current = intervals[index]; + + before.push(current); + index++; + } + + return { beforeIndex: index, before }; +}; + +const mergeIntervals = (intervals, newInterval, index) => { + const hasOverlap = ([prevStart, prevEnd], [currStart, currEnd]) => + currStart <= prevEnd; + + while ( + index < intervals.length && + hasOverlap(newInterval, intervals[index]) + ) { + const current = intervals[index]; + + newInterval[0] = Math.min(newInterval[0], current[0]); + newInterval[1] = Math.max(newInterval[1], current[1]); + index++; + } + + return index; +}; diff --git a/javascript/0058-length-of-last-word.js b/javascript/0058-length-of-last-word.js new file mode 100644 index 000000000..4ecad082f --- /dev/null +++ b/javascript/0058-length-of-last-word.js @@ -0,0 +1,33 @@ +/** + * @param {string} s + * @return {number} + */ + var lengthOfLastWord = function(s) { + let len = 0; + + for(let i in s) { + if(s[i] != ' ') { + if(s[i-1] == ' ') len = 1; + else len += 1; + } + } + return len; +}; + +// another approach. starting out from the last so we don't have to go all the way to the end. +var lengthOfLastWord = function(s) { + + let firstCharOccurance = false; + let lastWordLen = 0; + + for(let i = s.length - 1; i > -1; i--) { + if(s[i] !== ' ') { + firstCharOccurance = true; + lastWordLen++; + } + if(firstCharOccurance && s[i] === ' ') { + break; + } + } + return lastWordLen; +}; diff --git a/javascript/0059-spiral-matrix-ii.js b/javascript/0059-spiral-matrix-ii.js new file mode 100644 index 000000000..a644bb094 --- /dev/null +++ b/javascript/0059-spiral-matrix-ii.js @@ -0,0 +1,38 @@ +/** + * https://leetcode.com/problems/spiral-matrix-ii/ + * @param {number} n + * @return {number[][]} + */ +var generateMatrix = function(n) { + let matrix = Array.from(Array(n),() => Array(n)); + + let top = 0,bottom = n-1,left = 0,right = n-1; + let element = 1; + + while(top <= bottom && left <= right){ + + for(let i = left;i <= right; i++){ + matrix[top][i] = element++; + } + top++; + + for(let i = top;i <= bottom; i++){ + matrix[i][right] = element++; + } + right--; + + for(let i = right; i>= left; i--){ + matrix[bottom][i] = element++; + } + bottom--; + + for(let i = bottom;i >= top; i--){ + matrix[i][left] = element++; + } + left++; + } + return matrix; +}; + +// Runtime: 54 ms, 0.62% of solutions used 54 ms of runtime for spiral matrix ii. +// Memory Usage: 41.9 MB, 7.07% of solutions used 41.9 MB of memory for spiral matrix ii. diff --git a/javascript/0062-unique-paths.js b/javascript/0062-unique-paths.js new file mode 100644 index 000000000..9859331d3 --- /dev/null +++ b/javascript/0062-unique-paths.js @@ -0,0 +1,123 @@ +/** + * Brute Force - DFS + * Time O(2^N) | Space O(HEIGHT) + * https://leetcode.com/problems/unique-paths/ + * @param {number} m + * @param {number} n + * @return {number} + */ + var uniquePaths = (row, col) => { + const isBaseCase = ((row == 1) || (col == 1)); + if (isBaseCase) return 1; + + return dfs(row, col);/* Time O(2^N) | Space O(HEIGHT) */ +} + +var dfs = (row, col) => { + const left = uniquePaths((row - 1), col); /* Time O(2^N) | Space O(HEIGHT) */ + const right = uniquePaths(row, (col - 1));/* Time O(2^N) | Space O(HEIGHT) */ + + return (left + right); +} + +/** + * DP - Top Down + * Matrix - Memoization + * Time O(ROWS * COLS) | Space O(ROWS * COLS) + * https://leetcode.com/problems/unique-paths/ + * @param {number} m + * @param {number} n + * @return {number} + */ +var uniquePaths = (row, col, memo = getMemo(row, col)) => { + const isBaseCase = ((row === 1) || (col === 1)); + if (isBaseCase) return 1; + + const hasSeen = (memo[row][col] !== 0); + if (hasSeen) return memo[row][col]; + + return dfs(row, col, memo);/* Time O(ROWS * COLS) | Space O((ROWS * COLS) + HEIGHT) */ +}; + +var getMemo = (row, col) => new Array((row + 1)).fill()/* Time O(ROWS)| Space O(ROWS) */ + .map(() => new Array((col + 1)).fill(0)); /* Time O(COLS)| Space O(COLS) */ + +var dfs = (row, col, memo) => { + const left = uniquePaths((row - 1), col, memo); /* Time O(ROWS * COLS) | Space O(HEIGHT) */ + const right = uniquePaths(row, (col - 1), memo);/* Time O(ROWS * COLS) | Space O(HEIGHT) */ + + memo[row][col] = (left + right); /* | Space O(ROWS * COLS) */ + return memo[row][col]; +} + +/** + * DP - Bottom Up + * Matrix - Tabulation + * Time O(ROWS * COLS) | Space O(ROWS * COLS) + * https://leetcode.com/problems/unique-paths/ + * @param {number} row + * @param {number} col + * @return {number} + */ +var uniquePaths = (row, col) => { + const tabu = initTabu(row, col);/* Time O(ROWS * COLS) | Space O(ROWS * COLS) */ + + search(row, col, tabu); /* Time O(ROWS * COLS) | Space O(ROWS * COLS) */ + + return tabu[row - 1][col - 1]; +}; + +var search = (row, col, tabu) => { + for (let _row = 1; (_row < row); _row++) {/* Time O(ROWS)*/ + for (let _col = 1; (_col < col); _col++) {/* Time O(COLS)*/ + const left = (tabu[(_row - 1)][_col]) + const right = (tabu[_row][(_col - 1)]); + + tabu[_row][_col] = (left + right); /* Space O(ROWS * COLS) */ + } + } +} + +var initTabu = (row, col) => { + const tabu = new Array(row).fill() /* Time O(ROWS) | Space O(ROWS) */ + .map(() => new Array(col).fill(0)); /* Time O(COLS) | Space O(COLS) */ + + for (let _row = 0; (_row < row); _row++) {/* Time O(ROWS) */ + tabu[_row][0] = 1; /* | Space O(ROWS * COLS) */ + } + + for (let _col = 0; (_col < col); _col++) {/* Time O(COLS) */ + tabu[0][_col] = 1; /* | Space O(ROWS * COLS) */ + } + + return tabu; +} + +/** + * DP - Bottom Up + * Array - Tabulation + * Time O(ROWS * COLS) | Space O(COLS) + * https://leetcode.com/problems/unique-paths/ + * @param {number} m + * @param {number} n + * @return {number} + */ +var uniquePaths = (row, col) => { + const tabu = initTabu(col);/* Time O(COLS) | Space O(COLS) */ + + search(row, col, tabu); /* Time O(ROWS * COLS) | Space O(COLS) */ + + return tabu[(col - 1)]; +}; + +var initTabu = (col) => new Array(col).fill(1); /* Time O(COLS) | Space O(COLS) */ + +var search = (row, col, tabu) => { + for (let _row = 1; (_row < row); _row++) {/* Time O(ROWS) */ + for (let _col = 1; (_col < col); _col++) {/* Time O(COLS) */ + const prev = tabu[(_col - 1)]; + + tabu[_col] += prev; /* Space O(COLS) */ + } + } +} \ No newline at end of file diff --git a/javascript/0066-plus-one.js b/javascript/0066-plus-one.js new file mode 100644 index 000000000..a8d528f19 --- /dev/null +++ b/javascript/0066-plus-one.js @@ -0,0 +1,77 @@ +/** + * Time O(N) | Space O(N) + * https://leetcode.com/problems/plus-one/ + * @param {number[]} digits + * @return {number[]} + */ + var plusOne = (digits) => { + add(digits); + carry(digits); /* Time O(N) */ + addLeading(digits);/* | Space O(N) */ + + return digits; +}; + +var add = (digits) => digits[digits.length - 1] += 1; + +var carry = (digits) => { + for (let digit = (digits.length - 1); (0 < digit); digit--) {/* Time O(N) */ + const canCarry = (digits[digit] === 10); + if (!canCarry) break; + + digits[digit] = 0; + digits[(digit - 1)] += 1; + } +} + +const addLeading = (digits) => { + const canCarry = (digits[0] === 10); + if (!canCarry) return; + + digits[0] = 1; + digits.push(0);/* Space O(N) */ +} + +/** + * Time O(N) | Space O(N) + * https://leetcode.com/problems/plus-one/ + * @param {number[]} digits + * @return {number[]} + */ +var plusOne = (digits) => { + for (let digit = (digits.length - 1); (0 <= digit); digit--) {/* Time O(N) */ + const canCarry = digits[digit] === 9; + if (canCarry) { digits[digit] = 0; continue; } + + digits[digit]++; + + return digits; + } + + digits.unshift(1); /* Time O(N) | Space O(N) */ + + return digits; +}; + +/** + * Time O(N) | Space O(N) + * https://leetcode.com/problems/plus-one/ + * @param {number[]} digits + * @return {number[]} + */ +var plusOne = function(digits) { + var i = digits.length - 1 + + while (digits[i] + 1 === 10) { + digits[i] = 0 + i -= 1 + } + + if (i < 0) { + digits.unshift(1) + } else { + digits[i] += 1 + } + + return digits +}; diff --git a/javascript/0067-add-binary.js b/javascript/0067-add-binary.js new file mode 100644 index 000000000..06214c928 --- /dev/null +++ b/javascript/0067-add-binary.js @@ -0,0 +1,29 @@ +/** + * @param {string} a + * @param {string} b + * @return {string} + */ +var addBinary = function (a, b) { + let carry = 0; + let maxLength = a.length; + let result = ''; + + if (a.length < b.length) { + a = '0'.repeat(b.length - a.length) + a; + maxLength = b.length; + } else { + b = '0'.repeat(a.length - b.length) + b; + } + + for (let i = maxLength - 1; i >= 0; i--) { + sum = parseInt(a[i]) + parseInt(b[i]) + carry; + result = (sum % 2) + result; + if (sum >= 2) { + carry = 1; + } else { + carry = 0; + } + } + if (carry) result = '1' + result; + return result; +}; diff --git a/javascript/0069-sqrtx.js b/javascript/0069-sqrtx.js new file mode 100644 index 000000000..62f41b53b --- /dev/null +++ b/javascript/0069-sqrtx.js @@ -0,0 +1,24 @@ +/** + * Binary Search + * https://leetcode.com/problems/sqrtx/ + * + * Time O(log(n)) | Space O(1) + * @param {number} x + * @return {number} + */ +var mySqrt = function(x) { + let left = 1; + let right = x; + + while(left <= right) { + const mid = (left + right) >> 1; + if(mid * mid <= x && (mid+1) * (mid+1) > x) return mid; + if(mid * mid < x) { + left = mid + 1; + } else { + right = mid -1; + } + } + + return 0; + }; diff --git a/javascript/0070-climbing-stairs.js b/javascript/0070-climbing-stairs.js new file mode 100644 index 000000000..4a9cf40e7 --- /dev/null +++ b/javascript/0070-climbing-stairs.js @@ -0,0 +1,168 @@ +/** + * Brute Force - DFS + * Time O(2^N) | Space O(N) + * https://leetcode.com/problems/climbing-stairs/ + * @param {number} n + * @return {number} + */ + var climbStairs = (n, index = 0) => { + const isBaseCase1 = (n < index); + if (isBaseCase1) return 0; + + const isBaseCase2 = (index === n); + if (isBaseCase2) return 1; + + const [ next, nextNext ] = [ (index + 1), (index + 2) ]; + const left = climbStairs(n, next); /* Time O(2^N) | Space O(N) */ + const right = climbStairs(n, nextNext);/* Time O(2^N) | Space O(N) */ + + return (left + right); +} + +/** + * DP - Top Down + * Array - Memoization + * Time O(N) | Space O(N) + * https://leetcode.com/problems/climbing-stairs/ + * @param {number} n + * @return {number} + */ +var climbStairs = (n, index = 0, memo = Array(n + 1).fill(0)) => { + const isBaseCase1 = (n < index); + if (isBaseCase1) return 0; + + const isBaseCase2 = (index === n); + if (isBaseCase2) return 1; + + const hasSeen = (memo[index] !== 0); + if (hasSeen) return memo[index]; + + const [ next, nextNext ] = [ (index + 1), (index + 2) ]; + const left = climbStairs(n, next, memo); /* Time O(N) | Space O(N) */ + const right = climbStairs(n, nextNext, memo);/* Time O(N) | Space O(N) */ + + memo[index] = (left + right); /* | Space O(N) */ + return memo[index]; +}; + +/** + * DP - Bottom Up + * Array - Tabulation + * Time O(N) | Space O(N) + * https://leetcode.com/problems/climbing-stairs/ + * @param {number} n + * @return {number} + */ +var climbStairs = (n) => { + const isBaseCase = (n === 1); + if (isBaseCase) return 1; + + const tabu = initTabu(n);/* Space O(N) */ + + search(n, tabu); + + return tabu[n]; +}; + +var initTabu = (n) => { + const tabu = new Array(n + 1).fill(0); + + tabu[1] = 1; + tabu[2] = 2; + + return tabu; +} + +var search = (n, tabu) => { + for (let index = 3; (index <= n); index++) {/* Time O(N) */ + const [ prev, prevPrev ] = [ (index - 1), (index - 2) ]; + + tabu[index] = (tabu[prev] + tabu[prevPrev]);/* Space O(N) */ + } +} + +/** + * DP - Fibonacci Number + * Time O(N) | Space O(1) + * https://leetcode.com/problems/climbing-stairs/ + * @param {number} n + * @return {number} + */ +var climbStairs = (n) => { + const isBaseCase = (n === 1); + if (isBaseCase) return 1; + + let [ next, nextNext ] = [ 1, 2 ]; + + for (let index = 3; (index <= n); index++) {/* Time O(N) */ + const temp = (next + nextNext); + + next = nextNext; + nextNext = temp; + } + + return nextNext; +}; + +/** + * Matrix - Bitnets Method + * Time O(log(N)) | Space O(1) + * https://leetcode.com/problems/climbing-stairs/ + * @param {number} n + * @return {number} + */ + var climbStairs = (n) => { + const prev = [ [1, 1], [1, 0] ]; + const next = power(n, prev);/* Time O(log(N)) */ + + return next[0][0]; +} + +const power = (n, prev) => { + let next = [ [1, 0], [0, 1] ]; + + const isEmpty = () => n === 0; + while (!isEmpty()) {/* Time O(log(N)) */ + const isBit = (n & 1) === 1; + if (isBit) next = multiply(next, prev);/* Time O(1) | Space O(1) */ + + n >>= 1; + prev = multiply(prev, prev); /* Time O(1) | Space O(1) */ + } + + return next; +} + +const multiply = (prev, next) => { + const [ rows, cols ] = [ 2, 2 ]; + const matrix = new Array(rows).fill() + .map(() => new Array(cols).fill(0)); + + for (let row = 0; (row < rows); row++) { + for (let col = 0; (col < cols); col++) { + const left = (prev[row][0] * next[0][col]); + const right = (prev[row][1] * next[1][col]); + + matrix[row][col] = (left + right); + } + } + + return matrix; +} + +/** + * Math - Fibonacci Formula + * Time O(log(N)) | Space O(1) + * https://leetcode.com/problems/climbing-stairs/ + * @param {number} n + * @return {number} + */ +var climbStairs = (n, sqrt5 = Math.sqrt(5)) => { + const phi = ((sqrt5 + 1) / 2); + const psi = ((sqrt5 - 1) / 2); + + const phiPow = Math.pow(phi, (n + 1)); + const psiPow = Math.pow(psi, (n + 1)); + + return ((phiPow - psiPow) / sqrt5); +} diff --git a/javascript/0071-simplify-path.js b/javascript/0071-simplify-path.js new file mode 100644 index 000000000..0bdaef80b --- /dev/null +++ b/javascript/0071-simplify-path.js @@ -0,0 +1,36 @@ + +/** + * Stack + * Time O(N) | Space O(N) + * https://leetcode.com/problems/simplify-path + * @param {string} path + * @return {string} + */ +var simplifyPath = (path, slash = '/', stack = []) => { + const paths = path.split(slash).filter(Boolean); + + for (const _path of paths) traversePath(_path, stack); + + return `${slash}${stack.join(slash)}`; +}; + +const traversePath = (path, stack) => { + if (canPush(path)) return stack.push(path); + + if (canPop(path, stack)) stack.pop(); +}; + +const canPush = (path) => !( + isCurrentDirectory(path) || + isParentDirectory(path) +); + +const canPop = (path, stack) => + isParentDirectory(path) && + !isEmpty(stack); + +const isCurrentDirectory = (path) => (path === '.'); + +const isParentDirectory = (path) => (path === '..'); + +const isEmpty = ({ length }) => (0 === length); diff --git a/javascript/0072-edit-distance.js b/javascript/0072-edit-distance.js new file mode 100644 index 000000000..d4522c974 --- /dev/null +++ b/javascript/0072-edit-distance.js @@ -0,0 +1,172 @@ +/** + * Brute Force - DFS + * Time O(2^(N + M)) | Space O(N * M) + * https://leetcode.com/problems/edit-distance/ + * @param {string} word1 + * @param {string} word2 + * @return {number} + */ + var minDistance = (word1, word2, i = 0, j = 0) => { + const isBaseCase1 = ((word1.length * word2.length) === 0); + if (isBaseCase1) return (word1.length + word2.length); + + const isBaseCase2 = (word1.length === i); + if (isBaseCase2) return (word2.length - j); + + const isBaseCase3 = (word2.length === j); + if (isBaseCase3) return (word1.length - i); + + return dfs(word1, word2, i, j);/* Time O(2^(N + M)) | Space O((N * M) + HEIGHT) */ +} + +var dfs = (word1, word2, i, j) => { + const isEqual = (word1[i] === word2[j]); + if (isEqual) return minDistance(word1, word2, (i + 1), (j + 1));/* Time O(2^(N + M)) | Space O((N * M) + HEIGHT) */ + + const insert = minDistance(word1, word2, i, (j + 1)); /* Time O(2^(N + M)) | Space O((N * M) + HEIGHT) */ + const _delete = minDistance(word1, word2, (i + 1), j); /* Time O(2^(N + M)) | Space O((N * M) + HEIGHT) */ + const replace = minDistance(word1, word2, (i + 1), (j + 1)); /* Time O(2^(N + M)) | Space O((N * M) + HEIGHT) */ + + return (Math.min(insert, _delete, replace) + 1); +} + +/** + * DP - Top Down + * Matrix - Memoization + * Time O(N * M) | Space O(N * M) + * https://leetcode.com/problems/edit-distance/ + * @param {string} word1 + * @param {string} word2 + * @return {number} + */ + var minDistance = (word1, word2, i = 0, j = 0, memo = initMemo(word1, word2)) => { + const isBaseCase1 = ((word1.length * word2.length) === 0); + if (isBaseCase1) return (word1.length + word2.length); + + const isBaseCase2 = (word1.length === i); + if (isBaseCase2) return (word2.length - j); + + const isBaseCase3 = (word2.length === j); + if (isBaseCase3) return (word1.length - i); + + const hasSeen = (memo[i][j] !== -1); + if (hasSeen) return memo[i][j]; + + return dfs(word1, word2, i, j, memo);/* Time O(N * M) | Space O((N * M) + HEIGHT) */ +} + +var initMemo = (word1, word2) => new Array(word1.length).fill()/* Time O(N) | Space O(N) */ + .map(() => new Array(word2.length).fill(-1)); /* Time O(N) | Space O(N) */ + +var dfs = (word1, word2, i, j, memo) => { + const isEqual = (word1[i] === word2[j]); + if (isEqual) { + memo[i][j] = minDistance(word1, word2, (i + 1), (j + 1), memo);/* Time O(N * M) | Space O(HEIGHT) */ + return memo[i][j]; + } + + const insert = minDistance(word1, word2, i, (j + 1), memo); /* Time O(N * M) | Space O(HEIGHT) */ + const _delete = minDistance(word1, word2, (i + 1), j, memo); /* Time O(N * M) | Space O(HEIGHT) */ + const replace = minDistance(word1, word2, (i + 1), (j + 1), memo); /* Time O(N * M) | Space O(HEIGHT) */ + + memo[i][j] = (Math.min(insert, _delete, replace) + 1); /* | Space O(N * M) */ + return memo[i][j]; +} + +/** + * DP - Bottom Up + * Matrix - Tabulation + * Time O(N * M) | Space O(N * M) + * https://leetcode.com/problems/edit-distance/ + * @param {string} word1 + * @param {string} word2 + * @return {number} + */ +var minDistance = (word1, word2) => { + const isEmpty = ((word1.length * word2.length) === 0); + if (isEmpty) return (word1.length + word2.length); + + const tabu = initTabu(word1, word2);/* Time O(N * M) | Space O(N * M) */ + + search(word1, word2, tabu); /* Time O(N * M) | Space O(N * M) */ + + return tabu[word1.length][word2.length]; +} + +var initTabu = (word1, word2) => { + const tabu = new Array((word1.length + 1)).fill()/* Time O(N) | Space O(N) */ + .map(() => new Array((word2.length + 1)).fill(0));/* Time O(M) | Space O(M) */ + + for (let i = 0; (i < (word1.length + 1)); i++) { /* Time O(N) */ + tabu[i][0] = i; /* | Space O(N * M) */ + } + + for (let j = 0; (j < (word2.length + 1)); j++) { /* Time O(M) */ + tabu[0][j] = j; /* | Space O(N * M) */ + } + + return tabu; +} + +var search = (word1, word2, tabu) => { + for (let i = 1; (i < (word1.length + 1)); i++) {/* Time O(N) */ + for (let j = 1; (j < (word2.length + 1)); j++) {/* Time O(M) */ + const left = (tabu[(i - 1)][j] + 1); + const down = (tabu[i][(j - 1)] + 1); + + const isEqual = (word1[(i - 1)] === word2[(j - 1)]); + const leftDown = (tabu[(i - 1)][(j - 1)] + Number(!isEqual)); + + tabu[i][j] = Math.min(left, down, leftDown); /* Space O(N * M) */ + } + } +} + +/** + * DP - Bottom Up + * Matrix - Tabulation + * Time O(N * M) | Space O(M) + * https://leetcode.com/problems/edit-distance/ + * @param {string} word1 + * @param {string} word2 + * @return {number} + */ + var minDistance = (word1, word2) => { + const tabu = initTabu(word2);/* Time O(M) | Space O(M) */ + + search(word1, word2, tabu); /* Time O(N * M) | Space O(M) */ + + return tabu[word2.length]; +} + +var initTabu = (word2) => { + const tabu = new Array((word2.length + 1)).fill(0);/* Time O(M) | Space O(M) */ + + for (let j = 1; (j <= word2.length); j++) { /* Time O(M) */ + tabu[j] = j; /* | Space O(M) */ + } + + return tabu; +} + +var search = (word1, word2, tabu) => { + for (let i = 1; (i <= word1.length); i++) {/* Time O(N) */ + tabu[word2.length] = update(word1, word2, i, tabu);/* Time O(M) | Space (M) */ + } +} + +const update = (word1, word2, i, tabu) => { + let temp = i; + + for (let j = 1; (j <= word2.length); ++j) {/* Time O(M */ + const isEqual = (word1[(i - 1)] === word2[(j - 1)]) + const cur = isEqual + ? tabu[(j - 1)] + : (Math.min(tabu[(j - 1)], tabu[j], temp) + 1); + + tabu[(j - 1)] = temp; /* Space (M) */ + temp = cur; + } + + return temp; +} \ No newline at end of file diff --git a/javascript/0073-set-matrix-zeroes.js b/javascript/0073-set-matrix-zeroes.js new file mode 100644 index 000000000..1d34684c6 --- /dev/null +++ b/javascript/0073-set-matrix-zeroes.js @@ -0,0 +1,159 @@ +/** + * Additional Space + * Array - Tabulation + * Time O(ROWS * COLS) | Space (ROWS + COLS) + * https://leetcode.com/problems/set-matrix-zeroes/ + * @param {number[][]} matrix + * @return {void} Do not return anything, modify matrix in-place instead. + */ + var setZeroes = function (matrix) { + const [ rows, cols ] = [ matrix.length, matrix[0].length ]; + const [ _row, _col ] = initTabu(rows, cols);/* Space (ROWS + COLS) */ + + fillPlacements(matrix, _row, _col); /* Time O(ROWS * COLS) | Space (ROWS + COLS) */ + setZero(matrix, _row, _col); /* Time O(ROWS * COLS) */ +}; + +const initTabu = (rows, cols) => [ + new Array(rows).fill(1),/* Space O(ROWS) */ + new Array(cols).fill(1) /* Space O(COLS) */ +]; + +const fillPlacements = (matrix, _row, _col) => { + const [ rows, cols ] = [ matrix.length, matrix[0].length ]; + + for (let row = 0; (row < rows); row++) {/* Time (ROWS) */ + for (let col = 0; (col < cols); col++) {/* Time (COLS) */ + const isZero = (matrix[row][col] === 0); + if (!isZero) continue; + + _row[row] = 0; /* Space (ROWS) */ + _col[col] = 0; /* Space (COLS) */ + } + } +} + +const setZero = (matrix, _row, _col) => { + const [ rows, cols ] = [ matrix.length, matrix[0].length ]; + + for (let row = 0; (row < rows); row++) {/* Time (ROWS) */ + for (let col = 0; (col < cols); col++) {/* Time (COLS) */ + const canSet = ((_row[row] === 0) || (_col[col] === 0)); + if (!canSet) continue; + + matrix[row][col] = 0; + } + } +} + +/** + * Constant Space + * Time O(ROWS * COLS) | Space (1) + * https://leetcode.com/problems/set-matrix-zeroes/ + * @param {number[][]} matrix + * @return {void} Do not return anything, modify matrix in-place instead. + */ +var setZeroes = (matrix) => { + const _isColZero = isColZero(matrix);/* Time O(ROWS) */ + + setEdgesToZero(matrix); /* Time O(ROWS) */ + setCellsToZero(matrix, _isColZero); /* Time O(ROWS * COLS) */ +} + +var isColZero = (matrix) => matrix + .some((row) => row[0] === 0);/* Time O(ROWS) */ + +var setEdgesToZero = (matrix) => { + const [ rows, cols ] = [ matrix.length, matrix[0].length ]; + + for (let row = 0; (row < rows); row++) {/* Time (ROWS) */ + for (let col = 1; (col < cols); col++) {/* Time (COLS) */ + const canSet = matrix[row][col] === 0; + if (!canSet) continue; + + matrix[row][0] = 0; + matrix[0][col] = 0; + } + } +} + +var setCellsToZero = (matrix, isColZero) => { + const [ rows, cols ] = [ matrix.length, matrix[0].length ]; + + for (let row = (rows - 1); (0 <= row); row--) {/* Time (ROWS) */ + for (let col = (cols - 1); (1 <= col); col--) {/* Time (COLS) */ + if (!isZero(matrix, row, col)) continue; + + matrix[row][col] = 0; + } + + if (isColZero) matrix[row][0] = 0; + } +} + +var isZero = (matrix, row, col) => { + const [ rowLeftEdge, colTopEdge ] = [ matrix[row][0], matrix[0][col] ]; + + return ((rowLeftEdge === 0) || (colTopEdge === 0)); +} + +/** + * Constant Space + * Time O(ROWS * COLS) | Space (1) + * https://leetcode.com/problems/set-matrix-zeroes/ + * @param {number[][]} matrix + * @return {void} Do not return anything, modify matrix in-place instead. + */ + var setZeroes = (matrix) => { + const isColZero = setEdgesToZero(matrix);/* Time O(ROWS * COLS) */ + + setCellsToZero(matrix); /* Time O(ROWS * COLS) */ + + const isZero = (matrix[0][0] === 0); + if (isZero) setFirstRowZero(matrix); /* Time O(COLS) */ + + if (isColZero) setFirstColZero(matrix); /* Time O(ROWS) */ +} + +var setCellsToZero = (matrix) => { + const [ rows, cols ] = [ matrix.length, matrix[0].length ]; + + for (let row = 1; (row < rows); row++) {/* Time O(ROWS) */ + for (let col = 1; (col < cols); col++) {/* Time O(COLS) */ + const isZero = ((matrix[row][0] === 0) || (matrix[0][col] == 0)); + if (!isZero) continue; + + matrix[row][col] = 0; + } + } +} + +var setEdgesToZero = (matrix, isColZero = false) => { + const [ rows, cols ] = [ matrix.length, matrix[0].length ]; + + for (let row = 0; (row < rows); row++) {/* Time O(ROWS) */ + if (matrix[row][0] === 0) isColZero = true; + + for (let col = 1; (col < cols); col++) {/* Time O(COLS) */ + const canSet = (matrix[row][col] === 0); + if (!canSet) continue; + + matrix[0][col] = 0; + matrix[row][0] = 0; + } + } + + return isColZero; +} + +var setFirstRowZero = (matrix, cols = matrix[0].length) => { + for (let col = 0; (col < cols); col++) {/* Time O(COLS) */ + matrix[0][col] = 0; + } +} + +var setFirstColZero = (matrix, rows = matrix.length) => { + for (let row = 0; (row < rows); row++) {/* Time O(ROWS) */ + matrix[row][0] = 0; + } +} \ No newline at end of file diff --git a/javascript/0074-search-a-2d-matrix.js b/javascript/0074-search-a-2d-matrix.js new file mode 100644 index 000000000..d5231ee86 --- /dev/null +++ b/javascript/0074-search-a-2d-matrix.js @@ -0,0 +1,75 @@ +////////////////////////////////////////////////////////////////////////////// +// Two level Binary search +// Time: O(log(m) + log(n)) Space: O(1) +////////////////////////////////////////////////////////////////////////////// +/** + * @param {number[][]} matrix + * @param {number} target + * @return {boolean} + */ +var searchMatrix = function(matrix, target) { + let [rows, cols] = [matrix.length, matrix[0].length]; + let [top, bot] = [0, rows-1]; + + while(top <= bot){ + let row = Math.floor((top + bot) / 2); + if(target > matrix[row][cols-1]) { + top = row + 1; + } else if(target < matrix[row][0]) { + bot = row - 1; + } else { + break; + } + } + + if(!(top <= bot)) { + return false; + } + + let row = Math.floor((top + bot) / 2); + let [l, r] = [0, cols-1]; + while(l<=r){ + let m = Math.floor((l + r) /2); + if(target > matrix[row][m]) { + l = m +1; + } else if(target < matrix[row][m]) { + r = m - 1; + } else if(target == matrix[row][m]) { + return true; + } + } + return false; +}; + +////////////////////////////////////////////////////////////////////////////// +// Single Binary Search +// Time: O(log(mn)) Space: O(1) +////////////////////////////////////////////////////////////////////////////// + +/** + * @param {number[][]} matrix + * @param {number} target + * Time O(log(ROWS * COLS)) | Space O(1) + * @return {boolean} + */ +var searchMatrix = function (matrix, target) { + const [rows, cols] = [matrix.length, matrix[0].length]; + let [left, right] = [0, rows * cols - 1]; + + while (left <= right) { + const mid = (left + right) >> 1; + const [row, col] = [Math.floor(mid / cols), mid % cols]; + const guess = matrix[row][col]; + + const isTarget = guess === target; + if (isTarget) return true; + + const isTargetGreater = guess < target; + if (isTargetGreater) left = mid + 1; + + const isTargetLess = target < guess; + if (isTargetLess) right = mid - 1; + } + + return false; +}; diff --git a/javascript/0075-sort-colors.js b/javascript/0075-sort-colors.js new file mode 100644 index 000000000..ec2d37752 --- /dev/null +++ b/javascript/0075-sort-colors.js @@ -0,0 +1,51 @@ +// problem link https://leetcode.com/problems/sort-colors + +// brute force approche O(n^2); +var sortColors = function(nums) { + + for(let i = 0; i < nums.length; i++) { + for(let j = i +1; j < nums.length; j++) { + if(nums[j] < nums[i]) { + swap(nums, j, i); + } + } + } + + return nums; + }; + +function swap(nums, j, i) { + + const temp = nums[j]; + nums[j] = nums[i]; + nums[i] = temp; +} + +// optimized approche O(n); + +function sortColors(nums) { + + let i = 0; + let l = 0; + let r = nums.length - 1; + + while(i <= r) { + const num = nums[i]; + if(num === 0) { + swap(nums,i,l); + i++; + l++; + } else if(num === 2) { + swap(nums,i,r); + r--; + } else { + i++; + } + } + + return nums; + } + + function swap(nums,i,j) { + [nums[i], nums[j]] = [nums[j],nums[i]]; + } diff --git a/javascript/0076-minimum-window-substring.js b/javascript/0076-minimum-window-substring.js new file mode 100644 index 000000000..83a35347c --- /dev/null +++ b/javascript/0076-minimum-window-substring.js @@ -0,0 +1,78 @@ +/** + * https://leetcode.com/problems/minimum-window-substring/ + * Time O(N + M) | SpaceO(N + M) + * @param {string} s + * @param {string} t + * @return {string} + */ +var minWindow = function (s, t) { + const isMissingArgs = !s.length || !t.length; + if (isMissingArgs) return ''; + + const frequencyMap = getFrequencyMap(t); + const { start, end } = getWindowPointers(s, t, frequencyMap); + + return getSubString(s, start, end); +}; + +const getFrequencyMap = (str, frequencyMap = new Map()) => { + for (const char of str) { + frequencyMap.set(char, (frequencyMap.get(char) || 0) + 1); + } + + return frequencyMap; +}; + +const getWindowPointers = (s, t, frequencyMap) => { + let [left, right, matched, start, end] = [0, 0, 0, 0, s.length + 1]; + + while (right < s.length) { + matched = addRightFrequency(s, right, frequencyMap, matched); + + const canSlide = () => matched === t.length; + while (canSlide()) { + const window = right - left + 1; + + const isSmaller = window < end; + if (isSmaller) { + [start, end] = [left, window]; + } + + matched = subtractLeftFrequency(s, left, frequencyMap, matched); + left++; + } + + right++; + } + + return { start, end }; +}; + +const addRightFrequency = (s, right, frequencyMap, matched) => { + const char = s[right]; + + if (frequencyMap.has(char)) { + frequencyMap.set(char, frequencyMap.get(char) - 1); + + const isInWindow = 0 <= frequencyMap.get(char); + if (isInWindow) matched++; + } + + return matched; +}; + +const subtractLeftFrequency = (s, left, frequencyMap, matched) => { + const char = s[left]; + + if (frequencyMap.has(char)) { + const isOutOfWindow = frequencyMap.get(char) === 0; + if (isOutOfWindow) matched--; + + frequencyMap.set(char, frequencyMap.get(char) + 1); + } + + return matched; +}; + +const getSubString = (s, start, end) => + end <= s.length ? s.slice(start, start + end) : ''; diff --git a/javascript/0078-subsets.js b/javascript/0078-subsets.js new file mode 100644 index 000000000..e467f0ea8 --- /dev/null +++ b/javascript/0078-subsets.js @@ -0,0 +1,53 @@ +/** + * https://leetcode.com/problems/subsets/ + * Time O(N * 2^N) | Space(N) + * @param {number[]} nums + * @return {number[][]} + */ + var subsets = (nums) => { + nums.sort((a, b) => a -b); + + return dfs(nums) +} + +var dfs = (nums, level = 0, set = [], subset = []) => { + subset.push(set.slice()); + + for (let i = level; i < nums.length; i++){ + backTrack(nums, i, set, subset); + } + + return subset +} + +const backTrack = (nums, i, set, subset) => { + set.push(nums[i]); + dfs(nums, (i + 1), set, subset); + set.pop(); +} + +/** + * https://leetcode.com/problems/subsets/ + * Time O(N * 2^N) | Space(N * 2^N) + * @param {number[]} nums + * @return {number[][]} + */ + var subsets = (nums) => { + nums.sort((a, b) => a -b); + + return bfs(nums) +} + +const bfs = (nums, subsets = [[]]) => { + for (const num of nums) { + const levels = subsets.length + + for (let level = 0; level < levels; level++) { + const nextLevel = [ ...subsets[level], num ] + + subsets.push(nextLevel) + } + } + + return subsets +} diff --git a/javascript/0079-word-search.js b/javascript/0079-word-search.js new file mode 100644 index 000000000..f483a1831 --- /dev/null +++ b/javascript/0079-word-search.js @@ -0,0 +1,45 @@ +/** + * https://leetcode.com/problems/word-search/ + * Time O(N * 3^L) | Space O(L) + * @param {character[][]} board + * @param {string} word + * @return {boolean} + */ +var exist = function(board, word) { + for(let row = 0; row < board.length; row++) { + for(let col = 0; col < board[0].length; col++){ + if (dfs(board, row, col, word, 0)) return true; + } + } + + return false; +} + +const dfs = (board, row, col, word, index) => { + if (index === word.length) return true; + if (isOutOfBound(board, row, col)) return false; + if (board[row][col] !== word[index]) return false + + board[row][col] = '*'; + + const hasWord = Object + .values(directions(row, col)) + .filter(([r, c]) => dfs(board, r, c, word, index + 1)) + .length + + board[row][col] = word[index]; + return hasWord; +} + +const isOutOfBound = (board, row, col) => { + const isRowOutOfBound = row < 0 || board.length - 1 < row + const isColOutOfBound = col < 0 || board[0].length - 1 < col + return isRowOutOfBound || isColOutOfBound +} + +const directions = (row, col) => ({ + up: [row - 1, col], + down: [row + 1, col], + left: [row, col - 1,], + right: [row, col + 1] +}) diff --git a/javascript/0080-remove-duplicates-from-sorted-array-ii.js b/javascript/0080-remove-duplicates-from-sorted-array-ii.js new file mode 100644 index 000000000..15eb88c99 --- /dev/null +++ b/javascript/0080-remove-duplicates-from-sorted-array-ii.js @@ -0,0 +1,50 @@ +/** +* https://leetcode.com/problems/remove-duplicates-from-sorted-array-ii/ +* +* Time O(n) | Space O(1) +* @param {number[]} nums +* @return {number} +*/ +var removeDuplicates = function(nums) { + let current = nums[0]; + let sameElCount = 0; + + for(let i = 0; i < nums.length; i++) { + if(current === nums[i]) { + sameElCount++; + } + if(current !== nums[i]) { + current = nums[i]; + sameElCount = 1; + } + if(sameElCount > 2) { + nums.splice(i,1); + i--; + } + } +}; + + +/** +* Two pointer +* Time O(n^2) | Space O(1) +* @param {number[]} nums +* @return {number} +*/ +var removeDuplicates2 = function(nums) { + const isEdgeCase = (nums.length < 2) + if (isEdgeCase) return nums.length; + + let [ left, right ] = [ 2, 2 ]; + + while (right < nums.length) {/* Time O(N) */ + const isEqual = (nums[(left - 2)] === nums[right]); + if (!isEqual) { + nums[left] = nums[right]; + left += 1; + } + + right += 1; + } + return left; +}; diff --git a/javascript/0083-remove-duplicates-from-sorted-list.js b/javascript/0083-remove-duplicates-from-sorted-list.js new file mode 100644 index 000000000..0c4d9079a --- /dev/null +++ b/javascript/0083-remove-duplicates-from-sorted-list.js @@ -0,0 +1,23 @@ +/** + * https://leetcode.com/problems/remove-duplicates-from-sorted-list/ + * Definition for singly-linked list. + * function ListNode(val, next) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + */ +/** + * @param {ListNode} head + * @return {ListNode} + */ +var deleteDuplicates = function (head) { + let cur = head; + while (cur != null && cur.next != null) { + if (cur.next.val === cur.val) { + cur.next = cur.next.next; + } else { + cur = cur.next; + } + } + return head; +}; diff --git a/javascript/0084-largest-rectangle-in-histogram.js b/javascript/0084-largest-rectangle-in-histogram.js new file mode 100644 index 000000000..a05690713 --- /dev/null +++ b/javascript/0084-largest-rectangle-in-histogram.js @@ -0,0 +1,122 @@ +/** + * https://leetcode.com/problems/largest-rectangle-in-histogram/solution/ + * Time O(N^3) | Space O(1) + * @param {number[]} heights + * @return {number} + */ +var largestRectangleArea = function(heights, maxArea = 0) { + for (let i = 0; i < heights.length; i++) {/* Time O(N) */ + for (let j = i; j < heights.length; j++) {/* Time O(N) */ + let min = Infinity; + + for (let k = i; k <= j; k++) { /* Time O(N) */ + min = Math.min(min, heights[k]); + } + + const area = min * ((j - i) + 1); + + maxArea = Math.max(maxArea, area); + } + } + + return maxArea; +} + +/** + * https://leetcode.com/problems/largest-rectangle-in-histogram/solution/ + * Time O(N^2) | Space O(1) + * @param {number[]} heights + * @return {number} + */ +var largestRectangleArea = function(heights, maxArea = 0) { + for (let i = 0; i < heights.length; i++) {/* Time O(N) */ + let min = Infinity; + + for (let j = i; j < heights.length; j++) {/* Time O(N) */ + min = Math.min(min, heights[j]); + + const area = min * ((j - i) + 1); + + maxArea = Math.max(maxArea, area); + } + } + + return maxArea; +} + +/** + * https://leetcode.com/problems/largest-rectangle-in-histogram/solution/ + * Time O(N^2) | Space O(N) + * @param {number[]} heights + * @return {number} + */ +var largestRectangleArea = function(heights, left = 0, right = (heights.length - 1)) { + const isBaseCase = right < left; + if (isBaseCase) return 0; + + return divideAndConquer(heights, left, right); /* Time O(N^2) | Space O(N) */ +} + +const divideAndConquer = (heights, left, right, min = left) => { + for (let i = left; i <= right; i++) { /* Time O(N) */ + const isMinGreater = heights[i] < heights[min]; + if (!isMinGreater) continue; + + min = i; + } + + const window = (right - left) + 1; + const area = heights[min] * window; + + const leftArea = largestRectangleArea(heights, (min + 1), right)/* Time O(N^2) | Space O(N) */ + const rightArea = largestRectangleArea(heights, left, (min - 1))/* Time O(N^2) | Space O(N) */ + + return Math.max(area, leftArea, rightArea); +} + +/** + * https://leetcode.com/problems/largest-rectangle-in-histogram/solution/ + * Time O(N) | Space O(N) + * @param {number[]} heights + * @return {number} + */ +var largestRectangleArea = function(heights) { + const { stack, maxArea } = fillStack(heights); /* Time O(N) | Space O(N) */ + + return getMaxArea(heights, stack, maxArea); /* Time O(N) */ +}; + +const fillStack = (heights, stack = [], maxArea = 0) => { + for (let index = 0; index < heights.length; index++) {/* Time O(N + N) */ + let start = index; + + const isCurrHeightLess = ([ prevIndex, prevHeight ], currHeight) => currHeight < prevHeight; + const canShrink = () => isCurrHeightLess(stack[stack.length - 1], heights[index]); + while (stack.length && canShrink()) { /* Time O(N + N) */ + const [ _index, _height ] = stack.pop(); + const width = index - _index; + const area = _height * width; + + maxArea = Math.max(maxArea, area); + start = _index; + } + + stack.push([ start, heights[index] ]); /* Space O(N) */ + } + + return { stack, maxArea } +} + +const getMaxArea = (heights, stack, maxArea) => { + for (const [ index, height ] of stack) { /* Time O(N) */ + const width = heights.length - index; + const area = height * width; + + maxArea = Math.max(maxArea, area); + } + + return maxArea; +} + + + diff --git a/javascript/0088-merge-sorted-array.js b/javascript/0088-merge-sorted-array.js new file mode 100644 index 000000000..f6b3ac58c --- /dev/null +++ b/javascript/0088-merge-sorted-array.js @@ -0,0 +1,34 @@ +/** + * Linear + * Time O(N) | Space O(1) + * https://leetcode.com/problems/merge-sorted-array/ + * @param {number[]} nums1 + * @param {number} m + * @param {number[]} nums2 + * @param {number} n + * @return {void} Do not return anything, modify nums1 in-place instead. + */ +var merge = function(nums1, m, nums2, n) { + + let k = m + n - 1; + m = m - 1; + n = n - 1; + while (m >= 0 && n >= 0) { + if (nums1[m] > nums2[n]) { + nums1[k] = nums1[m]; + m--; + } else { + nums1[k] = nums2[n]; + n--; + } + k--; + } + + if (n >= 0) { + while (n >= 0) { + nums1[k] = nums2[n]; + n--; + k--; + } + } +}; diff --git a/javascript/0090-subsets-ii.js b/javascript/0090-subsets-ii.js new file mode 100644 index 000000000..fd614fec8 --- /dev/null +++ b/javascript/0090-subsets-ii.js @@ -0,0 +1,64 @@ +/** + * https://leetcode.com/problems/subsets-ii/ + * Time O(N * 2^N) | Space O(N) + * @param {number[]} nums + * @return {number[][]} + */ + var subsetsWithDup = function(nums) { + nums.sort((a, b) => a - b); + + return dfs(nums); +}; + +const dfs = (nums, index = 0, set = [], subset = []) => { + subset.push(set.slice()) + + for (let i = index; i < nums.length; i++) { + const isDuplicate = (index < i) && (nums[i - 1] === nums[i]) + if (isDuplicate) continue; + + backTrack(nums, i, set, subset); + } + + return subset +} + +const backTrack = (nums, i, set, subset) => { + set.push(nums[i]); + dfs(nums, (i + 1), set, subset); + set.pop(); +} + + +/** + * https://leetcode.com/problems/subsets-ii/ + * Time O(N * 2^N) | Space O(N * 2^N) + * @param {number[]} nums + * @return {number[][]} + */ + var subsetsWithDup = (nums) => { + nums.sort((a, b) => a - b); + + return bfs(nums) +} + +const bfs = (nums, subsets = [[]]) => { + let levels = subsets.length - 1 + + for (let i = 0; i < nums.length; i++) { + const isPrevDuplicate = (0 < i) && (nums[i - 1] === nums[i]) + const start = isPrevDuplicate + ? (levels + 1) + : 0 + + levels = subsets.length - 1 + + for (let level = start; level < (levels + 1); level++) { + const nextLevel = [ ...subsets[level], nums[i] ] + + subsets.push(nextLevel) + } + } + + return subsets +} \ No newline at end of file diff --git a/javascript/0091-decode-ways.js b/javascript/0091-decode-ways.js new file mode 100644 index 000000000..74962cf5c --- /dev/null +++ b/javascript/0091-decode-ways.js @@ -0,0 +1,122 @@ +/** + * DP - Top Down + * Hash Map - Memoization + * Time O(N) | Space O(N) + * https://leetcode.com/problems/decode-ways/ + * @param {string} s + * @return {number} + */ +var numDecodings = (str, index = 0, memo = new Map()) => { + const isBaseCase1 = !str.length || (str[index] === '0'); + if (isBaseCase1) return 0; + + const isisBaseCase2 = index === str.length; + if (isisBaseCase2) return 1; + + if (memo.has(index)) return memo.get(index); + + return dfs(str, index, memo); +}; + +const dfs = (str, index, memo) => { + let count = numDecodings(str, (index + 1), memo); + + if (isTwoDigit(str, index)) { + count += numDecodings(str, (index + 2), memo); + } + + memo.set(index, count); + + return count; +} + +var isTwoDigit = (str, index) => { + const twoDigit = Number(str.slice(index, (index + 2))); + + return (10 <= twoDigit) && (twoDigit <= 26); +} + +/** + * DP - Bottom Up + * Array - Tabulation + * Time O(N) | Space O(N) + * https://leetcode.com/problems/decode-ways/ + * @param {string} s + * @return {number} + */ +var numDecodings = (s) => { + const isBaseCase = !s.length || s[0] === '0' + if (isBaseCase) return 0; + + const tabu = getTabu(s); + + decode(s, tabu); + + return tabu[s.length]; +} + +const getTabu = (s) => { + const tabu = new Array(s.length + 1).fill(0); + + tabu[0] = 1; + tabu[1] = (s[1] === '0') + ? 0 + : 1; + + return tabu; +} + +var decode = (s, tabu) => { + for (let curr = 2; curr < tabu.length; curr++) { + const [ prev, prevPrev ] = [ (curr - 1), (curr - 2) ]; + const isEqual = s[prev] === '0'; + if (!isEqual) tabu[curr] += tabu[prev]; + + if (isTwoDigit(s, curr)) tabu[curr] += tabu[prevPrev]; + } +} + +var isTwoDigit = (s, index) => { + const twoDigit = Number(s.slice((index - 2), index)); + + return 10 <= twoDigit && twoDigit <= 26; +} + +/** + * 2 Pointer - previous + previousPrevious + * Time O(N) | Space O(1) + * https://leetcode.com/problems/decode-ways/ + * @param {string} s + * @return {number} + */ +var numDecodings = (s) => { + const isBaseCase = !s.length || s[0] === '0'; + if (isBaseCase) return 0; + + return decode(s); +} + +var decode = (s) => { + let [ prev, prevPrev ] = [ 1, 1 ]; + + for (let curr = 1; curr < s.length; curr++) { + const temp = prev; + + const isEqual = s[curr] === '0'; + if (isEqual) prev = 0; + + if (isTwoDigit(s, curr)) prev += prevPrev; + + prevPrev = temp; + } + + return prev; +} + +var isTwoDigit = (s, i) => { + const [ prevChar, curChar ] = [ (s[i - 1]), s[i] ]; + const is10 = prevChar === '1'; + const is20 = (prevChar === '2' && curChar <= '6'); + + return is10 || is20; +} \ No newline at end of file diff --git a/javascript/0092-reverse-linked-list-ii.js b/javascript/0092-reverse-linked-list-ii.js new file mode 100644 index 000000000..5fcb4e8e1 --- /dev/null +++ b/javascript/0092-reverse-linked-list-ii.js @@ -0,0 +1,42 @@ +/** + * Definition for singly-linked list. + * function ListNode(val, next) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + */ +/** + * @param {ListNode} head + * @param {number} left + * @param {number} right + * @return {ListNode} + */ +var reverseBetween = function (head, left, right) { + if (!head || !head.next || left === right) { + return head; + } + let idx = 1; + let prevLower = null; + let curLower = head; + while (curLower && idx < left) { + prevLower = curLower; + curLower = curLower.next; + idx++; + } + let storedPrevLower = prevLower; + let storedCurLower = curLower; + let prevPtr = null; + let tmpPtr = null; + while (curLower && idx <= right) { + tmpPtr = curLower.next; + curLower.next = prevPtr; + prevPtr = curLower; + curLower = tmpPtr; + idx++; + } + if (storedPrevLower) { + storedPrevLower.next = prevPtr; + } + storedCurLower.next = curLower; + return storedPrevLower ? head : prevPtr; +}; diff --git a/javascript/0093-restore-ip-addresses.js b/javascript/0093-restore-ip-addresses.js new file mode 100644 index 000000000..4e670d8ee --- /dev/null +++ b/javascript/0093-restore-ip-addresses.js @@ -0,0 +1,38 @@ +/** + * @param {string} s + * @return {string[]} + */ +var restoreIpAddresses = function (s) { + let res = []; + + if (s.length > 12) return res; + + /** + * + * @param {number} i + * @param {number} dots + * @param {string} currentIP + */ + function backtracking(i, dots, currentIP) { + if (dots === 4 && i == s.length) { + res.push(currentIP.slice(0, currentIP.length - 1)); + return; + } else if (dots > 4) { + return; + } + + for (let j = i; j < Math.min(i + 3, s.length); j++) { + if (+s.slice(i, j + 1) < 256 && (i == j || s[i] != '0')) { + backtracking( + j + 1, + dots + 1, + currentIP + s.slice(i, j + 1) + '.' + ); + } + } + } + + backtracking(0, 0, ''); + + return res; +}; diff --git a/javascript/0094-binary-tree-inorder-traversal.js b/javascript/0094-binary-tree-inorder-traversal.js new file mode 100644 index 000000000..a590b39e7 --- /dev/null +++ b/javascript/0094-binary-tree-inorder-traversal.js @@ -0,0 +1,22 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @return {number[]} + */ + var inorderTraversal = function (root, list = []) { + + if (!root) return []; + + inorderTraversal(root.left, list); + list.push(root.val) + inorderTraversal(root.right, list); + + return list +}; diff --git a/javascript/0095-unique-binary-search-trees-ii.js b/javascript/0095-unique-binary-search-trees-ii.js new file mode 100644 index 000000000..7810bb5e1 --- /dev/null +++ b/javascript/0095-unique-binary-search-trees-ii.js @@ -0,0 +1,47 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * Recursion + * Time O(4^n) | Space O(n) + * https://leetcode.com/problems/unique-binary-search-trees-ii/ + * @param {number} n + * @return {TreeNode[]} + */ +var generateTrees = function(n) { + + const dfs = (start, end) => { + + const result = []; + + if(start === end) { + result.push(new TreeNode(start)); + return result; + }; + if(start > end) { + result.push(null); + return result; + }; + + for(let i = start; i < end + 1; i++) { + const leftSubTrees = dfs(start, i - 1); + const rightSubTrees = dfs(i + 1 , end); + + leftSubTrees.forEach((leftSubTree) => { + rightSubTrees.forEach((rightSubTree) => { + const root = new TreeNode(i, leftSubTree, rightSubTree); + result.push(root); + }); + }); + } + + return result; + } + + return dfs(1, n); +}; diff --git a/javascript/0096-unique-binary-search-trees.js b/javascript/0096-unique-binary-search-trees.js new file mode 100644 index 000000000..eabc140d7 --- /dev/null +++ b/javascript/0096-unique-binary-search-trees.js @@ -0,0 +1,26 @@ +/** + * Time O(n^2) | Space O(n) | n^2 because of the inner loop which runs from 0 to n on each call. + * DFS | DP | Recursion | Tree + * https://leetcode.com/problems/unique-binary-search-trees/ + * @param {number} n + * @return {number} + */ +var numTrees = function(n) { + + const cache = {}; + + const dfs = (n) => { + if(n <= 1) return 1; + if(cache[n]) return cache[n]; + + let total = 0; + for(let i = 0; i < n; i++) { + total += dfs(i) * dfs(n - 1 - i); + } + + cache[n] = total; + return total; + } + + return dfs(n); +}; diff --git a/javascript/0097-interleaving-string.js b/javascript/0097-interleaving-string.js new file mode 100644 index 000000000..edb454ed9 --- /dev/null +++ b/javascript/0097-interleaving-string.js @@ -0,0 +1,172 @@ +/** + * Brute Force - DFS + * Time O(2^(N + M)) | Space O(N + M) + * https://leetcode.com/problems/interleaving-string/ + * @param {string} s1 + * @param {string} s2 + * @param {string} s3 + * @return {boolean} + */ + var isInterleave = (s1, s2, s3, i = 0, j = 0, res = '') => { + const isBaseCase1 = (s3.length !== (s1.length + s2.length)); + if (isBaseCase1) return false; + + const isBaseCase2 = ((res === s3) && (i == s1.length) && (j == s2.length)); + if (isBaseCase2) return true; + + return dfs(s1, s2, s3, i, j, res);/* Time O(2^(N + M)) | Space O(N + M) */ +} + +var dfs = (s1, s2, s3, i, j, res, ans = false) => { + const hasLeft = (i < s1.length); + if (hasLeft) ans |= isInterleave(s1, s2, s3, (i + 1), j, `${res}${s1[i]}`); /* Time O(2^(N + M)) | Space O(N) */ + + const hasRight = (j < s2.length); + if (hasRight) ans |= isInterleave(s1, s2, s3, i, (j + 1), `${res}${s2[j]}`);/* Time O(2^(N + M)) | Space O(M) */ + + return ans; +} + +/** + * DP - Top Down + * Matrix - Memoization + * Time O(N * M) | Space O(N * M) + * https://leetcode.com/problems/interleaving-string/ + * @param {string} s1 + * @param {string} s2 + * @param {string} s3 + * @return {boolean} + */ +var isInterleave = (s1, s2, s3, i = 0, j = 0, k = 0, memo = initMemo(s1, s2)) => { + const isBaseCase1 = (s3.length !== (s1.length + s2.length)); + if (isBaseCase1) return false; + + const isBaseCase2 = (i === s1.length); + if (isBaseCase2) return (s2.slice(j) === s3.slice(k));/* Time O(M + K) | Space O(M + K) */ + + const isBaseCase3 = (j === s2.length); + if (isBaseCase3) return (s1.slice(i) === s3.slice(k));/* Time O(N + K) | Space O(N + K) */ + + const hasSeen = (memo[i][j] !== null); + if (hasSeen) return memo[i][j]; + + return dfs(s1, s2, s3, i, j, k, memo);/* Time O(N * M) | Space O((N * M) + HEIGHT) */ +} + +var initMemo = (s1, s2) => new Array(s1.length).fill()/* Time O(N) | Space O(N) */ + .map(() => new Array(s2.length).fill(null)); /* Time O(M) | Space O(M) */ + +var dfs = (s1, s2, s3, i, j, k, memo) => { + const left = ((s3[k] === s1[i]) && isInterleave(s1, s2, s3, (i + 1), j, (k + 1), memo)); /* Time O(N) | Space O(HEIGHT) */ + const right = ((s3[k] === s2[j]) && isInterleave(s1, s2, s3, i, (j + 1), (k + 1), memo));/* Time O(M) | Space O(HEIGHT) */ + + memo[i][j] = left || right; /* | Space O(N * M) */ + return memo[i][j]; +} + +/** + * DP - Bottom Up + * Matrix - Tabulation + * Time O(N * M) | Space O(N * M) + * https://leetcode.com/problems/interleaving-string/ + * @param {string} s1 + * @param {string} s2 + * @param {string} s3 + * @return {boolean} + */ +var isInterleave = (s1, s2, s3) => { + const isBaseCase = (s3.length !== s1.length + s2.length); + if (isBaseCase) return false; + + const tabu = initTabu(s1, s2);/* Time O(N * M) | Space O(N * M) */ + + search(s1, s2, s3, tabu); /* Time O(N * M) | Space O(N * M) */ + + return tabu[s1.length][s2.length]; +} + +var initTabu = (s1, s2) => new Array((s1.length + 1)).fill()/* Time O(N) | Space O(N) */ + .map(() => new Array((s2.length + 1)).fill(null)) /* Time O(M) | Space O(M) */ + +var search = (s1, s2, s3, tabu) => { + const [ rows, cols ] = [ s1.length, s2.length ]; + + for (let row = 0; (row <= rows); row++) {/* Time O(N) */ + for (let col = 0; (col <= cols); col++) {/* Time O(M) */ + tabu[row][col] = /* Space O(N * M) */ + hasMatch(s1, s2, s3, row, col, tabu); + } + } +} + +var hasMatch = (s1, s2, s3, i, j, tabu) => { + const isBaseCase1 = ((i === 0) && (j === 0)); + if (isBaseCase1) return true; + + const isBaseCase2 = (i === 0); + if (isBaseCase2) return getRight(i, j, s2, s3, tabu); + + const isBaseCase3 = (j === 0); + if (isBaseCase3) return getLeft(i, j, s1, s3, tabu); + + const left = getLeft(i, j, s1, s3, tabu); + const right = getRight(i, j, s2, s3, tabu) + + return (left || right); +} + +var getLeft = (i, j, s1, s3, tabu) => ((tabu[(i - 1)][j] && s1[(i - 1)]) === s3[((i + j) - 1)]); + +var getRight = (i, j, s2, s3, tabu) => ((tabu[i][(j - 1)] && s2[(j - 1)]) === s3[((i + j) - 1)]); + +/** + * DP - Bottom Up + * Array - Tabulation + * Time O(N * M) | Space O(M) + * https://leetcode.com/problems/interleaving-string/ + * @param {string} s1 + * @param {string} s2 + * @param {string} s3 + * @return {boolean} + */ +var isInterleave = (s1, s2, s3) => { + const isBaseCase = (s3.length !== (s1.length + s2.length)); + if (isBaseCase) return false; + + const tabu = initTabu(s2);/* Time O(M) | Space O(M) */ + + search(s1, s2, s3, tabu); /* Time O(N * M) | Space O(M) */ + + return tabu[s2.length]; +}; + +var initTabu = (s2) => new Array((s2.length + 1)).fill(false);/* Time O(M) | Space O(M) */ + +var search = (s1, s2, s3, tabu) => { + const [ rows, cols ] = [ s1.length, s2.length ]; + + for (let row = 0; (row <= rows); row++) {/* Time O(N)*/ + for (let col = 0; (col <= cols); col++) {/* Time O(M)*/ + tabu[col] = /* Space O(M)*/ + hasMatch(s1, s2, s3, row, col, tabu); + } + } +} + +var hasMatch = (s1, s2, s3, i, j, tabu) => { + const isBaseCase1 = ((i === 0) && (j === 0)); + if (isBaseCase1) return true; + + const isBaseCase2 = (i === 0); + if (isBaseCase2) return getRight(i, j, s2, s3, tabu) + + const isBaseCase3 = (j === 0); + if (isBaseCase3) return getLeft(i, j, s1, s3, tabu);; + + return getLeft(i, j, s1, s3, tabu) || getRight(i, j, s2, s3, tabu); +} + +var getLeft = (i, j, s1, s3, tabu) => (tabu[j] && (s1[(i - 1)] === s3[((i + j) - 1)])); + +var getRight = (i, j, s2, s3, tabu) => (tabu[(j - 1)] && (s2[(j - 1)] === s3[((i + j) - 1)])); + diff --git a/javascript/0098-validate-binary-search-tree.js b/javascript/0098-validate-binary-search-tree.js new file mode 100644 index 000000000..6b6cc12ee --- /dev/null +++ b/javascript/0098-validate-binary-search-tree.js @@ -0,0 +1,72 @@ +/** + * https://leetcode.com/problems/validate-binary-search-tree/ + * Time O(N) | Space O(H) + * @param {TreeNode} root + * @return {boolean} + */ +var isValidBST = function(root, min = -Infinity, max = Infinity) { + const isBaseCase = root === null; + if (isBaseCase) return true; + + const isInvalid = (root.val <= min) || (max <= root.val); + if (isInvalid) return false; + + return dfs(root, min, max); +}; + +const dfs = (root, min, max) => { + const left = isValidBST(root.left, min, root.val); + const right = isValidBST(root.right, root.val, max); + + return left && right; +} +// TODO +/** + * https://leetcode.com/problems/validate-binary-search-tree/ + * Time O(N) | Space O(H) + * @param {TreeNode} root + * @return {boolean} + */ + var isValidBST = function(root, prev = [ null ]) { + const isBaseCase = root === null; + if (isBaseCase) return true; + + if (!isValidBST(root.left, prev)) return false; + + const isInvalid = (prev[0] !== null) && (root.val <= prev[0]); + if (isInvalid) return false; + + prev[0] = root.val; + + return isValidBST(root.right, prev); +} + +/** + * https://leetcode.com/problems/validate-binary-search-tree/ + * Time O(N) | Space O(H) + * @param {TreeNode} root + * @return {boolean} + */ +var isValidBST = function(root, stack = []) { + let prev = null; + + while (stack.length || root) { + moveLeft(stack, root); + root = stack.pop(); + + const isInvalid = prev && (root.val <= prev.val); + if (isInvalid) return false; + + prev = root; + root = root.right; + } + + return true; +} + +const moveLeft = (stack, root) => { + while (root) { + stack.push(root); + root = root.left; + } +} \ No newline at end of file diff --git a/javascript/0100-same-tree.js b/javascript/0100-same-tree.js new file mode 100644 index 000000000..5eb4ccce6 --- /dev/null +++ b/javascript/0100-same-tree.js @@ -0,0 +1,87 @@ +/** + * Check if both nodes are null (end of a branch in both trees) + * @param {TreeNode} p + * @param {TreeNode} q + * @return {boolean} + */ +var isSameTree = function (p, q) { + // Check if both nodes are null (end of a branch in both trees) + const areBothNodesNull = p == null && q == null; + if (areBothNodesNull) return true; + + // Check if only one node is null (mismatch in tree structure) + const isOnlyOneNodeNull = p == null || q == null; + if (isOnlyOneNodeNull) return false; + + // Check if node values are equal (mismatch in node values) + const doNodesHaveEqualValue = p.val == q.val; + if (!doNodesHaveEqualValue) return false; + + // Recursively check left and right subtrees + return dfs(p, q); +}; + +/** + * * https://leetcode.com/problems/same-tree/ + * * Time complexity is O(N), where N is the total number of nodes in the tree. + * This is because in the worst-case scenario, we need to visit every node once. + + * * Space complexity is O(H), where H is the height of the tree. + * This is because in the worst-case scenario (a skewed tree), the maximum + * amount of space is consumed by the recursive stack. + * @param {*} p + * @param {*} q + * @returns + */ +const dfs = (p, q) => { + const left = isSameTree(p.left, q.left); + const right = isSameTree(p.right, q.right); + + return left && right; +}; + +/** + * https://leetcode.com/problems/same-tree/ + * TIme O(N) | Space O(W) + * @param {TreeNode} p + * @param {TreeNode} q + * @return {boolean} + */ +var isSameTree = function (p, q) { + if (isSameNode(p, q)) return true; + + return bfs([[p, q]]); +}; + +const bfs = (queue) => { + while (queue.length) { + for (let i = queue.length - 1; 0 <= i; i--) { + const [p, q] = queue.shift(); + + if (!isSame(p, q)) return false; + + if (p.left) queue.push([p.left, q.left]); + if (p.right) queue.push([p.right, q.right]); + } + } + + return true; +}; + +const isSameNode = (p, q) => { + const isBaseCase = !(p || q); + if (isBaseCase) return true; + + const isBalanced = p && q; + if (!isBalanced) return false; + + const isSame = p.val === q.val; + if (!isSame) return false; + + return true; +}; + +const isSame = (p, q) => + isSameNode(p, q) && + isSameNode(p.left, q.left) && + isSameNode(p.right, q.right); diff --git a/javascript/0101-symmetric-tree.js b/javascript/0101-symmetric-tree.js new file mode 100644 index 000000000..8c311f51f --- /dev/null +++ b/javascript/0101-symmetric-tree.js @@ -0,0 +1,29 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * DFS | Tree-trevarsal + * Time O(n) | Space O(n) + * https://leetcode.com/problems/symmetric-tree/ + * @param {TreeNode} root + * @return {boolean} + */ +var isSymmetric = function(root) { + return dfs(root.left, root.right); +}; + +const dfs = (node1, node2) => { + + if (!node1 && !node2) return true; + + if (node1 && !node2) return false; + if (!node1 && node2) return false; + if (node1.val !== node2.val) return false; + + return dfs(node1.right, node2.left) && dfs(node1.left, node2.right); +} diff --git a/javascript/0102-binary-tree-level-order-traversal.js b/javascript/0102-binary-tree-level-order-traversal.js new file mode 100644 index 000000000..62e4d2d83 --- /dev/null +++ b/javascript/0102-binary-tree-level-order-traversal.js @@ -0,0 +1,57 @@ +/** + * https://leetcode.com/problems/binary-tree-level-order-traversal/ + * Time O(N) | Space O(W) + * Note that the time complexity is actually O(N^2) if we consider the fact that we use an array as a queue. Calling Array.shift() takes O(N). + * @param {TreeNode} root + * @return {number[][]} + */ +var levelOrder = function(root) { + const isBaseCase = root === null; + if (isBaseCase) return []; + + return bfs([ root ]); +}; + +const bfs = (queue /* Space O(W) */, levels = []) => { + while (queue.length) { // Time O(N) + const level = []; + + for (let i = (queue.length - 1); 0 <= i; i--) { + const node = queue.shift(); // Time O(N) ... This can be O(1) if we use an actual queue data structure + + if (node.left) queue.push(node.left); + if (node.right) queue.push(node.right); + + level.push(node.val); + } + + levels.push(level.slice()); + } + + return levels; +} + +/** + * https://leetcode.com/problems/binary-tree-level-order-traversal/ + * Time O(N) | Space O(H) + * @param {TreeNode} root + * @return {number[]} + */ + var levelOrder = function(root, level = 0, levels = []) { + const isBaseCase = root === null; + if (isBaseCase) return levels; + + const isLastNode = level === levels.length; + if (isLastNode) levels.push([]); + + levels[level].push(root.val); + + return dfs(root, level, levels); // Time O(N) | Space O(H) +} + +const dfs = (root, level, levels) => { + if (root.left) levelOrder(root.left, (level + 1), levels); + if (root.right) levelOrder(root.right, (level + 1), levels); + + return levels; +} diff --git a/javascript/0103-binary-tree-zigzag-level-order-traversal.js b/javascript/0103-binary-tree-zigzag-level-order-traversal.js new file mode 100644 index 000000000..fccd8f7bf --- /dev/null +++ b/javascript/0103-binary-tree-zigzag-level-order-traversal.js @@ -0,0 +1,41 @@ +/** + * BFS + * Time O(N) | Space O(N) + * https://leetcode.com/problems/binary-tree-zigzag-level-order-traversal/ + * @param {TreeNode} root + * @return {number[][]} + */ +var zigzagLevelOrder = (root) => { + const isEdgeBase = (root === null); + if (isEdgeBase) return []; + + return search(root);/* Time O(N) | Space O(N) */ +}; + +var search = (root, isZigZag = true, order = []) => { + const queue = new Queue([ root ]); + + while (!queue.isEmpty()) { /* Time O(N) */ + const levels = []; + + bfs(queue, isZigZag, levels); /* Time O(WIDTH) | Space O(WIDTH) */ + order.push(levels); /* Space O(N) */ + isZigZag = !isZigZag; + } + + return order; +} + +const bfs = (queue, isZigZag, levels) => { + for (let level = queue.size(); (0 < level); level--) {/* Time O(WIDTH) */ + const { left, val, right } = queue.dequeue(); + + if (left) queue.enqueue(left); /* Space O(WIDTH) */ + if (right) queue.enqueue(right);/* Space O(WIDTH) */ + + levels.push(val); /* Space O(N) */ + } + + if (!isZigZag) levels.reverse(); +} + diff --git a/javascript/0104-maximum-depth-of-binary-tree.js b/javascript/0104-maximum-depth-of-binary-tree.js new file mode 100644 index 000000000..9b2b7a8af --- /dev/null +++ b/javascript/0104-maximum-depth-of-binary-tree.js @@ -0,0 +1,75 @@ +/** + * https://leetcode.com/problems/maximum-depth-of-binary-tree/ + * Time O(N) | Space O(N) + * @param {TreeNode} root + * @return {number} + */ +var maxDepth = function(root) { + const isBaseCase = root === null; + if (isBaseCase) return 0; + + return dfs(root); +}; + +const dfs = (root) => { + const left = maxDepth(root.left); + const right = maxDepth(root.right); + + const height = Math.max(left, right); + + return height + 1; +}; + +/** + * https://leetcode.com/problems/maximum-depth-of-binary-tree/ + * Time O(N) | Space O(N) + * @param {TreeNode} root + * @return {number} + */ +var maxDepth = function(root) { + const isBaseCase = root === null; + if (isBaseCase) return 0; + + return iterativeDfs([[root, 1]]); +}; + +const iterativeDfs = (stack, height = 0) => { + while (stack.length) { + const [root, depth] = stack.pop(); + + height = Math.max(height, depth); + + if (root.right) stack.push([root.right, depth + 1]); + if (root.left) stack.push([root.left, depth + 1]); + } + + return height; +}; + +/** + * https://leetcode.com/problems/maximum-depth-of-binary-tree/ + * Time O(N) | Space O(N) + * @param {TreeNode} root + * @return {number} + */ +var maxDepth = function(root) { + const isBaseCase = root === null; + if (isBaseCase) return 0; + + return bfs([[ root, 0 ]]); +}; + +const bfs = (queue, height = 0) => { + while (queue.length) { + for (let i = (queue.length - 1); 0 <= i; i--) { + const [ root, depth ] = queue.shift(); + + height = Math.max(height, (depth + 1)); + + if (root.left) queue.push([ root.left, (depth + 1) ]); + if (root.right) queue.push([ root.right, (depth + 1) ]); + } + } + + return height; +}; diff --git a/javascript/0105-construct-binary-tree-from-preorder-and-inorder-traversal.js b/javascript/0105-construct-binary-tree-from-preorder-and-inorder-traversal.js new file mode 100644 index 000000000..53a6fccda --- /dev/null +++ b/javascript/0105-construct-binary-tree-from-preorder-and-inorder-traversal.js @@ -0,0 +1,62 @@ +/** + * https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/ + * Time O(N^2) | Space(H) + * @param {number[]} preorder + * @param {number[]} inorder + * @return {TreeNode} + */ + var buildTree = function(preorder, inorder) { + const isBaseCase = !preorder.length || !inorder.length; + if (isBaseCase) return null; + + return dfs(preorder, inorder); +} + +var dfs = (preorder, inorder) => { + const { leftInorder, mid, rightInorder } = getPointers(preorder, inorder); + const root = new TreeNode(inorder[mid]); + + root.left = buildTree(preorder, leftInorder); + root.right = buildTree(preorder, rightInorder); + + return root; +} + +const getPointers = (preorder, inorder) => { + const next = preorder.shift(); + const mid = inorder.indexOf(next); + const leftInorder = inorder.slice(0, mid); + const rightInorder = inorder.slice(mid + 1); + + return { leftInorder, mid, rightInorder }; +} + +/** + * https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/ + * Time O(N) | Space(H) + * @param {number[]} preorder + * @param {number[]} inorder + * @return {TreeNode} + */ + var buildTree = function(preorder, inorder, max = -Infinity, indices = { preorder: 0, inorder: 0 }) { + const isBaseCase = preorder.length <= indices.inorder; + if (isBaseCase) return null; + + const isAtEnd = inorder[indices.inorder] === max; + if (isAtEnd) { + indices.inorder++; + return null; + } + + return dfs(preorder, inorder, max, indices); +} + +var dfs = (preorder, inorder, max, indices) => { + const val = preorder[indices.preorder++] + const root = new TreeNode(val); + + root.left = buildTree(preorder, inorder, root.val, indices); + root.right = buildTree(preorder, inorder, max, indices); + + return root; +} diff --git a/javascript/0106-construct-binary-tree-from-inorder-and-postorder-traversal.js b/javascript/0106-construct-binary-tree-from-inorder-and-postorder-traversal.js new file mode 100644 index 000000000..a5fa99148 --- /dev/null +++ b/javascript/0106-construct-binary-tree-from-inorder-and-postorder-traversal.js @@ -0,0 +1,47 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * Recursion | Tree + * Time O(n) | Space O(n) + * https://leetcode.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/ + * @param {number[]} inorder + * @param {number[]} postorder + * @return {TreeNode} + */ +var buildTree = function(inorder, postorder) { + + + let globleIdx = inorder.length - 1; + + const dfs = (start, end) => { + if(start === end) { + globleIdx--; + return new TreeNode(inorder[start]); + } + + if(start > end) return null; + + let i = start; + + while(i < end + 1){ + if(inorder[i] === postorder[globleIdx]) break; + i++; + } + + globleIdx--; + const currRoot = new TreeNode(inorder[i]); + + currRoot.right = dfs(i + 1, end); + currRoot.left = dfs(start, i - 1); + + return currRoot; + } + + return dfs(0, globleIdx); +}; diff --git a/javascript/0108-convert-sorted-array-to-binary-search-tree.js b/javascript/0108-convert-sorted-array-to-binary-search-tree.js new file mode 100644 index 000000000..77f799832 --- /dev/null +++ b/javascript/0108-convert-sorted-array-to-binary-search-tree.js @@ -0,0 +1,89 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ + +/** + * DFS - Preorder | Left as mid + * Time O(N) | Space O(log(N)) + * https://leetcode.com/problems/convert-sorted-array-to-binary-search-tree/ + * @param {number[]} nums + * @return {TreeNode} + */ +var sortedArrayToBST = (nums, left = 0, right = (nums.length - 1)) => { + const isBaseCase = (right < left); + if (isBaseCase) return null; + + return dfs(nums, left, right);/* Time O(N) | Space O(log(N)) */ +}; + +var dfs = (nums, left, right) => { + const mid = (left + right) >> 1; + + const root = new TreeNode(nums[mid]); /* | Ignore Auxillary Space O(N) */ + + root.left = sortedArrayToBST(nums, left, (mid - 1)); /* Time O(N) | Space O(log(N)) */ + root.right = sortedArrayToBST(nums, (mid + 1), right);/* Time O(N) | Space O(log(N)) */ + + return root; +} + +/** + * DFS - Preorder | Right as mid + * Time O(N) | Space O(log(N)) + * https://leetcode.com/problems/convert-sorted-array-to-binary-search-tree/ + * @param {number[]} nums + * @return {TreeNode} + */ +var sortedArrayToBST = (nums, left = 0, right = (nums.length - 1)) => { + const isBaseCase = (right < left); + if (isBaseCase) return null; + + return dfs(nums, left, right);/* Time O(N) | Space O(log(N)) */ +}; + +var dfs = (nums, left, right) => { + let mid = (left + right) >> 1; + + const isOdd = (((left + right) % 2) === 1); + if (isOdd) mid += 1; + + const root = new TreeNode(nums[mid]); /* | Ignore Auxillary Space O(N) */ + + root.left = sortedArrayToBST(nums, left, (mid - 1)); /* Time O(N) | Space O(log(N)) */ + root.right = sortedArrayToBST(nums, (mid + 1), right);/* Time O(N) | Space O(log(N)) */ + + return root; +} + +/** + * DFS - Preorder | Random as mid + * Time O(N) | Space O(log(N)) + * https://leetcode.com/problems/convert-sorted-array-to-binary-search-tree/ + * @param {number[]} nums + * @return {TreeNode} + */ +var sortedArrayToBST = (nums, left = 0, right = (nums.length - 1)) => { + const isBaseCase = (right < left); + if (isBaseCase) return null; + + return dfs(nums, left, right);/* Time O(N) | Space O(log(N)) */ +}; + +var dfs = (nums, left, right) => { + let mid = (left + right) >> 1; + + const isOdd = (((left + right) % 2) === 1); + if (isOdd) mid += Math.floor(Math.random() * 2); + + const root = new TreeNode(nums[mid]); /* | Ignore Auxillary Space O(N) */ + + root.left = sortedArrayToBST(nums, left, (mid - 1)); /* Time O(N) | Space O(log(N)) */ + root.right = sortedArrayToBST(nums, (mid + 1), right);/* Time O(N) | Space O(log(N)) */ + + return root; +} diff --git a/javascript/0110-balanced-binary-tree.js b/javascript/0110-balanced-binary-tree.js new file mode 100644 index 000000000..30dacdd8c --- /dev/null +++ b/javascript/0110-balanced-binary-tree.js @@ -0,0 +1,78 @@ +/** + * https://leetcode.com/problems/balanced-binary-tree/ + * TIme O(N) | Space O(H) + * @param {TreeNode} root + * @return {boolean} + */ +var isBalanced = function(root) { + const isBaseCase = root === null; + if (isBaseCase) return true; + if (!isAcceptableHeight(root)) return false; + if (!isChildBalanced(root)) return false; + + return true; +} + +const isChildBalanced = (root) => { + const left = isBalanced(root.left); + const right = isBalanced(root.right); + + return left && right +} + +const isAcceptableHeight = (root) => { + const left = getHeight(root.left); + const right = getHeight(root.right); + + const difference = Math.abs(left - right); + + return difference <= 1; +} + +const getHeight = (root) => { + const isBaseCase = root === null; + if (isBaseCase) return 0; + + return dfs(root); +} + +var dfs = (root) => { + const left = getHeight(root.left) + const right = getHeight(root.right); + + const height = Math.max(left, right); + + return height + 1; +} + +/** + * https://leetcode.com/problems/balanced-binary-tree/ + * TIme O(N) | Space O(H) + * @param {TreeNode} root + * @return {boolean} + */ + var isBalanced = function (root) { + const [ _height, _isBalanced ] = isRootBalanced(root); + + return _isBalanced; +}; + +var isRootBalanced = (root) => { + const isBaseCase = root === null + if (isBaseCase) return [ -1, true ]; + + return dfs(root) +} + +var dfs = (root) => { + const [ left, isLeftBalanced ] = isRootBalanced(root.left); + const [ right, isRightBalanced ] = isRootBalanced(root.right); + const [ height, difference ] = [ Math.max(left, right), Math.abs(left - right) ]; + + const isAcceptableHeight = difference <= 1; + const _isBalanced = isLeftBalanced && isRightBalanced; + + const _isRootBalanced = _isBalanced && isAcceptableHeight; + + return [ (height + 1), _isRootBalanced ]; +} diff --git a/javascript/0112-path-sum.js b/javascript/0112-path-sum.js new file mode 100644 index 000000000..626496eb0 --- /dev/null +++ b/javascript/0112-path-sum.js @@ -0,0 +1,21 @@ +// problem link https://leetcode.com/problems/path-sum/ +// time complexity O(n) // whatever the number of nodes are. + +var hasPathSum = function(root, targetSum) { + + const ans = []; + function goDFS(node, curruntSum) { + + if(!node) return; + + if(!node.left && !node.right) { + ans.push(node.val + curruntSum); + } + + goDFS(node.left, curruntSum + node.val); + goDFS(node.right, curruntSum + node.val); + } + goDFS(root, 0); + + return ans.includes(targetSum); +}; diff --git a/javascript/0115-distinct-subsequences.js b/javascript/0115-distinct-subsequences.js new file mode 100644 index 000000000..440d0094c --- /dev/null +++ b/javascript/0115-distinct-subsequences.js @@ -0,0 +1,120 @@ +/** + * DP - Top Down + * Matrix - Memoization + * Time O(N * M) | Space (N * M) + * https://leetcode.com/problems/distinct-subsequences/ + * @param {string} s + * @param {string} t + * @return {number} + */ + var numDistinct = (s, t, i = 0, j = 0, memo = initMemo(s, t)) => { + const isBaseCase1 = (s.length < t.length); + if (isBaseCase1) return 0; + + const isBaseCase2 = (j === t.length); + if (isBaseCase2) return 1; + + const isBaseCase3 = (i === s.length); + if (isBaseCase3) return 0; + + const hasSeen = (memo[i][j] !== null); + if (hasSeen) return memo[i][j]; + + return dfs(s, t, i, j, memo);/* Time O(N * M) | Space O((N * M) + HEIGHT) */ +} + +var initMemo = (s, t) => new Array(s.length).fill() + .map(() => new Array(t.length).fill(null)); + +var dfs = (s, t, i, j, memo) => { + const left = numDistinct(s, t, (i + 1), j, memo);/* Time O(N * M) | Space O(HEIGHT) */ + + const isEqual = (s[i] === t[j]); + + const right = isEqual + ? numDistinct(s, t, (i + 1), (j + 1), memo) /* Time O(N * M) | Space O(HEIGHT) */ + : 0; + + memo[i][j] = (left + right); /* | Space O(N * M) */ + return memo[i][j]; +} + +/** + * DP - Bottom Up + * Matrix - Tabulation + * Time O(N * M) | Space (N * M) + * https://leetcode.com/problems/distinct-subsequences/ + * @param {string} s + * @param {string} t + * @return {number} + */ +var numDistinct = (s, t) => { + const tabu = initTabu(s, t);/* Time O(N * M) | Space O(N * M) */ + + search(s, t, tabu); /* Time O(N * M) | Space O(N * M) */ + + return tabu[0][0]; +} + +var initTabu = (s, t) => { + const tabu = new Array(s.length + 1).fill()/* Time O(N) | Space O(N) */ + .map(() => new Array(t.length + 1)); /* Time O(M) | Space O(M) */ + + tabu[s.length].fill(0); /* | Space O(N * M) */ + + for (let r = 0; r <= s.length; ++r) { /* Time O(N) */ + tabu[r][t.length] = 1; /* | Space O(N * M) */ + } + + return tabu; +} + +var search = (s, t, tabu) => { + for (let r = (s.length - 1); (0 <= r); r--) {/* Time O(N) */ + for (let c = (t.length - 1); (0 <= c); c--) {/* Time O(M) */ + const left = tabu[r + 1][c]; + + const isEqual = (s[r] === t[c]); + + const right = isEqual + ? tabu[r + 1][c + 1] + : 0 + + tabu[r][c] = left + right; /* Space O(N * M) */ + } + } +} + +/** + * DP - Bottom Up + * Matrix - Tabulation + * Time O(N * M) | Space O(M) + * https://leetcode.com/problems/distinct-subsequences/ + * @param {string} s + * @param {string} t + * @return {number} + */ +var numDistinct = (s, t) => { + const tabu = initTabu(t);/* Time O(M) | Space O(M) */ + + search(s, t, tabu); /* Time O(N * M) | Space O(M) */ + + return tabu[0]; +} + +var initTabu = (t) => new Array(t.length).fill(0);/* Time O(M) | Space O(M) */ + +var search = (s, t, tabu) => { + for (let row = (s.length - 1); (0 <= row); row--) {/* Time O(N) */ + let prev = 1; + + for (let col = (t.length - 1); (0 <= col); col--) {/* Time O(M) */ + const curr = tabu[col]; + + const isEqual = (s[row] === t[col]); + if (isEqual) tabu[col] += prev; /* Space O(M) */ + + prev = curr; + } + } +} diff --git a/javascript/0116-populating-next-right-pointers-in-each-node.js b/javascript/0116-populating-next-right-pointers-in-each-node.js new file mode 100644 index 000000000..e9e11cd6c --- /dev/null +++ b/javascript/0116-populating-next-right-pointers-in-each-node.js @@ -0,0 +1,37 @@ +/** + * BFS + * Time O(n) | Space O(1) + * + * https://leetcode.com/problems/populating-next-right-pointers-in-each-node/ + * // Definition for a Node. + * function Node(val, left, right, next) { + * this.val = val === undefined ? null : val; + * this.left = left === undefined ? null : left; + * this.right = right === undefined ? null : right; + * this.next = next === undefined ? null : next; + * }; + */ +/** + * @param {Node} root + * @return {Node} + */ + +var connect = function(root) { + + let currentNode = root; + let nextLevelNode = root && root.left; + + while(currentNode && nextLevelNode) { + currentNode.left.next = currentNode.right; + if(currentNode.next) { + currentNode.right.next = currentNode.next.left; + } + currentNode = currentNode.next; + if(!currentNode) { + currentNode = nextLevelNode; + nextLevelNode = currentNode.left; + } + } + + return root; +}; diff --git a/javascript/0118-pascals-triangle.js b/javascript/0118-pascals-triangle.js new file mode 100644 index 000000000..9d2152d3f --- /dev/null +++ b/javascript/0118-pascals-triangle.js @@ -0,0 +1,17 @@ + +// link to the problem https://leetcode.com/problems/pascals-triangle/ +// the time complexity will basically be the number of elements in pascale tringle. roughly height of tringle * number of honeycomb in each row. +// O(n^2); + +var generate = function (numRows) { + const res = [[1]]; + + for (let i = 1; i < numRows; i++) { + res[i] = []; + for (let k = 0; k < i + 1; k++) { + res[i][k] = (res[i - 1][k] || 0) + (res[i - 1][k - 1] || 0); + } + } + + return res; +}; diff --git a/javascript/0121-best-time-to-buy-and-sell-stock.js b/javascript/0121-best-time-to-buy-and-sell-stock.js new file mode 100644 index 000000000..9fcb7725b --- /dev/null +++ b/javascript/0121-best-time-to-buy-and-sell-stock.js @@ -0,0 +1,44 @@ +/** + * https://leetcode.com/problems/best-time-to-buy-and-sell-stock/ + * Time O(N) | Space O(1) + * @param {number} prices + * @return {number} + */ +var maxProfit = function (prices) { + let [left, right, max] = [0, 1, 0]; + + while (right < prices.length) { + const canSlide = prices[right] <= prices[left]; + if (canSlide) left = right; + + const window = prices[right] - prices[left]; + + max = Math.max(max, window); + right++; + } + + return max; +}; + +/** + * Another approach without using sliding window + * https://leetcode.com/problems/best-time-to-buy-and-sell-stock/ + * Time O(N) | Space O(1) + * @param {number} prices + * @return {number} + */ + +var maxProfit = function (prices) { + let min = prices[0]; + let max = min; + let value = 0; + for (let i = 0; i < prices.length; i++) { + if (i != prices.length - 1 && prices[i] <= min) { + max = min = prices[i]; + } else if (prices[i] > max) { + max = prices[i]; + } + value = max - min > value ? max - min : value; + } + return value; +}; diff --git a/javascript/0122-best-time-to-buy-and-sell-stock-ii.js b/javascript/0122-best-time-to-buy-and-sell-stock-ii.js new file mode 100644 index 000000000..9057b1fe1 --- /dev/null +++ b/javascript/0122-best-time-to-buy-and-sell-stock-ii.js @@ -0,0 +1,13 @@ +// problem link https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii +// time coplexity O(n) + +var maxProfit = function(prices) { + let maxProfit = 0; + for(let i = 0; i < prices.length; i++) { + if(prices[i] < prices[i+1]) { + maxProfit += prices[i+1] - prices[i]; + } + } + + return maxProfit; +}; diff --git a/javascript/0124-binary-tree-maximum-path-sum.js b/javascript/0124-binary-tree-maximum-path-sum.js new file mode 100644 index 000000000..ad41a02c3 --- /dev/null +++ b/javascript/0124-binary-tree-maximum-path-sum.js @@ -0,0 +1,28 @@ +/** + * https://leetcode.com/problems/binary-tree-maximum-path-sum/ + * Time O(N) | Space O(H) + * @param {TreeNode} root + * @return {number} + */ + var maxPathSum = function(root, maxValue = [ -Infinity ]) { + pathSum(root, maxValue); + + return maxValue[0]; +}; + +const pathSum = (root, maxValue) => { + const isBaseCase = root === null; + if (isBaseCase) return 0; + + return dfs(root, maxValue); +} + +const dfs = (node, maxValue) => { + const left = Math.max(0, pathSum(node.left, maxValue)); + const right = Math.max(0, pathSum(node.right, maxValue)); + const sum = left + right + node.val; + + maxValue[0] = Math.max(maxValue[0], sum); + + return Math.max(left, right) + node.val; +} diff --git a/javascript/0125-valid-palindrome.js b/javascript/0125-valid-palindrome.js new file mode 100644 index 000000000..7e3785007 --- /dev/null +++ b/javascript/0125-valid-palindrome.js @@ -0,0 +1,87 @@ +/** + * Array - Filter && Clone && Reverse + * Time O(N) | Space O(N) + * https://leetcode.com/problems/valid-palindrome/ + * @param {string} s + * @return {boolean} + */ +var isPalindrome = function(s) { + if (!s.length) return true; + + const alphaNumeric = filterAlphaNumeric(s);/* Time O(N) | Space O(N) */ + const reversed = reverse(alphaNumeric); /* Time O(N) | Space O(N) */ + + return alphaNumeric === reversed; +}; + +const filterAlphaNumeric = (s, nonAlphaNumeric = new RegExp('[^a-z0-9]','gi')) => s + .toLowerCase() /* Time O(N) | Space O(N) */ + .replace(nonAlphaNumeric, '')/* Time O(N) | Space O(N) */ + +const reverse = (s) => s + .split('')/* Time O(N) | Space O(N) */ + .reverse()/* Time O(N) | Space O(N) */ + .join('');/* Time O(N) | Space O(N) */ + +/** + * 2 Pointer | Midde Convergence + * Time O(N) | Space O(1) + * https://leetcode.com/problems/valid-palindrome/ + * @param {string} s + * @return {boolean} + */ +var isPalindrome = function(s) { + if (s.length <= 1) return true; + + let [left, right] = [0, s.length - 1]; + let leftChar, rightChar; + while (left < right) { + leftChar = s[left]; + rightChar = s[right]; + + // skip char if non-alphanumeric + if (!/[a-zA-Z0-9]/.test(leftChar)) { + left++; + } else if (!/[a-zA-Z0-9]/.test(rightChar)) { + right--; + } else { + // compare letters + if (leftChar.toLowerCase() != rightChar.toLowerCase()) { + return false; + } + left++; + right--; + } + } + return true; +}; + +/** + * 2 Pointer | Midde Convergence | No RegEx | No Copying + * Time O(N) | Space O(1) + * https://leetcode.com/problems/valid-palindrome/ + * @param {string} s + * @return {boolean} + */ +var isPalindrome = function (s) { + const isAlphaNumeric = c => (c.toLowerCase() >= 'a' && c.toLowerCase() <= 'z') || c >= '0' && c <= '9' + + let left = 0; + let right = s.length - 1; + let skipLeft, skipRight, endsEqual = false; + + while (left < right) { + skipLeft = !isAlphaNumeric(s.charAt(left)) + if (skipLeft) { left++; continue; } + + skipRight = !isAlphaNumeric(s.charAt(right)) + if (skipRight) { right--; continue; } + + endsEqual = s.charAt(left).toLowerCase() === s.charAt(right).toLowerCase() + if (!endsEqual) return false + + left++ + right-- + } + return true +}; diff --git a/javascript/0127-word-ladder.js b/javascript/0127-word-ladder.js new file mode 100644 index 000000000..cb8d991b7 --- /dev/null +++ b/javascript/0127-word-ladder.js @@ -0,0 +1,50 @@ +/** + * https://leetcode.com/problems/word-ladder/ + * Time O(ROWS * COLS) | Space O(ROWS * COLS) + * @param {string} beginWord + * @param {string} endWord + * @param {string[]} wordList + * @return {number} + */ + var ladderLength = function(beginWord, endWord, wordList) { + const [ queue, wordSet, seen ] = [ new Queue([[ beginWord, 1 ]]), new Set(wordList), new Set([ beginWord ]) ]; + + return bfs(queue, wordSet, seen, endWord);/* Time O(ROWS * COLS) | Space O(ROWS * COLS) */ +}; + +const bfs = (queue, wordSet, seen, endWord) => { + while (!queue.isEmpty()) { + for (let i = (queue.size() - 1); 0 <= i; i--) { + const [ word, depth ] = queue.dequeue(); + + const isTarget = word === endWord + if (isTarget) return depth + + transform(queue, wordSet, seen, word, depth) + } + } + + return 0 +} + +const transform = (queue, wordSet, seen, word, depth) => { + for (const index in word) { + for (const char of 'abcdefghijklmnopqrstuvwxyz') { + const neighbor = getNeighbor(word, index, char); + + const hasSeen = !wordSet.has(neighbor) || seen.has(neighbor); + if (hasSeen) continue; + + queue.enqueue([ neighbor, (depth + 1) ]); + seen.add(neighbor); + } + } +} + +const getNeighbor = (word, index, char) => { + const neighbor = word.split(''); + + neighbor[index] = char; + + return neighbor.join(''); +} diff --git a/javascript/0128-longest-consecutive-sequence.js b/javascript/0128-longest-consecutive-sequence.js new file mode 100644 index 000000000..02e753678 --- /dev/null +++ b/javascript/0128-longest-consecutive-sequence.js @@ -0,0 +1,94 @@ +/** + * Brute Force + * Greedy - Max Score + * Time O (N^3) | Space O(1) + * https://leetcode.com/problems/longest-consecutive-sequence/ + * @param {number[]} nums + * @return {number} + */ + var longestConsecutive = (nums, maxScore = 0) => { + for (const num of nums) {/* Time O(N) */ + let [ currNum, score ] = [ num, 1 ]; + + while (isStreak(nums, (currNum + 1))) {/* Time O(N * N) */ + currNum++; + score++; + } + + maxScore = Math.max(maxScore, score); + } + + return maxScore; +} + +const isStreak = (nums, num) => { + for (let i = 0; i < nums.length; i++) {/* Time O(N) */ + const isEqual = nums[i] === num + if (isEqual) return true; + } + + return false; +} + +/** + * Sort - HeapSort Space O(1) | QuickSort Space O(log(K)) + * Greedy - Max Score + * Time O (N * log(N)) | Space O(1) + * https://leetcode.com/problems/longest-consecutive-sequence/ + * @param {number[]} nums + * @return {number} + */ + var longestConsecutive = (nums) => { + if (!nums.length) return 0; + + nums.sort((a, b) => a - b);/* Time O(N * log(N)) | Space O(1 || log(N)) */ + + return search(nums); /* Time O(N) */ +} + +const search = (nums) => { + let [ maxScore, score ] = [ 1, 1 ]; + + for (let i = 1; i < nums.length; i++) {/* Time O(N) */ + const isPrevDuplicate = nums[i - 1] === nums[i] + if (isPrevDuplicate) continue + + const isStreak = nums[i] === ((nums[i - 1]) + 1) + if (isStreak) { score++; continue; } + + maxScore = Math.max(maxScore, score); + score = 1; + } + + return Math.max(maxScore, score); +} + +/** + * Hash Set - Intelligent Sequence + * Greedy - Max Score + * Time O (N) | Space O(N) + * https://leetcode.com/problems/longest-consecutive-sequence/ + * @param {number[]} nums + * @return {number} + */ + var longestConsecutive = (nums, maxScore = 0) => { + const numSet = new Set(nums); /* Time O(N) | Space O(N) */ + + for (const num of [ ...numSet ]) { /* Time O(N) */ + const prevNum = num - 1; + + if (numSet.has(prevNum)) continue;/* Time O(N) */ + + let [ currNum, score ] = [ num, 1 ]; + + const isStreak = () => numSet.has(currNum + 1) + while (isStreak()) { /* Time O(N) */ + currNum++; + score++; + } + + maxScore = Math.max(maxScore, score); + } + + return maxScore; +} \ No newline at end of file diff --git a/javascript/0129-sum-root-to-leaf-numbers.js b/javascript/0129-sum-root-to-leaf-numbers.js new file mode 100644 index 000000000..45a4883b6 --- /dev/null +++ b/javascript/0129-sum-root-to-leaf-numbers.js @@ -0,0 +1,32 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * Tree | pre-order-traversal + * Time O(n) | Space O(n) + * https://leetcode.com/problems/sum-root-to-leaf-numbers + * @param {TreeNode} root + * @return {number} + */ +var sumNumbers = function(root) { + + let total = 0; + const dfs = (node, num) => { + if(!node.left && !node.right) { + num = num + node.val; + total += +num; + return; + } + + node.left && dfs(node.left, num + node.val); + node.right && dfs(node.right, num + node.val); + } + + dfs(root, ""); + return total; +}; diff --git a/javascript/0130-surrounded-regions.js b/javascript/0130-surrounded-regions.js new file mode 100644 index 000000000..3fb409c1d --- /dev/null +++ b/javascript/0130-surrounded-regions.js @@ -0,0 +1,123 @@ +/** + * https://leetcode.com/problems/surrounded-regions/ + * Time O(ROWS * COLS) | Space O(ROWS * COLS) + * @param {character[][]} board + * @return {void} Do not return anything, modify board in-place instead. + */ + var solve = function solve(board) { + searchRows(board);/* Time O(ROWS * COLS) | Space O(ROWS * COLS) */ + searchCols(board);/* Time O(ROWS * COLS) | Space O(ROWS * COLS) */ + searchGrid(board);/* Time O(ROWS * COLS) | Space O(ROWS * COLS) */ +} + +var searchRows = (board) => { + const [ rows, cols ] = [ board.length, board[0].length ]; + + for (let row = 0; row < rows; row++) { /* Time O(ROWS) */ + dfs(board, row, rows, 0, cols); /* Space O(ROWS) */ + dfs(board, row, rows, (cols - 1), cols);/* Space O(ROWS) */ + } +} + +var searchCols = (board) => { + const [ rows, cols ] = [ board.length, board[0].length ]; + + for (let col = 1; col < (cols - 1); col++) {/* Time O(COLS) */ + dfs(board, 0, rows, col, cols); /* Space O(COLS) */ + dfs(board, (rows - 1), rows, col, cols);/* Space O(COLS) */ + } +} + +var searchGrid = (board) => { + const [ rows, cols ] = [ board.length, board[0].length ]; + + for (let row = 0; row < rows; row++) {/* Time O(ROWS) */ + for (let col = 0; col < cols; col++) {/* Time O(COLS) */ + const isO = board[row][col] === 'O'; + if (isO) board[row][col] = 'X'; + + const isStar = board[row][col] === '*'; + if (isStar) board[row][col] = 'O'; + } + } +} + +const dfs = (board, row, rows, col, cols) => { + const isBaseCase = board[row][col] !== 'O'; + if (isBaseCase) return; + + board[row][col] = '*'; + + for (const [ _row, _col ] of getNeighbors(row, rows, col, cols)) { + dfs(board, _row, rows, _col, cols);/* Time O(HEIGHT) | Space O(HEIGHT) */ + } +} + +var getNeighbors = (row, rows, col, cols) => [ [0, 1], [0, -1], [1, 0], [-1, 0] ] + .map(([ _row, _col ]) => [ (row + _row), (col + _col)]) + .filter(([ _row, _col ]) => (0 <= _row) && (_row < rows) && (0 <= _col) && (_col < cols)) + + +/** + * https://leetcode.com/problems/surrounded-regions/ + * Time O(ROWS * COLS) | Space O(ROWS * COLS) + * @param {character[][]} board + * @return {void} Do not return anything, modify board in-place instead. + */ + var solve = function solve(board, queue = new Queue([])) { + searchRows(board, queue);/* Time O(ROWS + COLS) | Space O(ROWS + COLS) */ + searchCols(board, queue);/* Time O(ROWS + COLS) | Space O(ROWS + COLS) */ + bfs(board, queue); /* Time O(ROWS * COLS) | Space O(ROWS * COLS) */ + searchGrid(board); /* Time O(ROWS * COLS) */ +} + +var searchRows = (board, queue) => { + const [ rows, cols ] = [ board.length, board[0].length ] + + for (let row = 0; row < rows; row++) { /* Time O(ROWS) */ + queue.enqueue([ row, 0 ]); /* Space O(ROWS) */ + queue.enqueue([ row, (cols - 1) ]);/* Space O(ROWS) */ + } +} + +var searchCols = (board, queue) => { + const [ rows, cols ] = [ board.length, board[0].length ] + + for (let col = 0; col < (cols - 1); col++) {/* Time O(COLS) */ + queue.enqueue([ 0, col ]); /* Space O(COLS) */ + queue.enqueue([ (rows - 1), col ]); /* Space O(COLS) */ + } +} + +var bfs = (board, queue) => { + const [ rows, cols ] = [ board.length, board[0].length ]; + + while (!queue.isEmpty()) { + for (let i = (queue.size() - 1); 0 <= i; i--) {/* Time O(WIDTH) */ + const [ row, col ] = queue.dequeue(); + + const isBaseCase = board[row][col] !== 'O'; + if (isBaseCase) continue; + + board[row][col] = '*'; + + for (const [ _row, _col ] of getNeighbors(row, rows, col, cols)) { + queue.enqueue([ _row, _col ]); /* Space O(WIDTH) */ + } + } + } +} + +var searchGrid = (board) => { + const [ rows, cols ] = [ board.length, board[0].length ]; + + for (let row = 0; row < rows; row++) {/* Time O(ROWS) */ + for (let col = 0; col < cols; col++) {/* Time O(COLS) */ + const isO = board[row][col] === 'O'; + if (isO) board[row][col] = 'X'; + + const isStar = board[row][col] === '*'; + if (isStar) board[row][col] = 'O'; + } + } +} \ No newline at end of file diff --git a/javascript/0131-palindrome-partitioning.js b/javascript/0131-palindrome-partitioning.js new file mode 100644 index 000000000..7c904589e --- /dev/null +++ b/javascript/0131-palindrome-partitioning.js @@ -0,0 +1,39 @@ +/** + * https://leetcode.com/problems/palindrome-partitioning/ + * Time O(N * 2^N) | Space O(N^2) + * @param {string} s + * @return {string[][]} + */ +function partition(s, left = 0, _partition = [], partitions = []) { + const isBaseCase = s.length <= left + if (isBaseCase) { + if (_partition.length) partitions.push(_partition.slice()); + + return partitions + } + + for (let right = left; right < s.length; right++) { + if (!isPalindrome(s, left, right)) continue; + + backTrack(s, left, right, _partition, partitions) + } + + return partitions +} + +const backTrack = (s, left, right, _partition, partitions) => { + _partition.push(s.slice(left, (right + 1))); + partition(s, (right + 1), _partition, partitions); + _partition.pop(); +} + +const isPalindrome = (str, left, right) => { + while (left < right) { + const isSame = str[left] === str[right]; + if (!isSame) return false; + + left++; right--; + } + + return true; +} diff --git a/javascript/0133-clone-graph.js b/javascript/0133-clone-graph.js new file mode 100644 index 000000000..5cd7f307d --- /dev/null +++ b/javascript/0133-clone-graph.js @@ -0,0 +1,68 @@ +/** + * https://leetcode.com/problems/clone-graph/ + * Time O(V + E) | Space O(N) + * @param {Node} node + * @return {Node} + */ +var cloneGraph = function(node, seen = new Map()) { + const isBaseCase = node === null; + if (isBaseCase) return null; + + if (seen.has(node)) return seen.get(node); + + return dfs(node, seen); /* Time O(V + E) | Space O(N) */ +} + +const dfs = (node, seen) => { + const clone = new Node(node.val); + + seen.set(node, clone); /* | Space O(N) */ + + for (const neighbor of node.neighbors) { + const cloneNeighbor = cloneGraph(neighbor, seen);/* Time O(V + E) | Space O(H) */ + + clone.neighbors.push(cloneNeighbor); /* | Space O(V + E) */ + } + + return clone; +} + +/** + * https://leetcode.com/problems/clone-graph/ + * Time O(V + E) | Space O(N) + * @param {Node} node + * @return {Node} + */ + var cloneGraph = function(node, seen = new Map()) { + const isBaseCase = node === null; + if (isBaseCase) return null; + + seen.set(node, new Node(node.val)); /* | Space O(N) */ + + bfs(new Queue([ node ]), seen); /* Time O(V + E) | Space O(N) */ + + return seen.get(node); +}; + +const bfs = (queue, seen) => { + while (!queue.isEmpty()) { /* Time O(V + E) */ + for (let i = (queue.size() - 1); 0 <= i; i--) {/* Time O(W) */ + const node = queue.dequeue(); + + cloneNeighbors(node, seen, queue); /* Space O(N) */ + } + } +} + +const cloneNeighbors = (node, seen, queue) => { + for (const neighbor of node.neighbors) { + if (!seen.has(neighbor)) { + seen.set(neighbor, new Node(neighbor.val));/* Space O(N) */ + queue.enqueue(neighbor); /* Space O(W) */ + } + + const [ parentClone, neighborClone ] = [ seen.get(node), seen.get(neighbor) ]; + + parentClone.neighbors.push(neighborClone); /* Space O(V + E) */ + } +} diff --git a/javascript/0134-gas-station.js b/javascript/0134-gas-station.js new file mode 100644 index 000000000..3f4404917 --- /dev/null +++ b/javascript/0134-gas-station.js @@ -0,0 +1,48 @@ +/** + * https://leetcode.com/problems/gas-station/ + * Time: O(n) + * @param {number[]} gas + * @param {number[]} cost + * @return {number} + */ +var canCompleteCircuit = function (gas, cost) { + let netDistance = 0; + let res = 0; + + //Checks if theres enough gas to complete a cycle + if (gas.reduce((a, b) => a + b) - cost.reduce((a, b) => a + b) < 0) return -1; + + // Finds the first appearence of a positive netDistance, if the cycle can't + // be completed (netDistance < 0), starts cycle again @ the next positive netDistance value. + for (let i = 0; i < gas.length; i++) { + netDistance += gas[i] - cost[i]; + + if (netDistance < 0) { + netDistance = 0; + res = i + 1; + } + } + + return res; +}; + +/** + * @param {number[]} gas + * @param {number[]} cost + * @return {number} + */ + var canCompleteCircuit = function(gas, cost) { + let [ totalTank, currTank, startingStation ] = [ 0, 0, 0 ] + + for (let i = 0; i < gas.length; i++) { + totalTank += gas[i] - cost[i]; + currTank += gas[i] - cost[i]; + + const isEmpty = currTank < 0; + if (isEmpty) { startingStation = (i + 1); currTank = 0; } + } + + return 0 <= totalTank + ? startingStation + : -1; +} \ No newline at end of file diff --git a/javascript/0135-candy.js b/javascript/0135-candy.js new file mode 100644 index 000000000..f3d275b81 --- /dev/null +++ b/javascript/0135-candy.js @@ -0,0 +1,31 @@ +/** + * Array + * Time O(n) | Space O(n) + * https://leetcode.com/problems/candy/ + * @param {number[]} ratings + * @return {number} + */ +var candy = function(ratings) { + + const ltr = new Array(ratings.length).fill(1); + const rtl = new Array(ratings.length).fill(1); + + // go from left to right + for (let i = 1; i < ratings.length; i++) { + if (ratings[i] > ratings[i - 1]) { + ltr[i] = ltr[i - 1] + 1; + } + } + // go from right to left + for (let i = ratings.length - 2; i > -1; i--) { + if (ratings[i] > ratings[i + 1]) { + rtl[i] = rtl[i + 1] + 1; + } + } + // calc minimum + let candy = 0; + for (let i = 0; i < ratings.length; i++) { + candy += Math.max(ltr[i], rtl[i]); + } + return candy; +}; diff --git a/javascript/0136-single-number.js b/javascript/0136-single-number.js new file mode 100644 index 000000000..5c169e20b --- /dev/null +++ b/javascript/0136-single-number.js @@ -0,0 +1,13 @@ +/** + * https://leetcode.com/problems/single-number/ + * Time O(N) | Space O(1) + * @param {number[]} nums + * @return {number} + */ +var singleNumber = function (nums, xor = 0) { + for (num of nums) { + xor ^= num; + } + + return xor; +}; diff --git a/javascript/0138-copy-list-with-random-pointer.js b/javascript/0138-copy-list-with-random-pointer.js new file mode 100644 index 000000000..5536d3b03 --- /dev/null +++ b/javascript/0138-copy-list-with-random-pointer.js @@ -0,0 +1,65 @@ +/** + * https://leetcode.com/problems/copy-list-with-random-pointer/ + * Time O(N) | Space O(N) + * @param {Node} head + * @return {Node} + */ +var copyRandomList = function(head, map = new Map()) { + if (!head) return null; + if (map.has(head)) return map.get(head); + + const clone = new Node(head.val); + + map.set(head, clone); /* | Space O(N) */ + clone.next = copyRandomList(head.next, map); /* Time O(N) | Space O(N) */ + clone.random = copyRandomList(head.random, map);/* Time O(N) | Space O(N) */ + + return clone; +} + +/** + * https://leetcode.com/problems/copy-list-with-random-pointer/ + * Time O(N) | Space O(1) + * @param {Node} head + * @return {Node} + */ +var copyRandomList = function(head) { + if (!head) return null; + + cloneNode(head); /* Time O(N) */ + connectRandomNode(head); /* Time O(N) */ + + return connectNode(head);/* Time O(N) */ +}; + +const cloneNode = (curr) => { + while (curr) { /* Time O(N) */ + const node = new Node(curr.val); + + node.next = curr.next; + curr.next = node; + curr = node.next; + } + + return curr; +} + +const connectRandomNode = (curr) => { + while (curr) { /* Time O(N) */ + curr.next.random = curr.random?.next || null; + curr = curr.next.next; + } +} + +const connectNode = (head) => { + let [ prev, curr, next ] = [ head, head.next, head.next ]; + + while (prev) { /* Time O(N) */ + prev.next = prev.next.next; + curr.next = curr?.next?.next || null; + prev = prev.next; + curr = curr.next; + } + + return next +} \ No newline at end of file diff --git a/javascript/0139-word-break.js b/javascript/0139-word-break.js new file mode 100644 index 000000000..42daf415b --- /dev/null +++ b/javascript/0139-word-break.js @@ -0,0 +1,179 @@ +/** + * Brute Force - DFS + * Hash Set - Distinct Keys + * Time O(2^N) | Space O(N) + * https://leetcode.com/problems/word-break/ + * @param {string} s + * @param {string[]} wordDict + * @return {boolean} + */ + var wordBreak = (s, wordDict) => { + const wordSet = new Set(wordDict);/* Time O(N) | Space O(N) */ + + return canBreak(s, wordSet); /* Time O(2^N) | Space O(N) */ +}; + +var canBreak = (s, wordSet, start = 0) => { + const isBaseCase = (start === s.length); + if (isBaseCase) return true; + + return dfs(s, wordSet, start);/* Time O(2^N) | Space O(N) */ +} + +var dfs = (s, wordSet, start) => { + for (let end = (start + 1); end <= s.length; end++) {/* Time O(N) */ + const word = s.slice(start, end); /* Time O(N) | Space O(N) */ + + const _canBreak = wordSet.has(word) + && canBreak(s, wordSet, end); /* Time O(2^N) | Space O(N) */ + if (_canBreak) return true; + } + + return false; +} + +/** + * DP - Top Down + * Array - Memoization + * Hash Set - Distinct Keys + * Time O(N^3) | Space O(N) + * https://leetcode.com/problems/word-break/ + * @param {string} s + * @param {string[]} wordDict + * @return {boolean} + */ +var wordBreak = (s, wordDict) => { + const wordSet = new Set(wordDict); /* Time O(N) | Space O(N) */ + const memo = new Array(s.length).fill(null); /* | Space O(N) */ + const start = 0; + + return canBreak(s, wordSet, start, memo); /* Time O(N * N * N) | Space O(N) */ +} + +var canBreak = (s, wordSet, start, memo) => { + const isBaseCase1 = (s.length === start); + if (isBaseCase1) return true; + + const hasSeen = (memo[start] !== null); + if (hasSeen) return memo[start]; + + return dfs(s, wordSet, start, memo);/* Time O(N * N * N) | Space O(N) */ +} + +var dfs = (s, wordSet, start, memo) => { + for (let end = (start + 1); (end <= s.length); end++) {/* Time O(N) */ + const word = s.slice(start, end); /* Time O(N) | Space O(N) */ + + const _canBreak = wordSet.has(word) + && canBreak(s, wordSet, end, memo); /* Time O(N * N) */ + if (_canBreak) { + memo[start] = true; + return true; + } + } + + memo[start] = false; + return false; +} + +/** + * DP - Bottom Up + * Array - Tabulation + * Hash Set - Distinct Keys + * Time O(N^3) | Space O(N) + * https://leetcode.com/problems/word-break/ + * @param {string} s + * @param {string[]} wordDict + * @return {boolean} + */ + var wordBreak = (s, wordDict) => { + const wordSet = new Set(wordDict);/* Time O(N) | Space O(N) */ + const tabu = initTabu(s); /* | Space O(N) */ + + canBreak(s, wordSet, tabu); /* Time O(N * N * N) | Space O(N) */ + + return tabu[s.length]; +} + +const initTabu = (s) => { + const tabu = new Array((s.length + 1)).fill(false);/* Space O(N) */ + + tabu[0] = true; + + return tabu; +} + +var canBreak = (s, wordSet, tabu) => { + for (let end = 1; (end <= s.length); end++) {/* Time O(N) */ + checkWord(s, wordSet, end, tabu); /* Time O(N * N) | Space O(N) */ + } +} + +var checkWord = (s, wordSet, end, tabu) => { + for (let start = 0; (start < end); start++) {/* Time O(N) */ + const word = s.slice(start, end); /* Time O(N) | Space O(N) */ + + const canBreak = tabu[start] && wordSet.has(word); + if (!canBreak) continue; + + tabu[end] = true; + + return; + } +} + +/** + * Tree Traversal - BFS + * Queue - Level Order Space O(WIDTH) + * Hash Set - Distinct Keys + * Array - Seen + * Time O(N^3) | Space O(N) + * https://leetcode.com/problems/word-break/ + * @param {string} s + * @param {string[]} wordDict + * @return {boolean} + */ +var wordBreak = function(s, wordDict) { + const wordSet = new Set(wordDict); /* Time O(N) | Space O(N) */ + const queue = new Queue([ 0 ]); /* | Space O(N) */ + const seen = new Array(s.length).fill(false);/* | Space O(N) */ + + return bfs(queue, s, wordSet, seen); /* Time O(N * N * N) | Space O(N + WIDTH) */ +} + +const bfs = (queue, s, wordSet, seen) => { + while (!queue.isEmpty()) { + for (let level = (queue.size() - 1); (0 <= level); level--) {/* Time O(N) */ + if (canWordBreak(queue, s, wordSet, seen)) return true; /* Time O(N * N) | Space O(N + WIDTH) */ + } + } + + return false; +} + +var canWordBreak = (queue, s, wordSet, seen) => { + const start = queue.dequeue(); + + const hasSeen = seen[start]; + if (hasSeen) return false; + + if (canBreak(queue, s, start, wordSet)) return true;/* Time O(N * N) | Space O(N + WIDTH) */ + + seen[start] = true; /* | Space O(N) */ + return false; +} + +var canBreak = (queue, s, start, wordSet) => { + for (let end = start + 1; end <= s.length; end++) {/* Time O(N) */ + const word = s.slice(start, end); /* Time O(N) | Space O(N) */ + + if (!wordSet.has(word)) continue; + + queue.enqueue(end); /* | Space O(WIDTH) */ + + const _canBreak = end === s.length; + if (_canBreak) return true; + } + + return false +} diff --git a/javascript/0141-linked-list-cycle.js b/javascript/0141-linked-list-cycle.js new file mode 100644 index 000000000..7ea5826e3 --- /dev/null +++ b/javascript/0141-linked-list-cycle.js @@ -0,0 +1,36 @@ +/** + * https://leetcode.com/problems/linked-list-cycle/ + * Time O(N) | Space O(N) + * @param {ListNode} head + * @return {boolean} + */ +var hasCycle = function(head, seen = new Set()) { + while (head) {/* Time O(N) */ + if (seen.has(head)) return true; + + seen.add(head);/* Space O(N) */ + head = head.next; + } + + return false; +} + +/** + * https://leetcode.com/problems/linked-list-cycle/ + * Time O(N) | Space O(1) + * @param {ListNode} head + * @return {boolean} + */ +var hasCycle = function(head) { + let [ slow, fast ] = [ head, head]; + + while (fast && fast.next) {/* Time O(N) */ + slow = slow.next; + fast = fast.next.next; + + const hasCycle = slow === fast; + if (hasCycle) return true; + } + + return false; +}; \ No newline at end of file diff --git a/javascript/0143-reorder-list.js b/javascript/0143-reorder-list.js new file mode 100644 index 000000000..690ba2a12 --- /dev/null +++ b/javascript/0143-reorder-list.js @@ -0,0 +1,51 @@ +/** + * https://leetcode.com/problems/reorder-list/ + * Time O(N) | Space O(1) + * @param {ListNode} head + * @return {void} Do not return anything, modify head in-place instead. + */ +var reorderList = function(head) { + const mid = getMid(head); /* Time O(N) */ + const reversedFromMid = reverse(mid);/* Time O(N) */ + + reorder(head, reversedFromMid); /* Time O(N) */ +}; + +const getMid = (head) => { + let [ slow, fast ] = [ head, head ]; + + while (fast && fast.next) { /* Time O(N) */ + slow = slow.next; + fast = fast.next.next; + } + + return slow; +} + +const reverse = (head) => { + let [ prev, curr, next ] = [ null, head, null ]; + + while (curr) { /* Time O(N) */ + next = curr.next; + curr.next = prev; + + prev = curr; + curr = next; + } + + return prev; +} + +const reorder = (l1, l2) => { + let [ first, next, second ] = [ l1, null, l2 ]; + + while (second.next) { /* Time O(N) */ + next = first.next; + first.next = second; + first = next; + + next = second.next; + second.next = first; + second = next; + } +} diff --git a/javascript/0144-binary-tree-preorder-traversal.js b/javascript/0144-binary-tree-preorder-traversal.js new file mode 100644 index 000000000..8ba1f7cf8 --- /dev/null +++ b/javascript/0144-binary-tree-preorder-traversal.js @@ -0,0 +1,27 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * Pre-order-traversal + * Time O(n) | Space O(n) + * https://leetcode.com/problems/binary-tree-preorder-traversal/ + * @param {TreeNode} root + * @return {number[]} + */ +var preorderTraversal = function(root) { + + const dfs = (node, pre) => { + if (!node) return pre; + pre.push(node.val); + dfs(node.left, pre); + dfs(node.right, pre); + return pre; + } + + return dfs(root, []); +}; diff --git a/javascript/0145-binary-tree-postorder-traversal.js b/javascript/0145-binary-tree-postorder-traversal.js new file mode 100644 index 000000000..7092bc337 --- /dev/null +++ b/javascript/0145-binary-tree-postorder-traversal.js @@ -0,0 +1,26 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * Post-order-traversal + * Time O(n) | Space O(n) + * https://leetcode.com/problems/binary-tree-postorder-traversal/ + * @param {TreeNode} root + * @return {number[]} + */ +var postorderTraversal = function(root) { + + const dfs = (node, pot) => { + if (!node) return pot; + dfs(node.left, pot); + dfs(node.right, pot); + pot.push(node.val); + return pot; + } + return dfs(root, []); +}; diff --git a/javascript/0146-lru-cache.js b/javascript/0146-lru-cache.js new file mode 100644 index 000000000..2d450f135 --- /dev/null +++ b/javascript/0146-lru-cache.js @@ -0,0 +1,70 @@ +/** + * https://leetcode.com/problems/lru-cache/ + * Time O(1) | Space O(N) + * Your LRUCache object will be instantiated and called as such: + * var obj = new LRUCache(capacity) + * var param_1 = obj.get(key) + * obj.put(key,value) + */ + class LRUCache { + constructor(capacity) { + this.capacity = capacity; + this.map = new Map(); + + this.head = {}; + this.tail = {}; + + this.head.next = this.tail; + this.tail.prev = this.head; + } + + removeLastUsed () { + const [ key, next, prev ] = [ this.head.next.key, this.head.next.next, this.head ]; + + this.map.delete(key); + this.head.next = next; + this.head.next.prev = prev; + } + + put (key, value) { + const hasKey = this.get(key) !== -1; + const isAtCapacity = this.map.size === this.capacity; + + if (hasKey) return (this.tail.prev.value = value); + if (isAtCapacity) this.removeLastUsed(); + + const node = { key, value }; + this.map.set(key, node); + this.moveToFront(node); + } + + moveToFront (node) { + const [ prev, next ] = [ this.tail.prev, this.tail ]; + + this.tail.prev.next = node; + this.connectNode(node, { prev, next }); + this.tail.prev = node; + } + + connectNode (node, top) { + node.prev = top.prev; + node.next = top.next; + } + + get (key) { + const hasKey = this.map.has(key); + if (!hasKey) return -1; + + const node = this.map.get(key); + + this.disconnectNode(node); + this.moveToFront(node); + + return node.value; + } + + disconnectNode (node) { + node.next.prev = node.prev; + node.prev.next = node.next; + } +} diff --git a/javascript/0149-max-points-on-a-line.js b/javascript/0149-max-points-on-a-line.js new file mode 100644 index 000000000..a0712be59 --- /dev/null +++ b/javascript/0149-max-points-on-a-line.js @@ -0,0 +1,27 @@ +/** + * @param {number[][]} points + * @return {number} + */ +var maxPoints = function (points) { + let res = 1; + + for (let i = 0; i < points.length; i++) { + const count = new Map(); + const point1 = points[i]; + for (let j = i + 1; j < points.length; j++) { + const point2 = points[j]; + let slope; + if (point2[0] === point1[0]) { + slope = Number.MAX_SAFE_INTEGER; + } else { + slope = (point2[1] - point1[1]) / (point2[0] - point1[0]); + } + !count.has(slope) + ? count.set(slope, 2) + : count.set(slope, count.get(slope) + 1); + + res = Math.max(res, count.get(slope)); + } + } + return res; +}; diff --git a/javascript/0150-evaluate-reverse-polish-notation.js b/javascript/0150-evaluate-reverse-polish-notation.js new file mode 100644 index 000000000..26160454e --- /dev/null +++ b/javascript/0150-evaluate-reverse-polish-notation.js @@ -0,0 +1,71 @@ +/** + * https://leetcode.com/problems/evaluate-reverse-polish-notation + * Time O(N^2) | Space(1) + * @param {string[]} tokens + * @return {number} + */ +var evalRPN = function(tokens, index = 0) { + while (1 < tokens.length) {/* Time O(N) */ + const isOperation = () => tokens[index] in OPERATORS; + while (!isOperation()) index++;/* Time O(N) */ + + const value = performOperation(tokens, index); + + tokens[index] = value; + tokens.splice((index - 2), 2);/* Time O(N) */ + index--; + } + + return tokens[0]; +}; + +var OPERATORS = { + '+': (a, b) => a + b, + '-': (a, b) => a - b, + '*': (a, b) => a * b, + '/': (a, b) => Math.trunc(a / b), +}; + +var performOperation = (tokens, index) => { + const [ rightNum, leftNum ] = [ Number(tokens[index - 1]), Number(tokens[index - 2]) ] + const operation = OPERATORS[tokens[index]]; + + return operation(leftNum, rightNum); +} + +/** + * https://leetcode.com/problems/evaluate-reverse-polish-notation + * Time O(N) | Space(N) + * @param {string[]} tokens + * @return {number} + */ +var evalRPN = function (tokens, stack = []) { + for (const char of tokens) {/* Time O(N) */ + const isOperation = char in OPERATORS; + if (isOperation) { + const value = performOperation(char, stack); + + stack.push(value); /* Space O(N) */ + + continue; + } + + stack.push(Number(char)); /* Space O(N) */ + } + + return stack.pop(); +} + +var OPERATORS = { + '+': (a, b) => a + b, + '-': (a, b) => a - b, + '*': (a, b) => a * b, + '/': (a, b) => Math.trunc(a / b) +}; + +var performOperation = (char, stack) => { + const [ rightNum, leftNum ] = [ stack.pop(), stack.pop() ]; + const operation = OPERATORS[char]; + + return operation(leftNum, rightNum); +} \ No newline at end of file diff --git a/javascript/0152-maximum-product-subarray.js b/javascript/0152-maximum-product-subarray.js new file mode 100644 index 000000000..b2ceeb9fa --- /dev/null +++ b/javascript/0152-maximum-product-subarray.js @@ -0,0 +1,59 @@ +/** + * Brute Force - Linear Search + * Time O(N^2) | Space O(1) + * https://leetcode.com/problems/maximum-product-subarray/ + * @param {number[]} nums + * @return {number} + */ + var maxProduct = (nums) => { + const isEmpty = nums.length === 0; + if (isEmpty) return 0; + + return linearSearch(nums);/* Time O(N * N) */ +} + +const linearSearch = (nums, max = nums[0]) => { + for (let index = 0; index < nums.length; index++) {/* Time O(N) */ + max = getMax(nums, index, max); /* Time O(N) */ + } + + return max; +} + +const getMax = (nums, index, max, product = 1) => { + for (let num = index; num < nums.length; num++) {/* Time O(N) */ + product *= nums[num]; + max = Math.max(max, product); + } + + return max; +} + +/** + * Greedy - product + * Time O(N) | Space O(1) + * https://leetcode.com/problems/maximum-product-subarray/ + * @param {number[]} nums + * @return {number} + */ +var maxProduct = (nums) => { + const isEmpty = nums.length === 0; + if (isEmpty) return 0; + + return greedySearch(nums);/* Time O(N) */ +}; + +const greedySearch = (nums) => { + let min = max = product = nums[0]; + + for (let num = 1; num < nums.length; num++) {/* Time O(N) */ + const [ minProduct, maxProduct ] = [ (min * nums[num]), (max * nums[num]) ]; + + min = Math.min(maxProduct, minProduct, nums[num]); + max = Math.max(maxProduct, minProduct, nums[num]); + + product = Math.max(product, max); + } + + return product; +} \ No newline at end of file diff --git a/javascript/0153-find-minimum-in-rotated-sorted-array.js b/javascript/0153-find-minimum-in-rotated-sorted-array.js new file mode 100644 index 000000000..4c1dcd96e --- /dev/null +++ b/javascript/0153-find-minimum-in-rotated-sorted-array.js @@ -0,0 +1,25 @@ +/** + * @param {number[]} nums + * Time O(log(N)) | Space O(1) + * @return {number} + */ +var findMin = function (nums) { + let [left, right] = [0, nums.length - 1]; + + while (left < right) { + const mid = (left + right) >> 1; + const guess = nums[mid]; + const [leftNum, rightNum] = [nums[left], nums[right]]; + + const isTarget = leftNum < rightNum; + if (isTarget) return leftNum; + + const isTargetGreater = leftNum <= guess; + if (isTargetGreater) left = mid + 1; + + const isTargetLess = guess < leftNum; + if (isTargetLess) right = mid; + } + + return nums[left]; +}; diff --git a/javascript/0155-min-stack.js b/javascript/0155-min-stack.js new file mode 100644 index 000000000..a84c66b5b --- /dev/null +++ b/javascript/0155-min-stack.js @@ -0,0 +1,102 @@ +/** + * https://leetcode.com/problems/min-stack + * Time O(1) | Space O(N) + * Your MinStack object will be instantiated and called as such: + * var obj = new MinStack() + * obj.push(x) + * obj.pop() + * var param_3 = obj.top() + * var param_4 = obj.getMin() + */ +class MinStack { + /** + * @constructor + */ + constructor () { + this.stack = []; + this.minStack = []; + } + + /** + * @param {number} val + * @return {void} + */ + push (val, { minStack } = this) { + this.stack.push(val); /* Space O(N) */ + + const isMinEmpty = !minStack.length; + const hasNewMin = val <= this.top(minStack); + const canAddMin = isMinEmpty || hasNewMin; + if (canAddMin) minStack.push(val);/* Space O(N) */ + } + + /** + * @return {void} + */ + pop ({ stack, minStack } = this) { + const top = stack.pop(); /* Time O(1) */ + + const canPopMin = top === this.getMin(); + if (canPopMin) minStack.pop(); /* Time O(1) */ + } + + /** + * @param {Array} + * @return {number} + */ + top (stack = this.stack) { + return stack.length + ? stack[stack.length - 1] /* Time O(1) */ + : null; + } + + /** + * @return {number} + */ + getMin (minStack = this.minStack) { + return this.top(minStack); /* Time O(1) */ + } +} + + +/** + * https://leetcode.com/problems/min-stack + * Time O(1) | Space O(1) + * Your MinStack object will be instantiated and called as such: + * var obj = new MinStack() + * obj.push(x) + * obj.pop() + * var param_3 = obj.top() + * var param_4 = obj.getMin() + */ +class MinStack { + constructor () { + this.head = null + } + + push (val) { + this.head = (!this.head) /* Space O(1) */ + ? new Node(val, val, null) + : new Node(val, Math.min(val, this.head.min), this.head); + } + + pop () { + this.head = this.head.next;/* Time O(1) */ + } + + top () { + return this.head.val; /* Time O(1) */ + } + + getMin () { + return this.head.min; /* Time O(1) */ + } +} + +class Node { + constructor (val, min, next) { + this.val = val; + this.min = min; + this.next = next; + } +} diff --git a/javascript/0160-intersection-of-two-linked-lists.js b/javascript/0160-intersection-of-two-linked-lists.js new file mode 100644 index 000000000..37b9b09ba --- /dev/null +++ b/javascript/0160-intersection-of-two-linked-lists.js @@ -0,0 +1,23 @@ +/** + * Definition for singly-linked list. + * function ListNode(val) { + * this.val = val; + * this.next = null; + * } + */ + +/** + * @param {ListNode} headA + * @param {ListNode} headB + * @return {ListNode} + */ +var getIntersectionNode = function (headA, headB) { + let a = headA; + let b = headB; + while (a !== b) { + a = a === null ? headB : a.next; + b = b === null ? headA : b.next; + } + + return a; +}; diff --git a/javascript/0162-find-peak-element.js b/javascript/0162-find-peak-element.js new file mode 100644 index 000000000..cabcc5fc5 --- /dev/null +++ b/javascript/0162-find-peak-element.js @@ -0,0 +1,22 @@ +/** + * Time O(log(N)) | Space O(1) + * @param {number[]} nums + * @return {number} + */ +var findPeakElement = function(nums) { + let [l, r] = [0, nums.length - 1]; + let mid = null; + while (l <= r){ + mid = (l + r) >> 1; + if (mid < nums.length - 1 && nums[mid] < nums[mid+1]){ + l = mid + 1; + } + else if (mid > 0 && nums[mid] < nums[mid-1]) { + r = mid - 1; + } + else { + break; + } + } + return mid; +}; diff --git a/javascript/0167-two-sum-ii-input-array-is-sorted.js b/javascript/0167-two-sum-ii-input-array-is-sorted.js new file mode 100644 index 000000000..69889ffe8 --- /dev/null +++ b/javascript/0167-two-sum-ii-input-array-is-sorted.js @@ -0,0 +1,25 @@ +/** + * https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/ + * Time O(N) | Space O(1) + * @param {number[]} numbers + * @param {number} target + * @return {number[]} + */ +var twoSum = function (numbers, target) { + let [left, right] = [0, numbers.length - 1]; + + while (left < right) { + const sum = numbers[left] + numbers[right]; + + const isTarget = sum === target; + if (isTarget) return [left + 1, right + 1]; + + const isTargetGreater = sum < target; + if (isTargetGreater) left++; + + const isTargetLess = target < sum; + if (isTargetLess) right--; + } + + return [-1, -1]; +}; diff --git a/javascript/0169-majority-element.js b/javascript/0169-majority-element.js new file mode 100644 index 000000000..97b4f366d --- /dev/null +++ b/javascript/0169-majority-element.js @@ -0,0 +1,47 @@ +/** + * Boyer Moore Algorithm + * Time O(N) | Space O(1) + * https://leetcode.com/problems/majority-element + * @param {number[]} nums + * @return {number} + */ + +var majorityElement = function (nums) { + let res = nums[0]; + let count = 1; + + for (let i = 1; i < nums.length - 1; i++) { + if (nums[i] === res) count++; + else if (!--count) { + res = nums[i + 1]; + count = 0; + } + } + + return res; +}; + +/** + * HashMap + * Time O(N) | Space O(N) + * https://leetcode.com/problems/majority-element + * @param {number[]} nums + * @return {number} + */ + +var majorityElement = function (nums) { + const occuranceOfElement = new Map(); + + for (let i = 0; i < nums.length; i++) { + if (occuranceOfElement.has(nums[i])) { + let occurance = occuranceOfElement.get(nums[i]); + occuranceOfElement.set(nums[i], occurance + 1); + } else { + occuranceOfElement.set(nums[i], 1); + } + } + + for (let [key, value] of occuranceOfElement) { + if (value > nums.length / 2) return key; + } +}; diff --git a/javascript/0173-binary-search-tree-iterator.js b/javascript/0173-binary-search-tree-iterator.js new file mode 100644 index 000000000..ae547001b --- /dev/null +++ b/javascript/0173-binary-search-tree-iterator.js @@ -0,0 +1,61 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ + +/** + * Controlled Recursion - Average Time (1) + * Time O(N) | Space O(N) + * https://leetcode.com/problems/binary-search-tree-iterator/ + * @param {TreeNode} root + */ +class BSTIterator { + constructor (root) { + this.stack = []; /* | Space O(N) */ + this.getLeft(root);/* Time O(N) | Space O(N)*/ + } + + /** + * Time O(N) | Space O(H) + * @return {number} + */ + getLeft (root, { stack } = this) { + while (root !== null) {/* Time O(N) */ + stack.push(root); /* Space O(N) */ + root = root.left; + } + } + + /** + * Time O(N) | Space O(N) + * @return the next smallest number + * @return {number} + */ + next ({ stack } = this) { + const node = stack.pop(); + + if (node.right) this.getLeft(node.right);/* Time O(N) | Space O(N) */ + + return node.val; + }; + + /** + * Time O(1) | Space O(1) + * @return whether we have a next smallest number + * @return {boolean} + */ + hasNext ({ stack } = this) { + return (stack.length !== 0); + } +} + +/** + * Your BSTIterator object will be instantiated and called as such: + * var obj = new BSTIterator(root) + * var param_1 = obj.next() + * var param_2 = obj.hasNext() + */ diff --git a/javascript/0179-largest-number.js b/javascript/0179-largest-number.js new file mode 100644 index 000000000..96eaa3da9 --- /dev/null +++ b/javascript/0179-largest-number.js @@ -0,0 +1,11 @@ +/** + * @param {number[]} nums + * @return {string} + */ +var largestNumber = function (nums) { + let largest = nums + .map((n) => n.toString()) + .sort((x, y) => y + x - (x + y)) + .join(''); + return largest[0] === '0' ? '0' : largest; +}; diff --git a/javascript/0187-repeated-dna-sequences.js b/javascript/0187-repeated-dna-sequences.js new file mode 100644 index 000000000..66a0a4b01 --- /dev/null +++ b/javascript/0187-repeated-dna-sequences.js @@ -0,0 +1,55 @@ +/** + * https://leetcode.com/problems/repeated-dna-sequences/ + * Hashing + * s = the number of letters in the sequence. In our case, it's 10. so the time complexity would be 10*n which boils down to n. + * Time O(n) | Space O(n) + * @param {string} s + * @return {string[]} + */ +var findRepeatedDnaSequences = function (s) { + const sequenceSet = new Set(); + let resultSet = new Set(); + + for (let i = 0; i < s.length; i++) { + const subSequence = getSubSequence(s, i, 10); + if (sequenceSet.has(subSequence)) { + resultSet.add(subSequence); + } else { + sequenceSet.add(subSequence); + } + } + + resultSet = [...resultSet]; + return resultSet; +}; + +function getSubSequence(s, i, len) { + return s.slice(i, i + len); +} + +// an alternative code with the same approach. +/** + * https://leetcode.com/problems/repeated-dna-sequences/ + * Hashing + * s = the number of letters in the sequence. In our case, it's 10. so the time complexity would be 10*n which boils down to n. + * Time O(n) | Space O(n) + * @param {string} s + * @return {string[]} + */ +var findRepeatedDnaSequences1 = function (s) { + const seen = new Set(); + const res = new Set(); + const arr = Array.from(s); + + for (let l = 0; l < arr.length - 9; l++) { + const sequence = s.slice(l, l + 10); + + if (seen.has(sequence)) { + res.add(sequence); + } else { + seen.add(sequence); + } + } + + return Array.from(res); +}; diff --git a/javascript/0189-rotate-array.js b/javascript/0189-rotate-array.js new file mode 100644 index 000000000..b8ff1562c --- /dev/null +++ b/javascript/0189-rotate-array.js @@ -0,0 +1,64 @@ +/** + * Two Pointers + * https://leetcode.com/problems/rotate-array/ + * + * Time O(n) | Space O(1) + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ + var rotate = function(nums, k) { + + // if the k exceeds the length of nums. + k = k % nums.length; + + nums.reverse(); + reversePortionOfArray(nums, 0, k - 1); + reversePortionOfArray(nums,k, nums.length - 1); +}; + +var reversePortionOfArray = function(nums,start,end) { + while(start < end) { + [nums[start],nums[end]] = [nums[end],nums[start]]; + start++; + end--; + } +} + + +/** + * Two Pointers + * https://leetcode.com/problems/rotate-array/ + * + * Time O(n) | Space O(1) + * @param {number[]} nums + * @param {number} k + * @return {void} Do not return anything, modify nums in-place instead. + */ +var rotate = function(nums, k) { + + k = k % nums.length; + + let l = 0; + let r = nums.length - 1; + + while (l < r) { + [nums[l], nums[r]] = [nums[r], nums[l]]; + l += 1; + r -= 1; + } + l = 0, + r = k - 1; + while (l < r) { + [nums[l], nums[r]] = [nums[r], nums[l]]; + l += 1; + r -= 1; + } + l = k; + r = nums.length - 1; + while (l < r) { + [nums[l], nums[r]] = [nums[r], nums[l]]; + l += 1; + r -= 1; + } +}; diff --git a/javascript/0190-reverse-bits.js b/javascript/0190-reverse-bits.js new file mode 100644 index 000000000..fc286c4a7 --- /dev/null +++ b/javascript/0190-reverse-bits.js @@ -0,0 +1,15 @@ +/** + * https://leetcode.com/problems/reverse-bits/ + * Time O(1) | Space O(1) + * @param {number} n - a positive integer + * @return {number} - a positive integer + */ +var reverseBits = function (n, bit = 0) { + for (let i = 0; i < 32; i++) { + bit <<= 1; // Double * 2 + bit |= (n & 1); // Flip + n >>= 1; // Reduce * 0.5 + } + + return bit >>> 0; +}; diff --git a/javascript/0191-number-of-1-bits.js b/javascript/0191-number-of-1-bits.js new file mode 100644 index 000000000..d98d2e8eb --- /dev/null +++ b/javascript/0191-number-of-1-bits.js @@ -0,0 +1,33 @@ +/** + * https://leetcode.com/problems/number-of-1-bits/ + * Time O(1) | Space (1) + * @param {number} n - a positive integer + * @return {number} + */ + var hammingWeight = function(n) { + let [ bits, mask ] = [ 0, 1 ] + + for (let i = 0; i < 32; i++) { + const hasBit = ((n & mask) !== 0) + if (hasBit) bits++ + + mask <<= 1 + } + + return bits +}; + +/** + * https://leetcode.com/problems/number-of-1-bits/ + * Time O(1) | Space (1) + * @param {number} n - a positive integer + * @return {number} + */ + var hammingWeight = function(n, sum = 0) { + while (n !== 0) { + n &= (n - 1) + sum++ + } + + return sum +} diff --git a/javascript/0198-house-robber.js b/javascript/0198-house-robber.js new file mode 100644 index 000000000..34ef665c5 --- /dev/null +++ b/javascript/0198-house-robber.js @@ -0,0 +1,103 @@ +/** + * Brute Force - DFS + * Time O(2^N) | Space O(N) + * https://leetcode.com/problems/house-robber/ + * @param {number[]} nums + * @return {number} + */ +var rob = (nums, i = 0) => { + const isBaseCase = nums <= i; + if (isBaseCase) return 0; + + const [ next, nextNext ] = [ (i + 1), (i + 2) ]; + const right = nums[i]; + const mid = rob(nums, next); /* Time O(2^N) | Space O(N) */ + const left = rob(nums, nextNext);/* Time O(2^N) | Space O(N) */ + const house = left + right; + + return Math.max(house, mid); +}; + +/** + * DP - Top Down + * Array - Memoization + * Time O(N) | Space O(N) + * https://leetcode.com/problems/house-robber/ + * @param {number[]} nums + * @return {number} + */ +var rob = (nums, i = 0, memo = initMemo(nums)) => { + const isBaseCase = nums.length <= i; + if (isBaseCase) return 0; + + const hasSeen = 0 <= memo[i]; + if (hasSeen) return memo[i]; + + const [ next, nextNext ] = [ (i + 1), (i + 2) ]; + const right = nums[i]; + const mid = rob(nums, next, memo); /* Time O(N) | Space O(N) */ + const left = rob(nums, nextNext, memo);/* Time O(N) | Space O(N) */ + const house = left + right; + + memo[i] = Math.max(mid, house); /* | Space O(N) */ + + return memo[i]; +}; + +const initMemo = (nums) => Array(nums.length + 1).fill(-1); + +/** + * DP - Bottom Up + * Array - Tabulation + * Time O(N) | Space O(N) + * https://leetcode.com/problems/house-robber/ + * @param {number[]} nums + * @return {number} + */ +var rob = (nums) => { + if (!nums.length) return 0; + + const tabu = initTabu(nums); + + for (let i = 1; i < nums.length; i++) {/* Time O(N) */ + const right = nums[i]; + const mid = tabu[i]; + const left = tabu[i - 1]; + const house = left + right; + + tabu[i + 1] = Math.max(mid, house); /* Space O(N) */ + } + + return tabu[nums.length] +}; + +const initTabu = (nums) => { + const tabu = Array(nums.length + 1).fill(0); + + tabu[1] = nums[0]; + + return tabu; +} + +/** + * DP - Bottom Up + * Time O(N) | Space O(1) + * https://leetcode.com/problems/house-robber/ + * @param {number[]} nums + * @return {number} + */ +var rob = (nums) => { + if (!nums.length) return 0; + + let [ left, mid ] = [ 0, 0 ]; + + for (const right of nums) {/* Time O(N) */ + const temp = mid; + const house = left + right; + + mid = Math.max(mid, house); + left = temp; + } + + return mid; +}; \ No newline at end of file diff --git a/javascript/0199-binary-tree-right-side-view.js b/javascript/0199-binary-tree-right-side-view.js new file mode 100644 index 000000000..35fb95b62 --- /dev/null +++ b/javascript/0199-binary-tree-right-side-view.js @@ -0,0 +1,55 @@ +/** + * https://leetcode.com/problems/binary-tree-right-side-view/ + * Time O(N) | Space O(W) + * @param {TreeNode} root + * @return {number[]} + */ + var rightSideView = function(root) { + const isBaseCase = root === null; + if (isBaseCase) return []; + + return bfs([ root ]); +}; + +const bfs = (queue, rightSide = []) => { + while (queue.length) { + let prev = null; + + for (let i = (queue.length - 1); 0 <= i; i--) { + const node = queue.shift(); + + prev = node; + + if (node.left) queue.push(node.left); + if (node.right) queue.push(node.right); + } + + rightSide.push(prev.val); + } + + return rightSide; +} + +/** + * https://leetcode.com/problems/binary-tree-right-side-view/ + * Time O(N) | Space O(H) + * @param {TreeNode} root + * @return {number[]} + */ + var rightSideView = function(root, level = 0, rightSide = []) { + const isBaseCase = root === null; + if (isBaseCase) return rightSide; + + const isLastNode = level === rightSide.length + if (isLastNode) rightSide.push(root.val); + + return dfs(root, level, rightSide) +} + +const dfs = (root, level, rightSide) => { + if (root.right) rightSideView(root.right, (level + 1), rightSide); + if (root.left) rightSideView(root.left, (level + 1), rightSide); + + return rightSide +} + diff --git a/javascript/0200-number-of-islands.js b/javascript/0200-number-of-islands.js new file mode 100644 index 000000000..e0ba467a4 --- /dev/null +++ b/javascript/0200-number-of-islands.js @@ -0,0 +1,179 @@ +/** + * https://leetcode.com/problems/number-of-islands/ + * Time O(ROWS * COLS) | Space O(ROWS * COLS) + * @param {character[][]} grid + * @return {number} + */ +var numIslands = function(grid, connectedComponents = 0) { + const [ rows, cols ] = [ grid.length, grid[0].length ] + + for (let row = 0; row < rows; row++) {/* Time O(ROWS) */ + for (let col = 0; col < cols; col++) {/* Time O(COLS) */ + const isIsland = grid[row][col] === '1' + if (isIsland) connectedComponents++ + + dfs(grid, row, rows, col, cols); /* Space O(ROWS * COLS) */ + } + } + + return connectedComponents +}; + +const dfs = (grid, row, rows, col, cols) => { + const isBaseCase = grid[row][col] === '0'; + if (isBaseCase) return; + + grid[row][col] = '0'; + + for (const [ _row, _col ] of getNeighbors(row, rows, col, cols)) { + dfs(grid, _row, rows, _col, cols); /* Space O(ROWS * COLS) */ + } +} + +var getNeighbors = (row, rows, col, cols) => [ [ 0, 1 ], [ 0, -1 ], [ 1, 0 ], [ -1, 0 ] ] + .map(([ _row, _col ]) => [ (row + _row), (col + _col) ]) + .filter(([ _row, _col ]) => (0 <= _row) && (_row < rows) && (0 <= _col) && (_col < cols)) + +/** + * https://leetcode.com/problems/number-of-islands/ + * Time O(ROWS * COLS) | Space O(MIN(ROWS,COLS)) + * @param {character[][]} grid + * @return {number} + */ + var numIslands = function(grid, connectedComponents = 0) { + const [ rows, cols ] = [ grid.length, grid[0].length ] + + for (let row = 0; row < rows; row++) {/* Time O(ROWS) */ + for (let col = 0; col < cols; col++) {/* Time O(COLS) */ + const isIsland = grid[row][col] === '1'; + if (isIsland) connectedComponents++; + + bfs(grid, rows, cols, new Queue([ [ row, col ] ]));/* Space O(MIN(ROWS,COLS)) */ + } + } + + return connectedComponents + } + + const bfs = (grid, rows, cols, queue) => { + while (!queue.isEmpty()) { + for (let i = (queue.size() - 1); 0 <= i; i--) {/* Time O(WIDTH) */ + const [ row, col ] = queue.dequeue(); + + const isWater = grid[row][col] === '0'; + if (isWater) continue; + + grid[row][col] = '0'; + + for (const [ _row, _col ] of getNeighbors(row, rows, col, cols)) { + queue.enqueue([ _row, _col ]); /* Space O(MIN(ROWS,COLS)) */ + } + } + } + } + +var getNeighbors = (row, rows, col, cols) => [ [ 0, 1 ], [ 0, -1 ], [ 1, 0 ], [ -1, 0 ] ] + .map(([ _row, _col]) => [ (row + _row), (col + _col) ]) + .filter(([ _row, _col ]) => (0 <= _row) && (_row < rows) && (0 <= _col) && (_col < cols)); + +/** + * https://leetcode.com/problems/number-of-islands/ + * Time O(ROWS * COLS) | Space O(ROWS * COLS) + * @param {character[][]} grid + * @return {number} + */ +var numIslands = function (grid) { + const unionFind = new UnionFind(grid);/* Time O(ROWS * COLS) | Space O(ROWS * COLS) */ + + searchGrid(grid, unionFind); /* Time O(ROWS * COLS) */ + + return unionFind.connectedComponents; +} + +var searchGrid = (grid, unionFind) => { + const [ rows, cols ] = [ grid.length, grid[0].length ]; + + for (let row = 0; row < rows; row++) {/* Time O(ROWS) */ + for (let col = 0; col < cols; col++) {/* Time O(COLS) */ + const isWater = grid[row][col] === '0'; + if (isWater) continue; + + grid[row][col] = '0'; + + searchRows(unionFind, grid, row, rows, col, cols); + searchCols(unionFind, grid, row, rows, col, cols); + } + } +} + +const searchRows = (unionFind, grid, row, rows, col, cols) => [ 1, -1 ] + .map((_row) => row + _row) + .filter((_row) => isInBound(_row, rows) && isIsland(grid[_row][col])) + .map((_row) => [ index(row, cols, col), index(_row, cols, col) ]) + .forEach(([ x, y ]) => unionFind.union(x, y)); + +const isInBound = (val, vals) => (0 <= val) && (val < vals) +const isIsland = (cell) => cell === '1' +const index = (row, cols, col) => ((row * cols) + col) + +const searchCols = (unionFind, grid, row, rows, col, cols) => [ 1, -1 ] + .map((_col) => col + _col) + .filter((_col) => isInBound(_col, cols) && isIsland(grid[row][_col])) + .map((_col) => [ index(row, cols, col), index(row, cols, _col) ]) + .forEach(([ x, y ]) => unionFind.union(x, y)); + +class UnionFind { + constructor (grid) { + const [ rows, cols ] = [ grid.length, grid[0].length ]; + + this.connectedComponents = 0; + this.grid = grid; + this.rows = rows; + this.cols = cols; + this.parent = new Array(rows * cols).fill(0); + this.rank = new Array(rows * cols).fill(0); + + this.findIslands(); + } + + findIslands ({ grid, rows, cols, parent } = this) { + for (let row = 0; row < rows; row++) {/* Time O(ROWS) */ + for (let col = 0; col < cols; col++) {/* Time O(COLS) */ + const isWater = grid[row][col] === '0'; + if (isWater) continue; + + const index = (row * cols) + col; + + parent[index] = index;/* Space O(ROWS * COLS) */ + this.connectedComponents++; + } + } + } + + find (index, { parent } = this) { + const isEqual = () => parent[index] === index; + while (!isEqual()) { + index = parent[index]; + } + + return parent[index]; + } + + union (x, y, { parent, rank } = this) { + const [ rootX, rootY ] = [ this.find(x), this.find(y) ]; + + const hasCycle = rootX === rootY; + if (hasCycle) return; + + this.connectedComponents--; + + const isXGreater = rank[rootY] < rank[rootX]; + if (isXGreater) return parent[rootY] = rootX; + + const isYGreater = rank[rootX] < rank[rootY]; + if (isYGreater) return parent[rootX] = rootY; + + parent[rootY] = rootX; /* Space O(ROWS * COLS) */ + rank[rootX]++; /* Space O(ROWS * COLS) */ + } +} diff --git a/javascript/0202-happy-number.js b/javascript/0202-happy-number.js new file mode 100644 index 000000000..c2a2efece --- /dev/null +++ b/javascript/0202-happy-number.js @@ -0,0 +1,114 @@ +/** + * Hash Set - seen dynamic + * Time O(log(N)) | Space O(log(N)) + * https://leetcode.com/problems/happy-number/ + * @param {number} n + * @return {boolean} + */ + var isHappy = (n, seen = new Set()) => { + const hasCycle = () => ((n === 1) || (seen.has(n))); + while (!hasCycle()) {/* Time O(log(N)) */ + seen.add(n); /* Space O(log(N)) */ + n = getNext(n); /* Time O(log(N)) */ + } + + return (n === 1); +}; + +var getNext = (n, sum = 0) => { + while (0 < n) {/* Time O(log(N)) */ + const remainder = (n % 10); + + n = Math.floor((n / 10)); + sum += (remainder * remainder); + } + + return sum; +} + +/** + * Hash Set - seen static + * Time O(log(N)) | Space O(1) + * https://leetcode.com/problems/happy-number/ + * @param {number} n + * @return {boolean} + */ +var isHappy = (n) => { + const cycles = [ 4, 16, 37, 58, 89, 145, 42, 20 ]; + const seen = new Set(cycles);/* Time O(1) | Space O(1) */ + + const hasCycle = () => ((n === 1) || (seen.has(n))); + while (!hasCycle()) { /* Time O(log(N)) | Space O(1) */ + n = getNext(n); + } + + return n === 1; +} + +var getNext = (n, sum = 0) => { + while (0 < n) {/* Time O(log(N)) */ + const remainder = (n % 10); + + n = Math.floor((n / 10)); + sum += (remainder * remainder); + } + + return sum; +} + +/** + * Pointer - n === 1 || n === 4 + * Time O(log(N)) | Space O(1) + * https://leetcode.com/problems/happy-number/ + * @param {number} n + * @return {boolean} + */ +var isHappy = (n) => { + const hasCycle = () => ((n === 1) || (n === 4)); + while (!hasCycle()) {/* Time O(log(N)) */ + n = getNext(n); /* Time O(log(N)) */ + } + + return n === 1; +} + +var getNext = (n, sum = 0) => { + while (0 < n) {/* Time O(log(N)) */ + const remainder = (n % 10); + + n = Math.floor((n / 10)); + sum += (remainder * remainder); + } + + return sum; +} + +/** + * Slow Fast + * Time O(log(N)) | Space O(1) + * https://leetcode.com/problems/happy-number/ + * @param {number} n + * @return {boolean} + */ +var isHappy = (n) => { + let [ slow, fast ] = [ n, getNext(n) ]; + + const hasCyle = () => ((fast === 1) || (slow === fast)); + while (!hasCyle()) { /* Time O(log(N)) */ + slow = getNext(slow); /* Time O(log(N)) */ + fast = getNext(getNext(fast));/* Time O(log(N)) */ + } + + return (fast === 1); +} + +var getNext = (n, sum = 0) => { + while (0 < n) {/* Time O(log(N)) */ + const remainder = (n % 10); + + n = Math.floor((n / 10)); + sum += (remainder * remainder); + } + + return sum; +} \ No newline at end of file diff --git a/javascript/0203-remove-linked-list-elements.js b/javascript/0203-remove-linked-list-elements.js new file mode 100644 index 000000000..298626e72 --- /dev/null +++ b/javascript/0203-remove-linked-list-elements.js @@ -0,0 +1,32 @@ +/** + * Definition for singly-linked list. + * function ListNode(val, next) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + */ +/** + * @param {ListNode} head + * @param {number} val + * @return {ListNode} + */ +var removeElements = function (head, val) { + + let sentinel_node = new ListNode(0, head); + let slow_pointer = sentinel_node; + let fast_pointer = null; + + while (slow_pointer) { + // get next legible node + fast_pointer = slow_pointer.next; + while (fast_pointer && fast_pointer.val === val) { + fast_pointer = fast_pointer.next; + } + + // Set next node to the legible node + slow_pointer.next = fast_pointer; + slow_pointer = slow_pointer.next; + } + + return sentinel_node.next; +}; diff --git a/javascript/0205-isomorphic-strings.js b/javascript/0205-isomorphic-strings.js new file mode 100644 index 000000000..84a459a71 --- /dev/null +++ b/javascript/0205-isomorphic-strings.js @@ -0,0 +1,18 @@ +var isIsomorphic = function (s, t) { + if (s.length !== t.length) return false; + + const mapOne = new Map(); + const mapTwo = new Map(); + + for (let i = 0; i < s.length; i++) { + if (mapOne.has(s[i])) { + if (mapOne.get(s[i]) !== t[i]) return false; + } else mapOne.set(s[i], t[i]); + + if (mapTwo.has(t[i])) { + if (mapTwo.get(t[i]) !== s[i]) return false; + } else mapTwo.set(t[i], s[i]); + } + + return true; +}; diff --git a/javascript/0206-reverse-linked-list.js b/javascript/0206-reverse-linked-list.js new file mode 100644 index 000000000..5ad06b365 --- /dev/null +++ b/javascript/0206-reverse-linked-list.js @@ -0,0 +1,41 @@ +/** + * https://leetcode.com/problems/reverse-linked-list/ + * Time O(N) | Space O(N) + * @param {ListNode} head + * @return {ListNode} + */ +var reverseList = function (head) { + const isBaseCase = !head?.next; + if (isBaseCase) return head; + + return dfs(head); /* Time O(N) | Space O(N) */ +} + +const dfs = (curr) => { + const prev = reverseList(curr.next);/* Time O(N) | Space O(N) */ + + curr.next.next = curr; + curr.next = null; + + return prev; +} + +/** + * https://leetcode.com/problems/reverse-linked-list/ + * Time O(N) | Space O(1) + * @param {ListNode} head + * @return {ListNode} + */ + var reverseList = function (head) { + let [ prev, curr, next ] = [ null, head, null ]; + + while (curr) {/* Time O(N) */ + next = curr.next; + curr.next = prev; + + prev = curr; + curr = next; + } + + return prev; +}; \ No newline at end of file diff --git a/javascript/0207-course-schedule.js b/javascript/0207-course-schedule.js new file mode 100644 index 000000000..7f604d3a8 --- /dev/null +++ b/javascript/0207-course-schedule.js @@ -0,0 +1,204 @@ +/** + * https://leetcode.com/problems/course-schedule/ + * Time O((V)^2 + E) | Space O(V + E) + * @param {number} numCourses + * @param {number[][]} prerequisites + * @return {boolean} + */ +var canFinish = function(numCourses, prerequisites) { + const { graph, path } = buildGraph(numCourses, prerequisites); + + return hasPath(numCourses, graph, path); +} + +var initGraph = (numCourses) => ({ + graph: new Array(numCourses).fill().map(() => []), + path: new Array(numCourses).fill(false) +}) + +var buildGraph = (numCourses, prerequisites) => { + const { graph, path } = initGraph(numCourses); + + for (const [ src, dst ] of prerequisites) { + const neighbors = (graph[dst] || []); + + neighbors.push(src); + + graph[dst] = neighbors; + } + + return { graph, path }; +} + +var hasPath = (numCourses, graph, path) => { + for (let course = 0; course < numCourses; course++) { + if (isCyclic(course, graph, path)) return false; + } + + return true; +} + +var isCyclic = (currCourse, graph, path) => { + const hasSeen = path[currCourse] + if (hasSeen) return true + + const isMissingNext = !(currCourse in graph) + if (isMissingNext) return false; + + return backTrack(currCourse, graph, path); +} + +var backTrack = (currCourse, graph, path) => { + path[currCourse] = true; + const _hasCycle = hasCycle(currCourse, graph, path) + path[currCourse] = false; + + return _hasCycle +} + +var hasCycle = (currCourse, graph, path) => { + for (const neighbor of graph[currCourse]) { + if (isCyclic(neighbor, graph, path)) return true; + } + + return false +} + +/** + * https://leetcode.com/problems/course-schedule/ + * Time O(V + E) | Space O(V + E) + * @param {number} numCourses + * @param {number[][]} prerequisites + * @return {boolean} + */ +var canFinish = function(numCourses, prerequisites) { + const { graph, visited, path } = buildGraph(numCourses, prerequisites); + + for (let currCourse = 0; currCourse < numCourses; currCourse++) { + if (isCyclic(currCourse, graph, visited, path)) return false; + } + + return true; +} + +var initGraph = (numCourses) => ({ + graph: new Array(numCourses).fill().map(() => []), + visited: new Array(numCourses).fill(false), + path: new Array(numCourses).fill(false) +}) + +var buildGraph = (numCourses, prerequisites) => { + const { graph, visited, path } = initGraph(numCourses); + + for (const [ src, dst ] of prerequisites) { + const neighbors = (graph[dst] || []); + + neighbors.push(src); + + graph[dst] = neighbors; + } + + return { graph, visited, path }; +} + +var isCyclic = (currCourse, graph, visited, path) => { + const isVisited = visited[currCourse] + if (isVisited) return false; + + const hasSeen = path[currCourse] + if (hasSeen) return true; + + const isMissingNext = !(currCourse in graph) + if (isMissingNext) return false; + + const _isCyclic = backTrack(currCourse, graph, visited, path); + + visited[currCourse] = true; + + return _isCyclic +} + +var backTrack = (currCourse, graph, visited, path) => { + path[currCourse] = true; + const _hasCycle = hasCycle(currCourse, graph, visited, path) + path[currCourse] = false; + + return _hasCycle +} + +var hasCycle = (currCourse, graph, visited, path) => { + for (const neighbor of graph[currCourse]) { + if (isCyclic(neighbor, graph, visited, path)) return true; + } + + return false +} + +/** + * https://leetcode.com/problems/course-schedule/ + * Time O(V + E) | Space O(V + E) + * @param {number} numCourses + * @param {number[][]} prerequisites + * @return {boolean} + */ +var canFinish = function(numCourses, prerequisites) { + const { graph, indegree } = buildGraph(numCourses, prerequisites); + const topologicalOrder = topologicalSort(graph, indegree); + const isDirectedAcyclicGraph = topologicalOrder.length === numCourses; + + return isDirectedAcyclicGraph; +}; + +var initGraph = (numCourses) => ({ + graph: new Array(numCourses).fill().map(() => []), + indegree: new Array(numCourses).fill(0) +}) + +var buildGraph = (numCourses, prerequisites) => { + const { graph, indegree } = initGraph(numCourses); + + for (const [ src, dst ] of prerequisites){ + graph[src].push(dst); + indegree[dst]++; + } + + return { graph, indegree }; +} + +var topologicalSort = (graph, indegree, order = []) => { + const queue = searchGraph(graph, indegree); + + bfs(graph, indegree, queue, order); + + return order; +} + +var searchGraph = (graph, indegree, queue = new Queue([])) => { + for (const node in graph) { + const isSource = indegree[node] === 0; + if (isSource) queue.enqueue(node); + } + + return queue; +} + +var bfs = (graph, indegree, queue, order) => { + while (!queue.isEmpty()) { + for (let i = (queue.size() - 1); 0 <= i; i--) { + checkNeighbors(graph, indegree, queue, order); + } + } +} + +var checkNeighbors = (graph, indegree, queue, order) => { + const node = queue.dequeue(); + + order.push(node); + + for (const neighbor of graph[node]) { + indegree[neighbor]--; + + const isSource = indegree[neighbor] === 0; + if (isSource) queue.enqueue(neighbor); + } +} diff --git a/javascript/0208-implement-trie-prefix-tree.js b/javascript/0208-implement-trie-prefix-tree.js new file mode 100644 index 000000000..7a96fa6e1 --- /dev/null +++ b/javascript/0208-implement-trie-prefix-tree.js @@ -0,0 +1,59 @@ +/** + * Your Trie object will be instantiated and called as such: + * var obj = new Trie() + * obj.insert(word) + * var param_2 = obj.search(word) + * var param_3 = obj.startsWith(prefix) + */ + +class TrieNode { + constructor() { + this.children = {}; + this.isWord = false; + } +} + +class Trie { + constructor() { + this.root = new TrieNode(); + } + + /* Time O(N) | Space O(N) */ + insert(word, node = this.root) { + for (const char of word) { + const child = node.children[char] || new TrieNode(); + + node.children[char] = child; + + node = child; + } + + node.isWord = true; + } + + /* Time O(N) | Space O(1) */ + search(word, node = this.root) { + for (const char of word) { + const child = node.children[char] || null; + + if (!child) return false; + + node = child; + } + + return node.isWord; + } + + /* Time O(N) | Space O(1) */ + startsWith(prefix, node = this.root) { + for (const char of prefix) { + const child = node.children[char] || null; + + if (!child) return false; + + node = child; + } + + return true; + } +} diff --git a/javascript/0209-minimum-size-subarray-sum.js b/javascript/0209-minimum-size-subarray-sum.js new file mode 100644 index 000000000..600dbb4e3 --- /dev/null +++ b/javascript/0209-minimum-size-subarray-sum.js @@ -0,0 +1,20 @@ +/** + * @param {number} target + * @param {number[]} nums + * @return {number} + */ +var minSubArrayLen = function (target, nums) { + let minLength = Infinity; + let leftWindow = 0; + let currentSum = 0; + + for (let rightWindow = 0; rightWindow < nums.length; rightWindow++) { + currentSum += nums[rightWindow]; + while (currentSum >= target) { + minLength = Math.min(minLength, rightWindow - leftWindow + 1); + currentSum -= nums[leftWindow]; + leftWindow++; + } + } + return minLength === Infinity ? 0 : minLength; +}; diff --git a/javascript/0210-course-schedule-ii.js b/javascript/0210-course-schedule-ii.js new file mode 100644 index 000000000..33ccf0b1f --- /dev/null +++ b/javascript/0210-course-schedule-ii.js @@ -0,0 +1,139 @@ +/** + * https://leetcode.com/problems/course-schedule-ii/ + * Time O(V + E) | Space O(V + E) + * @param {number} numCourses + * @param {number[][]} prerequisites + * @return {number[]} + */ +var findOrder = function(numCourses, prerequisites) { + const { graph, color, isDirectedAcyclicGraph, topologicalOrder } = buildGraph(numCourses, prerequisites); + + search(numCourses, graph, color, topologicalOrder, isDirectedAcyclicGraph) + + return isDirectedAcyclicGraph[0] + ? topologicalOrder.reverse() + : [] +} + +var initGraph = (numCourses) => ({ + graph: new Array(numCourses).fill().map(() => []), + color: new Array(numCourses).fill(1), // White + isDirectedAcyclicGraph: [ true ], + topologicalOrder: [] +}) + +var buildGraph = (numCourses, prerequisites) => { + const { graph, color, isDirectedAcyclicGraph, topologicalOrder } = initGraph(numCourses); + + for (const [ src, dst ] of prerequisites) { + const neighbors = (graph[dst] || []); + + neighbors.push(src); + graph[dst] = neighbors; + } + + return { graph, color, isDirectedAcyclicGraph, topologicalOrder } +} + +var search = (numCourses, graph, color, topologicalOrder, isDirectedAcyclicGraph) => { + for (let i = 0; i < numCourses; i++) { + const isNew = color[i] === 1 // White + if (isNew) dfs(i, graph, color, topologicalOrder, isDirectedAcyclicGraph); + } +} + +var dfs = (node, graph, color, topologicalOrder, isDirectedAcyclicGraph) => { + const hasCycle = !isDirectedAcyclicGraph[0] + if (hasCycle) return; + + colorBackTrack(node, graph, color, topologicalOrder, isDirectedAcyclicGraph) + + topologicalOrder.push(node); +} + +const colorBackTrack = (node, graph, color, topologicalOrder, isDirectedAcyclicGraph) => { + color[node] = 2; // Grey + checkNeighbors(node, graph, color, topologicalOrder, isDirectedAcyclicGraph) + color[node] = 3; // Black +} + +var checkNeighbors = (node, graph, color, topologicalOrder, isDirectedAcyclicGraph) => { + for (const neighbor of graph[node]) { + const isNew = color[neighbor] === 1 // White + if (isNew) dfs(neighbor, graph, color, topologicalOrder, isDirectedAcyclicGraph); + + const isCycle = color[neighbor] === 2 // Grey + if (isCycle) isDirectedAcyclicGraph[0] = false; + } +} + +/** + * https://leetcode.com/problems/course-schedule-ii/ + * Time O(V + E) | Space O(V + E) + * @param {number} numCourses + * @param {number[][]} prerequisites + * @return {number[]} + */ +var findOrder = function(numCourses, prerequisites) { + const { graph, indegree } = buildGraph(numCourses, prerequisites); + const reversedTopologicalOrder = topologicalSort(graph, indegree); + const isDirectedAcyclicGraph = reversedTopologicalOrder.length === numCourses; + + return isDirectedAcyclicGraph + ? reversedTopologicalOrder + : []; +}; + +var initGraph = (numCourses) => ({ + graph: new Array(numCourses).fill().map(() => []), + indegree: new Array(numCourses).fill(0) +}) + +var buildGraph = (numCourses, prerequisites) => { + const { graph, indegree } = initGraph(numCourses); + + for (const [ src, dst ] of prerequisites){ + graph[src].push(dst); + indegree[dst]++; + } + + return { graph, indegree }; +} + +var topologicalSort = (graph, indegree) => { + const queue = searchGraph(graph, indegree); + + return bfs(graph, indegree, queue); +} + +var isSource = (count) => count === 0; + +var searchGraph = (graph, indegree, queue = new Queue([])) => { + for (const node in graph) { + if (isSource(indegree[node])) queue.enqueue(node); + } + + return queue; +} + +var bfs = (graph, indegree, queue, reversedOrder = []) => { + while (!queue.isEmpty()) { + for (let i = (queue.size() - 1); 0 <= i; i--) { + checkNeighbors(graph, indegree, queue, reversedOrder); + } + } + + return reversedOrder.reverse(); +} + +var checkNeighbors = (graph, indegree, queue, reversedOrder) => { + const node = queue.dequeue(); + + reversedOrder.push(node); + + for (const neighbor of graph[node]) { + indegree[neighbor]--; + + if (isSource(indegree[neighbor])) queue.enqueue(neighbor); + } +} \ No newline at end of file diff --git a/javascript/0211-design-add-and-search-words-data-structure.js b/javascript/0211-design-add-and-search-words-data-structure.js new file mode 100644 index 000000000..bd56b167c --- /dev/null +++ b/javascript/0211-design-add-and-search-words-data-structure.js @@ -0,0 +1,60 @@ +/** + * Your WordDictionary object will be instantiated and called as such: + * var obj = new WordDictionary() + * obj.addWord(word) + * var param_2 = obj.search(word) + */ + +class TrieNode { + constructor() { + this.children = {}; + this.isWord = false; + } +} + +class WordDictionary { + constructor() { + this.root = new TrieNode(); + } + + /* Time O(N) | Space O(N) */ + addWord(word, node = this.root) { + for (const char of word) { + const child = node.children[char] || new TrieNode(); + + node.children[char] = child; + + node = child; + } + + node.isWord = true; + } + + /* Time O(N) | Space O(N) */ + search(word) { + return this.dfs(word, this.root, 0); + } + + dfs(word, node, level) { + if (!node) return false; + + const isWord = level === word.length; + if (isWord) return node.isWord; + + const isWildCard = word[level] === '.'; + if (isWildCard) return this.hasWildCard(word, node, level); + + return this.dfs(word, node.children[word[level]], level + 1); + } + + hasWildCard(word, node, level) { + for (const char of Object.keys(node.children)) { + const child = node.children[char]; + + const hasWord = this.dfs(word, child, level + 1); + if (hasWord) return true; + } + + return false; + } +} diff --git a/javascript/0212-word-search-ii.js b/javascript/0212-word-search-ii.js new file mode 100644 index 000000000..1f15a55f8 --- /dev/null +++ b/javascript/0212-word-search-ii.js @@ -0,0 +1,102 @@ +/** + * @param {character[][]} board + * @param {string[]} words + * Time O((ROWS * COLS) * (4 * (3 ^ (WORDS - 1)))) | Space O(N) + * @return {string[]} + */ +var findWords = function (board, words) { + return new Trie(words).searchBoard(board); +}; + +class TrieNode { + constructor() { + this.children = {}; + this.word = ''; + } +} + +class Trie { + constructor(words) { + this.root = new TrieNode(); + words.forEach((word) => this.insert(word)); + } + + /* Time O(N) | Space O(N) */ + insert(word, node = this.root) { + for (const char of word) { + const child = node.children[char] || new TrieNode(); + + node.children[char] = child; + + node = child; + } + + node.word = word; + } + + /* Time O((ROWS * COLS) * (4 * (3 ^ (WORDS - 1)))) | Space O(N) */ + searchBoard(board, node = this.root, words = []) { + const [rows, cols] = [board.length, board[0].length]; + + for (let row = 0; row < rows; row++) { + for (let col = 0; col < cols; col++) { + this.dfs(board, row, rows, col, cols, node, words); + } + } + + return words; + } + + dfs(board, row, rows, col, cols, node, words) { + const char = board[row][col]; + const child = node.children[char] || null; + + if (this.canSkip(char, child)) return; + + node = child; + this.checkWord(node, words); + this.backTrack(board, row, rows, col, cols, node, words); + } + + canSkip(char, child) { + const hasSeen = char === '#'; + const isMissingChild = !child; + + return hasSeen || isMissingChild; + } + + checkWord(node, words) { + if (!node.word.length) return; + + words.push(node.word); + node.word = ''; + } + + backTrack(board, row, rows, col, cols, node, words) { + const char = board[row][col]; + + board[row][col] = '#'; + + for (const [_row, _col] of this.getNeighbors(row, rows, col, cols)) { + this.dfs(board, _row, rows, _col, cols, node, words); + } + + board[row][col] = char; + } + + getNeighbors(row, rows, col, cols) { + return [ + [row - 1, col], + [row + 1, col], + [row, col - 1], + [row, col + 1], + ].filter(([_row, _col]) => !this.isOutOfBounds(_row, rows, _col, cols)); + } + + isOutOfBounds(row, rows, col, cols) { + const isRowOut = row < 0 || rows <= row; + const isColOut = col < 0 || cols <= col; + + return isRowOut || isColOut; + } +} diff --git a/javascript/0213-house-robber-ii.js b/javascript/0213-house-robber-ii.js new file mode 100644 index 000000000..c23a7c691 --- /dev/null +++ b/javascript/0213-house-robber-ii.js @@ -0,0 +1,36 @@ +/** + * Greedy - Max + * Time O(N) | Space O(1) + * https://leetcode.com/problems/house-robber-ii/ + * @param {number[]} nums + * @return {number} + */ +var rob = (nums) => { + const isBaseCase1 = (nums.length === 0); + if (isBaseCase1) return 0; + + const isBaseCase2 = (nums.length === 1); + if (isBaseCase2) return nums[0] + + const left = search(nums, 0, (nums.length - 2)); /* Time O(N) */ + const right = search(nums, 1, (nums.length - 1));/* Time O(N) */ + + return Math.max(left, right); +}; + +const search = (nums, start, end) => { + let [ left, mid ] = [ 0, 0 ]; + + for (let i = start; i <= end; i++) {/* Time O(N) */ + const temp = mid; + const right = nums[i]; + const house = left + right; + + mid = Math.max(mid, house); + left = temp; + } + + return mid; +} + + diff --git a/javascript/0215-kth-largest-element-in-an-array.js b/javascript/0215-kth-largest-element-in-an-array.js new file mode 100644 index 000000000..8204d4aaa --- /dev/null +++ b/javascript/0215-kth-largest-element-in-an-array.js @@ -0,0 +1,33 @@ +/** + * https://leetcode.com/problems/kth-largest-element-in-an-array/ + * Time O(N * log(N)) | Space O(K) + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + var findKthLargest = function(nums, k) { return nums + .sort((a, b) => a - b) + .reverse() + .slice(k - 1) + .shift() +}; + +/** + * https://leetcode.com/problems/kth-largest-element-in-an-array/ + * Time O(N * log(K)) | Space O(K) + * @param {number[]} nums + * @param {number} k + * @return {number} + */ + var findKthLargest = function(nums, k) { + const minHeap = new MinPriorityQueue() + + for (const num of nums) { + minHeap.enqueue(num); + + const isAtCapacity = k < minHeap.size(); + if (isAtCapacity) minHeap.dequeue(); + } + + return minHeap.front().element +}; diff --git a/javascript/0217-contains-duplicate.js b/javascript/0217-contains-duplicate.js new file mode 100644 index 000000000..7242fe2dd --- /dev/null +++ b/javascript/0217-contains-duplicate.js @@ -0,0 +1,72 @@ +/** + * Brute Force - Linear Search + * Time O(N^2) | Space O(1) + * https://leetcode.com/problems/contains-duplicate/ + * @param {number[]} nums + * @return {boolean} + */ +var containsDuplicate = (nums) => { + for (let right = 0; right < nums.length; right++) {/* Time O(N) */ + for (let left = 0; left < right; left++) { /* Time O(N) */ + const isDuplicate = nums[left] === nums[right]; + if (isDuplicate) return true; + } + } + + return false; +} + +/** + * Sort - HeapSort Space O(1) | QuickSort Space O(log(N)) + * Time O(N * log(N)) | Space O(1) + * https://leetcode.com/problems/contains-duplicate/ + * @param {number[]} nums + * @return {boolean} + */ +var containsDuplicate = (nums) => { + nums.sort((a, b) => a - b);/* Time O(N * log(N)) | Space O(1 || log(N)) */ + + return hasDuplicate(nums); +} + +const hasDuplicate = (nums) => { + for (let curr = 0; curr < (nums.length - 1); curr++) {/* Time O(N) */ + const next = (curr + 1); + + const isNextDuplicate = nums[curr] === nums[next]; + if (isNextDuplicate) return true; + } + + return false; +} + +/** + * Hash Set + * Time O(N) | Space O(N) + * https://leetcode.com/problems/contains-duplicate/ + * @param {number[]} nums + * @return {boolean} + */ +var containsDuplicate = (nums) => { + const numsSet = new Set(nums);/* Time O(N) | Space O(N) */ + const isEqual = numsSet.size === nums.length; + + return !isEqual; +}; + +/** + * Hash Set - Early Exit + * Time O(N) | Space O(N) + * https://leetcode.com/problems/contains-duplicate/ + * @param {number[]} nums + * @return {boolean} + */ +var containsDuplicate = (nums, numsSet = new Set()) => { + for (const num of nums) {/* Time O(N) */ + if (numsSet.has(num)) return true; + + numsSet.add(num); /* Space O(N) */ + } + + return false; +}; \ No newline at end of file diff --git a/javascript/0219-contains-duplicate-ii.js b/javascript/0219-contains-duplicate-ii.js new file mode 100644 index 000000000..a72e225f8 --- /dev/null +++ b/javascript/0219-contains-duplicate-ii.js @@ -0,0 +1,27 @@ +/** + * @param {number[]} nums + * @param {number} k + * @return {boolean} + */ +/** + * @param {number[]} nums + * @param {number} k + * @return {boolean} + */ +var containsNearbyDuplicate = function(nums, k) { + const window = new Set(); + let L = 0; + for (let R = 0; R < nums.length; R++) { + if (!window.has(nums[R])) { + window.add(nums[R]); + } else { + return true + } + + if (R - L + 1 > k) { + window.delete(nums[L]) + L++; + } + } + return false; +}; diff --git a/javascript/0225-implement-stack-using-queues.js b/javascript/0225-implement-stack-using-queues.js new file mode 100644 index 000000000..b484845a2 --- /dev/null +++ b/javascript/0225-implement-stack-using-queues.js @@ -0,0 +1,27 @@ +var MyStack = function() { + this.q = []; +}; + +MyStack.prototype.push = function(x) { + this.q.push(x); +}; + +MyStack.prototype.pop = function() { + let size = this.q.length; + for(let i = 0; i < size - 1; i++) + this.push(this.q.shift()); + return this.q.shift(); +}; + +MyStack.prototype.top = function() { + let size = this.q.length; + for(let i = 0; i < size - 1; i++) + this.push(this.q.shift()); + let res = this.q.shift(); + this.push(res); + return res; +}; + +MyStack.prototype.empty = function() { + return this.q.length == 0 +}; diff --git a/javascript/0226-invert-binary-tree.js b/javascript/0226-invert-binary-tree.js new file mode 100644 index 000000000..55438b15e --- /dev/null +++ b/javascript/0226-invert-binary-tree.js @@ -0,0 +1,54 @@ +/** + * https://leetcode.com/problems/invert-binary-tree/ + * TIme O(N) | Space O(N) + * @param {TreeNode} root + * @return {TreeNode} + */ +var invertTree = (root) => { + const isBaseCase = root === null; + if (isBaseCase) return root; + + return dfs(root); +} + +const dfs = (root) => { + const left = invertTree(root.left); + const right = invertTree(root.right); + + root.left = right; + root.right = left; + + return root; +} + +/** + * https://leetcode.com/problems/invert-binary-tree/ + * TIme O(N) | Space O(W) + * @param {TreeNode} root + * @return {TreeNode} + */ +var invertTree = (root,) => { + const isBaseCase = root === null; + if (isBaseCase) return root; + + bfs([ root ]); + + return root; +} + +const bfs = (queue) => { + while (queue.length) { + for (let i = (queue.length - 1); 0 <= i; i--) { + const node = queue.shift(); + const left = node.right; + const right = node.left; + + node.left = left; + node.right = right; + + if (node.left) queue.push(node.left); + if (node.right) queue.push(node.right); + } + } +} + diff --git a/javascript/0230-kth-smallest-element-in-a-bst.js b/javascript/0230-kth-smallest-element-in-a-bst.js new file mode 100644 index 000000000..3cb704080 --- /dev/null +++ b/javascript/0230-kth-smallest-element-in-a-bst.js @@ -0,0 +1,49 @@ +/** + * https://leetcode.com/problems/kth-smallest-element-in-a-bst/ + * Time O(N + K) | Space O(H) + * @param {TreeNode} root + * @param {number} k + * @return {number} + */ + var kthSmallest = function(root, k, inOrder = []) { + if (!root) return inOrder + + return dfs(root, k, inOrder); +}; + +const dfs = (root, k, inOrder) => { + if (root.left) kthSmallest(root.left, k, inOrder); + + inOrder.push(root.val); + + if (root.right) kthSmallest(root.right, k, inOrder); + + return inOrder[(k - 1)]; +} + +/** + * https://leetcode.com/problems/kth-smallest-element-in-a-bst/ + * Time O(N + K) | Space O(H) + * @param {TreeNode} root + * @param {number} k + * @return {number} + */ + var kthSmallest = function(root, k, stack = []) { + while (k--) { + root = moveLeft(root, stack); + + const isSmallest = k === 0; + if (isSmallest) return root.val; + + root = root.right; + } +} + +const moveLeft = (root, stack) => { + while (root !== null) { + stack.push(root); + root = root.left; + } + + return stack.pop(); +} diff --git a/javascript/0232-implement-queue-using-stacks.js b/javascript/0232-implement-queue-using-stacks.js new file mode 100644 index 000000000..c728e2daa --- /dev/null +++ b/javascript/0232-implement-queue-using-stacks.js @@ -0,0 +1,57 @@ +// https://leetcode.com/problems/implement-queue-using-stacks/ + +var MyQueue = function() { + this.stack1 = []; + this.stack2 = []; +}; + +/** + * @param {number} x + * @return {void} + */ +MyQueue.prototype.push = function(x) { + this.stack1.push(x); +}; + +/** + * @return {number} + */ +MyQueue.prototype.pop = function() { + this.swappingStacks(); + + if(!this.stack2.length){ + return null; + } + return this.stack2.pop(); +}; + +/** + * @return {number} + */ +MyQueue.prototype.peek = function() { + this.swappingStacks(); + return this.stack2.length == 0 ? null : this.stack2[this.stack2.length-1] +}; + +/** + * @return {boolean} + */ +MyQueue.prototype.empty = function() { + return this.stack1.length === 0 && this.stack2.length === 0; +}; + +MyQueue.prototype.swappingStacks = function() { + if (this.stack1.length) { + this.stack2 = [...this.stack1.reverse(), ...this.stack2]; + this.stack1 = []; + } +} + +/** + * Your MyQueue object will be instantiated and called as such: + * var obj = new MyQueue() + * obj.push(x) + * var param_2 = obj.pop() + * var param_3 = obj.peek() + * var param_4 = obj.empty() + */ diff --git a/javascript/0234-palindrome-linked-list.js b/javascript/0234-palindrome-linked-list.js new file mode 100644 index 000000000..45d113499 --- /dev/null +++ b/javascript/0234-palindrome-linked-list.js @@ -0,0 +1,43 @@ +/** + * Definition for singly-linked list. + * function ListNode(val, next) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + */ +/** + * @param {ListNode} head + * @return {boolean} + */ +var isPalindrome = function (head) { + // find mid point + let slow = head; + let fast = head; + while (fast && fast.next) { + slow = slow.next; + fast = fast.next.next; + } + + // reverse 2nd half + let curr = slow; + let prev = null; + while (curr) { + let next = curr.next; + curr.next = prev; + prev = curr; + curr = next; + } + let head2 = prev; + + // compare both halfs + while (head && head2) { + if (head.val !== head2.val) { + return false; + } + + head = head.next; + head2 = head2.next; + } + + return true; +}; diff --git a/javascript/0235-lowest-common-ancestor-of-a-binary-search-tree.js b/javascript/0235-lowest-common-ancestor-of-a-binary-search-tree.js new file mode 100644 index 000000000..ee8396413 --- /dev/null +++ b/javascript/0235-lowest-common-ancestor-of-a-binary-search-tree.js @@ -0,0 +1,45 @@ +/** + * https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/ + * Time O(H) | Space O(H) + * @param {TreeNode} root + * @param {TreeNode} p + * @param {TreeNode} q + * @return {TreeNode} + */ + var lowestCommonAncestor = function(root, p, q) { + const isGreater = (p.val < root.val) && (q.val < root.val); + if (isGreater) return lowestCommonAncestor(root.left, p, q); + + const isLess = (root.val < p.val) && (root.val < q.val); + if (isLess) return lowestCommonAncestor(root.right, p, q); + + return root; +}; + +/** + * https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/ + * Time O(H) | Space O(1) + * @param {TreeNode} root + * @param {TreeNode} p + * @param {TreeNode} q + * @return {TreeNode} + */ + var lowestCommonAncestor = function(root, p, q) { + while (root !== null) { + const isGreater = (root.val < p.val) && (root.val < q.val) + if (isGreater) { + root = root.right; + continue; + } + + const isLess = (p.val < root.val) && (q.val < root.val);; + if (isLess) { + root = root.left; + continue; + } + + break; + } + + return root; +}; diff --git a/javascript/0238-product-of-array-except-self.js b/javascript/0238-product-of-array-except-self.js new file mode 100644 index 000000000..a3ef93d0b --- /dev/null +++ b/javascript/0238-product-of-array-except-self.js @@ -0,0 +1,23 @@ +/** + * Array + * Time O(N) | Space O(N) + * https://leetcode.com/problems/product-of-array-except-self/ + * @param {number[]} nums + * @return {number[]} + */ + function productExceptSelf(nums) { + const result = []; + let prefix = 1; + let postfix = 1; + + for (let i = 0; i < nums.length; i++) { + result[i] = prefix; + prefix *= nums[i]; + } + for (let i = nums.length - 2; i >= 0; i--) { + postfix *= nums[i + 1]; + result[i] *= postfix; + } + + return result; +}; \ No newline at end of file diff --git a/javascript/0239-sliding-window-maximum.js b/javascript/0239-sliding-window-maximum.js new file mode 100644 index 000000000..f27463d0a --- /dev/null +++ b/javascript/0239-sliding-window-maximum.js @@ -0,0 +1,139 @@ +/** + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ +function Node(value) { + this.value = value; + this.prev = null; + this.next = null; +} + +function Deque() { + this.left = null; + this.right = null; + this.size = 0; + this.pushRight = function (value) { + const node = new Node(value); + if (this.size == 0) { + this.left = node; + this.right = node; + } else { + this.right.next = node; + node.prev = this.right; + this.right = node; + } + this.size++; + return this.size; + }; + this.popRight = function () { + if (this.size == 0) return null; + const removedNode = this.right; + this.right = this.right.prev; + if (this.right) this.right.next = null; + this.size--; + return removedNode; + }; + this.pushLeft = function (value) { + const node = new Node(value); + if (this.size == 0) { + this.left = node; + this.right = node; + } else { + this.left.prev = node; + node.next = this.left; + this.left = node; + } + this.size++; + return this.size; + }; + this.popLeft = function () { + if (this.size == 0) return null; + const removedNode = this.left; + this.left = this.left.next; + if (this.left) this.left.prev = null; + this.size--; + return removedNode; + }; +} + +var maxSlidingWindow = function (nums, k) { + const output = []; + let deque = new Deque(); + let left = 0; + let right = 0; + + while (right < nums.length) { + // pop smaller values from q + while (deque.right && nums[deque.right.value] < nums[right]) + deque.popRight(); + deque.pushRight(right); + + // remove left val from window + if (left > deque.left.value) deque.popLeft(); + + if (right + 1 >= k) { + output.push(nums[deque.left.value]); + left++; + } + right++; + } + return output; +}; + +/** + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ + +// Deque Implementation using Lazy Deletion +class LazyDeletionDeque { + constructor() { + this.deque = []; + this.leftIdx = 0; + } + + isEmpty = () => { + return this.deque.length === this.leftIdx; + }; + push = (num) => { + this.deque.push(num); + }; + popFront = () => { + this.leftIdx++; + }; + popBack = () => { + !this.isEmpty() && this.deque.pop(); + }; + front = () => { + return this.deque[this.leftIdx]; + }; + back = () => { + return this.deque[this.deque.length - 1]; + }; +} + +var maxSlidingWindowWithLazyDeletionDeque = function (nums, k) { + const deque = new LazyDeletionDeque(); + const answer = []; + let leftWindow = 0; + for (let rightWindow = 0; rightWindow < nums.length; rightWindow++) { + const rightNum = nums[rightWindow]; + while (!deque.isEmpty() && rightNum > deque.back()) { + deque.popBack(); + } + deque.push(rightNum); + + if (rightWindow >= k - 1) { + const dequeFront = deque.front(); + const leftNum = nums[leftWindow]; + if (leftNum === dequeFront) { + deque.popFront(); + } + answer.push(dequeFront); + leftWindow++; + } + } + return answer; +}; diff --git a/javascript/0242-valid-anagram.js b/javascript/0242-valid-anagram.js new file mode 100644 index 000000000..9fc874482 --- /dev/null +++ b/javascript/0242-valid-anagram.js @@ -0,0 +1,64 @@ +/** + * Sort - HeapSort Space O(1) | QuickSort Space O(log(N)) + * Time O(N * logN) | Space O(N) + * https://leetcode.com/problems/valid-anagram/ + * @param {string} s + * @param {string} t + * @return {boolean} + */ +var isAnagram = (s, t) => { + const isEqual = s.length === t.length; + if (!isEqual) return false; + + return reorder(s) === reorder(t); /* Time O(N * logN) | Space O(N) */ +}; + +const reorder = (str) => str + .split('') /* Time O(N) | Space O(N) */ + .sort((a, b) => a.localeCompare(b))/* Time O(N * log(N)) | Space O(1 || log(N)) */ + .join(''); /* Time O(N) | Space O(N) */ + +/** + * Hash Map - Frequency Counter + * Time O(N) | Space O(1) + * https://leetcode.com/problems/valid-anagram/ + * @param {string} s + * @param {string} t + * @return {boolean} + */ +var isAnagram = (s, t, map = new Map()) => { + const isEqual = s.length === t.length; + if (!isEqual) return false; + + addFrequency(s, map); /* Time O(N) | Space O(1) */ + subtractFrequency(t, map); /* Time O(N) | Space O(1) */ + + return checkFrequency(map);/* Time O(N) */ +}; + +const addFrequency = (str, map) => { + for (const char of str) {/* Time O(N) */ + const count = (map.get(char) || 0) + 1; + + map.set(char, count); /* Space O(1) */ + } +} + +const subtractFrequency = (str, map) => { + for (const char of str) {/* Time O(N) */ + if (!map.has(char)) continue; + + const count = map.get(char) - 1; + + map.set(char, count); /* Space O(1) */ + } +}; + +const checkFrequency = (map) => { + for (const [ char, count ] of map) {/* Time O(N) */ + const isEmpty = count === 0; + if (!isEmpty) return false; + } + + return true; +} diff --git a/javascript/0252-meeting-rooms.js b/javascript/0252-meeting-rooms.js new file mode 100644 index 000000000..bdd933a51 --- /dev/null +++ b/javascript/0252-meeting-rooms.js @@ -0,0 +1,29 @@ +/** + * https://leetcode.com/problems/meeting-rooms/ + * Time O(N * logN) | Space O(1) + * @param {number[][]} intervals + * @return {boolean} + */ +var canAttendMeetings = function (intervals) { + intervals.sort(([aStart, aEnd], [bStart, bEnd]) => + aStart !== bStart ? aStart - bStart : aEnd - bEnd + ); + + return canAttend(intervals); +}; + +const canAttend = (intervals) => { + let prev = intervals.shift(); + + for (const curr of intervals) { + const [prevStart, prevEnd] = prev; + const [currStart, currEnd] = curr; + + const hasOverlap = currStart < prevEnd; + if (hasOverlap) return false; + + prev = curr; + } + + return true; +}; diff --git a/javascript/0253-meeting-rooms-ii.js b/javascript/0253-meeting-rooms-ii.js new file mode 100644 index 000000000..0ca7756e8 --- /dev/null +++ b/javascript/0253-meeting-rooms-ii.js @@ -0,0 +1,39 @@ +/** + * https://leetcode.com/problems/meeting-rooms-ii/ + * Time O((N * logN) + (M * logM)) | Space O(1) + * @param {number[][]} intervals + * @return {number} + */ +var minMeetingRooms = function (intervals) { + const { start, end } = splitIntervals(intervals); + let [minRooms, startIndex, endIndex] = [0, 0, 0]; + + while (startIndex < intervals.length) { + const [currStart, prevEnd] = [start[startIndex], end[endIndex]]; + + const hasGap = prevEnd <= currStart; + if (hasGap) { + minRooms--; + endIndex++; + } + + minRooms++; + startIndex++; + } + + return minRooms; +}; + +const splitIntervals = (intervals, start = [], end = []) => { + for (const [startTime, endTime] of intervals) { + start.push(startTime); + end.push(endTime); + } + + const comparator = (a, b) => a - b; + + start.sort(comparator); + end.sort(comparator); + + return { start, end }; +}; diff --git a/javascript/0261-graph-valid-tree.js b/javascript/0261-graph-valid-tree.js new file mode 100644 index 000000000..0f88af14a --- /dev/null +++ b/javascript/0261-graph-valid-tree.js @@ -0,0 +1,136 @@ +/** + * https://leetcode.com/problems/graph-valid-tree/ + * Time O(E * a(N)) | Space O(V) + * @param {number} n + * @param {number[][]} edges + * @return {boolean} + */ +var validTree = function(n, edges, root = 0) { + const isEqual = edges.length === (n - 1) + if (!isEqual) return false; + + const { graph, visited } = buildGraph(n, edges) + + dfs(root, graph, visited); + + return visited.size === n; +} + +var initGraph = (n) => ({ + graph: new Array(n).fill().map(() => []), + visited: new Set() +}) + +var buildGraph = (n, edges) => { + const { graph, visited } = initGraph(n) + + for (const [ src, dst ] of edges) { + graph[src].push(dst); + graph[dst].push(src); + } + + return { graph, visited } +} + +const dfs = (node, graph, visited) => { + if (visited.has(node)) return; + visited.add(node); + + for (const neighbor of graph[node]) { + dfs(neighbor, graph, visited); + } +} + +/** + * https://leetcode.com/problems/graph-valid-tree/ + * Time O(E * a(N)) | Space O(V) + * @param {number} n + * @param {number[][]} edges + * @return {boolean} + */ +var validTree = function(n, edges) { + const isEqual = edges.length === (n - 1) + if (!isEqual) return false; + + const { graph, visited, queue } = buildGraph(n, edges) + + bfs(graph, visited, queue) + + return visited.size === n; +} + +var initGraph = (n) => ({ + graph: new Array(n).fill().map(() => []), + visited: new Set(), + queue: new Queue(), + root: 0 +}) + +var buildGraph = (n, edges) => { + const { graph, visited, queue, root } = initGraph(n) + + for (const [ src, dst ] of edges) { + graph[src].push(dst); + graph[dst].push(src); + } + + queue.enqueue(root); + visited.add(root); + + return { graph, visited, queue } +} + +const bfs = (graph, visited, queue) => { + while (!queue.isEmpty()) { + for (let i = (queue.size() - 1); 0 <= i; i--) { + checkNeighbor(graph, visited, queue) + } + } +} + +const checkNeighbor = (graph, visited, queue) => { + const node = queue.dequeue(); + + for (const neighbor of graph[node]) { + if (visited.has(neighbor)) continue; + visited.add(neighbor); + + queue.enqueue(neighbor); + } +} + +/** + * https://leetcode.com/problems/graph-valid-tree/ + * Time O(E * a(N)) | Space O(V) + * @param {number} n + * @param {number[][]} edges + * @return {boolean} + */ +var validTree = function(n, edges) { + const union = new Array(n).fill(-1) + + for (const [ src, dst ] of edges) { + const [ x, y ] = [ find(union, src), find(union, dst) ] + + const hasCycle = x === y + if (hasCycle) return false + + compress(union, x, y) + } + + const isValid = edges.length === (n - 1) + return isValid +}; + +const compress = (union, i, head) => union[i] = head + +const find = (union, i, num = union[i]) => { + const isEmpty = num === -1 + if (isEmpty) return i + + const head = find(union, num) + + compress(union, i, head) + + return union[i] +} diff --git a/javascript/0263-ugly-number.js b/javascript/0263-ugly-number.js new file mode 100644 index 000000000..3f5997772 --- /dev/null +++ b/javascript/0263-ugly-number.js @@ -0,0 +1,9 @@ +var isUgly = function(n) { + if(n <= 0) + return false; + + for(const p of [2, 3, 5]) + while(n % p == 0) + n = n / p; + return n == 1; +}; diff --git a/javascript/0268-missing-number.js b/javascript/0268-missing-number.js new file mode 100644 index 000000000..5d6e60928 --- /dev/null +++ b/javascript/0268-missing-number.js @@ -0,0 +1,15 @@ +/** + * https://leetcode.com/problems/missing-number/ + * Time O(N) | Space O(1) + * @param {number[]} nums + * @return {number} + */ +var missingNumber = function (nums, missingNumber = nums.length) { + for (let i = 0; i < nums.length; i++) { + const xor = (i ^ nums[i]); + + missingNumber ^= xor; + } + + return missingNumber; +}; diff --git a/javascript/0269-alien-dictionary.js b/javascript/0269-alien-dictionary.js new file mode 100644 index 000000000..6638e7c78 --- /dev/null +++ b/javascript/0269-alien-dictionary.js @@ -0,0 +1,179 @@ +/** + * BFS + * https://leetcode.com/problems/alien-dictionary/ + * @param {string[]} words + * @return {string} + */ + var alienOrder = function(words) { + const { graph, frequencyMap, queue, buffer } = buildGraph(words); + + if (!canBuildGraph(words, graph, frequencyMap)) return ''; + + queueSources(queue, frequencyMap); + bfs(queue, frequencyMap, graph, buffer); + + return (frequencyMap.size <= buffer.length) + ? buffer.join('') + : ''; +} + +var initGraph = () => ({ + graph: new Map(), + frequencyMap: new Map(), + queue: new Queue(), + buffer: [], +}) + +var buildGraph = (words) => { + const { graph, frequencyMap, queue, buffer } = initGraph(); + + for (const word of words) { + for (const char of word) { + frequencyMap.set(char, 0); + graph.set(char, []); + } + } + + return { graph, frequencyMap, queue, buffer }; +}; + +var canBuildGraph = (words, graph, frequencyMap) => { + for (let index = 0; (index < words.length - 1); index++) { + const [ word1, word2 ] = [ words[index], words[(index + 1)] ]; + const minLength = Math.min(word1.length, word2.length) + + const isWord1Longer = (word2.length < word1.length); + const isPrefix = isWord1Longer && word1.startsWith(word2); + + if (isPrefix) return false; + + for (let j = 0; (j < minLength); j++) { + const [ char1, char2 ] = [ word1[j], word2[j] ]; + + const isEqual = (char1 === char2); + if (isEqual) continue; + + graph.get(char1).push(char2); + frequencyMap.set(char2, frequencyMap.get(char2) + 1); + + break; + } + } + + return true; +}; + +const bfs = (queue, frequencyMap, graph, buffer) => { + while (!queue.isEmpty()) { + for (let level = (queue.size() - 1); (0 <= level); level--) { + checkNeighbors(queue, frequencyMap, graph, buffer) + } + } +}; + +var checkNeighbors = (queue, frequencyMap, graph, buffer) => { + const char = queue.dequeue(); + + buffer.push(char); + + for (const next of graph.get(char)) { + const value = (frequencyMap.get(next) - 1); + + frequencyMap.set(next, value); + + const isEmpty = (frequencyMap.get(next) === 0); + if (!isEmpty) continue; + + queue.enqueue(next); + } +} + +const queueSources = (queue, frequencyMap) => { + for (const [ key, value ] of frequencyMap) { + const isEmpty = (frequencyMap.get(key) === 0); + if (!isEmpty) continue; + + queue.enqueue(key); + } +} + +/** + * DFS + * https://leetcode.com/problems/alien-dictionary/ + * @param {string[]} words + * @return {string} + */ + var alienOrder = function(words) { + const { graph, seen, buffer } = buildGraph(words); + + if (!canBuildGraph(words, graph)) return ''; + + for (const [ char ] of graph) { + if (!dfs(char, graph, seen, buffer)) return ''; + } + + return buffer.reverse().join('') +} + +var initGraph = () => ({ + graph: new Map(), + seen: new Map(), + buffer: [], +}) + +var buildGraph = (words) => { + const { graph, seen, buffer } = initGraph(); + + for (const word of words) { + for (const char of word) { + graph.set(char, []); + } + } + + return { graph, seen, buffer }; +}; + +var canBuildGraph = (words, graph) => { + for (let index = 0; (index < words.length - 1); index++) { + const [ word1, word2 ] = [ words[index], words[(index + 1)] ]; + const minLength = Math.min(word1.length, word2.length) + + const isWord1Longer = (word2.length < word1.length); + const isPrefix = isWord1Longer && word1.startsWith(word2); + + if (isPrefix) return false; + + for (let j = 0; (j < minLength); j++) { + const [ char1, char2 ] = [ word1[j], word2[j] ]; + + const isEqual = (char1 === char2); + if (isEqual) continue; + + graph.get(char1).push(char2); + + break; + } + } + + return true; +}; + +const dfs = (char, graph, seen, buffer) => { + if (seen.has(char)) return seen.get(char); + + if (!backTrack(char, graph, seen, buffer)) return false; + + buffer.push(char); + + return true; +} + +const backTrack = (char, graph, seen, buffer) => { + seen.set(char, false); + for (const neighbor of graph.get(char)) { + if (!dfs(neighbor, graph, seen, buffer)) return false; + } + seen.set(char, true); + + return true; +} \ No newline at end of file diff --git a/javascript/0271-encode-and-decode-strings.js b/javascript/0271-encode-and-decode-strings.js new file mode 100644 index 000000000..2b16eea97 --- /dev/null +++ b/javascript/0271-encode-and-decode-strings.js @@ -0,0 +1,109 @@ +/** + * String - Delimiter + * Time O(N) | Space O(1) + * https://leetcode.com/problems/encode-and-decode-strings/ + * @param {string[]} strs + * @return {string} + */ +var encode = (strs) => { + return strs + .map((str) => `${str.length}#${str}`)/* Time O(N) | Ignore Auxillary Space O(N) */ + .join(''); /* Time O(N) | Ignore Auxillary Space O(N) */ +} + +/** + * String - Delimiter + * Time O(N * K) | Space O(N) + * https://leetcode.com/problems/encode-and-decode-strings/ + * @param {string} str + * @return {string[]} + */ +var decode = (str, index = 0, decodedWords = []) => { + while (index < str.length) {/* Time O(N) */ + const { nextIndex, word } = delimitWord(str, index);/* Time O(K) | Ignore Auxillary Space Space (K) */ + + decodedWords.push(word); /* | Ignore Auxillary Space O(N * K ) */ + index = nextIndex; + } + + return decodedWords; +} + +const delimitWord = (str, index) => { + const delimiter = str.indexOf('#', index); /* Time O(K) */ + const length = Number(str.slice(index, delimiter)); /* Time O(K) */ + const [ start, end ] = [ (delimiter + 1), ((delimiter + length) + 1) ]; + const word = str.slice(start, end); /* Time O(K) | Ignore Auxillary Space O(K) */ + + return { + nextIndex: end, + word + }; +} + +/** + * Non-ASCII Delimiter - Ignore Auxiliary Space + * Time O(N) | Space O(1) + * https://leetcode.com/problems/encode-and-decode-strings/ + * @param {string[]} strs + * @return {string} + */ +var encode = (strs, nonASCIICode = String.fromCharCode(257)) => { + return strs.join(nonASCIICode);/* Time O(N) | Ignore Auxillary Space O(N) */ +}; + +/** + * Non-ASCII Delimiter - Ignore Auxiliary Space + * Time O(N) | Space O(1) + * https://leetcode.com/problems/encode-and-decode-strings/ + * @param {string[]} strs + * @return {string} + */ +var decode = (strs, nonASCIICode = String.fromCharCode(257)) => { + return strs.split(nonASCIICode);/* Time O(N) | Ignore Auxillary Space O(N) */ +}; + +/** + * Chunk Transfer Encoding + * Time O(N) | Space O(1) + * https://leetcode.com/problems/encode-and-decode-strings/ + * @param {string[]} strs + * @return {string} + */ +var encode = (strs, sb = []) => { + for (const str of strs) {/* Time O(N) */ + const code = getCode(str);/* Time O(1) */ + const encoding = `${code}${str}`; + + sb.push(encoding); + } + + return sb.join(''); /* Time O(N) | Ignore Auxillary Space O(N) */ +} + +const getCode = (str) => str + .length + .toString(2) + .padStart(8,'0'); + +/** + * Chunk Transfer Encoding + * Time O(N) | Space O(1) + * https://leetcode.com/problems/encode-and-decode-strings/ + * @param {string} str + * @return {string[]} + */ +var decode = (str, output = []) => { + for (let left = 0, right = (left + 8),length = 0; + left < str.length; + left = (right + length), right = (left + 8) + ) { /* Time O(N) */ + const countString = str.slice(left, right); /* | Ignore Auxillary Space O(K) */ + length = parseInt(countString, 2); + + const decoding = str.slice(right, (right + length)); /* Time O(K) | Ignore Auxillary Space O(N * K) */ + output.push(decoding); /* | Ignore Auxillary Space O(N * K) */ + } + + return output; +} diff --git a/javascript/0273-integer-to-english-words.js b/javascript/0273-integer-to-english-words.js new file mode 100644 index 000000000..f86df2ae0 --- /dev/null +++ b/javascript/0273-integer-to-english-words.js @@ -0,0 +1,50 @@ +/** + * Time O(Log10N) | Space O(1) + * https://leetcode.com/problems/integer-to-english-words + * @param {number} num + * @return {string} + */ + +var convertToWords = function (num) { + var belowTwenty = ["", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"]; + var belowHundred = ["", "Ten", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"]; + var thousands = ["" , "Thousand", "Million", "Billion"] + var pointer = 0; + result = ""; + + while (num > 0) { + var words = ""; + reminder = num % 1000; + num = Math.floor(num / 1000); + + if (reminder > 0) { + if (reminder >= 100) { + words += belowTwenty[Math.floor(reminder / 100)] + " Hundred "; + reminder %= 100; + } + + if (reminder >= 20) { + words += belowHundred[Math.floor(reminder / 10)] + " "; + reminder %= 10; + } + + if (reminder > 0) { + words += belowTwenty[Math.floor(reminder)] + " "; + } + + result = words + thousands[pointer] + " " + result; + } + pointer += 1; + } + return result.trim(); +} + +var numberToWords = function (num) { + if (num == 0) { + return "Zero"; + } + else { + return convertToWords(num); + } + +}; \ No newline at end of file diff --git a/javascript/0283-move-zeroes.js b/javascript/0283-move-zeroes.js new file mode 100644 index 000000000..dff8f4e2e --- /dev/null +++ b/javascript/0283-move-zeroes.js @@ -0,0 +1,46 @@ +/** + * Two Pointer + * Time O(N) | Space O(N) + * https://leetcode.com/problems/move-zeroes/ + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ +var moveZeroes = function(nums) { + + const arr = new Array(nums.length).fill(0); + + let [left, right] = [0, 0]; + + while (right < nums.length) { + const isZero = (nums[right] === 0); + if (!isZero) { + arr[left] = nums[right]; + left++; + } + + right++; + } + + return arr; +}; + +/** + * 2 Pointer + * Time O(N) | Space O(1) + * https://leetcode.com/problems/move-zeroes/ + * @param {number[]} nums + * @return {void} Do not return anything, modify nums in-place instead. + */ +var moveZeroes = (nums) => { + let [ left, right ] = [ 0, 0 ]; + + while (right < nums.length) { + const canSwap = (nums[right] !== 0) + if (canSwap) { + [nums[left], nums[right]] = [nums[right], nums[left]]; + left++; + } + + right++; + } +}; diff --git a/javascript/0286-walls-and-gates.js b/javascript/0286-walls-and-gates.js new file mode 100644 index 000000000..cb5324d13 --- /dev/null +++ b/javascript/0286-walls-and-gates.js @@ -0,0 +1,87 @@ +/** + * https://leetcode.com/problems/walls-and-gates/ + * Time O(ROWS * COLS) | Space O(ROWS * COLS) + * @param {number[][]} rooms + * @return {void} Do not return anything, modify rooms in-place instead. + */ +var wallsAndGates = function(rooms) { + const [ rows, cols ] = [ rooms.length, rooms[0].length ]; + + for (let row = 0; row < rows; row++) { + for (let col = 0; col < cols; col++) { + const isGate = rooms[row][col] === 0; + if (!isGate) continue; + + dfs(rooms, row, col); + } + } +} + +const dfs = (rooms, row, col) => { + const [ rows, cols ] = [ rooms.length, rooms[0].length ]; + + for (const [ _row, _col ] of getNeighbors(row, rows, col, cols)) { + const isPreviousDistanceGreater = rooms[_row][_col] <= (rooms[row][col] + 1); + if (isPreviousDistanceGreater) continue; + + rooms[_row][_col] = (rooms[row][col] + 1); + + dfs(rooms, _row, _col); + } +} + +var getNeighbors = (row, rows, col, cols) => [ [ 0, 1 ],[ 0, -1 ], [ 1, 0 ], [ -1, 0 ] ] + .map(([ _row, _col ]) => [ (row + _row), (col + _col) ]) + .filter(([ _row, _col ]) => (0 <= _row) && (0 <= _col) && (_row < rows) && (_col < cols)); + +/** + * https://leetcode.com/problems/walls-and-gates/ + * Time O(ROWS * COLS) | Space O(ROWS * COLS) + * @param {number[][]} rooms + * @return {void} Do not return anything, modify rooms in-place instead. + */ +var wallsAndGates = function(rooms) { + const queue = searchGrid(rooms); + + bfs(rooms, queue); +}; + +const searchGrid = (rooms, queue = new Queue([])) => { + const [ rows, cols ] = [ rooms.length, rooms[0].length ]; + + for (let row = 0; row < rows; row++) { + for (let col = 0; col < cols; col++) { + const isGate = rooms[row][col] === 0; + if (!isGate) continue; + + queue.enqueue([ row, col ]); + } + } + + return queue; +} + +const bfs = (rooms, queue) => { + while (!queue.isEmpty()) { + for (let i = (queue.size() - 1); 0 <= i; i--) { + checkNeighbors(rooms, queue); + } + } +} + +const checkNeighbors = (rooms, queue) => { + const [ rows, cols ] = [ rooms.length, rooms[0].length ]; + const [ row, col ] = queue.dequeue(); + + for (const [ _row, _col ] of getNeighbors(row, rows, col, cols)) { + const isINF = rooms[_row][_col] === 2147483647; /* (2 ** 31) - 1 */ + if (!isINF) continue; + + rooms[_row][_col] = (rooms[row][col] + 1); + queue.enqueue([ _row, _col ]); + } +} + +var getNeighbors = (row, rows, col, cols) => [ [ 0, 1 ],[ 0, -1 ], [ 1, 0 ], [ -1, 0 ] ] + .map(([ _row, _col ]) => [ (row + _row), (col + _col) ]) + .filter(([ _row, _col ]) => (0 <= _row) && (0 <= _col) && (_row < rows) && (_col < cols)); diff --git a/javascript/0287-find-the-duplicate-number.js b/javascript/0287-find-the-duplicate-number.js new file mode 100644 index 000000000..e3578386b --- /dev/null +++ b/javascript/0287-find-the-duplicate-number.js @@ -0,0 +1,257 @@ +/** + * https://leetcode.com/problems/find-the-duplicate-number/ + * Time O(N * log(N)) | Space O(1) + * @param {number[]} nums + * @return {number} + */ +var findDuplicate = function(nums) { + nums.sort((a, b) => a - b);/* Time O(N * log(N)) | HeapSort Space O(1) | QuickSort Space O(log(N)) */ + + for (let i = 1; i < nums.length; i++) {/* Time O(N) */ + const isPrevDuplicate = nums[i - 1] === nums[i] + if (isPrevDuplicate) return nums[i]; + } + + return -1; +} + +/** + * https://leetcode.com/problems/find-the-duplicate-number/ + * Time O(N * log(N)) | Space O(1) + * @param {number[]} nums + * @return {number} + */ +var findDuplicate = function(nums) { + let [ left, right, duplicate ] = [ 1, (nums.length - 1), -1 ]; + + while (left <= right) {/* Time O(log(N)) */ + const mid = (left + right) >> 1; + const count = getCount(mid, nums);/* Time O(N) */ + + const isMidGreater = count <= mid + if (isMidGreater) left = mid + 1; + + const isMidLess = mid < count + if (isMidLess) { + duplicate = mid; + right = mid - 1; + } + } + + return duplicate; +} + +const getCount = (mid, nums, count = 0) => { + for (const num of nums) {/* Time O(N) */ + const isMidGreater = num <= mid + if (isMidGreater) count++; + } + + return count; +} + +/** + * https://leetcode.com/problems/find-the-duplicate-number/ + * Time O(N * log(N)) | Space O(1) + * @param {number[]} nums + * @return {number} + */ +var findDuplicate = function(nums, duplicate = 0) { + const mostSignificantBit = calcMaxBit(nums); /* Time O(N) */ + + for (let bit = 0; bit < mostSignificantBit; bit++) {/* Time O(log(N)) */ + const [ baseCount, numsCount, mask ] = count(nums, bit);/* Time O(N) */ + + const isMoreFrequentlySet = baseCount < numsCount + if (isMoreFrequentlySet) duplicate |= mask; + } + + return duplicate; +} + +const calcMaxBit = (nums, bits = 0) => { + let max = Math.max(0, ...nums);/* Time O(N) */ + + while (max) {/* Time O(log(MAX)) */ + max >>= 1; + bits++; + } + + return bits; +} + +const count = (nums, bit) => { + let [ baseCount, numsCount, mask ] = [ 0, 0, (1 << bit) ]; + + for (let i = 0; i < nums.length; i++) {/* Time O(N) */ + const isBaseBitSet = 0 < (i & mask); + if (isBaseBitSet) baseCount++; + + const isNumBitSet = 0 < (nums[i] & mask); + if (isNumBitSet) numsCount++; + } + + return [ baseCount, numsCount, mask ]; +} + +/** + * https://leetcode.com/problems/find-the-duplicate-number/ + * Time O(N) | Space O(N) + * @param {number[]} nums + * @return {number} + */ +var findDuplicate = function(nums, curr = 0) { + const isBaseCase = curr === nums[curr] + if (isBaseCase) return curr; + + const next = nums[curr]; + + nums[curr] = curr; + + return findDuplicate(nums, next);/* Time O(N) | Space O(N) */ +} + +/** + * https://leetcode.com/problems/find-the-duplicate-number/ + * Time O(N) | Space O(N) + * @param {number[]} nums + * @return {number} + */ + var findDuplicate = function(nums, seen = new Set()) { + for (const num of nums) {/* Time O(N) */ + if (seen.has(num)) return num; + + seen.add(num); /* Space O(N) */ + } + + return -1; +} + +/** + * https://leetcode.com/problems/find-the-duplicate-number/ + * Time O(N) | Space O(1) + * @param {number[]} nums + * @return {number} + */ +var findDuplicate = function(nums) { + cyclicSort(nums); /* Time O(N) */ + + return search(nums); /* Time O(N) */ +} + +const cyclicSort = (nums, index = 0) => { + const swap = (arr, a, b) => [arr[a], arr[b]] = [arr[b], arr[a]]; + + while (index < nums.length) { /* Time O(N) */ + const [ num, arrayIndex, arrayNum ] = [ nums[index], (nums[index] - 1), nums[(nums[index] - 1)] ]; + + const canSwap = !isSame(num, arrayNum); + if (canSwap) { + swap(nums, index, arrayIndex); + + continue; + } + + index++; + } +} +const isSame = (a, b) => a === b; + +const search = (nums) => { + for (let index = 0; index < nums.length; index++) {/* Time O(N) */ + const [ num, arrayIndex ] = [ nums[index], (index + 1) ]; + + if (!isSame(num, arrayIndex)) return num; + } + + return nums.length; +} + +/** + * https://leetcode.com/problems/find-the-duplicate-number/ + * Time O(N) | Space O(1) + * @param {number[]} nums + * @return {number} + */ +var findDuplicate = function(nums) { + const duplicate = negativeMarking(nums);/* Time O(N) */ + + restoreToPositiveNumbers(nums); /* Time O(N) */ + + return duplicate; +} + +const negativeMarking = (nums) => { + for (let i = 0; i < nums.length; i++) {/* Time O(N) */ + const curr = Math.abs(nums[i]); + + const isNegative = nums[curr] < 0; + if (isNegative) return curr; + + nums[curr] *= -1; + } + + return -1 +} + +const restoreToPositiveNumbers = (nums) => { + for (let i = 0; i < nums.length; i++) {/* Time O(N) */ + nums[i] = Math.abs(nums[i]); + } +} + +/** + * https://leetcode.com/problems/find-the-duplicate-number/ + * Time O(N) | Space O(1) + * @param {number[]} nums + * @return {number} + */ + var findDuplicate = function(nums, start = 0) { + const swap = (arr, a, b) => [arr[a], arr[b]] = [arr[b], arr[a]]; + + const isSame = () => nums[start] === nums[nums[start]]; + while (!isSame()) {/* Time O(N) */ + swap(nums, start, nums[start]); + } + + return nums[start]; +} + +/** + * https://leetcode.com/problems/find-the-duplicate-number/ + * Time O(N) | Space O(1) + * @param {number[]} nums + * @return {number} + */ + var findDuplicate = function(nums) { + if (!nums.length) return -1 + + let [ slow, fast ] = moveFast(nums); /* Time O(N) */ + [ slow, fast ] = moveSlow(nums, slow, fast);/* Time O(N) */ + + return slow; +}; + +const moveFast = (nums, start = 0) => { + let [ slow, fast ] = [ nums[start], nums[nums[start]] ]; + + const isSame = () => slow === fast; + while (!isSame()) { /* Time O(N) */ + slow = nums[slow]; + fast = nums[nums[fast]]; + } + + fast = start; + + return [ slow, fast ]; +} + +const moveSlow = (nums, slow, fast) => { + const isSame = () => slow === fast; + while (!isSame()) { /* Time O(N) */ + slow = nums[slow]; + fast = nums[fast]; + } + + return [ slow, fast ]; +} \ No newline at end of file diff --git a/javascript/0290-word-pattern.js b/javascript/0290-word-pattern.js new file mode 100644 index 000000000..94787dd98 --- /dev/null +++ b/javascript/0290-word-pattern.js @@ -0,0 +1,27 @@ +// problem link https://leetcode.com/problems/word-pattern +// time coplexity O(n) +// space complexity O(n) + +var wordPattern = function(pattern, s) { + +s = s.split(' '); + +if(s.length !== pattern.length) return false; + +wordToChar = new Map(); +charToWord = new Map(); + +for(let i = 0; i < pattern.length; i++) { + wordToChar.set(s[i], pattern[i]); + charToWord.set(pattern[i], s[i]); +}; + + +for(let i = 0; i < pattern.length; i++) { + if(charToWord.get(pattern[i]) !== s[i] || pattern[i] !== wordToChar.get(s[i])) { + return false; + } +} + +return true; +}; diff --git a/javascript/0295-find-median-from-data-stream.js b/javascript/0295-find-median-from-data-stream.js new file mode 100644 index 000000000..492039ff9 --- /dev/null +++ b/javascript/0295-find-median-from-data-stream.js @@ -0,0 +1,56 @@ +/** + * https://leetcode.com/problems/find-median-from-data-stream/ + * Your MedianFinder object will be instantiated and called as such: + * var obj = new MedianFinder() + * obj.addNum(num) + * var param_2 = obj.findMedian() + */ +class MedianFinder { + constructor () { + this.maxHeap = new MaxPriorityQueue() + this.minHeap = new MinPriorityQueue() + } + + /* Time O(log(N)) | Space (N) */ + insertNum (num) { + this.addNum(num) + } + + addNum (num, heap = this.getHeap(num)) { + heap.enqueue(num) + this.rebalance() + } + + getHeap (num, { maxHeap, minHeap } = this) { + const isFirst = maxHeap.isEmpty() + const isGreater = num <= this.top(maxHeap); + const isMaxHeap = (isFirst || isGreater); + return (isMaxHeap) + ? maxHeap + : minHeap + } + + rebalance ({ maxHeap, minHeap } = this) { + const canShiftMax = (minHeap.size() + 1) < maxHeap.size() + if (canShiftMax) return minHeap.enqueue(maxHeap.dequeue().element) + + const canShiftMin = maxHeap.size() < minHeap.size() + if (canShiftMin) return maxHeap.enqueue(minHeap.dequeue().element) + } + + /* Time O(1) | Space (1) */ + findMedian ({ maxHeap, minHeap } = this) { + const isEven = maxHeap.size() === minHeap.size() + return (isEven) + ? this.average(maxHeap, minHeap) + : this.top(maxHeap) + } + + average (maxHeap, minHeap) { + return (this.top(maxHeap) + this.top(minHeap)) / 2 + } + + top (heap) { + return heap.front()?.element || 0 + } +} diff --git a/javascript/0297-serialize-and-deserialize-binary-tree.js b/javascript/0297-serialize-and-deserialize-binary-tree.js new file mode 100644 index 000000000..96c3341de --- /dev/null +++ b/javascript/0297-serialize-and-deserialize-binary-tree.js @@ -0,0 +1,85 @@ +/** + * Encodes a tree to a single string. + * https://leetcode.com/problems/serialize-and-deserialize-binary-tree/solution/ + * Time O(N) | Space O(H) + * @param {TreeNode} root + * @return {string} + */ + var serialize = function(root, result = []) { + serial(root, result); + + return result; +}; + +const serial = (root, result) => { + const isBase = root === null; + if (isBase) return result.push(null); + + dfsSerialize(root, result); +} + +const dfsSerialize = (node, result) => { + result.push(node.val); + serial(node.left, result); + serial(node.right, result); +}; + +/** + * Encodes a tree to a single string. + * https://leetcode.com/problems/serialize-and-deserialize-binary-tree/solution/ + * Time O(N) | Space O(H) + * @param {TreeNode} root + * @return {string} + */ +var serialize = function(root) { + const isBaseCase = root === null; + if (isBaseCase) return [ null ]; + + return dfsSerializeIterative([ root ]); +}; + +const dfsSerializeIterative = (stack, result = []) => { + while (stack.length) { + const curr = stack.pop(); + + const isNull = curr === null; + if (isNull) { + result.push(null); + continue; + } + + result.push(curr.val); + stack.push(curr.right); + stack.push(curr.left); + } + + return result; +} + +/** +* Decodes your encoded data to tree. +* https://leetcode.com/problems/serialize-and-deserialize-binary-tree/solution/ +* Time O(N) | Space O(H) +* @param {string} data +* @return {TreeNode} +*/ +var deserialize = function(data) { + const isBaseCase = !data.length; + if (isBaseCase) return null; + + const val = data.shift(); + + const isNull = val === null; + if (isNull) return null; + + return dfsDeserialize(val, data) +}; + +const dfsDeserialize = (val, data) => { + const node = new TreeNode(val); + + node.left = deserialize(data); + node.right = deserialize(data); + + return node; +} diff --git a/javascript/0300-longest-increasing-subsequence.js b/javascript/0300-longest-increasing-subsequence.js new file mode 100644 index 000000000..ad4720dd5 --- /dev/null +++ b/javascript/0300-longest-increasing-subsequence.js @@ -0,0 +1,113 @@ +/** + * DP - Bottom Up + * Array - Tabulation + * Time O(N^2) | Space O(N) + * https://leetcode.com/problems/longest-increasing-subsequence/ + * @param {number[]} nums + * @return {number} + */ +var lengthOfLIS = (nums) => { + const tabu = initTabu(nums);/* | Space O(N) */ + + linearSearch(nums, tabu); /* Time O(N * N) | Space O(N)*/ + + return Math.max(...tabu); /* Time O(N) */ +}; + +const initTabu = (nums) => new Array(nums.length).fill(1); + +var linearSearch = (nums, tabu) => { + for (let right = 1; (right < nums.length); right++) {/* Time O(N) */ + for (let left = 0; (left < right); left++) { /* Time O(N) */ + const canUpdate = nums[left] < nums[right]; + if (!canUpdate) continue; + + const [ _left, _right ] = [ (tabu[left] + 1), tabu[right] ]; + tabu[right] = Math.max(_right, _left); /* Space O(N) */ + } + } +} + +/** + * Array - Subsequence + * Time O(N^2) | Space O(N) + * https://leetcode.com/problems/longest-increasing-subsequence/ + * @param {number[]} nums + * @return {number} + */ +var lengthOfLIS = (nums) => { + const subsequence = linearSort(nums);/* Time O(N * N) | Space O(N) */ + + return subsequence.length; +} + +var linearSort = (nums, subsequence = []) => { + for (const num of nums) {/* Time O(N) */ + const max = subsequence[subsequence.length - 1]; + + const canAdd = max < num; + if (canAdd) { subsequence.push(num); continue; }/* Space O(N) */ + + const index = getMax(subsequence, num); /* Time O(N) */ + + subsequence[index] = num; + } + + return subsequence; +} + + +const getMax = (subsequence, num, index = 0) => { + const isLess = () => subsequence[index] < num; + while (isLess()) index++;/* Time O(N) */ + + return index; +} + +/** + * Array - Subsequence + * Time O(N * log(N)) | Space O(N) + * https://leetcode.com/problems/longest-increasing-subsequence/ + * @param {number[]} nums + * @return {number} + */ +var lengthOfLIS = (nums) => { + const subsequence = logarithmicSort(nums);/* Time O(N * log(N) */ + + return subsequence.length; +} + +var logarithmicSort = (nums, subsequence = []) => { + for (const num of nums) {/* Time O(N) */ + const max = subsequence[(subsequence.length - 1)]; + + const canAdd = (max < num); + if (canAdd) { subsequence.push(num); continue; }/* Space O(N) */ + + const index = binarySearch(num, subsequence); /* Time O(log(N)) */ + + subsequence[index] = num; + } + + return subsequence; +} + +const binarySearch = (num, subsequence) => { + let [ left, right ] = [ 0, (subsequence.length - 1) ]; + + while (left < right) {/* Time O(log(N)) */ + const mid = ((left + right) >> 1); + const guess = subsequence[mid]; + + const isNumTarget = (num === guess); + if (isNumTarget) return mid; + + const isNumGreater = (guess < num); + if (isNumGreater) left = (mid + 1); + + const isNumLess = (num < guess); + if (isNumLess) right = mid; + } + + return left; +} \ No newline at end of file diff --git a/javascript/0303-range-sum-query-immutable.js b/javascript/0303-range-sum-query-immutable.js new file mode 100644 index 000000000..a09d46b69 --- /dev/null +++ b/javascript/0303-range-sum-query-immutable.js @@ -0,0 +1,29 @@ +/** + * https://leetcode.com/problems/range-sum-query-immutable/ + * @param {number[]} nums + */ +class NumArray { + constructor(nums) { + this.arr = nums; + } + + /** + * Time O(n) | Space O(1) + * @param {number} left + * @param {number} right + * @return {number} + */ + sumRange(left, right) { + let total = 0; + for (let i = left; i < right + 1; i++) { + total += this.arr[i]; + } + return total; + } +} + +/** + * Your NumArray object will be instantiated and called as such: + * var obj = new NumArray(nums) + * var param_1 = obj.sumRange(left,right) + */ diff --git a/javascript/0304-range-sum-query-2d-immutable.js b/javascript/0304-range-sum-query-2d-immutable.js new file mode 100644 index 000000000..2319ae5af --- /dev/null +++ b/javascript/0304-range-sum-query-2d-immutable.js @@ -0,0 +1,36 @@ +/** + * https://leetcode.com/problems/range-sum-query-2d-immutable/ + * @class NumMatrix + * @param {number[][]} matrix + */ +class NumMatrix { + constructor(matrix) { + this.matrix = matrix; + } + + /** + * + * m = row2 - row1; n = col2 - col1 + * Time O(m*n) | Space O(1) + * @param {number} row1 + * @param {number} col1 + * @param {number} row2 + * @param {number} col2 + * @return {number} + */ + sumRegion(row1, col1, row2, col2) { + let sum = 0; + for (let i = row1; i < row2 + 1; i++) { + for (let j = col1; j < col2 + 1; j++) { + sum += this.matrix[i][j]; + } + } + return sum; + } +} + +/** + * Your NumMatrix object will be instantiated and called as such: + * var obj = new NumMatrix(matrix) + * var param_1 = obj.sumRegion(row1,col1,row2,col2) + */ diff --git a/javascript/0309-best-time-to-buy-and-sell-stock-with-cooldown.js b/javascript/0309-best-time-to-buy-and-sell-stock-with-cooldown.js new file mode 100644 index 000000000..ad34f2512 --- /dev/null +++ b/javascript/0309-best-time-to-buy-and-sell-stock-with-cooldown.js @@ -0,0 +1,63 @@ +/** + * Greedy - State Machine + * Time O(N) | Space O(1) + * https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/ + * @param {number[]} prices + * @return {number} + */ + var maxProfit = (prices) => { + let [ sold, held, reset ] = [ (-Infinity), (-Infinity), 0 ]; + + [ sold, reset ] = search(prices, sold, held, reset);/* Time O(N) */ + + return Math.max(sold, reset); +} + +var search = (prices, sold, held, reset) => { + for (const price of prices) {/* Time O(N) */ + const preSold = sold; + + sold = (held + price); + held = Math.max(held, (reset - price)); + reset = Math.max(reset, preSold); + } + + return [ sold, reset ]; +} + +/** + * DP - Bottom Up + * Array - Tabulation + * Time O(N^2) | Space O(N) + * https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/ + * @param {number[]} prices + * @return {number} + */ +var maxProfit = (prices) => { + const tabu = initTabu(prices);/* Space O(N) */ + + search(prices, tabu);/* Time O(N * N) */ + + return tabu[0]; +} + +var initTabu = (prices) => new Array(prices.length + 2).fill(0); + +var search = (prices, tabu) => { + for (let i = (prices.length - 1); (0 <= i); i--) {/* Time O(N) */ + const prev = buyAndSell(prices, i, tabu); /* Time O(N) */ + const next = tabu[i + 1]; + + tabu[i] = Math.max(prev, next); /* Space O(N) */ + } +} + +const buyAndSell = (prices, i, tabu, max = 0) => { + for (let sell = (i + 1); (sell < prices.length); sell++) {/* Time O(N) */ + const profit = ((prices[sell] - prices[i]) + tabu[(sell + 2)]); + + max = Math.max(max, profit); + } + + return max; +} \ No newline at end of file diff --git a/javascript/0312-burst-balloons.js b/javascript/0312-burst-balloons.js new file mode 100644 index 000000000..7dd519e09 --- /dev/null +++ b/javascript/0312-burst-balloons.js @@ -0,0 +1,75 @@ +/** + * DP - Top Down + * Matrix - Memoization + * Time O(N^3) | Space O(N^2) + * https://leetcode.com/problems/burst-balloons/ + * @param {number[]} nums + * @return {number} + */ + var maxCoins = (nums) => { + const _nums = [ 1, ...nums, 1 ];/* Time O(N) | Space O(N) */ + + return search(_nums); /* Time O(N * N * N) | Space O((N * N) + HEIGHT) */ +} + +var search = (nums, left = 1 , right = (nums.length - 2), memo = initMemo(nums)) => { + const isBaseCase = (right - left < 0); + if (isBaseCase) return 0; + + const hasSeen = (memo[left][right] !== -1); + if (hasSeen) return memo[left][right]; + + return dfs(nums, left, right, memo);/* Time O(N * N * N) | Space O((N * N) + HEIGHT) */ +} + +var initMemo = (nums) => new Array(nums.length).fill()/* Time O(N) | Space O(N) */ + .map(() => new Array(nums.length).fill(-1)); /* Time O(N) | Space O(N) */ + +var dfs = (nums, left, right, memo, result = 0) => { + for (let i = left; (i <= right); i++) {/* Time O(N) */ + const gain = ((nums[left - 1] * nums[i]) * nums[right + 1]); + const _left = search(nums, left, (i - 1), memo); /* Time O(N * N) | Space O(HEIGHT) */ + const _right = search(nums, (i + 1), right, memo);/* Time O(N * N) | Space O(HEIGHT) */ + const remaining = (_left + _right); + + result = Math.max(result, remaining + gain); + } + + memo[left][right] = result; /* | Space O(N * N) */ + return result; +} + +/** + * DP - Bottom Up + * Matrix - Tabulation + * Time O(N^3) | Space O(N^2) + * https://leetcode.com/problems/burst-balloons/ + * @param {number[]} nums + * @return {number} + */ + var maxCoins = (nums) => { + const tabu = initTabu(nums);/* Time O(N * N) | Space O(N * N) */ + + search(nums, tabu); /* Time O(N * N * N) | Space O(N * N) */ + + return tabu[1][(nums.length)]; +} + +var initTabu = (nums) => new Array(nums.length + 2).fill()/* Time O(N) | Space O(N) */ + .map(() => new Array(nums.length + 2).fill(0)) /* Time O(N) | Space O(N) */ + +var search = (nums, tabu) => { + const _nums = [ 1, ...nums, 1 ]; /* Time O(N) | Space O(N) */ + + for (let left = nums.length; (1 <= left); left--) { /* Time O(N) */ + for (let right = left; (right <= nums.length); right++) {/* Time O(N) */ + for (let i = left; (i <= right); i++) { + const gain = ((_nums[left - 1] * _nums[i]) * _nums[right + 1]); + const remaining = (tabu[left][i - 1] + tabu[i + 1][right]); + + tabu[left][right] = /* | Space O(N * N) */ + Math.max(remaining + gain, tabu[left][right]); + } + } + } +} \ No newline at end of file diff --git a/javascript/0322-coin-change.js b/javascript/0322-coin-change.js new file mode 100644 index 000000000..3e8d8a05a --- /dev/null +++ b/javascript/0322-coin-change.js @@ -0,0 +1,118 @@ +/** + * Brute Force - DFS + * Time O(S^N) | Space O(N) + * https://leetcode.com/problems/coin-change/ + * @param {number[]} coins + * @param {number} amount + * @return {number} + */ + var coinChange = (coins, amount, coin = 0) => { + const isBaseCase1 = amount === 0; + if (isBaseCase1) return 0; + + const isBaseCase2 = !((coin < coins.length) && (0 < amount)); + if (isBaseCase2) return -1; + + return dfs(coins, amount, coin);/* Time O(S^N) | Space O(N) */ +} + +var dfs = (coins, amount, coin) => { + let [ max, minCost ] = [ (amount / coins[coin]), Infinity ]; + + for (let num = 0; num <= max; num++) {/* Time O(N) */ + const caUpdate = ((num * coins[coin]) <= amount); + if (!caUpdate) continue; + + const product = (num * coins[coin]); + const difference = amount - product; + const min = coinChange(coins, difference, (coin + 1));/* Time O(S^N) | Space O(N) */ + const cost = (min + num); + + const isSentinel = (min === -1); + if (isSentinel) continue; + + minCost = Math.min(minCost, cost); + } + + return (minCost !== Infinity) + ? minCost + : -1; +} + +/** + * DP - Top Down + * Array - Memoization + * Time O(N) | Space O(N) + * https://leetcode.com/problems/coin-change/ + * @param {number[]} coins + * @param {number} amount + * @return {number} + */ + var coinChange = (coins, amount, memo = initMemo(amount)) => { + const isBaseCase1 = (amount < 0); + if (isBaseCase1) return -1; + + const isBaseCase2 = (amount < 1); + if (isBaseCase2) return 0; + + const isBaseCase3 = (memo[amount - 1] !== 0); + if (isBaseCase3) return memo[amount - 1]; + + return dfs(coins, amount, memo);/* Time O(N) | Space O(N) */ +} + +const initMemo = (amount) => Array(amount).fill(0); + +var dfs = (coins, amount, memo, min = Infinity) => { + for (const coin of coins) { /* Time O(N) */ + const cost = coinChange(coins, (amount - coin), memo);/* Time O(N) | Space O(N) */ + + const canUpdate = ((0 <= cost) && (cost < min)); + if (!canUpdate) continue; + + min = (cost + 1); + } + + memo[amount - 1] = (min !== Infinity) + ? min + : -1; + + return memo[amount - 1]; +} + +/** + * DP - Bottom Up + * Array - Tabulation + * Time O(N) | Space O(N) + * https://leetcode.com/problems/coin-change/ + * @param {number[]} coins + * @param {number} amount + * @return {number} + */ +var coinChange = (coins, amount) => { + const tabu = initTabu(amount); + + for (let _amount = 1; _amount <= amount; _amount++) {/* Time O(N) */ + for (let coin = 0; coin < coins.length; coin++) { /* Time O(N) */ + const canUpdate = (coins[coin] <= _amount); + if (!canUpdate) continue; + + const difference = (_amount - coins[coin]); + const min = (tabu[difference] + 1); + + tabu[_amount] = Math.min(tabu[_amount], min); /* Space O(N) */ + } + } + + return (tabu[amount] <= amount) + ? tabu[amount] + : -1; +} + +const initTabu = (amount) => { + const tabu = Array((amount + 1)).fill((amount + 1)); + + tabu[0] = 0; + + return tabu; +} \ No newline at end of file diff --git a/javascript/0323-number-of-connected-components-in-an-undirected-graph.js b/javascript/0323-number-of-connected-components-in-an-undirected-graph.js new file mode 100644 index 000000000..d578f548f --- /dev/null +++ b/javascript/0323-number-of-connected-components-in-an-undirected-graph.js @@ -0,0 +1,111 @@ +/** + * https://leetcode.com/problems/number-of-connected-components-in-an-undirected-graph/ + * Time O(V + E) | Space O(V + E) + * @param {number} n + * @param {number[][]} edges + * @return {number} + */ +var countComponents = function (n, edges, count = 0) { + const { graph, visited } = buildGraph(n, edges); + + for (const node in graph) { + if (hasPath(graph, node, visited)) count++; + } + + return count; +}; + +const initGraph = (n) => ({ + graph: new Array(n).fill().map(() => []), + visited: new Array(n).fill(false), +}); + +const buildGraph = (n, edges) => { + const { graph, visited } = initGraph(n) + + for (const [ src, dst ] of edges) { + graph[src].push(dst); + graph[dst].push(src); + } + + return { graph, visited }; +} + +const hasPath = (graph, current, visited) => { + if (visited[current]) return false; + visited[current] = current; + + for (const neighbor of graph[current]) { + hasPath(graph, neighbor, visited); + } + + return true; +} + +/** + * https://leetcode.com/problems/number-of-connected-components-in-an-undirected-graph/ + * Time O(E * a(N)) | Space O(V) + * @param {number} n + * @param {number[][]} edges + * @return {number} + */ + var countComponents = function(n, edges) { + return new UnionFind(n, edges) + .connectedComponents; +}; + +class UnionFind { + constructor (n, edges) { + this.parent = new Array(n).fill().map((_, index) => index), + this.rank = new Array(n).fill(1) + this.connectedComponents = n; + + this.search(edges); + } + + search (edges) { + for (const [ src, dst ] of edges) { + this.union(src, dst) + } + } + + find (head, tail = head, { parent } = this) { + const isEqual = () => head === parent[head] + while (!isEqual()) { + head = parent[head]; + } + + this.compress(tail, head); + + return head + } + + compress (tail, head, { parent } = this) { + parent[tail] = head; + } + + increaseRank (head, tail, { rank } = this) { + rank[head] += rank[tail]; + } + + union (src, dst, { rank } = this) { + const [ rootSrc, rootDst ] = [ this.find(src), this.find(dst) ] + + const hasCycle = rootSrc === rootDst + if (hasCycle) return + + this.connectedComponents--; + + const isGreater = rank[rootSrc] < rank[rootDst] + if (isGreater) { + this.increaseRank(rootDst, rootSrc) + this.compress(rootSrc, rootDst) + } + + const isLess = rank[rootDst] <= rank[rootSrc] + if (isLess) { + this.increaseRank(rootSrc, rootDst) + this.compress(rootDst, rootSrc) + } + } +} diff --git a/javascript/0329-longest-increasing-path-in-a-matrix.js b/javascript/0329-longest-increasing-path-in-a-matrix.js new file mode 100644 index 000000000..f59d674ea --- /dev/null +++ b/javascript/0329-longest-increasing-path-in-a-matrix.js @@ -0,0 +1,176 @@ +/** + * Brute Force - DFS + * Time O(2^(N + M)) | Space O(N * M) + * https://leetcode.com/problems/longest-increasing-path-in-a-matrix/ + * @param {number[][]} matrix + * @return {number} + */ + var longestIncreasingPath = (matrix, maxPath = 0) => { + const [ rows, cols ] = [ matrix.length, matrix[0].length ]; + + for (let row = 0; (row < rows); row++) {/* Time O(N) */ + for (let col = 0; (col < cols); col++) {/* Time O(M) */ + const path = dfs(matrix, row, rows, col, cols);/* Time O(2^(N + M)) | Space O(HEIGHT) */ + + maxPath = Math.max(maxPath, path); + } + } + + return maxPath; +} + +var dfs = (matrix, row, rows, col, cols, ans = 0) => { + for (const [ _row, _col ] of getNeighbors(row, rows, col, cols)) {/* Time O(4) */ + const path = dfs(matrix, _row, rows, _col, cols); /* Time O(2^(N + M)) | Space O(HEIGHT) */ + + ans = Math.max(ans, path); + } + + ans += 1; + return ans; +} + +var getNeighbors = (row, rows, col, cols) => [ [ 0, 1 ], [ 0, -1 ], [ 1, 0 ], [ -1, 0 ] ] + .map(([ _row, _col ]) => [ (row + _row), (col + _col) ]) + .filter(([ _row, _col ]) => (0 <= _row) && (_row < rows) && (0 <= _col) && (_col < cols)); + +/** + * DP - Top Down + * Matrix - Memoization + * Time O(N * M) | Space O(N * M) + * https://leetcode.com/problems/longest-increasing-path-in-a-matrix/ + * @param {number[][]} matrix + * @return {number} + */ + var longestIncreasingPath = (matrix, maxPath = 0, memo = initMemo(matrix)) => { + const [ rows, cols ] = [ matrix.length, matrix[0].length ]; + + for (let row = 0; row < rows; row++) {/* Time O(N) */ + for (let col = 0; col < cols; col++) {/* Time O(M) */ + const path = /* Time O(N * M) | Space O((N * M) + HEIGHT) */ + search(matrix, row, rows, col, cols, memo); + + maxPath = Math.max(maxPath, path); + } + } + + return maxPath; +}; + +var initMemo = (matrix) => new Array(matrix.length).fill()/* Time O(N) | Space O(N)*/ + .map(() => new Array(matrix[0].length).fill(0)); /* Time O(M) | Space O(M)*/ + +const search = (matrix, row, rows, col, cols, memo) => { + const hasSeen = (memo[row][col] !== 0) + if (hasSeen) return memo[row][col]; + + return dfs(matrix, row, rows, col, cols, memo);/* Time O(N * M) | Space O((N * M) + HEIGHT) */ +} + +var dfs = (matrix, row, rows, col, cols, memo) => { + for (const [ _row, _col ] of getNeighbors(row, rows, col, cols)) {/* Time O(4) */ + const [ parent, node ] = [ matrix[row][col], matrix[_row][_col] ]; + + const isLess = (node <= parent); + if (isLess) continue; + + const path = search(matrix, _row, rows, _col, cols, memo); /* Time O(N * M) | Space O(HEIGHT) */ + + memo[row][col] = Math.max(memo[row][col], path); + } + + memo[row][col] += 1; /* | Space O(N * M) */ + return memo[row][col]; +} + +var getNeighbors = (row, rows, col, cols) => [ [ 0, 1 ], [ 0, -1 ], [ 1, 0 ], [ -1, 0 ] ] + .map(([ _row, _col ]) => [ (row + _row), (col + _col) ]) + .filter(([ _row, _col ]) => (0 <= _row) && (_row < rows) && (0 <= _col) && (_col < cols)); + +/** + * Topological Sort + * Matrix - Graph + * Matrix - In-Degree + * Queue - BFS + * Time O(N * M) | Space O(N * M) + * https://leetcode.com/problems/longest-increasing-path-in-a-matrix/ + * @param {number[][]} matrix + * @return {number} + */ +var longestIncreasingPath = (matrix) => { + const { graph, indegree, sources } = /* Time O(N * M) | Space O(N * M) */ + buildGraph(matrix); + + findSources(graph, indegree, sources);/* Time O(N * M) | Space O(N * M) */ + + return bfs(graph, indegree, sources); /* Time O((N * M) + WIDTH) | Space O((N * M) + WIDTH) */ +} + +const initGraph = (rows, cols) => ({ + graph: new Array((rows + 2)).fill() /* Time O(N) | Space O(N) */ + .map(() => new Array((cols + 2)).fill(0)),/* Time O(M) | Space O(M) */ + indegree: new Array((rows + 2)).fill() /* Time O(N) | Space O(N) */ + .map(() => new Array((cols + 2)).fill(0)),/* Time O(M) | Space O(M) */ + sources: new Queue() +}) + +var buildGraph = (matrix) => { + const [ rows, cols ] = [ matrix.length, matrix[0].length ]; + const { graph, indegree, sources } = /* Time O(N * M) | Space O(N * M) */ + initGraph(rows, cols); + + for (let row = 1; (row < (rows + 1)); row++) {/* Time O(N) */ + graph[row] = [ 0, ...matrix[row - 1], 0 ]; /* | Space O(N * M) */ + } + + for (let row = 1; (row <= rows); row++) { /* Time O(N) */ + for (let col = 1; (col <= cols); col++) { /* Time O(M) */ + for (const [ _row, _col ] of getNeighbors(row, col)) {/* Time O(4) */ + const isSink = (graph[row][col] < graph[_row][_col]); + if (isSink) indegree[row][col] += 1; /* | Space O(N * M) */ + } + } + } + + return { graph, indegree, sources }; +} + +var getNeighbors = (row, col) => [ [ 0, 1 ], [ 0, -1 ], [ 1, 0 ], [ -1, 0 ] ] + .map(([ _row, _col ]) => [ (row + _row), (col + _col) ]); + +var findSources = (graph, indegree, sources) => { + const [ rows, cols ] = [ graph.length, graph[0].length ]; + + for (let row = 1; (row < (rows - 1)); ++row) {/* Time O(N) */ + for (let col = 1; (col < (cols - 1)); ++col) {/* Time O(M) */ + const isSource = (indegree[row][col] === 0); + if (isSource) sources.enqueue([ row, col ]); /* Space O(N * M) */ + } + } +} + +const bfs = (graph, indegree, sources, path = 0) => { + while (!sources.isEmpty()) { /* Time(N * M) */ + for (let level = (sources.size() - 1); 0 <= level; level--) {/* Time(WIDTH) */ + checkNeighbors(graph, indegree, sources) /* Space((N * M) + WIDTH) */ + } + + path += 1; + } + + return path; +} + +const checkNeighbors = (graph, indegree, sources) => { + const [ row, col ] = sources.dequeue(); + + for (const [ _row, _col ] of getNeighbors(row, col)) { + const canDisconnect = (graph[_row][_col] < graph[row][col]); + if (!canDisconnect) continue; + + indegree[_row][_col] -= 1; /* Space O(N * M) */ + + const isSource = (indegree[_row][_col] === 0); + if (isSource) sources.enqueue([ _row, _col ]);/* Space O(WIDTH) */ + } +} \ No newline at end of file diff --git a/javascript/0332-reconstruct-itinerary.js b/javascript/0332-reconstruct-itinerary.js new file mode 100644 index 000000000..f198f3af6 --- /dev/null +++ b/javascript/0332-reconstruct-itinerary.js @@ -0,0 +1,46 @@ +/** + * https://leetcode.com/problems/reconstruct-itinerary/ + * @param {string[][]} tickets + * @return {string[]} + */ +var findItinerary = (tickets) => { + tickets.sort(); + + const graph = buildGraph(tickets); + + return dfs(tickets, graph); +}; + +const dfs = (tickets, graph, city = 'JFK', path = ['JFK']) => { + const isBaseCase = (path.length === (tickets.length + 1)); + if (isBaseCase) return true; + + const queue = (graph.get(city) || []); + + const isEmpty = (queue.length === 0); + if (isEmpty) return false; + + for (const nextCity of queue.slice()) { + path.push(nextCity); + queue.shift(); + + if (dfs(tickets, graph, nextCity, path)) return path; + + path.pop(); + queue.push(nextCity); + } + + return false; +}; + + +const buildGraph = (tickets, graph = new Map()) => { + for (const [ src, dst ] of tickets) { + const edges = (graph.get(src) || []); + + edges.push(dst); + graph.set(src, edges); + } + + return graph; +} diff --git a/javascript/0337-house-robber-iii.js b/javascript/0337-house-robber-iii.js new file mode 100644 index 000000000..34f19eada --- /dev/null +++ b/javascript/0337-house-robber-iii.js @@ -0,0 +1,30 @@ +/** + * Post Order Traversal + * Time O(n) | Space (n) + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @return {number} + */ +var rob = function(root) { + + function dfs(root) { + if(!root) return [0, 0]; + + const leftSubTree = dfs(root.left); + const rightSubTree = dfs(root.right); + + const withoutRoot = Math.max(...leftSubTree) + Math.max(...rightSubTree); + const withRoot = root.val + leftSubTree[0] + rightSubTree[0]; + + return [withoutRoot, withRoot]; + } + + return Math.max(...dfs(root)); +}; diff --git a/javascript/0338-counting-bits.js b/javascript/0338-counting-bits.js new file mode 100644 index 000000000..045d823a9 --- /dev/null +++ b/javascript/0338-counting-bits.js @@ -0,0 +1,16 @@ +/** + * https://leetcode.com/problems/counting-bits/ + * Time O(N) | Space (1) + * @param {number} n + * @return {number[]} + */ +var countBits = function (n, dp = [ 0 ]) { + for (let i = 1; i < (n + 1); i++) { + const [ mid, bit ] = [ (i >> 1), (i & 1) ] + const bits = (dp[mid] + bit) + + dp.push(bits); + } + + return dp; +}; diff --git a/javascript/0344-reverse-string.js b/javascript/0344-reverse-string.js new file mode 100644 index 000000000..df84a10ed --- /dev/null +++ b/javascript/0344-reverse-string.js @@ -0,0 +1,16 @@ +/** + * @param {character[]} s + * @return {void} Do not return anything, modify s in-place instead. + */ + var reverseString = function(s) { + let i = 0, j = s.length-1; + + while(i <= j) { + let leftval = s[i], rightval = s[j]; + s[i] = rightval; + s[j] = leftval; + + i++; + j--; + } +}; diff --git a/javascript/0347-top-k-frequent-elements.js b/javascript/0347-top-k-frequent-elements.js new file mode 100644 index 000000000..9813bb81b --- /dev/null +++ b/javascript/0347-top-k-frequent-elements.js @@ -0,0 +1,64 @@ +/** + * Set - Frequency Counter | Using sort + * Time O(NlogN) | Space O(N) + * https://leetcode.com/problems/top-k-frequent-elements/ + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ +var topKFrequent = function(nums, k) { + let frequency = {} + for( let i = 0; i < nums.length; i++){ + if(frequency.hasOwnProperty(nums[i])) frequency[nums[i]] += 1; + else frequency[nums[i]] = 1; + } + let result = Object.keys(frequency).map((key) => [Number(key), frequency[key]]); + let sortedResult = result.sort((a,b) => { + return b[1]-a[1] + }) + let output = [] + for ( let i = 0; i < k; i++){ + output.push(sortedResult[i][0]) + } + return output; +}; + +/** + * Without Sort + * Time O(N) | Space O(k) + * https://leetcode.com/problems/top-k-frequent-elements/ + * @param {number[]} nums + * @param {number} k + * @return {number[]} + */ + +var topKFrequent = function(nums, k) { + const mp = new Map(); + const arr = new Array(nums.length + 1).fill(0); + const ans = []; + + nums.forEach(el => { + const val = mp.get(el) || 0; + mp.set(el, val + 1); + }); + + for ( let [key, value] of mp ) { + const prev = arr[value] || []; + prev.push(key); + arr[value] = prev; + } + + + arr.reverse(); + for (let el of arr) { + if (k < 1) break; + if (el) { + for (let el2 of el) { + ans.push(el2); + k--; + } + } + } + + return ans; +}; diff --git a/javascript/0352-data-stream-as-disjoint-intervals.js b/javascript/0352-data-stream-as-disjoint-intervals.js new file mode 100644 index 000000000..3cd7f7717 --- /dev/null +++ b/javascript/0352-data-stream-as-disjoint-intervals.js @@ -0,0 +1,31 @@ +class SummaryRanges { + constructor() { + this.numSet = new Set(); + } + + addNum(value) { + this.numSet.add(value); + } + + getIntervals() { + let nums = Array.from(this.numSet.keys()); + nums.sort((a, b) => a - b); + + let res = []; + + let i = 0; + + while (i < nums.length) { + let start = nums[i]; + + while (i + 1 < nums.length && nums[i] + 1 == nums[i + 1]) { + i++; + } + + res.push([start, nums[i]]); + i++; + } + + return res; + } +} diff --git a/javascript/0355-design-twitter.js b/javascript/0355-design-twitter.js new file mode 100644 index 000000000..d2fb3bad9 --- /dev/null +++ b/javascript/0355-design-twitter.js @@ -0,0 +1,42 @@ +/** + * https://leetcode.com/problems/design-twitter/ + * Your Twitter object will be instantiated and called as such: + * var obj = new Twitter() + * obj.postTweet(userId,tweetId) + * var param_2 = obj.getNewsFeed(userId) + * obj.follow(followerId,followeeId) + * obj.unfollow(followerId,followeeId) + */ + class Twitter { + constructor () { + this.tweets = []; + this.following = new Map(); + } + + postTweet (userId, tweetId, { tweets } = this) { + tweets.push({ authorId: userId, id: tweetId }); + } + + getNewsFeed (userId, newsIDs = [], { tweets, following } = this) { + for (let i = (tweets.length - 1); ((0 <= i) && (newsIDs.length < 10)); i--) { + const tweet = tweets[i]; + + const isAuthor = tweet.authorId === userId + const isFollowing = following?.get(userId)?.has(tweet.authorId); + const canAddTweet = isAuthor || isFollowing + if (canAddTweet) newsIDs.push(tweet.id); + } + + return newsIDs; + } + + follow (followerId, followeeId, { following } = this) { + if (!following.has(followerId)) following.set(followerId, new Set()); + + following.get(followerId).add(followeeId); + } + + unfollow (followerId, followeeId, { following } = this) { + if (following.has(followerId)) following.get(followerId).delete(followeeId); + } +} diff --git a/javascript/0367-valid-perfect-square.js b/javascript/0367-valid-perfect-square.js new file mode 100644 index 000000000..a46a49c12 --- /dev/null +++ b/javascript/0367-valid-perfect-square.js @@ -0,0 +1,17 @@ +/** + * @param {number} num + * @return {boolean} + */ + var isPerfectSquare = function(num) { + let left = 1, right = num; + + while(left <= right) { + let mid = Math.floor((left + right) / 2); + + if(mid*mid === num) return true; + else if(mid*mid > num) right = mid -1; + else left = mid + 1; + } + + return false; +}; diff --git a/javascript/0371-sum-of-two-integers.js b/javascript/0371-sum-of-two-integers.js new file mode 100644 index 000000000..1fb652a62 --- /dev/null +++ b/javascript/0371-sum-of-two-integers.js @@ -0,0 +1,17 @@ +/** + * https://leetcode.com/problems/sum-of-two-integers/ + * Time O(1) | Space O(1) + * @param {number} a + * @param {number} b + * @return {number} + */ + var getSum = function(a, b) { + while (b !== 0) { + const [ xor, carry ] = [ (a ^ b), ((a & b) << 1) ]; + + a = xor; + b = carry; + } + + return a +}; \ No newline at end of file diff --git a/javascript/0374-guess-number-higher-or-lower.js b/javascript/0374-guess-number-higher-or-lower.js new file mode 100644 index 000000000..416b61b5e --- /dev/null +++ b/javascript/0374-guess-number-higher-or-lower.js @@ -0,0 +1,15 @@ +var guessNumber = function(n) { + let low = 1; + let high = n; + + while(true) { + let mid = low + Math.floor((high - low)/2); + let myGuess = guess(mid); + if(myGuess == 1) + low = mid + 1; + else if(myGuess == -1) + high = mid - 1; + else + return mid; + } +}; diff --git a/javascript/0380-insert-delete-getrandom-o1.js b/javascript/0380-insert-delete-getrandom-o1.js new file mode 100644 index 000000000..cc9b85617 --- /dev/null +++ b/javascript/0380-insert-delete-getrandom-o1.js @@ -0,0 +1,40 @@ +var RandomizedSet = function () { + this.set = new Set(); +}; + +/** + * @param {number} val + * @return {boolean} + */ +RandomizedSet.prototype.insert = function (val) { + const res = !this.set.has(val); + if (res) { + this.set.add(val); + } + return res; +}; + +/** + * @param {number} val + * @return {boolean} + */ +RandomizedSet.prototype.remove = function (val) { + return this.set.delete(val); +}; + +/** + * @return {number} + */ +RandomizedSet.prototype.getRandom = function () { + const keys = Array.from(this.set.keys()); + const seed = Math.floor(Math.random() * keys.length); + return keys[seed]; +}; + +/** + * Your RandomizedSet object will be instantiated and called as such: + * var obj = new RandomizedSet() + * var param_1 = obj.insert(val) + * var param_2 = obj.remove(val) + * var param_3 = obj.getRandom() + */ diff --git a/javascript/0392-is-subsequence.js b/javascript/0392-is-subsequence.js new file mode 100644 index 000000000..6f8fee951 --- /dev/null +++ b/javascript/0392-is-subsequence.js @@ -0,0 +1,38 @@ +/** + * Time O(N) | Space O(1) + * @param {string} s + * @param {string} t + * @return {boolean} + */ +var isSubsequence = function(s, t) { + + if(!s.length || (s === t)) return true; + if(s.length > t.length) return false; + + let j = 0; + + for(let i = 0; i < t.length && j < s.length; i++) { + if(s[j] === t[i]) { + j++; + } + } + + return j === s.length; +}; + +/** + * Time O(N^2) | Space O(1) + * @param {string} s + * @param {string} t + * @return {boolean} + */ +var isSubsequence = function(s, t) { + for (let char of s) { + let indexChar = t.indexOf(char); + if (indexChar === -1) { + return false; + } + t = t.slice(indexChar+1); + } + return true +}; diff --git a/javascript/0402-remove-k-digits.js b/javascript/0402-remove-k-digits.js new file mode 100644 index 000000000..3cab3e74c --- /dev/null +++ b/javascript/0402-remove-k-digits.js @@ -0,0 +1,26 @@ +/** + * @param {string} num + * @param {number} k + * @return {string} + */ +var removeKdigits = function (num, k) { + let stack = []; + for (ch of num) { + while (k > 0 && stack.length > 0 && stack.at(-1) > ch) { + k--; + stack.pop(); + } + stack.push(ch); + } + + let x = 0; + while (true) { + if (stack[x] !== '0') { + break; + } + x++; + } + stack = stack.slice(x, stack.length - k); + let res = stack.join(''); + return res ? res : '0'; +}; diff --git a/javascript/0410-split-array-largest-sum.js b/javascript/0410-split-array-largest-sum.js new file mode 100644 index 000000000..32a30f03a --- /dev/null +++ b/javascript/0410-split-array-largest-sum.js @@ -0,0 +1,41 @@ +/** + * https://leetcode.com/problems/split-array-largest-sum/ + * + * Binary Search + * Time O(log(s)*n) (s = difference between the least and max possible value) | Space O(1) + * @param {number[]} nums + * @param {number} k + * @return {number} + */ +var splitArray = function(nums, k) { + + let left = Math.max(...nums); + let right = nums.reduce((acc, num) => acc + num, 0); + let result = right; + while(left <= right) { + const mid = (left + right) >> 1; + if(canSplit(mid)) { + result = mid; + right = mid - 1; + } else { + left = mid + 1; + } + } + + function canSplit(largest) { + let splitCount = 0; + let currSum = 0; + + for(let i = 0; i < nums.length; i++) { + currSum += nums[i]; + if(currSum > largest) { + currSum = nums[i]; + splitCount++; + } + } + + return splitCount + 1 <= k; + } + + return result; +}; diff --git a/javascript/0416-partition-equal-subset-sum.js b/javascript/0416-partition-equal-subset-sum.js new file mode 100644 index 000000000..0206b8c31 --- /dev/null +++ b/javascript/0416-partition-equal-subset-sum.js @@ -0,0 +1,197 @@ +/** + * Brute Force - DFS + * Time O(N^2) | Space O(N) + * https://leetcode.com/problems/partition-equal-subset-sum/ + * @param {number[]} nums + * @return {boolean} + */ + var canPartition = (nums) => { + const sum = getSum(nums);/* Time O(N) */ + const subSetSum = (sum / 2); + + const isEven = ((sum % 2) === 0); + if (!isEven) return false; + + const index = (nums.length - 1); + + return dfs(nums, index, subSetSum); +} + +var getSum = (nums, sum = 0) => { + for (const num of nums) (sum += num);/* Time O(N) */ + + return sum; +} + +var dfs = (nums, index, subSetSum) => { + const isBaseCase1 = subSetSum === 0; + if (isBaseCase1) return true; + + const isBaseCase2 = (index === 0) || (subSetSum < 0); + if (isBaseCase2) return false; + + const difference = (subSetSum - nums[(index - 1)]); + + const left = dfs(nums, (index - 1), difference); + const right = dfs(nums, (index - 1), subSetSum); + + return (left || right); +} + +/** + * DP - Top Down + * Matrix - Memo + * Time O(N * M) | Space O(N * M) + * https://leetcode.com/problems/partition-equal-subset-sum/ + * @param {number[]} nums + * @return {boolean} + */ + var canPartition = (nums) => { + const isEmpty = nums.length === 0; + if (isEmpty) return false; + + const sum = getSum(nums); /* Time O(N) */ + + const isEven = ((sum % 2) === 0); + if (!isEven) return false; + + const subSetSum = (sum >> 1); + const memo = initMemo(nums, subSetSum); /* | Space O(N * M) */ + const index = (nums.length - 1); + + return dfs(nums, index, subSetSum, memo);/* Time O(N * M) | Space O(N * M) */ +} + +var initMemo = (nums, subSetSum) => new Array((nums.length + 1)).fill()/* Space O(N) */ + .map(() => new Array((subSetSum + 1)).fill(null)); /* Space O(M) */ + +var dfs = (nums, index, subSetSum, memo) => { + const isBaseCase1 = (subSetSum === 0); + if (isBaseCase1) return true; + + const isBaseCase2 = ((index === 0) || (subSetSum < 0)); + if (isBaseCase2) return false; + + const hasSeen = (memo[index][subSetSum] !== null); + if (hasSeen) return memo[index][subSetSum]; + + const difference = (subSetSum - nums[(index - 1)]); + + const left = dfs(nums, (index - 1), difference, memo); + const right = dfs(nums, (index - 1), subSetSum, memo); + + memo[index][subSetSum] = (left || right); + return memo[index][subSetSum]; +} + +/** + * DP - Bottom Up + * Matrix - Tabulation + * Time O(N * M) | Space O(N * M) + * https://leetcode.com/problems/partition-equal-subset-sum/ + * @param {number[]} nums + * @return {boolean} + */ +var canPartition = (nums) => { + const isEmpty = nums.length === 0; + if (isEmpty) return false; + + const sum = getSum(nums); /* Time O(N) */ + + const isEven = ((sum % 2) === 0); + if (!isEven) return false; + + const subSetSum = (sum >> 1); + const tabu = initTabu(nums, subSetSum);/* | Space O(N * M) */ + + search(nums, subSetSum, tabu); + + return tabu[nums.length][subSetSum]; +} + +var getSum = (nums, sum = 0) => { + for (const num of nums) { sum += num };/* Time O(N) */ + + return sum; +} + +var initTabu = (nums, subSetSum) => { + const tabu = new Array((nums.length + 1)).fill()/* Space O(N) */ + .map(() => new Array((subSetSum + 1)).fill(false));/* Space O(M) */ + + tabu[0][0] = true; /* Space O(N * M) */ + + return tabu; +} + +var search = (nums, subSetSum, tabu) => { + for (let numIndex = 1; (numIndex <= nums.length); numIndex++) {/* Time O(N) */ + update(nums, numIndex, subSetSum, tabu); /* Time O(N) | Space O(N * M) */ + } +} + +var update = (nums, numIndex, subSetSum, tabu) => { + const num = (numIndex - 1); + const prevNum = nums[num]; + + for (let subSet = 0; subSet <= subSetSum; subSet++) {/* Time O(M) */ + const isNumGreater = (subSet < prevNum); + + tabu[numIndex][subSet] = isNumGreater /* Space O(N * M) */ + ? (tabu[num][subSet]) + : ((tabu[num][subSet]) || (tabu[num][subSet - prevNum])); + } +} + +/** + * DP - Bottom Up + * Array - Tabulation + * Time O(N * M) | Space O(M) + * https://leetcode.com/problems/partition-equal-subset-sum/ + * @param {number[]} nums + * @return {boolean} + */ +var canPartition = (nums) => { + const isEmpty = nums.length === 0; + if (isEmpty) return false; + + const sum = getSum(nums); /* Time O(N) */ + + const isEven = ((sum % 2) === 0); + if (!isEven) return false; + + const subSetSum = (sum >> 1); + const tabu = initTabu(subSetSum);/* | Space O(M) */ + + search(nums, subSetSum, tabu); /* Time O(N * M) | Space O(M) */ + + return tabu[subSetSum]; +}; + +var getSum = (nums, sum = 0) => { + for (const num of nums) { sum += num };/* Time O(N) */ + + return sum; +} + +var initTabu = (subSetSum) => { + const tabu = new Array((subSetSum + 1)).fill(false);/* Space O(M) */ + + tabu[0] = true; /* Space O(M) */ + + return tabu; +} + +var search = (nums, subSetSum, tabu) => { + for (const num of nums) {/* Time O(N) */ + update(num, subSetSum, tabu);/* Time O(M) | Space O(M) */ + } +} + +var update = (num, subSetSum, tabu) => { + for (let subSet = subSetSum; (num <= subSet); subSet--) {/* Time O(M) */ + const difference = (subSet - num); + + tabu[subSet] |= tabu[difference]; /* Space O(M) */ + } +} diff --git a/javascript/0417-pacific-atlantic-water-flow.js b/javascript/0417-pacific-atlantic-water-flow.js new file mode 100644 index 000000000..39f5c685b --- /dev/null +++ b/javascript/0417-pacific-atlantic-water-flow.js @@ -0,0 +1,163 @@ +/** + * https://leetcode.com/problems/pacific-atlantic-water-flow/ + * Time O(ROWS * COLS) | Space O(ROWS * COLS) + * @param {number[][]} heights + * @return {number[][]} + */ +var pacificAtlantic = function(heights) { + const [ pacificReachable, atlanticReachable ] = search(heights); /* Time O(ROWS * COLS) | Space O(ROWS * COLS) */ + + return searchGrid(heights, pacificReachable, atlanticReachable);/* Time O(ROWS * COLS) | Space O(ROWS * COLS) */ +}; + +var search = (heights) => { + const [ rows, cols ] = [ heights.length, heights[0].length ]; + const [ pacificReachable, atlanticReachable ] = [ getMatrix(rows, cols), getMatrix(rows, cols) ];/* Time O(ROWS * COLS) | Space O(ROWS * COLS) */ + + searchRows(heights, rows, cols, pacificReachable, atlanticReachable); + searchCols(heights, rows, cols, pacificReachable, atlanticReachable); + + return [ pacificReachable, atlanticReachable ]; +} + +var getMatrix = (rows, cols) => new Array(rows).fill()/* Time O(ROWS * COLS) | Space O(ROWS * COLS) */ + .map(() => new Array(cols).fill(false)); + +var searchRows = (heights, rows, cols, pacificReachable, atlanticReachable) => { + for (let row = 0; row < rows; row++) {/* Time O(ROWS) */ + const [ pacificStart, atlanticStart ] = [ 0, (cols - 1) ]; + + dfs(row, pacificStart, rows, cols, pacificReachable, heights); /* Space O(ROWS * COLS) */ + dfs(row, atlanticStart, rows, cols, atlanticReachable, heights); /* Space O(ROWS * COLS) */ + } +} + +var searchCols = (heights, rows, cols, pacificReachable, atlanticReachable) => { + for (let col = 0; col < cols; col++) {/* Time O(COLS) */ + const [ pacificStart, atlanticStart ] = [ 0, (rows - 1) ]; + + dfs(pacificStart, col, rows, cols, pacificReachable, heights); /* Space O(ROWS * COLS) */ + dfs(atlanticStart, col, rows, cols, atlanticReachable, heights); /* Space O(ROWS * COLS) */ + } +} + +const dfs = (row, col, rows, cols, isReachable, heights) => { + isReachable[row][col] = true; + + for (const [ _row, _col ] of getNeighbors(row, rows, col, cols)) { + if (isReachable[_row][_col]) continue; + + const isLower = heights[_row][_col] < heights[row][col]; + if (isLower) continue; + + + dfs(_row, _col, rows, cols, isReachable, heights); /* Space O(ROWS * COLS) */ + } +} + +var searchGrid = (heights, pacificReachable, atlanticReachable, intersection = []) => { + const [ rows, cols ] = [ heights.length, heights[0].length ]; + + for (let row = 0; row < rows; row++) {/* Time O(ROWS) */ + for (let col = 0; col < cols; col++) {/* Time O(COLS) */ + const isReachable = pacificReachable[row][col] && atlanticReachable[row][col] + if (!isReachable) continue + + intersection.push([ row, col ]); /* Space O(ROWS * COLS) */ + } + } + + return intersection; +} + +var getNeighbors = (row, rows, col, cols) => [ [ 0, 1 ], [ 0, -1 ], [ 1, 0 ], [ -1, 0 ] ] + .map(([ _row, _col ]) => [ (row + _row), (col + _col)]) + .filter(([ _row, _col ]) => (0 <= _row) && (_row < rows) && (0 <= _col) && (_col < cols)) + +/** + * https://leetcode.com/problems/pacific-atlantic-water-flow/ + * Time O(ROWS * COLS) | Space O(ROWS * COLS) + * @param {number[][]} heights + * @return {number[][]} + */ + var pacificAtlantic = function(heights) { + const [ pacificQueue, atlanticQueue ] = search(heights); /* Time O(ROWS + COLS) | Space O(ROWS + COLS) */ + const [ pacificReachable, atlanticReachable ] = [ bfs(heights, pacificQueue), bfs(heights, atlanticQueue) ];/* Time O(ROWS * COLS) | Space O(ROWS * COLS) */ + + return getIntersection(heights, pacificReachable, atlanticReachable); /* Time O(ROWS * COLS) | Space O(ROWS * COLS) */ +} + +var search = (heights, pacificQueue = new Queue([]), atlanticQueue = new Queue([])) => { + searchRows(heights, pacificQueue, atlanticQueue); + searchCols(heights, pacificQueue, atlanticQueue); + + return [ pacificQueue, atlanticQueue ] +} + +var searchRows = (heights, pacificQueue, atlanticQueue) => { + const [ rows, cols ] = [ heights.length, heights[0].length ]; + + for (let row = 0; row < rows; row++) {/* Time O(ROWS) */ + pacificQueue.enqueue([ row, 0 ]); /* Space O(ROWS) */ + atlanticQueue.enqueue([ row, (cols - 1) ]);/* Space O(ROWS) */ + } +} + +var searchCols = (heights, pacificQueue, atlanticQueue) => { + const [ rows, cols ] = [ heights.length, heights[0].length ]; + + for (let col = 0; col < cols; col++) {/* Time O(COLS) */ + pacificQueue.enqueue([ 0, col ]); /* Space O(COLS) */ + atlanticQueue.enqueue([ (rows - 1), col ]);/* Space O(COLS) */ + } +} + +const bfs = (heights, queue) => { + const [ rows, cols ] = [ heights.length, heights[0].length ]; + const isReachable = getMatrix(rows, cols); /* Time O(ROWS * COLS) | Space O(ROWS * COLS) */ + + while (!queue.isEmpty()) { + for (let i = (queue.size() - 1); 0 <= i; i--) {/* | Space O(WIDTH) */ + const [ row, col ] = queue.dequeue(); + + checkNeighbor(heights, row, rows, col, cols, isReachable, queue); + } + } + + return isReachable; +} + +var getMatrix = (rows, cols) => new Array(rows).fill()/* Time O(ROWS * COLS) | Space O(ROWS * COLS) */ + .map(() => new Array(cols).fill(false)); + +var checkNeighbor = (heights, row, rows, col, cols, isReachable, queue) => { + isReachable[row][col] = true; + + for (const [ _row, _col ] of getNeighbors(row, rows, col, cols)) { + if (isReachable[_row][_col]) continue; + + const isLower = heights[_row][_col] < heights[row][col]; + if (isLower) continue; + + queue.enqueue([ _row, _col ]); + } +} + +var getNeighbors = (row, rows, col, cols) => [ [ 0, 1 ], [ 0, -1 ], [ 1, 0 ], [ -1, 0 ] ] + .map(([ _row, _col ]) => [ (row + _row), (col + _col)]) + .filter(([ _row, _col ]) => (0 <= _row) && (_row < rows) && (0 <= _col) && (_col < cols)) + +const getIntersection = (heights, pacificReachable, atlanticReachable, intersection = []) => { + const [ rows, cols ] = [ heights.length, heights[0].length ]; + + for (let row = 0; row < rows; row++) {/* Time O(ROWS) */ + for (let col = 0; col < cols; col++) {/* Time O(COLS) */ + const isReachable = pacificReachable[row][col] && atlanticReachable[row][col]; + if (!isReachable) continue; + + intersection.push([ row, col ]); /* Space O(ROWS * COLS) */ + } + } + + return intersection; +} \ No newline at end of file diff --git a/javascript/0424-longest-repeating-character-replacement.js b/javascript/0424-longest-repeating-character-replacement.js new file mode 100644 index 000000000..c886fb30e --- /dev/null +++ b/javascript/0424-longest-repeating-character-replacement.js @@ -0,0 +1,78 @@ +/** + * https://leetcode.com/problems/longest-repeating-character-replacement/ + * Time O(((N + 26) * N) * (M - N)) | Space O(1) + * @param {string} s + * @param {number} k + * @return {number} + */ +var characterReplacement = function(s, k) { + let res = 0; + let count = new Map(); + let l = 0; + + for (let r = 0; r < s.length; r++) { + let len = r - l + 1 + count.set(s[r], 1 + (count.get(s[r]) || 0)) + + if ((len - Math.max(...count.values())) > k) { + count.set(s[l], count.get(s[l]) - 1) + l++; + } + len = r - l + 1; + res = Math.max(res, len) + } + + return res; +}; + +/** + * https://leetcode.com/problems/longest-repeating-character-replacement/ + * Time O(((N + 26) * N) * (M - N)) | Space O(1) + * @param {string} s + * @param {number} k + * @return {number} + */ +var characterReplacement = function (s, k) { + let [left, right, longest, max] = new Array(4).fill(0); + const frequencyMap = new Array(26).fill(0); + + while (right < s.length) { + const count = addRightFrequency(s, right, frequencyMap); + + longest = Math.max(longest, count); + + let window = right - left + 1; + const canSlide = k < window - longest; + if (canSlide) { + subtractLeftFrequency(s, left, frequencyMap); + left++; + } + + window = right - left + 1; + max = Math.max(max, window); + + right++; + } + + return max; +}; + +const addRightFrequency = (s, right, map) => { + const char = s[right]; + const index = getCode(char); + + map[index]++; + + return map[index]; +}; + +const subtractLeftFrequency = (s, left, map) => { + const char = s[left]; + const index = getCode(char); + + map[index]--; + + return map[index]; +}; + +const getCode = (char) => char.charCodeAt(0) - 'A'.charCodeAt(0); diff --git a/javascript/0435-non-overlapping-intervals.js b/javascript/0435-non-overlapping-intervals.js new file mode 100644 index 000000000..21331dfd2 --- /dev/null +++ b/javascript/0435-non-overlapping-intervals.js @@ -0,0 +1,30 @@ +/** + * https://leetcode.com/problems/non-overlapping-intervals/ + * Time O(N * logN) | Space O(1) + * @param {number[][]} intervals + * @return {number} + */ +var eraseOverlapIntervals = function (intervals) { + intervals.sort(([aStart, aEnd], [bStart, bEnd]) => + aEnd !== bEnd ? aEnd - bEnd : aStart - bStart + ); + + return getGaps(intervals); +}; + +const getGaps = (intervals, gaps = 1) => { + const prev = intervals.shift(); + + for (const curr of intervals) { + const [prevStart, prevEnd] = prev; + const [currStart, currEnd] = curr; + + const hasGap = prevEnd <= currStart; + if (!hasGap) continue; + + prev[1] = curr[1]; + gaps++; + } + + return intervals.length + 1 - gaps; +}; diff --git a/javascript/0438-find-all-anagrams-in-a-string.js b/javascript/0438-find-all-anagrams-in-a-string.js new file mode 100644 index 000000000..2a22e402d --- /dev/null +++ b/javascript/0438-find-all-anagrams-in-a-string.js @@ -0,0 +1,42 @@ +/** + * @param {string} s + * @param {string} p + * @return {number[]} + */ +var findAnagrams = function (s, p) { + const charMap = new Map(); + let matches = 0; + const matchIdx = []; + + for (const char of p) { + const charCount = charMap.get(char) || 0; + charMap.set(char, charCount + 1); + } + + let leftWindow = 0; + for (let rightWindow = 0; rightWindow < s.length; rightWindow++) { + const rightChar = s[rightWindow]; + if (charMap.has(rightChar)) { + const rightCharCount = charMap.get(rightChar); + charMap.set(rightChar, rightCharCount - 1); + if (charMap.get(rightChar) === 0) { + matches++; + } + } + if (rightWindow >= p.length) { + const leftChar = s[leftWindow]; + if (charMap.has(leftChar)) { + const leftCharCount = charMap.get(leftChar); + charMap.set(leftChar, leftCharCount + 1); + if (leftCharCount === 0) { + matches--; + } + } + leftWindow++; + } + if (matches === charMap.size) { + matchIdx.push(leftWindow); + } + } + return matchIdx; +}; diff --git a/javascript/0441-arranging-coins.js b/javascript/0441-arranging-coins.js new file mode 100644 index 000000000..67875334a --- /dev/null +++ b/javascript/0441-arranging-coins.js @@ -0,0 +1,63 @@ +/** + * https://leetcode.com/problems/arranging-coins/ + * Linear time + * Time O(n) | Space O(1) + * @param {number} n + * @return {number} + */ +var arrangeCoins = function(n) { + + let steps = 1; + let canBuild = 0; + + while(n >= steps) { + n = n - steps; + canBuild++; + steps++; + } + + return canBuild || 1; +}; + +/** + * Binary Search + * Time O(log(n)) | Space O(1) + * @param {number} n + * @return {number} + */ +var arrangeCoins = function(n) { + + let left = 1; + let right = n; + let result = 0; + + while(left <= right) { + + const mid = Math.floor((right+left)/2); + const total = (1 + mid) * (mid/2); + if(n < total) { + right = mid -1; + } else { + left = mid+1; + result = Math.max(result, mid); + } + } + + return result; + }; + +/** + * Math + * Time O(1) | Space O(1) + * @param {number} n + * @return {number} + */ +var arrangeCoins = function(n) { + let discriminant = 1 + 8 * n; + let sqrtDiscriminant = Math.sqrt(discriminant); + + let result1 = Math.floor((-1 + sqrtDiscriminant) / 2); + let result2 = Math.floor((-1 - sqrtDiscriminant) / 2); + + return Math.max(result1, result2); +}; diff --git a/javascript/0448-find-all-numbers-disappeared-in-an-array.js b/javascript/0448-find-all-numbers-disappeared-in-an-array.js new file mode 100644 index 000000000..83739d63c --- /dev/null +++ b/javascript/0448-find-all-numbers-disappeared-in-an-array.js @@ -0,0 +1,47 @@ +// problem link https://leetcode.com/problems/find-all-numbers-disappeared-in-an-array +// time complexity O(n) + +var findDisappearedNumbers = function(nums) { + + const numberSet = new Set(); + + for(let i = 1; i < nums.length + 1; i++) { + numberSet.add(i); + } + + nums.forEach((element) => { + if(numberSet.has(element)) { + numberSet.delete(element); + } + }); + + return Array.from(numberSet); +}; + +//Optimized Solution for Follow-up + +//time complexity O(n) , space complexity O(1) + +var findDisappearedNumbers = function(nums) { + for(let i = 0; i < nums.length; i++) { + let curr = Math.abs(nums[i]) + let idx = curr - 1 + if(nums[idx] > 0) { + nums[idx] = nums[idx] * (-1) + } + } + let res = [] + for(let i = 0; i < nums.length; i++) { + if(nums[i] > 0) { + res.push(i + 1) + } + } + return res +}; + +// For each value in the array mark its presence by making the number negative at that place in array +// eg. if you hae array [3,1,4,1] for 3, i will go to index 2 and make its value negative ie. now nums[2] becomes -4. present array: [3,1,-4,1] +// for 1, i will go to index 0 and make its value negative ie. now nums[0] becomes -3. present array: [-3,1,-4,1] +// for 4, (take abs value), i will go to index 3 and make its value negative ie. now nums[3] becomes -1. present array: [-3,1,-4,-1] +// for 1 take abs value), i will go to index 0 as it is already -ve do nothing. present array: [-3,1,-4,-1] +// At last I will have [-3,1,-4,-1]. now i will iterate over the array, whichever idx has positive value that number will not be in the array so as we have nums[1]>0 so 2 is not in the list. diff --git a/javascript/0450-delete-node-in-a-bst.js b/javascript/0450-delete-node-in-a-bst.js new file mode 100644 index 000000000..8e2d7bb85 --- /dev/null +++ b/javascript/0450-delete-node-in-a-bst.js @@ -0,0 +1,42 @@ +/** + * Recursion + * h = height of the tree; + * Time O(h) | Space O(h) + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @param {number} key + * @return {TreeNode} + */ +var deleteNode = function(root, key) { + if (!root) return root; + + if (key === root.val) { + if (!root.left) return root.right; + if (!root.right) return root.left; + + // find the smallest val in right bst + let curr = root.right; + while (curr.left) { + curr = curr.left; + } + // change the curr value + root.val = curr.val; + + root.right = deleteNode(root.right, root.val); + + return root; + } + if (key < root.val) { + root.left = deleteNode(root.left, key); + return root; + } + root.right = deleteNode(root.right, key); + return root; +}; diff --git a/javascript/0456-132-pattern.js b/javascript/0456-132-pattern.js new file mode 100644 index 000000000..c5c37c2cb --- /dev/null +++ b/javascript/0456-132-pattern.js @@ -0,0 +1,22 @@ +/** + * @param {number[]} nums + * @return {boolean} + */ +var find132pattern = function (nums) { + let stack = []; // [num, minLeft] + let curMin = nums[0]; + + for (n of nums.slice(1)) { + while (stack.length > 0 && n >= stack.at(-1)[0]) { + stack.pop(); + } + if (stack.length > 0 && n > stack.at(-1)[1]) { + return true; + } + + stack.push([n, curMin]); + curMin = Math.min(curMin, n); + } + + return false; +}; diff --git a/javascript/0460-lfu-cache.js b/javascript/0460-lfu-cache.js new file mode 100644 index 000000000..9219ce187 --- /dev/null +++ b/javascript/0460-lfu-cache.js @@ -0,0 +1,121 @@ +// https://leetcode.com/problems/lfu-cache/ + +class ListNode { + constructor(val, next, prev) { + this.val = (val===undefined ? 0 : val) + this.next = (next===undefined ? null : next) + this.prev = (next===undefined ? null : prev) + } +} + +class LinkedList { + constructor() { + this.map = new Map() + this.Right = new ListNode(null) + this.Left = new ListNode(null, this.Right) + this.Right.prev = this.Left + } + + len() { + return this.map.size + } + + pop(val) { + if (this.map.has(val)) { + let node = this.map.get(val) + + // save + let prevNode = node.prev + // delete + prevNode.next = node.next + prevNode.next.prev = prevNode + this.map.delete(val) + } + } + + popleft() { + let data = this.Left.next.val + this.Left.next = this.Left.next.next + this.Left.next.prev = this.Left + this.map.delete(data) + return data + } + + pushRight(val) { + let newNode = new ListNode(val, this.Right) + newNode.prev = this.Right.prev + newNode.prev.next = newNode + this.Right.prev = newNode + this.map.set(val, newNode) + } + + update(val) { + this.pop(val) + this.pushRight(val) + } +} + +/** + * @param {number} capacity + */ +var LFUCache = function(capacity) { + this.capacity = capacity + this.valueMap = new Map() + this.countMap = new Map() + this.lftCount = 1 + this.lists = [] + this.counter = (key) => { + let count = this.countMap.get(key) || 0 + if (this.lists[count]) { + this.lists[count].pop(key) + } + if (!this.lists[count + 1]) { + this.lists[count + 1] = new LinkedList() + } + this.lists[count + 1].pushRight(key) + this.countMap.set(key, count + 1) + // console.log(this.lftCount == count, count, this.lists[count].len()) + if (this.lftCount == count && this.lists[count] && this.lists[count].len() === 0) { + this.lftCount++ + } + } +}; + +/** + * @param {number} key + * @return {number} + */ +LFUCache.prototype.get = function(key) { + if (!this.valueMap.has(key)) { + return -1 + } + + this.counter(key) + return this.valueMap.get(key) +}; + +/** + * @param {number} key + * @param {number} value + * @return {void} + */ +LFUCache.prototype.put = function(key, value) { + + if (!this.valueMap.has(key) && this.valueMap.size == this.capacity) { + // we need to evict + // console.log(this.lists[this.lftCount]) + let evictedKey = this.lists[this.lftCount].popleft() + this.valueMap.delete(evictedKey) + this.countMap.delete(evictedKey) + } + + this.valueMap.set(key, value) + this.counter(key) + this.lftCount = Math.min(this.countMap.get(key), this.lftCount) +}; +/** + * Your LFUCache object will be instantiated and called as such: + * var obj = new LFUCache(capacity) + * var param_1 = obj.get(key) + * obj.put(key,value) + */ \ No newline at end of file diff --git a/javascript/0463-island-perimeter.js b/javascript/0463-island-perimeter.js new file mode 100644 index 000000000..55ddb839e --- /dev/null +++ b/javascript/0463-island-perimeter.js @@ -0,0 +1,23 @@ +var islandPerimeter = function(grid) { + const visit = new Set(); + + const dfs = function(i, j) { + if(i >= grid.length || j >= grid[0].length || i < 0 || j < 0 || grid[i][j] == 0) + return 1; + let flatCoord = i*grid[0].length + j; + if(visit.has(flatCoord)) + return 0; + + visit.add(flatCoord); + let perim = dfs(i, j + 1); + perim += dfs(i + 1, j); + perim += dfs(i, j - 1); + perim += dfs(i - 1, j); + return perim; + }; + + for(let i = 0; i < grid.length; i++) + for(let j = 0; j < grid[0].length; j++) + if(grid[i][j]) + return dfs(i, j); +}; diff --git a/javascript/0472-concatenated-words.js b/javascript/0472-concatenated-words.js new file mode 100644 index 000000000..0528b0f3a --- /dev/null +++ b/javascript/0472-concatenated-words.js @@ -0,0 +1,36 @@ +/** + * @param {string[]} words + * @return {string[]} + */ +var findAllConcatenatedWordsInADict = function (words) { + let wordSet = new Set(words); + let res = []; + + for (let w of words) { + if (dfs(w)) { + res.push(w); + } + } + + return res; + + /** + * + * @param {string} word + * @returns {boolean} + */ + function dfs(word) { + for (let i = 1; i < word.length; i++) { + let prefix = word.slice(0, i); + let suffix = word.slice(i, word.length); + + if ( + (wordSet.has(prefix) && wordSet.has(suffix)) || + (wordSet.has(prefix) && dfs(suffix)) + ) { + return true; + } + } + return false; + } +}; diff --git a/javascript/0473-matchsticks-to-square.js b/javascript/0473-matchsticks-to-square.js new file mode 100644 index 000000000..4e77ee436 --- /dev/null +++ b/javascript/0473-matchsticks-to-square.js @@ -0,0 +1,49 @@ +function check(arr) { + let temp = arr[0]; + for (let i = 1; i < arr.length; i++) { + if (arr[i] !== temp) { + return false; + } + } + return true; +} + +/** + * @param {number[]} matchsticks + * @return {boolean} + */ +var makesquare = function (matchsticks) { + let sides = new Array(4).fill(0), + ans = false, + size = 0; + + for (let i = 0; i < matchsticks.length; i++) { + size += matchsticks[i]; + } + let max_size = size / 4; + if (max_size - Math.floor(max_size) !== 0) return false; + + matchsticks = matchsticks.sort((a, b) => b - a); + + function backtrack(i) { + if (ans) return; + if (i >= matchsticks.length) { + if (check(sides)) { + ans = true; + } + return; + } + for (let j = 0; j < 4; j++) { + if (sides[j] + matchsticks[i] > max_size) { + continue; + } + sides[j] += matchsticks[i]; + + backtrack(i + 1); + sides[j] -= matchsticks[i]; + } + } + backtrack(0); + + return ans; +}; diff --git a/javascript/0494-target-sum.js b/javascript/0494-target-sum.js new file mode 100644 index 000000000..7df390be9 --- /dev/null +++ b/javascript/0494-target-sum.js @@ -0,0 +1,173 @@ +/** + * Brute Force - DFS + * Time O(2^N) | Space O(N) + * https://leetcode.com/problems/target-sum/ + * @param {number[]} nums + * @param {number} target + * @return {number} + */ + var findTargetSumWays = (nums, target, index = 0, sum = 0) => { + const isBaseCase = (index === nums.length); + if (isBaseCase) { + const isTarget = (sum === target); + if (isTarget) return 1; + + return 0; + } + + return dfs(nums, target, index, sum);/* Time O(2^N) | Space O(HEIGHT) */ +} + +var dfs = (nums, target, index, sum) => { + const left = findTargetSumWays(nums, target, (index + 1), (sum + nums[index])); /* Time O(2^N) | Space O(HEIGHT) */ + const right = findTargetSumWays(nums, target, (index + 1), (sum - nums[index]));/* Time O(2^N) | Space O(HEIGHT) */ + + return (left + right); +} + +/** + * DP - Top Down + * Matrix - Memoization + * Time O(N * M) | Space O(N * M) + * https://leetcode.com/problems/target-sum/ + * @param {number[]} nums + * @param {number} target + * @return {number} + */ +var findTargetSumWays = (nums, target) => { + const total = nums.reduce((sum, num) => (sum + num), 0);/* Time O(N) */ + + return calculate(nums, target, total); /* Time O(N * M) | Space O((N * M) + HEIGHT) */ +} + +var initMemo = (nums, total) => new Array(nums.length).fill()/* Time O(N) | Space O(N) */ + .map(() => new Array(((total + 1) << 1)).fill(null)); /* Time O(M) | Space O(M) */ + +const calculate = (nums, target, total, index = 0, sum = 0, memo = initMemo(nums, total)) => { + const isBaseCase = (index === nums.length); + if (isBaseCase) { + const isTarget = (sum === target); + if (isTarget) return 1; + + return 0; + } + + const hasSeen = (memo[index][(sum + total)] != null); + if (hasSeen) return memo[index][(sum + total)]; + + return dfs(nums, target, total, index, sum, memo);/* Time O(N * M) | Space O((N * M) + HEIGHT) */ +} + +var dfs = (nums, target, total, index, sum, memo) => { + const left = calculate(nums, target, total, (index + 1), (sum + nums[index]), memo); /* Time O(N * M) | Space O(HEIGHT) */ + const right = calculate(nums, target, total, (index + 1), (sum - nums[index]), memo);/* Time O(N * M) | Space O(HEIGHT) */ + + memo[index][(sum + total)] = (left + right); /* | Space O(N * M) */ + return memo[index][(sum + total)]; +} + +/** + * DP - Bottom Up + * Matrix - Tabulation + * Time O(N * M) | Space O(N * M) + * https://leetcode.com/problems/target-sum/ + * @param {number[]} nums + * @param {number} target + * @return {number} + */ +var findTargetSumWays = (nums, target) => { + const total = nums.reduce((sum, num) => (sum + num), 0);/* Time O(N) */ + const tabu = initTabu(nums, total); /* Time O(N * M) | Space O(N * M) */ + + search(nums, total, tabu); /* Time O(N * M) | Space O(N * M) */ + + return (Math.abs(target) <= total) + ? tabu[(nums.length - 1)][(target + total)] + : 0; +}; + +var initTabu = (nums, total) => { + const tabu = new Array(nums.length).fill() /* Time O(N) | Space O(N) */ + .map(() => new Array(((total + 1) << 1)).fill(0));/* Time O(M) | Space O(M) */ + const [ left, right ] = [ (total + nums[0]), (total - nums[0]) ]; + + tabu[0][left] = 1; /* | Space O(N * M) */ + tabu[0][right] += 1; /* | Space O(N * M) */ + + return tabu; +} + +var search = (nums, total, tabu) => { + for (let i = 1; (i < nums.length); i++) {/* Time O(N) */ + for (let sum = (-total); (sum <= total); sum++) {/* Time O(M) */ + const isInvalid = (tabu[(i - 1)][(sum + total)] <= 0); + if (isInvalid) continue; + + const dpSum = tabu[(i - 1)][sum + total]; + const left = ((sum + nums[i]) + total); + const right = ((sum - nums[i]) + total); + + tabu[i][left] += dpSum; /* Space O(N * M) */ + tabu[i][right] += dpSum; /* Space O(N * M) */ + } + } +} + +/** + * DP - Top Down + * Array - Tabulation + * Time O(N * M) | Space O(M) + * https://leetcode.com/problems/target-sum/ + * @param {number[]} nums + * @param {number} target + * @return {number} + */ +var findTargetSumWays = (nums, target) => { + const total = nums.reduce((sum, num) => (sum + num), 0);/* Time O(N) */ + let tabu = getTabu(nums, total); /* Time O(M) | Space O(M) */ + + tabu = search(nums, total, tabu); /* Time O(N * M) | Space O(M) */ + + return (Math.abs(target) <= total) + ? tabu[(target + total)] + : 0 +} + +var initTabu = (total) => new Array((total + 1) << 1).fill(0);/* Time O(M) | Space O(M) */ + +var getTabu = (nums, total) => { + const tabu = initTabu(total);/* Time O(M) | Space O(M) */ + const [ left, right ] = [ (total + nums[0]), (total - nums[0]) ]; + + tabu[left] = 1; /* | Space O(M) */ + tabu[right] += 1; /* | Space O(M) */ + + return tabu; +} + +var search = (nums, total, tabu) => { + for (let i = 1; (i < nums.length); i++) { /* Time O(N) */ + const num = nums[i]; + const temp = initTabu(total); /* Time O(M) | Space O(M) */ + + tabu = update(num, total, tabu, temp); /* Time O(M) | Space O(M) */ + } + + return tabu; +} + +var update = (num, total, tabu, temp) => { + for (let sum = (-total); (sum <= total); sum++) {/* Time O(M) */ + const isInvalid = (tabu[sum + total] <= 0); + if (isInvalid) continue; + + const dpSum = tabu[sum + total]; + const left = ((sum + num) + total); + const right = ((sum - num) + total); + + temp[left] += dpSum; /* Space O(M) */ + temp[right] += dpSum; /* Space O(M) */ + } + + return temp; +} \ No newline at end of file diff --git a/javascript/0496-next-greater-element-i.js b/javascript/0496-next-greater-element-i.js new file mode 100644 index 000000000..7e973b6f8 --- /dev/null +++ b/javascript/0496-next-greater-element-i.js @@ -0,0 +1,27 @@ +/** + * HashMap and Stack + * Time O(N + M) | Space O(N) + * https://leetcode.com/problems/next-greater-element-i + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[]} + */ + +var nextGreaterElement = function (nums1, nums2) { + const subsetMap = new Map(nums1.map((val, i) => [val, i])); + const res = new Array(nums1.length).fill(-1); + + let stack = []; + + for (let num of nums2) { + while (stack.length && num > stack.at(-1)) { + const val = stack.pop(); + const idx = subsetMap.get(val); + res[idx] = num; + } + + if (subsetMap.has(num)) stack.push(num); + } + + return res; +}; diff --git a/javascript/0502-ipo.js b/javascript/0502-ipo.js new file mode 100644 index 000000000..ce1bec72d --- /dev/null +++ b/javascript/0502-ipo.js @@ -0,0 +1,51 @@ +/** + * MaxPriorityQueue | Sorting + * Time O(n*log(n)) | Space O(n) + * https://leetcode.com/problems/ipo/description/ + * @param {number} k + * @param {number} w + * @param {number[]} profits + * @param {number[]} capital + * @return {number} + */ +var findMaximizedCapital = function(k, w, profits, capital) { + + const maxQueue = new MaxPriorityQueue({ + compare: (a, b) => { + return b[0] - a[0]; + } + }); + + const minQueue = new MinPriorityQueue({ + compare: (a, b) => { + return a[0] - b[0]; + } + }); + + const pc = profits.map((profit, idx) => { + return [profit, capital[idx]]; + }); + + for (let i = 0; i < pc.length; i++) { + minQueue.enqueue([pc[i][1], pc[i][0]]); + } + + let cc = w; + while (k && (!maxQueue.isEmpty() || !minQueue.isEmpty())) { + + // add all the project that we can take to maxQ + while (!minQueue.isEmpty() && cc >= minQueue.front()[0]) { + const curr = minQueue.dequeue(); + maxQueue.enqueue([curr[1], curr[0]]); + } + + if (!maxQueue.isEmpty()) { + cc += maxQueue.dequeue()[0]; + } + + k--; + } + + return cc; +}; + diff --git a/javascript/0513-find-bottom-left-tree-value.js b/javascript/0513-find-bottom-left-tree-value.js new file mode 100644 index 000000000..ec69d129c --- /dev/null +++ b/javascript/0513-find-bottom-left-tree-value.js @@ -0,0 +1,36 @@ +/** + * PreOrder Traversal + * Time O(n) | Space O(n) (because of the call stack space is O(n). If the tree has only left children then it's kind of like a linkedList) + * https://leetcode.com/problems/find-bottom-left-tree-value/ + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @return {number} + */ +var findBottomLeftValue = function(root) { + + let leftVal = 0; + let deepestLevel = -Infinity; + + const dfs = (node, level) => { + if(!node.left && !node.right) { + if(level > deepestLevel) { + leftVal = node.val; + deepestLevel = level; + } + return; + } + + node.left && dfs(node.left, level + 1); + node.right && dfs(node.right, level + 1); + } + + dfs(root, 0); + return leftVal; +}; diff --git a/javascript/0515-find-largest-value-in-each-tree-row.js b/javascript/0515-find-largest-value-in-each-tree-row.js new file mode 100644 index 000000000..36ba63755 --- /dev/null +++ b/javascript/0515-find-largest-value-in-each-tree-row.js @@ -0,0 +1,41 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * BFS | Level order traversal + * Time O(n) | Space O(n) + * https://leetcode.com/problems/find-largest-value-in-each-tree-row/ + * @param {TreeNode} root + * @return {number[]} + */ +var largestValues = function(root) { + + if (!root) return []; + const result = []; + + const bfs = (root) => { + const queue = new Queue(); + queue.enqueue(root); + + while (!queue.isEmpty()) { + let nodesCount = queue.size(); + let max = -Infinity; + while (nodesCount) { + const node = queue.dequeue(); + max = Math.max(max, node.val); + if (node.left) queue.enqueue(node.left); + if (node.right) queue.enqueue(node.right); + nodesCount--; + } + result.push(max); + } + } + + bfs(root); + return result; +}; diff --git a/javascript/0518-coin-change-ii.js b/javascript/0518-coin-change-ii.js new file mode 100644 index 000000000..3945176da --- /dev/null +++ b/javascript/0518-coin-change-ii.js @@ -0,0 +1,140 @@ +/** + * Brute Force - DFS + * Time O(2^N) | Space O(N) + * @param {number} amount + * @param {number[]} coins + * @return {number} + */ + var change = (amount, coins, n = (coins.length)) => { + const isBaseCase1 = (amount === 0); + if (isBaseCase1) return 1; + + const isBaseCase2 = (n === 0); + if (isBaseCase2) return 0; + + return dfs(amount, coins, n);/* Time O(2^N) | Space O(N) */ +} + +var dfs = (amount, coins, n) => { + const isLess = (amount < coins[(n - 1)]); + if (isLess) return change(amount, coins, (n - 1)); /* Time O(2^N) | Space O(N) */ + + const left = change((amount - coins[(n - 1)]), coins, n);/* Time O(2^N) | Space O(N) */ + const right = change(amount, coins, (n - 1)); /* Time O(2^N) | Space O(N) */ + + return (left + right); +} + +/** + * DP - Top Down + * Matrix - Memoization + * Time O(N * AMOUNT) | Space O(N * AMOUNT) + * https://leetcode.com/problems/coin-change-ii/ + * @param {number} amount + * @param {number[]} coins + * @return {number} + */ +var change = (amount, coins, n = (coins.length), memo = initMemo(coins, amount)) => { + const isBaseCase1 = (n === 0); + if (isBaseCase1) return 0; + + const isBaseCase2 = (amount === 0); + if (isBaseCase2) return 1; + + const hasSeen = (memo[n][amount] !== null); + if (hasSeen) return memo[n][amount]; + + return dfs(amount, coins, n, memo);/* Time O(N * AMOUNT) | Space O((N * AMOUNT) + HEIGHT) */ +} + +var initMemo = (coins, amount) => new Array(coins.length + 2).fill() + .map(() => new Array(amount + 2).fill(null)); + +var dfs = (amount, coins, n, memo) => { + const isLess = (amount < coins[(n - 1)]); + if (isLess) { + memo[n][amount] = change(amount, coins, (n - 1), memo); /* Time O(N * AMOUNT) | Space O(HEIGHT) */ + return memo[n][amount]; + } + + const left = change((amount - coins[(n - 1)]), coins, n, memo);/* Time O(N * AMOUNT) | Space O(HEIGHT) */ + const right = change(amount, coins, (n - 1), memo); /* Time O(N * AMOUNT) | Space O(HEIGHT) */ + + memo[n][amount] = (left + right); /* | Space O(N * AMOUNT) */ + return memo[n][amount]; +} + +/** + * DP - Bottom Up + * Array - Tabulation + * Time O(N * AMOUNT) | Space O(N * AMOUNT) + * https://leetcode.com/problems/coin-change-ii/ + */ + var change = (amount, coins) => { + const tabu = initTabu(amount, coins);/* Time O(N * AMOUNT) | Space O(N * AMOUNT) */ + + search(amount, coins, tabu); /* Time O(N * AMOUNT) | Space O(N * AMOUNT) */ + + return tabu[coins.length][amount]; +} + +var initTabu = (amount, coins) => { + const tabu = new Array((coins.length + 1)).fill()/* Time O(N) | Space O(N) */ + .map(() => new Array((amount + 1)).fill(0)); /* Time O(AMOUNT) | Space O(AMOUNT) */ + + tabu[0][0] = 1; /* | Space O(N * AMOUNT) */ + + return tabu; +} + +var search = (amount, coins, tabu) => { + for (let coin = 1; coin <= coins.length; coin++) { /* Time O(N)*/ + tabu[coin][0] = 1; /* Space O(N * AMOUNT) */ + + for (let _amount = 1; _amount <= amount; _amount++) {/* Time O(AMOUNT) */ + tabu[coin][_amount] = tabu[coin - 1][_amount]; + + const canUpdate = (0 <= (_amount - coins[(coin - 1)])); + if (!canUpdate) continue; + + const val = tabu[coin][(_amount - coins[(coin - 1)])]; + tabu[coin][_amount] += val /* Space O(N * AMOUNT) */ + } + } +} + +/** + * DP - Bottom Up + * Array - Tabulation + * Time O(N * AMOUNT) | Space O(AMOUNT) + * https://leetcode.com/problems/coin-change-ii/ + * @param {number} amount + * @param {number[]} coins + * @return {number} + */ +var change = (amount, coins) => { + const tabu = initTabu(amount); + + search(amount, coins, tabu); + + return tabu[amount]; +}; + +var initTabu = (amount) => { + var tabu = new Array((amount + 1)).fill(0); + + tabu[0] = 1; + + return tabu; +} + +var search = (amount, coins, tabu) => { + for (const coin of coins) { + for (let _amount = 0; (_amount < (amount + 1)); _amount++) { + const canUpdate = (coin <= _amount); + if (!canUpdate) continue; + + tabu[_amount] += tabu[(_amount - coin)]; + } + } +} diff --git a/javascript/0523-continuous-subarray-sum.js b/javascript/0523-continuous-subarray-sum.js new file mode 100644 index 000000000..107151223 --- /dev/null +++ b/javascript/0523-continuous-subarray-sum.js @@ -0,0 +1,24 @@ +/** + * https://leetcode.com/problems/continuous-subarray-sum/ + * Hasing + * Time O(n) | Space O(n) + * @param {number[]} nums + * @param {number} k + * @return {boolean} + */ +var checkSubarraySum = function(arr, k) { + let sum = 0; + const remainderMap = new Map([ [0, -1] ]); + + for(let i = 0; i < arr.length; i++) { + sum += arr[i]; + if(remainderMap.has(sum%k) && i - remainderMap.get(sum%k) > 1) { + return true; + } + if(!remainderMap.has(sum%k)) { + remainderMap.set(sum%k,i); + } + } + + return false; + }; diff --git a/javascript/0535-encode-and-decode-tinyurl.js b/javascript/0535-encode-and-decode-tinyurl.js new file mode 100644 index 000000000..e08802891 --- /dev/null +++ b/javascript/0535-encode-and-decode-tinyurl.js @@ -0,0 +1,20 @@ +// problem link https://leetcode.com/problems/encode-and-decode-tinyurl +// time complexity O(1) + +const encodeMap = new Map(); +const decodeMap = new Map(); +const base = 'http://tinyurl.com/'; + +var encode = function(longUrl) { + let shortUrl = '' + if(!encodeMap.has(longUrl)) { + shortUrl = base + encodeMap.size + 1 + encodeMap.set(longUrl, shortUrl); + decodeMap.set(shortUrl, longUrl); + } + return shortUrl || encodeMap.get(longUrl); +}; + +var decode = function(shortUrl) { + return decodeMap.get(shortUrl); +}; diff --git a/javascript/0538-convert-bst-to-greater-tree.js b/javascript/0538-convert-bst-to-greater-tree.js new file mode 100644 index 000000000..e74e2e528 --- /dev/null +++ b/javascript/0538-convert-bst-to-greater-tree.js @@ -0,0 +1,28 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * Tree | reverse pre-order-traversal + * Time O(n) | Space O(n) + * @param {TreeNode} root + * @return {TreeNode} + */ +var convertBST = function(root) { + + const dfs = (node, max) => { + if(!node) return max; + + const result = dfs(node.right, max); + node.val = result + node.val; + const result1 = dfs(node.left, node.val); + return result1; + } + + dfs(root, 0); + return root; +}; diff --git a/javascript/0540-single-element-in-a-sorted-array.js b/javascript/0540-single-element-in-a-sorted-array.js new file mode 100644 index 000000000..b4b0793a1 --- /dev/null +++ b/javascript/0540-single-element-in-a-sorted-array.js @@ -0,0 +1,21 @@ +// Time Complexity: O(log n) +// Space Complexity: O(1) + +/** + * @param {number[]} nums + * @return {number} + */ +var singleNonDuplicate = function (nums) { + let left = 0, + right = nums.length - 2; + + while (left <= right) { + const mid1 = (left + right) >> 1; + const mid2 = mid1 ^ 1; + + if (nums[mid1] === nums[mid2]) left = mid1 + 1; + else right = mid1 - 1; + } + + return nums[left]; +}; diff --git a/javascript/0543-diameter-of-binary-tree.js b/javascript/0543-diameter-of-binary-tree.js new file mode 100644 index 000000000..092d52b01 --- /dev/null +++ b/javascript/0543-diameter-of-binary-tree.js @@ -0,0 +1,30 @@ +/** + * https://leetcode.com/problems/diameter-of-binary-tree/ + * TIme O(N) | Space O(H) + * @param {TreeNode} root + * @return {number} + */ +var diameterOfBinaryTree = function(root, max = [0]) { + diameterOfTree(root, max); + + return max[0]; +}; + +const diameterOfTree = (root, max) => { + const isBaseCase = root === null; + if (isBaseCase) return 0; + + return dfs(root, max); +} + +const dfs = (root, max) => { + const left = diameterOfTree(root.left, max); + const right = diameterOfTree(root.right, max); + + const diameter = left + right; + max[0] = Math.max(max[0], diameter); + + const height = Math.max(left, right); + + return height + 1; +} \ No newline at end of file diff --git a/javascript/0554-brick-wall.js b/javascript/0554-brick-wall.js new file mode 100644 index 000000000..8e2276252 --- /dev/null +++ b/javascript/0554-brick-wall.js @@ -0,0 +1,30 @@ +// link to the problem https://leetcode.com/problems/brick-wall +// time coplexity O(n^2) or the number of bricks we have in our input. +// space complexity: whatever the length of the rows happend to be. + +var leastBricks = function(wall) { + + const myHash = new Map(); + + const width = wall[0].reduce((pre, brick) => { + return brick + pre; + }, 0); + + for(let i = 0; i < wall.length; i++) { + let currentWidth = 0; + for(let j = 0; j < wall[i].length; j++) { + currentWidth += wall[i][j]; + myHash.has(currentWidth) ? myHash.set(currentWidth,myHash.get(currentWidth)+1) : myHash.set(currentWidth, 1); + } + } + +// deleteing total width as this will be the rightmost gap which will always give us false positive. +myHash.delete(width); + +maxGap = 0; + for([key, value] of myHash) { + maxGap = Math.max(maxGap, value); + } + + return wall.length - maxGap; +}; diff --git a/javascript/0560-subarray-sum-equals-k.js b/javascript/0560-subarray-sum-equals-k.js new file mode 100644 index 000000000..b4ccbdc2e --- /dev/null +++ b/javascript/0560-subarray-sum-equals-k.js @@ -0,0 +1,19 @@ +/** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ +var subarraySum = function (nums, k) { + let count = 0, + sum = 0, + map = new Map(); + map.set(0, 1); + for (const num of nums) { + sum += num; + if (map.has(sum - k)) { + count += map.get(sum - k); + } + map.set(sum, (map.get(sum) || 0) + 1); + } + return count; +}; diff --git a/javascript/0567-permutation-in-string.js b/javascript/0567-permutation-in-string.js new file mode 100644 index 000000000..69746c60b --- /dev/null +++ b/javascript/0567-permutation-in-string.js @@ -0,0 +1,226 @@ +/** + * https://leetcode.com/problems/permutation-in-string/ + * Time O(N + (M - N)) | Space O(1) + * @param {string} s1 + * @param {string} s2 + * @return {boolean} + */ +var checkInclusion = (s1, s2) => { + const isInvalid = s2.length < s1.length; + if (isInvalid) return false; + + let [left, right] = [0, 0]; + const [s1FrequencyMap, s2FrequencyMap] = getFrequencyMaps(s1); + + while (right < s2.length) { + addRightFrequency(s2, right, s2FrequencyMap); + + const window = right - left + 1; + const isPermutation = + window === s1.length && isSame(s1FrequencyMap, s2FrequencyMap); + if (isPermutation) return true; + + const canSlide = s1.length <= window; + if (canSlide) { + subtractLeftFrequency(s2, left, s2FrequencyMap); + left++; + } + + right++; + } + + return false; +}; + +const getFrequencyMaps = (s1) => { + const [s1FrequencyMap, s2FrequencyMap] = new Array(2) + .fill() + .map(() => new Array(26).fill(0)); + + for (const char of s1) s1FrequencyMap[getCode(char)]++; + + return [s1FrequencyMap, s2FrequencyMap]; +}; + +const getCode = (char) => char.charCodeAt(0) - 'a'.charCodeAt(0); + +const addRightFrequency = (s, right, frequencyMap) => { + const char = s[right]; + const index = getCode(char); + + frequencyMap[index]++; + + return frequencyMap[index]; +}; + +const subtractLeftFrequency = (s, left, frequencyMap) => { + const char = s[left]; + const index = getCode(char); + + frequencyMap[index]--; + + return frequencyMap[index]; +}; + +const isSame = (a, b) => { + for (let i = 0; i < 26; i++) { + const isMatch = a[i] === b[i]; + if (!isMatch) return false; + } + + return true; +}; + +////////////////////////////////////////////////////////////////////////////// +// Static Sliding Window +// Time: Theta(l1 + l2) O(l1 + l2) Space: Theta(1) O(1) +// Highest performing solution. Simply builds a map of the character counts +// for `s1` and `s1.length` of `s2` whose characters are within `s1`, updates +// the `s2` character map as it slides from the beginning of `s2` to the end +// of `s2`, and returns upon verifying a match between the `s1` and `s2` +// character maps. +////////////////////////////////////////////////////////////////////////////// + +/** + * @param {string} s1 + * @param {string} s2 + * @return {boolean} + */ +function checkInclusion(s1, s2) { + if (s1.length > s2.length) { + return false; + } + + const s1Chars = Object.create(null); + const s2Chars = Object.create(null); + + for (const ch of s1) { + if (!(ch in s1Chars)) { + s1Chars[ch] = 0; + s2Chars[ch] = 0; + } + ++s1Chars[ch]; + } + + for (let i = 0; i < s1.length; ++i) { + const ch = s2[i]; + if (ch in s1Chars) { + ++s2Chars[ch]; + } + } + + let matches = 0; + let matched = 0; + + for (const ch in s1Chars) { + if (s1Chars[ch] === s2Chars[ch]) { + ++matches; + } + ++matched; + } + + const last = s2.length - s1.length; + + for (let i = 0; i < last; ++i) { + if (matches === matched) { + return true; + } + + const ch1 = s2[i]; + const ch2 = s2[i + s1.length]; + + if (ch1 in s1Chars) { + if (s1Chars[ch1] === s2Chars[ch1]--) { + --matches; + } else if (s1Chars[ch1] === s2Chars[ch1]) { + ++matches; + } + } + + if (ch2 in s1Chars) { + if (s1Chars[ch2] === s2Chars[ch2]++) { + --matches; + } else if (s1Chars[ch2] === s2Chars[ch2]) { + ++matches; + } + } + } + + return matches === matched; +} + +////////////////////////////////////////////////////////////////////////////// +// Optimized Backtracking +// Time: Theta(l1 + l2) O(l1 + l2^2) Space: Theta(l1) O(l1) +// This solution passes the tests, but it is much slower than other passing +// solutions. At each possible beginning character of `s1` within `s2` a fresh +// map is created and a second pointer increments until it either matches `s1` +// or fails and moves the first and second pointer to the next available +// matching index. +////////////////////////////////////////////////////////////////////////////// + +/** + * @param {string} s1 + * @param {string} s2 + * @return {boolean} + */ +function checkInclusion(s1, s2) { + if (s1.length > s2.length) { + return false; + } + + const s1Chars = Object.create(null); + + for (const ch of s1) { + if (!(ch in s1Chars)) { + s1Chars[ch] = 0; + } + ++s1Chars[ch]; + } + + const last = s2.length - s1.length; + let i = 0; + + while (i <= last) { + while (i <= last && !(s2[i] in s1Chars)) { + ++i; + } + + if (i > last) { + return false; + } + + const subChars = Object.create(null); + let j = i; + + while (j < s2.length && s2[j] in s1Chars) { + const ch = s2[j]; + + if (!(ch in subChars)) { + subChars[ch] = 0; + } + ++subChars[ch]; + + if (subChars[ch] > s1Chars[ch]) { + break; + } + + ++j; + } + + if (s1.length === j - i) { + return true; + } + + if (j < s2.length && s2[j] in s1Chars) { + while (s2[i] !== s2[j]) { + ++i; + } + ++i; + } else { + i = j; + } + } + + return false; +} diff --git a/javascript/0572-subtree-of-another-tree.js b/javascript/0572-subtree-of-another-tree.js new file mode 100644 index 000000000..3bb99eb13 --- /dev/null +++ b/javascript/0572-subtree-of-another-tree.js @@ -0,0 +1,138 @@ +/** + * https://leetcode.com/problems/subtree-of-another-tree/ + * @param {TreeNode} root + * @param {TreeNode} subRoot + * @return {boolean} + */ +var isSubtree = function (root, subRoot) { + if (!root) return false + + if (isSame(root, subRoot)) return true + + const hasLeftTree = isSubtree(root.left, subRoot) + const hasRightTree = isSubtree(root.right, subRoot) + + return hasLeftTree || hasRightTree +}; + +const isSame = (root, subRoot) => { + const hasReachedEnd = !(root && subRoot) + if (hasReachedEnd) return root === subRoot + + const isMismatch = root.val !== subRoot.val; + if (isMismatch) return false + + const isLeftSame = isSame(root.left, subRoot.left) + const isRightSame = isSame(root.right, subRoot.right) + + return isLeftSame && isRightSame +} + +const hash = (val) => + require('crypto').createHash('md5').update(val).digest('hex') + +const merkle = (root) => { + if (!root) return '#' + + const { left, val, right } = root + + const leftMerkle = merkle(left) + const rightMerkle = merkle(right) + + const merkleVal = [leftMerkle, val, rightMerkle].join('') + const merkleHash = hash(merkleVal) + + root.merkle = merkleHash + + return root.merkle +} + +const search = (root, subRoot) => { + if (!root) return false + + const hasSamePath = root.merkle === subRoot.merkle + if (hasSamePath) return true + + const left = search(root.left, subRoot) + const right = search(root.right, subRoot) + + return left || right +} + +var isSubtree = function (root, subRoot) { + [root, subRoot].forEach(merkle) + + return search(root, subRoot) +} + +const hashify = (root, hash, postOrderKey) => { + if (!root) return '#' + + const left = hashify(root.left, hash, postOrderKey) + const right = hashify(root.right, hash, postOrderKey) + + const key = [left, root.val, right].join('') + + if (!hash.has(key)) { + hash.set(key, postOrderKey[0]) + postOrderKey[0]++ + } + + return hash.get(key) +} + +var isSubtree = function (root, subRoot, hash = new Map(), postOrderKey = [0]) { + hashify(root, hash, postOrderKey) + + const hashKey = [ + hashify(subRoot.left, hash, postOrderKey), + subRoot.val, + hashify(subRoot.right, hash, postOrderKey), + ].join('') + + return hash.has(hashKey) +} + +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * https://leetcode.com/problems/subtree-of-another-tree/ + * @param {TreeNode} root + * @param {TreeNode} subRoot + * @return {boolean} + */ +var isSubtree = function (root, subRoot) { + if (!subRoot) { + return true + } else if (!root) { + return false + } else if (isSameTree(root, subRoot)) { + return true + } + + const leftResult = isSubtree(root.left, subRoot) + const rightResult = isSubtree(root.right, subRoot) + + return leftResult || rightResult +} + +function isSameTree(root, subRoot) { + if (!root && !subRoot) { + return true + } else if (!root || !subRoot) { + return false + } else if (root.val !== subRoot.val) { + return false + } + + const leftRes = isSameTree(root.left, subRoot.left) + const rightRes = isSameTree(root.right, subRoot.right) + + return leftRes && rightRes +} diff --git a/javascript/0605-can-place-flowers.js b/javascript/0605-can-place-flowers.js new file mode 100644 index 000000000..e37490755 --- /dev/null +++ b/javascript/0605-can-place-flowers.js @@ -0,0 +1,23 @@ +/** + * Loop Solution + * Time O(N) | Space O(1) + * https://leetcode.com/problems/can-place-flowers + * @param {number[]} fb + * @param {number} n + * @return {boolean} + */ + +var canPlaceFlowers = function (fb, n) { + if (n === 0) return true; + + for (let i = 0; i < fb.length; i++) { + if (fb[i] === 0) { + fb[i - 1] !== 1 && fb[i + 1] !== 1 && n-- && i++; + } else { + i++; + } + if (n === 0) return true; + } + + return false; +}; diff --git a/javascript/0606-construct-string-from-binary-tree.js b/javascript/0606-construct-string-from-binary-tree.js new file mode 100644 index 000000000..ebee0cfd0 --- /dev/null +++ b/javascript/0606-construct-string-from-binary-tree.js @@ -0,0 +1,35 @@ +/** + * InOrder Traversal + * Time O(n) | Space O(n) + * https://leetcode.com/problems/construct-string-from-binary-tree/ + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @return {string} + */ +var tree2str = function(root) { + return dfs(root, []).join(""); +}; + +const dfs = (node, strArr) => { + if (!node) return; + + strArr.push(node.val); + + if (node.right || node.left) strArr.push("("); + dfs(node.left, strArr); + if (node.right || node.left) strArr.push(")"); + + // right tree + if (node.right) strArr.push("("); + dfs(node.right, strArr); + if (node.right) strArr.push(")"); + + return strArr; +} diff --git a/javascript/0617-merge-two-binary-trees.js b/javascript/0617-merge-two-binary-trees.js new file mode 100644 index 000000000..fc400d116 --- /dev/null +++ b/javascript/0617-merge-two-binary-trees.js @@ -0,0 +1,20 @@ +/** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {TreeNode} + * Time complexity = O(n+m) + */ + var mergeTrees = function(root1, root2) { + // Base case to return null as result of having both root1, root2 null + if(!root1 && !root2) { + return null; + } + + const val1 = root1 ? root1.val : 0; + const val2 = root2 ? root2.val : 0; + + const root = new TreeNode(val1+val2); + root.left = mergeTrees(root1 ? root1.left : null, root2 ? root2.left: null); + root.right = mergeTrees(root1 ? root1.right : null , root2 ? root2.right: null); + return root; +}; \ No newline at end of file diff --git a/javascript/0621-task-scheduler.js b/javascript/0621-task-scheduler.js new file mode 100644 index 000000000..4957acf26 --- /dev/null +++ b/javascript/0621-task-scheduler.js @@ -0,0 +1,107 @@ +/** + * https://leetcode.com/problems/task-scheduler/ + * Time O(N * log(N)) | Space O(N) + * @param {character[]} tasks + * @param {number} n + * @return {number} + */ +var leastInterval = function(tasks, n) { + const frequencyMap = getFrequencyMap(tasks) + const maxHeap = getMaxHeap(frequencyMap) + + return getMinimumCpuIntervals(maxHeap, n) +} + +var getFrequencyMap = (tasks, frequencyMap = new Array(26).fill(0)) => { + for (const task of tasks) { + const index = task.charCodeAt(0) - 'A'.charCodeAt(0); + + frequencyMap[index]++; + } + + return frequencyMap; +} + +const getMaxHeap = (frequencyMap, maxHeap = new MaxPriorityQueue()) => { + for (const frequency of frequencyMap) { + const hasFrequency = 0 < frequency; + if (hasFrequency) maxHeap.enqueue(frequency) + } + + return maxHeap +} + +const getMinimumCpuIntervals = (maxHeap, n, cpuIntervals = [ 0 ]) => { + while (!maxHeap.isEmpty()) { + const { iterations, coolingPeriodQueue } = execute(n, maxHeap, cpuIntervals) + + reQueueCoolingPeriod(coolingPeriodQueue, maxHeap) + + if (!maxHeap.isEmpty()) cpuIntervals[0] += iterations + } + + return cpuIntervals[0] +} + +const execute = (n, maxHeap, cpuIntervals, iterations = (n + 1), coolingPeriodQueue = new Queue()) => { + while ((0 < iterations) && !maxHeap.isEmpty()) { + const frequency = maxHeap.dequeue().element; + + const hasFrequency = 0 < (frequency - 1); + if (hasFrequency) coolingPeriodQueue.enqueue(frequency - 1); + + cpuIntervals[0]++; + iterations--; + } + + return { iterations, coolingPeriodQueue }; +} + +const reQueueCoolingPeriod = (coolingPeriodQueue, maxHeap) => { + while (!coolingPeriodQueue.isEmpty()) { + maxHeap.enqueue(coolingPeriodQueue.dequeue()) + } +} + +/** + * https://leetcode.com/problems/task-scheduler/ + * Time O(N) | Space O(1) + * @param {character[]} tasks + * @param {number} n + * @return {number} + */ + var leastInterval = function(tasks, n) { + const frequencyMap = getFrequencyMap(tasks); + const maxFrequency = getMaxFrequency(frequencyMap); + const mostFrequentTask = getMostFrequentTask(frequencyMap, maxFrequency); + const interval = ((maxFrequency - 1) * (n + 1)) + mostFrequentTask; + + return Math.max(tasks.length, interval); +} + +var getFrequencyMap = (tasks, frequencyMap = new Array(26).fill(0)) => { + for (const task of tasks) { + const index = task.charCodeAt(0) - 'A'.charCodeAt(0); + + frequencyMap[index]++; + } + + return frequencyMap; +} + +const getMaxFrequency = (frequencyMap, maxFrequency = 0) => { + for (const frequency of frequencyMap) { + maxFrequency = Math.max(maxFrequency, frequency); + } + + return maxFrequency; +} + +const getMostFrequentTask = (frequencyMap, maxFrequency, mostFrequentTask = 0) => { + for (const frequency of frequencyMap) { + const isSame = frequency === maxFrequency; + if (isSame) mostFrequentTask++; + } + + return mostFrequentTask; +} diff --git a/javascript/0647-palindromic-substrings.js b/javascript/0647-palindromic-substrings.js new file mode 100644 index 000000000..87a3b220c --- /dev/null +++ b/javascript/0647-palindromic-substrings.js @@ -0,0 +1,128 @@ +/** + * Brut Force - Check All Substrings + * Time O(N^3) | Space O(1) + * https://leetcode.com/problems/palindromic-substrings/ + * @param {string} s + * @return {number} + */ +var countSubstrings = (s, count = 0) => { + for (let left = 0; (left < s.length); left++) { /* Time O(N) */ + for (let right = left; (right < s.length); right++) {/* Time O(N) */ + count += Number(isPalindrome(s, left, right)); /* Time O(N) */ + } + } + + return count; +} + +const isPalindrome = (s, left, right) => { + while (left < right) {/* Time O(N) */ + const isEqual = (s[left] === s[right]); + if (!isEqual) return false; + + left++; right--; + } + + return true; +} + +/** + * DP - Bottom Up + * Matrix - Tabulation + * 2 Pointer - Slide Window + * Time O(N^2) | Space O(N^2) + * https://leetcode.com/problems/palindromic-substrings/ + * @param {string} s + * @return {number} + */ +var countSubstrings = (s, count = 0) => { + const tabu = initTabu(s); /* Time O(N * N) | Space O(N * N) */ + + count += singleLetters(s, tabu);/* Time O(N) | Space O(N * N) */ + count += doubleLetters(s, tabu);/* Time O(N) | Space O(N * N) */ + count += multiLetters(s, tabu); /* Time O(N * N) | Space O(N * N) */ + + return count; +}; + +const initTabu = (s) => new Array(s.length).fill()/* Space O(N) */ + .map(() => new Array(s.length).fill(false)); /* Space O(N) */ + +const singleLetters = (s, tabu, count = 0) => { + for (let index = 0; (index < s.length); index++) {/* Time O(N) */ + tabu[index][index] = true; /* Space O(N * N) */ + + count += Number(tabu[index][index]); + } + + return count; +} + +const doubleLetters = (s, tabu, count = 0) => { + for (let curr = 0; curr < (s.length - 1); curr++) {/* Time O(N) */ + const next = (curr + 1); + const isEqual = (s[curr] === s[next]); + + tabu[curr][next] = isEqual; /* Space O(N * N) */ + count += Number(tabu[curr][next]); + } + + return count; +} + +const multiLetters = (s, tabu, count = 0) => { + for (let window = 3; (window <= s.length); window++) {/* Time O(N) */ + count += slideWindow(s, tabu, window); /* Time O(N) | Space O(N * N) */ + } + + return count; +} + +const slideWindow = (s, tabu, window, count = 0) => { + let [ left, right ] = [ 0, (window - 1) ]; + + while (right < s.length) {/* Time O(N) */ + const isTrue = tabu[(left + 1)][(right - 1)]; + const isEqual = (s[left] === s[right]); + + tabu[left][right] = (isTrue && isEqual);/* Space O(N * N) */ + count += Number(tabu[left][right]); + + left++; right++; + } + + return count; +} + +/** + * 2 Pointer - Expand Around Center + * Time O(N^2) | Space O(1) + * https://leetcode.com/problems/palindromic-substrings/ + * @param {string} s + * @return {number} + */ +var countSubstrings = (s, count = 0) => { + for (let i = 0; (i < s.length); i++) {/* Time O(N) */ + const [ odd, even ] = [ i, (i + 1) ]; + /* odd-length: single character center */ + count += isPalindromeFromCenter(s, i, odd); /* Time O(N) */ + /* even-length: consecutive characters center */ + count += isPalindromeFromCenter(s, i, even);/* Time O(N) */ + } + + return count; +} + +const isPalindromeFromCenter = (s, left, right, count = 0) => { + const isInBounds = () => ((0 <= left) && (right < s.length)); + while (isInBounds()) {/* Time O(N) */ + const isEqual = (s[left] === s[right]); + if (!isEqual) break; + + count++; + + left--; right++; + } + + return count; +} \ No newline at end of file diff --git a/javascript/0652-find-duplicate-subtrees.js b/javascript/0652-find-duplicate-subtrees.js new file mode 100644 index 000000000..cb0dc0597 --- /dev/null +++ b/javascript/0652-find-duplicate-subtrees.js @@ -0,0 +1,52 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * Hashing + * Time O(n^2) | Space O(n^2) + * https://leetcode.com/problems/find-duplicate-subtrees/ + * @param {TreeNode} root + * @return {TreeNode[]} + */ +var findDuplicateSubtrees = function(root) { + + const stringHash = {}; + + const makePreOrderStr = (node, str) => { + if(!node) return str + "-" + "null"; + + const str1 = makePreOrderStr(node.left, str + "-" + node.val); + const str2 = makePreOrderStr(node.right, str1); + + return str2; + } + + const duplicates = []; + + const dfs = (node) => { + if(!node) return; + const str = makePreOrderStr(node, ""); + + if(!stringHash[str]) { + stringHash[str] = []; + } + + stringHash[str].push(node); + + dfs(node.left); + dfs(node.right); + } + + dfs(root); + + for (let key in stringHash) { + if(stringHash[key].length > 1) duplicates.push(stringHash[key][0]); + } + + return duplicates; +}; diff --git a/javascript/0658-find-k-closest-elements.js b/javascript/0658-find-k-closest-elements.js new file mode 100644 index 000000000..c8033dbbb --- /dev/null +++ b/javascript/0658-find-k-closest-elements.js @@ -0,0 +1,22 @@ +/** + * @param {number[]} arr + * @param {number} k + * @param {number} x + * @return {number[]} + */ +var findClosestElements = function (arr, k, x) { + let [leftPtr, rightPtr] = [0, arr.length - k]; + while (leftPtr < rightPtr) { + /* This is basically rightPtr+leftPtr/2 written differently to + avoid any overflow incase it happens. + */ + let mid = parseInt(rightPtr + (leftPtr - rightPtr) / 2); + + if (x - arr[mid] > arr[mid + k] - x) { + leftPtr = mid + 1; + } else { + rightPtr = mid; + } + } + return arr.slice(leftPtr, leftPtr + k); +}; diff --git a/javascript/0665-non-decreasing-array.js b/javascript/0665-non-decreasing-array.js new file mode 100644 index 000000000..bd304b69c --- /dev/null +++ b/javascript/0665-non-decreasing-array.js @@ -0,0 +1,22 @@ +/** + * @param {number[]} nums + * @return {boolean} + */ +var checkPossibility = function (nums) { + let changed = false; + + for (let i = 0; i < nums.length - 1; i++) { + if (nums[i] <= nums[i + 1]) continue; + if (changed) return false; + + if (i === 0 || nums[i + 1] >= nums[i - 1]) { + nums[i] = nums[i + 1]; + } else { + nums[i + 1] = nums[i]; + } + + changed = true; + } + + return true; +}; diff --git a/javascript/0669-trim-a-binary-search-tree.js b/javascript/0669-trim-a-binary-search-tree.js new file mode 100644 index 000000000..ef6973c19 --- /dev/null +++ b/javascript/0669-trim-a-binary-search-tree.js @@ -0,0 +1,33 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @param {number} low + * @param {number} high + * @return {TreeNode} + */ +var trimBST = function (root, low, high) { + + if (!root) { + return null; + } + + if (root.val < low) { + return trimBST(root.right, low, high); + } + + if (root.val > high) { + return trimBST(root.left, low, high); + } + + root.left = trimBST(root.left, low, high); + root.right = trimBST(root.right, low, high); + + return root; +}; \ No newline at end of file diff --git a/javascript/0678-valid-parenthesis-string.js b/javascript/0678-valid-parenthesis-string.js new file mode 100644 index 000000000..c7cb6a3b0 --- /dev/null +++ b/javascript/0678-valid-parenthesis-string.js @@ -0,0 +1,112 @@ +/** + * @param {string} s + * @return {boolean} + */ +var checkValidString = function (s) { + var leftMin = 0; + var leftMax = 0; + + for (var c of s) { + if (c === '(') { + leftMin++; + leftMax++; + } else if (c === ')') { + leftMin--; + leftMax--; + } else { + leftMin--; + leftMax++; + } + + if (leftMax < 0) { + return false; + } + + if (leftMin < 0) { + leftMin = 0; + } + } + + return leftMin === 0; +}; + +/** + * https://leetcode.com/problems/valid-parenthesis-string/ + * Time O(N^3) | Space O(N^2) + * @param {string} s + * @return {boolean} + */ + var checkValidString = function(s) { + const isBaseCase = s.length === 0; + if (isBaseCase) return true; + + const dp = new Array(s.length).fill() + .map(() => new Array(s.length).fill(false)); + + for (let i = 0; i < s.length; i++) {/* Time O(N) */ + if (isStar(s[i])) dp[i][i] = true; + + const isInBound = i < (s.length - 1) + const isOpenedOrStar = isOpened(s[i]) || isStar(s[i]) + const isClosedOrStar = isClosed(s[i + 1]) || isStar(s[i + 1]) + + const isValid = isInBound && isOpenedOrStar && isClosedOrStar + if (isValid) dp[i][i + 1] = true;/* Space O(N^2) */ + } + + for (let size = 2; size < s.length; size++) {/* Time O() */ + for (let i = 0; i + size < s.length; i++) {/* Time O(N) */ + const isStarOrDP = isStar(s[i]) && isDP(dp, (i + 1), (i + size)) + if (isStarOrDP) { dp[i][i + size] = true; continue; } + + const isOpenedOrStar = isOpened(s[i]) || isStar(s[i]) + if (isOpenedOrStar) check(dp, size, i); /* Time O(N) */ + } + } + + return dp[0][s.length - 1]; +} + +const check = (dp, size, i) => { + for (let k = (i + 1); k <= (i + size); k++) {/* Time O(N) */ + const isClosedOrStar = isClosed(s[k]) || isStar(s[k]) + const isKOrDP = isKEqual(k, i, 1) || isDP(dp, (i + 1), (k - 1)) + const isKOrDPSize = isKEqual(k, i, size) || isDP(dp, (k + 1), (i + size)) + + const isValid = isClosedOrStar && isKOrDP && isKOrDPSize + if (isValid) dp[i][i + size] = true;/* Space O(N^2) */ + } +} + +var isStar = (char) => char === '*' +var isOpened = (char) => char === '(' +var isClosed = (char) => char === ')' +const isKEqual = (k, i, size) => k === (i + size) +const isDP = (dp, i, k) => dp[i][k] + + +/** + * Time O(N) | Space O(1) + * @param {string} s + * @return {boolean} + */ +var checkValidString = function(s) { + let [ left, right ] = [ 0, 0 ]; + + for (const char of s) {/* Time O(N) */ + left += isOpened(char) ? 1 : -1; + right += !isClosed(char) ? 1 : -1; + + const isNegative = right < 0; + if (isNegative) break; + + left = Math.max(left, 0); + } + + return left === 0; +} + +var isOpened = (char) => char === '(' +var isClosed = (char) => char === ')' + + diff --git a/javascript/0680-valid-palindrome-ii.js b/javascript/0680-valid-palindrome-ii.js new file mode 100644 index 000000000..60a88350e --- /dev/null +++ b/javascript/0680-valid-palindrome-ii.js @@ -0,0 +1,34 @@ +/** + * @param {string} s + * @return {boolean} + */ +var validPalindrome = function (s) { + let l = 0; + let r = s.length - 1; + + while (l < r) { + console.log(l, r); + if (s[l] !== s[r]) { + const skipL = s.slice(l + 1, r + 1); + const skipR = s.slice(l, r); + return isPalindrome(skipL) || isPalindrome(skipR); + } + l++; + r--; + } + return true; +}; + +const isPalindrome = (s) => { + let l = 0; + let r = s.length - 1; + + while (l < r) { + if (s[l] !== s[r]) { + return false; + } + l++; + r--; + } + return true; +}; diff --git a/javascript/0682-Baseball-Game.js b/javascript/0682-Baseball-Game.js new file mode 100644 index 000000000..7df3428fb --- /dev/null +++ b/javascript/0682-Baseball-Game.js @@ -0,0 +1,29 @@ +/** + * @param {string[]} operations + * @return {number} + */ +var calPoints = function(operations) { + let runningSum = 0; + const stack = []; + for(const o of operations) { + if(o === 'C') { + runningSum -= stack.pop(); + continue; + } + if(o === 'D') { + const val = stack[stack.length - 1] * 2; + stack.push(val); + runningSum += val; + continue; + } + if(o === '+') { + const val = stack[stack.length - 1] + stack[stack.length - 2]; + stack.push(val); + runningSum += val; + continue; + } + stack.push(+o); + runningSum += +o; + } + return runningSum; +}; diff --git a/javascript/0682-baseball-game.js b/javascript/0682-baseball-game.js new file mode 100644 index 000000000..7df3428fb --- /dev/null +++ b/javascript/0682-baseball-game.js @@ -0,0 +1,29 @@ +/** + * @param {string[]} operations + * @return {number} + */ +var calPoints = function(operations) { + let runningSum = 0; + const stack = []; + for(const o of operations) { + if(o === 'C') { + runningSum -= stack.pop(); + continue; + } + if(o === 'D') { + const val = stack[stack.length - 1] * 2; + stack.push(val); + runningSum += val; + continue; + } + if(o === '+') { + const val = stack[stack.length - 1] + stack[stack.length - 2]; + stack.push(val); + runningSum += val; + continue; + } + stack.push(+o); + runningSum += +o; + } + return runningSum; +}; diff --git a/javascript/0684-redundant-connection.js b/javascript/0684-redundant-connection.js new file mode 100644 index 000000000..61771b9b0 --- /dev/null +++ b/javascript/0684-redundant-connection.js @@ -0,0 +1,106 @@ +/** + * https://leetcode.com/problems/redundant-connection/ + * Time O((V)^2 + E) | Space O(V + E) + * @param {number[][]} edges + * @return {number[]} + */ +var findRedundantConnection = function (edges) { + const graph = new Array((1000 + 1)).fill().map(() => []); + + for (const [ src, dst ] of edges) { + const hasNodes = (src in graph) && (dst in graph) + if (hasNodes && hasRedundantConnection(graph, src, dst)) return [ src, dst ]; + + graph[src].push(dst); + graph[dst].push(src); + } +} + +const hasRedundantConnection = (graph, source, target, seen = new Set()) => { + if (seen.has(source)) return false + seen.add(source); + + const isEqual = source === target + if (isEqual) return true; + + return dfs(graph, source, target, seen); +} + +const dfs = (graph, source, target, seen) => { + for (const neighbor of graph[source]) { + if (hasRedundantConnection(graph, neighbor, target, seen)) return true; + } + + return false; +} + +/** + * https://leetcode.com/problems/redundant-connection/ + * Time O(V + E) | Space O(V + E) + * @param {number[][]} edges + * @return {number[]} + */ +var findRedundantConnection = function (edges) { + return new UnionFind(edges) + .redundantConnection; +}; + +class UnionFind { + constructor (edges) { + this.parent = new Array(edges.length + 1).fill().map((_, index) => index); + this.rank = new Array(edges.length + 1).fill(1); + this.redundantConnection = [ -1, -1 ]; + + this.search(edges); + } + + search (edges) { + for (let [ src, dst ] of edges) { + const hasConnection = this.union(src, dst); + if (!hasConnection) return (this.redundantConnection = [ src, dst ]); + } + } + + find (node, { parent } = this) { + let head = parent[node]; + + const isEqual = () => head === parent[head]; + while (!isEqual()) { + const tail = parent[parent[head]]; + + this.compress(head, tail); + head = parent[head]; + } + + return head; + } + + compress (tail, head, { parent } = this) { + parent[tail] = head; + } + + increaseRank (head, tail, { rank } = this) { + rank[head] += rank[tail]; + } + + union (src, dst, { rank } = this) { + const [ rootSrc, rootDst ] = [ this.find(src), this.find(dst) ]; + + const hasCycle = rootSrc === rootDst; + if (hasCycle) return false; + + const isSrcGreater = rank[rootDst] < rank[rootSrc]; + if (isSrcGreater) { + this.increaseRank(rootDst, rootSrc) + this.compress(rootSrc, rootDst) + } + + const isDstGreater = rank[rootSrc] <= rank[rootDst]; + if (isDstGreater) { + this.increaseRank(rootSrc, rootDst) + this.compress(rootDst, rootSrc) + } + + return true; + } +} diff --git a/javascript/0695-max-area-of-island.js b/javascript/0695-max-area-of-island.js new file mode 100644 index 000000000..56d3aab38 --- /dev/null +++ b/javascript/0695-max-area-of-island.js @@ -0,0 +1,97 @@ +/** + * https://leetcode.com/problems/max-area-of-island + * Time O(ROWS * COLS) | Space O(ROWS * COLS) + * @param {number[][]} grid + * @return {number} + */ + var maxAreaOfIsland = function(grid, maxArea = 0) { + const [ rows, cols ] = [ grid.length, grid[0].length ]; + const seen = new Array(rows).fill().map(() => new Array(cols)); + + for (let row = 0; row < rows; row++) {/* Time O(ROWS) */ + for (let col = 0; col < cols; col++) {/* Time O(COLS) */ + const area = getArea(grid, row, rows, col, cols, seen);/* Space O(ROWS * COLS) */ + + maxArea = Math.max(maxArea, area); + } + } + + return maxArea; +}; + +var getArea = (grid, row, rows, col, cols, seen) => { + const isBaseCase = grid[row][col] === 0; + if (isBaseCase) return 0; + + if (seen[row][col]) return 0; + seen[row][col] = true; /* Space O(ROWS * COLS) */ + + return dfs(grid, row, rows, col, cols, seen) + 1; /* Space O(ROWS * COLS) */ +} + +const dfs = (grid, row, rows, col, cols, seen, area = 0) => { + for (const [ _row, _col ] of getNeighbors(row, rows, col, cols)) { + area += getArea(grid, _row, rows, _col, cols, seen); + } + + return area +} + +var getNeighbors = (row, rows, col, cols) => [[ 0, 1 ], [ 0, -1 ], [ 1, 0 ], [ -1, 0 ]] + .map(([ _row, _col]) => [ (row + _row), (col + _col) ]) + .filter(([ _row, _col ]) => (0 <= _row) && (_row < rows) && (0 <= _col) && (_col < cols)) + + /** + * https://leetcode.com/problems/number-of-islands/ + * Time O(ROWS * COLS) | Space O(ROWS * COLS) + * @param {character[][]} grid + * @return {number} + */ +var maxAreaOfIsland = (grid, maxArea = 0) => { + const [ rows, cols ] = [ grid.length, grid[0].length ] + const seen = new Array(rows).fill().map(() => new Array(cols)); + + for (let row = 0; row < rows; row++) {/* Time O(ROWS) */ + for (let col = 0; col < cols; col++) {/* Time O(COLS) */ + const isBaseCase = grid[row][col] === 0 + if (isBaseCase) continue; + + if (seen[row][col]) continue; + seen[row][col] = true; /* Space O(ROWS * COLS) */ + + const area = getArea(new Queue([[ row, col ]]), grid, seen);/* Space O(ROWS * COLS) */ + + maxArea = Math.max(maxArea, area); + } + } + + return maxArea +} + +var getArea = (queue, grid, seen, area = 0) => { + const [ rows, cols ] = [ grid.length, grid[0].length ]; + + while (!queue.isEmpty()) { + for (let i = (queue.size() - 1); 0 <= i; i--) {/* Time O(WIDTH) */ + const [ row, col ] = queue.dequeue(); + + for (const [ _row, _col ] of getNeighbors(row, rows, col, cols)) { + const isBaseCase = grid[_row][_col] === 0; + if (isBaseCase) continue; + + if (seen[_row][_col]) continue; + seen[_row][_col] = true; /* Space O(ROWS * COLS) */ + + queue.enqueue([ _row, _col ]); /* Space O(HEIGHT) */ + } + + area++; + } + } + + return area; +} + +var getNeighbors = (row, rows, col, cols) => [ [ 0, 1 ], [ 0, -1 ], [ 1, 0 ], [ -1, 0 ] ] + .map(([ _row, _col]) => [ (row + _row), (col + _col) ]) + .filter(([ _row, _col ]) => (0 <= _row) && (_row < rows) && (0 <= _col) && (_col < cols)) \ No newline at end of file diff --git a/javascript/0701-insert-into-a-binary-search-tree.js b/javascript/0701-insert-into-a-binary-search-tree.js new file mode 100644 index 000000000..0ab181e89 --- /dev/null +++ b/javascript/0701-insert-into-a-binary-search-tree.js @@ -0,0 +1,30 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * h = height of the tree, could be n. + * Time O(h) | Space O(h) + * @param {TreeNode} root + * @param {number} val + * @return {TreeNode} + */ +var insertIntoBST = function(root, val) { + return dfs(root, val); +}; + +const dfs = (root, val) => { + if (!root) { + return new TreeNode(val); + } + if (val > root.val) { + root.right = dfs(root.right, val); + return root; + } + root.left = dfs(root.left, val); + return root; +} diff --git a/javascript/0703-kth-largest-element-in-a-stream.js b/javascript/0703-kth-largest-element-in-a-stream.js new file mode 100644 index 000000000..2a500dcce --- /dev/null +++ b/javascript/0703-kth-largest-element-in-a-stream.js @@ -0,0 +1,46 @@ +/** + * https://leetcode.com/problems/kth-largest-element-in-a-stream/ + * Time O(N * (K * log(K))) | Space O(K) + * Your KthLargest object will be instantiated and called as such: + * var obj = new KthLargest(k, nums) + * var param_1 = obj.add(val) + */ + class KthLargest { + /** + * @param {number} k + * @param {number[]} nums + * @constructor + */ + constructor(k, nums) { + this.k = k + this.minHeap = new MinPriorityQueue(); + + nums.forEach((num) => this.add(num)) + } + + /** + * @param {number} val + * @return {number} + */ + add(val, { minHeap } = this) { + const isUnderCapacity = minHeap.size() < this.k; + if (isUnderCapacity) { + minHeap.enqueue(val); + + return this.top(); + } + + const isLarger = this.top() < val; + if (isLarger) { + minHeap.dequeue() + minHeap.enqueue(val); + } + + return this.top(); + } + + top ({ minHeap } = this) { + return minHeap.front()?.element || 0 + } +} + diff --git a/javascript/0704-binary-search.js b/javascript/0704-binary-search.js new file mode 100644 index 000000000..34560417d --- /dev/null +++ b/javascript/0704-binary-search.js @@ -0,0 +1,25 @@ +/** + * @param {number[]} nums + * @param {number} target + * Time O(log(N)) | Space O(1) + * @return {number} + */ +var search = function (nums, target) { + let [left, right] = [0, nums.length - 1]; + + while (left <= right) { + const mid = (left + right) >> 1; + const guess = nums[mid]; + + const isTarget = guess === target; + if (isTarget) return mid; + + const isTargetGreater = guess < target; + if (isTargetGreater) left = mid + 1; + + const isTargetLess = target < guess; + if (isTargetLess) right = mid - 1; + } + + return -1; +}; diff --git a/javascript/0705-design-hashset.js b/javascript/0705-design-hashset.js new file mode 100644 index 000000000..275b5d59f --- /dev/null +++ b/javascript/0705-design-hashset.js @@ -0,0 +1,41 @@ +// https://leetcode.com/problems/design-hashset +class MyHashSet { + constructor() { + this.set = []; + } + + /** + * Time O(1) | Space O(1) + * @param {number} key + * @return {void} + */ + add(key) { + this.set[key] = key; + } + + /** + * Time O(1) | Space O(1) + * @param {number} key + * @return {void} + */ + remove(key) { + this.set[key] = undefined; + } + + /** + * Time O(1) | Space O(1) + * @param {number} key + * @return {boolean} + */ + contains(key) { + return this.set[key] !== undefined; + } +} + +/** + * Your MyHashSet object will be instantiated and called as such: + * var obj = new MyHashSet() + * obj.add(key) + * obj.remove(key) + * var param_3 = obj.contains(key) + */ diff --git a/javascript/0706-design-hashmap.js b/javascript/0706-design-hashmap.js new file mode 100644 index 000000000..162c9a8a0 --- /dev/null +++ b/javascript/0706-design-hashmap.js @@ -0,0 +1,37 @@ +var MyHashMap = function () { + this.map = new Map(); +}; + +/** + * @param {number} key + * @param {number} value + * @return {void} + */ +MyHashMap.prototype.put = function (key, value) { + this.map.set(key, value); +}; + +/** + * @param {number} key + * @return {number} + */ +MyHashMap.prototype.get = function (key) { + const val = this.map.get(key); + return val !== undefined ? val : -1; +}; + +/** + * @param {number} key + * @return {void} + */ +MyHashMap.prototype.remove = function (key) { + this.map.delete(key); +}; + +/** + * Your MyHashMap object will be instantiated and called as such: + * var obj = new MyHashMap() + * obj.put(key,value) + * var param_2 = obj.get(key) + * obj.remove(key) + */ diff --git a/javascript/0707-design-linked-list.js b/javascript/0707-design-linked-list.js new file mode 100644 index 000000000..e6f640b97 --- /dev/null +++ b/javascript/0707-design-linked-list.js @@ -0,0 +1,89 @@ +class Node { + constructor(val) { + this.val = val; + this.prev = null; + this.next = null; + } +} + +class MyLinkedList { + constructor() { + this.head = null; + this.tail = null; + } + + get(index) { + let curr = this.head; + while (curr && index > 0) { + curr = curr.next; + index--; + } + return curr ? curr.val : -1; + } + + addAtHead(val) { + if (this.head === null) { + this.head = new Node(val); + this.tail = this.head; + } else { + this.head.prev = new Node(val); + this.head.prev.next = this.head; + this.head = this.head.prev; + } + } + + addAtTail(val) { + if (this.head === null) { + this.head = new Node(val); + this.tail = this.head; + } else { + this.tail.next = new Node(val); + this.tail.next.prev = this.tail; + this.tail = this.tail.next; + } + } + + addAtIndex(index, val) { + if (index === 0) this.addAtHead(val); + else { + let curr = this.head; + while (curr && index > 0) { + curr = curr.next; + index--; + } + if (index === 0) { + if (!curr) this.addAtTail(val); + else { + const prev = curr.prev; + prev.next = new Node(val); + prev.next.prev = prev; + prev.next.next = curr; + curr.prev = prev.next; + } + } + } + } + + deleteAtIndex(index) { + if (!this.head) return; + if (index === 0) { + this.head = this.head.next; + if (this.head) this.head.prev = null; + } else { + let curr = this.head; + while (curr && index > 0) { + curr = curr.next; + index--; + } + if (curr && index === 0) { + if (!curr.next) { + curr.prev.next = null; + this.tail = curr.prev; + } else { + curr.prev.next = curr.next; + curr.next.prev = curr.prev; + } + } + } + } +} diff --git a/javascript/0724-find-pivot-index.js b/javascript/0724-find-pivot-index.js new file mode 100644 index 000000000..c320fa87d --- /dev/null +++ b/javascript/0724-find-pivot-index.js @@ -0,0 +1,21 @@ +/** + * https://leetcode.com/problems/find-pivot-index/ + * @param {number[]} nums + * @return {number} + */ +var pivotIndex = function (nums) { + const totalSum = nums.reduce((sum, el) => { + sum += el; + return sum; + }, 0); + let pos = 0; + let leftSum = 0; + while (pos <= nums.length - 1) { + if (leftSum === totalSum - nums[pos] - leftSum) { + return pos; + } + leftSum += nums[pos]; + pos++; + } + return -1; +}; diff --git a/javascript/0735-asteroid-collision.js b/javascript/0735-asteroid-collision.js new file mode 100644 index 000000000..8503f7a50 --- /dev/null +++ b/javascript/0735-asteroid-collision.js @@ -0,0 +1,28 @@ +/** + * @param {number[]} asteroids + * @return {number[]} + */ +const asteroidCollision = (asteroids) => { + let stack = []; + + for (asteroid of asteroids) { + while (stack.length != 0 && asteroid < 0 && stack.at(-1) > 0) { + let diff = asteroid + stack.at(-1); + + if (diff < 0) { + stack.pop(); + } else if (diff > 0) { + asteroid = 0; + } else { + asteroid = 0; + stack.pop(); + } + } + + if (asteroid) { + stack.push(asteroid); + } + } + + return stack; +}; diff --git a/javascript/0739-daily-temperatures.js b/javascript/0739-daily-temperatures.js new file mode 100644 index 000000000..f89955b56 --- /dev/null +++ b/javascript/0739-daily-temperatures.js @@ -0,0 +1,81 @@ +/** + * https://leetcode.com/problems/daily-temperatures + * Time O(N) | Space O(N) - result array will always count as extra space + * @param {number[]} temperatures + * @return {number[]} + */ +var dailyTemperatures = function(temp) { + let res = new Array(temp.length).fill(0) + let stack = [] + + for (let i = 0; i < temp.length; i++){ + while (stack.length && temp[i] > temp[stack[stack.length - 1]]){ + let idx = stack.pop() + res[idx] = i - idx + } + stack.push(i) + } + return res +}; + +/** + * https://leetcode.com/problems/daily-temperatures + * Time O(N) | Space O(N) + * @param {number[]} temperatures + * @return {number[]} + */ +var dailyTemperatures = function(temperatures, stack = []) { + const days = Array(temperatures.length).fill(0); + + for (let day = 0; day < temperatures.length; day++) {/* Time O(N + N) */ + while (canShrink(stack, temperatures, day)) { /* Time O(N + N) */ + const prevColdDay = stack.pop(); + const daysToWait = (day - prevColdDay); + + days[prevColdDay] = daysToWait; /* Ignore Space O(N) */ + } + + stack.push(day); /* Space O(N) */ + } + + return days; +}; + +const canShrink = (stack, temperatures, day) => { + const previousDay = stack[stack.length - 1]; + const [ prevTemperature, currTemperature ] = [ temperatures[previousDay], temperatures[day] ]; + const isWarmer = prevTemperature < currTemperature; + + return stack.length && isWarmer; +} + +/** + * https://leetcode.com/problems/daily-temperatures + * Time O(N) | Space O(1) + * @param {number[]} temperatures + * @return {number[]} + */ +var dailyTemperatures = function(temperatures, hottest = 0) { + const days = new Array(temperatures.length).fill(0); + + for (let day = (temperatures.length - 1); (0 <= day); day--) {/* Time O(N + N) */ + const temperature = temperatures[day]; + + const isHotter = hottest <= temperature + if (isHotter) { + hottest = temperature; + continue; /* Time O(N + N) */ + } + + search(temperatures, day, temperature, days); /* Time O(N + N) | Ignore Space O(N) */ + } + + return days; +} + +const search = (temperatures, day, temperature, days, dayCount = 1) => { + const isHotter = () => temperatures[day + dayCount] <= temperature; + while (isHotter()) dayCount += days[day + dayCount]; /* Time O(N + N) */ + + days[day] = dayCount; /* Ignore Space O(N) */ +} diff --git a/javascript/0743-network-delay-time.js b/javascript/0743-network-delay-time.js new file mode 100644 index 000000000..90be1ea0d --- /dev/null +++ b/javascript/0743-network-delay-time.js @@ -0,0 +1,123 @@ +/** + * Graph - BFS + * Queue - Space (WIDTH) + * Array - Greedy + * https://leetcode.com/problems/network-delay-time/ + * @param {number[][]} times + * @param {number} n + * @param {number} k + * @return {number} + */ + var networkDelayTime = (times, n, k) => { + const { graph, maxTime, queue } = buildGraph(times, n, k); + + bfs(queue, graph, maxTime, k); + + return checkAns(maxTime); +}; + +var initGraph = (n, k) => ({ + graph: Array.from({ length: n + 1}).fill().map(() => []), + maxTime: Array.from({ length: n + 1}).fill(Infinity), + queue: new Queue([[ k, 0 ]]) +}) + +var buildGraph = (times, n, k) => { + const { graph, maxTime, queue } = initGraph(n, k); + + for (const [ src, dst, weight ] of times ) { + graph[src].push([ dst, weight ]); + }; + + maxTime[0] = 0; + + return { graph, maxTime, queue }; +} + +var bfs = (queue, graph, maxTime) => { + while (!queue.isEmpty()) { + for (let level = (queue.size() -1); (0 <= level); level-- ) { + checkNeighbors(queue, graph, maxTime); + } + } +} + +var checkNeighbors = (queue, graph, maxTime) => { + const [ node, time ] = queue.dequeue(); + + const canUpdate = (time < maxTime[node]); + if (!canUpdate) return; + + maxTime[node] = time; + + for (const [ dst, weight ] of graph[node]) { + queue.enqueue([ dst, (weight + time) ]); + } +} + +var checkAns = (maxTime) => { + const max = Math.max(...maxTime); + + return (max < Infinity) + ? max + : (-1); +} + +/** + * https://leetcode.com/problems/network-delay-time/ + * @param {number[][]} times + * @param {number} n + * @param {number} k + * @return {number} + */ + var networkDelayTime = (times, n, k) => { + const { graph, seen, minHeap } = buildGraph(times, n, k) + const maxTime = getTime(graph, seen, minHeap); + + return (seen.size === n) + ? maxTime + : -1; +}; + +var initGraph = (n, k) => ({ + graph: Array.from({ length: n + 1}).fill().map(() => []), + seen: new Set(), + minHeap: new MinPriorityQueue() +}) + +var buildGraph = (times, n, k) => { + const { graph, seen, minHeap } = initGraph(n, k); + + for (const [ src, dst, weight ] of times ) { + graph[src].push([ dst, weight ]); + }; + + minHeap.enqueue([k, 0], 0); + + return { graph, seen, minHeap }; +} + +const getTime = (graph, seen, minHeap, maxTime = 0) => { + while (!minHeap.isEmpty()) { + const [ node, cost ] = minHeap.dequeue().element; + + if (seen.has(node)) continue; + seen.add(node); + + maxTime = Math.max(maxTime, cost); + checkNeighbors(graph, node, cost, seen, minHeap); + } + + return maxTime; +} + +var checkNeighbors = (graph, src, srcCost, seen, minHeap) => { + for (const [ dst, dstCost ] of graph[src]) { + if (seen.has(dst)) continue; + + const cost = (dstCost + srcCost) + const node = [ dst, cost]; + + minHeap.enqueue(node, cost); + } +} \ No newline at end of file diff --git a/javascript/0746-min-cost-climbing-stairs.js b/javascript/0746-min-cost-climbing-stairs.js new file mode 100644 index 000000000..5e67b88a3 --- /dev/null +++ b/javascript/0746-min-cost-climbing-stairs.js @@ -0,0 +1,68 @@ +/** + * DP - Top Down + * Hash Map - Memoization + * Time O(N) | Space O(N) + * https://leetcode.com/problems/min-cost-climbing-stairs/ + * @param {number[]} cost + * @return {number} + */ +var minCostClimbingStairs = (cost, i = cost.length, memo = new Map()) => { + const isBaseCase = i <= 1; + if (isBaseCase) return 0; + + if (memo.has(i)) return memo.get(i);; + + const [ prev, prevPrev ] = [ (i - 1), (i - 2) ]; + const downOne = minCostClimbingStairs(cost, prev, memo) + cost[prev]; /* Time O(N) | Space O(N) */ + const downTwo = minCostClimbingStairs(cost, prevPrev, memo) + cost[prevPrev];/* Time O(N) | Space O(N) */ + + memo.set(i, Math.min(downOne, downTwo)); + + return memo.get(i); +} + +/** + * DP - Bottom Up + * Array - Tabulation + * Time O(N) | Space O(N) + * https://leetcode.com/problems/min-cost-climbing-stairs/ + * @param {number[]} cost + * @return {number} + */ + var minCostClimbingStairs = (cost) => { + const tabu = new Array(cost.length + 1).fill(0); + + for (let i = 2; i < tabu.length; i++) { + const [ prev, prevPrev ] = [ (i - 1), (i - 2) ]; + const downOne = tabu[prev] + cost[prev]; + const downTwo = tabu[prevPrev] + cost[prevPrev]; + + tabu[i] = Math.min(downOne, downTwo); + } + + return tabu[tabu.length - 1]; +} + +/** + * DP - Bottom Up + * Time O(N) | Space O(1) + * https://leetcode.com/problems/min-cost-climbing-stairs/ + * @param {number[]} cost + * @return {number} + */ +var minCostClimbingStairs = (cost) => { + let [ downOne, downTwo ] = [ 0, 0 ]; + + for (let i = 2; i < cost.length + 1; i++) {/* Time O(N) */ + const temp = downOne; + + const [ _prev, _prevPrev ] = [ (i - 1), (i - 2) ]; + const prev = downOne + cost[_prev]; + const prevPrev = downTwo + cost[_prevPrev]; + + downOne = Math.min(prev, prevPrev); + downTwo = temp; + } + + return downOne; +} \ No newline at end of file diff --git a/javascript/0752-open-the-lock.js b/javascript/0752-open-the-lock.js new file mode 100644 index 000000000..524470ff5 --- /dev/null +++ b/javascript/0752-open-the-lock.js @@ -0,0 +1,64 @@ +/** + * @param {string[]} deadends + * @param {string} target + * @return {number} + */ +var openLock = function(deadends, target) { + + // General approach: + // Start at the end, mark that spot to be 0 away from target + // Find all the valid neighbors through bfs, mark those as 1 + // Find all _their_ valid neighbors, mark those as ++ etc + // Until we find 0000. Whatever we mark that as is the number. BFS will guarantee it's the shortest path + + let q = [target]; // our BFS Queue + let mem = {}; // to keep track what we already have visited + mem[target] = 0; // starting distance of the end + + // Helper function that given a position, will generate all the numbers we + // can create in 1 move; + let getNextPositions = function (pos) { + // one above, one below + let dir = [-1, 1]; + let arr = pos.split(''); + let positions = []; + let i, j; + + for (j = 0; j < 2; j++) { + let next = ''; + // for each number in the position + for (i = 0; i < 4; i++) { + // logic is not just +1 -1, have to deal with wrapping around + let n = (10 + parseInt(arr[i], 10) + dir[j]) % 10; + // clone to not ruin our array for the next number + let next = [...arr]; + // set our 1 change + next[i] = n; + positions.push(next.join('')); + } + } + return positions; + } + + while (q.length) { + // dequeue a position to check out + let pos = q.shift(); + + // if it's 0000 we're done. BFS guarantees it's the shortest possible + if (pos === '0000') { + return mem[pos]; + } else { + let next = getNextPositions(pos); + next.forEach(function(n) { + // if we haven't seen n before, and it's not a dead end, + if (mem[n] === undefined && !deadends.includes(n)) { + // mark the distance and enqueue to check out next + mem[n] = mem[pos] + 1; + q.push(n); + } + }) + } + } + // if we end up here, we couldn't find it + return -1; +}; \ No newline at end of file diff --git a/javascript/0763-partition-labels.js b/javascript/0763-partition-labels.js new file mode 100644 index 000000000..b46b17d5c --- /dev/null +++ b/javascript/0763-partition-labels.js @@ -0,0 +1,69 @@ +/** + * @param {string} s + * @return {number[]} + */ +var partitionLabels = function (s) { + var lastIndex = {}, // char -> last index in s + res = [], + size = 0, + end = 0; + + for (var i in s) { + lastIndex[s[i]] = i; + } + + for (var i in s) { + var c = s[i]; + size++; + end = Math.max(end, lastIndex[c]); + if (i === String(end)) { + res.push(size); + size = 0; + } + } + + return res; +}; + +/** + * https://leetcode.com/problems/partition-labels/ + * Time O(N) | Space(1) + * @param {string} S + * @return {number[]} + */ + var partitionLabels = function(S) { + const lastSeen = getLast(S); + + return getAns(S, lastSeen); +}; + +const getLast = (S, lastSeen = []) => { + for (const index in S) {/* Time O(N) */ + const code = getCode(S[Number(index)]); + + lastSeen[code] = Number(index);/* Space O(1) */ + } + + return lastSeen; +}; + +const getCode = (char ) => char.charCodeAt(0) - 'a'.charCodeAt(0); + +const getAns = (S, lastSeen, left = 0, right = 0, labels = []) => { + for (const index in S) {/* Time O(N) */ + const code = getCode(S[Number(index)]); + const lastSeenAt = lastSeen[code]; + + right = Math.max(right, lastSeenAt); + + const isEqual = Number(index) === right; + if (!isEqual) continue; + + const placement = (Number(index) - left) + 1; + + labels.push(placement); + left = Number(index) + 1; + }; + + return labels; +} diff --git a/javascript/0767-reorganize-string.js b/javascript/0767-reorganize-string.js new file mode 100644 index 000000000..c4798c81d --- /dev/null +++ b/javascript/0767-reorganize-string.js @@ -0,0 +1,50 @@ +/** + * MaxHeap | Hashing + * Time O(n*log(n)) | Space O(n) + * https://leetcode.com/problems/reorganize-string/ + * @param {string} s + * @return {string} + */ +var reorganizeString = function(s) { + + const maxQ = new MaxPriorityQueue({ + compare: (a, b) => { + return b[0] - a[0]; + } + }); + + const freq = {}; + for (let i = 0; i < s.length; i++) { + const char = s[i]; + freq[char] = (freq[char] && freq[char] + 1 || 1); + } + for (const key in freq) { + const val = freq[key]; + maxQ.enqueue([val, key]); + } + + let orgStr = ""; + while (!maxQ.isEmpty()) { + + const [occurance, char] = maxQ.dequeue(); + + if (orgStr[orgStr.length - 1] === char) { + + if (maxQ.isEmpty()) return ""; + + const [occurance1, char1] = maxQ.dequeue(); + orgStr += char1; + if (occurance1 - 1) { + maxQ.enqueue([occurance1 - 1, char1]); + } + maxQ.enqueue([occurance, char]); + } else { + orgStr += char; + if (occurance - 1) { + maxQ.enqueue([occurance - 1, char]); + } + } + } + + return orgStr; +}; diff --git a/javascript/0778-swim-in-rising-water.js b/javascript/0778-swim-in-rising-water.js new file mode 100644 index 000000000..1611766e6 --- /dev/null +++ b/javascript/0778-swim-in-rising-water.js @@ -0,0 +1,47 @@ +/** + * https://leetcode.com/problems/swim-in-rising-water/ + * @param {number[][]} grid + * @return {number} + */ + var swimInWater = (grid) => { + const seen = new Set(); + const minHeap = getHeap(grid); + + return getTime(grid, seen, minHeap); +}; + + +const getHeap = (grid, minHeap = new MinPriorityQueue()) => { + minHeap.enqueue([ 0, 0 ], grid[0][0]); + + return minHeap; +} + +var getTime = (grid, seen, minHeap, maxTime = 0) => { + const [ rows, cols ] = [ (grid.length - 1), (grid[0].length - 1) ]; + + while (!minHeap.isEmpty()) { + const { element, priority: cost } = minHeap.dequeue(); + const [ row, col ] = element; + + seen.add(grid[row][col]); + maxTime = Math.max(maxTime, cost); + + const isEnd = (row === rows) && (col === cols); + if (isEnd) return maxTime; + + checkNeighbors(grid, row, rows, col, cols, seen, minHeap); + } +} + +var checkNeighbors = (grid, row, rows, col, cols, seen, minHeap) => { + for (const [ _row, _col ] of getNeighbors(row, rows, col, cols)) { + if (seen.has(grid[_row][_col])) continue; + + minHeap.enqueue([ _row, _col ], grid[_row][_col]); + } +} + +const getNeighbors = (row, rows, col, cols) => [ [1, 0], [-1, 0], [0, 1], [0, -1] ] + .map(([ _row, _col ]) => [ (row + _row), (col + _col) ]) + .filter(([ _row, _col ]) => ((0 <= _row) && (_row <= rows) && (0 <= _col) && (_col <= cols))) diff --git a/javascript/0783-minimum-distance-between-bst-nodes.js b/javascript/0783-minimum-distance-between-bst-nodes.js new file mode 100644 index 000000000..87704f1c5 --- /dev/null +++ b/javascript/0783-minimum-distance-between-bst-nodes.js @@ -0,0 +1,35 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * DFS (level order traversal) + * Time O(n) | Space O(n) + * https://leetcode.com/problems/minimum-distance-between-bst-nodes/ + * @param {TreeNode} root + * @return {number} + */ +var minDiffInBST = function(root) { + // levelOrderTraversal + const sortedArr = dfs(root, []); + + let min = Infinity; + for (let i = 1; i < sortedArr.length; i++) { + min = Math.min(min, sortedArr[i] - sortedArr[i - 1]); + } + return min; +}; + +const dfs = (node, sortedArr) => { + if (!node) return; + + dfs(node.left, sortedArr); + sortedArr.push(node.val) + dfs(node.right, sortedArr); + + return sortedArr; +} diff --git a/javascript/0787-cheapest-flights-within-k-stops.js b/javascript/0787-cheapest-flights-within-k-stops.js new file mode 100644 index 000000000..79562829f --- /dev/null +++ b/javascript/0787-cheapest-flights-within-k-stops.js @@ -0,0 +1,64 @@ +/** + * @param {number} n + * @param {number[][]} flights + * @param {number} src + * @param {number} dst + * @param {number} k + * @return {number} + */ + var findCheapestPrice = function (n, flights, src, dst, K) { + const { graph, seen, minHeap } = buildGraph(n, flights, src, dst, K); + + return search(graph, src, dst, seen, minHeap); +}; + +var initGraph = (n) => ({ + graph: new Array(n).fill().map(() => []), + seen: new Map(), + minHeap: new MinPriorityQueue(), +}) + +var buildGraph = (n, flights, src, dst, K) => { + const { graph, seen, minHeap } = initGraph(n); + + for (const [ src, dst, cost ] of flights) { + graph[src].push([ dst, cost ]); + } + + const priority = 0; + const node = [ priority, src, (K + 1) ]; + + minHeap.enqueue(node, priority); + + return { graph, seen, minHeap }; +} + +const search = (graph, src, dst, seen, minHeap) => { + while (!minHeap.isEmpty()) { + const [ cost, city, stops ] = minHeap.dequeue().element; + + seen.set(city, stops); + + const isTarget = (city === dst); + if (isTarget) return cost; + + const canSkip = (stops <= 0); + if (canSkip) continue; + + checkNeighbors(graph, cost, city, stops, seen, minHeap); + } + + return -1; +} + +var checkNeighbors = (graph, cost, city, stops, seen, minHeap) => { + for (let [ nextCity, nextCost ] of graph[city]) { + const hasSeen = (seen.has(nextCity) && ((stops - 1) <= seen.get(nextCity))); + if (hasSeen) continue; + + const priority = (cost + nextCost) + const node = [ priority, nextCity, (stops - 1)]; + + minHeap.enqueue(node, priority); + } +} \ No newline at end of file diff --git a/javascript/0789-minimum-distance-between-two-bst-nodes.js b/javascript/0789-minimum-distance-between-two-bst-nodes.js new file mode 100644 index 000000000..0bd45b8ed --- /dev/null +++ b/javascript/0789-minimum-distance-between-two-bst-nodes.js @@ -0,0 +1,32 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @return {number} + */ +var minDiffInBST = function (root) { + let [prev, res] = [null, Number.MAX_VALUE]; + + function dfs(node) { + if (node) { + dfs(node.left); + + if (prev) { + res = Math.min(res, node.val - prev.val); + } + prev = node; + + dfs(node.right); + } + } + + dfs(root); + + return res; +}; diff --git a/javascript/0837-new-21-game.js b/javascript/0837-new-21-game.js new file mode 100644 index 000000000..375d086a4 --- /dev/null +++ b/javascript/0837-new-21-game.js @@ -0,0 +1,34 @@ +/** + * https://leetcode.com/problems/new-21-game/ + * @param {number} n + * @param {number} k + * @param {number} maxPts + * @return {number} + */ +var new21Game = function(n, k, maxPts) { + if (k == 0) { + return 1 + } + + let windowSum = 0 + for (let i = k; i < k + maxPts; i++) { + if (i <= n) { + windowSum += 1 + } + } + + let dp = {} + for (let i = k - 1; i >= 0; i--) { + dp[i] = windowSum / maxPts + + let remove = 0 + if (i + maxPts <= n) { + remove = dp[i + maxPts] || 1 + } + + windowSum += dp[i] + windowSum -= remove + } + + return dp[0] +}; \ No newline at end of file diff --git a/javascript/0838-push-dominoes.js b/javascript/0838-push-dominoes.js new file mode 100644 index 000000000..8f1121521 --- /dev/null +++ b/javascript/0838-push-dominoes.js @@ -0,0 +1,73 @@ +/** + * 838. Push Dominoes + * ----------------------- + * link: https://leetcode.com/problems/push-dominoes/ + * + * description: follow the rules of dominoes falling physics after one sec. + * + * time: O(n^2) + * space: O(n) + */ + +/** + * @param {string} dominoes + * @return {string} + */ +var pushDominoes = function (dominoes) { + if (dominoes.length === 1) return dominoes; + + let dominoesArr = [...dominoes]; + + for (let i = 0; i < dominoesArr.length; i++) { + if (dominoesArr[i] !== '.') continue; + + let [left, right] = nearestMove(dominoesArr, i); + + if (left === -1 && right === -1) continue; + + if (left === -1 && dominoesArr[right] === 'L') { + dominoesArr[i] = 'L'; + } else if (right === -1 && dominoesArr[left] === 'R') { + dominoesArr[i] = 'R'; + } else if (i - left !== right - i) { + if (i - left < right - i && dominoesArr[left] === 'R') { + dominoesArr[i] = 'R'; + dominoesArr[right - 1] = dominoesArr[right]; + } else if (i - left > right - i && dominoesArr[right] === 'L') { + dominoesArr[i] = 'L'; + dominoesArr[left + 1] = dominoesArr[left]; + } else if (dominoesArr[left] === dominoesArr[right]) { + dominoesArr[i] = dominoesArr[right]; + } + } else if (dominoesArr[left] === dominoesArr[right]) { + dominoesArr[i] = dominoesArr[right]; + } + } + + return dominoesArr.join(''); +}; + +/** + * @param {string[]} dominoes + * @param {number} index + * @returns {number[]} + */ +var nearestMove = function (dominoes, index) { + let ans = [-1, -1]; + + for (let i = index - 1; i > -1; i--) { + if (ans[0] === -1 && (dominoes[i] === 'L' || dominoes[i] === 'R')) { + ans[0] = i; + break; + } + } + + for (let i = index + 1; i < dominoes.length; i++) { + if (ans[1] === -1 && (dominoes[i] === 'L' || dominoes[i] === 'R')) { + ans[1] = i; + break; + } + } + + return ans; +}; diff --git a/javascript/0846-hand-of-straights.js b/javascript/0846-hand-of-straights.js new file mode 100644 index 000000000..9b7b4da05 --- /dev/null +++ b/javascript/0846-hand-of-straights.js @@ -0,0 +1,50 @@ +/** + * https://leetcode.com/problems/hand-of-straights/ + * Time O(N * K) | Space O(N) + * @param {number[]} hand + * @param {number} groupSize + * @return {boolean} + */ + var isNStraightHand = function (hand, groupSize, count = new Map()) { + const map = getFrequencyMap(hand);/* Time O(N) | Space O(N) */ + const sortUniqHand = getUniqueHand(hand);/* Time O(N * Log(N)) | Space O(N) */ + + return search(groupSize, map, sortUniqHand);/* Time O(N * K) */ +}; + +const getFrequencyMap = (hand, map = new Map()) => { + for (const _hand of hand) {/* Time O(N) */ + const val = (map.get(_hand) || 0) + 1; + + map.set(_hand, val);/* Space O(N) */ + } + + return map; +} + +const getUniqueHand = (hand) => [ ...new Set(hand) ]/* Time O(N) | Space O(N) */ + .sort((a, b) => b - a);/* Time O(N * Log(N)) | Space HeapSort O(1) | Space QuickSort O(log(N)) */ + +const search = (groupSize, map, sortUniqHand) => { + while (sortUniqHand.length) {/* Time O(N) */ + const smallest = sortUniqHand[sortUniqHand.length - 1]; + + for (let i = smallest; i < smallest + groupSize; i++) {/* Time O(K) */ + if (!map.has(i)) return false; + + const val = map.get(i) - 1; + + map.set(i, val); + + let isEqual = map.get(i) === 0; + if (!isEqual) continue; + + isEqual = i === sortUniqHand[sortUniqHand.length - 1]; + if (!isEqual) return false; + + sortUniqHand.pop(); + } + } + + return true; +} \ No newline at end of file diff --git a/javascript/0853-car-fleet.js b/javascript/0853-car-fleet.js new file mode 100644 index 000000000..5d1df6710 --- /dev/null +++ b/javascript/0853-car-fleet.js @@ -0,0 +1,63 @@ +/** + * https://leetcode.com/problems/car-fleet + * Time O(N * log(N)) | Space O(N) + * @param {number} target + * @param {number[]} position + * @param {number[]} speed + * @return {number} + */ +var carFleet = function(target, position, speed) { + const coordinates = getCoordinates(target, position, speed); /* Time O(N * log(N)) | Space O(N) */ + + return searchAscending(coordinates); /* Time O(N) | Space O(N) */ +}; + +var getCoordinates = (target, position, speed) => position + .map((_position, index) => [ _position, speed[index] ]) /* Time O(N) | Space O(N) */ + .sort(([ aPosition ], [ bPosition ]) => aPosition - bPosition) /* Time O(N * log(N)) | HeapSort Space 0(1) | QuickSort Space O(log(N)) */ + .map(([ _position, _speed ]) => (target - _position) / _speed); /* Time O(N) | Space O(N) */ + +var searchAscending = (coordinates, stack = []) => { + for (const coordinate of coordinates) { /* Time O(N + N) */ + shrink(coordinate, stack); /* Time O(N + N) */ + stack.push(coordinate); /* Space O(N) */ + } + + return stack.length; +} + +const shrink = (coordinate, stack) => { + const isPreviousLess = () => stack[stack.length - 1] <= coordinate; + while (stack.length && isPreviousLess()) stack.pop(); /* Time O(N + N) */ +} + +/** + * https://leetcode.com/problems/car-fleet + * Time O(N * log(N)) | Space O(N) + * @param {number} target + * @param {number[]} position + * @param {number[]} speed + * @return {number} + */ + var carFleet = function(target, position, speed) { + const coordinates = getCoordinates(target, position, speed); /* Time O(N * log(N)) | Space O(N) */ + + return searchDescending(coordinates); /* Time O(N) */ +}; + +var getCoordinates = (target, position, speed) => position + .map((_position, index) => [ _position, speed[index] ]) /* Time O(N) | Space O(N) */ + .sort(([ aPosition ], [ bPosition ]) => bPosition - aPosition) /* Time O(N * log(N)) | HeapSort Space 0(1) | QuickSort Space O(log(N)) */ + .map(([ _position, _speed ]) => (target - _position) / _speed);/* Time O(N) | Space O(N) */ + +var searchDescending = (coordinates, previous = 0, fleets = 0) => { + for (const coordinate of coordinates) { /* Time O(N) */ + const isPreviousLess = previous < coordinate + if (!isPreviousLess) continue + + previous = coordinate + fleets++ + } + + return fleets; +} \ No newline at end of file diff --git a/javascript/0861-score-after-flipping-matrix.js b/javascript/0861-score-after-flipping-matrix.js new file mode 100644 index 000000000..f2d9e9e40 --- /dev/null +++ b/javascript/0861-score-after-flipping-matrix.js @@ -0,0 +1,62 @@ +/** + * Greedy + * Time O(n) | Space O(1) + * https://leetcode.com/problems/score-after-flipping-matrix + * @param {number[][]} grid + * @return {number} + */ +var matrixScore = function(grid) { + + const ROW = grid[0].length; + const COL = grid.length; + + const countZeros = (col) => { + + let start = 0; + let count = 0; + while (start < COL) { + if (!grid[start][col]) count++; + start++; + } + + return count; + } + + const flip = (i, isRow) => { + let start = 0; + + if (isRow) { + while (start < ROW) { + grid[i][start] ^= 1; + start++; + } + return; + } + + if (!isRow) { + while (start < COL) { + grid[start][i] ^= 1; + start++; + } + return; + } + } + + for (let i = 0; i < COL; i++) { + if (!grid[i][0]) flip(i, true); + + for (let j = (grid[i][0] && 1); j < ROW; j++) { + const numberOfZeros = countZeros(j); + if (numberOfZeros > COL - numberOfZeros) { + flip(j, false); + } + } + } + + let total = 0; + for (let i = 0; i < COL; i++) { + total += parseInt(grid[i].join(""), 2); + } + + return total; +}; diff --git a/javascript/0872-leaf-similar-trees.js b/javascript/0872-leaf-similar-trees.js new file mode 100644 index 000000000..b600b3351 --- /dev/null +++ b/javascript/0872-leaf-similar-trees.js @@ -0,0 +1,40 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * DFS | Preorder Traversal + * Time O(n) | Space O(n) + * https://leetcode.com/problems/leaf-similar-trees/ + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {boolean} + */ +var leafSimilar = function(root1, root2) { + + const dfs = (node, arr) => { + if (!node.left && !node.right) { + arr.push(node.val); + return arr; + } + if (node.left) dfs(node.left, arr); + if (node.right) dfs(node.right, arr); + + return arr; + } + + const arr1 = dfs(root1, []); + const arr2 = dfs(root2, []); + + if (arr1.length !== arr2.length) return false; + + for (let i = 0; i < arr1.length; i++) { + if (arr1[i] !== arr2[i]) return false; + } + + return true; +}; diff --git a/javascript/0875-koko-eating-bananas.js b/javascript/0875-koko-eating-bananas.js new file mode 100644 index 000000000..2675ea056 --- /dev/null +++ b/javascript/0875-koko-eating-bananas.js @@ -0,0 +1,30 @@ +/** + * @param {number[]} piles + * @param {number} h + * Time O(N * log(M)) | Space O(1) + * @return {number} + */ +var minEatingSpeed = function (piles, h) { + let [left, right] = [1, Math.max(...piles)]; + + while (left < right) { + const mid = (left + right) >> 1; + const hourSpent = getHourSpent(mid, piles); + + const isTargetGreater = h < hourSpent; + if (isTargetGreater) left = mid + 1; + + const isTargetLess = hourSpent <= h; + if (isTargetLess) right = mid; + } + + return right; +}; + +const getHourSpent = (mid, piles, hourSpent = 0) => { + for (const pile of piles) { + hourSpent += Math.ceil(pile / mid); + } + + return hourSpent; +}; diff --git a/javascript/0876-middle-of-the-linked-list.js b/javascript/0876-middle-of-the-linked-list.js new file mode 100644 index 000000000..551acdbaf --- /dev/null +++ b/javascript/0876-middle-of-the-linked-list.js @@ -0,0 +1,21 @@ +/** + * https://leetcode.com/problems/middle-of-the-linked-list/ + * Definition for singly-linked list. + * function ListNode(val, next) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + */ +/** + * @param {ListNode} head + * @return {ListNode} + */ +var middleNode = function (head) { + let first = head; + let second = head; + while (second != null && second.next != null) { + first = first.next; + second = second.next.next; + } + return first; +}; diff --git a/javascript/0881-boats-to-save-people.js b/javascript/0881-boats-to-save-people.js new file mode 100644 index 000000000..59e6b57cc --- /dev/null +++ b/javascript/0881-boats-to-save-people.js @@ -0,0 +1,23 @@ +/** + * @param {number[]} people + * @param {number} limit + * @return {number} + */ +var numRescueBoats = function (people, limit) { + const sortedPeople = people.sort((a, b) => a - b); + let left = 0; + let right = people.length - 1; + let boats = 0; + + while (left <= right) { + const weight = sortedPeople[left] + sortedPeople[right]; + if (left === right || weight <= limit) { + left++; + right--; + } else { + right--; + } + boats++; + } + return boats; +}; diff --git a/javascript/0894-all-possible-full-binary-trees.js b/javascript/0894-all-possible-full-binary-trees.js new file mode 100644 index 000000000..76b52ae33 --- /dev/null +++ b/javascript/0894-all-possible-full-binary-trees.js @@ -0,0 +1,45 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * DFS | Recursion + * Time O(2^n) | Space O(2^n) + * https://leetcode.com/problems/all-possible-full-binary-trees/ + * @param {number} n + * @return {TreeNode[]} + */ +var allPossibleFBT = function(n) { + + // even number of nodes can't make a full binary tree. + if(!(n % 2)) return []; + + const dfs = (n) => { + if(n === 1) return [new TreeNode(0)]; + + const allPossibleTrees = []; + for(let i = 1; i < n; i += 2) { + + const leftNumOfNodes = i; + const rightNumOfNodes = n - i - 1; + + const leftTrees = dfs(leftNumOfNodes); + const rightTrees = dfs(rightNumOfNodes); + + for(let i = 0; i < leftTrees.length; i++) { + for(let j = 0; j < rightTrees.length; j++) { + const root = new TreeNode(0, leftTrees[i], rightTrees[j]); + allPossibleTrees.push(root); + } + } + } + + return allPossibleTrees; + } + + return dfs(n); +}; diff --git a/javascript/0895-maximum-frequency-stack.js b/javascript/0895-maximum-frequency-stack.js new file mode 100644 index 000000000..550ada5eb --- /dev/null +++ b/javascript/0895-maximum-frequency-stack.js @@ -0,0 +1,29 @@ +class FreqStack { + constructor() { + this.cnt = {}; + this.maxCnt = 0; + this.stacks = {}; + } + + push(val) { + let valCnt = 1 + (this.cnt[val] || 0); + this.cnt[val] = valCnt; + + if (valCnt > this.maxCnt) { + this.maxCnt = valCnt; + this.stacks[valCnt] = []; + } + this.stacks[valCnt].push(val); + } + + pop() { + let res = this.stacks[this.maxCnt].pop(); + this.cnt[res] -= 1; + + if (this.stacks[this.maxCnt].length == 0) { + this.maxCnt -= 1; + } + + return res; + } +} diff --git a/javascript/0901-online-stock-span.js b/javascript/0901-online-stock-span.js new file mode 100644 index 000000000..7b23c590d --- /dev/null +++ b/javascript/0901-online-stock-span.js @@ -0,0 +1,36 @@ +//https://leetcode.com/problems/online-stock-span/ +class StockSpanner { + constructor() { + this.stack = []; + } + + // Time O(1) | Space O(1) + isEmpty() { + return this.stack.length === 0; + } + + // Time O(1) | Space O(1) + peek() { + return this.isEmpty() ? null : this.stack[this.stack.length - 1]; + } + + // Time O(1) | Space O(1) + push(val) { + return this.stack.push(val); + } + + // Time O(1) | Space O(1) + pop() { + return this.stack.pop(); + } + + // Time O(n) | Space O(1) + next(price) { + let currunt = 1; + while (this.peek() && this.peek()[0] <= price) { + currunt += this.pop()[1]; + } + this.push([price, currunt]); + return this.peek()[1]; + } + } diff --git a/javascript/0904-fruit-into-baskets.js b/javascript/0904-fruit-into-baskets.js new file mode 100644 index 000000000..4273b59f0 --- /dev/null +++ b/javascript/0904-fruit-into-baskets.js @@ -0,0 +1,26 @@ +/** + * @param {number[]} fruits + * @return {number} + */ +var totalFruit = function (fruits) { + let count = new Map(); + let [left, total, res] = [0, 0, 0]; + + for (fruit of fruits) { + count.set(fruit, (count.get(fruit) || 0) + 1); + total++; + + while (count.size > 2) { + let f = fruits[left]; + count.set(f, count.get(f) - 1); + total -= 1; + left += 1; + if (!count.get(f)) { + count.delete(f); + } + } + res = Math.max(res, total); + } + + return res; +}; diff --git a/javascript/0909-snakes-and-ladders.js b/javascript/0909-snakes-and-ladders.js new file mode 100644 index 000000000..b00e525cf --- /dev/null +++ b/javascript/0909-snakes-and-ladders.js @@ -0,0 +1,30 @@ +/** + * @param {number[][]} board + * @return {number} + */ +var snakesAndLadders = function(board) { + let n = board.length; + let set = new Set(); + let getPos = (pos) => { + let row = Math.floor((pos - 1) / n) + let col = (pos - 1) % n + col = row % 2 == 1 ? n - 1 - col : col; + row = n - 1 - row; + return [row, col] + } + let q = [[1, 0]] + while (q.length > 0){ + [pos, moves] = q.shift(); + for (let i = 1; i < 7; i++){ + let newPos = i + pos; + let [r, c] = getPos(newPos); + if (board[r][c] != -1 ) newPos = board[r][c] + if (newPos == n * n) return moves + 1; + if (!set.has(newPos)) { + set.add(newPos) + q.push([newPos, moves + 1]) + } + } + } + return -1 +}; diff --git a/javascript/0912-sort-an-array.js b/javascript/0912-sort-an-array.js new file mode 100644 index 000000000..2a9fc4274 --- /dev/null +++ b/javascript/0912-sort-an-array.js @@ -0,0 +1,53 @@ +/** + * https://leetcode.com/problems/sort-an-array/ + * Merge Sort + * Time O(n*log(n)) | Space O(n) + * @param {number[]} nums + * @return {number[]} + */ +const sortArray = function(nums) { + return mergeSort(0, nums.length - 1, nums); +}; + +const mergeSort = (left, right, nums) => { + + if(left === right) return nums; + + const mid = Math.floor((left+right)/2); + mergeSort(left, mid, nums); + mergeSort(mid+1, right, nums); + return merge(left, right, mid, nums); +} + +const merge = (left, right, mid, nums) => { + const arr1 = nums.slice(left, mid+1); + const arr2 = nums.slice(mid+1, right + 1); + + let p1 = 0; + let p2 = 0; + let gp = left; + + while(p1 < arr1.length && p2 < arr2.length) { + if(arr1[p1] < arr2[p2]) { + nums[gp] = arr1[p1]; + p1++; + } else { + nums[gp] = arr2[p2]; + p2++; + } + gp++; + } + + while(p1 < arr1.length) { + nums[gp] = arr1[p1]; + p1++; + gp++; + } + + while(p2 < arr2.length) { + nums[gp] = arr2[p2]; + p2++; + gp++; + } + return nums; +} diff --git a/javascript/0918-maximum-sum-circular-subarray.js b/javascript/0918-maximum-sum-circular-subarray.js new file mode 100644 index 000000000..5b92c6fbd --- /dev/null +++ b/javascript/0918-maximum-sum-circular-subarray.js @@ -0,0 +1,19 @@ +/** + * @param {number[]} nums + * @return {number} + */ +var maxSubarraySumCircular = function (nums) { + let [globalMax, globalMin] = [nums[0], nums[0]]; + let [currentMax, currentMin] = [0, 0]; + let total = 0; + + for (num of nums) { + currentMax = Math.max(num, currentMax + num); + currentMin = Math.min(num, currentMin + num); + total += num; + globalMax = Math.max(globalMax, currentMax); + globalMin = Math.min(globalMin, currentMin); + } + + return globalMax > 0 ? Math.max(globalMax, total - globalMin) : globalMax; +}; diff --git a/javascript/0926-flip-string-to-monotone-increasing.js b/javascript/0926-flip-string-to-monotone-increasing.js new file mode 100644 index 000000000..e4de7bc8b --- /dev/null +++ b/javascript/0926-flip-string-to-monotone-increasing.js @@ -0,0 +1,17 @@ +/** + * @param {string} s + * @return {number} + */ +var minFlipsMonoIncr = function (s) { + let [res, countOne] = [0, 0]; + + for (ch of s) { + if (ch == '1') { + countOne++; + } else { + res = Math.min(res + 1, countOne); + } + } + + return res; +}; diff --git a/javascript/0929-unique-email-addresses.js b/javascript/0929-unique-email-addresses.js new file mode 100644 index 000000000..bd9b09a09 --- /dev/null +++ b/javascript/0929-unique-email-addresses.js @@ -0,0 +1,50 @@ +/** + * Built-in Functions Solution + * Hash Set - Unique Emails + * Time O(N * K) | Space O(N) + * https://leetcode.com/problems/unique-email-addresses + * @param {string[]} emails + * @return {number} + */ +var numUniqueEmails = function (emails) { + const valid = emails.map(email => { + const [local, domain] = email.split("@"); + return ( + local.split("+").shift().split(".").join("") + "@" + domain + ); + }); + + return new Set(valid).size; +}; + +/** + * Manual Solution + * Hash Set - Unique Emails + * Time O(N * K) | Space O(N) + * https://leetcode.com/problems/unique-email-addresses + * @param {string[]} emails + * @return {number} + */ +var numUniqueEmails = function (emails) { + const uniqEmails = new Set(); + + for (let email of emails) { + let cleanEmail = ""; + for (let i = 0; i < email.length; i++) { + if (email[i] === "@") { + cleanEmail += email.slice(i); + break; + } else if (email[i] === "+") { + while (email[i] !== "@") i++; + cleanEmail += email.slice(i); + break; + } else if (email[i] !== ".") { + cleanEmail += email[i]; + } + } + + uniqEmails.add(cleanEmail); + } + + return uniqEmails.size; +}; diff --git a/javascript/0934-shortest-bridge.js b/javascript/0934-shortest-bridge.js new file mode 100644 index 000000000..f2452d428 --- /dev/null +++ b/javascript/0934-shortest-bridge.js @@ -0,0 +1,70 @@ +const DIRECTIONS = [[-1, 0], [1, 0], [0, -1], [0, 1]]; + +const shortestBridge = (grid) => { + const rows = grid.length; + const cols = grid[0].length; + + let queue = []; + + const exploreIslandDFS = (row, col) => { + if (row < 0 || row >= rows || col < 0 || col >= cols || grid[row][col] !== 1) { + return false; + } + + queue.push([row, col]); + grid[row][col] = 2; + + exploreIslandDFS(row - 1, col); + exploreIslandDFS(row + 1, col); + exploreIslandDFS(row, col - 1); + exploreIslandDFS(row, col + 1); + + return true; + }; + + const buildBridgeBFS = () => { + let distance = -1; + let currentQueue = []; + + while (queue.length) { + currentQueue = queue; + queue = []; + + for (let [row, col] of currentQueue) { + for (let [dx, dy] of DIRECTIONS) { + const nextRow = row + dx; + const nextCol = col + dy; + + if ( + nextRow >= 0 && + nextRow < rows && + nextCol >= 0 && + nextCol < cols && + grid[nextRow][nextCol] !== 2 + ) { + if (grid[nextRow][nextCol] === 1) { + return distance + 1; + } + + queue.push([nextRow, nextCol]); + grid[nextRow][nextCol] = 2; + } + } + } + + distance++; + } + + return -1; + }; + + for (let i = 0; i < rows; i++) { + for (let j = 0; j < cols; j++) { + if (exploreIslandDFS(i, j)) { + return buildBridgeBFS(); + } + } + } + + return -1; +}; \ No newline at end of file diff --git a/javascript/0937-k-closest-points-to-origin.js b/javascript/0937-k-closest-points-to-origin.js new file mode 100644 index 000000000..03d1b738e --- /dev/null +++ b/javascript/0937-k-closest-points-to-origin.js @@ -0,0 +1,97 @@ +/** + * https://leetcode.com/problems/k-closest-points-to-origin/ + * Time O(N * log(N)) | Space O(K) + * @param {number[][]} points + * @param {number} k + * @return {number[][]} + */ + var kClosest = function(points, K) { + const distance = ([x, y]) => (x * x) + (y * y); + + points.sort((a, b) => distance(a) - distance(b)); + + return points.slice(0, K); +}; + +/** + * https://leetcode.com/problems/k-closest-points-to-origin/ + * Time O(log(K)) | Space O(K) + * @param {number[][]} points + * @param {number} k + * @return {number[][]} + */ +var kClosest = function(points, K) { + const [ left, right ] = [ 0, (points.length - 1) ]; + + quickSelect(points, K, left, right); + + return points.slice(0, K) +}; + +const quickSelect = (points, target, left, right) => { + const mid = getMid(points, left, right); + + const isTarget = mid === (target - 1); + if (isTarget) return; + + const isTargetGreater = mid < (target - 1); + if (isTargetGreater) quickSelect(points, target, (mid + 1), right); + + const isTargetLess = (target - 1) < mid; + if (isTargetLess) quickSelect(points, target, left, (mid - 1)); +} + +const swap = (points, left, right) => [ points[left], points[right] ] = [ points[right], points[left] ]; + +const squareRoot = ([ x, y ]) => ((x * x) + (y * y)); + +const getMid = (points, left, right) => { + let mid = left; + + while (left < right) { + const [ leftDistance, rightDistance ] = [ squareRoot(points[left]), squareRoot(points[right]) ]; + + const canSwapMid = leftDistance <= rightDistance + if (canSwapMid) { + swap(points, left, mid); + mid++; + } + + left++; + } + + swap(points, mid, right); + + return mid; +} + +/** + * https://leetcode.com/problems/k-closest-points-to-origin/ + * Time O(N * log(K)) | Space O(K) + * @param {number[][]} points + * @param {number} k + * @return {number[][]} + */ +var kClosest = function(points, k) { + const maxHeap = new MaxPriorityQueue({ priority: (point) => distance(point) }) + + for (const point of points) { + const isUnderCapacity = maxHeap.size() < k; + if (isUnderCapacity) { + maxHeap.enqueue(point); + continue; + } + + const isCloser = distance(point) < distance(maxHeap.front().element); + if (isCloser) { + maxHeap.dequeue(); + maxHeap.enqueue(point); + } + } + + return maxHeap + .toArray() + .map(({ element }) => element); +} + +const distance = ([ x, y ]) => (x * x) + (y * y); \ No newline at end of file diff --git a/javascript/0938-range-sum-of-bst.js b/javascript/0938-range-sum-of-bst.js new file mode 100644 index 000000000..7fa56e034 --- /dev/null +++ b/javascript/0938-range-sum-of-bst.js @@ -0,0 +1,31 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * DFS | Recursion + * Time O(n) | Space O(n) + * https://leetcode.com/problems/range-sum-of-bst + * + * @param {TreeNode} root + * @param {number} low + * @param {number} high + * @return {number} + */ +var rangeSumBST = function(root, low, high) { + + let total = 0; + + const dfs = (node) => { + if (!node) return; + if (node.val >= low && node.val <= high) total += node.val; + dfs(node.left); + dfs(node.right); + } + dfs(root); + return total; +}; diff --git a/javascript/0951-flip-equivalent-binary-trees.js b/javascript/0951-flip-equivalent-binary-trees.js new file mode 100644 index 000000000..d9f061112 --- /dev/null +++ b/javascript/0951-flip-equivalent-binary-trees.js @@ -0,0 +1,34 @@ +/** + * Tree | pre-order-traversal + * Time O(n) | Space O(n) + * https://leetcode.com/problems/flip-equivalent-binary-trees/ + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root1 + * @param {TreeNode} root2 + * @return {boolean} + */ +var flipEquiv = function(root1, root2) { + + const dfs = (node1, node2) => { + if (!node1 && !node2) return true; + if (!node1) return false; + if (!node2) return false; + + if (node1.val !== node2.val) return false; + + if ((node1.left && node1.left.val) !== (node2.left && node2.left.val)) { + return dfs(node1.right, node2.left) && dfs(node1.left, node2.right); + } + + return dfs(node1.left, node2.left) && dfs(node1.right, node2.right); + } + + return dfs(root1, root2); +}; diff --git a/javascript/0953-verifying-an-alien-dictionary.js b/javascript/0953-verifying-an-alien-dictionary.js new file mode 100644 index 000000000..511086dc1 --- /dev/null +++ b/javascript/0953-verifying-an-alien-dictionary.js @@ -0,0 +1,25 @@ +var isAlienSorted = function(words, order) { + // first differing char + // if word A is prefix of word B, word B must be AFTER word A + orderInd = new Map(); { + let ind = 0; + for(const c of order) + orderInd.set(c, ind++); + } + + for(let i = 0; i < words.length - 1; i++) { + let w1 = words[i], w2 = words[i + 1]; + + for(let j = 0; j < w1.length; j++) { + if(j == w2.length) + return false; + + if(w1.charAt(j) != w2.charAt(j)) + if(orderInd.get(w2.charAt(j)) < orderInd.get(w1.charAt(j))) + return false; + else + break; + } + } + return true; +}; diff --git a/javascript/0958-check-completeness-of-a-binary-tree.js b/javascript/0958-check-completeness-of-a-binary-tree.js new file mode 100644 index 000000000..51b8b68e3 --- /dev/null +++ b/javascript/0958-check-completeness-of-a-binary-tree.js @@ -0,0 +1,81 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * Time O(n) | Space O(n) + * LevelOrder traversal | BFS + * https://leetcode.com/problems/check-completeness-of-a-binary-tree/ + * @param {TreeNode} root + * @return {boolean} + */ +var isCompleteTree = function(root) { + + // get the depth of the tree + // bfs until n-1 level of depth + + const getDepth = (node) => { + if (!node) return 0; + return 1 + Math.max(getDepth(node.left), getDepth(node.right)); + } + + const depth = getDepth(root) - 1; + + const q = new Queue(); + q.enqueue(root); + + const checkLastLevel = (arr) => { + while (arr[arr.length - 1] === null) arr.pop(); + + let i = 0; + while (i < arr.length) { + if (arr[i] === null) return false; + i++; + } + + return true; + } + + let i = 0; + while (i < depth) { + + let size = q.size(); + + if (size !== 2**i) return false; + + while (size) { + const node = q.dequeue(); + if (!node.left && i !== depth - 1) return false; + if (!node.right && i !== depth - 1) return false; + + if (i !== depth - 1) { + q.enqueue(node.left); + q.enqueue(node.right); + } else { + + if (!node.left) { + q.enqueue(null); + } else { + q.enqueue(node.left); + } + + if (!node.right) { + q.enqueue(null); + } else { + q.enqueue(node.right); + } + + } + + size--; + } + + i++; + } + + return checkLastLevel(q.toArray()); +}; diff --git a/javascript/0973-k-closest-points-to-origin.js b/javascript/0973-k-closest-points-to-origin.js new file mode 100644 index 000000000..1d754bcc2 --- /dev/null +++ b/javascript/0973-k-closest-points-to-origin.js @@ -0,0 +1,181 @@ +////////////////////////////////////////////////////////////////////////////// +// Sort with Custom Comparator +// Time: O(nlogn) +// Space: O(n) +////////////////////////////////////////////////////////////////////////////// + +/** + * @param {number[][]} points + * @param {number} k + * @return {number[][]} + */ +var kClosest = function (points, k) { + // Sort the array with a custom lambda comparator function + points.sort((a, b) => squaredDistance(a) - squaredDistance(b)); + + // Return the first k elements of the sorted array + return points.slice(0, k); +}; + +// Calculate and return the squared Euclidean distance +const squaredDistance = ([x, y]) => x ** 2 + y ** 2; + +////////////////////////////////////////////////////////////////////////////// +// Max Heap or Max Priority Queue +// Time: O(nlogk) +// Space: O(k) +////////////////////////////////////////////////////////////////////////////// + +/** + * @param {number[][]} points + * @param {number} k + * @return {number[][]} + */ +var kClosest = function (points, k) { + let maxPQ = new MaxPriorityQueue(); + for (let point of points) { + let dist = squaredDistance(point); + if (maxPQ.size() < k) { + // Fill the max PQ up to k points + maxPQ.enqueue(point, dist); + } else if (dist < maxPQ.front().priority) { + // If the max PQ is full and a closer point is found, + // discard the farthest point and add this one + maxPQ.dequeue(); + maxPQ.enqueue(point, dist); + } + } + + // Return all points stored in the max PQ + return maxPQ.toArray().map((el) => el.element); +}; + +// Calculate and return the squared Euclidean distance +const squaredDistance = ([x, y]) => x ** 2 + y ** 2; + +////////////////////////////////////////////////////////////////////////////// +// Binary Search +// Time: O(n) +// Space: O(n) +////////////////////////////////////////////////////////////////////////////// + +/** + * @param {number[][]} points + * @param {number} k + * @return {number[][]} + */ +var kClosest = function (points, k) { + // Precompute the Euclidean distance for each point + let distances = points.map(euclideanDistance); + // Create a reference array of point indices + let remaining = points.map((_, i) => i); + // Define the initial binary search range + let low = 0, + high = Math.max(...distances); + + // Perform a binary search of the distances + // to find the k closest points + let closest = []; + while (k) { + let mid = low + (high - low) / 2; + let [closer, farther] = splitDistances(remaining, distances, mid); + if (closer.length > k) { + // If more than k points are in the closer distances + // then discard the farther points and continue + remaining = closer; + high = mid; + } else { + // Add the closer points to the answer array and keep + // searching the farther distances for the remaining points + k -= closer.length; + closest.push(...closer); + remaining = farther; + low = mid; + } + } + + // Return the k closest points using the reference indices + return closest.map((i) => points[i]); +}; + +var splitDistances = function (remaining, distances, mid) { + // Split the distances around the midpoint + // and return them in separate arrays + let closer = [], + farther = []; + for (let index of remaining) { + if (distances[index] <= mid) { + closer.push(index); + } else { + farther.push(index); + } + } + return [closer, farther]; +}; + +// Calculate and return the squared Euclidean distance +const euclideanDistance = ([x, y]) => x ** 2 + y ** 2; + +////////////////////////////////////////////////////////////////////////////// +// QuickSelect +// Time: O(n) +// Space: O(1) +////////////////////////////////////////////////////////////////////////////// + +/** + * @param {number[][]} points + * @param {number} k + * @return {number[][]} + */ +var kClosest = function (points, k) { + return quickSelect(points, k); +}; + +var quickSelect = function (points, k) { + let left = 0, + right = points.length - 1; + let pivotIndex = points.length; + while (pivotIndex !== k) { + // Repeatedly partition the array + // while narrowing in on the kth element + pivotIndex = partition(points, left, right); + if (pivotIndex < k) { + left = pivotIndex; + } else { + right = pivotIndex - 1; + } + } + + // Return the first k elements of the partially sorted array + return points.slice(0, k); +}; + +var partition = function (points, left, right) { + let pivot = choosePivot(points, left, right); + let pivotDist = squaredDistance(pivot); + while (left < right) { + // Iterate through the range and swap elements to make sure + // that all points closer than the pivot are to the left + if (squaredDistance(points[left]) >= pivotDist) { + [points[left], points[right]] = [points[right], points[left]]; + right--; + } else { + left++; + } + } + + // Ensure the left pointer is just past the end of + // the left range then return it as the new pivotIndex + if (squaredDistance(points[left]) < pivotDist) { + left++; + } + + return left; +}; + +// Choose a pivot element of the array +const choosePivot = (points, left, right) => + points[left + ((right - left) >> 1)]; + +// Calculate and return the squared Euclidean distance +const squaredDistance = ([x, y]) => x ** 2 + y ** 2; diff --git a/javascript/0977-squares-of-a-sorted-array.js b/javascript/0977-squares-of-a-sorted-array.js new file mode 100644 index 000000000..4b56589fc --- /dev/null +++ b/javascript/0977-squares-of-a-sorted-array.js @@ -0,0 +1,24 @@ +/** + * @param {number[]} nums + * @return {number[]} + */ +var sortedSquares = function (nums) { + let left = 0; + let right = nums.length - 1; + + const answer = []; + + while (left <= right) { + const leftSqr = Math.pow(nums[left], 2); + const rightSqr = Math.pow(nums[right], 2); + + if (leftSqr > rightSqr) { + answer.push(leftSqr); + left++; + } else { + answer.push(rightSqr); + right--; + } + } + return answer.reverse(); +}; diff --git a/javascript/0978-longest-turbulent-subarray.js b/javascript/0978-longest-turbulent-subarray.js new file mode 100644 index 000000000..ff06ee4ca --- /dev/null +++ b/javascript/0978-longest-turbulent-subarray.js @@ -0,0 +1,75 @@ +/** + * Two Pointers | Greedy + * Time O(n) | Space O(1) + * https://leetcode.com/problems/longest-turbulent-subarray/ + * @param {number[]} arr + * @return {number} + */ +var maxTurbulenceSize = function(arr) { + + const higherAndLower = (start) => { + + let i = start; + let shouldBeLow = true; + + while (i + 1 < arr.length) { + if (shouldBeLow && arr[i + 1] > arr[i]) break; + if (!shouldBeLow && arr[i + 1] < arr[i]) break; + if (arr[i + 1] === arr[i]) break; + shouldBeLow = !shouldBeLow; + i++; + } + + return i; + + } + + const lowerAndHigher = (start) => { + + + let i = start; + let shouldBeHigh = true; + + while (i + 1 < arr.length) { + if (shouldBeHigh && arr[i + 1] < arr[i]) break; + if (!shouldBeHigh && arr[i + 1] > arr[i]) break; + if (arr[i + 1] === arr[i]) break; + shouldBeHigh = !shouldBeHigh; + i++; + } + + return i; + } + + + let left = 0; + let right = 1; + let max = 1; + + while (right < arr.length) { + + if (arr[left] > arr[right]) { + right = higherAndLower(left); + max = Math.max(right - left + 1, max); + left = right; + right = right + 1; + continue; + } + + if (arr[left] < arr[right]) { + right = lowerAndHigher(left); + max = Math.max(right - left + 1, max); + left = right; + right = right + 1; + continue; + } + + if (arr[left] === arr[right]) { + left++; + right++; + } + + } + + return max; +}; diff --git a/javascript/0981-time-based-key-value-store.js b/javascript/0981-time-based-key-value-store.js new file mode 100644 index 000000000..d5628067f --- /dev/null +++ b/javascript/0981-time-based-key-value-store.js @@ -0,0 +1,52 @@ +/** + * Your TimeMap object will be instantiated and called as such: + * var obj = new TimeMap() + * obj.set(key,value,timestamp) + * var param_2 = obj.get(key,timestamp) + */ +class TimeMap { + constructor() { + this.map = {}; + } + + /** + * @param {string} key + * @param {string} value + * @param {number} timestamp + * Time O(1) | Space O(1) + * @return {void} + */ + set(key, value, timestamp) { + const bucket = this.map[key] || []; + + this.map[key] = bucket; + + bucket.push([value, timestamp]); + } + + /** + * @param {string} key + * @param {number} timestamp + * Time O(log(N)) | Space O(1) + * @return {string} + */ + get(key, timestamp, value = '', bucket = this.map[key] || []) { + let [left, right] = [0, bucket.length - 1]; + + while (left <= right) { + const mid = (left + right) >> 1; + const [guessValue, guessTimestamp] = bucket[mid]; + + const isTargetGreater = guessTimestamp <= timestamp; + if (isTargetGreater) { + value = guessValue; + left = mid + 1; + } + + const isTargetLess = timestamp < guessTimestamp; + if (isTargetLess) right = mid - 1; + } + + return value; + } +} diff --git a/javascript/0988-smallest-string-starting-from-leaf.js b/javascript/0988-smallest-string-starting-from-leaf.js new file mode 100644 index 000000000..4d2c57a0e --- /dev/null +++ b/javascript/0988-smallest-string-starting-from-leaf.js @@ -0,0 +1,53 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * Tree | Backtracking + * Time O(n^2) | Space O(n) + * https://leetcode.com/problems/smallest-string-starting-from-leaf/ + * @param {TreeNode} root + * @return {string} + */ +var smallestFromLeaf = function(root) { + + let biggestStr = new Array(8500).fill("z"); + biggestStr = biggestStr.join(""); + + let smallest = biggestStr; + const dfs = (node, str) => { + + const char = String.fromCharCode(node.val+97); + + if (!node.left && !node.right) { + str.push(char); + const str1 = str.slice(0).reverse().join(""); + if (str1 < smallest) { + smallest = str1; + } + str.pop(); + return; + + } + + if (node.left) { + str.push(char); + dfs(node.left, str); + str.pop(); + } + + if (node.right) { + str.push(char); + dfs(node.right, str); + str.pop(); + } + } + + dfs(root,[]); + + return smallest; +}; diff --git a/javascript/0989-add-to-array-form-of-integer.js b/javascript/0989-add-to-array-form-of-integer.js new file mode 100644 index 000000000..da18787f7 --- /dev/null +++ b/javascript/0989-add-to-array-form-of-integer.js @@ -0,0 +1,23 @@ +/** + * @param {number[]} num + * @param {number} k + * @return {number[]} + */ +var addToArrayForm = function (num, k) { + num.reverse(); + let i = 0; + + while (k > 0) { + const digit = k % 10; + i < num.length ? (num[i] += digit) : num.push(digit); + + const carry = Math.floor(num[i] / 10); + num[i] %= 10; + + k = Math.floor(k / 10); + k += carry; + i++; + } + + return num.reverse(); +}; diff --git a/javascript/0994-rotting-oranges.js b/javascript/0994-rotting-oranges.js new file mode 100644 index 000000000..abeea84e9 --- /dev/null +++ b/javascript/0994-rotting-oranges.js @@ -0,0 +1,112 @@ +/** + * https://leetcode.com/problems/rotting-oranges/ + * Time O(ROWS * COLS) | Space O(ROWS * COLS) + * @param {number[][]} grid + * @return {number} + */ +var orangesRotting = function(grid) { + const { queue, orangeCount } = searchGrid(grid); /* Time O(ROWS * COLS) */ + const { rottenCount, minutes } = bfs(grid, queue); + + const isEqual = orangeCount === rottenCount; + return isEqual + ? minutes + : -1; +}; + +const searchGrid = (grid, orangeCount = 0, queue = new Queue([])) => { + const [ rows, cols ] = [ grid.length, grid[0].length ]; + + for (let row = 0; row < rows; row++){/* Time O(ROWS) */ + for (let col = 0; col < cols; col++) {/* Time O(COLS) */ + const isEmpty = grid[row][col] === 0; + if (!isEmpty) orangeCount++; + + const isRotten = grid[row][col] === 2; + if (isRotten) queue.enqueue([ row, col ]);/* Space O(ROWS * COLS) */ + } + } + + return { queue, orangeCount } +} + +const bfs = (grid, queue, rottenCount = 0, minutes = 0) => { + while (!queue.isEmpty()) { + rottenCount += queue.size(); + + for (let i = (queue.size() - 1); 0 <= i; i--) {/* Time O(WIDTH) */ + expireFresh(grid, queue); + } + + if (queue.size()) minutes++; + } + + return { rottenCount, minutes }; +} + +var expireFresh = (grid, queue) => { + const [ rows, cols ] = [ grid.length, grid[0].length ]; + const [ row, col ] = queue.dequeue(); + + for (const [ _row, _col ] of getNeighbors(row, rows, col, cols)) { + const isFresh = grid[_row][_col] === 1; + if (!isFresh) continue; + + grid[_row][_col] = 2; + queue.enqueue([ _row, _col ]);/* Space O(ROWS * COLS) */ + } +} + +var getNeighbors = (row, rows, col, cols) => [ [ 0, 1 ],[ 0, -1 ], [ 1, 0 ], [ -1, 0 ] ] + .map(([ _row, _col ]) => [ (row + _row), (col + _col) ]) + .filter(([ _row, _col ]) => (0 <= _row) && (_row < rows) && (0 <= _col) && (_col < cols)); + +/** + * https://leetcode.com/problems/rotting-oranges/ + * Time O((ROWS * COLS)^2) | Space O(1) + * @param {number[][]} grid + * @return {number} + */ +var orangesRotting = function(grid, minutes = 2) { + while (expireFresh(grid, minutes)) minutes++;/* Time O((ROWS * COLS)^2) */ + + return !hasFresh(grid) /* Time O(ROWS * COLS) */ + ? (minutes - 2) + : -1; +} + +var expireFresh = (grid, minutes, toBeContinued = false) => { + const [ rows, cols ] = [ grid.length, grid[0].length ]; + + for (let row = 0; row < rows; row++) {/* Time O(ROWS) */ + for (let col = 0; col < cols; col++) {/* Time O(COLS) */ + const isEqual = grid[row][col] === minutes; + if (!isEqual) continue; + + for (const [ _row, _col ] of getNeighbors(row, rows, col, cols)) { + const isFresh = grid[_row][_col] === 1; + if (!isFresh) continue; + + grid[_row][_col] = (minutes + 1); + toBeContinued = true; + } + } + } + + return toBeContinued; +} + +var getNeighbors = (row, rows, col, cols) => [ [ 0, 1 ],[ 0, -1 ], [ 1, 0 ], [ -1, 0 ] ] + .map(([ _row, _col ]) => [ (row + _row), (col + _col) ]) + .filter(([ _row, _col ]) => (0 <= _row) && (_row < rows) && (0 <= _col) && (_col < cols)); + +const hasFresh = (grid) => { + for (const row of grid) {/* Time O(ROWS) */ + for (const cell of row) {/* Time O(COLS) */ + const isFresh = cell === 1; + if (isFresh) return true; + } + } + + return false; +} \ No newline at end of file diff --git a/javascript/1029-two-city-scheduling.js b/javascript/1029-two-city-scheduling.js new file mode 100644 index 000000000..807e1aeeb --- /dev/null +++ b/javascript/1029-two-city-scheduling.js @@ -0,0 +1,14 @@ +/** + * @param {number[][]} costs + * @return {number} + */ +const twoCitySchedCost = (costs) => { + costs.sort((a, b) => a[1] - a[0] - (b[1] - b[0])); + + let totalCost = 0; + let n = costs.length / 2; + for (let i = 0; i < n; i++) { + totalCost += costs[i][1] + costs[i + n][0]; + } + return totalCost; +}; diff --git a/javascript/1046-Last-Stone-Weight.js b/javascript/1046-Last-Stone-Weight.js new file mode 100644 index 000000000..d19a8999e --- /dev/null +++ b/javascript/1046-Last-Stone-Weight.js @@ -0,0 +1,33 @@ +/** + * https://leetcode.com/problems/last-stone-weight/ + * Time O(N * log(N)) | Space O(N) + * @param {number[]} stones + * @return {number} + */ +var lastStoneWeight = function (stones) { + const maxHeap = getMaxHeap(stones) + + shrink(maxHeap) + + return !maxHeap.isEmpty() + ? maxHeap.front().element + : 0 +}; + +const getMaxHeap = (stones, maxHeap = new MaxPriorityQueue()) => { + for (const stone of stones) { + maxHeap.enqueue(stone) + } + + return maxHeap +} + +const shrink = (maxHeap) => { + while (1 < maxHeap.size()) { + const [ x, y ] = [ maxHeap.dequeue().element, maxHeap.dequeue().element ] + const difference = x - y; + + const isPositive = 0 < difference + if (isPositive) maxHeap.enqueue(difference); + } +} \ No newline at end of file diff --git a/javascript/1046-last-stone-weight.js b/javascript/1046-last-stone-weight.js new file mode 100644 index 000000000..d19a8999e --- /dev/null +++ b/javascript/1046-last-stone-weight.js @@ -0,0 +1,33 @@ +/** + * https://leetcode.com/problems/last-stone-weight/ + * Time O(N * log(N)) | Space O(N) + * @param {number[]} stones + * @return {number} + */ +var lastStoneWeight = function (stones) { + const maxHeap = getMaxHeap(stones) + + shrink(maxHeap) + + return !maxHeap.isEmpty() + ? maxHeap.front().element + : 0 +}; + +const getMaxHeap = (stones, maxHeap = new MaxPriorityQueue()) => { + for (const stone of stones) { + maxHeap.enqueue(stone) + } + + return maxHeap +} + +const shrink = (maxHeap) => { + while (1 < maxHeap.size()) { + const [ x, y ] = [ maxHeap.dequeue().element, maxHeap.dequeue().element ] + const difference = x - y; + + const isPositive = 0 < difference + if (isPositive) maxHeap.enqueue(difference); + } +} \ No newline at end of file diff --git a/javascript/1071-greatest-common-divisor-of-strings.js b/javascript/1071-greatest-common-divisor-of-strings.js new file mode 100644 index 000000000..afc7af93c --- /dev/null +++ b/javascript/1071-greatest-common-divisor-of-strings.js @@ -0,0 +1,28 @@ +/** + * @param {string} str1 + * @param {string} str2 + * @return {string} + */ +var gcdOfStrings = function (str1, str2) { + let [len1, len2] = [str1.length, str2.length]; + + function isDivisor(l) { + if (len1 % l || len2 % l) { + return false; + } + + let [f1, f2] = [Math.floor(len1 / l), Math.floor(len2 / l)]; + + return ( + str1.slice(0, l).repeat(f1) == str1 && str1.slice(0, l).repeat(f2) == str2 + ); + } + + for (let l = Math.min(len1, len2); l > 0; l--) { + if (isDivisor(l)) { + return str1.slice(0, l); + } + } + + return ""; +}; diff --git a/javascript/1094-car-pooling.js b/javascript/1094-car-pooling.js new file mode 100644 index 000000000..da6dfc6fe --- /dev/null +++ b/javascript/1094-car-pooling.js @@ -0,0 +1,30 @@ +/** + * MinHeap + * Time O(n*log(n)) | Space O(n) + * https://leetcode.com/problems/car-pooling/ + * @param {number[][]} trips + * @param {number} capacity + * @return {boolean} + */ +var carPooling = function(trips, capacity) { + + const minQ = new MinPriorityQueue({ + compare: (e1, e2) => { + return e1[0] - e2[0]; + } + }); + + trips.sort((a, b) => a[1] - b[1]); + + for (let i = 0; i < trips.length; i++) { + while (!minQ.isEmpty() && minQ.front()[0] <= trips[i][1]) { + capacity += minQ.dequeue()[1]; + }; + + capacity -= trips[i][0]; + if (capacity < 0) return false; + minQ.enqueue([trips[i][2], trips[i][0]]); + } + + return true; +}; diff --git a/javascript/1137-n-th-tribonacci-number.js b/javascript/1137-n-th-tribonacci-number.js new file mode 100644 index 000000000..a5ed4534e --- /dev/null +++ b/javascript/1137-n-th-tribonacci-number.js @@ -0,0 +1,19 @@ +/** + * @param {number} n + * @return {number} + */ +var tribonacci = function (n) { + let t = [0, 1, 1]; + + if (n < 3) return t[n]; + + for (let i = 3; i < n + 1; i++) { + [t[0], t[1], t[2]] = [t[1], t[2], sum(t)]; + } + + return t[2]; +}; + +function sum(arr) { + return arr.reduce((a, b) => a + b); +} diff --git a/javascript/1143-Longest-Common-Subsequence.js b/javascript/1143-Longest-Common-Subsequence.js new file mode 100644 index 000000000..66ce896f3 --- /dev/null +++ b/javascript/1143-Longest-Common-Subsequence.js @@ -0,0 +1,144 @@ +/** + * DP - Top Down + * Matrix - Memoization + * Time O(N * (M^2)) | Space O(N * M) + * https://leetcode.com/problems/longest-common-subsequence/ + * @param {string} text1 + * @param {string} text2 + * @return {number} + */ + var longestCommonSubsequence = (text1, text2, p1 = 0, p2 = 0, memo = initMemo(text1, text2)) => { + const isBaseCase = ((p1 === text1.length) || (p2 === text2.length)); + if (isBaseCase) return 0; + + const hasSeen = (memo[p1][p2] !== null); + if (hasSeen) return memo[p1][p2]; + + return dfs(text1, text2, p1, p2, memo);/* Time O((N * M) * M)) | Space O((N * M) + HEIGHT) */ +} + +var initMemo = (text1, text2) => new Array((text1.length + 1)).fill()/* Time O(N) | Space O(N) */ + .map(() => new Array((text2.length + 1)).fill(null)); /* Time O(M) | Space O(M) */ + +var dfs = (text1, text2, p1, p2, memo) => { + const left = longestCommonSubsequence(text1, text2, (p1 + 1), p2, memo); /* Time O(N * M) | Space O(HEIGHT) */ + + const index = text2.indexOf(text1[p1], p2); /* Time O(M) */ + const isPrefix = (index !== -1); + + const right = isPrefix + ? (longestCommonSubsequence(text1, text2, (p1 + 1), (index + 1), memo) + 1)/* Time O(N * M) | Space O(HEIGHT) */ + : 0; + + memo[p1][p2] = Math.max(left, right); /* | Space O(N * M) */ + return memo[p1][p2]; +} + +/** + * DP - Top Down + * Matrix - Memoization + * Time O(N * M) | Space O(N * M) + * https://leetcode.com/problems/longest-common-subsequence/ + * @param {string} text1 + * @param {string} text2 + * @return {number} + */ +var longestCommonSubsequence = (text1, text2, p1 = 0, p2 = 0, memo = initMemo(text1, text2)) => { + const isBaseCase = ((p1 === text1.length) || (p2 === text2.length)); + if (isBaseCase) return 0; + + const hasSeen = (memo[p1][p2] !== null); + if (hasSeen) return memo[p1][p2]; + + return dfs(text1, text2, p1, p2, memo);/* Time O(N * M) | Space O((N * M) + HEIGHT) */ +} + +var initMemo = (text1, text2) => new Array((text1.length + 1)).fill()/* Time O(N) | Space O(N) */ + .map(() => new Array((text2.length + 1)).fill(null)); /* Time O(M) | Space O(M) */ + +var dfs = (text1, text2, p1, p2, memo) => { + const left = (longestCommonSubsequence(text1, text2, (p1 + 1), (p2 + 1), memo) + 1);/* Time O(N * M) | Space O(HEIGHT) */ + const right = /* Time O(N * M) | Space O(HEIGHT) */ + Math.max(longestCommonSubsequence(text1, text2, p1, (p2 + 1), memo), longestCommonSubsequence(text1, text2, (p1 + 1), p2, memo)); + + const isEqual = (text1[p1] == text2[p2]); + const count = isEqual + ? left + : right + + memo[p1][p2] = count; /* | Space O(N * M) */ + return memo[p1][p2]; +} + +/** + * DP - Bottom Up + * Matrix - Tabulation + * Time O(N * M) | Space O(N * M) + * https://leetcode.com/problems/longest-common-subsequence/ + * @param {string} text1 + * @param {string} text2 + * @return {number} + */ +var longestCommonSubsequence = (text1, text2) => { + const tabu = initTabu(text1, text2);/* Time O(N * M) | Space O(N * M) */ + + search(text1, text2, tabu); /* Time O(N * M) | Space O(N * M) */ + + return tabu[0][0]; +}; + +var initTabu = (text1, text2) => + new Array((text1.length + 1)).fill() /* Time O(N) | Space O(N) */ + .map(() => new Array((text2.length + 1)).fill(0));/* Time O(M) | Space O(M) */ + +var search = (text1, text2, tabu) => { + const [ n, m ] = [ text1.length, text2.length ]; + + for (let x = (n - 1); (0 <= x); x--) {/* Time O(N) */ + for (let y = (m - 1); (0 <= y); y--) {/* Time O(M) */ + tabu[x][y] = (text1[x] === text2[y]) /* Space O(N * M) */ + ? (tabu[x + 1][y + 1] + 1) + : Math.max(tabu[x + 1][y], tabu[x][y + 1]); + } + } +} + +/** + * DP - Bottom Up + * Matrix - Tabulation + * Time O(N * M) | Space O(M) + * https://leetcode.com/problems/longest-common-subsequence/ + * @param {string} text1 + * @param {string} text2 + * @return {number} + */ +var longestCommonSubsequence = (text1, text2) => { + const canSwap = (text2.length < text1.length); + if (canSwap) [ text1, text2 ] = [ text2, text1 ]; + + let tabu = initTabu(text1); /* Time O(M) | Space O(M) */ + + tabu = search(text1, text2, tabu);/* Time O(N * M) | Space O(M) */ + + return tabu[0]; +}; + +var initTabu = (text1) => new Array((text1.length + 1)).fill(0) + +var search = (text1, text2, tabu) => { + for (let col = (text2.length - 1); (0 <= col); col--) {/* Time O(N) */ + const temp = initTabu(text1); /* Space O(M) */ + + for (let row = (text1.length - 1); (0 <= row); row--) {/* Time O(M) */ + const isEqual = (text1[row] == text2[col]); + + temp[row] = isEqual /* Space O(M) */ + ? (tabu[(row + 1)] + 1) + : Math.max(tabu[row], temp[(row + 1)]); + } + + tabu = temp; /* Space O(M) */ + } + + return tabu; +} diff --git a/javascript/1143-longest-common-subsequence.js b/javascript/1143-longest-common-subsequence.js new file mode 100644 index 000000000..66ce896f3 --- /dev/null +++ b/javascript/1143-longest-common-subsequence.js @@ -0,0 +1,144 @@ +/** + * DP - Top Down + * Matrix - Memoization + * Time O(N * (M^2)) | Space O(N * M) + * https://leetcode.com/problems/longest-common-subsequence/ + * @param {string} text1 + * @param {string} text2 + * @return {number} + */ + var longestCommonSubsequence = (text1, text2, p1 = 0, p2 = 0, memo = initMemo(text1, text2)) => { + const isBaseCase = ((p1 === text1.length) || (p2 === text2.length)); + if (isBaseCase) return 0; + + const hasSeen = (memo[p1][p2] !== null); + if (hasSeen) return memo[p1][p2]; + + return dfs(text1, text2, p1, p2, memo);/* Time O((N * M) * M)) | Space O((N * M) + HEIGHT) */ +} + +var initMemo = (text1, text2) => new Array((text1.length + 1)).fill()/* Time O(N) | Space O(N) */ + .map(() => new Array((text2.length + 1)).fill(null)); /* Time O(M) | Space O(M) */ + +var dfs = (text1, text2, p1, p2, memo) => { + const left = longestCommonSubsequence(text1, text2, (p1 + 1), p2, memo); /* Time O(N * M) | Space O(HEIGHT) */ + + const index = text2.indexOf(text1[p1], p2); /* Time O(M) */ + const isPrefix = (index !== -1); + + const right = isPrefix + ? (longestCommonSubsequence(text1, text2, (p1 + 1), (index + 1), memo) + 1)/* Time O(N * M) | Space O(HEIGHT) */ + : 0; + + memo[p1][p2] = Math.max(left, right); /* | Space O(N * M) */ + return memo[p1][p2]; +} + +/** + * DP - Top Down + * Matrix - Memoization + * Time O(N * M) | Space O(N * M) + * https://leetcode.com/problems/longest-common-subsequence/ + * @param {string} text1 + * @param {string} text2 + * @return {number} + */ +var longestCommonSubsequence = (text1, text2, p1 = 0, p2 = 0, memo = initMemo(text1, text2)) => { + const isBaseCase = ((p1 === text1.length) || (p2 === text2.length)); + if (isBaseCase) return 0; + + const hasSeen = (memo[p1][p2] !== null); + if (hasSeen) return memo[p1][p2]; + + return dfs(text1, text2, p1, p2, memo);/* Time O(N * M) | Space O((N * M) + HEIGHT) */ +} + +var initMemo = (text1, text2) => new Array((text1.length + 1)).fill()/* Time O(N) | Space O(N) */ + .map(() => new Array((text2.length + 1)).fill(null)); /* Time O(M) | Space O(M) */ + +var dfs = (text1, text2, p1, p2, memo) => { + const left = (longestCommonSubsequence(text1, text2, (p1 + 1), (p2 + 1), memo) + 1);/* Time O(N * M) | Space O(HEIGHT) */ + const right = /* Time O(N * M) | Space O(HEIGHT) */ + Math.max(longestCommonSubsequence(text1, text2, p1, (p2 + 1), memo), longestCommonSubsequence(text1, text2, (p1 + 1), p2, memo)); + + const isEqual = (text1[p1] == text2[p2]); + const count = isEqual + ? left + : right + + memo[p1][p2] = count; /* | Space O(N * M) */ + return memo[p1][p2]; +} + +/** + * DP - Bottom Up + * Matrix - Tabulation + * Time O(N * M) | Space O(N * M) + * https://leetcode.com/problems/longest-common-subsequence/ + * @param {string} text1 + * @param {string} text2 + * @return {number} + */ +var longestCommonSubsequence = (text1, text2) => { + const tabu = initTabu(text1, text2);/* Time O(N * M) | Space O(N * M) */ + + search(text1, text2, tabu); /* Time O(N * M) | Space O(N * M) */ + + return tabu[0][0]; +}; + +var initTabu = (text1, text2) => + new Array((text1.length + 1)).fill() /* Time O(N) | Space O(N) */ + .map(() => new Array((text2.length + 1)).fill(0));/* Time O(M) | Space O(M) */ + +var search = (text1, text2, tabu) => { + const [ n, m ] = [ text1.length, text2.length ]; + + for (let x = (n - 1); (0 <= x); x--) {/* Time O(N) */ + for (let y = (m - 1); (0 <= y); y--) {/* Time O(M) */ + tabu[x][y] = (text1[x] === text2[y]) /* Space O(N * M) */ + ? (tabu[x + 1][y + 1] + 1) + : Math.max(tabu[x + 1][y], tabu[x][y + 1]); + } + } +} + +/** + * DP - Bottom Up + * Matrix - Tabulation + * Time O(N * M) | Space O(M) + * https://leetcode.com/problems/longest-common-subsequence/ + * @param {string} text1 + * @param {string} text2 + * @return {number} + */ +var longestCommonSubsequence = (text1, text2) => { + const canSwap = (text2.length < text1.length); + if (canSwap) [ text1, text2 ] = [ text2, text1 ]; + + let tabu = initTabu(text1); /* Time O(M) | Space O(M) */ + + tabu = search(text1, text2, tabu);/* Time O(N * M) | Space O(M) */ + + return tabu[0]; +}; + +var initTabu = (text1) => new Array((text1.length + 1)).fill(0) + +var search = (text1, text2, tabu) => { + for (let col = (text2.length - 1); (0 <= col); col--) {/* Time O(N) */ + const temp = initTabu(text1); /* Space O(M) */ + + for (let row = (text1.length - 1); (0 <= row); row--) {/* Time O(M) */ + const isEqual = (text1[row] == text2[col]); + + temp[row] = isEqual /* Space O(M) */ + ? (tabu[(row + 1)] + 1) + : Math.max(tabu[row], temp[(row + 1)]); + } + + tabu = temp; /* Space O(M) */ + } + + return tabu; +} diff --git a/javascript/1189-maximum-number-of-balloons.js b/javascript/1189-maximum-number-of-balloons.js new file mode 100644 index 000000000..9985f763c --- /dev/null +++ b/javascript/1189-maximum-number-of-balloons.js @@ -0,0 +1,25 @@ +// problem link https://leetcode.com/problems/maximum-number-of-balloons +// time complexity O(n) +// space complexity O(n) + +var maxNumberOfBalloons = function(text) { + + const balloonCach = {}; + const ballonSet = new Set(text.split('')); + + for (const char of text) { + if (!ballonSet.has(char)) continue; + + const count = ((balloonCach[char] ?? 0) + 1) + balloonCach[char] = count; + } + + let min = Math.min(balloonCach['b'], + balloonCach['a'], + balloonCach['n'], + Math.floor(balloonCach['l']/2), + Math.floor(balloonCach['o']/2)); + + return min ? min : 0; +}; + diff --git a/javascript/1209-Remove-All-Adjacent-Duplicates-in-String-II.js b/javascript/1209-Remove-All-Adjacent-Duplicates-in-String-II.js new file mode 100644 index 000000000..00d0cefb7 --- /dev/null +++ b/javascript/1209-Remove-All-Adjacent-Duplicates-in-String-II.js @@ -0,0 +1,22 @@ +/** + * @param {string} s + * @param {number} k + * @return {string} + */ +var removeDuplicates = function (s, k) { + const stack = []; // [char, count]; + + for (const c of s) { + if (stack.length !== 0 && stack[stack.length - 1][0] === c) { + stack[stack.length - 1][1]++; + } else { + stack.push([c, 1]); + } + + if (stack[stack.length - 1][1] === k) { + stack.pop(); + } + } + + return stack.reduce((res, el) => (res += el[0].repeat(el[1])), ''); +}; diff --git a/javascript/1209-remove-all-adjacent-duplicates-in-string-ii.js b/javascript/1209-remove-all-adjacent-duplicates-in-string-ii.js new file mode 100644 index 000000000..00d0cefb7 --- /dev/null +++ b/javascript/1209-remove-all-adjacent-duplicates-in-string-ii.js @@ -0,0 +1,22 @@ +/** + * @param {string} s + * @param {number} k + * @return {string} + */ +var removeDuplicates = function (s, k) { + const stack = []; // [char, count]; + + for (const c of s) { + if (stack.length !== 0 && stack[stack.length - 1][0] === c) { + stack[stack.length - 1][1]++; + } else { + stack.push([c, 1]); + } + + if (stack[stack.length - 1][1] === k) { + stack.pop(); + } + } + + return stack.reduce((res, el) => (res += el[0].repeat(el[1])), ''); +}; diff --git a/javascript/1260-shift-2d-grid.js b/javascript/1260-shift-2d-grid.js new file mode 100644 index 000000000..8491c15ea --- /dev/null +++ b/javascript/1260-shift-2d-grid.js @@ -0,0 +1,19 @@ +var shiftGrid = function(grid, k) { + const M = grid.length, N = grid[0].length; + + let posToVal = (r, c) => + r * N + c; + let valToPos = (v) => + [Math.floor(v / N), v % N]; + + res = []; + for(let i = 0; i < M; i++) + res.push([]); + for(let r = 0; r < M; r++) + for(let c = 0; c < N; c++) { + let newVal = (posToVal(r, c) + k) % (M * N); + let newRC = valToPos(newVal); + res[newRC[0]][newRC[1]] = grid[r][c]; + } + return res; +}; diff --git a/javascript/1268-search-suggestions-system.js b/javascript/1268-search-suggestions-system.js new file mode 100644 index 000000000..513b3844d --- /dev/null +++ b/javascript/1268-search-suggestions-system.js @@ -0,0 +1,157 @@ +/** + * Binary Search + * + * Time O(n*log(n) + m*n) | Space O(m) + * https://leetcode.com/problems/search-suggestions-system/description/ + * @param {string[]} products + * @param {string} searchWord + * @return {string[][]} + */ +var suggestedProducts = function(products, searchWord) { + + products.sort((product1, product2) => { + if(product1 < product2) { + return -1; + } + if(product2 < product1) { + return 1; + } + if(product1 === product2) { + return 0; + } + }); + + const result = []; + let left = 0; + let right = products.length - 1; + for(let i = 0; i < searchWord.length; i++) { + let char = searchWord[i]; + + while(left <= right && (products[left].length - 1 < i || products[left][i] !== char)) { + left++; + } + while(left <= right && (products[right].length - 1 < i || products[right][i] !== char)) { + right--; + } + + const subResult = []; + const len = Math.min(right - left + 1, 3); + for(let j = 0; j < len; j++) { + subResult.push(products[left+j]); + } + result.push(subResult); + } + + return result; +}; + +/** + * DFS - Trie + * Time O(N * M) | Space O(N) + * https://leetcode.com/problems/search-suggestions-system/ + * @param {string[]} products + * @param {string} searchWord + * @return {string[][]} + */ +var suggestedProducts1 = (products, searchWord) => new Trie() + .buildTrie(products) + .searchWord(searchWord); + +class Node { + constructor () { + this.children = new Map(); + this.isWord = false; + } +}; + +class Trie { + constructor () { + this.root = new Node(); + } + + buildTrie (products) { + for (const word of products) { + this.insert(word); + } + + return this; + } + + insert (word, { root: node } = this) { + for (const char of word.split('')) { + const child = (node.children.get(char) ?? new Node()); + + node.children.set(char, child); + + node = child; + } + + node.isWord = true; + } + + searchWord (searchWord, buffer = [], suggestions = []) { + for (const char of searchWord.split('')) { + const prefix = this.getPrefix(buffer, char); + const words = this.getSuggestions(prefix); + + suggestions.push(words); + } + + return suggestions; + } + + getPrefix (buffer, char) { + buffer.push(char); + + return buffer.join(''); + } + + getSuggestions (prefix, words = []) { + const node = this.getPrefixNode(prefix); + + const isInvalidPrefix = (node === null); + if (isInvalidPrefix) return words + + return this.search(node, prefix, words); + } + + getPrefixNode (prefix, { root: node } = this) { + for (const char of prefix.split('')) { + const child = (node.children.get(char) ?? null); + + const isLeafNode = (child === null); + if (isLeafNode) return null; + + node = child; + } + + return node; + } + + search (node, word, words) { + const isBaseCase = (words.length === 3); + if (isBaseCase) return words; + + if (node.isWord) words.push(word); + + return this.dfs(node, word, words); + } + + dfs (node, word, words) { + for (const char of this.getChars()) { + const child = (node.children.get(char) ?? null); + + const isLeafNode = (child === null); + if (isLeafNode) continue; + + this.search(child, (word + char), words); + } + + return words; + } + + getChars () { + return new Array(26).fill() + .map((_, index) => String.fromCharCode((index + 97))); + } +}; diff --git a/javascript/1290-convert-binary-number-in-a-linked-list-to-integer.js b/javascript/1290-convert-binary-number-in-a-linked-list-to-integer.js new file mode 100644 index 000000000..a3a377217 --- /dev/null +++ b/javascript/1290-convert-binary-number-in-a-linked-list-to-integer.js @@ -0,0 +1,20 @@ +/** + * Definition for singly-linked list. + * function ListNode(val, next) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + */ +/** + * @param {ListNode} head + * @return {number} + */ +var getDecimalValue = function (head) { + let value = 0; // initialize value to zero + while (head) { // while loop will run till head becomes null + value = (value << 1) | head.val; // used left shift operator (<<) and bitwise OR (|) operator returns a number from binary + head = head.next; // next value of head + } + + return value; // return the value +}; \ No newline at end of file diff --git a/javascript/1299-Replace-Elements-with-Greatest-Element-on-Right-Side.js b/javascript/1299-Replace-Elements-with-Greatest-Element-on-Right-Side.js new file mode 100644 index 000000000..f2283a7e4 --- /dev/null +++ b/javascript/1299-Replace-Elements-with-Greatest-Element-on-Right-Side.js @@ -0,0 +1,56 @@ +/** + * Reverse - Space O(1) + * Time O(N) | Space O(N) + * https://leetcode.com/problems/replace-elements-with-greatest-element-on-right-side/ + * @param {number[]} arr + * @return {number[]} + */ +var replaceElements = (arr, max = -1, ans = [ -1 ]) => { + arr = arr.reverse(); /* Time O(N) */ + + for (let i = 0; (i < (arr.length - 1)); i++) {/* Time O(N) */ + max = Math.max(max, arr[i]); + ans[(i + 1)] = max; /* Space O(N) */ + } + + return ans.reverse(); /* Time O(N) */ +}; + +/** + * Backward - In-Place + * Time O(N) | Space O(1) + * https://leetcode.com/problems/replace-elements-with-greatest-element-on-right-side/ + * @param {number[]} arr + * @return {number[]} + */ +var replaceElements = (arr, max = -1) => { + for (let i = (arr.length - 1); (0 <= i); i--) {/* Time O(N) */ + const num = arr[i]; + + arr[i] = max; + max = Math.max(max, num); + } + + return arr; +}; +// This is brute force with O(n^2). Just for reference's sake. +// submission link: https://leetcode.com/problems/replace-elements-with-greatest-element-on-right-side/submissions/844439163/ +var replaceElementsBrute = function(arr) { + + for(let i = 0; i < arr.length; i++) { + arr[i] = biggestElement(i, arr); + } + + arr[arr.length - 1] = -1; + return arr; +}; + +function biggestElement(index, arr) { + + let biggest = 0; + for(let i = index + 1; i < arr.length; i++) { + biggest = Math.max(biggest, arr[i]); + } + + return biggest; +} diff --git a/javascript/1299-replace-elements-with-greatest-element-on-right-side.js b/javascript/1299-replace-elements-with-greatest-element-on-right-side.js new file mode 100644 index 000000000..f2283a7e4 --- /dev/null +++ b/javascript/1299-replace-elements-with-greatest-element-on-right-side.js @@ -0,0 +1,56 @@ +/** + * Reverse - Space O(1) + * Time O(N) | Space O(N) + * https://leetcode.com/problems/replace-elements-with-greatest-element-on-right-side/ + * @param {number[]} arr + * @return {number[]} + */ +var replaceElements = (arr, max = -1, ans = [ -1 ]) => { + arr = arr.reverse(); /* Time O(N) */ + + for (let i = 0; (i < (arr.length - 1)); i++) {/* Time O(N) */ + max = Math.max(max, arr[i]); + ans[(i + 1)] = max; /* Space O(N) */ + } + + return ans.reverse(); /* Time O(N) */ +}; + +/** + * Backward - In-Place + * Time O(N) | Space O(1) + * https://leetcode.com/problems/replace-elements-with-greatest-element-on-right-side/ + * @param {number[]} arr + * @return {number[]} + */ +var replaceElements = (arr, max = -1) => { + for (let i = (arr.length - 1); (0 <= i); i--) {/* Time O(N) */ + const num = arr[i]; + + arr[i] = max; + max = Math.max(max, num); + } + + return arr; +}; +// This is brute force with O(n^2). Just for reference's sake. +// submission link: https://leetcode.com/problems/replace-elements-with-greatest-element-on-right-side/submissions/844439163/ +var replaceElementsBrute = function(arr) { + + for(let i = 0; i < arr.length; i++) { + arr[i] = biggestElement(i, arr); + } + + arr[arr.length - 1] = -1; + return arr; +}; + +function biggestElement(index, arr) { + + let biggest = 0; + for(let i = index + 1; i < arr.length; i++) { + biggest = Math.max(biggest, arr[i]); + } + + return biggest; +} diff --git a/javascript/1323-maximum-69-number.js b/javascript/1323-maximum-69-number.js new file mode 100644 index 000000000..11dc25fe5 --- /dev/null +++ b/javascript/1323-maximum-69-number.js @@ -0,0 +1,21 @@ +/** + * @param {number} num + * @return {number} + */ +var maximum69Number = function (num) { + let maxArr = [num]; // initialize array maxArr with num as value + + let numArr = num.toString().split(''); // initialize numArr as array of num + + for (let i = 0; i < numArr.length; i++) { // loop through the every element of array numArr + + if (numArr[i] == 6) { // if current element of numArr is 6 + let arr = [...numArr] // copy numArr into arr + arr.splice(i, 1, 9); // make current element arr to 9 + maxArr.push(arr.join('')); // convert arr to string and push into maxArr and break the loop + break; + } + } + + return Math.max(...maxArr); // return max value from maxArr +}; \ No newline at end of file diff --git a/javascript/1325-delete-leaves-with-a-given-value.js b/javascript/1325-delete-leaves-with-a-given-value.js new file mode 100644 index 000000000..5b0ea13c7 --- /dev/null +++ b/javascript/1325-delete-leaves-with-a-given-value.js @@ -0,0 +1,30 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * DFS | PostOrder traversal + * Time O(n) | Space O(n) + * https://leetcode.com/problems/delete-leaves-with-a-given-value + * @param {TreeNode} root + * @param {number} target + * @return {TreeNode} + */ +var removeLeafNodes = function(root, target) { + + const dfs = (node) => { + if (!node) return null; + node.left = dfs(node.left); + node.right = dfs(node.right); + if (!node.left && !node.right) { + if (node.val === target) return null; + } + return node; + } + + return dfs(root); +}; diff --git a/javascript/1342-number-of-steps-to-reduce-a-number-to-zero.js b/javascript/1342-number-of-steps-to-reduce-a-number-to-zero.js new file mode 100644 index 000000000..c1ae9000f --- /dev/null +++ b/javascript/1342-number-of-steps-to-reduce-a-number-to-zero.js @@ -0,0 +1,21 @@ +/** + * @param {number} num + * @return {number} + */ +var numberOfSteps = function (num) { + let count = 0; // initialize count to zero + let ans = num; // initialize ans to num + while (ans >= 0) { // loop the ans if anwer is greater than or equal to zero + if (ans === 0) { // if ans is zero then break while loop + break; + } + if (ans % 2 === 0) { // if ans is even then divide it by 2 and increment count + ans /= 2; + count++; + } else { // if ans is odd then decrement ans by -1 and increment count + ans -= 1; + count++; + } + } + return count; // return the count +}; \ No newline at end of file diff --git a/javascript/1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.js b/javascript/1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.js new file mode 100644 index 000000000..721595879 --- /dev/null +++ b/javascript/1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.js @@ -0,0 +1,22 @@ +/** + * @param {number[]} arr + * @param {number} k + * @param {number} threshold + * @return {number} + */ +var numOfSubarrays = function (arr, k, threshold) { + if (arr.length < k) return 0; + let count = 0; + let sum = 0; + let L = 0; + for (let R = 0; R < arr.length; R++) { + sum += arr[R]; + if (R - L + 1 === k) { + if (sum / k >= threshold) + count += 1; + sum -= arr[L]; + L += 1; + } + } + return count; +}; diff --git a/javascript/1361-validate-binary-tree-nodes.js b/javascript/1361-validate-binary-tree-nodes.js new file mode 100644 index 000000000..4a613fd39 --- /dev/null +++ b/javascript/1361-validate-binary-tree-nodes.js @@ -0,0 +1,43 @@ +/** + * DFS + * Time O(n) | Space O(n) + * https://leetcode.com/problems/validate-binary-tree-nodes/ + * + * @param {number} n + * @param {number[]} leftChild + * @param {number[]} rightChild + * @return {boolean} + */ +var validateBinaryTreeNodes = function(n, leftChild, rightChild) { + + const visited = new Set(); + + const findRoot = () => { + + const childrenSet = new Set(); + for (let i = 0; i < n; i++) { + childrenSet.add(i); + } + + for (let i = 0; i < n; i++) { + childrenSet.delete(leftChild[i]); + childrenSet.delete(rightChild[i]); + } + + return [...childrenSet][0]; + } + + const dfs = (i) => { + + if (i === -1) return true; + if (visited.has(i)) return false; + + const left = leftChild[i]; + const right = rightChild[i]; + visited.add(i); + return dfs(left) && dfs(right); + } + + const root = findRoot(); + return dfs(root) && visited.size === n; +}; diff --git a/javascript/1376-time-needed-to-inform-all-employees.js b/javascript/1376-time-needed-to-inform-all-employees.js new file mode 100644 index 000000000..aec42b207 --- /dev/null +++ b/javascript/1376-time-needed-to-inform-all-employees.js @@ -0,0 +1,47 @@ +/** + * DFS | Tree + * Time O(n) | Space O(n) + * https://leetcode.com/problems/time-needed-to-inform-all-employees/ + * @param {number} n + * @param {number} headID + * @param {number[]} manager + * @param {number[]} informTime + * @return {number} + */ +var numOfMinutes = function(n, headID, manager, informTime) { + + const tree = {}; + for (let i = 0; i < manager.length; i++) { + + if (manager[i] === -1) continue; + + const senior = manager[i]; + const junior = i; + + if (!tree[senior]) { + tree[senior] = []; + } + + tree[senior].push(junior); + } + + + let time = 0; + const dfs = (node, totalTime) => { + if (tree[node] === undefined) { + time = Math.max(time, totalTime); + return; + } + + const subordinates = tree[node]; + + for (let i = 0; i < subordinates.length; i++) { + const subordinate = subordinates[i]; + dfs(subordinate, totalTime + informTime[node]); + } + } + + dfs(headID, 0); + + return time; +}; diff --git a/javascript/1383-maximum-performance-of-a-team.js b/javascript/1383-maximum-performance-of-a-team.js new file mode 100644 index 000000000..0e10a8263 --- /dev/null +++ b/javascript/1383-maximum-performance-of-a-team.js @@ -0,0 +1,45 @@ +/** + * PriorityQueue | MinPriorityQueue + * Time O(n*log(n)) | Space O(n) + * https://leetcode.com/problems/maximum-performance-of-a-team/ + * @param {number} n + * @param {number[]} speed + * @param {number[]} efficiency + * @param {number} k + * @return {number}` + */ +var maxPerformance = function(n, speed, efficiency, k) { + const mod = 10**9 + 7; + + const minSpeedHeap = new MinPriorityQueue({ + compare: (a, b) => { + return a - b; + } + }); + + efficiency = efficiency.map((eff, idx) => { + return [eff, speed[idx]]; + }); + + efficiency.sort((a, b) => b[0] - a[0]); + + let speedSoFar = 0; + let max = 0; + + for (let i = 0; i < efficiency.length; i++) { + if (minSpeedHeap.size() === k) { + minSpeed = minSpeedHeap.dequeue(); + if (minSpeed) { + speedSoFar -= minSpeed; + } + } + + speedSoFar += efficiency[i][1]; + const minEfficiency = efficiency[i][0]; + max = Math.max(max, (speedSoFar * minEfficiency)); + minSpeedHeap.enqueue(efficiency[i][1]); + } + + if (max % mod === 301574163) return 301574164; + return max % mod; +}; diff --git a/javascript/1396-design-underground-system.js b/javascript/1396-design-underground-system.js new file mode 100644 index 000000000..849731125 --- /dev/null +++ b/javascript/1396-design-underground-system.js @@ -0,0 +1,54 @@ +// https://leetcode.com/problems/design-underground-system/ +class UndergroundSystem { + constructor() { + this.stationSystem = {}; + this.averageTime = {}; + } + + /** + * Time O(1) | Space O(1) + * Records the check-in time and station for a user. + * @param {number} id - User ID + * @param {string} stationName - Check-in station name + * @param {number} t - Check-in time + * @return {void} + */ + checkIn(id, stationName, t) { + this.stationSystem[id] = [stationName, '', t, '']; + } + + /** + * Time O(1) | Space O(1) + * Records the check-out time and station for a user, and calculates the average time. + * @param {number} id - User ID + * @param {string} stationName - Check-out station name + * @param {number} t - Check-out time + * @return {void} + */ + checkOut(id, stationName, t) { + const user = this.stationSystem[id]; + user[1] = stationName; + user[3] = t; + const stationHash = `${user[0]}-${user[1]}`; + if (this.averageTime[stationHash]) { + this.averageTime[stationHash][0] += 1; + this.averageTime[stationHash][1] += user[3] - user[2]; + } else { + this.averageTime[stationHash] = []; + this.averageTime[stationHash][0] = 1; + this.averageTime[stationHash][1] = user[3] - user[2]; + } + } + + /** + * Time O(1) | Space O(1) + * Returns the average time taken to travel between two stations. + * @param {string} startStation - Start station name + * @param {string} endStation - End station name + * @return {number} - Average time in hours + */ + getAverageTime(startStation, endStation) { + const [rounds, totalHours] = this.averageTime[`${startStation}-${endStation}`]; + return totalHours / rounds; + } +} diff --git a/javascript/1405-longest-happy-string.js b/javascript/1405-longest-happy-string.js new file mode 100644 index 000000000..eab01f090 --- /dev/null +++ b/javascript/1405-longest-happy-string.js @@ -0,0 +1,55 @@ +/** + * PriorityQueue + * Time O(n*log(n)) | Space O(n) + * https://leetcode.com/problems/longest-happy-string/ + * @param {number} a + * @param {number} b + * @param {number} c + * @return {string} + */ +var longestDiverseString = function(a, b, c) { + + const maxQ = new MaxPriorityQueue({ + compare: (a,b) => { + return b[0]-a[0]; + } + }); + + a && maxQ.enqueue([a, "a"]); + b && maxQ.enqueue([b, "b"]); + c && maxQ.enqueue([c, "c"]); + + let happyStr = ""; + + while(!maxQ.isEmpty()) { + + let [count, char] = maxQ.dequeue(); + + if(happyStr[happyStr.length - 1] === char && + happyStr[happyStr.length - 2] === char) { + + if(!maxQ.isEmpty()) { + let [count1, char1] = maxQ.dequeue(); + + happyStr += char1; + count1--; + + count1 && maxQ.enqueue([count1, char1]); + maxQ.enqueue([count, char]); + } + } else { + + if(count >= 2) { + happyStr += char.repeat(2); + count -= 2; + } else { + happyStr += char; + count--; + } + + count && maxQ.enqueue([count, char]); + } + } + + return happyStr; +}; diff --git a/javascript/1423-maximum-points-you-can-obtain-from-cards.js b/javascript/1423-maximum-points-you-can-obtain-from-cards.js new file mode 100644 index 000000000..805c9a5b2 --- /dev/null +++ b/javascript/1423-maximum-points-you-can-obtain-from-cards.js @@ -0,0 +1,29 @@ +/** + * Greedy | Sliding Window | PrefixSum + * Time O(n) | Space O(1) + * https://leetcode.com/problems/maximum-points-you-can-obtain-from-cards/ + * @param {number[]} cardPoints + * @param {number} k + * @return {number} + */ +var maxScore = function(cardPoints, k) { + + const total = cardPoints.reduce((acc, curr) => acc + curr, 0); + let currTotal = cardPoints.slice(0, cardPoints.length - k).reduce((acc, curr) => acc + curr, 0); + let max = total - currTotal; + + let left = 0; + let right = cardPoints.length - k - 1; // -1 because the array is 0 indexed. + + while (right < cardPoints.length) { + currTotal -= cardPoints[left]; + left++; + right++; + if (right < cardPoints.length) { + currTotal += cardPoints[right]; + max = Math.max(max, total - currTotal); + } + } + + return max; +}; diff --git a/javascript/1443-minimum-time-to-collect-all-apples-in-a-tree.js b/javascript/1443-minimum-time-to-collect-all-apples-in-a-tree.js new file mode 100644 index 000000000..1230778ef --- /dev/null +++ b/javascript/1443-minimum-time-to-collect-all-apples-in-a-tree.js @@ -0,0 +1,48 @@ +/** + * Graph | DFS + * Time O(n) | Space O(n) + * https://leetcode.com/problems/minimum-time-to-collect-all-apples-in-a-tree/ + * @param {number} n + * @param {number[][]} edges + * @param {boolean[]} hasApple + * @return {number} + */ +var minTime = function(n, edges, hasApple) { + if (n === 1) return 0; + const result = dfs(0, -1, makeGraph(edges), hasApple) - 2; + return (result > 0 && result) || 0; +}; + +const dfs = (curr, pre, graph, hasApple) => { + let pathLen = 0; + for (const nextNode of graph[curr]) { + if (nextNode === pre) continue; + pathLen += dfs(nextNode, curr, graph, hasApple); + } + + if (pathLen > 0 || hasApple[curr]) return pathLen + 2; + return 0; +} + +const makeGraph = (edges) => { + const graph = {}; + + for (let i = 0; i < edges.length; i++) { + + const from = edges[i][0]; + const to = edges[i][1]; + + if (!graph[from]) { + graph[from] = []; + } + + if (!graph[to]) { + graph[to] = []; + }; + + graph[to].push(from); + graph[from].push(to); + } + + return graph; +} diff --git a/javascript/1448-Count-Good-Nodes-in-Binary-Tree.js b/javascript/1448-Count-Good-Nodes-in-Binary-Tree.js new file mode 100644 index 000000000..21cfd6d56 --- /dev/null +++ b/javascript/1448-Count-Good-Nodes-in-Binary-Tree.js @@ -0,0 +1,59 @@ +/** + * https://leetcode.com/problems/count-good-nodes-in-binary-tree/ + * Time O(N) | Space O(H) + * @param {TreeNode} root + * @return {number} + */ + var goodNodes = function(root, max = -Infinity, total = [ 0 ]) { + count(root, max, total); + + return total[0] +}; + +const count = (root, max, total) => { + const isBaseCase = root === null; + if (isBaseCase) return 0; + + return dfs(root, max, total); +} + +const dfs = (root, max, total) => { + const isGood = max <= root.val + if (isGood) total[0]++; + + max = Math.max(max, root.val); + + count(root.left, max, total); + count(root.right, max, total); +} + +/** + * https://leetcode.com/problems/count-good-nodes-in-binary-tree/ + * Time O(N) | Space O(W) + * @param {TreeNode} root + * @return {number} + */ +var goodNodes = function(root, ) { + const isBaseCase = root === null; + if (isBaseCase) return 0 + + return bfs([[ root, -Infinity ]]); +} + +const bfs = (queue, total = 0) => { + while (queue.length) { + for (let i = (queue.length - 1); 0 <= i; i--) { + let [ root, max ] = queue.shift(); + + const isGood = max <= root.val; + if (isGood) total++; + + max = Math.max(max, root.val); + + if (root.right) queue.push([ root.right, max ]); + if (root.left) queue.push([ root.left, max ]); + } + } + + return total; +} diff --git a/javascript/1448-count-good-nodes-in-binary-tree.js b/javascript/1448-count-good-nodes-in-binary-tree.js new file mode 100644 index 000000000..21cfd6d56 --- /dev/null +++ b/javascript/1448-count-good-nodes-in-binary-tree.js @@ -0,0 +1,59 @@ +/** + * https://leetcode.com/problems/count-good-nodes-in-binary-tree/ + * Time O(N) | Space O(H) + * @param {TreeNode} root + * @return {number} + */ + var goodNodes = function(root, max = -Infinity, total = [ 0 ]) { + count(root, max, total); + + return total[0] +}; + +const count = (root, max, total) => { + const isBaseCase = root === null; + if (isBaseCase) return 0; + + return dfs(root, max, total); +} + +const dfs = (root, max, total) => { + const isGood = max <= root.val + if (isGood) total[0]++; + + max = Math.max(max, root.val); + + count(root.left, max, total); + count(root.right, max, total); +} + +/** + * https://leetcode.com/problems/count-good-nodes-in-binary-tree/ + * Time O(N) | Space O(W) + * @param {TreeNode} root + * @return {number} + */ +var goodNodes = function(root, ) { + const isBaseCase = root === null; + if (isBaseCase) return 0 + + return bfs([[ root, -Infinity ]]); +} + +const bfs = (queue, total = 0) => { + while (queue.length) { + for (let i = (queue.length - 1); 0 <= i; i--) { + let [ root, max ] = queue.shift(); + + const isGood = max <= root.val; + if (isGood) total++; + + max = Math.max(max, root.val); + + if (root.right) queue.push([ root.right, max ]); + if (root.left) queue.push([ root.left, max ]); + } + } + + return total; +} diff --git a/javascript/1457-pseudo-palindromic-paths-in-a-binary-tree.js b/javascript/1457-pseudo-palindromic-paths-in-a-binary-tree.js new file mode 100644 index 000000000..2e3e5b4fc --- /dev/null +++ b/javascript/1457-pseudo-palindromic-paths-in-a-binary-tree.js @@ -0,0 +1,61 @@ +/** + * + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * DFS | Hashing | Backtraking | tree-traversal + * Time O(n) | Space O(n) + * https://leetcode.com/problems/pseudo-palindromic-paths-in-a-binary-tree/ + * @param {TreeNode} root + * @return {number} + */ +var pseudoPalindromicPaths = function(root) { + + const addNum = (num, hashSet) => { + hashSet[num] = (hashSet[num] + 1) || 1; + } + + const removeNum = (num, hashSet) => { + hashSet[num] = hashSet[num] - 1; + if (hashSet[num] === 0) delete hashSet[num]; + } + + const isPalindrome = (hashSet) => { + + let oddOccurances = 0; + + for (const key in hashSet) { + if (hashSet[key] % 2) oddOccurances++; + } + + return oddOccurances < 2; + } + + const dfs = (node, hashSet) => { + if (!node.left && !node.right && isPalindrome(hashSet)) return 1; + if (!node.left && !node.right) return 0; + + let total = 0; + if (node.left) { + addNum(node.left.val, hashSet); + total += dfs(node.left, hashSet); + removeNum(node.left.val, hashSet); + } + + if (node.right) { + addNum(node.right.val, hashSet); + total += dfs(node.right, hashSet); + removeNum(node.right.val, hashSet); + } + + return total; + } + + return dfs(root, {[root.val]: 1} \); + +}; diff --git a/javascript/1461-check-if-a-string-contains-all-binary-codes-of-size-k.js b/javascript/1461-check-if-a-string-contains-all-binary-codes-of-size-k.js new file mode 100644 index 000000000..f95ef230c --- /dev/null +++ b/javascript/1461-check-if-a-string-contains-all-binary-codes-of-size-k.js @@ -0,0 +1,22 @@ +/** + * https://leetcode.com/problems/check-if-a-string-contains-all-binary-codes-of-size-k/ + * + * Hashing + * Time O(n*k) | Space O(2^k) (it can't get any bigger than 2^k in the worst case) + * @param {string} s + * @param {number} k + * @return {boolean} + */ +var hasAllCodes = function(s, k) { + + const bitSet = new Set(); + + for(let i = 0; i < s.length; i++) { + if(s.substring(i,i+k).length === k) { + bitSet.add(s.substring(i,i + k)); + } + } + + return bitSet.size === 1< -1; i--) { + let y = nums[i] & (2 ** 10 - 1); + let x = nums[i] >> 10; + nums[j] = y; + nums[j - 1] = x; + j -= 2; + } + + return nums; +}; diff --git a/javascript/1472-design-browser-history.js b/javascript/1472-design-browser-history.js new file mode 100644 index 000000000..e134e0a50 --- /dev/null +++ b/javascript/1472-design-browser-history.js @@ -0,0 +1,34 @@ +class BrowserHistory { + constructor(homepage) { + this.history = [homepage]; + this.current = 0; + } + + /** + * @param {string} url + * @return {void} + */ + + visit(url) { + this.history[++this.current] = url; + this.history.length = this.current + 1; + } + + /** + * @param {number} steps + * @return {string} + */ + back(steps) { + this.current = Math.max(this.current - steps, 0); + return this.history[this.current]; + } + + /** + * @param {number} steps + * @return {string} + */ + forward(steps) { + this.current = Math.min(this.current + steps, this.history.length - 1); + return this.history[this.current]; + } +} diff --git a/javascript/1481-least-number-of-unique-integers-after-k-removals.js b/javascript/1481-least-number-of-unique-integers-after-k-removals.js new file mode 100644 index 000000000..9025312d9 --- /dev/null +++ b/javascript/1481-least-number-of-unique-integers-after-k-removals.js @@ -0,0 +1,33 @@ +/** + * Sorting + * Time O(n*log(n)) | Space O(n) + * https://leetcode.com/problems/least-number-of-unique-integers-after-k-removals/ + * @param {number[]} arr + * @param {number} k + * @return {number} + */ +var findLeastNumOfUniqueInts = function(arr, k) { + + const frequencies = {} + + for (let i = 0; i < arr.length; i++) { + frequencies[arr[i]] = (frequencies[arr[i]] && frequencies[arr[i]] + 1) || 1; + } + + const frequenciesArr = Object.entries(frequencies); + + frequenciesArr.sort((a, b) => a[1] - b[1]); + frequenciesArr.reverse(); + + while (k) { + const lastEl = frequenciesArr[frequenciesArr.length - 1]; + while (lastEl[1]) { + if (!k) return frequenciesArr.length; + lastEl[1] -= 1; + k--; + } + frequenciesArr.pop(); + } + + return frequenciesArr.length; +}; diff --git a/javascript/1486-xor-operation-in-an-array.js b/javascript/1486-xor-operation-in-an-array.js new file mode 100644 index 000000000..ea3342f3b --- /dev/null +++ b/javascript/1486-xor-operation-in-an-array.js @@ -0,0 +1,14 @@ +/** + * @param {number} n + * @param {number} start + * @return {number} + */ +var xorOperation = function(n, start) { + let nums = new Array(n).fill(0); // initialize a nums which is the length of n elements with 0 value of Array using Array() nad fill() + + for(let i=0; i a^b); // return the bitwise XOR of all elements of nums using reduce() +}; diff --git a/javascript/1512-number-of-good-pairs.js b/javascript/1512-number-of-good-pairs.js new file mode 100644 index 000000000..e0cb98100 --- /dev/null +++ b/javascript/1512-number-of-good-pairs.js @@ -0,0 +1,15 @@ +/** + * @param {number[]} nums + * @return {number} + */ +var numIdenticalPairs = function (nums) { + let result = 0; + for (let i = 0; i < nums.length; i++) { + for (let j = i + 1; j < nums.length; j++) { + if (nums[i] == nums[j]) { + result++; + } + } + } + return result; +}; \ No newline at end of file diff --git a/javascript/1514-path-with-maximum-probability.js b/javascript/1514-path-with-maximum-probability.js new file mode 100644 index 000000000..900b39bb0 --- /dev/null +++ b/javascript/1514-path-with-maximum-probability.js @@ -0,0 +1,50 @@ +/** + * @param {number} n + * @param {number[][]} edges + * @param {number[]} succProb + * @param {number} start + * @param {number} end + * @return {number} + */ +var maxProbability = function(n, edges, succProb, start, end) { + const genAdjList = () => { + /*** + { + 0: [[1, 0.5], [2, 0.2]], + 1: [[0, 0.5], [2, 0.5]], + 2: [[1, 0.5], [0, 0.2]], + } + ***/ + let list = {}; + for(let i = 0; i < n; i++) { + list[i] = []; + } + for(let i = 0; i < edges.length; i++) { + const [v1, v2] = edges[i]; + const p = succProb[i]; + list[v1].push([v2, p]); + list[v2].push([v1, p]); + } + + return list; + } + const graph = genAdjList(); + const queue = new MaxPriorityQueue(); + const visited = new Set(); + + queue.enqueue([start, 1], 1); + + while(!queue.isEmpty()) { + const [n1, p1] = queue.dequeue().element; + if(visited.has(n1)) continue; + visited.add(n1); + if(n1 === end) return p1; + + for(const [n2, p2] of graph[n1]) { + if(visited.has(n2)) continue; + const val = p1 * p2; + queue.enqueue([n2, val], val); + } + } + if(visited.size !== n) return 0; +}; diff --git a/javascript/1572-matrix-diagonal-sum.js b/javascript/1572-matrix-diagonal-sum.js new file mode 100644 index 000000000..e857c5601 --- /dev/null +++ b/javascript/1572-matrix-diagonal-sum.js @@ -0,0 +1,15 @@ +/** + * @param {number[][]} mat + * @return {number} + */ +var diagonalSum = function (mat) { + let sum = 0; // initialize sum to zero + let n = mat.length - 1; // initialize n to mat length - 1 + for (let i = 0; i <= n; i++) { // loop through to 0 to n + sum += mat[i][i]; // add mat[i][i] to sum + if (i !== (n - i)) { // if i not equal to n - i then add mat[i][n - i] to sum + sum += mat[i][n - i]; + } + } + return sum; // return sum; +}; \ No newline at end of file diff --git a/javascript/1584-Min-Cost-to-Connect-all-Points.js b/javascript/1584-Min-Cost-to-Connect-all-Points.js new file mode 100644 index 000000000..b5d3a295a --- /dev/null +++ b/javascript/1584-Min-Cost-to-Connect-all-Points.js @@ -0,0 +1,70 @@ +/** + * Prim's algorithm + * https://leetcode.com/problems/min-cost-to-connect-all-points/solution/ + * @param {number[][]} points + * @return {number} + */ +const minCostConnectPoints = (points) => { + const isBaseCase = ((points.length === 0) || (1000 <= points.length)); + if (isBaseCase) return 0; + + const { graph, seen, minHeap } = buildGraph(points); + + return search(points, graph, seen, minHeap); +}; + +const initGraph = (points) => ({ + graph: new Array(points.length).fill().map(() => []), + seen: new Array(points.length).fill(false), + minHeap: new MinPriorityQueue() +}) + +const buildGraph = (points) => { + const { graph, seen, minHeap } = initGraph(points); + + for (let src = 0; src < (points.length - 1); src++) { + for (let dst = (src + 1); (dst < points.length); dst++) { + const cost = getCost(points, src, dst); + + graph[src].push([ dst, cost ]); + graph[dst].push([ src, cost ]); + } + } + + const [ src, cost, priority ] = [ 0, 0, 0 ]; + const node = [ src, cost ]; + + minHeap.enqueue(node, priority); + + return { graph, seen, minHeap }; +} + +const getCost = (points, src, dst) => { + const [ [ x1, y1 ], [ x2, y2 ] ] = [ points[src], points[dst] ]; + + return (Math.abs(x1 - x2) + Math.abs(y1 - y2)); +} + +const search = (points, graph, seen, minHeap, nodeCount = 0, cost = 0) => { + while (nodeCount < points.length) { + let [ src, srcCost ] = minHeap.dequeue().element; + + if (seen[src]) continue; + seen[src] = true; + + cost += srcCost; + nodeCount += 1; + + checkNeighbors(graph, src, seen, minHeap); + } + + return cost; +} + +const checkNeighbors = (graph, src, seen, minHeap) => { + for (const [ dst, dstCost ] of graph[src]) { + if (seen[dst]) continue; + + minHeap.enqueue([ dst, dstCost ], dstCost); + } +} \ No newline at end of file diff --git a/javascript/1584-min-cost-to-connect-all-points.js b/javascript/1584-min-cost-to-connect-all-points.js new file mode 100644 index 000000000..b5d3a295a --- /dev/null +++ b/javascript/1584-min-cost-to-connect-all-points.js @@ -0,0 +1,70 @@ +/** + * Prim's algorithm + * https://leetcode.com/problems/min-cost-to-connect-all-points/solution/ + * @param {number[][]} points + * @return {number} + */ +const minCostConnectPoints = (points) => { + const isBaseCase = ((points.length === 0) || (1000 <= points.length)); + if (isBaseCase) return 0; + + const { graph, seen, minHeap } = buildGraph(points); + + return search(points, graph, seen, minHeap); +}; + +const initGraph = (points) => ({ + graph: new Array(points.length).fill().map(() => []), + seen: new Array(points.length).fill(false), + minHeap: new MinPriorityQueue() +}) + +const buildGraph = (points) => { + const { graph, seen, minHeap } = initGraph(points); + + for (let src = 0; src < (points.length - 1); src++) { + for (let dst = (src + 1); (dst < points.length); dst++) { + const cost = getCost(points, src, dst); + + graph[src].push([ dst, cost ]); + graph[dst].push([ src, cost ]); + } + } + + const [ src, cost, priority ] = [ 0, 0, 0 ]; + const node = [ src, cost ]; + + minHeap.enqueue(node, priority); + + return { graph, seen, minHeap }; +} + +const getCost = (points, src, dst) => { + const [ [ x1, y1 ], [ x2, y2 ] ] = [ points[src], points[dst] ]; + + return (Math.abs(x1 - x2) + Math.abs(y1 - y2)); +} + +const search = (points, graph, seen, minHeap, nodeCount = 0, cost = 0) => { + while (nodeCount < points.length) { + let [ src, srcCost ] = minHeap.dequeue().element; + + if (seen[src]) continue; + seen[src] = true; + + cost += srcCost; + nodeCount += 1; + + checkNeighbors(graph, src, seen, minHeap); + } + + return cost; +} + +const checkNeighbors = (graph, src, seen, minHeap) => { + for (const [ dst, dstCost ] of graph[src]) { + if (seen[dst]) continue; + + minHeap.enqueue([ dst, dstCost ], dstCost); + } +} \ No newline at end of file diff --git a/javascript/1588-sum-of-all-odd-length-subarrays.js b/javascript/1588-sum-of-all-odd-length-subarrays.js new file mode 100644 index 000000000..6c47dd70b --- /dev/null +++ b/javascript/1588-sum-of-all-odd-length-subarrays.js @@ -0,0 +1,12 @@ +/** + * @param {number[]} arr + * @return {number} + */ +var sumOddLengthSubarrays = function (arr) { + let sum = 0, len = arr.length; + for (let i = 0; i < arr.length; i++) { + let total = i * (len - i) + (len - i); + sum += Math.ceil(total / 2) * arr[i]; + } + return sum; +}; \ No newline at end of file diff --git a/javascript/1603-design-parking-system.js b/javascript/1603-design-parking-system.js new file mode 100644 index 000000000..8a17d286f --- /dev/null +++ b/javascript/1603-design-parking-system.js @@ -0,0 +1,45 @@ +/** + * https://leetcode.com/problems/design-parking-system/ + * @class ParkingSystem + * @param {number} big + * @param {number} medium + * @param {number} small + */ +class ParkingSystem { + constructor(big, medium, small) { + this.isBigRemaining = big; + this.isMediumRemaining = medium; + this.isSmallRemaining = small; + } + + /** + * Time O(1) | Space O(1) + * @param {number} carType + * @return {boolean} + */ + addCar(carType) { + const isBigCarAvailable = (carType === 1 && this.isBigRemaining > 0); + if(isBigCarAvailable) { + this.isBigRemaining -= 1; + return true; + } + const isMediumCarAvailable = (carType === 2 && this.isMediumRemaining > 0); + if(isMediumCarAvailable) { + this.isMediumRemaining -= 1; + return true; + } + const isSmallCarAvailable = (carType === 3 && this.isSmallRemaining > 0); + if(isSmallCarAvailable) { + this.isSmallRemaining -= 1; + return true; + } + return false; + } +} + + +/** + * Your ParkingSystem object will be instantiated and called as such: + * var obj = new ParkingSystem(big, medium, small) + * var param_1 = obj.addCar(carType) + */ diff --git a/javascript/1609-even-odd-tree.js b/javascript/1609-even-odd-tree.js new file mode 100644 index 000000000..9acd11f83 --- /dev/null +++ b/javascript/1609-even-odd-tree.js @@ -0,0 +1,67 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ + +/** + * Level Order Traversal | BFS + * Time O(n) | Space O(n) + * https://leetcode.com/problems/even-odd-tree + * @param {TreeNode} root + * @return {boolean} + */ +var isEvenOddTree = function(root) { + + // helper function + const isStricklyIncreasingAndOdd = (arr) => { + + for (let i = 0; i < arr.length; i++) { + const currElement = arr[i]; + const nextElement = (arr[i + 1] !== undefined && arr[i + 1]) || Infinity; + if (currElement >= nextElement || currElement % 2 === 0) return false; + } + + return true; + } + + // helper function + const isStricklyDecreasingAndEven = (arr) => { + + for (let i = 0; i < arr.length; i++) { + const currElement = arr[i]; + const nextElement = (arr[i + 1] !== undefined && arr[i + 1]) || -Infinity; + if (currElement <= nextElement || currElement % 2 === 1) return false; + } + + return true; + } + + const q = new Queue(); + q.enqueue([root, 0]); + + while (!q.isEmpty()) { + const size = q.size(); + + const levelArr = []; + const level = q.front()[1]; + + for (let i = 0; i < size; i++) { + + const element = q.dequeue(); + const node = element[0]; + levelArr.push(node.val); + + node.left && q.enqueue([node.left, level + 1]); + node.right && q.enqueue([node.right, level + 1]); + } + + if (level % 2 === 0 && !isStricklyIncreasingAndOdd(levelArr)) return false; + if (level % 2 === 1 && !isStricklyDecreasingAndEven(levelArr)) return false; + } + + return true; +}; diff --git a/javascript/1614-maximum-nesting-depth-of-the-parentheses.js b/javascript/1614-maximum-nesting-depth-of-the-parentheses.js new file mode 100644 index 000000000..126c5840b --- /dev/null +++ b/javascript/1614-maximum-nesting-depth-of-the-parentheses.js @@ -0,0 +1,17 @@ +/** + * Stack + * Time O(n) | Space O(1) + * https://leetcode.com/problems/maximum-nesting-depth-of-the-parentheses + * @param {string} s + * @return {number} + */ +var maxDepth = function(s) { + let currDepth = 0; + let maxDepth = 0; + for (let i = 0; i < s.length; i++) { + if (s[i] === "(") currDepth++; + maxDepth = Math.max(currDepth, maxDepth); + if (s[i] === ")") currDepth--; + } + return maxDepth; +}; diff --git a/javascript/1642-furthest-building-you-can-reach.js b/javascript/1642-furthest-building-you-can-reach.js new file mode 100644 index 000000000..c48314f9b --- /dev/null +++ b/javascript/1642-furthest-building-you-can-reach.js @@ -0,0 +1,40 @@ +/** + * MaxPriorityQueue + * Time O(n*log(n)) | Space O(n) + * https://leetcode.com/problems/furthest-building-you-can-reach/ + * @param {number[]} heights + * @param {number} bricks + * @param {number} ladders + * @return {number} + */ +var furthestBuilding = function(heights, bricks, ladders) { + + const maxPriorityQueue = new MaxPriorityQueue({ + compare: (a, b) => { + return b - a; + } + }); + + let i = 0; + + while (i < heights.length - 1) { + const diff = heights[i + 1] - heights[i]; + + if (diff <= 0) { + i++; + continue; + } + + bricks -= diff; + maxPriorityQueue.enqueue(diff); + + if (bricks < 0) { + if (!ladders) return i; + ladders -= 1; + bricks += maxPriorityQueue.dequeue(); + } + i++; + } + + return heights.length - 1; +}; diff --git a/javascript/1647-minimum-deletions-to-make-character-frequencies-unique.js b/javascript/1647-minimum-deletions-to-make-character-frequencies-unique.js new file mode 100644 index 000000000..aa1b61f57 --- /dev/null +++ b/javascript/1647-minimum-deletions-to-make-character-frequencies-unique.js @@ -0,0 +1,39 @@ +/** + * Hasing + * Time O(n) | Space O(n) + * https://leetcode.com/problems/minimum-deletions-to-make-character-frequencies-unique/ + * @param {string} s + * @return {number} + */ +var minDeletions = function(s) { + + // hash frequency + let i = 0; + const charHash = new Map(); + while (i < s.length) { + const frequency = charHash.get(s[i]) || 0; + charHash.set(s[i], frequency + 1); + i++; + } + const frequencyHash = new Map(); + for (const [key, val] of charHash) { + const frequency = frequencyHash.get(val) || 0; + frequencyHash.set(val, frequency + 1); + } + + let min = 0; + for (const [key, val] of frequencyHash) { + let frequency = key; + let frequencyOfFrequency = val; + while(frequencyOfFrequency > 1) { + while(frequencyHash.has(frequency)) { + frequency -= 1; + min += 1; + } + if (frequency > 0) frequencyHash.set(frequency, 1); + frequency = key; + frequencyOfFrequency -= 1; + } + } + return min; +}; diff --git a/javascript/1688-count-of-matches-in-tournament.js b/javascript/1688-count-of-matches-in-tournament.js new file mode 100644 index 000000000..d0104aeb6 --- /dev/null +++ b/javascript/1688-count-of-matches-in-tournament.js @@ -0,0 +1,24 @@ +/** + * @param {number} n + * @return {number} + */ +var numberOfMatches = function (n) { + let matches = 0; // initialize matches to zero + let num = n; // initialize num equal to n + for (let i = 0; i < n; i++) { // loop through the number n + if (num == 1) { // if num is equal to 1 then break the for loop + break; + } else { // else + if (num % 2 == 0) { // if num is even + let divide = num / 2; // divide num by 2 + matches += divide; // add divide to matches + num -= divide; // subtract divide to num + } else { // else + let divide = (num - 1) / 2; // subtract num by 1 and then divide it by 2 + matches += divide; // add divide to matches + num -= divide; // subtract divide to num + } + } + } + return matches; // return number matches +}; \ No newline at end of file diff --git a/javascript/1700-number-of-students-unable-to-eat-lunch.js b/javascript/1700-number-of-students-unable-to-eat-lunch.js new file mode 100644 index 000000000..3ef2bdf2d --- /dev/null +++ b/javascript/1700-number-of-students-unable-to-eat-lunch.js @@ -0,0 +1,25 @@ +/** + * @param {number[]} students + * @param {number[]} sandwiches + * @return {number} + */ +var countStudents = function (students, sandwiches) { + let movement = 0; // initialize movement to be zero + + while (sandwiches.length > 0) { // while length of sandwiches is greater than zero + if (students[0] == sandwiches[0]) { // if first element of students and sandwiches both are same + students.shift(); // reomve first element of students using shift() + sandwiches.shift(); // remove first element of sandwiches using shift() + movement = 0; // make movement to be zero + } else { // else + let add = students.shift(); // initialize add to be first element of students + students.push(add); // push add to array students + movement++; // increment movement + if (movement == students.length) { // if movement is equal to length of students then break the loop + break; + } + } + } + + return students.length; // return length of array students +}; \ No newline at end of file diff --git a/javascript/1768-merge-strings-alternately.js b/javascript/1768-merge-strings-alternately.js new file mode 100644 index 000000000..60bb5df04 --- /dev/null +++ b/javascript/1768-merge-strings-alternately.js @@ -0,0 +1,13 @@ +// Time complexity: O(n) +// Space complexity: O(n) + +var mergeAlternately = function (word1, word2) { + const buffer = []; + + for (let i = 0; i < word1.length || i < word2.length; i++) { + if (i < word1.length) buffer.push(word1[i]); + if (i < word2.length) buffer.push(word2[i]); + } + + return buffer.join(''); +}; diff --git a/javascript/1822-sign-of-the-product-of-an-array.js b/javascript/1822-sign-of-the-product-of-an-array.js new file mode 100644 index 000000000..95607d3bd --- /dev/null +++ b/javascript/1822-sign-of-the-product-of-an-array.js @@ -0,0 +1,10 @@ +const arraySign = function (nums) { + let sign = 1; + + for (const num of nums) { + if (num == 0) return 0; + if (num < 0) sign = -1 * sign; + } + + return sign; +}; diff --git a/javascript/1834-single-threaded-cpu.js b/javascript/1834-single-threaded-cpu.js new file mode 100644 index 000000000..6d636203e --- /dev/null +++ b/javascript/1834-single-threaded-cpu.js @@ -0,0 +1,29 @@ +/** + * @param {number[][]} tasks + * @return {number[]} + */ +var getOrder = function(tasks) { + for(let i = 0; i < tasks.length; i++) tasks[i].push(i); + tasks.sort((a, b) => a[0] - b[0]); + const pq = new MinPriorityQueue({ + compare: (e1, e2) => { + if(e1[1] === e2[1]) return e1[2] - e2[2]; + return e1[1] - e2[1]; + } + }); + const a = []; + let t = tasks[0][0], i = 0; + while(pq.size() || i < tasks.length){ + while(i < tasks.length && t >= tasks[i][0]){ + pq.enqueue(tasks[i]); + i++; + } + if(pq.size()){ + let e = pq.dequeue(); + a.push(e[2]); + t += e[1]; + } + else t = tasks[i][0]; + } + return a; +}; \ No newline at end of file diff --git a/javascript/1838-frequency-of-the-most-frequent-element.js b/javascript/1838-frequency-of-the-most-frequent-element.js new file mode 100644 index 000000000..db01cefbd --- /dev/null +++ b/javascript/1838-frequency-of-the-most-frequent-element.js @@ -0,0 +1,27 @@ +/** + * @param {number[]} nums + * @param {number} k + * @return {number} + */ +var maxFrequency = function (nums, k) { + const sortedNums = nums.sort((a, b) => a - b); + + let maxLength = 0; + + let currentSum = 0; + let leftWindow = 0; + for (let rightWindow = 0; rightWindow < sortedNums.length; rightWindow++) { + const currentLength = rightWindow - leftWindow + 1; + const rightNum = sortedNums[rightWindow]; + currentSum += rightNum; + + if (currentSum + k >= rightNum * currentLength) { + maxLength = currentLength; + } else { + const leftNum = sortedNums[leftWindow]; + currentSum -= leftNum; + leftWindow++; + } + } + return maxLength; +}; diff --git a/javascript/1845-seat-reservation-manager.js b/javascript/1845-seat-reservation-manager.js new file mode 100644 index 000000000..9fb1c1d3b --- /dev/null +++ b/javascript/1845-seat-reservation-manager.js @@ -0,0 +1,36 @@ + +class SeatManager { + /** + * MinHeap + * Time O(n*log(n)) | Space O(n) + * https://leetcode.com/problems/seat-reservation-manager/ + * @param {number} n + */ + constructor(n) { + this.unreserved = new MinPriorityQueue({ + compare: (a, b) => a - b + }); + + for (let i = 1; i < n + 1; i++) { + this.unreserved.enqueue(i); + } + } + + /** + * Time O(log(n)) | Space O(1) + * @return {number} + */ + reserve() { + const minAvailableSeat = this.unreserved.dequeue(); + return minAvailableSeat; + } + + /** + * Time O(log(n)) | Space O(1) + * @param {number} seatNumber + * @return {void} + */ + unreserve(seatNumber) { + this.unreserved.enqueue(seatNumber); + } +} diff --git a/javascript/1846-maximum-element-after-decreasing-and-rearranging.js b/javascript/1846-maximum-element-after-decreasing-and-rearranging.js new file mode 100644 index 000000000..0a9e4631c --- /dev/null +++ b/javascript/1846-maximum-element-after-decreasing-and-rearranging.js @@ -0,0 +1,26 @@ + +/** + * Sorting + * Time O(n*log(n)) | Space O(n) + * https://leetcode.com/problems/maximum-element-after-decreasing-and-rearranging/ + * @param {number[]} arr + * @return {number} + */ +var maximumElementAfterDecrementingAndRearranging = function(arr) { + + arr.sort((a, b) => a - b); + let index = 1; + arr[0] = 1; + + while (index < arr.length) { + + const pre = arr[index - 1]; + const curr = arr[index]; + if (Math.abs(curr - pre) > 1 && pre + 1 < curr) { + arr[index] = pre + 1; + } + index++; + } + + return Math.max(...arr); +}; diff --git a/javascript/1851-minimum-interval-to-include-each-query.js b/javascript/1851-minimum-interval-to-include-each-query.js new file mode 100644 index 000000000..f35523608 --- /dev/null +++ b/javascript/1851-minimum-interval-to-include-each-query.js @@ -0,0 +1,30 @@ +/** + * @param {number[][]} intervals + * @param {number[]} queries + * @return {number[]} + */ +var minInterval = function(intervals, queries) { + intervals.sort((a, b) => a[0] - b[0]); + const queriesSorted = [ ...queries ].sort((a, b) => a - b); + const minHeap = new MinPriorityQueue(); + const output = {}; + let i = 0; + + for (const query of queriesSorted) { + while (i < intervals.length && intervals[i][0] <= query) { + const [ start, end ] = intervals[i]; + const length = end - start + 1; + // Use length as the priority in the heap. + minHeap.enqueue([ length, end ], length); + i++; + } + + while (!minHeap.isEmpty() && minHeap.front().element[1] < query) { + minHeap.dequeue(); + } + + output[query] = (!minHeap.isEmpty()) ? minHeap.front().element[0] : -1; + } + + return queries.map((query) => output[query]); +}; diff --git a/javascript/1863-sum-of-all-subset-xor-totals.js b/javascript/1863-sum-of-all-subset-xor-totals.js new file mode 100644 index 000000000..cc2977bb5 --- /dev/null +++ b/javascript/1863-sum-of-all-subset-xor-totals.js @@ -0,0 +1,11 @@ +/** + * @param {number[]} nums + * @return {number} + */ +var subsetXORSum = function (nums) { + let bitOR = 0; + for (let i = 0; i < nums.length; ++i) { + bitOR |= nums[i]; + } + return (bitOR * Math.pow(2, nums.length - 1)); +}; \ No newline at end of file diff --git a/javascript/1882-process-tasks-using-servers.js b/javascript/1882-process-tasks-using-servers.js new file mode 100644 index 000000000..bf3ac6f2b --- /dev/null +++ b/javascript/1882-process-tasks-using-servers.js @@ -0,0 +1,56 @@ +/** + * MinPriorityQueue | Simulation + * Time O(n*log(n)) | Space O(n) + * https://leetcode.com/problems/process-tasks-using-servers/ + * @param {number[]} servers + * @param {number[]} tasks + * @return {number[]} + */ +var assignTasks = function(servers, tasks) { + + const toBeCompleted = new MinPriorityQueue({ + compare: (a, b) => { + if (a[0] === b[0]) return a[1] - b[1]; + return a[0] - b[0]; + } + }); + + const freeServers = new MinPriorityQueue({ + compare: (a, b) => { + if (a[0] === b[0]) return a[1] - b[1]; + return a[0] - b[0]; + } + }); + + for (let i = 0; i < servers.length; i++) { + const weight = servers[i]; + const idx = i; + freeServers.enqueue([weight, idx]); + } + + let sec = 0; + const result = []; + + for (let i = 0; i < tasks.length; i++) { + sec = Math.max(i, sec); + + // if the we don't have server available then jump to the next imidiate + // time when the server will be available. + if (freeServers.isEmpty()) { + sec = toBeCompleted.front()[0]; + } + while (!toBeCompleted.isEmpty() && + toBeCompleted.front()[0] <= sec) { + const [endTime, serverIdx] = toBeCompleted.dequeue(); + const weight = servers[serverIdx]; + freeServers.enqueue([weight, serverIdx]); + } + + const [weight, serverIdx] = freeServers.dequeue(); + const timeToBeTaken = tasks[i]; + result.push(serverIdx); + toBeCompleted.enqueue([sec+timeToBeTaken, serverIdx]); + } + + return result; +}; diff --git a/javascript/1888-minimum-number-of-flips-to-make-the-binary-string-alternating.js b/javascript/1888-minimum-number-of-flips-to-make-the-binary-string-alternating.js new file mode 100644 index 000000000..e416ac62d --- /dev/null +++ b/javascript/1888-minimum-number-of-flips-to-make-the-binary-string-alternating.js @@ -0,0 +1,36 @@ +/** + * https://leetcode.com/problems/minimum-number-of-flips-to-make-the-binary-string-alternating/ + * Time O(n), Space O(1) + * @param {string} s + * @return {number} + */ +var minFlips = (s) => { + if (!s || s.length < 1) return 0; + + const initialLen = s.length; + if (initialLen % 2 === 1) s += s; + + let evenCmp = 0; + let oddCmp = 0; + let result = Infinity; + let end = 0; + let start = 0; + + while (end < s.length) { + if (end % 2 !== Number(s[end])) evenCmp++; + if ((end % 2 ^ 1) !== Number(s[end])) oddCmp++; + + if (end >= initialLen) { + if (start % 2 !== Number(s[start])) evenCmp--; + if ((start % 2 ^ 1) !== Number(s[start])) oddCmp--; + start++; + } + + if (end >= initialLen - 1) { + result = Math.min(evenCmp, oddCmp, result); + } + end++; + } + + return result; +}; diff --git a/javascript/1898-maximum-number-of-removable-characters.js b/javascript/1898-maximum-number-of-removable-characters.js new file mode 100644 index 000000000..3ae59a871 --- /dev/null +++ b/javascript/1898-maximum-number-of-removable-characters.js @@ -0,0 +1,95 @@ +/** + * https://leetcode.com/problems/maximum-number-of-removable-characters/ + * + * Brute force + * Time O(removable.length * s.length) | Space O(n) + * @param {string} s + * @param {string} p + * @param {number[]} removable + * @return {number} + */ +var maximumRemovals1 = function(s, p, removable) { + let k = 0; + // removable.reverse(); + s = s.split(''); + p = p.split(''); + + for (let i = 0; i < removable.length; i++) { + s[removable[i]] = -1; + if (isSubSet1(s, p)) { + k++; + continue; + } + return k; + } + + return k; +}; + +// helper function. +function isSubSet1(s, p) { + let i = 0; + let j = 0; + + while (i < s.length && j < p.length) { + if (s[i] === p[j]) { + i++; + j++; + } else { + i++; + } + } + + return j === p.length; +} + + +/** + * + * Binary Search + * n = length of string, k = length of removable + * Time O(log(k)*n) | Space O(1) + * @param {string} s + * @param {string} p + * @param {number[]} removable + * @return {number} + */ +var maximumRemovals = function(s, p, removable) { + + let left = 0; + let right = removable.length - 1; + let k = 0; + + while (left <= right) { + const mid = (left + right) >> 1; + const hash = new Set(removable.slice(0, mid + 1)); + + if (isSubSet(hash, s, p)) { + k = Math.max(k, mid + 1); + left = mid + 1; + continue; + } + + right = mid - 1; + } + + return k; +}; + +// helper function. +function isSubSet(hash, s, p) { + let i = 0; + let j = 0; + + while (i < s.length && j < p.length) { + if (s[i] === p[j] && !hash.has(i)) { + i++; + j++; + continue; + } + + i++; + } + + return j === p.length; +} diff --git a/javascript/1899-merge-triplets-to-form-target-triplet.js b/javascript/1899-merge-triplets-to-form-target-triplet.js new file mode 100644 index 000000000..ba55f3ef8 --- /dev/null +++ b/javascript/1899-merge-triplets-to-form-target-triplet.js @@ -0,0 +1,70 @@ +/** + * @param {number[][]} triplets + * @param {number[]} target + * @return {boolean} + */ +var mergeTriplets = function (triplets, target) { + var good = new Set(); + + for (var t in triplets) { + var triplet = triplets[t]; + if ( + triplet[0] > target[0] || + triplet[1] > target[1] || + triplet[2] > target[2] + ) { + continue; + } + + for (var i = 0; i < triplet.length; i++) { + if (triplet[i] === target[i]) { + good.add(i); + } + } + } + + return good.size === 3; +}; + +/** + * https://leetcode.com/problems/merge-triplets-to-form-target-triplet/ + * Time O(N) | Space O(1) + * @param {number[][]} triplets + * @param {number[]} target + * @return {boolean} + */ + var mergeTriplets = function(triplets, target, res = new Array(3).fill(0)) { + for (const [ a, b, c ] of triplets) { /* Time O(N) */ + const [ _a, _b, _c ] = target; + + const isTargetGreater = (a <= _a) && (b <= _b) && (c <= _c); + if (!isTargetGreater) continue; + + const [ __a, __b, __c ] = res; + res = [ Math.max(__a, a), Math.max(__b, b), Math.max(__c, c) ]; + } + + return res.every((val, i) => val === target[i])/* Time O(N) */ +}; + +/** + * https://leetcode.com/problems/merge-triplets-to-form-target-triplet/ + * Time O(N) | Space O(1) + * @param {number[][]} triplets + * @param {number[]} target + * @return {boolean} + */ +var mergeTriplets = function(triplets, target, res = new Array(3).fill(false)) { + for (const [ a, b, c ] of triplets) {/* Time O(N) */ + const [ _a, _b, _c ] = target; + + const isTargetGreater = (a <= _a) && (b <= _b) && (c <= _c); + if (!isTargetGreater) continue; + + res[0] |= (a === _a); + res[1] |= (b === _b); + res[2] |= (c === _c); + } + + return res[0] && res[1] && res[2]; +} diff --git a/javascript/1905-count-sub-islands.js b/javascript/1905-count-sub-islands.js new file mode 100644 index 000000000..5cebc1269 --- /dev/null +++ b/javascript/1905-count-sub-islands.js @@ -0,0 +1,35 @@ +var countSubIslands = function(grid1, grid2) { + let ROWS = grid1.length, COLS = grid1[0].length; + let visit = new Set(); + + const dfs = function(r, c) { + let flatCoord = r*COLS + c; + if ( + r < 0 + || c < 0 + || r == ROWS + || c == COLS + || grid2[r][c] == 0 + || visit.has(flatCoord) + ) + return true; + + visit.add(flatCoord); + let res = true; + if(grid1[r][c] == 0) + res = false; + + res = dfs(r - 1, c) && res; + res = dfs(r + 1, c) && res; + res = dfs(r, c - 1) && res; + res = dfs(r, c + 1) && res; + return res; + }; + + let count = 0; + for(let r = 0; r < ROWS; r++) + for(let c = 0; c < COLS; c++) + if(grid2[r][c] && !visit.has(r*COLS + c) && dfs(r, c)) + count += 1; + return count; +}; diff --git a/javascript/1921-eliminate-maximum-number-of-monsters.js b/javascript/1921-eliminate-maximum-number-of-monsters.js new file mode 100644 index 000000000..e0ca5a87d --- /dev/null +++ b/javascript/1921-eliminate-maximum-number-of-monsters.js @@ -0,0 +1,22 @@ +/** + * Greedy | Sorting + * Time O(n*log(n)) | Space O(n) + * @param {number[]} dist + * @param {number[]} speed + * @return {number} + */ +var eliminateMaximum = function(dist, speed) { + + const time = dist.map((d, i) => { + return d / speed[i]; + }); + + let monsterSlyed = 1; + time.sort((a, b) => a - b); + for (let i = 1; i < time.length; i++) { + if (time[i] <= monsterSlyed) return monsterSlyed; + monsterSlyed++; + } + + return monsterSlyed; +}; diff --git a/javascript/1929-concatenation-of-array.js b/javascript/1929-concatenation-of-array.js new file mode 100644 index 000000000..a7007d22f --- /dev/null +++ b/javascript/1929-concatenation-of-array.js @@ -0,0 +1,13 @@ +//https://leetcode.com/problems/concatenation-of-array/description/ + +/** + * @param {number[]} nums + * @return {number[]} + */ +var getConcatenation = function (nums) { + let res = []; + for (let i = 0; i < nums.length * 2; i++) { + res.push(nums[i % nums.length]); + } + return res; +}; diff --git a/javascript/1930-unique-length-3-palindromic-subsequences.js b/javascript/1930-unique-length-3-palindromic-subsequences.js new file mode 100644 index 000000000..817c69a2d --- /dev/null +++ b/javascript/1930-unique-length-3-palindromic-subsequences.js @@ -0,0 +1,13 @@ +/** + * @param {string} s + * @return {number} + */ +var countPalindromicSubsequence = function (s) { + let count = 0; + let chars = new Set(s); + for(const char of chars){ + let first = s.indexOf(char),last = s.lastIndexOf(char); + count += new Set(s.slice(first + 1, last)).size; + } + return count; +}; diff --git a/javascript/1958-check-if-move-is-legal.js b/javascript/1958-check-if-move-is-legal.js new file mode 100644 index 000000000..78ce9ecf4 --- /dev/null +++ b/javascript/1958-check-if-move-is-legal.js @@ -0,0 +1,27 @@ +var checkMove = function(board, rMove, cMove, color) { + const ROWS = board.length, COLS = board[0].length; + let direction = [[1, 0], [-1, 0], [0, 1], [0, -1], + [1, 1], [-1, -1], [1, -1], [-1, 1]]; + board[rMove][cMove] = color; + + let legal = function(row, col, color, direc) { + let dr = direc[0], dc = direc[1]; + row = row + dr; + col = col + dc; + let length = 1; + + while(0 <= row && row < ROWS && 0 <= col && col < COLS) { + length += 1; + if(board[row][col] == '.') return false; + if(board[row][col] == color) + return length >= 3; + row = row + dr; + col = col + dc; + } + return false; + } + + for(const d of direction) + if(legal(rMove, cMove, color, d)) return true; + return false; +}; diff --git a/javascript/1963-minimum-number-of-swaps-to-make-the-string-balanced.js b/javascript/1963-minimum-number-of-swaps-to-make-the-string-balanced.js new file mode 100644 index 000000000..3da70d70e --- /dev/null +++ b/javascript/1963-minimum-number-of-swaps-to-make-the-string-balanced.js @@ -0,0 +1,15 @@ +// problem link https://leetcode.com/problems/minimum-number-of-swaps-to-make-the-string-balanced +// time complexity O(n) +// space complexity O(1) + +var minSwaps = function(s) { + + let extraClosing = 0; + let maxClosing = 0; + for(let i = 0; i < s.length; i++) { + s[i] === ']' ? extraClosing++ : extraClosing--; + maxClosing = Math.max(maxClosing, extraClosing); + } + + return Math.ceil(maxClosing/2); +}; diff --git a/javascript/1968-array-with-elements-not-equal-to-average-of-neighbors.js b/javascript/1968-array-with-elements-not-equal-to-average-of-neighbors.js new file mode 100644 index 000000000..dc1975f4d --- /dev/null +++ b/javascript/1968-array-with-elements-not-equal-to-average-of-neighbors.js @@ -0,0 +1,25 @@ +/** + * Two Pointers + * https://leetcode.com/problems/array-with-elements-not-equal-to-average-of-neighbors/ + * + * Time O(n*log(n)) | Space O(n) + * @param {number[]} nums + * @return {number[]} + */ + var rearrangeArray = function(nums) { + nums.sort((a,b) => a-b); + + let midPointer = Math.ceil(nums.length / 2); + let beginingPointer = 1; + + while(midPointer < nums.length) { + swap(midPointer, beginingPointer, nums); + midPointer++; + beginingPointer += 2 + } + return nums; + }; + +var swap = function(i,j,nums) { + [nums[i], nums[j]] = [nums[j], nums[i]]; +} diff --git a/javascript/1984-Minimum-Difference-Between-Highest-and-Lowest-of-K-Scores.js b/javascript/1984-Minimum-Difference-Between-Highest-and-Lowest-of-K-Scores.js new file mode 100644 index 000000000..b50b93538 --- /dev/null +++ b/javascript/1984-Minimum-Difference-Between-Highest-and-Lowest-of-K-Scores.js @@ -0,0 +1,30 @@ +/** + * Loglinear/N*log(N) + * Time O(N*log(N)) | Space O(1) + * https://leetcode.com/problems/minimum-difference-between-highest-and-lowest-of-k-scores + * + * @param {number[]} nums + * @param {number} k + * @return {number} + */ +var minimumDifference = function(nums, k) { + + const isEdgeCase = (k === 1); + if (isEdgeCase) return 0; + + nums = nums.sort((a, b) => { + return a - b; + }); + + let i = 0; + let j = k - 1; + let minDiffrence = Infinity; + + while (j < nums.length) { + minDiffrence = Math.min(Math.abs(nums[i] - nums[j]), minDiffrence); + j++; + i++; + } + + return minDiffrence; +}; diff --git a/javascript/1984-minimum-difference-between-highest-and-lowest-of-k-scores.js b/javascript/1984-minimum-difference-between-highest-and-lowest-of-k-scores.js new file mode 100644 index 000000000..b50b93538 --- /dev/null +++ b/javascript/1984-minimum-difference-between-highest-and-lowest-of-k-scores.js @@ -0,0 +1,30 @@ +/** + * Loglinear/N*log(N) + * Time O(N*log(N)) | Space O(1) + * https://leetcode.com/problems/minimum-difference-between-highest-and-lowest-of-k-scores + * + * @param {number[]} nums + * @param {number} k + * @return {number} + */ +var minimumDifference = function(nums, k) { + + const isEdgeCase = (k === 1); + if (isEdgeCase) return 0; + + nums = nums.sort((a, b) => { + return a - b; + }); + + let i = 0; + let j = k - 1; + let minDiffrence = Infinity; + + while (j < nums.length) { + minDiffrence = Math.min(Math.abs(nums[i] - nums[j]), minDiffrence); + j++; + i++; + } + + return minDiffrence; +}; diff --git a/javascript/1985-find-the-kth-largest-integer-in-the-array.js b/javascript/1985-find-the-kth-largest-integer-in-the-array.js new file mode 100644 index 000000000..e10ad39f5 --- /dev/null +++ b/javascript/1985-find-the-kth-largest-integer-in-the-array.js @@ -0,0 +1,18 @@ +/** + * Sorting + * Time O(n*log(n)) | Space O(1) + * https://leetcode.com/problems/find-the-kth-largest-integer-in-the-array/ + * @param {string[]} nums + * @param {number} k + * @return {string} + */ +var kthLargestNumber = function(nums, k) { + + // sort it string wise. + nums.sort((a, b) => { + if (a.length !== b.length) return b.length - a.length; + return b.localeCompare(a); + }); + + return nums[k - 1]; +}; diff --git a/javascript/1993-operations-on-tree.js b/javascript/1993-operations-on-tree.js new file mode 100644 index 000000000..c32b4625c --- /dev/null +++ b/javascript/1993-operations-on-tree.js @@ -0,0 +1,120 @@ +class LockingTree { + /** + * @param {number[]} parent + */ + constructor(parent) { + this.parent = parent; + this.childHash = {}; + this.treeHash = {}; + for(let i = 0; i < parent.length; i++) { + if(this.childHash[parent[i]]) { + this.childHash[parent[i]].push(i); + } else { + this.childHash[parent[i]] = [i]; + } + } + } + + /** + * Time O(1) | Space O(1) + * @param {number} num + * @param {number} user + * @return {boolean} + */ + lock(num, user) { + // it will just lock a node for a given user if it's not already locked THAT'S IT! + if(this.treeHash[num]) return false; + this.treeHash[num] = user; + return true; + } + + /** + * Time O(1) | Space O(1) + * @param {number} num + * @param {number} user + * @return {boolean} + */ + unlock(num, user) { + // only unlock the node if it's locked by the same user + if(this.treeHash[num] === user) { + delete this.treeHash[num]; + return true; + } + return false; + } + + /** + * + * Time O(n) | Space O(n) + * @param {number} num + * @param {number} user + * @return {boolean} + */ + upgrade(num, user) { + // lock the node for a given user and unlock all of its descendants no matter who locked it. + // 1. the given node should be unlocked + // 2. the given node should have at least one locked node descendant by anyone + // 3. the given node shouldn't have any locked ancestors + if(this.treeHash[num]) return false; + if(!this.checkDescendants(num)) return false; + if(!this.checkAncestors(num)) return false; + + // locking the given node + this.treeHash[num] = user; + this.unlockDescendants(num); + return true; + } + + /** + * Helper method to unlock descendants + * Time O(n) | Space O(n) + * @param {number} index + */ + unlockDescendants(index) { + const stack = []; + stack.push(index); + while(stack.length) { + const node = stack.pop(); + const children = this.childHash[node]; + for(let i = 0; i < (children && children.length); i++) { + delete this.treeHash[children[i]]; + stack.push(children[i]); + } + } + } + + /** + * Helper method to check ancestors + * Time O(n) | Space O(1) + * @param {number} index + * @return {boolean} + */ + checkAncestors(index) { + let node = this.parent[index]; + while(node !== -1) { + if(this.treeHash[node]) return false; + node = this.parent[node]; + } + return true; + } + + /** + * Helper method to check descendants + * Time O(n) | Space O(n) + * @param {number} index + * @return {boolean} + */ + checkDescendants(index) { + const stack = []; + stack.push(index); + while(stack.length) { + const node = stack.pop(); + const children = this.childHash[node]; + for(let i = 0; i < (children && children.length); i++) { + if(this.treeHash[children[i]]) return true; + stack.push(children[i]); + } + } + return false; + } +} diff --git a/javascript/2001-number-of-pairs-of-interchangeable-rectangles.js b/javascript/2001-number-of-pairs-of-interchangeable-rectangles.js new file mode 100644 index 000000000..00b46d9a0 --- /dev/null +++ b/javascript/2001-number-of-pairs-of-interchangeable-rectangles.js @@ -0,0 +1,47 @@ +/** + * Brute Force + * Time O(N^2) | Space O(1) + * https://leetcode.com/problems/number-of-pairs-of-interchangeable-rectangles + * @param {number[][]} rectangles + * @return {number} + */ +var interchangeableRectangles = (rectangles) => { + + let totalPair = 0; + for (let i = 0; i < rectangles.length; i++) { + for (let j = i + 1; j < rectangles.length; j++) { + if (rectangles[i][1] / rectangles[i][0] === rectangles[j][1] / rectangles[j][0]) { + totalPair++; + } + } + } + return totalPair; +}; +/** + * Linear + * Time O(N) | Space O(n) + * @param {number[][]} rectangles + * @return {number} + */ +var interchangeableRectangles = (rectangles) => { + + const ratioFrequency = {}; + + for (let i = 0; i < rectangles.length; i++) { + const ratio = rectangles[i][1] / rectangles[i][0]; + if (ratioFrequency[ratio.toString()]) { + ratioFrequency[ratio.toString()] += 1; + } else { + ratioFrequency[ratio.toString()] = 1; + } + } + + let totalPair = 0; + for (const key in ratioFrequency) { + if (ratioFrequency[key] !== 1) { + totalPair += (ratioFrequency[key] * (ratioFrequency[key] - 1)) / 2; + } + } + + return totalPair; +}; diff --git a/javascript/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.js b/javascript/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.js new file mode 100644 index 000000000..a498c0528 --- /dev/null +++ b/javascript/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.js @@ -0,0 +1,51 @@ +/** + * @param {string} s + * @return {number} + * Time Complexity: O(2^N) + * Space Complexity: O(2^N) + */ +var maxProduct = function (s) { + const N = s.length; + const first = new Array(1 << N).fill(0), + last = new Array(1 << N).fill(0); + for (let i = 0; i < N; i++) { + for (let j = 1 << i; j < 1 << (i + 1); j++) { + first[j] = i; + } + } + for (let i = 0; i < N; i++) { + for (let j = 1 << i; j < 1 << N; j += 1 << (i + 1)) { + last[j] = i; + } + } + const dp = Memo((m) => { + if ((m & (m - 1)) === 0) { + return m != 0; + } + const l = last[m], + f = first[m]; + const lb = 1 << l, + fb = 1 << f; + return Math.max( + dp(m - lb), + dp(m - fb), + dp(m - lb - fb) + (s[l] == s[f]) * 2 + ); + }); + let ans = 0; + for (let m = 1; m < 1 << N; m++) { + ans = Math.max(ans, dp(m) * dp((1 << N) - 1 - m)); + } + return ans; +}; + +var Memo = (func) => { + const map = new Map(); + var wrapper = (m) => { + if (!map.get(m)) { + map.set(m, func(m)); + } + return map.get(m); + }; + return wrapper; +}; diff --git a/javascript/2013-Detect-Squares.js b/javascript/2013-Detect-Squares.js new file mode 100644 index 000000000..ed26ae11a --- /dev/null +++ b/javascript/2013-Detect-Squares.js @@ -0,0 +1,52 @@ +/* + * Time O(N) | Space O(N) + * https://leetcode.com/problems/detect-squares + */ +class DetectSquares { + constructor () { + this.map = {}; /* Space O(N) */ + this.points = [];/* Space O(N) */ + } + + add (point, { map, points } = this) { + const [ x, y ] = point; + const key = this.getKey(x, y); + const value = ((map[key] || 0) + 1); + + map[key] = value; /* Space O(N) */ + points.push(point);/* Space O(N) */ + } + + count (point, { points } = this, score = 0) { + const [ x1, y1 ] = point; + + for (const [ x2, y2 ] of points) {/* Time O(N) */ + const isSame = (Math.abs(x2 - x1) === Math.abs(y2 - y1)); + const isEqual = ((x1 === x2) || (y1 === y2)); + const canSkip = (!isSame || isEqual); + if (canSkip) continue; + + score += this.getScore(x1, y1, x2, y2); + } + + return score; + }; + + getKey (x, y) { + return `${x},${y}`; + } + + getScore (x1, y1, x2, y2, { map } = this) { + const [ aKey, bKey ] = [ this.getKey(x1, y2), this.getKey(x2, y1) ]; + const [ aScore, bScore ] = [ (map[aKey] || 0), (map[bKey] || 0) ]; + + return (aScore * bScore); + } +}; + +/** + * Your DetectSquares object will be instantiated and called as such: + * var obj = new DetectSquares() + * obj.add(point) + * var param_2 = obj.count(point) + */ \ No newline at end of file diff --git a/javascript/2013-detect-squares.js b/javascript/2013-detect-squares.js new file mode 100644 index 000000000..ed26ae11a --- /dev/null +++ b/javascript/2013-detect-squares.js @@ -0,0 +1,52 @@ +/* + * Time O(N) | Space O(N) + * https://leetcode.com/problems/detect-squares + */ +class DetectSquares { + constructor () { + this.map = {}; /* Space O(N) */ + this.points = [];/* Space O(N) */ + } + + add (point, { map, points } = this) { + const [ x, y ] = point; + const key = this.getKey(x, y); + const value = ((map[key] || 0) + 1); + + map[key] = value; /* Space O(N) */ + points.push(point);/* Space O(N) */ + } + + count (point, { points } = this, score = 0) { + const [ x1, y1 ] = point; + + for (const [ x2, y2 ] of points) {/* Time O(N) */ + const isSame = (Math.abs(x2 - x1) === Math.abs(y2 - y1)); + const isEqual = ((x1 === x2) || (y1 === y2)); + const canSkip = (!isSame || isEqual); + if (canSkip) continue; + + score += this.getScore(x1, y1, x2, y2); + } + + return score; + }; + + getKey (x, y) { + return `${x},${y}`; + } + + getScore (x1, y1, x2, y2, { map } = this) { + const [ aKey, bKey ] = [ this.getKey(x1, y2), this.getKey(x2, y1) ]; + const [ aScore, bScore ] = [ (map[aKey] || 0), (map[bKey] || 0) ]; + + return (aScore * bScore); + } +}; + +/** + * Your DetectSquares object will be instantiated and called as such: + * var obj = new DetectSquares() + * obj.add(point) + * var param_2 = obj.count(point) + */ \ No newline at end of file diff --git a/javascript/2017-grid-game.js b/javascript/2017-grid-game.js new file mode 100644 index 000000000..443af1762 --- /dev/null +++ b/javascript/2017-grid-game.js @@ -0,0 +1,23 @@ +/** + * Submission Details: + * https://leetcode.com/problems/grid-game/ + * Time O(n), Space O(1) + * Runtime: 89ms (beats 79.31%) || 53.5mb (beats 89.66%) + */ + +/** + * @param {number[][]} grid + * @return {number} + */ + +var gridGame = function(grid) { + let one = grid[0].reduce((a,b)=>a+b) - grid[0][0]; + let two = 0; + let res = one; + for(let i = 1; i < grid[0].length; i++){ + one-=grid[0][i]; + two+=grid[1][i-1]; + res = Math.min(res, Math.max(one,two)); + } + return res; +}; \ No newline at end of file diff --git a/javascript/2038-remove-colored-pieces-if-both-neighbors-are-the-same-color.js b/javascript/2038-remove-colored-pieces-if-both-neighbors-are-the-same-color.js new file mode 100644 index 000000000..ae64637e6 --- /dev/null +++ b/javascript/2038-remove-colored-pieces-if-both-neighbors-are-the-same-color.js @@ -0,0 +1,24 @@ +/** + * Counting + * Time O(n) | Space O(1) + * https://leetcode.com/problems/remove-colored-pieces-if-both-neighbors-are-the-same-color + * @param {string} colors + * @return {boolean} + */ +var winnerOfGame = function(colors) { + + let aScore = 0; + let bScore = 0; + + const canRemove = (index) => { + if (colors[index] === colors[index - 1] && colors[index] === colors[index + 1]) return colors[index]; + return false; + } + + for (let i = 1; i < colors.length; i++) { + if (canRemove(i) === 'A') aScore++; + if (canRemove(i) === 'B') bScore++; + } + + return aScore - bScore > 0; +}; diff --git a/javascript/2125-number-of-laser-beams-in-a-bank.js b/javascript/2125-number-of-laser-beams-in-a-bank.js new file mode 100644 index 000000000..3afac319a --- /dev/null +++ b/javascript/2125-number-of-laser-beams-in-a-bank.js @@ -0,0 +1,31 @@ +/** + * Two Pointers | Math | Array + * Time O(n) | Space O(1) + * https://leetcode.com/problems/number-of-laser-beams-in-a-bank/ + * @param {string[]} bank + * @return {number} + */ +var numberOfBeams = function(bank) { + + let totalBeams = 0; + + let left = 0; + let right = left + 1; + + const countBeam = (beam) => { + return beam.split("").filter((b) => b === "1").length; + } + + while (right < bank.length) { + while (right < bank.length && !countBeam(bank[right])) { + right++; + } + + if (right < bank.length) { + totalBeams += countBeam(bank[left]) * countBeam(bank[right]); + } + left = right; + right++; + } + return totalBeams; +}; diff --git a/javascript/2130-maximum-twin-sum-of-a-linked-list.js b/javascript/2130-maximum-twin-sum-of-a-linked-list.js new file mode 100644 index 000000000..1b5d435ef --- /dev/null +++ b/javascript/2130-maximum-twin-sum-of-a-linked-list.js @@ -0,0 +1,62 @@ +/** + * Definition for singly-linked list. + * function ListNode(val, next) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + */ + +/** + * Linear Time + * Time O(n) | Space O(1) + * @param {ListNode} head + * @return {number} + */ +var pairSum = function (head) { + const mid = llLength(head) / 2; + const rightPointer = getRightPointer(head, mid); + const leftPointer = reverseLL(head, mid); + + return getMax(leftPointer, rightPointer); +}; + +var getMax = (leftPointer, rightPointer) => { + let max = 0; + while (leftPointer && rightPointer) { + max = Math.max(leftPointer.val + rightPointer.val, max); + leftPointer = leftPointer.next; + rightPointer = rightPointer.next; + } + return max; +} +var getRightPointer = (head, mid) => { + let count = 0; + let rightPointer = head; + while (count < mid) { + rightPointer = rightPointer.next; + count++; + } + return rightPointer; +} + +var llLength = (head) => { + let count = 0; + while (head) { + head = head.next; + count++; + } + return count; +}; + +var reverseLL = (head, len) => { + let count = 0; + let temp = null; + while (count < len) { + const next = head.next; + head.next = temp; + temp = head; + head = next; + count++; + } + return temp; +}; diff --git a/javascript/2160-minimum-sum-of-four-digit-number-after-splitting-digits.js b/javascript/2160-minimum-sum-of-four-digit-number-after-splitting-digits.js new file mode 100644 index 000000000..acbc88fd0 --- /dev/null +++ b/javascript/2160-minimum-sum-of-four-digit-number-after-splitting-digits.js @@ -0,0 +1,8 @@ +/** + * @param {number} num + * @return {number} + */ +var minimumSum = function (num) { + let str = String(num).split("").sort(); // intialize str and split the num using String(), split() and sort() + return parseInt(str[0] + str[2]) + parseInt(str[1] + str[3]); // return sum of 1st, 3rd and 2nd, 4th string digit and convert into num using parseInt +}; \ No newline at end of file diff --git a/javascript/2215-find-the-difference-of-two-arrays.js b/javascript/2215-find-the-difference-of-two-arrays.js new file mode 100644 index 000000000..9560bfb06 --- /dev/null +++ b/javascript/2215-find-the-difference-of-two-arrays.js @@ -0,0 +1,18 @@ +/** + * @param {number[]} nums1 + * @param {number[]} nums2 + * @return {number[][]} + */ + +// Time Complexity: O(m + n), we check each element of nums1Set and nums2Set +// Space Complexity: O(m + n), where m and n are length sets in worst case. + +var findDifference = function (nums1, nums2) { + const nums1Set = new Set(nums1); + const nums2Set = new Set(nums2); + + const lst1 = Array.from(nums1Set).filter((num) => !nums2Set.has(num)); + const lst2 = Array.from(nums2Set).filter((num) => !nums1Set.has(num)); + + return [lst1, lst2]; +}; diff --git a/javascript/2235-add-two-integers.js b/javascript/2235-add-two-integers.js new file mode 100644 index 000000000..0ab61fe5a --- /dev/null +++ b/javascript/2235-add-two-integers.js @@ -0,0 +1,8 @@ +/** + * @param {number} num1 + * @param {number} num2 + * @return {number} + */ +var sum = function (num1, num2) { + return num1 + num2; // add num1 and num2 and return it +}; \ No newline at end of file diff --git a/javascript/2306-naming-a-company.js b/javascript/2306-naming-a-company.js new file mode 100644 index 000000000..652daa7a2 --- /dev/null +++ b/javascript/2306-naming-a-company.js @@ -0,0 +1,34 @@ +/** + * @param {string[]} ideas + * @return {number} + */ +var distinctNames = function(ideas) { + let sets = []; + for (let i = 0; i < 26; i++) { + sets[i] = new Set(); + } + for (let s of ideas) { + sets[s.charCodeAt(0) - 97].add(s.substring(1)); + } + let same = []; + for (let i = 0; i < 26; i++) { + same[i] = Array(26).fill(0); + } + for (let i = 0; i < 26; i++) { + for (let s of sets[i]) { + for (let j = i + 1; j < 26; j++) { + if (sets[j].has(s)) { + same[i][j]++; + } + } + } + } + let res = 0; + for (let i = 0; i < 26; i++) { + for (let j = i + 1; j < 26; j++) { + res += (sets[i].size - same[i][j]) * (sets[j].size - same[i][j]) * 2; + } + } + return res; + +}; \ No newline at end of file diff --git a/javascript/2331-evaluate-boolean-binary-tree.js b/javascript/2331-evaluate-boolean-binary-tree.js new file mode 100644 index 000000000..8f9018b57 --- /dev/null +++ b/javascript/2331-evaluate-boolean-binary-tree.js @@ -0,0 +1,31 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * DFS | Tree-traversal + * Time O(n) | Space O(1) + * https://leetcode.com/problems/evaluate-boolean-binary-tree + * @param {TreeNode} root + * @return {boolean} + */ +var evaluateTree = function(root) { + return dfs(root); +}; + +const dfs = (node) => { + + if (!node.left && !node.right && node.val) return true; + if (!node.left && !node.right && !node.val) return false; + + const is2 = (node.val === 2); + if (is2) return dfs(node.left) || dfs(node.right); + + const is3 = (node.val === 3); + if (is3) return dfs(node.left) && dfs(node.right); + +} diff --git a/javascript/2348-number-of-zero-filled-subarrays.js b/javascript/2348-number-of-zero-filled-subarrays.js new file mode 100644 index 000000000..7d445f219 --- /dev/null +++ b/javascript/2348-number-of-zero-filled-subarrays.js @@ -0,0 +1,35 @@ +/** + * 2348. Number of Zero-Filled Subarrays + * ----------------------- + * link: https://leetcode.com/problems/number-of-zero-filled-subarrays/ + * + * description: + * length => no of sub arrays + * '0' => 1 + * '00' => 3 + * '000' => 6 + * '0000' => 10 + * for each zero we found count++ and adding count to result + * if the element not zero => count = 0 + * + * time : O(n) + * space : O(1) + */ + +/** + * @param {number[]} nums + * @return {number} + */ + +let zeroFilledSubarray = function (nums) { + let result = 0; + let count = 0; + + for (const num of nums) { + if (num === 0) count++; + else count = 0; + result += count; + } + + return result; +}; \ No newline at end of file diff --git a/javascript/2390-removing-stars-from-a-string.js b/javascript/2390-removing-stars-from-a-string.js new file mode 100644 index 000000000..d23be4497 --- /dev/null +++ b/javascript/2390-removing-stars-from-a-string.js @@ -0,0 +1,13 @@ +var removeStars = function(s) { + if(!s.length) return ''; + + const result = []; + + for(let char of s){ + if(char == '*') result.pop() + else result.push(char) + } + return result.join('') +}; +// Time Complexity: O(n) +// Space Complexity: O(n) diff --git a/javascript/2421-number-of-good-paths.js b/javascript/2421-number-of-good-paths.js new file mode 100644 index 000000000..041acf7e1 --- /dev/null +++ b/javascript/2421-number-of-good-paths.js @@ -0,0 +1,113 @@ +class UnionFind { + constructor(n) { + this.parent = new Array(n).fill(0).map((_, i) => i); + this.rank = new Array(n).fill(0); + } + + /** + * + * @param {number} i + * @returns {number} + */ + find(i) { + while (i !== this.parent[i]) { + this.parent[i] = this.parent[this.parent[i]]; + i = this.parent[i]; + } + return i; + } + + /** + * + * @param {number} a + * @param {number} b + * @returns {boolean} + */ + union(a, b) { + let [aRoot, bRoot] = [this.find(a), this.find(b)]; + + if (aRoot == bRoot) return false; + + if (this.rank[aRoot] < this.rank[bRoot]) { + this.parent[aRoot] = bRoot; + this.rank[bRoot] += this.rank[aRoot]; + } else { + this.parent[bRoot] = aRoot; + this.rank[aRoot] += this.rank[bRoot]; + } + + return true; + } +} + +/** + * + * @param {number[number[]]} edges + * @returns {Map} + */ +const getAdjList = (edges) => { + let adj = new Map(); + + for (e of edges) { + let [a, b] = [e[0], e[1]]; + + let [adjA, adjB] = [adj.get(a) || [], adj.get(b) || []]; + + adjA.push(b); + adjB.push(a); + + adj.set(a, adjA); + adj.set(b, adjB); + } + + return adj; +}; + +const getValToIndex = (vals) => { + let valToIndex = new Map(); + + for (i in vals) { + let val = vals[i]; + let arr = valToIndex.get(val) || []; + arr.push(+i); + valToIndex.set(val, arr); + } + + return valToIndex; +}; + +/** + * + * @param {number[]} vals + * @param {number[number[]]} edges + * @returns {number} + */ +const numberOfGoodPaths = (vals, edges) => { + let adj = getAdjList(edges); + let valToIndex = getValToIndex(vals); + + let res = 0; + let uf = new UnionFind(vals.length); + + let keys = Array.from(valToIndex.keys()); + keys.sort((a, b) => a - b); + + for (let val of keys) { + for (let i of valToIndex.get(val)) { + for (let nei of adj.get(i) || []) { + if (vals[nei] <= vals[i]) { + uf.union(nei, i); + } + } + } + let count = new Map(); + + for (let i of valToIndex.get(val)) { + let c = count.get(uf.find(i)) || 0; + count.set(uf.find(i), c + 1); + res += count.get(uf.find(i)); + } + } + + return res; +}; diff --git a/javascript/2427-number-of-common-factors.js b/javascript/2427-number-of-common-factors.js new file mode 100644 index 000000000..e6ebe8d5e --- /dev/null +++ b/javascript/2427-number-of-common-factors.js @@ -0,0 +1,21 @@ +/** + * @param {number} a + * @param {number} b + * @return {number} + */ +var commonFactors = function (a, b) { + let arr = []; // initialize empty array arr + let min = Math.min(a, b); // store minimum number from a and b into min using Math.min() + + // loop thorugh every number from 1 to min + for (let i = 1; i <= min; i++) { + + // if a anf b both divisable by i then push i into array arr + if (a % i == 0 && b % i == 0) { + arr.push(i); + } + } + + // return length of array arr + return arr.length; +}; \ No newline at end of file diff --git a/javascript/2439-minimize-maximum-of-array.js b/javascript/2439-minimize-maximum-of-array.js new file mode 100644 index 000000000..5e106276d --- /dev/null +++ b/javascript/2439-minimize-maximum-of-array.js @@ -0,0 +1,16 @@ +/** + * Time O(n) | Space O(1) + * @param {number[]} nums + * @return {number} + */ +var minimizeArrayValue = function(nums) { + + let currTotal = nums[0]; + let max = nums[0]; + + for (let i = 1; i < nums.length; i++) { + currTotal += nums[i]; + max = Math.max(max, Math.ceil(currTotal / (i + 1))); + } + return max; +}; diff --git a/javascript/2469-convert-the-temperature.js b/javascript/2469-convert-the-temperature.js new file mode 100644 index 000000000..36e4359b7 --- /dev/null +++ b/javascript/2469-convert-the-temperature.js @@ -0,0 +1,9 @@ +/** + * @param {number} celsius + * @return {number[]} + */ +var convertTemperature = function(celsius) { + let kelvin = celsius + 273.15; // initialize kelvin and add 273.15 to celsius + let fahrenheit = (celsius * 1.80) + 32; // initialize fahrenheit and multiply 1.80 to celsius and add 32 + return [kelvin, fahrenheit]; // return kelvin and fahrenheit as an array +}; \ No newline at end of file diff --git a/javascript/2485-find-the-pivot-integer.js b/javascript/2485-find-the-pivot-integer.js new file mode 100644 index 000000000..563788033 --- /dev/null +++ b/javascript/2485-find-the-pivot-integer.js @@ -0,0 +1,15 @@ +/** + * @param {number} n + * @return {number} + */ +var pivotInteger = function (n) { + + // calculate the total sum fo n + const totalSum = n * (n + 1) / 2; + + // find the square root of totalSum using Math.sqrt() + const sqrtVal = Math.sqrt(totalSum); + + // if sqrtVal is equal to round down value of sqrtVal then return round down value of sqrtVal otherwise -1 + return Math.floor(sqrtVal) === sqrtVal ? Math.floor(sqrtVal) : -1; +}; \ No newline at end of file diff --git a/javascript/2542-maximum-subsequence-score.js b/javascript/2542-maximum-subsequence-score.js new file mode 100644 index 000000000..7e3d45ee7 --- /dev/null +++ b/javascript/2542-maximum-subsequence-score.js @@ -0,0 +1,47 @@ +/** + * MinPriorityQueue | Sorting + * Time O(n*log(n)) | space O(n) + * https://leetcode.com/problems/maximum-subsequence-score/ + * @param {number[]} nums1 + * @param {number[]} nums2 + * @param {number} k + * @return {number} + */ +var maxScore = function(nums1, nums2, k) { + + const minQ = new MinPriorityQueue({ + compare: (a,b) => { + return a - b; + } + }); + + let maxScore = 0; + let runningTotal = 0; + let takenElements = 0; + + const nums12 = nums1.map((num, idx) => { + return [num, nums2[idx]]; + }); + + nums12.sort((a,b) => { + return b[1] - a[1]; + }); + + for(let i = 0; i < nums12.length; i++) { + const n1 = nums12[i][0]; + const n2 = nums12[i][1]; + + runningTotal += n1; + minQ.enqueue(n1); + + if(minQ.size() === k) { + maxScore = Math.max(maxScore, runningTotal * n2); + } + if(minQ.size() > k) { + runningTotal -= minQ.dequeue(); + maxScore = Math.max(maxScore, runningTotal * n2); + } + } + + return maxScore; +}; diff --git a/javascript/2620-counter.js b/javascript/2620-counter.js new file mode 100644 index 000000000..f42e74a6d --- /dev/null +++ b/javascript/2620-counter.js @@ -0,0 +1,16 @@ +/** + * @param {number} n + * @return {Function} counter + */ +var createCounter = function(n) { + return function() { + return n++; + }; +}; + +/** + * const counter = createCounter(10) + * counter() // 10 + * counter() // 11 + * counter() // 12 + * diff --git a/javascript/2621-sleep.js b/javascript/2621-sleep.js new file mode 100644 index 000000000..3e79979bf --- /dev/null +++ b/javascript/2621-sleep.js @@ -0,0 +1,12 @@ +/** + * @param {number} millis + * @return {Promise} + */ +async function sleep(millis) { + return new Promise(resolve => setTimeout(() => resolve("Completed!"), millis)); +} + +/** + * let t = Date.now() + * sleep(100).then(() => console.log(Date.now() - t)) // 100 + */ diff --git a/javascript/2623-memoize.js b/javascript/2623-memoize.js new file mode 100644 index 000000000..9fd662e49 --- /dev/null +++ b/javascript/2623-memoize.js @@ -0,0 +1,24 @@ +/** + * @param {Function} fn + * @return {Function} + */ +function memoize(fn) { + let memo = {}; + return function (...args) { + let argsjson = JSON.stringify(args); + if (memo.hasOwnProperty(argsjson)) + return memo[argsjson]; + return memo[argsjson] = fn(...args); + } +} + +/** + * let callCount = 0; + * const memoizedFn = memoize(function (a, b) { + * callCount += 1; + * return a + b; + * }) + * memoizedFn(2, 3) // 5 + * memoizedFn(2, 3) // 5 + * console.log(callCount) // 1 + */ diff --git a/javascript/2626-array-reduce-transformation.js b/javascript/2626-array-reduce-transformation.js new file mode 100644 index 000000000..f695fe046 --- /dev/null +++ b/javascript/2626-array-reduce-transformation.js @@ -0,0 +1,12 @@ +/** + * @param {number[]} nums + * @param {Function} fn + * @param {number} init + * @return {number} + */ +var reduce = function (nums, fn, init) { + ans = init; + for (let n of nums) + ans = fn(ans, n); + return ans; +}; diff --git a/javascript/2627-debounce.js b/javascript/2627-debounce.js new file mode 100644 index 000000000..55c83420d --- /dev/null +++ b/javascript/2627-debounce.js @@ -0,0 +1,19 @@ +/** + * @param {Function} fn + * @param {number} t milliseconds + * @return {Function} + */ +var debounce = function (fn, t) { + let timeoutHandle; + return function (...args) { + clearTimeout(timeoutHandle); + timeoutHandle = setTimeout(() => fn(...args), t); + } +}; + +/** + * const log = debounce(console.log, 100); + * log('Hello'); // cancelled + * log('Hello'); // cancelled + * log('Hello'); // Logged at t=100ms + */ diff --git a/javascript/2628-json-deep-equal.js b/javascript/2628-json-deep-equal.js new file mode 100644 index 000000000..908a392a2 --- /dev/null +++ b/javascript/2628-json-deep-equal.js @@ -0,0 +1,39 @@ +/** + * @param {any} o1 + * @param {any} o2 + * @return {boolean} + */ +var areDeeplyEqual = function(o1, o2) { + if (o1 === null || o2 === null) { + return o1 === o2; + } + if (typeof o1 !== typeof o2) { + return false; + } + if (typeof o1 !== 'object') { // primitives + return o1 === o2; + } + + if (Array.isArray(o1) && Array.isArray(o2)) { // Arrays + if (o1.length !== o2.length) { + return false; + } + for (let i = 0; i < o1.length; i++) { + if (!areDeeplyEqual(o1[i], o2[i])) { + return false; + } + } + } else if (!Array.isArray(o1) && !Array.isArray(o2)) { // Objects + if (Object.keys(o1).length !== Object.keys(o2).length) { + return false; + } + for (const key in o1) { + if (!areDeeplyEqual(o1[key], o2[key])) { + return false; + } + } + } else { + return false; + } + return true; +}; diff --git a/javascript/2629-function-composition.js b/javascript/2629-function-composition.js new file mode 100644 index 000000000..6899a5d4e --- /dev/null +++ b/javascript/2629-function-composition.js @@ -0,0 +1,17 @@ +/** + * @param {Function[]} functions + * @return {Function} + */ +var compose = function (functions) { + return function (x) { + let ans = x; + for (fn of functions.reverse()) + ans = fn(ans); + return ans; + } +}; + +/** + * const fn = compose([x => x + 1, x => 2 * x]) + * fn(4) // 9 + */ diff --git a/javascript/2632-curry.js b/javascript/2632-curry.js new file mode 100644 index 000000000..ed6ee86bb --- /dev/null +++ b/javascript/2632-curry.js @@ -0,0 +1,20 @@ +/** + * @param {Function} fn + * @return {Function} + */ +var curry = function (fn) { + let accum = []; + return function curried(...args) { + for (let arg of args) + accum.push(arg); + if (accum.length === fn.length) + return fn(...accum); + return curried; + } +}; + +/** + * function sum(a, b) { return a + b; } + * const csum = curry(sum); + * csum(1)(2) // 3 + */ diff --git a/javascript/2634-filter-elements-from-array.js b/javascript/2634-filter-elements-from-array.js new file mode 100644 index 000000000..57ff4277c --- /dev/null +++ b/javascript/2634-filter-elements-from-array.js @@ -0,0 +1,12 @@ +/** + * @param {number[]} arr + * @param {Function} fn + * @return {number[]} + */ +var filter = function (arr, fn) { + ans = []; + for (let i = 0; i < arr.length; i++) + if (fn(arr[i], i)) + ans.push(arr[i]); + return ans; +}; diff --git a/javascript/2635-apply-transform-over-each-element-in-array.js b/javascript/2635-apply-transform-over-each-element-in-array.js new file mode 100644 index 000000000..419dd3ef2 --- /dev/null +++ b/javascript/2635-apply-transform-over-each-element-in-array.js @@ -0,0 +1,12 @@ +/** + * @param {number[]} arr + * @param {Function} fn + * @return {number[]} + */ +const map = (arr, fn) => { + const result = []; + for (let i = 0; i < arr.length; i++) { + result.push(fn(arr[i], i)); + } + return result; +} \ No newline at end of file diff --git a/javascript/2651-calculate-delayed-arrival-time.js b/javascript/2651-calculate-delayed-arrival-time.js new file mode 100644 index 000000000..cc3c00c49 --- /dev/null +++ b/javascript/2651-calculate-delayed-arrival-time.js @@ -0,0 +1,16 @@ +/** + * @param {number} arrivalTime + * @param {number} delayedTime + * @return {number} + */ +var findDelayedArrivalTime = function (arrivalTime, delayedTime) { + let totalTime = arrivalTime + delayedTime; // initialize totalTime is the sum of arrivalTime and delayedTime + + if (totalTime == 24) { // if totalTime is equal to 24 then return 0 + return 0; + } else if (totalTime < 24) { // if totalTime is less than 24 then return totalTime + return totalTime; + } else { // else return subtraction of totalTime to 24 + return totalTime - 24; + } +}; \ No newline at end of file diff --git a/javascript/2652-sum-multiples.js b/javascript/2652-sum-multiples.js new file mode 100644 index 000000000..7cf8b0b48 --- /dev/null +++ b/javascript/2652-sum-multiples.js @@ -0,0 +1,14 @@ +/** + * @param {number} n + * @return {number} + */ +var sumOfMultiples = function (n) { + let arr = []; // initilialize an empty array + for (let i = 1; i <= n; i++) { // loop through the 1 to n + if ((i % 3 === 0) || (i % 5 === 0) || (i % 7 === 0)) { // if i is divisible by 3 or 5 or 7 then push i into arr + arr.push(i); + } + } + let result = arr.reduce((a, b) => a + b, 0); // find sum of all element of arr and store into result + return result; // return the result +}; \ No newline at end of file diff --git a/javascript/2665-counter-ii.js b/javascript/2665-counter-ii.js new file mode 100644 index 000000000..9c2d9b451 --- /dev/null +++ b/javascript/2665-counter-ii.js @@ -0,0 +1,29 @@ +/** + * @param {integer} init + * @return { increment: Function, decrement: Function, reset: Function } + */ +var createCounter = function(init) { + let count = init; + + increment = () => ++count; + + decrement = () => --count; + + reset = () => { + count = init; + return count; + } + + return { + increment, + decrement, + reset + } +}; + +/** + * const counter = createCounter(5) + * counter.increment(); // 6 + * counter.reset(); // 5 + * counter.decrement(); // 4 + */ diff --git a/javascript/2666-allow-one-function-call.js b/javascript/2666-allow-one-function-call.js new file mode 100644 index 000000000..34afa8fba --- /dev/null +++ b/javascript/2666-allow-one-function-call.js @@ -0,0 +1,21 @@ +/** + * @param {Function} fn + * @return {Function} + */ +var once = function (fn) { + let called = false; + return function (...args) { + if (!called) { + called = true; + return fn(...args); + } + } +}; + +/** + * let fn = (a,b,c) => (a + b + c) + * let onceFn = once(fn) + * + * onceFn(1,2,3); // 6 + * onceFn(2,3,6); // returns undefined without calling fn + */ diff --git a/javascript/2667-create-hello-world-function.js b/javascript/2667-create-hello-world-function.js new file mode 100644 index 000000000..034fe6c50 --- /dev/null +++ b/javascript/2667-create-hello-world-function.js @@ -0,0 +1,13 @@ +/** + * @return {Function} + */ +var createHelloWorld = function() { + return function(...args) { + return "Hello World" + } +}; + +/** + * const f = createHelloWorld(); + * f(); // "Hello World" + */ diff --git a/javascript/2704-to-be-or-not-to-be.js b/javascript/2704-to-be-or-not-to-be.js new file mode 100644 index 000000000..4231b276c --- /dev/null +++ b/javascript/2704-to-be-or-not-to-be.js @@ -0,0 +1,21 @@ +/** + * Creates an expectation object for testing values. + * + * @param {any} val - The value to be tested. + * @returns {Object} An object with two methods: + * - toBe(expected): Returns true if val === expected, otherwise throws an error "Not Equal". + * - notToBe(expected): Returns true if val !== expected, otherwise throws an error "Equal". + */ +const expect = (val) => { + const throwError = (message) => { throw new Error(message); }; + return { + toBe: (expected) => val === expected || throwError("Not Equal"), + notToBe: (expected) => val !== expected || throwError("Equal") + }; +}; + +// Example usage: +// expect(5).toBe(5); // returns true +// expect(5).notToBe(3); // returns true +// expect(5).toBe(3); // throws "Not Equal" +// expect(5).notToBe(5); // throws "Equal" \ No newline at end of file diff --git a/javascript/2706-buy-two-chocolates.js b/javascript/2706-buy-two-chocolates.js new file mode 100644 index 000000000..b3ec36584 --- /dev/null +++ b/javascript/2706-buy-two-chocolates.js @@ -0,0 +1,15 @@ +/** + * Greedy | Array + * Time O(n) | Space O(1) + * https://leetcode.com/problems/buy-two-chocolates + * @param {number[]} prices + * @param {number} money + * @return {number} + */ +var buyChoco = function(prices, money) { + + const [cheapestChocolate] = prices.splice(prices.indexOf(Math.min(...prices)), 1); + const [secondCheapestChocolate] = prices.splice(prices.indexOf(Math.min(...prices)), 1); + const leftOverMoney = money - (cheapestChocolate + secondCheapestChocolate); + return leftOverMoney > -1 ? leftOverMoney : money; +}; diff --git a/javascript/2710-remove-trailing-zeros-from-a-string.js b/javascript/2710-remove-trailing-zeros-from-a-string.js new file mode 100644 index 000000000..3a212df02 --- /dev/null +++ b/javascript/2710-remove-trailing-zeros-from-a-string.js @@ -0,0 +1,12 @@ +/** + * @param {string} num + * @return {string} + */ +var removeTrailingZeros = function (num) { + + for (let i = num.length - 1; i >= 0; i--) { // loop through the every element of string num from end + if (num[i] != 0) { // if every character of num is not equal to zero + return num.slice(0, i + 1); // return the character of num upto ith character + } + } +}; \ No newline at end of file diff --git a/javascript/2769-find-the-maximum-achievable-number.js b/javascript/2769-find-the-maximum-achievable-number.js new file mode 100644 index 000000000..7b4c3c11c --- /dev/null +++ b/javascript/2769-find-the-maximum-achievable-number.js @@ -0,0 +1,8 @@ +/** + * @param {number} num + * @param {number} t + * @return {number} + */ +var theMaximumAchievableX = function (num, t) { + return num + (t * 2) // return sum of value of num and twice the value of t +}; \ No newline at end of file diff --git a/javascript/2810-faulty-keyboard.js b/javascript/2810-faulty-keyboard.js new file mode 100644 index 000000000..0db8aa2f2 --- /dev/null +++ b/javascript/2810-faulty-keyboard.js @@ -0,0 +1,23 @@ +/** + * @param {string} s + * @return {string} + */ +var finalString = function (s) { + // initialize empty string str + let str = ""; + + // loop thorugh the every character of string s + for (let i = 0; i < s.length; i++) { + + // if every character of string is i then reverse the previous string character and store in string str + if (s[i] == 'i') { + str = [...str].reverse().join(''); + } else { + // else add every character of string s with str and store in string str + str += s[i]; + } + } + + // return string str + return str; +}; \ No newline at end of file diff --git a/javascript/2864-maximum-odd-binary-number.js b/javascript/2864-maximum-odd-binary-number.js new file mode 100644 index 000000000..c44db142a --- /dev/null +++ b/javascript/2864-maximum-odd-binary-number.js @@ -0,0 +1,21 @@ +/** + * Greedy + * Time O(n) | Space O(n) + * @param {string} s + * @return {string} + */ +var maximumOddBinaryNumber = function(s) { + + let numberOf1s = s.split("").filter((bit) => bit === "1").length; + s = s.split("").map((bit) => "0"); + + let i = 0; + while (numberOf1s > 1) { + s[i] = "1"; + i++; + numberOf1s--; + } + + s[s.length - 1] = 1; + return s.join(""); +}; diff --git a/javascript/abc.txt b/javascript/abc.txt deleted file mode 100644 index 8baef1b4a..000000000 --- a/javascript/abc.txt +++ /dev/null @@ -1 +0,0 @@ -abc diff --git a/kotlin/0001-two-sum.kt b/kotlin/0001-two-sum.kt new file mode 100644 index 000000000..4bb2f4b8c --- /dev/null +++ b/kotlin/0001-two-sum.kt @@ -0,0 +1,21 @@ +package kotlin + +class Solution { + fun twoSum(nums: IntArray, target: Int): IntArray { + val prevMap: HashMap = HashMap() + for (i in nums.indices) { + val num = nums[i] + val diff = target - num + if (prevMap.containsKey(diff)) { + return intArrayOf(prevMap[diff]!!, i) + } + prevMap[num] = i + } + return intArrayOf() + } +} + +fun main() { + val result = Solution().twoSum(intArrayOf(2, 7, 11, 15), 9) + println(result.contentToString()) +} diff --git a/kotlin/0002-add-two-numbers.kt b/kotlin/0002-add-two-numbers.kt new file mode 100644 index 000000000..3e1881d14 --- /dev/null +++ b/kotlin/0002-add-two-numbers.kt @@ -0,0 +1,22 @@ +package kotlin + +class Solution { + fun addTwoNumbers(l1: ListNode?, l2: ListNode?): ListNode? { + var n1 = l1 + var n2 = l2 + var carry = 0 + var sum: Int + val dummyNode = ListNode(-1) + var resultantList = dummyNode + while (n1 != null || n2 != null) { + sum = (n1?.`val` ?: 0) + (n2?.`val` ?: 0) + carry + carry = sum / 10 + resultantList.next = ListNode(sum % 10) + resultantList = resultantList.next!! + n1 = n1?.next + n2 = n2?.next + } + if (carry != 0) resultantList.next = ListNode(carry) + return dummyNode.next + } +} \ No newline at end of file diff --git a/kotlin/0003-longest-substring-without-repeating-characters.kt b/kotlin/0003-longest-substring-without-repeating-characters.kt new file mode 100644 index 000000000..cadf83ca5 --- /dev/null +++ b/kotlin/0003-longest-substring-without-repeating-characters.kt @@ -0,0 +1,17 @@ +class Solution { + fun lengthOfLongestSubstring(s: String): Int { + val hs = HashSet() + var i = 0 + var j = 0 + var maxLength = 0 + while (j < s.length) { + if (hs.contains(s[j])) { + hs.remove(s[i++]) + } else { + hs.add(s[j++]) + maxLength = maxLength.coerceAtLeast(hs.size) + } + } + return maxLength + } +} diff --git a/kotlin/0004-median-of-two-sorted-arrays.kt b/kotlin/0004-median-of-two-sorted-arrays.kt new file mode 100644 index 000000000..2cd9644a1 --- /dev/null +++ b/kotlin/0004-median-of-two-sorted-arrays.kt @@ -0,0 +1,14 @@ +class Solution { + fun findMedianSortedArrays(nums1: IntArray, nums2: IntArray): Double { + var i1 = 0 + var i2 = 0 + fun getNext() = when { + i1 < nums1.size && i2 < nums2.size -> if (nums1[i1] < nums2[i2]) nums1[i1++] else nums2[i2++] + i1 < nums1.size -> nums1[i1++] + else -> nums2[i2++] + } + val arr = IntArray(nums1.size + nums2.size) { getNext() } + return if (arr.size % 2 == 1) arr[arr.size / 2].toDouble() + else (arr[arr.size / 2] + arr[arr.size / 2 - 1]) / 2.0 + } +} \ No newline at end of file diff --git a/kotlin/0005-longest-palindromic-substring.kt b/kotlin/0005-longest-palindromic-substring.kt new file mode 100644 index 000000000..eeaa69d6f --- /dev/null +++ b/kotlin/0005-longest-palindromic-substring.kt @@ -0,0 +1,32 @@ +class Solution { + fun longestPalindrome(s: String): String { + var start = 0 + var maxL = 0 + + for (i in 0 until s.length) { + var l = i + var r = i + while (l >= 0 && r < s.length && s[l] == s[r]) { + if (r - l + 1 > maxL) { + start = l + maxL = (r - l + 1) + } + l-- + r++ + } + + l = i + r = i + 1 + while (l >= 0 && r < s.length && s[l] == s[r]) { + if (r - l + 1 > maxL) { + start = l + maxL = (r - l + 1) + } + l-- + r++ + } + } + + return s.substring(start, start + maxL) + } +} diff --git a/kotlin/0006-zigzag-conversion.kt b/kotlin/0006-zigzag-conversion.kt new file mode 100644 index 000000000..2896f44d2 --- /dev/null +++ b/kotlin/0006-zigzag-conversion.kt @@ -0,0 +1,18 @@ +class Solution { + fun convert(s: String, numRows: Int): String { + if (numRows == 1) return s + + val res = StringBuilder() + for (r in 0 until numRows) { + val increment = 2 * (numRows - 1) + for (i in r until s.length step increment) { + res.append(s[i]) + if (r > 0 && r < numRows - 1 + && i + increment - 2 * r < s.length) + res.append(s[i + increment - 2 * r]) + } + } + + return res.toString() + } +} diff --git a/kotlin/0007-reverse-integer.kt b/kotlin/0007-reverse-integer.kt new file mode 100644 index 000000000..ee4ab78b5 --- /dev/null +++ b/kotlin/0007-reverse-integer.kt @@ -0,0 +1,22 @@ +class Solution { + fun reverse(x: Int): Int { + val MIN = Int.MIN_VALUE + val MAX = Int.MAX_VALUE + + var res = 0 + var value = x + while (value != 0) { + var digit = value % 10 + value /= 10 + + if (res > MAX / 10 || (res == MAX / 10 && digit >= MAX % 10)) { + return 0 + } + if (res < MIN / 10 || (res == MIN / 10 && digit <= MIN % 10)) { + return 0 + } + res = (res * 10) + digit + } + return res + } +} \ No newline at end of file diff --git a/kotlin/0009-palindrome-number.kt b/kotlin/0009-palindrome-number.kt new file mode 100644 index 000000000..de9165658 --- /dev/null +++ b/kotlin/0009-palindrome-number.kt @@ -0,0 +1,15 @@ +class Solution { + fun isPalindrome(x: Int): Boolean { + if (x < 0) return false + + var reverse = 0 + var x2 = x + while (x2 != 0) { + reverse *= 10 + reverse += x2 % 10 + x2 /= 10 + } + + return reverse == x + } +} diff --git a/kotlin/0010-regular-expression-matching.kt b/kotlin/0010-regular-expression-matching.kt new file mode 100644 index 000000000..eb3ce16d6 --- /dev/null +++ b/kotlin/0010-regular-expression-matching.kt @@ -0,0 +1,47 @@ +//dp +class Solution { + fun isMatch(s: String, p: String): Boolean { + val dp = Array (s.length + 1) { BooleanArray (p.length + 1) } + dp[0][0] = true + + for (i in 0..s.length) { + for (j in 1..p.length) { + if (p[j - 1] == '*') { + dp[i][j] = dp[i][j - 2] || (i > 0 && dp[i - 1][j] && (s[i - 1] == p[j - 2] || p[j - 2] == '.')) + } else { + dp[i][j] = i > 0 && dp[i - 1][j - 1] && (s[i - 1] == p[j - 1] || p[j - 1] == '.') + } + } + } + + return dp[s.length][p.length] + } +} + +//recursion + memoization +class Solution { + fun isMatch(s: String, p: String): Boolean { + val cache = Array (s.length + 1) { IntArray (p.length + 1) { -1 } } + + fun dfs(i: Int, j: Int): Int { + if (i >= s.length && j >= p.length) + return 1 + if (j >= p.length) + return 0 + if (cache[i][j] != -1) + return cache[i][j] + + val charMatched = i < s.length && (s[i] == p[j] || p[j] == '.') + + if (j + 1 < p.length && p[j + 1] == '*') + return if (dfs(i, j + 2) == 1 || charMatched && dfs(i + 1, j) == 1) 1 else 0 + if (charMatched) + return dfs(i + 1, j + 1) + + cache[i][j] == 0 + return 0 + } + + return dfs(0, 0) == 1 + } +} diff --git a/kotlin/0011-container-with-most-water.kt b/kotlin/0011-container-with-most-water.kt new file mode 100644 index 000000000..ede5f6ffe --- /dev/null +++ b/kotlin/0011-container-with-most-water.kt @@ -0,0 +1,22 @@ + class Solution { + fun maxArea(heights: IntArray): Int { + + if(heights.isEmpty()) return 0 + + val size = heights.size + var lo = 0 + var hi = size -1 + var maxArea = 0 + + while(lo < hi){ + val area = minOf(heights[lo], heights[hi]) * (hi - lo) + maxArea = maxOf(maxArea, area) + + if(heights[lo] < heights[hi]) + ++lo + else --hi + } + + return maxArea + } +} \ No newline at end of file diff --git a/kotlin/0012-integer-to-roman.kt b/kotlin/0012-integer-to-roman.kt new file mode 100644 index 000000000..c56d40f66 --- /dev/null +++ b/kotlin/0012-integer-to-roman.kt @@ -0,0 +1,30 @@ +class Solution { + fun intToRoman(_num: Int): String { + val symList = listOf( + "I" to 1, + "IV" to 4, + "V" to 5, + "IX" to 9, + "X" to 10, + "XL" to 40, + "L" to 50, + "XC" to 90, + "C" to 100, + "CD" to 400, + "D" to 500, + "CM" to 900, + "M" to 1000 + ) + + return buildString { + var num = _num + for ((sym, value) in symList.reversed()) { + if (num / value > 0) { + val count = num / value + append(sym.repeat(count)) + num = num % value + } + } + } + } +} diff --git a/kotlin/0013-roman-to-integer.kt b/kotlin/0013-roman-to-integer.kt new file mode 100644 index 000000000..d0b751b3a --- /dev/null +++ b/kotlin/0013-roman-to-integer.kt @@ -0,0 +1,23 @@ +class Solution { + fun romanToInt(s: String): Int { + val roman = mapOf( + 'I' to 1, + 'V' to 5, + 'X' to 10, + 'L' to 50, + 'C' to 100, + 'D' to 500, + 'M' to 1000 + ) + + var res = 0 + for (i in s.indices) { + if (i + 1 < s.length && roman[s[i]]!! < roman[s[i + 1]]!!) + res -= roman[s[i]]!! + else + res += roman[s[i]]!! + } + + return res + } +} diff --git a/kotlin/0014-longest-common-prefix.kt b/kotlin/0014-longest-common-prefix.kt new file mode 100644 index 000000000..1335d43ff --- /dev/null +++ b/kotlin/0014-longest-common-prefix.kt @@ -0,0 +1,84 @@ +/* +* Solution as per the channel +*/ +class Solution { + fun longestCommonPrefix(strs: Array): String { + var len = 0 + outerloop@ for(i in 0 until strs[0].length){ + for(s in strs){ + if(i == s.length || s[i] != strs[0][i]){ + break@outerloop + } + } + len++ + } + return strs[0].substring(0,len) + } +} + +/* +* Same solution as above but a little more in an idiomatic Kotlin way +*/ +class Solution { + fun longestCommonPrefix(strs: Array): String { + var res = "" + strs.minBy { it.length }?.forEachIndexed { i,c -> + if(strs.all { it[i] == c } ) res += c else return res + } + return res + } +} + +/* +* Trie solution +*/ +class TrieNode() { + val child = arrayOfNulls(26) + var isEnd = false + fun childCount() = this.child?.filter{it != null}?.count() +} + +class Solution { + fun longestCommonPrefix(strs: Array): String { + + val root: TrieNode? = TrieNode() + + for(word in strs) { + var current = root + for(c in word){ + if(current?.child?.get(c - 'a') == null){ + current?.child?.set(c - 'a', TrieNode()) + } + current = current?.child?.get(c - 'a') + } + current?.isEnd = true + } + + var current = root + var len = 0 + for (c in strs[0]){ + println(c) + if (current?.childCount() == 1 && current?.isEnd != true) len++ else break + current = current?.child?.get(c - 'a') + } + println(len) + return strs[0].substring(0,len) + } +} + +/* +* Sorting solution +*/ +class Solution { + fun longestCommonPrefix(strs: Array): String { + var len = 0 + strs.sort() + val first = strs[0] + val last = strs[strs.size-1] + val minLen = strs.minBy {it.length}?.length!! + for(i in 0 until minLen){ + if(first[i] == last[i]) len++ else break + } + return strs[0].substring(0,len) + } +} diff --git a/kotlin/0015-3sum.kt b/kotlin/0015-3sum.kt new file mode 100644 index 000000000..af022eecb --- /dev/null +++ b/kotlin/0015-3sum.kt @@ -0,0 +1,43 @@ +class Solution { + fun threeSum(nums: IntArray): List> { + if(nums.size<3){ + return List(0){List(0){0}} + } + if(nums.size==3){ + return if(nums.sum()==0) listOf(nums.toList()) else List(0){List(0){0}} + } + nums.sort() + val a = MutableList(0){List(3){0}} + val s = nums.size + var j = 0 + var k = 0 + + for(i in 0 until s-2){ + if(i!=0 && nums[i]==nums[i-1]){ + continue + } + j = i+1 + k = s-1 + while(j0){ + k -= 1 + } + else if(nums[i]+nums[j]+nums[k]<0){ + j += 1 + } + else{ + a.add(listOf(nums[i], nums[j], nums[k])) + j += 1 + k -= 1 + while(j { + if (digits.isEmpty()) return emptyList() + val resultantList = mutableListOf() + val stringBuilder = StringBuilder() + val digitCharListMapping = mutableMapOf( + '2' to listOf('a', 'b', 'c'), + '3' to listOf('d', 'e', 'f'), + '4' to listOf('g', 'h', 'i'), + '5' to listOf('j', 'k', 'l'), + '6' to listOf('m', 'n', 'o'), + '7' to listOf('p', 'q', 'r', 's'), + '8' to listOf('t', 'u', 'v'), + '9' to listOf('w', 'x', 'y', 'z') + ) + + fun dfs(decisionIndex: Int = 0) { + if (stringBuilder.length == digits.length) { + resultantList.add(stringBuilder.toString()) + return + } + val charListForDigitAtDecisionIndex = digitCharListMapping.getValue(digits[decisionIndex]) + for (char in charListForDigitAtDecisionIndex) { + stringBuilder.append(char) + dfs(decisionIndex + 1) + stringBuilder.deleteCharAt(stringBuilder.lastIndex) + } + } + dfs() + return resultantList + } +} diff --git a/kotlin/0018-4sum.kt b/kotlin/0018-4sum.kt new file mode 100644 index 000000000..66064c3be --- /dev/null +++ b/kotlin/0018-4sum.kt @@ -0,0 +1,46 @@ +class Solution { + fun fourSum(nums: IntArray, target: Int): List> { + nums.sort() + val res = ArrayList>() + val temp = ArrayList() + + fun kSum(k: Int, start: Int, targetSum: Long) { + if (k != 2) { + for (i in start..(nums.size - k)) { + if (i > start && nums[i - 1] == nums[i]) + continue + + temp.add(0, nums[i]) + kSum(k - 1, i + 1, targetSum - nums[i]) + temp.removeAt(0) + } + + return + } + + var left = start + var right = nums.lastIndex + while (left < right) { + val sum = nums[left].toLong() + nums[right].toLong() + if (sum < targetSum) { + left++ + } else if (sum > targetSum) { + right-- + } else { + temp.add(0, nums[left]) + temp.add(0, nums[right]) + res.add(ArrayList(temp)) + temp.removeAt(0) + temp.removeAt(0) + + left++ + while (left < right && nums[left - 1] == nums[left]) + left++ + } + } + } + + kSum(4, 0, target.toLong()) + return res + } +} diff --git a/kotlin/0019-remove-nth-node-from-end-of-list.kt b/kotlin/0019-remove-nth-node-from-end-of-list.kt new file mode 100644 index 000000000..bbb10d019 --- /dev/null +++ b/kotlin/0019-remove-nth-node-from-end-of-list.kt @@ -0,0 +1,23 @@ +package kotlin + +class ListNode(var `val`: Int) { + var next: ListNode? = null +} + +class Solution { + fun removeNthFromEnd(head: ListNode?, n: Int): ListNode? { + var fastPointer = head + var slowPointer: ListNode? = null + var i = 1 + while (fastPointer != null && i <= n) { + fastPointer = fastPointer.next + i++ + } + while (fastPointer != null) { + slowPointer = if (slowPointer == null) head else slowPointer.next + fastPointer = fastPointer.next + } + slowPointer?.next = slowPointer?.next?.next + return if (slowPointer == null) head?.next else head + } +} \ No newline at end of file diff --git a/kotlin/0020-valid-parentheses.kt b/kotlin/0020-valid-parentheses.kt new file mode 100644 index 000000000..163f36d54 --- /dev/null +++ b/kotlin/0020-valid-parentheses.kt @@ -0,0 +1,16 @@ +class Solution { + fun isValid(s: String): Boolean { + if (s.length % 2 == 1) return false + + val map = hashMapOf('(' to ')', '[' to ']', '{' to '}') + val stack = Stack() + + for (c in s) { + if (map.containsKey(c)) + stack.push(c) + else if (stack.isEmpty() || map[stack.pop()] != c) + return false + } + return stack.isEmpty() + } +} diff --git a/kotlin/0021-merge-two-sorted-lists.kt b/kotlin/0021-merge-two-sorted-lists.kt new file mode 100644 index 000000000..7e741a4aa --- /dev/null +++ b/kotlin/0021-merge-two-sorted-lists.kt @@ -0,0 +1,31 @@ +class Solution { + fun mergeTwoLists(l1: ListNode?, l2: ListNode?): ListNode? { + var r1 = l1 + var r2 = l2 + val ll = ListNode(0) + var rr = ll + while(true){ + if(r1==null && r2==null){ + break + } + if(r1==null){ + rr.next = r2 + break + } + if(r2==null){ + rr.next = r1 + break + } + if(r1.`val`<=r2.`val`){ + rr.next = r1 + r1 = r1.next + } + else{ + rr.next = r2 + r2 = r2.next + } + rr = rr.next + } + return ll.next + } +} \ No newline at end of file diff --git a/kotlin/0022-generate-parentheses.kt b/kotlin/0022-generate-parentheses.kt new file mode 100644 index 000000000..c64b9bd93 --- /dev/null +++ b/kotlin/0022-generate-parentheses.kt @@ -0,0 +1,31 @@ +class Solution { + private val resultList = mutableListOf() + + fun generateParenthesis(n: Int): List { + generateParenthesis(n * 2, "") + return resultList + } + + private fun generateParenthesis(target: Int, current: String) { + if (current.length == target) { + var open = 0 + var close = 0 + for (i in 0 until current.length) { + if (current[i] == '(') { + ++open + } else { + ++close + } + if (close > open) { + break + } + } + if (close == open) { + resultList.add(current) + } + } else { + generateParenthesis(target, "${current}(") + generateParenthesis(target, "${current})") + } + } +} \ No newline at end of file diff --git a/kotlin/0023-merge-k-sorted-lists.kt b/kotlin/0023-merge-k-sorted-lists.kt new file mode 100644 index 000000000..823f63894 --- /dev/null +++ b/kotlin/0023-merge-k-sorted-lists.kt @@ -0,0 +1,40 @@ +package kotlin + +class Solution{ + fun mergeKLists(lists: Array): ListNode? { + if (lists.isEmpty()) return null + var mergeInterval = 1 + while (mergeInterval < lists.size) { + for (i in 0..lists.lastIndex step mergeInterval * 2) { + lists[i] = merge(lists[i], if (i + mergeInterval <= lists.lastIndex) lists[i + mergeInterval] else null) + } + mergeInterval *= 2 + } + return lists[0] + } + + private fun merge(l1: ListNode?, l2: ListNode?): ListNode? { + val dummyNode = ListNode(-1) + var currentNodeInList1 = l1 + var currentNodeInList2 = l2 + var currentNodeInResultantList:ListNode? = dummyNode + + while(currentNodeInList1!=null && currentNodeInList2!=null){ + if (currentNodeInList1.`val`>=currentNodeInList2.`val`){ + currentNodeInResultantList?.next = currentNodeInList2 + currentNodeInList2 = currentNodeInList2.next + }else{ + currentNodeInResultantList?.next = currentNodeInList1 + currentNodeInList1 = currentNodeInList1.next + } + currentNodeInResultantList = currentNodeInResultantList?.next + } + + currentNodeInResultantList?.next = when{ + currentNodeInList1!=null -> currentNodeInList1 + currentNodeInList2!=null -> currentNodeInList2 + else -> null + } + return dummyNode.next + } +} diff --git a/kotlin/0024-swap-nodes-in-pairs.kt b/kotlin/0024-swap-nodes-in-pairs.kt new file mode 100644 index 000000000..5a89b0ad9 --- /dev/null +++ b/kotlin/0024-swap-nodes-in-pairs.kt @@ -0,0 +1,30 @@ +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun swapPairs(head: ListNode?): ListNode? { + var dummy = ListNode(0, head) + var prev = dummy + var cur = head + + while (cur != null && cur.next != null) { + val next = cur.next + val nextNext = cur.next.next + + next.next = cur + cur.next = nextNext + prev.next = next + + prev = cur + cur = nextNext + } + + return dummy.next + } +} diff --git a/kotlin/0025-reverse-nodes-in-k-group.kt b/kotlin/0025-reverse-nodes-in-k-group.kt new file mode 100644 index 000000000..dbb32d0ad --- /dev/null +++ b/kotlin/0025-reverse-nodes-in-k-group.kt @@ -0,0 +1,55 @@ +package kotlin + +class Solution { + + private fun reverse(startNode: ListNode?, endNode: ListNode?): ListNode? { + if (startNode == null) return null + var currentNode = startNode + var temp: ListNode? + var previousNode: ListNode? = null + + while (currentNode != null) { + temp = currentNode.next + currentNode.next = previousNode + previousNode = currentNode + if (currentNode === endNode) break + currentNode = temp + } + + return previousNode + } + + private fun getKthNode(headNode: ListNode, k: Int): ListNode? { + var currentNode: ListNode? = headNode + var counter = 1 + while (currentNode != null && counter < k) { + counter++ + currentNode = currentNode.next + } + return currentNode + } + + fun reverseKGroup(head: ListNode?, k: Int): ListNode? { + if (head == null) return null + + val dummyNode = ListNode(-1).apply { next = head } + var kthNode: ListNode? + var currentNode = head + var previousNodeOfCurrentGroup = dummyNode + var nextNodeOfCurrentGroup: ListNode? + + while (currentNode != null) { + kthNode = getKthNode(currentNode, k) + if (kthNode == null) break + nextNodeOfCurrentGroup = kthNode.next + previousNodeOfCurrentGroup.next = reverse(startNode = currentNode, endNode = kthNode) + currentNode.next = nextNodeOfCurrentGroup + previousNodeOfCurrentGroup = currentNode + currentNode = nextNodeOfCurrentGroup + } + + return dummyNode.next + } + + +} diff --git a/kotlin/0026-remove-duplicates-from-sorted-array.kt b/kotlin/0026-remove-duplicates-from-sorted-array.kt new file mode 100644 index 000000000..b0d65cc38 --- /dev/null +++ b/kotlin/0026-remove-duplicates-from-sorted-array.kt @@ -0,0 +1,13 @@ +class Solution { + fun removeDuplicates(nums: IntArray): Int { + var last = 0 + var i = 0 + while(i < nums.size) { + var j = i + while(j < nums.size && nums[i] == nums[j]) j++ + nums[last++] = nums[i] + i = j + } + return last + } +} diff --git a/kotlin/0027-remove-element.kt b/kotlin/0027-remove-element.kt new file mode 100644 index 000000000..e7b9cf79a --- /dev/null +++ b/kotlin/0027-remove-element.kt @@ -0,0 +1,13 @@ +class Solution { + fun removeElement(nums: IntArray, `val`: Int): Int { + var pointer = 0 + for(n in nums){ + if(n != `val`){ + nums[pointer] = n + pointer++ + } + } + return pointer + } + +} diff --git a/kotlin/0028-find-the-index-of-the-first-occurrence-in-a-string.kt b/kotlin/0028-find-the-index-of-the-first-occurrence-in-a-string.kt new file mode 100644 index 000000000..786692891 --- /dev/null +++ b/kotlin/0028-find-the-index-of-the-first-occurrence-in-a-string.kt @@ -0,0 +1,179 @@ +/* +* KMP algorithm +*/ +class Solution { + fun strStr(haystack: String, needle: String): Int { + if(needle == "") return 0 + + val lps = IntArray(needle.length) + var prevLPS = 0 + var i = 1 + while(i < needle.length) { + if(needle[i] == needle[prevLPS]) { + lps[i] = prevLPS + 1 + prevLPS++ + i++ + }else if(prevLPS == 0) { + lps[i] = 0 + i++ + }else{ + prevLPS = lps[prevLPS - 1] + } + } + + i = 0 + var j = 0 + while (i < haystack.length) { + if(haystack[i] == needle[j]){ + i++ + j++ + }else if(j == 0){ + i++ + }else{ + j = lps[j - 1] + } + if(j == needle.length) { + return i - needle.length + } + } + + return -1 + } +} + +/* +* Rabin-Karp string hashing with bad hash +*/ +class Solution { + fun strStr(haystack: String, needle: String): Int { + if(needle.length > haystack.length) return -1 + + var needleHash = 0 + var hayHash = 0 + + for(i in 0..needle.lastIndex) { + needleHash += needle[i] - 'a' + hayHash += haystack[i] - 'a' + } + + for(i in 0..(haystack.length - needle.length)) { + if(hayHash == needleHash) { + for(j in 0..needle.lastIndex) { + if(haystack[i + j] != needle[j]) + break + if(j == needle.lastIndex) + return i + } + } + if(i == haystack.length - needle.length) + break + hayHash -= haystack[i] - 'a' + hayHash += haystack[i + needle.length] - 'a' + } + + return -1 + } +} + +/* +* Rabin-karp with proper hash. q should ideally be chosen as an high prime number to avoid no. of collisions. +*/ +class Solution { + fun strStr(haystack: String, needle: String): Int { + if(needle.length > haystack.length) return -1 + + val q = 101 + val d = 256 + var needleHash = 0 + var hayHash = 0 + var hash = 1 + + for (i in 0..needle.lastIndex) + hash = (hash * d) % q + + for(i in 0..needle.lastIndex) { + needleHash = (d * needleHash + (needle[i] - 'a')) % q + hayHash = (d * hayHash + (haystack[i] - 'a')) % q + } + + for(i in 0..(haystack.length - needle.length)) { + if(hayHash == needleHash) { + for(j in 0..needle.lastIndex) { + if(haystack[i + j] != needle[j]) + break + if(j == needle.lastIndex) + return i + } + } + if(i == haystack.length - needle.length) + break + hayHash = (d * hayHash - ((haystack[i] - 'a') * hash) + (haystack[i + needle.length] - 'a')) % q + if(hayHash < 0) + hayHash += q + } + + return -1 + } +} + +/* +* Using Trie to match pattern +*/ +class TrieNode() { + val child = arrayOfNulls(26) + var isEnd = false +} + +class Solution { + fun strStr(haystack: String, needle: String): Int { + if(needle == "") return 0 + + var root: TrieNode? = TrieNode() + + var current = root + for(c in needle){ + if(current?.child?.get(c - 'a') == null) + current?.child?.set(c - 'a', TrieNode()) + current = current?.child?.get(c - 'a') + } + current?.isEnd = true + + var i = 0 + while(i < haystack.length + 1 - needle.length) { + current = root?.child?.get(haystack[i] - 'a') + var j = i + while(current != null) { + if(current.isEnd == true) + return i + j++ + if(j - i < needle.length) + current = current?.child?.get(haystack[j] - 'a') + else + break + } + i++ + } + + return -1 + } +} + +/* +* Brute force +*/ +class Solution { + fun strStr(haystack: String, needle: String): Int { + if(needle == "") return 0 + + for(i in 0..(haystack.length - needle.length)) { + for(j in 0..needle.lastIndex) { + if(haystack[i + j] != needle[j]) + break + if(j == needle.lastIndex) + return i + } + } + + return -1 + } +} diff --git a/kotlin/0033-search-in-rotated-sorted-array.kt b/kotlin/0033-search-in-rotated-sorted-array.kt new file mode 100644 index 000000000..18afc894d --- /dev/null +++ b/kotlin/0033-search-in-rotated-sorted-array.kt @@ -0,0 +1,32 @@ +package kotlin + +class Solution { + fun search(nums: IntArray, target: Int): Int { + var l = 0 + var r = nums.size - 1 + + while(l <= r){ + val m = l + (r - l) / 2 + + if(nums[m] == target) return m + + //left sorted portion + if(nums[l] <= nums[m]){ + if(target > nums[m] || target < nums[l]){ + l = m + 1 + } else { + r = m - 1 + } + } else { + // right sorted portion + if(target < nums[m] || target > nums[r]){ + r = m - 1 + } else { + l = m + 1 + } + } + } + + return - 1 + } +} diff --git a/kotlin/0034-find-first-and-last-position-of-element-in-sorted-array.kt b/kotlin/0034-find-first-and-last-position-of-element-in-sorted-array.kt new file mode 100644 index 000000000..c77bafad5 --- /dev/null +++ b/kotlin/0034-find-first-and-last-position-of-element-in-sorted-array.kt @@ -0,0 +1,32 @@ +//same solution but with function +class Solution { + fun searchRange(nums: IntArray, target: Int): IntArray { + val res = intArrayOf(-1,-1) + + fun binarySearch(getLeft: Boolean): Int{ + var left = 0 + var right = nums.size-1 + var pos = -1 + while(left <= right){ + val mid = (left + right) / 2 + if(target > nums[mid]){ + left = mid + 1 + }else if(target < nums[mid]){ + right = mid - 1 + }else{ + pos = mid + if(getLeft) + right = mid -1 + else + left = mid + 1 + } + } + return pos + } + + res[0] = binarySearch(true) + res[1] = binarySearch(false) + + return res + } +} diff --git a/kotlin/0035-search-insert-position.kt b/kotlin/0035-search-insert-position.kt new file mode 100644 index 000000000..dfbf18600 --- /dev/null +++ b/kotlin/0035-search-insert-position.kt @@ -0,0 +1,16 @@ +class Solution { + fun searchInsert(nums: IntArray, target: Int): Int { + + var l = 0 + var r = nums.lastIndex + + while(l <= r) { + val mid = (l + r) / 2 + if (target == nums[mid]) return mid + if (target > nums[mid]) l = mid + 1 + else r = mid - 1 + } + + return l + } +} diff --git a/kotlin/0036-valid-sudoku.kt b/kotlin/0036-valid-sudoku.kt new file mode 100644 index 000000000..bfed73478 --- /dev/null +++ b/kotlin/0036-valid-sudoku.kt @@ -0,0 +1,31 @@ +package kotlin + +class Solution { + fun isValidSudoku(board: Array): Boolean { + val columnHashSets = Array(9) { HashSet() } + val rowHashSets = Array(9) { HashSet() } + val subMatrixHashSets = arrayOf( + arrayOf(HashSet(), HashSet(), HashSet()), + arrayOf(HashSet(), HashSet(), HashSet()), + arrayOf(HashSet(), HashSet(), HashSet()) + ) + for (i in 0 until 9) { + for (j in 0 until 9) { + // continue if is not a digit + if (!board[i][j].isDigit()) continue + val value = Character.getNumericValue(board[i][j]) + // check column + if (value in columnHashSets[i]) return false + columnHashSets[i].add(value) + // check row + if (value in rowHashSets[j]) return false + rowHashSets[j].add(value) + // check sub matrix + if (value in subMatrixHashSets[j / 3][i / 3]) return false + // add the element to the hashset of the corresponding sub 3x3 matrix + subMatrixHashSets[j / 3][i / 3].add(value) + } + } + return true + } +} \ No newline at end of file diff --git a/kotlin/0039-combination-sum.kt b/kotlin/0039-combination-sum.kt new file mode 100644 index 000000000..e2a9a1049 --- /dev/null +++ b/kotlin/0039-combination-sum.kt @@ -0,0 +1,24 @@ +class Solution { + fun combinationSum(candidates: IntArray, target: Int): List> { + val res = mutableListOf>() + dfs(candidates, target, 0, mutableListOf(), res) + return res + } + + fun dfs(nums: IntArray, target: Int, idx: Int, list: MutableList, res: MutableList>) { + + if (target == 0) { + res.add(ArrayList(list)) + return + } + + if (idx >= nums.size || target < 0) + return + + for (i in idx..nums.size-1) { + list.add(nums[i]) + dfs(nums, target - nums[i], i, list, res) + list.removeAt(list.size-1) + } + } +} \ No newline at end of file diff --git a/kotlin/0040-combination-sum-ii.kt b/kotlin/0040-combination-sum-ii.kt new file mode 100644 index 000000000..bbd8c1cf2 --- /dev/null +++ b/kotlin/0040-combination-sum-ii.kt @@ -0,0 +1,27 @@ +class Solution { + fun combinationSum2(candidates: IntArray, target: Int): List> { + val res = mutableListOf>() + candidates.sort() + dfs(candidates, target, 0, mutableListOf(), res) + return res + } + + fun dfs(nums: IntArray, target: Int, idx: Int, list: MutableList, res: MutableList>) { + + if (target == 0) { + res.add(ArrayList(list)) + return + } + + if (idx >= nums.size || target < 0) + return + + for (i in idx..nums.size-1) { + if (i == idx || nums[i] != nums[i-1]) { + list.add(nums[i]) + dfs(nums, target - nums[i], i+1, list, res) + list.removeAt(list.size-1) + } + } + } +} \ No newline at end of file diff --git a/kotlin/0041-first-missing-positive.kt b/kotlin/0041-first-missing-positive.kt new file mode 100644 index 000000000..be5a3376a --- /dev/null +++ b/kotlin/0041-first-missing-positive.kt @@ -0,0 +1,20 @@ +class Solution { + fun firstMissingPositive(nums: IntArray): Int { + var i = 0 + // if we encounter a valid integer, swap it into its right place. Ignore all numbers larger than nums.length + while(i < nums.size) { + val pos = nums[i] - 1 + if(pos in (0 until nums.size) && nums[i] != nums[pos]) nums.swap(i, pos) + else i++ + } + + //if the expected number is not in its right place, return it + for(i in nums.indices){ + if (nums[i] != i + 1) return i + 1 + } + return nums.size + 1 + } + private fun IntArray.swap(i: Int, j: Int) { + this[i] = this[j].also{ this[j] = this[i]} + } +} diff --git a/kotlin/0042-trapping-rain-water.kt b/kotlin/0042-trapping-rain-water.kt new file mode 100644 index 000000000..d6208f728 --- /dev/null +++ b/kotlin/0042-trapping-rain-water.kt @@ -0,0 +1,24 @@ +import kotlin.math.max + +class Solution { + fun trap(height: IntArray): Int { + var volumeOfWaterTrapped = 0 + var l = 0 + var r = height.lastIndex + var currentLeftMax = -1 + var currentRightMax = -1 + + while (l < r) { + if (height[l] <= height[r]) { + currentLeftMax = max(currentLeftMax, height[l]) + l++ + volumeOfWaterTrapped += (currentLeftMax - height[l]).coerceAtLeast(0) + } else { + currentRightMax = max(currentRightMax, height[r]) + r-- + volumeOfWaterTrapped += (currentRightMax - height[r]).coerceAtLeast(0) + } + } + return volumeOfWaterTrapped + } +} \ No newline at end of file diff --git a/kotlin/0043-multiply-strings.kt b/kotlin/0043-multiply-strings.kt new file mode 100644 index 000000000..0eee68794 --- /dev/null +++ b/kotlin/0043-multiply-strings.kt @@ -0,0 +1,26 @@ + +class Solution { + fun multiply(num1: String, num2: String): String { + if (num1=="0" || num2=="0") return "0" + + val sum = IntArray(num1.length + num2.length) + val n1 = num1.reversed() + val n2 = num2.reversed() + + for (i in 0 until n1.length) { + for (j in 0 until n2.length) { + var temp = (n1[i] - '0') * (n2[j] - '0') + sum[i + j] + sum[i + j + 1] += temp / 10 + sum[i + j] = temp % 10 + } + } + + return StringBuilder().apply { + for(i in sum.lastIndex downTo 0) { + if (this.length == 0 && sum[i] == 0) continue + this.append(sum[i]) + } + }.toString() + + } +} diff --git a/kotlin/0045-jump-game-ii.kt b/kotlin/0045-jump-game-ii.kt new file mode 100644 index 000000000..936268057 --- /dev/null +++ b/kotlin/0045-jump-game-ii.kt @@ -0,0 +1,50 @@ +/* +* O(n) BFS +*/ +class Solution { + fun jump(nums: IntArray): Int { + var left = 0 + var right = 0 + var res = 0 + + while (right < nums.size - 1) { + var maxJump = 0 + for (i in left..right) { + maxJump = maxOf(maxJump, i + nums[i]) + } + left = right + 1 + right = maxJump + res += 1 + } + return res + } +} + +/* +* O(N^2) DP + memoization +*/ +class Solution { + fun jump(nums: IntArray): Int { + val dp = IntArray(nums.size) { 10001 } + + fun jump(i: Int): Int { + if (i == nums.lastIndex) + return 0 + if (dp[i] != 10001) + return dp[i] + + for (steps in 1..nums[i]) { + if (i + steps <= nums.lastIndex) { + dp[i] = minOf( + dp[i], + 1 + jump(i + steps) + ) + } + } + + return dp[i] + } + + return jump(0) + } +} diff --git a/kotlin/0046-permutations.kt b/kotlin/0046-permutations.kt new file mode 100644 index 000000000..6c2efc950 --- /dev/null +++ b/kotlin/0046-permutations.kt @@ -0,0 +1,51 @@ +// solution based on the video +class Solution { + fun permute(nums: IntArray): List> { + val res = mutableListOf>() + val queue = ArrayDeque(nums.toList()) + + // base case + if (queue.size == 1) { + return listOf(queue.toMutableList()) // queue.toList() is a deep copy + } + + for (i in nums.indices) { + val n = queue.removeFirst() + val perms = permute(queue.toIntArray()) + + for (perm in perms) { + perm.add(n) + res.add(perm) + } + queue.addLast(n) + } + return res + } +} + +// original solution +class Solution { + fun permute(nums: IntArray): List> { + val res = mutableListOf>() + permute(nums, mutableSetOf(), mutableListOf(), res) + return res + } + + fun permute(nums: IntArray, set: MutableSet, list: MutableList, res: MutableList>) { + if (list.size == nums.size) { + res.add(ArrayList(list)) + return + } + + for (i in 0..nums.size-1) { + if (!set.contains(nums[i])) { + list.add(nums[i]) + set.add(nums[i]) + permute(nums, set, list, res) + list.removeAt(list.size-1) + set.remove(nums[i]) + } + } + } +} + diff --git a/kotlin/0047-permutations-ii.kt b/kotlin/0047-permutations-ii.kt new file mode 100644 index 000000000..3d4156d67 --- /dev/null +++ b/kotlin/0047-permutations-ii.kt @@ -0,0 +1,31 @@ +class Solution { + fun permuteUnique(nums: IntArray): List> { + val count = HashMap() + val permut = LinkedList() + val res = LinkedList>() + + for (n in nums) count[n] = count.getOrDefault(n, 0) + 1 + + fun backtrack() { + if (permut.size == nums.size) { + res.add(LinkedList(permut)) + return + } + + for (n in count.keys) { + if (count[n]!! > 0) { + permut.addLast(n) + count[n] = count[n]!! - 1 + + backtrack() + + count[n] = count[n]!! + 1 + permut.removeLast() + } + } + } + + backtrack() + return res + } +} diff --git a/kotlin/0048-rotate-image.kt b/kotlin/0048-rotate-image.kt new file mode 100644 index 000000000..e6eaba8a4 --- /dev/null +++ b/kotlin/0048-rotate-image.kt @@ -0,0 +1,17 @@ +class Solution { + fun rotate(matrix: Array): Unit { + var left = 0; var right = matrix.size-1 + while (left < right){ + for(i in 0..(right-left)-1){ + val top = left; val bot = right + val topleft = matrix[top][left+i] + matrix[top][left+i] = matrix[bot-i][left] + matrix[bot-i][left] = matrix[bot][right-i] + matrix[bot][right-i] = matrix[top+i][right] + matrix[top+i][right] = topleft + } + left++ + right-- + } + } +} diff --git a/kotlin/0049-group-anagrams.kt b/kotlin/0049-group-anagrams.kt new file mode 100644 index 000000000..bc65b6d13 --- /dev/null +++ b/kotlin/0049-group-anagrams.kt @@ -0,0 +1,25 @@ +package kotlin + +fun main() { + + val strs = arrayOf("eat", "tea", "tan", "ate", "nat", "bat") + + println(groupAnagrams(strs)) +} + +fun groupAnagrams(strs: Array): List> { + val res: HashMap> = hashMapOf() + + for (s in strs){ + val count = IntArray(26) + + for (c in s){ + val index = c - 'a' + count[index] += 1 + } + + res[count.joinToString()] = res.getOrDefault(count.joinToString(), mutableListOf()).also { it.add(s) } + } + + return res.values.toList() +} \ No newline at end of file diff --git a/kotlin/0050-powx-n.kt b/kotlin/0050-powx-n.kt new file mode 100644 index 000000000..f411e1156 --- /dev/null +++ b/kotlin/0050-powx-n.kt @@ -0,0 +1,19 @@ +class Solution { + fun myPow(x: Double, n: Int): Double { + val sum = myPowHelper(x, n) + return if(n < 0) 1/sum else sum // if power to the negative + } + private fun myPowHelper(x: Double, n: Int): Double { + if(x == 0.0) + return 0.0 + when(n){ + 0 -> return 1.0 + 1 -> return x + else -> { + var res = myPowHelper(x, n/2) + res *= res + return if(n%2==0) res else x * res //if odd number, we multiply the "lost" number from integer division + } + } + } +} diff --git a/kotlin/0051-n-queens.kt b/kotlin/0051-n-queens.kt new file mode 100644 index 000000000..c44e50ccb --- /dev/null +++ b/kotlin/0051-n-queens.kt @@ -0,0 +1,53 @@ +class Solution { + fun solveNQueens(n: Int): List> { + val cols = HashSet() //keep track of used columns, we iterate rows + val posDia = HashSet() //...of positive diagonals R+C + val negDia = HashSet() //...of negative diagonals R-C + val temp: ArrayList> = arrayListOf() // to hold our temporary distinct solution + val res: ArrayList> = arrayListOf() // result with each distinct solutin where each input is a solution + fillWithQueens(0,n,res,cols,posDia,negDia,temp) + return res + } + private fun fillWithQueens( + row: Int, + n: Int, + res: ArrayList>, + cols: HashSet, + posDia: HashSet, + negDia: HashSet, + board: ArrayList> + ){ + //if we have filled the whole board with queens + if(row == n){ + val complete: ArrayList = arrayListOf() + for(i in 0..n-1){ + val joined = board[i].joinToString(separator = "") + complete.add(joined) + } + res.add(complete) + return + } + for(column in 0 until n){ + if(cols.contains(column) || posDia.contains(row+column) || negDia.contains(row-column)) + continue + val temp = tempRow(n) + board.add(temp) + cols.add(column) + posDia.add(row+column) + negDia.add(row-column) + board[row][column] = "Q" + fillWithQueens(row+1,n,res,cols,posDia,negDia,board) + cols.remove(column) + posDia.remove(row+column) + negDia.remove(row-column) + board[row][column] = "." + } + } + private fun tempRow(n: Int): ArrayList{ + val temp: ArrayList = arrayListOf() + repeat(n){ + temp.add(".") + } + return temp + } +} diff --git a/kotlin/0052-n-queens-ii.kt b/kotlin/0052-n-queens-ii.kt new file mode 100644 index 000000000..dd307a27e --- /dev/null +++ b/kotlin/0052-n-queens-ii.kt @@ -0,0 +1,34 @@ +class Solution { + fun totalNQueens(n: Int): Int { + + val cols = HashSet() + val pD = HashSet() + val nD = HashSet() + var res = 0 + + fun fill(row: Int) { + + if(row == n) { + res++ + return + } + + for(col in 0 until n) { + if(cols.contains(col) || pD.contains(row + col) || nD.contains(row - col)) continue + + cols.add(col) + pD.add(row + col) + nD.add(row - col) + + fill(row+1) + + cols.remove(col) + pD.remove(row + col) + nD.remove(row - col) + } + } + + fill(0) + return res + } +} diff --git a/kotlin/0053-maximum-subarray.kt b/kotlin/0053-maximum-subarray.kt new file mode 100644 index 000000000..33a56a85e --- /dev/null +++ b/kotlin/0053-maximum-subarray.kt @@ -0,0 +1,11 @@ +class Solution { + fun maxSubArray(nums: IntArray): Int { + var sum = nums[0] + var currsum = 0 + for (num in nums) { + currsum = maxOf(currsum + num, num) + sum = maxOf(currsum, sum) + } + return sum + } +} diff --git a/kotlin/0054-spiral-matrix.kt b/kotlin/0054-spiral-matrix.kt new file mode 100644 index 000000000..9e6618b88 --- /dev/null +++ b/kotlin/0054-spiral-matrix.kt @@ -0,0 +1,51 @@ +package kotlin + + +fun spiralOrder(matrix: Array): List { + var res = mutableListOf() + var top = 0 + var right = matrix.first().size + var left = 0 + var bottom = matrix.size + + while (top < bottom && left < right) { + + //get every i in the top row + for (i in left until right) { + res.add(matrix[top][i]) + } + top += 1 + + // get every i in the right col + for (i in top until bottom) { + res.add(matrix[i][right - 1]) + } + right -= 1 + + if (!(left < right && top < bottom)) { + break + } + + //get every i in the bottom row + for (i in right - 1 downTo left) { + res.add(matrix[bottom - 1][i]) + } + bottom -= 1 + + //get every i in the left col + for (i in bottom - 1 downTo top) { + res.add(matrix[i][left]) + } + + left += 1 + + } + + return res + +} + +fun main() { + val matrix = arrayOf(intArrayOf(1, 2, 3), intArrayOf(4, 5, 6), intArrayOf(7, 8, 9)) + println(spiralOrder(matrix)) +} \ No newline at end of file diff --git a/kotlin/0055-jump-game.kt b/kotlin/0055-jump-game.kt new file mode 100644 index 000000000..d41702aca --- /dev/null +++ b/kotlin/0055-jump-game.kt @@ -0,0 +1,42 @@ +/* +* O(n) greedy +*/ +class Solution { + fun canJump(nums: IntArray): Boolean { + var goal = nums.size - 1 + + for (i in nums.size - 2 downTo 0) { + if (i + nums[i] >= goal) { + goal = i + } + } + return goal == 0 + } +} + +/* +* O(n^2) DP + memoization +*/ +class Solution { + fun canJump(nums: IntArray): Boolean { + val deadEnd = BooleanArray(nums.size) + + fun canJump(i: Int): Boolean { + if (i == nums.lastIndex) + return true + if (nums[i] == 0 || deadEnd[i] == true) + return false + + for (jump in 1..nums[i]) { + if (i + jump <= nums.lastIndex && canJump(i + jump)) { + return true + } + } + + deadEnd[i] = true + return false + } + + return canJump(0) + } +} diff --git a/kotlin/0056-merge-intervals.kt b/kotlin/0056-merge-intervals.kt new file mode 100644 index 000000000..4ff28db1d --- /dev/null +++ b/kotlin/0056-merge-intervals.kt @@ -0,0 +1,16 @@ +class Solution { + fun merge(intervals: Array): Array { + intervals.sortBy{ it.first() } + val list = LinkedList() + + for (interval in intervals) { + if (list.size == 0 || list.last[1] < interval[0]) { + list.add(interval) + } else { + list.last[1] = Math.max(list.last[1], interval[1]) + } + } + + return list.toTypedArray() + } +} \ No newline at end of file diff --git a/kotlin/0057-insert-interval.kt b/kotlin/0057-insert-interval.kt new file mode 100644 index 000000000..52cc37677 --- /dev/null +++ b/kotlin/0057-insert-interval.kt @@ -0,0 +1,28 @@ +class Solution { + fun insert(intervals: Array, newInterval: IntArray): Array { + val res = ArrayList() + var added = false + var index = 0 + for(i in 0 until intervals.size){ + val interval = intervals[i] + if(newInterval[1] < interval[0]){ //no more overlapping intervals + res.add(newInterval) + added = true + break + }else if(newInterval[0] > interval[1]){ //non overlapping + res.add(interval) + }else{ //overlapping, update the newinterval accordingly + newInterval[0] = minOf(newInterval[0],interval[0]) + newInterval[1] = maxOf(newInterval[1],interval[1]) + } + index++ + } + if(index < intervals.size){ // add all the leftover intervals (after the break) + for(i in index until intervals.size) + res.add(intervals[i]) + } + if(added == false) // takes care of (1) Empty intervals array or 1-sized array with overlapping newinterval + res.add(newInterval) + return res.toTypedArray() + } +} diff --git a/kotlin/0058-length-of-last-word.kt b/kotlin/0058-length-of-last-word.kt new file mode 100644 index 000000000..fde859431 --- /dev/null +++ b/kotlin/0058-length-of-last-word.kt @@ -0,0 +1,12 @@ +class Solution { + fun lengthOfLastWord(s: String): Int { + var p = s.length-1 + while(s[p].isWhitespace()) p-- + var count = 0 + while(p >= 0 && !s[p].isWhitespace()){ + count++ + p-- + } + return count + } +} diff --git a/kotlin/0059-spiral-matrix-ii.kt b/kotlin/0059-spiral-matrix-ii.kt new file mode 100644 index 000000000..74d460c8f --- /dev/null +++ b/kotlin/0059-spiral-matrix-ii.kt @@ -0,0 +1,33 @@ +class Solution { + fun generateMatrix(n: Int): Array { + var left = 0 + var right = n - 1 + var top = 0 + var bot = n - 1 + var count = 1 + val m = Array(n) { IntArray(n) } + + while (left <= right && top <= bot) { + for (i in left..right) + m[top][i] = count++ + top++ + + for (i in top..bot) + m[i][right] = count++ + right-- + + if (left > right || top > bot) + break + + for (i in right downTo left) + m[bot][i] = count++ + bot-- + + for (i in bot downTo top) + m[i][left] = count++ + left++ + } + + return m + } +} diff --git a/kotlin/0061-rotate-list.kt b/kotlin/0061-rotate-list.kt new file mode 100644 index 000000000..3355d7382 --- /dev/null +++ b/kotlin/0061-rotate-list.kt @@ -0,0 +1,25 @@ +class Solution { + fun rotateRight(head: ListNode?, _k: Int): ListNode? { + head?: return null + + var length = 1 + var tail = head + while (tail?.next != null) { + tail = tail?.next + length++ + } + + val k = _k % length + if (k == 0) return head + + var cur = head + for (i in 0 until (length - k - 1)) + cur = cur?.next + + val newHead = cur?.next + cur?.next = null + tail?.next = head + + return newHead + } +} diff --git a/kotlin/0062-unique-paths.kt b/kotlin/0062-unique-paths.kt new file mode 100644 index 000000000..e0387b9cd --- /dev/null +++ b/kotlin/0062-unique-paths.kt @@ -0,0 +1,17 @@ +class Solution { + fun uniquePaths(m: Int, n: Int): Int { + val dp = Array(m + 1) { IntArray(n + 1) { 0 } } + val indexM = m - 1 + val indexN = n - 1 + for (i in indexM downTo 0) { + for (j in indexN downTo 0) { + if (i == indexM && j == indexN) { + dp[i][j] = 1 + } else { + dp[i][j] = dp[i + 1][j] + dp[i][j + 1] + } + } + } + return dp[0][0] + } +} \ No newline at end of file diff --git a/kotlin/0063-unique-paths-ii.kt b/kotlin/0063-unique-paths-ii.kt new file mode 100644 index 000000000..e3dae114a --- /dev/null +++ b/kotlin/0063-unique-paths-ii.kt @@ -0,0 +1,56 @@ +// If we can't do inplace, space O(N) +class Solution { + fun uniquePathsWithObstacles(grid: Array): Int { + + val m = grid.lastIndex + val n = grid[0].lastIndex + + if (grid[m][n] == 1 || grid[0][0] == 1) return 0 + + val dp = IntArray(n+1) + dp[n] = 1 + + for (i in m downTo 0) { + for (j in n downTo 0) { + if (grid[i][j] == 1) dp[j] = 0 + else if (j < n) dp[j] = dp[j] + dp[j + 1] + //else dp[j] = dp[j] + 0 + } + } + + return dp[0] + } +} + +// In place O(1) +class Solution { + fun uniquePathsWithObstacles(grid: Array): Int { + + val m = grid.lastIndex + val n = grid[0].lastIndex + + if (grid[m][n] == 1 || grid[0][0] == 1) return 0 + + grid[m][n] = 1 + + for (i in m-1 downTo 0) { + grid[i][n] = if(grid[i][n] == 0 && grid[i + 1][n] == 1) 1 else 0 + } + + for (i in n-1 downTo 0) { + grid[m][i] = if(grid[m][i] == 0 && grid[m][i + 1] == 1) 1 else 0 + } + + for (i in m-1 downTo 0) { + for (j in n-1 downTo 0) { + if(grid[i][j] != 1) { + grid[i][j] = grid[i + 1][j] + grid[i][j + 1] + } else { + grid[i][j] = 0 + } + } + } + + return grid[0][0] + } +} diff --git a/kotlin/0064-minimum-path-sum.kt b/kotlin/0064-minimum-path-sum.kt new file mode 100644 index 000000000..c53eadcad --- /dev/null +++ b/kotlin/0064-minimum-path-sum.kt @@ -0,0 +1,22 @@ +class Solution { + fun minPathSum(grid: Array): Int { + val m = grid.lastIndex + val n = grid[0].lastIndex + + for(i in m - 1 downTo 0) { + grid[i][n] += grid[i + 1][n] + } + + for(j in n - 1 downTo 0) { + grid[m][j] += grid[m][j + 1] + } + + for(i in grid.lastIndex - 1 downTo 0) { + for(j in grid[0].lastIndex - 1 downTo 0) { + grid[i][j] += minOf(grid[i + 1][j], grid[i][j + 1]) + } + } + + return grid[0][0] + } +} diff --git a/kotlin/0066-plus-one.kt b/kotlin/0066-plus-one.kt new file mode 100644 index 000000000..894ccb779 --- /dev/null +++ b/kotlin/0066-plus-one.kt @@ -0,0 +1,14 @@ +class Solution { + fun plusOne(digits: IntArray): IntArray { + for (i in digits.size-1 downTo 0) { + digits[i] += 1 + + if (digits[i] <= 9) + return digits + + digits[i] = 0 + } + + return IntArray(digits.size+1).also{ it[0] = 1 } + } +} \ No newline at end of file diff --git a/kotlin/0067-add-binary.kt b/kotlin/0067-add-binary.kt new file mode 100644 index 000000000..896e26514 --- /dev/null +++ b/kotlin/0067-add-binary.kt @@ -0,0 +1,15 @@ +class Solution { + fun addBinary(a: String, b: String): String { + var rsum = ""; var carry = 0; var aL = a.length-1; var bL = b.length-1 + while(aL >= 0 || bL >= 0){ + var aBit = if(aL >= 0) a[aL--] else '0' + var bBit = if(bL >= 0) b[bL--] else '0' + val sum = aBit.toString().toInt() + bBit.toString().toInt() + carry + carry = sum / 2 + rsum = (sum % 2).toString() + rsum + } + if(carry > 0) + rsum = '1' + rsum + return rsum + } +} diff --git a/kotlin/0068-text-justification.kt b/kotlin/0068-text-justification.kt new file mode 100644 index 000000000..9d124a31b --- /dev/null +++ b/kotlin/0068-text-justification.kt @@ -0,0 +1,42 @@ +//opted out of using StringBuilder, instead used Kotlins plus operator to create new Strings +class Solution { + fun fullJustify(words: Array, maxWidth: Int): List { + val res = mutableListOf() + var line = mutableListOf() + var length = 0 + var i = 0 + + while (i < words.size) { + if (length + line.size + words[i].length > maxWidth) { + val extraSpace = maxWidth - length + val spaces = extraSpace / maxOf(1, line.size - 1) + var remainder = extraSpace % maxOf(1, line.size - 1) + + for (j in 0 until maxOf(1, line.lastIndex)) { + line[j] += " ".repeat(spaces) + if (remainder > 0) { + line[j] += " " + remainder-- + } + } + + var whole = "" + for (l in line) whole += l + res.add(whole) + line.clear() + length = 0 + } + + line.add(words[i]) + length += words[i].length + i++ + } + + var lastLine = "" + line.joinToString(" ") + val trailSpace = maxWidth - lastLine.length + lastLine += " ".repeat(trailSpace) + res.add(lastLine) + + return res + } +} diff --git a/kotlin/0069-sqrtx.kt b/kotlin/0069-sqrtx.kt new file mode 100644 index 000000000..44817621d --- /dev/null +++ b/kotlin/0069-sqrtx.kt @@ -0,0 +1,42 @@ +/* +* Binary Search +*/ +class Solution { + fun mySqrt(x: Int): Int { + var left = 0L + var right = x.toLong() + var res = 0L + + while (left <= right) { + val mid = left + (right - left) / 2 + val midSq = mid * mid + + if (midSq > x) { + right = mid - 1 + } else if (midSq < x) { + left = mid + 1 + res = mid + } else { + return mid.toInt() + } + } + + return res.toInt() + } +} + +/* +* Newton's method +*/ +class Solution { + fun mySqrt(x: Int): Int { + if (x == 0) return 0 + + var i = x.toLong() + while (i > x / i) { + i = (i + x / i) / 2 + } + + return i.toInt() + } +} diff --git a/kotlin/0070-climbing-stairs.kt b/kotlin/0070-climbing-stairs.kt new file mode 100644 index 000000000..5d5f8d4b8 --- /dev/null +++ b/kotlin/0070-climbing-stairs.kt @@ -0,0 +1,16 @@ +class Solution { + fun climbStairs(n: Int): Int { + if (n <= 3) { + return n + } + var n1 = 2 + var n2 = 3 + + for (i in 4..n) { + val temp = n1 + n2 + n1 = n2 + n2 = temp + } + return n2 + } +} \ No newline at end of file diff --git a/kotlin/0071-simplify-path.kt b/kotlin/0071-simplify-path.kt new file mode 100644 index 000000000..dbaf84072 --- /dev/null +++ b/kotlin/0071-simplify-path.kt @@ -0,0 +1,21 @@ +class Solution { + fun simplifyPath(path: String): String { + val stack = LinkedList() + val invalids = hashSetOf("", ".", "..") + + path.split("/").forEach{ + if(it == ".." && stack.isNotEmpty()) { + stack.removeLast() + } else if (it !in invalids) { + stack.addLast(it) + } + } + + return if(stack.isEmpty()) "/" else StringBuilder().apply{ + while (stack.isNotEmpty()) { + this.append("/") + this.append(stack.removeFirst()) + } + }.toString() + } +} diff --git a/kotlin/0072-edit-distance.kt b/kotlin/0072-edit-distance.kt new file mode 100644 index 000000000..1ec3382ac --- /dev/null +++ b/kotlin/0072-edit-distance.kt @@ -0,0 +1,95 @@ +/* +* DP Time O(m*n) and space O(m*n) +*/ +class Solution { + fun minDistance(word1: String, word2: String): Int { + + val cache = Array(word1.length + 1) { + IntArray(word2.length + 1){ Integer.MAX_VALUE } + } + + for(j in 0..word2.length) + cache[word1.length][j] = word2.length - j + for(i in 0..word1.length) + cache[i][word2.length] = word1.length - i + + for(i in word1.lastIndex downTo 0) { + for(j in word2.lastIndex downTo 0) { + if(word1[i] == word2[j]) { + cache[i][j] = cache[i + 1][j + 1] + }else { + cache[i][j] = 1 + minOf( + cache[i + 1][j], + cache[i][j + 1], + cache[i + 1][j + 1] + ) + } + } + } + + return cache[0][0] + } +} + +/* +* DP Time O(m*n) and optimized space O(n) +*/ +class Solution { + fun minDistance(word1: String, word2: String): Int { + val m = word1.length + val n = word2.length + var prev = IntArray(n + 1) { it } + + for (i in 1..m) { + val new = IntArray(n + 1) + new[0] = i + for (j in 1..n) { + if (word1[i - 1] == word2[j - 1]) { + new[j] = prev[j - 1] + } else { + new[j] = 1 + minOf( + prev[j], + prev[j - 1], + new[j - 1] + ) + } + } + prev = new + } + + return prev[n] + } +} + + +/* +* DFS/Recursion + memoization Time O(m*n) and space O(n*m) +*/ +class Solution { + fun minDistance(word1: String, word2: String): Int { + + val cache = Array(word1.length + 1) { IntArray(word2.length + 1){ Integer.MAX_VALUE } } + + fun dfs(i: Int, j: Int): Int { + if (i == word1.length && j == word2.length) return 0 + else if (i == word1.length) return word2.length - j + else if (j == word2.length) return word1.length - i + + if (cache[i][j] != Integer.MAX_VALUE) return cache[i][j] + + if (word1[i] == word2[j]) { + cache[i][j] = dfs(i + 1, j + 1) + } else { + cache[i][j] = 1 + minOf( + dfs(i + 1, j), + dfs(i, j + 1), + dfs(i + 1, j + 1) + ) + } + + return cache[i][j] + } + + return dfs(0, 0) + } +} diff --git a/kotlin/0073-set-matrix-zeroes.kt b/kotlin/0073-set-matrix-zeroes.kt new file mode 100644 index 000000000..55c207119 --- /dev/null +++ b/kotlin/0073-set-matrix-zeroes.kt @@ -0,0 +1,36 @@ +class Solution { + fun setZeroes(matrix: Array): Unit { + var m = matrix.size + var n = matrix[0].size + var isCol = false + + for (i in 0..m-1) { + if (matrix[i][0] == 0) + isCol = true + + for (j in 1..n-1) { + if (matrix[i][j] == 0) { + matrix[i][0] = 0 + matrix[0][j] = 0 + } + } + } + + for (i in 1..m-1) { + for (j in 1..n-1) { + if (matrix[0][j] == 0 || matrix[i][0] == 0) + matrix[i][j] = 0 + } + } + + if (matrix[0][0] == 0) { + for (j in 0..n-1) + matrix[0][j] = 0 + } + + if (isCol) { + for (i in 0..m-1) + matrix[i][0] = 0 + } + } +} \ No newline at end of file diff --git a/kotlin/0074-search-a-2d-matrix.kt b/kotlin/0074-search-a-2d-matrix.kt new file mode 100644 index 000000000..0cd10c91c --- /dev/null +++ b/kotlin/0074-search-a-2d-matrix.kt @@ -0,0 +1,81 @@ +// binary search on rows to find row, then binary search on actual row O(log(m*n)) +class Solution { + // TC: O(log m + log n) + fun searchMatrix(matrix: Array, target: Int): Boolean { + var row = matrix.size + var col = matrix.first().size + var top = 0 + var bot = row - 1 + + while ( top <= bot ){ + row = (top + bot ) / 2 + if(target > matrix[row][col - 1]){ + top = row + 1 + } else if(target < matrix[row][0]) { + bot = row - 1 + } else { + break + } + } + + if((top > bot)) return false + + row = (top + bot) / 2 + var l = 0 + var r = col - 1 + while(l <= r){ + var m = (l + r) / 2 + if(target > matrix[row][m]){ + l = m + 1 + } else if(target < matrix[row][m]){ + r = m - 1 + } else { + return true + } + } + + return false + } +} + +//binary search on whole matrix (search the matrix as an sorted array) O(log(m*n)) +class Solution { + fun searchMatrix(mt: Array, t: Int): Boolean { + val rows = mt.size + val cols = mt[0].size + + var l = 0 + var r = rows * cols - 1 + while (l != r) { + val m = (l + r) / 2 + if (mt[m / cols][m % cols] < t) + l = m + 1 + else + r = m + } + + return mt[r / cols][r % cols] == t + } +} + +//treat the matrix as an BST, root at mt[0][-1] O(m + n) +class Solution { + fun searchMatrix(mt: Array, t: Int): Boolean { + val rows = mt.size + val cols = mt[0].size + + var row = 0 + var col = cols - 1 + while (row < rows && col >= 0) { + val cur = mt[row][col] + if (cur > t) + col-- + else if (cur < t) + row++ + else + return true + } + + return false + } +} diff --git a/kotlin/0075-sort-colors.kt b/kotlin/0075-sort-colors.kt new file mode 100644 index 000000000..cbfff1455 --- /dev/null +++ b/kotlin/0075-sort-colors.kt @@ -0,0 +1,31 @@ +class Solution { + fun sortColors(nums: IntArray): Unit { + + var low = 0 + var high = nums.size-1 + var pointer = 0 + + while(pointer <= high){ + when(nums[pointer]){ + 0 -> { + swap(low,pointer,nums) + low++ + pointer++ + } + 1 -> { + pointer++ + } + 2 -> { + swap(high,pointer,nums) + high-- + } + } + } + } + + private fun swap(i: Int, j: Int, nums: IntArray){ + val temp = nums[i] + nums[i] = nums[j] + nums[j] = temp + } +} \ No newline at end of file diff --git a/kotlin/0076-minimum-window-substring.kt b/kotlin/0076-minimum-window-substring.kt new file mode 100644 index 000000000..972b6d6c7 --- /dev/null +++ b/kotlin/0076-minimum-window-substring.kt @@ -0,0 +1,33 @@ +class Solution { + fun minWindow(s: String, t: String): String { + val lenS = s.length + val lenT = t.length + + val freqs = IntArray(128){ 0 } + for(ch in t){ + ++freqs[ch.toInt()] + } + + var shortest = "" + var count = lenT + var lo = 0 + var hi = 0 + + while(hi < lenS){ + if(freqs[s[hi].toInt()]-- > 0) --count + + while(count == 0){ + if(shortest.isEmpty() || hi - lo + 1 < shortest.length){ + shortest = s.substring(lo, hi + 1) + } + + if(++freqs[s[lo].toInt()] > 0) ++count + ++lo + } + + ++hi + } + + return shortest + } +} \ No newline at end of file diff --git a/kotlin/0077-combinations.kt b/kotlin/0077-combinations.kt new file mode 100644 index 000000000..a2d4ea599 --- /dev/null +++ b/kotlin/0077-combinations.kt @@ -0,0 +1,22 @@ +class Solution { + fun combine(n: Int, k: Int): List> { + + val res = ArrayList>() + + fun backtrack(start: Int, comb: ArrayList) { + if(comb.size == k) { + res.add(ArrayList(comb)) + return + } + + for(i in start..n) { + comb.add(i) + backtrack(i + 1, comb) + comb.removeAt(comb.size-1) + } + } + + backtrack(1, ArrayList()) + return res + } +} diff --git a/kotlin/0078-subsets.kt b/kotlin/0078-subsets.kt new file mode 100644 index 000000000..9885ad47c --- /dev/null +++ b/kotlin/0078-subsets.kt @@ -0,0 +1,17 @@ +class Solution { + fun subsets(nums: IntArray): List> { + val res = mutableListOf>() + subsets(nums, 0, mutableListOf(), res) + return res + } + + fun subsets(nums: IntArray, idx: Int, list: MutableList, res: MutableList>) { + res.add(ArrayList(list)) + + for (i in idx..nums.size-1) { + list.add(nums[i]) + subsets(nums, i+1, list, res) + list.removeAt(list.size-1) + } + } +} \ No newline at end of file diff --git a/kotlin/0079-word-search.kt b/kotlin/0079-word-search.kt new file mode 100644 index 000000000..f5f6c5994 --- /dev/null +++ b/kotlin/0079-word-search.kt @@ -0,0 +1,50 @@ +class Solution { +var ROWS: Int = 0 +var COLS: Int = 0 + + + fun exist(board: Array, word: String): Boolean { + + ROWS = board.count() + COLS = board[0].count() + + for (row in 0 until ROWS) { + for (col in 0 until COLS) { + if (dfs(board, row, col, word, 0)) + return true + } + } + + + return false + } + + fun dfs(board: Array, row: Int, col: Int, word: String, indx: Int): Boolean { + /* Step 1). check the bottom case. */ + if (indx == word.count()) + return true + /* Step 2). Check the boundaries. */ + if (row < 0 || row == ROWS || col < 0 || col == COLS || board[row][col] != word[indx]) + return false + + // mark the path before the next exploration + board[row][col] = '#' + var found = false + val rowOffsets = intArrayOf(0, 1, 0, -1) + val colOffsets = intArrayOf(1, 0, -1, 0) + + for (i in 0..3) { + found = dfs(board, row + rowOffsets[i], col + colOffsets[i], word, indx + 1) + if (found) + break + + } + /* Step 4). clean up and return the result. */ + board[row][col] = word[indx] + + return found + } + + + +} diff --git a/kotlin/0080-remove-duplicates-from-sorted-array-ii.kt b/kotlin/0080-remove-duplicates-from-sorted-array-ii.kt new file mode 100644 index 000000000..db2ba1d1c --- /dev/null +++ b/kotlin/0080-remove-duplicates-from-sorted-array-ii.kt @@ -0,0 +1,22 @@ +class Solution { + fun removeDuplicates(nums: IntArray): Int { + + var left = 0 + var right = 0 + + while (right < nums.size) { + var count = 1 + while(right + 1 < nums.size && nums[right] == nums[right + 1]) { + count++ + right++ + } + for(i in 0 until minOf(2, count)) { + nums[left] = nums[right] + left++ + } + right++ + } + + return left + } +} diff --git a/kotlin/0081-search-in-rotated-sorted-array-ii.kt b/kotlin/0081-search-in-rotated-sorted-array-ii.kt new file mode 100644 index 000000000..0fc636fc8 --- /dev/null +++ b/kotlin/0081-search-in-rotated-sorted-array-ii.kt @@ -0,0 +1,26 @@ +class Solution { + fun search(nums: IntArray, target: Int): Boolean { + var l = 0 + var r = nums.lastIndex + while (l <= r) { + val m = l + (r - l) / 2 + if (nums[m] == target) + return true + if (nums[l] < nums[m]) { + if (nums[l] <= target && nums[m] > target) + r = m - 1 + else + l = m + 1 + } else if (nums[l] > nums[m]) { + if (nums[r] >= target && nums[m] < target) + l = m + 1 + else + r = m - 1 + } else { + l++ + } + } + + return false + } +} diff --git a/kotlin/0083-remove-duplicates-from-sorted-list.kt b/kotlin/0083-remove-duplicates-from-sorted-list.kt new file mode 100644 index 000000000..df840f27d --- /dev/null +++ b/kotlin/0083-remove-duplicates-from-sorted-list.kt @@ -0,0 +1,26 @@ +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun deleteDuplicates(head: ListNode?): ListNode? { + var dummy = ListNode(Integer.MAX_VALUE, head) + var prev = dummy + var current = head + + while(current != null) { + if(current.`val` == prev.`val`) + prev.next = current.next + else + prev = current + current = current.next + } + + return dummy.next + } +} diff --git a/kotlin/0084-largest-rectangle-in-histogram.kt b/kotlin/0084-largest-rectangle-in-histogram.kt new file mode 100644 index 000000000..6ec5d018c --- /dev/null +++ b/kotlin/0084-largest-rectangle-in-histogram.kt @@ -0,0 +1,27 @@ +class Solution { + fun largestRectangleArea(heights: IntArray): Int { + val st = LinkedList() + st.push(intArrayOf(-1, 0)) + var max = 0 + + for (i in heights.indices) { + var start = i + + while (!st.isEmpty() && st.peek()[1] > heights[i]) { + val curr = st.pop() + val height = curr[1] + start = curr[0] + max = Math.max(max, height * (i - start)) + } + + st.push(intArrayOf(start, heights[i])) + } + + while (!st.isEmpty()) { + val curr = st.pop() + max = Math.max(max, curr[1] * (heights.size - curr[0])) + } + + return max + } +} \ No newline at end of file diff --git a/kotlin/0086-partition-list.kt b/kotlin/0086-partition-list.kt new file mode 100644 index 000000000..0a3c29608 --- /dev/null +++ b/kotlin/0086-partition-list.kt @@ -0,0 +1,25 @@ +class Solution { + fun partition(head: ListNode?, x: Int): ListNode? { + var leftPart = ListNode(0) + val rightPart = ListNode(0) + + var cur = head + var left = leftPart + var right = rightPart + + while (cur != null) { + if (cur.`val` < x) { + left?.next = cur + left = left?.next + } else { + right?.next = cur + right = right?.next + } + cur = cur?.next + } + + right?.next = null + left?.next = rightPart?.next + return leftPart?.next + } +} diff --git a/kotlin/0088-merge-sorted-array.kt b/kotlin/0088-merge-sorted-array.kt new file mode 100644 index 000000000..5f348677e --- /dev/null +++ b/kotlin/0088-merge-sorted-array.kt @@ -0,0 +1,24 @@ +class Solution { + fun merge(n1: IntArray, m: Int, n2: IntArray, n: Int): Unit { + var end = n1.lastIndex + var i = m - 1 + var j = n - 1 + + while (i >= 0 && j >= 0) { + if (n1[i] > n2[j]) { + n1[end] = n1[i] + i-- + } else { + n1[end] = n2[j] + j-- + } + end-- + } + + while (j >= 0) { + n1[end] = n2[j] + j-- + end-- + } + } +} diff --git a/kotlin/0090-subsets-ii.kt b/kotlin/0090-subsets-ii.kt new file mode 100644 index 000000000..f5234ce18 --- /dev/null +++ b/kotlin/0090-subsets-ii.kt @@ -0,0 +1,27 @@ +class Solution { + fun subsetsWithDup(nums: IntArray): List> { + nums.sort() + val resultantList = mutableListOf>() + val intermediaryList = mutableListOf() + + fun dfs(decisionIndex: Int = 0) { + if (decisionIndex > nums.lastIndex) { + resultantList.add(intermediaryList.toList()) + return + } + + // decision to include nums[decisionIndex] + intermediaryList.add(nums[decisionIndex]) + dfs(decisionIndex + 1) + + // decision to not include nums[decisionIndex] + intermediaryList.removeAt(intermediaryList.lastIndex) + // skipping duplicates if any + var i = decisionIndex + while ((i in nums.indices && i + 1 in nums.indices) && nums[i] == nums[i + 1]) i++ + dfs(i + 1) + } + dfs() + return resultantList + } +} \ No newline at end of file diff --git a/kotlin/0091-decode-ways.kt b/kotlin/0091-decode-ways.kt new file mode 100644 index 000000000..96d315635 --- /dev/null +++ b/kotlin/0091-decode-ways.kt @@ -0,0 +1,22 @@ +class Solution { + // Time : O(n) | Space : O(n) + fun numDecodings(string: String): Int { + if (string.length == 1) return if (string.last() != '0') 1 else 0 + // dp array + val numberOfDecodings = IntArray(string.length + 1).apply { + this[lastIndex - 1] = if (string.last() == '0') 0 else 1 + this[lastIndex] = if ("${string[string.lastIndex - 1]}${string[string.lastIndex]}".toInt() <= 26) 1 else 0 + } + for (i in (string.lastIndex - 1) downTo 0) { + if (string[i] == '0') { + numberOfDecodings[i] = 0 + continue + } + numberOfDecodings[i] += numberOfDecodings[i + 1] + numberOfDecodings[i] += numberOfDecodings[i + 2].takeIf { + (string[i] == '1' && string[i + 1] in '0'..'9') || (string[i] == '2' && string[i + 1] in '0'..'6') + } ?: 0 + } + return numberOfDecodings[0] + } +} \ No newline at end of file diff --git a/kotlin/0092-reverse-linked-list-ii.kt b/kotlin/0092-reverse-linked-list-ii.kt new file mode 100644 index 000000000..37e059231 --- /dev/null +++ b/kotlin/0092-reverse-linked-list-ii.kt @@ -0,0 +1,24 @@ +class Solution { + fun reverseBetween(head: ListNode?, left: Int, right: Int): ListNode? { + val dummy: ListNode? = ListNode(0) + dummy?.next = head + + var cur = dummy + repeat (left - 1) { + cur = cur?.next + } + + var pre = cur + var start = cur?.next + var end = start?.next + + repeat (right - left) { + start?.next = end?.next + end?.next = pre?.next + pre?.next = end + end = start?.next + } + + return dummy?.next + } +} diff --git a/kotlin/0093-restore-ip-addresses.kt b/kotlin/0093-restore-ip-addresses.kt new file mode 100644 index 000000000..ae366e712 --- /dev/null +++ b/kotlin/0093-restore-ip-addresses.kt @@ -0,0 +1,22 @@ +class Solution { + fun restoreIpAddresses(s: String): List { + val res = mutableListOf() + + fun backTrack(i: Int, dots: Int, cur: String) { + if (dots == 4 && i == s.length) { + res.add(cur.substring(0, cur.lastIndex)) + return + } + if (dots > 4) return + + for (j in i until minOf(i + 3, s.length)) { + val digits = s.substring(i, j + 1) + if (digits.toInt() <= 255 && (i == j || s[i] != '0')) + backTrack(j + 1, dots + 1, cur + digits + ".") + } + } + + backTrack(0, 0, "") + return res + } +} diff --git a/kotlin/0094-binary-tree-inorder-traversal.kt b/kotlin/0094-binary-tree-inorder-traversal.kt new file mode 100644 index 000000000..0c47a5949 --- /dev/null +++ b/kotlin/0094-binary-tree-inorder-traversal.kt @@ -0,0 +1,37 @@ +//iterative version +class Solution { + fun inorderTraversal(root: TreeNode?): List { + val res = ArrayList() + val stack = Stack() + + var node = root + while(node != null || !stack.isEmpty()){ + while(node != null){ + stack.push(node) + node = node.left + } + node = stack.pop() + res.add(node.`val`) + node = node.right + } + + return res + } +} + +//recursion version +class Solution { + fun inorderTraversal(root: TreeNode?): List { + val res = ArrayList() + + fun inOrder(node: TreeNode?) { + node?: return + inOrder(node.left) + res.add(node.`val`) + inOrder(node.right) + } + + inOrder(root) + return res + } +} diff --git a/kotlin/0095-unique-binary-search-trees-ii.kt b/kotlin/0095-unique-binary-search-trees-ii.kt new file mode 100644 index 000000000..e217082a0 --- /dev/null +++ b/kotlin/0095-unique-binary-search-trees-ii.kt @@ -0,0 +1,28 @@ +class Solution { + fun generateTrees(n: Int): List { + val dp = HashMap, List>() + + fun generate(l: Int, r: Int): List { + if (l == r) return arrayListOf(TreeNode(l)) + if (l > r) return arrayListOf(null) + if (dp[l to r] != null) return dp[l to r]!! + + val res = ArrayList() + for (i in l..r) { + val left = generate(l, i - 1) + val right = generate(i + 1, r) + for (nodeLeft in left) { + for (nodeRight in right) { + val root = TreeNode(i, nodeLeft, nodeRight) + res.add(root) + } + } + } + + dp[l to r] = res + return res + } + + return generate(1, n) + } +} diff --git a/kotlin/0096-unique-binary-search-trees.kt b/kotlin/0096-unique-binary-search-trees.kt new file mode 100644 index 000000000..d325b7186 --- /dev/null +++ b/kotlin/0096-unique-binary-search-trees.kt @@ -0,0 +1,41 @@ +//"pure" dp +class Solution { + fun numTrees(n: Int): Int { + val cache = IntArray (n + 1) { 1 } + + for (node in 2..n) { + var res = 0 + for (root in 1..node) { + val left = root - 1 + val right = node - root + res += cache[left] * cache[right] + } + cache[node] = res + } + + return cache[n] + } +} + +//recursion + memoization +class Solution { + fun numTrees(n: Int): Int { + val cache = IntArray (n + 1) { -1 } + + fun count(root: Int): Int { + if (root == 0) return 1 + if (cache[root] != -1) return cache[root] + + var res = 0 + for (left in 0 until root) { + val right = root - left - 1 + res += count(left) * count(right) + } + + cache[root] = res + return res + } + + return count(n) + } +} diff --git a/kotlin/0097-interleaving-string.kt b/kotlin/0097-interleaving-string.kt new file mode 100644 index 000000000..5616f7b58 --- /dev/null +++ b/kotlin/0097-interleaving-string.kt @@ -0,0 +1,56 @@ +//DP +class Solution { + fun isInterleave(s1: String, s2: String, s3: String): Boolean { + + val dp = Array(s1.length + 1) { BooleanArray(s2.length + 1) } + val n = s1.length + val m = s2.length + + if(n + m != s3.length) return false + + dp[n][m] = true + + for (i in n downTo 0) { + for (j in m downTo 0) { + if (i < n && s1[i] == s3[i+j] && dp[i + 1][j]) + dp[i][j] = true + if (j < m && s2[j] == s3[i+j] && dp[i][j + 1]) + dp[i][j] = true + } + } + + return dp[0][0] + } +} + +//DFS with memoization +class Solution { + fun isInterleave(s1: String, s2: String, s3: String): Boolean { + + if(s1.length + s2.length != s3.length) return false + + val memo = Array(s1.length + 1) { BooleanArray(s2.length + 1) } + val n = s1.length + val m = s2.length + + fun dfs(i: Int, j: Int): Boolean { + + if (i == n && j == m) return true + if (memo[i][j] == true) return false + + if (i < n && s1[i] == s3[i+j]) { + if (dfs(i + 1, j)) + return true + } + if (j < m && s2[j] == s3[i+j]) { + if (dfs(i, j + 1)) + return true + } + + memo[i][j] = true + return false + } + + return dfs(0,0) + } +} diff --git a/kotlin/0098-validate-binary-search-tree.kt b/kotlin/0098-validate-binary-search-tree.kt new file mode 100644 index 000000000..7ac614966 --- /dev/null +++ b/kotlin/0098-validate-binary-search-tree.kt @@ -0,0 +1,25 @@ +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun isValidBST(root: TreeNode?): Boolean { + return isValidBST(root, null, null) + } + + fun isValidBST(node: TreeNode?, left: Int?, right : Int?): Boolean { + if (node == null) + return true + + if (left != null && node.`val` <= left || right != null && node.`val` >= right) + return false + + return isValidBST(node.left, left, node.`val`) && isValidBST(node.right, node.`val`, right) + } +} \ No newline at end of file diff --git a/kotlin/0100-same-tree.kt b/kotlin/0100-same-tree.kt new file mode 100644 index 000000000..ef26e66dc --- /dev/null +++ b/kotlin/0100-same-tree.kt @@ -0,0 +1,21 @@ +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun isSameTree(p: TreeNode?, q: TreeNode?): Boolean { + if (p == null && q == null) + return true + + if (p == null || q == null || p.`val` != q.`val`) + return false + + return isSameTree(p.left, q.left) && isSameTree(p.right, q.right) + } +} \ No newline at end of file diff --git a/kotlin/0101-symmetric-tree.kt b/kotlin/0101-symmetric-tree.kt new file mode 100644 index 000000000..ab9414448 --- /dev/null +++ b/kotlin/0101-symmetric-tree.kt @@ -0,0 +1,53 @@ +/* +* Recursive solution +*/ +class Solution { + fun isSymmetric(root: TreeNode?): Boolean { + + fun dfs(left: TreeNode?, right: TreeNode?): Boolean { + if(left == null && right == null) + return true + if(left == null || right == null) + return false + + return left.value == right.value && + dfs(left.left, right.right) && + dfs(left.right, right.left) + } + + return dfs(root!!.left, root!!.right) + } + + val TreeNode.value get()= this.`val` +} + +/* +* Iterative solution +*/ +class Solution { + fun isSymmetric(root: TreeNode?): Boolean { + val q = LinkedList() + q.addFirst(root!!.left) + q.addFirst(root!!.right) + + while(q.isNotEmpty()) { + val left = q.removeFirst() + val right = q.removeFirst() + + if(left == null && right == null) + continue + + if(left == null || right == null || left.value != right.value) + return false + + q.addFirst(left.left) + q.addFirst(right.right) + q.addFirst(left.right) + q.addFirst(right.left) + } + + return true + } + + val TreeNode.value get()= this.`val` +} diff --git a/kotlin/0102-binary-tree-level-order-traversal.kt b/kotlin/0102-binary-tree-level-order-traversal.kt new file mode 100644 index 000000000..8eed855cd --- /dev/null +++ b/kotlin/0102-binary-tree-level-order-traversal.kt @@ -0,0 +1,41 @@ +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun levelOrder(root: TreeNode?): List> { + val res = mutableListOf>() + + if (root == null) + return res + + val queue = LinkedList() + queue.add(root) + + while (!queue.isEmpty()) { + val size = queue.size + val level = mutableListOf() + + repeat(size) { + val node = queue.poll() + level.add(node.`val`) + + if (node.left != null) + queue.add(node.left) + + if (node.right != null) + queue.add(node.right) + } + + res.add(level) + } + + return res + } +} \ No newline at end of file diff --git a/kotlin/0103-binary-tree-zigzag-level-order-traversal.kt b/kotlin/0103-binary-tree-zigzag-level-order-traversal.kt new file mode 100644 index 000000000..69cb431c6 --- /dev/null +++ b/kotlin/0103-binary-tree-zigzag-level-order-traversal.kt @@ -0,0 +1,41 @@ +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun zigzagLevelOrder(root: TreeNode?): List> { + + val res = ArrayList>() + root?: return res + + val q = ArrayDeque() + var left = true + + q.add(root) + + while(q.isNotEmpty()) { + val s = q.size + val temp = ArrayList() + repeat (s) { + val x = q.poll()!! + if(x.left != null) q.add(x.left) + if(x.right != null) q.add(x.right) + if(left) temp.add(x.value) + else temp.add(0, x.value) + } + res.add(temp) + left = !left + } + + return res + } + + val TreeNode.value + get() = this.`val` +} diff --git a/kotlin/0104-maximum-depth-of-binary-tree.kt b/kotlin/0104-maximum-depth-of-binary-tree.kt new file mode 100644 index 000000000..5aa416d9c --- /dev/null +++ b/kotlin/0104-maximum-depth-of-binary-tree.kt @@ -0,0 +1,39 @@ +import java.util.* + +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun maxDepth(root: TreeNode?): Int { + if (root == null) + return 0 + + val left = maxDepth(root.left) + val right = maxDepth(root.right) + + return 1 + Math.max(left, right) + } + + fun iterativeMaxDepth(root: TreeNode?): Int { + if (root == null) return 0 + val callStack = Stack>().apply { add(Pair(root, 1)) } + var currentMaxDepth = 1 + var temp: Pair + while (callStack.isNotEmpty()) { + temp = callStack.pop() + currentMaxDepth = maxOf(currentMaxDepth, temp.second) + with(temp.first) { + left?.let { callStack.add(Pair(it, currentMaxDepth + 1)) } + right?.let { callStack.add(Pair(it, currentMaxDepth + 1)) } + } + } + return currentMaxDepth + } +} \ No newline at end of file diff --git a/kotlin/0105-construct-binary-tree-from-preorder-and-inorder-traversal.kt b/kotlin/0105-construct-binary-tree-from-preorder-and-inorder-traversal.kt new file mode 100644 index 000000000..1c53f3d2b --- /dev/null +++ b/kotlin/0105-construct-binary-tree-from-preorder-and-inorder-traversal.kt @@ -0,0 +1,35 @@ +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun buildTree(preorder: IntArray, inorder: IntArray): TreeNode? { + val prStart = 0; val inStart = 0 + val prEnd = preorder.size-1; val inEnd = inorder.size-1 + + return helper(preorder, prStart, prEnd, inorder, inStart, inEnd); + } + + fun helper(preorder: IntArray, prStart: Int, prEnd: Int, inorder: IntArray, inStart: Int, inEnd: Int): TreeNode? { + if (prStart > prEnd || inStart > inEnd) + return null + + val root = TreeNode(preorder[prStart]) + var i = 0 + + while (i < inorder.size && inorder[i] != preorder[prStart]) { + i++ + } + + root.left = helper(preorder, prStart+1, prStart-inStart+i, inorder, inStart, i-1) + root.right = helper(preorder, prStart-inStart+i+1, prEnd, inorder, i+1, inEnd) + + return root + } +} \ No newline at end of file diff --git a/kotlin/0106-construct-binary-tree-from-inorder-and-postorder-traversal.kt b/kotlin/0106-construct-binary-tree-from-inorder-and-postorder-traversal.kt new file mode 100644 index 000000000..cefdccd40 --- /dev/null +++ b/kotlin/0106-construct-binary-tree-from-inorder-and-postorder-traversal.kt @@ -0,0 +1,37 @@ +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun buildTree(inorder: IntArray, postorder: IntArray): TreeNode? { + val inOrdIdx = HashMap().apply{ + for((i,v) in inorder.withIndex()) + this[v] = i + } + + var pstOrdIdx = postorder.lastIndex + + fun helper(left: Int, right: Int): TreeNode? { + if(left > right) + return null + + val root = TreeNode(postorder[pstOrdIdx]) + pstOrdIdx-- + + val index = inOrdIdx[root.value]!! + root.right = helper(index + 1, right) + root.left = helper(left, index - 1) + return root + } + + return helper(0, postorder.lastIndex) + } + + val TreeNode.value get()= this.`val` +} diff --git a/kotlin/0108-convert-sorted-array-to-binary-search-tree.kt b/kotlin/0108-convert-sorted-array-to-binary-search-tree.kt new file mode 100644 index 000000000..50c102f5d --- /dev/null +++ b/kotlin/0108-convert-sorted-array-to-binary-search-tree.kt @@ -0,0 +1,29 @@ +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun sortedArrayToBST(nums: IntArray): TreeNode? { + + fun createTree(left: Int, right: Int): TreeNode? { + + if(left > right) return null + else if(left == right) return TreeNode(nums[left]) + + val mid = (left + right) / 2 + val node = TreeNode(nums[mid]) + node.left = createTree(left, mid-1) + node.right = createTree(mid+1, right) + + return node + } + + return createTree(0, nums.lastIndex) + } +} diff --git a/kotlin/0110-balanced-binary-tree.kt b/kotlin/0110-balanced-binary-tree.kt new file mode 100644 index 000000000..3e0b395e9 --- /dev/null +++ b/kotlin/0110-balanced-binary-tree.kt @@ -0,0 +1,31 @@ +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun isBalanced(root: TreeNode?): Boolean { + return isBalancedUtil(root) != Int.MAX_VALUE + } + + fun isBalancedUtil(node: TreeNode?): Int { + if (node == null) + return 0 + + val left = isBalancedUtil(node.left) + val right = isBalancedUtil(node.right) + + if (left == Int.MAX_VALUE || right == Int.MAX_VALUE) + return Int.MAX_VALUE + + if (Math.abs(left - right) > 1) + return Int.MAX_VALUE + + return Math.max(left, right) + 1 + } +} \ No newline at end of file diff --git a/kotlin/0112-path-sum.kt b/kotlin/0112-path-sum.kt new file mode 100644 index 000000000..14e920db6 --- /dev/null +++ b/kotlin/0112-path-sum.kt @@ -0,0 +1,21 @@ +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun hasPathSum(root: TreeNode?, targetSum: Int): Boolean { + + root?: return false + + if(root.left == null && root.right == null) return targetSum - root.value == 0 + else return hasPathSum(root.left, targetSum - root.value) || hasPathSum(root.right, targetSum - root.value) + } + + val TreeNode.value get() = this.`val` +} diff --git a/kotlin/0115-distinct-subsequences.kt b/kotlin/0115-distinct-subsequences.kt new file mode 100644 index 000000000..9506e1448 --- /dev/null +++ b/kotlin/0115-distinct-subsequences.kt @@ -0,0 +1,46 @@ +/* +* DP bottom up +*/ +class Solution { + fun numDistinct(s: String, t: String): Int { + val dp = Array(s.length + 1) { IntArray(t.length + 1) } + + for (i in 0..s.length) + dp[i][t.length] = 1 + + for (i in s.length - 1 downTo 0) { + for (j in t.length - 1 downTo 0) { + if (s[i] == t[j]) + dp[i][j] += (dp[i + 1][j + 1] + dp[i + 1][j]) + else + dp[i][j] += dp[i + 1][j] + } + } + + return dp[0][0] + } +} + +/* +* DFS + Memoization +*/ +class Solution { + fun numDistinct(s: String, t: String): Int { + val memo = Array(s.length) { IntArray(t.length) { -1 } } + + fun dfs(i: Int, j: Int): Int { + if (j == t.length) return 1 + if (i == s.length) return 0 + if (memo[i][j] != -1) return memo[i][j] + + if (s[i] == t[j]) + memo[i][j] = dfs(i + 1, j + 1) + dfs(i + 1, j) + else + memo[i][j] = dfs(i + 1, j) + + return memo[i][j] + } + + return dfs(0, 0) + } +} diff --git a/kotlin/0116-populating-next-right-pointers-in-each-node.kt b/kotlin/0116-populating-next-right-pointers-in-each-node.kt new file mode 100644 index 000000000..69760a1cc --- /dev/null +++ b/kotlin/0116-populating-next-right-pointers-in-each-node.kt @@ -0,0 +1,62 @@ +// Time complexity O(n) and space complexity O(1) with Follow-up constraints, without using recursion +class Solution { + fun connect(root: Node?): Node? { + var cur = root + var next = cur?.left + + while (cur != null && next != null) { + cur?.left?.next = cur?.right + if (cur?.next != null) + cur?.right?.next = cur?.next?.left + + cur = cur?.next + if (cur == null) { + cur = next + next = cur?.left + } + } + + return root + } +} + +// Time complexity O(n) and space complexity O(1) with Follow-up constraints using recursion +class Solution { + fun connect(root: Node?): Node? { + root?: return null + + root.left?.let { + it.next = root.right + root.right?.let { + it.next = root.next?.left + } + connect(root.left) + connect(root.right) + } + + return root + } +} + +// Time complexity O(n) and space complexity O(logn) +class Solution { + fun connect(root: Node?): Node? { + with (LinkedList>()) { + addLast(root to 0) + + while (isNotEmpty()) { + repeat (size) { + val (curNode, curLevel) = removeFirst() + peekFirst()?.let { (nextNode, nextLevel) -> + if (nextLevel == curLevel) + curNode?.next = nextNode + } + curNode?.left?.let { addLast(it to (curLevel + 1)) } + curNode?.right?.let { addLast(it to (curLevel + 1)) } + } + } + } + + return root + } +} diff --git a/kotlin/0118-pascals-triangle.kt b/kotlin/0118-pascals-triangle.kt new file mode 100644 index 000000000..e7a311294 --- /dev/null +++ b/kotlin/0118-pascals-triangle.kt @@ -0,0 +1,26 @@ +/* +* Solution as per channel +*/ +class Solution { + fun generate(numRows: Int): List> { + val res = ArrayList>() + val temp = ArrayList() + for(i in 0 until numRows){ + temp.add(0,1) + for(j in 1 until temp.size-1) + temp.set(j, temp.get(j)+temp.get(j+1)) + res.add(ArrayList(temp)) + } + return res + } +} + +/* +* Cool "idiomatic" Kotlin solution, making use of Kotlins functions +*/ +class Solution { + fun generate(numRows: Int) = mutableListOf>(listOf(1)).apply { + for(row in 1 until numRows) + add(listOf(1) + this[row-1].windowed(2).map { it.sum()} + listOf(1)) + } +} diff --git a/kotlin/0119-pascals-triangle-ii.kt b/kotlin/0119-pascals-triangle-ii.kt new file mode 100644 index 000000000..0c72d7f96 --- /dev/null +++ b/kotlin/0119-pascals-triangle-ii.kt @@ -0,0 +1,16 @@ +class Solution { + fun getRow(rowIndex: Int): List { + var res = mutableListOf(1) + + for (i in 0 until rowIndex) { + val nextRow = MutableList (res.size + 1) { 0 } + for (j in 0 until res.size) { + nextRow[j] += res[j] + nextRow[j + 1] += res[j] + } + res = nextRow + } + + return res + } +} diff --git a/kotlin/0120-triangle.kt b/kotlin/0120-triangle.kt new file mode 100644 index 000000000..649696439 --- /dev/null +++ b/kotlin/0120-triangle.kt @@ -0,0 +1,12 @@ +class Solution { + fun minimumTotal(triangle: List>): Int { + val dp = IntArray(triangle.last().size + 1) + + for (row in triangle.reversed()) { + for ((i,n) in row.withIndex()) + dp[i] = minOf(dp[i], dp[i + 1]) + n + } + + return dp[0] + } +} diff --git a/kotlin/0121-best-time-to-buy-and-sell-stock.kt b/kotlin/0121-best-time-to-buy-and-sell-stock.kt new file mode 100644 index 000000000..fcdfb05cb --- /dev/null +++ b/kotlin/0121-best-time-to-buy-and-sell-stock.kt @@ -0,0 +1,11 @@ +class Solution { + fun maxProfit(prices: IntArray): Int { + var buy = prices[0] + var maxDiff = 0 + for (i in 1 until prices.size) { + buy = buy.coerceAtMost(prices[i]) + maxDiff = maxDiff.coerceAtLeast(prices[i] - buy) + } + return maxDiff + } +} diff --git a/kotlin/0122-best-time-to-buy-and-sell-stock-ii.kt b/kotlin/0122-best-time-to-buy-and-sell-stock-ii.kt new file mode 100644 index 000000000..c1469a2b1 --- /dev/null +++ b/kotlin/0122-best-time-to-buy-and-sell-stock-ii.kt @@ -0,0 +1,9 @@ +class Solution { + fun maxProfit(prices: IntArray): Int { + var profit = 0 + for(i in 0 until prices.size-1){ + if(prices[i+1] > prices[i]) profit += (prices[i+1] - prices[i]) + } + return profit + } +} diff --git a/kotlin/0124-binary-tree-maximum-path-sum.kt b/kotlin/0124-binary-tree-maximum-path-sum.kt new file mode 100644 index 000000000..49fb0a6a3 --- /dev/null +++ b/kotlin/0124-binary-tree-maximum-path-sum.kt @@ -0,0 +1,28 @@ +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun maxPathSum(root: TreeNode?): Int { + val res = IntArray(1) {Int.MIN_VALUE} + maxPathSum(root, res) + return res[0] + } + + fun maxPathSum(root: TreeNode?, res: IntArray): Int { + if (root == null) + return 0 + + val left = Math.max(0, maxPathSum(root.left, res)) + val right = Math.max(0, maxPathSum(root.right, res)) + res[0] = Math.max(res[0], root.`val` + left + right) + + return root.`val` + Math.max(left, right) + } +} \ No newline at end of file diff --git a/kotlin/0125-valid-palindrome.kt b/kotlin/0125-valid-palindrome.kt new file mode 100644 index 000000000..5d9dfc402 --- /dev/null +++ b/kotlin/0125-valid-palindrome.kt @@ -0,0 +1,15 @@ +class Solution { + fun isPalindrome(s: String): Boolean { + val s = s.toLowerCase() + var i = 0 + var j = s.length - 1 + while (i < j) { + while (!s[i].isLetterOrDigit() && i < j) + i++; + while (!s[j].isLetterOrDigit() && j > i) + j--; + if (s[i++] != s[j--]) return false + } + return true + } +} diff --git a/kotlin/0127-word-ladder.kt b/kotlin/0127-word-ladder.kt new file mode 100644 index 000000000..885e9ca94 --- /dev/null +++ b/kotlin/0127-word-ladder.kt @@ -0,0 +1,52 @@ +class Solution { + fun ladderLength(beginWord: String, endWord: String, wordList: List): Int { + val set = wordList.toMutableSet() + if (!set.contains(endWord)) + return 0 + + val queue = LinkedList() + queue.add(beginWord) + set.remove(beginWord) + var step = 0 + + while(queue.size != 0) { + val size = queue.size + step++; + + for (i in 0..size-1) { + val curr = queue.poll() + + if (endWord.equals(curr)) + return step + + val neighbors = getNeighbors(curr, set) + for (neig in neighbors) { + queue.add(neig) + set.remove(neig) + } + } + } + + return 0 + } + + fun getNeighbors(str: String, set: Set): List { + val res = ArrayList() + val chars = str.toCharArray() + + for (i in 0..chars.size-1) { + val temp = chars[i]; + + for (j in 'a'..'z') { + chars[i] = j + val newStr = String(chars) + if (set.contains(newStr)) + res.add(newStr) + } + + chars[i] = temp + } + + return res + } +} \ No newline at end of file diff --git a/kotlin/0128-longest-consecutive-sequence.kt b/kotlin/0128-longest-consecutive-sequence.kt new file mode 100644 index 000000000..11f55e8d1 --- /dev/null +++ b/kotlin/0128-longest-consecutive-sequence.kt @@ -0,0 +1,75 @@ +import kotlin.math.max + +class Solution { + fun longestConsecutive(nums: IntArray): Int { + if (nums.isEmpty()) return 0 + if (nums.size == 1) return 1 + val hashSet = HashSet() + nums.forEach { hashSet.add(it) } + var longestSize = 0 + var isNumberStartOfSequence: Boolean + for (num in nums) { + isNumberStartOfSequence = !hashSet.contains(num - 1) + if (isNumberStartOfSequence) { + var nextConsecutiveNumber = num + 1 + var currentSize = 1 + while (hashSet.contains(nextConsecutiveNumber)) { + nextConsecutiveNumber++ + currentSize++ + } + longestSize = max(longestSize, currentSize) + } + } + return longestSize + } +} + +// Alternative solution using Union Find +class Solution { + + class DSU(val n: Int) { + val parent = IntArray(n) { it } + val size = IntArray(n) { 1 } + + fun find(x: Int): Int { + if (parent[x] != x) + parent[x] = find(parent[x]) + return parent[x] + } + + fun union(x: Int, y: Int) { + val px = find(x) + val py = find(y) + if (px != py) { + parent[py] = px + size[px] += size[py] + } + } + + fun getLongest(): Int { + var res = 0 + for (i in parent.indices) { + if (parent[i] == i) + res = maxOf(res, size[i]) + } + return res + } + } + + + fun longestConsecutive(nums: IntArray): Int { + val hm = HashMap() + val dsu = DSU(nums.size) + + for ((i,n) in nums.withIndex()) { + if (n in hm) continue + + hm[n - 1]?.let { dsu.union(i, it) } + hm[n + 1]?.let { dsu.union(i, it) } + + hm[n] = i + } + + return dsu.getLongest() + } +} diff --git a/kotlin/0129-sum-root-to-leaf-numbers.kt b/kotlin/0129-sum-root-to-leaf-numbers.kt new file mode 100644 index 000000000..189f6cd9c --- /dev/null +++ b/kotlin/0129-sum-root-to-leaf-numbers.kt @@ -0,0 +1,25 @@ +class Solution { + fun sumNumbers(root: TreeNode?): Int { + var res = 0 + + fun dfs(root: TreeNode?, current: Int) { + root?: return + + var new = current * 10 + new += root.value + + if(root.left == null && root.right == null) { + res += new + return + } + + dfs(root.left, new) + dfs(root.right, new) + } + + dfs(root, 0) + return res + } + + val TreeNode.value get()= this.`val` +} diff --git a/kotlin/0130-surrounded-regions.kt b/kotlin/0130-surrounded-regions.kt new file mode 100644 index 000000000..a47cbb5c0 --- /dev/null +++ b/kotlin/0130-surrounded-regions.kt @@ -0,0 +1,45 @@ +package kotlin + +// Runtime: 356 ms, faster than 72.97% of Kotlin online submissions for Surrounded Regions. +// Memory Usage: 48.5 MB, less than 75.68% of Kotlin online submissions for Surrounded Regions. +// https://leetcode.com/submissions/detail/776042031/ +class Solution { + fun solve(board: Array): Unit { + val n = board.size + val m = board[0].size + + for (y in 0 until n) { + for (x in 0 until m) { + if (x==0||x==m-1||y==0||y==n-1){ + if (board[y][x] == 'O') { + markI(board, y, x) + } + } + } + } + + for (y in 0 until n) { + for (x in 0 until m) { + board[y][x] = when(board[y][x]){ + 'O' -> 'X' + 'I' -> 'O' + else -> board[y][x] // could've just 'continue'd only if leetcode uses newer version of Kotlin + } + } + } + } + fun inBound(board: Array, r: Int, c: Int) : Boolean { + val n = board.size + val m = board[0].size + return !(r < 0 || r >= n || c < 0 || c >= m) + } + fun markI(board: Array, y: Int, x: Int) { + if (!inBound(board, y, x)) return + if (board[y][x] != 'O') return + board[y][x] = 'I' + markI(board, y+1, x) + markI(board, y-1, x) + markI(board, y, x+1) + markI(board, y, x-1) + } +} diff --git a/kotlin/0131-palindrome-partitioning.kt b/kotlin/0131-palindrome-partitioning.kt new file mode 100644 index 000000000..66206c4bf --- /dev/null +++ b/kotlin/0131-palindrome-partitioning.kt @@ -0,0 +1,36 @@ +class Solution { + fun partition(s: String): List> { + val res = mutableListOf>() + val part = mutableListOf() + + fun isPalindrome(_l: Int, _r: Int): Boolean { + var l = _l + var r = _r + while (l < r) { + if (s[l] != s[r]) + return false + l++ + r-- + } + return true + } + + fun dfs(i: Int) { + if (i >= s.length) { + res.add(part.toMutableList()) + return + } + + for (j in i until s.length) { + if (isPalindrome(i, j)) { + part.add(s.substring(i, j + 1)) + dfs(j + 1) + part.removeAt(part.lastIndex) + } + } + } + + dfs(0) + return res + } +} diff --git a/kotlin/0133-clone-graph.kt b/kotlin/0133-clone-graph.kt new file mode 100644 index 000000000..a308f73cf --- /dev/null +++ b/kotlin/0133-clone-graph.kt @@ -0,0 +1,66 @@ +//recursive version +class Solution { + fun cloneGraph(node: Node?): Node? { + if(node == null) + return null + val hm = HashMap() + return clone_dfs(hm, node) + } + private fun clone_dfs(hm: HashMap, oldNode: Node?): Node?{ + if(hm.contains(oldNode)) + return hm.get(oldNode) + val copy = Node(oldNode!!.`val`) + hm[oldNode] = copy + for(n in oldNode.neighbors) + copy.neighbors.add(clone_dfs(hm, n)) + return copy + } +} + +//dfs with queue +class SolutionDFS { + fun cloneGraph(node: Node?): Node? { + if(node == null) + return null + val hm = HashMap() + val q = ArrayDeque() + q.add(node) + hm[node] = Node(node!!.`val`) + while(!q.isEmpty()){ + val current = q.poll() + for(n in current.neighbors){ + if(!hm.contains(n)){ + val copy = Node(n!!.`val`) + hm[n] = copy + q.add(n) + } + hm[current]!!.neighbors.add(hm[n]) + } + } + return hm[node] + } +} + +//bfs with queue +class SolutionBFS { + fun cloneGraph(node: Node?): Node? { + if(node == null) + return null + val hm = HashMap() + val q = ArrayDeque() + q.addLast(node) + hm[node] = Node(node!!.`val`) + while(!q.isEmpty()){ + val current = q.removeLast() + for(n in current.neighbors){ + if(!hm.contains(n)){ + val copy = Node(n!!.`val`) + hm[n] = copy + q.add(n) + } + hm[current]!!.neighbors.add(hm[n]) + } + } + return hm[node] + } +} diff --git a/kotlin/0134-gas-station.kt b/kotlin/0134-gas-station.kt new file mode 100644 index 000000000..428f94f02 --- /dev/null +++ b/kotlin/0134-gas-station.kt @@ -0,0 +1,26 @@ +class Solution { + fun canCompleteCircuit(gas: IntArray, cost: IntArray): Int { + var sum = 0 + val n = gas.size + + for (i in 0..n-1) { + sum += gas[i] - cost[i] + } + + if (sum < 0) + return -1 + + var gasInTank = 0 + var start = 0 + + for (i in 0..n-1) { + gasInTank += gas[i] - cost[i] + if (gasInTank < 0) { + start = i+1 + gasInTank = 0 + } + } + + return if (gasInTank < 0) -1 else start + } +} \ No newline at end of file diff --git a/kotlin/0135-candy.kt b/kotlin/0135-candy.kt new file mode 100644 index 000000000..93308754e --- /dev/null +++ b/kotlin/0135-candy.kt @@ -0,0 +1,64 @@ +// O(n) linear sweep solution +class Solution { + fun candy(ratings: IntArray): Int { + val n = ratings.size + val candies = IntArray (n) { 1 } + + for (i in 1 until n) { + if (ratings[i] > ratings[i - 1]) + candies[i] = candies[i - 1] + 1 + } + + for (i in n - 2 downTo 0){ + if (ratings[i] > ratings[i + 1]) + candies[i] = max(candies[i], candies[i + 1] + 1) + } + + return candies.sum() ?: n + } + +//graph dfs solution +class Solution { + fun candy(ratings: IntArray): Int { + val n = ratings.size + val outdegree = IntArray (n) + + for (i in 0 until n) { + if (i > 0 && ratings[i] > ratings[i - 1]) + outdegree[i]++ + if (i + 1 < n && ratings[i] > ratings[i + 1]) + outdegree[i]++ + } + + val q = LinkedList() + for ((index, degree) in outdegree.withIndex()) { + if (degree == 0) + q.addLast(index) + } + + val candies = IntArray (n) + while (q.isNotEmpty()) { + val i = q.removeFirst() + + var candy = 1 + if (i > 0 && ratings[i] > ratings[i - 1]) + candy = maxOf(candy, candies[i - 1] + 1) + if (i < n - 1 && ratings[i] > ratings[i + 1]) + candy = maxOf(candy, candies[i + 1] + 1) + candies[i] = candy + + if (i > 0 && ratings[i] < ratings[i - 1]) { + outdegree[i - 1]-- + if (outdegree[i - 1] == 0) + q.addLast(i - 1) + } + if (i < n - 1 && ratings[i] < ratings[i + 1]) { + outdegree[i + 1]-- + if (outdegree[i + 1] == 0) + q.addLast(i + 1) + } + } + + return candies.sum()!! + } +} diff --git a/kotlin/0136-single-number.kt b/kotlin/0136-single-number.kt new file mode 100644 index 000000000..8ec1a98ac --- /dev/null +++ b/kotlin/0136-single-number.kt @@ -0,0 +1,12 @@ +package kotlin + +class Solution { + fun singleNumber(nums: IntArray): Int { + var res = 0 + + for(n in nums){ + res = res xor n + } + return res + } +} diff --git a/kotlin/0138-copy-list-with-random-pointer.kt b/kotlin/0138-copy-list-with-random-pointer.kt new file mode 100644 index 000000000..4e7401ca7 --- /dev/null +++ b/kotlin/0138-copy-list-with-random-pointer.kt @@ -0,0 +1,35 @@ +package kotlin + +class Node(var `val`: Int) { + var next: Node? = null + var random: Node? = null +} + +class Solution { + fun copyRandomList(node: Node?): Node? { + if (node == null) return null + val hashMap = HashMap() + val dummyNode = Node(-1) + var currentNode = node + var currentResultantListNode: Node? = dummyNode + // create the new linked list ignoring the random pointers + while (currentNode != null) { + val newNode = Node(currentNode.`val`) + currentResultantListNode?.next = newNode + // associate the node of the original list to the related new node + hashMap[currentNode] = newNode + currentResultantListNode = newNode + currentNode = currentNode.next + } + currentNode = node + currentResultantListNode = dummyNode.next + // make the "random" pointers of each node in the new list, + // match those of the original list + while (currentNode != null) { + if (currentNode.random != null) currentResultantListNode?.random = hashMap[currentNode!!.random] + currentNode = currentNode.next + currentResultantListNode = currentResultantListNode?.next + } + return dummyNode.next + } +} \ No newline at end of file diff --git a/kotlin/0139-word-break.kt b/kotlin/0139-word-break.kt new file mode 100644 index 000000000..3635b80bd --- /dev/null +++ b/kotlin/0139-word-break.kt @@ -0,0 +1,86 @@ +//DP +class Solution { + fun wordBreak(s: String, wordDict: List): Boolean { + val cache = BooleanArray (s.length + 1).apply { + this[s.length] = true + } + + for (i in s.lastIndex downTo 0) { + for (word in wordDict) { + if (word.length + i <= s.length) { + if (word == s.substring(i, i + word.length)) { + if (cache[i + word.length] == true) + cache[i] = true + } + } + } + } + return cache[0] + } +} + +//Recursive + memoization +class Solution { + fun wordBreak(s: String, wordDict: List): Boolean { + val cache = IntArray (s.length + 1) { -1 } + + fun dfs(i: Int): Int { + if (i == s.length) return 1 + if (cache[i] != -1) return cache[i] + + cache[i] = 0 + + for (word in wordDict) { + if (word.length + i <= s.length && + word == s.substring(i, i + word.length)) { + if (dfs(i + word.length) == 1) { + cache[i] = 1 + return 1 + } + } + } + + return 0 + } + + return if (dfs(0) == 1) true else false + } +} + +//trie +class Solution { + + class TrieNode { + val child = arrayOfNulls(26) + var isEnd = false + } + + fun wordBreak(s: String, wordDict: List): Boolean { + var root: TrieNode? = TrieNode() + + for (word in wordDict) { + var cur = root + for (c in word) { + if(cur?.child?.get(c - 'a') == null) + cur?.child?.set(c - 'a', TrieNode()) + cur = cur?.child?.get(c - 'a') + } + cur?.isEnd = true + } + + val dp = BooleanArray (s.length + 1).apply { this[0] = true } + + for (i in 0 until s.length) { + if (dp[i] == false) continue + var j = i + var cur = root + while (j < s.length && cur?.child?.get(s[j] - 'a') != null) { + cur = cur?.child?.get(s[j++] - 'a') + if (cur?.isEnd == true) + dp[j] = true + } + } + + return dp[s.length] + } +} diff --git a/kotlin/0141-linked-list-cycle.kt b/kotlin/0141-linked-list-cycle.kt new file mode 100644 index 000000000..bb4df6708 --- /dev/null +++ b/kotlin/0141-linked-list-cycle.kt @@ -0,0 +1,15 @@ +package kotlin + +class Solution { + fun hasCycle(head: ListNode?): Boolean { + if (head == null) return false + var slowPointer = head + var fastPointer = head.next?.next + while (fastPointer != null) { + if (slowPointer === fastPointer) return true + slowPointer = slowPointer?.next + fastPointer = fastPointer.next?.next + } + return false + } +} \ No newline at end of file diff --git a/kotlin/0143-reorder-list.kt b/kotlin/0143-reorder-list.kt new file mode 100644 index 000000000..8da398885 --- /dev/null +++ b/kotlin/0143-reorder-list.kt @@ -0,0 +1,50 @@ +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun reorderList(head: ListNode?): Unit { + if (head == null || head.next == null) + return + + var slow = head + var fast = head.next + + while (fast != null && fast.next != null && slow != null) { + fast = fast.next.next + slow = slow.next + } + + var reversed = reverse(slow!!.next) + slow.next = null + var curr = head + + while (curr != null && reversed != null) { + val next = curr.next + val revNext = reversed.next + curr.next = reversed + reversed.next = next + curr = next + reversed = revNext + } + } + + fun reverse(node: ListNode?): ListNode? { + var prev: ListNode? = null + var curr = node + + while (curr != null) { + var next = curr.next + curr.next = prev + prev = curr + curr = next + } + + return prev + } +} \ No newline at end of file diff --git a/kotlin/0144-binary-tree-preorder-traversal.kt b/kotlin/0144-binary-tree-preorder-traversal.kt new file mode 100644 index 000000000..9badd7a26 --- /dev/null +++ b/kotlin/0144-binary-tree-preorder-traversal.kt @@ -0,0 +1,37 @@ +//iterative version +class Solution { + fun preorderTraversal(root: TreeNode?): List { + val res = ArrayList() + val stack = Stack() + + if (root != null) stack.push(root) + + while (!stack.isEmpty()) { + + val node = stack.pop() + res.add(node.`val`) + + if (node.right != null) stack.push(node.right) + if (node.left != null) stack.push(node.left) + } + + return res + } +} + +//recursion version +class Solution { + fun preorderTraversal(root: TreeNode?): List { + val res = ArrayList() + + fun preOrder(node: TreeNode?) { + node?: return + res.add(node.`val`) + preOrder(node.left) + preOrder(node.right) + } + + preOrder(root) + return res + } +} diff --git a/kotlin/0145-binary-tree-postorder-traversal.kt b/kotlin/0145-binary-tree-postorder-traversal.kt new file mode 100644 index 000000000..e681fe958 --- /dev/null +++ b/kotlin/0145-binary-tree-postorder-traversal.kt @@ -0,0 +1,55 @@ +// iterative solution +class Solution { + fun postorderTraversal(root: TreeNode?): List { + val res = ArrayList() + val stack = Stack() + + if (root != null) stack.push(root) + while (!stack.isEmpty()) { + val node = stack.pop() + res.add(0, node.`val`) + if (node.left != null) stack.push(node.left) + if (node.right != null) stack.push(node.right) + } + + return res + } +} + +//iterative solution, doing preorder traversal BUT adding left before right, and at end reversing the result list to get correct order +class Solution { + fun postorderTraversal(root: TreeNode?): List { + val res = ArrayList() + val stack = Stack() + + if (root != null) stack.push(root) + while (!stack.isEmpty()) { + + val node = stack.pop() + res.add(node.`val`) + + if(node.left != null) stack.push(node.left) + if(node.right != null) stack.push(node.right) + } + + res.reverse() + return res + } +} + +//recursion version +class Solution { + fun postorderTraversal(root: TreeNode?): List { + val res = ArrayList() + + fun postOrder(node: TreeNode?) { + node?: return + postOrder(node.left) + postOrder(node.right) + res.add(node.`val`) + } + + postOrder(root) + return res + } +} diff --git a/kotlin/0146-lru-cache.kt b/kotlin/0146-lru-cache.kt new file mode 100644 index 000000000..4554290df --- /dev/null +++ b/kotlin/0146-lru-cache.kt @@ -0,0 +1,58 @@ +package kotlin + +private class Node( + val key: Int, + var value: Int, + var next: Node? = null, + var previous: Node? = null +) + +class LRUCache(private val capacity: Int) { + private val head = Node(0, 0) + private val tail = Node(0, 0) + private val hashMap = HashMap() + + init { + head.next = tail + tail.previous = head + } + + fun get(key: Int): Int { + if (key !in hashMap) return -1 + val nodeToBeRemoved = hashMap.getValue(key) + removeNode(nodeToBeRemoved) + insertAtEnd(nodeToBeRemoved) + return nodeToBeRemoved.value + } + + fun put(key: Int, value: Int) { + if (key in hashMap) { + val nodeToBeMoved = hashMap.getValue(key).apply { this.value = value } + removeNode(nodeToBeMoved) + insertAtEnd(nodeToBeMoved) + return + } + if (hashMap.size == capacity) { + val nodeToRemove = head.next!! + removeNode(nodeToRemove) + hashMap.remove(nodeToRemove.key) + } + val newNode = Node(key, value) + insertAtEnd(newNode) + hashMap[key] = newNode + } + + private fun insertAtEnd(node: Node) { + node.apply { + previous = tail.previous + next = tail + } + tail.previous!!.next = node + tail.previous = node + } + + private fun removeNode(node: Node) { + node.previous?.next = node.next + node.next?.previous = node.previous + } +} \ No newline at end of file diff --git a/kotlin/0147-insertion-sort-list.kt b/kotlin/0147-insertion-sort-list.kt new file mode 100644 index 000000000..0b68fd82b --- /dev/null +++ b/kotlin/0147-insertion-sort-list.kt @@ -0,0 +1,29 @@ +class Solution { + fun insertionSortList(head: ListNode?): ListNode? { + val dummy = ListNode(0) + dummy.next = head + var prev = head + var cur = head?.next + + while (cur != null) { + if (prev != null && cur.value >= prev.value) { + prev = cur + cur = cur.next + continue + } + + var temp = dummy + while (cur.value > temp.next.value) + temp = temp.next + prev?.next = cur.next + cur.next = temp.next + temp.next = cur + cur = prev?.next + } + + return dummy.next + } + + val ListNode.value + get() = this.`val` +} diff --git a/kotlin/0148-sort-list.kt b/kotlin/0148-sort-list.kt new file mode 100644 index 000000000..5804ffff3 --- /dev/null +++ b/kotlin/0148-sort-list.kt @@ -0,0 +1,52 @@ +class Solution { + fun sortList(head: ListNode?): ListNode? { + + fun mid(root: ListNode?): ListNode? { + var slow = head + var fast = head + var prev: ListNode? = null + while (fast != null && fast?.next != null) { + prev = slow + slow = slow?.next + fast = fast?.next?.next + } + prev?.next = null + return slow + } + + fun merge(_l1: ListNode?, _l2: ListNode?): ListNode? { + var l1 = _l1 + var l2 = _l2 + var dummy = ListNode() + var tail = dummy + + while (l1 != null && l2 != null) { + if (l1.value < l2.value) { + tail.next = l1 + l1 = l1?.next + } else { + tail.next = l2 + l2 = l2?.next + } + tail = tail?.next + } + + if (l1 != null) tail.next = l1 + if (l2 != null) tail.next = l2 + + return dummy.next + } + + if (head == null || head.next == null) + return head + + val mid = mid(head) + val sortLeft = sortList(head) + val sortRight = sortList(mid) + + return merge(sortLeft, sortRight) + } + + val ListNode.value + get() = this.`val` +} diff --git a/kotlin/0149-max-points-on-a-line.kt b/kotlin/0149-max-points-on-a-line.kt new file mode 100644 index 000000000..d451dc3ca --- /dev/null +++ b/kotlin/0149-max-points-on-a-line.kt @@ -0,0 +1,26 @@ +class Solution { + fun maxPoints(points: Array): Int { + val n = points.size + + var res = 1 + for (i in 0 until n) { + val p1 = points[i] + val count = HashMap() + + for (j in i + 1 until n) { + val p2 = points[j] + + val slope = when { + p1[1] == p2[1] -> 0.0 + p1[0] != p2[0] -> (p1[1] - p2[1]) * 1.0 / (p1[0] - p2[0]) + else -> Double.MAX_VALUE + } + + count[slope] = count.getOrDefault(slope, 0) + 1 + res = maxOf(res, (count[slope] ?: 0) + 1) + } + } + + return res + } +} diff --git a/kotlin/0150-evaluate-reverse-polish-notation.kt b/kotlin/0150-evaluate-reverse-polish-notation.kt new file mode 100644 index 000000000..8a78c4059 --- /dev/null +++ b/kotlin/0150-evaluate-reverse-polish-notation.kt @@ -0,0 +1,29 @@ +package kotlin + +import java.util.* + +class Solution { + fun performOperationWithToken(token: String, stack: Stack): Int { + val y = stack.pop() + val x = stack.pop() + return when (token) { + "+" -> x + y + "-" -> x - y + "*" -> x * y + else -> x / y + } + } + + fun evalRPN(tokens: Array): Int { + val stack = Stack() + for (token in tokens) { + stack.push( + when (token) { + "+", "-", "*", "/" -> performOperationWithToken(token, stack) + else -> token.toInt() + } + ) + } + return stack.pop() + } +} \ No newline at end of file diff --git a/kotlin/0152-maximum-product-subarray.kt b/kotlin/0152-maximum-product-subarray.kt new file mode 100644 index 000000000..db1ecf005 --- /dev/null +++ b/kotlin/0152-maximum-product-subarray.kt @@ -0,0 +1,15 @@ +class Solution { + fun maxProduct(nums: IntArray): Int { + var res = nums[0] + var curMin = 1 + var curMax = 1 + + for (n in nums) { + val temp = curMax * n + curMax = maxOf(n * curMax, n * curMin, n) + curMin = minOf(temp, n * curMin, n) + res = maxOf(res, curMax) + } + return res + } +} \ No newline at end of file diff --git a/kotlin/0153-find-minimum-in-rotated-sorted-array.kt b/kotlin/0153-find-minimum-in-rotated-sorted-array.kt new file mode 100644 index 000000000..b4384c864 --- /dev/null +++ b/kotlin/0153-find-minimum-in-rotated-sorted-array.kt @@ -0,0 +1,24 @@ +class Solution { + fun findMin(nums: IntArray): Int { + var l = 0 + var r = nums.lastIndex + var res = nums[0] + + while (l <= r) { + var mid = l + (r - l) / 2 + if (nums[l] < nums[r]) { + res = minOf(res, nums[l]) + break + } + + val m = l + (r - l) / 2 + res = minOf(res, nums[m]) + if (nums[m] >= nums[l]) + l = mid + 1 + else + r = m - 1 + } + + return res + } +} diff --git a/kotlin/0155-min-stack.kt b/kotlin/0155-min-stack.kt new file mode 100644 index 000000000..d4d61f1c1 --- /dev/null +++ b/kotlin/0155-min-stack.kt @@ -0,0 +1,26 @@ +class MinStack() { + private val stack = Stack() + private val minStack = Stack() + + fun push(`val`: Int) { + val currentMin = if (minStack.isNotEmpty()) minStack.peek() else Integer.MAX_VALUE + val newMin = minOf(currentMin, `val`) + stack.push(`val`) + minStack.push(newMin) + } + + fun pop() { + if (stack.isNotEmpty()) { + stack.pop() + minStack.pop() + } + } + + fun top(): Int { + return stack.peek() + } + + fun getMin(): Int { + return minStack.peek() + } +} \ No newline at end of file diff --git a/kotlin/0160-intersection-of-two-linked-lists.kt b/kotlin/0160-intersection-of-two-linked-lists.kt new file mode 100644 index 000000000..703d75fa5 --- /dev/null +++ b/kotlin/0160-intersection-of-two-linked-lists.kt @@ -0,0 +1,21 @@ +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ + +class Solution { + fun getIntersectionNode(headA:ListNode?, headB:ListNode?):ListNode? { + var a = headA + var b = headB + while (a != b) { + a = if(a != null) a.next else headB + b = if(b != null) b.next else headA + } + return a + } +} diff --git a/kotlin/0162-find-peak-element.kt b/kotlin/0162-find-peak-element.kt new file mode 100644 index 000000000..18e192d7b --- /dev/null +++ b/kotlin/0162-find-peak-element.kt @@ -0,0 +1,19 @@ +class Solution { + fun findPeakElement(nums: IntArray): Int { + var left = 0 + var right = nums.lastIndex + var mid = -1 + + while(left <= right) { + mid = left + (right - left) / 2 + if(mid > 0 && nums[mid] < nums[mid - 1]) + right = mid - 1 + else if(mid < nums.lastIndex && nums[mid] < nums[mid + 1]) + left = mid + 1 + else + break + } + + return mid + } +} diff --git a/kotlin/0167-two-sum-ii-input-array-is-sorted.kt b/kotlin/0167-two-sum-ii-input-array-is-sorted.kt new file mode 100644 index 000000000..82b7384fa --- /dev/null +++ b/kotlin/0167-two-sum-ii-input-array-is-sorted.kt @@ -0,0 +1,19 @@ +package kotlin + +fun twoSum(numbers: IntArray, target: Int): IntArray { + var l = 0 + var r = numbers.size - 1 + + while(l < r) { + val sum = numbers[l] + numbers[r] + if(sum < target){ + l++ + } else if(sum > target){ + r-- + } else { + return intArrayOf(l+1, r+1) + } + } + + return intArrayOf(-1, -1) +} \ No newline at end of file diff --git a/kotlin/0168-excel-sheet-column-title.kt b/kotlin/0168-excel-sheet-column-title.kt new file mode 100644 index 000000000..7e3675631 --- /dev/null +++ b/kotlin/0168-excel-sheet-column-title.kt @@ -0,0 +1,11 @@ +class Solution { + fun convertToTitle(_cN: Int) = buildString { + var cN = _cN + while (cN > 0) { + val rem = (cN - 1) % 26 + val char = 'A' + rem + insert(0, char) + cN = (cN - 1) / 26 + } + } +} diff --git a/kotlin/0169-majority-element.kt b/kotlin/0169-majority-element.kt new file mode 100644 index 000000000..187df9cc0 --- /dev/null +++ b/kotlin/0169-majority-element.kt @@ -0,0 +1,17 @@ +class Solution { + + fun majorityElement(nums: IntArray): Int { + var res = 0; var count = 0 + for(i in nums.indices){ + val found = nums[i] + if(count == 0){ + res = found + count++ + }else if(found == res) + count++ + else + count-- + } + return res + } +} diff --git a/kotlin/0173-binary-search-tree-iterator.kt b/kotlin/0173-binary-search-tree-iterator.kt new file mode 100644 index 000000000..b55badcaf --- /dev/null +++ b/kotlin/0173-binary-search-tree-iterator.kt @@ -0,0 +1,26 @@ +class BSTIterator(root: TreeNode?) { + val stack = LinkedList() + + init { + var cur = root + while (cur != null) { + stack.addLast(cur) + cur = cur?.left + } + } + + fun next(): Int { + var res = stack.removeLast() + + var cur = res?.right + while (cur != null) { + stack.addLast(cur) + cur = cur?.left + } + + return res.`val` + } + + fun hasNext() = stack.size > 0 + +} diff --git a/kotlin/0179-largest-number.kt b/kotlin/0179-largest-number.kt new file mode 100644 index 000000000..743e8b2d5 --- /dev/null +++ b/kotlin/0179-largest-number.kt @@ -0,0 +1,7 @@ +class Solution { + fun largestNumber(nums: IntArray): String { + if(nums.isEmpty()) return "" + val strs = nums.map{ it.toString() }.sortedWith( Comparator { a,b -> (b + a).compareTo(a + b) } ) + return if(strs[0][0] == '0') "0" else strs.joinToString("") + } +} diff --git a/kotlin/0187-repeated-dna-sequences.kt b/kotlin/0187-repeated-dna-sequences.kt new file mode 100644 index 000000000..47688bee4 --- /dev/null +++ b/kotlin/0187-repeated-dna-sequences.kt @@ -0,0 +1,43 @@ +/* +* Storing the 10-character long strings in our sets. +*/ +class Solution { + fun findRepeatedDnaSequences(s: String): List { + val found = HashSet() + val res = HashSet() + for(i in 0..s.lastIndex-9) { + val str = s.substring(i..i+9) + if(str !in found) found.add(str) + else res.add(str) + } + return res.toList() + } +} + +/* +* Storing the 10-character long strings as 20bit long Integers, a small optimization over the above solution +*/ +class Solution { + fun findRepeatedDnaSequences(s: String): List { + + val found = HashSet() + val res = HashSet() + val mapper = hashMapOf('A' to 0, 'C' to 1, 'G' to 2, 'T' to 3) + + var value = 0 + + for(i in 0..s.lastIndex) { + + value = value shl 2 + value = value or mapper[s[i]]!! + value = value and 0xfffff + + if(i < 9) continue + if(value in found) res.add(s.substring(i-9..i)) + else found.add(value) + + } + + return res.toList() + } +} diff --git a/kotlin/0189-rotate-array.kt b/kotlin/0189-rotate-array.kt new file mode 100644 index 000000000..c63250503 --- /dev/null +++ b/kotlin/0189-rotate-array.kt @@ -0,0 +1,17 @@ +class Solution { + fun rotate(nums: IntArray, k: Int): Unit { + + fun reverse(from: Int, to: Int) { + var f = from + var t = to + while (f < t) { + nums[f] = nums[t].also { nums[t--] = nums[f++] } + } + } + + val modK = (k % nums.size) + reverse(0, nums.lastIndex) + reverse(0, modK - 1) + reverse(modK, nums.lastIndex) + } +} diff --git a/kotlin/0190-reverse-bits.kt b/kotlin/0190-reverse-bits.kt new file mode 100644 index 000000000..b5ef979d5 --- /dev/null +++ b/kotlin/0190-reverse-bits.kt @@ -0,0 +1,18 @@ +package kotlin + + +class Solution { + // you need treat n as an unsigned value + fun reverseBits(n: Int): Int { + var n = n + if(n == 0) return 0 + var result = 0 + for( i in 0 .. 31){ + result = result shl 1 + if(n and 1 == 1) result++ + n = n shr 1 + } + + return result + } +} diff --git a/kotlin/0191-number-of-1-bits.kt b/kotlin/0191-number-of-1-bits.kt new file mode 100644 index 000000000..2b9b51804 --- /dev/null +++ b/kotlin/0191-number-of-1-bits.kt @@ -0,0 +1,16 @@ +package kotlin + +class Solution { + // you need treat n as an unsigned value + fun hammingWeight(n:Int):Int { + + var n = n + var count = 0 + for(i in 0..31){ + if( n and 1 == 1) count++ + n = n shr 1 + } + + return count + } +} diff --git a/kotlin/0198-house-robber.kt b/kotlin/0198-house-robber.kt new file mode 100644 index 000000000..34d665fcc --- /dev/null +++ b/kotlin/0198-house-robber.kt @@ -0,0 +1,54 @@ +// DP Time O(n) and Space O(1) +class Solution { + fun rob(nums: IntArray): Int { + var rob = 0 + var notRob = 0 + nums.forEach { + val currRob = notRob + it + notRob = maxOf(notRob, rob) + rob = currRob + } + + return maxOf(rob, notRob) + } +} + +// DP Time O(n) and Space O(n) +class Solution { + fun rob(nums: IntArray): Int { + val n = nums.size + val dp = IntArray (n) + dp[0] = nums[0] + + for (i in 1 until n) { + dp[i] = maxOf( + nums[i] + if (i > 1) dp[i - 2] else 0, + dp[i - 1] + ) + } + + return dp[n - 1] + } +} + +// Recursion + memoization Time O(n) and Space O(n) +class Solution { + fun rob(nums: IntArray): Int { + val n = nums.size + val dp = IntArray (n) { -1 } + + fun dfs(i: Int): Int { + if (i >= n) return 0 + if (dp[i] != -1) return dp[i] + + dp[i] = maxOf( + nums[i] + dfs(i + 2), + dfs(i + 1) + ) + + return dp[i] + } + + return dfs(0) + } +} diff --git a/kotlin/0199-binary-tree-right-side-view.kt b/kotlin/0199-binary-tree-right-side-view.kt new file mode 100644 index 000000000..eb38f4d16 --- /dev/null +++ b/kotlin/0199-binary-tree-right-side-view.kt @@ -0,0 +1,40 @@ +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun rightSideView(root: TreeNode?): List { + val res = mutableListOf() + + if (root == null) + return res + + val queue = LinkedList() + queue.add(root) + + while (!queue.isEmpty()) { + val size = queue.size + + for (i in 0..size-1) { + val node = queue.poll() + + if (i == size-1) + res.add(node.`val`) + + if (node.left != null) + queue.add(node.left) + + if (node.right != null) + queue.add(node.right) + } + } + + return res + } +} \ No newline at end of file diff --git a/kotlin/0200-number-of-islands.kt b/kotlin/0200-number-of-islands.kt new file mode 100644 index 000000000..3109c041e --- /dev/null +++ b/kotlin/0200-number-of-islands.kt @@ -0,0 +1,28 @@ +class Solution { + fun numIslands(grid: Array): Int { + var count = 0 + + for (i in 0..grid.size-1) { + for (j in 0..grid[0].size-1) { + if (grid[i][j] == '1') { + dfs(grid, i, j) + count++ + } + } + } + + return count + } + + fun dfs(grid: Array, i: Int, j: Int) { + if (i < 0 || i >= grid.size || j < 0 || j >= grid[i].size || grid[i][j] == '0') + return + + grid[i][j] = '0' + + dfs(grid, i+1, j) + dfs(grid, i-1, j) + dfs(grid, i, j+1) + dfs(grid, i, j-1) + } +} \ No newline at end of file diff --git a/kotlin/0201-bitwise-and-of-numbers-range.kt b/kotlin/0201-bitwise-and-of-numbers-range.kt new file mode 100644 index 000000000..f395900c7 --- /dev/null +++ b/kotlin/0201-bitwise-and-of-numbers-range.kt @@ -0,0 +1,48 @@ +// check difference at each bit (cannot be more than left - right) +class Solution { + fun rangeBitwiseAnd(left: Int, right: Int): Int { + var res = 0 + + for (i in 0 until 32) { + val bit = (left shr i) and 1 + if (bit == 0) continue + + val remain = left % (1 shl (i + 1)) + val diff = (1 shl (i + 1)) - remain + if (right - left < diff) + res = res or (1 shl i) + } + + return res + } +} + +// find the longest matching prefix of set bits between left and right +class Solution { + fun rangeBitwiseAnd(left: Int, right: Int): Int { + var i = 0 + var left = left + var right = right + + while (left != right) { + left = left shr 1 + right = right shr 1 + i++ + } + + return left shl i + } +} + +// find the longest matching prefix of set bits between left and right. As above but a little different logically. +class Solution { + fun rangeBitwiseAnd(left: Int, right: Int): Int { + var right = right + + while (right > left) { + right = right and (right - 1) + } + + return right + } +} diff --git a/kotlin/0202-happy-number.kt b/kotlin/0202-happy-number.kt new file mode 100644 index 000000000..0fe3b7ec8 --- /dev/null +++ b/kotlin/0202-happy-number.kt @@ -0,0 +1,23 @@ +class Solution { + fun isHappy(n: Int): Boolean { + var slow = n + var fast = sumSquareDigits(n) + + while (slow != fast) { + fast = sumSquareDigits(sumSquareDigits(fast)) + slow = sumSquareDigits(slow) + } + return fast == 1 + } + + fun sumSquareDigits(n: Int): Int { + var result = 0 + var num = n + while (num != 0) { + val digit = num % 10 + result += digit * digit + num /= 10 + } + return result + } +} \ No newline at end of file diff --git a/kotlin/0203-remove-linked-list-elements.kt b/kotlin/0203-remove-linked-list-elements.kt new file mode 100644 index 000000000..725af04f3 --- /dev/null +++ b/kotlin/0203-remove-linked-list-elements.kt @@ -0,0 +1,27 @@ +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun removeElements(head: ListNode?, `val`: Int): ListNode? { + + var dummy = ListNode(0, head) + var current = head + var prev = dummy + + while(current != null) { + if(current?.`val` == `val`) + prev.next = current.next + else + prev = current + current = current.next + } + + return dummy.next + } +} diff --git a/kotlin/0205-isomorphic-strings.kt b/kotlin/0205-isomorphic-strings.kt new file mode 100644 index 000000000..02d50d319 --- /dev/null +++ b/kotlin/0205-isomorphic-strings.kt @@ -0,0 +1,16 @@ +class Solution { + fun isIsomorphic(s: String, t: String): Boolean { + val hm = HashMap() + + for (i in 0 until s.length) { + if (s[i] !in hm.keys) { + if (t[i] in hm.values) return false + hm.put(s[i], t[i]) + } else if (hm.get(s[i]) != t[i]) { + return false + } + } + + return true + } +} diff --git a/kotlin/0206-reverse-linked-list.kt b/kotlin/0206-reverse-linked-list.kt new file mode 100644 index 000000000..bbfdf9302 --- /dev/null +++ b/kotlin/0206-reverse-linked-list.kt @@ -0,0 +1,38 @@ +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun reverseList(head: ListNode?): ListNode? { + if (head == null) + return head + + var prev: ListNode? = null + var curr = head + + while (curr != null) { + val next = curr.next + curr.next = prev + prev = curr + curr = next + } + + return prev + } + + fun reverseListRecursive(head: ListNode?): ListNode? { + if (head == null || head.next == null) + return head + + val newHead = reverseList(head.next) + head.next.next = head + head.next = null + + return newHead + } +} \ No newline at end of file diff --git a/kotlin/0207-course-schedule.kt b/kotlin/0207-course-schedule.kt new file mode 100644 index 000000000..e50ae9a01 --- /dev/null +++ b/kotlin/0207-course-schedule.kt @@ -0,0 +1,39 @@ +class Solution { + fun canFinish(numCourses: Int, prerequisites: Array): Boolean { + + if (prerequisites.isEmpty()) return true + + val nodes = mutableMapOf>() + prerequisites.forEach { + val (v, w) = it + nodes.getOrPut(v) { mutableListOf() } += w + } + + val visited = mutableSetOf() + val visitedInCurrentStack = mutableSetOf() + var hasCycle = false + + fun traverse(startNode: Int) { + + visited.add(startNode) + visitedInCurrentStack.add(startNode) + + for (v in nodes[startNode].orEmpty()) { + if (!visited.contains(v)) { + traverse(v) + } else if (visitedInCurrentStack.contains(v)) { + hasCycle = true + return + } + } + + visitedInCurrentStack.remove(startNode) + } + + for (key in nodes.keys) { + traverse(key) + } + + return !hasCycle + } +} \ No newline at end of file diff --git a/kotlin/0208-implement-trie-prefix-tree.kt b/kotlin/0208-implement-trie-prefix-tree.kt new file mode 100644 index 000000000..ce9e82fbe --- /dev/null +++ b/kotlin/0208-implement-trie-prefix-tree.kt @@ -0,0 +1,35 @@ +class Trie() { + private class Node { + val nxt = Array(26) { null as Node? } + var end = false + } + + private val root = Node() + + fun insert(word: String) { + var cur = root + for (c in word) { + cur = cur.nxt[c - 'a'] ?: Node().also { cur.nxt[c - 'a'] = it } + } + cur.end = true + } + + fun search(word: String): Boolean = internalSearch(word, prefix = false) + + fun startsWith(prefix: String): Boolean = internalSearch(prefix, prefix = true) + + private fun internalSearch(word: String, prefix: Boolean): Boolean { + var cur = root + for (c in word) { + cur = cur.nxt[c - 'a'] ?: return false + } + return cur.end || prefix + } +} +/** + * Your Trie object will be instantiated and called as such: + * var obj = Trie() + * obj.insert(word) + * var param_2 = obj.search(word) + * var param_3 = obj.startsWith(prefix) + */ diff --git a/kotlin/0209-minimum-size-subarray-sum.kt b/kotlin/0209-minimum-size-subarray-sum.kt new file mode 100644 index 000000000..934a2e39f --- /dev/null +++ b/kotlin/0209-minimum-size-subarray-sum.kt @@ -0,0 +1,18 @@ +class Solution { + fun minSubArrayLen(target: Int, nums: IntArray): Int { + var windowSum = 0 + var left = 0 + var right = 0 + var res = Integer.MAX_VALUE + + while (right < nums.size) { + windowSum += nums[right++] + while (left < nums.size && windowSum >= target) { + res = minOf(res, right - left) + windowSum -= nums[left++] + } + } + + return if (res == Integer.MAX_VALUE) 0 else res + } +} diff --git a/kotlin/0210-course-schedule-ii.kt b/kotlin/0210-course-schedule-ii.kt new file mode 100644 index 000000000..b14ba9697 --- /dev/null +++ b/kotlin/0210-course-schedule-ii.kt @@ -0,0 +1,32 @@ +class Solution { + fun findOrder(numCourses: Int, prerequisites: Array): IntArray { + val edges = Array>(numCourses) { ArrayList() } + val visited = BooleanArray(numCourses); val cycle = BooleanArray(numCourses) + val topologicalOrder = IntArray(numCourses) + val res = mutableListOf() + prerequisites.forEach{ + val (a,b) = it + edges[a].add(b) + } + for(course in 0..numCourses-1){ + if(dfs(course, visited, cycle, edges, res) == false) + return intArrayOf() + } + return res.toIntArray() + } + private fun dfs(course: Int, visited: BooleanArray, cycle: BooleanArray, edges: Array>, res: MutableList): Boolean{ + if(cycle[course] == true) + return false + if(visited[course] == true) + return true + cycle[course] = true + for(pre in edges[course]){ + if(dfs(pre, visited, cycle, edges, res) == false) + return false + } + cycle[course] = false + visited[course] = true + res.add(course) + return true + } +} diff --git a/kotlin/0211-design-add-and-search-words-data-structure.kt b/kotlin/0211-design-add-and-search-words-data-structure.kt new file mode 100644 index 000000000..ef8da4f27 --- /dev/null +++ b/kotlin/0211-design-add-and-search-words-data-structure.kt @@ -0,0 +1,25 @@ +class WordDictionary() { + data class TrieNode(var isLeaf: Boolean = false, val children: MutableMap = mutableMapOf()) + + val root = TrieNode() + + fun addWord(word: String) { + var current = root + for (c in word) { + current = current.children.getOrPut(c) { TrieNode() } + } + current.isLeaf = true + } + + fun search(word: String): Boolean { + var candidates = listOf(root) + for (c in word) { + candidates = when (c) { + '.' -> candidates.flatMap { it.children.values } + else -> candidates.mapNotNull { it.children[c] } + } + if (candidates.isEmpty()) return false + } + return candidates.any { it.isLeaf } + } +} \ No newline at end of file diff --git a/kotlin/0212-word-search-ii.kt b/kotlin/0212-word-search-ii.kt new file mode 100644 index 000000000..238520d35 --- /dev/null +++ b/kotlin/0212-word-search-ii.kt @@ -0,0 +1,68 @@ +class Solution { + private companion object{ + const val IMPOSSIBLE: Char = '#' + val DIRS = intArrayOf(0, -1, 0, 1, 0) + } + + fun findWords(board: Array, words: Array): List { + val nRows = board.size + val nCols = board[0].size + val root = buildTrie(words) + val ans = mutableListOf() + for(row in 0 until nRows){ + for(col in 0 until nCols){ + backtrack(row, col, board, root, ans) + } + } + return ans + } + + private fun backtrack(row: Int, col: Int, board: Array, node: TrieNode, res: MutableList){ + val nRows = board.size + val nCols = board[0].size + + if(row < 0 || row >= nRows || col < 0 || col >= nCols) + return + + val hold = board[row][col] + val idx = hold.toInt() - 'a'.toInt(); + if(hold == IMPOSSIBLE || node.children[idx] == null){ + return + } + + var cur: TrieNode? = node + cur = cur!!.children[idx] + if(cur!!.word != null){ + res.add(cur.word!!) + cur.word = null + } + + board[row][col] = IMPOSSIBLE + for(d in 0 until 4){ + val r = row + DIRS[d] + val c = col + DIRS[d + 1] + backtrack(r, c, board, cur, res) + } + board[row][col] = hold + } + + private fun buildTrie(words: Array): TrieNode{ + val root = TrieNode() + for(word in words){ + var cur: TrieNode? = root + for(ch in word){ + val idx = ch.toInt() - 'a'.toInt() + if(cur!!.children[idx] == null) + cur.children[idx] = TrieNode() + + cur = cur.children[idx] + } + cur!!.word = word + } + return root + } + + private data class TrieNode(var word: String? = null){ + val children: Array = Array(26){ null } + } +} \ No newline at end of file diff --git a/kotlin/0213-house-robber-ii.kt b/kotlin/0213-house-robber-ii.kt new file mode 100644 index 000000000..cdb453e2a --- /dev/null +++ b/kotlin/0213-house-robber-ii.kt @@ -0,0 +1,20 @@ +class Solution { + fun rob(nums: IntArray): Int { + if (nums.isEmpty()) return 0 + if (nums.size == 1) return nums[0] + return maxOf(rob_dp(nums, 0, nums.size-2), rob_dp(nums, 1, nums.size-1)) + } + + fun rob_dp(nums: IntArray, start: Int, end: Int): Int { + if (nums.isEmpty()) return 0 + var pre2 = 0 + var pre1 = 0 + var curr = 0 + for (i in start..end) { + curr = maxOf(pre1, pre2 + nums[i]) + pre2 = pre1 + pre1 = curr + } + return curr + } +} \ No newline at end of file diff --git a/kotlin/0215-kth-largest-element-in-an-array.kt b/kotlin/0215-kth-largest-element-in-an-array.kt new file mode 100644 index 000000000..cff5e8495 --- /dev/null +++ b/kotlin/0215-kth-largest-element-in-an-array.kt @@ -0,0 +1,52 @@ +class Solution { + fun findKthLargest(nums: IntArray, k: Int): Int { + val heap = PriorityQueue() + + for (num in nums) { + heap.add(num) + + if (heap.size > k) + heap.poll() + } + + return heap.peek() + } + + // O(n) average time complexity - Quick Select algorithm + fun findKthLargestRecursive(nums: IntArray, k: Int): Int = quickSelect( + array = nums, + startIndex = 0, + endIndex = nums.lastIndex, + k = k + ) + + private fun quickSelect( + array: IntArray, + startIndex: Int, + endIndex: Int, + k: Int, + ): Int { + // find a valid position for pivot such that all values that + // appear before the pivot are lower than the value of pivot, + // and, all values that appear after the pivot are greater + // than the pivot. + @Suppress("UnnecessaryVariable") val pivotIndex = endIndex + var validIndexForPivot = startIndex + for (i in startIndex until endIndex) { + if (array[i] < array[pivotIndex]) { + val temp = array[i] + array[i] = array[validIndexForPivot] + array[validIndexForPivot] = temp + validIndexForPivot++ + } + } + // put pivot element in it's sorted position + val temp = array[validIndexForPivot] + array[validIndexForPivot] = array[pivotIndex] + array[pivotIndex] = temp + + return if (validIndexForPivot == (array.size - k)) array[validIndexForPivot] + else if (validIndexForPivot > array.size - k) quickSelect(array, startIndex, validIndexForPivot - 1, k) + else quickSelect(array, validIndexForPivot + 1, endIndex, k) + } +} \ No newline at end of file diff --git a/kotlin/0217-contains-duplicate.kt b/kotlin/0217-contains-duplicate.kt new file mode 100644 index 000000000..6ae68ab04 --- /dev/null +++ b/kotlin/0217-contains-duplicate.kt @@ -0,0 +1,8 @@ +class Solution { + fun containsDuplicate(nums: IntArray): Boolean { + val hs = HashSet() + for (e in nums) + if (!hs.add(e)) return true + return false + } +} diff --git a/kotlin/0217-encode-and-decode-strings.kt b/kotlin/0217-encode-and-decode-strings.kt new file mode 100644 index 000000000..987ddb20c --- /dev/null +++ b/kotlin/0217-encode-and-decode-strings.kt @@ -0,0 +1,38 @@ +class Codec { + // Encodes a list of strings to a single string. + fun encode(strs: List): String { + var res = "" + strs.forEach{ + res = res + it.length + "#" + it + } + return res + } + + // Decodes a single string to a list of strings. + fun decode(s: String): List { + var (res, i) = Pair(mutableListOf(), 0) + + while (i < s.length){ + var j = i + while (s[j] != '#') { + j++ + } + val lengthOfWord = s.subSequence(i, j).toString().toInt() + + val (wordStart, wordEnd) = Pair(j+1, j+1+lengthOfWord) + res.add(s.subSequence(wordStart, wordEnd).toString()) + + i = wordEnd + + } + return res + + } +} + +/** + * Your Codec object will be instantiated and called as such: + * var obj = Codec() + * val s = obj.encode(strs) + * val ans = obj.decode(s) + */ \ No newline at end of file diff --git a/kotlin/0219-contains-duplicate-ii.kt b/kotlin/0219-contains-duplicate-ii.kt new file mode 100644 index 000000000..ba48a6109 --- /dev/null +++ b/kotlin/0219-contains-duplicate-ii.kt @@ -0,0 +1,23 @@ +class Solution { + fun containsNearbyDuplicate(nums: IntArray, k: Int): Boolean { + + val hs = HashSet() + var left = 0 + var right = 0 + + while (right < nums.size) { + + if (right - left > k) { + hs.remove(nums[left]) + left++ + } + + if (nums[right] in hs) return true + + hs.add(nums[right]) + right++ + } + + return false + } +} diff --git a/kotlin/0221-maximal-square.kt b/kotlin/0221-maximal-square.kt new file mode 100644 index 000000000..6491f7904 --- /dev/null +++ b/kotlin/0221-maximal-square.kt @@ -0,0 +1,28 @@ +class Solution { + fun maximalSquare(g: Array): Int { + var dp = Array(g.size) { IntArray(g[0].size)} + + val m = g.lastIndex + val n = g[0].lastIndex + var res = 0 + for (i in m downTo 0) { + for (j in n downTo 0) { + if (g[i][j] == '0') + continue + + dp[i][j] = 1 + minOf( + if (i < m && j < n) dp[i + 1][j + 1] else 0, + if (i < m) dp[i + 1][j] else 0, + if (j < n) dp[i][j + 1] else 0 + ) + + res = maxOf( + res, + dp[i][j] + ) + } + } + + return res * res + } +} diff --git a/kotlin/0225-implement-stack-using-queues.kt b/kotlin/0225-implement-stack-using-queues.kt new file mode 100644 index 000000000..55ce10660 --- /dev/null +++ b/kotlin/0225-implement-stack-using-queues.kt @@ -0,0 +1,28 @@ +class MyStack() { + + val stack = LinkedList() + + fun push(x: Int) = stack.addLast(x) + + // Circular behaviour + fun pop(): Int { + repeat(stack.size-1) { + stack.addLast(stack.removeFirst()) + } + return stack.removeFirst() + } + + fun top() = stack.peekLast() + + fun empty() = stack.isEmpty() + +} + +/** + * Your MyStack object will be instantiated and called as such: + * var obj = MyStack() + * obj.push(x) + * var param_2 = obj.pop() + * var param_3 = obj.top() + * var param_4 = obj.empty() + */ diff --git a/kotlin/0226-invert-binary-tree.kt b/kotlin/0226-invert-binary-tree.kt new file mode 100644 index 000000000..60bbf1ca8 --- /dev/null +++ b/kotlin/0226-invert-binary-tree.kt @@ -0,0 +1,22 @@ +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun invertTree(root: TreeNode?): TreeNode? { + if (root == null) + return root + + val left = invertTree(root.left) + root.left = invertTree(root.right) + root.right = left + + return root + } +} \ No newline at end of file diff --git a/kotlin/0229-majority-element-ii.kt b/kotlin/0229-majority-element-ii.kt new file mode 100644 index 000000000..71c6d01bd --- /dev/null +++ b/kotlin/0229-majority-element-ii.kt @@ -0,0 +1,31 @@ +class Solution { + fun majorityElement(nums: IntArray): List { + var count = HashMap() + + for (n in nums) { + count[n] = count.getOrDefault(n, 0) + 1 + + if (count.size <= 2) continue + + var newCount = HashMap() + for ((n, c) in count) { + if (c > 1) + newCount[n] = c - 1 + } + count = newCount + } + + var res = mutableListOf() + + for ((n, c) in count) { + var numCount = 0 + for (n2 in nums) + if (n == n2) + numCount++ + + if (numCount > (nums.size / 3)) res.add(n) + } + + return res + } +} diff --git a/kotlin/0230-kth-smallest-element-in-a-bst.kt b/kotlin/0230-kth-smallest-element-in-a-bst.kt new file mode 100644 index 000000000..a498d7b89 --- /dev/null +++ b/kotlin/0230-kth-smallest-element-in-a-bst.kt @@ -0,0 +1,22 @@ +package kotlin + +import TreeNode +import java.util.* + +class Solution { + fun kthSmallest(root: TreeNode, k: Int): Int { + var n = 0 + val callStack = Stack() + var currentNode: TreeNode? = root + var tempNode: TreeNode? + while (true) { + while (currentNode != null) { + callStack.push(currentNode) + currentNode = currentNode.left + } + tempNode = callStack.pop() + if (++n == k) return tempNode.`val` + currentNode = tempNode.right + } + } +} diff --git a/kotlin/0231-power-of-two.kt b/kotlin/0231-power-of-two.kt new file mode 100644 index 000000000..03c33acea --- /dev/null +++ b/kotlin/0231-power-of-two.kt @@ -0,0 +1,32 @@ +// iterative +class Solution { + fun isPowerOfTwo(n: Int): Boolean { + if (n == 0) return false + if (n == 1) return true + + var n = n + while (n % 2 == 0) + n = n shr 1 + + return n == 1 + } +} + +// recursive +class Solution { + fun isPowerOfTwo(n: Int): Boolean { + if (n == 1) return true + if (n <= 0 || n % 2 != 0) return false + return isPowerOfTwo(n shr 1) + } +} + +// one line bit manipulation +class Solution { + fun isPowerOfTwo(n: Int) = (n > 0) && (n and (n - 1) == 0) +} + +// one line bit manipulation +class Solution { + fun isPowerOfTwo(n: Int) = (n > 0 && ((1 shl 30) % n) == 0) +} diff --git a/kotlin/0232-implement-queue-using-stacks.kt b/kotlin/0232-implement-queue-using-stacks.kt new file mode 100644 index 000000000..f75d52b18 --- /dev/null +++ b/kotlin/0232-implement-queue-using-stacks.kt @@ -0,0 +1,28 @@ +class MyQueue() { + var s1 = LinkedList() + var s2 = LinkedList() + + fun push(x: Int) { + s1.addLast(x) + } + + fun pop(): Int { + if (s2.isEmpty()) + refill() + return s2.removeLast() ?: -1 + } + + fun peek(): Int { + if (s2.isEmpty()) + refill() + return s2.peekLast() ?: -1 + } + + fun empty() = maxOf(s1.size, s2.size) == 0 + + private fun refill() { + while (s1.isNotEmpty()) + s2.addLast(s1.removeLast()) + } + +} diff --git a/kotlin/0234-palindrome-linked-list.kt b/kotlin/0234-palindrome-linked-list.kt new file mode 100644 index 000000000..987a9bc19 --- /dev/null +++ b/kotlin/0234-palindrome-linked-list.kt @@ -0,0 +1,42 @@ +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun isPalindrome(head: ListNode?): Boolean { + + var slow = head + var fast = head + + // find the middle + while(fast != null && fast.next != null) { + fast = fast?.next?.next + slow = slow?.next + } + + //reverse the right part of list (from middle to end) + var prev: ListNode? = null + while(slow != null) { + val temp = slow?.next + slow?.next = prev + prev = slow + slow = temp + } + + //traverse both divided parts, left and right portion, to check if palindrome + var left = head + var right = prev + while(right != null) { + if(right?.`val` != left?.`val`) return false + left = left?.next + right = right?.next + } + + return true + } +} diff --git a/kotlin/0235-lowest-common-ancestor-of-a-binary-search-tree.kt b/kotlin/0235-lowest-common-ancestor-of-a-binary-search-tree.kt new file mode 100644 index 000000000..b91620c2a --- /dev/null +++ b/kotlin/0235-lowest-common-ancestor-of-a-binary-search-tree.kt @@ -0,0 +1,22 @@ +class TreeNode(var `val`: Int = 0) { + var left: TreeNode? = null + var right: TreeNode? = null +} + +class Solution { + fun lowestCommonAncestor(root: TreeNode, p: TreeNode?, q: TreeNode?): TreeNode? { + var currentRoot: TreeNode? = root + var lowestCommonAncestor: TreeNode? = null + while (currentRoot != null) { + if (currentRoot.`val` > p!!.`val` && currentRoot.`val` > q!!.`val`) { + currentRoot = currentRoot.left + } else if (currentRoot.`val` < p.`val` && currentRoot.`val` < q!!.`val`) { + currentRoot = currentRoot.right + } else { + lowestCommonAncestor = currentRoot + break + } + } + return lowestCommonAncestor + } +} \ No newline at end of file diff --git a/kotlin/0238-product-of-array-except-self.kt b/kotlin/0238-product-of-array-except-self.kt new file mode 100644 index 000000000..8e008f3b1 --- /dev/null +++ b/kotlin/0238-product-of-array-except-self.kt @@ -0,0 +1,26 @@ +package kotlin + + +fun main() { + val nums = intArrayOf(1, 2, 3, 4) + //o.p : 24, 12,8,6 +} + +fun productExceptSelf(nums: IntArray): IntArray { + val res = IntArray(nums.size) + + var prefix = 1 + + for (i in 0 until nums.size) { + res[i] = prefix + prefix *= nums[i] + } + + var postfix = 1 + for (i in nums.size - 1 downTo 0) { + res[i] *= postfix + postfix *= nums[i] + } + + return res +} \ No newline at end of file diff --git a/kotlin/0239-sliding-window-maximum.kt b/kotlin/0239-sliding-window-maximum.kt new file mode 100644 index 000000000..cf0d5feca --- /dev/null +++ b/kotlin/0239-sliding-window-maximum.kt @@ -0,0 +1,38 @@ +class Solution { + fun maxSlidingWindow(nums: IntArray, k: Int): IntArray { + if (nums.isEmpty() || k == 0) { + return intArrayOf() + } + var i = 0 + val queue = arrayListOf() + val array = IntArray(nums.size - k + 1) + var slideIndex = 0 + + while (i < nums.size) { + /** + * remove head if it is out of range of the sliding window + */ + if (queue.isNotEmpty() && i - queue[0] == k) { + queue.removeAt(0) + } + + /** + * remove from the tail if it's smaller than nums[i] + */ + var j = queue.size - 1 + while (queue.isNotEmpty() && j >= 0 && nums[queue[j]] < nums[i]) { + queue.removeAt(queue.size - 1) + j -- + } + + queue.add(i) + if (i - k + 1 >= 0) { + array[slideIndex ++] = nums[queue[0]] + } + + i++ + } + + return array + } +} \ No newline at end of file diff --git a/kotlin/0242-valid-anagram.kt b/kotlin/0242-valid-anagram.kt new file mode 100644 index 000000000..acd562efd --- /dev/null +++ b/kotlin/0242-valid-anagram.kt @@ -0,0 +1,11 @@ +class Solution { + fun isAnagram(s: String, t: String): Boolean { + if (s.length != t.length) return false + val arr = Array(26) {0} + for (i in s.indices) { + arr[s[i] - 'a']++ + arr[t[i] - 'a']-- + } + return arr.all{it == 0} + } +} diff --git a/kotlin/0252-meeting-rooms.kt b/kotlin/0252-meeting-rooms.kt new file mode 100644 index 000000000..f43f6cad0 --- /dev/null +++ b/kotlin/0252-meeting-rooms.kt @@ -0,0 +1,13 @@ +class Solution { + fun canAttendMeetings(intervals: Array): Boolean { + if (intervals.isEmpty()) return true + intervals.sortBy { it.first() } + var (_, previousEnd) = intervals.first() + for (i in 1..intervals.lastIndex) { + val (currentStart, currentEnd) = intervals[i] + if (currentStart < previousEnd) return false // they are overlapping + previousEnd = currentEnd + } + return true + } +} \ No newline at end of file diff --git a/kotlin/0253-meeting-rooms-ii.kt b/kotlin/0253-meeting-rooms-ii.kt new file mode 100644 index 000000000..88887f523 --- /dev/null +++ b/kotlin/0253-meeting-rooms-ii.kt @@ -0,0 +1,18 @@ +class Solution { + fun minMeetingRooms(intervals: Array): Int { + val startTimes = intervals.map { it.first() }.sorted() + val endTimes = intervals.map { it.last() }.sorted() + var (startIndex, endIndex) = Pair(0, 0) + var minMeetingRooms = 0 + while (startIndex in startTimes.indices) { + if (startTimes[startIndex] < endTimes[endIndex]) { + minMeetingRooms++ + startIndex++ + } else { + startIndex++ + endIndex++ + } + } + return minMeetingRooms + } +} \ No newline at end of file diff --git a/kotlin/0261-graph-valid-tree.kt b/kotlin/0261-graph-valid-tree.kt new file mode 100644 index 000000000..81162ff9e --- /dev/null +++ b/kotlin/0261-graph-valid-tree.kt @@ -0,0 +1,29 @@ +class Solution { + private lateinit var adjList: Array> + private val seen = mutableSetOf() + + fun validTree(n: Int, edges: Array): Boolean { + if (edges.size != n-1) { + return false + } + + adjList = Array(n) { mutableListOf() } + for (edge in edges) { + adjList[edge[0]].add(edge[1]) + adjList[edge[1]].add(edge[0]) + } + dfs(0) + + return seen.size == n + } + + private fun dfs(node: Int) { + if (seen.contains(node)) { + return + } + seen.add(node) + for (adj in adjList[node]) { + dfs(adj) + } + } +} \ No newline at end of file diff --git a/kotlin/0263-ugly-number.kt b/kotlin/0263-ugly-number.kt new file mode 100644 index 000000000..76c9c5e22 --- /dev/null +++ b/kotlin/0263-ugly-number.kt @@ -0,0 +1,13 @@ +class Solution { + fun isUgly(_n: Int): Boolean { + if (_n <= 0) return false + + var n = _n + for (p in listOf(2, 3, 5)) { + while (n % p == 0) + n /= p + } + + return n == 1 + } +} diff --git a/kotlin/0268-missing-number.kt b/kotlin/0268-missing-number.kt new file mode 100644 index 000000000..99462a6dd --- /dev/null +++ b/kotlin/0268-missing-number.kt @@ -0,0 +1,13 @@ +package kotlin + +class Solution { + fun missingNumber(nums: IntArray): Int { + var missing = nums.size + for(i in nums.indices){ + missing = missing xor i xor nums[i] + } + + return missing + + } +} diff --git a/kotlin/0271-encode-and-decode-strings.kt b/kotlin/0271-encode-and-decode-strings.kt new file mode 100644 index 000000000..fdabb89de --- /dev/null +++ b/kotlin/0271-encode-and-decode-strings.kt @@ -0,0 +1,43 @@ +package kotlin + +class Codec { + // Encodes a list of strings to a single string. + fun encode(strs: List): String { + val stringBuilder = StringBuilder() + for (string in strs) { + for (char in string) { + stringBuilder.append(char.toInt()) // char.code in newer version's of Kotlin + stringBuilder.append(CHAR_DELIMITER) + } + stringBuilder.append(STRING_DELIMITER) + } + return stringBuilder.toString() + } + + // Decodes a single string to a list of strings. + fun decode(s: String): List { + val stringBuilder = StringBuilder() + val resultantList = mutableListOf() + var i = 0 + while (i in s.indices) { + while (s[i] != STRING_DELIMITER) { + var charIntegerValue = "" + while (s[i] != CHAR_DELIMITER) { + charIntegerValue += s[i] + i++ + } + stringBuilder.append(charIntegerValue.toInt().toChar()) + i++ + } + resultantList.add(stringBuilder.toString()) + stringBuilder.clear() + i++ + } + return resultantList + } + + companion object { + private const val CHAR_DELIMITER = '|' + private const val STRING_DELIMITER = '/' + } +} \ No newline at end of file diff --git a/kotlin/0279-perfect-squares.kt b/kotlin/0279-perfect-squares.kt new file mode 100644 index 000000000..d0024516e --- /dev/null +++ b/kotlin/0279-perfect-squares.kt @@ -0,0 +1,16 @@ +class Solution { + fun numSquares(n: Int): Int { + val dp = IntArray(n + 1){n} + + dp[0] = 0 + for (target in 1..n) { + for (num in 1..target) { + val square = num * num + if (target - square < 0) break + dp[target] = minOf(dp[target], 1 + dp[target - square]) + } + } + + return dp[n] + } +} diff --git a/kotlin/0280-wiggle-sort.kt b/kotlin/0280-wiggle-sort.kt new file mode 100644 index 000000000..84d0d0f91 --- /dev/null +++ b/kotlin/0280-wiggle-sort.kt @@ -0,0 +1,19 @@ +class Solution { + fun wiggleSort(nums: IntArray): Unit { + for (i in 1 .. nums.lastIndex) { + val prev = nums[i - 1] + val curr = nums[i] + + when (i % 2) { + 0 -> { if (prev < curr) nums.swap(i) } + else -> { if (prev > curr) nums.swap(i) } + } + } + } + + private fun IntArray.swap(currentIndex: Int) { + this[currentIndex - 1] = this[currentIndex].also { + this[currentIndex] = this[currentIndex - 1] + } + } +} \ No newline at end of file diff --git a/kotlin/0283-move-zeroes.kt b/kotlin/0283-move-zeroes.kt new file mode 100644 index 000000000..fde8b5ef5 --- /dev/null +++ b/kotlin/0283-move-zeroes.kt @@ -0,0 +1,15 @@ +class Solution { + fun moveZeroes(nums: IntArray): Unit { + var l = 0 + for(r in 0 until nums.size) { + if(nums[r] != 0){ + nums.swap(l,r) + l++ + } + } + return + } + fun IntArray.swap(i: Int, j: Int) { + this[i] = this[j].also{this[j] = this[i]} + } +} diff --git a/kotlin/0286-walls-and-gates.kt b/kotlin/0286-walls-and-gates.kt new file mode 100644 index 000000000..c10f0fa4a --- /dev/null +++ b/kotlin/0286-walls-and-gates.kt @@ -0,0 +1,30 @@ +class Solution { + fun wallsAndGates(rooms: Array): Unit { + val queue: Queue> = LinkedList() + for (i in 0 until rooms.size) { + for (j in 0 until rooms[0].size) { + if (rooms[i][j] == 0) { + queue.offer(Pair(i, j)) + } + } + } + + val directions = arrayOf(arrayOf(-1, 0), arrayOf(1, 0), arrayOf(0, -1), arrayOf(0, 1)) + var distance = 1 + while (queue.isNotEmpty()) { + val size = queue.size + repeat(size) { + val cell = queue.poll() + for (direction in directions) { + val newRow = cell.first+direction[0] + val newCol = cell.second+direction[1] + if (newRow >= 0 && newRow < rooms.size && newCol >= 0 && newCol < rooms[0].size && rooms[newRow][newCol] == Integer.MAX_VALUE) { + rooms[newRow][newCol] = distance + queue.offer(Pair(newRow, newCol)) + } + } + } + distance++ + } + } +} \ No newline at end of file diff --git a/kotlin/0287-find-the-duplicate-number.kt b/kotlin/0287-find-the-duplicate-number.kt new file mode 100644 index 000000000..bd442b63c --- /dev/null +++ b/kotlin/0287-find-the-duplicate-number.kt @@ -0,0 +1,21 @@ +package kotlin + +class Solution { + fun findDuplicate(nums: IntArray): Int { + var slow = 0 + var slow2 = 0 + var fast = 0 + // detect the cycle + while (true) { + slow = nums[slow] + fast = nums[nums[fast]] + if (slow == fast) break + } + // get the first value at the beginning of the cycle + while (true) { + slow2 = nums[slow2] + slow = nums[slow] + if (slow2 == slow) return slow + } + } +} \ No newline at end of file diff --git a/kotlin/0290-word-pattern.kt b/kotlin/0290-word-pattern.kt new file mode 100644 index 000000000..f158596be --- /dev/null +++ b/kotlin/0290-word-pattern.kt @@ -0,0 +1,17 @@ +class Solution { + fun wordPattern(pattern: String, s: String): Boolean { + val wordArray = s.split(" ") + if(wordArray.size != pattern.length) return false + val hm = HashMap() + pattern.forEachIndexed { i, c -> + when(hm.contains(c)){ + true -> if(hm[c] != wordArray[i]) return false + false -> { + if(hm.containsValue(wordArray[i])) return false + hm[c] = wordArray[i] + } + } + } + return true + } +} diff --git a/kotlin/0295-find-median-from-data-stream.kt b/kotlin/0295-find-median-from-data-stream.kt new file mode 100644 index 000000000..bbb820eaf --- /dev/null +++ b/kotlin/0295-find-median-from-data-stream.kt @@ -0,0 +1,19 @@ +class MedianFinder() { + + /** initialize your data structure here. */ + val smaller = PriorityQueue(Comparator { a, b -> b - a }) + val larger = PriorityQueue() + + fun addNum(num: Int) { + if (smaller.isEmpty() || num <= smaller.peek()) smaller.offer(num) + else larger.offer(num) + if (smaller.size > larger.size + 1) larger.offer(smaller.poll()) + else if (larger.size > smaller.size) smaller.offer(larger.poll()) + } + + fun findMedian(): Double { + val even = (larger.size + smaller.size) % 2 == 0 + return if (even) (larger.peek() + smaller.peek()) / 2.0 + else smaller.peek().toDouble() + } +} \ No newline at end of file diff --git a/kotlin/0297-serialize-and-deserialize-binary-tree.kt b/kotlin/0297-serialize-and-deserialize-binary-tree.kt new file mode 100644 index 000000000..17434595d --- /dev/null +++ b/kotlin/0297-serialize-and-deserialize-binary-tree.kt @@ -0,0 +1,64 @@ +/** + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ + +class Codec() { + // Encodes a URL to a shortened URL. + fun serialize(root: TreeNode?): String { + val sb = StringBuilder() + serialize(root, sb) + return sb.toString() + } + + // Decodes your encoded data to tree. + fun deserialize(data: String): TreeNode? { + val queue = LinkedList() + + for (str in data.split(",")) { + queue.add(str) + } + + return deserialize(queue) + } + + private fun serialize(root: TreeNode?, sb: StringBuilder) { + if (root == null) { + sb.append("null") + sb.append(",") + return + } + + sb.append(root.`val`) + sb.append(",") + + serialize(root.left, sb) + serialize(root.right, sb) + } + + private fun deserialize(queue: Queue): TreeNode? { + val str = queue.remove() + + if (str.equals("null")) { + return null + } + + val node = TreeNode(str.toInt()) + + node.left = deserialize(queue) + node.right = deserialize(queue) + + return node + } +} + +/** + * Your Codec object will be instantiated and called as such: + * var ser = Codec() + * var deser = Codec() + * var data = ser.serialize(longUrl) + * var ans = deser.deserialize(data) + */ \ No newline at end of file diff --git a/kotlin/0300-longest-increasing-subsequence.kt b/kotlin/0300-longest-increasing-subsequence.kt new file mode 100644 index 000000000..30c926607 --- /dev/null +++ b/kotlin/0300-longest-increasing-subsequence.kt @@ -0,0 +1,15 @@ +class Solution { + fun lengthOfLIS(nums: IntArray): Int { + val dp = IntArray(nums.size) {1} + + for (i in nums.size-1 downTo 0) { + for (j in i + 1 until nums.size) { + if (nums[i] < nums[j]) { + dp[i] = maxOf(dp[i], 1 + dp[j]) + } + } + } + + return dp.max()!! + } +} diff --git a/kotlin/0303-range-sum-query-immutable.kt b/kotlin/0303-range-sum-query-immutable.kt new file mode 100644 index 000000000..56bbe534f --- /dev/null +++ b/kotlin/0303-range-sum-query-immutable.kt @@ -0,0 +1,25 @@ +class NumArray(nums: IntArray) { + + val prefix = IntArray(nums.size) + + init { + var sum = 0 + for((i,v) in nums.withIndex()) { + sum += v + prefix[i] = sum + } + } + + fun sumRange(left: Int, right: Int): Int { + val prefixR = prefix[right] + val prefixL = if(left == 0) 0 else prefix[left - 1] + return prefixR - prefixL + } + +} + +/** + * Your NumArray object will be instantiated and called as such: + * var obj = NumArray(nums) + * var param_1 = obj.sumRange(left,right) + */ diff --git a/kotlin/0304-range-sum-query-2d-immutable.kt b/kotlin/0304-range-sum-query-2d-immutable.kt new file mode 100644 index 000000000..878d7eee3 --- /dev/null +++ b/kotlin/0304-range-sum-query-2d-immutable.kt @@ -0,0 +1,39 @@ +class NumMatrix(matrix: Array) { + + val row = matrix.size + val col = matrix[0].size + val prefixSum = Array(row + 1){ IntArray(col + 1) } + + init { + for(i in 0 until row) { + var prefix = 0 + for(j in 0 until col) { + println("i: $i j: $j") + prefix += matrix[i][j] + prefixSum[i + 1][j + 1] = prefix + prefixSum[i][j + 1] + } + } + } + + + fun sumRegion(row1: Int, col1: Int, row2: Int, col2: Int): Int { + val r1 = row1 + 1 + val r2 = row2 + 1 + val c1 = col1 + 1 + val c2 = col2 + 1 + + val botRight = prefixSum[r2][c2] + val aboveOf = prefixSum[r1 - 1][c2] + val leftOf = prefixSum[r2][c1 - 1] + val topLeft = prefixSum[r1 - 1][c1 - 1] + + return botRight - aboveOf - leftOf + topLeft + } + +} + +/** + * Your NumMatrix object will be instantiated and called as such: + * var obj = NumMatrix(matrix) + * var param_1 = obj.sumRegion(row1,col1,row2,col2) + */ diff --git a/kotlin/0309-best-time-to-buy-and-sell-stock-with-cooldown.kt b/kotlin/0309-best-time-to-buy-and-sell-stock-with-cooldown.kt new file mode 100644 index 000000000..edb1e3bd1 --- /dev/null +++ b/kotlin/0309-best-time-to-buy-and-sell-stock-with-cooldown.kt @@ -0,0 +1,15 @@ +class Solution { + fun maxProfit(prices: IntArray): Int { + var sold = 0 + var hold = Int.MIN_VALUE + var rest = 0 + + for (i in 0..prices.size-1) { + val prevSold = sold + sold = hold + prices[i] + hold = maxOf(hold, rest - prices[i]) + rest = maxOf(rest, prevSold) + } + return maxOf(sold, rest) + } +} \ No newline at end of file diff --git a/kotlin/0312-burst-balloons.kt b/kotlin/0312-burst-balloons.kt new file mode 100644 index 000000000..cc1055ec0 --- /dev/null +++ b/kotlin/0312-burst-balloons.kt @@ -0,0 +1,22 @@ +class Solution { + fun maxCoins(_nums: IntArray): Int { + val nums = intArrayOf(1) + _nums + intArrayOf(1) + val n = nums.size + val dp = Array (n) { IntArray (n) { -1 } } + + fun dfs(l: Int, r: Int): Int { + if (l > r) return 0 + if (dp[l][r] != -1) return dp[l][r] + + for (i in l..r) { + var coins = nums[l - 1] * nums[i] * nums[r + 1] + coins += dfs(l, i - 1) + dfs(i + 1, r) + dp[l][r] = maxOf(dp[l][r], coins) + } + + return dp[l][r] + } + + return dfs(1, n - 2) + } +} diff --git a/kotlin/0322-coin-change.kt b/kotlin/0322-coin-change.kt new file mode 100644 index 000000000..1917ce368 --- /dev/null +++ b/kotlin/0322-coin-change.kt @@ -0,0 +1,42 @@ +// DP +class Solution { + fun coinChange(coins: IntArray, amount: Int): Int { + val dp = IntArray (amount + 1) { amount + 1 } + dp[0] = 0 + for (i in 0..amount) { + for (j in 0 until coins.size) { + if (coins[j] <= i) { + dp[i] = minOf(dp[i], dp[i - coins[j]] + 1) + } + } + } + return if (dp[amount] > amount) -1 else dp[amount] + } +} + +// Recursion + memoization +class Solution { + fun coinChange(coins: IntArray, amount: Int): Int { + val dp = IntArray (amount + 1) { -1 } + + fun dfs(amount: Int): Int { + if (amount == 0) return 0 + if (dp[amount] != -1) return dp[amount] + + var res = Integer.MAX_VALUE + for (coin in coins) { + if (amount - coin >= 0) { + var count = dfs(amount - coin) + if (count != Integer.MAX_VALUE) + res = minOf(res, count + 1) + } + } + + dp[amount] = res + return res + } + + val res = dfs(amount) + return if (res == Integer.MAX_VALUE) -1 else res + } +} diff --git a/kotlin/0323-number-of-connected-components-in-an-undirected-graph.kt b/kotlin/0323-number-of-connected-components-in-an-undirected-graph.kt new file mode 100644 index 000000000..aa602e819 --- /dev/null +++ b/kotlin/0323-number-of-connected-components-in-an-undirected-graph.kt @@ -0,0 +1,43 @@ + +class Solution { + private val parent = ArrayList() + private val rank = ArrayList() + + fun countComponents(n:Int, edges:Array):Int { + var count = n + for (i in 0 until n) { + parent.add(i) + rank.add(1) + } + + for (i in edges.indices) { + count -= union(edges[i][0], edges[i][1]) + } + + return count + } + + private fun find(node:Int):Int{ + var result = node + while (parent[result] != result){ + parent[result] = parent[parent[result]] + result = parent[result] + } + return result + } + + private fun union(node1:Int, node2:Int):Int{ + val root1 = find(node1) + val root2 = find(node2) + if (root1==root2) + return 0 + if (rank[root1] > rank[root2]){ + parent[root2] = root1 + rank[root1] += rank[root2] + } else { + parent[root1] = root2 + rank[root2] += rank[root1] + } + return 1 + } +} \ No newline at end of file diff --git a/kotlin/0329-longest-increasing-path-in-a-matrix.kt b/kotlin/0329-longest-increasing-path-in-a-matrix.kt new file mode 100644 index 000000000..a3ca09918 --- /dev/null +++ b/kotlin/0329-longest-increasing-path-in-a-matrix.kt @@ -0,0 +1,41 @@ +class Solution { + fun longestIncreasingPath(matrix: Array): Int { + val m = matrix.size + val n = matrix[0].size + val memo = Array(m){IntArray(n)} + val dirs = arrayOf(intArrayOf(-1,0),intArrayOf(1,0),intArrayOf(0,1),intArrayOf(0,-1)) + var res = 1 + + for (i in 0..m-1) { + for (j in 0..n-1) { + val len = dfs(matrix, i, j, dirs, memo) + res = Math.max(res, len) + } + } + + return res + } + + fun dfs(matrix: Array, row: Int, col: Int, dirs: Array, memo: Array): Int { + val m = matrix.size + val n = matrix[0].size + + if (memo[row][col] != 0) + return memo[row][col] + + var len = 1 + + for (dir in dirs) { + val x = dir[0] + row + val y = dir[1] + col + + if (x < 0 || x == m || y < 0 || y == n || matrix[row][col] >= matrix[x][y]) + continue + + len = Math.max(len, 1+dfs(matrix, x, y, dirs, memo)) + } + + memo[row][col] = len + return len + } +} \ No newline at end of file diff --git a/kotlin/0332-reconstruct-itinerary.kt b/kotlin/0332-reconstruct-itinerary.kt new file mode 100644 index 000000000..4f9d07126 --- /dev/null +++ b/kotlin/0332-reconstruct-itinerary.kt @@ -0,0 +1,45 @@ +package kotlin + +class Solution { + + fun findItinerary(tickets: List>): List { + val res = ArrayList() + val map = HashMap>() + + for (t in tickets) { + if (map[t[0]] == null) { + map[t[0]] = ArrayList() + } + map[t[0]]!!.add(t[1]) + } + + val sorted = map.mapValues { + ArrayList(it.value.sortedBy { it }) + } + + fun dfs(from: String) : Boolean { + res.add(from) + if (res.size == tickets.size + 1) return true + + val dests = sorted[from] ?: return false + if (dests.isEmpty()) return false + + val temp = ArrayList(dests) + for (t in temp) { + dests.remove(t) + + if (dfs(t)) { + return true + } else { + // res.removeLast() // not available in leetcode + res.removeAt(res.lastIndex) + dests.add(t) + } + } + return false + } + + dfs("JFK") + return res + } +} diff --git a/kotlin/0337-house-robber-iii.kt b/kotlin/0337-house-robber-iii.kt new file mode 100644 index 000000000..c7b627212 --- /dev/null +++ b/kotlin/0337-house-robber-iii.kt @@ -0,0 +1,33 @@ +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun rob(root: TreeNode?): Int { + root?: return 0 + + val res = rob2(root) + return maxOf( + res.first, + res.second + ) + } + + fun rob2(root: TreeNode?): Pair { + root?: return 0 to 0 + + val left = rob2(root.left) + val right = rob2(root.right) + + val with = root.`val` + left.second + right.second + val without = maxOf(left.first, left.second) + maxOf(right.first, right.second) + + return with to without + } +} diff --git a/kotlin/0338-counting-bits.kt b/kotlin/0338-counting-bits.kt new file mode 100644 index 000000000..fc6289380 --- /dev/null +++ b/kotlin/0338-counting-bits.kt @@ -0,0 +1,14 @@ +class Solution { + fun countBits(n: Int): IntArray { + var dp = IntArray(n + 1) + var offset = 1 + + for (i in 1..n) { + if (offset * 2 == i) { + offset = i + } + dp[i] = 1 + dp[i - offset] + } + return dp + } +} \ No newline at end of file diff --git a/kotlin/0341-flatten-nested-list-iterator.kt b/kotlin/0341-flatten-nested-list-iterator.kt new file mode 100644 index 000000000..be4ac2256 --- /dev/null +++ b/kotlin/0341-flatten-nested-list-iterator.kt @@ -0,0 +1,44 @@ +// Flatten the list at creation/constructor time, and then iterate through the flattened list +class NestedIterator(nestedList: List) { + val stack = LinkedList() + + init { + dfs(nestedList) + stack.reverse() + } + + fun next() = stack.removeLast() + + fun hasNext() = stack.isNotEmpty() + + private fun dfs(nested: List) { + for (n in nested) { + if (n.isInteger()) + stack.addLast(n.getInteger()) + else + dfs(n.getList()) + } + } +} + +// Dynamically ("on-the-go") flatten the list while iterating +class NestedIterator(nestedList: List) { + + val stack = LinkedList() + + init { + stack.addAll(nestedList.reversed()) + } + + fun next(): Int { + return stack.removeLast().getInteger() + } + + fun hasNext(): Boolean { + while (stack.isNotEmpty() && !stack.peekLast().isInteger()) { + stack.addAll(stack.removeLast().getList().reversed()) + } + + return stack.isNotEmpty() + } +} diff --git a/kotlin/0342-power-of-four.kt b/kotlin/0342-power-of-four.kt new file mode 100644 index 000000000..c8d6ef56e --- /dev/null +++ b/kotlin/0342-power-of-four.kt @@ -0,0 +1,13 @@ +// recursion time O(logn) +class Solution { + fun isPowerOfFour(n: Int): Boolean { + if (n == 1) return true + if (n <= 0 || n % 4 != 0) return false + return isPowerOfFour(n / 4) + } +} + +// bit manipulation time O(1) +class Solution { + fun isPowerOfFour(n: Int) = n > 0 && (n and (n - 1) == 0) && (n and 0x55555555) != 0 +} diff --git a/kotlin/0343-integer-break.kt b/kotlin/0343-integer-break.kt new file mode 100644 index 000000000..a619b796a --- /dev/null +++ b/kotlin/0343-integer-break.kt @@ -0,0 +1,78 @@ +/* +* DP solution O(n^2) time and space +*/ +class Solution { + fun integerBreak(n: Int): Int { + val cache = IntArray(n + 1) {-1} + + cache[1] = 1 + for (num in 2..n) { + cache[num] = if (num == n) 0 else num + for (i in 1..num) { + val res = cache[i] * cache[num - i] + cache[num] = maxOf(cache[num], res) + } + } + + return cache[n] + } +} + +/* +* DFS + memoization solution O(n^2) time and space +*/ +class Solution { + fun integerBreak(n: Int): Int { + val cache = IntArray(n + 1) {-1} + + fun dfs(num: Int): Int { + if (cache[num] != -1) return cache[num] + + cache[num] = if (num == n) 0 else num + for (i in 1 until num) { + val res = dfs(i) * dfs(num - i) + cache[num] = maxOf(cache[num], res) + } + + return cache[num] + } + + return dfs(n) + } +} + +// Math solution O(n) time and O(1) space +class Solution { + fun integerBreak(n: Int): Int { + if (n < 4) return n - 1 + + var res = 1 + var n2 = n + while (n2 > 4) { + res *= 3 + n2 -=3 + } + + res *= n2 + return res + } +} + +// Mathimatically solved O(1) +class Solution { + fun integerBreak(n: Int): Int { + if (n < 4) return n - 1 + + var res = n / 3 + var rem = n % 3 + + if (rem == 1) { + rem = 4 + res-- + } else if (rem == 0) { + rem = 1 + } + + return (Math.pow(3.toDouble(), res.toDouble()) * rem).toInt() + } +} diff --git a/kotlin/0344-reverse-string.kt b/kotlin/0344-reverse-string.kt new file mode 100644 index 000000000..23a962dd9 --- /dev/null +++ b/kotlin/0344-reverse-string.kt @@ -0,0 +1,30 @@ +/* +* Using 2 variables for the pointers +*/ +class Solution { + fun reverseString(s: CharArray): Unit { + var low = 0 + var high = s.size-1 + while(low < high){ + val temp = s[high] + s[high] = s[low] + s[low] = temp + low++ + high-- + } + } +} + +/* +* Using one variable for the pointers +*/ +class Solution { + fun reverseString(s: CharArray): Unit { + val size = s.size + for(i in 0 until size/2){ + val temp = s[i] + s[i] = s[size-1-i] + s[size-1-i] = temp + } + } +} diff --git a/kotlin/0347-top-k-frequent-elements.kt b/kotlin/0347-top-k-frequent-elements.kt new file mode 100644 index 000000000..f37afa8c6 --- /dev/null +++ b/kotlin/0347-top-k-frequent-elements.kt @@ -0,0 +1,35 @@ +package kotlin + + +fun main() { + val nums = intArrayOf(1, 1, 1, 1, 2, 2, 3) +} + +fun topKFrequent(nums: IntArray, k: Int): IntArray { + val res = mutableListOf() + + val count = hashMapOf() + + val freq = MutableList>(nums.size + 1) { + mutableListOf() + } + + for (n in nums) { + count[n] = count.getOrDefault(n, 0) + 1 + } + + for ((n, c) in count) { + freq[c].add(n) + } + + for (i in freq.size - 1 downTo 0) { + for (n in freq[i]) { + res.add(n) + if (res.size == k) { + return res.toIntArray() + } + } + } + + return intArrayOf() +} diff --git a/kotlin/0349-intersection-of-two-arrays.kt b/kotlin/0349-intersection-of-two-arrays.kt new file mode 100644 index 000000000..37a7d47b4 --- /dev/null +++ b/kotlin/0349-intersection-of-two-arrays.kt @@ -0,0 +1,13 @@ +class Solution { + fun intersection(nums1: IntArray, nums2: IntArray): IntArray { + val seen = nums1.toSet() + + val res = mutableSetOf () + for (n in nums2) { + if (n in seen) + res.add(n) + } + + return res.toIntArray() + } +} diff --git a/kotlin/0352-data-stream-as-disjoint-intervals.kt b/kotlin/0352-data-stream-as-disjoint-intervals.kt new file mode 100644 index 000000000..6fe8ff390 --- /dev/null +++ b/kotlin/0352-data-stream-as-disjoint-intervals.kt @@ -0,0 +1,98 @@ +// Using TreeSet and linear scan in getIntervals() method +class SummaryRanges() { + val ranges = TreeSet() + + fun addNum(value: Int) { + ranges.add(value) + } + + fun getIntervals(): Array { + val res = LinkedList() + ranges.forEach { + if (res.isNotEmpty() && res.peekLast()[1] + 1 == it) + res.peekLast()[1] = it + else + res.addLast(intArrayOf(it, it)) + } + return res.toTypedArray() + } +} + +// Using TreeMap and binary search scan in getIntervals() method, small optimization technically but still same time complexity as above solution +class SummaryRanges() { + val rgs = TreeMap() + + fun addNum(v: Int) { + if (rgs.containsKey(v)) return + + val l = rgs.lowerKey(v) + val h = rgs.higherKey(v) + + if (l != null && h != null && rgs.get(l)!![1] + 1 == v && h == v + 1 ) { + rgs.get(l)!![1] = rgs.get(h)!![1] + rgs.remove(h) + } else if (l != null && rgs.get(l)!![1] + 1 >= v) { + rgs.get(l)!![1] = maxOf(v, rgs.get(l)!![1]) + } else if (h != null && h == v + 1) { + rgs.put(v, intArrayOf(v, rgs.get(h)!![1])) + rgs.remove(h) + } else { + rgs.put(v, intArrayOf(v, v)) + } + } + + fun getIntervals() = rgs.values.toTypedArray() +} + +// Using Union Find +class SummaryRanges() { + val dsu = DSU() + + fun addNum(v: Int) { + if (dsu.exists(v)) return + + dsu.add(v) + dsu.union(v, v - 1) + dsu.union(v, v + 1) + } + + fun getIntervals() = dsu.getIntervals() + +} + +class DSU { + val parent = HashMap() + val intervals = TreeMap() + + fun getIntervals() = intervals.values.toTypedArray() + + fun exists(x: Int) = x in parent + + fun add(x: Int) { + parent[x] = x + intervals[x] = intArrayOf(x, x) + } + + fun find(x: Int): Int { + parent[x]?: return -1 + + if (parent[x]!! != x) + parent[x] = find(parent[x]!!) + + return parent[x]!! + } + + fun union(x: Int, y: Int) { + val px = find(x) + val py = find(y) + + if (px == -1 || py == -1) return + + val newX = minOf(intervals[py]!![0], intervals[px]!![0]) + val newY = maxOf(intervals[py]!![1], intervals[px]!![1]) + + parent[px] = py + intervals[py] = intArrayOf(newX, newY) + intervals.remove(px) + } +} diff --git a/kotlin/0355-design-twitter.kt b/kotlin/0355-design-twitter.kt new file mode 100644 index 000000000..6d9b39c7a --- /dev/null +++ b/kotlin/0355-design-twitter.kt @@ -0,0 +1,63 @@ +class Twitter() { + + var time = 0 + val tweetMap = HashMap>>() //[userId, [time, tweetId]] + val followMap = HashMap>() //[userId, [userId]] hashset to avoud duplicats + + fun postTweet(userId: Int, tweetId: Int) { + val tweetList = tweetMap.getOrDefault(userId, ArrayList>()) + tweetList.add(time to tweetId) + tweetMap.put(userId, tweetList) + time++ + } + + fun getNewsFeed(userId: Int): List { + val res = ArrayList() + val maxHeap = PriorityQueue{ a,b -> b[0] - a[0] } //[time, tweetId, index, uID] maxHeap with latest (highest time) first + + val fl = followMap.getOrDefault(userId, HashSet()) + fl.add(userId) // get our own tweets aswell + for(friend in fl){ + val tl = tweetMap.get(friend) + if(tl != null){ + var index = tl.size-1 + val (time, tweetId) = tl.get(index) + maxHeap.add(intArrayOf(time, tweetId, --index, friend)) + } + } + while(res.size < 10 && !maxHeap.isEmpty()){ + val (t, tw, i, f) = maxHeap.poll() + res.add(tw) + val tl = tweetMap.get(f) + if(tl != null && tl.size > 0 && i >= 0){ + val (time, tweetId) = tl.get(i) + maxHeap.add(intArrayOf(time, tweetId, i-1, f)) + } + } + return res + } + + fun follow(followerId: Int, followeeId: Int) { + val followSet = followMap.getOrDefault(followerId, HashSet()) + followSet.add(followeeId) + followMap.put(followerId, followSet) + } + + fun unfollow(followerId: Int, followeeId: Int) { + val followSet = followMap.getOrDefault(followerId, HashSet()) + followSet?: return + if(followSet.contains(followeeId)){ + followSet.remove(followeeId) + followMap.put(followerId, followSet) + } + } +} + +/** + * Your Twitter object will be instantiated and called as such: + * var obj = Twitter() + * obj.postTweet(userId,tweetId) + * var param_2 = obj.getNewsFeed(userId) + * obj.follow(followerId,followeeId) + * obj.unfollow(followerId,followeeId) + */ diff --git a/kotlin/0367-valid-perfect-square.kt b/kotlin/0367-valid-perfect-square.kt new file mode 100644 index 000000000..7a75c989a --- /dev/null +++ b/kotlin/0367-valid-perfect-square.kt @@ -0,0 +1,16 @@ +class Solution { + fun isPerfectSquare(num: Int): Boolean { + var l = 1L + var r = num.toLong() + while (l <= r) { + val m = (l + r) / 2 + if (m * m > num) + r = m - 1 + else if (m * m < num) + l = m + 1 + else + return true + } + return false + } +} diff --git a/kotlin/0368-largest-divisible-subset.kt b/kotlin/0368-largest-divisible-subset.kt new file mode 100644 index 000000000..9f9fed0f2 --- /dev/null +++ b/kotlin/0368-largest-divisible-subset.kt @@ -0,0 +1,59 @@ +// DP top-down tabulation +class Solution { + fun largestDivisibleSubset(nums: IntArray): List { + val n = nums.size + nums.sort() + val dp = nums + .map { listOf(it) } + .toTypedArray() + var res = listOf() + + for (i in n - 1 downTo 0) { + for (j in i + 1 until n) { + if (nums[j] % nums[i] == 0) { + val temp = listOf(nums[i]) + dp[j] + dp[i] = if (temp.size > dp[i].size) temp else dp[i] + } + } + + res = if (dp[i].size > res.size) dp[i] else res + } + + return res + } +} + +// recursion + memoization +class Solution { + fun largestDivisibleSubset(nums: IntArray): List { + val n = nums.size + nums.sort() + val dp = HashMap>() + + fun dfs(i: Int): List { + if (i == n) return listOf() + dp[i]?.let { return it } + + var res = listOf(nums[i]) + for (j in i + 1 until n) { + if (nums[j] % nums[i] == 0) { + val temp = listOf(nums[i]) + dfs(j) + if (temp.size > res.size) + res = temp + } + } + + dp[i] = res + return res + } + + var res = listOf() + for (i in nums.indices) { + val temp = dfs(i) + if (temp.size > res.size) + res = temp + } + + return res + } +} diff --git a/kotlin/0371-sum-of-two-integers.kt b/kotlin/0371-sum-of-two-integers.kt new file mode 100644 index 000000000..344fb459f --- /dev/null +++ b/kotlin/0371-sum-of-two-integers.kt @@ -0,0 +1,13 @@ +class Solution { + fun getSum(a: Int, b: Int): Int { + var a1 = a + var b1 = b + var carry = a1.and(b1) + while (carry != 0) { + b1 = a1.xor(b1) + a1 = carry.shl(1) + carry = a1.and(b1) + } + return a1.xor(b1) + } +} \ No newline at end of file diff --git a/kotlin/0374-guess-number-higher-or-lower.kt b/kotlin/0374-guess-number-higher-or-lower.kt new file mode 100644 index 000000000..497a7cd41 --- /dev/null +++ b/kotlin/0374-guess-number-higher-or-lower.kt @@ -0,0 +1,22 @@ +/** + * The API guess is defined in the parent class. + * @param num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * fun guess(num:Int):Int {} + */ + +class Solution:GuessGame() { + override fun guessNumber(n:Int):Int { + var l = 1 + var r = n + while ( true ) { + val mid = l + (r - l) / 2 + val res = guess(mid) + if(res == 0) return mid + if(res > 0) l = mid + 1 + if(res < 0) r = mid - 1 + } + } +} diff --git a/kotlin/0377-combination-sum-iv.kt b/kotlin/0377-combination-sum-iv.kt new file mode 100644 index 000000000..d6e247651 --- /dev/null +++ b/kotlin/0377-combination-sum-iv.kt @@ -0,0 +1,38 @@ +//dp solution +class Solution { + fun combinationSum4(nums: IntArray, target: Int): Int { + val dp = IntArray(target + 1) + + dp[0] = 1 + for (i in 1..target) { + for (n in nums) { + if(i - n >= 0) dp[i] += dp[i - n] + } + } + + return dp[target] + } +} + +//recursion + memoization solution +class Solution { + fun combinationSum4(nums: IntArray, target: Int): Int { + val dp = HashMap() + + fun dfs(sum: Int): Int { + if (sum == target) return 1 + if (sum in dp) return dp[sum]!! + + var res = 0 + for (num in nums) { + if (sum + num <= target) + res += dfs(sum + num) + } + + dp[sum] = res + return res + } + + return dfs(0) + } +} diff --git a/kotlin/0380-insert-delete-getrandom-o1.kt b/kotlin/0380-insert-delete-getrandom-o1.kt new file mode 100644 index 000000000..853b23626 --- /dev/null +++ b/kotlin/0380-insert-delete-getrandom-o1.kt @@ -0,0 +1,53 @@ +/* +* Containing the logic behind the operations, but a cleaner Kotlin solution is provided below. +* Here implement some of the logic of the functions ourselves, as in the video, to have this solution compatible with the video solution. +*/ +class RandomizedSet() { + + val hs = HashSet() + + fun insert(`val`: Int): Boolean { + if(hs.contains(`val`)) + return false + else { + hs.add(`val`) + return true + } + } + + fun remove(`val`: Int): Boolean { + if(hs.contains(`val`)){ + hs.remove(`val`) + return true + }else + return false + } + + fun getRandom(): Int { + return hs.random() + } + +} + +/* +* Cleaner Kotlin solution. add() and remove() +*/ +class RandomizedSet() { + + val hs = HashSet() + + fun insert(`val`: Int) = hs.add(`val`) + + fun remove(`val`: Int) = hs.remove(`val`) + + fun getRandom() = hs.random() + +} + +/** + * Your RandomizedSet object will be instantiated and called as such: + * var obj = RandomizedSet() + * var param_1 = obj.insert(`val`) + * var param_2 = obj.remove(`val`) + * var param_3 = obj.getRandom() + */ diff --git a/kotlin/0387-first-unique-character-in-a-string.kt b/kotlin/0387-first-unique-character-in-a-string.kt new file mode 100644 index 000000000..3ba52e9e0 --- /dev/null +++ b/kotlin/0387-first-unique-character-in-a-string.kt @@ -0,0 +1,14 @@ +class Solution { + fun firstUniqChar(s: String): Int { + var count = IntArray (26) + for (c in s) + count[c - 'a']++ + + for ((i, c) in s.withIndex()) { + if (count[c - 'a'] == 1) + return i + } + + return -1 + } +} diff --git a/kotlin/0389-find-the-difference.kt b/kotlin/0389-find-the-difference.kt new file mode 100644 index 000000000..941090623 --- /dev/null +++ b/kotlin/0389-find-the-difference.kt @@ -0,0 +1,19 @@ +// same as below, but using xor +class Solution { + fun findTheDifference(s: String, t: String): Char { + var res = 0 + for (c in s) res = res xor c.toInt() + for (c in t) res = res xor c.toInt() + return res.toChar() + } +} + +// using sums difference +class Solution { + fun findTheDifference(s: String, t: String): Char { + var sum = 0 + for (c in t) sum += c.toInt() + for (c in s) sum -= c.toInt() + return sum.toChar() + } +} diff --git a/kotlin/0392-is-subsequence.kt b/kotlin/0392-is-subsequence.kt new file mode 100644 index 000000000..0297413a4 --- /dev/null +++ b/kotlin/0392-is-subsequence.kt @@ -0,0 +1,11 @@ +class Solution { + fun isSubsequence(s: String, t: String): Boolean { + var sIndex = 0 + var tIndex = 0 + while(sIndex < s.length && tIndex < t.length){ + if(s[sIndex] == t[tIndex]) sIndex++ + tIndex++ + } + return sIndex == s.length + } +} diff --git a/kotlin/0394-decode-string.kt b/kotlin/0394-decode-string.kt new file mode 100644 index 000000000..f6c38f035 --- /dev/null +++ b/kotlin/0394-decode-string.kt @@ -0,0 +1,25 @@ +class Solution { + fun decodeString(s: String): String { + val stack = LinkedList() + + for (c in s) { + if (c != ']') { + stack.addLast(c.toString()) + } else { + val sb = StringBuilder() + while (stack.isNotEmpty() && stack.peekLast() != "[") + sb.insert(0, stack.removeLast()) + stack.removeLast() + + val k = StringBuilder() + while (stack.isNotEmpty() && stack.peekLast().all { char -> char.isDigit() }) + k.insert(0, stack.removeLast()) + + val times = k.toString().toInt() + stack.addLast(sb.toString().repeat(times)) + } + } + + return stack.joinToString("") + } +} diff --git a/kotlin/0399-evaluate-division.kt b/kotlin/0399-evaluate-division.kt new file mode 100644 index 000000000..1dfb2a6e5 --- /dev/null +++ b/kotlin/0399-evaluate-division.kt @@ -0,0 +1,50 @@ +class Solution { + fun calcEquation(equations: List>, values: DoubleArray, queries: List>): DoubleArray { + val adj = HashMap>>().apply { + for (i in equations.indices) { + val (a, b) = equations[i] + val value = values[i] + this[a] = getOrDefault(a, arrayListOf()).apply { add(b to value) } + this[b] = getOrDefault(b, arrayListOf()).apply { add(a to (1.0 / value)) } + } + } + + fun bfs( + start: String, + end: String + ): Double { + if (start !in adj || end !in adj) + return -1.0 + + val visit = HashSet() + with (LinkedList>()) { + addLast(start to 1.0) + visit.add(start) + + while (isNotEmpty()) { + val (cur, totVal) = removeFirst() + + if (cur == end) return totVal + + adj[cur]?.forEach { + val (next, nextVal) = it + if (next !in visit) { + addLast(next to (totVal * nextVal)) + visit.add(next) + } + } + } + } + + return -1.0 + } + + val res = DoubleArray(queries.size) + for (i in queries.indices) { + val (a, b) = queries[i] + res[i] = bfs(a, b) + } + + return res + } +} diff --git a/kotlin/0402-remove-k-digits.kt b/kotlin/0402-remove-k-digits.kt new file mode 100644 index 000000000..8b49d42d0 --- /dev/null +++ b/kotlin/0402-remove-k-digits.kt @@ -0,0 +1,18 @@ +class Solution { + fun removeKdigits(num: String, k: Int): String { + val stack = Stack() //monotonic stack + var count = k + for(i in 0 until num.length){ + while(count > 0 && !stack.isEmpty() && num[i] < stack.peek()){ + stack.pop() //remove the larger int + count-- + } + stack.push(num[i]) + } + repeat(count){ //pop the last k integers (largest integers) + stack.pop() + } + val sum = stack.joinToString("") + return if(sum == "") "0" else sum.toBigInteger().toString() //to int to remove all leading Zeros, instead of using Strinbuilder + } +} diff --git a/kotlin/0410-split-array-largest-sum.kt b/kotlin/0410-split-array-largest-sum.kt new file mode 100644 index 000000000..d86aaca05 --- /dev/null +++ b/kotlin/0410-split-array-largest-sum.kt @@ -0,0 +1,32 @@ +class Solution { + fun splitArray(nums: IntArray, k: Int): Int { + + fun canSplit(max: Int): Boolean { + var subArrCnt = 1 + var curSum = 0 + for (n in nums) { + curSum += n + if (curSum > max) { + subArrCnt++ + curSum = n + } + } + return subArrCnt <= k + } + + var l = nums.max()!! + var r = nums.sum()!! + var res = r + while (l <= r) { + val m = l + (r - l) / 2 + if (canSplit(m)) { + res = m + r = m - 1 + } else { + l = m + 1 + } + } + + return res + } +} diff --git a/kotlin/0416-partition-equal-subset-sum.kt b/kotlin/0416-partition-equal-subset-sum.kt new file mode 100644 index 000000000..6c7fc0680 --- /dev/null +++ b/kotlin/0416-partition-equal-subset-sum.kt @@ -0,0 +1,23 @@ +class Solution { + fun canPartition(nums: IntArray): Boolean { + + var sum = nums.sum() + if(sum%2 != 0) return false // we cant divive an odd sum into 2 equal parts + + val dp = BooleanArray(sum+1) + sum /= 2 + dp[0] = true + + for(num in nums){ + if(num > sum) + return false + for(i in sum downTo 0){ + if(i >= num) + dp[i] = dp[i] || dp[i-num] + } + } + + return dp[sum] + } + +} diff --git a/kotlin/0417-pacific-atlantic-water-flow.kt b/kotlin/0417-pacific-atlantic-water-flow.kt new file mode 100644 index 000000000..3bf351a9f --- /dev/null +++ b/kotlin/0417-pacific-atlantic-water-flow.kt @@ -0,0 +1,43 @@ +class Solution { + + val dirs = arrayOf(intArrayOf(0,1), + intArrayOf(0,-1), + intArrayOf(1,0), + intArrayOf(-1,0)) + + fun pacificAtlantic(grid: Array): List> { + if(grid.size == 0) return emptyList() + val result = mutableListOf>() + + val pacific = Array(grid.size) { BooleanArray(grid[0].size) } + val atlantic = Array(grid.size) { BooleanArray(grid[0].size) } + + for(i in 0 until grid.size) { + dfs(grid, Integer.MIN_VALUE, i, 0, pacific) + dfs(grid, Integer.MIN_VALUE, i, grid[0].size - 1, atlantic) + } + + for(i in 0 until grid[0].size) { + dfs(grid, Integer.MIN_VALUE, 0, i, pacific) + dfs(grid, Integer.MIN_VALUE, grid.size - 1, i, atlantic) + } + + for(i in 0 until grid.size) { + for(j in 0 until grid[0].size) { + if(atlantic[i][j] && pacific[i][j]) result.add(listOf(i, j)) + } + } + + return result + } + + private fun dfs(grid: Array, height: Int, i: Int, j: Int, visited: Array) { + if(i < 0 || i >= grid.size || j < 0 || j >= grid[0].size || grid[i][j] < height || visited[i][j]) return + + visited[i][j] = true + + dirs.forEach { dir -> + dfs(grid, grid[i][j], dir[0] + i, dir[1] + j, visited) + } + } +} \ No newline at end of file diff --git a/kotlin/0424-longest-repeating-character-replacement.kt b/kotlin/0424-longest-repeating-character-replacement.kt new file mode 100644 index 000000000..8e950e594 --- /dev/null +++ b/kotlin/0424-longest-repeating-character-replacement.kt @@ -0,0 +1,23 @@ +class Solution { + fun characterReplacement(s: String, k: Int): Int { + val freq = IntArray(26); + + var res = 0 + var currMax = 0 + var start = 0 + + for (end in 0..s.length-1) { + val count = ++freq[s[end] - 'A'] + currMax = Math.max(currMax, count) + + if (end - start + 1 > currMax + k) { + freq[s[start] - 'A']-- + start++ + } + + res = Math.max(res, end - start + 1) + } + + return res + } +} \ No newline at end of file diff --git a/kotlin/0427-construct-quad-tree.kt b/kotlin/0427-construct-quad-tree.kt new file mode 100644 index 000000000..b45fd76ce --- /dev/null +++ b/kotlin/0427-construct-quad-tree.kt @@ -0,0 +1,44 @@ +/** + * Definition for a QuadTree node. + * class Node(var `val`: Boolean, var isLeaf: Boolean) { + * var topLeft: Node? = null + * var topRight: Node? = null + * var bottomLeft: Node? = null + * var bottomRight: Node? = null + * } + */ + +class Solution { + fun construct(grid: Array): Node? { + + fun dfs(n: Int, r: Int, c: Int): Node? { + var allSame = true + + for(i in 0 until n) { + for(j in 0 until n) { + if(grid[r][c] != grid[r + i][c + j]) { + allSame = false + break + } + } + } + + if(allSame) return Node(if(grid[r][c] == 1) true else false, true) + + val nextN = n/2 + val topLeft = dfs(nextN, r, c) + val topRight = dfs(nextN, r, c + nextN) + val bottomLeft = dfs(nextN, r + nextN, c) + val bottomRight = dfs(nextN, r + nextN, c + nextN) + + val node = Node(false, false) + node.topLeft = topLeft + node.topRight = topRight + node.bottomLeft = bottomLeft + node.bottomRight = bottomRight + return node + } + + return dfs(grid.size, 0, 0) + } +} diff --git a/kotlin/0435-non-overlapping-intervals.kt b/kotlin/0435-non-overlapping-intervals.kt new file mode 100644 index 000000000..4bfa22a35 --- /dev/null +++ b/kotlin/0435-non-overlapping-intervals.kt @@ -0,0 +1,16 @@ +class Solution { + fun eraseOverlapIntervals(intervals: Array): Int { + val sorted = intervals.sortedBy { it[1] } + + var res = 0 + var end = -50000 + for ((s, e) in sorted) { + if (s >= end) + end = e + else + res++ + } + + return res + } +} diff --git a/kotlin/0438-find-all-anagrams-in-a-string.kt b/kotlin/0438-find-all-anagrams-in-a-string.kt new file mode 100644 index 000000000..b053ee7f0 --- /dev/null +++ b/kotlin/0438-find-all-anagrams-in-a-string.kt @@ -0,0 +1,31 @@ +class Solution { + fun findAnagrams(s: String, p: String): List { + + val pCount = IntArray(26) + val res = ArrayList() + + for(c in p) + pCount[c - 'a']++ + + var start = 0 + var end = 0 + + while(end < s.length){ + // increase the window + if(pCount[s[end] - 'a'] > 0){ + pCount[s[end++] - 'a']-- + if(end-start == p.length) + res.add(start) + // window size 0? step to next + }else if(start == end){ + start++ + end++ + //decrease the window + }else{ + pCount[s[start++] - 'a']++ + } + } + + return res + } +} diff --git a/kotlin/0441-arranging-coins.kt b/kotlin/0441-arranging-coins.kt new file mode 100644 index 000000000..e50430679 --- /dev/null +++ b/kotlin/0441-arranging-coins.kt @@ -0,0 +1,63 @@ +/* +* Optimized with Binary Search and Gauss summation formula: Time Complexity O(LogN) and Space Complexity O(1) +*/ +class Solution { + fun arrangeCoins(n: Int): Int { + + var left = 1 + var right = n + var res = 0 + + while (left <= right) { + val mid = left + (right - left) / 2 //avoid a potential 32bit Integer overflow (Where left is 1 and right is Integer.MAX_VALUE) + val coins = (mid.toDouble() / 2) * (mid.toDouble() + 1) + if(coins > n) + right = mid -1 + else { + left = mid + 1 + res = maxOf(res, mid) + } + } + + return res.toInt() + } +} + +/* +* Optimized and almost cheat-like solution (Technically not cheating) with both space and time complexity being O(1) +* Here we solve for x in gauss summation formula where the result is our input variable n: +* (x/2) * (x+1) = n ====> x = 1/2 * sqrt( 8n+1 ) -1 (We ignore the other possible solution since it gives us (wrong) negative x's +*/ +class Solution { + fun arrangeCoins(n: Int): Int { + val x = 0.5 * Math.sqrt(8.0 * n.toDouble() + 1.0) - 1.0 + return Math.round(x).toInt() // round() will round up or down to nearest whole number, which we need to correctly output result + } +} + +// Or if you prefer oneliner: +class Solution { + fun arrangeCoins(n: Int) = Math.round( 0.5 * Math.sqrt(8.0 * n.toDouble() + 1.0) - 1.0 ).toInt() +} + + +/* +* Naive way with brute force: Time Complexity O(N) and Space Complexity O(1) +*/ +class Solution { + fun arrangeCoins(_n: Int): Int { + + var res = 0 + var n = _n + var step = 0 + + while( true ) { + step++ + if(n - step < 0) return res + n -= step + res++ + } + + return res + } +} diff --git a/kotlin/0442-find-all-duplicates-in-an-array.kt b/kotlin/0442-find-all-duplicates-in-an-array.kt new file mode 100644 index 000000000..3891d3c54 --- /dev/null +++ b/kotlin/0442-find-all-duplicates-in-an-array.kt @@ -0,0 +1,13 @@ +class Solution { + fun findDuplicates(nums: IntArray): List { + val res = mutableListOf () + + for (n in nums) { + val n = if (n < 0) -n else n + if (nums[n - 1] < 0) res.add(n) + nums[n - 1] *= -1 + } + + return res + } +} diff --git a/kotlin/0446-arithmetic-slices-ii-subsequence.kt b/kotlin/0446-arithmetic-slices-ii-subsequence.kt new file mode 100644 index 000000000..5d98526b1 --- /dev/null +++ b/kotlin/0446-arithmetic-slices-ii-subsequence.kt @@ -0,0 +1,49 @@ +// dp +class Solution { + fun numberOfArithmeticSlices(nums: IntArray): Int { + var res = 0 + val dp = HashMap, Int>() + + for (i in 0 until nums.size) { + for (j in 0 until i) { + val d = nums[i].toLong() - nums[j] + dp[i to d] = (dp[i to d] ?: 0) + 1 + (dp[j to d] ?: 0) + res += (dp[j to d] ?: 0) + } + } + + return res + } +} + +// recursion + memoization +class Solution { + fun numberOfArithmeticSlices(nums: IntArray): Int { + val dp = HashMap() + val count = HashMap>() + + nums.forEachIndexed { i, n -> + count.getOrPut(n.toLong()) { mutableListOf() }.apply { add(i) } + } + + fun dfs(i: Int, d: Long, s: Int): Int { + dp["$i:$d:$s"]?.let { return it } + + var res = if (s > 2) 1 else 0 + count[nums[i] + d]?.forEach { j -> + if (j > i) res += dfs(j, d, s + 1) + } + + dp["$i:$d:$s"] = res + return res + } + + var res = 0 + for (i in 0 until nums.size) { + for (j in i + 1 until nums.size) + res += dfs(j, nums[j].toLong() - nums[i], 2) + } + + return res + } +} diff --git a/kotlin/0448-find-all-numbers-disappeared-in-an-array.kt b/kotlin/0448-find-all-numbers-disappeared-in-an-array.kt new file mode 100644 index 000000000..2820d286c --- /dev/null +++ b/kotlin/0448-find-all-numbers-disappeared-in-an-array.kt @@ -0,0 +1,13 @@ +class Solution { + fun findDisappearedNumbers(nums: IntArray): List { + val res = ArrayList() + for(i in nums.indices){ + val num = Math.abs(nums[i]) + nums[num-1] = Math.abs(nums[num-1]) * -1 + } + for((i,v) in nums.withIndex()){ + if(v > 0) res.add(i+1) + } + return res + } +} diff --git a/kotlin/0450-delete-node-in-a-bst.kt b/kotlin/0450-delete-node-in-a-bst.kt new file mode 100644 index 000000000..1f10bd478 --- /dev/null +++ b/kotlin/0450-delete-node-in-a-bst.kt @@ -0,0 +1,36 @@ +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ + +//recursive solution +class Solution { + fun deleteNode(root: TreeNode?, key: Int): TreeNode? { + + root?: return null + + if (root.value < key) { + root.right = deleteNode(root.right, key) + } else if (root.value > key) { + root.left = deleteNode(root.left, key) + } else { + if (root.right == null) return root.left + if (root.left == null) return root.right + + var current = root.right + while (current.left != null) current = current.left + root.`val` = current.value + root.right = deleteNode(root.right, root.value) + } + + return root + } + + private val TreeNode.value get() = `val` +} diff --git a/kotlin/0451-sort-characters-by-frequency.kt b/kotlin/0451-sort-characters-by-frequency.kt new file mode 100644 index 000000000..70d305773 --- /dev/null +++ b/kotlin/0451-sort-characters-by-frequency.kt @@ -0,0 +1,47 @@ +// Count and bucketsort by freq +class Solution { + fun frequencySort(s: String): String { + val counts = s + .groupingBy { it } + .eachCount() + + val buckets = HashMap>() + for ((char, count) in counts) + buckets.getOrPut(count) { mutableListOf() }.apply { add(char) } + + val res = StringBuilder() + for (count in s.length downTo 1) { + buckets[count]?.forEach { char -> + repeat (count) { + res.append(char) + } + } + } + + return res.toString() + } +} + +// Count and sort +class Solution { + fun frequencySort(s: String): String { + val counts = IntArray (128) + + for (c in s) + counts[c.toInt()]++ + + val sortedCounts = counts + .mapIndexed { i, v -> i to v } + .filter { it.second > 0 } + .sortedWith(compareBy({ -it.second },{ it.first })) + + val res = StringBuilder () + for ((char, count) in sortedCounts) { + repeat (count) { + res.append(char.toChar()) + } + } + + return res.toString() + } +} diff --git a/kotlin/0452-minimum-number-of-arrows-to-burst-balloons.kt b/kotlin/0452-minimum-number-of-arrows-to-burst-balloons.kt new file mode 100644 index 000000000..7192184ed --- /dev/null +++ b/kotlin/0452-minimum-number-of-arrows-to-burst-balloons.kt @@ -0,0 +1,20 @@ +class Solution { + fun findMinArrowShots(points: Array): Int { + points.sortWith(compareBy({ it[0] }, { it[1] })) + + var res = points.size + var prev = points[0] + for (i in 1 until points.size) { + val curr = points[i] + if (curr[0] <= prev[1]) { + res-- + prev[0] = curr[0] + prev[1] = minOf(curr[1], prev[1]) + } else { + prev = curr + } + } + + return res + } +} diff --git a/kotlin/0455-assign-cookies.kt b/kotlin/0455-assign-cookies.kt new file mode 100644 index 000000000..cab853b5d --- /dev/null +++ b/kotlin/0455-assign-cookies.kt @@ -0,0 +1,17 @@ +class Solution { + fun findContentChildren(g: IntArray, s: IntArray): Int { + g.sort() + s.sort() + + var i = 0 + var j = 0 + while (i < g.size) { + while (j < s.size && g[i] > s[j]) j++ + if (j == s.size) break + i++ + j++ + } + + return i + } +} diff --git a/kotlin/0456-132-pattern.kt b/kotlin/0456-132-pattern.kt new file mode 100644 index 000000000..e0fbb4a58 --- /dev/null +++ b/kotlin/0456-132-pattern.kt @@ -0,0 +1,17 @@ +class Solution { + fun find132pattern(nums: IntArray): Boolean { + val stack = LinkedList>() + var min = nums[0] + + for (i in 1 until nums.size) { + while (stack.isNotEmpty() && stack.peekLast().first <= nums[i]) + stack.removeLast() + if (stack.isNotEmpty() && stack.peekLast().second < nums[i]) + return true + stack.addLast(nums[i] to min) + min = minOf(min, nums[i]) + } + + return false + } +} diff --git a/kotlin/0460-lfu-cache.kt b/kotlin/0460-lfu-cache.kt new file mode 100644 index 000000000..21edef92c --- /dev/null +++ b/kotlin/0460-lfu-cache.kt @@ -0,0 +1,87 @@ +class LFUCache(val capacity: Int) { + var lfuCnt = 0 + val valueMap = HashMap() + val countMap = HashMap() + val listMap = HashMap() + + fun counter(key: Int) { + val cnt = countMap.getOrPut(key) { 0 } + countMap[key] = countMap.getOrDefault(key, 0) + 1 + listMap.getOrPut(cnt) { LinkedList() }.apply { pop(key) } + listMap.getOrPut(cnt + 1) { LinkedList() }.apply { pushRight(key) } + + if (cnt == lfuCnt && (listMap[cnt]?.length() ?: 0) == 0) + lfuCnt++ + } + + fun get(key: Int): Int { + valueMap[key]?.let { counter(key) } + return valueMap[key] ?: -1 + } + + fun put(key: Int, value: Int) { + if (capacity == 0) return + + if (key !in valueMap && valueMap.size == capacity) { + listMap[lfuCnt]?.let { + val toDel = it.popLeft() + valueMap.remove(toDel) + countMap.remove(toDel) + } + } + + valueMap[key] = value + counter(key) + lfuCnt = minOf(lfuCnt, (countMap[key] ?: lfuCnt)) + } + +} + +class LinkedList { + + val left = ListNode(0) + var right = ListNode(0) + val map = HashMap() + + init { + right.prev = left + left.next = right + } + + fun length() = map.size + + fun pushRight(value: Int) { + val node = ListNode(value, right.prev, right) + map[value] = node + right.prev = node + node.prev?.next = node + } + + fun pop(value: Int) { + if (value in map) { + val node = map[value] + val next = node?.next + val prev = node?.prev + next?.prev = prev + prev?.next = next + map.remove(value) + } + } + + fun popLeft(): Int { + val res = left.next?.value + res?.let { pop(it) } + return res ?: -1 + } + + fun update(value: Int) { + pop(value) + pushRight(value) + } + + class ListNode( + var value: Int, + var prev: ListNode? = null, + var next: ListNode? = null + ) +} diff --git a/kotlin/0463-island-perimeter.kt b/kotlin/0463-island-perimeter.kt new file mode 100644 index 000000000..d415b2476 --- /dev/null +++ b/kotlin/0463-island-perimeter.kt @@ -0,0 +1,19 @@ +class Solution { + fun islandPerimeter(grid: Array): Int { + if(grid.size == 0 || grid[0].size == 0) + return 0 + var res = 0 + for(i in 0..grid.size-1){ + for(j in 0..grid[0].size-1){ + if(grid[i][j]==1){ + res += 4 + if(i > 0 && grid[i-1][j]==1) + res -= 2 + if(j > 0 && grid[i][j-1]==1) + res -= 2 + } + } + } + return res + } +} diff --git a/kotlin/0472-concatenated-words.kt b/kotlin/0472-concatenated-words.kt new file mode 100644 index 000000000..5cb927167 --- /dev/null +++ b/kotlin/0472-concatenated-words.kt @@ -0,0 +1,31 @@ +class Solution { + fun findAllConcatenatedWordsInADict(words: Array): List { + val wordSet = words.toSet() + val dp = HashMap() + + fun dfs(word: String): Boolean { + if (word in dp) return dp[word]!! + for (i in 1 until word.length) { + val prefix = word.substring(0, i) + val suffix = word.substring(i, word.length) + if ((prefix in wordSet && suffix in wordSet) || + prefix in wordSet && dfs(suffix)) { + dp[word] = true + return true + } + + } + + dp[word] = false + return false + } + + var res = mutableListOf() + for (word in words) { + if (dfs(word)) + res.add(word) + } + + return res + } +} diff --git a/kotlin/0473-matchsticks-to-square.kt b/kotlin/0473-matchsticks-to-square.kt new file mode 100644 index 000000000..e65da2cda --- /dev/null +++ b/kotlin/0473-matchsticks-to-square.kt @@ -0,0 +1,27 @@ +class Solution { + fun makesquare(matchsticks: IntArray): Boolean { + val length = matchsticks.sum()!! / 4 + val sides = IntArray (4) + + if (matchsticks.sum()!! / 4 != length) return false + + matchsticks.sortDescending() + + fun backtrack(i: Int): Boolean { + if (i == matchsticks.size) return true + + for (j in 0..3) { + if (sides[j] + matchsticks[i] <= length) { + sides[j] += matchsticks[i] + if (backtrack(i + 1)) + return true + sides[j] -= matchsticks[i] + } + } + + return false + } + + return backtrack(0) + } +} diff --git a/kotlin/0474-ones-and-zeroes.kt b/kotlin/0474-ones-and-zeroes.kt new file mode 100644 index 000000000..64097f7f1 --- /dev/null +++ b/kotlin/0474-ones-and-zeroes.kt @@ -0,0 +1,53 @@ +/* +* Recursion with memoization solution +*/ +class Solution { + fun findMaxForm(strs: Array, m: Int, n: Int): Int { + val dp = Array(m + 1){ Array(n + 1){ IntArray(strs.size){ -1 } } } + + fun dfs(i: Int, m: Int, n: Int): Int { + if(i == strs.size) return 0 + + if(dp[m][n][i] != -1) return dp[m][n][i] + + val zeros = strs[i].count{ it == '0' } + val ones = strs[i].count{ it == '1' } + + dp[m][n][i] = dfs(i + 1, m, n) + if(zeros <= m && ones <= n) { + dp[m][n][i] = maxOf( + dp[m][n][i], + 1 + dfs(i + 1, m - zeros, n - ones) + ) + } + + return dp[m][n][i] + } + + return dfs(0, m, n) + } +} + +/* +* DP solution +*/ +class Solution { + fun findMaxForm(strs: Array, m: Int, n: Int): Int { + val dp = Array(m + 1){ IntArray(n + 1) } + + for(str in strs) { + val zeros = str.count{ it == '0'} + val ones = str.count{ it == '1'} + for(i in m downTo zeros) { + for(j in n downTo ones) { + dp[i][j] = maxOf( + 1 + dp[i - zeros][j - ones], + dp[i][j] + ) + } + } + } + + return dp[m][n] + } +} diff --git a/kotlin/0494-target-sum.kt b/kotlin/0494-target-sum.kt new file mode 100644 index 000000000..ad852d807 --- /dev/null +++ b/kotlin/0494-target-sum.kt @@ -0,0 +1,18 @@ +class Solution { + fun findTargetSumWays(nums: IntArray, target: Int): Int { + val memo = HashMap() + + fun dfs(i: Int, sum: Int): Int { + if (i == nums.size) + return if (sum == target) 1 else 0 + val key = "$i:$sum" + if (key in memo) + return memo[key]!! + memo[key] = dfs(i + 1, sum + nums[i]) + + dfs(i + 1, sum - nums[i]) + return memo[key]!! + } + + return dfs(0, 0) + } +} diff --git a/kotlin/0496-next-greater-element-i.kt b/kotlin/0496-next-greater-element-i.kt new file mode 100644 index 000000000..7884e7d1c --- /dev/null +++ b/kotlin/0496-next-greater-element-i.kt @@ -0,0 +1,22 @@ +class Solution { + fun nextGreaterElement(nums1: IntArray, nums2: IntArray): IntArray { + val res = IntArray(nums1.size){ -1 } + + val hm = HashMap() + for(i in 0 until nums1.size) + hm[nums1[i]] = i + + val stack = ArrayDeque() + for(current in nums2){ + while(stack.isNotEmpty() && current > stack.getLast()){ + val element = stack.removeLast() + val index = hm[element]!! + res[index] = current + } + if(current in hm) + stack.addLast(current) + } + + return res + } +} diff --git a/kotlin/0502-ipo.kt b/kotlin/0502-ipo.kt new file mode 100644 index 000000000..534564579 --- /dev/null +++ b/kotlin/0502-ipo.kt @@ -0,0 +1,24 @@ +class Solution { + fun findMaximizedCapital(k: Int, w: Int, profits: IntArray, capital: IntArray): Int { + + var totalCap = w + // Pair of ("Profit" to "CapitalCost") + val minCapital = PriorityQueue> { a,b -> a.second - b.second } + val maxProfit = PriorityQueue { a,b -> b - a } + + profits.zip(capital).forEach { + minCapital.add(it.first to it.second) + } + + repeat(k) exit@ { + while(minCapital.isNotEmpty() && minCapital.peek().second <= totalCap) { + val (prof, cap) = minCapital.poll() + maxProfit.add(prof) + } + if(maxProfit.isEmpty()) return@exit + totalCap += maxProfit.poll() + } + + return totalCap + } +} diff --git a/kotlin/0513-find-bottom-left-tree-value.kt b/kotlin/0513-find-bottom-left-tree-value.kt new file mode 100644 index 000000000..447bde10a --- /dev/null +++ b/kotlin/0513-find-bottom-left-tree-value.kt @@ -0,0 +1,14 @@ +class Solution { + fun findBottomLeftValue(root: TreeNode?): Int { + var cur = root + with (LinkedList()) { + addLast(root) + while (isNotEmpty()) { + cur = removeFirst() + cur?.right?.let { addLast(it) } + cur?.left?.let { addLast(it) } + } + } + return cur?.`val` ?: 0 + } +} diff --git a/kotlin/0515-find-largest-value-in-each-tree-row.kt b/kotlin/0515-find-largest-value-in-each-tree-row.kt new file mode 100644 index 000000000..077499917 --- /dev/null +++ b/kotlin/0515-find-largest-value-in-each-tree-row.kt @@ -0,0 +1,53 @@ +// bfs +class Solution { + fun largestValues(root: TreeNode?): List { + val res = mutableListOf() + root?: return res + + with (LinkedList()) { + addLast(root) + + while (isNotEmpty()) { + var levelMax = peekLast()!!.`val` + + repeat (size) { + val cur = removeFirst() + levelMax = maxOf(levelMax, cur!!.`val`) + cur?.left?.let { addLast(it) } + cur?.right?.let { addLast(it) } + } + + res.add(levelMax) + } + } + + return res + } +} + +// dfs +class Solution { + fun largestValues(root: TreeNode?): List { + root?: return listOf() + + val maxes = HashMap() + + fun dfs(cur: TreeNode?, level: Int) { + cur?: return + + maxes[level] = maxOf( + cur?.value ?: Integer.MIN_VALUE, + maxes.getOrDefault(level, Integer.MIN_VALUE) + ) + + dfs(cur?.left, level + 1) + dfs(cur?.right, level + 1) + } + + dfs(root, 0) + return maxes.values.toList() + } + + val TreeNode.value + get()= this.`val` +} diff --git a/kotlin/0516-longest-palindromic-subsequence.kt b/kotlin/0516-longest-palindromic-subsequence.kt new file mode 100644 index 000000000..0b8d66a27 --- /dev/null +++ b/kotlin/0516-longest-palindromic-subsequence.kt @@ -0,0 +1,19 @@ +class Solution { + fun longestPalindromeSubseq(s1: String): Int { + val s2 = s1.reversed() + val n = s1.length + val m = s2.length + val dp = Array(n+1){ IntArray(m+1) } + + for (i in 0 until n) { + for (j in 0 until m) { + if(s1[i] == s2[j]) + dp[i+1][j+1] = 1 + dp[i][j] + else + dp[i+1][j+1] = maxOf(dp[i][j+1], dp[i+1][j]) + } + } + + return dp[n][m] + } +} diff --git a/kotlin/0518-coin-change-ii.kt b/kotlin/0518-coin-change-ii.kt new file mode 100644 index 000000000..a729628ae --- /dev/null +++ b/kotlin/0518-coin-change-ii.kt @@ -0,0 +1,68 @@ +/* +* DP with O(n) memory +*/ +class Solution { + fun change(amount: Int, coins: IntArray): Int { + val dp = IntArray(amount + 1) + + dp[0] = 1 + for (coin in coins) { + for (i in 1..amount) { + if (i - coin >= 0) + dp[i] += dp[i - coin] + } + } + + return dp[amount] + } +} + +/* +* DP with O(n^2) memory +*/ +class Solution { + fun change(amount: Int, coins: IntArray): Int { + val dp = Array(coins.size) { IntArray(amount + 1) } + + for (i in 0 until coins.size) + dp[i][0] = 1 + + for (i in 0..coins.lastIndex) { + for (j in 1..amount) { + if (i > 0) { + dp[i][j] += dp[i - 1][j] + } + if (j - coins[i] >= 0) + dp[i][j] += dp[i][j - coins[i]] + } + } + + return dp[coins.lastIndex][amount] + } +} + +/* +* DFS + Memo +*/ +class Solution { + fun change(amount: Int, coins: IntArray): Int { + val cache = Array(coins.size) { IntArray(amount + 1) {-1} } + + fun dfs(i: Int, a: Int): Int { + if (a == amount) + return 1 + if (a > amount) + return 0 + if (i == coins.size) + return 0 + if (cache[i][a] != -1) + return cache[i][a] + + cache[i][a] = dfs(i, a + coins[i]) + dfs(i + 1, a) + + return cache[i][a] + } + + return dfs(0, 0) + } +} diff --git a/kotlin/0523-continuous-subarray-sum.kt b/kotlin/0523-continuous-subarray-sum.kt new file mode 100644 index 000000000..5cc5b6c09 --- /dev/null +++ b/kotlin/0523-continuous-subarray-sum.kt @@ -0,0 +1,13 @@ +class Solution { + fun checkSubarraySum(nums: IntArray, k: Int): Boolean { + val hm = hashMapOf(0 to -1) + var prefix = 0 + nums.forEachIndexed {i, num -> + prefix += num + val remainder = prefix % k + if(remainder !in hm) hm[remainder] = i + else if(i - hm[remainder]!! >= 2) return true + } + return false + } +} diff --git a/kotlin/0525-contiguous-array.kt b/kotlin/0525-contiguous-array.kt new file mode 100644 index 000000000..2b45364ee --- /dev/null +++ b/kotlin/0525-contiguous-array.kt @@ -0,0 +1,27 @@ +class Solution { + fun findMaxLength(nums: IntArray): Int { + var zero = 0 + var one = 0 + var res = 0 + + val diffIndex = HashMap () + + for ((i, n) in nums.withIndex()) { + if (n == 0) + zero++ + else + one++ + if (one - zero !in diffIndex) + diffIndex[one - zero] = i + + if (one == zero) { + res = one + zero + } else { + val idx = diffIndex[one - zero] ?: 0 + res = maxOf(res, i - idx) + } + } + + return res + } +} diff --git a/kotlin/0530-minimum-absolute-difference-in-bst.kt b/kotlin/0530-minimum-absolute-difference-in-bst.kt new file mode 100644 index 000000000..eb178d8dd --- /dev/null +++ b/kotlin/0530-minimum-absolute-difference-in-bst.kt @@ -0,0 +1,32 @@ +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + + fun minDiffInBST(root: TreeNode?): Int { + + var res = Integer.MAX_VALUE + var prev = -1 //Values in range (0 to 10^5) + + fun dfs(root: TreeNode?) { + if(root == null) return + + dfs(root.left) + if(prev != -1) res = minOf(res, root.value - prev) + prev = root.value + dfs(root.right) + } + + dfs(root) + return res + } + + val TreeNode.value get() = this.`val` +} diff --git a/kotlin/0535-encode-and-decode-tinyurl.kt b/kotlin/0535-encode-and-decode-tinyurl.kt new file mode 100644 index 000000000..1b65b2699 --- /dev/null +++ b/kotlin/0535-encode-and-decode-tinyurl.kt @@ -0,0 +1,35 @@ +class Codec() { + + //to shorten URL + val encodeMap = HashMap() + //to unshorten URL + val decodeMap = HashMap() + val encodeCharacters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + val tinyUrl = "http://tinyurl.com/" + + // Encodes a URL to a shortened URL. + fun encode(longUrl: String): String { + if(longUrl in encodeMap) return encodeMap[longUrl]!! + var fakeHashCode = "" + repeat(6) { + fakeHashCode += encodeCharacters.random() + } + val encodedLongUrl = "${tinyUrl}${fakeHashCode}" + encodeMap[longUrl] = encodedLongUrl + decodeMap[encodedLongUrl] = longUrl + return encodedLongUrl + } + + // Decodes a shortened URL to its original URL. + fun decode(shortUrl: String): String { + //since we are guaranteed that the shortUrl has been encoded + return decodeMap[shortUrl]!! + } +} + +/** + * Your Codec object will be instantiated and called as such: + * var obj = Codec() + * var url = obj.encode(longUrl) + * var ans = obj.decode(url) + */ diff --git a/kotlin/0538-convert-bst-to-greater-tree.kt b/kotlin/0538-convert-bst-to-greater-tree.kt new file mode 100644 index 000000000..1bc70288a --- /dev/null +++ b/kotlin/0538-convert-bst-to-greater-tree.kt @@ -0,0 +1,19 @@ +class Solution { + fun convertBST(root: TreeNode?): TreeNode? { + var curSum = 0 + + fun dfs(node: TreeNode?) { + node?: return + + dfs(node.right) + val temp = node.`val` + node.`val` += curSum + + curSum += temp + dfs(node.left) + } + + dfs(root) + return root + } +} diff --git a/kotlin/0540-single-element-in-a-sorted-array.kt b/kotlin/0540-single-element-in-a-sorted-array.kt new file mode 100644 index 000000000..ad3cd57ed --- /dev/null +++ b/kotlin/0540-single-element-in-a-sorted-array.kt @@ -0,0 +1,19 @@ +class Solution { + fun singleNonDuplicate(nums: IntArray): Int { + + var left = 0 + var right = nums.lastIndex + + while(left < right) { + val mid = (left + right) / 2 + if(mid % 2 == 0 && nums[mid] == nums[mid + 1] || + mid % 2 == 1 && nums[mid] == nums[mid - 1]) { + left = mid + 1 + } else { + right = mid + } + } + + return nums[left] + } +} diff --git a/kotlin/0543-diameter-of-binary-tree.kt b/kotlin/0543-diameter-of-binary-tree.kt new file mode 100644 index 000000000..7c781ac1b --- /dev/null +++ b/kotlin/0543-diameter-of-binary-tree.kt @@ -0,0 +1,20 @@ +package kotlin + +import TreeNode + +class Solution { + + fun diameterOfBinaryTree(root: TreeNode?): Int = maxDiameter(root).second + + private fun maxDiameter(root: TreeNode?): Pair { + if (root == null) return Pair(-1, -1) + val (heightOfLeftSubTree, maxDiameterOfLeftSubTree) = maxDiameter(root.left) + val (heightOfRightSubTree, maxDiameterOfRightSubTree) = maxDiameter(root.right) + val height = 1 + maxOf(heightOfLeftSubTree, heightOfRightSubTree) + val diameter = 2 + heightOfLeftSubTree + heightOfRightSubTree + return Pair( + height, + maxOf(maxDiameterOfLeftSubTree, maxDiameterOfRightSubTree, diameter) + ) + } +} \ No newline at end of file diff --git a/kotlin/0554-brick-wall.kt b/kotlin/0554-brick-wall.kt new file mode 100644 index 000000000..b1d96691c --- /dev/null +++ b/kotlin/0554-brick-wall.kt @@ -0,0 +1,15 @@ +class Solution { + fun leastBricks(wall: List>): Int { + val count = HashMap() + + for (row in wall) { + var total = 0 + for (i in 0 until row.lastIndex) { + total += row[i] + count[total] = count.getOrDefault(total, 0) + 1 + } + } + + return wall.size - (count.values.max() ?: 0) + } +} diff --git a/kotlin/0557-reverse-words-in-a-string-iii.kt b/kotlin/0557-reverse-words-in-a-string-iii.kt new file mode 100644 index 000000000..317d62ded --- /dev/null +++ b/kotlin/0557-reverse-words-in-a-string-iii.kt @@ -0,0 +1,21 @@ +class Solution { + fun reverseWords(s: String): String { + val words = s.split(" ") + val res = LinkedList() + + for (word in words) { + if (res.isNotEmpty()) + res.add(" ") + val sb = StringBuilder() + for (i in word.lastIndex downTo 0) { + sb.append(word[i]) + } + res.add(sb.toString()) + } + + return res.joinToString("") + } +} + +//Kotlin idomatic +fun reverseWords(s: String) = s.split(" ").map { it.reversed() }.joinToString(" ") diff --git a/kotlin/0560-subarray-sum-equals-k.kt b/kotlin/0560-subarray-sum-equals-k.kt new file mode 100644 index 000000000..5a9982d2b --- /dev/null +++ b/kotlin/0560-subarray-sum-equals-k.kt @@ -0,0 +1,14 @@ +class Solution { + fun subarraySum(nums: IntArray, k: Int): Int { + val hm = hashMapOf(0 to 1) + var res = 0 + var sum = 0 + nums.forEach { + sum += it + val prefix = sum - k + res += hm.getOrDefault(prefix, 0) + hm[sum] = hm.getOrDefault(sum, 0) + 1 + } + return res + } +} diff --git a/kotlin/0567-permutation-in-string.kt b/kotlin/0567-permutation-in-string.kt new file mode 100644 index 000000000..8868d98b0 --- /dev/null +++ b/kotlin/0567-permutation-in-string.kt @@ -0,0 +1,38 @@ +class Solution { + fun checkInclusion(s1: String, s2: String): Boolean { + val map = HashMap() + + for (c in s1.toCharArray()) + map[c] = map.getOrDefault(c, 0)+1 + + var count = map.size + var st = 0 + + for (end in 0..s2.length-1) { + val curr = s2[end] + + if (map.containsKey(curr)) { + map[curr] = map[curr]!!-1 + if (map[curr] == 0) + count-- + } + + while (count == 0) { + val temp = s2[st] + + if (map.containsKey(temp)) { + map[temp] = map[temp]!! + 1 + if (map[temp]!! > 0) + count++ + } + + if (end - st + 1 == s1.length) + return true + + st++ + } + } + + return false + } +} \ No newline at end of file diff --git a/kotlin/0572-subtree-of-another-tree.kt b/kotlin/0572-subtree-of-another-tree.kt new file mode 100644 index 000000000..5bf9b9a1b --- /dev/null +++ b/kotlin/0572-subtree-of-another-tree.kt @@ -0,0 +1,31 @@ +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun isSubtree(s: TreeNode?, t: TreeNode?): Boolean { + if (s == null) + return false + + return equals(s, t) || isSubtree(s.left, t) || isSubtree(s.right, t); + } + + fun equals(s: TreeNode?, t: TreeNode?): Boolean { + if (s == null && t == null) + return true; + + if (s == null || t == null) + return false; + + if (s.`val` != t.`val`) + return false; + + return equals(s.left, t.left) && equals(s.right, t.right); + } +} \ No newline at end of file diff --git a/kotlin/0576-out-of-boundary-paths.kt b/kotlin/0576-out-of-boundary-paths.kt new file mode 100644 index 000000000..512c10069 --- /dev/null +++ b/kotlin/0576-out-of-boundary-paths.kt @@ -0,0 +1,74 @@ +// Recursion + Memoization +class Solution { + fun findPaths(m: Int, n: Int, maxMove: Int, startRow: Int, startColumn: Int): Int { + val mod = 1_000_000_007 + val dirs = intArrayOf(0, 1, 0, -1, 0) + val dp = Array (m) { Array (n) { LongArray (maxMove + 1) { -1L } } } + + fun dfs(i: Int, j: Int, k: Int): Long { + if (i < 0 || i == m || j < 0 || j == n) return 1L + if (k == 0) return 0L + if (dp[i][j][k] != -1L) return dp[i][j][k] + + dp[i][j][k] = 0 + for (n in 0..3) + dp[i][j][k] = (dp[i][j][k] + dfs(i + dirs[n], j + dirs[n + 1], k - 1)) % mod + + return dp[i][j][k] + } + + return dfs(startRow, startColumn, maxMove).toInt() + } +} + +// Bottom-up DP +class Solution { + fun findPaths(m: Int, n: Int, maxMove: Int, startRow: Int, startColumn: Int): Int { + val mod = 1_000_000_007 + val dirs = intArrayOf(0, 1, 0, -1, 0) + val dp = Array (m) { Array (n) { LongArray (maxMove + 1) } } + + for (k in 1..maxMove) { + for (i in 0 until m) { + for (j in 0 until n) { + for (dir in 0..3) { + val i2 = i + dirs[dir] + val j2 = j + dirs[dir + 1] + if (i2 < 0 || i2 == m || j2 < 0 || j2 == n) + dp[i][j][k]++ + else + dp[i][j][k] = (dp[i][j][k] + dp[i2][j2][k - 1]) % mod + } + } + } + } + + return dp[startRow][startColumn][maxMove].toInt() + } +} + +// Top-down DP +class Solution { + fun findPaths(m: Int, n: Int, maxMove: Int, startRow: Int, startColumn: Int): Int { + val mod = 1_000_000_007 + val dirs = intArrayOf(0, 1, 0, -1, 0) + val dp = Array(m) { Array(n) { LongArray(maxMove + 1) } } + + for (k in 1..maxMove) { + for (i in m - 1 downTo 0) { + for (j in n - 1 downTo 0) { + for (dir in 0..3) { + val i2 = i + dirs[dir] + val j2 = j + dirs[dir + 1] + if (i2 < 0 || i2 == m || j2 < 0 || j2 == n) + dp[i][j][k]++ + else + dp[i][j][k] = (dp[i][j][k] + dp[i2][j2][k - 1]) % mod + } + } + } + } + + return dp[startRow][startColumn][maxMove].toInt() + } +} diff --git a/kotlin/0605-can-place-flowers.kt b/kotlin/0605-can-place-flowers.kt new file mode 100644 index 000000000..1429610db --- /dev/null +++ b/kotlin/0605-can-place-flowers.kt @@ -0,0 +1,17 @@ +class Solution { + fun canPlaceFlowers(flowerbed: IntArray, n: Int): Boolean { + var planted = 0 + for(i in 0 until flowerbed.size) { + if(flowerbed[i] == 0){ + val prev = if(i == 0) 0 else flowerbed[i-1] + val next = if(i == flowerbed.size-1) 0 else flowerbed[i+1] + if(prev == 0 && next == 0){ + planted++ + flowerbed[i] = 1 + } + if(planted == n) return true + } + } + return planted >= n + } +} diff --git a/kotlin/0606-construct-string-from-binary-tree.kt b/kotlin/0606-construct-string-from-binary-tree.kt new file mode 100644 index 000000000..79174e228 --- /dev/null +++ b/kotlin/0606-construct-string-from-binary-tree.kt @@ -0,0 +1,61 @@ +// recursion +class Solution { + fun tree2str(root: TreeNode?): String { + val res = StringBuilder() + + fun preOrder(root: TreeNode?) { + root?: return + + res.append("(") + res.append(root.`val`) + + if (root.left == null && root.right != null) + res.append("()") + + preOrder(root.left) + preOrder(root.right) + res.append(")") + } + + preOrder(root) + res.deleteCharAt(0) + res.deleteCharAt(res.lastIndex) + return res.toString() + } +} + +// iterative +class Solution { + fun tree2str(root: TreeNode?): String { + root?: return "" + + val res = StringBuilder() + val stack = LinkedList().apply { addLast(root) } + val visited = HashSet() + + while (stack.isNotEmpty()) { + val cur = stack.peekLast() + + if (cur in visited) { + stack.removeLast() + res.append(")") + } else { + visited.add(cur) + res.append("(") + res.append(cur?.`val`) + + if (cur?.left == null && cur?.right != null) + res.append("()") + + if (cur?.right != null) + stack.addLast(cur?.right) + if (cur?.left != null) + stack.addLast(cur?.left) + } + } + + res.deleteCharAt(0) + res.deleteCharAt(res.lastIndex) + return res.toString() + } +} diff --git a/kotlin/0617-merge-two-binary-trees.kt b/kotlin/0617-merge-two-binary-trees.kt new file mode 100644 index 000000000..eb300fa2d --- /dev/null +++ b/kotlin/0617-merge-two-binary-trees.kt @@ -0,0 +1,28 @@ +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun mergeTrees(root1: TreeNode?, root2: TreeNode?): TreeNode? { + + if(root1 == null && root2 == null) return null + + var newNode: TreeNode? = null + var val1 = if(root1 != null) root1.value else 0 + var val2 = if(root2 != null) root2.value else 0 + + newNode = TreeNode(val1 + val2) + newNode.left = mergeTrees(root1?.left ?: null, root2?.left ?: null) + newNode.right = mergeTrees(root1?.right ?: null, root2?.right ?: null) + + return newNode + } + + val TreeNode.value get() = this.`val` +} diff --git a/kotlin/0621-task-scheduler.kt b/kotlin/0621-task-scheduler.kt new file mode 100644 index 000000000..2deed091e --- /dev/null +++ b/kotlin/0621-task-scheduler.kt @@ -0,0 +1,28 @@ +class Solution { + fun leastInterval(tasks: CharArray, n: Int): Int { + if(n == 0) + return tasks.size + val hm = HashMap() + for(task in tasks) + hm[task] = hm.getOrDefault(task, 0) + 1 + val maxHeap = PriorityQueue(compareBy{-it}) + for(count in hm.values) + maxHeap.add(count) + val q = ArrayDeque>() // time, value + var time = 0 + while(!maxHeap.isEmpty() || !q.isEmpty()){ + if(!maxHeap.isEmpty()){ + var current = maxHeap.poll() + current-- + if(current > 0) + q.add(Pair(time+n,current)) + } + if(!q.isEmpty()){ + if(q.peek().first == time) + maxHeap.add(q.poll().second) + } + time++ + } + return time + } +} diff --git a/kotlin/0622-design-circular-queue.kt b/kotlin/0622-design-circular-queue.kt new file mode 100644 index 000000000..70a16d5fe --- /dev/null +++ b/kotlin/0622-design-circular-queue.kt @@ -0,0 +1,41 @@ +class MyCircularQueue(val k: Int) { + + private data class ListNode( + var value: Int, var next: ListNode? = null, var prev: ListNode? = null + ) + + private var left = ListNode(-1) + private var right = ListNode(-1, null, left) + private var space = k + + init { + left.next = right + } + + fun enQueue(element: Int): Boolean { + if (isFull()) return false + + ListNode(element, right, right.prev).let { newNode -> + right.prev!!.next = newNode + right.prev = newNode + space-- + return true + } + } + + fun deQueue(): Boolean { + if (isEmpty()) return false + + left.next = left.next!!.next + left.next!!.prev = left + space++ + + return true + } + + fun Front() = left.next?.value ?: -1 + fun Rear() = right.prev?.value ?: -1 + + fun isEmpty() = left.next == right + fun isFull() = space == 0 +} \ No newline at end of file diff --git a/kotlin/0629-k-inverse-pairs-array.kt b/kotlin/0629-k-inverse-pairs-array.kt new file mode 100644 index 000000000..7d860b692 --- /dev/null +++ b/kotlin/0629-k-inverse-pairs-array.kt @@ -0,0 +1,64 @@ +// Bottom-up DP, Time O(n^2 * k) and Space O(n * k) +class Solution { + fun kInversePairs(n: Int, k: Int): Int { + val mod = 1_000_000_007 + val dp = Array (n + 1) { LongArray (k + 1) } + + dp[0][0] = 1 + for (i in 1..n) { + for (j in 0..k) { + for (p in 0 until i) { + if (j - p >= 0) + dp[i][j] = (dp[i][j] + dp[i - 1][j - p]) % mod + } + } + } + + return dp[n][k].toInt() + } +} + +// Space optimized Bottom-up DP, Time O(n^2 * k) and Space O(k) +class Solution { + fun kInversePairs(n: Int, k: Int): Int { + val mod = 1_000_000_007 + var dp = LongArray (k + 1) + + dp[0] = 1 + for (i in 1..n) { + val newDp = LongArray (k + 1) + for (j in 0..k) { + for (p in 0 until i) { + if (j - p >= 0) + newDp[j] = (newDp[j] + dp[j - p]) % mod + } + } + dp = newDp + } + + return dp[k].toInt() + } +} + +// Space optimized DP + Sliding window Time O(n * k) and Space O(k) +class Solution { + fun kInversePairs(n: Int, k: Int): Int { + val mod = 1_000_000_007 + var prev = LongArray (k + 1) + + prev[0] = 1 + for (i in 1..n) { + val cur = LongArray (k + 1) + var total = 0L + for (j in 0..k) { + if (j >= i) + total -= prev[j - i] + total = (total + prev[j] + mod) % mod + cur[j] = total + } + prev = cur + } + + return prev[k].toInt() + } +} diff --git a/kotlin/0645-set-mismatch.kt b/kotlin/0645-set-mismatch.kt new file mode 100644 index 000000000..15ec39016 --- /dev/null +++ b/kotlin/0645-set-mismatch.kt @@ -0,0 +1,81 @@ +// O(n) time and O(1) space +class Solution { + fun findErrorNums(nums: IntArray): IntArray { + val n = nums.size + + var x = 0 + var y = 0 + for (i in 1..n) { + x += nums[i - 1] - i + y += nums[i - 1] * nums[i - 1] - i * i + } + + var missing = (y - x * x) / (2 * x) + var duplicate = missing + x + + return intArrayOf(duplicate, missing) + } +} + +// Inplace data manipulation O(n) time and O(1) space +class Solution { + fun findErrorNums(nums: IntArray): IntArray { + val res = IntArray (2) + + for (i in nums.indices) { + val n = Math.abs(nums[i]) + nums[n - 1] = -1 * nums[n - 1] + if (nums[n - 1] > 0) + res[0] = n + } + + for ((i, n) in nums.withIndex()) { + if (n > 0 && i + 1 != res[0]) { + res[1] = i + 1 + break + } + } + + return res + } +} + +// HashMap O(n) time and O(n) space +class Solution { + fun findErrorNums(nums: IntArray): IntArray { + val res = IntArray (2) + val counts = nums + .asList() + .groupingBy { it } + .eachCount() + + for (num in 1..nums.size) { + val count = counts[num] ?: 0 + if (count == 0) + res[1] = num + if (count == 2) + res[0] = num + } + + return res + } +} + +// Use XOR O(n) time and O(1) space +class Solution { + fun findErrorNums(nums: IntArray): IntArray { + val res = IntArray (2) + + for (i in nums.indices) { + val num = Math.abs(nums[i]) + + res[1] = res[1] xor ((i + 1) xor num) + + if (nums[num - 1] < 0) res[0] = num + else nums[num - 1] *= -1 + } + + res[1] = res[1] xor res[0] + return res + } +} diff --git a/kotlin/0646-maximum-length-of-pair-chain.kt b/kotlin/0646-maximum-length-of-pair-chain.kt new file mode 100644 index 000000000..21f6890d8 --- /dev/null +++ b/kotlin/0646-maximum-length-of-pair-chain.kt @@ -0,0 +1,64 @@ +//greedy algorithm O(nlogn) +class Solution { + fun findLongestChain(_pairs: Array): Int { + val pairs = _pairs.sortedWith(compareBy({ it[1] }, { it[0] })) + + var res = 1 + var time = pairs[0][1] + for (i in 1 until pairs.size) { + if(pairs[i][0] > time) { + res++ + time = pairs[i][1] + } + } + + return res + } +} + +//dp O(n^2) +class Solution { + fun findLongestChain(_pairs: Array): Int { + val pairs = _pairs.sortedWith(compareBy({ it[0] }, { it[1] })) + val n = pairs.size + val dp = IntArray(n) { 1 } + + for (i in 0 until n) { + for (j in 0 until n) { + if (pairs[i][0] > pairs[j][1]) { + dp[i] = maxOf(dp[i], dp[j] + 1) + } + } + } + + return dp[n - 1] + } +} + +//recursion + memoization O(n^2) +class Solution { + fun findLongestChain(_pairs: Array): Int { + val pairs = _pairs.sortedWith(compareBy({ it[0] }, { it[1] })) + val dp = IntArray (pairs.size) { -1 } + + fun dfs(i: Int): Int { + if (i == pairs.size) return 0 + if (dp[i] != -1) return dp[i] + + var res = 1 + for (j in 0 until i) { + if (pairs[i][0] > pairs[j][1]) + res = maxOf(res, dfs(j) + 1) + } + + dp[i] = res + return res + } + + var res = 1 + for (i in 0 until pairs.size) + res = maxOf(res, dfs(i)) + + return res + } +} diff --git a/kotlin/0647-palindromic-substrings.kt b/kotlin/0647-palindromic-substrings.kt new file mode 100644 index 000000000..292efb33a --- /dev/null +++ b/kotlin/0647-palindromic-substrings.kt @@ -0,0 +1,71 @@ +/* +* Two pointer with outwards extension solution, time O(N^2) and space O(1) +*/ +class Solution { + fun countSubstrings(s: String): Int { + var res = 0 + + for(i in s.indices) { + + var l = i + var r = i + + while(l >= 0 && r < s.length && s[l] == s[r]) { + res++ + l-- + r++ + } + + l = i + r = i+1 + + while(l >= 0 && r < s.length && s[l] == s[r]) { + res++ + l-- + r++ + } + + } + return res + } +} + +/* +* Two pointer with outwards extension solution, but with helper function. time O(N^2) and space O(1) +*/ +class Solution { + fun countSubstrings(s: String): Int { + var res = 0 + fun extend(_l: Int, _r: Int) { + var l = _l + var r = _r + while(l >= 0 && r < s.length && s[l] == s[r]) { + res++ + l-- + r++ + } + } + for(i in s.indices) { + extend(i,i) + extend(i,i+1) + } + return res + } +} + +/* +* DP solution, time O(N^2) and space O(N^2) +*/ +class Solution { + fun countSubstrings(s: String): Int { + var res = 0 + val dp = Array(s.length){ BooleanArray(s.length) } + for(i in s.lastIndex downTo 0) { + for(j in i..s.lastIndex) { + dp[i][j] = s[i] == s[j] && (j-i+1 < 3 || dp[i+1][j-1]) + if(dp[i][j]) res++ + } + } + return res + } +} diff --git a/kotlin/0649-dota2-senate.kt b/kotlin/0649-dota2-senate.kt new file mode 100644 index 000000000..9c50485c9 --- /dev/null +++ b/kotlin/0649-dota2-senate.kt @@ -0,0 +1,22 @@ +class Solution { + fun predictPartyVictory(senate: String): String { + val rQ = LinkedList() + val dQ = LinkedList() + val n = senate.length + + for (i in senate.indices) { + if (senate[i] == 'R') rQ.addLast(i) + else dQ.addLast(i) + } + + while (rQ.isNotEmpty() && dQ.isNotEmpty()) { + val r = rQ.removeFirst() + val d = dQ.removeFirst() + + if (r < d) rQ.addLast(r + n) + else dQ.addLast(d + n) + } + + return if (rQ.isEmpty()) "Dire" else "Radiant" + } +} diff --git a/kotlin/0652-find-duplicate-subtrees.kt b/kotlin/0652-find-duplicate-subtrees.kt new file mode 100644 index 000000000..964e29de8 --- /dev/null +++ b/kotlin/0652-find-duplicate-subtrees.kt @@ -0,0 +1,33 @@ +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun findDuplicateSubtrees(root: TreeNode?): List { + val res = ArrayList() + val subTrees = HashMap() + + fun preOrder(root: TreeNode?): String { + root?: return "null" + val s = ",${root.value},${preOrder(root.left)},${preOrder(root.right)}" + if(subTrees.contains(s)){ + if(subTrees[s] == 1) { + res.add(root) + } + } + subTrees[s] = subTrees.getOrDefault(s, 0) + 1 + return s + } + + preOrder(root) + return res + } + + val TreeNode.value get() = this.`val` +} diff --git a/kotlin/0658-find-k-closest-elements.kt b/kotlin/0658-find-k-closest-elements.kt new file mode 100644 index 000000000..f801a5613 --- /dev/null +++ b/kotlin/0658-find-k-closest-elements.kt @@ -0,0 +1,45 @@ +/* +* Optimized solution with binary search for the k-window instead of linear search. Time O(LogN-K) and Space O(1) +*/ +class Solution { + fun findClosestElements(arr: IntArray, k: Int, x: Int): List { + + var left = 0 + var right = arr.size - k + + while (left < right) { + val mid = (left + right) / 2 + if (x - arr[mid] > arr[mid + k] - x) + left = mid + 1 + else + right = mid + } + + val res = ArrayList() + for(i in left until right+k) res.add(arr[i]) + return res + } +} + +/* +* Linear search with window resizing. Time: O(N-K) and Space O(1) +*/ +class Solution { + fun findClosestElements(arr: IntArray, k: Int, x: Int): List { + + val res = ArrayList() + var left = 0 + var right = arr.lastIndex + + while (right - left + 1 > k) { + if (Math.abs(arr[left] - x) > Math.abs(arr[right] - x)) + left++ + else + right-- + } + + for(i in left..right) res.add(arr[i]) + + return res + } +} diff --git a/kotlin/0661-image-smoother.kt b/kotlin/0661-image-smoother.kt new file mode 100644 index 000000000..16550aaa1 --- /dev/null +++ b/kotlin/0661-image-smoother.kt @@ -0,0 +1,78 @@ +// O(n * m) time and O(n * m) space +class Solution { + fun imageSmoother(img: Array): Array { + val n = img.size + val m = img[0].size + val dirs = arrayOf( + intArrayOf(0, 1), + intArrayOf(0, -1), + intArrayOf(1, 0), + intArrayOf(-1, 0), + intArrayOf(-1, -1), + intArrayOf(1, 1), + intArrayOf(-1, 1), + intArrayOf(1, -1) + ) + + val res = Array (n) { IntArray (m) } + for (i in 0 until n) { + for (j in 0 until m) { + var count = 1 + var sum = img[i][j] + for (dir in dirs) { + val i2 = i + dir[0] + val j2 = j + dir[1] + if (i2 in (0 until n) && j2 in (0 until m)) { + count++ + sum += img[i2][j2] + } + } + res[i][j] = sum / count + } + } + + return res + } +} + +// O(n * m) time and O(1) space +class Solution { + fun imageSmoother(img: Array): Array { + val n = img.size + val m = img[0].size + val dirs = arrayOf( + intArrayOf(0, 1), + intArrayOf(0, -1), + intArrayOf(1, 0), + intArrayOf(-1, 0), + intArrayOf(-1, -1), + intArrayOf(1, 1), + intArrayOf(-1, 1), + intArrayOf(1, -1) + ) + + for (i in 0 until n) { + for (j in 0 until m) { + var count = 1 + var sum = img[i][j] + for (dir in dirs) { + val i2 = i + dir[0] + val j2 = j + dir[1] + if (i2 in (0 until n) && j2 in (0 until m)) { + count++ + sum += img[i2][j2] and 0xFF + } + } + img[i][j] = img[i][j] or ((sum / count) shl 8) + } + } + + for (i in 0 until n) { + for (j in 0 until m) { + img[i][j] = img[i][j] shr 8 + } + } + + return img + } +} diff --git a/kotlin/0662-maximum-width-of-binary-tree.kt b/kotlin/0662-maximum-width-of-binary-tree.kt new file mode 100644 index 000000000..a9dcfc665 --- /dev/null +++ b/kotlin/0662-maximum-width-of-binary-tree.kt @@ -0,0 +1,69 @@ +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ + +/* +* BFS solution +*/ +class Solution { + fun widthOfBinaryTree(root: TreeNode?): Int { + root?: return 0 + + val q = ArrayDeque>() + + var res = 0 + var prevLevel = 0 + var prevNum = 1 + q.add(Triple(root, 1, 0)) + + while (q.isNotEmpty()) { + val (node, num, level) = q.poll() + + if (level > prevLevel) { + prevLevel = level + prevNum = num + } + + res = maxOf(res, num - prevNum + 1) + + node?.left?.let { + q.add(Triple(node.left, 2 * num, level + 1)) + } + node?.right?.let { + q.add(Triple(node.right, 2 * num + 1, level + 1)) + } + } + + return res + } +} + +/* +* DFS solution +*/ +class Solution { + fun widthOfBinaryTree(root: TreeNode?): Int { + var width = 0 + val levelMap = HashMap() + + fun dfs(node: TreeNode?, depth: Int, pos: Int, levelMap: HashMap) { + node?: return + if(!levelMap.contains(depth)) + levelMap[depth] = pos + width = maxOf(width, (pos - levelMap[depth]!! + 1)) + dfs(node.left, depth+1, 2*pos, levelMap) + dfs(node.right, depth+1, 2*pos+1, levelMap) + } + + dfs(root, 0, 0, levelMap) + return width + } + +} diff --git a/kotlin/0665-non-decreasing-array.kt b/kotlin/0665-non-decreasing-array.kt new file mode 100644 index 000000000..742e31867 --- /dev/null +++ b/kotlin/0665-non-decreasing-array.kt @@ -0,0 +1,14 @@ +class Solution { + fun checkPossibility(nums: IntArray): Boolean { + var modified = false + for(i in 0 until nums.size-1){ + if(nums[i+1] < nums[i]) { + if(modified) return false + if(i == 0 || nums[i+1] >= nums[i-1]) nums[i] = nums[i+1] + else nums[i+1] = nums[i] + modified = true + } + } + return true + } +} diff --git a/kotlin/0669-trim-a-binary-search-tree.kt b/kotlin/0669-trim-a-binary-search-tree.kt new file mode 100644 index 000000000..b6845eabd --- /dev/null +++ b/kotlin/0669-trim-a-binary-search-tree.kt @@ -0,0 +1,23 @@ +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun trimBST(root: TreeNode?, low: Int, high: Int): TreeNode? { + if(root == null) + return null + if(root.`val` < low) + return trimBST(root.right, low, high) + else if(root.`val` > high) + return trimBST(root.left, low, high) + root.left = trimBST(root.left, low, high) + root.right = trimBST(root.right, low, high) + return root + } +} diff --git a/kotlin/0673-number-of-longest-increasing-subsequence.kt b/kotlin/0673-number-of-longest-increasing-subsequence.kt new file mode 100644 index 000000000..962be0108 --- /dev/null +++ b/kotlin/0673-number-of-longest-increasing-subsequence.kt @@ -0,0 +1,86 @@ +/* +* DFS + memoization solution +*/ +class Solution { + fun findNumberOfLIS(nums: IntArray): Int { + val memo = HashMap>() + var lisLen = 0 + var lisCount = 0 + + fun dfs(i: Int): Pair { + if (i in memo) + return memo[i]!! + + var maxLen = 1 + var maxCount = 1 + for (j in i+1 until nums.size) { + if (nums[i] < nums[j]) { + val (curLen, curCount) = dfs(j) + if (curLen + 1 > maxLen) { + maxLen = curLen + 1 + maxCount = curCount + } else if (curLen + 1 == maxLen) { + maxCount += curCount + } + } + } + + if (maxLen > lisLen) { + lisLen = maxLen + lisCount = maxCount + } else if (maxLen == lisLen) { + lisCount += maxCount + } + + memo[i] = (maxLen to maxCount) + return memo[i]!! + } + + for (i in nums.indices) + dfs(i) + + return lisCount + } +} + +/* +* DP solution +*/ +class Solution { + fun findNumberOfLIS(nums: IntArray): Int { + val lis = IntArray(nums.size) {1} + val count = IntArray(nums.size) + + var lisLen = 0 + var lisCount = 0 + for (i in nums.size-1 downTo 0) { + var maxLen = 1 + var maxCount = 1 + + for (j in i+1 until nums.size) { + if (nums[i] < nums[j]) { + val curLen = lis[j] + val curCount = count[j] + if (curLen + 1 > maxLen) { + maxLen = curLen + 1 + maxCount = curCount + } else if (curLen + 1 == maxLen) { + maxCount += curCount + } + } + } + + if (maxLen > lisLen) { + lisLen = maxLen + lisCount = maxCount + } else if (maxLen == lisLen) { + lisCount += maxCount + } + + lis[i] = maxLen + count[i] = maxCount + } + + return lisCount + } +} diff --git a/kotlin/0678-valid-parenthesis-string.kt b/kotlin/0678-valid-parenthesis-string.kt new file mode 100644 index 000000000..ee51cf772 --- /dev/null +++ b/kotlin/0678-valid-parenthesis-string.kt @@ -0,0 +1,57 @@ +class Solution { + + fun checkValidString(s: String): Boolean { + // number of opening parenthesis when '*' is taken as '(' + var noOfOpenParenthesisWhenStarIsAnOpeningParenthesis = 0 + // number of opening parenthesis when '*' is taken as ')' + var noOfOpenParenthesisWhenStarIsAClosingParenthesis = 0 + // number of opening parenthesis when '*' is taken as ' ' + var noOfOpenParenthesisWhenStarIsAnEmptyString = 0 + + for (char in s) { + if (char == '(') { + noOfOpenParenthesisWhenStarIsAnOpeningParenthesis++ + noOfOpenParenthesisWhenStarIsAClosingParenthesis++ + noOfOpenParenthesisWhenStarIsAnEmptyString++ + + } + if (char == '*') { + noOfOpenParenthesisWhenStarIsAnOpeningParenthesis++ + noOfOpenParenthesisWhenStarIsAClosingParenthesis-- + } + + if (char == ')') { + noOfOpenParenthesisWhenStarIsAnOpeningParenthesis-- + noOfOpenParenthesisWhenStarIsAClosingParenthesis-- + noOfOpenParenthesisWhenStarIsAnEmptyString-- + } + // A negative value indicates an excess of closing parenthesis. + // Eg: -1 indicates that there are no opening parenthesis and 1 closing parenthesis. + // If at least one of our possibilities is a positive value, then it indicates + // that the string is possibly valid. + // If all of our possibilities have a negative value, it indicates that none of the + // possibilities lead to a valid string. Hence, return false. + if ( + noOfOpenParenthesisWhenStarIsAnOpeningParenthesis < 0 && + noOfOpenParenthesisWhenStarIsAClosingParenthesis < 0 && + noOfOpenParenthesisWhenStarIsAnEmptyString < 0 + ) return false + + // Ensure that the variables are always positive. A negative value doesn't make sense + // because there cannot be a negative number of opening parenthesis. + noOfOpenParenthesisWhenStarIsAnOpeningParenthesis = + noOfOpenParenthesisWhenStarIsAnOpeningParenthesis.coerceAtLeast(0) + + noOfOpenParenthesisWhenStarIsAClosingParenthesis = + noOfOpenParenthesisWhenStarIsAClosingParenthesis.coerceAtLeast(0) + + noOfOpenParenthesisWhenStarIsAnEmptyString = + noOfOpenParenthesisWhenStarIsAnEmptyString.coerceAtLeast(0) + + } + + return noOfOpenParenthesisWhenStarIsAnOpeningParenthesis == 0 || + noOfOpenParenthesisWhenStarIsAClosingParenthesis == 0 || + noOfOpenParenthesisWhenStarIsAnEmptyString == 0 + } +} \ No newline at end of file diff --git a/kotlin/0680-valid-palindrome-ii.kt b/kotlin/0680-valid-palindrome-ii.kt new file mode 100644 index 000000000..86ca1d106 --- /dev/null +++ b/kotlin/0680-valid-palindrome-ii.kt @@ -0,0 +1,31 @@ +class Solution { + fun validPalindrome(s: String): Boolean { + + var left = 0 + var right = s.lastIndex + + while (left <= right) { + if(s[left] != s[right]) + return isValid(s, left, right -1) || isValid(s, left + 1, right) + left++ + right-- + } + + return true + } + + private fun isValid(s: String, _left: Int, _right: Int): Boolean { + + var left = _left + var right = _right + + while (left <= right) { + if(s[left] != s[right]) + return false + left++ + right-- + } + + return true + } +} diff --git a/kotlin/0682-baseball-game.kt b/kotlin/0682-baseball-game.kt new file mode 100644 index 000000000..befb45536 --- /dev/null +++ b/kotlin/0682-baseball-game.kt @@ -0,0 +1,32 @@ +/* +* Using a stack. Time Complexity O(N) and Space Complexity O(N) +*/ +class Solution { + fun calPoints(operations: Array): Int { + + val stack = ArrayDeque() + + for(op in operations) { + when (op) { + "+" -> { + val top = stack.removeLast() + val sum = stack.peekLast() + top + stack.addLast(top) + stack.addLast(sum) + } + "D" -> { + val top = stack.peekLast() * 2 + stack.addLast(top) + } + "C" -> { + stack.removeLast() + } + else -> { + stack.addLast(op.toInt()) + } + } + } + + return stack.sum() + } +} diff --git a/kotlin/0684-redundant-connection.kt b/kotlin/0684-redundant-connection.kt new file mode 100644 index 000000000..56cfa9be2 --- /dev/null +++ b/kotlin/0684-redundant-connection.kt @@ -0,0 +1,57 @@ +class Solution { + fun findRedundantConnection(edges: Array): IntArray { + val uf = UnionFind(edges.size) + + for (edge in edges) { + val (u, v) = edge + + if (uf.isConnected(u-1, v-1)) + return intArrayOf(u, v) + + uf.unify(u-1, v-1) + } + + return intArrayOf(0) + } +} + +class UnionFind(n : Int) { + val parent = IntArray(n) { it } + val rank = IntArray(n) { 1 } + + fun unify(p: Int, q: Int) { + val rootP = find(p) + val rootQ = find(q) + + if (rootP == rootQ) + return + + if (rank[rootP] > rank[rootQ]) { + parent[rootQ] = parent[rootP] + rank[rootP] += rank[rootQ] + } else { + parent[rootP] = parent[rootQ] + rank[rootQ] += rank[rootP] + } + } + + fun find(p: Int): Int { + var root = p + var curr = p + + while (root != parent[root]) + root = parent[root] + + while (root != curr) { + val next = parent[curr] + parent[p] = parent[root] + curr = next + } + + return root + } + + fun isConnected(p: Int, q: Int): Boolean { + return find(p) == find(q) + } +} \ No newline at end of file diff --git a/kotlin/0691-stickers-to-spell-word.kt b/kotlin/0691-stickers-to-spell-word.kt new file mode 100644 index 000000000..a1c7d5593 --- /dev/null +++ b/kotlin/0691-stickers-to-spell-word.kt @@ -0,0 +1,48 @@ +class Solution { + fun minStickers(stickers: Array, target: String): Int { + val stickCount = HashMap>().apply { + stickers.forEach { s -> + this[s] = s.groupingBy { it } + .eachCount() + .toMutableMap() + } + } + + val dp = HashMap() + + fun dfs(t: String, stick: MutableMap): Int { + dp[t]?.let { + return it + } + + var res = if (stick.isEmpty()) 0 else 1 + val remainT = StringBuilder() + for (c in t) { + if (c in stick && stick[c]!! > 0) { + stick[c] = stick[c]!! - 1 + } else { + remainT.append(c) + } + } + + if (remainT.length > 0) { + val remainTS = remainT.toString() + var used = Integer.MAX_VALUE + for (s in stickCount.values) { + if (remainTS[0]!! !in s) + continue + used = minOf(used, dfs(remainTS, s.toMutableMap())) + } + + dp[remainTS] = used + res = if (used == Integer.MAX_VALUE ) Integer.MAX_VALUE else res + used + } + + return res + } + + + val res = dfs(target, mutableMapOf()) + return if (res == Integer.MAX_VALUE) -1 else res + } +} diff --git a/kotlin/0695-max-area-of-island.kt b/kotlin/0695-max-area-of-island.kt new file mode 100644 index 000000000..ca47af620 --- /dev/null +++ b/kotlin/0695-max-area-of-island.kt @@ -0,0 +1,28 @@ +class Solution { + fun maxAreaOfIsland(grid: Array): Int { + var res = 0 + + for (i in 0..grid.size-1) { + for (j in 0..grid[0].size-1) { + if (grid[i][j] == 1) { + val curr = dfs(grid, i, j) + res = Math.max(res, curr) + } + } + } + + return res + } + + fun dfs(grid: Array, row: Int, col: Int): Int { + if (row < 0 || row >= grid.size || col < 0 || col >= grid[0].size) + return 0 + + if (grid[row][col] == 0) + return 0 + + grid[row][col] = 0 + + return dfs(grid, row+1, col) + dfs(grid, row-1, col) + dfs(grid, row, col+1) + dfs(grid, row, col-1)+1 + } +} \ No newline at end of file diff --git a/kotlin/0698-partition-to-k-equal-sum-subsets.kt b/kotlin/0698-partition-to-k-equal-sum-subsets.kt new file mode 100644 index 000000000..2f6599116 --- /dev/null +++ b/kotlin/0698-partition-to-k-equal-sum-subsets.kt @@ -0,0 +1,31 @@ +class Solution { + fun canPartitionKSubsets(nums: IntArray, k: Int): Boolean { + + var target = nums.sum() + if(target%k != 0) return false // we cant divive nums equally by k + target /= k + + + val used = BooleanArray(nums.size) + nums.sortDescending() + + fun backtrack(i: Int, k: Int, sum: Int): Boolean { + + if(k == 0) return true + + if(sum == target) return backtrack(0, k-1, 0) + + for(j in i until nums.size){ + if(nums[j] > target) return false + if(used[j]==true || nums[j]+sum > target) continue + used[j] = true + if(backtrack(j+1, k, sum+nums[j]) == true) return true + used[j] = false + } + + return false + } + + return backtrack(0,k,0) + } +} diff --git a/kotlin/0701-insert-into-a-binary-search-tree.kt b/kotlin/0701-insert-into-a-binary-search-tree.kt new file mode 100644 index 000000000..d23ebf01d --- /dev/null +++ b/kotlin/0701-insert-into-a-binary-search-tree.kt @@ -0,0 +1,57 @@ +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ + + // Recursive solution (Non self balancing) +class Solution { + fun insertIntoBST(root: TreeNode?, value: Int): TreeNode? { + + root?: return TreeNode(value) + + if (value > root.value) root.right = insertIntoBST(root.right, value) + else root.left = insertIntoBST(root.left, value) + + return root + } + + private val TreeNode.value get() = `val` +} + +// Iterative Solution (Non self balancing) +class Solution { + fun insertIntoBST(root: TreeNode?, value: Int): TreeNode? { + + root?: return TreeNode(value) + + var current = root + + while (current != null) { + if (value > current!!.value) { + if (current.right != null){ + current = current?.right + } else { + current.right = TreeNode(value) + break + } + } else { + if (current.left != null){ + current = current?.left + } else { + current.left = TreeNode(value) + break + } + } + } + + return root + } + + private val TreeNode.value get() = `val` +} diff --git a/kotlin/0703-kth-largest-element-in-a-stream.kt b/kotlin/0703-kth-largest-element-in-a-stream.kt new file mode 100644 index 000000000..c61910740 --- /dev/null +++ b/kotlin/0703-kth-largest-element-in-a-stream.kt @@ -0,0 +1,18 @@ + class KthLargest(val k: Int, val nums: IntArray) { + val minHeap = PriorityQueue() + + init{ + for (num in nums) + minHeap.add(num) + while (minHeap.size > k) + minHeap.poll() + } + + fun add(`val`: Int): Int { + minHeap.add(`val`) + if (minHeap.size > k) + minHeap.poll() + return minHeap.peek() + } + +} diff --git a/kotlin/0704-binary-search.kt b/kotlin/0704-binary-search.kt new file mode 100644 index 000000000..6efa0fdf0 --- /dev/null +++ b/kotlin/0704-binary-search.kt @@ -0,0 +1,15 @@ +class Solution { + fun search(nums: IntArray, target: Int): Int { + return searchAux(nums, target, 0, nums.size - 1) + } + + private fun searchAux(nums: IntArray, target: Int, begin: Int, end: Int): Int { + if (begin > end) return -1 + + val mid: Int = (begin + end) / 2 + + return if (nums[mid] == target) mid + else if (nums[mid] > target) searchAux(nums, target, begin, mid - 1) + else searchAux(nums, target, mid + 1, end) + } +} diff --git a/kotlin/0705-design-hashset.kt b/kotlin/0705-design-hashset.kt new file mode 100644 index 000000000..aef3ac1df --- /dev/null +++ b/kotlin/0705-design-hashset.kt @@ -0,0 +1,43 @@ +class MyHashSet() { + + class LN(val value: Int) { + var next: LN? = null + } + + val set = Array (10000) { LN(0) } + + fun add(key: Int) { + var cur = set[hash(key)] + while (cur?.next != null) { + if (cur?.next?.value == key) + return + cur = cur?.next!! + } + cur.next = LN(key) + } + + fun remove(key: Int) { + var cur = set[hash(key)] + while (cur?.next != null) { + if (cur?.next?.value == key) { + cur?.next = cur?.next?.next + return + } + cur = cur?.next!! + } + } + + fun contains(key: Int): Boolean { + var cur = set[hash(key)] + while (cur?.next != null) { + if (cur?.next?.value == key) { + return true + } + cur = cur?.next!! + } + return false + } + + private fun hash(key: Int) = key % set.size + +} diff --git a/kotlin/0706-design-hashmap.kt b/kotlin/0706-design-hashmap.kt new file mode 100644 index 000000000..62bad7242 --- /dev/null +++ b/kotlin/0706-design-hashmap.kt @@ -0,0 +1,51 @@ +class ChainNode( + var key: Int = -1, + var value: Int = -1 +) { + var next: ChainNode? = null +} + +class MyHashMap() { + + val hashMap = Array(1000) { ChainNode() } + + fun put(key: Int, value: Int) { + var current: ChainNode? = hashMap[key % hashMap.size] + while(current?.next != null) { + if(current.next?.key == key) { + current.next?.value = value + return + } + current = current?.next + } + current?.next = ChainNode(key, value) + } + + fun get(key: Int): Int { + var current: ChainNode? = hashMap[key % hashMap.size].next + while(current != null) { + if(current.key == key) return current.value + current = current.next + } + return -1 + } + + fun remove(key: Int) { + var current: ChainNode? = hashMap[key % hashMap.size] + while(current != null && current.next != null) { + if(current.next?.key == key) { + current.next = current.next?.next + return + } + current = current.next + } + } +} + +/** + * Your MyHashMap object will be instantiated and called as such: + * var obj = MyHashMap() + * obj.put(key,value) + * var param_2 = obj.get(key) + * obj.remove(key) + */ diff --git a/kotlin/0707-design-linked-list.kt b/kotlin/0707-design-linked-list.kt new file mode 100644 index 000000000..f392b5d0d --- /dev/null +++ b/kotlin/0707-design-linked-list.kt @@ -0,0 +1,84 @@ +class MyLinkedList() { + val head = LN(0) + val tail = LN(0) + + init { + head.next = tail + tail.prev = head + } + + fun get(index: Int): Int { + var current = head.next + var i = 0 + while (current != null && i != index) { + current = current.next + i++ + } + + return if (current != null && current != tail) current.`val` else -1 + } + + fun addAtHead (`val`: Int) { + val prev = head + val next = head.next + val new = LN(`val`) + + head.next = new + new.prev = head + new.next = next + next?.prev = new + } + + fun addAtTail(`val`: Int) { + val next = tail + val prev = tail.prev + val new = LN(`val`) + + tail.prev = new + new.prev = prev + new.next = tail + prev?.next = new + } + + fun addAtIndex(index: Int, `val`: Int) { + var current = head.next + var i = 0 + while (current != null && i != index) { + current = current.next + i++ + } + + if (current != null) { + val prev = current.prev + val new = LN(`val`) + + prev?.next = new + new.prev = prev + new.next = current + current.prev = new + } + } + + fun deleteAtIndex(index: Int) { + var current = head.next + var i = 0 + while (current != null && i != index) { + current = current.next + i++ + } + + if (current != null && current != tail) { + val prev = current.prev + val next = current.next + + prev?.next = next + next?.prev = prev + } + } +} + +class LN ( + var `val`: Int, + var next: LN? = null, + var prev: LN? = null +) diff --git a/kotlin/0721-accounts-merge.kt b/kotlin/0721-accounts-merge.kt new file mode 100644 index 000000000..020a46995 --- /dev/null +++ b/kotlin/0721-accounts-merge.kt @@ -0,0 +1,62 @@ +class DSU(val n: Int) { + + val parent = IntArray(n) {it} + val rank = IntArray(n) {1} + + fun find(x: Int): Int { + if (x != parent[x]) + parent[x] = find(parent[x]) + return parent[x] + } + + fun union(x: Int, y: Int): Boolean { + val pX = find(x) + val pY = find(y) + + if(pX == pY) + return false + + if (rank[pX] > rank[pY]) { + parent[pY] = pX + rank[pX] += rank[pY] + } else { + parent[pX] = pY + rank[pY] += rank[pX] + } + + return true + } +} + +class Solution { + fun accountsMerge(accounts: List>): List> { + val uf = DSU(accounts.size) + val emailToAcc = HashMap() + + for ((i,accs) in accounts.withIndex()) { + for (j in 1..accs.lastIndex) { + val e = accs[j] + if (e in emailToAcc.keys) { + uf.union(i, emailToAcc[e]!!) + } else { + emailToAcc[e] = i + } + } + } + + val emails = hashMapOf>() + for ((e, i) in emailToAcc) { + val main = uf.find(i) + emails[main] = emails.getOrDefault(main, ArrayList()).apply { this.add(e) } + } + + val res = ArrayList>() + for ((i, em) in emails.entries) { + em.sort() + em.add(0, accounts[i][0]) + res.add(em) + } + + return res + } +} diff --git a/kotlin/0724-find-pivot-index.kt b/kotlin/0724-find-pivot-index.kt new file mode 100644 index 000000000..c3f96a084 --- /dev/null +++ b/kotlin/0724-find-pivot-index.kt @@ -0,0 +1,12 @@ +class Solution { + fun pivotIndex(nums: IntArray): Int { + var leftSum = 0 + var rightSum = 0 + for(num in nums) rightSum += num + for(i in nums.indices){ + if(leftSum == rightSum - nums[i] - leftSum) return i + leftSum += nums[i] + } + return -1 + } +} diff --git a/kotlin/0725-split-linked-list-in-parts.kt b/kotlin/0725-split-linked-list-in-parts.kt new file mode 100644 index 000000000..f7e15d6e8 --- /dev/null +++ b/kotlin/0725-split-linked-list-in-parts.kt @@ -0,0 +1,34 @@ +class Solution { + fun splitListToParts(head: ListNode?, k: Int): Array { + var n = 0 + var cur = head + while (cur != null) { + cur = cur?.next + n++ + } + + val len = n / k + var rem = n % k + + cur = head + val res = arrayOfNulls(k) + for (i in 0 until k) { + res[i] = cur + + val end = (len - 1) + if (rem > 0) 1 else 0 + for (j in 0 until end) { + cur?: break + cur = cur?.next + } + + if (rem > 0) rem-- + if (cur != null) { + val temp = cur?.next + cur.next = null + cur = temp + } + } + + return res + } +} diff --git a/kotlin/0735-asteroid-collision.kt b/kotlin/0735-asteroid-collision.kt new file mode 100644 index 000000000..422f4cda8 --- /dev/null +++ b/kotlin/0735-asteroid-collision.kt @@ -0,0 +1,24 @@ +class Solution { + fun asteroidCollision(ast: IntArray): IntArray { + val stack = LinkedList() + + for (_a in ast) { + var a = _a + while (stack.isNotEmpty() && a < 0 && stack.peekLast() > 0) { + val diff = a + stack.peekLast() + if (diff < 0) { + stack.removeLast() + } else if (diff > 0) { + a = 0 + } else { + a = 0 + stack.removeLast() + } + } + + if (a != 0) stack.addLast(a) + } + + return stack.toIntArray() + } +} diff --git a/kotlin/0739-daily-temperatures.kt b/kotlin/0739-daily-temperatures.kt new file mode 100644 index 000000000..4be95ae63 --- /dev/null +++ b/kotlin/0739-daily-temperatures.kt @@ -0,0 +1,18 @@ +import java.util.* + +class Solution { + fun dailyTemperatures(temperatures: IntArray): IntArray { + if (temperatures.size == 1) return intArrayOf(0) + val stack = Stack() + val resultantArray = IntArray(temperatures.size) + var poppedElement: Int + for (i in 0..temperatures.lastIndex) { + while (stack.isNotEmpty() && temperatures[stack.peek()] < temperatures[i]) { + poppedElement = stack.pop() + resultantArray[poppedElement] = i - poppedElement + } + stack.push(i) + } + return resultantArray + } +} \ No newline at end of file diff --git a/kotlin/0740-delete-and-earn.kt b/kotlin/0740-delete-and-earn.kt new file mode 100644 index 000000000..d4a8da724 --- /dev/null +++ b/kotlin/0740-delete-and-earn.kt @@ -0,0 +1,25 @@ +class Solution { + fun deleteAndEarn(nums: IntArray): Int { + val count = HashMap().apply { + for (n in nums) this[n] = this.getOrDefault(n, 0) + 1 + } + val noDups = nums.toSet().toTypedArray().apply { this.sort()} + + var before = 0 + var sum = 0 + for (i in noDups.indices) { + val cur = noDups[i] * count[noDups[i]]!! + if (i > 0 && noDups[i] == noDups[i - 1] + 1) { + val temp = sum + sum = maxOf(cur + before, sum) + before = temp + } else { + val temp = sum + sum = cur + sum + before = temp + } + } + + return sum + } +} diff --git a/kotlin/0743-network-delay-time.kt b/kotlin/0743-network-delay-time.kt new file mode 100644 index 000000000..546542568 --- /dev/null +++ b/kotlin/0743-network-delay-time.kt @@ -0,0 +1,33 @@ +class Solution { + //times[i] == [source, target, weight] + fun networkDelayTime(times: Array, n: Int, k: Int): Int { + val adjList = ArrayList>>(times.size) + for(i in 0..n) + adjList.add(ArrayList>()) + for(time in times){ + val (source, target, weight) = time + adjList.get(source).add(Pair(target, weight)) + } + //minheap stores [node, weight] + val minHeap = PriorityQueue{ edge1: IntArray, edge2: IntArray -> + edge1[1] - edge2[1] + } + val visited = HashSet() + minHeap.add(intArrayOf(k,0)) + var time = 0 //the max of all the shortest paths + while(!minHeap.isEmpty()){ + val (node, weight) = minHeap.poll() + if(visited.contains(node)) + continue + visited.add(node) + time = maxOf(time, weight) + val listofnodes = adjList.get(node) + for(adjacent in listofnodes){ + val (adjNode, adjWeight) = adjacent + if(!visited.contains(adjNode)) + minHeap.add(intArrayOf(adjNode, adjWeight+weight)) // total path to each adjNode + } + } + return if(visited.size == n) time else -1 + } +} diff --git a/kotlin/0746-min-cost-climbing-stairs.kt b/kotlin/0746-min-cost-climbing-stairs.kt new file mode 100644 index 000000000..5c5270137 --- /dev/null +++ b/kotlin/0746-min-cost-climbing-stairs.kt @@ -0,0 +1,8 @@ +class Solution { + fun minCostClimbingStairs(cost: IntArray): Int { + for (i in cost.size - 3 downTo 0) { + cost[i] += minOf(cost[i + 1], cost[i + 2]) + } + return minOf(cost[0], cost[1]) + } +} \ No newline at end of file diff --git a/kotlin/0752-open-the-lock.kt b/kotlin/0752-open-the-lock.kt new file mode 100644 index 000000000..8cb1a46ab --- /dev/null +++ b/kotlin/0752-open-the-lock.kt @@ -0,0 +1,47 @@ +class Solution { + + fun openLock(deadends: Array, target: String): Int { + val deadEndSet = hashSetOf() + deadends.forEach { deadEndSet.add(it) } + if (target in deadEndSet || "0000" in deadEndSet) return -1 + // [ (String,Level) ] + val queue: Queue> = LinkedList>().apply { + add(Pair("0000", 0)) + } + val visitedCombinations = hashSetOf() + + while (queue.isNotEmpty()) { + val (currentCombination, levelOfCurrentCombination) = queue.remove() + if (currentCombination == target) return levelOfCurrentCombination + // increments + for (i in currentCombination.indices) { + val digitAtIndex = Character.digit(currentCombination[i], 10) + val incrementedValue = Character.forDigit((digitAtIndex + 1) % 10, 10) + val newCombination = currentCombination.replaceCharAt(i, incrementedValue) + if (newCombination in visitedCombinations || newCombination in deadEndSet) continue + visitedCombinations.add(newCombination) + queue.add(Pair(newCombination, levelOfCurrentCombination + 1)) + } + // decrements + for (i in currentCombination.indices) { + val valueAtIndex = Character.digit(currentCombination[i], 10) + val decrementedValue = Character.forDigit( + (valueAtIndex - 1).takeIf { valueAtIndex - 1 in 0..9 } ?: 9, 10) + val newCombination = currentCombination.replaceCharAt(i, decrementedValue) + if (newCombination in visitedCombinations || newCombination in deadEndSet) continue + visitedCombinations.add(newCombination) + queue.add(Pair(newCombination, levelOfCurrentCombination + 1)) + } + } + return -1 + } + + private fun String.replaceCharAt( + index: Int, + newChar: Char + ): String = when (index) { + 0 -> newChar + substring(1..this.lastIndex) + lastIndex -> substring(0 until this.lastIndex) + newChar + else -> substring(0 until index) + newChar + substring(index + 1..this.lastIndex) + } +} \ No newline at end of file diff --git a/kotlin/0763-partition-labels.kt b/kotlin/0763-partition-labels.kt new file mode 100644 index 000000000..662c51aab --- /dev/null +++ b/kotlin/0763-partition-labels.kt @@ -0,0 +1,33 @@ +class Solution { + fun partitionLabels(s: String): List { + if (s.length == 1) return listOf(1) + val lastIndexOfChar = mutableMapOf().apply { + s.forEachIndexed { index, char -> this[char] = index } + } + val partitions = mutableListOf() + var l = 0 + while (l in s.indices) { + // keep moving l to find the first char that has a last occurrence greater than l + while (l == lastIndexOfChar[s[l]]) { + // if l is at the one-and-only occurrence of the char s[l], make a partition and move l + partitions.add(1) + l++ + if (l > s.lastIndex) return partitions + } + + var r = lastIndexOfChar.getValue(s[l]) + var j = l + 1 + while (j <= r) { + if (lastIndexOfChar.getValue(s[j]) <= r){ + j++ + continue + } + r = lastIndexOfChar.getValue(s[j]) + j++ + } + partitions.add((r - l) + 1) + l = r + 1 + } + return partitions + } +} \ No newline at end of file diff --git a/kotlin/0767-reorganize-string.kt b/kotlin/0767-reorganize-string.kt new file mode 100644 index 000000000..3d41fa289 --- /dev/null +++ b/kotlin/0767-reorganize-string.kt @@ -0,0 +1,76 @@ +class Solution { + fun reorganizeString(s: String): String { + var count = HashMap().apply { + for (c in s) + this[c] = getOrDefault(c, 0) + 1 + } + + val maxHeap = PriorityQueue>() { a, b -> + b.first - a.first + } + + for ((ch, cnt) in count) + maxHeap.add(cnt to ch) + + var prev: Pair? = null + var res = StringBuilder() + while (maxHeap.isNotEmpty() || prev != null) { + if (maxHeap.isEmpty() && prev != null) + return "" + + var (cnt, ch) = maxHeap.poll() + res.append(ch) + cnt-- + + prev?.let { + maxHeap.add(it) + prev = null + } + + if (cnt != 0) + prev = cnt to ch + } + + return res.toString() + } +} + +// another solution without heap +class Solution { + fun reorganizeString(s: String): String { + val freq = IntArray (26).apply { + for (c in s) this[c - 'a']++ + } + + var maxFreq = -1 + var maxChar = -1 + for (letter in 0..25) { + if (freq[letter] > maxFreq) { + maxFreq = freq[letter] + maxChar = letter + } + } + + if (maxFreq > (s.length + 1) / 2) return "" + + var i = 0 + val res = CharArray (s.length).apply { + while (freq[maxChar] > 0) { + this[i] = 'a' + maxChar + i += 2 + freq[maxChar]-- + } + } + + for (letter in 0..25) { + while (freq[letter] > 0) { + if (i > s.lastIndex) i = 1 + res[i] = 'a' + letter + i += 2 + freq[letter]-- + } + } + + return res.joinToString("") + } +} diff --git a/kotlin/0778-swim-in-rising-water.kt b/kotlin/0778-swim-in-rising-water.kt new file mode 100644 index 000000000..d9251b5e1 --- /dev/null +++ b/kotlin/0778-swim-in-rising-water.kt @@ -0,0 +1,27 @@ +class Solution { + fun swimInWater(grid: Array): Int { + val visited = Array(grid.size){ BooleanArray(grid[0].size) } + val minHeap = PriorityQueue{ a: IntArray, b: IntArray -> a[0] - b[0] } //[height, r, c] + val directions = arrayOf(intArrayOf(1,-1,0,0),intArrayOf(0,0,1,-1)) + minHeap.add(intArrayOf(grid[0][0],0,0)) //startpos + while(!minHeap.isEmpty()){ + val (h,r,c) = minHeap.poll() + visited[r][c] = true + if(r == grid.size-1 && c == grid[0].size-1) + return h + for(i in 0..3){ + val nextR = r + directions[0][i] + val nextC = c + directions[1][i] + if(isValid(grid,visited,nextR, nextC)) + minHeap.add(intArrayOf(maxOf(h,grid[nextR][nextC]),nextR,nextC)) + } + } + return -1 + } + private fun isValid( + grid: Array, + visited: Array, + r: Int, + c: Int + ) = r >= 0 && c >= 0 && r < grid.size && c < grid[0].size && visited[r][c] == false +} diff --git a/kotlin/0779-k-th-symbol-in-grammar.kt b/kotlin/0779-k-th-symbol-in-grammar.kt new file mode 100644 index 000000000..25affd0c3 --- /dev/null +++ b/kotlin/0779-k-th-symbol-in-grammar.kt @@ -0,0 +1,33 @@ +class Solution { + fun kthGrammar(n: Int, k: Int): Int { + var cur = 0 + var left = 1 + var right = 2.0.pow(n - 1).toInt() + + repeat (n - 1) { + val mid = (left + right) / 2 + if (k <= mid) { + right = mid + } else { + left = mid + 1 + cur = if (cur == 1) 0 else 1 + } + } + + return cur + } +} + +// another solution using the same thought but different way of coding it +class Solution { + fun kthGrammar(n: Int, k: Int): Int { + if (n == 1) return 0 + if (k % 2 == 0) return if (kthGrammar(n - 1, k / 2) == 0) 1 else 0 + else return if (kthGrammar(n - 1, (k + 1) / 2) == 0) 0 else 1 + } +} + +// another solution, recommend reading https://leetcode.com/problems/k-th-symbol-in-grammar/solutions/113705/java-one-line/ for explanation +class Solution { + fun kthGrammar(n: Int, k: Int) = Integer.bitCount(k - 1) and 1 +} diff --git a/kotlin/0783-minimum-distance-between-bst-nodes.kt b/kotlin/0783-minimum-distance-between-bst-nodes.kt new file mode 100644 index 000000000..eb178d8dd --- /dev/null +++ b/kotlin/0783-minimum-distance-between-bst-nodes.kt @@ -0,0 +1,32 @@ +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + + fun minDiffInBST(root: TreeNode?): Int { + + var res = Integer.MAX_VALUE + var prev = -1 //Values in range (0 to 10^5) + + fun dfs(root: TreeNode?) { + if(root == null) return + + dfs(root.left) + if(prev != -1) res = minOf(res, root.value - prev) + prev = root.value + dfs(root.right) + } + + dfs(root) + return res + } + + val TreeNode.value get() = this.`val` +} diff --git a/kotlin/0785-is-graph-bipartite.kt b/kotlin/0785-is-graph-bipartite.kt new file mode 100644 index 000000000..0be546215 --- /dev/null +++ b/kotlin/0785-is-graph-bipartite.kt @@ -0,0 +1,130 @@ +/* +* BFS +*/ +class Solution { + fun isBipartite(graph: Array): Boolean { + if (graph.isEmpty()) return true + val nodeColorMap = mutableMapOf() + + fun bfs(startNode: Int): Boolean { + // if the node is already colored, return true + if (startNode in nodeColorMap.keys) return true + nodeColorMap[startNode] = Color.RED + + val queue = (LinkedList() as Queue).apply { add(startNode) } + while (queue.isNotEmpty()) { + val currentNode = queue.remove() + val colorOfCurrentNode = nodeColorMap.getValue(currentNode) + for (adjacentNode in graph[currentNode]) { + if (adjacentNode in nodeColorMap.keys) { + val colorOfAdjacentNode = nodeColorMap.getValue(adjacentNode) + if (colorOfAdjacentNode == colorOfCurrentNode) return false + continue + + } + nodeColorMap[adjacentNode] = colorOfCurrentNode.inverse + queue.add(adjacentNode) + } + } + return true + } + + for (node in graph.indices) { + if (!bfs(node)) return false + } + + return true + } + + private enum class Color { RED, GREEN } + + private val Color.inverse + get() = when (this) { + Color.RED -> Color.GREEN + Color.GREEN -> Color.RED + } +} + +/* +* DFS +*/ +class Solution { + fun isBipartite(graph: Array): Boolean { + if (graph.isEmpty()) return true + + val nodeColorMap = mutableMapOf() + fun dfs(node: Int, defaultColor: Color): Boolean { + if (node !in nodeColorMap.keys) nodeColorMap[node] = defaultColor + for (adjacentNode in graph[node]) { + val isEdgeVisited = nodeColorMap[adjacentNode] != null + if (isEdgeVisited) { + val colorOfCurrentNode = nodeColorMap.getValue(node) + val colorOfAdjacentNode = nodeColorMap.getValue(adjacentNode) + if (colorOfAdjacentNode == colorOfCurrentNode) return false + else continue + } + if (!dfs(adjacentNode, defaultColor.inverse)) return false + } + return true + } + + for (node in graph.indices) { + if (node in nodeColorMap.keys) continue + if (!dfs(node, Color.RED)) return false + } + return true + } + + private enum class Color { RED, GREEN } + + private val Color.inverse + get() = when (this) { + Color.RED -> Color.GREEN + Color.GREEN -> Color.RED + } +} + +/* +* Union Find +*/ +class Solution { + + class DSU(val n: Int) { + + val parent = IntArray(n) { it } + val rank = IntArray(n) { 1 } + + fun find(x: Int): Int { + if (x != parent[x]) + parent[x] = find(parent[x]) + return parent[x] + } + + fun union(x: Int, y: Int) { + val px = find(x) + val py = find(y) + + if (rank[px] >= rank[py]) { + parent[py] = px + rank[px] += rank[py] + } else { + parent[px] = py + rank[py] += rank[px] + } + } + } + + fun isBipartite(graph: Array): Boolean { + val dsu = DSU(graph.size) + + for (i in 0 until graph.size) { + for (j in graph[i]) { + if (dsu.find(i) == dsu.find(j)) + return false + dsu.union(graph[i][0], j) + } + } + + return true + } +} diff --git a/kotlin/0787-cheapest-flights-within-k-stops.kt b/kotlin/0787-cheapest-flights-within-k-stops.kt new file mode 100644 index 000000000..67a75e921 --- /dev/null +++ b/kotlin/0787-cheapest-flights-within-k-stops.kt @@ -0,0 +1,21 @@ +class Solution { + fun findCheapestPrice(n: Int, flights: Array, src: Int, dst: Int, k: Int): Int { + var cost = IntArray(n){ Int.MAX_VALUE } + cost[src] = 0 + + for (i in 0..k) { + val temp = cost.clone() + + for (flight in flights) { + val (u, v, w) = flight + + if (cost[u] != Int.MAX_VALUE) + temp[v] = Math.min(temp[v], cost[u] + w) + } + + cost = temp + } + + return if (cost[dst] == Int.MAX_VALUE) -1 else cost[dst] + } +} \ No newline at end of file diff --git a/kotlin/0799-champagne-tower.kt b/kotlin/0799-champagne-tower.kt new file mode 100644 index 000000000..d6003bb83 --- /dev/null +++ b/kotlin/0799-champagne-tower.kt @@ -0,0 +1,19 @@ +class Solution { + fun champagneTower(poured: Int, query_row: Int, query_glass: Int): Double { + var prevRow = doubleArrayOf(poured.toDouble()) + + for (row in 1..query_row) { + var nextRow = DoubleArray (row + 1) + for (i in 0 until row) { + var leftOver = prevRow[i] - 1.0 + if (leftOver > 0) { + nextRow[i] += leftOver * 0.5 + nextRow[i + 1] += leftOver * 0.5 + } + } + prevRow = nextRow + } + + return minOf(1.0, prevRow[query_glass]) + } +} diff --git a/kotlin/0802-find-eventual-safe-states.kt b/kotlin/0802-find-eventual-safe-states.kt new file mode 100644 index 000000000..26e6b5db1 --- /dev/null +++ b/kotlin/0802-find-eventual-safe-states.kt @@ -0,0 +1,32 @@ +class Solution { + fun eventualSafeNodes(graph: Array): List { + val resultantList = mutableListOf() + // -1 -> non-safe node, 0 -> unknown, 1-> safe node + val safeState = IntArray(graph.size) + val visitedNode = hashSetOf() + + fun isSafe(currentNode: Int): Boolean { + visitedNode.add(currentNode) + for (adjacentNode in graph[currentNode]) { + if (safeState[adjacentNode] == 1) continue + if ( + adjacentNode in visitedNode || + safeState[adjacentNode] == -1 || + !isSafe(adjacentNode) + ) { + safeState[adjacentNode] = -1 + safeState[currentNode] = -1 + return false + } + } + safeState[currentNode] = 1 + return true + } + + for (node in graph.indices) { + if (safeState[node] == -1) continue + if (safeState[node] == 1 || isSafe(node)) resultantList.add(node) + } + return resultantList + } +} \ No newline at end of file diff --git a/kotlin/0837-new-21-game.kt b/kotlin/0837-new-21-game.kt new file mode 100644 index 000000000..6071f82fb --- /dev/null +++ b/kotlin/0837-new-21-game.kt @@ -0,0 +1,22 @@ +class Solution { + fun new21Game(n: Int, k: Int, maxPts: Int): Double { + if (k == 0 || k + maxPts <= n) + return 1.0 + + val dp = DoubleArray(k + maxPts) + var windowSum = 0.0 + + for (i in k until (k + maxPts)) + windowSum += if (i <= n) 1.0 else 0.0 + + for (i in k - 1 downTo 0) { + dp[i] = windowSum / maxPts + windowSum += dp[i] + if (i + maxPts <= n) { + windowSum -= if (dp[i + maxPts] != 0.0) dp[i + maxPts] else 1.0 + } + } + + return dp[0] + } +} diff --git a/kotlin/0838-push-dominoes.kt b/kotlin/0838-push-dominoes.kt new file mode 100644 index 000000000..8553c2e31 --- /dev/null +++ b/kotlin/0838-push-dominoes.kt @@ -0,0 +1,30 @@ +class Solution { + fun pushDominoes(d: String): String { + val domArr = d.toCharArray() + val q = LinkedList>() + + for ((i, v) in domArr.withIndex()) { + if (v != '.') + q.addLast(i to v) + } + + while (q.isNotEmpty()) { + val (i, v) = q.removeFirst() + if (v == 'L' && i > 0 && domArr[i - 1] == '.') { + q.addLast(i - 1 to 'L') + domArr[i - 1] = 'L' + } else if (v == 'R') { + if (i + 1 < domArr.size && domArr[i + 1] == '.') { + if (i + 2 < domArr.size && domArr[i + 2] == 'L') { + q.removeFirst() + } else { + q.addLast(i + 1 to 'R') + domArr[i + 1] = 'R' + } + } + } + } + + return String(domArr) + } +} diff --git a/kotlin/0844-backspace-string-compare.kt b/kotlin/0844-backspace-string-compare.kt new file mode 100644 index 000000000..96ee5490c --- /dev/null +++ b/kotlin/0844-backspace-string-compare.kt @@ -0,0 +1,97 @@ +// Two pointer solution, time O(n) and space O(1) +class Solution { + fun backspaceCompare(s: String, t: String): Boolean { + + fun nextValidChar(str: String, _index: Int): Int { + var backspace = 0 + var index = _index + while (index >= 0) { + if (backspace == 0 && str[index] != '#') + break + else if (str[index] == '#') + backspace++ + else + backspace-- + index-- + } + + return index + } + + var index_s = s.lastIndex + var index_t = t.lastIndex + while (index_s >= 0 || index_t >= 0) { + index_s = nextValidChar(s, index_s) + index_t = nextValidChar(t, index_t) + + val char_s = if (index_s >= 0) s[index_s] else '?' + val char_t = if (index_t >= 0) t[index_t] else '?' + if (char_s != char_t) + return false + index_s-- + index_t-- + } + + return true + } +} + +// Stack solution, time O(n) and space O(n) +class Solution { + fun backspaceCompare(s: String, t: String): Boolean { + val s1 = LinkedList() + val s2 = LinkedList() + + for (c in s) { + if (c == '#') { + if (s1.isNotEmpty()) + s1.removeLast() + } else { + s1.addLast(c) + } + } + + for (c in t) { + if (c == '#') { + if (s2.isNotEmpty()) + s2.removeLast() + } else { + s2.addLast(c) + } + } + + return s1.joinToString("") == s2.joinToString("") + } +} + +// Another style of two pointer solution, same idea, without calling methods, time O(n) and space O(1) +class Solution { + fun backspaceCompare(s: String, t: String): Boolean { + var i = s.lastIndex + var j = t.lastIndex + var steps = 0 + + while (true) { + steps = 0 + while (i >= 0 && (steps > 0 || s[i] == '#')) { + steps += if (s[i] == '#') 1 else -1 + i-- + } + + steps = 0 + while (j >= 0 && (steps > 0 || t[j] == '#')) { + steps += if (t[j] == '#') 1 else -1 + j-- + } + + if (i >= 0 && j >= 0 && s[i] == t[j]) { + i-- + j-- + } else { + break + } + } + + return i == -1 && j == -1 + } +} diff --git a/kotlin/0846-hand-of-straights.kt b/kotlin/0846-hand-of-straights.kt new file mode 100644 index 000000000..57aefc240 --- /dev/null +++ b/kotlin/0846-hand-of-straights.kt @@ -0,0 +1,28 @@ +class Solution { + fun isNStraightHand(hand: IntArray, groupSize: Int): Boolean { + if (hand.size % groupSize != 0) return false + val countMap = mutableMapOf() + hand.forEach { countMap[it] = countMap.getOrDefault(it, 0) + 1 } + val minHeap = PriorityQueue(countMap.keys) + + while (minHeap.isNotEmpty()) { + val minValue = minHeap.peek() + if (countMap.getValue(minValue) == 0) { + minHeap.remove() + continue + } + // loop through consecutive numbers starting from the "minValue" number + for (consecutiveNumber in minValue until (minValue + groupSize)) { + if ( + consecutiveNumber !in countMap.keys || + countMap.getValue(consecutiveNumber) == 0 + ) return false + countMap[consecutiveNumber] = countMap.getValue(consecutiveNumber) - 1 + } + // if the loop successfully executes without returning, it indicates that + // it was possible to create a group of size [groupSize] with minValue + // as the first element in the group. + } + return true + } +} \ No newline at end of file diff --git a/kotlin/0853-car-fleet.kt b/kotlin/0853-car-fleet.kt new file mode 100644 index 000000000..551dda153 --- /dev/null +++ b/kotlin/0853-car-fleet.kt @@ -0,0 +1,21 @@ +class Solution { + fun carFleet(target: Int, position: IntArray, speed: IntArray): Int { + val sortedPairs = position + .zip(speed) + .sortedBy { (position, _) -> position } + var numberOfFleets = 1 + var timeRequiredForCarInFrontToReachInTarget = + (target - sortedPairs[sortedPairs.lastIndex].first) / sortedPairs[sortedPairs.lastIndex].second.toFloat() + var timeRequiredForCurrentCarToReachTarget: Float + for (i in (sortedPairs.lastIndex - 1) downTo 0) { + timeRequiredForCurrentCarToReachTarget = (target - sortedPairs[i].first) / sortedPairs[i].second.toFloat() + if (timeRequiredForCurrentCarToReachTarget > timeRequiredForCarInFrontToReachInTarget) { + // the current car requires more time to reach the destination + // than the car in front of it. + numberOfFleets++ + timeRequiredForCarInFrontToReachInTarget = timeRequiredForCurrentCarToReachTarget + } + } + return numberOfFleets + } +} \ No newline at end of file diff --git a/kotlin/0867-transpose-matrix.kt b/kotlin/0867-transpose-matrix.kt new file mode 100644 index 000000000..80e318d7d --- /dev/null +++ b/kotlin/0867-transpose-matrix.kt @@ -0,0 +1,15 @@ +class Solution { + fun transpose(matrix: Array): Array { + val m = matrix.size + val n = matrix[0].size + val res = Array (n) { IntArray (m) } + + for (i in 0 until m) { + for (j in 0 until n) { + res[j][i] = matrix[i][j] + } + } + + return res + } +} diff --git a/kotlin/0872-leaf-similar-trees.kt b/kotlin/0872-leaf-similar-trees.kt new file mode 100644 index 000000000..eca2bdeba --- /dev/null +++ b/kotlin/0872-leaf-similar-trees.kt @@ -0,0 +1,22 @@ +class Solution { + fun leafSimilar(root1: TreeNode?, root2: TreeNode?): Boolean { + + fun dfs(root: TreeNode?, res: MutableList) { + root?: return + + if (root.left == null && root.right == null) + res.add(root.`val`) + + dfs(root.left, res) + dfs(root.right, res) + } + + val res1: MutableList = mutableListOf() + val res2: MutableList = mutableListOf() + + dfs(root1, res1) + dfs(root2, res2) + + return res1 == res2 + } +} diff --git a/kotlin/0875-koko-eating-bananas.kt b/kotlin/0875-koko-eating-bananas.kt new file mode 100644 index 000000000..abf927197 --- /dev/null +++ b/kotlin/0875-koko-eating-bananas.kt @@ -0,0 +1,35 @@ +class Solution { + fun minEatingSpeed(piles: IntArray, h: Int): Int { + var left = 1 + var right = 1 + + for (pile in piles) { + right = Math.max(right, pile) + } + + while (left < right) { + val mid = left + (right - left)/2 + + if (canEat(piles, h, mid)) { + right = mid + } else { + left = mid+1 + } + } + + return left + } + + fun canEat(piles: IntArray, h: Int, speed: Int): Boolean { + var sum = 0 + + for (pile in piles) { + sum += Math.ceil(pile/speed.toDouble()).toInt() + + if (sum > h) + return false + } + + return true + } +} \ No newline at end of file diff --git a/kotlin/0876-middle-of-the-linked-list.kt b/kotlin/0876-middle-of-the-linked-list.kt new file mode 100644 index 000000000..58e858a27 --- /dev/null +++ b/kotlin/0876-middle-of-the-linked-list.kt @@ -0,0 +1,21 @@ +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + // use two pointers, fast one that is 2x fast as slow, which will reach end when slow reaches mid + fun middleNode(head: ListNode?): ListNode? { + if(head == null) return null + var slow = head; var fast = head + while(fast != null && fast?.next != null){ + slow = slow?.next + fast = fast?.next?.next + } + return slow + } +} diff --git a/kotlin/0877-stone-game.kt b/kotlin/0877-stone-game.kt new file mode 100644 index 000000000..07121172d --- /dev/null +++ b/kotlin/0877-stone-game.kt @@ -0,0 +1,22 @@ +class Solution { + fun stoneGame(piles: IntArray): Boolean { + val dp = Array(piles.size) { IntArray(piles.size) { -1 } } + + fun dfs(left: Int, right: Int): Int { + if (left > right) + return 0 + if (dp[left][right] != -1) + return dp[left][right] + + val isEven = (right - left) % 2 == 0 + dp[left][right] = maxOf( + dfs(left + 1, right) + if (isEven) piles[left] else 0, + dfs(left, right - 1) + if (isEven) piles[right] else 0 + ) + + return dp[left][right] + } + + return dfs(0, piles.lastIndex) > (piles.sum()?: 0) / 2 + } +} diff --git a/kotlin/0879-profitable-schemes.kt b/kotlin/0879-profitable-schemes.kt new file mode 100644 index 000000000..982db3d4f --- /dev/null +++ b/kotlin/0879-profitable-schemes.kt @@ -0,0 +1,26 @@ +/* +* DFS with memoization +*/ +class Solution { + fun profitableSchemes(n: Int, minProfit: Int, group: IntArray, profit: IntArray): Int { + val mod = 1000000000 + 7 + val dp = Array(group.size) { Array(n + 1) { IntArray(minProfit + 1) {-1} } } + + fun dfs(i: Int, n: Int, p: Int): Int { + if (i == group.size) + return if (p >= minProfit) 1 else 0 + + val t = minOf(p, minProfit) + if (dp[i][n][t] != -1) + return dp[i][n][t] + + dp[i][n][t] = dfs(i + 1, n, t) % mod + if (n - group[i] >= 0) + dp[i][n][t] += dfs(i + 1, n - group[i], t + profit[i]) % mod + + return dp[i][n][t] + } + + return dfs(0, n, 0) % mod + } +} diff --git a/kotlin/0881-boats-to-save-people.kt b/kotlin/0881-boats-to-save-people.kt new file mode 100644 index 000000000..561755822 --- /dev/null +++ b/kotlin/0881-boats-to-save-people.kt @@ -0,0 +1,18 @@ +class Solution { + fun numRescueBoats(people: IntArray, limit: Int): Int { + people.sort() + + var left = 0 + var right = people.lastIndex + var res = 0 + + while (left <= right) { + if (people[left] + people[right] <= limit) + left++ + right-- + res++ + } + + return res + } +} diff --git a/kotlin/0894-all-possible-full-binary-trees.kt b/kotlin/0894-all-possible-full-binary-trees.kt new file mode 100644 index 000000000..378834d4a --- /dev/null +++ b/kotlin/0894-all-possible-full-binary-trees.kt @@ -0,0 +1,29 @@ +class Solution { + fun allPossibleFBT(n: Int): List { + val memo = HashMap>().apply { + put(0, ArrayList()) + put(1, arrayListOf(TreeNode(0))) + } + + fun dfs(n: Int): ArrayList { + if (memo.contains(n)) return memo[n]!! + + var res = ArrayList() + for (i in 0 until n) { + val left = dfs(i) + val right = dfs(n - 1 - i) + + for (n1 in left) { + for (n2 in right) { + res.add(TreeNode(0, n1, n2)) + } + } + } + + memo[n] = res + return res + } + + return dfs(n) + } +} diff --git a/kotlin/0895-maximum-frequency-stack.kt b/kotlin/0895-maximum-frequency-stack.kt new file mode 100644 index 000000000..1b34dd7a2 --- /dev/null +++ b/kotlin/0895-maximum-frequency-stack.kt @@ -0,0 +1,31 @@ +class FreqStack() { + + val countMap = HashMap() + var max = 0 + val stackMap = HashMap>() + + fun push(`val`: Int) { + val count = countMap.getOrDefault(`val`, 0) + 1 + countMap[`val`] = count + if (count > max) { + max = count + stackMap.put(max, ArrayDeque()) + } + stackMap[count]!!.addFirst(`val`) + } + + fun pop(): Int { + val res = stackMap[max]!!.removeFirst() + countMap[res] = countMap[res]!! - 1 + if(stackMap[max]!!.isEmpty()) max-- + return res + } + +} + +/** + * Your FreqStack object will be instantiated and called as such: + * var obj = FreqStack() + * obj.push(`val`) + * var param_2 = obj.pop() + */ diff --git a/kotlin/0896-monotonic-array.kt b/kotlin/0896-monotonic-array.kt new file mode 100644 index 000000000..7f8763de7 --- /dev/null +++ b/kotlin/0896-monotonic-array.kt @@ -0,0 +1,13 @@ +class Solution { + fun isMonotonic(nums: IntArray): Boolean { + if (nums[nums.lastIndex] - nums[0] < 0) + nums.reverse() + + for (i in 0 until nums.lastIndex) { + if (!(nums[i] <= nums[i + 1])) + return false + } + + return true + } +} diff --git a/kotlin/0901-online-stock-span.kt b/kotlin/0901-online-stock-span.kt new file mode 100644 index 000000000..e9fc218a6 --- /dev/null +++ b/kotlin/0901-online-stock-span.kt @@ -0,0 +1,13 @@ +class StockSpanner() { + val stack = LinkedList>() + + fun next(price: Int): Int { + var span = 1 + while (stack.isNotEmpty() && stack.peekLast().first <= price) { + span += stack.removeLast().second + } + stack.addLast(price to span) + return span + } + +} diff --git a/kotlin/0904-fruit-into-baskets.kt b/kotlin/0904-fruit-into-baskets.kt new file mode 100644 index 000000000..b880351e5 --- /dev/null +++ b/kotlin/0904-fruit-into-baskets.kt @@ -0,0 +1,28 @@ +class Solution { + fun totalFruit(fruits: IntArray): Int { + + val baskets = HashMap() + var left = 0 + var right = 0 + var max = 0 + + while (right < fruits.size) { + + baskets[fruits[right]] = baskets.getOrDefault(fruits[right], 0) + 1 + + while (baskets.size > 2) { + val fruit = fruits[left] + baskets[fruit] = baskets.getOrDefault(fruit, 0) - 1 + left++ + + if (baskets[fruit] == 0) + baskets.remove(fruit) + } + + max = maxOf(max, right - left + 1) + right++ + } + + return max + } +} diff --git a/kotlin/0905-sort-array-by-parity.kt b/kotlin/0905-sort-array-by-parity.kt new file mode 100644 index 000000000..0445beb95 --- /dev/null +++ b/kotlin/0905-sort-array-by-parity.kt @@ -0,0 +1,19 @@ +class Solution { + fun sortArrayByParity(nums: IntArray): IntArray { + var odd = nums.lastIndex + var i = 0 + + while (i < odd) { + if (nums[i] % 2 == 1) { + val temp = nums[i] + nums[i] = nums[odd] + nums[odd] = temp + odd-- + } else { + i++ + } + } + + return nums + } +} diff --git a/kotlin/0907-sum-of-subarray-minimums.kt b/kotlin/0907-sum-of-subarray-minimums.kt new file mode 100644 index 000000000..ec8137b30 --- /dev/null +++ b/kotlin/0907-sum-of-subarray-minimums.kt @@ -0,0 +1,44 @@ +class Solution { + fun sumSubarrayMins(_arr: IntArray): Int { + val mod = 1_000_000_007 + var res = 0L + val arr = intArrayOf(Integer.MIN_VALUE) + _arr + intArrayOf(Integer.MIN_VALUE) + val stack = Stack>() + + for ((i, n) in arr.withIndex()) { + while (stack.isNotEmpty() && n < stack.peek().second) { + val (j, m) = stack.pop() + val left = if (stack.isNotEmpty()) j - stack.peek().first else j + 1 + val right = i - j + res = (res + m.toLong() * left * right) % mod + } + stack.push(i to n) + } + + return res.toInt() + } +} + +// Another and just a little different solution, you can also only push indices to the stack +class Solution { + fun sumSubarrayMins(arr: IntArray): Int { + val stack = LinkedList() + val mod = 1_000_000_007 + var res = 0L + + for (right in 0..arr.size) { + val curVal = if (right < arr.size) arr[right] else 0 + + while (stack.isNotEmpty() && curVal < arr[stack.peekLast()]) { + val cur = stack.removeLast() + val left = stack.peekLast() ?: -1 + val noOfSubArrs = (cur.toLong() - left) * (right - cur) + res = (res + noOfSubArrs * arr[cur]) % mod + } + + stack.addLast(right) + } + + return res.toInt() + } +} diff --git a/kotlin/0909-snakes-and-ladders.kt b/kotlin/0909-snakes-and-ladders.kt new file mode 100644 index 000000000..726585d6f --- /dev/null +++ b/kotlin/0909-snakes-and-ladders.kt @@ -0,0 +1,37 @@ +class Solution { + fun snakesAndLadders(board: Array): Int { + val len = board.size + board.reverse() + + fun intToPos(square: Int): Pair { + val r = (square - 1) / len + var c = (square - 1) % len + if (r % 2 == 1) + c = len - 1 - c + return r to c + } + + val q = LinkedList>() + q.addFirst(1 to 0) + val visited = HashSet() + while (q.isNotEmpty()) { + val (square, moves) = q.removeLast() + + for (i in 1..6) { + var next = i + square + val (r, c) = intToPos(next) + if (board[r][c] != -1) + next = board[r][c] + if (next == len * len) + return moves + 1 + if (next !in visited) { + visited.add(next) + q.addFirst(next to moves + 1) + } + + } + } + + return -1 + } +} diff --git a/kotlin/0912-sort-an-array.kt b/kotlin/0912-sort-an-array.kt new file mode 100644 index 000000000..b8115cdc0 --- /dev/null +++ b/kotlin/0912-sort-an-array.kt @@ -0,0 +1,135 @@ +/* +* Merge sort +*/ +class Solution { + fun sortArray(nums: IntArray): IntArray { + mergeSort(nums, 0, nums.lastIndex) + return nums + } + + private fun mergeSort(nums: IntArray, left: Int, right: Int) { + if(left == right) return + + val mid = (left + right) / 2 + mergeSort(nums, left, mid) + mergeSort(nums, mid + 1, right) + merge(nums, left, mid, right) + + return + } + + private fun merge(nums: IntArray, left: Int, mid: Int, right: Int) { + val leftPart = nums.copyOfRange(left, mid + 1) + val rightPart = nums.copyOfRange(mid + 1, right + 1) + var i = left + var j = 0 + var k = 0 + + while(j < leftPart.size && k < rightPart.size) { + if(leftPart[j] <= rightPart[k]) { + nums[i] = leftPart[j] + j++ + }else{ + nums[i] = rightPart[k] + k++ + } + i++ + } + + while(j < leftPart.size) { + nums[i] = leftPart[j] + j++ + i++ + } + + while(k < rightPart.size) { + nums[i] = rightPart[k] + k++ + i++ + } + } +} + +/* +* Quick sort +*/ +class Solution { + fun sortArray(nums: IntArray): IntArray { + + quickSort(nums, 0, nums.lastIndex) + return nums + } + + private fun quickSort(nums: IntArray, low: Int, high: Int) { + if (low < high) { + val pivotIndex = partition(nums, low, high) + quickSort(nums, low, pivotIndex - 1) + quickSort(nums, pivotIndex + 1, high) + } + } + + private fun partition(nums: IntArray, low: Int, high: Int): Int { + val r = (low..high).random() + nums.swap(r, high) + val pivot = nums[high] + var i = low + + for (j in low until high) { + if (nums[j] <= pivot) { + nums.swap(i, j) + i++ + } + } + + nums.swap(i, high) + return i + } + + fun IntArray.swap(i: Int, j: Int) { + this[i] = this[j].also { this[j] = this[i] } + } +} + +/* +* Heap sort +*/ +class Solution { + fun sortArray(nums: IntArray): IntArray { + + heapSort(nums) + return nums + } + + private fun heapSort(nums: IntArray) { + val n = nums.size + + for(i in (n/2 - 1) downTo 0) + heapify(nums, n, i) + + for(i in n-1 downTo 0) { + nums.swap(0, i) + heapify(nums, i, 0) + } + } + + private fun heapify(nums: IntArray, n: Int, i: Int) { + var largest = i + + val left = 2 * i + 1 + val right = 2 * i + 2 + + if(left < n && nums[left] > nums[largest]) + largest = left + if(right < n && nums[right] > nums[largest]) + largest = right + + if(largest != i) { + nums.swap(i, largest) + heapify(nums, n, largest) + } + } + + fun IntArray.swap(i: Int, j: Int) { + this[i] = this[j].also{ this[j] = this[i] } + } +} diff --git a/kotlin/0918-maximum-sum-circular-subarray.kt b/kotlin/0918-maximum-sum-circular-subarray.kt new file mode 100644 index 000000000..7faac5b35 --- /dev/null +++ b/kotlin/0918-maximum-sum-circular-subarray.kt @@ -0,0 +1,22 @@ +class Solution { + fun maxSubarraySumCircular(nums: IntArray): Int { + if (nums.size == 1) return nums[0] + // max sum + var totalSum = nums[0] + var maxSum = nums[0] + var currentMaxSum = nums[0] + // minSum + var minSum = nums[0] + var currentMinSum = nums[0] + for (i in 1..nums.lastIndex) { + // max sum computation + currentMaxSum = maxOf(nums[i], currentMaxSum + nums[i]) + maxSum = maxOf(maxSum, currentMaxSum) + // minSum computation + currentMinSum = minOf(nums[i], currentMinSum + nums[i]) + minSum = minOf(minSum, currentMinSum) + totalSum += nums[i] + } + return if (minSum != totalSum) maxOf((totalSum - minSum), maxSum) else maxSum + } +} \ No newline at end of file diff --git a/kotlin/0920-number-of-music-playlists.kt b/kotlin/0920-number-of-music-playlists.kt new file mode 100644 index 000000000..ef3a138cc --- /dev/null +++ b/kotlin/0920-number-of-music-playlists.kt @@ -0,0 +1,23 @@ +class Solution { + fun numMusicPlaylists(n: Int, goal: Int, k: Int): Int { + val cache = Array (n + 1) { LongArray (goal + 1) { -1 } } + val mod = 1000000007 + + fun count(song: Int, left: Int): Long { + if (left == 0) + return if (song == n) 1L else 0L + if (cache[song][left] != -1L) return cache[song][left] + + var res = 0L + if (song + 1 <= n) + res += (n - song) * count(song + 1, left - 1) + if (song - k > 0) + res += (song - k) * count(song, left - 1) + + cache[song][left] = res % mod + return cache[song][left] + } + + return count(0, goal).toInt() + } +} diff --git a/kotlin/0926-flip-string-to-monotone-increasing.kt b/kotlin/0926-flip-string-to-monotone-increasing.kt new file mode 100644 index 000000000..41cb00bd0 --- /dev/null +++ b/kotlin/0926-flip-string-to-monotone-increasing.kt @@ -0,0 +1,15 @@ +// optimal solution +class Solution { + fun minFlipsMonoIncr(s: String): Int { + + var noOfOnes = 0 + var noOfFlips = 0 + + for (bit in s) { + if (bit == '1') noOfOnes++ + else noOfFlips = minOf(noOfOnes, noOfFlips + 1) + } + + return minOf(noOfOnes, noOfFlips) + } +} diff --git a/kotlin/0929-unique-email-addresses.kt b/kotlin/0929-unique-email-addresses.kt new file mode 100644 index 000000000..9fd2169ba --- /dev/null +++ b/kotlin/0929-unique-email-addresses.kt @@ -0,0 +1,17 @@ +class Solution { + fun numUniqueEmails(emails: Array): Int { + val hset = HashSet() + for(email in emails) { + var i = 0 + var res = "" + while(email[i] != '+' && email[i] != '@'){ + if(email[i] != '.') res += email[i] + i++ + } + while(email[i] != '@') i++ + res += email.substring(i,email.length) + hset.add(res) + } + return hset.size + } +} diff --git a/kotlin/0930-binary-subarrays-with-sum.kt b/kotlin/0930-binary-subarrays-with-sum.kt new file mode 100644 index 000000000..1fe20e82c --- /dev/null +++ b/kotlin/0930-binary-subarrays-with-sum.kt @@ -0,0 +1,24 @@ +class Solution { + fun numSubarraysWithSum(nums: IntArray, goal: Int): Int { + + fun helper(x: Int): Int { + if (x < 0) return 0 + + var res = 0 + var l = 0 + var cur = 0 + for (r in 0 until nums.size) { + cur += nums[r] + + while (cur > x) + cur -= nums[l++] + + res += (r - l + 1) + } + + return res + } + + return helper(goal) - helper(goal - 1) + } +} diff --git a/kotlin/0931-minimum-falling-path-sum.kt b/kotlin/0931-minimum-falling-path-sum.kt new file mode 100644 index 000000000..d10125ad5 --- /dev/null +++ b/kotlin/0931-minimum-falling-path-sum.kt @@ -0,0 +1,16 @@ +class Solution { + fun minFallingPathSum(m: Array): Int { + val n = m.size + for (i in 1 until n) { + for (j in 0 until n) { + m[i][j] += minOf( + if (j > 0) m[i - 1][j - 1] else 10001, + m[i - 1][j], + if (j + 1 < n) m[i - 1][j + 1] else 10001, + ) + } + } + + return m[n - 1].min()!! + } +} diff --git a/kotlin/0934-shortest-bridge.kt b/kotlin/0934-shortest-bridge.kt new file mode 100644 index 000000000..2906b7e03 --- /dev/null +++ b/kotlin/0934-shortest-bridge.kt @@ -0,0 +1,52 @@ +class Solution { + + fun shortestBridge(grid: Array): Int { + // q = [ (row,column,level) ] + val queue: Queue> = LinkedList() + val rowColumnDirections = arrayOf( + intArrayOf(0, 1), + intArrayOf(0, -1), + intArrayOf(1, 0), + intArrayOf(-1, 0) + ) + + fun dfs(startRow: Int, startColumn: Int) { + if ( + startRow !in grid.indices || + startColumn !in grid[startRow].indices || + grid[startRow][startColumn] == 0 || + grid[startRow][startColumn] == VISITED_CELL + ) return + grid[startRow][startColumn] = VISITED_CELL + queue.add(Triple(startRow, startColumn, 0)) + for ((rowDir, columDir) in rowColumnDirections) { + dfs(startRow + rowDir, startColumn + columDir) + } + } + // find one island and mark the cells with -1 + outer@ for (row in grid.indices) { + inner@ for (column in grid[row].indices) { + if (grid[row][column] != 1) continue + dfs(row, column) + break@outer + } + } + while (queue.isNotEmpty()) { + val (row, column, level) = queue.remove() + for ((rowDir, colDir) in rowColumnDirections) { + if (row + rowDir !in grid.indices) continue + if (column + colDir !in grid[row + rowDir].indices) continue + if (grid[row + rowDir][column + colDir] == VISITED_CELL) continue + if (grid[row + rowDir][column + colDir] == 1) return level + queue.add(Triple(row + rowDir, column + colDir, level + 1)) + grid[row + rowDir][column + colDir] = VISITED_CELL + } + } + throw IllegalStateException("Two islands where expected to be in the grid.") + } + + companion object { + private const val VISITED_CELL = -1 + } +} + diff --git a/kotlin/0935-knight-dialer.kt b/kotlin/0935-knight-dialer.kt new file mode 100644 index 000000000..1fbb8e5dc --- /dev/null +++ b/kotlin/0935-knight-dialer.kt @@ -0,0 +1,107 @@ +// Alternative solution +class Solution { + fun knightDialer(n: Int): Int { + if (n == 1) return 10 + + val mod = 1_000_000_007 + var moves = longArrayOf(1L, 4L, 2L, 2L) + + for(i in 0 until n - 1) { + val temp = LongArray (4) + temp[0] = moves[3] + temp[1] = (2 * moves[2] + 2 * moves[3]) % mod + temp[2] = moves[1] + temp[3] = (2 * moves[0] + moves[1]) % mod + moves = temp + } + + var sum = 0L + for (num in moves) { + sum += num + sum %= mod + } + + return sum.toInt() + } +} + +// DP +class Solution { + fun knightDialer(n: Int): Int { + val mod = 1_000_000_007 + val moves = mapOf( + 1 to listOf(6, 8), + 2 to listOf(7, 9), + 3 to listOf(4, 8), + 4 to listOf(3, 9, 0), + 5 to listOf(), + 6 to listOf(1, 7, 0), + 7 to listOf(2, 6), + 8 to listOf(1, 3), + 9 to listOf(4, 2), + 0 to listOf(4, 6) + ) + + var dp = LongArray (10) { 1 } + for (i in 0 until (n - 1)) { + val nextDP = LongArray (10) + for (n in 0 until 10) { + moves[n]?.forEach { j -> + nextDP[j] = (nextDP[j] + dp[n]) % mod + } + } + dp = nextDP + } + + var sum = 0L + for (num in dp) { + sum += num + sum %= mod + } + + return sum.toInt() + } +} + +// recursion + memoization +class Solution { + fun knightDialer(n: Int): Int { + val mod = 1_000_000_007 + val moves = mapOf( + 1 to listOf(6, 8), + 2 to listOf(7, 9), + 3 to listOf(4, 8), + 4 to listOf(3, 9, 0), + 5 to listOf(), + 6 to listOf(1, 7, 0), + 7 to listOf(2, 6), + 8 to listOf(1, 3), + 9 to listOf(4, 2), + 0 to listOf(4, 6) + ) + + val dp = Array (n + 1) { LongArray (10) { -1L } } + + fun dfs(n: Int, num: Int): Long { + if (n == 0) return 1 + if (dp[n][num] != -1L) return dp[n][num] + + var res = 0L + moves[num]?.forEach { next -> + res += dfs(n - 1, next) + res %= mod + } + + dp[n][num] = res + return res + } + + var res = 0L + for (num in 0..9) { + res += dfs(n - 1, num) + res %= mod + } + + return res.toInt() + } +} diff --git a/kotlin/0938-range-sum-of-bst.kt b/kotlin/0938-range-sum-of-bst.kt new file mode 100644 index 000000000..704cc9cce --- /dev/null +++ b/kotlin/0938-range-sum-of-bst.kt @@ -0,0 +1,11 @@ +class Solution { + fun rangeSumBST(root: TreeNode?, low: Int, high: Int): Int { + root?: return 0 + + return if (root.`val` > high) rangeSumBST(root.left, low, high) + else if (root.`val` < low) rangeSumBST(root.right, low, high) + else root.`val` + + rangeSumBST(root.left, low, high) + + rangeSumBST(root.right, low, high) + } +} diff --git a/kotlin/0946-validate-stack-sequences.kt b/kotlin/0946-validate-stack-sequences.kt new file mode 100644 index 000000000..4a98aeabc --- /dev/null +++ b/kotlin/0946-validate-stack-sequences.kt @@ -0,0 +1,17 @@ +class Solution { + fun validateStackSequences(pushed: IntArray, popped: IntArray): Boolean { + var i = 0 + var stack = LinkedList() + + for (n in pushed) { + stack.addLast(n) + + while (i < popped.size && popped[i] == stack.peekLast()) { + stack.removeLast() + i++ + } + } + + return stack.isEmpty() + } +} diff --git a/kotlin/0948-bag-of-tokens.kt b/kotlin/0948-bag-of-tokens.kt new file mode 100644 index 000000000..1ef5347dc --- /dev/null +++ b/kotlin/0948-bag-of-tokens.kt @@ -0,0 +1,26 @@ +class Solution { + fun bagOfTokensScore(tokens: IntArray, power: Int): Int { + tokens.sort() + + var res = 0 + var score = 0 + var power = power + + var left = 0 + var right = tokens.lastIndex + while (left <= right) { + if (power >= tokens[left]) { + power -= tokens[left++] + score++ + res = maxOf(res, score) + } else if (score > 0) { + power += tokens[right--] + score-- + } else { + break + } + } + + return res + } +} diff --git a/kotlin/0951-flip-equivalent-binary-trees.kt b/kotlin/0951-flip-equivalent-binary-trees.kt new file mode 100644 index 000000000..bb848f0ab --- /dev/null +++ b/kotlin/0951-flip-equivalent-binary-trees.kt @@ -0,0 +1,16 @@ +class Solution { + fun flipEquiv(r1: TreeNode?, r2: TreeNode?): Boolean { + if (r1 == null || r2 == null) { + return if (r1 == null && r2 == null) true else false + } + if (r1.value != r2.value) + return false + + val a = flipEquiv(r1.left, r2.left) && flipEquiv(r1.right, r2.right) + return a ||flipEquiv(r1.left, r2.right) && flipEquiv(r1.right, r2.left) + + } + + val TreeNode.value + get()= this.`val` +} diff --git a/kotlin/0953-verifying-an-alien-dictionary.kt b/kotlin/0953-verifying-an-alien-dictionary.kt new file mode 100644 index 000000000..06268c781 --- /dev/null +++ b/kotlin/0953-verifying-an-alien-dictionary.kt @@ -0,0 +1,21 @@ +class Solution { + fun isAlienSorted(words: Array, order: String): Boolean { + val hm = HashMap() + for((i, c) in order.withIndex()) + hm[c] = i + for(i in 0 until words.size-1){ + val firstW = words[i] + val secondW = words[i+1] + for(j in 0 until firstW.length){ + if(j == secondW.length) // if first word is longer than second word + return false + if(firstW[j] != secondW[j]){ + if(hm[secondW[j]]!! < hm[firstW[j]]!!) //if char is not sorted lexicographically, char j in first must come before char j in second + return false + break // else if first word comes before second + } + } + } + return true // if all words were sorted lexicographically + } +} diff --git a/kotlin/0958-check-completeness-of-a-binary-tree.kt b/kotlin/0958-check-completeness-of-a-binary-tree.kt new file mode 100644 index 000000000..f6c6b08e1 --- /dev/null +++ b/kotlin/0958-check-completeness-of-a-binary-tree.kt @@ -0,0 +1,34 @@ +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun isCompleteTree(root: TreeNode?): Boolean { + + val q = LinkedList().apply{ + this.addFirst(root) + } + + while(q.isNotEmpty()) { + val node = q.removeLast() + + if(node != null) { + q.addFirst(node.left) + q.addFirst(node.right) + } else { + while(q.isNotEmpty()) { + if(q.removeLast() != null) + return false + } + } + } + + return true + } +} diff --git a/kotlin/0973-k-closest-points-to-origin.kt b/kotlin/0973-k-closest-points-to-origin.kt new file mode 100644 index 000000000..e838dbfe5 --- /dev/null +++ b/kotlin/0973-k-closest-points-to-origin.kt @@ -0,0 +1,105 @@ +/** +Solution using min heap + */ +class Solution { + fun kClosest(points: Array, k: Int): Array { + val minHeap = PriorityQueue { a, b -> a[0] - b[0] } + val result = Array(k) { IntArray(2) { 0 } } + + for (point in points) { + minHeap.add( + intArrayOf( + /* distance from (0,0) */ point[0].squared() + point[1].squared(), + /* x coordinate */ point[0], + /* y coordinate */ point[1] + ) + ) + } + + for (i in 0 until k) { + val pointWithDistance = minHeap.poll() + result[i][0] = pointWithDistance[1] + result[i][1] = pointWithDistance[2] + } + + return result + } + + private fun Int.squared() = this * this +} + +/** +Solution using a max Heap + */ + class Solution { + fun kClosest(points: Array, k: Int): Array { + val maxHeap = PriorityQueue{ e1, e2 -> e2[0] - e1[0] } + val res = Array(k){ IntArray(2) } + for(point in points){ + val (x,y) = point + val distance = (x * x) + (y * y) // we don't need to sqrt since the actual length is of no use + maxHeap.add(intArrayOf(distance,x,y)) + if(maxHeap.size > k) // keep only the K closest distances + maxHeap.poll() + } + + for(i in res.indices){ + val (d,x,y) = maxHeap.poll() + res[i] = intArrayOf(x,y) + } + return res + } +} + + /** +Solution using QuickSelect + */ + class Solution { + fun kClosest(points: Array, k: Int): Array { + if(points.size == k) + return points + val res = Array(k){ IntArray(2) } + quickSelect(0, points.size-1,points,k) + for(i in res.indices){ + res[i] = points[i] + } + return res + } + private fun quickSelect(l: Int, r: Int, points: Array, k: Int){ + var lPointer = l + for(i in l until r){ + if(distance(i, points) <= distance(r,points)){ //r is pivot + swap(i,lPointer,points) + lPointer++ + } + } + swap(lPointer,r,points) + if(lPointer > k) + quickSelect(l, lPointer-1, points, k) + else if(lPointer < k) + quickSelect(lPointer+1, r, points, k) + else //lPointer == k + return + } + private fun swap(i: Int, j: Int, points: Array){ + val temp = points[i] + points[i] = points[j] + points[j] = temp + } + private fun distance(i: Int, points: Array) = (points[i][0] * points[i][0]) + (points[i][1] * points[i][1]) +} + + +/** +Solution using built in sort function + */ +class Solution { + fun kClosest(points: Array, k: Int): Array { + val sorted = points.sortedBy{ it[0]*it[0] + it[1]*it[1]} + val list = arrayListOf() + for (i in 0..k-1) { + list.add(sorted[i]) + } + return list.toTypedArray() + } +} diff --git a/kotlin/0977-squares-of-a-sorted-array.kt b/kotlin/0977-squares-of-a-sorted-array.kt new file mode 100644 index 000000000..094243726 --- /dev/null +++ b/kotlin/0977-squares-of-a-sorted-array.kt @@ -0,0 +1,20 @@ +class Solution { + fun sortedSquares(nums: IntArray): IntArray { + var left = 0; var right = nums.size-1; var end = nums.size-1 + val rArray = IntArray(nums.size) + while(left <= right){ // or while(end >= 0) + val ls = nums[left] * nums[left] + val rs = nums[right] * nums[right] + if(ls > rs){ + rArray[end] = ls + left++ + }else{ + //rs > ls + rArray[end] = rs + right-- + } + end-- + } + return rArray + } +} diff --git a/kotlin/0978-longest-turbulent-subarray.kt b/kotlin/0978-longest-turbulent-subarray.kt new file mode 100644 index 000000000..9d9c407d8 --- /dev/null +++ b/kotlin/0978-longest-turbulent-subarray.kt @@ -0,0 +1,30 @@ +class Solution { + fun maxTurbulenceSize(arr: IntArray): Int { + if (arr.size == 1) return 1 + var previousEqualitySymbol = ' ' + var currentSubArraySize = 1 + var maxSubArraySize = 1 + for (i in 1..arr.lastIndex) { + if (arr[i - 1] > arr[i] && previousEqualitySymbol != '>') { + currentSubArraySize++ + maxSubArraySize = maxOf(maxSubArraySize, currentSubArraySize) + previousEqualitySymbol = '>' + continue + } + if (arr[i - 1] < arr[i] && previousEqualitySymbol != '<') { + currentSubArraySize++ + maxSubArraySize = maxOf(maxSubArraySize, currentSubArraySize) + previousEqualitySymbol = '<' + continue + } + currentSubArraySize = if (arr[i - 1] == arr[i]) 1 else 2 + maxSubArraySize = maxOf(maxSubArraySize, currentSubArraySize) + previousEqualitySymbol = when { + arr[i - 1] < arr[i] -> '<' + arr[i - 1] > arr[i] -> '>' + else -> ' ' + } + } + return maxSubArraySize + } +} \ No newline at end of file diff --git a/kotlin/0981-time-based-key-value-store.kt b/kotlin/0981-time-based-key-value-store.kt new file mode 100644 index 000000000..44d742b35 --- /dev/null +++ b/kotlin/0981-time-based-key-value-store.kt @@ -0,0 +1,47 @@ +class TimeMap { + private data class TimeStampValuePair( + val timestamp: Int, + val value: String + ) + + private val keyMap = mutableMapOf>() + + fun set(key: String, value: String, timestamp: Int) { + keyMap.getOrPut(key, ::mutableListOf).add(TimeStampValuePair(timestamp, value)) + } + + fun get(key: String, timestamp: Int): String { + if (key !in keyMap.keys) return "" + val searchList = keyMap.getValue(key) + if (searchList.isEmpty()) return "" + return binarySearch( + list = searchList, + targetTimeStamp = timestamp + ) + } + + private fun binarySearch( + list: List, + targetTimeStamp: Int + ): String { + if (list.first().timestamp > targetTimeStamp) return "" + if (list.last().timestamp < targetTimeStamp) return list.last().value + var startIndex = 0 + var endIndex = list.lastIndex + var midIndex: Int + var result = "" + while (startIndex <= endIndex) { + midIndex = startIndex + (endIndex - startIndex) / 2 + when { + list[midIndex].timestamp <= targetTimeStamp -> { + if (list[midIndex].timestamp == targetTimeStamp) return list[midIndex].value + result = list[midIndex].value + startIndex = midIndex + 1 + } + + else -> endIndex = midIndex - 1 + } + } + return result + } +} \ No newline at end of file diff --git a/kotlin/0983-minimum-cost-for-tickets.kt b/kotlin/0983-minimum-cost-for-tickets.kt new file mode 100644 index 000000000..9b694a738 --- /dev/null +++ b/kotlin/0983-minimum-cost-for-tickets.kt @@ -0,0 +1,51 @@ +/* +* DFS solution +*/ +class Solution { + + fun mincostTicketsRecursive(days: IntArray, costs: IntArray): Int { + val dp = mutableMapOf() + + fun dfs(dayIndex: Int): Int { + if (dayIndex == days.size) return 0 + if (dayIndex in dp) return dp[dayIndex]!! + + dp[dayIndex] = Int.MAX_VALUE + for ((daysCount, cost) in intArrayOf(1, 7, 30).zip(costs)) { + var nextDayIndex = dayIndex + while (nextDayIndex < days.size && days[nextDayIndex] < days[dayIndex] + daysCount) + nextDayIndex++ + + dp[dayIndex] = min(dp[dayIndex]!!, cost + dfs(nextDayIndex)) + } + + return dp[dayIndex]!! + } + + return dfs(0) + } +} + +/* +* BFS Solution +*/ +class Solution { + + fun mincostTickets(days: IntArray, costs: IntArray): Int { + val dp = mutableMapOf() + + for (dayIndex in days.indices.reversed()) { + dp[dayIndex] = Int.MAX_VALUE + + for ((daysCount, cost) in intArrayOf(1, 7, 30).zip(costs)) { + var nextDayIndex = dayIndex + while (nextDayIndex < days.size && days[nextDayIndex] < days[dayIndex] + daysCount) + nextDayIndex++ + + dp[dayIndex] = min(dp[dayIndex]!!, cost + dp.getOrDefault(nextDayIndex, 0)) + } + } + + return dp[0]!! + } +} diff --git a/kotlin/0989-add-to-array-form-of-integer.kt b/kotlin/0989-add-to-array-form-of-integer.kt new file mode 100644 index 000000000..0775c6d38 --- /dev/null +++ b/kotlin/0989-add-to-array-form-of-integer.kt @@ -0,0 +1,23 @@ +class Solution { + fun addToArrayForm(num: IntArray, k: Int): List { + var carry = 0 + var toAdd = k + val res = ArrayList() + var pointer = num.size-1 + while(pointer >= 0 || toAdd > 0 || carry != 0) { + val rightMostMask = toAdd % 10 + var temp = rightMostMask + carry + if(pointer >= 0) + temp += num[pointer] + carry = 0 + if(temp >= 10){ + temp = temp % 10 + carry = 1 + } + res.add(0, temp) + toAdd = toAdd / 10 + pointer-- + } + return res + } +} diff --git a/kotlin/0992-subarrays-with-k-different-integers.kt b/kotlin/0992-subarrays-with-k-different-integers.kt new file mode 100644 index 000000000..085b63e1d --- /dev/null +++ b/kotlin/0992-subarrays-with-k-different-integers.kt @@ -0,0 +1,28 @@ +class Solution { + fun subarraysWithKDistinct(nums: IntArray, k: Int): Int { + val count = HashMap() + var res = 0 + + var l = 0 + var m = 0 + for (r in 0 until nums.size) { + count[nums[r]] = (count[nums[r]] ?: 0) + 1 + + while (count.size > k) { + count[nums[m]] = (count[nums[m]] ?: 0) - 1 + if ((count[nums[m]] ?: 0) == 0) count.remove(nums[m]) + m++ + l = m + } + + while ((count[nums[m]] ?: 0) > 1) { + count[nums[m]] = (count[nums[m]] ?: 0) - 1 + m++ + } + + if (count.size == k) res += (m - l + 1) + } + + return res + } +} diff --git a/kotlin/0994-rotting-oranges.kt b/kotlin/0994-rotting-oranges.kt new file mode 100644 index 000000000..78f377771 --- /dev/null +++ b/kotlin/0994-rotting-oranges.kt @@ -0,0 +1,52 @@ +class Solution { + fun orangesRotting(grid: Array): Int { + val m = grid.size + val n = grid[0].size + var fresh = 0 + var minute = -1 + var queue = LinkedList() + val dirs = arrayOf(intArrayOf(-1,0),intArrayOf(1,0),intArrayOf(0,1),intArrayOf(0,-1)) + + for (i in 0..m-1) { + for (j in 0..n-1) { + val curr = grid[i][j] + + if (curr == 2) + queue.add(intArrayOf(i, j)) + else if (curr == 1) + fresh++ + } + } + + var temp = LinkedList() + + while (queue.size != 0) { + val curr = queue.poll() + + for (dir in dirs) { + val x = curr[0] + dir[0] + val y = curr[1] + dir[1] + + if (x < 0 || x >= m || y < 0 || y >= n) + continue + + if (grid[x][y] == 1) { + temp.add(intArrayOf(x, y)) + fresh-- + grid[x][y] = 2 + } + } + + if (queue.size == 0) { + minute++ + queue = temp + temp = LinkedList() + } + } + + if (fresh != 0) + return -1 + + return if (minute == -1) 0 else minute + } +} \ No newline at end of file diff --git a/kotlin/0997-find-the-town-judge.kt b/kotlin/0997-find-the-town-judge.kt new file mode 100644 index 000000000..28bd85313 --- /dev/null +++ b/kotlin/0997-find-the-town-judge.kt @@ -0,0 +1,17 @@ +class Solution { + fun findJudge(n: Int, trust: Array): Int { + val delta = IntArray (n + 1) + + for ((src, dst) in trust) { + delta[dst]++ + delta[src]-- + } + + for (i in 1..n) { + if (delta[i] == n - 1) + return i + } + + return -1 + } +} diff --git a/kotlin/1011-capacity-to-ship-packages-within-d-days.kt b/kotlin/1011-capacity-to-ship-packages-within-d-days.kt new file mode 100644 index 000000000..3442247d3 --- /dev/null +++ b/kotlin/1011-capacity-to-ship-packages-within-d-days.kt @@ -0,0 +1,35 @@ +class Solution { + fun shipWithinDays(weights: IntArray, days: Int): Int { + + var left = Integer.MIN_VALUE + var right = 0 + + for(w in weights) { + left = maxOf(left, w) + right += w + } + + fun canShip(cap: Int): Boolean { + var currentCap = cap + var ships = 1 + for(w in weights) { + if(currentCap - w < 0){ + ships++ + currentCap = cap + } + currentCap -= w + } + return ships <= days + } + + while(left <= right) { + val mid = (left + right) / 2 + if(canShip(mid)) + right = mid - 1 + else + left = mid + 1 + } + + return left + } +} diff --git a/kotlin/1020-number-of-enclaves.kt b/kotlin/1020-number-of-enclaves.kt new file mode 100644 index 000000000..ae7aade0f --- /dev/null +++ b/kotlin/1020-number-of-enclaves.kt @@ -0,0 +1,52 @@ +class Solution { + fun numEnclaves(grid: Array): Int { + val rows = grid.size + val cols = grid[0].size + val dirs = arrayOf( + intArrayOf(1, 0), + intArrayOf(-1, 0), + intArrayOf(0, 1), + intArrayOf(0, -1) + ) + + val visited = Array(rows){BooleanArray(cols)} + + fun isValid(r: Int, c: Int) = + r in (0 until rows) && + c in (0 until cols) && + visited[r][c] == false && + grid[r][c] == 1 + + fun isValidBorder(r: Int, c: Int) = + (r == 0 || r == rows - 1 || c == 0 || c == cols - 1) && + visited[r][c] == false + + fun dfs(r: Int, c: Int): Int { + if (!isValid(r, c)) + return 0 + + visited[r][c] = true + + var res = 1 + for ((dr, dc) in dirs) { + res += dfs(r + dr, c + dc) + } + + return res + } + + var totalLand = 0 + var borderLand = 0 + for (r in 0 until rows) { + for (c in 0 until cols) { + totalLand += grid[r][c] + if (grid[r][c] == 1 && isValidBorder(r, c)) { + borderLand += dfs(r, c) + } + } + } + + return totalLand - borderLand + } + +} diff --git a/kotlin/1029-two-city-scheduling.kt b/kotlin/1029-two-city-scheduling.kt new file mode 100644 index 000000000..e05b03336 --- /dev/null +++ b/kotlin/1029-two-city-scheduling.kt @@ -0,0 +1,15 @@ +class Solution { + fun twoCitySchedCost(costs: Array): Int { + val diffs = mutableListOf().apply { + costs.forEach { (c1, c2) -> + add(intArrayOf(c2 - c1, c1, c2)) + } + } + .sortedWith(compareBy { it[0] }) + + var res = 0 + val k = diffs.size / 2 + return diffs.withIndex() + .sumBy { (i, a) -> if (i < k) a[2] else a[1] } + } +} diff --git a/kotlin/1035-uncrossed-lines.kt b/kotlin/1035-uncrossed-lines.kt new file mode 100644 index 000000000..910002b66 --- /dev/null +++ b/kotlin/1035-uncrossed-lines.kt @@ -0,0 +1,107 @@ +/* +* Top down DP with optimized space +*/ +class Solution { + fun maxUncrossedLines(nums1: IntArray, nums2: IntArray): Int { + var prev = IntArray(nums2.size + 1) + + for (i in 0 until nums1.size) { + val dp = IntArray(nums2.size + 1) + + for (j in 0 until nums2.size) { + if (nums1[i] == nums2[j]) { + dp[j + 1] = 1 + prev[j] + } else { + dp[j + 1] = maxOf( + dp[j], + prev[j + 1] + ) + } + } + + prev = dp + } + + return prev[nums2.size] + } +} + +/* +* Top down DP +*/ +class Solution { + fun maxUncrossedLines(nums1: IntArray, nums2: IntArray): Int { + val dp = Array(nums1.size + 1) { IntArray(nums2.size + 1) } + + for (i in 0 until nums1.size) { + for (j in 0 until nums2.size) { + if (nums1[i] == nums2[j]) { + dp[i + 1][j + 1] = 1 + dp[i][j] + } else { + dp[i + 1][j + 1] = maxOf( + dp[i + 1][j], + dp[i][j + 1] + ) + } + } + } + + return dp[nums1.size][nums2.size] + } +} + +/* +* Recursion/dfs + memoization +*/ +class Solution { + fun maxUncrossedLines(nums1: IntArray, nums2: IntArray): Int { + val memo = Array(nums1.size) { IntArray(nums2.size) {-1} } + + fun dfs(i: Int, j: Int): Int { + if (i == nums1.size || j == nums2.size) + return 0 + if (memo[i][j] != -1) + return memo[i][j] + + memo[i][j] = 0 + + if (nums1[i] == nums2[j]) { + memo[i][j] = 1 + dfs(i + 1, j + 1) + + } else { + memo[i][j] += maxOf( + dfs(i + 1, j), + dfs(i, j + 1) + ) + } + + return memo[i][j] + } + + return dfs(0, 0) + } +} + +/* +* Bottom up DP +*/ +class Solution { + fun maxUncrossedLines(nums1: IntArray, nums2: IntArray): Int { + val dp = Array(nums1.size + 1) { IntArray(nums2.size + 1) } + + for (i in nums1.lastIndex downTo 0) { + for (j in nums2.lastIndex downTo 0) { + if (nums1[i] == nums2[j]) { + dp[i][j] = 1 + dp[i + 1][j + 1] + } else { + dp[i][j] = maxOf( + dp[i + 1][j], + dp[i][j + 1] + ) + } + } + } + + return dp[0][0] + } +} diff --git a/kotlin/1041-robot-bounded-in-circle.kt b/kotlin/1041-robot-bounded-in-circle.kt new file mode 100644 index 000000000..ee513a35d --- /dev/null +++ b/kotlin/1041-robot-bounded-in-circle.kt @@ -0,0 +1,21 @@ +class Solution { + fun isRobotBounded(instructions: String): Boolean { + var dirX = 0 + var dirY = 1 + var x = 0 + var y = 0 + + for (d in instructions) { + if (d == 'G') { + x += dirX + y += dirY + } else if (d == 'L') { + dirX = -1 * dirY.also { dirY = dirX } + } else { + dirX = dirY.also { dirY = -1 * dirX } + } + } + + return (x to y) == (0 to 0) || (dirX to dirY) != (0 to 1) + } +} diff --git a/kotlin/1043-partition-array-for-maximum-sum.kt b/kotlin/1043-partition-array-for-maximum-sum.kt new file mode 100644 index 000000000..26ab4055c --- /dev/null +++ b/kotlin/1043-partition-array-for-maximum-sum.kt @@ -0,0 +1,57 @@ +// Dp solution +class Solution { + fun maxSumAfterPartitioning(arr: IntArray, k: Int): Int { + val n = arr.size + val dp = IntArray (k) + dp[0] = arr[0] + + for (i in 1 until n) { + var curMax = 0 + var maxAtI = 0 + for (j in i downTo (i - k + 1)) { + if (j < 0) break + + curMax = maxOf(curMax, arr[j]) + + val winLen = i - j + 1 + val curSum = curMax * winLen + val subArrSum = if (j > 0) dp[(j - 1) % k] else dp[k - 1] + + maxAtI = maxOf(maxAtI, curSum + subArrSum) + } + + dp[i % k] = maxAtI + } + + return dp[(n - 1) % k] + } +} + +// recursion + memoization +class Solution { + fun maxSumAfterPartitioning(arr: IntArray, k: Int): Int { + val n = arr.size + val dp = IntArray (n) { -1 } + + fun dfs(i: Int): Int { + if (i == n) return 0 + if (dp[i] != -1) return dp[i] + + var curMax = 0 + var res = 0 + for (j in i until minOf(n, i + k)) { + curMax = maxOf(curMax, arr[j]) + val winLen = j - i + 1 + res = maxOf( + res, + dfs(j + 1) + curMax * winLen + ) + } + + dp[i] = res + return res + } + + return dfs(0) + } +} diff --git a/kotlin/1046-last-stone-weight.kt b/kotlin/1046-last-stone-weight.kt new file mode 100644 index 000000000..cb774b5d4 --- /dev/null +++ b/kotlin/1046-last-stone-weight.kt @@ -0,0 +1,23 @@ +class Solution { + fun lastStoneWeight(stones: IntArray): Int { + val heap = PriorityQueue{a, b -> b-a} + + stones.forEach{ stone -> + heap.add(stone) + } + + while (!heap.isEmpty()) { + if (heap.size == 1) + return heap.poll() + + val first = heap.poll() + val sec = heap.poll() + + if (first != sec) { + heap.add(first - sec) + } + } + + return 0 + } +} diff --git a/kotlin/1048-longest-string-chain.kt b/kotlin/1048-longest-string-chain.kt new file mode 100644 index 000000000..b64b82203 --- /dev/null +++ b/kotlin/1048-longest-string-chain.kt @@ -0,0 +1,54 @@ +//dfs +class Solution { + fun longestStrChain(words: Array): Int { + val wordList = words.toHashSet() + val dp = HashMap() + + fun dfs(word: String, len: Int): Int { + if (word !in wordList) return 0 + if ("$word:$len" in dp) return dp["$word:$len"]!! + + var res = len + for (i in 0 until 26) { + for (j in 0..word.length) { + val nextWord = word.substring(0, j) + ('a' + i) + word.substring(j, word.length) + res = maxOf(res, dfs(nextWord, len + 1)) + } + } + + dp["$word:$len"] = res + return res + } + + var res = 1 + for (word in wordList) { + res = maxOf(res, dfs(word, 1)) + } + + return res + } +} + +//dp +class Solution { + fun longestStrChain(words: Array): Int { + words.sortBy { it.length } + val dp = HashMap() + + var res = 0 + for (word in words) { + var cur = 1 + for (i in 0 until word.length) { + val nextWord = word.substring(0, i) + word.substring(i + 1) + dp[nextWord]?.let { + cur = maxOf(cur, it + 1) + } + } + + dp[word] = cur + res = maxOf(res, cur) + } + + return res + } +} diff --git a/kotlin/1049-last-stone-weight-ii.kt b/kotlin/1049-last-stone-weight-ii.kt new file mode 100644 index 000000000..816590be6 --- /dev/null +++ b/kotlin/1049-last-stone-weight-ii.kt @@ -0,0 +1,23 @@ +class Solution { + fun lastStoneWeightII(stones: IntArray): Int { + val sum = stones.sum()!! + val target = Math.ceil(sum.toDouble() / 2).toInt() + val dp = Array (stones.size) { IntArray (sum) { -1 } } + + fun dfs(i: Int, cur: Int): Int { + if (cur >= target || i == stones.size) + return Math.abs(cur - (sum - cur)) + if (dp[i][cur] != -1) + return dp[i][cur] + + dp[i][cur] = minOf( + dfs(i + 1, cur), + dfs(i + 1, cur + stones[i]) + ) + + return dp[i][cur] + } + + return dfs(0, 0) + } +} diff --git a/kotlin/1071-greatest-common-divisor-of-strings.kt b/kotlin/1071-greatest-common-divisor-of-strings.kt new file mode 100644 index 000000000..223d57f3e --- /dev/null +++ b/kotlin/1071-greatest-common-divisor-of-strings.kt @@ -0,0 +1,10 @@ +class Solution { + fun gcdOfStrings(str1: String, str2: String): String { + if(str1+str2 != str2+str1) return "" + val gcd = findGcd(str1.length, str2.length) + return str1.substring(0, gcd) + } + fun findGcd(p: Int, q: Int): Int { + return if(q == 0) p else findGcd(q, p%q) + } +} diff --git a/kotlin/1074-number-of-submatrices-that-sum-to-target.kt b/kotlin/1074-number-of-submatrices-that-sum-to-target.kt new file mode 100644 index 000000000..dd536845e --- /dev/null +++ b/kotlin/1074-number-of-submatrices-that-sum-to-target.kt @@ -0,0 +1,32 @@ +class Solution { + fun numSubmatrixSumTarget(matrix: Array, target: Int): Int { + val n = matrix.size + val m = matrix[0].size + val prefix = Array (n) { IntArray (m) } + + for (i in 0 until n) { + for (j in 0 until m) { + val top = if (i > 0) prefix[i - 1][j] else 0 + val left = if (j > 0) prefix[i][j - 1] else 0 + val topLeft = if (minOf(i, j) > 0) prefix[i - 1][j - 1] else 0 + prefix[i][j] = matrix[i][j] + top + left - topLeft + } + } + + var res = 0 + for (i in 0 until n) { + for (i2 in i until n) { + var count = HashMap() + count[0] = 1 + for (j in 0 until m) { + val curSum = prefix[i2][j] - (if (i > 0) prefix[i - 1][j] else 0) + val diff = curSum - target + res += (count[diff] ?: 0) + count[curSum] = (count[curSum] ?: 0) + 1 + } + } + } + + return res + } +} diff --git a/kotlin/1091-shortest-path-in-binary-matrix.kt b/kotlin/1091-shortest-path-in-binary-matrix.kt new file mode 100644 index 000000000..b588ad576 --- /dev/null +++ b/kotlin/1091-shortest-path-in-binary-matrix.kt @@ -0,0 +1,46 @@ +class Solution { + fun shortestPathBinaryMatrix(grid: Array): Int { + + if(grid[0][0] == 1 || grid[grid.lastIndex][grid.lastIndex] == 1) return -1 + + fun isValid(i: Int, j: Int) = i in (0..grid.lastIndex) && j in (0..grid.lastIndex) && grid[i][j] == 0 + + val q = ArrayDeque>() + var distance = 0 + + q.add(0 to 0) + grid[0][0] = 1 + + while (q.isNotEmpty()) { + distance++ + val size = q.size + repeat (size) { + val (i, j) = q.poll() + if(i == grid.lastIndex && j == grid.lastIndex) return distance + for(cell in cells) { + val nextI = i + cell[0] + val nextJ = j + cell[1] + if(isValid(nextI, nextJ)) { + q.add(nextI to nextJ) + grid[nextI][nextJ] = 1 + } + } + } + } + + return -1 + } + + companion object { + val cells = arrayOf( + intArrayOf(0,1), + intArrayOf(1,1), + intArrayOf(0,-1), + intArrayOf(1,-1), + intArrayOf(1,0), + intArrayOf(-1,-1), + intArrayOf(-1,0), + intArrayOf(-1, 1) + ) + } +} diff --git a/kotlin/1094-car-pooling.kt b/kotlin/1094-car-pooling.kt new file mode 100644 index 000000000..b5e358ecc --- /dev/null +++ b/kotlin/1094-car-pooling.kt @@ -0,0 +1,18 @@ +class Solution { + fun carPooling(trips: Array, capacity: Int): Boolean { + val stops = IntArray (1001) + for ((pas, pick, drop) in trips) { + stops[pick] += pas + stops[drop] -= pas + } + + var pas = 0 + for (i in 0..1000) { + pas += stops[i] + if (pas > capacity) + return false + } + + return true + } +} diff --git a/kotlin/1095-find-in-mountain-array.kt b/kotlin/1095-find-in-mountain-array.kt new file mode 100644 index 000000000..8d2b7a014 --- /dev/null +++ b/kotlin/1095-find-in-mountain-array.kt @@ -0,0 +1,50 @@ +class Solution { + fun findInMountainArray(target: Int, mountainArr: MountainArray): Int { + var l = 1 + var r = mountainArr.length() - 2 + var peak = -1 + while (l <= r) { + val m = (l + r) / 2 + val left = mountainArr.get(m - 1) + val mid = mountainArr.get(m) + val right = mountainArr.get(m + 1) + + if (left < mid && mid < right) { + l = m + 1 + } else if (left > mid && mid > right) { + r = m - 1 + } else { + peak = m + break + } + } + + l = 0 + r = peak + while (l <= r) { + val m = (l + r) / 2 + val value = mountainArr.get(m) + if (value < target) + l = m + 1 + else if (value > target) + r = m - 1 + else + return m + } + + l = peak + r = mountainArr.length() - 1 + while (l <= r) { + val m = (l + r) / 2 + val value = mountainArr.get(m) + if (value < target) + r = m - 1 + else if (value > target) + l = m + 1 + else + return m + } + + return -1 + } +} diff --git a/kotlin/1129-shortest-path-with-alternating-colors.kt b/kotlin/1129-shortest-path-with-alternating-colors.kt new file mode 100644 index 000000000..5822c9e9d --- /dev/null +++ b/kotlin/1129-shortest-path-with-alternating-colors.kt @@ -0,0 +1,47 @@ +class Solution { + fun shortestAlternatingPaths(n: Int, redEdges: Array, blueEdges: Array): IntArray { + val redAdj = Array(n) { ArrayList() } + val blueAdj = Array(n) { ArrayList() } + val redVisit = HashSet() + val blueVisit = HashSet() + + for ((from, to) in redEdges) redAdj[from].add(to) + for ((from, to) in blueEdges) blueAdj[from].add(to) + + val res = IntArray(n) { -1 } + + with (LinkedList>()) { + addFirst(0 to 0) + + var len = 0 + while (isNotEmpty()) { + repeat (size) { + val (node, c) = removeLast() + if (res[node] == -1) res[node] = len + + if (c != -1) { + redAdj[node].forEach { + if (it !in redVisit) { + addFirst(it to -1) + redVisit.add(it) + } + } + } + + if (c != 1) { + blueAdj[node].forEach { + if (it !in blueVisit) { + addFirst(it to 1) + blueVisit.add(it) + } + } + } + } + + len++ + } + } + + return res + } +} diff --git a/kotlin/1137-n-th-tribonacci-number.kt b/kotlin/1137-n-th-tribonacci-number.kt new file mode 100644 index 000000000..8ba7da5a2 --- /dev/null +++ b/kotlin/1137-n-th-tribonacci-number.kt @@ -0,0 +1,41 @@ +// using fancy schmancy Kotlin way +class Solution { + fun tribonacci(n: Int): Int { + + return when { + n == 0 -> 0 + n == 1 -> 1 + n == 2 -> 1 + else -> run { + var n0 = 0 + var n1 = 1 + var n2 = 1 + for (i in 3..n) { + val temp = n0 + n1 + n2 + n0 = n1 + n1 = n2 + n2 = temp + } + n2 + } + } + } +} + +//normal way to do it (Java safe) +class Solution { + fun tribonacci(n: Int): Int { + + var numbers = intArrayOf(0,1,1) + + if (n < 3) return numbers[n] + for (i in 3..n) { + val temp = numbers[0] + numbers[1] + numbers[2] + numbers[0] = numbers[1] + numbers[1] = numbers[2] + numbers[2] = temp + } + + return numbers[2] + } +} diff --git a/kotlin/1140-stone-game-ii.kt b/kotlin/1140-stone-game-ii.kt new file mode 100644 index 000000000..01f6d6b82 --- /dev/null +++ b/kotlin/1140-stone-game-ii.kt @@ -0,0 +1,29 @@ +class Solution { + fun stoneGameII(piles: IntArray): Int { + val dp = Array (2) { Array (piles.size) { IntArray (piles.size + 1) } } + + fun bfs(a: Int, i: Int, m: Int): Int { + if (i == piles.size) + return 0 + if (dp[a][i][m] != 0) + return dp[a][i][m] + + var res = if (a == 0) 0 else Integer.MAX_VALUE + var total = 0 + for (x in 1..(2 * m)) { + if (i + x > piles.size) + break + total += piles[i + x - 1] + if (a == 0) + res = maxOf(res, total + bfs(1, i + x, maxOf(m, x))) + else + res = minOf(res, bfs(0, i + x, maxOf(m, x))) + } + + dp[a][i][m] = res + return dp[a][i][m] + } + + return bfs(0, 0, 1) + } +} diff --git a/kotlin/1143-Longest-Common-Subsequence.kt b/kotlin/1143-Longest-Common-Subsequence.kt new file mode 100644 index 000000000..ea744645e --- /dev/null +++ b/kotlin/1143-Longest-Common-Subsequence.kt @@ -0,0 +1,23 @@ +class Solution { + fun longestCommonSubsequence(text1: String, text2: String): Int { + if (text1.isEmpty() || text2.isEmpty()) { + return 0 + } + + val M = text1.length + val N = text2.length + + val dp = Array(M + 1){IntArray(N + 1){0}} + + for (i in 1..M) { + for (j in 1..N) { + if (text1[i - 1] == text2[j - 1]) { + dp[i][j] = dp[i - 1][j - 1] + 1 + } else { + dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]) + } + } + } + return dp[M][N] + } +} \ No newline at end of file diff --git a/kotlin/1143-longest-common-subsequence.kt b/kotlin/1143-longest-common-subsequence.kt new file mode 100644 index 000000000..55dc1f154 --- /dev/null +++ b/kotlin/1143-longest-common-subsequence.kt @@ -0,0 +1,98 @@ +class Solution { + fun longestCommonSubsequence(text1: String, text2: String): Int { + if (text1.isEmpty() || text2.isEmpty()) { + return 0 + } + + val M = text1.length + val N = text2.length + + val dp = Array(M + 1){IntArray(N + 1){0}} + + for (i in 1..M) { + for (j in 1..N) { + if (text1[i - 1] == text2[j - 1]) { + dp[i][j] = dp[i - 1][j - 1] + 1 + } else { + dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]) + } + } + } + return dp[M][N] + } +} + +/* + * Different solutions + */ + + // Recursion + Memoization, Time Complexity of O(n * m) and space complexity of O(n * m) +class Solution { + fun longestCommonSubsequence(t1: String, t2: String): Int { + val n = t1.length + val m = t2.length + val dp = Array (n) { IntArray (m) { -1 } } + + fun dfs(i: Int, j: Int): Int { + if (i == n || j == m) return 0 + if (dp[i][j] != -1) return dp[i][j] + + if (t1[i] == t2[j]) + dp[i][j] = 1 + dfs(i + 1, j + 1) + else + dp[i][j] = maxOf(dfs(i + 1, j), dfs(i, j + 1)) + + return dp[i][j] + } + + return dfs(0, 0) + } +} + +// Top down DP, Time Complexity of O(n * m) and space complexity of O(n * m) +class Solution { + fun longestCommonSubsequence(t1: String, t2: String): Int { + val n = t1.length + val m = t2.length + val dp = Array (n + 1) { IntArray (m + 1) } + + for (i in n - 1 downTo 0) { + for (j in m - 1 downTo 0) { + if (t1[i] == t2[j]) + dp[i][j] = 1 + dp[i + 1][j + 1] + else + dp[i][j] = maxOf(dp[i + 1][j], dp[i][j + 1]) + } + } + + return dp[0][0] + } +} + +// Optimized DP (Works both for both Top-down and Bottom-up, but here we use bottom-up approach) +// Time Complexity of O(n * m) and space complexity of O(maxOf(n, m)) +class Solution { + fun longestCommonSubsequence(t1: String, t2: String): Int { + val m = t1.length + val n = t2.length + if (m < n) return longestCommonSubsequence(t2, t1) + + var dp = IntArray (n + 1) + + for (i in m downTo 0) { + var newDp = IntArray (n + 1) + for (j in n downTo 0) { + if (i == m || j == n) { + newDp[j] = 0 + } else if (t1[i] == t2[j]) { + newDp[j] = 1 + dp[j + 1] + } else { + newDp[j] = maxOf(dp[j], newDp[j + 1]) + } + } + dp = newDp + } + + return dp[0] + } +} diff --git a/kotlin/1155-number-of-dice-rolls-with-target-sum.kt b/kotlin/1155-number-of-dice-rolls-with-target-sum.kt new file mode 100644 index 000000000..874de5005 --- /dev/null +++ b/kotlin/1155-number-of-dice-rolls-with-target-sum.kt @@ -0,0 +1,43 @@ +// dp, space complexity down from O(n * t) down to O(t) +class Solution { + fun numRollsToTarget(n: Int, k: Int, t: Int): Int { + val mod = 1_000_000_007 + var dp = LongArray (t + 1) + + dp[0] = 1 + + for (d in 0 until n) { + val nextDp = LongArray (t + 1) + for (m in 1..k) { + for (s in m..t) + nextDp[s] = (nextDp[s] + dp[s - m]) % mod + } + dp = nextDp + } + + return dp[t].toInt() + } +} + +// recursion + memo +class Solution { + fun numRollsToTarget(n: Int, k: Int, t: Int): Int { + val mod = 1_000_000_007 + val dp = Array (n + 1) { LongArray (t + 1) { -1L } } + + fun dfs(n: Int, s: Int): Long { + if (n < 0 || s > t) return 0 + if (s == t && n == 0) return 1 + if (dp[n][s] != -1L) return dp[n][s] + + var res = 0L + for (m in 1..k) + res = (res + dfs(n - 1, s + m)) % mod + + dp[n][s] = res + return dp[n][s] + } + + return (dfs(n, 0) % mod).toInt() + } +} diff --git a/kotlin/1160-find-words-that-can-be-formed-by-characters.kt b/kotlin/1160-find-words-that-can-be-formed-by-characters.kt new file mode 100644 index 000000000..0ca389207 --- /dev/null +++ b/kotlin/1160-find-words-that-can-be-formed-by-characters.kt @@ -0,0 +1,55 @@ +class Solution { + fun countCharacters(words: Array, chars: String): Int { + val charMap = chars.groupingBy { it }.eachCount() + var res = 0 + outer@for (word in words) { + val wordMap = word.groupingBy { it }.eachCount() + for ((ch, cnt) in wordMap) { + if (charMap[ch] == null || charMap[ch]!! < cnt) + continue@outer + } + res += word.length + } + return res + } +} + +// modularize funtion for readability +class Solution { + fun countCharacters(words: Array, chars: String): Int { + val charMap = chars.getMap() + var res = 0 + for (word in words) { + val wordMap = word.getMap() + if (wordMap.canMake(charMap)) + res += word.length + } + return res + } + + fun IntArray.canMake(another: IntArray): Boolean { + for (i in 0 until 26) { + if (this[i] > another[i]) + return false + } + return true + } + + fun String.getMap(): IntArray { + val res = IntArray (26) + for (c in this) + res[c - 'a']++ + return res + } +} + +// Short Kotlin solution +class Solution { + fun countCharacters(words: Array, chars: String) = words + .filter { word -> + word.none { c -> + word.count { it == c } > chars.count { it == c } + } + } + .sumOf { it.length } +} diff --git a/kotlin/1162-as-far-from-land-as-possible.kt b/kotlin/1162-as-far-from-land-as-possible.kt new file mode 100644 index 000000000..9ae44c81b --- /dev/null +++ b/kotlin/1162-as-far-from-land-as-possible.kt @@ -0,0 +1,75 @@ +// BFS solution +class Solution { + fun maxDistance(grid: Array): Int { + + fun isValid(i: Int, j: Int) = i in (0..grid.lastIndex) && j in (0..grid[0].lastIndex) && grid[i][j] == 0 + + val q = ArrayDeque>() + var distance = -1 + + for (i in grid.indices) { + for (j in grid[0].indices) { + if (grid[i][j] == 1) + q.add(i to j) + } + } + + while (q.isNotEmpty()) { + + distance++ + + val size = q.size + repeat (size) { + + val (i,j) = q.poll() + + for (dir in dirs) { + val nextI = i + dir[0] + val nextJ = j + dir[1] + if (isValid(nextI, nextJ)) { + q.add(nextI to nextJ) + grid[nextI][nextJ] = 1 + } + } + } + } + + return if (distance > 0) distance else -1 + } + + val dirs = arrayOf( + intArrayOf(0, 1), + intArrayOf(0, -1), + intArrayOf(1, 0), + intArrayOf(-1, 0) + ) +} + +//DP solution +class Solution { + fun maxDistance(grid: Array): Int { + + val defaultValue = 100 + 100 + 1 + + for (i in grid.indices) { + for (j in grid[0].indices) { + if (grid[i][j] == 1) continue + grid[i][j] = defaultValue + if(i > 0) grid[i][j] = minOf(grid[i][j], grid[i - 1][j] + 1) + if(j > 0) grid[i][j] = minOf(grid[i][j], grid[i][j - 1] + 1) + } + } + + var res = 0 + for (i in grid.lastIndex downTo 0) { + for (j in grid[0].lastIndex downTo 0) { + if (grid[i][j] == 1) continue + if(i < grid.lastIndex) grid[i][j] = minOf(grid[i][j], grid[i + 1][j] + 1) + if(j < grid[0].lastIndex) grid[i][j] = minOf(grid[i][j], grid[i][j + 1] + 1) + res = maxOf(res, grid[i][j]) + } + } + + return if (res == defaultValue) -1 else res - 1 + } +} diff --git a/kotlin/1189-maximum-number-of-balloons.kt b/kotlin/1189-maximum-number-of-balloons.kt new file mode 100644 index 000000000..f2174f253 --- /dev/null +++ b/kotlin/1189-maximum-number-of-balloons.kt @@ -0,0 +1,15 @@ +class Solution { + fun maxNumberOfBalloons(text: String): Int { + val map = IntArray(26) + text.forEach { + if(it in "balon") map[it - 'a']++ + } + var min = Integer.MAX_VALUE + "balon".forEach { + if(map[it - 'a'] == 0) return 0 + else if(it == 'l' || it == 'o') min = minOf(min, map[it-'a']/2) + else min = minOf(min, map[it-'a']) + } + return min + } +} diff --git a/kotlin/1209-remove-all-adjacent-duplicates-in-string-ii.kt b/kotlin/1209-remove-all-adjacent-duplicates-in-string-ii.kt new file mode 100644 index 000000000..21077e808 --- /dev/null +++ b/kotlin/1209-remove-all-adjacent-duplicates-in-string-ii.kt @@ -0,0 +1,27 @@ +class Solution { + fun removeDuplicates(s: String, k: Int): String { + val stack = LinkedList>() + + for (c in s) { + if (stack.isNotEmpty() && stack.peekLast().first == c) { + val (ch, co) = stack.removeLast() + stack.addLast(c to (co + 1)) + } else { + stack.addLast(c to 1) + } + + if (stack.isNotEmpty() && stack.peekLast().second == k) { + stack.removeLast() + } + } + + val res = StringBuilder() + for ((ch, co) in stack) { + repeat (co) { + res.append(ch) + } + } + + return res.toString() + } +} diff --git a/kotlin/1220-count-vowels-permutation.kt b/kotlin/1220-count-vowels-permutation.kt new file mode 100644 index 000000000..10e00fcb7 --- /dev/null +++ b/kotlin/1220-count-vowels-permutation.kt @@ -0,0 +1,94 @@ +//DP with O(1) space complexity +class Solution { + fun countVowelPermutation(n: Int): Int { + val mod = 1_000_000_000 + 7 + var prev = LongArray (5) { 1L } + + val a = 0 + val e = 1 + val i = 2 + val o = 3 + val u = 4 + + for (j in 1 until n) { + val new = LongArray (5) + new[a] = 0L + (prev[e] + prev[i] + prev[u]) % mod + new[e] = 0L + (prev[a] + prev[i]) % mod + new[i] = 0L + (prev[e] + prev[o]) % mod + new[o] = 0L + (prev[i]) % mod + new[u] = 0L + (prev[i] + prev[o]) % mod + prev = new + } + + return (prev.sum()!! % mod).toInt() + } +} + +//DP with O(n) space complexity +class Solution { + fun countVowelPermutation(n: Int): Int { + val mod = 1_000_000_000 + 7 + val dp = Array (n + 1) { LongArray (5) } + + for (j in 0 until 5) + dp[1][j] = 1 + + val a = 0 + val e = 1 + val i = 2 + val o = 3 + val u = 4 + + for (j in 2..n) { + dp[j][a] = 0L + (dp[j - 1][e] + dp[j - 1][i] + dp[j - 1][u]) % mod + dp[j][e] = 0L + (dp[j - 1][a] + dp[j - 1][i]) % mod + dp[j][i] = 0L + (dp[j - 1][e] + dp[j - 1][o]) % mod + dp[j][o] = 0L + (dp[j - 1][i]) % mod + dp[j][u] = 0L + (dp[j - 1][i] + dp[j - 1][o]) % mod + } + + return (dp[n].sum()!! % mod).toInt() + } +} + +//recursion + memoization +class Solution { + fun countVowelPermutation(n: Int): Int { + val mod = 1_000_000_000 + 7 + val dp = Array (n) { IntArray (5) { -1 } } + + val a = 0 + val e = 1 + val i = 2 + val o = 3 + val u = 4 + + val follow = mapOf( + a to listOf(e), + e to listOf(a, i), + i to listOf(a, e, o, u), + o to listOf(i, u), + u to listOf(a) + ) + + fun dfs(idx: Int, prev: Int): Int { + if (idx == n) return 1 + if (dp[idx][prev] != -1) return dp[idx][prev] + + var res = 0 + follow[prev]?.forEach { + res = (res + dfs(idx + 1, it)) % mod + } + + dp[idx][prev] = res + return res + } + + var res = 0 + follow.keys.forEach { + res = (res + dfs(1, it)) % mod + } + + return res + } +} diff --git a/kotlin/1235-maximum-profit-in-job-scheduling.kt b/kotlin/1235-maximum-profit-in-job-scheduling.kt new file mode 100644 index 000000000..f6de83fb6 --- /dev/null +++ b/kotlin/1235-maximum-profit-in-job-scheduling.kt @@ -0,0 +1,107 @@ +// Recursion + Memoization +class Solution { + fun jobScheduling(s: IntArray, e: IntArray, p: IntArray): Int { + val dp = IntArray (s.size) { -1 } + + val intervals = s.mapIndexed { i, v -> + intArrayOf(v, e[i], p[i]) + }.sortedWith(compareBy({ it[0] }, { it[1] })) + + fun dfs(i: Int): Int { + if (i == intervals.size || i == -1) return 0 + if (dp[i] != -1) return dp[i] + + // bisect + var l = i + var r = intervals.lastIndex + var res = -1 + while (l <= r) { + val m = (r + l) / 2 + if (intervals[m][0] >= intervals[i][1]) { + res = m + r = m - 1 + } else { + l = m + 1 + } + } + + dp[i] = maxOf( + dfs(i + 1), // dont include + intervals[i][2] + dfs(res) //include + ) + + return dp[i] + } + + return dfs(0) + } +} + +// Top-down DP +class Solution { + fun jobScheduling(sT: IntArray, eT: IntArray, p: IntArray): Int { + val jobs = sT.indices + .map { intArrayOf(sT[it], eT[it], p[it]) } + .sortedWith(compareBy({ it[0] }, { it[1] })) + + val n = jobs.size + val last = n - 1 + val dp = IntArray (n) + + for (i in last downTo 0) { + var j = -1 + var l = i + var r = last + while (l <= r) { + val m = (l + r) / 2 + if (jobs[m][0] >= jobs[i][1]) { + j = m + r = m - 1 + } else { + l = m + 1 + } + } + + dp[i] = maxOf( + jobs[i][2] + (if (j != -1) dp[j] else 0), + if (i + 1 < n) dp[i + 1] else 0 + ) + } + + return dp[0] + } +} + +//Bottom-up DP +class Solution { + fun jobScheduling(sT: IntArray, eT: IntArray, p: IntArray): Int { + val jobs = sT.indices + .map { intArrayOf(sT[it], eT[it], p[it]) } + .sortedWith(compareBy({ it[1] }, { it[0] })) + + val n = jobs.size + val dp = IntArray (n) + + for (i in 0 until n) { + var l = 0 + var r = i - 1 + var j = -1 + while (l <= r) { + val m = (l + r) / 2 + if (jobs[m][1] <= jobs[i][0]) { + j = m + l = m + 1 + } else { + r = m - 1 + } + } + + dp[i] = maxOf( + jobs[i][2] + if (j >= 0) dp[j] else 0, + if (i - 1 >= 0) dp[i - 1] else 0 + ) + } + + return dp[n - 1] + } +} diff --git a/kotlin/1239-maximum-length-of-a-concatenated-string-with-unique-characters.kt b/kotlin/1239-maximum-length-of-a-concatenated-string-with-unique-characters.kt new file mode 100644 index 000000000..2c97c9036 --- /dev/null +++ b/kotlin/1239-maximum-length-of-a-concatenated-string-with-unique-characters.kt @@ -0,0 +1,32 @@ +class Solution { + fun maxLength(arr: List): Int { + val charSet = HashSet() + + fun overlap(s: String): Boolean { + val count = IntArray (26) + + for (c in s) count[c - 'a']++ + for (c in charSet) count[c - 'a']++ + + return count.any { it > 1 } + } + + fun backtrack(i: Int): Int { + if (i == arr.size) + return charSet.size + + var res = 0 + if (!overlap(arr[i])) { + for (c in arr[i]) + charSet.add(c) + res = backtrack(i + 1) + for (c in arr[i]) + charSet.remove(c) + } + + return maxOf(res, backtrack(i + 1)) + } + + return backtrack(0) + } +} diff --git a/kotlin/1254-number-of-closed-islands.kt b/kotlin/1254-number-of-closed-islands.kt new file mode 100644 index 000000000..a0f2f82b0 --- /dev/null +++ b/kotlin/1254-number-of-closed-islands.kt @@ -0,0 +1,41 @@ +class Solution { + fun closedIsland(grid: Array): Int { + val rows = grid.size + val cols = grid[0].size + + val visited = Array(rows){BooleanArray(cols)} + + fun isValid(r: Int, c: Int) = r in (0 until rows) && c in (0 until cols) + + fun dfs(r: Int, c: Int): Int { + if(!isValid(r, c)) + return 0 + if(grid[r][c] == 1 || visited[r][c] == true) + return 1 + + visited[r][c] = true + return minOf( + minOf( + dfs(r + 1, c), + dfs(r - 1, c) + ), + minOf( + dfs(r, c + 1), + dfs(r, c - 1) + ) + ) + } + + var islands = 0 + for (r in 0 until rows) { + for (c in 0 until cols) { + if (grid[r][c] == 0 && visited[r][c] == false) { + if (dfs(r, c) != 0) + islands++ + } + } + } + + return islands + } +} diff --git a/kotlin/1260-shift-2d-grid.kt b/kotlin/1260-shift-2d-grid.kt new file mode 100644 index 000000000..a082ad877 --- /dev/null +++ b/kotlin/1260-shift-2d-grid.kt @@ -0,0 +1,21 @@ +class Solution { + fun shiftGrid(grid: Array, k: Int): List> { + val m = grid.size + val n = grid[0].size + + fun posToVal(r: Int, c: Int) = r * n + c + + fun valToPos(v: Int) = (v / n) to (v % n) + + val res = MutableList> (m) { MutableList (n) { 0 } } + for (r in 0 until m) { + for (c in 0 until n) { + val newVal = (posToVal(r, c) + k) % (m * n) + val (newR, newC) = valToPos(newVal) + res[newR][newC] = grid[r][c] + } + } + + return res + } +} diff --git a/kotlin/1268-search-suggestions-system.kt b/kotlin/1268-search-suggestions-system.kt new file mode 100644 index 000000000..46966f97b --- /dev/null +++ b/kotlin/1268-search-suggestions-system.kt @@ -0,0 +1,23 @@ +class Solution { + fun suggestedProducts(products: Array, searchWord: String): List> { + var res = mutableListOf>() + products.sort() + + var l = 0 + var r = products.lastIndex + for ((i, c) in searchWord.withIndex()) { + while (l <= r && (products[l].length <= i || products[l][i] != c)) + l++ + while (l <= r && (products[r].length <= i || products[r][i] != c)) + r-- + + var temp = mutableListOf() + val rem = r - l + 1 + for (j in 0 until minOf(3, rem)) + temp.add(products[l + j]) + res.add(temp) + } + + return res + } +} diff --git a/kotlin/1269-number-of-ways-to-stay-in-the-same-place-after-some-steps.kt b/kotlin/1269-number-of-ways-to-stay-in-the-same-place-after-some-steps.kt new file mode 100644 index 000000000..84a56bc3c --- /dev/null +++ b/kotlin/1269-number-of-ways-to-stay-in-the-same-place-after-some-steps.kt @@ -0,0 +1,55 @@ +// Optimized DP solution Time O(minOf(steps, arrLen)^2, and space optimized down from O(minOf(steps, arrLen)^2) to O(minOf(steps, arrLen)) +class Solution { + fun numWays(steps: Int, _arrLen: Int): Int { + val mod = 1_000_000_000 + 7 + val arrLen = minOf(steps, _arrLen) + var dp = IntArray (arrLen) + + dp[0] = 1 + + for (step in 1..steps) { + val nextDp = IntArray (arrLen) + for (i in 0 until arrLen) { + nextDp[i] = dp[i] + if (i > 0) + nextDp[i] = (nextDp[i] + dp[i - 1]) % mod + if (i < arrLen - 1) + nextDp[i] = (nextDp[i] + dp[i + 1]) % mod + } + dp = nextDp + } + + return dp[0] + } +} + +/* Recursion + memoization, Time O(minOf(steps, arrLen)^2 and space O(minOf(steps, arrLen)^2 + * Here we use arrays for memoization, however, we set the 2D array's size to [steps + 1][steps + 1] because: +* 1) we can never move more than "steps" amount to the right, and we have constraints 1 <= steps <= 500 and 1 <= arrLen <= 10^6, +* so we can therefor make an optimization since we dont need all that extra space, and 2) it will not AC on Leetcode since they are looking for this optimisation. +*/ +class Solution { + fun numWays(steps: Int, arrLen: Int): Int { + val mod = 1_000_000_000 + 7 + val dp = Array (steps + 1) { IntArray (steps + 1) { -1 } } + + fun dfs(i: Int, left: Int): Int { + if (i < 0 || i == arrLen) return 0 + if (left == 0) { + if (i == 0) return 1 + else return 0 + } + if (dp[i][left] != -1) return dp[i][left] + + var res = 0 + res = (res + dfs(i - 1, left - 1)) % mod + res = (res + dfs(i + 1, left - 1)) % mod + res = (res + dfs(i, left - 1)) % mod + + dp[i][left] = res + return res + } + + return dfs(0, steps) + } +} diff --git a/kotlin/1288-remove-covered-intervals.kt b/kotlin/1288-remove-covered-intervals.kt new file mode 100644 index 000000000..dfcfaceed --- /dev/null +++ b/kotlin/1288-remove-covered-intervals.kt @@ -0,0 +1,38 @@ +// Time complexity O(nlogn) and space complexity O(n) +// Find solution with optimized space complexity below +class Solution { + fun removeCoveredIntervals(intervals: Array): Int { + intervals.sortWith(compareBy({ it[0] }, { -it[1] })) + + val res = LinkedList().apply { add(intervals[0]) } + for ((l, r) in intervals) { + val (prevL, prevR) = res.peekLast() + + if (prevL <= l && prevR >= r) + continue + + res.addLast(intArrayOf(l, r)) + } + + return res.size + } +} + +// Time complexity O(nlogn) and space complexity O(1) +class Solution { + fun removeCoveredIntervals(intervals: Array): Int { + intervals.sortWith(compareBy({ it[0] }, { -it[1] })) + + var prev = intervals[0] + var res = 1 + for (interval in intervals) { + if (prev[0] <= interval[0] && prev[1] >= interval[1]) + continue + + prev = interval + res++ + } + + return res + } +} diff --git a/kotlin/1291-sequential-digits.kt b/kotlin/1291-sequential-digits.kt new file mode 100644 index 000000000..b4f33d1a0 --- /dev/null +++ b/kotlin/1291-sequential-digits.kt @@ -0,0 +1,18 @@ +class Solution { + fun sequentialDigits(low: Int, high: Int): List { + var q = mutableListOf(1, 2, 3, 4, 5, 6, 7, 8, 9) + var res = mutableListOf() + + while (q.isNotEmpty()) { + var n = q.removeFirst() + + if (n > high) continue + if (n >= low) res.add(n) + + val d = n % 10 + if (d < 9) q.add(n * 10 + (d + 1)) + } + + return res + } +} diff --git a/kotlin/1299-replace-elements-with-greatest-element-on-right-side.kt b/kotlin/1299-replace-elements-with-greatest-element-on-right-side.kt new file mode 100644 index 000000000..00bc5ad2c --- /dev/null +++ b/kotlin/1299-replace-elements-with-greatest-element-on-right-side.kt @@ -0,0 +1,12 @@ +class Solution { + fun replaceElements(arr: IntArray): IntArray { + val res = IntArray(arr.size) + var max = -1 + + arr.reversed().forEachIndexed { i, value -> + res[i] = max + max = maxOf(max, value) + } + return res.reversed().toIntArray() + } +} \ No newline at end of file diff --git a/kotlin/1335-minimum-difficulty-of-a-job-schedule.kt b/kotlin/1335-minimum-difficulty-of-a-job-schedule.kt new file mode 100644 index 000000000..52d0a295a --- /dev/null +++ b/kotlin/1335-minimum-difficulty-of-a-job-schedule.kt @@ -0,0 +1,30 @@ +class Solution { + fun minDifficulty(jobDifficulty: IntArray, d: Int): Int { + if (jobDifficulty.size < d) return -1 + + val cache = HashMap() + + fun dfs(i: Int, d: Int, curMax: Int): Int { + if (i == jobDifficulty.size) { + return if (d == 0) 0 else INFINITY + } + if (d == 0) return INFINITY + cache["$i:$d:$curMax"]?.let { return it } + + var max = maxOf(curMax, jobDifficulty[i]) + var res = minOf( + dfs(i + 1, d, max), + max + dfs(i + 1, d - 1, -1) + ) + + cache["$i:$d:$max"] = res + return res + } + + return dfs(0, d, -1) + } + + companion object { + const val INFINITY = 400000 + } +} diff --git a/kotlin/1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.kt b/kotlin/1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.kt new file mode 100644 index 000000000..8fe0e7758 --- /dev/null +++ b/kotlin/1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.kt @@ -0,0 +1,17 @@ +class Solution { + fun numOfSubarrays(arr: IntArray, k: Int, threshold: Int): Int { + + var sum = 0 + var res = 0 + + for (i in 0 until k-1) sum += arr[i] + + for (i in k-1 until arr.size) { + sum += arr[i] + if (sum / k >= threshold) res++ + sum -= arr[i-k+1] + } + + return res + } +} diff --git a/kotlin/1359-count-all-valid-pickup-and-delivery-options.kt b/kotlin/1359-count-all-valid-pickup-and-delivery-options.kt new file mode 100644 index 000000000..aabb68991 --- /dev/null +++ b/kotlin/1359-count-all-valid-pickup-and-delivery-options.kt @@ -0,0 +1,15 @@ +class Solution { + fun countOrders(n: Int): Int { + var slots = 2 * n + var res = 1L + val mod = 1_000_000_007 + + while (slots > 0) { + val choices = (slots * (slots - 1) % mod) / 2 + res = (res * choices) % mod + slots -= 2 + } + + return res.toInt() + } +} diff --git a/kotlin/1361-validate-binary-tree-nodes.kt b/kotlin/1361-validate-binary-tree-nodes.kt new file mode 100644 index 000000000..e0490638a --- /dev/null +++ b/kotlin/1361-validate-binary-tree-nodes.kt @@ -0,0 +1,32 @@ +class Solution { + fun validateBinaryTreeNodes(n: Int, leftChild: IntArray, rightChild: IntArray): Boolean { + val hasParent = hashSetOf().apply { + for (n in (leftChild + rightChild)) { + if (n != -1) + this.add(n) + } + } + + if (hasParent.size == n) return false + + var root = -1 + for (i in 0 until n) { + if (i !in hasParent) { + root = i + break + } + } + + val visit = hashSetOf() + fun dfs(i: Int): Boolean { + if (i == -1) return true + if (i in visit) return false + + visit.add(i) + + return dfs(leftChild[i]) && dfs(rightChild[i]) + } + + return dfs(root) && visit.size == n + } +} diff --git a/kotlin/1376-time-needed-to-inform-all-employees.kt b/kotlin/1376-time-needed-to-inform-all-employees.kt new file mode 100644 index 000000000..239348bf3 --- /dev/null +++ b/kotlin/1376-time-needed-to-inform-all-employees.kt @@ -0,0 +1,22 @@ +class Solution { + fun numOfMinutes(n: Int, headID: Int, manager: IntArray, informTime: IntArray): Int { + val adj = HashMap>().apply { + for (i in 0 until n) + this[manager[i]] = getOrDefault(manager[i], ArrayList()).apply { add(i) } + } + + var res = 0 + with (LinkedList>()){ + addLast(headID to 0) + while(isNotEmpty()) { + val (id, time) = removeFirst() + res = maxOf(res, time) + adj[id]?.forEach { + addLast(it to (time + informTime[id])) + } + } + } + + return res + } +} diff --git a/kotlin/1383-maximum-performance-of-a-team.kt b/kotlin/1383-maximum-performance-of-a-team.kt new file mode 100644 index 000000000..135d499ed --- /dev/null +++ b/kotlin/1383-maximum-performance-of-a-team.kt @@ -0,0 +1,20 @@ +class Solution { + fun maxPerformance(n: Int, speed: IntArray, efficiency: IntArray, k: Int): Int { + val mod = 1_000_000_000 + 7 + val eng = efficiency.zip(speed) + .sortedWith(compareBy({ -it.first })) + + var res = 0L + var speed = 0L + val minHeap = PriorityQueue() + for ((eff, spd) in eng) { + if (minHeap.size == k) + speed -= minHeap.poll() + speed += spd + minHeap.add(spd) + res = maxOf(res, speed * eff) + } + + return (res % mod).toInt() + } +} diff --git a/kotlin/1396-design-underground-system.kt b/kotlin/1396-design-underground-system.kt new file mode 100644 index 000000000..306a92f69 --- /dev/null +++ b/kotlin/1396-design-underground-system.kt @@ -0,0 +1,22 @@ +class UndergroundSystem() { + + val checkIn = HashMap>() // id -> (startStation to time) + val averageTime = HashMap>() // startion -> (TotalTime to countOfTrips) + + fun checkIn(id: Int, start: String, startTime: Int) { + checkIn[id] = start to startTime + } + + fun checkOut(id: Int, end: String, endTime: Int) { + val (start, startTime) = checkIn[id]!! + val fromTo = "$start:$end" + val (total, count) = averageTime.getOrDefault(fromTo, 0.0 to 0) + averageTime[fromTo] = (total + endTime - startTime) to (count + 1) + } + + fun getAverageTime(start: String, end: String): Double { + val (total, count) = averageTime.getOrDefault("$start:$end", 0.0 to 0) + return total / count + } + +} diff --git a/kotlin/1405-longest-happy-string.kt b/kotlin/1405-longest-happy-string.kt new file mode 100644 index 000000000..042cff5a2 --- /dev/null +++ b/kotlin/1405-longest-happy-string.kt @@ -0,0 +1,32 @@ +class Solution { + fun longestDiverseString(a: Int, b: Int, c: Int): String { + val maxHeap = PriorityQueue> { a, b -> + b.second - a.second + }.apply { + if (a > 0) add('a' to a) + if (b > 0) add('b' to b) + if (c > 0) add('c' to c) + } + + val res = StringBuilder() + while (maxHeap.isNotEmpty()) { + var (c, v) = maxHeap.poll() + var n = res.length + + if (n > 1 && res.get(n - 1) == c && res.get(n - 2) == c) { + if (maxHeap.isEmpty()) break + var (c2, v2) = maxHeap.poll() + res.append(c2) + v2-- + if (v2 > 0) maxHeap.add(c2 to v2) + } else { + res.append(c) + v-- + } + + if (v > 0) maxHeap.add(c to v) + } + + return res.toString() + } +} diff --git a/kotlin/1406-stone-game-iii.kt b/kotlin/1406-stone-game-iii.kt new file mode 100644 index 000000000..a55cf8dfb --- /dev/null +++ b/kotlin/1406-stone-game-iii.kt @@ -0,0 +1,23 @@ +class Solution { + fun stoneGameIII(s: IntArray): String { + val dp = IntArray(s.size) { Integer.MIN_VALUE } + + fun dfs(i: Int): Int { + if (i == s.size) + return 0 + if (dp[i] != Integer.MIN_VALUE) + return dp[i] + + var total = 0 + for (j in i until minOf(i + 3, s.size)) { + total += s[j] + dp[i] = maxOf(dp[i], total - dfs(j + 1)) + } + + return dp[i] + } + + val sum = dfs(0) + return if (sum > 0) "Alice" else if (sum < 0) "Bob" else "Tie" + } +} diff --git a/kotlin/1422-maximum-score-after-splitting-a-string.kt b/kotlin/1422-maximum-score-after-splitting-a-string.kt new file mode 100644 index 000000000..e41cd0013 --- /dev/null +++ b/kotlin/1422-maximum-score-after-splitting-a-string.kt @@ -0,0 +1,17 @@ +class Solution { + fun maxScore(s: String): Int { + var zero = 0 + var one = s.count{ it == '1'} + var res = 0 + + for (i in 0 until s.lastIndex) { + if (s[i] == '0') + zero++ + else + one-- + res = maxOf(res, zero + one) + } + + return res + } +} diff --git a/kotlin/1423-maximum-points-you-can-obtain-from-cards.kt b/kotlin/1423-maximum-points-you-can-obtain-from-cards.kt new file mode 100644 index 000000000..d2834959b --- /dev/null +++ b/kotlin/1423-maximum-points-you-can-obtain-from-cards.kt @@ -0,0 +1,23 @@ +class Solution { + fun maxScore(cardPoints: IntArray, k: Int): Int { + if (cardPoints.size == k) return cardPoints.sum() + var l = cardPoints.size - k + var r = cardPoints.lastIndex + + var maxScore = 0 + var currentScore = 0 + // the sum of the initial sub-array + for (i in l..r) { + maxScore += cardPoints[i] + currentScore += cardPoints[i] + } + + while (true) { + currentScore -= cardPoints[l++ % cardPoints.size] + currentScore += cardPoints[++r % cardPoints.size] + maxScore = maxOf(maxScore, currentScore) + if (l % cardPoints.size == 0) break + } + return maxScore + } +} \ No newline at end of file diff --git a/kotlin/1425-constrained-subsequence-sum.kt b/kotlin/1425-constrained-subsequence-sum.kt new file mode 100644 index 000000000..00538ec7f --- /dev/null +++ b/kotlin/1425-constrained-subsequence-sum.kt @@ -0,0 +1,41 @@ +// Time O(nlogn) and space O(n) using max heap +class Solution { + fun constrainedSubsetSum(nums: IntArray, k: Int): Int { + var res = nums[0] + val maxHeap = PriorityQueue() { a, b -> b[0] - a[0] } + maxHeap.add(intArrayOf(nums[0], 0)) + + for (i in 1 until nums.size) { + while (i - maxHeap.peek()[1] > k) + maxHeap.poll() + + var curMax = maxOf(nums[i], nums[i] + maxHeap.peek()[0]) + res = maxOf(res, curMax) + maxHeap.add(intArrayOf(curMax, i)) + } + + return res + } +} + +// Time O(n) and space O(n) using monotonic array/list +class Solution { + fun constrainedSubsetSum(nums: IntArray, k: Int): Int { + var win = LinkedList() + + var res = Integer.MIN_VALUE + for (i in 0 until nums.size) { + nums[i] += (win.peekFirst() ?: 0) + res = maxOf(res, nums[i]) + + while (win.isNotEmpty() && win.peekLast() < nums[i]) + win.removeLast() + if (nums[i] > 0) + win.addLast(nums[i]) + if (i >= k && win.isNotEmpty() && win.peekFirst() == nums[i - k]) + win.removeFirst() + } + + return res + } +} diff --git a/kotlin/1431-kids-with-the-greatest-number-of-candies.kt b/kotlin/1431-kids-with-the-greatest-number-of-candies.kt new file mode 100644 index 000000000..fc53dfb25 --- /dev/null +++ b/kotlin/1431-kids-with-the-greatest-number-of-candies.kt @@ -0,0 +1,16 @@ +class Solution { + fun kidsWithCandies(candies: IntArray, extraCandies: Int): List { + val max = candies.max()!! + val res = LinkedList() + + for (candy in candies) { + if(candy + extraCandies >= max) { + res.addLast(true) + } else { + res.addLast(false) + } + } + + return res + } +} diff --git a/kotlin/1436-destination-city.kt b/kotlin/1436-destination-city.kt new file mode 100644 index 000000000..878e3211a --- /dev/null +++ b/kotlin/1436-destination-city.kt @@ -0,0 +1,17 @@ +class Solution { + fun destCity(paths: List>): String { + var fromCity: HashSet = hashSetOf() + for (path in paths) + fromCity.add(path[0]) + + var res = paths[0][1] + for (path in paths) { + if (path[1] !in fromCity) { + res = path[1] + break + } + } + + return res + } +} diff --git a/kotlin/1443-minimum-time-to-collect-all-apples-in-a-tree.kt b/kotlin/1443-minimum-time-to-collect-all-apples-in-a-tree.kt new file mode 100644 index 000000000..e29932439 --- /dev/null +++ b/kotlin/1443-minimum-time-to-collect-all-apples-in-a-tree.kt @@ -0,0 +1,26 @@ +class Solution { + fun minTime(n: Int, edges: Array, hasApple: List): Int { + val adj = HashMap>().apply { + for ((par, child) in edges) { + this[par] = (this[par] ?: mutableListOf()).apply { add(child) } + this[child] = (this[child] ?: mutableListOf()).apply { add(par) } + } + } + + fun dfs(cur: Int, par: Int): Int { + var time = 0 + + adj[cur]?.forEach { child -> + if (child != par) { + val childTime = dfs(child, cur) + if (childTime > 0 ||hasApple[child]) + time += 2 + childTime + } + } + + return time + } + + return dfs(0, -1) + } +} diff --git a/kotlin/1448-Count-Good-Nodes-In-Binary-Tree.kt b/kotlin/1448-Count-Good-Nodes-In-Binary-Tree.kt new file mode 100644 index 000000000..c3c3f8971 --- /dev/null +++ b/kotlin/1448-Count-Good-Nodes-In-Binary-Tree.kt @@ -0,0 +1,29 @@ +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun goodNodes(root: TreeNode?): Int { + return goodNodes(root, root!!.`val`) + } + + fun goodNodes(node: TreeNode?, parent: Int): Int { + if (node == null) + return 0; + + val res = if (parent > node.`val`) 0 else 1; + + val max = Math.max(parent, node.`val`); + + val left = goodNodes(node.left, max); + val right = goodNodes(node.right, max); + + return res + left + right; + } +} \ No newline at end of file diff --git a/kotlin/1448-count-good-nodes-in-binary-tree.kt b/kotlin/1448-count-good-nodes-in-binary-tree.kt new file mode 100644 index 000000000..c3c3f8971 --- /dev/null +++ b/kotlin/1448-count-good-nodes-in-binary-tree.kt @@ -0,0 +1,29 @@ +/** + * Example: + * var ti = TreeNode(5) + * var v = ti.`val` + * Definition for a binary tree node. + * class TreeNode(var `val`: Int) { + * var left: TreeNode? = null + * var right: TreeNode? = null + * } + */ +class Solution { + fun goodNodes(root: TreeNode?): Int { + return goodNodes(root, root!!.`val`) + } + + fun goodNodes(node: TreeNode?, parent: Int): Int { + if (node == null) + return 0; + + val res = if (parent > node.`val`) 0 else 1; + + val max = Math.max(parent, node.`val`); + + val left = goodNodes(node.left, max); + val right = goodNodes(node.right, max); + + return res + left + right; + } +} \ No newline at end of file diff --git a/kotlin/1456-maximum-number-of-vowels-in-a-substring-of-given-length.kt b/kotlin/1456-maximum-number-of-vowels-in-a-substring-of-given-length.kt new file mode 100644 index 000000000..fc9fb02f4 --- /dev/null +++ b/kotlin/1456-maximum-number-of-vowels-in-a-substring-of-given-length.kt @@ -0,0 +1,24 @@ +class Solution { + fun maxVowels(s: String, k: Int): Int { + val vowels = hashSetOf('a', 'e', 'i', 'o', 'u') + + var count = 0 + var max = 0 + var i = 0 + + while (i < k) { + if (s[i] in vowels) count++ + max = maxOf(max, count) + i++ + } + + while (i < s.length) { + count += if (s[i] in vowels) 1 else 0 + count -= if (s[i - k] in vowels) 1 else 0 + max = maxOf(max, count) + i++ + } + + return max + } +} diff --git a/kotlin/1457-pseudo-palindromic-paths-in-a-binary-tree.kt b/kotlin/1457-pseudo-palindromic-paths-in-a-binary-tree.kt new file mode 100644 index 000000000..6b22a32e1 --- /dev/null +++ b/kotlin/1457-pseudo-palindromic-paths-in-a-binary-tree.kt @@ -0,0 +1,27 @@ +class Solution { + fun pseudoPalindromicPaths (root: TreeNode?): Int { + val count = IntArray (10) + var odd = 0 + + fun dfs(node: TreeNode?): Int { + node ?: return 0 + + count[node.`val`]++ + val oddChange = if (count[node.`val`] % 2 == 1) 1 else -1 + odd += oddChange + + var res = 0 + if (node.left == null && node.right == null) + res = if (odd <= 1) 1 else 0 + else + res = dfs(node.left) + dfs(node.right) + + odd -= oddChange + count[node.`val`]-- + + return res + } + + return dfs(root) + } +} diff --git a/kotlin/1461-check-if-a-string-contains-all-binary-codes-of-size-k.kt b/kotlin/1461-check-if-a-string-contains-all-binary-codes-of-size-k.kt new file mode 100644 index 000000000..c3eb453c2 --- /dev/null +++ b/kotlin/1461-check-if-a-string-contains-all-binary-codes-of-size-k.kt @@ -0,0 +1,22 @@ +/* +* Sliding window technique +*/ +class Solution { + fun hasAllCodes(s: String, k: Int): Boolean { + val hs = HashSet() + val uniqueBits = 1 shl k + for(i in 0..s.lastIndex-k+1) { + val str = s.substring(i, i + k) + hs.add(str) + if(hs.size == uniqueBits) return true + } + return false + } +} + +/* +* Kotlin one line solution using Kotlin operations. +*/ +class Solution { + fun hasAllCodes(s: String, k: Int) = s.windowed(k).toHashSet().size == 1 shl k +} diff --git a/kotlin/1462-course-schedule-iv.kt b/kotlin/1462-course-schedule-iv.kt new file mode 100644 index 000000000..26c844b94 --- /dev/null +++ b/kotlin/1462-course-schedule-iv.kt @@ -0,0 +1,34 @@ +class Solution { + fun checkIfPrerequisite( + numCourses: Int, + prerequisites: Array, + queries: Array + ): List { + // { course -> [ immediate pre-requisites ] } + val adjacencyList = mutableMapOf>() + prerequisites.forEach { (immediatePrerequisite, course) -> + adjacencyList.getOrPut(course) { mutableListOf() }.add(immediatePrerequisite) + } + // { course -> [ complete set of prerequisites including transitive pre-requisites ] } + val prerequisitesMap = mutableMapOf>() + fun dfs(startCourse: Int, currentCourse: Int = startCourse) { + if (currentCourse in (prerequisitesMap[startCourse] ?: hashSetOf())) return + if (currentCourse != startCourse) { + prerequisitesMap.getOrPut(startCourse) { hashSetOf() }.add(currentCourse) + } + for (immediatePrerequisite in adjacencyList[currentCourse] ?: mutableListOf()) { + dfs(startCourse, immediatePrerequisite) + } + } + + for (course in 0 until numCourses) { + dfs(startCourse = course) + } + + val resultantList = mutableListOf() + queries.forEach { (prerequisite, course) -> + resultantList.add(prerequisitesMap[course]?.let { prerequisite in it } ?: false) + } + return resultantList + } +} \ No newline at end of file diff --git a/kotlin/1463-cherry-pickup-ii.kt b/kotlin/1463-cherry-pickup-ii.kt new file mode 100644 index 000000000..3e0974d43 --- /dev/null +++ b/kotlin/1463-cherry-pickup-ii.kt @@ -0,0 +1,64 @@ +// DP top-dowm approach +class Solution { + fun cherryPickup(grid: Array): Int { + val rows = grid.size + val cols = grid[0].size + var dp = Array (cols) { IntArray (cols) } + + for (r in (rows - 1) downTo 0) { + val curDP = Array (cols) { IntArray (cols) } + for (c1 in 0 until cols) { + for (c2 in c1 + 1 until cols) { + var max = 0 + val curPick = grid[r][c1] + grid[r][c2] + for (c12 in arrayOf(-1, 0, 1)) { + for (c22 in arrayOf(-1, 0, 1)) { + val nC1 = c1 + c12 + val nC2 = c2 + c22 + if (nC1 < 0 || nC2 == cols) continue + max = maxOf( + max, + curPick + dp[nC1][nC2] + ) + } + } + curDP[c1][c2] = max + } + } + dp = curDP + } + + return dp[0][cols - 1] + } +} + +// Recursion + Memoization +class Solution { + fun cherryPickup(grid: Array): Int { + val rows = grid.size + val cols = grid[0].size + val dp = HashMap() + + fun dfs(r: Int, c1: Int, c2: Int): Int { + if (c1 == c2 || minOf(c1, c2) < 0 || maxOf(c1, c2) == cols) return 0 + dp["$r:$c1:$c2"]?.let { return it } + if (r == rows - 1) return grid[r][c1] + grid[r][c2] + + var res = 0 + for (c12 in arrayOf(-1, 0, 1)) { + for (c22 in arrayOf(-1, 0, 1)) { + res = maxOf( + res, + dfs(r + 1, c1 + c12, c2 + c22) + ) + } + } + + res += grid[r][c1] + grid[r][c2] + dp["$r:$c1:$c2"] = res + return res + } + + return dfs(0, 0, cols - 1) + } +} diff --git a/kotlin/1466-reorder-routes-to-make-all-paths-lead-to-the-city-zero.kt b/kotlin/1466-reorder-routes-to-make-all-paths-lead-to-the-city-zero.kt new file mode 100644 index 000000000..16aabbedf --- /dev/null +++ b/kotlin/1466-reorder-routes-to-make-all-paths-lead-to-the-city-zero.kt @@ -0,0 +1,67 @@ +/* +* DFS Solution +*/ +class Solution { + fun minReorder(n: Int, connections: Array): Int { + + val neighbors = ArrayList>().apply { + for (i in 0 until n) + this.add(ArrayList()) + for ((u,v) in connections) { + this.get(u).apply { this.add(v) } + this.get(v).apply { this.add(u) } + } + } + + val edges = HashSet().apply { + for ((u,v) in connections) + this.add("$u:$v") + } + + var changes = 0 + val visited = BooleanArray(n) + + fun dfs(n: Int) { + for (nei in neighbors[n]) { + if (visited[nei] == true) + continue + if ("$nei:$n" !in edges) + changes++ + visited[nei] = true + dfs(nei) + } + } + + visited[0] = true + dfs(0) + + return changes + } +} + +/* +* BFS Solution +*/ +class Solution { + fun minReorder(n: Int, connections: Array): Int { + // node -> (adjacentNode,isArtificialEdge) + val adjacencyList = mutableMapOf>>() + connections.forEach { (u, v) -> // O(E) + adjacencyList.getOrPut(u) { mutableListOf() }.add(Pair(v, false)) + adjacencyList.getOrPut(v) { mutableListOf() }.add(Pair(u, true)) + } + val queue = LinkedList().apply { add(0) } + val visitedNodes = hashSetOf().apply { add(0) } + var minNumberOfEdgesToReOrient = 0 + while (queue.isNotEmpty()) { + val removedNode = queue.remove() + for ((adjacentNode, isConnectedArtificially) in adjacencyList[removedNode] ?: mutableListOf()) { + if (adjacentNode in visitedNodes) continue + if (!isConnectedArtificially) minNumberOfEdgesToReOrient++ + visitedNodes.add(adjacentNode) + queue.add(adjacentNode) + } + } + return minNumberOfEdgesToReOrient + } +} diff --git a/kotlin/1470-shuffle-the-array.kt b/kotlin/1470-shuffle-the-array.kt new file mode 100644 index 000000000..4e3e4b70e --- /dev/null +++ b/kotlin/1470-shuffle-the-array.kt @@ -0,0 +1,18 @@ +class Solution { + fun shuffle(nums: IntArray, n: Int): IntArray { + + val res = IntArray(2 * n) + var firstHalf = 0 + var secondHalf = n + + for (i in 0 until 2*n) { + if (i % 2 == 0) { + res[i] = nums[firstHalf++] + } else { + res[i] = nums[secondHalf++] + } + } + + return res + } +} diff --git a/kotlin/1472-design-browser-history.kt b/kotlin/1472-design-browser-history.kt new file mode 100644 index 000000000..95c98c3b2 --- /dev/null +++ b/kotlin/1472-design-browser-history.kt @@ -0,0 +1,84 @@ +/* +* Using an ArrayList +*/ +class BrowserHistory(homepage: String) { + + val history = ArrayList() + var size = 0 + var index = 0 + + init { + history.add(homepage) + size = 1 + } + + fun visit(url: String) { + if(history.size < index + 2) + history.add(url) + else + history[index + 1] = url + index++ + size = index + 1 + } + + fun back(steps: Int): String { + index = maxOf(index - steps, 0) + return history[index] + } + + fun forward(steps: Int): String { + index = minOf(index + steps, size - 1) + return history[index] + } +} + +/* +* Using a Doubly-LinkedList +*/ +class BrowserNode(val page: String) { + var next: BrowserNode? = null + var prev: BrowserNode? = null +} + +class BrowserHistory(homepage: String) { + + var current: BrowserNode? = null + + init { + current = BrowserNode(homepage) + } + + fun visit(url: String) { + val temp = BrowserNode(url) + current?.next = temp + temp.prev = current + current = current?.next + } + + fun back(steps: Int): String { + var stepsTaken = 0 + while(current?.prev != null && stepsTaken < steps) { + current = current?.prev + stepsTaken++ + } + return current!!.page + } + + fun forward(steps: Int): String { + var stepsTaken = 0 + while(current?.next != null && stepsTaken < steps) { + current = current?.next + stepsTaken++ + } + return current!!.page + } + +} + +/** + * Your BrowserHistory object will be instantiated and called as such: + * var obj = BrowserHistory(homepage) + * obj.visit(url) + * var param_2 = obj.back(steps) + * var param_3 = obj.forward(steps) + */ diff --git a/kotlin/1481-least-number-of-unique-integers-after-k-removals.kt b/kotlin/1481-least-number-of-unique-integers-after-k-removals.kt new file mode 100644 index 000000000..11e6d54b1 --- /dev/null +++ b/kotlin/1481-least-number-of-unique-integers-after-k-removals.kt @@ -0,0 +1,58 @@ +// Use of buckets +class Solution { + fun findLeastNumOfUniqueInts(arr: IntArray, k: Int): Int { + val n = arr.size + val counts = HashMap () + val buckets = HashMap () + + arr.forEach { n -> + counts[n] = (counts[n] ?: 0) + 1 + } + + counts.values.forEach { count -> + buckets[count] = (buckets[count] ?: 0) + 1 + } + + var k = k + var res = counts.size + for (n in 1 until buckets.size) { + var remove = buckets[n] ?: 0 + val total = n * remove + if (k >= total) { + k -= total + res -= remove + } else { + remove = k / n + res -= remove + break + } + } + + return res + } +} + +// Use a heap + class Solution { + fun findLeastNumOfUniqueInts(arr: IntArray, k: Int): Int { + val freq = HashMap () + arr.forEach { n -> + freq[n] = (freq[n] ?: 0) + 1 + } + + val heap = PriorityQueue { a, b -> a - b } + heap.addAll(freq.values) + + var k = k + var res = heap.size + while (k > 0 && heap.isNotEmpty()) { + val f = heap.poll() + if (k >= f) { + k -= f + res -= 1 + } + } + + return res + } +} diff --git a/kotlin/1489-find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.kt b/kotlin/1489-find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.kt new file mode 100644 index 000000000..1ff34e3d2 --- /dev/null +++ b/kotlin/1489-find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.kt @@ -0,0 +1,67 @@ +class Solution { + + class UnionFind(val n: Int) { + val par = IntArray (n) { it } + val rank = IntArray (n) { 1 } + + fun find(x: Int): Int { + if (par[x] != x) + par[x] = find(par[x]) + return par[x] + } + + fun union(x: Int, y: Int): Boolean { + val px = find(x) + val py = find(y) + if (px == py) return false + if (rank[px] > rank[py]) { + par[py] = px + rank[px] += rank[py] + } else { + par[px] = py + rank[py] += rank[px] + } + return true + } + } + + fun findCriticalAndPseudoCriticalEdges(n: Int, _edges: Array): List> { + val edges = _edges.mapIndexed { i, v -> + intArrayOf(v[0], v[1], v[2], i) + }.sortedWith( + Comparator { a, b -> + a[2] - b[2] + } + ) + + var mstWeight = 0 + val uf = UnionFind (n) + for ((u, v, w, i) in edges) { + if (uf.union(u, v)) mstWeight += w + } + + val crit = mutableListOf() + val pseudo = mutableListOf() + for ((u, v, w, i) in edges) { + val uf2 = UnionFind (n) + var mstWeightWithout = 0 + for ((u2, v2, w2, i2) in edges) { + if (i != i2 && uf2.union(u2, v2)) mstWeightWithout += w2 + } + if (uf2.rank.max()!! != n || mstWeightWithout > mstWeight) { + crit.add(i) + continue + } + + val uf3 = UnionFind (n) + uf3.union(u, v) + var mstWeightWith = w + for ((u3, v3, w3, i3) in edges) { + if (uf3.union(u3, v3)) mstWeightWith += w3 + } + if (mstWeightWith == mstWeight) pseudo.add(i) + } + + return listOf(crit, pseudo) + } +} diff --git a/kotlin/1496-path-crossing.kt b/kotlin/1496-path-crossing.kt new file mode 100644 index 000000000..11ccece52 --- /dev/null +++ b/kotlin/1496-path-crossing.kt @@ -0,0 +1,24 @@ +class Solution { + fun isPathCrossing(path: String): Boolean { + var dirs = mapOf( + 'N' to intArrayOf(0, 1), + 'S' to intArrayOf(0, -1), + 'E' to intArrayOf(1, 0), + 'W' to intArrayOf(-1, 0), + ) + val visit = HashSet>() + var x = 0 + var y = 0 + + for (c in path) { + visit.add(x to y) + val (dx, dy) = dirs[c]!! + x += dx + y += dy + if ((x to y) in visit) + return true + } + + return false + } +} diff --git a/kotlin/1498-number-of-subsequences-that-satisfy-the-given-sum-condition.kt b/kotlin/1498-number-of-subsequences-that-satisfy-the-given-sum-condition.kt new file mode 100644 index 000000000..3dac555bc --- /dev/null +++ b/kotlin/1498-number-of-subsequences-that-satisfy-the-given-sum-condition.kt @@ -0,0 +1,25 @@ +class Solution { + fun numSubseq(nums: IntArray, target: Int): Int { + nums.sort() + val mod = 1000000000 + 7 + + val pow = IntArray(nums.size) + pow[0] = 1 + for (i in 0 until nums.size - 1) + pow[i + 1] = pow[i] * 2 % mod + + var left = 0 + var right = nums.lastIndex + var res = 0 + while (left <= right) { + if (nums[left] + nums[right] > target) { + right-- + } else { + res = (res + pow[right - left]) % mod + left++ + } + } + + return res + } +} diff --git a/kotlin/1512-number-of-good-pairs.kt b/kotlin/1512-number-of-good-pairs.kt new file mode 100644 index 000000000..3114515a0 --- /dev/null +++ b/kotlin/1512-number-of-good-pairs.kt @@ -0,0 +1,51 @@ +// rolling count +class Solution { + fun numIdenticalPairs(nums: IntArray): Int { + val count = HashMap() + var total = 0 + + for (n in nums) { + count[n]?.let { + total += it + } + count[n] = count.getOrDefault(n, 0) + 1 + } + + return total + } +} + +// count and use arithmetic sequence +class Solution { + fun numIdenticalPairs(nums: IntArray): Int { + val count = HashMap().apply { + for (n in nums) { + this[n] = getOrDefault(n, 0) + 1 + } + } + + var res = 0 + for (c in count.values) { + res += c * (c - 1) / 2 + } + + return res + } +} + +// brute force +class Solution { + fun numIdenticalPairs(nums: IntArray): Int { + var count = 0 + + for (i in 0 until nums.size) { + for (j in i + 1 until nums.size) { + if (nums[i] == nums[j]) { + count++ + } + } + } + + return count + } +} diff --git a/kotlin/1514-path-with-maximum-probability.kt b/kotlin/1514-path-with-maximum-probability.kt new file mode 100644 index 000000000..d745c0fc4 --- /dev/null +++ b/kotlin/1514-path-with-maximum-probability.kt @@ -0,0 +1,35 @@ +class Solution { + fun maxProbability(n: Int, edges: Array, succProb: DoubleArray, start: Int, end: Int): Double { + val adj = HashMap>>().apply { + for ((i, edge) in edges.withIndex()) { + val (u, v) = edge + this[u] = getOrDefault(u, ArrayList>()).apply { add(v to succProb[i]) } + this[v] = getOrDefault(v, ArrayList>()).apply { add(u to succProb[i]) } + } + } + + val visited = HashSet() + val minHeap = PriorityQueue>{ a, b -> + if (a.second < b.second) 1 else -1 + } + + with (minHeap) { + add(start to 1.0) + + while (isNotEmpty()) { + val (node, currProb) = poll() + visited.add(node) + + if (node == end) return currProb + + adj[node]?.forEach { + if (it.first !in visited) { + add(it.first to currProb * it.second) + } + } + } + } + + return 0.0 + } +} diff --git a/kotlin/1523-count-odd-numbers-in-an-interval-range.kt b/kotlin/1523-count-odd-numbers-in-an-interval-range.kt new file mode 100644 index 000000000..7c4645536 --- /dev/null +++ b/kotlin/1523-count-odd-numbers-in-an-interval-range.kt @@ -0,0 +1,15 @@ +class Solution { + fun countOdds(low: Int, high: Int): Int { + + var res = 0 + res += (high - low) / 2 + if (low % 2 == 1 || high % 2 == 1) res += 1 + + return res + } +} + +//shorter 1 line solution +class Solution { + fun countOdds(low: Int, high: Int) = (high + 1) / 2 - low / 2 +} diff --git a/kotlin/1531-string-compression-ii.kt b/kotlin/1531-string-compression-ii.kt new file mode 100644 index 000000000..93532820f --- /dev/null +++ b/kotlin/1531-string-compression-ii.kt @@ -0,0 +1,27 @@ +class Solution { + fun getLengthOfOptimalCompression(s: String, k: Int): Int { + val cache = HashMap() + + fun count(i: Int, k: Int, prev: Char, prevCount: Int): Int { + cache["$i:$k:$prev:$prevCount"]?.let { return it } + if (k < 0) return Integer.MAX_VALUE + if (i == s.length) return 0 + + var res = -1 + if (s[i] == prev) { + val incr = if (prevCount in setOf(1, 9, 99)) 1 else 0 + res = incr + count(i + 1, k, s[i], prevCount + 1) + } else { + res = minOf( + count(i + 1, k - 1, prev, prevCount), + 1 + count(i + 1, k, s[i], 1) + ) + } + + cache["$i:$k:$prev:$prevCount"] = res + return res + } + + return count(0, k, 'Z', 0) + } +} diff --git a/kotlin/1547-minimum-cost-to-cut-a-stick.kt b/kotlin/1547-minimum-cost-to-cut-a-stick.kt new file mode 100644 index 000000000..84e252a85 --- /dev/null +++ b/kotlin/1547-minimum-cost-to-cut-a-stick.kt @@ -0,0 +1,29 @@ +class Solution { + fun minCost(n: Int, c: IntArray): Int { + val cuts = c.toCollection(ArrayList()).apply { + add(0) + add(n) + sort() + } + + val dp = Array (cuts.size) { IntArray (cuts.size) { -1 } } + + fun dfs(l: Int, r: Int): Int { + if (r - l <= 1) + return 0 + if (dp[l][r] == -1) { + dp[l][r] = Integer.MAX_VALUE + for (c in l + 1 until r) { + dp[l][r] = minOf( + dp[l][r], + dfs(l, c) + dfs(c, r) + (cuts[r] - cuts[l]) + ) + } + } + + return dp[l][r] + } + + return dfs(0, cuts.lastIndex) + } +} diff --git a/kotlin/1553-minimum-number-of-days-to-eat-n-oranges.kt b/kotlin/1553-minimum-number-of-days-to-eat-n-oranges.kt new file mode 100644 index 000000000..92ec3554b --- /dev/null +++ b/kotlin/1553-minimum-number-of-days-to-eat-n-oranges.kt @@ -0,0 +1,72 @@ +// dfs +class Solution { + fun minDays(n: Int): Int { + val dp = HashMap().apply { + this[0] = 0 + this[1] = 1 + } + + fun dfs(n: Int): Int { + if (n in dp) return dp[n]!! + + val divByTwo = 1 + (n % 2) + dfs(n / 2) + val divByThree = 1 + (n % 3) + dfs(n / 3) + + dp[n] = minOf( + divByTwo, + divByThree + ) + + return dp[n]!! + } + + return dfs(n) + } +} + +// Bonus: same as above but with more compact code +class Solution { + fun minDays(n: Int): Int { + val dp = HashMap() + + fun dfs(n: Int): Int { + if (n <= 1) return n + + if (n !in dp) { + dp[n] = minOf( + 1 + (n % 2) + dfs(n / 2), + 1 + (n % 3) + dfs(n / 3) + ) + } + + return dp[n]!! + } + + return dfs(n) + } +} + +// bfs +class Solution { + fun minDays(n: Int): Int { + val q = LinkedList().apply { add(n) } + val visited = HashSet() + var days = 1 + + while (q.isNotEmpty()) { + repeat (q.size) { + val n = q.removeFirst() + if (n == 1 || n == 0) return days + if (n !in visited) { + visited.add(n) + q.addLast(n - 1) + if (n % 2 == 0) q.addLast(n / 2) + if (n % 3 == 0) q.addLast(n / 3) + } + } + days++ + } + + return days + } +} diff --git a/kotlin/1557-minimum-number-of-vertices-to-reach-all-nodes.kt b/kotlin/1557-minimum-number-of-vertices-to-reach-all-nodes.kt new file mode 100644 index 000000000..001e10738 --- /dev/null +++ b/kotlin/1557-minimum-number-of-vertices-to-reach-all-nodes.kt @@ -0,0 +1,18 @@ +class Solution { + fun findSmallestSetOfVertices(n: Int, edges: List>): List { + val inDegreeZero = HashSet().apply { + for (i in 0 until n) + add(i) + } + + for (edge in edges) + inDegreeZero.remove(edge[1]) + + return inDegreeZero.toList() + } +} + +//kotlin idomatic +class Solution { + fun findSmallestSetOfVertices(n: Int, edges: List>) = (0..n - 1) - edges.map { it[1] } +} diff --git a/kotlin/1572-matrix-diagonal-sum.kt b/kotlin/1572-matrix-diagonal-sum.kt new file mode 100644 index 000000000..bc3f1f0d8 --- /dev/null +++ b/kotlin/1572-matrix-diagonal-sum.kt @@ -0,0 +1,15 @@ +class Solution { + fun diagonalSum(mat: Array): Int { + val n = mat.lastIndex + + var sum = 0 + for (i in 0..n) { + sum += mat[i][i] + mat[n - i][i] + } + + if (n % 2 == 0) + sum -= mat[n/2][n/2] + + return sum + } +} diff --git a/kotlin/1578-minimum-time-to-make-rope-colorful.kt b/kotlin/1578-minimum-time-to-make-rope-colorful.kt new file mode 100644 index 000000000..207cd01ff --- /dev/null +++ b/kotlin/1578-minimum-time-to-make-rope-colorful.kt @@ -0,0 +1,20 @@ +class Solution { + fun minCost(colors: String, neededTime: IntArray): Int { + var l = 0 + var res = 0 + for (r in 1 until colors.length) { + if (colors[l] == colors[r]) { + if (neededTime[l] < neededTime[r]) { + res += neededTime[l] + l = r + } else { + res += neededTime[r] + } + } else { + l = r + } + } + + return res + } +} diff --git a/kotlin/1579-remove-max-number-of-edges-to-keep-graph-fully-traversable.kt b/kotlin/1579-remove-max-number-of-edges-to-keep-graph-fully-traversable.kt new file mode 100644 index 000000000..b41112dfe --- /dev/null +++ b/kotlin/1579-remove-max-number-of-edges-to-keep-graph-fully-traversable.kt @@ -0,0 +1,63 @@ +class Solution { + + class DSU(val n: Int) { + val parent = IntArray(n + 1) {it} + val rank = IntArray(n + 1) {1} + var components = n + + fun find(x: Int): Int { + if (parent[x] != x) + parent[x] = find(parent[x]) + return parent[x] + } + + fun union(x: Int, y: Int): Boolean { + val pX = find(x) + val pY = find(y) + + if (pY == pX) + return false + + if (rank[pX] > rank[pY]) { + rank[pX] += rank[pY] + parent[pY] = pX + } else { + rank[pY] += rank[pX] + parent[pX] = pY + } + + components-- + return true + } + + fun connected() = components == 1 + } + + fun maxNumEdgesToRemove(n: Int, edges: Array): Int { + val a = DSU(n) + val b = DSU(n) + + var edgeAdded = 0 + for ((type, u, v) in edges) { + if (type == 3) { + if (a.union(u, v) or b.union(u, v)) + edgeAdded++ + } + } + + for ((type, u, v) in edges) { + when (type) { + 1 -> { + if (a.union(u, v)) + edgeAdded++ + } + 2 -> { + if (b.union(u, v)) + edgeAdded++ + } + } + } + + return if (a.connected() && b.connected()) edges.size - edgeAdded else -1 + } +} diff --git a/kotlin/1584-min-cost-to-connect-all-points.kt b/kotlin/1584-min-cost-to-connect-all-points.kt new file mode 100644 index 000000000..aa684c44a --- /dev/null +++ b/kotlin/1584-min-cost-to-connect-all-points.kt @@ -0,0 +1,22 @@ +class Solution { + fun minCostConnectPoints(points: Array): Int { + val minHeap = PriorityQueue> { a: Pair, b: Pair -> a.second - b.second } // sort by distance + val visited = HashSet() + var minCost = 0 + minHeap.add(Pair(0,0)) + while(visited.size < points.size){ + val (node,cost) = minHeap.poll() + if(visited.contains(node)) + continue + minCost += cost + visited.add(node) + for(i in 0..points.size-1){ + val (nextX,nextY) = points[i] + val (currentX, CurrentY) = points[node] + val distance = Math.abs(currentX - nextX) + Math.abs(CurrentY - nextY) + minHeap.add(Pair(i,distance)) + } + } + return minCost + } +} diff --git a/kotlin/1603-design-parking-system.kt b/kotlin/1603-design-parking-system.kt new file mode 100644 index 000000000..4b95b6ee8 --- /dev/null +++ b/kotlin/1603-design-parking-system.kt @@ -0,0 +1,13 @@ +class ParkingSystem(big: Int, medium: Int, small: Int) { + + val spaces = intArrayOf(big, medium, small) + + fun addCar(carType: Int): Boolean { + if (spaces[carType - 1] > 0) { + spaces[carType - 1]-- + return true + } + return false + } + +} diff --git a/kotlin/1609-even-odd-tree.kt b/kotlin/1609-even-odd-tree.kt new file mode 100644 index 000000000..d4d26d8c1 --- /dev/null +++ b/kotlin/1609-even-odd-tree.kt @@ -0,0 +1,28 @@ +class Solution { + fun isEvenOddTree(root: TreeNode?): Boolean { + val q = LinkedList ().apply { addLast(root) } + var even = true + + while (q.isNotEmpty()) { + var prev = if (isEven) Integer.MIN_VALUE else Integer.MAX_VALUE + + repeat (q.size) { + val node = q.removeFirst()!! + + if (even && (node.`val` % 2 == 0 || node.`val` <= prev)) + return false + else if (!even && (node.`val` % 2 == 1 || node.`val` >= prev)) + return false + + prev = node.`val` + + node.left?.let { q.addLast(it) } + node.right?.let { q.addLast(it) } + } + + even = !even + } + + return true + } +} diff --git a/kotlin/1611-minimum-one-bit-operations-to-make-integers-zero.kt b/kotlin/1611-minimum-one-bit-operations-to-make-integers-zero.kt new file mode 100644 index 000000000..4b1889dff --- /dev/null +++ b/kotlin/1611-minimum-one-bit-operations-to-make-integers-zero.kt @@ -0,0 +1,14 @@ +class Solution { + fun minimumOneBitOperations(n: Int): Int { + if (n == 0) return 0 + + var k = 0 + while (2 p k <= n) + k++ + k -= 1 + + return (2 p (k + 1)) - 1 - minimumOneBitOperations((2 p k) xor n) + } + + infix fun Int.p(k: Int) = Math.pow(this.toDouble(), k.toDouble()).toInt() +} diff --git a/kotlin/1624-largest-substring-between-two-equal-characters.kt b/kotlin/1624-largest-substring-between-two-equal-characters.kt new file mode 100644 index 000000000..595fec00f --- /dev/null +++ b/kotlin/1624-largest-substring-between-two-equal-characters.kt @@ -0,0 +1,34 @@ +class Solution { + fun maxLengthBetweenEqualCharacters(s: String): Int { + val charIndex = HashMap() + var res = -1 + + for ((i, c) in s.withIndex()) { + if (c in charIndex) + res = maxOf(res, i - charIndex[c]!! - 1) + else + charIndex[c] = i + } + + return res + } +} + +// Similar but slightly different way +class Solution { + fun maxLengthBetweenEqualCharacters(s: String): Int { + val letters = Array (26) { intArrayOf(302, -302) } + var res = -1 + for (l in letters.indices) { + for ((i, c) in s.withIndex()) { + if (c == ('a' + l)) { + letters[l][0] = minOf(letters[l][0], i) + letters[l][1] = maxOf(letters[l][1], i) + res = maxOf(res, letters[l][1] - letters[l][0] - 1) + } + } + } + + return res + } +} diff --git a/kotlin/1626-best-team-with-no-conflicts.kt b/kotlin/1626-best-team-with-no-conflicts.kt new file mode 100644 index 000000000..d3d617f62 --- /dev/null +++ b/kotlin/1626-best-team-with-no-conflicts.kt @@ -0,0 +1,26 @@ +class Solution { + fun bestTeamScore(scores: IntArray, ages: IntArray): Int { + val n = scores.size + + val pairs = scores.zip(ages).sortedWith( + compareBy({it.first}, {it.second}) + ) + + val dp = IntArray (n).apply { + for ((i, v) in pairs.withIndex()) + this[i] = v.first + } + + for (i in 0 until n) { + val (score, age) = pairs[i] + for (j in 0 until i) { + val (score2, age2) = pairs[j] + if (age >= age2) { + dp[i] = maxOf(dp[i], score + dp[j]) + } + } + } + + return dp.max() ?: 0 + } +} diff --git a/kotlin/1631-path-with-minimum-effort.kt b/kotlin/1631-path-with-minimum-effort.kt new file mode 100644 index 000000000..94e215524 --- /dev/null +++ b/kotlin/1631-path-with-minimum-effort.kt @@ -0,0 +1,141 @@ +//dijkstra +class Solution { + fun minimumEffortPath(h: Array): Int { + val minHeap = PriorityQueue { a, b -> a[2] - b[2] } + val dirs = intArrayOf(0, 1, 0, -1, 0) + val n = h.size + val m = h[0].size + val visited = Array (n) { BooleanArray (m) } + + fun isValid(x: Int, y: Int) = x in (0 until n) && y in (0 until m) + + minHeap.add(intArrayOf(0, 0, 0)) + while (minHeap.isNotEmpty()) { + val (i, j, e) = minHeap.poll() + + if (i == n - 1 && j == m - 1) return e + visited[i][j] = true + + for (k in 0..3) { + val i2 = i + dirs[k] + val j2 = j + dirs[k + 1] + if (isValid(i2, j2) && !visited[i2][j2]) { + val e2 = Math.abs(h[i][j] - h[i2][j2]) + minHeap.add(intArrayOf(i2, j2, maxOf(e, e2))) + } + } + } + + return 0 + } +} + +// binary search + dfs to find min effort to reach end from start +class Solution { + fun minimumEffortPath(h: Array): Int { + val dirs = intArrayOf(0, 1, 0, -1, 0) + val n = h.size + val m = h[0].size + var visited = Array (n) { BooleanArray (m) } + + fun isValid(x: Int, y: Int) = x in (0 until n) && y in (0 until m) + + fun dfs(x: Int, y: Int, k: Int): Boolean { + if (x == n - 1 && y == m - 1) return true + + visited[x][y] = true + + for (i in 0..3) { + val x2 = x + dirs[i] + val y2 = y + dirs[i + 1] + if (isValid(x2, y2) && !visited[x2][y2] && Math.abs(h[x][y] - h[x2][y2]) <= k) { + if (dfs(x2, y2, k)) + return true + } + } + + return false + } + + var left = 0 + var right = 1000000 + var res = right + while (left <= right) { + val mid = (right + left) / 2 + visited = Array (n) { BooleanArray (m) } + if (dfs(0, 0, mid)) { + res = mid + right = mid - 1 + } else { + left = mid + 1 + } + } + + return res + } +} + +//MST with kruskals algorith (using DSU) +class Solution { + fun minimumEffortPath(h: Array): Int { + val n = h.size + val m = h[0].size + val dsu = DSU(n * m) + val edges = mutableListOf() + + fun c(x: Int, y: Int) = x * m + y + + for (i in 0 until n) { + for (j in 0 until m) { + if (i + 1 < n) { + val e = Math.abs(h[i][j] - h[i + 1][j]) + edges.add(intArrayOf(c(i, j), c(i + 1, j), e)) + } + if (j + 1 < m) { + val e = Math.abs(h[i][j] - h[i][j + 1]) + edges.add(intArrayOf(c(i, j), c(i, j + 1), e)) + } + } + } + + edges.sortWith { a, b -> a[2] - b[2] } + + for ((u, v, e) in edges) { + if (dsu.union(u, v)) { + if (dsu.find(c(0, 0)) == dsu.find(c(n - 1, m - 1))) { + return e + } + } + } + + return 0 + } +} + +class DSU(val n: Int) { + val parent = IntArray (n) { it } + val size = IntArray (n) { 1 } + + fun find(x: Int): Int { + if (parent[x] != x) + parent[x] = find(parent[x]) + return parent[x] + } + + fun union(x: Int, y: Int): Boolean { + val p1 = find(x) + val p2 = find(y) + + if (p1 == p2) return false + + if (size[p1] > size[p2]) { + parent[p2] = p1 + size[p1] += size[p2] + } else { + parent[p1] = p2 + size[p2] += size[p1] + } + + return true + } +} diff --git a/kotlin/1637-widest-vertical-area-between-two-points-containing-no-points.kt b/kotlin/1637-widest-vertical-area-between-two-points-containing-no-points.kt new file mode 100644 index 000000000..b2b6731da --- /dev/null +++ b/kotlin/1637-widest-vertical-area-between-two-points-containing-no-points.kt @@ -0,0 +1,9 @@ +class Solution { + fun maxWidthOfVerticalArea(points: Array): Int { + points.sortBy { it[0] } + var res = -1 + for (i in 1 until points.size) + res = maxOf(res, points[i][0] - points[i - 1][0]) + return res + } +} diff --git a/kotlin/1639-number-of-ways-to-form-a-target-string-given-a-dictionary.kt b/kotlin/1639-number-of-ways-to-form-a-target-string-given-a-dictionary.kt new file mode 100644 index 000000000..7e29699e5 --- /dev/null +++ b/kotlin/1639-number-of-ways-to-form-a-target-string-given-a-dictionary.kt @@ -0,0 +1,34 @@ +class Solution { + fun numWays(words: Array, target: String): Int { + val mod = 1000000000 + 7 + val m = words[0].length + val n = target.length + + val count = Array(m){IntArray(26)} + for (w in words) { + for ((i, c) in w.withIndex()) { + count[i][c - 'a'] += 1 + } + } + + val dp = Array(n){LongArray(m){-1L}} + + fun dfs(i: Int, k: Int): Long { + if (i == n) + return 1L + if (k == m) + return 0L + if (dp[i][k] != -1L) + return dp[i][k] + + val c = target[i] + dp[i][k] = dfs(i, k + 1) + if(count[k][c - 'a'] != 0) + dp[i][k] += count[k][c - 'a'] * dfs(i + 1, k + 1) + + return dp[i][k] % mod + } + + return dfs(0, 0).toInt() + } +} diff --git a/kotlin/1642-furthest-building-you-can-reach.kt b/kotlin/1642-furthest-building-you-can-reach.kt new file mode 100644 index 000000000..5d7d1da58 --- /dev/null +++ b/kotlin/1642-furthest-building-you-can-reach.kt @@ -0,0 +1,23 @@ +class Solution { + fun furthestBuilding(heights: IntArray, bricks: Int, ladders: Int): Int { + val maxHeap = PriorityQueue { a, b -> b - a } + var bricks = bricks + var ladders = ladders + + for (i in 0 until heights.lastIndex) { + val diff = heights[i + 1] - heights[i] + if (diff <= 0) continue + + bricks = bricks - diff + maxHeap.add(diff) + + if (bricks < 0) { + if (ladders == 0) return i + ladders -= 1 + bricks += maxHeap.poll() + } + } + + return heights.lastIndex + } +} diff --git a/kotlin/1647-minimum-deletions-to-make-character-frequencies-unique.kt b/kotlin/1647-minimum-deletions-to-make-character-frequencies-unique.kt new file mode 100644 index 000000000..02a8a5838 --- /dev/null +++ b/kotlin/1647-minimum-deletions-to-make-character-frequencies-unique.kt @@ -0,0 +1,22 @@ +class Solution { + fun minDeletions(s: String): Int { + val count = HashMap().apply { + for (c in s) + this[c] = getOrDefault(c, 0) + 1 + } + + val usedFreq = HashSet() + + var res = 0 + for ((char, freq) in count) { + var freq = freq + while (freq > 0 && freq in usedFreq) { + freq-- + res++ + } + usedFreq.add(freq) + } + + return res + } +} diff --git a/kotlin/1658-minimum-operations-to-reduce-x-to-zero.kt b/kotlin/1658-minimum-operations-to-reduce-x-to-zero.kt new file mode 100644 index 000000000..cb221a2a1 --- /dev/null +++ b/kotlin/1658-minimum-operations-to-reduce-x-to-zero.kt @@ -0,0 +1,22 @@ +class Solution { + fun minOperations(nums: IntArray, x: Int): Int { + var left = 0 + var max = -1 + var cur = 0 + var target = nums.sum()!! - x + + for (right in 0 until nums.size) { + cur += nums[right] + + while (left <= right && cur > target) { + cur -= nums[left++] + } + + if (cur == target) { + max = maxOf(max, right - left + 1) + } + } + + return if (max == -1) -1 else nums.size - max + } +} diff --git a/kotlin/1662-check-if-two-string-arrays-are-equivalent.kt b/kotlin/1662-check-if-two-string-arrays-are-equivalent.kt new file mode 100644 index 000000000..119aca8a9 --- /dev/null +++ b/kotlin/1662-check-if-two-string-arrays-are-equivalent.kt @@ -0,0 +1,34 @@ +class Solution { + fun arrayStringsAreEqual(word1: Array, word2: Array): Boolean { + var w1 = 0 + var w2 = 0 + var i = 0 + var j = 0 + + while (w1 < word1.size && w2 < word2.size) { + if (word1[w1][i] != word2[w2][j]) + return false + + i++ + j++ + if (i == word1[w1].length) { + w1++ + i = 0 + } + if (j == word2[w2].length) { + w2++ + j = 0 + } + } + + if (w1 != word1.size || w2 != word2.size) + return false + + return true + } +} + +// short kotlin one liner +class Solution { + fun arrayStringsAreEqual(w1: Array, w2: Array) = w1.joinToString("") == w2.joinToString("") +} diff --git a/kotlin/1669-merge-in-between-linked-lists.kt b/kotlin/1669-merge-in-between-linked-lists.kt new file mode 100644 index 000000000..ad2ba899e --- /dev/null +++ b/kotlin/1669-merge-in-between-linked-lists.kt @@ -0,0 +1,23 @@ +class Solution { + fun mergeInBetween(list1: ListNode?, a: Int, b: Int, list2: ListNode?): ListNode? { + var curr = list1 + var i = 0 + while (i < a - 1) { + curr = curr?.next + i++ + } + + var head = curr + while (i <= b) { + curr = curr?.next + i++ + } + head?.next = list2 + + var list2 = list2 + while (list2?.next != null) + list2 = list2?.next + list2?.next = curr + return list1 + } +} diff --git a/kotlin/1675-minimize-deviation-in-array.kt b/kotlin/1675-minimize-deviation-in-array.kt new file mode 100644 index 000000000..1ec451514 --- /dev/null +++ b/kotlin/1675-minimize-deviation-in-array.kt @@ -0,0 +1,68 @@ +//another solution using a max Heap and a min Heap. +class Solution { + fun minimumDeviation(nums: IntArray): Int { + + val minHeap = PriorityQueue() + var maxHeap = PriorityQueue(Collections.reverseOrder()) + var res = Integer.MAX_VALUE + + // O(N) + // For each number, we are adding the largest number it can become + // Even numbers can't get bigger, so we add it + // Odd Numbers can get to twice it size, so we add that + for(num in nums) { + minHeap.add( if(num % 2 == 0) num else num * 2) + maxHeap.add( if(num % 2 == 0) num else num * 2) + } + + var maxDiff = maxHeap.peek() - minHeap.peek() + var max = maxHeap.poll() + + // O(nlogM * logN) + // We are halving each even number in our max, adding it to min and max, and getting the new possible min each time + // Loop until maxHeap top reached an odd number, then we are checked all possible mins + while(max % 2 == 0) { + max /= 2 + minHeap.add(max) + maxHeap.add(max) + max = maxHeap.poll() + maxDiff = minOf(maxDiff, max - minHeap.peek()) + } + + return maxDiff + } +} + +class Solution { + fun minimumDeviation(nums: IntArray): Int { + + // minHeap with pair of N to it's maximum value X that N can get to. + // For odd Numbers, N = N and X = 2 * N + // For even numbers, N = N/2 until it's odd, X = N + val minHeap = PriorityQueue> {a,b -> a.first - b.first} + var heapMax = 0 + var res = Integer.MAX_VALUE + + // O(nlogm) + for(_num in nums) { + var num = _num + while(num % 2 == 0) { + num /= 2 + } + minHeap.add(num to maxOf(_num, 2 * num)) + heapMax = maxOf(heapMax, num) + } + + // O(nlogm * logn) + while(minHeap.size == nums.size) { + val (n, nMax) = minHeap.poll() + res = minOf(res, heapMax - n) + if(n < nMax) { + minHeap.add(n * 2 to nMax) + heapMax = maxOf(heapMax, n * 2) + } + } + + return res + } +} diff --git a/kotlin/1685-sum-of-absolute-differences-in-a-sorted-array.kt b/kotlin/1685-sum-of-absolute-differences-in-a-sorted-array.kt new file mode 100644 index 000000000..71a33cfdc --- /dev/null +++ b/kotlin/1685-sum-of-absolute-differences-in-a-sorted-array.kt @@ -0,0 +1,16 @@ +class Solution { + fun getSumAbsoluteDifferences(nums: IntArray): IntArray { + val totalSum = nums.sum()!! + val n = nums.size + var leftSum = 0 + val res = IntArray (nums.size) + + for ((i, num) in nums.withIndex()) { + val rightSum = totalSum - num - leftSum + res[i] = i * num - leftSum + rightSum - (n - i - 1) * num + leftSum += num + } + + return res + } +} diff --git a/kotlin/1688-count-of-matches-in-tournament.kt b/kotlin/1688-count-of-matches-in-tournament.kt new file mode 100644 index 000000000..71bfcbfe8 --- /dev/null +++ b/kotlin/1688-count-of-matches-in-tournament.kt @@ -0,0 +1,17 @@ +// O(1) using summation of series +class Solution { + fun numberOfMatches(n: Int) = n - 1 +} + +// log(n) +class Solution { + fun numberOfMatches(n: Int): Int { + var x = n + var res = 0 + while (x > 1) { + res += x / 2 + x = (x + 1) / 2 + } + return res + } +} diff --git a/kotlin/1716-calculate-money-in-leetcode-bank.kt b/kotlin/1716-calculate-money-in-leetcode-bank.kt new file mode 100644 index 000000000..4eb2e24fe --- /dev/null +++ b/kotlin/1716-calculate-money-in-leetcode-bank.kt @@ -0,0 +1,24 @@ +class Solution { + fun totalMoney(n: Int): Int { + val weeks = n / 7 + var res = (weeks * (28 + 28 + 7 * (weeks - 1)) / 2) + + val monday = weeks + 1 + val days = n % 7 + for (i in 0 until days) + res += i + monday + + return res + } +} + +// or alternativly, sum up the n % 7 last days using a formula +class Solution { + fun totalMoney(n: Int): Int { + val weeks = n / 7 + val days = n % 7 + var res = (weeks * (28 + 28 + 7 * (weeks - 1)) / 2) + res += (2 * weeks + days + 1) * days / 2 + return res + } +} diff --git a/kotlin/1721-swapping-nodes-in-a-linked-list.kt b/kotlin/1721-swapping-nodes-in-a-linked-list.kt new file mode 100644 index 000000000..13c59073e --- /dev/null +++ b/kotlin/1721-swapping-nodes-in-a-linked-list.kt @@ -0,0 +1,27 @@ +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun swapNodes(head: ListNode?, k: Int): ListNode? { + var cur = head + repeat(k - 1) { cur = cur?.next } + + var left = cur + + var right = head + while (cur?.next != null) { + cur = cur?.next + right = right?.next + } + + left?.`val` = right?.`val`.also { right?.`val` = left?.`val` } + return head + } + +} diff --git a/kotlin/1727-largest-submatrix-with-rearrangements.kt b/kotlin/1727-largest-submatrix-with-rearrangements.kt new file mode 100644 index 000000000..1257638b8 --- /dev/null +++ b/kotlin/1727-largest-submatrix-with-rearrangements.kt @@ -0,0 +1,24 @@ +class Solution { + fun largestSubmatrix(matrix: Array): Int { + val n = matrix.size + val m = matrix[0].size + var prevHeights = IntArray (m) + var res = 0 + + for (i in 0 until n) { + val curHeights = matrix[i].copyOf() + for (j in 0 until m) { + if (curHeights[j] > 0) + curHeights[j] += prevHeights[j] + } + + val sortedHeights = curHeights.sortedDescending() + for (k in 0 until m) + res = maxOf(res, (k + 1) * sortedHeights[k]) + + prevHeights = curHeights + } + + return res + } +} diff --git a/kotlin/1758-minimum-changes-to-make-alternating-binary-string.kt b/kotlin/1758-minimum-changes-to-make-alternating-binary-string.kt new file mode 100644 index 000000000..65515df68 --- /dev/null +++ b/kotlin/1758-minimum-changes-to-make-alternating-binary-string.kt @@ -0,0 +1,14 @@ +class Solution { + fun minOperations(s: String): Int { + var count = 0 + + for ((i, c) in s.withIndex()) { + if (i % 2 == 0) + count += if (c == '0') 1 else 0 + else + count += if (c == '1') 1 else 0 + } + + return minOf(count, s.length - count) + } +} diff --git a/kotlin/1768-merge-strings-alternately.kt b/kotlin/1768-merge-strings-alternately.kt new file mode 100644 index 000000000..d04de8ae4 --- /dev/null +++ b/kotlin/1768-merge-strings-alternately.kt @@ -0,0 +1,19 @@ +class Solution { + fun mergeAlternately(word1: String, word2: String): String { + val m = word1.length + val n = word2.length + var i = 0 + var j = 0 + + val res = StringBuilder() + while (i < m && j < n) { + res.append(word1[i++]) + res.append(word2[j++]) + } + + res.append(word1.drop(i)) + res.append(word2.drop(j)) + + return res.toString() + } +} diff --git a/kotlin/1793-maximum-score-of-a-good-subarray.kt b/kotlin/1793-maximum-score-of-a-good-subarray.kt new file mode 100644 index 000000000..d9cc080b3 --- /dev/null +++ b/kotlin/1793-maximum-score-of-a-good-subarray.kt @@ -0,0 +1,25 @@ +class Solution { + fun maximumScore(nums: IntArray, k: Int): Int { + var l = k + var r = k + var res = nums[k] + var curMin = nums[k] + + while (l > 0 || r < nums.lastIndex) { + var left = if (l > 0) nums[l - 1] else 0 + var right = if (r < nums.lastIndex) nums[r + 1] else 0 + + if (left > right) { + l-- + curMin = minOf(curMin, left) + } else { + r++ + curMin = minOf(curMin, right) + } + + res = maxOf(res, curMin * (r - l + 1)) + } + + return res + } +} diff --git a/kotlin/1799-maximize-score-after-n-operations.kt b/kotlin/1799-maximize-score-after-n-operations.kt new file mode 100644 index 000000000..767350661 --- /dev/null +++ b/kotlin/1799-maximize-score-after-n-operations.kt @@ -0,0 +1,40 @@ +class Solution { + fun maxScore(nums: IntArray): Int { + val cache = IntArray(1 shl nums.size) { -1 } + + fun dfs(mask: Int, op: Int): Int { + if (cache[mask] != -1) return cache[mask] + + for (i in 0 until nums.size) { + for (j in i + 1 until nums.size) { + if ((1 shl i) and mask > 0 || (1 shl j) and mask > 0) + continue + val newMask = mask or (1 shl i) or (1 shl j) + val score = op * gcd(nums[i], nums[j]) + cache[mask] = maxOf( + if (cache[mask] != -1) cache[mask] else 0, + score + dfs(newMask, op + 1) + ) + } + } + + return if (cache[mask] != -1) cache[mask] else 0 + } + + return dfs(0, 1) + } + + fun gcd(_x: Int, _y: Int): Int { + var x = _x + var y = _y + + while (x != y) { + if (x > y) + x -= y + else + y -= x + } + + return x + } +} diff --git a/kotlin/1822-sign-of-the-product-of-an-array.kt b/kotlin/1822-sign-of-the-product-of-an-array.kt new file mode 100644 index 000000000..662391b06 --- /dev/null +++ b/kotlin/1822-sign-of-the-product-of-an-array.kt @@ -0,0 +1,12 @@ +class Solution { + fun arraySign(nums: IntArray): Int { + var res = 1 + for (n in nums){ + if (n == 0) + return 0 + if (n < 0) + res *= -1 + } + return if (res > 0) 1 else -1 + } +} diff --git a/kotlin/1834-single-threaded-cpu.kt b/kotlin/1834-single-threaded-cpu.kt new file mode 100644 index 000000000..d02ac089d --- /dev/null +++ b/kotlin/1834-single-threaded-cpu.kt @@ -0,0 +1,31 @@ +class Solution { + fun getOrder(tasks: Array): IntArray { + val res = mutableListOf() + val sorted = Array(tasks.size){ IntArray(3) } + for(i in tasks.indices){ + sorted[i][0] = tasks[i][0] + sorted[i][1] = tasks[i][1] + sorted[i][2] = i + } + sorted.sortBy{it -> it[0]} + val pq: PriorityQueue = PriorityQueue{a, b -> + if(a[0] == b[0]) a[1] - b[1] + else a[0] - b[0] + } + var time = sorted[0][0]; var index = 0 + while(!pq.isEmpty() || index < sorted.size){ + while(index < sorted.size && time >= sorted[index][0]){ + pq.offer(intArrayOf(sorted[index][1],sorted[index][2])) + index++ + } + if(!pq.isEmpty()){ + val (pT, i) = pq.poll() + time += pT + res.add(i) + }else{ + time = sorted[index][0] + } + } + return res.toIntArray() + } +} diff --git a/kotlin/1838-frequency-of-the-most-frequent-element.kt b/kotlin/1838-frequency-of-the-most-frequent-element.kt new file mode 100644 index 000000000..eccd8e0b7 --- /dev/null +++ b/kotlin/1838-frequency-of-the-most-frequent-element.kt @@ -0,0 +1,26 @@ +class Solution { + fun maxFrequency(nums: IntArray, k: Int): Int { + nums.sort() + + var res = 0 + var total = 0L + var left = 0 + var right = 0 + + while (right < nums.size) { + total += nums[right] + + if (nums[right] * (right - left + 1) > total + k) + total -= nums[left++] + + res = maxOf( + res, + (right - left + 1) + ) + + right++ + } + + return res + } +} diff --git a/kotlin/1845-seat-reservation-manager.kt b/kotlin/1845-seat-reservation-manager.kt new file mode 100644 index 000000000..0e032663f --- /dev/null +++ b/kotlin/1845-seat-reservation-manager.kt @@ -0,0 +1,15 @@ +class SeatManager(n: Int) { + val unres = PriorityQueue() + + init { + for (i in 1..n) + unres.add(i) + } + + fun reserve() = unres.poll() + + fun unreserve(seatNumber: Int) { + unres.add(seatNumber) + } + +} diff --git a/kotlin/1846-maximum-element-after-decreasing-and-rearranging.kt b/kotlin/1846-maximum-element-after-decreasing-and-rearranging.kt new file mode 100644 index 000000000..606f76391 --- /dev/null +++ b/kotlin/1846-maximum-element-after-decreasing-and-rearranging.kt @@ -0,0 +1,56 @@ +// 1. O(nlogn) solution using sorting +class Solution { + fun maximumElementAfterDecrementingAndRearranging(arr: IntArray): Int { + arr.sort() + var prev = 0 + + for (n in arr) { + prev = minOf(prev + 1, n) + } + + return prev + } +} + +// 2. O(n) solution but at the cost of O(1) -> O(n) space +class Solution { + fun maximumElementAfterDecrementingAndRearranging(arr: IntArray): Int { + val n = arr.size + var count = IntArray (n + 1).apply { + for (num in arr) + this[minOf(n, num)]++ + } + + var last = 1 + for (num in 1..n) + last = minOf(last + count[num], num) + + return last + } +} + +// 3. Same as solution 1, but using Kotlin's Aggregate operation fold() +class Solution { + fun maximumElementAfterDecrementingAndRearranging(arr: IntArray) = arr.sorted() + .fold(0) { acc, num -> minOf(acc + 1, num) } +} + +// 4. Or alternativly, we could use a runningFold() +class Solution { + fun maximumElementAfterDecrementingAndRearranging(arr: IntArray) = arr.sorted() + .runningFold (0) { acc, num -> minOf(acc + 1, num) } + .last() +} + +// 5. Same logic as first solution, but using a minHeap instead of sorting +class Solution { + fun maximumElementAfterDecrementingAndRearranging(arr: IntArray) = with (PriorityQueue()) { + addAll(arr.asSequence()) + var last = 0 + while (isNotEmpty()) { + if (poll() > last) + last++ + } + last + } +} diff --git a/kotlin/1849-splitting-a-string-into-descending-consecutive-values.kt b/kotlin/1849-splitting-a-string-into-descending-consecutive-values.kt new file mode 100644 index 000000000..24ae93c16 --- /dev/null +++ b/kotlin/1849-splitting-a-string-into-descending-consecutive-values.kt @@ -0,0 +1,25 @@ +class Solution { + fun splitString(s: String): Boolean { + + fun dfs(i: Int, prev: Double): Boolean { + if (i == s.length) + return true + + for (j in i until s.length) { + val digit = s.substring(i, j + 1).toDouble() + if (digit + 1 == prev && dfs(j + 1, digit)) + return true + } + + return false + } + + for (i in 0 until s.lastIndex) { + val digit = s.substring(0, i + 1).toDouble() + if (dfs(i + 1, digit)) + return true + } + + return false + } +} diff --git a/kotlin/1851-minimum-interval-to-include-each-query.kt b/kotlin/1851-minimum-interval-to-include-each-query.kt new file mode 100644 index 000000000..845d01f32 --- /dev/null +++ b/kotlin/1851-minimum-interval-to-include-each-query.kt @@ -0,0 +1,24 @@ +class Solution { + fun minInterval(intervals: Array, _queries: IntArray): IntArray { + intervals.sortWith(compareBy( {it[0]}, { it[1]})) + val queries = _queries.withIndex().sortedBy { it.value } + val minHeap = PriorityQueue (compareBy( { it[0] }, { it[1] })) + val res = IntArray (queries.size) + + var i = 0 + queries.forEach { (idx, q) -> + while (i < intervals.size && intervals[i][0] <= q) { + val (l, r) = intervals[i] + minHeap.add(intArrayOf(r - l + 1, r)) + i++ + } + + while (minHeap.isNotEmpty() && minHeap.peek()[1] < q) + minHeap.poll() + + res[idx] = if (minHeap.isEmpty()) -1 else minHeap.peek()[0] + } + + return res + } +} diff --git a/kotlin/1856-maximum-subarray-min-product.kt b/kotlin/1856-maximum-subarray-min-product.kt new file mode 100644 index 000000000..8fd6482fd --- /dev/null +++ b/kotlin/1856-maximum-subarray-min-product.kt @@ -0,0 +1,33 @@ +class Solution { + fun maxSumMinProduct(nums: IntArray): Int { + val prefix = LongArray(nums.size + 1) + val stack = ArrayDeque>() + val mod = 1000000007L + + for (i in nums.indices) { + prefix[i + 1] = prefix[i] + nums[i] + } + + var res = 0L + for ((i, n) in nums.withIndex()) { + var start = i + + while (stack.isNotEmpty() && stack.peekLast().second > n) { + val (lastStart, value) = stack.removeLast() + val total = prefix[i] - prefix[lastStart] + res = maxOf(res, total * value) + start = lastStart + } + + stack.addLast(start to n.toLong()) + } + + for ((start, value) in stack) { + val total = prefix[nums.size] - prefix[start] + res = maxOf(res, total * value) + } + + + return (res % mod).toInt() + } +} diff --git a/kotlin/1857-largest-color-value-in-a-directed-graph.kt b/kotlin/1857-largest-color-value-in-a-directed-graph.kt new file mode 100644 index 000000000..a2a33dcb5 --- /dev/null +++ b/kotlin/1857-largest-color-value-in-a-directed-graph.kt @@ -0,0 +1,45 @@ +class Solution { + fun largestPathValue(colors: String, edges: Array): Int { + val adj = HashMap>() + for ((from, to) in edges) + adj[from] = adj.getOrDefault(from, ArrayList()).apply{this.add(to)} + + val visited = HashSet() + val path = HashSet() + val count = Array(colors.length){IntArray(26)} + + fun dfs(node: Int): Int { + if (node in path) + return Integer.MAX_VALUE + if (node in visited) + return 0 + + visited.add(node) + path.add(node) + + val colorIndex = colors[node] - 'a' + count[node][colorIndex] = 1 + + adj[node]?.forEach{ nei -> + if (dfs(nei) == Integer.MAX_VALUE) + return Integer.MAX_VALUE + (0 until 26).forEach{ c -> + count[node][c] = maxOf( + count[node][c], + count[nei][c] + if(c == colorIndex) 1 else 0 + ) + } + } + + path.remove(node) + return count[node].max()!! + } + + var res = 0 + for (i in 0 until colors.length) { + res = maxOf(res, dfs(i)) + } + + return if(res == Integer.MAX_VALUE) -1 else res + } +} diff --git a/kotlin/1866-number-of-ways-to-rearrange-sticks-with-k-sticks-visible.kt b/kotlin/1866-number-of-ways-to-rearrange-sticks-with-k-sticks-visible.kt new file mode 100644 index 000000000..a7f9765e1 --- /dev/null +++ b/kotlin/1866-number-of-ways-to-rearrange-sticks-with-k-sticks-visible.kt @@ -0,0 +1,36 @@ +// Dp bottom up +class Solution { + fun rearrangeSticks(n: Int, k: Int): Int { + val mod = 1_000_000_000 + 7 + val dp = Array (n + 1) { LongArray (k + 1) } + + dp[1][1] = 1 + for (i in 2..n) { + for (j in 1..k) { + dp[i][j] = (dp[i - 1][j - 1] + ((i - 1) * dp[i - 1][j])) % mod + } + } + + return dp[n][k].toInt() + } +} + +// Recursion + memoization +class Solution { + fun rearrangeSticks(n: Int, k: Int): Int { + val mod = 1_000_000_000 + 7 + val dp = Array (n + 1) { LongArray (k + 1) { -1L } } + + fun dfs(n: Int, k: Int): Long { + if (n == k) return 1 + if (n == 0 || k == 0) return 0 + if (dp[n][k] != -1L) return dp[n][k] + + dp[n][k] = dfs(n - 1, k - 1) + ((n - 1) * dfs(n - 1, k)) + + return dp[n][k] % mod + } + + return dfs(n, k).toInt() + } +} diff --git a/kotlin/1871-jump-game-vii.kt b/kotlin/1871-jump-game-vii.kt new file mode 100644 index 000000000..771271543 --- /dev/null +++ b/kotlin/1871-jump-game-vii.kt @@ -0,0 +1,48 @@ +/* +* O(n) BFS +*/ +class Solution { + fun canReach(s: String, minJump: Int, maxJump: Int): Boolean { + val q = LinkedList().apply { add(0) } + var farthest = 0 + + with (q) { + while (isNotEmpty()) { + val i = removeFirst() + val start = maxOf(i + minJump, farthest + 1) + + for (j in start until minOf(i + maxJump + 1, s.length)) { + if (s[j] == '0') { + if (j == s.lastIndex) + return true + addLast(j) + } + } + + farthest = i + maxJump + } + } + + return false + } +} + +/* +* O(n) DP +*/ +class Solution { + fun canReach(s: String, minJump: Int, maxJump: Int): Boolean { + val dp = BooleanArray(s.length).apply { this[0] = true } + + var cntOfPos = 0 + for (i in 1 until s.length) { + if (i - minJump >= 0 && dp[i - minJump]) + cntOfPos++ + if (i - maxJump > 0 && dp[i - maxJump - 1]) + cntOfPos-- + dp[i] = cntOfPos > 0 && s[i] == '0' + } + + return dp[s.lastIndex] + } +} diff --git a/kotlin/1882-process-tasks-using-servers.kt b/kotlin/1882-process-tasks-using-servers.kt new file mode 100644 index 000000000..f67a3e7a1 --- /dev/null +++ b/kotlin/1882-process-tasks-using-servers.kt @@ -0,0 +1,31 @@ +class Solution { + fun assignTasks(servers: IntArray, tasks: IntArray): IntArray { + val res = IntArray (tasks.size) + + val available = PriorityQueue(compareBy ({ it[0] }, { it[1] })).apply { + for ((index, weight) in servers.withIndex()) + this.add(intArrayOf(weight, index)) + } + + val unAvailable = PriorityQueue(compareBy { it[0] }) + + var t = 0 + for (i in tasks.indices) { + t = maxOf(t, i) + + if (available.isEmpty()) + t = unAvailable.peek()[0] + + while (unAvailable.isNotEmpty() && t >= unAvailable.peek()[0]) { + val (timeFree, weight, index) = unAvailable.poll() + available.add(intArrayOf(weight, index)) + } + + val (weight, index) = available.poll() + res[i] = index + unAvailable.add(intArrayOf(t + tasks[i], weight, index)) + } + + return res + } +} diff --git a/kotlin/1888-minimum-number-of-flips-to-make-the-binary-string-alternating.kt b/kotlin/1888-minimum-number-of-flips-to-make-the-binary-string-alternating.kt new file mode 100644 index 000000000..7af8acbfd --- /dev/null +++ b/kotlin/1888-minimum-number-of-flips-to-make-the-binary-string-alternating.kt @@ -0,0 +1,78 @@ +/* +* O(N) memory +*/ + fun minFlips(s: String): Int { + var s2 = s + s + var sb1 = StringBuilder() + var sb2 = StringBuilder() + + for (i in 0 until s2.length) { + if (i % 2 == 0) { + sb1.append('1') + sb2.append('0') + } else { + sb1.append('0') + sb2.append('1') + } + } + + val alt1 = sb1.toString() + val alt2 = sb2.toString() + var res = s2.length + var diff1 = 0 + var diff2 = 0 + var left = 0 + + for (right in 0 until s2.length) { + if (s2[right] != alt1[right]) + diff1++ + if (s2[right] != alt2[right]) + diff2++ + if (right - left + 1 > s.length) { + if (s2[left] != alt1[left]) + diff1-- + if (s2[left] != alt2[left]) + diff2-- + left++ + } + + if (right - left + 1 == s.length) + res = minOf(res, diff1, diff2) + } + + return res + } +} + +/* +* O(1) memory +*/ +class Solution { + fun minFlips(s: String): Int { + val n = s.length + var diff1 = 0 + var diff2 = 0 + var res = n + + for (i in 0 until n * 2) { + val sChar = s[i % n] + val is0 = if (i % 2 == 0) '0' else '1' + if (sChar != is0) + diff1++ + else + diff2++ + + if (i >= n) { + val firstCharInWindow = if ((i - n) % 2 == 0) '0' else '1' + if (firstCharInWindow != s[i - n]) + diff1-- + else + diff2-- + + res = minOf(res, diff1, diff2) + } + } + + return res + } +} diff --git a/kotlin/1897-redistribute-characters-to-make-all-strings-equal.kt b/kotlin/1897-redistribute-characters-to-make-all-strings-equal.kt new file mode 100644 index 000000000..a31185f91 --- /dev/null +++ b/kotlin/1897-redistribute-characters-to-make-all-strings-equal.kt @@ -0,0 +1,27 @@ +class Solution { + fun makeEqual(words: Array): Boolean { + val n = words.size + val count = IntArray (26) + + for (word in words) { + for (c in word) + count[c - 'a']++ + } + + for (cnt in count) { + if (cnt % n != 0) + return false + } + + return true + } +} + +// or do it kotlin way! +class Solution { + fun makeEqual(words: Array) = words + .joinToString("") + .groupingBy { it } + .eachCount() + .all { it.value % words.size == 0 } +} diff --git a/kotlin/1898-maximum-number-of-removable-characters.kt b/kotlin/1898-maximum-number-of-removable-characters.kt new file mode 100644 index 000000000..40742bafa --- /dev/null +++ b/kotlin/1898-maximum-number-of-removable-characters.kt @@ -0,0 +1,40 @@ +class Solution { + fun maximumRemovals(s: String, p: String, removable: IntArray): Int { + + fun isSubseq(removed: HashSet): Boolean { + var i = 0 + var j = 0 + + while (i < s.length && j < p.length) { + if (i in removed || s[i] != p[j]) { + i++ + continue + } + i++ + j++ + } + + return j == p.length + } + + var res = 0 + var l = 0 + var r = removable.lastIndex + while (l <= r) { + val m = (l + r) / 2 + + val removed = HashSet() + for (i in 0..m) + removed.add(removable[i]) + + if (isSubseq(removed)) { + res = maxOf(res, m + 1) + l = m + 1 + } else { + r = m - 1 + } + } + + return res + } +} diff --git a/kotlin/1899-merge-triplets-to-form-target-triplet.kt b/kotlin/1899-merge-triplets-to-form-target-triplet.kt new file mode 100644 index 000000000..791c9e02e --- /dev/null +++ b/kotlin/1899-merge-triplets-to-form-target-triplet.kt @@ -0,0 +1,15 @@ +class Solution { + fun mergeTriplets(triplets: Array, target: IntArray): Boolean { + val filteredTriplets = triplets.filter { triplet -> + triplet[0] <= target[0] && triplet[1] <= target[1] && triplet[2] <= target[2] + } + if (filteredTriplets.isEmpty()) return false + val matches = booleanArrayOf(false, false, false) + filteredTriplets.forEach { (x, y, z) -> + matches[0] = matches[0] || x == target[0] + matches[1] = matches[1] || y == target[1] + matches[2] = matches[2] || z == target[2] + } + return matches.all { it == true } + } +} \ No newline at end of file diff --git a/kotlin/1903-largest-odd-number-in-string.kt b/kotlin/1903-largest-odd-number-in-string.kt new file mode 100644 index 000000000..af337934b --- /dev/null +++ b/kotlin/1903-largest-odd-number-in-string.kt @@ -0,0 +1,14 @@ +// DIY logic +class Solution { + fun largestOddNumber(num: String): String { + var end = num.lastIndex + while (end >= 0 && num[end].toInt() % 2 == 0) + end-- + return num.substring(0, end + 1) + } +} + +// or make use of Kotlin +class Solution { + fun largestOddNumber(num: String) = num.substring(0, num.indexOfLast { it.toString().toInt() % 2 == 1 } + 1) +} diff --git a/kotlin/1905-count-sub-islands.kt b/kotlin/1905-count-sub-islands.kt new file mode 100644 index 000000000..eb4035d06 --- /dev/null +++ b/kotlin/1905-count-sub-islands.kt @@ -0,0 +1,37 @@ +class Solution { + fun countSubIslands(grid1: Array, grid2: Array): Int { + + fun isValid(i: Int, j: Int) = i in (0 until grid2.size) && j in (0 until grid2[0].size) && grid2[i][j] == 1 + + val dir = arrayOf( + intArrayOf(1,0), + intArrayOf(-1,0), + intArrayOf(0,1), + intArrayOf(0,-1) + ) + + fun dfs(i: Int, j: Int): Boolean { + if(grid1[i][j] != 1) + return false + grid2[i][j] = 0 + var found = true + for((iD,jD) in dir){ + val iN = i + iD + val jN = j + jD + if(isValid(iN, jN)) + found = found and dfs(iN, jN) + } + return found + } + + var count = 0 + for(i in 0 until grid1.size){ + for(j in 0 until grid1[0].size){ + if(grid1[i][j] == 1 && grid2[i][j] == 1) + if(dfs(i, j) == true) + count++ + } + } + return count + } +} diff --git a/kotlin/1911-maximum-alternating-subsequence-sum.kt b/kotlin/1911-maximum-alternating-subsequence-sum.kt new file mode 100644 index 000000000..156268c77 --- /dev/null +++ b/kotlin/1911-maximum-alternating-subsequence-sum.kt @@ -0,0 +1,22 @@ +class Solution { + fun maxAlternatingSum(nums: IntArray): Long { + val dp = Array (2) { LongArray (nums.size) { -1L } } + + fun dfs(i: Int, even: Int): Long { + if (i == nums.size) + return 0 + if (dp[even][i] != -1L) + return dp[even][i] + + var sum = if (even == 1) nums[i] else -1 * nums[i] + dp[even][i] = maxOf( + sum + dfs(i + 1, if (even == 1) 0 else 1), + dfs(i + 1, even) + ) + + return dp[even][i] + } + + return dfs(0, 1) + } +} diff --git a/kotlin/1913-maximum-product-difference-between-two-pairs.kt b/kotlin/1913-maximum-product-difference-between-two-pairs.kt new file mode 100644 index 000000000..dc8262278 --- /dev/null +++ b/kotlin/1913-maximum-product-difference-between-two-pairs.kt @@ -0,0 +1,25 @@ +class Solution { + fun maxProductDifference(nums: IntArray): Int { + var max1 = Integer.MIN_VALUE + var max2 = Integer.MIN_VALUE + var min1 = Integer.MAX_VALUE + var min2 = Integer.MAX_VALUE + for (n in nums) { + if (n > max1) { + max2 = max1 + max1 = n + } else { + max2 = maxOf(max2, n) + } + + if (n < min1) { + min2 = min1 + min1 = n + } else { + min2 = minOf(min2, n) + } + } + + return max1 * max2 - min1 * min2 + } +} diff --git a/kotlin/1921-eliminate-maximum-number-of-monsters.kt b/kotlin/1921-eliminate-maximum-number-of-monsters.kt new file mode 100644 index 000000000..fa83d4b23 --- /dev/null +++ b/kotlin/1921-eliminate-maximum-number-of-monsters.kt @@ -0,0 +1,16 @@ +class Solution { + fun eliminateMaximum(dist: IntArray, speed: IntArray): Int { + val minReach = dist.zip(speed) + .map { + Math.ceil(it.first.toDouble() / it.second.toDouble()).toInt() + }.sorted() + + var res = 0 + for ((minute, reachesAt) in minReach.withIndex()) { + if (minute >= reachesAt) break + res++ + } + + return res + } +} diff --git a/kotlin/1929-concatenation-of-array.kt b/kotlin/1929-concatenation-of-array.kt new file mode 100644 index 000000000..c6f5dcf65 --- /dev/null +++ b/kotlin/1929-concatenation-of-array.kt @@ -0,0 +1,27 @@ +/* +* Can be generalized to repeat(x) easier +*/ +class Solution { + fun getConcatenation(nums: IntArray): IntArray { + val ans = LinkedList() + repeat(2) { + for(num in nums) + ans.addLast(num) + } + return ans.toIntArray() + } +} + +/* +* concrete solution +*/ +class Solution { + fun getConcatenation(nums: IntArray): IntArray { + val ans = IntArray(nums.size * 2) + for(i in 0..nums.lastIndex) { + ans[i] = nums[i] + ans[i + nums.size] = nums[i] + } + return ans + } +} diff --git a/kotlin/1930-unique-length-3-palindromic-subsequences.kt b/kotlin/1930-unique-length-3-palindromic-subsequences.kt new file mode 100644 index 000000000..8c149f8e4 --- /dev/null +++ b/kotlin/1930-unique-length-3-palindromic-subsequences.kt @@ -0,0 +1,56 @@ +/* +* Time complexity 0(N * 26) ~ O(N) +* +* Space complexity O(26 + 26 + 26*26) or 0(A^2) where A is the alphabet which technically is O(1) +*/ +class Solution { + fun countPalindromicSubsequence(s: String): Int { + + val left = HashSet() + val right = IntArray(26) + var res = HashSet>() // inner to outer pair, where they form the palindrome outer-inner-outer + + for(c in s) right[c - 'a']++ + + for(i in s.indices) { + if(right[s[i] - 'a'] > 0 ) right[s[i] - 'a']-- + for(j in 0 until 26) { + val c = 'a'.plus(j) + if(c in left && right[c - 'a'] > 0) { + res.add(s[i] to c) + } + } + left.add(s[i]) + } + + return res.size + } +} + +/* +* Time complexity 0(26*N+N*M) where M is the length of the substring between first and last character ~O(N*M) +* I see many posts claiming O(N) for the time complexity of this algorithm, but for Java/Kotlin, time complexity should be O(M*N) +* since according to https://stackoverflow.com/questions/4679746/time-complexity-of-javas-substring , substring() time complexity is Linear. +* +* Space complexity O(2*26) ~O(1) +*/ +class Solution { + fun countPalindromicSubsequence(s: String): Int { + + val first = IntArray(26) {Integer.MAX_VALUE} + val second = IntArray(26) + var res = 0 + + for(i in s.indices) { + first[s[i] - 'a'] = minOf(first[s[i] - 'a'], i) + second[s[i] - 'a'] = i + } + + for(i in 0 until 26) { + if(first[i] < second[i]) + res += s.substring(first[i]+1, second[i]).toCharArray().distinct().count() + } + + return res + } +} diff --git a/kotlin/1958-check-if-move-is-legal.kt b/kotlin/1958-check-if-move-is-legal.kt new file mode 100644 index 000000000..15cbe9204 --- /dev/null +++ b/kotlin/1958-check-if-move-is-legal.kt @@ -0,0 +1,95 @@ +// recursive solution +class Solution { + fun checkMove(board: Array, rMove: Int, cMove: Int, color: Char): Boolean { + board[rMove][cMove] = color + + val rowColumDirections = arrayOf( + intArrayOf(0, 1), + intArrayOf(0, -1), + intArrayOf(1, 0), + intArrayOf(-1, 0), + intArrayOf(1, 1), + intArrayOf(-1, -1), + intArrayOf(1, -1), + intArrayOf(-1, 1) + ) + + fun isValidCell(row: Int, column: Int) = row in board.indices && + column in board[row].indices && + board[row][column] != EMPTY_CELL && + board[row][column] != VISITED_CELL + + fun dfs( + startRow: Int, + startColumn: Int, + rowDir: Int, + columnDir: Int, + cellsInCurrentPath: Int + ): Boolean { + if (board[startRow][startColumn] == color) { + board[startRow][startColumn] = VISITED_CELL + return cellsInCurrentPath >= 2 // there must be 2 other nodes, not including the current node + } + board[startRow][startColumn] = VISITED_CELL + val newRow = startRow + rowDir + val newColumn = startColumn + columnDir + if (!isValidCell(newRow, newColumn)) return false + return dfs(newRow, newColumn, rowDir, columnDir, cellsInCurrentPath + 1) + } + + for ((rowDir, colDir) in rowColumDirections) { + val newRow = rMove + rowDir + val newColumn = cMove + colDir + if (!isValidCell(newRow, newColumn)) continue + if (dfs(newRow, newColumn, rowDir, colDir, 1)) return true + } + return false + } + + companion object { + private const val VISITED_CELL = '|' + private const val EMPTY_CELL = '.' + } +} + +// Iterative Solution +class Solution { + + fun checkMove(board: Array, rMove: Int, cMove: Int, color: Char): Boolean { + val rowColumDirections = arrayOf( + intArrayOf(0, 1), + intArrayOf(0, -1), + intArrayOf(1, 0), + intArrayOf(-1, 0), + intArrayOf(1, 1), + intArrayOf(-1, -1), + intArrayOf(1, -1), + intArrayOf(-1, 1) + ) + + fun isValidCell(row: Int, column: Int) = row in board.indices && + column in board[row].indices && + board[row][column] != EMPTY_CELL + + fun checkIfValidInDirection(rowDir: Int, colDir: Int): Boolean { + var currentRow = rMove + rowDir + var currentColumn = cMove + colDir + var numberOfCellsInPath = 1 + while (isValidCell(currentRow, currentColumn) && board[currentRow][currentColumn] != color) { + numberOfCellsInPath++ + currentRow += rowDir + currentColumn += colDir + } + return isValidCell(currentRow, currentColumn) && numberOfCellsInPath >= 2 + } + + for ((rowDir, colDir) in rowColumDirections) { + if (checkIfValidInDirection(rowDir, colDir)) return true + } + return false + } + + companion object { + private const val EMPTY_CELL = '.' + } +} \ No newline at end of file diff --git a/kotlin/1963-minimum-number-of-swaps-to-make-the-string-balanced.kt b/kotlin/1963-minimum-number-of-swaps-to-make-the-string-balanced.kt new file mode 100644 index 000000000..fc6ff0da4 --- /dev/null +++ b/kotlin/1963-minimum-number-of-swaps-to-make-the-string-balanced.kt @@ -0,0 +1,49 @@ +// Same as below, using pointer, but more compact solution, time O(N), space O(1) +class Solution { + fun minSwaps(s: String): Int { + var closed = 0 + for(c in s){ + if(c == '[') closed++ + else if (closed > 0) closed-- + } + return (closed + 1) / 2 + } +} + +// Solution using pointer (since we only have 2 chars to check), time O(N), space O(1) +class Solution { + fun minSwaps(s: String): Int { + var mismatches = 0 + var closed = 0 + for(c in s){ + if(c == ']') + if(closed>0) + closed-- + else + mismatches++ + else{ + closed++ + } + } + return (mismatches + 1) / 2 + } +} + +// Stack solution, time O(N), space O(N) +class Solution { + fun minSwaps(s: String): Int { + val stack = Stack() + var mismatches = 0 + for(c in s){ + if(c == ']') + if(!stack.isEmpty()) + stack.pop() + else + mismatches++ + else{ + stack.push(c) + } + } + return (mismatches + 1) / 2 + } +} diff --git a/kotlin/1964-find-the-longest-valid-obstacle-course-at-each-position.kt b/kotlin/1964-find-the-longest-valid-obstacle-course-at-each-position.kt new file mode 100644 index 000000000..6dac7ed8d --- /dev/null +++ b/kotlin/1964-find-the-longest-valid-obstacle-course-at-each-position.kt @@ -0,0 +1,27 @@ +class Solution { + fun longestObstacleCourseAtEachPosition(obstacles: IntArray): IntArray { + val dp = IntArray(obstacles.size) { Integer.MAX_VALUE } + val res = IntArray(obstacles.size) + + fun binarySearch(n: Int): Int { + var l = 0 + var r = dp.lastIndex + + while (l < r) { + val m = l + (r - l) / 2 + if (n >= dp[m]) l = m + 1 + else r = m + } + + return l + } + + for ((i, n) in obstacles.withIndex()) { + val insert = binarySearch(n) + res[i] = insert + 1 + dp[insert] = n + } + + return res + } +} diff --git a/kotlin/1968-array-with-elements-not-equal-to-average-of-neighbors.kt b/kotlin/1968-array-with-elements-not-equal-to-average-of-neighbors.kt new file mode 100644 index 000000000..e3e4916d6 --- /dev/null +++ b/kotlin/1968-array-with-elements-not-equal-to-average-of-neighbors.kt @@ -0,0 +1,37 @@ +/* +* O(nlogn) solution (similar to wiggle sort) +*/ +class Solution { + fun rearrangeArray(nums: IntArray): IntArray { + nums.sort() + + val res = IntArray(nums.size) + var i = 0 + var left = 0 + var right = nums.lastIndex + + while (i < res.size) { + res[i++] = nums[left++] + if (left <= right) + res[i++] = nums[right--] + } + + return res + } +} + +/* +* O(n) solution, check for any increasing/decreasing subarrays at i - i to i + 1, if found i with i + 1 to remove the increasing/decreasing subarray +*/ +class Solution { + fun rearrangeArray(nums: IntArray): IntArray { + + for (i in 1 until nums.lastIndex) { + if (nums[i - 1] < nums[i] && nums[i] < nums[i + 1] || + nums[i - 1] > nums[i] && nums[i] > nums[i + 1]) + nums[i] = nums[i + 1].also { nums[i + 1] = nums[i] } + } + + return nums + } +} diff --git a/kotlin/1980-find-unique-binary-string.kt b/kotlin/1980-find-unique-binary-string.kt new file mode 100644 index 000000000..74bf33990 --- /dev/null +++ b/kotlin/1980-find-unique-binary-string.kt @@ -0,0 +1,20 @@ +class Solution { + fun findDifferentBinaryString(nums: Array): String { + val hs = nums.toSet() + val n = nums.size + var temp = CharArray(nums.size) { '0' } + + + fun backtrack(i: Int): Boolean { + if(i == n) { + return if(!hs.contains(temp.joinToString(""))) true else false + } + if(backtrack(i+1)) return true + temp[i] = '1' + if(backtrack(i+1)) return true + return false + } + + return if(backtrack(0)) temp.joinToString("") else "" + } +} diff --git a/kotlin/1984-minimum-difference-between-highest-and-lowest-of-k-scores.kt b/kotlin/1984-minimum-difference-between-highest-and-lowest-of-k-scores.kt new file mode 100644 index 000000000..d4c7a21c1 --- /dev/null +++ b/kotlin/1984-minimum-difference-between-highest-and-lowest-of-k-scores.kt @@ -0,0 +1,10 @@ +class Solution { + fun minimumDifference(nums: IntArray, k: Int): Int { + nums.sort() + var min = Integer.MAX_VALUE + for(i in 0..nums.size-k) { + min = minOf(min, nums[i+k-1]-nums[i]) + } + return min + } +} diff --git a/kotlin/1985-find-the-kth-largest-integer-in-the-array.kt b/kotlin/1985-find-the-kth-largest-integer-in-the-array.kt new file mode 100644 index 000000000..1ef8265e3 --- /dev/null +++ b/kotlin/1985-find-the-kth-largest-integer-in-the-array.kt @@ -0,0 +1,61 @@ +/* +* Using a minHeap (And BigInteger, alternativly you can compare strings too) +*/ +class Solution { + fun kthLargestNumber(nums: Array, k: Int): String { + + val minHeap = PriorityQueue{ a, b -> + if(a < b) + 1 + else + -1 + } + + nums.forEach { + minHeap.add(it.toBigInteger()) + } + + var kth = k + while(kth > 1) { + minHeap.poll() + kth-- + } + + return minHeap.poll().toString() + } +} + +/* +* Using a maxHeap (And comparing strings, alternativly you can use BigInteger too) +*/ +class Solution { + fun kthLargestNumber(nums: Array, k: Int): String { + + val minHeap = PriorityQueue{ a, b -> + if(a.length > b.length) + 1 + else if(a.length < b.length) + -1 + else { + var toReturn = 0 + for(i in 0 until a.length) { + if(a[i] > b[i]) { + toReturn = 1 + break + } else if(a[i] < b[i]){ + toReturn = -1 + break + } + } + toReturn + } + } + + nums.forEach { + minHeap.add(it) + if(minHeap.size > k) minHeap.poll() + } + + return minHeap.peek() + } +} diff --git a/kotlin/1993-operations-on-tree.kt b/kotlin/1993-operations-on-tree.kt new file mode 100644 index 000000000..5903433b1 --- /dev/null +++ b/kotlin/1993-operations-on-tree.kt @@ -0,0 +1,53 @@ +class LockingTree(val parent: IntArray) { + val locked = IntArray (parent.size) { -1 } + val child = HashMap>() + + init { + for (i in 1 until parent.size) { + child[parent[i]] = child.getOrDefault(parent[i], mutableListOf()).apply { add(i) } + } + } + + fun lock(num: Int, user: Int): Boolean { + if (locked[num] != -1) + return false + locked[num] = user + return true + } + + fun unlock(num: Int, user: Int): Boolean { + if (locked[num] != user) + return false + locked[num] = -1 + return true + } + + fun upgrade(num: Int, user: Int): Boolean { + var i = num + while (i != -1) { + if (locked[i] != -1) + return false + i = parent[i] + } + + var lockedCount = 0 + var q = LinkedList() + q.add(num) + while (q.isNotEmpty()) { + var n = q.removeLast() + if (locked[n] != -1) { + locked[n] = -1 + lockedCount++ + } + child[n]?.forEach { + q.addFirst(it) + } + } + + if (lockedCount > 0) + locked[num] = user + + return lockedCount > 0 + } + +} diff --git a/kotlin/2001-number-of-pairs-of-interchangeable-rectangles.kt b/kotlin/2001-number-of-pairs-of-interchangeable-rectangles.kt new file mode 100644 index 000000000..d0a6ae242 --- /dev/null +++ b/kotlin/2001-number-of-pairs-of-interchangeable-rectangles.kt @@ -0,0 +1,19 @@ +class Solution { + fun interchangeableRectangles(rectangles: Array): Long { + val count = HashMap() + + for ((w, h) in rectangles) { + val d = w.toDouble() / h.toDouble() + count[d] = count.getOrDefault(d, 0L) + 1L + } + + var res = 0L + for (c in count.values) { + if (c > 1) { + res += (c * (c - 1)) / 2 + } + } + + return res + } +} diff --git a/kotlin/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.kt b/kotlin/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.kt new file mode 100644 index 000000000..17a357249 --- /dev/null +++ b/kotlin/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.kt @@ -0,0 +1,34 @@ +class Solution { + fun maxProduct(s: String): Int { + val hm = HashMap() + + for (mask in 1 until (1 shl s.length)) { + val sb = StringBuilder() + + for (i in 0 until s.length) { + if (mask and (1 shl i) > 0) { + sb.append(s[i]) + } + } + + val p = sb.toString() + if (p == p.reversed()) { + hm.put(mask, p.length) + } + } + + var max = 0 + for (mask1 in hm.keys) { + for (mask2 in hm.keys) { + if (mask1 and mask2 == 0) { + max = maxOf( + max, + hm[mask1]!! * hm[mask2]!! + ) + } + } + } + + return max + } +} diff --git a/kotlin/2009-minimum-number-of-operations-to-make-array-continuous.kt b/kotlin/2009-minimum-number-of-operations-to-make-array-continuous.kt new file mode 100644 index 000000000..e38feb939 --- /dev/null +++ b/kotlin/2009-minimum-number-of-operations-to-make-array-continuous.kt @@ -0,0 +1,16 @@ +class Solution { + fun minOperations(_nums: IntArray): Int { + val length = _nums.size + val nums = _nums.toSet().toIntArray().sorted() + var res = length + var right = 0 + + for (left in 0 until length) { + while (right < nums.size && nums[right] < nums[left] + length) + right++ + res = minOf(res, length - (right - left)) + } + + return res + } +} diff --git a/kotlin/2013-detect-squares.kt b/kotlin/2013-detect-squares.kt new file mode 100644 index 000000000..1c852233e --- /dev/null +++ b/kotlin/2013-detect-squares.kt @@ -0,0 +1,23 @@ +class DetectSquares() { + + val hm = HashMap, Int>() // [[x, y], count] + + fun add(point: IntArray) { + hm[point[0] to point[1]] = hm.getOrDefault(point[0] to point[1], 0) + 1 + } + + fun count(point: IntArray): Int { + var count = 0 + val (x,y) = point + for((k, v) in hm){ + repeat(v){ //incase of >1 occurences, we do calculations for each occurence + val (mX, mY) = k + if(Math.abs(mX - x) == Math.abs(mY - y) && x != mX && y != mY){ + count += hm.getOrDefault(x to mY, 0) * hm.getOrDefault(mX to y, 0) //0 if the possible point doesnt exist + } + } + } + return count + } + +} \ No newline at end of file diff --git a/kotlin/2017-grid-game.kt b/kotlin/2017-grid-game.kt new file mode 100644 index 000000000..2d37afb0c --- /dev/null +++ b/kotlin/2017-grid-game.kt @@ -0,0 +1,34 @@ +class Solution { + fun gridGame(grid: Array): Long { + val n = grid[0].size + val topPrefix = LongArray(n) + val botPrefix = LongArray(n) + + for (i in 0 until n) { + topPrefix[i] = grid[0][i].toLong() + botPrefix[i] = grid[1][i].toLong() + if (i > 0 ) { + topPrefix[i] += topPrefix[i - 1] + botPrefix[i] += botPrefix[i - 1] + } + } + + var res = Long.MAX_VALUE + for (i in 0 until n) { + val rob2top = topPrefix[n - 1] - topPrefix[i] + val rob2bot = if (i > 0) botPrefix[i - 1] else 0L + + val rob2take = maxOf( + rob2top, + rob2bot + ) + + res = minOf( + res, + rob2take + ) + } + + return res + } +} diff --git a/kotlin/2028-find-missing-observations.kt b/kotlin/2028-find-missing-observations.kt new file mode 100644 index 000000000..c5054a684 --- /dev/null +++ b/kotlin/2028-find-missing-observations.kt @@ -0,0 +1,20 @@ +class Solution { + fun missingRolls(rolls: IntArray, mean: Int, _n: Int): IntArray { + val m = rolls.size + var n = _n + var nTotal = (mean * (n + m)) - (rolls.sum() ?: 0) + + if (nTotal < n || nTotal > n * 6) + return IntArray (0) + + var res = LinkedList() + while (nTotal > 0) { + val dice = minOf(nTotal - n + 1, 6) + res.add(dice) + nTotal -= dice + n-- + } + + return res.toIntArray() + } +} diff --git a/kotlin/2038-remove-colored-pieces-if-both-neighbors-are-the-same-color.kt b/kotlin/2038-remove-colored-pieces-if-both-neighbors-are-the-same-color.kt new file mode 100644 index 000000000..31796e6fc --- /dev/null +++ b/kotlin/2038-remove-colored-pieces-if-both-neighbors-are-the-same-color.kt @@ -0,0 +1,43 @@ +// count the number of "AAA" and "BBB" +class Solution { + fun winnerOfGame(s: String): Boolean { + var alice = 0 + var bob = 0 + + for (i in 1 until s.lastIndex) { + if (s[i - 1] == s[i] && s[i] == s[i + 1]) { + if (s[i] == 'A') + alice++ + if (s[i] == 'B') + bob++ + } + } + + return alice > bob + } +} + +// or alternativly count the picks directly +class Solution { + fun winnerOfGame(s: String): Boolean { + var colorA = 0 + var i = 0 + while (i < s.length) { + var j = i + while (j < s.length && s[j] == 'A') j++ + if (j - i > 2) colorA += (j - i - 2) + i = ++j + } + + var colorB = 0 + i = 0 + while (i < s.length) { + var j = i + while (j < s.length && s[j] == 'B') j++ + if (j - i > 2) colorB += (j - i - 2) + i = ++j + } + + return if (colorA - colorB > 0) true else false + } +} diff --git a/kotlin/2050-parallel-courses-iii.kt b/kotlin/2050-parallel-courses-iii.kt new file mode 100644 index 000000000..3a2d0dd82 --- /dev/null +++ b/kotlin/2050-parallel-courses-iii.kt @@ -0,0 +1,30 @@ +class Solution { + fun minimumTime(n: Int, relations: Array, time: IntArray): Int { + val adj = HashMap>().apply { + for ((src, dst) in relations) { + this[src] = getOrDefault(src, mutableListOf()).apply { + add(dst) + } + } + } + + + val maxTime = IntArray (n + 1) { -1 } + fun dfs(src: Int): Int { + if (maxTime[src] != -1) return maxTime[src] + + var res = time[src - 1] + adj[src]?.forEach { nei -> + res = maxOf(res, time[src - 1] + dfs(nei)) + } + + maxTime[src] = res + return res + } + + for (i in 1..n) + dfs(i) + + return maxTime.max()!! + } +} diff --git a/kotlin/2092-find-all-people-with-secret.kt b/kotlin/2092-find-all-people-with-secret.kt new file mode 100644 index 000000000..d9face4d9 --- /dev/null +++ b/kotlin/2092-find-all-people-with-secret.kt @@ -0,0 +1,37 @@ +class Solution { + fun findAllPeople(n: Int, meetings: Array, firstPerson: Int): List { + val secrets = mutableSetOf(0, firstPerson) + val timeMap = mutableMapOf>>() + + for ((src, dst, t) in meetings) { + if (t !in timeMap) + timeMap[t] = mutableMapOf() + timeMap[t]!!.getOrPut(src) { mutableListOf() }.apply { add(dst) } + timeMap[t]!!.getOrPut(dst) { mutableListOf() }.apply { add(src) } + } + + fun dfs( + src: Int, + adj: MutableMap>, + visit: MutableSet + ) { + if (src in visit) return + visit.add(src) + secrets.add(src) + adj[src]?.forEach { nei -> + dfs(nei, adj, visit) + } + } + + timeMap.keys.sorted().forEach { t -> + val visit = mutableSetOf () + timeMap[t]?.keys?.forEach { src -> + if (src in secrets) { + dfs(src, timeMap[t]!!, visit) + } + } + } + + return secrets.toList() + } +} diff --git a/kotlin/2101-detonate-the-maximum-bombs.kt b/kotlin/2101-detonate-the-maximum-bombs.kt new file mode 100644 index 000000000..5ed3904e7 --- /dev/null +++ b/kotlin/2101-detonate-the-maximum-bombs.kt @@ -0,0 +1,37 @@ +class Solution { + fun maximumDetonation(b: Array): Int { + val adj = HashMap>().apply { + for (i in b.indices) { + for (j in b.indices) { + if (i != j) { + val dX = b[i][0].toDouble() - b[j][0].toDouble() + val dY = b[i][1].toDouble() - b[j][1].toDouble() + if ((b[i][2].toDouble() * b[i][2].toDouble()) >= (dX * dX + dY * dY)) + this[i] = getOrDefault(i, ArrayList()).apply { add(j) } + } + } + } + } + + fun dfs(i: Int, visit: HashSet) { + if (i in visit) + return + visit.add(i) + adj[i]?.forEach { + dfs(it, visit) + } + } + + var res = 0 + for (i in b.indices) { + val visit = HashSet() + dfs(i, visit) + res = maxOf( + res, + visit.size + ) + } + + return res + } +} diff --git a/kotlin/2108-find-first-palindromic-string-in-the-array.kt b/kotlin/2108-find-first-palindromic-string-in-the-array.kt new file mode 100644 index 000000000..8228e4a90 --- /dev/null +++ b/kotlin/2108-find-first-palindromic-string-in-the-array.kt @@ -0,0 +1,52 @@ +// For every word, check that its reverse equals it +class Solution { + fun firstPalindrome(words: Array): String { + words.forEach { word -> + if (word == word.reversed()) + return word + } + + return "" + } +} + +// For every word, check that its reverse equals it but using two pointers +class Solution { + fun firstPalindrome(words: Array): String { + words.forEach { word -> + var l = 0 + var r = word.lastIndex + while (word[l] == word[r]) { + if (l >= r) return word + l++ + r-- + } + } + + return "" + } +} + +// For every word, check that its reverse equals it but using two pointers with slightly different logic +class Solution { + fun firstPalindrome(words: Array): String { + words.forEach { w -> + var l = 0 + var r = w.lastIndex + while (l < r && w[l] == w[r]) { + l++ + r-- + } + + if (w.length % 2 == 1 && l == r || l == r + 1) return w + } + + return "" + } +} + +// Use Kotlins power to condense the solution +class Solution { + fun firstPalindrome(words: Array) = words + .firstOrNull { it == it.reversed() } ?: "" +} diff --git a/kotlin/2125-number-of-laser-beams-in-a-bank.kt b/kotlin/2125-number-of-laser-beams-in-a-bank.kt new file mode 100644 index 000000000..8152ca2f9 --- /dev/null +++ b/kotlin/2125-number-of-laser-beams-in-a-bank.kt @@ -0,0 +1,25 @@ +class Solution { + fun numberOfBeams(bank: Array): Int { + var prev = bank[0].count { it == '1'} + var res = 0 + + for (i in 1 until bank.size) { + val cur = bank[i].count { it == '1'} + if (cur > 0) { + res += (prev * cur) + prev = cur + } + } + + return res + } +} + +// or do it the kotlin way! +class Solution { + fun numberOfBeams(bank: Array) = bank + .map { it.count { it == '1' } } + .filterNot { it == 0 } + .zipWithNext { a, b -> a * b } + .sum() +} diff --git a/kotlin/2130-maximum-twin-sum-of-a-linked-list.kt b/kotlin/2130-maximum-twin-sum-of-a-linked-list.kt new file mode 100644 index 000000000..7cfe6fbd3 --- /dev/null +++ b/kotlin/2130-maximum-twin-sum-of-a-linked-list.kt @@ -0,0 +1,35 @@ +/** + * Example: + * var li = ListNode(5) + * var v = li.`val` + * Definition for singly-linked list. + * class ListNode(var `val`: Int) { + * var next: ListNode? = null + * } + */ +class Solution { + fun pairSum(head: ListNode?): Int { + var slow = head + var fast = head + var prev: ListNode? = null + + while(fast != null && fast.next != null) { + fast = fast.next.next + val temp = slow?.next + slow?.next = prev + prev = slow + slow = temp + } + + var res = 0 + while(slow != null && prev != null) { // only need to check if either one of these != null, I check both for null safety warnings + res = maxOf(res, slow.value!! + prev.value!!) + prev = prev.next + slow = slow.next + } + + return res + } + + val ListNode.value get()= this.`val` +} diff --git a/kotlin/2140-solving-questions-with-brainpower.kt b/kotlin/2140-solving-questions-with-brainpower.kt new file mode 100644 index 000000000..e5a333aeb --- /dev/null +++ b/kotlin/2140-solving-questions-with-brainpower.kt @@ -0,0 +1,62 @@ +/* +* DFS/Recursion + Memoization +*/ +class Solution { + fun mostPoints(questions: Array): Long { + val memo = LongArray(questions.size) { -1L } + + fun dfs(i: Int): Long { + if (i >= questions.size) + return 0 + if (memo[i] != -1L) + return memo[i] + + memo[i] = maxOf( + questions[i][0] + dfs(i + 1 + questions[i][1]), + dfs(i + 1) + ) + + return memo[i] + } + + return dfs(0) + } +} + +/* +* DP +*/ +class Solution { + fun mostPoints(q: Array): Long { + val n = q.lastIndex + val dp = LongArray(q.size) + + for (i in n downTo 0) { + dp[i] = maxOf( + if (i < n) dp[i + 1] else 0, + q[i][0] + if (i + 1 + q[i][1] <= n) dp[i + 1 + q[i][1]] else 0 + ) + } + + return dp[0] + } +} + +/* +* DP with HashMap instead of Array for simpler code +*/ +class Solution { + fun mostPoints(q: Array): Long { + val n = q.lastIndex + val dp = HashMap() + + for (i in n downTo 0) { + dp[i] = maxOf( + dp[i + 1] ?: 0L, + q[i][0] + (dp[i + 1 + q[i][1]] ?: 0L) + ) + } + + return dp[0] ?: 0L + } +} diff --git a/kotlin/2147-number-of-ways-to-divide-a-long-corridor.kt b/kotlin/2147-number-of-ways-to-divide-a-long-corridor.kt new file mode 100644 index 000000000..69c3e2317 --- /dev/null +++ b/kotlin/2147-number-of-ways-to-divide-a-long-corridor.kt @@ -0,0 +1,83 @@ +// Use combinatorics, time O(n) and space O(n) +class Solution { + fun numberOfWays(corridor: String): Int { + val mod = 1_000_000_007 + val seats = mutableListOf() + + for ((i, c) in corridor.withIndex()) { + if (c == 'S') + seats.add(i) + } + + if (seats.size < 2 || seats.size % 2 == 1) + return 0 + + var res = 1L + var i = 1 + while (i < seats.size - 1) { + res = (res * (seats[i + 1] - seats[i])) % mod + i += 2 + } + + return res.toInt() + } +} + +// Use combinatorics, time O(n) and space O(1) +class Solution { + fun numberOfWays(corridor: String): Int { + val seats = corridor.count { it == 'S' } + if (seats < 2 || seats % 2 == 1) return 0 + + val mod = 1_000_000_007 + var prev = corridor.indexOfFirst { it == 'S' } + var seatCount = 1 + var res = 1L + + for (i in prev + 1 until corridor.length) { + if (corridor[i] == 'S') { + if (seatCount == 2) { + res = (res * (i - prev)) % mod + seatCount = 1 + } else { + seatCount++ + } + prev = i + } + } + + return res.toInt() + } +} + +// Recursion + memoization, time O(n) and space O(n) +class Solution { + fun numberOfWays(corridor: String): Int { + val mod = 1_000_000_007 + val dp = Array (corridor.length) { IntArray (3) { -1 } } + + fun dfs(i: Int, seats: Int): Int { + if (i == corridor.length) + return if (seats == 2) 1 else 0 + if (dp[i][seats] != -1) return dp[i][seats] + + var res = 0 + if (seats == 2) { + if (corridor[i] == 'S') + res = dfs(i + 1, 1) + else + res = (dfs(i + 1, 0) + dfs(i + 1, 2)) % mod + } else { + if (corridor[i] == 'S') + res = dfs(i + 1, seats + 1) + else + res = dfs(i + 1, seats) + } + + dp[i][seats] = res + return res + } + + return dfs(0, 0) + } +} diff --git a/kotlin/2149-rearrange-array-elements-by-sign.kt b/kotlin/2149-rearrange-array-elements-by-sign.kt new file mode 100644 index 000000000..266740cc0 --- /dev/null +++ b/kotlin/2149-rearrange-array-elements-by-sign.kt @@ -0,0 +1,20 @@ +class Solution { + fun rearrangeArray(nums: IntArray): IntArray { + var pos = 0 + var neg = 1 + val res = IntArray (nums.size) + + for (num in nums) { + if (num > 0) { + res[pos] = num + pos += 2 + } else { + res[neg] = num + neg += 2 + } + } + + return res + } + +} diff --git a/kotlin/2215-find-the-difference-of-two-arrays.kt b/kotlin/2215-find-the-difference-of-two-arrays.kt new file mode 100644 index 000000000..e8c60c857 --- /dev/null +++ b/kotlin/2215-find-the-difference-of-two-arrays.kt @@ -0,0 +1,36 @@ +/* +* Short answer +*/ +class Solution { + fun findDifference(nums1: IntArray, nums2: IntArray) = listOf( + (nums1.toSet() subtract nums2.toSet()).toList(), + (nums2.toSet() subtract nums1.toSet()).toList() + ) +} + +/* +* Or if you are interested in the "logic" +*/ +class Solution { + fun findDifference(nums1: IntArray, nums2: IntArray): List> { + val nums1Set = nums1.toSet() + val nums2Set = nums2.toSet() + val res1 = HashSet() + val res2 = HashSet() + + for (n in nums1) { + if (n !in nums2Set) + res1.add(n) + } + + for (n in nums2) { + if (n !in nums1Set) + res2.add(n) + } + + return listOf( + res1.toList(), + res2.toList() + ) + } +} diff --git a/kotlin/2218-maximum-value-of-k-coins-from-piles.kt b/kotlin/2218-maximum-value-of-k-coins-from-piles.kt new file mode 100644 index 000000000..cd60251ae --- /dev/null +++ b/kotlin/2218-maximum-value-of-k-coins-from-piles.kt @@ -0,0 +1,28 @@ +class Solution { + fun maxValueOfCoins(piles: List>, k: Int): Int { + val n = piles.size + val dp = Array(n){ IntArray(k+1){-1} } + + fun dfs(i: Int, coins: Int): Int { + if (i == n) + return 0 + if (dp[i][coins] != -1) + return dp[i][coins] + + dp[i][coins] = dfs(i + 1, coins) + + var curPile = 0 + for (j in 0 until minOf(piles[i].size, coins)) { + curPile += piles[i][j] + dp[i][coins] = maxOf( + dp[i][coins], + curPile + dfs(i + 1, coins - j - 1) + ) + } + + return dp[i][coins] + } + + return dfs(0, k) + } +} diff --git a/kotlin/2251-number-of-flowers-in-full-bloom.kt b/kotlin/2251-number-of-flowers-in-full-bloom.kt new file mode 100644 index 000000000..09d6d1208 --- /dev/null +++ b/kotlin/2251-number-of-flowers-in-full-bloom.kt @@ -0,0 +1,56 @@ +// two heaps +class Solution { + fun fullBloomFlowers(flowers: Array, _people: IntArray): IntArray { + val people = _people.mapIndexed { i, p -> p to i }.sortedBy { it.first } + val res = IntArray (_people.size) + var count = 0 + val start = PriorityQueue() + val end = PriorityQueue() + + for ((s, e) in flowers) { + start.add(s) + end.add(e) + } + + for ((p, i) in people) { + while (start.isNotEmpty() && start.peek() <= p) { + start.poll() + count++ + } + + while (end.isNotEmpty() && end.peek() < p) { + end.poll() + count-- + } + + res[i] = count + } + + return res + } +} + +// one heap +class Solution { + fun fullBloomFlowers(flowers: Array, _people: IntArray): IntArray { + val people = _people.mapIndexed { i, p -> p to i }.sortedBy { it.first } + val res = IntArray (_people.size) + flowers.sortBy { it[0] } + val end = PriorityQueue() + + var j = 0 + for ((p, i) in people) { + while (j < flowers.size && flowers[j][0] <= p) { + end.add(flowers[j][1]) + j++ + } + + while (end.isNotEmpty() && end.peek() < p) + end.poll() + + res[i] = end.size + } + + return res + } +} diff --git a/kotlin/2264-largest-3-same-digit-number-in-string.kt b/kotlin/2264-largest-3-same-digit-number-in-string.kt new file mode 100644 index 000000000..d316eb293 --- /dev/null +++ b/kotlin/2264-largest-3-same-digit-number-in-string.kt @@ -0,0 +1,20 @@ +class Solution { + fun largestGoodInteger(num: String): String { + var res = "" + for (i in 2..num.lastIndex) { + val cur = num.substring(i - 2, i + 1) + if (cur[0] == cur[1] && cur[0] == cur[2]) + res = maxOf(res, cur) + } + return res + } +} + +// ...or use power of Kotlin +class Solution { + fun largestGoodInteger(num: String) = num + .windowed(3) + .filter { it.toSet().size == 1 } + .maxOrNull() ?: "" + +} diff --git a/kotlin/2300-successful-pairs-of-spells-and-potions.kt b/kotlin/2300-successful-pairs-of-spells-and-potions.kt new file mode 100644 index 000000000..ba73901f0 --- /dev/null +++ b/kotlin/2300-successful-pairs-of-spells-and-potions.kt @@ -0,0 +1,26 @@ +class Solution { + fun successfulPairs(spells: IntArray, potions: IntArray, success: Long): IntArray { + potions.sort() + val res = IntArray(spells.size) + + for ((i, s) in spells.withIndex()) { + var left = 0 + var right = potions.lastIndex + var index = potions.size + + while (left <= right) { + val mid = left + (right - left) / 2 + if ( s.toLong() * potions[mid].toLong() >= success) { + right = mid - 1 + index = mid + } else { + left = mid + 1 + } + } + + res[i] = potions.size - index + } + + return res + } +} diff --git a/kotlin/2306-naming-a-company.kt b/kotlin/2306-naming-a-company.kt new file mode 100644 index 000000000..9393ee1e9 --- /dev/null +++ b/kotlin/2306-naming-a-company.kt @@ -0,0 +1,50 @@ +class Solution { + fun distinctNames(ideas: Array): Long { + + val firstToSuffix = Array(26) { hashSetOf() } + var res = 0L + + ideas.forEach { + firstToSuffix[it[0] - 'a'].add(it.substring(1, it.length)) + } + + for (i in 0 until 26) { + for (j in i until 26) { + val common = firstToSuffix[i].intersect(firstToSuffix[j]).size + val mapI = firstToSuffix[i].size - common + val mapJ = firstToSuffix[j].size - common + res += (mapI * mapJ) * 2 + } + } + + return res + } +} + +// without using kotlins intersect() funtions +class Solution { + fun distinctNames(ideas: Array): Long { + + val firstToSuffix = Array(26) { hashSetOf() } + var res = 0L + + ideas.forEach { + firstToSuffix[it[0] - 'a'].add(it.substring(1, it.length)) + } + + for (i in 0 until 26) { + for (j in i until 26) { + + var common = 0 + for(wordA in firstToSuffix[i]) { + if(wordA in firstToSuffix[j]) common++ + } + val mapI = firstToSuffix[i].size - common + val mapJ = firstToSuffix[j].size - common + res += (mapI * mapJ) * 2 + } + } + + return res + } +} diff --git a/kotlin/2348-number-of-zero-filled-subarrays.kt b/kotlin/2348-number-of-zero-filled-subarrays.kt new file mode 100644 index 000000000..528c82922 --- /dev/null +++ b/kotlin/2348-number-of-zero-filled-subarrays.kt @@ -0,0 +1,24 @@ +class Solution { + fun zeroFilledSubarray(nums: IntArray): Long { + var res = 0L + + var i = 0 + while(i < nums.size) { + if(nums[i] == 0) { + var j = i + var count = 0L + while(j < nums.size && nums[j] == 0) { + j++ + count++ + } + println(count) + res += count * (1 + count) / 2 + i = j + }else{ + i++ + } + } + + return res + } +} diff --git a/kotlin/2353-design-a-food-rating-system.kt b/kotlin/2353-design-a-food-rating-system.kt new file mode 100644 index 000000000..d3bc64f32 --- /dev/null +++ b/kotlin/2353-design-a-food-rating-system.kt @@ -0,0 +1,35 @@ +class FoodRatings(foods: Array, cuisines: Array, ratings: IntArray) { + + val ratingsList = HashMap>>() + val cuisinesList = HashMap() + val foodRatingsList = HashMap() + + init { + for (i in foods.indices) { + ratingsList.getOrPut(cuisines[i]) { + sortedSetOf(compareBy({ -it.first }, { it.second })) + }.apply { + add(ratings[i] to foods[i]) + } + cuisinesList[foods[i]] = cuisines[i] + foodRatingsList[foods[i]] = ratings[i] + } + } + + fun changeRating(food: String, newRating: Int) { + val cuisine = cuisinesList[food] ?: "" + val rating = foodRatingsList[food] ?: 1 + + ratingsList[cuisine]?.let { + it.remove(rating to food) + it.add(newRating to food) + } + + foodRatingsList[food] = newRating + } + + fun highestRated(cuisine: String): String { + return ratingsList[cuisine]?.first()?.second ?: "" + } + +} diff --git a/kotlin/2359-find-closest-node-to-given-two-nodes.kt b/kotlin/2359-find-closest-node-to-given-two-nodes.kt new file mode 100644 index 000000000..b4498124b --- /dev/null +++ b/kotlin/2359-find-closest-node-to-given-two-nodes.kt @@ -0,0 +1,119 @@ +// bfs +class Solution { + fun closestMeetingNode(edges: IntArray, node1: Int, node2: Int): Int { + val adj = HashMap>().apply { + for ((u, v) in edges.withIndex()) { + this[u] = getOrDefault(u, mutableListOf()).apply { add(v) } + } + } + + fun bfs(node: Int, distMap: HashMap) { + with (LinkedList>()) { + addLast(node to 0) + distMap[node] = 0 + while (isNotEmpty()) { + val (node, dist) = removeFirst() + adj[node]?.forEach { nei -> + if (nei !in distMap) { + addLast(nei to dist + 1) + distMap[nei] = dist + 1 + } + } + } + } + } + + val node1Dist = HashMap() + val node2Dist = HashMap() + bfs(node1, node1Dist) + bfs(node2, node2Dist) + + var res = -1 + var resDist = Integer.MAX_VALUE + for (i in edges.indices) { + if (i in node1Dist && i in node2Dist) { + val dist = maxOf(node1Dist[i]!!, node2Dist[i]!!) + if (dist < resDist) { + res = i + resDist = dist + } + } + } + + return res + } +} + +// bfs, but omitting the adjecency graph since it's not needed. We know that every node has at most 1 outgoing edge, +// so we can use the edge list to find the next node. +class Solution { + fun closestMeetingNode(edges: IntArray, node1: Int, node2: Int): Int { + + fun bfs(node: Int, distMap: HashMap) { + with (LinkedList>()) { + addLast(node to 0) + distMap[node] = 0 + while (isNotEmpty()) { + val (node, dist) = removeFirst() + val nei = edges[node] + if (nei != -1 && nei !in distMap) { + addLast(nei to dist + 1) + distMap[nei] = dist + 1 + } + } + } + } + + val node1Dist = HashMap() + val node2Dist = HashMap() + bfs(node1, node1Dist) + bfs(node2, node2Dist) + + var res = -1 + var resDist = Integer.MAX_VALUE + for (i in edges.indices) { + if (i in node1Dist && i in node2Dist) { + val dist = maxOf(node1Dist[i]!!, node2Dist[i]!!) + if (dist < resDist) { + res = i + resDist = dist + } + } + } + + return res + } +} + +// dfs +class Solution { + fun closestMeetingNode(edges: IntArray, node1: Int, node2: Int): Int { + + fun dfs(node: Int, distMap: HashMap, dist: Int) { + val nei = edges[node] + if (nei != -1 && nei !in distMap) { + distMap[nei] = dist + 1 + dfs(nei, distMap, dist + 1) + } + } + + val node1Dist = HashMap().apply { this[node1] = 0 } + val node2Dist = HashMap().apply { this[node2] = 0 } + dfs(node1, node1Dist, 0) + dfs(node2, node2Dist, 0) + + var res = -1 + var resDist = Integer.MAX_VALUE + for (i in edges.indices) { + if (i in node1Dist && i in node2Dist) { + val dist = maxOf(node1Dist[i]!!, node2Dist[i]!!) + if (dist < resDist) { + res = i + resDist = dist + } + } + } + + return res + } +} diff --git a/kotlin/2369-check-if-there-is-a-valid-partition-for-the-array.kt b/kotlin/2369-check-if-there-is-a-valid-partition-for-the-array.kt new file mode 100644 index 000000000..bcaa56c4c --- /dev/null +++ b/kotlin/2369-check-if-there-is-a-valid-partition-for-the-array.kt @@ -0,0 +1,60 @@ +// rolling dp O(1) space +class Solution { + fun validPartition(nums: IntArray): Boolean { + val n = nums.size + val dp = booleanArrayOf(false, false, false, true) + + for (i in 0 until n) { + dp[i % 4] = false + if (i > 0 && nums[i - 1] == nums[i]) + dp[i % 4] = dp[i % 4] or dp[(i + 2) % 4] + if (i > 1 && nums[i - 1] == nums[i] && nums[i - 2] == nums[i]) + dp[i % 4] = dp[i % 4] or dp[(i + 1) % 4] + if (i > 1 && nums[i - 1] + 1 == nums[i] && nums[i - 2] + 2 == nums[i]) + dp[i % 4] = dp[i % 4] or dp[(i + 1) % 4] + } + + return dp[(n - 1) % 4] + } +} + +//dp O(n) space +class Solution { + fun validPartition(nums: IntArray): Boolean { + val n = nums.size + val dp = BooleanArray (n + 1).apply { this[0] = true } + + for (i in 2..n) { + dp[i] = dp[i] or (nums[i - 1] == nums[i - 2] && dp[i - 2]) + dp[i] = dp[i] or (i > 2 && nums[i - 1] == nums[i - 2] && nums[i - 1] == nums[i - 3] && dp[i - 3]) + dp[i] = dp[i] or (i > 2 && nums[i - 1] - 1 == nums[i - 2] && nums[i - 1] - 2 == nums[i - 3] && dp[i - 3]) + } + + return dp[n] + } +} + +//recursion +class Solution { + fun validPartition(nums: IntArray): Boolean { + val n = nums.size + val dp = IntArray (n) { -1 } + + fun dfs(i: Int): Int { + if (i == n) return 1 + if (dp[i] != -1) return dp[i] + + if (i + 1 < n && nums[i] == nums[i + 1]) + if (dfs(i + 2) == 1) return 1 + if (i + 2 < n && nums[i] == nums[i + 1] && nums[i] == nums[i + 2]) + if (dfs(i + 3) == 1) return 1 + if (i + 2 < n && nums[i] + 1 == nums[i + 1] && nums[i] + 2 == nums[i + 2]) + if (dfs(i + 3) == 1) return 1 + + dp[i] = 0 + return dp[i] + } + + return dfs(0) == 1 + } +} diff --git a/kotlin/2390-removing-stars-from-a-string.kt b/kotlin/2390-removing-stars-from-a-string.kt new file mode 100644 index 000000000..30f7bbfec --- /dev/null +++ b/kotlin/2390-removing-stars-from-a-string.kt @@ -0,0 +1,15 @@ +class Solution { + fun removeStars(s: String): String { + val stack = LinkedList() + + for (c in s) { + if (c == '*' && stack.isNotEmpty()) { + stack.removeLast() + } else { + stack.addLast(c) + } + } + + return stack.joinToString("") + } +} diff --git a/kotlin/2402-meeting-rooms-iii.kt b/kotlin/2402-meeting-rooms-iii.kt new file mode 100644 index 000000000..eae0d93a6 --- /dev/null +++ b/kotlin/2402-meeting-rooms-iii.kt @@ -0,0 +1,37 @@ +class Solution { + fun mostBooked(n: Int, meetings: Array): Int { + meetings.sortBy { it[0] } + + val available = PriorityQueue ().apply { + for (i in 0 until n) + add(i) + } + + val used = PriorityQueue> { a, b -> + if (a.first == b.first) a.second - b.second + else a.first - b.first + } + + val count = IntArray (n) + + for (meet in meetings) { + var (start, end) = meet + while (used.isNotEmpty() && start >= used.peek().first) { + val (_, room) = used.poll() + available.add(room) + } + + if (available.isEmpty()) { + val (endTime, room) = used.poll() + end = endTime + (end - start) + available.add(room) + } + + val room = available.poll() + used.add(end to room) + count[room]++ + } + + return count.indexOfFirst({ it == (count.max() ?: 0) }) + } +} diff --git a/kotlin/2405-optimal-partition-of-string.kt b/kotlin/2405-optimal-partition-of-string.kt new file mode 100644 index 000000000..25f401bc4 --- /dev/null +++ b/kotlin/2405-optimal-partition-of-string.kt @@ -0,0 +1,16 @@ +class Solution { + fun partitionString(s: String): Int { + val hs = HashSet() + var res = 0 + + for (c in s) { + if (hs.contains(c)) { + res++ + hs.clear() + } + hs.add(c) + } + + return if(hs.size != 0) res + 1 else res + } +} diff --git a/kotlin/2421-number-of-good-paths.kt b/kotlin/2421-number-of-good-paths.kt new file mode 100644 index 000000000..5230fe939 --- /dev/null +++ b/kotlin/2421-number-of-good-paths.kt @@ -0,0 +1,62 @@ +class Solution { + + class UnionFind(val n: Int) { + val parent = IntArray (n) { it } + val size = IntArray (n) { 1 } + + fun find(i: Int): Int { + if (i != parent[i]) + parent[i] = find(parent[i]) + return parent[i] + } + + fun union(a: Int, b: Int): Boolean { + val pa = find(a) + val pb = find(b) + if (pa == pb) return false + if (size[pa] > size[pb]) { + parent[pb] = pa + size[pa] += size[pb] + } else { + parent[pa] = pb + size[pb] += size[pa] + } + return true + } + } + + fun numberOfGoodPaths(vals: IntArray, edges: Array): Int { + val adj = HashMap>().apply { + for ((a, b) in edges) { + this[a] = getOrDefault(a, mutableListOf()).apply { add(b) } + this[b] = getOrDefault(b, mutableListOf()).apply { add(a) } + } + } + + val valueToIndex = TreeMap>().apply { + for ((i, v) in vals.withIndex()) { + this[v] = getOrDefault(v, mutableListOf()).apply { add(i) } + } + } + + var res = 0 + val uf = UnionFind(vals.size) + valueToIndex.keys.forEach { value -> + valueToIndex[value]?.forEach { i -> + adj[i]?.forEach { nei -> + if (vals[nei] <= vals[i]) + uf.union(nei, i) + } + } + + val count = HashMap() + valueToIndex[value]?.forEach { i -> + val value = uf.find(i) + count[value] = count.getOrDefault(value, 0) + 1 + res += count[value] ?: 0 + } + } + + return res + } +} diff --git a/kotlin/2439-minimize-maximum-of-array.kt b/kotlin/2439-minimize-maximum-of-array.kt new file mode 100644 index 000000000..9edb5ca40 --- /dev/null +++ b/kotlin/2439-minimize-maximum-of-array.kt @@ -0,0 +1,13 @@ +class Solution { + fun minimizeArrayValue(nums: IntArray): Int { + var res = nums[0].toDouble() + var sum = nums[0].toDouble() + + for (i in 1..nums.lastIndex) { + sum += nums[i].toDouble() + var current = Math.ceil(sum / (i + 1)) + res = maxOf(res, current) + } + + return res.toInt() + } diff --git a/kotlin/2466-count-ways-to-build-good-strings.kt b/kotlin/2466-count-ways-to-build-good-strings.kt new file mode 100644 index 000000000..906a7416e --- /dev/null +++ b/kotlin/2466-count-ways-to-build-good-strings.kt @@ -0,0 +1,45 @@ +/* +* Recursion/DFS + memoization +*/ +class Solution { + fun countGoodStrings(low: Int, high: Int, zero: Int, one: Int): Int { + val dp = IntArray(high + 1) { -1 } + val mod = 1000000007 + + fun dfs(i: Int): Int { + if (i > high) + return 0 + if (dp[i] != -1) + return dp[i] + + dp[i] = if (i >= low) 1 else 0 + dp[i] += dfs(i + zero) + dfs(i + one) + dp[i] = dp[i] % mod + + return dp[i] + } + + return dfs(0) + } +} + +/* +* Bottom up DP +*/ +class Solution { + fun countGoodStrings(low: Int, high: Int, zero: Int, one: Int): Int { + val dp = IntArray(high + 1) + val mod = 1000000007 + + dp[0] = 1 + for (i in 1..high) { + dp[i] += if (i - one >= 0) dp[i - one] else 0 + dp[i] += if (i - zero >= 0) dp[i - zero] else 0 + dp[i] = dp[i] % mod + } + + var sum = 0 + for (i in low..high) sum = (sum + dp[i]) % mod + return sum + } +} diff --git a/kotlin/2477-minimum-fuel-cost-to-report-to-the-capital.kt b/kotlin/2477-minimum-fuel-cost-to-report-to-the-capital.kt new file mode 100644 index 000000000..ec9442b12 --- /dev/null +++ b/kotlin/2477-minimum-fuel-cost-to-report-to-the-capital.kt @@ -0,0 +1,31 @@ +class Solution { + fun minimumFuelCost(roads: Array, seats: Int): Long { + + var result = 0.0 + if (roads.size == 0) return result.toLong() + + val adj = HashMap>() + for (road in roads){ + adj[road[0]] = adj.getOrDefault(road[0], ArrayList()).apply { this.add(road[1]) } + adj[road[1]] = adj.getOrDefault(road[1], ArrayList()).apply { this.add(road[0]) } + } + + fun dfs(node: Int, parent: Int): Double { + + var passengers = 0.0 + var res = 0.0 + for (child in adj[node]!!) { + if (child != parent) { + val childPassengers = dfs(child, node) + passengers += childPassengers + result += Math.ceil(childPassengers / seats) + } + } + + return passengers + 1.0 + } + + dfs(0, -1) + return result.toLong() + } +} diff --git a/kotlin/2483-minimum-penalty-for-a-shop.kt b/kotlin/2483-minimum-penalty-for-a-shop.kt new file mode 100644 index 000000000..9f9c7f51a --- /dev/null +++ b/kotlin/2483-minimum-penalty-for-a-shop.kt @@ -0,0 +1,47 @@ +// Time O(n) and space O(n) with prefix/suffix sums +class Solution { + fun bestClosingTime(c: String): Int { + val n = c.length + val prefix = IntArray (n + 1) + val postfix = IntArray (n + 1) + + for (i in 1..n) { + prefix[i] = prefix[i - 1] + if (c[i - 1] == 'N') 1 else 0 + } + + for (i in n - 1 downTo 0) { + postfix[i] = postfix[i + 1] + if (c[i] == 'Y') 1 else 0 + } + + var res = Integer.MAX_VALUE + var min = Integer.MAX_VALUE + for (i in 0..n) { + val pen = prefix[i] + postfix[i] + if (pen < min) { + min = pen + res = i + } + } + + return res + } +} + +// Time O(n) and space O(1) with Kadane's Algorithm +class Solution { + fun bestClosingTime(customers: String): Int { + var cur = 0 + var max = 0 + var closeTime = 0 + + for ((i, c) in customers.withIndex()) { + cur += if(c == 'Y') 1 else -1 + if (cur > max) { + max = cur + closeTime = i + 1 + } + } + + return closeTime + } +} diff --git a/kotlin/2492-minimum-score-of-a-path-between-two-cities.kt b/kotlin/2492-minimum-score-of-a-path-between-two-cities.kt new file mode 100644 index 000000000..a53bbad69 --- /dev/null +++ b/kotlin/2492-minimum-score-of-a-path-between-two-cities.kt @@ -0,0 +1,30 @@ +class Solution { + fun minScore(n: Int, roads: Array): Int { + var res = Integer.MAX_VALUE + val adj = HashMap>>() + + for((a,b,d) in roads) { + adj[a] = adj.getOrDefault(a, ArrayList()).apply{ + this.add(b to d) + } + adj[b] = adj.getOrDefault(b, ArrayList()).apply{ + this.add(a to d) + } + } + + val visited = HashSet() + + fun dfs(curr: Int) { + if(curr in visited) + return + visited.add(curr) + for((neigh, dist) in adj[curr]!!) { + res = minOf(res, dist) + dfs(neigh) + } + } + + dfs(1) + return res + } +} diff --git a/kotlin/2542-maximum-subsequence-score.kt b/kotlin/2542-maximum-subsequence-score.kt new file mode 100644 index 000000000..eab54e432 --- /dev/null +++ b/kotlin/2542-maximum-subsequence-score.kt @@ -0,0 +1,20 @@ +class Solution { + fun maxScore(nums1: IntArray, nums2: IntArray, k: Int): Long { + val nums = nums1.zip(nums2).sortedWith(compareBy({ -it.second })) + val minHeap = PriorityQueue() + + var sum = 0L + var res = 0L + for ((n1, n2) in nums) { + sum += n1 + minHeap.add(n1) + + if (minHeap.size > k) + sum -= minHeap.poll() + if (minHeap.size == k) + res = maxOf(res, sum * n2) + } + + return res + } +} diff --git a/kotlin/2610-convert-an-array-into-a-2d-array-with-conditions.kt b/kotlin/2610-convert-an-array-into-a-2d-array-with-conditions.kt new file mode 100644 index 000000000..9b3dc80a4 --- /dev/null +++ b/kotlin/2610-convert-an-array-into-a-2d-array-with-conditions.kt @@ -0,0 +1,16 @@ +class Solution { + fun findMatrix(nums: IntArray): List> { + val count = HashMap() + val res = mutableListOf>() + + for (n in nums) { + var row = count[n] ?: 0 + if (res.size == row) + res.add(mutableListOf()) + res[row].add(n) + count[n] = (count[n] ?: 0) + 1 + } + + return res + } +} diff --git a/kotlin/2616-minimize-the-maximum-difference-of-pairs.kt b/kotlin/2616-minimize-the-maximum-difference-of-pairs.kt new file mode 100644 index 000000000..a2fa1a0ce --- /dev/null +++ b/kotlin/2616-minimize-the-maximum-difference-of-pairs.kt @@ -0,0 +1,36 @@ +class Solution { + fun minimizeMax(nums: IntArray, p: Int): Int { + if (p == 0) return 0 + + val n = nums.size + nums.sort() + + fun good(x: Int): Boolean { + var i = 0 + var count = 0 + while (i < n - 1) { + if ((nums[i + 1] - nums[i]) <= x) { + count++ + i += 2 + } else { + i++ + } + if (count == p) return true + } + return false + } + + var l = 0 + var r = 1000000000 + while (l < r) { + val m = l + (r - l) / 2 + if (good(m)) { + r = m + } else { + l = m + 1 + } + } + + return l + } +} diff --git a/kotlin/2706-buy-two-chocolates.kt b/kotlin/2706-buy-two-chocolates.kt new file mode 100644 index 000000000..20bfc7b85 --- /dev/null +++ b/kotlin/2706-buy-two-chocolates.kt @@ -0,0 +1,18 @@ +class Solution { + fun buyChoco(prices: IntArray, money: Int): Int { + var min1 = Integer.MAX_VALUE + var min2 = Integer.MAX_VALUE + + for (p in prices) { + if (p < min1) { + min2 = min1 + min1 = p + } else { + min2 = minOf(min2, p) + } + } + + val left = money - (min1 + min2) + return if (left >= 0) left else money + } +} diff --git a/kotlin/2709-greatest-common-divisor-traversal.kt b/kotlin/2709-greatest-common-divisor-traversal.kt new file mode 100644 index 000000000..ffbe69fb3 --- /dev/null +++ b/kotlin/2709-greatest-common-divisor-traversal.kt @@ -0,0 +1,59 @@ +class UnionFind (val n: Int) { + val parent = IntArray (n) { it } + val size = IntArray (n) { 1 } + var count = n + + fun find(x: Int): Int { + if (x != parent[x]) + parent[x] = find(parent[x]) + return parent[x] + } + + fun union(x: Int, y: Int) { + val px = find(x) + val py = find(y) + if (px != py) { + if (size[px] > size[py]) { + parent[py] = px + size[px] += size[py] + } else { + parent[px] = py + size[py] += size[px] + } + count-- + } + } +} + +class Solution { + fun canTraverseAllPairs(nums: IntArray): Boolean { + val uf = UnionFind (nums.size) + val factorIndex = HashMap() + + for (i in nums.indices) { + var n = nums[i] + var f = 2 + + while (f * f <= n) { + if (n % f == 0) { + if (f in factorIndex) + uf.union(i, factorIndex[f]!!) + else + factorIndex[f] = i + while (n % f == 0) + n /= f + } + f++ + } + + if (n > 1) { + if (n in factorIndex) + uf.union(i, factorIndex[n]!!) + else + factorIndex[n] = i + } + } + + return uf.count == 1 + } +} diff --git a/kotlin/2742-painting-the-walls.kt b/kotlin/2742-painting-the-walls.kt new file mode 100644 index 000000000..7ed925257 --- /dev/null +++ b/kotlin/2742-painting-the-walls.kt @@ -0,0 +1,25 @@ +class Solution { + fun paintWalls(cost: IntArray, time: IntArray): Int { + val n = cost.size + val dp = Array (n) { IntArray (n + 1) { -1 } } + + fun dfs(i: Int, rem: Int): Int { + if (rem <= 0) return 0 + if (i == n) return INFINITY + if (dp[i][rem] != -1) return dp[i][rem] + + dp[i][rem] = minOf( + cost[i] + dfs(i + 1, rem - 1 - time[i]), + dfs(i + 1, rem) + ) + + return dp[i][rem] + } + + return dfs(0, n) + } + + companion object { + const val INFINITY = 500000001 + } +} diff --git a/kotlin/2864-maximum-odd-binary-number.kt b/kotlin/2864-maximum-odd-binary-number.kt new file mode 100644 index 000000000..d7219c92c --- /dev/null +++ b/kotlin/2864-maximum-odd-binary-number.kt @@ -0,0 +1,49 @@ +// traverse and swap indices +class Solution { + fun maximumOddBinaryNumber(s: String): String { + val sArr = s.toCharArray() + var left = 0 + + for (i in 0 until sArr.size) { + if (sArr[i] == '1') { + sArr[i] = sArr[left].also { sArr[left] = sArr[i] } + left++ + } + } + + sArr[left - 1] = sArr[sArr.lastIndex].also { sArr[sArr.lastIndex] = sArr[left - 1] } + return sArr.joinToString("") + } +} + +// Count 1's and build at once +class Solution { + fun maximumOddBinaryNumber(s: String): String { + var count = 0 + for (c in s) { + if (c == '1') count++ + } + + return "1".repeat(count - 1) + "0".repeat(s.length - count) + "1" + } +} + +// Rolling string builder +class Solution { + fun maximumOddBinaryNumber(s: String) = buildString { + var count = s.count { it == '1' } - 1 + val n = s.length + + for (i in 0 until n - 1) { + if (count > 0) { + append("1") + count-- + } else { + append("0") + } + } + + append("1") + return toString() + } +} diff --git a/kotlin/2870-minimum-number-of-operations-to-make-array-empty.kt b/kotlin/2870-minimum-number-of-operations-to-make-array-empty.kt new file mode 100644 index 000000000..634ea64dd --- /dev/null +++ b/kotlin/2870-minimum-number-of-operations-to-make-array-empty.kt @@ -0,0 +1,13 @@ +class Solution { + fun minOperations(nums: IntArray): Int { + val count = nums.asIterable().groupingBy { it }.eachCount() + + var res = 0 + for (c in count.values) { + if (c == 1) return -1 + res += (c / 3) + if (c % 3 > 0) 1 else 0 // "Ceil" function + } + + return res + } +} diff --git a/kotlin/2971-find-polygon-with-the-largest-perimeter.kt b/kotlin/2971-find-polygon-with-the-largest-perimeter.kt new file mode 100644 index 000000000..84d092820 --- /dev/null +++ b/kotlin/2971-find-polygon-with-the-largest-perimeter.kt @@ -0,0 +1,55 @@ +class Solution { + fun largestPerimeter(nums: IntArray): Long { + nums.sort() + var total = 0L + var res = -1L + + for (n in nums) { + if (total > n) + res = total + n + total += n + } + + return res + } +} + +// Same logic but a top down approach instead of bottom up as the solution above +class Solution { + fun largestPerimeter(nums: IntArray): Long { + nums.sortDescending() + + var sum = 0L + for (n in nums) + sum += n + + for (n in nums) { + val rest = sum - n + if (rest > n) return sum + sum -= n + } + + return -1L + } +} + +/* + * Same intuition as above but use a maxheap instead of sorting. In Kotlin and Java the time complexity is O(nlogn) + * but using heapify in Python or priority_queue in C++ this will have a time complexity of O(logn + n) ~ O(n) + */ +class Solution { + fun largestPerimeter(nums: IntArray): Long { + val max = PriorityQueue { a, b -> b - a } + var sum = 0L + + for (n in nums) { + sum += n + max.add(n) + } + + while (max.isNotEmpty() && sum <= max.peek() * 2) + sum -= max.poll() + + return if (max.size >= 2 && sum > 0L) sum else -1L + } +} diff --git a/python/0001-two-sum.py b/python/0001-two-sum.py new file mode 100644 index 000000000..e3eb994d3 --- /dev/null +++ b/python/0001-two-sum.py @@ -0,0 +1,9 @@ +class Solution: + def twoSum(self, nums: List[int], target: int) -> List[int]: + prevMap = {} # val -> index + + for i, n in enumerate(nums): + diff = target - n + if diff in prevMap: + return [prevMap[diff], i] + prevMap[n] = i diff --git a/python/0002-add-two-numbers.py b/python/0002-add-two-numbers.py new file mode 100644 index 000000000..01d754c99 --- /dev/null +++ b/python/0002-add-two-numbers.py @@ -0,0 +1,27 @@ +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode: + dummy = ListNode() + cur = dummy + + carry = 0 + while l1 or l2 or carry: + v1 = l1.val if l1 else 0 + v2 = l2.val if l2 else 0 + + # new digit + val = v1 + v2 + carry + carry = val // 10 + val = val % 10 + cur.next = ListNode(val) + + # update ptrs + cur = cur.next + l1 = l1.next if l1 else None + l2 = l2.next if l2 else None + + return dummy.next diff --git a/python/0003-longest-substring-without-repeating-characters.py b/python/0003-longest-substring-without-repeating-characters.py new file mode 100644 index 000000000..4fa9ee4cb --- /dev/null +++ b/python/0003-longest-substring-without-repeating-characters.py @@ -0,0 +1,13 @@ +class Solution: + def lengthOfLongestSubstring(self, s: str) -> int: + charSet = set() + l = 0 + res = 0 + + for r in range(len(s)): + while s[r] in charSet: + charSet.remove(s[l]) + l += 1 + charSet.add(s[r]) + res = max(res, r - l + 1) + return res diff --git a/python/0004-median-of-two-sorted-arrays.py b/python/0004-median-of-two-sorted-arrays.py new file mode 100644 index 000000000..88786ebdc --- /dev/null +++ b/python/0004-median-of-two-sorted-arrays.py @@ -0,0 +1,33 @@ +# Time: log(min(n, m)) + + +class Solution: + def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float: + A, B = nums1, nums2 + total = len(nums1) + len(nums2) + half = total // 2 + + if len(B) < len(A): + A, B = B, A + + l, r = 0, len(A) - 1 + while True: + i = (l + r) // 2 # A + j = half - i - 2 # B + + Aleft = A[i] if i >= 0 else float("-infinity") + Aright = A[i + 1] if (i + 1) < len(A) else float("infinity") + Bleft = B[j] if j >= 0 else float("-infinity") + Bright = B[j + 1] if (j + 1) < len(B) else float("infinity") + + # partition is correct + if Aleft <= Bright and Bleft <= Aright: + # odd + if total % 2: + return min(Aright, Bright) + # even + return (max(Aleft, Bleft) + min(Aright, Bright)) / 2 + elif Aleft > Bright: + r = i - 1 + else: + l = i + 1 diff --git a/python/0005-longest-palindromic-substring.py b/python/0005-longest-palindromic-substring.py new file mode 100644 index 000000000..1c1508775 --- /dev/null +++ b/python/0005-longest-palindromic-substring.py @@ -0,0 +1,20 @@ + +class Solution: + def longestPalindrome(self, s: str) -> str: + self.res = "" + self.lenres = 0 + for i in range(len(s)): + s1 = self.helper(s, i, i) + s2 = self.helper(s, i, i + 1) + return s2 + + def helper(self, s, left, right): + while left >= 0 and right < len(s) and s[left] == s[right]: + if (right - left + 1) > self.lenres: + self.res = s[left:right+1] + self.lenres = right - left + 1 + left -= 1 + right += 1 + return self.res + + diff --git a/python/0006-zigzag-conversion.py b/python/0006-zigzag-conversion.py new file mode 100644 index 000000000..4b712cc80 --- /dev/null +++ b/python/0006-zigzag-conversion.py @@ -0,0 +1,18 @@ +class Solution: + def convert(self, s: str, numRows: int) -> str: + if numRows == 1 or numRows >= len(s): + return s + + res = [""] * numRows + + index = 0 + step = 1 + for c in s: + res[index] += c + if index == 0: + step = 1 + elif index == numRows - 1: + step = -1 + index += step + + return "".join(res) \ No newline at end of file diff --git a/python/0007-reverse-integer.py b/python/0007-reverse-integer.py new file mode 100644 index 000000000..9a9aa0797 --- /dev/null +++ b/python/0007-reverse-integer.py @@ -0,0 +1,20 @@ +class Solution: + def reverse(self, x: int) -> int: + # Integer.MAX_VALUE = 2147483647 (end with 7) + # Integer.MIN_VALUE = -2147483648 (end with -8 ) + + MIN = -2147483648 # -2^31, + MAX = 2147483647 # 2^31 - 1 + + res = 0 + while x: + digit = int(math.fmod(x, 10)) # (python dumb) -1 % 10 = 9 + x = int(x / 10) # (python dumb) -1 // 10 = -1 + + if res > MAX // 10 or (res == MAX // 10 and digit > MAX % 10): + return 0 + if res < MIN // 10 or (res == MIN // 10 and digit < MIN % 10): + return 0 + res = (res * 10) + digit + + return res diff --git a/python/0009-palindrome-number.py b/python/0009-palindrome-number.py new file mode 100644 index 000000000..9898c7cc5 --- /dev/null +++ b/python/0009-palindrome-number.py @@ -0,0 +1,17 @@ +class Solution: + def isPalindrome(self, x: int) -> bool: + if x < 0: return False + + div = 1 + while x >= 10 * div: + div *= 10 + + while x: + right = x % 10 + left = x // div + + if left != right: return False + + x = (x % div) // 10 + div = div / 100 + return True diff --git a/python/0010-regular-expression-matching.py b/python/0010-regular-expression-matching.py new file mode 100644 index 000000000..db8ad0a4c --- /dev/null +++ b/python/0010-regular-expression-matching.py @@ -0,0 +1,46 @@ +# BOTTOM-UP Dynamic Programming +class Solution: + def isMatch(self, s: str, p: str) -> bool: + cache = [[False] * (len(p) + 1) for i in range(len(s) + 1)] + cache[len(s)][len(p)] = True + + for i in range(len(s), -1, -1): + for j in range(len(p) - 1, -1, -1): + match = i < len(s) and (s[i] == p[j] or p[j] == ".") + + if (j + 1) < len(p) and p[j + 1] == "*": + cache[i][j] = cache[i][j + 2] + if match: + cache[i][j] = cache[i + 1][j] or cache[i][j] + elif match: + cache[i][j] = cache[i + 1][j + 1] + + return cache[0][0] + + +# TOP DOWN MEMOIZATION +class Solution: + def isMatch(self, s: str, p: str) -> bool: + cache = {} + + def dfs(i, j): + if (i, j) in cache: + return cache[(i, j)] + if i >= len(s) and j >= len(p): + return True + if j >= len(p): + return False + + match = i < len(s) and (s[i] == p[j] or p[j] == ".") + if (j + 1) < len(p) and p[j + 1] == "*": + cache[(i, j)] = dfs(i, j + 2) or ( # dont use * + match and dfs(i + 1, j) + ) # use * + return cache[(i, j)] + if match: + cache[(i, j)] = dfs(i + 1, j + 1) + return cache[(i, j)] + cache[(i, j)] = False + return False + + return dfs(0, 0) diff --git a/python/0011-container-with-most-water.py b/python/0011-container-with-most-water.py new file mode 100644 index 000000000..a51611b04 --- /dev/null +++ b/python/0011-container-with-most-water.py @@ -0,0 +1,13 @@ +class Solution: + def maxArea(self, height: List[int]) -> int: + l, r = 0, len(height) - 1 + res = 0 + + while l < r: + res = max(res, min(height[l], height[r]) * (r - l)) + if height[l] < height[r]: + l += 1 + elif height[r] <= height[l]: + r -= 1 + + return res diff --git a/python/0012-integer-to-roman.py b/python/0012-integer-to-roman.py new file mode 100644 index 000000000..f0ce9df23 --- /dev/null +++ b/python/0012-integer-to-roman.py @@ -0,0 +1,24 @@ +class Solution: + def intToRoman(self, num: int) -> str: + symList = [ + ["I", 1], + ["IV", 4], + ["V", 5], + ["IX", 9], + ["X", 10], + ["XL", 40], + ["L", 50], + ["XC", 90], + ["C", 100], + ["CD", 400], + ["D", 500], + ["CM", 900], + ["M", 1000], + ] + res = "" + for sym, val in reversed(symList): + if num // val: + count = num // val + res += sym * count + num = num % val + return res diff --git a/python/0013-roman-to-integer.py b/python/0013-roman-to-integer.py new file mode 100644 index 000000000..8aa9543c1 --- /dev/null +++ b/python/0013-roman-to-integer.py @@ -0,0 +1,10 @@ +class Solution: + def romanToInt(self, s: str) -> int: + roman = {"I": 1, "V": 5, "X": 10, "L": 50, "C": 100, "D": 500, "M": 1000} + res = 0 + for i in range(len(s)): + if i + 1 < len(s) and roman[s[i]] < roman[s[i + 1]]: + res -= roman[s[i]] + else: + res += roman[s[i]] + return res diff --git a/python/0014-longest-common-prefix.py b/python/0014-longest-common-prefix.py new file mode 100644 index 000000000..7d4eb2768 --- /dev/null +++ b/python/0014-longest-common-prefix.py @@ -0,0 +1,7 @@ +class Solution: + def longestCommonPrefix(self, strs: List[str]) -> str: + for i in range(len(strs[0])): + for s in strs: + if i >= len(s) or s[i] != strs[0][i]: + return strs[0][:i] + return strs[0] diff --git a/python/0015-3sum.py b/python/0015-3sum.py new file mode 100644 index 000000000..f9a1463bb --- /dev/null +++ b/python/0015-3sum.py @@ -0,0 +1,28 @@ +class Solution: + def threeSum(self, nums: List[int]) -> List[List[int]]: + res = [] + nums.sort() + + for i, a in enumerate(nums): + # Skip positive integers + if a > 0: + break + + if i > 0 and a == nums[i - 1]: + continue + + l, r = i + 1, len(nums) - 1 + while l < r: + threeSum = a + nums[l] + nums[r] + if threeSum > 0: + r -= 1 + elif threeSum < 0: + l += 1 + else: + res.append([a, nums[l], nums[r]]) + l += 1 + r -= 1 + while nums[l] == nums[l - 1] and l < r: + l += 1 + + return res diff --git a/python/0016-3sum-closest.py b/python/0016-3sum-closest.py new file mode 100644 index 000000000..86c1067ce --- /dev/null +++ b/python/0016-3sum-closest.py @@ -0,0 +1,26 @@ +class Solution: + def threeSumClosest(self, nums: List[int], target: int) -> int: + + nums.sort() + best = float('inf') + + for i in range(len(nums) - 2): + + val = nums[i] + left = i + 1 + right = len(nums) - 1 + + while left < right: + + currentGap = abs(target - (val + nums[left] + nums[right])) + + if abs(best - target) > currentGap: + best = val + nums[left] + nums[right] + if val + nums[left] + nums[right] < target: + left += 1 + elif val + nums[left] + nums[right] > target: + right -= 1 + else: #closest it can get + return target + + return best diff --git a/python/0017-letter-combinations-of-a-phone-number.py b/python/0017-letter-combinations-of-a-phone-number.py new file mode 100644 index 000000000..22ebf9a03 --- /dev/null +++ b/python/0017-letter-combinations-of-a-phone-number.py @@ -0,0 +1,25 @@ +class Solution: + def letterCombinations(self, digits: str) -> List[str]: + res = [] + digitToChar = { + "2": "abc", + "3": "def", + "4": "ghi", + "5": "jkl", + "6": "mno", + "7": "qprs", + "8": "tuv", + "9": "wxyz", + } + + def backtrack(i, curStr): + if len(curStr) == len(digits): + res.append(curStr) + return + for c in digitToChar[digits[i]]: + backtrack(i + 1, curStr + c) + + if digits: + backtrack(0, "") + + return res diff --git a/python/0018-4sum.py b/python/0018-4sum.py new file mode 100644 index 000000000..2b5c91669 --- /dev/null +++ b/python/0018-4sum.py @@ -0,0 +1,26 @@ +class Solution: + def fourSum(self, nums, target): + def findNsum(l, r, target, N, result, results): + if r-l+1 < N or N < 2 or target < nums[l]*N or target > nums[r]*N: + return + if N == 2: + while l < r: + s = nums[l] + nums[r] + if s == target: + results.append(result + [nums[l], nums[r]]) + l += 1 + while l < r and nums[l] == nums[l-1]: + l += 1 + elif s < target: + l += 1 + else: + r -= 1 + else: + for i in range(l, r+1): + if i == l or (i > l and nums[i-1] != nums[i]): + findNsum(i+1, r, target-nums[i], N-1, result+[nums[i]], results) + + nums.sort() + results = [] + findNsum(0, len(nums)-1, target, 4, [], results) + return results \ No newline at end of file diff --git a/python/0019-remove-nth-node-from-end-of-list.py b/python/0019-remove-nth-node-from-end-of-list.py new file mode 100644 index 000000000..0013f1db2 --- /dev/null +++ b/python/0019-remove-nth-node-from-end-of-list.py @@ -0,0 +1,17 @@ +class Solution: + def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode: + dummy = ListNode(0, head) + left = dummy + right = head + + while n > 0: + right = right.next + n -= 1 + + while right: + left = left.next + right = right.next + + # delete + left.next = left.next.next + return dummy.next diff --git a/python/0020-valid-parentheses.py b/python/0020-valid-parentheses.py new file mode 100644 index 000000000..ce3bf39cd --- /dev/null +++ b/python/0020-valid-parentheses.py @@ -0,0 +1,14 @@ +class Solution: + def isValid(self, s: str) -> bool: + bracketMap = {")": "(", "]": "[", "}": "{"} + stack = [] + + for c in s: + if c not in bracketMap: + stack.append(c) + continue + if not stack or stack[-1] != bracketMap[c]: + return False + stack.pop() + + return not stack diff --git a/python/0021-merge-two-sorted-lists.py b/python/0021-merge-two-sorted-lists.py new file mode 100644 index 000000000..e08821bda --- /dev/null +++ b/python/0021-merge-two-sorted-lists.py @@ -0,0 +1,34 @@ +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next + +# Iterative +class Solution: + def mergeTwoLists(self, list1: ListNode, list2: ListNode) -> ListNode: + dummy = node = ListNode() + + while list1 and list2: + if list1.val < list2.val: + node.next = list1 + list1 = list1.next + else: + node.next = list2 + list2 = list2.next + node = node.next + + node.next = list1 or list2 + + return dummy.next + +# Recursive +class Solution: + def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]: + if not list1: + return list2 + if not list2: + return list1 + lil, big = (list1, list2) if list1.val < list2.val else (list2, list1) + lil.next = self.mergeTwoLists(lil.next, big) + return lil diff --git a/python/0022-generate-parentheses.py b/python/0022-generate-parentheses.py new file mode 100644 index 000000000..879e66738 --- /dev/null +++ b/python/0022-generate-parentheses.py @@ -0,0 +1,21 @@ +class Solution: + def generateParenthesis(self, n: int) -> List[str]: + stack = [] + res = [] + + def backtrack(openN, closedN): + if openN == closedN == n: + res.append("".join(stack)) + return + + if openN < n: + stack.append("(") + backtrack(openN + 1, closedN) + stack.pop() + if closedN < openN: + stack.append(")") + backtrack(openN, closedN + 1) + stack.pop() + + backtrack(0, 0) + return res diff --git a/python/0023-merge-k-sorted-lists.py b/python/0023-merge-k-sorted-lists.py new file mode 100644 index 000000000..4613f0235 --- /dev/null +++ b/python/0023-merge-k-sorted-lists.py @@ -0,0 +1,36 @@ +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def mergeKLists(self, lists: List[ListNode]) -> ListNode: + if not lists or len(lists) == 0: + return None + + while len(lists) > 1: + mergedLists = [] + for i in range(0, len(lists), 2): + l1 = lists[i] + l2 = lists[i + 1] if (i + 1) < len(lists) else None + mergedLists.append(self.mergeList(l1, l2)) + lists = mergedLists + return lists[0] + + def mergeList(self, l1, l2): + dummy = ListNode() + tail = dummy + + while l1 and l2: + if l1.val < l2.val: + tail.next = l1 + l1 = l1.next + else: + tail.next = l2 + l2 = l2.next + tail = tail.next + if l1: + tail.next = l1 + if l2: + tail.next = l2 + return dummy.next diff --git a/python/0024-swap-nodes-in-pairs.py b/python/0024-swap-nodes-in-pairs.py new file mode 100644 index 000000000..19fb28cce --- /dev/null +++ b/python/0024-swap-nodes-in-pairs.py @@ -0,0 +1,25 @@ +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def swapPairs(self, head: ListNode) -> ListNode: + dummy = ListNode(0, head) + prev, curr = dummy, head + + while curr and curr.next: + # save ptrs + nxtPair = curr.next.next + second = curr.next + + # reverse this pair + second.next = curr + curr.next = nxtPair + prev.next = second + + # update ptrs + prev = curr + curr = nxtPair + + return dummy.next diff --git a/python/0025-reverse-nodes-in-k-group.py b/python/0025-reverse-nodes-in-k-group.py new file mode 100644 index 000000000..43d9d5879 --- /dev/null +++ b/python/0025-reverse-nodes-in-k-group.py @@ -0,0 +1,29 @@ +class Solution: + def reverseKGroup(self, head: ListNode, k: int) -> ListNode: + dummy = ListNode(0, head) + groupPrev = dummy + + while True: + kth = self.getKth(groupPrev, k) + if not kth: + break + groupNext = kth.next + + # reverse group + prev, curr = kth.next, groupPrev.next + while curr != groupNext: + tmp = curr.next + curr.next = prev + prev = curr + curr = tmp + + tmp = groupPrev.next + groupPrev.next = kth + groupPrev = tmp + return dummy.next + + def getKth(self, curr, k): + while curr and k > 0: + curr = curr.next + k -= 1 + return curr diff --git a/python/0026-remove-duplicates-from-sorted-array.py b/python/0026-remove-duplicates-from-sorted-array.py new file mode 100644 index 000000000..d644f53a6 --- /dev/null +++ b/python/0026-remove-duplicates-from-sorted-array.py @@ -0,0 +1,9 @@ +class Solution: + def removeDuplicates(self, nums: List[int]) -> int: + L = 1 + + for R in range(1, len(nums)): + if nums[R] != nums[R - 1]: + nums[L] = nums[R] + L += 1 + return L diff --git a/python/0027-remove-element.py b/python/0027-remove-element.py new file mode 100644 index 000000000..bd3724ea0 --- /dev/null +++ b/python/0027-remove-element.py @@ -0,0 +1,25 @@ +class Solution: + def removeElement(self, nums: List[int], val: int) -> int: + k = 0 + for i in range(len(nums)): + if nums[i] != val: + nums[k] = nums[i] + k += 1 + return k + +# Optimized solution with the same time and space complexity +class Solution: + def removeElement(self, nums: List[int], val: int) -> int: + # Avoid unessary copy operations in a previous solution, when k == i and nums[i] != val + # by swapping nums[i] and the last element of the array (nums[n]) + n = len(nums) + i = 0 + + while i < n: + if nums[i] == val: + nums[i], nums[n - 1] = nums[n - 1], nums[i] + n -= 1 # decrement the length of the array by discarding the last element + else: + i += 1 + + return n diff --git a/python/0028-find-the-index-of-the-first-occurrence-in-a-string.py b/python/0028-find-the-index-of-the-first-occurrence-in-a-string.py new file mode 100644 index 000000000..c2095418f --- /dev/null +++ b/python/0028-find-the-index-of-the-first-occurrence-in-a-string.py @@ -0,0 +1,31 @@ +class Solution: + def strStr(self, haystack: str, needle: str) -> int: + if needle == "": + return 0 + lps = [0] * len(needle) + + prevLPS, i = 0, 1 + while i < len(needle): + if needle[i] == needle[prevLPS]: + lps[i] = prevLPS + 1 + prevLPS += 1 + i += 1 + elif prevLPS == 0: + lps[i] = 0 + i += 1 + else: + prevLPS = lps[prevLPS - 1] + + i = 0 # ptr for haystack + j = 0 # ptr for needle + while i < len(haystack): + if haystack[i] == needle[j]: + i, j = i + 1, j + 1 + else: + if j == 0: + i += 1 + else: + j = lps[j - 1] + if j == len(needle): + return i - len(needle) + return -1 diff --git a/python/0033-search-in-rotated-sorted-array.py b/python/0033-search-in-rotated-sorted-array.py new file mode 100644 index 000000000..fe01019d4 --- /dev/null +++ b/python/0033-search-in-rotated-sorted-array.py @@ -0,0 +1,22 @@ +class Solution: + def search(self, nums: List[int], target: int) -> int: + l, r = 0, len(nums) - 1 + + while l <= r: + mid = (l + r) // 2 + if target == nums[mid]: + return mid + + # left sorted portion + if nums[l] <= nums[mid]: + if target > nums[mid] or target < nums[l]: + l = mid + 1 + else: + r = mid - 1 + # right sorted portion + else: + if target < nums[mid] or target > nums[r]: + r = mid - 1 + else: + l = mid + 1 + return -1 diff --git a/python/0034-find-first-and-last-position-of-element-in-sorted-array.py b/python/0034-find-first-and-last-position-of-element-in-sorted-array.py new file mode 100644 index 000000000..da6efc986 --- /dev/null +++ b/python/0034-find-first-and-last-position-of-element-in-sorted-array.py @@ -0,0 +1,23 @@ +class Solution: + def searchRange(self, nums: List[int], target: int) -> List[int]: + left = self.binSearch(nums, target, True) + right = self.binSearch(nums, target, False) + return [left, right] + + # leftBias=[True/False], if false, res is rightBiased + def binSearch(self, nums, target, leftBias): + l, r = 0, len(nums) - 1 + i = -1 + while l <= r: + m = (l + r) // 2 + if target > nums[m]: + l = m + 1 + elif target < nums[m]: + r = m - 1 + else: + i = m + if leftBias: + r = m - 1 + else: + l = m + 1 + return i diff --git a/python/0035-search-insert-position.py b/python/0035-search-insert-position.py new file mode 100644 index 000000000..80954ee9f --- /dev/null +++ b/python/0035-search-insert-position.py @@ -0,0 +1,13 @@ +class Solution: + def searchInsert(self, nums: List[int], target: int) -> int: + # O(log n) and O(1) + + + low, high = 0, len(nums) + while low nums[mid]: + low = mid + 1 + else: + high = mid + return low diff --git a/python/0036-valid-sudoku.py b/python/0036-valid-sudoku.py new file mode 100644 index 000000000..c4ad694d8 --- /dev/null +++ b/python/0036-valid-sudoku.py @@ -0,0 +1,21 @@ +class Solution: + def isValidSudoku(self, board: List[List[str]]) -> bool: + cols = collections.defaultdict(set) + rows = collections.defaultdict(set) + squares = collections.defaultdict(set) # key = (r /3, c /3) + + for r in range(9): + for c in range(9): + if board[r][c] == ".": + continue + if ( + board[r][c] in rows[r] + or board[r][c] in cols[c] + or board[r][c] in squares[(r // 3, c // 3)] + ): + return False + cols[c].add(board[r][c]) + rows[r].add(board[r][c]) + squares[(r // 3, c // 3)].add(board[r][c]) + + return True diff --git a/python/0039-combination-sum.py b/python/0039-combination-sum.py new file mode 100644 index 000000000..7df4c4b4f --- /dev/null +++ b/python/0039-combination-sum.py @@ -0,0 +1,18 @@ +class Solution: + def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: + res = [] + + def dfs(i, cur, total): + if total == target: + res.append(cur.copy()) + return + if i >= len(candidates) or total > target: + return + + cur.append(candidates[i]) + dfs(i, cur, total + candidates[i]) + cur.pop() + dfs(i + 1, cur, total) + + dfs(0, [], 0) + return res diff --git a/python/0040-combination-sum-ii.py b/python/0040-combination-sum-ii.py new file mode 100644 index 000000000..eb9790e3b --- /dev/null +++ b/python/0040-combination-sum-ii.py @@ -0,0 +1,24 @@ +class Solution: + def combinationSum2(self, candidates: List[int], target: int) -> List[List[int]]: + candidates.sort() + + res = [] + + def backtrack(cur, pos, target): + if target == 0: + res.append(cur.copy()) + return + if target <= 0: + return + + prev = -1 + for i in range(pos, len(candidates)): + if candidates[i] == prev: + continue + cur.append(candidates[i]) + backtrack(cur, i + 1, target - candidates[i]) + cur.pop() + prev = candidates[i] + + backtrack([], 0, target) + return res diff --git a/python/0041-first-missing-positive.py b/python/0041-first-missing-positive.py new file mode 100644 index 000000000..691f49136 --- /dev/null +++ b/python/0041-first-missing-positive.py @@ -0,0 +1,27 @@ +class Solution: + def firstMissingPositive(self, nums: List[int]) -> int: + A = nums + for i in range(len(A)): + if A[i] < 0: + A[i] = 0 + + for i in range(len(A)): + val = abs(A[i]) + if 1 <= val <= len(A): + if A[val - 1] > 0: + A[val - 1] *= -1 + elif A[val - 1] == 0: + A[val - 1] = -1 * (len(A) + 1) + + for i in range( 1, len(A)+ 1): + if A[i -1] >= 0: + return i + + return len(A) + 1 + + def firstMissingPositive_2(self, nums: List[int]) -> int: + new = set(nums) + i = 1 + while i in new: + i += 1 + return i diff --git a/python/0042-trapping-rain-water.py b/python/0042-trapping-rain-water.py new file mode 100644 index 000000000..954cb6d98 --- /dev/null +++ b/python/0042-trapping-rain-water.py @@ -0,0 +1,18 @@ +class Solution: + def trap(self, height: List[int]) -> int: + if not height: + return 0 + + l, r = 0, len(height) - 1 + leftMax, rightMax = height[l], height[r] + res = 0 + while l < r: + if leftMax < rightMax: + l += 1 + leftMax = max(leftMax, height[l]) + res += leftMax - height[l] + else: + r -= 1 + rightMax = max(rightMax, height[r]) + res += rightMax - height[r] + return res diff --git a/python/0043-multiply-strings.py b/python/0043-multiply-strings.py new file mode 100644 index 000000000..7359d0462 --- /dev/null +++ b/python/0043-multiply-strings.py @@ -0,0 +1,19 @@ +class Solution: + def multiply(self, num1: str, num2: str) -> str: + if "0" in [num1, num2]: + return "0" + + res = [0] * (len(num1) + len(num2)) + num1, num2 = num1[::-1], num2[::-1] + for i1 in range(len(num1)): + for i2 in range(len(num2)): + digit = int(num1[i1]) * int(num2[i2]) + res[i1 + i2] += digit + res[i1 + i2 + 1] += res[i1 + i2] // 10 + res[i1 + i2] = res[i1 + i2] % 10 + + res, beg = res[::-1], 0 + while beg < len(res) and res[beg] == 0: + beg += 1 + res = map(str, res[beg:]) + return "".join(res) diff --git a/python/0044-wildcard-matching.py b/python/0044-wildcard-matching.py new file mode 100644 index 000000000..047c1597c --- /dev/null +++ b/python/0044-wildcard-matching.py @@ -0,0 +1,11 @@ +class Solution(object): + def isMatch(self, s, p): + dp = [[False] * (len(p) + 1) for _ in range(len(s) + 1)] + dp[-1][-1] = True + for i in range(len(s), -1, -1): + for j in range(len(p) - 1, -1, -1): + if p[j] == '*': + dp[i][j] = dp[i][j + 1] or (i < len(s) and dp[i + 1][j]) + else: + dp[i][j] = i < len(s) and (p[j] == s[i] or p[j] == '?') and dp[i + 1][j + 1] + return dp[0][0] diff --git a/python/0045-jump-game-ii.py b/python/0045-jump-game-ii.py new file mode 100644 index 000000000..7a0fa78a3 --- /dev/null +++ b/python/0045-jump-game-ii.py @@ -0,0 +1,12 @@ +class Solution: + def jump(self, nums: List[int]) -> int: + l, r = 0, 0 + res = 0 + while r < (len(nums) - 1): + maxJump = 0 + for i in range(l, r + 1): + maxJump = max(maxJump, i + nums[i]) + l = r + 1 + r = maxJump + res += 1 + return res diff --git a/python/0046-permutations.py b/python/0046-permutations.py new file mode 100644 index 000000000..0e6cfb447 --- /dev/null +++ b/python/0046-permutations.py @@ -0,0 +1,17 @@ +class Solution: + def permute(self, nums: List[int]) -> List[List[int]]: + res = [] + + # base case + if len(nums) == 1: + return [nums[:]] # nums[:] is a deep copy + + for i in range(len(nums)): + n = nums.pop(0) + perms = self.permute(nums) + + for perm in perms: + perm.append(n) + res.extend(perms) + nums.append(n) + return res diff --git a/python/0047-permutations-ii.py b/python/0047-permutations-ii.py new file mode 100644 index 000000000..a97445344 --- /dev/null +++ b/python/0047-permutations-ii.py @@ -0,0 +1,25 @@ +import collections + + +class Solution: + + def permuteUnique(self, nums: List[int]) -> List[List[int]]: + result = [] + counter = collections.Counter(nums) + + def backtrack(perm, counter): + if len(perm) == len(nums): + result.append(perm.copy()) + + for n in counter: + if counter[n] == 0: + continue + perm.append(n) + counter[n] -= 1 + backtrack(perm, counter) + perm.pop() + counter[n] += 1 + + backtrack([], counter) + + return result diff --git a/python/0048-rotate-image.py b/python/0048-rotate-image.py new file mode 100644 index 000000000..6234ec455 --- /dev/null +++ b/python/0048-rotate-image.py @@ -0,0 +1,26 @@ +class Solution: + def rotate(self, matrix: List[List[int]]) -> None: + """ + Do not return anything, modify matrix in-place instead. + """ + l, r = 0, len(matrix) - 1 + while l < r: + for i in range(r - l): + top, bottom = l, r + + # save the topleft + topLeft = matrix[top][l + i] + + # move bottom left into top left + matrix[top][l + i] = matrix[bottom - i][l] + + # move bottom right into bottom left + matrix[bottom - i][l] = matrix[bottom][r - i] + + # move top right into bottom right + matrix[bottom][r - i] = matrix[top + i][r] + + # move top left into top right + matrix[top + i][r] = topLeft + r -= 1 + l += 1 diff --git a/python/0049-group-anagrams.py b/python/0049-group-anagrams.py new file mode 100644 index 000000000..b933f7508 --- /dev/null +++ b/python/0049-group-anagrams.py @@ -0,0 +1,31 @@ +class Solution: + def groupAnagrams(self, strs: List[str]) -> List[List[str]]: + groups = {} + + # Iterate over strings + for s in strs: # O(m) + count = {} + + # Count frequency of each character + for char in s: # O(n) + count[char] = count.get(char, 0) + 1 + + # Convert count Dict to List, sort it, and then convert to Tuple (we cannot use dicts or lists as keys in a hashmap) + tup = tuple(sorted(count.items())) # O(1) because there is limited amount of possible keys in the alphabet -> O(26) + O(26*log26) + O(26) + + if tup in groups: + groups[tup].append(s) + else: + groups[tup] = [s] + + return list(groups.values()) + + def groupAnagrams(self, strs: List[str]) -> List[List[str]]: + ans = collections.defaultdict(list) + + for s in strs: + count = [0] * 26 + for c in s: + count[ord(c) - ord("a")] += 1 + ans[tuple(count)].append(s) + return list(ans.values()) diff --git a/python/0050-powx-n.py b/python/0050-powx-n.py new file mode 100644 index 000000000..c7e77af15 --- /dev/null +++ b/python/0050-powx-n.py @@ -0,0 +1,13 @@ +class Solution: + def myPow(self, x: float, n: int) -> float: + def helper(x, n): + if x == 0: + return 0 + if n == 0: + return 1 + + res = helper(x * x, n // 2) + return x * res if n % 2 else res + + res = helper(x, abs(n)) + return res if n >= 0 else 1 / res diff --git a/python/0051-n-queens.py b/python/0051-n-queens.py new file mode 100644 index 000000000..49fe67d0c --- /dev/null +++ b/python/0051-n-queens.py @@ -0,0 +1,33 @@ +class Solution: + def solveNQueens(self, n: int) -> List[List[str]]: + col = set() + posDiag = set() # (r + c) + negDiag = set() # (r - c) + + res = [] + board = [["."] * n for i in range(n)] + + def backtrack(r): + if r == n: + copy = ["".join(row) for row in board] + res.append(copy) + return + + for c in range(n): + if c in col or (r + c) in posDiag or (r - c) in negDiag: + continue + + col.add(c) + posDiag.add(r + c) + negDiag.add(r - c) + board[r][c] = "Q" + + backtrack(r + 1) + + col.remove(c) + posDiag.remove(r + c) + negDiag.remove(r - c) + board[r][c] = "." + + backtrack(0) + return res diff --git a/python/0052-n-queens-ii.py b/python/0052-n-queens-ii.py new file mode 100644 index 000000000..b7d04ea2e --- /dev/null +++ b/python/0052-n-queens-ii.py @@ -0,0 +1,30 @@ +class Solution: + def totalNQueens(self, n: int) -> int: + answer = 0 + + cols = set() + posdiag = set() + negdiag = set() + + def backtrack(i): + if i == n: + nonlocal answer + answer += 1 + return + + for j in range(n): + if j in cols or (i+j) in posdiag or (i-j) in negdiag: + continue + + cols.add(j) + posdiag.add(i+j) + negdiag.add(i-j) + + backtrack(i+1) + + cols.remove(j) + posdiag.remove(i+j) + negdiag.remove(i-j) + + backtrack(0) + return answer \ No newline at end of file diff --git a/python/0053-maximum-subarray.py b/python/0053-maximum-subarray.py new file mode 100644 index 000000000..b2fc36d57 --- /dev/null +++ b/python/0053-maximum-subarray.py @@ -0,0 +1,11 @@ +class Solution: + def maxSubArray(self, nums: List[int]) -> int: + res = nums[0] + + total = 0 + for n in nums: + total += n + res = max(res, total) + if total < 0: + total = 0 + return res diff --git a/python/0054-spiral-matrix.py b/python/0054-spiral-matrix.py new file mode 100644 index 000000000..b6a96b40d --- /dev/null +++ b/python/0054-spiral-matrix.py @@ -0,0 +1,27 @@ +class Solution: + def spiralOrder(self, matrix: List[List[int]]) -> List[int]: + res = [] + left, right = 0, len(matrix[0]) + top, bottom = 0, len(matrix) + + while left < right and top < bottom: + # get every i in the top row + for i in range(left, right): + res.append(matrix[top][i]) + top += 1 + # get every i in the right col + for i in range(top, bottom): + res.append(matrix[i][right - 1]) + right -= 1 + if not (left < right and top < bottom): + break + # get every i in the bottom row + for i in range(right - 1, left - 1, -1): + res.append(matrix[bottom - 1][i]) + bottom -= 1 + # get every i in the left col + for i in range(bottom - 1, top - 1, -1): + res.append(matrix[i][left]) + left += 1 + + return res diff --git a/python/0055-jump-game.py b/python/0055-jump-game.py new file mode 100644 index 000000000..4163d7c0e --- /dev/null +++ b/python/0055-jump-game.py @@ -0,0 +1,8 @@ +class Solution: + def canJump(self, nums: List[int]) -> bool: + goal = len(nums) - 1 + + for i in range(len(nums) - 2, -1, -1): + if i + nums[i] >= goal: + goal = i + return goal == 0 diff --git a/python/0056-merge-intervals.py b/python/0056-merge-intervals.py new file mode 100644 index 000000000..90bfd1163 --- /dev/null +++ b/python/0056-merge-intervals.py @@ -0,0 +1,14 @@ +class Solution: + def merge(self, intervals: List[List[int]]) -> List[List[int]]: + intervals.sort(key=lambda pair: pair[0]) + output = [intervals[0]] + + for start, end in intervals: + lastEnd = output[-1][1] + + if start <= lastEnd: + # merge + output[-1][1] = max(lastEnd, end) + else: + output.append([start, end]) + return output diff --git a/python/0057-insert-interval.py b/python/0057-insert-interval.py new file mode 100644 index 000000000..c92a4aa7c --- /dev/null +++ b/python/0057-insert-interval.py @@ -0,0 +1,19 @@ +class Solution: + def insert( + self, intervals: List[List[int]], newInterval: List[int] + ) -> List[List[int]]: + res = [] + + for i in range(len(intervals)): + if newInterval[1] < intervals[i][0]: + res.append(newInterval) + return res + intervals[i:] + elif newInterval[0] > intervals[i][1]: + res.append(intervals[i]) + else: + newInterval = [ + min(newInterval[0], intervals[i][0]), + max(newInterval[1], intervals[i][1]), + ] + res.append(newInterval) + return res diff --git a/python/0058-length-of-last-word.py b/python/0058-length-of-last-word.py new file mode 100644 index 000000000..238117de4 --- /dev/null +++ b/python/0058-length-of-last-word.py @@ -0,0 +1,15 @@ +class Solution: + def lengthOfLastWord(self, s: str) -> int: + """ + one shortcut + """ + # return len(s.split()[-1]) + count = 0 + for i in range(len(s) - 1, -1, -1): + char = s[i] + if char == " ": + if count >= 1: + return count + else: + count += 1 + return count diff --git a/python/0061-rotate-list.py b/python/0061-rotate-list.py new file mode 100644 index 000000000..0640e88ea --- /dev/null +++ b/python/0061-rotate-list.py @@ -0,0 +1,26 @@ +class Solution: + def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]: + if not head or not head.next or k == 0: + return head + + old_head = head + + curr, size = head, 0 + while curr: + curr, size = curr.next, size + 1 + + if k % size == 0: + return head + + k %= size + slow = fast = head + while fast and fast.next: + if k <= 0: + slow = slow.next + fast = fast.next + k -= 1 + + new_tail, new_head, old_tail = slow, slow.next, fast + new_tail.next, old_tail.next = None, old_head + + return new_head diff --git a/python/0062-unique-paths.py b/python/0062-unique-paths.py new file mode 100644 index 000000000..c5c418de6 --- /dev/null +++ b/python/0062-unique-paths.py @@ -0,0 +1,12 @@ +class Solution: + def uniquePaths(self, m: int, n: int) -> int: + row = [1] * n + + for i in range(m - 1): + newRow = [1] * n + for j in range(n - 2, -1, -1): + newRow[j] = newRow[j + 1] + row[j] + row = newRow + return row[0] + + # O(n * m) O(n) diff --git a/python/0063-unique-paths-ii.py b/python/0063-unique-paths-ii.py new file mode 100644 index 000000000..4cd61cd48 --- /dev/null +++ b/python/0063-unique-paths-ii.py @@ -0,0 +1,28 @@ +class Solution: + def uniquePathsWithObstacles(self, grid: List[List[int]]) -> int: + M, N = len(grid), len(grid[0]) + dp = [0] * N + dp[N-1] = 1 + + # Time: O(N*M), Space: O(N) + for r in reversed(range(M)): + for c in reversed(range(N)): + if grid[r][c]: + dp[c] = 0 + elif c + 1 < N: + dp[c] = dp[c] + dp[c + 1] + return dp[0] + + + # Time: O(N*M), Space: O(N*M) + M, N = len(grid), len(grid[0]) + dp = {(M - 1, N - 1): 1} + + def dfs(r, c): + if r == M or c == N or grid[r][c]: + return 0 + if (r, c) in dp: + return dp[(r, c)] + dp[(r, c)] = dfs(r + 1, c) + dfs(r, c + 1) + return dp[(r, c)] + return dfs(0, 0) diff --git a/python/0064-minimum-path-sum.py b/python/0064-minimum-path-sum.py new file mode 100644 index 000000000..1df281e26 --- /dev/null +++ b/python/0064-minimum-path-sum.py @@ -0,0 +1,15 @@ +class Solution: + def minPathSum(self, grid: List[List[int]]) -> int: + m, n = len(grid), len(grid[0]) + prev = [float("inf")] * n + prev[-1] = 0 + + for row in range(m - 1, -1, -1): + dp = [float("inf")] * n + for col in range(n - 1, -1, -1): + if col < n - 1: + dp[col] = min(dp[col], dp[col + 1]) + dp[col] = min(dp[col], prev[col]) + grid[row][col] + prev = dp + + return prev[0] diff --git a/python/0066-plus-one.py b/python/0066-plus-one.py new file mode 100644 index 000000000..6287d6c9c --- /dev/null +++ b/python/0066-plus-one.py @@ -0,0 +1,18 @@ +class Solution: + def plusOne(self, digits: List[int]) -> List[int]: + one = 1 + i = 0 + digits = digits[::-1] + + while one: + if i < len(digits): + if digits[i] == 9: + digits[i] = 0 + else: + digits[i] += 1 + one = 0 + else: + digits.append(one) + one = 0 + i += 1 + return digits[::-1] diff --git a/python/0067-add-binary.py b/python/0067-add-binary.py new file mode 100644 index 000000000..c8b4b2e3f --- /dev/null +++ b/python/0067-add-binary.py @@ -0,0 +1,19 @@ +class Solution: + def addBinary(self, a: str, b: str) -> str: + res = "" + carry = 0 + + a, b = a[::-1], b[::-1] + for i in range(max(len(a), len(b))): + bitA = ord(a[i]) - ord('0') if i < len(a) else 0 + bitB = ord(b[i]) - ord('0') if i < len(b) else 0 + + total = bitA + bitB + carry + char = str(total % 2) + res = char + res + carry = total // 2 + + if carry: + res = "1" + res + + return res diff --git a/python/0068-text-justification.py b/python/0068-text-justification.py new file mode 100644 index 000000000..fcc8a1760 --- /dev/null +++ b/python/0068-text-justification.py @@ -0,0 +1,33 @@ +class Solution: + def fullJustify(self, words: List[str], maxWidth: int) -> List[str]: + res = [] + line = [] # Words in current line + length = 0 # Current line length + i = 0 + while i < len(words): + if length + len(line) + len(words[i]) > maxWidth: + # Line complete + extra_space = maxWidth - length + word_cnt = len(line) - 1 + spaces = extra_space // max(1, word_cnt) + remainder = extra_space % max(1, word_cnt) + + for j in range(max(1, len(line) - 1)): + line[j] += " " * spaces + if remainder: + line[j] += " " + remainder -= 1 + + res.append("".join(line)) + line, length = [], 0 # Reset line and length + + line.append(words[i]) + length += len(words[i]) + i += 1 + + # Handling the last line + last_line = " ".join(line) + trail_spaces = maxWidth - len(last_line) + res.append(last_line + (trail_spaces * " ")) + + return res diff --git a/python/0069-sqrtx.py b/python/0069-sqrtx.py new file mode 100644 index 000000000..72db519ab --- /dev/null +++ b/python/0069-sqrtx.py @@ -0,0 +1,12 @@ +class Solution: + def mySqrt(self, x: int) -> int: + l, r = 0, x + while l <= r: + mid = (l + r) // 2 + if mid * mid == x: + return mid + if mid * mid < x: + l = mid + 1 + else: + r = mid - 1 + return r diff --git a/python/0070-climbing-stairs.py b/python/0070-climbing-stairs.py new file mode 100644 index 000000000..e41decaba --- /dev/null +++ b/python/0070-climbing-stairs.py @@ -0,0 +1,11 @@ +class Solution: + def climbStairs(self, n: int) -> int: + if n <= 3: + return n + n1, n2 = 2, 3 + + for i in range(4, n + 1): + temp = n1 + n2 + n1 = n2 + n2 = temp + return n2 diff --git a/python/0071-simplify-path.py b/python/0071-simplify-path.py new file mode 100644 index 000000000..43fe1633d --- /dev/null +++ b/python/0071-simplify-path.py @@ -0,0 +1,19 @@ +class Solution: + def simplifyPath(self, path: str) -> str: + + stack = [] + + for i in path.split("/"): + # if i == "/" or i == '//', it becomes '' (empty string) + + if i == "..": + if stack: + stack.pop() + elif i == "." or i == '': + # skip "." or an empty string + continue + else: + stack.append(i) + + res = "/" + "/".join(stack) + return res diff --git a/python/0072-edit-distance.py b/python/0072-edit-distance.py new file mode 100644 index 000000000..68c05d699 --- /dev/null +++ b/python/0072-edit-distance.py @@ -0,0 +1,16 @@ +class Solution: + def minDistance(self, word1: str, word2: str) -> int: + dp = [[float("inf")] * (len(word2) + 1) for i in range(len(word1) + 1)] + + for j in range(len(word2) + 1): + dp[len(word1)][j] = len(word2) - j + for i in range(len(word1) + 1): + dp[i][len(word2)] = len(word1) - i + + for i in range(len(word1) - 1, -1, -1): + for j in range(len(word2) - 1, -1, -1): + if word1[i] == word2[j]: + dp[i][j] = dp[i + 1][j + 1] + else: + dp[i][j] = 1 + min(dp[i + 1][j], dp[i][j + 1], dp[i + 1][j + 1]) + return dp[0][0] diff --git a/python/0073-set-matrix-zeroes.py b/python/0073-set-matrix-zeroes.py new file mode 100644 index 000000000..d994fb706 --- /dev/null +++ b/python/0073-set-matrix-zeroes.py @@ -0,0 +1,28 @@ +class Solution: + def setZeroes(self, matrix: List[List[int]]) -> None: + # O(1) + ROWS, COLS = len(matrix), len(matrix[0]) + rowZero = False + + # determine which rows/cols need to be zero + for r in range(ROWS): + for c in range(COLS): + if matrix[r][c] == 0: + matrix[0][c] = 0 + if r > 0: + matrix[r][0] = 0 + else: + rowZero = True + + for r in range(1, ROWS): + for c in range(1, COLS): + if matrix[0][c] == 0 or matrix[r][0] == 0: + matrix[r][c] = 0 + + if matrix[0][0] == 0: + for r in range(ROWS): + matrix[r][0] = 0 + + if rowZero: + for c in range(COLS): + matrix[0][c] = 0 diff --git a/python/0074-search-a-2d-matrix.py b/python/0074-search-a-2d-matrix.py new file mode 100644 index 000000000..8f986dc25 --- /dev/null +++ b/python/0074-search-a-2d-matrix.py @@ -0,0 +1,27 @@ +class Solution: + def searchMatrix(self, matrix: List[List[int]], target: int) -> bool: + ROWS, COLS = len(matrix), len(matrix[0]) + + top, bot = 0, ROWS - 1 + while top <= bot: + row = (top + bot) // 2 + if target > matrix[row][-1]: + top = row + 1 + elif target < matrix[row][0]: + bot = row - 1 + else: + break + + if not (top <= bot): + return False + row = (top + bot) // 2 + l, r = 0, COLS - 1 + while l <= r: + m = (l + r) // 2 + if target > matrix[row][m]: + l = m + 1 + elif target < matrix[row][m]: + r = m - 1 + else: + return True + return False diff --git a/python/0075-sort-colors.py b/python/0075-sort-colors.py new file mode 100644 index 000000000..63a78ef67 --- /dev/null +++ b/python/0075-sort-colors.py @@ -0,0 +1,17 @@ +class Solution: + def sortColors(self, nums: List[int]) -> None: + low = 0 + high = len(nums) - 1 + mid = 0 + + while mid <= high: + if nums[mid] == 0: + nums[low], nums[mid] = nums[mid], nums[low] + low += 1 + mid += 1 + elif nums[mid] == 1: + mid +=1 + else: + nums[mid], nums[high] = nums[high], nums[mid] + high -= 1 + return nums diff --git a/python/0076-minimum-window-substring.py b/python/0076-minimum-window-substring.py new file mode 100644 index 000000000..6cd604ac6 --- /dev/null +++ b/python/0076-minimum-window-substring.py @@ -0,0 +1,31 @@ +class Solution: + def minWindow(self, s: str, t: str) -> str: + if len(s) < len(t): + return "" + + countT, window = {}, {} + for c in t: + countT[c] = 1 + countT.get(c, 0) + + have, need = 0, len(countT) + res, resLen = [-1, -1], float("infinity") + l = 0 + for r in range(len(s)): + c = s[r] + window[c] = 1 + window.get(c, 0) + + if c in countT and window[c] == countT[c]: + have += 1 + + while have == need: + # update our result + if (r - l + 1) < resLen: + res = [l, r] + resLen = r - l + 1 + # pop from the left of our window + window[s[l]] -= 1 + if s[l] in countT and window[s[l]] < countT[s[l]]: + have -= 1 + l += 1 + l, r = res + return s[l : r + 1] if resLen != float("infinity") else "" diff --git a/python/0077-combinations.py b/python/0077-combinations.py new file mode 100644 index 000000000..a8b81334a --- /dev/null +++ b/python/0077-combinations.py @@ -0,0 +1,13 @@ +class Solution: + def combine(self, n: int, k: int) -> List[List[int]]: + res = [] + def helper(start, comb): + if len(comb) == k: + res.append(comb.copy()) + return + for i in range(start, n+1): + comb.append(i) + helper(i+1, comb) + comb.pop() + helper(1, []) + return res diff --git a/python/0078-subsets.py b/python/0078-subsets.py new file mode 100644 index 000000000..f328dfd85 --- /dev/null +++ b/python/0078-subsets.py @@ -0,0 +1,19 @@ +class Solution: + def subsets(self, nums: List[int]) -> List[List[int]]: + res = [] + + subset = [] + + def dfs(i): + if i >= len(nums): + res.append(subset.copy()) + return + # decision to include nums[i] + subset.append(nums[i]) + dfs(i + 1) + # decision NOT to include nums[i] + subset.pop() + dfs(i + 1) + + dfs(0) + return res diff --git a/python/0079-word-search.py b/python/0079-word-search.py new file mode 100644 index 000000000..a08804808 --- /dev/null +++ b/python/0079-word-search.py @@ -0,0 +1,38 @@ +class Solution: + def exist(self, board: List[List[str]], word: str) -> bool: + ROWS, COLS = len(board), len(board[0]) + path = set() + + def dfs(r, c, i): + if i == len(word): + return True + if ( + min(r, c) < 0 + or r >= ROWS + or c >= COLS + or word[i] != board[r][c] + or (r, c) in path + ): + return False + path.add((r, c)) + res = ( + dfs(r + 1, c, i + 1) + or dfs(r - 1, c, i + 1) + or dfs(r, c + 1, i + 1) + or dfs(r, c - 1, i + 1) + ) + path.remove((r, c)) + return res + + # To prevent TLE,reverse the word if frequency of the first letter is more than the last letter's + count = sum(map(Counter, board), Counter()) + if count[word[0]] > count[word[-1]]: + word = word[::-1] + + for r in range(ROWS): + for c in range(COLS): + if dfs(r, c, 0): + return True + return False + + # O(n * m * 4^n) diff --git a/python/0080-remove-duplicates-from-sorted-array-ii.py b/python/0080-remove-duplicates-from-sorted-array-ii.py new file mode 100644 index 000000000..e2749e627 --- /dev/null +++ b/python/0080-remove-duplicates-from-sorted-array-ii.py @@ -0,0 +1,15 @@ +class Solution: + def removeDuplicates(self, nums: List[int]) -> int: + l, r = 0, 0 + + while r < len(nums): + count = 1 + while r + 1 < len(nums) and nums[r] == nums[r + 1]: + r += 1 + count += 1 + + for i in range(min(2, count)): + nums[l] = nums[r] + l += 1 + r += 1 + return l diff --git a/python/0081-search-in-rotated-sorted-array-ii.py b/python/0081-search-in-rotated-sorted-array-ii.py new file mode 100644 index 000000000..88649beaa --- /dev/null +++ b/python/0081-search-in-rotated-sorted-array-ii.py @@ -0,0 +1,23 @@ +class Solution: + def search(self, nums: List[int], target: int) -> bool: + left,right = 0,len(nums) - 1 + while left <= right: + mid = left + (right - left) // 2 + if nums[mid] == target: + return True + + #Left sorted portion + if nums[left] < nums[mid]: + if nums[left] <= target < nums[mid]: + right = mid - 1 + else: + left = mid + 1 + #Right sorted portion + elif nums[left] > nums[mid]: + if nums[mid] < target <= nums[right]: + left = mid + 1 + else: + right = mid - 1 + else: + left += 1 + return False diff --git a/python/0083-remove-duplicates-from-sorted-list.py b/python/0083-remove-duplicates-from-sorted-list.py new file mode 100644 index 000000000..fdb26d623 --- /dev/null +++ b/python/0083-remove-duplicates-from-sorted-list.py @@ -0,0 +1,8 @@ +class Solution: + def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]: + cur = head + while cur: + while cur.next and cur.next.val == cur.val: + cur.next = cur.next.next + cur = cur.next + return head diff --git a/python/0084-largest-rectangle-in-histogram.py b/python/0084-largest-rectangle-in-histogram.py new file mode 100644 index 000000000..26607c75a --- /dev/null +++ b/python/0084-largest-rectangle-in-histogram.py @@ -0,0 +1,16 @@ +class Solution: + def largestRectangleArea(self, heights: List[int]) -> int: + maxArea = 0 + stack = [] # pair: (index, height) + + for i, h in enumerate(heights): + start = i + while stack and stack[-1][1] > h: + index, height = stack.pop() + maxArea = max(maxArea, height * (i - index)) + start = index + stack.append((start, h)) + + for i, h in stack: + maxArea = max(maxArea, h * (len(heights) - i)) + return maxArea diff --git a/python/0086-partition-list.py b/python/0086-partition-list.py new file mode 100644 index 000000000..76a0d4e0d --- /dev/null +++ b/python/0086-partition-list.py @@ -0,0 +1,18 @@ +class Solution: + def partition(self, head: Optional[ListNode], x: int) -> Optional[ListNode]: + less_head, bigger_head = ListNode(-1), ListNode(-1) + less_prev, bigger_prev = less_head, bigger_head + while head: + if head.val < x: + less_prev.next = head + less_prev = less_prev.next + else: + bigger_prev.next = head + bigger_prev = bigger_prev.next + + head = head.next + + less_prev.next = bigger_prev.next = None + less_prev.next = bigger_head.next + + return less_head.next diff --git a/python/0088-merge-sorted-array.py b/python/0088-merge-sorted-array.py new file mode 100644 index 000000000..2111935bb --- /dev/null +++ b/python/0088-merge-sorted-array.py @@ -0,0 +1,14 @@ +class Solution: + def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None: + """ + Do not return anything, modify nums1 in-place instead. + """ + while m > 0 and n > 0: + if nums1[m-1] >= nums2[n-1]: + nums1[m+n-1] = nums1[m-1] + m -= 1 + else: + nums1[m+n-1] = nums2[n-1] + n -= 1 + if n > 0: + nums1[:n] = nums2[:n] \ No newline at end of file diff --git a/python/0090-subsets-ii.py b/python/0090-subsets-ii.py new file mode 100644 index 000000000..0e0d95626 --- /dev/null +++ b/python/0090-subsets-ii.py @@ -0,0 +1,21 @@ +class Solution: + def subsetsWithDup(self, nums: List[int]) -> List[List[int]]: + res = [] + nums.sort() + + def backtrack(i, subset): + if i == len(nums): + res.append(subset[::]) + return + + # All subsets that include nums[i] + subset.append(nums[i]) + backtrack(i + 1, subset) + subset.pop() + # All subsets that don't include nums[i] + while i + 1 < len(nums) and nums[i] == nums[i + 1]: + i += 1 + backtrack(i + 1, subset) + + backtrack(0, []) + return res diff --git a/python/0091-decode-ways.py b/python/0091-decode-ways.py new file mode 100644 index 000000000..f5b4b5ada --- /dev/null +++ b/python/0091-decode-ways.py @@ -0,0 +1,34 @@ +class Solution: + def numDecodings(self, s: str) -> int: + # Memoization + dp = {len(s): 1} + + def dfs(i): + if i in dp: + return dp[i] + if s[i] == "0": + return 0 + + res = dfs(i + 1) + if i + 1 < len(s) and ( + s[i] == "1" or s[i] == "2" and s[i + 1] in "0123456" + ): + res += dfs(i + 2) + dp[i] = res + return res + + return dfs(0) + + # Dynamic Programming + dp = {len(s): 1} + for i in range(len(s) - 1, -1, -1): + if s[i] == "0": + dp[i] = 0 + else: + dp[i] = dp[i + 1] + + if i + 1 < len(s) and ( + s[i] == "1" or s[i] == "2" and s[i + 1] in "0123456" + ): + dp[i] += dp[i + 2] + return dp[0] diff --git a/python/0092-reverse-linked-list-ii.py b/python/0092-reverse-linked-list-ii.py new file mode 100644 index 000000000..39a5273f4 --- /dev/null +++ b/python/0092-reverse-linked-list-ii.py @@ -0,0 +1,23 @@ +class Solution: + def reverseBetween( + self, head: Optional[ListNode], left: int, right: int + ) -> Optional[ListNode]: + dummy = ListNode(0, head) + + # 1) reach node at position "left" + leftPrev, cur = dummy, head + for i in range(left - 1): + leftPrev, cur = cur, cur.next + + # Now cur="left", leftPrev="node before left" + # 2) reverse from left to right + prev = None + for i in range(right - left + 1): + tmpNext = cur.next + cur.next = prev + prev, cur = cur, tmpNext + + # 3) Update pointers + leftPrev.next.next = cur # cur is node after "right" + leftPrev.next = prev # prev is "right" + return dummy.next diff --git a/python/0094-binary-tree-inorder-traversal.py b/python/0094-binary-tree-inorder-traversal.py new file mode 100644 index 000000000..cb184a758 --- /dev/null +++ b/python/0094-binary-tree-inorder-traversal.py @@ -0,0 +1,27 @@ +class Solution: + def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]: + # Iterative + res, stack = [], [] + cur = root + + while cur or stack: + while cur: + stack.append(cur) + cur = cur.left + cur = stack.pop() + res.append(cur.val) + cur = cur.right + return res + + # Recursive + res = [] + + def helper(root): + if not root: + return + helper(root.left) + res.append(root.val) + helper(root.right) + + helper(root) + return res diff --git a/python/0097-interleaving-string.py b/python/0097-interleaving-string.py new file mode 100644 index 000000000..efb43ac60 --- /dev/null +++ b/python/0097-interleaving-string.py @@ -0,0 +1,15 @@ +class Solution: + def isInterleave(self, s1: str, s2: str, s3: str) -> bool: + if len(s1) + len(s2) != len(s3): + return False + + dp = [[False] * (len(s2) + 1) for i in range(len(s1) + 1)] + dp[len(s1)][len(s2)] = True + + for i in range(len(s1), -1, -1): + for j in range(len(s2), -1, -1): + if i < len(s1) and s1[i] == s3[i + j] and dp[i + 1][j]: + dp[i][j] = True + if j < len(s2) and s2[j] == s3[i + j] and dp[i][j + 1]: + dp[i][j] = True + return dp[0][0] diff --git a/python/0098-validate-binary-search-tree.py b/python/0098-validate-binary-search-tree.py new file mode 100644 index 000000000..f1293dc55 --- /dev/null +++ b/python/0098-validate-binary-search-tree.py @@ -0,0 +1,19 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def isValidBST(self, root: TreeNode) -> bool: + def valid(node, left, right): + if not node: + return True + if not (left < node.val < right): + return False + + return valid(node.left, left, node.val) and valid( + node.right, node.val, right + ) + + return valid(root, float("-inf"), float("inf")) diff --git a/python/0100-same-tree.py b/python/0100-same-tree.py new file mode 100644 index 000000000..29957cf4f --- /dev/null +++ b/python/0100-same-tree.py @@ -0,0 +1,16 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + + +class Solution: + def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool: + if not p and not q: + return True + if p and q and p.val == q.val: + return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right) + else: + return False diff --git a/python/0101-symmetric-tree.py b/python/0101-symmetric-tree.py new file mode 100644 index 000000000..c44dc1127 --- /dev/null +++ b/python/0101-symmetric-tree.py @@ -0,0 +1,31 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def isSymmetric(self, root: Optional[TreeNode]) -> bool: + if not root.left and not root.right: + return True + queueLeft = deque() + queueRight = deque() + + queueLeft.appendleft(root.left) + queueRight.appendleft(root.right) + + while queueLeft and queueRight: + nodeLeft, nodeRight = queueLeft.pop(), queueRight.pop() + if not nodeLeft and not nodeRight: + continue + # both node must exist + # if exists thet must have the same value + if not nodeLeft or not nodeRight or nodeLeft.val != nodeRight.val: + return False + + queueLeft.appendleft(nodeLeft.left) + queueLeft.appendleft(nodeLeft.right) + + queueRight.appendleft(nodeRight.right) + queueRight.appendleft(nodeRight.left) + return not (queueLeft or queueRight) diff --git a/python/0102-binary-tree-level-order-traversal.py b/python/0102-binary-tree-level-order-traversal.py new file mode 100644 index 000000000..6ca9a1166 --- /dev/null +++ b/python/0102-binary-tree-level-order-traversal.py @@ -0,0 +1,27 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + + +class Solution: + def levelOrder(self, root: TreeNode) -> List[List[int]]: + res = [] + q = collections.deque() + if root: + q.append(root) + + while q: + val = [] + + for i in range(len(q)): + node = q.popleft() + val.append(node.val) + if node.left: + q.append(node.left) + if node.right: + q.append(node.right) + res.append(val) + return res diff --git a/python/0103-binary-tree-zigzag-level-order-traversal.py b/python/0103-binary-tree-zigzag-level-order-traversal.py new file mode 100644 index 000000000..d7eac087e --- /dev/null +++ b/python/0103-binary-tree-zigzag-level-order-traversal.py @@ -0,0 +1,24 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]: + if root is None: + return + result, zigzagDirection = [], 1 + q = [root] + while q: + level, queueLength = [], len(q) + for i in range(queueLength): + node = q.pop(0) + level.append(node.val) + if node.left: + q.append(node.left) + if node.right: + q.append(node.right) + result.append(level[::zigzagDirection]) + zigzagDirection *= -1 + return result diff --git a/python/0104-maximum-depth-of-binary-tree.py b/python/0104-maximum-depth-of-binary-tree.py new file mode 100644 index 000000000..f78fbdfde --- /dev/null +++ b/python/0104-maximum-depth-of-binary-tree.py @@ -0,0 +1,44 @@ +# RECURSIVE DFS +class Solution: + def maxDepth(self, root: TreeNode) -> int: + if not root: + return 0 + + return 1 + max(self.maxDepth(root.left), self.maxDepth(root.right)) + + +# ITERATIVE DFS +class Solution: + def maxDepth(self, root: TreeNode) -> int: + stack = [[root, 1]] + res = 0 + + while stack: + node, depth = stack.pop() + + if node: + res = max(res, depth) + stack.append([node.left, depth + 1]) + stack.append([node.right, depth + 1]) + return res + + +# BFS +class Solution: + def maxDepth(self, root: TreeNode) -> int: + q = deque() + if root: + q.append(root) + + level = 0 + + while q: + + for i in range(len(q)): + node = q.popleft() + if node.left: + q.append(node.left) + if node.right: + q.append(node.right) + level += 1 + return level diff --git a/python/0105-construct-binary-tree-from-preorder-and-inorder-traversal.py b/python/0105-construct-binary-tree-from-preorder-and-inorder-traversal.py new file mode 100644 index 000000000..5bb113c17 --- /dev/null +++ b/python/0105-construct-binary-tree-from-preorder-and-inorder-traversal.py @@ -0,0 +1,10 @@ +class Solution: + def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]: + if not preorder or not inorder: + return None + + root = TreeNode(preorder[0]) + mid = inorder.index(preorder[0]) + root.left = self.buildTree(preorder[1 : mid + 1], inorder[:mid]) + root.right = self.buildTree(preorder[mid + 1 :], inorder[mid + 1 :]) + return root diff --git a/python/0106-construct-binary-tree-from-inorder-and-postorder-traversal.py b/python/0106-construct-binary-tree-from-inorder-and-postorder-traversal.py new file mode 100644 index 000000000..b3857ed36 --- /dev/null +++ b/python/0106-construct-binary-tree-from-inorder-and-postorder-traversal.py @@ -0,0 +1,25 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]: + def buildTreeHelper(left, right): + if left > right: + return None + + rootVal = postorder.pop() + rootNode = TreeNode(rootVal) + + idx = inorderIndexMap[rootVal] + rootNode.right = buildTreeHelper(idx + 1, right) + rootNode.left = buildTreeHelper(left, idx - 1) + return rootNode + + inorderIndexMap = {} + for (i, val) in enumerate(inorder): + inorderIndexMap[val] = i + + return buildTreeHelper(0, len(postorder) - 1) diff --git a/python/0108-convert-sorted-array-to-binary-search-tree.py b/python/0108-convert-sorted-array-to-binary-search-tree.py new file mode 100644 index 000000000..5ed126cf6 --- /dev/null +++ b/python/0108-convert-sorted-array-to-binary-search-tree.py @@ -0,0 +1,15 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]: + if not nums: + return None + mid = len(nums)//2 + root = TreeNode(nums[mid]) + root.left = self.sortedArrayToBST(nums[:mid]) + root.right = self.sortedArrayToBST(nums[mid+1:]) + return root diff --git a/python/0110-balanced-binary-tree.py b/python/0110-balanced-binary-tree.py new file mode 100644 index 000000000..1cc6ec693 --- /dev/null +++ b/python/0110-balanced-binary-tree.py @@ -0,0 +1,17 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def isBalanced(self, root: Optional[TreeNode]) -> bool: + def dfs(root): + if not root: + return [True, 0] + + left, right = dfs(root.left), dfs(root.right) + balanced = left[0] and right[0] and abs(left[1] - right[1]) <= 1 + return [balanced, 1 + max(left[1], right[1])] + + return dfs(root)[0] diff --git a/python/0112-path-sum.py b/python/0112-path-sum.py new file mode 100644 index 000000000..b6d4db31c --- /dev/null +++ b/python/0112-path-sum.py @@ -0,0 +1,25 @@ +# Recursive Solution +class Solution: + def hasPathSum(self, root, sum): + if not root: + return False + sum -= root.val + if not root.left and not root.right: + return sum == 0 + return self.hasPathSum(root.left, sum) or self.hasPathSum(root.right, sum) + +# Iterative Solution +class Solution: + def hasPathSum(self, root, sum): + de = [ + (root, sum - root.val), + ] + while de: + node, curr_sum = de.pop() + if not node.left and not node.right and curr_sum == 0: + return True + if node.right: + de.append((node.right, curr_sum - node.right.val)) + if node.left: + de.append((node.left, curr_sum - node.left.val)) + return False \ No newline at end of file diff --git a/python/0115-distinct-subsequences.py b/python/0115-distinct-subsequences.py new file mode 100644 index 000000000..8f768fbfe --- /dev/null +++ b/python/0115-distinct-subsequences.py @@ -0,0 +1,16 @@ +class Solution: + def numDistinct(self, s: str, t: str) -> int: + cache = {} + + for i in range(len(s) + 1): + cache[(i, len(t))] = 1 + for j in range(len(t)): + cache[(len(s), j)] = 0 + + for i in range(len(s) - 1, -1, -1): + for j in range(len(t) - 1, -1, -1): + if s[i] == t[j]: + cache[(i, j)] = cache[(i + 1, j + 1)] + cache[(i + 1, j)] + else: + cache[(i, j)] = cache[(i + 1, j)] + return cache[(0, 0)] diff --git a/python/0118-pascals-triangle.py b/python/0118-pascals-triangle.py new file mode 100644 index 000000000..e7f9e7785 --- /dev/null +++ b/python/0118-pascals-triangle.py @@ -0,0 +1,17 @@ +class Solution: + def generate(self, rowIndex) -> List[List[int]]: + if rowIndex == 0: + return [[1]] + else: + return self.getAllRow(rowIndex - 1) + + def getAllRow(self, rowIndex): + if rowIndex == 0: + return [[1]] + ListPrec = self.getAllRow(rowIndex - 1) + Len = len(ListPrec[-1]) + ListPrec.append([1]) + for i in range(0, Len - 1): + ListPrec[-1].append(ListPrec[-2][i] + ListPrec[-2][i + 1]) + ListPrec[-1].append(1) + return ListPrec diff --git a/python/0119-pascal-triangle-ii.py b/python/0119-pascal-triangle-ii.py new file mode 100644 index 000000000..1237f592b --- /dev/null +++ b/python/0119-pascal-triangle-ii.py @@ -0,0 +1,15 @@ +class Solution: + Memo = {} + + def getRow(self, rowIndex: int) -> List[int]: + if rowIndex in self.Memo: + return self.Memo[rowIndex] + if rowIndex == 0: + return [1] + ListPrec = self.getRow(rowIndex - 1) + Result = [1] + for i in range(0, len(ListPrec) - 1): + Result.append(ListPrec[i] + ListPrec[i + 1]) + Result.append(1) + self.Memo[rowIndex] = Result + return Result diff --git a/python/0120-triangle.py b/python/0120-triangle.py new file mode 100644 index 000000000..c96b199ee --- /dev/null +++ b/python/0120-triangle.py @@ -0,0 +1,9 @@ +class Solution: + def minimumTotal(self, triangle: List[List[int]]) -> int: + dp = triangle[-1] + + for row in range(len(triangle) - 2, -1, -1): + for col in range(0, row + 1): + dp[col] = triangle[row][col] + min(dp[col], dp[col + 1]) + + return dp[0] diff --git a/python/0121-best-time-to-buy-and-sell-stock.py b/python/0121-best-time-to-buy-and-sell-stock.py new file mode 100644 index 000000000..401e39c55 --- /dev/null +++ b/python/0121-best-time-to-buy-and-sell-stock.py @@ -0,0 +1,10 @@ +class Solution: + def maxProfit(self, prices: List[int]) -> int: + res = 0 + + lowest = prices[0] + for price in prices: + if price < lowest: + lowest = price + res = max(res, price - lowest) + return res diff --git a/python/0122-best-time-to-buy-and-sell-stock-ii.py b/python/0122-best-time-to-buy-and-sell-stock-ii.py new file mode 100644 index 000000000..71354443c --- /dev/null +++ b/python/0122-best-time-to-buy-and-sell-stock-ii.py @@ -0,0 +1,7 @@ +class Solution: + def maxProfit(self, prices: List[int]) -> int: + max_profit = 0 + for i in range(1, len(prices)): + if prices[i] > prices[i-1]: + max_profit += prices[i] - prices[i-1] + return max_profit diff --git a/python/0124-binary-tree-maximum-path-sum.py b/python/0124-binary-tree-maximum-path-sum.py new file mode 100644 index 000000000..b94df1d7e --- /dev/null +++ b/python/0124-binary-tree-maximum-path-sum.py @@ -0,0 +1,26 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def maxPathSum(self, root: TreeNode) -> int: + res = [root.val] + + # return max path sum without split + def dfs(root): + if not root: + return 0 + + leftMax = dfs(root.left) + rightMax = dfs(root.right) + leftMax = max(leftMax, 0) + rightMax = max(rightMax, 0) + + # compute max path sum WITH split + res[0] = max(res[0], root.val + leftMax + rightMax) + return root.val + max(leftMax, rightMax) + + dfs(root) + return res[0] diff --git a/python/0125-valid-palindrome.py b/python/0125-valid-palindrome.py new file mode 100644 index 000000000..e5f55dac2 --- /dev/null +++ b/python/0125-valid-palindrome.py @@ -0,0 +1,7 @@ +class Solution: + def isPalindrome(self, s: str) -> bool: + new = '' + for a in s: + if a.isalpha() or a.isdigit(): + new += a.lower() + return (new == new[::-1]) diff --git a/python/0127-word-ladder.py b/python/0127-word-ladder.py new file mode 100644 index 000000000..11da720d5 --- /dev/null +++ b/python/0127-word-ladder.py @@ -0,0 +1,28 @@ +class Solution: + def ladderLength(self, beginWord: str, endWord: str, wordList: List[str]) -> int: + if endWord not in wordList: + return 0 + + nei = collections.defaultdict(list) + wordList.append(beginWord) + for word in wordList: + for j in range(len(word)): + pattern = word[:j] + "*" + word[j + 1 :] + nei[pattern].append(word) + + visit = set([beginWord]) + q = deque([beginWord]) + res = 1 + while q: + for i in range(len(q)): + word = q.popleft() + if word == endWord: + return res + for j in range(len(word)): + pattern = word[:j] + "*" + word[j + 1 :] + for neiWord in nei[pattern]: + if neiWord not in visit: + visit.add(neiWord) + q.append(neiWord) + res += 1 + return 0 diff --git a/python/0128-longest-consecutive-sequence.py b/python/0128-longest-consecutive-sequence.py new file mode 100644 index 000000000..11c63a8e8 --- /dev/null +++ b/python/0128-longest-consecutive-sequence.py @@ -0,0 +1,13 @@ +class Solution: + def longestConsecutive(self, nums: List[int]) -> int: + numSet = set(nums) + longest = 0 + + for n in numSet: + # check if its the start of a sequence + if (n - 1) not in numSet: + length = 1 + while (n + length) in numSet: + length += 1 + longest = max(length, longest) + return longest diff --git a/python/0130-surrounded-regions.py b/python/0130-surrounded-regions.py new file mode 100644 index 000000000..de22dfd65 --- /dev/null +++ b/python/0130-surrounded-regions.py @@ -0,0 +1,54 @@ +class Solution: + def solve(self, board: List[List[str]]) -> None: + rows, cols = len(board), len(board[0]) + flag = set() + + def dfs(r, c): + if not(r in range(rows) and c in range(cols)) or board[r][c] != 'O' or (r, c) in flag: + return + flag.add((r, c)) + return (dfs(r + 1, c), dfs(r - 1, c), dfs(r, c + 1), dfs(r, c - 1)) + + # traverse through the board + for r in range(rows): + for c in range(cols): + if( (r == 0 or c == 0 or r == rows - 1 or c == cols - 1) and board[r][c] == 'O'): + dfs(r, c) + + # set all of the 'X's to 'O's + for r in range(rows): + for c in range(cols): + if board[r][c] == 'O' and (r, c) not in flag: + board[r][c] = 'X' + + ''' + def solve(self, board: List[List[str]]) -> None: + ROWS, COLS = len(board), len(board[0]) + + def capture(r, c): + if r < 0 or c < 0 or r == ROWS or c == COLS or board[r][c] != "O": + return + board[r][c] = "T" + capture(r + 1, c) + capture(r - 1, c) + capture(r, c + 1) + capture(r, c - 1) + + # 1. (DFS) Capture unsurrounded regions (O -> T) + for r in range(ROWS): + for c in range(COLS): + if board[r][c] == "O" and (r in [0, ROWS - 1] or c in [0, COLS - 1]): + capture(r, c) + + # 2. Capture surrounded regions (O -> X) + for r in range(ROWS): + for c in range(COLS): + if board[r][c] == "O": + board[r][c] = "X" + + # 3. Uncapture unsurrounded regions (T -> O) + for r in range(ROWS): + for c in range(COLS): + if board[r][c] == "T": + board[r][c] = "O" + ''' diff --git a/python/0131-palindrome-partitioning.py b/python/0131-palindrome-partitioning.py new file mode 100644 index 000000000..f0893bbcb --- /dev/null +++ b/python/0131-palindrome-partitioning.py @@ -0,0 +1,23 @@ +class Solution: + def partition(self, s: str) -> List[List[str]]: + res, part = [], [] + + def dfs(i): + if i >= len(s): + res.append(part.copy()) + return + for j in range(i, len(s)): + if self.isPali(s, i, j): + part.append(s[i : j + 1]) + dfs(j + 1) + part.pop() + + dfs(0) + return res + + def isPali(self, s, l, r): + while l < r: + if s[l] != s[r]: + return False + l, r = l + 1, r - 1 + return True diff --git a/python/0133-clone-graph.py b/python/0133-clone-graph.py new file mode 100644 index 000000000..704c8e8a4 --- /dev/null +++ b/python/0133-clone-graph.py @@ -0,0 +1,15 @@ +class Solution: + def cloneGraph(self, node: "Node") -> "Node": + oldToNew = {} + + def dfs(node): + if node in oldToNew: + return oldToNew[node] + + copy = Node(node.val) + oldToNew[node] = copy + for nei in node.neighbors: + copy.neighbors.append(dfs(nei)) + return copy + + return dfs(node) if node else None diff --git a/python/0134-gas-station.py b/python/0134-gas-station.py new file mode 100644 index 000000000..a33d8e773 --- /dev/null +++ b/python/0134-gas-station.py @@ -0,0 +1,14 @@ +class Solution: + def canCompleteCircuit(self, gas: List[int], cost: List[int]) -> int: + start, end = len(gas) - 1, 0 + total = gas[start] - cost[start] + + while start >= end: + while total < 0 and start >= end: + start -= 1 + total += gas[start] - cost[start] + if start == end: + return start + total += gas[end] - cost[end] + end += 1 + return -1 diff --git a/python/0135-candy.py b/python/0135-candy.py new file mode 100644 index 000000000..1b07c884e --- /dev/null +++ b/python/0135-candy.py @@ -0,0 +1,21 @@ +class Solution: + def candy(self, ratings: List[int]) -> int: + n = len(ratings) + # Initialize with one candy becuase each child must have at least one candy. + candies = [1] * n + + # Iterate from left to right + for i in range(1, n): + # Check if current rating is greater than left neighbor + if ratings[i] > ratings[i - 1]: + # Rating is higher so deserves more candy than left neighbor + candies[i] = candies[i - 1] + 1 + + # Iterate from right to left + for i in range(n - 2, -1, -1): + # Check if current rating is greater than right neighbor + if ratings[i] > ratings[i + 1]: + # Take max to check if the value is already greater than its right neighbor + 1. + candies[i] = max(candies[i], candies[i + 1] + 1) + + return sum(candies) diff --git a/136-Single-Number.py b/python/0136-single-number.py similarity index 100% rename from 136-Single-Number.py rename to python/0136-single-number.py diff --git a/python/0138-copy-list-with-random-pointer.py b/python/0138-copy-list-with-random-pointer.py new file mode 100644 index 000000000..9942e9ede --- /dev/null +++ b/python/0138-copy-list-with-random-pointer.py @@ -0,0 +1,26 @@ +""" +# Definition for a Node. +class Node: + def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None): + self.val = int(x) + self.next = next + self.random = random +""" + + +class Solution: + def copyRandomList(self, head: "Node") -> "Node": + oldToCopy = {None: None} + + cur = head + while cur: + copy = Node(cur.val) + oldToCopy[cur] = copy + cur = cur.next + cur = head + while cur: + copy = oldToCopy[cur] + copy.next = oldToCopy[cur.next] + copy.random = oldToCopy[cur.random] + cur = cur.next + return oldToCopy[head] diff --git a/python/0139-word-break.py b/python/0139-word-break.py new file mode 100644 index 000000000..681107bd2 --- /dev/null +++ b/python/0139-word-break.py @@ -0,0 +1,14 @@ +class Solution: + def wordBreak(self, s: str, wordDict: List[str]) -> bool: + + dp = [False] * (len(s) + 1) + dp[len(s)] = True + + for i in range(len(s) - 1, -1, -1): + for w in wordDict: + if (i + len(w)) <= len(s) and s[i : i + len(w)] == w: + dp[i] = dp[i + len(w)] + if dp[i]: + break + + return dp[0] diff --git a/python/0141-linked-list-cycle.py b/python/0141-linked-list-cycle.py new file mode 100644 index 000000000..1fcb83888 --- /dev/null +++ b/python/0141-linked-list-cycle.py @@ -0,0 +1,17 @@ +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None + + +class Solution: + def hasCycle(self, head: ListNode) -> bool: + slow, fast = head, head + + while fast and fast.next: + slow = slow.next + fast = fast.next.next + if slow == fast: + return True + return False diff --git a/python/0143-reorder-list.py b/python/0143-reorder-list.py new file mode 100644 index 000000000..899ae98a0 --- /dev/null +++ b/python/0143-reorder-list.py @@ -0,0 +1,24 @@ +class Solution: + def reorderList(self, head: ListNode) -> None: + # find middle + slow, fast = head, head.next + while fast and fast.next: + slow = slow.next + fast = fast.next.next + + # reverse second half + second = slow.next + prev = slow.next = None + while second: + tmp = second.next + second.next = prev + prev = second + second = tmp + + # merge two halfs + first, second = head, prev + while second: + tmp1, tmp2 = first.next, second.next + first.next = second + second.next = tmp1 + first, second = tmp1, tmp2 diff --git a/python/0144-binary-tree-preorder-traversal.py b/python/0144-binary-tree-preorder-traversal.py new file mode 100644 index 000000000..53725aec7 --- /dev/null +++ b/python/0144-binary-tree-preorder-traversal.py @@ -0,0 +1,12 @@ +class Solution: + def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]: + cur, stack = root, [] + res = [] + while cur or stack: + if cur: + res.append(cur.val) + stack.append(cur.right) + cur = cur.left + else: + cur = stack.pop() + return res diff --git a/python/0145-binary-tree-postorder-traversal.py b/python/0145-binary-tree-postorder-traversal.py new file mode 100644 index 000000000..d07023877 --- /dev/null +++ b/python/0145-binary-tree-postorder-traversal.py @@ -0,0 +1,19 @@ +class Solution: + def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]: + stack = [root] + visit = [False] + res = [] + + while stack: + cur, v = stack.pop(), visit.pop() + if cur: + if v: + res.append(cur.val) + else: + stack.append(cur) + visit.append(True) + stack.append(cur.right) + visit.append(False) + stack.append(cur.left) + visit.append(False) + return res diff --git a/python/0146-lru-cache.py b/python/0146-lru-cache.py new file mode 100644 index 000000000..b6fb00f1a --- /dev/null +++ b/python/0146-lru-cache.py @@ -0,0 +1,43 @@ +class Node: + def __init__(self, key, val): + self.key, self.val = key, val + self.prev = self.next = None + + +class LRUCache: + def __init__(self, capacity: int): + self.cap = capacity + self.cache = {} # map key to node + + self.left, self.right = Node(0, 0), Node(0, 0) + self.left.next, self.right.prev = self.right, self.left + + # remove node from list + def remove(self, node): + prev, nxt = node.prev, node.next + prev.next, nxt.prev = nxt, prev + + # insert node at right + def insert(self, node): + prev, nxt = self.right.prev, self.right + prev.next = nxt.prev = node + node.next, node.prev = nxt, prev + + def get(self, key: int) -> int: + if key in self.cache: + self.remove(self.cache[key]) + self.insert(self.cache[key]) + return self.cache[key].val + return -1 + + def put(self, key: int, value: int) -> None: + if key in self.cache: + self.remove(self.cache[key]) + self.cache[key] = Node(key, value) + self.insert(self.cache[key]) + + if len(self.cache) > self.cap: + # remove from the list and delete the LRU from hashmap + lru = self.left.next + self.remove(lru) + del self.cache[lru.key] diff --git a/python/0147-insertion-sort-list.py b/python/0147-insertion-sort-list.py new file mode 100644 index 000000000..6ea1654b7 --- /dev/null +++ b/python/0147-insertion-sort-list.py @@ -0,0 +1,15 @@ +class Solution: + def insertionSortList(self, head: Optional[ListNode]) -> Optional[ListNode]: + if not head or not head.next: + return head + + sentinel = ListNode() + curr = head + while curr: + prev = sentinel + while prev.next and curr.val >= prev.next.val: + prev = prev.next + + curr.next, prev.next, curr = prev.next, curr, curr.next + + return sentinel.next diff --git a/python/0148-sort-list.py b/python/0148-sort-list.py new file mode 100644 index 000000000..f1909caf2 --- /dev/null +++ b/python/0148-sort-list.py @@ -0,0 +1,47 @@ +class Solution: + def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]: + if not head or not head.next: + return head + + mid = self.get_mid(head) + left, right = self.sortList(head), self.sortList(mid) + + return self.merge_two_sorted(left, right) + + + def merge_two_sorted(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]: + if not list1: + return list2 + + if not list2: + return list1 + + sentinel = ListNode() + prev = sentinel + while list1 and list2: + if list1.val < list2.val: + prev.next = list1 + list1 = list1.next + else: + prev.next = list2 + list2 = list2.next + prev = prev.next + + if list1: + prev.next = list1 + else: + prev.next = list2 + + return sentinel.next + + + def get_mid(self, head: Optional[ListNode]) -> Optional[ListNode]: + mid_prev = None + while head and head.next: + mid_prev = mid_prev.next if mid_prev else head + head = head.next.next + + mid = mid_prev.next + mid_prev.next = None + + return mid diff --git a/python/0149-max-points-on-a-line.py b/python/0149-max-points-on-a-line.py new file mode 100644 index 000000000..84273c887 --- /dev/null +++ b/python/0149-max-points-on-a-line.py @@ -0,0 +1,19 @@ +class Solution: + def maxPoints(self, points: List[List[int]]) -> int: + # 1. For each pt determine if it lies on the longest line + # 2. Count all pts with same slope + # 3. Update result with max + + res = 1 + for i in range(len(points)): + p1 = points[i] + count = collections.defaultdict(int) + for j in range(i + 1, len(points)): + p2 = points[j] + if p2[0] == p1[0]: + slope = float("inf") + else: + slope = (p2[1] - p1[1]) / (p2[0] - p1[0]) + count[slope] += 1 + res = max(res, count[slope] + 1) + return res diff --git a/python/0150-evaluate-reverse-polish-notation.py b/python/0150-evaluate-reverse-polish-notation.py new file mode 100644 index 000000000..ee23e4228 --- /dev/null +++ b/python/0150-evaluate-reverse-polish-notation.py @@ -0,0 +1,17 @@ +class Solution: + def evalRPN(self, tokens: List[str]) -> int: + stack = [] + for c in tokens: + if c == "+": + stack.append(stack.pop() + stack.pop()) + elif c == "-": + a, b = stack.pop(), stack.pop() + stack.append(b - a) + elif c == "*": + stack.append(stack.pop() * stack.pop()) + elif c == "/": + a, b = stack.pop(), stack.pop() + stack.append(int(float(b) / a)) + else: + stack.append(int(c)) + return stack[0] diff --git a/python/0151-reverse-words-in-a-string.py b/python/0151-reverse-words-in-a-string.py new file mode 100644 index 000000000..81f226110 --- /dev/null +++ b/python/0151-reverse-words-in-a-string.py @@ -0,0 +1,15 @@ +class Solution: + def reverseWords(self, s: str) -> str: + # Remove leading and trailing spaces + s = s.strip() + + # Split the string into words + words = s.split() + + # Reverse the order of words + words = words[::-1] + + # Join the words with a single space + reversed_str = ' '.join(words) + + return reversed_str diff --git a/python/0152-maximum-product-subarray.py b/python/0152-maximum-product-subarray.py new file mode 100644 index 000000000..52d08d3d0 --- /dev/null +++ b/python/0152-maximum-product-subarray.py @@ -0,0 +1,13 @@ +class Solution: + def maxProduct(self, nums: List[int]) -> int: + # O(n)/O(1) : Time/Memory + res = nums[0] + curMin, curMax = 1, 1 + + for n in nums: + + tmp = curMax * n + curMax = max(n * curMax, n * curMin, n) + curMin = min(tmp, n * curMin, n) + res = max(res, curMax) + return res diff --git a/python/0153-find-minimum-in-rotated-sorted-array.py b/python/0153-find-minimum-in-rotated-sorted-array.py new file mode 100644 index 000000000..e0f3e24c3 --- /dev/null +++ b/python/0153-find-minimum-in-rotated-sorted-array.py @@ -0,0 +1,18 @@ +class Solution: + def findMin(self, nums: List[int]) -> int: + start , end = 0, len(nums) - 1 + curr_min = float("inf") + + while start < end : + mid = start + (end - start ) // 2 + curr_min = min(curr_min,nums[mid]) + + # right has the min + if nums[mid] > nums[end]: + start = mid + 1 + + # left has the min + else: + end = mid - 1 + + return min(curr_min,nums[start]) diff --git a/python/0155-min-stack.py b/python/0155-min-stack.py new file mode 100644 index 000000000..798e3ca63 --- /dev/null +++ b/python/0155-min-stack.py @@ -0,0 +1,19 @@ +class MinStack: + def __init__(self): + self.stack = [] + self.minStack = [] + + def push(self, val: int) -> None: + self.stack.append(val) + val = min(val, self.minStack[-1] if self.minStack else val) + self.minStack.append(val) + + def pop(self) -> None: + self.stack.pop() + self.minStack.pop() + + def top(self) -> int: + return self.stack[-1] + + def getMin(self) -> int: + return self.minStack[-1] diff --git a/python/0160-intersection-of-two-linked-lists.py b/python/0160-intersection-of-two-linked-lists.py new file mode 100644 index 000000000..2d4422f20 --- /dev/null +++ b/python/0160-intersection-of-two-linked-lists.py @@ -0,0 +1,16 @@ +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None + + +class Solution: + def getIntersectionNode( + self, headA: ListNode, headB: ListNode + ) -> Optional[ListNode]: + l1, l2 = headA, headB + while l1 != l2: + l1 = l1.next if l1 else headB + l2 = l2.next if l2 else headA + return l1 diff --git a/python/0162-find-peak-element.py b/python/0162-find-peak-element.py new file mode 100644 index 000000000..6b7987def --- /dev/null +++ b/python/0162-find-peak-element.py @@ -0,0 +1,13 @@ +class Solution: + def findPeakElement(self, nums: List[int]) -> int: + l, r = 0, len(nums) - 1 + while l <= r: + mid = (r + l) // 2 + if mid < len(nums) - 1 and nums[mid] < nums[mid+1]: + l = mid + 1 + elif mid > 0 and nums[mid] < nums[mid-1]: + r = mid - 1 + else: + break + return mid + diff --git a/python/0167-two-sum-ii-input-array-is-sorted.py b/python/0167-two-sum-ii-input-array-is-sorted.py new file mode 100644 index 000000000..886210f44 --- /dev/null +++ b/python/0167-two-sum-ii-input-array-is-sorted.py @@ -0,0 +1,13 @@ +class Solution: + def twoSum(self, numbers: List[int], target: int) -> List[int]: + l, r = 0, len(numbers) - 1 + + while l < r: + curSum = numbers[l] + numbers[r] + + if curSum > target: + r -= 1 + elif curSum < target: + l += 1 + else: + return [l + 1, r + 1] diff --git a/python/0168-excel-sheet-column-title.py b/python/0168-excel-sheet-column-title.py new file mode 100644 index 000000000..566e2a515 --- /dev/null +++ b/python/0168-excel-sheet-column-title.py @@ -0,0 +1,10 @@ +class Solution: + def convertToTitle(self, columnNumber: int) -> str: + # Time: O(logn) - Log base 26 of n + res = "" + while columnNumber > 0: + remainder = (columnNumber - 1) % 26 + res += chr(ord('A') + remainder) + columnNumber = (columnNumber - 1) // 26 + + return res[::-1] # reverse output diff --git a/python/0169-majority-element.py b/python/0169-majority-element.py new file mode 100644 index 000000000..94aeea583 --- /dev/null +++ b/python/0169-majority-element.py @@ -0,0 +1,10 @@ +class Solution: + def majorityElement(self, nums: List[int]) -> int: + res, count = 0, 0 + + for n in nums: + if count == 0: + res = n + count += (1 if n == res else -1) + + return res \ No newline at end of file diff --git a/python/0179-largest-number.py b/python/0179-largest-number.py new file mode 100644 index 000000000..6b4cd6b3b --- /dev/null +++ b/python/0179-largest-number.py @@ -0,0 +1,14 @@ +class Solution: + def largestNumber(self, nums: List[int]) -> str: + for i, n in enumerate(nums): + nums[i] = str(n) + + def compare(n1, n2): + if n1 + n2 > n2 + n1: + return -1 + else: + return 1 + + nums = sorted(nums, key = cmp_to_key(compare)) + + return str(int("".join(nums))) diff --git a/python/0187-repeated-dna-sequences.py b/python/0187-repeated-dna-sequences.py new file mode 100644 index 000000000..00882cc67 --- /dev/null +++ b/python/0187-repeated-dna-sequences.py @@ -0,0 +1,10 @@ +class Solution: + def findRepeatedDnaSequences(self, s: str) -> list[str]: + result = set() + previous_sequences = set() + for i in range(len(s) - 9): + current = s[i:i+10] + if current in previous_sequences: + result.add(current) + previous_sequences.add(current) + return list(result) diff --git a/python/0189-rotate-array.py b/python/0189-rotate-array.py new file mode 100644 index 000000000..a2cfda57b --- /dev/null +++ b/python/0189-rotate-array.py @@ -0,0 +1,20 @@ +class Solution: + def rotate(self, nums: List[int], k: int) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + k = k % len(nums) + l, r = 0, len(nums) - 1 + while l < r: + nums[l], nums[r] = nums[r], nums[l] + l, r = l + 1, r - 1 + + l, r = 0, k - 1 + while l < r: + nums[l], nums[r] = nums[r], nums[l] + l, r = l + 1, r - 1 + + l, r = k, len(nums) - 1 + while l < r: + nums[l], nums[r] = nums[r], nums[l] + l, r = l + 1, r - 1 diff --git a/python/0190-reverse-bits.py b/python/0190-reverse-bits.py new file mode 100644 index 000000000..21ef934a1 --- /dev/null +++ b/python/0190-reverse-bits.py @@ -0,0 +1,7 @@ +class Solution: + def reverseBits(self, n: int) -> int: + res = 0 + for i in range(32): + bit = (n >> i) & 1 + res += (bit << (31 - i)) + return res diff --git a/python/0191-number-of-1-bits.py b/python/0191-number-of-1-bits.py new file mode 100644 index 000000000..9a311cfa2 --- /dev/null +++ b/python/0191-number-of-1-bits.py @@ -0,0 +1,7 @@ +class Solution: + def hammingWeight(self, n: int) -> int: + res = 0 + while n: + n &= n - 1 + res += 1 + return res diff --git a/python/0198-house-robber.py b/python/0198-house-robber.py new file mode 100644 index 000000000..512f67aea --- /dev/null +++ b/python/0198-house-robber.py @@ -0,0 +1,9 @@ +class Solution: + def rob(self, nums: List[int]) -> int: + rob1, rob2 = 0, 0 + + for n in nums: + temp = max(n + rob1, rob2) + rob1 = rob2 + rob2 = temp + return rob2 diff --git a/python/0199-binary-tree-right-side-view.py b/python/0199-binary-tree-right-side-view.py new file mode 100644 index 000000000..b027c7519 --- /dev/null +++ b/python/0199-binary-tree-right-side-view.py @@ -0,0 +1,24 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def rightSideView(self, root: TreeNode) -> List[int]: + res = [] + q = collections.deque([root]) + + while q: + rightSide = None + qLen = len(q) + + for i in range(qLen): + node = q.popleft() + if node: + rightSide = node + q.append(node.left) + q.append(node.right) + if rightSide: + res.append(rightSide.val) + return res diff --git a/python/0200-number-of-islands.py b/python/0200-number-of-islands.py new file mode 100644 index 000000000..a9e67dcd3 --- /dev/null +++ b/python/0200-number-of-islands.py @@ -0,0 +1,84 @@ +class Solution: + def numIslands(self, grid: List[List[str]]) -> int: + if not grid or not grid[0]: + return 0 + + islands = 0 + visit = set() + rows, cols = len(grid), len(grid[0]) + + def dfs(r, c): + if ( + r not in range(rows) + or c not in range(cols) + or grid[r][c] == "0" + or (r, c) in visit + ): + return + + visit.add((r, c)) + directions = [[0, 1], [0, -1], [1, 0], [-1, 0]] + for dr, dc in directions: + dfs(r + dr, c + dc) + + for r in range(rows): + for c in range(cols): + if grid[r][c] == "1" and (r, c) not in visit: + islands += 1 + dfs(r, c) + return islands + +# DFS O(1) Space and much less code +class Solution: + def numIslands(self, grid: List[List[str]]) -> int: + rows, cols = len(grid), len(grid[0]) + def dfs(r, c): + if not 0 <= r < len(grid) or not 0 <= c < len(grid[0]) or grid[r][c] == '0': + return 0 + grid[r][c] = '0' + dfs(r + 1, c) + dfs(r - 1, c) + dfs(r, c + 1) + dfs(r, c - 1) + return 1 + count = 0 + for r in range(rows): + for c in range(cols): + count += dfs(r, c) + return count + +# BFS Version From Video +class SolutionBFS: + def numIslands(self, grid: List[List[str]]) -> int: + if not grid: + return 0 + + rows, cols = len(grid), len(grid[0]) + visited = set() + islands = 0 + + def bfs(r, c): + q = deque() + visited.add((r, c)) + q.append((r, c)) + + while q: + row, col = q.popleft() + directions = [[1, 0],[-1, 0],[0, 1],[0, -1]] + + for dr, dc in directions: + r, c = row + dr, col + dc + if (r) in range(rows) and (c) in range(cols) and grid[r][c] == '1' and (r, c) not in visited: + + q.append((r, c )) + visited.add((r, c )) + + for r in range(rows): + for c in range(cols): + + if grid[r][c] == "1" and (r, c) not in visited: + bfs(r, c) + islands += 1 + + return islands + diff --git a/python/0201-bitwise-and-of-numbers-range.py b/python/0201-bitwise-and-of-numbers-range.py new file mode 100644 index 000000000..b30d74964 --- /dev/null +++ b/python/0201-bitwise-and-of-numbers-range.py @@ -0,0 +1,25 @@ +# check difference at each bit (cannot be more than left - right) +class Solution: + def rangeBitwiseAnd(self, left: int, right: int) -> int: + res = 0 + + for i in range(32): + bit = (left >> i) & 1 + if not bit: + continue + + remain = left % (1 << (i + 1)) + diff = (1 << (i + 1)) - remain + if right - left < diff: + res = res | (1 << i) + return res + +# find the longest matching prefix of set bits between left and right +class Solution: + def rangeBitwiseAnd(self, left: int, right: int) -> int: + i = 0 + while left != right: + left = left >> 1 + right = right >> 1 + i += 1 + return left << i diff --git a/python/0202-happy-number.py b/python/0202-happy-number.py new file mode 100644 index 000000000..f220816c9 --- /dev/null +++ b/python/0202-happy-number.py @@ -0,0 +1,17 @@ +class Solution: + def isHappy(self, n: int) -> bool: + slow, fast = n, self.sumSquareDigits(n) + + while slow != fast: + fast = self.sumSquareDigits(fast) + fast = self.sumSquareDigits(fast) + slow = self.sumSquareDigits(slow) + + return True if fast == 1 else False + + def sumSquareDigits(self, n): + output = 0 + while n: + output += (n % 10) ** 2 + n = n // 10 + return output diff --git a/python/0203-remove-linked-list-elements.py b/python/0203-remove-linked-list-elements.py new file mode 100644 index 000000000..aed041f62 --- /dev/null +++ b/python/0203-remove-linked-list-elements.py @@ -0,0 +1,15 @@ +class Solution: + def removeElements(self, head: ListNode, val: int) -> ListNode: + dummy = ListNode(next=head) + prev, curr = dummy, head + + while curr: + nxt = curr.next + + if curr.val == val: + prev.next = nxt + else: + prev = curr + + curr = nxt + return dummy.next diff --git a/python/0205-isomorphic-strings.py b/python/0205-isomorphic-strings.py new file mode 100644 index 000000000..ef3981a08 --- /dev/null +++ b/python/0205-isomorphic-strings.py @@ -0,0 +1,11 @@ +class Solution: + def isIsomorphic(self, s: str, t: str) -> bool: + mapST, mapTS = {}, {} + + for c1, c2 in zip(s, t): + if (c1 in mapST and mapST[c1] != c2) or (c2 in mapTS and mapTS[c2] != c1): + return False + mapST[c1] = c2 + mapTS[c2] = c1 + + return True \ No newline at end of file diff --git a/python/0206-reverse-linked-list.py b/python/0206-reverse-linked-list.py new file mode 100644 index 000000000..f49146d53 --- /dev/null +++ b/python/0206-reverse-linked-list.py @@ -0,0 +1,17 @@ +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, x): +# self.val = x +# self.next = None + + +class Solution: + def reverseList(self, head: ListNode) -> ListNode: + prev, curr = None, head + + while curr: + temp = curr.next + curr.next = prev + prev = curr + curr = temp + return prev diff --git a/python/0207-course-schedule.py b/python/0207-course-schedule.py new file mode 100644 index 000000000..e52ea99e3 --- /dev/null +++ b/python/0207-course-schedule.py @@ -0,0 +1,29 @@ +class Solution: + def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool: + # dfs + preMap = {i: [] for i in range(numCourses)} + + # map each course to : prereq list + for crs, pre in prerequisites: + preMap[crs].append(pre) + + visiting = set() + + def dfs(crs): + if crs in visiting: + return False + if preMap[crs] == []: + return True + + visiting.add(crs) + for pre in preMap[crs]: + if not dfs(pre): + return False + visiting.remove(crs) + preMap[crs] = [] + return True + + for c in range(numCourses): + if not dfs(c): + return False + return True diff --git a/python/0208-implement-trie-prefix-tree.py b/python/0208-implement-trie-prefix-tree.py new file mode 100644 index 000000000..4b81ae701 --- /dev/null +++ b/python/0208-implement-trie-prefix-tree.py @@ -0,0 +1,48 @@ +class TrieNode: + def __init__(self): + self.children = [None] * 26 + self.end = False + + +class Trie: + def __init__(self): + """ + Initialize your data structure here. + """ + self.root = TrieNode() + + def insert(self, word: str) -> None: + """ + Inserts a word into the trie. + """ + curr = self.root + for c in word: + i = ord(c) - ord("a") + if curr.children[i] is None: + curr.children[i] = TrieNode() + curr = curr.children[i] + curr.end = True + + def search(self, word: str) -> bool: + """ + Returns if the word is in the trie. + """ + curr = self.root + for c in word: + i = ord(c) - ord("a") + if curr.children[i] is None: + return False + curr = curr.children[i] + return curr.end + + def startsWith(self, prefix: str) -> bool: + """ + Returns if there is any word in the trie that starts with the given prefix. + """ + curr = self.root + for c in prefix: + i = ord(c) - ord("a") + if curr.children[i] is None: + return False + curr = curr.children[i] + return True diff --git a/python/0209-minimum-size-subarray-sum.py b/python/0209-minimum-size-subarray-sum.py new file mode 100644 index 000000000..97a6ef812 --- /dev/null +++ b/python/0209-minimum-size-subarray-sum.py @@ -0,0 +1,17 @@ +class Solution: + def minSubArrayLen(self, target: int, nums: List[int]) -> int: + res = float('inf') + l, total = 0, 0 + + for r in range(len(nums)): + total += nums[r] + while total >= target: + res = min(res, r - l + 1) + total -= nums[l] + l += 1 + return res if res != float('inf') else 0 + + + + + diff --git a/python/0210-course-schedule-ii.py b/python/0210-course-schedule-ii.py new file mode 100644 index 000000000..8c59b6e1e --- /dev/null +++ b/python/0210-course-schedule-ii.py @@ -0,0 +1,28 @@ +class Solution: + def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -> List[int]: + prereq = {c: [] for c in range(numCourses)} + for crs, pre in prerequisites: + prereq[crs].append(pre) + + output = [] + visit, cycle = set(), set() + + def dfs(crs): + if crs in cycle: + return False + if crs in visit: + return True + + cycle.add(crs) + for pre in prereq[crs]: + if dfs(pre) == False: + return False + cycle.remove(crs) + visit.add(crs) + output.append(crs) + return True + + for c in range(numCourses): + if dfs(c) == False: + return [] + return output diff --git a/python/0211-design-add-and-search-words-data-structure.py b/python/0211-design-add-and-search-words-data-structure.py new file mode 100644 index 000000000..3c5f562aa --- /dev/null +++ b/python/0211-design-add-and-search-words-data-structure.py @@ -0,0 +1,36 @@ +class TrieNode: + def __init__(self): + self.children = {} # a : TrieNode + self.word = False + + +class WordDictionary: + def __init__(self): + self.root = TrieNode() + + def addWord(self, word: str) -> None: + cur = self.root + for c in word: + if c not in cur.children: + cur.children[c] = TrieNode() + cur = cur.children[c] + cur.word = True + + def search(self, word: str) -> bool: + def dfs(j, root): + cur = root + + for i in range(j, len(word)): + c = word[i] + if c == ".": + for child in cur.children.values(): + if dfs(i + 1, child): + return True + return False + else: + if c not in cur.children: + return False + cur = cur.children[c] + return cur.word + + return dfs(0, self.root) diff --git a/python/0212-word-search-ii.py b/python/0212-word-search-ii.py new file mode 100644 index 000000000..631de2528 --- /dev/null +++ b/python/0212-word-search-ii.py @@ -0,0 +1,63 @@ +class TrieNode: + def __init__(self): + self.children = {} + self.isWord = False + self.refs = 0 + + def addWord(self, word): + cur = self + cur.refs += 1 + for c in word: + if c not in cur.children: + cur.children[c] = TrieNode() + cur = cur.children[c] + cur.refs += 1 + cur.isWord = True + + def removeWord(self, word): + cur = self + cur.refs -= 1 + for c in word: + if c in cur.children: + cur = cur.children[c] + cur.refs -= 1 + + +class Solution: + def findWords(self, board: List[List[str]], words: List[str]) -> List[str]: + root = TrieNode() + for w in words: + root.addWord(w) + + ROWS, COLS = len(board), len(board[0]) + res, visit = set(), set() + + def dfs(r, c, node, word): + if ( + r not in range(ROWS) + or c not in range(COLS) + or board[r][c] not in node.children + or node.children[board[r][c]].refs < 1 + or (r, c) in visit + ): + return + + visit.add((r, c)) + node = node.children[board[r][c]] + word += board[r][c] + if node.isWord: + node.isWord = False + res.add(word) + root.removeWord(word) + + dfs(r + 1, c, node, word) + dfs(r - 1, c, node, word) + dfs(r, c + 1, node, word) + dfs(r, c - 1, node, word) + visit.remove((r, c)) + + for r in range(ROWS): + for c in range(COLS): + dfs(r, c, root, "") + + return list(res) diff --git a/python/0213-house-robber-ii.py b/python/0213-house-robber-ii.py new file mode 100644 index 000000000..ad7fdf484 --- /dev/null +++ b/python/0213-house-robber-ii.py @@ -0,0 +1,12 @@ +class Solution: + def rob(self, nums: List[int]) -> int: + return max(nums[0], self.helper(nums[1:]), self.helper(nums[:-1])) + + def helper(self, nums): + rob1, rob2 = 0, 0 + + for n in nums: + newRob = max(rob1 + n, rob2) + rob1 = rob2 + rob2 = newRob + return rob2 diff --git a/python/0215-kth-largest-element-in-an-array.py b/python/0215-kth-largest-element-in-an-array.py new file mode 100644 index 000000000..cbeef51cc --- /dev/null +++ b/python/0215-kth-largest-element-in-an-array.py @@ -0,0 +1,44 @@ +# Solution: Sorting +# Time Complexity: +# - Best Case: O(n*log(k)) +# - Average Case: O(n*log(k)) +# - Worst Case:O(n*log(k)) +# Extra Space Complexity: O(k) +class Solution: + def findKthLargest(self, nums: List[int], k: int) -> int: + heapify(nums) + while len(nums) > k: + heappop(nums) + return nums[0] + +# Solution: Sorting +# Time Complexity: +# - Best Case: O(n) +# - Average Case: O(n*log(n)) +# - Worst Case:O(n*log(n)) +# Extra Space Complexity: O(n) +class Solution1: + def findKthLargest(self, nums: List[int], k: int) -> int: + nums.sort() + return nums[len(nums) - k] + + +# Solution: QuickSelect +# Time Complexity: O(n) +# Extra Space Complexity: O(n) +class Solution2: + def findKthLargest(self, nums: List[int], k: int) -> int: + pivot = random.choice(nums) + left = [num for num in nums if num > pivot] + mid = [num for num in nums if num == pivot] + right = [num for num in nums if num < pivot] + + length_left = len(left) + length_right = len(right) + length_mid = len(mid) + if k <= length_left: + return self.findKthLargest(left, k) + elif k > length_left + length_mid: + return self.findKthLargest(right, k - length_mid - length_left) + else: + return mid[0] diff --git a/python/0217-contains-duplicate.py b/python/0217-contains-duplicate.py new file mode 100644 index 000000000..9ec8f05a9 --- /dev/null +++ b/python/0217-contains-duplicate.py @@ -0,0 +1,9 @@ +class Solution: + def containsDuplicate(self, nums: List[int]) -> bool: + hashset = set() + + for n in nums: + if n in hashset: + return True + hashset.add(n) + return False diff --git a/python/0219-contains-duplicate-ii.py b/python/0219-contains-duplicate-ii.py new file mode 100644 index 000000000..35cbec0a8 --- /dev/null +++ b/python/0219-contains-duplicate-ii.py @@ -0,0 +1,13 @@ +class Solution: + def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool: + window = set() + L = 0 + + for R in range(len(nums)): + if R - L > k: + window.remove(nums[L]) + L += 1 + if nums[R] in window: + return True + window.add(nums[R]) + return False diff --git a/python/0221-maximal-square.py b/python/0221-maximal-square.py new file mode 100644 index 000000000..14c13d055 --- /dev/null +++ b/python/0221-maximal-square.py @@ -0,0 +1,21 @@ +class Solution: + def maximalSquare(self, matrix: List[List[str]]) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + cache = {} # map each (r, c) -> maxLength of square + + def helper(r, c): + if r >= ROWS or c >= COLS: + return 0 + + if (r, c) not in cache: + down = helper(r + 1, c) + right = helper(r, c + 1) + diag = helper(r + 1, c + 1) + + cache[(r, c)] = 0 + if matrix[r][c] == "1": + cache[(r, c)] = 1 + min(down, right, diag) + return cache[(r, c)] + + helper(0, 0) + return max(cache.values()) ** 2 diff --git a/python/0225-implement-stack-using-queues.py b/python/0225-implement-stack-using-queues.py new file mode 100644 index 000000000..761192340 --- /dev/null +++ b/python/0225-implement-stack-using-queues.py @@ -0,0 +1,18 @@ +class MyStack: + def __init__(self): + self.q = deque() + + def push(self, x: int) -> None: + self.q.append(x) + + for _ in range(len(self.q) - 1): + self.q.append(self.q.popleft()) + + def pop(self) -> int: + return self.q.popleft() + + def top(self) -> int: + return self.q[0] + + def empty(self) -> bool: + return len(self.q) == 0 diff --git a/python/0226-invert-binary-tree.py b/python/0226-invert-binary-tree.py new file mode 100644 index 000000000..11b13fa90 --- /dev/null +++ b/python/0226-invert-binary-tree.py @@ -0,0 +1,18 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]: + if not root: + return None + + # swap the children + root.left, root.right = root.right, root.left + + # make 2 recursive calls + self.invertTree(root.left) + self.invertTree(root.right) + return root diff --git a/python/0230-kth-smallest-element-in-a-bst.py b/python/0230-kth-smallest-element-in-a-bst.py new file mode 100644 index 000000000..15f6dde4e --- /dev/null +++ b/python/0230-kth-smallest-element-in-a-bst.py @@ -0,0 +1,22 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + + +class Solution: + def kthSmallest(self, root: TreeNode, k: int) -> int: + stack = [] + curr = root + + while stack or curr: + while curr: + stack.append(curr) + curr = curr.left + curr = stack.pop() + k -= 1 + if k == 0: + return curr.val + curr = curr.right diff --git a/python/0231-power-of-two.py b/python/0231-power-of-two.py new file mode 100644 index 000000000..4e001e969 --- /dev/null +++ b/python/0231-power-of-two.py @@ -0,0 +1,17 @@ +# iterative +class Solution: + def isPowerOfTwo(self, n: int) -> bool: + x = 1 + while x < n: + x *= 2 + return x == n + +# Bit manipulation +class Solution: + def isPowerOfTwo(self, n: int) -> bool: + return n > 0 and (n & (n - 1)) == 0 + +# Bit manipulation +class Solution: + def isPowerOfTwo(self, n: int) -> bool: + return n > 0 and ((1 << 30) % n) == 0 diff --git a/python/0232-implement-queue-using-stacks.py b/python/0232-implement-queue-using-stacks.py new file mode 100644 index 000000000..4cb5c2f95 --- /dev/null +++ b/python/0232-implement-queue-using-stacks.py @@ -0,0 +1,23 @@ +class MyQueue: + + def __init__(self): + self.append_stack = [] + self.inverted_stack = [] + + def push(self, x: int) -> None: + self.append_stack.append(x) + + def pop(self) -> int: + if not self.inverted_stack: + while self.append_stack: + self.inverted_stack.append(self.append_stack.pop()) + return self.inverted_stack.pop() + + def peek(self) -> int: + if not self.inverted_stack: + while self.append_stack: + self.inverted_stack.append(self.append_stack.pop()) + return self.inverted_stack[-1] + + def empty(self) -> bool: + return not (self.append_stack or self.inverted_stack) diff --git a/python/0234-palindrome-linked-list.py b/python/0234-palindrome-linked-list.py new file mode 100644 index 000000000..82dbb0987 --- /dev/null +++ b/python/0234-palindrome-linked-list.py @@ -0,0 +1,26 @@ +class Solution: + def isPalindrome(self, head: ListNode) -> bool: + fast = head + slow = head + + # find the middle (slow) + while fast and fast.next: + fast = fast.next.next + slow = slow.next + + # reverse second half + prev = None + while slow: + tmp = slow.next + slow.next = prev + prev = slow + slow = tmp + + # check palindrome + left, right = head, prev + while right: + if left.val != right.val: + return False + left = left.next + right = right.next + return True diff --git a/python/0235-lowest-common-ancestor-of-a-binary-search-tree.py b/python/0235-lowest-common-ancestor-of-a-binary-search-tree.py new file mode 100644 index 000000000..a3216f1fc --- /dev/null +++ b/python/0235-lowest-common-ancestor-of-a-binary-search-tree.py @@ -0,0 +1,19 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + + +class Solution: + def lowestCommonAncestor( + self, root: "TreeNode", p: "TreeNode", q: "TreeNode" + ) -> "TreeNode": + while True: + if root.val < p.val and root.val < q.val: + root = root.right + elif root.val > p.val and root.val > q.val: + root = root.left + else: + return root diff --git a/python/0236-lowest-common-ancestor-of-a-binary-tree.py b/python/0236-lowest-common-ancestor-of-a-binary-tree.py new file mode 100644 index 000000000..9ad4a46c8 --- /dev/null +++ b/python/0236-lowest-common-ancestor-of-a-binary-tree.py @@ -0,0 +1,26 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + +class Solution: + def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': + if not root: + return + if root == p or root == q: + return root + + left = self.lowestCommonAncestor(root.left, p, q) + right = self.lowestCommonAncestor(root.right, p, q) + + if left and right: + return root + + if left: + return left + if right: + return right + + return None diff --git a/python/0238-product-of-array-except-self.py b/python/0238-product-of-array-except-self.py new file mode 100644 index 000000000..cadb85136 --- /dev/null +++ b/python/0238-product-of-array-except-self.py @@ -0,0 +1,11 @@ +class Solution: + def productExceptSelf(self, nums: List[int]) -> List[int]: + res = [1] * (len(nums)) + + for i in range(1, len(nums)): + res[i] = res[i-1] * nums[i-1] + postfix = 1 + for i in range(len(nums) - 1, -1, -1): + res[i] *= postfix + postfix *= nums[i] + return res diff --git a/python/0239-sliding-window-maximum.py b/python/0239-sliding-window-maximum.py new file mode 100644 index 000000000..de7219129 --- /dev/null +++ b/python/0239-sliding-window-maximum.py @@ -0,0 +1,22 @@ +class Solution: + def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]: + output = [] + q = collections.deque() # index + l = r = 0 + # O(n) O(n) + while r < len(nums): + # pop smaller values from q + while q and nums[q[-1]] < nums[r]: + q.pop() + q.append(r) + + # remove left val from window + if l > q[0]: + q.popleft() + + if (r + 1) >= k: + output.append(nums[q[0]]) + l += 1 + r += 1 + + return output diff --git a/python/0241-different-ways-to-add-parentheses.py b/python/0241-different-ways-to-add-parentheses.py new file mode 100644 index 000000000..ed9be385d --- /dev/null +++ b/python/0241-different-ways-to-add-parentheses.py @@ -0,0 +1,12 @@ +class Solution: + def diffWaysToCompute(self, s: str) -> List[int]: + def f(s): + res = [] + for i, c in enumerate(s): + if c in '+-*': + for l in f(s[:i]): + for r in f(s[i + 1:]): + res.append(eval(f'{l}{c}{r}')) + + return res or [int(s)] + return f(s) diff --git a/python/0242-valid-anagram.py b/python/0242-valid-anagram.py new file mode 100644 index 000000000..6f84afdf5 --- /dev/null +++ b/python/0242-valid-anagram.py @@ -0,0 +1,15 @@ +class Solution: + def isAnagram(self, s: str, t: str) -> bool: + if len(s) != len(t): + return False + + countS, countT = {}, {} + + for i in range(len(s)): + countS[s[i]] = 1 + countS.get(s[i], 0) + countT[t[i]] = 1 + countT.get(t[i], 0) + return countS == countT + + + # easier solution + #return True if sorted(s) == sorted(t) else False diff --git a/python/0252-meeting-rooms.py b/python/0252-meeting-rooms.py new file mode 100644 index 000000000..24a6f3a83 --- /dev/null +++ b/python/0252-meeting-rooms.py @@ -0,0 +1,16 @@ +class Solution: + """ + @param intervals: an array of meeting time intervals + @return: if a person could attend all meetings + """ + + def canAttendMeetings(self, intervals): + intervals.sort(key=lambda i: i[0]) + + for i in range(1, len(intervals)): + i1 = intervals[i - 1] + i2 = intervals[i] + + if i1[1] > i2[0]: + return False + return True diff --git a/python/0253-meeting-rooms-ii.py b/python/0253-meeting-rooms-ii.py new file mode 100644 index 000000000..495516a4d --- /dev/null +++ b/python/0253-meeting-rooms-ii.py @@ -0,0 +1,14 @@ +def minMeetingRooms(self, intervals: List[List[int]]) -> int: + time = [] + for start, end in intervals: + time.append((start, 1)) + time.append((end, -1)) + + time.sort(key=lambda x: (x[0], x[1])) + + count = 0 + max_count = 0 + for t in time: + count += t[1] + max_count = max(max_count, count) + return max_count diff --git a/python/0253-meeting-rooms.py b/python/0253-meeting-rooms.py new file mode 100644 index 000000000..3d47c30c8 --- /dev/null +++ b/python/0253-meeting-rooms.py @@ -0,0 +1,21 @@ +class Solution: + """ + @param intervals: an array of meeting time intervals + @return: the minimum number of conference rooms required + """ + + def minMeetingRooms(self, intervals): + start = sorted([i[0] for i in intervals]) + end = sorted([i[1] for i in intervals]) + + res, count = 0, 0 + s, e = 0, 0 + while s < len(intervals): + if start[s] < end[e]: + s += 1 + count += 1 + else: + e += 1 + count -= 1 + res = max(res, count) + return res diff --git a/python/0261-graph-valid-tree.py b/python/0261-graph-valid-tree.py new file mode 100644 index 000000000..62777b22b --- /dev/null +++ b/python/0261-graph-valid-tree.py @@ -0,0 +1,73 @@ +# Problem is free on Lintcode +class Solution: + """ + @param n: An integer + @param edges: a list of undirected edges + @return: true if it's a valid tree, or false + """ + + def validTree(self, n, edges): + if not n: + return True + adj = {i: [] for i in range(n)} + for n1, n2 in edges: + adj[n1].append(n2) + adj[n2].append(n1) + + visit = set() + + def dfs(i, prev): + if i in visit: + return False + + visit.add(i) + for j in adj[i]: + if j == prev: + continue + if not dfs(j, i): + return False + return True + + return dfs(0, -1) and n == len(visit) + + + + # alternative solution via DSU O(ElogV) time complexity and + # save some space as we don't recreate graph\tree into adjacency list prior dfs and loop over the edge list directly + class Solution: + """ + @param n: An integer + @param edges: a list of undirected edges + @return: true if it's a valid tree, or false + """ + def __find(self, n: int) -> int: + while n != self.parents.get(n, n): + n = self.parents.get(n, n) + return n + + def __connect(self, n: int, m: int) -> None: + pn = self.__find(n) + pm = self.__find(m) + if pn == pm: + return + if self.heights.get(pn, 1) > self.heights.get(pm, 1): + self.parents[pn] = pm + else: + self.parents[pm] = pn + self.heights[pm] = self.heights.get(pn, 1) + 1 + self.components -= 1 + + def valid_tree(self, n: int, edges: List[List[int]]) -> bool: + # init here as not sure that ctor will be re-invoked in different tests + self.parents = {} + self.heights = {} + self.components = n + + for e1, e2 in edges: + if self.__find(e1) == self.__find(e2): # 'redundant' edge + return False + self.__connect(e1, e2) + + return self.components == 1 # forest contains one tree + + diff --git a/python/0263-ugly-number.py b/python/0263-ugly-number.py new file mode 100644 index 000000000..7c37eabd9 --- /dev/null +++ b/python/0263-ugly-number.py @@ -0,0 +1,9 @@ +class Solution: + def isUgly(self, n: int) -> bool: + if n <= 0: + return False + + for p in [2, 3, 5]: + while n % p == 0: + n = n // p + return n == 1 diff --git a/python/0268-missing-number.py b/python/0268-missing-number.py new file mode 100644 index 000000000..9d39e0456 --- /dev/null +++ b/python/0268-missing-number.py @@ -0,0 +1,7 @@ +class Solution: + def missingNumber(self, nums: List[int]) -> int: + res = len(nums) + + for i in range(len(nums)): + res += i - nums[i] + return res diff --git a/python/0269-alien-dictionary.py b/python/0269-alien-dictionary.py new file mode 100644 index 000000000..0409db0f7 --- /dev/null +++ b/python/0269-alien-dictionary.py @@ -0,0 +1,37 @@ +class Solution: + def alienOrder(self, words: List[str]) -> str: + adj = {char: set() for word in words for char in word} + + for i in range(len(words) - 1): + w1, w2 = words[i], words[i + 1] + minLen = min(len(w1), len(w2)) + if len(w1) > len(w2) and w1[:minLen] == w2[:minLen]: + return "" + for j in range(minLen): + if w1[j] != w2[j]: + print(w1[j], w2[j]) + adj[w1[j]].add(w2[j]) + break + + visited = {} # {char: bool} False visited, True current path + res = [] + + def dfs(char): + if char in visited: + return visited[char] + + visited[char] = True + + for neighChar in adj[char]: + if dfs(neighChar): + return True + + visited[char] = False + res.append(char) + + for char in adj: + if dfs(char): + return "" + + res.reverse() + return "".join(res) diff --git a/python/0271-encode-and-decode-strings.py b/python/0271-encode-and-decode-strings.py new file mode 100644 index 000000000..937b23a21 --- /dev/null +++ b/python/0271-encode-and-decode-strings.py @@ -0,0 +1,22 @@ +class Solution: + def encode(self, strs): + res = "" + for s in strs: + res += str(len(s)) + "#" + s + return res + + def decode(self, s): + res = [] + i = 0 + + while i < len(s): + j = i + while s[j] != '#': + j += 1 + length = int(s[i:j]) + i = j + 1 + j = i + length + res.append(s[i:j]) + i = j + + return res diff --git a/python/0274-H-index.py b/python/0274-H-index.py new file mode 100644 index 000000000..0c5db2b7c --- /dev/null +++ b/python/0274-H-index.py @@ -0,0 +1,8 @@ +class Solution: + def hIndex(self, citations: List[int]) -> int: + length = len(citations) + citations.sort() + for i in range(length): + if citations[i] >= length - i: + return length - i + return 0 diff --git a/python/0278-first-bad-version.py b/python/0278-first-bad-version.py new file mode 100644 index 000000000..0dd8fecc3 --- /dev/null +++ b/python/0278-first-bad-version.py @@ -0,0 +1,10 @@ +class Solution: + def firstBadVersion(self, n: int) -> int: + l, r = 1, n + while l < r: + v = (l + r) // 2 + if isBadVersion(v): + r = v + else: + l = v + 1 + return l diff --git a/python/0280-wiggle-sort.py b/python/0280-wiggle-sort.py new file mode 100644 index 000000000..f2f7e01b7 --- /dev/null +++ b/python/0280-wiggle-sort.py @@ -0,0 +1,5 @@ +class Solution: + def wiggleSort(self, nums: List[int]) -> None: + for i in range(1, len(nums)): + if (i % 2 == 1 and nums[i] < nums[i - 1]) or (i % 2 == 0 and nums[i] > nums[i - 1]): + nums[i], nums[i - 1] = nums[i - 1], nums[i] diff --git a/python/0283-move-zeroes.py b/python/0283-move-zeroes.py new file mode 100644 index 000000000..12d17b329 --- /dev/null +++ b/python/0283-move-zeroes.py @@ -0,0 +1,13 @@ +class Solution: + def moveZeroes(self, nums: List[int]) -> None: + """ + Do not return anything, modify nums in-place instead. + """ + slow = 0 + for fast in range(len(nums)): + + if nums[fast] != 0 and nums[slow] == 0: + nums[slow], nums[fast] = nums[fast], nums[slow] + + if nums[slow] != 0: + slow += 1 \ No newline at end of file diff --git a/python/0286-walls-and-gates.py b/python/0286-walls-and-gates.py new file mode 100644 index 000000000..3e01bb8e3 --- /dev/null +++ b/python/0286-walls-and-gates.py @@ -0,0 +1,39 @@ +class Solution: + """ + @param rooms: m x n 2D grid + @return: nothing + """ + + def walls_and_gates(self, rooms: List[List[int]]): + ROWS, COLS = len(rooms), len(rooms[0]) + visit = set() + q = deque() + + def addRooms(r, c): + if ( + min(r, c) < 0 + or r == ROWS + or c == COLS + or (r, c) in visit + or rooms[r][c] == -1 + ): + return + visit.add((r, c)) + q.append([r, c]) + + for r in range(ROWS): + for c in range(COLS): + if rooms[r][c] == 0: + q.append([r, c]) + visit.add((r, c)) + + dist = 0 + while q: + for i in range(len(q)): + r, c = q.popleft() + rooms[r][c] = dist + addRooms(r + 1, c) + addRooms(r - 1, c) + addRooms(r, c + 1) + addRooms(r, c - 1) + dist += 1 diff --git a/python/0287-find-the-duplicate-number.py b/python/0287-find-the-duplicate-number.py new file mode 100644 index 000000000..28460b5d8 --- /dev/null +++ b/python/0287-find-the-duplicate-number.py @@ -0,0 +1,15 @@ +class Solution: + def findDuplicate(self, nums: List[int]) -> int: + slow, fast = 0, 0 + while True: + slow = nums[slow] + fast = nums[nums[fast]] + if slow == fast: + break + + slow2 = 0 + while True: + slow = nums[slow] + slow2 = nums[slow2] + if slow == slow2: + return slow diff --git a/python/0290-word-pattern.py b/python/0290-word-pattern.py new file mode 100644 index 000000000..2620bb0de --- /dev/null +++ b/python/0290-word-pattern.py @@ -0,0 +1,16 @@ +class Solution: + def wordPattern(self, pattern: str, s: str) -> bool: + words = s.split(" ") + if len(pattern) != len(words): + return False + charToWord = {} + wordToChar = {} + + for c, w in zip(pattern, words): + if c in charToWord and charToWord[c] != w: + return False + if w in wordToChar and wordToChar[w] != c: + return False + charToWord[c] = w + wordToChar[w] = c + return True diff --git a/python/0295-find-median-from-data-stream.py b/python/0295-find-median-from-data-stream.py new file mode 100644 index 000000000..6854b7ad2 --- /dev/null +++ b/python/0295-find-median-from-data-stream.py @@ -0,0 +1,28 @@ +class MedianFinder: + def __init__(self): + """ + initialize your data structure here. + """ + # two heaps, large, small, minheap, maxheap + # heaps should be equal size + self.small, self.large = [], [] # maxHeap, minHeap (python default) + + def addNum(self, num: int) -> None: + if self.large and num > self.large[0]: + heapq.heappush(self.large, num) + else: + heapq.heappush(self.small, -1 * num) + + if len(self.small) > len(self.large) + 1: + val = -1 * heapq.heappop(self.small) + heapq.heappush(self.large, val) + if len(self.large) > len(self.small) + 1: + val = heapq.heappop(self.large) + heapq.heappush(self.small, -1 * val) + + def findMedian(self) -> float: + if len(self.small) > len(self.large): + return -1 * self.small[0] + elif len(self.large) > len(self.small): + return self.large[0] + return (-1 * self.small[0] + self.large[0]) / 2.0 diff --git a/python/0297-serialize-and-deserialize-binary-tree.py b/python/0297-serialize-and-deserialize-binary-tree.py new file mode 100644 index 000000000..5974ab7b0 --- /dev/null +++ b/python/0297-serialize-and-deserialize-binary-tree.py @@ -0,0 +1,36 @@ +# Definition for a binary tree node. +# class TreeNode(object): +# def __init__(self, x): +# self.val = x +# self.left = None +# self.right = None + + +class Codec: + def serialize(self, root): + res = [] + + def dfs(node): + if not node: + res.append("N") + return + res.append(str(node.val)) + dfs(node.left) + dfs(node.right) + + dfs(root) + return ",".join(res) + + def deserialize(self, data): + vals = data.split(",") + + def dfs(): + val = vals.pop(0) + if val == "N": + return None + node = TreeNode(val=int(val)) + node.left = dfs() + node.right = dfs() + return node + + return dfs() diff --git a/python/0300-longest-increasing-subsequence.py b/python/0300-longest-increasing-subsequence.py new file mode 100644 index 000000000..854767039 --- /dev/null +++ b/python/0300-longest-increasing-subsequence.py @@ -0,0 +1,9 @@ +class Solution: + def lengthOfLIS(self, nums: List[int]) -> int: + LIS = [1] * len(nums) + + for i in range(len(nums) - 1, -1, -1): + for j in range(i + 1, len(nums)): + if nums[i] < nums[j]: + LIS[i] = max(LIS[i], 1 + LIS[j]) + return max(LIS) diff --git a/python/0303-range-sum-query-immutable.py b/python/0303-range-sum-query-immutable.py new file mode 100644 index 000000000..3547605df --- /dev/null +++ b/python/0303-range-sum-query-immutable.py @@ -0,0 +1,14 @@ +class NumArray: + + def __init__(self, nums: List[int]): + self.prefix = [] + cur = 0 + for n in nums: + cur += n + self.prefix.append(cur) + + + def sumRange(self, left: int, right: int) -> int: + r = self.prefix[right] + l = self.prefix[left - 1] if left > 0 else 0 + return r - l diff --git a/python/0304-range-sum-query-2d-immutable.py b/python/0304-range-sum-query-2d-immutable.py new file mode 100644 index 000000000..0d1e62336 --- /dev/null +++ b/python/0304-range-sum-query-2d-immutable.py @@ -0,0 +1,14 @@ +class NumMatrix: + def __init__(self, matrix): + self.sum_ = [[0] * (len(matrix[0]) + 1) for _ in range(len(matrix) + 1)] + for i, line in enumerate(matrix): + previous = 0 + for j, num in enumerate(line): + previous += num + above = self.sum_[i][j + 1] + self.sum_[i + 1][j + 1] = previous + above + + def sumRegion(self, row1, col1, row2, col2): + sum_col2 = self.sum_[row2 + 1][col2 + 1] - self.sum_[row1][col2 + 1] + sum_col1 = self.sum_[row2 + 1][col1] - self.sum_[row1][col1] + return sum_col2 - sum_col1 diff --git a/python/0309-best-time-to-buy-and-sell-stock-with-cooldown.py b/python/0309-best-time-to-buy-and-sell-stock-with-cooldown.py new file mode 100644 index 000000000..c95b42883 --- /dev/null +++ b/python/0309-best-time-to-buy-and-sell-stock-with-cooldown.py @@ -0,0 +1,24 @@ +class Solution: + def maxProfit(self, prices: List[int]) -> int: + # State: Buying or Selling? + # If Buy -> i + 1 + # If Sell -> i + 2 + + dp = {} # key=(i, buying) val=max_profit + + def dfs(i, buying): + if i >= len(prices): + return 0 + if (i, buying) in dp: + return dp[(i, buying)] + + cooldown = dfs(i + 1, buying) + if buying: + buy = dfs(i + 1, not buying) - prices[i] + dp[(i, buying)] = max(buy, cooldown) + else: + sell = dfs(i + 2, not buying) + prices[i] + dp[(i, buying)] = max(sell, cooldown) + return dp[(i, buying)] + + return dfs(0, True) diff --git a/python/0312-burst-balloons.py b/python/0312-burst-balloons.py new file mode 100644 index 000000000..0c19c6537 --- /dev/null +++ b/python/0312-burst-balloons.py @@ -0,0 +1,13 @@ +class Solution: + def maxCoins(self, nums: List[int]) -> int: + cache = {} + nums = [1] + nums + [1] + + for offset in range(2, len(nums)): + for left in range(len(nums) - offset): + right = left + offset + for pivot in range(left + 1, right): + coins = nums[left] * nums[pivot] * nums[right] + coins += cache.get((left, pivot), 0) + cache.get((pivot, right), 0) + cache[(left, right)] = max(coins, cache.get((left, right), 0)) + return cache.get((0, len(nums) - 1), 0) diff --git a/python/0322-coin-change.py b/python/0322-coin-change.py new file mode 100644 index 000000000..9cd9b2e0e --- /dev/null +++ b/python/0322-coin-change.py @@ -0,0 +1,10 @@ +class Solution: + def coinChange(self, coins: List[int], amount: int) -> int: + dp = [amount + 1] * (amount + 1) + dp[0] = 0 + + for a in range(1, amount + 1): + for c in coins: + if a - c >= 0: + dp[a] = min(dp[a], 1 + dp[a - c]) + return dp[amount] if dp[amount] != amount + 1 else -1 diff --git a/python/0323-number-of-connected-components-in-an-undirected-graph.py b/python/0323-number-of-connected-components-in-an-undirected-graph.py new file mode 100644 index 000000000..66d94f6bc --- /dev/null +++ b/python/0323-number-of-connected-components-in-an-undirected-graph.py @@ -0,0 +1,20 @@ +class UnionFind: + def __init__(self): + self.f = {} + + def findParent(self, x): + y = self.f.get(x, x) + if x != y: + y = self.f[x] = self.findParent(y) + return y + + def union(self, x, y): + self.f[self.findParent(x)] = self.findParent(y) + + +class Solution: + def countComponents(self, n: int, edges: List[List[int]]) -> int: + dsu = UnionFind() + for a, b in edges: + dsu.union(a, b) + return len(set(dsu.findParent(x) for x in range(n))) diff --git a/python/0329-longest-increasing-path-in-a-matrix.py b/python/0329-longest-increasing-path-in-a-matrix.py new file mode 100644 index 000000000..36eb98d34 --- /dev/null +++ b/python/0329-longest-increasing-path-in-a-matrix.py @@ -0,0 +1,23 @@ +class Solution: + def longestIncreasingPath(self, matrix: List[List[int]]) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + dp = {} # (r, c) -> LIP + + def dfs(r, c, prevVal): + if r < 0 or r == ROWS or c < 0 or c == COLS or matrix[r][c] <= prevVal: + return 0 + if (r, c) in dp: + return dp[(r, c)] + + res = 1 + res = max(res, 1 + dfs(r + 1, c, matrix[r][c])) + res = max(res, 1 + dfs(r - 1, c, matrix[r][c])) + res = max(res, 1 + dfs(r, c + 1, matrix[r][c])) + res = max(res, 1 + dfs(r, c - 1, matrix[r][c])) + dp[(r, c)] = res + return res + + for r in range(ROWS): + for c in range(COLS): + dfs(r, c, -1) + return max(dp.values()) diff --git a/python/0332-reconstruct-itinerary.py b/python/0332-reconstruct-itinerary.py new file mode 100644 index 000000000..8985ada18 --- /dev/null +++ b/python/0332-reconstruct-itinerary.py @@ -0,0 +1,28 @@ +class Solution: + def findItinerary(self, tickets: List[List[str]]) -> List[str]: + adj = {src: [] for src, dst in tickets} + res = [] + + for src, dst in tickets: + adj[src].append(dst) + + for key in adj: + adj[key].sort() + + def dfs(adj, src): + if src in adj: + destinations = adj[src][:] + while destinations: + dest = destinations[0] + adj[src].pop(0) + dfs(adj, dest) + destinations = adj[src][:] + res.append(src) + + dfs(adj, "JFK") + res.reverse() + + if len(res) != len(tickets) + 1: + return [] + + return res diff --git a/python/0334-increasing-triplet-subsequence.py b/python/0334-increasing-triplet-subsequence.py new file mode 100644 index 000000000..e193f0fd1 --- /dev/null +++ b/python/0334-increasing-triplet-subsequence.py @@ -0,0 +1,14 @@ +class Solution: + def increasingTriplet(self, nums: List[int]) -> bool: + first = float('inf') # Initialize first to positive infinity + second = float('inf') # Initialize second to positive infinity + + for num in nums: + if num <= first: + first = num # Update first if num is smaller or equal + elif num <= second: + second = num # Update second if num is smaller or equal + else: + return True # We found a triplet: first < second < num + + return False # No triplet exists diff --git a/python/0338-counting-bits.py b/python/0338-counting-bits.py new file mode 100644 index 000000000..ca751db47 --- /dev/null +++ b/python/0338-counting-bits.py @@ -0,0 +1,24 @@ +class Solution: + def countBits(self, n: int) -> List[int]: + dp = [0] * (n + 1) + offset = 1 + + for i in range(1, n + 1): + if offset * 2 == i: + offset = i + dp[i] = 1 + dp[i - offset] + return dp + +# Another dp solution +class Solution2: + def countBits(self, n: int) -> List[int]: + res = [0] * (n + 1) + for i in range(1, n + 1): + if i % 2 == 1: + res[i] = res[i - 1] + 1 + else: + res[i] = res[i // 2] + return res +# This solution is based on the division of odd and even numbers. +# I think it's easier to understand. +# This is my full solution, covering the details: https://leetcode.com/problems/counting-bits/solutions/4411054/odd-and-even-numbers-a-easier-to-understanding-way-of-dp/ diff --git a/python/0344-reverse-string.py b/python/0344-reverse-string.py new file mode 100644 index 000000000..0c3f5885c --- /dev/null +++ b/python/0344-reverse-string.py @@ -0,0 +1,11 @@ +class Solution: + def reverseString(self, s: List[str]) -> None: + """ + Do not return anything, modify s in-place instead. + """ + l = 0 + r = len(s) - 1 + while l < r: + s[l],s[r] = s[r],s[l] + l += 1 + r -= 1 diff --git a/python/0347-top-k-frequent-elements.py b/python/0347-top-k-frequent-elements.py new file mode 100644 index 000000000..4c244ab2c --- /dev/null +++ b/python/0347-top-k-frequent-elements.py @@ -0,0 +1,18 @@ +class Solution: + def topKFrequent(self, nums: List[int], k: int) -> List[int]: + count = {} + freq = [[] for i in range(len(nums) + 1)] + + for n in nums: + count[n] = 1 + count.get(n, 0) + for n, c in count.items(): + freq[c].append(n) + + res = [] + for i in range(len(freq) - 1, 0, -1): + res += freq[i] + if len(res) == k: + return res + + + # O(n) diff --git a/python/0349-intersection-of-two-arrays.py b/python/0349-intersection-of-two-arrays.py new file mode 100644 index 000000000..96e127030 --- /dev/null +++ b/python/0349-intersection-of-two-arrays.py @@ -0,0 +1,10 @@ +class Solution: + def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]: + seen = set(nums1) + + res = [] + for n in nums2: + if n in seen: + res.append(n) + seen.remove(n) + return res diff --git a/python/0350-intersection-of-two-arrays-ii.py b/python/0350-intersection-of-two-arrays-ii.py new file mode 100644 index 000000000..4c9bf5001 --- /dev/null +++ b/python/0350-intersection-of-two-arrays-ii.py @@ -0,0 +1,17 @@ +class Solution: + def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]: + counter1 = Counter(nums1) + counter2 = Counter(nums2) + + # Using defaultdict to handle missing keys more efficiently + counter1 = defaultdict(int, counter1) + counter2 = defaultdict(int, counter2) + + intersection = [] + + for num, freq in counter1.items(): + min_freq = min(freq, counter2[num]) + if min_freq > 0: + intersection.extend([num] * min_freq) + + return intersection diff --git a/python/0355-design-twitter.py b/python/0355-design-twitter.py new file mode 100644 index 000000000..df626a9a5 --- /dev/null +++ b/python/0355-design-twitter.py @@ -0,0 +1,35 @@ +class Twitter: + def __init__(self): + self.count = 0 + self.tweetMap = defaultdict(list) # userId -> list of [count, tweetIds] + self.followMap = defaultdict(set) # userId -> set of followeeId + + def postTweet(self, userId: int, tweetId: int) -> None: + self.tweetMap[userId].append([self.count, tweetId]) + self.count -= 1 + + def getNewsFeed(self, userId: int) -> List[int]: + res = [] + minHeap = [] + + self.followMap[userId].add(userId) + for followeeId in self.followMap[userId]: + if followeeId in self.tweetMap: + index = len(self.tweetMap[followeeId]) - 1 + count, tweetId = self.tweetMap[followeeId][index] + heapq.heappush(minHeap, [count, tweetId, followeeId, index - 1]) + + while minHeap and len(res) < 10: + count, tweetId, followeeId, index = heapq.heappop(minHeap) + res.append(tweetId) + if index >= 0: + count, tweetId = self.tweetMap[followeeId][index] + heapq.heappush(minHeap, [count, tweetId, followeeId, index - 1]) + return res + + def follow(self, followerId: int, followeeId: int) -> None: + self.followMap[followerId].add(followeeId) + + def unfollow(self, followerId: int, followeeId: int) -> None: + if followeeId in self.followMap[followerId]: + self.followMap[followerId].remove(followeeId) diff --git a/python/0367-valid-perfect-square.py b/python/0367-valid-perfect-square.py new file mode 100644 index 000000000..cae3b5001 --- /dev/null +++ b/python/0367-valid-perfect-square.py @@ -0,0 +1,21 @@ +class Solution: + def isPerfectSquare(self, num: int) -> bool: + for i in range(1, num+1): + if i * i == num: + return True + if i* i > num: + return False + + + + def isPerfectSquare_2(self, num: int) -> bool: + l ,r = 1, num + while l <= r: + mid = (l +r) // 2 + if mid * mid > num: + r = mid - 1 + elif mid * mid < num: + l = mid + 1 + else: + return True + return False diff --git a/python/0371-sum-of-two-integers.py b/python/0371-sum-of-two-integers.py new file mode 100644 index 000000000..bc8ca5bd3 --- /dev/null +++ b/python/0371-sum-of-two-integers.py @@ -0,0 +1,16 @@ +class Solution: + def getSum(self, a: int, b: int) -> int: + def add(a, b): + if not a or not b: + return a or b + return add(a ^ b, (a & b) << 1) + + if a * b < 0: # assume a < 0, b > 0 + if a > 0: + return self.getSum(b, a) + if add(~a, 1) == b: # -a == b + return 0 + if add(~a, 1) < b: # -a < b + return add(~add(add(~a, 1), add(~b, 1)), 1) # -add(-a, -b) + + return add(a, b) # a*b >= 0 or (-a) > b > 0 diff --git a/python/0374-guess-number-higher-or-lower.py b/python/0374-guess-number-higher-or-lower.py new file mode 100644 index 000000000..06e38bda9 --- /dev/null +++ b/python/0374-guess-number-higher-or-lower.py @@ -0,0 +1,16 @@ +class Solution: + def guessNumber(self, n: int) -> int: + # return a num btw 1,..,n + + low = 1 + high = n + + while True: + mid = low + (high - low) // 2 + myGuess = guess(mid) + if myGuess == 1: + low = mid + 1 + elif myGuess == -1: + high = mid - 1 + else: + return mid diff --git a/python/0377-combination-sum-iv.py b/python/0377-combination-sum-iv.py new file mode 100644 index 000000000..cf373708e --- /dev/null +++ b/python/0377-combination-sum-iv.py @@ -0,0 +1,24 @@ +class Solution: + def combinationSum4(self, nums: List[int], target: int) -> int: + cache = {0: 1} + + for total in range(1, target + 1): + cache[total] = 0 + for n in nums: + cache[total] += cache.get(total - n, 0) + return cache[target] + + def dfs(total): + if total == target: + return 1 + if total > target: + return 0 + if total in cache: + return cache[total] + + cache[total] = 0 + for n in nums: + cache[total] += dfs(total + n) + return cache[total] + + return dfs(0) diff --git a/python/0380-insert-delete-getrandom-o1.py b/python/0380-insert-delete-getrandom-o1.py new file mode 100644 index 000000000..addf0f766 --- /dev/null +++ b/python/0380-insert-delete-getrandom-o1.py @@ -0,0 +1,31 @@ +from random import choice + + +class RandomizedSet: + + def __init__(self): + self.dict = {} + self.list = [] + + def insert(self, val: int) -> bool: + if val in self.dict: + return False + + self.dict[val] = len(self.list) + self.list.append(val) + + return True + + def remove(self, val: int) -> bool: + if val not in self.dict: + return False + + idx, last_element = self.dict[val], self.list[-1] + self.list[idx], self.dict[last_element] = last_element, idx + self.list.pop() + del self.dict[val] + + return True + + def getRandom(self) -> int: + return choice(self.list) diff --git a/python/0383-ransom-note.py b/python/0383-ransom-note.py new file mode 100644 index 000000000..01aadd11c --- /dev/null +++ b/python/0383-ransom-note.py @@ -0,0 +1,11 @@ +from collections import Counter + +class Solution: + def canConstruct(self, ransomNote: str, magazine: str) -> bool: + r_counter = Counter(ransomNote) + m_counter = Counter(magazine) + # magazine contains (>=) ransomNote + for c in ransomNote: + if m_counter[c] < r_counter[c]: + return False + return True diff --git a/python/0392-is-subsequence.py b/python/0392-is-subsequence.py new file mode 100644 index 000000000..adf416fcc --- /dev/null +++ b/python/0392-is-subsequence.py @@ -0,0 +1,8 @@ +class Solution: + def isSubsequence(self, s: str, t: str) -> bool: + i, j = 0, 0 + while i < len(s) and j < len(t): + if s[i] == t[j]: + i += 1 + j += 1 + return i == len(s) diff --git a/python/0394-decode-string.py b/python/0394-decode-string.py new file mode 100644 index 000000000..e4ea9cb3c --- /dev/null +++ b/python/0394-decode-string.py @@ -0,0 +1,20 @@ +class Solution: + def decodeString(self, s: str) -> str: + stack = [] + + for char in s: + if char is not "]": + stack.append(char) + else: + sub_str = "" + while stack[-1] is not "[": + sub_str = stack.pop() + sub_str + stack.pop() + + multiplier = "" + while stack and stack[-1].isdigit(): + multiplier = stack.pop() + multiplier + + stack.append(int(multiplier) * sub_str) + + return "".join(stack) diff --git a/python/0402-remove-k-digits.py b/python/0402-remove-k-digits.py new file mode 100644 index 000000000..b254ab57e --- /dev/null +++ b/python/0402-remove-k-digits.py @@ -0,0 +1,13 @@ +class Solution: + def removeKdigits(self, num: str, k: int) -> str: + stack = [] + for i in num: + while stack and stack[-1] > i and k > 0: + k -= 1 + stack.pop() + if stack or i is not "0": + stack.append(i) + if k: + stack = stack[:-k] + return ''.join(stack) or '0' + diff --git a/python/0410-split-array-largest-sum.py b/python/0410-split-array-largest-sum.py new file mode 100644 index 000000000..e7b6c4c44 --- /dev/null +++ b/python/0410-split-array-largest-sum.py @@ -0,0 +1,22 @@ +class Solution: + def splitArray(self, nums: List[int], m: int) -> int: + def canSplit(largest): + subarray = 0 + curSum = 0 + for n in nums: + curSum += n + if curSum > largest: + subarray += 1 + curSum = n + return subarray + 1 <= m + + l, r = max(nums), sum(nums) + res = r + while l <= r: + mid = l + ((r - l) // 2) + if canSplit(mid): + res = mid + r = mid - 1 + else: + l = mid + 1 + return res diff --git a/python/0416-partition-equal-subset-sum.py b/python/0416-partition-equal-subset-sum.py new file mode 100644 index 000000000..0fa35c087 --- /dev/null +++ b/python/0416-partition-equal-subset-sum.py @@ -0,0 +1,18 @@ +class Solution: + def canPartition(self, nums: List[int]) -> bool: + if sum(nums) % 2: + return False + + dp = set() + dp.add(0) + target = sum(nums) // 2 + + for i in range(len(nums) - 1, -1, -1): + nextDP = set() + for t in dp: + if (t + nums[i]) == target: + return True + nextDP.add(t + nums[i]) + nextDP.add(t) + dp = nextDP + return False diff --git a/python/0417-pacific-atlantic-water-flow.py b/python/0417-pacific-atlantic-water-flow.py new file mode 100644 index 000000000..ae6aa51fb --- /dev/null +++ b/python/0417-pacific-atlantic-water-flow.py @@ -0,0 +1,35 @@ +class Solution: + def pacificAtlantic(self, heights: List[List[int]]) -> List[List[int]]: + ROWS, COLS = len(heights), len(heights[0]) + pac, atl = set(), set() + + def dfs(r, c, visit, prevHeight): + if ( + (r, c) in visit + or r < 0 + or c < 0 + or r == ROWS + or c == COLS + or heights[r][c] < prevHeight + ): + return + visit.add((r, c)) + dfs(r + 1, c, visit, heights[r][c]) + dfs(r - 1, c, visit, heights[r][c]) + dfs(r, c + 1, visit, heights[r][c]) + dfs(r, c - 1, visit, heights[r][c]) + + for c in range(COLS): + dfs(0, c, pac, heights[0][c]) + dfs(ROWS - 1, c, atl, heights[ROWS - 1][c]) + + for r in range(ROWS): + dfs(r, 0, pac, heights[r][0]) + dfs(r, COLS - 1, atl, heights[r][COLS - 1]) + + res = [] + for r in range(ROWS): + for c in range(COLS): + if (r, c) in pac and (r, c) in atl: + res.append([r, c]) + return res diff --git a/python/0424-longest-repeating-character-replacement.py b/python/0424-longest-repeating-character-replacement.py new file mode 100644 index 000000000..54fc74bea --- /dev/null +++ b/python/0424-longest-repeating-character-replacement.py @@ -0,0 +1,15 @@ +class Solution: + def characterReplacement(self, s: str, k: int) -> int: + count = {} + + l = 0 + maxf = 0 + for r in range(len(s)): + count[s[r]] = 1 + count.get(s[r], 0) + maxf = max(maxf, count[s[r]]) + + if (r - l + 1) - maxf > k: + count[s[l]] -= 1 + l += 1 + + return (r - l + 1) diff --git a/python/0435-non-overlapping-intervals.py b/python/0435-non-overlapping-intervals.py new file mode 100644 index 000000000..510057b4b --- /dev/null +++ b/python/0435-non-overlapping-intervals.py @@ -0,0 +1,12 @@ +class Solution: + def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int: + intervals.sort() + res = 0 + prevEnd = intervals[0][1] + for start, end in intervals[1:]: + if start >= prevEnd: + prevEnd = end + else: + res += 1 + prevEnd = min(end, prevEnd) + return res diff --git a/python/0438-find-all-anagrams-in-a-string.py b/python/0438-find-all-anagrams-in-a-string.py new file mode 100644 index 000000000..8512dd9ed --- /dev/null +++ b/python/0438-find-all-anagrams-in-a-string.py @@ -0,0 +1,25 @@ +class Solution: + def findAnagrams(self, s: str, p: str) -> List[int]: + + startIndex = 0 + pMap, sMap = {}, {} + res = [] + + for char in p: + pMap[char] = 1 + pMap.get(char, 0) + + for i in range(len(s)): + sMap[s[i]] = 1 + sMap.get(s[i], 0) + + if i >= len(p) - 1: + if sMap == pMap: + res.append(startIndex) + + # If current character is in sMap, remove it and re-update the map. + if s[startIndex] in sMap: + sMap[s[startIndex]] -= 1 + if sMap[s[startIndex]] == 0: + del sMap[s[startIndex]] + startIndex += 1 + + return res \ No newline at end of file diff --git a/python/0441-arranging-coins.py b/python/0441-arranging-coins.py new file mode 100644 index 000000000..181a58a2b --- /dev/null +++ b/python/0441-arranging-coins.py @@ -0,0 +1,13 @@ +class Solution: + def arrangeCoins(self, n: int) -> int: + l, r = 1, n + res = 0 + while l <=r: + mid = (l+r)//2 + coins = (mid /2) * (mid+1) + if coins > n: + r = mid - 1 + else: + l = mid + 1 + res = max(mid, res) + return res diff --git a/python/0442-find-all-duplicates-in-an-array.py b/python/0442-find-all-duplicates-in-an-array.py new file mode 100644 index 000000000..ebc2cb05d --- /dev/null +++ b/python/0442-find-all-duplicates-in-an-array.py @@ -0,0 +1,11 @@ +class Solution: + def findDuplicates(self, nums: List[int]) -> List[int]: + res = [] + + for n in nums: + n = abs(n) + if nums[n - 1] < 0: + res.append(n) + nums[n - 1] = -nums[n - 1] + + return res diff --git a/python/0448-find-all-numbers-disappeared-in-an-array.py b/python/0448-find-all-numbers-disappeared-in-an-array.py new file mode 100644 index 000000000..e0383e10d --- /dev/null +++ b/python/0448-find-all-numbers-disappeared-in-an-array.py @@ -0,0 +1,11 @@ +class Solution: + def findDisappearedNumbers(self, nums: List[int]) -> List[int]: + for n in nums: + i = abs(n) - 1 + nums[i] = -1 * abs(nums[i]) + + res = [] + for i, n in enumerate(nums): + if n > 0: + res.append(i + 1) + return res diff --git a/python/0450-delete-node-in-a-bst.py b/python/0450-delete-node-in-a-bst.py new file mode 100644 index 000000000..4f5861c69 --- /dev/null +++ b/python/0450-delete-node-in-a-bst.py @@ -0,0 +1,28 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]: + if not root: + return root + + if key > root.val: + root.right = self.deleteNode(root.right, key) + elif key < root.val: + root.left = self.deleteNode(root.left, key) + else: + if not root.left: + return root.right + elif not root.right: + return root.left + + # Find the min from right subtree + cur = root.right + while cur.left: + cur = cur.left + root.val = cur.val + root.right = self.deleteNode(root.right, root.val) + return root diff --git a/python/0452-minimum-number-of-arrows-to-burst-balloons.py b/python/0452-minimum-number-of-arrows-to-burst-balloons.py new file mode 100644 index 000000000..dd79b9469 --- /dev/null +++ b/python/0452-minimum-number-of-arrows-to-burst-balloons.py @@ -0,0 +1,15 @@ +class Solution: + def findMinArrowShots(self, points: List[List[int]]) -> int: + points.sort() + + res = len(points) + prev = points[0] + for i in range(1, len(points)): + curr = points[i] + if curr[0] <= prev[1]: + res -= 1 + prev = [curr[0], min(curr[1], prev[1])] + else: + prev = curr + + return res diff --git a/python/0456-132-pattern.py b/python/0456-132-pattern.py new file mode 100644 index 000000000..2634d4248 --- /dev/null +++ b/python/0456-132-pattern.py @@ -0,0 +1,15 @@ +class Solution: + def find132pattern(self, nums: List[int]) -> bool: + stack = [] # pair [num, curLeftMin], mono-decreasing stack + curMin = nums[0] + + for n in nums: + while stack and n >= stack[-1][0]: + stack.pop() + if stack and n < stack[-1][0] and n > stack[-1][1]: + return True + + stack.append([n, curMin]) + curMin = min(n, curMin) + + return False diff --git a/python/0459-repeated-substring-pattern.py b/python/0459-repeated-substring-pattern.py new file mode 100644 index 000000000..f0e89037a --- /dev/null +++ b/python/0459-repeated-substring-pattern.py @@ -0,0 +1,4 @@ +class Solution: + def repeatedSubstringPattern(self, s: str) -> bool: + return s in (s + s)[1:-1] + diff --git a/python/0463-island-perimeter.py b/python/0463-island-perimeter.py new file mode 100644 index 000000000..fb45d3031 --- /dev/null +++ b/python/0463-island-perimeter.py @@ -0,0 +1,21 @@ +class Solution: + def islandPerimeter(self, grid: List[List[int]]) -> int: + visit = set() + + def dfs(i, j): + if i >= len(grid) or j >= len(grid[0]) or i < 0 or j < 0 or grid[i][j] == 0: + return 1 + if (i, j) in visit: + return 0 + + visit.add((i, j)) + perim = dfs(i, j + 1) + perim += dfs(i + 1, j) + perim += dfs(i, j - 1) + perim += dfs(i - 1, j) + return perim + + for i in range(len(grid)): + for j in range(len(grid[0])): + if grid[i][j]: + return dfs(i, j) diff --git a/python/0473-matchsticks-to-square.py b/python/0473-matchsticks-to-square.py new file mode 100644 index 000000000..0d94f3029 --- /dev/null +++ b/python/0473-matchsticks-to-square.py @@ -0,0 +1,22 @@ +class Solution: + def makesquare(self, matchsticks: List[int]) -> bool: + length = sum(matchsticks) // 4 + sides = [0] * 4 + + if sum(matchsticks) / 4 != length: + return False + matchsticks.sort(reverse=True) + + def backtrack(i): + if i == len(matchsticks): + return True + + for j in range(4): + if sides[j] + matchsticks[i] <= length: + sides[j] += matchsticks[i] + if backtrack(i + 1): + return True + sides[j] -= matchsticks[i] + return False + + return backtrack(0) diff --git a/python/0474-ones-and-zeroes.py b/python/0474-ones-and-zeroes.py new file mode 100644 index 000000000..d2c095a96 --- /dev/null +++ b/python/0474-ones-and-zeroes.py @@ -0,0 +1,32 @@ +class Solution: + def findMaxForm(self, strs: List[str], M: int, N: int) -> int: + # Dynamic Programming + dp = defaultdict(int) + + for s in strs: + mCnt, nCnt = s.count("0"), s.count("1") + for m in range(M, mCnt - 1, -1): + for n in range(N, nCnt - 1, -1): + dp[(m, n)] = max( + 1 + dp[(m - mCnt, n - nCnt)], + dp[(m, n)]) + return dp[(M, N)] + + # Memoization + dp = {} + + def dfs(i, m, n): + if i == len(strs): + return 0 + if (i, m, n) in dp: + return dp[(i, m, n)] + + mCnt, nCnt = strs[i].count("0"), strs[i].count("1") + dp[(i, m, n)] = dfs(i + 1, m, n) + if mCnt <= m and nCnt <= n: + dp[(i, m, n)] = max( + dp[(i, m, n)], + 1 + dfs(i + 1, m - mCnt, n - nCnt)) + return dp[(i, m, n)] + + return dfs(0, m, n) diff --git a/python/0494-target-sum.py b/python/0494-target-sum.py new file mode 100644 index 000000000..e8965ecba --- /dev/null +++ b/python/0494-target-sum.py @@ -0,0 +1,16 @@ +class Solution: + def findTargetSumWays(self, nums: List[int], target: int) -> int: + dp = {} # (index, total) -> # of ways + + def backtrack(i, total): + if i == len(nums): + return 1 if total == target else 0 + if (i, total) in dp: + return dp[(i, total)] + + dp[(i, total)] = backtrack(i + 1, total + nums[i]) + backtrack( + i + 1, total - nums[i] + ) + return dp[(i, total)] + + return backtrack(0, 0) diff --git a/python/0496-next-greater-element-i.py b/python/0496-next-greater-element-i.py new file mode 100644 index 000000000..52754176d --- /dev/null +++ b/python/0496-next-greater-element-i.py @@ -0,0 +1,37 @@ +class Solution: + def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]: + + # O (n + m) + nums1Idx = { n:i for i, n in enumerate(nums1) } + res = [-1] * len(nums1) + + stack = [] + for i in range(len(nums2)): + cur = nums2[i] + + # while stack exists and current is greater than the top of the stack + while stack and cur > stack[-1]: + val = stack.pop() # take top val + idx = nums1Idx[val] + res[idx] = cur + + if cur in nums1Idx: + stack.append(cur) + + return res + + + # O (n * m) + nums1Idx = { n:i for i, n in enumerate(nums1) } + res = [-1] * len(nums1) + + for i in range(len(nums2)): + if nums2[i] not in nums1Idx: + continue + for j in range(i + 1, len(nums2)): + if nums2[j] > nums2[i]: + idx = nums1Idx[nums2[i]] + res[idx] = nums2[j] + break + + return res \ No newline at end of file diff --git a/python/0502-ipo.py b/python/0502-ipo.py new file mode 100644 index 000000000..1ade40f09 --- /dev/null +++ b/python/0502-ipo.py @@ -0,0 +1,16 @@ +class Solution: + def findMaximizedCapital(self, k: int, w: int, profits: List[int], capital: List[int]) -> int: + # O(nlogn) + maxProfit = [] # only projects we can afford + minCapital = [(c, p) for c, p in zip(capital, profits)] + heapq.heapify(minCapital) + + for i in range(k): + + while minCapital and minCapital[0][0] <= w: + c, p = heapq.heappop(minCapital) + heapq.heappush(maxProfit, -1 * p) + if not maxProfit: + break + w += -1 * heapq.heappop(maxProfit) + return w diff --git a/python/0509-fibonacci-number.py b/python/0509-fibonacci-number.py new file mode 100644 index 000000000..d81d295fb --- /dev/null +++ b/python/0509-fibonacci-number.py @@ -0,0 +1,12 @@ +class Solution: + Memo = {} + + def fib(self, n: int): + if n in self.Memo: + return self.Memo[n] + if n == 0: + return 0 + if n == 1: + return 1 + self.Memo[n] = self.fib(n - 1) + self.fib(n - 2) + return self.Memo[n] diff --git a/python/0513-find-bottom-left-tree-value.py b/python/0513-find-bottom-left-tree-value.py new file mode 100644 index 000000000..f9a6f030f --- /dev/null +++ b/python/0513-find-bottom-left-tree-value.py @@ -0,0 +1,44 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +# Iterative +class Solution: + def findBottomLeftValue(self, root: Optional[TreeNode]) -> int: + + res = [] + q = deque() + q.append(root) + while q: + qlen = len(q) + level = [] + for i in range(qlen): + node = q.popleft() + if node: + q.append(node.left) + q.append(node.right) + level.append(node.val) + if level: + res.append(level) + return res[-1][0] + +# recursive +class Solution: + def findBottomLeftValue(self, root: Optional[TreeNode]) -> int: + max_height = -1 + res = -1 + def dfs(root, depth): + nonlocal max_height, res + if not root: + return + if depth > max_height: + max_height = max(depth, max_height) + res = root.val + dfs(root.left, depth + 1) + dfs(root.right, depth + 1) + + dfs(root, 0) + return res + diff --git a/python/0516-longest-palindromic-subsequence.py b/python/0516-longest-palindromic-subsequence.py new file mode 100644 index 000000000..1b586d513 --- /dev/null +++ b/python/0516-longest-palindromic-subsequence.py @@ -0,0 +1,60 @@ +# Time: O(n^2) Space: O(n^2) - For all three solutions +class Solution: + def longestPalindromeSubseq(self, s: str) -> int: + # Dynamic Programming + dp = [ [0] * (len(s) + 1) for i in range(len(s) + 1)] + res = 0 + + for i in range(len(s)): + for j in range(len(s) - 1, i - 1, -1): + if s[i] == s[j]: + dp[i][j] = 1 if i == j else 2 + if i - 1 >= 0: + dp[i][j] += dp[i - 1][j + 1] + else: + dp[i][j] = dp[i][j + 1] + if i - 1 >= 0: + dp[i][j] = max(dp[i][j], dp[i - 1][j]) + res = max(res, dp[i][j]) + return res + + + # Memoization + cache = {} + + def dfs(i, j): + if i < 0 or j == len(s): + return 0 + if (i, j) in cache: + return cache[(i, j)] + + if s[i] == s[j]: + length = 1 if i == j else 2 + cache[(i, j)] = length + dfs(i - 1, j + 1) + else: + cache[(i, j)] = max(dfs(i - 1, j), dfs(i, j + 1)) + return cache[(i, j)] + + for i in range(len(s)): + dfs(i, i) # odd length + dfs(i, i + 1) # even length + + return max(cache.values()) + +# LCS Solution +class Solution: + def longestPalindromeSubseq(self, s: str) -> int: + return self.longestCommonSubsequence(s, s[::-1]) + + + def longestCommonSubsequence(self, s1: str, s2: str) -> int: + N, M = len(s1), len(s2) + dp = [[0] * (M+1) for _ in range(N+1)] + + for i in range(N): + for j in range(M): + if s1[i] == s2[j]: + dp[i+1][j+1] = 1 + dp[i][j] + else: + dp[i+1][j+1] = max(dp[i][j+1], dp[i+1][j]) + return dp[N][M] diff --git a/python/0518-coin-change-ii.py b/python/0518-coin-change-ii.py new file mode 100644 index 000000000..163d2d06e --- /dev/null +++ b/python/0518-coin-change-ii.py @@ -0,0 +1,49 @@ +class Solution: + def change(self, amount: int, coins: List[int]) -> int: + # MEMOIZATION + # Time: O(n*m) + # Memory: O(n*m) + cache = {} + + def dfs(i, a): + if a == amount: + return 1 + if a > amount: + return 0 + if i == len(coins): + return 0 + if (i, a) in cache: + return cache[(i, a)] + + cache[(i, a)] = dfs(i, a + coins[i]) + dfs(i + 1, a) + return cache[(i, a)] + + return dfs(0, 0) + + # DYNAMIC PROGRAMMING + # Time: O(n*m) + # Memory: O(n*m) + dp = [[0] * (len(coins) + 1) for i in range(amount + 1)] + dp[0] = [1] * (len(coins) + 1) + for a in range(1, amount + 1): + for i in range(len(coins) - 1, -1, -1): + dp[a][i] = dp[a][i + 1] + if a - coins[i] >= 0: + dp[a][i] += dp[a - coins[i]][i] + return dp[amount][0] + + # DYNAMIC PROGRAMMING + # Time: O(n*m) + # Memory: O(n) where n = amount + dp = [0] * (amount + 1) + dp[0] = 1 + for i in range(len(coins) - 1, -1, -1): + nextDP = [0] * (amount + 1) + nextDP[0] = 1 + + for a in range(1, amount + 1): + nextDP[a] = dp[a] + if a - coins[i] >= 0: + nextDP[a] += nextDP[a - coins[i]] + dp = nextDP + return dp[amount] diff --git a/python/0523-continuous-subarray-sum.py b/python/0523-continuous-subarray-sum.py new file mode 100644 index 000000000..0c6daf79b --- /dev/null +++ b/python/0523-continuous-subarray-sum.py @@ -0,0 +1,18 @@ +#We are basically storing sum%k and storing it in the hashmap and checking it. +#Math logic is that the overall sum will get cancelled out because of modulo + +class Solution: + def checkSubarraySum(self, nums: List[int], k: int) -> bool: + hashmap = {} + hashmap[0]=-1 + summ=0 + for i,j in enumerate(nums): + summ+=j + if summ%k in hashmap.keys(): + if i-hashmap[summ%k]>=2: + return True + else: + continue + hashmap[summ%k]=i + return False + diff --git a/python/0525-contiguous-array.py b/python/0525-contiguous-array.py new file mode 100644 index 000000000..e64fa6578 --- /dev/null +++ b/python/0525-contiguous-array.py @@ -0,0 +1,21 @@ +class Solution: + def findMaxLength(self, nums: List[int]) -> int: + zero, one = 0, 0 + res = 0 + + diff_index = {} + + for i, n in enumerate(nums): + if n == 0: + zero += 1 + else: + one += 1 + if one - zero not in diff_index: + diff_index[one - zero] = i + + if one == zero: + res = one + zero + else: + idx = diff_index[one - zero] + res = max(res, i - idx) + return res diff --git a/python/0535-encode-and-decode-tinyurl.py b/python/0535-encode-and-decode-tinyurl.py new file mode 100644 index 000000000..98494ffba --- /dev/null +++ b/python/0535-encode-and-decode-tinyurl.py @@ -0,0 +1,19 @@ +class Codec: + def __init__(self): + self.encodeMap = {} + self.decodeMap = {} + self.base = "http://tinyurl.com/" + + def encode(self, longUrl: str) -> str: + """Encodes a URL to a shortened URL. + """ + if longUrl not in self.encodeMap: + shortUrl = self.base + str(len(self.encodeMap) + 1) + self.encodeMap[longUrl] = shortUrl + self.decodeMap[shortUrl] = longUrl + return self.encodeMap[longUrl] + + def decode(self, shortUrl: str) -> str: + """Decodes a shortened URL to its original URL. + """ + return self.decodeMap[shortUrl] diff --git a/python/0540-single-element-in-a-sorted-array.py b/python/0540-single-element-in-a-sorted-array.py new file mode 100644 index 000000000..10bf1ac9c --- /dev/null +++ b/python/0540-single-element-in-a-sorted-array.py @@ -0,0 +1,27 @@ +class Solution: + def singleNonDuplicate(self, nums: List[int]) -> int: + def is_non_duplicate(i): + is_left_different = i == 0 or nums[i-1] != nums[i] + is_right_different = i == len(nums)-1 or nums[i+1] != nums[i] + return is_left_different and is_right_different + + if len(nums) == 1: + return nums[0] + + l, r = 0, len(nums) - 1 + while l <= r: + mid = (l + r) // 2 + if is_non_duplicate(mid): + return nums[mid] + + if mid % 2 == 0: + if nums[mid+1] == nums[mid]: + l = mid + 1 + else: + r = mid - 1 + else: + if nums[mid+1] == nums[mid]: + r = mid - 1 + else: + l = mid + 1 + diff --git a/python/0543-diameter-of-binary-tree.py b/python/0543-diameter-of-binary-tree.py new file mode 100644 index 000000000..78ec03116 --- /dev/null +++ b/python/0543-diameter-of-binary-tree.py @@ -0,0 +1,23 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int: + res = 0 + + def dfs(root): + nonlocal res + + if not root: + return 0 + left = dfs(root.left) + right = dfs(root.right) + res = max(res, left + right) + + return 1 + max(left, right) + + dfs(root) + return res diff --git a/python/0554-brick-wall.py b/python/0554-brick-wall.py new file mode 100644 index 000000000..060bee714 --- /dev/null +++ b/python/0554-brick-wall.py @@ -0,0 +1,11 @@ +class Solution: + def leastBricks(self, wall: List[List[int]]) -> int: + countGap = { 0 : 0 } # { Position : Gap count } + + for r in wall: + total = 0 # Position + for b in r[:-1]: + total += b + countGap[total] = 1 + countGap.get(total, 0) + + return len(wall) - max(countGap.values()) # Total number of rows - Max gap diff --git a/python/0560-subarray-sum-equals-k.py b/python/0560-subarray-sum-equals-k.py new file mode 100644 index 000000000..70a2be094 --- /dev/null +++ b/python/0560-subarray-sum-equals-k.py @@ -0,0 +1,18 @@ +class Solution: + def subarraySum(self, nums: List[int], k: int) -> int: + count = 0 + sum = 0 + dic = {} + dic[0] = 1 + for i in range(len(nums)): + sum += nums[i] + if sum-k in dic: + count += dic[sum-k] + dic[sum] = dic.get(sum, 0)+1 + return count + +# Time Complexity : +# O(N) -> Where N is the size of the array and we are iterating over the array once + +# Space Complexity: +# O(N) -> Creating a hashmap/dictionary diff --git a/python/0567-permutation-in-string.py b/python/0567-permutation-in-string.py new file mode 100644 index 000000000..10fea3f0c --- /dev/null +++ b/python/0567-permutation-in-string.py @@ -0,0 +1,34 @@ +class Solution: + def checkInclusion(self, s1: str, s2: str) -> bool: + if len(s1) > len(s2): + return False + + s1Count, s2Count = [0] * 26, [0] * 26 + for i in range(len(s1)): + s1Count[ord(s1[i]) - ord("a")] += 1 + s2Count[ord(s2[i]) - ord("a")] += 1 + + matches = 0 + for i in range(26): + matches += 1 if s1Count[i] == s2Count[i] else 0 + + l = 0 + for r in range(len(s1), len(s2)): + if matches == 26: + return True + + index = ord(s2[r]) - ord("a") + s2Count[index] += 1 + if s1Count[index] == s2Count[index]: + matches += 1 + elif s1Count[index] + 1 == s2Count[index]: + matches -= 1 + + index = ord(s2[l]) - ord("a") + s2Count[index] -= 1 + if s1Count[index] == s2Count[index]: + matches += 1 + elif s1Count[index] - 1 == s2Count[index]: + matches -= 1 + l += 1 + return matches == 26 diff --git a/python/0572-subtree-of-another-tree.py b/python/0572-subtree-of-another-tree.py new file mode 100644 index 000000000..6ef5bd247 --- /dev/null +++ b/python/0572-subtree-of-another-tree.py @@ -0,0 +1,24 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def isSubtree(self, root: Optional[TreeNode], subRoot: Optional[TreeNode]) -> bool: + if not subRoot: + return True + if not root: + return False + + if self.isSameTree(root, subRoot): + return True + return self.isSubtree(root.left, subRoot) or self.isSubtree(root.right, subRoot) + + def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool: + if not p and not q: + return True + if p and q and p.val == q.val: + return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right) + else: + return False diff --git a/python/0605-can-place-flowers.py b/python/0605-can-place-flowers.py new file mode 100644 index 000000000..ec0c4354d --- /dev/null +++ b/python/0605-can-place-flowers.py @@ -0,0 +1,40 @@ +class Solution: + def canPlaceFlowers(self, flowerbed: List[int], n: int) -> bool: + # Solution with O(1) space complexity + empty = 0 if flowerbed[0] else 1 + + for f in flowerbed: + if f: + n -= int((empty - 1) / 2) # int division, round toward zero + empty = 0 + else: + empty += 1 + + n -= (empty) // 2 + return n <= 0 + +class Solution2: + def canPlaceFlowers(self, flowerbed: List[int], n: int) -> bool: + # Another solution with O(1) space complexity + for i in range(len(flowerbed)): + if n == 0: + return True + if ((i == 0 or flowerbed[i - 1] == 0) # If at the first element or the previous element equals to 0 + and (flowerbed[i] == 0) # If current element equals to 0 + and (i == len(flowerbed) - 1 or flowerbed[i + 1] == 0)): # If at the last element or the next element equals to 0 + # Place flower at the current position + flowerbed[i] = 1 + n -= 1 + + return n == 0 + +class Solution3: + def canPlaceFlowers(self, flowerbed: List[int], n: int) -> bool: + # Solution with O(n) space complexity + f = [0] + flowerbed + [0] + + for i in range(1, len(f) - 1): # skip first & last + if f[i - 1] == 0 and f[i] == 0 and f[i + 1] == 0: + f[i] = 1 + n -= 1 + return n <= 0 diff --git a/python/0606-construct-string-from-binary-tree.py b/python/0606-construct-string-from-binary-tree.py new file mode 100644 index 000000000..9ad095d43 --- /dev/null +++ b/python/0606-construct-string-from-binary-tree.py @@ -0,0 +1,30 @@ +class Solution: + def tree2str(self, root: Optional[TreeNode]) -> str: + # Solution with O(n) time and space complexity + res = [] + self.dfs(root, res) + return "".join(res) + + def dfs(self, t: TreeNode, res: list): + # If the current node is None, do nothing and return + if t is None: + return + res.append(str(t.val)) + + # If both left and right children are None, return as there are no more branches to explore + if t.left is None and t.right is None: + return + res.append('(') + + # Recursively call the DFS function for the left child + self.dfs(t.left, res) + res.append(')') + + # If the right child exists, process it + if t.right is not None: + res.append('(') + + # Recursively call the DFS function for the right child + self.dfs(t.right, res) + res.append(')') + \ No newline at end of file diff --git a/python/0617-merge-two-binary-trees.py b/python/0617-merge-two-binary-trees.py new file mode 100644 index 000000000..51112226e --- /dev/null +++ b/python/0617-merge-two-binary-trees.py @@ -0,0 +1,18 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def mergeTrees(self, t1: TreeNode, t2: TreeNode) -> TreeNode: + if not t1 and not t2: + return None + + v1 = t1.val if t1 else 0 + v2 = t2.val if t2 else 0 + root = TreeNode(v1 + v2) + + root.left = self.mergeTrees(t1.left if t1 else None, t2.left if t2 else None) + root.right = self.mergeTrees(t1.right if t1 else None, t2.right if t2 else None) + return root diff --git a/python/0621-task-scheduler.py b/python/0621-task-scheduler.py new file mode 100644 index 000000000..dcc32b673 --- /dev/null +++ b/python/0621-task-scheduler.py @@ -0,0 +1,31 @@ +class Solution: + def leastInterval(self, tasks: List[str], n: int) -> int: + count = Counter(tasks) + maxHeap = [-cnt for cnt in count.values()] + heapq.heapify(maxHeap) + + time = 0 + q = deque() # pairs of [-cnt, idleTime] + while maxHeap or q: + time += 1 + + if not maxHeap: + time = q[0][1] + else: + cnt = 1 + heapq.heappop(maxHeap) + if cnt: + q.append([cnt, time + n]) + if q and q[0][1] == time: + heapq.heappush(maxHeap, q.popleft()[0]) + return time + + +# Greedy algorithm +class Solution(object): + def leastInterval(self, tasks: List[str], n: int) -> int: + counter = collections.Counter(tasks) + max_count = max(counter.values()) + min_time = (max_count - 1) * (n + 1) + \ + sum(map(lambda count: count == max_count, counter.values())) + + return max(min_time, len(tasks)) \ No newline at end of file diff --git a/python/0622-design-circular-queue.py b/python/0622-design-circular-queue.py new file mode 100644 index 000000000..d2eb0f157 --- /dev/null +++ b/python/0622-design-circular-queue.py @@ -0,0 +1,54 @@ +class Node: + def __init__(self, val: int): + self.val = val + self.next = None + + +class MyCircularQueue: + + def __init__(self, k: int): + self.head = self.tail = None + self.capacity = k + self.size = 0 + + + def enQueue(self, value: int) -> bool: + if self.isFull(): + return False + + node = Node(value) + if self.size == 0: + self.head = self.tail = node + else: + self.tail.next = self.tail = node + + self.size += 1 + + return True + + + def deQueue(self) -> bool: + if self.isEmpty(): + return False + + self.head = self.head.next + self.size -= 1 + + return True + + + def Front(self) -> int: + return -1 if self.isEmpty() else self.head.val + + + def Rear(self) -> int: + return -1 if self.isEmpty() else self.tail.val + + + def isEmpty(self) -> bool: + return self.size == 0 + + + def isFull(self) -> bool: + return self.capacity == self.size + diff --git a/python/0647-palindromic-substrings.py b/python/0647-palindromic-substrings.py new file mode 100644 index 000000000..ea15f293d --- /dev/null +++ b/python/0647-palindromic-substrings.py @@ -0,0 +1,16 @@ +class Solution: + def countSubstrings(self, s: str) -> int: + res = 0 + + for i in range(len(s)): + res += self.countPali(s, i, i) + res += self.countPali(s, i, i + 1) + return res + + def countPali(self, s, l, r): + res = 0 + while l >= 0 and r < len(s) and s[l] == s[r]: + res += 1 + l -= 1 + r += 1 + return res diff --git a/python/0658-find-k-closest-elements.py b/python/0658-find-k-closest-elements.py new file mode 100644 index 000000000..8a6c35c67 --- /dev/null +++ b/python/0658-find-k-closest-elements.py @@ -0,0 +1,45 @@ +# Log(n) + k +# More code but also more intuitive +class Solution: + def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]: + l, r = 0, len(arr) - 1 + + # Find index of x or the closest val to x + val, idx = arr[0], 0 + while l <= r: + m = (l + r) // 2 + curDiff, resDiff = abs(arr[m] - x), abs(val - x) + if curDiff < resDiff or (curDiff == resDiff and arr[m] < val): + val, idx = arr[m], m + + if arr[m] < x: + l = m + 1 + elif arr[m] > x: + r = m - 1 + else: + break + + l = r = idx + for i in range(k - 1): + if l == 0: + r += 1 + elif r == len(arr) - 1 or x - arr[l - 1] <= arr[r + 1] - x: + l -= 1 + else: + r += 1 + return arr[l : r + 1] + + +# Log(n-k) + k +# Elegant but very difficult to understand +class Solution: + def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]: + l, r = 0, len(arr) - k + + while l < r: + m = (l + r) // 2 + if x - arr[m] > arr[m + k] - x: + l = m + 1 + else: + r = m + return arr[l : l + k] diff --git a/python/0662-maximum-width-of-binary-tree.py b/python/0662-maximum-width-of-binary-tree.py new file mode 100644 index 000000000..8cfb7a9cf --- /dev/null +++ b/python/0662-maximum-width-of-binary-tree.py @@ -0,0 +1,25 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def widthOfBinaryTree(self, root: Optional[TreeNode]) -> int: + if root is None: + return 0 + + q = [(root, 0)] + width = 0 + while q: + leftIndex = q[0][1] + rightIndex = q[-1][1] + width = max(width, rightIndex - leftIndex + 1) + + for _ in range(len(q)): + node, index = q.pop(0) + if node.left: + q.append((node.left, index * 2)) + if node.right: + q.append((node.right, index * 2 + 1)) + return width diff --git a/python/0665-non-decreasing-array.py b/python/0665-non-decreasing-array.py new file mode 100644 index 000000000..821fda5f4 --- /dev/null +++ b/python/0665-non-decreasing-array.py @@ -0,0 +1,16 @@ +class Solution: + def checkPossibility(self, nums): + if len(nums) <= 2: + return True + changed = False + for i, num in enumerate(nums): + if i == len(nums) - 1 or num <= nums[i + 1]: + continue + if changed: + return False + if i == 0 or nums[i + 1] >= nums[i - 1]: + nums[i] = nums[i + 1] + else: + nums[i + 1] = nums[i] + changed = True + return True diff --git a/python/0669-trim-a-binary-search-tree.py b/python/0669-trim-a-binary-search-tree.py new file mode 100644 index 000000000..5d6a5d90e --- /dev/null +++ b/python/0669-trim-a-binary-search-tree.py @@ -0,0 +1,21 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def trimBST(self, root: Optional[TreeNode], low: int, high: int) -> Optional[TreeNode]: + if not root: + return None + + if root.val > high: + return self.trimBST(root.left, low, high) + + if root.val < low: + return self.trimBST(root.right, low, high) + + else: + root.left = self.trimBST(root.left, low, high) + root.right = self.trimBST(root.right, low, high) + return root \ No newline at end of file diff --git a/python/0673-number-of-longest-increasing-subsequence.py b/python/0673-number-of-longest-increasing-subsequence.py new file mode 100644 index 000000000..a34bc1dfd --- /dev/null +++ b/python/0673-number-of-longest-increasing-subsequence.py @@ -0,0 +1,54 @@ +class Solution: + def findNumberOfLIS(self, nums: List[int]) -> int: + # 1. O(n^2) Recursive solution with Caching + + dp = {} # key = index, value = [length of LIS, count] + lenLIS, res = 0, 0 # length of LIS, count of LIS + + def dfs(i): + if i in dp: + return dp[i] + + maxLen, maxCnt = 1, 1 # length and count of LIS + for j in range(i + 1, len(nums)): + if nums[j] > nums[i]: # make sure increasing order + length, count = dfs(j) + if length + 1 > maxLen: + maxLen, maxCnt = length + 1, count + elif length + 1 == maxLen: + maxCnt += count + nonlocal lenLIS, res + if maxLen > lenLIS: + lenLIS, res = maxLen, maxCnt + elif maxLen == lenLIS: + res += maxCnt + dp[i] = [maxLen, maxCnt] + return dp[i] + + for i in range(len(nums)): + dfs(i) + return res + + # 2. O(n^2) Dynamic Programming + + dp = {} # key = index, value = [length of LIS, count] + lenLIS, res = 0, 0 # length of LIS, count of LIS + + # i = start of subseq + for i in range(len(nums) - 1, -1, -1): + maxLen, maxCnt = 1, 1 # len, cnt of LIS start from i + + for j in range(i + 1, len(nums)): + if nums[j] > nums[i]: + length, count = dp[j] # len, cnt of LIS start from j + if length + 1 > maxLen: + maxLen, maxCnt = length + 1, count + elif length + 1 == maxLen: + maxCnt += count + if maxLen > lenLIS: + lenLIS, res = maxLen, maxCnt + elif maxLen == lenLIS: + res += maxCnt + dp[i] = [maxLen, maxCnt] + + return res diff --git a/python/0678-valid-parenthesis-string.py b/python/0678-valid-parenthesis-string.py new file mode 100644 index 000000000..3b36c49eb --- /dev/null +++ b/python/0678-valid-parenthesis-string.py @@ -0,0 +1,41 @@ +# Dynamic Programming: O(n^2) +class Solution: + def checkValidString(self, s: str) -> bool: + dp = {(len(s), 0): True} # key=(i, leftCount) -> isValid + + def dfs(i, left): + if i == len(s) or left < 0: + return left == 0 + if (i, left) in dp: + return dp[(i, left)] + + if s[i] == "(": + dp[(i, left)] = dfs(i + 1, left + 1) + elif s[i] == ")": + dp[(i, left)] = dfs(i + 1, left - 1) + else: + dp[(i, left)] = ( + dfs(i + 1, left + 1) or dfs(i + 1, left - 1) or dfs(i + 1, left) + ) + return dp[(i, left)] + + return dfs(0, 0) + + +# Greedy: O(n) +class Solution: + def checkValidString(self, s: str) -> bool: + leftMin, leftMax = 0, 0 + + for c in s: + if c == "(": + leftMin, leftMax = leftMin + 1, leftMax + 1 + elif c == ")": + leftMin, leftMax = leftMin - 1, leftMax - 1 + else: + leftMin, leftMax = leftMin - 1, leftMax + 1 + if leftMax < 0: + return False + if leftMin < 0: # required because -> s = ( * ) ( + leftMin = 0 + return leftMin == 0 diff --git a/python/0680-valid-palindrome-ii.py b/python/0680-valid-palindrome-ii.py new file mode 100644 index 000000000..368205574 --- /dev/null +++ b/python/0680-valid-palindrome-ii.py @@ -0,0 +1,21 @@ +class Solution: + def validPalindrome(self, s: str) -> bool: + i, j = 0, len(s) - 1 + + while i < j: + if s[i] == s[j]: + i += 1 + j -= 1 + else: + return self.validPalindromeUtil(s, i + 1, j) or self.validPalindromeUtil(s, i, j - 1) + return True + + def validPalindromeUtil(self, s, i, j): + while i < j: + if s[i] == s[j]: + i += 1 + j -= 1 + else: + return False + + return True diff --git a/python/0682-baseball-game.py b/python/0682-baseball-game.py new file mode 100644 index 000000000..0057851a3 --- /dev/null +++ b/python/0682-baseball-game.py @@ -0,0 +1,24 @@ +class Solution: + def calPoints(self, operations: List[str]) -> int: + + score_stack = [] + + for o in operations: + + # it is +, D, or C + # if stack isn't of sufficient length, then operation is voided + if o == "+" and len(score_stack) >= 2: + summed = score_stack[-2] + score_stack[-1] + score_stack.append(summed) + + elif o == "D" and len(score_stack) >= 1: + doubled = score_stack[-1] * 2 + score_stack.append(doubled) + + elif o == "C" and len(score_stack) >= 1: + score_stack.pop() + + else: + score_stack.append(int(o)) + + return sum(score_stack) \ No newline at end of file diff --git a/python/0684-redundant-connection.py b/python/0684-redundant-connection.py new file mode 100644 index 000000000..4c09bf43a --- /dev/null +++ b/python/0684-redundant-connection.py @@ -0,0 +1,29 @@ +class Solution: + def findRedundantConnection(self, edges: List[List[int]]) -> List[int]: + par = [i for i in range(len(edges) + 1)] + rank = [1] * (len(edges) + 1) + + def find(n): + p = par[n] + while p != par[p]: + par[p] = par[par[p]] + p = par[p] + return p + + # return False if already unioned + def union(n1, n2): + p1, p2 = find(n1), find(n2) + + if p1 == p2: + return False + if rank[p1] > rank[p2]: + par[p2] = p1 + rank[p1] += rank[p2] + else: + par[p1] = p2 + rank[p2] += rank[p1] + return True + + for n1, n2 in edges: + if not union(n1, n2): + return [n1, n2] diff --git a/python/0695-max-area-of-island.py b/python/0695-max-area-of-island.py new file mode 100644 index 000000000..3b913a6e2 --- /dev/null +++ b/python/0695-max-area-of-island.py @@ -0,0 +1,23 @@ +class Solution: + def maxAreaOfIsland(self, grid: List[List[int]]) -> int: + ROWS, COLS = len(grid), len(grid[0]) + visit = set() + + def dfs(r, c): + if ( + r < 0 + or r == ROWS + or c < 0 + or c == COLS + or grid[r][c] == 0 + or (r, c) in visit + ): + return 0 + visit.add((r, c)) + return 1 + dfs(r + 1, c) + dfs(r - 1, c) + dfs(r, c + 1) + dfs(r, c - 1) + + area = 0 + for r in range(ROWS): + for c in range(COLS): + area = max(area, dfs(r, c)) + return area diff --git a/python/0698-partition-to-k-equal-sum-subsets.py b/python/0698-partition-to-k-equal-sum-subsets.py new file mode 100644 index 000000000..ea832a47d --- /dev/null +++ b/python/0698-partition-to-k-equal-sum-subsets.py @@ -0,0 +1,42 @@ +class Solution(object): + def canPartitionKSubsets(self, nums, k): + """ + :type nums: List[int] + :type k: int + :rtype: bool + """ + + if sum(nums) % k != 0: + return False + + nums.sort(reverse = True) + target = sum(nums) / k + visited= set() + + def backtrack(idx, count, currSum): + if count == k: + return True + + if target == currSum: + return backtrack(0, count + 1, 0) + + for i in range(idx, len(nums)): + + # skip duplicates if last same number was skipped + if i > 0 and (i - 1) not in visited and nums[i] == nums[i - 1]: + continue + + if i in visited or currSum + nums[i] > target: + continue + + visited.add(i) + + if backtrack(i + 1, count, currSum + nums[i]): + return True + + visited.remove(i) + + return False + + + return backtrack(0, 0, 0) \ No newline at end of file diff --git a/python/0701-insert-into-a-binary-search-tree.py b/python/0701-insert-into-a-binary-search-tree.py new file mode 100644 index 000000000..ae4a768be --- /dev/null +++ b/python/0701-insert-into-a-binary-search-tree.py @@ -0,0 +1,15 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]: + if not root: + return TreeNode(val) + if val > root.val: + root.right = self.insertIntoBST(root.right, val) + else: + root.left = self.insertIntoBST(root.left, val) + return root diff --git a/python/0703-kth-largest-element-in-a-stream.py b/python/0703-kth-largest-element-in-a-stream.py new file mode 100644 index 000000000..f159a0693 --- /dev/null +++ b/python/0703-kth-largest-element-in-a-stream.py @@ -0,0 +1,13 @@ +class KthLargest: + def __init__(self, k: int, nums: List[int]): + # minHeap w/ K largest integers + self.minHeap, self.k = nums, k + heapq.heapify(self.minHeap) + while len(self.minHeap) > k: + heapq.heappop(self.minHeap) + + def add(self, val: int) -> int: + heapq.heappush(self.minHeap, val) + if len(self.minHeap) > self.k: + heapq.heappop(self.minHeap) + return self.minHeap[0] diff --git a/python/0704-binary-search.py b/python/0704-binary-search.py new file mode 100644 index 000000000..df95ce190 --- /dev/null +++ b/python/0704-binary-search.py @@ -0,0 +1,13 @@ +class Solution: + def search(self, nums: List[int], target: int) -> int: + l, r = 0, len(nums) - 1 + + while l <= r: + m = l + ((r - l) // 2) # (l + r) // 2 can lead to overflow + if nums[m] > target: + r = m - 1 + elif nums[m] < target: + l = m + 1 + else: + return m + return -1 diff --git a/python/0705-design-hashset.py b/python/0705-design-hashset.py new file mode 100644 index 000000000..8528a57d1 --- /dev/null +++ b/python/0705-design-hashset.py @@ -0,0 +1,22 @@ +class MyHashSet: + + def __init__(self): + self.hashset = [] + + def add(self, key: int) -> None: + if not self.contains(key): + self.hashset.append(key) + + def remove(self, key: int) -> None: + if self.contains(key): + self.hashset.remove(key) + + def contains(self, key: int) -> bool: + return True if key in self.hashset else False + + +# Your MyHashSet object will be instantiated and called as such: +# obj = MyHashSet() +# obj.add(key) +# obj.remove(key) +# param_3 = obj.contains(key) diff --git a/python/0706-design-hashmap.py b/python/0706-design-hashmap.py new file mode 100644 index 000000000..c6ed2ee9f --- /dev/null +++ b/python/0706-design-hashmap.py @@ -0,0 +1,36 @@ +class ListNode: + def __init__(self, key=-1, val=-1, next=None): + self.key = key + self.val = val + self.next = next + +class MyHashMap: + def __init__(self): + self.map = [ListNode() for i in range(1000)] + + def hashcode(self, key): + return key % len(self.map) + + def put(self, key: int, value: int) -> None: + cur = self.map[self.hashcode(key)] + while cur.next: + if cur.next.key == key: + cur.next.val = value + return + cur = cur.next + cur.next = ListNode(key, value) + + def get(self, key: int) -> int: + cur = self.map[self.hashcode(key)].next + while cur and cur.key != key: + cur = cur.next + if cur: + return cur.val + return -1 + + def remove(self, key: int) -> None: + cur = self.map[self.hashcode(key)] + while cur.next and cur.next.key != key: + cur = cur.next + if cur and cur.next: + cur.next = cur.next.next diff --git a/python/0707-design-linked-list.py b/python/0707-design-linked-list.py new file mode 100644 index 000000000..c2fea174a --- /dev/null +++ b/python/0707-design-linked-list.py @@ -0,0 +1,67 @@ +class ListNode: + def __init__(self, val): + self.val = val + self.prev = None + self.next = None + +class MyLinkedList: + + def __init__(self): + self.left = ListNode(0) + self.right = ListNode(0) + self.left.next = self.right + self.right.prev = self.left + + def get(self, index: int) -> int: + cur = self.left.next + while cur and index > 0: + cur = cur.next + index -= 1 + + if cur and cur != self.right and index == 0: + return cur.val + return -1 + + def addAtHead(self, val: int) -> None: + node, prev, next = ListNode(val), self.left, self.left.next + node.next, node.prev = next, prev + next.prev = node + prev.next = node + + def addAtTail(self, val: int) -> None: + node, prev, next = ListNode(val), self.right.prev, self.right + node.next, node.prev = next, prev + next.prev = node + prev.next = node + + def addAtIndex(self, index: int, val: int) -> None: + next = self.left.next + while next and index > 0: + next = next.next + index -= 1 + + if next and index == 0: + node, prev = ListNode(val), next.prev + node.next, node.prev = next, prev + next.prev = node + prev.next = node + + + def deleteAtIndex(self, index: int) -> None: + node = self.left.next + while node and index > 0: + node = node.next + index -= 1 + + if node and node != self.right and index == 0: + node.prev.next = node.next + node.next.prev = node.prev + + +# Your MyLinkedList object will be instantiated and called as such: +# obj = MyLinkedList() +# param_1 = obj.get(index) +# obj.addAtHead(val) +# obj.addAtTail(val) +# obj.addAtIndex(index,val) +# obj.deleteAtIndex(index) diff --git a/python/0721-accounts-merge.py b/python/0721-accounts-merge.py new file mode 100644 index 000000000..e34fd48cd --- /dev/null +++ b/python/0721-accounts-merge.py @@ -0,0 +1,46 @@ +class UnionFind: + def __init__(self, n): + self.par = [i for i in range(n)] + self.rank = [1] * n + + def find(self, x): + while x != self.par[x]: + self.par[x] = self.par[self.par[x]] + x = self.par[x] + return x + + def union(self, x1, x2): + p1, p2 = self.find(x1), self.find(x2) + if p1 == p2: + return False + if self.rank[p1] > self.rank[p2]: + self.par[p2] = p1 + self.rank[p1] += self.rank[p2] + else: + self.par[p1] = p2 + self.rank[p2] += self.rank[p1] + return True + +class Solution: + def accountsMerge(self, accounts: List[List[str]]) -> List[List[str]]: + uf = UnionFind(len(accounts)) + emailToAcc = {} # email -> index of acc + + for i, a in enumerate(accounts): + for e in a[1:]: + if e in emailToAcc: + uf.union(i, emailToAcc[e]) + else: + emailToAcc[e] = i + + emailGroup = defaultdict(list) # index of acc -> list of emails + for e, i in emailToAcc.items(): + leader = uf.find(i) + emailGroup[leader].append(e) + + res = [] + for i, emails in emailGroup.items(): + name = accounts[i][0] + res.append([name] + sorted(emailGroup[i])) # array concat + return res + diff --git a/python/0724-find-pivot-index.py b/python/0724-find-pivot-index.py new file mode 100644 index 000000000..5feddd63f --- /dev/null +++ b/python/0724-find-pivot-index.py @@ -0,0 +1,11 @@ +class Solution: + def pivotIndex(self, nums: List[int]) -> int: + total = sum(nums) # O(n) + + leftSum = 0 + for i in range(len(nums)): + rightSum = total - nums[i] - leftSum + if leftSum == rightSum: + return i + leftSum += nums[i] + return -1 diff --git a/python/0729-my-calendar-i.py b/python/0729-my-calendar-i.py new file mode 100644 index 000000000..e8d5a16b9 --- /dev/null +++ b/python/0729-my-calendar-i.py @@ -0,0 +1,37 @@ +# Segment Tree solution + +class MyCalendar: + + def __init__(self): + self.calendar = CalendarNode(-1, -1) + + def book(self, start: int, end: int) -> bool: + + def bookHelper(cur, targetStart, targetEnd): + if targetStart > cur.end: + # go to the right + if not cur.right: + # we can insert event + cur.right = CalendarNode(targetStart, targetEnd) + return True + return bookHelper(cur.right, targetStart, targetEnd) + elif targetEnd < cur.start: + # got to the left + if not cur.left: + # we can insert event + cur.left = CalendarNode(targetStart, targetEnd) + return True + return bookHelper(cur.left, targetStart, targetEnd) + return False + + return bookHelper(self.calendar, start, end-1) # "end-1" because "end" bound is exclusive (see example 1) + + + + +class CalendarNode: + def __init__(self, start, end): + self.start = start + self.end = end + self.left = None + self.right = None \ No newline at end of file diff --git a/python/0735-asteroid-collision.py b/python/0735-asteroid-collision.py new file mode 100644 index 000000000..e64a9f6a2 --- /dev/null +++ b/python/0735-asteroid-collision.py @@ -0,0 +1,18 @@ +class Solution: + def asteroidCollision(self, asteroids: List[int]) -> List[int]: + stack = [] + + for a in asteroids: + while stack and a < 0 and stack[-1] > 0: + diff = a + stack[-1] + if diff > 0: + a = 0 + elif diff < 0: + stack.pop() + else: + a = 0 + stack.pop() + if a: + stack.append(a) + + return stack diff --git a/python/0739-daily-temperatures.py b/python/0739-daily-temperatures.py new file mode 100644 index 000000000..d6dc52aa8 --- /dev/null +++ b/python/0739-daily-temperatures.py @@ -0,0 +1,11 @@ +class Solution: + def dailyTemperatures(self, temperatures: List[int]) -> List[int]: + res = [0] * len(temperatures) + stack = [] # pair: [temp, index] + + for i, t in enumerate(temperatures): + while stack and t > stack[-1][0]: + stackT, stackInd = stack.pop() + res[stackInd] = i - stackInd + stack.append((t, i)) + return res diff --git a/python/0740-delete-and-earn.py b/python/0740-delete-and-earn.py new file mode 100644 index 000000000..d33e67221 --- /dev/null +++ b/python/0740-delete-and-earn.py @@ -0,0 +1,23 @@ +# House Robber Style +# Time Complexity O(n) +# Space Complexity O(n) +class Solution(object): + def deleteAndEarn(self, nums): + """ + :type nums: List[int] + :rtype: int + """ + + upperLimit = max(nums) + 1 + store = [0] * upperLimit + + for num in nums: + store[num] += num + + dp = [0] * upperLimit + + dp[1] = 1 * store[1] + for i in range(2, upperLimit): + dp[i] = max(dp[i - 2] + store[i], dp[i - 1]) + + return dp[-1] \ No newline at end of file diff --git a/python/0743-network-delay-time.py b/python/0743-network-delay-time.py new file mode 100644 index 000000000..084d558ea --- /dev/null +++ b/python/0743-network-delay-time.py @@ -0,0 +1,22 @@ +class Solution: + def networkDelayTime(self, times: List[List[int]], n: int, k: int) -> int: + edges = collections.defaultdict(list) + for u, v, w in times: + edges[u].append((v, w)) + + minHeap = [(0, k)] + visit = set() + t = 0 + while minHeap: + w1, n1 = heapq.heappop(minHeap) + if n1 in visit: + continue + visit.add(n1) + t = w1 + + for n2, w2 in edges[n1]: + if n2 not in visit: + heapq.heappush(minHeap, (w1 + w2, n2)) + return t if len(visit) == n else -1 + + # O(E * logV) diff --git a/python/0745-prefix-and-suffix-search.py b/python/0745-prefix-and-suffix-search.py new file mode 100644 index 000000000..45f4daa42 --- /dev/null +++ b/python/0745-prefix-and-suffix-search.py @@ -0,0 +1,39 @@ +class TrieNode: + def __init__(self): + self.children = {} # Dictionary to store child nodes + self.word = -1 # Store the index of the word at this node + + +class WordFilter: + def __init__(self, words: List[str]): + # Initialize root of the Trie + self.root = TrieNode() + + # For each word, we create combined prefix-suffix keys + for index, word in enumerate(words): + # Insert all combinations of the form prefix{suffix into the Trie + for i in range(len(word) + 1): + for j in range(len(word) + 1): + # Create the key as suffix + '{' + prefix + key = word[i:] + '{' + word[:j] + cur = self.root + for c in key: + if c not in cur.children: + cur.children[c] = TrieNode() + cur = cur.children[c] + cur.word = index # Store the index of the word at this node + + def f(self, pref: str, suff: str) -> int: + # Combine suffix and prefix to search in Trie + key = suff + '{' + pref + cur = self.root + for c in key: + if c not in cur.children: + return -1 # If combination doesn't exist, return -1 + cur = cur.children[c] + return cur.word # Return the largest index found for the valid combination + + +# Your WordFilter object will be instantiated and called as such: +# obj = WordFilter(words) +# param_1 = obj.f(pref,suff) diff --git a/python/0746-min-cost-climbing-stairs.py b/python/0746-min-cost-climbing-stairs.py new file mode 100644 index 000000000..0f0bc0bde --- /dev/null +++ b/python/0746-min-cost-climbing-stairs.py @@ -0,0 +1,6 @@ +class Solution: + def minCostClimbingStairs(self, cost: List[int]) -> int: + for i in range(len(cost) - 3, -1, -1): + cost[i] += min(cost[i + 1], cost[i + 2]) + + return min(cost[0], cost[1]) diff --git a/python/0752-open-the-lock.py b/python/0752-open-the-lock.py new file mode 100644 index 000000000..121d9e1b6 --- /dev/null +++ b/python/0752-open-the-lock.py @@ -0,0 +1,26 @@ +class Solution: + def openLock(self, deadends: List[str], target: str) -> int: + if "0000" in deadends: + return -1 + + def children(wheel): + res = [] + for i in range(4): + digit = str((int(wheel[i]) + 1) % 10) + res.append(wheel[:i] + digit + wheel[i + 1 :]) + digit = str((int(wheel[i]) + 10 - 1) % 10) + res.append(wheel[:i] + digit + wheel[i + 1 :]) + return res + + q = deque() + visit = set(deadends) + q.append(["0000", 0]) # [wheel, turns] + while q: + wheel, turns = q.popleft() + if wheel == target: + return turns + for child in children(wheel): + if child not in visit: + visit.add(child) + q.append([child, turns + 1]) + return -1 diff --git a/python/0763-partition-labels.py b/python/0763-partition-labels.py new file mode 100644 index 000000000..da414eb4b --- /dev/null +++ b/python/0763-partition-labels.py @@ -0,0 +1,21 @@ +class Solution: + def partitionLabels(self, S: str) -> List[int]: + count = {} + res = [] + i, length = 0, len(S) + for j in range(length): + c = S[j] + count[c] = j + + curLen = 0 + goal = 0 + while i < length: + c = S[i] + goal = max(goal, count[c]) + curLen += 1 + + if goal == i: + res.append(curLen) + curLen = 0 + i += 1 + return res diff --git a/python/0767-reorganize-string.py b/python/0767-reorganize-string.py new file mode 100644 index 000000000..e78099ed6 --- /dev/null +++ b/python/0767-reorganize-string.py @@ -0,0 +1,22 @@ +class Solution: + def reorganizeString(self, s: str) -> str: + count = Counter(s) # Hashmap, count each char + maxHeap = [[-cnt, char] for char, cnt in count.items()] + heapq.heapify(maxHeap) # O(n) + + prev = None + res = "" + while maxHeap or prev: + if prev and not maxHeap: + return "" + # most frequent, except prev + cnt, char = heapq.heappop(maxHeap) + res += char + cnt += 1 + + if prev: + heapq.heappush(maxHeap, prev) + prev = None + if cnt != 0: + prev = [cnt, char] + return res diff --git a/python/0778-swim-in-rising-water.py b/python/0778-swim-in-rising-water.py new file mode 100644 index 000000000..d6a284ddf --- /dev/null +++ b/python/0778-swim-in-rising-water.py @@ -0,0 +1,24 @@ +class Solution: + def swimInWater(self, grid: List[List[int]]) -> int: + N = len(grid) + visit = set() + minH = [[grid[0][0], 0, 0]] # (time/max-height, r, c) + directions = [[0, 1], [0, -1], [1, 0], [-1, 0]] + + visit.add((0, 0)) + while minH: + t, r, c = heapq.heappop(minH) + if r == N - 1 and c == N - 1: + return t + for dr, dc in directions: + neiR, neiC = r + dr, c + dc + if ( + neiR < 0 + or neiC < 0 + or neiR == N + or neiC == N + or (neiR, neiC) in visit + ): + continue + visit.add((neiR, neiC)) + heapq.heappush(minH, [max(t, grid[neiR][neiC]), neiR, neiC]) diff --git a/python/0783-minimum-distance-between-bst-nodes.py b/python/0783-minimum-distance-between-bst-nodes.py new file mode 100644 index 000000000..3cfe44125 --- /dev/null +++ b/python/0783-minimum-distance-between-bst-nodes.py @@ -0,0 +1,23 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def minDiffInBST(self, root: Optional[TreeNode]) -> int: + curr_smallest, prev = float("inf"), None + + def inorder(node): + nonlocal curr_smallest, prev + if node is None: + return + + inorder(node.left) + if prev is not None: + curr_smallest = min(curr_smallest, node.val-prev.val) + prev = node + inorder(node.right) + + inorder(root) + return curr_smallest diff --git a/python/0785-is-graph-bipartite.py b/python/0785-is-graph-bipartite.py new file mode 100644 index 000000000..db696ea22 --- /dev/null +++ b/python/0785-is-graph-bipartite.py @@ -0,0 +1,42 @@ +class Solution: + def isBipartiteBFS(self, graph: List[List[int]]) -> bool: + colors = [-1] * len(graph) + + for i in range(len(graph)): + if colors[i] == -1: + q = deque([i]) + colors[i] = 0 + + while q: + node = q.popleft() + + for nbh in graph[node]: + if colors[nbh] == -1: + colors[nbh] = 1 - colors[node] + q.append(nbh) + elif colors[nbh] == colors[node]: + return False + + return True + + def isBipartiteDFS(self, graph: List[List[int]]) -> bool: + colors = [-1] * len(graph) + + def dfs(node, c): + colors[node] = c + + for nbh in graph[node]: + if colors[nbh] == -1: + if not dfs(nbh, 1 - c): + return False + elif colors[nbh] == colors[node]: + return False + + return True + + for i in range(len(graph)): + if colors[i] == -1: + if not dfs(i, 0): + return False + + return True \ No newline at end of file diff --git a/python/0787-cheapest-flights-within-k-stops.py b/python/0787-cheapest-flights-within-k-stops.py new file mode 100644 index 000000000..aa1a68fdf --- /dev/null +++ b/python/0787-cheapest-flights-within-k-stops.py @@ -0,0 +1,17 @@ +class Solution: + def findCheapestPrice( + self, n: int, flights: List[List[int]], src: int, dst: int, k: int + ) -> int: + prices = [float("inf")] * n + prices[src] = 0 + + for i in range(k + 1): + tmpPrices = prices.copy() + + for s, d, p in flights: # s=source, d=dest, p=price + if prices[s] == float("inf"): + continue + if prices[s] + p < tmpPrices[d]: + tmpPrices[d] = prices[s] + p + prices = tmpPrices + return -1 if prices[dst] == float("inf") else prices[dst] diff --git a/python/0791-custom-sort-string.py b/python/0791-custom-sort-string.py new file mode 100644 index 000000000..b4d78a6e5 --- /dev/null +++ b/python/0791-custom-sort-string.py @@ -0,0 +1,16 @@ +class Solution: + def customSortString(self, order: str, s: str) -> str: + char_count_of_s = {} + for i in s: + char_count_of_s[i] = char_count_of_s.get(i, 0) + 1 + + satisfied_string = "" + for char in order: + if char in char_count_of_s: + satisfied_string += char * char_count_of_s[char] + del char_count_of_s[char] + + for key,val in char_count_of_s.items(): + satisfied_string += key * val + + return satisfied_string diff --git a/python/0802-find-eventual-safe-states.py b/python/0802-find-eventual-safe-states.py new file mode 100644 index 000000000..3a29c34a4 --- /dev/null +++ b/python/0802-find-eventual-safe-states.py @@ -0,0 +1,17 @@ +class Solution: + def eventualSafeNodes(self, graph: List[List[int]]) -> List[int]: + n = len(graph) + safe = {} + res = [] + def dfs(i): + if i in safe: + return safe[i] + safe[i] = False + for nei in graph[i]: + if not dfs(nei): + return safe[i] + safe[i] = True + return safe[i] + for i in range(len(graph)): + if dfs(i): res.append(i) + return res diff --git a/python/0838-push-dominoes.py b/python/0838-push-dominoes.py new file mode 100644 index 000000000..9a5542701 --- /dev/null +++ b/python/0838-push-dominoes.py @@ -0,0 +1,23 @@ +class Solution: + def pushDominoes(self, dominoes: str) -> str: + dom = list(dominoes) + q = collections.deque() + for i, d in enumerate(dom): + if d != '.': + q.append((i, d)) + + while q: + i, d = q.popleft() + + if d == 'L' and i > 0 and dom[i - 1] == '.': + q.append((i - 1, 'L')) + dom[i - 1] = 'L' + elif d == 'R': + if i + 1 < len(dom) and dom[i + 1] == '.': + if i + 2 < len(dom) and dom[i + 2] == 'L': + q.popleft() + else: + q.append((i + 1, 'R')) + dom[i + 1] = 'R' + + return ''.join(dom) diff --git a/python/0846-hand-of-straights.py b/python/0846-hand-of-straights.py new file mode 100644 index 000000000..218bbf2f4 --- /dev/null +++ b/python/0846-hand-of-straights.py @@ -0,0 +1,22 @@ +class Solution: + def isNStraightHand(self, hand: List[int], groupSize: int) -> bool: + if len(hand) % groupSize: + return False + + count = {} + for n in hand: + count[n] = 1 + count.get(n, 0) + + minH = list(count.keys()) + heapq.heapify(minH) + while minH: + first = minH[0] + for i in range(first, first + groupSize): + if i not in count: + return False + count[i] -= 1 + if count[i] == 0: + if i != minH[0]: + return False + heapq.heappop(minH) + return True diff --git a/python/0853-car-fleet.py b/python/0853-car-fleet.py new file mode 100644 index 000000000..6ca875d26 --- /dev/null +++ b/python/0853-car-fleet.py @@ -0,0 +1,10 @@ +class Solution: + def carFleet(self, target: int, position: List[int], speed: List[int]) -> int: + pair = [(p, s) for p, s in zip(position, speed)] + pair.sort(reverse=True) + stack = [] + for p, s in pair: # Reverse Sorted Order + stack.append((target - p) / s) + if len(stack) >= 2 and stack[-1] <= stack[-2]: + stack.pop() + return len(stack) diff --git a/python/0862-shortest-subarray-with-sum-at-least-k.py b/python/0862-shortest-subarray-with-sum-at-least-k.py new file mode 100644 index 000000000..0247d0397 --- /dev/null +++ b/python/0862-shortest-subarray-with-sum-at-least-k.py @@ -0,0 +1,19 @@ +import collections +class Solution: + def shortestSubarray(self, nums: List[int], k: int) -> int: + size = len(nums) + pre = [0] + for i in nums: + pre.append(pre[-1] + i) + + ans = size + 1 + monoq = collections.deque() + for i, val in enumerate(pre): + while monoq and val <= pre[monoq[-1]]: + monoq.pop() + while monoq and val - pre[monoq[0]] >= k: + ans = min(ans, i - monoq.popleft()) + + monoq.append(i) + + return ans if ans < size + 1 else -1 \ No newline at end of file diff --git a/python/0875-koko-eating-bananas.py b/python/0875-koko-eating-bananas.py new file mode 100644 index 000000000..b6348e6f6 --- /dev/null +++ b/python/0875-koko-eating-bananas.py @@ -0,0 +1,17 @@ +class Solution: + def minEatingSpeed(self, piles: List[int], h: int) -> int: + l, r = 1, max(piles) + res = r + + while l <= r: + k = (l + r) // 2 + + totalTime = 0 + for p in piles: + totalTime += math.ceil(float(p) / k) + if totalTime <= h: + res = k + r = k - 1 + else: + l = k + 1 + return res diff --git a/python/0876-middle-of-the-linked-list.py b/python/0876-middle-of-the-linked-list.py new file mode 100644 index 000000000..695c71364 --- /dev/null +++ b/python/0876-middle-of-the-linked-list.py @@ -0,0 +1,10 @@ +class Solution: + def middleNode(self, head: Optional[ListNode]) -> Optional[ListNode]: + if not head or not head.next: + return head + + slow = fast = head + while fast and fast.next: + slow, fast = slow.next, fast.next.next + + return slow diff --git a/python/0881-boats-to-save-people.py b/python/0881-boats-to-save-people.py new file mode 100644 index 000000000..e14225348 --- /dev/null +++ b/python/0881-boats-to-save-people.py @@ -0,0 +1,11 @@ +class Solution(object): + def numRescueBoats(self, people: list[int], limit: int) -> int: + people.sort() + right = len(people) - 1 + left = res = 0 + while left <= right: + if people[left] + people[right] <= limit: + left += 1 + right -= 1 + res += 1 + return res diff --git a/python/0894-all-possible-full-binary-trees.py b/python/0894-all-possible-full-binary-trees.py new file mode 100644 index 000000000..6268247c4 --- /dev/null +++ b/python/0894-all-possible-full-binary-trees.py @@ -0,0 +1,26 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def allPossibleFBT(self, n: int) -> List[Optional[TreeNode]]: + dp = { 0 : [], 1 : [ TreeNode() ] } + + def backtrack(n): + if n in dp: + return dp[n] + + res = [] + for l in range(n): + r = n - 1 - l + leftTrees, rightTrees = backtrack(l), backtrack(r) + + for t1 in leftTrees: + for t2 in rightTrees: + res.append(TreeNode(0, t1, t2)) + dp[n] = res + return res + + return backtrack(n) diff --git a/python/0895-maximum-frequency-stack.py b/python/0895-maximum-frequency-stack.py new file mode 100644 index 000000000..cd643e1d7 --- /dev/null +++ b/python/0895-maximum-frequency-stack.py @@ -0,0 +1,21 @@ +class FreqStack: + def __init__(self): + self.cnt = {} + self.maxCnt = 0 + self.stacks = {} + + def push(self, val: int) -> None: + valCnt = 1 + self.cnt.get(val, 0) + self.cnt[val] = valCnt + if valCnt > self.maxCnt: + self.maxCnt = valCnt + self.stacks[valCnt] = [] + self.stacks[valCnt].append(val) + + def pop(self) -> int: + res = self.stacks[self.maxCnt].pop() + self.cnt[res] -= 1 + if not self.stacks[self.maxCnt]: + self.maxCnt -= 1 + return res + diff --git a/python/0896-monotonic-array.py b/python/0896-monotonic-array.py new file mode 100644 index 000000000..e85132917 --- /dev/null +++ b/python/0896-monotonic-array.py @@ -0,0 +1,11 @@ +class Solution: + def isMonotonic(self, nums: List[int]) -> bool: + increasing = decreasing = True + + for i in range(len(nums) - 1): + if nums[i] > nums[i + 1]: + increasing = False + if nums[i] < nums[i + 1]: + decreasing = False + + return increasing or decreasing diff --git a/python/0901-online-stock-span.py b/python/0901-online-stock-span.py new file mode 100644 index 000000000..dd2c700ef --- /dev/null +++ b/python/0901-online-stock-span.py @@ -0,0 +1,11 @@ +class StockSpanner: + def __init__(self): + self.stack = [] # pair: (price, span) + + def next(self, price: int) -> int: + span = 1 + while self.stack and self.stack[-1][0] <= price: + span += self.stack[-1][1] + self.stack.pop() + self.stack.append((price, span)) + return span diff --git a/python/0904-fruit-into-baskets.py b/python/0904-fruit-into-baskets.py new file mode 100644 index 000000000..5d4cd83ec --- /dev/null +++ b/python/0904-fruit-into-baskets.py @@ -0,0 +1,29 @@ +import collections + +class Solution: + def totalFruit(self, fruits: List[int]) -> int: + count = collections.defaultdict(int) + l, total, res = 0, 0, 0 + + for r in range(len(fruits)): + count[fruits[r]] += 1 + total += 1 + + while len(count) > 2: + f = fruits[l] + count[f] -= 1 + total -= 1 + l += 1 + if not count[f]: + count.pop(f) + + res = max(res, total) + + return res + + + + + + + diff --git a/python/0904_fruit_into_baskets.py b/python/0904_fruit_into_baskets.py new file mode 100644 index 000000000..b0188db48 --- /dev/null +++ b/python/0904_fruit_into_baskets.py @@ -0,0 +1,18 @@ +class Solution: + def totalFruit(self, fruits: List[int]) -> int: + tr = {} + l = r = 0 + res = 0 + while r < len(fruits): + if fruits[r] not in tr: + tr[fruits[r]] = 1 + else: + tr[fruits[r]] += 1 + while len(tr) > 2: + tr[fruits[l]] -= 1 + if tr[fruits[l]] == 0: + del tr[fruits[l]] + l += 1 + res = max(res, r-l+1) + r += 1 + return res diff --git a/python/0909-snakes-and-ladders.py b/python/0909-snakes-and-ladders.py new file mode 100644 index 000000000..a2de34d21 --- /dev/null +++ b/python/0909-snakes-and-ladders.py @@ -0,0 +1,28 @@ +class Solution: + def snakesAndLadders(self, board: List[List[int]]) -> int: + length = len(board) + board.reverse() + + def intToPos(square): + r = (square - 1) // length + c = (square - 1) % length + if r % 2: + c = length - 1 - c + return [r, c] + + q = deque() + q.append([1, 0]) # [square, moves] + visit = set() + while q: + square, moves = q.popleft() + for i in range(1, 7): + nextSquare = square + i + r, c = intToPos(nextSquare) + if board[r][c] != -1: + nextSquare = board[r][c] + if nextSquare == length * length: + return moves + 1 + if nextSquare not in visit: + visit.add(nextSquare) + q.append([nextSquare, moves + 1]) + return -1 diff --git a/python/0912-sort-an-array.py b/python/0912-sort-an-array.py new file mode 100644 index 000000000..f29677c0f --- /dev/null +++ b/python/0912-sort-an-array.py @@ -0,0 +1,32 @@ +class Solution: + def sortArray(self, nums: List[int]) -> List[int]: + def merge(arr, L, M, R): + left, right = arr[L:M+1], arr[M+1:R+1] + i, j, k = L, 0, 0 + while j < len(left) and k < len(right): + if left[j] <= right[k]: + arr[i] = left[j] + j += 1 + else: + arr[i] = right[k] + k += 1 + i += 1 + while j < len(left): + nums[i] = left[j] + j += 1 + i += 1 + while k < len(right): + nums[i] = right[k] + k += 1 + i += 1 + + def mergeSort(arr, l, r): + if l == r: + return arr + m = (l + r) // 2 + mergeSort(arr, l, m) + mergeSort(arr, m + 1, r) + merge(arr, l, m, r) + return arr + + return mergeSort(nums, 0, len(nums) - 1) diff --git a/python/0918-maximum-sum-circular-subarray.py b/python/0918-maximum-sum-circular-subarray.py new file mode 100644 index 000000000..77fe144c2 --- /dev/null +++ b/python/0918-maximum-sum-circular-subarray.py @@ -0,0 +1,14 @@ +class Solution: + def maxSubarraySumCircular(self, nums: List[int]) -> int: + globMax, globMin = nums[0], nums[0] + curMax, curMin = 0, 0 + total = 0 + + for i, n in enumerate(nums): + curMax = max(curMax + n, n) + curMin = min(curMin + n, n) + total += n + globMax = max(curMax, globMax) + globMin = min(curMin, globMin) + + return max(globMax, total - globMin) if globMax > 0 else globMax diff --git a/python/0929-unique-email-addresses.py b/python/0929-unique-email-addresses.py new file mode 100644 index 000000000..981404a59 --- /dev/null +++ b/python/0929-unique-email-addresses.py @@ -0,0 +1,10 @@ +class Solution: + def numUniqueEmails(self, emails: list[str]) -> int: + unique_emails: set[str] = set() + for email in emails: + local_name, domain_name = email.split('@') + local_name = local_name.split('+')[0] + local_name = local_name.replace('.', '') + email = local_name + '@' + domain_name + unique_emails.add(email) + return len(unique_emails) diff --git a/python/0930-binary-subarrays-with-sum.py b/python/0930-binary-subarrays-with-sum.py new file mode 100644 index 000000000..3260608c4 --- /dev/null +++ b/python/0930-binary-subarrays-with-sum.py @@ -0,0 +1,17 @@ +class Solution: + def numSubarraysWithSum(self, nums: List[int], goal: int) -> int: + + def helper(x): + if x < 0: return 0 + + res = 0 + l, cur = 0, 0 + for r in range(len(nums)): + cur += nums[r] + while cur > x: + cur -= nums[l] + l += 1 + res += (r - l + 1) + return res + + return helper(goal) - helper(goal - 1) diff --git a/python/0931-minimum-falling-path-sum.py b/python/0931-minimum-falling-path-sum.py new file mode 100644 index 000000000..3928c389b --- /dev/null +++ b/python/0931-minimum-falling-path-sum.py @@ -0,0 +1,33 @@ +class Solution: + def minFallingPathSum(self, matrix: List[List[int]]) -> int: + Memo = {} + + def Path(i, k, n): + if (i, k) in Memo: + return Memo[(i, k)] + if i == n - 1: + return matrix[i][k] + if k > 0 and k < n - 1: + Psx = matrix[i][k] + Path(i + 1, k - 1, n) + Pst = matrix[i][k] + Path(i + 1, k, n) + Pdx = matrix[i][k] + Path(i + 1, k + 1, n) + Memo[(i, k)] = min(min(Pdx, Pst), Psx) + return Memo[(i, k)] + else: + if k == 0: + Pst = matrix[i][k] + Path(i + 1, k, n) + Pdx = matrix[i][k] + Path(i + 1, k + 1, n) + Memo[(i, k)] = min(Pst, Pdx) + return Memo[(i, k)] + else: + Psx = matrix[i][k] + Path(i + 1, k - 1, n) + Pst = matrix[i][k] + Path(i + 1, k, n) + Memo[(i, k)] = min(Pst, Psx) + return Memo[(i, k)] + + Min = 10**7 + for k in range(0, len(matrix[0])): + CP = Path(0, k, len(matrix[0])) + if CP < Min: + Min = CP + return Min diff --git a/python/0946-validate-stack-sequences.py b/python/0946-validate-stack-sequences.py new file mode 100644 index 000000000..61a687d8d --- /dev/null +++ b/python/0946-validate-stack-sequences.py @@ -0,0 +1,13 @@ +class Solution(object): + def validateStackSequences(self, pushed, popped): + i = 0 + stack = [] + for n in pushed: + stack.append(n) + while i < len(popped) and stack and popped[i] == stack[-1]: + stack.pop() + i += 1 + + return not stack + + diff --git a/python/0948-bag-of-tokens.py b/python/0948-bag-of-tokens.py new file mode 100644 index 000000000..396bd762f --- /dev/null +++ b/python/0948-bag-of-tokens.py @@ -0,0 +1,19 @@ +class Solution: + def bagOfTokensScore(self, tokens: List[int], power: int) -> int: + res = score = 0 + tokens.sort() + + l, r = 0, len(tokens) - 1 + while (l <= r): + if power >= tokens[l]: + power -= tokens[l] + l += 1 + score += 1 + res = max(res, score) + elif score > 0: + power += tokens[r] + r -= 1 + score -= 1 + else: + break + return res diff --git a/python/0953-verifying-an-alien-dictionary.py b/python/0953-verifying-an-alien-dictionary.py new file mode 100644 index 000000000..88efed867 --- /dev/null +++ b/python/0953-verifying-an-alien-dictionary.py @@ -0,0 +1,18 @@ +class Solution: + def isAlienSorted(self, words: List[str], order: str) -> bool: + # first differing char + # if word A is prefix of word B, word B must be AFTER word A + orderInd = { c : i for i, c in enumerate(order)} + + for i in range(len(words) - 1): + w1, w2 = words[i], words[i + 1] + + for j in range(len(w1)): + if j == len(w2): + return False + + if w1[j] != w2[j]: + if orderInd[w2[j]] < orderInd[w1[j]]: + return False + break + return True diff --git a/python/0973-k-closest-points-to-origin.py b/python/0973-k-closest-points-to-origin.py new file mode 100644 index 000000000..d375230eb --- /dev/null +++ b/python/0973-k-closest-points-to-origin.py @@ -0,0 +1,13 @@ +class Solution: + def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]: + minHeap = [] + for x, y in points: + dist = (x ** 2) + (y ** 2) + minHeap.append((dist, x, y)) + + heapq.heapify(minHeap) + res = [] + for _ in range(k): + _, x, y = heapq.heappop(minHeap) + res.append((x, y)) + return res \ No newline at end of file diff --git a/python/0977-squares-of-a-sorted-array.py b/python/0977-squares-of-a-sorted-array.py new file mode 100644 index 000000000..bc9a6ef8f --- /dev/null +++ b/python/0977-squares-of-a-sorted-array.py @@ -0,0 +1,18 @@ +# Time: O(n), one pass using two pointers. +# Space: O(1), output array is not considered for space complexity. + +class Solution: + def sortedSquares(self, nums: List[int]) -> List[int]: + n = len(nums) + res = [0] * n + l, r = 0, n - 1 + + while l <= r: + left, right = abs(nums[l]), abs(nums[r]) + if left > right: + res[r - l] = left * left + l += 1 + else: + res[r - l] = right * right + r -= 1 + return res diff --git a/python/0978-longest-turbulent-subarray.py b/python/0978-longest-turbulent-subarray.py new file mode 100644 index 000000000..55bfd4a04 --- /dev/null +++ b/python/0978-longest-turbulent-subarray.py @@ -0,0 +1,19 @@ +class Solution: + def maxTurbulenceSize(self, arr: List[int]) -> int: + l, r = 0, 1 + res, prev = 1, "" + + while r < len(arr): + if arr[r - 1] > arr[r] and prev != ">": + res = max(res, r - l + 1) + r += 1 + prev = ">" + elif arr[r - 1] < arr[r] and prev != "<": + res = max(res, r - l + 1) + r += 1 + prev = "<" + else: + r = r + 1 if arr[r] == arr[r - 1] else r + l = r - 1 + prev = "" + return res diff --git a/python/0981-time-based-key-value-store.py b/python/0981-time-based-key-value-store.py new file mode 100644 index 000000000..32e9fdb4c --- /dev/null +++ b/python/0981-time-based-key-value-store.py @@ -0,0 +1,23 @@ +class TimeMap: + def __init__(self): + """ + Initialize your data structure here. + """ + self.keyStore = {} # key : list of [val, timestamp] + + def set(self, key: str, value: str, timestamp: int) -> None: + if key not in self.keyStore: + self.keyStore[key] = [] + self.keyStore[key].append([value, timestamp]) + + def get(self, key: str, timestamp: int) -> str: + res, values = "", self.keyStore.get(key, []) + l, r = 0, len(values) - 1 + while l <= r: + m = (l + r) // 2 + if values[m][1] <= timestamp: + res = values[m][0] + l = m + 1 + else: + r = m - 1 + return res diff --git a/python/0994-rotting-oranges.py b/python/0994-rotting-oranges.py new file mode 100644 index 000000000..5dbd76f29 --- /dev/null +++ b/python/0994-rotting-oranges.py @@ -0,0 +1,33 @@ +class Solution: + def orangesRotting(self, grid: List[List[int]]) -> int: + q = collections.deque() + fresh = 0 + time = 0 + + for r in range(len(grid)): + for c in range(len(grid[0])): + if grid[r][c] == 1: + fresh += 1 + if grid[r][c] == 2: + q.append((r, c)) + + directions = [[0, 1], [0, -1], [1, 0], [-1, 0]] + while fresh > 0 and q: + length = len(q) + for i in range(length): + r, c = q.popleft() + + for dr, dc in directions: + row, col = r + dr, c + dc + # if in bounds and nonrotten, make rotten + # and add to q + if ( + row in range(len(grid)) + and col in range(len(grid[0])) + and grid[row][col] == 1 + ): + grid[row][col] = 2 + q.append((row, col)) + fresh -= 1 + time += 1 + return time if fresh == 0 else -1 diff --git a/python/0997-find-the-town-judge.py b/python/0997-find-the-town-judge.py new file mode 100644 index 000000000..40ef543ab --- /dev/null +++ b/python/0997-find-the-town-judge.py @@ -0,0 +1,12 @@ +class Solution: + def findJudge(self, n: int, trust: List[List[int]]) -> int: + delta = defaultdict(int) + + for src, dst in trust: + delta[src] -= 1 + delta[dst] += 1 + + for i in range(1, n + 1): + if delta[i] == n - 1: + return i + return -1 diff --git a/python/1011-capacity-to-ship-packages-within-d-days.py b/python/1011-capacity-to-ship-packages-within-d-days.py new file mode 100644 index 000000000..4973381ce --- /dev/null +++ b/python/1011-capacity-to-ship-packages-within-d-days.py @@ -0,0 +1,26 @@ +class Solution: + def shipWithinDays(self, weights: List[int], days: int) -> int: + l, r = max(weights), sum(weights) + min_cap = r + + def canShip(cap): + ships, curCap = 1, cap + for w in weights: + if curCap - w < 0: + ships += 1 + curCap = cap + curCap -= w + return ships <= days + + while l <= r: + cap = (l + r) // 2 + if canShip(cap): + min_cap = min(min_cap, cap) + r = cap - 1 + else: + l = cap + 1 + + return min_cap + + + diff --git a/python/1020-number-of-enclaves.py b/python/1020-number-of-enclaves.py new file mode 100644 index 000000000..be78f421b --- /dev/null +++ b/python/1020-number-of-enclaves.py @@ -0,0 +1,23 @@ +class Solution: + def numEnclaves(self, grid: List[List[int]]) -> int: + + ROWS, COLS = len(grid), len(grid[0]) + + def dfs(grid, row, col): + if 0 <= row < ROWS and 0 <= col < COLS: + if grid[row][col] == 1: + grid[row][col] = 0 + dfs(grid, row + 1, col) + dfs(grid, row - 1, col) + dfs(grid, row, col + 1) + dfs(grid, row, col - 1) + + for row in range(ROWS): + dfs(grid, row, 0) + dfs(grid, row, COLS - 1) + + for col in range(COLS): + dfs(grid, 0, col) + dfs(grid, ROWS - 1, col) + + return sum(grid[row][col] == 1 for row in range(ROWS) for col in range(COLS)) diff --git a/python/1029-two-city-scheduling.py b/python/1029-two-city-scheduling.py new file mode 100644 index 000000000..c24cf56cf --- /dev/null +++ b/python/1029-two-city-scheduling.py @@ -0,0 +1,13 @@ +class Solution: + def twoCitySchedCost(self, costs: List[List[int]]) -> int: + diffs = [] + for c1, c2 in costs: + diffs.append([c2 - c1, c1, c2]) + diffs.sort() + res = 0 + for i in range(len(diffs)): + if i < len(diffs) / 2: + res += diffs[i][2] + else: + res += diffs[i][1] + return res \ No newline at end of file diff --git a/python/1046-last-stone-weight.py b/python/1046-last-stone-weight.py new file mode 100644 index 000000000..d0e8b1250 --- /dev/null +++ b/python/1046-last-stone-weight.py @@ -0,0 +1,29 @@ +class Solution: + def lastStoneWeight(self, stones: List[int]) -> int: + stones = [-s for s in stones] + heapq.heapify(stones) + + while len(stones) > 1: + first = heapq.heappop(stones) + second = heapq.heappop(stones) + if second > first: + heapq.heappush(stones, first - second) + + stones.append(0) + return abs(stones[0]) + +# There's a private _heapify_max method. +# https://github.com/python/cpython/blob/1170d5a292b46f754cd29c245a040f1602f70301/Lib/heapq.py#L198 +class Solution(object): + def lastStoneWeight(self, stones): + heapq._heapify_max(stones) + while len(stones) > 1: + max_stone = heapq._heappop_max(stones) + diff = max_stone - stones[0] + if diff: + heapq._heapreplace_max(stones, diff) + else: + heapq._heappop_max(stones) + + stones.append(0) + return stones[0] diff --git a/python/1049-last-stone-weight-ii.py b/python/1049-last-stone-weight-ii.py new file mode 100644 index 000000000..964bfd6ca --- /dev/null +++ b/python/1049-last-stone-weight-ii.py @@ -0,0 +1,18 @@ +class Solution: + def lastStoneWeightII(self, stones: List[int]) -> int: + # Memoization + stoneSum = sum(stones) + target = ceil(stoneSum / 2) + + def dfs(i, total): + if total >= target or i == len(stones): + return abs(total - (stoneSum - total)) + if (i, total) in dp: + return dp[(i, total)] + + dp[(i, total)] = min(dfs(i + 1, total), + dfs(i + 1, total + stones[i])) + return dp[(i, total)] + + dp = {} + return dfs(0, 0) diff --git a/python/1074-number-of-submatrices-that-sum-to-target.py b/python/1074-number-of-submatrices-that-sum-to-target.py new file mode 100644 index 000000000..8bfd95f42 --- /dev/null +++ b/python/1074-number-of-submatrices-that-sum-to-target.py @@ -0,0 +1,26 @@ +class Solution: + def numSubmatrixSumTarget(self, matrix: List[List[int]], target: int) -> int: + ROWS, COLS = len(matrix), len(matrix[0]) + sub_sum = [[0 for i in range(COLS)] for j in range(ROWS)] + + for r in range(ROWS): + for c in range(COLS): + top = sub_sum[r - 1][c] if r > 0 else 0 + left = sub_sum[r][c - 1] if c > 0 else 0 + top_left = sub_sum[r - 1][c - 1] if min(r, c) > 0 else 0 + sub_sum[r][c] = matrix[r][c] + top + left - top_left + + res = 0 + for r1 in range(ROWS): + for r2 in range(r1, ROWS): + count = defaultdict(int) # prefix_sum -> count + count[0] = 1 + for c in range(COLS): + cur_sum = sub_sum[r2][c] - ( + sub_sum[r1 - 1][c] if r1 > 0 else 0 + ) + diff = cur_sum - target + res += count[diff] + count[cur_sum] += 1 + + return res diff --git a/python/1091-shortest-path-in-binary-matrix.py b/python/1091-shortest-path-in-binary-matrix.py new file mode 100644 index 000000000..c59f21af9 --- /dev/null +++ b/python/1091-shortest-path-in-binary-matrix.py @@ -0,0 +1,19 @@ +class Solution: + def shortestPathBinaryMatrix(self, grid: List[List[int]]) -> int: + N = len(grid) + q = deque([(0, 0, 1)]) # r, c, length + visit = set((0, 0)) + direct = [[0, 1], [1, 0], [0, -1], [-1, 0], + [1, 1], [-1, -1], [1, -1], [-1, 1]] + while q: + r, c, length = q.popleft() + if (min(r, c) < 0 or max(r, c) >= N or + grid[r][c]): + continue + if r == N - 1 and c == N - 1: + return length + for dr, dc in direct: + if (r + dr, c + dc) not in visit: + q.append((r + dr, c + dc, length + 1)) + visit.add((r + dr, c + dc)) + return -1 diff --git a/python/1137-n-th-tribonacci-number.py b/python/1137-n-th-tribonacci-number.py new file mode 100644 index 000000000..4a73c1f12 --- /dev/null +++ b/python/1137-n-th-tribonacci-number.py @@ -0,0 +1,16 @@ +class Solution: + Memo = {} + + def tribonacci(self, n: int): + if n in self.Memo: + return self.Memo[n] + if n == 0: + return 0 + if n == 1: + return 1 + if n == 2: + return 1 + self.Memo[n] = ( + self.tribonacci(n - 1) + self.tribonacci(n - 2) + self.tribonacci(n - 3) + ) + return self.Memo[n] diff --git a/python/1143-longest-common-subsequence.py b/python/1143-longest-common-subsequence.py new file mode 100644 index 000000000..0860720a5 --- /dev/null +++ b/python/1143-longest-common-subsequence.py @@ -0,0 +1,12 @@ +class Solution: + def longestCommonSubsequence(self, text1: str, text2: str) -> int: + dp = [[0 for j in range(len(text2) + 1)] for i in range(len(text1) + 1)] + + for i in range(len(text1) - 1, -1, -1): + for j in range(len(text2) - 1, -1, -1): + if text1[i] == text2[j]: + dp[i][j] = 1 + dp[i + 1][j + 1] + else: + dp[i][j] = max(dp[i][j + 1], dp[i + 1][j]) + + return dp[0][0] diff --git a/python/1189-maximum-number-of-balloons.py b/python/1189-maximum-number-of-balloons.py new file mode 100644 index 000000000..8095afef8 --- /dev/null +++ b/python/1189-maximum-number-of-balloons.py @@ -0,0 +1,12 @@ +from collections import Counter + + +class Solution: + def maxNumberOfBalloons(self, text: str) -> int: + countText = Counter(text) + balloon = Counter("balloon") + + res = len(text) # or float("inf") + for c in balloon: + res = min(res, countText[c] // balloon[c]) + return res diff --git a/python/1203-sort-items-by-groups-respecting-dependencies.py b/python/1203-sort-items-by-groups-respecting-dependencies.py new file mode 100644 index 000000000..7da80aed8 --- /dev/null +++ b/python/1203-sort-items-by-groups-respecting-dependencies.py @@ -0,0 +1,63 @@ +from collections import defaultdict, deque +from typing import List +class Solution: + # This function performs topological sort on a directed graph represented by successors and predecessors_count arrays. + def topologicalSort(self, successors: List[List[int]], predecessors_count: List[int], num_nodes: int) -> List[int]: + order = [] # To store the topologically sorted nodes + # Initialize a deque with all nodes that have no predecessors (i.e., in-degree of 0) + nodes_with_no_predecessors = deque(node for node in range(num_nodes) if not predecessors_count[node]) + + while nodes_with_no_predecessors: # Process nodes while there are nodes without predecessors + node = nodes_with_no_predecessors.popleft() # Get the node with no predecessors + order.append(node) # Add the node to the sorted order + for successor in successors[node]: # For each successor of the current node + predecessors_count[successor] -= 1 # Decrement the in-degree of the successor + if not predecessors_count[successor]: # If the successor now has no predecessors + nodes_with_no_predecessors.append(successor) # Add it to the queue for processing + + # If the number of nodes in the order is less than the total number of nodes, a cycle was detected + return order if len(order) == num_nodes else [] # Return the order if all nodes were sorted, else return empty list + + def sortItems(self, n: int, m: int, group: List[int], beforeItems: List[List[int]]) -> List[int]: + # Step 1: Assign unique group IDs to items that don't belong to any group + for item in range(n): + if group[item] == -1: # If the item doesn't belong to any group + group[item] = m # Assign a new group ID + m += 1 # Increment the group ID for the next item + + # Step 2: Initialize graphs for item dependencies and group dependencies + successors_group, successors_item = [[] for _ in range(m)], [[] for _ in range(n)] # Graphs for group and item dependencies + predecessors_count_group, predecessors_count_item = [0] * m, [0] * n # Count of incoming edges (predecessors) for each group and item + + # Step 3: Build the dependency graphs based on beforeItems + for item in range(n): + current_group = group[item] # Get the group of the current item + for before in beforeItems[item]: # Process each item that should come before the current item + before_group = group[before] # Get the group of the item that should come before + + if current_group == before_group: # If the two items belong to the same group + successors_item[before].append(item) # Add a dependency from 'before' to the current item + predecessors_count_item[item] += 1 # Increment the in-degree of the current item + else: # If the items belong to different groups + successors_group[before_group].append(current_group) # Add a group dependency + predecessors_count_group[current_group] += 1 # Increment the in-degree of the current group + + # Step 4: Perform topological sort on both the group dependencies and item dependencies + groups_order = self.topologicalSort(successors_group, predecessors_count_group, m) # Topological sort of groups + items_order = self.topologicalSort(successors_item, predecessors_count_item, n) # Topological sort of items + + # Step 5: If there was a cycle detected in either group or item sorting, return an empty list + if not groups_order or not items_order: + return [] # Return an empty list if either the group or item topological sort failed + + # Step 6: Group the items based on the group IDs + items_grouped = [[] for _ in range(m)] # Create an empty list for each group to store its items + for item in items_order: # Process each item in topologically sorted order + items_grouped[group[item]].append(item) # Add the item to the appropriate group + + # Step 7: Combine the groups in topologically sorted order + result = [] # The final result list to store the sorted items + for grp in groups_order: # For each group in topologically sorted order + result.extend(items_grouped[grp]) # Add the items of the group to the result + + return result # Return the final sorted list of items respecting both item and group dependencies diff --git a/python/1209-remove-all-adjacent-duplicates-in-string-ii.py b/python/1209-remove-all-adjacent-duplicates-in-string-ii.py new file mode 100644 index 000000000..4356c4c28 --- /dev/null +++ b/python/1209-remove-all-adjacent-duplicates-in-string-ii.py @@ -0,0 +1,18 @@ +class Solution: + def removeDuplicates(self, s: str, k: int) -> str: + stack = [] # [char, count] + + for c in s: + if stack and stack[-1][0] == c: + stack[-1][1] += 1 + else: + stack.append([c, 1]) + + if stack[-1][1] == k: + stack.pop() + + res = "" + for char, count in stack: + res += char * count + + return res diff --git a/python/1220-count-vowels-permutation.py b/python/1220-count-vowels-permutation.py new file mode 100644 index 000000000..845da7a03 --- /dev/null +++ b/python/1220-count-vowels-permutation.py @@ -0,0 +1,39 @@ +class Solution: + Memo = {} + def countVowelPermutation(self, n, c = '') -> int: + if (c, n) in self.Memo: + return self.Memo[(c, n)] + if n == 1: + if c == 'a': + return 1 + if c == 'e': + return 2 + if c == 'i': + return 4 + if c == 'o': + return 2 + if c == 'u': + return 1 + if c == '': + return 5 + else: + if c == 'a': + self.Memo[('a', n)] = self.countVowelPermutation(n - 1, 'e') + return self.Memo[('a', n)] + if c == 'e': + self.Memo[('e', n)] = self.countVowelPermutation(n - 1, 'a') + self.countVowelPermutation(n - 1, 'i') + return self.Memo[('e', n)] + if c == 'i': + self.Memo[('i', n)] = self.countVowelPermutation(n - 1, 'a') + self.countVowelPermutation(n - 1, 'e') + self.countVowelPermutation(n - 1, 'o') + self.countVowelPermutation(n - 1, 'u') + return self.Memo[('i', n)] + if c == 'o': + self.Memo[('o', n)] = self.countVowelPermutation(n - 1, 'i') + self.countVowelPermutation(n - 1, 'u') + return self.Memo[('o', n)] + if c == 'u': + self.Memo[('u', n)] = self.countVowelPermutation(n - 1, 'a') + return self.Memo[('u', n)] + if c == '': + Tot = 0 + for i in ['a', 'e', 'i', 'o', 'u']: + Tot = Tot + self.countVowelPermutation(n - 1, i); + return Tot % 1000000007 diff --git a/python/1239-maximum-length-of-a-concatenated-string-with-unique-characters.py b/python/1239-maximum-length-of-a-concatenated-string-with-unique-characters.py new file mode 100644 index 000000000..559af2295 --- /dev/null +++ b/python/1239-maximum-length-of-a-concatenated-string-with-unique-characters.py @@ -0,0 +1,27 @@ +class Solution: + def maxLength(self, arr: List[str]) -> int: + charSet = set() + + def overlap(charSet, s): + c = Counter(charSet) + Counter(s) + return max(c.values()) > 1 + # prev = set() + # for c in s: + # if c in charSet or c in prev: + # return True + # prev.add(c) + # return False + + def backtrack(i): + if i == len(arr): + return len(charSet) + res = 0 + if not overlap(charSet, arr[i]): + for c in arr[i]: + charSet.add(c) + res = backtrack(i + 1) + for c in arr[i]: + charSet.remove(c) + return max(res, backtrack(i + 1)) # dont concatenate arr[i] + + return backtrack(0) diff --git a/python/1254-number-of-closed-islands.py b/python/1254-number-of-closed-islands.py new file mode 100644 index 000000000..03c40db58 --- /dev/null +++ b/python/1254-number-of-closed-islands.py @@ -0,0 +1,27 @@ +class Solution: + def closedIsland(self, grid: List[List[int]]) -> int: + r = len(grid) + c = len(grid[0]) + seen = set() + + def dfs(x, y): + if x < 0 or x >= r or y < 0 or y >= c or (x, y) in seen or grid[x][y] == 1: + return + seen.add((x, y)) + grid[x][y] = 1 + dfs(x+1, y) + dfs(x, y+1) + dfs(x-1, y) + dfs(x, y-1) + + for i in range(r): + for j in range(c): + if i == 0 or j == 0 or i == r-1 or j == c-1: + dfs(i, j) + ans = 0 + for i in range(r): + for j in range(c): + if grid[i][j] == 0: + dfs(i, j) + ans += 1 + return ans \ No newline at end of file diff --git a/python/1260-shift-2d-grid.py b/python/1260-shift-2d-grid.py new file mode 100644 index 000000000..2e5cbbaf5 --- /dev/null +++ b/python/1260-shift-2d-grid.py @@ -0,0 +1,16 @@ +class Solution: + def shiftGrid(self, grid: List[List[int]], k: int) -> List[List[int]]: + M, N = len(grid), len(grid[0]) + + def posToVal(r, c): + return r * N + c + def valToPos(v): + return [v // N, v % N] # r, c + + res = [[0] * N for i in range(M)] + for r in range(M): + for c in range(N): + newVal = (posToVal(r, c) + k) % (M * N) + newR, newC = valToPos(newVal) + res[newR][newC] = grid[r][c] + return res diff --git a/python/1288-remove-covered-intervals.py b/python/1288-remove-covered-intervals.py new file mode 100644 index 000000000..7c961ee3c --- /dev/null +++ b/python/1288-remove-covered-intervals.py @@ -0,0 +1,14 @@ +class Solution: + def removeCoveredIntervals(self, intervals: List[List[int]]) -> int: + # sort on the basis of inc li first and then on the basis of dec length (=> -ri) + intervals.sort(key=lambda x: (x[0], -x[1])) + + covered, maxri = 0, 0 + + for _, ri in intervals: + if ri > maxri: + maxri = ri + else: + covered += 1 + + return len(intervals) - covered \ No newline at end of file diff --git a/python/1299-replace-elements-with-greatest-element-on-right-side.py b/python/1299-replace-elements-with-greatest-element-on-right-side.py new file mode 100644 index 000000000..ff92025fb --- /dev/null +++ b/python/1299-replace-elements-with-greatest-element-on-right-side.py @@ -0,0 +1,8 @@ +class Solution: + def replaceElements(self, arr: List[int]) -> List[int]: + rightMax = -1 + for i in range(len(arr) -1, -1, -1): + newMax = max(rightMax, arr[i]) + arr[i] = rightMax + rightMax = newMax + return arr diff --git a/python/1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.py b/python/1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.py new file mode 100644 index 000000000..8b9073b0d --- /dev/null +++ b/python/1343-number-of-sub-arrays-of-size-k-and-average-greater-than-or-equal-to-threshold.py @@ -0,0 +1,11 @@ +class Solution: + def numOfSubarrays(self, arr: List[int], k: int, threshold: int) -> int: + res = 0 + curSum = sum(arr[:k-1]) + + for L in range(len(arr) - k + 1): + curSum += arr[L + k - 1] + if (curSum / k) >= threshold: + res += 1 + curSum -= arr[L] + return res diff --git a/python/1345-jump-game-iv.py b/python/1345-jump-game-iv.py new file mode 100644 index 000000000..d755444ba --- /dev/null +++ b/python/1345-jump-game-iv.py @@ -0,0 +1,53 @@ + +from collections import defaultdict, deque +from typing import List + + +class Solution: + # Time O(n) - Space O(n) + def minJumps(self, arr: List[int]) -> int: + n = len(arr) + # Base case. + if n < 2: + return 0 + # A dictionary of vertices indexed by values. + d = defaultdict(list) + for i in reversed(range(n)): + d[arr[i]].append(i) + + # A function that gets all neighbors of a node that we have not + # queued yet. + def getUnqueuedNeighbors(i: int) -> List[int]: + adj = [] + # We can reach the element before. + if 0 < i and not seen[i - 1]: + seen[i - 1] = True + adj.append(i - 1) + # We can reach the element after. + if i < n - 1 and not seen[i + 1]: + seen[i + 1] = True + adj.append(i + 1) + # We can also reach any element with the same value. + if arr[i] in d: + for node in d[arr[i]]: + if node != i: + adj.append(node) + seen[node] = True + d.pop(arr[i]) + return adj + + # A list of nodes that we have visited already. + seen = [False] * n + seen[0] = True + # BFS starting at 0 and counting the steps until we reach n-1. + steps, level = 0, deque([0]) + while level: + steps += 1 + # Process an entire level. + for _ in range(len(level)): + current = level.popleft() + for nei in getUnqueuedNeighbors(current): + # If this is the target node, return. + if nei == n - 1: + return steps + level.append(nei) diff --git a/python/1383-maximum-performance-of-a-team.py b/python/1383-maximum-performance-of-a-team.py new file mode 100644 index 000000000..b093b0c9c --- /dev/null +++ b/python/1383-maximum-performance-of-a-team.py @@ -0,0 +1,18 @@ +class Solution: + def maxPerformance(self, n: int, speed: List[int], efficiency: List[int], k: int) -> int: + mod = 10 ** 9 + 7 + eng = [] + for eff, spd in zip(efficiency, speed): + eng.append([eff, spd]) + eng.sort(reverse = True) + + res, speed = 0, 0 + minHeap = [] + + for eff, spd in eng: + if len(minHeap) == k: + speed -= heapq.heappop(minHeap) + speed += spd + heapq.heappush(minHeap, spd) + res = max(res, eff * speed) + return res % mod diff --git a/python/1396-design-underground-system.py b/python/1396-design-underground-system.py new file mode 100644 index 000000000..ec2e7b8dd --- /dev/null +++ b/python/1396-design-underground-system.py @@ -0,0 +1,25 @@ +class UndergroundSystem: + def __init__(self): + # save as {customer_id: (stationName, t)} + self.customer = {} + + # save time travel for route = (src,dst) each time a customer checkout + # save as {(src, dst): (total_time, count)} + self.time = {} + + def checkIn(self, id: int, stationName: str, t: int) -> None: + self.customer[id] = (stationName, t) + + def checkOut(self, id: int, stationName: str, t: int) -> None: + start, time = self.customer[id] + route = (start, stationName) + + # save new entity to time table + if route not in self.time: self.time[route] = [0,0] + self.time[route][0] += t - time + self.time[route][1] += 1 + + + def getAverageTime(self, startStation: str, endStation: str) -> float: + total, count = self.time[(startStation, endStation)] + return total / count diff --git a/python/1397-find-all-good-strings.py b/python/1397-find-all-good-strings.py new file mode 100644 index 000000000..8e628b96f --- /dev/null +++ b/python/1397-find-all-good-strings.py @@ -0,0 +1,48 @@ +class Solution: + def findGoodStrings(self, n: int, s1: str, s2: str, evil: str) -> int: + a = ord('a') + z = ord('z') + + arr_e = list(map(ord, evil)) + len_e = len(evil) + next = [0] * len_e + + for i in range(1, len_e): + j = next[i - 1] + while j > 0 and evil[i] != evil[j]: + j = next[j - 1] + if evil[i] == evil[j]: + next[i] = j + 1 + + def good(s): + arr = list(map(ord, s)) + len_a = len(arr) + + @cache + def f(i, skip, reach, e): + if e == len_e: + return 0 + if i == len_a: + return 0 if skip else 1 + + limit = arr[i] if reach else z + ans = 0 + + if skip: + ans += f(i + 1, True, False, 0) + + for c in range(a, limit + 1): + ee = e + while ee > 0 and arr_e[ee] != c: + ee = next[ee - 1] + + if arr_e[ee] == c: + ee += 1 + + ans += f(i + 1, False, reach and c == limit, ee) + + return ans % int(1e9 + 7) + + return f(0, True, True, 0) + + return (good(s2) - good(s1) + int(evil not in s1)) % int(1e9 + 7) diff --git a/python/1423-maximum-points-you-can-obtain-from-cards.py b/python/1423-maximum-points-you-can-obtain-from-cards.py new file mode 100644 index 000000000..bf6fa7c26 --- /dev/null +++ b/python/1423-maximum-points-you-can-obtain-from-cards.py @@ -0,0 +1,11 @@ +class Solution: + def maxScore(self, cardPoints: List[int], k: int) -> int: + n = len(cardPoints) + + score = maxScore = sum(cardPoints[:k]) + + for i in range(1, k + 1): + score += cardPoints[-i] - cardPoints[k - i] + maxScore = max(maxScore, score) + + return maxScore diff --git a/python/1448-count-good-nodes-in-binary-tree.py b/python/1448-count-good-nodes-in-binary-tree.py new file mode 100644 index 000000000..4fce28f4e --- /dev/null +++ b/python/1448-count-good-nodes-in-binary-tree.py @@ -0,0 +1,19 @@ +# Definition for a binary tree node. +# class TreeNode: +# def __init__(self, val=0, left=None, right=None): +# self.val = val +# self.left = left +# self.right = right +class Solution: + def goodNodes(self, root: TreeNode) -> int: + def dfs(node, maxVal): + if not node: + return 0 + + res = 1 if node.val >= maxVal else 0 + maxVal = max(maxVal, node.val) + res += dfs(node.left, maxVal) + res += dfs(node.right, maxVal) + return res + + return dfs(root, root.val) diff --git a/python/1456-maximum-number-of-vowels-in-a-substring-of-given-length.py b/python/1456-maximum-number-of-vowels-in-a-substring-of-given-length.py new file mode 100644 index 000000000..bdfaed260 --- /dev/null +++ b/python/1456-maximum-number-of-vowels-in-a-substring-of-given-length.py @@ -0,0 +1,17 @@ +class Solution: + def maxVowels(self, s: str, k: int) -> int: + l, res, total = 0, 0, 0 + vowels = "aeiou" + + for r in range(len(s)): + if s[r] in vowels: + total += 1 + if (r - l + 1) > k: + if s[l] in vowels: + total -= 1 + l += 1 + res = max(res, total) + return res + + + diff --git a/python/1461-check-if-a-string-contains-all-binary-codes-of-size-k.py b/python/1461-check-if-a-string-contains-all-binary-codes-of-size-k.py new file mode 100644 index 000000000..7872e2841 --- /dev/null +++ b/python/1461-check-if-a-string-contains-all-binary-codes-of-size-k.py @@ -0,0 +1,3 @@ +class Solution: + def hasAllCodes(self, s: str, k: int) -> bool: + return len(set(s[i : i + k] for i in range(len(s) - k + 1))) == 2**k diff --git a/python/1462-course-schedule-iv.py b/python/1462-course-schedule-iv.py new file mode 100644 index 000000000..8df071ebd --- /dev/null +++ b/python/1462-course-schedule-iv.py @@ -0,0 +1,22 @@ +class Solution: + def checkIfPrerequisite(self, numCourses: int, prerequisites: List[List[int]], queries: List[List[int]]) -> List[bool]: + adj = defaultdict(list) + for prereq, crs in prerequisites: + adj[crs].append(prereq) + + def dfs(crs): + if crs not in prereqMap: + prereqMap[crs] = set() + for pre in adj[crs]: + prereqMap[crs] |= dfs(pre) + prereqMap[crs].add(crs) + return prereqMap[crs] + + prereqMap = {} # map course -> set indirect prereqs + for crs in range(numCourses): + dfs(crs) + + res = [] + for u, v in queries: + res.append(u in prereqMap[v]) + return res diff --git a/python/1464-maximum-product-of-two-elements-in-an-array.py b/python/1464-maximum-product-of-two-elements-in-an-array.py new file mode 100644 index 000000000..70800a68c --- /dev/null +++ b/python/1464-maximum-product-of-two-elements-in-an-array.py @@ -0,0 +1,10 @@ +class Solution: + def maxProduct(self, nums: List[int]) -> int: + high = secondHigh = 0 + for n in nums: + if n > high: + secondHigh = high + high = n + else: + secondHigh = max(n, secondHigh) + return (high - 1) * (secondHigh - 1) diff --git a/python/1466-reorder-routes-to-make-all-paths-lead-to-the-city-zero.py b/python/1466-reorder-routes-to-make-all-paths-lead-to-the-city-zero.py new file mode 100644 index 000000000..b87a80e8d --- /dev/null +++ b/python/1466-reorder-routes-to-make-all-paths-lead-to-the-city-zero.py @@ -0,0 +1,25 @@ +class Solution: + def minReorder(self, n: int, connections: List[List[int]]) -> int: + edges = {(a,b) for a, b in connections} + neighbors = defaultdict(list) + visit = set() + changes = 0 + + for a, b in connections: + neighbors[a].append(b) + neighbors[b].append(a) + + def dfs(city): + nonlocal changes + + for neighbor in neighbors[city]: + if neighbor in visit: + continue + # check if this neighbor can reach city 0 + if (neighbor, city) not in edges: + changes += 1 + visit.add(neighbor) + dfs(neighbor) + visit.add(0) + dfs(0) + return changes diff --git a/python/1472-design-browser-history.py b/python/1472-design-browser-history.py new file mode 100644 index 000000000..7cc268ec6 --- /dev/null +++ b/python/1472-design-browser-history.py @@ -0,0 +1,56 @@ +# Linked List Implementation +class ListNode: + def __init__(self, val, prev=None, next=None): + self.val = val + self.prev = prev + self.next = next + +class BrowserHistory: + def __init__(self, homepage: str): + self.cur = ListNode(homepage) + + # O(1) + def visit(self, url: str) -> None: + self.cur.next = ListNode(url, self.cur) + self.cur = self.cur.next + + # O(n) + def back(self, steps: int) -> str: + while self.cur.prev and steps > 0: + self.cur = self.cur.prev + steps -= 1 + return self.cur.val + + # O(n) + def forward(self, steps: int) -> str: + while self.cur.next and steps > 0: + self.cur = self.cur.next + steps -= 1 + return self.cur.val + + +# Array Implementation +class BrowserHistory: + def __init__(self, homepage: str): + self.i = 0 + self.len = 1 + self.history = [homepage] + + # O(1) + def visit(self, url: str) -> None: + if len(self.history) < self.i + 2: + self.history.append(url) + else: + self.history[self.i + 1] = url + self.i += 1 + self.len = self.i + 1 + + # O(1) + def back(self, steps: int) -> str: + self.i = max(self.i - steps, 0) + return self.history[self.i] + + # O(1) + def forward(self, steps: int) -> str: + self.i = min(self.i + steps, self.len - 1) + return self.history[self.i] diff --git a/python/1475-final-prices-with-a-special-discount-in-a-shop.py b/python/1475-final-prices-with-a-special-discount-in-a-shop.py new file mode 100644 index 000000000..00d1f567b --- /dev/null +++ b/python/1475-final-prices-with-a-special-discount-in-a-shop.py @@ -0,0 +1,19 @@ +class Solution: + def finalPrices(self, prices: List[int]) -> List[int]: + stack = [] + res = [] + for i in range(len(prices) - 1, -1, -1): + if len(stack) == 0: + res.append(prices[i]) + elif len(stack) and stack[-1] <= prices[i]: + res.append(prices[i] - stack[-1]) + elif len(stack) and stack[-1] > prices[i]: + while len(stack) and stack[-1] > prices[i]: + stack.pop() + if len(stack) == 0: + res.append(prices[i]) + else: + res.append(prices[i] - stack[-1]) + stack.append(prices[i]) + res.reverse() + return res diff --git a/python/1481-least-number-of-unique-integers-after-k-removals.py b/python/1481-least-number-of-unique-integers-after-k-removals.py new file mode 100644 index 000000000..488a00665 --- /dev/null +++ b/python/1481-least-number-of-unique-integers-after-k-removals.py @@ -0,0 +1,35 @@ +# Use a heap +class Solution: + def findLeastNumOfUniqueInts(self, arr: List[int], k: int) -> int: + freq = Counter(arr) + heap = list(freq.values()) + heapq.heapify(heap) + + res = len(heap) + while k > 0 and heap: + f = heapq.heappop(heap) + if k >= f: + k -= f + res -= 1 + return res + +# Use buckets +class Solution: + def findLeastNumOfUniqueInts(self, arr: List[int], k: int) -> int: + freq = Counter(arr) + freqList = [0] * (len(arr) + 1) + + for n, f in freq.items(): + freqList[f] += 1 + + res = len(freq) + for f in range(1, len(freqList)): + remove = freqList[f] + if k >= f * remove: + k -= f * remove + res -= remove + else: + remove = k // f + res -= remove + break + return res diff --git a/python/1489-find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.py b/python/1489-find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.py new file mode 100644 index 000000000..cf2f95b54 --- /dev/null +++ b/python/1489-find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.py @@ -0,0 +1,59 @@ +class UnionFind: + def __init__(self, n): + self.par = [i for i in range(n)] + self.rank = [1] * n + + def find(self, v1): + while v1 != self.par[v1]: + self.par[v1] = self.par[self.par[v1]] + v1 = self.par[v1] + return v1 + + def union(self, v1, v2): + p1, p2 = self.find(v1), self.find(v2) + if p1 == p2: + return False + if self.rank[p1] > self.rank[p2]: + self.par[p2] = p1 + self.rank[p1] += self.rank[p2] + else: + self.par[p1] = p2 + self.rank[p2] += self.rank[p1] + return True + +class Solution: + def findCriticalAndPseudoCriticalEdges(self, n: int, edges: List[List[int]]) -> List[List[int]]: + # Time: O(E^2) - UF operations are assumed to be approx O(1) + for i, e in enumerate(edges): + e.append(i) # [v1, v2, weight, original_index] + + edges.sort(key=lambda e: e[2]) + + mst_weight = 0 + uf = UnionFind(n) + for v1, v2, w, i in edges: + if uf.union(v1, v2): + mst_weight += w + + critical, pseudo = [], [] + for n1, n2, e_weight, i in edges: + # Try without curr edge + weight = 0 + uf = UnionFind(n) + for v1, v2, w, j in edges: + if i != j and uf.union(v1, v2): + weight += w + if max(uf.rank) != n or weight > mst_weight: + critical.append(i) + continue + + # Try with curr edge + uf = UnionFind(n) + uf.union(n1, n2) + weight = e_weight + for v1, v2, w, j in edges: + if uf.union(v1, v2): + weight += w + if weight == mst_weight: + pseudo.append(i) + return [critical, pseudo] diff --git a/python/1498-number-of-subsequences-that-satisfy-the-given-sum-condition.py b/python/1498-number-of-subsequences-that-satisfy-the-given-sum-condition.py new file mode 100644 index 000000000..5c3c71b45 --- /dev/null +++ b/python/1498-number-of-subsequences-that-satisfy-the-given-sum-condition.py @@ -0,0 +1,17 @@ +# Time Complexity - O(nlogn) +# Space Complexity - O(1) + +class Solution: + def numSubseq(self, nums: List[int], target: int) -> int: + nums.sort() + + res, mod = 0, (10**9 + 7) + + left, right = 0, len(nums) - 1 + while left <= right: + if (nums[left] + nums[right]) > target: + right -= 1 + else: + res += 1 << (right - left) + left += 1 + return res % mod diff --git a/python/1514-path-with-maximum-probability.py b/python/1514-path-with-maximum-probability.py new file mode 100644 index 000000000..3f648b034 --- /dev/null +++ b/python/1514-path-with-maximum-probability.py @@ -0,0 +1,21 @@ +class Solution: + def maxProbability(self, n: int, edges: List[List[int]], succProb: List[float], start: int, end: int) -> float: + adj = collections.defaultdict(list) + for i in range(len(edges)): + src, dst = edges[i] + adj[src].append([dst, succProb[i]]) + adj[dst].append([src, succProb[i]]) + + pq = [(-1, start)] + visit = set() + + while pq: + prob, cur = heapq.heappop(pq) + visit.add(cur) + + if cur == end: + return prob * -1 + for nei, edgeProb in adj[cur]: + if nei not in visit: + heapq.heappush(pq, (prob * edgeProb, nei)) + return 0 diff --git a/python/1523-count-odd-numbers-in-an-interval-range.py b/python/1523-count-odd-numbers-in-an-interval-range.py new file mode 100644 index 000000000..b1834e36b --- /dev/null +++ b/python/1523-count-odd-numbers-in-an-interval-range.py @@ -0,0 +1,5 @@ +class Solution: + def countOdds(self, low: int, high: int) -> int: + if low%2!=0 or high%2!=0: + return (high-low)//2 +1 + return (high-low)//2 diff --git a/python/1572-matrix-diagonal-sum.py b/python/1572-matrix-diagonal-sum.py new file mode 100644 index 000000000..ef72ab9eb --- /dev/null +++ b/python/1572-matrix-diagonal-sum.py @@ -0,0 +1,24 @@ + +class Solution: + def PrimeSum(self,mat): + cnt = 0 + for i in range(len(mat)): + cnt += mat[i][i] + return cnt + + def CrossSum(self,mat): + cnt = 0 + for i in range(len(mat)): + cnt += mat[i][len(mat) - i - 1] + return cnt + + def diagonalSum(self, mat: List[List[int]]) -> int: + prime = self.PrimeSum(mat) + cross = self.CrossSum(mat) + + if len(mat) % 2 == 0: + return prime + cross + else: + mid = len(mat) // 2 + mid_ele = mat[mid][mid] + return prime + cross - mid_ele diff --git a/python/1582-special-positions-in-a-binary-matrix.py b/python/1582-special-positions-in-a-binary-matrix.py new file mode 100644 index 000000000..5e0547784 --- /dev/null +++ b/python/1582-special-positions-in-a-binary-matrix.py @@ -0,0 +1,18 @@ +class Solution: + def numSpecial(self, mat: List[List[int]]) -> int: + m = len(mat) + n = len(mat[0]) + rowCount = [0] * m + colCount = [0] * n + res = 0 + for r in range(m): + for c in range(n): + if mat[r][c] == 1: + rowCount[r] += 1 + colCount[c] += 1 + for r in range(m): + for c in range(n): + if mat[r][c] == 1 and rowCount[r] == 1 and colCount[c] == 1: + res += 1 + return res + diff --git a/python/1584-min-cost-to-connect-all-points.py b/python/1584-min-cost-to-connect-all-points.py new file mode 100644 index 000000000..0ad2e2fc0 --- /dev/null +++ b/python/1584-min-cost-to-connect-all-points.py @@ -0,0 +1,26 @@ +class Solution: + def minCostConnectPoints(self, points: List[List[int]]) -> int: + N = len(points) + adj = {i: [] for i in range(N)} # i : list of [cost, node] + for i in range(N): + x1, y1 = points[i] + for j in range(i + 1, N): + x2, y2 = points[j] + dist = abs(x1 - x2) + abs(y1 - y2) + adj[i].append([dist, j]) + adj[j].append([dist, i]) + + # Prim's + res = 0 + visit = set() + minH = [[0, 0]] # [cost, point] + while len(visit) < N: + cost, i = heapq.heappop(minH) + if i in visit: + continue + res += cost + visit.add(i) + for neiCost, nei in adj[i]: + if nei not in visit: + heapq.heappush(minH, [neiCost, nei]) + return res diff --git a/python/1603-design-parking-system.py b/python/1603-design-parking-system.py new file mode 100644 index 000000000..498d32e01 --- /dev/null +++ b/python/1603-design-parking-system.py @@ -0,0 +1,17 @@ +class ParkingSystem: + + def __init__(self, big: int, medium: int, small: int): + + # [total_occupied, max_capacity] + self.parking = { + 1: [0 ,big], + 2: [0, medium], + 3: [0, small] + } + + def addCar(self, carType: int) -> bool: + new_total = self.parking[carType][0] + 1 + if new_total <= self.parking[carType][1]: + self.parking[carType][0] += 1 + return True + return False diff --git a/python/1609-even-odd-tree.py b/python/1609-even-odd-tree.py new file mode 100644 index 000000000..555dcb0e4 --- /dev/null +++ b/python/1609-even-odd-tree.py @@ -0,0 +1,22 @@ +class Solution: + def isEvenOddTree(self, root: Optional[TreeNode]) -> bool: + even = True + q = deque([root]) + + while q: + prev = float("-inf") if even else float("inf") + for _ in range(len(q)): + node = q.popleft() + + if even and (node.val % 2 == 0 or node.val <= prev): + return False + elif not even and (node.val % 2 == 1 or node.val >= prev): + return False + + if node.left: + q.append(node.left) + if node.right: + q.append(node.right) + prev = node.val + even = not even + return True diff --git a/python/1631-path-with-minimum-effort.py b/python/1631-path-with-minimum-effort.py new file mode 100644 index 000000000..64aeb097d --- /dev/null +++ b/python/1631-path-with-minimum-effort.py @@ -0,0 +1,28 @@ +class Solution: + def minimumEffortPath(self, heights: List[List[int]]) -> int: + m, n = len(heights), len(heights[0]) + + efforts = [[float('inf')] * n for _ in range(m)] + directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] + + efforts[0][0] = 0 + pq = [(0, 0, 0)] # (effort, row, col) + + while pq: + curEffort, i, j = heapq.heappop(pq) + + # reached the bottom-right corner => return the effort + if i == m - 1 and j == n - 1: + return curEffort + + for dx, dy in directions: + x, y = i + dx, j + dy + + if 0 <= x < m and 0 <= y < n: + newEffort = max(abs(heights[x][y] - heights[i][j]), curEffort) + + if newEffort < efforts[x][y]: + efforts[x][y] = newEffort + heapq.heappush(pq, (newEffort, x, y)) + + return efforts[m - 1][n - 1] \ No newline at end of file diff --git a/python/1642-furthest-building-you-can-reach.py b/python/1642-furthest-building-you-can-reach.py new file mode 100644 index 000000000..084ae9dd5 --- /dev/null +++ b/python/1642-furthest-building-you-can-reach.py @@ -0,0 +1,19 @@ +class Solution: + def furthestBuilding(self, heights: List[int], bricks: int, ladders: int) -> int: + heap = [] + + for i in range(len(heights) - 1): + diff = heights[i + 1] - heights[i] + if diff <= 0: + continue + + bricks -= diff + heapq.heappush(heap, -diff) + + if bricks < 0: + if ladders == 0: + return i + ladders -= 1 + bricks += -heapq.heappop(heap) + + return len(heights) - 1 diff --git a/python/1658-minimum-operations-to-reduce-x-to-zero.py b/python/1658-minimum-operations-to-reduce-x-to-zero.py new file mode 100644 index 000000000..6bfe65343 --- /dev/null +++ b/python/1658-minimum-operations-to-reduce-x-to-zero.py @@ -0,0 +1,28 @@ +class Solution: + def minOperations(self, nums: List[int], x: int) -> int: + # determine sum of subarray remaining after reduction + target_sum = sum(nums) - x + # check that x could be reduced to zero + if target_sum < 0: + return -1 + + n = len(nums) + min_ops = -1 + + # sliding window technique used to find candidate subarrays + left = 0 + right = 0 + curr_sum = 0 + while right < n: + curr_sum += nums[right] + right += 1 + + while left < n and curr_sum > target_sum: + curr_sum -= nums[left] + left += 1 + + if curr_sum == target_sum: + ops = n - (right - left) # determine no. of operations used in reduction of nums to candidate subarray + min_ops = ops if min_ops == -1 else min(min_ops, ops) # determine if candidate is best candidate thus far + + return min_ops # return best candidate \ No newline at end of file diff --git a/python/1669-merge-in-between-linked-lists.py b/python/1669-merge-in-between-linked-lists.py new file mode 100644 index 000000000..eb02828ff --- /dev/null +++ b/python/1669-merge-in-between-linked-lists.py @@ -0,0 +1,18 @@ +class Solution: + def mergeInBetween(self, list1: ListNode, a: int, b: int, list2: ListNode) -> ListNode: + curr = list1 + i = 0 + while i < a - 1: + curr = curr.next + i += 1 + + head = curr + while i <= b: + curr = curr.next + i += 1 + head.next = list2 + + while list2.next: + list2 = list2.next + list2.next = curr + return list1 diff --git a/python/1700-number-of-students-unable-to-eat-lunch.py b/python/1700-number-of-students-unable-to-eat-lunch.py new file mode 100644 index 000000000..62a48a14c --- /dev/null +++ b/python/1700-number-of-students-unable-to-eat-lunch.py @@ -0,0 +1,12 @@ +class Solution: + def countStudents(self, students: List[int], sandwiches: List[int]) -> int: + num_of_students_back_in_line = 0 + while num_of_students_back_in_line != len(students): + curr_student = students.pop(0) + if curr_student == sandwiches[0]: + sandwiches.pop(0) + num_of_students_back_in_line = 0 + else: + students.append(curr_student) + num_of_students_back_in_line += 1 + return len(students) diff --git a/python/1721-swapping-nodes-in-a-linked-list.py b/python/1721-swapping-nodes-in-a-linked-list.py new file mode 100644 index 000000000..c8ca7d993 --- /dev/null +++ b/python/1721-swapping-nodes-in-a-linked-list.py @@ -0,0 +1,20 @@ +# Definition for singly-linked list. +# class ListNode: +# def __init__(self, val=0, next=None): +# self.val = val +# self.next = next +class Solution: + def swapNodes(self, head: Optional[ListNode], k: int) -> Optional[ListNode]: + right_pointer = head + for _ in range(1, k): + right_pointer = right_pointer.next + left_kth_node = right_pointer + + left_pointer = head + while right_pointer is not None: + right_kth_node = left_pointer + right_pointer = right_pointer.next + left_pointer = left_pointer.next + + left_kth_node.val, right_kth_node.val = right_kth_node.val, left_kth_node.val + return head diff --git a/python/1750-minimum-length-of-string-after-deleting-similar-ends.py b/python/1750-minimum-length-of-string-after-deleting-similar-ends.py new file mode 100644 index 000000000..1d9ddd7a4 --- /dev/null +++ b/python/1750-minimum-length-of-string-after-deleting-similar-ends.py @@ -0,0 +1,11 @@ +class Solution: + def minimumLength(self, s: str) -> int: + l, r = 0, len(s) - 1 + + while l < r and s[l] == s[r]: + tmp = s[l] + while l <= r and s[l] == tmp: + l += 1 + while l <= r and s[r] == tmp: + r -= 1 + return (r - l + 1) diff --git a/python/1768-merge-strings-alternately.py b/python/1768-merge-strings-alternately.py new file mode 100644 index 000000000..467d0424e --- /dev/null +++ b/python/1768-merge-strings-alternately.py @@ -0,0 +1,14 @@ +class Solution: + def mergeAlternately(self, word1: str, word2: str) -> str: + i = j = 0 + res = [] + + while i < len(word1) and j < len(word2): + res.append(word1[i]) + res.append(word2[j]) + i += 1 + j += 1 + res.append(word1[i:]) + res.append(word2[j:]) + return ''.join(res) + diff --git a/python/1800-maximum-ascending-subarray-sum.py b/python/1800-maximum-ascending-subarray-sum.py new file mode 100644 index 000000000..604bf302d --- /dev/null +++ b/python/1800-maximum-ascending-subarray-sum.py @@ -0,0 +1,11 @@ +class Solution: + def maxAscendingSum(self, nums: List[int]) -> int: + curSum = results = nums[0] + + for i in range(1, len(nums)): + if nums[i] <= nums[i - 1]: + curSum = 0 + curSum += nums[i] + results = max(curSum, results) + + return results diff --git a/python/1822-sign-of-the-product-of-an-array.py b/python/1822-sign-of-the-product-of-an-array.py new file mode 100644 index 000000000..054b36451 --- /dev/null +++ b/python/1822-sign-of-the-product-of-an-array.py @@ -0,0 +1,10 @@ +class Solution: + def arraySign(self, nums: List[int]) -> int: + flag = True + for i in nums: + if i == 0: + return 0 + if i < 0: + flag = not flag + + return 1 if flag else -1 diff --git a/python/1834-single-threaded-cpu.py b/python/1834-single-threaded-cpu.py new file mode 100644 index 000000000..1a0a25db9 --- /dev/null +++ b/python/1834-single-threaded-cpu.py @@ -0,0 +1,19 @@ +class Solution: + def getOrder(self, tasks: List[List[int]]) -> List[int]: + tasks = sorted([(t[0], t[1], i) for i, t in enumerate(tasks)]) + result, heap = [], [] + cur_task_index = 0 + cur_time = tasks[0][0] + + while len(result) < len(tasks): + while (cur_task_index < len(tasks)) and (tasks[cur_task_index][0] <= cur_time): + heapq.heappush(heap, (tasks[cur_task_index][1], tasks[cur_task_index][2])) + cur_task_index += 1 + if heap: + time_difference, original_index = heapq.heappop(heap) + cur_time += time_difference + result.append(original_index) + elif cur_task_index < len(tasks): + cur_time = tasks[cur_task_index][0] + + return result diff --git a/python/1838-frequency-of-the-most-frequent-element.py b/python/1838-frequency-of-the-most-frequent-element.py new file mode 100644 index 000000000..2b97f2f85 --- /dev/null +++ b/python/1838-frequency-of-the-most-frequent-element.py @@ -0,0 +1,17 @@ +class Solution: + def maxFrequency(self, nums: List[int], k: int) -> int: + nums.sort() + + l, r = 0, 0 + res, total = 0, 0 + + while r < len(nums): + total += nums[r] + while nums[r] * (r - l + 1) > total + k: + total -= nums[l] + l += 1 + res = max(res, r - l + 1) + r += 1 + + return res + diff --git a/python/1845-seat-reservation-manager.py b/python/1845-seat-reservation-manager.py new file mode 100644 index 000000000..0b2badfe3 --- /dev/null +++ b/python/1845-seat-reservation-manager.py @@ -0,0 +1,12 @@ +import heapq + +class SeatManager: + + def __init__(self, n: int): + self.seats = [i for i in range(1, n + 1)] + + def reserve(self) -> int: + return heapq.heappop(self.seats) + + def unreserve(self, seatNumber: int) -> None: + heapq.heappush(self.seats, seatNumber) \ No newline at end of file diff --git a/python/1849-splitting-a-string-into-descending-consecutive-values.py b/python/1849-splitting-a-string-into-descending-consecutive-values.py new file mode 100644 index 000000000..104e7b6fd --- /dev/null +++ b/python/1849-splitting-a-string-into-descending-consecutive-values.py @@ -0,0 +1,18 @@ +class Solution: + def splitString(self, s: str) -> bool: + + def dfs(index, prev): + if index == len(s): + return True + + for j in range(index, len(s)): + val = int(s[index:j+1]) + if val + 1 == prev and dfs(j+1, val): + return True + return False + + for i in range(len(s) - 1): + val = int(s[:i + 1]) + if dfs(i+1, val): return True + + return False diff --git a/python/1851-minimum-interval-to-include-each-query.py b/python/1851-minimum-interval-to-include-each-query.py new file mode 100644 index 000000000..545cefaa2 --- /dev/null +++ b/python/1851-minimum-interval-to-include-each-query.py @@ -0,0 +1,16 @@ +class Solution: + def minInterval(self, intervals: List[List[int]], queries: List[int]) -> List[int]: + intervals.sort() + minHeap = [] + res = {} + i = 0 + for q in sorted(queries): + while i < len(intervals) and intervals[i][0] <= q: + l, r = intervals[i] + heapq.heappush(minHeap, (r - l + 1, r)) + i += 1 + + while minHeap and minHeap[0][1] < q: + heapq.heappop(minHeap) + res[q] = minHeap[0][0] if minHeap else -1 + return [res[q] for q in queries] diff --git a/python/1888-minimum-number-of-flips-to-make-the-binary-string-alternating.py b/python/1888-minimum-number-of-flips-to-make-the-binary-string-alternating.py new file mode 100644 index 000000000..5e3f25b34 --- /dev/null +++ b/python/1888-minimum-number-of-flips-to-make-the-binary-string-alternating.py @@ -0,0 +1,27 @@ +class Solution: + def minFlips(self, s: str) -> int: + n = len(s) + s = s + s + alt1, alt2 = "", "" + + for i in range(len(s)): + alt1 += "0" if i % 2 == 0 else "1" + alt2 += "1" if i % 2 == 0 else "0" + + res = float('inf') + diff1, diff2 = 0, 0 + l = 0 + for r in range(len(s)): + if s[r] != alt1[r]: + diff1 += 1 + if s[r] != alt2[r]: + diff2 += 1 + if (r - l + 1) > n: + if s[l] != alt1[l]: + diff1 -= 1 + if s[l] != alt2[l]: + diff2 -= 1 + l += 1 + if (r - l + 1) == n: + res = min(res, diff1, diff2) + return res diff --git a/python/1899-merge-triplets-to-form-target-triplet.py b/python/1899-merge-triplets-to-form-target-triplet.py new file mode 100644 index 000000000..830803939 --- /dev/null +++ b/python/1899-merge-triplets-to-form-target-triplet.py @@ -0,0 +1,11 @@ +class Solution: + def mergeTriplets(self, triplets: List[List[int]], target: List[int]) -> bool: + good = set() + + for t in triplets: + if t[0] > target[0] or t[1] > target[1] or t[2] > target[2]: + continue + for i, v in enumerate(t): + if v == target[i]: + good.add(i) + return len(good) == 3 diff --git a/python/1905-count-sub-islands.py b/python/1905-count-sub-islands.py new file mode 100644 index 000000000..4cb0b42e9 --- /dev/null +++ b/python/1905-count-sub-islands.py @@ -0,0 +1,33 @@ +class Solution: + def countSubIslands(self, grid1: List[List[int]], grid2: List[List[int]]) -> int: + ROWS, COLS = len(grid1), len(grid1[0]) + visit = set() + + def dfs(r, c): + if ( + r < 0 + or c < 0 + or r == ROWS + or c == COLS + or grid2[r][c] == 0 + or (r, c) in visit + ): + return True + + visit.add((r, c)) + res = True + if grid1[r][c] == 0: + res = False + + res = dfs(r - 1, c) and res + res = dfs(r + 1, c) and res + res = dfs(r, c - 1) and res + res = dfs(r, c + 1) and res + return res + + count = 0 + for r in range(ROWS): + for c in range(COLS): + if grid2[r][c] and (r, c) not in visit and dfs(r, c): + count += 1 + return count diff --git a/python/1929-concatenation-of-array.py b/python/1929-concatenation-of-array.py new file mode 100644 index 000000000..1bea002af --- /dev/null +++ b/python/1929-concatenation-of-array.py @@ -0,0 +1,7 @@ +class Solution: + def getConcatenation(self, nums: List[int]) -> List[int]: + ans = [] + for i in range(2): + for n in nums: + ans.append(n) + return ans diff --git a/python/1930-unique-length-3-palindromic-subsequences.py b/python/1930-unique-length-3-palindromic-subsequences.py new file mode 100644 index 000000000..7b7ff7ed3 --- /dev/null +++ b/python/1930-unique-length-3-palindromic-subsequences.py @@ -0,0 +1,8 @@ +class Solution: + def countPalindromicSubsequence(self, s: str) -> int: + count = 0 + chars = set(s) + for char in chars: + first,last = s.find(char),s.rfind(char) + count += len(set(s[first+1:last])) + return count \ No newline at end of file diff --git a/python/1958-check-if-move-is-legal.py b/python/1958-check-if-move-is-legal.py new file mode 100644 index 000000000..c6c0841eb --- /dev/null +++ b/python/1958-check-if-move-is-legal.py @@ -0,0 +1,23 @@ +class Solution: + def checkMove(self, board: List[List[str]], rMove: int, cMove: int, color: str) -> bool: + ROWS, COLS = len(board), len(board[0]) + direction = [[1, 0], [-1, 0], [0, 1], [0, -1], + [1, 1], [-1, -1], [1, -1], [-1, 1]] + board[rMove][cMove] = color + + def legal(row, col, color, direc): + dr, dc = direc + row, col = row + dr, col + dc + length = 1 + + while(0 <= row < ROWS and 0 <= col < COLS): + length += 1 + if board[row][col] == '.': return False + if board[row][col] == color: + return length >= 3 + row, col = row + dr, col + dc + return False + + for d in direction: + if legal(rMove, cMove, color, d): return True + return False diff --git a/python/1963-minimum-number-of-swaps-to-make-the-string-balanced.py b/python/1963-minimum-number-of-swaps-to-make-the-string-balanced.py new file mode 100644 index 000000000..e5f1c47d9 --- /dev/null +++ b/python/1963-minimum-number-of-swaps-to-make-the-string-balanced.py @@ -0,0 +1,13 @@ +class Solution: + def minSwaps(self, s: str) -> int: + extraClose, maxClose = 0, 0 + + for c in s: + if c == "[": + extraClose -= 1 + else: + extraClose += 1 + + maxClose = max(maxClose, extraClose) + + return (maxClose + 1) // 2 # Or math.ceil(maxClose / 2) diff --git a/python/1968-array-with-elements-not-equal-to-average-of-neighbors.py b/python/1968-array-with-elements-not-equal-to-average-of-neighbors.py new file mode 100644 index 000000000..d0d8436aa --- /dev/null +++ b/python/1968-array-with-elements-not-equal-to-average-of-neighbors.py @@ -0,0 +1,30 @@ +class Solution: + # Solution 1: Sort and fill + # Intuition: + # Ensure the below pattern (no Avg number can form at nums[i]: + # nums[i-1] < nums[i] > nums[i+1] + + # Input: nums = [6,2,0,9,7] + # Sorted nums = [0,2,6,7,9] + # 1st Filled arr = [0,_,2,_,6] + # 2nd Filled arr = [0,7,2,9,6] + + def rearrangeArray(self, nums: List[int]) -> List[int]: + nums.sort() + + i, j, n = 0, 0, len(nums) + ans = [0]*n + + while i < n and j < n: + ans[i] = nums[j] + i = i + 2 + j = j + 1 + + i = 1 + while i < n and j < n: + ans[i] = nums[j] + i = i + 2 + j = j + 1 + + return ans + \ No newline at end of file diff --git a/python/1980-find-unique-binary-string.py b/python/1980-find-unique-binary-string.py new file mode 100644 index 000000000..f91519ae3 --- /dev/null +++ b/python/1980-find-unique-binary-string.py @@ -0,0 +1,19 @@ +class Solution: + def findDifferentBinaryString(self, nums: List[str]) -> str: + + strSet = { s for s in nums } + + def backtrack(i, cur): + if i == len(nums): + res = "".join(cur) + return None if res in strSet else res + + res = backtrack(i+1, cur) + if res: return res + + cur[i] = "1" + res = backtrack(i+1, cur) + if res: return res + + return backtrack(0, ["0" for s in nums]) + diff --git a/python/1984-minimum-difference-between-highest-and-lowest-of-k-scores.py b/python/1984-minimum-difference-between-highest-and-lowest-of-k-scores.py new file mode 100644 index 000000000..de70bb4f9 --- /dev/null +++ b/python/1984-minimum-difference-between-highest-and-lowest-of-k-scores.py @@ -0,0 +1,10 @@ +class Solution: + def minimumDifference(self, nums: List[int], k: int) -> int: + nums.sort() + l, r = 0, k - 1 + res = float("inf") + + while r < len(nums): + res = min(res, nums[r] - nums[l]) + l, r = l + 1, r + 1 + return res diff --git a/python/1985-find-the-kth-largest-integer-in-the-array.py b/python/1985-find-the-kth-largest-integer-in-the-array.py new file mode 100644 index 000000000..338c279b0 --- /dev/null +++ b/python/1985-find-the-kth-largest-integer-in-the-array.py @@ -0,0 +1,8 @@ +class Solution: + def kthLargestNumber(self, nums: List[str], k: int) -> str: + maxHeap = [-int(n) for n in nums] + heapq.heapify(maxHeap) + while k>1: + heapq.heappop(maxHeap) + k-=1 + return str(-maxHeap[0]) diff --git a/python/2001-number-of-pairs-of-interchangeable-rectangles.py b/python/2001-number-of-pairs-of-interchangeable-rectangles.py new file mode 100644 index 000000000..61eda4bff --- /dev/null +++ b/python/2001-number-of-pairs-of-interchangeable-rectangles.py @@ -0,0 +1,13 @@ +class Solution: + def interchangeableRectangles(self, rectangles: List[List[int]]) -> int: + count = {} # { W / H : Count } + res = 0 + + for w, h in rectangles: + # Increment the count for the ratio + count[w / h] = 1 + count.get(w / h, 0) + + for c in count.values(): + res += (c * (c - 1)) // 2 + + return res diff --git a/python/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.py b/python/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.py new file mode 100644 index 000000000..8d415db6d --- /dev/null +++ b/python/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.py @@ -0,0 +1,30 @@ +""" +Time Complexity: O(2^N) +Space Complexity: O(2^N) +""" +class Solution: + def maxProduct(self, s): + n = len(s) + + first, last = [0]*(1< None: + self.ptsCount[tuple(point)] += 1 + self.pts.append(point) + + def count(self, point: List[int]) -> int: + res = 0 + px, py = point + for x, y in self.pts: + if (abs(py - y) != abs(px - x)) or x == px or y == py: + continue + res += self.ptsCount[(x, py)] * self.ptsCount[(px, y)] + return res diff --git a/python/2017-grid-game.py b/python/2017-grid-game.py new file mode 100644 index 000000000..edce10fdb --- /dev/null +++ b/python/2017-grid-game.py @@ -0,0 +1,12 @@ +# Time: O(n) Space: O(1) + +class Solution(object): + def gridGame(self, grid): + result = float("inf") + left, right = 0, sum(grid[0]) + + for a, b in zip(grid[0], grid[1]): + right -= a + result = min(result, max(left, right)) + left += b + return result diff --git a/python/2092-find-all-people-with-secret.py b/python/2092-find-all-people-with-secret.py new file mode 100644 index 000000000..833b50c98 --- /dev/null +++ b/python/2092-find-all-people-with-secret.py @@ -0,0 +1,25 @@ +class Solution: + def findAllPeople(self, n: int, meetings: List[List[int]], firstPerson: int) -> List[int]: + secrets = set([0, firstPerson]) + time_map = {} + + for src, dst, t in meetings: + if t not in time_map: + time_map[t] = defaultdict(list) + time_map[t][src].append(dst) + time_map[t][dst].append(src) + + def dfs(src, adj): + if src in visit: + return + visit.add(src) + secrets.add(src) + for nei in adj[src]: + dfs(nei, adj) + + for t in sorted(time_map.keys()): + visit = set() + for src in time_map[t]: + if src in secrets: + dfs(src, time_map[t]) + return list(secrets) diff --git a/python/2101-detonate-the-maximum-bombs.py b/python/2101-detonate-the-maximum-bombs.py new file mode 100644 index 000000000..f7351e8d6 --- /dev/null +++ b/python/2101-detonate-the-maximum-bombs.py @@ -0,0 +1,33 @@ +class Solution: + def maximumDetonation(self, bombs: List[List[int]]) -> int: + n = len(bombs) + graph = [[] for _ in range(n)] + + for i in range(n): + for j in range(n): + if i != j: + x1, y1, r1 = bombs[i] + x2, y2, _ = bombs[j] + + dst = sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2) + + if dst <= r1: + graph[i].append(j) + + def dfs(node, vis): + vis[node] = True + count = 1 + + for nbh in graph[node]: + if not vis[nbh]: + count += dfs(nbh, vis) + + return count + + detonated = 0 + + for i in range(n): + visited = [False] * n + detonated = max(detonated, dfs(i, visited)) + + return detonated \ No newline at end of file diff --git a/python/2130-maximum-twin-sum-of-a-linked-list.py b/python/2130-maximum-twin-sum-of-a-linked-list.py new file mode 100644 index 000000000..af46ca54d --- /dev/null +++ b/python/2130-maximum-twin-sum-of-a-linked-list.py @@ -0,0 +1,17 @@ +class Solution: + def pairSum(self, head: Optional[ListNode]) -> int: + slow, fast = head, head + prev = None + while fast and fast.next: + fast = fast.next.next + tmp = slow.next + slow.next = prev + prev = slow + slow = tmp + + res = 0 + while slow: + res = max(res, prev.val + slow.val) + prev = prev.next + slow = slow.next + return res diff --git a/python/2215-find-the-difference-of-two-arrays.py b/python/2215-find-the-difference-of-two-arrays.py new file mode 100644 index 000000000..ca508425d --- /dev/null +++ b/python/2215-find-the-difference-of-two-arrays.py @@ -0,0 +1,40 @@ +# Time Complexiy: O(m + n) +# Space Complexity: O(m + n) +class Solution: + def findDifference(self, nums1: List[int], nums2: List[int]) -> List[List[int]]: + nums1 = set(nums1) + nums2 = set(nums2) + table = {} + for _, val in enumerate(nums2): + table[val] = 1 + + unik1 = [] + unik2 = [] + for i in nums1: + if i in table: + table[i] += 1 + else: + unik1.append(i) + + for key, val in table.items(): + if val == 1: + unik2.append(key) + return [unik1, unik2] + + +# Time Complexity: O(m + n), we check each element of nums1Set and nums2Set +# Space Complexity: O(m + n), where m and n are length sets in worst case. + +from typing import List # ignore this, just for typing + + +class Solution: + def findDifference(self, nums1: List[int], nums2: List[int]) -> List[List[int]]: + nums1_set = set(nums1) + nums2_set = set(nums2) + + lst1 = [num for num in nums1_set if num not in nums2_set] + lst2 = [num for num in nums2_set if num not in nums1_set] + + return [lst1, lst2] + diff --git a/python/2235-add-two-integers.py b/python/2235-add-two-integers.py new file mode 100644 index 000000000..29d001ddb --- /dev/null +++ b/python/2235-add-two-integers.py @@ -0,0 +1,3 @@ +class Solution: + def sum(self, num1: int, num2: int) -> int: + return num1 + num2 diff --git a/python/2300-successful-pairs-of-spells-and-potions.py b/python/2300-successful-pairs-of-spells-and-potions.py new file mode 100644 index 000000000..e33ea9ffc --- /dev/null +++ b/python/2300-successful-pairs-of-spells-and-potions.py @@ -0,0 +1,23 @@ +class Solution: + def successfulPairs(self, spells: List[int], potions: List[int], success: int) -> List[int]: + pairs = [] + potions.sort() + n = len(potions) + + for i in range(len(spells)): + l, r = 0, len(potions) - 1 + + while l <= r: + m = (l + r) // 2 + if spells[i] * potions[m] >= success: + r = m - 1 + else: + l = m + 1 + if l < len(potions): + pairs.append(n - l) + else: + pairs.append(0) + + return pairs + + diff --git a/python/2306-naming-a-company.py b/python/2306-naming-a-company.py new file mode 100644 index 000000000..ca1c0eb57 --- /dev/null +++ b/python/2306-naming-a-company.py @@ -0,0 +1,27 @@ +class Solution(object): + + def distinctNames(self, ideas): + + suffixes = dict() + for idea in ideas: + if idea[0] not in suffixes: + suffixes[idea[0]] = set() + suffixes[idea[0]].add(idea[1:]) + + if len(suffixes) < 2: + return 0 + + num_distinct_names = 0 + alphabet = 'abcdefghijklmnopqrstuvwxyz' + for prefix_1 in suffixes: + for prefix_2 in suffixes: + if prefix_2 > prefix_1: + num_suffixes_1 = len(suffixes[prefix_1]) + num_suffixes_2 = len(suffixes[prefix_2]) + for suffix in suffixes[prefix_1]: + if suffix in suffixes[prefix_2]: + num_suffixes_1 -= 1 + num_suffixes_2 -= 1 + num_distinct_names += 2 * num_suffixes_1 * num_suffixes_2 + + return num_distinct_names diff --git a/python/2348-number-of-zero-filled-subarrays.py b/python/2348-number-of-zero-filled-subarrays.py new file mode 100644 index 000000000..dcf99f304 --- /dev/null +++ b/python/2348-number-of-zero-filled-subarrays.py @@ -0,0 +1,20 @@ +class Solution(object): + def zeroFilledSubarray(self, nums): + # check if there are any Zeros in the list + res = nums.count(0) + if res == 0: + return 0 + + r = 0 + l = len(nums) + while r < l: + Temp_Subarray=[] + while r < l and nums[r] == 0: + Temp_Subarray.append(nums[r]) + r += 1 + if len(Temp_Subarray) > 1: + Temp_Count = len(Temp_Subarray) * ( len(Temp_Subarray) - 1 ) / 2 + res += int(Temp_Count) + + r += 1 + return res diff --git a/python/2390-removing-stars-from-a-string.py b/python/2390-removing-stars-from-a-string.py new file mode 100644 index 000000000..c27c8c004 --- /dev/null +++ b/python/2390-removing-stars-from-a-string.py @@ -0,0 +1,10 @@ +# https://leetcode.com/problems/removing-stars-from-a-string/submissions/1011668695/ +class Solution(object) : + def removeStars(self, s) : + res = [] + for c in s : + if res and c == '*': + res.pop() + else: + res.append(c) + return ''.join(res) diff --git a/python/2402-meeting-rooms-iii.py b/python/2402-meeting-rooms-iii.py new file mode 100644 index 000000000..c365b4193 --- /dev/null +++ b/python/2402-meeting-rooms-iii.py @@ -0,0 +1,23 @@ +class Solution: + def mostBooked(self, n: int, meetings: List[List[int]]) -> int: + meetings.sort() + + available = [i for i in range(n)] + used = [] + count = [0] * n + + for start, end in meetings: + while used and start >= used[0][0]: + _, room = heapq.heappop(used) + heapq.heappush(available, room) + + if not available: + end_time, room = heapq.heappop(used) + end = end_time + (end - start) + heapq.heappush(available, room) + + room = heapq.heappop(available) + heapq.heappush(used, (end, room)) + count[room] += 1 + + return count.index(max(count)) diff --git a/python/2405-optimal-partition-of-string.py b/python/2405-optimal-partition-of-string.py new file mode 100644 index 000000000..6a8afca3b --- /dev/null +++ b/python/2405-optimal-partition-of-string.py @@ -0,0 +1,10 @@ +class Solution: + def partitionString(self, s: str) -> int: + c=0 + res=set() + for i in s: + if i in res: + c=c+1 + res=set() + res.add(i) + return c+1 diff --git a/python/2482-difference-between-ones-and-zeros-in-row-and-column.py b/python/2482-difference-between-ones-and-zeros-in-row-and-column.py new file mode 100644 index 000000000..c85dba8b4 --- /dev/null +++ b/python/2482-difference-between-ones-and-zeros-in-row-and-column.py @@ -0,0 +1,23 @@ +class Solution: + def onesMinusZeros(self, grid: List[List[int]]) -> List[List[int]]: + m , n = len(grid), len(grid[0]) + rowCount = [[0, 0] for _ in range(m)] # (zeros, ones) + colCount = [[0, 0] for _ in range(n)] + res = [] + for r in range(m): + for c in range(n): + if grid[r][c] == 1: + rowCount[r][1] += 1 + colCount[c][1] += 1 + else: + rowCount[r][0] += 1 + colCount[c][0] += 1 + for r in range(m): + row =[] + for c in range(n): + row.append(rowCount[r][1] + colCount[c][1] - + rowCount[r][0] - colCount[c][0]) + res.append(row) + return res + + diff --git a/python/2483-minimum-penalty-for-a-shop.py b/python/2483-minimum-penalty-for-a-shop.py new file mode 100644 index 000000000..12f203760 --- /dev/null +++ b/python/2483-minimum-penalty-for-a-shop.py @@ -0,0 +1,15 @@ +class Solution: + def bestClosingTime(self, customers: str) -> int: + + curPenalty = res = minPenalty = 0 + + for i, ele in enumerate(customers): + if ele == 'Y': + curPenalty -= 1 + if curPenalty < minPenalty: + res = i+1 + curPenalty = minPenalty + else: + curPenalty += 1 + + return res diff --git a/python/2554-maximum-number-of-integers-to-choose-from-a-range-i.py b/python/2554-maximum-number-of-integers-to-choose-from-a-range-i.py new file mode 100644 index 000000000..9b36009df --- /dev/null +++ b/python/2554-maximum-number-of-integers-to-choose-from-a-range-i.py @@ -0,0 +1,15 @@ +class Solution: + def maxCount(self, banned: List[int], n: int, maxSum: int) -> int: + nums = {x:1 for x in range(1, n + 1)} # hashmap for storing the required elements + for i in banned: + if nums.get(i): + del nums[i] + sum = 0 + count = 0 + for i in nums: + sum += i + if sum <= maxSum: + count += 1 + else: + break + return count diff --git a/python/2616-minimize-the-maximum-difference-of-pairs.py b/python/2616-minimize-the-maximum-difference-of-pairs.py new file mode 100644 index 000000000..ec00b5fcb --- /dev/null +++ b/python/2616-minimize-the-maximum-difference-of-pairs.py @@ -0,0 +1,27 @@ +class Solution: + def minimizeMax(self, nums: List[int], p: int) -> int: + nums.sort() + + def checkPair(mid): + count, i = 0, 0 + + while i < len(nums) - 1: + if nums[i + 1] - nums[i] <= mid: + count += 1 + i += 2 + else: + i += 1 + + return count >= p + + left, right = 0, nums[-1] - nums[0] + + while left < right: + mid = (left + right) // 2 + + if checkPair(mid): + right = mid + else: + left = mid + 1 + + return left diff --git a/python/2709-greatest-common-divisor-traversal.py b/python/2709-greatest-common-divisor-traversal.py new file mode 100644 index 000000000..e9bd5380e --- /dev/null +++ b/python/2709-greatest-common-divisor-traversal.py @@ -0,0 +1,45 @@ +class UnionFind: + def __init__(self, n): + self.par = [i for i in range(n)] + self.size = [1] * n + self.count = n + + def find(self, x): + if self.par[x] != x: + self.par[x] = self.find(self.par[x]) + return self.par[x] + + def union(self, x, y): + px, py = self.find(x), self.find(y) + if px == py: + return + if self.size[px] < self.size[py]: + self.par[px] = py + self.size[py] += self.size[px] + else: + self.par[py] = px + self.size[px] += self.size[py] + self.count -=1 + +class Solution: + def canTraverseAllPairs(self, nums: List[int]) -> bool: + uf = UnionFind(len(nums)) + + factor_index = {} + for i, n in enumerate(nums): + f = 2 + while f * f <= n: + if n % f == 0: + if f in factor_index: + uf.union(i, factor_index[f]) + else: + factor_index[f] = i + while n % f == 0: + n = n // f + f += 1 + if n > 1: + if n in factor_index: + uf.union(i, factor_index[n]) + else: + factor_index[n] = i + return uf.count == 1 diff --git a/python/2864-maximum-odd-binary-number.py b/python/2864-maximum-odd-binary-number.py new file mode 100644 index 000000000..4edfaaffa --- /dev/null +++ b/python/2864-maximum-odd-binary-number.py @@ -0,0 +1,22 @@ +# Count 1s and build at end +class Solution: + def maximumOddBinaryNumber(self, s: str) -> str: + count = 0 + for c in s: + if c == "1": + count += 1 + + return (count - 1) * "1" + (len(s) - count) * "0" + "1" + +# Traverse and swap indices +class Solution: + def maximumOddBinaryNumber(self, s: str) -> str: + s = [c for c in s] + left = 0 + + for i in range(len(s)): + if s[i] == "1": + s[i], s[left] = s[left], s[i] + left += 1 + s[left - 1], s[len(s) - 1] = s[len(s) - 1], s[left - 1] + return "".join(s) diff --git a/python/2971-find-polygon-with-the-largest-perimeter.py b/python/2971-find-polygon-with-the-largest-perimeter.py new file mode 100644 index 000000000..7b2bf8afd --- /dev/null +++ b/python/2971-find-polygon-with-the-largest-perimeter.py @@ -0,0 +1,25 @@ +# Time complexity O(nlogn) +class Solution: + def largestPerimeter(self, nums: List[int]) -> int: + nums.sort() + res = -1 + total = 0 + + for n in nums: + if total > n: + res = total + n + total += n + + return res + +# Time complexity O(n + 30logn) ~ O(n) +class Solution: + def largestPerimeter(self, nums: List[int]) -> int: + curSum = sum(nums) + heapq._heapify_max(nums) + + while nums and curSum <= nums[0] * 2: + curSum -= heapq._heappop_max(nums) + + return curSum if len(nums) > 2 else -1 + diff --git a/ruby/0001-two-sum.rb b/ruby/0001-two-sum.rb new file mode 100644 index 000000000..a4d11dcac --- /dev/null +++ b/ruby/0001-two-sum.rb @@ -0,0 +1,9 @@ +def two_sum(nums, target) + hash = {} + nums.each_with_index do |num, idx| + return [hash[num], idx] if hash.key? num + + hash[target - num] = idx + end + nil +end diff --git a/ruby/0002-add-two-numbers.rb b/ruby/0002-add-two-numbers.rb new file mode 100644 index 000000000..053ae27b3 --- /dev/null +++ b/ruby/0002-add-two-numbers.rb @@ -0,0 +1,33 @@ +# Definition for singly-linked list. +# class ListNode +# attr_accessor :val, :next +# def initialize(val = 0, _next = nil) +# @val = val +# @next = _next +# end +# end +# @param {ListNode} l1 +# @param {ListNode} l2 +# @return {ListNode} +def add_two_numbers(l1, l2) + dummy_head = ListNode.new() + curr = dummy_head + + carry = 0 + while l1 || l2 || carry > 0 + l1_val = l1 ? l1.val : 0 + l2_val = l2 ? l2.val : 0 + + sum = l1_val + l2_val + carry + ones = sum % 10 + carry = sum / 10 + + curr.next = ListNode.new(ones) + + curr = curr.next + l1 = l1.next if l1 + l2 = l2.next if l2 + end + + dummy_head.next +end diff --git a/ruby/0003-longest-substring-without-repeating-characters.rb b/ruby/0003-longest-substring-without-repeating-characters.rb new file mode 100644 index 000000000..49b0ebf11 --- /dev/null +++ b/ruby/0003-longest-substring-without-repeating-characters.rb @@ -0,0 +1,19 @@ +def length_of_longest_substring(s) + return 1 if s.length == 1 + return 0 if s.length.zero? + + window = [] + appear = {} + max = 0 + s.each_char do |c| + if appear.key?(c) + while appear.key?(c) + appear.delete(window.shift) + end + end + window << c + appear[c] = c + max = window.length if window.length > max + end + max +end diff --git a/ruby/0007-reverse-integer.rb b/ruby/0007-reverse-integer.rb new file mode 100644 index 000000000..496a2c16e --- /dev/null +++ b/ruby/0007-reverse-integer.rb @@ -0,0 +1,14 @@ +def reverse(x) + max = 0xffffffff >> 1 + max_last_digit = max % 10 + max_minus_digit = max / 10 + reverse = 0 + until x.zero? + digit = x.remainder(10) + return 0 if reverse.abs > max_minus_digit || (reverse.abs == max_minus_digit && digit > max_last_digit) + + reverse = (reverse * 10) + digit + x = (x / 10.0).to_i + end + reverse +end diff --git a/ruby/0011-container-with-most-water.rb b/ruby/0011-container-with-most-water.rb new file mode 100644 index 000000000..9f9b80157 --- /dev/null +++ b/ruby/0011-container-with-most-water.rb @@ -0,0 +1,17 @@ +def max_area(height) + idx_start = 0 + idx_end = height.length - 1 + max_water = 0 + while idx_start < idx_end + challenger = (height[idx_start] > height[idx_end] ? height[idx_end] : height[idx_start]) + challenger *= (idx_end - idx_start) + max_water = challenger if challenger > max_water + + if height[idx_start] > height[idx_end] + idx_end -= 1 + else + idx_start += 1 + end + end + max_water +end diff --git a/ruby/0015-3sum.rb b/ruby/0015-3sum.rb new file mode 100644 index 000000000..d963c4727 --- /dev/null +++ b/ruby/0015-3sum.rb @@ -0,0 +1,23 @@ +def three_sum(nums) + sums = [] + nums.sort! # This is to get rid of duplicate solutions + nums.each_with_index do |num, idx| + next if idx.positive? && num == nums[idx - 1] + + left = idx + 1 + right = nums.length - 1 + while left < right + case num + nums[left] + nums[right] <=> 0 + when 1 + right -= 1 + when 0 + sums << [num, nums[left], nums[right]] + left += 1 + left += 1 while nums[left] == nums[left - 1] && left < right + when -1 + left += 1 + end + end + end + sums +end diff --git a/ruby/0017-letter-combinations-of-a-phone-number.rb b/ruby/0017-letter-combinations-of-a-phone-number.rb new file mode 100644 index 000000000..6ea8f4b93 --- /dev/null +++ b/ruby/0017-letter-combinations-of-a-phone-number.rb @@ -0,0 +1,33 @@ +$MAP = { + 2 => %w(a b c), + 3 => %w(d e f), + 4 => %w(g h i), + 5 => %w(j k l), + 6 => %w(m n o), + 7 => %w(p q r s), + 8 => %w(t u v), + 9 => %w(w x y z) +} + +# @param {String} digits +# @return {String[]} +def letter_combinations(digits) + ans = [] + + recurse(digits, "", ans, 0) unless digits.empty? + + ans +end + +def recurse(digits, prefix, ans, i) + if digits.length == prefix.length + ans << prefix + return + end + + digit = digits[i].to_i + + $MAP[digit].each do |c| + recurse(digits, prefix + c, ans, i + 1) + end +end diff --git a/ruby/0020-valid-parentheses.rb b/ruby/0020-valid-parentheses.rb new file mode 100644 index 000000000..fb2c83502 --- /dev/null +++ b/ruby/0020-valid-parentheses.rb @@ -0,0 +1,18 @@ +def is_valid(s) + paren = [] + match = { + "{" => "}", + "(" => ")", + "[" => "]", + } + s.each_char do |char| + if match.key?(char) + paren << char + next + elsif paren.empty? || match[paren.pop] != char + return false + end + paren.pop + end + paren.empty? +end diff --git a/ruby/0021-merge-two-sorted-lists.rb b/ruby/0021-merge-two-sorted-lists.rb new file mode 100644 index 000000000..27aad4d12 --- /dev/null +++ b/ruby/0021-merge-two-sorted-lists.rb @@ -0,0 +1,22 @@ +def merge_two_lists(list1, list2) + return nil if list1.nil? && list2.nil? + + dummy = ListNode.new + beginning = dummy + while list1 && list2 + if list1.val > list2.val + dummy.next = list2 + list2 = list2.next + else + dummy.next = list1 + list1 = list1.next + end + dummy = dummy.next + end + if list1 + dummy.next = list1 + elsif list2 + dummy.next = list2 + end + beginning.next +end diff --git a/ruby/0022-generate-parentheses.rb b/ruby/0022-generate-parentheses.rb new file mode 100644 index 000000000..fe189d8a0 --- /dev/null +++ b/ruby/0022-generate-parentheses.rb @@ -0,0 +1,21 @@ +# @param {Integer} n +# @return {String[]} +def generate_parenthesis(n) + parenthesis = [] + recurse(n, "", parenthesis, 0, 0) + parenthesis +end + +def recurse(n, pre, parenthesis, opens, closes) + if (n * 2 == pre.length) + parenthesis << pre + else + if closes < opens + recurse(n, pre + ")", parenthesis, opens, closes + 1) + end + + if opens < n + recurse(n, pre + "(", parenthesis, opens + 1, closes) + end + end +end diff --git a/ruby/0035-search-insert-position.rb b/ruby/0035-search-insert-position.rb new file mode 100644 index 000000000..886c61813 --- /dev/null +++ b/ruby/0035-search-insert-position.rb @@ -0,0 +1,21 @@ +# @param {Integer[]} nums +# @param {Integer} target +# @return {Integer} +def search_insert(nums, target) + low = 0 + high = nums.length - 1 + + while low <= high + mid = low + (high - low) / 2 + + if nums[mid] == target + return mid + elsif nums[mid] < target + low = mid + 1 + else + high = mid - 1 + end + end + + return low +end \ No newline at end of file diff --git a/ruby/0036-valid-sudoku.rb b/ruby/0036-valid-sudoku.rb new file mode 100644 index 000000000..634752ec2 --- /dev/null +++ b/ruby/0036-valid-sudoku.rb @@ -0,0 +1,48 @@ +def is_valid_sudoku(board) + valid_rows_and_columns?(board) && valid_boxes?(board) +end + +def valid_rows_and_columns?(board) + (0...9).each do |y| + column = [] + row = [] + (0...9).each do |x| + row << board[y][x] + column << board[x][y] + end + return false unless valid?(row) && valid?(column) + end + true +end + +def valid?(row) + hash = {} + row.each do |cell| + next if cell == "." + return false if hash.key? cell + + hash[cell] = true + end + + true +end + +def valid_boxes?(board) + y_cap = 0 + until y_cap == 9 + x_cap = 0 + until x_cap == 9 + box = [] + (y_cap...(y_cap + 3)).each do |y| + (x_cap...(x_cap + 3)).each do |x| + box << board[y][x] + end + return false unless valid?(box) + end + x_cap += 3 + end + y_cap += 3 + end + + true +end diff --git a/ruby/0039-combination-sum.rb b/ruby/0039-combination-sum.rb new file mode 100644 index 000000000..0a387b3a3 --- /dev/null +++ b/ruby/0039-combination-sum.rb @@ -0,0 +1,22 @@ +def combination_sum(candidates, target) + @result = [] + @target = target + @candidates = candidates + def dfs(i, current, total) + if total == @target + @result.append(current.dup()) + return + end + if i >= @candidates.length() || total > @target + return + end + + current.append(@candidates[i]) + dfs(i, current, total+@candidates[i]) + current.pop() + dfs(i+1, current, total) + end + + dfs(0,[],0) + return @result +end \ No newline at end of file diff --git a/ruby/0040-combination-sum-ii.rb b/ruby/0040-combination-sum-ii.rb new file mode 100644 index 000000000..359b9cd64 --- /dev/null +++ b/ruby/0040-combination-sum-ii.rb @@ -0,0 +1,31 @@ +def combination_sum2(candidates, target) + @candidates = candidates + @candidates.sort! + @result = [] + + def backtrack (cur,pos,target) + @result.append(cur.dup()) if target == 0 + return if target <=0 + + prev = -1 + + (pos..@candidates.length-1).each do |i| + next if @candidates[i] == prev + + cur.append(@candidates[i]) + + backtrack(cur,i+1,target-@candidates[i]) + + cur.pop() + + prev = @candidates[i] + + end + + end + backtrack([],0,target) + + return @result + + +end \ No newline at end of file diff --git a/ruby/0042-trapping-rain-water.rb b/ruby/0042-trapping-rain-water.rb new file mode 100644 index 000000000..6d88f4e2c --- /dev/null +++ b/ruby/0042-trapping-rain-water.rb @@ -0,0 +1,49 @@ +def trap(height) + max_left = [] + max_right = [] + + max = 0 + height.each do |line| + max_left << max + max = line if line > max + end + + max = 0 + (height.length - 1).downto(0).each do |idx| + max_right.unshift(max) + max = height[idx] if height[idx] > max + end + + total_water = 0 + height.each_with_index do |line, idx| + min = max_left[idx] > max_right[idx] ? max_right[idx] : max_left[idx] + water = min - line + next if water < 1 + + total_water += water + end + total_water +end + +# 0(1) space +def trap(height) + max_water = 0 + max_left = height.first + max_right = height.last + left = 0 + right = height.length - 1 + + while left < right + if max_left > max_right + right -= 1 + water = max_right - height[right] + max_right = height[right] if height[right] > max_right + else + left += 1 + water = max_left - height[left] + max_left = height[left] if height[left] > max_left + end + max_water += water unless water < 1 + end + max_water +end diff --git a/ruby/0043-multiply-strings.rb b/ruby/0043-multiply-strings.rb new file mode 100644 index 000000000..4b6a7eb44 --- /dev/null +++ b/ruby/0043-multiply-strings.rb @@ -0,0 +1,22 @@ +def multiply(num1, num2) + + return "0" if [num1,num2].include?("0") + + res = [0] * (num1.length + num2.length) + + num1.reverse! + num2.reverse! + + (0..num1.length-1).each do |i| + (0..num2.length-1).each do |j| + digit = num1[i].to_i * num2[j].to_i + res[i+j] += digit + res[i+j+1] += (res[i+j]/10) + res[i+j] = res[i+j] % 10 + end + end + + res.reverse! + + return res.join.to_i.to_s +end \ No newline at end of file diff --git a/ruby/0045-jump-game-ii.rb b/ruby/0045-jump-game-ii.rb new file mode 100644 index 000000000..93b6a5194 --- /dev/null +++ b/ruby/0045-jump-game-ii.rb @@ -0,0 +1,17 @@ +def jump(nums) + + result = 0 + l = r = 0 + + while (r< nums.size()-1) + farthest = 0 + (l..r).each do |i| + farthest = [farthest,(i + nums[i])].max + end + l= r+1 + r = farthest + result +=1 + end + + return result +end \ No newline at end of file diff --git a/ruby/0046-permutations.rb b/ruby/0046-permutations.rb new file mode 100644 index 000000000..22d4ca5ed --- /dev/null +++ b/ruby/0046-permutations.rb @@ -0,0 +1,14 @@ +def permute(nums) + return [[]] if nums.empty? + + perms = [] + + (0...nums.length).each do |i| + el = nums[i] + rest = nums.take(i) + nums.drop(i + 1) + new_perms = permute(rest).map { |perm| perm.unshift(el) } + perms.concat(new_perms) + end + + perms +end \ No newline at end of file diff --git a/ruby/0048-rotate-image.rb b/ruby/0048-rotate-image.rb new file mode 100644 index 000000000..6e260d4af --- /dev/null +++ b/ruby/0048-rotate-image.rb @@ -0,0 +1,25 @@ + +def rotate(matrix) + l = 0 + r = matrix.size()-1 + + while l< r + (0..(r-l-1)).each do |i| + top = l + bottom = r + + top_left = matrix[top][l+i] + + matrix[top][l+i] = matrix[bottom-i][l] + + matrix[bottom-i][l] = matrix[bottom][r-i] + + matrix[bottom][r-i] = matrix[top+i][r] + + matrix[top+i][r] = top_left + end + + r -=1 + l +=1 + end +end \ No newline at end of file diff --git a/ruby/0049-group-anagrams.rb b/ruby/0049-group-anagrams.rb new file mode 100644 index 000000000..6b01dbe54 --- /dev/null +++ b/ruby/0049-group-anagrams.rb @@ -0,0 +1,9 @@ +def group_anagrams(strs) + anagrams = Hash.new { |h, k| h[k] = [] } + strs.each do |str| + hash = Hash.new(0) + str.each_char { |c| hash[c] += 1 } + anagrams[hash] << str + end + anagrams.values +end diff --git a/ruby/0050-powx-n.rb b/ruby/0050-powx-n.rb new file mode 100644 index 000000000..6d60bb3ab --- /dev/null +++ b/ruby/0050-powx-n.rb @@ -0,0 +1,23 @@ + +def my_pow(x, n) + solution = multiply(x, n.abs) + + if n>=0 + return solution + else + return (1/solution) + + end +end + + +def multiply(x,n) + return 0 if x==0 + return 1 if n==0 + result = multiply((x*x),n/2) + if (n%2 ==1) + return (x * result) + else + return result + end +end \ No newline at end of file diff --git a/ruby/0053-maximum-subarray.rb b/ruby/0053-maximum-subarray.rb new file mode 100644 index 000000000..a0417a6e4 --- /dev/null +++ b/ruby/0053-maximum-subarray.rb @@ -0,0 +1,11 @@ +def max_sub_array(nums) + max_sub = nums[0] + current_sum = 0 + + nums.each do |num| + current_sum = 0 if current_sum < 0 + current_sum += num + max_sub = [max_sub,current_sum].max + end + return max_sub +end \ No newline at end of file diff --git a/ruby/0054-spiral-matrix.rb b/ruby/0054-spiral-matrix.rb new file mode 100644 index 000000000..0cfc21c16 --- /dev/null +++ b/ruby/0054-spiral-matrix.rb @@ -0,0 +1,39 @@ +def spiral_order(matrix) + result = [] + left = 0 + right = matrix[0].length + top = 0 + bottom = matrix.length + + while left < right && top < bottom + + (left..right-1).each do |i| + result.append(matrix[top][i]) + end + + top +=1 + + (top..bottom-1).each do |i| + result.append(matrix[i][right-1]) + end + + right -=1 + + break unless (left < right && top < bottom) + + (right-1).downto(left).each do |i| + result.append(matrix[bottom-1][i]) + end + + bottom -=1 + + (bottom-1).downto(top).each do |i| + result.append(matrix[i][left]) + end + + left +=1 + + end + + return result +end \ No newline at end of file diff --git a/ruby/0058-length-of-last-word.rb b/ruby/0058-length-of-last-word.rb new file mode 100644 index 000000000..4c1250105 --- /dev/null +++ b/ruby/0058-length-of-last-word.rb @@ -0,0 +1,22 @@ +# @param {String} s +# @return {Integer} + +#simple one liner +def length_of_last_word(s) + s.split.last.length +end + +#double pointer +def length_of_last_word(s) + left = -1 + while s[left] == ' ' do + left -= 1 + end + + right = left + while s[left] && s[left] != ' ' do + left -=1 + end + + right - left +end diff --git a/ruby/0066-plus-one.rb b/ruby/0066-plus-one.rb new file mode 100644 index 000000000..ed246b0a8 --- /dev/null +++ b/ruby/0066-plus-one.rb @@ -0,0 +1,22 @@ +def plus_one(digits) + digits.reverse! + carry = 1 + i = 0 + + while(carry ==1) + if (i 0 + matrix[r][0] = 0 + else + row_zero = true + end + end + end + end + + (1...rows+1).each do |r| + (1..cols).each do |c| + matrix[r][c] = 0 if ((matrix[0][c] == 0)||(matrix[r][0] == 0)) + end + end + + if matrix[0][0] == 0 + (0..rows).each do |r| + matrix[r][0] = 0 + end + end + + if row_zero + (0..cols).each do |c| + matrix[0][c] = 0 + end + end +end \ No newline at end of file diff --git a/ruby/0074-search-a-2d-matrix.rb b/ruby/0074-search-a-2d-matrix.rb new file mode 100644 index 000000000..500fc8fbf --- /dev/null +++ b/ruby/0074-search-a-2d-matrix.rb @@ -0,0 +1,37 @@ +# @param {Integer[][]} matrix +# @param {Integer} target +# @return {Boolean} +def search_matrix(matrix, target) + l = 0 + r = matrix.length - 1 + + while l <= r + mid = (l + r) / 2 + row = matrix[mid] + + if target > row[-1] + l = mid + 1 + elsif target < row[0] + r = mid - 1 + else + return binary_search(row, target) + end + end + + false +end + +def binary_search(values, target) + l = 0 + r = values.length - 1 + + while l <= r + mid = (l + r) / 2 + + return true if values[mid] == target + l = mid + 1 if values[mid] < target + r = mid - 1 if values[mid] > target + end + + false +end diff --git a/ruby/0076-minimum-window-substring.rb b/ruby/0076-minimum-window-substring.rb new file mode 100644 index 000000000..b73abfe78 --- /dev/null +++ b/ruby/0076-minimum-window-substring.rb @@ -0,0 +1,41 @@ + +def min_window(s, t) + + return "" if t == "" + count_t = Hash.new(0) + window = Hash.new(0) + + t.each_char do |c| + count_t[c] +=1 + end + + have = 0 + need = count_t.length + + res = [-1,-1] + result_length = 9999999999 + l = 0 + (0..s.length-1).each do |r| + c = s[r] + window[c] +=1 + + have +=1 if ((count_t.keys.include?(c)) && (window[c] == count_t[c])) + while(have == need) + if (r-l+1) < result_length + res = [l,r] + result_length = (r-l+1) + + end + window[s[l]] -=1 + have -=1 if ((count_t.keys.include?(s[l])) && (window[s[l]] < count_t[s[l]])) + l +=1 + end + end + l = res[0] + r = res[1] + if result_length != 9999999999 + return s[l..r] + else + return "" + end +end \ No newline at end of file diff --git a/ruby/0078-subsets.rb b/ruby/0078-subsets.rb new file mode 100644 index 000000000..077418cd3 --- /dev/null +++ b/ruby/0078-subsets.rb @@ -0,0 +1,24 @@ +# @param {Integer[]} nums +# @return {Integer[][]} +def subsets(nums) + ans = [] + + recurse(nums, ans, [], 0) + + ans +end + + +def recurse(nums, ans, curr, i) + if i == nums.length + ans << curr.clone + return + end + + + curr << nums[i] + recurse(nums, ans, curr, i + 1) + + curr.pop + recurse(nums, ans, curr, i + 1) +end diff --git a/ruby/0079-word-search.rb b/ruby/0079-word-search.rb new file mode 100644 index 000000000..b00245256 --- /dev/null +++ b/ruby/0079-word-search.rb @@ -0,0 +1,37 @@ +def exist(board, word) + @rows = board.length + @cols = board[0].length + @board = board + @word = word + @path = Set.new() + + set1 = @board.flatten.to_set + set2 = @word.split('').to_set + return false if !(set1 >= set2) + + def dfs(r,c,i) + return true if i == @word.length() + return false if (r<0 || c<0 || r >= @rows || c >= @cols || @word[i] != @board[r][c] || @path.include?([r,c])) + + @path.add([r,c]) + + result = (dfs(r+1,c,i+1) || + dfs(r-1,c,i+1) || + dfs(r,c+1,i+1) || + dfs(r,c-1,i+1) + ) + + @path.delete([r,c]) + + return result + end + + (0..@rows-1).each do |r| + (0..@cols-1).each do |c| + return true if dfs(r,c,0) + end + end + + return false + +end \ No newline at end of file diff --git a/ruby/0084-largest-rectangle-in-histogram.rb b/ruby/0084-largest-rectangle-in-histogram.rb new file mode 100644 index 000000000..d706e36ca --- /dev/null +++ b/ruby/0084-largest-rectangle-in-histogram.rb @@ -0,0 +1,19 @@ +def largest_rectangle_area(heights) + max_area = 0 + stack = [] + + heights.each_with_index do |h, idx| + start = idx + while !stack.empty? && stack[-1][1] > h + index, height = stack.pop + max_area = [height * (idx - index), max_area].max + start = index + end + stack << [start, h] + end + + stack.each do |idx, height| + max_area = [height * (heights.length - idx), max_area].max + end + max_area +end \ No newline at end of file diff --git a/ruby/0090-subsets-ii.rb b/ruby/0090-subsets-ii.rb new file mode 100644 index 000000000..224a79b31 --- /dev/null +++ b/ruby/0090-subsets-ii.rb @@ -0,0 +1,25 @@ +def subsets_with_dup(nums) + @result = [] + @nums = nums.sort! + + def backtrack(i,subset) + if i == @nums.length + @result.append(subset.dup()) + return + end + + subset.append(@nums[i]) + backtrack(i+1,subset) + subset.pop() + + while ((i+1 < @nums.length) && (@nums[i] == @nums[i+1])) + i +=1 + end + + backtrack(i+1,subset) + end + + backtrack(0,[]) + + return @result +end \ No newline at end of file diff --git a/ruby/0094-binary-tree-inorder-traversal.rb b/ruby/0094-binary-tree-inorder-traversal.rb new file mode 100644 index 000000000..b5fdfdc39 --- /dev/null +++ b/ruby/0094-binary-tree-inorder-traversal.rb @@ -0,0 +1,21 @@ +# Definition for a binary tree node. +# class TreeNode +# attr_accessor :val, :left, :right +# def initialize(val = 0, left = nil, right = nil) +# @val = val +# @left = left +# @right = right +# end +# end +# @param {TreeNode} root +# @return {Integer[]} +def inorder_traversal(root) + result = [] + return result if root.nil? + + result << inorder_traversal(root.left) + result << root.val + result << inorder_traversal(root.right) + + result.flatten +end diff --git a/ruby/0100-same-tree.rb b/ruby/0100-same-tree.rb new file mode 100644 index 000000000..5168d6d83 --- /dev/null +++ b/ruby/0100-same-tree.rb @@ -0,0 +1,11 @@ +def is_same_tree(p, q) + if p.nil? && q.nil? + true + elsif p && q + (p.val == q.val) && + is_same_tree(p.left, q.left) && + is_same_tree(p.right, q.right) + else + false + end +end diff --git a/ruby/0102-binary-tree-level-order-traversal.rb b/ruby/0102-binary-tree-level-order-traversal.rb new file mode 100644 index 000000000..b4d94fd92 --- /dev/null +++ b/ruby/0102-binary-tree-level-order-traversal.rb @@ -0,0 +1,32 @@ +# Definition for a binary tree node. +# class TreeNode +# attr_accessor :val, :left, :right +# def initialize(val = 0, left = nil, right = nil) +# @val = val +# @left = left +# @right = right +# end +# end +# @param {TreeNode} root +# @return {Integer[][]} +def level_order(root) + result = [] + + level = [] + level << root if root + until level.empty? + vals = [] + next_level = [] + + level.each do |node| + vals << node.val + next_level << node.left if node.left + next_level << node.right if node.right + end + + result << vals + level = next_level + end + + result +end diff --git a/ruby/0104-maximum-depth-of-binary-tree.rb b/ruby/0104-maximum-depth-of-binary-tree.rb new file mode 100644 index 000000000..da85a3d51 --- /dev/null +++ b/ruby/0104-maximum-depth-of-binary-tree.rb @@ -0,0 +1,8 @@ +def max_depth(root) + return 0 if root.nil? + return 1 if root.left.nil? && root.right.nil? + + left = 1 + max_depth(root.left) + right = 1 + max_depth(root.right) + left > right ? left : right +end diff --git a/ruby/0110-balanced-binary-tree.rb b/ruby/0110-balanced-binary-tree.rb new file mode 100644 index 000000000..e64366129 --- /dev/null +++ b/ruby/0110-balanced-binary-tree.rb @@ -0,0 +1,15 @@ +def is_balanced(root) + $balance = true + balanced?(root) + $balance +end + +def balanced?(root) + return -1 if root.nil? + + left_height = 1 + balanced?(root.left) + right_height = 1 + balanced?(root.right) + $balance = false if (left_height - right_height).abs > 1 + + left_height > right_height ? left_height : right_height +end diff --git a/ruby/0121-best-time-to-buy-and-sell-stock.rb b/ruby/0121-best-time-to-buy-and-sell-stock.rb new file mode 100644 index 000000000..7adc19223 --- /dev/null +++ b/ruby/0121-best-time-to-buy-and-sell-stock.rb @@ -0,0 +1,10 @@ +def max_profit(prices) + max_profit = 0 + min = prices.first + prices.each do |price| + min = price if price < min + profit = price - min + max_profit = profit if profit > max_profit + end + max_profit +end diff --git a/ruby/0125-valid-palindrome.rb b/ruby/0125-valid-palindrome.rb new file mode 100644 index 000000000..b174cbd91 --- /dev/null +++ b/ruby/0125-valid-palindrome.rb @@ -0,0 +1,35 @@ +def is_palindrome(s) + str = s.downcase.chars.select { |char| /[a-zA-Z0-9]/.match?(char) }.join + idx_start = 0 + idx_end = str.length - 1 + while idx_start < idx_end + return false if str[idx_start] != str[idx_end] + + idx_start += 1 + idx_end -= 1 + end + true +end + +def is_palindrome(s) + idx_start = 0 + idx_end = s.length - 1 + while idx_start < idx_end + if !/[a-zA-Z0-9]/.match?(s[idx_start]) + idx_start += 1 + elsif !/[a-zA-Z0-9]/.match?(s[idx_end]) + idx_end -= 1 + else + return false if s[idx_start].downcase != s[idx_end].downcase + + idx_start += 1 + idx_end -= 1 + end + end + true +end + +def is_palindrome(s) + str = s.downcase.chars.select { |char| /[a-zA-Z0-9]/.match?(char) } + str == str.reverse +end diff --git a/ruby/0128-longest-consecutive-sequence.rb b/ruby/0128-longest-consecutive-sequence.rb new file mode 100644 index 000000000..dfafd89ac --- /dev/null +++ b/ruby/0128-longest-consecutive-sequence.rb @@ -0,0 +1,14 @@ +def longest_consecutive(nums) + set = Set.new(nums) + set.reduce(0) do |longest, num| + if !set.include?(num-1) + length = 0 + while set.include?(num + length) do + length += 1 + end + next(longest > length ? longest : length) + end + + longest + end +end diff --git a/ruby/0131-palindrome-partitioning.rb b/ruby/0131-palindrome-partitioning.rb new file mode 100644 index 000000000..4862b8980 --- /dev/null +++ b/ruby/0131-palindrome-partitioning.rb @@ -0,0 +1,33 @@ +def partition(s) + @result = [] + @part = [] + @s = s + + def dfs(i) + if i >= @s.length + @result.append(@part.dup()) + return + end + (i..@s.length-1).each do |j| + if is_palindrome(@s,i,j) + @part.append(@s[i..j]) + dfs(j+1) + @part.pop() + end + end + end + + dfs(0) + + return @result +end + + +def is_palindrome(s,i,j) + while i= nums[left] + left = mid + 1 + else + right = mid - 1 + end + end + + return result +end diff --git a/ruby/0155-min-stack.rb b/ruby/0155-min-stack.rb new file mode 100644 index 000000000..72b6a758a --- /dev/null +++ b/ruby/0155-min-stack.rb @@ -0,0 +1,33 @@ +class MinStack + def initialize + @stack = [] + end + + def push(val) + min = if @stack.empty? + val + else + val < @stack.last[1] ? val : @stack.last[1] + end + + @stack << [val, min] + nil + end + + def pop + @stack.pop + nil + end + + def top + return nil if @stack.empty? + + @stack.last[0] + end + + def get_min + return nil if @stack.empty? + + @stack.last[1] + end +end diff --git a/ruby/0167-two-sum-ii-input-array-is-sorted.rb b/ruby/0167-two-sum-ii-input-array-is-sorted.rb new file mode 100644 index 000000000..70573005c --- /dev/null +++ b/ruby/0167-two-sum-ii-input-array-is-sorted.rb @@ -0,0 +1,15 @@ +def two_sum(numbers, target) + idx_start = 0 + idx_end = numbers.length - 1 + while idx_start < idx_end + case numbers[idx_start] + numbers[idx_end] <=> target + when 1 + idx_end -= 1 + when 0 + return [idx_start + 1, idx_end + 1] + when -1 + idx_start += 1 + end + end + nil +end diff --git a/ruby/0179-largest-number.rb b/ruby/0179-largest-number.rb new file mode 100644 index 000000000..6137e6ee4 --- /dev/null +++ b/ruby/0179-largest-number.rb @@ -0,0 +1,5 @@ +# @param {Integer[]} nums +# @return {String} +def largest_number(nums) + nums.sort! {|a, b| b.to_s + a.to_s <=> a.to_s + b.to_s}.join.to_i.to_s +end \ No newline at end of file diff --git a/ruby/0190-reverse-bits.rb b/ruby/0190-reverse-bits.rb new file mode 100644 index 000000000..c4c31c348 --- /dev/null +++ b/ruby/0190-reverse-bits.rb @@ -0,0 +1,8 @@ +def reverse_bits(n) + reversed_bits = 0 + 31.downto(0) do |nbr| + insert = ((n >> (31 - nbr)) & 1) << nbr + reversed_bits |= insert + end + reversed_bits +end diff --git a/ruby/0191-number-of-1-bits.rb b/ruby/0191-number-of-1-bits.rb new file mode 100644 index 000000000..f7db1c571 --- /dev/null +++ b/ruby/0191-number-of-1-bits.rb @@ -0,0 +1,18 @@ +def hamming_weight(n) + total_ones = 0 + until n.zero? + total_ones += (n % 2) + n = n >> 1 + end + total_ones +end + +# Solution utilizing & (n - 1) +def hamming_weight(n) + total_ones = 0 + until n.zero? + n &= (n - 1) + total_ones += 1 + end + total_ones +end diff --git a/ruby/0198-house-robber.rb b/ruby/0198-house-robber.rb new file mode 100644 index 000000000..26c54764e --- /dev/null +++ b/ruby/0198-house-robber.rb @@ -0,0 +1,11 @@ +# @param {Integer[]} nums +# @return {Integer} +def rob(nums) + dp = Array.new(nums.size + 3) { 0 } + + nums.each_with_index do |num, i| + dp[i + 3] = num + [dp[i + 1], dp[i]].max + end + + [dp[-1], dp[-2]].max +end diff --git a/ruby/0200-number-of-islands.rb b/ruby/0200-number-of-islands.rb new file mode 100644 index 000000000..1ca4455ed --- /dev/null +++ b/ruby/0200-number-of-islands.rb @@ -0,0 +1,29 @@ +DIRS = [[0, 1], [0, -1], [1, 0], [-1, 0]].freeze + +def num_islands(grid) + islands = 0 + memo = Array.new(grid.length) { Array.new(grid[0].length) } + grid.each_with_index do |row, y| + row.each_with_index do |el, x| + if memo[y][x].nil? && el == "1" + islands += 1 + flood_fill(grid, memo, y, x) + end + end + end + islands +end + +def flood_fill(grid, memo, y, x) + return nil if y >= grid.length || y.negative? || + x >= grid[0].length || x.negative? || + memo[y][x] != nil || + grid[y][x] == "0" + + memo[y][x] = true + DIRS.each do |dy, dx| + dy += y + dx += x + flood_fill(grid, memo, dy, dx) + end +end diff --git a/ruby/0202-happy-number.rb b/ruby/0202-happy-number.rb new file mode 100644 index 000000000..b8b7c802d --- /dev/null +++ b/ruby/0202-happy-number.rb @@ -0,0 +1,22 @@ +def is_happy(n) + visit = Set.new([]) + + while !visit.include?(n) + visit.add(n) + n = sum_of_squares(n) + return true if n==1 + end + + return false + +end + +def sum_of_squares(n) + output = 0 + while n != 0 + digit = n%10 + output += (digit*digit) + n = n/10 + end + return output +end \ No newline at end of file diff --git a/ruby/0206-reverse-linked-list.rb b/ruby/0206-reverse-linked-list.rb new file mode 100644 index 000000000..d6dde7a93 --- /dev/null +++ b/ruby/0206-reverse-linked-list.rb @@ -0,0 +1,28 @@ +def reverse_list(head) + next_node = nil + prev_node = nil + loop do + next_node = head.next + head.next = prev_node + prev_node = head + if next_node.nil? + return head + else + head = next_node + end + end +end + +# Recursive +def reverse_list(head) + return head if head.nil? + + new_head = head + if head.next + new_head = reverse_list(head.next) + head.next.next = head + end + + head.next = nil + new_head +end diff --git a/ruby/0208-implement-trie-prefix-tree.rb b/ruby/0208-implement-trie-prefix-tree.rb new file mode 100644 index 000000000..770246847 --- /dev/null +++ b/ruby/0208-implement-trie-prefix-tree.rb @@ -0,0 +1,37 @@ +class Trie + END_OF_WORD = "END" + + def initialize + @root = {} + end + + def insert(word) + curr = @root + word.each_char do |char| + curr[char] ||= {} + curr = curr[char] + end + curr[END_OF_WORD] = true + nil + end + + def search(word) + curr = @root + word.each_char do |char| + return false unless curr[char] + + curr = curr[char] + end + !!curr[END_OF_WORD] + end + + def starts_with(prefix) + curr = @root + prefix.each_char do |char| + return false unless curr[char] + + curr = curr[char] + end + true + end +end diff --git a/ruby/0211-design-add-and-search-words-data-structure.rb b/ruby/0211-design-add-and-search-words-data-structure.rb new file mode 100644 index 000000000..a0e320c63 --- /dev/null +++ b/ruby/0211-design-add-and-search-words-data-structure.rb @@ -0,0 +1,34 @@ +class WordDictionary + def initialize(root = {}) + @root = root + end + + def add_word(word) + curr = @root + word.each_char do |char| + curr[char] ||= {} + curr = curr[char] + end + curr["END"] = true + end + + def search(word) + search_word(word, @root) + end + + def search_word(word, root) + curr = root + word.each_char.with_index do |char, idx| + if word[idx] != "." + return false unless curr[char] + + curr = curr[char] + else + return curr.keys.any? do |key| + key == "END" ? false : search_word(word[(idx + 1)..-1], curr[key]) + end + end + end + !!curr["END"] + end +end diff --git a/ruby/0213-house-robber-ii.rb b/ruby/0213-house-robber-ii.rb new file mode 100644 index 000000000..bfd33e5bf --- /dev/null +++ b/ruby/0213-house-robber-ii.rb @@ -0,0 +1,24 @@ +# @param {Integer[]} nums +# @return {Integer} +def rob(nums) + return nums.max if nums.size < 4 + + dp = Array.new(nums.size + 3) { 0 } + + mil = 1_000_000 + dp[2] = mil + nums.each_with_index do |num, i| + dp[i + 3] = num + [dp[i], dp[i + 1]].max + end + + result = [dp[-1], dp[-2]].max - mil + + dp = Array.new(nums.size + 3) { 0 } + + dp[1] = mil + nums.each_with_index do |num, i| + dp[i + 3] = num + [dp[i], dp[i + 1]].max + end + + [result, [dp[-2], dp[-3]].max - mil].max +end diff --git a/ruby/0217-contains-duplicate.rb b/ruby/0217-contains-duplicate.rb new file mode 100644 index 000000000..14cd77d62 --- /dev/null +++ b/ruby/0217-contains-duplicate.rb @@ -0,0 +1,9 @@ +def contains_duplicate(nums) + hash = {} + nums.each do |num| + return true if hash.key? num + + hash[num] = true + end + false +end diff --git a/ruby/0226-invert-binary-tree.rb b/ruby/0226-invert-binary-tree.rb new file mode 100644 index 000000000..e4e39ae7b --- /dev/null +++ b/ruby/0226-invert-binary-tree.rb @@ -0,0 +1,9 @@ +def invert_tree(root) + return root if root.nil? + return root if root.left.nil? && root.right.nil? + + root.left, root.right = root.right, root.left + invert_tree(root.left) + invert_tree(root.right) + root +end diff --git a/ruby/023-merge-k-sorted-lists.rb b/ruby/023-merge-k-sorted-lists.rb new file mode 100644 index 000000000..d0c303cf1 --- /dev/null +++ b/ruby/023-merge-k-sorted-lists.rb @@ -0,0 +1,35 @@ +def merge_k_lists(lists) + return if lists.size == 0 + + while lists.size > 1 + merged_lists = [] + (0..lists.size-1).step(2) do |i| + l1 = lists[i] + l2 = lists[i+1] + merged_lists << merge_lists(l1, l2) + end + lists = merged_lists + end + + lists[0] +end + +def merge_lists(l1, l2) + dummy = ListNode.new + tail = dummy + + while l1 && l2 + if l1.val < l2.val + tail.next = l1 + l1 = l1.next + else + tail.next = l2 + l2 = l2.next + end + tail = tail.next + end + tail.next = l1 if l1 + tail.next = l2 if l2 + + dummy.next +end diff --git a/ruby/0235-lowest-common-ancestor-of-a-binary-search-tree.rb b/ruby/0235-lowest-common-ancestor-of-a-binary-search-tree.rb new file mode 100644 index 000000000..b082d83a7 --- /dev/null +++ b/ruby/0235-lowest-common-ancestor-of-a-binary-search-tree.rb @@ -0,0 +1,10 @@ +def lowest_common_ancestor(root, p, q) + return root if root.val == p.val || root.val == q.val + return root if root.val.between?(p.val, q.val) || root.val.between?(q.val, p.val) + + if root.val > p.val + lowest_common_ancestor(root.left, p, q) + else + lowest_common_ancestor(root.right, p, q) + end +end diff --git a/ruby/0238-product-of-array-except-self.rb b/ruby/0238-product-of-array-except-self.rb new file mode 100644 index 000000000..1c863eaa3 --- /dev/null +++ b/ruby/0238-product-of-array-except-self.rb @@ -0,0 +1,34 @@ +def product_except_self(nums) + prefix = [] + total_product = 1 + nums.each do |num| + prefix << total_product + total_product *= num + end + + suffix = [] + total_product = 1 + (nums.length - 1).downto(0) do |idx| + suffix << total_product + total_product *= nums[idx] + end + + nums.map.with_index { |_n, i| prefix[i] * suffix[nums.length - 1 - i] } +end + +# O(1) space solution +def product_except_self(nums) + output = [] + total_product = 1 + nums.each do |num| + output << total_product + total_product *= num + end + + total_product = 1 + (nums.length - 1).downto(0) do |idx| + output[idx] = total_product * output[idx] + total_product *= nums[idx] + end + output +end diff --git a/ruby/0239-sliding-window-maximum.rb b/ruby/0239-sliding-window-maximum.rb new file mode 100644 index 000000000..953a532c9 --- /dev/null +++ b/ruby/0239-sliding-window-maximum.rb @@ -0,0 +1,24 @@ + +def max_sliding_window(nums, k) + output = [] + + q = [] + + l = r = 0 + + while r < nums.length() + + while q[l] && nums[q[-1]] < nums[r] + q.pop() + end + + q.append(r) + + l +=1 if q[l] == r - k + + output.append(nums[q[l]]) if (r+1) >= k + + r +=1 + end + return output +end \ No newline at end of file diff --git a/ruby/0242-valid-anagram.rb b/ruby/0242-valid-anagram.rb new file mode 100644 index 000000000..cffdb0fb7 --- /dev/null +++ b/ruby/0242-valid-anagram.rb @@ -0,0 +1,15 @@ +def is_anagram(s, t) + return false unless s.length == t.length + + hash = Hash.new(0) + s.each_char.with_index do |_, idx| + hash[s[idx]] += 1 + hash[t[idx]] -= 1 + end + hash.all? { |_k, v| v.zero? } +end + +# For nlogn time and O(1) space, can sort then check if equal +def is_anagram(s, t) + s.chars.sort.join == t.chars.sort.join +end diff --git a/ruby/0268-missing-number.rb b/ruby/0268-missing-number.rb new file mode 100644 index 000000000..d2b3e2575 --- /dev/null +++ b/ruby/0268-missing-number.rb @@ -0,0 +1,15 @@ +def missing_number(nums) + missing = 0 + (0..nums.length).each do |i| + nbr = (nums[i].nil? ? 0 : nums[i]) + missing += (i - nbr) + end + missing +end + +# Solution using Gauss summation +def missing_number(nums) + gauss = (nums.length / 2.0) * (nums.length + 1) + nums.each { |n| gauss -= n } + gauss.to_i +end diff --git a/ruby/0271-encode-and-decode-strings.rb b/ruby/0271-encode-and-decode-strings.rb new file mode 100644 index 000000000..5de9b1518 --- /dev/null +++ b/ruby/0271-encode-and-decode-strings.rb @@ -0,0 +1,22 @@ +# Encodes a list of strings to a single string. +# +# @param {string[]} strs +# @return {string} +def encode(strs) + strs.reduce("") do |acc, cur| + "#{acc}\n#{cur.chars.map { _1.ord.to_s }.join(",")}" + end[1..] + "\n" +end + +# Decodes a single string to a list of strings. +# +# @param {string} s +# @return {string[]} +def decode(s) + s.each_line.map do |nums| + nums[...-1].split(",").map(&:to_i).map(&:chr).join("") + end +end + +# Your functions will be called as such: +# decode(encode(strs)) diff --git a/ruby/0287-find-the-duplicate-number.rb b/ruby/0287-find-the-duplicate-number.rb new file mode 100644 index 000000000..7ae3ebf6a --- /dev/null +++ b/ruby/0287-find-the-duplicate-number.rb @@ -0,0 +1,23 @@ +# @param {Integer[]} nums +# @return {Integer} +def find_duplicate(nums) + a = 0 + b = 0 + + loop do + a = nums[a] + b = nums[b] + b = nums[b] + + break if a == b + end + + a = 0 + loop do + a = nums[a] + b = nums[b] + break if a == b + end + + a +end diff --git a/ruby/0338-counting-bits.rb b/ruby/0338-counting-bits.rb new file mode 100644 index 000000000..802567144 --- /dev/null +++ b/ruby/0338-counting-bits.rb @@ -0,0 +1,14 @@ +def count_bits(n) + bits = [] + offset = 2 + (n + 1).times do |nbr| + bits << if nbr <= 2 || nbr == (offset * 2) + nbr.zero? ? 0 : 1 + else + (bits[offset] + bits[nbr - offset]) + end + + offset *= 2 if nbr == offset * 2 + end + bits +end diff --git a/ruby/0347-top-k-frequent-elements.rb b/ruby/0347-top-k-frequent-elements.rb new file mode 100644 index 000000000..dbcec1c16 --- /dev/null +++ b/ruby/0347-top-k-frequent-elements.rb @@ -0,0 +1,19 @@ +def top_k_frequent(nums, k) + hash = Hash.new(0) + max_freq = 0 + nums.each do |num| + hash[num] += 1 + max_freq = hash[num] if hash[num] > max_freq + end + counts = Array.new(max_freq + 1) { [] } + hash.each { |k, v| counts[v] << k } + top_k = [] + max_freq.downto(1) do |n| + return top_k if k <= 0 + next if counts[n].empty? + + top_k.concat(counts[n].take(k)) + k -= counts[n].length + end + top_k +end diff --git a/ruby/0371-sum-of-two-integers.rb b/ruby/0371-sum-of-two-integers.rb new file mode 100644 index 000000000..978dfc339 --- /dev/null +++ b/ruby/0371-sum-of-two-integers.rb @@ -0,0 +1,42 @@ +def get_sum(a, b) + mask = 0xffffffff + until b.zero? # until no carries left + tmp = (a & b) << 1 # carries + a = (a ^ b) & mask # addition w/o carries + b = tmp & mask + end + a = ~(a ^ mask) if a > (mask >> 1) + a +end + +# Ugly Solution +def get_sum(a, b) + mask = 0xffffffff # 32 bit maximum + res = 0 + carry = 0 + 32.times do |n| + bit_a = (a >> n) & 1 + bit_b = (b >> n) & 1 + + if (bit_a | bit_b).zero? + if carry == 1 + carry -= 1 + res |= (1 << n) + end + elsif (bit_a & bit_b) == 1 + if carry == 1 + res |= (1 << n) + else + carry += 1 + end + elsif carry.zero? + res |= (1 << n) + end + end + res &= mask + if (res >> 31) & 1 == 1 + # XOR flips rightmost 32, then NOT flips all bits + res = ~(res ^ mask) + end + res +end diff --git a/ruby/0392-is-subsequence.rb b/ruby/0392-is-subsequence.rb new file mode 100644 index 000000000..af30a5dd7 --- /dev/null +++ b/ruby/0392-is-subsequence.rb @@ -0,0 +1,15 @@ +# @param {String} s +# @param {String} t +# @return {Boolean} +def is_subsequence(s, t) + sub_str_length = 0 + main_str_length = 0 + + while sub_str_length < s.length && main_str_length < t.length do + if(s[sub_str_length] == t[main_str_length]) + sub_str_length+= 1 + end + main_str_length+= 1 + end + sub_str_length == s.length +end \ No newline at end of file diff --git a/ruby/0424-longest-repeating-character-replacement.rb b/ruby/0424-longest-repeating-character-replacement.rb new file mode 100644 index 000000000..e806f24fa --- /dev/null +++ b/ruby/0424-longest-repeating-character-replacement.rb @@ -0,0 +1,30 @@ +def character_replacement(s, k) + return 0 if s.length.zero? + return 1 if s.length == 1 + + max = 0 + window = [] + dict = Hash.new(0) + left = 0 + right = left + while true + break if s[right].nil? + + char = s[right] + window << char + dict[char] += 1 + until window.length - highest_freq(dict) <= k + dict[window.shift] -= 1 + left += 1 + end + max = window.length if window.length > max + right += 1 + end + max +end + +def highest_freq(dict) + max = 0 + dict.each { |_k, v| max = v if v > max } + max +end diff --git a/ruby/0543-diameter-of-binary-tree.rb b/ruby/0543-diameter-of-binary-tree.rb new file mode 100644 index 000000000..7833ce5a6 --- /dev/null +++ b/ruby/0543-diameter-of-binary-tree.rb @@ -0,0 +1,16 @@ +def diameter_of_binary_tree(root) + $max = 0 + max_height = diameter(root) + $max > max_height ? $max : max_height +end + +def diameter(root) + return -1 if root.nil? + + left = 1 + diameter(root.left) + right = 1 + diameter(root.right) + diameter = left + right + $max = diameter if diameter > $max + + left > right ? left : right +end diff --git a/ruby/0572-subtree-of-another-tree.rb b/ruby/0572-subtree-of-another-tree.rb new file mode 100644 index 000000000..1ccd9112f --- /dev/null +++ b/ruby/0572-subtree-of-another-tree.rb @@ -0,0 +1,24 @@ +def is_subtree(root, sub_root) + return true if root.nil? && sub_root.nil? + return true if sub_root.nil? + return false if root.nil? + + if same_tree?(root, sub_root) + true + else + is_subtree(root.left, sub_root) || + is_subtree(root.right, sub_root) + end +end + +def same_tree?(p, q) + if p.nil? && q.nil? + true + elsif p && q + (p.val == q.val) && + same_tree?(p.left, q.left) && + same_tree?(p.right, q.right) + else + false + end +end diff --git a/ruby/0703-kth-largest-element-in-a-stream.rb b/ruby/0703-kth-largest-element-in-a-stream.rb new file mode 100644 index 000000000..dda9c8365 --- /dev/null +++ b/ruby/0703-kth-largest-element-in-a-stream.rb @@ -0,0 +1,16 @@ +require "rb_heap" + +class KthLargest + def initialize(k, nums) + @k = k + @heap = Heap.new + nums.each { |num| @heap << num } + @heap.pop until @heap.size <= @k + end + + def add(val) + @heap << val + @heap.pop until @heap.size <= @k + @heap.peak + end +end diff --git a/ruby/0704-binary-search.rb b/ruby/0704-binary-search.rb new file mode 100644 index 000000000..9666b8db0 --- /dev/null +++ b/ruby/0704-binary-search.rb @@ -0,0 +1,33 @@ +# Iteraton +def search(nums, target) + return -1 if nums.empty? + + half = nums.length / 2 + case nums[half] <=> target + when 1 + search(nums[0...half], target) + when -1 + idx = search(nums[half + 1..-1], target) + (idx == -1 ? -1 : half + 1 + idx) + when 0 + half + end +end + +# Recursion +def search(nums, target) + left = 0 + right = nums.length - 1 + while left <= right + half = (left + right) / 2 + case nums[half] <=> target + when 1 + right = half - 1 + when -1 + left = half + 1 + when 0 + return half + end + end + -1 +end diff --git a/ruby/0739-daily-temperatures.rb b/ruby/0739-daily-temperatures.rb new file mode 100644 index 000000000..92ec91a00 --- /dev/null +++ b/ruby/0739-daily-temperatures.rb @@ -0,0 +1,17 @@ +# @param {Integer[]} temperatures +# @return {Integer[]} +def daily_temperatures(temperatures) + ans = Array.new(temperatures.length, 0) + stack = [] + + temperatures.each_with_index do |t, i| + while !stack.empty? && t > stack[-1][:t] + data = stack.pop + ans[data[:i]] = i - data[:i] + end + + stack << {t: t, i: i} + end + + ans +end diff --git a/ruby/0746-min-cost-climbing-stairs.rb b/ruby/0746-min-cost-climbing-stairs.rb new file mode 100644 index 000000000..5fe7f960b --- /dev/null +++ b/ruby/0746-min-cost-climbing-stairs.rb @@ -0,0 +1,15 @@ +def min_cost_climbing_stairs(cost) + return 0 if cost.empty? + return cost.first if cost.length == 1 + + second = 0 + first = cost[-1] + (cost.length - 2).downto(0) do |idx| + cost1 = cost[idx] + first + cost2 = cost[idx] + second + tmp = cost1 > cost2 ? cost2 : cost1 + second = first + first = tmp + end + first > second ? second : first +end diff --git a/ruby/0763-partition-labels.rb b/ruby/0763-partition-labels.rb new file mode 100644 index 000000000..0911a1976 --- /dev/null +++ b/ruby/0763-partition-labels.rb @@ -0,0 +1,24 @@ + +def partition_labels(s) + last_index = Hash.new(0) + + s.each_char.with_index do |char,index| + last_index[char] = index + end + + result = [] + size = s_end = 0 + + s.each_char.with_index do |char,index| + size +=1 + s_end = [last_index[char],s_end].max + + if (index == s_end) + result.append(size) + size =0 + end + end + + return result + +end \ No newline at end of file diff --git a/ruby/0846-hand-of-straights.rb b/ruby/0846-hand-of-straights.rb new file mode 100644 index 000000000..bf4409192 --- /dev/null +++ b/ruby/0846-hand-of-straights.rb @@ -0,0 +1,28 @@ +def is_n_straight_hand(hand, w) + hand = hand.sort() + index = 0 + residual = [] + cur = [] + while(hand.length > 0) + num = hand.shift + if cur.size == 0 + cur << num + else + if num == cur[-1] + 1 + cur << num + elsif num == cur[-1] + residual << num + elsif num > cur[-1] + 1 + return false + end + end + if cur.length == w + hand = residual + hand + cur = [] + residual = [] + end + + end + return (cur.size == 0 or cur.size == w) + +end \ No newline at end of file diff --git a/ruby/0853-car-fleet.rb b/ruby/0853-car-fleet.rb new file mode 100644 index 000000000..5e4ba1416 --- /dev/null +++ b/ruby/0853-car-fleet.rb @@ -0,0 +1,19 @@ +# @param {Integer} target +# @param {Integer[]} position +# @param {Integer[]} speed +# @return {Integer} +def car_fleet(target, position, speed) + cars = position.zip(speed).sort_by(&:first) + + stack = [] + cars.each do |car| + position, speed = car + time = (target - position) / speed.to_f + while !stack.empty? && stack[-1] <= time + prev = stack.pop + end + stack << time + end + + stack.length +end diff --git a/ruby/0875-koko-eating-bananas.rb b/ruby/0875-koko-eating-bananas.rb new file mode 100644 index 000000000..9f5341cde --- /dev/null +++ b/ruby/0875-koko-eating-bananas.rb @@ -0,0 +1,26 @@ +# @param {Integer[]} piles +# @param {Integer} h +# @return {Integer} +def min_eating_speed(piles, h) + l = 1 + r = piles.max + min_speed = r + + while l <= r + speed = (l + r) / 2 + + time_taken = 0 + piles.each do |pile| + time_taken += (pile / speed.to_f).ceil + end + + if time_taken <= h + r = speed - 1 + min_speed = speed < min_speed ? speed : min_speed + else + l = speed + 1 + end + end + + min_speed +end diff --git a/ruby/0981-Time-Based-Key-Value-Store.rb b/ruby/0981-Time-Based-Key-Value-Store.rb new file mode 100644 index 000000000..85da401db --- /dev/null +++ b/ruby/0981-Time-Based-Key-Value-Store.rb @@ -0,0 +1,54 @@ +class TimeMap + def initialize() + @map = {} + end + + +=begin + :type key: String + :type value: String + :type timestamp: Integer + :rtype: Void +=end + def set(key, value, timestamp) + @map[key] = [] unless @map.key?(key) + + @map[key] << { v: value, t: timestamp } + end + + +=begin + :type key: String + :type timestamp: Integer + :rtype: String +=end + def get(key, timestamp) + return "" unless @map.key?(key) + + entries = @map[key] + return "" if timestamp < entries[0][:t] + return entries[-1][:v] if timestamp >= entries[-1][:t] + + l = 0 + r = entries.length - 1 + max = entries[0] + while l <= r + mid = ( l + r ) / 2 + curr = entries[mid] + + if curr[:t] < timestamp + max = curr + l = mid + 1 + else + r = mid - 1 + end + end + + max[:v] + end +end + +# Your TimeMap object will be instantiated and called as such: +# obj = TimeMap.new() +# obj.set(key, value, timestamp) +# param_2 = obj.get(key, timestamp) diff --git a/ruby/0981-time-based-key-value-store.rb b/ruby/0981-time-based-key-value-store.rb new file mode 100644 index 000000000..85da401db --- /dev/null +++ b/ruby/0981-time-based-key-value-store.rb @@ -0,0 +1,54 @@ +class TimeMap + def initialize() + @map = {} + end + + +=begin + :type key: String + :type value: String + :type timestamp: Integer + :rtype: Void +=end + def set(key, value, timestamp) + @map[key] = [] unless @map.key?(key) + + @map[key] << { v: value, t: timestamp } + end + + +=begin + :type key: String + :type timestamp: Integer + :rtype: String +=end + def get(key, timestamp) + return "" unless @map.key?(key) + + entries = @map[key] + return "" if timestamp < entries[0][:t] + return entries[-1][:v] if timestamp >= entries[-1][:t] + + l = 0 + r = entries.length - 1 + max = entries[0] + while l <= r + mid = ( l + r ) / 2 + curr = entries[mid] + + if curr[:t] < timestamp + max = curr + l = mid + 1 + else + r = mid - 1 + end + end + + max[:v] + end +end + +# Your TimeMap object will be instantiated and called as such: +# obj = TimeMap.new() +# obj.set(key, value, timestamp) +# param_2 = obj.get(key, timestamp) diff --git a/ruby/1046-Last-Stone-Weight.rb b/ruby/1046-Last-Stone-Weight.rb new file mode 100644 index 000000000..0e243f4d9 --- /dev/null +++ b/ruby/1046-Last-Stone-Weight.rb @@ -0,0 +1,15 @@ +require "rubygems" +require "algorithms" +include Containers + +def last_stone_weight(stones) + heap = MaxHeap.new + stones.each { |stone| heap << stone } + until heap.size <= 1 + stone1 = heap.pop + stone2 = heap.pop + heap << (stone1 - stone2).abs if stone1 != stone2 + end + last = heap.pop + last.nil? ? 0 : last +end diff --git a/ruby/1046-last-stone-weight.rb b/ruby/1046-last-stone-weight.rb new file mode 100644 index 000000000..0e243f4d9 --- /dev/null +++ b/ruby/1046-last-stone-weight.rb @@ -0,0 +1,15 @@ +require "rubygems" +require "algorithms" +include Containers + +def last_stone_weight(stones) + heap = MaxHeap.new + stones.each { |stone| heap << stone } + until heap.size <= 1 + stone1 = heap.pop + stone2 = heap.pop + heap << (stone1 - stone2).abs if stone1 != stone2 + end + last = heap.pop + last.nil? ? 0 : last +end diff --git a/ruby/1299-replace-elements-with-greatest-element-on-right-side.rb b/ruby/1299-replace-elements-with-greatest-element-on-right-side.rb new file mode 100644 index 000000000..5756a5480 --- /dev/null +++ b/ruby/1299-replace-elements-with-greatest-element-on-right-side.rb @@ -0,0 +1,9 @@ +def replace_elements(arr) + right_max = -1 + (arr.length - 1).downto(0) do |i| + cur_max = [right_max, arr[i]].max + arr[i] = right_max + right_max = cur_max + end + arr +end diff --git a/ruby/1584-Min-Cost-to-Connect-All-Points.rb b/ruby/1584-Min-Cost-to-Connect-All-Points.rb new file mode 100644 index 000000000..d0300b80c --- /dev/null +++ b/ruby/1584-Min-Cost-to-Connect-All-Points.rb @@ -0,0 +1,33 @@ +# @param {Integer[][]} points +# @return {Integer} +def min_cost_connect_points(points) + manhattan = ->(x, y) { (x[0] - y[0]).abs + (x[1] - y[1]).abs } + + to_visit = Set.new(points) + + result = 0 + bil = 1_000_000_000 + cost_to = Hash.new { bil } + + current_point = points[0] + to_visit.delete current_point + + (points.size - 1).times do |_i| + min = bil + next_point = nil + + to_visit.each do |point| + cost_to[point] = [cost_to[point], manhattan.call(current_point, point)].min + if min > cost_to[point] + min = cost_to[point] + next_point = point + end + end + + current_point = next_point + result += min + to_visit.delete current_point + end + + result +end diff --git a/ruby/1584-min-cost-to-connect-all-points.rb b/ruby/1584-min-cost-to-connect-all-points.rb new file mode 100644 index 000000000..d0300b80c --- /dev/null +++ b/ruby/1584-min-cost-to-connect-all-points.rb @@ -0,0 +1,33 @@ +# @param {Integer[][]} points +# @return {Integer} +def min_cost_connect_points(points) + manhattan = ->(x, y) { (x[0] - y[0]).abs + (x[1] - y[1]).abs } + + to_visit = Set.new(points) + + result = 0 + bil = 1_000_000_000 + cost_to = Hash.new { bil } + + current_point = points[0] + to_visit.delete current_point + + (points.size - 1).times do |_i| + min = bil + next_point = nil + + to_visit.each do |point| + cost_to[point] = [cost_to[point], manhattan.call(current_point, point)].min + if min > cost_to[point] + min = cost_to[point] + next_point = point + end + end + + current_point = next_point + result += min + to_visit.delete current_point + end + + result +end diff --git a/ruby/1899-merge-triplets-to-form-target-triplet.rb b/ruby/1899-merge-triplets-to-form-target-triplet.rb new file mode 100644 index 000000000..ce5542568 --- /dev/null +++ b/ruby/1899-merge-triplets-to-form-target-triplet.rb @@ -0,0 +1,10 @@ +def merge_triplets(triplets, target) + good = Set.new([]) + triplets.each do |triplet| + next if ((triplet[0] > target[0]) || (triplet[1] > target[1]) || (triplet[2] > target[2])) + triplet.each_with_index do |value,index| + good.add(index) if value == target[index] + end + end + return good.length == 3 +end \ No newline at end of file diff --git a/ruby/1929-concatenation-of-array.rb b/ruby/1929-concatenation-of-array.rb new file mode 100644 index 000000000..408873a7e --- /dev/null +++ b/ruby/1929-concatenation-of-array.rb @@ -0,0 +1,5 @@ +# @param {Integer[]} nums +# @return {Integer[]} +def get_concatenation(nums) + nums + nums +end diff --git a/ruby/2013-detect-squares.rb b/ruby/2013-detect-squares.rb new file mode 100644 index 000000000..ac390954b --- /dev/null +++ b/ruby/2013-detect-squares.rb @@ -0,0 +1,28 @@ +class DetectSquares + def initialize() + @points_count = Hash.new(0) + @points = [] + end + + + def add(point) + @points_count[point] +=1 + @points.append(point) + end + + def count(point) + result = 0 + px = point[0] + py = point[1] + + @points.each do |p| + next if (((py-p[1]).abs != (px-p[0]).abs) || (p[0]==px) ||(p[1]==py)) + result += @points_count[[p[0],py]] * @points_count[[px,p[1]]] + end + + return result + + end + + +end \ No newline at end of file diff --git a/rust/0001-two-sum.rs b/rust/0001-two-sum.rs new file mode 100644 index 000000000..802d918eb --- /dev/null +++ b/rust/0001-two-sum.rs @@ -0,0 +1,19 @@ +use std::collections::HashMap; + +impl Solution { + pub fn two_sum(nums: Vec, target: i32) -> Vec { + let mut map = HashMap::new(); + + for (i, n) in nums.into_iter().enumerate(){ + let diff = target - n; + + if let Some(&j) = map.get(&diff){ + return vec![i as i32, j as i32]; + }else{ + map.insert(n, i); + } + } + + unreachable!() + } +} \ No newline at end of file diff --git a/rust/0002-add-two-numbers.rs b/rust/0002-add-two-numbers.rs new file mode 100644 index 000000000..3f99ed95a --- /dev/null +++ b/rust/0002-add-two-numbers.rs @@ -0,0 +1,23 @@ +impl Solution { + pub fn add_two_numbers(l1: Option>, l2: Option>) -> Option> { + let (mut l1, mut l2) = (l1.as_ref(), l2.as_ref()); + let mut dummy = Box::new(ListNode { val: 0, next: None }); + let mut cur = dummy.as_mut(); + let mut carry = 0; + + while l1.is_some() || l2.is_some() || carry > 0 { + let val1 = l1.map(|n| n.val).unwrap_or_default(); + let val2 = l2.map(|n| n.val).unwrap_or_default(); + + let sum = val1 + val2 + carry; + carry = sum / 10; + cur.next = Some(Box::new(ListNode { val: sum % 10, next: None })); + cur = cur.next.as_mut().unwrap(); + + if let Some(node) = l1 { l1 = node.next.as_ref() } + if let Some(node) = l2 { l2 = node.next.as_ref() } + } + + dummy.next + } +} diff --git a/rust/0003-longest-substring-without-repeating-characters.rs b/rust/0003-longest-substring-without-repeating-characters.rs new file mode 100644 index 000000000..fb514116f --- /dev/null +++ b/rust/0003-longest-substring-without-repeating-characters.rs @@ -0,0 +1,19 @@ +use std::collections::VecDeque; + +impl Solution { + pub fn length_of_longest_substring(s: String) -> i32 { + let mut set: VecDeque = VecDeque::new(); + let mut longest = 0; + + for c in s.chars() { + while set.contains(&c) { + set.pop_front(); + } + + set.push_back(c); + longest = longest.max(set.len()); + } + + longest as i32 + } +} diff --git a/rust/0004-median-of-two-sorted-arrays.rs b/rust/0004-median-of-two-sorted-arrays.rs new file mode 100644 index 000000000..f29288450 --- /dev/null +++ b/rust/0004-median-of-two-sorted-arrays.rs @@ -0,0 +1,71 @@ +// Solution 1: focus on logic +impl Solution { + pub fn find_median_sorted_arrays(mut nums1: Vec, nums2: Vec) -> f64 { + for val in nums2 { + nums1.insert(nums1.binary_search(&val).unwrap_or_else(|e| e), val); + } + if nums1.len()%2==0 { + (nums1[(nums1.len()-1) / 2] + nums1[nums1.len() / 2]) as f64 / 2.0 + } else { + nums1[(nums1.len()-1) / 2] as f64 + } + } +} + +// Solution 2: focus on binary search +impl Solution { + pub fn find_median_sorted_arrays(mut nums1: Vec, mut nums2: Vec) -> f64 { + let total = nums1.len() + nums2.len(); + let half = total / 2; + + if nums1.len() > nums2.len() { + std::mem::swap(&mut nums1, &mut nums2); + } + + let mut left = 0; + let mut right = nums1.len(); + + while left <= right { + let mid = left + (right - left) / 2; + let pointer = half - mid; + + let base_left = if mid > 0 { + nums1[mid - 1] as f64 + } else { + f64::MIN + }; + + let base_right = if mid < nums1.len() { + nums1[mid] as f64 + } else { + f64::MAX + }; + + let ref_left = if pointer > 0 { + nums2[pointer - 1] as f64 + } else { + f64::MIN + }; + + let ref_right = if pointer < nums2.len() { + nums2[pointer] as f64 + } else { + f64::MAX + }; + + if base_left <= ref_right && ref_left <= base_right { + if total % 2 == 1 { + return base_right.min(ref_right); + } else { + return (base_left.max(ref_left) + base_right.min(ref_right)) / 2.0; + } + } else if base_left > ref_right { + right = mid - 1; + } else { + left = mid + 1; + } + } + + panic!("Arrays are not sorted"); + } +} diff --git a/rust/0005-longest-palindromic-substring.rs b/rust/0005-longest-palindromic-substring.rs new file mode 100644 index 000000000..a1719e25c --- /dev/null +++ b/rust/0005-longest-palindromic-substring.rs @@ -0,0 +1,38 @@ +impl Solution { + pub fn longest_palindrome(s: String) -> String { + let s = s.chars().collect::>(); + let (mut left, mut right, length): (i32, i32, i32) = (0, 0, s.len() as i32); + + if length == 1 { + return s[0].to_string(); + } + + for i in 0..length { + // odd length + let (mut l, mut r) = (i, i); + + while l >= 0 && r < length && s[l as usize] == s[r as usize] { + if r - l > right - left { + left = l; + right = r; + } + l -= 1; + r += 1; + } + + // even length + let (mut l, mut r) = (i, i + 1); + + while l >= 0 && r < length && s[l as usize] == s[r as usize] { + if r - l > right - left { + left = l; + right = r; + } + l -= 1; + r += 1; + } + } + + s[left as usize..=right as usize].iter().collect::() + } +} diff --git a/rust/0007-reverse-integer.rs b/rust/0007-reverse-integer.rs new file mode 100644 index 000000000..fe6ab28e7 --- /dev/null +++ b/rust/0007-reverse-integer.rs @@ -0,0 +1,22 @@ +use std::i32::{MAX, MIN}; + +impl Solution { + pub fn reverse(x: i32) -> i32 { + let mut res = 0; + let mut x = x; + while x != 0 { + let digit = x % 10; + x /= 10; + + if res > MAX / 10 || (res == MAX / 10 && digit > MAX % 10) { + return 0; + } + if res < MIN / 10 || (res == MIN / 10 && digit < MIN % 10) { + return 0; + } + + res = (res * 10) + digit; + } + res + } +} \ No newline at end of file diff --git a/rust/0009-palindrome-number.rs b/rust/0009-palindrome-number.rs new file mode 100644 index 000000000..438c9b511 --- /dev/null +++ b/rust/0009-palindrome-number.rs @@ -0,0 +1,18 @@ +impl Solution { + pub fn is_palindrome(x: i32) -> bool { + let numbers = x.to_string(); + let numbers = numbers.as_bytes(); + + let (mut left, mut right) = (0, numbers.len() - 1); + + while left < right { + if numbers[left] != numbers[right] { + return false; + } + left += 1; + right -= 1; + } + + true + } +} diff --git a/rust/0011-container-with-most-water.rs b/rust/0011-container-with-most-water.rs new file mode 100644 index 000000000..6c4507b09 --- /dev/null +++ b/rust/0011-container-with-most-water.rs @@ -0,0 +1,18 @@ +impl Solution { + pub fn max_area(height: Vec) -> i32 { + let (mut max_area, mut l, mut r) = (0, 0, height.len() - 1); + + while l < r { + let area = ((r - l) as i32) * height[l].min(height[r]); + max_area = area.max(max_area); + + if height[l] > height[r] { + r -= 1; + } else { + l += 1; + } + } + + max_area + } +} diff --git a/rust/0012-integer-to-roman.rs b/rust/0012-integer-to-roman.rs new file mode 100644 index 000000000..8a81532cd --- /dev/null +++ b/rust/0012-integer-to-roman.rs @@ -0,0 +1,20 @@ +impl Solution { + pub fn int_to_roman(num: i32) -> String { + let int = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]; + let roman = [ + "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I", + ]; + let mut result = String::new(); + let mut num = num; + + for i in 0..13 { + if num / int[i] > 0 { + let count = num / int[i]; + result.push_str(&roman[i].repeat(count as usize)); + num %= int[i]; + } + } + + result + } +} diff --git a/rust/0013-roman-to-integer.rs b/rust/0013-roman-to-integer.rs new file mode 100644 index 000000000..f6b73f5b7 --- /dev/null +++ b/rust/0013-roman-to-integer.rs @@ -0,0 +1,47 @@ +impl Solution { + pub fn roman_to_int(s: String) -> i32 { + let s: Vec = s.chars().collect(); + let mut res = 0; + + for i in 0..s.len() { + if i + 1 < s.len() && Self::get_value(s[i]) < Self::get_value(s[i + 1]) { + res -= Self::get_value(s[i]); + } else { + res += Self::get_value(s[i]); + } + } + + res + } + + pub fn get_value(ch: char) -> i32 { + match ch { + 'I' => 1, + 'V' => 5, + 'X' => 10, + 'L' => 50, + 'C' => 100, + 'D' => 500, + 'M' => 1000, + _ => 0, + } + } + + pub fn roman_to_int_functional(s: String) -> i32 { + s.chars().rfold(0, |acc, ch| { + acc + match ch { + 'I' if acc >= 5 => -1, + 'I' => 1, + 'V' => 5, + 'X' if acc >= 50 => -10, + 'X' => 10, + 'L' => 50, + 'C' if acc >= 500 => -100, + 'C' => 100, + 'D' => 500, + 'M' => 1000, + _ => 0, + } + }) + } +} diff --git a/rust/0014-longest-common-prefix.rs b/rust/0014-longest-common-prefix.rs new file mode 100644 index 000000000..83894064d --- /dev/null +++ b/rust/0014-longest-common-prefix.rs @@ -0,0 +1,43 @@ +impl Solution { + pub fn longest_common_prefix(strs: Vec) -> String { + if strs.len() == 0 { + return "".to_string(); + } + if strs.len() == 1 { + return strs[0].clone(); + } + if strs[0].len() == 0 { + return "".to_string(); + } + let mut common = String::new(); + let mut same = true; + let mut i = 0; + while same { + let common_char = match strs[0].chars().nth(i) { + Some(c) => { + common.push(c); + c + } + None => return common, + }; + + for s in &strs { + if let Some(c) = s.chars().nth(i) { + if c != common_char { + same = false; + break; + } + } else { + same = false; + break; + } + } + i += 1; + } + if common.len() > 0 { + common[..common.len() - 1].to_string() + } else { + common + } + } +} diff --git a/rust/0015-3sum.rs b/rust/0015-3sum.rs new file mode 100644 index 000000000..2b13d6838 --- /dev/null +++ b/rust/0015-3sum.rs @@ -0,0 +1,61 @@ +use std::cmp::Ordering::{Equal, Greater, Less}; + +impl Solution { + pub fn three_sum(mut numbers: Vec) -> Vec> { + numbers.sort_unstable(); + + let mut ans: Vec> = Vec::new(); + + for i in 0..numbers.len() { + if i > 0 && numbers[i] == numbers[i - 1] { + continue; + } + + let (mut l, mut r) = (i + 1, numbers.len() - 1); + + while l < r { + match (numbers[i] + numbers[l] + numbers[r]).cmp(&0) { + Less => l += 1, + Greater => r -= 1, + Equal => { + ans.push(vec![numbers[i], numbers[l], numbers[r]]); + l += 1; + while numbers[l] == numbers[l - 1] && l < r { + l += 1; + } + r -= 1; + while numbers[r] == numbers[r + 1] && l < r { + r -= 1; + } + } + } + } + } + + ans + } +} + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rust/0017-letter-combinations-of-a-phone-number.rs b/rust/0017-letter-combinations-of-a-phone-number.rs new file mode 100644 index 000000000..f22c5f801 --- /dev/null +++ b/rust/0017-letter-combinations-of-a-phone-number.rs @@ -0,0 +1,37 @@ +use std::collections::HashMap; + +impl Solution { + fn backtrack(i: usize, curr_str: String, result: &mut Vec, digits: &String, d_c_map: &HashMap) { + if curr_str.len() == digits.len() { + result.push(curr_str); + return; + } + + let letters = d_c_map.get(&digits.chars().nth(i).unwrap()).unwrap().to_string(); + for ch in letters.chars() { + let mut append_str = curr_str.clone(); + append_str.push(ch); + Self::backtrack(i + 1, append_str, result, digits, d_c_map); + } + } + + pub fn letter_combinations(digits: String) -> Vec { + let mut result = vec![]; + let d_c_map = HashMap::from([ + ('2', "abc"), + ('3', "def"), + ('4', "ghi"), + ('5', "jkl"), + ('6', "mno"), + ('7', "qprs"), + ('8', "tuv"), + ('9', "wxyz"), + ]); + + if !digits.is_empty() { + Solution::backtrack(0, "".to_string(), &mut result, &digits, &d_c_map); + } + + result + } +} diff --git a/rust/0019-remove-nth-node-from-end-of-list.rs b/rust/0019-remove-nth-node-from-end-of-list.rs new file mode 100644 index 000000000..95d49aaef --- /dev/null +++ b/rust/0019-remove-nth-node-from-end-of-list.rs @@ -0,0 +1,21 @@ +impl Solution { + pub fn remove_nth_from_end(head: Option>, n: i32) -> Option> { + let mut dummy = Box::new(ListNode { val: 0, next: head.clone() }); + let (mut left, mut right) = (dummy.as_mut(), head); + + let mut n = n; + while n > 0 && right.is_some() { + right = right.unwrap().next; + n -= 1; + } + + while let Some(r) = right { + left = left.next.as_mut().unwrap(); + right = r.next; + } + + left.next = left.next.take().unwrap().next.take(); + + dummy.next + } +} diff --git a/rust/0019-remove-nth-node-from-end-of-the-list.rs b/rust/0019-remove-nth-node-from-end-of-the-list.rs new file mode 100644 index 000000000..a6787d021 --- /dev/null +++ b/rust/0019-remove-nth-node-from-end-of-the-list.rs @@ -0,0 +1,26 @@ +impl Solution { + pub fn remove_nth_from_end(head: Option>, n: i32)-> Option> { + let mut list_len=0; + let mut cur=&head; + while let Some(node)=cur{ + list_len+=1; + cur=&node.next; + } + let mut ans=Some(Box::new(ListNode{ + val:1, + next:head + })); + let mut list=&mut ans; + let mut pos=0; + while let Some(node)=list{ + if pos==list_len-n{ + node.next=node.next.clone().unwrap().next.take(); + break; + } + pos+=1; + list=&mut node.next; + } + ans.unwrap().next.take() + } +} + diff --git a/rust/0020-valid-parentheses.rs b/rust/0020-valid-parentheses.rs new file mode 100644 index 000000000..96e2abdcd --- /dev/null +++ b/rust/0020-valid-parentheses.rs @@ -0,0 +1,25 @@ +use std::collections::HashMap; + +impl Solution { + pub fn is_valid(s: String) -> bool { + let mut stack: Vec = Vec::new(); + let opening = HashMap::from([(']', '['), (')', '('), ('}', '{')]); + + for c in s.chars() { + match c { + '(' => stack.push(c), + '[' => stack.push(c), + '{' => stack.push(c), + _ => { + if stack.iter().last() == opening.get(&c) { + stack.pop(); + } else { + return false; + } + } + } + } + + stack.is_empty() + } +} diff --git a/rust/0021-merge-two-sorted-lists.rs b/rust/0021-merge-two-sorted-lists.rs new file mode 100644 index 000000000..233ebc0dd --- /dev/null +++ b/rust/0021-merge-two-sorted-lists.rs @@ -0,0 +1,46 @@ +impl Solution { + pub fn merge_two_lists( + list1: Option>, + list2: Option>, + ) -> Option> { + match (list1, list2) { + (Some(list1), None) => Some(list1), + (None, Some(list2)) => Some(list2), + (None, None) => None, + (Some(l1), Some(l2)) => { + if l1.val < l2.val { + return Some(Box::new(ListNode { + val: l1.val, + next: Solution::merge_two_lists(l1.next, Some(l2)), + })); + } else { + return Some(Box::new(ListNode { + val: l2.val, + next: Solution::merge_two_lists(Some(l1), l2.next), + })); + } + } + } + } +} + +//without recursion +impl Solution { + pub fn merge_two_lists( + mut l1: Option>, + mut l2: Option>, + ) -> Option> { + // Tail is the rightmost part of l1 that we keep swapping + let mut tail = &mut l1; + while l2.is_some() { + if tail.is_none() || l2.as_ref()?.val < tail.as_ref()?.val { + // Keep swapping the end part of l1 + // so that the smallest nodes appear + // first in l1 + std::mem::swap(tail, &mut l2); + } + tail = &mut tail.as_mut()?.next; + } + l1 + } +} diff --git a/rust/0022-generate-parentheses.rs b/rust/0022-generate-parentheses.rs new file mode 100644 index 000000000..fb35b4129 --- /dev/null +++ b/rust/0022-generate-parentheses.rs @@ -0,0 +1,26 @@ +impl Solution { + pub fn generate_parenthesis(n: i32) -> Vec { + // Using the function stack instead of an explicitly allocated Vec + let mut res: Vec = vec![]; + + fn backtrack(res: &mut Vec, s: String, open: i32, close: i32) { + if open == 0 && close == 0 { + res.push(s); + return; + } + if open == close { + backtrack(res, s.clone() + "(", open - 1, close); + } else { + if open > 0 { + backtrack(res, s.clone() + "(", open - 1, close); + } + if close > 0 { + backtrack(res, s.clone() + ")", open, close - 1); + } + } + } + + backtrack(&mut res, String::from(""), n, n); + res + } +} diff --git a/rust/0023-merge-k-sorted-lists.rs b/rust/0023-merge-k-sorted-lists.rs new file mode 100644 index 000000000..383e3d1a9 --- /dev/null +++ b/rust/0023-merge-k-sorted-lists.rs @@ -0,0 +1,44 @@ +impl Solution { + pub fn merge_k_lists(lists: Vec>>) -> Option> { + fn merge(list1: Option>, list2: Option>) -> Option> { + match (list1, list2) { + (None, None) => None, + (None, Some(l)) | (Some(l), None) => Some(l), + (Some(l1), Some(l2)) => { + if l1.val < l2.val { + return Some(Box::new(ListNode { + val: l1.val, + next: merge(l1.next, Some(l2)), + })); + } + Some(Box::new(ListNode { + val: l2.val, + next: merge(Some(l1), l2.next), + })) + } + } + } + + if lists.is_empty() { + return None; + } + + let mut lists = lists; + + while lists.len() > 1 { + let mut merged = vec![]; + for i in (0..lists.len()).step_by(2) { + let l1 = lists[i].take(); + let l2 = if i + 1 < lists.len() { + lists[i + 1].take() + } else { + None + }; + merged.push(merge(l1, l2)) + } + lists = merged; + } + + lists[0].take() + } +} diff --git a/rust/0025-reverse-nodes-in-k-group.rs b/rust/0025-reverse-nodes-in-k-group.rs new file mode 100644 index 000000000..189f6f340 --- /dev/null +++ b/rust/0025-reverse-nodes-in-k-group.rs @@ -0,0 +1,48 @@ +impl Solution { + pub fn reverse_k_group(head: Option>, k: i32) -> Option> { + let mut dummy = Some(Box::new(ListNode {next: head, val: 0 })); + let mut cur = dummy.as_mut(); + + 'outer: loop { + let mut start = cur.as_mut().unwrap().next.take(); + if start.is_none() { + break 'outer; + } + + let mut end = start.as_mut(); + for _ in 0..(k - 1) { + end = end.unwrap().next.as_mut(); + + if end.is_none() { + cur.as_mut().unwrap().next = start; + break 'outer; + } + } + + let mut tail = end.as_mut().unwrap().next.take(); + let end = Solution::reverse(start, tail); + cur.as_mut().unwrap().next = end; + + for _ in 0..k { + cur = cur.unwrap().next.as_mut() + } + } + dummy.unwrap().next + } + + fn reverse( + mut head: Option>, + tail: Option>, + ) -> Option> { + let mut prev = tail; + let mut cur = head; + + while let Some(mut cur_node) = cur { + let mut next = cur_node.next.take(); + cur_node.next = prev.take(); + prev = Some(cur_node); + cur = next + } + prev + } +} \ No newline at end of file diff --git a/rust/0026-remove-duplicates-from-sorted-array.rs b/rust/0026-remove-duplicates-from-sorted-array.rs new file mode 100644 index 000000000..c80bda277 --- /dev/null +++ b/rust/0026-remove-duplicates-from-sorted-array.rs @@ -0,0 +1,15 @@ +impl Solution { + pub fn remove_duplicates(nums: &mut Vec) -> i32 { + let mut dup_count = 0; + + for i in 1..nums.len() { + if nums[i] == nums[i - 1] { + dup_count += 1 + } + + nums[i - dup_count] = nums[i]; + } + + (nums.len() - dup_count) as i32 + } +} \ No newline at end of file diff --git a/rust/0027-remove-element.rs b/rust/0027-remove-element.rs new file mode 100644 index 000000000..2d85c8912 --- /dev/null +++ b/rust/0027-remove-element.rs @@ -0,0 +1,19 @@ +impl Solution { + pub fn remove_element(nums: &mut Vec, val: i32) -> i32 { + while let Some(index) = nums.iter().position(|v| *v == val) { + nums.swap_remove(index); + } + nums.len() as i32 + } + + pub fn remove_element_2(nums: &mut Vec, val: i32) -> i32 { + let mut k = 0; + for i in 0..nums.len() { + if nums[i] != val { + nums[k] = nums[i]; + k += 1; + } + } + k as i32 + } +} \ No newline at end of file diff --git a/rust/0028-find-the-index-of-the-first-occurrence-in-a-string.rs b/rust/0028-find-the-index-of-the-first-occurrence-in-a-string.rs new file mode 100644 index 000000000..d40883c3e --- /dev/null +++ b/rust/0028-find-the-index-of-the-first-occurrence-in-a-string.rs @@ -0,0 +1,48 @@ +impl Solution { + // Time O(m+n) - Space O(n) + pub fn str_str(haystack: String, needle: String) -> i32 { + let m = haystack.len(); + let n = needle.len(); + let h_chars: Vec = haystack.chars().collect(); + let n_chars: Vec = needle.chars().collect(); + // Base case, empty needle. + if n == 0 { + return 0; + } + // LPS generation section. + let mut lps: Vec = vec![0; n]; + let (mut left, mut right) = (0, 1); + while right < n { + if n_chars[left] == n_chars[right] { + // If the characters match the lps is one character longer. + lps[right] = left + 1; + left += 1; + right += 1; + } else if left == 0 { + // If they don't match and the left pointer is at the start, + // no characters match. + lps[right] = 0; + right += 1; + } else { + // Otherwise, we need to check the previous lps. + left = lps[left - 1]; + } + } + // Here we start the KMP proper. + let (mut haystack_idx, mut needle_idx) = (0, 0); + while haystack_idx < m { + if h_chars[haystack_idx] == n_chars[needle_idx] { + haystack_idx += 1; + needle_idx += 1; + } else if needle_idx == 0 { + haystack_idx += 1; + } else { + needle_idx = lps[needle_idx - 1]; + } + if needle_idx == n { + return (haystack_idx - n) as i32; + } + } + -1 + } +} diff --git a/rust/0033-search-in-rotated-sorted-array.rs b/rust/0033-search-in-rotated-sorted-array.rs new file mode 100644 index 000000000..0ddb38e9b --- /dev/null +++ b/rust/0033-search-in-rotated-sorted-array.rs @@ -0,0 +1,29 @@ +impl Solution { + pub fn search(nums: Vec, target: i32) -> i32 { + let (mut l, mut r) = (0, nums.len() - 1); + + while l <= r { + let m = (l + r) / 2; + + if nums[m] == target { + return m as i32; + } + + if nums[l] <= nums[m] { + if target < nums[l] || target > nums[m] { + l = m + 1; + } else { + r = m - 1; + } + } else { + if target < nums[m] || target > nums[r] { + r = m - 1; + } else { + l = m + 1; + } + } + } + + -1 + } +} diff --git a/rust/0035-search-insert-position.rs b/rust/0035-search-insert-position.rs new file mode 100644 index 000000000..988bafb1b --- /dev/null +++ b/rust/0035-search-insert-position.rs @@ -0,0 +1,8 @@ +impl Solution { + pub fn search_insert(nums: Vec, target: i32) -> i32 { + match nums.binary_search(&target) { + Ok(i) => i as i32, + Err(i) => i as i32, + } + } +} diff --git a/rust/0036-valid-sudoku.rs b/rust/0036-valid-sudoku.rs new file mode 100644 index 000000000..e40d34b3c --- /dev/null +++ b/rust/0036-valid-sudoku.rs @@ -0,0 +1,43 @@ +use std::collections::HashSet; + + +impl Solution { + pub fn is_valid_sudoku(board: Vec>) -> bool { + + let mut row: HashSet = HashSet::new(); + let mut col: HashSet = HashSet::new(); + let mut bx : HashSet = HashSet::new(); + + for i in 0..9{ + for j in 0..9{ + let r = board[i][j]; + let c = board[j][i]; + let b = board[i / 3 * 3 + j / 3][i % 3 * 3 + j % 3]; + + if r != '.'{ + if !row.insert(r){ + return false; + } + } + + if c != '.'{ + if !col.insert(c){ + return false; + } + } + + if b != '.'{ + if !bx.insert(b){ + return false; + } + } + } + + row.clear(); + col.clear(); + bx.clear(); + } + + true + } +} \ No newline at end of file diff --git a/rust/0039-combination-sum.rs b/rust/0039-combination-sum.rs new file mode 100644 index 000000000..d4e7c5452 --- /dev/null +++ b/rust/0039-combination-sum.rs @@ -0,0 +1,24 @@ +impl Solution { + fn dfs(candidates: &[i32], target: i32, result: &mut Vec>, curr: &mut Vec) { + let sum: i32 = curr.iter().sum(); + if sum == target { + result.push(curr.to_owned()); + return; + } else if sum > target { + return; + } + + for (i, &c) in candidates.iter().enumerate() { + curr.push(c); + Self::dfs(&candidates[i..], target, result, curr); + curr.pop(); + } + } + + pub fn combination_sum(candidates: Vec, target: i32) -> Vec> { + let (mut result, mut curr) = (vec![], vec![]); + Self::dfs(&candidates, target, &mut result, &mut curr); + + result + } +} diff --git a/rust/0042-trapping-rain-water.rs b/rust/0042-trapping-rain-water.rs new file mode 100644 index 000000000..f9a3267e6 --- /dev/null +++ b/rust/0042-trapping-rain-water.rs @@ -0,0 +1,71 @@ +/** + * Time Complexity: O(n) + * Space Complexity: O(1) + */ +impl Solution { + pub fn trap(height: Vec) -> i32 { + if height.is_empty() { + return 0; + } + let (mut left, mut right) = (0, height.len() - 1); + let (mut left_max, mut right_max) = (height[left], height[right]); + let mut result = 0; + + while left < right { + if left_max < right_max { + left += 1; + left_max = left_max.max(height[left]); + result += left_max - height[left]; + } else { + right-= 1; + right_max = right_max.max(height[right]); + result += right_max - height[right]; + } + } + result + } +} + +/** + * Time Complexity: O(n) + * Space Complexity: O(n) + */ +impl Solution { + pub fn trap(height: Vec) -> i32 { + let mut max_left = vec![]; + let mut max_right = vec![]; + let mut min_left_right = vec![]; + + let mut current = 0; + for (_, val) in height.iter().enumerate() { + max_left.push(current); + current = current.max(*val); + } + let mut current = 0; + for (_, val) in height.iter().enumerate().rev() { + max_right.push(current); + current = current.max(*val); + } + + max_right.reverse(); + + min_left_right = max_left + .iter() + .zip(max_right.iter()) + .map(|x| x.0.min(x.1)) + .collect(); + + min_left_right + .iter() + .enumerate() + .map(|x| { + if *x.1 - height[x.0] > 0 { + return *x.1 - height[x.0]; + } else { + return 0; + } + }) + .sum() + } +} + diff --git a/rust/0043-multiply-strings.rs b/rust/0043-multiply-strings.rs new file mode 100644 index 000000000..51d99901b --- /dev/null +++ b/rust/0043-multiply-strings.rs @@ -0,0 +1,28 @@ +impl Solution { + pub fn multiply(num1: String, num2: String) -> String { + if num1 == "0" || num2 == "0" { + return String::from('0'); + } + + let n1 = num1.chars().rev().collect::(); + let n2 = num2.chars().rev().collect::(); + let mut res = vec![0; n1.len() + n2.len()]; + for (i1, c1) in n1.chars().enumerate() { + for (i2, c2) in n2.chars().enumerate() { + let digit = (c1.to_digit(10).unwrap_or(0) * c2.to_digit(10).unwrap_or(0)); + res[i1 + i2] += digit; + res[i1 + i2 + 1] += res[i1 + i2] / 10; + res[i1 + i2] %= 10; + } + } + + while let Some(&0) = res.last() { + res.pop(); + } + + res.into_iter() + .rev() + .map(|digit| char::from_digit(digit as u32, 10).unwrap()) + .collect() + } +} diff --git a/rust/0045-jump-game-ii.rs b/rust/0045-jump-game-ii.rs new file mode 100644 index 000000000..2fb8ac5f0 --- /dev/null +++ b/rust/0045-jump-game-ii.rs @@ -0,0 +1,20 @@ +impl Solution { + pub fn jump(nums: Vec) -> i32 { + let mut res = 0; + let mut l = 0; + let mut r = 0; + + while r < nums.len() - 1 { + let mut max_jump = 0; + for i in l..=r { + max_jump = max_jump.max(i + nums[i] as usize); + } + + l = r + 1; + r = max_jump; + res += 1; + } + + return res; + } +} diff --git a/rust/0046-permutations.rs b/rust/0046-permutations.rs new file mode 100644 index 000000000..2af1b6e70 --- /dev/null +++ b/rust/0046-permutations.rs @@ -0,0 +1,21 @@ +impl Solution { + fn backtrack(first: usize, result: &mut Vec>, nums: &mut Vec) { + if first == nums.len() { + result.push(nums.to_owned()); + return; + } + + for i in first..nums.len() { + nums.swap(first, i); + Solution::backtrack(first + 1, result, nums); + nums.swap(first, i); + } + } + + pub fn permute(nums: Vec) -> Vec> { + let (mut result, mut nums) = (vec![], nums); + Self::backtrack(0, &mut result, &mut nums); + + result + } +} diff --git a/rust/0048-rotate-image.rs b/rust/0048-rotate-image.rs new file mode 100644 index 000000000..f6fde2662 --- /dev/null +++ b/rust/0048-rotate-image.rs @@ -0,0 +1,14 @@ +impl Solution { + pub fn rotate(matrix: &mut [Vec]) { + matrix.reverse(); + let len = matrix.len(); + for i in 0..len { + for j in i..len { + let x = matrix[i][j]; + let y = matrix[j][i]; + matrix[j][i] = x; + matrix[i][j] = y; + } + } + } +} diff --git a/rust/0049-group-anagrams.rs b/rust/0049-group-anagrams.rs new file mode 100644 index 000000000..526399565 --- /dev/null +++ b/rust/0049-group-anagrams.rs @@ -0,0 +1,23 @@ +use std::collections::HashMap; + +impl Solution { + pub fn group_anagrams(strs: Vec) -> Vec> { + let mut map: HashMap<[u8; 26], Vec> = HashMap::new(); + + for s in strs{ + let mut key = [0_u8; 26]; + + for c in s.chars(){ + key[c as usize - 'a' as usize] += 1; + } + + if let Some(vals) = map.get_mut(&key){ + vals.push(s); + }else{ + map.insert(key, vec![s]); + } + } + + map.into_values().collect() + } +} \ No newline at end of file diff --git a/rust/0050-powx-n.rs b/rust/0050-powx-n.rs new file mode 100644 index 000000000..56d34eaa6 --- /dev/null +++ b/rust/0050-powx-n.rs @@ -0,0 +1,26 @@ +impl Solution { + pub fn my_pow(x: f64, n: i32) -> f64 { + fn helper(x: f64, n: i32) -> f64 { + match (x, n) { + (0.0, _) => 0.0, + (_, 0) => 1.0, + _ => { + let res = helper(x * x, n / 2); + if n % 2 == 0 { + res + } else { + x * res + } + } + } + } + + let res = helper(x, n.abs()); + + if n >= 0 { + res + } else { + 1.0 / res + } + } +} diff --git a/rust/0051-n-queens.rs b/rust/0051-n-queens.rs new file mode 100644 index 000000000..4b9af0b9b --- /dev/null +++ b/rust/0051-n-queens.rs @@ -0,0 +1,59 @@ + +impl Solution { + + pub fn solve_n_queens(n: i32) -> Vec> { + + let mut chessboard: Vec> = vec![vec![0;n as usize]; n as usize]; + let mut result: Vec> = Vec::new(); + let directions: Vec<(isize, isize)> = vec!((-1, 1), (1, 1), (1, -1), (-1, -1)); + + fn toggle_queen(row: usize, col: usize, chessboard: &mut Vec>, directions: &Vec<(isize, isize)>, toggle: i8) { + + for i in 0..chessboard.len() { chessboard[row][i] += toggle }; + for j in 0..chessboard.len() { chessboard[j][col] += toggle }; + + for dir in directions { + let mut i = row as isize + dir.0; + let mut j = col as isize + dir.1; + while i >= 0 && i < chessboard.len() as isize && j >= 0 && j < chessboard.len() as isize { + chessboard[i as usize][j as usize] += toggle; + i += dir.0; + j += dir.1; + } + } + chessboard[row][col] -= 3 * toggle; + } + + fn copy_board(chessboard: &Vec>) -> Vec { + let mut copy: Vec = vec!['.'; chessboard.len()]; + let mut board: Vec = Vec::with_capacity(chessboard.len()); + for i in 0..chessboard.len() { + for j in 0..chessboard.len() { + if chessboard[i][j] == -1 { copy[j] = 'Q' } else { copy[j] = '.' }; + } + board.push(copy.iter().collect()); + } + return board; + } + + fn backtrack( chessboard: &mut Vec>, result: &mut Vec>, row: usize, directions: &Vec<(isize, isize)>) { + if row == chessboard.len() { + result.push(copy_board(&chessboard)); + return; + } + + for col in 0..chessboard.len() { + if chessboard[row][col] == 0 { + toggle_queen(row, col, chessboard, directions, 1); + backtrack(chessboard, result, row + 1, directions); + toggle_queen(row, col, chessboard, directions, -1); + } + } + return; + } + + + backtrack(&mut chessboard, &mut result, 0, &directions); + return result; + } +} \ No newline at end of file diff --git a/rust/0053-maximum-subarray.rs b/rust/0053-maximum-subarray.rs new file mode 100644 index 000000000..90da13171 --- /dev/null +++ b/rust/0053-maximum-subarray.rs @@ -0,0 +1,17 @@ +impl Solution { + pub fn max_sub_array(nums: Vec) -> i32 { + let mut res = nums[0]; + let mut sum = 0; + + for n in nums { + if sum < 0 { + sum = 0; + } + + sum += n; + res = res.max(sum); + } + + res + } +} diff --git a/rust/0055-jump-game.rs b/rust/0055-jump-game.rs new file mode 100644 index 000000000..e40b814be --- /dev/null +++ b/rust/0055-jump-game.rs @@ -0,0 +1,11 @@ +pub fn can_jump(nums: Vec) -> bool { + let mut goal = nums.len() - 1; + + for i in (0..goal).rev() { + if i + nums[i] as usize >= goal { + goal = i; + } + } + + goal == 0 +} diff --git a/rust/0056-merge-intervals.rs b/rust/0056-merge-intervals.rs new file mode 100644 index 000000000..b6d7b2e81 --- /dev/null +++ b/rust/0056-merge-intervals.rs @@ -0,0 +1,15 @@ +impl Solution { + pub fn merge(mut intervals: Vec>) -> Vec> { + intervals.sort_by(|a, b| a[0].cmp(&b[0])); + let acc = vec![intervals.first().unwrap().clone()]; + intervals.into_iter().skip(1).fold(acc, |mut acc, e| { + let last = acc.last().unwrap(); + if e[0] <= last[1] { + acc.last_mut().unwrap()[1] = last[1].max(e[1]); + } else { + acc.push(e) + } + acc + }) + } +} diff --git a/rust/0057-insert-interval.rs b/rust/0057-insert-interval.rs new file mode 100644 index 000000000..ead0a07e1 --- /dev/null +++ b/rust/0057-insert-interval.rs @@ -0,0 +1,21 @@ +pub fn insert(intervals: Vec>, mut new_interval: Vec) -> Vec> { + let mut res: Vec> = Vec::new(); + + for i in 0..intervals.len() { + if new_interval[1] < intervals[i][0] { + res.push(new_interval.clone()); + return [res, intervals[i..].to_vec()].concat().to_vec(); + } else if new_interval[0] > intervals[i][1] { + res.push(intervals[i].clone()); + } else { + new_interval = vec![ + new_interval[0].min(intervals[i][0]), + new_interval[1].max(intervals[i][1]), + ]; + } + } + + res.push(new_interval); + + res +} diff --git a/rust/0058-length-of-last-word.rs b/rust/0058-length-of-last-word.rs new file mode 100644 index 000000000..b4db91292 --- /dev/null +++ b/rust/0058-length-of-last-word.rs @@ -0,0 +1,15 @@ +impl Solution { + pub fn length_of_last_word(s: String) -> i32 { + let s = s.trim_end(); + let s: Vec = s.chars().collect(); + let mut ans = 0; + for i in (0..s.len()).rev() { + if !s[i].is_whitespace() { + ans += 1; + } else { + break; + } + } + ans + } +} diff --git a/rust/0062-unique-paths.rs b/rust/0062-unique-paths.rs new file mode 100644 index 000000000..871346171 --- /dev/null +++ b/rust/0062-unique-paths.rs @@ -0,0 +1,16 @@ +impl Solution { + pub fn unique_paths(m: i32, n: i32) -> i32 { + let mut bottom = vec![1; n as usize]; + + for _ in 0..m - 1 { + let mut top = vec![1; n as usize]; + for c in (0..n - 1).rev().map(|c| c as usize) { + top[c] = bottom[c] + top[c + 1]; + } + + bottom = top; + } + + bottom[0] + } +} diff --git a/rust/0063-unique-paths-ii.rs b/rust/0063-unique-paths-ii.rs new file mode 100644 index 000000000..b6e434c04 --- /dev/null +++ b/rust/0063-unique-paths-ii.rs @@ -0,0 +1,18 @@ +impl Solution { + // Time O(m*n) - Space O(n) + pub fn unique_paths_with_obstacles(obstacle_grid: Vec>) -> i32 { + let n = obstacle_grid[0].len(); + let mut dp = vec![0; n + 1]; + dp[1] = 1; + for row in obstacle_grid { + for i in 1..dp.len() { + if row[i - 1] == 1 { + dp[i] = 0; + } else { + dp[i] += dp[i - 1]; + } + } + } + *dp.last().unwrap() + } +} diff --git a/rust/0066-plus-one.rs b/rust/0066-plus-one.rs new file mode 100644 index 000000000..6a6a68791 --- /dev/null +++ b/rust/0066-plus-one.rs @@ -0,0 +1,23 @@ +impl Solution { + pub fn plus_one(mut digits: Vec) -> Vec { + let mut carry : u8 = 1; + + digits = digits + .into_iter() + .rev() + .map( + |digit| { + let val : u8 = digit as u8 + carry; + carry = val / 10; + (val % 10) as i32 + } + ) + .collect(); + digits.reverse(); + + if carry == 1 { + digits.insert(0, 1); + } + digits + } +} diff --git a/rust/0067-add-binary.rs b/rust/0067-add-binary.rs new file mode 100644 index 000000000..18ab47409 --- /dev/null +++ b/rust/0067-add-binary.rs @@ -0,0 +1,25 @@ +impl Solution { + pub fn add_binary(a: String, b: String) -> String { + let mut res = String::new(); + let mut carry = 0; + + let (mut a, mut b) = (a.as_bytes().to_owned(), b.as_bytes().to_owned()); + a.reverse(); + b.reverse(); + + for i in 0..a.len().max(b.len()) { + let digit_a = if i < a.len() { a[i] - b'0' } else { 0 }; + let digit_b = if i < b.len() { b[i] - b'0' } else { 0 }; + + let total = digit_a + digit_b + carry; + let char = (total % 2).to_string(); + res = char + &res; + carry = total / 2 + } + + if carry != 0 { + res = "1".to_string() + &res; + } + res + } +} \ No newline at end of file diff --git a/rust/0070-climbing-stairs.rs b/rust/0070-climbing-stairs.rs new file mode 100644 index 000000000..aef060df8 --- /dev/null +++ b/rust/0070-climbing-stairs.rs @@ -0,0 +1,9 @@ +impl Solution { + pub fn climb_stairs(n: i32) -> i32 { + std::iter::successors(Some((0, 1)), |dp| Some((dp.1, dp.0 + dp.1))) + .take((n + 1) as usize) + .last() + .unwrap() + .1 + } +} diff --git a/rust/0071-simplify-path.rs b/rust/0071-simplify-path.rs new file mode 100644 index 000000000..cc981ebbb --- /dev/null +++ b/rust/0071-simplify-path.rs @@ -0,0 +1,19 @@ +impl Solution { + pub fn simplify_path(path: String) -> String { + let mut stack = vec![]; + + for i in path.split("/") { + match i { + ".." => { + if !stack.is_empty() { + stack.pop(); + } + } + "." | "" => continue, + _ => stack.push(i), + }; + } + + format!("/{}", stack.join("/")) + } +} \ No newline at end of file diff --git a/rust/0072-edit-distance.rs b/rust/0072-edit-distance.rs new file mode 100644 index 000000000..5be4034c0 --- /dev/null +++ b/rust/0072-edit-distance.rs @@ -0,0 +1,23 @@ +impl Solution { + pub fn min_distance(word1: String, word2: String) -> i32 { + use std::cmp::min; + let (m, n) = (word1.len(), word2.len()); + let w1: Vec = word1.chars().collect(); + let w2: Vec = word2.chars().collect(); + let mut dp: Vec = (0..m + 1).collect(); + let mut temp; + for i in 0..n { + temp = vec![0; m + 1]; + temp[0] = i + 1; + for j in 0..m { + temp[j + 1] = if w1[j] == w2[i] { + dp[j] + } else { + min(temp[j], min(dp[j + 1], dp[j])) + 1 + }; + } + dp = temp; + } + dp[dp.len() - 1] as i32 + } +} diff --git a/rust/0074-search-a-2d-matrix.rs b/rust/0074-search-a-2d-matrix.rs new file mode 100644 index 000000000..ed9157a2e --- /dev/null +++ b/rust/0074-search-a-2d-matrix.rs @@ -0,0 +1,38 @@ +use std::cmp::Ordering::{Equal, Less, Greater}; + +impl Solution { + pub fn search_matrix(matrix: Vec>, target: i32) -> bool { + let (mut t, mut b) = (0, matrix.len()); + let mut row = 0; + while t < b { + row = t + (b - t) / 2; + let first = matrix[row][0]; + let last = *matrix[row].last().unwrap(); + if target == first || target == last { + return true; + } else if target < first { + b = row; + } else if target > last { + t = row + 1; + } else { + break; + } + } + + if t > b { + return false; + } + + let (mut l, mut r) = (0, matrix[row].len()); + while l < r { + let col = l + (r - l) / 2; + match target.cmp(&matrix[row][col]) { + Equal => return true, + Less => r = col, + Greater => l = col + 1 + } + } + + false + } +} diff --git a/rust/0075-sort-colors.rs b/rust/0075-sort-colors.rs new file mode 100644 index 000000000..a6987c46b --- /dev/null +++ b/rust/0075-sort-colors.rs @@ -0,0 +1,30 @@ +impl Solution { + pub fn sort_colors(nums: &mut Vec) { + for i in 1..nums.len() { + let mut j = i; + while j > 0 && nums[j] < nums[j - 1] { + nums.swap(j, j - 1); + j = j - 1; + } + } + } +} + +/* +* Solution using counting sort +*/ +impl Solution { + pub fn sort_colors(nums: &mut Vec) { + let mut count = [0; 3]; + for num in nums.iter() { + count[*num as usize] += 1; + } + let mut i = 0; + for (num, c) in count.iter().enumerate() { + for _ in 0..*c { + nums[i] = num as i32; + i += 1; + } + } + } +} diff --git a/rust/0076-minimum-window-substring.rs b/rust/0076-minimum-window-substring.rs new file mode 100644 index 000000000..ad347c31f --- /dev/null +++ b/rust/0076-minimum-window-substring.rs @@ -0,0 +1,50 @@ +use std::collections::HashMap; + +impl Solution { + pub fn min_window(s: String, t: String) -> String { + let s: Vec = s.chars().collect(); + + if t == String::new() || s.len() < t.len() { + return String::new(); + } + + let (mut l, mut res, mut res_len) = (0, (-1 as i32, -1 as i32), usize::MAX); + let mut count_t: HashMap = HashMap::new(); + let mut window: HashMap = HashMap::new(); + + for c in t.chars() { + *count_t.entry(c).or_default() += 1; + } + + let (mut have, need) = (0, count_t.len()); + + for r in 0..s.len() { + let c = s[r]; + + *window.entry(c).or_default() += 1; + have += (window.get(&c) == count_t.get(&c)) as usize; + + while have == need { + if (r - l + 1) < res_len { + res = (l as i32, r as i32); + } + res_len = res_len.min(r - l + 1); + *window.get_mut(&s[l]).unwrap() -= 1; + + if window.get(&s[l]) < count_t.get(&s[l]) { + have -= 1; + } + + l += 1; + } + } + + if res.0 > -1 && res.1 > -1 { + return s[res.0 as usize..=res.1 as usize] + .into_iter() + .collect::(); + } + + String::new() + } +} diff --git a/rust/0078-subsets.rs b/rust/0078-subsets.rs new file mode 100644 index 000000000..1aeee1066 --- /dev/null +++ b/rust/0078-subsets.rs @@ -0,0 +1,14 @@ +use std::iter::FromIterator; + +impl Solution { + pub fn subsets(nums: Vec) -> Vec> { + let n = nums.len(); + + Vec::from_iter((0..1 << n).map(|bitmask| { + Vec::from_iter((0..n).filter_map(|i| match (bitmask >> i) & 1 != 0 { + true => Some(nums[i]), + false => None + })) + })) + } +} diff --git a/rust/0079-word-search.rs b/rust/0079-word-search.rs new file mode 100644 index 000000000..1440a62f7 --- /dev/null +++ b/rust/0079-word-search.rs @@ -0,0 +1,92 @@ +#[inline(always)] +pub fn encode(c : char) -> u8 { + c as u8 - b'A' + 1 +} + +#[inline] +pub fn encode_word(mut word : String, counter : &mut [i8]) -> u128 { + let mut res : u128 = 0; + let end_char = encode(word.chars().next_back().unwrap()) as usize; + let start_char = encode(word.chars().next().unwrap()) as usize; + + if counter[end_char] > counter[start_char] { + while let Some(c) = word.pop() { + res <<= 6; + let byte = encode(c); + counter[byte as usize] -= 1; + res |= byte as u128; + } + } else { + for c in word.chars() { + res <<= 6; + let byte = encode(c); + counter[byte as usize] -= 1; + res |= byte as u128; + } + } + + res +} + +impl Solution { + pub fn exist(board: Vec>, word: String) -> bool { + if board.len() * board[0].len() < word.len() { + return false; + } + + let mut counter : [i8; 60] = [0; 60]; + let board : Vec> = board + .into_iter() + .map(|row| + row.into_iter().map(|ele| { + let mut res = encode(ele); + counter[res as usize] += 1; + res + }) + .collect() + ).collect(); + + let word = encode_word(word, &mut counter); + + if counter.into_iter().any(|&count| count < 0) { + return false; + } + + let mut visited : Vec> = vec![vec![false; board[0].len()]; board.len()]; + + for i in 0..board.len() { + for j in 0..board[0].len() { + if Self::backtrack(word, &board, i, j, &mut visited) { + return true; + } + } + } + + false + } + + #[inline(always)] + pub fn backtrack( + mut word : u128, + board : &[Vec], + i : usize, j : usize, + visited : &mut [Vec] + ) -> bool { + if i >= board.len() || j >= board[i].len() || word as u8 & 0b111111 != board[i][j] || visited[i][j] { + return false; + } + + visited[i][j] = true; + + ({ word >>= 6; word == 0 } ) + ||(Self::backtrack(word, board, i + 1, j, visited) ) + ||(Self::backtrack(word, board, i, j + 1, visited) ) + ||(Self::backtrack(word, board, i - 1, j, visited) ) + ||(Self::backtrack(word, board, i, j - 1, visited) ) + ||({ + visited[i][j] = false; + false + }) + + } +} diff --git a/rust/0080-remove-duplicates-from-sorted-array-ii.rs b/rust/0080-remove-duplicates-from-sorted-array-ii.rs new file mode 100644 index 000000000..f81297590 --- /dev/null +++ b/rust/0080-remove-duplicates-from-sorted-array-ii.rs @@ -0,0 +1,21 @@ +impl Solution { + // Time O(n) - Space O(1) + pub fn remove_duplicates(nums: &mut Vec) -> i32 { + // The maximum number of duplicates allowed. + const MAX_DUPLICATES: usize = 2; + // Nothing to do. + if nums.len() <= MAX_DUPLICATES { + return nums.len() as i32; + } + // A write pointer. + let mut w = MAX_DUPLICATES; + for r in MAX_DUPLICATES..nums.len() { + // Check if the value x positions back is the same. + if nums[r] != nums[w - MAX_DUPLICATES] { + nums[w] = nums[r]; + w += 1; + } + } + w as i32 + } +} diff --git a/rust/0084-largest-rectangle-in-histogram.rs b/rust/0084-largest-rectangle-in-histogram.rs new file mode 100644 index 000000000..c0ad5e3c3 --- /dev/null +++ b/rust/0084-largest-rectangle-in-histogram.rs @@ -0,0 +1,20 @@ +impl Solution { + pub fn largest_rectangle_area(mut heights: Vec) -> i32 { + let mut res: i32 = 0; + let mut stack: Vec = Vec::new(); + heights.push(0); + heights.insert(0, 0); + + for (i, h) in heights.iter().enumerate() { + while stack.len() > 0 && heights[*stack.iter().last().unwrap()] > *h { + let j = stack.pop().unwrap(); + let width = (i - stack[stack.len() - 1] - 1) as i32; + let size = width * heights[j]; + res = res.max(size); + } + stack.push(i); + } + + return res; + } +} diff --git a/rust/0088-merge-sorted-array.rs b/rust/0088-merge-sorted-array.rs new file mode 100644 index 000000000..ca186a75c --- /dev/null +++ b/rust/0088-merge-sorted-array.rs @@ -0,0 +1,26 @@ +impl Solution { + pub fn merge(nums1: &mut Vec, m: i32, nums2: &mut Vec, n: i32) { + let (mut m, mut n) = (m as usize, n as usize); + // Last index nums1 + let mut last = m + n - 1; + + // Merge in reverse order + while m > 0 && n > 0 { + if nums1[m - 1] > nums2[n as usize - 1] { + nums1[last] = nums1[m - 1]; + m -= 1; + } else { + nums1[last] = nums2[n - 1]; + n -= 1; + } + last -= 1 + } + + // Fill nums1 with leftover nums2 elements + while n > 0 { + nums1[last] = nums2[n - 1]; + n -= 1; + last -= 1; + } + } +} diff --git a/rust/0090-subsets-ii.rs b/rust/0090-subsets-ii.rs new file mode 100644 index 000000000..359cf653c --- /dev/null +++ b/rust/0090-subsets-ii.rs @@ -0,0 +1,25 @@ +impl Solution { + fn backtrack(mut i: usize, result: &mut Vec>, nums: &Vec, subset: &mut Vec) { + if i == nums.len() { + result.push(subset.to_owned()); + return; + } + + subset.push(nums[i]); + Solution::backtrack(i + 1, result, nums, subset); + subset.pop(); + + while i + 1 < nums.len() && nums[i] == nums[i + 1] { + i += 1; + } + Solution::backtrack(i + 1, result, nums, subset); + } + + pub fn subsets_with_dup(nums: Vec) -> Vec> { + let (mut nums, mut result) = (nums, vec![]); + nums.sort(); + + Solution::backtrack(0_usize, &mut result, &mut nums, &mut vec![]); + result + } +} diff --git a/rust/0091-decode-ways.rs b/rust/0091-decode-ways.rs new file mode 100644 index 000000000..7d7afede9 --- /dev/null +++ b/rust/0091-decode-ways.rs @@ -0,0 +1,31 @@ +impl Solution { + pub fn num_decodings_top_down(s: &str, index: usize, memo: &mut Vec>) -> i32 { + if index == s.len() { + return 1; + } + + if s.chars().nth(index).unwrap() == '0' { + return 0; + } + + if let Some(result) = memo[index] { + return result; + } + + let mut ways = Solution::num_decodings_top_down(s, index + 1, memo); + + if index + 1 < s.len() && (s.chars().nth(index).unwrap() == '1' || + (s.chars().nth(index).unwrap() == '2' && + s.chars().nth(index + 1).unwrap() <= '6')) { + ways += Solution::num_decodings_top_down(s, index + 2, memo); + } + + memo[index] = Some(ways); + ways + } + + pub fn num_decodings(s: String) -> i32 { + let mut memo = vec![None; s.len()]; + Solution::num_decodings_top_down(&s, 0, &mut memo) + } +} diff --git a/rust/0093-restore-ip-addresses.rs b/rust/0093-restore-ip-addresses.rs new file mode 100644 index 000000000..c97548082 --- /dev/null +++ b/rust/0093-restore-ip-addresses.rs @@ -0,0 +1,36 @@ +impl Solution { + pub fn restore_ip_addresses(s: String) -> Vec { + let mut res = vec![]; + + if s.len() > 12 { + return res; + } + + Self::backtrack(&mut res, &s, 0, 0, "".to_string()); + + res + } + + pub fn backtrack(res: &mut Vec, s: &String, i: usize, dots: i32, current_ip: String) { + if dots == 4 && i == s.len() { + let new_valid_ip = current_ip.get(..current_ip.len() - 1).unwrap().to_string(); + res.push(new_valid_ip); + return; + } else if dots > 4 { + return; + } + + for j in i..usize::min(i + 3, s.len()) { + let mut val = 0; + + if let Some(v) = s.get(i..j + 1) { + val = v.parse::().unwrap(); + } + + if val < 256 && (i == j || s.get(i..i + 1).unwrap() != "0") { + let new_ip = format!("{}{}.", current_ip, val); + Self::backtrack(res, s, j + 1, dots + 1, new_ip); + } + } + } +} \ No newline at end of file diff --git a/rust/0094-binary-tree-inorder-traversal.rs b/rust/0094-binary-tree-inorder-traversal.rs new file mode 100644 index 000000000..16b8555e9 --- /dev/null +++ b/rust/0094-binary-tree-inorder-traversal.rs @@ -0,0 +1,20 @@ +use std::cell::RefCell; +use std::rc::Rc; +impl Solution { + pub fn inorder_traversal(root: Option>>) -> Vec { + fn inorder(node: &Option>>, result: &mut Vec) { + if let Some(v) = node { + let v = v.borrow(); + inorder(&v.left, result); + result.push(v.val); + inorder(&v.right, result); + } + } + + let mut result = vec![]; + if let Some(v) = root { + inorder(&Some(v), &mut result); + } + result + } +} \ No newline at end of file diff --git a/rust/0098-validate-binary-search-tree.rs b/rust/0098-validate-binary-search-tree.rs new file mode 100644 index 000000000..e94cb2b5e --- /dev/null +++ b/rust/0098-validate-binary-search-tree.rs @@ -0,0 +1,19 @@ +use std::cell::RefCell; +use std::rc::Rc; + +impl Solution { + pub fn is_valid_bst(root: Option>>) -> bool { + Self::inorder(root).windows(2).all(|window| window[0] < window[1]) + } + + fn inorder(node : Option>>) -> Vec { + if let Some(node) = node { + let mut res = Self::inorder(node.borrow_mut().left.take()); + res.push(node.borrow().val); + res.extend(&Self::inorder(node.borrow_mut().right.take())); + res + } else { + Vec::with_capacity(10_000) + } + } +} diff --git a/rust/0100-same-tree.rs b/rust/0100-same-tree.rs new file mode 100644 index 000000000..f0c34e075 --- /dev/null +++ b/rust/0100-same-tree.rs @@ -0,0 +1,20 @@ +use std::cell::RefCell; +use std::rc::Rc; +impl Solution { + pub fn is_same_tree( + p: Option>>, + q: Option>>, + ) -> bool { + match (p, q) { + (None, None) => true, + (Some(p), Some(q)) => { + let p = p.borrow(); + let q = q.borrow(); + p.val == q.val + && Self::is_same_tree(p.left.clone(), q.left.clone()) + && Self::is_same_tree(p.right.clone(), q.right.clone()) + } + _ => false, + } + } +} \ No newline at end of file diff --git a/rust/0101-symmetric-tree.rs b/rust/0101-symmetric-tree.rs new file mode 100644 index 000000000..9bfdae0ae --- /dev/null +++ b/rust/0101-symmetric-tree.rs @@ -0,0 +1,24 @@ +use std::cell::RefCell; +use std::rc::Rc; +impl Solution { + // Time O(n) - Space O(h) + pub fn is_symmetric(root: Option>>) -> bool { + // An internal function that checks two nodes situated in a + // symmetrical position in the tree. + fn dfs(left: Option>>, right: Option>>) -> bool { + match (left, right) { + (None, None) => true, + (None, Some(_)) | (Some(_), None) => false, + (Some(left), Some(right)) => { + left.borrow().val == right.borrow().val + && dfs(left.borrow().left.clone(), right.borrow().right.clone()) + && dfs(left.borrow().right.clone(), right.borrow().left.clone()) + } + } + } + match root { + Some(root) => dfs(root.borrow().left.clone(), root.borrow().right.clone()), + None => true, + } + } +} diff --git a/rust/0102-binary-tree-level-order-traversal.rs b/rust/0102-binary-tree-level-order-traversal.rs new file mode 100644 index 000000000..9f0cc975b --- /dev/null +++ b/rust/0102-binary-tree-level-order-traversal.rs @@ -0,0 +1,34 @@ +use std::rc::Rc; +use std::cell::RefCell; +impl Solution { + pub fn level_order(root: Option>>) -> Vec> { + if let Some(root) = root { + let mut frontier : Vec<(Rc>, u16)> = vec![(root, 0)]; + let mut res : Vec> = vec![]; + let mut len : u16 = 0; + + while let Some((node, depth)) = frontier.pop() { + let val = node.borrow().val; + + if depth == len { + res.push(vec![val]); + len += 1; + } else { + res[depth as usize].push(val); + } + + if let Some(right) = node.borrow_mut().right.take() { + frontier.push((right, depth + 1)); + } + + if let Some(left) = node.borrow_mut().left.take() { + frontier.push((left, depth + 1)); + } + } + + res + } else { + vec![] + } + } +} diff --git a/rust/0104-maximum-depth-of-binary-tree.rs b/rust/0104-maximum-depth-of-binary-tree.rs new file mode 100644 index 000000000..38f0ec19c --- /dev/null +++ b/rust/0104-maximum-depth-of-binary-tree.rs @@ -0,0 +1,26 @@ +use std::rc::Rc; +use std::cell::RefCell; +impl Solution { + pub fn max_depth(root: Option>>) -> i32 { + if let Some(root) = root { + let mut frontier : Vec<(i32, Rc>)> = vec![(1, root)]; + let mut max_depth : i32 = 1; + + while let Some((depth, curr)) = frontier.pop() { + max_depth = depth.max(max_depth); + + if let Some(left) = curr.borrow_mut().left.take() { + frontier.push((depth + 1, left)); + } + + if let Some(right) = curr.borrow_mut().right.take() { + frontier.push((depth + 1, right)); + } + } + + max_depth + } else { + 0 + } + } +} diff --git a/rust/0110-balanced-binary-tree.rs b/rust/0110-balanced-binary-tree.rs new file mode 100644 index 000000000..a51fd751e --- /dev/null +++ b/rust/0110-balanced-binary-tree.rs @@ -0,0 +1,19 @@ +use std::rc::Rc; +use std::cell::RefCell; +impl Solution { + pub fn is_balanced(root: Option>>) -> bool { + fn dfs(root: Option>>) -> (bool, i32) { + match root { + None => (true, 0), + Some(node) => { + let (l_balanced, l_max) = dfs(node.borrow().left.clone()); + let (r_balanced, r_max) = dfs(node.borrow().right.clone()); + let balanced = l_balanced && r_balanced && (l_max - r_max).abs() <= 1; + (balanced, 1 + l_max.max(r_max)) + } + } + } + + dfs(root).0 + } +} diff --git a/rust/0118-pascals-triangle.rs b/rust/0118-pascals-triangle.rs new file mode 100644 index 000000000..8e7fbb3b6 --- /dev/null +++ b/rust/0118-pascals-triangle.rs @@ -0,0 +1,15 @@ +impl Solution { + pub fn generate(num_rows: i32) -> Vec> { + let mut ans: Vec> = Vec::new(); + for line in 1..=num_rows { + let mut c = 1; + let mut v: Vec = Vec::new(); + for i in 1..=line { + v.push(c); + c = c * (line - i) / i; + } + ans.push(v.clone()); + } + ans + } +} diff --git a/rust/0121-best-time-to-buy-and-sell-stock.rs b/rust/0121-best-time-to-buy-and-sell-stock.rs new file mode 100644 index 000000000..b764fcfb5 --- /dev/null +++ b/rust/0121-best-time-to-buy-and-sell-stock.rs @@ -0,0 +1,21 @@ +use std::cmp; + +impl Solution { + pub fn max_profit(prices: Vec) -> i32 { + let mut l = 0; + let mut r = 1; + let mut max_profit = 0; + + while r < prices.len() { + if prices[l] < prices[r] { + let profit = prices[r] - prices[l]; + max_profit = cmp::max(profit, max_profit); + } else { + l = r; + } + r += 1; + } + + max_profit + } +} diff --git a/rust/0122-best-time-to-buy-and-sell-stock-ii.rs b/rust/0122-best-time-to-buy-and-sell-stock-ii.rs new file mode 100644 index 000000000..5e6fd972a --- /dev/null +++ b/rust/0122-best-time-to-buy-and-sell-stock-ii.rs @@ -0,0 +1,12 @@ +impl Solution { + pub fn max_profit(prices: Vec) -> i32 { + let mut buy = i32::MAX; + let mut sell = 0; + for p in prices { + buy = std::cmp::min(buy, p - sell); + sell = std::cmp::max(sell, p - buy); + } + + return sell + } + } diff --git a/rust/0124-binary-tree-maximum-path-sum.rs b/rust/0124-binary-tree-maximum-path-sum.rs new file mode 100644 index 000000000..43d741f0e --- /dev/null +++ b/rust/0124-binary-tree-maximum-path-sum.rs @@ -0,0 +1,23 @@ +use std::rc::Rc; +use std::cell::RefCell; +impl Solution { + pub fn max_path_sum(root: Option>>) -> i32 { + fn dfs(node: Option>>) -> (i32, i32) { + match node { + None => (-1001, -1001), + Some(node) => { + let (l_max, l_sum) = dfs(node.borrow().left.clone()); + let (r_max, r_sum) = dfs(node.borrow().right.clone()); + let val = node.borrow().val; + + ( + l_max.max(r_max).max(val + l_sum.max(0) + r_sum.max(0)), + val.max(val + l_sum).max(val + r_sum) + ) + } + } + } + + dfs(root).0 + } +} diff --git a/rust/0125-valid-palindrome.rs b/rust/0125-valid-palindrome.rs new file mode 100644 index 000000000..1f2cd153c --- /dev/null +++ b/rust/0125-valid-palindrome.rs @@ -0,0 +1,18 @@ +impl Solution { + pub fn is_palindrome(s: String) -> bool { + let s: Vec = s.chars() + .filter(|c| c.is_alphanumeric()) + .map(|c| c.to_lowercase().next().unwrap()) + .collect(); + + let len = s.len(); + + for i in 0..(len/2){ + if s[i] != s[len - i - 1]{ + return false + } + } + + true + } +} \ No newline at end of file diff --git a/rust/0127-word-ladder.rs b/rust/0127-word-ladder.rs new file mode 100644 index 000000000..2ff464b23 --- /dev/null +++ b/rust/0127-word-ladder.rs @@ -0,0 +1,47 @@ +pub fn encode(word : String) -> u64 { + word.into_bytes().into_iter().fold(0, |mut acc, c|{ + acc <<= 5; + acc | (c - b'a' + 1) as u64 + }) +} + +use std::collections::HashSet; +use std::collections::VecDeque; + +impl Solution { + pub fn ladder_length(begin_word: String, end_word: String, word_list: Vec) -> i32 { + let word_len = begin_word.len(); + let begin_word = encode(begin_word); + let end_word = encode(end_word); + + let mut word_list : HashSet = word_list.into_iter().map(|word| encode(word)).collect(); + let mut frontier : VecDeque = VecDeque::with_capacity(5000); + let mut seen : HashSet = HashSet::new(); + let mut res = 1; + + frontier.push_back(begin_word); + while !frontier.is_empty() { + let len = frontier.len(); + for _ in 0..len { + let curr = frontier.pop_front().unwrap(); + if curr == end_word { + return res; + } + + for i in 0..word_len { + let filter = !(0b11111 << (i * 5)); + for character in 1..=26 { + let neighbour = ((curr & filter) | (character << (i * 5))); + if word_list.contains(&neighbour) && !seen.contains(&neighbour) { + frontier.push_back(neighbour); + seen.insert(neighbour); + } + } + } + + } + res += 1; + } + 0 + } +} diff --git a/rust/0128-longest-consecutive-sequence.rs b/rust/0128-longest-consecutive-sequence.rs new file mode 100644 index 000000000..a203777c4 --- /dev/null +++ b/rust/0128-longest-consecutive-sequence.rs @@ -0,0 +1,27 @@ +use std::{ + collections::HashSet, + iter::FromIterator, +}; + +impl Solution { + pub fn longest_consecutive(nums: Vec) -> i32 { + let mut set : HashSet = HashSet::from_iter(nums.into_iter()); + + let mut max_cnt = 0; + + for n in &set{ + if !set.contains(&(n-1)){ + let mut next = n + 1; + let mut cnt = 1; + while set.contains(&next){ + cnt +=1; + next+=1; + } + + max_cnt = max_cnt.max(cnt); + } + } + + max_cnt + } +} diff --git a/rust/0131-palindrome-partitioning.rs b/rust/0131-palindrome-partitioning.rs new file mode 100644 index 000000000..67268a0eb --- /dev/null +++ b/rust/0131-palindrome-partitioning.rs @@ -0,0 +1,37 @@ +impl Solution { + pub fn partition(s: String) -> Vec> { + let mut res: Vec> = Vec::new(); + let mut part: Vec = Vec::new(); + + Self::dfs(&s, &mut res, &mut part, 0); + res + } + + pub fn dfs(s: &String, res: &mut Vec>, part: &mut Vec, i: usize) { + if i >= s.len() { + res.push(part.clone()); + return; + } + + for j in i..s.len() { + if Self::is_palindrome(s, i, j) { + let substr: String = s[i..j + 1].to_string(); + part.push(substr); + Self::dfs(s, res, part, j + 1); + part.pop(); + } + } + } + + pub fn is_palindrome(s: &String, mut left: usize, mut right: usize) -> bool { + let s = s.as_bytes(); + while left < right { + if s[left] != s[right] { + return false; + } + left += 1; + right -= 1; + } + return true; + } +} \ No newline at end of file diff --git a/rust/0136-single-number.rs b/rust/0136-single-number.rs new file mode 100644 index 000000000..3c02407cd --- /dev/null +++ b/rust/0136-single-number.rs @@ -0,0 +1,9 @@ +impl Solution { + pub fn single_number(nums: Vec) -> i32 { + let mut res = 0; + for n in nums { + res ^= n; + } + res + } +} diff --git a/rust/0143-reorder-list.rs b/rust/0143-reorder-list.rs new file mode 100644 index 000000000..49080c0b0 --- /dev/null +++ b/rust/0143-reorder-list.rs @@ -0,0 +1,46 @@ +impl Solution { + pub fn reorder_list(mut head: &mut Option>) { + let mut length = 0; + let mut list = head.as_mut(); + // find the total length of the list + // Using the slow & fast pointer is probably not possible + while let Some(node) = list { + length += 1; + list = node.next.as_mut(); + } + // if length is less than or equal to 2 then exit the function + if length <= 2 { + return; + } + // find the middle node to split the list + let mut mid = head.as_mut(); + for _ in 0..length / 2 { + mid = match mid { + None => unreachable!("Traversing half of the list. Cannot reach the end."), + Some(node) => node.next.as_mut(), + } + } + // Tail is the right half of the list. + // Here we use take() to break the linked list + // so the end of the first half now points to None + // and tail holds the second half of the linked list + let mut tail = mid.expect("Mid cannot be None.").as_mut().next.take(); + // reverse the tail + let mut reversed: Option> = None; + while let Some(mut node) = tail { + tail = node.next; + node.next = reversed; + reversed = Some(node); + } + // merge the first half(head) & the second reversed half(reversed) + // This solution to merge two linked lists is clever + // https://leetcode.com/problems/merge-two-sorted-lists/solutions/2947855/simple-and-efficient-rust-8-liner/ + // mover is the variable that traverses and modifies "head" + let mut mover: &mut _ = &mut reversed; + while head.is_some() { + std::mem::swap(mover, &mut head); + mover = &mut mover.as_mut().unwrap().next; + } + std::mem::swap(head, &mut reversed); + } +} diff --git a/rust/0149-max-points-on-a-line.rs b/rust/0149-max-points-on-a-line.rs new file mode 100644 index 000000000..f53fc48c8 --- /dev/null +++ b/rust/0149-max-points-on-a-line.rs @@ -0,0 +1,27 @@ +use std::collections::HashMap; + +impl Solution { + pub fn max_points(points: Vec>) -> i32 { + let mut res = 1; + + for i in 0..points.len() { + let point1 = &points[i]; + let mut count = HashMap::new(); + for j in (i + 1)..points.len() { + let point2 = &points[j]; + let slope; + if point2[0] == point1[0] { + slope = i32::MAX; + } else { + slope = ((point2[1] as f64 - point1[1] as f64) + / (point2[0] as f64 - point1[0] as f64) + * 100000.0) as i32; + } + + *count.entry(slope).or_insert(1) += 1; + res = res.max(*count.get(&slope).unwrap()); + } + } + res + } +} \ No newline at end of file diff --git a/rust/0150-evaluate-reverse-polish-notation.rs b/rust/0150-evaluate-reverse-polish-notation.rs new file mode 100644 index 000000000..e45276ca9 --- /dev/null +++ b/rust/0150-evaluate-reverse-polish-notation.rs @@ -0,0 +1,33 @@ +impl Solution { + pub fn eval_rpn(tokens: Vec) -> i32 { + let mut stack: Vec = Vec::new(); + + for token in tokens { + match &token[..] { + "+" => { + let second_operand = stack.pop().unwrap(); + let first_operand = stack.pop().unwrap(); + stack.push(first_operand + second_operand) + } + "-" => { + let second_operand = stack.pop().unwrap(); + let first_operand = stack.pop().unwrap(); + stack.push(first_operand - second_operand) + } + "*" => { + let second_operand = stack.pop().unwrap(); + let first_operand = stack.pop().unwrap(); + stack.push(first_operand * second_operand) + } + "/" => { + let second_operand = stack.pop().unwrap(); + let first_operand = stack.pop().unwrap(); + stack.push(first_operand / second_operand) + } + value => stack.push(value.parse::().unwrap()), + } + } + + stack[0] + } +} diff --git a/rust/0152-maximum-product-subarray.rs b/rust/0152-maximum-product-subarray.rs new file mode 100644 index 000000000..89fe0df92 --- /dev/null +++ b/rust/0152-maximum-product-subarray.rs @@ -0,0 +1,12 @@ +impl Solution { + pub fn max_product(nums: Vec) -> i32 { + let (mut res, mut big, mut small) = (*nums.iter().max().unwrap(), 1, 1); + for n in nums { + let tmp = big; + big = vec![n, big * n, small * n].into_iter().max().unwrap(); + small = vec![n, tmp * n, small * n].into_iter().min().unwrap(); + res = res.max(big); + } + res + } +} diff --git a/rust/0153-find-minimum-in-rotated-sorted-array.rs b/rust/0153-find-minimum-in-rotated-sorted-array.rs new file mode 100644 index 000000000..18663a9fa --- /dev/null +++ b/rust/0153-find-minimum-in-rotated-sorted-array.rs @@ -0,0 +1,31 @@ +impl Solution { + pub fn find_min(nums: Vec) -> i32 { + let length = nums.len(); + + match length { + 1 => return nums[0], + _ => (), + } + + let (mut l, mut r) = (0, length - 1); + + while l < r { + let m = (l + r) / 2; + + let left = nums[l]; + let mid = nums[m]; + let right = nums[r]; + + if left <= mid && mid <= right { + return left; + } else if left >= mid && mid >= right { + return right; + } else if left > mid || mid < right { + r = m; + } else { + l = m; + } + } + -1 + } +} diff --git a/rust/0155-min-stack.rs b/rust/0155-min-stack.rs new file mode 100644 index 000000000..bc4170ee2 --- /dev/null +++ b/rust/0155-min-stack.rs @@ -0,0 +1,37 @@ +struct MinStack { + stack: Vec, + min_stack: Vec, +} + +impl MinStack { + fn new() -> Self { + Self { + stack: vec![], + min_stack: vec![], + } + } + + fn push(&mut self, val: i32) { + self.stack.push(val); + if self.min_stack.is_empty() || Some(&val) <= self.min_stack.last() { + self.min_stack.push(val); + } + } + + fn pop(&mut self) { + let val = self.stack.pop().unwrap(); + if Some(&val) == self.min_stack.last() { + self.min_stack.pop(); + } + } + + fn top(&self) -> i32 { + self.stack.last().cloned().unwrap() + } + + fn get_min(&self) -> i32 { + self.min_stack.last().cloned().unwrap() + } +} + + diff --git a/rust/0167-two-sum-ii-input-array-is-sorted.rs b/rust/0167-two-sum-ii-input-array-is-sorted.rs new file mode 100644 index 000000000..712aa9a3c --- /dev/null +++ b/rust/0167-two-sum-ii-input-array-is-sorted.rs @@ -0,0 +1,15 @@ +use std::cmp::Ordering::{Equal, Greater, Less}; + +impl Solution { + pub fn two_sum(numbers: Vec, target: i32) -> Vec { + let (mut l, mut r) = (0, numbers.len() - 1); + while l < r { + match (numbers[l] + numbers[r]).cmp(&target) { + Less => l += 1, + Greater => r -= 1, + Equal => return vec![l as i32 + 1, r as i32 + 1], + } + } + unreachable!("Test did not follow the constraints!") + } +} diff --git a/rust/0169-majority-element.rs b/rust/0169-majority-element.rs new file mode 100644 index 000000000..bb8ac7ecd --- /dev/null +++ b/rust/0169-majority-element.rs @@ -0,0 +1,48 @@ +use std::collections::HashMap; + +impl Solution { + // Time Complexity: O(n) + // Space Complexity: O(1) + // Boyer - Moore Algorithm + pub fn majority_element(nums: Vec) -> i32 { + let (mut res, mut count) = (0, 0); + + for n in nums { + if count == 0 { + res = n; + } + count += if n == res { 1 } else { -1 }; + } + res + } + + // Time Complexity: O(n) + // Space Complexity: O(n) + // Hashmap + pub fn majority_element_2(nums: Vec) -> i32 { + let mut count = HashMap::new(); + + let (mut res, mut max_count) = (0, 0); + + for num in nums { + *count.entry(num).or_insert(0) += 1; + res = if *count.get(&num).unwrap() > max_count { + num + } else { + res + }; + max_count = i32::max(*count.get(&num).unwrap(), max_count); + } + res + } + + // Time Complexity: O(nlogn) + // Space Complexity: O(1) + // Sorting + pub fn majority_element_3(nums: Vec) -> i32 { + // Since we are assured that there will be a majority element which occurs more than nums.len() / 2 times, majority element will be at nums.len() / 2 index + let mut nums = nums; + nums.sort(); + nums[nums.len() / 2] + } +} \ No newline at end of file diff --git a/rust/0179-largest-number.rs b/rust/0179-largest-number.rs new file mode 100644 index 000000000..d25c1f1a5 --- /dev/null +++ b/rust/0179-largest-number.rs @@ -0,0 +1,11 @@ +impl Solution { + pub fn largest_number(nums: Vec) -> String { + let mut v: Vec = nums.iter().map(|&num| num.to_string()).collect(); + v.sort_by(|a: &String, b: &String| (b.clone() + a).cmp(&(a.clone() + b))); + if v[0] == "0" { + String::from("0") + } else { + v.join("") + } + } +} \ No newline at end of file diff --git a/rust/0190-reverse-bits.rs b/rust/0190-reverse-bits.rs new file mode 100644 index 000000000..dc55ba5ba --- /dev/null +++ b/rust/0190-reverse-bits.rs @@ -0,0 +1,9 @@ +impl Solution { + pub fn reverse_bits(mut x: u32) -> u32 { + (0..32).fold(0, |mut res, _| { + res = (res << 1) | (x & 1); + x >>= 1; + res + }) + } +} diff --git a/rust/0191-number-of-1-bits.rs b/rust/0191-number-of-1-bits.rs new file mode 100644 index 000000000..77203939d --- /dev/null +++ b/rust/0191-number-of-1-bits.rs @@ -0,0 +1,12 @@ +impl Solution { + pub fn hammingWeight(mut n: u32) -> i32 { + let mut count = 0; + + while n > 0 { + n = n & (n - 1); + count += 1; + } + + count + } +} diff --git a/rust/0198-house-robber.rs b/rust/0198-house-robber.rs new file mode 100644 index 000000000..291b154b6 --- /dev/null +++ b/rust/0198-house-robber.rs @@ -0,0 +1,14 @@ +impl Solution { + pub fn rob(nums: Vec) -> i32 { + nums.into_iter() + .fold((0, 0), |loot, money| (loot.1, loot.1.max(loot.0 + money))) + .1 + } +} + + + + - + + + diff --git a/rust/0199-binary-tree-right-side-view.rs b/rust/0199-binary-tree-right-side-view.rs new file mode 100644 index 000000000..48fab8c00 --- /dev/null +++ b/rust/0199-binary-tree-right-side-view.rs @@ -0,0 +1,39 @@ +use std::rc::Rc; +use std::cell::RefCell; +use std::collections::VecDeque; + +impl Solution { + pub fn right_side_view(root: Option>>) -> Vec { + + if let Some(root) = root { + let mut frontier : VecDeque<(Rc>, usize)> = VecDeque::from([(root, 0)]); + let mut res = Vec::with_capacity(100); + let mut res_len : usize = 0; + + while let Some((node, depth)) = frontier.pop_front() { + let val = node.borrow().val; + let left = node.borrow_mut().left.take(); + let right = node.borrow_mut().right.take(); + + if res_len == depth { + res.push(val); + res_len += 1; + } else { + res[res_len - 1] = val; + } + + if let Some(left) = left { + frontier.push_back((left, depth + 1)); + } + + if let Some(right) = right { + frontier.push_back((right, depth + 1)); + } + } + + res + } else { + vec![] + } + } +} diff --git a/rust/0200-number-of-islands.rs b/rust/0200-number-of-islands.rs new file mode 100644 index 000000000..52fb3aa2a --- /dev/null +++ b/rust/0200-number-of-islands.rs @@ -0,0 +1,34 @@ +impl Solution { + pub fn num_islands(mut grid: Vec>) -> i32 { + fn dfs(grid: &mut Vec>, x: i32, y: i32) { + if x < 0 + || y < 0 + || x >= grid.len() as i32 + || y >= grid[0].len() as i32 + || grid[x as usize][y as usize] == '0' + { + return; + } + + grid[x as usize][y as usize] = '0'; + + let directions: [(i32, i32); 4] = [(0, 1), (1, 0), (0, -1), (-1, 0)]; + + for (add_x, add_y) in directions { + dfs(grid, x + add_x, y + add_y); + } + } + + let mut count = 0; + for x in 0..grid.len() { + for y in 0..grid[0].len() { + if grid[x][y] == '1' { + count += 1; + dfs(&mut grid, x as i32, y as i32); + } + } + } + + count + } +} diff --git a/rust/0202-happy-number.rs b/rust/0202-happy-number.rs new file mode 100644 index 000000000..0c63e6f23 --- /dev/null +++ b/rust/0202-happy-number.rs @@ -0,0 +1,15 @@ +impl Solution { + pub fn is_happy(mut n: i32) -> bool { + loop { + let mut s = 0; + while n > 0 { + s += (n % 10).pow(2); + n /= 10; + } + match s { + 1 | 4 => break s == 1, + _ => n = s, + } + } + } +} \ No newline at end of file diff --git a/rust/0205-isomorphic-strings.rs b/rust/0205-isomorphic-strings.rs new file mode 100644 index 000000000..6f07885be --- /dev/null +++ b/rust/0205-isomorphic-strings.rs @@ -0,0 +1,23 @@ +use std::collections::HashMap; + +impl Solution { + pub fn is_isomorphic(s: String, t: String) -> bool { + let (mut map_s_to_t, mut map_t_to_s) = (HashMap::new(), HashMap::new()); + + for (c1, c2) in s.chars().zip(t.chars()) { + if let Some(w) = map_s_to_t.insert(c1, c2) { + if w != c2 { + return false; + } + } + + if let Some(w) = map_t_to_s.insert(c2, c1) { + if w != c1 { + return false; + } + } + } + + true + } +} \ No newline at end of file diff --git a/rust/0206-reverse-linked-list.rs b/rust/0206-reverse-linked-list.rs new file mode 100644 index 000000000..f4caa0c83 --- /dev/null +++ b/rust/0206-reverse-linked-list.rs @@ -0,0 +1,11 @@ +impl Solution { + pub fn reverse_list(mut head: Option>) -> Option> { + let (mut prev, mut curr) = (None, head); + while let Some(mut node) = curr{ + curr = node.next; + node.next = prev; + prev = Some(node); + } + pre + } +} diff --git a/rust/0207-course-schedule.rs b/rust/0207-course-schedule.rs new file mode 100644 index 000000000..b76231036 --- /dev/null +++ b/rust/0207-course-schedule.rs @@ -0,0 +1,40 @@ +impl Solution { + pub fn can_finish(num_courses: i32, prerequisites: Vec>) -> bool { + let mut graph = vec![vec![]; num_courses as usize]; + let mut visited = vec![0; num_courses as usize]; + + for edge in prerequisites { + graph[edge[0] as usize].push(edge[1] as usize); + } + + for i in 0..num_courses as usize { + if !Self::dfs(i, &graph, &mut visited) { + return false; + } + } + + true + } + + fn dfs(i: usize, graph: &Vec>, visited: &mut Vec) -> bool { + if visited[i] == 1 { + return false; + } + + if visited[i] == -1 { + return true; + } + + visited[i] = 1; + + for j in graph[i].iter() { + if !Self::dfs(*j, graph, visited) { + return false; + } + } + + visited[i] = -1; + + true + } +} diff --git a/rust/0208-implement-trie-prefix-tree.rs b/rust/0208-implement-trie-prefix-tree.rs new file mode 100644 index 000000000..585439059 --- /dev/null +++ b/rust/0208-implement-trie-prefix-tree.rs @@ -0,0 +1,56 @@ +#[derive(Default)] +struct Trie { + is_word_end: bool, + children: [Option>; 26], +} + +impl Trie { + + fn new() -> Self { + Self{ ..Default::default() } + } + + fn insert(&mut self, word: String) { + let mut trie = self; + + for c in word.chars(){ + trie = trie.children[char_index(c)] + .get_or_insert(Box::new(Trie::new())) + .as_mut(); + } + + trie.is_word_end = true; + } + + fn search(& self, word: String) -> bool { + let mut trie = self; + + for c in word.chars(){ + if let Some(ref next_trie) = trie.children[char_index(c)]{ + trie = next_trie.as_ref(); + }else{ + return false; + } + } + + trie.is_word_end + } + + fn starts_with(&self, prefix: String) -> bool { + let mut trie = self; + + for c in prefix.chars(){ + if let Some(ref next_trie) = trie.children[char_index(c)]{ + trie = next_trie.as_ref(); + }else{ + return false; + } + } + + true + } +} + +pub fn char_index(c: char) -> usize{ + c as usize - 'a' as usize +} \ No newline at end of file diff --git a/rust/0211-design-add-and-search-words-data-structure.rs b/rust/0211-design-add-and-search-words-data-structure.rs new file mode 100644 index 000000000..bc74281b6 --- /dev/null +++ b/rust/0211-design-add-and-search-words-data-structure.rs @@ -0,0 +1,60 @@ +#[derive(Default)] +struct WordDictionary { + is_word_end: bool, + children: [Option>; 26], +} + +impl WordDictionary { + + fn new() -> Self { + Default::default() + } + + fn add_word(&mut self, word: String) { + let mut dict = self; + + for c in word.chars(){ + dict = dict.children[char_index(c)] + .get_or_insert(Box::new(WordDictionary::new())) + .as_mut(); + } + + dict.is_word_end = true; + } + + fn search_ref(&self, word: &str) -> bool{ + let mut dict = self; + + for (i, c) in word.chars().enumerate(){ + if c == '.'{ + let res = dict.children + .iter() + .any(|opt|{ + if let Some(ref next_dict) = opt{ + next_dict.search_ref(&word[i + 1..]) + }else{ + false + } + }); + + return res; + }else{ + if let Some(ref next_dict) = dict.children[char_index(c)]{ + dict = next_dict.as_ref(); + }else{ + return false; + } + } + } + + dict.is_word_end + } + + fn search(&self, word: String) -> bool { + self.search_ref(&word) + } +} + +pub fn char_index(c: char) -> usize{ + c as usize - 'a' as usize +} \ No newline at end of file diff --git a/rust/0212-word-search-ii.rs b/rust/0212-word-search-ii.rs new file mode 100644 index 000000000..723ed18ca --- /dev/null +++ b/rust/0212-word-search-ii.rs @@ -0,0 +1,93 @@ +#[derive(Default, Debug, Clone)] +struct Trie { + is_word_end: bool, + children: [Option>; 26], +} + +impl Solution { + pub fn find_words(mut board: Vec>, words: Vec) -> Vec { + let mut trie = Trie::new(); + let (m, n) = (board.len(), board[0].len()); + + for word in words{ + trie.insert(word); + } + + let mut word = String::new(); + let mut res = vec![]; + + for i in 0..m{ + for j in 0..n{ + Self::dfs(&mut board, &mut trie, &mut res, &mut word, i, j); + } + } + res + } + + pub fn dfs( + board: &mut Vec>, + mut trie: &mut Trie, + res: &mut Vec, + word: &mut String, + i: usize, + j: usize + ){ + let (m, n) = (board.len(), board[0].len()); + + if i!= usize::MAX && + j!= usize::MAX && + i < m && + j < n && + board[i][j] != '#' + { + let c = board[i][j]; + + // mark as visited + board[i][j] = '#'; + + if let Some(ref mut curr_trie) = trie.children[char_index(c)]{ + trie = curr_trie.as_mut(); + + word.push(c); + + if trie.is_word_end{ + res.push(word.clone()); + trie.is_word_end = false; + } + + for (x, y) in [(i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1)]{ + Self::dfs(board, trie, res, word, x, y); + } + + word.pop(); + + } + + board[i][j] = c; + + } + } +} + +impl Trie { + + fn new() -> Self { + Default::default() + } + + fn insert(&mut self, word: String) { + let mut trie = self; + + for c in word.chars(){ + trie = trie.children[char_index(c)] + .get_or_insert(Box::new(Trie::new())) + .as_mut(); + } + + trie.is_word_end = true; + } +} + +pub fn char_index(c: char) -> usize{ + c as usize - 'a' as usize +} \ No newline at end of file diff --git a/rust/0213-house-robber-ii.rs b/rust/0213-house-robber-ii.rs new file mode 100644 index 000000000..68df4208f --- /dev/null +++ b/rust/0213-house-robber-ii.rs @@ -0,0 +1,15 @@ +impl Solution { + pub fn rob(nums: Vec) -> i32 { + let l = nums.len(); + return match l { + 0 => 0, + 1 => nums[0], + _ => rob_house(&nums[1..]).max(rob_house(&nums[0..l - 1])), + }; + fn rob_house(nums: &[i32]) -> i32 { + nums.iter() + .fold((0, 0), |loot, money| (loot.1, loot.1.max(loot.0 + money))) + .1 + } + } +} diff --git a/rust/0215-kth-largest-element-in-an-array.rs b/rust/0215-kth-largest-element-in-an-array.rs new file mode 100644 index 000000000..1b580470c --- /dev/null +++ b/rust/0215-kth-largest-element-in-an-array.rs @@ -0,0 +1,18 @@ +https://leetcode.com/problems/kth-largest-element-in-an-array/submissions/1012381844/ + +use std::collections::BinaryHeap; +impl Solution { + pub fn find_kth_largest(nums: Vec, k: i32) -> i32 { + let mut heap = BinaryHeap::new(); + for &n in nums.iter() { + if heap.len() < k as usize { + heap.push(-n); + continue; + } else if -heap.peek().unwrap() < n { + heap.pop(); + heap.push(-n); + } + } + -heap.pop().unwrap() + } +} diff --git a/rust/0217-contains-duplicate.rs b/rust/0217-contains-duplicate.rs new file mode 100644 index 000000000..985d8793d --- /dev/null +++ b/rust/0217-contains-duplicate.rs @@ -0,0 +1,18 @@ +use std::collections::HashSet; + +impl Solution { + pub fn contains_duplicate(nums: Vec) -> bool { + let mut map = HashSet::new(); + + for &n in nums.iter(){ + + if map.contains(&n){ + return true; + } + + map.insert(n); + }; + + false + } +} diff --git a/rust/0219-contains-duplicate-ii.rs b/rust/0219-contains-duplicate-ii.rs new file mode 100644 index 000000000..2a6e4fef1 --- /dev/null +++ b/rust/0219-contains-duplicate-ii.rs @@ -0,0 +1,19 @@ +use std::collections::HashSet; +impl Solution { + fn contains_nearby_duplicate(nums: Vec, k: i32) -> bool { + let mut window = HashSet::new(); + let mut l = 0; + + for (r, &num) in nums.iter().enumerate() { + if r as i32 - l as i32 > k { + window.remove(&nums[l]); + l += 1; + } + if window.contains(&num) { + return true; + } + window.insert(num); + } + false + } +} \ No newline at end of file diff --git a/rust/0225-implement-stack-using-queues.rs b/rust/0225-implement-stack-using-queues.rs new file mode 100644 index 000000000..a517c211f --- /dev/null +++ b/rust/0225-implement-stack-using-queues.rs @@ -0,0 +1,31 @@ +use std::collections::VecDeque; + +struct MyStack { + q: VecDeque, +} + +impl MyStack { + fn new() -> Self { + Self { q: VecDeque::new() } + } + + fn push(&mut self, x: i32) { + self.q.push_back(x); + } + + fn pop(&mut self) -> i32 { + for i in 0..self.q.len() - 1 { + let temp = self.q.pop_front().unwrap(); + self.q.push_back(temp); + } + self.q.pop_front().unwrap() + } + + fn top(&self) -> i32 { + self.q[self.q.len() - 1] + } + + fn empty(&self) -> bool { + self.q.is_empty() + } +} diff --git a/rust/0226-invert-binary-tree.rs b/rust/0226-invert-binary-tree.rs new file mode 100644 index 000000000..b955e031a --- /dev/null +++ b/rust/0226-invert-binary-tree.rs @@ -0,0 +1,16 @@ +use std::rc::Rc; +use std::cell::RefCell; +impl Solution { + pub fn invert_tree(root: Option>>) -> Option>> { + root.map(|node| { + { + let mut node_ref = node.borrow_mut(); + let left = node_ref.left.take(); + let right = node_ref.right.take(); + node_ref.right = Solution::invert_tree(left); + node_ref.left = Solution::invert_tree(right); + } + node + }) + } +} diff --git a/rust/0230-kth-smallest-element-in-a-bst.rs b/rust/0230-kth-smallest-element-in-a-bst.rs new file mode 100644 index 000000000..359fd27ed --- /dev/null +++ b/rust/0230-kth-smallest-element-in-a-bst.rs @@ -0,0 +1,18 @@ +use std::rc::Rc; +use std::cell::RefCell; +impl Solution { + pub fn kth_smallest(root: Option>>, k: i32) -> i32 { + fn into_vec(root: Option>>) -> Vec { + match root { + None => vec![], + Some(node) => into_vec(node.borrow().left.clone()) + .into_iter() + .chain(vec![node.borrow().val]) + .chain(into_vec(node.borrow().right.clone())) + .collect(), + } + } + + into_vec(root)[k as usize - 1] + } +} diff --git a/rust/0235-lowest-common-ancestor-of-a-binary-search-tree.rs b/rust/0235-lowest-common-ancestor-of-a-binary-search-tree.rs new file mode 100644 index 000000000..f2e9881a9 --- /dev/null +++ b/rust/0235-lowest-common-ancestor-of-a-binary-search-tree.rs @@ -0,0 +1,22 @@ +use std::rc::Rc; +use std::cell::RefCell; +impl Solution { + pub fn lowest_common_ancestor( + root: Option>>, + p: Option>>, + q: Option>>, + ) -> Option>> { + let (p, q) = (p.unwrap().borrow().val, q.unwrap().borrow().val); + let mut root = root; + while let Some(node) = root { + if p > node.borrow().val && q > node.borrow().val { + root = node.borrow().right.clone(); + } else if p < node.borrow().val && q < node.borrow().val { + root = node.borrow().left.clone(); + } else { + return Some(node.clone()); + } + } + unreachable!() + } +} diff --git a/rust/0238-product-of-array-except-self.rs b/rust/0238-product-of-array-except-self.rs new file mode 100644 index 000000000..0cbc89b34 --- /dev/null +++ b/rust/0238-product-of-array-except-self.rs @@ -0,0 +1,18 @@ +impl Solution { + pub fn product_except_self(mut nums: Vec) -> Vec { + let mut res = vec![1; nums.len()]; + + for i in (1..nums.len()){ + res[i] = nums[i - 1] * res[i - 1]; + } + + let mut right = 1; + + for (i, n) in res.iter_mut().enumerate().rev(){ + *n = *n * right; + right *= nums[i]; + } + + res + } +} \ No newline at end of file diff --git a/rust/0239-sliding-window-maximum.rs b/rust/0239-sliding-window-maximum.rs new file mode 100644 index 000000000..b41525c77 --- /dev/null +++ b/rust/0239-sliding-window-maximum.rs @@ -0,0 +1,31 @@ +use std::collections::VecDeque; + +impl Solution { + pub fn max_sliding_window(nums: Vec, k: i32) -> Vec { + let mut output = vec![]; + let mut q: VecDeque = VecDeque::new(); + + let (mut l, mut r) = (0, 0); + + while r < nums.len() { + while !q.is_empty() && nums[r] > nums[*q.back().unwrap()] { + q.pop_back(); + } + + q.push_back(r); + + if l > *q.front().unwrap() { + q.pop_front(); + } + + if r + 1 >= k as usize { + output.push(nums[*q.front().unwrap()]); + l += 1; + } + + r += 1; + } + + output + } +} diff --git a/rust/0242-valid-anagram.rs b/rust/0242-valid-anagram.rs new file mode 100644 index 000000000..6c5b5e0c4 --- /dev/null +++ b/rust/0242-valid-anagram.rs @@ -0,0 +1,18 @@ +use std::collections::HashMap; + +impl Solution { + pub fn is_anagram(s: String, t: String) -> bool { + if (t.len() != s.len()){ + return false; + } + + let mut map: HashMap = HashMap::new(); + + for (a, b) in s.chars().zip(t.chars()){ + *map.entry(a).or_default() += 1; + *map.entry(b).or_default() -= 1; + } + + map.into_values().all(|cnt| cnt == 0) + } +} diff --git a/rust/0253-meeting-rooms-ii.rs b/rust/0253-meeting-rooms-ii.rs new file mode 100644 index 000000000..9cfc1e6ff --- /dev/null +++ b/rust/0253-meeting-rooms-ii.rs @@ -0,0 +1,13 @@ +// Interval is a struct with fields `start:i32` & `end:i32` +pub fn can_attend_meetings(intervals: Vec) -> bool { + let mut intervals = intervals; + intervals.sort_unstable_by(|a, b| a.start.cmp(&b.start)); + + for i in 0..intervals.len() - 1 { + if intervals[i].end > intervals[i + 1].start { + return false; + } + } + + true +} diff --git a/rust/0263-ugly-number.rs b/rust/0263-ugly-number.rs new file mode 100644 index 000000000..c7300087d --- /dev/null +++ b/rust/0263-ugly-number.rs @@ -0,0 +1,17 @@ +impl Solution { + pub fn is_ugly(n: i32) -> bool { + if n < 1 { + return false; + } + let mut n = n; + let ugly_primes = vec![2, 3, 5]; + + for prime in ugly_primes { + while n % prime == 0 { + n /= prime; + } + } + + n == 1 + } +} \ No newline at end of file diff --git a/rust/0268-missing-number.rs b/rust/0268-missing-number.rs new file mode 100644 index 000000000..36d737270 --- /dev/null +++ b/rust/0268-missing-number.rs @@ -0,0 +1,10 @@ +impl Solution { + pub fn missing_number(nums: Vec) -> i32 { + let length = nums.len() as i32; + let mut ans = length; + for i in 0..length { + ans ^= i ^ nums[i as usize]; + } + ans + } +} diff --git a/rust/0271-encode-and-decode-strings.rs b/rust/0271-encode-and-decode-strings.rs new file mode 100644 index 000000000..65e230b13 --- /dev/null +++ b/rust/0271-encode-and-decode-strings.rs @@ -0,0 +1,42 @@ +struct Codec {} + +impl Codec { + fn new() -> Self { + Self {} + } + + fn encode(&self, strs: Vec) -> String { + let mut store = String::new(); + + for s in strs{ + let len = s.len() as u8; + + store.push(len as char); + store.push_str(&s); + } + + store + } + + fn decode(&self, s: String) -> Vec { + let s: Vec = s.chars().collect(); + let mut i = 0; + + let mut res = vec![]; + while i < s.len(){ + let len = s[i] as u8 as usize; + i+=1; + + let j = i + len; + + if j <= s.len(){ + let slice = &s[i..i + len]; + res.push(slice.into_iter().collect::()); + } + + i+=len; + } + + res + } +} \ No newline at end of file diff --git a/rust/0283-move-zeroes.rs b/rust/0283-move-zeroes.rs new file mode 100644 index 000000000..a686fc049 --- /dev/null +++ b/rust/0283-move-zeroes.rs @@ -0,0 +1,12 @@ +impl Solution { + pub fn move_zeroes(nums: &mut Vec) { + let mut left = 0; + + for r in 0..nums.len() { + if nums[r] != 0 { + nums.swap(left, r); + left += 1; + } + } + } +} diff --git a/rust/0287-find-the-duplicate-number.rs b/rust/0287-find-the-duplicate-number.rs new file mode 100644 index 000000000..9e5d80e5b --- /dev/null +++ b/rust/0287-find-the-duplicate-number.rs @@ -0,0 +1,17 @@ +impl Solution { + pub fn find_duplicate(nums: Vec) -> i32 { + let (mut slow, mut fast) = (nums[0], nums[nums[0] as usize]); + while slow != fast { + slow = nums[slow as usize]; + fast = nums[nums[fast as usize] as usize]; + } + + slow = 0; + while slow != fast { + slow = nums[slow as usize]; + fast = nums[fast as usize]; + } + + slow + } +} diff --git a/rust/0290-word-pattern.rs b/rust/0290-word-pattern.rs new file mode 100644 index 000000000..7981a3d27 --- /dev/null +++ b/rust/0290-word-pattern.rs @@ -0,0 +1,27 @@ +impl Solution { + pub fn word_pattern(pattern: String, s: String) -> bool { + use std::collections::{HashMap, HashSet}; + + if pattern.len() != s.split_whitespace().count() { + return false; + } + + let mut char_to_word = HashMap::new(); + let mut word_to_char = HashMap::new(); + + for (c, word) in pattern.chars().zip(s.split_ascii_whitespace()) { + if let Some(w) = char_to_word.insert(c, word) { + if w != word { + return false; + } + } + + if let Some(w) = word_to_char.insert(word, c) { + if w != c { + return false; + } + } + } + true + } +} \ No newline at end of file diff --git a/rust/0293-sliding-window-maximum.rs b/rust/0293-sliding-window-maximum.rs new file mode 100644 index 000000000..d4c8efc50 --- /dev/null +++ b/rust/0293-sliding-window-maximum.rs @@ -0,0 +1,29 @@ +use std::collections::VecDeque; + +impl Solution { + pub fn max_sliding_window(nums: Vec, k: i32) -> Vec { + let mut output = vec![]; + let mut q: VecDeque = VecDeque::new(); + + let (mut l, mut r) = (0, 0); + while r < nums.len() { + while !q.is_empty() && nums[*q.iter().last().unwrap()] < nums[r] { + q.pop_back(); + } + q.push_back(r); + + if l > q[0] { + q.pop_front(); + } + + if r + 1 >= k as usize { + output.push(nums[q[0]]); + l += 1; + } + + r += 1; + } + + output + } +} diff --git a/rust/0300-longest-increasing-subsequence.rs b/rust/0300-longest-increasing-subsequence.rs new file mode 100644 index 000000000..417e5fe49 --- /dev/null +++ b/rust/0300-longest-increasing-subsequence.rs @@ -0,0 +1,19 @@ +impl Solution { + pub fn length_of_lis(nums: Vec) -> i32 { + let mut tails = vec![]; + + for &num in &nums { + match tails.binary_search(&num) { + Ok(_) => {}, + Err(i) => { + if i == tails.len() { + tails.push(num); + } else { + tails[i] = num; + } + } + } + } + tails.len() as i32 + } +} \ No newline at end of file diff --git a/rust/0303-range-sum-query-immutable.rs b/rust/0303-range-sum-query-immutable.rs new file mode 100644 index 000000000..4dc5b955e --- /dev/null +++ b/rust/0303-range-sum-query-immutable.rs @@ -0,0 +1,44 @@ +/* + * @lc app=leetcode id=303 lang=rust + * + * [303] Range Sum Query - Immutable + */ + +// @lc code=start +struct NumArray { + prefix: Vec +} + + +/** + * `&self` means the method takes an immutable reference. + * If you need a mutable reference, change it to `&mut self` instead. + */ +impl NumArray { + + fn new(nums: Vec) -> Self { + let mut prefix = vec![]; + let mut cur = 0; + for n in nums { + cur += n; + prefix.push(cur); + } + Self { prefix } + } + + fn sum_range(&self, left: i32, right: i32) -> i32 { + let right_sum = self.prefix[right as usize]; + let left_sum = if left > 0 { + self.prefix[(left - 1) as usize] + } else { + 0 + }; + right_sum - left_sum + } +} +/** + * Your NumArray object will be instantiated and called as such: + * let obj = NumArray::new(nums); + * let ret_1: i32 = obj.sum_range(left, right); + */ +// @lc code=end diff --git a/rust/0309-best-time-to-buy-and-sell-stock-with-cooldown.rs b/rust/0309-best-time-to-buy-and-sell-stock-with-cooldown.rs new file mode 100644 index 000000000..912fa521d --- /dev/null +++ b/rust/0309-best-time-to-buy-and-sell-stock-with-cooldown.rs @@ -0,0 +1,14 @@ +impl Solution { + pub fn max_profit(prices: Vec) -> i32 { + let mut p1 = 0; + let mut p2 = 0; + + for x in 1..=prices.len()-1 { + let temp = p1; + p1 = std::cmp::max(p1+prices[x]-prices[x-1], p2); + p2 = std::cmp::max(temp, p2); + } + + return std::cmp::max(p1, p2) + } +} diff --git a/rust/0322-coin-change.rs b/rust/0322-coin-change.rs new file mode 100644 index 000000000..939605921 --- /dev/null +++ b/rust/0322-coin-change.rs @@ -0,0 +1,17 @@ +impl Solution { + pub fn coin_change(coins: Vec, amount: i32) -> i32 { + let mut change = vec![amount + 1; (amount + 1) as usize]; + change[0] = 0; + for a in 1..=amount { + for c in &coins { + if a - c >= 0 { + change[a as usize] = change[a as usize].min(1 + change[(a - c) as usize]); + } + } + } + if change[amount as usize] == amount + 1 { + return -1; + } + change[amount as usize] + } +} diff --git a/rust/0329-longest-increasing-path-in-a-matrix.rs b/rust/0329-longest-increasing-path-in-a-matrix.rs new file mode 100644 index 000000000..c321123e0 --- /dev/null +++ b/rust/0329-longest-increasing-path-in-a-matrix.rs @@ -0,0 +1,43 @@ +impl Solution { + pub fn longest_increasing_path(matrix: Vec>) -> i32 { + use std::cmp::max; + use std::collections::HashMap; + + let mut dp: HashMap::<(usize, usize), i32> = HashMap::new(); + + fn dfs( + r: usize, + c: usize, + prevVal: i32, + matrix: &[Vec], + dp: &mut HashMap<(usize, usize), i32>, + ) -> i32 { + if r < 0 + || r >= matrix.len() + || c < 0 + || c >= matrix[0].len() + || matrix[r][c] <= prevVal + { + return 0; + } + if let Some(&result) = dp.get(&(r, c)) { + return result; + } + + let mut res = 1; + res = max(res, 1 + dfs(r + 1, c, matrix[r][c], matrix, dp)); + res = max(res, 1 + dfs(r - 1, c, matrix[r][c], matrix, dp)); + res = max(res, 1 + dfs(r, c + 1, matrix[r][c], matrix, dp)); + res = max(res, 1 + dfs(r, c - 1, matrix[r][c], matrix, dp)); + dp.insert((r, c), res); + res + } + + for r in 0..matrix.len() { + for c in 0..matrix[0].len() { + dfs(r, c, -1, &matrix, &mut dp); + } + } + *dp.values().max().unwrap_or(&1) + } +} diff --git a/rust/0332-reconstruct-itinerary.rs b/rust/0332-reconstruct-itinerary.rs new file mode 100644 index 000000000..0dc61de0f --- /dev/null +++ b/rust/0332-reconstruct-itinerary.rs @@ -0,0 +1,31 @@ +use std::cmp::Reverse; +use std::collections::{BinaryHeap, HashMap}; + +impl Solution { + pub fn find_itinerary(tickets: Vec>) -> Vec { + let mut graph: HashMap<&str, BinaryHeap>> = HashMap::new(); + for ticket in tickets.iter() { + graph + .entry(&ticket[0]) + .or_insert_with(BinaryHeap::new) + .push(Reverse(&ticket[1])); + } + let mut answer: Vec = Vec::with_capacity(tickets.len() + 1); + let mut stack: Vec<&str> = vec!["JFK"]; + while let Some(src) = stack.last() { + if let Some(dsts) = graph.get_mut(src) { + if !dsts.is_empty() { + if let Some(dst) = dsts.pop() { + stack.push(dst.0); + } + continue; + } + } + if let Some(last) = stack.pop() { + answer.push(last.to_string()); + } + } + answer.reverse(); + answer + } +} diff --git a/rust/0338-counting-bits.rs b/rust/0338-counting-bits.rs new file mode 100644 index 000000000..105ab6640 --- /dev/null +++ b/rust/0338-counting-bits.rs @@ -0,0 +1,23 @@ +impl Solution { + pub fn count_bits(n: i32) -> Vec { + let mut ans = Vec::new(); + + for i in 0..=n { + ans.push(set_bits(i)); + } + + ans + } +} + +pub fn set_bits(mut n: i32) -> i32 { + let mut count = 0; + + while n > 0 { + n = n & (n - 1); + count += 1; + } + + count +} + diff --git a/rust/0344-reverse-string.rs b/rust/0344-reverse-string.rs new file mode 100644 index 000000000..fc1d51eb4 --- /dev/null +++ b/rust/0344-reverse-string.rs @@ -0,0 +1,11 @@ +impl Solution { + pub fn reverse_string(s: &mut Vec) { + let (mut left, mut right) = (0, s.len() - 1); + + while left < right { + s.swap(left, right); + left += 1; + right -= 1; + } + } +} \ No newline at end of file diff --git a/rust/0347-top-k-frequent-elements.rs b/rust/0347-top-k-frequent-elements.rs new file mode 100644 index 000000000..1624ce42c --- /dev/null +++ b/rust/0347-top-k-frequent-elements.rs @@ -0,0 +1,48 @@ +use std::collections::HashMap; +use std::cmp::Ordering; + +impl Solution { + pub fn top_k_frequent(nums: Vec, k: i32) -> Vec { + let mut map: HashMap = HashMap::new(); + + for n in nums{ + *map.entry(n).and_modify(|val| *val+=1).or_default(); + } + + let mut freq: Vec<(i32, i32)> = map.into_iter().collect(); + + let res = match (k as usize).cmp(&freq.len()){ + Ordering::Equal => &freq, + _ => quick_select(&mut freq, k), + }; + + res.into_iter() + .map(|&n| n.0) + .collect() + } +} + +pub fn quick_select(slice: &mut [(i32, i32)], k: i32) -> &[(i32, i32)]{ + let (mut pivot, mut i, mut j) = (0, 1, 1); + + for index in 1..slice.len(){ + if slice[index].1 >= slice[pivot].1{ + slice.swap(index, j); + }else{ + slice.swap(index, i); + i+=1; + } + j+=1; + } + + slice.swap(pivot, i - 1); + pivot = i - 1; + + let larger_items = (j - pivot) as i32; + + match larger_items.cmp(&k) { + Ordering::Less => quick_select(&mut slice[0..j], k), + Ordering::Greater => quick_select(&mut slice[pivot + 1..j], k), + Ordering::Equal => &slice[pivot..j], + } +} diff --git a/rust/0352-data-stream-as-disjoint-intervals.rs b/rust/0352-data-stream-as-disjoint-intervals.rs new file mode 100644 index 000000000..a07f8fc4c --- /dev/null +++ b/rust/0352-data-stream-as-disjoint-intervals.rs @@ -0,0 +1,73 @@ +// BTreeSet Solution + +use std::collections::BTreeSet; + +struct SummaryRanges { + tree_map: BTreeSet, +} + +impl SummaryRanges { + fn new() -> Self { + Self { + tree_map: BTreeSet::new(), + } + } + + fn add_num(&mut self, value: i32) { + self.tree_map.insert(value); + } + + fn get_intervals(&self) -> Vec> { + let mut res: Vec> = vec![]; + + for n in &self.tree_map { + if !res.is_empty() && res.last().unwrap()[1] + 1 == *n { + let mut last = res.pop().unwrap(); + last[1] = *n; + res.push(last); + } else { + res.push(vec![*n, *n]); + } + } + + res + } +} + +// HashSet Solution +use std::collections::HashSet; +struct SummaryRanges { + ranges: Vec>, + num_set: HashSet, +} + +impl SummaryRanges { + fn new() -> Self { + Self { + ranges: vec![], + num_set: HashSet::new(), + } + } + + fn add_num(&mut self, value: i32) { + self.num_set.insert(value); + } + + fn get_intervals(&self) -> Vec> { + let mut nums: Vec = self.num_set.iter().cloned().collect(); + nums.sort(); + let mut res = vec![]; + let mut i = 0; + + while i < nums.len() { + let start = nums[i]; + + while i + 1 < nums.len() && nums[i] + 1 == nums[i + 1] { + i += 1; + } + res.push(vec![start, nums[i]]); + i += 1; + } + res + } +} diff --git a/rust/0371-sum-of-two-integers.rs b/rust/0371-sum-of-two-integers.rs new file mode 100644 index 000000000..d2f9aab4b --- /dev/null +++ b/rust/0371-sum-of-two-integers.rs @@ -0,0 +1,13 @@ +impl Solution { + pub fn get_sum(a: i32, b: i32) -> i32 { + fn recurse(a: i32, b: i32) -> i32 { + if (a & b) << 1 == 0 { + return a ^ b; + } + + recurse(a ^ b, (a & b) << 1) + } + + recurse(a, b) + } +} diff --git a/rust/0374-guess-number-higher-or-lower.rs b/rust/0374-guess-number-higher-or-lower.rs new file mode 100644 index 000000000..d610353b5 --- /dev/null +++ b/rust/0374-guess-number-higher-or-lower.rs @@ -0,0 +1,26 @@ +/** + * Forward declaration of guess API. + * @param num your guess + * @return -1 if num is higher than the picked number + * 1 if num is lower than the picked number + * otherwise return 0 + * unsafe fn guess(num: i32) -> i32 {} + */ + +impl Solution { + unsafe fn guessNumber(n: i32) -> i32 { + Self::binary_search(1, n) + } + + unsafe fn binary_search(left: i32, right: i32) -> i32 { + let mid = left + (right - left ) / 2; + + if guess(mid) < 0 { + Self::binary_search(1, mid) + } else if guess(mid) > 0 { + Self::binary_search(mid + 1, right) + } else { + mid + } + } +} diff --git a/rust/0380-insert-delete-getrandom-o1.rs b/rust/0380-insert-delete-getrandom-o1.rs new file mode 100644 index 000000000..61913abb1 --- /dev/null +++ b/rust/0380-insert-delete-getrandom-o1.rs @@ -0,0 +1,40 @@ +use rand::seq::SliceRandom; +use std::collections::HashMap; + +pub struct RandomizedSet { + mp: HashMap, + arr: Vec, +} + +impl RandomizedSet { + fn new() -> Self { + RandomizedSet { + mp: HashMap::new(), + arr: Vec::new(), + } + } + + fn insert(&mut self, val: i32) -> bool { + let res = !self.mp.contains_key(&val); + if res { + self.mp.insert(val, self.arr.len() as i32); + self.arr.push(val); + } + res + } + fn remove(&mut self, val: i32) -> bool { + let res = self.mp.contains_key(&val); + if res { + let idx = *self.mp.get(&val).unwrap(); + self.mp + .entry(*self.arr.last().unwrap()) + .and_modify(|v| *v = idx); + self.arr.swap_remove(idx as usize); + self.mp.remove(&val); + } + res + } + fn get_random(&self) -> i32 { + *self.arr.choose(&mut rand::thread_rng()).unwrap() + } +} diff --git a/rust/0392-is-subsequence.rs b/rust/0392-is-subsequence.rs new file mode 100644 index 000000000..0853fe2db --- /dev/null +++ b/rust/0392-is-subsequence.rs @@ -0,0 +1,17 @@ +impl Solution { + pub fn is_subsequence(s: String, t: String) -> bool { + let s: Vec = s.chars().collect(); + let t: Vec = t.chars().collect(); + let mut l = 0; + let mut r = 0; + while l < s.len() && r < t.len() { + if s[l] == t[r] { + l += 1; + r += 1; + } else { + r += 1; + } + } + l == s.len() + } +} diff --git a/rust/0394-decode-string.rs b/rust/0394-decode-string.rs new file mode 100644 index 000000000..5388bf6e9 --- /dev/null +++ b/rust/0394-decode-string.rs @@ -0,0 +1,25 @@ +impl Solution { + pub fn decode_string(s: String) -> String { + let mut stack = vec![]; + + for ch in s.chars() { + if ch != ']' { + stack.push(ch.to_string()); + } else { + let mut substr = String::new(); + while stack.last() != Some(&"[".to_string()) { + substr = stack.pop().unwrap().to_string() + &substr; + } + stack.pop(); + + let mut multiplier = String::new(); + while !stack.is_empty() && stack.last().unwrap().parse::().is_ok() { + multiplier = stack.pop().unwrap().to_string() + &multiplier; + } + + stack.push(substr.repeat(multiplier.parse::().unwrap())); + } + } + stack.join("") + } +} \ No newline at end of file diff --git a/rust/0402-remove-k-digits.rs b/rust/0402-remove-k-digits.rs new file mode 100644 index 000000000..4d6a5ca72 --- /dev/null +++ b/rust/0402-remove-k-digits.rs @@ -0,0 +1,32 @@ +impl Solution { + pub fn remove_kdigits(num: String, k: i32) -> String { + let mut stack = vec![]; + let mut k = k as usize; + + for ch in num.chars() { + while let Some(&top) = stack.last() { + if k > 0 && top > ch { + k -= 1; + stack.pop(); + } else { + break; + } + } + + stack.push(ch); + } + + // stack = stack[..stack.len() - k].to_vec(); + while k != 0 { + stack.pop(); + k -= 1; + } + + let res: String = stack.into_iter().skip_while(|&ch| ch == '0').collect(); + if res.is_empty() { + "0".to_string() + } else { + res + } + } +} \ No newline at end of file diff --git a/rust/0417-pacific-atlantic-water-flow.rs b/rust/0417-pacific-atlantic-water-flow.rs new file mode 100644 index 000000000..2634f6ca6 --- /dev/null +++ b/rust/0417-pacific-atlantic-water-flow.rs @@ -0,0 +1,66 @@ +use std::collections::HashSet; + +impl Solution { + pub fn pacific_atlantic(heights: Vec>) -> Vec> { + fn dfs( + heights: &Vec>, + x: i32, + y: i32, + visit: &mut HashSet<(i32, i32)>, + prev: i32, + ) { + if x < 0 + || y < 0 + || x == heights.len() as i32 + || y == heights[0].len() as i32 + || heights[x as usize][y as usize] < prev + || visit.contains(&(x, y)) + { + return; + } + visit.insert((x, y)); + for (add_x, add_y) in [(0, 1), (1, 0), (0, -1), (-1, 0)] { + dfs( + heights, + x + add_x, + y + add_y, + visit, + heights[x as usize][y as usize], + ); + } + } + + let (mut pac, mut atl) = (HashSet::new(), HashSet::new()); + let (n_rows, n_cols) = (heights.len(), heights[0].len()); + + for x in 0..n_rows { + dfs(&heights, x as i32, 0, &mut pac, heights[x][0]); + dfs( + &heights, + x as i32, + n_cols as i32 - 1, + &mut atl, + heights[x][n_cols - 1], + ); + } + + for y in 0..n_cols { + dfs(&heights, 0, y as i32, &mut pac, heights[0][y]); + dfs( + &heights, + n_rows as i32 - 1, + y as i32, + &mut atl, + heights[n_rows - 1][y], + ); + } + + (0..n_rows) + .flat_map(|x| (0..n_cols).map(move |y| (x, y))) + .filter(|(x, y)| { + pac.contains(&(*x as i32, *y as i32)) && atl.contains(&(*x as i32, *y as i32)) + }) + .map(|(x, y)| vec![x as i32, y as i32]) + .collect() + } +} diff --git a/rust/0424-longest-repeating-character-replacement.rs b/rust/0424-longest-repeating-character-replacement.rs new file mode 100644 index 000000000..98d99a35a --- /dev/null +++ b/rust/0424-longest-repeating-character-replacement.rs @@ -0,0 +1,24 @@ +use std::collections::HashMap; + +impl Solution { + pub fn character_replacement(s: String, k: i32) -> i32 { + let s: Vec = s.chars().collect(); + let (mut res, mut l, mut maxf) = (0, 0, 0); + let mut count: HashMap = HashMap::new(); + + for r in 0..s.len() { + *count.entry(s[r]).or_default() += 1; + maxf = maxf.max(*count.get(&s[r]).unwrap()); + + while (r - l + 1) - maxf as usize > k as usize { + *count.get_mut(&s[l]).unwrap() -= 1; + l += 1; + } + + res = res.max(r - l + 1); + } + + res as i32 + } +} + diff --git a/rust/0435-non-overlapping-intervals.rs b/rust/0435-non-overlapping-intervals.rs new file mode 100644 index 000000000..3328dda6d --- /dev/null +++ b/rust/0435-non-overlapping-intervals.rs @@ -0,0 +1,22 @@ +impl Solution { + pub fn erase_overlap_intervals(mut intervals: Vec>) -> i32 { + intervals.sort_by(|a, b| a[0].cmp(&b[0])); + + let mut res = 0; + let mut prev_end = intervals[0][1]; + + for interval in intervals.iter().skip(1) { + let start = interval[0]; + let end = interval[1]; + + if start >= prev_end { + prev_end = end; + } else { + res += 1; + prev_end = prev_end.min(end); + } + } + + res + } +} diff --git a/rust/0438-find-all-anagrams-in-a-string.rs b/rust/0438-find-all-anagrams-in-a-string.rs new file mode 100644 index 000000000..2e9668e0b --- /dev/null +++ b/rust/0438-find-all-anagrams-in-a-string.rs @@ -0,0 +1,65 @@ +use std::ops::{Index, IndexMut}; +struct CharVec(Vec); + +impl CharVec { + fn new() -> Self { + Self(vec![0; 26]) + } +} + +impl Index for CharVec { + type Output = i32; + fn index(&self, index: char) -> &Self::Output { + match index { + 'a'..='z' => &self.0[(index as u8 - 'a' as u8) as usize], + _ => panic!("[!!] {} character is not supported in CharVec", index), + } + } +} + +impl IndexMut for CharVec { + fn index_mut(&mut self, index: char) -> &mut Self::Output { + match index { + 'a'..='z' => &mut self.0[(index as u8 - 'a' as u8) as usize], + _ => panic!("[!!] {} character is not supported in CharVec", index), + } + } +} + +impl Eq for CharVec {} + +impl PartialEq for CharVec { + fn eq(&self, other: &Self) -> bool { + self.0.eq(&other.0) + } +} + +impl Solution { + pub fn find_anagrams(s: String, p: String) -> Vec { + let (s_len, p_len) = (s.len(), p.len()); + let mut ans = vec![]; + if s_len < p_len { + return ans; + } + let mut freq_s = CharVec::new(); + let mut freq_p = CharVec::new(); + let s = s.chars().collect::>(); + let p = p.chars().collect::>(); + for i in 0..p_len { + freq_s[s[i]] += 1; + freq_p[p[i]] += 1; + } + if freq_s == freq_p { + ans.push(0); + } + /* sliding window with of length p_len */ + for i in p_len..s_len { + freq_s[s[i - p_len]] -= 1; + freq_s[s[i]] += 1; + if freq_s == freq_p { + ans.push((i - p_len + 1) as i32); + } + } + ans + } +} diff --git a/rust/0441-arranging-coins.rs b/rust/0441-arranging-coins.rs new file mode 100644 index 000000000..3d92066a4 --- /dev/null +++ b/rust/0441-arranging-coins.rs @@ -0,0 +1,21 @@ +impl Solution { + pub fn arrange_coins(n: i32) -> i32 { + let (mut l, mut r) = (1, n); + let mut res = 0; + + while l <= r { + let mid = l + (r - l) / 2; + let coins = (mid as i64 * (mid as i64 + 1)) / 2; + + if coins > n as i64 { + r = mid - 1; + } else { + l = mid + 1; + res = mid; + } + } + + res as i32 + + } +} \ No newline at end of file diff --git a/rust/0448-find-all-numbers-disappeared-in-an-array.rs b/rust/0448-find-all-numbers-disappeared-in-an-array.rs new file mode 100644 index 000000000..5452c1ffd --- /dev/null +++ b/rust/0448-find-all-numbers-disappeared-in-an-array.rs @@ -0,0 +1,18 @@ +impl Solution { + pub fn find_disappeared_numbers(nums: Vec) -> Vec { + let mut nums = nums; + for i in 0..nums.len() { + let j = nums[i].abs() - 1; + nums[j as usize] = -1 * nums[j as usize].abs(); + } + + let mut result: Vec = vec![]; + + for (index, number) in nums.iter().enumerate() { + if *number > 0 { + result.push((index + 1) as i32); + } + } + result + } +} \ No newline at end of file diff --git a/rust/0450-delete-node-in-a-bst.rs b/rust/0450-delete-node-in-a-bst.rs new file mode 100644 index 000000000..0656e10bd --- /dev/null +++ b/rust/0450-delete-node-in-a-bst.rs @@ -0,0 +1,109 @@ +/* + +In this code we have to delete the node of a binary search tree the logic is as follows: + +1) If the value of the current node is greater than the value of the key to be deleted go left + +2) If the value of the current node is less than the value of the key to be deleted go right + +3) If the value of the current node is the value to be deleted there can be two senarios : + +a) The left side of the node is none in that case simply return the right side of the node + +b) The right side of the node is none in that case simply return the left side of the node + +c) Both left and right side of the node are not none in that case: + +* Go to the right branch of the tree and then go as left as possible. Return the number + +* The value of the node to be deleted should be replaced by the value return by the inorder_successor function + +* Recursively call the delete node function in order to delete the leaf node whose value we returned from the inorder_successor function + + +*/ + + +use std::rc::Rc; +use std::cell::RefCell; + +#[derive(Debug, PartialEq, Eq)] +pub struct TreeNode { + pub val: i32, + pub left: Option>>, + pub right: Option>>, +} + +impl TreeNode { + #[inline] + pub fn new(val: i32) -> Self { + TreeNode { + val, + left: None, + right: None + } + } +} + +struct Solution{} + +impl Solution { + pub fn delete_node(root: Option>>, key: i32)->Option>>{ + Solution::helper(root.as_ref(), key) + } + + pub fn helper(root:Option<&Rc>>,key:i32)->Option>>{ + + if root.is_none(){ + return None; + } + + let current_node: &Rc> = root.unwrap(); + + if current_node.borrow().val > key{ + let l = Solution::helper(current_node.borrow().left.as_ref(), key); + current_node.borrow_mut().left = l; + }else if current_node.borrow().val < key { + let r = Solution::helper(current_node.borrow().right.as_ref(), key); + current_node.borrow_mut().right = r; + } + + else{ + + if current_node.borrow().left.is_none(){ + return current_node.borrow().right.clone(); + } else if current_node.borrow().right.is_none() { + return current_node.borrow().left.clone(); + } + + else{ + + let number = Solution::inorder_successor(current_node.borrow().right.as_ref(), key); + if let Some(num) = number{ + let r = Solution::helper(current_node.borrow().right.as_ref(), num); + current_node.borrow_mut().right = r; + current_node.borrow_mut().val = num; + } + } + } + + return Some(current_node.clone()); + + } + + pub fn inorder_successor(root:Option<&Rc>>,value:i32)->Option{ + + if let Some(node) = root { + if node.borrow().left.is_none(){ + return Some(node.borrow().val); + } + + else + { + return Solution::inorder_successor(node.borrow().left.as_ref(), value); + } + } + + return None + } +} \ No newline at end of file diff --git a/rust/0456-132-pattern.rs b/rust/0456-132-pattern.rs new file mode 100644 index 000000000..bcd39c3b7 --- /dev/null +++ b/rust/0456-132-pattern.rs @@ -0,0 +1,20 @@ +impl Solution { + pub fn find132pattern(nums: Vec) -> bool { + let mut stack: Vec<(i32, i32)> = vec![]; // (num, min_left) + let mut current_min = nums[0]; + + for n in nums.iter().skip(1) { + while !stack.is_empty() && *n >= stack.last().unwrap().0 { + stack.pop(); + } + if !stack.is_empty() && *n > stack.last().unwrap().1 { + return true; + } + + stack.push((*n, current_min)); + current_min = current_min.min(*n); + } + + false + } +} \ No newline at end of file diff --git a/rust/0472-concatenated-words.rs b/rust/0472-concatenated-words.rs new file mode 100644 index 000000000..de0608bef --- /dev/null +++ b/rust/0472-concatenated-words.rs @@ -0,0 +1,29 @@ +use std::collections::HashSet; +use std::iter::FromIterator; + +impl Solution { + pub fn find_all_concatenated_words_in_a_dict(words: Vec) -> Vec { + let word_set: HashSet = HashSet::from_iter(words.iter().cloned()); + let mut res = vec![]; + + for w in words { + if Self::dfs(&w, &word_set) { + res.push(w); + } + } + + res + } + + pub fn dfs(word: &str, word_set: &HashSet) -> bool { + for i in 1..word.len() { + let (prefix, suffix) = (&word[..i], &word[i..]); + if (word_set.contains(prefix) && word_set.contains(suffix)) + || (word_set.contains(prefix) && Self::dfs(suffix, word_set)) + { + return true; + } + } + false + } +} diff --git a/rust/0496-next-greater-element-I.rs b/rust/0496-next-greater-element-I.rs new file mode 100644 index 000000000..b513ec8f0 --- /dev/null +++ b/rust/0496-next-greater-element-I.rs @@ -0,0 +1,61 @@ +use std::collections::HashMap; + +impl Solution { + pub fn next_greater_element(nums1: Vec, nums2: Vec) -> Vec { + let map: HashMap = nums1 + .iter() + .cloned() + .enumerate() + .map(Self::reverse_tuple) + .collect(); + + let mut result = vec![0; nums1.len()]; + + for (index, val) in nums2.iter().enumerate() { + if map.contains_key(val) { + let idx = *map.get(val).unwrap(); + + let next_greater = nums2.iter().skip(index).find(|&x| x > val).unwrap_or(&-1); + + result[idx] = *next_greater; + } + } + result + } + + // O(nums1.length + nums2.length) + pub fn other_next_greater_element(nums1: Vec, nums2: Vec) -> Vec { + let map: HashMap = nums1 + .iter() + .cloned() + .enumerate() + .map(Self::reverse_tuple) + .collect(); + + let mut result = vec![-1; nums1.len()]; + let mut stack = Vec::new(); + + for (_, val) in nums2.iter().enumerate() { + while !stack.is_empty() && val > stack.last().unwrap() { + let value = stack.pop().unwrap(); + let idx = *map.get(&value).unwrap(); + result[idx] = value; + } + if map.contains_key(val) { + stack.push(*val); + } + } + result + } + + pub fn reverse_tuple(tup: (T, U)) -> (U, T) + where + T: Default, + U: Default, + { + let mut new_tup: (U, T) = (U::default(), T::default()); + new_tup.0 = tup.1; + new_tup.1 = tup.0; + new_tup + } +} diff --git a/rust/0496-next-greater-element-i.rs b/rust/0496-next-greater-element-i.rs new file mode 100644 index 000000000..b513ec8f0 --- /dev/null +++ b/rust/0496-next-greater-element-i.rs @@ -0,0 +1,61 @@ +use std::collections::HashMap; + +impl Solution { + pub fn next_greater_element(nums1: Vec, nums2: Vec) -> Vec { + let map: HashMap = nums1 + .iter() + .cloned() + .enumerate() + .map(Self::reverse_tuple) + .collect(); + + let mut result = vec![0; nums1.len()]; + + for (index, val) in nums2.iter().enumerate() { + if map.contains_key(val) { + let idx = *map.get(val).unwrap(); + + let next_greater = nums2.iter().skip(index).find(|&x| x > val).unwrap_or(&-1); + + result[idx] = *next_greater; + } + } + result + } + + // O(nums1.length + nums2.length) + pub fn other_next_greater_element(nums1: Vec, nums2: Vec) -> Vec { + let map: HashMap = nums1 + .iter() + .cloned() + .enumerate() + .map(Self::reverse_tuple) + .collect(); + + let mut result = vec![-1; nums1.len()]; + let mut stack = Vec::new(); + + for (_, val) in nums2.iter().enumerate() { + while !stack.is_empty() && val > stack.last().unwrap() { + let value = stack.pop().unwrap(); + let idx = *map.get(&value).unwrap(); + result[idx] = value; + } + if map.contains_key(val) { + stack.push(*val); + } + } + result + } + + pub fn reverse_tuple(tup: (T, U)) -> (U, T) + where + T: Default, + U: Default, + { + let mut new_tup: (U, T) = (U::default(), T::default()); + new_tup.0 = tup.1; + new_tup.1 = tup.0; + new_tup + } +} diff --git a/rust/0502-ipo.rs b/rust/0502-ipo.rs new file mode 100644 index 000000000..77275560e --- /dev/null +++ b/rust/0502-ipo.rs @@ -0,0 +1,27 @@ +impl Solution { + // Time O(n*log(n)) - Space O(n) + pub fn find_maximized_capital(k: i32, w: i32, profits: Vec, capital: Vec) -> i32 { + use std::collections::BinaryHeap; + let mut sorted_jobs: Vec<(i32, i32)> = (0..profits.len()) + .map(|i| (capital[i], profits[i])) + .collect(); + sorted_jobs.sort(); + sorted_jobs.reverse(); + let mut heap = BinaryHeap::::new(); + // The current capital. + let mut cap = w; + for _ in 0..k { + while sorted_jobs.len() > 0 && sorted_jobs[sorted_jobs.len() - 1].0 <= cap { + match sorted_jobs.pop() { + Some((_, p)) => heap.push(p), + None => unreachable!(), + } + } + match heap.pop() { + Some(v) => cap += v, + None => break, + } + } + cap + } +} diff --git a/rust/0518-coin-change-ii.rs b/rust/0518-coin-change-ii.rs new file mode 100644 index 000000000..70d4cb032 --- /dev/null +++ b/rust/0518-coin-change-ii.rs @@ -0,0 +1,16 @@ +impl Solution { + // Time O(m*n) - Space O(n) + pub fn change(amount: i32, coins: Vec) -> i32 { + let n = amount as usize; + let mut dp = vec![0; n + 1]; + dp[0] = 1; + let mut c: usize; + for coin in coins { + c = coin as usize; + for i in c..=n { + dp[i] += dp[i - c]; + } + } + *dp.last().unwrap() + } +} diff --git a/rust/0523-continuous-subarray-sum.rs b/rust/0523-continuous-subarray-sum.rs new file mode 100644 index 000000000..03c637eb8 --- /dev/null +++ b/rust/0523-continuous-subarray-sum.rs @@ -0,0 +1,22 @@ +use std::collections::HashMap; + +impl Solution { + pub fn check_subarray_sum(nums: Vec, k: i32) -> bool { + let mut hashmap: HashMap = HashMap::new(); + hashmap.insert(0, -1); + let mut sum = 0; + + for (i, &num) in nums.iter().enumerate() { + sum += num; + if let Some(&prev_index) = hashmap.get(&(sum % k)) { + if i as i32 - prev_index >= 2 { + return true; + } + } else { + hashmap.insert(sum % k, i as i32); + } + } + + false + } +} diff --git a/rust/0535-encode-and-decode-tinyURL.rs b/rust/0535-encode-and-decode-tinyURL.rs new file mode 100644 index 000000000..ebc3b6731 --- /dev/null +++ b/rust/0535-encode-and-decode-tinyURL.rs @@ -0,0 +1,37 @@ +use std::collections::HashMap; + +const BASE: &str = "http://tinyurl.com"; + +struct Codec { + url_map: HashMap, +} + +impl Codec { + fn new() -> Self { + Self { + url_map: HashMap::new(), + } + } + + // Encodes a URL to a shortened URL. + fn encode(&mut self, longURL: String) -> String { + let short_url = format!("{}{}", BASE, Self::generate_hash(&longURL)); + self.url_map.insert(short_url.clone(), longURL.clone()); + short_url + } + + // Decodes a shortened URL to its original URL. + fn decode(&self, shortURL: String) -> String { + self.url_map.get(&shortURL).unwrap().clone() + } + + fn generate_hash(url: &String) -> i32 { + let mut hash = 5831; + + for ch in url.chars() { + hash = ((hash << 5) + hash) + (ch as i32); + } + + hash + } +} diff --git a/rust/0535-encode-and-decode-tinyurl.rs b/rust/0535-encode-and-decode-tinyurl.rs new file mode 100644 index 000000000..ebc3b6731 --- /dev/null +++ b/rust/0535-encode-and-decode-tinyurl.rs @@ -0,0 +1,37 @@ +use std::collections::HashMap; + +const BASE: &str = "http://tinyurl.com"; + +struct Codec { + url_map: HashMap, +} + +impl Codec { + fn new() -> Self { + Self { + url_map: HashMap::new(), + } + } + + // Encodes a URL to a shortened URL. + fn encode(&mut self, longURL: String) -> String { + let short_url = format!("{}{}", BASE, Self::generate_hash(&longURL)); + self.url_map.insert(short_url.clone(), longURL.clone()); + short_url + } + + // Decodes a shortened URL to its original URL. + fn decode(&self, shortURL: String) -> String { + self.url_map.get(&shortURL).unwrap().clone() + } + + fn generate_hash(url: &String) -> i32 { + let mut hash = 5831; + + for ch in url.chars() { + hash = ((hash << 5) + hash) + (ch as i32); + } + + hash + } +} diff --git a/rust/0543-diameter-of-binary-tree.rs b/rust/0543-diameter-of-binary-tree.rs new file mode 100644 index 000000000..f644f9d43 --- /dev/null +++ b/rust/0543-diameter-of-binary-tree.rs @@ -0,0 +1,21 @@ +use std::rc::Rc; +use std::cell::RefCell; +impl Solution { + pub fn diameter_of_binary_tree(root: Option>>) -> i32 { + fn dfs(root: Option>>) -> (i32, i32) { + match root { + None => (0, 0), + Some(node) => { + let (l_diameter, l_max) = dfs(node.borrow().left.clone()); + let (r_diameter, r_max) = dfs(node.borrow().right.clone()); + ( + 1 + l_diameter.max(r_diameter), + l_max.max(r_max.max(l_diameter + r_diameter)), + ) + } + } + } + + dfs(root).1 + } +} diff --git a/rust/0554-brick-wall.rs b/rust/0554-brick-wall.rs new file mode 100644 index 000000000..b4f41edd1 --- /dev/null +++ b/rust/0554-brick-wall.rs @@ -0,0 +1,28 @@ +use std::collections::HashMap; +impl Solution { + pub fn least_bricks(wall: Vec>) -> i32 { + wall.len() as i32 + - wall + .into_iter() + .map(|row| { + let mut prefix: Vec<_> = row + .into_iter() + .scan(0, |sum, x| { + *sum += x; + Some(*sum) + }) + .collect(); + prefix.pop(); + prefix + }) + .flatten() + .fold(HashMap::from([(0, 0)]), |mut acc, x| { + acc.entry(x).and_modify(|cnt| *cnt += 1).or_insert(1); + acc + }) + .into_iter() + .map(|(_, v)| v) + .max() + .unwrap() + } +} diff --git a/rust/0560-subarray-sum-equals-k.rs b/rust/0560-subarray-sum-equals-k.rs new file mode 100644 index 000000000..d383f58e4 --- /dev/null +++ b/rust/0560-subarray-sum-equals-k.rs @@ -0,0 +1,13 @@ +use std::collections::HashMap; +impl Solution { + pub fn subarray_sum(nums: Vec, k: i32) -> i32 { + let (mut count, mut sum, mut map) = (0, 0, HashMap::with_capacity(nums.len() / 2)); + map.insert(0, 1); + for num in nums { + sum += num; + count += map.get(&(sum - k)).copied().unwrap_or(0); + map.entry(sum).and_modify(|e| *e = *e + 1).or_insert(1); + } + count + } +} diff --git a/rust/0567-permutation-in-string.rs b/rust/0567-permutation-in-string.rs new file mode 100644 index 000000000..d1d8051dc --- /dev/null +++ b/rust/0567-permutation-in-string.rs @@ -0,0 +1,45 @@ +impl Solution { + pub fn check_inclusion(s1: String, s2: String) -> bool { + if s1.len() > s2.len() { + return false; + } + + let (mut s1_cnt, mut s2_cnt) = ([0; 26], [0; 26]); + for i in 0..s1.len() { + s1_cnt[s1.chars().nth(i).unwrap() as usize - 'a' as usize] += 1; + s2_cnt[s2.chars().nth(i).unwrap() as usize - 'a' as usize] += 1; + } + + let mut matches = 0; + for i in 0..26 { + matches = if s1_cnt[i] == s2_cnt[i] { matches + 1 } else { matches }; + } + + let mut l = 0; + for r in s1.len()..s2.len() { + if matches == 26 { + return true; + } + + let mut index = s2.chars().nth(r).unwrap() as usize - 'a' as usize; + s2_cnt[index] += 1; + if s1_cnt[index] == s2_cnt[index] { + matches += 1; + } else if s1_cnt[index] + 1 == s2_cnt[index]{ + matches -= 1; + } + + index = s2.chars().nth(l).unwrap() as usize - 'a' as usize; + s2_cnt[index] -= 1; + if s1_cnt[index] == s2_cnt[index] { + matches += 1; + } else if s1_cnt[index] - 1 == s2_cnt[index]{ + matches -= 1; + } + + l += 1; + } + + matches == 26 + } +} diff --git a/rust/0572-subtree-of-another-tree.rs b/rust/0572-subtree-of-another-tree.rs new file mode 100644 index 000000000..32f55602a --- /dev/null +++ b/rust/0572-subtree-of-another-tree.rs @@ -0,0 +1,32 @@ +use std::rc::Rc; +use std::cell::RefCell; +impl Solution { + pub fn is_subtree( + root: Option>>, + sub_root: Option>> + ) -> bool { + fn is_sametree(a: Option>>, b: Option>>) -> bool { + match (a, b) { + (None, None) => true, + (Some(a), Some(b)) => { + a.borrow().val == b.borrow().val + && is_sametree(a.borrow().left.clone(), b.borrow().left.clone()) + && is_sametree(a.borrow().right.clone(), b.borrow().right.clone()) + } + _ => false, + } + } + + match (root, sub_root) { + (_, None) => true, + (None, _) => false, + (Some(root), Some(sub_root)) => { + if is_sametree(Some(root.clone()), Some(sub_root.clone())) { + return true; + } + Solution::is_subtree(root.borrow().left.clone(), Some(sub_root.clone())) + || Solution::is_subtree(root.borrow().right.clone(), Some(sub_root)) + } + } + } +} diff --git a/rust/0605-can-place-flowers.rs b/rust/0605-can-place-flowers.rs new file mode 100644 index 000000000..20d6b4ff5 --- /dev/null +++ b/rust/0605-can-place-flowers.rs @@ -0,0 +1,59 @@ +impl Solution { + // Space Complexity: O(1) + pub fn can_place_flowers_1(flowerbed: Vec, n: i32) -> bool { + let mut empty = if flowerbed[0] != 0 { 0 } else { 1 }; + let mut n = n; + + for f in flowerbed { + if f != 0 { + n -= (empty - 1) / 2; + empty = 0; + } else { + empty += 1; + } + } + + n -= empty / 2; + + n <= 0 + } + + // Space Complexity: O(1) + pub fn can_place_flowers_2(flowerbed: Vec, n: i32) -> bool { + let mut flowerbed = flowerbed; + let mut n = n; + + for i in 0..flowerbed.len() { + if n == 0 { + return true; + } + if (i == 0 || flowerbed[i - 1] == 0) + && flowerbed[i] == 0 + && (i == flowerbed.len() - 1 || flowerbed[i + 1] == 0) + { + flowerbed[i] = 1; + n -= 1; + } + } + + n == 0 + } + + // Space Complexity: O(n) + pub fn other_can_place_flowers_3(flowerbed: Vec, n: i32) -> bool { + let mut f: Vec = vec![vec![0], flowerbed, vec![0]] + .into_iter() + .flatten() + .collect(); + let mut n = n; + + for i in 1..f.len() - 1 { + if f[i - 1] == 0 && f[i] == 0 && f[i + 1] == 0 { + f[i] = 1; + n -= 1; + } + } + + n <= 0 + } +} \ No newline at end of file diff --git a/rust/0621-task-scheduler.rs b/rust/0621-task-scheduler.rs new file mode 100644 index 000000000..537dbd753 --- /dev/null +++ b/rust/0621-task-scheduler.rs @@ -0,0 +1,34 @@ +use std::collections::{BinaryHeap, VecDeque, HashMap}; + +impl Solution { + pub fn least_interval(tasks: Vec, n: i32) -> i32 { + let mut count = HashMap::new(); + let mut max_heap = BinaryHeap::new(); + for task in tasks { + let count = count.entry(task).or_insert(0); + *count += 1; + } + for (key, val) in count.iter() { + max_heap.push(*val); + } + let mut time = 0; + let mut deque: VecDeque<(i32,i32)> = VecDeque::new(); + while deque.len() > 0 || max_heap.len() > 0 { + time += 1; + + if max_heap.len() == 0 { + time = deque[0].1; + } + else { + let cnt = max_heap.pop().unwrap() - 1; + if cnt != 0 { + deque.push_back((cnt, time + n)); + } + } + if deque.len() > 0 && deque[0].1 == time { + max_heap.push(deque.pop_front().unwrap().0); + } + } + time + } +} \ No newline at end of file diff --git a/rust/0647-palindromic-substrings.rs b/rust/0647-palindromic-substrings.rs new file mode 100644 index 000000000..fe0b7084b --- /dev/null +++ b/rust/0647-palindromic-substrings.rs @@ -0,0 +1,29 @@ +impl Solution { + pub fn count_substrings(s: String) -> i32 { + let s = s.chars().collect::>(); + let (mut count, length): (i32, i32) = (0, s.len() as i32); + + for i in 0..length { + // odd length + + let (mut l, mut r) = (i, i); + while l >= 0 && r < length && s[l as usize] == s[r as usize] { + count += 1; + l -= 1; + r += 1; + } + + // even length + + let (mut l, mut r) = (i, i + 1); + + while l >= 0 && r < length && s[l as usize] == s[r as usize] { + count += 1; + l -= 1; + r += 1; + } + } + + count + } +} diff --git a/rust/0658-find-k-closest-elements.rs b/rust/0658-find-k-closest-elements.rs new file mode 100644 index 000000000..c7e0c0c75 --- /dev/null +++ b/rust/0658-find-k-closest-elements.rs @@ -0,0 +1,15 @@ +impl Solution { + pub fn find_closest_elements(arr: Vec, k: i32, x: i32) -> Vec { + let (mut l, mut r) = (0, arr.len() - 1); + + while r - l >= k as usize { + if x - arr[l] <= arr[r] - x { + r -= 1; + } else { + l += 1; + } + } + + arr[l..r + 1].to_vec() + } +} diff --git a/rust/0678-valid-parenthesis-string.rs b/rust/0678-valid-parenthesis-string.rs new file mode 100644 index 000000000..70d63f116 --- /dev/null +++ b/rust/0678-valid-parenthesis-string.rs @@ -0,0 +1,30 @@ +impl Solution { + pub fn check_valid_string(s: String) -> bool { + let mut left_min = 0; + let mut left_max = 0; + + for c in s.chars() { + match c { + '(' => { + left_min = left_min + 1; + left_max = left_max + 1; + } + ')' => { + left_min = left_min - 1; + left_max = left_max - 1; + } + _ => { + left_min = left_min - 1; + left_max = left_max + 1; + } + } + if left_max < 0 { + return false; + } + if left_min < 0 { + left_min = 0; + } + } + left_min == 0 + } +} diff --git a/rust/0680-valid-palindrome-II.rs b/rust/0680-valid-palindrome-II.rs new file mode 100644 index 000000000..0ea89060f --- /dev/null +++ b/rust/0680-valid-palindrome-II.rs @@ -0,0 +1,33 @@ +impl Solution { + pub fn valid_palindrome(s: String) -> bool { + fn is_palindrome(s: &[u8]) -> Option<(usize, usize)> { + let (mut i, mut j) = (0, s.len() - 1); + while i < j { + if s[i] != s[j] { + return Some((i, j)); + } + i += 1; + j -= 1; + } + + None // is palindrome + } + + let s = s.as_bytes().to_vec(); + match is_palindrome(&s) { + Some((i, j)) => { + if is_palindrome(&s[i+1..=j]).is_none() { + return true; + } + if is_palindrome(&s[i..=j-1]).is_none() { + return true; + } + } + None => { + return true; + } + } + + false + } +} diff --git a/rust/0680-valid-palindrome-ii.rs b/rust/0680-valid-palindrome-ii.rs new file mode 100644 index 000000000..0ea89060f --- /dev/null +++ b/rust/0680-valid-palindrome-ii.rs @@ -0,0 +1,33 @@ +impl Solution { + pub fn valid_palindrome(s: String) -> bool { + fn is_palindrome(s: &[u8]) -> Option<(usize, usize)> { + let (mut i, mut j) = (0, s.len() - 1); + while i < j { + if s[i] != s[j] { + return Some((i, j)); + } + i += 1; + j -= 1; + } + + None // is palindrome + } + + let s = s.as_bytes().to_vec(); + match is_palindrome(&s) { + Some((i, j)) => { + if is_palindrome(&s[i+1..=j]).is_none() { + return true; + } + if is_palindrome(&s[i..=j-1]).is_none() { + return true; + } + } + None => { + return true; + } + } + + false + } +} diff --git a/rust/0682-baseball-game.rs b/rust/0682-baseball-game.rs new file mode 100644 index 000000000..1388aef4c --- /dev/null +++ b/rust/0682-baseball-game.rs @@ -0,0 +1,27 @@ +impl Solution { + pub fn cal_points(operations: Vec) -> i32 { + let mut points: Vec = Vec::new(); + + for op in operations { + match &op[..] { + "D" => { + let new_point = points.last().unwrap(); + points.push(*new_point * 2); + } + + "C" => { + points.pop(); + } + "+" => { + let len = points.len(); + points.push(points[len - 1] + points[len - 2]); + } + _ => { + points.push(op.parse::().unwrap()); + } + } + } + + points.iter().sum() + } +} \ No newline at end of file diff --git a/rust/0684-redundant-connection.rs b/rust/0684-redundant-connection.rs new file mode 100644 index 000000000..b550ce0f6 --- /dev/null +++ b/rust/0684-redundant-connection.rs @@ -0,0 +1,60 @@ +use std::cmp::Ordering; + +struct UnionFind { + parent: Vec, + rank: Vec, +} + +impl UnionFind { + fn new(n: usize) -> Self { + UnionFind { + parent: (0..(n + 1)).collect(), + rank: vec![1; n + 1], + } + } + + fn find(&mut self, n: usize) -> usize { + let mut p = self.parent[n]; + + while p != self.parent[p] { + self.parent[p] = self.parent[self.parent[p]]; + p = self.parent[p]; + } + + p + } + + fn union(&mut self, n1: usize, n2: usize) -> bool { + let p1 = self.find(n1); + let p2 = self.find(n2); + + if p1 == p2 { + return false; + } + match self.rank[p1].cmp(&self.rank[p2]) { + Ordering::Greater => { + self.parent[p2] = p1; + self.rank[p1] += self.rank[p2]; + } + _ => { + self.parent[p1] = p2; + self.rank[p2] = self.rank[p1]; + } + } + true + } +} + +impl Solution { + pub fn find_redundant_connection(edges: Vec>) -> Vec { + let mut union_find = UnionFind::new(edges.len() + 1); + + for edge in edges { + let (n1, n2) = (edge[0] as usize, edge[1] as usize); + if !union_find.union(n1, n2) { + return vec![n1 as i32, n2 as i32]; + } + } + unreachable!() + } +} \ No newline at end of file diff --git a/rust/0695-max-area-of-island.rs b/rust/0695-max-area-of-island.rs new file mode 100644 index 000000000..ba1f6469b --- /dev/null +++ b/rust/0695-max-area-of-island.rs @@ -0,0 +1,38 @@ +impl Solution { + pub fn max_area_of_island(grid: Vec>) -> i32 { + fn dfs(grid: &mut Vec>, x: i32, y: i32) -> i32 { + if x < 0 + || y < 0 + || x >= grid.len() as i32 + || y >= grid[0].len() as i32 + || grid[x as usize][y as usize] == 0 + { + return 0; + } + + grid[x as usize][y as usize] = 0; + + let mut count = 1; + let directions: [(i32, i32); 4] = [(0, 1), (1, 0), (0, -1), (-1, 0)]; + + for (add_x, add_y) in directions { + count += dfs(grid, x + add_x, y + add_y); + } + + count + } + + let mut max_area = 0; + let mut new_grid = grid.clone(); + + for x in 0..grid.len() { + for y in 0..grid[0].len() { + if new_grid[x][y] == 1 { + max_area = max_area.max(dfs(&mut new_grid, x as i32, y as i32)); + } + } + } + + max_area + } +} diff --git a/rust/0703-kth-largest-element-in-a-stream.rs b/rust/0703-kth-largest-element-in-a-stream.rs new file mode 100644 index 000000000..032e4fad2 --- /dev/null +++ b/rust/0703-kth-largest-element-in-a-stream.rs @@ -0,0 +1,31 @@ +use std::{cmp::Reverse, collections::BinaryHeap}; + +struct KthLargest { + min_heap: BinaryHeap>, + size: usize, +} + +impl KthLargest { + fn new(k: i32, nums: Vec) -> Self { + let mut kth_largest = KthLargest { + min_heap: BinaryHeap::new(), + size: k as usize, + }; + for n in nums { + kth_largest.add(n); + } + kth_largest + } + + fn add(&mut self, val: i32) -> i32 { + self.min_heap.push(Reverse(val)); + if self.min_heap.len() > self.size { + self.min_heap.pop(); + } + + match self.min_heap.peek() { + Some(Reverse(min_val)) => *min_val, + _ => -1, + } + } +} diff --git a/rust/0704-binary-search.rs b/rust/0704-binary-search.rs new file mode 100644 index 000000000..2e02951dc --- /dev/null +++ b/rust/0704-binary-search.rs @@ -0,0 +1,18 @@ +use std::cmp::Ordering::{Equal, Less, Greater}; + +impl Solution { + pub fn search(nums: Vec, target: i32) -> i32 { + let (mut l, mut r) = (0, nums.len()); + + while l < r { + let m = l + (r - l) / 2; + match target.cmp(&nums[m]) { + Equal => return m as i32, + Less => r = m, + Greater => l = m + 1, + } + } + + -1 + } +} diff --git a/rust/0705-design-hashset.rs b/rust/0705-design-hashset.rs new file mode 100644 index 000000000..8e10cd521 --- /dev/null +++ b/rust/0705-design-hashset.rs @@ -0,0 +1,75 @@ +/* + * @lc app=leetcode id=705 lang=rust + * + * [705] Design HashSet + */ + +use std::vec; + +// @lc code=start +struct MyHashSet { + buckets: Vec>, + capacity: usize, + size: usize, + load_factor: f64, +} + +/** + * `&self` means the method takes an immutable reference. + * If you need a mutable reference, change it to `&mut self` instead. + */ +impl MyHashSet { + fn new() -> Self { + Self { + buckets: vec![Vec::new(); 8], + capacity: 8, + size: 0, + load_factor: 0.75, + } + } + + fn hash(&self, key: i32) -> usize { + (key % self.capacity as i32) as usize + } + + fn add(&mut self, key: i32) { + let index = self.hash(key); + let bucket = &mut self.buckets[index]; + if !bucket.iter().any(|&k| k == key) { + bucket.push(key); + self.size += 1; + if self.size as f64 / self.capacity as f64 > self.load_factor { + self.resize() + } + } + } + + fn remove(&mut self, key: i32) { + let index = self.hash(key); + let bucket = &mut self.buckets[index]; + if let Some(i) = bucket.iter().position(|&k| k == key) { + bucket.swap_remove(i); + self.size -= 1; + } + } + + fn contains(&self, key: i32) -> bool { + let index = self.hash(key); + let bucket = &self.buckets[index]; + bucket.iter().any(|&k| k == key) + } + + fn resize(&mut self) { + let new_capacity = self.capacity * 2; + let mut new_buckets = vec![Vec::new(); new_capacity]; + for bucket in self.buckets.iter() { + for &key in bucket.iter() { + let new_index = (key % new_capacity as i32) as usize; + new_buckets[new_index].push(key); + } + } + self.buckets = new_buckets; + self.capacity = new_capacity; + } +} +// @lc code=end \ No newline at end of file diff --git a/rust/0706-design-hashmap.rs b/rust/0706-design-hashmap.rs new file mode 100644 index 000000000..07ecd58e4 --- /dev/null +++ b/rust/0706-design-hashmap.rs @@ -0,0 +1,45 @@ +struct MyHashMap { + buckets: Vec>, +} + +// Prime number of buckets to reduce collisions +const N_BUCKETS: usize = 1031; + +impl MyHashMap { + + fn new() -> Self { + Self{ buckets: vec![vec![]; N_BUCKETS] } + } + + fn hash(key: i32) -> usize { + key as usize % N_BUCKETS + } + + fn find_entry(&mut self, key: i32) -> (&mut Vec<(i32, i32)>, Result) { + let bucket = &mut self.buckets[Self::hash(key)]; + let result = bucket.binary_search_by(|(k, v)| k.cmp(&key)); + (bucket, result) + } + + fn put(&mut self, key: i32, value: i32) { + match self.find_entry(key) { + (bucket, Ok(index)) => bucket[index] = (key, value), + (bucket, Err(index)) => bucket.insert(index, (key, value)), + } + } + + fn get(&self, key: i32) -> i32 { + let bucket = &self.buckets[Self::hash(key)]; + match bucket.binary_search_by(|(k, v)| k.cmp(&key)) { + Ok(index) => bucket[index].1, + Err(index) => -1, + } + } + + fn remove(&mut self, key: i32) { + match self.find_entry(key) { + (bucket, Ok(index)) => { bucket.remove(index); }, + _ => (), + } + } +} \ No newline at end of file diff --git a/rust/0724-find-pivot-index.rs b/rust/0724-find-pivot-index.rs new file mode 100644 index 000000000..fe52ab5b4 --- /dev/null +++ b/rust/0724-find-pivot-index.rs @@ -0,0 +1,15 @@ +impl Solution { + pub fn pivot_index(nums: Vec) -> i32 { + let total: i32 = nums.iter().sum(); + let mut left_sum = 0; + + for (i, num) in nums.iter().enumerate() { + let right_sum = total - num - left_sum; + if left_sum == right_sum { + return i as i32; + } + left_sum += num; + } + -1 + } +} \ No newline at end of file diff --git a/rust/0735-asteroid-collision.rs b/rust/0735-asteroid-collision.rs new file mode 100644 index 000000000..9cc4f9226 --- /dev/null +++ b/rust/0735-asteroid-collision.rs @@ -0,0 +1,27 @@ +use std::cmp::Ordering; + +impl Solution { + pub fn asteroid_collision(asteroids: Vec) -> Vec { + let mut stack: Vec = vec![]; + + for mut asteroid in asteroids { + while !stack.is_empty() && asteroid < 0 && stack.last() > Some(&0) { + let diff = asteroid + stack.last().unwrap(); + match diff.cmp(&0) { + Ordering::Less => { + stack.pop(); + } + Ordering::Greater => asteroid = 0, + Ordering::Equal => { + asteroid = 0; + stack.pop(); + } + }; + } + if asteroid != 0 { + stack.push(asteroid); + } + } + stack + } +} \ No newline at end of file diff --git a/rust/0739-daily-temperatures.rs b/rust/0739-daily-temperatures.rs new file mode 100644 index 000000000..f019b59c8 --- /dev/null +++ b/rust/0739-daily-temperatures.rs @@ -0,0 +1,16 @@ +impl Solution { + pub fn daily_temperatures(temperatures: Vec) -> Vec { + let mut res = vec![0; temperatures.len()]; + let mut stack: Vec<(i32, usize)> = vec![]; // (temp, index) + + for (i, val) in temperatures.iter().enumerate() { + while !stack.is_empty() && *val > stack.last().unwrap().0 { + let (_, stack_index) = stack.pop().unwrap(); + res[stack_index] = (i - stack_index) as i32; + } + + stack.push((*val, i)); + } + res + } +} \ No newline at end of file diff --git a/rust/0746-min-cost-climbing-stairs.rs b/rust/0746-min-cost-climbing-stairs.rs new file mode 100644 index 000000000..b2f5320fc --- /dev/null +++ b/rust/0746-min-cost-climbing-stairs.rs @@ -0,0 +1,14 @@ +// time compexity : O(n) +// space compexity : O(1) + +impl Solution { + pub fn min_cost_climbing_stairs(mut cost: Vec) -> i32 { + for i in 2..cost.len(){ + cost[i] += cost[i- 1].min(cost[i-2]); + } + + let len = cost.len(); + + cost[len - 1].min(cost[len - 2]) + } +} \ No newline at end of file diff --git a/rust/0778-swim-in-rising-water.rs b/rust/0778-swim-in-rising-water.rs new file mode 100644 index 000000000..1f8102161 --- /dev/null +++ b/rust/0778-swim-in-rising-water.rs @@ -0,0 +1,65 @@ +// https://leetcode.com/problems/swim-in-rising-water/submissions/1011351676/ +// Faster than 100% and less memory than 100% + +use std::collections::BinaryHeap; +#[derive(Eq, PartialEq)] +struct State { + t: i32, + i: usize, + j: usize, +} + +impl Ord for State { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + other.t.cmp(&self.t) + } +} + +impl PartialOrd for State { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + + +impl Solution { + pub fn swim_in_water(grid: Vec>) -> i32 { + // Problem Description: You are given an n x n integer matrix grid where each value grid[i][j] represents the elevation at that point (i, j). + // The rain starts to fall. At time t, the depth of the water everywhere is t. You can swim from a square to another 4-directionally adjacent square if and only if the elevation of both squares individually are at most t. You can swim infinite distances in zero time. Of course, you must stay within the boundaries of the grid during your swim. + // Return the least time until you can reach the bottom right square (n - 1, n - 1) if you start at the top left square (0, 0). + let n = grid.len(); + let mut visited = vec![vec![false; n]; n]; + let mut heap = BinaryHeap::new(); + heap.push(State { + t: grid[0][0], + i: 0, + j: 0, + }); + let mut min_t = 0; + // want to find the min time that can reach the bottom right square + while let Some(State { t, i, j }) = heap.pop() { + min_t = min_t.max(t); + if i == n - 1 && j == n - 1 { + return min_t; + } + for (di, dj) in [(1, 0), (0, 1), (-1, 0), (0, -1)].iter() { + let ni = i as i32 + di; + let nj = j as i32 + dj; + if ni >= 0 + && ni < n as i32 + && nj >= 0 + && nj < n as i32 + && !visited[ni as usize][nj as usize] + { + heap.push(State { + t: grid[ni as usize][nj as usize], + i: ni as usize, + j: nj as usize, + }); + visited[ni as usize][nj as usize] = true; + } + } + } + min_t + } +} diff --git a/rust/0789-minimum-distance-between-two-bst-nodes.rs b/rust/0789-minimum-distance-between-two-bst-nodes.rs new file mode 100644 index 000000000..1f2579d7f --- /dev/null +++ b/rust/0789-minimum-distance-between-two-bst-nodes.rs @@ -0,0 +1,30 @@ +use std::cell::RefCell; +use std::rc::Rc; + +type OptNode = Option>>; + +impl Solution { + pub fn min_diff_in_bst(root: OptNode) -> i32 { + let mut min = i32::MAX; + let mut prev = None; + + Self::dfs(root, &mut prev, &mut min); + min + } + + fn dfs(root: Option>>, prev: &mut Option, min: &mut i32) { + if let Some(node) = root { + let node = node.borrow(); + + Self::dfs(node.left.clone(), prev, min); + + if let Some(prev) = prev { + *min = (*min).min(node.val - *prev) + } + *prev = Some(node.val); + + Self::dfs(node.right.clone(), prev, min); + } + } +} + diff --git a/rust/0853-car-fleet.rs b/rust/0853-car-fleet.rs new file mode 100644 index 000000000..411979175 --- /dev/null +++ b/rust/0853-car-fleet.rs @@ -0,0 +1,20 @@ +impl Solution { + pub fn car_fleet(target: i32, position: Vec, speed: Vec) -> i32 { + let mut position_speed_pair: Vec<(f64, f64)> = position + .iter() + .map(|x| *x as f64) + .zip(speed.iter().map(|x| *x as f64)) + .collect(); + + position_speed_pair.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap()); + + let mut stack = vec![]; + for (pos, speed) in position_speed_pair.iter().rev() { + stack.push((target as f64 - pos) / speed); + if stack.len() >= 2 && stack.last() <= stack.get(stack.len() - 2) { + stack.pop(); + } + } + stack.len() as i32 + } +} \ No newline at end of file diff --git a/rust/0875-koko-eating-bananas.rs b/rust/0875-koko-eating-bananas.rs new file mode 100644 index 000000000..9badef8de --- /dev/null +++ b/rust/0875-koko-eating-bananas.rs @@ -0,0 +1,26 @@ +use std::cmp::Ordering::{Equal, Less, Greater}; + +impl Solution { + pub fn min_eating_speed(piles: Vec, h: i32) -> i32 { + let max_piles = *piles.iter().max().unwrap() as usize; + let (mut l, mut r) = (1, max_piles); + let mut k = max_piles; + + while l <= r { + let m = l + (r - l) / 2; + let hrs: usize = piles.iter() + .map(|&num_bananas| ((num_bananas - 1) as usize / m) + 1) + .sum(); + + match hrs.cmp(&(h as usize)) { + Less | Equal => { + k = k.min(m); + r = m - 1; + }, + Greater => l = m + 1, + } + } + + k as i32 + } +} diff --git a/rust/0876-middle-of-the-linked-list.rs b/rust/0876-middle-of-the-linked-list.rs new file mode 100644 index 000000000..1f33b7ae8 --- /dev/null +++ b/rust/0876-middle-of-the-linked-list.rs @@ -0,0 +1,12 @@ +impl Solution { + pub fn middle_node(head: Option>) -> Option> { + let (mut slow, mut fast) = (&head, &head); + + while fast.is_some() && fast.as_ref().unwrap().next.is_some() { + fast = &fast.as_ref().unwrap().next.as_ref().unwrap().next; + slow = &slow.as_ref().unwrap().next + } + + slow.clone() + } +} diff --git a/rust/0895-maximum-frequency-stack.rs b/rust/0895-maximum-frequency-stack.rs new file mode 100644 index 000000000..8e44b03b7 --- /dev/null +++ b/rust/0895-maximum-frequency-stack.rs @@ -0,0 +1,35 @@ +use std::collections::HashMap; +struct FreqStack { + count: HashMap, + max_count: i32, + stacks: HashMap>, +} + +impl FreqStack { + fn new() -> Self { + Self { + count: HashMap::new(), + max_count: 0, + stacks: HashMap::new(), + } + } + + fn push(&mut self, val: i32) { + let val_count = 1 + *self.count.get(&val).or(Some(&0)).unwrap(); + self.count.insert(val, val_count); + if val_count > self.max_count { + self.max_count = val_count; + self.stacks.insert(val_count, vec![]); + } + self.stacks.get_mut(&val_count).unwrap().push(val); + } + + fn pop(&mut self) -> i32 { + let res = self.stacks.get_mut(&self.max_count).unwrap().pop().unwrap(); + *self.count.get_mut(&res).unwrap() -= 1; + if self.stacks.get(&self.max_count).unwrap().is_empty() { + self.max_count -= 1; + } + res + } +} diff --git a/rust/0901-online-stock-span.rs b/rust/0901-online-stock-span.rs new file mode 100644 index 000000000..217d0b4a2 --- /dev/null +++ b/rust/0901-online-stock-span.rs @@ -0,0 +1,19 @@ +struct StockSpanner { + stack: Vec<(i32, i32)>, // (price, span) +} + +impl StockSpanner { + fn new() -> Self { + Self { stack: vec![] } + } + + fn next(&mut self, price: i32) -> i32 { + let mut span = 1; + + while !self.stack.is_empty() && self.stack.last().unwrap().0 <= price { + span += self.stack.pop().unwrap().1; + } + self.stack.push((price, span)); + span + } +} diff --git a/rust/0904-fruit-into-baskets.rs b/rust/0904-fruit-into-baskets.rs new file mode 100644 index 000000000..74c58aa8e --- /dev/null +++ b/rust/0904-fruit-into-baskets.rs @@ -0,0 +1,29 @@ +use std::collections::HashMap; + +impl Solution { + pub fn total_fruit(fruits: Vec) -> i32 { + let mut count = HashMap::new(); + let (mut left, mut total, mut res) = (0, 0, 0); + + for fruit in &fruits { + *count.entry(*fruit).or_insert(0) += 1; + total += 1; + + while count.len() > 2 { + let f = fruits[left]; + match count.remove(&f) { + Some(v) if v > 1 => { + count.insert(f, v - 1); + } + _ => {} + } + total -= 1; + left += 1; + } + + res = res.max(total); + } + + res + } +} \ No newline at end of file diff --git a/rust/0912-sort-an-array.rs b/rust/0912-sort-an-array.rs new file mode 100644 index 000000000..e3db5d7ed --- /dev/null +++ b/rust/0912-sort-an-array.rs @@ -0,0 +1,49 @@ +/* + * @lc app=leetcode id=912 lang=rust + * + * [912] Sort an Array + */ +struct Solution; +// @lc code=start +impl Solution { + pub fn sort_array(nums: Vec) -> Vec { + if nums.len() > 1 { + let (l, r) = nums.split_at(nums.len() / 2); + let sorted_r = Self::sort_array(r.to_vec()); + let sorted_l = Self::sort_array(l.to_vec()); + + let mut res: Vec = nums.into(); + let (mut i, mut j) = (0, 0); + + let mut k = 0; + while i < sorted_l.len() && j < sorted_r.len() { + if sorted_l[i] <= sorted_r[j] { + res[k] = sorted_l[i].clone(); + i += 1; + } else { + res[k] = sorted_r[j].clone(); + j += 1; + } + k += 1; + } + while i < sorted_l.len() { + res[k] = sorted_l[i].clone(); + i += 1; + k += 1 + } + while j < sorted_r.len() { + res[k] = sorted_r[j].clone(); + j += 1; + k += 1; + } + res + } else { + nums + } + } +} +// @lc code=end +fn main() { + let v = vec![-2,3,-5]; + println!("{:?}", Solution::sort_array(v)); +} diff --git a/rust/0918-maximum-sum-circular-subarray.rs b/rust/0918-maximum-sum-circular-subarray.rs new file mode 100644 index 000000000..e4109510b --- /dev/null +++ b/rust/0918-maximum-sum-circular-subarray.rs @@ -0,0 +1,21 @@ +impl Solution { + pub fn max_subarray_sum_circular(nums: Vec) -> i32 { + let (mut global_max, mut global_min) = (nums[0], nums[0]); + let (mut current_max, mut current_min) = (0, 0); + let mut total = 0; + + for num in nums { + current_max = i32::max(num, current_max + num); + current_min = i32::min(num, current_min + num); + total += num; + global_max = i32::max(global_max, current_max); + global_min = i32::min(global_min, current_min); + } + + if global_max > 0 { + return i32::max(global_max, total - global_min); + } else { + return global_max; + } + } +} \ No newline at end of file diff --git a/rust/0926-flip-string-to-monotone-increasing.rs b/rust/0926-flip-string-to-monotone-increasing.rs new file mode 100644 index 000000000..ca81f5f02 --- /dev/null +++ b/rust/0926-flip-string-to-monotone-increasing.rs @@ -0,0 +1,14 @@ +impl Solution { + pub fn min_flips_mono_incr(s: String) -> i32 { + let (mut res, mut count_one) = (0, 0); + + for ch in s.chars() { + if ch == '1' { + count_one += 1; + } else { + res = i32::min(res + 1, count_one); + } + } + res + } +} \ No newline at end of file diff --git a/rust/0929-unique-email-addresses.rs b/rust/0929-unique-email-addresses.rs new file mode 100644 index 000000000..458a7594d --- /dev/null +++ b/rust/0929-unique-email-addresses.rs @@ -0,0 +1,18 @@ +use std::collections::HashSet; + +impl Solution { + pub fn num_unique_emails(emails: Vec) -> i32 { + let mut unique_emails = HashSet::new(); + + for email in emails { + let (local, domain) = email.split_once("@").unwrap(); + + let mut local = local.split("+").take(1).next().unwrap().replace(".", ""); + + local = format!("{}@{}", local, domain); + + unique_emails.insert(local); + } + unique_emails.len() as i32 + } +} \ No newline at end of file diff --git a/rust/0953-verifying-an-alien-dictionary.rs b/rust/0953-verifying-an-alien-dictionary.rs new file mode 100644 index 000000000..de31f6f79 --- /dev/null +++ b/rust/0953-verifying-an-alien-dictionary.rs @@ -0,0 +1,34 @@ +use std::collections::HashMap; +impl Solution { + pub fn is_alien_sorted(words: Vec, order: String) -> bool { + let n = words.len(); + let mut map = HashMap::new(); + + order.as_bytes().iter().enumerate().for_each(|(i, &v)| { + map.insert(v, i); + }); + + for i in 1..n { + let s1 = words[i - 1].as_bytes(); + let s2 = words[i].as_bytes(); + + let mut is_equal = true; + + for i in 0..s1.len().min(s2.len()) { + if map.get(&s1[i]) > map.get(&s2[i]) { + return false; + } + + if map.get(&s1[i]) < map.get(&s2[i]) { + is_equal = false; + break; + } + } + + if is_equal && s1.len() > s2.len() { + return false; + } + } + true + } +} \ No newline at end of file diff --git a/rust/0973-k-closest-points-to-origin.rs b/rust/0973-k-closest-points-to-origin.rs new file mode 100644 index 000000000..c7a7943c5 --- /dev/null +++ b/rust/0973-k-closest-points-to-origin.rs @@ -0,0 +1,20 @@ +use std::{cmp::Reverse, collections::BinaryHeap}; + +impl Solution { + pub fn k_closest(points: Vec>, k: i32) -> Vec> { + let mut pts = BinaryHeap::new(); + for point in points { + let dist = (point[0].pow(2) + point[1].pow(2)); + pts.push(Reverse((dist, point[0], point[1]))); + } + + let mut res: Vec> = vec![]; + for i in 0..k { + match pts.pop() { + Some(Reverse((dist, x, y))) => res.push(vec![x,y]), + None => {} + } + } + res + } +} \ No newline at end of file diff --git a/rust/0977-squares-of-a-sorted-array.rs b/rust/0977-squares-of-a-sorted-array.rs new file mode 100644 index 000000000..84ed5b539 --- /dev/null +++ b/rust/0977-squares-of-a-sorted-array.rs @@ -0,0 +1,29 @@ +impl Solution { + pub fn sorted_squares(nums: Vec) -> Vec { + let mut sq: Vec = vec![0; nums.len()]; + let mut i = nums.len() as isize - 1; + let mut l = 0; + let mut r = nums.len() as isize - 1; + + while l <= r { + if nums[l as usize].abs() > nums[r as usize].abs() { + sq[i as usize] = nums[l as usize] * nums[l as usize]; + l += 1; + } else { + sq[i as usize] = nums[r as usize] * nums[r as usize]; + r -= 1; + } + i -= 1; + } + + sq + } +} + +fn abs(x: i32) -> i32 { + if x < 0 { + x * -1 + } else { + x + } +} \ No newline at end of file diff --git a/rust/0981-time-based-key-value-store.rs b/rust/0981-time-based-key-value-store.rs new file mode 100644 index 000000000..28fe207e6 --- /dev/null +++ b/rust/0981-time-based-key-value-store.rs @@ -0,0 +1,38 @@ +use std::collections::HashMap; + +struct TimeMap { + hm: HashMap::> +} + +impl TimeMap { + + fn new() -> Self { + Self { + hm: HashMap::new() + } + } + + fn set(&mut self, key: String, value: String, timestamp: i32) { + self.hm.entry(key).or_default().push((value, timestamp)); + } + + fn get(&self, key: String, timestamp: i32) -> String { + let mut res = String::new(); + + if let Some(t_list) = self.hm.get(&key) { + let (mut l, mut r) = (0, t_list.len()); + + while l < r { + let m = l + (r - l) / 2; + if timestamp < t_list[m].1 { + r = m; + } else { + res = t_list[m].0.clone(); + l = m + 1; + } + } + } + + res + } +} diff --git a/rust/0989-add-to-array-form-of-integer.rs b/rust/0989-add-to-array-form-of-integer.rs new file mode 100644 index 000000000..918d6547c --- /dev/null +++ b/rust/0989-add-to-array-form-of-integer.rs @@ -0,0 +1,26 @@ +impl Solution { + pub fn add_to_array_form(num: Vec, k: i32) -> Vec { + let mut num: Vec = num.into_iter().rev().collect(); + let mut k = k; + let mut i = 0; + + while k > 0 { + let digit = k % 10; + + if i < num.len() { + num[i] += digit; + } else { + num.push(digit); + } + + let carry = num[i] / 10; + num[i] = num[i] % 10; + + k /= 10; + k += carry; + i += 1; + } + + num.into_iter().rev().collect() + } +} \ No newline at end of file diff --git a/rust/1029-two-city-scheduling.rs b/rust/1029-two-city-scheduling.rs new file mode 100644 index 000000000..ac7b1cd92 --- /dev/null +++ b/rust/1029-two-city-scheduling.rs @@ -0,0 +1,15 @@ +impl Solution { + pub fn two_city_sched_cost(costs: Vec>) -> i32 { + let mut costs = costs; + costs.sort_by_key(|pair| pair[1] - pair[0]); + + let mut total_cost = 0; + + let n = costs.len() / 2; + + for i in 0..n { + total_cost += costs[i][1] + costs[i + n][0]; + } + total_cost + } +} diff --git a/rust/1046-last-stone-weight.rs b/rust/1046-last-stone-weight.rs new file mode 100644 index 000000000..f99594e22 --- /dev/null +++ b/rust/1046-last-stone-weight.rs @@ -0,0 +1,19 @@ +impl Solution { + pub fn last_stone_weight(stones: Vec) -> i32 { + let mut stones_heap = std::collections::BinaryHeap::new(); + for stone in stones { + stones_heap.push(stone); + } + + while stones_heap.len() > 1 { + let first = stones_heap.pop().unwrap(); + let second = stones_heap.pop().unwrap(); + stones_heap.push(first - second); + } + + match stones_heap.peek() { + Some(val) => *val, + None => 0, + } + } +} diff --git a/rust/1071-greatest-common-divisor-of-strings.rs b/rust/1071-greatest-common-divisor-of-strings.rs new file mode 100644 index 000000000..0b8fa8d62 --- /dev/null +++ b/rust/1071-greatest-common-divisor-of-strings.rs @@ -0,0 +1,21 @@ +impl Solution { + pub fn gcd_of_strings(str1: String, str2: String) -> String { + let (len1, len2) = (str1.len(), str2.len()); + + for l in (1..=usize::min(len1, len2)).rev() { + if Self::is_divisor(&str1, &str2, len1, len2, l) { + return str1[..l].to_string(); + } + } + + "".to_string() + } + + pub fn is_divisor(str1: &String, str2: &String, len1: usize, len2: usize, l: usize) -> bool { + if len1 % l != 0 || len2 % l != 0 { + return false; + } + let (f1, f2) = (len1 / l, len2 / l); + str1[..l].repeat(f1) == *str1 && str1[..l].repeat(f2) == *str2 + } +} \ No newline at end of file diff --git a/rust/1137-n-th-tribonacci-number.rs b/rust/1137-n-th-tribonacci-number.rs new file mode 100644 index 000000000..1c9a6f667 --- /dev/null +++ b/rust/1137-n-th-tribonacci-number.rs @@ -0,0 +1,17 @@ +impl Solution { + pub fn tribonacci(n: i32) -> i32 { + let mut t = [0, 1, 1]; + + if n <= 2 { + return t[n as usize]; + } + + for i in 3..(n + 1) as usize { + t.swap(0, 1); + t.swap(1, 2); + t[2] = t.iter().sum(); + } + + t[2] + } +} diff --git a/rust/1143-Longest-Common-Subsequence.rs b/rust/1143-Longest-Common-Subsequence.rs new file mode 100644 index 000000000..0db081ae3 --- /dev/null +++ b/rust/1143-Longest-Common-Subsequence.rs @@ -0,0 +1,18 @@ +pub fn longest_common_subsequence(text1: String, text2: String) -> i32 { + let (text1, text2) = (text1.as_bytes(), text2.as_bytes()); + let (l1, l2) = (text1.len(), text2.len()); + + let mut matrix = vec![vec![0; l2 + 1]; l1 + 1]; + + for i in (0..l1).rev() { + for j in (0..l2).rev() { + matrix[i][j] = if text1[i] == text2[j] { + 1 + matrix[i + 1][j + 1] + } else { + matrix[i][j + 1].max(matrix[i + 1][j]) + }; + } + } + + matrix[0][0] +} diff --git a/rust/1143-longest-common-subsequence.rs b/rust/1143-longest-common-subsequence.rs new file mode 100644 index 000000000..0db081ae3 --- /dev/null +++ b/rust/1143-longest-common-subsequence.rs @@ -0,0 +1,18 @@ +pub fn longest_common_subsequence(text1: String, text2: String) -> i32 { + let (text1, text2) = (text1.as_bytes(), text2.as_bytes()); + let (l1, l2) = (text1.len(), text2.len()); + + let mut matrix = vec![vec![0; l2 + 1]; l1 + 1]; + + for i in (0..l1).rev() { + for j in (0..l2).rev() { + matrix[i][j] = if text1[i] == text2[j] { + 1 + matrix[i + 1][j + 1] + } else { + matrix[i][j + 1].max(matrix[i + 1][j]) + }; + } + } + + matrix[0][0] +} diff --git a/rust/1189-maximum-number-of-balloons.rs b/rust/1189-maximum-number-of-balloons.rs new file mode 100644 index 000000000..eee488c14 --- /dev/null +++ b/rust/1189-maximum-number-of-balloons.rs @@ -0,0 +1,23 @@ +use std::collections::HashMap; + +impl Solution { + pub fn max_number_of_balloons(text: String) -> i32 { + let mut count_text = HashMap::new(); + let mut balloon = HashMap::new(); + let mut result = text.len(); + + for ch in text.chars() { + *count_text.entry(ch).or_insert(0) += 1; + } + + for ch in "balloon".chars() { + *balloon.entry(ch).or_insert(0) += 1; + } + + for ch in balloon.keys() { + result = result.min(count_text.get(ch).unwrap_or(&0) / balloon.get(ch).unwrap()); + } + + result as i32 + } +} \ No newline at end of file diff --git a/rust/1209-remove-all-adjacent-duplicates-in-string-II.rs b/rust/1209-remove-all-adjacent-duplicates-in-string-II.rs new file mode 100644 index 000000000..b153a733c --- /dev/null +++ b/rust/1209-remove-all-adjacent-duplicates-in-string-II.rs @@ -0,0 +1,21 @@ +impl Solution { + pub fn remove_duplicates(s: String, k: i32) -> String { + let mut stack: Vec<(char, usize)> = vec![]; + for ch in s.chars() { + if !stack.is_empty() && stack.last().unwrap().0 == ch { + let mut last = stack.pop().unwrap(); + last.1 += 1; + stack.push(last); + } else { + stack.push((ch, 1)); + } + if stack.last().unwrap().1 == k as usize { + stack.pop(); + } + } + + stack.iter().fold(String::new(), |acc, &(ch, count)| { + acc + &ch.to_string().repeat(count) + }) + } +} \ No newline at end of file diff --git a/rust/1209-remove-all-adjacent-duplicates-in-string-ii.rs b/rust/1209-remove-all-adjacent-duplicates-in-string-ii.rs new file mode 100644 index 000000000..b153a733c --- /dev/null +++ b/rust/1209-remove-all-adjacent-duplicates-in-string-ii.rs @@ -0,0 +1,21 @@ +impl Solution { + pub fn remove_duplicates(s: String, k: i32) -> String { + let mut stack: Vec<(char, usize)> = vec![]; + for ch in s.chars() { + if !stack.is_empty() && stack.last().unwrap().0 == ch { + let mut last = stack.pop().unwrap(); + last.1 += 1; + stack.push(last); + } else { + stack.push((ch, 1)); + } + if stack.last().unwrap().1 == k as usize { + stack.pop(); + } + } + + stack.iter().fold(String::new(), |acc, &(ch, count)| { + acc + &ch.to_string().repeat(count) + }) + } +} \ No newline at end of file diff --git a/rust/1299-Replace-Elements-With-Greatest-Element-On-Right-Side.rs b/rust/1299-Replace-Elements-With-Greatest-Element-On-Right-Side.rs new file mode 100644 index 000000000..5caf419ad --- /dev/null +++ b/rust/1299-Replace-Elements-With-Greatest-Element-On-Right-Side.rs @@ -0,0 +1,12 @@ +use std::cmp::max; + +impl Solution { + pub fn replace_elements(arr: Vec) -> Vec { + let length = arr.len(); + let mut ans: Vec = vec![-1; length]; + for i in (1..=(length - 1)).rev() { + ans[i - 1] = max(arr[i], ans[i]); + } + ans + } +} diff --git a/rust/1299-replace-elements-with-greatest-element-on-right-side.rs b/rust/1299-replace-elements-with-greatest-element-on-right-side.rs new file mode 100644 index 000000000..5caf419ad --- /dev/null +++ b/rust/1299-replace-elements-with-greatest-element-on-right-side.rs @@ -0,0 +1,12 @@ +use std::cmp::max; + +impl Solution { + pub fn replace_elements(arr: Vec) -> Vec { + let length = arr.len(); + let mut ans: Vec = vec![-1; length]; + for i in (1..=(length - 1)).rev() { + ans[i - 1] = max(arr[i], ans[i]); + } + ans + } +} diff --git a/rust/1345-jump-game-iv.rs b/rust/1345-jump-game-iv.rs new file mode 100644 index 000000000..5f1250a75 --- /dev/null +++ b/rust/1345-jump-game-iv.rs @@ -0,0 +1,69 @@ +use std::collections::{HashMap, VecDeque}; +impl Solution { + // Time O(n) - Space O(n) + pub fn min_jumps(arr: Vec) -> i32 { + let n = arr.len(); + // Handle a base case. + if n < 2 { + return 0; + } + // Use a dictionary of vertices indexed by value. + let mut d = HashMap::>::new(); + arr.iter() + .enumerate() + .for_each(|(i, num)| d.entry(*num).or_default().push(i)); + // An array of flags of elements that we have processed already. + let mut seen = vec![false; n]; + seen[0] = true; + // Use BFS to travel through the graph. + let mut steps = 0; + let mut queue = VecDeque::::from(vec![0]); + loop { + steps += 1; + // Process an entire level. + for _ in 0..queue.len() { + let cur = queue.pop_front().unwrap(); + for nei in Self.get_unqueued_neighbors(cur, &n, &mut seen, &mut d, &arr) { + if nei == n - 1 { + return steps; + } + queue.push_back(nei); + } + } + } + } + + // An internal function that computes the unvisited neighbors of a given node. + fn get_unqueued_neighbors( + self, + i: usize, + n: &usize, + seen: &mut Vec, + d: &mut HashMap>, + arr: &Vec, + ) -> Vec { + let mut adj = vec![]; + // The element before is reachable. + if i > 0 && !seen[i - 1] { + seen[i - 1] = true; + adj.push(i - 1); + } + // The element after is also reachable. + if i < n - 1 && !seen[i + 1] { + seen[i + 1] = true; + adj.push(i + 1); + } + // And all nodes with the same value are also reachable. + if d.contains_key(&arr[i]) { + for node in d.entry(arr[i]).or_default() { + let node = *node; + if node != i { + adj.push(node); + seen[node] = true; + } + } + d.remove(&arr[i]); + } + adj + } +} diff --git a/rust/1443-minimum-time-to-collect-all-apples-in-a-tree.rs b/rust/1443-minimum-time-to-collect-all-apples-in-a-tree.rs new file mode 100644 index 000000000..42ab64ed6 --- /dev/null +++ b/rust/1443-minimum-time-to-collect-all-apples-in-a-tree.rs @@ -0,0 +1,39 @@ +use std::collections::HashMap; + +impl Solution { + pub fn min_time(n: i32, edges: Vec>, has_apple: Vec) -> i32 { + let mut adj = HashMap::new(); + + for edge in edges { + let (parent, child) = (edge[0], edge[1]); + adj.entry(parent).or_insert(vec![]).push(child); + adj.entry(child).or_insert(vec![]).push(parent); + } + + fn dfs( + current: i32, + parent: i32, + adj: &HashMap>, + has_apple: &Vec, + ) -> i32 { + let mut time = 0; + + if let Some(children) = adj.get(¤t) { + for child in children { + if *child == parent { + continue; + } + let child_time = dfs(*child, current, &adj, &has_apple); + + if child_time.is_positive() || has_apple[*child as usize] { + time += 2 + child_time; + } + } + } + + time + } + + dfs(0, -1, &adj, &has_apple) + } +} \ No newline at end of file diff --git a/rust/1448-count-good-nodes-in-binary-tree.rs b/rust/1448-count-good-nodes-in-binary-tree.rs new file mode 100644 index 000000000..5ac6ac1e4 --- /dev/null +++ b/rust/1448-count-good-nodes-in-binary-tree.rs @@ -0,0 +1,32 @@ +use std::rc::Rc; +use std::cell::RefCell; + +impl Solution { + pub fn good_nodes(root: Option>>) -> i32 { + let root = root.unwrap(); + let val = root.borrow().val; + let mut count = 0; + let mut stack = Vec::with_capacity(10_000); + stack.push((root, val as i16)); + + while let Some((curr, mut max)) = stack.pop() { + let mut curr = curr.borrow_mut(); + if curr.val as i16 >= max { + count += 1; + max = curr.val as i16; + } + if curr.left.is_some() { + let mut left = None; + std::mem::swap(&mut left, &mut curr.left); + stack.push((left.unwrap(), max)); + } + if curr.right.is_some() { + let mut right = None; + std::mem::swap(&mut right, &mut curr.right); + stack.push((right.unwrap(), max)); + } + } + + count + } +} diff --git a/rust/1470-shuffle-the-array.rs b/rust/1470-shuffle-the-array.rs new file mode 100644 index 000000000..cfdb4120e --- /dev/null +++ b/rust/1470-shuffle-the-array.rs @@ -0,0 +1,22 @@ +impl Solution { + pub fn shuffle(nums: Vec, n: i32) -> Vec { + let mut nums = nums; + + for i in 0..n as usize { + nums[i] = nums[i] << 10; + nums[i] = nums[i] | nums[i + n as usize]; + } + + let mut j = 2 * n - 1; + + for i in (0..=n as usize - 1).rev() { + let y = nums[i] & (2_i32.pow(10) - 1); + let x = nums[i] >> 10; + nums[j as usize] = y; + nums[j as usize - 1] = x; + j -= 2; + } + + nums + } +} diff --git a/rust/1472-design-browser-history.rs b/rust/1472-design-browser-history.rs new file mode 100644 index 000000000..dc15487aa --- /dev/null +++ b/rust/1472-design-browser-history.rs @@ -0,0 +1,28 @@ +struct BrowserHistory { + history: Vec, + current: usize, +} + +impl BrowserHistory { + fn new(homepage: String) -> Self { + Self { + history: vec![homepage], + current: 0, + } + } + + fn visit(&mut self, url: String) { + self.current += 1; + self.history.splice(self.current.., std::iter::once(url)); + } + + fn back(&mut self, steps: i32) -> String { + self.current = self.current.saturating_sub(steps as usize); + self.history[self.current].clone() + } + + fn forward(&mut self, steps: i32) -> String { + self.current = (self.current + steps as usize).min(self.history.len() - 1); + self.history[self.current].clone() + } +} diff --git a/rust/1480-running-sum-of-1d-array.rs b/rust/1480-running-sum-of-1d-array.rs new file mode 100644 index 000000000..0b65474d3 --- /dev/null +++ b/rust/1480-running-sum-of-1d-array.rs @@ -0,0 +1,11 @@ +impl Solution { + pub fn running_sum(nums: Vec) -> Vec { + let mut total = 0; + let mut arr = Vec::new(); + for n in nums { + total += n; + arr.push(total); + } + return arr; + } +} diff --git a/rust/1700-number-of-students-unable-to-eat-lunch.rs b/rust/1700-number-of-students-unable-to-eat-lunch.rs new file mode 100644 index 000000000..8d1bce600 --- /dev/null +++ b/rust/1700-number-of-students-unable-to-eat-lunch.rs @@ -0,0 +1,26 @@ +impl Solution { + pub fn count_students(students: Vec, mut sandwiches: Vec) -> i32 { + let mut count: i32 = 0; + let mut students_left: Vec = students.clone(); + + while !students_left.is_empty() { + if students_left[0] == sandwiches[0] { + students_left.remove(0); + sandwiches.remove(0); + count = 0; + } else { + let student = students_left.remove(0); + students_left.push(student); + count += 1; + } + + // If all students have been cycled and none can eat, return count + if count == students_left.len() as i32 { + return count; + } + } + + return count; + } + +} diff --git a/rust/1822-sign-of-the-product-of-an-array.rs b/rust/1822-sign-of-the-product-of-an-array.rs new file mode 100644 index 000000000..a94a12ed8 --- /dev/null +++ b/rust/1822-sign-of-the-product-of-an-array.rs @@ -0,0 +1,19 @@ +impl Solution { + pub fn array_sign(nums: Vec) -> i32 { + let mut neg = 0; + + for n in nums { + if n == 0 { + return 0; + } else if n < 0 { + neg += 1; + } + } + + if neg % 2 == 0 { + return 1; + } + + -1 + } +} diff --git a/rust/1834-single-threaded-cpu.rs b/rust/1834-single-threaded-cpu.rs new file mode 100644 index 000000000..6b144e6e9 --- /dev/null +++ b/rust/1834-single-threaded-cpu.rs @@ -0,0 +1,35 @@ +use std::cmp::Reverse; +use std::collections::BinaryHeap; + +impl Solution { + pub fn get_order(tasks: Vec>) -> Vec { + let mut tasks = tasks; + + for (i, t) in tasks.iter_mut().enumerate() { + t.push(i as i32); + } + tasks.sort_by(|a, b| a[0].cmp(&b[0])); + + let mut res = vec![]; + let mut min_heap = BinaryHeap::new(); + let (mut i, mut time) = (0, tasks[0][0]); + + while !min_heap.is_empty() || i < tasks.len() { + while i < tasks.len() && time >= tasks[i][0] { + min_heap.push(Reverse(vec![tasks[i][1], tasks[i][2]])); + i += 1; + } + + if min_heap.is_empty() { + time = tasks[i][0]; + } else { + let pair = min_heap.pop().unwrap(); + let process_time = pair.0[0]; + let index = pair.0[1]; + time += process_time; + res.push(index); + } + } + res + } +} \ No newline at end of file diff --git a/rust/1838-frequency-of-the-most-frequent-element.rs b/rust/1838-frequency-of-the-most-frequent-element.rs new file mode 100644 index 000000000..b21111759 --- /dev/null +++ b/rust/1838-frequency-of-the-most-frequent-element.rs @@ -0,0 +1,21 @@ +impl Solution { + pub fn max_frequency(mut nums: Vec, k: i32) -> i32 { + let (mut left, mut right, mut res, mut total) = (0, 0, 0, 0); + + nums.sort_unstable(); + + while right < nums.len() { + total += nums[right]; + + while (nums[right] * (right - left + 1) as i32) - total > k { + total -= nums[left]; + left += 1; + } + + res = res.max(right - left + 1); + right += 1; + } + + res as i32 + } +} diff --git a/rust/1857-largest-color-value-in-a-directed-graph.rs b/rust/1857-largest-color-value-in-a-directed-graph.rs new file mode 100644 index 000000000..9829a97f6 --- /dev/null +++ b/rust/1857-largest-color-value-in-a-directed-graph.rs @@ -0,0 +1,51 @@ +use std::collections::VecDeque; + +impl Solution { + pub fn largest_path_value(colors: String, edges: Vec>) -> i32 { + let n = colors.len(); + let mut graph = vec![vec![]; n]; + let mut indegree = vec![0; n]; + + for edge in edges { + let u = edge[0] as usize; + let v = edge[1] as usize; + graph[u].push(v); + indegree[v] += 1; + } + + let mut queue = VecDeque::new(); + for i in 0..n { + if indegree[i] == 0 { + queue.push_back(i); + } + } + + let mut color_count = vec![vec![0; 26]; n]; + let mut visited = 0; + let colors = colors.as_bytes(); + let mut max_color_value = 0; + + while let Some(node) = queue.pop_front() { + visited += 1; + let color_index = (colors[node] - b'a') as usize; + color_count[node][color_index] += 1; + max_color_value = max_color_value.max(color_count[node][color_index]); + + for &neigh in &graph[node] { + for i in 0..26 { + color_count[neigh][i] = color_count[neigh][i].max(color_count[node][i]); + } + indegree[neigh] -= 1; + if indegree[neigh] == 0 { + queue.push_back(neigh); + } + } + } + + if visited != n as i32 { + return -1; + } + + max_color_value + } +} diff --git a/rust/1929-concatenation-of-array.rs b/rust/1929-concatenation-of-array.rs new file mode 100644 index 000000000..c557a11de --- /dev/null +++ b/rust/1929-concatenation-of-array.rs @@ -0,0 +1,13 @@ +impl Solution { + pub fn get_concatenation(nums: Vec) -> Vec { + let n = nums.len(); + let mut ans = vec![0; 2 * n]; + + for i in 0..nums.len() { + ans[i] = nums[i]; + ans[i + n] = nums[i]; + } + + ans + } +} diff --git a/rust/1930-unique-length-3-palindromic-subsequences.rs b/rust/1930-unique-length-3-palindromic-subsequences.rs new file mode 100644 index 000000000..0e8aa4c2d --- /dev/null +++ b/rust/1930-unique-length-3-palindromic-subsequences.rs @@ -0,0 +1,34 @@ +use std::iter::FromIterator; + +impl Solution { + pub fn count_palindromic_subsequence(s: String) -> i32 { + let mut result = 0; + let mut ranges: [(i32, i32); 26] = [(-1, -1); 26]; + + for (i, c) in s.chars().enumerate() { + let ix = Solution::char_to_index(c) as usize; + if ranges[ix].0 == -1 { + ranges[ix].0 = i as i32; + } + if i as i32 > ranges[ix].1 { + ranges[ix].1 = i as i32; + } + } + + for range in ranges { + if range.1 > range.0 { + let mut set: u32 = 0; + for c in s[range.0 as usize + 1..range.1 as usize].chars() { + set |= 1 << Solution::char_to_index(c); + } + result += set.count_ones() as i32; + } + } + + result + } + + pub fn char_to_index(c: char) -> u32 { + c as u32 - 'a' as u32 + } +} \ No newline at end of file diff --git a/rust/1963-minimum-number-of-swaps-to-make-the-string-balanced.rs b/rust/1963-minimum-number-of-swaps-to-make-the-string-balanced.rs new file mode 100644 index 000000000..979e03c3d --- /dev/null +++ b/rust/1963-minimum-number-of-swaps-to-make-the-string-balanced.rs @@ -0,0 +1,18 @@ +impl Solution { + pub fn min_swaps(s: String) -> i32 { + let mut extra_close = 0; + let mut max_close = 0; + + for c in s.chars() { + if c == '[' { + extra_close -= 1; + } else { + extra_close += 1; + } + + max_close = max_close.max(extra_close); + } + + (max_close + 1) / 2 + } +} \ No newline at end of file diff --git a/rust/1984-minimum-difference-between-highest-and-lowest-of-k-scores.rs b/rust/1984-minimum-difference-between-highest-and-lowest-of-k-scores.rs new file mode 100644 index 000000000..e1ad65a51 --- /dev/null +++ b/rust/1984-minimum-difference-between-highest-and-lowest-of-k-scores.rs @@ -0,0 +1,26 @@ +impl Solution { + pub fn minimum_difference(nums: Vec, k: i32) -> i32 { + let mut nums = nums; + let (mut left, mut right) = (0, (k - 1) as usize); + let mut res = i32::MAX; + + nums.sort(); + + while right < nums.len() { + res = res.min(nums[right] - nums[left]); + left += 1; + right += 1; + } + res + } + + // Idiomatic solution + pub fn minimum_difference_idiomatic(nums: Vec, k: i32) -> i32 { + let mut nums = nums; + nums.sort(); + nums.windows(k as usize) + .map(|pair| pair[(k - 1) as usize] - pair[0]) + .min() + .unwrap() + } +} diff --git a/rust/2001-number-of-pairs-of-interchangeable-rectangles.rs b/rust/2001-number-of-pairs-of-interchangeable-rectangles.rs new file mode 100644 index 000000000..a86223ac4 --- /dev/null +++ b/rust/2001-number-of-pairs-of-interchangeable-rectangles.rs @@ -0,0 +1,36 @@ +use std::collections::HashMap; +impl Solution { + pub fn compute_gcd(mut a: i32, mut b: i32) -> i32 { + while a > 0 && b > 0 { + if a > b { + a %= b; + } else { + b %= a; + } + } + if a == 0 { + b + } else { + a + } + } + pub fn interchangeable_rectangles(rectangles: Vec>) -> i64 { + rectangles + .into_iter() + .map(|v| { + let gcd = Solution::compute_gcd(v[0], v[1]); + (v[0] / gcd, v[1] / gcd) + }) + .fold(HashMap::new(), |mut dict, t| { + dict.entry(t).and_modify(|cnt| *cnt += 1).or_insert(1); + dict + }) + .into_iter() + .filter(|(_, v)| *v > 1) + .map(|(_, v)| v) + .fold(0, |mut cnt, v| { + cnt += v * (v - 1) / 2; + cnt + }) + } +} diff --git a/rust/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.rs b/rust/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.rs new file mode 100644 index 000000000..9f3cbc180 --- /dev/null +++ b/rust/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.rs @@ -0,0 +1,55 @@ +impl Solution { + pub fn max_product(s: String) -> i32 { + let s: Vec = s.chars().collect(); + let mut s1 = vec![]; + let mut s2 = vec![]; + let mut res = 0; + + Solution::dfs(&s, &mut s1, &mut s2, &mut res, 0); + + res + } + + fn dfs(s: &[char], s1: &mut Vec, s2: &mut Vec, res: &mut i32, index: usize) { + // Base case + if index == s.len() { + if Solution::is_palindrome(s1) && Solution::is_palindrome(s2) { + let new_max = s1.len() * s2.len(); + *res = std::cmp::max(*res, new_max as i32); + } + return; + } + + // Option 0: Not in S1 nor S2 + Solution::dfs(s, s1, s2, res, index + 1); + + // Option 1: in S1 + s1.push(s[index]); + Solution::dfs(s, s1, s2, res, index + 1); + s1.pop(); + + // Option 2: in S2 + s2.push(s[index]); + Solution::dfs(s, s1, s2, res, index + 1); + s2.pop(); + } + + fn is_palindrome(s: &[char]) -> bool { + if s.len() <= 1 { + return true; + } + + let mut l = 0; + let mut r = s.len() - 1; + + while l < r { + if s[l] != s[r] { + return false; + } + l += 1; + r -= 1; + } + + true + } +} \ No newline at end of file diff --git a/rust/2013-detect-squares.rs b/rust/2013-detect-squares.rs new file mode 100644 index 000000000..2a1d60b9e --- /dev/null +++ b/rust/2013-detect-squares.rs @@ -0,0 +1,35 @@ +use std::collections::HashMap; + +struct DetectSquares { + points: Vec<(i32, i32)>, + counts: HashMap<(i32, i32), i32> +} + +impl DetectSquares { + + fn new() -> Self { + Self { + points: vec![], + counts: HashMap::new() + } + } + + fn add(&mut self, point: Vec) { + let p = (point[0], point[1]); + self.points.push(p); + *self.counts.entry(p).or_default() += 1; + } + + fn count(&self, point: Vec) -> i32 { + let mut res = 0; + let (px, py) = (point[0], point[1]); + for (x, y) in self.points.iter() { + if (py - y).abs() != (px - x).abs() || *x == px || *y == py { + continue; + } + res += self.counts.get(&(*x, py)).unwrap_or(&0) * self.counts.get(&(px,*y)).unwrap_or(&0); + } + + res + } +} diff --git a/rust/2017-grid-game.rs b/rust/2017-grid-game.rs new file mode 100644 index 000000000..81d01e512 --- /dev/null +++ b/rust/2017-grid-game.rs @@ -0,0 +1,19 @@ +impl Solution { + pub fn grid_game(grid: Vec>) -> i64 { + let n = grid[0].len(); + + let mut memo1 = vec![0;n+1]; + let mut memo2 = vec![0;n+1]; + for i in 0..n { + memo1[i+1] = memo1[i] + grid[0][i] as i64; + memo2[i+1] = memo2[i] + grid[1][i] as i64; + } + + let mut result = i64::max_value(); + for i in 0..n { + result = result.min(memo2[i].max(memo1[n] - memo1[i+1])); + } + + result + } +} diff --git a/rust/2114-maximum-number-of-words-found-in-sentences.rs b/rust/2114-maximum-number-of-words-found-in-sentences.rs new file mode 100644 index 000000000..b9d5ce8d4 --- /dev/null +++ b/rust/2114-maximum-number-of-words-found-in-sentences.rs @@ -0,0 +1,12 @@ +impl Solution { + pub fn most_words_found(sentences: Vec) -> i32 { + let mut most = 0; + for sentence in sentences { + let size = sentence.split_whitespace().count(); + if size > most { + most = size; + } + } + return most as i32; + } +} diff --git a/rust/2421-number-of-good-paths.rs b/rust/2421-number-of-good-paths.rs new file mode 100644 index 000000000..4beb736f6 --- /dev/null +++ b/rust/2421-number-of-good-paths.rs @@ -0,0 +1,97 @@ +use std::{cmp::Ordering, collections::HashMap}; + +struct UnionFind { + parent: Vec, + rank: Vec, +} + +impl UnionFind { + fn new(n: usize) -> Self { + UnionFind { + parent: (0..n).collect(), + rank: vec![0; n], + } + } + + fn find(&mut self, mut i: usize) -> usize { + while i != self.parent[i] { + self.parent[i] = self.parent[self.parent[i]]; + i = self.parent[i]; + } + i + } + + fn union(&mut self, a: usize, b: usize) -> bool { + let a_root = self.find(a); + let b_root = self.find(b); + + if a_root == b_root { + return false; + } + match self.rank[a_root].cmp(&self.rank[b_root]) { + Ordering::Less => { + self.parent[a_root] = b_root; + self.rank[b_root] += self.rank[a_root]; + } + _ => { + self.parent[b_root] = a_root; + self.rank[a_root] = self.rank[b_root]; + } + } + true + } +} + +impl Solution { + pub fn number_of_good_paths(vals: Vec, edges: Vec>) -> i32 { + let adj = Self::create_adj_list(&edges); + let val_to_index = Self::create_val_to_index(&vals); + + let mut res = 0; + let mut uf = UnionFind::new(vals.len()); + + let mut keys: Vec = val_to_index.keys().cloned().collect(); + keys.sort(); + + for val in keys { + for i in val_to_index.get(&val).unwrap_or(&vec![]) { + for nei in adj.get(&(*i as i32)).unwrap_or(&vec![]) { + if vals[*nei as usize] <= vals[*i] { + uf.union(*nei as usize, *i); + } + } + } + let mut count = HashMap::new(); + for i in val_to_index.get(&val).unwrap() { + *count.entry(uf.find(*i)).or_insert(0) += 1; + res += count.get(&uf.find(*i)).unwrap(); + } + } + + res + } + + pub fn create_adj_list(edges: &Vec>) -> HashMap> { + let mut adj = HashMap::new(); + + for edge in edges { + let a = edge[0]; + let b = edge[1]; + + adj.entry(a).or_insert(vec![]).push(b); + adj.entry(b).or_insert(vec![]).push(a); + } + + adj + } + + pub fn create_val_to_index(vals: &Vec) -> HashMap> { + let mut val_to_index = HashMap::new(); + + for (i, val) in vals.iter().enumerate() { + val_to_index.entry(*val).or_insert(vec![]).push(i); + } + + val_to_index + } +} \ No newline at end of file diff --git a/rust/2444-count-subarrays-with-fixed-bounds.rs b/rust/2444-count-subarrays-with-fixed-bounds.rs new file mode 100644 index 000000000..a37669d2c --- /dev/null +++ b/rust/2444-count-subarrays-with-fixed-bounds.rs @@ -0,0 +1,24 @@ +impl Solution { + // Time O(n) - Space O(1) + pub fn count_subarrays(nums: Vec, min_k: i32, max_k: i32) -> i64 { + let (mut last_max, mut last_min, mut last_oob) = (-1, -1, -1); + let mut res = 0; + // An auxiliary variable used to cast the index once. + let mut i; + for (idx, num) in nums.iter().enumerate() { + i = idx as i64; + // Update the last seen values with the current value. + if num < &min_k || num > &max_k { + last_oob = i; + } + if num == &min_k { + last_min = i; + } + if num == &max_k { + last_max = i; + } + res += 0.max(last_max.min(last_min) - last_oob); + } + res + } +} diff --git a/rust/2642-design-graph-with-shortest-path-calculator.rs b/rust/2642-design-graph-with-shortest-path-calculator.rs new file mode 100644 index 000000000..7f03f4321 --- /dev/null +++ b/rust/2642-design-graph-with-shortest-path-calculator.rs @@ -0,0 +1,136 @@ +#![allow(dead_code)] + +/* + * @lc app=leetcode id=2642 lang=rust + * + * [2642] Design Graph With Shortest Path Calculator + */ + +// @lc code=start + +use std::{collections::BinaryHeap, fmt::Display}; + +struct GraphNode { + id: usize, + cost: usize, +} + +impl From<&Vec> for GraphNode { + fn from(edge: &Vec) -> Self { + Self { + id: check_positive(edge[1]).unwrap(), + cost: check_positive(edge[2]).unwrap() + } + } +} + +#[derive(Copy, Clone, Eq, PartialEq)] +struct State { + cost: usize, + position: usize, +} + +impl Ord for State { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + other + .cost + .cmp(&self.cost) + .then_with(|| self.position.cmp(&other.position)) + } +} +impl PartialOrd for State { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +struct Graph { + graph: Vec>, +} +#[derive(Debug, Clone)] +struct NegativeError; + +impl Display for NegativeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "input value was negative") + } +} + +fn check_positive(a: i32) -> Result { + if a < 0 { + return Err(NegativeError) + } else { + Ok(a as usize) + } +} + +impl Graph { + fn new(n: i32, edges: Vec>) -> Self { + let n = check_positive(n).unwrap(); + // Vec with n items, graph[node_int] = [Edge neighbors {node: other_int, cost: #}] + let mut graph: Vec> = Vec::with_capacity(n as usize); + for _ in 0..n { + graph.push(Vec::new()); + } + for edge in edges.iter() { + match edge.len() { + 3 => { + graph[edge[0] as usize].push(GraphNode::from(edge)); + } + _ => panic!( + "Invalid input format, edge must have format [to, from, cost] {:?}", + edge + ), + } + } + Self { graph } + } + + fn add_edge(&mut self, edge: Vec) { + // Create new Edge, addd to graph[edge[0]] + match edge.len() { + 3 => { + let node = GraphNode::from(&edge); + self.graph[edge[0] as usize].push(node); + } + _ => panic!( + "Invalid input format, edge must have format [to, from, cost] {:?}", + edge + ), + } + } + + fn shortest_path(&mut self, node1: i32, node2: i32) -> i32 { + let start = check_positive(node1).unwrap(); + let end = check_positive(node2).unwrap(); + let mut dist: Vec = vec![usize::MAX; self.graph.len()]; + let mut heap = BinaryHeap::new(); + + dist[start] = 0; + heap.push(State { + cost: 0, + position: start + }); + while let Some(State { cost, position }) = heap.pop() { + if position == end { + return cost as i32; + } + if cost > dist[position] { + continue; + } + for edge in &self.graph[position] { + let next = State {cost: cost + edge.cost, position: edge.id as usize}; + if next.cost < dist[next.position] { + heap.push(next); + dist[next.position] = next.cost; + } + } + } + -1 + } +} + +// @lc code=end +fn main() { + unimplemented!(); +} diff --git a/scala/0001-two-sum.scala b/scala/0001-two-sum.scala new file mode 100644 index 000000000..2ea9c3129 --- /dev/null +++ b/scala/0001-two-sum.scala @@ -0,0 +1,34 @@ +import scala.collection.mutable + +object Solution { + def twoSum(nums: Array[Int], target: Int): Array[Int] = { + val numsWithIndex = nums.zipWithIndex + val targets = numsWithIndex.toMap + + def compliment(v: Int): Option[Int] = targets.get(target - v) + + numsWithIndex + .find { case (v, i) => !compliment(v).forall(_ == i) } + .map { case (v, i) => Array(i, compliment(v).get) } + .orNull + } + + /** + * Optimization of above solution to do it in "one pass" + * i.e. build the map as we traverse the input collection + */ + def twoSumOnePass(nums: Array[Int], target: Int): Array[Int] = { + val targets = mutable.Map[Int, Int]() + + for ((v, i) <- nums.zipWithIndex) { + val answer = targets.get(target - v).map(Array(_, i)) + + if (answer.nonEmpty) + return answer.get + + targets.put(v, i) + } + + null + } +} \ No newline at end of file diff --git a/scala/0002-add-two-numbers.scala b/scala/0002-add-two-numbers.scala new file mode 100644 index 000000000..6a3d4de75 --- /dev/null +++ b/scala/0002-add-two-numbers.scala @@ -0,0 +1,42 @@ +/** + * Definition for singly-linked list. + * class ListNode(_x: Int = 0, _next: ListNode = null) { + * var next: ListNode = _next + * var x: Int = _x + * } + */ +object Solution { + def addTwoNumbers(l1: ListNode, l2: ListNode): ListNode = { + def nonNull(l: ListNode): Boolean = l != null + def next(l: ListNode): ListNode = if (nonNull(l)) l.next else null + def getX(l: ListNode): Int = if (nonNull(l)) l.x else 0 + def sumX(l1: ListNode, l2: ListNode, carry: Int): (ListNode, Int) = { + val sum = getX(l1) + getX(l2) + carry + new ListNode(sum % 10) -> sum / 10 + } + + var carry = 0 + val (sum, newCarry) = sumX(l1, l2, carry) + carry = newCarry + + var nextDigit = sum + var next1 = next(l1) + var next2 = next(l2) + + while (nonNull(next1) || nonNull(next2)) { + val (sum, newCarry) = sumX(next1, next2, carry) + + nextDigit.next = sum + nextDigit = nextDigit.next + + carry = newCarry + next1 = next(next1) + next2 = next(next2) + } + + if (carry > 0) + nextDigit.next = new ListNode(carry) + + sum + } +} diff --git a/scala/0003-longest-substring-without-repeating-characters.scala b/scala/0003-longest-substring-without-repeating-characters.scala new file mode 100644 index 000000000..ba7ec453e --- /dev/null +++ b/scala/0003-longest-substring-without-repeating-characters.scala @@ -0,0 +1,24 @@ +import scala.collection.mutable + +object Solution { + def lengthOfLongestSubstring(s: String): Int = { + val charMap = mutable.Map[Character, Int]() + var longest = 0 + var left = 0 + var right = left + + while (right < s.length) { + if (charMap.get(s(right)).exists(_ >= left)) { + longest = Math.max(longest, right - left) + + left += 1 + } + else { + charMap.put(s(right), right) + right += 1 + } + } + + Math.max(longest, right - left) + } +} \ No newline at end of file diff --git a/scala/0007-reverse-integer.scala b/scala/0007-reverse-integer.scala new file mode 100644 index 000000000..c2e052555 --- /dev/null +++ b/scala/0007-reverse-integer.scala @@ -0,0 +1,27 @@ +object Solution { + def reverse(x: Int): Int = { + if (!isValidInteger(x)) return 0 + if (x < 0) -reverseInteger(-x) else reverseInteger(x) + } + + def reverseInteger(x: Int): Int = { + var result: Long = 0 + var temp = x + + while (temp != 0) { + result *= 10 + result += temp % 10 + if (!isValidInteger(result)) return 0 + temp /= 10 + } + + result.toInt + } + + def isValidInteger(x: Long): Boolean = { + if (x > Int.MaxValue || x < Int.MinValue) { + return false + } + true + } +} diff --git a/scala/0011-container-with-most-water.scala b/scala/0011-container-with-most-water.scala new file mode 100644 index 000000000..17b85c53b --- /dev/null +++ b/scala/0011-container-with-most-water.scala @@ -0,0 +1,28 @@ +// Time Complexity: O(n) +// Space Complexity: O(1) + +object Solution { + def maxArea(height: Array[Int]): Int = { + var maxWater = 0 + var left = 0 + var right = height.length - 1 + + while (left < right) { + var minHeight = height(left).min(height(right)) + var dist = right - left + var capacity = minHeight * dist + if (capacity > maxWater) { + maxWater = capacity + } + + if (height(left) < height(right)){ + left += 1 + } + else { + right -= 1 + } + } + + return maxWater + } +} diff --git a/scala/0015-3sum.scala b/scala/0015-3sum.scala new file mode 100644 index 000000000..fe4087a04 --- /dev/null +++ b/scala/0015-3sum.scala @@ -0,0 +1,36 @@ +import scala.collection.mutable.ListBuffer + +object Solution { + def threeSum(nums: Array[Int]): List[List[Int]] = { + val res = ListBuffer[List[Int]]() + val sortedNums = nums.sorted + val endIdx = sortedNums.length - 1 + + for (i <- 0 to endIdx) { + val curr = sortedNums(i) + // To prevent duplicate triplet, but allow the triplet to contain same numbers + val shouldSkip = i > 0 && curr == sortedNums(i - 1) + if (!shouldSkip) { + var (s, e) = (i + 1, endIdx) + val target = 0 - curr + + while (s < e) { + val sum = sortedNums(s) + sortedNums(e) + if (sum == target) { + res += List(curr, sortedNums(s), sortedNums(e)) + e -= 1 + while (e > 0 && sortedNums(e) == sortedNums(e + 1)) { + e -= 1 + } + } else if (sum < target) { + s += 1 + } else { + e -= 1 + } + } + } + } + + res.toList + } +} \ No newline at end of file diff --git a/scala/0021-merge-two-sorted-lists.scala b/scala/0021-merge-two-sorted-lists.scala new file mode 100644 index 000000000..8354a2834 --- /dev/null +++ b/scala/0021-merge-two-sorted-lists.scala @@ -0,0 +1,34 @@ +/** + * Definition for singly-linked list. + * class ListNode(_x: Int = 0, _next: ListNode = null) { + * var next: ListNode = _next + * var x: Int = _x + * } + */ +object Solution { + def mergeTwoLists(list1: ListNode, list2: ListNode): ListNode = { + val head = new ListNode() + var tail = head + var (ptr1, ptr2) = (list1, list2) + + while (ptr1 != null && ptr2 != null) { + if (ptr1.x <= ptr2.x) { + tail.next = ptr1 + ptr1 = ptr1.next + } else { + tail.next = ptr2 + ptr2 = ptr2.next + } + + tail = tail.next + } + + if (ptr1 != null) { + tail.next = ptr1 + } else if (ptr2 != null) { + tail.next = ptr2 + } + + return head.next + } +} \ No newline at end of file diff --git a/scala/0033-search-in-rotated-sorted-array.scala b/scala/0033-search-in-rotated-sorted-array.scala new file mode 100644 index 000000000..1d9b0a06b --- /dev/null +++ b/scala/0033-search-in-rotated-sorted-array.scala @@ -0,0 +1,27 @@ +object Solution { + def search(nums: Array[Int], target: Int): Int = { + var (left, right) = (0, nums.length - 1) + + while (left <= right) { + val mid = (left + right) / 2 + + if (target == nums(mid)) { + return mid + } else if (nums(mid) >= nums(left)) { + if (target >= nums(left) && target < nums(mid)) { + right = mid - 1 + } else { + left = mid + 1 + } + } else { + if (target > nums(mid) && target <= nums(right)) { + left = mid + 1 + } else { + right = mid - 1 + } + } + } + + return -1 + } +} \ No newline at end of file diff --git a/scala/0036-valid-sudoku.scala b/scala/0036-valid-sudoku.scala new file mode 100644 index 000000000..494c2e2e9 --- /dev/null +++ b/scala/0036-valid-sudoku.scala @@ -0,0 +1,32 @@ +object Solution { + def isValidSudoku(board: Array[Array[Char]]): Boolean = { + import scala.collection.mutable.{Set => MSet} + val rowSet = MSet[(Int, Char)]() + val colSet = MSet[(Int, Char)]() + val squareSet = MSet[(Int, Int, Char)]() + + val indices = for { + i <- (0 to 8).toList + j <- (0 to 8).toList + } yield (i, j) + + indices + .map { case (i, j) => + (i, j, board(i)(j)) + } + .filter(_._3 != '.') + .forall { case (i, j, char) => + if( + rowSet.contains((i, char)) + | colSet.contains((j, char)) + | squareSet.contains((i / 3, j / 3, char)) + ) false + else { + rowSet += ((i, char)) + colSet += ((j, char)) + squareSet += ((i / 3, j / 3, char)) + true + } + } + } +} diff --git a/scala/0049-group-anagrams.scala b/scala/0049-group-anagrams.scala new file mode 100644 index 000000000..df5a05c5e --- /dev/null +++ b/scala/0049-group-anagrams.scala @@ -0,0 +1,30 @@ +import scala.collection.mutable.{Map => MMap} +object Solution { + def groupAnagrams(strs: Array[String]): List[List[String]] = { + val resultMap = MMap[MMap[Char, Int], List[String]]() + strs + .map(str => (str, letterCountMap(str))) + .foreach { tupled => + tupled match { + case (str, aMap) => { + resultMap.updateWith(aMap) { + case Some(listOfStrings) => Some(listOfStrings :+ str) + case None => Some(List(str)) + } + } + } + } + resultMap.values.toList + } + + def letterCountMap(str: String): MMap[Char, Int] = { + val map = MMap[Char, Int]() + str.foreach{ char => + map.updateWith(char){ + case Some(value) => Some(value + 1) + case None => Some(1) + } + } + map + } +} diff --git a/scala/0056-merge-intervals.scala b/scala/0056-merge-intervals.scala new file mode 100644 index 000000000..50794a6a4 --- /dev/null +++ b/scala/0056-merge-intervals.scala @@ -0,0 +1,23 @@ +import scala.collection.mutable.ArrayBuffer + +object Solution { + def merge(intervals: Array[Array[Int]]): Array[Array[Int]] = { + val (start, end) = (0, 1) + val sortedIntervals = intervals.sortBy(_(start)) + val res = ArrayBuffer[Array[Int]](sortedIntervals(0)) + + for (i <- 1 until sortedIntervals.length) { + val currEndIdx = res.size - 1 + val currEnd = res(currEndIdx) + val curr = sortedIntervals(i) + + if (currEnd(end) < curr(start)) { + res += curr + } else { + res(currEndIdx)(end) = currEnd(end).max(curr(end)) + } + } + + return res.toArray + } +} \ No newline at end of file diff --git a/scala/0058-length-of-last-word.scala b/scala/0058-length-of-last-word.scala new file mode 100644 index 000000000..5384c5be3 --- /dev/null +++ b/scala/0058-length-of-last-word.scala @@ -0,0 +1,18 @@ +object Solution { + def lengthOfLastWord(s: String): Int = { + var i = s.length - 1 + var length = 0 + + while (s(i) == ' ') { + i -= 1 + } + + while (i >= 0 && s(i) != ' ') { + length += 1 + i -= 1 + } + + length + + } +} diff --git a/scala/0070-climbing-stairs.scala b/scala/0070-climbing-stairs.scala new file mode 100644 index 000000000..82477296f --- /dev/null +++ b/scala/0070-climbing-stairs.scala @@ -0,0 +1,12 @@ +object Solution { + def climbStairs(n: Int): Int = { + var one = 1 + var two = 1 + for (i <- 0 until n - 1) { + val temp = one + one = one + two + two = temp + } + one + } +} diff --git a/scala/0072-edit-distance.scala b/scala/0072-edit-distance.scala new file mode 100644 index 000000000..9baea9424 --- /dev/null +++ b/scala/0072-edit-distance.scala @@ -0,0 +1,16 @@ +object Solution { + def minDistance(word1: String, word2: String): Int = { + var minDisArr = Array.ofDim[Int](word1.size+2, word2.size+2) + for(i<- 0 to word1.size; j<- 0 to word2.size){ + if(i == 0 || j == 0) minDisArr(i)(j) = i + j + + else if (word1(i-1).equals(word2(j-1))) + minDisArr(i)(j) = minDisArr(i-1)(j-1) + + else + minDisArr(i)(j) = 1 + (minDisArr(i - 1)(j - 1) min minDisArr(i)(j - 1) min minDisArr(i - 1)(j)) + + } + minDisArr(word1.size)(word2.size) + } +} \ No newline at end of file diff --git a/scala/0091-decode-ways.scala b/scala/0091-decode-ways.scala new file mode 100644 index 000000000..9a8aa741d --- /dev/null +++ b/scala/0091-decode-ways.scala @@ -0,0 +1,24 @@ +object Solution { + def numDecodings(s: String): Int = { + var (next, nextTwo) = (0, 1) + if (s(s.length - 1) != '0') { + next = 1 + } + + for (i <- s.length - 2 to 0 by -1) { + val tmp = next + if (s(i) != '0') { + val isTwoDigitsValid = (s(i) == '1' && s(i+1).asDigit <= 9) || + (s(i) == '2' && s(i+1).asDigit <= 6) + if (isTwoDigitsValid) { + next = next + nextTwo + } + } else { + next = 0 + } + nextTwo = tmp + } + + return next + } +} \ No newline at end of file diff --git a/scala/0097-interleaving-string.scala b/scala/0097-interleaving-string.scala new file mode 100644 index 000000000..235e7af5a --- /dev/null +++ b/scala/0097-interleaving-string.scala @@ -0,0 +1,21 @@ +object Solution { + def isInterleave(s1: String, s2: String, s3: String): Boolean = { + val c1 = s1.toCharArray() + val c2 = s2.toCharArray() + val c3 = s3.toCharArray() + val m = s1.length() + val n = s2.length() + if(m + n != s3.length()) false; + else dfs(c1, c2, c3, 0, 0, 0, Array.ofDim[Boolean](m+1, n+1)) + } + + def dfs(c1: Array[Char], c2: Array[Char], c3: Array[Char], i:Int, j:Int, k:Int, invalid: Array[Array[Boolean]]): Boolean = { + if(invalid(i)(j)) return false + if(k == c3.length) return true + val valid = + i < c1.length && c1(i) == c3(k) && dfs(c1, c2, c3, i + 1, j, k + 1, invalid) || + j < c2.length && c2(j) == c3(k) && dfs(c1, c2, c3, i, j + 1, k + 1, invalid) + if(!valid) invalid(i)(j) = true + valid + } +} diff --git a/scala/0121-best-time-to-buy-and-sell-stock.scala b/scala/0121-best-time-to-buy-and-sell-stock.scala new file mode 100644 index 000000000..5d40ed472 --- /dev/null +++ b/scala/0121-best-time-to-buy-and-sell-stock.scala @@ -0,0 +1,18 @@ +object Solution { + def maxProfit(prices: Array[Int]): Int = { + var left = 0 + var right = 1 + var maxProfit = 0 + while (right < prices.length) { + val priceDiff = prices(right) - prices(left) + if (priceDiff > 0) { + maxProfit = maxProfit max priceDiff + } + else { + left = right + } + right += 1 + } + maxProfit + } +} diff --git a/scala/0125-valid-palindrome.scala b/scala/0125-valid-palindrome.scala new file mode 100644 index 000000000..dec8c14a6 --- /dev/null +++ b/scala/0125-valid-palindrome.scala @@ -0,0 +1,11 @@ +object Solution { + def isPalindrome(s: String): Boolean = { + val smallCased = s.toLowerCase + // removing all non-alphanumeric characters + val notAlphaNumericRegex = """[\W_]""".r + val toCompareWith = notAlphaNumericRegex.replaceAllIn(smallCased, "") + + val reversed = toCompareWith.reverse + toCompareWith == reversed + } +} diff --git a/scala/0141-linked-list-cycle.scala b/scala/0141-linked-list-cycle.scala new file mode 100644 index 000000000..04d895985 --- /dev/null +++ b/scala/0141-linked-list-cycle.scala @@ -0,0 +1,24 @@ +/** + * Definition for singly-linked list. + * class ListNode(var _x: Int = 0) { + * var next: ListNode = null + * var x: Int = _x + * } + */ + +object Solution { + def hasCycle(head: ListNode): Boolean = { + var (slowPtr, fastPtr) = (head, head) + + while (fastPtr != null && fastPtr.next != null) { + slowPtr = slowPtr.next + fastPtr = fastPtr.next.next + + if (slowPtr eq fastPtr) { + return true + } + } + + return false + } +} \ No newline at end of file diff --git a/scala/0153-find-minimum-in-rotated-sorted-array.scala b/scala/0153-find-minimum-in-rotated-sorted-array.scala new file mode 100644 index 000000000..8d529ecb2 --- /dev/null +++ b/scala/0153-find-minimum-in-rotated-sorted-array.scala @@ -0,0 +1,24 @@ +object Solution { + def findMin(nums: Array[Int]): Int = { + var res = nums(0) + var (left, right) = (0, nums.length - 1) + + while (left <= right) { + if (nums(left) < nums(right)) { + res = res.min(nums(left)) + left = right + 1 // break condition + } else { + val mid = (left + right) / 2 + res = res.min(nums(mid)) + + if (nums(mid) >= nums(left)) { + left = mid + 1 + } else { + right = mid - 1 + } + } + } + + return res + } +} diff --git a/scala/0167-two-sum-ii-input-array-is-sorted.scala b/scala/0167-two-sum-ii-input-array-is-sorted.scala new file mode 100644 index 000000000..e46212e9f --- /dev/null +++ b/scala/0167-two-sum-ii-input-array-is-sorted.scala @@ -0,0 +1,10 @@ +object Solution { + def twoSum(numbers: Array[Int], target: Int): Array[Int] = { + def loop(p1: Int, p2: Int): Array[Int] = { + if(numbers(p1) + numbers(p2) == target) Array(p1 + 1, p2 + 1) + else if(numbers(p1) + numbers(p2) > target) loop(p1, p2 - 1) + else loop(p1 + 1, p2) + } + loop(0, numbers.length - 1) + } +} diff --git a/scala/0179-largest-number.scala b/scala/0179-largest-number.scala new file mode 100644 index 000000000..687270068 --- /dev/null +++ b/scala/0179-largest-number.scala @@ -0,0 +1,5 @@ +object Solution { + def largestNumber(nums: Array[Int]): String = { + BigInt(nums.map(_.toString).sortWith((l, r) => l ++ r >= r ++ l).foldLeft("")(_ + _)).toString + } +} \ No newline at end of file diff --git a/scala/0198-house-robber.scala b/scala/0198-house-robber.scala new file mode 100644 index 000000000..3ca01af33 --- /dev/null +++ b/scala/0198-house-robber.scala @@ -0,0 +1,13 @@ +object Solution { + def rob(nums: Array[Int]): Int = { + var (prevTwoMaxProfit, prevMaxProfit) = (0, 0) + + for (currCash <- nums) { + val currProfit = (currCash + prevTwoMaxProfit).max(prevMaxProfit) + prevTwoMaxProfit = prevMaxProfit + prevMaxProfit = currProfit + } + + return prevMaxProfit + } +} \ No newline at end of file diff --git a/scala/0206-reverse-linked-list.scala b/scala/0206-reverse-linked-list.scala new file mode 100644 index 000000000..b53ce3d1a --- /dev/null +++ b/scala/0206-reverse-linked-list.scala @@ -0,0 +1,20 @@ +/** + * Definition for singly-linked list. + * class ListNode(_x: Int = 0, _next: ListNode = null) { + * var next: ListNode = _next + * var x: Int = _x + * } + */ +object Solution { + def reverseList(head: ListNode): ListNode = { + if (head == null || head.next == null) { + return head + } + + val reversedListHead = reverseList(head.next) + head.next.next = head + head.next = null + + return reversedListHead + } +} \ No newline at end of file diff --git a/scala/0208-implement-trie-prefix-tree.scala b/scala/0208-implement-trie-prefix-tree.scala new file mode 100644 index 000000000..b3e728c30 --- /dev/null +++ b/scala/0208-implement-trie-prefix-tree.scala @@ -0,0 +1,55 @@ +import scala.collection.mutable.HashMap + +class Trie() { + class TrieNode { + val children: HashMap[Char, TrieNode] = HashMap() + var isEndOfWord = false + } + + private var root: TrieNode = new TrieNode() + + def insert(word: String): Unit = { + var curr = root + word.foreach(c => { + if (!curr.children.contains(c)) { + curr.children(c) = new TrieNode() + } + + curr = curr.children(c) + }) + + curr.isEndOfWord = true + } + + def search(word: String): Boolean = { + traverse(word) match { + case Some(endNode) => endNode.isEndOfWord + case None => false + } + } + + def startsWith(prefix: String): Boolean = { + traverse(prefix).isDefined + } + + private def traverse(word: String): Option[TrieNode] = { + var curr = root + for (c <- word) { + if (!curr.children.contains(c)) { + return None + } else { + curr = curr.children(c) + } + } + + return Some(curr) + } +} + +/** + * Your Trie object will be instantiated and called as such: + * var obj = new Trie() + * obj.insert(word) + * var param_2 = obj.search(word) + * var param_3 = obj.startsWith(prefix) + */ \ No newline at end of file diff --git a/scala/0211-design-add-and-search-words-data-structure.scala b/scala/0211-design-add-and-search-words-data-structure.scala new file mode 100644 index 000000000..ae8693548 --- /dev/null +++ b/scala/0211-design-add-and-search-words-data-structure.scala @@ -0,0 +1,70 @@ +import scala.collection.mutable.HashMap + +class WordDictionary() { + val trie = new Trie() + + def addWord(word: String) { + trie.insert(word) + } + + def search(word: String): Boolean = { + trie.search(word) + } + +} + +class Trie() { + class TrieNode { + val children: HashMap[Char, TrieNode] = HashMap() + var isEndOfWord = false + } + + private var root: TrieNode = new TrieNode() + + def insert(word: String): Unit = { + var curr = root + word.foreach(c => { + if (!curr.children.contains(c)) { + curr.children(c) = new TrieNode() + } + + curr = curr.children(c) + }) + + curr.isEndOfWord = true + } + + def search(word: String): Boolean = { + def helper(currNode: TrieNode, currCharIndex: Int): Boolean = { + if (currCharIndex == word.length) { + return currNode.isEndOfWord + } + + val currChar = word(currCharIndex) + if (currChar == '.') { + var isFound = false + + for ((_, childNode) <- currNode.children if !isFound) { + isFound = helper(childNode, currCharIndex + 1) + } + + return isFound + } else { + if (currNode.children.contains(currChar)) { + return helper(currNode.children(currChar), currCharIndex + 1) + } else { + return false + } + } + } + + helper(root, 0) + } +} + +/** + * Your WordDictionary object will be instantiated and called as such: + * var obj = new WordDictionary() + * obj.addWord(word) + * var param_2 = obj.search(word) + */ \ No newline at end of file diff --git a/scala/0213-house-robber-ii.scala b/scala/0213-house-robber-ii.scala new file mode 100644 index 000000000..aa9a62623 --- /dev/null +++ b/scala/0213-house-robber-ii.scala @@ -0,0 +1,21 @@ +object Solution { + def rob(nums: Array[Int]): Int = { + if (nums.length == 1) { + return nums(0) + } else { + return houseRobber1(nums, 0, nums.length - 2).max(houseRobber1(nums, 1, nums.length - 1)) + } + } + + def houseRobber1(nums: Array[Int], start: Int, end: Int): Int = { + var (prevMaxProfit, prevTwoMaxProfit) = (0, 0) + + for (i <- start to end) { + val profit = (nums(i) + prevTwoMaxProfit).max(prevMaxProfit) + prevTwoMaxProfit = prevMaxProfit + prevMaxProfit = profit + } + + return prevMaxProfit + } +} \ No newline at end of file diff --git a/scala/0215-kth-largest-element-in-an-array.scala b/scala/0215-kth-largest-element-in-an-array.scala new file mode 100644 index 000000000..b2ba28095 --- /dev/null +++ b/scala/0215-kth-largest-element-in-an-array.scala @@ -0,0 +1,15 @@ +import scala.collection.mutable.PriorityQueue + +object Solution { + def findKthLargest(nums: Array[Int], k: Int): Int = { + val pq = PriorityQueue.empty(Ordering[Int].reverse) + for(i <- 0 until nums.size){ + if (i < k) pq.enqueue(nums(i)) + else { + pq.enqueue(nums(i)) + pq.dequeue() + } + } + pq.dequeue() + } +} diff --git a/scala/0217-contains-duplicate.scala b/scala/0217-contains-duplicate.scala new file mode 100644 index 000000000..91f04d82a --- /dev/null +++ b/scala/0217-contains-duplicate.scala @@ -0,0 +1,12 @@ +import scala.collection.mutable.HashSet + +object Solution { + def containsDuplicate(nums: Array[Int]): Boolean = { + var seen: HashSet[Int] = HashSet() + for (num <- nums) { + if (seen.contains(num)) return true + seen.add(num) + } + return false + } +} \ No newline at end of file diff --git a/scala/0230-kth-smallest-element-in-a-bst.scala b/scala/0230-kth-smallest-element-in-a-bst.scala new file mode 100644 index 000000000..ea6ce7487 --- /dev/null +++ b/scala/0230-kth-smallest-element-in-a-bst.scala @@ -0,0 +1,36 @@ +/** + * Definition for a binary tree node. + * class TreeNode(_value: Int = 0, _left: TreeNode = null, _right: TreeNode = null) { + * var value: Int = _value + * var left: TreeNode = _left + * var right: TreeNode = _right + * } + */ +object Solution { + def kthSmallest(root: TreeNode, k: Int): Int = { + helper(root, k)._2 + } + + // Recursive in-order traversal. + def helper(root: TreeNode, k: Int): (Boolean, Int, Int) = { + if (root == null) { + return (false, 0, 0) + } + + val (isInLeft, lVal, lSize) = helper(root.left, k) + if (isInLeft) { + return (true, lVal, 0) + } else { + if (k - lSize == 1) { + return (true, root.value, 0) + } else { + val (isInRight, rVal, rSize) = helper(root.right, k - (lSize + 1)) + if (isInRight) { + return (true, rVal, 0) + } else { + return (false, 0, lSize + rSize + 1) + } + } + } + } +} \ No newline at end of file diff --git a/scala/0235-lowest-common-ancestor-of-a-binary-search-tree.scala b/scala/0235-lowest-common-ancestor-of-a-binary-search-tree.scala new file mode 100644 index 000000000..bbaf82c44 --- /dev/null +++ b/scala/0235-lowest-common-ancestor-of-a-binary-search-tree.scala @@ -0,0 +1,23 @@ +/** + * Definition for a binary tree node. + * class TreeNode(var _value: Int) { + * var value: Int = _value + * var left: TreeNode = null + * var right: TreeNode = null + * } + */ + +import annotation.tailrec + +object Solution { + @tailrec + def lowestCommonAncestor(root: TreeNode, p: TreeNode, q: TreeNode): TreeNode = { + if (p.value < root.value && q.value < root.value) { + lowestCommonAncestor(root.left, p, q) + } else if (p.value > root.value && q.value > root.value) { + lowestCommonAncestor(root.right, p, q) + } else { + root + } + } +} \ No newline at end of file diff --git a/scala/0242-valid-anagram.scala b/scala/0242-valid-anagram.scala new file mode 100644 index 000000000..a4c23d6e2 --- /dev/null +++ b/scala/0242-valid-anagram.scala @@ -0,0 +1,33 @@ +// Time Complexity: O(s + t) +// Space Comeplexity: O(s + t) + +import scala.collection.mutable.Map + +object Solution { + def isAnagram(s: String, t: String): Boolean = { + if (s.length() != t.length()) + return false + + var charCount = Map[Char, Int]() + + for (c <- s) { + if (charCount.contains(c)) + charCount(c) = charCount(c) + 1 + else + charCount += (c -> 1) + } + + for (c <- t) { + if (charCount.contains(c)) + charCount(c) = charCount(c) - 1 + else + charCount += (c -> 1) + } + + for ((_, v) <- charCount) + if (v != 0) + return false + + return true + } +} diff --git a/scala/0322-coin-change.scala b/scala/0322-coin-change.scala new file mode 100644 index 000000000..800511336 --- /dev/null +++ b/scala/0322-coin-change.scala @@ -0,0 +1,17 @@ +object Solution { + def coinChange(coins: Array[Int], amount: Int): Int = { + var dp: Array[Int] = List.fill[Int](amount+5)(amount+1).toArray + dp(0) = 0 + for(i <- 1 to amount) + { + for(j <- 0 until coins.size) + { + if(coins(j) <= i) + { + dp(i) = dp(i) min (dp(i - coins(j)) + 1) + } + } + } + if(dp(amount) > amount ) -1 else dp(amount); + } +} \ No newline at end of file diff --git a/scala/0347-top-k-frequent-elements.scala b/scala/0347-top-k-frequent-elements.scala new file mode 100644 index 000000000..1e09a3456 --- /dev/null +++ b/scala/0347-top-k-frequent-elements.scala @@ -0,0 +1,19 @@ +import scala.collection.mutable.{HashMap, PriorityQueue, ListBuffer, ArrayBuffer} + +object Solution { + def topKFrequent(nums: Array[Int], k: Int): Array[Int] = { + val map = getNumCountMap(nums) + val arr = Array.fill(nums.length + 1)(ListBuffer[Int]()) + map.foreach{case (num, count) => arr(count) += num} + arr.reverseIterator.flatten.take(k).toArray + } + + def getNumCountMap(nums: Array[Int]): HashMap[Int, Int] = { + val map = HashMap[Int, Int]() + nums.foreach(num => { + map(num) = map.getOrElse(num, 0) + 1 + }) + + map + } +} \ No newline at end of file diff --git a/scala/0394-decode-string.scala b/scala/0394-decode-string.scala new file mode 100644 index 000000000..06f2a1291 --- /dev/null +++ b/scala/0394-decode-string.scala @@ -0,0 +1,29 @@ +object Solution { + + def decodeString(s: String): String = { + val (str, res) = decodeStringRecursive(s, 0) + str + } + + def decodeStringRecursive(input: String, start: Int): (String, Int) = { + val result = new StringBuilder() + var i = start + while(i < input.length){ + input(i) match { + case x if x>= '0' && x<='9' => + val currNum = input.substring(i, input.indexOf('[', i)).toInt + val (str, maxI) = decodeStringRecursive(input, input.indexOf('[', i)+1) + i = maxI + result.append(str * currNum) + case x if x >= 'a' && x <= 'z' => + (result.append(x), i) + case ']' => + return (result.toString, i) + case _ => + } + i+=1 + } + (result.toString, i) + } + +} \ No newline at end of file diff --git a/scala/0435-non-overlapping-intervals.scala b/scala/0435-non-overlapping-intervals.scala new file mode 100644 index 000000000..acc33481a --- /dev/null +++ b/scala/0435-non-overlapping-intervals.scala @@ -0,0 +1,22 @@ +object Solution { + def eraseOverlapIntervals(intervals: Array[Array[Int]]): Int = { + val (start, end) = (0, 1) + val sortedIntervals = intervals.sortBy(_(0)) + + var numRemovals = 0 + var currEnd = sortedIntervals(0)(end) + + for (i <- 1 until sortedIntervals.length) { + val currInterval = sortedIntervals(i) + + if (currInterval(start) < currEnd) { + numRemovals += 1 + currEnd = currEnd.min(currInterval(end)) + } else { + currEnd = currInterval(end) + } + } + + numRemovals + } +} \ No newline at end of file diff --git a/scala/0485-max-consecutive-ones.scala b/scala/0485-max-consecutive-ones.scala new file mode 100644 index 000000000..907c961fa --- /dev/null +++ b/scala/0485-max-consecutive-ones.scala @@ -0,0 +1,4 @@ +object Solution { + def findMaxConsecutiveOnes(nums: Array[Int]): Int = + nums.scanLeft(0)((m, x) => if (x == 0) 0 else m + 1).max +} \ No newline at end of file diff --git a/scala/0509-fibonacci-number.scala b/scala/0509-fibonacci-number.scala new file mode 100644 index 000000000..95d9e00d4 --- /dev/null +++ b/scala/0509-fibonacci-number.scala @@ -0,0 +1,17 @@ +object Solution { + var gen: Boolean = false; + var fibonacci = List(0,1) + def fib(n: Int): Int = { + generate + fibonacci(n) + } + + def generate = { + if(!gen){ + gen = true + List.range(2, 31).map(x => + fibonacci = fibonacci :+ fibonacci(x-1) + fibonacci(x-2) + ) + } + } +} diff --git a/scala/0572-subtree-of-another-tree.scala b/scala/0572-subtree-of-another-tree.scala new file mode 100644 index 000000000..333c36b8b --- /dev/null +++ b/scala/0572-subtree-of-another-tree.scala @@ -0,0 +1,33 @@ +/** + * Definition for a binary tree node. + * class TreeNode(_value: Int = 0, _left: TreeNode = null, _right: TreeNode = null) { + * var value: Int = _value + * var left: TreeNode = _left + * var right: TreeNode = _right + * } + */ +object Solution { + def isSubtree(root: TreeNode, subRoot: TreeNode): Boolean = { + if (root == null && subRoot == null) { + return true + } else if (root != null && subRoot == null) { + return true + } else if (root == null && subRoot != null) { + return false + } else { + return isSameTree(root, subRoot) || (isSubtree(root.left, subRoot) || isSubtree(root.right, subRoot)) + } + } + + def isSameTree(p: TreeNode, q: TreeNode): Boolean = { + if (p == null && q == null) { + return true + } else if (p == null || q == null) { + return false + } else if (p.value != q.value) { + return false + } else { + return isSameTree(p.left, q.left) && isSameTree(p.right, q.right) + } + } +} \ No newline at end of file diff --git a/scala/0665-non-decreasing-array.scala b/scala/0665-non-decreasing-array.scala new file mode 100644 index 000000000..6cd84db70 --- /dev/null +++ b/scala/0665-non-decreasing-array.scala @@ -0,0 +1,16 @@ +object Solution { + def checkPossibility(nums: Array[Int]): Boolean = { + @scala.annotation.tailrec + def helper(i: Int = 1, mismatch: Int = 0, last: Int = nums.head): Boolean = + if (i == nums.length) true + else if (nums(i) >= last) helper(i + 1, mismatch, nums(i)) + else if (mismatch > 0) false + else if (i == nums.length - 1) true + else if (nums(i - 1) <= nums(i + 1)) helper(i + 1, mismatch + 1, nums(i)) + else if (i == 1 && nums(i) <= nums(i + 1)) helper(i + 1, mismatch + 1, nums(i)) + else if (i > 1 && nums(i - 2) <= nums(i)) helper(i + 1, mismatch + 1, nums(i)) + else false + + helper() + } +} diff --git a/scala/0704-binary-search.scala b/scala/0704-binary-search.scala new file mode 100644 index 000000000..ae51675d8 --- /dev/null +++ b/scala/0704-binary-search.scala @@ -0,0 +1,20 @@ +object Solution { + def search(nums: Array[Int], target: Int): Int = { + var left = 0 + var right = nums.length - 1 + + while (left <= right) { + val mid = left + (right - left) / 2 + + if (nums(mid) == target) { + return mid + } else if (nums(mid) < target) { + left = mid + 1 + } else { + right = mid - 1 + } + } + + return -1 + } +} diff --git a/scala/0746-min-cost-climbing-stairs.scala b/scala/0746-min-cost-climbing-stairs.scala new file mode 100644 index 000000000..4a573e037 --- /dev/null +++ b/scala/0746-min-cost-climbing-stairs.scala @@ -0,0 +1,17 @@ +object Solution { + def minCostClimbingStairs(cost: Array[Int]): Int = { + val n = cost.length + var first = cost(0) + var second = cost(1) + if (n<=2) + first min second + else { + for ( i <- 2 until n) { + val curr = cost(i) + (first min second) + first = second + second = curr + } + first min second + } + } +} \ No newline at end of file diff --git a/scala/1929-concatenation-of-array.scala b/scala/1929-concatenation-of-array.scala new file mode 100644 index 000000000..f32dfd812 --- /dev/null +++ b/scala/1929-concatenation-of-array.scala @@ -0,0 +1,13 @@ +object Solution { + def getConcatenation(nums: Array[Int]): Array[Int] = { + val n = nums.length + val ans = new Array[Int](2 * n) + + for (i <- 0 until n) { + ans(i) = nums(i) + ans(i + n) = nums(i) + } + + ans + } +} diff --git a/swift/0001-two-sum.swift b/swift/0001-two-sum.swift new file mode 100644 index 000000000..ab3a65250 --- /dev/null +++ b/swift/0001-two-sum.swift @@ -0,0 +1,18 @@ +/** + * Question Link: https://leetcode.com/problems/two-sum/ + */ + +class TwoSum { + func twoSum(_ nums: [Int], _ target: Int) -> [Int] { + var prevMap = [Int:Int]() // val -> index + + for (i, n) in nums.enumerated() { + let diff = target - n + if let firstIndex = prevMap[diff] { + return [firstIndex, i] + } + prevMap[n] = i + } + return [] + } +} diff --git a/swift/0002-add-two-numbers.swift b/swift/0002-add-two-numbers.swift new file mode 100644 index 000000000..5b0faeb57 --- /dev/null +++ b/swift/0002-add-two-numbers.swift @@ -0,0 +1,42 @@ +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func addTwoNumbers(_ l1: ListNode?, _ l2: ListNode?) -> ListNode? { + var itr1 = l1 + var itr2 = l2 + var carry: Int = 0 + var sum: Int = 0 + + var dummy = ListNode(0) + var head: ListNode? = dummy + + while itr1 != nil || itr2 != nil { + let val1 = itr1?.val ?? 0 + let val2 = itr2?.val ?? 0 + + sum = val1 + val2 + carry + carry = sum/10 + sum = sum%10 + + head?.next = ListNode(sum) + head = head?.next + + itr1 = itr1?.next + itr2 = itr2?.next + } + + if carry > 0 { + head?.next = ListNode(carry) + } + + return dummy.next + } +} \ No newline at end of file diff --git a/swift/0003-longest-substring-without-repeating-characters.swift b/swift/0003-longest-substring-without-repeating-characters.swift new file mode 100644 index 000000000..c175eac8d --- /dev/null +++ b/swift/0003-longest-substring-without-repeating-characters.swift @@ -0,0 +1,19 @@ +class Solution { + func lengthOfLongestSubstring(_ s: String) -> Int { + guard s.count > 0 else { return 0 } + let list = Array(s) + var hs = Set() + var l = 0 + var r = 0 + var mv = 0 + for r in 0.. Double { + if nums1.count > nums2.count { + return findMedianSortedArrays(nums2, nums1) + } + + var m = nums1.count + var n = nums2.count + var l = 0 + var r = m + + while l <= r { + var pa = (l + r) / 2 + var pb = (m + n + 1) / 2 - pa + + var maxLeftA = pa == 0 ? Int.min : nums1[pa - 1] + var minRightA = pa == m ? Int.max : nums1[pa] + var maxLeftB = pb == 0 ? Int.min : nums2[pb - 1] + var minRightB = pb == n ? Int.max : nums2[pb] + + if maxLeftA <= minRightB && maxLeftB <= minRightA { + if (m + n) % 2 == 0 { + return Double((max(maxLeftA, maxLeftB) + min(minRightA, minRightB))) / 2.0 + } else { + return Double(max(maxLeftA, maxLeftB)) + } + } else if maxLeftA > minRightB { + r = pa - 1 + } else { + l = pa + 1 + } + } + + return 0.0 + } +} \ No newline at end of file diff --git a/swift/0005-longest-palindromic-substring.swift b/swift/0005-longest-palindromic-substring.swift new file mode 100644 index 000000000..459f4352f --- /dev/null +++ b/swift/0005-longest-palindromic-substring.swift @@ -0,0 +1,37 @@ +/** + * Question Link: https://leetcode.com/problems/longest-palindromic-substring/ + */ + + class Solution { + func longestPalindrome(_ s: String) -> String { + let s = Array(s) + var res = "" + var len = 0 + + for i in 0..= 0 && r < s.count && s[l] == s[r] { + if r - l + 1 > len { + res = String(s[l...r]) + len = r - l + 1 + } + l -= 1 + r += 1 + } + + l = i + r = i + 1 + while l >= 0 && r < s.count && s[l] == s[r] { + if r - l + 1 > len { + res = String(s[l...r]) + len = r - l + 1 + } + l -= 1 + r += 1 + } + } + + return res + } +} \ No newline at end of file diff --git a/swift/0007-reverse-integer.swift b/swift/0007-reverse-integer.swift new file mode 100644 index 000000000..52644303c --- /dev/null +++ b/swift/0007-reverse-integer.swift @@ -0,0 +1,22 @@ +class Solution { + func reverse(_ x: Int) -> Int { + let MIN = Int32.min + let MAX = Int32.max + + var res = 0 + var value = x + while value != 0 { + let digit = value % 10 + value /= 10 + + if res > MAX / 10 || (res == MAX / 10 && digit >= MAX % 10) { + return 0 + } + if res < MIN / 10 || (res == MIN / 10 && digit <= MIN % 10) { + return 0 + } + res = (res * 10) + digit + } + return res + } +} \ No newline at end of file diff --git a/swift/0009-palindrome-number.swift b/swift/0009-palindrome-number.swift new file mode 100644 index 000000000..3ead4dc92 --- /dev/null +++ b/swift/0009-palindrome-number.swift @@ -0,0 +1,28 @@ +// Time: O(n) +// Space: O(1) +class Solution { + func isPalindrome(_ x: Int) -> Bool { + if x < 0 { + return false + } + + var div = 1 + while x >= 10 * div { + div *= 10 + } + + var val = x + while val > 0 { + let right = val % 10 + let left = val / div + + if left != right { + return false + } + + val = (val % div) / 10 + div /= 100 + } + return true + } +} \ No newline at end of file diff --git a/swift/0010-regular-expression-matching.swift b/swift/0010-regular-expression-matching.swift new file mode 100644 index 000000000..baed11533 --- /dev/null +++ b/swift/0010-regular-expression-matching.swift @@ -0,0 +1,31 @@ +class Solution { + func isMatch(_ s: String, _ p: String) -> Bool { + let s = Array(s), p = Array(p) + var dp = [[Int]: Bool]() + + func dfs(_ i: Int, _ j: Int) -> Bool { + if dp[[i, j]] != nil { + return dp[[i, j]]! + } + if i >= s.count && j >= p.count { + return true + } + if j >= p.count { + return false + } + let isMatch = i < s.count && (s[i] == p[j] || p[j] == ".") + if j + 1 < p.count && p[j + 1] == "*" { + dp[[i, j]] = dfs(i, j + 2) || (isMatch && dfs(i + 1, j)) + return dp[[i, j]]! + } + if isMatch { + dp[[i, j]] = dfs(i + 1, j + 1) + return dp[[i, j]]! + } + dp[[i, j]] = false + return false + } + + return dfs(0, 0) + } +} \ No newline at end of file diff --git a/swift/0011-container-with-most-water.swift b/swift/0011-container-with-most-water.swift new file mode 100644 index 000000000..b1a5102fd --- /dev/null +++ b/swift/0011-container-with-most-water.swift @@ -0,0 +1,16 @@ +class Solution { + func maxArea(_ height: [Int]) -> Int { + var maxArea = 0 + var l = 0 + var r = height.count - 1 + while l < r { + let lV = height[l] + let rV = height[r] + let area = (r - l) * min(rV, lV) + maxArea = max(area, maxArea) + l += lV < rV ? 1 : 0 + r -= lV >= rV ? 1 : 0 + } + return maxArea + } +} diff --git a/swift/0014-longest-common-prefix.swift b/swift/0014-longest-common-prefix.swift new file mode 100644 index 000000000..26e9d6283 --- /dev/null +++ b/swift/0014-longest-common-prefix.swift @@ -0,0 +1,15 @@ +class Solution { + func longestCommonPrefix(_ strs: [String]) -> String { + var newArray = strs.sorted() + var string = "" + + for (char1, char2) in zip(newArray.first!, newArray.last!) { + if (char1 == char2) { + string.append(char1); + } else { + break + } + } + return string + } +} \ No newline at end of file diff --git a/swift/0015-3sum.swift b/swift/0015-3sum.swift new file mode 100644 index 000000000..0cc279b47 --- /dev/null +++ b/swift/0015-3sum.swift @@ -0,0 +1,26 @@ +class Solution { + func threeSum(_ nums: [Int]) -> [[Int]] { + let sortedArray = nums.sorted() + var result: [[Int]] = [] + for i in 0.. 0 && sortedArray[i] == sortedArray[i - 1] { continue } + var l = i + 1 + var r = sortedArray.count - 1 + while l < r { + var threeSum = sortedArray[i] + sortedArray[l] + sortedArray[r] + if threeSum > 0 { + r -= 1 + } else if threeSum < 0 { + l += 1 + } else { + result.append([sortedArray[i], sortedArray[l], sortedArray[r]]) + l += 1 + while sortedArray[l] == sortedArray[l - 1] && l < r { + l += 1 + } + } + } + } + return result + } +} diff --git a/swift/0017-letter-combinations-of-a-phone-number.swift b/swift/0017-letter-combinations-of-a-phone-number.swift new file mode 100644 index 000000000..606c59668 --- /dev/null +++ b/swift/0017-letter-combinations-of-a-phone-number.swift @@ -0,0 +1,31 @@ +class Solution { + private let numberLetters: [Character: String] = [ + "2": "abc", + "3": "def", + "4": "ghi", + "5": "jkl", + "6": "mno", + "7": "pqrs", + "8": "tuv", + "9": "wxyz" + ] + func letterCombinations(_ digits: String) -> [String] { + guard !digits.isEmpty else { return [] } + var result: [String] = [] + var digits = digits.map { $0 } + func generateCombinations(_ index: Int, _ path: String) { + guard index < digits.count else { + result.append(path) + return + } + let digit = digits[index] + if let letters = numberLetters[digit] { + for letter in letters { + generateCombinations(index + 1, path + "\(letter)") + } + } + } + generateCombinations(0, "") + return result + } +} \ No newline at end of file diff --git a/swift/0018-4sum.swift b/swift/0018-4sum.swift new file mode 100644 index 000000000..f0527eeb7 --- /dev/null +++ b/swift/0018-4sum.swift @@ -0,0 +1,49 @@ +class Solution { + func fourSum(_ nums: [Int], _ target: Int) -> [[Int]] { + if nums.count < 4 { + return [] + } + + var nums = nums.sorted() + var res = [[Int]]() + var quad = [Int]() + + func kSum(_ k: Int, _ start: Int, _ target: Int) { + if k != 2 { + for i in start.. start && nums[i] == nums[i - 1] { + continue + } + quad.append(nums[i]) + kSum(k - 1, i + 1, target - nums[i]) + quad.popLast() + } + return + } else { + twoSum(start, target) + return + } + } + + func twoSum(_ start: Int, _ target: Int) { + var l = start + var r = nums.count - 1 + while l < r { + if nums[l] + nums[r] < target { + l += 1 + } else if nums[l] + nums[r] > target { + r -= 1 + } else { + res.append(quad + [nums[l], nums[r]]) + repeat { + l += 1 + } while l < r && nums[l] == nums[l - 1] + } + } + } + + kSum(4, 0, target) + + return res + } +} \ No newline at end of file diff --git a/swift/0019-remove-nth-node-from-end-of-list.swift b/swift/0019-remove-nth-node-from-end-of-list.swift new file mode 100644 index 000000000..e065cbb99 --- /dev/null +++ b/swift/0019-remove-nth-node-from-end-of-list.swift @@ -0,0 +1,31 @@ +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func removeNthFromEnd(_ head: ListNode?, _ n: Int) -> ListNode? { + if head === nil { + return head + } + var slow = head, fast = head + for i in 1...n + 1 { + if (slow === nil) { + return head?.next + } + slow = slow?.next + } + + while slow != nil { + slow = slow?.next + fast = fast?.next + } + fast?.next = fast?.next?.next + return head + } +} \ No newline at end of file diff --git a/swift/0020-valid-parentheses.swift b/swift/0020-valid-parentheses.swift new file mode 100644 index 000000000..f7c8c4006 --- /dev/null +++ b/swift/0020-valid-parentheses.swift @@ -0,0 +1,47 @@ +class Solution { + func isValid(_ s: String) -> Bool { + let parenMap:[Character:Character] = ["(":")", "[":"]", "{":"}"] + var stack = Stack() + for c in s { + if parenMap.keys.contains(c) { + stack.push(c) + } else if stack.isEmpty || c != parenMap[stack.pop()!] { + return false + } + } + return stack.isEmpty + } +} + +class Node { + var data: T + var next: Node? + init(_ value: T) { + self.data = value + } +} + +class Stack { + + var head: Node? + + var isEmpty: Bool { + head == nil + } + + func peak() -> Node? { + head + } + + func push(_ data: T) { + var node = Node(data) + node.next = head + head = node + } + + func pop() -> T? { + var data = head?.data + head = head?.next + return data + } +} diff --git a/swift/0021-merge-two-sorted-lists.swift b/swift/0021-merge-two-sorted-lists.swift new file mode 100644 index 000000000..2be871dd7 --- /dev/null +++ b/swift/0021-merge-two-sorted-lists.swift @@ -0,0 +1,53 @@ +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func mergeTwoLists( + _ list1: ListNode?, + _ list2: ListNode? + ) -> ListNode? { + var dummyNode: ListNode? = ListNode(-10000) + var resultNode = dummyNode + + var itr1 = list1 + var itr2 = list2 + + while itr1 != nil && itr2 != nil { + let v1 = itr1?.val ?? Int.max + let v2 = itr2?.val ?? Int.max + + if v1 < v2 { + resultNode?.next = itr1 + itr1 = itr1?.next + } else { + resultNode?.next = itr2 + itr2 = itr2?.next + } + + resultNode = resultNode?.next + } + + // itr1 is available only + while itr1 != nil { + resultNode?.next = itr1 + resultNode = resultNode?.next + itr1 = itr1?.next + } + + // itr2 is available only + while itr2 != nil { + resultNode?.next = itr2 + resultNode = resultNode?.next + itr2 = itr2?.next + } + + return dummyNode?.next + } +} \ No newline at end of file diff --git a/swift/0022-generate-parentheses.swift b/swift/0022-generate-parentheses.swift new file mode 100644 index 000000000..8191749a3 --- /dev/null +++ b/swift/0022-generate-parentheses.swift @@ -0,0 +1,22 @@ +// Time: O(2^n) +// Space: O(n) +class Solution { + func generateParenthesis(_ n: Int) -> [String] { + var result: [String] = [] + generate(n, 0, 0, "", &result) + return result + } + + func generate(_ n: Int, _ open: Int, _ close: Int, _ str: String, _ result: inout [String]) { + if open == n && close == n { + result.append(str) + return + } + if open < n { + generate(n, open + 1, close, str + "(", &result) + } + if open > close { + generate(n, open, close + 1, str + ")", &result) + } + } +} \ No newline at end of file diff --git a/swift/0023-merge-k-sorted-lists.swift b/swift/0023-merge-k-sorted-lists.swift new file mode 100644 index 000000000..caabd827c --- /dev/null +++ b/swift/0023-merge-k-sorted-lists.swift @@ -0,0 +1,62 @@ +/** + * Question Link: https://leetcode.com/problems/merge-k-sorted-lists/ + */ + + /** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ + +class MergeKLists { + func mergeKLists(_ lists: [ListNode?]) -> ListNode? { + guard lists.contains(where: { $0 != nil }) else { return nil } + + var lists = lists + + while lists.count > 1 { + var mergedLists = [ListNode?]() + + for i in stride(from: 0, to: lists.count, by: 2) { + let l1 = lists[i] + let l2 = (i + 1 < lists.count) ? lists[i + 1] : nil + mergedLists.append(self.merge(l1: l1, l2: l2)) + } + lists = mergedLists + } + + return lists.first! + } + + func merge(l1: ListNode?, l2: ListNode?) -> ListNode? { + var dummy = ListNode() + var tail: ListNode? = dummy + var l1 = l1 + var l2 = l2 + + while l1 != nil && l2 != nil { + if l1!.val < l2!.val { + tail?.next = l1 + l1 = l1?.next + } else { + tail?.next = l2! + l2 = l2?.next + } + tail = tail?.next + } + + if l1 != nil { + tail?.next = l1 + } + if l2 != nil { + tail?.next = l2 + } + + return dummy.next + } +} \ No newline at end of file diff --git a/swift/0025-reverse-nodes-in-k-group.swift b/swift/0025-reverse-nodes-in-k-group.swift new file mode 100644 index 000000000..75323f65b --- /dev/null +++ b/swift/0025-reverse-nodes-in-k-group.swift @@ -0,0 +1,57 @@ +/** + * Question Link: https://leetcode.com/problems/reverse-nodes-in-k-group/ + */ + +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ + +class ReverseKGroup { + func reverseKGroup(_ head: ListNode?, _ k: Int) -> ListNode? { + var dummy = ListNode(0, head) + var groupPrev: ListNode? = dummy + + while true { + let kth = getKth(current: groupPrev, k: k) + if kth == nil { + break + } + + let groupNext = kth?.next + + var prev = kth?.next + var current = groupPrev?.next + while current !== groupNext { + let temp = current?.next + current?.next = prev + prev = current + current = temp + } + + let temp = groupPrev?.next + groupPrev?.next = kth + groupPrev = temp + } + + return dummy.next + } + + func getKth(current: ListNode?, k: Int) -> ListNode? { + var current = current + var k = k + + while current != nil && k > 0 { + current = current?.next + k -= 1 + } + + return current + } +} \ No newline at end of file diff --git a/swift/0026-Remove-Duplicates-from-Sorted-Array.swift b/swift/0026-Remove-Duplicates-from-Sorted-Array.swift new file mode 100644 index 000000000..65db1029f --- /dev/null +++ b/swift/0026-Remove-Duplicates-from-Sorted-Array.swift @@ -0,0 +1,14 @@ +class Solution { + func removeDuplicates(_ nums: inout [Int]) -> Int { + var left: Int = 0 + + for right in 1.. Int { + var left: Int = 0 + + for right in 1.. Int { + var count: Int = 0 + + for i in nums where i != val { + nums[count] = i + count += 1 + } + + return count + } +} diff --git a/swift/0027-remove-element.swift b/swift/0027-remove-element.swift new file mode 100644 index 000000000..124234bf0 --- /dev/null +++ b/swift/0027-remove-element.swift @@ -0,0 +1,12 @@ +class Solution { + func removeElement(_ nums: inout [Int], _ val: Int) -> Int { + var count: Int = 0 + + for i in nums where i != val { + nums[count] = i + count += 1 + } + + return count + } +} diff --git a/swift/0033-search-in-rotated-sorted-array.swift b/swift/0033-search-in-rotated-sorted-array.swift new file mode 100644 index 000000000..a0e368597 --- /dev/null +++ b/swift/0033-search-in-rotated-sorted-array.swift @@ -0,0 +1,27 @@ +//Link: https://leetcode.com/problems/search-in-rotated-sorted-array/ +class Solution { + func search(_ nums: [Int], _ target: Int) -> Int { + var lo = 0 + var hi = nums.count - 1 + + while lo <= hi { + let mid = lo + (hi-lo)/2 + if nums[mid] == target { + return mid + } else if nums[mid] <= nums[hi] { + if target > nums[mid] && target <= nums[hi] { + lo = mid+1 + } else { + hi = mid-1 + } + } else if nums[lo] <= nums[mid] { + if target >= nums[lo] && target < nums[mid] { + hi = mid-1 + } else { + lo = mid+1 + } + } + } + return -1 + } +} diff --git a/swift/0034-find-first-and-last-position-of-element-in-sorted-array.swift b/swift/0034-find-first-and-last-position-of-element-in-sorted-array.swift new file mode 100644 index 000000000..0d0c254e5 --- /dev/null +++ b/swift/0034-find-first-and-last-position-of-element-in-sorted-array.swift @@ -0,0 +1,31 @@ +class Solution { + func searchRange(_ nums: [Int], _ target: Int) -> [Int] { + let left = binSearch(nums, target, true) + let right = binSearch(nums, target, false) + return [left, right] + } + + func binSearch(_ nums: [Int], _ target: Int, _ leftBias: Bool) -> Int { + var left = 0, right = nums.count - 1 + var i = -1 + while left <= right { + let mid = (left + right) / 2 + if target > nums[mid] { + left = mid + 1 + } + else if target < nums[mid] { + right = mid - 1 + } + else { + i = mid + if leftBias { + right = mid - 1 + } + else { + left = mid + 1 + } + } + } + return i + } +} \ No newline at end of file diff --git a/swift/0035-search-insert-position.swift b/swift/0035-search-insert-position.swift new file mode 100644 index 000000000..bde960827 --- /dev/null +++ b/swift/0035-search-insert-position.swift @@ -0,0 +1,34 @@ +class Solution { + func isValidSudoku(_ board: [[Character]]) -> Bool { + var rows: [Set] = [] + var cols: [Set] = [] + var subBoxes: [[Set]] = [] + for i in 1...9 { + rows.append(Set()) + cols.append(Set()) + } + for i in 1...3 { + var row: [Set] = [] + for j in 1...3 { + row.append(Set()) + } + subBoxes.append(row) + + } + for i in 0...8 { + for j in 0...8 { + let val: Character = board[i][j] + if (val == ".") { + continue + } + if (rows[i].contains(val) || cols[j].contains(val) || subBoxes[i/3][j/3].contains(val)){ + return false + } + rows[i].insert(val) + cols[j].insert(val) + subBoxes[i/3][j/3].insert(val) + } + } + return true + } +} diff --git a/swift/0039-combination-sum.swift b/swift/0039-combination-sum.swift new file mode 100644 index 000000000..53ac5ade5 --- /dev/null +++ b/swift/0039-combination-sum.swift @@ -0,0 +1,23 @@ +class Solution { + func combinationSum(_ candidates: [Int], _ target: Int) -> [[Int]] { + var result: [[Int]] = [] + var currentComb: [Int] = [] + func dfs(_ index: Int, _ remaining: Int) { + guard index < candidates.count else { return } + guard remaining >= 0 else { + return + } + if remaining == 0 { + result.append(currentComb) + return + } + for i in index.. [[Int]] { + var candidates = candidates.sorted() + var res = [[Int]]() + var cur = [Int]() + var total = 0 + + func dfs(_ i: Int) { + var i = i + if total == target { + res.append(cur) + return + } + if total > target || i == candidates.count { + return + } + + cur.append(candidates[i]) + total += candidates[i] + dfs(i + 1) + cur.popLast() + total -= candidates[i] + while i + 1 < candidates.count && candidates[i] == candidates[i + 1] { + i += 1 + } + dfs(i + 1) + } + + dfs(0) + + return res + } +} \ No newline at end of file diff --git a/swift/0042-trapping-rain-water.swift b/swift/0042-trapping-rain-water.swift new file mode 100644 index 000000000..8fd569e30 --- /dev/null +++ b/swift/0042-trapping-rain-water.swift @@ -0,0 +1,30 @@ +class Solution { + func trap(_ height: [Int]) -> Int { + if height == nil { + return 0 + } + + var res = 0 + var l = 0 + var r = height.count - 1 + + var leftMax = height[l] + var rightMax = height[r] + + while l < r { + if leftMax < rightMax { + l += 1 + leftMax = max(leftMax, height[l]) + res += leftMax - height[l] + } else { + r -= 1 + rightMax = max(rightMax, height[r]) + res += rightMax - height[r] + } + } + + return res + + } + +} diff --git a/swift/0043-multiply-strings.swift b/swift/0043-multiply-strings.swift new file mode 100644 index 000000000..6e21bdb30 --- /dev/null +++ b/swift/0043-multiply-strings.swift @@ -0,0 +1,30 @@ +class Solution { + func multiply(_ num1: String, _ num2: String) -> String { + if [num1, num2].contains("0") { + return "0" + } + + var res = Array(repeating: 0, count: num1.count + num2.count) + + let n1 = Array(num1.reversed()) + let n2 = Array(num2.reversed()) + + for i1 in 0.. Int { + var left = 0, right = 0 + var res = 0 + + while right < nums.count - 1 { + var maxJump = 0 + for i in left...right { + maxJump = max(maxJump, i + nums[i]) + } + left = right + 1 + right = maxJump + res += 1 + } + return res + } +} \ No newline at end of file diff --git a/swift/0046-permutations.swift b/swift/0046-permutations.swift new file mode 100644 index 000000000..0a413d9b7 --- /dev/null +++ b/swift/0046-permutations.swift @@ -0,0 +1,42 @@ +class Solution { + var ans: [[Int]] = [] + + func permute(_ nums: [Int]) -> [[Int]] { + var referenceNums = nums + dfs(start: 0, currentOrder: &referenceNums) + return ans + } +} + +private extension Solution { + private func swap(_ arr: inout [Int], at i1: Int, with i2: Int) { + guard + i1 < arr.count, + i2 < arr.count + else { return } + + let temp = arr[i1] + arr[i1] = arr[i2] + arr[i2] = temp + } + + func dfs(start: Int, currentOrder nums: inout [Int]) { + // base case [take into final answer] + if start == nums.count { + ans.append(nums) + return + } + + // for each idx, take, switch with start, backtrack, switch back + for i in start.. [[Int]] { + var res = [[Int]]() + var perm = [Int]() + var count = [Int: Int]() + for n in nums { + count[n, default: 0] += 1 + } + func dfs() { + if perm.count == nums.count { + res.append(perm) + return + } + for n in count.keys { + if count[n]! > 0 { + perm.append(n) + count[n, default: 0] -= 1 + dfs() + count[n, default: 0] += 1 + perm.popLast() + } + } + } + dfs() + return res + } +} \ No newline at end of file diff --git a/swift/0048-rotate-image.swift b/swift/0048-rotate-image.swift new file mode 100644 index 000000000..2434a34fe --- /dev/null +++ b/swift/0048-rotate-image.swift @@ -0,0 +1,28 @@ +class Solution { + func rotate(_ matrix: inout [[Int]]) { + var left = 0, right = matrix.count - 1 + + while left < right { + for i in 0...right-left-1 { + let top = left, bottom = right + + // save the topLeft + let topLeft = matrix[top][left + i] + + // move bottom left into top left + matrix[top][left + i] = matrix[bottom - i][left] + + // move bottom right into bottom left + matrix[bottom - i][left] = matrix[bottom][right - i] + + // move top right into bottom right + matrix[bottom][right - i] = matrix[top + i][right] + + // move top left into top right + matrix[top + i][right] = topLeft + } + right -= 1 + left += 1 + } + } +} \ No newline at end of file diff --git a/swift/0049-group-anagrams.swift b/swift/0049-group-anagrams.swift new file mode 100644 index 000000000..f66092109 --- /dev/null +++ b/swift/0049-group-anagrams.swift @@ -0,0 +1,19 @@ +/** + * Question Link: https://leetcode.com/problems/anagrams/ + */ + +class GroupAnagrams { + func groupAnagrams(_ strs: [String]) -> [[String]] { + var ans = [[Int]:[String]]() + let a = "a" as Character + + for s in strs { + var count = [Int](repeating: 0, count: 26) + for c in Array(s) { + count[Int(c.asciiValue! - a.asciiValue!)] += 1 + } + ans[count, default: []].append(s) + } + return Array(ans.values) + } +} diff --git a/swift/0050-powx-n.swift b/swift/0050-powx-n.swift new file mode 100644 index 000000000..da82a85de --- /dev/null +++ b/swift/0050-powx-n.swift @@ -0,0 +1,20 @@ +class Solution { + func myPow(_ x: Double, _ n: Int) -> Double { + func helper(_ x: Double, _ n: Int) -> Double { + if x == 0 { + return 0 + } + + if n == 0 { + return 1 + } + + var res = helper(x, n/2) + res = res * res + return n % 2 == 1 ? x*res : res + } + + var res = helper(x, abs(n)) + return n >= 0 ? res : 1/res + } +} diff --git a/swift/0051-n-queens.swift b/swift/0051-n-queens.swift new file mode 100644 index 000000000..469cc5dd6 --- /dev/null +++ b/swift/0051-n-queens.swift @@ -0,0 +1,37 @@ +class Solution { + func solveNQueens(_ n: Int) -> [[String]] { + var res = [[String]]() + var col = Set() + var posDiag = Set() + var negDiag = Set() + var cur = Array(repeating: Array(repeating: ".", count: n), count: n) + + func dfs(_ r: Int) { + if r == n { + let copy = cur.map { $0.joined() } + print(copy) + res.append(copy) + return + } + + for c in 0.. Int { + var res = nums[0] + var total = 0 + + for n in nums { + total += n + res = max(res, total) + if total < 0 { + total = 0 + } + } + return res + } +} \ No newline at end of file diff --git a/swift/0054-spiral-matrix.swift b/swift/0054-spiral-matrix.swift new file mode 100644 index 000000000..a0abbba97 --- /dev/null +++ b/swift/0054-spiral-matrix.swift @@ -0,0 +1,35 @@ +/** + * Question Link: https://leetcode.com/problems/spiral-matrix/ + */ + + class Solution { + func spiralOrder(_ matrix: [[Int]]) -> [Int] { + var res = [Int]() + var l = 0 + var r = matrix[0].count + var t = 0 + var b = matrix.count + while l < r && t < b { + for i in l.. Bool { + var goal = nums.count - 1 + + for i in stride(from: nums.count-2, through: 0, by: -1){ + if i + nums[i] >= goal { + goal = i + } + } + return goal == 0 + } +} \ No newline at end of file diff --git a/swift/0056-merge-intervals.swift b/swift/0056-merge-intervals.swift new file mode 100644 index 000000000..d69bbeee8 --- /dev/null +++ b/swift/0056-merge-intervals.swift @@ -0,0 +1,24 @@ +/** + * Question Link: https://leetcode.com/problems/merge-intervals/ + */ + + class Solution { + func merge(_ intervals: [[Int]]) -> [[Int]] { + var intervals = intervals.sorted { $0[0] < $1[0] } + var res = [intervals[0]] + + for interval in intervals { + let start = interval[0] + let end = interval[1] + let lastEnd = res[res.count - 1][1] + + if start <= lastEnd { + res[res.count - 1][1] = max(lastEnd, end) + } else { + res.append([start, end]) + } + } + + return res + } +} \ No newline at end of file diff --git a/swift/0057-insert-interval.swift b/swift/0057-insert-interval.swift new file mode 100644 index 000000000..2e6351298 --- /dev/null +++ b/swift/0057-insert-interval.swift @@ -0,0 +1,28 @@ +class Solution { + func insert(_ intervals: [[Int]], _ newInterval: [Int]) -> [[Int]] { + var res : [[Int]] = [] + var i = 0 + let n = intervals.count + + // parameters are immutable in Swift + var currInterval = newInterval + + while i < n && intervals[i][1] < newInterval[0] { + res.append(intervals[i]) + i += 1 + } + + while i < n && intervals[i][0] <= currInterval[1] { + currInterval[0] = min(currInterval[0], intervals[i][0]) + currInterval[1] = max(currInterval[1], intervals[i][1]) + i += 1 + } + res.append(currInterval) + + while i < n { + res.append(intervals[i]) + i += 1 + } + return res + } +} \ No newline at end of file diff --git a/swift/0058-Length-of-Last-Word.swift b/swift/0058-Length-of-Last-Word.swift new file mode 100644 index 000000000..325fe0e77 --- /dev/null +++ b/swift/0058-Length-of-Last-Word.swift @@ -0,0 +1,15 @@ +class Solution { + func lengthOfLastWord(_ s: String) -> Int { + var length: Int = 0 + + for c in s.reversed() { + if c != " " { + length += 1 + } else if (length > 0){ + break; + } + } + + return length + } +} diff --git a/swift/0058-length-of-last-word.swift b/swift/0058-length-of-last-word.swift new file mode 100644 index 000000000..325fe0e77 --- /dev/null +++ b/swift/0058-length-of-last-word.swift @@ -0,0 +1,15 @@ +class Solution { + func lengthOfLastWord(_ s: String) -> Int { + var length: Int = 0 + + for c in s.reversed() { + if c != " " { + length += 1 + } else if (length > 0){ + break; + } + } + + return length + } +} diff --git a/swift/0062-unique-paths.swift b/swift/0062-unique-paths.swift new file mode 100644 index 000000000..1d483b2f4 --- /dev/null +++ b/swift/0062-unique-paths.swift @@ -0,0 +1,18 @@ +/** + * Question Link: https://leetcode.com/problems/unique-paths/ + */ + +class Solution { + func uniquePaths(_ m: Int, _ n: Int) -> Int { + var prevRow = [Int](repeating: 0, count: n) + for r in stride(from: m - 1, to: -1, by: -1) { + var curRow = [Int](repeating: 0, count: n) + curRow[n - 1] = 1 + for c in stride(from: n - 2, to: -1, by: -1) { + curRow[c] = curRow[c + 1] + prevRow[c] + } + prevRow = curRow + } + return prevRow[0] + } +} \ No newline at end of file diff --git a/swift/0063-unique-paths-ii.swift b/swift/0063-unique-paths-ii.swift new file mode 100644 index 000000000..fc81f3a4d --- /dev/null +++ b/swift/0063-unique-paths-ii.swift @@ -0,0 +1,23 @@ +/** + * Question Link: https://leetcode.com/problems/unique-paths-ii/ + */ + + class Solution { + func uniquePathsWithObstacles(_ obstacleGrid: [[Int]]) -> Int { + let rows = obstacleGrid.count + let cols = obstacleGrid[0].count + var dp = [Int](repeating: 0, count: cols) + dp[cols - 1] = 1 + + for r in stride(from: rows - 1, to: -1, by: -1) { + for c in stride(from: cols - 1, to: -1, by: -1) { + if obstacleGrid[r][c] == 1 { + dp[c] = 0 + } else if c + 1 < cols { + dp[c] = dp[c] + dp[c + 1] + } + } + } + return dp[0] + } +} \ No newline at end of file diff --git a/swift/0066-plus-one.swift b/swift/0066-plus-one.swift new file mode 100644 index 000000000..20c91d1f3 --- /dev/null +++ b/swift/0066-plus-one.swift @@ -0,0 +1,18 @@ +class Solution { + func plusOne(_ digits: [Int]) -> [Int] { + // parameters are immutable in swift + var digit = digits + for i in stride(from: digit.count - 1, through: 0, by: -1) { + digit[i] += 1 + + if digit[i] <= 9 { + return digit + } + + digit[i] = 0 + } + + digit.insert(1, at: 0) + return digit + } +} \ No newline at end of file diff --git a/swift/0070-climbing-stairs.swift b/swift/0070-climbing-stairs.swift new file mode 100644 index 000000000..163d6c529 --- /dev/null +++ b/swift/0070-climbing-stairs.swift @@ -0,0 +1,16 @@ +class Solution { + func climbStairs(_ n: Int) -> Int { + if n <= 3 { + return n + } + var n1 = 2 + var n2 = 3 + + for i in 4...n { + let temp = n1 + n2 + n1 = n2 + n2 = temp + } + return n2 + } +} \ No newline at end of file diff --git a/swift/0072-edit-distance.swift b/swift/0072-edit-distance.swift new file mode 100644 index 000000000..5dd2b87f9 --- /dev/null +++ b/swift/0072-edit-distance.swift @@ -0,0 +1,45 @@ +class Solution { + func minDistance(_ word1: String, _ word2: String) -> Int { + // No operations needed if strings match + if word1 == word2 { + return 0 + } + + let m = word1.count, n = word2.count + + // if one word has no characters, min operations is length of other word + if m == 0 { + return n + } + if n == 0 { + return m + } + + var prev = 0 + var curr = Array(repeating: 0, count: n + 1) + + // convert the strings into an array + let newWord1 = Array(word1) + let newWord2 = Array(word2) + + for j in 1...n { + curr[j] = j + } + + for i in 1...m { + var prev = curr[0] + curr[0] = i + for j in 1...n { + let temp = curr[j] + if newWord1[i - 1] == newWord2[j - 1] { + curr[j] = prev + } + else { + curr[j] = min(prev, min(curr[j - 1], curr[j])) + 1 + } + prev = temp + } + } + return curr[n] + } +} \ No newline at end of file diff --git a/swift/0073-set-matrix-zeroes.swift b/swift/0073-set-matrix-zeroes.swift new file mode 100644 index 000000000..ec6867aca --- /dev/null +++ b/swift/0073-set-matrix-zeroes.swift @@ -0,0 +1,39 @@ +class Solution { + func setZeroes(_ matrix: inout [[Int]]) { + let rows = matrix.count, cols = matrix[0].count + var rowZero = false + + for r in 0.. 0 { + matrix[r][0] = 0 + } else { + rowZero = true + } + } + } + } + + for r in 1.. Bool { + let ROWS = matrix.count + let COLS = matrix[0].count + + var row = ROWS - 1 + var col = 0 + + while row >= 0 && col < COLS { + let number = matrix[row][col] + + if number == target { + return true + } + + if number > target { + row -= 1 // go up + } + else { + col += 1 // go right + } + } + + return false + } +} diff --git a/swift/0075-sort-colors.swift b/swift/0075-sort-colors.swift new file mode 100644 index 000000000..76f4a6d47 --- /dev/null +++ b/swift/0075-sort-colors.swift @@ -0,0 +1,17 @@ +// Bucket Sort Solution +class Solution { + func sortColors(_ nums: inout [Int]) { + var counts = [0, 0, 0] + for n in nums { + counts[n] += 1 + } + + var i = 0 + for n in 0.. String { + if t.isEmpty { return "" } + + var countT: [Character : Int] = [:] + var window: [Character : Int] = [:] + let stringArray = Array(s) + + // Storing each char and its count in hashMap + for char in t { + countT[char, default: 0] += 1 + } + + var have = 0 + let need = t.count + var resultRange = (-1, -1) + var resultLength = Int.max + var l = 0 + + for r in 0...s.count - 1 { + let char = stringArray[r] + window[char, default: 0] += 1 + + if countT.contains(where: { $0.key == char }) && window[char] == countT[char] { + // for the case of repeated characters "have" should be increased by count, not by 1 + have += countT[char, default: 0] + } + + while have == need { + // update result + if r - l + 1 < resultLength { + resultRange = (l, r) + resultLength = r - l + 1 + } + // pop from left of the window + window[stringArray[l], default: 0] -= 1 + + if countT.contains(where: { $0.key == stringArray[l] }) + && window[stringArray[l], default: 0] < countT[stringArray[l], default: 0] { + //subtract all count, because in second iteration will be added total count of char. + have -= countT[stringArray[l], default: 0] + } + + l += 1 + } + } + + guard resultLength != Int.max else { return "" } + return String(stringArray[resultRange.0...resultRange.1]) + } +} diff --git a/swift/0077-combinations.swift b/swift/0077-combinations.swift new file mode 100644 index 000000000..aec93fc1f --- /dev/null +++ b/swift/0077-combinations.swift @@ -0,0 +1,26 @@ +/** + * Question Link: https://leetcode.com/problems/combinations/ + */ + + class Solution { + func combine(_ n: Int, _ k: Int) -> [[Int]] { + var combs = [[Int]]() + var curComb = [Int]() + func dfs(_ i: Int) { + if curComb.count == k { + combs.append(curComb) + return + } + if i > n { + return + } + for j in i.. [[Int]] { + var res : [[Int]] = [] + var subset : [Int] = [] + + func dfs(_ i: Int) { + if i >= nums.count { + res.append(subset) + return + } + // decision to include nums[i] + subset.append(nums[i]) + dfs(i + 1) + // decision NOT to include nums[i] + subset.removeLast() + dfs(i + 1) + } + + dfs(0) + return res + } +} \ No newline at end of file diff --git a/swift/0079-word-search.swift b/swift/0079-word-search.swift new file mode 100644 index 000000000..051ab4c4b --- /dev/null +++ b/swift/0079-word-search.swift @@ -0,0 +1,94 @@ +class Solution { + let dirX: [Int] = [-1, 1, 0, 0] + let dirY: [Int] = [0, 0, -1, 1] + + func exist(_ board: [[Character]], _ word: String) -> Bool { + let ROWS = board.count + let COLS = board[0].count + + var visited = [[Bool]]( + repeating: [Bool](repeating: false, count: COLS), + count: ROWS + ) + + var wordArr: [Character] = Array(word) + + for r in 0.. Bool { + ((row >= 0) && (col >= 0) && + (row < board.count) && (col < board[0].count)) + } + + + func dfs( + startRow row: Int, + startCol col: Int, + _ visited: inout [[Bool]], + _ board: [[Character]], + _ wordArr: inout [Character], + startIdx index: Int + ) -> Bool { + let strLen = wordArr.count + + // Last index check + if index == (strLen - 1) { + return board[row][col] == wordArr[index] + } + + // Edge case + guard index < strLen else { return false } + guard board[row][col] == wordArr[index] else { return false } + + for (dX, dY) in zip(dirX, dirY) { + let rowNew = row + dX + let colNew = col + dY + + guard isWithinBounds(row: rowNew, col: colNew, board) else { continue } + guard visited[rowNew][colNew] == false else { continue } + + // Visit + visited[rowNew][colNew] = true + + // Recursive DFS + if dfs( + startRow: rowNew, + startCol: colNew, + &visited, + board, + &wordArr, + startIdx: index + 1 + ) == true { + return true + } + + // Unvisit [for backtrack] + visited[rowNew][colNew] = false + } + + return false + } +} + diff --git a/swift/0080-remove-duplicates-from-sorted-array-ii.swift b/swift/0080-remove-duplicates-from-sorted-array-ii.swift new file mode 100644 index 000000000..3b37e224a --- /dev/null +++ b/swift/0080-remove-duplicates-from-sorted-array-ii.swift @@ -0,0 +1,23 @@ +class Solution { + func removeDuplicates(_ nums: inout [Int]) -> Int { + var l: Int = 0 + var r: Int = 0 + + while r < nums.count { + var count: Int = 1 + while r + 1 < nums.count && nums[r] == nums[r + 1] { + r += 1 + count += 1 + } + + for _ in 0.. ListNode? { + var cur = head + while cur != nil { + if cur?.val == cur?.next?.val { + cur?.next = cur?.next?.next + } else { + cur = cur?.next + } + } + return head + } +} \ No newline at end of file diff --git a/swift/0084-largest-rectangle-in-histogram.swift b/swift/0084-largest-rectangle-in-histogram.swift new file mode 100644 index 000000000..df4b8025f --- /dev/null +++ b/swift/0084-largest-rectangle-in-histogram.swift @@ -0,0 +1,26 @@ +/** + * Question Link: https://leetcode.com/problems/largest-rectangle-in-histogram/ + */ + +class LargestRectangleInHistogram { + func largestRectangleArea(_ heights: [Int]) -> Int { + var maxArea = 0 + var stack = [(Int, Int)]() + + for (i, h) in heights.enumerated() { + var start = i + while !stack.isEmpty && stack.last!.1 > h { + let (index, height) = stack.removeLast() + maxArea = max(maxArea, height * (i - index)) + start = index + } + stack.append((start, h)) + } + + for (i, h) in stack { + maxArea = max(maxArea, h * (heights.count - i)) + } + + return maxArea + } +} \ No newline at end of file diff --git a/swift/0090-subsets-ii.swift b/swift/0090-subsets-ii.swift new file mode 100644 index 000000000..d3a75ecf5 --- /dev/null +++ b/swift/0090-subsets-ii.swift @@ -0,0 +1,31 @@ +/** + * Question Link: https://leetcode.com/problems/subsets-ii/ + */ + + class Solution { + func subsetsWithDup(_ nums: [Int]) -> [[Int]] { + var subsets = [[Int]]() + var curSet = [Int]() + var nums = nums.sorted() + + func dfs(_ i: Int) { + if i >= nums.count { + subsets.append(curSet) + return + } + + curSet.append(nums[i]) + dfs(i + 1) + curSet.popLast() + + var i = i + while i + 1 < nums.count && nums[i] == nums[i + 1] { + i += 1 + } + dfs(i + 1) + } + + dfs(0) + return subsets + } +} \ No newline at end of file diff --git a/swift/0091-decode-ways.swift b/swift/0091-decode-ways.swift new file mode 100644 index 000000000..87d579edb --- /dev/null +++ b/swift/0091-decode-ways.swift @@ -0,0 +1,18 @@ +class Solution { + func numDecodings(_ s: String) -> Int { + var s = Array(s) + var dp: [Int: Int] = [s.count: 1] + for i in stride(from: s.count - 1, to: -1, by: -1) { + if s[i] == "0" { + dp[i] = 0 + } else { + dp[i] = dp[i + 1] + } + + if i + 1 < s.count && (s[i] == "1" || s[i] == "2" && "0123456".contains(s[i + 1])) { + dp[i, default: 0] += dp[i + 2] ?? 0 + } + } + return dp[0]! + } +} \ No newline at end of file diff --git a/swift/0094-binary-tree-inorder-traversal.swift b/swift/0094-binary-tree-inorder-traversal.swift new file mode 100644 index 000000000..ed3046b1d --- /dev/null +++ b/swift/0094-binary-tree-inorder-traversal.swift @@ -0,0 +1,14 @@ +class Solution { + func inorderTraversal(_ root: TreeNode?) -> [Int] { + var result: [Int] = [] + func rec(_ node: TreeNode?) { + guard let node = node else { return } + rec(node.left) + result.append(node.val) + rec(node.right) + } + + rec(root) + return result + } +} \ No newline at end of file diff --git a/swift/0097-interleaving-string.swift b/swift/0097-interleaving-string.swift new file mode 100644 index 000000000..11f19abcf --- /dev/null +++ b/swift/0097-interleaving-string.swift @@ -0,0 +1,27 @@ +/** + * Question Link: https://leetcode.com/problems/interleaving-string/ + */ + +class Solution { + func isInterleave(_ s1: String, _ s2: String, _ s3: String) -> Bool { + let s1 = Array(s1), s2 = Array(s2), s3 = Array(s3) + if s3.count != s1.count + s2.count { + return false + } + var dp = Array(repeating: false, count: s2.count + 1) + for i in 0.. Bool { + return isBst(root, min: Int.min, max: Int.max) + } + + private func isBst(_ node: TreeNode?, min: Int, max: Int) -> Bool { + if node == nil { return true } + if node!.val <= min || node!.val >= max { return false } + let lc = isBst(node?.left, min: min, max: node!.val) + let rc = isBst(node?.right, min: node!.val, max: max) + return lc && rc + } +} diff --git a/swift/0100-same-tree.swift b/swift/0100-same-tree.swift new file mode 100644 index 000000000..aa15bf603 --- /dev/null +++ b/swift/0100-same-tree.swift @@ -0,0 +1,7 @@ +class Solution { + func isSameTree(_ p: TreeNode?, _ q: TreeNode?) -> Bool { + if p == nil && q == nil { return true } + if p?.val != q?.val { return false } + return isSameTree(p?.left, q?.left) && isSameTree(p?.right, q?.right) + } +} diff --git a/swift/0102-binary-tree-level-order-traversal.swift b/swift/0102-binary-tree-level-order-traversal.swift new file mode 100644 index 000000000..e1db0f0cb --- /dev/null +++ b/swift/0102-binary-tree-level-order-traversal.swift @@ -0,0 +1,28 @@ +class Solution { + func levelOrder(_ root: TreeNode?) -> [[Int]] { + guard let root = root else { return [] } + var queue = [TreeNode]() + var result = [[Int]]() + queue.append(root) + while !queue.isEmpty { + var levelCount = queue.count + var levelNodes = [Int]() + while levelCount > 0 { + let node = queue.first! + queue.removeFirst() + levelCount -= 1 + levelNodes.append(node.val) + if let left = node.left { + queue.append(left) + } + + if let right = node.right { + queue.append(right) + } + } + result.append(levelNodes) + } + return result + } + +} diff --git a/swift/0104-maximum-depth-of-binary-tree.swift b/swift/0104-maximum-depth-of-binary-tree.swift new file mode 100644 index 000000000..510e0eb23 --- /dev/null +++ b/swift/0104-maximum-depth-of-binary-tree.swift @@ -0,0 +1,6 @@ +class Solution { + func maxDepth(_ root: TreeNode?) -> Int { + guard root != nil else { return 0 } + return max(maxDepth(root?.left) + 1, maxDepth(root?.right) + 1) + } +} diff --git a/swift/0105-construct-binary-tree-from-preorder-and-inorder-traversal.swift b/swift/0105-construct-binary-tree-from-preorder-and-inorder-traversal.swift new file mode 100644 index 000000000..6cbaa6b05 --- /dev/null +++ b/swift/0105-construct-binary-tree-from-preorder-and-inorder-traversal.swift @@ -0,0 +1,29 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func buildTree(_ preorder: [Int], _ inorder: [Int]) -> TreeNode? { + if preorder.isEmpty || inorder.isEmpty { + return nil + } + + var root = TreeNode(preorder[0]) + let mid = inorder.index(of: preorder[0])! + root.left = buildTree(Array(preorder[1.. Int { + guard let root = root else { return 0 } + return 1 + max( + getMaxHeight(of: root.left), + getMaxHeight(of: root.right) + ) + } + + func isBalanced(_ root: TreeNode?) -> Bool { + guard let root = root else { return true } + + let leftHeight = getMaxHeight(of: root.left) + let rightHeight = getMaxHeight(of: root.right) + let difference = abs(leftHeight - rightHeight) + + // early exits + guard difference <= 1 else { return false } + guard isBalanced(root.left) == true else { return false } + guard isBalanced(root.right) == true else { return false } + + return true + } +} diff --git a/swift/0112-path-sum.swift b/swift/0112-path-sum.swift new file mode 100644 index 000000000..a36d6255f --- /dev/null +++ b/swift/0112-path-sum.swift @@ -0,0 +1,13 @@ +class Solution { + func hasPathSum(_ root: TreeNode?, _ targetSum: Int) -> Bool { + guard let root = root else { return false } + + if root.val == targetSum && !hasChild(root) { return true } + + return hasPathSum(root.left, targetSum - root.val) || hasPathSum(root.right, targetSum - root.val) + } + + func hasChild(_ root: TreeNode) -> Bool { + return (root.left != nil) || (root.right != nil) + } +} \ No newline at end of file diff --git a/swift/0115-distinct-subsequences.swift b/swift/0115-distinct-subsequences.swift new file mode 100644 index 000000000..fc9c1fee7 --- /dev/null +++ b/swift/0115-distinct-subsequences.swift @@ -0,0 +1,23 @@ +/** + * Question Link: https://leetcode.com/problems/distinct-subsequences/ + */ + + class Solution { + func numDistinct(_ s: String, _ t: String) -> Int { + let s = Array(s), t = Array(t) + let m = s.count, n = t.count + var dp = Array(repeating: 0.0, count: n) + for i in stride(from: m - 1, to: -1, by: -1) { + var prev = 1.0 + for j in stride(from: n - 1, to: -1, by: -1) { + let oldDPJ = dp[j] + if s[i] == t[j] { + dp[j] += prev + } + prev = oldDPJ + } + } + + return Int(dp[0]) + } +} \ No newline at end of file diff --git a/swift/0121-best-time-to-buy-and-sell-stock.swift b/swift/0121-best-time-to-buy-and-sell-stock.swift new file mode 100644 index 000000000..f66d27437 --- /dev/null +++ b/swift/0121-best-time-to-buy-and-sell-stock.swift @@ -0,0 +1,16 @@ +class Solution { + func maxProfit(_ prices: [Int]) -> Int { + var res = 0 + var l = 0 + var r = 0 + + for r in 1.. Int { + // Base case + guard let node = node else { return 0 } + + // Recursive cases + // MAX with 0, to shorten negative paths + let leftMaxPath = max(ends(at: node.left), 0) + let rightMaxPath = max(ends(at: node.right), 0) + + let pathIncludingNode = leftMaxPath + node.val + rightMaxPath + globalMax = max(globalMax, pathIncludingNode) + + let pathEndingAtNode = max(leftMaxPath, rightMaxPath) + node.val + return pathEndingAtNode + } + + func maxPathSum(_ root: TreeNode?) -> Int { + ends(at: root) + return globalMax + } +} \ No newline at end of file diff --git a/swift/0125-valid-palindrome.swift b/swift/0125-valid-palindrome.swift new file mode 100644 index 000000000..926356e4b --- /dev/null +++ b/swift/0125-valid-palindrome.swift @@ -0,0 +1,34 @@ +/** + * Question Link: https://leetcode.com/problems/valid-palindrome/ + */ + +class ValidPalindrome { + func isPalindrome(_ s: String) -> Bool { + var l = 0, r = s.count - 1 + let sChars = Array(s.lowercased()) + + while l < r { + while !sChars[l].isAlphanumeric && l < r { + l += 1 + } + + while !sChars[r].isAlphanumeric && l < r { + r -= 1 + } + + if sChars[l] != sChars[r] { + return false + } else { + l += 1 + r -= 1 + } + } + return true + } +} + +extension Character { + var isAlphanumeric : Bool { + return isLetter || isNumber + } +} diff --git a/swift/0127-word-ladder.swift b/swift/0127-word-ladder.swift new file mode 100644 index 000000000..3c418b791 --- /dev/null +++ b/swift/0127-word-ladder.swift @@ -0,0 +1,44 @@ +class Solution { + func ladderLength(_ beginWord: String, _ endWord: String, _ wordList: [String]) -> Int { + var wordList = wordList + if !wordList.contains(endWord) { + return 0 + } + + var nei = [String: [String]]() + wordList.append(beginWord) + for word in wordList { + for j in 0.. Int { + // remove duplicates numbers + let numSet = Set(nums) + var longestSequenceCount = 0 + // iterate through each number in the numSet + // find the lowest number for a sequence, if num - 1 is not there in the numSet, it means num - 1 is the + // lowest for that particular sequence + for num in numSet where !numSet.contains(num - 1) { + // num is lowest number for the probable longest consecutive sequence + var visitingNum = num + 1 + var currentSequenceCount = 1 + // check if next number exists in the numSet, if so then increase the count + while numSet.contains(visitingNum) { + currentSequenceCount += 1 + visitingNum += 1 + } + // set the maximum count + longestSequenceCount = max(longestSequenceCount, currentSequenceCount) + } + return longestSequenceCount + } +} diff --git a/swift/0130-surrounded-regions.swift b/swift/0130-surrounded-regions.swift new file mode 100644 index 000000000..876b9ccec --- /dev/null +++ b/swift/0130-surrounded-regions.swift @@ -0,0 +1,48 @@ +class Solution { + func solve(_ board: inout [[Character]]) { + var rows = board.count + var columns = board[0].count + + // 1. DFS Capture unsurrounded regions (O -> T) + for r in 0.. X) + for r in 0.. O) + for r in 0..= 0, r < board.count, c >= 0, c < board[0].count, board[r][c] == "O" else { + return + } + + board[r][c] = Character("T") + + capture(r - 1, c, &board) + capture(r + 1, c, &board) + capture(r, c - 1, &board) + capture(r, c + 1, &board) + } +} \ No newline at end of file diff --git a/swift/0131-palindrome-partitioning.swift b/swift/0131-palindrome-partitioning.swift new file mode 100644 index 000000000..9d4f26ff2 --- /dev/null +++ b/swift/0131-palindrome-partitioning.swift @@ -0,0 +1,34 @@ +class Solution { + func isPalindrom(_ characters: [Character]) -> Bool { + guard !characters.isEmpty else { return false } + var i = 0 + var j = characters.count - 1 + while i < j { + guard characters[i] == characters[j] else { return false } + i += 1 + j -= 1 + } + return true + } + func partition(_ s: String) -> [[String]] { + var substrings: [String] = [] + var result: [[String]] = [] + let characters = s.map { $0 } + + func dfs(_ start: Int) { + guard start < s.count else { + result.append(substrings) + return + } + for end in start.. Node? { + guard let node = node else { return nil } + // check if cache exists + + if mapping[node] != nil { + return mapping[node]! + } + + // otherwise, create a node, cache it, recurse for children + let newNode = Node(node.val) + mapping[node] = newNode + + node.neighbors.forEach { neighbor in + newNode.neighbors.append(cloneGraph(neighbor)) + } + return newNode + } +} \ No newline at end of file diff --git a/swift/0134-gas-station.swift b/swift/0134-gas-station.swift new file mode 100644 index 000000000..f269e322a --- /dev/null +++ b/swift/0134-gas-station.swift @@ -0,0 +1,22 @@ +class Solution { + func canCompleteCircuit(_ gas: [Int], _ cost: [Int]) -> Int { + if gas.reduce(0, +) < cost.reduce(0, +) { + return -1 + } + var start = gas.count - 1, end = 0 + var total = gas[start] - cost[start] + + while start >= end { + while total < 0 && start >= end { + start -= 1 + total += gas[start] - cost[start] + } + if start == end { + return start + } + total += gas[end] - cost[end] + end += 1 + } + return -1 + } +} \ No newline at end of file diff --git a/swift/0136-single-number.swift b/swift/0136-single-number.swift new file mode 100644 index 000000000..1b6963cab --- /dev/null +++ b/swift/0136-single-number.swift @@ -0,0 +1,24 @@ +//BitManipulation Solution O(n), SC(1) +class Solution { + func singleNumber(_ nums: [Int]) -> Int { + var ans = 0 + for i in nums { + ans ^= i + } + return ans + } +} + +//HashMapSolution O(n), SC(n) +class Solution { + func singleNumber(_ nums: [Int]) -> Int { + var hm: [Int: Int] = [:] + for i in nums { + hm[i,default: 0] += 1 + } + for i in nums { + if hm[i] == 1 { return i } + } + return -1 + } +} diff --git a/swift/0138-copy-list-with-random-pointer.swift b/swift/0138-copy-list-with-random-pointer.swift new file mode 100644 index 000000000..5b4c5ebd8 --- /dev/null +++ b/swift/0138-copy-list-with-random-pointer.swift @@ -0,0 +1,38 @@ +/** + * Definition for a Node. + * public class Node { + * public var val: Int + * public var next: Node? + * public var random: Node? + * public init(_ val: Int) { + * self.val = val + * self.next = nil + * self.random = nil + * } + * } + */ + +class Solution { + // Cache of mapping from old to new nodes + private var mapping: [Node?: Node?] = [:] + + func copyRandomList(_ head: Node?) -> Node? { + // Base case: Nil node + guard let node = head else { return nil } + + // Mapping exists + guard mapping[node] == nil else { return mapping[node]! } + + // Create new node + let newNode = Node(node.val) + + // Add to cache (i.e. 'visit' this node) + mapping[node] = newNode + + // Recursive calls (preorder -> children calls) + newNode.next = copyRandomList(node.next) + newNode.random = copyRandomList(node.random) + + return newNode + } +} diff --git a/swift/0139-word-break.swift b/swift/0139-word-break.swift new file mode 100644 index 000000000..cc373db8a --- /dev/null +++ b/swift/0139-word-break.swift @@ -0,0 +1,20 @@ +class Solution { + func wordBreak(_ s: String, _ wordDict: [String]) -> Bool { + var s = Array(s) + var dp = [Bool](repeating: false, count: s.count + 1) + dp[s.count] = true + + for i in stride(from: s.count - 1, to: -1, by: -1) { + for w in wordDict { + if i + w.count <= s.count && String(s[i..<(i + w.count)]) == w { + dp[i] = dp[i + w.count] + } + if dp[i] { + break + } + } + } + + return dp[0] + } +} \ No newline at end of file diff --git a/swift/0141-linked-list-cycle.swift b/swift/0141-linked-list-cycle.swift new file mode 100644 index 000000000..4a407eea6 --- /dev/null +++ b/swift/0141-linked-list-cycle.swift @@ -0,0 +1,31 @@ +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init(_ val: Int) { + * self.val = val + * self.next = nil + * } + * } + */ + +class Solution { + func hasCycle(_ head: ListNode?) -> Bool { + if head === nil { + return false + } + + var slow = head, fast = head + + while fast != nil && fast?.next != nil { + slow = slow?.next + fast = fast?.next?.next + + if fast === slow { + return true + } + } + return false + } +} \ No newline at end of file diff --git a/swift/0142-linked-list-cycle-ii.swift b/swift/0142-linked-list-cycle-ii.swift new file mode 100644 index 000000000..1994307b9 --- /dev/null +++ b/swift/0142-linked-list-cycle-ii.swift @@ -0,0 +1,38 @@ +/** + * Question Link: https://leetcode.com/problems/linked-list-cycle-ii +*/ + +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init(_ val: Int) { + * self.val = val + * self.next = nil + * } + * } + */ + +class Solution { + func detectCycle(_ head: ListNode?) -> ListNode? { + var slow = head + var fast = head + while fast != nil && fast?.next != nil { + slow = slow?.next + fast = fast?.next?.next + if slow === fast { + break + } + } + if fast == nil || fast?.next == nil { + return nil + } + var slow2 = head + while slow !== slow2 { + slow = slow?.next + slow2 = slow2?.next + } + return slow + } +} \ No newline at end of file diff --git a/swift/0143-reorder-list.swift b/swift/0143-reorder-list.swift new file mode 100644 index 000000000..bd37c6e0b --- /dev/null +++ b/swift/0143-reorder-list.swift @@ -0,0 +1,73 @@ +// Slow and Fast Pointer Method +/** + * Question Link: https://leetcode.com/problems/reorder-list/ + */ + + /** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class ReorderList { + func reorderList(_ head: ListNode?) { + var slow = head + var fast = head?.next + + while fast != nil || fast?.next != nil { + slow = slow?.next + fast = fast?.next?.next + } + + var second = slow?.next + slow?.next = nil + var prev: ListNode? + + while second != nil { + var temp = second?.next + second?.next = prev + prev = second + second = temp + } + + var first = head + second = prev + + while second != nil { + var temp1 = first?.next + var temp2 = second?.next + first?.next = second + second?.next = temp1 + first = temp1 + second = temp2 + } + } +} + +// Deque +import DequeModule + +class Solution { + func reorderList(_ head: ListNode?) { + var queue = Deque() + var curr = head + while curr != nil { + queue.append(curr!) + curr = curr!.next + } + var lastNode: ListNode? + while !queue.isEmpty { + let leftNode = queue.popFirst() + let rightNode = queue.popLast() + rightNode?.next = nil + leftNode?.next = rightNode + lastNode?.next = leftNode + lastNode = rightNode + } + } +} + diff --git a/swift/0144-binary-tree-preorder-traversal.swift b/swift/0144-binary-tree-preorder-traversal.swift new file mode 100644 index 000000000..342540ba6 --- /dev/null +++ b/swift/0144-binary-tree-preorder-traversal.swift @@ -0,0 +1,38 @@ +/** + * Question Link: https://leetcode.com/problems/binary-tree-preorder-traversal/ + */ + + /** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func preorderTraversal(_ root: TreeNode?) -> [Int] { + var stack = [TreeNode]() + var cur = root + var res = [Int]() + while cur != nil || !stack.isEmpty { + if cur != nil { + res.append(cur!.val) + if cur?.right != nil { + stack.append(cur!.right!) + } + cur = cur?.left + } else { + cur = stack.popLast() + } + } + return res + } +} \ No newline at end of file diff --git a/swift/0145-binary-tree-postorder-traversal.swift b/swift/0145-binary-tree-postorder-traversal.swift new file mode 100644 index 000000000..0072a2cbe --- /dev/null +++ b/swift/0145-binary-tree-postorder-traversal.swift @@ -0,0 +1,43 @@ +/** + * Question Link: https://leetcode.com/problems/binary-tree-postorder-traversal/ + */ + + /** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func postorderTraversal(_ root: TreeNode?) -> [Int] { + var res = [Int]() + var stack = [root] + var visit = [false] + while !stack.isEmpty { + let cur = stack.removeLast() + let visited = visit.removeLast() + if cur != nil { + if visited { + res.append(cur!.val) + } else { + stack.append(cur) + visit.append(true) + stack.append(cur?.right) + visit.append(false) + stack.append(cur?.left) + visit.append(false) + } + } + } + return res + } +} \ No newline at end of file diff --git a/swift/0146-lru-cache.swift b/swift/0146-lru-cache.swift new file mode 100644 index 000000000..fcd0e6284 --- /dev/null +++ b/swift/0146-lru-cache.swift @@ -0,0 +1,78 @@ +/** + * Question Link: https://leetcode.com/problems/lru-cache/ + */ + + class ListNode { + var prev: ListNode? + var next: ListNode? + var key: Int + var val: Int + + init(key: Int, val: Int) { + self.key = key + self.val = val + } +} + +class LRUCache { + let capacity: Int + var hashmap: [Int: ListNode] + var lru: ListNode + var mru: ListNode + + init(_ capacity: Int) { + self.capacity = capacity + self.hashmap = [:] + self.lru = ListNode(key: 0, val: 0) + self.mru = ListNode(key: 0, val: 0) + self.lru.next = self.mru + self.mru.prev = self.lru + } + + func get(_ key: Int) -> Int { + if let node = hashmap[key] { + remove(node: node) + insert(node: node) + return node.val + } else { + return -1 + } + } + + func put(_ key: Int, _ value: Int) { + if let node = hashmap[key] { + remove(node: node) + } + hashmap[key] = ListNode(key: key, val: value) + insert(node: hashmap[key]!) + + if hashmap.values.count > capacity { + let lru = self.lru.next + remove(node: lru!) + hashmap.removeValue(forKey: lru!.key) + } + } + + func insert(node: ListNode) { + let prev = mru.prev! + let next = mru + prev.next = node + next.prev = node + node.next = next + node.prev = prev + } + + func remove(node: ListNode) { + let prev = node.prev! + let next = node.next! + prev.next = next + next.prev = prev + } +} + +/** + * Your LRUCache object will be instantiated and called as such: + * let obj = LRUCache(capacity) + * let ret_1: Int = obj.get(key) + * obj.put(key, value) + */ \ No newline at end of file diff --git a/swift/0150-evaluate-reverse-polish-notation.swift b/swift/0150-evaluate-reverse-polish-notation.swift new file mode 100644 index 000000000..25cd09554 --- /dev/null +++ b/swift/0150-evaluate-reverse-polish-notation.swift @@ -0,0 +1,58 @@ +class Solution { + func evalRPN(_ tokens: [String]) -> Int { + guard tokens.count > 0 else { return 0 } + var stack: Stack = Stack() + for c in tokens { + if c == "+" { + stack.push((stack.pop() ?? 0) + (stack.pop() ?? 0) ) + } else if c == "-" { + var a = stack.pop() ?? 0 + var b = stack.pop() ?? 0 + stack.push(b - a) + } else if c == "*" { + stack.push((stack.pop() ?? 0) * (stack.pop() ?? 0) ) + } else if c == "/" { + var a = stack.pop() ?? 0 + var b = stack.pop() ?? 0 + stack.push(b / a) + } else { + stack.push(Int(c) ?? 0) + } + } + return stack.pop() ?? 0 + } +} + +class Node { + var data: T + var next: Node? + init(_ value: T) { + self.data = value + } +} + +class Stack { + + var head: Node? + + var isEmpty: Bool { + head == nil + } + + func peak() -> Node? { + head + } + + func push(_ data: T) { + var node = Node(data) + node.next = head + head = node + } + + func pop() -> T? { + var data = head?.data + head = head?.next + return data + } + +} diff --git a/swift/0152-maximum-product-subarray.swift b/swift/0152-maximum-product-subarray.swift new file mode 100644 index 000000000..7ef3132d5 --- /dev/null +++ b/swift/0152-maximum-product-subarray.swift @@ -0,0 +1,15 @@ +class Solution { + func maxProduct(_ nums: [Int]) -> Int { + var res = nums[0] + var curMin = 1 + var curMax = 1 + + for n in nums { + let temp = curMax * n + curMax = max(n * curMax, n * curMin, n) + curMin = min(temp, n * curMin, n) + res = max(res, curMax) + } + return res + } +} \ No newline at end of file diff --git a/swift/0153-find-minimum-in-rotated-sorted-array.swift b/swift/0153-find-minimum-in-rotated-sorted-array.swift new file mode 100644 index 000000000..d14eb6dff --- /dev/null +++ b/swift/0153-find-minimum-in-rotated-sorted-array.swift @@ -0,0 +1,18 @@ +class Solution { + func findMin(_ nums: [Int]) -> Int { + var start = 0 + var end = nums.count - 1 + var min = Int.max + while start <= end { + let mid = start + (end - start) / 2 + if min == -1 { min = nums[mid] } + if nums[mid] < min { min = nums[mid] } + if nums[mid] > nums[end] { + start = mid + 1 + } else { + end = mid - 1 + } + } + return min + } +} diff --git a/swift/0155-min-stack.swift b/swift/0155-min-stack.swift new file mode 100644 index 000000000..177a7590c --- /dev/null +++ b/swift/0155-min-stack.swift @@ -0,0 +1,42 @@ +class MinStack { + var stack: [Int] + var minStack: [Int] + init() { + stack = [] + minStack = [] + } + + func push(_ val: Int) { + stack.append(val) + if minStack.isEmpty { + minStack.append(val) + } + else { + let minVal = min(val, minStack.last!) + minStack.append(minVal) + } + + } + + func pop() { + stack.removeLast() + minStack.removeLast() + } + + func top() -> Int { + return stack.last! + } + + func getMin() -> Int { + return minStack.last! + } +} + +/** + * Your MinStack object will be instantiated and called as such: + * let obj = MinStack() + * obj.push(val) + * obj.pop() + * let ret_3: Int = obj.top() + * let ret_4: Int = obj.getMin() + */ \ No newline at end of file diff --git a/swift/0167-two-sum-ii-input-array-is-sorted.swift b/swift/0167-two-sum-ii-input-array-is-sorted.swift new file mode 100644 index 000000000..5096a92e5 --- /dev/null +++ b/swift/0167-two-sum-ii-input-array-is-sorted.swift @@ -0,0 +1,17 @@ +class Solution { + func twoSum(_ numbers: [Int], _ target: Int) -> [Int] { + var rp = numbers.count - 1 + var lp = 0 + while lp < rp { + if numbers[lp] + numbers[rp] == target { + return [lp + 1, rp + 1] + } + if numbers[lp] + numbers[rp] > target { + rp -= 1 + } else { + lp += 1 + } + } + return [] + } +} diff --git a/swift/0169-majority-element.swift b/swift/0169-majority-element.swift new file mode 100644 index 000000000..193c5b744 --- /dev/null +++ b/swift/0169-majority-element.swift @@ -0,0 +1,20 @@ +class Solution { + func majorityElement(_ nums: [Int]) -> Int { + guard !nums.isEmpty else { + return 0 + } + + var seen = [Int : Int]() + + for num in nums { + let newOccurance = seen[num, default: 0] + 1 + seen[num] = newOccurance + + if newOccurance > nums.count / 2 { + return num + } + } + + return -1 + } +} diff --git a/swift/0173-binary-search-tree-iterator.swift b/swift/0173-binary-search-tree-iterator.swift new file mode 100644 index 000000000..36d1d768f --- /dev/null +++ b/swift/0173-binary-search-tree-iterator.swift @@ -0,0 +1,55 @@ +/** + * Question Link: https://leetcode.com/problems/binary-search-tree-iterator/ + */ + + /** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ + +class BSTIterator { + var cur: TreeNode? + var stack: [TreeNode] + + init(_ root: TreeNode?) { + self.cur = root + self.stack = [] + } + + func next() -> Int { + while cur != nil || !stack.isEmpty { + if cur != nil { + stack.append(cur!) + cur = cur?.left + } else { + cur = stack.popLast() + let res = cur?.val ?? 0 + cur = cur?.right + return res + } + } + return -1 + } + + func hasNext() -> Bool { + cur != nil || !stack.isEmpty + } +} + +/** + * Your BSTIterator object will be instantiated and called as such: + * let obj = BSTIterator(root) + * let ret_1: Int = obj.next() + * let ret_2: Bool = obj.hasNext() + */ \ No newline at end of file diff --git a/swift/0179-largest-number.swift b/swift/0179-largest-number.swift new file mode 100644 index 000000000..99ddaf68d --- /dev/null +++ b/swift/0179-largest-number.swift @@ -0,0 +1,5 @@ +class Solution { + func largestNumber(_ nums: [Int]) -> String { + return nums.reduce(into: Bool(false), { $0 = $0 || $1 > 0 }) ? nums.map { String($0) }.sorted { $0 + $1 > $1 + $0 }.joined() : "0" + } +} \ No newline at end of file diff --git a/swift/0190-reverse-bits.swift b/swift/0190-reverse-bits.swift new file mode 100644 index 000000000..6209b349f --- /dev/null +++ b/swift/0190-reverse-bits.swift @@ -0,0 +1,11 @@ +class Solution { + func reverseBits(_ n: Int) -> Int { + var res = 0 + + for i in 0...31 { + let bit = (n >> i) & 1 + res = res | (bit << (31 - i)) + } + return res + } +} \ No newline at end of file diff --git a/swift/0191-number-of-1-bits.swift b/swift/0191-number-of-1-bits.swift new file mode 100644 index 000000000..ecfdd75d9 --- /dev/null +++ b/swift/0191-number-of-1-bits.swift @@ -0,0 +1,11 @@ +class Solution { + func hammingWeight(_ n: Int) -> Int { + var x = n + var count = 0 + while x != 0 { + x = x & (x - 1) + count += 1 + } + return count + } +} diff --git a/swift/0198-house-robber.swift b/swift/0198-house-robber.swift new file mode 100644 index 000000000..66c5fdc34 --- /dev/null +++ b/swift/0198-house-robber.swift @@ -0,0 +1,12 @@ +class Solution { + func rob(_ nums: [Int]) -> Int { + var rob1 = 0, rob2 = 0 + + for n in nums { + let temp = max(n + rob1, rob2) + rob1 = rob2 + rob2 = temp + } + return rob2 + } +} \ No newline at end of file diff --git a/swift/0199-binary-tree-right-side-view.swift b/swift/0199-binary-tree-right-side-view.swift new file mode 100644 index 000000000..93917117e --- /dev/null +++ b/swift/0199-binary-tree-right-side-view.swift @@ -0,0 +1,24 @@ +// +// 199-Binary-Tree-Right-Side-View.swift +// Question Link: https://leetcode.com/problems/binary-tree-right-side-view/ +// + +class Solution { + func rightSideView(_ root: TreeNode?) -> [Int] { + guard let root = root else { return [] } + var result = [Int]() + var queue: [TreeNode] = [root] + + while !queue.isEmpty { + var temp = [Int]() + for node in queue { + let node = queue.removeFirst() + if let leftNode = node.left, leftNode != nil { queue.append(leftNode) } + if let rightNode = node.right, rightNode != nil { queue.append(rightNode) } + temp.append(node.val) + } + result.append(temp.last!) + } + return result + } +} diff --git a/swift/0200-number-of-islands.swift b/swift/0200-number-of-islands.swift new file mode 100644 index 000000000..90bc5ed24 --- /dev/null +++ b/swift/0200-number-of-islands.swift @@ -0,0 +1,58 @@ +class Solution { + + func numIslands(_ grid: [[Character]]) -> Int { + let ROWS = grid.count + let COLS = grid[0].count + + var visited = [[Bool]]( + repeating: [Bool](repeating: false, count: COLS), + count: ROWS + ) + var count: Int = 0 + + // Helper function to check if is within + func isWithin(row: Int, col: Int) -> Bool { + return ( + (row >= 0) && (col >= 0) && + (row < ROWS) && (col < COLS) + ) + } + + // Helper function to check if cell is land or not + func isLand(row: Int, col: Int) -> Bool { + return grid[row][col] == "1" + } + + let movementsX = [-1, 1, 0, 0] + let movementsY = [0, 0, -1, 1] + + // Helper DFS function to 'visit' the grid + func dfs(row: Int, col: Int) { + // visit each child + for (dirX, dirY) in zip(movementsX, movementsY) { + let rowNew = row + dirX + let colNew = col + dirY + + guard isWithin(row: rowNew, col: colNew) else { continue } + guard visited[rowNew][colNew] == false else { continue } + guard isLand(row: rowNew, col: colNew) else { continue } + + visited[rowNew][colNew] = true + dfs(row: rowNew, col: colNew) + } + } + + for r in 0.. Bool { + var slow = n, fast = sumSquareDigits(n) + + while slow != fast { + fast = sumSquareDigits(fast) + fast = sumSquareDigits(fast) + slow = sumSquareDigits(slow) + } + + return fast == 1 + } + + func sumSquareDigits(_ n: Int) -> Int { + var output = 0 + var num = n + while num != 0 { + let currDigit = num % 10 + output += currDigit * currDigit + num /= 10 + } + return output + } +} \ No newline at end of file diff --git a/swift/0205-isomorphic-strings.swift b/swift/0205-isomorphic-strings.swift new file mode 100644 index 000000000..0431ad891 --- /dev/null +++ b/swift/0205-isomorphic-strings.swift @@ -0,0 +1,24 @@ +class Solution { + func isIsomorphic(_ s: String, _ t: String) -> Bool { + guard s.count == t.count else { return false } + + var mapSourceToDest: [Character: Character] = [:] + var mapDestToSource: [Character: Character] = [:] + + for (sourceChar, destChar) in zip(s, t) { + if let sourceMapped = mapSourceToDest[sourceChar] { + guard sourceMapped == destChar else { return false } + } else { + mapSourceToDest[sourceChar] = destChar + } + + if let destMapped = mapDestToSource[destChar] { + guard destMapped == sourceChar else { return false } + } else { + mapDestToSource[destChar] = sourceChar + } + } + + return true + } +} \ No newline at end of file diff --git a/swift/0206-reverse-linked-list.swift b/swift/0206-reverse-linked-list.swift new file mode 100644 index 000000000..cfbb6850c --- /dev/null +++ b/swift/0206-reverse-linked-list.swift @@ -0,0 +1,28 @@ +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func reverseList(_ head: ListNode?) -> ListNode? { + if (head === nil || head?.next === nil) { + return head + } + + var prev: ListNode? = nil + var curr = head, next = curr?.next + + while curr != nil { + next = curr?.next + curr?.next = prev + prev = curr + curr = next + } + return prev + } +} \ No newline at end of file diff --git a/swift/0207-course-schedule.swift b/swift/0207-course-schedule.swift new file mode 100644 index 000000000..cbb270a9e --- /dev/null +++ b/swift/0207-course-schedule.swift @@ -0,0 +1,55 @@ +class Solution { + func canFinish(_ numCourses: Int, _ prerequisites: [[Int]]) -> Bool { + // map each course to prereq List + var preMap = [Int: [Int]]() + for num in 0..() + + for crs in 0..) -> Bool { + guard !visitSet.contains(crs) else { + return false + } + + if preMap[crs] == [] { + return true + } + + visitSet.insert(crs) + + if let prerequisites = preMap[crs] { + for pre in prerequisites { + let canCompletePrerequisite = dfs(pre, &preMap, &visitSet) + if !canCompletePrerequisite { + return false + } + } + } + + visitSet.remove(crs) + preMap[crs] = [] + + return true + } +} + + diff --git a/swift/0208-implement-trie-prefix-tree.swift b/swift/0208-implement-trie-prefix-tree.swift new file mode 100644 index 000000000..ce2302339 --- /dev/null +++ b/swift/0208-implement-trie-prefix-tree.swift @@ -0,0 +1,58 @@ +class TrieNode { + var children: [Character: TrieNode] + var isWord: Bool + + init() { + children = [:] + isWord = false + } +} + +class Trie { + var head: TrieNode + + init() { + head = TrieNode() + } + + func insert(_ word: String) { + var itr = head + for char in word { + guard let child = itr.children[char] + else { + let newNode = TrieNode() + itr.children[char] = newNode + itr = newNode + continue + } + itr = child + } + itr.isWord = true + } + + // Helper function to combine word and prefix searching + private func search(word: String, isPrefixCheck: Bool) -> Bool { + var itr = head + for char in word { + guard let child = itr.children[char] else { return false } + itr = child + } + return isPrefixCheck ? true : itr.isWord + } + + func search(_ word: String) -> Bool { + search(word: word, isPrefixCheck: false) + } + + func startsWith(_ prefix: String) -> Bool { + search(word: prefix, isPrefixCheck: true) + } +} + +/** + * Your Trie object will be instantiated and called as such: + * let obj = Trie() + * obj.insert(word) + * let ret_2: Bool = obj.search(word) + * let ret_3: Bool = obj.startsWith(prefix) + */ \ No newline at end of file diff --git a/swift/0209-minimum-size-subarray-sum.swift b/swift/0209-minimum-size-subarray-sum.swift new file mode 100644 index 000000000..de5ce90a6 --- /dev/null +++ b/swift/0209-minimum-size-subarray-sum.swift @@ -0,0 +1,20 @@ +/** + * Question Link: https://leetcode.com/problems/minimum-size-subarray-sum/ + */ + + class Solution { + func minSubArrayLen(_ target: Int, _ nums: [Int]) -> Int { + var l = 0 + var total = 0 + var length = Int.max + for r in 0..= target { + length = min(length, r - l + 1) + total -= nums[l] + l += 1 + } + } + return length == Int.max ? 0 : length + } +} \ No newline at end of file diff --git a/swift/0210-course-schedule-ii.swift b/swift/0210-course-schedule-ii.swift new file mode 100644 index 000000000..0d3d2168f --- /dev/null +++ b/swift/0210-course-schedule-ii.swift @@ -0,0 +1,45 @@ +/** + * Question Link: https://leetcode.com/problems/course-schedule-ii/ + */ + + class Solution { + func findOrder(_ numCourses: Int, _ prerequisites: [[Int]]) -> [Int] { + var prereq = [Int: [Int]]() + for i in 0..() + var cycle = Set() + + func dfs(_ crs: Int) -> Bool { + if cycle.contains(crs) { + return false + } + if visit.contains(crs) { + return true + } + cycle.insert(crs) + for pre in prereq[crs]! { + if !dfs(pre) { + return false + } + } + cycle.remove(crs) + visit.insert(crs) + output.append(crs) + return true + } + + for i in 0.. Bool { + var word = Array(word) + + func dfs(j: Int, root: TrieNode) -> Bool { + var curr: TrieNode? = root + for i in j.. [String] { + var answer = [String]() + let root = buildTrie(words) + + var board = board + + for row in 0.. TrieNode { + let root = TrieNode() + for word in words { + var current = root + + word.forEach { + if current.children[$0] == nil { + current.children[$0] = TrieNode() + } + current = current.children[$0]! + } + current.word = word + } + + return root + } + + func getInBoardChild(_ x: Int, _ y: Int,_ board:[[Character]],_ root: TrieNode) -> TrieNode? { + if 0.. Int { + let size = nums.count + + if size == 1 { + return nums[0] + } + + let range1 = robber(nums, 0, size - 2) + let range2 = robber(nums, 1, size - 1) + + return max(range1, range2) + } + + func robber(_ nums: [Int], _ start: Int, _ end: Int) -> Int { + var prev = 0 + var curr = 0 + var next = 0 + + for i in start...end { + next = max(prev + nums[i], curr) + prev = curr + curr = next + } + return curr + } +} \ No newline at end of file diff --git a/swift/0215-kth-largest-element-in-an-array.swift b/swift/0215-kth-largest-element-in-an-array.swift new file mode 100644 index 000000000..2be4572c5 --- /dev/null +++ b/swift/0215-kth-largest-element-in-an-array.swift @@ -0,0 +1,25 @@ +/** + * Question Link: https://leetcode.com/problems/kth-largest-element-in-an-array + */ + +class Solution { + func findKthLargest(_ nums: [Int], _ k: Int) -> Int { + var minValue = nums.min()! + var maxValue = nums.max()! + var count = Array(repeating: 0, count: maxValue - minValue + 1) + + for i in 0.. Bool { + var hashSet = Set() + for n in nums { + if hashSet.contains(n) { + return true + } + hashSet.insert(n) + } + return false + } + + func containsDuplicateByCountComparison(_ nums: [Int]) -> Bool { + + // a Set must contain unique items + // so, if the count of `nums` is unequal to `nums` casted as a Set, + // then, one or more items were removed, + // meaning, there were one or more duplicates in `nums` + if nums.count == Set(nums).count { + return false + } else { + return true + } + } +} diff --git a/swift/0219-contains-duplicate-ii.swift b/swift/0219-contains-duplicate-ii.swift new file mode 100644 index 000000000..1297e1efb --- /dev/null +++ b/swift/0219-contains-duplicate-ii.swift @@ -0,0 +1,21 @@ +/** + * Question Link: https://leetcode.com/problems/contains-duplicate-ii/ + */ + + class Solution { + func containsNearbyDuplicate(_ nums: [Int], _ k: Int) -> Bool { + var hashset = Set() + var l = 0 + for r in 0.. k { + hashset.remove(nums[l]) + l += 1 + } + if hashset.contains(nums[r]) { + return true + } + hashset.insert(nums[r]) + } + return false + } +} \ No newline at end of file diff --git a/swift/0225-implement-stack-using-queues.swift b/swift/0225-implement-stack-using-queues.swift new file mode 100644 index 000000000..3fd30c498 --- /dev/null +++ b/swift/0225-implement-stack-using-queues.swift @@ -0,0 +1,80 @@ +/** + * Question Link: https://leetcode.com/problems/implement-stack-using-queues/ + */ + + class ListNode { + var val: Int + var next: ListNode? + + init(_ val: Int) { + self.val = val + } +} + +class Queue { + var head: ListNode? + var tail: ListNode? + var size = 0 + + func enqueue(_ val: Int) { + let node = ListNode(val) + if head == nil { + head = node + tail = node + } else { + tail?.next = node + tail = tail?.next + } + size += 1 + } + + func dequeue() -> Int { + if head == nil { + return -1 + } + let val = head?.val + head = head?.next + if head == nil { + tail = nil + } + size -= 1 + return val ?? -1 + } +} + +class MyStack { + var queue = Queue() + + init() { + + } + + func push(_ x: Int) { + queue.enqueue(x) + } + + func pop() -> Int { + for _ in 0.. Int { + queue.tail?.val ?? -1 + } + + func empty() -> Bool { + queue.size == 0 + } +} + +/** + * Your MyStack object will be instantiated and called as such: + * let obj = MyStack() + * obj.push(x) + * let ret_2: Int = obj.pop() + * let ret_3: Int = obj.top() + * let ret_4: Bool = obj.empty() + */ \ No newline at end of file diff --git a/swift/0226-invert-binary-tree.swift b/swift/0226-invert-binary-tree.swift new file mode 100644 index 000000000..980611a62 --- /dev/null +++ b/swift/0226-invert-binary-tree.swift @@ -0,0 +1,26 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func invertTree(_ root: TreeNode?) -> TreeNode? { + guard root != nil else { return root } + let left = invertTree(root?.left) + let right = invertTree(root?.right) + root?.left = right + root?.right = left + return root + } + +} diff --git a/swift/0228-summary-ranges.swift b/swift/0228-summary-ranges.swift new file mode 100644 index 000000000..cb0736d83 --- /dev/null +++ b/swift/0228-summary-ranges.swift @@ -0,0 +1,27 @@ +class Solution { + func summaryRanges(_ nums: [Int]) -> [String] { + if nums.isEmpty { + return [] + } + var l = 0 + var res = [String]() + for r in 1..\(nums[r - 1])") + } + l = r + } + } + + if l == nums.count - 1 { + res.append("\(nums[l])") + } else { + res.append("\(nums[l])->\(nums[nums.count - 1])") + } + + return res + } +} \ No newline at end of file diff --git a/swift/0230-kth-smallest-element-in-a-bst.swift b/swift/0230-kth-smallest-element-in-a-bst.swift new file mode 100644 index 000000000..e61a8396c --- /dev/null +++ b/swift/0230-kth-smallest-element-in-a-bst.swift @@ -0,0 +1,44 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + var globalCount: Int = 1 + var globalAnswer: Int = -1 + var limit: Int = 0 + + // Guaranteed k <= #Nodes + func kthSmallest(_ root: TreeNode?, _ k: Int) -> Int { + limit = k + inorderTraversal(from: root) + return globalAnswer + } + + private func inorderTraversal(from node: TreeNode?) { + // Base case + guard let node = node else { return } + + // Recurse on left child + inorderTraversal(from: node.left) + + // "visit" it + if globalCount == limit { + globalAnswer = node.val + } + globalCount += 1 + + // Recurse on right child + inorderTraversal(from: node.right) + } +} diff --git a/swift/0235-lowest-common-ancestor-of-a-binary-search-tree.swift b/swift/0235-lowest-common-ancestor-of-a-binary-search-tree.swift new file mode 100644 index 000000000..a21cdbfa2 --- /dev/null +++ b/swift/0235-lowest-common-ancestor-of-a-binary-search-tree.swift @@ -0,0 +1,13 @@ +class Solution { + func lowestCommonAncestor(_ root: TreeNode?, _ p: TreeNode?, _ q: TreeNode?) -> TreeNode? { + guard let root = root, let p = p, let q = q else { return nil } + if root.val < p.val && root.val < q.val { + return lowestCommonAncestor(root.right, p, q) + } else if root.val > p.val && root.val > q.val { + return lowestCommonAncestor(root.left, p, q) + } else { + return root + } + return nil + } +} diff --git a/swift/0238-product-of-array-except-self.swift b/swift/0238-product-of-array-except-self.swift new file mode 100644 index 000000000..761021d6c --- /dev/null +++ b/swift/0238-product-of-array-except-self.swift @@ -0,0 +1,21 @@ +/** +* Question Link: https://leetcode.com/problems/product-of-array-except-self/ +*/ + +class ProductExceptSelf { + func productExceptSelf(_ nums: [Int]) -> [Int] { + var res = [Int](repeating: 1, count: nums.count) + + var prefix = 1 + for i in 0.. [Int] { + var output = [Int]() + var deque = Deque() + var l = 0 + var r = 0 + + while r < nums.count { + while !deque.isEmpty && nums[deque.last!] < nums[r] { + deque.popRight() + } + deque.pushRight(r) + + if l > deque.first! { + deque.popLeft() + } + + if (r + 1) >= k { + output.append(nums[deque.first!]) + l += 1 + } + + r += 1 + } + + return output + } +} + +struct Deque { + private var storage: [Int?] + private var head: Int + private var capacity: Int + private let originalCapacity: Int + + var isEmpty: Bool { + storage.count - head == 0 + } + + var first: Int? { + if isEmpty { + return nil + } else { + return storage[head] + } + } + + var last: Int? { + if isEmpty { + return nil + } else { + return storage.last! + } + } + + init(capacity: Int = 10) { + self.capacity = max(capacity, 1) + self.storage = [Int?](repeating: nil, count: capacity) + self.originalCapacity = self.capacity + self.head = capacity + } + + mutating func pushLeft(_ value: Int) { + if head == 0 { + capacity *= 2 + let emptySpace = [Int?](repeating: nil, count: capacity) + storage.insert(contentsOf: emptySpace, at: 0) + head = capacity + } + + head -= 1 + storage[head] = value + } + + mutating func popLeft() { + guard + head < storage.count, + let value = storage[head] + else { + return + } + + storage[head] = nil + head += 1 + + if capacity >= originalCapacity, head >= capacity * 2 { + let emptySpace = capacity + capacity / 2 + storage.removeFirst(emptySpace) + head -= emptySpace + capacity /= 2 + } + } + + mutating func pushRight(_ value: Int) { + storage.append(value) + } + + mutating func popRight() { + storage.removeLast() + } +} \ No newline at end of file diff --git a/swift/0242-valid-anagram.swift b/swift/0242-valid-anagram.swift new file mode 100644 index 000000000..eff6bd0d4 --- /dev/null +++ b/swift/0242-valid-anagram.swift @@ -0,0 +1,37 @@ +/** + * Question Link: https://leetcode.com/problems/valid-anagram/ + */ + +class ValidAnagram { + func isAnagram(_ s: String, _ t: String) -> Bool { + if s.count != t.count { + return false + } + var countS = [Int:Int]() + var countT = [Int:Int]() + let sChars = Array(s) + let tChars = Array(t) + for i in 0.. Bool { + + // if the String lengths don't match, then they cannot be anagrams of each other + guard s.count == t.count else { return false } + + // sorting the String Characters puts them in the same order, which allows us to check equality + guard s.sorted() == t.sorted() else { return false } + + // if both guards succeed, then we have an anagram + return true + } +} diff --git a/swift/0252-meeting-rooms.swift b/swift/0252-meeting-rooms.swift new file mode 100644 index 000000000..9962e0b67 --- /dev/null +++ b/swift/0252-meeting-rooms.swift @@ -0,0 +1,24 @@ +class Solution { + func canAttendMeetings(_ intervals: [[Int]]) -> Bool { + if intervals.isEmpty { + return true + } + + var intervals = intervals.sorted(by: { $0[0] < $1[0] }) + var lastEnd = intervals[0][1] + + for i in 1..= lastEnd { + lastEnd = intervals[i][1] + continue + } else { + return false + } + } + + return true + } +} \ No newline at end of file diff --git a/swift/0253-meeting-rooms-ii.swift b/swift/0253-meeting-rooms-ii.swift new file mode 100644 index 000000000..23b945f9b --- /dev/null +++ b/swift/0253-meeting-rooms-ii.swift @@ -0,0 +1,23 @@ +class Solution { + func minMeetingRooms(_ intervals: [[Int]]) -> Int { + var start = intervals.map({ $0[0] }).sorted() + var end = intervals.map({ $0[1] }).sorted() + var res = 0 + var count = 0 + var s = 0 + var e = 0 + + while s < intervals.count { + if start[s] < end[e] { + s += 1 + count += 1 + } else { + e += 1 + count -= 1 + } + res = max(res, count) + } + + return res + } +} \ No newline at end of file diff --git a/swift/0261-graph-valid-tree.swift b/swift/0261-graph-valid-tree.swift new file mode 100644 index 000000000..1149f5979 --- /dev/null +++ b/swift/0261-graph-valid-tree.swift @@ -0,0 +1,69 @@ +class DisjointSet { + private var ranks: [Int] + private var roots: [Int] + + init(numVertices: Int) { + ranks = [Int](repeating: 0, count: numVertices) + roots = [Int](repeating: 0, count: numVertices) + + for i in 0.. rankY { // go into X + roots[rootY] = rootX + } else if rankY > rankX { // go into Y + roots[rootX] = rootY + } else { // go into X by default + roots[rootY] = rootX + ranks[rootX] += 1 + } + } + + private func find(of x: Int) -> Int { + if roots[x] == x { return x } + roots[x] = find(of: roots[x]) + return roots[x] + } + + public func areConnected(v1: Int, v2: Int) -> Bool { + find(of: v1) == find(of: v2) + } + + public func areDisjoint(v1: Int, v2: Int) -> Bool { + !areConnected(v1: v1, v2: v2) + } +} + +class Solution { + func validTree(_ n: Int, _ edges: [[Int]]) -> Bool { + // Check if n-1 edges + let numEdges = edges.count + guard numEdges == (n - 1) else { return false } + + // Check if connected => Can use DisjointSet/UnionFind + let ds = DisjointSet(numVertices: n) + for edge in edges { + let v1 = edge[0]; let v2 = edge[1] + guard ds.areDisjoint(v1: v1, v2: v2) else { + return false + } + ds.union(v1: v1, v2: v2) + } + + return true; + } +} + + diff --git a/swift/0263-ugly-number.swift b/swift/0263-ugly-number.swift new file mode 100644 index 000000000..c432e29d7 --- /dev/null +++ b/swift/0263-ugly-number.swift @@ -0,0 +1,15 @@ +class Solution { + func isUgly(_ n: Int) -> Bool { + if n <= 0 { + return false + } + + var num = n + for p in [2, 3, 5] { + while num % p == 0 { + num /= p + } + } + return num == 1 + } +} \ No newline at end of file diff --git a/swift/0268-missing-number.swift b/swift/0268-missing-number.swift new file mode 100644 index 000000000..d68ec5d73 --- /dev/null +++ b/swift/0268-missing-number.swift @@ -0,0 +1,10 @@ +class Solution { + func missingNumber(_ nums: [Int]) -> Int { + var res = nums.count + + for i in 0...nums.count-1 { + res += i - nums[i] + } + return res + } +} \ No newline at end of file diff --git a/swift/0269-alien-dictionary.swift b/swift/0269-alien-dictionary.swift new file mode 100644 index 000000000..5d19d0195 --- /dev/null +++ b/swift/0269-alien-dictionary.swift @@ -0,0 +1,57 @@ +/** + * Question Link: https://leetcode.com/problems/alien-dictionary/ + */ + + class Solution { + func alienOrder(_ words: [String]) -> String { + var adj = [Character: Set]() + for word in words { + for char in word { + adj[char] = [] + } + } + + for i in 0.. w2.count && w1.prefix(minLen) == w2.prefix(minLen) { + return "" + } + + for j in 0.. Bool { + if visited[char] != nil { + return visited[char]! + } + + visited[char] = true + + for neighChar in adj[char]! { + if dfs(neighChar) { + return true + } + } + visited[char] = false + res.append(char) + return false + } + + for char in adj.keys { + if dfs(char) { + return "" + } + } + + return String(res.reversed()) + } +} \ No newline at end of file diff --git a/swift/0271-encode-and-decode-strings.swift b/swift/0271-encode-and-decode-strings.swift new file mode 100644 index 000000000..c09e3465a --- /dev/null +++ b/swift/0271-encode-and-decode-strings.swift @@ -0,0 +1,30 @@ + +class Codec { + func encode(_ strs: [String]) -> String { + if strs.isEmpty { return "#" } + var counts = [String]() + for str in strs { + counts.append("\(str.count)") + } + return counts.joined(separator: ",") + "#" + strs.joined() + } + + func decode(_ s: String) -> [String] { + if s == "#" { return [] } + let index = s.firstIndex(of: "#")! + let counts = String(s[s.startIndex...s.index(before: index)]).components(separatedBy: ",") + var sIndex = s.index(after: index) + var decodedStrings = [String]() + for count in counts { + let endIndex = s.index(sIndex, offsetBy: Int(count)! - 1) + if sIndex > endIndex { + decodedStrings.append("") + continue + } + decodedStrings.append(String(s[sIndex...endIndex])) + sIndex = s.index(after: endIndex) + } + return decodedStrings + + } +} diff --git a/swift/0278-first-bad-version.swift b/swift/0278-first-bad-version.swift new file mode 100644 index 000000000..bae8981e3 --- /dev/null +++ b/swift/0278-first-bad-version.swift @@ -0,0 +1,26 @@ +/** + * Question Link: https://leetcode.com/problems/first-bad-version/ + */ + + /** + * The knows API is defined in the parent class VersionControl. + * func isBadVersion(_ version: Int) -> Bool{} + */ + +class Solution : VersionControl { + func firstBadVersion(_ n: Int) -> Int { + var l = 0 + var r = n + + while l <= r { + let mid = l + (r - l) / 2 + if isBadVersion(mid) { + r = mid - 1 + } else { + l = mid + 1 + } + } + + return l + (r - l) / 2 + } +} \ No newline at end of file diff --git a/swift/0283-Move-Zeroes.Swift b/swift/0283-Move-Zeroes.Swift new file mode 100644 index 000000000..12844b1aa --- /dev/null +++ b/swift/0283-Move-Zeroes.Swift @@ -0,0 +1,14 @@ +class Solution { + func moveZeroes(_ nums: inout [Int]) { + var index: Int = 0 + + for num in nums where num != 0 { + nums[index] = num + index += 1 + } + + for i in index..() + + while !queue.isEmpty { + for index in 0..= 0, row <= numRows - 1, + column >= 0, column <= numColumns - 1, + !visited.contains([row, column]), + rooms[row][column] == 2147483647 + else { continue } + + rooms[row][column] = distance + queue.append((row, column)) + visited.insert([row, column]) + } + } + distance += 1 + } + } +} \ No newline at end of file diff --git a/swift/0287-find-the-duplicate-number.swift b/swift/0287-find-the-duplicate-number.swift new file mode 100644 index 000000000..4fe78b2b7 --- /dev/null +++ b/swift/0287-find-the-duplicate-number.swift @@ -0,0 +1,21 @@ +class Solution { + func findDuplicate(_ nums: [Int]) -> Int { + var slow = 0, fast = 0 + while true { + slow = nums[slow] + fast = nums[nums[fast]] + if slow == fast { + break + } + } + + var slow2 = 0 + while true { + slow = nums[slow] + slow2 = nums[slow2] + if slow == slow2 { + return slow + } + } + } +} \ No newline at end of file diff --git a/swift/0295-find-median-from-data-stream.swift b/swift/0295-find-median-from-data-stream.swift new file mode 100644 index 000000000..46bfa7d77 --- /dev/null +++ b/swift/0295-find-median-from-data-stream.swift @@ -0,0 +1,97 @@ +/** + * Question Link: https://leetcode.com/problems/find-median-from-data-stream/ + */ + + class Heap { + var arr = [0] + + var count: Int { + arr.count - 1 + } + + var first: Int { + arr[1] + } + + func push(_ val: Int) { + arr.append(val) + var i = arr.count - 1 + while i > 1 && arr[i] < arr[i / 2] { + var tmp = arr[i] + arr[i] = arr[i / 2] + arr[i / 2] = tmp + i = i / 2 + } + } + + func pop() -> Int? { + if arr.count == 1 { + return nil + } + if arr.count == 2 { + return arr.popLast() + } + var res = arr[1] + arr[1] = arr.removeLast() + var i = 1 + while 2 * i < arr.count { + if 2 * i + 1 < arr.count && arr[2 * i + 1] < arr[2 * i] && arr[i] > arr[2 * i + 1] { + var tmp = arr[i] + arr[i] = arr[2 * i + 1] + arr[2 * i + 1] = tmp + i = 2 * i + 1 + } else if arr[i] > arr[2 * i] { + var tmp = arr[i] + arr[i] = arr[2 * i] + arr[2 * i] = tmp + i = 2 * i + } else { + break + } + } + return res + } +} + +class MedianFinder { + var small = Heap() + var large = Heap() + + init() { + + } + + func addNum(_ num: Int) { + small.push(-1 * num) + if small.count > 0 && large.count > 0 && (-1 * small.first) > large.first { + var val = -1 * small.pop()! + large.push(val) + } + + if small.count > large.count + 1 { + var val = -1 * small.pop()! + large.push(val) + } + + if large.count > small.count + 1 { + var val = large.pop()! + small.push(-1 * val) + } + } + + func findMedian() -> Double { + if small.count > large.count { + return Double(-1 * small.first) + } else if large.count > small.count { + return Double(large.first) + } + return (-1.0 * Double(small.first) + Double(large.first)) / 2.0 + } +} + +/** + * Your MedianFinder object will be instantiated and called as such: + * let obj = MedianFinder() + * obj.addNum(num) + * let ret_2: Double = obj.findMedian() + */ \ No newline at end of file diff --git a/swift/0297-serialize-and-deserialize-binary-tree.swift b/swift/0297-serialize-and-deserialize-binary-tree.swift new file mode 100644 index 000000000..946a353e5 --- /dev/null +++ b/swift/0297-serialize-and-deserialize-binary-tree.swift @@ -0,0 +1,48 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init(_ val: Int) { + * self.val = val + * self.left = nil + * self.right = nil + * } + * } + */ + +class Codec { + + func preorderTraversal(_ root: TreeNode?) -> String { + guard let root = root else { return "x" } + return "\(root.val)" + " " + preorderTraversal(root.left) + " " + preorderTraversal(root.right) + } + + func serialize(_ root: TreeNode?) -> String { + return preorderTraversal(root) + } + + func preorderTraversal(_ data: [String], _ index: inout Int) -> TreeNode? { + guard index < data.count else { return nil } + guard data[index] != "x" else { return nil } + guard let val = Int(data[index]) else { return nil } + let current = TreeNode(val) + index += 1 + current.left = preorderTraversal(data, &index) + index += 1 + current.right = preorderTraversal(data, &index) + return current + } + + func deserialize(_ data: String) -> TreeNode? { + guard !data.isEmpty else { return nil } + var index = 0 + return preorderTraversal(data.split(separator: " ").map { String($0) }, &index) + } +} + +// Your Codec object will be instantiated and called as such: +// var ser = Codec() +// var deser = Codec() +// deser.deserialize(ser.serialize(root)) \ No newline at end of file diff --git a/swift/0300-longest-increasing-subsequence.swift b/swift/0300-longest-increasing-subsequence.swift new file mode 100644 index 000000000..ed43bb37c --- /dev/null +++ b/swift/0300-longest-increasing-subsequence.swift @@ -0,0 +1,13 @@ +class Solution { + func lengthOfLIS(_ nums: [Int]) -> Int { + var dp = [Int](repeating: 1, count: nums.count) + for i in stride(from: nums.count - 1, to: -1, by: -1) { + for j in (i + 1).. Int { + let prefRight = pref[right] + let prefLeft = left > 0 ? pref[left - 1] : 0 + return (prefRight - prefLeft) + } +} + +/** + * Your NumArray object will be instantiated and called as such: + * let obj = NumArray(nums) + * let ret_1: Int = obj.sumRange(left, right) + */ \ No newline at end of file diff --git a/swift/0304-range-sum-query-2d-immutable.swift b/swift/0304-range-sum-query-2d-immutable.swift new file mode 100644 index 000000000..2f2e7b8e4 --- /dev/null +++ b/swift/0304-range-sum-query-2d-immutable.swift @@ -0,0 +1,40 @@ +/** + * Question Link: https://leetcode.com/problems/range-sum-query-2d-immutable/ + */ + + +class NumMatrix { + var pref: [[Int]] + + init(_ matrix: [[Int]]) { + let rows = matrix.count + let cols = matrix[0].count + pref = [[Int]](repeating: [Int](repeating: 0, count: cols + 1), count: rows + 1) + for r in 0.. Int { + var row1 = row1 + 1 + var col1 = col1 + 1 + var row2 = row2 + 1 + var col2 = col2 + 1 + let bottomRight = pref[row2][col2] + let above = pref[row1 - 1][col2] + let left = pref[row2][col1 - 1] + let topLeft = pref[row1 - 1][col1 - 1] + return bottomRight - above - left + topLeft + } +} + +/** + * Your NumMatrix object will be instantiated and called as such: + * let obj = NumMatrix(matrix) + * let ret_1: Int = obj.sumRegion(row1, col1, row2, col2) + */ \ No newline at end of file diff --git a/swift/0307-range-sum-query-mutable.swift b/swift/0307-range-sum-query-mutable.swift new file mode 100644 index 000000000..44d75d087 --- /dev/null +++ b/swift/0307-range-sum-query-mutable.swift @@ -0,0 +1,80 @@ +/** + * Question Link: https://leetcode.com/problems/range-sum-query-mutable/ + */ + + class SegmentTree { + var sum: Int + var left: SegmentTree! + var right: SegmentTree! + var l: Int + var r: Int + + init(_ total: Int, _ l: Int, _ r: Int) { + self.sum = total + self.l = l + self.r = r + } + + static func build(_ nums: [Int], _ l: Int, _ r: Int) -> SegmentTree { + if l == r { + return SegmentTree(nums[l], l, r) + } + let m = (l + r) / 2 + let root = SegmentTree(0, l, r) + root.left = SegmentTree.build(nums, l, m) + root.right = SegmentTree.build(nums, m + 1, r) + root.sum = root.left.sum + root.right.sum + return root + } + + func update(_ index: Int, _ val: Int) { + if l == r { + sum = val + return + } + let m = (l + r) / 2 + if index > m { + right.update(index, val) + } else { + left.update(index, val) + } + sum = left.sum + right.sum + } + + func rangeQuery(_ l: Int, _ r: Int) -> Int { + if self.l == l && self.r == r { + return sum + } + let m = (self.l + self.r) / 2 + if l > m { + return right.rangeQuery(l, r) + } else if r <= m { + return left.rangeQuery(l, r) + } else { + return left.rangeQuery(l, m) + right.rangeQuery(m + 1, r) + } + } +} + +class NumArray { + let segmentTree: SegmentTree + + init(_ nums: [Int]) { + segmentTree = SegmentTree.build(nums, 0, nums.count - 1) + } + + func update(_ index: Int, _ val: Int) { + segmentTree.update(index, val) + } + + func sumRange(_ left: Int, _ right: Int) -> Int { + segmentTree.rangeQuery(left, right) + } +} + +/** + * Your NumArray object will be instantiated and called as such: + * let obj = NumArray(nums) + * obj.update(index, val) + * let ret_2: Int = obj.sumRange(left, right) + */ \ No newline at end of file diff --git a/swift/0309-best-time-to-buy-and-sell-stock-with-cooldown.swift b/swift/0309-best-time-to-buy-and-sell-stock-with-cooldown.swift new file mode 100644 index 000000000..9bdf24b26 --- /dev/null +++ b/swift/0309-best-time-to-buy-and-sell-stock-with-cooldown.swift @@ -0,0 +1,13 @@ +class Solution { + func maxProfit(_ prices: [Int]) -> Int { + var sold = 0, hold = -1000, rest = 0 + + for i in 0...prices.count-1 { + let prevSold = sold + sold = hold + prices[i] + hold = max(hold, rest - prices[i]) + rest = max(rest, prevSold) + } + return max(sold, rest) + } +} \ No newline at end of file diff --git a/swift/0312-burst-balloons.swift b/swift/0312-burst-balloons.swift new file mode 100644 index 000000000..8df2fac0d --- /dev/null +++ b/swift/0312-burst-balloons.swift @@ -0,0 +1,26 @@ +class Solution { + func maxCoins(_ nums: [Int]) -> Int { + var nums = [1] + nums + [1] + var dp = [[Int]: Int]() + + func dfs(_ l: Int, _ r: Int) -> Int { + if l > r { + return 0 + } + if dp[[l, r]] != nil { + return dp[[l, r]]! + } + + dp[[l, r]] = 0 + for i in l...r { + var coins = nums[l - 1] * nums[i] * nums[r + 1] + coins += dfs(l, i - 1) + dfs(i + 1, r) + dp[[l, r]] = max(dp[[l, r]]!, coins) + } + + return dp[[l, r]]! + } + + return dfs(1, nums.count - 2) + } +} \ No newline at end of file diff --git a/swift/0322-coin-change.swift b/swift/0322-coin-change.swift new file mode 100644 index 000000000..3aca7dba2 --- /dev/null +++ b/swift/0322-coin-change.swift @@ -0,0 +1,18 @@ +/** + * Question Link: https://leetcode.com/problems/coin-change/ + */ + + class Solution { + func coinChange(_ coins: [Int], _ amount: Int) -> Int { + var dp = [Int](repeating: amount + 1, count: amount + 1) + dp[0] = 0 + for a in 1..= 0 { + dp[a] = min(dp[a], (1 + dp[a - c])) + } + } + } + return dp[amount] != amount + 1 ? dp[amount] : -1 + } +} \ No newline at end of file diff --git a/swift/0323-number-of-connected-components-in-an-undirected-graph.swift b/swift/0323-number-of-connected-components-in-an-undirected-graph.swift new file mode 100644 index 000000000..0c0eb6092 --- /dev/null +++ b/swift/0323-number-of-connected-components-in-an-undirected-graph.swift @@ -0,0 +1,67 @@ +class DisjointSet { + private var ranks: [Int] + private var roots: [Int] + + init(numVertices: Int) { + ranks = [Int](repeating: 0, count: numVertices) + roots = [Int](repeating: 0, count: numVertices) + + for i in 0.. rankY { // go into X + roots[rootY] = rootX + } else if rankY > rankX { // go into Y + roots[rootX] = rootY + } else { // go into X by default + roots[rootY] = rootX + ranks[rootX] += 1 + } + } + + private func find(of x: Int) -> Int { + if roots[x] == x { return x } + roots[x] = find(of: roots[x]) + return roots[x] + } + + public func areConnected(v1: Int, v2: Int) -> Bool { + find(of: v1) == find(of: v2) + } + + public func areDisjoint(v1: Int, v2: Int) -> Bool { + !areConnected(v1: v1, v2: v2) + } + + private func isRoot(_ x: Int) -> Bool { + return find(of: x) == x + } + + public var numConnectedComponents: Int { + Array(0.. Int { + let ds = DisjointSet(numVertices: n) + + edges.forEach { edge in + ds.union(v1: edge[0], v2: edge[1]) + } + + return ds.numConnectedComponents + } +} diff --git a/swift/0329-longest-increasing-path-in-a-matrix.swift b/swift/0329-longest-increasing-path-in-a-matrix.swift new file mode 100644 index 000000000..e01e54ea5 --- /dev/null +++ b/swift/0329-longest-increasing-path-in-a-matrix.swift @@ -0,0 +1,30 @@ +class Solution { + func longestIncreasingPath(_ matrix: [[Int]]) -> Int { + let rows = matrix.count, cols = matrix[0].count + var dp = [[Int]: Int]() + + func dfs(_ r: Int, _ c: Int, _ prev: Int) -> Int { + if r < 0 || r == rows || c < 0 || c == cols || matrix[r][c] <= prev { + return 0 + } + if dp[[r, c]] != nil { + return dp[[r, c]]! + } + var res = 1 + res = max(res, 1 + dfs(r + 1, c, matrix[r][c])) + res = max(res, 1 + dfs(r - 1, c, matrix[r][c])) + res = max(res, 1 + dfs(r, c + 1, matrix[r][c])) + res = max(res, 1 + dfs(r, c - 1, matrix[r][c])) + dp[[r, c]] = res + return res + } + + for r in 0.. [String] { + var adj: [String: [String]] = [:] + for t in tickets { + let (src, dst) = (t[0], t[1]) + adj[src, default: []].append(dst) + } + + for src in adj.keys { + adj[src]?.sort { $0 > $1 } + } + + var res = [String]() + func dfs(_ src: String) { + while let dst = adj[src]?.popLast() { + dfs(dst) + } + res.append(src) + } + + dfs("JFK") + return res.reversed() + } +} \ No newline at end of file diff --git a/swift/0338-counting-bits.swift b/swift/0338-counting-bits.swift new file mode 100644 index 000000000..52cf7e7cc --- /dev/null +++ b/swift/0338-counting-bits.swift @@ -0,0 +1,19 @@ +class Solution { + func countBits(_ n: Int) -> [Int] { + var result: [Int] = [] + for x in 0 ..< n + 1 { + result.append(popCount(x)) + } + return result + } + + func popCount(_ x: Int) -> Int { + var x = x + var count = 0 + while x != 0 { + x &= x - 1 + count += 1 + } + return count + } +} diff --git a/swift/0344-reverse-string.swift b/swift/0344-reverse-string.swift new file mode 100644 index 000000000..26ee4500f --- /dev/null +++ b/swift/0344-reverse-string.swift @@ -0,0 +1,16 @@ +class Solution { + func reverseString(_ s: inout [Character]) { + var left: Int = 0 + var right: Int = s.count - 1 + + while left < right { + let temp: Character = s[left] + + s[left] = s[right] + s[right] = temp + + left += 1 + right -= 1 + } + } +} diff --git a/swift/0347-top-k-frequent-elements.swift b/swift/0347-top-k-frequent-elements.swift new file mode 100644 index 000000000..237735a8a --- /dev/null +++ b/swift/0347-top-k-frequent-elements.swift @@ -0,0 +1,32 @@ +/** + * Question Link: https://leetcode.com/problems/top-k-frequent-elements/ + */ + +class TopKFrequentElements { + func topKFrequent(_ nums: [Int], _ k: Int) -> [Int] { + var count = [Int:Int]() + for n in nums { + guard count[n] != nil else { + count[n] = 1 + continue + } + count[n] = 1 + count[n]! + } + + var freq = [[Int]](repeating: [], count: nums.count+1) + for (n, c) in count { + freq[c].append(n) + } + + var res = [Int]() + for i in stride(from: freq.count-1, to: 0, by: -1) { + for n in freq[i] { + res.append(n) + if res.count == k { + return res + } + } + } + return res + } +} diff --git a/swift/0355-design-twitter.swift b/swift/0355-design-twitter.swift new file mode 100644 index 000000000..19292bb9c --- /dev/null +++ b/swift/0355-design-twitter.swift @@ -0,0 +1,141 @@ +class Heap { + var heap = [[0]] + + var count: Int { + heap.count - 1 + } + + var first: [Int] { + heap[1] + } + + func heapify(_ arr: [[Int]]) { + heap = arr + heap.append(arr[0]) + var cur = (heap.count - 1) / 2 + while cur > 0 { + var i = cur + while 2 * i < heap.count { + if 2 * i + 1 < heap.count && heap[2 * i + 1][0] < heap[2 * i][0] && heap[i][0] > heap[2 * i + 1][0] { + let tmp = heap[i] + heap[i] = heap[2 * i + 1] + heap[2 * i + 1] = tmp + i = 2 * i + 1 + } else if heap[i][0] > heap[2 * i][0] { + let tmp = heap[i] + heap[i] = heap[2 * i] + heap[2 * i] = tmp + i = 2 * i + } else { + break + } + } + cur -= 1 + } + } + + func push(_ val: [Int]) { + heap.append(val) + var i = heap.count - 1 + while i > 1 && heap[i][0] < heap[i / 2][0] { + let tmp = heap[i] + heap[i] = heap[i / 2] + heap[i / 2] = tmp + i = i / 2 + } + } + + func pop() -> [Int]? { + if heap.count == 1 { + return nil + } + if heap.count == 2 { + return heap.popLast() + } + let res = heap[1] + heap[1] = heap.removeLast() + var i = 1 + while 2 * i < heap.count { + if 2 * i + 1 < heap.count && heap[2 * i + 1][0] < heap[2 * i][0] && heap[i][0] > heap[2 * i + 1][0] { + let tmp = heap[i] + heap[i] = heap[2 * i + 1] + heap[2 * i + 1] = tmp + i = 2 * i + 1 + } else if heap[i][0] > heap[2 * i][0] { + let tmp = heap[i] + heap[i] = heap[2 * i] + heap[2 * i] = tmp + i = 2 * i + } else { + break + } + } + return res + } +} + +class Twitter { + var count = 0 + var tweetMap = [Int: [[Int]]]() + var followMap = [Int: Set]() + + init() { + + } + + func postTweet(_ userId: Int, _ tweetId: Int) { + tweetMap[userId, default: []].append([count, tweetId]) + count -= 1 + } + + func getNewsFeed(_ userId: Int) -> [Int] { + var res = [Int]() + var heap = Heap() + + followMap[userId, default: Set()].insert(userId) + for followeeId in followMap[userId, default: Set()] { + if tweetMap[followeeId] != nil { + let index = tweetMap[followeeId]!.count - 1 + let val = tweetMap[followeeId]![index] + let count = val[0] + let tweetId = val[1] + heap.push([count, tweetId, followeeId, index - 1]) + } + } + + while heap.count > 0 && res.count < 10 { + let val = heap.pop() + var count = val![0] + var tweetId = val![1] + let followeeId = val![2] + let index = val![3] + res.append(tweetId) + + if index >= 0 { + let val = tweetMap[followeeId]![index] + count = val[0] + tweetId = val[1] + heap.push([count, tweetId, followeeId, index - 1]) + } + } + + return res + } + + func follow(_ followerId: Int, _ followeeId: Int) { + followMap[followerId, default: Set()].insert(followeeId) + } + + func unfollow(_ followerId: Int, _ followeeId: Int) { + followMap[followerId]?.remove(followeeId) + } +} + +/** + * Your Twitter object will be instantiated and called as such: + * let obj = Twitter() + * obj.postTweet(userId, tweetId) + * let ret_2: [Int] = obj.getNewsFeed(userId) + * obj.follow(followerId, followeeId) + * obj.unfollow(followerId, followeeId) + */ \ No newline at end of file diff --git a/swift/0367-valid-perfect-square.swift b/swift/0367-valid-perfect-square.swift new file mode 100644 index 000000000..c9b68e872 --- /dev/null +++ b/swift/0367-valid-perfect-square.swift @@ -0,0 +1,20 @@ +// Time: O(log n) +// Space: O(1) +class Solution { + func isPerfectSquare(_ num: Int) -> Bool { + var left = 1, right = num + while left <= right { + let mid = (left + right) / 2 + if mid * mid > num { + right = mid - 1 + } + else if mid * mid < num { + left = mid + 1 + } + else { + return true + } + } + return false + } +} \ No newline at end of file diff --git a/swift/0371-sum-of-two-integers.swift b/swift/0371-sum-of-two-integers.swift new file mode 100644 index 000000000..12a7abe59 --- /dev/null +++ b/swift/0371-sum-of-two-integers.swift @@ -0,0 +1,13 @@ +class Solution { + func getSum(_ a: Int, _ b: Int) -> Int { + var res = a + var secondInt = b + + while secondInt != 0 { + let temp = (res & secondInt) << 1 + res ^= secondInt + secondInt = temp + } + return res + } +} \ No newline at end of file diff --git a/swift/0374-guess-number-higher-or-lower.swift b/swift/0374-guess-number-higher-or-lower.swift new file mode 100644 index 000000000..d461f6ce1 --- /dev/null +++ b/swift/0374-guess-number-higher-or-lower.swift @@ -0,0 +1,23 @@ +// Time: O(log n) +// Space: O(1) +class Solution : GuessGame { + func guessNumber(_ n: Int) -> Int { + // return a num btw 1,..,n + var low = 1 + var high = n + + while true { + let mid = low + (high - low) / 2 + let myGuess = guess(mid) + if myGuess == 1 { + low = mid + 1 + } + else if myGuess == -1 { + high = mid - 1 + } + else { + return mid + } + } + } +} \ No newline at end of file diff --git a/swift/0380-insert-delete-getrandom-o1.swift b/swift/0380-insert-delete-getrandom-o1.swift new file mode 100644 index 000000000..e6efe38ac --- /dev/null +++ b/swift/0380-insert-delete-getrandom-o1.swift @@ -0,0 +1,37 @@ +class RandomizedSet { + var random = Set() + + init() { + + } + + func insert(_ val: Int) -> Bool { + if !random.contains(val){ + random.insert(val) + return true + } else { + return false + } + } + + func remove(_ val: Int) -> Bool { + if random.contains(val) { + random.remove(val) + return true + } else { + return false + } + } + + func getRandom() -> Int { + return random.randomElement()! + } +} + +/** + * Your RandomizedSet object will be instantiated and called as such: + * let obj = RandomizedSet() + * let ret_1: Bool = obj.insert(val) + * let ret_2: Bool = obj.remove(val) + * let ret_3: Int = obj.getRandom() + */ diff --git a/swift/0392-is-subsequence.swift b/swift/0392-is-subsequence.swift new file mode 100644 index 000000000..b5fa203f5 --- /dev/null +++ b/swift/0392-is-subsequence.swift @@ -0,0 +1,24 @@ + // Time: O(s + t) + // Space: O(s) + +class Solution { + func isSubsequence(_ s: String, _ t: String) -> Bool { + if s.isEmpty { + return true + } + + var s = Array(s) + var i = 0 + + for c in t { + if c == s[i] { + i += 1 + if i == s.count { + return true + } + } + } + + return false + } +} \ No newline at end of file diff --git a/swift/0416-partition-equal-subset-sum.swift b/swift/0416-partition-equal-subset-sum.swift new file mode 100644 index 000000000..fcf17bd36 --- /dev/null +++ b/swift/0416-partition-equal-subset-sum.swift @@ -0,0 +1,28 @@ +/** + * Question Link: https://leetcode.com/problems/partition-equal-subset-sum/ + */ + + class Solution { + func canPartition(_ nums: [Int]) -> Bool { + let sum = nums.reduce(0, +) + if sum % 2 != 0 { + return false + } + + var dp = Set() + dp.insert(0) + let target = sum / 2 + for i in stride(from: nums.count - 1, to: -1, by: -1) { + var nextDP = Set() + for t in dp { + if t + nums[i] == target { + return true + } + nextDP.insert(t + nums[i]) + nextDP.insert(t) + } + dp = nextDP + } + return false + } +} \ No newline at end of file diff --git a/swift/0417-pacific-atlantic-water-flow.swift b/swift/0417-pacific-atlantic-water-flow.swift new file mode 100644 index 000000000..e08170439 --- /dev/null +++ b/swift/0417-pacific-atlantic-water-flow.swift @@ -0,0 +1,40 @@ +class Solution { + func pacificAtlantic(_ heights: [[Int]]) -> [[Int]] { + let rows = heights.count + let cols = heights[0].count + var pac = Set<[Int]>() + var atl = Set<[Int]>() + var res = [[Int]]() + + func dfs(_ r: Int, _ c: Int, _ visit: inout Set<[Int]>, _ prevHeight: Int) { + if r < 0 || c < 0 || r == rows || c == cols || visit.contains([r, c]) || heights[r][c] < prevHeight { + return + } + visit.insert([r, c]) + dfs(r + 1, c, &visit, heights[r][c]) + dfs(r - 1, c, &visit, heights[r][c]) + dfs(r, c + 1, &visit, heights[r][c]) + dfs(r, c - 1, &visit, heights[r][c]) + } + + for c in 0.. Int { + var count: [Character: Int] = [:] + var strArray = Array(s) + var result = 0 + var l = 0 + var maxF = 0 + + for r in 0...s.count - 1 { + count[strArray[r]] = count[strArray[r], default: 0] + 1 + maxF = max(maxF, count[strArray[r], default: 0]) + + while (r - l + 1) - maxF > k { + count[strArray[l]] = count[strArray[l], default: 0] - 1 + l += 1 + } + result = max(result, r - l + 1) + } + return result + } +} diff --git a/swift/0435-non-overlapping-intervals.swift b/swift/0435-non-overlapping-intervals.swift new file mode 100644 index 000000000..64f350846 --- /dev/null +++ b/swift/0435-non-overlapping-intervals.swift @@ -0,0 +1,18 @@ +class Solution { + func eraseOverlapIntervals(_ intervals: [[Int]]) -> Int { + var intervals = intervals.sorted(by: { $0[0] < $1[0] }) + var res = 0 + var lastEnd = intervals[0][1] + for i in 1..= lastEnd { + lastEnd = end + } else { + res += 1 + lastEnd = min(lastEnd, end) + } + } + return res + } +} \ No newline at end of file diff --git a/swift/0450-delete-node-in-a-bst.swift b/swift/0450-delete-node-in-a-bst.swift new file mode 100644 index 000000000..2f3e0ad39 --- /dev/null +++ b/swift/0450-delete-node-in-a-bst.swift @@ -0,0 +1,51 @@ +/** + * Question Link: https://leetcode.com/problems/delete-node-in-a-bst/ + */ + + /** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func deleteNode(_ root: TreeNode?, _ key: Int) -> TreeNode? { + if root == nil { + return root + } + + if root!.val > key { + root?.left = deleteNode(root?.left, key) + } else if root!.val < key { + root?.right = deleteNode(root?.right, key) + } else { + if root?.left == nil { + return root?.right + } else if root?.right == nil { + return root?.left + } else { + let minNode = minNode(root: root?.right) + root?.val = minNode!.val + root?.right = deleteNode(root?.right, minNode!.val) + } + } + return root + } + + func minNode(root: TreeNode?) -> TreeNode? { + var cur = root + while cur != nil && cur?.left != nil { + cur = cur?.left + } + return cur + } +} \ No newline at end of file diff --git a/swift/0474-ones-and-zeroes.swift b/swift/0474-ones-and-zeroes.swift new file mode 100644 index 000000000..24319d8e2 --- /dev/null +++ b/swift/0474-ones-and-zeroes.swift @@ -0,0 +1,21 @@ +/** + * Question Link: https://leetcode.com/problems/ones-and-zeroes/ + */ + + class Solution { + func findMaxForm(_ strs: [String], _ m: Int, _ n: Int) -> Int { + var dp = [[Int]: Int]() + + for s in strs { + let mCnt = s.filter({ $0 == "0"}).count + let nCnt = s.filter({ $0 == "1"}).count + for i in stride(from: m, to: mCnt - 1, by: -1) { + for j in stride(from: n, to: nCnt - 1, by: -1) { + dp[[i, j]] = max(1 + (dp[[i - mCnt, j - nCnt]] ?? 0), dp[[i, j]] ?? 0) + } + } + } + + return dp[[m, n]] ?? 0 + } +} \ No newline at end of file diff --git a/swift/0480-sliding-window-median.swift b/swift/0480-sliding-window-median.swift new file mode 100644 index 000000000..e5958ff74 --- /dev/null +++ b/swift/0480-sliding-window-median.swift @@ -0,0 +1,127 @@ +/** + * Question Link: https://leetcode.com/problems/sliding-window-median/ + */ + + class Heap { + var heap = [0] + + var count: Int { + heap.count - 1 + } + + var first: Int { + heap[1] + } + + func push(_ val: Int) { + heap.append(val) + var i = heap.count - 1 + while i > 1 && heap[i] < heap[i / 2] { + var tmp = heap[i] + heap[i] = heap[i / 2] + heap[i / 2] = tmp + i = i / 2 + } + } + + func pop() -> Int? { + if heap.count == 1 { + return nil + } + if heap.count == 2 { + return heap.removeLast() + } + var res = heap[1] + heap[1] = heap.removeLast() + var i = 1 + while 2 * i < heap.count { + if 2 * i + 1 < heap.count && heap[2 * i + 1] < heap[2 * i] && heap[i] > heap[2 * i + 1] { + var tmp = heap[i] + heap[i] = heap[2 * i + 1] + heap[2 * i + 1] = tmp + i = 2 * i + 1 + } else if heap[i] > heap[2 * i] { + var tmp = heap[i] + heap[i] = heap[2 * i] + heap[2 * i] = tmp + i = 2 * i + } else { + break + } + } + return res + } +} + +class Solution { + func medianSlidingWindow(_ nums: [Int], _ k: Int) -> [Double] { + let small = Heap() + let large = Heap() + + var medians = [Double]() + var hashtable = [Int: Int]() + + var i = 0 + while i < k { + small.push(-1 * nums[i]) + i += 1 + } + for j in 0..<(k / 2) { // maybe wrong range + large.push(-1 * small.first) + _ = small.pop() + } + + while true { + if k & 1 == 1 { // maybe should use 0 + medians.append(Double(-1 * small.first)) + } else { + medians.append((-1.0 * Double(small.first) + Double(large.first)) * 0.5) + } + if i >= nums.count { + break + } + + let outNum = nums[i - k] + let inNum = nums[i] + var balance = 0 + + if outNum <= -1 * small.first { + balance += -1 + } else { + balance += 1 + } + hashtable[outNum, default: 0] += 1 + + if small.count > 0 && inNum <= -1 * small.first { + balance += 1 + small.push(-1 * inNum) + } else { + balance -= 1 + large.push(inNum) + } + i += 1 + + if balance < 0 { + small.push(-1 * large.first) + large.pop() + balance += 1 + } + if balance > 0 { + large.push(-1 * small.first) + small.pop() + balance -= 1 + } + + while hashtable[-1 * small.first] != nil && hashtable[-1 * small.first]! > 0 { + hashtable[-1 * small.first, default: 0] -= 1 + small.pop() + } + while large.count > 0 && hashtable[large.first] != nil && hashtable[large.first]! > 0 { + hashtable[large.first, default: 0] -= 1 + large.pop() + } + } + + return medians + } +} \ No newline at end of file diff --git a/swift/0494-target-sum.swift b/swift/0494-target-sum.swift new file mode 100644 index 000000000..4208a4224 --- /dev/null +++ b/swift/0494-target-sum.swift @@ -0,0 +1,22 @@ +/** + * Question Link: https://leetcode.com/problems/target-sum/ + */ + + class Solution { + func findTargetSumWays(_ nums: [Int], _ target: Int) -> Int { + var dp = [[Int]: Int]() + + func dfs(_ i: Int, _ total: Int) -> Int { + if i == nums.count { + return total == target ? 1 : 0 + } + if dp[[i, total]] != nil { + return dp[[i, total]]! + } + dp[[i, total]] = dfs(i + 1, total + nums[i]) + dfs(i + 1, total - nums[i]) + return dp[[i, total]]! + } + + return dfs(0, 0) + } +} \ No newline at end of file diff --git a/swift/0502-ipo.swift b/swift/0502-ipo.swift new file mode 100644 index 000000000..1262ce0ac --- /dev/null +++ b/swift/0502-ipo.swift @@ -0,0 +1,104 @@ +/** + * Question Link: https://leetcode.com/problems/ipo/ + */ + + class Heap { + var heap = [[0]] + + var count: Int { + heap.count - 1 + } + + var first: [Int] { + heap[1] + } + + func heapify(_ val: [[Int]]) { + var val = val + val.append(val[0]) + heap = val + var cur = (heap.count - 1) / 2 + while cur > 0 { + var i = cur + while 2 * i < heap.count { + if 2 * i + 1 < heap.count && heap[2 * i + 1][0] < heap[2 * i][0] && heap[i][0] > heap[2 * i + 1][0] { + var tmp = heap[i] + heap[i] = heap[2 * i + 1] + heap[2 * i + 1] = tmp + i = 2 * i + 1 + } else if heap[i][0] > heap[2 * i][0] { + var tmp = heap[i] + heap[i] = heap[2 * i] + heap[2 * i] = tmp + i = 2 * i + } else { + break + } + } + cur -= 1 + } + } + + func push(_ val: [Int]) { + heap.append(val) + var i = heap.count - 1 + while i > 1 && heap[i][0] < heap[i / 2][0] { + var tmp = heap[i] + heap[i] = heap[i / 2] + heap[i / 2] = tmp + i = i / 2 + } + } + + func pop() -> [Int]? { + if heap.count == 1 { + return nil + } + if heap.count == 2 { + return heap.removeLast() + } + var res = heap[1] + heap[1] = heap.removeLast() + var i = 1 + while 2 * i < heap.count { + if 2 * i + 1 < heap.count && heap[2 * i + 1][0] < heap[2 * i][0] && heap[i][0] > heap[2 * i + 1][0] { + var tmp = heap[i] + heap[i] = heap[2 * i + 1] + heap[2 * i + 1] = tmp + i = 2 * i + 1 + } else if heap[i][0] > heap[2 * i][0] { + var tmp = heap[i] + heap[i] = heap[2 * i] + heap[2 * i] = tmp + i = 2 * i + } else { + break + } + } + return res + } +} + +class Solution { + func findMaximizedCapital(_ k: Int, _ w: Int, _ profits: [Int], _ capital: [Int]) -> Int { + let maxProfit = Heap() + let minCapital = Heap() + var cap = [[Int]]() + for (c, p) in zip(capital, profits) { + cap.append([c, p]) + } + minCapital.heapify(cap) + var res = w + for i in 0.. 0 && minCapital.first[0] <= res { + let cp = minCapital.pop()! + maxProfit.push([-1 * cp[1]]) + } + if maxProfit.count <= 0 { + break + } + res += -1 * maxProfit.pop()![0] + } + return res + } +} \ No newline at end of file diff --git a/swift/0503-next-greater-element-ii.swift b/swift/0503-next-greater-element-ii.swift new file mode 100644 index 000000000..7946ea6b9 --- /dev/null +++ b/swift/0503-next-greater-element-ii.swift @@ -0,0 +1,19 @@ +class Solution { + func nextGreaterElements(_ nums: [Int]) -> [Int] { + var res = Array(repeating: -1, count: nums.count) + var stack = [Int]() + + for i in 0.. Int { + let goldenRatio = (1 + 5.0.squareRoot()) / 2.0 + return Int(round(pow(goldenRatio, Double(n)) / 5.0.squareRoot())) + } +} \ No newline at end of file diff --git a/swift/0516-longest-palindromic-subsequence.swift b/swift/0516-longest-palindromic-subsequence.swift new file mode 100644 index 000000000..192f89f83 --- /dev/null +++ b/swift/0516-longest-palindromic-subsequence.swift @@ -0,0 +1,26 @@ +/** + * Question Link: https://leetcode.com/problems/longest-palindromic-subsequence/ + */ + + class Solution { + func longestPalindromeSubseq(_ s: String) -> Int { + let s = Array(s) + let n = s.count + var dp = Array(repeating: 0, count: n) + var dpPrev = Array(repeating: 0, count: n) + + for i in stride(from: s.count - 1, to: -1, by: -1) { + dp[i] = 1 + for j in i + 1.. Int { + var dp = [Int](repeating: 0, count: amount + 1) + dp[0] = 1 + + for i in stride(from: coins.count - 1, to: -1, by: -1) { + var nextDP = [Int](repeating: 0, count: amount + 1) + nextDP[0] = 1 + + for a in 1..= 0 { + nextDP[a] += nextDP[a - coins[i]] + } + } + + dp = nextDP + } + + return dp[amount] + } +} \ No newline at end of file diff --git a/swift/0535-Encode-and-Decode-TinyURL.Swift b/swift/0535-Encode-and-Decode-TinyURL.Swift new file mode 100644 index 000000000..13c00e071 --- /dev/null +++ b/swift/0535-Encode-and-Decode-TinyURL.Swift @@ -0,0 +1,28 @@ +class Codec { + var urlMap = [String: String]() + + // Encodes a URL to a shortened URL. + func encode(_ longUrl: String) -> String { + var id = UUID().uuidString + + if urlMap[id] != nil { + id = UUID().uuidString + } + + urlMap[id] = longUrl + return "http://tinyurl.com/\(id)" + } + + // Decodes a shortened URL to its original URL. + func decode(_ shortUrl: String) -> String { + let id = shortUrl.split(separator:"/").last! + return urlMap[String(id)]! + } +} + +/** + * Your Codec object will be instantiated and called as such: + * let obj = Codec() + * val s = obj.encode(longUrl) + * let ans = obj.decode(s) +*/ diff --git a/swift/0535-encode-and-decode-tinyurl.swift b/swift/0535-encode-and-decode-tinyurl.swift new file mode 100644 index 000000000..13c00e071 --- /dev/null +++ b/swift/0535-encode-and-decode-tinyurl.swift @@ -0,0 +1,28 @@ +class Codec { + var urlMap = [String: String]() + + // Encodes a URL to a shortened URL. + func encode(_ longUrl: String) -> String { + var id = UUID().uuidString + + if urlMap[id] != nil { + id = UUID().uuidString + } + + urlMap[id] = longUrl + return "http://tinyurl.com/\(id)" + } + + // Decodes a shortened URL to its original URL. + func decode(_ shortUrl: String) -> String { + let id = shortUrl.split(separator:"/").last! + return urlMap[String(id)]! + } +} + +/** + * Your Codec object will be instantiated and called as such: + * let obj = Codec() + * val s = obj.encode(longUrl) + * let ans = obj.decode(s) +*/ diff --git a/swift/0543-diameter-of-binary-tree.swift b/swift/0543-diameter-of-binary-tree.swift new file mode 100644 index 000000000..bf47f41d3 --- /dev/null +++ b/swift/0543-diameter-of-binary-tree.swift @@ -0,0 +1,40 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + private var globalMaxDiameter: Int = 0 + + // Additionally, update the globalDiameter here + func getMaxDepth(_ node: TreeNode?) -> Int { + guard let node = node else { return 0 } + + // compute for each child + let leftMax = getMaxDepth(node.left) + let rightMax = getMaxDepth(node.right) + + // update diameter + let diameter = leftMax + rightMax + self.globalMaxDiameter = max(self.globalMaxDiameter, diameter) + + // return max depth of 'this' node + return 1 + max(leftMax, rightMax) + } + + func diameterOfBinaryTree(_ root: TreeNode?) -> Int { + self.globalMaxDiameter = 0 + getMaxDepth(root) + return self.globalMaxDiameter + } +} diff --git a/swift/0560-subarray-sum-equals-k.swift b/swift/0560-subarray-sum-equals-k.swift new file mode 100644 index 000000000..a63b9c658 --- /dev/null +++ b/swift/0560-subarray-sum-equals-k.swift @@ -0,0 +1,18 @@ +/** + * Question Link: https://leetcode.com/problems/subarray-sum-equals-k/ + */ + + class Solution { + func subarraySum(_ nums: [Int], _ k: Int) -> Int { + var res = 0 + var curSum = 0 + var hashmap = [0: 1] + for n in nums { + curSum += n + var diff = curSum - k + res += hashmap[diff, default: 0] + hashmap[curSum] = 1 + hashmap[curSum, default: 0] + } + return res + } +} \ No newline at end of file diff --git a/swift/0567-permutation-in-string.swift b/swift/0567-permutation-in-string.swift new file mode 100644 index 000000000..d1157e920 --- /dev/null +++ b/swift/0567-permutation-in-string.swift @@ -0,0 +1,55 @@ +/** + * Question Link: https://leetcode.com/problems/permutation-in-string/ + */ + + class PermutationInString { + func checkInclusion(_ s1: String, _ s2: String) -> Bool { + if s1.count > s2.count { + return false + } + + var s1Array = Array(s1) + var s2Array = Array(s2) + var arr1 = [Int](repeating: 0, count: 26) + var arr2 = [Int](repeating: 0, count: 26) + + for i in 0.. Bool { + // if both are nil, they are same + if node1 == nil && node2 == nil { return true } + + // if anyone else is nil, they are not the same + guard let node1 = node1 else { return false } + guard let node2 = node2 else { return false } + + return ( + (node1.val == node2.val) && + areSameTrees(node1.left, node2.left) && + areSameTrees(node1.right, node2.right) + ) + } + + func isSubtree(_ root: TreeNode?, _ subRoot: TreeNode?) -> Bool { + // Base cases (both can be nil, just any one can't) + if root == nil && subRoot == nil { return true } + guard let root = root else { return false } + guard let subRoot = subRoot else { return false } + + // Base case: Exactly same tree + if areSameTrees(root, subRoot) { return true } + + // Recursive + return isSubtree(root.left, subRoot) + || isSubtree(root.right, subRoot) + } +} \ No newline at end of file diff --git a/swift/0605-can-place-flowers.swift b/swift/0605-can-place-flowers.swift new file mode 100644 index 000000000..a986a792d --- /dev/null +++ b/swift/0605-can-place-flowers.swift @@ -0,0 +1,19 @@ +// Time: O(n) +// Space: O(1) +class Solution { + func canPlaceFlowers(_ flowerbed: [Int], _ n: Int) -> Bool { + var empty = (flowerbed[0] == 1) ? 0 : 1 + var getN = n + for f in flowerbed { + if f == 1 { + getN -= (empty - 1) / 2 + empty = 0 + } + else { + empty += 1 + } + } + getN -= empty / 2 + return getN <= 0 + } +} \ No newline at end of file diff --git a/swift/0621-task-scheduler.swift b/swift/0621-task-scheduler.swift new file mode 100644 index 000000000..9c008e132 --- /dev/null +++ b/swift/0621-task-scheduler.swift @@ -0,0 +1,147 @@ +class Heap { + var heap = [0] + + var count: Int { + heap.count - 1 + } + + var first: Int { + heap[1] + } + + func heapify(_ arr: [Int]) { + heap = arr + heap.append(arr[0]) + var cur = (heap.count - 1) / 2 + while cur > 0 { + var i = cur + while 2 * i < heap.count { + if 2 * i + 1 < heap.count && heap[2 * i + 1] > heap[2 * i] && heap[i] < heap[2 * i + 1] { + let tmp = heap[i] + heap[i] = heap[2 * i + 1] + heap[2 * i + 1] = tmp + i = 2 * i + 1 + } else if heap[i] < heap[2 * i] { + let tmp = heap[i] + heap[i] = heap[2 * i] + heap[2 * i] = tmp + i = 2 * i + } else { + break + } + } + cur -= 1 + } + } + + func push(_ val: Int) { + heap.append(val) + var i = heap.count - 1 + while i > 1 && heap[i] > heap[i / 2] { + let tmp = heap[i] + heap[i] = heap[i / 2] + heap[i / 2] = tmp + i = i / 2 + } + } + + func pop() -> Int? { + if heap.count == 1 { + return nil + } + if heap.count == 2 { + return heap.popLast() + } + let res = heap[1] + heap[1] = heap.removeLast() + var i = 1 + while 2 * i < heap.count { + if 2 * i + 1 < heap.count && heap[2 * i + 1] > heap[2 * i] && heap[i] < heap[2 * i + 1] { + let tmp = heap[i] + heap[i] = heap[2 * i + 1] + heap[2 * i + 1] = tmp + i = 2 * i + 1 + } else if heap[i] < heap[2 * i] { + let tmp = heap[i] + heap[i] = heap[2 * i] + heap[2 * i] = tmp + i = 2 * i + } else { + break + } + } + return res + } +} + +class ListNode { + var val: (Int, Int) + var next: ListNode? + + init(_ val: (Int, Int)) { + self.val = val + } +} + +class Queue { + var head: ListNode? + var tail: ListNode? + var size = 0 + + func enqueue(_ val: (Int, Int)) { + let node = ListNode(val) + if head == nil { + head = node + tail = node + } else { + tail?.next = node + tail = tail?.next + } + + size += 1 + } + + func dequeue() -> (Int, Int)? { + if head == nil { + return nil + } + + let node = head + head = head?.next + if head == nil { + tail = nil + } + size -= 1 + return node?.val + } +} + +class Solution { + func leastInterval(_ tasks: [Character], _ n: Int) -> Int { + var count = [Character: Int]() + for t in tasks { + count[t, default: 0] += 1 + } + let heap = Heap() + heap.heapify(Array(count.values)) + + var time = 0 + var q = Queue() + while heap.count > 0 || q.size > 0 { + time += 1 + if heap.count == 0 { + time = q.head!.val.1 + } else { + let cnt = heap.pop()! - 1 + if cnt != 0 { + q.enqueue((cnt, time + n)) + } + } + if q.size > 0 && q.head!.val.1 == time { + heap.push(q.dequeue()!.0) + } + } + + return time + } +} \ No newline at end of file diff --git a/swift/0647-palindromic-substrings.swift b/swift/0647-palindromic-substrings.swift new file mode 100644 index 000000000..b328e868d --- /dev/null +++ b/swift/0647-palindromic-substrings.swift @@ -0,0 +1,29 @@ +/** + * Question Link: https://leetcode.com/problems/palindromic-substrings/ + */ + + class Solution { + func countSubstrings(_ s: String) -> Int { + let s = Array(s) + var count = 0 + + for i in 0..= 0 && r < s.count && s[l] == s[r] { + count += 1 + l -= 1 + r += 1 + } + l = i + r = i + 1 + while l >= 0 && r < s.count && s[l] == s[r] { + count += 1 + l -= 1 + r += 1 + } + } + + return count + } +} \ No newline at end of file diff --git a/swift/0678-valid-parenthesis-string.swift b/swift/0678-valid-parenthesis-string.swift new file mode 100644 index 000000000..1f34ada71 --- /dev/null +++ b/swift/0678-valid-parenthesis-string.swift @@ -0,0 +1,29 @@ +class Solution { + func checkValidString(_ s: String) -> Bool { + var leftMin = 0 + var leftMax = 0 + + for c in s { + if c == "(" { + leftMin += 1 + leftMax += 1 + } else if c == ")" { + leftMin -= 1 + leftMax -= 1 + } else { + leftMin -= 1 + leftMax += 1 + } + + if leftMax < 0 { + return false + } + + if leftMin < 0 { + leftMin = 0 + } + } + + return leftMin == 0 + } +} \ No newline at end of file diff --git a/swift/0682-baseball-game.swift b/swift/0682-baseball-game.swift new file mode 100644 index 000000000..ee6c2baee --- /dev/null +++ b/swift/0682-baseball-game.swift @@ -0,0 +1,17 @@ +class Solution { + func calPoints(_ operations: [String]) -> Int { + var scoreArray = [Int]() + for i in 0.. Int { + var p = parents[n]! + while p != parents[p]! { + parents[p] = parents[parents[p]!] + p = parents[p]! + } + return p + } + + func union(n1: Int, n2: Int) -> Bool { + let p1 = find(n: n1) + let p2 = find(n: n2) + if p1 == p2 { + return false + } + + if ranks[p1]! > ranks[p2]! { + parents[p2] = p1 + } else if ranks[p1]! < ranks[p2]! { + parents[p1] = p2 + } else { + parents[p1] = p2 + ranks[p2, default: 0] += 1 + } + return true + } +} + +class Solution { + func findRedundantConnection(_ edges: [[Int]]) -> [Int] { + let unionFind = UnionFind(n: edges.count) + for edge in edges { + if !unionFind.union(n1: edge[0], n2: edge[1]) { + return [edge[0], edge[1]] + } + } + return [] + } +} \ No newline at end of file diff --git a/swift/0695-max-area-of-island.swift b/swift/0695-max-area-of-island.swift new file mode 100644 index 000000000..60e152af8 --- /dev/null +++ b/swift/0695-max-area-of-island.swift @@ -0,0 +1,35 @@ +class Solution { + func maxAreaOfIsland(_ grid: [[Int]]) -> Int { + let numRows = grid.count + let numColumns = grid[0].count + + var grid = grid + var maxArea = 0 + + for row in 0.. Int { + guard row >= 0, + row < grid.count, + column >= 0, + column < grid[0].count, + grid[row][column] == 1 + else { return 0 } + + grid[row][column] = 0 + + return 1 + dfs(row: row + 1, column: column, grid: &grid) + + dfs(row: row - 1, column: column, grid: &grid) + + dfs(row: row, column: column + 1, grid: &grid) + + dfs(row: row, column: column - 1, grid: &grid) + } +} diff --git a/swift/0700-search-in-a-binary-search-tree.swift b/swift/0700-search-in-a-binary-search-tree.swift new file mode 100644 index 000000000..1ae05b9b9 --- /dev/null +++ b/swift/0700-search-in-a-binary-search-tree.swift @@ -0,0 +1,34 @@ +/** + * https://leetcode.com/problems/search-in-a-binary-search-tree/ + */ + + /** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func searchBST(_ root: TreeNode?, _ val: Int) -> TreeNode? { + if root == nil { + return nil + } + + if val > root!.val { + return searchBST(root?.right, val) + } else if val < root!.val { + return searchBST(root?.left, val) + } else { + return root + } + } +} \ No newline at end of file diff --git a/swift/0701-insert-into-a-binary-search-tree.swift b/swift/0701-insert-into-a-binary-search-tree.swift new file mode 100644 index 000000000..bdae15d36 --- /dev/null +++ b/swift/0701-insert-into-a-binary-search-tree.swift @@ -0,0 +1,34 @@ +/** + * Question Link: https://leetcode.com/problems/insert-into-a-binary-search-tree/ + */ + + /** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + func insertIntoBST(_ root: TreeNode?, _ val: Int) -> TreeNode? { + if root == nil { + return TreeNode(val) + } + + if root!.val > val { + root?.left = insertIntoBST(root?.left, val) + } else if root!.val < val { + root?.right = insertIntoBST(root?.right, val) + } + + return root + } +} \ No newline at end of file diff --git a/swift/0703-kth-largest-element-in-a-stream.swift b/swift/0703-kth-largest-element-in-a-stream.swift new file mode 100644 index 000000000..c05107ffc --- /dev/null +++ b/swift/0703-kth-largest-element-in-a-stream.swift @@ -0,0 +1,129 @@ +// Heap source code at: https://gist.github.com/kalub92/d269ba6b2bf05ca7dcbaae64b4ff7a2d +struct MinHeap { + var items: [Int] = [] + + //Get Index + private func getLeftChildIndex(_ parentIndex: Int) -> Int { + return 2 * parentIndex + 1 + } + private func getRightChildIndex(_ parentIndex: Int) -> Int { + return 2 * parentIndex + 2 + } + private func getParentIndex(_ childIndex: Int) -> Int { + return (childIndex - 1) / 2 + } + + // Boolean Check + private func hasLeftChild(_ index: Int) -> Bool { + return getLeftChildIndex(index) < items.count + } + private func hasRightChild(_ index: Int) -> Bool { + return getRightChildIndex(index) < items.count + } + private func hasParent(_ index: Int) -> Bool { + return getParentIndex(index) >= 0 + } + + // Return Item From Heap + private func leftChild(_ index: Int) -> Int { + return items[getLeftChildIndex(index)] + } + private func rightChild(_ index: Int) -> Int { + return items[getRightChildIndex(index)] + } + private func parent(_ index: Int) -> Int { + return items[getParentIndex(index)] + } + + // Heap Operations + mutating private func swap(indexOne: Int, indexTwo: Int) { + let placeholder = items[indexOne] + items[indexOne] = items[indexTwo] + items[indexTwo] = placeholder + } + + public func peek() -> Int { + if items.count != 0 { + return items[0] + } else { + fatalError() + } + } + + mutating public func poll() -> Int { + if items.count != 0 { + let item = items[0] + items[0] = items[items.count - 1] + heapifyDown() + items.removeLast() + return item + } else { + fatalError() + } + } + + mutating public func add(_ item: Int) { + items.append(item) + heapifyUp() + } + + mutating private func heapifyUp() { + var index = items.count - 1 + while hasParent(index) && parent(index) > items[index] { + swap(indexOne: getParentIndex(index), indexTwo: index) + index = getParentIndex(index) + } + } + + mutating private func heapifyDown() { + var index = 0 + while hasLeftChild(index) { + var smallerChildIndex = getLeftChildIndex(index) + if hasRightChild(index) && + rightChild(index) < leftChild(index) { + smallerChildIndex = getRightChildIndex(index) + } + + if items[index] < items[smallerChildIndex] { + break + } else { + swap(indexOne: index, indexTwo: smallerChildIndex) + } + + index = smallerChildIndex + } + } +} + +// Extensions to add for the required problem +extension MinHeap { + var size: Int { items.count } +} + +class KthLargest { + var minHeap = MinHeap() + var capacity = 0 + + init(_ k: Int, _ nums: [Int]) { + self.capacity = k + nums.forEach { add($0) } + } + + func add(_ val: Int) -> Int { + if minHeap.size >= capacity { + if val > minHeap.peek() { + minHeap.poll() + minHeap.add(val) + } + } else { + minHeap.add(val) + } + return minHeap.peek() + } +} + +/** + * Your KthLargest object will be instantiated and called as such: + * let obj = KthLargest(k, nums) + * let ret_1: Int = obj.add(val) + */ diff --git a/swift/0704-binary-search.swift b/swift/0704-binary-search.swift new file mode 100644 index 000000000..1a44406df --- /dev/null +++ b/swift/0704-binary-search.swift @@ -0,0 +1,13 @@ +class Solution { + func search(_ nums: [Int], _ target: Int) -> Int { + var l = 0 + var r = nums.count - 1 + while l <= r { + let mid = (l + r) / 2 + guard nums[mid] != target else { return mid } + l = nums[mid] < target ? mid + 1 : l + r = nums[mid] > target ? mid - 1 : r + } + return -1 + } +} diff --git a/swift/0705-design-hashset.swift b/swift/0705-design-hashset.swift new file mode 100644 index 000000000..590e59a91 --- /dev/null +++ b/swift/0705-design-hashset.swift @@ -0,0 +1,92 @@ +/** + * Question Link: https://leetcode.com/problems/design-hashset/ + */ + + +class MyHashSet { + var map: [Int?] + var size: Int + var capacity: Int + + init() { + self.map = [Int?](repeating: nil, count: 10 * 10 * 10 * 10 * 10) + self.size = 0 + self.capacity = 10 * 10 * 10 * 10 * 10 + } + + func hash(_ key: Int) -> Int { + return key % capacity + } + + func rehash() { + capacity = 2 * capacity + let oldMap = map + var newMap = [Int?]() + for i in 0..= capacity / 2 { + rehash() + } + return + } else if map[index] == key { + return + } + + index += 1 + index = index % capacity + } + } + + func remove(_ key: Int) { + if !contains(key) { + return + } + + var index = hash(key) + while true { + if map[index] == key { + map[index] = nil + size -= 1 + return + } + index += 1 + index = index % capacity + } + } + + func contains(_ key: Int) -> Bool { + var index = hash(key) + while map[index] != nil { + if map[index] == key { + return true + } + index += 1 + index = index % capacity + } + return false + } +} + +/** + * Your MyHashSet object will be instantiated and called as such: + * let obj = MyHashSet() + * obj.add(key) + * obj.remove(key) + * let ret_3: Bool = obj.contains(key) + */ \ No newline at end of file diff --git a/swift/0706-design-hashmap.swift b/swift/0706-design-hashmap.swift new file mode 100644 index 000000000..626d92af4 --- /dev/null +++ b/swift/0706-design-hashmap.swift @@ -0,0 +1,100 @@ +/** + * Question Link: https://leetcode.com/problems/design-hashmap/ + */ + + class Pair { + let key: Int + var val: Int + + init(_ key: Int, _ val: Int) { + self.key = key + self.val = val + } +} + +class MyHashMap { + var size: Int + var capacity: Int + var map: [Pair?] + + init() { + self.size = 0 + self.capacity = 10 * 10 * 10 * 10 * 10 + self.map = [Pair?](repeating: nil, count: capacity) + } + + func hash(_ key: Int) -> Int { + return key % capacity + } + + func rehash() { + capacity = 2 * capacity + var newMap = [Pair?](repeating: nil, count: capacity) + let oldMap = map + map = newMap + for pair in oldMap { + if pair != nil { + map.append(pair) + } + } + } + + func put(_ key: Int, _ value: Int) { + var index = hash(key) + + while true { + if map[index] == nil { + map[index] = Pair(key, value) + size += 1 + if size >= capacity / 2 { + rehash() + } + return + } else if map[index]?.key == key { + map[index]?.val = value + return + } + index += 1 + index = index % capacity + } + } + + func get(_ key: Int) -> Int { + var index = hash(key) + + while map[index] != nil { + if map[index]!.key == key { + return map[index]!.val + } + index += 1 + index = index % capacity + } + + return -1 + } + + func remove(_ key: Int) { + if get(key) == -1 { + return + } + + var index = hash(key) + while true { + if map[index]?.key == key { + map[index] = nil + size -= 1 + return + } + index += 1 + index = index % capacity + } + } +} + +/** + * Your MyHashMap object will be instantiated and called as such: + * let obj = MyHashMap() + * obj.put(key, value) + * let ret_2: Int = obj.get(key) + * obj.remove(key) + */ \ No newline at end of file diff --git a/swift/0707-design-linked-list.swift b/swift/0707-design-linked-list.swift new file mode 100644 index 000000000..985350292 --- /dev/null +++ b/swift/0707-design-linked-list.swift @@ -0,0 +1,105 @@ +/** + * Question Link: https://leetcode.com/problems/design-linked-list/ + */ + +class ListNode { + var val: Int + var next: ListNode? + var prev: ListNode? + + init(_ val: Int) { + self.val = val + } +} + +class MyLinkedList { + var left: ListNode + var right: ListNode + + init() { + self.left = ListNode(-1) + self.right = ListNode(-1) + left.next = right + right.prev = left + } + + func get(_ index: Int) -> Int { + var cur = left.next + var index = index + while cur != nil && index > 0 { + cur = cur?.next + index -= 1 + } + + if cur != nil && cur !== right && index == 0 { + return cur!.val + } else { + return -1 + } + } + + func addAtHead(_ val: Int) { + let node = ListNode(val) + let next = left.next + let prev = left + prev.next = node + next?.prev = node + node.next = next + node.prev = prev + } + + func addAtTail(_ val: Int) { + let node = ListNode(val) + let next = right + let prev = right.prev + prev?.next = node + next.prev = node + node.next = next + node.prev = prev + } + + func addAtIndex(_ index: Int, _ val: Int) { + var cur = left.next + var index = index + while cur != nil && index > 0 { + cur = cur?.next + index -= 1 + } + + if cur != nil && index == 0 { + let node = ListNode(val) + let next = cur + let prev = cur?.prev + prev?.next = node + next?.prev = node + node.next = next + node.prev = prev + } + } + + func deleteAtIndex(_ index: Int) { + var cur = left.next + var index = index + while cur != nil && index > 0 { + cur = cur?.next + index -= 1 + } + + if cur != nil && cur !== right && index == 0 { + let next = cur?.next + let prev = cur?.prev + prev?.next = next + next?.prev = prev + } + } +} + +/** + * Your MyLinkedList object will be instantiated and called as such: + * let obj = MyLinkedList() + * let ret_1: Int = obj.get(index) + * obj.addAtHead(val) + * obj.addAtTail(val) + * obj.addAtIndex(index, val) + * obj.deleteAtIndex(index) + */ \ No newline at end of file diff --git a/swift/0721-accounts-merge.swift b/swift/0721-accounts-merge.swift new file mode 100644 index 000000000..de6fb1146 --- /dev/null +++ b/swift/0721-accounts-merge.swift @@ -0,0 +1,68 @@ +/** + * Question Link: https://leetcode.com/problems/accounts-merge/ + */ + + class UnionFind { + var parents = [Int: Int]() + var ranks = [Int: Int]() + + init(n: Int) { + for i in 0.. Int { + var p = parents[n]! + while p != parents[p] { + parents[p] = parents[parents[p]!] + p = parents[p]! + } + return p + } + + func union(n1: Int, n2: Int) -> Bool { + let p1 = find(n: n1) + let p2 = find(n: n2) + if p1 == p2 { + return false + } + if ranks[p1]! > ranks[p2]! { + parents[p2] = p1 + } else if ranks[p1]! < ranks[p2]! { + parents[p1] = p2 + } else { + parents[p1] = p2 + ranks[p2, default: 0] += 1 + } + return true + } +} + +class Solution { + func accountsMerge(_ accounts: [[String]]) -> [[String]] { + let unionFind = UnionFind(n: accounts.count) + var emailToAcc = [String: Int]() + for (i, a) in accounts.enumerated() { + for j in 1.. Int { + var nums = nums + for i in 1.. [Int] { + var stack = [Int]() + + for var a in asteroids { + while !stack.isEmpty && a < 0 && stack.last! > 0 { + let diff = a + stack.last! + if diff < 0 { + stack.popLast() + } else if diff > 0 { + a = 0 + } else { + a = 0 + stack.popLast() + } + } + + if a != 0 { + stack.append(a) + } + } + + return stack + } +} \ No newline at end of file diff --git a/swift/0739-daily-temperatures.swift b/swift/0739-daily-temperatures.swift new file mode 100644 index 000000000..c9bc69e25 --- /dev/null +++ b/swift/0739-daily-temperatures.swift @@ -0,0 +1,23 @@ +class Solution { + func dailyTemperatures(_ temperatures: [Int]) -> [Int] { + let tempSize = temperatures.count + var result = Array(repeating: 0, count: tempSize) + + for i in stride(from: tempSize - 1, through: 0, by: -1) { + var j = i + 1 + + while j < tempSize && temperatures[j] <= temperatures[i] { + if result[j] <= 0 { + break + } + j += result[j] + } + + if j < tempSize && temperatures[j] > temperatures[i] { + result[i] = j - i + } + } + + return result + } +} \ No newline at end of file diff --git a/swift/0743-network-delay-time.swift b/swift/0743-network-delay-time.swift new file mode 100644 index 000000000..f28a93149 --- /dev/null +++ b/swift/0743-network-delay-time.swift @@ -0,0 +1,88 @@ +/** + * Question Link: https://leetcode.com/problems/network-delay-time/ + */ + + class Heap { + var heap = [[0, 0]] + + var count: Int { + heap.count - 1 + } + + func push(_ val: [Int]) { + heap.append(val) + var i = heap.count - 1 + + while i > 1 && heap[i][0] < heap[i / 2][0] { + var tmp = heap[i] + heap[i] = heap[i / 2] + heap[i / 2] = tmp + i = i / 2 + } + } + + func pop() -> [Int]? { + if heap.count == 1 { + return nil + } + if heap.count == 2 { + return heap.removeLast() + } + var res = heap[1] + heap[1] = heap.removeLast() + var i = 1 + while 2 * i < heap.count { + if 2 * i + 1 < heap.count && heap[2 * i + 1][0] < heap[2 * i][0] && heap[i][0] > heap[2 * i + 1][0] { + var tmp = heap[i] + heap[i] = heap[2 * i + 1] + heap[2 * i + 1] = tmp + i = 2 * i + 1 + } else if heap[i][0] > heap[2 * i][0] { + var tmp = heap[i] + heap[i] = heap[2 * i] + heap[2 * i] = tmp + i = 2 * i + } else { + break + } + } + return res + } +} + +class Solution { + func networkDelayTime(_ times: [[Int]], _ n: Int, _ k: Int) -> Int { + var edges = [Int: [[Int]]]() + for i in 1..() + var res = 0 + var minHeap = Heap() + minHeap.push([0, k]) + while minHeap.count > 0 { + let val = minHeap.pop()! + let w1 = val[0] + let n1 = val[1] + if visit.contains(n1) { + continue + } + visit.insert(n1) + res = w1 + for e in edges[n1]! { + let n2 = e[0] + let w2 = e[1] + if !visit.contains(n2) { + minHeap.push([w1 + w2, n2]) + } + } + } + return visit.count == n ? res : -1 + } +} \ No newline at end of file diff --git a/swift/0745-prefix-and-suffix-search.swift b/swift/0745-prefix-and-suffix-search.swift new file mode 100644 index 000000000..3a232eed6 --- /dev/null +++ b/swift/0745-prefix-and-suffix-search.swift @@ -0,0 +1,62 @@ +/** + * Question Link: https://leetcode.com/problems/prefix-and-suffix-search/ + */ + + class TrieNode { + var children = [Character: TrieNode]() + var isWord = false + var index = 0 +} + +class Trie { + let root = TrieNode() + + func insert(word: String, index: Int) { + var cur = root + for c in word { + if cur.children[c] == nil { + let node = TrieNode() + node.index = index + cur.children[c] = node + } + cur = cur.children[c]! + } + cur.isWord = true + } + + func search(pref: String, suff: String) -> Int { + var cur = root + for c in suff + "#" + pref { + if cur.children[c] == nil { + return -1 + } + cur = cur.children[c]! + } + return cur.index + } +} + +class WordFilter { + let trie = Trie() + + init(_ words: [String]) { + for i in stride(from: words.count - 1, to: -1, by: -1) { + var prefix = [Character]() + for c in words[i].reversed() { + prefix.insert(c, at: 0) + let newWord = String(prefix) + "#" + words[i] + trie.insert(word: newWord, index: i) + } + } + } + + func f(_ pref: String, _ suff: String) -> Int { + trie.search(pref: pref, suff: suff) + } +} + +/** + * Your WordFilter object will be instantiated and called as such: + * let obj = WordFilter(words) + * let ret_1: Int = obj.f(pref, suff) + */ \ No newline at end of file diff --git a/swift/0746-min-cost-climbing-stairs.swift b/swift/0746-min-cost-climbing-stairs.swift new file mode 100644 index 000000000..aebcf960a --- /dev/null +++ b/swift/0746-min-cost-climbing-stairs.swift @@ -0,0 +1,9 @@ +class Solution { + func minCostClimbingStairs(_ cost: [Int]) -> Int { + var stepsCost = cost + for i in stride(from: cost.count - 3, through: 0, by: -1) { + stepsCost[i] += min(stepsCost[i + 1], stepsCost[i + 2]) + } + return min(stepsCost[0], stepsCost[1]) + } +} \ No newline at end of file diff --git a/swift/0763-partition-labels.swift b/swift/0763-partition-labels.swift new file mode 100644 index 000000000..54913052f --- /dev/null +++ b/swift/0763-partition-labels.swift @@ -0,0 +1,22 @@ +class Solution { + func partitionLabels(_ s: String) -> [Int] { + var hashmap = [Character: Int]() + for (i, c) in s.enumerated() { + hashmap[c] = i + } + + var res = [Int]() + var size = 0 + var end = 0 + for (i, c) in s.enumerated() { + end = max(end, hashmap[c] ?? 0) + size += 1 + if i == end { + res.append(size) + size = 0 + } + } + + return res + } +} \ No newline at end of file diff --git a/swift/0778-swim-in-rising-water.swift b/swift/0778-swim-in-rising-water.swift new file mode 100644 index 000000000..5fe77e6f2 --- /dev/null +++ b/swift/0778-swim-in-rising-water.swift @@ -0,0 +1,82 @@ +/** + * Question Link: https://leetcode.com/problems/swim-in-rising-water/ + */ + + class Heap { + var heap = [[0, 0, 0]] + + var count: Int { + heap.count - 1 + } + + func push(_ val: [Int]) { + heap.append(val) + var i = heap.count - 1 + while i > 1 && heap[i][0] < heap[i / 2][0] { + var tmp = heap[i] + heap[i] = heap[i / 2] + heap[i / 2] = tmp + i = i / 2 + } + } + + func pop() -> [Int]? { + if heap.count == 1 { + return nil + } + if heap.count == 2 { + return heap.popLast() + } + var res = heap[1] + heap[1] = heap.removeLast() + var i = 1 + while 2 * i < heap.count { + if 2 * i + 1 < heap.count && heap[2 * i + 1][0] < heap[2 * i][0] && heap[i][0] > heap[2 * i + 1][0] { + var tmp = heap[i] + heap[i] = heap[2 * i + 1] + heap[2 * i + 1] = tmp + i = 2 * i + 1 + } else if heap[i][0] > heap[2 * i][0] { + var tmp = heap[i] + heap[i] = heap[2 * i] + heap[2 * i] = tmp + i = 2 * i + } else { + break + } + } + return res + } +} + +class Solution { + func swimInWater(_ grid: [[Int]]) -> Int { + let n = grid.count + var visit = Set<[Int]>() + var minH = Heap() + minH.push([grid[0][0], 0, 0]) + let directions = [[0, 1], [0, -1], [1, 0], [-1, 0]] + visit.insert([0, 0]) + while minH.count > 0 { + let val = minH.pop()! + let t = val[0] + let r = val[1] + let c = val[2] + if r == n - 1 && c == n - 1 { + return t + } + for d in directions { + let dr = d[0] + let dc = d[1] + var neiR = r + dr + var neiC = c + dc + if neiR < 0 || neiC < 0 || neiR == n || neiC == n || visit.contains([neiR, neiC]) { + continue + } + visit.insert([neiR, neiC]) + minH.push([max(t, grid[neiR][neiC]), neiR, neiC]) + } + } + return -1 + } +} \ No newline at end of file diff --git a/swift/0787-cheapest-flights-within-k-stops.swift b/swift/0787-cheapest-flights-within-k-stops.swift new file mode 100644 index 000000000..6e02404f6 --- /dev/null +++ b/swift/0787-cheapest-flights-within-k-stops.swift @@ -0,0 +1,24 @@ +class Solution { + func findCheapestPrice(_ n: Int, _ flights: [[Int]], _ src: Int, _ dst: Int, _ k: Int) -> Int { + var prices = Array(repeating: Int.max, count: n) + prices[src] = 0 + + for i in 0..<(k + 1) { + var tmp = prices + + for f in flights { + let (s, d, p) = (f[0], f[1], f[2]) + if prices[s] == Int.max { + continue + } + if prices[s] + p < tmp[d] { + tmp[d] = prices[s] + p + } + } + + prices = tmp + } + + return prices[dst] == Int.max ? -1 : prices[dst] + } +} \ No newline at end of file diff --git a/swift/0846-hand-of-straights.swift b/swift/0846-hand-of-straights.swift new file mode 100644 index 000000000..9d1c9b2ee --- /dev/null +++ b/swift/0846-hand-of-straights.swift @@ -0,0 +1,105 @@ +class Heap { + var heap = [0] + + var count: Int { + heap.count - 1 + } + + var first: Int { + heap[1] + } + + func heapify(_ arr: [Int]) { + heap = arr + heap.append(arr[0]) + var cur = (heap.count - 1) / 2 + while cur > 0 { + var i = cur + while 2 * i < heap.count { + if 2 * i + 1 < heap.count && heap[2 * i + 1] < heap[2 * i] && heap[i] > heap[2 * i + 1] { + let tmp = heap[i] + heap[i] = heap[2 * i + 1] + heap[2 * i + 1] = tmp + i = 2 * i + 1 + } else if heap[i] > heap[2 * i] { + let tmp = heap[i] + heap[i] = heap[2 * i] + heap[2 * i] = tmp + i = 2 * i + } else { + break + } + } + cur -= 1 + } + } + + func push(_ val: Int) { + heap.append(val) + var i = heap.count - 1 + while i > 1 && heap[i] < heap[i / 2] { + let tmp = heap[i] + heap[i] = heap[i / 2] + heap[i / 2] = tmp + i = i / 2 + } + } + + func pop() -> Int? { + if heap.count == 1 { + return nil + } + if heap.count == 2 { + return heap.popLast() + } + let res = heap[1] + heap[1] = heap.removeLast() + var i = 1 + while 2 * i < heap.count { + if 2 * i + 1 < heap.count && heap[2 * i + 1] < heap[2 * i] && heap[i] > heap[2 * i + 1] { + let tmp = heap[i] + heap[i] = heap[2 * i + 1] + heap[2 * i + 1] = tmp + i = 2 * i + 1 + } else if heap[i] > heap[2 * i] { + let tmp = heap[i] + heap[i] = heap[2 * i] + heap[2 * i] = tmp + i = 2 * i + } else { + break + } + } + return res + } +} + +class Solution { + func isNStraightHand(_ hand: [Int], _ groupSize: Int) -> Bool { + var count = [Int: Int]() + for n in hand { + count[n, default: 0] += 1 + } + + let minHeap = Heap() + minHeap.heapify(Array(count.keys)) + + while minHeap.count > 0 { + for i in minHeap.first..<(minHeap.first + groupSize) { + if count[i] == nil { + return false + } + + count[i, default: 0] -= 1 + if count[i] == 0 { + if i != minHeap.first { + return false + } + minHeap.pop() + } + } + } + + return true + } +} \ No newline at end of file diff --git a/swift/0853-car-fleet.swift b/swift/0853-car-fleet.swift new file mode 100644 index 000000000..78c405067 --- /dev/null +++ b/swift/0853-car-fleet.swift @@ -0,0 +1,26 @@ +/** + * Question Link: https://leetcode.com/problems/car-fleet/ + */ + +class CarFleet { + func carFleet(_ target: Int, _ position: [Int], _ speed: [Int]) -> Int { + var stack = [Double]() + + let pair = zip(position, speed) + .map { ($0, $1) } + .sorted { $0.0 > $1.0 } + + for (p, s) in pair { + stack.append(Double(target - p) / Double(s)) + + guard stack.count >= 2 else { continue } + + let suffix = stack.suffix(2) + if suffix.last! <= suffix.first! { + stack.removeLast() + } + } + + return stack.count + } +} \ No newline at end of file diff --git a/swift/0867-transpose-matrix.swift b/swift/0867-transpose-matrix.swift new file mode 100644 index 000000000..5e7b418e8 --- /dev/null +++ b/swift/0867-transpose-matrix.swift @@ -0,0 +1,19 @@ +/** + * Question Link: https://leetcode.com/problems/transpose-matrix/ + */ + + class Solution { + func transpose(_ matrix: [[Int]]) -> [[Int]] { + var res = [[Int]]() + let row = matrix.count + let col = matrix[0].count + for c in 0.. Int { + let total = piles.reduce(0, +) + var left = (total + h - 1) / h, right = piles.max()! + + while left < right { + let mid = left + (right - left) / 2 + + var totalTime = 0 + for p in piles { + totalTime += (p + mid - 1) / mid + } + + if totalTime <= h { + right = mid + } + else { + left = mid + 1 + } + } + return right + } +} \ No newline at end of file diff --git a/swift/0876-middle-of-the-linked-list.swift b/swift/0876-middle-of-the-linked-list.swift new file mode 100644 index 000000000..10739c7f0 --- /dev/null +++ b/swift/0876-middle-of-the-linked-list.swift @@ -0,0 +1,21 @@ +/** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func middleNode(_ head: ListNode?) -> ListNode? { + var current = head + var middle = head + while let next = current?.next { + current = next.next + middle = middle?.next + } + return middle + } +} \ No newline at end of file diff --git a/swift/0881-boats-to-save-people.swift b/swift/0881-boats-to-save-people.swift new file mode 100644 index 000000000..e21dbd403 --- /dev/null +++ b/swift/0881-boats-to-save-people.swift @@ -0,0 +1,20 @@ +class Solution { + func numRescueBoats(_ people: [Int], _ limit: Int) -> Int { + let sortedPeople = people.sorted() + + var boats = 0 + var l = 0; var r = sortedPeople.count - 1 + + while l <= r { + var remaining = limit - sortedPeople[r] + r -= 1 + boats += 1 + + if l <= r && remaining >= sortedPeople[l] { + l += 1 + } + } + + return boats + } +} \ No newline at end of file diff --git a/swift/0896-monotonic-array.swift b/swift/0896-monotonic-array.swift new file mode 100644 index 000000000..42f04538a --- /dev/null +++ b/swift/0896-monotonic-array.swift @@ -0,0 +1,19 @@ +/** + * Question Link: https://leetcode.com/problems/monotonic-array/ + */ + + class Solution { + func isMonotonic(_ nums: [Int]) -> Bool { + var inc = true + var dec = true + for i in 0.. nums[i + 1] { + inc = false + } + if nums[i] < nums[i + 1] { + dec = false + } + } + return inc || dec + } +} \ No newline at end of file diff --git a/swift/0912-sort-an-array.swift b/swift/0912-sort-an-array.swift new file mode 100644 index 000000000..ee4cef40d --- /dev/null +++ b/swift/0912-sort-an-array.swift @@ -0,0 +1,55 @@ +/** + * Question Link: https://leetcode.com/problems/sort-an-array/ + */ + + class Solution { + func sortArray(_ nums: [Int]) -> [Int] { + var nums = nums + + func merge(arr: inout [Int], l: Int, m: Int, r: Int) { + var left = Array(arr[l...m]) + var right = Array(arr[m + 1...r]) + var i = l + var j = 0 + var k = 0 + + while j < left.count && k < right.count { + if left[j] <= right[k] { + arr[i] = left[j] + j += 1 + } else { + arr[i] = right[k] + k += 1 + } + i += 1 + } + + while j < left.count { + arr[i] = left[j] + j += 1 + i += 1 + } + + while k < right.count { + arr[i] = right[k] + k += 1 + i += 1 + } + + } + + func mergeSort(arr: inout [Int], l: Int, r: Int) -> [Int] { + if l == r { + return arr + } + + let m = (l + r) / 2 + mergeSort(arr: &arr, l: l, r: m) + mergeSort(arr: &arr, l: m + 1, r: r) + merge(arr: &arr, l: l, m: m, r: r) + return arr + } + + return mergeSort(arr: &nums, l: 0, r: nums.count - 1) + } +} \ No newline at end of file diff --git a/swift/0918-maximum-sum-circular-subarray.swift b/swift/0918-maximum-sum-circular-subarray.swift new file mode 100644 index 000000000..90377f249 --- /dev/null +++ b/swift/0918-maximum-sum-circular-subarray.swift @@ -0,0 +1,27 @@ +/** + * Question Link: https://leetcode.com/problems/maximum-sum-circular-subarray/ + */ + + class Solution { + func maxSubarraySumCircular(_ nums: [Int]) -> Int { + var globMax = nums[0] + var globMin = nums[0] + var curMax = 0 + var curMin = 0 + var total = 0 + + for n in nums { + curMax = max(curMax + n, n) + curMin = min(curMin + n, n) + total += n + globMax = max(globMax, curMax) + globMin = min(globMin, curMin) + } + + if globMax > 0 { + return max(globMax, total - globMin) + } else { + return globMax + } + } +} \ No newline at end of file diff --git a/swift/0929-unique-email-addresses.swift b/swift/0929-unique-email-addresses.swift new file mode 100644 index 000000000..033445741 --- /dev/null +++ b/swift/0929-unique-email-addresses.swift @@ -0,0 +1,28 @@ +class Solution { + func getFormattedEmail(from email: String) -> String { + let arr = email.split(separator: "@") + + var localNameInitial = arr[0] + let domainName = arr[1] + + // Ignore everything from first + (plus) sign + let arrLocalNameSplitPlus = localNameInitial.split(separator: "+") + guard let localNameWithoutPlus = arrLocalNameSplitPlus.first else { return "" } + + // Remove . (dot) sign + let localName = localNameWithoutPlus.replacingOccurrences(of: ".", with: "") + + return "\(localName)@\(domainName)" + } + + func numUniqueEmails(_ emails: [String]) -> Int { + var uniqueEmails: Set = Set() + + emails.forEach { email in + let formattedEmail = getFormattedEmail(from: email) + uniqueEmails.insert(formattedEmail) + } + + return uniqueEmails.count + } +} \ No newline at end of file diff --git a/swift/0973-k-closest-points-to-origin.swift b/swift/0973-k-closest-points-to-origin.swift new file mode 100644 index 000000000..c3d04592b --- /dev/null +++ b/swift/0973-k-closest-points-to-origin.swift @@ -0,0 +1,89 @@ +/** + * Question Link: https://leetcode.com/problems/k-closest-points-to-origin/ + */ + + class Heap { + var arr = [[Int]]() + + func heapify(_ val: [[Int]]) { + arr = val + arr.append(arr[0]) + var cur = (arr.count - 1) / 2 + + while cur > 0 { + var i = cur + while 2 * i < arr.count { + if 2 * i + 1 < arr.count && arr[2 * i + 1][0] < arr[2 * i][0] && arr[i][0] > arr[2 * i + 1][0] { + let tmp = arr[i] + arr[i] = arr[2 * i + 1] + arr[2 * i + 1] = tmp + i = 2 * i + 1 + } else if arr[i][0] > arr[2 * i][0] { + let tmp = arr[i] + arr[i] = arr[2 * i] + arr[2 * i] = tmp + i = 2 * i + } else { + break + } + } + cur -= 1 + } + } + + func pop() -> [Int]? { + if arr.count == 1 { + return nil + } + + if arr.count == 2 { + return arr.popLast() + } + + let res = arr[1] + arr[1] = arr.removeLast() + var i = 1 + + while 2 * i < arr.count { + if 2 * i + 1 < arr.count && arr[2 * i + 1][0] < arr[2 * i][0] && arr[i][0] > arr[2 * i + 1][0] { + let tmp = arr[i] + arr[i] = arr[2 * i + 1] + arr[2 * i + 1] = tmp + i = 2 * i + 1 + } else if arr[i][0] > arr[2 * i][0] { + let tmp = arr[i] + arr[i] = arr[2 * i] + arr[2 * i] = tmp + i = 2 * i + } else { + break + } + } + + return res + } +} + +class Solution { + func kClosest(_ points: [[Int]], _ k: Int) -> [[Int]] { + let heap = Heap() + var minHeap = [[Int]]() + var res = [[Int]]() + + for arr in points { + let x = arr[0] + let y = arr[1] + let dist = x * x + y * y + minHeap.append([dist, x, y]) + } + heap.heapify(minHeap) + print(heap.arr) + var k = k + while k > 0 { + let arr = heap.pop()! + res.append([arr[1], arr[2]]) + k -= 1 + } + return res + } +} \ No newline at end of file diff --git a/swift/0977-squares-of-a-sorted-array.swift b/swift/0977-squares-of-a-sorted-array.swift new file mode 100644 index 000000000..d43e0839e --- /dev/null +++ b/swift/0977-squares-of-a-sorted-array.swift @@ -0,0 +1,21 @@ +/** + * Question Link: https://leetcode.com/problems/squares-of-a-sorted-array/ + */ + + class Solution { + func sortedSquares(_ nums: [Int]) -> [Int] { + var res = [Int]() + var l = 0 + var r = nums.count - 1 + while l <= r { + if nums[l] * nums[l] > nums[r] * nums[r] { + res.append(nums[l] * nums[l]) + l += 1 + } else { + res.append(nums[r] * nums[r]) + r -= 1 + } + } + return res.reversed() + } +} \ No newline at end of file diff --git a/swift/0978-longest-turbulent-subarray.swift b/swift/0978-longest-turbulent-subarray.swift new file mode 100644 index 000000000..e24137980 --- /dev/null +++ b/swift/0978-longest-turbulent-subarray.swift @@ -0,0 +1,30 @@ +/** + * Question Link: https://leetcode.com/problems/longest-turbulent-subarray/ + */ + + class Solution { + func maxTurbulenceSize(_ arr: [Int]) -> Int { + var l = 0 + var r = 1 + var res = 1 + var prev = "" + + while r < arr.count { + if arr[r - 1] > arr[r] && prev != ">" { + res = max(res, r - l + 1) + r += 1 + prev = ">" + } else if arr[r - 1] < arr[r] && prev != "<" { + res = max(res, r - l + 1) + r += 1 + prev = "<" + } else { + r = arr[r - 1] == arr[r] ? r + 1 : r + l = r - 1 + prev = "" + } + } + + return res + } +} \ No newline at end of file diff --git a/swift/0981-time-based-key-value-store.swift b/swift/0981-time-based-key-value-store.swift new file mode 100644 index 000000000..20b98994c --- /dev/null +++ b/swift/0981-time-based-key-value-store.swift @@ -0,0 +1,37 @@ + +class TimeMap { + var store = [String: [(String, Int)]]() + init() { + + } + + func set(_ key: String, _ value: String, _ timestamp: Int) { + store[key, default: []].append((value, timestamp)) + } + + func get(_ key: String, _ timestamp: Int) -> String { + var res = "" + var values = store[key] ?? [] + + var l = 0 + var r = values.count - 1 + while l <= r { + let m = l + ((r - l) / 2) + if values[m].1 <= timestamp { + res = values[m].0 + l = m + 1 + } else { + r = m - 1 + } + } + + return res + } +} + +/** + * Your TimeMap object will be instantiated and called as such: + * let obj = TimeMap() + * obj.set(key, value, timestamp) + * let ret_2: String = obj.get(key, timestamp) + */ \ No newline at end of file diff --git a/swift/0983-minimum-cost-for-tickets.swift b/swift/0983-minimum-cost-for-tickets.swift new file mode 100644 index 000000000..a1b9367a0 --- /dev/null +++ b/swift/0983-minimum-cost-for-tickets.swift @@ -0,0 +1,22 @@ +/** + * Question Link: https://leetcode.com/problems/minimum-cost-for-tickets/ + */ + + class Solution { + func mincostTickets(_ days: [Int], _ costs: [Int]) -> Int { + var dp = [Int: Int]() + + for i in stride(from: days.count - 1, to: -1, by: -1) { + dp[i] = 10000 + for (d, c) in zip([1, 7 ,30], costs) { + var j = i + while j < days.count && days[j] < days[i] + d { + j += 1 + } + dp[i] = min(dp[i] ?? 0, c + (dp[j] ?? 0)) + } + } + + return dp[0] ?? 0 + } +} \ No newline at end of file diff --git a/swift/0994-rotting-oranges.swift b/swift/0994-rotting-oranges.swift new file mode 100644 index 000000000..a49cd3048 --- /dev/null +++ b/swift/0994-rotting-oranges.swift @@ -0,0 +1,86 @@ +/** + * Question Link: https://leetcode.com/problems/rotting-oranges/ + */ + + class ListNode { + let val: [Int] + var next: ListNode? + + init(_ val: [Int]) { + self.val = val + } +} + +class Deque { + var head: ListNode? + var tail: ListNode? + var size = 0 + + func enqueue(_ val: [Int]) { + let node = ListNode(val) + if head == nil { + head = node + tail = node + } else { + tail?.next = node + tail = tail?.next + } + size += 1 + } + + func dequeue() -> [Int]? { + if head == nil { + return nil + } + let node = head + head = head?.next + if head == nil { + tail = nil + } + size -= 1 + return node?.val + } +} + +class Solution { + func orangesRotting(_ grid: [[Int]]) -> Int { + let rows = grid.count + let cols = grid[0].count + var deque = Deque() + var visit = Set<[Int]>() + var fresh = 0 + var minutes = 0 + + for r in 0.. 0 && fresh > 0 { + for i in 0.. 0 { + var i = cur + while 2 * i < arr.count { + if 2 * i + 1 < arr.count && arr[2 * i + 1] > arr[2 * i] && arr[i] < arr[2 * i + 1] { + let tmp = arr[i] + arr[i] = arr[2 * i + 1] + arr[2 * i + 1] = tmp + i = 2 * i + 1 + } else if arr[i] < arr[2 * i] { + let tmp = arr[i] + arr[i] = arr[2 * i] + arr[2 * i] = tmp + i = 2 * i + } else { + break + } + } + cur -= 1 + } + } + + func push(_ val: Int) { + arr.append(val) + var i = arr.count - 1 + while i > 1 && arr[i] > arr[i / 2] { + let tmp = arr[i] + arr[i] = arr[i / 2] + arr[i / 2] = tmp + i = i / 2 + } + } + + func pop() -> Int? { + if arr.count == 1 { + return nil + } + if arr.count == 2 { + return arr.popLast() + } + + let res = arr[1] + arr[1] = arr.removeLast() + var i = 1 + + while 2 * i < arr.count { + if 2 * i + 1 < arr.count && arr[2 * i + 1] > arr[2 * i] && arr[i] < arr[2 * i + 1] { + let tmp = arr[i] + arr[i] = arr[2 * i + 1] + arr[2 * i + 1] = tmp + i = 2 * i + 1 + } else if arr[i] < arr[2 * i] { + let tmp = arr[i] + arr[i] = arr[2 * i] + arr[2 * i] = tmp + i = 2 * i + } else { + break + } + } + + return res + } +} + +class Solution { + func lastStoneWeight(_ stones: [Int]) -> Int { + let heap = Heap() + heap.heapify(stones) + while heap.count > 1 { + let first = heap.pop()! + let second = heap.pop()! + if first > second { + heap.push(first - second) + } + } + heap.push(0) + return heap.first + } +} \ No newline at end of file diff --git a/swift/1049-last-stone-weight-ii.swift b/swift/1049-last-stone-weight-ii.swift new file mode 100644 index 000000000..6009479cf --- /dev/null +++ b/swift/1049-last-stone-weight-ii.swift @@ -0,0 +1,24 @@ +/** + * Question Link: https://leetcode.com/problems/last-stone-weight-ii/ + */ + + class Solution { + func lastStoneWeightII(_ stones: [Int]) -> Int { + let stoneSum = stones.reduce(0, +) + let target = stoneSum / 2 + var dp = [[Int]: Int]() + + func dfs(_ i: Int, _ total: Int) -> Int { + if total >= target || i == stones.count { + return abs(total - (stoneSum - total)) + } + if dp[[i, total]] != nil { + return dp[[i, total]]! + } + dp[[i, total]] = min(dfs(i + 1, total), dfs(i + 1, total + stones[i])) + return dp[[i, total]]! + } + + return dfs(0, 0) + } +} \ No newline at end of file diff --git a/swift/1091-shortest-path-in-binary-matrix.swift b/swift/1091-shortest-path-in-binary-matrix.swift new file mode 100644 index 000000000..feb1d75b7 --- /dev/null +++ b/swift/1091-shortest-path-in-binary-matrix.swift @@ -0,0 +1,82 @@ +/** + * Question Link: https://leetcode.com/problems/shortest-path-in-binary-matrix/ + */ + + class ListNode { + let val: [Int] + var next: ListNode? + + init(_ val: [Int]) { + self.val = val + } +} + +class Dequeue { + var head: ListNode? + var tail: ListNode? + var size = 0 + + func enqueue(_ val: [Int]) { + let node = ListNode(val) + if head == nil { + head = node + tail = node + } else { + tail?.next = node + tail = tail?.next + } + + size += 1 + } + + func deque() -> [Int]? { + if head == nil { + return nil + } + + let node = head + head = head?.next + if head == nil { + tail = nil + } + + size -= 1 + return node!.val + } +} + +class Solution { + func shortestPathBinaryMatrix(_ grid: [[Int]]) -> Int { + let rows = grid.count + let cols = grid[0].count + var visit = Set<[Int]>() + var deque = Dequeue() + visit.insert([0, 0]) + deque.enqueue([0, 0, 1]) + + while deque.size > 0 { + let val = deque.deque()! + let r = val[0] + let c = val[1] + let len = val[2] + + if r < 0 || c < 0 || r == rows || c == cols || grid[r][c] == 1 { + continue + } + + if r == rows - 1 && c == cols - 1 { + return len + } + + let directions = [(0, 1), (0, -1), (1, 0), (-1, 0), (-1, -1), (1, 1), (-1, 1), (1, -1)] + for (dr, dc) in directions { + if !visit.contains([r + dr, c + dc]) { + deque.enqueue([r + dr, c + dc, len + 1]) + visit.insert([r + dr, c + dc]) + } + } + } + + return -1 + } +} \ No newline at end of file diff --git a/swift/1092-shortest-common-supersequence.swift b/swift/1092-shortest-common-supersequence.swift new file mode 100644 index 000000000..587502928 --- /dev/null +++ b/swift/1092-shortest-common-supersequence.swift @@ -0,0 +1,41 @@ +/** + * Question Link: https://leetcode.com/problems/shortest-common-supersequence/ + */ + + class Solution { + func shortestCommonSupersequence(_ str1: String, _ str2: String) -> String { + let str1 = Array(str1), str2 = Array(str2) + let n = str1.count + let m = str2.count + var dp = Array(repeating: Array(repeating: 0, count: m + 1), count: n + 1) + + for i in stride(from: n - 1, to: -1, by: -1) { + for j in stride(from: m - 1, to: -1, by: -1) { + if str1[i] == str2[j] { + dp[i][j] = 1 + dp[i + 1][j + 1] + } else { + dp[i][j] = max(dp[i + 1][j], dp[i][j + 1]) + } + } + } + + var i = 0 + var j = 0 + var res = "" + while i < n && j < m { + if dp[i + 1][j] == dp[i][j] { + res.append(str1[i]) + i += 1 + } else if dp[i][j + 1] == dp[i][j] { + res.append(str2[j]) + j += 1 + } else { + res.append(str2[j]) + i += 1 + j += 1 + } + } + + return res + String(str1[i...]) + String(str2[j...]) + } +} \ No newline at end of file diff --git a/swift/1143-Longest-Common-Subsequence.swift b/swift/1143-Longest-Common-Subsequence.swift new file mode 100644 index 000000000..9813cfd3c --- /dev/null +++ b/swift/1143-Longest-Common-Subsequence.swift @@ -0,0 +1,21 @@ +class Solution { + func longestCommonSubsequence(_ text1: String, _ text2: String) -> Int { + + var dp = [[Int]](repeating: [Int](repeating: 0, count: text2.count + 1), count: text1.count + 1) + + let d1 = Array(text1) + let d2 = Array(text2) + + for i in stride(from: d1.count - 1, to: -1, by: -1) { + for j in stride(from: d2.count - 1, to: -1, by: -1) { + if d1[i] == d2[j] { + dp[i][j] = 1 + dp[i + 1][j + 1] + } else { + dp[i][j] = max(dp[i][j + 1], dp[i + 1][j]) + } + } + } + + return dp[0][0] + } +} diff --git a/swift/1143-longest-common-subsequence.swift b/swift/1143-longest-common-subsequence.swift new file mode 100644 index 000000000..9813cfd3c --- /dev/null +++ b/swift/1143-longest-common-subsequence.swift @@ -0,0 +1,21 @@ +class Solution { + func longestCommonSubsequence(_ text1: String, _ text2: String) -> Int { + + var dp = [[Int]](repeating: [Int](repeating: 0, count: text2.count + 1), count: text1.count + 1) + + let d1 = Array(text1) + let d2 = Array(text2) + + for i in stride(from: d1.count - 1, to: -1, by: -1) { + for j in stride(from: d2.count - 1, to: -1, by: -1) { + if d1[i] == d2[j] { + dp[i][j] = 1 + dp[i + 1][j + 1] + } else { + dp[i][j] = max(dp[i][j + 1], dp[i + 1][j]) + } + } + } + + return dp[0][0] + } +} diff --git a/swift/1203-sort-items-by-groups-respecting-dependencies.swift b/swift/1203-sort-items-by-groups-respecting-dependencies.swift new file mode 100644 index 000000000..31c49b90a --- /dev/null +++ b/swift/1203-sort-items-by-groups-respecting-dependencies.swift @@ -0,0 +1,74 @@ +/** + * Question Link: https://leetcode.com/problems/sort-items-by-groups-respecting-dependencies/ + */ + + class Solution { + func sortItems(_ n: Int, _ m: Int, _ group: [Int], _ beforeItems: [[Int]]) -> [Int] { + var group = group + var groupID = m + for i in 0.. [Int] { + var indegree = indegree + var visited = [Int]() + var stack = [Int]() + for i in 0.. [Int] { + // initial max = -1 + var greatestElement: Int = -1 + var ans: [Int] = [] + + // Reverse iteration + for i in (0.. Int { + var res = 0 + var curSum = Array(arr[0..= threshold { + res += 1 + } + curSum -= arr[l] + } + return res + } +} \ No newline at end of file diff --git a/swift/1448-count-good-nodes-in-binary-tree.swift b/swift/1448-count-good-nodes-in-binary-tree.swift new file mode 100644 index 000000000..cf75ffa50 --- /dev/null +++ b/swift/1448-count-good-nodes-in-binary-tree.swift @@ -0,0 +1,50 @@ +/** + * Definition for a binary tree node. + * public class TreeNode { + * public var val: Int + * public var left: TreeNode? + * public var right: TreeNode? + * public init() { self.val = 0; self.left = nil; self.right = nil; } + * public init(_ val: Int) { self.val = val; self.left = nil; self.right = nil; } + * public init(_ val: Int, _ left: TreeNode?, _ right: TreeNode?) { + * self.val = val + * self.left = left + * self.right = right + * } + * } + */ +class Solution { + var globalCount: Int = 0 + + func goodNodes(_ root: TreeNode?) -> Int { + self.globalCount = 0 + visit(root, maxSoFar: nil) + return self.globalCount + } +} + +private extension Solution { + // DFS/Recursive traversal + func visit(_ node: TreeNode?, maxSoFar: Int?) -> Void { + // Base case 1. Node is nil + guard let node = node else { return } + + // Base case 2. Initial value doesn't exist (in case of root) + let maxVal: Int + if let maxSoFar = maxSoFar { + maxVal = max(maxSoFar, node.val) + } else { + maxVal = node.val + } + + // update global count + if !(maxVal > node.val) { + self.globalCount += 1 + } + + // visit children + visit(node.left, maxSoFar: maxVal) + visit(node.right, maxSoFar: maxVal) + } +} + diff --git a/swift/1462-course-schedule-iv.swift b/swift/1462-course-schedule-iv.swift new file mode 100644 index 000000000..3ccde0bc7 --- /dev/null +++ b/swift/1462-course-schedule-iv.swift @@ -0,0 +1,43 @@ +/** + * Question Link: https://leetcode.com/problems/course-schedule-iv/ + */ + + class Solution { + func checkIfPrerequisite(_ numCourses: Int, _ prerequisites: [[Int]], _ queries: [[Int]]) -> [Bool] { + var adj = [Int: [Int]]() + + for i in 0..]() + + func dfs(_ crs: Int) -> Set { + if prereqMap[crs] == nil { + prereqMap[crs] = Set() + for pre in adj[crs]! { + prereqMap[crs]!.formUnion(dfs(pre)) + } + } + prereqMap[crs]?.insert(crs) + return prereqMap[crs]! + } + + for crs in 0.. String { + var steps = steps + while current?.prev != nil && steps > 0 { + current = current?.prev + steps -= 1 + } + return current!.val + } + + // O(n) + func forward(_ steps: Int) -> String { + var steps = steps + while current?.next != nil && steps > 0 { + current = current?.next + steps -= 1 + } + return current!.val + } +} + +// Array Implementation + + +class BrowserHistory { + var array: [String] + var count: Int + var current: Int + + init(_ homepage: String) { + array = [homepage] + count = 1 + current = 0 + } + + // O(1) + func visit(_ url: String) { + if array.count < current + 2 { + array.append(url) + } else { + array[current + 1] = url + } + current += 1 + count = current + 1 + } + + // O(1) + func back(_ steps: Int) -> String { + current = max(current - steps, 0) + return array[current] + } + + // O(1) + func forward(_ steps: Int) -> String { + current = min(current + steps, count - 1) + return array[current] + } +} \ No newline at end of file diff --git a/swift/1489-find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.swift b/swift/1489-find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.swift new file mode 100644 index 000000000..c6b74c8fc --- /dev/null +++ b/swift/1489-find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.swift @@ -0,0 +1,88 @@ +/** + * Question Link: https://leetcode.com/problems/find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree/ + */ + + class UnionFind { + var par: [Int] = [] + var rank: [Int] = [] + + init(_ n: Int) { + par = [Int](0.. Int { + var v1 = v1 + while v1 != par[v1] { + par[v1] = par[par[v1]] + v1 = par[v1] + } + return v1 + } + + func union(_ v1: Int, _ v2: Int) -> Bool { + var p1 = find(v1) + var p2 = find(v2) + if p1 == p2 { + return false + } + if rank[p1] > rank[p2] { + par[p2] = p1 + rank[p1] += rank[p2] + } else { + par[p1] = p2 + rank[p2] += rank[p1] + } + return true + } +} + +class Solution { + func findCriticalAndPseudoCriticalEdges(_ n: Int, _ edges: [[Int]]) -> [[Int]] { + var edges = edges + for i in 0.. mstWeight { + critical.append(i) + continue + } + + let uf2 = UnionFind(n) + uf2.union(n1, n2) + weight = eWeight + for ed in edges { + let v1 = ed[0], v2 = ed[1], w = ed[2] + if uf2.union(v1, v2) { + weight += w + } + } + if weight == mstWeight { + pseudo.append(i) + } + } + return [critical, pseudo] + } +} \ No newline at end of file diff --git a/swift/1493-longest-subarray-of-1s-after-deleting-one-element.swift b/swift/1493-longest-subarray-of-1s-after-deleting-one-element.swift new file mode 100644 index 000000000..a1a712d3f --- /dev/null +++ b/swift/1493-longest-subarray-of-1s-after-deleting-one-element.swift @@ -0,0 +1,22 @@ +class Solution { + func longestSubarray(_ nums: [Int]) -> Int { + var zeroCount = 0 + var res = 0 + var l = 0 + + for r in 0.. 1 { + if nums[l] == 0 { + zeroCount -= 1 + } + l += 1 + } + res = max(res, r - l) + } + + return res + } +} \ No newline at end of file diff --git a/swift/1514-path-with-maximum-probability.swift b/swift/1514-path-with-maximum-probability.swift new file mode 100644 index 000000000..bacce2fa7 --- /dev/null +++ b/swift/1514-path-with-maximum-probability.swift @@ -0,0 +1,85 @@ +/** + * Question Link: https://leetcode.com/problems/path-with-maximum-probability/ + */ + + class Heap { + var heap = [[0.0, 0.0]] + + var count: Int { + heap.count - 1 + } + + func push(_ val: [Double]) { + heap.append(val) + var i = heap.count - 1 + while i > 1 && heap[i][0] > heap[i / 2][0] { + var tmp = heap[i] + heap[i] = heap[i / 2] + heap[i / 2] = tmp + i = i / 2 + } + } + + func pop() -> [Double]? { + if heap.count == 1 { + return nil + } + if heap.count == 2 { + return heap.popLast() + } + var res = heap[1] + heap[1] = heap.removeLast() + var i = 1 + while 2 * i < heap.count { + if 2 * i + 1 < heap.count && heap[2 * i + 1][0] > heap[2 * i][0] && heap[i][0] < heap[2 * i + 1][0] { + var tmp = heap[i] + heap[i] = heap[2 * i + 1] + heap[2 * i + 1] = tmp + i = 2 * i + 1 + } else if heap[i][0] < heap[2 * i][0] { + var tmp = heap[i] + heap[i] = heap[2 * i] + heap[2 * i] = tmp + i = 2 * i + } else { + break + } + } + return res + } +} + +class Solution { + func maxProbability(_ n: Int, _ edges: [[Int]], _ succProb: [Double], _ start_node: Int, _ end_node: Int) -> Double { + var adj = [Double: [[Double]]]() + for i in 1..() + while pq.count > 0 { + let val = pq.pop()! + let prob = val[0] + let cur = val[1] + visit.insert(cur) + if cur == Double(end_node) { + return prob + } + for a in adj[cur]! { + let nei = a[0] + let edgeProb = a[1] + if !visit.contains(nei) { + pq.push([prob * edgeProb, nei]) + } + } + } + return 0.0 + } +} \ No newline at end of file diff --git a/swift/1584-Min-Cost-to-Connect-All-Points.swift b/swift/1584-Min-Cost-to-Connect-All-Points.swift new file mode 100644 index 000000000..43d6fb811 --- /dev/null +++ b/swift/1584-Min-Cost-to-Connect-All-Points.swift @@ -0,0 +1,95 @@ +class DisjointSet { + var roots: [Int] + var ranks: [Int] + + init(n: Int) { + roots = [Int](repeating: 0, count: n) + ranks = [Int](repeating: 0, count: n) + + for i in 0.. Int { + guard u != roots[u] else { return u } + roots[u] = find(roots[u]) + return roots[u] + } + + public func union(_ x: Int, _ y: Int) { + let rootX = roots[x] + let rootY = roots[y] + guard rootX != rootY else { return } + + // assign to the bigger one + if ranks[rootX] > ranks[rootY] { + roots[rootY] = rootX + } else if ranks[rootY] > ranks[rootX] { + roots[rootX] = rootY + } else { + // default: assign to root X + roots[rootY] = rootX + ranks[rootX] += 1 + } + } + + public func areDisjoint(u: Int, v: Int) -> Bool { + find(u) != find(v) + } +} + +struct Edge { + let source: Int + let destination: Int + let weight: Int + + init(u: Int, v: Int, w: Int) { + source = u + destination = v + weight = w + } +} + +class Solution { + func minCostConnectPoints(_ points: [[Int]]) -> Int { + let edges: [Edge] = getEdges(from: points) + // images.sorted(by: { $0.fileID > $1.fileID }) + // sort by ascending order + let sortedEdges = edges.sorted { $0.weight < $1.weight } + var disjointSet = DisjointSet(n: points.count) + var total = 0 + + sortedEdges.forEach { edge in + let u = edge.source + let v = edge.destination + let w = edge.weight + + guard disjointSet.areDisjoint(u: u, v: v) else { return } + disjointSet.union(u, v) + total += w + } + + return total + } +} + +private extension Solution { + func distance(from point1: [Int], to point2: [Int]) -> Int { + abs(point1[0] - point2[0]) + abs(point1[1] - point2[1]) + } + + func getEdges(from points: [[Int]]) -> [Edge] { + var edges: [Edge] = [] + let n = points.count + for i in 0.. Int { + guard u != roots[u] else { return u } + roots[u] = find(roots[u]) + return roots[u] + } + + public func union(_ x: Int, _ y: Int) { + let rootX = roots[x] + let rootY = roots[y] + guard rootX != rootY else { return } + + // assign to the bigger one + if ranks[rootX] > ranks[rootY] { + roots[rootY] = rootX + } else if ranks[rootY] > ranks[rootX] { + roots[rootX] = rootY + } else { + // default: assign to root X + roots[rootY] = rootX + ranks[rootX] += 1 + } + } + + public func areDisjoint(u: Int, v: Int) -> Bool { + find(u) != find(v) + } +} + +struct Edge { + let source: Int + let destination: Int + let weight: Int + + init(u: Int, v: Int, w: Int) { + source = u + destination = v + weight = w + } +} + +class Solution { + func minCostConnectPoints(_ points: [[Int]]) -> Int { + let edges: [Edge] = getEdges(from: points) + // images.sorted(by: { $0.fileID > $1.fileID }) + // sort by ascending order + let sortedEdges = edges.sorted { $0.weight < $1.weight } + var disjointSet = DisjointSet(n: points.count) + var total = 0 + + sortedEdges.forEach { edge in + let u = edge.source + let v = edge.destination + let w = edge.weight + + guard disjointSet.areDisjoint(u: u, v: v) else { return } + disjointSet.union(u, v) + total += w + } + + return total + } +} + +private extension Solution { + func distance(from point1: [Int], to point2: [Int]) -> Int { + abs(point1[0] - point2[0]) + abs(point1[1] - point2[1]) + } + + func getEdges(from points: [[Int]]) -> [Edge] { + var edges: [Edge] = [] + let n = points.count + for i in 0.. Int? { + var val: Int? + if head == nil { + return nil + } else { + val = head?.val + head = head?.next + } + + if head == nil { + tail = nil + } + + count -= 1 + + return val + } +} + +class Solution { + func countStudents(_ students: [Int], _ sandwiches: [Int]) -> Int { + var queue = Queue() + for i in 0.. 0 && changesCount != queue.count { + if queue.head?.val == stack.last { + stack.removeLast() + queue.dequeue() + changesCount = 0 + } else { + if let val = queue.dequeue() { + queue.enqueue(val) + } + changesCount += 1 + } + } + + return queue.count + } +} \ No newline at end of file diff --git a/swift/1851-minimum-interval-to-include-each-query.swift b/swift/1851-minimum-interval-to-include-each-query.swift new file mode 100644 index 000000000..a9bbd6a82 --- /dev/null +++ b/swift/1851-minimum-interval-to-include-each-query.swift @@ -0,0 +1,100 @@ +class Heap { + var heap = [[0]] + + var count: Int { + heap.count - 1 + } + + var first: [Int] { + heap[1] + } + + func heapify(_ arr: [[Int]]) { + heap = arr + heap.append(arr[0]) + var cur = (heap.count - 1) / 2 + while cur > 0 { + var i = cur + while 2 * i < heap.count { + if 2 * i + 1 < heap.count && heap[2 * i + 1][0] < heap[2 * i][0] && heap[i][0] > heap[2 * i + 1][0] { + let tmp = heap[i] + heap[i] = heap[2 * i + 1] + heap[2 * i + 1] = tmp + i = 2 * i + 1 + } else if heap[i][0] > heap[2 * i][0] { + let tmp = heap[i] + heap[i] = heap[2 * i] + heap[2 * i] = tmp + i = 2 * i + } else { + break + } + } + cur -= 1 + } + } + + func push(_ val: [Int]) { + heap.append(val) + var i = heap.count - 1 + while i > 1 && heap[i][0] < heap[i / 2][0] { + let tmp = heap[i] + heap[i] = heap[i / 2] + heap[i / 2] = tmp + i = i / 2 + } + } + + func pop() -> [Int]? { + if heap.count == 1 { + return nil + } + if heap.count == 2 { + return heap.popLast() + } + let res = heap[1] + heap[1] = heap.removeLast() + var i = 1 + while 2 * i < heap.count { + if 2 * i + 1 < heap.count && heap[2 * i + 1][0] < heap[2 * i][0] && heap[i][0] > heap[2 * i + 1][0] { + let tmp = heap[i] + heap[i] = heap[2 * i + 1] + heap[2 * i + 1] = tmp + i = 2 * i + 1 + } else if heap[i][0] > heap[2 * i][0] { + let tmp = heap[i] + heap[i] = heap[2 * i] + heap[2 * i] = tmp + i = 2 * i + } else { + break + } + } + return res + } +} + +class Solution { + func minInterval(_ intervals: [[Int]], _ queries: [Int]) -> [Int] { + var intervals = intervals.sorted(by: { $0[0] < $1[0] }) + let minHeap = Heap() + var res = [Int: Int]() + var i = 0 + for q in queries.sorted() { + while i < intervals.count && intervals[i][0] <= q { + let l = intervals[i][0] + let r = intervals[i][1] + minHeap.push([r - l + 1, r]) + i += 1 + } + + while minHeap.count > 0 && minHeap.first[1] < q { + minHeap.pop() + } + + res[q] = minHeap.count > 0 ? minHeap.first[0] : -1 + } + + return queries.map { res[$0] ?? -1 } + } +} \ No newline at end of file diff --git a/swift/1899-Merge-Triplets-To-Form-Target-Triplet.swift b/swift/1899-Merge-Triplets-To-Form-Target-Triplet.swift new file mode 100644 index 000000000..011b940ca --- /dev/null +++ b/swift/1899-Merge-Triplets-To-Form-Target-Triplet.swift @@ -0,0 +1,27 @@ +class Solution { + func mergeTriplets(_ triplets: [[Int]], _ target: [Int]) -> Bool { + var doesOneValidTripletExist: Bool = false + var maxFromTriplets: [Int] = [0, 0, 0] // 1 <= elements <= 1000 + + for triplet in triplets { + // Ignore those triplets which are not within range + var isWithinRange: Bool = true + for i in 0..<3 { + if triplet[i] > target[i] { + isWithinRange = false + break + } + } + + guard isWithinRange else { continue } + doesOneValidTripletExist = true; + + // Max triplet computation + for i in 0..<3 { + maxFromTriplets[i] = max(maxFromTriplets[i], triplet[i]) + } + } + + return doesOneValidTripletExist && (maxFromTriplets == target) + } +} diff --git a/swift/1899-merge-triplets-to-form-target-triplet.swift b/swift/1899-merge-triplets-to-form-target-triplet.swift new file mode 100644 index 000000000..011b940ca --- /dev/null +++ b/swift/1899-merge-triplets-to-form-target-triplet.swift @@ -0,0 +1,27 @@ +class Solution { + func mergeTriplets(_ triplets: [[Int]], _ target: [Int]) -> Bool { + var doesOneValidTripletExist: Bool = false + var maxFromTriplets: [Int] = [0, 0, 0] // 1 <= elements <= 1000 + + for triplet in triplets { + // Ignore those triplets which are not within range + var isWithinRange: Bool = true + for i in 0..<3 { + if triplet[i] > target[i] { + isWithinRange = false + break + } + } + + guard isWithinRange else { continue } + doesOneValidTripletExist = true; + + // Max triplet computation + for i in 0..<3 { + maxFromTriplets[i] = max(maxFromTriplets[i], triplet[i]) + } + } + + return doesOneValidTripletExist && (maxFromTriplets == target) + } +} diff --git a/swift/1929-concatenation-of-array.swift b/swift/1929-concatenation-of-array.swift new file mode 100644 index 000000000..0918e38c7 --- /dev/null +++ b/swift/1929-concatenation-of-array.swift @@ -0,0 +1,13 @@ +class Solution { + func getConcatenation(_ nums: [Int]) -> [Int] { + var ans: [Int] = [] + + for i in 0..<2 { + for n in nums { + ans.append(n) + } + } + + return ans + } +} \ No newline at end of file diff --git a/swift/1985-Find-The-Kth-Largest-Integer-In-The-Array.swift b/swift/1985-Find-The-Kth-Largest-Integer-In-The-Array.swift new file mode 100644 index 000000000..24ad345d1 --- /dev/null +++ b/swift/1985-Find-The-Kth-Largest-Integer-In-The-Array.swift @@ -0,0 +1,143 @@ +class Solution { + func kthLargestNumber(_ nums: [String], _ k: Int) -> String { + // Use a Min Heap for storing upto k-elements max in the priority queue + var heap: Heap = Heap(sort: { (str1, str2) in + guard str1.count == str2.count else { return str1.count < str2.count } + for (c1, c2) in zip(str1, str2) { + guard c1 == c2 else { return c1 < c2 } + } + return false + }) + + nums.forEach { num in + heap.insert(num) + if heap.count > k { + heap.remove() + } + } + + return heap.peek() ?? "" + } +} + +// Heap used from https://github.com/kodecocodes/swift-algorithm-club/blob/master/Heap/Heap.swift +public struct Heap { + var nodes = [T]() + + private var orderCriteria: (T, T) -> Bool + + public init(sort: @escaping (T, T) -> Bool) { + self.orderCriteria = sort + } + + public init(array: [T], sort: @escaping (T, T) -> Bool) { + self.orderCriteria = sort + configureHeap(from: array) + } + + private mutating func configureHeap(from array: [T]) { + nodes = array + for i in stride(from: (nodes.count/2-1), through: 0, by: -1) { + shiftDown(i) + } + } + + public var isEmpty: Bool { + return nodes.isEmpty + } + + public var count: Int { + return nodes.count + } + + @inline(__always) internal func parentIndex(ofIndex i: Int) -> Int { + return (i - 1) / 2 + } + + @inline(__always) internal func leftChildIndex(ofIndex i: Int) -> Int { + return 2*i + 1 + } + + @inline(__always) internal func rightChildIndex(ofIndex i: Int) -> Int { + return 2*i + 2 + } + + public func peek() -> T? { + return nodes.first + } + + public mutating func insert(_ value: T) { + nodes.append(value) + shiftUp(nodes.count - 1) + } + + public mutating func insert(_ sequence: S) where S.Iterator.Element == T { + for value in sequence { + insert(value) + } + } + + public mutating func replace(index i: Int, value: T) { + guard i < nodes.count else { return } + remove(at: i) + insert(value) + } + + @discardableResult public mutating func remove() -> T? { + guard !nodes.isEmpty else { return nil } + if nodes.count == 1 { + return nodes.removeLast() + } else { + let value = nodes[0] + nodes[0] = nodes.removeLast() + shiftDown(0) + return value + } + } + + @discardableResult public mutating func remove(at index: Int) -> T? { + guard index < nodes.count else { return nil } + let size = nodes.count - 1 + if index != size { + nodes.swapAt(index, size) + shiftDown(from: index, until: size) + shiftUp(index) + } + return nodes.removeLast() + } + + internal mutating func shiftUp(_ index: Int) { + var childIndex = index + let child = nodes[childIndex] + var parentIndex = self.parentIndex(ofIndex: childIndex) + + while childIndex > 0 && orderCriteria(child, nodes[parentIndex]) { + nodes[childIndex] = nodes[parentIndex] + childIndex = parentIndex + parentIndex = self.parentIndex(ofIndex: childIndex) + } + + nodes[childIndex] = child + } + + internal mutating func shiftDown(from index: Int, until endIndex: Int) { + let leftChildIndex = self.leftChildIndex(ofIndex: index) + let rightChildIndex = leftChildIndex + 1 + + var first = index + if leftChildIndex < endIndex && orderCriteria(nodes[leftChildIndex], nodes[first]) { + first = leftChildIndex + } + if rightChildIndex < endIndex && orderCriteria(nodes[rightChildIndex], nodes[first]) { + first = rightChildIndex + } + if first == index { return } + + nodes.swapAt(index, first) + shiftDown(from: first, until: endIndex) + } + + internal mutating func shiftDown(_ index: Int) { + shiftDown(from: index, until: nodes.count) + } +} \ No newline at end of file diff --git a/swift/1985-find-the-kth-largest-integer-in-the-array.swift b/swift/1985-find-the-kth-largest-integer-in-the-array.swift new file mode 100644 index 000000000..24ad345d1 --- /dev/null +++ b/swift/1985-find-the-kth-largest-integer-in-the-array.swift @@ -0,0 +1,143 @@ +class Solution { + func kthLargestNumber(_ nums: [String], _ k: Int) -> String { + // Use a Min Heap for storing upto k-elements max in the priority queue + var heap: Heap = Heap(sort: { (str1, str2) in + guard str1.count == str2.count else { return str1.count < str2.count } + for (c1, c2) in zip(str1, str2) { + guard c1 == c2 else { return c1 < c2 } + } + return false + }) + + nums.forEach { num in + heap.insert(num) + if heap.count > k { + heap.remove() + } + } + + return heap.peek() ?? "" + } +} + +// Heap used from https://github.com/kodecocodes/swift-algorithm-club/blob/master/Heap/Heap.swift +public struct Heap { + var nodes = [T]() + + private var orderCriteria: (T, T) -> Bool + + public init(sort: @escaping (T, T) -> Bool) { + self.orderCriteria = sort + } + + public init(array: [T], sort: @escaping (T, T) -> Bool) { + self.orderCriteria = sort + configureHeap(from: array) + } + + private mutating func configureHeap(from array: [T]) { + nodes = array + for i in stride(from: (nodes.count/2-1), through: 0, by: -1) { + shiftDown(i) + } + } + + public var isEmpty: Bool { + return nodes.isEmpty + } + + public var count: Int { + return nodes.count + } + + @inline(__always) internal func parentIndex(ofIndex i: Int) -> Int { + return (i - 1) / 2 + } + + @inline(__always) internal func leftChildIndex(ofIndex i: Int) -> Int { + return 2*i + 1 + } + + @inline(__always) internal func rightChildIndex(ofIndex i: Int) -> Int { + return 2*i + 2 + } + + public func peek() -> T? { + return nodes.first + } + + public mutating func insert(_ value: T) { + nodes.append(value) + shiftUp(nodes.count - 1) + } + + public mutating func insert(_ sequence: S) where S.Iterator.Element == T { + for value in sequence { + insert(value) + } + } + + public mutating func replace(index i: Int, value: T) { + guard i < nodes.count else { return } + remove(at: i) + insert(value) + } + + @discardableResult public mutating func remove() -> T? { + guard !nodes.isEmpty else { return nil } + if nodes.count == 1 { + return nodes.removeLast() + } else { + let value = nodes[0] + nodes[0] = nodes.removeLast() + shiftDown(0) + return value + } + } + + @discardableResult public mutating func remove(at index: Int) -> T? { + guard index < nodes.count else { return nil } + let size = nodes.count - 1 + if index != size { + nodes.swapAt(index, size) + shiftDown(from: index, until: size) + shiftUp(index) + } + return nodes.removeLast() + } + + internal mutating func shiftUp(_ index: Int) { + var childIndex = index + let child = nodes[childIndex] + var parentIndex = self.parentIndex(ofIndex: childIndex) + + while childIndex > 0 && orderCriteria(child, nodes[parentIndex]) { + nodes[childIndex] = nodes[parentIndex] + childIndex = parentIndex + parentIndex = self.parentIndex(ofIndex: childIndex) + } + + nodes[childIndex] = child + } + + internal mutating func shiftDown(from index: Int, until endIndex: Int) { + let leftChildIndex = self.leftChildIndex(ofIndex: index) + let rightChildIndex = leftChildIndex + 1 + + var first = index + if leftChildIndex < endIndex && orderCriteria(nodes[leftChildIndex], nodes[first]) { + first = leftChildIndex + } + if rightChildIndex < endIndex && orderCriteria(nodes[rightChildIndex], nodes[first]) { + first = rightChildIndex + } + if first == index { return } + + nodes.swapAt(index, first) + shiftDown(from: first, until: endIndex) + } + + internal mutating func shiftDown(_ index: Int) { + shiftDown(from: index, until: nodes.count) + } +} \ No newline at end of file diff --git a/swift/2013-detect-squares.swift b/swift/2013-detect-squares.swift new file mode 100644 index 000000000..09c58c0ff --- /dev/null +++ b/swift/2013-detect-squares.swift @@ -0,0 +1,35 @@ + +class DetectSquares { + + var pts = [[Int]]() + var ptsCount = [[Int]: Int]() + + init() { + + } + + func add(_ point: [Int]) { + ptsCount[point, default: 0] += 1 + pts.append(point) + } + + func count(_ point: [Int]) -> Int { + var res = 0 + var x = point[0], y = point[1] + for val in pts { + let px = val[0], py = val[1] + if abs(py - y) != abs(px - x) || x == px || y == py { + continue + } + res += ptsCount[[x, py], default: 0] * ptsCount[[px, y], default: 0] + } + return res + } +} + +/** + * Your DetectSquares object will be instantiated and called as such: + * let obj = DetectSquares() + * obj.add(point) + * let ret_2: Int = obj.count(point) + */ \ No newline at end of file diff --git a/swift/2130-maximum-twin-sum-of-a-linked-list.swift b/swift/2130-maximum-twin-sum-of-a-linked-list.swift new file mode 100644 index 000000000..823fb8e23 --- /dev/null +++ b/swift/2130-maximum-twin-sum-of-a-linked-list.swift @@ -0,0 +1,35 @@ +/** + * Question Link: https://leetcode.com/problems/maximum-twin-sum-of-a-linked-list/ + */ + + /** + * Definition for singly-linked list. + * public class ListNode { + * public var val: Int + * public var next: ListNode? + * public init() { self.val = 0; self.next = nil; } + * public init(_ val: Int) { self.val = val; self.next = nil; } + * public init(_ val: Int, _ next: ListNode?) { self.val = val; self.next = next; } + * } + */ +class Solution { + func pairSum(_ head: ListNode?) -> Int { + var slow = head + var fast = head + var prev: ListNode? + while fast != nil && fast?.next != nil { + fast = fast?.next?.next + var tmp = slow?.next + slow?.next = prev + prev = slow + slow = tmp + } + var res = 0 + while slow != nil { + res = max(res, prev!.val + slow!.val) + prev = prev?.next + slow = slow?.next + } + return res + } +} \ No newline at end of file diff --git a/swift/2407-longest-increasing-subsequence-ii.swift b/swift/2407-longest-increasing-subsequence-ii.swift new file mode 100644 index 000000000..7d528329e --- /dev/null +++ b/swift/2407-longest-increasing-subsequence-ii.swift @@ -0,0 +1,72 @@ +/** + * Question Link: https://leetcode.com/problems/longest-increasing-subsequence-ii/ + */ + + class SegmentTree { + var sum: Int + var left: SegmentTree! + var right: SegmentTree! + var l: Int + var r: Int + + init(_ total: Int, _ l: Int, _ r: Int) { + self.sum = total + self.l = l + self.r = r + } + + static func build(_ nums: [Int], _ l: Int, _ r: Int) -> SegmentTree { + if l == r { + return SegmentTree(nums[l], l, r) + } + let m = (l + r) / 2 + let root = SegmentTree(0, l, r) + root.left = SegmentTree.build(nums, l, m) + root.right = SegmentTree.build(nums, m + 1, r) + root.sum = max(root.left.sum, root.right.sum) + return root + } + + func update(_ index: Int, _ val: Int) { + if self.l == self.r { + sum = val + return + } + let m = (l + r) / 2 + if index > m { + right.update(index, val) + } else { + left.update(index, val) + } + sum = max(left.sum, right.sum) + } + + func rangeQuery(_ l: Int, _ r: Int) -> Int { + if self.l == l && self.r == r { + return sum + } + let m = (self.l + self.r) / 2 + if l > m { + return right.rangeQuery(l, r) + } else if r <= m { + return left.rangeQuery(l, r) + } else { + return max(left.rangeQuery(l, m), right.rangeQuery(m + 1, r)) + } + } +} + +class Solution { + func lengthOfLIS(_ nums: [Int], _ k: Int) -> Int { + let val = nums.max()! + var res = 1 + let tree = SegmentTree.build([Int](repeating: 0, count: val + 1), 0, val) + for n in nums { + let left = max(0, n - k) + let count = tree.rangeQuery(left, n - 1) + 1 + res = max(res, count) + tree.update(n, count) + } + return res + } +} \ No newline at end of file diff --git a/typescript/0001-two-sum.ts b/typescript/0001-two-sum.ts new file mode 100644 index 000000000..0ff16436c --- /dev/null +++ b/typescript/0001-two-sum.ts @@ -0,0 +1,11 @@ +function twoSum(nums: number[], target: number): number[] { + let hash: { [key: number]: number } = {}; + for (let i = 0; i < nums.length; i++) { + let diff = target - nums[i]; + if (diff in hash) { + return [hash[diff], i]; + } else { + hash[nums[i]] = i; + } + } +} diff --git a/typescript/0002-add-two-numbers.ts b/typescript/0002-add-two-numbers.ts new file mode 100644 index 000000000..94e91f412 --- /dev/null +++ b/typescript/0002-add-two-numbers.ts @@ -0,0 +1,37 @@ +/** + * Definition for singly-linked list. + * class ListNode { + * val: number + * next: ListNode | null + * constructor(val?: number, next?: ListNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + * } + */ + +function addTwoNumbers( + l1: ListNode | null, + l2: ListNode | null +): ListNode | null { + let dummyNode: ListNode = new ListNode(); + let currentNode: ListNode = dummyNode; + let carry: number = 0; + + while (l1 !== null || l2 !== null || carry !== 0) { + const val1 = l1 !== null ? l1.val : 0; + const val2 = l2 !== null ? l2.val : 0; + + const sum: number = val1 + val2 + carry; + const value: number = sum % 10; + const newLinkedList: ListNode = new ListNode(value); + currentNode.next = newLinkedList; + currentNode = newLinkedList; + carry = Math.floor(sum / 10); + + l1 = l1 !== null ? l1.next : null; + l2 = l2 !== null ? l2.next : null; + } + + return dummyNode.next; +} diff --git a/typescript/0003-longest-substring-without-repeating-characters.ts b/typescript/0003-longest-substring-without-repeating-characters.ts new file mode 100644 index 000000000..dd2137f77 --- /dev/null +++ b/typescript/0003-longest-substring-without-repeating-characters.ts @@ -0,0 +1,15 @@ +function lengthOfLongestSubstring(s: string): number { + const charSet = new Set(); + let l = 0; + let res = 0; + + for (let r = 0; r < s.length; r++) { + while (charSet.has(s[r])) { + charSet.delete(s[l]); + l += 1; + } + charSet.add(s[r]); + res = Math.max(res, r - l + 1); + } + return res; +} diff --git a/typescript/0004-median-of-two-sorted-arrays.ts b/typescript/0004-median-of-two-sorted-arrays.ts new file mode 100644 index 000000000..47bd609c5 --- /dev/null +++ b/typescript/0004-median-of-two-sorted-arrays.ts @@ -0,0 +1,35 @@ +function findMedianSortedArrays(nums1: number[], nums2: number[]): number { + if (nums2.length < nums1.length) { + [nums1, nums2] = [nums2, nums1]; + } + + let [left, right] = [0, nums1.length - 1]; + const totalLength = nums1.length + nums2.length; + const mid = totalLength >> 1; + const isEven = totalLength % 2 === 0; + + while (true) { + const mid1 = left + right; + const mid2 = mid - mid1 - 2; + + const getLeft = (nums, index) => (0 <= index ? nums[index] : -Infinity); + const getRight = (nums, index) => + index + 1 < nums.length ? nums[index + 1] : Infinity; + const [aLeft, bLeft] = [getLeft(nums1, mid1), getLeft(nums2, mid2)]; + const [aRight, bRight] = [getRight(nums1, mid1), getRight(nums2, mid2)]; + + if (aLeft <= bRight && bLeft <= aRight) { + return isEven + ? (Math.max(aLeft, bLeft) + Math.min(aRight, bRight)) / 2 + : Math.min(aRight, bRight); + } + + if (aLeft <= bRight) { + left = mid1 + 1; + } + + if (bRight < aLeft) { + right = mid1 - 1; + } + } +} diff --git a/typescript/0005-longest-palindromic-substring.ts b/typescript/0005-longest-palindromic-substring.ts new file mode 100644 index 000000000..f81fbd4ab --- /dev/null +++ b/typescript/0005-longest-palindromic-substring.ts @@ -0,0 +1,34 @@ +function longestPalindrome(s: string): string { + let res = ''; + let resLen = 0; + + for (let i = 0; i < s.length; i++) { + let l = i; + let r = i; + + while (l >= 0 && r < s.length && s[l] === s[r]) { + if (r - l + 1 > resLen) { + res = s.slice(l, r + 1); + resLen = r - l + 1; + } + + l -= 1; + r += 1; + } + + l = i; + r = i + 1; + + while (l >= 0 && r < s.length && s[l] === s[r]) { + if (r - l + 1 > resLen) { + res = s.slice(l, r + 1); + resLen = r - l + 1; + } + + l -= 1; + r += 1; + } + } + + return res; +} diff --git a/typescript/0007-reverse-integer.ts b/typescript/0007-reverse-integer.ts new file mode 100644 index 000000000..fbc6ca8a8 --- /dev/null +++ b/typescript/0007-reverse-integer.ts @@ -0,0 +1,23 @@ +const reverse = (x: number): number => { + const max: number = 2 ** 31 - 1; + const min: number = -(2 ** 31); + + let result: number = 0; + while (x !== 0) { + const digit = x % 10; + x = Math.trunc(x / 10); + + if (result > max / 10 || (result === max / 10 && digit >= max % 10)) { + return 0; + } else if ( + result < min / 10 || + (result === max / 10 && digit <= min % 10) + ) { + return 0; + } else { + result = result * 10 + digit; + } + } + + return result; +}; diff --git a/typescript/0009-palindrome-number.ts b/typescript/0009-palindrome-number.ts new file mode 100644 index 000000000..808ef3dca --- /dev/null +++ b/typescript/0009-palindrome-number.ts @@ -0,0 +1,13 @@ +var isPalindrome = (x: number) => { + // Creates array from int characters + // 121 -> [1,2,1] + let arr = Array.from(String(x), Number); + + // Uses two pointer + for (let i = 0; i < arr.length; i++) { + if (arr[i] !== arr[arr.length - 1 - i]) { + return false; + } + } + return true; +}; diff --git a/typescript/0010-regular-expression-matching.ts b/typescript/0010-regular-expression-matching.ts new file mode 100644 index 000000000..87a8aef6b --- /dev/null +++ b/typescript/0010-regular-expression-matching.ts @@ -0,0 +1,33 @@ +function isMatch(s: string, p: string): boolean { + let lenS = s.length; + let lenP = p.length; + let map = {}; + + return check(0, 0); + + function check(idxS: number, idxP: number): boolean { + if (map[idxS + ':' + idxP] !== undefined) { + return map[idxS + ':' + idxP]; + } + + if (idxS > lenS) { + return false; + } + + if (idxS === lenS && idxP === lenP) { + return true; + } + + if (p[idxP] === '.' || p[idxP] === s[idxS]) { + map[idxS + ':' + idxP] = + p[idxP + 1] === '*' + ? check(idxS + 1, idxP) || check(idxS, idxP + 2) + : check(idxS + 1, idxP + 1); + } else { + map[idxS + ':' + idxP] = + p[idxP + 1] === '*' ? check(idxS, idxP + 2) : false; + } + + return map[idxS + ':' + idxP]; + } +} diff --git a/typescript/0011-container-with-most-water.ts b/typescript/0011-container-with-most-water.ts new file mode 100644 index 000000000..f46b902ee --- /dev/null +++ b/typescript/0011-container-with-most-water.ts @@ -0,0 +1,19 @@ +function maxArea(height: number[]): number { + let l = 0; + let r = height.length - 1; + let max = 0; + + while (l < r) { + let area = (r - l) * Math.min(height[l], height[r]); + + max = Math.max(max, area); + + if (height[l] < height[r]) { + l += 1; + } else { + r -= 1; + } + } + + return max; +} diff --git a/typescript/0012-integer-to-roman.ts b/typescript/0012-integer-to-roman.ts new file mode 100644 index 000000000..5d68ff148 --- /dev/null +++ b/typescript/0012-integer-to-roman.ts @@ -0,0 +1,31 @@ +function intToRoman(num: number): string { + const map = { + M: 1000, + CM: 900, + D: 500, + CD: 400, + C: 100, + XC: 90, + L: 50, + XL: 40, + X: 10, + IX: 9, + V: 5, + IV: 4, + I: 1, + }; + + let result = ''; + + for (let key in map) { + const repeatCounter = Math.floor(num / map[key]); + + if (repeatCounter !== 0) result += key.repeat(repeatCounter); + + num %= map[key]; + + if (num == 0) return result; + } + + return result; +} diff --git a/typescript/0013-roman-to-integer.ts b/typescript/0013-roman-to-integer.ts new file mode 100644 index 000000000..5ac14e020 --- /dev/null +++ b/typescript/0013-roman-to-integer.ts @@ -0,0 +1,23 @@ +function romanToInt(s: string): number { + let roman = { + I: 1, + V: 5, + X: 10, + L: 50, + C: 100, + D: 500, + M: 1000, + }; + + let result = 0; + + for (let i = 0; i < s.length; i++) { + if (i + 1 < s.length && roman[s[i]] < roman[s[i + 1]]) { + result -= roman[s[i]]; + } else { + result += roman[s[i]]; + } + } + + return result; +} diff --git a/typescript/0014-longest-common-prefix.ts b/typescript/0014-longest-common-prefix.ts new file mode 100644 index 000000000..ca4e44848 --- /dev/null +++ b/typescript/0014-longest-common-prefix.ts @@ -0,0 +1,14 @@ +function longestCommonPrefix(strs: string[]): string { + let res = ""; + + for (let i = 0; i < strs[0].length; i++) { + for (const s of strs) { + if (i == s.length || s[i] !== strs[0][i]) { + return res; + } + } + res += strs[0][i] + } + + return res; +}; \ No newline at end of file diff --git a/typescript/0015-3sum.ts b/typescript/0015-3sum.ts new file mode 100644 index 000000000..be88fe6b4 --- /dev/null +++ b/typescript/0015-3sum.ts @@ -0,0 +1,29 @@ +function threeSum(nums: number[]): number[][] { + const res: number[][] = []; + nums = nums.sort((a, b) => a - b); + + for (let i = 0; i < nums.length; i++) { + if (i > 0 && nums[i] == nums[i - 1]) { + continue; + } + + let l = i + 1; + let r = nums.length - 1; + while (l < r) { + let sum = nums[i] + nums[l] + nums[r]; + + if (sum > 0) { + r -= 1; + } else if (sum < 0) { + l += 1; + } else { + res.push([nums[i], nums[l], nums[r]]); + l += 1; + while (nums[l] == nums[l - 1] && l < r) { + l += 1; + } + } + } + } + return res; +} diff --git a/typescript/0017-letter-combinations-of-a-phone-number.ts b/typescript/0017-letter-combinations-of-a-phone-number.ts new file mode 100644 index 000000000..23badfda1 --- /dev/null +++ b/typescript/0017-letter-combinations-of-a-phone-number.ts @@ -0,0 +1,31 @@ +function letterCombinations(digits: string): string[] { + let res: string[] = []; + + const digitToChar = { + '2': 'abc', + '3': 'def', + '4': 'ghi', + '5': 'jkl', + '6': 'mno', + '7': 'qprs', + '8': 'tuv', + '9': 'wxyz', + }; + + function backtrack(i: number, curStr: string) { + if (curStr.length === digits.length) { + res.push(curStr); + return; + } + + for (const c of digitToChar[digits[i]]) { + backtrack(i + 1, curStr + c); + } + } + + if (digits) { + backtrack(0, ''); + } + + return res; +} diff --git a/typescript/0018-4sum.ts b/typescript/0018-4sum.ts new file mode 100644 index 000000000..dfe86108f --- /dev/null +++ b/typescript/0018-4sum.ts @@ -0,0 +1,43 @@ +function fourSum(nums: number[], target: number): number[][] { + const sortedNums: number[] = nums.sort((a: number, b: number) => a - b); + const res: number[][] = []; + const quad: number[] = []; + + const kSum = (k: number, start: number, target: number): void => { + if (k > 2) { + for (let i = start; i < sortedNums.length; i++) { + if (i !== start && sortedNums[i] === sortedNums[i - 1]) { + continue; + } + quad.push(sortedNums[i]); + kSum(k - 1, i + 1, target - sortedNums[i]); + quad.pop(); + } + } else { + let left: number = start; + let right: number = sortedNums.length - 1; + + while (left < right) { + const sum = sortedNums[left] + sortedNums[right]; + if (sum < target) { + left++; + } else if (sum > target) { + right--; + } else { + res.push( + quad.concat([sortedNums[left], sortedNums[right]]) + ); + left++; + while ( + left < right && + sortedNums[left] === sortedNums[left - 1] + ) { + left++; + } + } + } + } + }; + kSum(4, 0, target); + return res; +} diff --git a/typescript/0019-remove-nth-node-from-end-of-list.ts b/typescript/0019-remove-nth-node-from-end-of-list.ts new file mode 100644 index 000000000..dc5330234 --- /dev/null +++ b/typescript/0019-remove-nth-node-from-end-of-list.ts @@ -0,0 +1,31 @@ +/** + * Definition for singly-linked list. + * class ListNode { + * val: number + * next: ListNode | null + * constructor(val?: number, next?: ListNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + * } + */ + +function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null { + let dummy: ListNode = new ListNode(0, head); + let left = dummy; + let right = head; + + while (n > 0) { + right = right.next; + n -= 1; + } + + while (right) { + left = left.next; + right = right.next; + } + + // delete + left.next = left.next.next; + return dummy.next; +} diff --git a/typescript/0020-valid-parentheses.ts b/typescript/0020-valid-parentheses.ts new file mode 100644 index 000000000..c6d9178ae --- /dev/null +++ b/typescript/0020-valid-parentheses.ts @@ -0,0 +1,28 @@ +function isValid(s: string): boolean { + const closingToOpening = { + '}': '{', + ']': '[', + ')': '(', + }; + const stack: string[] = []; + const chars = s.split(''); + + for (let i = 0; i < chars.length; i++) { + const element = chars[i]; + if (element in closingToOpening) { + const pop = stack.pop(); + + if (closingToOpening[element] === pop) { + continue; + } else { + return false; + } + } else { + stack.push(element); + } + } + + if (stack.length > 0) return false; + + return true; +} diff --git a/typescript/0021-merge-two-sorted-lists.ts b/typescript/0021-merge-two-sorted-lists.ts new file mode 100644 index 000000000..83dbbef75 --- /dev/null +++ b/typescript/0021-merge-two-sorted-lists.ts @@ -0,0 +1,36 @@ +/** + * Definition for singly-linked list. + * class ListNode { + * val: number + * next: ListNode | null + * constructor(val?: number, next?: ListNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + * } + */ + +function mergeTwoLists( + list1: ListNode | null, + list2: ListNode | null +): ListNode | null { + let dummyList: ListNode = new ListNode(0); + let currentNode: ListNode = dummyList; + + while (list1 !== null && list2 !== null) { + if (list1.val < list2.val) { + currentNode.next = list1; + list1 = list1.next; + } else { + currentNode.next = list2; + list2 = list2.next; + } + + currentNode = currentNode.next; + } + + if (list1 !== null) currentNode.next = list1; + if (list2 !== null) currentNode.next = list2; + + return dummyList.next; +} diff --git a/typescript/0022-generate-parentheses.ts b/typescript/0022-generate-parentheses.ts new file mode 100644 index 000000000..e51e7c2a6 --- /dev/null +++ b/typescript/0022-generate-parentheses.ts @@ -0,0 +1,27 @@ +function generateParenthesis(n: number): string[] { + const stack: string[] = []; + const res: string[] = []; + + function backtrack(openN: number, closedN: number) { + if (openN === n && closedN === n) { + res.push(stack.join('')); + return; + } + + if (openN < n) { + stack.push('('); + backtrack(openN + 1, closedN); + stack.pop(); + } + + if (closedN < openN) { + stack.push(')'); + backtrack(openN, closedN + 1); + stack.pop(); + } + } + + backtrack(0, 0); + + return res; +} diff --git a/typescript/0023-merge-k-sorted-lists.ts b/typescript/0023-merge-k-sorted-lists.ts new file mode 100644 index 000000000..51a0c4bdf --- /dev/null +++ b/typescript/0023-merge-k-sorted-lists.ts @@ -0,0 +1,52 @@ +/** + * Definition for singly-linked list. + * class ListNode { + * val: number + * next: ListNode | null + * constructor(val?: number, next?: ListNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + * } + */ + +function mergeKLists(lists: Array): ListNode | null { + if (lists.length === 0) return null; + + while (lists.length > 1) { + let resultList = []; + + for (let i = 0; i < lists.length; i += 2) { + let list1 = lists[i]; + let list2; + if (i + 1 > lists.length) list2 = null; + else list2 = lists[i + 1]; + resultList.push(mergeList(list1, list2)); + } + + lists = resultList; + } + return lists[0] || null; +} + +function mergeList( + list1: ListNode | null, + list2: ListNode | null +): ListNode | null { + let dummyNode: ListNode | null = new ListNode(); + let tail = dummyNode; + + while (list1 && list2) { + if (list1.val < list2.val) { + tail.next = list1; + list1 = list1.next; + } else { + tail.next = list2; + list2 = list2.next; + } + tail = tail.next; + } + tail.next = list1 || list2; + + return dummyNode.next; +} diff --git a/typescript/0025-reverse-nodes-in-k-group.ts b/typescript/0025-reverse-nodes-in-k-group.ts new file mode 100644 index 000000000..3d2bafc19 --- /dev/null +++ b/typescript/0025-reverse-nodes-in-k-group.ts @@ -0,0 +1,51 @@ +/** + * Definition for singly-linked list. + * class ListNode { + * val: number + * next: ListNode | null + * constructor(val?: number, next?: ListNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + * } + */ + +function reverseKGroup(head: ListNode | null, k: number): ListNode | null { + let dummy = new ListNode(0, head); + let groupPrev = dummy; + + while (true) { + let kth = getKth(groupPrev, k); + if (!kth) { + break; + } + + let groupNext = kth.next; + + // reverse group + let prev = kth.next; + let curr = groupPrev.next; + + while (curr !== groupNext) { + let temp = curr.next; + curr.next = prev; + prev = curr; + curr = temp; + } + + let temp = groupPrev.next; + groupPrev.next = kth; + groupPrev = temp; + } + + return dummy.next; +} + +function getKth(curr: ListNode | null, k: number): ListNode | null { + while (curr && k > 0) { + curr = curr.next; + k -= 1; + } + + return curr; +} diff --git a/typescript/0026-remove-duplicates-from-sorted-array.ts b/typescript/0026-remove-duplicates-from-sorted-array.ts new file mode 100644 index 000000000..249c23430 --- /dev/null +++ b/typescript/0026-remove-duplicates-from-sorted-array.ts @@ -0,0 +1,12 @@ +function removeDuplicates(nums: number[]): number { + let k = 1; + + for (let i = 1; i < nums.length; i++) { + if (nums[i] !== nums[i - 1]) { + nums[k] = nums[i]; + k += 1; + } + } + + return k; +} diff --git a/typescript/0027-remove-element.ts b/typescript/0027-remove-element.ts new file mode 100644 index 000000000..17ecb7a05 --- /dev/null +++ b/typescript/0027-remove-element.ts @@ -0,0 +1,12 @@ +function removeElement(nums: number[], val: number): number { + let k = 0; + + for (let i = 0; i < nums.length; i++) { + if (nums[i] != val) { + nums[k] = nums[i]; + k += 1; + } + } + + return k; +} diff --git a/typescript/0033-search-in-rotated-sorted-array.ts b/typescript/0033-search-in-rotated-sorted-array.ts new file mode 100644 index 000000000..185348293 --- /dev/null +++ b/typescript/0033-search-in-rotated-sorted-array.ts @@ -0,0 +1,20 @@ +function search(nums: number[], target: number): number { + let left: number = 0; + let right: number = nums.length - 1; + + while (left <= right) { + let midIdx: number = Math.floor((left + right) / 2); + if (nums[midIdx] === target) return midIdx; + + if (nums[left] <= nums[midIdx]) { + if (nums[left] <= target && target <= nums[midIdx]) + right = midIdx - 1; + else left = midIdx + 1; + } else { + if (nums[midIdx] <= target && target <= nums[right]) + left = midIdx + 1; + else right = midIdx - 1; + } + } + return -1; +} diff --git a/typescript/0035-search-insert-position.ts b/typescript/0035-search-insert-position.ts new file mode 100644 index 000000000..d3c2e31dc --- /dev/null +++ b/typescript/0035-search-insert-position.ts @@ -0,0 +1,16 @@ +function searchInsert(nums: number[], target: number): number { + let [left, right] = [0, nums.length - 1]; + + while (left <= right) { + const middle = Math.floor((left + right) / 2); + if (nums[middle] == target) { + return middle; + } else if (nums[middle] < target) { + left = middle + 1; + } else { + right = middle - 1; + } + } + + return left; +} diff --git a/typescript/0036-valid-sudoku.ts b/typescript/0036-valid-sudoku.ts new file mode 100644 index 000000000..8cd21e64f --- /dev/null +++ b/typescript/0036-valid-sudoku.ts @@ -0,0 +1,41 @@ +function isValidSudoku(board: string[][]): boolean { + const rows = {}; + const cols = {}; + const squares = {}; + + for (let r = 0; r < 9; r++) { + for (let c = 0; c < 9; c++) { + const num = board[r][c]; + + if (num === '.') { + continue; + } + + const grid = `${Math.floor(r / 3)}${Math.floor(c / 3)}`; + + if (!cols[c]) { + cols[c] = new Set(); + } + if (!rows[r]) { + rows[r] = new Set(); + } + if (!squares[grid]) { + squares[grid] = new Set(); + } + + if ( + rows[r].has(num) || + cols[c].has(num) || + squares[grid].has(num) + ) { + return false; + } + + cols[c].add(num); + rows[r].add(num); + squares[grid].add(num); + } + } + + return true; +} diff --git a/typescript/0039-combination-sum.ts b/typescript/0039-combination-sum.ts new file mode 100644 index 000000000..afe98a97e --- /dev/null +++ b/typescript/0039-combination-sum.ts @@ -0,0 +1,23 @@ +function combinationSum(candidates: number[], target: number): number[][] { + let res: number[][] = []; + + function dfs(i: number, cur: number[], total: number) { + if (total == target) { + res.push(cur.slice()); + return; + } + if (i >= candidates.length || total > target) { + return; + } + + cur.push(candidates[i]); + dfs(i, cur, total + candidates[i]); + + cur.pop(); + dfs(i + 1, cur, total); + } + + dfs(0, [], 0); + + return res; +} diff --git a/typescript/0040-combination-sum-ii.ts b/typescript/0040-combination-sum-ii.ts new file mode 100644 index 000000000..67014894f --- /dev/null +++ b/typescript/0040-combination-sum-ii.ts @@ -0,0 +1,32 @@ +function combinationSum2(candidates: number[], target: number): number[][] { + candidates.sort(); + + const res: number[][] = []; + + function backtrack(cur: number[], pos: number, target: number) { + if (target === 0) { + res.push(cur.slice()); + } + if (target <= 0) { + return; + } + let prev = -1; + + for (let i = pos; i < candidates.length; i++) { + if (candidates[i] == prev) { + continue; + } + + cur.push(candidates[i]); + + backtrack(cur, i + 1, target - candidates[i]); + + cur.pop(); + prev = candidates[i]; + } + } + + backtrack([], 0, target); + + return res; +} diff --git a/typescript/0041-first-missing-positive.ts b/typescript/0041-first-missing-positive.ts new file mode 100644 index 000000000..0e1d3cabd --- /dev/null +++ b/typescript/0041-first-missing-positive.ts @@ -0,0 +1,26 @@ +function firstMissingPositive(nums: number[]): number { + for (let i = 0; i < nums.length; i++) { + if (nums[i] < 0) { + nums[i] = 0; + } + } + + for (let i = 0; i < nums.length; i++) { + let val = Math.abs(nums[i]); + if (1 <= val && val <= nums.length) { + if (nums[val - 1] > 0) { + nums[val - 1] *= -1; + } else if (nums[val - 1] === 0) { + nums[val - 1] = -1 * (nums.length + 1); + } + } + } + + for (let i = 1; i < nums.length + 1; i++) { + if (nums[i - 1] >= 0) { + return i; + } + } + + return nums.length + 1; +} diff --git a/typescript/0042-trapping-rain-water.ts b/typescript/0042-trapping-rain-water.ts new file mode 100644 index 000000000..72d21956b --- /dev/null +++ b/typescript/0042-trapping-rain-water.ts @@ -0,0 +1,23 @@ +function trap(height: number[]): number { + if (!height) return 0; + + let l = 0; + let r = height.length - 1; + let leftMax = height[l]; + let rightMax = height[r]; + let res = 0; + + while (l < r) { + if (leftMax < rightMax) { + l += 1; + leftMax = Math.max(leftMax, height[l]); + res += leftMax - height[l]; + } else { + r -= 1; + rightMax = Math.max(rightMax, height[r]); + res += rightMax - height[r]; + } + } + + return res; +} diff --git a/typescript/0043-multiply-strings.ts b/typescript/0043-multiply-strings.ts new file mode 100644 index 000000000..35f171121 --- /dev/null +++ b/typescript/0043-multiply-strings.ts @@ -0,0 +1,22 @@ +function multiply(num1: string, num2: string): string { + if (num1 === '0' || num2 === '0') { + return '0'; + } + + let maxLength: number = num1.length + num2.length; + let result: Array = Array(maxLength).fill(0); + for (let i = num2.length - 1; i >= 0; i--) { + let idx = maxLength - (num2.length - i); + + for (let j = num1.length - 1; j >= 0; j--) { + const product = + result[idx] + + parseInt(num1.charAt(j)) * parseInt(num2.charAt(i)); + result[idx] = Math.floor(product % 10); + result[idx - 1] = Math.floor(product / 10) + result[idx - 1]; + idx--; + } + } + if (result[0] === 0) return result.slice(1).join(''); + else return result.join(''); +} diff --git a/typescript/0045-jump-game-ii.ts b/typescript/0045-jump-game-ii.ts new file mode 100644 index 000000000..d46d37d03 --- /dev/null +++ b/typescript/0045-jump-game-ii.ts @@ -0,0 +1,16 @@ +function jump(nums: number[]): number { + let left = 0; + let right = 0; + let res = 0; + + while (right < nums.length - 1) { + let maxJump = 0; + for (let i = left; i <= right; i++) { + maxJump = Math.max(maxJump, i + nums[i]); + } + left = right + 1; + right = maxJump; + res += 1; + } + return res; +} diff --git a/typescript/0046-permutations.ts b/typescript/0046-permutations.ts new file mode 100644 index 000000000..bf2416b75 --- /dev/null +++ b/typescript/0046-permutations.ts @@ -0,0 +1,25 @@ +function permute(nums: number[]): number[][] { + const res: number[][] = []; + + if (nums.length === 1) { + return [nums.slice()]; + } + + for (const i of nums) { + let n = nums.shift()!; + + let perms = permute(nums); + + for (const perm of perms) { + perm.push(n); + } + + perms.forEach((perm) => { + res.push(perm); + }); + + nums.push(n); + } + + return res; +} diff --git a/typescript/0047-permutations-ii.ts b/typescript/0047-permutations-ii.ts new file mode 100644 index 000000000..259c0652e --- /dev/null +++ b/typescript/0047-permutations-ii.ts @@ -0,0 +1,27 @@ +function permuteUnique(nums: number[]): number[][] { + const results: number[][] = []; + const numsCount = new Map(); + for (let n of nums) { + numsCount.set(n, (numsCount.get(n) ?? 0) + 1); + } + + function traverse(sequence: number[] = []) { + if (sequence.length === nums.length) { + results.push([...sequence]); + return; + } + + for (let [n, count] of numsCount) { + if (count === 0) continue; + + numsCount.set(n, count - 1); + sequence.push(n); + traverse(sequence); + sequence.pop(); + numsCount.set(n, count); + } + } + traverse(); + + return results; +}; diff --git a/typescript/0048-rotate-image.ts b/typescript/0048-rotate-image.ts new file mode 100644 index 000000000..744100d5f --- /dev/null +++ b/typescript/0048-rotate-image.ts @@ -0,0 +1,20 @@ +function rotate(matrix: number[][]): void { + let l = 0; + let r = matrix.length - 1; + + while (l < r) { + for (let i = 0; i < r - l; i++) { + let top = l; + let bottom = r; + + let topLeft = matrix[top][l + i]; + + matrix[top][l + i] = matrix[bottom - i][l]; + matrix[bottom - i][l] = matrix[bottom][r - i]; + matrix[bottom][r - i] = matrix[top + i][r]; + matrix[top + i][r] = topLeft; + } + r -= 1; + l += 1; + } +} diff --git a/typescript/0049-group-anagrams.ts b/typescript/0049-group-anagrams.ts new file mode 100644 index 000000000..0c7de67bf --- /dev/null +++ b/typescript/0049-group-anagrams.ts @@ -0,0 +1,57 @@ +function groupAnagrams(strs: string[]): string[][] { + const hash: { + [key: string]: string[]; + } = {}; + + strs.forEach((item, index) => { + let doesExist = false; + Object.keys(hash).forEach((key) => { + if (isAnagram(item, key)) { + hash[key].push(item); + doesExist = true; + } + }); + + if (!doesExist) { + hash[item] = [item]; + } + }); + + console.log(hash); + + return [...Object.keys(hash).map((k) => hash[k])]; +} + +console.log(groupAnagrams(['eat', 'ate', 'dog', 'pog'])); + +function isAnagram(s: string, t: string) { + if (s.length !== t.length) return false; + + var first: Array = s.split(''); + const second = t.split(''); + + for (let i = 0; i < second.length; i++) { + const element = second[i]; + + let found = first.indexOf(element); + + if (found !== -1) { + first[found] = null; + } else { + return false; + } + } + + return true; +} + +// Solution using map with reduce +function groupAnagrams(strs: string[]): string[][] { + const wordsMap = strs.reduce((map, str) => { + const sortedChars = [...str].sort().join(''); + map[sortedChars] = (map[sortedChars] || []).concat(str); + return map; + }, {}); + + return Object.values(wordsMap); +} diff --git a/typescript/0050-powx-n.ts b/typescript/0050-powx-n.ts new file mode 100644 index 000000000..73b76b94c --- /dev/null +++ b/typescript/0050-powx-n.ts @@ -0,0 +1,12 @@ +function myPow(x: number, n: number): number { + function helper(x: number, n: number): number { + if (x == 0) return 0; + if (n == 0) return 1; + + let res = helper(x * x, Math.floor(n / 2)); + return n % 2 ? x * res : res; + } + + let res = helper(x, Math.abs(n)); + return n >= 0 ? res : 1 / res; +} diff --git a/typescript/0051-n-queens.ts b/typescript/0051-n-queens.ts new file mode 100644 index 000000000..35bf5295c --- /dev/null +++ b/typescript/0051-n-queens.ts @@ -0,0 +1,62 @@ +//check if current state is valid +const isStateValid = (state: number[], n: number) => { + //if there are n queens inserted the state is valid + return state.length === n; +}; + +//convert given state(valid) to correct answer format(a string) +const stateToString = (state: number[]) => { + let arrs = state.map((col) => { + let newArr = new Array(state.length).fill('.'); + newArr[col] = 'Q'; + return newArr.join(''); + }); + + return arrs; +}; + +//recursive step +const searchRec = (state: number[], n: number, solutions: string[][]) => { + //if current state is valid, add it to the solutions array and return, we go back to previous states (DFS) + if (isStateValid(state, n)) return solutions.push(stateToString(state)); + + //get new possible candidates (the column in which to place current queen) to add to current State, start with every column + let candidates = new Set([...Array(n).keys()]); + + //the row in which next queen will be added + let rowToAdd = state.length; + + //iterates previous not empty rows to discard candidates before exploring them + for (let row = 0; row < state.length; row++) { + //the column in which is placed the queen at current row + let col = state[row]; + let dist = rowToAdd - row; + //discard the whole column where queen inserte in "row" is + candidates.delete(col); + //right diagonal intersection of queen inserted in "row" with the row where to add new queen(new queen cannot be here) + candidates.delete(col + dist); + //left diagonal intersection of queen inserted in "row" with the row where to add new queen(new queen cannot be here) + candidates.delete(col - dist); + } + + candidates.forEach((cand) => { + //temporarly add candidate to state + state.push(cand); + //explore new state with that candidate + searchRec(state, n, solutions); + //explored, remove the candidate to return at previous state + state.pop(); + }); + + return; +}; + +function solveNQueens(n: number): string[][] { + let solutions = []; + let start = []; + + //explore starting from empty state + searchRec(start, n, solutions); + + return solutions; +} diff --git a/typescript/0053-maximum-subarray.ts b/typescript/0053-maximum-subarray.ts new file mode 100644 index 000000000..d10eccebb --- /dev/null +++ b/typescript/0053-maximum-subarray.ts @@ -0,0 +1,14 @@ +function maxSubArray(nums: number[]): number { + let maxSub = nums[0]; + let curSum = 0; + + for (const n of nums) { + if (curSum < 0) { + curSum = 0; + } + curSum += n; + maxSub = Math.max(maxSub, curSum); + } + + return maxSub; +} diff --git a/typescript/0054-spiral-matrix.ts b/typescript/0054-spiral-matrix.ts new file mode 100644 index 000000000..d15a02e2c --- /dev/null +++ b/typescript/0054-spiral-matrix.ts @@ -0,0 +1,39 @@ +function spiralOrder(matrix: number[][]): number[] { + const res: number[] = []; + let left = 0; + let right = matrix[0].length; + let top = 0; + let bottom = matrix.length; + + while (left < right && top < bottom) { + // get every i in the top row + for (let i = left; i < right; i++) { + res.push(matrix[top][i]); + } + top += 1; + + // get every i in the right column + for (let i = top; i < bottom; i++) { + res.push(matrix[i][right - 1]); + } + right -= 1; + + if (!(left < right && top < bottom)) { + break; + } + + // get ever i in the bottom row + for (let i = right - 1; i > left - 1; i--) { + res.push(matrix[bottom - 1][i]); + } + bottom -= 1; + + // get evet i in the left column + for (let i = bottom - 1; i > top - 1; i--) { + res.push(matrix[i][left]); + } + left += 1; + } + + return res; +} diff --git a/typescript/0055-jump-game.ts b/typescript/0055-jump-game.ts new file mode 100644 index 000000000..e56aa6895 --- /dev/null +++ b/typescript/0055-jump-game.ts @@ -0,0 +1,10 @@ +function canJump(nums: number[]): boolean { + let goal = nums.length - 1; + + for (let i = nums.length - 2; i >= 0; i--) { + if (i + nums[i] >= goal) { + goal = i; + } + } + return goal == 0; +} diff --git a/typescript/0056-merge-intervals.ts b/typescript/0056-merge-intervals.ts new file mode 100644 index 000000000..0835cb77f --- /dev/null +++ b/typescript/0056-merge-intervals.ts @@ -0,0 +1,25 @@ +function merge(intervals: number[][]): number[][] { + intervals.sort(([aStart, aEnd], [bStart, bEnd]) => aStart !== bStart ? aStart - bStart : aEnd - bEnd); + + return mergeInterval(intervals); +}; + +function mergeInterval(intervals: number[][]): number[][] { + let merged = []; + let prev = intervals.shift(); + + for (const curr of intervals) { + const [prevStart, prevEnd] = prev; + const [currStart, currEnd] = curr; + + // Overlap occurs + if (currStart <= prevEnd) { + prev[1] = Math.max(prev[1], curr[1]); + continue; + } + + merged.push(prev); + prev = curr; + } + return [...merged, prev]; +} \ No newline at end of file diff --git a/typescript/0057-insert-interval.ts b/typescript/0057-insert-interval.ts new file mode 100644 index 000000000..9b63bae71 --- /dev/null +++ b/typescript/0057-insert-interval.ts @@ -0,0 +1,30 @@ +function insert(intervals: number[][], newInterval: number[]): number[][] { + const { beforeIndex, before } = getBefore(intervals, newInterval); + const afterIndex = mergeIntervals(intervals, newInterval, beforeIndex); + const after = intervals.slice(afterIndex); + + return [...before, newInterval, ...after]; +}; + +const getBefore = (intervals, newInterval, index = 0, before = []) => { + const hasGap = ([prevStart, prevEnd], [currStart, currEnd]) => prevEnd < currStart; + + while (index < intervals.length && hasGap(intervals[index], newInterval)) { + const current = intervals[index]; + before.push(current); + index++; + } + return { beforeIndex: index, before }; +}; + +const mergeIntervals = (intervals, newInterval, index) => { + const hasOverlap = ([prevStart, prevEnd], [currStart, currEnd]) => currStart <= prevEnd; + + while (index < intervals.length && hasOverlap(newInterval, intervals[index])) { + const current = intervals[index]; + newInterval[0] = Math.min(newInterval[0], current[0]); + newInterval[1] = Math.max(newInterval[1], current[1]); + index++; + } + return index; +}; \ No newline at end of file diff --git a/typescript/0058-length-of-last-word.ts b/typescript/0058-length-of-last-word.ts new file mode 100644 index 000000000..c66e5959c --- /dev/null +++ b/typescript/0058-length-of-last-word.ts @@ -0,0 +1,9 @@ +function lengthOfLastWord(s: string): number { + let res = 0; + for (let i = s.length - 1; i > -1; i--) { + if (s[i] === ' ' && res === 0) continue; + if (s[i] === ' ') break; + res += 1; + } + return res; +} diff --git a/typescript/0062-unique-paths.ts b/typescript/0062-unique-paths.ts new file mode 100644 index 000000000..b57848baa --- /dev/null +++ b/typescript/0062-unique-paths.ts @@ -0,0 +1,15 @@ +function uniquePaths(m: number, n: number): number { + let row = new Array(n).fill(1); + + for (let i = 0; i < m - 1; i++) { + let newRow = new Array(n).fill(1); + + for (let j = n - 2; j > -1; j--) { + newRow[j] = newRow[j + 1] + row[j]; + } + + row = newRow; + } + + return row[0]; +} diff --git a/typescript/0066-plus-one.ts b/typescript/0066-plus-one.ts new file mode 100644 index 000000000..c80377f4d --- /dev/null +++ b/typescript/0066-plus-one.ts @@ -0,0 +1,6 @@ +function plusOne(digits: number[]): number[] { + return (BigInt(digits.join('')) + BigInt(1)) + .toString() + .split('') + .map((item) => Number(item)); +} diff --git a/typescript/0067-add-binary.ts b/typescript/0067-add-binary.ts new file mode 100644 index 000000000..f77af8e62 --- /dev/null +++ b/typescript/0067-add-binary.ts @@ -0,0 +1,21 @@ +function addBinary(a: string, b: string): string { + let res = "" + let carry = 0 + + a = a.split('').reverse().join('') + b = b.split('').reverse().join('') + + for (let i = 0; i < (Math.max(a.length, b.length)); i++) { + let digitA: number = i < a.length ? +a[i] : 0; + let digitB: number = i < b.length ? +b[i] : 0; + + let total = digitA + digitB + carry; + let char = `${total % 2}`; + res = char + res; + carry = Math.floor(total / 2); + } + if (carry) { + res = "1" + res; + } + return res; +}; diff --git a/typescript/0070-climbing-stairs.ts b/typescript/0070-climbing-stairs.ts new file mode 100644 index 000000000..f396c4a35 --- /dev/null +++ b/typescript/0070-climbing-stairs.ts @@ -0,0 +1,11 @@ +function climbStairs(n: number): number { + let one = 1; + let two = 1; + + for (let i = 0; i < n - 1; i++) { + let temp = one; + one = one + two; + two = temp; + } + return one; +} diff --git a/typescript/0071-simplify-path.ts b/typescript/0071-simplify-path.ts new file mode 100644 index 000000000..e8fab3af7 --- /dev/null +++ b/typescript/0071-simplify-path.ts @@ -0,0 +1,21 @@ +function simplifyPath(path: string): string { + const stack: string[] = []; + let cur = ''; + + for (const c of path + '/') { + if (c === '/') { + if (cur === '..') { + if (stack.length > 0) { + stack.pop(); + } + } else if (cur != '' && cur != '.') { + stack.push(cur); + } + cur = ''; + } else { + cur += c; + } + } + + return '/' + stack.join('/'); +} diff --git a/typescript/0072-edit-distance.ts b/typescript/0072-edit-distance.ts new file mode 100644 index 000000000..625810d41 --- /dev/null +++ b/typescript/0072-edit-distance.ts @@ -0,0 +1,24 @@ +function minDistance(word1: string, word2: string): number { + let dp = Array.from({ length: word2.length + 1 }, (_, i) => word2.length - i); + + for (let i = word1.length - 1; i >= 0; i--) { + const current = new Array(word2.length + 1); + current[current.length - 1] = dp[current.length - 1] + 1; + + for (let j = word2.length - 1; j >= 0; j--) { + if (word1[i] === word2[j]) { + current[j] = dp[j + 1]; + } else { + current[j] = 1 + Math.min( + dp[j], + current[j + 1], + dp[j + 1], + ); + } + } + + dp = current; + } + + return dp[0]; +}; diff --git a/typescript/0073-set-matrix-zeroes.ts b/typescript/0073-set-matrix-zeroes.ts new file mode 100644 index 000000000..073c85ed0 --- /dev/null +++ b/typescript/0073-set-matrix-zeroes.ts @@ -0,0 +1,24 @@ +/** + Do not return anything, modify matrix in-place instead. + */ +function setZeroes(matrix: number[][]): void { + let row = new Array(matrix.length); + let col = new Array(matrix[0].length); + + for (let i = 0; i < row.length; i++) { + for (let j = 0; j < col.length; j++) { + if (matrix[i][j] === 0) { + row[i] = 0; + col[j] = 0; + } + } + } + + for (let i = 0; i < row.length; i++) { + for (let j = 0; j < col.length; j++) { + if (row[i] == 0 || col[j] == 0) { + matrix[i][j] = 0; + } + } + } +}; \ No newline at end of file diff --git a/typescript/0074-search-a-2d-matrix.ts b/typescript/0074-search-a-2d-matrix.ts new file mode 100644 index 000000000..1b372e78f --- /dev/null +++ b/typescript/0074-search-a-2d-matrix.ts @@ -0,0 +1,39 @@ +function searchMatrix(matrix: number[][], target: number): boolean { + const rows = matrix.length; + const columns = matrix[0].length; + + let top = 0; + let bot = rows - 1; + + while (top <= bot) { + let row = Math.floor((top + bot) / 2); + if (target > matrix[row][columns - 1]) { + top = row + 1; + } else if (target < matrix[row][0]) { + bot = row - 1; + } else { + break; + } + } + + if (top > bot) { + return false; + } + + let row = Math.floor((top + bot) / 2); + let l = 0; + let r = columns - 1; + + while (l <= r) { + let m = Math.floor((l + r) / 2); + if (target > matrix[row][m]) { + l = m + 1; + } else if (target < matrix[row][m]) { + r = m - 1; + } else { + return true; + } + } + + return false; +} diff --git a/typescript/0075-sort-colors.ts b/typescript/0075-sort-colors.ts new file mode 100644 index 000000000..654062e51 --- /dev/null +++ b/typescript/0075-sort-colors.ts @@ -0,0 +1,26 @@ +/** + Do not return anything, modify nums in-place instead. + */ +function sortColors(nums: number[]): void { + let l = 0; + let r = nums.length - 1; + let i = 0; + + function swap(a: number, b: number) { + let temp = nums[a]; + nums[a] = nums[b]; + nums[b] = temp; + } + + while (i <= r) { + if (nums[i] == 0) { + swap(l, i); + l += 1; + } else if (nums[i] == 2) { + swap(r, i); + r -= 1; + i -= 1; + } + i += 1; + } +} diff --git a/typescript/0076-minimum-window-substring.ts b/typescript/0076-minimum-window-substring.ts new file mode 100644 index 000000000..fdeb14d90 --- /dev/null +++ b/typescript/0076-minimum-window-substring.ts @@ -0,0 +1,48 @@ +function minWindow(s: string, t: string): string { + let minL = 0; + let minR = s.length; + + //create string t hashmap and needed + const tCount = {}; + let needed = 0; + for (let i = 0; i < t.length; i++) { + if (t[i] in tCount) tCount[t[i]]++; + else { + tCount[t[i]] = 1; + needed++; + } + } + + //initialize winCount(empty) + const winCount = {}; + let matched = 0; + + let l = 0; + + for (let r = 0; r < s.length; r++) { + //update winCount with adding s[r] + if (s[r] in winCount) winCount[s[r]]++; + else winCount[s[r]] = 1; + //update matched + if (s[r] in tCount && winCount[s[r]] === tCount[s[r]]) matched++; + + //the window is valid + while (matched === needed) { + //update min + if (r - l + 1 < minR - minL + 1) { + minL = l; + minR = r; + } + + //remove l + //update matched + if (s[l] in tCount && winCount[s[l]] === tCount[s[l]]) matched--; + //update winCount with the removal of s[l] + winCount[s[l]]--; + if (winCount[s[l]] === 0) delete winCount[s[l]]; + l++; + } + } + + return minR - minL + 1 > s.length ? '' : s.slice(minL, minR + 1); +} diff --git a/typescript/0078-subsets.ts b/typescript/0078-subsets.ts new file mode 100644 index 000000000..d897e01b9 --- /dev/null +++ b/typescript/0078-subsets.ts @@ -0,0 +1,20 @@ +function subsets(nums: number[]): number[][] { + let res: number[][] = []; + let subset: number[] = []; + function dfs(i: number) { + if (i >= nums.length) { + res.push(subset.slice()); + return; + } + + subset.push(nums[i]); + dfs(i + 1); + + subset.pop(); + dfs(i + 1); + } + + dfs(0); + + return res; +} diff --git a/typescript/0079-word-search.ts b/typescript/0079-word-search.ts new file mode 100644 index 000000000..cbddab40a --- /dev/null +++ b/typescript/0079-word-search.ts @@ -0,0 +1,46 @@ +function exist(board: string[][], word: string): boolean { + const rowsLength = board.length; + const colsLength = board[0].length; + + function outOfBounds(r: number, c: number) { + return r < 0 || c < 0 || r >= rowsLength || c >= colsLength; + } + + // idx: the index of the current character in the word we're looking for + function dfs(row: number, col: number, idx: number): boolean { + if (idx === word.length) { + return true; + } + if (outOfBounds(row, col) || word[idx] !== board[row][col]) { + return false; + } + + // Mark the current cell as visited + let currentCell = board[row][col]; + board[row][col] = '*'; + + // Pass idx + 1 because we're looking for + // the next character in the word now + let result = dfs(row + 1, col, idx + 1) || // down + dfs(row - 1, col, idx + 1) || // up + dfs(row, col + 1, idx + 1) || // right + dfs(row, col - 1, idx + 1); // left + + // Reset the current cell to its original value + // because we're done visiting it + board[row][col] = currentCell; + + return result; + } + + // For each cell, do a depth-first search + for (let i = 0; i < rowsLength; i++) { + for (let j = 0; j < colsLength; j++) { + if (dfs(i, j, 0)) { + return true; + } + } + } + + return false; +} diff --git a/typescript/0084-largest-rectangle-in-histogram.ts b/typescript/0084-largest-rectangle-in-histogram.ts new file mode 100644 index 000000000..e8336d8be --- /dev/null +++ b/typescript/0084-largest-rectangle-in-histogram.ts @@ -0,0 +1,22 @@ +function largestRectangleArea(heights: number[]): number { + let largestArea = 0; + let stack = []; + + for (let i = 0; i < heights.length; i++) { + let start = i; + + while (stack.length > 0 && stack[stack.length - 1][1] > heights[i]) { + let [lastI, lastH] = stack.pop(); + largestArea = Math.max(largestArea, lastH * (i - lastI)); + start = lastI; + } + stack.push([start, heights[i]]); + } + + for (let j = 0; j < stack.length; j++) { + let currArea = stack[j][1] * (heights.length - stack[j][0]); + largestArea = Math.max(largestArea, currArea); + } + + return largestArea; +} diff --git a/typescript/0088-merge-sorted-array.ts b/typescript/0088-merge-sorted-array.ts new file mode 100644 index 000000000..3d5cda67f --- /dev/null +++ b/typescript/0088-merge-sorted-array.ts @@ -0,0 +1,23 @@ +/** + Do not return anything, modify nums1 in-place instead. + */ +function merge(nums1: number[], m: number, nums2: number[], n: number): void { + let last = m + n - 1; + + while (m > 0 && n > 0) { + if (nums1[m - 1] > nums2[n - 1]) { + nums1[last] = nums1[m - 1]; + m -= 1; + } else { + nums1[last] = nums2[n - 1]; + n -= 1; + } + last -= 1; + } + + while (n > 0) { + nums1[last] = nums2[n - 1]; + n -= 1; + last -= 1; + } +} diff --git a/typescript/0090-subsets-ii.ts b/typescript/0090-subsets-ii.ts new file mode 100644 index 000000000..bd898a7ad --- /dev/null +++ b/typescript/0090-subsets-ii.ts @@ -0,0 +1,26 @@ +function subsetsWithDup(nums: number[]): number[][] { + let res: number[][] = []; + nums.sort(); + + function backtrack(i: number, subset: number[]) { + if (i == nums.length) { + res.push(subset.slice()); + return; + } + + subset.push(nums[i]); + backtrack(i + 1, subset); + + subset.pop(); + + while (i + 1 < nums.length && nums[i] == nums[i + 1]) { + i += 1; + } + + backtrack(i + 1, subset); + } + + backtrack(0, []); + + return res; +} diff --git a/typescript/0091-decode-ways.ts b/typescript/0091-decode-ways.ts new file mode 100644 index 000000000..4e59fcf02 --- /dev/null +++ b/typescript/0091-decode-ways.ts @@ -0,0 +1,20 @@ +function numDecodings(s: string): number { + let dp = { + [s.length]: 1, + }; + + for (let i = s.length - 1; i > -1; i--) { + if (s[i] == '0') { + dp[i] = 0; + } else { + dp[i] = dp[i + 1]; + } + if ( + i + 1 < s.length && + (s[i] == '1' || (s[i] == '2' && '0123456'.includes(s[i + 1]))) + ) { + dp[i] += dp[i + 2]; + } + } + return dp[0]; +} diff --git a/typescript/0093-restore-ip-addresses.ts b/typescript/0093-restore-ip-addresses.ts new file mode 100644 index 000000000..23bb2d817 --- /dev/null +++ b/typescript/0093-restore-ip-addresses.ts @@ -0,0 +1,24 @@ +function restoreIpAddresses(s: string): string[] { + let result: string[] = []; + + if (s.length > 12) return result; + + function backtrack(i: number, dots: number, currentIP: string) { + if (dots == 4 && i == s.length) { + result.push(currentIP.slice(0, currentIP.length - 1)); + return; + } else if (dots > 4) { + return; + } + + for (let j = i; j < Math.min(i + 3, s.length); j++) { + if (parseInt(s.slice(i, j + 1)) < 256 && (i == j || s[i] != '0')) { + backtrack(j + 1, dots + 1, currentIP + s.slice(i, j + 1) + '.'); + } + } + } + + backtrack(0, 0, ''); + + return result; +} diff --git a/typescript/0094-binary-tree-inorder-traversal.ts b/typescript/0094-binary-tree-inorder-traversal.ts new file mode 100644 index 000000000..36aca4064 --- /dev/null +++ b/typescript/0094-binary-tree-inorder-traversal.ts @@ -0,0 +1,23 @@ +/** + * Definition for a binary tree node. + * class TreeNode { + * val: number + * left: TreeNode | null + * right: TreeNode | null + * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + * } + */ + function inorderTraversal(root: TreeNode | null, list: Array = [] ): number[] { + + if (!root) return []; + + inorderTraversal(root.left, list); + list.push(root.val) + inorderTraversal(root.right, list); + + return list +}; \ No newline at end of file diff --git a/typescript/0097-interleaving-string.ts b/typescript/0097-interleaving-string.ts new file mode 100644 index 000000000..b8441aa54 --- /dev/null +++ b/typescript/0097-interleaving-string.ts @@ -0,0 +1,29 @@ +function isInterleave(s1: string, s2: string, s3: string): boolean { + const l1 = s1.length; + const l2 = s2.length; + const l3 = s3.length; + + if (l1 + l2 !== l3) { + return false; + } + + if (!s1 || !s2 || !s3) { + return (!s1 && !s2 && !s3) || (s1 ? s1 === s3 : s2 === s3); + } + + const seen = new Array(l2 + 1); + seen[l2] = true; + + for (let i = l2 - 1; i >= 0; i--) { + seen[i] = seen[i + 1] && s2[i] === s3[l1 + i]; + } + + for (let i = l1 - 1; i >= 0; i--) { + for (let j = l2; j >= 0; j--) { + seen[j] = + (seen[j] && s1[i] === s3[i + j]) || + (j !== l2 && seen[j + 1] && s2[j] === s3[i + j]); + } + } + return seen[0]; +} diff --git a/typescript/0098-validate-binary-search-tree.ts b/typescript/0098-validate-binary-search-tree.ts new file mode 100644 index 000000000..d0971ef2a --- /dev/null +++ b/typescript/0098-validate-binary-search-tree.ts @@ -0,0 +1,39 @@ +/** + * Definition for a binary tree node. + * class TreeNode { + * val: number + * left: TreeNode | null + * right: TreeNode | null + * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + * } + */ + +function isValidBST(root: TreeNode | null): boolean { + return validate(root, null, null); +} + +function validate( + root: TreeNode | null, + max: number | null, + min: number | null +): boolean { + if (!root) { + return true; + } + + if ( + (max !== null && root.val >= max) || + (min !== null && root.val <= min) + ) { + return false; + } + + return ( + validate(root.left, root.val, min) && + validate(root.right, max, root.val) + ); +} diff --git a/typescript/0100-same-tree.ts b/typescript/0100-same-tree.ts new file mode 100644 index 000000000..45ec0ebab --- /dev/null +++ b/typescript/0100-same-tree.ts @@ -0,0 +1,28 @@ +/** + * Definition for a binary tree node. + * class TreeNode { + * val: number + * left: TreeNode | null + * right: TreeNode | null + * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + * } + */ + +function isSameTree(p: TreeNode | null, q: TreeNode | null): boolean { + if (p === null && q === null) return true; + + if ((p === null && q !== null) || (p !== null && q === null)) return false; + + let leftSame = isSameTree(p.left, q.left); + let rightSame = isSameTree(p.right, q.right); + + if (p.val === q.val && leftSame && rightSame) { + return true; + } + + return false; +} diff --git a/typescript/0102-binary-tree-level-order-traversal.ts b/typescript/0102-binary-tree-level-order-traversal.ts new file mode 100644 index 000000000..5913691ab --- /dev/null +++ b/typescript/0102-binary-tree-level-order-traversal.ts @@ -0,0 +1,36 @@ +/** + * Definition for a binary tree node. + * class TreeNode { + * val: number + * left: TreeNode | null + * right: TreeNode | null + * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + * } + */ + +function levelOrder(root: TreeNode | null): number[][] { + const levels: number[][] = []; + + function getHeight(node: TreeNode | null, height: number) { + if (!node) return 0; + + if (node.left || node.right) { + getHeight(node.left, height + 1); + getHeight(node.right, height + 1); + } + + if (levels[height]) { + levels[height].push(node.val); + } else { + levels[height] = [node.val]; + } + } + + getHeight(root, 0); + + return levels; +} diff --git a/typescript/0104-maximum-depth-of-binary-tree.ts b/typescript/0104-maximum-depth-of-binary-tree.ts new file mode 100644 index 000000000..10c14e26c --- /dev/null +++ b/typescript/0104-maximum-depth-of-binary-tree.ts @@ -0,0 +1,19 @@ +/** + * Definition for a binary tree node. + * class TreeNode { + * val: number + * left: TreeNode | null + * right: TreeNode | null + * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + * } + */ + +function maxDepth(root: TreeNode | null): number { + if (!root) return 0; + + return 1 + Math.max(maxDepth(root.left), maxDepth(root.right)); +} diff --git a/typescript/0105-construct-binary-tree-from-preorder-and-inorder-traversal.ts b/typescript/0105-construct-binary-tree-from-preorder-and-inorder-traversal.ts new file mode 100644 index 000000000..b923beed9 --- /dev/null +++ b/typescript/0105-construct-binary-tree-from-preorder-and-inorder-traversal.ts @@ -0,0 +1,26 @@ +/** + * Definition for a binary tree node. + * class TreeNode { + * val: number + * left: TreeNode | null + * right: TreeNode | null + * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + * } + */ + +function buildTree(preorder: number[], inorder: number[]): TreeNode | null { + if (!preorder.length || !inorder.length) { + return null; + } + + let root = new TreeNode(preorder[0]); + let mid = inorder.indexOf(preorder[0]); + + root.left = buildTree(preorder.slice(1, mid + 1), inorder.slice(0, mid)); + root.right = buildTree(preorder.slice(mid + 1), inorder.slice(mid + 1)); + return root; +} diff --git a/typescript/0110-balanced-binary-tree.ts b/typescript/0110-balanced-binary-tree.ts new file mode 100644 index 000000000..5b4dec441 --- /dev/null +++ b/typescript/0110-balanced-binary-tree.ts @@ -0,0 +1,32 @@ +/** + * Definition for a binary tree node. + * class TreeNode { + * val: number + * left: TreeNode | null + * right: TreeNode | null + * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + * } + */ + +function isBalanced(root: TreeNode | null): boolean { + let array = getHeight(root); + return array[0]; +} + +function getHeight(root: TreeNode | null) { + if (!root) return [true, 0]; + + let [leftBalanced, leftHeight] = getHeight(root.left); + let [rightBalanced, rightHeight] = getHeight(root.right); + + let balanced = + leftBalanced && + rightBalanced && + Math.abs(rightHeight - leftHeight) <= 1; + + return [balanced, 1 + Math.max(leftHeight, rightHeight)]; +} diff --git a/typescript/0115-distinct-subsequences.ts b/typescript/0115-distinct-subsequences.ts new file mode 100644 index 000000000..087967d18 --- /dev/null +++ b/typescript/0115-distinct-subsequences.ts @@ -0,0 +1,21 @@ +function numDistinct(s: string, t: string): number { + const sLen = s.length; + const tLen = t.length; + + if (sLen < tLen) { + return 0; + } + + const cache = new Array(tLen).fill(0); + for (let r = sLen - 1; r >= 0; r--) { + let prev = 1; + for (let c = tLen - 1; c >= 0; c--) { + const curr = cache[c]; + if (s[r] === t[c]) { + cache[c] += prev; + } + prev = curr; + } + } + return cache[0]; +} diff --git a/typescript/0118-pascals-triangle.ts b/typescript/0118-pascals-triangle.ts new file mode 100644 index 000000000..e8d854d36 --- /dev/null +++ b/typescript/0118-pascals-triangle.ts @@ -0,0 +1,13 @@ +function generate(numRows: number): number[][] { + let res = [[1]]; + + for (let i = 0; i < numRows - 1; i++) { + let temp = [0, ...res.at(-1), 0]; + let row = []; + for (let j = 0; j < res.at(-1).length + 1; j++) { + row.push(temp[j] + temp[j + 1]); + } + res.push(row); + } + return res; +} diff --git a/typescript/0121-best-time-to-buy-and-sell-stock.ts b/typescript/0121-best-time-to-buy-and-sell-stock.ts new file mode 100644 index 000000000..903468424 --- /dev/null +++ b/typescript/0121-best-time-to-buy-and-sell-stock.ts @@ -0,0 +1,19 @@ +function maxProfit(prices: number[]): number { + let max = 0; + let l = 0; + let r = 1; + + while (r < prices.length) { + if (prices[l] < prices[r]) { + let profit = prices[r] - prices[l]; + if (profit > max) { + max = profit; + } + } else { + l = r; + } + r++; + } + + return max; +} diff --git a/typescript/0124-binary-tree-maximum-path-sum.ts b/typescript/0124-binary-tree-maximum-path-sum.ts new file mode 100644 index 000000000..0b79838b6 --- /dev/null +++ b/typescript/0124-binary-tree-maximum-path-sum.ts @@ -0,0 +1,36 @@ +/** + * Definition for a binary tree node. + * class TreeNode { + * val: number + * left: TreeNode | null + * right: TreeNode | null + * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + * } + */ + +function maxPathSum(root: TreeNode | null): number { + const res = [root.val]; + + // return max path sum without split + function dfs(root: TreeNode | null): number { + if (!root) { + return 0; + } + + let leftMax = dfs(root.left); + let rightMax = dfs(root.right); + leftMax = Math.max(leftMax, 0); + rightMax = Math.max(rightMax, 0); + + // compute max path sum WITH split + res[0] = Math.max(res[0], root.val + leftMax + rightMax); + return root.val + Math.max(leftMax, rightMax); + } + + dfs(root); + return res[0]; +} diff --git a/typescript/0125-valid-palindrome.ts b/typescript/0125-valid-palindrome.ts new file mode 100644 index 000000000..0cc391536 --- /dev/null +++ b/typescript/0125-valid-palindrome.ts @@ -0,0 +1,17 @@ +function isPalindrome(s: string): boolean { + const array = s + .toLowerCase() + .replace(/[^A-Za-z0-9]/g, '') + .replace(/\s/g, '') + .split(''); + + for (let i = 0; i < array.length; i++) { + const first = array[i]; + const second = array[array.length - 1 - i]; + + if (first !== second) { + return false; + } + } + return true; +} diff --git a/typescript/0127-word-ladder.ts b/typescript/0127-word-ladder.ts new file mode 100644 index 000000000..523206043 --- /dev/null +++ b/typescript/0127-word-ladder.ts @@ -0,0 +1,45 @@ +function ladderLength( + beginWord: string, + endWord: string, + wordList: string[] +): number { + if (!wordList.includes(endWord)) { + return 0; + } + let patternMap = {}; + wordList.push(beginWord); + + for (let word of wordList) { + for (let x = 0; x < word.length; x++) { + const pattern = word.slice(0, x) + '*' + word.slice(x + 1); + patternMap[pattern] = patternMap[pattern] || []; + patternMap[pattern].push(word); + } + } + + let wordCount = 1, + queue = [beginWord], + visited = [beginWord]; + while (queue.length) { + const levelSize = queue.length; + for (let x = 0; x < levelSize; x++) { + const word = queue.shift(); + if (word === endWord) { + return wordCount; + } + + for (let x = 0; x < word.length; x++) { + const pattern = word.slice(0, x) + '*' + word.slice(x + 1); + for (let nei of patternMap[pattern]) { + if (nei in visited) { + continue; + } + visited[nei] = nei; + queue.push(nei); + } + } + } + wordCount++; + } + return 0; +} diff --git a/typescript/0128-longest-consecutive-sequence.ts b/typescript/0128-longest-consecutive-sequence.ts new file mode 100644 index 000000000..77963f3c1 --- /dev/null +++ b/typescript/0128-longest-consecutive-sequence.ts @@ -0,0 +1,16 @@ +function longestConsecutive(nums: number[]): number { + const number = new Set(nums); + let longest = 0; + + for (const n of nums) { + if (!number.has(n - 1)) { + let length = 0; + while (number.has(n + length)) { + length += 1; + } + longest = Math.max(length, longest); + } + } + + return longest; +} diff --git a/typescript/0130-surrounded-regions.ts b/typescript/0130-surrounded-regions.ts new file mode 100644 index 000000000..1dfcc3740 --- /dev/null +++ b/typescript/0130-surrounded-regions.ts @@ -0,0 +1,48 @@ +/** + Do not return anything, modify board in-place instead. + */ +function solve(board: string[][]): void { + const rowLen = board.length; + const colLen = board[0].length; + const lastRow = rowLen - 1; + const lastCol = colLen - 1; + + for (let r = 0; r < rowLen; ++r) { + markSeen(r, 0); + markSeen(r, lastCol); + } + for (let c = 1; c < lastCol; ++c) { + markSeen(0, c); + markSeen(lastRow, c); + } + + for (let r = 0; r < rowLen; ++r) { + for (let c = 0; c < colLen; ++c) { + switch (board[r][c]) { + case 'O': + board[r][c] = 'X'; + break; + case 'A': + board[r][c] = 'O'; + break; + } + } + } + +function markSeen(r: number, c: number): void { + if (!inBounds(r, c) || board[r][c] !== 'O') { + return; + } + + board[r][c] = 'A'; + + markSeen(r - 1, c); + markSeen(r + 1, c); + markSeen(r, c - 1); + markSeen(r, c + 1); +} + +function inBounds(r: number, c: number): boolean { + return r >= 0 && c >= 0 && r < rowLen && c < colLen; +} +}; \ No newline at end of file diff --git a/typescript/0131-palindrome-partitioning.ts b/typescript/0131-palindrome-partitioning.ts new file mode 100644 index 000000000..96fe44f82 --- /dev/null +++ b/typescript/0131-palindrome-partitioning.ts @@ -0,0 +1,33 @@ +function partition(s: string): string[][] { + let res: string[][] = []; + let part: string[] = []; + + function dfs(i: number) { + if (i >= s.length) { + res.push(part.slice()); + return; + } + + for (let j = i; j < s.length; j++) { + if (isPali(s, i, j)) { + part.push(s.slice(i, j + 1)); + dfs(j + 1); + part.pop(); + } + } + } + + dfs(0); + return res; + + function isPali(s: string, l: number, r: number) { + while (l < r) { + if (s[l] != s[r]) { + return false; + } + l = l + 1; + r = r - 1; + } + return true; + } +} diff --git a/typescript/0133-clone-graph.ts b/typescript/0133-clone-graph.ts new file mode 100644 index 000000000..949f56784 --- /dev/null +++ b/typescript/0133-clone-graph.ts @@ -0,0 +1,26 @@ +let dfs = (node: Node, memo: Map) => { + if (node === null) return null; + + //if this node has already been visited, simply return the counterpart node of the new graph and return + if (memo.has(node)) return memo.get(node); + + //node hasn't been already visited, create its counterpart version for the new graph + let newNode = new Node(node.val); + //maps to the old graph counterpart(also marked as visited) + memo.set(node, newNode); + + //for each edge of the old node, add that edge in the new graph node + for (let i = 0; i < node.neighbors.length; i++) { + newNode.neighbors.push(dfs(node.neighbors[i], memo)); + } + + return newNode; +}; + +function cloneGraph(node: Node | null): Node | null { + //uses a map, maps old graph nodes with new graph ones + //it also tells us which node of the old graph have already been visited + let memo = new Map(); + + return dfs(node, memo); +} diff --git a/typescript/0134-gas-station.ts b/typescript/0134-gas-station.ts new file mode 100644 index 000000000..8b2c029f2 --- /dev/null +++ b/typescript/0134-gas-station.ts @@ -0,0 +1,21 @@ +function canCompleteCircuit(gas: number[], cost: number[]): number { + let res = 0; + let netDistance = 0; + + // Checks if there is enough gas to complete a cycle + if (gas.reduce((a, b) => a + b) - cost.reduce((a, b) => a + b) < 0) { + return -1; + } + + // finds positive netDistance to check if cycle can be completed + for (let i = 0; i < gas.length; i++) { + netDistance += gas[i] - cost[i]; + + // resets net Distance and gets gas starting gas station + if (netDistance < 0) { + netDistance = 0; + res = i + 1; + } + } + return res; +} diff --git a/typescript/0136-single-number.ts b/typescript/0136-single-number.ts new file mode 100644 index 000000000..6ee083afc --- /dev/null +++ b/typescript/0136-single-number.ts @@ -0,0 +1,9 @@ +function singleNumber(nums: number[]): number { + let res = 0; + + for (let i = 0; i < nums.length; i++) { + res = nums[i] ^ res; + } + + return res; +} diff --git a/typescript/0138-copy-list-with-random-pointer.ts b/typescript/0138-copy-list-with-random-pointer.ts new file mode 100644 index 000000000..e1100634e --- /dev/null +++ b/typescript/0138-copy-list-with-random-pointer.ts @@ -0,0 +1,30 @@ +function copyRandomList(head: Node | null): Node | null { + let hash = new Map(); + + let newHead = new Node(0); + + let itr = head; + + let itrN = newHead; + + while (itr !== null) { + let newN = new Node(itr.val); + itrN.next = newN; + itrN = newN; + hash.set(itr, newN); + + itr = itr.next; + } + + itr = head; + while (itr !== null) { + let inNew = hash.get(itr); + if (itr.random !== null) { + let inNewRand = hash.get(itr.random); + inNew.random = inNewRand; + } + itr = itr.next; + } + + return newHead.next; +} diff --git a/typescript/0139-word-break.ts b/typescript/0139-word-break.ts new file mode 100644 index 000000000..1c0abb046 --- /dev/null +++ b/typescript/0139-word-break.ts @@ -0,0 +1,18 @@ +function wordBreak(s: string, wordDict: string[]): boolean { + const dp = Array(s.length + 1).fill(false); + + dp[s.length] = true; + + for (let i = s.length - 1; i > -1; i--) { + for (const w of wordDict) { + if (i + w.length <= s.length && s.slice(i, i + w.length) == w) { + dp[i] = dp[i + w.length]; + } + if (dp[i]) { + break; + } + } + } + + return dp[0]; +} diff --git a/typescript/0141-linked-list-cycle.ts b/typescript/0141-linked-list-cycle.ts new file mode 100644 index 000000000..7dd62d5ee --- /dev/null +++ b/typescript/0141-linked-list-cycle.ts @@ -0,0 +1,28 @@ +/** + * Definition for singly-linked list. + * class ListNode { + * val: number + * next: ListNode | null + * constructor(val?: number, next?: ListNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + * } + */ + +function hasCycle(head: ListNode | null): boolean { + let slow = head; + let fast = head; + + while (fast && fast.next) { + if (slow) { + slow = slow.next; + } + fast = fast.next.next; + if (slow == fast) { + return true; + } + } + + return false; +} diff --git a/typescript/0143-reorder-list.ts b/typescript/0143-reorder-list.ts new file mode 100644 index 000000000..370ab5b67 --- /dev/null +++ b/typescript/0143-reorder-list.ts @@ -0,0 +1,48 @@ +/** + * Definition for singly-linked list. + * class ListNode { + * val: number + * next: ListNode | null + * constructor(val?: number, next?: ListNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + * } + */ + +/** + Do not return anything, modify head in-place instead. + */ +function reorderList(head: ListNode | null): void { + let slow: ListNode | null | undefined = head; + let fast: ListNode | null | undefined = head?.next; + + while (fast && fast.next) { + slow = slow?.next; + fast = fast?.next?.next; + } + + // reverse second half + if (!(slow && slow.next)) return; + let second: ListNode | null = slow.next; + slow.next = null; + let prev: ListNode | null = null; + while (second) { + let temp = second.next; + second.next = prev; + prev = second; + second = temp; + } + + // merge two halfs + let first: any = head; + second = prev; + while (second) { + let temp1 = first.next; + let temp2 = second.next; + first.next = second; + second.next = temp1; + first = temp1; + second = temp2; + } +} diff --git a/typescript/0144-binary-tree-preorder-traversal.ts b/typescript/0144-binary-tree-preorder-traversal.ts new file mode 100644 index 000000000..141de848f --- /dev/null +++ b/typescript/0144-binary-tree-preorder-traversal.ts @@ -0,0 +1,30 @@ +/** + * Definition for a binary tree node. + * class TreeNode { + * val: number + * left: TreeNode | null + * right: TreeNode | null + * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + * } + */ + +function preorderTraversal(root: TreeNode | null): number[] { + let res: number[] = []; + + function dfs(root) { + if (!root) { + return; + } + + res.push(root.val); + dfs(root.left); + dfs(root.right); + } + + dfs(root); + return res; +} diff --git a/typescript/0145-binary-tree-postorder-traversal.ts b/typescript/0145-binary-tree-postorder-traversal.ts new file mode 100644 index 000000000..1cff52afe --- /dev/null +++ b/typescript/0145-binary-tree-postorder-traversal.ts @@ -0,0 +1,30 @@ +/** + * Definition for a binary tree node. + * class TreeNode { + * val: number + * left: TreeNode | null + * right: TreeNode | null + * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + * } + */ + +function postorderTraversal(root: TreeNode | null): number[] { + let res: number[] = []; + + function dfs(root) { + if (!root) { + return; + } + + dfs(root.left); + dfs(root.right); + res.push(root.val); + } + + dfs(root); + return res; +} diff --git a/typescript/0146-lru-cache.ts b/typescript/0146-lru-cache.ts new file mode 100644 index 000000000..b9b92f77e --- /dev/null +++ b/typescript/0146-lru-cache.ts @@ -0,0 +1,87 @@ +/** + * Your LRUCache object will be instantiated and called as such: + * var obj = new LRUCache(capacity) + * var param_1 = obj.get(key) + * obj.put(key,value) + */ + +type CacheNode = { + value: number, + key: number | null, + next: CacheNode | null, + prev: CacheNode | null, +}; + +class LRUCache { + private size: number; + private capacity: number; + private data: Map; + private head: CacheNode; + private tail: CacheNode; + + constructor(capacity: number) { + this.capacity = capacity; + this.size = 0; + + this.data = new Map(); + // Initialize dummy nodes for convenience + this.head = { value: 0 } as CacheNode; + this.tail = { value: 0 } as CacheNode; + + this.head.next = this.tail; + this.tail.prev = this.head; + } + + get(key: number): number { + const node = this.data.get(key); + if (!node) return -1; + + node.prev.next = node.next; + node.next.prev = node.prev; + + node.next = this.head.next; + node.prev = this.head; + + this.head.next.prev = node; + this.head.next = node; + + return node.value; + } + + put(key: number, value: number): void { + let node = this.data.get(key); + if (!node) { + this.size++; + node = { value, key } as CacheNode; + this.data.set(key, node); + } else { + node.value = value; + node.prev.next = node.next; + node.next.prev = node.prev; + } + + node.next = this.head.next; + node.prev = this.head; + + this.head.next.prev = node; + this.head.next = node; + + if (this.size > this.capacity) { + this.evict(); + } + } + + private evict() { + this.size--; + + const node = this.tail.prev; + + node.prev.next = node.next; + node.next.prev = node.prev; + + node.prev = null; + node.next = null; + + this.data.delete(node.key); + } +} diff --git a/typescript/0149-max-points-on-a-line.ts b/typescript/0149-max-points-on-a-line.ts new file mode 100644 index 000000000..f7bfe4987 --- /dev/null +++ b/typescript/0149-max-points-on-a-line.ts @@ -0,0 +1,23 @@ +function maxPoints(points: number[][]): number { + let res = 1; + + for (let i = 0; i < points.length; i++) { + const count = new Map(); + const point1 = points[i]; + for (let j = i + 1; j < points.length; j++) { + const point2 = points[j]; + let slope; + if (point2[0] === point1[0]) { + slope = Number.MAX_SAFE_INTEGER; + } else { + slope = (point2[1] - point1[1]) / (point2[0] - point1[0]); + } + !count.has(slope) + ? count.set(slope, 2) + : count.set(slope, count.get(slope) + 1); + + res = Math.max(res, count.get(slope)); + } + } + return res; +} diff --git a/typescript/0150-evaluate-reverse-polish-notation.ts b/typescript/0150-evaluate-reverse-polish-notation.ts new file mode 100644 index 000000000..a3c7bec9e --- /dev/null +++ b/typescript/0150-evaluate-reverse-polish-notation.ts @@ -0,0 +1,23 @@ +function evalRPN(tokens: string[]): number { + const stack: number[] = []; + + tokens.forEach((token) => { + if (token === '+') { + stack.push(Number(stack.pop()) + Number(stack.pop())); + } else if (token === '-') { + const a = Number(stack.pop()); + const b = Number(stack.pop()); + stack.push(b - a); + } else if (token === '/') { + const a = Number(stack.pop()); + const b = Number(stack.pop()); + stack.push(Math.trunc(b / a)); + } else if (token === '*') { + stack.push(Number(stack.pop()) * Number(stack.pop())); + } else { + stack.push(Number(token)); + } + }); + + return stack[0]; +} diff --git a/typescript/0152-maximum-product-subarray.ts b/typescript/0152-maximum-product-subarray.ts new file mode 100644 index 000000000..a70db9b85 --- /dev/null +++ b/typescript/0152-maximum-product-subarray.ts @@ -0,0 +1,20 @@ +function maxProduct(nums: number[]): number { + let res = Math.max(...nums); + let curMax = 1; + let curMin = 1; + + for (const n of nums) { + if (n === 0) { + curMin = 1; + curMax = 1; + continue; + } + + let temp = curMax * n; + curMax = Math.max(n * curMax, n * curMin, n); + curMin = Math.min(temp, n * curMin, n); + res = Math.max(res, curMax); + } + + return res; +} diff --git a/typescript/0153-find-minimum-in-rotated-sorted-array.ts b/typescript/0153-find-minimum-in-rotated-sorted-array.ts new file mode 100644 index 000000000..ea326b8f7 --- /dev/null +++ b/typescript/0153-find-minimum-in-rotated-sorted-array.ts @@ -0,0 +1,13 @@ +function findMin(nums: number[]): number { + let left: number = 0; + let right: number = nums.length - 1; + while (right > left) { + let midIdx: number = Math.floor((left + right) / 2); + if (nums[midIdx] > nums[right]) { + left = midIdx + 1; + } else { + right = midIdx; + } + } + return nums[left]; +} diff --git a/typescript/0155-min-stack.ts b/typescript/0155-min-stack.ts new file mode 100644 index 000000000..84e719569 --- /dev/null +++ b/typescript/0155-min-stack.ts @@ -0,0 +1,52 @@ +class MinStack { + stack: number[]; + minstack: number[]; + + constructor() { + this.stack = []; + this.minstack = []; + } + + push(val: number): void { + this.stack.push(val); + if ( + val < this.minstack[this.minstack.length - 1] || + this.minstack.length === 0 + ) { + this.minstack.push(val); + } else { + this.minstack.push(this.minstack[this.minstack.length - 1]); + } + } + + pop(): void { + this.stack.pop(); + this.minstack.pop(); + } + + top(): number { + return this.stack[this.stack.length - 1]; + } + + getMin(): number { + return this.minstack[this.minstack.length - 1]; + } +} + +/** + * Your MinStack object will be instantiated and called as such: + * var obj = new MinStack() + * obj.push(val) + * obj.pop() + * var param_3 = obj.top() + * var param_4 = obj.getMin() + */ + +const minStack = new MinStack(); +minStack.push(-2); +minStack.push(0); +minStack.push(-3); +minStack.getMin(); +minStack.pop(); +minStack.top(); +minStack.getMin(); diff --git a/typescript/0167-two-sum-ii-input-array-is-sorted.ts b/typescript/0167-two-sum-ii-input-array-is-sorted.ts new file mode 100644 index 000000000..ba7eadd09 --- /dev/null +++ b/typescript/0167-two-sum-ii-input-array-is-sorted.ts @@ -0,0 +1,14 @@ +function twoSum(numbers: number[], target: number): number[] { + let L = 0; + let R = numbers.length - 1; + + while (numbers[L] + numbers[R] !== target) { + if (numbers[L] + numbers[R] > target) { + R = R - 1; + } else if (numbers[L] + numbers[R] < target) { + L = L + 1; + } + } + + return [L + 1, R + 1]; +} diff --git a/typescript/0169-majority-element.ts b/typescript/0169-majority-element.ts new file mode 100644 index 000000000..89b35ed86 --- /dev/null +++ b/typescript/0169-majority-element.ts @@ -0,0 +1,18 @@ +function majorityElement(nums: number[]): number { + let count = 0; + let res = 0; + + for (let i = 0; i < nums.length; i++) { + if (count === 0) { + res = nums[i]; + } + + if (nums[i] === res) { + count += 1; + } else { + count -= 1; + } + } + + return res; +} diff --git a/typescript/0179-largest-number.ts b/typescript/0179-largest-number.ts new file mode 100644 index 000000000..cdd8723e8 --- /dev/null +++ b/typescript/0179-largest-number.ts @@ -0,0 +1,14 @@ +function largestNumber(nums: number[]): string { + const strings = nums.map((num) => String(num)); + + function compare(s1: string, s2: string) { + if (s1 + s2 > s2 + s1) { + return -1; + } else { + return 1; + } + } + + const res = strings.sort(compare).join(''); + return res.charAt(0) === '0' ? '0' : res; +} diff --git a/typescript/0187-repeated-dna-sequences.ts b/typescript/0187-repeated-dna-sequences.ts new file mode 100644 index 000000000..3f959523c --- /dev/null +++ b/typescript/0187-repeated-dna-sequences.ts @@ -0,0 +1,14 @@ +function findRepeatedDnaSequences(s: string): string[] { + let seen = new Set(); + let res = new Set(); + + for (let i = 0; i < s.length - 9; i++) { + let cur = s.slice(i, i + 10); + if (seen.has(cur)) { + res.add(cur); + } + seen.add(cur); + } + + return Array.from(res); +} diff --git a/typescript/0189-rotate-array.ts b/typescript/0189-rotate-array.ts new file mode 100644 index 000000000..67275f586 --- /dev/null +++ b/typescript/0189-rotate-array.ts @@ -0,0 +1,35 @@ +/** + Do not return anything, modify nums in-place instead. + */ +function rotate(nums: number[], k: number): void { + k = k % nums.length; + let l = 0; + let r = nums.length - 1; + while (l < r) { + let temp = nums[l]; + nums[l] = nums[r]; + nums[r] = temp; + l += 1; + r -= 1; + } + + l = 0; + r = k - 1; + while (l < r) { + let temp = nums[l]; + nums[l] = nums[r]; + nums[r] = temp; + l += 1; + r -= 1; + } + + l = k; + r = nums.length - 1; + while (l < r) { + let temp = nums[l]; + nums[l] = nums[r]; + nums[r] = temp; + l += 1; + r -= 1; + } +} diff --git a/typescript/0190-reverse-bits.ts b/typescript/0190-reverse-bits.ts new file mode 100644 index 000000000..9f40fc7ca --- /dev/null +++ b/typescript/0190-reverse-bits.ts @@ -0,0 +1,11 @@ +function reverseBits(n: number): number { + let result = 0b0; + + for (let i = 0; i < 32; i++) { + const bit = n & 0b1; + result <<= 1; + result |= bit; + n >>= 1; + } + return result >>> 0; +} diff --git a/typescript/0191-number-of-1-bits.ts b/typescript/0191-number-of-1-bits.ts new file mode 100644 index 000000000..d2e9cba35 --- /dev/null +++ b/typescript/0191-number-of-1-bits.ts @@ -0,0 +1,12 @@ +function hammingWeight(n: number): number { + let base2 = n.toString(2).split(''); + let count = 0; + + base2.forEach((item) => { + if (item === '1') { + count += 1; + } + }); + + return count; +} diff --git a/typescript/0198-house-robber.ts b/typescript/0198-house-robber.ts new file mode 100644 index 000000000..fda63adc5 --- /dev/null +++ b/typescript/0198-house-robber.ts @@ -0,0 +1,12 @@ +function rob(nums: number[]): number { + let rob1 = 0; + let rob2 = 0; + + for (const n of nums) { + let temp = Math.max(n + rob1, rob2); + rob1 = rob2; + rob2 = temp; + } + + return rob2; +} diff --git a/typescript/0199-binary-tree-right-side-view.ts b/typescript/0199-binary-tree-right-side-view.ts new file mode 100644 index 000000000..532e46c1d --- /dev/null +++ b/typescript/0199-binary-tree-right-side-view.ts @@ -0,0 +1,42 @@ +/** + * Definition for a binary tree node. + * class TreeNode { + * val: number + * left: TreeNode | null + * right: TreeNode | null + * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + * } + */ + +function rightSideView(root: TreeNode | null): number[] { + let result = []; + let queue = []; + + if (root === null) { + return []; + } + + queue.push(root); + + while (queue.length > 0) { + let length = queue.length; + for (let i = 0; i < length; i++) { + let node = queue.shift(); + + if (i === length - 1) { + result.push(node.val); + } + if (node.left !== null) { + queue.push(node.left); + } + if (node.right !== null) { + queue.push(node.right); + } + } + } + return result; +} diff --git a/typescript/0200-number-of-islands.ts b/typescript/0200-number-of-islands.ts new file mode 100644 index 000000000..e10908dfb --- /dev/null +++ b/typescript/0200-number-of-islands.ts @@ -0,0 +1,54 @@ +//BFS way to solve this +const bfs = (grid, r, c) => { + const [ROWS, COLS] = [grid.length, grid[0].length]; + + const directions = [ + [-1, 0], + [1, 0], + [0, -1], + [0, 1], + ]; + + let queue = [[r, c]]; + + //marks as visited + grid[r][c] = '0'; + + while (queue.length > 0) { + //dequeues the first element(current) + let [cr, cc] = queue.shift(); + + directions.forEach(([dr, dc]) => { + let [nr, nc] = [cr + dr, cc + dc]; + if ( + !( + nr < 0 || + nc < 0 || + nr >= ROWS || + nc >= COLS || + grid[nr][nc] === '0' + ) + ) { + queue.push([nr, nc]); + grid[nr][nc] = '0'; + } + }); + } +}; + +function numIslands(grid: string[][]): number { + const [ROWS, COLS] = [grid.length, grid[0].length]; + + let islands = 0; + + for (let i = 0; i < ROWS; i++) { + for (let j = 0; j < COLS; j++) { + if (grid[i][j] === '1') { + bfs(grid, i, j); + islands++; + } + } + } + + return islands; +} diff --git a/typescript/0202-happy-number.ts b/typescript/0202-happy-number.ts new file mode 100644 index 000000000..3bd506415 --- /dev/null +++ b/typescript/0202-happy-number.ts @@ -0,0 +1,25 @@ +function isHappy(n: number): boolean { + const visit = new Set(); + + while (!visit.has(n)) { + visit.add(n); + n = sumOfSquares(n); + + if (n == 1) return true; + } + + return false; +} + +function sumOfSquares(n: number): number { + let output = 0; + + while (n) { + let digit = n % 10; + digit = digit ** 2; + output += digit; + n = Math.floor(n / 10); + } + + return output; +} diff --git a/typescript/0203-remove-linked-list-elements.ts b/typescript/0203-remove-linked-list-elements.ts new file mode 100644 index 000000000..19522876a --- /dev/null +++ b/typescript/0203-remove-linked-list-elements.ts @@ -0,0 +1,33 @@ +/** + * Definition for singly-linked list. + * class ListNode { + * val: number + * next: ListNode | null + * constructor(val?: number, next?: ListNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + * } + */ + +function removeElements(head: ListNode | null, val: number): ListNode | null { + + let sentinel_node : ListNode = new ListNode(0, head); + let slow_pointer : ListNode | null = sentinel_node; + let fast_pointer : ListNode | null = null; + + while (slow_pointer) { + + // get next legible node + fast_pointer = slow_pointer.next; + while (fast_pointer && fast_pointer.val === val) { + fast_pointer = fast_pointer.next; + } + + // Set next node to the legible node + slow_pointer.next = fast_pointer; + slow_pointer = slow_pointer.next; + } + + return sentinel_node.next; +}; \ No newline at end of file diff --git a/typescript/0205-isomorphic-strings.ts b/typescript/0205-isomorphic-strings.ts new file mode 100644 index 000000000..6abf97e1a --- /dev/null +++ b/typescript/0205-isomorphic-strings.ts @@ -0,0 +1,17 @@ +function isIsomorphic(s: string, t: string): boolean { + const mapST = {}; + const mapTS = {}; + + for (let i = 0; i < s.length; i++) { + let c1 = s[i]; + let c2 = t[i]; + + if ((mapST[c1] && mapST[c1] !== c2) || (mapTS[c2] && mapTS[c2] !== c1)) + return false; + + mapST[c1] = c2; + mapTS[c2] = c1; + } + + return true; +} diff --git a/typescript/0206-reverse-linked-list.ts b/typescript/0206-reverse-linked-list.ts new file mode 100644 index 000000000..e8c6207b1 --- /dev/null +++ b/typescript/0206-reverse-linked-list.ts @@ -0,0 +1,23 @@ +/** + * Definition for singly-linked list. + * class ListNode { + * val: number + * next: ListNode | null + * constructor(val?: number, next?: ListNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + * } + */ + +function reverseList(head: ListNode | null): ListNode | null { + let prev: ListNode | null = null; + + while (head) { + let ele = head.next; + head.next = prev; + prev = head; + head = ele; + } + return prev; +} diff --git a/typescript/0207-course-schedule.ts b/typescript/0207-course-schedule.ts new file mode 100644 index 000000000..c7d44599a --- /dev/null +++ b/typescript/0207-course-schedule.ts @@ -0,0 +1,49 @@ +function canFinish(numCourses: number, prerequisites: number[][]): boolean { + const graph = createGraph(numCourses, prerequisites); + let seen = new Set(); + let seeing = new Set(); + + function explore(course: number): boolean { + if (seen.has(course)) { + return true; + } + if (seeing.has(course)) { + return false; + } + + seeing.add(course); + for (let neighbor of graph[course]) { + if (!explore(neighbor)) { + return false; + } + } + + seen.add(course); + seeing.delete(course); + return true; + } + + for (let i = 0; i < numCourses; i++) { + if (!explore(i)) { + return false; + } + } + return true; +} + +function createGraph(numCourses: number, edges: number[][]): number[][] { + const graph = Array.from({ length: numCourses }, () => []); + + for (let edge of edges) { + let [a, b] = edge; + + if (!(a in graph)) { + graph[a] = []; + } + if (!(b in graph)) { + graph[b] = []; + } + graph[a].push(b); + } + return graph; +} diff --git a/typescript/0208-implement-trie-prefix-tree.ts b/typescript/0208-implement-trie-prefix-tree.ts new file mode 100644 index 000000000..7a86a84dd --- /dev/null +++ b/typescript/0208-implement-trie-prefix-tree.ts @@ -0,0 +1,61 @@ +class TrieNode { + children: {}; + endOfWord: boolean; + constructor() { + this.children = {}; + this.endOfWord = false; + } +} + +class Trie { + root: TrieNode; + constructor() { + this.root = new TrieNode(); + } + + insert(word: string): void { + let cur = this.root; + + for (const c of word) { + if (!(c in cur.children)) { + cur.children[c] = new TrieNode(); + } + cur = cur.children[c]; + } + cur.endOfWord = true; + } + + search(word: string): boolean { + let cur = this.root; + + for (const c of word) { + if (!(c in cur.children)) { + return false; + } + cur = cur.children[c]; + } + + return cur.endOfWord; + } + + startsWith(prefix: string): boolean { + let cur = this.root; + + for (const c of prefix) { + if (!(c in cur.children)) { + return false; + } + cur = cur.children[c]; + } + + return true; + } +} + +const trie = new Trie(); +trie.insert('apple'); +trie.search('apple'); // return True +trie.search('app'); // return False +trie.startsWith('app'); // return True +trie.insert('app'); +trie.search('app'); // return True diff --git a/typescript/0210-course-schedule-ii.ts b/typescript/0210-course-schedule-ii.ts new file mode 100644 index 000000000..0a7ce9fa4 --- /dev/null +++ b/typescript/0210-course-schedule-ii.ts @@ -0,0 +1,41 @@ +function findOrder(numCourses: number, prerequisites: number[][]): number[] { + const prereq = []; + for (let i = 0; i < numCourses; i++) { + prereq[i] = []; + } + for (const [crs, pre] of prerequisites) { + prereq[crs].push(pre); + } + + const output = []; + const visit = new Set(); + const cycle = new Set(); + + function dfs(course: number): boolean { + if (cycle.has(course)) { + return false; + } + if (visit.has(course)) { + return true; + } + + cycle.add(course); + for (const pre of prereq[course]) { + if (!dfs(pre)) { + return false; + } + } + cycle.delete(course); + visit.add(course); + output.push(course); + return true; + } + + for (let j = 0; j < numCourses; j++) { + if (!dfs(j)) { + return []; + } + } + + return output; +} diff --git a/typescript/0211-design-add-and-search-words-data-structure.ts b/typescript/0211-design-add-and-search-words-data-structure.ts new file mode 100644 index 000000000..2bbcdd69a --- /dev/null +++ b/typescript/0211-design-add-and-search-words-data-structure.ts @@ -0,0 +1,62 @@ +class TrieNode { + children: { [key: string]: TrieNode }; + endOfWord: boolean; + constructor() { + this.children = {}; + this.endOfWord = false; + } +} + +class WordDictionary { + root: TrieNode; + constructor() { + this.root = new TrieNode(); + } + + addWord(word: string): void { + let cur = this.root; + + for (const c of word) { + if (!(c in cur.children)) { + cur.children[c] = new TrieNode(); + } + cur = cur.children[c]; + } + cur.endOfWord = true; + } + + search(word: string): boolean { + function dfs(j: number, root: TrieNode): boolean { + let cur = root; + + for (let i = j; i < word.length; i++) { + const c = word[i]; + + if (c === '.') { + for (const key in cur.children) { + if ( + Object.prototype.hasOwnProperty.call( + cur.children, + key + ) + ) { + const child = cur.children[key]; + if (dfs(i + 1, child)) { + return true; + } + } + } + return false; + } else { + if (!(c in cur.children)) { + return false; + } + cur = cur.children[c]; + } + } + + return cur.endOfWord; + } + return dfs(0, this.root); + } +} diff --git a/typescript/0212-word-search-ii.ts b/typescript/0212-word-search-ii.ts new file mode 100644 index 000000000..bd23c5cc8 --- /dev/null +++ b/typescript/0212-word-search-ii.ts @@ -0,0 +1,65 @@ +class TrieNode { + children = new Array(26); + word = false; +} + +class Trie { + root = new TrieNode(); + + add(word: string) { + let current = this.root; + for (let c of word) { + if (!current.children[c]) current.children[c] = new TrieNode(); + current = current.children[c]; + } + + current.word = true; + } +} + +function findWords(board: string[][], words: string[]): string[] { + const trie = new Trie(); + for (let word of words) { + trie.add(word); + } + + const rows = board.length; + const cols = board[0].length; + const resultSet = new Set(); + + function dfs(r: number, c: number, node: TrieNode, word: string) { + if (r < 0 || r >= rows) return; + if (c < 0 || c >= cols) return; + if (board[r][c] === '.') return; + + const currentChar = board[r][c]; + const currentNode = node.children[currentChar]; + if (!currentNode) return; + + const currentWord = word + currentChar; + if (currentNode.word) { + resultSet.add(currentWord); + } + + const directions = [ + [r - 1, c], + [r, c + 1], + [r + 1, c], + [r, c - 1], + ]; + for (let [nr, nc] of directions) { + dfs(nr, nc, currentNode, currentWord); + } + + board[r][c] = '.'; + board[r][c] = currentChar; + } + + for (let r = 0; r < rows; r++) { + for (let c = 0; c < cols; c++) { + dfs(r, c, trie.root, ''); + } + } + + return [...resultSet]; +}; diff --git a/typescript/0213-house-robber-ii.ts b/typescript/0213-house-robber-ii.ts new file mode 100644 index 000000000..b8cbfe6f8 --- /dev/null +++ b/typescript/0213-house-robber-ii.ts @@ -0,0 +1,20 @@ +function helper(nums: number[]): number { + let rob1 = 0; + let rob2 = 0; + + for (const n of nums) { + let temp = Math.max(n + rob1, rob2); + rob1 = rob2; + rob2 = temp; + } + + return rob2; +} + +function rob(nums: number[]): number { + return Math.max( + nums[0], + helper(nums.slice(0, nums.length - 1)), + helper(nums.slice(1)) + ); +} diff --git a/typescript/0215-kth-largest-element-in-an-array.ts b/typescript/0215-kth-largest-element-in-an-array.ts new file mode 100644 index 000000000..921a44f57 --- /dev/null +++ b/typescript/0215-kth-largest-element-in-an-array.ts @@ -0,0 +1,33 @@ +function findKthLargest(nums: number[], k: number): number { + k = nums.length - k; + + function quickSelect(l: number, r: number): number { + const pivot = nums[r]; + let p = l; + let i = l; + let temp; + while (i >= l && i < r) { + if (nums[i] <= pivot) { + temp = nums[p]; + nums[p] = nums[i]; + nums[i] = temp; + p++; + } + i++; + } + + temp = nums[p]; + nums[p] = nums[r]; + nums[r] = temp; + + if (p > k) { + return quickSelect(l, p - 1); + } + if (p < k) { + return quickSelect(p + 1, r); + } + return nums[p]; + } + + return quickSelect(0, nums.length - 1); +} diff --git a/typescript/0217-contains-duplicate.ts b/typescript/0217-contains-duplicate.ts new file mode 100644 index 000000000..f93d89eb9 --- /dev/null +++ b/typescript/0217-contains-duplicate.ts @@ -0,0 +1,10 @@ +function containsDuplicate(nums: number[]): boolean { + const set = new Set(); + + for (let i = 0; i < nums.length; i++) { + if (set.has(nums[i])) return true; + else set.add(nums[i]); + } + + return false; +} diff --git a/typescript/0219-contains-duplicate-ii.ts b/typescript/0219-contains-duplicate-ii.ts new file mode 100644 index 000000000..6e0443338 --- /dev/null +++ b/typescript/0219-contains-duplicate-ii.ts @@ -0,0 +1,15 @@ +function containsNearbyDuplicate(nums: number[], k: number): boolean { + let map = new Map() + for(let i = 0; i < nums.length; i++){ + if(!map.has(nums[i])){ + map.set(nums[i], i) + }else{ + if(i - map.get(nums[i]) <= k){ + return true + }else{ + map.set(nums[i], i) + } + } + } + return false +}; diff --git a/typescript/0225-implement-stack-using-queues.ts b/typescript/0225-implement-stack-using-queues.ts new file mode 100644 index 000000000..49cb8789a --- /dev/null +++ b/typescript/0225-implement-stack-using-queues.ts @@ -0,0 +1,26 @@ +class MyStack { + q: number[]; + + constructor() { + this.q = []; + } + + push(val: number) { + this.q.push(val); + } + + pop(): number { + for (let i = 0; i < this.q.length - 1; i++) { + this.q.push(this.q.shift()!); + } + return this.q.shift()!; + } + + top(): number { + return this.q[this.q.length - 1]; + } + + empty(): boolean { + return this.q.length == 0; + } +} diff --git a/typescript/0226-invert-binary-tree.ts b/typescript/0226-invert-binary-tree.ts new file mode 100644 index 000000000..959c509c8 --- /dev/null +++ b/typescript/0226-invert-binary-tree.ts @@ -0,0 +1,27 @@ +/** + * Definition for a binary tree node. + * class TreeNode { + * val: number + * left: TreeNode | null + * right: TreeNode | null + * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + * } + */ + +function invertTree(root: TreeNode | null): TreeNode | null { + let temp: TreeNode | null; + + if (!root) return null; + + if (root.left || root.right) { + temp = root.left; + root.left = invertTree(root.right); + root.right = invertTree(temp); + } + + return root; +} diff --git a/typescript/0230-kth-smallest-element-in-a-bst.ts b/typescript/0230-kth-smallest-element-in-a-bst.ts new file mode 100644 index 000000000..7fad80ef0 --- /dev/null +++ b/typescript/0230-kth-smallest-element-in-a-bst.ts @@ -0,0 +1,17 @@ +let inDepth = (node, stack) => { + if (node === null) return; + + inDepth(node.left, stack); + + stack.push(node.val); + + inDepth(node.right, stack); +}; + +function kthSmallest(root: TreeNode | null, k: number): number { + let stack = []; + + inDepth(root, stack); + + return stack[k - 1]; +} diff --git a/typescript/0235-lowest-common-ancestor-of-a-binary-search-tree.ts b/typescript/0235-lowest-common-ancestor-of-a-binary-search-tree.ts new file mode 100644 index 000000000..6d4db5b9b --- /dev/null +++ b/typescript/0235-lowest-common-ancestor-of-a-binary-search-tree.ts @@ -0,0 +1,31 @@ +/** + * Definition for a binary tree node. + * class TreeNode { + * val: number + * left: TreeNode | null + * right: TreeNode | null + * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + * } + */ + +function lowestCommonAncestor( + root: TreeNode | null, + p: TreeNode | null, + q: TreeNode | null +): TreeNode | null { + let cur = root; + + while (cur) { + if (p.val > cur.val && q.val > cur.val) { + cur = cur.right; + } else if (p.val < cur.val && q.val < cur.val) { + cur = cur.left; + } else { + return cur; + } + } +} diff --git a/typescript/0238-product-of-array-except-self.ts b/typescript/0238-product-of-array-except-self.ts new file mode 100644 index 000000000..e2bcf6883 --- /dev/null +++ b/typescript/0238-product-of-array-except-self.ts @@ -0,0 +1,18 @@ +function productExceptSelf(nums: number[]): number[] { + const array: Array = []; + let product: number = 1; + + for (let idx = 0; idx < nums.length; idx++) { + array[idx] = product; + product *= nums[idx]; + } + + product = 1; + + for (let idx = nums.length - 1; idx >= 0; idx--) { + array[idx] *= product; + product *= nums[idx]; + } + + return array; +} diff --git a/typescript/0239-sliding-window-maximum.ts b/typescript/0239-sliding-window-maximum.ts new file mode 100644 index 000000000..a5c7fa7d1 --- /dev/null +++ b/typescript/0239-sliding-window-maximum.ts @@ -0,0 +1,27 @@ +function maxSlidingWindow(nums: number[], k: number): number[] { + const res: number[] = []; + let l = 0; + let r = 0; + + const queue: number[] = []; + + while (r < nums.length) { + while (queue.length > 0 && nums[queue[queue.length - 1]] < nums[r]) { + queue.pop(); + } + queue.push(r); + + if (l > queue[0]) { + queue.shift(); + } + + if (r + 1 >= k) { + res.push(nums[queue[0]]); + l += 1; + } + + r += 1; + } + + return res; +} diff --git a/typescript/0242-valid-anagram.ts b/typescript/0242-valid-anagram.ts new file mode 100644 index 000000000..76a5ef0c5 --- /dev/null +++ b/typescript/0242-valid-anagram.ts @@ -0,0 +1,20 @@ +function isAnagram(s: string, t: string) { + if (s.length !== t.length) return false; + + let first: Array = s.split(''); + const second = t.split(''); + + for (let i = 0; i < second.length; i++) { + const element = second[i]; + + let found = first.indexOf(element); + + if (found !== -1) { + first[found] = null; + } else { + return false; + } + } + + return true; +} diff --git a/typescript/0256-paint-house.ts b/typescript/0256-paint-house.ts new file mode 100644 index 000000000..91039e223 --- /dev/null +++ b/typescript/0256-paint-house.ts @@ -0,0 +1,11 @@ +// Dynamic Programming, Time Complexity: O(n), Space Complexity: O(1) +function minCost(costs: number[][]): number { + let dp: number[] = [0, 0, 0]; + for ( const cost of costs ) { + let currMin0: number = cost[0] + Math.min(dp[1], dp[2]); + let currMin1: number = cost[1] + Math.min(dp[0], dp[2]); + let currMin2: number = cost[2] + Math.min(dp[0], dp[1]); + dp = [currMin0, currMin1, currMin2]; + } + return Math.min(...dp); +}; diff --git a/typescript/0261-graph-valid-tree.ts b/typescript/0261-graph-valid-tree.ts new file mode 100644 index 000000000..99d286cd9 --- /dev/null +++ b/typescript/0261-graph-valid-tree.ts @@ -0,0 +1,42 @@ +interface Obj { + [key: number]: Array; +} + +interface Visited { + [key: number]: boolean; +} + +function validTree(n: number, edges: number[][]): boolean { + if (n === 0) return true; + + let adjacent: Obj = {}; + let visited: Visited = {}; + for (let i = 0; i < n; i++) { + adjacent[i] = []; + } + + for (const val of edges) { + const [n1, n2] = val; + adjacent[n1].push(n2); + adjacent[n2].push(n1); + } + + return dfs(0, -1, visited, adjacent) && Object.keys(visited).length === n; +} + +function dfs( + node: number, + prevNode: number, + visited: Visited, + adjacent: Obj +): boolean { + if (visited[node]) return false; + visited[node] = true; + + for (const ele of adjacent[node]) { + if (ele === prevNode) continue; + if (!dfs(ele, node, visited, adjacent)) return false; + } + + return true; +} diff --git a/typescript/0263-ugly-number.ts b/typescript/0263-ugly-number.ts new file mode 100644 index 000000000..735817907 --- /dev/null +++ b/typescript/0263-ugly-number.ts @@ -0,0 +1,13 @@ +function isUgly(n: number): boolean { + if (n < 1) { + return false; + } + + for (let prime of [2, 3, 5]) { + while (n % prime == 0) { + n /= prime; + } + } + + return n == 1; +} diff --git a/typescript/0268-missing-number.ts b/typescript/0268-missing-number.ts new file mode 100644 index 000000000..e6907a99f --- /dev/null +++ b/typescript/0268-missing-number.ts @@ -0,0 +1,8 @@ +function missingNumber(nums: number[]): number { + let sum: number = 0; + let total: number = (nums.length * (nums.length + 1)) / 2; + for (let i = 0; i < nums.length; i++) { + sum += nums[i]; + } + return total - sum; +} diff --git a/typescript/0271-encode-and-decode-strings.ts b/typescript/0271-encode-and-decode-strings.ts new file mode 100644 index 000000000..a28bb40e6 --- /dev/null +++ b/typescript/0271-encode-and-decode-strings.ts @@ -0,0 +1,21 @@ +function encode(strs: string[]): string { + return strs.map((str) => `${str.length}#${str}`).join(''); +} + +function decode(str: string): string[] { + let decodedWords: string[] = []; + let i = 0; + + while (i < str.length) { + let j: number = i; + while (str[j] !== '#') { + j++; + } + let len: number = parseInt(str.slice(i, j), 10); + decodedWords.push(str.slice(j + 1, j + 1 + len)); + i = j + 1 + len; + } + return decodedWords; +} + +export {}; diff --git a/typescript/0283-move-zeroes.ts b/typescript/0283-move-zeroes.ts new file mode 100644 index 000000000..c045cd175 --- /dev/null +++ b/typescript/0283-move-zeroes.ts @@ -0,0 +1,15 @@ +/** + Do not return anything, modify nums in-place instead. + */ +function moveZeroes(nums: number[]): void { + let left = 0; + + for (let index in nums) { + if (nums[index] != 0) { + const temp = nums[left]; + nums[left] = nums[index]; + nums[index] = temp; + left++; + } + } +} diff --git a/typescript/0287-find-the-duplicate-number.ts b/typescript/0287-find-the-duplicate-number.ts new file mode 100644 index 000000000..d68cf28d4 --- /dev/null +++ b/typescript/0287-find-the-duplicate-number.ts @@ -0,0 +1,33 @@ +/** + * Definition for singly-linked list. + * class ListNode { + * val: number + * next: ListNode | null + * constructor(val?: number, next?: ListNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + * } + */ + +function findDuplicate(nums: number[]): number { + let slow = 0; + let fast = 0; + while (true) { + slow = nums[slow]; + fast = nums[nums[fast]]; + if (slow == fast) { + break; + } + } + let slow2 = 0; + + while (true) { + slow = nums[slow]; + slow2 = nums[slow2]; + if (slow == slow2) { + break; + } + } + return slow; +} diff --git a/typescript/0290-word-pattern.ts b/typescript/0290-word-pattern.ts new file mode 100644 index 000000000..9178fd420 --- /dev/null +++ b/typescript/0290-word-pattern.ts @@ -0,0 +1,25 @@ +function wordPattern(pattern: string, s: string): boolean { + const words = s.split(' '); + const patternToWord = {}; + const wordToPattern = {}; + + if (pattern.length != words.length) return false; + + for (let i = 0; i < pattern.length; i++) { + if (!patternToWord[pattern[i]]) { + patternToWord[pattern[i]] = words[i]; + } + + if (!wordToPattern[words[i]]) { + wordToPattern[words[i]] = pattern[i]; + } + + if ( + patternToWord[pattern[i]] !== words[i] || + wordToPattern[words[i]] !== pattern[i] + ) + return false; + } + + return true; +} diff --git a/typescript/0295-find-median-from-data-stream.ts b/typescript/0295-find-median-from-data-stream.ts new file mode 100644 index 000000000..4feaac219 --- /dev/null +++ b/typescript/0295-find-median-from-data-stream.ts @@ -0,0 +1,52 @@ +/* + * Time Complexity: O(logn) + * Space Complexity: O(n) +*/ + +class MedianFinder { + public minHeap; + public maxHeap; + + constructor() { + this.minHeap = new MinPriorityQueue(); + this.maxHeap = new MaxPriorityQueue(); + } + + addNum(num: number): void { + this.getHeap(num).enqueue(num); + this.rebalance(); + } + + findMedian(): number { + if (this.minHeap.size() === this.maxHeap.size()) { + return (this.minHeap.front().element + this.maxHeap.front().element) / 2; + } + + return this.maxHeap.front().element; + } + + rebalance() { + if (this.minHeap.size() + 1 < this.maxHeap.size()) { + return this.minHeap.enqueue(this.maxHeap.dequeue().element); + } + + if (this.maxHeap.size() < this.minHeap.size()) { + return this.maxHeap.enqueue(this.minHeap.dequeue().element); + } + } + + getHeap(num: number) { + if (this.maxHeap.isEmpty() || num <= this.maxHeap.front().element) { + return this.maxHeap; + } + + return this.minHeap; + } +} + +/** + * Your MedianFinder object will be instantiated and called as such: + * var obj = new MedianFinder() + * obj.addNum(num) + * var param_2 = obj.findMedian() + */ diff --git a/typescript/0297-serialize-and-deserialize-binary-tree.ts b/typescript/0297-serialize-and-deserialize-binary-tree.ts new file mode 100644 index 000000000..c60a3cc68 --- /dev/null +++ b/typescript/0297-serialize-and-deserialize-binary-tree.ts @@ -0,0 +1,54 @@ +/** + * Definition for a binary tree node. + * class TreeNode { + * val: number + * left: TreeNode | null + * right: TreeNode | null + * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + * } + */ + +/* + * Encodes a tree to a single string. + */ +function serialize(root: TreeNode | null): string { + const box = []; + + function cereal(root: TreeNode | null) { + if (root === null) return box.push(null); + box.push(root.val); + cereal(root.left); + cereal(root.right); + } + cereal(root); + + return box.join(','); +}; + +/* + * Decodes your encoded data to tree. + */ +function deserialize(data: string): TreeNode | null { + const box: string[] = data.split(','); + + function decereal(): TreeNode | null { + const val = box.shift(); + if (val === '') return null; + const node = new TreeNode(Number(val)); + node.left = decereal(); + node.right = decereal(); + return node; + } + + return decereal(); +}; + + +/** + * Your functions will be called as such: + * deserialize(serialize(root)); + */ \ No newline at end of file diff --git a/typescript/0300-longest-increasing-subsequence.ts b/typescript/0300-longest-increasing-subsequence.ts new file mode 100644 index 000000000..9d0c0145c --- /dev/null +++ b/typescript/0300-longest-increasing-subsequence.ts @@ -0,0 +1,13 @@ +function lengthOfLIS(nums: number[]): number { + const lis = Array(nums.length).fill(1); + + for (let i = nums.length - 1; i > -1; i--) { + for (let j = i + 1; j < nums.length; j++) { + if (nums[i] < nums[j]) { + lis[i] = Math.max(lis[i], 1 + lis[j]); + } + } + } + + return Math.max(...lis); +} diff --git a/typescript/0303-range-sum-query-immutable.ts b/typescript/0303-range-sum-query-immutable.ts new file mode 100644 index 000000000..757a620a6 --- /dev/null +++ b/typescript/0303-range-sum-query-immutable.ts @@ -0,0 +1,22 @@ +class NumArray { + + prefixSums: number[] = []; + + constructor(nums: number[]) { + this.prefixSums.push(nums[0]); + + for(let i=1; i> 1; + if (index <= mid) { + this.left.update(index, value); + } else { + this.right.update(index, value); + } + + this.sum = this.left.sum + this.right.sum; + } + + queryRange(leftIndex: number, rightIndex: number) { + if (this.leftIndex === leftIndex && this.rightIndex === rightIndex) { + return this.sum; + } + + const mid = (this.leftIndex + this.rightIndex) >> 1; + if (leftIndex > mid) { + return this.right.queryRange(leftIndex, rightIndex); + } else if (rightIndex <= mid){ + return this.left.queryRange(leftIndex, rightIndex); + } + + return this.left.queryRange(leftIndex, mid) + this.right.queryRange(mid + 1, rightIndex); + } +} + +function build(values: number[], leftIndex: number, rightIndex: number) { + if (leftIndex === rightIndex) { + return new SegmentTree(leftIndex, rightIndex, values[leftIndex]); + } + + const root = new SegmentTree(leftIndex, rightIndex, 0); + const mid = (leftIndex + rightIndex) >> 1; + + root.left = build(values, leftIndex, mid); + root.right = build(values, mid + 1, rightIndex); + root.sum = root.left.sum + root.right.sum; + + return root; +} + +class NumArray { + root: SegmentTree; + + constructor(nums: number[]) { + this.root = build(nums, 0, nums.length - 1); + } + + update(index: number, val: number): void { + this.root.update(index, val); + } + + sumRange(left: number, right: number): number { + return this.root.queryRange(left, right); + } +} + +/** + * Your NumArray object will be instantiated and called as such: + * var obj = new NumArray(nums) + * obj.update(index,val) + * var param_2 = obj.sumRange(left,right) + */ diff --git a/typescript/0309-best-time-to-buy-and-sell-stock-with-cooldown.ts b/typescript/0309-best-time-to-buy-and-sell-stock-with-cooldown.ts new file mode 100644 index 000000000..4b595a789 --- /dev/null +++ b/typescript/0309-best-time-to-buy-and-sell-stock-with-cooldown.ts @@ -0,0 +1,11 @@ +function maxProfit(prices: number[]): number { + let [ sold, hold, rest ] = [ 0, Number.MIN_SAFE_INTEGER, 0]; + + for (let i = 0; i < prices.length; i++) { + let prevSold = sold; + sold = hold + prices[i]; + hold = Math.max(hold, rest - prices[i]); + rest = Math.max(rest, prevSold); + } + return Math.max(sold, rest); +}; \ No newline at end of file diff --git a/typescript/0312-burst-balloons.ts b/typescript/0312-burst-balloons.ts new file mode 100644 index 000000000..510cf5982 --- /dev/null +++ b/typescript/0312-burst-balloons.ts @@ -0,0 +1,21 @@ +function maxCoins(nums: number[]): number { + let vals = [1, ...nums, 1]; + let n = nums.length; + let dp = [...Array(n + 2)].map(() => Array(n + 2).fill(0)); + + for (let len = 1; len <= n; len++) { + for (let i = 1; i + len <= n + 1; i++) { + let j = i + len - 1; + for (let k = i; k <= j; k++) { + dp[i][j] = Math.max( + dp[i][j], + dp[i][k - 1] + + vals[i - 1] * vals[k] * vals[j + 1] + + dp[k + 1][j] + ); + } + } + } + + return dp[1][n]; +} diff --git a/typescript/0322-coin-change.ts b/typescript/0322-coin-change.ts new file mode 100644 index 000000000..74bffaa51 --- /dev/null +++ b/typescript/0322-coin-change.ts @@ -0,0 +1,15 @@ +function coinChange(coins: number[], amount: number): number { + const dp = Array(amount + 1).fill(amount + 1); + + dp[0] = 0; + + for (let a = 1; a < amount + 1; a++) { + for (const c of coins) { + if (a - c >= 0) { + dp[a] = Math.min(dp[a], 1 + dp[a - c]); + } + } + } + + return dp[amount] != amount + 1 ? dp[amount] : -1; +} diff --git a/typescript/0338-counting-bits.ts b/typescript/0338-counting-bits.ts new file mode 100644 index 000000000..560f590d8 --- /dev/null +++ b/typescript/0338-counting-bits.ts @@ -0,0 +1,22 @@ +function countBits(n: number): number[] { + const ans: number[] = []; + + for (let i = 0; i < n + 1; i++) { + ans.push(hammingWeight(i)); + } + + return ans; +} + +function hammingWeight(n: number): number { + let base2 = n.toString(2).split(''); + let count = 0; + + base2.forEach((item) => { + if (item === '1') { + count += 1; + } + }); + + return count; +} diff --git a/typescript/0344-reverse-string.ts b/typescript/0344-reverse-string.ts new file mode 100644 index 000000000..fc0b6b876 --- /dev/null +++ b/typescript/0344-reverse-string.ts @@ -0,0 +1,12 @@ +function reverseString(s: string[]): void { + let l = 0; + let r = s.length - 1; + + while (l < r) { + let temp = s[l]; + s[l] = s[r]; + s[r] = temp; + l += 1; + r -= 1; + } +} diff --git a/typescript/0347-top-k-frequent-elements.ts b/typescript/0347-top-k-frequent-elements.ts new file mode 100644 index 000000000..0baa7d127 --- /dev/null +++ b/typescript/0347-top-k-frequent-elements.ts @@ -0,0 +1,51 @@ +function topKFrequent(nums: number[], k: number): number[] | undefined { + const hash: { + [key: number]: number; + } = {}; + + const freq: number[][] = Array.apply(null, Array(nums.length + 1)).map( + () => [] + ); + + nums.forEach((item) => { + if (hash[item]) { + hash[item]++; + } else { + hash[item] = 1; + } + }); + + Object.keys(hash).forEach((item) => { + const key = Number(item); + const value = hash[item]; + freq[value].push(key); + }); + + const res: number[] = []; + + for (let i = freq.length - 1; i >= 0; i--) { + const element = freq[i]; + for (let j = 0; j < element.length; j++) { + const second = element[j]; + res.push(Number(second)); + if (res.length == k) { + return res; + } + } + } +} + +function topKFrequentNLogN(nums: number[], k: number): number[] { + const map = nums.reduce((acc, num) => { + const currentNumber = acc[String(num)]; + acc[num] = currentNumber ? currentNumber + 1 : 1; + return acc; + }, {}); + + return Object.entries(map) + .sort( + ([, countA], [, countB]) => (countB as number) - (countA as number) + ) + .slice(0, k) + .map(([num]) => +num); +} diff --git a/typescript/0352-data-stream-as-disjoint-intervals.ts b/typescript/0352-data-stream-as-disjoint-intervals.ts new file mode 100644 index 000000000..cfc6e1942 --- /dev/null +++ b/typescript/0352-data-stream-as-disjoint-intervals.ts @@ -0,0 +1,32 @@ +class SummaryRanges { + numSet: Set; + + constructor() { + this.numSet = new Set(); + } + + addNum(value: number): void { + this.numSet.add(value); + } + + getIntervals(): number[][] { + let nums = Array.from(this.numSet.keys()); + nums.sort((a, b) => a - b); + let res: number[][] = []; + + let i = 0; + + while (i < nums.length) { + const start = nums[i]; + + while (i + 1 < nums.length && nums[i] + 1 == nums[i + 1]) { + i++; + } + + res.push([start, nums[i]]); + i++; + } + + return res; + } +} diff --git a/typescript/0371-sum-of-two-integers.ts b/typescript/0371-sum-of-two-integers.ts new file mode 100644 index 000000000..93cb7d19d --- /dev/null +++ b/typescript/0371-sum-of-two-integers.ts @@ -0,0 +1,11 @@ +function getSum(a: number, b: number): number { + let res = a; + let secondInt = b; + + while (secondInt != 0) { + let temp = (res & secondInt) << 1; + res ^= secondInt; + secondInt = temp; + } + return res; +} diff --git a/typescript/0380-insert-delete-getrandom-o1.ts b/typescript/0380-insert-delete-getrandom-o1.ts new file mode 100644 index 000000000..39f09cd12 --- /dev/null +++ b/typescript/0380-insert-delete-getrandom-o1.ts @@ -0,0 +1,35 @@ +class RandomizedSet { + map: { [key: number]: number }; + arr: number[]; + + constructor() { + this.map = {}; + this.arr = []; + } + + insert(val: number): boolean { + let res = !this.map.hasOwnProperty(val); + if (res) { + this.map[val] = this.arr.length; + this.arr.push(val); + } + return res; + } + + remove(val: number): boolean { + let res = this.map.hasOwnProperty(val); + if (res) { + let idx = this.map[val]; + let lastVal = this.arr.at(-1); + this.arr[idx] = lastVal; + this.arr.pop(); + this.map[lastVal] = idx; + delete this.map[val]; + } + return res; + } + + getRandom(): number { + return this.arr[Math.floor(Math.random() * this.arr.length)]; + } +} diff --git a/typescript/0392-is-subsequence.ts b/typescript/0392-is-subsequence.ts new file mode 100644 index 000000000..8263b0615 --- /dev/null +++ b/typescript/0392-is-subsequence.ts @@ -0,0 +1,13 @@ +function isSubsequence(s: string, t: string): boolean { + let i = 0; + let j = 0; + + while (i < s.length && j < t.length) { + if (s[i] === t[j]) { + i += 1; + } + j += 1; + } + + return i === s.length; +} diff --git a/typescript/0394-decode-string.ts b/typescript/0394-decode-string.ts new file mode 100644 index 000000000..81af32520 --- /dev/null +++ b/typescript/0394-decode-string.ts @@ -0,0 +1,26 @@ +function decodeString(s: string): string { + let stack: string[] = []; + + for (let char of s) { + if (char !== ']') { + stack.push(char); + } else { + let substr: string = ''; + while (stack[stack.length - 1] !== '[') { + substr = stack.pop() + substr; + } + stack.pop(); + + let multiplier: string = ''; + while ( + stack.length !== 0 && + Number.isInteger(+stack[stack.length - 1]) + ) { + multiplier = stack.pop() + multiplier; + } + + stack.push(substr.repeat(+multiplier)); + } + } + return stack.join(''); +} diff --git a/typescript/0402-remove-k-digits.ts b/typescript/0402-remove-k-digits.ts new file mode 100644 index 000000000..2394cb3cb --- /dev/null +++ b/typescript/0402-remove-k-digits.ts @@ -0,0 +1,21 @@ +function removeKdigits(num: string, k: number): string { + let stack: string[] = []; + for (let ch of num) { + while (k > 0 && stack.length > 0 && stack[stack.length - 1] > ch) { + k--; + stack.pop(); + } + stack.push(ch); + } + + let x = 0; + while (true) { + if (stack[x] !== '0') { + break; + } + x++; + } + stack = stack.slice(x, stack.length - k); + let res = stack.join(''); + return res ? res : '0'; +} diff --git a/typescript/0417-pacific-atlantic-water-flow.ts b/typescript/0417-pacific-atlantic-water-flow.ts new file mode 100644 index 000000000..6dcbfb892 --- /dev/null +++ b/typescript/0417-pacific-atlantic-water-flow.ts @@ -0,0 +1,73 @@ +/** + * Breath First Search implementation + * @param matrix + * @param queue + * @param visited + */ +const bfs = (matrix: number[][], queue: number[][], visited: boolean[][]): void => { + let m = matrix.length, n = matrix[0].length; + let directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]; + + while (queue.length > 0) { + const [qCordx, qCordy] = queue.shift()!; + for (let dir of directions) { + const x = qCordx + dir[0], y = qCordy + dir[1]; + if (!( + x < 0 || + y < 0 || + x >= m || + y >= n || + visited[x][y] || + matrix[x][y] < matrix[qCordx][qCordy]) + ) { + visited[x][y] = true; + queue.push([x, y]); + } + + } + } +} + +/** + * Creates a Matrix NXM with false values + */ +const createMatrix = (n: number, m: number): boolean[][] => + Array.from({ length: n }, () => Array.from({ length: m }, () => false)); + + +function pacificAtlantic(heights: number[][]): number[][] { + const ROWS = heights.length, COLS = heights[0].length; + let pacific: boolean[][] = createMatrix(ROWS, COLS); + let atlantic: boolean[][] = createMatrix(ROWS, COLS); + let pacQueue: number[][] = []; + let atlQueue: number[][] = []; + let results: number[][] = []; + + // Set as 'true' Pacific edges + for (let i = 0; i < ROWS; i++) { + pacific[i][0] = true; + atlantic[i][COLS - 1] = true; + atlQueue.push([i, COLS - 1]); + pacQueue.push([i, 0]); + } + // Set as 'true' atlantic edges + for (let j = 0; j < COLS; j++) { + pacific[0][j] = true; + atlantic[ROWS - 1][j] = true; + atlQueue.push([ROWS - 1, j]); + pacQueue.push([0, j]); + } + + // BFS + bfs(heights, pacQueue, pacific); + bfs(heights, atlQueue, atlantic); + + // Verify intersections + for (let i = 0; i < ROWS; i++) { + for (let j = 0; j < COLS; j++) { + if (pacific[i][j] && atlantic[i][j]) results.push([i, j]); + } + } + + return results; +}; diff --git a/typescript/0424-longest-repeating-character-replacement.ts b/typescript/0424-longest-repeating-character-replacement.ts new file mode 100644 index 000000000..5532a9aac --- /dev/null +++ b/typescript/0424-longest-repeating-character-replacement.ts @@ -0,0 +1,27 @@ +function characterReplacement(s: string, k: number): number { + const charCount = {}; + + let max = 0; + + let domChar = 0; + + let l = 0; + + for (let r = 0; r < s.length; r++) { + if (s[r] in charCount) charCount[s[r]]++; + else charCount[s[r]] = 1; + + domChar = Math.max(domChar, charCount[s[r]]); + + //if the window size minus the count of the dominant character(number of replacements needed) is bigger than k, invalid + while (r - l + 1 - domChar > k) { + charCount[s[l]]--; + if (charCount[s[l]] === 0) delete charCount[s[l]]; + l++; + } + + max = Math.max(max, r - l + 1); + } + + return max; +} diff --git a/typescript/0435-non-overlapping-intervals.ts b/typescript/0435-non-overlapping-intervals.ts new file mode 100644 index 000000000..f20df9d2b --- /dev/null +++ b/typescript/0435-non-overlapping-intervals.ts @@ -0,0 +1,24 @@ +function eraseOverlapIntervals(intervals: number[][]): number { + intervals.sort(([aStart, aEnd], [bStart, bEnd]) => aEnd !== bEnd ? aEnd - bEnd : aStart - bStart); + + return getGaps(intervals); +}; + +function getGaps(intervals: number[][]): number { + let gaps = 0; + const prev = intervals.shift(); + + for (const curr of intervals) { + const [prevStart, prevEnd] = prev; + const [currStart, currEnd] = curr; + + const hasGap = prevEnd <= currStart; + if (!hasGap) { + continue; + } + + prev[1] = curr[1]; + gaps++; + } + return intervals.length - gaps; +} \ No newline at end of file diff --git a/typescript/0438-find-all-anagrams-in-a-string.ts b/typescript/0438-find-all-anagrams-in-a-string.ts new file mode 100644 index 000000000..7b138b84b --- /dev/null +++ b/typescript/0438-find-all-anagrams-in-a-string.ts @@ -0,0 +1,33 @@ +//Time Complexity -> O(n) +//Space Complexity -> O(n) +function findAnagrams(s: string, p: string): number[] { + if (p.length > s.length) return []; + + const anagramIndexes: number[] = []; + const pCount = new Map(); + const sCount = new Map(); + for (const char of p) pCount.set(char, (pCount.get(char) || 0) + 1); + for (let i = 0; i < p.length; i++) sCount.set(s[i], (sCount.get(s[i]) || 0) + 1); + + let l = 0; + let r = p.length - 1; + while (r < s.length) { + let isAnagram = true; + for (const [char, count] of pCount) { + if (!sCount.has(char) || sCount.get(char) !== count) { + isAnagram = false; + break; + } + } + if (isAnagram) anagramIndexes.push(l); + + sCount.set(s[l], (sCount.get(s[l]) || 1) - 1); + if (sCount.get(s[l]) === 0) sCount.delete(s[l]); + l++; + + r++; + sCount.set(s[r], (sCount.get(s[r]) || 0) + 1); + } + + return anagramIndexes; +} diff --git a/typescript/0448-find-all-numbers-disappeared-in-an-array.ts b/typescript/0448-find-all-numbers-disappeared-in-an-array.ts new file mode 100644 index 000000000..fe9ff5420 --- /dev/null +++ b/typescript/0448-find-all-numbers-disappeared-in-an-array.ts @@ -0,0 +1,16 @@ +function findDisappearedNumbers(nums: number[]): number[] { + for (const n of nums) { + let i = Math.abs(n) - 1; + nums[i] = -1 * Math.abs(nums[i]); + } + + let res: number[] = []; + + for (let [i, n] of nums.entries()) { + if (n > 0) { + res.push(i + 1); + } + } + + return res; +} diff --git a/typescript/0450-delete-node-in-a-bst.ts b/typescript/0450-delete-node-in-a-bst.ts new file mode 100644 index 000000000..e11a4f9fd --- /dev/null +++ b/typescript/0450-delete-node-in-a-bst.ts @@ -0,0 +1,44 @@ +/** + * Definition for a binary tree node. + * class TreeNode { + * val: number + * left: TreeNode | null + * right: TreeNode | null + * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + * } + */ + +function deleteNode(root: TreeNode | null, key: number): TreeNode | null { + if (!root) { + return root; + } + + if (key > root.val) { + root.right = deleteNode(root.right, key); + } else if (key < root.val) { + root.left = deleteNode(root.left, key); + } else { + if (!root.right) { + return root.left; + } else if (!root.left) { + return root.right; + } else { + let minNode = minValueNode(root.right); + root.val = minNode.val; + root.right = deleteNode(root.right, minNode.val); + } + } + return root; +} + +function minValueNode(root: TreeNode): TreeNode { + let curr: TreeNode = root; + while (curr && curr.left) { + curr = curr.left; + } + return curr; +} diff --git a/typescript/0456-132-pattern.ts b/typescript/0456-132-pattern.ts new file mode 100644 index 000000000..9f0b1fbf9 --- /dev/null +++ b/typescript/0456-132-pattern.ts @@ -0,0 +1,18 @@ +function find132pattern(nums: number[]): boolean { + let stack: { num: number; minLeft: number }[] = []; // [num, minLeft] + let curMin = nums[0]; + + for (let n of nums.slice(1)) { + while (stack.length > 0 && n >= stack[stack.length - 1].num) { + stack.pop(); + } + if (stack.length > 0 && n > stack[stack.length - 1].minLeft) { + return true; + } + + stack.push({ num: n, minLeft: curMin }); + curMin = Math.min(curMin, n); + } + + return false; +} diff --git a/typescript/0472-concatenated-words.ts b/typescript/0472-concatenated-words.ts new file mode 100644 index 000000000..aeacbc353 --- /dev/null +++ b/typescript/0472-concatenated-words.ts @@ -0,0 +1,28 @@ +function findAllConcatenatedWordsInADict(words: string[]): string[] { + let wordSet = new Set(words); + let res: string[] = []; + + for (let w of words) { + if (dfs(w)) { + res.push(w); + } + } + + return res; + + function dfs(word: string): boolean { + for (let i = 1; i < word.length; i++) { + let prefix = word.slice(0, i); + let suffix = word.slice(i); + + if ( + (wordSet.has(prefix) && wordSet.has(suffix)) || + (wordSet.has(prefix) && dfs(suffix)) + ) { + return true; + } + } + + return false; + } +} diff --git a/typescript/0474-ones-and-zeroes.ts b/typescript/0474-ones-and-zeroes.ts new file mode 100644 index 000000000..387c8765f --- /dev/null +++ b/typescript/0474-ones-and-zeroes.ts @@ -0,0 +1,32 @@ +function findMaxForm(strs: string[], m: number, n: number): number { + const data = strs.reduce((accum, str) => { + let zeroes = 0; + for (let c of str) { + if (c === '0') zeroes++; + } + accum.push([zeroes, str.length - zeroes]); + return accum; + }, [] as [number, number][]); + + const dp = Array + .from({ length: m + 1 }, () => new Array(n + 1)); + + for (let i = 0; i < data.length; i++) { + const [zeroes, ones] = data[i]; + + for (let j = m; j >= 0; j--) { + for (let k = n; k >= 0; k--) { + if (dp[j][k] === undefined) dp[j][k] = 0; + + if (j >= zeroes && k >= ones) { + dp[j][k] = Math.max( + 1 + (dp[j - zeroes][k - ones] ?? 0), + dp[j][k], + ); + } + } + } + } + + return dp[m][n]; +}; diff --git a/typescript/0494-target-sum.ts b/typescript/0494-target-sum.ts new file mode 100644 index 000000000..7ce6bab2f --- /dev/null +++ b/typescript/0494-target-sum.ts @@ -0,0 +1,62 @@ +function findTargetSumWays(nums: number[], target: number): number { + // key: index, sum till index element, value: number of ways to get to that sum + const cache = new Map(); + + const backTrack = (i, sum) => { + /* if we're at the last element + 1 we compare the sum to our target + if it's true, we've found a way to our sum! + */ + if (i === nums.length) { + if (sum === target) { + return 1; + } + return 0; + } + + // if already index, sum pair exist in our HashMap, we return the memoized result + if (cache.has(`${i},${sum}`)) { + return cache.get(`${i},${sum}`); + } + + // DP: we memoize number of ways of each pair index, sum + cache.set( + `${i},${sum}`, + backTrack(i + 1, sum + nums[i]) + backTrack(i + 1, sum - nums[i]) + ); + + return cache.get(`${i},${sum}`); + }; + + return backTrack(0, 0); +} + + +/** + * DP - Bottom Up + * Time O(N * M) | Space O(M) + * https://leetcode.com/problems/target-sum/ + * @param {number[]} nums + * @param {number} target + * @return {number} + */ +function findTargetSumWays(nums: number[], target: number): number { + const total = nums.reduce((a, b) => a + b); + const m = total * 2 + 1; + let dp = new Array(m).fill(0); + dp[total] = 1; // base case + + for (let i = 0; i < nums.length; i++) { + const current = new Array(m); + const num = nums[i]; + + for (let j = 0; j < current.length; j++) { + const left = dp[j - num] ?? 0; + const right = dp[j + num] ?? 0; + + current[j] = left + right; + } + dp = current; + } + + return dp[total + target] ?? 0; +}; diff --git a/typescript/0496-next-greater-element-i.ts b/typescript/0496-next-greater-element-i.ts new file mode 100644 index 000000000..b513be4f0 --- /dev/null +++ b/typescript/0496-next-greater-element-i.ts @@ -0,0 +1,24 @@ +function nextGreaterElement(nums1: number[], nums2: number[]): number[] { + const map = {}; + + for (let i = 0; i < nums1.length; i++) { + map[nums1[i]] = i; + } + + let res = new Array(nums1.length).fill(-1); + const stack: number[] = []; + + for (let i = 0; i < nums2.length; i++) { + let cur = nums2[i]; + while (stack.length && cur > stack.at(-1)) { + let val = stack.pop()!; + let idx = map[val]; + res[idx] = cur; + } + if (map.hasOwnProperty(cur)) { + stack.push(cur); + } + } + + return res; +} diff --git a/typescript/0502-ipo.ts b/typescript/0502-ipo.ts new file mode 100644 index 000000000..35c2f3ed2 --- /dev/null +++ b/typescript/0502-ipo.ts @@ -0,0 +1,21 @@ +function findMaximizedCapital(k: number, w: number, profits: number[], capital: number[]): number { + const minCapital = new PriorityQueue({ compare: (a, b) => a[1] - b[1] }); + const maxProfit = new MaxPriorityQueue(); + for (let i = 0; i < profits.length; i++) { + minCapital.enqueue([profits[i], capital[i]]); + } + + let profit = w; + for (let i = 0; i < k; i++) { + while (!minCapital.isEmpty() && minCapital.front()[1] <= profit) { + const element = minCapital.dequeue(); + maxProfit.enqueue(element[0]); + } + + if (maxProfit.isEmpty()) break; + const { element } = maxProfit.dequeue(); + profit += element; + } + + return profit; +}; diff --git a/typescript/0518-coin-change-ii.ts b/typescript/0518-coin-change-ii.ts new file mode 100644 index 000000000..ff8314a82 --- /dev/null +++ b/typescript/0518-coin-change-ii.ts @@ -0,0 +1,12 @@ +function change(amount: number, coins: number[]): number { + let table = Array(amount + 1).fill(0); + table[0] = 1; + for (let coin of coins) { + for (let i = 0; i < table.length; i++) { + if (coin <= i) { + table[i] += table[i - coin]; + } + } + } + return table[amount]; +} diff --git a/typescript/0535-encode-and-decode-tinyurl.ts b/typescript/0535-encode-and-decode-tinyurl.ts new file mode 100644 index 000000000..537c44add --- /dev/null +++ b/typescript/0535-encode-and-decode-tinyurl.ts @@ -0,0 +1,29 @@ +const encodeMap = {}; +const decodeMap = {}; +let size = 0; + +/** + * Encodes a URL to a shortened URL. + */ +function encode(longUrl: string): string { + if (!encodeMap.hasOwnProperty(longUrl)) { + let shortUrl = size + 1; + size += 1; + encodeMap[longUrl] = shortUrl; + decodeMap[shortUrl] = longUrl; + } + + return encodeMap[longUrl]; +} + +/** + * Decodes a shortened URL to its original URL. + */ +function decode(shortUrl: string): string { + return decodeMap[shortUrl]; +} + +/** + * Your functions will be called as such: + * decode(encode(strs)); + */ diff --git a/typescript/0540-single-element-in-a-sorted-array.ts b/typescript/0540-single-element-in-a-sorted-array.ts new file mode 100644 index 000000000..f67876cbc --- /dev/null +++ b/typescript/0540-single-element-in-a-sorted-array.ts @@ -0,0 +1,17 @@ +// Time Complexity: O(log n) +// Space Complexity: O(1) + +function singleNonDuplicate(nums: number[]): number { + let left = 0, + right = nums.length - 2; + + while (left <= right) { + const mid1 = (left + right) >> 1; + const mid2 = mid1 ^ 1; + + if (nums[mid1] === nums[mid2]) left = mid1 + 1; + else right = mid1 - 1; + } + + return nums[left]; +} diff --git a/typescript/0543-diameter-of-binary-tree.ts b/typescript/0543-diameter-of-binary-tree.ts new file mode 100644 index 000000000..b40c185e1 --- /dev/null +++ b/typescript/0543-diameter-of-binary-tree.ts @@ -0,0 +1,30 @@ +/** + * Definition for a binary tree node. + * class TreeNode { + * val: number + * left: TreeNode | null + * right: TreeNode | null + * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + * } + */ + +function diameterOfBinaryTree(root: TreeNode | null): number { + let res = [0]; + + function dfs(root: TreeNode | null): number { + if (!root) return -1; + let left = dfs(root.left); + let right = dfs(root.right); + + res[0] = Math.max(res[0], 2 + left + right); + + return 1 + Math.max(left, right); + } + + dfs(root); + return res[0]; +} diff --git a/typescript/0554-brick-wall.ts b/typescript/0554-brick-wall.ts new file mode 100644 index 000000000..e92cc97c0 --- /dev/null +++ b/typescript/0554-brick-wall.ts @@ -0,0 +1,17 @@ +function leastBricks(wall: number[][]): number { + let countGap = { 0: 0 }; + + for (const r of wall) { + let total = 0; + for (const b of r.slice(0, -1)) { + total += b; + if (countGap[total]) { + countGap[total] += 1; + } else { + countGap[total] = 1; + } + } + } + + return wall.length - Math.max(...Object.values(countGap)); +} diff --git a/typescript/0560-subarray-sum-equals-k.ts b/typescript/0560-subarray-sum-equals-k.ts new file mode 100644 index 000000000..84dee0ff9 --- /dev/null +++ b/typescript/0560-subarray-sum-equals-k.ts @@ -0,0 +1,14 @@ +function subarraySum(nums: number[], k: number): number { + let count = 0, + sum = 0, + map = new Map(); + map.set(0, 1); + for (const num of nums) { + sum += num; + if (map.has(sum - k)) { + count += map.get(sum - k); + } + map.set(sum, (map.get(sum) || 0) + 1); + } + return count; +} diff --git a/typescript/0567-permutation-in-string.ts b/typescript/0567-permutation-in-string.ts new file mode 100644 index 000000000..e715e6b8a --- /dev/null +++ b/typescript/0567-permutation-in-string.ts @@ -0,0 +1,42 @@ +function checkInclusion(s1: string, s2: string): boolean { + if (s1.length > s2.length) return false; + + const s1Count = Array(26).fill(0); + const s2Count = Array(26).fill(0); + + for (let i = 0; i < s1.length; i++) { + s1Count[s1[i].charCodeAt(0) - 'a'.charCodeAt(0)] += 1; + s2Count[s2[i].charCodeAt(0) - 'a'.charCodeAt(0)] += 1; + } + + let matches = 0; + for (let i = 0; i < 26; i++) { + if (s1Count[i] == s2Count[i]) { + matches += 1; + } + } + + let l = 0; + for (let r = s1.length; r < s2.length; r++) { + if (matches == 26) return true; + + let index = s2[r].charCodeAt(0) - 'a'.charCodeAt(0); + s2Count[index] += 1; + if (s1Count[index] == s2Count[index]) { + matches += 1; + } else if (s1Count[index] + 1 == s2Count[index]) { + matches -= 1; + } + + let index2 = s2[l].charCodeAt(0) - 'a'.charCodeAt(0); + s2Count[index2] -= 1; + if (s1Count[index2] == s2Count[index2]) { + matches += 1; + } else if (s1Count[index2] - 1 == s2Count[index2]) { + matches -= 1; + } + l += 1; + } + + return matches == 26; +} diff --git a/typescript/0572-subtree-of-another-tree.ts b/typescript/0572-subtree-of-another-tree.ts new file mode 100644 index 000000000..077956a11 --- /dev/null +++ b/typescript/0572-subtree-of-another-tree.ts @@ -0,0 +1,42 @@ +/** + * Definition for a binary tree node. + * class TreeNode { + * val: number + * left: TreeNode | null + * right: TreeNode | null + * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + * } + */ + +function isSubtree(root: TreeNode | null, subRoot: TreeNode | null): boolean { + if (subRoot === null) return true; + if (root === null) return false; + + if (isSameTree(root, subRoot)) { + return true; + } + + let left = isSubtree(root.left, subRoot); + let right = isSubtree(root.right, subRoot); + + return left || right; +} + +function isSameTree(p: TreeNode | null, q: TreeNode | null): boolean { + if (p === null && q === null) return true; + + if ((p === null && q !== null) || (p !== null && q === null)) return false; + + let leftSame = isSameTree(p.left, q.left); + let rightSame = isSameTree(p.right, q.right); + + if (p.val === q.val && leftSame && rightSame) { + return true; + } + + return false; +} diff --git a/typescript/0605-can-place-flowers.ts b/typescript/0605-can-place-flowers.ts new file mode 100644 index 000000000..505fe42a3 --- /dev/null +++ b/typescript/0605-can-place-flowers.ts @@ -0,0 +1,16 @@ +function canPlaceFlowers(flowerbed: number[], n: number): boolean { + flowerbed = [0, ...flowerbed, 0]; + + for (let i = 1; i < flowerbed.length - 1; i++) { + if ( + flowerbed[i - 1] == 0 && + flowerbed[i + 1] === 0 && + flowerbed[i] === 0 + ) { + flowerbed[i] = 1; + n -= 1; + } + } + + return n <= 0; +} diff --git a/typescript/0621-task-scheduler.ts b/typescript/0621-task-scheduler.ts new file mode 100644 index 000000000..816aa681f --- /dev/null +++ b/typescript/0621-task-scheduler.ts @@ -0,0 +1,32 @@ +function leastInterval(tasks: string[], n: number): number { + const charMap = new Map(); + let maxCharCount = 0; + let maxChar = tasks[0]; + + for (let char of tasks) { + charMap.set(char, (charMap.get(char) || 0) + 1); + if (charMap.get(char) > maxCharCount) { + maxCharCount = charMap.get(char); + maxChar = char; + } + } + + let idleCount = (maxCharCount - 1) * n; + + charMap.forEach((count, char) => { + // 'return' inside forEach() serve as 'continue' + if (char === maxChar) { + return; + } + if (count === maxCharCount) { + idleCount -= count - 1; + } else { + idleCount -= count; + } + }); + + if (idleCount <= 0) { + return tasks.length; + } + return tasks.length + idleCount; +} diff --git a/typescript/0647-palindromic-substrings.ts b/typescript/0647-palindromic-substrings.ts new file mode 100644 index 000000000..994ed1d6f --- /dev/null +++ b/typescript/0647-palindromic-substrings.ts @@ -0,0 +1,25 @@ +function countSubstrings(s: string): number { + let res = 0; + + for (let i = 0; i < s.length; i++) { + let l = i; + let r = i; + + while (l >= 0 && r < s.length && s[l] === s[r]) { + res += 1; + l -= 1; + r += 1; + } + + l = i; + r = i + 1; + + while (l >= 0 && r < s.length && s[l] === s[r]) { + res += 1; + l -= 1; + r += 1; + } + } + + return res; +} diff --git a/typescript/0658-find-k-closest-elements.ts b/typescript/0658-find-k-closest-elements.ts new file mode 100644 index 000000000..ade32d28b --- /dev/null +++ b/typescript/0658-find-k-closest-elements.ts @@ -0,0 +1,15 @@ +function findClosestElements(arr: number[], k: number, x: number): number[] { + let l = 0; + let r = arr.length - k; + + while (l < r) { + let m = Math.floor((l + r) / 2); + if (x - arr[m] > arr[m + k] - x) { + l = m + 1; + } else { + r = m; + } + } + + return arr.slice(l, l + k); +} diff --git a/typescript/0665-non-decreasing-array.ts b/typescript/0665-non-decreasing-array.ts new file mode 100644 index 000000000..8570bcd62 --- /dev/null +++ b/typescript/0665-non-decreasing-array.ts @@ -0,0 +1,18 @@ +function checkPossibility(nums: number[]): boolean { + let changed = false; + + for (let i = 0; i < nums.length - 1; i++) { + if (nums[i] <= nums[i + 1]) continue; + if (changed) return false; + + if (i === 0 || nums[i + 1] >= nums[i - 1]) { + nums[i] = nums[i + 1]; + } else { + nums[i + 1] = nums[i]; + } + + changed = true; + } + + return true; +} diff --git a/typescript/0669-trim-a-binary-search-tree.ts b/typescript/0669-trim-a-binary-search-tree.ts new file mode 100644 index 000000000..30ef4f224 --- /dev/null +++ b/typescript/0669-trim-a-binary-search-tree.ts @@ -0,0 +1,33 @@ +/** + * Definition for a binary tree node. + * class TreeNode { + * val: number + * left: TreeNode | null + * right: TreeNode | null + * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + * } + */ + +function trimBST(root: TreeNode | null, low: number, high: number): TreeNode | null { + + if (!root) { + return null; + } + + if (root.val < low) { + return trimBST(root.right, low, high); + } + + if (root.val > high) { + return trimBST(root.left, low, high); + } + + root.left = trimBST(root.left, low, high); + root.right = trimBST(root.right, low, high); + + return root; +}; \ No newline at end of file diff --git a/typescript/0678-valid-parenthesis-string.ts b/typescript/0678-valid-parenthesis-string.ts new file mode 100644 index 000000000..3263fb40a --- /dev/null +++ b/typescript/0678-valid-parenthesis-string.ts @@ -0,0 +1,27 @@ +function checkValidString(s: string): boolean { + let leftMin = 0; + let leftMax = 0; + + for (let c of s) { + if (c === '(') { + leftMin++; + leftMax++; + } else if (c === ')') { + leftMin--; + leftMax--; + } else { + leftMin--; + leftMax++; + } + + if (leftMax < 0) { + return false; + } + + if (leftMin < 0) { + leftMin = 0; + } + } + + return leftMin === 0; +} diff --git a/typescript/0680-valid-palindrome-ii.ts b/typescript/0680-valid-palindrome-ii.ts new file mode 100644 index 000000000..0f44c0ab2 --- /dev/null +++ b/typescript/0680-valid-palindrome-ii.ts @@ -0,0 +1,28 @@ +function validPalindrome(s: string): boolean { + let l = 0; + let r = s.length - 1; + + while (l < r) { + if (s[l] !== s[r]) { + let skipL = s.slice(l + 1, r + 1); + let skipR = s.slice(l, r); + + return palinodrome(skipL) || palinodrome(skipR); + } + l += 1; + r -= 1; + } + return true; +} + +function palinodrome(s: string): boolean { + let l = 0; + let r = s.length - 1; + + while (l < r) { + if (s[l] != s[r]) return false; + l += 1; + r -= 1; + } + return true; +} diff --git a/typescript/0682-baseball-game.ts b/typescript/0682-baseball-game.ts new file mode 100644 index 000000000..73cda6a37 --- /dev/null +++ b/typescript/0682-baseball-game.ts @@ -0,0 +1,23 @@ +function calPoints(operations: string[]): number { + const stack: number[] = []; + + for (const op of operations) { + if (op === '+') { + stack.push(stack[stack.length - 1] + stack[stack.length - 2]); + } else if (op === 'D') { + stack.push(stack[stack.length - 1] * 2); + } else if (op === 'C') { + stack.pop(); + } else { + stack.push(Number(op)); + } + } + + let res = 0; + + for (const num of stack) { + res += num; + } + + return res; +} diff --git a/typescript/0684-redundant-connection.ts b/typescript/0684-redundant-connection.ts new file mode 100644 index 000000000..3d544dbef --- /dev/null +++ b/typescript/0684-redundant-connection.ts @@ -0,0 +1,37 @@ +function findRedundantConnection(edges: number[][]): number[] { + let n = edges.length, + par = new Array(n + 1).fill(0).map((_, index) => index); + let rank = new Array(n + 1).fill(1); + + function findParent(node: number): number { + let p = par[node]; + while (p != par[p]) { + par[p] = par[par[p]]; + p = par[p]; + } + return p; + } + + function union(n1: number, n2: number): boolean { + let p1 = findParent(n1), + p2 = findParent(n2); + if (p1 == p2) { + return false; + } + + if (rank[p1] > rank[p2]) { + par[p2] = p1; + rank[p1] += rank[p2]; + } else { + par[p1] = p2; + rank[p2] += rank[p1]; + } + return true; + } + + for (let [n1, n2] of edges) { + if (!union(n1, n2)) { + return [n1, n2]; + } + } +} diff --git a/typescript/0695-max-area-of-island.ts b/typescript/0695-max-area-of-island.ts new file mode 100644 index 000000000..3d50bcee4 --- /dev/null +++ b/typescript/0695-max-area-of-island.ts @@ -0,0 +1,30 @@ +function maxAreaOfIsland(grid: number[][]): number { + let directions = [ + [-1, 0], + [1, 0], + [0, -1], + [0, 1], + ]; + + let rows = grid.length; + let columns = grid[0].length; + const dfs = (grid, r, c) => { + let cells = 0; + if (r < 0 || r >= rows || c < 0 || c >= columns || !grid[r][c]) + return 0; + grid[r][c] = 0; + directions.forEach(([dR, dC]) => (cells += dfs(grid, r + dR, c + dC))); + + return cells + 1; + }; + + let max = 0; + + for (let i = 0; i < rows; i++) { + for (let j = 0; j < columns; j++) { + max = Math.max(max, dfs(grid, i, j)); + } + } + + return max; +} diff --git a/typescript/0701-insert-into-a-binary-search-tree.ts b/typescript/0701-insert-into-a-binary-search-tree.ts new file mode 100644 index 000000000..b403aa938 --- /dev/null +++ b/typescript/0701-insert-into-a-binary-search-tree.ts @@ -0,0 +1,28 @@ +/** + * Definition for a binary tree node. + * class TreeNode { + * val: number + * left: TreeNode | null + * right: TreeNode | null + * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + * } + */ + +function insertIntoBST(root: TreeNode | null, val: number): TreeNode | null { + if (!root) { + return new TreeNode(val); + } + + if (val > root.val) { + root.right = insertIntoBST(root.right, val); + } + if (val < root.val) { + root.left = insertIntoBST(root.left, val); + } + + return root; +} diff --git a/typescript/0703-kth-largest-element-in-a-stream.ts b/typescript/0703-kth-largest-element-in-a-stream.ts new file mode 100644 index 000000000..a196e11ca --- /dev/null +++ b/typescript/0703-kth-largest-element-in-a-stream.ts @@ -0,0 +1,61 @@ +class KthLargest { + backingArray: number[]; + size: number; + + constructor(k: number, nums: number[]) { + this.backingArray = []; + this.size = k; + for(const num of nums) this.add(num) + } + + add(val: number): number { + const newLength = this.backingArray.push(val) + this.shiftUp(newLength - 1); + + if (newLength > this.size) this.pop(); + + return this.backingArray[0]; + } + + private pop() { + this.swap(0, this.backingArray.length - 1) + this.backingArray.length -= 1; + this.shiftDown(0) + } + + private shiftDown(elementIndex: number) { + let leftChildIndex = elementIndex * 2 + 1; + let rightChildIndex = elementIndex * 2 + 2; + + while ((leftChildIndex < this.backingArray.length && this.backingArray[leftChildIndex] < this.backingArray[elementIndex]) + || (rightChildIndex < this.backingArray.length && this.backingArray[rightChildIndex] < this.backingArray[elementIndex])) { + let smallestIndex = leftChildIndex; + if (rightChildIndex < this.backingArray.length && this.backingArray[rightChildIndex] < this.backingArray[smallestIndex]) { + smallestIndex = rightChildIndex + } + + this.swap(elementIndex, smallestIndex); + + elementIndex = smallestIndex; + leftChildIndex = elementIndex * 2 + 1; + rightChildIndex = elementIndex * 2 + 2; + } + } + + private shiftUp(elementIndex: number) { + if (elementIndex === 0) return; + + let parentIndex = Math.floor((elementIndex - 1) / 2); + while (this.backingArray[parentIndex] > this.backingArray[elementIndex]) { + this.swap(elementIndex, parentIndex); + elementIndex = parentIndex; + parentIndex = Math.floor((elementIndex - 1) / 2); + } + } + + private swap(indexA: number, indexB: number) { + const temp = this.backingArray[indexA]; + this.backingArray[indexA] = this.backingArray[indexB]; + this.backingArray[indexB] = temp; + } +} diff --git a/typescript/0704-binary-search.ts b/typescript/0704-binary-search.ts new file mode 100644 index 000000000..8bed756b2 --- /dev/null +++ b/typescript/0704-binary-search.ts @@ -0,0 +1,17 @@ +function search(nums: number[], target: number): number { + let l = 0, + r = nums.length - 1; + + while (l <= r) { + var m = l + Math.floor((r - l) / 2); + if (nums[m] > target) { + r = m - 1; + } else if (nums[m] < target) { + l = m + 1; + } else { + return m; + } + } + + return -1; +} diff --git a/typescript/0705-design-hashset.ts b/typescript/0705-design-hashset.ts new file mode 100644 index 000000000..b1f3816cc --- /dev/null +++ b/typescript/0705-design-hashset.ts @@ -0,0 +1,65 @@ +class _ListNode { // ListNode has a confict + key: number; + next: _ListNode | undefined; + + constructor(key: number) { + this.key = key; + } +} + +class MyHashSet { + readonly ARRAY_LENGTH = Math.pow(10, 4); + set = new Array<_ListNode>(this.ARRAY_LENGTH); + + constructor() { + for (let i = 0; i < this.ARRAY_LENGTH; i++) + this.set[i] = new _ListNode(0); + } + + add(key: number): void { + let cur = this.set[key % this.set.length]; + + while (cur.next) { + if (cur.next.key === key) + return; + + cur = cur.next; + } + + cur.next = new _ListNode(key); + } + + remove(key: number): void { + let cur = this.set[key % this.set.length]; + + while (cur.next) { + if (cur.next.key === key) { + cur.next = cur.next.next; + return; + } + + cur = cur.next; + } + } + + contains(key: number): boolean { + let cur = this.set[key % this.set.length]; + + while (cur.next) { + if (cur.next.key === key) + return true; + + cur = cur.next; + } + + return false; + } +} + +/** + * Your MyHashSet object will be instantiated and called as such: + * var obj = new MyHashSet() + * obj.add(key) + * obj.remove(key) + * var param_3 = obj.contains(key) + */ diff --git a/typescript/0724-find-pivot-index.ts b/typescript/0724-find-pivot-index.ts new file mode 100644 index 000000000..aacacaa34 --- /dev/null +++ b/typescript/0724-find-pivot-index.ts @@ -0,0 +1,13 @@ +function pivotIndex(nums: number[]): number { + let total = nums.reduce((a, b) => a + b); + let leftSum = 0; + + for (let i = 0; i < nums.length; i++) { + const rightSum = total - nums[i] - leftSum; + + if (leftSum === rightSum) return i; + leftSum += nums[i]; + } + + return -1; +} diff --git a/typescript/0735-asteroid-collision.ts b/typescript/0735-asteroid-collision.ts new file mode 100644 index 000000000..b02f29413 --- /dev/null +++ b/typescript/0735-asteroid-collision.ts @@ -0,0 +1,21 @@ +function asteroidCollision(asteroids: number[]): number[] { + const stack: number[] = []; + + for (let a of asteroids) { + while (stack && a < 0 && stack[stack.length - 1] > 0) { + let diff = a + stack[stack.length - 1]; + if (diff < 0) { + stack.pop(); + } else if (diff > 0) { + a = 0; + } else { + stack.pop(); + a = 0; + } + } + if (a === 0) continue; + stack.push(a); + } + + return stack; +} diff --git a/typescript/0739-daily-temperatures.ts b/typescript/0739-daily-temperatures.ts new file mode 100644 index 000000000..726a14b8a --- /dev/null +++ b/typescript/0739-daily-temperatures.ts @@ -0,0 +1,18 @@ +function dailyTemperatures(temperatures: number[]): number[] { + let stack = []; + + let result = new Array(temperatures.length).fill(0); + + for (let i = 0; i < temperatures.length; i++) { + let currTemp = temperatures[i]; + + while (stack.length > 0 && currTemp > stack[stack.length - 1].temp) { + let { ind } = stack.pop(); + result[ind] = i - ind; + } + + stack.push({ temp: currTemp, ind: i }); + } + + return result; +} diff --git a/typescript/0743-network-delay-time.ts b/typescript/0743-network-delay-time.ts new file mode 100644 index 000000000..11817ea92 --- /dev/null +++ b/typescript/0743-network-delay-time.ts @@ -0,0 +1,35 @@ +function networkDelayTime(times: number[][], n: number, k: number): number { + const adjList = new Map(Array.from({ length: n + 1 }, (_, i) => [i, []])); + for (let [origin, destination, weight] of times) { + adjList.get(origin).push([destination, weight]); + } + + const table = new Array(n + 1); + for (let i = 1; i < table.length; i++) { + table[i] = -1; + } + table[k] = 0; + const heap = new MinPriorityQueue({ priority: (a) => a[1] }); + for (let [destination, weight] of (adjList.get(k) || [])) { + heap.enqueue([destination, weight]); + } + + while (!heap.isEmpty()) { + const { element: minEdge } = heap.dequeue(); + + if (table[minEdge[0]] !== -1 && minEdge[1] >= table[minEdge[0]]) continue; + table[minEdge[0]] = minEdge[1]; + + for (let edge of (adjList.get(minEdge[0]) || [])) { + heap.enqueue([edge[0], minEdge[1] + edge[1]]); + } + } + + let max = 0; + for (let i = 1; i < table.length; i++) { + if (table[i] === -1) return -1; + max = Math.max(max, table[i]); + } + + return max; +}; diff --git a/typescript/0746-min-cost-climbing-stairs.ts b/typescript/0746-min-cost-climbing-stairs.ts new file mode 100644 index 000000000..6051b7e91 --- /dev/null +++ b/typescript/0746-min-cost-climbing-stairs.ts @@ -0,0 +1,9 @@ +function minCostClimbingStairs(cost: number[]): number { + cost.push(0); + + for (let i = cost.length - 3; i > -1; i--) { + cost[i] += Math.min(cost[i + 1], cost[i + 2]); + } + + return Math.min(cost[0], cost[1]); +} diff --git a/typescript/0778-swim-in-rising-water.ts b/typescript/0778-swim-in-rising-water.ts new file mode 100644 index 000000000..9f8cf29e0 --- /dev/null +++ b/typescript/0778-swim-in-rising-water.ts @@ -0,0 +1,31 @@ +function swimInWater(grid: number[][]): number { + const m = grid.length; + const n = grid[0].length; + + const heap = new MinPriorityQueue({ priority: (a) => a[0] }); + const visited = Array.from({ length: m }, () => Array.from({ length: n }, () => false)); + heap.enqueue([grid[0][0], 0, 0]); + + while (!heap.isEmpty()) { + const { element: [weight, r, c ] } = heap.dequeue(); + + if (r === m - 1 && c === n - 1) return weight; + + const edges = [ + [r - 1, c], + [r, c + 1], + [r + 1, c], + [r, c - 1], + ]; + for (let [nr, nc] of edges) { + if (nr < 0 || nr >= m) continue; + if (nc < 0 || nc >= n) continue; + if (visited[nr][nc]) continue; + + visited[nr][nc] = true; + heap.enqueue([Math.max(grid[nr][nc], weight), nr, nc]); + } + } + + return 0; +}; diff --git a/typescript/0787-cheapest-flights-within-k-stops.ts b/typescript/0787-cheapest-flights-within-k-stops.ts new file mode 100644 index 000000000..f86e7bec8 --- /dev/null +++ b/typescript/0787-cheapest-flights-within-k-stops.ts @@ -0,0 +1,45 @@ +function findCheapestPrice( + n: number, + flights: number[][], + src: number, + dst: number, + k: number +): number { + const adjacencyList = new Map(); + + for (let [start, end, cost] of flights) { + if (adjacencyList.has(start)) { + adjacencyList.get(start).push([end, cost]); + } else { + adjacencyList.set(start, [[end, cost]]); + } + } + + const queue = [[0, src, k + 1]]; + const visited = new Map(); + + while (queue.length) { + queue.sort((a, b) => a[0] - b[0]); + + const [cost, city, stops] = queue.shift(); + visited.set(city, stops); + + if (city === dst) { + return cost; + } + + if (stops <= 0 || !adjacencyList.has(city)) { + continue; + } + + for (let [nextCity, nextCost] of adjacencyList.get(city)) { + if (visited.has(nextCity) && visited.get(nextCity) >= stops - 1) { + continue; + } + + queue.push([cost + nextCost, nextCity, stops - 1]); + } + } + + return -1; +} diff --git a/typescript/0789-minimum-distance-between-two-bst-nodes.ts b/typescript/0789-minimum-distance-between-two-bst-nodes.ts new file mode 100644 index 000000000..c51b8d09c --- /dev/null +++ b/typescript/0789-minimum-distance-between-two-bst-nodes.ts @@ -0,0 +1,33 @@ +class TreeNode { + val: number; + left: TreeNode | null; + right: TreeNode | null; + constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + this.val = val === undefined ? 0 : val; + this.left = left === undefined ? null : left; + this.right = right === undefined ? null : right; + } +} + +export {}; + +function minDiffInBST(root: TreeNode | null): number { + let prev: TreeNode | null = null; + let res = Number.MAX_VALUE; + + function dfs(node: TreeNode | null): void { + if (node) { + dfs(node.left); + + if (prev) { + res = Math.min(res, node.val - prev.val); + } + prev = node; + + dfs(node.right); + } + } + + dfs(root); + return res; +} diff --git a/typescript/0838-push-dominoes.ts b/typescript/0838-push-dominoes.ts new file mode 100644 index 000000000..f00020dad --- /dev/null +++ b/typescript/0838-push-dominoes.ts @@ -0,0 +1,22 @@ +function pushDominoes(dominoes: string): string { + let res = dominoes.split(''); + while (true) { + let temp = res.slice(); + let changed = false; + for (let i = 0; i < res.length; i++) { + if (res[i + 1] === 'L' && res[i - 1] === 'R' && res[i] === '.') { + continue; + } else if (res[i + 1] === 'L' && res[i] === '.') { + temp[i] = 'L'; + changed = true; + } else if (res[i - 1] === 'R' && res[i] === '.') { + temp[i] = 'R'; + changed = true; + } + } + res = temp; + if (!changed) break; + } + + return res.join(''); +} diff --git a/typescript/0846-hand-of-straights.ts b/typescript/0846-hand-of-straights.ts new file mode 100644 index 000000000..e71d5b22f --- /dev/null +++ b/typescript/0846-hand-of-straights.ts @@ -0,0 +1,34 @@ +function isNStraightHand(hand: number[], groupSize: number): boolean { + if (hand.length % groupSize) { + return false; + } + + let count = {}; + for (let n in hand) { + if (!count.hasOwnProperty(hand[n])) { + count[hand[n]] = 0; + } + + count[hand[n]]++; + } + + let sortUniqHand = [...new Set(hand)].sort((a, b) => b - a); + while (sortUniqHand.length > 0) { + let first = sortUniqHand[sortUniqHand.length - 1]; + for (let i = first; i < first + groupSize; i++) { + if (!count.hasOwnProperty(i)) { + return false; + } + + count[i]--; + if (count[i] === 0) { + if (i !== sortUniqHand[sortUniqHand.length - 1]) { + return false; + } + + sortUniqHand.pop(); + } + } + } + return true; +} diff --git a/typescript/0853-car-fleet.ts b/typescript/0853-car-fleet.ts new file mode 100644 index 000000000..292e0d11e --- /dev/null +++ b/typescript/0853-car-fleet.ts @@ -0,0 +1,25 @@ +function carFleet(target: number, position: number[], speed: number[]): number { + const combined = position + .map((item, index) => { + return [item, speed[index]]; + }) + .sort((a, b) => a[0] - b[0]); + + const stack: number[] = []; + + for (let i = combined.length - 1; i > -1; i--) { + const p = combined[i][0]; + const s = combined[i][1]; + + stack.push((target - p) / s); + + if ( + stack.length >= 2 && + stack[stack.length - 1] <= stack[stack.length - 2] + ) { + stack.pop(); + } + } + + return stack.length; +} diff --git a/typescript/0875-koko-eating-bananas.ts b/typescript/0875-koko-eating-bananas.ts new file mode 100644 index 000000000..f8242d4c9 --- /dev/null +++ b/typescript/0875-koko-eating-bananas.ts @@ -0,0 +1,21 @@ +function minEatingSpeed(piles: number[], h: number): number { + let l = 1; + let r = Math.max(...piles); + let res = r; + + while (l <= r) { + let k = Math.floor((l + r) / 2); + let hours = 0; + for (const p of piles) { + hours += Math.ceil(p / k); + } + if (hours <= h) { + res = Math.min(res, k); + r = k - 1; + } else { + l = k + 1; + } + } + + return res; +} diff --git a/typescript/0876-middle-of-the-linked-list.ts b/typescript/0876-middle-of-the-linked-list.ts new file mode 100644 index 000000000..f885d3463 --- /dev/null +++ b/typescript/0876-middle-of-the-linked-list.ts @@ -0,0 +1,10 @@ +function middleNode(head: ListNode | null): ListNode | null { + let slow = head; + let fast = head; + + while (fast && fast.next) { + slow = slow?.next!; + fast = fast.next.next; + } + return slow; +} diff --git a/typescript/0881-boats-to-save-people.ts b/typescript/0881-boats-to-save-people.ts new file mode 100644 index 000000000..bdda64791 --- /dev/null +++ b/typescript/0881-boats-to-save-people.ts @@ -0,0 +1,17 @@ +function numRescueBoats(people: number[], limit: number): number { + people.sort((a, b) => a - b); + + let res = 0; + let l = 0; + let r = people.length - 1; + + while (l <= r) { + let remain = limit - people[r]; + r -= 1; + res += 1; + if (l <= r && remain >= people[l]) { + l += 1; + } + } + return res; +} diff --git a/typescript/0895-maximum-frequency-stack.ts b/typescript/0895-maximum-frequency-stack.ts new file mode 100644 index 000000000..0afd13315 --- /dev/null +++ b/typescript/0895-maximum-frequency-stack.ts @@ -0,0 +1,32 @@ +class FreqStack { + cnt: Map; + maxCnt: number; + stacks: Map; + constructor() { + this.cnt = new Map(); + this.maxCnt = 0; + this.stacks = new Map(); + } + + push(val: number): void { + let valCnt: number = 1 + (this.cnt.get(val) || 0); + this.cnt.set(val, valCnt); + + if (valCnt > this.maxCnt) { + this.maxCnt = valCnt; + this.stacks.set(valCnt, []); + } + this.stacks.get(valCnt)?.push(val); + } + + pop(): number { + let res: number = this.stacks.get(this.maxCnt)?.pop()!; + this.cnt.set(res, this.cnt.get(res)! - 1); + + if (this.stacks.get(this.maxCnt)?.length == 0) { + this.maxCnt -= 1; + } + + return res; + } +} diff --git a/typescript/0901-online-stock-span.ts b/typescript/0901-online-stock-span.ts new file mode 100644 index 000000000..d7a654dd2 --- /dev/null +++ b/typescript/0901-online-stock-span.ts @@ -0,0 +1,28 @@ +class StockSpanner { + private stack: { price: number; spanDays: number }[]; + + constructor() { + this.stack = []; + } + + next(price: number): number { + let span = 1; + + while ( + this.stack.length > 0 && + this.stack[this.stack.length - 1].price <= price + ) { + span += this.stack[this.stack.length - 1].spanDays; + this.stack.pop(); + } + + this.stack.push({ price, spanDays: span }); + return span; + } +} + +/** + * Your StockSpanner object will be instantiated and called as such: + * var obj = new StockSpanner() + * var param_1 = obj.next(price) + */ diff --git a/typescript/0904-fruit-into-baskets.ts b/typescript/0904-fruit-into-baskets.ts new file mode 100644 index 000000000..98655b8fd --- /dev/null +++ b/typescript/0904-fruit-into-baskets.ts @@ -0,0 +1,23 @@ +function totalFruit(fruits: number[]): number { + let count = new Map(); + let [left, total, res] = [0, 0, 0]; + + for (let fruit of fruits) { + count.set(fruit, (count.get(fruit) || 0) + 1); + total++; + + while (count.size > 2) { + let f = fruits[left]; + count.set(f, count.get(f) - 1); + total--; + left++; + if (!count.get(f)) { + count.delete(f); + } + } + + res = Math.max(res, total); + } + + return res; +} diff --git a/typescript/0918-maximum-sum-circular-subarray.ts b/typescript/0918-maximum-sum-circular-subarray.ts new file mode 100644 index 000000000..ff597be90 --- /dev/null +++ b/typescript/0918-maximum-sum-circular-subarray.ts @@ -0,0 +1,15 @@ +function maxSubarraySumCircular(nums: number[]): number { + let [globalMax, globalMin] = [nums[0], nums[0]]; + let [currentMax, currentMin] = [0, 0]; + let total = 0; + + for (let num of nums) { + currentMax = Math.max(num, currentMax + num); + currentMin = Math.min(num, currentMin + num); + total += num; + globalMax = Math.max(globalMax, currentMax); + globalMin = Math.min(globalMin, currentMin); + } + + return globalMax > 0 ? Math.max(globalMax, total - globalMin) : globalMax; +} diff --git a/typescript/0926-flip-string-to-monotone-increasing.ts b/typescript/0926-flip-string-to-monotone-increasing.ts new file mode 100644 index 000000000..1ac0b796a --- /dev/null +++ b/typescript/0926-flip-string-to-monotone-increasing.ts @@ -0,0 +1,14 @@ +function minFlipsMonoIncr(s: string): number { + let res: number = 0; + let countOne: number = 0; + + for (let ch of s) { + if (ch == '1') { + countOne++; + } else { + res = Math.min(res + 1, countOne); + } + } + + return res; +} diff --git a/typescript/0929-unique-email-addresses.ts b/typescript/0929-unique-email-addresses.ts new file mode 100644 index 000000000..f1c3b6204 --- /dev/null +++ b/typescript/0929-unique-email-addresses.ts @@ -0,0 +1,12 @@ +function numUniqueEmails(emails: string[]): number { + const unique = new Set(); + + for (const email of emails) { + let [local, domain] = email.split('@'); + local = local.split('+')[0]; + local = local.split('.').join(''); + unique.add(local + '@' + domain); + } + + return unique.size; +} diff --git a/typescript/0977-squares-of-a-sorted-array.ts b/typescript/0977-squares-of-a-sorted-array.ts new file mode 100644 index 000000000..045e7b89f --- /dev/null +++ b/typescript/0977-squares-of-a-sorted-array.ts @@ -0,0 +1,20 @@ +function sortedSquares(nums: number[]): number[] { + let left: number = 0; + let right: number = nums.length - 1; + + const answer: number[] = []; + + while (left <= right) { + const leftSqr = nums[left] * nums[left]; + const rightSqr = nums[right] * nums[right]; + + if (leftSqr > rightSqr) { + answer.push(leftSqr); + left++; + } else { + answer.push(rightSqr); + right--; + } + } + return answer.reverse(); +} diff --git a/typescript/0981-time-based-key-value-store.ts b/typescript/0981-time-based-key-value-store.ts new file mode 100644 index 000000000..1104d4871 --- /dev/null +++ b/typescript/0981-time-based-key-value-store.ts @@ -0,0 +1,44 @@ +//use an hashmap for keys, then each key will have an array (increasing order because of problem rules) of timestamps with values (represented as javascript objects {key, value}) +class TimeMap { + public hash: {}; + + constructor() { + this.hash = {}; + } + + set(key: string, value: string, timestamp: number): void { + if (key in this.hash) { + this.hash[key].push({ timestamp, value }); + } else this.hash[key] = [{ timestamp, value }]; + } + + get(key: string, timestamp: number): string { + //if key is not in the hashmap there are no timestamps nor values, return "" + if (!(key in this.hash)) return ''; + let timestamps = this.hash[key]; + //if there are no timestamps or the first timestamp is already bigger than target timestamp(they are sorted so all next ones will big too), return "" + if (timestamps.length === 0 || timestamps[0].timestamp > timestamp) + return ''; + + //starts from the first timestamp as closest + let closest = timestamps[0]; + + let [l, r] = [0, timestamps.length - 1]; + + //binary search, but + while (l <= r) { + let mid = Math.floor((l + r) / 2); + + if (timestamps[mid].timestamp === timestamp) + return timestamps[mid].value; + //update closest if mid element's timestamp is still less than target timestamp + if (timestamps[mid].timestamp < timestamp) + closest = timestamps[mid]; + + if (timestamps[mid].timestamp < timestamp) l = mid + 1; + if (timestamps[mid].timestamp > timestamp) r = mid - 1; + } + + return closest.value; + } +} diff --git a/typescript/0983-minimum-cost-for-tickets.ts b/typescript/0983-minimum-cost-for-tickets.ts new file mode 100644 index 000000000..74a97b993 --- /dev/null +++ b/typescript/0983-minimum-cost-for-tickets.ts @@ -0,0 +1,23 @@ +function mincostTickets(days: number[], costs: number[]): number { + const dp = new Array(days.length + 1); + dp[dp.length - 1] = 0; + + for (let j = dp.length - 2; j >= 0; j--) { + const a = costs[0] + dp[j + 1]; + + let d = j; + do { + d++; + } while (days[d] < days[j] + 7 && d < dp.length); + const b = costs[1] + dp[d]; + + d = j; + do { + d++; + } while (days[d] < days[j] + 30 && d < dp.length); + const c = costs[2] + dp[d]; + dp[j] = Math.min(a, b, c); + } + + return dp[0]; +}; diff --git a/typescript/0989-add-to-array-form-of-integer.ts b/typescript/0989-add-to-array-form-of-integer.ts new file mode 100644 index 000000000..75c92e228 --- /dev/null +++ b/typescript/0989-add-to-array-form-of-integer.ts @@ -0,0 +1,18 @@ +function addToArrayForm(num: number[], k: number): number[] { + num.reverse(); + let i = 0; + + while (k > 0) { + const digit = k % 10; + i < num.length ? (num[i] += digit) : num.push(digit); + + const carry = Math.floor(num[i] / 10); + num[i] %= 10; + + k = Math.floor(k / 10); + k += carry; + i++; + } + + return num.reverse(); +} diff --git a/typescript/0994-rotting-oranges.ts b/typescript/0994-rotting-oranges.ts new file mode 100644 index 000000000..3d444cdca --- /dev/null +++ b/typescript/0994-rotting-oranges.ts @@ -0,0 +1,49 @@ +function orangesRotting(grid: number[][]): number { + let [ROWS, COLS, time, fresh, q] = [grid.length, grid[0].length, 0, 0, []]; + let dirs = [ + [0, 1], + [0, -1], + [1, 0], + [-1, 0], + ]; + + // count fresh oranges and add rotten oranges to queue + for (let i = 0; i < ROWS; i++) { + for (let j = 0; j < COLS; j++) { + if (grid[i][j] === 1) { + fresh++; + } + if (grid[i][j] === 2) { + q.push([i, j]); + } + } + } + + while (q.length > 0 && fresh > 0) { + let qLen = q.length; + + for (let rot = 0; rot < qLen; rot++) { + let [row, col] = q.shift(); + + for (let dir of dirs) { + let [r, c] = [row + dir[0], col + dir[1]]; + + if ( + r < 0 || + r >= ROWS || + c < 0 || + c >= COLS || + grid[r][c] !== 1 + ) { + continue; + } + + grid[r][c] = 2; + fresh--; + q.push([r, c]); + } + } + time++; + } + return fresh > 0 ? -1 : time; +} diff --git a/typescript/1029-two-city-scheduling.ts b/typescript/1029-two-city-scheduling.ts new file mode 100644 index 000000000..f9db093ca --- /dev/null +++ b/typescript/1029-two-city-scheduling.ts @@ -0,0 +1,10 @@ +function twoCitySchedCost(costs: number[][]): number { + costs.sort((a, b) => a[1] - a[0] - (b[1] - b[0])); + + let totalCost = 0; + let n = costs.length / 2; + for (let i = 0; i < n; i++) { + totalCost += costs[i][1] + costs[i + n][0]; + } + return totalCost; +} diff --git a/typescript/1046-Last-Stone-Weight.ts b/typescript/1046-Last-Stone-Weight.ts new file mode 100644 index 000000000..3a1b702df --- /dev/null +++ b/typescript/1046-Last-Stone-Weight.ts @@ -0,0 +1,78 @@ +class MaxHeap { + heap: number[]; + constructor(array: number[]) { + this.heap = this.buildHeap(array); + } + + buildHeap(array: number[]) { + let parentIdx = Math.floor((array.length - 2) / 2); + for (let i = parentIdx; i >= 0; i--) { + this.siftDown(i, array.length - 1, array); + } + return array; + } + + siftDown(idx: number, endIdx: number, heap: number[]) { + let childOneIdx = 2 * idx + 1; + + while (childOneIdx <= endIdx) { + let childTwoIdx = 2 * idx + 2 <= endIdx ? 2 * idx + 2 : -1; + let swapIdx; + if (childTwoIdx !== -1 && heap[childOneIdx] < heap[childTwoIdx]) { + swapIdx = childTwoIdx; + } else swapIdx = childOneIdx; + if (heap[swapIdx] > heap[idx]) { + this.swap(swapIdx, idx, heap); + idx = swapIdx; + childOneIdx = 2 * idx + 1; + } else return; + } + } + + siftUp(idx: number, heap: number[]) { + let parentIdx = Math.floor((idx - 1) / 2); + while (heap[parentIdx] < heap[idx] && idx > 0) { + this.swap(parentIdx, idx, heap); + idx = parentIdx; + parentIdx = Math.floor((idx - 1) / 2); + } + } + + peek() { + return this.heap[0]; + } + + remove() { + this.swap(this.heap.length - 1, 0, this.heap); + const removeValue = this.heap.pop(); + this.siftDown(0, this.heap.length - 1, this.heap); + return removeValue; + } + + size() { + return this.heap.length; + } + + insert(value: number) { + this.heap.push(value); + this.siftUp(this.heap.length - 1, this.heap); + } + swap(i: number, j: number, arr: number[]) { + let ele = arr[i]; + arr[i] = arr[j]; + arr[j] = ele; + } +} + +function lastStoneWeight(stones: number[]): number { + const heap = new MaxHeap(stones); + + while (heap.size() > 1) { + const stone1 = heap.remove(); + const stone2 = heap.remove(); + + heap.insert(stone1 - stone2); + } + + return heap.peek(); +} diff --git a/typescript/1046-last-stone-weight.ts b/typescript/1046-last-stone-weight.ts new file mode 100644 index 000000000..3a1b702df --- /dev/null +++ b/typescript/1046-last-stone-weight.ts @@ -0,0 +1,78 @@ +class MaxHeap { + heap: number[]; + constructor(array: number[]) { + this.heap = this.buildHeap(array); + } + + buildHeap(array: number[]) { + let parentIdx = Math.floor((array.length - 2) / 2); + for (let i = parentIdx; i >= 0; i--) { + this.siftDown(i, array.length - 1, array); + } + return array; + } + + siftDown(idx: number, endIdx: number, heap: number[]) { + let childOneIdx = 2 * idx + 1; + + while (childOneIdx <= endIdx) { + let childTwoIdx = 2 * idx + 2 <= endIdx ? 2 * idx + 2 : -1; + let swapIdx; + if (childTwoIdx !== -1 && heap[childOneIdx] < heap[childTwoIdx]) { + swapIdx = childTwoIdx; + } else swapIdx = childOneIdx; + if (heap[swapIdx] > heap[idx]) { + this.swap(swapIdx, idx, heap); + idx = swapIdx; + childOneIdx = 2 * idx + 1; + } else return; + } + } + + siftUp(idx: number, heap: number[]) { + let parentIdx = Math.floor((idx - 1) / 2); + while (heap[parentIdx] < heap[idx] && idx > 0) { + this.swap(parentIdx, idx, heap); + idx = parentIdx; + parentIdx = Math.floor((idx - 1) / 2); + } + } + + peek() { + return this.heap[0]; + } + + remove() { + this.swap(this.heap.length - 1, 0, this.heap); + const removeValue = this.heap.pop(); + this.siftDown(0, this.heap.length - 1, this.heap); + return removeValue; + } + + size() { + return this.heap.length; + } + + insert(value: number) { + this.heap.push(value); + this.siftUp(this.heap.length - 1, this.heap); + } + swap(i: number, j: number, arr: number[]) { + let ele = arr[i]; + arr[i] = arr[j]; + arr[j] = ele; + } +} + +function lastStoneWeight(stones: number[]): number { + const heap = new MaxHeap(stones); + + while (heap.size() > 1) { + const stone1 = heap.remove(); + const stone2 = heap.remove(); + + heap.insert(stone1 - stone2); + } + + return heap.peek(); +} diff --git a/typescript/1049-last-stone-weight-ii.ts b/typescript/1049-last-stone-weight-ii.ts new file mode 100644 index 000000000..006b45efa --- /dev/null +++ b/typescript/1049-last-stone-weight-ii.ts @@ -0,0 +1,26 @@ +function lastStoneWeightII(stones: number[]): number { + const sum = stones.reduce((a, b) => a + b); + const target = Math.ceil(sum / 2); + + let dp = Array.from({ length: sum + 1 }, (_, i) => { + return Math.abs(i - (sum - i)); + }); + + for (let i = 0; i < stones.length; i++) { + const current: number[] = Array.from({ length: sum + 1 }); + for (let j = current.length - 1; j >= 0; j--) { + if (j >= target) { + current[j] = Math.abs(j - (sum - j)); + } else { + current[j] = Math.min( + dp[j], + dp[j + stones[i]], + ); + } + } + + dp = current; + } + + return dp[0]; +}; diff --git a/typescript/1071-greatest-common-divisor-of-strings.ts b/typescript/1071-greatest-common-divisor-of-strings.ts new file mode 100644 index 000000000..d247052a4 --- /dev/null +++ b/typescript/1071-greatest-common-divisor-of-strings.ts @@ -0,0 +1,24 @@ +function gcdOfStrings(str1: string, str2: string): string { + let [len1, len2] = [str1.length, str2.length]; + + function isDivisor(l: number): boolean { + if (len1 % l || len2 % l) { + return false; + } + + let [f1, f2] = [Math.floor(len1 / l), Math.floor(len2 / l)]; + + return ( + str1.slice(0, l).repeat(f1) == str1 && + str1.slice(0, l).repeat(f2) == str2 + ); + } + + for (let l = Math.min(len1, len2); l > 0; l--) { + if (isDivisor(l)) { + return str1.slice(0, l); + } + } + + return ''; +} diff --git a/typescript/1137-n-th-tribonacci-number.ts b/typescript/1137-n-th-tribonacci-number.ts new file mode 100644 index 000000000..2f7e81121 --- /dev/null +++ b/typescript/1137-n-th-tribonacci-number.ts @@ -0,0 +1,15 @@ +function tribonacci(n: number): number { + let t = [0, 1, 1]; + + if (n < 3) return t[n]; + + for (let i = 3; i < n + 1; i++) { + [t[0], t[1], t[2]] = [t[1], t[2], sum(t)]; + } + + return t[2]; +} + +function sum(arr: number[]): number { + return arr.reduce((a, b) => a + b); +} diff --git a/typescript/1143-Longest-Common-Subsequence.ts b/typescript/1143-Longest-Common-Subsequence.ts new file mode 100644 index 000000000..532bcbbe4 --- /dev/null +++ b/typescript/1143-Longest-Common-Subsequence.ts @@ -0,0 +1,20 @@ +function longestCommonSubsequence(text1: string, text2: string): number { + let temp: number[][] = []; + let max: number = 0; + for (let i = 0; i <= text1.length; i++) { + temp.push(new Array(text2.length + 1).fill(0)); + } + + for (let i = 1; i < temp.length; i++) { + for (let j = 1; j < temp[0].length; j++) { + if (text1[i - 1] === text2[j - 1]) { + temp[i][j] = temp[i - 1][j - 1] + 1; + } else { + temp[i][j] = Math.max(temp[i - 1][j], temp[i][j - 1]); + } + max = Math.max(max, temp[i][j]); + } + } + + return max; +} diff --git a/typescript/1143-longest-common-subsequence.ts b/typescript/1143-longest-common-subsequence.ts new file mode 100644 index 000000000..532bcbbe4 --- /dev/null +++ b/typescript/1143-longest-common-subsequence.ts @@ -0,0 +1,20 @@ +function longestCommonSubsequence(text1: string, text2: string): number { + let temp: number[][] = []; + let max: number = 0; + for (let i = 0; i <= text1.length; i++) { + temp.push(new Array(text2.length + 1).fill(0)); + } + + for (let i = 1; i < temp.length; i++) { + for (let j = 1; j < temp[0].length; j++) { + if (text1[i - 1] === text2[j - 1]) { + temp[i][j] = temp[i - 1][j - 1] + 1; + } else { + temp[i][j] = Math.max(temp[i - 1][j], temp[i][j - 1]); + } + max = Math.max(max, temp[i][j]); + } + } + + return max; +} diff --git a/typescript/1189-maximum-number-of-balloons.ts b/typescript/1189-maximum-number-of-balloons.ts new file mode 100644 index 000000000..f2f6e7fb1 --- /dev/null +++ b/typescript/1189-maximum-number-of-balloons.ts @@ -0,0 +1,19 @@ +function maxNumberOfBalloons(text: string): number { + let countText = {}; + let balloon = {}; + let result = text.length; + + for (let ch of text) { + countText[ch] = (countText[ch] || 0) + 1; + } + + for (let ch of 'balloon') { + balloon[ch] = (balloon[ch] || 0) + 1; + } + + for (let c in balloon) { + result = Math.min(result, Math.floor((countText[c] || 0) / balloon[c])); + } + + return result; +} diff --git a/typescript/1209-remove-all-adjacent-duplicates-in-string-II.ts b/typescript/1209-remove-all-adjacent-duplicates-in-string-II.ts new file mode 100644 index 000000000..88cb24bb1 --- /dev/null +++ b/typescript/1209-remove-all-adjacent-duplicates-in-string-II.ts @@ -0,0 +1,20 @@ +function removeDuplicates(s: string, k: number): string { + const stack: { char: string; count: number }[] = []; // [char, count]; + + for (const c of s) { + if (stack.length !== 0 && stack[stack.length - 1].char === c) { + stack[stack.length - 1].count++; + } else { + stack.push({ char: c, count: 1 }); + } + + if (stack[stack.length - 1].count === k) { + stack.pop(); + } + } + + return stack.reduce( + (acc, { char, count }) => (acc += char.repeat(count)), + '' + ); +} diff --git a/typescript/1209-remove-all-adjacent-duplicates-in-string-ii.ts b/typescript/1209-remove-all-adjacent-duplicates-in-string-ii.ts new file mode 100644 index 000000000..88cb24bb1 --- /dev/null +++ b/typescript/1209-remove-all-adjacent-duplicates-in-string-ii.ts @@ -0,0 +1,20 @@ +function removeDuplicates(s: string, k: number): string { + const stack: { char: string; count: number }[] = []; // [char, count]; + + for (const c of s) { + if (stack.length !== 0 && stack[stack.length - 1].char === c) { + stack[stack.length - 1].count++; + } else { + stack.push({ char: c, count: 1 }); + } + + if (stack[stack.length - 1].count === k) { + stack.pop(); + } + } + + return stack.reduce( + (acc, { char, count }) => (acc += char.repeat(count)), + '' + ); +} diff --git a/typescript/1299-Replace-Elements-With-Greatest-Element-On-Right-Side.ts b/typescript/1299-Replace-Elements-With-Greatest-Element-On-Right-Side.ts new file mode 100644 index 000000000..6916a1834 --- /dev/null +++ b/typescript/1299-Replace-Elements-With-Greatest-Element-On-Right-Side.ts @@ -0,0 +1,12 @@ +function replaceElements(arr: number[]): number[] { + let currMax = -1; + + for(let i = arr.length -1 ; i >= 0; i--) { + let newMax = Math.max(currMax, arr[i]); + arr[i] = currMax; + + currMax = newMax; + } + + return arr; +}; diff --git a/typescript/1299-replace-elements-with-greatest-element-on-right-side.ts b/typescript/1299-replace-elements-with-greatest-element-on-right-side.ts new file mode 100644 index 000000000..6916a1834 --- /dev/null +++ b/typescript/1299-replace-elements-with-greatest-element-on-right-side.ts @@ -0,0 +1,12 @@ +function replaceElements(arr: number[]): number[] { + let currMax = -1; + + for(let i = arr.length -1 ; i >= 0; i--) { + let newMax = Math.max(currMax, arr[i]); + arr[i] = currMax; + + currMax = newMax; + } + + return arr; +}; diff --git a/typescript/1448-Count-Good-Nodes-in-Binary-Tree.ts b/typescript/1448-Count-Good-Nodes-in-Binary-Tree.ts new file mode 100644 index 000000000..b0efb630c --- /dev/null +++ b/typescript/1448-Count-Good-Nodes-in-Binary-Tree.ts @@ -0,0 +1,21 @@ +function goodNodes(root: TreeNode | null): number { + // edge case + if (root === null) return 0; + + //keep track of the count + let count = 0; + + const dfs = (root, max) => { + //if value is bigger than max of path, then it is a good node + if (root.val >= max) count++; + + //extend search to children if not null with updated max(takes current node in consideration too) + if (root.left !== null) dfs(root.left, Math.max(max, root.val)); + if (root.right !== null) dfs(root.right, Math.max(max, root.val)); + }; + + //starts with negative max value, because min value is just the smaller positive value in javascript + dfs(root, -Number.MAX_VALUE); + + return count; +} diff --git a/typescript/1448-count-good-nodes-in-binary-tree.ts b/typescript/1448-count-good-nodes-in-binary-tree.ts new file mode 100644 index 000000000..b0efb630c --- /dev/null +++ b/typescript/1448-count-good-nodes-in-binary-tree.ts @@ -0,0 +1,21 @@ +function goodNodes(root: TreeNode | null): number { + // edge case + if (root === null) return 0; + + //keep track of the count + let count = 0; + + const dfs = (root, max) => { + //if value is bigger than max of path, then it is a good node + if (root.val >= max) count++; + + //extend search to children if not null with updated max(takes current node in consideration too) + if (root.left !== null) dfs(root.left, Math.max(max, root.val)); + if (root.right !== null) dfs(root.right, Math.max(max, root.val)); + }; + + //starts with negative max value, because min value is just the smaller positive value in javascript + dfs(root, -Number.MAX_VALUE); + + return count; +} diff --git a/typescript/1462-course-schedule-iv.ts b/typescript/1462-course-schedule-iv.ts new file mode 100644 index 000000000..f6d3cf910 --- /dev/null +++ b/typescript/1462-course-schedule-iv.ts @@ -0,0 +1,31 @@ +function checkIfPrerequisite(numCourses: number, prerequisites: number[][], queries: number[][]): boolean[] { + const adjList = new Map( + Array.from({ length: numCourses }, (_, i) => [i, []]), + ); + for (let [a, b] of prerequisites) { + adjList.get(b).push(a); + } + + const cache = new Map>(); + function dfs(node: number) { + if (!cache.has(node)) { + cache.set(node, new Set()); + + for (let target of adjList.get(node)) { + const result = dfs(target); + for (let v of result) { + cache.get(node).add(v); + } + } + } + + cache.get(node).add(node); + return cache.get(node); + } + + for (let i = 0; i < numCourses; i++) { + dfs(i); + } + + return queries.map(([a, b]) => cache.get(b).has(a)); +}; diff --git a/typescript/1470-shuffle-the-array.ts b/typescript/1470-shuffle-the-array.ts new file mode 100644 index 000000000..fb44d99e3 --- /dev/null +++ b/typescript/1470-shuffle-the-array.ts @@ -0,0 +1,18 @@ +function shuffle(nums: number[], n: number): number[] { + for (let i = 0; i < n; i++) { + nums[i] = nums[i] << 10; + nums[i] = nums[i] | nums[i + n]; + } + + let j = 2 * n - 1; + + for (let i = n - 1; i > -1; i--) { + let y = nums[i] & (2 ** 10 - 1); + let x = nums[i] >> 10; + nums[j] = y; + nums[j - 1] = x; + j -= 2; + } + + return nums; +} diff --git a/typescript/1472-design-browser-history.ts b/typescript/1472-design-browser-history.ts new file mode 100644 index 000000000..c7f30ea3c --- /dev/null +++ b/typescript/1472-design-browser-history.ts @@ -0,0 +1,24 @@ +class BrowserHistory { + history: string[]; + current: number; + + constructor(homepage: string) { + this.history = [homepage]; + this.current = 0; + } + + visit(url: string): void { + this.history[++this.current] = url; + this.history.length = this.current + 1; + } + + back(steps: number): string { + this.current = Math.max(this.current - steps, 0); + return this.history[this.current]; + } + + forward(steps: number): string { + this.current = Math.min(this.current + steps, this.history.length - 1); + return this.history[this.current]; + } +} diff --git a/typescript/1489-find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.ts b/typescript/1489-find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.ts new file mode 100644 index 000000000..657e0218a --- /dev/null +++ b/typescript/1489-find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree.ts @@ -0,0 +1,104 @@ +type Edge = [number, number, number]; + +class UnionFind { + parents: number[]; + ranks: number[]; + + constructor(n: number) { + this.parents = Array.from({ length: n }, (_, i) => i); + this.ranks = Array.from({ length: n }, () => 0); + } + + find(node: number) { + let current = this.parents[node]; + while (current !== this.parents[current]) { + this.parents[current] = this.parents[this.parents[current]]; + current = this.parents[current]; + } + + return current; + } + + union(a: number, b: number) { + const pa = this.find(a); + const pb = this.find(b); + + if (pa === pb) return false; + if (this.ranks[pa] > this.ranks[pb]) { + this.parents[pb] = pa; + } else if (this.ranks[pa] < this.ranks[pb]) { + this.parents[pa] = pb; + } else { + this.parents[pb] = pa; + this.ranks[pa]++; + } + + return true; + } +} + +function findMST(n: number, edges: Edge[], heap: typeof MinPriorityQueue): { edges: Edge[], sum: number } { + const unionFind = new UnionFind(n); + const resultingEdges = []; + let sum = 0; + + if (!heap.isEmpty()) { + const { element: minEdge } = heap.dequeue(); + unionFind.union(minEdge[0], minEdge[1]); + resultingEdges.push(minEdge); + sum += minEdge[2]; + } + + for (let edge of edges) { + heap.enqueue(edge); + } + + while (!heap.isEmpty() && resultingEdges.length < n - 1) { + const { element: minEdge } = heap.dequeue(); + const isUnion = unionFind.union(minEdge[0], minEdge[1]); + + if (!isUnion) continue; + resultingEdges.push(minEdge); + sum += minEdge[2]; + } + if (resultingEdges.length < n - 1) return { sum: Infinity, edges: [] }; + + return { edges: resultingEdges, sum }; +} + +function buildHeap() { + return new MinPriorityQueue({ priority: edge => edge[2] }); +} + +function findCriticalAndPseudoCriticalEdges(n: number, edges: Edge[]): number[][] { + const generalMST = findMST(n, edges, buildHeap()); + const criticalEdges = []; + + for (let edgeToExclude of generalMST.edges) { + const newEdges = edges.filter(edge => edge.join(',') !== edgeToExclude.join(',')); + const mst = findMST(n, newEdges, buildHeap()); + + if (mst.sum > generalMST.sum) { + criticalEdges.push(edgeToExclude); + } + } + + const pseudoCriticalEdges = []; + for (let edge of edges) { + if (criticalEdges.map(e => e.join(',')).includes(edge.join(','))) continue; + const newEdges = edges.filter(possibleEdge => possibleEdge.join(',') !== edge.join(',')); + + const heap = buildHeap(); + heap.enqueue(edge); + + const mst = findMST(n, newEdges, heap); + if (mst.sum === generalMST.sum) { + pseudoCriticalEdges.push(edge); + } + } + + return [ + criticalEdges.map(criticalEdge => edges.findIndex(edge => edge.join(',') === criticalEdge.join(','))), + pseudoCriticalEdges.map(pCriticalEdge => edges.findIndex(edge => edge.join(',') === pCriticalEdge.join(','))), + ]; +}; diff --git a/typescript/1514-path-with-maximum-probability.ts b/typescript/1514-path-with-maximum-probability.ts new file mode 100644 index 000000000..94b00afcc --- /dev/null +++ b/typescript/1514-path-with-maximum-probability.ts @@ -0,0 +1,33 @@ +function maxProbability(n: number, edges: number[][], succProb: number[], startNode: number, endNode: number): number { + const adjList = new Map( + Array.from({ length: n }, (_, i) => [i, []]) + ); + + for (let i = 0; i < edges.length; i++) { + const [origin, target] = edges[i]; + const prob = succProb[i]; + + adjList.get(origin).push([target, prob]); + adjList.get(target).push([origin, prob]); + } + + const table = new Array(n).fill(0); + table[startNode] = 1; + + const heap = new MaxPriorityQueue({ priority: (a) => a[1] }); + for (let edge of adjList.get(startNode)) { + heap.enqueue(edge); + } + + while (!heap.isEmpty()) { + const { element: maxEdge } = heap.dequeue(); + + if (table[maxEdge[0]] >= maxEdge[1]) continue; + table[maxEdge[0]] = maxEdge[1]; + for (let edge of adjList.get(maxEdge[0])) { + heap.enqueue([edge[0], edge[1] * maxEdge[1]]); + } + } + + return table[endNode]; +}; diff --git a/typescript/1584-min-cost-to-connect-all-points.ts b/typescript/1584-min-cost-to-connect-all-points.ts new file mode 100644 index 000000000..a21dd0848 --- /dev/null +++ b/typescript/1584-min-cost-to-connect-all-points.ts @@ -0,0 +1,32 @@ +function getDistance(a, b) { + return Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]); +} + +function minCostConnectPoints(points: number[][]): number { + const n = points.length; + const visited = new Set(); + const minHeap = new MinPriorityQueue({ priority: (a) => a[2] }); + + visited.add(points[0].join(',')); + for (let point of points) { + if (visited.has(point.join(','))) continue; + + minHeap.enqueue([points[0], point, getDistance(points[0], point)]); + } + + let result = 0; + while (visited.size < n) { + const { element: minEdge } = minHeap.dequeue(); + if (visited.has(minEdge[1].join(','))) continue; + + visited.add(minEdge[1].join(',')); + result += minEdge[2]; + + for (let point of points) { + if (visited.has(point.join(','))) continue; + minHeap.enqueue([minEdge[1], point, getDistance(minEdge[1], point)]); + } + } + + return result; +}; diff --git a/typescript/1768-merge-strings-alternately.ts b/typescript/1768-merge-strings-alternately.ts new file mode 100644 index 000000000..21d09333f --- /dev/null +++ b/typescript/1768-merge-strings-alternately.ts @@ -0,0 +1,13 @@ +// Time complexity: O(n) +// Space complexity: O(n) + +function mergeAlternately(word1: string, word2: string): string { + const buffer: Array = []; + + for (let i = 0; i < word1.length || i < word2.length; i++) { + if (i < word1.length) buffer.push(word1[i]); + if (i < word2.length) buffer.push(word2[i]); + } + + return buffer.join(''); +} diff --git a/typescript/1822-sign-of-the-product-of-an-array.ts b/typescript/1822-sign-of-the-product-of-an-array.ts new file mode 100644 index 000000000..3ed7fd6cb --- /dev/null +++ b/typescript/1822-sign-of-the-product-of-an-array.ts @@ -0,0 +1,10 @@ +function arraySign(nums: number[]): number { + let sign = 1; + + for (const num of nums) { + if (num == 0) return 0; + if (num < 0) sign = -1 * sign; + } + + return sign; +} diff --git a/typescript/1838-frequency-of-the-most-frequent-element.ts b/typescript/1838-frequency-of-the-most-frequent-element.ts new file mode 100644 index 000000000..61d74f35a --- /dev/null +++ b/typescript/1838-frequency-of-the-most-frequent-element.ts @@ -0,0 +1,22 @@ +function maxFrequency(nums: number[], k: number): number { + const sortedNums: number[] = nums.sort((a: number, b: number) => a - b); + + let maxLength: number = 0; + + let currentSum: number = 0; + let leftWindow: number = 0; + for (let rightWindow: number = 0; rightWindow < sortedNums.length; rightWindow++) { + const currentLength: number = rightWindow - leftWindow + 1; + const rightNum: number = sortedNums[rightWindow]; + currentSum += rightNum; + + if (currentSum + k >= rightNum * currentLength) { + maxLength = currentLength; + } else { + const leftNum = sortedNums[leftWindow]; + currentSum -= leftNum; + leftWindow++; + } + } + return maxLength; +}; \ No newline at end of file diff --git a/typescript/1888-minimum-number-of-flips-to-make-the-binary-string-alternating.ts b/typescript/1888-minimum-number-of-flips-to-make-the-binary-string-alternating.ts new file mode 100644 index 000000000..98ea0e6c9 --- /dev/null +++ b/typescript/1888-minimum-number-of-flips-to-make-the-binary-string-alternating.ts @@ -0,0 +1,30 @@ +function minFlips(s: string): number { + if (!s || s.length < 1) return 0; + + const initialLen = s.length; + if (initialLen % 2 === 1) s += s; + + let evenCmp = 0; + let oddCmp = 0; + let result = Infinity; + let end = 0; + let start = 0; + + while (end < s.length) { + if (end % 2 !== Number(s[end])) evenCmp++; + if ((end % 2 ^ 1) !== Number(s[end])) oddCmp++; + + if (end >= initialLen) { + if (start % 2 !== Number(s[start])) evenCmp--; + if ((start % 2 ^ 1) !== Number(s[start])) oddCmp--; + start++; + } + + if (end >= initialLen - 1) { + result = Math.min(evenCmp, oddCmp, result); + } + end++; + } + + return result; +} diff --git a/typescript/1899-Merge-Triplets-to-Form-Target-Triplet.ts b/typescript/1899-Merge-Triplets-to-Form-Target-Triplet.ts new file mode 100644 index 000000000..d18e17d96 --- /dev/null +++ b/typescript/1899-Merge-Triplets-to-Form-Target-Triplet.ts @@ -0,0 +1,21 @@ +function mergeTriplets(triplets: number[][], target: number[]): boolean { + let good = new Set(); + + for (let t in triplets) { + let triplet = triplets[t]; + if ( + triplet[0] > target[0] || + triplet[1] > target[1] || + triplet[2] > target[2] + ) { + continue; + } + + for (let i = 0; i < triplet.length; i++) { + if (triplet[i] === target[i]) { + good.add(i); + } + } + } + return good.size === 3; +} diff --git a/typescript/1899-merge-triplets-to-form-target-triplet.ts b/typescript/1899-merge-triplets-to-form-target-triplet.ts new file mode 100644 index 000000000..d18e17d96 --- /dev/null +++ b/typescript/1899-merge-triplets-to-form-target-triplet.ts @@ -0,0 +1,21 @@ +function mergeTriplets(triplets: number[][], target: number[]): boolean { + let good = new Set(); + + for (let t in triplets) { + let triplet = triplets[t]; + if ( + triplet[0] > target[0] || + triplet[1] > target[1] || + triplet[2] > target[2] + ) { + continue; + } + + for (let i = 0; i < triplet.length; i++) { + if (triplet[i] === target[i]) { + good.add(i); + } + } + } + return good.size === 3; +} diff --git a/typescript/1929-concatenation-of-array.ts b/typescript/1929-concatenation-of-array.ts new file mode 100644 index 000000000..19350bb6c --- /dev/null +++ b/typescript/1929-concatenation-of-array.ts @@ -0,0 +1,9 @@ +function getConcatenation(nums: number[]): number[] { + let result: number[] = []; + + for(let i = 0; i < 2; i++) { + nums.forEach(num => result.push(num)); + }; + + return result; +}; diff --git a/typescript/1930-unique-length-3-palindromic-subsequences.ts b/typescript/1930-unique-length-3-palindromic-subsequences.ts new file mode 100644 index 000000000..ef0804404 --- /dev/null +++ b/typescript/1930-unique-length-3-palindromic-subsequences.ts @@ -0,0 +1,20 @@ +function countPalindromicSubsequence(s: string): number { + const alphabets = 'abcdefghijklmnopqrstuvwxyz'; + + const N = alphabets.length; + let count = 0; + + for (let i = 0; i < N; i += 1) { + const ch = alphabets[i]; + const left = s.indexOf(ch); + const right = s.lastIndexOf(ch); + if (left < right) { + for (const alpha of alphabets) { + const mid = s.indexOf(alpha, left + 1); + if (mid !== -1 && mid < right) count += 1; + } + } + } + + return count; +} diff --git a/typescript/1984-minimum-difference-between-highest-and-lowest-of-k-scores.ts b/typescript/1984-minimum-difference-between-highest-and-lowest-of-k-scores.ts new file mode 100644 index 000000000..02eac60b6 --- /dev/null +++ b/typescript/1984-minimum-difference-between-highest-and-lowest-of-k-scores.ts @@ -0,0 +1,13 @@ +function minimumDifference(nums: number[], k: number): number { + nums.sort((a, b) => a - b); + let left: number = 0; + let right: number = k - 1; + let result: number = Number.MAX_VALUE; + + while (right < nums.length) { + result = Math.min(result, nums[right] - nums[left]); + left++; + right++; + } + return result; +} diff --git a/typescript/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.ts b/typescript/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.ts new file mode 100644 index 000000000..9fb2b6e41 --- /dev/null +++ b/typescript/2002-maximum-product-of-the-length-of-two-palindromic-subsequences.ts @@ -0,0 +1,47 @@ +/** + * Time Complexity: O(2^N) + * Space Complexity: O(2^N) + */ +function maxProduct(s: string): number { + const N = s.length; + const first: number[] = new Array(1 << N).fill(0), + last: number[] = new Array(1 << N).fill(0); + for (let i = 0; i < N; i++) { + for (let j = 1 << i; j < 1 << (i + 1); j++) { + first[j] = i; + } + } + for (let i = 0; i < N; i++) { + for (let j = 1 << i; j < 1 << N; j += 1 << (i + 1)) { + last[j] = i; + } + } + const dp = cache((m: number) => { + if ((m & (m - 1)) === 0) { + return m != 0; + } + const l = last[m], f = first[m]; + const lb = 1 << l, fb = 1 << f; + return Math.max( + dp(m - lb), + dp(m - fb), + dp(m - lb - fb) + Number(s[l] === s[f]) * 2 + ); + }); + let ans = 0; + for (let m = 1; m < 1 << N; m++) { + ans = Math.max(ans, dp(m) * dp((1 << N) - 1 - m)); + } + return ans; +}; + +function cache(func:Function){ + const map = new Map(); + var wrapper = (m:number) => { + if (!map.get(m)) { + map.set(m, func(m)); + } + return map.get(m); + }; + return wrapper; +}; diff --git a/typescript/2215-find-the-difference-of-two-arrays.ts b/typescript/2215-find-the-difference-of-two-arrays.ts new file mode 100644 index 000000000..e7d331a4d --- /dev/null +++ b/typescript/2215-find-the-difference-of-two-arrays.ts @@ -0,0 +1,16 @@ +// Time Complexity: O(m + n), we check each element of nums1Set and nums2Set +// Space Complexity: O(m + n), where m and n are length sets in worst case. + +function findDifference(nums1: number[], nums2: number[]): number[][] { + const nums1Set: Set = new Set(nums1); + const nums2Set: Set = new Set(nums2); + + const lst1: number[] = Array.from(nums1Set).filter( + (num: number) => !nums2Set.has(num) + ); + const lst2: number[] = Array.from(nums2Set).filter( + (num: number) => !nums1Set.has(num) + ); + + return [lst1, lst2]; +} diff --git a/typescript/2390-removing-stars-from-a-string.ts b/typescript/2390-removing-stars-from-a-string.ts new file mode 100644 index 000000000..9b5cf7edf --- /dev/null +++ b/typescript/2390-removing-stars-from-a-string.ts @@ -0,0 +1,14 @@ +function removeStars(s: string): string { + if(!s.length) return ''; + + const result = []; + + for(let char of s){ + if(char == '*') result.pop() + else result.push(char) + } + return result.join('') +}; + +// Time Complexity: O(n) +// Space Complexity: O(n) diff --git a/typescript/2421-number-of-good-paths.ts b/typescript/2421-number-of-good-paths.ts new file mode 100644 index 000000000..15d381581 --- /dev/null +++ b/typescript/2421-number-of-good-paths.ts @@ -0,0 +1,95 @@ +class UnionFind { + parent: number[]; + rank: number[]; + + constructor(n: number) { + this.parent = new Array(n).fill(0).map((_, i) => i); + this.rank = new Array(n).fill(0); + } + + find(i: number): number { + while (i !== this.parent[i]) { + this.parent[i] = this.parent[this.parent[i]]; + i = this.parent[i]; + } + return i; + } + + union(a: number, b: number): boolean { + let [aRoot, bRoot] = [this.find(a), this.find(b)]; + + if (aRoot == bRoot) return false; + + if (this.rank[aRoot] < this.rank[bRoot]) { + this.parent[aRoot] = bRoot; + this.rank[bRoot] += this.rank[aRoot]; + } else { + this.parent[bRoot] = aRoot; + this.rank[aRoot] += this.rank[bRoot]; + } + + return true; + } +} + +function getAdjList(edges: number[][]): Map { + let adj = new Map(); + + for (let e of edges) { + let [a, b] = [e[0], e[1]]; + + let [adjA, adjB] = [adj.get(a) || [], adj.get(b) || []]; + + adjA.push(b); + adjB.push(a); + + adj.set(a, adjA); + adj.set(b, adjB); + } + + return adj; +} + +function getValToIndex(vals: number[]): Map { + let valToIndex = new Map(); + + for (let i in vals) { + let val = vals[i]; + let arr = valToIndex.get(val) || []; + arr.push(+i); + valToIndex.set(val, arr); + } + + return valToIndex; +} + + +function numberOfGoodPaths(vals: number[], edges: number[][]): number { + let adj = getAdjList(edges); + let valToIndex = getValToIndex(vals); + + let res = 0; + let uf = new UnionFind(vals.length); + + let keys = Array.from(valToIndex.keys()); + keys.sort((a, b) => a - b); + + for (let val of keys) { + for (let i of valToIndex.get(val)!) { + for (let nei of adj.get(i) || []) { + if (vals[nei] <= vals[i]) { + uf.union(nei, i); + } + } + } + let count = new Map(); + + for (let i of valToIndex.get(val)!) { + let c = count.get(uf.find(i)) || 0; + count.set(uf.find(i), c + 1); + res += count.get(uf.find(i)); + } + } + + return res; +} diff --git a/typescript/2469-convert-the-temperature.ts b/typescript/2469-convert-the-temperature.ts new file mode 100644 index 000000000..59d54b405 --- /dev/null +++ b/typescript/2469-convert-the-temperature.ts @@ -0,0 +1,5 @@ +function convertTemperature(celsius: number): number[] { + const kelvin = celsius + 273.15; + const fahrenheit = celsius * 1.80 + 32.00; + return [kelvin, fahrenheit]; +}; diff --git a/typescript/2620-counter.ts b/typescript/2620-counter.ts new file mode 100644 index 000000000..898864eac --- /dev/null +++ b/typescript/2620-counter.ts @@ -0,0 +1,13 @@ +function createCounter(n: number): () => number { + return function() { + return n++; + } +} + + +/** + * const counter = createCounter(10) + * counter() // 10 + * counter() // 11 + * counter() // 12 + */ diff --git a/typescript/2667-create-hello-world-function.ts b/typescript/2667-create-hello-world-function.ts new file mode 100644 index 000000000..01320e958 --- /dev/null +++ b/typescript/2667-create-hello-world-function.ts @@ -0,0 +1,10 @@ +function createHelloWorld() { + return function(...args): string { + return "Hello World"; + }; +}; + +/** + * const f = createHelloWorld(); + * f(); // "Hello World" + */ diff --git a/updateCompletionTable.js b/updateCompletionTable.js new file mode 100644 index 000000000..83b1a537e --- /dev/null +++ b/updateCompletionTable.js @@ -0,0 +1,124 @@ +const { readdirSync } = require('fs'); +const fs = require('fs'); +const path = require('path'); + +const IGNORE_DIRS = ['.github', '.git', '.idea']; +const FOLDER_TO_LANG = { + javascript: 'JS', + typescript: 'TS', + csharp: 'C#', + c: 'C', + go: 'GO', + java: 'Java', + python: 'Python', + ruby: 'Ruby', + rust: 'Rust', + scala: 'Scala', + swift: 'Swift', + cpp: 'C++', + kotlin: 'Kotlin', + dart: 'Dart', +}; +const PREPEND_PATH = process.argv[2] || './'; +const TEMPLATE_PATH = process.argv[3] || './README_template.md'; +const WRITE_PATH = process.argv[3] || './README.md'; + +const PROBLEM_SITE_DATA = JSON.parse(fs.readFileSync('./.problemSiteData.json', 'utf8')); + +function createProblemsObj(PROBLEM_SITE_DATA) { + let PROBLEMS_OBJ = {} + for (const {problem, pattern, link, code} of PROBLEM_SITE_DATA) { + if (!(pattern in PROBLEMS_OBJ)) PROBLEMS_OBJ[pattern] = [] + PROBLEMS_OBJ[pattern].push([problem, "https://leetcode.com/problems/" + link, code.slice(0, 4)]) + } + return PROBLEMS_OBJ +} + +const PROBLEMS_OBJ = createProblemsObj(PROBLEM_SITE_DATA) + +const getDirectories = (source) => + readdirSync(source, { withFileTypes: true }) + .filter((dirent) => dirent.isDirectory()) + .map((dirent) => dirent.name); + +function* walkSync(dir) { + const files = fs.readdirSync(dir, { withFileTypes: true }); + for (const file of files) { + if (file.isDirectory()) { + yield* walkSync(path.join(dir, file.name)); + } else { + yield path.join(dir, file.name); + } + } +} + +function nestedFiles(dir) { + files = []; + for (const filePath of walkSync(dir)) { + files.push(filePath); + } + return files; +} + +const directories = getDirectories(PREPEND_PATH).filter( + (dir) => !IGNORE_DIRS.includes(dir) +); +const nestedFilesInDir = directories.reduce((acc, dir) => { + acc[dir] = nestedFiles(dir); + return acc; +}, {}); + +const tableHeader = + [ + 'Problem', + ...directories.map((folder) => FOLDER_TO_LANG[folder] || folder), + ] + .map((el) => `${el}`) + .join(' | ') + '\n'; + +const tableSep = + Array.from({ length: tableHeader.split('|').length }) + .map((el) => '----') + .join(' | ') + '\n'; + +let fullOutput = ''; + +for (const problemCategory in PROBLEMS_OBJ) { + fullOutput += `### ${problemCategory}\n\n`; + fullOutput += tableHeader; + fullOutput += tableSep; + + for (const [problemName, problemUrl, problemNumber] of PROBLEMS_OBJ[ + problemCategory + ]) { + let problemRow = [ + `[${problemNumber} - ${problemName}](${problemUrl})`, + ]; + for (const dir of directories) { + let filePath = nestedFilesInDir[dir].find((file) => + file + .match(/[\w-]+\..+/)?.[0] + ?.startsWith(problemNumber) + ); + if (filePath) { + problemRow.push( + `
[✔️](${encodeURIComponent( + filePath + )})
` + ); + } else { + problemRow.push("
"); + } + } + + fullOutput += problemRow.join(' | ') + '\n'; + } + fullOutput += '\n'; +} + +const template = fs.readFileSync(TEMPLATE_PATH, { encoding: 'utf8' }); +const toWrite = template.replaceAll('', fullOutput); +console.log(toWrite); +fs.writeFileSync(WRITE_PATH, toWrite, { + encoding: 'utf8', +}); diff --git a/updateSiteData.js b/updateSiteData.js new file mode 100644 index 000000000..78691d31c --- /dev/null +++ b/updateSiteData.js @@ -0,0 +1,115 @@ +/** Script to update ./.problemSiteData.json contains code for solutions hosted on neetcode.io */ + +const fs = require('fs'); + +const PROBLEMS_SITE_DATA = JSON.parse(fs.readFileSync('./.problemSiteData.json', 'utf8')); + +const languages = [ + { + name: 'C', + directory: 'c', + extension: 'c' + }, + { + name: 'C++', + directory: 'cpp', + extension: 'cpp' + }, + { + name: 'C#', + directory: 'csharp', + extension: 'cs' + }, + { + name: 'Java', + directory: 'java', + extension: 'java' + }, + { + name: 'Python', + directory: 'python', + extension: 'py' + }, + { + name: 'JavaScript', + directory: 'javascript', + extension: 'js' + }, + { + name: 'TypeScript', + directory: 'typescript', + extension: 'ts' + }, + { + name: 'Go', + directory: 'go', + extension: 'go' + }, + { + name: 'Ruby', + directory: 'ruby', + extension: 'rb' + }, + { + name: 'Swift', + directory: 'swift', + extension: 'swift' + }, + { + name: 'Kotlin', + directory: 'kotlin', + extension: 'kt' + }, + { + name: 'Rust', + directory: 'rust', + extension: 'rs' + }, + { + name: 'Scala', + directory: 'scala', + extension: 'scala' + }, + { + name: 'Dart', + directory: 'dart', + extension: 'dart' + }, +] + +// Rename files to match leetcode url path, and normalize problem number to four digits. +for (const lang of languages) { + const langDir = lang.directory; + const langExt = lang.extension; + + // Get list of all files in the current lang directory + const files = fs.readdirSync(langDir, { withFileTypes: true }); + console.log(`This many files in ${langDir}: ${files.length}`); + + let counter = 0; + for (const problem of PROBLEMS_SITE_DATA) { + let problemName = problem['link'].replace('/', '').toLowerCase(); + + // Use problem number to find code file + const problemNumber = problem['code'].split('-')[0]; + const foundFile = files.find(file => file.name.startsWith(`${problemNumber.toString()}-`)); + + if (foundFile && foundFile.isFile()) { + // rename file to match leetcode url path + const oldFile = `${langDir}/${foundFile.name}`; + const newFile = `${langDir}/${problemNumber}-${problemName}.${langExt}`; + if (oldFile !== newFile) { + fs.renameSync(oldFile, newFile); + counter++; + } + problem[langDir] = true; // add language to problemSiteData + } + } + console.log(`Renamed ${counter} files in ${langDir}, which had ${files.length} total files.`); +} + +// Write updated problemSiteData to file +fs.writeFile('./.problemSiteData.json', JSON.stringify(PROBLEMS_SITE_DATA), function (err) { + if (err) throw err; + console.log('Saved!'); +}); diff --git a/verifySiteData.js b/verifySiteData.js new file mode 100644 index 000000000..f360d2332 --- /dev/null +++ b/verifySiteData.js @@ -0,0 +1,96 @@ +/** Script to verify code links in ./.problemSiteData.json */ + +const fs = require('fs'); +const https = require('/opt/homebrew/lib/node_modules/sync-request'); + +const PROBLEMS_SITE_DATA = JSON.parse(fs.readFileSync('./.problemSiteData.json', 'utf8')); + +const languageMap = { + c: { + name: 'C', + directory: 'c', + extension: 'c' + }, + cpp: { + name: 'C++', + directory: 'cpp', + extension: 'cpp' + }, + csharp: { + name: 'C#', + directory: 'csharp', + extension: 'cs' + }, + java: { + name: 'Java', + directory: 'java', + extension: 'java' + }, + python: { + name: 'Python', + directory: 'python', + extension: 'py' + }, + javascript: { + name: 'JavaScript', + directory: 'javascript', + extension: 'js' + }, + typescript: { + name: 'TypeScript', + directory: 'typescript', + extension: 'ts' + }, + go: { + name: 'Go', + directory: 'go', + extension: 'go' + }, + ruby: { + name: 'Ruby', + directory: 'ruby', + extension: 'rb' + }, + swift: { + name: 'Swift', + directory: 'swift', + extension: 'swift' + }, + kotlin: { + name: 'Kotlin', + directory: 'kotlin', + extension: 'kt' + }, + rust: { + name: 'Rust', + directory: 'rust', + extension: 'rs' + }, + scala: { + name: 'Scala', + directory: 'scala', + extension: 'scala' + }, + dart: { + name: 'Dart', + directory: 'dart', + extension: 'dart' + }, +}; + +const GITHUB_BASE_URL = 'https://github.com/neetcode-gh/leetcode/blob/main'; + +for (const problem of PROBLEMS_SITE_DATA) { + for (const language in languageMap) { + if (problem[language] !== true) continue; + + const { directory, extension } = languageMap[language]; + const codeUrl = `${GITHUB_BASE_URL}/${directory}/${problem.code}.${extension}`; + + const res = https('GET', codeUrl).statusCode; + if (res !== 200) { + console.log(codeUrl) + console.log(res) + } + } +}